From 47062ff2445c0b217fda9c0e852475843a7713ca Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 16 Apr 2012 21:23:25 +0200 Subject: Initial import --- amd64-codegen.h | 1831 +++++++++++++++++++++++++++++++++++++ main.cpp | 50 + qmljs_objects.cpp | 109 +++ qmljs_objects.h | 264 ++++++ qmljs_runtime.cpp | 339 +++++++ qmljs_runtime.h | 623 +++++++++++++ qv4codegen.cpp | 1780 ++++++++++++++++++++++++++++++++++++ qv4codegen_p.h | 228 +++++ qv4ir.cpp | 697 ++++++++++++++ qv4ir_p.h | 666 ++++++++++++++ qv4isel.cpp | 319 +++++++ qv4isel_p.h | 43 + tests/simple.js | 12 + v4.pro | 29 + x86-codegen.h | 2640 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 15 files changed, 9630 insertions(+) create mode 100644 amd64-codegen.h create mode 100644 main.cpp create mode 100644 qmljs_objects.cpp create mode 100644 qmljs_objects.h create mode 100644 qmljs_runtime.cpp create mode 100644 qmljs_runtime.h create mode 100644 qv4codegen.cpp create mode 100644 qv4codegen_p.h create mode 100644 qv4ir.cpp create mode 100644 qv4ir_p.h create mode 100644 qv4isel.cpp create mode 100644 qv4isel_p.h create mode 100644 tests/simple.js create mode 100644 v4.pro create mode 100644 x86-codegen.h diff --git a/amd64-codegen.h b/amd64-codegen.h new file mode 100644 index 0000000000..3dea9cd306 --- /dev/null +++ b/amd64-codegen.h @@ -0,0 +1,1831 @@ +/* + * amd64-codegen.h: Macros for generating amd64 code + * + * Authors: + * Paolo Molaro (lupus@ximian.com) + * Intel Corporation (ORP Project) + * Sergey Chaban (serge@wildwestsoftware.com) + * Dietmar Maurer (dietmar@ximian.com) + * Patrik Torstensson + * Zalman Stern + * + * Copyright (C) 2000 Intel Corporation. All rights reserved. + * Copyright (C) 2001, 2002 Ximian, Inc. + */ + +#ifndef AMD64_H +#define AMD64_H + +//#include + +typedef enum { + AMD64_RAX = 0, + AMD64_RCX = 1, + AMD64_RDX = 2, + AMD64_RBX = 3, + AMD64_RSP = 4, + AMD64_RBP = 5, + AMD64_RSI = 6, + AMD64_RDI = 7, + AMD64_R8 = 8, + AMD64_R9 = 9, + AMD64_R10 = 10, + AMD64_R11 = 11, + AMD64_R12 = 12, + AMD64_R13 = 13, + AMD64_R14 = 14, + AMD64_R15 = 15, + AMD64_RIP = 16, + AMD64_NREG +} AMD64_Reg_No; + +typedef enum { + AMD64_XMM0 = 0, + AMD64_XMM1 = 1, + AMD64_XMM2 = 2, + AMD64_XMM3 = 3, + AMD64_XMM4 = 4, + AMD64_XMM5 = 5, + AMD64_XMM6 = 6, + AMD64_XMM7 = 7, + AMD64_XMM8 = 8, + AMD64_XMM9 = 9, + AMD64_XMM10 = 10, + AMD64_XMM11 = 11, + AMD64_XMM12 = 12, + AMD64_XMM13 = 13, + AMD64_XMM14 = 14, + AMD64_XMM15 = 15, + AMD64_XMM_NREG = 16, +} AMD64_XMM_Reg_No; + +typedef enum +{ + AMD64_REX_B = 1, /* The register in r/m field, base register in SIB byte, or reg in opcode is 8-15 rather than 0-7 */ + AMD64_REX_X = 2, /* The index register in SIB byte is 8-15 rather than 0-7 */ + AMD64_REX_R = 4, /* The reg field of ModRM byte is 8-15 rather than 0-7 */ + AMD64_REX_W = 8 /* Opeartion is 64-bits instead of 32 (default) or 16 (with 0x66 prefix) */ +} AMD64_REX_Bits; + +#if defined(__default_codegen__) + +#define amd64_codegen_pre(inst) +#define amd64_codegen_post(inst) + +#elif defined(__native_client_codegen__) + +#define amd64_codegen_pre(inst) guint8* _codegen_start = (inst); amd64_nacl_instruction_pre(); +#define amd64_codegen_post(inst) (amd64_nacl_instruction_post(&_codegen_start, &(inst)), _codegen_start); + +/* Because of rex prefixes, etc, call sequences are not constant size. */ +/* These pre- and post-sequence hooks remedy this by aligning the call */ +/* sequence after we emit it, since we will know the exact size then. */ +#define amd64_call_sequence_pre(inst) guint8* _code_start = (inst); +#define amd64_call_sequence_post(inst) \ + (mono_nacl_align_call(&_code_start, &(inst)), _code_start); + +/* Native client can load/store using one of the following registers */ +/* as a base: rip, r15, rbp, rsp. Any other base register needs to have */ +/* its upper 32 bits cleared and reference memory using r15 as the base. */ +#define amd64_is_valid_nacl_base(reg) \ + ((reg) == AMD64_RIP || (reg) == AMD64_R15 || \ + (reg) == AMD64_RBP || (reg) == AMD64_RSP) + +#endif /*__native_client_codegen__*/ + +#ifdef TARGET_WIN32 +#define AMD64_ARG_REG1 AMD64_RCX +#define AMD64_ARG_REG2 AMD64_RDX +#define AMD64_ARG_REG3 AMD64_R8 +#define AMD64_ARG_REG4 AMD64_R9 +#else +#define AMD64_ARG_REG1 AMD64_RDI +#define AMD64_ARG_REG2 AMD64_RSI +#define AMD64_ARG_REG3 AMD64_RDX +#define AMD64_ARG_REG4 AMD64_RCX +#endif + +#ifdef TARGET_WIN32 +#define AMD64_CALLEE_REGS ((1< 4) ? AMD64_REX_W : 0) | \ + (((reg_modrm) > 7) ? AMD64_REX_R : 0) | \ + (((reg_index) > 7) ? AMD64_REX_X : 0) | \ + (((reg_rm_base_opcode) > 7) ? AMD64_REX_B : 0); \ + if ((_amd64_rex_bits != 0) || (((width) == 1))) *(inst)++ = AMD64_REX(_amd64_rex_bits); \ + } while (0) +#elif defined(__native_client_codegen__) +#define amd64_emit_rex(inst, width, reg_modrm, reg_index, reg_rm_base_opcode) do \ + { \ + unsigned char _amd64_rex_bits = \ + (((width) > 4) ? AMD64_REX_W : 0) | \ + (((reg_modrm) > 7) ? AMD64_REX_R : 0) | \ + (((reg_index) > 7) ? AMD64_REX_X : 0) | \ + (((reg_rm_base_opcode) > 7) ? AMD64_REX_B : 0); \ + amd64_nacl_tag_rex((inst)); \ + if ((_amd64_rex_bits != 0) || (((width) == 1))) *(inst)++ = AMD64_REX(_amd64_rex_bits); \ + } while (0) +#endif + +typedef union { + guint64 val; + unsigned char b [8]; +} amd64_imm_buf; + +#include "x86-codegen.h" + +/* In 64 bit mode, all registers have a low byte subregister */ +#undef X86_IS_BYTE_REG +#define X86_IS_BYTE_REG(reg) 1 + +#define amd64_modrm_mod(modrm) ((modrm) >> 6) +#define amd64_modrm_reg(modrm) (((modrm) >> 3) & 0x7) +#define amd64_modrm_rm(modrm) ((modrm) & 0x7) + +#define amd64_rex_r(rex) ((((rex) >> 2) & 0x1) << 3) +#define amd64_rex_x(rex) ((((rex) >> 1) & 0x1) << 3) +#define amd64_rex_b(rex) ((((rex) >> 0) & 0x1) << 3) + +#define amd64_sib_scale(sib) ((sib) >> 6) +#define amd64_sib_index(sib) (((sib) >> 3) & 0x7) +#define amd64_sib_base(sib) ((sib) & 0x7) + +#define amd64_is_imm32(val) ((gint64)val >= -((gint64)1<<31) && (gint64)val <= (((gint64)1<<31)-1)) + +#define x86_imm_emit64(inst,imm) \ + do { \ + amd64_imm_buf imb; \ + imb.val = (guint64) (imm); \ + *(inst)++ = imb.b [0]; \ + *(inst)++ = imb.b [1]; \ + *(inst)++ = imb.b [2]; \ + *(inst)++ = imb.b [3]; \ + *(inst)++ = imb.b [4]; \ + *(inst)++ = imb.b [5]; \ + *(inst)++ = imb.b [6]; \ + *(inst)++ = imb.b [7]; \ + } while (0) + +#define amd64_membase_emit(inst,reg,basereg,disp) do { \ + if ((basereg) == AMD64_RIP) { \ + x86_address_byte ((inst), 0, (reg)&0x7, 5); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + else \ + x86_membase_emit ((inst),(reg)&0x7, (basereg)&0x7, (disp)); \ +} while (0) + +#define amd64_alu_reg_imm_size_body(inst,opc,reg,imm,size) \ + do { \ + if (x86_is_imm8((imm))) { \ + amd64_emit_rex(inst, size, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0x83; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((reg) == AMD64_RAX) { \ + amd64_emit_rex(inst, size, 0, 0, 0); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ + x86_imm_emit32 ((inst), (imm)); \ + } else { \ + amd64_emit_rex(inst, size, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0x81; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define amd64_alu_reg_reg_size_body(inst,opc,dreg,reg,size) \ + do { \ + amd64_emit_rex(inst, size, (dreg), 0, (reg)); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#if defined(__default_codegen__) + +#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) \ + amd64_alu_reg_imm_size_body((inst), (opc), (reg), (imm), (size)) + +#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) \ + amd64_alu_reg_reg_size_body((inst), (opc), (dreg), (reg), (size)) + +#elif defined(__native_client_codegen__) +/* NaCl modules may not directly update RSP or RBP other than direct copies */ +/* between them. Instead the lower 4 bytes are updated and then added to R15 */ +#define amd64_is_nacl_stack_reg(reg) (((reg) == AMD64_RSP) || ((reg) == AMD64_RBP)) + +#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) \ + do{ \ + amd64_codegen_pre(inst); \ + if (amd64_is_nacl_stack_reg(reg)) { \ + if (((opc) != X86_ADD) && ((opc) != X86_SUB)) \ + g_assert_not_reached(); \ + amd64_alu_reg_imm_size_body((inst), (opc), (reg), (imm), 4); \ + /* Use LEA instead of ADD to preserve flags */ \ + amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ + } else { \ + amd64_alu_reg_imm_size_body((inst), (opc), (reg), (imm), (size)); \ + } \ + amd64_codegen_post(inst); \ + } while(0) + +#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + if (amd64_is_nacl_stack_reg((dreg)) && ((reg) != AMD64_R15)) { \ + if (((opc) != X86_ADD && (opc) != X86_SUB)) \ + g_assert_not_reached(); \ + amd64_alu_reg_reg_size_body((inst), (opc), (dreg), (reg), 4); \ + /* Use LEA instead of ADD to preserve flags */ \ + amd64_lea_memindex_size((inst), (dreg), (dreg), 0, AMD64_R15, 0, 8); \ + } else { \ + amd64_alu_reg_reg_size_body((inst), (opc), (dreg), (reg), (size)); \ + } \ + amd64_codegen_post(inst); \ + } while (0) + +#endif /*__native_client_codegen__*/ + +#define amd64_alu_reg_imm(inst,opc,reg,imm) amd64_alu_reg_imm_size((inst),(opc),(reg),(imm),8) + +#define amd64_alu_reg_reg(inst,opc,dreg,reg) amd64_alu_reg_reg_size ((inst),(opc),(dreg),(reg),8) + +#define amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst),(size),(reg),0,(basereg)); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + amd64_membase_emit (inst, reg, basereg, disp); \ + amd64_codegen_post(inst); \ +} while (0) + +#define amd64_mov_regp_reg(inst,regp,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, (regp)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_regp_emit ((inst), (reg), (regp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_membase_reg(inst,basereg,disp,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_mem_reg(inst,mem,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, 0); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_address_byte ((inst), 0, (reg), 4); \ + x86_address_byte ((inst), 0, 4, 5); \ + x86_imm_emit32 ((inst), (mem)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_reg_reg(inst,dreg,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (dreg), 0, (reg)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_reg_mem_body(inst,reg,mem,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, 0); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_address_byte ((inst), 0, (reg), 4); \ + x86_address_byte ((inst), 0, 4, 5); \ + x86_imm_emit32 ((inst), (mem)); \ + amd64_codegen_post(inst); \ + } while (0) + +#if defined(__default_codegen__) +#define amd64_mov_reg_mem(inst,reg,mem,size) \ + do { \ + amd64_mov_reg_mem_body((inst),(reg),(mem),(size)); \ + } while (0) +#elif defined(__native_client_codegen__) +/* We have to re-base memory reads because memory isn't zero based. */ +#define amd64_mov_reg_mem(inst,reg,mem,size) \ + do { \ + amd64_mov_reg_membase((inst),(reg),AMD64_R15,(mem),(size)); \ + } while (0) +#endif /* __native_client_codegen__ */ + +#define amd64_mov_reg_membase_body(inst,reg,basereg,disp,size) \ + do { \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define amd64_mov_reg_memindex_size_body(inst,reg,basereg,disp,indexreg,shift,size) \ + do { \ + amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); \ + x86_mov_reg_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(size) == 8 ? 4 : (size)); \ + } while (0) + +#if defined(__default_codegen__) + +#define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) \ + amd64_mov_reg_memindex_size_body((inst),(reg),(basereg),(disp),(indexreg),(shift),(size)) +#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) \ + do { \ + amd64_mov_reg_membase_body((inst), (reg), (basereg), (disp), (size)); \ + } while (0) + +#elif defined(__native_client_codegen__) + +#define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) \ + do { \ + amd64_codegen_pre(inst); \ + if (amd64_is_nacl_stack_reg((reg))) { \ + /* Clear upper 32 bits with mov of size 4 */ \ + amd64_mov_reg_memindex_size_body((inst), (reg), (basereg), (disp), (indexreg), (shift), 4); \ + /* Add %r15 using LEA to preserve flags */ \ + amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ + } else { \ + amd64_mov_reg_memindex_size_body((inst), (reg), (basereg), (disp), (indexreg), (shift), (size)); \ + } \ + amd64_codegen_post(inst); \ + } while(0) + +#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) \ + do { \ + amd64_codegen_pre(inst); \ + if (amd64_is_nacl_stack_reg((reg))) { \ + /* Clear upper 32 bits with mov of size 4 */ \ + amd64_mov_reg_membase_body((inst), (reg), (basereg), (disp), 4); \ + /* Add %r15 */ \ + amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ + } else { \ + amd64_mov_reg_membase_body((inst), (reg), (basereg), (disp), (size)); \ + } \ + amd64_codegen_post(inst); \ + } while (0) + +#endif /*__native_client_codegen__*/ + +#define amd64_movzx_reg_membase(inst,reg,basereg,disp,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb6; break; \ + case 2: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb7; break; \ + case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsxd_reg_mem(inst,reg,mem) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst,8,(reg),0,0); \ + *(inst)++ = (unsigned char)0x63; \ + x86_mem_emit ((inst), ((reg)&0x7), (mem)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsxd_reg_membase(inst,reg,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst,8,(reg),0,(basereg)); \ + *(inst)++ = (unsigned char)0x63; \ + x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsxd_reg_reg(inst,dreg,reg) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst,8,(dreg),0,(reg)); \ + *(inst)++ = (unsigned char)0x63; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ + } while (0) + +/* Pretty much the only instruction that supports a 64-bit immediate. Optimize for common case of + * 32-bit immediate. Pepper with casts to avoid warnings. + */ +#define amd64_mov_reg_imm_size(inst,reg,imm,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, (size), 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0xb8 + ((reg) & 0x7); \ + if ((size) == 8) \ + x86_imm_emit64 ((inst), (guint64)(imm)); \ + else \ + x86_imm_emit32 ((inst), (int)(guint64)(imm)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_reg_imm(inst,reg,imm) \ + do { \ + int _amd64_width_temp = ((guint64)(imm) == (guint64)(int)(guint64)(imm)); \ + amd64_codegen_pre(inst); \ + amd64_mov_reg_imm_size ((inst), (reg), (imm), (_amd64_width_temp ? 4 : 8)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_set_reg_template(inst,reg) amd64_mov_reg_imm_size ((inst),(reg), 0, 8) + +#define amd64_set_template(inst,reg) amd64_set_reg_template((inst),(reg)) + +#define amd64_mov_membase_imm(inst,basereg,disp,imm,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size) == 1 ? 0 : (size), 0, 0, (basereg)); \ + if ((size) == 1) { \ + *(inst)++ = (unsigned char)0xc6; \ + x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + *(inst)++ = (unsigned char)0xc7; \ + x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + *(inst)++ = (unsigned char)0xc7; \ + x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + amd64_codegen_post(inst); \ + } while (0) + + +#define amd64_lea_membase_body(inst,reg,basereg,disp) \ + do { \ + amd64_emit_rex(inst, 8, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x8d; \ + amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#if defined(__default_codegen__) +#define amd64_lea_membase(inst,reg,basereg,disp) \ + amd64_lea_membase_body((inst), (reg), (basereg), (disp)) +#elif defined(__native_client_codegen__) +/* NaCl modules may not write directly into RSP/RBP. Instead, use a */ +/* 32-bit LEA and add R15 to the effective address */ +#define amd64_lea_membase(inst,reg,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + if (amd64_is_nacl_stack_reg(reg)) { \ + /* 32-bit LEA */ \ + amd64_emit_rex((inst), 4, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x8d; \ + amd64_membase_emit((inst), (reg), (basereg), (disp)); \ + /* Use a 64-bit LEA instead of an ADD to preserve flags */ \ + amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ + } else { \ + amd64_lea_membase_body((inst), (reg), (basereg), (disp)); \ + } \ + amd64_codegen_post(inst); \ + } while (0) +#endif /*__native_client_codegen__*/ + +/* Instruction are implicitly 64-bits so don't generate REX for just the size. */ +#define amd64_push_reg(inst,reg) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, 0, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0x50 + ((reg) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +/* Instruction is implicitly 64-bits so don't generate REX for just the size. */ +#define amd64_push_membase(inst,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, 0, 0, 0, (basereg)); \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 6, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_pop_reg_body(inst,reg) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, 0, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0x58 + ((reg) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#if defined(__default_codegen__) + +#define amd64_call_reg(inst,reg) \ + do { \ + amd64_emit_rex(inst, 0, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst), 2, ((reg) & 0x7)); \ + } while (0) + + +#define amd64_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) +#define amd64_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) + +#define amd64_pop_reg(inst,reg) amd64_pop_reg_body((inst), (reg)) + +#elif defined(__native_client_codegen__) + +/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ +#define amd64_jump_reg_size(inst,reg,size) \ + do { \ + amd64_codegen_pre((inst)); \ + amd64_alu_reg_imm_size((inst), X86_AND, (reg), (nacl_align_byte), 4); \ + amd64_alu_reg_reg_size((inst), X86_ADD, (reg), AMD64_R15, 8); \ + amd64_emit_rex ((inst),0,0,0,(reg)); \ + x86_jump_reg((inst),((reg)&0x7)); \ + amd64_codegen_post((inst)); \ + } while (0) + +/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ +#define amd64_jump_mem_size(inst,mem,size) \ + do { \ + amd64_codegen_pre((inst)); \ + amd64_mov_reg_mem((inst), (mem), AMD64_R11, 4); \ + amd64_jump_reg_size((inst), AMD64_R11, 4); \ + amd64_codegen_post((inst)); \ + } while (0) + +#define amd64_call_reg_internal(inst,reg) \ + do { \ + amd64_codegen_pre((inst)); \ + amd64_alu_reg_imm_size((inst), X86_AND, (reg), (nacl_align_byte), 4); \ + amd64_alu_reg_reg_size((inst), X86_ADD, (reg), AMD64_R15, 8); \ + amd64_emit_rex((inst), 0, 0, 0, (reg)); \ + x86_call_reg((inst), ((reg) & 0x7)); \ + amd64_codegen_post((inst)); \ + } while (0) + +#define amd64_call_reg(inst,reg) \ + do { \ + amd64_codegen_pre((inst)); \ + amd64_call_sequence_pre(inst); \ + amd64_call_reg_internal((inst), (reg)); \ + amd64_call_sequence_post(inst); \ + amd64_codegen_post((inst)); \ + } while (0) + + +#define amd64_ret(inst) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_pop_reg_body((inst), AMD64_R11); \ + amd64_jump_reg_size((inst), AMD64_R11, 8); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_leave(inst) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_mov_reg_reg((inst), AMD64_RSP, AMD64_RBP, 8); \ + amd64_pop_reg_body((inst), AMD64_R11); \ + amd64_mov_reg_reg_size((inst), AMD64_RBP, AMD64_R11, 4); \ + amd64_alu_reg_reg_size((inst), X86_ADD, AMD64_RBP, AMD64_R15, 8); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_pop_reg(inst,reg) \ + do { \ + amd64_codegen_pre(inst); \ + if (amd64_is_nacl_stack_reg((reg))) { \ + amd64_pop_reg_body((inst), AMD64_R11); \ + amd64_mov_reg_reg_size((inst), (reg), AMD64_R11, 4); \ + amd64_alu_reg_reg_size((inst), X86_ADD, (reg), AMD64_R15, 8); \ + } else { \ + amd64_pop_reg_body((inst), (reg)); \ + } \ + amd64_codegen_post(inst); \ + } while (0) + +#endif /*__native_client_codegen__*/ + +#define amd64_movsd_reg_regp(inst,reg,regp) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf2); \ + amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsd_regp_reg(inst,regp,reg) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf2); \ + amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movss_reg_regp(inst,reg,regp) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf3); \ + amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movss_regp_reg(inst,regp,reg) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf3); \ + amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsd_reg_membase(inst,reg,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf2); \ + amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movss_reg_membase(inst,reg,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf3); \ + amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsd_membase_reg(inst,basereg,disp,reg) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf2); \ + amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movss_membase_reg(inst,basereg,disp,reg) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf3); \ + amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +/* The original inc_reg opcode is used as the REX prefix */ +#define amd64_inc_reg_size(inst,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst),(size),0,0,(reg)); \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst),0,(reg) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_dec_reg_size(inst,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst),(size),0,0,(reg)); \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst),1,(reg) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_fld_membase_size(inst,basereg,disp,is_double,size) do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst),0,0,0,(basereg)); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ + amd64_membase_emit ((inst), 0, (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +#if defined (__default_codegen__) + +/* From the AMD64 Software Optimization Manual */ +#define amd64_padding_size(inst,size) \ + do { \ + switch ((size)) { \ + case 1: *(inst)++ = 0x90; break; \ + case 2: *(inst)++ = 0x66; *(inst)++ = 0x90; break; \ + case 3: *(inst)++ = 0x66; *(inst)++ = 0x66; *(inst)++ = 0x90; break; \ + default: amd64_emit_rex ((inst),8,0,0,0); x86_padding ((inst), (size) - 1); \ + }; \ + } while (0) + +#define amd64_call_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); *(inst)++ = (unsigned char)0xff; amd64_membase_emit ((inst),2, (basereg),(disp)); } while (0) +#define amd64_jump_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); *(inst)++ = (unsigned char)0xff; amd64_membase_emit ((inst), 4, (basereg), (disp)); } while (0) + +#define amd64_jump_code_size(inst,target,size) do { \ + if (amd64_is_imm32 ((gint64)(target) - (gint64)(inst))) { \ + x86_jump_code((inst),(target)); \ + } else { \ + amd64_jump_membase ((inst), AMD64_RIP, 0); \ + *(guint64*)(inst) = (guint64)(target); \ + (inst) += 8; \ + } \ +} while (0) + +#elif defined(__native_client_codegen__) + +/* The 3-7 byte NOP sequences in amd64_padding_size below are all illegal in */ +/* 64-bit Native Client because they load into rSP/rBP or use duplicate */ +/* prefixes. Instead we use the NOPs recommended in Section 3.5.1.8 of the */ +/* Intel64 and IA-32 Architectures Optimization Reference Manual and */ +/* Section 4.13 of AMD Software Optimization Guide for Family 10h Processors. */ + +#define amd64_padding_size(inst,size) \ + do { \ + unsigned char *code_start = (inst); \ + switch ((size)) { \ + /* xchg %eax,%eax, recognized by hardware as a NOP */ \ + case 1: *(inst)++ = 0x90; break; \ + /* xchg %ax,%ax */ \ + case 2: *(inst)++ = 0x66; *(inst)++ = 0x90; \ + break; \ + /* nop (%rax) */ \ + case 3: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ + *(inst)++ = 0x00; \ + break; \ + /* nop 0x0(%rax) */ \ + case 4: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ + x86_address_byte ((inst), 1, 0, AMD64_RAX); \ + x86_imm_emit8 ((inst), 0); \ + break; \ + /* nop 0x0(%rax,%rax) */ \ + case 5: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ + x86_address_byte ((inst), 1, 0, 4); \ + x86_address_byte ((inst), 0, AMD64_RAX, AMD64_RAX); \ + x86_imm_emit8 ((inst), 0); \ + break; \ + /* nopw 0x0(%rax,%rax) */ \ + case 6: *(inst)++ = 0x66; *(inst)++ = 0x0f; \ + *(inst)++ = 0x1f; \ + x86_address_byte ((inst), 1, 0, 4); \ + x86_address_byte ((inst), 0, AMD64_RAX, AMD64_RAX); \ + x86_imm_emit8 ((inst), 0); \ + break; \ + /* nop 0x0(%rax) (32-bit displacement) */ \ + case 7: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ + x86_address_byte ((inst), 2, 0, AMD64_RAX); \ + x86_imm_emit32((inst), 0); \ + break; \ + /* nop 0x0(%rax,%rax) (32-bit displacement) */ \ + case 8: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ + x86_address_byte ((inst), 2, 0, 4); \ + x86_address_byte ((inst), 0, AMD64_RAX, AMD64_RAX); \ + x86_imm_emit32 ((inst), 0); \ + break; \ + default: \ + g_assert_not_reached(); \ + } \ + g_assert(code_start + (size) == (unsigned char *)(inst)); \ + } while (0) + + +/* Size is ignored for Native Client calls, we restrict jumping to 32-bits */ +#define amd64_call_membase_size(inst,basereg,disp,size) \ + do { \ + amd64_codegen_pre((inst)); \ + amd64_call_sequence_pre(inst); \ + amd64_mov_reg_membase((inst), AMD64_R11, (basereg), (disp), 4); \ + amd64_call_reg_internal((inst), AMD64_R11); \ + amd64_call_sequence_post(inst); \ + amd64_codegen_post((inst)); \ + } while (0) + +/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ +#define amd64_jump_membase_size(inst,basereg,disp,size) \ + do { \ + amd64_mov_reg_membase((inst), AMD64_R11, (basereg), (disp), 4); \ + amd64_jump_reg_size((inst), AMD64_R11, 4); \ + } while (0) + +/* On Native Client we can't jump more than INT_MAX in either direction */ +#define amd64_jump_code_size(inst,target,size) \ + do { \ + /* x86_jump_code used twice in case of */ \ + /* relocation by amd64_codegen_post */ \ + guint8* jump_start; \ + amd64_codegen_pre(inst); \ + assert(amd64_is_imm32 ((gint64)(target) - (gint64)(inst))); \ + x86_jump_code((inst),(target)); \ + inst = amd64_codegen_post(inst); \ + jump_start = (inst); \ + x86_jump_code((inst),(target)); \ + mono_amd64_patch(jump_start, (target)); \ +} while (0) + +#endif /*__native_client_codegen__*/ + +/* + * SSE + */ + +//TODO Reorganize SSE opcode defines. + +/* Two opcode SSE defines */ + +#define emit_sse_reg_reg_op2_size(inst,dreg,reg,op1,op2,size) do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ + *(inst)++ = (unsigned char)(op1); \ + *(inst)++ = (unsigned char)(op2); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_reg_op2(inst,dreg,reg,op1,op2) emit_sse_reg_reg_op2_size ((inst), (dreg), (reg), (op1), (op2), 0) + +#define emit_sse_reg_reg_op2_imm(inst,dreg,reg,op1,op2,imm) do { \ + amd64_codegen_pre(inst); \ + emit_sse_reg_reg_op2 ((inst), (dreg), (reg), (op1), (op2)); \ + x86_imm_emit8 ((inst), (imm)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_membase_reg_op2(inst,basereg,disp,reg,op1,op2) do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst), 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)(op1); \ + *(inst)++ = (unsigned char)(op2); \ + amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_membase_op2(inst,dreg,basereg,disp,op1,op2) do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst), 0, (dreg), 0, (basereg) == AMD64_RIP ? 0 : (basereg)); \ + *(inst)++ = (unsigned char)(op1); \ + *(inst)++ = (unsigned char)(op2); \ + amd64_membase_emit ((inst), (dreg), (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +/* Three opcode SSE defines */ + +#define emit_opcode3(inst,op1,op2,op3) do { \ + *(inst)++ = (unsigned char)(op1); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ +} while (0) + +#define emit_sse_reg_reg_size(inst,dreg,reg,op1,op2,op3,size) do { \ + amd64_codegen_pre(inst); \ + *(inst)++ = (unsigned char)(op1); \ + amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_reg(inst,dreg,reg,op1,op2,op3) emit_sse_reg_reg_size ((inst), (dreg), (reg), (op1), (op2), (op3), 0) + +#define emit_sse_reg_reg_imm(inst,dreg,reg,op1,op2,op3,imm) do { \ + amd64_codegen_pre(inst); \ + emit_sse_reg_reg ((inst), (dreg), (reg), (op1), (op2), (op3)); \ + x86_imm_emit8 ((inst), (imm)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_membase_reg(inst,basereg,disp,reg,op1,op2,op3) do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), (unsigned char)(op1)); \ + amd64_emit_rex ((inst), 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ + amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_membase(inst,dreg,basereg,disp,op1,op2,op3) do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), (unsigned char)(op1)); \ + amd64_emit_rex ((inst), 0, (dreg), 0, (basereg) == AMD64_RIP ? 0 : (basereg)); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ + amd64_membase_emit ((inst), (dreg), (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +/* Four opcode SSE defines */ + +#define emit_sse_reg_reg_op4_size(inst,dreg,reg,op1,op2,op3,op4,size) do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), (unsigned char)(op1)); \ + amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ + *(inst)++ = (unsigned char)(op4); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_reg_op4(inst,dreg,reg,op1,op2,op3,op4) emit_sse_reg_reg_op4_size ((inst), (dreg), (reg), (op1), (op2), (op3), (op4), 0) + +/* specific SSE opcode defines */ + +#define amd64_sse_xorpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg), 0x66, 0x0f, 0x57) + +#define amd64_sse_xorpd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst),(dreg),(basereg), (disp), 0x66, 0x0f, 0x57) + +#define amd64_sse_andpd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst),(dreg),(basereg), (disp), 0x66, 0x0f, 0x54) + +#define amd64_sse_movsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x10) + +#define amd64_sse_movsd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0xf2, 0x0f, 0x10) + +#define amd64_sse_movsd_membase_reg(inst,basereg,disp,reg) emit_sse_membase_reg ((inst), (basereg), (disp), (reg), 0xf2, 0x0f, 0x11) + +#define amd64_sse_movss_membase_reg(inst,basereg,disp,reg) emit_sse_membase_reg ((inst), (basereg), (disp), (reg), 0xf3, 0x0f, 0x11) + +#define amd64_sse_movss_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0xf3, 0x0f, 0x10) + +#define amd64_sse_comisd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg),0x66,0x0f,0x2f) + +#define amd64_sse_comisd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0x66, 0x0f, 0x2f) + +#define amd64_sse_ucomisd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg),0x66,0x0f,0x2e) + +#define amd64_sse_cvtsd2si_reg_reg(inst,dreg,reg) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2d, 8) + +#define amd64_sse_cvttsd2si_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2c, (size)) + +#define amd64_sse_cvttsd2si_reg_reg(inst,dreg,reg) amd64_sse_cvttsd2si_reg_reg_size ((inst), (dreg), (reg), 8) + +#define amd64_sse_cvtsi2sd_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2a, (size)) + +#define amd64_sse_cvtsi2sd_reg_reg(inst,dreg,reg) amd64_sse_cvtsi2sd_reg_reg_size ((inst), (dreg), (reg), 8) + +#define amd64_sse_cvtsd2ss_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5a) + +#define amd64_sse_cvtss2sd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf3, 0x0f, 0x5a) + +#define amd64_sse_addsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x58) + +#define amd64_sse_subsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5c) + +#define amd64_sse_mulsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x59) + +#define amd64_sse_divsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5e) + +#define amd64_sse_sqrtsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x51) + + +#define amd64_sse_pinsrw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm ((inst), (dreg), (reg), 0x66, 0x0f, 0xc4, (imm)) + +#define amd64_sse_pextrw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm ((inst), (dreg), (reg), 0x66, 0x0f, 0xc5, (imm)) + + +#define amd64_sse_cvttsd2si_reg_xreg_size(inst,reg,xreg,size) emit_sse_reg_reg_size ((inst), (reg), (xreg), 0xf2, 0x0f, 0x2c, (size)) + + +#define amd64_sse_addps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x58) + +#define amd64_sse_divps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5e) + +#define amd64_sse_mulps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x59) + +#define amd64_sse_subps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5c) + +#define amd64_sse_maxps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5f) + +#define amd64_sse_minps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5d) + +#define amd64_sse_cmpps_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_op2_imm((inst), (dreg), (reg), 0x0f, 0xc2, (imm)) + +#define amd64_sse_andps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x54) + +#define amd64_sse_andnps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x55) + +#define amd64_sse_orps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x56) + +#define amd64_sse_xorps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x57) + +#define amd64_sse_sqrtps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x51) + +#define amd64_sse_rsqrtps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x52) + +#define amd64_sse_rcpps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x53) + +#define amd64_sse_addsubps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0xd0) + +#define amd64_sse_haddps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x7c) + +#define amd64_sse_hsubps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x7d) + +#define amd64_sse_movshdup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf3, 0x0f, 0x16) + +#define amd64_sse_movsldup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf3, 0x0f, 0x12) + + +#define amd64_sse_pshufhw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0xf3, 0x0f, 0x70, (imm)) + +#define amd64_sse_pshuflw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0xf2, 0x0f, 0x70, (imm)) + +#define amd64_sse_pshufd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0x70, (imm)) + +#define amd64_sse_shufps_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_op2_imm((inst), (dreg), (reg), 0x0f, 0xC6, (imm)) + +#define amd64_sse_shufpd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0xC6, (imm)) + + +#define amd64_sse_addpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x58) + +#define amd64_sse_divpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5e) + +#define amd64_sse_mulpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x59) + +#define amd64_sse_subpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5c) + +#define amd64_sse_maxpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5f) + +#define amd64_sse_minpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5d) + +#define amd64_sse_cmppd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0xc2, (imm)) + +#define amd64_sse_andpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x54) + +#define amd64_sse_andnpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x55) + +#define amd64_sse_orpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x56) + +#define amd64_sse_sqrtpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x51) + +#define amd64_sse_rsqrtpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x52) + +#define amd64_sse_rcppd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x53) + +#define amd64_sse_addsubpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd0) + +#define amd64_sse_haddpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x7c) + +#define amd64_sse_hsubpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x7d) + +#define amd64_sse_movddup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x12) + + +#define amd64_sse_pmovmskb_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd7) + + +#define amd64_sse_pand_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdb) + +#define amd64_sse_por_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xeb) + +#define amd64_sse_pxor_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xef) + + +#define amd64_sse_paddb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfc) + +#define amd64_sse_paddw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfd) + +#define amd64_sse_paddd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfe) + +#define amd64_sse_paddq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd4) + + +#define amd64_sse_psubb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf8) + +#define amd64_sse_psubw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf9) + +#define amd64_sse_psubd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfa) + +#define amd64_sse_psubq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfb) + + +#define amd64_sse_pmaxub_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xde) + +#define amd64_sse_pmaxuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3e) + +#define amd64_sse_pmaxud_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3f) + + +#define amd64_sse_pmaxsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3c) + +#define amd64_sse_pmaxsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xee) + +#define amd64_sse_pmaxsd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3d) + + +#define amd64_sse_pavgb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe0) + +#define amd64_sse_pavgw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe3) + + +#define amd64_sse_pminub_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xda) + +#define amd64_sse_pminuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3a) + +#define amd64_sse_pminud_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3b) + + +#define amd64_sse_pminsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x38) + +#define amd64_sse_pminsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xea) + +#define amd64_sse_pminsd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x39) + + +#define amd64_sse_pcmpeqb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x74) + +#define amd64_sse_pcmpeqw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x75) + +#define amd64_sse_pcmpeqd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x76) + +#define amd64_sse_pcmpeqq_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x29) + + +#define amd64_sse_pcmpgtb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x64) + +#define amd64_sse_pcmpgtw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x65) + +#define amd64_sse_pcmpgtd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x66) + +#define amd64_sse_pcmpgtq_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x37) + + +#define amd64_sse_psadbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf6) + + +#define amd64_sse_punpcklbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x60) + +#define amd64_sse_punpcklwd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x61) + +#define amd64_sse_punpckldq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x62) + +#define amd64_sse_punpcklqdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6c) + +#define amd64_sse_unpcklpd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x14) + +#define amd64_sse_unpcklps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x14) + + +#define amd64_sse_punpckhbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x68) + +#define amd64_sse_punpckhwd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x69) + +#define amd64_sse_punpckhdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6a) + +#define amd64_sse_punpckhqdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6d) + +#define amd64_sse_unpckhpd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x15) + +#define amd64_sse_unpckhps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x15) + + +#define amd64_sse_packsswb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x63) + +#define amd64_sse_packssdw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6b) + +#define amd64_sse_packuswb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x67) + +#define amd64_sse_packusdw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x2b) + + +#define amd64_sse_paddusb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdc) + +#define amd64_sse_psubusb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd8) + +#define amd64_sse_paddusw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdd) + +#define amd64_sse_psubusw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd8) + + +#define amd64_sse_paddsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xec) + +#define amd64_sse_psubsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe8) + +#define amd64_sse_paddsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xed) + +#define amd64_sse_psubsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe9) + + +#define amd64_sse_pmullw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd5) + +#define amd64_sse_pmulld_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x40) + +#define amd64_sse_pmuludq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf4) + +#define amd64_sse_pmulhuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe4) + +#define amd64_sse_pmulhw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe5) + + +#define amd64_sse_psrlw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x71, (imm)) + +#define amd64_sse_psrlw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd1) + + +#define amd64_sse_psraw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x71, (imm)) + +#define amd64_sse_psraw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe1) + + +#define amd64_sse_psllw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x71, (imm)) + +#define amd64_sse_psllw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf1) + + +#define amd64_sse_psrld_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x72, (imm)) + +#define amd64_sse_psrld_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd2) + + +#define amd64_sse_psrad_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x72, (imm)) + +#define amd64_sse_psrad_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe2) + + +#define amd64_sse_pslld_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x72, (imm)) + +#define amd64_sse_pslld_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf2) + + +#define amd64_sse_psrlq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x73, (imm)) + +#define amd64_sse_psrlq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd3) + + +#define amd64_sse_psraq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x73, (imm)) + +#define amd64_sse_psraq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe3) + + +#define amd64_sse_psllq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x73, (imm)) + +#define amd64_sse_psllq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf3) + + +#define amd64_sse_cvtdq2pd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF3, 0x0F, 0xE6) + +#define amd64_sse_cvtdq2ps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0F, 0x5B) + +#define amd64_sse_cvtpd2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF2, 0x0F, 0xE6) + +#define amd64_sse_cvtpd2ps_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0x5A) + +#define amd64_sse_cvtps2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0x5B) + +#define amd64_sse_cvtps2pd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0F, 0x5A) + +#define amd64_sse_cvttpd2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0xE6) + +#define amd64_sse_cvttps2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF3, 0x0F, 0x5B) + + +#define amd64_movd_xreg_reg_size(inst,dreg,sreg,size) emit_sse_reg_reg_size((inst), (dreg), (sreg), 0x66, 0x0f, 0x6e, (size)) + +#define amd64_movd_reg_xreg_size(inst,dreg,sreg,size) emit_sse_reg_reg_size((inst), (sreg), (dreg), 0x66, 0x0f, 0x7e, (size)) + +#define amd64_movd_xreg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase((inst), (dreg), (basereg), (disp), 0x66, 0x0f, 0x6e) + + +#define amd64_movlhps_reg_reg(inst,dreg,sreg) emit_sse_reg_reg_op2((inst), (dreg), (sreg), 0x0f, 0x16) + +#define amd64_movhlps_reg_reg(inst,dreg,sreg) emit_sse_reg_reg_op2((inst), (dreg), (sreg), 0x0f, 0x12) + + +#define amd64_sse_movups_membase_reg(inst, basereg, disp, reg) emit_sse_membase_reg_op2((inst), (basereg), (disp), (reg), 0x0f, 0x11) + +#define amd64_sse_movups_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x10) + +#define amd64_sse_movaps_membase_reg(inst, basereg, disp, reg) emit_sse_membase_reg_op2((inst), (basereg), (disp), (reg), 0x0f, 0x29) + +#define amd64_sse_movaps_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x28) + +#define amd64_sse_movaps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x28) + +#define amd64_sse_movntps_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x2b) + +#define amd64_sse_prefetch_reg_membase(inst, arg, basereg, disp) emit_sse_reg_membase_op2((inst), (arg), (basereg), (disp), 0x0f, 0x18) + +/* Generated from x86-codegen.h */ + +#define amd64_breakpoint_size(inst,size) do { x86_breakpoint(inst); } while (0) +#define amd64_cld_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_cld(inst); amd64_codegen_post(inst); } while (0) +#define amd64_stosb_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosb(inst); amd64_codegen_post(inst); } while (0) +#define amd64_stosl_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosl(inst); amd64_codegen_post(inst); } while (0) +#define amd64_stosd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosd(inst); amd64_codegen_post(inst); } while (0) +#define amd64_movsb_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsb(inst); amd64_codegen_post(inst); } while (0) +#define amd64_movsl_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsl(inst); amd64_codegen_post(inst); } while (0) +#define amd64_movsd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsd(inst); amd64_codegen_post(inst); } while (0) +#define amd64_prefix_size(inst,p,size) do { x86_prefix((inst), p); } while (0) +#define amd64_rdtsc_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_rdtsc(inst); amd64_codegen_post(inst); } while (0) +#define amd64_cmpxchg_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_cmpxchg_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_cmpxchg_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_cmpxchg_mem_reg((inst),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_cmpxchg_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_cmpxchg_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_xchg_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_xchg_reg_reg((inst),((dreg)&0x7),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_xchg_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_xchg_mem_reg((inst),(mem),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_xchg_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_xchg_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_inc_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_inc_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_inc_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_inc_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +//#define amd64_inc_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_inc_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_dec_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_dec_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_dec_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_dec_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +//#define amd64_dec_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_dec_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_not_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_not_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_not_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_not_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_not_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_not_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_neg_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_neg_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_neg_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_neg_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_neg_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_neg_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_nop_size(inst,size) do { amd64_codegen_pre(inst); x86_nop(inst); amd64_codegen_post(inst); } while (0) +//#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_reg_imm((inst),(opc),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_mem_imm_size(inst,opc,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_alu_mem_imm((inst),(opc),(mem),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_membase_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_alu_membase_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_membase8_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_alu_membase8_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_mem_reg_size(inst,opc,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_mem_reg((inst),(opc),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_membase_reg_size(inst,opc,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_alu_membase_reg((inst),(opc),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +//#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_alu_reg_reg((inst),(opc),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_reg8_reg8_size(inst,opc,dreg,reg,is_dreg_h,is_reg_h,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_alu_reg8_reg8((inst),(opc),((dreg)&0x7),((reg)&0x7),(is_dreg_h),(is_reg_h)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_reg_mem_size(inst,opc,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_reg_mem((inst),(opc),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) +//#define amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_alu_reg_membase((inst),(opc),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_test_reg_imm_size(inst,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_test_reg_imm((inst),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_test_mem_imm_size(inst,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_test_mem_imm((inst),(mem),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_test_membase_imm_size(inst,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_test_membase_imm((inst),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_test_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_test_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_test_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_test_mem_reg((inst),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_test_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_test_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_reg_imm_size(inst,opc,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_reg_imm((inst),(opc),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_mem_imm_size(inst,opc,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_mem_imm((inst),(opc),(mem),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_membase_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_shift_membase_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_reg_size(inst,opc,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_reg((inst),(opc),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_mem_size(inst,opc,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_mem((inst),(opc),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_membase_size(inst,opc,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_shift_membase((inst),(opc),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_shrd_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shrd_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_shrd_reg_imm_size(inst,dreg,reg,shamt,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shrd_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(shamt)); amd64_codegen_post(inst); } while (0) +#define amd64_shld_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shld_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_shld_reg_imm_size(inst,dreg,reg,shamt,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shld_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(shamt)); amd64_codegen_post(inst); } while (0) +#define amd64_mul_reg_size(inst,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mul_reg((inst),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_mul_mem_size(inst,mem,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_mul_mem((inst),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_mul_membase_size(inst,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_mul_membase((inst),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_imul_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_imul_reg_mem((inst),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_imul_reg_membase((inst),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_reg_imm_size(inst,dreg,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_imul_reg_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_mem_imm_size(inst,reg,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_imul_reg_mem_imm((inst),((reg)&0x7),(mem),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_membase_imm_size(inst,reg,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_imul_reg_membase_imm((inst),((reg)&0x7),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_div_reg_size(inst,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_div_reg((inst),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_div_mem_size(inst,mem,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_div_mem((inst),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_div_membase_size(inst,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_div_membase((inst),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_mem_reg((inst),(mem),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_regp_reg_size(inst,regp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(regp),0,(reg)); x86_mov_regp_reg((inst),(regp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_mov_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_memindex_reg_size(inst,basereg,disp,indexreg,shift,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_mov_memindex_reg((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_mov_reg_reg((inst),((dreg)&0x7),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_reg_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_reg_mem((inst),((reg)&0x7),(mem),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_reg_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_mov_reg_membase((inst),((reg)&0x7),((basereg)&0x7),(disp),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_mov_reg_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_clear_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_clear_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_reg_imm_size(inst,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_reg_imm((inst),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_mem_imm_size(inst,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_mov_mem_imm((inst),(mem),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_membase_imm_size(inst,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_mov_membase_imm((inst),((basereg)&0x7),(disp),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_memindex_imm_size(inst,basereg,disp,indexreg,shift,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,(indexreg),(basereg)); x86_mov_memindex_imm((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_lea_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_lea_mem((inst),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) +//#define amd64_lea_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_lea_membase((inst),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_lea_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_lea_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift)); amd64_codegen_post(inst); } while (0) +#define amd64_widen_reg_size(inst,dreg,reg,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_widen_reg((inst),((dreg)&0x7),((reg)&0x7),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) +#define amd64_widen_mem_size(inst,dreg,mem,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,0); x86_widen_mem((inst),((dreg)&0x7),(mem),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) +#define amd64_widen_membase_size(inst,dreg,basereg,disp,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(basereg)); x86_widen_membase((inst),((dreg)&0x7),((basereg)&0x7),(disp),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) +#define amd64_widen_memindex_size(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),(indexreg),(basereg)); x86_widen_memindex((inst),((dreg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) +#define amd64_cdq_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_cdq(inst); amd64_codegen_post(inst); } while (0) +#define amd64_wait_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_wait(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fp_op_mem_size(inst,opc,mem,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op_mem((inst),(opc),(mem),(is_double)); amd64_codegen_post(inst); } while (0) +#define amd64_fp_op_membase_size(inst,opc,basereg,disp,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fp_op_membase((inst),(opc),((basereg)&0x7),(disp),(is_double)); amd64_codegen_post(inst); } while (0) +#define amd64_fp_op_size(inst,opc,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op((inst),(opc),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fp_op_reg_size(inst,opc,index,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op_reg((inst),(opc),(index),(pop_stack)); amd64_codegen_post(inst); } while (0) +#define amd64_fp_int_op_membase_size(inst,opc,basereg,disp,is_int,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fp_int_op_membase((inst),(opc),((basereg)&0x7),(disp),(is_int)); amd64_codegen_post(inst); } while (0) +#define amd64_fstp_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fstp((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fcompp_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcompp(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fucompp_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucompp(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fnstsw_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fnstsw(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fnstcw_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fnstcw((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_fnstcw_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_fnstcw_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_fldcw_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldcw((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_fldcw_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fldcw_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_fchs_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fchs(inst); amd64_codegen_post(inst); } while (0) +#define amd64_frem_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_frem(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fxch_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fxch((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fcomi_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcomi((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fcomip_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcomip((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fucomi_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucomi((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fucomip_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucomip((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fld_size(inst,mem,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld((inst),(mem),(is_double)); amd64_codegen_post(inst); } while (0) +//#define amd64_fld_membase_size(inst,basereg,disp,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fld_membase((inst),((basereg)&0x7),(disp),(is_double)); amd64_codegen_post(inst); } while (0) +#define amd64_fld80_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld80_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_fld80_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_fld80_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_fild_size(inst,mem,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fild((inst),(mem),(is_long)); amd64_codegen_post(inst); } while (0) +#define amd64_fild_membase_size(inst,basereg,disp,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fild_membase((inst),((basereg)&0x7),(disp),(is_long)); amd64_codegen_post(inst); } while (0) +#define amd64_fld_reg_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld_reg((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fldz_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldz(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fld1_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld1(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fldpi_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldpi(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fst_size(inst,mem,is_double,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fst((inst),(mem),(is_double),(pop_stack)); amd64_codegen_post(inst); } while (0) +#define amd64_fst_membase_size(inst,basereg,disp,is_double,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fst_membase((inst),((basereg)&0x7),(disp),(is_double),(pop_stack)); amd64_codegen_post(inst); } while (0) +#define amd64_fst80_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fst80_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_fst80_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fst80_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_fist_pop_size(inst,mem,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fist_pop((inst),(mem),(is_long)); amd64_codegen_post(inst); } while (0) +#define amd64_fist_pop_membase_size(inst,basereg,disp,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fist_pop_membase((inst),((basereg)&0x7),(disp),(is_long)); amd64_codegen_post(inst); } while (0) +#define amd64_fstsw_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_fstsw(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fist_membase_size(inst,basereg,disp,is_int,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fist_membase((inst),((basereg)&0x7),(disp),(is_int)); amd64_codegen_post(inst); } while (0) +//#define amd64_push_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_push_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_push_regp_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_push_regp((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_push_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_push_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +//#define amd64_push_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_push_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_push_memindex_size(inst,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,(indexreg),(basereg)); x86_push_memindex((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift)); amd64_codegen_post(inst); } while (0) +#define amd64_push_imm_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_push_imm((inst),(imm)); amd64_codegen_post(inst); } while (0) +//#define amd64_pop_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_pop_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_pop_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pop_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_pop_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_pop_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_pushad_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pushad(inst); amd64_codegen_post(inst); } while (0) +#define amd64_pushfd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pushfd(inst); amd64_codegen_post(inst); } while (0) +#define amd64_popad_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_popad(inst); amd64_codegen_post(inst); } while (0) +#define amd64_popfd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_popfd(inst); amd64_codegen_post(inst); } while (0) +#define amd64_loop_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loop((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_loope_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loope((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_loopne_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loopne((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_jump32_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_jump32((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_jump8_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_jump8((inst),(imm)); amd64_codegen_post(inst); } while (0) +#if !defined( __native_client_codegen__ ) +/* Defined above for Native Client, so they can be used in other macros */ +#define amd64_jump_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),0,0,0,(reg)); x86_jump_reg((inst),((reg)&0x7)); } while (0) +#define amd64_jump_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_jump_mem((inst),(mem)); } while (0) +#endif +#define amd64_jump_disp_size(inst,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_jump_disp((inst),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_branch8_size(inst,cond,imm,is_signed,size) do { x86_branch8((inst),(cond),(imm),(is_signed)); } while (0) +#define amd64_branch32_size(inst,cond,imm,is_signed,size) do { x86_branch32((inst),(cond),(imm),(is_signed)); } while (0) +#define amd64_branch_size_body(inst,cond,target,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_branch((inst),(cond),(target),(is_signed)); amd64_codegen_post(inst); } while (0) +#if defined(__default_codegen__) +#define amd64_branch_size(inst,cond,target,is_signed,size) do { amd64_branch_size_body((inst),(cond),(target),(is_signed),(size)); } while (0) +#elif defined(__native_client_codegen__) +#define amd64_branch_size(inst,cond,target,is_signed,size) \ + do { \ + /* amd64_branch_size_body used twice in */ \ + /* case of relocation by amd64_codegen_post */ \ + guint8* branch_start; \ + amd64_codegen_pre(inst); \ + amd64_branch_size_body((inst),(cond),(target),(is_signed),(size)); \ + inst = amd64_codegen_post(inst); \ + branch_start = inst; \ + amd64_branch_size_body((inst),(cond),(target),(is_signed),(size)); \ + mono_amd64_patch(branch_start, (target)); \ + } while (0) +#endif + +#define amd64_branch_disp_size(inst,cond,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_branch_disp((inst),(cond),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_set_reg_size(inst,cond,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex((inst),1,0,0,(reg)); x86_set_reg((inst),(cond),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_set_mem_size(inst,cond,mem,is_signed,size) do { amd64_codegen_pre(inst); x86_set_mem((inst),(cond),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_set_membase_size(inst,cond,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_set_membase((inst),(cond),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) +//#define amd64_call_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_call_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_call_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_call_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) + +#if defined(__default_codegen__) + +#define amd64_call_imm_size(inst,disp,size) do { x86_call_imm((inst),(disp)); } while (0) +#define amd64_call_code_size(inst,target,size) do { x86_call_code((inst),(target)); } while (0) + +#elif defined(__native_client_codegen__) +/* Size is ignored for Native Client calls, we restrict jumping to 32-bits */ +#define amd64_call_imm_size(inst,disp,size) \ + do { \ + amd64_codegen_pre((inst)); \ + amd64_call_sequence_pre((inst)); \ + x86_call_imm((inst),(disp)); \ + amd64_call_sequence_post((inst)); \ + amd64_codegen_post((inst)); \ + } while (0) + +/* x86_call_code is called twice below, first so we can get the size of the */ +/* call sequence, and again so the exact offset from "inst" is used, since */ +/* the sequence could have moved from amd64_call_sequence_post. */ +/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ +#define amd64_call_code_size(inst,target,size) \ + do { \ + amd64_codegen_pre((inst)); \ + guint8* adjusted_start; \ + guint8* call_start; \ + amd64_call_sequence_pre((inst)); \ + x86_call_code((inst),(target)); \ + adjusted_start = amd64_call_sequence_post((inst)); \ + call_start = adjusted_start; \ + x86_call_code(adjusted_start, (target)); \ + amd64_codegen_post((inst)); \ + mono_amd64_patch(call_start, (target)); \ + } while (0) + +#endif /*__native_client_codegen__*/ + +//#define amd64_ret_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_ret(inst); amd64_codegen_post(inst); } while (0) +#define amd64_ret_imm_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_ret_imm((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_cmov_reg_size(inst,cond,is_signed,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_cmov_reg((inst),(cond),(is_signed),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_cmov_mem_size(inst,cond,is_signed,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_cmov_mem((inst),(cond),(is_signed),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_cmov_membase_size(inst,cond,is_signed,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_cmov_membase((inst),(cond),(is_signed),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_enter_size(inst,framesize) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_enter((inst),(framesize)); amd64_codegen_post(inst); } while (0) +//#define amd64_leave_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_leave(inst); amd64_codegen_post(inst); } while (0) +#define amd64_sahf_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_sahf(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fsin_size(inst,size) do { amd64_codegen_pre(inst); x86_fsin(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fcos_size(inst,size) do { amd64_codegen_pre(inst); x86_fcos(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fabs_size(inst,size) do { amd64_codegen_pre(inst); x86_fabs(inst); amd64_codegen_post(inst); } while (0) +#define amd64_ftst_size(inst,size) do { amd64_codegen_pre(inst); x86_ftst(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fxam_size(inst,size) do { amd64_codegen_pre(inst); x86_fxam(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fpatan_size(inst,size) do { amd64_codegen_pre(inst); x86_fpatan(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fprem_size(inst,size) do { amd64_codegen_pre(inst); x86_fprem(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fprem1_size(inst,size) do { amd64_codegen_pre(inst); x86_fprem1(inst); amd64_codegen_post(inst); } while (0) +#define amd64_frndint_size(inst,size) do { amd64_codegen_pre(inst); x86_frndint(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fsqrt_size(inst,size) do { amd64_codegen_pre(inst); x86_fsqrt(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fptan_size(inst,size) do { amd64_codegen_pre(inst); x86_fptan(inst); amd64_codegen_post(inst); } while (0) +//#define amd64_padding_size(inst,size) do { amd64_codegen_pre(inst); x86_padding((inst),(size)); amd64_codegen_post(inst); } while (0) +#define amd64_prolog_size(inst,frame_size,reg_mask,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_prolog((inst),(frame_size),(reg_mask)); amd64_codegen_post(inst); } while (0) +#define amd64_epilog_size(inst,reg_mask,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_epilog((inst),(reg_mask)); amd64_codegen_post(inst); } while (0) +#define amd64_xadd_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_xadd_reg_reg ((inst), (dreg), (reg), (size)); amd64_codegen_post(inst); } while (0) +#define amd64_xadd_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_xadd_mem_reg((inst),(mem),((reg)&0x7), (size)); amd64_codegen_post(inst); } while (0) +#define amd64_xadd_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_xadd_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size)); amd64_codegen_post(inst); } while (0) + + + + +#define amd64_breakpoint(inst) amd64_breakpoint_size(inst,8) +#define amd64_cld(inst) amd64_cld_size(inst,8) +#define amd64_stosb(inst) amd64_stosb_size(inst,8) +#define amd64_stosl(inst) amd64_stosl_size(inst,8) +#define amd64_stosd(inst) amd64_stosd_size(inst,8) +#define amd64_movsb(inst) amd64_movsb_size(inst,8) +#define amd64_movsl(inst) amd64_movsl_size(inst,8) +#define amd64_movsd(inst) amd64_movsd_size(inst,8) +#define amd64_prefix(inst,p) amd64_prefix_size(inst,p,8) +#define amd64_rdtsc(inst) amd64_rdtsc_size(inst,8) +#define amd64_cmpxchg_reg_reg(inst,dreg,reg) amd64_cmpxchg_reg_reg_size(inst,dreg,reg,8) +#define amd64_cmpxchg_mem_reg(inst,mem,reg) amd64_cmpxchg_mem_reg_size(inst,mem,reg,8) +#define amd64_cmpxchg_membase_reg(inst,basereg,disp,reg) amd64_cmpxchg_membase_reg_size(inst,basereg,disp,reg,8) +#define amd64_xchg_reg_reg(inst,dreg,reg,size) amd64_xchg_reg_reg_size(inst,dreg,reg,size) +#define amd64_xchg_mem_reg(inst,mem,reg,size) amd64_xchg_mem_reg_size(inst,mem,reg,size) +#define amd64_xchg_membase_reg(inst,basereg,disp,reg,size) amd64_xchg_membase_reg_size(inst,basereg,disp,reg,size) +#define amd64_xadd_reg_reg(inst,dreg,reg,size) amd64_xadd_reg_reg_size(inst,dreg,reg,size) +#define amd64_xadd_mem_reg(inst,mem,reg,size) amd64_xadd_mem_reg_size(inst,mem,reg,size) +#define amd64_xadd_membase_reg(inst,basereg,disp,reg,size) amd64_xadd_membase_reg_size(inst,basereg,disp,reg,size) +#define amd64_inc_mem(inst,mem) amd64_inc_mem_size(inst,mem,8) +#define amd64_inc_membase(inst,basereg,disp) amd64_inc_membase_size(inst,basereg,disp,8) +#define amd64_inc_reg(inst,reg) amd64_inc_reg_size(inst,reg,8) +#define amd64_dec_mem(inst,mem) amd64_dec_mem_size(inst,mem,8) +#define amd64_dec_membase(inst,basereg,disp) amd64_dec_membase_size(inst,basereg,disp,8) +#define amd64_dec_reg(inst,reg) amd64_dec_reg_size(inst,reg,8) +#define amd64_not_mem(inst,mem) amd64_not_mem_size(inst,mem,8) +#define amd64_not_membase(inst,basereg,disp) amd64_not_membase_size(inst,basereg,disp,8) +#define amd64_not_reg(inst,reg) amd64_not_reg_size(inst,reg,8) +#define amd64_neg_mem(inst,mem) amd64_neg_mem_size(inst,mem,8) +#define amd64_neg_membase(inst,basereg,disp) amd64_neg_membase_size(inst,basereg,disp,8) +#define amd64_neg_reg(inst,reg) amd64_neg_reg_size(inst,reg,8) +#define amd64_nop(inst) amd64_nop_size(inst,8) +//#define amd64_alu_reg_imm(inst,opc,reg,imm) amd64_alu_reg_imm_size(inst,opc,reg,imm,8) +#define amd64_alu_mem_imm(inst,opc,mem,imm) amd64_alu_mem_imm_size(inst,opc,mem,imm,8) +#define amd64_alu_membase_imm(inst,opc,basereg,disp,imm) amd64_alu_membase_imm_size(inst,opc,basereg,disp,imm,8) +#define amd64_alu_mem_reg(inst,opc,mem,reg) amd64_alu_mem_reg_size(inst,opc,mem,reg,8) +#define amd64_alu_membase_reg(inst,opc,basereg,disp,reg) amd64_alu_membase_reg_size(inst,opc,basereg,disp,reg,8) +//#define amd64_alu_reg_reg(inst,opc,dreg,reg) amd64_alu_reg_reg_size(inst,opc,dreg,reg,8) +#define amd64_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) amd64_alu_reg8_reg8_size(inst,opc,dreg,reg,is_dreg_h,is_reg_h,8) +#define amd64_alu_reg_mem(inst,opc,reg,mem) amd64_alu_reg_mem_size(inst,opc,reg,mem,8) +#define amd64_alu_reg_membase(inst,opc,reg,basereg,disp) amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,8) +#define amd64_test_reg_imm(inst,reg,imm) amd64_test_reg_imm_size(inst,reg,imm,8) +#define amd64_test_mem_imm(inst,mem,imm) amd64_test_mem_imm_size(inst,mem,imm,8) +#define amd64_test_membase_imm(inst,basereg,disp,imm) amd64_test_membase_imm_size(inst,basereg,disp,imm,8) +#define amd64_test_reg_reg(inst,dreg,reg) amd64_test_reg_reg_size(inst,dreg,reg,8) +#define amd64_test_mem_reg(inst,mem,reg) amd64_test_mem_reg_size(inst,mem,reg,8) +#define amd64_test_membase_reg(inst,basereg,disp,reg) amd64_test_membase_reg_size(inst,basereg,disp,reg,8) +#define amd64_shift_reg_imm(inst,opc,reg,imm) amd64_shift_reg_imm_size(inst,opc,reg,imm,8) +#define amd64_shift_mem_imm(inst,opc,mem,imm) amd64_shift_mem_imm_size(inst,opc,mem,imm,8) +#define amd64_shift_membase_imm(inst,opc,basereg,disp,imm) amd64_shift_membase_imm_size(inst,opc,basereg,disp,imm,8) +#define amd64_shift_reg(inst,opc,reg) amd64_shift_reg_size(inst,opc,reg,8) +#define amd64_shift_mem(inst,opc,mem) amd64_shift_mem_size(inst,opc,mem,8) +#define amd64_shift_membase(inst,opc,basereg,disp) amd64_shift_membase_size(inst,opc,basereg,disp,8) +#define amd64_shrd_reg(inst,dreg,reg) amd64_shrd_reg_size(inst,dreg,reg,8) +#define amd64_shrd_reg_imm(inst,dreg,reg,shamt) amd64_shrd_reg_imm_size(inst,dreg,reg,shamt,8) +#define amd64_shld_reg(inst,dreg,reg) amd64_shld_reg_size(inst,dreg,reg,8) +#define amd64_shld_reg_imm(inst,dreg,reg,shamt) amd64_shld_reg_imm_size(inst,dreg,reg,shamt,8) +#define amd64_mul_reg(inst,reg,is_signed) amd64_mul_reg_size(inst,reg,is_signed,8) +#define amd64_mul_mem(inst,mem,is_signed) amd64_mul_mem_size(inst,mem,is_signed,8) +#define amd64_mul_membase(inst,basereg,disp,is_signed) amd64_mul_membase_size(inst,basereg,disp,is_signed,8) +#define amd64_imul_reg_reg(inst,dreg,reg) amd64_imul_reg_reg_size(inst,dreg,reg,8) +#define amd64_imul_reg_mem(inst,reg,mem) amd64_imul_reg_mem_size(inst,reg,mem,8) +#define amd64_imul_reg_membase(inst,reg,basereg,disp) amd64_imul_reg_membase_size(inst,reg,basereg,disp,8) +#define amd64_imul_reg_reg_imm(inst,dreg,reg,imm) amd64_imul_reg_reg_imm_size(inst,dreg,reg,imm,8) +#define amd64_imul_reg_mem_imm(inst,reg,mem,imm) amd64_imul_reg_mem_imm_size(inst,reg,mem,imm,8) +#define amd64_imul_reg_membase_imm(inst,reg,basereg,disp,imm) amd64_imul_reg_membase_imm_size(inst,reg,basereg,disp,imm,8) +#define amd64_div_reg(inst,reg,is_signed) amd64_div_reg_size(inst,reg,is_signed,8) +#define amd64_div_mem(inst,mem,is_signed) amd64_div_mem_size(inst,mem,is_signed,8) +#define amd64_div_membase(inst,basereg,disp,is_signed) amd64_div_membase_size(inst,basereg,disp,is_signed,8) +//#define amd64_mov_mem_reg(inst,mem,reg,size) amd64_mov_mem_reg_size(inst,mem,reg,size) +//#define amd64_mov_regp_reg(inst,regp,reg,size) amd64_mov_regp_reg_size(inst,regp,reg,size) +//#define amd64_mov_membase_reg(inst,basereg,disp,reg,size) amd64_mov_membase_reg_size(inst,basereg,disp,reg,size) +#define amd64_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) amd64_mov_memindex_reg_size(inst,basereg,disp,indexreg,shift,reg,size) +//#define amd64_mov_reg_reg(inst,dreg,reg,size) amd64_mov_reg_reg_size(inst,dreg,reg,size) +//#define amd64_mov_reg_mem(inst,reg,mem,size) amd64_mov_reg_mem_size(inst,reg,mem,size) +//#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) amd64_mov_reg_membase_size(inst,reg,basereg,disp,size) +#define amd64_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) +#define amd64_clear_reg(inst,reg) amd64_clear_reg_size(inst,reg,8) +//#define amd64_mov_reg_imm(inst,reg,imm) amd64_mov_reg_imm_size(inst,reg,imm,8) +#define amd64_mov_mem_imm(inst,mem,imm,size) amd64_mov_mem_imm_size(inst,mem,imm,size) +//#define amd64_mov_membase_imm(inst,basereg,disp,imm,size) amd64_mov_membase_imm_size(inst,basereg,disp,imm,size) +#define amd64_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) amd64_mov_memindex_imm_size(inst,basereg,disp,indexreg,shift,imm,size) +#define amd64_lea_mem(inst,reg,mem) amd64_lea_mem_size(inst,reg,mem,8) +//#define amd64_lea_membase(inst,reg,basereg,disp) amd64_lea_membase_size(inst,reg,basereg,disp,8) +#define amd64_lea_memindex(inst,reg,basereg,disp,indexreg,shift) amd64_lea_memindex_size(inst,reg,basereg,disp,indexreg,shift,8) +#define amd64_widen_reg(inst,dreg,reg,is_signed,is_half) amd64_widen_reg_size(inst,dreg,reg,is_signed,is_half,8) +#define amd64_widen_mem(inst,dreg,mem,is_signed,is_half) amd64_widen_mem_size(inst,dreg,mem,is_signed,is_half,8) +#define amd64_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) amd64_widen_membase_size(inst,dreg,basereg,disp,is_signed,is_half,8) +#define amd64_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) amd64_widen_memindex_size(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half,8) +#define amd64_cdq(inst) amd64_cdq_size(inst,8) +#define amd64_wait(inst) amd64_wait_size(inst,8) +#define amd64_fp_op_mem(inst,opc,mem,is_double) amd64_fp_op_mem_size(inst,opc,mem,is_double,8) +#define amd64_fp_op_membase(inst,opc,basereg,disp,is_double) amd64_fp_op_membase_size(inst,opc,basereg,disp,is_double,8) +#define amd64_fp_op(inst,opc,index) amd64_fp_op_size(inst,opc,index,8) +#define amd64_fp_op_reg(inst,opc,index,pop_stack) amd64_fp_op_reg_size(inst,opc,index,pop_stack,8) +#define amd64_fp_int_op_membase(inst,opc,basereg,disp,is_int) amd64_fp_int_op_membase_size(inst,opc,basereg,disp,is_int,8) +#define amd64_fstp(inst,index) amd64_fstp_size(inst,index,8) +#define amd64_fcompp(inst) amd64_fcompp_size(inst,8) +#define amd64_fucompp(inst) amd64_fucompp_size(inst,8) +#define amd64_fnstsw(inst) amd64_fnstsw_size(inst,8) +#define amd64_fnstcw(inst,mem) amd64_fnstcw_size(inst,mem,8) +#define amd64_fnstcw_membase(inst,basereg,disp) amd64_fnstcw_membase_size(inst,basereg,disp,8) +#define amd64_fldcw(inst,mem) amd64_fldcw_size(inst,mem,8) +#define amd64_fldcw_membase(inst,basereg,disp) amd64_fldcw_membase_size(inst,basereg,disp,8) +#define amd64_fchs(inst) amd64_fchs_size(inst,8) +#define amd64_frem(inst) amd64_frem_size(inst,8) +#define amd64_fxch(inst,index) amd64_fxch_size(inst,index,8) +#define amd64_fcomi(inst,index) amd64_fcomi_size(inst,index,8) +#define amd64_fcomip(inst,index) amd64_fcomip_size(inst,index,8) +#define amd64_fucomi(inst,index) amd64_fucomi_size(inst,index,8) +#define amd64_fucomip(inst,index) amd64_fucomip_size(inst,index,8) +#define amd64_fld(inst,mem,is_double) amd64_fld_size(inst,mem,is_double,8) +#define amd64_fld_membase(inst,basereg,disp,is_double) amd64_fld_membase_size(inst,basereg,disp,is_double,8) +#define amd64_fld80_mem(inst,mem) amd64_fld80_mem_size(inst,mem,8) +#define amd64_fld80_membase(inst,basereg,disp) amd64_fld80_membase_size(inst,basereg,disp,8) +#define amd64_fild(inst,mem,is_long) amd64_fild_size(inst,mem,is_long,8) +#define amd64_fild_membase(inst,basereg,disp,is_long) amd64_fild_membase_size(inst,basereg,disp,is_long,8) +#define amd64_fld_reg(inst,index) amd64_fld_reg_size(inst,index,8) +#define amd64_fldz(inst) amd64_fldz_size(inst,8) +#define amd64_fld1(inst) amd64_fld1_size(inst,8) +#define amd64_fldpi(inst) amd64_fldpi_size(inst,8) +#define amd64_fst(inst,mem,is_double,pop_stack) amd64_fst_size(inst,mem,is_double,pop_stack,8) +#define amd64_fst_membase(inst,basereg,disp,is_double,pop_stack) amd64_fst_membase_size(inst,basereg,disp,is_double,pop_stack,8) +#define amd64_fst80_mem(inst,mem) amd64_fst80_mem_size(inst,mem,8) +#define amd64_fst80_membase(inst,basereg,disp) amd64_fst80_membase_size(inst,basereg,disp,8) +#define amd64_fist_pop(inst,mem,is_long) amd64_fist_pop_size(inst,mem,is_long,8) +#define amd64_fist_pop_membase(inst,basereg,disp,is_long) amd64_fist_pop_membase_size(inst,basereg,disp,is_long,8) +#define amd64_fstsw(inst) amd64_fstsw_size(inst,8) +#define amd64_fist_membase(inst,basereg,disp,is_int) amd64_fist_membase_size(inst,basereg,disp,is_int,8) +//#define amd64_push_reg(inst,reg) amd64_push_reg_size(inst,reg,8) +#define amd64_push_regp(inst,reg) amd64_push_regp_size(inst,reg,8) +#define amd64_push_mem(inst,mem) amd64_push_mem_size(inst,mem,8) +//#define amd64_push_membase(inst,basereg,disp) amd64_push_membase_size(inst,basereg,disp,8) +#define amd64_push_memindex(inst,basereg,disp,indexreg,shift) amd64_push_memindex_size(inst,basereg,disp,indexreg,shift,8) +#define amd64_push_imm(inst,imm) amd64_push_imm_size(inst,imm,8) +//#define amd64_pop_reg(inst,reg) amd64_pop_reg_size(inst,reg,8) +#define amd64_pop_mem(inst,mem) amd64_pop_mem_size(inst,mem,8) +#define amd64_pop_membase(inst,basereg,disp) amd64_pop_membase_size(inst,basereg,disp,8) +#define amd64_pushad(inst) amd64_pushad_size(inst,8) +#define amd64_pushfd(inst) amd64_pushfd_size(inst,8) +#define amd64_popad(inst) amd64_popad_size(inst,8) +#define amd64_popfd(inst) amd64_popfd_size(inst,8) +#define amd64_loop(inst,imm) amd64_loop_size(inst,imm,8) +#define amd64_loope(inst,imm) amd64_loope_size(inst,imm,8) +#define amd64_loopne(inst,imm) amd64_loopne_size(inst,imm,8) +#define amd64_jump32(inst,imm) amd64_jump32_size(inst,imm,8) +#define amd64_jump8(inst,imm) amd64_jump8_size(inst,imm,8) +#define amd64_jump_reg(inst,reg) amd64_jump_reg_size(inst,reg,8) +#define amd64_jump_mem(inst,mem) amd64_jump_mem_size(inst,mem,8) +#define amd64_jump_membase(inst,basereg,disp) amd64_jump_membase_size(inst,basereg,disp,8) +#define amd64_jump_code(inst,target) amd64_jump_code_size(inst,target,8) +#define amd64_jump_disp(inst,disp) amd64_jump_disp_size(inst,disp,8) +#define amd64_branch8(inst,cond,imm,is_signed) amd64_branch8_size(inst,cond,imm,is_signed,8) +#define amd64_branch32(inst,cond,imm,is_signed) amd64_branch32_size(inst,cond,imm,is_signed,8) +#define amd64_branch(inst,cond,target,is_signed) amd64_branch_size(inst,cond,target,is_signed,8) +#define amd64_branch_disp(inst,cond,disp,is_signed) amd64_branch_disp_size(inst,cond,disp,is_signed,8) +#define amd64_set_reg(inst,cond,reg,is_signed) amd64_set_reg_size(inst,cond,reg,is_signed,8) +#define amd64_set_mem(inst,cond,mem,is_signed) amd64_set_mem_size(inst,cond,mem,is_signed,8) +#define amd64_set_membase(inst,cond,basereg,disp,is_signed) amd64_set_membase_size(inst,cond,basereg,disp,is_signed,8) +#define amd64_call_imm(inst,disp) amd64_call_imm_size(inst,disp,8) +//#define amd64_call_reg(inst,reg) amd64_call_reg_size(inst,reg,8) +#define amd64_call_mem(inst,mem) amd64_call_mem_size(inst,mem,8) +#define amd64_call_membase(inst,basereg,disp) amd64_call_membase_size(inst,basereg,disp,8) +#define amd64_call_code(inst,target) amd64_call_code_size(inst,target,8) +//#define amd64_ret(inst) amd64_ret_size(inst,8) +#define amd64_ret_imm(inst,imm) amd64_ret_imm_size(inst,imm,8) +#define amd64_cmov_reg(inst,cond,is_signed,dreg,reg) amd64_cmov_reg_size(inst,cond,is_signed,dreg,reg,8) +#define amd64_cmov_mem(inst,cond,is_signed,reg,mem) amd64_cmov_mem_size(inst,cond,is_signed,reg,mem,8) +#define amd64_cmov_membase(inst,cond,is_signed,reg,basereg,disp) amd64_cmov_membase_size(inst,cond,is_signed,reg,basereg,disp,8) +#define amd64_enter(inst,framesize) amd64_enter_size(inst,framesize) +//#define amd64_leave(inst) amd64_leave_size(inst,8) +#define amd64_sahf(inst) amd64_sahf_size(inst,8) +#define amd64_fsin(inst) amd64_fsin_size(inst,8) +#define amd64_fcos(inst) amd64_fcos_size(inst,8) +#define amd64_fabs(inst) amd64_fabs_size(inst,8) +#define amd64_ftst(inst) amd64_ftst_size(inst,8) +#define amd64_fxam(inst) amd64_fxam_size(inst,8) +#define amd64_fpatan(inst) amd64_fpatan_size(inst,8) +#define amd64_fprem(inst) amd64_fprem_size(inst,8) +#define amd64_fprem1(inst) amd64_fprem1_size(inst,8) +#define amd64_frndint(inst) amd64_frndint_size(inst,8) +#define amd64_fsqrt(inst) amd64_fsqrt_size(inst,8) +#define amd64_fptan(inst) amd64_fptan_size(inst,8) +#define amd64_padding(inst,size) amd64_padding_size(inst,size) +#define amd64_prolog(inst,frame,reg_mask) amd64_prolog_size(inst,frame,reg_mask,8) +#define amd64_epilog(inst,reg_mask) amd64_epilog_size(inst,reg_mask,8) + +#endif // AMD64_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000000..e5064b080f --- /dev/null +++ b/main.cpp @@ -0,0 +1,50 @@ + +#include "qmljs_objects.h" +#include "qv4codegen_p.h" + +#include +#include +#include +#include +#include + +#include + +int main(int argc, char *argv[]) +{ + using namespace QQmlJS; + + GC_INIT(); + + QCoreApplication app(argc, argv); + QStringList args = app.arguments(); + args.removeFirst(); + + Engine engine; + foreach (const QString &fn, args) { + QFile file(fn); + if (file.open(QFile::ReadOnly)) { + const QString code = QString::fromUtf8(file.readAll()); + file.close(); + + Lexer lexer(&engine); + lexer.setCode(code, 1, false); + Parser parser(&engine); + + const bool parsed = parser.parseProgram(); + + foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { + std::cerr << qPrintable(fn) << ':' << m.loc.startLine << ':' << m.loc.startColumn + << ": error: " << qPrintable(m.message) << std::endl; + } + + if (parsed) { + using namespace AST; + Program *program = AST::cast(parser.rootNode()); + + Codegen cg; + cg(program); + } + } + } +} diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp new file mode 100644 index 0000000000..580d6e512b --- /dev/null +++ b/qmljs_objects.cpp @@ -0,0 +1,109 @@ + +#include "qmljs_objects.h" +#include + +bool Object::get(String *name, Value *result) +{ + if (Property *prop = getProperty(name)) { + *result = prop->value; + return true; + } + + __qmljs_init_undefined(0, result); + return false; +} + +Property *Object::getOwnProperty(String *name) +{ + if (members) { + if (Property *prop = members->find(name)) { + return prop; + } + } + return 0; +} + +Property *Object::getProperty(String *name) +{ + if (Property *prop = getOwnProperty(name)) + return prop; + else if (prototype) + return prototype->getProperty(name); + return 0; +} + +void Object::put(String *name, const Value &value, bool flag) +{ + if (! members) + members = new (GC) Table(); + + members->insert(name, value); +} + +bool Object::canPut(String *name) +{ + if (Property *prop = getOwnProperty(name)) + return true; + if (! prototype) + return extensible; + if (Property *inherited = prototype->getProperty(name)) { + return inherited->isWritable(); + } else { + return extensible; + } + return true; +} + +bool Object::hasProperty(String *name) const +{ + if (members) + return members->find(name) != 0; + + return false; +} + +bool Object::deleteProperty(String *name, bool flag) +{ + if (members) + return members->remove(name); + + return false; +} + +void Object::defaultValue(Value *result, int typeHint) +{ + Context *ctx = 0; // ### + + if (typeHint == STRING_HINT) { + if (asFunctionObject() != 0) + __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("function"))); + else + __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("object"))); + } else { + __qmljs_init_undefined(ctx, result); + } +} + + +bool FunctionObject::hasInstance(const Value &value) const +{ + Q_UNUSED(value); + return false; +} + +Value FunctionObject::call(const Value &thisObject, const Value args[], unsigned argc) +{ + (void) thisObject; + + Value v; + __qmljs_init_undefined(0, &v); + return v; +} + +Value FunctionObject::construct(const Value args[], unsigned argc) +{ + Value thisObject; + __qmljs_init_object(0, &thisObject, new (GC) Object); + call(thisObject, args, argc); + return thisObject; +} diff --git a/qmljs_objects.h b/qmljs_objects.h new file mode 100644 index 0000000000..972ef4a593 --- /dev/null +++ b/qmljs_objects.h @@ -0,0 +1,264 @@ +#ifndef QMLJS_OBJECTS_H +#define QMLJS_OBJECTS_H + +#include "qmljs_runtime.h" + +#include +#include +#include + +struct Value; +struct Object; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct FunctionObject; +struct ErrorObject; + +struct String: gc_cleanup { + String(const QString &text) + : _text(text), _hashValue(0) {} + + inline const QString &text() const { + return _text; + } + + inline unsigned hashValue() const { + if (! _hashValue) + _hashValue = qHash(_text); + + return _hashValue; + } + + static String *get(Context *ctx, const QString &s) { + return new (GC) String(s); + } + +private: + QString _text; + mutable unsigned _hashValue; +}; + +struct Property: gc { + String *name; + Value value; + PropertyAttributes flags; + Property *next; + + Property(String *name, const Value &value, PropertyAttributes flags = NoAttributes) + : name(name), value(value), flags(flags), next(0) {} + + inline bool isWritable() const { return flags & WritableAttribute; } + inline bool isEnumerable() const { return flags & EnumerableAttribute; } + inline bool isConfigurable() const { return flags & ConfigurableAttribute; } + + inline bool hasName(String *n) const { + if (name == n || (name->hashValue() == n->hashValue() && name->text() == n->text())) + return true; + return false; + } + + inline unsigned hashValue() const { + return name->hashValue(); + } +}; + +class Table: public gc +{ + Property **_properties; + Property **_buckets; + int _propertyCount; + int _bucketCount; + int _allocated; + +public: + Table() + : _properties(0) + , _buckets(0) + , _propertyCount(-1) + , _bucketCount(11) + , _allocated(0) {} + + bool empty() const { return _propertyCount == -1; } + unsigned size() const { return _propertyCount + 1; } + + typedef Property **iterator; + iterator begin() const { return _properties; } + iterator end() const { return _properties + (_propertyCount + 1); } + + bool remove(String *name) + { + if (_properties) { + const unsigned hash = name->hashValue() % _bucketCount; + + if (Property *prop = _buckets[hash]) { + if (prop->hasName(name)) { + _buckets[hash] = prop->next; + return true; + } + + do { + Property *next = prop->next; + + if (next && next->hasName(name)) { + prop->next = next->next; + return true; + } + prop = next; + } while (prop); + } + } + + return false; + } + + Property *find(String *name) const + { + if (! _properties) + return 0; + + for (Property *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { + if (prop->hasName(name)) + return prop; + } + + return 0; + } + + Property *insert(String *name, const Value &value) + { + if (Property *prop = find(name)) { + prop->value = value; + return prop; + } + + if (++_propertyCount == _allocated) { + if (! _allocated) + _allocated = 4; + else + _allocated *= 2; + + Property **properties = new (GC) Property*[_allocated]; + std::copy(_properties, _properties + _propertyCount, properties); + _properties = properties; + } + + Property *prop = new (GC) Property(name, value); + _properties[_propertyCount] = prop; + + if (! _buckets || 3 * _propertyCount >= 2 * _bucketCount) { + rehash(); + } else { + Property *&bucket = _buckets[prop->hashValue() % _bucketCount]; + prop->next = bucket; + bucket = prop; + } + + return prop; + } + +private: + void rehash() + { + if (_bucketCount) + _bucketCount *= 2; // ### next prime + + _buckets = new (GC) Property *[_bucketCount]; + std::fill(_buckets, _buckets + _bucketCount, (Property *) 0); + + for (int i = 0; i <= _propertyCount; ++i) { + Property *prop = _properties[i]; + Property *&bucket = _buckets[prop->name->hashValue() % _bucketCount]; + prop->next = bucket; + bucket = prop; + } + } +}; + +struct Object: gc_cleanup { + Object *prototype; + String *klass; + Table *members; + bool extensible; + + Object() + : prototype(0) + , klass(0) + , members(0) + , extensible(true) {} + + virtual ~Object() {} + + virtual FunctionObject *asFunctionObject() { return 0; } + + virtual bool get(String *name, Value *result); + virtual Property *getOwnProperty(String *name); + virtual Property *getProperty(String *name); + virtual void put(String *name, const Value &value, bool flag = 0); + virtual bool canPut(String *name); + virtual bool hasProperty(String *name) const; + virtual bool deleteProperty(String *name, bool flag); + virtual void defaultValue(Value *result, int typeHint); + // ### TODO: defineOwnProperty(name, descriptor, boolean) -> boolean +}; + +struct BooleanObject: Object { + Value value; + BooleanObject(const Value &value): value(value) {} + virtual void defaultValue(Value *result, int typehint) { *result = value; } +}; + +struct NumberObject: Object { + Value value; + NumberObject(const Value &value): value(value) {} + virtual void defaultValue(Value *result, int typehint) { *result = value; } +}; + +struct StringObject: Object { + Value value; + StringObject(const Value &value): value(value) {} + virtual void defaultValue(Value *result, int typehint) { *result = value; } +}; + +struct ArrayObject: Object { +}; + +struct FunctionObject: Object { + Object *scope; + String **formalParameterList; + + FunctionObject(Object *scope = 0): scope(scope), formalParameterList(0) {} + virtual FunctionObject *asFunctionObject() { return this; } + + virtual bool hasInstance(const Value &value) const; + virtual Value call(const Value &thisObject, const Value args[], unsigned argc); + virtual Value construct(const Value args[], unsigned argc); +}; + +struct ErrorObject: Object { + String *message; + ErrorObject(String *message): message(message) {} +}; + +struct ArgumentsObject: Object { +}; + +struct Context: gc { + Value activation; + Value thisObject; + Object *scope; + Value *arguments; + unsigned argumentCount; + + Context() + : scope(0) + , arguments(0) + , argumentCount(0) + { + activation.type = NULL_TYPE; + thisObject.type = NULL_TYPE; + } +}; + +#endif // QMLJS_OBJECTS_H diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp new file mode 100644 index 0000000000..e7ee6f9b51 --- /dev/null +++ b/qmljs_runtime.cpp @@ -0,0 +1,339 @@ + +#include "qmljs_runtime.h" +#include "qmljs_objects.h" +#include +#include + +Value Value::string(Context *ctx, const QString &s) +{ + return string(ctx, String::get(ctx, s)); +} + +extern "C" { + +void __qmljs_string_literal_undefined(Context *ctx, Value *result) +{ + __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("undefined"))); +} + +void __qmljs_string_literal_null(Context *ctx, Value *result) +{ + __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("null"))); +} + +void __qmljs_string_literal_true(Context *ctx, Value *result) +{ + __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("true"))); +} + +void __qmljs_string_literal_false(Context *ctx, Value *result) +{ + __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("false"))); +} + +void __qmljs_string_literal_object(Context *ctx, Value *result) +{ + __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("object"))); +} + +void __qmljs_string_literal_boolean(Context *ctx, Value *result) +{ + __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("boolean"))); +} + +void __qmljs_string_literal_number(Context *ctx, Value *result) +{ + __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("number"))); +} + +void __qmljs_string_literal_string(Context *ctx, Value *result) +{ + __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("string"))); +} + +void __qmljs_string_literal_function(Context *ctx, Value *result) +{ + __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("function"))); +} + +void __qmljs_delete(Context *ctx, Value *result, const Value *value) +{ + (void) ctx; + (void) result; + (void) value; +} + +void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Value *right) +{ + if (right->type == OBJECT_TYPE) { + if (FunctionObject *function = right->objectValue->asFunctionObject()) { + bool r = function->hasInstance(*left); + __qmljs_init_boolean(ctx, result, r); + return; + } + } + + __qmljs_throw_type_error(ctx, result); +} + +void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *right) +{ + if (right->type == OBJECT_TYPE) { + Value s; + __qmljs_to_string(ctx, &s, left); + bool r = right->objectValue->hasProperty(s.stringValue); + __qmljs_init_boolean(ctx, result, r); + } else { + __qmljs_throw_type_error(ctx, result); + } +} + +int __qmljs_string_length(Context *, String *string) +{ + return string->text().length(); +} + +double __qmljs_string_to_number(Context *, String *string) +{ + bool ok; + return string->text().toDouble(&ok); // ### TODO +} + +void __qmljs_string_from_number(Context *ctx, Value *result, double number) +{ + String *string = String::get(ctx, QString::number(number, 'g', 16)); + __qmljs_init_string(ctx, result, string); +} + +bool __qmljs_string_compare(Context *, String *left, String *right) +{ + return left->text() < right->text(); +} + +bool __qmljs_string_equal(Context *, String *left, String *right) +{ + return left == right || + (left->hashValue() == right->hashValue() && + left->text() == right->text()); +} + +String *__qmljs_string_concat(Context *ctx, String *first, String *second) +{ + return String::get(ctx, first->text() + second->text()); +} + +bool __qmljs_is_function(Context *, const Value *value) +{ + return value->objectValue->asFunctionObject() != 0; +} + +void __qmljs_object_default_value(Context *ctx, Value *result, Object *object, int typeHint) +{ + object->defaultValue(result, typeHint); +} + +void __qmljs_throw_type_error(Context *ctx, Value *result) +{ + __qmljs_init_object(ctx, result, new (GC) ErrorObject(String::get(ctx, QLatin1String("type error")))); +} + +void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean) +{ + Value value; + __qmljs_init_boolean(ctx, &value, boolean); + __qmljs_init_object(ctx, result, new (GC) BooleanObject(value)); +} + +void __qmljs_new_number_object(Context *ctx, Value *result, double number) +{ + Value value; + __qmljs_init_number(ctx, &value, number); + __qmljs_init_object(ctx, result, new (GC) NumberObject(value)); +} + +void __qmljs_new_string_object(Context *ctx, Value *result, String *string) +{ + Value value; + __qmljs_init_string(ctx, &value, string); + __qmljs_init_object(ctx, result, new (GC) StringObject(value)); +} + +void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value) +{ + object->objectValue->put(name, *value, /*flags*/ 0); +} + +void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double number) +{ + Value value; + __qmljs_init_number(ctx, &value, number); + object->objectValue->put(name, value, /*flag*/ 0); +} + +void __qmljs_set_property_string(Context *ctx, Value *object, String *name, String *s) +{ + Value value; + __qmljs_init_string(ctx, &value, s); + object->objectValue->put(name, value, /*flag*/ 0); +} + +void __qmljs_set_activation_property(Context *ctx, String *name, Value *value) +{ + __qmljs_set_property(ctx, &ctx->activation, name, value); +} + +void __qmljs_set_activation_property_number(Context *ctx, String *name, double value) +{ + __qmljs_set_property_number(ctx, &ctx->activation, name, value); +} + +void __qmljs_set_activation_property_string(Context *ctx, String *name, String *value) +{ + __qmljs_set_property_string(ctx, &ctx->activation, name, value); +} + +void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name) +{ + Q_ASSERT(object->type == OBJECT_TYPE); + object->objectValue->get(name, result); +} + +void __qmljs_get_activation_property(Context *ctx, Value *result, String *name) +{ + __qmljs_get_property(ctx, result, &ctx->activation, name); +} + +void __qmljs_get_activation(Context *ctx, Value *result) +{ + *result = ctx->activation; +} + +void __qmljs_get_thisObject(Context *ctx, Value *result) +{ + *result = ctx->thisObject; +} + +void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y, bool leftFirst) +{ + Value px, py; + + if (leftFirst) { + __qmljs_to_primitive(ctx, &px, x, NUMBER_HINT); + __qmljs_to_primitive(ctx, &py, y, NUMBER_HINT); + } else { + __qmljs_to_primitive(ctx, &py, x, NUMBER_HINT); + __qmljs_to_primitive(ctx, &px, y, NUMBER_HINT); + } + + if (px.type == STRING_TYPE && py.type == STRING_TYPE) { + bool r = __qmljs_string_compare(ctx, px.stringValue, py.stringValue); + __qmljs_init_boolean(ctx, result, r); + } else { + double nx = __qmljs_to_number(ctx, &px); + double ny = __qmljs_to_number(ctx, &py); + if (isnan(nx) || isnan(ny)) { + __qmljs_init_undefined(ctx, result); + } else { + const int sx = signbit(nx); + const int sy = signbit(ny); + const bool ix = isinf(nx); + const bool iy = isinf(ny); + bool r = false; + + if (ix && !sx) { + r = false; + } else if (iy && !sy) { + r = true; + } else if (iy && sy) { + r = false; + } else if (ix && sx) { + r = true; + } else if (nx == ny) { + r = false; + } else { + r = nx < ny; + } + __qmljs_init_boolean(ctx, result, r); + } + } +} + +bool __qmljs_equal(Context *ctx, const Value *x, const Value *y) +{ + if (x->type == y->type) { + switch ((ValueType) x->type) { + case UNDEFINED_TYPE: + return true; + case NULL_TYPE: + return true; + case BOOLEAN_TYPE: + return x->booleanValue == y->booleanValue; + break; + case NUMBER_TYPE: + return x->numberValue == y->numberValue; + case STRING_TYPE: + return __qmljs_string_equal(ctx, x->stringValue, y->stringValue); + case OBJECT_TYPE: + return x->objectValue == y->objectValue; + } + // unreachable + } else { + if (x->type == NULL_TYPE && y->type == UNDEFINED_TYPE) { + return true; + } else if (x->type == UNDEFINED_TYPE && y->type == NULL_TYPE) { + return true; + } else if (x->type == NUMBER_TYPE && y->type == STRING_TYPE) { + Value ny; + __qmljs_init_number(ctx, &ny, __qmljs_to_number(ctx, y)); + return __qmljs_equal(ctx, x, &ny); + } else if (x->type == STRING_TYPE && y->type == NUMBER_TYPE) { + Value nx; + __qmljs_init_number(ctx, &nx, __qmljs_to_number(ctx, x)); + return __qmljs_equal(ctx, &nx, y); + } else if (x->type == BOOLEAN_TYPE) { + Value nx; + __qmljs_init_number(ctx, &nx, (double) x->booleanValue); + return __qmljs_equal(ctx, &nx, y); + } else if (y->type == BOOLEAN_TYPE) { + Value ny; + __qmljs_init_number(ctx, &ny, (double) y->booleanValue); + return __qmljs_equal(ctx, x, &ny); + } else if ((x->type == NUMBER_TYPE || x->type == STRING_TYPE) && y->type == OBJECT_TYPE) { + Value py; + __qmljs_to_primitive(ctx, &py, y, PREFERREDTYPE_HINT); + return __qmljs_equal(ctx, x, &py); + } else if (x->type == OBJECT_TYPE && (y->type == NUMBER_TYPE || y->type == STRING_TYPE)) { + Value px; + __qmljs_to_primitive(ctx, &px, x, PREFERREDTYPE_HINT); + return __qmljs_equal(ctx, &px, y); + } + } + + return false; +} + +void __qmljs_call(Context *ctx, Value *result, const Value *function, + const Value *thisObject, const Value *arguments, int argc) +{ + if (function->type != OBJECT_TYPE) { + __qmljs_throw_type_error(ctx, result); + } else if (FunctionObject *f = function->objectValue->asFunctionObject()) { + *result = f->call(*thisObject, arguments, argc); + } else { + __qmljs_throw_type_error(ctx, result); + } +} + +void __qmjs_construct(Context *ctx, Value *result, const Value *function, const Value *arguments, int argc) +{ + if (function->type != OBJECT_TYPE) { + __qmljs_throw_type_error(ctx, result); + } else if (FunctionObject *f = function->objectValue->asFunctionObject()) { + *result = f->construct(arguments, argc); + } else { + __qmljs_throw_type_error(ctx, result); + } +} + + +} // extern "C" diff --git a/qmljs_runtime.h b/qmljs_runtime.h new file mode 100644 index 0000000000..1b4b894f65 --- /dev/null +++ b/qmljs_runtime.h @@ -0,0 +1,623 @@ +#ifndef QMLJS_RUNTIME_H +#define QMLJS_RUNTIME_H + +#include +#include +#include + +enum ValueType { + UNDEFINED_TYPE, + NULL_TYPE, + BOOLEAN_TYPE, + NUMBER_TYPE, + STRING_TYPE, + OBJECT_TYPE +}; + +enum TypeHint { + PREFERREDTYPE_HINT, + NUMBER_HINT, + STRING_HINT +}; + +enum PropertyAttributes { + NoAttributes = 0, + ValueAttribute = 1, + WritableAttribute = 2, + EnumerableAttribute = 4, + ConfigurableAttribute = 8 +}; + +struct Value; +struct Object; +struct String; +struct Context; + +extern "C" { + +// constructors +void __qmljs_init_undefined(Context *ctx, Value *result); +void __qmljs_init_null(Context *ctx, Value *result); +void __qmljs_init_boolean(Context *ctx, Value *result, bool value); +void __qmljs_init_number(Context *ctx, Value *result, double number); +void __qmljs_init_string(Context *ctx, Value *result, String *string); +void __qmljs_init_object(Context *ctx, Value *result, Object *object); + +bool __qmljs_is_function(Context *ctx, const Value *value); + +// string literals +void __qmljs_string_literal_undefined(Context *ctx, Value *result); +void __qmljs_string_literal_null(Context *ctx, Value *result); +void __qmljs_string_literal_true(Context *ctx, Value *result); +void __qmljs_string_literal_false(Context *ctx, Value *result); +void __qmljs_string_literal_object(Context *ctx, Value *result); +void __qmljs_string_literal_boolean(Context *ctx, Value *result); +void __qmljs_string_literal_number(Context *ctx, Value *result); +void __qmljs_string_literal_string(Context *ctx, Value *result); +void __qmljs_string_literal_function(Context *ctx, Value *result); + +// strings +int __qmljs_string_length(Context *ctx, String *string); +double __qmljs_string_to_number(Context *ctx, String *string); +void __qmljs_string_from_number(Context *ctx, Value *result, double number); +bool __qmljs_string_compare(Context *ctx, String *left, String *right); +bool __qmljs_string_equal(Context *ctx, String *left, String *right); +String *__qmljs_string_concat(Context *ctx, String *first, String *second); + +// objects +void __qmljs_object_default_value(Context *ctx, Value *result, Object *object, int typeHint); +void __qmljs_throw_type_error(Context *ctx, Value *result); +void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean); +void __qmljs_new_number_object(Context *ctx, Value *result, double n); +void __qmljs_new_string_object(Context *ctx, Value *result, String *string); +void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value); +void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double value); +void __qmljs_set_property_string(Context *ctx, Value *object, String *name, String *value); +void __qmljs_set_activation_property(Context *ctx, String *name, Value *value); +void __qmljs_set_activation_property_number(Context *ctx, String *name, double value); +void __qmljs_set_activation_property_string(Context *ctx, String *name, String *value); +void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name); +void __qmljs_get_activation_property(Context *ctx, Value *result, String *name); + +// context +void __qmljs_get_activation(Context *ctx, Value *result); +void __qmljs_get_thisObject(Context *ctx, Value *result); + +// type conversion and testing +void __qmljs_to_primitive(Context *ctx, Value *result, const Value *value, int typeHint); +bool __qmljs_to_boolean(Context *ctx, const Value *value); +double __qmljs_to_number(Context *ctx, const Value *value); +double __qmljs_to_integer(Context *ctx, const Value *value); +int __qmljs_to_int32(Context *ctx, const Value *value); +unsigned __qmljs_to_uint32(Context *ctx, const Value *value); +unsigned short __qmljs_to_uint16(Context *ctx, const Value *value); +void __qmljs_to_string(Context *ctx, Value *result, const Value *value); +void __qmljs_to_object(Context *ctx, Value *result, const Value *value); +bool __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value); +bool __qmljs_is_callable(Context *ctx, const Value *value); +void __qmljs_default_value(Context *ctx, Value *result, const Value *value, int typeHint); + +void __qmljs_compare(Context *ctx, Value *result, const Value *left, const Value *right, bool leftFlag); +bool __qmljs_equal(Context *ctx, const Value *x, const Value *y); +bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y); + +// unary operators +void __qmljs_delete(Context *ctx, Value *result, const Value *value); +void __qmljs_typeof(Context *ctx, Value *result, const Value *value); +void __qmljs_postincr(Context *ctx, Value *result, const Value *value); +void __qmljs_postdecr(Context *ctx, Value *result, const Value *value); +void __qmljs_uplus(Context *ctx, Value *result, const Value *value); +void __qmljs_uminus(Context *ctx, Value *result, const Value *value); +void __qmljs_compl(Context *ctx, Value *result, const Value *value); +void __qmljs_not(Context *ctx, Value *result, const Value *value); +void __qmljs_preincr(Context *ctx, Value *result, const Value *value); +void __qmljs_predecr(Context *ctx, Value *result, const Value *value); + +// binary operators +void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_bit_or(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_bit_xor(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_bit_and(Context *ctx, Value *result, const Value *left,const Value *right); +void __qmljs_add(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_sub(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_mul(Context *ctx, Value *result, const Value *left,const Value *right); +void __qmljs_div(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_mod(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_shl(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_shr(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_le(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_eq(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *right); + +void __qmljs_call(Context *ctx, Value *result, const Value *function, const Value *thisObject, const Value *arguments, int argc); +void __qmjs_construct(Context *ctx, Value *result, const Value *function, const Value *arguments, int argc); + +} // extern "C" + +struct Value { + int type; + union { + bool booleanValue; + double numberValue; + Object *objectValue; + String *stringValue; + }; + + static inline Value boolean(Context *ctx, bool value) { + Value v; + __qmljs_init_boolean(ctx, &v, value); + return v; + } + + static inline Value number(Context *ctx, double value) { + Value v; + __qmljs_init_number(ctx, &v, value); + return v; + } + + static inline Value object(Context *ctx, Object *value) { + Value v; + __qmljs_init_object(ctx, &v, value); + return v; + } + + static inline Value string(Context *ctx, String *value) { + Value v; + __qmljs_init_string(ctx, &v, value); + return v; + } + + static Value string(Context *ctx, const QString &string); +}; + +extern "C" { + +// constructors +inline void __qmljs_init_undefined(Context *, Value *result) +{ + result->type = UNDEFINED_TYPE; +} + +inline void __qmljs_init_null(Context *, Value *result) +{ + result->type = NULL_TYPE; +} + +inline void __qmljs_init_boolean(Context *, Value *result, bool value) +{ + result->type = BOOLEAN_TYPE; + result->booleanValue = value; +} + +inline void __qmljs_init_number(Context *, Value *result, double value) +{ + result->type = NUMBER_TYPE; + result->numberValue = value; +} + +inline void __qmljs_init_string(Context *, Value *result, String *value) +{ + result->type = STRING_TYPE; + result->stringValue = value; +} + +inline void __qmljs_init_object(Context *, Value *result, Object *object) +{ + result->type = OBJECT_TYPE; + result->objectValue = object; +} + + +// type conversion and testing +inline void __qmljs_to_primitive(Context *ctx, Value *result, const Value *value, int typeHint) +{ + switch ((ValueType) value->type) { + case UNDEFINED_TYPE: + case NULL_TYPE: + case BOOLEAN_TYPE: + case NUMBER_TYPE: + case STRING_TYPE: + *result = *value; + break; + case OBJECT_TYPE: + __qmljs_default_value(ctx, result, value, typeHint); + break; + } +} + +inline bool __qmljs_to_boolean(Context *ctx, const Value *value) +{ + switch ((ValueType) value->type) { + case UNDEFINED_TYPE: + case NULL_TYPE: + return false; + case BOOLEAN_TYPE: + return value->booleanValue; + case NUMBER_TYPE: + if (! value->numberValue || isnan(value->numberValue)) + return false; + return true; + case STRING_TYPE: + return __qmljs_string_length(ctx, value->stringValue) > 0; + case OBJECT_TYPE: + return true; + } + Q_ASSERT(!"unreachable"); + return false; +} + +inline double __qmljs_to_number(Context *ctx, const Value *value) +{ + switch ((ValueType) value->type) { + case UNDEFINED_TYPE: + return nan(""); + case NULL_TYPE: + return 0; + case BOOLEAN_TYPE: + return (double) value->booleanValue; + case NUMBER_TYPE: + return value->numberValue; + case STRING_TYPE: + return __qmljs_string_to_number(ctx, value->stringValue); + case OBJECT_TYPE: { + Value prim; + __qmljs_to_primitive(ctx, &prim, value, NUMBER_HINT); + return __qmljs_to_number(ctx, &prim); + } + } // switch + return 0; // unreachable +} + +inline double __qmljs_to_integer(Context *ctx, const Value *value) +{ + const double number = __qmljs_to_number(ctx, value); + if (isnan(number)) + return +0; + else if (! number || isinf(number)) + return number; + return signbit(number) * floor(fabs(number)); +} + +inline int __qmljs_to_int32(Context *ctx, const Value *value) +{ + const double number = __qmljs_to_number(ctx, value); + if (! number || isnan(number) || isinf(number)) + return +0; + return (int) trunc(number); // ### +} + +inline unsigned __qmljs_to_uint32(Context *ctx, const Value *value) +{ + const double number = __qmljs_to_number(ctx, value); + if (! number || isnan(number) || isinf(number)) + return +0; + return (unsigned) trunc(number); // ### +} + +inline unsigned short __qmljs_to_uint16(Context *ctx, const Value *value) +{ + const double number = __qmljs_to_number(ctx, value); + if (! number || isnan(number) || isinf(number)) + return +0; + return (unsigned short) trunc(number); // ### +} + +inline void __qmljs_to_string(Context *ctx, Value *result, const Value *value) +{ + switch ((ValueType) value->type) { + case UNDEFINED_TYPE: + __qmljs_string_literal_undefined(ctx, result); + break; + case NULL_TYPE: + __qmljs_string_literal_null(ctx, result); + break; + case BOOLEAN_TYPE: + if (value->booleanValue) + __qmljs_string_literal_true(ctx, result); + else + __qmljs_string_literal_false(ctx, result); + break; + case NUMBER_TYPE: + __qmljs_string_from_number(ctx, result, value->numberValue); + break; + case STRING_TYPE: + *result = *value; + break; + case OBJECT_TYPE: { + Value prim; + __qmljs_to_primitive(ctx, &prim, value, STRING_HINT); + __qmljs_to_string(ctx, result, &prim); + break; + } + + } // switch +} + +inline void __qmljs_to_object(Context *ctx, Value *result, const Value *value) +{ + switch ((ValueType) value->type) { + case UNDEFINED_TYPE: + case NULL_TYPE: + __qmljs_throw_type_error(ctx, result); + break; + case BOOLEAN_TYPE: + __qmljs_new_boolean_object(ctx, result, value->booleanValue); + break; + case NUMBER_TYPE: + __qmljs_new_number_object(ctx, result, value->numberValue); + break; + case STRING_TYPE: + __qmljs_new_string_object(ctx, result, value->stringValue); + break; + case OBJECT_TYPE: + *result = *value; + break; + } +} + +inline bool __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value) +{ + switch ((ValueType) value->type) { + case UNDEFINED_TYPE: + case NULL_TYPE: + __qmljs_throw_type_error(ctx, result); + return false; + case BOOLEAN_TYPE: + case NUMBER_TYPE: + case STRING_TYPE: + case OBJECT_TYPE: + return true; + } +} + +inline bool __qmljs_is_callable(Context *ctx, const Value *value) +{ + if (value->type == OBJECT_TYPE) + return __qmljs_is_function(ctx, value); + else + return false; +} + +inline void __qmljs_default_value(Context *ctx, Value *result, const Value *value, int typeHint) +{ + if (value->type == OBJECT_TYPE) + __qmljs_object_default_value(ctx, result, value->objectValue, typeHint); + else + __qmljs_init_undefined(ctx, result); +} + + +// unary operators +inline void __qmljs_typeof(Context *ctx, Value *result, const Value *value) +{ + switch ((ValueType) value->type) { + case UNDEFINED_TYPE: + __qmljs_string_literal_undefined(ctx, result); + break; + case NULL_TYPE: + __qmljs_string_literal_object(ctx, result); + break; + case BOOLEAN_TYPE: + __qmljs_string_literal_boolean(ctx, result); + break; + case NUMBER_TYPE: + __qmljs_string_literal_number(ctx, result); + break; + case STRING_TYPE: + __qmljs_string_literal_string(ctx, result); + break; + case OBJECT_TYPE: + if (__qmljs_is_callable(ctx, value)) + __qmljs_string_literal_function(ctx, result); + else + __qmljs_string_literal_object(ctx, result); // ### implementation-defined + break; + } +} + +inline void __qmljs_uplus(Context *ctx, Value *result, const Value *value) +{ + double n = __qmljs_to_number(ctx, value); + __qmljs_init_number(ctx, result, n); +} + +inline void __qmljs_uminus(Context *ctx, Value *result, const Value *value) +{ + double n = __qmljs_to_number(ctx, value); + __qmljs_init_number(ctx, result, -n); +} + +inline void __qmljs_compl(Context *ctx, Value *result, const Value *value) +{ + int n = __qmljs_to_int32(ctx, value); + __qmljs_init_number(ctx, result, ~n); +} + +inline void __qmljs_not(Context *ctx, Value *result, const Value *value) +{ + bool b = __qmljs_to_boolean(ctx, value); + __qmljs_init_number(ctx, result, !b); +} + +// binary operators +inline void __qmljs_bit_or(Context *ctx, Value *result, const Value *left, const Value *right) +{ + int lval = __qmljs_to_int32(ctx, left); + int rval = __qmljs_to_int32(ctx, right); + __qmljs_init_number(ctx, result, lval | rval); +} + +inline void __qmljs_bit_xor(Context *ctx, Value *result, const Value *left, const Value *right) +{ + int lval = __qmljs_to_int32(ctx, left); + int rval = __qmljs_to_int32(ctx, right); + __qmljs_init_number(ctx, result, lval ^ rval); +} + +inline void __qmljs_bit_and(Context *ctx, Value *result, const Value *left, const Value *right) +{ + int lval = __qmljs_to_int32(ctx, left); + int rval = __qmljs_to_int32(ctx, right); + __qmljs_init_number(ctx, result, lval & rval); +} + +inline void __qmljs_add(Context *ctx, Value *result, const Value *left, const Value *right) +{ + Value pleft, pright; + __qmljs_to_primitive(ctx, &pleft, left, PREFERREDTYPE_HINT); + __qmljs_to_primitive(ctx, &pright, right, PREFERREDTYPE_HINT); + if (pleft.type == STRING_TYPE || pright.type == STRING_TYPE) { + if (pleft.type != STRING_TYPE) + __qmljs_to_string(ctx, &pleft, &pleft); + if (pright.type != STRING_TYPE) + __qmljs_to_string(ctx, &pright, &pright); + String *string = __qmljs_string_concat(ctx, pleft.stringValue, pright.stringValue); + __qmljs_init_string(ctx, result, string); + } else { + double x = __qmljs_to_number(ctx, &pleft); + double y = __qmljs_to_number(ctx, &pright); + __qmljs_init_number(ctx, result, x + y); + } +} + +inline void __qmljs_sub(Context *ctx, Value *result, const Value *left, const Value *right) +{ + double lval = __qmljs_to_number(ctx, left); + double rval = __qmljs_to_number(ctx, right); + __qmljs_init_number(ctx, result, lval - rval); +} + +inline void __qmljs_mul(Context *ctx, Value *result, const Value *left, const Value *right) +{ + double lval = __qmljs_to_number(ctx, left); + double rval = __qmljs_to_number(ctx, right); + __qmljs_init_number(ctx, result, lval * rval); +} + +inline void __qmljs_div(Context *ctx, Value *result, const Value *left, const Value *right) +{ + double lval = __qmljs_to_number(ctx, left); + double rval = __qmljs_to_number(ctx, right); + __qmljs_init_number(ctx, result, lval * rval); +} + +inline void __qmljs_mod(Context *ctx, Value *result, const Value *left, const Value *right) +{ + double lval = __qmljs_to_number(ctx, left); + double rval = __qmljs_to_number(ctx, right); + __qmljs_init_number(ctx, result, fmod(lval, rval)); +} + +inline void __qmljs_shl(Context *ctx, Value *result, const Value *left, const Value *right) +{ + int lval = __qmljs_to_int32(ctx, left); + unsigned rval = __qmljs_to_uint32(ctx, right); + __qmljs_init_number(ctx, result, lval << rval); +} + +inline void __qmljs_shr(Context *ctx, Value *result, const Value *left, const Value *right) +{ + int lval = __qmljs_to_int32(ctx, left); + unsigned rval = __qmljs_to_uint32(ctx, right); + __qmljs_init_number(ctx, result, lval >> rval); +} + +inline void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const Value *right) +{ + unsigned lval = __qmljs_to_uint32(ctx, left); + unsigned rval = __qmljs_to_uint32(ctx, right); + __qmljs_init_number(ctx, result, lval << rval); +} + +inline void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Value *right) +{ + __qmljs_compare(ctx, result, right, left, false); + + if (result->type == UNDEFINED_TYPE) + __qmljs_init_boolean(ctx, result, false); +} + +inline void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Value *right) +{ + __qmljs_compare(ctx, result, left, right, true); + + if (result->type == UNDEFINED_TYPE) + __qmljs_init_boolean(ctx, result,false); +} + +inline void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Value *right) +{ + __qmljs_compare(ctx, result, right, left, false); + + bool r = ! (result->type == UNDEFINED_TYPE || + (result->type == BOOLEAN_TYPE && result->booleanValue == true)); + + __qmljs_init_boolean(ctx, result, r); +} + +inline void __qmljs_le(Context *ctx, Value *result, const Value *left, const Value *right) +{ + __qmljs_compare(ctx, result, left, right, true); + + bool r = ! (result->type == UNDEFINED_TYPE || + (result->type == BOOLEAN_TYPE && result->booleanValue == true)); + + __qmljs_init_boolean(ctx, result, r); +} + +inline void __qmljs_eq(Context *ctx, Value *result, const Value *left, const Value *right) +{ + bool r = __qmljs_equal(ctx, left, right); + __qmljs_init_boolean(ctx, result, r); +} + +inline void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *right) +{ + bool r = ! __qmljs_equal(ctx, left, right); + __qmljs_init_boolean(ctx, result, r); +} + +inline void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right) +{ + bool r = __qmljs_strict_equal(ctx, left, right); + __qmljs_init_boolean(ctx, result, r); +} + +inline void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *right) +{ + bool r = ! __qmljs_strict_equal(ctx, left, right); + __qmljs_init_boolean(ctx, result, r); +} + +inline bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y) +{ + if (x->type != y->type) + return false; + + switch ((ValueType) x->type) { + case UNDEFINED_TYPE: + case NULL_TYPE: + return true; + case BOOLEAN_TYPE: + return x->booleanValue == y->booleanValue; + case NUMBER_TYPE: + return x->numberValue == y->numberValue; + case STRING_TYPE: + return __qmljs_string_equal(ctx, x->stringValue, y->stringValue); + case OBJECT_TYPE: + return x->objectValue == y->objectValue; + } + Q_ASSERT(!"unreachable"); + return false; +} + +} // extern "C" + +#endif // QMLJS_RUNTIME_H diff --git a/qv4codegen.cpp b/qv4codegen.cpp new file mode 100644 index 0000000000..52e99171a7 --- /dev/null +++ b/qv4codegen.cpp @@ -0,0 +1,1780 @@ +#include "qv4codegen_p.h" +#include "qv4isel_p.h" + +#include +#include +#include +#include +#include +#include + +using namespace QQmlJS; +using namespace AST; + +namespace { +QTextStream qout(stdout, QIODevice::WriteOnly); + +class IntSet +{ + IntSet(const IntSet &other); + IntSet &operator = (const IntSet &other); + +public: + unsigned *info; + unsigned *check; + unsigned count; + unsigned size; + + IntSet(unsigned size = 32) + : info(new unsigned[size]) + , check(new unsigned[size]) + , count(0) + , size(size) + { + } + + ~IntSet() { + delete[] info; + delete[] check; + } + + typedef const unsigned *const_iterator; + typedef const_iterator iterator; + const_iterator begin() const { return info; } + const_iterator end() const { return info + count; } + + bool empty() const { return count == 0; } + void clear() { count = 0; } + + inline void insert(unsigned value) { + if (! contains(value)) { + info[count] = value; + check[value] = count; + ++count; + } + } + + inline void remove(unsigned value) { + const unsigned index = check[value]; + if (index < count && info[index] == value) { + if (--count) { + const int v = info[count]; + check[v] = index; + info[index] = v; + } + } + } + + inline bool contains(unsigned value) const { + const unsigned index = check[value]; + return index < count && info[index] == value; + } + + template + void insert(_It first, _It last) { + for (; first != last; ++first) + insert(*first); + } + + template + void remove(_It first, _It last) { + for (; first != last; ++first) + remove(*first); + } + + void insert(const IntSet &other) { + insert(other.begin(), other.end()); + } + + bool isEqualTo(const unsigned *it, unsigned length) const { + if (count == length) { + for (unsigned i = 0; i < length; ++i) { + if (! contains(it[i])) + return false; + } + return true; + } + return false; + } + + bool operator == (const IntSet &other) const { + if (count == other.count) { + for (unsigned i = 0; i < count; ++i) { + if (! other.contains(info[i])) + return false; + } + return true; + } + return false; + } + + bool operator != (const IntSet &other) const { + return ! operator == (other); + } +}; + +void edge(IR::BasicBlock *source, IR::BasicBlock *target) +{ + if (! source->out.contains(target)) + source->out.append(target); + + if (! target->in.contains(source)) + target->in.append(source); +} + +void dfs(IR::BasicBlock *block, + QSet *V, + QVector *blocks) +{ + if (! V->contains(block)) { + V->insert(block); + + foreach (IR::BasicBlock *succ, block->out) + dfs(succ, V, blocks); + + blocks->append(block); + } +} + +struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor +{ + IR::Function *_function; + IR::Stmt *_stmt; + + ComputeUseDef(IR::Function *function) + : _function(function) + , _stmt(0) {} + + void operator()(IR::Stmt *s) { + qSwap(_stmt, s); + _stmt->accept(this); + qSwap(_stmt, s); + } + + virtual void visitConst(IR::Const *) {} + virtual void visitString(IR::String *) {} + virtual void visitName(IR::Name *) {} + virtual void visitClosure(IR::Closure *) {} + virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); } + virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(IR::Member *e) { e->base->accept(this); } + virtual void visitExp(IR::Exp *s) { s->expr->accept(this); } + virtual void visitEnter(IR::Enter *) {} + virtual void visitLeave(IR::Leave *) {} + virtual void visitJump(IR::Jump *) {} + virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); } + virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } + + virtual void visitTemp(IR::Temp *e) { + if (! _stmt->uses.contains(e->index)) + _stmt->uses.append(e->index); + } + + virtual void visitCall(IR::Call *e) { + e->base->accept(this); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(IR::New *e) { + e->base->accept(this); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitMove(IR::Move *s) { + if (IR::Temp *t = s->target->asTemp()) { + if (! _stmt->defs.contains(t->index)) + _stmt->defs.append(t->index); + } else { + s->target->accept(this); + } + s->source->accept(this); + } +}; + +struct RegAlloc: IR::StmtVisitor, IR::ExprVisitor +{ + QHash RD; + QHash RA; + QList _freeRegisters; + QSet _processed; + QSet _kill; + IR::Stmt *_stmt; + unsigned _registerCount; + + RegAlloc(): _stmt(0), _registerCount(1) {} + + void operator()(IR::Stmt *s) { + _kill.clear(); + qSwap(_stmt, s); + _stmt->accept(this); + qSwap(_stmt, s); + foreach (unsigned r, _kill) + freeRegister(r); + } + + unsigned newReg() + { + if (! _freeRegisters.isEmpty()) + return _freeRegisters.takeLast(); + + return _registerCount++; + } + + void freeRegister(unsigned virtualRegister) + { + unsigned physRegister = RA.value(virtualRegister); + RA.remove(virtualRegister); + RD.remove(physRegister); + + if (! _freeRegisters.contains(physRegister)) + _freeRegisters.append(physRegister); + } + + unsigned getReg(IR::Temp *t) + { + const int virtualRegister = t->index; + + if (unsigned reg = RA.value(virtualRegister)) { + return reg; + } + + const unsigned reg = newReg(); + RA[virtualRegister] = reg; + RD[reg] = virtualRegister; + return reg; + } + + virtual void visitConst(IR::Const *) {} + virtual void visitString(IR::String *) {} + virtual void visitName(IR::Name *) {} + virtual void visitClosure(IR::Closure *) {} + + virtual void visitTemp(IR::Temp *e) { + const unsigned virtualRegister = e->index; + e->index = getReg(e); + if (! _stmt->liveOut.contains(virtualRegister)) + _kill.insert(virtualRegister); + } + + virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); } + virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitCall(IR::Call *e) { + e->base->accept(this); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(IR::New *e) { + e->base->accept(this); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(IR::Member *e) { e->base->accept(this); } + + virtual void visitExp(IR::Exp *s) { s->expr->accept(this); } + virtual void visitEnter(IR::Enter *) {} + virtual void visitLeave(IR::Leave *) {} + + virtual void visitMove(IR::Move *s) { + s->source->accept(this); + s->target->accept(this); + } + + virtual void visitJump(IR::Jump *) {} + virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); } + virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } +}; + +void liveness(IR::Function *function) +{ + QSet V; + QVector blocks; + + // + // compute the CFG + // + foreach (IR::BasicBlock *block, function->basicBlocks) { + if (IR::Stmt *term = block->terminator()) { + if (IR::Jump *j = term->asJump()) + edge(block, j->target); + else if (IR::CJump *cj = term->asCJump()) { + edge(block, cj->iftrue); + edge(block, cj->iffalse); + } + } + } + + ComputeUseDef computeUseDef(function); + foreach (IR::BasicBlock *block, function->basicBlocks) { + foreach (IR::Stmt *s, block->statements) + computeUseDef(s); + } + + dfs(function->basicBlocks.first(), &V, &blocks); + + IntSet liveOut(function->tempCount); + bool changed; + + do { + changed = false; + + foreach (IR::BasicBlock *block, blocks) { + liveOut.clear(); + + foreach (IR::BasicBlock *succ, block->out) + liveOut.insert(succ->liveIn.begin(), succ->liveIn.end()); + + if (block->out.isEmpty() || liveOut.empty() || ! liveOut.isEqualTo(block->liveOut.constData(), block->liveOut.size())) { + block->liveOut.resize(liveOut.count); + qCopy(liveOut.begin(), liveOut.end(), block->liveOut.begin()); + + for (int i = block->statements.size() - 1; i != -1; --i) { + IR::Stmt *stmt = block->statements.at(i); + stmt->liveOut.resize(liveOut.count); + qCopy(liveOut.begin(), liveOut.end(), stmt->liveOut.begin()); + liveOut.remove(stmt->defs.begin(), stmt->defs.end()); + liveOut.insert(stmt->uses.begin(), stmt->uses.end()); + stmt->liveIn.resize(liveOut.count); + qCopy(liveOut.begin(), liveOut.end(), stmt->liveIn.begin()); + } + + if (! changed && ! liveOut.isEqualTo(block->liveIn.constData(), block->liveIn.size())) + changed = true; + + block->liveIn.resize(liveOut.count); + qCopy(liveOut.begin(), liveOut.end(), block->liveIn.begin()); + } + } + } while (changed); +} + +struct FindLocals: Visitor +{ + using Visitor::visit; + + QList locals; + + virtual bool visit(VariableDeclaration *ast) + { + if (! locals.contains(ast->name)) + locals.append(ast->name); + return true; + } + + virtual bool visit(FunctionExpression *ast) + { + if (! locals.contains(ast->name)) + locals.append(ast->name); + return false; + } + + virtual bool visit(FunctionDeclaration *ast) + { + if (! locals.contains(ast->name)) + locals.append(ast->name); + return false; + } +}; + +} // end of anonymous namespace + +Codegen::Codegen() + : _function(0) + , _block(0) + , _exitBlock(0) + , _returnAddress(0) +{ +} + +void Codegen::operator()(AST::Program *node) +{ + IR::Module module; + _module = &module; + + IR::Function *globalCode = _module->newFunction(QLatin1String("%entry")); + _function = globalCode; + _block = _function->newBasicBlock(); + _exitBlock = _function->newBasicBlock(); + _returnAddress = _block->newTemp(); + _exitBlock->RET(_exitBlock->TEMP(_returnAddress), IR::UndefinedType); + + program(node); + + if (! _block->isTerminated()) { + _block->JUMP(_exitBlock); + } + + InstructionSelection isel(_module); + + foreach (IR::Function *function, _module->functions) { + linearize(function); + isel.visitFunction(function); + } +} + +IR::Expr *Codegen::member(IR::Expr *base, const QString *name) +{ + if (base->asTemp() || base->asName()) + return _block->MEMBER(base, name); + else { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), base); + return _block->MEMBER(_block->TEMP(t), name); + } +} + +IR::Expr *Codegen::subscript(IR::Expr *base, IR::Expr *index) +{ + if (base->asTemp() || base->asName()) + return _block->SUBSCRIPT(base, index); + else { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), base); + return _block->SUBSCRIPT(_block->TEMP(t), index); + } +} + +IR::Expr *Codegen::argument(IR::Expr *expr) +{ + if (expr && ! expr->asTemp()) { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + return expr; +} + +IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) +{ + if (left && ! left->asTemp()) { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + if (right && ! right->asTemp()) { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), right); + right = _block->TEMP(t); + } + + return _block->BINOP(op, left, right); +} + +void Codegen::accept(Node *node) +{ + if (node) + node->accept(this); +} + +void Codegen::statement(Statement *ast) +{ + accept(ast); +} + +void Codegen::statement(ExpressionNode *ast) +{ + if (ast) { + Result r(nx); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + if (r.format == ex) { + _block->EXP(*r); + } + } +} + +void Codegen::condition(ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) +{ + if (ast) { + Result r(iftrue, iffalse); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + if (r.format == ex) { + _block->CJUMP(*r, r.iftrue, r.iffalse); + } + } +} + +Codegen::Result Codegen::expression(ExpressionNode *ast) +{ + Result r; + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + } + return r; +} + +QString Codegen::propertyName(PropertyName *ast) +{ + QString p; + if (ast) { + qSwap(_property, p); + accept(ast); + qSwap(_property, p); + } + return p; +} + +Codegen::Result Codegen::sourceElement(SourceElement *ast) +{ + Result r(nx); + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + } + return r; +} + +Codegen::UiMember Codegen::uiObjectMember(UiObjectMember *ast) +{ + UiMember m; + if (ast) { + qSwap(_uiMember, m); + accept(ast); + qSwap(_uiMember, m); + } + return m; +} + +void Codegen::argumentList(ArgumentList *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::caseBlock(CaseBlock *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::caseClause(CaseClause *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::caseClauses(CaseClauses *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::catchNode(Catch *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::defaultClause(DefaultClause *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::elementList(ElementList *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::elision(Elision *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::finallyNode(Finally *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::formalParameterList(FormalParameterList *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::functionBody(FunctionBody *ast) +{ + if (ast) + sourceElements(ast->elements); +} + +void Codegen::program(Program *ast) +{ + if (ast) + sourceElements(ast->elements); +} + +void Codegen::propertyNameAndValueList(PropertyNameAndValueList *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::sourceElements(SourceElements *ast) +{ + for (SourceElements *it = ast; it; it = it->next) { + sourceElement(it->element); + } +} + +void Codegen::statementList(StatementList *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::uiArrayMemberList(UiArrayMemberList *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::uiImport(UiImport *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::uiImportList(UiImportList *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::uiObjectInitializer(UiObjectInitializer *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::uiObjectMemberList(UiObjectMemberList *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::uiParameterList(UiParameterList *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::uiProgram(UiProgram *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::uiQualifiedId(UiQualifiedId *) +{ + Q_ASSERT(!"not implemented"); +} + +void Codegen::variableDeclaration(VariableDeclaration *ast) +{ + if (ast->expression) { + Result expr = expression(ast->expression); + + if (expr.code) + _block->MOVE(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), *expr); + } +} + +void Codegen::variableDeclarationList(VariableDeclarationList *ast) +{ + for (VariableDeclarationList *it = ast; it; it = it->next) { + variableDeclaration(it->declaration); + } +} + + +bool Codegen::visit(ArgumentList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseBlock *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseClause *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseClauses *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(Catch *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(DefaultClause *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(ElementList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(Elision *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(Finally *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(FormalParameterList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(FunctionBody *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(Program *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyNameAndValueList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(SourceElements *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(StatementList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(UiArrayMemberList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(UiImport *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(UiImportList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(UiObjectInitializer *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(UiObjectMemberList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(UiParameterList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(UiProgram *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(UiQualifiedId *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(VariableDeclaration *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(VariableDeclarationList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool Codegen::visit(Expression *ast) +{ + statement(ast->left); + accept(ast->right); + return false; +} + +bool Codegen::visit(ArrayLiteral *ast) +{ + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), _block->NEW(_block->NAME(QLatin1String("Array"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); + int index = 0; + for (ElementList *it = ast->elements; it; it = it->next) { + for (Elision *elision = it->elision; elision; elision = elision->next) + ++index; + Result expr = expression(it->expression); + _block->MOVE(subscript(_block->TEMP(t), _block->CONST(IR::NumberType, index)), *expr); + ++index; + } + for (Elision *elision = ast->elision; elision; elision = elision->next) + ++index; // ### set the size of the array + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(ArrayMemberExpression *ast) +{ + Result base = expression(ast->base); + Result index = expression(ast->expression); + _expr.code = subscript(*base, *index); + return false; +} + +static IR::AluOp baseOp(int op) +{ + switch ((QSOperator::Op) op) { + case QSOperator::InplaceAnd: return IR::OpAnd; + case QSOperator::InplaceSub: return IR::OpSub; + case QSOperator::InplaceDiv: return IR::OpDiv; + case QSOperator::InplaceAdd: return IR::OpAdd; + case QSOperator::InplaceLeftShift: return IR::OpLShift; + case QSOperator::InplaceMod: return IR::OpMod; + case QSOperator::InplaceMul: return IR::OpMul; + case QSOperator::InplaceOr: return IR::OpBitOr; + case QSOperator::InplaceRightShift: return IR::OpRShift; + case QSOperator::InplaceURightShift: return IR::OpURShift; + case QSOperator::InplaceXor: return IR::OpBitXor; + default: return IR::OpInvalid; + } +} + +bool Codegen::visit(BinaryExpression *ast) +{ + if (ast->op == QSOperator::And) { + if (_expr.accept(cx)) { + IR::BasicBlock *iftrue = _function->newBasicBlock(); + condition(ast->left, iftrue, _expr.iffalse); + _block = iftrue; + condition(ast->right, _expr.iftrue, _expr.iffalse); + } else { + IR::BasicBlock *iftrue = _function->newBasicBlock(); + IR::BasicBlock *iffalse = _function->newBasicBlock(); + IR::BasicBlock *endif = _function->newBasicBlock(); + + const unsigned r = _block->newTemp(); + + condition(ast->left, iftrue, iffalse); + _block = iffalse; + + _block->MOVE(_block->TEMP(r), _block->CONST(IR::BoolType, 0)); + _block->JUMP(endif); + + _block = iftrue; + _block->MOVE(_block->TEMP(r), *expression(ast->right)); + if (! _block->isTerminated()) + _block->JUMP(endif); + + _expr.code = _block->TEMP(r); + _block = endif; + } + return false; + } else if (ast->op == QSOperator::Or) { + if (_expr.accept(cx)) { + IR::BasicBlock *iffalse = _function->newBasicBlock(); + condition(ast->left, _expr.iftrue, iffalse); + _block = iffalse; + condition(ast->right, _expr.iftrue, _expr.iffalse); + } else { + IR::BasicBlock *iffalse = _function->newBasicBlock(); + IR::BasicBlock *endif = _function->newBasicBlock(); + + const unsigned r = _block->newTemp(); + _block->MOVE(_block->TEMP(r), *expression(ast->left)); + _block->CJUMP(_block->TEMP(r), endif, iffalse); + _block = iffalse; + _block->MOVE(_block->TEMP(r), *expression(ast->right)); + if (! _block->isTerminated()) + _block->JUMP(endif); + + _block = endif; + _expr.code = _block->TEMP(r); + } + return false; + } + + Result left = expression(ast->left); + Result right = expression(ast->right); + + switch (ast->op) { + case QSOperator::Or: + case QSOperator::And: + break; + + case QSOperator::Assign: + if (_expr.accept(nx)) { + _block->MOVE(*left, *right); + } else { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), *right); + _block->MOVE(*left, _block->TEMP(t)); + _expr.code = _block->TEMP(t); + } + break; + + case QSOperator::InplaceAnd: + case QSOperator::InplaceSub: + case QSOperator::InplaceDiv: + case QSOperator::InplaceAdd: + case QSOperator::InplaceLeftShift: + case QSOperator::InplaceMod: + case QSOperator::InplaceMul: + case QSOperator::InplaceOr: + case QSOperator::InplaceRightShift: + case QSOperator::InplaceURightShift: + case QSOperator::InplaceXor: { + _block->MOVE(*left, *right, baseOp(ast->op)); + if (_expr.accept(nx)) { + // nothing to do + } else { + _expr.code = *left; + } + break; + } + + case QSOperator::In: + case QSOperator::InstanceOf: + case QSOperator::Equal: + case QSOperator::NotEqual: + case QSOperator::Ge: + case QSOperator::Gt: + case QSOperator::Le: + case QSOperator::Lt: + case QSOperator::StrictEqual: + case QSOperator::StrictNotEqual: + if (false && _expr.accept(cx)) { + _block->CJUMP(binop(IR::binaryOperator(ast->op), *left, *right), _expr.iftrue, _expr.iffalse); + } else { + IR::Expr *e = binop(IR::binaryOperator(ast->op), *left, *right); + if (e->asConst() || e->asString()) + _expr.code = e; + else { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), e); + _expr.code = _block->TEMP(t); + } + } + break; + + case QSOperator::Add: + case QSOperator::BitAnd: + case QSOperator::BitOr: + case QSOperator::BitXor: + case QSOperator::Div: + case QSOperator::LShift: + case QSOperator::Mod: + case QSOperator::Mul: + case QSOperator::RShift: + case QSOperator::Sub: + case QSOperator::URShift: { + IR::Expr *e = binop(IR::binaryOperator(ast->op), *left, *right); + if (e->asConst() || e->asString()) + _expr.code = e; + else { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), e); + _expr.code = _block->TEMP(t); + } + break; + } + + } // switch + + return false; +} + +bool Codegen::visit(CallExpression *ast) +{ + Result base = expression(ast->base); + IR::ExprList *args = 0, **args_it = &args; + for (ArgumentList *it = ast->arguments; it; it = it->next) { + Result arg = expression(it->expression); + IR::Expr *actual = argument(*arg); + *args_it = _function->New(); + (*args_it)->init(actual); + args_it = &(*args_it)->next; + } + _expr.code = _block->CALL(*base, args); + return false; +} + +bool Codegen::visit(ConditionalExpression *ast) +{ + IR::BasicBlock *iftrue = _function->newBasicBlock(); + IR::BasicBlock *iffalse = _function->newBasicBlock(); + IR::BasicBlock *endif = _function->newBasicBlock(); + + const unsigned t = _block->newTemp(); + + condition(ast->expression, iftrue, iffalse); + + _block = iftrue; + _block->MOVE(_block->TEMP(t), *expression(ast->ok)); + _block->JUMP(endif); + + _block = iffalse; + _block->MOVE(_block->TEMP(t), *expression(ast->ko)); + _block->JUMP(endif); + + _block = endif; + + _expr.code = _block->TEMP(t); + + return false; +} + +bool Codegen::visit(DeleteExpression *) +{ + Q_ASSERT(!"not implemented"); + return false; +} + +bool Codegen::visit(FalseLiteral *) +{ + _expr.code = _block->CONST(IR::BoolType, 0); + return false; +} + +bool Codegen::visit(FieldMemberExpression *ast) +{ + Result base = expression(ast->base); + _expr.code = member(*base, _function->newString(ast->name.toString())); + return false; +} + +bool Codegen::visit(FunctionExpression *ast) +{ + defineFunction(ast, false); + return false; +} + +bool Codegen::visit(IdentifierExpression *ast) +{ + _expr.code = _block->NAME(ast->name.toString(), + ast->identifierToken.startLine, + ast->identifierToken.startColumn); + return false; +} + +bool Codegen::visit(NestedExpression *ast) +{ + accept(ast->expression); + return false; +} + +bool Codegen::visit(NewExpression *ast) +{ + Result base = expression(ast->expression); + _expr.code = _block->NEW(*base, 0); + return false; +} + +bool Codegen::visit(NewMemberExpression *ast) +{ + Result base = expression(ast->base); + IR::ExprList *args = 0, **args_it = &args; + for (ArgumentList *it = ast->arguments; it; it = it->next) { + Result arg = expression(it->expression); + IR::Expr *actual = argument(*arg); + *args_it = _function->New(); + (*args_it)->init(actual); + args_it = &(*args_it)->next; + } + _expr.code = _block->NEW(*base, args); + return false; +} + +bool Codegen::visit(NotExpression *ast) +{ + Result expr = expression(ast->expression); + if (_expr.accept(cx)) { + _block->CJUMP(_block->UNOP(IR::OpNot, *expr), _expr.iftrue, _expr.iffalse); + } else { + const unsigned r = _block->newTemp(); + _block->MOVE(_block->TEMP(r), _block->UNOP(IR::OpNot, *expr)); + _expr.code = _block->TEMP(r); + } + return false; +} + +bool Codegen::visit(NullExpression *) +{ + _expr.code = _block->CONST(IR::NullType, 0); + return false; +} + +bool Codegen::visit(NumericLiteral *ast) +{ + _expr.code = _block->CONST(IR::NumberType, ast->value); + return false; +} + +bool Codegen::visit(ObjectLiteral *ast) +{ + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), _block->NEW(_block->NAME(QLatin1String("Object"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); + for (PropertyNameAndValueList *it = ast->properties; it; it = it->next) { + QString name = propertyName(it->name); + Result value = expression(it->value); + _block->MOVE(member(_block->TEMP(t), _function->newString(name)), *value); + } + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(PostDecrementExpression *ast) +{ + Result expr = expression(ast->base); + if (_expr.accept(nx)) { + _block->MOVE(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub); + } else { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), *expr); + _block->MOVE(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub); + _expr.code = _block->TEMP(t); + } + return false; +} + +bool Codegen::visit(PostIncrementExpression *ast) +{ + Result expr = expression(ast->base); + if (_expr.accept(nx)) { + _block->MOVE(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd); + } else { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), *expr); + _block->MOVE(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd); + _expr.code = _block->TEMP(t); + } + return false; +} + +bool Codegen::visit(PreDecrementExpression *ast) +{ + Result expr = expression(ast->expression); + _block->MOVE(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub); + if (_expr.accept(nx)) { + // nothing to do + } else { + _expr.code = *expr; + } + return false; +} + +bool Codegen::visit(PreIncrementExpression *ast) +{ + Result expr = expression(ast->expression); + _block->MOVE(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd); + + if (_expr.accept(nx)) { + // nothing to do + } else { + _expr.code = *expr; + } + return false; +} + +bool Codegen::visit(RegExpLiteral *) +{ + Q_ASSERT(!"not implemented"); + return false; +} + +bool Codegen::visit(StringLiteral *ast) +{ + _expr.code = _block->STRING(_function->newString(ast->value.toString())); + return false; +} + +bool Codegen::visit(ThisExpression *ast) +{ + _expr.code = _block->NAME(QLatin1String("this"), ast->thisToken.startLine, ast->thisToken.startColumn); + return false; +} + +bool Codegen::visit(TildeExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), _block->UNOP(IR::OpCompl, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(TrueLiteral *) +{ + _expr.code = _block->CONST(IR::BoolType, 1); + return false; +} + +bool Codegen::visit(TypeOfExpression *ast) +{ + Result expr = expression(ast->expression); + IR::ExprList *args = _function->New(); + args->init(argument(*expr)); + _expr.code = _block->CALL(_block->NAME(QLatin1String("typeof"), ast->typeofToken.startLine, ast->typeofToken.startColumn), args); + return false; +} + +bool Codegen::visit(UnaryMinusExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), _block->UNOP(IR::OpUMinus, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(UnaryPlusExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), _block->UNOP(IR::OpUPlus, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(VoidExpression *ast) +{ + statement(ast->expression); // ### CHECK + return false; +} + +bool Codegen::visit(FunctionDeclaration *ast) +{ + defineFunction(ast); + return false; +} + +void Codegen::linearize(IR::Function *function) +{ + IR::BasicBlock *entryBlock = function->basicBlocks.at(0); + IR::BasicBlock *exitBlock = function->basicBlocks.at(1); + + Q_UNUSED(entryBlock); + + QSet V; + V.insert(exitBlock); + + QVector trace; + + for (int i = 0; i < function->basicBlocks.size(); ++i) { + IR::BasicBlock *block = function->basicBlocks.at(i); + if (block->statements.isEmpty()) { + if ((i + 1) < function->basicBlocks.size()) { + IR::BasicBlock *next = function->basicBlocks.at(i + 1); + block->JUMP(next); + } + } + } + + foreach (IR::BasicBlock *block, function->basicBlocks) { + while (block) { + if (V.contains(block)) + break; + + V.insert(block); + block->index = trace.size(); + trace.append(block); + + if (IR::Stmt *term = block->terminator()) { + block = 0; + + if (IR::Jump *j = term->asJump()) { + block = j->target; + } else if (IR::CJump *cj = term->asCJump()) { + if (! V.contains(cj->iffalse)) + block = cj->iffalse; + else + block = cj->iftrue; + } + } + } + } + exitBlock->index = trace.size(); + trace.append(exitBlock); + function->basicBlocks = trace; + + liveness(function); + + { + RegAlloc regalloc; + foreach (IR::BasicBlock *block, function->basicBlocks) { + foreach (IR::Stmt *s, block->statements) + regalloc(s); + } + function->tempCount = regalloc._registerCount - 1; + } + + static bool showCode = !qgetenv("SHOW_CODE").isNull(); + if (showCode) { + QVector code; + QHash leader; + + foreach (IR::BasicBlock *block, function->basicBlocks) { + leader.insert(block->statements.first(), block); + foreach (IR::Stmt *s, block->statements) { + code.append(s); + } + } + + QString name; + if (function->name && !function->name->isEmpty()) + name = *function->name; + else + name.sprintf("%p", function); + + qout << "function " << name << "("; + for (int i = 0; i < function->formals.size(); ++i) { + if (i != 0) + qout << ", "; + qout << *function->formals.at(i); + } + qout << ")" << endl + << "{" << endl; + + foreach (const QString *local, function->locals) { + qout << " var " << *local << ';' << endl; + } + + for (int i = 0; i < code.size(); ++i) { + IR::Stmt *s = code.at(i); + + if (IR::BasicBlock *bb = leader.value(s)) { + qout << endl; + qout << 'L' << bb << ':' << endl; + } + IR::Stmt *n = (i + 1) < code.size() ? code.at(i + 1) : 0; + if (n && s->asJump() && s->asJump()->target == leader.value(n)) { + continue; + } + + QByteArray str; + QBuffer buf(&str); + buf.open(QIODevice::WriteOnly); + QTextStream out(&buf); + s->dump(out, IR::Stmt::MIR); + out.flush(); + + for (int i = 60 - str.size(); i >= 0; --i) + str.append(' '); + + qout << " " << str; + + // if (! s->uses.isEmpty()) { + // qout << " // uses:"; + // foreach (unsigned use, s->uses) { + // qout << " %" << use; + // } + // } + + // if (! s->defs.isEmpty()) { + // qout << " // defs:"; + // foreach (unsigned def, s->defs) { + // qout << " %" << def; + // } + // } + + // if (! s->liveOut.isEmpty()) { + // qout << " // lives:"; + // foreach (unsigned live, s->liveOut) { + // qout << " %" << live; + // } + // } + + qout << endl; + + if (n && s->asCJump() && s->asCJump()->iffalse != leader.value(n)) { + qout << " goto L" << s->asCJump()->iffalse << ";" << endl; + } + } + + qout << "}" << endl + << endl; + } +} + +void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) +{ + IR::Function *function = _module->newFunction(ast->name.toString()); + IR::BasicBlock *entryBlock = function->newBasicBlock(); + IR::BasicBlock *exitBlock = function->newBasicBlock(); + unsigned returnAddress = entryBlock->newTemp(); + + exitBlock->RET(_block->TEMP(returnAddress), IR::InvalidType); + + qSwap(_function, function); + qSwap(_block, entryBlock); + qSwap(_exitBlock, exitBlock); + qSwap(_returnAddress, returnAddress); + + for (FormalParameterList *it = ast->formals; it; it = it->next) { + _function->RECEIVE(it->name.toString()); + } + + FindLocals locals; + if (ast->body) + ast->body->accept(&locals); + + foreach (const QStringRef &local, locals.locals) { + _function->LOCAL(local.toString()); + } + + functionBody(ast->body); + + if (! _block->isTerminated()) + _block->JUMP(_exitBlock); + + qSwap(_function, function); + qSwap(_block, entryBlock); + qSwap(_exitBlock, exitBlock); + qSwap(_returnAddress, returnAddress); + + if (_expr.accept(nx)) { + // nothing to do + } else { + _expr.code = _block->CLOSURE(function); + } +} + +bool Codegen::visit(IdentifierPropertyName *ast) +{ + _property = ast->id.toString(); + return false; +} + +bool Codegen::visit(NumericLiteralPropertyName *ast) +{ + _property = QString::number(ast->id, 'g', 16); + return false; +} + +bool Codegen::visit(StringLiteralPropertyName *ast) +{ + _property = ast->id.toString(); + return false; +} + +bool Codegen::visit(FunctionSourceElement *ast) +{ + statement(ast->declaration); + return false; +} + +bool Codegen::visit(StatementSourceElement *ast) +{ + statement(ast->statement); + return false; +} + +bool Codegen::visit(Block *ast) +{ + for (StatementList *it = ast->statements; it; it = it->next) { + statement(it->statement); + } + return false; +} + +bool Codegen::visit(BreakStatement *) +{ + _block->JUMP(_loop.breakBlock); + return false; +} + +bool Codegen::visit(ContinueStatement *) +{ + _block->JUMP(_loop.continueBlock); + return false; +} + +bool Codegen::visit(DebuggerStatement *) +{ + Q_ASSERT(!"not implemented"); + return false; +} + +bool Codegen::visit(DoWhileStatement *ast) +{ + IR::BasicBlock *loopbody = _function->newBasicBlock(); + IR::BasicBlock *loopend = _function->newBasicBlock(); + + Loop loop(loopend, loopbody); + qSwap(_loop, loop); + + if (_block->isTerminated()) + _block->JUMP(loopbody); + _block = loopbody; + statement(ast->statement); + condition(ast->expression, loopbody, loopend); + _block = loopend; + + qSwap(_loop, loop); + return false; +} + +bool Codegen::visit(EmptyStatement *) +{ + return false; +} + +bool Codegen::visit(ExpressionStatement *ast) +{ + statement(ast->expression); + return false; +} + +bool Codegen::visit(ForEachStatement *) +{ + Q_ASSERT(!"not implemented"); + return false; +} + +bool Codegen::visit(ForStatement *ast) +{ + IR::BasicBlock *forcond = _function->newBasicBlock(); + IR::BasicBlock *forbody = _function->newBasicBlock(); + IR::BasicBlock *forstep = _function->newBasicBlock(); + IR::BasicBlock *forend = _function->newBasicBlock(); + + Loop loop(forend, forstep); + qSwap(_loop, loop); + + statement(ast->initialiser); + if (! _block->isTerminated()) + _block->JUMP(forcond); + + _block = forcond; + condition(ast->condition, forbody, forend); + + _block = forbody; + statement(ast->statement); + if (! _block->isTerminated()) + _block->JUMP(forstep); + + _block = forstep; + statement(ast->expression); + if (! _block->isTerminated()) + _block->JUMP(forcond); + _block = forend; + + qSwap(_loop, loop); + + return false; +} + +bool Codegen::visit(IfStatement *ast) +{ + IR::BasicBlock *iftrue = _function->newBasicBlock(); + IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock() : 0; + IR::BasicBlock *endif = _function->newBasicBlock(); + condition(ast->expression, iftrue, ast->ko ? iffalse : endif); + + _block = iftrue; + statement(ast->ok); + if (! _block->isTerminated()) + _block->JUMP(endif); + + if (ast->ko) { + _block = iffalse; + statement(ast->ko); + if (! _block->isTerminated()) + _block->JUMP(endif); + } + + _block = endif; + + return false; +} + +bool Codegen::visit(LabelledStatement *) +{ + Q_ASSERT(!"not implemented"); + return false; +} + +bool Codegen::visit(LocalForEachStatement *) +{ + Q_ASSERT(!"not implemented"); + return false; +} + +bool Codegen::visit(LocalForStatement *ast) +{ + IR::BasicBlock *forcond = _function->newBasicBlock(); + IR::BasicBlock *forbody = _function->newBasicBlock(); + IR::BasicBlock *forstep = _function->newBasicBlock(); + IR::BasicBlock *forend = _function->newBasicBlock(); + + Loop loop(forend, forstep); + qSwap(_loop, loop); + + variableDeclarationList(ast->declarations); + if (! _block->isTerminated()) + _block->JUMP(forcond); + + _block = forcond; + condition(ast->condition, forbody, forend); + + _block = forbody; + statement(ast->statement); + if (! _block->isTerminated()) + _block->JUMP(forstep); + + _block = forstep; + statement(ast->expression); + if (! _block->isTerminated()) + _block->JUMP(forcond); + _block = forend; + + qSwap(_loop, loop); + return false; +} + +bool Codegen::visit(ReturnStatement *ast) +{ + if (ast->expression) { + Result expr = expression(ast->expression); + _block->MOVE(_block->TEMP(_returnAddress), *expr); + } + + _block->JUMP(_exitBlock); + return false; +} + +bool Codegen::visit(SwitchStatement *) +{ + Q_ASSERT(!"not implemented"); + return false; +} + +bool Codegen::visit(ThrowStatement *) +{ + //Q_ASSERT(!"not implemented"); + _expr.code = _block->CONST(IR::UndefinedType, 0); + return false; +} + +bool Codegen::visit(TryStatement *) +{ + Q_ASSERT(!"not implemented"); + return false; +} + +bool Codegen::visit(VariableStatement *ast) +{ + variableDeclarationList(ast->declarations); + return false; +} + +bool Codegen::visit(WhileStatement *ast) +{ + IR::BasicBlock *whilecond = _function->newBasicBlock(); + IR::BasicBlock *whilebody = _function->newBasicBlock(); + IR::BasicBlock *whileend = _function->newBasicBlock(); + + Loop loop(whileend, whilecond); + qSwap(_loop, loop); + + _block->JUMP(whilecond); + _block = whilecond; + condition(ast->expression, whilebody, whileend); + + _block = whilebody; + statement(ast->statement); + if (! _block->isTerminated()) + _block->JUMP(whilecond); + + _block = whileend; + qSwap(_loop, loop); + + return false; +} + +bool Codegen::visit(WithStatement *) +{ + Q_ASSERT(!"not implemented"); + return false; +} + +bool Codegen::visit(UiArrayBinding *) +{ + Q_ASSERT(!"not implemented"); + return false; +} + +bool Codegen::visit(UiObjectBinding *) +{ + Q_ASSERT(!"not implemented"); + return false; +} + +bool Codegen::visit(UiObjectDefinition *) +{ + Q_ASSERT(!"not implemented"); + return false; +} + +bool Codegen::visit(UiPublicMember *) +{ + Q_ASSERT(!"not implemented"); + return false; +} + +bool Codegen::visit(UiScriptBinding *) +{ + Q_ASSERT(!"not implemented"); + return false; +} + +bool Codegen::visit(UiSourceElement *) +{ + Q_ASSERT(!"not implemented"); + return false; +} diff --git a/qv4codegen_p.h b/qv4codegen_p.h new file mode 100644 index 0000000000..6f3cb90015 --- /dev/null +++ b/qv4codegen_p.h @@ -0,0 +1,228 @@ +#ifndef QV4CODEGEN_P_H +#define QV4CODEGEN_P_H + +#include "qv4ir_p.h" +#include + +namespace QQmlJS { + +namespace AST { +class UiParameterList; +} + +class Codegen: protected AST::Visitor +{ +public: + Codegen(); + + void operator()(AST::Program *ast); + +protected: + enum Format { ex, cx, nx }; + struct Result { + IR::Expr *code; + IR::BasicBlock *iftrue; + IR::BasicBlock *iffalse; + Format format; + Format requested; + + explicit Result(Format requested = ex) + : code(0) + , iftrue(0) + , iffalse(0) + , format(ex) + , requested(requested) {} + + explicit Result(IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) + : code(0) + , iftrue(iftrue) + , iffalse(iffalse) + , format(ex) + , requested(cx) {} + + inline IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; } + inline IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; } + + bool accept(Format f) + { + if (requested == f) { + format = f; + return true; + } + return false; + } + }; + + struct UiMember { + }; + + struct Loop { + IR::BasicBlock *breakBlock; + IR::BasicBlock *continueBlock; + + Loop() + : breakBlock(0), continueBlock(0) {} + + Loop(IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock) + : breakBlock(breakBlock), continueBlock(continueBlock) {} + }; + + IR::Expr *member(IR::Expr *base, const QString *name); + IR::Expr *subscript(IR::Expr *base, IR::Expr *index); + IR::Expr *argument(IR::Expr *expr); + IR::Expr *binop(IR::AluOp op, IR::Expr *left, IR::Expr *right); + + void linearize(IR::Function *function); + void defineFunction(AST::FunctionExpression *ast, bool isDeclaration = false); + + void statement(AST::Statement *ast); + void statement(AST::ExpressionNode *ast); + void condition(AST::ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse); + Result expression(AST::ExpressionNode *ast); + QString propertyName(AST::PropertyName *ast); + Result sourceElement(AST::SourceElement *ast); + UiMember uiObjectMember(AST::UiObjectMember *ast); + + void accept(AST::Node *node); + + void argumentList(AST::ArgumentList *ast); + void caseBlock(AST::CaseBlock *ast); + void caseClause(AST::CaseClause *ast); + void caseClauses(AST::CaseClauses *ast); + void catchNode(AST::Catch *ast); + void defaultClause(AST::DefaultClause *ast); + void elementList(AST::ElementList *ast); + void elision(AST::Elision *ast); + void finallyNode(AST::Finally *ast); + void formalParameterList(AST::FormalParameterList *ast); + void functionBody(AST::FunctionBody *ast); + void program(AST::Program *ast); + void propertyNameAndValueList(AST::PropertyNameAndValueList *ast); + void sourceElements(AST::SourceElements *ast); + void statementList(AST::StatementList *ast); + void uiArrayMemberList(AST::UiArrayMemberList *ast); + void uiImport(AST::UiImport *ast); + void uiImportList(AST::UiImportList *ast); + void uiObjectInitializer(AST::UiObjectInitializer *ast); + void uiObjectMemberList(AST::UiObjectMemberList *ast); + void uiParameterList(AST::UiParameterList *ast); + void uiProgram(AST::UiProgram *ast); + void uiQualifiedId(AST::UiQualifiedId *ast); + void variableDeclaration(AST::VariableDeclaration *ast); + void variableDeclarationList(AST::VariableDeclarationList *ast); + + // nodes + virtual bool visit(AST::ArgumentList *ast); + virtual bool visit(AST::CaseBlock *ast); + virtual bool visit(AST::CaseClause *ast); + virtual bool visit(AST::CaseClauses *ast); + virtual bool visit(AST::Catch *ast); + virtual bool visit(AST::DefaultClause *ast); + virtual bool visit(AST::ElementList *ast); + virtual bool visit(AST::Elision *ast); + virtual bool visit(AST::Finally *ast); + virtual bool visit(AST::FormalParameterList *ast); + virtual bool visit(AST::FunctionBody *ast); + virtual bool visit(AST::Program *ast); + virtual bool visit(AST::PropertyNameAndValueList *ast); + virtual bool visit(AST::SourceElements *ast); + virtual bool visit(AST::StatementList *ast); + virtual bool visit(AST::UiArrayMemberList *ast); + virtual bool visit(AST::UiImport *ast); + virtual bool visit(AST::UiImportList *ast); + virtual bool visit(AST::UiObjectInitializer *ast); + virtual bool visit(AST::UiObjectMemberList *ast); + virtual bool visit(AST::UiParameterList *ast); + virtual bool visit(AST::UiProgram *ast); + virtual bool visit(AST::UiQualifiedId *ast); + virtual bool visit(AST::VariableDeclaration *ast); + virtual bool visit(AST::VariableDeclarationList *ast); + + // expressions + virtual bool visit(AST::Expression *ast); + virtual bool visit(AST::ArrayLiteral *ast); + virtual bool visit(AST::ArrayMemberExpression *ast); + virtual bool visit(AST::BinaryExpression *ast); + virtual bool visit(AST::CallExpression *ast); + virtual bool visit(AST::ConditionalExpression *ast); + virtual bool visit(AST::DeleteExpression *ast); + virtual bool visit(AST::FalseLiteral *ast); + virtual bool visit(AST::FieldMemberExpression *ast); + virtual bool visit(AST::FunctionExpression *ast); + virtual bool visit(AST::IdentifierExpression *ast); + virtual bool visit(AST::NestedExpression *ast); + virtual bool visit(AST::NewExpression *ast); + virtual bool visit(AST::NewMemberExpression *ast); + virtual bool visit(AST::NotExpression *ast); + virtual bool visit(AST::NullExpression *ast); + virtual bool visit(AST::NumericLiteral *ast); + virtual bool visit(AST::ObjectLiteral *ast); + virtual bool visit(AST::PostDecrementExpression *ast); + virtual bool visit(AST::PostIncrementExpression *ast); + virtual bool visit(AST::PreDecrementExpression *ast); + virtual bool visit(AST::PreIncrementExpression *ast); + virtual bool visit(AST::RegExpLiteral *ast); + virtual bool visit(AST::StringLiteral *ast); + virtual bool visit(AST::ThisExpression *ast); + virtual bool visit(AST::TildeExpression *ast); + virtual bool visit(AST::TrueLiteral *ast); + virtual bool visit(AST::TypeOfExpression *ast); + virtual bool visit(AST::UnaryMinusExpression *ast); + virtual bool visit(AST::UnaryPlusExpression *ast); + virtual bool visit(AST::VoidExpression *ast); + virtual bool visit(AST::FunctionDeclaration *ast); + + // property names + virtual bool visit(AST::IdentifierPropertyName *ast); + virtual bool visit(AST::NumericLiteralPropertyName *ast); + virtual bool visit(AST::StringLiteralPropertyName *ast); + + // source elements + virtual bool visit(AST::FunctionSourceElement *ast); + virtual bool visit(AST::StatementSourceElement *ast); + + // statements + virtual bool visit(AST::Block *ast); + virtual bool visit(AST::BreakStatement *ast); + virtual bool visit(AST::ContinueStatement *ast); + virtual bool visit(AST::DebuggerStatement *ast); + virtual bool visit(AST::DoWhileStatement *ast); + virtual bool visit(AST::EmptyStatement *ast); + virtual bool visit(AST::ExpressionStatement *ast); + virtual bool visit(AST::ForEachStatement *ast); + virtual bool visit(AST::ForStatement *ast); + virtual bool visit(AST::IfStatement *ast); + virtual bool visit(AST::LabelledStatement *ast); + virtual bool visit(AST::LocalForEachStatement *ast); + virtual bool visit(AST::LocalForStatement *ast); + virtual bool visit(AST::ReturnStatement *ast); + virtual bool visit(AST::SwitchStatement *ast); + virtual bool visit(AST::ThrowStatement *ast); + virtual bool visit(AST::TryStatement *ast); + virtual bool visit(AST::VariableStatement *ast); + virtual bool visit(AST::WhileStatement *ast); + virtual bool visit(AST::WithStatement *ast); + + // ui object members + virtual bool visit(AST::UiArrayBinding *ast); + virtual bool visit(AST::UiObjectBinding *ast); + virtual bool visit(AST::UiObjectDefinition *ast); + virtual bool visit(AST::UiPublicMember *ast); + virtual bool visit(AST::UiScriptBinding *ast); + virtual bool visit(AST::UiSourceElement *ast); + +private: + Result _expr; + QString _property; + UiMember _uiMember; + Loop _loop; + IR::Module *_module; + IR::Function *_function; + IR::BasicBlock *_block; + IR::BasicBlock *_exitBlock; + unsigned _returnAddress; +}; + +} // end of namespace QQmlJS + +#endif // QV4CODEGEN_P_H diff --git a/qv4ir.cpp b/qv4ir.cpp new file mode 100644 index 0000000000..e3a15118cc --- /dev/null +++ b/qv4ir.cpp @@ -0,0 +1,697 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4ir_p.h" +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace IR { + +const char *typeName(Type t) +{ + switch (t) { + case InvalidType: return "invalid"; + case UndefinedType: return "undefined"; + case NullType: return "null"; + case VoidType: return "void"; + case StringType: return "string"; + case UrlType: return "QUrl"; + case ColorType: return "QColor"; + case SGAnchorLineType: return "SGAnchorLine"; + case AttachType: return "AttachType"; + case ObjectType: return "object"; + case VariantType: return "variant"; + case VarType: return "var"; + case BoolType: return "bool"; + case IntType: return "int"; + case FloatType: return "float"; + case NumberType: return "number"; + default: return "invalid"; + } +} + +inline bool isNumberType(IR::Type ty) +{ + return ty >= IR::FirstNumberType; +} + +inline bool isStringType(IR::Type ty) +{ + return ty == IR::StringType || ty == IR::UrlType || ty == IR::ColorType; +} + +IR::Type maxType(IR::Type left, IR::Type right) +{ + if (isStringType(left) && isStringType(right)) { + // String promotions (url to string) are more specific than + // identity conversions (AKA left == right). That's because + // we want to ensure we convert urls to strings in binary + // expressions. + return IR::StringType; + } else if (left == right) + return left; + else if (isNumberType(left) && isNumberType(right)) { + IR::Type ty = qMax(left, right); + return ty == FloatType ? NumberType : ty; // promote floats + } else if ((isNumberType(left) && isStringType(right)) || + (isNumberType(right) && isStringType(left))) + return IR::StringType; + else + return IR::InvalidType; +} + +bool isRealType(IR::Type type) +{ + return type == IR::NumberType || type == IR::FloatType; +} + +const char *opname(AluOp op) +{ + switch (op) { + case OpInvalid: return "?"; + + case OpIfTrue: return "(bool)"; + case OpNot: return "!"; + case OpUMinus: return "-"; + case OpUPlus: return "+"; + case OpCompl: return "~"; + + case OpBitAnd: return "&"; + case OpBitOr: return "|"; + case OpBitXor: return "^"; + + case OpAdd: return "+"; + case OpSub: return "-"; + case OpMul: return "*"; + case OpDiv: return "/"; + case OpMod: return "%"; + + case OpLShift: return "<<"; + case OpRShift: return ">>"; + case OpURShift: return ">>>"; + + case OpGt: return ">"; + case OpLt: return "<"; + case OpGe: return ">="; + case OpLe: return "<="; + case OpEqual: return "=="; + case OpNotEqual: return "!="; + case OpStrictEqual: return "==="; + case OpStrictNotEqual: return "!=="; + + case OpAnd: return "&&"; + case OpOr: return "||"; + + default: return "?"; + + } // switch +} + +AluOp binaryOperator(int op) +{ + switch (static_cast(op)) { + case QSOperator::Add: return OpAdd; + case QSOperator::And: return OpAnd; + case QSOperator::BitAnd: return OpBitAnd; + case QSOperator::BitOr: return OpBitOr; + case QSOperator::BitXor: return OpBitXor; + case QSOperator::Div: return OpDiv; + case QSOperator::Equal: return OpEqual; + case QSOperator::Ge: return OpGe; + case QSOperator::Gt: return OpGt; + case QSOperator::Le: return OpLe; + case QSOperator::LShift: return OpLShift; + case QSOperator::Lt: return OpLt; + case QSOperator::Mod: return OpMod; + case QSOperator::Mul: return OpMul; + case QSOperator::NotEqual: return OpNotEqual; + case QSOperator::Or: return OpOr; + case QSOperator::RShift: return OpRShift; + case QSOperator::StrictEqual: return OpStrictEqual; + case QSOperator::StrictNotEqual: return OpStrictNotEqual; + case QSOperator::Sub: return OpSub; + case QSOperator::URShift: return OpURShift; + default: return OpInvalid; + } +} + +void Const::dump(QTextStream &out) +{ + switch (type) { + case QQmlJS::IR::UndefinedType: + out << "undefined"; + break; + case QQmlJS::IR::NullType: + out << "null"; + break; + case QQmlJS::IR::VoidType: + out << "void"; + break; + case QQmlJS::IR::BoolType: + out << (value ? "true" : "false"); + break; + default: + out << QString::number(value, 'g', 16); + break; + } +} + +void String::dump(QTextStream &out) +{ + out << '"' << escape(*value) << '"'; +} + +QString String::escape(const QString &s) +{ + QString r; + for (int i = 0; i < s.length(); ++i) { + const QChar ch = s.at(i); + if (ch == QLatin1Char('\n')) + r += QLatin1String("\\n"); + else if (ch == QLatin1Char('\r')) + r += QLatin1String("\\r"); + else if (ch == QLatin1Char('\\')) + r += QLatin1String("\\\\"); + else if (ch == QLatin1Char('"')) + r += QLatin1String("\\\""); + else if (ch == QLatin1Char('\'')) + r += QLatin1String("\\'"); + else + r += ch; + } + return r; +} + +void Name::init(Type type, const QString *id, quint32 line, quint32 column) +{ + this->type = type; + this->id = id; + this->line = line; + this->column = column; +} + +void Name::dump(QTextStream &out) +{ + out << *id; +} + +void Temp::dump(QTextStream &out) +{ + out << '%' << index; +} + +void Closure::dump(QTextStream &out) +{ + QString name = value->name ? *value->name : QString(); + if (name.isEmpty()) + name.sprintf("%p", value); + out << "closure(" << name << ')'; +} + +void Unop::dump(QTextStream &out) +{ + out << opname(op); + expr->dump(out); +} + +Type Unop::typeForOp(AluOp op, Expr *expr) +{ + switch (op) { + case OpIfTrue: return BoolType; + case OpNot: return BoolType; + + case OpUMinus: + case OpUPlus: + case OpCompl: + return maxType(expr->type, NumberType); + + default: + break; + } + + return InvalidType; +} + +void Binop::dump(QTextStream &out) +{ + left->dump(out); + out << ' ' << opname(op) << ' '; + right->dump(out); +} + +Type Binop::typeForOp(AluOp op, Expr *left, Expr *right) +{ + if (! (left && right)) + return InvalidType; + + switch (op) { + case OpInvalid: + return InvalidType; + + // unary operators + case OpIfTrue: + case OpNot: + case OpUMinus: + case OpUPlus: + case OpCompl: + return InvalidType; + + // bit fields + case OpBitAnd: + case OpBitOr: + case OpBitXor: + return IntType; + + case OpAdd: + if (left->type == StringType) + return StringType; + return NumberType; + + case OpSub: + case OpMul: + case OpDiv: + case OpMod: + return NumberType; + + case OpLShift: + case OpRShift: + case OpURShift: + return IntType; + + case OpAnd: + case OpOr: + return BoolType; + + case OpGt: + case OpLt: + case OpGe: + case OpLe: + case OpEqual: + case OpNotEqual: + case OpStrictEqual: + case OpStrictNotEqual: + return BoolType; + } // switch + + return InvalidType; +} + +void Call::dump(QTextStream &out) +{ + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +Type Call::typeForFunction(Expr *) +{ + return InvalidType; +} + +void New::dump(QTextStream &out) +{ + out << "new "; + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +Type New::typeForFunction(Expr *) +{ + return InvalidType; +} + +void Subscript::dump(QTextStream &out) +{ + base->dump(out); + out << '['; + index->dump(out); + out << ']'; +} + +void Member::dump(QTextStream &out) +{ + base->dump(out); + out << '.' << *name; +} + +void Exp::dump(QTextStream &out, Mode) +{ + out << "(void) "; + expr->dump(out); + out << ';'; +} + +void Enter::dump(QTextStream &out, Mode) +{ + out << "%enter("; + expr->dump(out); + out << ");"; +} + +void Leave::dump(QTextStream &out, Mode) +{ + out << "%leave"; + out << ';'; +} + +void Move::dump(QTextStream &out, Mode) +{ + target->dump(out); + out << ' '; + if (op != OpInvalid) + out << opname(op); + out << "= "; +// if (source->type != target->type) +// out << typeName(source->type) << "_to_" << typeName(target->type) << '('; + source->dump(out); +// if (source->type != target->type) +// out << ')'; + out << ';'; +} + +void Jump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "goto " << 'L' << target << ';'; +} + +void CJump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "if ("; + cond->dump(out); + if (mode == HIR) + out << ") goto " << 'L' << iftrue << "; else goto " << 'L' << iffalse << ';'; + else + out << ") goto " << 'L' << iftrue << ";"; +} + +void Ret::dump(QTextStream &out, Mode) +{ + out << "return"; + if (expr) { + out << ' '; + expr->dump(out); + } + out << ';'; +} + +Function *Module::newFunction(const QString &name) +{ + Function *f = new Function(this, name); + functions.append(f); + return f; +} + +Function::~Function() +{ + qDeleteAll(basicBlocks); +} + +const QString *Function::newString(const QString &text) +{ + return &*strings.insert(text); +} + +BasicBlock *Function::newBasicBlock() +{ + return i(new BasicBlock(this)); +} + +void Function::dump(QTextStream &out, Stmt::Mode mode) +{ + QString n = name ? *name : QString(); + if (n.isEmpty()) + n.sprintf("%p", this); + out << "function " << n << "() {" << endl; + foreach (const QString *formal, formals) + out << "\treceive " << *formal << ';' << endl; + foreach (const QString *local, locals) + out << "\tlocal " << *local << ';' << endl; + foreach (BasicBlock *bb, basicBlocks) + bb->dump(out, mode); + out << '}' << endl; +} + +unsigned BasicBlock::newTemp() +{ + return function->tempCount++; +} + +Temp *BasicBlock::TEMP(unsigned index) +{ + Temp *e = function->New(); + e->init(IR::InvalidType, index); + return e; +} + +Expr *BasicBlock::CONST(Type type, double value) +{ + Const *e = function->New(); + e->init(type, value); + return e; +} + +Expr *BasicBlock::STRING(const QString *value) +{ + String *e = function->New(); + e->init(value); + return e; +} + +Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) +{ + Name *e = function->New(); + e->init(InvalidType, function->newString(id), line, column); + return e; +} + +Closure *BasicBlock::CLOSURE(Function *function) +{ + Closure *clos = function->New(); + clos->init(IR::InvalidType, function); + return clos; +} + +Expr *BasicBlock::UNOP(AluOp op, Expr *expr) +{ + Unop *e = function->New(); + e->init(op, expr); + return e; +} + +Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) +{ + if (left && right) { + if (Const *c1 = left->asConst()) { + if (Const *c2 = right->asConst()) { + const IR::Type ty = Binop::typeForOp(op, left, right); + + switch (op) { + case OpAdd: return CONST(ty, c1->value + c2->value); + case OpAnd: return CONST(ty, c1->value ? c2->value : 0); + case OpBitAnd: return CONST(ty, int(c1->value) & int(c2->value)); + case OpBitOr: return CONST(ty, int(c1->value) | int(c2->value)); + case OpBitXor: return CONST(ty, int(c1->value) ^ int(c2->value)); + case OpDiv: return CONST(ty, c1->value / c2->value); + case OpEqual: return CONST(ty, c1->value == c2->value); + case OpGe: return CONST(ty, c1->value >= c2->value); + case OpGt: return CONST(ty, c1->value > c2->value); + case OpLe: return CONST(ty, c1->value <= c2->value); + case OpLShift: return CONST(ty, int(c1->value) << int(c2->value)); + case OpLt: return CONST(ty, c1->value < c2->value); + case OpMod: return CONST(ty, ::fmod(c1->value, c2->value)); + case OpMul: return CONST(ty, c1->value * c2->value); + case OpNotEqual: return CONST(ty, c1->value != c2->value); + case OpOr: return CONST(ty, c1->value ? c1->value : c2->value); + case OpRShift: return CONST(ty, int(c1->value) >> int(c2->value)); + case OpStrictEqual: return CONST(ty, c1->value == c2->value); + case OpStrictNotEqual: return CONST(ty, c1->value != c2->value); + case OpSub: return CONST(ty, c1->value - c2->value); + case OpURShift: return CONST(ty, unsigned(c1->value) >> int(c2->value)); + + case OpIfTrue: // unary ops + case OpNot: + case OpUMinus: + case OpUPlus: + case OpCompl: + case OpInvalid: + break; + } + } + } else if (op == OpAdd) { + if (String *s1 = left->asString()) { + if (String *s2 = right->asString()) { + return STRING(function->newString(*s1->value + *s2->value)); + } + } + } + } + + Binop *e = function->New(); + e->init(op, left, right); + return e; +} + +Expr *BasicBlock::CALL(Expr *base, ExprList *args) +{ + Call *e = function->New(); + e->init(base, args); + return e; +} + +Expr *BasicBlock::NEW(Expr *base, ExprList *args) +{ + New *e = function->New(); + e->init(base, args); + return e; +} + +Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index) +{ + Subscript *e = function->New(); + e->init(base, index); + return e; +} + +Expr *BasicBlock::MEMBER(Expr *base, const QString *name) +{ + Member*e = function->New(); + e->init(base, name); + return e; +} + +Stmt *BasicBlock::EXP(Expr *expr) +{ + Exp *s = function->New(); + s->init(expr); + statements.append(s); + return s; +} + +Stmt *BasicBlock::ENTER(Expr *expr) +{ + Enter *s = function->New(); + s->init(expr); + statements.append(s); + return s; +} + +Stmt *BasicBlock::LEAVE() +{ + Leave *s = function->New(); + s->init(); + statements.append(s); + return s; +} + +Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op) +{ + Move *s = function->New(); + s->init(target, source, op); + statements.append(s); + return s; +} + +Stmt *BasicBlock::JUMP(BasicBlock *target) +{ + if (isTerminated()) + return 0; + + Jump *s = function->New(); + s->init(target); + statements.append(s); + return s; +} + +Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) +{ + if (isTerminated()) + return 0; + + CJump *s = function->New(); + s->init(cond, iftrue, iffalse); + statements.append(s); + return s; +} + +Stmt *BasicBlock::RET(Expr *expr, Type type) +{ + if (isTerminated()) + return 0; + + Ret *s = function->New(); + s->init(expr, type); + statements.append(s); + return s; +} + +void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) +{ + out << 'L' << this << ':' << endl; + foreach (Stmt *s, statements) { + out << '\t'; + s->dump(out, mode); + out << endl; + } +} + +} // end of namespace IR +} // end of namespace QQmlJS + +QT_END_NAMESPACE diff --git a/qv4ir_p.h b/qv4ir_p.h new file mode 100644 index 0000000000..dbb327692c --- /dev/null +++ b/qv4ir_p.h @@ -0,0 +1,666 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4IR_P_H +#define QV4IR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QTextStream; +class QQmlType; + +namespace QQmlJS { + +namespace IR { + +struct BasicBlock; +struct Function; +struct Module; + +struct Stmt; +struct Expr; + +// expressions +struct Const; +struct String; +struct Name; +struct Temp; +struct Closure; +struct Unop; +struct Binop; +struct Call; +struct New; +struct Subscript; +struct Member; + +// statements +struct Exp; +struct Enter; +struct Leave; +struct Move; +struct Jump; +struct CJump; +struct Ret; + +enum AluOp { + OpInvalid = 0, + + OpIfTrue, + OpNot, + OpUMinus, + OpUPlus, + OpCompl, + + OpBitAnd, + OpBitOr, + OpBitXor, + + OpAdd, + OpSub, + OpMul, + OpDiv, + OpMod, + + OpLShift, + OpRShift, + OpURShift, + + OpGt, + OpLt, + OpGe, + OpLe, + OpEqual, + OpNotEqual, + OpStrictEqual, + OpStrictNotEqual, + + OpAnd, + OpOr +}; +AluOp binaryOperator(int op); +const char *opname(IR::AluOp op); + +enum Type { + InvalidType, + UndefinedType, + NullType, + VoidType, + StringType, + UrlType, + ColorType, + SGAnchorLineType, + AttachType, + ObjectType, + VariantType, + VarType, + + FirstNumberType, + BoolType = FirstNumberType, + IntType, + FloatType, + NumberType +}; +Type maxType(IR::Type left, IR::Type right); +bool isRealType(IR::Type type); +const char *typeName(IR::Type t); + +struct ExprVisitor { + virtual ~ExprVisitor() {} + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *) {} + virtual void visitClosure(Closure *) {} + virtual void visitUnop(Unop *) {} + virtual void visitBinop(Binop *) {} + virtual void visitCall(Call *) {} + virtual void visitNew(New *) {} + virtual void visitSubscript(Subscript *) {} + virtual void visitMember(Member *) {} +}; + +struct StmtVisitor { + virtual ~StmtVisitor() {} + virtual void visitExp(Exp *) {} + virtual void visitEnter(Enter *) {} + virtual void visitLeave(Leave *) {} + virtual void visitMove(Move *) {} + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *) {} + virtual void visitRet(Ret *) {} +}; + +struct Expr { + Type type; + + Expr(): type(InvalidType) {} + virtual ~Expr() {} + virtual void accept(ExprVisitor *) = 0; + virtual Const *asConst() { return 0; } + virtual String *asString() { return 0; } + virtual Name *asName() { return 0; } + virtual Temp *asTemp() { return 0; } + virtual Closure *asClosure() { return 0; } + virtual Unop *asUnop() { return 0; } + virtual Binop *asBinop() { return 0; } + virtual Call *asCall() { return 0; } + virtual New *asNew() { return 0; } + virtual Subscript *asSubscript() { return 0; } + virtual Member *asMember() { return 0; } + virtual void dump(QTextStream &out) = 0; +}; + +struct ExprList { + Expr *expr; + ExprList *next; + + void init(Expr *expr, ExprList *next = 0) + { + this->expr = expr; + this->next = next; + } +}; + +struct Const: Expr { + double value; + + void init(Type type, double value) + { + this->type = type; + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitConst(this); } + virtual Const *asConst() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct String: Expr { + const QString *value; + + void init(const QString *value) + { + this->type = StringType; + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitString(this); } + virtual String *asString() { return this; } + + virtual void dump(QTextStream &out); + static QString escape(const QString &s); +}; + +struct Name: Expr { + const QString *id; + quint32 line; + quint32 column; + + void init(Type type, const QString *id, quint32 line, quint32 column); + + virtual void accept(ExprVisitor *v) { v->visitName(this); } + virtual Name *asName() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Temp: Expr { + unsigned index; + + void init(Type type, unsigned index) + { + this->type = type; + this->index = index; + } + + virtual void accept(ExprVisitor *v) { v->visitTemp(this); } + virtual Temp *asTemp() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Closure: Expr { + Function *value; + + void init(Type type, Function *value) + { + this->type = type; + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitClosure(this); } + virtual Closure *asClosure() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Unop: Expr { + AluOp op; + Expr *expr; + + void init(AluOp op, Expr *expr) + { + this->type = this->typeForOp(op, expr); + this->op = op; + this->expr = expr; + } + + virtual void accept(ExprVisitor *v) { v->visitUnop(this); } + virtual Unop *asUnop() { return this; } + + virtual void dump(QTextStream &out); + +private: + static Type typeForOp(AluOp op, Expr *expr); +}; + +struct Binop: Expr { + AluOp op; + Expr *left; + Expr *right; + + void init(AluOp op, Expr *left, Expr *right) + { + this->type = typeForOp(op, left, right); + this->op = op; + this->left = left; + this->right = right; + } + + virtual void accept(ExprVisitor *v) { v->visitBinop(this); } + virtual Binop *asBinop() { return this; } + + virtual void dump(QTextStream &out); + + static Type typeForOp(AluOp op, Expr *left, Expr *right); +}; + +struct Call: Expr { + Expr *base; + ExprList *args; + + void init(Expr *base, ExprList *args) + { + this->type = typeForFunction(base); + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitCall(this); } + virtual Call *asCall() { return this; } + + virtual void dump(QTextStream &out); + +private: + static Type typeForFunction(Expr *base); +}; + +struct New: Expr { + Expr *base; + ExprList *args; + + void init(Expr *base, ExprList *args) + { + this->type = typeForFunction(base); + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitNew(this); } + virtual New *asNew() { return this; } + + virtual void dump(QTextStream &out); + +private: + static Type typeForFunction(Expr *base); +}; + +struct Subscript: Expr { + Expr *base; + Expr *index; + + void init(Expr *base, Expr *index) + { + this->type = typeForFunction(base); + this->base = base; + this->index = index; + } + + virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } + virtual Subscript *asSubscript() { return this; } + + virtual void dump(QTextStream &out); + +private: + static Type typeForFunction(Expr *) { return IR::InvalidType; } +}; + +struct Member: Expr { + Expr *base; + const QString *name; + + void init(Expr *base, const QString *name) + { + this->type = typeForFunction(base); + this->base = base; + this->name = name; + } + + virtual void accept(ExprVisitor *v) { v->visitMember(this); } + virtual Member *asMember() { return this; } + + virtual void dump(QTextStream &out); + +private: + static Type typeForFunction(Expr *) { return IR::InvalidType; } +}; + +struct Stmt { + enum Mode { + HIR, + MIR + }; + + QVector uses; + QVector defs; + QVector liveIn; + QVector liveOut; + + virtual ~Stmt() {} + virtual Stmt *asTerminator() { return 0; } + + virtual void accept(StmtVisitor *) = 0; + virtual Exp *asExp() { return 0; } + virtual Move *asMove() { return 0; } + virtual Enter *asEnter() { return 0; } + virtual Leave *asLeave() { return 0; } + virtual Jump *asJump() { return 0; } + virtual CJump *asCJump() { return 0; } + virtual Ret *asRet() { return 0; } + virtual void dump(QTextStream &out, Mode mode = HIR) = 0; +}; + +struct Exp: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual void accept(StmtVisitor *v) { v->visitExp(this); } + virtual Exp *asExp() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Move: Stmt { + Expr *target; + Expr *source; + AluOp op; + + void init(Expr *target, Expr *source, AluOp op) + { + this->target = target; + this->source = source; + this->op = op; + } + + virtual void accept(StmtVisitor *v) { v->visitMove(this); } + virtual Move *asMove() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Enter: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual void accept(StmtVisitor *v) { v->visitEnter(this); } + virtual Enter *asEnter() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Leave: Stmt { + void init() {} + + virtual void accept(StmtVisitor *v) { v->visitLeave(this); } + virtual Leave *asLeave() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Jump: Stmt { + BasicBlock *target; + + void init(BasicBlock *target) + { + this->target = target; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitJump(this); } + virtual Jump *asJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct CJump: Stmt { + Expr *cond; + BasicBlock *iftrue; + BasicBlock *iffalse; + + void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) + { + this->cond = cond; + this->iftrue = iftrue; + this->iffalse = iffalse; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitCJump(this); } + virtual CJump *asCJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Ret: Stmt { + Expr *expr; + Type type; + + void init(Expr *expr, Type type) + { + this->expr = expr; + this->type = type; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitRet(this); } + virtual Ret *asRet() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Module { + MemoryPool pool; + QVector functions; + + Function *newFunction(const QString &name); +}; + +struct Function { + Module *module; + MemoryPool *pool; + const QString *name; + QVector basicBlocks; + int tempCount; + QSet strings; + QList formals; + QList locals; + + template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } + + Function(Module *module, const QString &name) + : module(module), pool(&module->pool), tempCount(0) { this->name = newString(name); } + + ~Function(); + + BasicBlock *newBasicBlock(); + const QString *newString(const QString &text); + + void RECEIVE(const QString &name) { formals.append(newString(name)); } + void LOCAL(const QString &name) { locals.append(newString(name)); } + + inline BasicBlock *i(BasicBlock *block) { basicBlocks.append(block); return block; } + + void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); +}; + +struct BasicBlock { + Function *function; + QVector statements; + QVector in; + QVector out; + QVector liveIn; + QVector liveOut; + int index; + int offset; + + BasicBlock(Function *function): function(function), index(-1), offset(-1) {} + ~BasicBlock() {} + + template inline Instr i(Instr i) { statements.append(i); return i; } + + inline bool isEmpty() const { + return statements.isEmpty(); + } + + inline Stmt *terminator() const { + if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0) + return statements.at(statements.size() - 1); + return 0; + } + + inline bool isTerminated() const { + if (terminator() != 0) + return true; + return false; + } + + unsigned newTemp(); + + Temp *TEMP(unsigned index); + + Expr *CONST(Type type, double value); + Expr *STRING(const QString *value); + + Name *NAME(const QString &id, quint32 line, quint32 column); + + Closure *CLOSURE(Function *function); + + Expr *UNOP(AluOp op, Expr *expr); + Expr *BINOP(AluOp op, Expr *left, Expr *right); + Expr *CALL(Expr *base, ExprList *args = 0); + Expr *NEW(Expr *base, ExprList *args = 0); + Expr *SUBSCRIPT(Expr *base, Expr *index); + Expr *MEMBER(Expr *base, const QString *name); + + Stmt *EXP(Expr *expr); + Stmt *ENTER(Expr *expr); + Stmt *LEAVE(); + + Stmt *MOVE(Expr *target, Expr *source, AluOp op = IR::OpInvalid); + + Stmt *JUMP(BasicBlock *target); + Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); + Stmt *RET(Expr *expr, Type type); + + void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); +}; + +} // end of namespace IR + +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV4IR_P_H diff --git a/qv4isel.cpp b/qv4isel.cpp new file mode 100644 index 0000000000..7308157e5c --- /dev/null +++ b/qv4isel.cpp @@ -0,0 +1,319 @@ + +#include "qv4isel_p.h" +#include "qmljs_runtime.h" +#include "qmljs_objects.h" + +#define TARGET_AMD64 +typedef quint64 guint64; +typedef qint64 gint64; +typedef uchar guint8; +typedef uint guint32; +typedef void *gpointer; +#include "amd64-codegen.h" + +#include + +#ifndef NO_UDIS86 +# include +#endif + +using namespace QQmlJS; + +static inline bool protect(const void *addr, size_t size) +{ + size_t pageSize = sysconf(_SC_PAGESIZE); + size_t iaddr = reinterpret_cast(addr); + size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); + int mode = PROT_READ | PROT_WRITE | PROT_EXEC; + return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; +} + +static inline void +amd64_patch (unsigned char* code, gpointer target) +{ + guint8 rex = 0; + +#ifdef __native_client_codegen__ + code = amd64_skip_nops (code); +#endif +#if defined(__native_client_codegen__) && defined(__native_client__) + if (nacl_is_code_address (code)) { + /* For tail calls, code is patched after being installed */ + /* but not through the normal "patch callsite" method. */ + unsigned char buf[kNaClAlignment]; + unsigned char *aligned_code = (uintptr_t)code & ~kNaClAlignmentMask; + int ret; + memcpy (buf, aligned_code, kNaClAlignment); + /* Patch a temp buffer of bundle size, */ + /* then install to actual location. */ + amd64_patch (buf + ((uintptr_t)code - (uintptr_t)aligned_code), target); + ret = nacl_dyncode_modify (aligned_code, buf, kNaClAlignment); + g_assert (ret == 0); + return; + } + target = nacl_modify_patch_target (target); +#endif + + /* Skip REX */ + if ((code [0] >= 0x40) && (code [0] <= 0x4f)) { + rex = code [0]; + code += 1; + } + + if ((code [0] & 0xf8) == 0xb8) { + /* amd64_set_reg_template */ + *(guint64*)(code + 1) = (guint64)target; + } + else if ((code [0] == 0x8b) && rex && x86_modrm_mod (code [1]) == 0 && x86_modrm_rm (code [1]) == 5) { + /* mov 0(%rip), %dreg */ + *(guint32*)(code + 2) = (guint32)(guint64)target - 7; + } + else if ((code [0] == 0xff) && (code [1] == 0x15)) { + /* call *(%rip) */ + *(guint32*)(code + 2) = ((guint32)(guint64)target) - 7; + } + else if (code [0] == 0xe8) { + /* call */ + gint64 disp = (guint8*)target - (guint8*)code; + assert (amd64_is_imm32 (disp)); + x86_patch (code, (unsigned char*)target); + } + else + x86_patch (code, (unsigned char*)target); +} +InstructionSelection::InstructionSelection(IR::Module *module) + : _module(module) + , _function(0) + , _block(0) + , _code(0) + , _codePtr(0) +{ +} + +InstructionSelection::~InstructionSelection() +{ +} + +void InstructionSelection::visitFunction(IR::Function *function) +{ + uchar *code = (uchar *) malloc(getpagesize()); + Q_ASSERT(! (size_t(code) & 15)); + + protect(code, getpagesize()); + + uchar *codePtr = code; + + qSwap(_code, code); + qSwap(_codePtr, codePtr); + + int locals = function->tempCount * sizeof(Value); + locals = (locals + 15) & ~15; + + amd64_push_reg(_codePtr, AMD64_R14); + amd64_mov_reg_reg(_codePtr, AMD64_R14, AMD64_RDI, 8); + amd64_alu_reg_imm(_codePtr, X86_SUB, AMD64_RSP, locals); + + foreach (IR::BasicBlock *block, function->basicBlocks) { + _block = block; + _addrs[block] = _codePtr; + foreach (IR::Stmt *s, block->statements) { + s->accept(this); + } + } + + QHashIterator > it(_patches); + while (it.hasNext()) { + it.next(); + uchar *target = _addrs[it.key()]; + foreach (uchar *instr, it.value()) { + amd64_patch(instr, target); + } + } + + amd64_alu_reg_imm(_codePtr, X86_ADD, AMD64_RSP, locals); + amd64_pop_reg(_codePtr, AMD64_R14); + amd64_ret(_codePtr); + + qSwap(_codePtr, codePtr); + qSwap(_code, code); + + static bool showCode = !qgetenv("SHOW_CODE").isNull(); + if (showCode) { +#ifndef NO_UDIS86 + ud_t ud_obj; + + ud_init(&ud_obj); + ud_set_input_buffer(&ud_obj, code, codePtr - code); + ud_set_mode(&ud_obj, 64); + ud_set_syntax(&ud_obj, UD_SYN_INTEL); + + while (ud_disassemble(&ud_obj)) { + printf("\t%s\n", ud_insn_asm(&ud_obj)); + } +#endif + } + + void (*f)(Context *) = (void (*)(Context *)) code; + + Context *ctx = new (GC) Context; + ctx->activation = Value::object(ctx, new (GC) ArgumentsObject); + f(ctx); + Value d; + ctx->activation.objectValue->get(String::get(ctx, QLatin1String("d")), &d); + __qmljs_to_string(ctx, &d, &d); + qDebug() << qPrintable(d.stringValue->text()); +} + +String *InstructionSelection::identifier(const QString &s) +{ + String *&id = _identifiers[s]; + if (! id) + id = new (GC) String(s); + return id; +} + +void InstructionSelection::visitExp(IR::Exp *s) +{ + // if (IR::Call *c = s->expr->asCall()) { + // return; + // } + Q_ASSERT(!"TODO"); +} + +void InstructionSelection::visitEnter(IR::Enter *) +{ + Q_ASSERT(!"TODO"); +} + +void InstructionSelection::visitLeave(IR::Leave *) +{ + Q_ASSERT(!"TODO"); +} + +void InstructionSelection::visitMove(IR::Move *s) +{ + // %rdi, %rsi, %rdx, %rcx, %r8 and %r9 + if (s->op == IR::OpInvalid) { + if (IR::Name *n = s->target->asName()) { + String *propertyName = identifier(*n->id); + + if (IR::Const *c = s->source->asConst()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); + amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); + amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); + amd64_call_code(_codePtr, __qmljs_set_activation_property_number); + return; + } else if (IR::Temp *t = s->source->asTemp()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); + amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 8 + sizeof(Value) * (t->index - 1)); + amd64_call_code(_codePtr, __qmljs_set_activation_property); + return; + } + } else if (IR::Temp *t = s->target->asTemp()) { + const int offset = 8 + sizeof(Value) * (t->index - 1); + if (IR::Name *n = s->source->asName()) { + String *propertyName = identifier(*n->id); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, offset); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, propertyName); + amd64_call_code(_codePtr, __qmljs_get_activation_property); + return; + } else if (IR::Const *c = s->source->asConst()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, offset); + amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); + amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); + amd64_call_code(_codePtr, __qmljs_init_number); + return; + } else if (IR::Binop *b = s->source->asBinop()) { + IR::Temp *l = b->left->asTemp(); + IR::Temp *r = b->right->asTemp(); + if (l && r) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, offset); + amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 8 + sizeof(Value) * (l->index - 1)); + amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 8 + sizeof(Value) * (r->index - 1)); + + void (*op)(Context *, Value *, const Value *, const Value *) = 0; + + switch (b->op) { + case QQmlJS::IR::OpInvalid: + case QQmlJS::IR::OpIfTrue: + case QQmlJS::IR::OpNot: + case QQmlJS::IR::OpUMinus: + case QQmlJS::IR::OpUPlus: + case QQmlJS::IR::OpCompl: + Q_ASSERT(!"unreachable"); + break; + + case QQmlJS::IR::OpBitAnd: op = __qmljs_bit_and; break; + case QQmlJS::IR::OpBitOr: op = __qmljs_bit_or; break; + case QQmlJS::IR::OpBitXor: op = __qmljs_bit_xor; break; + case QQmlJS::IR::OpAdd: op = __qmljs_add; break; + case QQmlJS::IR::OpSub: op = __qmljs_sub; break; + case QQmlJS::IR::OpMul: op = __qmljs_mul; break; + case QQmlJS::IR::OpDiv: op = __qmljs_div; break; + case QQmlJS::IR::OpMod: op = __qmljs_mod; break; + case QQmlJS::IR::OpLShift: op = __qmljs_shl; break; + case QQmlJS::IR::OpRShift: op = __qmljs_shr; break; + case QQmlJS::IR::OpURShift: op = __qmljs_ushr; break; + case QQmlJS::IR::OpGt: op = __qmljs_gt; break; + case QQmlJS::IR::OpLt: op = __qmljs_lt; break; + case QQmlJS::IR::OpGe: op = __qmljs_ge; break; + case QQmlJS::IR::OpLe: op = __qmljs_le; break; + case QQmlJS::IR::OpEqual: op = __qmljs_eq; break; + case QQmlJS::IR::OpNotEqual: op = __qmljs_ne; break; + case QQmlJS::IR::OpStrictEqual: op = __qmljs_se; break; + case QQmlJS::IR::OpStrictNotEqual: op = __qmljs_sne; break; + + case QQmlJS::IR::OpAnd: + case QQmlJS::IR::OpOr: + Q_ASSERT(!"unreachable"); + break; + } + amd64_call_code(_codePtr, op); + return; + } + } + } + } else { + // inplace assignment, e.g. x += 1, ++x, ... + } + Q_ASSERT(!"TODO"); +} + +void InstructionSelection::visitJump(IR::Jump *s) +{ + if (_block->index + 1 != s->target->index) { + _patches[s->target].append(_codePtr); + amd64_jump32(_codePtr, 0); + } +} + +void InstructionSelection::visitCJump(IR::CJump *s) +{ + if (IR::Temp *t = s->cond->asTemp()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, 8 + sizeof(Value) * (t->index - 1)); + amd64_call_code(_codePtr, __qmljs_to_boolean); + amd64_alu_reg_imm_size(_codePtr, X86_CMP, X86_EAX, 0, 4); + _patches[s->iftrue].append(_codePtr); + amd64_branch32(_codePtr, X86_CC_NZ, 0, 1); + + if (_block->index + 1 != s->iffalse->index) { + _patches[s->iffalse].append(_codePtr); + amd64_jump32(_codePtr, 0); + } + return; + } + Q_ASSERT(!"TODO"); +} + +void InstructionSelection::visitRet(IR::Ret *s) +{ + qWarning() << "TODO: RET"; + //Q_ASSERT(!"TODO"); +} + diff --git a/qv4isel_p.h b/qv4isel_p.h new file mode 100644 index 0000000000..6a1531b01a --- /dev/null +++ b/qv4isel_p.h @@ -0,0 +1,43 @@ +#ifndef QV4ISEL_P_H +#define QV4ISEL_P_H + +#include "qv4ir_p.h" +#include "qmljs_objects.h" + +#include + +namespace QQmlJS { + +class InstructionSelection: protected IR::StmtVisitor +{ +public: + InstructionSelection(IR::Module *module); + ~InstructionSelection(); + + void visitFunction(IR::Function *function); + +protected: + String *identifier(const QString &s); + + virtual void visitExp(IR::Exp *); + virtual void visitEnter(IR::Enter *); + virtual void visitLeave(IR::Leave *); + virtual void visitMove(IR::Move *); + virtual void visitJump(IR::Jump *); + virtual void visitCJump(IR::CJump *); + virtual void visitRet(IR::Ret *); + +private: + IR::Module *_module; + IR::Function *_function; + IR::BasicBlock *_block; + uchar *_code; + uchar *_codePtr; + QHash > _patches; + QHash _addrs; + QHash _identifiers; +}; + +} // end of namespace QQmlJS + +#endif // QV4ISEL_P_H diff --git a/tests/simple.js b/tests/simple.js new file mode 100644 index 0000000000..90073a007a --- /dev/null +++ b/tests/simple.js @@ -0,0 +1,12 @@ + +var a = 1 +var b = 2 +var c = 10 +var d = 100 + +for (i = 0; i < 1000000; i = i + 1) { + if (a == 1) + d = a + b * c + else + d = 321 +} diff --git a/v4.pro b/v4.pro new file mode 100644 index 0000000000..58571124c0 --- /dev/null +++ b/v4.pro @@ -0,0 +1,29 @@ +QT = core qmldevtools-private +CONFIG -= app_bundle +CONFIG += console + +DEFINES += __default_codegen__ + +LIBS += -lgc + +udis86:LIBS += -ludis86 +else:DEFINES += NO_UDIS86 + +SOURCES += main.cpp \ + qv4codegen.cpp \ + qv4ir.cpp \ + qmljs_runtime.cpp \ + qmljs_objects.cpp \ + qv4isel.cpp + +HEADERS += \ + qv4codegen_p.h \ + qv4ir_p.h \ + qmljs_runtime.h \ + qmljs_objects.h \ + qv4isel_p.h \ + x86-codegen.h \ + amd64-codegen.h + + + diff --git a/x86-codegen.h b/x86-codegen.h new file mode 100644 index 0000000000..fd2c528c86 --- /dev/null +++ b/x86-codegen.h @@ -0,0 +1,2640 @@ +/* + * x86-codegen.h: Macros for generating x86 code + * + * Authors: + * Paolo Molaro (lupus@ximian.com) + * Intel Corporation (ORP Project) + * Sergey Chaban (serge@wildwestsoftware.com) + * Dietmar Maurer (dietmar@ximian.com) + * Patrik Torstensson + * + * Copyright (C) 2000 Intel Corporation. All rights reserved. + * Copyright (C) 2001, 2002 Ximian, Inc. + */ + +#ifndef X86_H +#define X86_H +#include + +#ifdef __native_client_codegen__ +extern gint8 nacl_align_byte; +#endif /* __native_client_codegen__ */ + + +#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) +#define x86_codegen_pre(inst_ptr_ptr, inst_len) do { mono_nacl_align_inst(inst_ptr_ptr, inst_len); } while (0) +#define x86_call_sequence_pre_val(inst) guint8* _code_start = (inst); +#define x86_call_sequence_post_val(inst) \ + (mono_nacl_align_call(&_code_start, &(inst)), _code_start); +#define x86_call_sequence_pre(inst) x86_call_sequence_pre_val((inst)) +#define x86_call_sequence_post(inst) x86_call_sequence_post_val((inst)) +#else +#define x86_codegen_pre(inst_ptr_ptr, inst_len) do {} while (0) +/* Two variants are needed to avoid warnings */ +#define x86_call_sequence_pre_val(inst) guint8* _code_start = (inst); +#define x86_call_sequence_post_val(inst) _code_start +#define x86_call_sequence_pre(inst) +#define x86_call_sequence_post(inst) +#endif /* __native_client_codegen__ */ + + +/* +// x86 register numbers +*/ +typedef enum { + X86_EAX = 0, + X86_ECX = 1, + X86_EDX = 2, + X86_EBX = 3, + X86_ESP = 4, + X86_EBP = 5, + X86_ESI = 6, + X86_EDI = 7, + X86_NREG +} X86_Reg_No; + +typedef enum { + X86_XMM0, + X86_XMM1, + X86_XMM2, + X86_XMM3, + X86_XMM4, + X86_XMM5, + X86_XMM6, + X86_XMM7, + X86_XMM_NREG +} X86_XMM_Reg_No; + +/* +// opcodes for alu instructions +*/ +typedef enum { + X86_ADD = 0, + X86_OR = 1, + X86_ADC = 2, + X86_SBB = 3, + X86_AND = 4, + X86_SUB = 5, + X86_XOR = 6, + X86_CMP = 7, + X86_NALU +} X86_ALU_Opcode; +/* +// opcodes for shift instructions +*/ +typedef enum { + X86_SHLD, + X86_SHLR, + X86_ROL = 0, + X86_ROR = 1, + X86_RCL = 2, + X86_RCR = 3, + X86_SHL = 4, + X86_SHR = 5, + X86_SAR = 7, + X86_NSHIFT = 8 +} X86_Shift_Opcode; +/* +// opcodes for floating-point instructions +*/ +typedef enum { + X86_FADD = 0, + X86_FMUL = 1, + X86_FCOM = 2, + X86_FCOMP = 3, + X86_FSUB = 4, + X86_FSUBR = 5, + X86_FDIV = 6, + X86_FDIVR = 7, + X86_NFP = 8 +} X86_FP_Opcode; +/* +// integer conditions codes +*/ +typedef enum { + X86_CC_EQ = 0, X86_CC_E = 0, X86_CC_Z = 0, + X86_CC_NE = 1, X86_CC_NZ = 1, + X86_CC_LT = 2, X86_CC_B = 2, X86_CC_C = 2, X86_CC_NAE = 2, + X86_CC_LE = 3, X86_CC_BE = 3, X86_CC_NA = 3, + X86_CC_GT = 4, X86_CC_A = 4, X86_CC_NBE = 4, + X86_CC_GE = 5, X86_CC_AE = 5, X86_CC_NB = 5, X86_CC_NC = 5, + X86_CC_LZ = 6, X86_CC_S = 6, + X86_CC_GEZ = 7, X86_CC_NS = 7, + X86_CC_P = 8, X86_CC_PE = 8, + X86_CC_NP = 9, X86_CC_PO = 9, + X86_CC_O = 10, + X86_CC_NO = 11, + X86_NCC +} X86_CC; + +/* FP status */ +enum { + X86_FP_C0 = 0x100, + X86_FP_C1 = 0x200, + X86_FP_C2 = 0x400, + X86_FP_C3 = 0x4000, + X86_FP_CC_MASK = 0x4500 +}; + +/* FP control word */ +enum { + X86_FPCW_INVOPEX_MASK = 0x1, + X86_FPCW_DENOPEX_MASK = 0x2, + X86_FPCW_ZERODIV_MASK = 0x4, + X86_FPCW_OVFEX_MASK = 0x8, + X86_FPCW_UNDFEX_MASK = 0x10, + X86_FPCW_PRECEX_MASK = 0x20, + X86_FPCW_PRECC_MASK = 0x300, + X86_FPCW_ROUNDC_MASK = 0xc00, + + /* values for precision control */ + X86_FPCW_PREC_SINGLE = 0, + X86_FPCW_PREC_DOUBLE = 0x200, + X86_FPCW_PREC_EXTENDED = 0x300, + + /* values for rounding control */ + X86_FPCW_ROUND_NEAREST = 0, + X86_FPCW_ROUND_DOWN = 0x400, + X86_FPCW_ROUND_UP = 0x800, + X86_FPCW_ROUND_TOZERO = 0xc00 +}; + +/* +// prefix code +*/ +typedef enum { + X86_LOCK_PREFIX = 0xF0, + X86_REPNZ_PREFIX = 0xF2, + X86_REPZ_PREFIX = 0xF3, + X86_REP_PREFIX = 0xF3, + X86_CS_PREFIX = 0x2E, + X86_SS_PREFIX = 0x36, + X86_DS_PREFIX = 0x3E, + X86_ES_PREFIX = 0x26, + X86_FS_PREFIX = 0x64, + X86_GS_PREFIX = 0x65, + X86_UNLIKELY_PREFIX = 0x2E, + X86_LIKELY_PREFIX = 0x3E, + X86_OPERAND_PREFIX = 0x66, + X86_ADDRESS_PREFIX = 0x67 +} X86_Prefix; + +static const unsigned char +x86_cc_unsigned_map [X86_NCC] = { + 0x74, /* eq */ + 0x75, /* ne */ + 0x72, /* lt */ + 0x76, /* le */ + 0x77, /* gt */ + 0x73, /* ge */ + 0x78, /* lz */ + 0x79, /* gez */ + 0x7a, /* p */ + 0x7b, /* np */ + 0x70, /* o */ + 0x71, /* no */ +}; + +static const unsigned char +x86_cc_signed_map [X86_NCC] = { + 0x74, /* eq */ + 0x75, /* ne */ + 0x7c, /* lt */ + 0x7e, /* le */ + 0x7f, /* gt */ + 0x7d, /* ge */ + 0x78, /* lz */ + 0x79, /* gez */ + 0x7a, /* p */ + 0x7b, /* np */ + 0x70, /* o */ + 0x71, /* no */ +}; + +typedef union { + int val; + unsigned char b [4]; +} x86_imm_buf; + +#define X86_NOBASEREG (-1) + +/* +// bitvector mask for callee-saved registers +*/ +#define X86_ESI_MASK (1<> 6) +#define x86_modrm_reg(modrm) (((modrm) >> 3) & 0x7) +#define x86_modrm_rm(modrm) ((modrm) & 0x7) + +#define x86_address_byte(inst,m,o,r) do { *(inst)++ = ((((m)&0x03)<<6)|(((o)&0x07)<<3)|(((r)&0x07))); } while (0) +#define x86_imm_emit32(inst,imm) \ + do { \ + x86_imm_buf imb; imb.val = (int) (imm); \ + *(inst)++ = imb.b [0]; \ + *(inst)++ = imb.b [1]; \ + *(inst)++ = imb.b [2]; \ + *(inst)++ = imb.b [3]; \ + } while (0) +#define x86_imm_emit16(inst,imm) do { *(short*)(inst) = (imm); (inst) += 2; } while (0) +#define x86_imm_emit8(inst,imm) do { *(inst) = (unsigned char)((imm) & 0xff); ++(inst); } while (0) +#define x86_is_imm8(imm) (((int)(imm) >= -128 && (int)(imm) <= 127)) +#define x86_is_imm16(imm) (((int)(imm) >= -(1<<16) && (int)(imm) <= ((1<<16)-1))) + +#define x86_reg_emit(inst,r,regno) do { x86_address_byte ((inst), 3, (r), (regno)); } while (0) +#define x86_reg8_emit(inst,r,regno,is_rh,is_rnoh) do {x86_address_byte ((inst), 3, (is_rh)?((r)|4):(r), (is_rnoh)?((regno)|4):(regno));} while (0) +#define x86_regp_emit(inst,r,regno) do { x86_address_byte ((inst), 0, (r), (regno)); } while (0) +#define x86_mem_emit(inst,r,disp) do { x86_address_byte ((inst), 0, (r), 5); x86_imm_emit32((inst), (disp)); } while (0) + +#define kMaxMembaseEmitPadding 6 + +#define x86_membase_emit_body(inst,r,basereg,disp) do {\ + if ((basereg) == X86_ESP) { \ + if ((disp) == 0) { \ + x86_address_byte ((inst), 0, (r), X86_ESP); \ + x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ + } else if (x86_is_imm8((disp))) { \ + x86_address_byte ((inst), 1, (r), X86_ESP); \ + x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ + x86_imm_emit8 ((inst), (disp)); \ + } else { \ + x86_address_byte ((inst), 2, (r), X86_ESP); \ + x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + break; \ + } \ + if ((disp) == 0 && (basereg) != X86_EBP) { \ + x86_address_byte ((inst), 0, (r), (basereg)); \ + break; \ + } \ + if (x86_is_imm8((disp))) { \ + x86_address_byte ((inst), 1, (r), (basereg)); \ + x86_imm_emit8 ((inst), (disp)); \ + } else { \ + x86_address_byte ((inst), 2, (r), (basereg)); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + } while (0) + +#if defined(__native_client_codegen__) && defined(TARGET_AMD64) +#define x86_membase_emit(inst,r,basereg,disp) \ + do { \ + amd64_nacl_membase_handler(&(inst), (basereg), (disp), (r)) ; \ + } while (0) +#else /* __default_codegen__ || 32-bit NaCl codegen */ +#define x86_membase_emit(inst,r,basereg,disp) \ + do { \ + x86_membase_emit_body((inst),(r),(basereg),(disp)); \ + } while (0) +#endif + +#define kMaxMemindexEmitPadding 6 + +#define x86_memindex_emit(inst,r,basereg,disp,indexreg,shift) \ + do { \ + if ((basereg) == X86_NOBASEREG) { \ + x86_address_byte ((inst), 0, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), 5); \ + x86_imm_emit32 ((inst), (disp)); \ + } else if ((disp) == 0 && (basereg) != X86_EBP) { \ + x86_address_byte ((inst), 0, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ + } else if (x86_is_imm8((disp))) { \ + x86_address_byte ((inst), 1, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ + x86_imm_emit8 ((inst), (disp)); \ + } else { \ + x86_address_byte ((inst), 2, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + } while (0) + +/* + * target is the position in the code where to jump to: + * target = code; + * .. output loop code... + * x86_mov_reg_imm (code, X86_EAX, 0); + * loop = code; + * x86_loop (code, -1); + * ... finish method + * + * patch displacement + * x86_patch (loop, target); + * + * ins should point at the start of the instruction that encodes a target. + * the instruction is inspected for validity and the correct displacement + * is inserted. + */ +#define x86_do_patch(ins,target) \ + do { \ + unsigned char* pos = (ins) + 1; \ + int disp, size = 0; \ + switch (*(unsigned char*)(ins)) { \ + case 0xe8: case 0xe9: ++size; break; /* call, jump32 */ \ + case 0x0f: if (!(*pos >= 0x70 && *pos <= 0x8f)) assert (0); \ + ++size; ++pos; break; /* prefix for 32-bit disp */ \ + case 0xe0: case 0xe1: case 0xe2: /* loop */ \ + case 0xeb: /* jump8 */ \ + /* conditional jump opcodes */ \ + case 0x70: case 0x71: case 0x72: case 0x73: \ + case 0x74: case 0x75: case 0x76: case 0x77: \ + case 0x78: case 0x79: case 0x7a: case 0x7b: \ + case 0x7c: case 0x7d: case 0x7e: case 0x7f: \ + break; \ + default: assert (0); \ + } \ + disp = (target) - pos; \ + if (size) x86_imm_emit32 (pos, disp - 4); \ + else if (x86_is_imm8 (disp - 1)) x86_imm_emit8 (pos, disp - 1); \ + else assert (0); \ + } while (0) + +#if defined( __native_client_codegen__ ) && defined(TARGET_X86) + +#define x86_skip_nops(inst) \ + do { \ + int in_nop = 0; \ + do { \ + in_nop = 0; \ + if (inst[0] == 0x90) { \ + in_nop = 1; \ + inst += 1; \ + } \ + if (inst[0] == 0x8b && inst[1] == 0xc0) { \ + in_nop = 1; \ + inst += 2; \ + } \ + if (inst[0] == 0x8d && inst[1] == 0x6d \ + && inst[2] == 0x00) { \ + in_nop = 1; \ + inst += 3; \ + } \ + if (inst[0] == 0x8d && inst[1] == 0x64 \ + && inst[2] == 0x24 && inst[3] == 0x00) { \ + in_nop = 1; \ + inst += 4; \ + } \ + /* skip inst+=5 case because it's the 4-byte + 1-byte case */ \ + if (inst[0] == 0x8d && inst[1] == 0xad \ + && inst[2] == 0x00 && inst[3] == 0x00 \ + && inst[4] == 0x00 && inst[5] == 0x00) { \ + in_nop = 1; \ + inst += 6; \ + } \ + if (inst[0] == 0x8d && inst[1] == 0xa4 \ + && inst[2] == 0x24 && inst[3] == 0x00 \ + && inst[4] == 0x00 && inst[5] == 0x00 \ + && inst[6] == 0x00 ) { \ + in_nop = 1; \ + inst += 7; \ + } \ + } while ( in_nop ); \ + } while (0) + +#if defined(__native_client__) +#define x86_patch(ins,target) \ + do { \ + unsigned char* inst = (ins); \ + guint8* new_target = nacl_modify_patch_target((target)); \ + x86_skip_nops((inst)); \ + x86_do_patch((inst), new_target); \ + } while (0) +#else /* __native_client__ */ +#define x86_patch(ins,target) \ + do { \ + unsigned char* inst = (ins); \ + guint8* new_target = (target); \ + x86_skip_nops((inst)); \ + x86_do_patch((inst), new_target); \ + } while (0) +#endif /* __native_client__ */ + +#else +#define x86_patch(ins,target) do { x86_do_patch((ins), (target)); } while (0) +#endif /* __native_client_codegen__ */ + +#ifdef __native_client_codegen__ +/* The breakpoint instruction is illegal in Native Client, although the HALT */ +/* instruction is allowed. The breakpoint is used several places in mini-x86.c */ +/* and exceptions-x86.c. */ +#define x86_breakpoint(inst) \ + do { \ + *(inst)++ = 0xf4; \ + } while (0) +#else +#define x86_breakpoint(inst) \ + do { \ + *(inst)++ = 0xcc; \ + } while (0) +#endif + +#define x86_cld(inst) do { *(inst)++ =(unsigned char)0xfc; } while (0) +#define x86_stosb(inst) do { *(inst)++ =(unsigned char)0xaa; } while (0) +#define x86_stosl(inst) do { *(inst)++ =(unsigned char)0xab; } while (0) +#define x86_stosd(inst) x86_stosl((inst)) +#define x86_movsb(inst) do { *(inst)++ =(unsigned char)0xa4; } while (0) +#define x86_movsl(inst) do { *(inst)++ =(unsigned char)0xa5; } while (0) +#define x86_movsd(inst) x86_movsl((inst)) + +#if defined(__default_codegen__) +#define x86_prefix(inst,p) \ + do { \ + *(inst)++ =(unsigned char) (p); \ + } while (0) +#elif defined(__native_client_codegen__) +#if defined(TARGET_X86) +/* kNaClAlignment - 1 is the max value we can pass into x86_codegen_pre. */ +/* This keeps us from having to call x86_codegen_pre with specific */ +/* knowledge of the size of the instruction that follows it, and */ +/* localizes the alignment requirement to this spot. */ +#define x86_prefix(inst,p) \ + do { \ + x86_codegen_pre(&(inst), kNaClAlignment - 1); \ + *(inst)++ =(unsigned char) (p); \ + } while (0) +#elif defined(TARGET_AMD64) +/* We need to tag any prefixes so we can perform proper membase sandboxing */ +/* See: mini-amd64.c:amd64_nacl_membase_handler for verbose details */ +#define x86_prefix(inst,p) \ + do { \ + amd64_nacl_tag_legacy_prefix((inst)); \ + *(inst)++ =(unsigned char) (p); \ + } while (0) + +#endif /* TARGET_AMD64 */ + +#endif /* __native_client_codegen__ */ + +#define x86_rdtsc(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = 0x0f; \ + *(inst)++ = 0x31; \ + } while (0) + +#define x86_cmpxchg_reg_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xb1; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_cmpxchg_mem_reg(inst,mem,reg) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xb1; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_cmpxchg_membase_reg(inst,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xb1; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_xchg_reg_reg(inst,dreg,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0x86; \ + else \ + *(inst)++ = (unsigned char)0x87; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_xchg_mem_reg(inst,mem,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0x86; \ + else \ + *(inst)++ = (unsigned char)0x87; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_xchg_membase_reg(inst,basereg,disp,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0x86; \ + else \ + *(inst)++ = (unsigned char)0x87; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_xadd_reg_reg(inst,dreg,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0F; \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0xC0; \ + else \ + *(inst)++ = (unsigned char)0xC1; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_xadd_mem_reg(inst,mem,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0F; \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0xC0; \ + else \ + *(inst)++ = (unsigned char)0xC1; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_xadd_membase_reg(inst,basereg,disp,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0F; \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0xC0; \ + else \ + *(inst)++ = (unsigned char)0xC1; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_inc_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_inc_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_inc_reg(inst,reg) do { *(inst)++ = (unsigned char)0x40 + (reg); } while (0) + +#define x86_dec_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 1, (mem)); \ + } while (0) + +#define x86_dec_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 1, (basereg), (disp)); \ + } while (0) + +#define x86_dec_reg(inst,reg) do { *(inst)++ = (unsigned char)0x48 + (reg); } while (0) + +#define x86_not_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 2, (mem)); \ + } while (0) + +#define x86_not_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } while (0) + +#define x86_not_reg(inst,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 2, (reg)); \ + } while (0) + +#define x86_neg_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 3, (mem)); \ + } while (0) + +#define x86_neg_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 3, (basereg), (disp)); \ + } while (0) + +#define x86_neg_reg(inst,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 3, (reg)); \ + } while (0) + +#define x86_nop(inst) do { *(inst)++ = (unsigned char)0x90; } while (0) + +#define x86_alu_reg_imm(inst,opc,reg,imm) \ + do { \ + if ((reg) == X86_EAX) { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ + x86_imm_emit32 ((inst), (imm)); \ + break; \ + } \ + if (x86_is_imm8((imm))) { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x83; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x81; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_alu_mem_imm(inst,opc,mem,imm) \ + do { \ + if (x86_is_imm8((imm))) { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x83; \ + x86_mem_emit ((inst), (opc), (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 10); \ + *(inst)++ = (unsigned char)0x81; \ + x86_mem_emit ((inst), (opc), (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_alu_membase_imm(inst,opc,basereg,disp,imm) \ + do { \ + if (x86_is_imm8((imm))) { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x83; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x81; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_alu_membase8_imm(inst,opc,basereg,disp,imm) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x80; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_alu_mem_reg(inst,opc,mem,reg) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_alu_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_alu_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +/** + * @x86_alu_reg8_reg8: + * Supports ALU operations between two 8-bit registers. + * dreg := dreg opc reg + * X86_Reg_No enum is used to specify the registers. + * Additionally is_*_h flags are used to specify what part + * of a given 32-bit register is used - high (TRUE) or low (FALSE). + * For example: dreg = X86_EAX, is_dreg_h = TRUE -> use AH + */ +#define x86_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 2; \ + x86_reg8_emit ((inst), (dreg), (reg), (is_dreg_h), (is_reg_h)); \ + } while (0) + +#define x86_alu_reg_mem(inst,opc,reg,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_alu_reg_membase(inst,opc,reg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_test_reg_imm(inst,reg,imm) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + if ((reg) == X86_EAX) { \ + *(inst)++ = (unsigned char)0xa9; \ + } else { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 0, (reg)); \ + } \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_test_mem_imm(inst,mem,imm) \ + do { \ + x86_codegen_pre(&(inst), 10); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_test_membase_imm(inst,basereg,disp,imm) \ + do { \ + x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_test_reg_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0x85; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_test_mem_reg(inst,mem,reg) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x85; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_test_membase_reg(inst,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x85; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_shift_reg_imm(inst,opc,reg,imm) \ + do { \ + if ((imm) == 1) { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd1; \ + x86_reg_emit ((inst), (opc), (reg)); \ + } else { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0xc1; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_shift_mem_imm(inst,opc,mem,imm) \ + do { \ + if ((imm) == 1) { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xd1; \ + x86_mem_emit ((inst), (opc), (mem)); \ + } else { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0xc1; \ + x86_mem_emit ((inst), (opc), (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_shift_membase_imm(inst,opc,basereg,disp,imm) \ + do { \ + if ((imm) == 1) { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xd1; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + } else { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xc1; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_shift_reg(inst,opc,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd3; \ + x86_reg_emit ((inst), (opc), (reg)); \ + } while (0) + +#define x86_shift_mem(inst,opc,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xd3; \ + x86_mem_emit ((inst), (opc), (mem)); \ + } while (0) + +#define x86_shift_membase(inst,opc,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xd3; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + } while (0) + +/* + * Multi op shift missing. + */ + +#define x86_shrd_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xad; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_shrd_reg_imm(inst,dreg,reg,shamt) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xac; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + x86_imm_emit8 ((inst), (shamt)); \ + } while (0) + +#define x86_shld_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xa5; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_shld_reg_imm(inst,dreg,reg,shamt) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xa4; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + x86_imm_emit8 ((inst), (shamt)); \ + } while (0) + +/* + * EDX:EAX = EAX * rm + */ +#define x86_mul_reg(inst,reg,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 4 + ((is_signed) ? 1 : 0), (reg)); \ + } while (0) + +#define x86_mul_mem(inst,mem,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 4 + ((is_signed) ? 1 : 0), (mem)); \ + } while (0) + +#define x86_mul_membase(inst,basereg,disp,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 4 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ + } while (0) + +/* + * r *= rm + */ +#define x86_imul_reg_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xaf; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_imul_reg_mem(inst,reg,mem) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xaf; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_imul_reg_membase(inst,reg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xaf; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +/* + * dreg = rm * imm + */ +#define x86_imul_reg_reg_imm(inst,dreg,reg,imm) \ + do { \ + if (x86_is_imm8 ((imm))) { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x6b; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x69; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_imul_reg_mem_imm(inst,reg,mem,imm) \ + do { \ + if (x86_is_imm8 ((imm))) { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x6b; \ + x86_mem_emit ((inst), (reg), (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x69; \ + x86_reg_emit ((inst), (reg), (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_imul_reg_membase_imm(inst,reg,basereg,disp,imm) \ + do { \ + if (x86_is_imm8 ((imm))) { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x6b; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x69; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +/* + * divide EDX:EAX by rm; + * eax = quotient, edx = remainder + */ + +#define x86_div_reg(inst,reg,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 6 + ((is_signed) ? 1 : 0), (reg)); \ + } while (0) + +#define x86_div_mem(inst,mem,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 6 + ((is_signed) ? 1 : 0), (mem)); \ + } while (0) + +#define x86_div_membase(inst,basereg,disp,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 6 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ + } while (0) + +#define x86_mov_mem_reg(inst,mem,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_mov_regp_reg(inst,regp,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_regp_emit ((inst), (reg), (regp)); \ + } while (0) + +#define x86_mov_membase_reg(inst,basereg,disp,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_mov_reg_reg(inst,dreg,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_mov_reg_mem(inst,reg,mem,size) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define kMovRegMembasePadding (2 + kMaxMembaseEmitPadding) + +#define x86_mov_reg_membase(inst,reg,basereg,disp,size) \ + do { \ + x86_codegen_pre(&(inst), kMovRegMembasePadding); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +/* + * Note: x86_clear_reg () chacnges the condition code! + */ +#define x86_clear_reg(inst,reg) x86_alu_reg_reg((inst), X86_XOR, (reg), (reg)) + +#define x86_mov_reg_imm(inst,reg,imm) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0xb8 + (reg); \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_mov_mem_imm(inst,mem,imm,size) \ + do { \ + if ((size) == 1) { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0xc6; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + x86_codegen_pre(&(inst), 9); \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 10); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_mov_membase_imm(inst,basereg,disp,imm,size) \ + do { \ + if ((size) == 1) { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xc6; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + x86_codegen_pre(&(inst), 4 + kMaxMembaseEmitPadding); \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) \ + do { \ + if ((size) == 1) { \ + x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0xc6; \ + x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + x86_codegen_pre(&(inst), 4 + kMaxMemindexEmitPadding); \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_lea_mem(inst,reg,mem) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x8d; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_lea_membase(inst,reg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x8d; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_lea_memindex(inst,reg,basereg,disp,indexreg,shift) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0x8d; \ + x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_widen_reg(inst,dreg,reg,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + g_assert (is_half || X86_IS_BYTE_REG (reg)); \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_widen_mem(inst,dreg,mem,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_mem_emit ((inst), (dreg), (mem)); \ + } while (0) + +#define x86_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_membase_emit ((inst), (dreg), (basereg), (disp)); \ + } while (0) + +#define x86_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_memindex_emit ((inst), (dreg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_cdq(inst) do { *(inst)++ = (unsigned char)0x99; } while (0) +#define x86_wait(inst) do { *(inst)++ = (unsigned char)0x9b; } while (0) + +#define x86_fp_op_mem(inst,opc,mem,is_double) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ + x86_mem_emit ((inst), (opc), (mem)); \ + } while (0) + +#define x86_fp_op_membase(inst,opc,basereg,disp,is_double) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + } while (0) + +#define x86_fp_op(inst,opc,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd8; \ + *(inst)++ = (unsigned char)0xc0+((opc)<<3)+((index)&0x07); \ + } while (0) + +#define x86_fp_op_reg(inst,opc,index,pop_stack) \ + do { \ + static const unsigned char map[] = { 0, 1, 2, 3, 5, 4, 7, 6, 8}; \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (pop_stack) ? (unsigned char)0xde : (unsigned char)0xdc; \ + *(inst)++ = (unsigned char)0xc0+(map[(opc)]<<3)+((index)&0x07); \ + } while (0) + +/** + * @x86_fp_int_op_membase + * Supports FPU operations between ST(0) and integer operand in memory. + * Operation encoded using X86_FP_Opcode enum. + * Operand is addressed by [basereg + disp]. + * is_int specifies whether operand is int32 (TRUE) or int16 (FALSE). + */ +#define x86_fp_int_op_membase(inst,opc,basereg,disp,is_int) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (is_int) ? (unsigned char)0xda : (unsigned char)0xde; \ + x86_membase_emit ((inst), opc, (basereg), (disp)); \ + } while (0) + +#define x86_fstp(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdd; \ + *(inst)++ = (unsigned char)0xd8+(index); \ + } while (0) + +#define x86_fcompp(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xde; \ + *(inst)++ = (unsigned char)0xd9; \ + } while (0) + +#define x86_fucompp(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xda; \ + *(inst)++ = (unsigned char)0xe9; \ + } while (0) + +#define x86_fnstsw(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xe0; \ + } while (0) + +#define x86_fnstcw(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xd9; \ + x86_mem_emit ((inst), 7, (mem)); \ + } while (0) + +#define x86_fnstcw_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xd9; \ + x86_membase_emit ((inst), 7, (basereg), (disp)); \ + } while (0) + +#define x86_fldcw(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xd9; \ + x86_mem_emit ((inst), 5, (mem)); \ + } while (0) + +#define x86_fldcw_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xd9; \ + x86_membase_emit ((inst), 5, (basereg), (disp)); \ + } while (0) + +#define x86_fchs(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xe0; \ + } while (0) + +#define x86_frem(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xf8; \ + } while (0) + +#define x86_fxch(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xc8 + ((index) & 0x07); \ + } while (0) + +#define x86_fcomi(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdb; \ + *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ + } while (0) + +#define x86_fcomip(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ + } while (0) + +#define x86_fucomi(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdb; \ + *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ + } while (0) + +#define x86_fucomip(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ + } while (0) + +#define x86_fld(inst,mem,is_double) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_fld_membase(inst,basereg,disp,is_double) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_fld80_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 5, (mem)); \ + } while (0) + +#define x86_fld80_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 5, (basereg), (disp)); \ + } while (0) + +#define x86_fild(inst,mem,is_long) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_mem_emit ((inst), 5, (mem)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 0, (mem)); \ + } \ + } while (0) + +#define x86_fild_membase(inst,basereg,disp,is_long) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_membase_emit ((inst), 5, (basereg), (disp)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } \ + } while (0) + +#define x86_fld_reg(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xc0 + ((index) & 0x07); \ + } while (0) + +#define x86_fldz(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xee; \ + } while (0) + +#define x86_fld1(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xe8; \ + } while (0) + +#define x86_fldpi(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xeb; \ + } while (0) + +#define x86_fst(inst,mem,is_double,pop_stack) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ + x86_mem_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (mem)); \ + } while (0) + +#define x86_fst_membase(inst,basereg,disp,is_double,pop_stack) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ + x86_membase_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (basereg), (disp)); \ + } while (0) + +#define x86_fst80_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 7, (mem)); \ + } while (0) + + +#define x86_fst80_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 7, (basereg), (disp)); \ + } while (0) + + +#define x86_fist_pop(inst,mem,is_long) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_mem_emit ((inst), 7, (mem)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 3, (mem)); \ + } \ + } while (0) + +#define x86_fist_pop_membase(inst,basereg,disp,is_long) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_membase_emit ((inst), 7, (basereg), (disp)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 3, (basereg), (disp)); \ + } \ + } while (0) + +#define x86_fstsw(inst) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x9b; \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xe0; \ + } while (0) + +/** + * @x86_fist_membase + * Converts content of ST(0) to integer and stores it at memory location + * addressed by [basereg + disp]. + * is_int specifies whether destination is int32 (TRUE) or int16 (FALSE). + */ +#define x86_fist_membase(inst,basereg,disp,is_int) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + if ((is_int)) { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } else { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } \ + } while (0) + + +#define x86_push_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0x50 + (reg); \ + } while (0) + +#define x86_push_regp(inst,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xff; \ + x86_regp_emit ((inst), 6, (reg)); \ + } while (0) + +#define x86_push_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 6, (mem)); \ + } while (0) + +#define x86_push_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 6, (basereg), (disp)); \ + } while (0) + +#define x86_push_memindex(inst,basereg,disp,indexreg,shift) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0xff; \ + x86_memindex_emit ((inst), 6, (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_push_imm_template(inst) x86_push_imm (inst, 0xf0f0f0f0) + +#define x86_push_imm(inst,imm) \ + do { \ + int _imm = (int) (imm); \ + if (x86_is_imm8 (_imm)) { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0x6A; \ + x86_imm_emit8 ((inst), (_imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x68; \ + x86_imm_emit32 ((inst), (_imm)); \ + } \ + } while (0) + +#define x86_pop_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0x58 + (reg); \ + } while (0) + +#define x86_pop_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x87; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_pop_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x87; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_pushad(inst) do { *(inst)++ = (unsigned char)0x60; } while (0) +#define x86_pushfd(inst) do { *(inst)++ = (unsigned char)0x9c; } while (0) +#define x86_popad(inst) do { *(inst)++ = (unsigned char)0x61; } while (0) +#define x86_popfd(inst) do { *(inst)++ = (unsigned char)0x9d; } while (0) + +#define x86_loop(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xe2; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_loope(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xe1; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_loopne(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xe0; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#if defined(TARGET_X86) +#define x86_jump32(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0xe9; \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_jump8(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xeb; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) +#elif defined(TARGET_AMD64) +/* These macros are used directly from mini-amd64.c and other */ +/* amd64 specific files, so they need to be instrumented directly. */ +#define x86_jump32(inst,imm) \ + do { \ + amd64_codegen_pre(inst); \ + *(inst)++ = (unsigned char)0xe9; \ + x86_imm_emit32 ((inst), (imm)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define x86_jump8(inst,imm) \ + do { \ + amd64_codegen_pre(inst); \ + *(inst)++ = (unsigned char)0xeb; \ + x86_imm_emit8 ((inst), (imm)); \ + amd64_codegen_post(inst); \ + } while (0) +#endif + +#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) +#define x86_jump_reg(inst,reg) do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x83; /* and */ \ + x86_reg_emit ((inst), 4, (reg)); /* reg */ \ + *(inst)++ = (unsigned char)nacl_align_byte; \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst), 4, (reg)); \ + } while (0) + +/* Let's hope ECX is available for these... */ +#define x86_jump_mem(inst,mem) do { \ + x86_mov_reg_mem(inst, (X86_ECX), (mem), 4); \ + x86_jump_reg(inst, (X86_ECX)); \ + } while (0) + +#define x86_jump_membase(inst,basereg,disp) do { \ + x86_mov_reg_membase(inst, (X86_ECX), basereg, disp, 4); \ + x86_jump_reg(inst, (X86_ECX)); \ + } while (0) + +/* like x86_jump_membase, but force a 32-bit displacement */ +#define x86_jump_membase32(inst,basereg,disp) do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x8b; \ + x86_address_byte ((inst), 2, X86_ECX, (basereg)); \ + x86_imm_emit32 ((inst), (disp)); \ + x86_jump_reg(inst, (X86_ECX)); \ + } while (0) +#else /* __native_client_codegen__ */ +#define x86_jump_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst), 4, (reg)); \ + } while (0) + +#define x86_jump_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 4, (mem)); \ + } while (0) + +#define x86_jump_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 4, (basereg), (disp)); \ + } while (0) +#endif /* __native_client_codegen__ */ +/* + * target is a pointer in our buffer. + */ +#define x86_jump_code_body(inst,target) \ + do { \ + int t; \ + x86_codegen_pre(&(inst), 2); \ + t = (unsigned char*)(target) - (inst) - 2; \ + if (x86_is_imm8(t)) { \ + x86_jump8 ((inst), t); \ + } else { \ + x86_codegen_pre(&(inst), 5); \ + t = (unsigned char*)(target) - (inst) - 5; \ + x86_jump32 ((inst), t); \ + } \ + } while (0) + +#if defined(__default_codegen__) +#define x86_jump_code(inst,target) \ + do { \ + x86_jump_code_body((inst),(target)); \ + } while (0) +#elif defined(__native_client_codegen__) && defined(TARGET_X86) +#define x86_jump_code(inst,target) \ + do { \ + guint8* jump_start = (inst); \ + x86_jump_code_body((inst),(target)); \ + x86_patch(jump_start, (target)); \ + } while (0) +#elif defined(__native_client_codegen__) && defined(TARGET_AMD64) +#define x86_jump_code(inst,target) \ + do { \ + /* jump_code_body is used twice because there are offsets */ \ + /* calculated based on the IP, which can change after the */ \ + /* call to amd64_codegen_post */ \ + amd64_codegen_pre(inst); \ + x86_jump_code_body((inst),(target)); \ + inst = amd64_codegen_post(inst); \ + x86_jump_code_body((inst),(target)); \ + } while (0) +#endif /* __native_client_codegen__ */ + +#define x86_jump_disp(inst,disp) \ + do { \ + int t = (disp) - 2; \ + if (x86_is_imm8(t)) { \ + x86_jump8 ((inst), t); \ + } else { \ + t -= 3; \ + x86_jump32 ((inst), t); \ + } \ + } while (0) + +#if defined(TARGET_X86) +#define x86_branch8(inst,cond,imm,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)]; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)]; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_branch32(inst,cond,imm,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x10; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x10; \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) +#elif defined(TARGET_AMD64) +/* These macros are used directly from mini-amd64.c and other */ +/* amd64 specific files, so they need to be instrumented directly. */ +#define x86_branch8(inst,cond,imm,is_signed) \ + do { \ + amd64_codegen_pre(inst); \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)]; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)]; \ + x86_imm_emit8 ((inst), (imm)); \ + amd64_codegen_post(inst); \ + } while (0) +#define x86_branch32(inst,cond,imm,is_signed) \ + do { \ + amd64_codegen_pre(inst); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x10; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x10; \ + x86_imm_emit32 ((inst), (imm)); \ + amd64_codegen_post(inst); \ + } while (0) +#endif + +#if defined(TARGET_X86) +#define x86_branch(inst,cond,target,is_signed) \ + do { \ + int offset; \ + guint8* branch_start; \ + x86_codegen_pre(&(inst), 2); \ + offset = (target) - (inst) - 2; \ + branch_start = (inst); \ + if (x86_is_imm8 ((offset))) \ + x86_branch8 ((inst), (cond), offset, (is_signed)); \ + else { \ + x86_codegen_pre(&(inst), 6); \ + offset = (target) - (inst) - 6; \ + x86_branch32 ((inst), (cond), offset, (is_signed)); \ + } \ + x86_patch(branch_start, (target)); \ + } while (0) +#elif defined(TARGET_AMD64) +/* This macro is used directly from mini-amd64.c and other */ +/* amd64 specific files, so it needs to be instrumented directly. */ + +#define x86_branch_body(inst,cond,target,is_signed) \ + do { \ + int offset = (target) - (inst) - 2; \ + if (x86_is_imm8 ((offset))) \ + x86_branch8 ((inst), (cond), offset, (is_signed)); \ + else { \ + offset = (target) - (inst) - 6; \ + x86_branch32 ((inst), (cond), offset, (is_signed)); \ + } \ + } while (0) + +#if defined(__default_codegen__) +#define x86_branch(inst,cond,target,is_signed) \ + do { \ + x86_branch_body((inst),(cond),(target),(is_signed)); \ + } while (0) +#elif defined(__native_client_codegen__) +#define x86_branch(inst,cond,target,is_signed) \ + do { \ + /* branch_body is used twice because there are offsets */ \ + /* calculated based on the IP, which can change after */ \ + /* the call to amd64_codegen_post */ \ + amd64_codegen_pre(inst); \ + x86_branch_body((inst),(cond),(target),(is_signed)); \ + inst = amd64_codegen_post(inst); \ + x86_branch_body((inst),(cond),(target),(is_signed)); \ + } while (0) +#endif /* __native_client_codegen__ */ + +#endif /* TARGET_AMD64 */ + +#define x86_branch_disp(inst,cond,disp,is_signed) \ + do { \ + int offset = (disp) - 2; \ + if (x86_is_imm8 ((offset))) \ + x86_branch8 ((inst), (cond), offset, (is_signed)); \ + else { \ + offset -= 4; \ + x86_branch32 ((inst), (cond), offset, (is_signed)); \ + } \ + } while (0) + +#define x86_set_reg(inst,cond,reg,is_signed) \ + do { \ + g_assert (X86_IS_BYTE_REG (reg)); \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ + x86_reg_emit ((inst), 0, (reg)); \ + } while (0) + +#define x86_set_mem(inst,cond,mem,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_set_membase(inst,cond,basereg,disp,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_call_imm_body(inst,disp) \ + do { \ + *(inst)++ = (unsigned char)0xe8; \ + x86_imm_emit32 ((inst), (int)(disp)); \ + } while (0) + +#define x86_call_imm(inst,disp) \ + do { \ + x86_call_sequence_pre((inst)); \ + x86_call_imm_body((inst), (disp)); \ + x86_call_sequence_post((inst)); \ + } while (0) + + +#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) +#define x86_call_reg_internal(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0x83; /* and */ \ + x86_reg_emit ((inst), 4, (reg)); /* reg */ \ + *(inst)++ = (unsigned char)nacl_align_byte; \ + *(inst)++ = (unsigned char)0xff; /* call */ \ + x86_reg_emit ((inst), 2, (reg)); /* reg */ \ + } while (0) + +#define x86_call_reg(inst, reg) do { \ + x86_call_sequence_pre((inst)); \ + x86_call_reg_internal(inst, reg); \ + x86_call_sequence_post((inst)); \ + } while (0) + + +/* It appears that x86_call_mem() is never used, so I'm leaving it out. */ +#define x86_call_membase(inst,basereg,disp) do { \ + x86_call_sequence_pre((inst)); \ + /* x86_mov_reg_membase() inlined so its fixed size */ \ + *(inst)++ = (unsigned char)0x8b; \ + x86_address_byte ((inst), 2, (X86_ECX), (basereg)); \ + x86_imm_emit32 ((inst), (disp)); \ + x86_call_reg_internal(inst, X86_ECX); \ + x86_call_sequence_post((inst)); \ + } while (0) +#else /* __native_client_codegen__ */ +#define x86_call_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst), 2, (reg)); \ + } while (0) + +#define x86_call_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 2, (mem)); \ + } while (0) + +#define x86_call_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } while (0) +#endif /* __native_client_codegen__ */ + + +#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) + +#define x86_call_code(inst,target) \ + do { \ + int _x86_offset; \ + guint8* call_start; \ + guint8* _aligned_start; \ + x86_call_sequence_pre_val((inst)); \ + _x86_offset = (unsigned char*)(target) - (inst); \ + _x86_offset -= 5; \ + x86_call_imm_body ((inst), _x86_offset); \ + _aligned_start = x86_call_sequence_post_val((inst)); \ + call_start = _aligned_start; \ + _x86_offset = (unsigned char*)(target) - (_aligned_start); \ + _x86_offset -= 5; \ + x86_call_imm_body ((_aligned_start), _x86_offset); \ + x86_patch(call_start, (target)); \ + } while (0) + +#define SIZE_OF_RET 6 +#define x86_ret(inst) do { \ + *(inst)++ = (unsigned char)0x59; /* pop ecx */ \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x83; /* and 0xffffffff, ecx */ \ + *(inst)++ = (unsigned char)0xe1; \ + *(inst)++ = (unsigned char)nacl_align_byte; \ + *(inst)++ = (unsigned char)0xff; /* jmp ecx */ \ + *(inst)++ = (unsigned char)0xe1; \ + } while (0) + +/* pop return address */ +/* pop imm bytes from stack */ +/* return */ +#define x86_ret_imm(inst,imm) do { \ + *(inst)++ = (unsigned char)0x59; /* pop ecx */ \ + x86_alu_reg_imm ((inst), X86_ADD, X86_ESP, imm); \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x83; /* and 0xffffffff, ecx */ \ + *(inst)++ = (unsigned char)0xe1; \ + *(inst)++ = (unsigned char)nacl_align_byte; \ + *(inst)++ = (unsigned char)0xff; /* jmp ecx */ \ + *(inst)++ = (unsigned char)0xe1; \ +} while (0) +#else /* __native_client_codegen__ */ + +#define x86_call_code(inst,target) \ + do { \ + int _x86_offset; \ + _x86_offset = (unsigned char*)(target) - (inst); \ + _x86_offset -= 5; \ + x86_call_imm_body ((inst), _x86_offset); \ + } while (0) + +#define x86_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) + +#define x86_ret_imm(inst,imm) \ + do { \ + if ((imm) == 0) { \ + x86_ret ((inst)); \ + } else { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0xc2; \ + x86_imm_emit16 ((inst), (imm)); \ + } \ + } while (0) +#endif /* __native_client_codegen__ */ + +#define x86_cmov_reg(inst,cond,is_signed,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char) 0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_cmov_mem(inst,cond,is_signed,reg,mem) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char) 0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_cmov_membase(inst,cond,is_signed,reg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char) 0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_enter(inst,framesize) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0xc8; \ + x86_imm_emit16 ((inst), (framesize)); \ + *(inst)++ = 0; \ + } while (0) + +#define x86_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) +#define x86_sahf(inst) do { *(inst)++ = (unsigned char)0x9e; } while (0) + +#define x86_fsin(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfe; } while (0) +#define x86_fcos(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xff; } while (0) +#define x86_fabs(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe1; } while (0) +#define x86_ftst(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe4; } while (0) +#define x86_fxam(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe5; } while (0) +#define x86_fpatan(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf3; } while (0) +#define x86_fprem(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf8; } while (0) +#define x86_fprem1(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf5; } while (0) +#define x86_frndint(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfc; } while (0) +#define x86_fsqrt(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfa; } while (0) +#define x86_fptan(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf2; } while (0) + +#define x86_padding(inst,size) \ + do { \ + switch ((size)) { \ + case 1: x86_nop ((inst)); break; \ + case 2: *(inst)++ = 0x8b; \ + *(inst)++ = 0xc0; break; \ + case 3: *(inst)++ = 0x8d; *(inst)++ = 0x6d; \ + *(inst)++ = 0x00; break; \ + case 4: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ + *(inst)++ = 0x24; *(inst)++ = 0x00; \ + break; \ + case 5: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ + *(inst)++ = 0x24; *(inst)++ = 0x00; \ + x86_nop ((inst)); break; \ + case 6: *(inst)++ = 0x8d; *(inst)++ = 0xad; \ + *(inst)++ = 0x00; *(inst)++ = 0x00; \ + *(inst)++ = 0x00; *(inst)++ = 0x00; \ + break; \ + case 7: *(inst)++ = 0x8d; *(inst)++ = 0xa4; \ + *(inst)++ = 0x24; *(inst)++ = 0x00; \ + *(inst)++ = 0x00; *(inst)++ = 0x00; \ + *(inst)++ = 0x00; break; \ + default: assert (0); \ + } \ + } while (0) + +#ifdef __native_client_codegen__ + +#define kx86NaClLengthOfCallReg 5 +#define kx86NaClLengthOfCallImm 5 +#define kx86NaClLengthOfCallMembase (kx86NaClLengthOfCallReg + 6) + +#endif /* __native_client_codegen__ */ + +#define x86_prolog(inst,frame_size,reg_mask) \ + do { \ + unsigned i, m = 1; \ + x86_enter ((inst), (frame_size)); \ + for (i = 0; i < X86_NREG; ++i, m <<= 1) { \ + if ((reg_mask) & m) \ + x86_push_reg ((inst), i); \ + } \ + } while (0) + +#define x86_epilog(inst,reg_mask) \ + do { \ + unsigned i, m = 1 << X86_EDI; \ + for (i = X86_EDI; m != 0; i--, m=m>>1) { \ + if ((reg_mask) & m) \ + x86_pop_reg ((inst), i); \ + } \ + x86_leave ((inst)); \ + x86_ret ((inst)); \ + } while (0) + + +typedef enum { + X86_SSE_SQRT = 0x51, + X86_SSE_RSQRT = 0x52, + X86_SSE_RCP = 0x53, + X86_SSE_ADD = 0x58, + X86_SSE_DIV = 0x5E, + X86_SSE_MUL = 0x59, + X86_SSE_SUB = 0x5C, + X86_SSE_MIN = 0x5D, + X86_SSE_MAX = 0x5F, + X86_SSE_COMP = 0xC2, + X86_SSE_AND = 0x54, + X86_SSE_ANDN = 0x55, + X86_SSE_OR = 0x56, + X86_SSE_XOR = 0x57, + X86_SSE_UNPCKL = 0x14, + X86_SSE_UNPCKH = 0x15, + + X86_SSE_ADDSUB = 0xD0, + X86_SSE_HADD = 0x7C, + X86_SSE_HSUB = 0x7D, + X86_SSE_MOVSHDUP = 0x16, + X86_SSE_MOVSLDUP = 0x12, + X86_SSE_MOVDDUP = 0x12, + + X86_SSE_PAND = 0xDB, + X86_SSE_POR = 0xEB, + X86_SSE_PXOR = 0xEF, + + X86_SSE_PADDB = 0xFC, + X86_SSE_PADDW = 0xFD, + X86_SSE_PADDD = 0xFE, + X86_SSE_PADDQ = 0xD4, + + X86_SSE_PSUBB = 0xF8, + X86_SSE_PSUBW = 0xF9, + X86_SSE_PSUBD = 0xFA, + X86_SSE_PSUBQ = 0xFB, + + X86_SSE_PMAXSB = 0x3C, /*sse41*/ + X86_SSE_PMAXSW = 0xEE, + X86_SSE_PMAXSD = 0x3D, /*sse41*/ + + X86_SSE_PMAXUB = 0xDE, + X86_SSE_PMAXUW = 0x3E, /*sse41*/ + X86_SSE_PMAXUD = 0x3F, /*sse41*/ + + X86_SSE_PMINSB = 0x38, /*sse41*/ + X86_SSE_PMINSW = 0xEA, + X86_SSE_PMINSD = 0x39,/*sse41*/ + + X86_SSE_PMINUB = 0xDA, + X86_SSE_PMINUW = 0x3A, /*sse41*/ + X86_SSE_PMINUD = 0x3B, /*sse41*/ + + X86_SSE_PAVGB = 0xE0, + X86_SSE_PAVGW = 0xE3, + + X86_SSE_PCMPEQB = 0x74, + X86_SSE_PCMPEQW = 0x75, + X86_SSE_PCMPEQD = 0x76, + X86_SSE_PCMPEQQ = 0x29, /*sse41*/ + + X86_SSE_PCMPGTB = 0x64, + X86_SSE_PCMPGTW = 0x65, + X86_SSE_PCMPGTD = 0x66, + X86_SSE_PCMPGTQ = 0x37, /*sse42*/ + + X86_SSE_PSADBW = 0xf6, + + X86_SSE_PSHUFD = 0x70, + + X86_SSE_PUNPCKLBW = 0x60, + X86_SSE_PUNPCKLWD = 0x61, + X86_SSE_PUNPCKLDQ = 0x62, + X86_SSE_PUNPCKLQDQ = 0x6C, + + X86_SSE_PUNPCKHBW = 0x68, + X86_SSE_PUNPCKHWD = 0x69, + X86_SSE_PUNPCKHDQ = 0x6A, + X86_SSE_PUNPCKHQDQ = 0x6D, + + X86_SSE_PACKSSWB = 0x63, + X86_SSE_PACKSSDW = 0x6B, + + X86_SSE_PACKUSWB = 0x67, + X86_SSE_PACKUSDW = 0x2B,/*sse41*/ + + X86_SSE_PADDUSB = 0xDC, + X86_SSE_PADDUSW = 0xDD, + X86_SSE_PSUBUSB = 0xD8, + X86_SSE_PSUBUSW = 0xD9, + + X86_SSE_PADDSB = 0xEC, + X86_SSE_PADDSW = 0xED, + X86_SSE_PSUBSB = 0xE8, + X86_SSE_PSUBSW = 0xE9, + + X86_SSE_PMULLW = 0xD5, + X86_SSE_PMULLD = 0x40,/*sse41*/ + X86_SSE_PMULHUW = 0xE4, + X86_SSE_PMULHW = 0xE5, + X86_SSE_PMULUDQ = 0xF4, + + X86_SSE_PMOVMSKB = 0xD7, + + X86_SSE_PSHIFTW = 0x71, + X86_SSE_PSHIFTD = 0x72, + X86_SSE_PSHIFTQ = 0x73, + X86_SSE_SHR = 2, + X86_SSE_SAR = 4, + X86_SSE_SHL = 6, + + X86_SSE_PSRLW_REG = 0xD1, + X86_SSE_PSRAW_REG = 0xE1, + X86_SSE_PSLLW_REG = 0xF1, + + X86_SSE_PSRLD_REG = 0xD2, + X86_SSE_PSRAD_REG = 0xE2, + X86_SSE_PSLLD_REG = 0xF2, + + X86_SSE_PSRLQ_REG = 0xD3, + X86_SSE_PSLLQ_REG = 0xF3, + + X86_SSE_PREFETCH = 0x18, + X86_SSE_MOVNTPS = 0x2B, + X86_SSE_MOVHPD_REG_MEMBASE = 0x16, + X86_SSE_MOVHPD_MEMBASE_REG = 0x17, + + X86_SSE_MOVSD_REG_MEMBASE = 0x10, + X86_SSE_MOVSD_MEMBASE_REG = 0x11, + + X86_SSE_PINSRB = 0x20,/*sse41*/ + X86_SSE_PINSRW = 0xC4, + X86_SSE_PINSRD = 0x22,/*sse41*/ + + X86_SSE_PEXTRB = 0x14,/*sse41*/ + X86_SSE_PEXTRW = 0xC5, + X86_SSE_PEXTRD = 0x16,/*sse41*/ + + X86_SSE_SHUFP = 0xC6, + + X86_SSE_CVTDQ2PD = 0xE6, + X86_SSE_CVTDQ2PS = 0x5B, + X86_SSE_CVTPD2DQ = 0xE6, + X86_SSE_CVTPD2PS = 0x5A, + X86_SSE_CVTPS2DQ = 0x5B, + X86_SSE_CVTPS2PD = 0x5A, + X86_SSE_CVTTPD2DQ = 0xE6, + X86_SSE_CVTTPS2DQ = 0x5B, +} X86_SSE_Opcode; + + +/* minimal SSE* support */ +#define x86_movsd_reg_membase(inst,dreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf2; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_membase_emit ((inst), (dreg), (basereg), (disp)); \ + } while (0) + +#define x86_cvttsd2si(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0xf2; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x2c; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0F; \ + *(inst)++ = (unsigned char)(opc); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_reg_membase(inst,opc,sreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)(opc); \ + x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ + } while (0) + +#define x86_sse_alu_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0F; \ + *(inst)++ = (unsigned char)(opc); \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_sse_alu_reg_reg_imm8(inst,opc,dreg,reg, imm8) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x0F; \ + *(inst)++ = (unsigned char)(opc); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + *(inst)++ = (unsigned char)(imm8); \ + } while (0) + +#define x86_sse_alu_pd_reg_reg_imm8(inst,opc,dreg,reg, imm8) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x66; \ + x86_sse_alu_reg_reg_imm8 ((inst), (opc), (dreg), (reg), (imm8)); \ + } while (0) + +#define x86_sse_alu_pd_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x66; \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_pd_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x66; \ + x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ + } while (0) + +#define x86_sse_alu_pd_reg_membase(inst,opc,dreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x66; \ + x86_sse_alu_reg_membase ((inst), (opc), (dreg),(basereg), (disp)); \ + } while (0) + +#define x86_sse_alu_pd_reg_reg_imm(inst,opc,dreg,reg,imm) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + x86_sse_alu_pd_reg_reg ((inst), (opc), (dreg), (reg)); \ + *(inst)++ = (unsigned char)(imm); \ + } while (0) + +#define x86_sse_alu_pd_reg_membase_imm(inst,opc,dreg,basereg,disp,imm) \ + do { \ + x86_codegen_pre(&(inst), 4 + kMaxMembaseEmitPadding); \ + x86_sse_alu_pd_reg_membase ((inst), (opc), (dreg),(basereg), (disp)); \ + *(inst)++ = (unsigned char)(imm); \ + } while (0) + + +#define x86_sse_alu_ps_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_ps_reg_reg_imm(inst,opc,dreg,reg, imm) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + *(inst)++ = (unsigned char)imm; \ + } while (0) + + +#define x86_sse_alu_sd_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0xF2; \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_sd_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xF2; \ + x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ + } while (0) + + +#define x86_sse_alu_ss_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0xF3; \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_ss_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xF3; \ + x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ + } while (0) + + + +#define x86_sse_alu_sse41_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0x0F; \ + *(inst)++ = (unsigned char)0x38; \ + *(inst)++ = (unsigned char)(opc); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_movups_reg_membase(inst,sreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ + } while (0) + +#define x86_movups_membase_reg(inst,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_movaps_reg_membase(inst,sreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x28; \ + x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ + } while (0) + +#define x86_movaps_membase_reg(inst,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x29; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_movaps_reg_reg(inst,dreg,sreg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x28; \ + x86_reg_emit ((inst), (dreg), (sreg)); \ + } while (0) + + +#define x86_movd_reg_xreg(inst,dreg,sreg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x7e; \ + x86_reg_emit ((inst), (sreg), (dreg)); \ + } while (0) + +#define x86_movd_xreg_reg(inst,dreg,sreg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x6e; \ + x86_reg_emit ((inst), (dreg), (sreg)); \ + } while (0) + +#define x86_movd_xreg_membase(inst,sreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x6e; \ + x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ + } while (0) + +#define x86_pshufw_reg_reg(inst,dreg,sreg,mask,high_words) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)(high_words) ? 0xF3 : 0xF2; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x70; \ + x86_reg_emit ((inst), (dreg), (sreg)); \ + *(inst)++ = (unsigned char)mask; \ + } while (0) + +#define x86_sse_shift_reg_imm(inst,opc,mode, dreg,imm) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + x86_sse_alu_pd_reg_reg (inst, opc, mode, dreg); \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_sse_shift_reg_reg(inst,opc,dreg,sreg) \ + do { \ + x86_sse_alu_pd_reg_reg (inst, opc, dreg, sreg); \ + } while (0) + + + +#endif // X86_H + -- cgit v1.2.3 From 4373d3a78443200fd34d794dc8a0e8ec131a131b Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Thu, 3 May 2012 17:57:00 +0200 Subject: Add instruction selection for string values. --- qv4isel.cpp | 12 ++++++++++++ tests/simple.js | 2 ++ 2 files changed, 14 insertions(+) diff --git a/qv4isel.cpp b/qv4isel.cpp index 7308157e5c..b7d31bf270 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -204,6 +204,12 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); amd64_call_code(_codePtr, __qmljs_set_activation_property_number); return; + } else if (IR::String *str = s->source->asString()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, new (GC) String(*str->value)); + amd64_call_code(_codePtr, __qmljs_set_activation_property_string); + return; } else if (IR::Temp *t = s->source->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); @@ -227,6 +233,12 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); amd64_call_code(_codePtr, __qmljs_init_number); return; + } else if (IR::String *str = s->source->asString()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, offset); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, new (GC) String(*str->value)); + amd64_call_code(_codePtr, __qmljs_init_string); + return; } else if (IR::Binop *b = s->source->asBinop()) { IR::Temp *l = b->left->asTemp(); IR::Temp *r = b->right->asTemp(); diff --git a/tests/simple.js b/tests/simple.js index 90073a007a..30408c20f3 100644 --- a/tests/simple.js +++ b/tests/simple.js @@ -10,3 +10,5 @@ for (i = 0; i < 1000000; i = i + 1) { else d = 321 } + +d = "the result is " + d -- cgit v1.2.3 From 1b1a6d17471fcaa3a324cbfc312c374d49da8464 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Thu, 3 May 2012 18:06:23 +0200 Subject: Generate code for boolean values. --- qmljs_runtime.cpp | 12 ++++++++++++ qmljs_runtime.h | 2 ++ qv4isel.cpp | 38 ++++++++++++++++++++++++++++++++------ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index e7ee6f9b51..b95b80aef7 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -163,6 +163,13 @@ void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *valu object->objectValue->put(name, *value, /*flags*/ 0); } +void __qmljs_set_property_boolean(Context *ctx, Value *object, String *name, bool number) +{ + Value value; + __qmljs_init_boolean(ctx, &value, number); + object->objectValue->put(name, value, /*flag*/ 0); +} + void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double number) { Value value; @@ -182,6 +189,11 @@ void __qmljs_set_activation_property(Context *ctx, String *name, Value *value) __qmljs_set_property(ctx, &ctx->activation, name, value); } +void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool value) +{ + __qmljs_set_property_boolean(ctx, &ctx->activation, name, value); +} + void __qmljs_set_activation_property_number(Context *ctx, String *name, double value) { __qmljs_set_property_number(ctx, &ctx->activation, name, value); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 1b4b894f65..b2059cad92 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -71,9 +71,11 @@ void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean); void __qmljs_new_number_object(Context *ctx, Value *result, double n); void __qmljs_new_string_object(Context *ctx, Value *result, String *string); void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value); +void __qmljs_set_property_boolean(Context *ctx, Value *object, String *name, bool value); void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double value); void __qmljs_set_property_string(Context *ctx, Value *object, String *name, String *value); void __qmljs_set_activation_property(Context *ctx, String *name, Value *value); +void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool value); void __qmljs_set_activation_property_number(Context *ctx, String *name, double value); void __qmljs_set_activation_property_string(Context *ctx, String *name, String *value); void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name); diff --git a/qv4isel.cpp b/qv4isel.cpp index b7d31bf270..a475e7628e 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -200,9 +200,22 @@ void InstructionSelection::visitMove(IR::Move *s) if (IR::Const *c = s->source->asConst()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); - amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); - amd64_call_code(_codePtr, __qmljs_set_activation_property_number); + + switch (c->type) { + case IR::BoolType: + amd64_mov_reg_imm(_codePtr, AMD64_RDX, c->value != 0); + amd64_call_code(_codePtr, __qmljs_set_activation_property_boolean); + break; + + case IR::NumberType: + amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); + amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); + amd64_call_code(_codePtr, __qmljs_set_activation_property_number); + break; + + default: + Q_ASSERT(!"TODO"); + } return; } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); @@ -229,9 +242,22 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Const *c = s->source->asConst()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, offset); - amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); - amd64_call_code(_codePtr, __qmljs_init_number); + + switch (c->type) { + case IR::BoolType: + amd64_mov_reg_imm(_codePtr, AMD64_RDX, c->value != 0); + amd64_call_code(_codePtr, __qmljs_init_boolean); + break; + + case IR::NumberType: + amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); + amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); + amd64_call_code(_codePtr, __qmljs_init_number); + break; + + default: + Q_ASSERT(!"TODO"); + } return; } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); -- cgit v1.2.3 From 365e5176d3fa4d20c4be2da1338f0fd7cc0c789a Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 4 May 2012 10:44:28 +0200 Subject: Fix relational expressions --- qmljs_runtime.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index b2059cad92..f31f54cc17 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -556,7 +556,7 @@ inline void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Val inline void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Value *right) { - __qmljs_compare(ctx, result, right, left, false); + __qmljs_compare(ctx, result, left, right, false); bool r = ! (result->type == UNDEFINED_TYPE || (result->type == BOOLEAN_TYPE && result->booleanValue == true)); @@ -566,7 +566,7 @@ inline void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Val inline void __qmljs_le(Context *ctx, Value *result, const Value *left, const Value *right) { - __qmljs_compare(ctx, result, left, right, true); + __qmljs_compare(ctx, result, right, left, true); bool r = ! (result->type == UNDEFINED_TYPE || (result->type == BOOLEAN_TYPE && result->booleanValue == true)); -- cgit v1.2.3 From 82360468498b5d5f7504426c4fc7d4afc4cf5284 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 4 May 2012 11:01:16 +0200 Subject: Simplify instruction selections for temporaries --- qmljs_runtime.cpp | 21 +-------------------- qv4isel.cpp | 43 ++++++++++++++++++++++++++++++------------- qv4isel_p.h | 2 ++ 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index b95b80aef7..c6a2dd990a 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -246,26 +246,7 @@ void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y if (isnan(nx) || isnan(ny)) { __qmljs_init_undefined(ctx, result); } else { - const int sx = signbit(nx); - const int sy = signbit(ny); - const bool ix = isinf(nx); - const bool iy = isinf(ny); - bool r = false; - - if (ix && !sx) { - r = false; - } else if (iy && !sy) { - r = true; - } else if (iy && sy) { - r = false; - } else if (ix && sx) { - r = true; - } else if (nx == ny) { - r = false; - } else { - r = nx < ny; - } - __qmljs_init_boolean(ctx, result, r); + __qmljs_init_boolean(ctx, result, nx < ny); } } } diff --git a/qv4isel.cpp b/qv4isel.cpp index a475e7628e..9d05a8e0f7 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -109,10 +109,16 @@ void InstructionSelection::visitFunction(IR::Function *function) int locals = function->tempCount * sizeof(Value); locals = (locals + 15) & ~15; + amd64_push_reg(_codePtr, AMD64_RBP); amd64_push_reg(_codePtr, AMD64_R14); + amd64_push_reg(_codePtr, AMD64_R15); + + amd64_mov_reg_reg(_codePtr, AMD64_RBP, AMD64_RSP, 8); amd64_mov_reg_reg(_codePtr, AMD64_R14, AMD64_RDI, 8); amd64_alu_reg_imm(_codePtr, X86_SUB, AMD64_RSP, locals); + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_R15, AMD64_R15); + foreach (IR::BasicBlock *block, function->basicBlocks) { _block = block; _addrs[block] = _codePtr; @@ -131,27 +137,29 @@ void InstructionSelection::visitFunction(IR::Function *function) } amd64_alu_reg_imm(_codePtr, X86_ADD, AMD64_RSP, locals); + amd64_pop_reg(_codePtr, AMD64_R15); amd64_pop_reg(_codePtr, AMD64_R14); + amd64_pop_reg(_codePtr, AMD64_RBP); amd64_ret(_codePtr); qSwap(_codePtr, codePtr); qSwap(_code, code); +#ifndef NO_UDIS86 static bool showCode = !qgetenv("SHOW_CODE").isNull(); if (showCode) { -#ifndef NO_UDIS86 ud_t ud_obj; ud_init(&ud_obj); ud_set_input_buffer(&ud_obj, code, codePtr - code); ud_set_mode(&ud_obj, 64); - ud_set_syntax(&ud_obj, UD_SYN_INTEL); + ud_set_syntax(&ud_obj, UD_SYN_ATT); while (ud_disassemble(&ud_obj)) { printf("\t%s\n", ud_insn_asm(&ud_obj)); } -#endif } +#endif void (*f)(Context *) = (void (*)(Context *)) code; @@ -159,7 +167,7 @@ void InstructionSelection::visitFunction(IR::Function *function) ctx->activation = Value::object(ctx, new (GC) ArgumentsObject); f(ctx); Value d; - ctx->activation.objectValue->get(String::get(ctx, QLatin1String("d")), &d); + ctx->activation.objectValue->get(identifier("d"), &d); __qmljs_to_string(ctx, &d, &d); qDebug() << qPrintable(d.stringValue->text()); } @@ -172,6 +180,16 @@ String *InstructionSelection::identifier(const QString &s) return id; } +int InstructionSelection::tempOffset(IR::Temp *t) +{ + return sizeof(Value) * (t->index - 1); +} + +void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) +{ + amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * (t->index - 1)); +} + void InstructionSelection::visitExp(IR::Exp *s) { // if (IR::Call *c = s->expr->asCall()) { @@ -226,22 +244,21 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Temp *t = s->source->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); - amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 8 + sizeof(Value) * (t->index - 1)); + loadTempAddress(AMD64_RDX, t); amd64_call_code(_codePtr, __qmljs_set_activation_property); return; } } else if (IR::Temp *t = s->target->asTemp()) { - const int offset = 8 + sizeof(Value) * (t->index - 1); if (IR::Name *n = s->source->asName()) { String *propertyName = identifier(*n->id); amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, offset); + loadTempAddress(AMD64_RSI, t); amd64_mov_reg_imm(_codePtr, AMD64_RDX, propertyName); amd64_call_code(_codePtr, __qmljs_get_activation_property); return; } else if (IR::Const *c = s->source->asConst()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, offset); + loadTempAddress(AMD64_RSI, t); switch (c->type) { case IR::BoolType: @@ -261,7 +278,7 @@ void InstructionSelection::visitMove(IR::Move *s) return; } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, offset); + loadTempAddress(AMD64_RSI, t); amd64_mov_reg_imm(_codePtr, AMD64_RDX, new (GC) String(*str->value)); amd64_call_code(_codePtr, __qmljs_init_string); return; @@ -270,9 +287,9 @@ void InstructionSelection::visitMove(IR::Move *s) IR::Temp *r = b->right->asTemp(); if (l && r) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, offset); - amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 8 + sizeof(Value) * (l->index - 1)); - amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 8 + sizeof(Value) * (r->index - 1)); + loadTempAddress(AMD64_RSI, t); + loadTempAddress(AMD64_RDX, l); + loadTempAddress(AMD64_RCX, r); void (*op)(Context *, Value *, const Value *, const Value *) = 0; @@ -334,7 +351,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) { if (IR::Temp *t = s->cond->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, 8 + sizeof(Value) * (t->index - 1)); + loadTempAddress(AMD64_RSI, t); amd64_call_code(_codePtr, __qmljs_to_boolean); amd64_alu_reg_imm_size(_codePtr, X86_CMP, X86_EAX, 0, 4); _patches[s->iftrue].append(_codePtr); diff --git a/qv4isel_p.h b/qv4isel_p.h index 6a1531b01a..dabc434a1f 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -18,6 +18,8 @@ public: protected: String *identifier(const QString &s); + int tempOffset(IR::Temp *t); + void loadTempAddress(int reg, IR::Temp *t); virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); -- cgit v1.2.3 From 50e7efb54c7c02578127646e72470b26e2b93c2a Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 4 May 2012 12:18:59 +0200 Subject: Use assert instead of Q_ASSERT. That is, at least for now we want the asserts also in release. --- qv4isel.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/qv4isel.cpp b/qv4isel.cpp index 9d05a8e0f7..78e67a14a0 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -12,6 +12,7 @@ typedef void *gpointer; #include "amd64-codegen.h" #include +#include #ifndef NO_UDIS86 # include @@ -97,7 +98,7 @@ InstructionSelection::~InstructionSelection() void InstructionSelection::visitFunction(IR::Function *function) { uchar *code = (uchar *) malloc(getpagesize()); - Q_ASSERT(! (size_t(code) & 15)); + assert(! (size_t(code) & 15)); protect(code, getpagesize()); @@ -195,17 +196,17 @@ void InstructionSelection::visitExp(IR::Exp *s) // if (IR::Call *c = s->expr->asCall()) { // return; // } - Q_ASSERT(!"TODO"); + assert(!"TODO"); } void InstructionSelection::visitEnter(IR::Enter *) { - Q_ASSERT(!"TODO"); + assert(!"TODO"); } void InstructionSelection::visitLeave(IR::Leave *) { - Q_ASSERT(!"TODO"); + assert(!"TODO"); } void InstructionSelection::visitMove(IR::Move *s) @@ -232,7 +233,7 @@ void InstructionSelection::visitMove(IR::Move *s) break; default: - Q_ASSERT(!"TODO"); + assert(!"TODO"); } return; } else if (IR::String *str = s->source->asString()) { @@ -273,7 +274,7 @@ void InstructionSelection::visitMove(IR::Move *s) break; default: - Q_ASSERT(!"TODO"); + assert(!"TODO"); } return; } else if (IR::String *str = s->source->asString()) { @@ -300,7 +301,7 @@ void InstructionSelection::visitMove(IR::Move *s) case QQmlJS::IR::OpUMinus: case QQmlJS::IR::OpUPlus: case QQmlJS::IR::OpCompl: - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); break; case QQmlJS::IR::OpBitAnd: op = __qmljs_bit_and; break; @@ -325,7 +326,7 @@ void InstructionSelection::visitMove(IR::Move *s) case QQmlJS::IR::OpAnd: case QQmlJS::IR::OpOr: - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); break; } amd64_call_code(_codePtr, op); @@ -336,7 +337,7 @@ void InstructionSelection::visitMove(IR::Move *s) } else { // inplace assignment, e.g. x += 1, ++x, ... } - Q_ASSERT(!"TODO"); + assert(!"TODO"); } void InstructionSelection::visitJump(IR::Jump *s) @@ -363,12 +364,12 @@ void InstructionSelection::visitCJump(IR::CJump *s) } return; } - Q_ASSERT(!"TODO"); + assert(!"TODO"); } void InstructionSelection::visitRet(IR::Ret *s) { qWarning() << "TODO: RET"; - //Q_ASSERT(!"TODO"); + //assert(!"TODO"); } -- cgit v1.2.3 From 59c7dad30755a18d74f23ac644e16c0f7d39fbe4 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 4 May 2012 13:03:14 +0200 Subject: Add copy instructions to the qmljs runtime --- qmljs_runtime.cpp | 12 ++++++++++++ qmljs_runtime.h | 2 ++ qv4isel.cpp | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index c6a2dd990a..cc811c706c 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -189,6 +189,11 @@ void __qmljs_set_activation_property(Context *ctx, String *name, Value *value) __qmljs_set_property(ctx, &ctx->activation, name, value); } +void __qmljs_copy_activation_property(Context *ctx, String *name, String *other) +{ + __qmljs_copy_property(ctx, &ctx->activation, name, &ctx->activation, other); +} + void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool value) { __qmljs_set_property_boolean(ctx, &ctx->activation, name, value); @@ -225,6 +230,13 @@ void __qmljs_get_thisObject(Context *ctx, Value *result) *result = ctx->thisObject; } +void __qmljs_copy_property(Context *ctx, Value *target, String *name, Value *source, String *other) +{ + Value v; + source->objectValue->get(other, &v); + target->objectValue->put(name, v); +} + void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y, bool leftFirst) { Value px, py; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index f31f54cc17..18d0c61af4 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -80,6 +80,8 @@ void __qmljs_set_activation_property_number(Context *ctx, String *name, double v void __qmljs_set_activation_property_string(Context *ctx, String *name, String *value); void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name); void __qmljs_get_activation_property(Context *ctx, Value *result, String *name); +void __qmljs_copy_activation_property(Context *ctx, String *name, String *other); +void __qmljs_copy_property(Context *ctx, Value *target, String *name, Value *source, String *other); // context void __qmljs_get_activation(Context *ctx, Value *result); diff --git a/qv4isel.cpp b/qv4isel.cpp index 78e67a14a0..6c25b6660e 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -248,6 +248,12 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RDX, t); amd64_call_code(_codePtr, __qmljs_set_activation_property); return; + } else if (IR::Name *other = s->source->asName()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*other->id)); + amd64_call_code(_codePtr, __qmljs_copy_activation_property); + return; } } else if (IR::Temp *t = s->target->asTemp()) { if (IR::Name *n = s->source->asName()) { -- cgit v1.2.3 From 63f02b9ad2562e91f838618cd6c11e834addbf7c Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 4 May 2012 13:13:32 +0200 Subject: Add tests for the for-statement. --- tests/for/for.1.js | 7 +++++++ tests/for/for.2.js | 8 ++++++++ tests/for/for.3.js | 8 ++++++++ 3 files changed, 23 insertions(+) create mode 100644 tests/for/for.1.js create mode 100644 tests/for/for.2.js create mode 100644 tests/for/for.3.js diff --git a/tests/for/for.1.js b/tests/for/for.1.js new file mode 100644 index 0000000000..523938b308 --- /dev/null +++ b/tests/for/for.1.js @@ -0,0 +1,7 @@ + +var d = "" +var x = 0 +for (var i = 0; i < 20; i = i + 1) { + x = x + i + d = d + (i + ") x = " + x + "\n") +} diff --git a/tests/for/for.2.js b/tests/for/for.2.js new file mode 100644 index 0000000000..0ef80e85e9 --- /dev/null +++ b/tests/for/for.2.js @@ -0,0 +1,8 @@ +var d = "" +var x = 0 +for (var i = 0; i < 20; i = i + 1) { + if (i % 2) + continue + x = x + i + d = d + (i + ") x = " + x + "\n") +} diff --git a/tests/for/for.3.js b/tests/for/for.3.js new file mode 100644 index 0000000000..bdc641db13 --- /dev/null +++ b/tests/for/for.3.js @@ -0,0 +1,8 @@ +var d = "" +var x = 0 +for (var i = 0; i < 20; i = i + 1) { + if (i == 10) + break + x = x + i + d = d + (i + ") x = " + x + "\n") +} -- cgit v1.2.3 From 663a045cc2382e9e9793c4bcaadba608b2a1e011 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 4 May 2012 13:43:22 +0200 Subject: Generate code for unary expressions. --- qv4codegen.cpp | 10 +++------- qv4isel.cpp | 18 ++++++++++++++++++ tests/for/for.4.js | 8 ++++++++ 3 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 tests/for/for.4.js diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 52e99171a7..b0e318de63 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1133,13 +1133,9 @@ bool Codegen::visit(NewMemberExpression *ast) bool Codegen::visit(NotExpression *ast) { Result expr = expression(ast->expression); - if (_expr.accept(cx)) { - _block->CJUMP(_block->UNOP(IR::OpNot, *expr), _expr.iftrue, _expr.iffalse); - } else { - const unsigned r = _block->newTemp(); - _block->MOVE(_block->TEMP(r), _block->UNOP(IR::OpNot, *expr)); - _expr.code = _block->TEMP(r); - } + const unsigned r = _block->newTemp(); + _block->MOVE(_block->TEMP(r), _block->UNOP(IR::OpNot, *expr)); + _expr.code = _block->TEMP(r); return false; } diff --git a/qv4isel.cpp b/qv4isel.cpp index 6c25b6660e..f3c4c6602b 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -289,6 +289,24 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_imm(_codePtr, AMD64_RDX, new (GC) String(*str->value)); amd64_call_code(_codePtr, __qmljs_init_string); return; + } else if (IR::Unop *u = s->source->asUnop()) { + if (IR::Temp *e = u->expr->asTemp()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + loadTempAddress(AMD64_RDX, e); + void (*op)(Context *, Value *, const Value *) = 0; + switch (u->op) { + case QQmlJS::IR::OpIfTrue: assert(!"unreachable"); break; + case QQmlJS::IR::OpNot: op = __qmljs_not; break; + case QQmlJS::IR::OpUMinus: op = __qmljs_uminus; break; + case QQmlJS::IR::OpUPlus: op = __qmljs_uplus; break; + case QQmlJS::IR::OpCompl: op = __qmljs_compl; break; + default: assert(!"unreachable"); break; + } // switch + amd64_call_code(_codePtr, op); + return; + } + } else if (IR::Binop *b = s->source->asBinop()) { IR::Temp *l = b->left->asTemp(); IR::Temp *r = b->right->asTemp(); diff --git a/tests/for/for.4.js b/tests/for/for.4.js new file mode 100644 index 0000000000..cf66fd00f0 --- /dev/null +++ b/tests/for/for.4.js @@ -0,0 +1,8 @@ +var d = "" +var x = 0 +for (var i = 0; i < 20; i = i + 1) { + if (! (i % 2)) + continue + x = x + i + d = d + (i + ") x = " + x + "\n") +} -- cgit v1.2.3 From 83d6ed271a02bb7233c056ec953d5e7d83da2e99 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 4 May 2012 13:48:57 +0200 Subject: Make the simple test more interesting --- tests/simple.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/simple.js b/tests/simple.js index 30408c20f3..6562f63bf3 100644 --- a/tests/simple.js +++ b/tests/simple.js @@ -6,7 +6,7 @@ var d = 100 for (i = 0; i < 1000000; i = i + 1) { if (a == 1) - d = a + b * c + d = d + a + b * c else d = 321 } -- cgit v1.2.3 From 1318bda1b24914bd49c4a82d3d89375e492342fd Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 4 May 2012 15:12:37 +0200 Subject: Remove boehm GC --- main.cpp | 2 -- qmljs_objects.cpp | 24 +++++++++---- qmljs_objects.h | 105 +++++++++++++++++++++++++++--------------------------- qmljs_runtime.cpp | 14 +++++--- qv4isel.cpp | 26 ++++++++------ v4.pro | 2 -- 6 files changed, 95 insertions(+), 78 deletions(-) diff --git a/main.cpp b/main.cpp index e5064b080f..f16eec2561 100644 --- a/main.cpp +++ b/main.cpp @@ -14,8 +14,6 @@ int main(int argc, char *argv[]) { using namespace QQmlJS; - GC_INIT(); - QCoreApplication app(argc, argv); QStringList args = app.arguments(); args.removeFirst(); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 580d6e512b..e15c000f73 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -2,6 +2,11 @@ #include "qmljs_objects.h" #include +Object::~Object() +{ + delete members; +} + bool Object::get(String *name, Value *result) { if (Property *prop = getProperty(name)) { @@ -34,19 +39,22 @@ Property *Object::getProperty(String *name) void Object::put(String *name, const Value &value, bool flag) { + Q_UNUSED(flag); + if (! members) - members = new (GC) Table(); + members = new Table(); members->insert(name, value); } bool Object::canPut(String *name) { - if (Property *prop = getOwnProperty(name)) + if (Property *prop = getOwnProperty(name)) { + Q_UNUSED(prop); return true; - if (! prototype) + } else if (! prototype) { return extensible; - if (Property *inherited = prototype->getProperty(name)) { + } else if (Property *inherited = prototype->getProperty(name)) { return inherited->isWritable(); } else { return extensible; @@ -64,6 +72,8 @@ bool Object::hasProperty(String *name) const bool Object::deleteProperty(String *name, bool flag) { + Q_UNUSED(flag); + if (members) return members->remove(name); @@ -93,7 +103,9 @@ bool FunctionObject::hasInstance(const Value &value) const Value FunctionObject::call(const Value &thisObject, const Value args[], unsigned argc) { - (void) thisObject; + Q_UNUSED(thisObject); + Q_UNUSED(args); + Q_UNUSED(argc); Value v; __qmljs_init_undefined(0, &v); @@ -103,7 +115,7 @@ Value FunctionObject::call(const Value &thisObject, const Value args[], unsigned Value FunctionObject::construct(const Value args[], unsigned argc) { Value thisObject; - __qmljs_init_object(0, &thisObject, new (GC) Object); + __qmljs_init_object(0, &thisObject, new Object); call(thisObject, args, argc); return thisObject; } diff --git a/qmljs_objects.h b/qmljs_objects.h index 972ef4a593..19450483ae 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -3,9 +3,9 @@ #include "qmljs_runtime.h" -#include #include #include +#include struct Value; struct Object; @@ -16,7 +16,7 @@ struct ArrayObject; struct FunctionObject; struct ErrorObject; -struct String: gc_cleanup { +struct String { String(const QString &text) : _text(text), _hashValue(0) {} @@ -32,7 +32,8 @@ struct String: gc_cleanup { } static String *get(Context *ctx, const QString &s) { - return new (GC) String(s); + Q_UNUSED(ctx); + return new String(s); } private: @@ -40,22 +41,26 @@ private: mutable unsigned _hashValue; }; -struct Property: gc { +struct Property { String *name; Value value; PropertyAttributes flags; Property *next; + int index; Property(String *name, const Value &value, PropertyAttributes flags = NoAttributes) - : name(name), value(value), flags(flags), next(0) {} + : name(name), value(value), flags(flags), next(0), index(-1) {} inline bool isWritable() const { return flags & WritableAttribute; } inline bool isEnumerable() const { return flags & EnumerableAttribute; } inline bool isConfigurable() const { return flags & ConfigurableAttribute; } inline bool hasName(String *n) const { - if (name == n || (name->hashValue() == n->hashValue() && name->text() == n->text())) + if (name == n) { return true; + } else if (name->hashValue() == n->hashValue() && name->text() == n->text()) { + return true; + } return false; } @@ -64,13 +69,9 @@ struct Property: gc { } }; -class Table: public gc +class Table { - Property **_properties; - Property **_buckets; - int _propertyCount; - int _bucketCount; - int _allocated; + Q_DISABLE_COPY(Table) public: Table() @@ -80,47 +81,33 @@ public: , _bucketCount(11) , _allocated(0) {} - bool empty() const { return _propertyCount == -1; } - unsigned size() const { return _propertyCount + 1; } + ~Table() + { + qDeleteAll(_properties, _properties + _propertyCount + 1); + delete[] _properties; + delete[] _buckets; + } + + inline bool isEmpty() const { return _propertyCount == -1; } typedef Property **iterator; - iterator begin() const { return _properties; } - iterator end() const { return _properties + (_propertyCount + 1); } + inline iterator begin() const { return _properties; } + inline iterator end() const { return _properties + (_propertyCount + 1); } bool remove(String *name) { - if (_properties) { - const unsigned hash = name->hashValue() % _bucketCount; - - if (Property *prop = _buckets[hash]) { - if (prop->hasName(name)) { - _buckets[hash] = prop->next; - return true; - } - - do { - Property *next = prop->next; - - if (next && next->hasName(name)) { - prop->next = next->next; - return true; - } - prop = next; - } while (prop); - } - } - + Q_UNUSED(name); + assert(!"TODO"); return false; } Property *find(String *name) const { - if (! _properties) - return 0; - - for (Property *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { - if (prop->hasName(name)) - return prop; + if (_properties) { + for (Property *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { + if (prop->hasName(name)) + return prop; + } } return 0; @@ -139,12 +126,14 @@ public: else _allocated *= 2; - Property **properties = new (GC) Property*[_allocated]; + Property **properties = new Property*[_allocated]; std::copy(_properties, _properties + _propertyCount, properties); + delete[] _properties; _properties = properties; } - Property *prop = new (GC) Property(name, value); + Property *prop = new Property(name, value); + prop->index = _propertyCount; _properties[_propertyCount] = prop; if (! _buckets || 3 * _propertyCount >= 2 * _bucketCount) { @@ -164,19 +153,29 @@ private: if (_bucketCount) _bucketCount *= 2; // ### next prime - _buckets = new (GC) Property *[_bucketCount]; + if (_buckets) + delete[] _buckets; + + _buckets = new Property *[_bucketCount]; std::fill(_buckets, _buckets + _bucketCount, (Property *) 0); for (int i = 0; i <= _propertyCount; ++i) { Property *prop = _properties[i]; - Property *&bucket = _buckets[prop->name->hashValue() % _bucketCount]; + Property *&bucket = _buckets[prop->hashValue() % _bucketCount]; prop->next = bucket; bucket = prop; } } + +private: + Property **_properties; + Property **_buckets; + int _propertyCount; + int _bucketCount; + int _allocated; }; -struct Object: gc_cleanup { +struct Object { Object *prototype; String *klass; Table *members; @@ -188,7 +187,7 @@ struct Object: gc_cleanup { , members(0) , extensible(true) {} - virtual ~Object() {} + virtual ~Object(); virtual FunctionObject *asFunctionObject() { return 0; } @@ -206,19 +205,19 @@ struct Object: gc_cleanup { struct BooleanObject: Object { Value value; BooleanObject(const Value &value): value(value) {} - virtual void defaultValue(Value *result, int typehint) { *result = value; } + virtual void defaultValue(Value *result, int /*typehint*/) { *result = value; } }; struct NumberObject: Object { Value value; NumberObject(const Value &value): value(value) {} - virtual void defaultValue(Value *result, int typehint) { *result = value; } + virtual void defaultValue(Value *result, int /*typehint*/) { *result = value; } }; struct StringObject: Object { Value value; StringObject(const Value &value): value(value) {} - virtual void defaultValue(Value *result, int typehint) { *result = value; } + virtual void defaultValue(Value *result, int /*typehint*/) { *result = value; } }; struct ArrayObject: Object { @@ -244,7 +243,7 @@ struct ErrorObject: Object { struct ArgumentsObject: Object { }; -struct Context: gc { +struct Context { Value activation; Value thisObject; Object *scope; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index cc811c706c..3e4f6a5da6 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -58,9 +58,11 @@ void __qmljs_string_literal_function(Context *ctx, Value *result) void __qmljs_delete(Context *ctx, Value *result, const Value *value) { + Q_UNIMPLEMENTED(); (void) ctx; (void) result; (void) value; + assert(!"TODO"); } void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Value *right) @@ -129,37 +131,39 @@ bool __qmljs_is_function(Context *, const Value *value) void __qmljs_object_default_value(Context *ctx, Value *result, Object *object, int typeHint) { + Q_UNUSED(ctx); object->defaultValue(result, typeHint); } void __qmljs_throw_type_error(Context *ctx, Value *result) { - __qmljs_init_object(ctx, result, new (GC) ErrorObject(String::get(ctx, QLatin1String("type error")))); + __qmljs_init_object(ctx, result, new ErrorObject(String::get(ctx, QLatin1String("type error")))); } void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean) { Value value; __qmljs_init_boolean(ctx, &value, boolean); - __qmljs_init_object(ctx, result, new (GC) BooleanObject(value)); + __qmljs_init_object(ctx, result, new BooleanObject(value)); } void __qmljs_new_number_object(Context *ctx, Value *result, double number) { Value value; __qmljs_init_number(ctx, &value, number); - __qmljs_init_object(ctx, result, new (GC) NumberObject(value)); + __qmljs_init_object(ctx, result, new NumberObject(value)); } void __qmljs_new_string_object(Context *ctx, Value *result, String *string) { Value value; __qmljs_init_string(ctx, &value, string); - __qmljs_init_object(ctx, result, new (GC) StringObject(value)); + __qmljs_init_object(ctx, result, new StringObject(value)); } void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value) { + Q_UNUSED(ctx); object->objectValue->put(name, *value, /*flags*/ 0); } @@ -211,6 +215,7 @@ void __qmljs_set_activation_property_string(Context *ctx, String *name, String * void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name) { + Q_UNUSED(ctx); Q_ASSERT(object->type == OBJECT_TYPE); object->objectValue->get(name, result); } @@ -232,6 +237,7 @@ void __qmljs_get_thisObject(Context *ctx, Value *result) void __qmljs_copy_property(Context *ctx, Value *target, String *name, Value *source, String *other) { + Q_UNUSED(ctx); Value v; source->objectValue->get(other, &v); target->objectValue->put(name, v); diff --git a/qv4isel.cpp b/qv4isel.cpp index f3c4c6602b..94f3d2cbb5 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -164,8 +164,8 @@ void InstructionSelection::visitFunction(IR::Function *function) void (*f)(Context *) = (void (*)(Context *)) code; - Context *ctx = new (GC) Context; - ctx->activation = Value::object(ctx, new (GC) ArgumentsObject); + Context *ctx = new Context; + ctx->activation = Value::object(ctx, new ArgumentsObject); f(ctx); Value d; ctx->activation.objectValue->get(identifier("d"), &d); @@ -177,7 +177,7 @@ String *InstructionSelection::identifier(const QString &s) { String *&id = _identifiers[s]; if (! id) - id = new (GC) String(s); + id = new String(s); return id; } @@ -191,21 +191,21 @@ void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * (t->index - 1)); } -void InstructionSelection::visitExp(IR::Exp *s) +void InstructionSelection::visitExp(IR::Exp *) { - // if (IR::Call *c = s->expr->asCall()) { - // return; - // } + Q_UNIMPLEMENTED(); assert(!"TODO"); } void InstructionSelection::visitEnter(IR::Enter *) { + Q_UNIMPLEMENTED(); assert(!"TODO"); } void InstructionSelection::visitLeave(IR::Leave *) { + Q_UNIMPLEMENTED(); assert(!"TODO"); } @@ -233,13 +233,14 @@ void InstructionSelection::visitMove(IR::Move *s) break; default: + Q_UNIMPLEMENTED(); assert(!"TODO"); } return; } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, new (GC) String(*str->value)); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, new String(*str->value)); amd64_call_code(_codePtr, __qmljs_set_activation_property_string); return; } else if (IR::Temp *t = s->source->asTemp()) { @@ -280,13 +281,14 @@ void InstructionSelection::visitMove(IR::Move *s) break; default: + Q_UNIMPLEMENTED(); assert(!"TODO"); } return; } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, new (GC) String(*str->value)); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, new String(*str->value)); amd64_call_code(_codePtr, __qmljs_init_string); return; } else if (IR::Unop *u = s->source->asUnop()) { @@ -361,6 +363,7 @@ void InstructionSelection::visitMove(IR::Move *s) } else { // inplace assignment, e.g. x += 1, ++x, ... } + Q_UNIMPLEMENTED(); assert(!"TODO"); } @@ -388,12 +391,13 @@ void InstructionSelection::visitCJump(IR::CJump *s) } return; } + Q_UNIMPLEMENTED(); assert(!"TODO"); } void InstructionSelection::visitRet(IR::Ret *s) { - qWarning() << "TODO: RET"; - //assert(!"TODO"); + Q_UNIMPLEMENTED(); + Q_UNUSED(s); } diff --git a/v4.pro b/v4.pro index 58571124c0..b50ea3e8ad 100644 --- a/v4.pro +++ b/v4.pro @@ -4,8 +4,6 @@ CONFIG += console DEFINES += __default_codegen__ -LIBS += -lgc - udis86:LIBS += -ludis86 else:DEFINES += NO_UDIS86 -- cgit v1.2.3 From 81825142d4df0392b781f24272aa934a750bc811 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 4 May 2012 15:28:04 +0200 Subject: Put the code in namespaces. --- qmljs_objects.cpp | 2 ++ qmljs_objects.h | 6 ++++++ qmljs_runtime.cpp | 2 ++ qmljs_runtime.h | 6 ++++++ qv4codegen.cpp | 2 +- qv4isel.cpp | 2 ++ qv4isel_p.h | 6 ++++-- 7 files changed, 23 insertions(+), 3 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index e15c000f73..57332aba69 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -2,6 +2,8 @@ #include "qmljs_objects.h" #include +using namespace QQmlJS::VM; + Object::~Object() { delete members; diff --git a/qmljs_objects.h b/qmljs_objects.h index 19450483ae..cabdf5c124 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -7,6 +7,9 @@ #include #include +namespace QQmlJS { +namespace VM { + struct Value; struct Object; struct BooleanObject; @@ -260,4 +263,7 @@ struct Context { } }; +} // namespace VM +} // namespace QQmlJS + #endif // QMLJS_OBJECTS_H diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 3e4f6a5da6..ee7384efcb 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -4,6 +4,8 @@ #include #include +using namespace QQmlJS::VM; + Value Value::string(Context *ctx, const QString &s) { return string(ctx, String::get(ctx, s)); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 18d0c61af4..349d714fbc 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -5,6 +5,9 @@ #include #include +namespace QQmlJS { +namespace VM { + enum ValueType { UNDEFINED_TYPE, NULL_TYPE, @@ -624,4 +627,7 @@ inline bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y) } // extern "C" +} // namespace VM +} // namespace QQmlJS + #endif // QMLJS_RUNTIME_H diff --git a/qv4codegen.cpp b/qv4codegen.cpp index b0e318de63..3b176ef11e 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -409,7 +409,7 @@ void Codegen::operator()(AST::Program *node) _block->JUMP(_exitBlock); } - InstructionSelection isel(_module); + x86_64::InstructionSelection isel(_module); foreach (IR::Function *function, _module->functions) { linearize(function); diff --git a/qv4isel.cpp b/qv4isel.cpp index 94f3d2cbb5..e68569ebc0 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -19,6 +19,8 @@ typedef void *gpointer; #endif using namespace QQmlJS; +using namespace QQmlJS::x86_64; +using namespace QQmlJS::VM; static inline bool protect(const void *addr, size_t size) { diff --git a/qv4isel_p.h b/qv4isel_p.h index dabc434a1f..9c85c19c87 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -7,6 +7,7 @@ #include namespace QQmlJS { +namespace x86_64 { class InstructionSelection: protected IR::StmtVisitor { @@ -17,7 +18,7 @@ public: void visitFunction(IR::Function *function); protected: - String *identifier(const QString &s); + VM::String *identifier(const QString &s); int tempOffset(IR::Temp *t); void loadTempAddress(int reg, IR::Temp *t); @@ -37,9 +38,10 @@ private: uchar *_codePtr; QHash > _patches; QHash _addrs; - QHash _identifiers; + QHash _identifiers; }; +} // end of namespace x86_64 } // end of namespace QQmlJS #endif // QV4ISEL_P_H -- cgit v1.2.3 From ee7024a86ce01a6dbdc6c105e17147a25cf44b82 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 4 May 2012 20:22:04 +0200 Subject: Compile --- qmljs_runtime.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index ee7384efcb..f95060e61f 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -4,7 +4,8 @@ #include #include -using namespace QQmlJS::VM; +namespace QQmlJS { +namespace VM { Value Value::string(Context *ctx, const QString &s) { @@ -350,3 +351,7 @@ void __qmjs_construct(Context *ctx, Value *result, const Value *function, const } // extern "C" + + +} // namespace VM +} // namespace QQmlJS -- cgit v1.2.3 From 6d0e807ebf6cfc147490b4b71f4de7a1f253fd45 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 7 May 2012 10:45:58 +0200 Subject: Simplify liveness. --- qmljs_runtime.h | 5 ++ qv4codegen.cpp | 162 +++++++++++--------------------------------------------- qv4ir.cpp | 8 +-- qv4ir_p.h | 25 ++++++--- 4 files changed, 59 insertions(+), 141 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 349d714fbc..d295c3fca2 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -221,6 +221,11 @@ inline void __qmljs_init_object(Context *, Value *result, Object *object) result->objectValue = object; } +inline void __qmljs_copy(Value *result, Value *source) +{ + result->type = source->type; + result->numberValue = source->numberValue; +} // type conversion and testing inline void __qmljs_to_primitive(Context *ctx, Value *result, const Value *value, int typeHint) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 3b176ef11e..3407ed3c10 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -14,105 +15,6 @@ using namespace AST; namespace { QTextStream qout(stdout, QIODevice::WriteOnly); -class IntSet -{ - IntSet(const IntSet &other); - IntSet &operator = (const IntSet &other); - -public: - unsigned *info; - unsigned *check; - unsigned count; - unsigned size; - - IntSet(unsigned size = 32) - : info(new unsigned[size]) - , check(new unsigned[size]) - , count(0) - , size(size) - { - } - - ~IntSet() { - delete[] info; - delete[] check; - } - - typedef const unsigned *const_iterator; - typedef const_iterator iterator; - const_iterator begin() const { return info; } - const_iterator end() const { return info + count; } - - bool empty() const { return count == 0; } - void clear() { count = 0; } - - inline void insert(unsigned value) { - if (! contains(value)) { - info[count] = value; - check[value] = count; - ++count; - } - } - - inline void remove(unsigned value) { - const unsigned index = check[value]; - if (index < count && info[index] == value) { - if (--count) { - const int v = info[count]; - check[v] = index; - info[index] = v; - } - } - } - - inline bool contains(unsigned value) const { - const unsigned index = check[value]; - return index < count && info[index] == value; - } - - template - void insert(_It first, _It last) { - for (; first != last; ++first) - insert(*first); - } - - template - void remove(_It first, _It last) { - for (; first != last; ++first) - remove(*first); - } - - void insert(const IntSet &other) { - insert(other.begin(), other.end()); - } - - bool isEqualTo(const unsigned *it, unsigned length) const { - if (count == length) { - for (unsigned i = 0; i < length; ++i) { - if (! contains(it[i])) - return false; - } - return true; - } - return false; - } - - bool operator == (const IntSet &other) const { - if (count == other.count) { - for (unsigned i = 0; i < count; ++i) { - if (! other.contains(info[i])) - return false; - } - return true; - } - return false; - } - - bool operator != (const IntSet &other) const { - return ! operator == (other); - } -}; - void edge(IR::BasicBlock *source, IR::BasicBlock *target) { if (! source->out.contains(target)) @@ -146,6 +48,8 @@ struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor , _stmt(0) {} void operator()(IR::Stmt *s) { + Q_ASSERT(! s->d); + s->d = new IR::Stmt::Data; qSwap(_stmt, s); _stmt->accept(this); qSwap(_stmt, s); @@ -167,8 +71,8 @@ struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } virtual void visitTemp(IR::Temp *e) { - if (! _stmt->uses.contains(e->index)) - _stmt->uses.append(e->index); + if (! _stmt->d->uses.contains(e->index)) + _stmt->d->uses.append(e->index); } virtual void visitCall(IR::Call *e) { @@ -185,8 +89,8 @@ struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor virtual void visitMove(IR::Move *s) { if (IR::Temp *t = s->target->asTemp()) { - if (! _stmt->defs.contains(t->index)) - _stmt->defs.append(t->index); + if (! _stmt->d->defs.contains(t->index)) + _stmt->d->defs.append(t->index); } else { s->target->accept(this); } @@ -255,7 +159,7 @@ struct RegAlloc: IR::StmtVisitor, IR::ExprVisitor virtual void visitTemp(IR::Temp *e) { const unsigned virtualRegister = e->index; e->index = getReg(e); - if (! _stmt->liveOut.contains(virtualRegister)) + if (! _stmt->d->liveOut.testBit(virtualRegister)) _kill.insert(virtualRegister); } @@ -317,37 +221,30 @@ void liveness(IR::Function *function) dfs(function->basicBlocks.first(), &V, &blocks); - IntSet liveOut(function->tempCount); bool changed; - do { changed = false; foreach (IR::BasicBlock *block, blocks) { - liveOut.clear(); - + const QBitArray previousLiveIn = block->liveIn; + const QBitArray previousLiveOut = block->liveOut; + QBitArray live(function->tempCount); + block->liveOut = live; foreach (IR::BasicBlock *succ, block->out) - liveOut.insert(succ->liveIn.begin(), succ->liveIn.end()); - - if (block->out.isEmpty() || liveOut.empty() || ! liveOut.isEqualTo(block->liveOut.constData(), block->liveOut.size())) { - block->liveOut.resize(liveOut.count); - qCopy(liveOut.begin(), liveOut.end(), block->liveOut.begin()); - - for (int i = block->statements.size() - 1; i != -1; --i) { - IR::Stmt *stmt = block->statements.at(i); - stmt->liveOut.resize(liveOut.count); - qCopy(liveOut.begin(), liveOut.end(), stmt->liveOut.begin()); - liveOut.remove(stmt->defs.begin(), stmt->defs.end()); - liveOut.insert(stmt->uses.begin(), stmt->uses.end()); - stmt->liveIn.resize(liveOut.count); - qCopy(liveOut.begin(), liveOut.end(), stmt->liveIn.begin()); - } - - if (! changed && ! liveOut.isEqualTo(block->liveIn.constData(), block->liveIn.size())) + live |= succ->liveIn; + for (int i = block->statements.size() - 1; i != -1; --i) { + IR::Stmt *s = block->statements.at(i); + s->d->liveOut = live; + foreach (unsigned d, s->d->defs) + live.clearBit(d); + foreach (unsigned u, s->d->uses) + live.setBit(u); + s->d->liveIn = live; + } + block->liveIn = live; + if (! changed) { + if (previousLiveIn != block->liveIn || previousLiveOut != block->liveOut) changed = true; - - block->liveIn.resize(liveOut.count); - qCopy(liveOut.begin(), liveOut.end(), block->liveIn.begin()); } } } while (changed); @@ -1340,7 +1237,7 @@ void Codegen::linearize(IR::Function *function) liveness(function); - { + if (1) { RegAlloc regalloc; foreach (IR::BasicBlock *block, function->basicBlocks) { foreach (IR::Stmt *s, block->statements) @@ -1385,7 +1282,7 @@ void Codegen::linearize(IR::Function *function) if (IR::BasicBlock *bb = leader.value(s)) { qout << endl; - qout << 'L' << bb << ':' << endl; + qout << 'L' << bb->index << ':' << endl; } IR::Stmt *n = (i + 1) < code.size() ? code.at(i + 1) : 0; if (n && s->asJump() && s->asJump()->target == leader.value(n)) { @@ -1435,6 +1332,11 @@ void Codegen::linearize(IR::Function *function) qout << "}" << endl << endl; } + + foreach (IR::BasicBlock *block, function->basicBlocks) { + foreach (IR::Stmt *s, block->statements) + s->destroyData(); + } } void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) diff --git a/qv4ir.cpp b/qv4ir.cpp index e3a15118cc..5d55fe5396 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -427,7 +427,7 @@ void Move::dump(QTextStream &out, Mode) void Jump::dump(QTextStream &out, Mode mode) { Q_UNUSED(mode); - out << "goto " << 'L' << target << ';'; + out << "goto " << 'L' << target->index << ';'; } void CJump::dump(QTextStream &out, Mode mode) @@ -436,9 +436,9 @@ void CJump::dump(QTextStream &out, Mode mode) out << "if ("; cond->dump(out); if (mode == HIR) - out << ") goto " << 'L' << iftrue << "; else goto " << 'L' << iffalse << ';'; + out << ") goto " << 'L' << iftrue->index << "; else goto " << 'L' << iffalse->index << ';'; else - out << ") goto " << 'L' << iftrue << ";"; + out << ") goto " << 'L' << iftrue->index << ";"; } void Ret::dump(QTextStream &out, Mode) @@ -683,7 +683,7 @@ Stmt *BasicBlock::RET(Expr *expr, Type type) void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) { - out << 'L' << this << ':' << endl; + out << 'L' << index << ':' << endl; foreach (Stmt *s, statements) { out << '\t'; s->dump(out, mode); diff --git a/qv4ir_p.h b/qv4ir_p.h index dbb327692c..a606563df0 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -57,6 +57,7 @@ #include #include +#include QT_BEGIN_HEADER @@ -430,12 +431,17 @@ struct Stmt { MIR }; - QVector uses; - QVector defs; - QVector liveIn; - QVector liveOut; + struct Data { + QVector uses; + QVector defs; + QBitArray liveIn; + QBitArray liveOut; + }; + + Data *d; - virtual ~Stmt() {} + Stmt(): d(0) {} + virtual ~Stmt() { Q_UNREACHABLE(); } virtual Stmt *asTerminator() { return 0; } virtual void accept(StmtVisitor *) = 0; @@ -447,6 +453,11 @@ struct Stmt { virtual CJump *asCJump() { return 0; } virtual Ret *asRet() { return 0; } virtual void dump(QTextStream &out, Mode mode = HIR) = 0; + + void destroyData() { + delete d; + d = 0; + } }; struct Exp: Stmt { @@ -598,8 +609,8 @@ struct BasicBlock { QVector statements; QVector in; QVector out; - QVector liveIn; - QVector liveOut; + QBitArray liveIn; + QBitArray liveOut; int index; int offset; -- cgit v1.2.3 From da1231f7268903086acdffd6787ad40b83ba3831 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 7 May 2012 14:14:43 +0200 Subject: Generate code for print calls. --- qv4isel.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- tests/simple.js | 2 +- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/qv4isel.cpp b/qv4isel.cpp index e68569ebc0..7c6e216b21 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -12,6 +12,7 @@ typedef void *gpointer; #include "amd64-codegen.h" #include +#include #include #ifndef NO_UDIS86 @@ -169,10 +170,6 @@ void InstructionSelection::visitFunction(IR::Function *function) Context *ctx = new Context; ctx->activation = Value::object(ctx, new ArgumentsObject); f(ctx); - Value d; - ctx->activation.objectValue->get(identifier("d"), &d); - __qmljs_to_string(ctx, &d, &d); - qDebug() << qPrintable(d.stringValue->text()); } String *InstructionSelection::identifier(const QString &s) @@ -193,8 +190,52 @@ void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * (t->index - 1)); } -void InstructionSelection::visitExp(IR::Exp *) +void __qmljs_print(Context *ctx, const Value *args, size_t argc) { + for (int i = 0; i < argc; ++i) { + Value v; + __qmljs_to_string(ctx, &v, &args[i]); + if (i) + std::cout << ' '; + std::cout << qPrintable(v.stringValue->text()); + } + std::cout << std::endl; +} + +void InstructionSelection::visitExp(IR::Exp *s) +{ + if (IR::Call *c = s->expr->asCall()) { + IR::Name *n = c->base->asName(); + if (n && *n->id == QLatin1String("print")) { + int argc = 0; + for (IR::ExprList *it = c->args; it; it = it->next) + ++argc; + amd64_mov_reg_imm(_codePtr, AMD64_RDI, argc * sizeof(Value)); + amd64_mov_reg_imm(_codePtr, AMD64_RAX, malloc); + amd64_call_reg(_codePtr, AMD64_RAX); + amd64_mov_reg_reg(_codePtr, AMD64_R15, AMD64_RAX, 8); + + argc = 0; + for (IR::ExprList *it = c->args; it; it = it->next) { + IR::Temp *t = it->expr->asTemp(); + Q_ASSERT(t != 0); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_R15, argc * sizeof(Value)); + loadTempAddress(AMD64_RSI, t); + amd64_call_code(_codePtr, __qmljs_copy); + ++argc; + } + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_mov_reg_reg(_codePtr, AMD64_RSI, AMD64_R15, 8); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, argc); + amd64_call_code(_codePtr, __qmljs_print); + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); + amd64_mov_reg_imm(_codePtr, AMD64_RAX, free); + amd64_call_reg(_codePtr, AMD64_RAX); + return; + } + } Q_UNIMPLEMENTED(); assert(!"TODO"); } diff --git a/tests/simple.js b/tests/simple.js index 6562f63bf3..e0a3984a3f 100644 --- a/tests/simple.js +++ b/tests/simple.js @@ -11,4 +11,4 @@ for (i = 0; i < 1000000; i = i + 1) { d = 321 } -d = "the result is " + d +print("the result is", d) -- cgit v1.2.3 From 0103f7bd2d2797dafef27e62ca3c63f25c4b9dfe Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 7 May 2012 14:46:37 +0200 Subject: Move the instruction selection pass. --- main.cpp | 32 +++++++++++++++++++++++++++++++- qv4codegen.cpp | 9 ++------- qv4codegen_p.h | 2 +- qv4ir_p.h | 8 +++++++- qv4isel.cpp | 41 +++++++++-------------------------------- qv4isel_p.h | 5 +++-- 6 files changed, 53 insertions(+), 44 deletions(-) diff --git a/main.cpp b/main.cpp index f16eec2561..e95a3aaa36 100644 --- a/main.cpp +++ b/main.cpp @@ -1,6 +1,7 @@ #include "qmljs_objects.h" #include "qv4codegen_p.h" +#include "qv4isel_p.h" #include #include @@ -8,8 +9,18 @@ #include #include +#include #include +static inline bool protect(const void *addr, size_t size) +{ + size_t pageSize = sysconf(_SC_PAGESIZE); + size_t iaddr = reinterpret_cast(addr); + size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); + int mode = PROT_READ | PROT_WRITE | PROT_EXEC; + return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; +} + int main(int argc, char *argv[]) { using namespace QQmlJS; @@ -41,7 +52,26 @@ int main(int argc, char *argv[]) Program *program = AST::cast(parser.rootNode()); Codegen cg; - cg(program); + IR::Module module; + cg(program, &module); + + const size_t codeSize = 10 * getpagesize(); + uchar *code = (uchar *) malloc(codeSize); + + x86_64::InstructionSelection isel(&module, code); + QHash codeByName; + foreach (IR::Function *function, module.functions) { + isel(function); + if (function->name && ! function->name->isEmpty()) + codeByName.insert(*function->name, function); + } + + if (! protect(code, codeSize)) + Q_UNREACHABLE(); + + VM::Context *ctx = new VM::Context; + ctx->activation = VM::Value::object(ctx, new VM::ArgumentsObject); + codeByName.value(QLatin1String("%entry"))->code(ctx); } } } diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 3407ed3c10..6f9181bc60 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1,5 +1,4 @@ #include "qv4codegen_p.h" -#include "qv4isel_p.h" #include #include @@ -288,10 +287,9 @@ Codegen::Codegen() { } -void Codegen::operator()(AST::Program *node) +void Codegen::operator()(AST::Program *node, IR::Module *module) { - IR::Module module; - _module = &module; + _module = module; IR::Function *globalCode = _module->newFunction(QLatin1String("%entry")); _function = globalCode; @@ -306,11 +304,8 @@ void Codegen::operator()(AST::Program *node) _block->JUMP(_exitBlock); } - x86_64::InstructionSelection isel(_module); - foreach (IR::Function *function, _module->functions) { linearize(function); - isel.visitFunction(function); } } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 6f3cb90015..f1163c14c1 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -15,7 +15,7 @@ class Codegen: protected AST::Visitor public: Codegen(); - void operator()(AST::Program *ast); + void operator()(AST::Program *ast, IR::Module *module); protected: enum Format { ex, cx, nx }; diff --git a/qv4ir_p.h b/qv4ir_p.h index a606563df0..b13ebdc04c 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -68,6 +68,11 @@ class QQmlType; namespace QQmlJS { +namespace VM { +struct Context; +struct Value; +} + namespace IR { struct BasicBlock; @@ -585,11 +590,12 @@ struct Function { QSet strings; QList formals; QList locals; + void (*code)(VM::Context *); template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } Function(Module *module, const QString &name) - : module(module), pool(&module->pool), tempCount(0) { this->name = newString(name); } + : module(module), pool(&module->pool), tempCount(0), code(0) { this->name = newString(name); } ~Function(); diff --git a/qv4isel.cpp b/qv4isel.cpp index 7c6e216b21..a5a63da17a 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -23,15 +23,6 @@ using namespace QQmlJS; using namespace QQmlJS::x86_64; using namespace QQmlJS::VM; -static inline bool protect(const void *addr, size_t size) -{ - size_t pageSize = sysconf(_SC_PAGESIZE); - size_t iaddr = reinterpret_cast(addr); - size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); - int mode = PROT_READ | PROT_WRITE | PROT_EXEC; - return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; -} - static inline void amd64_patch (unsigned char* code, gpointer target) { @@ -85,12 +76,13 @@ amd64_patch (unsigned char* code, gpointer target) else x86_patch (code, (unsigned char*)target); } -InstructionSelection::InstructionSelection(IR::Module *module) +InstructionSelection::InstructionSelection(IR::Module *module, uchar *buffer) : _module(module) , _function(0) , _block(0) - , _code(0) - , _codePtr(0) + , _buffer(buffer) + , _code(buffer) + , _codePtr(buffer) { } @@ -98,17 +90,11 @@ InstructionSelection::~InstructionSelection() { } -void InstructionSelection::visitFunction(IR::Function *function) +void InstructionSelection::operator()(IR::Function *function) { - uchar *code = (uchar *) malloc(getpagesize()); - assert(! (size_t(code) & 15)); - - protect(code, getpagesize()); - - uchar *codePtr = code; - - qSwap(_code, code); - qSwap(_codePtr, codePtr); + _code = (uchar *) ((size_t(_code) + 15) & ~15); + function->code = (void (*)(VM::Context *)) _code; + _codePtr = _code; int locals = function->tempCount * sizeof(Value); locals = (locals + 15) & ~15; @@ -146,16 +132,13 @@ void InstructionSelection::visitFunction(IR::Function *function) amd64_pop_reg(_codePtr, AMD64_RBP); amd64_ret(_codePtr); - qSwap(_codePtr, codePtr); - qSwap(_code, code); - #ifndef NO_UDIS86 static bool showCode = !qgetenv("SHOW_CODE").isNull(); if (showCode) { ud_t ud_obj; ud_init(&ud_obj); - ud_set_input_buffer(&ud_obj, code, codePtr - code); + ud_set_input_buffer(&ud_obj, _code, _codePtr - _code); ud_set_mode(&ud_obj, 64); ud_set_syntax(&ud_obj, UD_SYN_ATT); @@ -164,12 +147,6 @@ void InstructionSelection::visitFunction(IR::Function *function) } } #endif - - void (*f)(Context *) = (void (*)(Context *)) code; - - Context *ctx = new Context; - ctx->activation = Value::object(ctx, new ArgumentsObject); - f(ctx); } String *InstructionSelection::identifier(const QString &s) diff --git a/qv4isel_p.h b/qv4isel_p.h index 9c85c19c87..0672bc5437 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -12,10 +12,10 @@ namespace x86_64 { class InstructionSelection: protected IR::StmtVisitor { public: - InstructionSelection(IR::Module *module); + InstructionSelection(IR::Module *module, uchar *code); ~InstructionSelection(); - void visitFunction(IR::Function *function); + void operator()(IR::Function *function); protected: VM::String *identifier(const QString &s); @@ -34,6 +34,7 @@ private: IR::Module *_module; IR::Function *_function; IR::BasicBlock *_block; + uchar *_buffer; uchar *_code; uchar *_codePtr; QHash > _patches; -- cgit v1.2.3 From 1c18b5774fc44ad203db8ea1a751e9b6b2a76378 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 7 May 2012 14:49:27 +0200 Subject: Fix the examples --- tests/for/for.1.js | 3 +-- tests/for/for.2.js | 3 +-- tests/for/for.3.js | 3 +-- tests/for/for.4.js | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/for/for.1.js b/tests/for/for.1.js index 523938b308..9c1454dd31 100644 --- a/tests/for/for.1.js +++ b/tests/for/for.1.js @@ -1,7 +1,6 @@ -var d = "" var x = 0 for (var i = 0; i < 20; i = i + 1) { x = x + i - d = d + (i + ") x = " + x + "\n") + print((i + ") x = " + x)) } diff --git a/tests/for/for.2.js b/tests/for/for.2.js index 0ef80e85e9..f403160dc8 100644 --- a/tests/for/for.2.js +++ b/tests/for/for.2.js @@ -1,8 +1,7 @@ -var d = "" var x = 0 for (var i = 0; i < 20; i = i + 1) { if (i % 2) continue x = x + i - d = d + (i + ") x = " + x + "\n") + print((i + ") x = " + x)) } diff --git a/tests/for/for.3.js b/tests/for/for.3.js index bdc641db13..f745366d41 100644 --- a/tests/for/for.3.js +++ b/tests/for/for.3.js @@ -1,8 +1,7 @@ -var d = "" var x = 0 for (var i = 0; i < 20; i = i + 1) { if (i == 10) break x = x + i - d = d + (i + ") x = " + x + "\n") + print((i + ") x = " + x)) } diff --git a/tests/for/for.4.js b/tests/for/for.4.js index cf66fd00f0..6e282181f4 100644 --- a/tests/for/for.4.js +++ b/tests/for/for.4.js @@ -1,8 +1,7 @@ -var d = "" var x = 0 for (var i = 0; i < 20; i = i + 1) { if (! (i % 2)) continue x = x + i - d = d + (i + ") x = " + x + "\n") + print((i + ") x = " + x)) } -- cgit v1.2.3 From dc716f8d372f282027ff7bd950b6f000594f2224 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 7 May 2012 16:05:05 +0200 Subject: Add naive support for function calls. --- main.cpp | 31 +++++++++++++++++-- qmljs_objects.cpp | 37 ++++++++++++++-------- qmljs_objects.h | 27 +++++++++++++--- qmljs_runtime.cpp | 50 +++++++++++++++++++----------- qmljs_runtime.h | 12 +++++--- qv4codegen.cpp | 4 ++- qv4isel.cpp | 92 +++++++++++++++++++++++++++++-------------------------- qv4isel_p.h | 1 + 8 files changed, 169 insertions(+), 85 deletions(-) diff --git a/main.cpp b/main.cpp index e95a3aaa36..4be5e07e80 100644 --- a/main.cpp +++ b/main.cpp @@ -21,6 +21,24 @@ static inline bool protect(const void *addr, size_t size) return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; } +namespace builtins { +using namespace QQmlJS::VM; +struct Print: FunctionObject +{ + virtual void call(Context *ctx) + { + for (size_t i = 0; i < ctx->argumentCount; ++i) { + Value v; + __qmljs_to_string(ctx, &v, &ctx->arguments[i]); + if (i) + std::cout << ' '; + std::cout << qPrintable(v.stringValue->text()); + } + std::cout << std::endl; + } +}; +} // builtins + int main(int argc, char *argv[]) { using namespace QQmlJS; @@ -62,15 +80,24 @@ int main(int argc, char *argv[]) QHash codeByName; foreach (IR::Function *function, module.functions) { isel(function); - if (function->name && ! function->name->isEmpty()) + if (function->name && ! function->name->isEmpty()) { codeByName.insert(*function->name, function); + } } if (! protect(code, codeSize)) Q_UNREACHABLE(); VM::Context *ctx = new VM::Context; - ctx->activation = VM::Value::object(ctx, new VM::ArgumentsObject); + ctx->activation = VM::Value::object(ctx, new VM::ArgumentsObject(ctx)); + ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("print")), + VM::Value::object(ctx, new builtins::Print())); + foreach (IR::Function *function, module.functions) { + if (function->name && ! function->name->isEmpty()) { + ctx->activation.objectValue->put(VM::String::get(ctx, *function->name), + VM::Value::object(ctx, new VM::ScriptFunction(function))); + } + } codeByName.value(QLatin1String("%entry"))->code(ctx); } } diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 57332aba69..37e3537e6a 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -1,5 +1,7 @@ #include "qmljs_objects.h" +#include "qv4ir_p.h" +#include #include using namespace QQmlJS::VM; @@ -103,21 +105,32 @@ bool FunctionObject::hasInstance(const Value &value) const return false; } -Value FunctionObject::call(const Value &thisObject, const Value args[], unsigned argc) +void FunctionObject::call(Context *ctx) { - Q_UNUSED(thisObject); - Q_UNUSED(args); - Q_UNUSED(argc); + Q_UNUSED(ctx); +} + +void FunctionObject::construct(Context *ctx) +{ + Q_UNUSED(ctx); + Q_UNIMPLEMENTED(); +} - Value v; - __qmljs_init_undefined(0, &v); - return v; +void ScriptFunction::call(VM::Context *ctx) +{ + // bind the actual arguments. ### slow + for (int i = 0; i < function->formals.size(); ++i) { + const QString *f = function->formals.at(i); + ctx->activation.objectValue->put(String::get(ctx, *f), ctx->arguments[i]); + } + function->code(ctx); } -Value FunctionObject::construct(const Value args[], unsigned argc) +Property *ArgumentsObject::getOwnProperty(String *name) { - Value thisObject; - __qmljs_init_object(0, &thisObject, new Object); - call(thisObject, args, argc); - return thisObject; + if (Property *prop = Object::getOwnProperty(name)) + return prop; + else if (context && context->scope) + return context->scope->getOwnProperty(name); + return 0; } diff --git a/qmljs_objects.h b/qmljs_objects.h index cabdf5c124..17c85b0701 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -8,6 +8,11 @@ #include namespace QQmlJS { + +namespace IR { +struct Function; +} + namespace VM { struct Value; @@ -234,8 +239,15 @@ struct FunctionObject: Object { virtual FunctionObject *asFunctionObject() { return this; } virtual bool hasInstance(const Value &value) const; - virtual Value call(const Value &thisObject, const Value args[], unsigned argc); - virtual Value construct(const Value args[], unsigned argc); + virtual void call(Context *ctx); + virtual void construct(Context *ctx); +}; + +struct ScriptFunction: FunctionObject { + IR::Function *function; + + ScriptFunction(IR::Function *function): function(function) {} + virtual void call(Context *ctx); }; struct ErrorObject: Object { @@ -244,22 +256,29 @@ struct ErrorObject: Object { }; struct ArgumentsObject: Object { + Context *context; + ArgumentsObject(Context *context): context(context) {} + virtual Property *getOwnProperty(String *name); }; struct Context { + Context *parent; Value activation; Value thisObject; Object *scope; Value *arguments; - unsigned argumentCount; + size_t argumentCount; + Value result; Context() - : scope(0) + : parent(0) + , scope(0) , arguments(0) , argumentCount(0) { activation.type = NULL_TYPE; thisObject.type = NULL_TYPE; + result.type = UNDEFINED_TYPE; } }; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index f95060e61f..4870c0bf65 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -1,6 +1,7 @@ #include "qmljs_runtime.h" #include "qmljs_objects.h" +#include #include #include @@ -326,29 +327,42 @@ bool __qmljs_equal(Context *ctx, const Value *x, const Value *y) return false; } -void __qmljs_call(Context *ctx, Value *result, const Value *function, - const Value *thisObject, const Value *arguments, int argc) +Context *__qmljs_new_context(Context *current, Value *thisObject, size_t argc) { - if (function->type != OBJECT_TYPE) { - __qmljs_throw_type_error(ctx, result); - } else if (FunctionObject *f = function->objectValue->asFunctionObject()) { - *result = f->call(*thisObject, arguments, argc); - } else { - __qmljs_throw_type_error(ctx, result); - } + Context *ctx = new Context; + ctx->parent = current; + ctx->scope = current->activation.objectValue; + __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); + if (thisObject) + ctx->thisObject = *thisObject; + else + __qmljs_init_null(ctx, &ctx->thisObject); + ctx->arguments = new Value[argc]; + ctx->argumentCount = argc; + return ctx; } -void __qmjs_construct(Context *ctx, Value *result, const Value *function, const Value *arguments, int argc) -{ - if (function->type != OBJECT_TYPE) { - __qmljs_throw_type_error(ctx, result); - } else if (FunctionObject *f = function->objectValue->asFunctionObject()) { - *result = f->construct(arguments, argc); - } else { - __qmljs_throw_type_error(ctx, result); - } +void __qmljs_dispose_context(Context *ctx) +{ + delete[] ctx->arguments; + delete ctx; } +void __qmljs_call_activation_property(Context *context, Value *result, String *name) +{ + Value func; + context->parent->activation.objectValue->get(name, &func); + if (func.type == OBJECT_TYPE) { + if (FunctionObject *f = func.objectValue->asFunctionObject()) { + f->call(context); + __qmljs_copy(result, &context->result); + } else { + Q_ASSERT(!"not a function"); + } + } else { + Q_ASSERT(!"not a callable object"); + } +} } // extern "C" diff --git a/qmljs_runtime.h b/qmljs_runtime.h index d295c3fca2..438206bed1 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -38,6 +38,11 @@ struct Context; extern "C" { +// context +Context *__qmljs_new_context(Context *current, Value *thisObject, size_t argc); +void __qmljs_dispose_context(Context *ctx); +void __qmljs_call_activation_property(Context *, Value *result, String *name); + // constructors void __qmljs_init_undefined(Context *ctx, Value *result); void __qmljs_init_null(Context *ctx, Value *result); @@ -143,9 +148,6 @@ void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *rig void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right); void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_call(Context *ctx, Value *result, const Value *function, const Value *thisObject, const Value *arguments, int argc); -void __qmjs_construct(Context *ctx, Value *result, const Value *function, const Value *arguments, int argc); - } // extern "C" struct Value { @@ -550,7 +552,7 @@ inline void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const V inline void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Value *right) { - __qmljs_compare(ctx, result, right, left, false); + __qmljs_compare(ctx, result, left, right, false); if (result->type == UNDEFINED_TYPE) __qmljs_init_boolean(ctx, result, false); @@ -566,7 +568,7 @@ inline void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Val inline void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Value *right) { - __qmljs_compare(ctx, result, left, right, false); + __qmljs_compare(ctx, result, right, left, false); bool r = ! (result->type == UNDEFINED_TYPE || (result->type == BOOLEAN_TYPE && result->booleanValue == true)); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 6f9181bc60..24c6c8188a 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -932,7 +932,9 @@ bool Codegen::visit(CallExpression *ast) (*args_it)->init(actual); args_it = &(*args_it)->next; } - _expr.code = _block->CALL(*base, args); + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), _block->CALL(*base, args)); + _expr.code = _block->TEMP(t); return false; } diff --git a/qv4isel.cpp b/qv4isel.cpp index a5a63da17a..f0fc00f5c9 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -92,6 +92,7 @@ InstructionSelection::~InstructionSelection() void InstructionSelection::operator()(IR::Function *function) { + _code = _codePtr; _code = (uchar *) ((size_t(_code) + 15) & ~15); function->code = (void (*)(VM::Context *)) _code; _codePtr = _code; @@ -167,54 +168,43 @@ void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * (t->index - 1)); } -void __qmljs_print(Context *ctx, const Value *args, size_t argc) +void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) { - for (int i = 0; i < argc; ++i) { - Value v; - __qmljs_to_string(ctx, &v, &args[i]); - if (i) - std::cout << ' '; - std::cout << qPrintable(v.stringValue->text()); + int argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) + ++argc; + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, argc); + amd64_call_code(_codePtr, __qmljs_new_context); + + amd64_mov_reg_reg(_codePtr, AMD64_R15, AMD64_RAX, 8); + + argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) { + IR::Temp *t = it->expr->asTemp(); + Q_ASSERT(t != 0); + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R15, offsetof(Context, arguments), 8); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RAX, argc * sizeof(Value)); + loadTempAddress(AMD64_RSI, t); + amd64_call_code(_codePtr, __qmljs_copy); + ++argc; } - std::cout << std::endl; + + String *id = identifier(*call->base->asName()->id); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); + loadTempAddress(AMD64_RSI, result); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, id); + amd64_call_code(_codePtr, __qmljs_call_activation_property); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); + amd64_call_code(_codePtr, __qmljs_dispose_context); } -void InstructionSelection::visitExp(IR::Exp *s) +void InstructionSelection::visitExp(IR::Exp *) { - if (IR::Call *c = s->expr->asCall()) { - IR::Name *n = c->base->asName(); - if (n && *n->id == QLatin1String("print")) { - int argc = 0; - for (IR::ExprList *it = c->args; it; it = it->next) - ++argc; - amd64_mov_reg_imm(_codePtr, AMD64_RDI, argc * sizeof(Value)); - amd64_mov_reg_imm(_codePtr, AMD64_RAX, malloc); - amd64_call_reg(_codePtr, AMD64_RAX); - amd64_mov_reg_reg(_codePtr, AMD64_R15, AMD64_RAX, 8); - - argc = 0; - for (IR::ExprList *it = c->args; it; it = it->next) { - IR::Temp *t = it->expr->asTemp(); - Q_ASSERT(t != 0); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_R15, argc * sizeof(Value)); - loadTempAddress(AMD64_RSI, t); - amd64_call_code(_codePtr, __qmljs_copy); - ++argc; - } - - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_mov_reg_reg(_codePtr, AMD64_RSI, AMD64_R15, 8); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, argc); - amd64_call_code(_codePtr, __qmljs_print); - - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); - amd64_mov_reg_imm(_codePtr, AMD64_RAX, free); - amd64_call_reg(_codePtr, AMD64_RAX); - return; - } - } - Q_UNIMPLEMENTED(); - assert(!"TODO"); +// Q_UNIMPLEMENTED(); +// assert(!"TODO"); } void InstructionSelection::visitEnter(IR::Enter *) @@ -305,6 +295,11 @@ void InstructionSelection::visitMove(IR::Move *s) assert(!"TODO"); } return; + } else if (IR::Temp *t2 = s->source->asTemp()) { + loadTempAddress(AMD64_RDI, t); + loadTempAddress(AMD64_RSI, t2); + amd64_call_code(_codePtr, __qmljs_copy); + return; } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); @@ -378,6 +373,11 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_call_code(_codePtr, op); return; } + } else if (IR::Call *c = s->source->asCall()) { + if (c->base->asName()) { + callActivationProperty(c, t); + return; + } } } } else { @@ -417,6 +417,12 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { + if (IR::Temp *t = s->expr->asTemp()) { + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_R14, offsetof(Context, result)); + loadTempAddress(AMD64_RSI, t); + amd64_call_code(_codePtr, __qmljs_copy); + return; + } Q_UNIMPLEMENTED(); Q_UNUSED(s); } diff --git a/qv4isel_p.h b/qv4isel_p.h index 0672bc5437..753da042ea 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -21,6 +21,7 @@ protected: VM::String *identifier(const QString &s); int tempOffset(IR::Temp *t); void loadTempAddress(int reg, IR::Temp *t); + void callActivationProperty(IR::Call *call, IR::Temp *result); virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); -- cgit v1.2.3 From 928b291726b318372a197c334ed84d02c30d87be Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 7 May 2012 17:50:19 +0200 Subject: Test function calls --- tests/fact.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/fact.js diff --git a/tests/fact.js b/tests/fact.js new file mode 100644 index 0000000000..c00717d698 --- /dev/null +++ b/tests/fact.js @@ -0,0 +1,15 @@ + +function fact1(n) { + if (n > 0) + return n * fact1(n - 1); + else + return 1 +} + +function fact2(n) { + return n > 0 ? n * fact2(n - 1) : 1 +} + +print("fact(12) = ", fact1(12)) +print("fact(12) = ", fact2(12)) + -- cgit v1.2.3 From 0cd210f35a7b11b872d9f9ca1ff5243e9ac083cf Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 8 May 2012 08:58:17 +0200 Subject: Remove the register allocator. It needs to be done in a different way --- qv4codegen.cpp | 120 ++++----------------------------------------------------- qv4isel.cpp | 4 +- 2 files changed, 10 insertions(+), 114 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 24c6c8188a..4dbb1a3b22 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -97,102 +97,6 @@ struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor } }; -struct RegAlloc: IR::StmtVisitor, IR::ExprVisitor -{ - QHash RD; - QHash RA; - QList _freeRegisters; - QSet _processed; - QSet _kill; - IR::Stmt *_stmt; - unsigned _registerCount; - - RegAlloc(): _stmt(0), _registerCount(1) {} - - void operator()(IR::Stmt *s) { - _kill.clear(); - qSwap(_stmt, s); - _stmt->accept(this); - qSwap(_stmt, s); - foreach (unsigned r, _kill) - freeRegister(r); - } - - unsigned newReg() - { - if (! _freeRegisters.isEmpty()) - return _freeRegisters.takeLast(); - - return _registerCount++; - } - - void freeRegister(unsigned virtualRegister) - { - unsigned physRegister = RA.value(virtualRegister); - RA.remove(virtualRegister); - RD.remove(physRegister); - - if (! _freeRegisters.contains(physRegister)) - _freeRegisters.append(physRegister); - } - - unsigned getReg(IR::Temp *t) - { - const int virtualRegister = t->index; - - if (unsigned reg = RA.value(virtualRegister)) { - return reg; - } - - const unsigned reg = newReg(); - RA[virtualRegister] = reg; - RD[reg] = virtualRegister; - return reg; - } - - virtual void visitConst(IR::Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitName(IR::Name *) {} - virtual void visitClosure(IR::Closure *) {} - - virtual void visitTemp(IR::Temp *e) { - const unsigned virtualRegister = e->index; - e->index = getReg(e); - if (! _stmt->d->liveOut.testBit(virtualRegister)) - _kill.insert(virtualRegister); - } - - virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); } - virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitCall(IR::Call *e) { - e->base->accept(this); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitNew(IR::New *e) { - e->base->accept(this); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(IR::Member *e) { e->base->accept(this); } - - virtual void visitExp(IR::Exp *s) { s->expr->accept(this); } - virtual void visitEnter(IR::Enter *) {} - virtual void visitLeave(IR::Leave *) {} - - virtual void visitMove(IR::Move *s) { - s->source->accept(this); - s->target->accept(this); - } - - virtual void visitJump(IR::Jump *) {} - virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); } - virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } -}; - void liveness(IR::Function *function) { QSet V; @@ -228,9 +132,9 @@ void liveness(IR::Function *function) const QBitArray previousLiveIn = block->liveIn; const QBitArray previousLiveOut = block->liveOut; QBitArray live(function->tempCount); - block->liveOut = live; foreach (IR::BasicBlock *succ, block->out) live |= succ->liveIn; + block->liveOut = live; for (int i = block->statements.size() - 1; i != -1; --i) { IR::Stmt *s = block->statements.at(i); s->d->liveOut = live; @@ -1234,15 +1138,6 @@ void Codegen::linearize(IR::Function *function) liveness(function); - if (1) { - RegAlloc regalloc; - foreach (IR::BasicBlock *block, function->basicBlocks) { - foreach (IR::Stmt *s, block->statements) - regalloc(s); - } - function->tempCount = regalloc._registerCount - 1; - } - static bool showCode = !qgetenv("SHOW_CODE").isNull(); if (showCode) { QVector code; @@ -1312,12 +1207,13 @@ void Codegen::linearize(IR::Function *function) // } // } - // if (! s->liveOut.isEmpty()) { - // qout << " // lives:"; - // foreach (unsigned live, s->liveOut) { - // qout << " %" << live; - // } - // } + if (! s->d->liveOut.isEmpty()) { + qout << " // lives:"; + for (int i = 0; i < s->d->liveOut.size(); ++i) { + if (s->d->liveOut.testBit(i)) + qout << " %" << i; + } + } qout << endl; diff --git a/qv4isel.cpp b/qv4isel.cpp index f0fc00f5c9..5424b666d7 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -160,12 +160,12 @@ String *InstructionSelection::identifier(const QString &s) int InstructionSelection::tempOffset(IR::Temp *t) { - return sizeof(Value) * (t->index - 1); + return sizeof(Value) * t->index; } void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) { - amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * (t->index - 1)); + amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * t->index); } void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) -- cgit v1.2.3 From acc9293bd101c7871d5fbd7e94b271f65ba87e1d Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 8 May 2012 10:32:33 +0200 Subject: Initialize the return address --- qv4codegen.cpp | 2 ++ qv4isel.cpp | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 4dbb1a3b22..eb4ebd4684 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -200,6 +200,7 @@ void Codegen::operator()(AST::Program *node, IR::Module *module) _block = _function->newBasicBlock(); _exitBlock = _function->newBasicBlock(); _returnAddress = _block->newTemp(); + _block->MOVE(_block->TEMP(_returnAddress), _block->CONST(IR::UndefinedType, 0)); _exitBlock->RET(_exitBlock->TEMP(_returnAddress), IR::UndefinedType); program(node); @@ -1239,6 +1240,7 @@ void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) IR::BasicBlock *exitBlock = function->newBasicBlock(); unsigned returnAddress = entryBlock->newTemp(); + entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(IR::UndefinedType, 0)); exitBlock->RET(_block->TEMP(returnAddress), IR::InvalidType); qSwap(_function, function); diff --git a/qv4isel.cpp b/qv4isel.cpp index 5424b666d7..06a9715081 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -279,6 +279,14 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RSI, t); switch (c->type) { + case IR::NullType: + amd64_call_code(_codePtr, __qmljs_init_null); + break; + + case IR::UndefinedType: + amd64_call_code(_codePtr, __qmljs_init_undefined); + break; + case IR::BoolType: amd64_mov_reg_imm(_codePtr, AMD64_RDX, c->value != 0); amd64_call_code(_codePtr, __qmljs_init_boolean); -- cgit v1.2.3 From 02abaa637ca1cb6c718e99fc158e915a6e227dba Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 8 May 2012 10:34:57 +0200 Subject: Make Context a POD value. --- qmljs_objects.cpp | 6 +++--- qmljs_objects.h | 12 ++++++------ qmljs_runtime.cpp | 1 + 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 37e3537e6a..61533b8592 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -126,11 +126,11 @@ void ScriptFunction::call(VM::Context *ctx) function->code(ctx); } -Property *ArgumentsObject::getOwnProperty(String *name) +Property *ArgumentsObject::getProperty(String *name) { - if (Property *prop = Object::getOwnProperty(name)) + if (Property *prop = Object::getProperty(name)) return prop; else if (context && context->scope) - return context->scope->getOwnProperty(name); + return context->scope->getProperty(name); return 0; } diff --git a/qmljs_objects.h b/qmljs_objects.h index 17c85b0701..b0109ec25d 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -258,7 +258,7 @@ struct ErrorObject: Object { struct ArgumentsObject: Object { Context *context; ArgumentsObject(Context *context): context(context) {} - virtual Property *getOwnProperty(String *name); + virtual Property *getProperty(String *name); }; struct Context { @@ -270,12 +270,12 @@ struct Context { size_t argumentCount; Value result; - Context() - : parent(0) - , scope(0) - , arguments(0) - , argumentCount(0) + void init() { + parent = 0; + scope = 0; + arguments = 0; + argumentCount = 0; activation.type = NULL_TYPE; thisObject.type = NULL_TYPE; result.type = UNDEFINED_TYPE; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 4870c0bf65..a85ef9da69 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -330,6 +330,7 @@ bool __qmljs_equal(Context *ctx, const Value *x, const Value *y) Context *__qmljs_new_context(Context *current, Value *thisObject, size_t argc) { Context *ctx = new Context; + ctx->init(); ctx->parent = current; ctx->scope = current->activation.objectValue; __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); -- cgit v1.2.3 From 08c29dabd8686c044be2d92a8222c0ca0193f0ec Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 8 May 2012 11:13:02 +0200 Subject: Improve frame instantiations. --- main.cpp | 1 + qmljs_objects.cpp | 64 ++++++++++++++++++++++++++++++++++++++----------------- qmljs_objects.h | 30 ++++++++++++++++---------- qmljs_runtime.cpp | 2 ++ 4 files changed, 66 insertions(+), 31 deletions(-) diff --git a/main.cpp b/main.cpp index 4be5e07e80..88b0bc79ef 100644 --- a/main.cpp +++ b/main.cpp @@ -89,6 +89,7 @@ int main(int argc, char *argv[]) Q_UNREACHABLE(); VM::Context *ctx = new VM::Context; + ctx->init(); ctx->activation = VM::Value::object(ctx, new VM::ArgumentsObject(ctx)); ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("print")), VM::Value::object(ctx, new builtins::Print())); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 61533b8592..e628a24f42 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -13,8 +13,8 @@ Object::~Object() bool Object::get(String *name, Value *result) { - if (Property *prop = getProperty(name)) { - *result = prop->value; + if (Value *prop = getProperty(name)) { + *result = *prop; return true; } @@ -22,22 +22,24 @@ bool Object::get(String *name, Value *result) return false; } -Property *Object::getOwnProperty(String *name) +Value *Object::getOwnProperty(String *name, PropertyAttributes *attributes) { if (members) { if (Property *prop = members->find(name)) { - return prop; + if (attributes) + *attributes = prop->attributes; + return &prop->value; } } return 0; } -Property *Object::getProperty(String *name) +Value *Object::getProperty(String *name, PropertyAttributes *attributes) { - if (Property *prop = getOwnProperty(name)) + if (Value *prop = getOwnProperty(name, attributes)) return prop; else if (prototype) - return prototype->getProperty(name); + return prototype->getProperty(name, attributes); return 0; } @@ -53,13 +55,13 @@ void Object::put(String *name, const Value &value, bool flag) bool Object::canPut(String *name) { - if (Property *prop = getOwnProperty(name)) { - Q_UNUSED(prop); - return true; + PropertyAttributes attrs = PropertyAttributes(); + if (getOwnProperty(name, &attrs)) { + return attrs & WritableAttribute; } else if (! prototype) { return extensible; - } else if (Property *inherited = prototype->getProperty(name)) { - return inherited->isWritable(); + } else if (prototype->getProperty(name, &attrs)) { + return attrs & WritableAttribute; } else { return extensible; } @@ -116,21 +118,43 @@ void FunctionObject::construct(Context *ctx) Q_UNIMPLEMENTED(); } -void ScriptFunction::call(VM::Context *ctx) +ScriptFunction::ScriptFunction(IR::Function *function) + : function(function) { - // bind the actual arguments. ### slow - for (int i = 0; i < function->formals.size(); ++i) { - const QString *f = function->formals.at(i); - ctx->activation.objectValue->put(String::get(ctx, *f), ctx->arguments[i]); + formalParameterCount = function->formals.size(); + if (formalParameterCount) { + formalParameterList = new String*[formalParameterCount]; + for (size_t i = 0; i < formalParameterCount; ++i) { + formalParameterList[i] = String::get(0, *function->formals.at(i)); // ### unique + } } +} + +ScriptFunction::~ScriptFunction() +{ + delete[] formalParameterList; +} + +void ScriptFunction::call(VM::Context *ctx) +{ function->code(ctx); } -Property *ArgumentsObject::getProperty(String *name) +Value *ArgumentsObject::getProperty(String *name, PropertyAttributes *attributes) { - if (Property *prop = Object::getProperty(name)) + if (context) { + for (size_t i = 0; i < context->formalCount; ++i) { + String *formal = context->formals[i]; + if (__qmljs_string_equal(context, formal, name)) { + if (attributes) + *attributes = PropertyAttributes(*attributes | WritableAttribute); + return &context->arguments[i]; + } + } + } + if (Value *prop = Object::getProperty(name, attributes)) return prop; else if (context && context->scope) - return context->scope->getProperty(name); + return context->scope->getProperty(name, attributes); return 0; } diff --git a/qmljs_objects.h b/qmljs_objects.h index b0109ec25d..f7f5f1506e 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -52,16 +52,16 @@ private: struct Property { String *name; Value value; - PropertyAttributes flags; + PropertyAttributes attributes; Property *next; int index; Property(String *name, const Value &value, PropertyAttributes flags = NoAttributes) - : name(name), value(value), flags(flags), next(0), index(-1) {} + : name(name), value(value), attributes(flags), next(0), index(-1) {} - inline bool isWritable() const { return flags & WritableAttribute; } - inline bool isEnumerable() const { return flags & EnumerableAttribute; } - inline bool isConfigurable() const { return flags & ConfigurableAttribute; } + inline bool isWritable() const { return attributes & WritableAttribute; } + inline bool isEnumerable() const { return attributes & EnumerableAttribute; } + inline bool isConfigurable() const { return attributes & ConfigurableAttribute; } inline bool hasName(String *n) const { if (name == n) { @@ -199,9 +199,10 @@ struct Object { virtual FunctionObject *asFunctionObject() { return 0; } - virtual bool get(String *name, Value *result); - virtual Property *getOwnProperty(String *name); - virtual Property *getProperty(String *name); + bool get(String *name, Value *result); + + virtual Value *getOwnProperty(String *name, PropertyAttributes *attributes = 0); + virtual Value *getProperty(String *name, PropertyAttributes *attributes = 0); virtual void put(String *name, const Value &value, bool flag = 0); virtual bool canPut(String *name); virtual bool hasProperty(String *name) const; @@ -234,8 +235,9 @@ struct ArrayObject: Object { struct FunctionObject: Object { Object *scope; String **formalParameterList; + size_t formalParameterCount; - FunctionObject(Object *scope = 0): scope(scope), formalParameterList(0) {} + FunctionObject(Object *scope = 0): scope(scope), formalParameterList(0), formalParameterCount(0) {} virtual FunctionObject *asFunctionObject() { return this; } virtual bool hasInstance(const Value &value) const; @@ -246,7 +248,9 @@ struct FunctionObject: Object { struct ScriptFunction: FunctionObject { IR::Function *function; - ScriptFunction(IR::Function *function): function(function) {} + ScriptFunction(IR::Function *function); + virtual ~ScriptFunction(); + virtual void call(Context *ctx); }; @@ -258,7 +262,7 @@ struct ErrorObject: Object { struct ArgumentsObject: Object { Context *context; ArgumentsObject(Context *context): context(context) {} - virtual Property *getProperty(String *name); + virtual Value *getProperty(String *name, PropertyAttributes *attributes); }; struct Context { @@ -269,6 +273,8 @@ struct Context { Value *arguments; size_t argumentCount; Value result; + String **formals; + size_t formalCount; void init() { @@ -279,6 +285,8 @@ struct Context { activation.type = NULL_TYPE; thisObject.type = NULL_TYPE; result.type = UNDEFINED_TYPE; + formals = 0; + formalCount = 0; } }; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index a85ef9da69..ae6d5057cb 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -355,6 +355,8 @@ void __qmljs_call_activation_property(Context *context, Value *result, String *n context->parent->activation.objectValue->get(name, &func); if (func.type == OBJECT_TYPE) { if (FunctionObject *f = func.objectValue->asFunctionObject()) { + context->formals = f->formalParameterList; + context->formalCount = f->formalParameterCount; f->call(context); __qmljs_copy(result, &context->result); } else { -- cgit v1.2.3 From 6f3022a3d4fc469a5234f454e245239b908e19b0 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 8 May 2012 13:21:52 +0200 Subject: Added a simple syntax checker. --- main.cpp | 104 +++++++++++++++++++++++++++------------------------ qv4syntaxchecker.cpp | 79 ++++++++++++++++++++++++++++++++++++++ qv4syntaxchecker_p.h | 33 ++++++++++++++++ v4.pro | 6 ++- 4 files changed, 172 insertions(+), 50 deletions(-) create mode 100644 qv4syntaxchecker.cpp create mode 100644 qv4syntaxchecker_p.h diff --git a/main.cpp b/main.cpp index 88b0bc79ef..5ca39262ed 100644 --- a/main.cpp +++ b/main.cpp @@ -2,6 +2,7 @@ #include "qmljs_objects.h" #include "qv4codegen_p.h" #include "qv4isel_p.h" +#include "qv4syntaxchecker_p.h" #include #include @@ -39,6 +40,60 @@ struct Print: FunctionObject }; } // builtins + +void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &code) +{ + using namespace QQmlJS; + + Lexer lexer(engine); + lexer.setCode(code, 1, false); + Parser parser(engine); + + const bool parsed = parser.parseProgram(); + + foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { + std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn + << ": error: " << qPrintable(m.message) << std::endl; + } + + if (parsed) { + using namespace AST; + Program *program = AST::cast(parser.rootNode()); + + Codegen cg; + IR::Module module; + cg(program, &module); + + const size_t codeSize = 10 * getpagesize(); + uchar *code = (uchar *) malloc(codeSize); + + x86_64::InstructionSelection isel(&module, code); + QHash codeByName; + foreach (IR::Function *function, module.functions) { + isel(function); + if (function->name && ! function->name->isEmpty()) { + codeByName.insert(*function->name, function); + } + } + + if (! protect(code, codeSize)) + Q_UNREACHABLE(); + + VM::Context *ctx = new VM::Context; + ctx->init(); + ctx->activation = VM::Value::object(ctx, new VM::ArgumentsObject(ctx)); + ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("print")), + VM::Value::object(ctx, new builtins::Print())); + foreach (IR::Function *function, module.functions) { + if (function->name && ! function->name->isEmpty()) { + ctx->activation.objectValue->put(VM::String::get(ctx, *function->name), + VM::Value::object(ctx, new VM::ScriptFunction(function))); + } + } + codeByName.value(QLatin1String("%entry"))->code(ctx); + } +} + int main(int argc, char *argv[]) { using namespace QQmlJS; @@ -53,54 +108,7 @@ int main(int argc, char *argv[]) if (file.open(QFile::ReadOnly)) { const QString code = QString::fromUtf8(file.readAll()); file.close(); - - Lexer lexer(&engine); - lexer.setCode(code, 1, false); - Parser parser(&engine); - - const bool parsed = parser.parseProgram(); - - foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { - std::cerr << qPrintable(fn) << ':' << m.loc.startLine << ':' << m.loc.startColumn - << ": error: " << qPrintable(m.message) << std::endl; - } - - if (parsed) { - using namespace AST; - Program *program = AST::cast(parser.rootNode()); - - Codegen cg; - IR::Module module; - cg(program, &module); - - const size_t codeSize = 10 * getpagesize(); - uchar *code = (uchar *) malloc(codeSize); - - x86_64::InstructionSelection isel(&module, code); - QHash codeByName; - foreach (IR::Function *function, module.functions) { - isel(function); - if (function->name && ! function->name->isEmpty()) { - codeByName.insert(*function->name, function); - } - } - - if (! protect(code, codeSize)) - Q_UNREACHABLE(); - - VM::Context *ctx = new VM::Context; - ctx->init(); - ctx->activation = VM::Value::object(ctx, new VM::ArgumentsObject(ctx)); - ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("print")), - VM::Value::object(ctx, new builtins::Print())); - foreach (IR::Function *function, module.functions) { - if (function->name && ! function->name->isEmpty()) { - ctx->activation.objectValue->put(VM::String::get(ctx, *function->name), - VM::Value::object(ctx, new VM::ScriptFunction(function))); - } - } - codeByName.value(QLatin1String("%entry"))->code(ctx); - } + evaluate(&engine, fn, code); } } } diff --git a/qv4syntaxchecker.cpp b/qv4syntaxchecker.cpp new file mode 100644 index 0000000000..64247d6954 --- /dev/null +++ b/qv4syntaxchecker.cpp @@ -0,0 +1,79 @@ + +#include "qv4syntaxchecker_p.h" + +using namespace QQmlJS; + +SyntaxChecker::SyntaxChecker() + : Lexer(&m_engine) + , m_stateStack(128) +{ +} + +void QQmlJS::SyntaxChecker::clearText() +{ + m_code.clear(); + m_tokens.clear(); +} + +void SyntaxChecker::appendText(const QString &text) +{ + m_code += text; +} + +QString SyntaxChecker::text() const +{ + return m_code; +} + +bool SyntaxChecker::canEvaluate() +{ + int yyaction = 0; + int yytoken = -1; + int yytos = -1; + + setCode(m_code, 1); + + m_tokens.clear(); + m_tokens.append(T_FEED_JS_PROGRAM); + + do { + if (++yytos == m_stateStack.size()) + m_stateStack.resize(m_stateStack.size() * 2); + + m_stateStack[yytos] = yyaction; + +again: + if (yytoken == -1 && action_index[yyaction] != -TERMINAL_COUNT) { + if (m_tokens.isEmpty()) + yytoken = lex(); + else + yytoken = m_tokens.takeFirst(); + } + + yyaction = t_action(yyaction, yytoken); + if (yyaction > 0) { + if (yyaction == ACCEPT_STATE) { + --yytos; + return true; + } + yytoken = -1; + } else if (yyaction < 0) { + const int ruleno = -yyaction - 1; + yytos -= rhs[ruleno]; + yyaction = nt_action(m_stateStack[yytos], lhs[ruleno] - TERMINAL_COUNT); + } + } while (yyaction); + + const int errorState = m_stateStack[yytos]; + if (t_action(errorState, T_AUTOMATIC_SEMICOLON) && canInsertAutomaticSemicolon(yytoken)) { + yyaction = errorState; + m_tokens.prepend(yytoken); + yytoken = T_SEMICOLON; + goto again; + } + + if (yytoken != EOF_SYMBOL) + return true; + + return false; +} diff --git a/qv4syntaxchecker_p.h b/qv4syntaxchecker_p.h new file mode 100644 index 0000000000..508e9f8abb --- /dev/null +++ b/qv4syntaxchecker_p.h @@ -0,0 +1,33 @@ +#ifndef QV4SYNTAXCHECKER_P_H +#define QV4SYNTAXCHECKER_P_H + +#include +#include + +#include +#include +#include + +namespace QQmlJS { + +class SyntaxChecker: Lexer +{ +public: + SyntaxChecker(); + + QString text() const; + void clearText(); + void appendText(const QString &text); + + bool canEvaluate(); + +private: + Engine m_engine; + QVector m_stateStack; + QList m_tokens; + QString m_code; +}; + +} // end of QQmlJS namespace + +#endif // QV4SYNTAXCHECKER_P_H diff --git a/v4.pro b/v4.pro index b50ea3e8ad..c9d9b1a147 100644 --- a/v4.pro +++ b/v4.pro @@ -12,7 +12,8 @@ SOURCES += main.cpp \ qv4ir.cpp \ qmljs_runtime.cpp \ qmljs_objects.cpp \ - qv4isel.cpp + qv4isel.cpp \ + qv4syntaxchecker.cpp HEADERS += \ qv4codegen_p.h \ @@ -21,7 +22,8 @@ HEADERS += \ qmljs_objects.h \ qv4isel_p.h \ x86-codegen.h \ - amd64-codegen.h + amd64-codegen.h \ + qv4syntaxchecker_p.h -- cgit v1.2.3 From f71ce49202d2fb3008664e59bf5b73af5ab2c609 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 8 May 2012 14:28:32 +0200 Subject: Optimize local arguments --- qv4codegen.cpp | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ qv4codegen_p.h | 1 + qv4ir_p.h | 8 +++++- 3 files changed, 79 insertions(+), 8 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index eb4ebd4684..d6685c3240 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -153,11 +153,38 @@ void liveness(IR::Function *function) } while (changed); } -struct FindLocals: Visitor +struct ScanFunctionBody: Visitor { using Visitor::visit; + // search for locals QList locals; + bool directEval; + + ScanFunctionBody() + : directEval(false) + { + } + + void operator()(Node *node) { + directEval = false; + locals.clear(); + if (node) + node->accept(this); + } + +protected: + virtual bool visit(CallExpression *ast) + { + if (! directEval) { + if (IdentifierExpression *id = cast(ast->base)) { + if (id->name == QLatin1String("eval")) { + directEval = true; + } + } + } + return true; + } virtual bool visit(VariableDeclaration *ast) { @@ -196,6 +223,7 @@ void Codegen::operator()(AST::Program *node, IR::Module *module) _module = module; IR::Function *globalCode = _module->newFunction(QLatin1String("%entry")); + globalCode->directEval = true; // ### remove _function = globalCode; _block = _function->newBasicBlock(); _exitBlock = _function->newBasicBlock(); @@ -468,8 +496,18 @@ void Codegen::variableDeclaration(VariableDeclaration *ast) if (ast->expression) { Result expr = expression(ast->expression); - if (expr.code) + if (! expr.code) + expr.code = _block->CONST(IR::UndefinedType, 0); + + if (! _function->directEval) { + const int index = tempForLocalVariable(ast->name); + if (index != -1) { + _block->MOVE(_block->TEMP(index), *expr); + return; + } + } else { _block->MOVE(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), *expr); + } } } @@ -895,9 +933,18 @@ bool Codegen::visit(FunctionExpression *ast) bool Codegen::visit(IdentifierExpression *ast) { + if (! _function->directEval) { + int index = tempForLocalVariable(ast->name); + if (index != -1) { + _expr.code = _block->TEMP(index); + return false; + } + } + _expr.code = _block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn); + return false; } @@ -1235,9 +1282,21 @@ void Codegen::linearize(IR::Function *function) void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) { + ScanFunctionBody functionInfo; + functionInfo(ast->body); + IR::Function *function = _module->newFunction(ast->name.toString()); IR::BasicBlock *entryBlock = function->newBasicBlock(); IR::BasicBlock *exitBlock = function->newBasicBlock(); + + if (! functionInfo.directEval) { + for (int i = 0; i < functionInfo.locals.size(); ++i) { + unsigned t = entryBlock->newTemp(); + Q_ASSERT(t == unsigned(i)); + entryBlock->MOVE(entryBlock->TEMP(t), entryBlock->CONST(IR::UndefinedType, 0)); + } + } + unsigned returnAddress = entryBlock->newTemp(); entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(IR::UndefinedType, 0)); @@ -1252,11 +1311,7 @@ void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) _function->RECEIVE(it->name.toString()); } - FindLocals locals; - if (ast->body) - ast->body->accept(&locals); - - foreach (const QStringRef &local, locals.locals) { + foreach (const QStringRef &local, functionInfo.locals) { _function->LOCAL(local.toString()); } @@ -1277,6 +1332,15 @@ void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) } } +int Codegen::tempForLocalVariable(const QStringRef &string) const +{ + for (int i = 0; i < _function->locals.size(); ++i) { + if (*_function->locals.at(i) == string) + return i; + } + return -1; +} + bool Codegen::visit(IdentifierPropertyName *ast) { _property = ast->id.toString(); diff --git a/qv4codegen_p.h b/qv4codegen_p.h index f1163c14c1..41e24847c3 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -74,6 +74,7 @@ protected: void linearize(IR::Function *function); void defineFunction(AST::FunctionExpression *ast, bool isDeclaration = false); + int tempForLocalVariable(const QStringRef &string) const; void statement(AST::Statement *ast); void statement(AST::ExpressionNode *ast); diff --git a/qv4ir_p.h b/qv4ir_p.h index b13ebdc04c..a77a58fe76 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -591,11 +591,17 @@ struct Function { QList formals; QList locals; void (*code)(VM::Context *); + bool directEval; template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } Function(Module *module, const QString &name) - : module(module), pool(&module->pool), tempCount(0), code(0) { this->name = newString(name); } + : module(module) + , pool(&module->pool) + , tempCount(0) + , code(0) + , directEval(false) + { this->name = newString(name); } ~Function(); -- cgit v1.2.3 From 6ac720f095c7e89d11d1136b271f7493e080f988 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 8 May 2012 14:29:01 +0200 Subject: Optimized version of simple.js --- tests/simple2.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/simple2.js diff --git a/tests/simple2.js b/tests/simple2.js new file mode 100644 index 0000000000..484d1fe68c --- /dev/null +++ b/tests/simple2.js @@ -0,0 +1,19 @@ + +function main() +{ + var a = 1 + var b = 2 + var c = 10 + var d = 100 + + for (i = 0; i < 1000000; i = i + 1) { + if (a == 1) + d = d + a + b * c + else + d = 321 + } + + print("the result is", d) +} + +main() -- cgit v1.2.3 From 84df5088b888698bc2ae3770b1fecd9c8893bae4 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 8 May 2012 14:54:05 +0200 Subject: Fix nx representation of call expressions. --- qmljs_runtime.cpp | 3 ++- qv4codegen.cpp | 4 +--- qv4isel.cpp | 15 +++++++++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index ae6d5057cb..29cb423ef6 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -358,7 +358,8 @@ void __qmljs_call_activation_property(Context *context, Value *result, String *n context->formals = f->formalParameterList; context->formalCount = f->formalParameterCount; f->call(context); - __qmljs_copy(result, &context->result); + if (result) + __qmljs_copy(result, &context->result); } else { Q_ASSERT(!"not a function"); } diff --git a/qv4codegen.cpp b/qv4codegen.cpp index d6685c3240..f350dfb636 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -875,9 +875,7 @@ bool Codegen::visit(CallExpression *ast) (*args_it)->init(actual); args_it = &(*args_it)->next; } - const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), _block->CALL(*base, args)); - _expr.code = _block->TEMP(t); + _expr.code = _block->CALL(*base, args); return false; } diff --git a/qv4isel.cpp b/qv4isel.cpp index 06a9715081..fa99eacf41 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -194,17 +194,24 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu String *id = identifier(*call->base->asName()->id); amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); - loadTempAddress(AMD64_RSI, result); + if (result) + loadTempAddress(AMD64_RSI, result); + else + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); amd64_mov_reg_imm(_codePtr, AMD64_RDX, id); amd64_call_code(_codePtr, __qmljs_call_activation_property); amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); amd64_call_code(_codePtr, __qmljs_dispose_context); } -void InstructionSelection::visitExp(IR::Exp *) +void InstructionSelection::visitExp(IR::Exp *s) { -// Q_UNIMPLEMENTED(); -// assert(!"TODO"); + if (IR::Call *c = s->expr->asCall()) { + callActivationProperty(c, 0); + return; + } + Q_UNIMPLEMENTED(); + assert(!"TODO"); } void InstructionSelection::visitEnter(IR::Enter *) -- cgit v1.2.3 From 36e7fe1994d906853fbadecddd9fcf3281af9ec9 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 8 May 2012 16:49:16 +0200 Subject: Inline a code for add,sub,mul and div. --- qv4isel.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/qv4isel.cpp b/qv4isel.cpp index fa99eacf41..74bf86ab50 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -282,27 +282,27 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_call_code(_codePtr, __qmljs_get_activation_property); return; } else if (IR::Const *c = s->source->asConst()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); switch (c->type) { case IR::NullType: - amd64_call_code(_codePtr, __qmljs_init_null); + amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NULL_TYPE, 4); break; case IR::UndefinedType: - amd64_call_code(_codePtr, __qmljs_init_undefined); + amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, UNDEFINED_TYPE, 4); break; case IR::BoolType: - amd64_mov_reg_imm(_codePtr, AMD64_RDX, c->value != 0); - amd64_call_code(_codePtr, __qmljs_init_boolean); + amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, BOOLEAN_TYPE, 4); + amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(Value, booleanValue), c->value != 0, 1); break; case IR::NumberType: amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); - amd64_call_code(_codePtr, __qmljs_init_number); + amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NUMBER_TYPE, 4); + amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), X86_XMM0); break; default: @@ -348,6 +348,46 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RDX, l); loadTempAddress(AMD64_RCX, r); + uchar *label1 = 0, *label2 = 0, *label3 = 0; + + if (b->op == IR::OpMul || b->op == IR::OpAdd || b->op == IR::OpSub || b->op == IR::OpDiv) { + amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RDX, 0, NUMBER_TYPE, 4); + label1 = _codePtr; + amd64_branch8(_codePtr, X86_CC_NE, 0, 0); + amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RCX, 0, NUMBER_TYPE, 4); + label2 = _codePtr; + amd64_branch8(_codePtr, X86_CC_NE, 0, 0); + amd64_movsd_reg_membase(_codePtr, X86_XMM0, AMD64_RDX, offsetof(Value, numberValue)); + amd64_movsd_reg_membase(_codePtr, X86_XMM1, AMD64_RCX, offsetof(Value, numberValue)); + switch (b->op) { + case IR::OpAdd: + amd64_sse_addsd_reg_reg(_codePtr, X86_XMM0, X86_XMM1); + break; + case IR::OpSub: + amd64_sse_subsd_reg_reg(_codePtr, X86_XMM0, X86_XMM1); + break; + case IR::OpMul: + amd64_sse_mulsd_reg_reg(_codePtr, X86_XMM0, X86_XMM1); + break; + case IR::OpDiv: + amd64_sse_divsd_reg_reg(_codePtr, X86_XMM0, X86_XMM1); + break; + default: + Q_UNREACHABLE(); + } // switch + + amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NUMBER_TYPE, 4); + amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), X86_XMM0); + label3 = _codePtr; + amd64_jump32(_codePtr, 0); + return; + } + + if (label1 && label2) { + amd64_patch(label1, _codePtr); + amd64_patch(label2, _codePtr); + } + void (*op)(Context *, Value *, const Value *, const Value *) = 0; switch (b->op) { @@ -386,6 +426,8 @@ void InstructionSelection::visitMove(IR::Move *s) break; } amd64_call_code(_codePtr, op); + if (label3) + amd64_patch(label3, _codePtr); return; } } else if (IR::Call *c = s->source->asCall()) { -- cgit v1.2.3 From 36bb57e07e128400f74412df293de9439ac36924 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 8 May 2012 17:31:16 +0200 Subject: Fix typo --- qv4isel.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/qv4isel.cpp b/qv4isel.cpp index 74bf86ab50..2a75dd8887 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -380,7 +380,6 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), X86_XMM0); label3 = _codePtr; amd64_jump32(_codePtr, 0); - return; } if (label1 && label2) { -- cgit v1.2.3 From 48428701b26d61e1e7034160f7a2c2e0a9e044c7 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 8 May 2012 18:43:02 +0200 Subject: Generate inline code instead of calls to __qmljs_to_boolean. --- qv4isel.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/qv4isel.cpp b/qv4isel.cpp index 2a75dd8887..1cb3bc31ff 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -456,7 +456,22 @@ void InstructionSelection::visitCJump(IR::CJump *s) if (IR::Temp *t = s->cond->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); + + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, 0, 4); + amd64_alu_reg_imm(_codePtr, X86_CMP, AMD64_RAX, BOOLEAN_TYPE); + + uchar *label1 = _codePtr; + amd64_branch32(_codePtr, X86_CC_NE, 0, 0); + + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(Value, booleanValue), 1); + + uchar *label2 = _codePtr; + amd64_jump32(_codePtr, 0); + + amd64_patch(label1, _codePtr); amd64_call_code(_codePtr, __qmljs_to_boolean); + + amd64_patch(label2, _codePtr); amd64_alu_reg_imm_size(_codePtr, X86_CMP, X86_EAX, 0, 4); _patches[s->iftrue].append(_codePtr); amd64_branch32(_codePtr, X86_CC_NZ, 0, 1); -- cgit v1.2.3 From 5282ff06df5df581f00420c793697cf5d9d7d182 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 9 May 2012 11:04:57 +0200 Subject: Initial support for object literals --- main.cpp | 19 +++++++++++++++ qmljs_runtime.cpp | 34 ++++++++++++++++++++++---- qmljs_runtime.h | 1 + qv4codegen.cpp | 2 +- qv4isel.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- qv4isel_p.h | 1 + tests/obj.1.js | 8 +++++++ tests/simple2.js | 2 +- 8 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 tests/obj.1.js diff --git a/main.cpp b/main.cpp index 5ca39262ed..d2db6c86e2 100644 --- a/main.cpp +++ b/main.cpp @@ -23,7 +23,9 @@ static inline bool protect(const void *addr, size_t size) } namespace builtins { + using namespace QQmlJS::VM; + struct Print: FunctionObject { virtual void call(Context *ctx) @@ -38,6 +40,18 @@ struct Print: FunctionObject std::cout << std::endl; } }; + +struct ObjectCtor: FunctionObject +{ + virtual void construct(Context *ctx) + { + __qmljs_init_object(ctx, &ctx->result, new Object()); + } + + virtual void call(Context *) { + assert(!"not here"); + } +}; } // builtins @@ -82,8 +96,13 @@ void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &co VM::Context *ctx = new VM::Context; ctx->init(); ctx->activation = VM::Value::object(ctx, new VM::ArgumentsObject(ctx)); + ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("print")), VM::Value::object(ctx, new builtins::Print())); + + ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("Object")), + VM::Value::object(ctx, new builtins::ObjectCtor())); + foreach (IR::Function *function, module.functions) { if (function->name && ! function->name->isEmpty()) { ctx->activation.objectValue->put(VM::String::get(ctx, *function->name), diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 29cb423ef6..8dfcb827bd 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -219,9 +219,14 @@ void __qmljs_set_activation_property_string(Context *ctx, String *name, String * void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name) { - Q_UNUSED(ctx); - Q_ASSERT(object->type == OBJECT_TYPE); - object->objectValue->get(name, result); + if (object->type == OBJECT_TYPE) { + object->objectValue->get(name, result); + } else { + Value o; + __qmljs_to_object(ctx, &o, object); + assert(o.type == OBJECT_TYPE); + __qmljs_get_property(ctx, result, &o, name); + } } void __qmljs_get_activation_property(Context *ctx, Value *result, String *name) @@ -361,10 +366,29 @@ void __qmljs_call_activation_property(Context *context, Value *result, String *n if (result) __qmljs_copy(result, &context->result); } else { - Q_ASSERT(!"not a function"); + assert(!"not a function"); + } + } else { + assert(!"not a callable object"); + } +} + +void __qmljs_construct_activation_property(Context *context, Value *result, String *name) +{ + Value func; + context->parent->activation.objectValue->get(name, &func); + if (func.type == OBJECT_TYPE) { + if (FunctionObject *f = func.objectValue->asFunctionObject()) { + context->formals = f->formalParameterList; + context->formalCount = f->formalParameterCount; + f->construct(context); + if (result) + __qmljs_copy(result, &context->result); + } else { + assert(!"not a function"); } } else { - Q_ASSERT(!"not a callable object"); + assert(!"not a callable object"); } } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 438206bed1..61aa230c85 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -42,6 +42,7 @@ extern "C" { Context *__qmljs_new_context(Context *current, Value *thisObject, size_t argc); void __qmljs_dispose_context(Context *ctx); void __qmljs_call_activation_property(Context *, Value *result, String *name); +void __qmljs_construct_activation_property(Context *, Value *result, String *name); // constructors void __qmljs_init_undefined(Context *ctx, Value *result); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index f350dfb636..a53b78511d 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -244,7 +244,7 @@ void Codegen::operator()(AST::Program *node, IR::Module *module) IR::Expr *Codegen::member(IR::Expr *base, const QString *name) { - if (base->asTemp() || base->asName()) + if (base->asTemp() /*|| base->asName()*/) return _block->MEMBER(base, name); else { const unsigned t = _block->newTemp(); diff --git a/qv4isel.cpp b/qv4isel.cpp index 1cb3bc31ff..4fb6ca0153 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -204,6 +204,42 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu amd64_call_code(_codePtr, __qmljs_dispose_context); } +void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) +{ + int argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) + ++argc; + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, argc); + amd64_call_code(_codePtr, __qmljs_new_context); + + amd64_mov_reg_reg(_codePtr, AMD64_R15, AMD64_RAX, 8); + + argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) { + IR::Temp *t = it->expr->asTemp(); + Q_ASSERT(t != 0); + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R15, offsetof(Context, arguments), 8); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RAX, argc * sizeof(Value)); + loadTempAddress(AMD64_RSI, t); + amd64_call_code(_codePtr, __qmljs_copy); + ++argc; + } + + String *id = identifier(*call->base->asName()->id); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); + if (result) + loadTempAddress(AMD64_RSI, result); + else + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, id); + amd64_call_code(_codePtr, __qmljs_construct_activation_property); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); + amd64_call_code(_codePtr, __qmljs_dispose_context); +} + void InstructionSelection::visitExp(IR::Exp *s) { if (IR::Call *c = s->expr->asCall()) { @@ -321,6 +357,20 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_imm(_codePtr, AMD64_RDX, new String(*str->value)); amd64_call_code(_codePtr, __qmljs_init_string); return; + } else if (IR::New *ctor = s->source->asNew()) { + constructActivationProperty(ctor, t); + return; + } else if (IR::Member *m = s->source->asMember()) { + //__qmljs_get_property(ctx, result, object, name); + if (IR::Temp *base = m->base->asTemp()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + loadTempAddress(AMD64_RDX, base); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*m->name)); + amd64_call_code(_codePtr, __qmljs_get_property); + return; + } + assert(!"todo"); } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); @@ -435,6 +485,25 @@ void InstructionSelection::visitMove(IR::Move *s) return; } } + } else if (IR::Member *m = s->target->asMember()) { + if (IR::Temp *base = m->base->asTemp()) { + if (IR::Const *c = s->source->asConst()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, base); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); + amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); + amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); + amd64_call_code(_codePtr, __qmljs_set_property_number); + return; + } else if (IR::String *str = s->source->asString()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, base); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, VM::String::get(0, *str->value)); + amd64_call_code(_codePtr, __qmljs_set_property_string); + return; + } + } } } else { // inplace assignment, e.g. x += 1, ++x, ... @@ -472,7 +541,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) amd64_call_code(_codePtr, __qmljs_to_boolean); amd64_patch(label2, _codePtr); - amd64_alu_reg_imm_size(_codePtr, X86_CMP, X86_EAX, 0, 4); + amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 0, 4); _patches[s->iftrue].append(_codePtr); amd64_branch32(_codePtr, X86_CC_NZ, 0, 1); diff --git a/qv4isel_p.h b/qv4isel_p.h index 753da042ea..360fc38e6c 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -22,6 +22,7 @@ protected: int tempOffset(IR::Temp *t); void loadTempAddress(int reg, IR::Temp *t); void callActivationProperty(IR::Call *call, IR::Temp *result); + void constructActivationProperty(IR::New *call, IR::Temp *result); virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); diff --git a/tests/obj.1.js b/tests/obj.1.js new file mode 100644 index 0000000000..de2bd6318e --- /dev/null +++ b/tests/obj.1.js @@ -0,0 +1,8 @@ + +var point = { x: 123, y: 321 } +print(point, point.x, point.y) + +var obj = {} +obj.x = 123 +obj.str = "ciao" +print(obj, obj.str, obj.x) diff --git a/tests/simple2.js b/tests/simple2.js index 484d1fe68c..4a9f966da1 100644 --- a/tests/simple2.js +++ b/tests/simple2.js @@ -6,7 +6,7 @@ function main() var c = 10 var d = 100 - for (i = 0; i < 1000000; i = i + 1) { + for (var i = 0; i < 1000000; i = i + 1) { if (a == 1) d = d + a + b * c else -- cgit v1.2.3 From 80af77292ef8ee1ae7be8e543edacfe17ae3f96f Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 9 May 2012 11:28:57 +0200 Subject: Some progress on closures. --- qmljs_runtime.cpp | 12 ++++++++++++ qmljs_runtime.h | 7 +++++++ qv4isel.cpp | 13 +++++++++++++ tests/fun.1.js | 7 +++++++ 4 files changed, 39 insertions(+) create mode 100644 tests/fun.1.js diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 8dfcb827bd..bdc4d98ed0 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -192,6 +192,13 @@ void __qmljs_set_property_string(Context *ctx, Value *object, String *name, Stri object->objectValue->put(name, value, /*flag*/ 0); } +void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR::Function *function) +{ + Value value; + __qmljs_init_object(ctx, &value, new VM::ScriptFunction(function)); + object->objectValue->put(name, value, /*flag*/ 0); +} + void __qmljs_set_activation_property(Context *ctx, String *name, Value *value) { __qmljs_set_property(ctx, &ctx->activation, name, value); @@ -217,6 +224,11 @@ void __qmljs_set_activation_property_string(Context *ctx, String *name, String * __qmljs_set_property_string(ctx, &ctx->activation, name, value); } +void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Function *value) +{ + __qmljs_set_property_closure(ctx, &ctx->activation, name, value); +} + void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name) { if (object->type == OBJECT_TYPE) { diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 61aa230c85..70a5782458 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -6,6 +6,11 @@ #include namespace QQmlJS { + +namespace IR { +struct Function; +} + namespace VM { enum ValueType { @@ -83,10 +88,12 @@ void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *valu void __qmljs_set_property_boolean(Context *ctx, Value *object, String *name, bool value); void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double value); void __qmljs_set_property_string(Context *ctx, Value *object, String *name, String *value); +void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR::Function *function); void __qmljs_set_activation_property(Context *ctx, String *name, Value *value); void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool value); void __qmljs_set_activation_property_number(Context *ctx, String *name, double value); void __qmljs_set_activation_property_string(Context *ctx, String *name, String *value); +void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Function *function); void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name); void __qmljs_get_activation_property(Context *ctx, Value *result, String *name); void __qmljs_copy_activation_property(Context *ctx, String *name, String *other); diff --git a/qv4isel.cpp b/qv4isel.cpp index 4fb6ca0153..72a4bdbd3b 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -308,6 +308,12 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*other->id)); amd64_call_code(_codePtr, __qmljs_copy_activation_property); return; + } else if (IR::Closure *clos = s->source->asClosure()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, clos->value); + amd64_call_code(_codePtr, __qmljs_set_activation_property_closure); + return; } } else if (IR::Temp *t = s->target->asTemp()) { if (IR::Name *n = s->source->asName()) { @@ -502,6 +508,13 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_imm(_codePtr, AMD64_RCX, VM::String::get(0, *str->value)); amd64_call_code(_codePtr, __qmljs_set_property_string); return; + } else if (IR::Closure *clos = s->source->asClosure()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, base); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, clos->value); + amd64_call_code(_codePtr, __qmljs_set_property_closure); + return; } } } diff --git a/tests/fun.1.js b/tests/fun.1.js new file mode 100644 index 0000000000..9d73cade94 --- /dev/null +++ b/tests/fun.1.js @@ -0,0 +1,7 @@ + +function foo(a,b) { print(a, b) } +var foo2 = foo +var foo3 = function(a,b) { print(a, b); } +foo(1,2) +foo2(1,2) +foo3(1, 2) -- cgit v1.2.3 From 63e50c2e1cbbbfa86e24425e1b640d67d1ade845 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 9 May 2012 12:00:30 +0200 Subject: Bind the `this' object. --- qmljs_runtime.cpp | 13 ++++++++++- qmljs_runtime.h | 1 + qv4isel.cpp | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++----- qv4isel_p.h | 1 + tests/obj.2.js | 10 +++++++++ 5 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 tests/obj.2.js diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index bdc4d98ed0..0b8eca8052 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -367,11 +367,22 @@ void __qmljs_dispose_context(Context *ctx) } void __qmljs_call_activation_property(Context *context, Value *result, String *name) +{ + __qmljs_call_property(context, result, &context->parent->activation, name); +} + +void __qmljs_call_property(Context *context, Value *result, Value *base, String *name) { Value func; - context->parent->activation.objectValue->get(name, &func); + Value thisObject = *base; + if (thisObject.type != OBJECT_TYPE) + __qmljs_to_object(context, &thisObject, base); + + assert(thisObject.type == OBJECT_TYPE); + thisObject.objectValue->get(name, &func); if (func.type == OBJECT_TYPE) { if (FunctionObject *f = func.objectValue->asFunctionObject()) { + context->thisObject = thisObject; context->formals = f->formalParameterList; context->formalCount = f->formalParameterCount; f->call(context); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 70a5782458..d2e72bc241 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -48,6 +48,7 @@ Context *__qmljs_new_context(Context *current, Value *thisObject, size_t argc); void __qmljs_dispose_context(Context *ctx); void __qmljs_call_activation_property(Context *, Value *result, String *name); void __qmljs_construct_activation_property(Context *, Value *result, String *name); +void __qmljs_call_property(Context *context, Value *result, Value *base, String *name); // constructors void __qmljs_init_undefined(Context *ctx, Value *result); diff --git a/qv4isel.cpp b/qv4isel.cpp index 72a4bdbd3b..11340250c3 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -170,6 +170,9 @@ void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) { + IR::Name *baseName = call->base->asName(); + assert(baseName != 0); + int argc = 0; for (IR::ExprList *it = call->args; it; it = it->next) ++argc; @@ -192,7 +195,7 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu ++argc; } - String *id = identifier(*call->base->asName()->id); + String *id = identifier(*baseName->id); amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); if (result) loadTempAddress(AMD64_RSI, result); @@ -204,6 +207,49 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu amd64_call_code(_codePtr, __qmljs_dispose_context); } +void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) +{ + IR::Member *member = call->base->asMember(); + assert(member != 0); + assert(member->base->asTemp()); + + int argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) + ++argc; + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, argc); + amd64_call_code(_codePtr, __qmljs_new_context); + + amd64_mov_reg_reg(_codePtr, AMD64_R15, AMD64_RAX, 8); + + argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) { + IR::Temp *t = it->expr->asTemp(); + Q_ASSERT(t != 0); + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R15, offsetof(Context, arguments), 8); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RAX, argc * sizeof(Value)); + loadTempAddress(AMD64_RSI, t); + amd64_call_code(_codePtr, __qmljs_copy); + ++argc; + } + + // __qmljs_call_property(Context *context, Value *result, Value *base, String *name) + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); + if (result) + loadTempAddress(AMD64_RSI, result); + else + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + loadTempAddress(AMD64_RDX, member->base->asTemp()); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name)); + amd64_call_code(_codePtr, __qmljs_call_property); + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); + amd64_call_code(_codePtr, __qmljs_dispose_context); +} + void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) { int argc = 0; @@ -243,8 +289,13 @@ void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp * void InstructionSelection::visitExp(IR::Exp *s) { if (IR::Call *c = s->expr->asCall()) { - callActivationProperty(c, 0); - return; + if (c->base->asName()) { + callActivationProperty(c, 0); + return; + } else if (c->base->asMember()) { + callProperty(c, 0); + return; + } } Q_UNIMPLEMENTED(); assert(!"TODO"); @@ -317,11 +368,15 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Temp *t = s->target->asTemp()) { if (IR::Name *n = s->source->asName()) { - String *propertyName = identifier(*n->id); amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, propertyName); - amd64_call_code(_codePtr, __qmljs_get_activation_property); + if (*n->id == QLatin1String("this")) { // ### `this' should be a builtin. + amd64_call_code(_codePtr, __qmljs_get_thisObject); + } else { + String *propertyName = identifier(*n->id); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, propertyName); + amd64_call_code(_codePtr, __qmljs_get_activation_property); + } return; } else if (IR::Const *c = s->source->asConst()) { loadTempAddress(AMD64_RSI, t); diff --git a/qv4isel_p.h b/qv4isel_p.h index 360fc38e6c..e06b39c981 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -22,6 +22,7 @@ protected: int tempOffset(IR::Temp *t); void loadTempAddress(int reg, IR::Temp *t); void callActivationProperty(IR::Call *call, IR::Temp *result); + void callProperty(IR::Call *call, IR::Temp *result); void constructActivationProperty(IR::New *call, IR::Temp *result); virtual void visitExp(IR::Exp *); diff --git a/tests/obj.2.js b/tests/obj.2.js new file mode 100644 index 0000000000..e9b1be5be0 --- /dev/null +++ b/tests/obj.2.js @@ -0,0 +1,10 @@ + + +var obj = { + x: 10, + y: 20, + dump: function() { print("hello", this.x, this.y) } +} + +print(obj.x, obj.y, obj.dump) +obj.dump() -- cgit v1.2.3 From 210c00ed35403eae40cf52ba0eac3acb7e5448a6 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 9 May 2012 13:30:22 +0200 Subject: Some more progress on function calls --- qmljs_objects.cpp | 6 ++-- qv4codegen.cpp | 93 ++++++++++++++++++++++++++++++++----------------------- qv4codegen_p.h | 1 + qv4isel.cpp | 8 +++++ tests/fun.2.js | 9 ++++++ 5 files changed, 76 insertions(+), 41 deletions(-) create mode 100644 tests/fun.2.js diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index e628a24f42..dfc84a806f 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -114,8 +114,10 @@ void FunctionObject::call(Context *ctx) void FunctionObject::construct(Context *ctx) { - Q_UNUSED(ctx); - Q_UNIMPLEMENTED(); + __qmljs_init_object(ctx, &ctx->thisObject, new Object()); + // ### set the prototype + call(ctx); + ctx->result = ctx->thisObject; } ScriptFunction::ScriptFunction(IR::Function *function) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index a53b78511d..369d7e77a2 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -5,8 +5,8 @@ #include #include #include -#include #include +#include using namespace QQmlJS; using namespace AST; @@ -228,7 +228,7 @@ void Codegen::operator()(AST::Program *node, IR::Module *module) _block = _function->newBasicBlock(); _exitBlock = _function->newBasicBlock(); _returnAddress = _block->newTemp(); - _block->MOVE(_block->TEMP(_returnAddress), _block->CONST(IR::UndefinedType, 0)); + move(_block->TEMP(_returnAddress), _block->CONST(IR::UndefinedType, 0)); _exitBlock->RET(_exitBlock->TEMP(_returnAddress), IR::UndefinedType); program(node); @@ -248,7 +248,7 @@ IR::Expr *Codegen::member(IR::Expr *base, const QString *name) return _block->MEMBER(base, name); else { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), base); + move(_block->TEMP(t), base); return _block->MEMBER(_block->TEMP(t), name); } } @@ -259,7 +259,7 @@ IR::Expr *Codegen::subscript(IR::Expr *base, IR::Expr *index) return _block->SUBSCRIPT(base, index); else { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), base); + move(_block->TEMP(t), base); return _block->SUBSCRIPT(_block->TEMP(t), index); } } @@ -268,7 +268,7 @@ IR::Expr *Codegen::argument(IR::Expr *expr) { if (expr && ! expr->asTemp()) { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), expr); + move(_block->TEMP(t), expr); expr = _block->TEMP(t); } return expr; @@ -278,19 +278,32 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) { if (left && ! left->asTemp()) { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), left); + move(_block->TEMP(t), left); left = _block->TEMP(t); } if (right && ! right->asTemp()) { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), right); + move(_block->TEMP(t), right); right = _block->TEMP(t); } return _block->BINOP(op, left, right); } +void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) +{ + if (target->asMember()) { + if (! (source->asTemp() || source->asConst())) { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), source); + _block->MOVE(target, _block->TEMP(t), op); + return; + } + } + _block->MOVE(target, source, op); +} + void Codegen::accept(Node *node) { if (node) @@ -502,11 +515,11 @@ void Codegen::variableDeclaration(VariableDeclaration *ast) if (! _function->directEval) { const int index = tempForLocalVariable(ast->name); if (index != -1) { - _block->MOVE(_block->TEMP(index), *expr); + move(_block->TEMP(index), *expr); return; } } else { - _block->MOVE(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), *expr); + move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), *expr); } } } @@ -679,13 +692,13 @@ bool Codegen::visit(Expression *ast) bool Codegen::visit(ArrayLiteral *ast) { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), _block->NEW(_block->NAME(QLatin1String("Array"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); + move(_block->TEMP(t), _block->NEW(_block->NAME(QLatin1String("Array"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); int index = 0; for (ElementList *it = ast->elements; it; it = it->next) { for (Elision *elision = it->elision; elision; elision = elision->next) ++index; Result expr = expression(it->expression); - _block->MOVE(subscript(_block->TEMP(t), _block->CONST(IR::NumberType, index)), *expr); + move(subscript(_block->TEMP(t), _block->CONST(IR::NumberType, index)), *expr); ++index; } for (Elision *elision = ast->elision; elision; elision = elision->next) @@ -738,11 +751,11 @@ bool Codegen::visit(BinaryExpression *ast) condition(ast->left, iftrue, iffalse); _block = iffalse; - _block->MOVE(_block->TEMP(r), _block->CONST(IR::BoolType, 0)); + move(_block->TEMP(r), _block->CONST(IR::BoolType, 0)); _block->JUMP(endif); _block = iftrue; - _block->MOVE(_block->TEMP(r), *expression(ast->right)); + move(_block->TEMP(r), *expression(ast->right)); if (! _block->isTerminated()) _block->JUMP(endif); @@ -761,10 +774,10 @@ bool Codegen::visit(BinaryExpression *ast) IR::BasicBlock *endif = _function->newBasicBlock(); const unsigned r = _block->newTemp(); - _block->MOVE(_block->TEMP(r), *expression(ast->left)); + move(_block->TEMP(r), *expression(ast->left)); _block->CJUMP(_block->TEMP(r), endif, iffalse); _block = iffalse; - _block->MOVE(_block->TEMP(r), *expression(ast->right)); + move(_block->TEMP(r), *expression(ast->right)); if (! _block->isTerminated()) _block->JUMP(endif); @@ -784,11 +797,11 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::Assign: if (_expr.accept(nx)) { - _block->MOVE(*left, *right); + move(*left, *right); } else { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), *right); - _block->MOVE(*left, _block->TEMP(t)); + move(_block->TEMP(t), *right); + move(*left, _block->TEMP(t)); _expr.code = _block->TEMP(t); } break; @@ -804,7 +817,7 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::InplaceRightShift: case QSOperator::InplaceURightShift: case QSOperator::InplaceXor: { - _block->MOVE(*left, *right, baseOp(ast->op)); + move(*left, *right, baseOp(ast->op)); if (_expr.accept(nx)) { // nothing to do } else { @@ -831,7 +844,7 @@ bool Codegen::visit(BinaryExpression *ast) _expr.code = e; else { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), e); + move(_block->TEMP(t), e); _expr.code = _block->TEMP(t); } } @@ -853,7 +866,7 @@ bool Codegen::visit(BinaryExpression *ast) _expr.code = e; else { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), e); + move(_block->TEMP(t), e); _expr.code = _block->TEMP(t); } break; @@ -890,11 +903,11 @@ bool Codegen::visit(ConditionalExpression *ast) condition(ast->expression, iftrue, iffalse); _block = iftrue; - _block->MOVE(_block->TEMP(t), *expression(ast->ok)); + move(_block->TEMP(t), *expression(ast->ok)); _block->JUMP(endif); _block = iffalse; - _block->MOVE(_block->TEMP(t), *expression(ast->ko)); + move(_block->TEMP(t), *expression(ast->ko)); _block->JUMP(endif); _block = endif; @@ -970,7 +983,9 @@ bool Codegen::visit(NewMemberExpression *ast) (*args_it)->init(actual); args_it = &(*args_it)->next; } - _expr.code = _block->NEW(*base, args); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->NEW(*base, args)); + _expr.code = _block->TEMP(t); return false; } @@ -978,7 +993,7 @@ bool Codegen::visit(NotExpression *ast) { Result expr = expression(ast->expression); const unsigned r = _block->newTemp(); - _block->MOVE(_block->TEMP(r), _block->UNOP(IR::OpNot, *expr)); + move(_block->TEMP(r), _block->UNOP(IR::OpNot, *expr)); _expr.code = _block->TEMP(r); return false; } @@ -998,11 +1013,11 @@ bool Codegen::visit(NumericLiteral *ast) bool Codegen::visit(ObjectLiteral *ast) { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), _block->NEW(_block->NAME(QLatin1String("Object"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); + move(_block->TEMP(t), _block->NEW(_block->NAME(QLatin1String("Object"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); for (PropertyNameAndValueList *it = ast->properties; it; it = it->next) { QString name = propertyName(it->name); Result value = expression(it->value); - _block->MOVE(member(_block->TEMP(t), _function->newString(name)), *value); + move(member(_block->TEMP(t), _function->newString(name)), *value); } _expr.code = _block->TEMP(t); return false; @@ -1012,11 +1027,11 @@ bool Codegen::visit(PostDecrementExpression *ast) { Result expr = expression(ast->base); if (_expr.accept(nx)) { - _block->MOVE(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub); + move(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub); } else { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), *expr); - _block->MOVE(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub); + move(_block->TEMP(t), *expr); + move(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub); _expr.code = _block->TEMP(t); } return false; @@ -1026,11 +1041,11 @@ bool Codegen::visit(PostIncrementExpression *ast) { Result expr = expression(ast->base); if (_expr.accept(nx)) { - _block->MOVE(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd); + move(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd); } else { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), *expr); - _block->MOVE(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd); + move(_block->TEMP(t), *expr); + move(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd); _expr.code = _block->TEMP(t); } return false; @@ -1039,7 +1054,7 @@ bool Codegen::visit(PostIncrementExpression *ast) bool Codegen::visit(PreDecrementExpression *ast) { Result expr = expression(ast->expression); - _block->MOVE(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub); + move(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub); if (_expr.accept(nx)) { // nothing to do } else { @@ -1051,7 +1066,7 @@ bool Codegen::visit(PreDecrementExpression *ast) bool Codegen::visit(PreIncrementExpression *ast) { Result expr = expression(ast->expression); - _block->MOVE(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd); + move(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd); if (_expr.accept(nx)) { // nothing to do @@ -1083,7 +1098,7 @@ bool Codegen::visit(TildeExpression *ast) { Result expr = expression(ast->expression); const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), _block->UNOP(IR::OpCompl, *expr)); + move(_block->TEMP(t), _block->UNOP(IR::OpCompl, *expr)); _expr.code = _block->TEMP(t); return false; } @@ -1107,7 +1122,7 @@ bool Codegen::visit(UnaryMinusExpression *ast) { Result expr = expression(ast->expression); const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), _block->UNOP(IR::OpUMinus, *expr)); + move(_block->TEMP(t), _block->UNOP(IR::OpUMinus, *expr)); _expr.code = _block->TEMP(t); return false; } @@ -1116,7 +1131,7 @@ bool Codegen::visit(UnaryPlusExpression *ast) { Result expr = expression(ast->expression); const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), _block->UNOP(IR::OpUPlus, *expr)); + move(_block->TEMP(t), _block->UNOP(IR::OpUPlus, *expr)); _expr.code = _block->TEMP(t); return false; } @@ -1536,7 +1551,7 @@ bool Codegen::visit(ReturnStatement *ast) { if (ast->expression) { Result expr = expression(ast->expression); - _block->MOVE(_block->TEMP(_returnAddress), *expr); + move(_block->TEMP(_returnAddress), *expr); } _block->JUMP(_exitBlock); diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 41e24847c3..e58ceb5038 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -71,6 +71,7 @@ protected: IR::Expr *subscript(IR::Expr *base, IR::Expr *index); IR::Expr *argument(IR::Expr *expr); IR::Expr *binop(IR::AluOp op, IR::Expr *left, IR::Expr *right); + void move(IR::Expr *target, IR::Expr *source, IR::AluOp op = IR::OpInvalid); void linearize(IR::Function *function); void defineFunction(AST::FunctionExpression *ast, bool isDeclaration = false); diff --git a/qv4isel.cpp b/qv4isel.cpp index 11340250c3..e574392c04 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -563,6 +563,14 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_imm(_codePtr, AMD64_RCX, VM::String::get(0, *str->value)); amd64_call_code(_codePtr, __qmljs_set_property_string); return; + } else if (IR::Temp *t = s->source->asTemp()) { + // __qmljs_set_property(ctx, object, name, value); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, base); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); + loadTempAddress(AMD64_RCX, t); + amd64_call_code(_codePtr, __qmljs_set_property); + return; } else if (IR::Closure *clos = s->source->asClosure()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, base); diff --git a/tests/fun.2.js b/tests/fun.2.js new file mode 100644 index 0000000000..7eabdd0a93 --- /dev/null +++ b/tests/fun.2.js @@ -0,0 +1,9 @@ + +function Point(x,y) { + this.x = 10 + this.y = 20 +} + +var pt = new Point(10, 20) +print(pt.x, pt.y) + -- cgit v1.2.3 From 7e106fa2e7367f33473a8c20032d0721b1b224eb Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 9 May 2012 13:51:44 +0200 Subject: Fix instruction selections for closures. --- qmljs_runtime.cpp | 5 +++++ qmljs_runtime.h | 1 + qv4isel.cpp | 14 ++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 0b8eca8052..b9811af066 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -15,6 +15,11 @@ Value Value::string(Context *ctx, const QString &s) extern "C" { +void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos) +{ + __qmljs_init_object(ctx, result, new ScriptFunction(clos)); +} + void __qmljs_string_literal_undefined(Context *ctx, Value *result) { __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("undefined"))); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index d2e72bc241..7e815a7799 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -57,6 +57,7 @@ void __qmljs_init_boolean(Context *ctx, Value *result, bool value); void __qmljs_init_number(Context *ctx, Value *result, double number); void __qmljs_init_string(Context *ctx, Value *result, String *string); void __qmljs_init_object(Context *ctx, Value *result, Object *object); +void __qmljs_init_closure(Context *, Value *result, IR::Function *clos); bool __qmljs_is_function(Context *ctx, const Value *value); diff --git a/qv4isel.cpp b/qv4isel.cpp index e574392c04..16d278dd35 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -23,6 +23,10 @@ using namespace QQmlJS; using namespace QQmlJS::x86_64; using namespace QQmlJS::VM; +namespace { +QTextStream qout(stdout, QIODevice::WriteOnly); +} + static inline void amd64_patch (unsigned char* code, gpointer target) { @@ -136,6 +140,7 @@ void InstructionSelection::operator()(IR::Function *function) #ifndef NO_UDIS86 static bool showCode = !qgetenv("SHOW_CODE").isNull(); if (showCode) { + printf("code size: %ld bytes\n", (_codePtr - _code)); ud_t ud_obj; ud_init(&ud_obj); @@ -418,6 +423,12 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_imm(_codePtr, AMD64_RDX, new String(*str->value)); amd64_call_code(_codePtr, __qmljs_init_string); return; + } else if (IR::Closure *clos = s->source->asClosure()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, clos->value); + amd64_call_code(_codePtr, __qmljs_init_closure); + return; } else if (IR::New *ctor = s->source->asNew()) { constructActivationProperty(ctor, t); return; @@ -584,7 +595,10 @@ void InstructionSelection::visitMove(IR::Move *s) } else { // inplace assignment, e.g. x += 1, ++x, ... } + Q_UNIMPLEMENTED(); + s->dump(qout, IR::Stmt::MIR); + qout << endl; assert(!"TODO"); } -- cgit v1.2.3 From eb46225e113aee00583a4d6e9806ec16687e43ce Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 9 May 2012 14:30:59 +0200 Subject: Inline __qmljs_copy. --- qv4isel.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qv4isel.cpp b/qv4isel.cpp index 16d278dd35..4be1cb1cbc 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -415,7 +415,10 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Temp *t2 = s->source->asTemp()) { loadTempAddress(AMD64_RDI, t); loadTempAddress(AMD64_RSI, t2); - amd64_call_code(_codePtr, __qmljs_copy); + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, 0, 4); + amd64_mov_membase_reg(_codePtr, AMD64_RDI, 0, AMD64_RAX, 4); + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(Value, numberValue), 8); + amd64_mov_membase_reg(_codePtr, AMD64_RDI, offsetof(Value, numberValue), AMD64_RAX, 8); return; } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); -- cgit v1.2.3 From d8b7b6067851ec45dd72d2e25ae2b170784b0108 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 9 May 2012 14:34:36 +0200 Subject: Rename simple2.js --- tests/simple.2.js | 19 +++++++++++++++++++ tests/simple2.js | 19 ------------------- 2 files changed, 19 insertions(+), 19 deletions(-) create mode 100644 tests/simple.2.js delete mode 100644 tests/simple2.js diff --git a/tests/simple.2.js b/tests/simple.2.js new file mode 100644 index 0000000000..4a9f966da1 --- /dev/null +++ b/tests/simple.2.js @@ -0,0 +1,19 @@ + +function main() +{ + var a = 1 + var b = 2 + var c = 10 + var d = 100 + + for (var i = 0; i < 1000000; i = i + 1) { + if (a == 1) + d = d + a + b * c + else + d = 321 + } + + print("the result is", d) +} + +main() diff --git a/tests/simple2.js b/tests/simple2.js deleted file mode 100644 index 4a9f966da1..0000000000 --- a/tests/simple2.js +++ /dev/null @@ -1,19 +0,0 @@ - -function main() -{ - var a = 1 - var b = 2 - var c = 10 - var d = 100 - - for (var i = 0; i < 1000000; i = i + 1) { - if (a == 1) - d = d + a + b * c - else - d = 321 - } - - print("the result is", d) -} - -main() -- cgit v1.2.3 From 6d0f09908d5c9a4e78bb369d583943830811e3cc Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 9 May 2012 15:06:30 +0200 Subject: Set the prototype delegator. --- qmljs_objects.cpp | 7 +++++-- qmljs_runtime.cpp | 15 +++++++++++++-- qmljs_runtime.h | 1 + tests/fun.2.js | 5 +++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index dfc84a806f..ee6399a464 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -115,9 +115,12 @@ void FunctionObject::call(Context *ctx) void FunctionObject::construct(Context *ctx) { __qmljs_init_object(ctx, &ctx->thisObject, new Object()); - // ### set the prototype call(ctx); - ctx->result = ctx->thisObject; + Value proto; + if (get(String::get(ctx, QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol + if (proto.type == OBJECT_TYPE) + ctx->thisObject.objectValue->prototype = proto.objectValue; + } } ScriptFunction::ScriptFunction(IR::Function *function) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index b9811af066..b9b1a0042d 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -402,16 +402,27 @@ void __qmljs_call_property(Context *context, Value *result, Value *base, String } void __qmljs_construct_activation_property(Context *context, Value *result, String *name) +{ + __qmljs_construct_property(context, result, &context->activation, name); +} + +void __qmljs_construct_property(Context *context, Value *result, Value *base, String *name) { Value func; - context->parent->activation.objectValue->get(name, &func); + Value thisObject = *base; + if (thisObject.type != OBJECT_TYPE) + __qmljs_to_object(context, &thisObject, base); + + assert(thisObject.type == OBJECT_TYPE); + thisObject.objectValue->get(name, &func); if (func.type == OBJECT_TYPE) { if (FunctionObject *f = func.objectValue->asFunctionObject()) { + context->thisObject = thisObject; context->formals = f->formalParameterList; context->formalCount = f->formalParameterCount; f->construct(context); if (result) - __qmljs_copy(result, &context->result); + __qmljs_copy(result, &context->thisObject); } else { assert(!"not a function"); } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 7e815a7799..ab30c5c3ab 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -49,6 +49,7 @@ void __qmljs_dispose_context(Context *ctx); void __qmljs_call_activation_property(Context *, Value *result, String *name); void __qmljs_construct_activation_property(Context *, Value *result, String *name); void __qmljs_call_property(Context *context, Value *result, Value *base, String *name); +void __qmljs_construct_property(Context *context, Value *result, Value *base, String *name); // constructors void __qmljs_init_undefined(Context *ctx, Value *result); diff --git a/tests/fun.2.js b/tests/fun.2.js index 7eabdd0a93..c3fb613ac6 100644 --- a/tests/fun.2.js +++ b/tests/fun.2.js @@ -4,6 +4,11 @@ function Point(x,y) { this.y = 20 } +Point.prototype = { + print: function() { print(this.x, this.y) } +} + var pt = new Point(10, 20) print(pt.x, pt.y) +pt.print() -- cgit v1.2.3 From 908cc655471ed5da3622ce05f30e864bf67b1f1f Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 9 May 2012 15:47:55 +0200 Subject: Add callValue. --- main.cpp | 2 +- qmljs_objects.cpp | 5 +++-- qmljs_objects.h | 3 ++- qmljs_runtime.cpp | 24 +++++++++++++++++++++--- qmljs_runtime.h | 1 + qv4isel.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ qv4isel_p.h | 1 + 7 files changed, 74 insertions(+), 7 deletions(-) diff --git a/main.cpp b/main.cpp index d2db6c86e2..3266a355db 100644 --- a/main.cpp +++ b/main.cpp @@ -106,7 +106,7 @@ void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &co foreach (IR::Function *function, module.functions) { if (function->name && ! function->name->isEmpty()) { ctx->activation.objectValue->put(VM::String::get(ctx, *function->name), - VM::Value::object(ctx, new VM::ScriptFunction(function))); + VM::Value::object(ctx, new VM::ScriptFunction(ctx, function))); } } codeByName.value(QLatin1String("%entry"))->code(ctx); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index ee6399a464..7f4b8553cd 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -123,8 +123,9 @@ void FunctionObject::construct(Context *ctx) } } -ScriptFunction::ScriptFunction(IR::Function *function) - : function(function) +ScriptFunction::ScriptFunction(Context *context, IR::Function *function) + : context(context) + , function(function) { formalParameterCount = function->formals.size(); if (formalParameterCount) { diff --git a/qmljs_objects.h b/qmljs_objects.h index f7f5f1506e..15df292ee3 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -246,9 +246,10 @@ struct FunctionObject: Object { }; struct ScriptFunction: FunctionObject { + Context *context; IR::Function *function; - ScriptFunction(IR::Function *function); + ScriptFunction(Context *context, IR::Function *function); virtual ~ScriptFunction(); virtual void call(Context *ctx); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index b9b1a0042d..305362cea8 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -17,7 +17,7 @@ extern "C" { void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos) { - __qmljs_init_object(ctx, result, new ScriptFunction(clos)); + __qmljs_init_object(ctx, result, new ScriptFunction(ctx, clos)); } void __qmljs_string_literal_undefined(Context *ctx, Value *result) @@ -200,7 +200,7 @@ void __qmljs_set_property_string(Context *ctx, Value *object, String *name, Stri void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR::Function *function) { Value value; - __qmljs_init_object(ctx, &value, new VM::ScriptFunction(function)); + __qmljs_init_object(ctx, &value, new VM::ScriptFunction(ctx, function)); object->objectValue->put(name, value, /*flag*/ 0); } @@ -387,7 +387,25 @@ void __qmljs_call_property(Context *context, Value *result, Value *base, String thisObject.objectValue->get(name, &func); if (func.type == OBJECT_TYPE) { if (FunctionObject *f = func.objectValue->asFunctionObject()) { - context->thisObject = thisObject; + __qmljs_init_null(context, &context->thisObject); + context->formals = f->formalParameterList; + context->formalCount = f->formalParameterCount; + f->call(context); + if (result) + __qmljs_copy(result, &context->result); + } else { + assert(!"not a function"); + } + } else { + assert(!"not a callable object"); + } +} + +void __qmljs_call_value(Context *context, Value *result, Value *func) +{ + if (func->type == OBJECT_TYPE) { + if (FunctionObject *f = func->objectValue->asFunctionObject()) { + __qmljs_init_null(context, &context->thisObject); context->formals = f->formalParameterList; context->formalCount = f->formalParameterCount; f->call(context); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index ab30c5c3ab..61d620f4d5 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -50,6 +50,7 @@ void __qmljs_call_activation_property(Context *, Value *result, String *name); void __qmljs_construct_activation_property(Context *, Value *result, String *name); void __qmljs_call_property(Context *context, Value *result, Value *base, String *name); void __qmljs_construct_property(Context *context, Value *result, Value *base, String *name); +void __qmljs_call_value(Context *context, Value *result, Value *func); // constructors void __qmljs_init_undefined(Context *ctx, Value *result); diff --git a/qv4isel.cpp b/qv4isel.cpp index 4be1cb1cbc..35a770683e 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -212,6 +212,45 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu amd64_call_code(_codePtr, __qmljs_dispose_context); } + +void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) +{ + IR::Temp *baseTemp = call->base->asTemp(); + assert(baseTemp != 0); + + int argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) + ++argc; + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, argc); + amd64_call_code(_codePtr, __qmljs_new_context); + + amd64_mov_reg_reg(_codePtr, AMD64_R15, AMD64_RAX, 8); + + argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) { + IR::Temp *t = it->expr->asTemp(); + Q_ASSERT(t != 0); + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R15, offsetof(Context, arguments), 8); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RAX, argc * sizeof(Value)); + loadTempAddress(AMD64_RSI, t); + amd64_call_code(_codePtr, __qmljs_copy); + ++argc; + } + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); + if (result) + loadTempAddress(AMD64_RSI, result); + else + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + loadTempAddress(AMD64_RDX, baseTemp); + amd64_call_code(_codePtr, __qmljs_call_value); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); + amd64_call_code(_codePtr, __qmljs_dispose_context); +} + void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) { IR::Member *member = call->base->asMember(); @@ -297,6 +336,9 @@ void InstructionSelection::visitExp(IR::Exp *s) if (c->base->asName()) { callActivationProperty(c, 0); return; + } else if (c->base->asTemp()) { + callValue(c, 0); + return; } else if (c->base->asMember()) { callProperty(c, 0); return; @@ -558,6 +600,9 @@ void InstructionSelection::visitMove(IR::Move *s) if (c->base->asName()) { callActivationProperty(c, t); return; + } else if (c->base->asTemp()) { + callValue(c, t); + return; } } } else if (IR::Member *m = s->target->asMember()) { diff --git a/qv4isel_p.h b/qv4isel_p.h index e06b39c981..77bf4bbca9 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -24,6 +24,7 @@ protected: void callActivationProperty(IR::Call *call, IR::Temp *result); void callProperty(IR::Call *call, IR::Temp *result); void constructActivationProperty(IR::New *call, IR::Temp *result); + void callValue(IR::Call *call, IR::Temp *result); virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); -- cgit v1.2.3 From 2b16365d6fa9a22786ec12d2d6387f3eb19a193b Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 9 May 2012 17:04:25 +0200 Subject: Fix function calls --- qmljs_runtime.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 305362cea8..49380d0502 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -378,16 +378,22 @@ void __qmljs_call_activation_property(Context *context, Value *result, String *n void __qmljs_call_property(Context *context, Value *result, Value *base, String *name) { + Value baseObject; + Value thisObject; + if (base) { + if (baseObject.type != OBJECT_TYPE) + __qmljs_to_object(context, &baseObject, base); + thisObject = baseObject; + } else { + baseObject = context->activation; + __qmljs_init_null(context, &thisObject); + } + assert(baseObject.type == OBJECT_TYPE); Value func; - Value thisObject = *base; - if (thisObject.type != OBJECT_TYPE) - __qmljs_to_object(context, &thisObject, base); - - assert(thisObject.type == OBJECT_TYPE); - thisObject.objectValue->get(name, &func); + baseObject.objectValue->get(name, &func); if (func.type == OBJECT_TYPE) { if (FunctionObject *f = func.objectValue->asFunctionObject()) { - __qmljs_init_null(context, &context->thisObject); + context->thisObject = thisObject; context->formals = f->formalParameterList; context->formalCount = f->formalParameterCount; f->call(context); -- cgit v1.2.3 From 8200e41180c657f6b2955b16ca5655ff3c6b7253 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 9 May 2012 17:28:55 +0200 Subject: Initialize baseObject. --- qmljs_runtime.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 49380d0502..3486a65500 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -381,14 +381,15 @@ void __qmljs_call_property(Context *context, Value *result, Value *base, String Value baseObject; Value thisObject; if (base) { + baseObject = *base; if (baseObject.type != OBJECT_TYPE) - __qmljs_to_object(context, &baseObject, base); + __qmljs_to_object(context, &baseObject, &baseObject); + assert(baseObject.type == OBJECT_TYPE); thisObject = baseObject; } else { baseObject = context->activation; __qmljs_init_null(context, &thisObject); } - assert(baseObject.type == OBJECT_TYPE); Value func; baseObject.objectValue->get(name, &func); if (func.type == OBJECT_TYPE) { -- cgit v1.2.3 From c75421ee43f22df8c2405302e6118548c97f13bc Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Thu, 10 May 2012 10:56:02 +0200 Subject: Add the String object constructor. --- main.cpp | 26 +++++++++++++++++++++++++- qmljs_objects.h | 15 +++++++++++++++ qmljs_runtime.h | 3 +++ tests/string.1.js | 7 +++++++ 4 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tests/string.1.js diff --git a/main.cpp b/main.cpp index 3266a355db..ef5d6eb520 100644 --- a/main.cpp +++ b/main.cpp @@ -48,10 +48,31 @@ struct ObjectCtor: FunctionObject __qmljs_init_object(ctx, &ctx->result, new Object()); } - virtual void call(Context *) { + virtual void call(Context *) + { assert(!"not here"); } }; + +struct StringCtor: FunctionObject +{ + virtual void construct(Context *ctx) + { + Value arg = ctx->argument(0); + __qmljs_to_string(ctx, &arg, &arg); + __qmljs_init_object(ctx, &ctx->thisObject, new StringObject(arg)); + } + + virtual void call(Context *ctx) + { + const Value arg = ctx->argument(0); + if (arg.is(UNDEFINED_TYPE)) + __qmljs_init_string(ctx, &ctx->result, String::get(ctx, QString())); + else + __qmljs_to_string(ctx, &ctx->result, &arg); + } +}; + } // builtins @@ -103,6 +124,9 @@ void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &co ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("Object")), VM::Value::object(ctx, new builtins::ObjectCtor())); + ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("String")), + VM::Value::object(ctx, new builtins::StringCtor())); + foreach (IR::Function *function, module.functions) { if (function->name && ! function->name->isEmpty()) { ctx->activation.objectValue->put(VM::String::get(ctx, *function->name), diff --git a/qmljs_objects.h b/qmljs_objects.h index 15df292ee3..dbb1ab8d8b 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -277,6 +277,21 @@ struct Context { String **formals; size_t formalCount; + inline Value argument(size_t index = 0) + { + Value arg; + getArgument(&arg, index); + return arg; + } + + inline void getArgument(Value *result, size_t index) + { + if (index < argumentCount) + *result = arguments[index]; + else + __qmljs_init_undefined(this, result); + } + void init() { parent = 0; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 61d620f4d5..c75e502060 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -171,6 +171,9 @@ struct Value { String *stringValue; }; + inline bool is(ValueType t) const { return type == t; } + inline bool isNot(ValueType t) const { return type != t; } + static inline Value boolean(Context *ctx, bool value) { Value v; __qmljs_init_boolean(ctx, &v, value); diff --git a/tests/string.1.js b/tests/string.1.js new file mode 100644 index 0000000000..1495552e33 --- /dev/null +++ b/tests/string.1.js @@ -0,0 +1,7 @@ + +var s = String(123) + 1 +print(s) + +var s2 = new String(123) +print(s2) + -- cgit v1.2.3 From 14ce5bb3897d0c19beddcf4b0bdabc8a05e310f0 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Thu, 10 May 2012 12:14:20 +0200 Subject: Fix constructors and some work on the String prototype. --- main.cpp | 41 +++++++++++++++++++++++++++++++++++------ qmljs_objects.cpp | 10 +++++----- qmljs_objects.h | 11 +++++++++++ qmljs_runtime.cpp | 8 ++++++++ qv4codegen.cpp | 7 +++++++ qv4isel.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- qv4isel_p.h | 1 + tests/string.1.js | 8 +++++++- 8 files changed, 127 insertions(+), 14 deletions(-) diff --git a/main.cpp b/main.cpp index ef5d6eb520..8077479a4f 100644 --- a/main.cpp +++ b/main.cpp @@ -73,6 +73,30 @@ struct StringCtor: FunctionObject } }; +struct StringPrototype: Object +{ + StringPrototype(Context *ctx, FunctionObject *ctor) + { + setProperty(ctx, QLatin1String("constructor"), Value::object(ctx, ctor)); + setProperty(ctx, QLatin1String("toString"), toString); + } + + void setProperty(Context *ctx, const QString &name, const Value &value) + { + put(String::get(ctx, name), value); + } + + void setProperty(Context *ctx, const QString &name, void (*code)(Context *)) + { + setProperty(ctx, name, Value::object(ctx, new NativeFunction(code))); + } + + static void toString(Context *ctx) + { + __qmljs_to_string(ctx, &ctx->result, &ctx->thisObject); + } +}; + } // builtins @@ -116,20 +140,25 @@ void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &co VM::Context *ctx = new VM::Context; ctx->init(); - ctx->activation = VM::Value::object(ctx, new VM::ArgumentsObject(ctx)); - ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("print")), + VM::String *prototype = VM::String::get(ctx, QLatin1String("prototype")); + + VM::Object *globalObject = new VM::ArgumentsObject(ctx); + __qmljs_init_object(ctx, &ctx->activation, globalObject); + + globalObject->put(VM::String::get(ctx, QLatin1String("print")), VM::Value::object(ctx, new builtins::Print())); - ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("Object")), + globalObject->put(VM::String::get(ctx, QLatin1String("Object")), VM::Value::object(ctx, new builtins::ObjectCtor())); - ctx->activation.objectValue->put(VM::String::get(ctx, QLatin1String("String")), - VM::Value::object(ctx, new builtins::StringCtor())); + VM::FunctionObject *stringCtor = new builtins::StringCtor(); + stringCtor->put(prototype, VM::Value::object(ctx, new builtins::StringPrototype(ctx, stringCtor))); + globalObject->put(VM::String::get(ctx, QLatin1String("String")), VM::Value::object(ctx, stringCtor)); foreach (IR::Function *function, module.functions) { if (function->name && ! function->name->isEmpty()) { - ctx->activation.objectValue->put(VM::String::get(ctx, *function->name), + globalObject->put(VM::String::get(ctx, *function->name), VM::Value::object(ctx, new VM::ScriptFunction(ctx, function))); } } diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 7f4b8553cd..7a373516f8 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -116,11 +116,6 @@ void FunctionObject::construct(Context *ctx) { __qmljs_init_object(ctx, &ctx->thisObject, new Object()); call(ctx); - Value proto; - if (get(String::get(ctx, QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol - if (proto.type == OBJECT_TYPE) - ctx->thisObject.objectValue->prototype = proto.objectValue; - } } ScriptFunction::ScriptFunction(Context *context, IR::Function *function) @@ -146,6 +141,11 @@ void ScriptFunction::call(VM::Context *ctx) function->code(ctx); } +void ScriptFunction::construct(VM::Context *ctx) +{ + function->code(ctx); +} + Value *ArgumentsObject::getProperty(String *name, PropertyAttributes *attributes) { if (context) { diff --git a/qmljs_objects.h b/qmljs_objects.h index dbb1ab8d8b..323367a9a8 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -245,6 +245,14 @@ struct FunctionObject: Object { virtual void construct(Context *ctx); }; +struct NativeFunction: FunctionObject { + void (*code)(Context *); + + NativeFunction(void (*code)(Context *)): code(code) {} + virtual void call(Context *ctx) { code(ctx); } + virtual void construct(Context *ctx) { code(ctx); } +}; + struct ScriptFunction: FunctionObject { Context *context; IR::Function *function; @@ -253,6 +261,7 @@ struct ScriptFunction: FunctionObject { virtual ~ScriptFunction(); virtual void call(Context *ctx); + virtual void construct(Context *ctx); }; struct ErrorObject: Object { @@ -276,6 +285,7 @@ struct Context { Value result; String **formals; size_t formalCount; + bool calledAsConstructor; inline Value argument(size_t index = 0) { @@ -303,6 +313,7 @@ struct Context { result.type = UNDEFINED_TYPE; formals = 0; formalCount = 0; + calledAsConstructor = false; } }; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 3486a65500..b0a3c100ea 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -445,7 +445,15 @@ void __qmljs_construct_property(Context *context, Value *result, Value *base, St context->thisObject = thisObject; context->formals = f->formalParameterList; context->formalCount = f->formalParameterCount; + context->calledAsConstructor = true; f->construct(context); + + Value proto; + if (f->get(String::get(context, QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol + if (proto.type == OBJECT_TYPE) + context->thisObject.objectValue->prototype = proto.objectValue; + } + if (result) __qmljs_copy(result, &context->thisObject); } else { diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 369d7e77a2..2bc9dbd8f2 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -300,6 +300,13 @@ void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) _block->MOVE(target, _block->TEMP(t), op); return; } + } else if (! target->asTemp()) { + if (! (source->asConst() || source->asTemp() || source->asName() || source->asSubscript())) { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), source); + _block->MOVE(target, _block->TEMP(t), op); + return; + } } _block->MOVE(target, source, op); } diff --git a/qv4isel.cpp b/qv4isel.cpp index 35a770683e..a689bec753 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -330,6 +330,49 @@ void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp * amd64_call_code(_codePtr, __qmljs_dispose_context); } +void InstructionSelection::constructProperty(IR::New *ctor, IR::Temp *result) +{ + IR::Member *member = ctor->base->asMember(); + assert(member != 0); + assert(member->base->asTemp()); + + int argc = 0; + for (IR::ExprList *it = ctor->args; it; it = it->next) + ++argc; + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, argc); + amd64_call_code(_codePtr, __qmljs_new_context); + + amd64_mov_reg_reg(_codePtr, AMD64_R15, AMD64_RAX, 8); + + argc = 0; + for (IR::ExprList *it = ctor->args; it; it = it->next) { + IR::Temp *t = it->expr->asTemp(); + Q_ASSERT(t != 0); + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R15, offsetof(Context, arguments), 8); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RAX, argc * sizeof(Value)); + loadTempAddress(AMD64_RSI, t); + amd64_call_code(_codePtr, __qmljs_copy); + ++argc; + } + + // __qmljs_construct_property(Context *context, Value *result, Value *base, String *name) + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); + if (result) + loadTempAddress(AMD64_RSI, result); + else + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + loadTempAddress(AMD64_RDX, member->base->asTemp()); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name)); + amd64_call_code(_codePtr, __qmljs_construct_property); + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); + amd64_call_code(_codePtr, __qmljs_dispose_context); +} + void InstructionSelection::visitExp(IR::Exp *s) { if (IR::Call *c = s->expr->asCall()) { @@ -475,8 +518,13 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_call_code(_codePtr, __qmljs_init_closure); return; } else if (IR::New *ctor = s->source->asNew()) { - constructActivationProperty(ctor, t); - return; + if (ctor->base->asName()) { + constructActivationProperty(ctor, t); + return; + } else if (ctor->base->asMember()) { + constructProperty(ctor, t); + return; + } } else if (IR::Member *m = s->source->asMember()) { //__qmljs_get_property(ctx, result, object, name); if (IR::Temp *base = m->base->asTemp()) { @@ -600,6 +648,9 @@ void InstructionSelection::visitMove(IR::Move *s) if (c->base->asName()) { callActivationProperty(c, t); return; + } else if (c->base->asMember()) { + callProperty(c, t); + return; } else if (c->base->asTemp()) { callValue(c, t); return; diff --git a/qv4isel_p.h b/qv4isel_p.h index 77bf4bbca9..a52b291e9f 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -24,6 +24,7 @@ protected: void callActivationProperty(IR::Call *call, IR::Temp *result); void callProperty(IR::Call *call, IR::Temp *result); void constructActivationProperty(IR::New *call, IR::Temp *result); + void constructProperty(IR::New *ctor, IR::Temp *result); void callValue(IR::Call *call, IR::Temp *result); virtual void visitExp(IR::Exp *); diff --git a/tests/string.1.js b/tests/string.1.js index 1495552e33..5ae5535881 100644 --- a/tests/string.1.js +++ b/tests/string.1.js @@ -3,5 +3,11 @@ var s = String(123) + 1 print(s) var s2 = new String(123) -print(s2) +print(s2, s2.toString, s2.toString()) + +var s3 = String.prototype.constructor(321) +print(s3) + +var s4 = new String.prototype.constructor(321) +print(s4) -- cgit v1.2.3 From a97bc91c7f3d08595245a88247776a7fb6c6516e Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Sun, 13 May 2012 13:50:55 +0200 Subject: Fix nested functions --- main.cpp | 16 +++-- qmljs_objects.cpp | 7 +-- qmljs_objects.h | 19 +++--- qmljs_runtime.cpp | 178 +++++++++++++++++++++++++++++++++++------------------ qmljs_runtime.h | 14 ++--- qv4codegen.cpp | 54 ++++++++++++---- qv4codegen_p.h | 1 + qv4ir_p.h | 10 ++- qv4isel.cpp | 179 ++++++++++++++++++++++-------------------------------- tests/fun.3.js | 17 ++++++ 10 files changed, 295 insertions(+), 200 deletions(-) create mode 100644 tests/fun.3.js diff --git a/main.cpp b/main.cpp index 8077479a4f..1bf71b4d26 100644 --- a/main.cpp +++ b/main.cpp @@ -28,6 +28,8 @@ using namespace QQmlJS::VM; struct Print: FunctionObject { + Print(Context *scope): FunctionObject(scope) {} + virtual void call(Context *ctx) { for (size_t i = 0; i < ctx->argumentCount; ++i) { @@ -43,9 +45,11 @@ struct Print: FunctionObject struct ObjectCtor: FunctionObject { + ObjectCtor(Context *scope): FunctionObject(scope) {} + virtual void construct(Context *ctx) { - __qmljs_init_object(ctx, &ctx->result, new Object()); + __qmljs_init_object(ctx, &ctx->thisObject, new Object()); } virtual void call(Context *) @@ -56,6 +60,8 @@ struct ObjectCtor: FunctionObject struct StringCtor: FunctionObject { + StringCtor(Context *scope): FunctionObject(scope) {} + virtual void construct(Context *ctx) { Value arg = ctx->argument(0); @@ -88,7 +94,7 @@ struct StringPrototype: Object void setProperty(Context *ctx, const QString &name, void (*code)(Context *)) { - setProperty(ctx, name, Value::object(ctx, new NativeFunction(code))); + setProperty(ctx, name, Value::object(ctx, new NativeFunction(ctx, code))); } static void toString(Context *ctx) @@ -147,12 +153,12 @@ void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &co __qmljs_init_object(ctx, &ctx->activation, globalObject); globalObject->put(VM::String::get(ctx, QLatin1String("print")), - VM::Value::object(ctx, new builtins::Print())); + VM::Value::object(ctx, new builtins::Print(ctx))); globalObject->put(VM::String::get(ctx, QLatin1String("Object")), - VM::Value::object(ctx, new builtins::ObjectCtor())); + VM::Value::object(ctx, new builtins::ObjectCtor(ctx))); - VM::FunctionObject *stringCtor = new builtins::StringCtor(); + VM::FunctionObject *stringCtor = new builtins::StringCtor(ctx); stringCtor->put(prototype, VM::Value::object(ctx, new builtins::StringPrototype(ctx, stringCtor))); globalObject->put(VM::String::get(ctx, QLatin1String("String")), VM::Value::object(ctx, stringCtor)); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 7a373516f8..a1a97e5feb 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -118,8 +118,8 @@ void FunctionObject::construct(Context *ctx) call(ctx); } -ScriptFunction::ScriptFunction(Context *context, IR::Function *function) - : context(context) +ScriptFunction::ScriptFunction(Context *scope, IR::Function *function) + : FunctionObject(scope) , function(function) { formalParameterCount = function->formals.size(); @@ -143,6 +143,7 @@ void ScriptFunction::call(VM::Context *ctx) void ScriptFunction::construct(VM::Context *ctx) { + __qmljs_init_object(ctx, &ctx->thisObject, new Object()); function->code(ctx); } @@ -160,7 +161,5 @@ Value *ArgumentsObject::getProperty(String *name, PropertyAttributes *attributes } if (Value *prop = Object::getProperty(name, attributes)) return prop; - else if (context && context->scope) - return context->scope->getProperty(name, attributes); return 0; } diff --git a/qmljs_objects.h b/qmljs_objects.h index 323367a9a8..5631b7db80 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -233,11 +233,11 @@ struct ArrayObject: Object { }; struct FunctionObject: Object { - Object *scope; + Context *scope; String **formalParameterList; size_t formalParameterCount; - FunctionObject(Object *scope = 0): scope(scope), formalParameterList(0), formalParameterCount(0) {} + FunctionObject(Context *scope): scope(scope), formalParameterList(0), formalParameterCount(0) {} virtual FunctionObject *asFunctionObject() { return this; } virtual bool hasInstance(const Value &value) const; @@ -248,16 +248,15 @@ struct FunctionObject: Object { struct NativeFunction: FunctionObject { void (*code)(Context *); - NativeFunction(void (*code)(Context *)): code(code) {} + NativeFunction(Context *scope, void (*code)(Context *)): FunctionObject(scope), code(code) {} virtual void call(Context *ctx) { code(ctx); } virtual void construct(Context *ctx) { code(ctx); } }; struct ScriptFunction: FunctionObject { - Context *context; IR::Function *function; - ScriptFunction(Context *context, IR::Function *function); + ScriptFunction(Context *scope, IR::Function *function); virtual ~ScriptFunction(); virtual void call(Context *ctx); @@ -279,7 +278,6 @@ struct Context { Context *parent; Value activation; Value thisObject; - Object *scope; Value *arguments; size_t argumentCount; Value result; @@ -287,6 +285,14 @@ struct Context { size_t formalCount; bool calledAsConstructor; + Value *lookup(String *name) { + if (activation.is(OBJECT_TYPE)) { + if (Value *prop = activation.objectValue->getProperty(name)) + return prop; + } + return parent ? parent->lookup(name) : 0; + } + inline Value argument(size_t index = 0) { Value arg; @@ -305,7 +311,6 @@ struct Context { void init() { parent = 0; - scope = 0; arguments = 0; argumentCount = 0; activation.type = NULL_TYPE; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index b0a3c100ea..0f751059aa 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -1,6 +1,7 @@ #include "qmljs_runtime.h" #include "qmljs_objects.h" +#include "qv4ir_p.h" #include #include #include @@ -200,38 +201,52 @@ void __qmljs_set_property_string(Context *ctx, Value *object, String *name, Stri void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR::Function *function) { Value value; - __qmljs_init_object(ctx, &value, new VM::ScriptFunction(ctx, function)); + __qmljs_init_closure(ctx, &value, function); object->objectValue->put(name, value, /*flag*/ 0); } void __qmljs_set_activation_property(Context *ctx, String *name, Value *value) { - __qmljs_set_property(ctx, &ctx->activation, name, value); + if (Value *prop = ctx->lookup(name)) + __qmljs_copy(prop, value); + else + ctx->activation.objectValue->put(name, *value); } void __qmljs_copy_activation_property(Context *ctx, String *name, String *other) { - __qmljs_copy_property(ctx, &ctx->activation, name, &ctx->activation, other); + if (Value *source = ctx->lookup(other)) + __qmljs_set_activation_property(ctx, name, source); + else + assert(!"reference error"); } -void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool value) +void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool b) { - __qmljs_set_property_boolean(ctx, &ctx->activation, name, value); + Value value; + __qmljs_init_boolean(ctx, &value, b); + __qmljs_set_activation_property(ctx, name, &value); } -void __qmljs_set_activation_property_number(Context *ctx, String *name, double value) +void __qmljs_set_activation_property_number(Context *ctx, String *name, double number) { - __qmljs_set_property_number(ctx, &ctx->activation, name, value); + Value value; + __qmljs_init_number(ctx, &value, number); + __qmljs_set_activation_property(ctx, name, &value); } -void __qmljs_set_activation_property_string(Context *ctx, String *name, String *value) +void __qmljs_set_activation_property_string(Context *ctx, String *name, String *string) { - __qmljs_set_property_string(ctx, &ctx->activation, name, value); + Value value; + __qmljs_init_string(ctx, &value, string); + __qmljs_set_activation_property(ctx, name, &value); } -void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Function *value) +void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Function *clos) { - __qmljs_set_property_closure(ctx, &ctx->activation, name, value); + Value value; + __qmljs_init_closure(ctx, &value, clos); + __qmljs_set_activation_property(ctx, name, &value); } void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name) @@ -248,7 +263,10 @@ void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *na void __qmljs_get_activation_property(Context *ctx, Value *result, String *name) { - __qmljs_get_property(ctx, result, &ctx->activation, name); + if (Value *prop = ctx->lookup(name)) + *result = *prop; + else + assert(!"reference error"); } void __qmljs_get_activation(Context *ctx, Value *result) @@ -349,34 +367,16 @@ bool __qmljs_equal(Context *ctx, const Value *x, const Value *y) return false; } -Context *__qmljs_new_context(Context *current, Value *thisObject, size_t argc) +void __qmljs_call_activation_property(Context *context, Value *result, String *name, Value *args, int argc) { - Context *ctx = new Context; - ctx->init(); - ctx->parent = current; - ctx->scope = current->activation.objectValue; - __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); - if (thisObject) - ctx->thisObject = *thisObject; - else - __qmljs_init_null(ctx, &ctx->thisObject); - ctx->arguments = new Value[argc]; - ctx->argumentCount = argc; - return ctx; -} - -void __qmljs_dispose_context(Context *ctx) -{ - delete[] ctx->arguments; - delete ctx; -} + Value *func = context->lookup(name); + if (! func) + assert(!"reference error"); -void __qmljs_call_activation_property(Context *context, Value *result, String *name) -{ - __qmljs_call_property(context, result, &context->parent->activation, name); + __qmljs_call_value(context, result, func, args, argc); } -void __qmljs_call_property(Context *context, Value *result, Value *base, String *name) +void __qmljs_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) { Value baseObject; Value thisObject; @@ -394,12 +394,18 @@ void __qmljs_call_property(Context *context, Value *result, Value *base, String baseObject.objectValue->get(name, &func); if (func.type == OBJECT_TYPE) { if (FunctionObject *f = func.objectValue->asFunctionObject()) { - context->thisObject = thisObject; - context->formals = f->formalParameterList; - context->formalCount = f->formalParameterCount; - f->call(context); + Context *ctx = new Context; + ctx->init(); + __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); + ctx->parent = f->scope; + ctx->thisObject = thisObject; + ctx->formals = f->formalParameterList; + ctx->formalCount = f->formalParameterCount; + ctx->arguments = args; + ctx->argumentCount = argc; + f->call(ctx); if (result) - __qmljs_copy(result, &context->result); + __qmljs_copy(result, &ctx->result); } else { assert(!"not a function"); } @@ -408,16 +414,27 @@ void __qmljs_call_property(Context *context, Value *result, Value *base, String } } -void __qmljs_call_value(Context *context, Value *result, Value *func) +void __qmljs_call_value(Context *context, Value *result, const Value *func, Value *args, int argc) { if (func->type == OBJECT_TYPE) { if (FunctionObject *f = func->objectValue->asFunctionObject()) { - __qmljs_init_null(context, &context->thisObject); - context->formals = f->formalParameterList; - context->formalCount = f->formalParameterCount; - f->call(context); + Context *ctx = new Context; + ctx->init(); + ctx->parent = f->scope; + __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); + __qmljs_init_null(ctx, &ctx->thisObject); + ctx->formals = f->formalParameterList; + ctx->formalCount = f->formalParameterCount; + if (argc) { + ctx->arguments = new Value[argc]; + std::copy(args, args + argc, ctx->arguments); + } else { + ctx->arguments = 0; + } + ctx->argumentCount = argc; + f->call(ctx); if (result) - __qmljs_copy(result, &context->result); + __qmljs_copy(result, &ctx->result); } else { assert(!"not a function"); } @@ -426,12 +443,49 @@ void __qmljs_call_value(Context *context, Value *result, Value *func) } } -void __qmljs_construct_activation_property(Context *context, Value *result, String *name) +void __qmljs_construct_activation_property(Context *context, Value *result, String *name, Value *args, int argc) { - __qmljs_construct_property(context, result, &context->activation, name); + Value *func = context->lookup(name); + if (! func) + assert(!"reference error"); + + __qmljs_construct_value(context, result, func, args, argc); +} + +void __qmljs_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc) +{ + if (func->type == OBJECT_TYPE) { + if (FunctionObject *f = func->objectValue->asFunctionObject()) { + Context *ctx = new Context; + ctx->init(); + ctx->parent = f->scope; + __qmljs_init_null(ctx, &ctx->thisObject); + __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); + ctx->formals = f->formalParameterList; + ctx->formalCount = f->formalParameterCount; + ctx->arguments = args; + ctx->argumentCount = argc; + ctx->calledAsConstructor = true; + f->construct(ctx); + assert(ctx->thisObject.is(OBJECT_TYPE)); + ctx->result = ctx->thisObject; + Value proto; + if (f->get(String::get(ctx, QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol + if (proto.type == OBJECT_TYPE) + ctx->thisObject.objectValue->prototype = proto.objectValue; + } + + if (result) + __qmljs_copy(result, &ctx->thisObject); + } else { + assert(!"not a function"); + } + } else { + assert(!"not a callable object"); + } } -void __qmljs_construct_property(Context *context, Value *result, Value *base, String *name) +void __qmljs_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) { Value func; Value thisObject = *base; @@ -442,20 +496,28 @@ void __qmljs_construct_property(Context *context, Value *result, Value *base, St thisObject.objectValue->get(name, &func); if (func.type == OBJECT_TYPE) { if (FunctionObject *f = func.objectValue->asFunctionObject()) { - context->thisObject = thisObject; - context->formals = f->formalParameterList; - context->formalCount = f->formalParameterCount; - context->calledAsConstructor = true; - f->construct(context); + Context *ctx = new Context; + ctx->init(); + ctx->parent = f->scope; + ctx->thisObject = thisObject; + __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); + ctx->formals = f->formalParameterList; + ctx->formalCount = f->formalParameterCount; + ctx->calledAsConstructor = true; + ctx->arguments = args; + ctx->argumentCount = argc; + ctx->calledAsConstructor = true; + f->construct(ctx); + assert(ctx->thisObject.is(OBJECT_TYPE)); Value proto; - if (f->get(String::get(context, QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol + if (f->get(String::get(ctx, QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol if (proto.type == OBJECT_TYPE) - context->thisObject.objectValue->prototype = proto.objectValue; + ctx->thisObject.objectValue->prototype = proto.objectValue; } if (result) - __qmljs_copy(result, &context->thisObject); + __qmljs_copy(result, &ctx->thisObject); } else { assert(!"not a function"); } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index c75e502060..143319ede8 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -44,13 +44,13 @@ struct Context; extern "C" { // context -Context *__qmljs_new_context(Context *current, Value *thisObject, size_t argc); -void __qmljs_dispose_context(Context *ctx); -void __qmljs_call_activation_property(Context *, Value *result, String *name); -void __qmljs_construct_activation_property(Context *, Value *result, String *name); -void __qmljs_call_property(Context *context, Value *result, Value *base, String *name); -void __qmljs_construct_property(Context *context, Value *result, Value *base, String *name); -void __qmljs_call_value(Context *context, Value *result, Value *func); +void __qmljs_call_activation_property(Context *, Value *result, String *name, Value *args, int argc); +void __qmljs_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc); +void __qmljs_call_value(Context *context, Value *result, const Value *func, Value *args, int argc); + +void __qmljs_construct_activation_property(Context *, Value *result, String *name, Value *args, int argc); +void __qmljs_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc); +void __qmljs_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc); // constructors void __qmljs_init_undefined(Context *ctx, Value *result); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 2bc9dbd8f2..221ab5e66e 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -159,15 +159,19 @@ struct ScanFunctionBody: Visitor // search for locals QList locals; - bool directEval; + int maxNumberOfArguments; + bool hasDirectEval; + bool hasNestedFunctions; ScanFunctionBody() - : directEval(false) + : maxNumberOfArguments(0) + , hasDirectEval(false) + , hasNestedFunctions(false) { } void operator()(Node *node) { - directEval = false; + hasDirectEval = false; locals.clear(); if (node) node->accept(this); @@ -176,13 +180,26 @@ struct ScanFunctionBody: Visitor protected: virtual bool visit(CallExpression *ast) { - if (! directEval) { + if (! hasDirectEval) { if (IdentifierExpression *id = cast(ast->base)) { if (id->name == QLatin1String("eval")) { - directEval = true; + hasDirectEval = true; } } } + int argc = 0; + for (AST::ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + maxNumberOfArguments = qMax(maxNumberOfArguments, argc); + return true; + } + + virtual bool visit(NewMemberExpression *ast) + { + int argc = 0; + for (AST::ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + maxNumberOfArguments = qMax(maxNumberOfArguments, argc); return true; } @@ -195,6 +212,7 @@ protected: virtual bool visit(FunctionExpression *ast) { + hasNestedFunctions = true; if (! locals.contains(ast->name)) locals.append(ast->name); return false; @@ -202,6 +220,7 @@ protected: virtual bool visit(FunctionDeclaration *ast) { + hasNestedFunctions = true; if (! locals.contains(ast->name)) locals.append(ast->name); return false; @@ -223,7 +242,9 @@ void Codegen::operator()(AST::Program *node, IR::Module *module) _module = module; IR::Function *globalCode = _module->newFunction(QLatin1String("%entry")); - globalCode->directEval = true; // ### remove + globalCode->hasDirectEval = true; // ### remove + globalCode->hasNestedFunctions = true; // ### remove + globalCode->redArea = 10; // ### remove _function = globalCode; _block = _function->newBasicBlock(); _exitBlock = _function->newBasicBlock(); @@ -291,6 +312,15 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) return _block->BINOP(op, left, right); } +IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args) +{ + if (base->asMember() || base->asName() || base->asTemp()) + return _block->CALL(base, args); + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), base); + return _block->CALL(_block->TEMP(t), args); +} + void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) { if (target->asMember()) { @@ -519,7 +549,7 @@ void Codegen::variableDeclaration(VariableDeclaration *ast) if (! expr.code) expr.code = _block->CONST(IR::UndefinedType, 0); - if (! _function->directEval) { + if (! _function->needsActivation()) { const int index = tempForLocalVariable(ast->name); if (index != -1) { move(_block->TEMP(index), *expr); @@ -895,7 +925,7 @@ bool Codegen::visit(CallExpression *ast) (*args_it)->init(actual); args_it = &(*args_it)->next; } - _expr.code = _block->CALL(*base, args); + _expr.code = call(*base, args); return false; } @@ -951,7 +981,7 @@ bool Codegen::visit(FunctionExpression *ast) bool Codegen::visit(IdentifierExpression *ast) { - if (! _function->directEval) { + if (! _function->needsActivation()) { int index = tempForLocalVariable(ast->name); if (index != -1) { _expr.code = _block->TEMP(index); @@ -1121,7 +1151,7 @@ bool Codegen::visit(TypeOfExpression *ast) Result expr = expression(ast->expression); IR::ExprList *args = _function->New(); args->init(argument(*expr)); - _expr.code = _block->CALL(_block->NAME(QLatin1String("typeof"), ast->typeofToken.startLine, ast->typeofToken.startColumn), args); + _expr.code = call(_block->NAME(QLatin1String("typeof"), ast->typeofToken.startLine, ast->typeofToken.startColumn), args); return false; } @@ -1308,8 +1338,10 @@ void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) IR::Function *function = _module->newFunction(ast->name.toString()); IR::BasicBlock *entryBlock = function->newBasicBlock(); IR::BasicBlock *exitBlock = function->newBasicBlock(); + function->hasDirectEval = functionInfo.hasDirectEval; + function->redArea = functionInfo.maxNumberOfArguments; - if (! functionInfo.directEval) { + if (! functionInfo.hasDirectEval) { for (int i = 0; i < functionInfo.locals.size(); ++i) { unsigned t = entryBlock->newTemp(); Q_ASSERT(t == unsigned(i)); diff --git a/qv4codegen_p.h b/qv4codegen_p.h index e58ceb5038..63c12c357a 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -71,6 +71,7 @@ protected: IR::Expr *subscript(IR::Expr *base, IR::Expr *index); IR::Expr *argument(IR::Expr *expr); IR::Expr *binop(IR::AluOp op, IR::Expr *left, IR::Expr *right); + IR::Expr *call(IR::Expr *base, IR::ExprList *args); void move(IR::Expr *target, IR::Expr *source, IR::AluOp op = IR::OpInvalid); void linearize(IR::Function *function); diff --git a/qv4ir_p.h b/qv4ir_p.h index a77a58fe76..57fe54bd7b 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -587,11 +587,13 @@ struct Function { const QString *name; QVector basicBlocks; int tempCount; + int redArea; QSet strings; QList formals; QList locals; void (*code)(VM::Context *); - bool directEval; + bool hasDirectEval: 1; + bool hasNestedFunctions: 1; template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } @@ -599,8 +601,10 @@ struct Function { : module(module) , pool(&module->pool) , tempCount(0) + , redArea(0) , code(0) - , directEval(false) + , hasDirectEval(false) + , hasNestedFunctions(false) { this->name = newString(name); } ~Function(); @@ -614,6 +618,8 @@ struct Function { inline BasicBlock *i(BasicBlock *block) { basicBlocks.append(block); return block; } void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); + + inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval; } }; struct BasicBlock { diff --git a/qv4isel.cpp b/qv4isel.cpp index a689bec753..c53741be74 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -101,7 +101,7 @@ void InstructionSelection::operator()(IR::Function *function) function->code = (void (*)(VM::Context *)) _code; _codePtr = _code; - int locals = function->tempCount * sizeof(Value); + int locals = (function->tempCount + function->redArea) * sizeof(Value); locals = (locals + 15) & ~15; amd64_push_reg(_codePtr, AMD64_RBP); @@ -179,37 +179,30 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu assert(baseName != 0); int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) + for (IR::ExprList *it = call->args; it; it = it->next) { ++argc; + } - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, argc); - amd64_call_code(_codePtr, __qmljs_new_context); - - amd64_mov_reg_reg(_codePtr, AMD64_R15, AMD64_RAX, 8); - - argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - IR::Temp *t = it->expr->asTemp(); - Q_ASSERT(t != 0); - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R15, offsetof(Context, arguments), 8); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RAX, argc * sizeof(Value)); - loadTempAddress(AMD64_RSI, t); + int i = 0; + for (IR::ExprList *it = call->args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); + loadTempAddress(AMD64_RSI, arg); amd64_call_code(_codePtr, __qmljs_copy); - ++argc; } - String *id = identifier(*baseName->id); - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context + if (result) loadTempAddress(AMD64_RSI, result); else amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, id); + + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id)); + amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); amd64_call_code(_codePtr, __qmljs_call_activation_property); - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); - amd64_call_code(_codePtr, __qmljs_dispose_context); } @@ -219,36 +212,30 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) assert(baseTemp != 0); int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) + for (IR::ExprList *it = call->args; it; it = it->next) { ++argc; + } - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, argc); - amd64_call_code(_codePtr, __qmljs_new_context); - - amd64_mov_reg_reg(_codePtr, AMD64_R15, AMD64_RAX, 8); - - argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - IR::Temp *t = it->expr->asTemp(); - Q_ASSERT(t != 0); - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R15, offsetof(Context, arguments), 8); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RAX, argc * sizeof(Value)); - loadTempAddress(AMD64_RSI, t); + int i = 0; + for (IR::ExprList *it = call->args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); + loadTempAddress(AMD64_RSI, arg); amd64_call_code(_codePtr, __qmljs_copy); - ++argc; } - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context + if (result) loadTempAddress(AMD64_RSI, result); else amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + loadTempAddress(AMD64_RDX, baseTemp); + amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); amd64_call_code(_codePtr, __qmljs_call_value); - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); - amd64_call_code(_codePtr, __qmljs_dispose_context); } void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) @@ -258,119 +245,99 @@ void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) assert(member->base->asTemp()); int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) + for (IR::ExprList *it = call->args; it; it = it->next) { ++argc; + } - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, argc); - amd64_call_code(_codePtr, __qmljs_new_context); - - amd64_mov_reg_reg(_codePtr, AMD64_R15, AMD64_RAX, 8); - - argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - IR::Temp *t = it->expr->asTemp(); - Q_ASSERT(t != 0); - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R15, offsetof(Context, arguments), 8); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RAX, argc * sizeof(Value)); - loadTempAddress(AMD64_RSI, t); + int i = 0; + for (IR::ExprList *it = call->args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); + loadTempAddress(AMD64_RSI, arg); amd64_call_code(_codePtr, __qmljs_copy); - ++argc; } - // __qmljs_call_property(Context *context, Value *result, Value *base, String *name) + //__qmljs_call_property(ctx, result, base, name, args, argc); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); if (result) loadTempAddress(AMD64_RSI, result); else amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + loadTempAddress(AMD64_RDX, member->base->asTemp()); amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name)); + amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); amd64_call_code(_codePtr, __qmljs_call_property); - - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); - amd64_call_code(_codePtr, __qmljs_dispose_context); } void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) { + IR::Name *baseName = call->base->asName(); + assert(baseName != 0); + int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) + for (IR::ExprList *it = call->args; it; it = it->next) { ++argc; + } - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, argc); - amd64_call_code(_codePtr, __qmljs_new_context); - - amd64_mov_reg_reg(_codePtr, AMD64_R15, AMD64_RAX, 8); - - argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - IR::Temp *t = it->expr->asTemp(); - Q_ASSERT(t != 0); - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R15, offsetof(Context, arguments), 8); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RAX, argc * sizeof(Value)); - loadTempAddress(AMD64_RSI, t); + int i = 0; + for (IR::ExprList *it = call->args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); + loadTempAddress(AMD64_RSI, arg); amd64_call_code(_codePtr, __qmljs_copy); - ++argc; } - String *id = identifier(*call->base->asName()->id); - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context + if (result) loadTempAddress(AMD64_RSI, result); else amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, id); + + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id)); + amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); amd64_call_code(_codePtr, __qmljs_construct_activation_property); - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); - amd64_call_code(_codePtr, __qmljs_dispose_context); } -void InstructionSelection::constructProperty(IR::New *ctor, IR::Temp *result) +void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) { - IR::Member *member = ctor->base->asMember(); + IR::Member *member = call->base->asMember(); assert(member != 0); assert(member->base->asTemp()); int argc = 0; - for (IR::ExprList *it = ctor->args; it; it = it->next) + for (IR::ExprList *it = call->args; it; it = it->next) { ++argc; + } - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, argc); - amd64_call_code(_codePtr, __qmljs_new_context); - - amd64_mov_reg_reg(_codePtr, AMD64_R15, AMD64_RAX, 8); - - argc = 0; - for (IR::ExprList *it = ctor->args; it; it = it->next) { - IR::Temp *t = it->expr->asTemp(); - Q_ASSERT(t != 0); - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R15, offsetof(Context, arguments), 8); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RAX, argc * sizeof(Value)); - loadTempAddress(AMD64_RSI, t); + int i = 0; + for (IR::ExprList *it = call->args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); + loadTempAddress(AMD64_RSI, arg); amd64_call_code(_codePtr, __qmljs_copy); - ++argc; } - // __qmljs_construct_property(Context *context, Value *result, Value *base, String *name) + //__qmljs_call_property(ctx, result, base, name, args, argc); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); if (result) loadTempAddress(AMD64_RSI, result); else amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + loadTempAddress(AMD64_RDX, member->base->asTemp()); amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name)); + amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); amd64_call_code(_codePtr, __qmljs_construct_property); - - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R15, 8); - amd64_call_code(_codePtr, __qmljs_dispose_context); } void InstructionSelection::visitExp(IR::Exp *s) diff --git a/tests/fun.3.js b/tests/fun.3.js new file mode 100644 index 0000000000..5add270f35 --- /dev/null +++ b/tests/fun.3.js @@ -0,0 +1,17 @@ + +function fix(f) { + var k = function (x) { + return f(function (z) { return x(x)(z) }) + } + return k(k) +} + +var F = function (f) { + return function (n) { + return n == 0 ? 1 : n * f(n - 1) + } +} + +var fact = fix(F) + +print("the factorial of 12 is", fact(12)) -- cgit v1.2.3 From fdcefa8616fc8285a880af04f54519da5fd8ca2a Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 14 May 2012 10:50:13 +0200 Subject: Do a quick scan of the global code. --- qv4codegen.cpp | 11 +++++++---- qv4ir_p.h | 4 ++-- qv4isel.cpp | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 221ab5e66e..9fdf5925db 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -241,10 +241,13 @@ void Codegen::operator()(AST::Program *node, IR::Module *module) { _module = module; + ScanFunctionBody globalCodeInfo; + globalCodeInfo(node); + IR::Function *globalCode = _module->newFunction(QLatin1String("%entry")); - globalCode->hasDirectEval = true; // ### remove - globalCode->hasNestedFunctions = true; // ### remove - globalCode->redArea = 10; // ### remove + globalCode->hasDirectEval = globalCodeInfo.hasDirectEval; + globalCode->hasNestedFunctions = true; // ### FIXME: initialize it with globalCodeInfo.hasNestedFunctions; + globalCode->maxNumberOfArguments = globalCodeInfo.maxNumberOfArguments; _function = globalCode; _block = _function->newBasicBlock(); _exitBlock = _function->newBasicBlock(); @@ -1339,7 +1342,7 @@ void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) IR::BasicBlock *entryBlock = function->newBasicBlock(); IR::BasicBlock *exitBlock = function->newBasicBlock(); function->hasDirectEval = functionInfo.hasDirectEval; - function->redArea = functionInfo.maxNumberOfArguments; + function->maxNumberOfArguments = functionInfo.maxNumberOfArguments; if (! functionInfo.hasDirectEval) { for (int i = 0; i < functionInfo.locals.size(); ++i) { diff --git a/qv4ir_p.h b/qv4ir_p.h index 57fe54bd7b..0476263cab 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -587,7 +587,7 @@ struct Function { const QString *name; QVector basicBlocks; int tempCount; - int redArea; + int maxNumberOfArguments; QSet strings; QList formals; QList locals; @@ -601,7 +601,7 @@ struct Function { : module(module) , pool(&module->pool) , tempCount(0) - , redArea(0) + , maxNumberOfArguments(0) , code(0) , hasDirectEval(false) , hasNestedFunctions(false) diff --git a/qv4isel.cpp b/qv4isel.cpp index c53741be74..c0c88c35ae 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -101,7 +101,7 @@ void InstructionSelection::operator()(IR::Function *function) function->code = (void (*)(VM::Context *)) _code; _codePtr = _code; - int locals = (function->tempCount + function->redArea) * sizeof(Value); + int locals = (function->tempCount + function->maxNumberOfArguments) * sizeof(Value); locals = (locals + 15) & ~15; amd64_push_reg(_codePtr, AMD64_RBP); -- cgit v1.2.3 From c996f53bedd685571ae4d414c3677678e7263faf Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 14 May 2012 14:34:36 +0200 Subject: Store the actual arguments in TEMPs with negative indices. This should make it possible to optimize function calls. --- qmljs_objects.h | 7 ++++++- qmljs_runtime.cpp | 43 ++++++++++++++++++++++++++++++++++--------- qv4codegen.cpp | 27 +++++++++++++++++++++++---- qv4codegen_p.h | 3 ++- qv4ir.cpp | 5 ++++- qv4ir_p.h | 4 ++-- qv4isel.cpp | 13 +++++++------ qv4isel_p.h | 1 - 8 files changed, 78 insertions(+), 25 deletions(-) diff --git a/qmljs_objects.h b/qmljs_objects.h index 5631b7db80..8658178ac2 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -236,8 +236,13 @@ struct FunctionObject: Object { Context *scope; String **formalParameterList; size_t formalParameterCount; + bool needsActivation; - FunctionObject(Context *scope): scope(scope), formalParameterList(0), formalParameterCount(0) {} + FunctionObject(Context *scope) + : scope(scope) + , formalParameterList(0) + , formalParameterCount(0) + , needsActivation(true) {} virtual FunctionObject *asFunctionObject() { return this; } virtual bool hasInstance(const Value &value) const; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 0f751059aa..3bacfa3ea4 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -396,13 +396,20 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S if (FunctionObject *f = func.objectValue->asFunctionObject()) { Context *ctx = new Context; ctx->init(); - __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); ctx->parent = f->scope; + if (f->needsActivation) + __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); + else + __qmljs_init_null(ctx, &ctx->activation); ctx->thisObject = thisObject; ctx->formals = f->formalParameterList; ctx->formalCount = f->formalParameterCount; ctx->arguments = args; ctx->argumentCount = argc; + if (argc && f->needsActivation) { + ctx->arguments = new Value[argc]; + std::copy(args, args + argc, ctx->arguments); + } f->call(ctx); if (result) __qmljs_copy(result, &ctx->result); @@ -416,22 +423,26 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S void __qmljs_call_value(Context *context, Value *result, const Value *func, Value *args, int argc) { + Q_UNUSED(context); + if (func->type == OBJECT_TYPE) { if (FunctionObject *f = func->objectValue->asFunctionObject()) { Context *ctx = new Context; ctx->init(); ctx->parent = f->scope; - __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); + if (f->needsActivation) + __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); + else + __qmljs_init_null(ctx, &ctx->activation); __qmljs_init_null(ctx, &ctx->thisObject); ctx->formals = f->formalParameterList; ctx->formalCount = f->formalParameterCount; - if (argc) { + ctx->arguments = args; + ctx->argumentCount = argc; + if (argc && f->needsActivation) { ctx->arguments = new Value[argc]; std::copy(args, args + argc, ctx->arguments); - } else { - ctx->arguments = 0; } - ctx->argumentCount = argc; f->call(ctx); if (result) __qmljs_copy(result, &ctx->result); @@ -454,17 +465,25 @@ void __qmljs_construct_activation_property(Context *context, Value *result, Stri void __qmljs_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc) { + Q_UNUSED(context); if (func->type == OBJECT_TYPE) { if (FunctionObject *f = func->objectValue->asFunctionObject()) { Context *ctx = new Context; ctx->init(); ctx->parent = f->scope; + if (f->needsActivation) + __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); + else + __qmljs_init_null(ctx, &ctx->activation); __qmljs_init_null(ctx, &ctx->thisObject); - __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); ctx->formals = f->formalParameterList; ctx->formalCount = f->formalParameterCount; ctx->arguments = args; ctx->argumentCount = argc; + if (argc && f->needsActivation) { + ctx->arguments = new Value[argc]; + std::copy(args, args + argc, ctx->arguments); + } ctx->calledAsConstructor = true; f->construct(ctx); assert(ctx->thisObject.is(OBJECT_TYPE)); @@ -499,13 +518,19 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba Context *ctx = new Context; ctx->init(); ctx->parent = f->scope; + if (f->needsActivation) + __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); + else + __qmljs_init_null(ctx, &ctx->activation); ctx->thisObject = thisObject; - __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); ctx->formals = f->formalParameterList; ctx->formalCount = f->formalParameterCount; - ctx->calledAsConstructor = true; ctx->arguments = args; ctx->argumentCount = argc; + if (argc && f->needsActivation) { + ctx->arguments = new Value[argc]; + std::copy(args, args + argc, ctx->arguments); + } ctx->calledAsConstructor = true; f->construct(ctx); assert(ctx->thisObject.is(OBJECT_TYPE)); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 9fdf5925db..f1aea0c60b 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -553,11 +553,16 @@ void Codegen::variableDeclaration(VariableDeclaration *ast) expr.code = _block->CONST(IR::UndefinedType, 0); if (! _function->needsActivation()) { - const int index = tempForLocalVariable(ast->name); + int index = indexOfLocal(ast->name); if (index != -1) { move(_block->TEMP(index), *expr); return; } + index = indexOfArgument(ast->name); + if (index != -1) { + move(_block->TEMP(-(index + 1)), *expr); + return; + } } else { move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), *expr); } @@ -985,11 +990,16 @@ bool Codegen::visit(FunctionExpression *ast) bool Codegen::visit(IdentifierExpression *ast) { if (! _function->needsActivation()) { - int index = tempForLocalVariable(ast->name); + int index = indexOfLocal(ast->name); if (index != -1) { _expr.code = _block->TEMP(index); return false; } + index = indexOfArgument(ast->name); + if (index != -1) { + _expr.code = _block->TEMP(-(index + 1)); + return false; + } } _expr.code = _block->NAME(ast->name.toString(), @@ -1344,7 +1354,7 @@ void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) function->hasDirectEval = functionInfo.hasDirectEval; function->maxNumberOfArguments = functionInfo.maxNumberOfArguments; - if (! functionInfo.hasDirectEval) { + if (! function->needsActivation()) { for (int i = 0; i < functionInfo.locals.size(); ++i) { unsigned t = entryBlock->newTemp(); Q_ASSERT(t == unsigned(i)); @@ -1387,7 +1397,7 @@ void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) } } -int Codegen::tempForLocalVariable(const QStringRef &string) const +int Codegen::indexOfLocal(const QStringRef &string) const { for (int i = 0; i < _function->locals.size(); ++i) { if (*_function->locals.at(i) == string) @@ -1396,6 +1406,15 @@ int Codegen::tempForLocalVariable(const QStringRef &string) const return -1; } +int Codegen::indexOfArgument(const QStringRef &string) const +{ + for (int i = 0; i < _function->formals.size(); ++i) { + if (*_function->formals.at(i) == string) + return i; + } + return -1; +} + bool Codegen::visit(IdentifierPropertyName *ast) { _property = ast->id.toString(); diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 63c12c357a..61ef851785 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -76,7 +76,8 @@ protected: void linearize(IR::Function *function); void defineFunction(AST::FunctionExpression *ast, bool isDeclaration = false); - int tempForLocalVariable(const QStringRef &string) const; + int indexOfLocal(const QStringRef &string) const; + int indexOfArgument(const QStringRef &string) const; void statement(AST::Statement *ast); void statement(AST::ExpressionNode *ast); diff --git a/qv4ir.cpp b/qv4ir.cpp index 5d55fe5396..0ea0b4d52e 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -241,7 +241,10 @@ void Name::dump(QTextStream &out) void Temp::dump(QTextStream &out) { - out << '%' << index; + if (index < 0) + out << '#' << -(index + 1); // negative and 1-based. + else + out << '%' << index; // temp } void Closure::dump(QTextStream &out) diff --git a/qv4ir_p.h b/qv4ir_p.h index 0476263cab..594077a33a 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -268,9 +268,9 @@ struct Name: Expr { }; struct Temp: Expr { - unsigned index; + int index; - void init(Type type, unsigned index) + void init(Type type, int index) { this->type = type; this->index = index; diff --git a/qv4isel.cpp b/qv4isel.cpp index c0c88c35ae..260cdd0637 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -163,14 +163,15 @@ String *InstructionSelection::identifier(const QString &s) return id; } -int InstructionSelection::tempOffset(IR::Temp *t) -{ - return sizeof(Value) * t->index; -} - void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) { - amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * t->index); + if (t->index < 0) { + const int arg = -t->index - 1; + amd64_mov_reg_membase(_codePtr, reg, AMD64_R14, offsetof(Context, arguments), 8); + amd64_lea_membase(_codePtr, reg, reg, sizeof(Value) * arg); + } else { + amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * t->index); + } } void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) diff --git a/qv4isel_p.h b/qv4isel_p.h index a52b291e9f..22f8056376 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -19,7 +19,6 @@ public: protected: VM::String *identifier(const QString &s); - int tempOffset(IR::Temp *t); void loadTempAddress(int reg, IR::Temp *t); void callActivationProperty(IR::Call *call, IR::Temp *result); void callProperty(IR::Call *call, IR::Temp *result); -- cgit v1.2.3 From 3a7f8a31ed85b2643f2491ed600f29c3e61e2c78 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 14 May 2012 16:03:10 +0200 Subject: Initial work on the String object. --- main.cpp | 69 +---------- qmljs_objects.cpp | 10 ++ qmljs_objects.h | 10 +- qmljs_runtime.cpp | 66 ++++++++++- qmljs_runtime.h | 20 +++- qv4ecmaobjects.cpp | 333 +++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4ecmaobjects_p.h | 62 ++++++++++ tests/string.1.js | 7 ++ v4.pro | 7 +- 9 files changed, 510 insertions(+), 74 deletions(-) create mode 100644 qv4ecmaobjects.cpp create mode 100644 qv4ecmaobjects_p.h diff --git a/main.cpp b/main.cpp index 1bf71b4d26..a3c0b7326f 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,7 @@ #include "qv4codegen_p.h" #include "qv4isel_p.h" #include "qv4syntaxchecker_p.h" +#include "qv4ecmaobjects_p.h" #include #include @@ -37,72 +38,12 @@ struct Print: FunctionObject __qmljs_to_string(ctx, &v, &ctx->arguments[i]); if (i) std::cout << ' '; - std::cout << qPrintable(v.stringValue->text()); + std::cout << qPrintable(v.stringValue->toQString()); } std::cout << std::endl; } }; -struct ObjectCtor: FunctionObject -{ - ObjectCtor(Context *scope): FunctionObject(scope) {} - - virtual void construct(Context *ctx) - { - __qmljs_init_object(ctx, &ctx->thisObject, new Object()); - } - - virtual void call(Context *) - { - assert(!"not here"); - } -}; - -struct StringCtor: FunctionObject -{ - StringCtor(Context *scope): FunctionObject(scope) {} - - virtual void construct(Context *ctx) - { - Value arg = ctx->argument(0); - __qmljs_to_string(ctx, &arg, &arg); - __qmljs_init_object(ctx, &ctx->thisObject, new StringObject(arg)); - } - - virtual void call(Context *ctx) - { - const Value arg = ctx->argument(0); - if (arg.is(UNDEFINED_TYPE)) - __qmljs_init_string(ctx, &ctx->result, String::get(ctx, QString())); - else - __qmljs_to_string(ctx, &ctx->result, &arg); - } -}; - -struct StringPrototype: Object -{ - StringPrototype(Context *ctx, FunctionObject *ctor) - { - setProperty(ctx, QLatin1String("constructor"), Value::object(ctx, ctor)); - setProperty(ctx, QLatin1String("toString"), toString); - } - - void setProperty(Context *ctx, const QString &name, const Value &value) - { - put(String::get(ctx, name), value); - } - - void setProperty(Context *ctx, const QString &name, void (*code)(Context *)) - { - setProperty(ctx, name, Value::object(ctx, new NativeFunction(ctx, code))); - } - - static void toString(Context *ctx) - { - __qmljs_to_string(ctx, &ctx->result, &ctx->thisObject); - } -}; - } // builtins @@ -153,10 +94,10 @@ void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &co __qmljs_init_object(ctx, &ctx->activation, globalObject); globalObject->put(VM::String::get(ctx, QLatin1String("print")), - VM::Value::object(ctx, new builtins::Print(ctx))); + VM::Value::object(ctx, new builtins::Print(ctx))); globalObject->put(VM::String::get(ctx, QLatin1String("Object")), - VM::Value::object(ctx, new builtins::ObjectCtor(ctx))); + VM::Value::object(ctx, new builtins::ObjectCtor(ctx))); VM::FunctionObject *stringCtor = new builtins::StringCtor(ctx); stringCtor->put(prototype, VM::Value::object(ctx, new builtins::StringPrototype(ctx, stringCtor))); @@ -165,7 +106,7 @@ void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &co foreach (IR::Function *function, module.functions) { if (function->name && ! function->name->isEmpty()) { globalObject->put(VM::String::get(ctx, *function->name), - VM::Value::object(ctx, new VM::ScriptFunction(ctx, function))); + VM::Value::object(ctx, new VM::ScriptFunction(ctx, function))); } } codeByName.value(QLatin1String("%entry"))->code(ctx); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index a1a97e5feb..bcc08d3119 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -11,6 +11,16 @@ Object::~Object() delete members; } +void Object::setProperty(Context *ctx, const QString &name, const Value &value) +{ + put(String::get(ctx, name), value); +} + +void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context *)) +{ + setProperty(ctx, name, Value::object(ctx, new NativeFunction(ctx, code))); +} + bool Object::get(String *name, Value *result) { if (Value *prop = getProperty(name)) { diff --git a/qmljs_objects.h b/qmljs_objects.h index 8658178ac2..2dd4b096cf 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -28,7 +28,7 @@ struct String { String(const QString &text) : _text(text), _hashValue(0) {} - inline const QString &text() const { + inline const QString &toQString() const { return _text; } @@ -66,7 +66,7 @@ struct Property { inline bool hasName(String *n) const { if (name == n) { return true; - } else if (name->hashValue() == n->hashValue() && name->text() == n->text()) { + } else if (name->hashValue() == n->hashValue() && name->toQString() == n->toQString()) { return true; } return false; @@ -209,6 +209,12 @@ struct Object { virtual bool deleteProperty(String *name, bool flag); virtual void defaultValue(Value *result, int typeHint); // ### TODO: defineOwnProperty(name, descriptor, boolean) -> boolean + + // + // helpers + // + void setProperty(Context *ctx, const QString &name, const Value &value); + void setProperty(Context *ctx, const QString &name, void (*code)(Context *)); }; struct BooleanObject: Object { diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 3bacfa3ea4..6fdf179761 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -14,6 +14,62 @@ Value Value::string(Context *ctx, const QString &s) return string(ctx, String::get(ctx, s)); } +int Value::toInt32(double number) +{ + if (! number || isnan(number) || isinf(number)) + return +0; + return (int) trunc(number); // ### +} + +int Value::toInteger(double number) +{ + if (isnan(number)) + return +0; + else if (! number || isinf(number)) + return number; + const double v = floor(fabs(number)); + return signbit(number) ? -v : v; +} + +int Value::toUInt16(Context *ctx) +{ + return __qmljs_to_uint16(ctx, this); +} + +int Value::toInt32(Context *ctx) +{ + return __qmljs_to_int32(ctx, this); +} + +uint Value::toUInt32(Context *ctx) +{ + return __qmljs_to_int32(ctx, this); +} + +bool Value::toBoolean(Context *ctx) const +{ + return __qmljs_to_boolean(ctx, this); +} + +double Value::toInteger(Context *ctx) const +{ + return __qmljs_to_integer(ctx, this); +} + +double Value::toNumber(Context *ctx) const +{ + return __qmljs_to_number(ctx, this); +} + +String *Value::toString(Context *ctx) const +{ + Value v; + __qmljs_to_string(ctx, &v, this); + assert(v.is(STRING_TYPE)); + return v.stringValue; +} + + extern "C" { void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos) @@ -102,13 +158,13 @@ void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *rig int __qmljs_string_length(Context *, String *string) { - return string->text().length(); + return string->toQString().length(); } double __qmljs_string_to_number(Context *, String *string) { bool ok; - return string->text().toDouble(&ok); // ### TODO + return string->toQString().toDouble(&ok); // ### TODO } void __qmljs_string_from_number(Context *ctx, Value *result, double number) @@ -119,19 +175,19 @@ void __qmljs_string_from_number(Context *ctx, Value *result, double number) bool __qmljs_string_compare(Context *, String *left, String *right) { - return left->text() < right->text(); + return left->toQString() < right->toQString(); } bool __qmljs_string_equal(Context *, String *left, String *right) { return left == right || (left->hashValue() == right->hashValue() && - left->text() == right->text()); + left->toQString() == right->toQString()); } String *__qmljs_string_concat(Context *ctx, String *first, String *second) { - return String::get(ctx, first->text() + second->text()); + return String::get(ctx, first->toQString() + second->toQString()); } bool __qmljs_is_function(Context *, const Value *value) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 143319ede8..2a0f42d1dd 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -199,6 +199,23 @@ struct Value { } static Value string(Context *ctx, const QString &string); + + static int toInteger(double number); + static int toInt32(double value); + int toUInt16(Context *ctx); + int toInt32(Context *ctx); + uint toUInt32(Context *ctx); + bool toBoolean(Context *ctx) const; + double toInteger(Context *ctx) const; + double toNumber(Context *ctx) const; + String *toString(Context *ctx) const; + + inline bool isUndefined() const { return is(UNDEFINED_TYPE); } + inline bool isNull() const { return is(NULL_TYPE); } + inline bool isString() const { return is(STRING_TYPE); } + inline bool isBoolean() const { return is(BOOLEAN_TYPE); } + inline bool isNumber() const { return is(NUMBER_TYPE); } + inline bool isObject() const { return is(OBJECT_TYPE); } }; extern "C" { @@ -311,7 +328,8 @@ inline double __qmljs_to_integer(Context *ctx, const Value *value) return +0; else if (! number || isinf(number)) return number; - return signbit(number) * floor(fabs(number)); + const double v = floor(fabs(number)); + return signbit(number) ? -v : v; } inline int __qmljs_to_int32(Context *ctx, const Value *value) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp new file mode 100644 index 0000000000..455a454892 --- /dev/null +++ b/qv4ecmaobjects.cpp @@ -0,0 +1,333 @@ + +#include "qv4ecmaobjects_p.h" +#include +#include +#include + +using namespace QQmlJS::VM; + +// +// Object +// +ObjectCtor::ObjectCtor(Context *scope) + : FunctionObject(scope) +{ +} + +void ObjectCtor::construct(Context *ctx) +{ + __qmljs_init_object(ctx, &ctx->thisObject, new Object()); +} + +void ObjectCtor::call(Context *) +{ + assert(!"not here"); +} + + +ObjectPrototype::ObjectPrototype(Context *ctx, FunctionObject *ctor) +{ + setProperty(ctx, QLatin1String("constructor"), Value::object(ctx, ctor)); +} + +// +// String +// +StringCtor::StringCtor(Context *scope) + : FunctionObject(scope) +{ +} + +void StringCtor::construct(Context *ctx) +{ + Value arg = ctx->argument(0); + __qmljs_to_string(ctx, &arg, &arg); + __qmljs_init_object(ctx, &ctx->thisObject, new StringObject(arg)); +} + +void StringCtor::call(Context *ctx) +{ + const Value arg = ctx->argument(0); + if (arg.is(UNDEFINED_TYPE)) + __qmljs_init_string(ctx, &ctx->result, String::get(ctx, QString())); + else + __qmljs_to_string(ctx, &ctx->result, &arg); +} + +StringPrototype::StringPrototype(Context *ctx, FunctionObject *ctor) +{ + setProperty(ctx, QLatin1String("constructor"), Value::object(ctx, ctor)); + setProperty(ctx, QLatin1String("toString"), method_toString); + setProperty(ctx, QLatin1String("valueOf"), method_valueOf); + setProperty(ctx, QLatin1String("charAt"), method_charAt); + setProperty(ctx, QLatin1String("charCodeAt"), method_charCodeAt); + setProperty(ctx, QLatin1String("concat"), method_concat); + setProperty(ctx, QLatin1String("indexOf"), method_indexOf); + setProperty(ctx, QLatin1String("lastIndexOf"), method_lastIndexOf); + setProperty(ctx, QLatin1String("localeCompare"), method_localeCompare); + setProperty(ctx, QLatin1String("match"), method_match); + setProperty(ctx, QLatin1String("replace"), method_replace); + setProperty(ctx, QLatin1String("search"), method_search); + setProperty(ctx, QLatin1String("slice"), method_slice); + setProperty(ctx, QLatin1String("split"), method_split); + setProperty(ctx, QLatin1String("substr"), method_substr); + setProperty(ctx, QLatin1String("substring"), method_substring); + setProperty(ctx, QLatin1String("toLowerCase"), method_toLowerCase); + setProperty(ctx, QLatin1String("toLocaleLowerCase"), method_toLocaleLowerCase); + setProperty(ctx, QLatin1String("toUpperCase"), method_toUpperCase); + setProperty(ctx, QLatin1String("toLocaleUpperCase"), method_toLocaleUpperCase); + setProperty(ctx, QLatin1String("fromCharCode"), method_fromCharCode); +} + +QString StringPrototype::getThisString(Context *ctx) +{ + Value v; + __qmljs_to_string(ctx, &v, &ctx->thisObject); + assert(v.is(STRING_TYPE)); + return v.stringValue->toQString(); +} + +void StringPrototype::method_toString(Context *ctx) +{ + __qmljs_to_string(ctx, &ctx->result, &ctx->thisObject); +} + +void StringPrototype::method_valueOf(Context *ctx) +{ + ctx->thisObject.objectValue->defaultValue(&ctx->result, STRING_HINT); +} + +void StringPrototype::method_charAt(Context *ctx) +{ + const QString str = getThisString(ctx); + + int pos = 0; + if (ctx->argumentCount > 0) + pos = (int) ctx->argument(0).toInteger(ctx); + + QString result; + if (pos >= 0 && pos < str.length()) + result += str.at(pos); + + ctx->result = Value::string(ctx, result); +} + +void StringPrototype::method_charCodeAt(Context *ctx) +{ + const QString str = getThisString(ctx); + + int pos = 0; + if (ctx->argumentCount > 0) + pos = (int) ctx->argument(0).toInteger(ctx); + + double result = qSNaN(); + + if (pos >= 0 && pos < str.length()) + result = str.at(pos).unicode(); + + __qmljs_init_number(ctx, &ctx->result, result); +} + +void StringPrototype::method_concat(Context *ctx) +{ + QString value = getThisString(ctx); + + for (unsigned i = 0; i < ctx->argumentCount; ++i) { + Value v; + __qmljs_to_string(ctx, &v, &ctx->arguments[i]); + assert(v.is(STRING_TYPE)); + value += v.stringValue->toQString(); + } + + ctx->result = Value::string(ctx, value); +} + +void StringPrototype::method_indexOf(Context *ctx) +{ + QString value = getThisString(ctx); + + QString searchString; + if (ctx->argumentCount) + searchString = ctx->argument(0).toString(ctx)->toQString(); + + int pos = 0; + if (ctx->argumentCount > 1) + pos = (int) ctx->argument(1).toInteger(ctx); + + int index = -1; + if (! value.isEmpty()) + index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); + + __qmljs_init_number(ctx, &ctx->result, index); +} + +void StringPrototype::method_lastIndexOf(Context *ctx) +{ + const QString value = getThisString(ctx); + + QString searchString; + if (ctx->argumentCount) { + Value v; + __qmljs_to_string(ctx, &v, &ctx->arguments[0]); + searchString = v.stringValue->toQString(); + } + + Value posArg = ctx->argument(1); + double position = __qmljs_to_number(ctx, &posArg); + if (qIsNaN(position)) + position = +qInf(); + else + position = trunc(position); + + int pos = trunc(qMin(qMax(position, 0.0), double(value.length()))); + if (!searchString.isEmpty() && pos == value.length()) + --pos; + int index = value.lastIndexOf(searchString, pos); + __qmljs_init_number(ctx, &ctx->result, index); +} + +void StringPrototype::method_localeCompare(Context *ctx) +{ + const QString value = getThisString(ctx); + const QString that = ctx->argument(0).toString(ctx)->toQString(); + __qmljs_init_number(ctx, &ctx->result, QString::localeAwareCompare(value, that)); +} + +void StringPrototype::method_match(Context *) +{ + // requires Regexp + Q_UNIMPLEMENTED(); +} + +void StringPrototype::method_replace(Context *) +{ + // requires Regexp + Q_UNIMPLEMENTED(); +} + +void StringPrototype::method_search(Context *) +{ + // requires Regexp + Q_UNIMPLEMENTED(); +} + +void StringPrototype::method_slice(Context *ctx) +{ + const QString text = getThisString(ctx); + const int length = text.length(); + + int start = int (ctx->argument(0).toInteger(ctx)); + int end = ctx->argument(1).isUndefined() + ? length : int (ctx->argument(1).toInteger(ctx)); + + if (start < 0) + start = qMax(length + start, 0); + else + start = qMin(start, length); + + if (end < 0) + end = qMax(length + end, 0); + else + end = qMin(end, length); + + int count = qMax(0, end - start); + ctx->result = Value::string(ctx, text.mid(start, count)); +} + +void StringPrototype::method_split(Context *) +{ + // requires Array + Q_UNIMPLEMENTED(); +} + +void StringPrototype::method_substr(Context *ctx) +{ + const QString value = getThisString(ctx); + + double start = 0; + if (ctx->argumentCount > 0) + start = ctx->argument(0).toInteger(ctx); + + double length = +qInf(); + if (ctx->argumentCount > 1) + length = ctx->argument(1).toInteger(ctx); + + double count = value.length(); + if (start < 0) + start = qMax(count + start, 0.0); + + length = qMin(qMax(length, 0.0), count - start); + + qint32 x = Value::toInt32(start); + qint32 y = Value::toInt32(length); + ctx->result = Value::string(ctx, value.mid(x, y)); +} + +void StringPrototype::method_substring(Context *ctx) +{ + QString value = getThisString(ctx); + int length = value.length(); + + double start = 0; + double end = length; + + if (ctx->argumentCount > 0) + start = ctx->argument(0).toInteger(ctx); + + if (ctx->argumentCount > 1) + end = ctx->argument(1).toInteger(ctx); + + if (qIsNaN(start) || start < 0) + start = 0; + + if (qIsNaN(end) || end < 0) + end = 0; + + if (start > length) + start = length; + + if (end > length) + end = length; + + if (start > end) { + double was = start; + start = end; + end = was; + } + + qint32 x = Value::toInt32(start); + qint32 y = Value::toInt32(end - start); + ctx->result = Value::string(ctx, value.mid(x, y)); +} + +void StringPrototype::method_toLowerCase(Context *ctx) +{ + QString value = getThisString(ctx); + ctx->result = Value::string(ctx, value.toLower()); +} + +void StringPrototype::method_toLocaleLowerCase(Context *ctx) +{ + method_toLowerCase(ctx); +} + +void StringPrototype::method_toUpperCase(Context *ctx) +{ + QString value = getThisString(ctx); + ctx->result = Value::string(ctx, value.toUpper()); +} + +void StringPrototype::method_toLocaleUpperCase(Context *ctx) +{ + method_toUpperCase(ctx); +} + +void StringPrototype::method_fromCharCode(Context *ctx) +{ + QString str; + for (unsigned i = 0; i < ctx->argumentCount; ++i) { + QChar c(ctx->argument(i).toUInt16(ctx)); + str += c; + } + ctx->result = Value::string(ctx, str); +} diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h new file mode 100644 index 0000000000..1b5d6309dc --- /dev/null +++ b/qv4ecmaobjects_p.h @@ -0,0 +1,62 @@ +#ifndef QV4ECMAOBJECTS_P_H +#define QV4ECMAOBJECTS_P_H + +#include "qmljs_objects.h" + +namespace QQmlJS { +namespace VM { + +struct ObjectCtor: FunctionObject +{ + ObjectCtor(Context *scope); + + virtual void construct(Context *ctx); + virtual void call(Context *); +}; + +struct ObjectPrototype: Object +{ + ObjectPrototype(Context *ctx, FunctionObject *ctor); +}; + +struct StringCtor: FunctionObject +{ + StringCtor(Context *scope); + + virtual void construct(Context *ctx); + virtual void call(Context *ctx); +}; + +struct StringPrototype: Object +{ + StringPrototype(Context *ctx, FunctionObject *ctor); + +protected: + static QString getThisString(Context *ctx); + + static void method_toString(Context *ctx); + static void method_valueOf(Context *ctx); + static void method_charAt(Context *ctx); + static void method_charCodeAt(Context *ctx); + static void method_concat(Context *ctx); + static void method_indexOf(Context *ctx); + static void method_lastIndexOf(Context *ctx); + static void method_localeCompare(Context *ctx); + static void method_match(Context *ctx); + static void method_replace(Context *ctx); + static void method_search(Context *ctx); + static void method_slice(Context *ctx); + static void method_split(Context *ctx); + static void method_substr(Context *ctx); + static void method_substring(Context *ctx); + static void method_toLowerCase(Context *ctx); + static void method_toLocaleLowerCase(Context *ctx); + static void method_toUpperCase(Context *ctx); + static void method_toLocaleUpperCase(Context *ctx); + static void method_fromCharCode(Context *ctx); +}; + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/tests/string.1.js b/tests/string.1.js index 5ae5535881..8cc76bb82d 100644 --- a/tests/string.1.js +++ b/tests/string.1.js @@ -11,3 +11,10 @@ print(s3) var s4 = new String.prototype.constructor(321) print(s4) +print(s4.charAt(0), s4.charAt(1), s4.charCodeAt(0)) + +print(s4.concat("ciao", "cool")) +print(s4.indexOf('1')) +print(s4.lastIndexOf('1', 1)) + +print("ss", s4.slice(0, 2)) diff --git a/v4.pro b/v4.pro index c9d9b1a147..fc26789e3f 100644 --- a/v4.pro +++ b/v4.pro @@ -13,7 +13,8 @@ SOURCES += main.cpp \ qmljs_runtime.cpp \ qmljs_objects.cpp \ qv4isel.cpp \ - qv4syntaxchecker.cpp + qv4syntaxchecker.cpp \ + qv4ecmaobjects.cpp HEADERS += \ qv4codegen_p.h \ @@ -23,7 +24,9 @@ HEADERS += \ qv4isel_p.h \ x86-codegen.h \ amd64-codegen.h \ - qv4syntaxchecker_p.h + qv4syntaxchecker_p.h \ + qv4ecmaobjects_p.h + -- cgit v1.2.3 From b8d6b82bcc0b4444605c4a676761f055273f94df Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 14 May 2012 16:35:04 +0200 Subject: Initial work on the Number object. --- main.cpp | 8 +- qv4ecmaobjects.cpp | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++- qv4ecmaobjects_p.h | 21 ++++++ 3 files changed, 240 insertions(+), 5 deletions(-) diff --git a/main.cpp b/main.cpp index a3c0b7326f..1c7c910d14 100644 --- a/main.cpp +++ b/main.cpp @@ -99,10 +99,14 @@ void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &co globalObject->put(VM::String::get(ctx, QLatin1String("Object")), VM::Value::object(ctx, new builtins::ObjectCtor(ctx))); - VM::FunctionObject *stringCtor = new builtins::StringCtor(ctx); - stringCtor->put(prototype, VM::Value::object(ctx, new builtins::StringPrototype(ctx, stringCtor))); + VM::FunctionObject *stringCtor = new VM::StringCtor(ctx); + stringCtor->put(prototype, VM::Value::object(ctx, new VM::StringPrototype(ctx, stringCtor))); globalObject->put(VM::String::get(ctx, QLatin1String("String")), VM::Value::object(ctx, stringCtor)); + VM::FunctionObject *numberCtor = new VM::NumberCtor(ctx); + numberCtor->put(prototype, VM::Value::object(ctx, new VM::NumberPrototype(ctx, numberCtor))); + globalObject->put(VM::String::get(ctx, QLatin1String("Number")), VM::Value::object(ctx, numberCtor)); + foreach (IR::Function *function, module.functions) { if (function->name && ! function->name->isEmpty()) { globalObject->put(VM::String::get(ctx, *function->name), diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 455a454892..6c93003d96 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -40,9 +40,12 @@ StringCtor::StringCtor(Context *scope) void StringCtor::construct(Context *ctx) { - Value arg = ctx->argument(0); - __qmljs_to_string(ctx, &arg, &arg); - __qmljs_init_object(ctx, &ctx->thisObject, new StringObject(arg)); + Value value; + if (ctx->argumentCount) + value = Value::string(ctx, ctx->argument(0).toString(ctx)); + else + value = Value::string(ctx, QString()); + __qmljs_init_object(ctx, &ctx->thisObject, new StringObject(value)); } void StringCtor::call(Context *ctx) @@ -331,3 +334,210 @@ void StringPrototype::method_fromCharCode(Context *ctx) } ctx->result = Value::string(ctx, str); } + +// +// Number object +// +NumberCtor::NumberCtor(Context *scope) + : FunctionObject(scope) +{ +} + +void NumberCtor::construct(Context *ctx) +{ + const double n = ctx->argument(0).toNumber(ctx); + __qmljs_init_object(ctx, &ctx->thisObject, new NumberObject(Value::number(ctx, n))); +} + +void NumberCtor::call(Context *ctx) +{ + double value = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; + __qmljs_init_number(ctx, &ctx->result, value); +} + +NumberPrototype::NumberPrototype(Context *ctx, FunctionObject *ctor) +{ + ctor->setProperty(ctx, QLatin1String("NaN"), Value::number(ctx, qSNaN())); + ctor->setProperty(ctx, QLatin1String("NEGATIVE_INFINITY"), Value::number(ctx, -qInf())); + ctor->setProperty(ctx, QLatin1String("POSITIVE_INFINITY"), Value::number(ctx, qInf())); + ctor->setProperty(ctx, QLatin1String("MAX_VALUE"), Value::number(ctx, 1.7976931348623158e+308)); +#ifdef __INTEL_COMPILER +# pragma warning( push ) +# pragma warning(disable: 239) +#endif + ctor->setProperty(ctx, QLatin1String("MIN_VALUE"), Value::number(ctx, 5e-324)); +#ifdef __INTEL_COMPILER +# pragma warning( pop ) +#endif + + setProperty(ctx, QLatin1String("constructor"), Value::object(ctx, ctor)); + setProperty(ctx, QLatin1String("toString"), method_toString); + setProperty(ctx, QLatin1String("toLocalString"), method_toLocaleString); + setProperty(ctx, QLatin1String("valueOf"), method_valueOf); + setProperty(ctx, QLatin1String("toToFixed"), method_toFixed); + setProperty(ctx, QLatin1String("toExponential"), method_toExponential); + setProperty(ctx, QLatin1String("toPrecision"), method_toPrecision); +} + +void NumberPrototype::method_toString(Context *ctx) +{ + Value self = ctx->thisObject; + assert(self.isObject()); +// if (self.classInfo() != classInfo) { +// return throwThisObjectTypeError( +// ctx, QLatin1String("Number.prototype.toString")); +// } + + Value arg = ctx->argument(0); + if (!arg.isUndefined()) { + int radix = arg.toInt32(ctx); +// if (radix < 2 || radix > 36) +// return ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") +// .arg(radix)); + if (radix != 10) { + Value internalValue; + self.objectValue->defaultValue(&internalValue, NUMBER_HINT); + + QString str; + double num = internalValue.toNumber(ctx); + if (qIsNaN(num)) { + ctx->result = Value::string(ctx, QLatin1String("NaN")); + return; + } else if (qIsInf(num)) { + ctx->result = Value::string(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); + return; + } + bool negative = false; + if (num < 0) { + negative = true; + num = -num; + } + double frac = num - ::floor(num); + num = Value::toInteger(num); + do { + char c = (char)::fmod(num, radix); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.prepend(QLatin1Char(c)); + num = ::floor(num / radix); + } while (num != 0); + if (frac != 0) { + str.append(QLatin1Char('.')); + do { + frac = frac * radix; + char c = (char)::floor(frac); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.append(QLatin1Char(c)); + frac = frac - ::floor(frac); + } while (frac != 0); + } + if (negative) + str.prepend(QLatin1Char('-')); + ctx->result = Value::string(ctx, str); + return; + } + } + + Value internalValue; + self.objectValue->defaultValue(&internalValue, NUMBER_HINT); + + String *str = internalValue.toString(ctx); + ctx->result = Value::string(ctx, str); +} + +void NumberPrototype::method_toLocaleString(Context *ctx) +{ + Value self = ctx->thisObject; + assert(self.isObject()); +// if (self.classInfo() != classInfo) { +// return throwThisObjectTypeError( +// context, QLatin1String("Number.prototype.toLocaleString")); +// } + Value internalValue; + self.objectValue->defaultValue(&internalValue, STRING_HINT); + String *str = internalValue.toString(ctx); + ctx->result = Value::string(ctx, str); +} + +void NumberPrototype::method_valueOf(Context *ctx) +{ + Value self = ctx->thisObject; + assert(self.isObject()); +// if (self.classInfo() != classInfo) { +// return throwThisObjectTypeError( +// context, QLatin1String("Number.prototype.toLocaleString")); +// } + Value internalValue; + self.objectValue->defaultValue(&internalValue, NUMBER_HINT); + ctx->result = internalValue; +} + +void NumberPrototype::method_toFixed(Context *ctx) +{ + Value self = ctx->thisObject; + assert(self.isObject()); +// if (self.classInfo() != classInfo) { +// return throwThisObjectTypeError( +// ctx, QLatin1String("Number.prototype.toFixed")); +// } + double fdigits = 0; + + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(ctx); + + if (qIsNaN(fdigits)) + fdigits = 0; + + Value internalValue; + self.objectValue->defaultValue(&internalValue, NUMBER_HINT); + + double v = internalValue.toNumber(ctx); + QString str; + if (qIsNaN(v)) + str = QString::fromLatin1("NaN"); + else if (qIsInf(v)) + str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); + else + str = QString::number(v, 'f', int (fdigits)); + ctx->result = Value::string(ctx, str); +} + +void NumberPrototype::method_toExponential(Context *ctx) +{ + Value self = ctx->thisObject; + assert(self.isObject()); +// if (self.classInfo() != classInfo) { +// return throwThisObjectTypeError( +// ctx, QLatin1String("Number.prototype.toFixed")); +// } + double fdigits = 0; + + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(ctx); + + Value internalValue; + self.objectValue->defaultValue(&internalValue, NUMBER_HINT); + + double v = internalValue.toNumber(ctx); + QString z = QString::number(v, 'e', int (fdigits)); + ctx->result = Value::string(ctx, z); +} + +void NumberPrototype::method_toPrecision(Context *ctx) +{ + Value self = ctx->thisObject; + assert(self.isObject()); +// if (self.classInfo() != classInfo) { +// return throwThisObjectTypeError( +// ctx, QLatin1String("Number.prototype.toFixed")); +// } + double fdigits = 0; + + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(ctx); + + Value internalValue; + self.objectValue->defaultValue(&internalValue, NUMBER_HINT); + + double v = internalValue.toNumber(ctx); + ctx->result = Value::string(ctx, QString::number(v, 'g', int (fdigits))); +} diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 1b5d6309dc..16d5086fcd 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -56,6 +56,27 @@ protected: static void method_fromCharCode(Context *ctx); }; +struct NumberCtor: FunctionObject +{ + NumberCtor(Context *scope); + + virtual void construct(Context *ctx); + virtual void call(Context *ctx); +}; + +struct NumberPrototype: Object +{ + NumberPrototype(Context *ctx, FunctionObject *ctor); + +protected: + static void method_toString(Context *ctx); + static void method_toLocaleString(Context *ctx); + static void method_valueOf(Context *ctx); + static void method_toFixed(Context *ctx); + static void method_toExponential(Context *ctx); + static void method_toPrecision(Context *ctx); +}; + } // end of namespace VM } // end of namespace QQmlJS -- cgit v1.2.3 From a5109fad15b0d15fcf29e8119df04eb7dbd822bd Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 14 May 2012 16:37:28 +0200 Subject: Fix typo --- qv4ecmaobjects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 6c93003d96..bdb3d3f881 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -374,7 +374,7 @@ NumberPrototype::NumberPrototype(Context *ctx, FunctionObject *ctor) setProperty(ctx, QLatin1String("toString"), method_toString); setProperty(ctx, QLatin1String("toLocalString"), method_toLocaleString); setProperty(ctx, QLatin1String("valueOf"), method_valueOf); - setProperty(ctx, QLatin1String("toToFixed"), method_toFixed); + setProperty(ctx, QLatin1String("toFixed"), method_toFixed); setProperty(ctx, QLatin1String("toExponential"), method_toExponential); setProperty(ctx, QLatin1String("toPrecision"), method_toPrecision); } -- cgit v1.2.3 From 4d77fb3a9baa15fe407e8e241c917a06f836a961 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 14 May 2012 17:12:25 +0200 Subject: Introduce the ExecutionEngine. --- main.cpp | 31 ++++++++----------------------- qmljs_objects.cpp | 48 +++++++++++++++++++++++++++++++++++++++++------- qmljs_objects.h | 34 +++++++++++++++++++++++++++++----- qmljs_runtime.cpp | 34 ++++++++++++++++++---------------- qv4ecmaobjects.cpp | 41 ++++++++++++++++++++++++++++++++--------- qv4ecmaobjects_p.h | 6 ++++++ qv4isel.cpp | 12 +++++------- qv4isel_p.h | 4 ++-- 8 files changed, 141 insertions(+), 69 deletions(-) diff --git a/main.cpp b/main.cpp index 1c7c910d14..459c560d41 100644 --- a/main.cpp +++ b/main.cpp @@ -47,7 +47,7 @@ struct Print: FunctionObject } // builtins -void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &code) +void evaluate(QQmlJS::VM::ExecutionEngine *vm, QQmlJS::Engine *engine, const QString &fileName, const QString &code) { using namespace QQmlJS; @@ -73,7 +73,7 @@ void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &co const size_t codeSize = 10 * getpagesize(); uchar *code = (uchar *) malloc(codeSize); - x86_64::InstructionSelection isel(&module, code); + x86_64::InstructionSelection isel(vm, &module, code); QHash codeByName; foreach (IR::Function *function, module.functions) { isel(function); @@ -85,31 +85,15 @@ void evaluate(QQmlJS::Engine *engine, const QString &fileName, const QString &co if (! protect(code, codeSize)) Q_UNREACHABLE(); - VM::Context *ctx = new VM::Context; - ctx->init(); + VM::Object *globalObject = vm->globalObject.objectValue; + VM::Context *ctx = vm->rootContext; - VM::String *prototype = VM::String::get(ctx, QLatin1String("prototype")); - - VM::Object *globalObject = new VM::ArgumentsObject(ctx); - __qmljs_init_object(ctx, &ctx->activation, globalObject); - - globalObject->put(VM::String::get(ctx, QLatin1String("print")), + globalObject->put(vm->identifier(QLatin1String("print")), VM::Value::object(ctx, new builtins::Print(ctx))); - globalObject->put(VM::String::get(ctx, QLatin1String("Object")), - VM::Value::object(ctx, new builtins::ObjectCtor(ctx))); - - VM::FunctionObject *stringCtor = new VM::StringCtor(ctx); - stringCtor->put(prototype, VM::Value::object(ctx, new VM::StringPrototype(ctx, stringCtor))); - globalObject->put(VM::String::get(ctx, QLatin1String("String")), VM::Value::object(ctx, stringCtor)); - - VM::FunctionObject *numberCtor = new VM::NumberCtor(ctx); - numberCtor->put(prototype, VM::Value::object(ctx, new VM::NumberPrototype(ctx, numberCtor))); - globalObject->put(VM::String::get(ctx, QLatin1String("Number")), VM::Value::object(ctx, numberCtor)); - foreach (IR::Function *function, module.functions) { if (function->name && ! function->name->isEmpty()) { - globalObject->put(VM::String::get(ctx, *function->name), + globalObject->put(vm->identifier(*function->name), VM::Value::object(ctx, new VM::ScriptFunction(ctx, function))); } } @@ -125,13 +109,14 @@ int main(int argc, char *argv[]) QStringList args = app.arguments(); args.removeFirst(); + VM::ExecutionEngine vm; Engine engine; foreach (const QString &fn, args) { QFile file(fn); if (file.open(QFile::ReadOnly)) { const QString code = QString::fromUtf8(file.readAll()); file.close(); - evaluate(&engine, fn, code); + evaluate(&vm, &engine, fn, code); } } } diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index bcc08d3119..704eeadaa8 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -1,6 +1,7 @@ #include "qmljs_objects.h" #include "qv4ir_p.h" +#include "qv4ecmaobjects_p.h" #include #include @@ -13,7 +14,7 @@ Object::~Object() void Object::setProperty(Context *ctx, const QString &name, const Value &value) { - put(String::get(ctx, name), value); + put(ctx->engine->identifier(name), value); } void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context *)) @@ -96,15 +97,13 @@ bool Object::deleteProperty(String *name, bool flag) return false; } -void Object::defaultValue(Value *result, int typeHint) +void Object::defaultValue(Context *ctx, Value *result, int typeHint) { - Context *ctx = 0; // ### - if (typeHint == STRING_HINT) { if (asFunctionObject() != 0) - __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("function"))); + __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("function"))); else - __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("object"))); + __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("object"))); } else { __qmljs_init_undefined(ctx, result); } @@ -136,7 +135,7 @@ ScriptFunction::ScriptFunction(Context *scope, IR::Function *function) if (formalParameterCount) { formalParameterList = new String*[formalParameterCount]; for (size_t i = 0; i < formalParameterCount; ++i) { - formalParameterList[i] = String::get(0, *function->formals.at(i)); // ### unique + formalParameterList[i] = scope->engine->identifier(*function->formals.at(i)); } } } @@ -173,3 +172,38 @@ Value *ArgumentsObject::getProperty(String *name, PropertyAttributes *attributes return prop; return 0; } + +ExecutionEngine::ExecutionEngine() +{ + rootContext = new VM::Context; + rootContext->init(this); + + // + // set up the global object + // + VM::Object *glo = new VM::ArgumentsObject(rootContext); + __qmljs_init_object(rootContext, &globalObject, glo); + __qmljs_init_object(rootContext, &rootContext->activation, glo); + + objectCtor = ObjectCtor::create(this); + stringCtor = StringCtor::create(this); + numberCtor = NumberCtor::create(this); + + String *prototype = String::get(rootContext, QLatin1String("prototype")); + + objectCtor.objectValue->get(prototype, &objectPrototype); + stringCtor.objectValue->get(prototype, &stringPrototype); + numberCtor.objectValue->get(prototype, &numberPrototype); + + glo->put(VM::String::get(rootContext, QLatin1String("Object")), objectCtor); + glo->put(VM::String::get(rootContext, QLatin1String("String")), stringCtor); + glo->put(VM::String::get(rootContext, QLatin1String("Number")), numberCtor); +} + +String *ExecutionEngine::identifier(const QString &s) +{ + String *&id = identifiers[s]; + if (! id) + id = new String(s); + return id; +} diff --git a/qmljs_objects.h b/qmljs_objects.h index 2dd4b096cf..7d1a328d2a 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -23,6 +23,8 @@ struct StringObject; struct ArrayObject; struct FunctionObject; struct ErrorObject; +struct Context; +struct ExecutionEngine; struct String { String(const QString &text) @@ -207,7 +209,7 @@ struct Object { virtual bool canPut(String *name); virtual bool hasProperty(String *name) const; virtual bool deleteProperty(String *name, bool flag); - virtual void defaultValue(Value *result, int typeHint); + virtual void defaultValue(Context *ctx, Value *result, int typeHint); // ### TODO: defineOwnProperty(name, descriptor, boolean) -> boolean // @@ -220,19 +222,19 @@ struct Object { struct BooleanObject: Object { Value value; BooleanObject(const Value &value): value(value) {} - virtual void defaultValue(Value *result, int /*typehint*/) { *result = value; } + virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } }; struct NumberObject: Object { Value value; NumberObject(const Value &value): value(value) {} - virtual void defaultValue(Value *result, int /*typehint*/) { *result = value; } + virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } }; struct StringObject: Object { Value value; StringObject(const Value &value): value(value) {} - virtual void defaultValue(Value *result, int /*typehint*/) { *result = value; } + virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } }; struct ArrayObject: Object { @@ -286,6 +288,7 @@ struct ArgumentsObject: Object { }; struct Context { + ExecutionEngine *engine; Context *parent; Value activation; Value thisObject; @@ -319,8 +322,9 @@ struct Context { __qmljs_init_undefined(this, result); } - void init() + void init(ExecutionEngine *eng) { + engine = eng; parent = 0; arguments = 0; argumentCount = 0; @@ -333,6 +337,26 @@ struct Context { } }; +struct ExecutionEngine +{ + Context *rootContext; + Value globalObject; + + Value objectCtor; + Value stringCtor; + Value numberCtor; + + Value objectPrototype; + Value stringPrototype; + Value numberPrototype; + + QHash identifiers; + + ExecutionEngine(); + + String *identifier(const QString &s); +}; + } // namespace VM } // namespace QQmlJS diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 6fdf179761..68365f0a87 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -79,47 +79,47 @@ void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos) void __qmljs_string_literal_undefined(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("undefined"))); + __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("undefined"))); } void __qmljs_string_literal_null(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("null"))); + __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("null"))); } void __qmljs_string_literal_true(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("true"))); + __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("true"))); } void __qmljs_string_literal_false(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("false"))); + __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("false"))); } void __qmljs_string_literal_object(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("object"))); + __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("object"))); } void __qmljs_string_literal_boolean(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("boolean"))); + __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("boolean"))); } void __qmljs_string_literal_number(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("number"))); + __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("number"))); } void __qmljs_string_literal_string(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("string"))); + __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("string"))); } void __qmljs_string_literal_function(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("function"))); + __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("function"))); } void __qmljs_delete(Context *ctx, Value *result, const Value *value) @@ -198,7 +198,7 @@ bool __qmljs_is_function(Context *, const Value *value) void __qmljs_object_default_value(Context *ctx, Value *result, Object *object, int typeHint) { Q_UNUSED(ctx); - object->defaultValue(result, typeHint); + object->defaultValue(ctx, result, typeHint); } void __qmljs_throw_type_error(Context *ctx, Value *result) @@ -218,6 +218,7 @@ void __qmljs_new_number_object(Context *ctx, Value *result, double number) Value value; __qmljs_init_number(ctx, &value, number); __qmljs_init_object(ctx, result, new NumberObject(value)); + result->objectValue->prototype = ctx->engine->numberPrototype.objectValue; } void __qmljs_new_string_object(Context *ctx, Value *result, String *string) @@ -225,6 +226,7 @@ void __qmljs_new_string_object(Context *ctx, Value *result, String *string) Value value; __qmljs_init_string(ctx, &value, string); __qmljs_init_object(ctx, result, new StringObject(value)); + result->objectValue->prototype = ctx->engine->stringPrototype.objectValue; } void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value) @@ -451,7 +453,7 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S if (func.type == OBJECT_TYPE) { if (FunctionObject *f = func.objectValue->asFunctionObject()) { Context *ctx = new Context; - ctx->init(); + ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); @@ -484,7 +486,7 @@ void __qmljs_call_value(Context *context, Value *result, const Value *func, Valu if (func->type == OBJECT_TYPE) { if (FunctionObject *f = func->objectValue->asFunctionObject()) { Context *ctx = new Context; - ctx->init(); + ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); @@ -525,7 +527,7 @@ void __qmljs_construct_value(Context *context, Value *result, const Value *func, if (func->type == OBJECT_TYPE) { if (FunctionObject *f = func->objectValue->asFunctionObject()) { Context *ctx = new Context; - ctx->init(); + ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); @@ -545,7 +547,7 @@ void __qmljs_construct_value(Context *context, Value *result, const Value *func, assert(ctx->thisObject.is(OBJECT_TYPE)); ctx->result = ctx->thisObject; Value proto; - if (f->get(String::get(ctx, QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol + if (f->get(ctx->engine->identifier(QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol if (proto.type == OBJECT_TYPE) ctx->thisObject.objectValue->prototype = proto.objectValue; } @@ -572,7 +574,7 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba if (func.type == OBJECT_TYPE) { if (FunctionObject *f = func.objectValue->asFunctionObject()) { Context *ctx = new Context; - ctx->init(); + ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); @@ -592,7 +594,7 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba assert(ctx->thisObject.is(OBJECT_TYPE)); Value proto; - if (f->get(String::get(ctx, QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol + if (f->get(ctx->engine->identifier(QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol if (proto.type == OBJECT_TYPE) ctx->thisObject.objectValue->prototype = proto.objectValue; } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index bdb3d3f881..9b678dcd11 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -9,6 +9,14 @@ using namespace QQmlJS::VM; // // Object // +Value ObjectCtor::create(ExecutionEngine *engine) +{ + Context *ctx = engine->rootContext; + ObjectCtor *ctor = new ObjectCtor(ctx); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::object(ctx, new ObjectPrototype(ctx, ctor))); + return Value::object(ctx, ctor); +} + ObjectCtor::ObjectCtor(Context *scope) : FunctionObject(scope) { @@ -24,7 +32,6 @@ void ObjectCtor::call(Context *) assert(!"not here"); } - ObjectPrototype::ObjectPrototype(Context *ctx, FunctionObject *ctor) { setProperty(ctx, QLatin1String("constructor"), Value::object(ctx, ctor)); @@ -33,6 +40,14 @@ ObjectPrototype::ObjectPrototype(Context *ctx, FunctionObject *ctor) // // String // +Value StringCtor::create(ExecutionEngine *engine) +{ + Context *ctx = engine->rootContext; + StringCtor *ctor = new StringCtor(ctx); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::object(ctx, new StringPrototype(ctx, ctor))); + return Value::object(ctx, ctor); +} + StringCtor::StringCtor(Context *scope) : FunctionObject(scope) { @@ -97,7 +112,7 @@ void StringPrototype::method_toString(Context *ctx) void StringPrototype::method_valueOf(Context *ctx) { - ctx->thisObject.objectValue->defaultValue(&ctx->result, STRING_HINT); + ctx->thisObject.objectValue->defaultValue(ctx, &ctx->result, STRING_HINT); } void StringPrototype::method_charAt(Context *ctx) @@ -338,6 +353,14 @@ void StringPrototype::method_fromCharCode(Context *ctx) // // Number object // +Value NumberCtor::create(ExecutionEngine *engine) +{ + Context *ctx = engine->rootContext; + NumberCtor *ctor = new NumberCtor(ctx); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::object(ctx, new NumberPrototype(ctx, ctor))); + return Value::object(ctx, ctor); +} + NumberCtor::NumberCtor(Context *scope) : FunctionObject(scope) { @@ -396,7 +419,7 @@ void NumberPrototype::method_toString(Context *ctx) // .arg(radix)); if (radix != 10) { Value internalValue; - self.objectValue->defaultValue(&internalValue, NUMBER_HINT); + self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); QString str; double num = internalValue.toNumber(ctx); @@ -438,7 +461,7 @@ void NumberPrototype::method_toString(Context *ctx) } Value internalValue; - self.objectValue->defaultValue(&internalValue, NUMBER_HINT); + self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); String *str = internalValue.toString(ctx); ctx->result = Value::string(ctx, str); @@ -453,7 +476,7 @@ void NumberPrototype::method_toLocaleString(Context *ctx) // context, QLatin1String("Number.prototype.toLocaleString")); // } Value internalValue; - self.objectValue->defaultValue(&internalValue, STRING_HINT); + self.objectValue->defaultValue(ctx, &internalValue, STRING_HINT); String *str = internalValue.toString(ctx); ctx->result = Value::string(ctx, str); } @@ -467,7 +490,7 @@ void NumberPrototype::method_valueOf(Context *ctx) // context, QLatin1String("Number.prototype.toLocaleString")); // } Value internalValue; - self.objectValue->defaultValue(&internalValue, NUMBER_HINT); + self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); ctx->result = internalValue; } @@ -488,7 +511,7 @@ void NumberPrototype::method_toFixed(Context *ctx) fdigits = 0; Value internalValue; - self.objectValue->defaultValue(&internalValue, NUMBER_HINT); + self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); double v = internalValue.toNumber(ctx); QString str; @@ -515,7 +538,7 @@ void NumberPrototype::method_toExponential(Context *ctx) fdigits = ctx->argument(0).toInteger(ctx); Value internalValue; - self.objectValue->defaultValue(&internalValue, NUMBER_HINT); + self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); double v = internalValue.toNumber(ctx); QString z = QString::number(v, 'e', int (fdigits)); @@ -536,7 +559,7 @@ void NumberPrototype::method_toPrecision(Context *ctx) fdigits = ctx->argument(0).toInteger(ctx); Value internalValue; - self.objectValue->defaultValue(&internalValue, NUMBER_HINT); + self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); double v = internalValue.toNumber(ctx); ctx->result = Value::string(ctx, QString::number(v, 'g', int (fdigits))); diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 16d5086fcd..4d8cb1d7b1 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -8,6 +8,8 @@ namespace VM { struct ObjectCtor: FunctionObject { + static Value create(ExecutionEngine *engine); + ObjectCtor(Context *scope); virtual void construct(Context *ctx); @@ -21,6 +23,8 @@ struct ObjectPrototype: Object struct StringCtor: FunctionObject { + static Value create(ExecutionEngine *engine); + StringCtor(Context *scope); virtual void construct(Context *ctx); @@ -58,6 +62,8 @@ protected: struct NumberCtor: FunctionObject { + static Value create(ExecutionEngine *engine); + NumberCtor(Context *scope); virtual void construct(Context *ctx); diff --git a/qv4isel.cpp b/qv4isel.cpp index 260cdd0637..e6bdd1e100 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -80,8 +80,9 @@ amd64_patch (unsigned char* code, gpointer target) else x86_patch (code, (unsigned char*)target); } -InstructionSelection::InstructionSelection(IR::Module *module, uchar *buffer) - : _module(module) +InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *buffer) + : _engine(engine) + , _module(module) , _function(0) , _block(0) , _buffer(buffer) @@ -157,10 +158,7 @@ void InstructionSelection::operator()(IR::Function *function) String *InstructionSelection::identifier(const QString &s) { - String *&id = _identifiers[s]; - if (! id) - id = new String(s); - return id; + return _engine->identifier(s); } void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) @@ -638,7 +636,7 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, base); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, VM::String::get(0, *str->value)); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, VM::String::get(_engine->rootContext, *str->value)); amd64_call_code(_codePtr, __qmljs_set_property_string); return; } else if (IR::Temp *t = s->source->asTemp()) { diff --git a/qv4isel_p.h b/qv4isel_p.h index 22f8056376..ac6092d5a5 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -12,7 +12,7 @@ namespace x86_64 { class InstructionSelection: protected IR::StmtVisitor { public: - InstructionSelection(IR::Module *module, uchar *code); + InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *code); ~InstructionSelection(); void operator()(IR::Function *function); @@ -35,6 +35,7 @@ protected: virtual void visitRet(IR::Ret *); private: + VM::ExecutionEngine *_engine; IR::Module *_module; IR::Function *_function; IR::BasicBlock *_block; @@ -43,7 +44,6 @@ private: uchar *_codePtr; QHash > _patches; QHash _addrs; - QHash _identifiers; }; } // end of namespace x86_64 -- cgit v1.2.3 From acc535d18fb3618cf0e0dc4018ea76c5c0aad109 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 14 May 2012 17:38:53 +0200 Subject: Initial implementation of the Math Object. --- qmljs_objects.cpp | 5 +- qmljs_objects.h | 2 +- qv4ecmaobjects.cpp | 310 ++++++++++++++++++++++++++++++++++++++++++++++++----- qv4ecmaobjects_p.h | 25 +++++ 4 files changed, 312 insertions(+), 30 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 704eeadaa8..aed6c37b6d 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -17,8 +17,9 @@ void Object::setProperty(Context *ctx, const QString &name, const Value &value) put(ctx->engine->identifier(name), value); } -void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context *)) +void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context *), int count) { + Q_UNUSED(count); setProperty(ctx, name, Value::object(ctx, new NativeFunction(ctx, code))); } @@ -131,6 +132,7 @@ ScriptFunction::ScriptFunction(Context *scope, IR::Function *function) : FunctionObject(scope) , function(function) { + needsActivation = function->needsActivation(); formalParameterCount = function->formals.size(); if (formalParameterCount) { formalParameterList = new String*[formalParameterCount]; @@ -198,6 +200,7 @@ ExecutionEngine::ExecutionEngine() glo->put(VM::String::get(rootContext, QLatin1String("Object")), objectCtor); glo->put(VM::String::get(rootContext, QLatin1String("String")), stringCtor); glo->put(VM::String::get(rootContext, QLatin1String("Number")), numberCtor); + glo->put(VM::String::get(rootContext, QLatin1String("Math")), Value::object(rootContext, new MathObject(rootContext))); } String *ExecutionEngine::identifier(const QString &s) diff --git a/qmljs_objects.h b/qmljs_objects.h index 7d1a328d2a..6c644918c1 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -216,7 +216,7 @@ struct Object { // helpers // void setProperty(Context *ctx, const QString &name, const Value &value); - void setProperty(Context *ctx, const QString &name, void (*code)(Context *)); + void setProperty(Context *ctx, const QString &name, void (*code)(Context *), int count = 0); }; struct BooleanObject: Object { diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 9b678dcd11..1e00940fe8 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -6,6 +6,8 @@ using namespace QQmlJS::VM; +static const double qt_PI = 2.0 * ::asin(1.0); + // // Object // @@ -236,7 +238,7 @@ void StringPrototype::method_slice(Context *ctx) int start = int (ctx->argument(0).toInteger(ctx)); int end = ctx->argument(1).isUndefined() - ? length : int (ctx->argument(1).toInteger(ctx)); + ? length : int (ctx->argument(1).toInteger(ctx)); if (start < 0) start = qMax(length + start, 0); @@ -406,17 +408,17 @@ void NumberPrototype::method_toString(Context *ctx) { Value self = ctx->thisObject; assert(self.isObject()); -// if (self.classInfo() != classInfo) { -// return throwThisObjectTypeError( -// ctx, QLatin1String("Number.prototype.toString")); -// } + // if (self.classInfo() != classInfo) { + // return throwThisObjectTypeError( + // ctx, QLatin1String("Number.prototype.toString")); + // } Value arg = ctx->argument(0); if (!arg.isUndefined()) { int radix = arg.toInt32(ctx); -// if (radix < 2 || radix > 36) -// return ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") -// .arg(radix)); + // if (radix < 2 || radix > 36) + // return ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") + // .arg(radix)); if (radix != 10) { Value internalValue; self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); @@ -471,10 +473,10 @@ void NumberPrototype::method_toLocaleString(Context *ctx) { Value self = ctx->thisObject; assert(self.isObject()); -// if (self.classInfo() != classInfo) { -// return throwThisObjectTypeError( -// context, QLatin1String("Number.prototype.toLocaleString")); -// } + // if (self.classInfo() != classInfo) { + // return throwThisObjectTypeError( + // context, QLatin1String("Number.prototype.toLocaleString")); + // } Value internalValue; self.objectValue->defaultValue(ctx, &internalValue, STRING_HINT); String *str = internalValue.toString(ctx); @@ -485,10 +487,10 @@ void NumberPrototype::method_valueOf(Context *ctx) { Value self = ctx->thisObject; assert(self.isObject()); -// if (self.classInfo() != classInfo) { -// return throwThisObjectTypeError( -// context, QLatin1String("Number.prototype.toLocaleString")); -// } + // if (self.classInfo() != classInfo) { + // return throwThisObjectTypeError( + // context, QLatin1String("Number.prototype.toLocaleString")); + // } Value internalValue; self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); ctx->result = internalValue; @@ -498,10 +500,10 @@ void NumberPrototype::method_toFixed(Context *ctx) { Value self = ctx->thisObject; assert(self.isObject()); -// if (self.classInfo() != classInfo) { -// return throwThisObjectTypeError( -// ctx, QLatin1String("Number.prototype.toFixed")); -// } + // if (self.classInfo() != classInfo) { + // return throwThisObjectTypeError( + // ctx, QLatin1String("Number.prototype.toFixed")); + // } double fdigits = 0; if (ctx->argumentCount > 0) @@ -528,10 +530,10 @@ void NumberPrototype::method_toExponential(Context *ctx) { Value self = ctx->thisObject; assert(self.isObject()); -// if (self.classInfo() != classInfo) { -// return throwThisObjectTypeError( -// ctx, QLatin1String("Number.prototype.toFixed")); -// } + // if (self.classInfo() != classInfo) { + // return throwThisObjectTypeError( + // ctx, QLatin1String("Number.prototype.toFixed")); + // } double fdigits = 0; if (ctx->argumentCount > 0) @@ -549,10 +551,10 @@ void NumberPrototype::method_toPrecision(Context *ctx) { Value self = ctx->thisObject; assert(self.isObject()); -// if (self.classInfo() != classInfo) { -// return throwThisObjectTypeError( -// ctx, QLatin1String("Number.prototype.toFixed")); -// } + // if (self.classInfo() != classInfo) { + // return throwThisObjectTypeError( + // ctx, QLatin1String("Number.prototype.toFixed")); + // } double fdigits = 0; if (ctx->argumentCount > 0) @@ -564,3 +566,255 @@ void NumberPrototype::method_toPrecision(Context *ctx) double v = internalValue.toNumber(ctx); ctx->result = Value::string(ctx, QString::number(v, 'g', int (fdigits))); } + +// +// Math object +// +MathObject::MathObject(Context *ctx) +{ + setProperty(ctx, QLatin1String("E"), Value::number(ctx, ::exp(1.0))); + setProperty(ctx, QLatin1String("LN2"), Value::number(ctx, ::log(2.0))); + setProperty(ctx, QLatin1String("LN10"), Value::number(ctx, ::log(10.0))); + setProperty(ctx, QLatin1String("LOG2E"), Value::number(ctx, 1.0/::log(2.0))); + setProperty(ctx, QLatin1String("LOG10E"), Value::number(ctx, 1.0/::log(10.0))); + setProperty(ctx, QLatin1String("PI"), Value::number(ctx, qt_PI)); + setProperty(ctx, QLatin1String("SQRT1_2"), Value::number(ctx, ::sqrt(0.5))); + setProperty(ctx, QLatin1String("SQRT2"), Value::number(ctx, ::sqrt(2.0))); + + setProperty(ctx, QLatin1String("abs"), method_abs, 1); + setProperty(ctx, QLatin1String("acos"), method_acos, 1); + setProperty(ctx, QLatin1String("asin"), method_asin, 0); + setProperty(ctx, QLatin1String("atan"), method_atan, 1); + setProperty(ctx, QLatin1String("atan2"), method_atan2, 2); + setProperty(ctx, QLatin1String("ceil"), method_ceil, 1); + setProperty(ctx, QLatin1String("cos"), method_cos, 1); + setProperty(ctx, QLatin1String("exp"), method_exp, 1); + setProperty(ctx, QLatin1String("floor"), method_floor, 1); + setProperty(ctx, QLatin1String("log"), method_log, 1); + setProperty(ctx, QLatin1String("max"), method_max, 2); + setProperty(ctx, QLatin1String("min"), method_min, 2); + setProperty(ctx, QLatin1String("pow"), method_pow, 2); + setProperty(ctx, QLatin1String("random"), method_random, 0); + setProperty(ctx, QLatin1String("round"), method_round, 1); + setProperty(ctx, QLatin1String("sin"), method_sin, 1); + setProperty(ctx, QLatin1String("sqrt"), method_sqrt, 1); + setProperty(ctx, QLatin1String("tan"), method_tan, 1); +} + +/* copies the sign from y to x and returns the result */ +static double copySign(double x, double y) +{ + uchar *xch = (uchar *)&x; + uchar *ych = (uchar *)&y; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + xch[0] = (xch[0] & 0x7f) | (ych[0] & 0x80); + else + xch[7] = (xch[7] & 0x7f) | (ych[7] & 0x80); + return x; +} + +void MathObject::method_abs(Context *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v == 0) // 0 | -0 + ctx->result = Value::number(ctx, 0); + else + ctx->result = Value::number(ctx, v < 0 ? -v : v); +} + +void MathObject::method_acos(Context *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v > 1) + ctx->result = Value::number(ctx, qSNaN()); + else + ctx->result = Value::number(ctx, ::acos(v)); +} + +void MathObject::method_asin(Context *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v > 1) + ctx->result = Value::number(ctx, qSNaN()); + else + ctx->result = Value::number(ctx, ::asin(v)); +} + +void MathObject::method_atan(Context *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v == 0.0) + ctx->result = Value::number(ctx, v); + else + ctx->result = Value::number(ctx, ::atan(v)); +} + +void MathObject::method_atan2(Context *ctx) +{ + double v1 = ctx->argument(0).toNumber(ctx); + double v2 = ctx->argument(1).toNumber(ctx); + if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) { + ctx->result = Value::number(ctx, copySign(0, -1.0)); + return; + } + if ((v1 == 0.0) && (v2 == 0.0)) { + if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) { + ctx->result = Value::number(ctx, qt_PI); + return; + } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) { + ctx->result = Value::number(ctx, -qt_PI); + return; + } + } + ctx->result = Value::number(ctx, ::atan2(v1, v2)); +} + +void MathObject::method_ceil(Context *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v < 0.0 && v > -1.0) + ctx->result = Value::number(ctx, copySign(0, -1.0)); + else + ctx->result = Value::number(ctx, ::ceil(v)); +} + +void MathObject::method_cos(Context *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + ctx->result = Value::number(ctx, ::cos(v)); +} + +void MathObject::method_exp(Context *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (qIsInf(v)) { + if (copySign(1.0, v) == -1.0) + ctx->result = Value::number(ctx, 0); + else + ctx->result = Value::number(ctx, qInf()); + } else { + ctx->result = Value::number(ctx, ::exp(v)); + } +} + +void MathObject::method_floor(Context *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + ctx->result = Value::number(ctx, ::floor(v)); +} + +void MathObject::method_log(Context *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v < 0) + ctx->result = Value::number(ctx, qSNaN()); + else + ctx->result = Value::number(ctx, ::log(v)); +} + +void MathObject::method_max(Context *ctx) +{ + double mx = -qInf(); + for (unsigned i = 0; i < ctx->argumentCount; ++i) { + double x = ctx->argument(i).toNumber(ctx); + if (x > mx || qIsNaN(x)) + mx = x; + } + ctx->result = Value::number(ctx, mx); +} + +void MathObject::method_min(Context *ctx) +{ + double mx = qInf(); + for (unsigned i = 0; i < ctx->argumentCount; ++i) { + double x = ctx->argument(i).toNumber(ctx); + if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) + || (x < mx) || qIsNaN(x)) { + mx = x; + } + } + ctx->result = Value::number(ctx, mx); +} + +void MathObject::method_pow(Context *ctx) +{ + double x = ctx->argument(0).toNumber(ctx); + double y = ctx->argument(1).toNumber(ctx); + + if (qIsNaN(y)) { + ctx->result = Value::number(ctx, qSNaN()); + return; + } + + if (y == 0) { + ctx->result = Value::number(ctx, 1); + } else if (((x == 1) || (x == -1)) && qIsInf(y)) { + ctx->result = Value::number(ctx, qSNaN()); + } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { + ctx->result = Value::number(ctx, qInf()); + } else if ((x == 0) && copySign(1.0, x) == -1.0) { + if (y < 0) { + if (::fmod(-y, 2.0) == 1.0) + ctx->result = Value::number(ctx, -qInf()); + else + ctx->result = Value::number(ctx, qInf()); + } else if (y > 0) { + if (::fmod(y, 2.0) == 1.0) + ctx->result = Value::number(ctx, copySign(0, -1.0)); + else + ctx->result = Value::number(ctx, 0); + } + } + +#ifdef Q_OS_AIX + else if (qIsInf(x) && copySign(1.0, x) == -1.0) { + if (y > 0) { + if (::fmod(y, 2.0) == 1.0) + ctx->result = Value::number(ctx, -qInf()); + else + ctx->result = Value::number(ctx, qInf()); + } else if (y < 0) { + if (::fmod(-y, 2.0) == 1.0) + ctx->result = Value::number(ctx, copySign(0, -1.0)); + else + ctx->result = Value::number(ctx, 0); + } + } +#endif + else { + ctx->result = Value::number(ctx, ::pow(x, y)); + } +} + +void MathObject::method_random(Context *ctx) +{ + ctx->result = Value::number(ctx, qrand() / (double) RAND_MAX); +} + +void MathObject::method_round(Context *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + v = copySign(::floor(v + 0.5), v); + ctx->result = Value::number(ctx, v); +} + +void MathObject::method_sin(Context *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + ctx->result = Value::number(ctx, ::sin(v)); +} + +void MathObject::method_sqrt(Context *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + ctx->result = Value::number(ctx, ::sqrt(v)); +} + +void MathObject::method_tan(Context *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v == 0.0) + ctx->result = Value::number(ctx, v); + else + ctx->result = Value::number(ctx, ::tan(v)); +} diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 4d8cb1d7b1..386c2f11f4 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -83,6 +83,31 @@ protected: static void method_toPrecision(Context *ctx); }; +struct MathObject: Object +{ + MathObject(Context *ctx); + +protected: + static void method_abs(Context *ctx); + static void method_acos(Context *ctx); + static void method_asin(Context *ctx); + static void method_atan(Context *ctx); + static void method_atan2(Context *ctx); + static void method_ceil(Context *ctx); + static void method_cos(Context *ctx); + static void method_exp(Context *ctx); + static void method_floor(Context *ctx); + static void method_log(Context *ctx); + static void method_max(Context *ctx); + static void method_min(Context *ctx); + static void method_pow(Context *ctx); + static void method_random(Context *ctx); + static void method_round(Context *ctx); + static void method_sin(Context *ctx); + static void method_sqrt(Context *ctx); + static void method_tan(Context *ctx); +}; + } // end of namespace VM } // end of namespace QQmlJS -- cgit v1.2.3 From 0370c15f5e98c70e115c8cfdd1208a0765c4b51c Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 14 May 2012 17:42:47 +0200 Subject: Fix the activation. --- qmljs_objects.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index aed6c37b6d..b9d0da77a9 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -132,7 +132,6 @@ ScriptFunction::ScriptFunction(Context *scope, IR::Function *function) : FunctionObject(scope) , function(function) { - needsActivation = function->needsActivation(); formalParameterCount = function->formals.size(); if (formalParameterCount) { formalParameterList = new String*[formalParameterCount]; -- cgit v1.2.3 From b0f2345a4758c78f2d28595e4eabc955d4f28da5 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 14 May 2012 17:58:33 +0200 Subject: Skip TEMPs with negative index. That is, TEMP nodes with negative index are a bit special, we use them to optimize the argument passing. --- qv4codegen.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index f1aea0c60b..15f525d000 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -70,6 +70,9 @@ struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } virtual void visitTemp(IR::Temp *e) { + if (e->index < 0) + return; + if (! _stmt->d->uses.contains(e->index)) _stmt->d->uses.append(e->index); } @@ -88,8 +91,10 @@ struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor virtual void visitMove(IR::Move *s) { if (IR::Temp *t = s->target->asTemp()) { - if (! _stmt->d->defs.contains(t->index)) - _stmt->d->defs.append(t->index); + if (t->index >= 0) { + if (! _stmt->d->defs.contains(t->index)) + _stmt->d->defs.append(t->index); + } } else { s->target->accept(this); } -- cgit v1.2.3 From 87df1fc85e4ac76c4a640484ac1ec28d8e09b5e3 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 15 May 2012 09:05:03 +0200 Subject: Use %rbp to index the local variables. --- qv4isel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4isel.cpp b/qv4isel.cpp index e6bdd1e100..6e60cc9f78 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -168,7 +168,7 @@ void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) amd64_mov_reg_membase(_codePtr, reg, AMD64_R14, offsetof(Context, arguments), 8); amd64_lea_membase(_codePtr, reg, reg, sizeof(Value) * arg); } else { - amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * t->index); + amd64_lea_membase(_codePtr, reg, AMD64_RBP, sizeof(Value) * -t->index); } } -- cgit v1.2.3 From 35c89b6f0a789ce67cb640685090af1ad17b79ff Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 15 May 2012 10:02:21 +0200 Subject: Create the activation only when needed. --- qmljs_objects.cpp | 1 + qmljs_runtime.cpp | 12 ++++++++---- qv4codegen.cpp | 1 + tests/fact.2.js | 8 ++++++++ 4 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 tests/fact.2.js diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index b9d0da77a9..aed6c37b6d 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -132,6 +132,7 @@ ScriptFunction::ScriptFunction(Context *scope, IR::Function *function) : FunctionObject(scope) , function(function) { + needsActivation = function->needsActivation(); formalParameterCount = function->formals.size(); if (formalParameterCount) { formalParameterList = new String*[formalParameterCount]; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 68365f0a87..a93cab93ef 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -452,7 +452,8 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S baseObject.objectValue->get(name, &func); if (func.type == OBJECT_TYPE) { if (FunctionObject *f = func.objectValue->asFunctionObject()) { - Context *ctx = new Context; + Context k; + Context *ctx = f->needsActivation ? new Context : &k; ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) @@ -485,7 +486,8 @@ void __qmljs_call_value(Context *context, Value *result, const Value *func, Valu if (func->type == OBJECT_TYPE) { if (FunctionObject *f = func->objectValue->asFunctionObject()) { - Context *ctx = new Context; + Context k; + Context *ctx = f->needsActivation ? new Context : &k; ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) @@ -526,7 +528,8 @@ void __qmljs_construct_value(Context *context, Value *result, const Value *func, Q_UNUSED(context); if (func->type == OBJECT_TYPE) { if (FunctionObject *f = func->objectValue->asFunctionObject()) { - Context *ctx = new Context; + Context k; + Context *ctx = f->needsActivation ? new Context : &k; ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) @@ -573,7 +576,8 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba thisObject.objectValue->get(name, &func); if (func.type == OBJECT_TYPE) { if (FunctionObject *f = func.objectValue->asFunctionObject()) { - Context *ctx = new Context; + Context k; + Context *ctx = f->needsActivation ? new Context : &k; ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 15f525d000..608eba848e 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1357,6 +1357,7 @@ void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) IR::BasicBlock *entryBlock = function->newBasicBlock(); IR::BasicBlock *exitBlock = function->newBasicBlock(); function->hasDirectEval = functionInfo.hasDirectEval; + function->hasNestedFunctions = functionInfo.hasNestedFunctions; function->maxNumberOfArguments = functionInfo.maxNumberOfArguments; if (! function->needsActivation()) { diff --git a/tests/fact.2.js b/tests/fact.2.js new file mode 100644 index 0000000000..d8f750b5a1 --- /dev/null +++ b/tests/fact.2.js @@ -0,0 +1,8 @@ + +function fact(n) { + return n > 1 ? n * fact(n - 1) : 1 +} + +for (var i = 0; i < 1000000; i = i + 1) + fact(12) + -- cgit v1.2.3 From d9c263385cd2953d41233ad035840ff83f636439 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 15 May 2012 10:35:19 +0200 Subject: Add methods to allocate JS entities. --- main.cpp | 2 +- qmljs_objects.cpp | 104 +++++++++++++++++++++++++++++++++++++++++++++++++---- qmljs_objects.h | 32 +++++++++++++---- qmljs_runtime.cpp | 26 +++++++------- qv4ecmaobjects.cpp | 18 +++++----- qv4isel.cpp | 4 +-- 6 files changed, 148 insertions(+), 38 deletions(-) diff --git a/main.cpp b/main.cpp index 459c560d41..e2feac9c6c 100644 --- a/main.cpp +++ b/main.cpp @@ -94,7 +94,7 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, QQmlJS::Engine *engine, const QSt foreach (IR::Function *function, module.functions) { if (function->name && ! function->name->isEmpty()) { globalObject->put(vm->identifier(*function->name), - VM::Value::object(ctx, new VM::ScriptFunction(ctx, function))); + VM::Value::object(ctx, ctx->engine->newScriptFunction(ctx, function))); } } codeByName.value(QLatin1String("%entry"))->code(ctx); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index aed6c37b6d..fd9647e808 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -7,6 +7,11 @@ using namespace QQmlJS::VM; +String *String::get(Context *ctx, const QString &s) +{ + return ctx->engine->newString(s); +} + Object::~Object() { delete members; @@ -20,7 +25,7 @@ void Object::setProperty(Context *ctx, const QString &name, const Value &value) void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context *), int count) { Q_UNUSED(count); - setProperty(ctx, name, Value::object(ctx, new NativeFunction(ctx, code))); + setProperty(ctx, name, Value::object(ctx, ctx->engine->newNativeFunction(ctx, code))); } bool Object::get(String *name, Value *result) @@ -124,7 +129,7 @@ void FunctionObject::call(Context *ctx) void FunctionObject::construct(Context *ctx) { - __qmljs_init_object(ctx, &ctx->thisObject, new Object()); + __qmljs_init_object(ctx, &ctx->thisObject, ctx->engine->newObject()); call(ctx); } @@ -154,7 +159,7 @@ void ScriptFunction::call(VM::Context *ctx) void ScriptFunction::construct(VM::Context *ctx) { - __qmljs_init_object(ctx, &ctx->thisObject, new Object()); + __qmljs_init_object(ctx, &ctx->thisObject, ctx->engine->newObject()); function->code(ctx); } @@ -177,13 +182,13 @@ Value *ArgumentsObject::getProperty(String *name, PropertyAttributes *attributes ExecutionEngine::ExecutionEngine() { - rootContext = new VM::Context; + rootContext = newContext(); rootContext->init(this); // // set up the global object // - VM::Object *glo = new VM::ArgumentsObject(rootContext); + VM::Object *glo = newArgumentsObject(rootContext); __qmljs_init_object(rootContext, &globalObject, glo); __qmljs_init_object(rootContext, &rootContext->activation, glo); @@ -200,13 +205,98 @@ ExecutionEngine::ExecutionEngine() glo->put(VM::String::get(rootContext, QLatin1String("Object")), objectCtor); glo->put(VM::String::get(rootContext, QLatin1String("String")), stringCtor); glo->put(VM::String::get(rootContext, QLatin1String("Number")), numberCtor); - glo->put(VM::String::get(rootContext, QLatin1String("Math")), Value::object(rootContext, new MathObject(rootContext))); + glo->put(VM::String::get(rootContext, QLatin1String("Math")), Value::object(rootContext, newMathObject(rootContext))); +} + +Context *ExecutionEngine::newContext() +{ + return new Context(); } String *ExecutionEngine::identifier(const QString &s) { String *&id = identifiers[s]; if (! id) - id = new String(s); + id = newString(s); return id; } + +FunctionObject *ExecutionEngine::newNativeFunction(Context *scope, void (*code)(Context *)) +{ + return new NativeFunction(scope, code); +} + +FunctionObject *ExecutionEngine::newScriptFunction(Context *scope, IR::Function *function) +{ + return new ScriptFunction(scope, function); +} + +Object *ExecutionEngine::newObject() +{ + return new Object(); +} + +FunctionObject *ExecutionEngine::newObjectCtor(Context *ctx) +{ + return new ObjectCtor(ctx); +} + +Object *ExecutionEngine::newObjectPrototype(Context *ctx, FunctionObject *proto) +{ + return new ObjectPrototype(ctx, proto); +} + +String *ExecutionEngine::newString(const QString &s) +{ + return new String(s); +} + +Object *ExecutionEngine::newStringObject(const Value &value) +{ + return new StringObject(value); +} + +FunctionObject *ExecutionEngine::newStringCtor(Context *ctx) +{ + return new StringCtor(ctx); +} + +Object *ExecutionEngine::newStringPrototype(Context *ctx, FunctionObject *proto) +{ + return new StringPrototype(ctx, proto); +} + +Object *ExecutionEngine::newNumberObject(const Value &value) +{ + return new NumberObject(value); +} + +FunctionObject *ExecutionEngine::newNumberCtor(Context *ctx) +{ + return new NumberCtor(ctx); +} + +Object *ExecutionEngine::newNumberPrototype(Context *ctx, FunctionObject *proto) +{ + return new NumberPrototype(ctx, proto); +} + +Object *ExecutionEngine::newBooleanObject(const Value &value) +{ + return new BooleanObject(value); +} + +Object *ExecutionEngine::newErrorObject(const Value &value) +{ + return new ErrorObject(value); +} + +Object *ExecutionEngine::newMathObject(Context *ctx) +{ + return new MathObject(ctx); +} + +Object *ExecutionEngine::newArgumentsObject(Context *ctx) +{ + return new ArgumentsObject(ctx); +} diff --git a/qmljs_objects.h b/qmljs_objects.h index 6c644918c1..26987a257a 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -41,10 +41,7 @@ struct String { return _hashValue; } - static String *get(Context *ctx, const QString &s) { - Q_UNUSED(ctx); - return new String(s); - } + static String *get(Context *ctx, const QString &s); private: QString _text; @@ -277,8 +274,8 @@ struct ScriptFunction: FunctionObject { }; struct ErrorObject: Object { - String *message; - ErrorObject(String *message): message(message) {} + Value message; + ErrorObject(const Value &message): message(message) {} }; struct ArgumentsObject: Object { @@ -354,7 +351,30 @@ struct ExecutionEngine ExecutionEngine(); + Context *newContext(); + String *identifier(const QString &s); + + FunctionObject *newNativeFunction(Context *scope, void (*code)(Context *)); + FunctionObject *newScriptFunction(Context *scope, IR::Function *function); + + Object *newObject(); + FunctionObject *newObjectCtor(Context *ctx); + Object *newObjectPrototype(Context *ctx, FunctionObject *proto); + + String *newString(const QString &s); + Object *newStringObject(const Value &value); + FunctionObject *newStringCtor(Context *ctx); + Object *newStringPrototype(Context *ctx, FunctionObject *proto); + + Object *newNumberObject(const Value &value); + FunctionObject *newNumberCtor(Context *ctx); + Object *newNumberPrototype(Context *ctx, FunctionObject *proto); + + Object *newBooleanObject(const Value &value); + Object *newErrorObject(const Value &value); + Object *newMathObject(Context *ctx); + Object *newArgumentsObject(Context *ctx); }; } // namespace VM diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index a93cab93ef..4f7418f2e2 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -74,7 +74,7 @@ extern "C" { void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos) { - __qmljs_init_object(ctx, result, new ScriptFunction(ctx, clos)); + __qmljs_init_object(ctx, result, ctx->engine->newScriptFunction(ctx, clos)); } void __qmljs_string_literal_undefined(Context *ctx, Value *result) @@ -203,21 +203,21 @@ void __qmljs_object_default_value(Context *ctx, Value *result, Object *object, i void __qmljs_throw_type_error(Context *ctx, Value *result) { - __qmljs_init_object(ctx, result, new ErrorObject(String::get(ctx, QLatin1String("type error")))); + __qmljs_init_object(ctx, result, ctx->engine->newErrorObject(Value::string(ctx, QLatin1String("type error")))); } void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean) { Value value; __qmljs_init_boolean(ctx, &value, boolean); - __qmljs_init_object(ctx, result, new BooleanObject(value)); + __qmljs_init_object(ctx, result, ctx->engine->newBooleanObject(value)); } void __qmljs_new_number_object(Context *ctx, Value *result, double number) { Value value; __qmljs_init_number(ctx, &value, number); - __qmljs_init_object(ctx, result, new NumberObject(value)); + __qmljs_init_object(ctx, result, ctx->engine->newNumberObject(value)); result->objectValue->prototype = ctx->engine->numberPrototype.objectValue; } @@ -225,7 +225,7 @@ void __qmljs_new_string_object(Context *ctx, Value *result, String *string) { Value value; __qmljs_init_string(ctx, &value, string); - __qmljs_init_object(ctx, result, new StringObject(value)); + __qmljs_init_object(ctx, result, ctx->engine->newStringObject(value)); result->objectValue->prototype = ctx->engine->stringPrototype.objectValue; } @@ -453,11 +453,11 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S if (func.type == OBJECT_TYPE) { if (FunctionObject *f = func.objectValue->asFunctionObject()) { Context k; - Context *ctx = f->needsActivation ? new Context : &k; + Context *ctx = f->needsActivation ? context->engine->newContext() : &k; ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) - __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); + __qmljs_init_object(ctx, &ctx->activation, ctx->engine->newArgumentsObject(ctx)); else __qmljs_init_null(ctx, &ctx->activation); ctx->thisObject = thisObject; @@ -487,11 +487,11 @@ void __qmljs_call_value(Context *context, Value *result, const Value *func, Valu if (func->type == OBJECT_TYPE) { if (FunctionObject *f = func->objectValue->asFunctionObject()) { Context k; - Context *ctx = f->needsActivation ? new Context : &k; + Context *ctx = f->needsActivation ? context->engine->newContext() : &k; ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) - __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); + __qmljs_init_object(ctx, &ctx->activation, ctx->engine->newArgumentsObject(ctx)); else __qmljs_init_null(ctx, &ctx->activation); __qmljs_init_null(ctx, &ctx->thisObject); @@ -529,11 +529,11 @@ void __qmljs_construct_value(Context *context, Value *result, const Value *func, if (func->type == OBJECT_TYPE) { if (FunctionObject *f = func->objectValue->asFunctionObject()) { Context k; - Context *ctx = f->needsActivation ? new Context : &k; + Context *ctx = f->needsActivation ? context->engine->newContext() : &k; ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) - __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); + __qmljs_init_object(ctx, &ctx->activation, ctx->engine->newArgumentsObject(ctx)); else __qmljs_init_null(ctx, &ctx->activation); __qmljs_init_null(ctx, &ctx->thisObject); @@ -577,11 +577,11 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba if (func.type == OBJECT_TYPE) { if (FunctionObject *f = func.objectValue->asFunctionObject()) { Context k; - Context *ctx = f->needsActivation ? new Context : &k; + Context *ctx = f->needsActivation ? context->engine->newContext() : &k; ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) - __qmljs_init_object(ctx, &ctx->activation, new ArgumentsObject(ctx)); + __qmljs_init_object(ctx, &ctx->activation, ctx->engine->newArgumentsObject(ctx)); else __qmljs_init_null(ctx, &ctx->activation); ctx->thisObject = thisObject; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 1e00940fe8..33e444a55c 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -14,8 +14,8 @@ static const double qt_PI = 2.0 * ::asin(1.0); Value ObjectCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; - ObjectCtor *ctor = new ObjectCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::object(ctx, new ObjectPrototype(ctx, ctor))); + FunctionObject *ctor = ctx->engine->newObjectCtor(ctx); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::object(ctx, ctx->engine->newObjectPrototype(ctx, ctor))); return Value::object(ctx, ctor); } @@ -26,7 +26,7 @@ ObjectCtor::ObjectCtor(Context *scope) void ObjectCtor::construct(Context *ctx) { - __qmljs_init_object(ctx, &ctx->thisObject, new Object()); + __qmljs_init_object(ctx, &ctx->thisObject, ctx->engine->newObject()); } void ObjectCtor::call(Context *) @@ -45,8 +45,8 @@ ObjectPrototype::ObjectPrototype(Context *ctx, FunctionObject *ctor) Value StringCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; - StringCtor *ctor = new StringCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::object(ctx, new StringPrototype(ctx, ctor))); + FunctionObject *ctor = ctx->engine->newStringCtor(ctx); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::object(ctx, ctx->engine->newStringPrototype(ctx, ctor))); return Value::object(ctx, ctor); } @@ -62,7 +62,7 @@ void StringCtor::construct(Context *ctx) value = Value::string(ctx, ctx->argument(0).toString(ctx)); else value = Value::string(ctx, QString()); - __qmljs_init_object(ctx, &ctx->thisObject, new StringObject(value)); + __qmljs_init_object(ctx, &ctx->thisObject, ctx->engine->newStringObject(value)); } void StringCtor::call(Context *ctx) @@ -358,8 +358,8 @@ void StringPrototype::method_fromCharCode(Context *ctx) Value NumberCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; - NumberCtor *ctor = new NumberCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::object(ctx, new NumberPrototype(ctx, ctor))); + FunctionObject *ctor = ctx->engine->newNumberCtor(ctx); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::object(ctx, ctx->engine->newNumberPrototype(ctx, ctor))); return Value::object(ctx, ctor); } @@ -371,7 +371,7 @@ NumberCtor::NumberCtor(Context *scope) void NumberCtor::construct(Context *ctx) { const double n = ctx->argument(0).toNumber(ctx); - __qmljs_init_object(ctx, &ctx->thisObject, new NumberObject(Value::number(ctx, n))); + __qmljs_init_object(ctx, &ctx->thisObject, ctx->engine->newNumberObject(Value::number(ctx, n))); } void NumberCtor::call(Context *ctx) diff --git a/qv4isel.cpp b/qv4isel.cpp index 6e60cc9f78..573c3d5d9c 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -400,7 +400,7 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, new String(*str->value)); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, _engine->newString(*str->value)); amd64_call_code(_codePtr, __qmljs_set_activation_property_string); return; } else if (IR::Temp *t = s->source->asTemp()) { @@ -474,7 +474,7 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, new String(*str->value)); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, _engine->newString(*str->value)); amd64_call_code(_codePtr, __qmljs_init_string); return; } else if (IR::Closure *clos = s->source->asClosure()) { -- cgit v1.2.3 From 6ffb058b18341684d78d9fd49ddbba8904bb761f Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 15 May 2012 10:53:20 +0200 Subject: Cleanup function prototypes. --- main.cpp | 4 +- qmljs_objects.cpp | 26 ++++---- qmljs_objects.h | 2 +- qmljs_runtime.cpp | 92 ++++++++++++++-------------- qmljs_runtime.h | 96 ++++++++++++++--------------- qv4ecmaobjects.cpp | 174 ++++++++++++++++++++++++++--------------------------- qv4isel.cpp | 5 +- 7 files changed, 199 insertions(+), 200 deletions(-) diff --git a/main.cpp b/main.cpp index e2feac9c6c..dec537a9de 100644 --- a/main.cpp +++ b/main.cpp @@ -89,12 +89,12 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, QQmlJS::Engine *engine, const QSt VM::Context *ctx = vm->rootContext; globalObject->put(vm->identifier(QLatin1String("print")), - VM::Value::object(ctx, new builtins::Print(ctx))); + VM::Value::fromObject(new builtins::Print(ctx))); foreach (IR::Function *function, module.functions) { if (function->name && ! function->name->isEmpty()) { globalObject->put(vm->identifier(*function->name), - VM::Value::object(ctx, ctx->engine->newScriptFunction(ctx, function))); + VM::Value::fromObject(ctx->engine->newScriptFunction(ctx, function))); } } codeByName.value(QLatin1String("%entry"))->code(ctx); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index fd9647e808..2e77b4d382 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -25,7 +25,7 @@ void Object::setProperty(Context *ctx, const QString &name, const Value &value) void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context *), int count) { Q_UNUSED(count); - setProperty(ctx, name, Value::object(ctx, ctx->engine->newNativeFunction(ctx, code))); + setProperty(ctx, name, Value::fromObject(ctx->engine->newNativeFunction(ctx, code))); } bool Object::get(String *name, Value *result) @@ -35,7 +35,7 @@ bool Object::get(String *name, Value *result) return true; } - __qmljs_init_undefined(0, result); + __qmljs_init_undefined(result); return false; } @@ -107,11 +107,11 @@ void Object::defaultValue(Context *ctx, Value *result, int typeHint) { if (typeHint == STRING_HINT) { if (asFunctionObject() != 0) - __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("function"))); + __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("function"))); else - __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("object"))); + __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("object"))); } else { - __qmljs_init_undefined(ctx, result); + __qmljs_init_undefined(result); } } @@ -129,7 +129,7 @@ void FunctionObject::call(Context *ctx) void FunctionObject::construct(Context *ctx) { - __qmljs_init_object(ctx, &ctx->thisObject, ctx->engine->newObject()); + __qmljs_init_object(&ctx->thisObject, ctx->engine->newObject()); call(ctx); } @@ -159,7 +159,7 @@ void ScriptFunction::call(VM::Context *ctx) void ScriptFunction::construct(VM::Context *ctx) { - __qmljs_init_object(ctx, &ctx->thisObject, ctx->engine->newObject()); + __qmljs_init_object(&ctx->thisObject, ctx->engine->newObject()); function->code(ctx); } @@ -189,8 +189,8 @@ ExecutionEngine::ExecutionEngine() // set up the global object // VM::Object *glo = newArgumentsObject(rootContext); - __qmljs_init_object(rootContext, &globalObject, glo); - __qmljs_init_object(rootContext, &rootContext->activation, glo); + __qmljs_init_object(&globalObject, glo); + __qmljs_init_object(&rootContext->activation, glo); objectCtor = ObjectCtor::create(this); stringCtor = StringCtor::create(this); @@ -202,10 +202,10 @@ ExecutionEngine::ExecutionEngine() stringCtor.objectValue->get(prototype, &stringPrototype); numberCtor.objectValue->get(prototype, &numberPrototype); - glo->put(VM::String::get(rootContext, QLatin1String("Object")), objectCtor); - glo->put(VM::String::get(rootContext, QLatin1String("String")), stringCtor); - glo->put(VM::String::get(rootContext, QLatin1String("Number")), numberCtor); - glo->put(VM::String::get(rootContext, QLatin1String("Math")), Value::object(rootContext, newMathObject(rootContext))); + glo->put(identifier(QLatin1String("Object")), objectCtor); + glo->put(identifier(QLatin1String("String")), stringCtor); + glo->put(identifier(QLatin1String("Number")), numberCtor); + glo->put(identifier(QLatin1String("Math")), Value::fromObject(newMathObject(rootContext))); } Context *ExecutionEngine::newContext() diff --git a/qmljs_objects.h b/qmljs_objects.h index 26987a257a..9326e9a0a4 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -316,7 +316,7 @@ struct Context { if (index < argumentCount) *result = arguments[index]; else - __qmljs_init_undefined(this, result); + __qmljs_init_undefined(result); } void init(ExecutionEngine *eng) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 4f7418f2e2..8054a60317 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -9,9 +9,9 @@ namespace QQmlJS { namespace VM { -Value Value::string(Context *ctx, const QString &s) +Value Value::fromString(Context *ctx, const QString &s) { - return string(ctx, String::get(ctx, s)); + return fromString(ctx->engine->newString(s)); } int Value::toInt32(double number) @@ -74,52 +74,52 @@ extern "C" { void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos) { - __qmljs_init_object(ctx, result, ctx->engine->newScriptFunction(ctx, clos)); + __qmljs_init_object(result, ctx->engine->newScriptFunction(ctx, clos)); } void __qmljs_string_literal_undefined(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("undefined"))); + __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("undefined"))); } void __qmljs_string_literal_null(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("null"))); + __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("null"))); } void __qmljs_string_literal_true(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("true"))); + __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("true"))); } void __qmljs_string_literal_false(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("false"))); + __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("false"))); } void __qmljs_string_literal_object(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("object"))); + __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("object"))); } void __qmljs_string_literal_boolean(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("boolean"))); + __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("boolean"))); } void __qmljs_string_literal_number(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("number"))); + __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("number"))); } void __qmljs_string_literal_string(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("string"))); + __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("string"))); } void __qmljs_string_literal_function(Context *ctx, Value *result) { - __qmljs_init_string(ctx, result, ctx->engine->identifier(QLatin1String("function"))); + __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("function"))); } void __qmljs_delete(Context *ctx, Value *result, const Value *value) @@ -136,7 +136,7 @@ void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Va if (right->type == OBJECT_TYPE) { if (FunctionObject *function = right->objectValue->asFunctionObject()) { bool r = function->hasInstance(*left); - __qmljs_init_boolean(ctx, result, r); + __qmljs_init_boolean(result, r); return; } } @@ -150,7 +150,7 @@ void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *rig Value s; __qmljs_to_string(ctx, &s, left); bool r = right->objectValue->hasProperty(s.stringValue); - __qmljs_init_boolean(ctx, result, r); + __qmljs_init_boolean(result, r); } else { __qmljs_throw_type_error(ctx, result); } @@ -170,7 +170,7 @@ double __qmljs_string_to_number(Context *, String *string) void __qmljs_string_from_number(Context *ctx, Value *result, double number) { String *string = String::get(ctx, QString::number(number, 'g', 16)); - __qmljs_init_string(ctx, result, string); + __qmljs_init_string(result, string); } bool __qmljs_string_compare(Context *, String *left, String *right) @@ -203,29 +203,29 @@ void __qmljs_object_default_value(Context *ctx, Value *result, Object *object, i void __qmljs_throw_type_error(Context *ctx, Value *result) { - __qmljs_init_object(ctx, result, ctx->engine->newErrorObject(Value::string(ctx, QLatin1String("type error")))); + __qmljs_init_object(result, ctx->engine->newErrorObject(Value::fromString(ctx, QLatin1String("type error")))); } void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean) { Value value; - __qmljs_init_boolean(ctx, &value, boolean); - __qmljs_init_object(ctx, result, ctx->engine->newBooleanObject(value)); + __qmljs_init_boolean(&value, boolean); + __qmljs_init_object(result, ctx->engine->newBooleanObject(value)); } void __qmljs_new_number_object(Context *ctx, Value *result, double number) { Value value; - __qmljs_init_number(ctx, &value, number); - __qmljs_init_object(ctx, result, ctx->engine->newNumberObject(value)); + __qmljs_init_number(&value, number); + __qmljs_init_object(result, ctx->engine->newNumberObject(value)); result->objectValue->prototype = ctx->engine->numberPrototype.objectValue; } void __qmljs_new_string_object(Context *ctx, Value *result, String *string) { Value value; - __qmljs_init_string(ctx, &value, string); - __qmljs_init_object(ctx, result, ctx->engine->newStringObject(value)); + __qmljs_init_string(&value, string); + __qmljs_init_object(result, ctx->engine->newStringObject(value)); result->objectValue->prototype = ctx->engine->stringPrototype.objectValue; } @@ -238,21 +238,21 @@ void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *valu void __qmljs_set_property_boolean(Context *ctx, Value *object, String *name, bool number) { Value value; - __qmljs_init_boolean(ctx, &value, number); + __qmljs_init_boolean(&value, number); object->objectValue->put(name, value, /*flag*/ 0); } void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double number) { Value value; - __qmljs_init_number(ctx, &value, number); + __qmljs_init_number(&value, number); object->objectValue->put(name, value, /*flag*/ 0); } void __qmljs_set_property_string(Context *ctx, Value *object, String *name, String *s) { Value value; - __qmljs_init_string(ctx, &value, s); + __qmljs_init_string(&value, s); object->objectValue->put(name, value, /*flag*/ 0); } @@ -282,21 +282,21 @@ void __qmljs_copy_activation_property(Context *ctx, String *name, String *other) void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool b) { Value value; - __qmljs_init_boolean(ctx, &value, b); + __qmljs_init_boolean(&value, b); __qmljs_set_activation_property(ctx, name, &value); } void __qmljs_set_activation_property_number(Context *ctx, String *name, double number) { Value value; - __qmljs_init_number(ctx, &value, number); + __qmljs_init_number(&value, number); __qmljs_set_activation_property(ctx, name, &value); } void __qmljs_set_activation_property_string(Context *ctx, String *name, String *string) { Value value; - __qmljs_init_string(ctx, &value, string); + __qmljs_init_string(&value, string); __qmljs_set_activation_property(ctx, name, &value); } @@ -359,14 +359,14 @@ void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y if (px.type == STRING_TYPE && py.type == STRING_TYPE) { bool r = __qmljs_string_compare(ctx, px.stringValue, py.stringValue); - __qmljs_init_boolean(ctx, result, r); + __qmljs_init_boolean(result, r); } else { double nx = __qmljs_to_number(ctx, &px); double ny = __qmljs_to_number(ctx, &py); if (isnan(nx) || isnan(ny)) { - __qmljs_init_undefined(ctx, result); + __qmljs_init_undefined(result); } else { - __qmljs_init_boolean(ctx, result, nx < ny); + __qmljs_init_boolean(result, nx < ny); } } } @@ -397,19 +397,19 @@ bool __qmljs_equal(Context *ctx, const Value *x, const Value *y) return true; } else if (x->type == NUMBER_TYPE && y->type == STRING_TYPE) { Value ny; - __qmljs_init_number(ctx, &ny, __qmljs_to_number(ctx, y)); + __qmljs_init_number(&ny, __qmljs_to_number(ctx, y)); return __qmljs_equal(ctx, x, &ny); } else if (x->type == STRING_TYPE && y->type == NUMBER_TYPE) { Value nx; - __qmljs_init_number(ctx, &nx, __qmljs_to_number(ctx, x)); + __qmljs_init_number(&nx, __qmljs_to_number(ctx, x)); return __qmljs_equal(ctx, &nx, y); } else if (x->type == BOOLEAN_TYPE) { Value nx; - __qmljs_init_number(ctx, &nx, (double) x->booleanValue); + __qmljs_init_number(&nx, (double) x->booleanValue); return __qmljs_equal(ctx, &nx, y); } else if (y->type == BOOLEAN_TYPE) { Value ny; - __qmljs_init_number(ctx, &ny, (double) y->booleanValue); + __qmljs_init_number(&ny, (double) y->booleanValue); return __qmljs_equal(ctx, x, &ny); } else if ((x->type == NUMBER_TYPE || x->type == STRING_TYPE) && y->type == OBJECT_TYPE) { Value py; @@ -446,7 +446,7 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S thisObject = baseObject; } else { baseObject = context->activation; - __qmljs_init_null(context, &thisObject); + __qmljs_init_null(&thisObject); } Value func; baseObject.objectValue->get(name, &func); @@ -457,9 +457,9 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) - __qmljs_init_object(ctx, &ctx->activation, ctx->engine->newArgumentsObject(ctx)); + __qmljs_init_object(&ctx->activation, ctx->engine->newArgumentsObject(ctx)); else - __qmljs_init_null(ctx, &ctx->activation); + __qmljs_init_null(&ctx->activation); ctx->thisObject = thisObject; ctx->formals = f->formalParameterList; ctx->formalCount = f->formalParameterCount; @@ -491,10 +491,10 @@ void __qmljs_call_value(Context *context, Value *result, const Value *func, Valu ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) - __qmljs_init_object(ctx, &ctx->activation, ctx->engine->newArgumentsObject(ctx)); + __qmljs_init_object(&ctx->activation, ctx->engine->newArgumentsObject(ctx)); else - __qmljs_init_null(ctx, &ctx->activation); - __qmljs_init_null(ctx, &ctx->thisObject); + __qmljs_init_null(&ctx->activation); + __qmljs_init_null(&ctx->thisObject); ctx->formals = f->formalParameterList; ctx->formalCount = f->formalParameterCount; ctx->arguments = args; @@ -533,10 +533,10 @@ void __qmljs_construct_value(Context *context, Value *result, const Value *func, ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) - __qmljs_init_object(ctx, &ctx->activation, ctx->engine->newArgumentsObject(ctx)); + __qmljs_init_object(&ctx->activation, ctx->engine->newArgumentsObject(ctx)); else - __qmljs_init_null(ctx, &ctx->activation); - __qmljs_init_null(ctx, &ctx->thisObject); + __qmljs_init_null(&ctx->activation); + __qmljs_init_null(&ctx->thisObject); ctx->formals = f->formalParameterList; ctx->formalCount = f->formalParameterCount; ctx->arguments = args; @@ -581,9 +581,9 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba ctx->init(context->engine); ctx->parent = f->scope; if (f->needsActivation) - __qmljs_init_object(ctx, &ctx->activation, ctx->engine->newArgumentsObject(ctx)); + __qmljs_init_object(&ctx->activation, ctx->engine->newArgumentsObject(ctx)); else - __qmljs_init_null(ctx, &ctx->activation); + __qmljs_init_null(&ctx->activation); ctx->thisObject = thisObject; ctx->formals = f->formalParameterList; ctx->formalCount = f->formalParameterCount; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 2a0f42d1dd..b2b998737c 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -53,13 +53,13 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba void __qmljs_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc); // constructors -void __qmljs_init_undefined(Context *ctx, Value *result); -void __qmljs_init_null(Context *ctx, Value *result); -void __qmljs_init_boolean(Context *ctx, Value *result, bool value); -void __qmljs_init_number(Context *ctx, Value *result, double number); -void __qmljs_init_string(Context *ctx, Value *result, String *string); -void __qmljs_init_object(Context *ctx, Value *result, Object *object); -void __qmljs_init_closure(Context *, Value *result, IR::Function *clos); +void __qmljs_init_undefined(Value *result); +void __qmljs_init_null(Value *result); +void __qmljs_init_boolean(Value *result, bool value); +void __qmljs_init_number(Value *result, double number); +void __qmljs_init_string(Value *result, String *string); +void __qmljs_init_object(Value *result, Object *object); +void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos); bool __qmljs_is_function(Context *ctx, const Value *value); @@ -174,33 +174,33 @@ struct Value { inline bool is(ValueType t) const { return type == t; } inline bool isNot(ValueType t) const { return type != t; } - static inline Value boolean(Context *ctx, bool value) { + static inline Value fromBoolean(bool value) { Value v; - __qmljs_init_boolean(ctx, &v, value); + __qmljs_init_boolean(&v, value); return v; } - static inline Value number(Context *ctx, double value) { + static inline Value fromNumber(double value) { Value v; - __qmljs_init_number(ctx, &v, value); + __qmljs_init_number(&v, value); return v; } - static inline Value object(Context *ctx, Object *value) { + static inline Value fromObject(Object *value) { Value v; - __qmljs_init_object(ctx, &v, value); + __qmljs_init_object(&v, value); return v; } - static inline Value string(Context *ctx, String *value) { + static inline Value fromString(String *value) { Value v; - __qmljs_init_string(ctx, &v, value); + __qmljs_init_string(&v, value); return v; } - static Value string(Context *ctx, const QString &string); + static Value fromString(Context *ctx, const QString &fromString); - static int toInteger(double number); + static int toInteger(double fromNumber); static int toInt32(double value); int toUInt16(Context *ctx); int toInt32(Context *ctx); @@ -221,35 +221,35 @@ struct Value { extern "C" { // constructors -inline void __qmljs_init_undefined(Context *, Value *result) +inline void __qmljs_init_undefined(Value *result) { result->type = UNDEFINED_TYPE; } -inline void __qmljs_init_null(Context *, Value *result) +inline void __qmljs_init_null(Value *result) { result->type = NULL_TYPE; } -inline void __qmljs_init_boolean(Context *, Value *result, bool value) +inline void __qmljs_init_boolean(Value *result, bool value) { result->type = BOOLEAN_TYPE; result->booleanValue = value; } -inline void __qmljs_init_number(Context *, Value *result, double value) +inline void __qmljs_init_number(Value *result, double value) { result->type = NUMBER_TYPE; result->numberValue = value; } -inline void __qmljs_init_string(Context *, Value *result, String *value) +inline void __qmljs_init_string(Value *result, String *value) { result->type = STRING_TYPE; result->stringValue = value; } -inline void __qmljs_init_object(Context *, Value *result, Object *object) +inline void __qmljs_init_object(Value *result, Object *object) { result->type = OBJECT_TYPE; result->objectValue = object; @@ -437,7 +437,7 @@ inline void __qmljs_default_value(Context *ctx, Value *result, const Value *valu if (value->type == OBJECT_TYPE) __qmljs_object_default_value(ctx, result, value->objectValue, typeHint); else - __qmljs_init_undefined(ctx, result); + __qmljs_init_undefined(result); } @@ -472,25 +472,25 @@ inline void __qmljs_typeof(Context *ctx, Value *result, const Value *value) inline void __qmljs_uplus(Context *ctx, Value *result, const Value *value) { double n = __qmljs_to_number(ctx, value); - __qmljs_init_number(ctx, result, n); + __qmljs_init_number(result, n); } inline void __qmljs_uminus(Context *ctx, Value *result, const Value *value) { double n = __qmljs_to_number(ctx, value); - __qmljs_init_number(ctx, result, -n); + __qmljs_init_number(result, -n); } inline void __qmljs_compl(Context *ctx, Value *result, const Value *value) { int n = __qmljs_to_int32(ctx, value); - __qmljs_init_number(ctx, result, ~n); + __qmljs_init_number(result, ~n); } inline void __qmljs_not(Context *ctx, Value *result, const Value *value) { bool b = __qmljs_to_boolean(ctx, value); - __qmljs_init_number(ctx, result, !b); + __qmljs_init_number(result, !b); } // binary operators @@ -498,21 +498,21 @@ inline void __qmljs_bit_or(Context *ctx, Value *result, const Value *left, const { int lval = __qmljs_to_int32(ctx, left); int rval = __qmljs_to_int32(ctx, right); - __qmljs_init_number(ctx, result, lval | rval); + __qmljs_init_number(result, lval | rval); } inline void __qmljs_bit_xor(Context *ctx, Value *result, const Value *left, const Value *right) { int lval = __qmljs_to_int32(ctx, left); int rval = __qmljs_to_int32(ctx, right); - __qmljs_init_number(ctx, result, lval ^ rval); + __qmljs_init_number(result, lval ^ rval); } inline void __qmljs_bit_and(Context *ctx, Value *result, const Value *left, const Value *right) { int lval = __qmljs_to_int32(ctx, left); int rval = __qmljs_to_int32(ctx, right); - __qmljs_init_number(ctx, result, lval & rval); + __qmljs_init_number(result, lval & rval); } inline void __qmljs_add(Context *ctx, Value *result, const Value *left, const Value *right) @@ -526,11 +526,11 @@ inline void __qmljs_add(Context *ctx, Value *result, const Value *left, const Va if (pright.type != STRING_TYPE) __qmljs_to_string(ctx, &pright, &pright); String *string = __qmljs_string_concat(ctx, pleft.stringValue, pright.stringValue); - __qmljs_init_string(ctx, result, string); + __qmljs_init_string(result, string); } else { double x = __qmljs_to_number(ctx, &pleft); double y = __qmljs_to_number(ctx, &pright); - __qmljs_init_number(ctx, result, x + y); + __qmljs_init_number(result, x + y); } } @@ -538,49 +538,49 @@ inline void __qmljs_sub(Context *ctx, Value *result, const Value *left, const Va { double lval = __qmljs_to_number(ctx, left); double rval = __qmljs_to_number(ctx, right); - __qmljs_init_number(ctx, result, lval - rval); + __qmljs_init_number(result, lval - rval); } inline void __qmljs_mul(Context *ctx, Value *result, const Value *left, const Value *right) { double lval = __qmljs_to_number(ctx, left); double rval = __qmljs_to_number(ctx, right); - __qmljs_init_number(ctx, result, lval * rval); + __qmljs_init_number(result, lval * rval); } inline void __qmljs_div(Context *ctx, Value *result, const Value *left, const Value *right) { double lval = __qmljs_to_number(ctx, left); double rval = __qmljs_to_number(ctx, right); - __qmljs_init_number(ctx, result, lval * rval); + __qmljs_init_number(result, lval * rval); } inline void __qmljs_mod(Context *ctx, Value *result, const Value *left, const Value *right) { double lval = __qmljs_to_number(ctx, left); double rval = __qmljs_to_number(ctx, right); - __qmljs_init_number(ctx, result, fmod(lval, rval)); + __qmljs_init_number(result, fmod(lval, rval)); } inline void __qmljs_shl(Context *ctx, Value *result, const Value *left, const Value *right) { int lval = __qmljs_to_int32(ctx, left); unsigned rval = __qmljs_to_uint32(ctx, right); - __qmljs_init_number(ctx, result, lval << rval); + __qmljs_init_number(result, lval << rval); } inline void __qmljs_shr(Context *ctx, Value *result, const Value *left, const Value *right) { int lval = __qmljs_to_int32(ctx, left); unsigned rval = __qmljs_to_uint32(ctx, right); - __qmljs_init_number(ctx, result, lval >> rval); + __qmljs_init_number(result, lval >> rval); } inline void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const Value *right) { unsigned lval = __qmljs_to_uint32(ctx, left); unsigned rval = __qmljs_to_uint32(ctx, right); - __qmljs_init_number(ctx, result, lval << rval); + __qmljs_init_number(result, lval << rval); } inline void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Value *right) @@ -588,7 +588,7 @@ inline void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Val __qmljs_compare(ctx, result, left, right, false); if (result->type == UNDEFINED_TYPE) - __qmljs_init_boolean(ctx, result, false); + __qmljs_init_boolean(result, false); } inline void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Value *right) @@ -596,7 +596,7 @@ inline void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Val __qmljs_compare(ctx, result, left, right, true); if (result->type == UNDEFINED_TYPE) - __qmljs_init_boolean(ctx, result,false); + __qmljs_init_boolean(result, false); } inline void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Value *right) @@ -606,7 +606,7 @@ inline void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Val bool r = ! (result->type == UNDEFINED_TYPE || (result->type == BOOLEAN_TYPE && result->booleanValue == true)); - __qmljs_init_boolean(ctx, result, r); + __qmljs_init_boolean(result, r); } inline void __qmljs_le(Context *ctx, Value *result, const Value *left, const Value *right) @@ -616,31 +616,31 @@ inline void __qmljs_le(Context *ctx, Value *result, const Value *left, const Val bool r = ! (result->type == UNDEFINED_TYPE || (result->type == BOOLEAN_TYPE && result->booleanValue == true)); - __qmljs_init_boolean(ctx, result, r); + __qmljs_init_boolean(result, r); } inline void __qmljs_eq(Context *ctx, Value *result, const Value *left, const Value *right) { bool r = __qmljs_equal(ctx, left, right); - __qmljs_init_boolean(ctx, result, r); + __qmljs_init_boolean(result, r); } inline void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *right) { bool r = ! __qmljs_equal(ctx, left, right); - __qmljs_init_boolean(ctx, result, r); + __qmljs_init_boolean(result, r); } inline void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right) { bool r = __qmljs_strict_equal(ctx, left, right); - __qmljs_init_boolean(ctx, result, r); + __qmljs_init_boolean(result, r); } inline void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *right) { bool r = ! __qmljs_strict_equal(ctx, left, right); - __qmljs_init_boolean(ctx, result, r); + __qmljs_init_boolean(result, r); } inline bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 33e444a55c..74d1484f88 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -15,8 +15,8 @@ Value ObjectCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; FunctionObject *ctor = ctx->engine->newObjectCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::object(ctx, ctx->engine->newObjectPrototype(ctx, ctor))); - return Value::object(ctx, ctor); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newObjectPrototype(ctx, ctor))); + return Value::fromObject(ctor); } ObjectCtor::ObjectCtor(Context *scope) @@ -26,7 +26,7 @@ ObjectCtor::ObjectCtor(Context *scope) void ObjectCtor::construct(Context *ctx) { - __qmljs_init_object(ctx, &ctx->thisObject, ctx->engine->newObject()); + __qmljs_init_object(&ctx->thisObject, ctx->engine->newObject()); } void ObjectCtor::call(Context *) @@ -36,7 +36,7 @@ void ObjectCtor::call(Context *) ObjectPrototype::ObjectPrototype(Context *ctx, FunctionObject *ctor) { - setProperty(ctx, QLatin1String("constructor"), Value::object(ctx, ctor)); + setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); } // @@ -46,8 +46,8 @@ Value StringCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; FunctionObject *ctor = ctx->engine->newStringCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::object(ctx, ctx->engine->newStringPrototype(ctx, ctor))); - return Value::object(ctx, ctor); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newStringPrototype(ctx, ctor))); + return Value::fromObject(ctor); } StringCtor::StringCtor(Context *scope) @@ -59,24 +59,24 @@ void StringCtor::construct(Context *ctx) { Value value; if (ctx->argumentCount) - value = Value::string(ctx, ctx->argument(0).toString(ctx)); + value = Value::fromString(ctx->argument(0).toString(ctx)); else - value = Value::string(ctx, QString()); - __qmljs_init_object(ctx, &ctx->thisObject, ctx->engine->newStringObject(value)); + value = Value::fromString(ctx, QString()); + __qmljs_init_object(&ctx->thisObject, ctx->engine->newStringObject(value)); } void StringCtor::call(Context *ctx) { const Value arg = ctx->argument(0); if (arg.is(UNDEFINED_TYPE)) - __qmljs_init_string(ctx, &ctx->result, String::get(ctx, QString())); + __qmljs_init_string(&ctx->result, String::get(ctx, QString())); else __qmljs_to_string(ctx, &ctx->result, &arg); } StringPrototype::StringPrototype(Context *ctx, FunctionObject *ctor) { - setProperty(ctx, QLatin1String("constructor"), Value::object(ctx, ctor)); + setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); setProperty(ctx, QLatin1String("toString"), method_toString); setProperty(ctx, QLatin1String("valueOf"), method_valueOf); setProperty(ctx, QLatin1String("charAt"), method_charAt); @@ -129,7 +129,7 @@ void StringPrototype::method_charAt(Context *ctx) if (pos >= 0 && pos < str.length()) result += str.at(pos); - ctx->result = Value::string(ctx, result); + ctx->result = Value::fromString(ctx, result); } void StringPrototype::method_charCodeAt(Context *ctx) @@ -145,7 +145,7 @@ void StringPrototype::method_charCodeAt(Context *ctx) if (pos >= 0 && pos < str.length()) result = str.at(pos).unicode(); - __qmljs_init_number(ctx, &ctx->result, result); + __qmljs_init_number(&ctx->result, result); } void StringPrototype::method_concat(Context *ctx) @@ -159,7 +159,7 @@ void StringPrototype::method_concat(Context *ctx) value += v.stringValue->toQString(); } - ctx->result = Value::string(ctx, value); + ctx->result = Value::fromString(ctx, value); } void StringPrototype::method_indexOf(Context *ctx) @@ -178,7 +178,7 @@ void StringPrototype::method_indexOf(Context *ctx) if (! value.isEmpty()) index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); - __qmljs_init_number(ctx, &ctx->result, index); + __qmljs_init_number(&ctx->result, index); } void StringPrototype::method_lastIndexOf(Context *ctx) @@ -203,14 +203,14 @@ void StringPrototype::method_lastIndexOf(Context *ctx) if (!searchString.isEmpty() && pos == value.length()) --pos; int index = value.lastIndexOf(searchString, pos); - __qmljs_init_number(ctx, &ctx->result, index); + __qmljs_init_number(&ctx->result, index); } void StringPrototype::method_localeCompare(Context *ctx) { const QString value = getThisString(ctx); const QString that = ctx->argument(0).toString(ctx)->toQString(); - __qmljs_init_number(ctx, &ctx->result, QString::localeAwareCompare(value, that)); + __qmljs_init_number(&ctx->result, QString::localeAwareCompare(value, that)); } void StringPrototype::method_match(Context *) @@ -251,7 +251,7 @@ void StringPrototype::method_slice(Context *ctx) end = qMin(end, length); int count = qMax(0, end - start); - ctx->result = Value::string(ctx, text.mid(start, count)); + ctx->result = Value::fromString(ctx, text.mid(start, count)); } void StringPrototype::method_split(Context *) @@ -280,7 +280,7 @@ void StringPrototype::method_substr(Context *ctx) qint32 x = Value::toInt32(start); qint32 y = Value::toInt32(length); - ctx->result = Value::string(ctx, value.mid(x, y)); + ctx->result = Value::fromString(ctx, value.mid(x, y)); } void StringPrototype::method_substring(Context *ctx) @@ -317,13 +317,13 @@ void StringPrototype::method_substring(Context *ctx) qint32 x = Value::toInt32(start); qint32 y = Value::toInt32(end - start); - ctx->result = Value::string(ctx, value.mid(x, y)); + ctx->result = Value::fromString(ctx, value.mid(x, y)); } void StringPrototype::method_toLowerCase(Context *ctx) { QString value = getThisString(ctx); - ctx->result = Value::string(ctx, value.toLower()); + ctx->result = Value::fromString(ctx, value.toLower()); } void StringPrototype::method_toLocaleLowerCase(Context *ctx) @@ -334,7 +334,7 @@ void StringPrototype::method_toLocaleLowerCase(Context *ctx) void StringPrototype::method_toUpperCase(Context *ctx) { QString value = getThisString(ctx); - ctx->result = Value::string(ctx, value.toUpper()); + ctx->result = Value::fromString(ctx, value.toUpper()); } void StringPrototype::method_toLocaleUpperCase(Context *ctx) @@ -349,7 +349,7 @@ void StringPrototype::method_fromCharCode(Context *ctx) QChar c(ctx->argument(i).toUInt16(ctx)); str += c; } - ctx->result = Value::string(ctx, str); + ctx->result = Value::fromString(ctx, str); } // @@ -359,8 +359,8 @@ Value NumberCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; FunctionObject *ctor = ctx->engine->newNumberCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::object(ctx, ctx->engine->newNumberPrototype(ctx, ctor))); - return Value::object(ctx, ctor); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newNumberPrototype(ctx, ctor))); + return Value::fromObject(ctor); } NumberCtor::NumberCtor(Context *scope) @@ -371,31 +371,31 @@ NumberCtor::NumberCtor(Context *scope) void NumberCtor::construct(Context *ctx) { const double n = ctx->argument(0).toNumber(ctx); - __qmljs_init_object(ctx, &ctx->thisObject, ctx->engine->newNumberObject(Value::number(ctx, n))); + __qmljs_init_object(&ctx->thisObject, ctx->engine->newNumberObject(Value::fromNumber(n))); } void NumberCtor::call(Context *ctx) { double value = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; - __qmljs_init_number(ctx, &ctx->result, value); + __qmljs_init_number(&ctx->result, value); } NumberPrototype::NumberPrototype(Context *ctx, FunctionObject *ctor) { - ctor->setProperty(ctx, QLatin1String("NaN"), Value::number(ctx, qSNaN())); - ctor->setProperty(ctx, QLatin1String("NEGATIVE_INFINITY"), Value::number(ctx, -qInf())); - ctor->setProperty(ctx, QLatin1String("POSITIVE_INFINITY"), Value::number(ctx, qInf())); - ctor->setProperty(ctx, QLatin1String("MAX_VALUE"), Value::number(ctx, 1.7976931348623158e+308)); + ctor->setProperty(ctx, QLatin1String("NaN"), Value::fromNumber(qSNaN())); + ctor->setProperty(ctx, QLatin1String("NEGATIVE_INFINITY"), Value::fromNumber(-qInf())); + ctor->setProperty(ctx, QLatin1String("POSITIVE_INFINITY"), Value::fromNumber(qInf())); + ctor->setProperty(ctx, QLatin1String("MAX_VALUE"), Value::fromNumber(1.7976931348623158e+308)); #ifdef __INTEL_COMPILER # pragma warning( push ) # pragma warning(disable: 239) #endif - ctor->setProperty(ctx, QLatin1String("MIN_VALUE"), Value::number(ctx, 5e-324)); + ctor->setProperty(ctx, QLatin1String("MIN_VALUE"), Value::fromNumber(5e-324)); #ifdef __INTEL_COMPILER # pragma warning( pop ) #endif - setProperty(ctx, QLatin1String("constructor"), Value::object(ctx, ctor)); + setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); setProperty(ctx, QLatin1String("toString"), method_toString); setProperty(ctx, QLatin1String("toLocalString"), method_toLocaleString); setProperty(ctx, QLatin1String("valueOf"), method_valueOf); @@ -426,10 +426,10 @@ void NumberPrototype::method_toString(Context *ctx) QString str; double num = internalValue.toNumber(ctx); if (qIsNaN(num)) { - ctx->result = Value::string(ctx, QLatin1String("NaN")); + ctx->result = Value::fromString(ctx, QLatin1String("NaN")); return; } else if (qIsInf(num)) { - ctx->result = Value::string(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); + ctx->result = Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); return; } bool negative = false; @@ -457,7 +457,7 @@ void NumberPrototype::method_toString(Context *ctx) } if (negative) str.prepend(QLatin1Char('-')); - ctx->result = Value::string(ctx, str); + ctx->result = Value::fromString(ctx, str); return; } } @@ -466,7 +466,7 @@ void NumberPrototype::method_toString(Context *ctx) self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); String *str = internalValue.toString(ctx); - ctx->result = Value::string(ctx, str); + ctx->result = Value::fromString(str); } void NumberPrototype::method_toLocaleString(Context *ctx) @@ -480,7 +480,7 @@ void NumberPrototype::method_toLocaleString(Context *ctx) Value internalValue; self.objectValue->defaultValue(ctx, &internalValue, STRING_HINT); String *str = internalValue.toString(ctx); - ctx->result = Value::string(ctx, str); + ctx->result = Value::fromString(str); } void NumberPrototype::method_valueOf(Context *ctx) @@ -523,7 +523,7 @@ void NumberPrototype::method_toFixed(Context *ctx) str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); else str = QString::number(v, 'f', int (fdigits)); - ctx->result = Value::string(ctx, str); + ctx->result = Value::fromString(ctx, str); } void NumberPrototype::method_toExponential(Context *ctx) @@ -544,7 +544,7 @@ void NumberPrototype::method_toExponential(Context *ctx) double v = internalValue.toNumber(ctx); QString z = QString::number(v, 'e', int (fdigits)); - ctx->result = Value::string(ctx, z); + ctx->result = Value::fromString(ctx, z); } void NumberPrototype::method_toPrecision(Context *ctx) @@ -564,7 +564,7 @@ void NumberPrototype::method_toPrecision(Context *ctx) self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); double v = internalValue.toNumber(ctx); - ctx->result = Value::string(ctx, QString::number(v, 'g', int (fdigits))); + ctx->result = Value::fromString(ctx, QString::number(v, 'g', int (fdigits))); } // @@ -572,14 +572,14 @@ void NumberPrototype::method_toPrecision(Context *ctx) // MathObject::MathObject(Context *ctx) { - setProperty(ctx, QLatin1String("E"), Value::number(ctx, ::exp(1.0))); - setProperty(ctx, QLatin1String("LN2"), Value::number(ctx, ::log(2.0))); - setProperty(ctx, QLatin1String("LN10"), Value::number(ctx, ::log(10.0))); - setProperty(ctx, QLatin1String("LOG2E"), Value::number(ctx, 1.0/::log(2.0))); - setProperty(ctx, QLatin1String("LOG10E"), Value::number(ctx, 1.0/::log(10.0))); - setProperty(ctx, QLatin1String("PI"), Value::number(ctx, qt_PI)); - setProperty(ctx, QLatin1String("SQRT1_2"), Value::number(ctx, ::sqrt(0.5))); - setProperty(ctx, QLatin1String("SQRT2"), Value::number(ctx, ::sqrt(2.0))); + setProperty(ctx, QLatin1String("E"), Value::fromNumber(::exp(1.0))); + setProperty(ctx, QLatin1String("LN2"), Value::fromNumber(::log(2.0))); + setProperty(ctx, QLatin1String("LN10"), Value::fromNumber(::log(10.0))); + setProperty(ctx, QLatin1String("LOG2E"), Value::fromNumber(1.0/::log(2.0))); + setProperty(ctx, QLatin1String("LOG10E"), Value::fromNumber(1.0/::log(10.0))); + setProperty(ctx, QLatin1String("PI"), Value::fromNumber(qt_PI)); + setProperty(ctx, QLatin1String("SQRT1_2"), Value::fromNumber(::sqrt(0.5))); + setProperty(ctx, QLatin1String("SQRT2"), Value::fromNumber(::sqrt(2.0))); setProperty(ctx, QLatin1String("abs"), method_abs, 1); setProperty(ctx, QLatin1String("acos"), method_acos, 1); @@ -617,36 +617,36 @@ void MathObject::method_abs(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v == 0) // 0 | -0 - ctx->result = Value::number(ctx, 0); + ctx->result = Value::fromNumber(0); else - ctx->result = Value::number(ctx, v < 0 ? -v : v); + ctx->result = Value::fromNumber(v < 0 ? -v : v); } void MathObject::method_acos(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v > 1) - ctx->result = Value::number(ctx, qSNaN()); + ctx->result = Value::fromNumber(qSNaN()); else - ctx->result = Value::number(ctx, ::acos(v)); + ctx->result = Value::fromNumber(::acos(v)); } void MathObject::method_asin(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v > 1) - ctx->result = Value::number(ctx, qSNaN()); + ctx->result = Value::fromNumber(qSNaN()); else - ctx->result = Value::number(ctx, ::asin(v)); + ctx->result = Value::fromNumber(::asin(v)); } void MathObject::method_atan(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v == 0.0) - ctx->result = Value::number(ctx, v); + ctx->result = Value::fromNumber(v); else - ctx->result = Value::number(ctx, ::atan(v)); + ctx->result = Value::fromNumber(::atan(v)); } void MathObject::method_atan2(Context *ctx) @@ -654,34 +654,34 @@ void MathObject::method_atan2(Context *ctx) double v1 = ctx->argument(0).toNumber(ctx); double v2 = ctx->argument(1).toNumber(ctx); if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) { - ctx->result = Value::number(ctx, copySign(0, -1.0)); + ctx->result = Value::fromNumber(copySign(0, -1.0)); return; } if ((v1 == 0.0) && (v2 == 0.0)) { if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) { - ctx->result = Value::number(ctx, qt_PI); + ctx->result = Value::fromNumber(qt_PI); return; } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) { - ctx->result = Value::number(ctx, -qt_PI); + ctx->result = Value::fromNumber(-qt_PI); return; } } - ctx->result = Value::number(ctx, ::atan2(v1, v2)); + ctx->result = Value::fromNumber(::atan2(v1, v2)); } void MathObject::method_ceil(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v < 0.0 && v > -1.0) - ctx->result = Value::number(ctx, copySign(0, -1.0)); + ctx->result = Value::fromNumber(copySign(0, -1.0)); else - ctx->result = Value::number(ctx, ::ceil(v)); + ctx->result = Value::fromNumber(::ceil(v)); } void MathObject::method_cos(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); - ctx->result = Value::number(ctx, ::cos(v)); + ctx->result = Value::fromNumber(::cos(v)); } void MathObject::method_exp(Context *ctx) @@ -689,27 +689,27 @@ void MathObject::method_exp(Context *ctx) double v = ctx->argument(0).toNumber(ctx); if (qIsInf(v)) { if (copySign(1.0, v) == -1.0) - ctx->result = Value::number(ctx, 0); + ctx->result = Value::fromNumber(0); else - ctx->result = Value::number(ctx, qInf()); + ctx->result = Value::fromNumber(qInf()); } else { - ctx->result = Value::number(ctx, ::exp(v)); + ctx->result = Value::fromNumber(::exp(v)); } } void MathObject::method_floor(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); - ctx->result = Value::number(ctx, ::floor(v)); + ctx->result = Value::fromNumber(::floor(v)); } void MathObject::method_log(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v < 0) - ctx->result = Value::number(ctx, qSNaN()); + ctx->result = Value::fromNumber(qSNaN()); else - ctx->result = Value::number(ctx, ::log(v)); + ctx->result = Value::fromNumber(::log(v)); } void MathObject::method_max(Context *ctx) @@ -720,7 +720,7 @@ void MathObject::method_max(Context *ctx) if (x > mx || qIsNaN(x)) mx = x; } - ctx->result = Value::number(ctx, mx); + ctx->result = Value::fromNumber(mx); } void MathObject::method_min(Context *ctx) @@ -733,7 +733,7 @@ void MathObject::method_min(Context *ctx) mx = x; } } - ctx->result = Value::number(ctx, mx); + ctx->result = Value::fromNumber(mx); } void MathObject::method_pow(Context *ctx) @@ -742,27 +742,27 @@ void MathObject::method_pow(Context *ctx) double y = ctx->argument(1).toNumber(ctx); if (qIsNaN(y)) { - ctx->result = Value::number(ctx, qSNaN()); + ctx->result = Value::fromNumber(qSNaN()); return; } if (y == 0) { - ctx->result = Value::number(ctx, 1); + ctx->result = Value::fromNumber(1); } else if (((x == 1) || (x == -1)) && qIsInf(y)) { - ctx->result = Value::number(ctx, qSNaN()); + ctx->result = Value::fromNumber(qSNaN()); } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { - ctx->result = Value::number(ctx, qInf()); + ctx->result = Value::fromNumber(qInf()); } else if ((x == 0) && copySign(1.0, x) == -1.0) { if (y < 0) { if (::fmod(-y, 2.0) == 1.0) - ctx->result = Value::number(ctx, -qInf()); + ctx->result = Value::fromNumber(-qInf()); else - ctx->result = Value::number(ctx, qInf()); + ctx->result = Value::fromNumber(qInf()); } else if (y > 0) { if (::fmod(y, 2.0) == 1.0) - ctx->result = Value::number(ctx, copySign(0, -1.0)); + ctx->result = Value::fromNumber(copySign(0, -1.0)); else - ctx->result = Value::number(ctx, 0); + ctx->result = Value::fromNumber(0); } } @@ -782,39 +782,39 @@ void MathObject::method_pow(Context *ctx) } #endif else { - ctx->result = Value::number(ctx, ::pow(x, y)); + ctx->result = Value::fromNumber(::pow(x, y)); } } void MathObject::method_random(Context *ctx) { - ctx->result = Value::number(ctx, qrand() / (double) RAND_MAX); + ctx->result = Value::fromNumber(qrand() / (double) RAND_MAX); } void MathObject::method_round(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); v = copySign(::floor(v + 0.5), v); - ctx->result = Value::number(ctx, v); + ctx->result = Value::fromNumber(v); } void MathObject::method_sin(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); - ctx->result = Value::number(ctx, ::sin(v)); + ctx->result = Value::fromNumber(::sin(v)); } void MathObject::method_sqrt(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); - ctx->result = Value::number(ctx, ::sqrt(v)); + ctx->result = Value::fromNumber(::sqrt(v)); } void MathObject::method_tan(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v == 0.0) - ctx->result = Value::number(ctx, v); + ctx->result = Value::fromNumber(v); else - ctx->result = Value::number(ctx, ::tan(v)); + ctx->result = Value::fromNumber(::tan(v)); } diff --git a/qv4isel.cpp b/qv4isel.cpp index 573c3d5d9c..d99bfc2ffc 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -472,9 +472,8 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_membase_reg(_codePtr, AMD64_RDI, offsetof(Value, numberValue), AMD64_RAX, 8); return; } else if (IR::String *str = s->source->asString()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, _engine->newString(*str->value)); + loadTempAddress(AMD64_RDI, t); + amd64_mov_reg_imm(_codePtr, AMD64_RSI, _engine->newString(*str->value)); amd64_call_code(_codePtr, __qmljs_init_string); return; } else if (IR::Closure *clos = s->source->asClosure()) { -- cgit v1.2.3 From a909c6727978e077bfbfa7808d4ead09005bfe0b Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 15 May 2012 10:58:22 +0200 Subject: Remove String::get() --- qmljs_objects.cpp | 7 +------ qmljs_objects.h | 2 -- qmljs_runtime.cpp | 4 ++-- qv4ecmaobjects.cpp | 2 +- qv4isel.cpp | 2 +- 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 2e77b4d382..3439ae7434 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -7,11 +7,6 @@ using namespace QQmlJS::VM; -String *String::get(Context *ctx, const QString &s) -{ - return ctx->engine->newString(s); -} - Object::~Object() { delete members; @@ -196,7 +191,7 @@ ExecutionEngine::ExecutionEngine() stringCtor = StringCtor::create(this); numberCtor = NumberCtor::create(this); - String *prototype = String::get(rootContext, QLatin1String("prototype")); + String *prototype = identifier(QLatin1String("prototype")); objectCtor.objectValue->get(prototype, &objectPrototype); stringCtor.objectValue->get(prototype, &stringPrototype); diff --git a/qmljs_objects.h b/qmljs_objects.h index 9326e9a0a4..ae4efab409 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -41,8 +41,6 @@ struct String { return _hashValue; } - static String *get(Context *ctx, const QString &s); - private: QString _text; mutable unsigned _hashValue; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 8054a60317..2c283599a1 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -169,7 +169,7 @@ double __qmljs_string_to_number(Context *, String *string) void __qmljs_string_from_number(Context *ctx, Value *result, double number) { - String *string = String::get(ctx, QString::number(number, 'g', 16)); + String *string = ctx->engine->newString(QString::number(number, 'g', 16)); __qmljs_init_string(result, string); } @@ -187,7 +187,7 @@ bool __qmljs_string_equal(Context *, String *left, String *right) String *__qmljs_string_concat(Context *ctx, String *first, String *second) { - return String::get(ctx, first->toQString() + second->toQString()); + return ctx->engine->newString(first->toQString() + second->toQString()); } bool __qmljs_is_function(Context *, const Value *value) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 74d1484f88..4e215ef9ed 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -69,7 +69,7 @@ void StringCtor::call(Context *ctx) { const Value arg = ctx->argument(0); if (arg.is(UNDEFINED_TYPE)) - __qmljs_init_string(&ctx->result, String::get(ctx, QString())); + __qmljs_init_string(&ctx->result, ctx->engine->newString(QString())); else __qmljs_to_string(ctx, &ctx->result, &arg); } diff --git a/qv4isel.cpp b/qv4isel.cpp index d99bfc2ffc..3e8d27a976 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -635,7 +635,7 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, base); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, VM::String::get(_engine->rootContext, *str->value)); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, _engine->newString(*str->value)); amd64_call_code(_codePtr, __qmljs_set_property_string); return; } else if (IR::Temp *t = s->source->asTemp()) { -- cgit v1.2.3 From 3dd5bfc0fd7bdcfe48cfdf90900b169c901a39cc Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 15 May 2012 11:19:10 +0200 Subject: Add the Boolean Object. --- qmljs_objects.cpp | 20 +++++++++++++++-- qmljs_objects.h | 3 +++ qmljs_runtime.cpp | 1 + qv4ecmaobjects.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4ecmaobjects_p.h | 19 ++++++++++++++++ 5 files changed, 105 insertions(+), 2 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 3439ae7434..3192ac943c 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -258,7 +258,9 @@ FunctionObject *ExecutionEngine::newStringCtor(Context *ctx) Object *ExecutionEngine::newStringPrototype(Context *ctx, FunctionObject *proto) { - return new StringPrototype(ctx, proto); + Object *stringProto = new StringPrototype(ctx, proto); + stringProto->prototype = objectPrototype.objectValue; + return stringProto; } Object *ExecutionEngine::newNumberObject(const Value &value) @@ -273,7 +275,9 @@ FunctionObject *ExecutionEngine::newNumberCtor(Context *ctx) Object *ExecutionEngine::newNumberPrototype(Context *ctx, FunctionObject *proto) { - return new NumberPrototype(ctx, proto); + Object *numberProto = new NumberPrototype(ctx, proto); + numberProto->prototype = objectPrototype.objectValue; + return numberProto; } Object *ExecutionEngine::newBooleanObject(const Value &value) @@ -281,6 +285,18 @@ Object *ExecutionEngine::newBooleanObject(const Value &value) return new BooleanObject(value); } +FunctionObject *ExecutionEngine::newBooleanCtor(Context *ctx) +{ + return new BooleanCtor(ctx); +} + +Object *ExecutionEngine::newBooleanPrototype(Context *ctx, FunctionObject *proto) +{ + Object *booleanProto = new BooleanPrototype(ctx, proto); + booleanProto->prototype = objectPrototype.objectValue; + return booleanProto; +} + Object *ExecutionEngine::newErrorObject(const Value &value) { return new ErrorObject(value); diff --git a/qmljs_objects.h b/qmljs_objects.h index ae4efab409..ebc3cd006b 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -370,6 +370,9 @@ struct ExecutionEngine Object *newNumberPrototype(Context *ctx, FunctionObject *proto); Object *newBooleanObject(const Value &value); + FunctionObject *newBooleanCtor(Context *ctx); + Object *newBooleanPrototype(Context *ctx, FunctionObject *proto); + Object *newErrorObject(const Value &value); Object *newMathObject(Context *ctx); Object *newArgumentsObject(Context *ctx); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 2c283599a1..572928b132 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -211,6 +211,7 @@ void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean) Value value; __qmljs_init_boolean(&value, boolean); __qmljs_init_object(result, ctx->engine->newBooleanObject(value)); + result->objectValue->prototype = ctx->engine->objectPrototype.objectValue; } void __qmljs_new_number_object(Context *ctx, Value *result, double number) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 4e215ef9ed..506e866d72 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -567,6 +567,70 @@ void NumberPrototype::method_toPrecision(Context *ctx) ctx->result = Value::fromString(ctx, QString::number(v, 'g', int (fdigits))); } +// +// Boolean object +// +Value BooleanCtor::create(ExecutionEngine *engine) +{ + Context *ctx = engine->rootContext; + FunctionObject *ctor = ctx->engine->newBooleanCtor(ctx); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newBooleanPrototype(ctx, ctor))); + return Value::fromObject(ctor); +} + +BooleanCtor::BooleanCtor(Context *scope) + : FunctionObject(scope) +{ +} + +void BooleanCtor::construct(Context *ctx) +{ + const double n = ctx->argument(0).toBoolean(ctx); + __qmljs_init_object(&ctx->thisObject, ctx->engine->newBooleanObject(Value::fromBoolean(n))); +} + +void BooleanCtor::call(Context *ctx) +{ + double value = ctx->argumentCount ? ctx->argument(0).toBoolean(ctx) : 0; + __qmljs_init_boolean(&ctx->result, value); +} + +BooleanPrototype::BooleanPrototype(Context *ctx, FunctionObject *ctor) +{ + ctor->setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); + ctor->setProperty(ctx, QLatin1String("toString"), method_toString); + ctor->setProperty(ctx, QLatin1String("valueOf"), method_valueOf); +} + +void BooleanPrototype::method_toString(Context *ctx) +{ + Value self = ctx->thisObject; +// if (self.classInfo() != classInfo) { +// return throwThisObjectTypeError( +// context, QLatin1String("Boolean.prototype.toString")); +// } + assert(self.isObject()); + Value internalValue; + self.objectValue->defaultValue(ctx, &internalValue, PREFERREDTYPE_HINT); + assert(internalValue.isBoolean()); + bool v = internalValue.booleanValue; + ctx->result = Value::fromString(ctx, QLatin1String(v ? "true" : "false")); +} + +void BooleanPrototype::method_valueOf(Context *ctx) +{ + Value self = ctx->thisObject; +// if (self.classInfo() != classInfo) { +// return throwThisObjectTypeError( +// context, QLatin1String("Boolean.prototype.valueOf")); +// } + assert(self.isObject()); + Value internalValue; + self.objectValue->defaultValue(ctx, &internalValue, PREFERREDTYPE_HINT); + assert(internalValue.isBoolean()); + ctx->result = internalValue; +} + // // Math object // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 386c2f11f4..d37aefc23f 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -83,6 +83,25 @@ protected: static void method_toPrecision(Context *ctx); }; +struct BooleanCtor: FunctionObject +{ + static Value create(ExecutionEngine *engine); + + BooleanCtor(Context *scope); + + virtual void construct(Context *ctx); + virtual void call(Context *ctx); +}; + +struct BooleanPrototype: Object +{ + BooleanPrototype(Context *ctx, FunctionObject *ctor); + +protected: + static void method_toString(Context *ctx); + static void method_valueOf(Context *ctx); +}; + struct MathObject: Object { MathObject(Context *ctx); -- cgit v1.2.3 From 37290dc90e47cf040298f3d891a9fbe8cee1178c Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 16 May 2012 11:58:07 +0200 Subject: checkpoint --- qmljs_objects.cpp | 17 +++++++++++++++ qmljs_objects.h | 13 +++++++++++- qmljs_runtime.cpp | 3 +++ qv4codegen.cpp | 41 +++++++++++++++++++++++++++++------- qv4codegen_p.h | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4ir.cpp | 7 ++++--- qv4ir_p.h | 2 +- qv4isel.cpp | 15 +++++++++---- 8 files changed, 144 insertions(+), 17 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 3192ac943c..011b39c05e 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -140,11 +140,20 @@ ScriptFunction::ScriptFunction(Context *scope, IR::Function *function) formalParameterList[i] = scope->engine->identifier(*function->formals.at(i)); } } + + varCount = function->locals.size(); + if (varCount) { + varList = new String*[varCount]; + for (size_t i = 0; i < varCount; ++i) { + varList[i] = scope->engine->identifier(*function->locals.at(i)); + } + } } ScriptFunction::~ScriptFunction() { delete[] formalParameterList; + delete[] varList; } void ScriptFunction::call(VM::Context *ctx) @@ -161,6 +170,14 @@ void ScriptFunction::construct(VM::Context *ctx) Value *ArgumentsObject::getProperty(String *name, PropertyAttributes *attributes) { if (context) { + for (size_t i = 0; i < context->varCount; ++i) { + String *var = context->vars[i]; + if (__qmljs_string_equal(context, var, name)) { + if (attributes) + *attributes = PropertyAttributes(*attributes | WritableAttribute); + return &context->locals[i]; + } + } for (size_t i = 0; i < context->formalCount; ++i) { String *formal = context->formals[i]; if (__qmljs_string_equal(context, formal, name)) { diff --git a/qmljs_objects.h b/qmljs_objects.h index ebc3cd006b..fdb0b98e78 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -239,12 +239,16 @@ struct FunctionObject: Object { Context *scope; String **formalParameterList; size_t formalParameterCount; + String **varList; + size_t varCount; bool needsActivation; FunctionObject(Context *scope) : scope(scope) , formalParameterList(0) , formalParameterCount(0) + , varList(0) + , varCount(0) , needsActivation(true) {} virtual FunctionObject *asFunctionObject() { return this; } @@ -289,15 +293,19 @@ struct Context { Value thisObject; Value *arguments; size_t argumentCount; + Value *locals; Value result; String **formals; size_t formalCount; + String **vars; + size_t varCount; bool calledAsConstructor; Value *lookup(String *name) { if (activation.is(OBJECT_TYPE)) { - if (Value *prop = activation.objectValue->getProperty(name)) + if (Value *prop = activation.objectValue->getProperty(name)) { return prop; + } } return parent ? parent->lookup(name) : 0; } @@ -323,11 +331,14 @@ struct Context { parent = 0; arguments = 0; argumentCount = 0; + locals = 0; activation.type = NULL_TYPE; thisObject.type = NULL_TYPE; result.type = UNDEFINED_TYPE; formals = 0; formalCount = 0; + vars = 0; + varCount = 0; calledAsConstructor = false; } }; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 572928b132..e52ef24e05 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -504,6 +504,9 @@ void __qmljs_call_value(Context *context, Value *result, const Value *func, Valu ctx->arguments = new Value[argc]; std::copy(args, args + argc, ctx->arguments); } + ctx->vars = f->varList; + ctx->varCount = f->varCount; + ctx->locals = 0; f->call(ctx); if (result) __qmljs_copy(result, &ctx->result); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 608eba848e..97b004bf1f 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -158,25 +158,33 @@ void liveness(IR::Function *function) } while (changed); } -struct ScanFunctionBody: Visitor +} // end of anonymous namespace + +struct Codegen::ScanFunctionBody: Visitor { using Visitor::visit; // search for locals + Codegen::Environment *env; QList locals; int maxNumberOfArguments; bool hasDirectEval; bool hasNestedFunctions; ScanFunctionBody() - : maxNumberOfArguments(0) + : env(0) + , maxNumberOfArguments(0) , hasDirectEval(false) , hasNestedFunctions(false) { } - void operator()(Node *node) { + void operator()(Node *node, Environment *e) + { + env = e; + maxNumberOfArguments = 0; hasDirectEval = false; + hasNestedFunctions = false; locals.clear(); if (node) node->accept(this); @@ -210,6 +218,7 @@ protected: virtual bool visit(VariableDeclaration *ast) { + env->enter(ast->name); if (! locals.contains(ast->name)) locals.append(ast->name); return true; @@ -217,6 +226,7 @@ protected: virtual bool visit(FunctionExpression *ast) { + env->enter(ast->name); hasNestedFunctions = true; if (! locals.contains(ast->name)) locals.append(ast->name); @@ -225,6 +235,7 @@ protected: virtual bool visit(FunctionDeclaration *ast) { + env->enter(ast->name); hasNestedFunctions = true; if (! locals.contains(ast->name)) locals.append(ast->name); @@ -232,13 +243,13 @@ protected: } }; -} // end of anonymous namespace - Codegen::Codegen() - : _function(0) + : _module(0) + , _function(0) , _block(0) , _exitBlock(0) , _returnAddress(0) + , _env(0) { } @@ -246,8 +257,9 @@ void Codegen::operator()(AST::Program *node, IR::Module *module) { _module = module; + Scope scope(this, newEnvironment()); ScanFunctionBody globalCodeInfo; - globalCodeInfo(node); + globalCodeInfo(node, _env); IR::Function *globalCode = _module->newFunction(QLatin1String("%entry")); globalCode->hasDirectEval = globalCodeInfo.hasDirectEval; @@ -269,6 +281,9 @@ void Codegen::operator()(AST::Program *node, IR::Module *module) foreach (IR::Function *function, _module->functions) { linearize(function); } + + qDeleteAll(_allEnvironments); + _allEnvironments.clear(); } IR::Expr *Codegen::member(IR::Expr *base, const QString *name) @@ -568,6 +583,7 @@ void Codegen::variableDeclaration(VariableDeclaration *ast) move(_block->TEMP(-(index + 1)), *expr); return; } + Q_UNREACHABLE(); } else { move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), *expr); } @@ -1252,7 +1268,9 @@ void Codegen::linearize(IR::Function *function) trace.append(exitBlock); function->basicBlocks = trace; +#ifndef QV4_NO_LIVENESS liveness(function); +#endif static bool showCode = !qgetenv("SHOW_CODE").isNull(); if (showCode) { @@ -1304,6 +1322,7 @@ void Codegen::linearize(IR::Function *function) s->dump(out, IR::Stmt::MIR); out.flush(); +#ifndef QV4_NO_LIVENESS for (int i = 60 - str.size(); i >= 0; --i) str.append(' '); @@ -1330,6 +1349,9 @@ void Codegen::linearize(IR::Function *function) qout << " %" << i; } } +#else + qout << " " << str; +#endif qout << endl; @@ -1342,16 +1364,19 @@ void Codegen::linearize(IR::Function *function) << endl; } +#ifndef QV4_NO_LIVENESS foreach (IR::BasicBlock *block, function->basicBlocks) { foreach (IR::Stmt *s, block->statements) s->destroyData(); } +#endif } void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) { + Scope scope(this, newEnvironment()); ScanFunctionBody functionInfo; - functionInfo(ast->body); + functionInfo(ast->body, _env); IR::Function *function = _module->newFunction(ast->name.toString()); IR::BasicBlock *entryBlock = function->newBasicBlock(); diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 61ef851785..92afa90bd6 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -53,6 +53,65 @@ protected: } }; + struct Environment { + Environment *parent; + QHash members; + int count; + + Environment(Environment *parent = 0) + : parent(parent) + , count(0) {} + + int findMember(const QStringRef &name) const + { + return members.value(name, -1); + } + + bool lookupMember(const QStringRef &name, Environment **scope, int *index, int *distance) + { + Environment *it = this; + *distance = 0; + for (; it; it = it->parent, ++(*distance)) { + int idx = it->findMember(name); + if (idx != -1) { + *scope = it; + *index = idx; + return true; + } + } + return false; + } + + void enter(const QStringRef &name) + { + int idx = members.value(name, -1); + if (idx == -1) + members.insert(name, count++); + } + }; + + Environment *newEnvironment() + { + Environment *scope = new Environment(_env); + return scope; + } + + Environment *changeEnvironment(Environment *env) + { + qSwap(_env, env); + return env; + } + + struct Scope { + Codegen *cg; + Environment *previous; + inline Scope(Codegen *cg, Environment *env) { previous = cg->changeEnvironment(env); } + inline ~Scope() { cg->changeEnvironment(previous); } + + private: + Q_DISABLE_COPY(Scope) + }; + struct UiMember { }; @@ -225,6 +284,10 @@ private: IR::BasicBlock *_block; IR::BasicBlock *_exitBlock; unsigned _returnAddress; + QVector _allEnvironments; + Environment *_env; + + struct ScanFunctionBody; }; } // end of namespace QQmlJS diff --git a/qv4ir.cpp b/qv4ir.cpp index 0ea0b4d52e..6bcf6b5b8f 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -241,10 +241,11 @@ void Name::dump(QTextStream &out) void Temp::dump(QTextStream &out) { - if (index < 0) + if (index < 0) { out << '#' << -(index + 1); // negative and 1-based. - else + } else { out << '%' << index; // temp + } } void Closure::dump(QTextStream &out) @@ -496,7 +497,7 @@ unsigned BasicBlock::newTemp() return function->tempCount++; } -Temp *BasicBlock::TEMP(unsigned index) +Temp *BasicBlock::TEMP(int index) { Temp *e = function->New(); e->init(IR::InvalidType, index); diff --git a/qv4ir_p.h b/qv4ir_p.h index 594077a33a..e65b52a689 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -655,7 +655,7 @@ struct BasicBlock { unsigned newTemp(); - Temp *TEMP(unsigned index); + Temp *TEMP(int index); Expr *CONST(Type type, double value); Expr *STRING(const QString *value); diff --git a/qv4isel.cpp b/qv4isel.cpp index 3e8d27a976..b50dd83e2c 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -97,12 +97,14 @@ InstructionSelection::~InstructionSelection() void InstructionSelection::operator()(IR::Function *function) { + qSwap(_function, function); + _code = _codePtr; _code = (uchar *) ((size_t(_code) + 15) & ~15); - function->code = (void (*)(VM::Context *)) _code; + _function->code = (void (*)(VM::Context *)) _code; _codePtr = _code; - int locals = (function->tempCount + function->maxNumberOfArguments) * sizeof(Value); + int locals = (_function->tempCount + _function->maxNumberOfArguments) * sizeof(Value); locals = (locals + 15) & ~15; amd64_push_reg(_codePtr, AMD64_RBP); @@ -115,7 +117,11 @@ void InstructionSelection::operator()(IR::Function *function) amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_R15, AMD64_R15); - foreach (IR::BasicBlock *block, function->basicBlocks) { + amd64_lea_membase(_codePtr, AMD64_RAX, AMD64_RSP, _function->maxNumberOfArguments * sizeof(Value)); + amd64_mov_membase_reg(_codePtr, AMD64_R14, offsetof(Context, locals), AMD64_RAX, 8); + + + foreach (IR::BasicBlock *block, _function->basicBlocks) { _block = block; _addrs[block] = _codePtr; foreach (IR::Stmt *s, block->statements) { @@ -154,6 +160,7 @@ void InstructionSelection::operator()(IR::Function *function) } } #endif + qSwap(_function, _function); } String *InstructionSelection::identifier(const QString &s) @@ -168,7 +175,7 @@ void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) amd64_mov_reg_membase(_codePtr, reg, AMD64_R14, offsetof(Context, arguments), 8); amd64_lea_membase(_codePtr, reg, reg, sizeof(Value) * arg); } else { - amd64_lea_membase(_codePtr, reg, AMD64_RBP, sizeof(Value) * -t->index); + amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * (_function->maxNumberOfArguments + t->index)); } } -- cgit v1.2.3 From 41d4ac6294c27b6fa80fb3d8a9513b94b6254389 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 16 May 2012 16:19:55 +0200 Subject: Refactored the stack frames. The new layout should simplify the use of static context environments while compiling the code. --- qmljs_objects.cpp | 59 ++++++++++++++++++++++++++++++ qmljs_objects.h | 19 +++++++--- qmljs_runtime.cpp | 107 +++++++----------------------------------------------- qmljs_runtime.h | 12 ++++++ qv4codegen.cpp | 6 ++- qv4codegen_p.h | 2 +- qv4isel.cpp | 13 +++---- 7 files changed, 109 insertions(+), 109 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 011b39c05e..44d8430eb2 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -328,3 +328,62 @@ Object *ExecutionEngine::newArgumentsObject(Context *ctx) { return new ArgumentsObject(ctx); } + +void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc) +{ + engine = e; + parent = f->scope; + + if (f->needsActivation) + __qmljs_init_object(&activation, engine->newArgumentsObject(this)); + else + __qmljs_init_null(&activation); + + if (object) + thisObject = *object; + else + __qmljs_init_null(&thisObject); + + formals = f->formalParameterList; + formalCount = f->formalParameterCount; + arguments = args; + argumentCount = argc; + if (argc && f->needsActivation) { + arguments = new Value[argc]; + std::copy(args, args + argc, arguments); + } + vars = f->varList; + varCount = f->varCount; + locals = varCount ? new Value[varCount] : 0; + std::fill(locals, locals + varCount, Value::undefinedValue()); +} + +void Context::leaveCallContext(FunctionObject *f, Value *returnValue) +{ + if (returnValue) + __qmljs_copy(returnValue, &result); + + if (! f->needsActivation) { + delete[] locals; + locals = 0; + } +} + +void Context::initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc) +{ + initCallContext(e, object, f, args, argc); + calledAsConstructor = true; +} + +void Context::leaveConstructorContext(FunctionObject *f, Value *returnValue) +{ + assert(thisObject.is(OBJECT_TYPE)); + result = thisObject; + + Value proto; + if (f->get(engine->identifier(QLatin1String("prototype")), &proto)) { + if (proto.type == OBJECT_TYPE) + thisObject.objectValue->prototype = proto.objectValue; + } + leaveCallContext(f, returnValue); +} diff --git a/qmljs_objects.h b/qmljs_objects.h index fdb0b98e78..08f9bcbcfc 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -301,13 +301,16 @@ struct Context { size_t varCount; bool calledAsConstructor; - Value *lookup(String *name) { - if (activation.is(OBJECT_TYPE)) { - if (Value *prop = activation.objectValue->getProperty(name)) { - return prop; + Value *lookup(String *name) + { + for (Context *ctx = this; ctx; ctx = ctx->parent) { + if (ctx->activation.is(OBJECT_TYPE)) { + if (Value *prop = ctx->activation.objectValue->getProperty(name)) { + return prop; + } } } - return parent ? parent->lookup(name) : 0; + return 0; } inline Value argument(size_t index = 0) @@ -341,6 +344,12 @@ struct Context { varCount = 0; calledAsConstructor = false; } + + void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc); + void leaveCallContext(FunctionObject *f, Value *r); + + void initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc); + void leaveConstructorContext(FunctionObject *f, Value *returnValue); }; struct ExecutionEngine diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index e52ef24e05..337f2f7a41 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -238,6 +238,7 @@ void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *valu void __qmljs_set_property_boolean(Context *ctx, Value *object, String *name, bool number) { + Q_UNUSED(ctx); Value value; __qmljs_init_boolean(&value, number); object->objectValue->put(name, value, /*flag*/ 0); @@ -245,6 +246,7 @@ void __qmljs_set_property_boolean(Context *ctx, Value *object, String *name, boo void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double number) { + Q_UNUSED(ctx); Value value; __qmljs_init_number(&value, number); object->objectValue->put(name, value, /*flag*/ 0); @@ -252,6 +254,7 @@ void __qmljs_set_property_number(Context *ctx, Value *object, String *name, doub void __qmljs_set_property_string(Context *ctx, Value *object, String *name, String *s) { + Q_UNUSED(ctx); Value value; __qmljs_init_string(&value, s); object->objectValue->put(name, value, /*flag*/ 0); @@ -266,9 +269,9 @@ void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR: void __qmljs_set_activation_property(Context *ctx, String *name, Value *value) { - if (Value *prop = ctx->lookup(name)) - __qmljs_copy(prop, value); - else + if (Value *prop = ctx->lookup(name)) { + *prop = *value; + } else ctx->activation.objectValue->put(name, *value); } @@ -455,24 +458,9 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S if (FunctionObject *f = func.objectValue->asFunctionObject()) { Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->init(context->engine); - ctx->parent = f->scope; - if (f->needsActivation) - __qmljs_init_object(&ctx->activation, ctx->engine->newArgumentsObject(ctx)); - else - __qmljs_init_null(&ctx->activation); - ctx->thisObject = thisObject; - ctx->formals = f->formalParameterList; - ctx->formalCount = f->formalParameterCount; - ctx->arguments = args; - ctx->argumentCount = argc; - if (argc && f->needsActivation) { - ctx->arguments = new Value[argc]; - std::copy(args, args + argc, ctx->arguments); - } + ctx->initCallContext(context->engine, &thisObject, f, args, argc); f->call(ctx); - if (result) - __qmljs_copy(result, &ctx->result); + ctx->leaveCallContext(f, result); } else { assert(!"not a function"); } @@ -483,33 +471,13 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S void __qmljs_call_value(Context *context, Value *result, const Value *func, Value *args, int argc) { - Q_UNUSED(context); - if (func->type == OBJECT_TYPE) { if (FunctionObject *f = func->objectValue->asFunctionObject()) { Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->init(context->engine); - ctx->parent = f->scope; - if (f->needsActivation) - __qmljs_init_object(&ctx->activation, ctx->engine->newArgumentsObject(ctx)); - else - __qmljs_init_null(&ctx->activation); - __qmljs_init_null(&ctx->thisObject); - ctx->formals = f->formalParameterList; - ctx->formalCount = f->formalParameterCount; - ctx->arguments = args; - ctx->argumentCount = argc; - if (argc && f->needsActivation) { - ctx->arguments = new Value[argc]; - std::copy(args, args + argc, ctx->arguments); - } - ctx->vars = f->varList; - ctx->varCount = f->varCount; - ctx->locals = 0; + ctx->initCallContext(context->engine, 0, f, args, argc); f->call(ctx); - if (result) - __qmljs_copy(result, &ctx->result); + ctx->leaveCallContext(f, result); } else { assert(!"not a function"); } @@ -534,33 +502,9 @@ void __qmljs_construct_value(Context *context, Value *result, const Value *func, if (FunctionObject *f = func->objectValue->asFunctionObject()) { Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->init(context->engine); - ctx->parent = f->scope; - if (f->needsActivation) - __qmljs_init_object(&ctx->activation, ctx->engine->newArgumentsObject(ctx)); - else - __qmljs_init_null(&ctx->activation); - __qmljs_init_null(&ctx->thisObject); - ctx->formals = f->formalParameterList; - ctx->formalCount = f->formalParameterCount; - ctx->arguments = args; - ctx->argumentCount = argc; - if (argc && f->needsActivation) { - ctx->arguments = new Value[argc]; - std::copy(args, args + argc, ctx->arguments); - } - ctx->calledAsConstructor = true; + ctx->initConstructorContext(context->engine, 0, f, args, argc); f->construct(ctx); - assert(ctx->thisObject.is(OBJECT_TYPE)); - ctx->result = ctx->thisObject; - Value proto; - if (f->get(ctx->engine->identifier(QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol - if (proto.type == OBJECT_TYPE) - ctx->thisObject.objectValue->prototype = proto.objectValue; - } - - if (result) - __qmljs_copy(result, &ctx->thisObject); + ctx->leaveConstructorContext(f, result); } else { assert(!"not a function"); } @@ -582,33 +526,10 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba if (FunctionObject *f = func.objectValue->asFunctionObject()) { Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->init(context->engine); - ctx->parent = f->scope; - if (f->needsActivation) - __qmljs_init_object(&ctx->activation, ctx->engine->newArgumentsObject(ctx)); - else - __qmljs_init_null(&ctx->activation); - ctx->thisObject = thisObject; - ctx->formals = f->formalParameterList; - ctx->formalCount = f->formalParameterCount; - ctx->arguments = args; - ctx->argumentCount = argc; - if (argc && f->needsActivation) { - ctx->arguments = new Value[argc]; - std::copy(args, args + argc, ctx->arguments); - } + ctx->initConstructorContext(context->engine, 0, f, args, argc); ctx->calledAsConstructor = true; f->construct(ctx); - assert(ctx->thisObject.is(OBJECT_TYPE)); - - Value proto; - if (f->get(ctx->engine->identifier(QLatin1String("prototype")), &proto)) { // ### `prototype' should be a unique symbol - if (proto.type == OBJECT_TYPE) - ctx->thisObject.objectValue->prototype = proto.objectValue; - } - - if (result) - __qmljs_copy(result, &ctx->thisObject); + ctx->leaveConstructorContext(f, result); } else { assert(!"not a function"); } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index b2b998737c..467e30215c 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -174,6 +174,18 @@ struct Value { inline bool is(ValueType t) const { return type == t; } inline bool isNot(ValueType t) const { return type != t; } + static inline Value undefinedValue() { + Value v; + v.type = UNDEFINED_TYPE; + return v; + } + + static inline Value nullValue() { + Value v; + v.type = NULL_TYPE; + return v; + } + static inline Value fromBoolean(bool value) { Value v; __qmljs_init_boolean(&v, value); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 97b004bf1f..ff50fb955d 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -256,6 +256,7 @@ Codegen::Codegen() void Codegen::operator()(AST::Program *node, IR::Module *module) { _module = module; + _env = 0; Scope scope(this, newEnvironment()); ScanFunctionBody globalCodeInfo; @@ -1385,11 +1386,12 @@ void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) function->hasNestedFunctions = functionInfo.hasNestedFunctions; function->maxNumberOfArguments = functionInfo.maxNumberOfArguments; - if (! function->needsActivation()) { + //if (! function->needsActivation()) + { for (int i = 0; i < functionInfo.locals.size(); ++i) { unsigned t = entryBlock->newTemp(); Q_ASSERT(t == unsigned(i)); - entryBlock->MOVE(entryBlock->TEMP(t), entryBlock->CONST(IR::UndefinedType, 0)); + //entryBlock->MOVE(entryBlock->TEMP(t), entryBlock->CONST(IR::UndefinedType, 0)); } } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 92afa90bd6..c44341306a 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -105,7 +105,7 @@ protected: struct Scope { Codegen *cg; Environment *previous; - inline Scope(Codegen *cg, Environment *env) { previous = cg->changeEnvironment(env); } + inline Scope(Codegen *cg, Environment *env): cg(cg) { previous = cg->changeEnvironment(env); } inline ~Scope() { cg->changeEnvironment(previous); } private: diff --git a/qv4isel.cpp b/qv4isel.cpp index b50dd83e2c..77e2e068c5 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -104,7 +104,7 @@ void InstructionSelection::operator()(IR::Function *function) _function->code = (void (*)(VM::Context *)) _code; _codePtr = _code; - int locals = (_function->tempCount + _function->maxNumberOfArguments) * sizeof(Value); + int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) * sizeof(Value); locals = (locals + 15) & ~15; amd64_push_reg(_codePtr, AMD64_RBP); @@ -115,12 +115,6 @@ void InstructionSelection::operator()(IR::Function *function) amd64_mov_reg_reg(_codePtr, AMD64_R14, AMD64_RDI, 8); amd64_alu_reg_imm(_codePtr, X86_SUB, AMD64_RSP, locals); - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_R15, AMD64_R15); - - amd64_lea_membase(_codePtr, AMD64_RAX, AMD64_RSP, _function->maxNumberOfArguments * sizeof(Value)); - amd64_mov_membase_reg(_codePtr, AMD64_R14, offsetof(Context, locals), AMD64_RAX, 8); - - foreach (IR::BasicBlock *block, _function->basicBlocks) { _block = block; _addrs[block] = _codePtr; @@ -174,8 +168,11 @@ void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) const int arg = -t->index - 1; amd64_mov_reg_membase(_codePtr, reg, AMD64_R14, offsetof(Context, arguments), 8); amd64_lea_membase(_codePtr, reg, reg, sizeof(Value) * arg); + } else if (t->index < _function->locals.size()) { + amd64_mov_reg_membase(_codePtr, reg, AMD64_R14, offsetof(Context, locals), 8); + amd64_lea_membase(_codePtr, reg, reg, sizeof(Value) * t->index); } else { - amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * (_function->maxNumberOfArguments + t->index)); + amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * (_function->maxNumberOfArguments + t->index - _function->locals.size())); } } -- cgit v1.2.3 From acb76025c450c5167f2c582de2c82c5078a5230a Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 16 May 2012 16:31:59 +0200 Subject: Store the locals in %r15. --- qv4isel.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qv4isel.cpp b/qv4isel.cpp index 77e2e068c5..3a85815fe0 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -115,6 +115,8 @@ void InstructionSelection::operator()(IR::Function *function) amd64_mov_reg_reg(_codePtr, AMD64_R14, AMD64_RDI, 8); amd64_alu_reg_imm(_codePtr, X86_SUB, AMD64_RSP, locals); + amd64_mov_reg_membase(_codePtr, AMD64_R15, AMD64_R14, offsetof(Context, locals), 8); + foreach (IR::BasicBlock *block, _function->basicBlocks) { _block = block; _addrs[block] = _codePtr; @@ -169,8 +171,7 @@ void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) amd64_mov_reg_membase(_codePtr, reg, AMD64_R14, offsetof(Context, arguments), 8); amd64_lea_membase(_codePtr, reg, reg, sizeof(Value) * arg); } else if (t->index < _function->locals.size()) { - amd64_mov_reg_membase(_codePtr, reg, AMD64_R14, offsetof(Context, locals), 8); - amd64_lea_membase(_codePtr, reg, reg, sizeof(Value) * t->index); + amd64_lea_membase(_codePtr, reg, AMD64_R15, sizeof(Value) * t->index); } else { amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * (_function->maxNumberOfArguments + t->index - _function->locals.size())); } -- cgit v1.2.3 From a34f11737ff29c383ab87b58258213a2ec92d74b Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 16 May 2012 19:00:31 +0200 Subject: Simplified the compiler. --- main.cpp | 24 +++--- qv4codegen.cpp | 235 ++++++++++++++++++++++++++++++--------------------------- qv4codegen_p.h | 62 ++++++++------- 3 files changed, 168 insertions(+), 153 deletions(-) diff --git a/main.cpp b/main.cpp index dec537a9de..9026a96f07 100644 --- a/main.cpp +++ b/main.cpp @@ -68,18 +68,14 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, QQmlJS::Engine *engine, const QSt Codegen cg; IR::Module module; - cg(program, &module); + IR::Function *globalCode = cg(program, &module); const size_t codeSize = 10 * getpagesize(); uchar *code = (uchar *) malloc(codeSize); x86_64::InstructionSelection isel(vm, &module, code); - QHash codeByName; foreach (IR::Function *function, module.functions) { isel(function); - if (function->name && ! function->name->isEmpty()) { - codeByName.insert(*function->name, function); - } } if (! protect(code, codeSize)) @@ -91,13 +87,19 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, QQmlJS::Engine *engine, const QSt globalObject->put(vm->identifier(QLatin1String("print")), VM::Value::fromObject(new builtins::Print(ctx))); - foreach (IR::Function *function, module.functions) { - if (function->name && ! function->name->isEmpty()) { - globalObject->put(vm->identifier(*function->name), - VM::Value::fromObject(ctx->engine->newScriptFunction(ctx, function))); - } + ctx->varCount = globalCode->locals.size(); + if (ctx->varCount) { + ctx->locals = new VM::Value[ctx->varCount]; + ctx->vars = new VM::String*[ctx->varCount]; + std::fill(ctx->locals, ctx->locals + ctx->varCount, VM::Value::undefinedValue()); + for (size_t i = 0; i < ctx->varCount; ++i) + ctx->vars[i] = ctx->engine->identifier(*globalCode->locals.at(i)); } - codeByName.value(QLatin1String("%entry"))->code(ctx); + + globalCode->code(ctx); + + delete[] ctx->locals; + delete[] ctx->vars; } } diff --git a/qv4codegen.cpp b/qv4codegen.cpp index ff50fb955d..a5f2b3d537 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -160,87 +161,110 @@ void liveness(IR::Function *function) } // end of anonymous namespace -struct Codegen::ScanFunctionBody: Visitor +class Codegen::ScanFunctions: Visitor { - using Visitor::visit; - - // search for locals - Codegen::Environment *env; - QList locals; - int maxNumberOfArguments; - bool hasDirectEval; - bool hasNestedFunctions; - - ScanFunctionBody() - : env(0) - , maxNumberOfArguments(0) - , hasDirectEval(false) - , hasNestedFunctions(false) +public: + ScanFunctions(Codegen *cg) + : _cg(cg) + , _env(0) { } - void operator()(Node *node, Environment *e) + void operator()(Node *node) { - env = e; - maxNumberOfArguments = 0; - hasDirectEval = false; - hasNestedFunctions = false; - locals.clear(); + _env = 0; if (node) node->accept(this); } protected: + using Visitor::visit; + using Visitor::endVisit; + + inline void enterEnvironment(Node *node) + { + Environment *e = _cg->newEnvironment(node, _env); + _envStack.append(e); + _env = e; + } + + inline void leaveEnvironment() + { + _envStack.pop(); + _env = _envStack.isEmpty() ? 0 : _envStack.top(); + } + + virtual bool visit(Program *ast) + { + enterEnvironment(ast); + return true; + } + + virtual void endVisit(Program *) + { + leaveEnvironment(); + } + virtual bool visit(CallExpression *ast) { - if (! hasDirectEval) { + if (! _env->hasDirectEval) { if (IdentifierExpression *id = cast(ast->base)) { if (id->name == QLatin1String("eval")) { - hasDirectEval = true; + _env->hasDirectEval = true; } } } int argc = 0; - for (AST::ArgumentList *it = ast->arguments; it; it = it->next) + for (ArgumentList *it = ast->arguments; it; it = it->next) ++argc; - maxNumberOfArguments = qMax(maxNumberOfArguments, argc); + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); return true; } virtual bool visit(NewMemberExpression *ast) { int argc = 0; - for (AST::ArgumentList *it = ast->arguments; it; it = it->next) + for (ArgumentList *it = ast->arguments; it; it = it->next) ++argc; - maxNumberOfArguments = qMax(maxNumberOfArguments, argc); + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); return true; } virtual bool visit(VariableDeclaration *ast) { - env->enter(ast->name); - if (! locals.contains(ast->name)) - locals.append(ast->name); + _env->enter(ast->name.toString()); return true; } virtual bool visit(FunctionExpression *ast) { - env->enter(ast->name); - hasNestedFunctions = true; - if (! locals.contains(ast->name)) - locals.append(ast->name); - return false; + _env->hasNestedFunctions = true; + _env->enter(ast->name.toString()); + enterEnvironment(ast); + return true; + } + + virtual void endVisit(FunctionExpression *) + { + leaveEnvironment(); } virtual bool visit(FunctionDeclaration *ast) { - env->enter(ast->name); - hasNestedFunctions = true; - if (! locals.contains(ast->name)) - locals.append(ast->name); - return false; + _env->hasNestedFunctions = true; + _env->enter(ast->name.toString()); + enterEnvironment(ast); + return true; } + + virtual void endVisit(FunctionDeclaration *) + { + leaveEnvironment(); + } + + Codegen *_cg; + Environment *_env; + QStack _envStack; }; Codegen::Codegen() @@ -253,38 +277,36 @@ Codegen::Codegen() { } -void Codegen::operator()(AST::Program *node, IR::Module *module) +IR::Function *Codegen::operator()(Program *node, IR::Module *module) { _module = module; _env = 0; - Scope scope(this, newEnvironment()); - ScanFunctionBody globalCodeInfo; - globalCodeInfo(node, _env); + ScanFunctions scan(this); + scan(node); - IR::Function *globalCode = _module->newFunction(QLatin1String("%entry")); - globalCode->hasDirectEval = globalCodeInfo.hasDirectEval; - globalCode->hasNestedFunctions = true; // ### FIXME: initialize it with globalCodeInfo.hasNestedFunctions; - globalCode->maxNumberOfArguments = globalCodeInfo.maxNumberOfArguments; - _function = globalCode; - _block = _function->newBasicBlock(); - _exitBlock = _function->newBasicBlock(); - _returnAddress = _block->newTemp(); - move(_block->TEMP(_returnAddress), _block->CONST(IR::UndefinedType, 0)); - _exitBlock->RET(_exitBlock->TEMP(_returnAddress), IR::UndefinedType); - - program(node); - - if (! _block->isTerminated()) { - _block->JUMP(_exitBlock); - } + IR::Function *globalCode = defineFunction(QLatin1String("%entry"), node, 0, node->elements); foreach (IR::Function *function, _module->functions) { linearize(function); } - qDeleteAll(_allEnvironments); - _allEnvironments.clear(); + qDeleteAll(_envMap); + _envMap.clear(); + + return globalCode; +} + +void Codegen::enterEnvironment(Node *node) +{ + _env = _envMap.value(node); + Q_ASSERT(_env); +} + +void Codegen::leaveEnvironment() +{ + Q_ASSERT(_env); + _env = _env->parent; } IR::Expr *Codegen::member(IR::Expr *base, const QString *name) @@ -504,8 +526,9 @@ void Codegen::functionBody(FunctionBody *ast) void Codegen::program(Program *ast) { - if (ast) + if (ast) { sourceElements(ast->elements); + } } void Codegen::propertyNameAndValueList(PropertyNameAndValueList *) @@ -569,25 +592,11 @@ void Codegen::variableDeclaration(VariableDeclaration *ast) { if (ast->expression) { Result expr = expression(ast->expression); + Q_ASSERT(expr.code); - if (! expr.code) - expr.code = _block->CONST(IR::UndefinedType, 0); - - if (! _function->needsActivation()) { - int index = indexOfLocal(ast->name); - if (index != -1) { - move(_block->TEMP(index), *expr); - return; - } - index = indexOfArgument(ast->name); - if (index != -1) { - move(_block->TEMP(-(index + 1)), *expr); - return; - } - Q_UNREACHABLE(); - } else { - move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), *expr); - } + const int index = _env->findMember(ast->name.toString()); + Q_ASSERT(index != -1); + move(_block->TEMP(index), *expr); } } @@ -1005,14 +1014,21 @@ bool Codegen::visit(FieldMemberExpression *ast) bool Codegen::visit(FunctionExpression *ast) { - defineFunction(ast, false); + IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0, false); + if (_expr.accept(nx)) { + const int index = _env->findMember(ast->name.toString()); + Q_ASSERT(index != -1); + move(_block->TEMP(index), _block->CLOSURE(function)); + } else { + _expr.code = _block->CLOSURE(function); + } return false; } bool Codegen::visit(IdentifierExpression *ast) { if (! _function->needsActivation()) { - int index = indexOfLocal(ast->name); + int index = _env->findMember(ast->name.toString()); if (index != -1) { _expr.code = _block->TEMP(index); return false; @@ -1216,7 +1232,14 @@ bool Codegen::visit(VoidExpression *ast) bool Codegen::visit(FunctionDeclaration *ast) { - defineFunction(ast); + IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0, true); + if (_expr.accept(nx)) { + const int index = _env->findMember(ast->name.toString()); + Q_ASSERT(index != -1); + move(_block->TEMP(index), _block->CLOSURE(function)); + } else { + _expr.code = _block->CLOSURE(function); + } return false; } @@ -1373,47 +1396,42 @@ void Codegen::linearize(IR::Function *function) #endif } -void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) +IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body, bool /*isDeclaration*/) { - Scope scope(this, newEnvironment()); - ScanFunctionBody functionInfo; - functionInfo(ast->body, _env); - - IR::Function *function = _module->newFunction(ast->name.toString()); + enterEnvironment(ast); + IR::Function *function = _module->newFunction(name); IR::BasicBlock *entryBlock = function->newBasicBlock(); IR::BasicBlock *exitBlock = function->newBasicBlock(); - function->hasDirectEval = functionInfo.hasDirectEval; - function->hasNestedFunctions = functionInfo.hasNestedFunctions; - function->maxNumberOfArguments = functionInfo.maxNumberOfArguments; + function->hasDirectEval = _env->hasDirectEval; + function->hasNestedFunctions = _env->hasNestedFunctions; + function->maxNumberOfArguments = _env->maxNumberOfArguments; - //if (! function->needsActivation()) - { - for (int i = 0; i < functionInfo.locals.size(); ++i) { - unsigned t = entryBlock->newTemp(); - Q_ASSERT(t == unsigned(i)); - //entryBlock->MOVE(entryBlock->TEMP(t), entryBlock->CONST(IR::UndefinedType, 0)); - } + for (int i = 0; i < _env->vars.size(); ++i) { + unsigned t = entryBlock->newTemp(); + Q_ASSERT(t == unsigned(i)); } unsigned returnAddress = entryBlock->newTemp(); entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(IR::UndefinedType, 0)); - exitBlock->RET(_block->TEMP(returnAddress), IR::InvalidType); + exitBlock->RET(exitBlock->TEMP(returnAddress), IR::InvalidType); qSwap(_function, function); qSwap(_block, entryBlock); qSwap(_exitBlock, exitBlock); qSwap(_returnAddress, returnAddress); - for (FormalParameterList *it = ast->formals; it; it = it->next) { + for (FormalParameterList *it = formals; it; it = it->next) { _function->RECEIVE(it->name.toString()); } - foreach (const QStringRef &local, functionInfo.locals) { - _function->LOCAL(local.toString()); + foreach (const QString &local, _env->vars) { + _function->LOCAL(local); } - functionBody(ast->body); + sourceElements(body); if (! _block->isTerminated()) _block->JUMP(_exitBlock); @@ -1423,11 +1441,8 @@ void Codegen::defineFunction(FunctionExpression *ast, bool /*isDeclaration*/) qSwap(_exitBlock, exitBlock); qSwap(_returnAddress, returnAddress); - if (_expr.accept(nx)) { - // nothing to do - } else { - _expr.code = _block->CLOSURE(function); - } + leaveEnvironment(); + return function; } int Codegen::indexOfLocal(const QStringRef &string) const diff --git a/qv4codegen_p.h b/qv4codegen_p.h index c44341306a..f8780016dd 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -15,7 +15,7 @@ class Codegen: protected AST::Visitor public: Codegen(); - void operator()(AST::Program *ast, IR::Module *module); + IR::Function *operator()(AST::Program *ast, IR::Module *module); protected: enum Format { ex, cx, nx }; @@ -55,19 +55,24 @@ protected: struct Environment { Environment *parent; - QHash members; - int count; + QHash members; + QVector vars; + int maxNumberOfArguments; + bool hasDirectEval; + bool hasNestedFunctions; - Environment(Environment *parent = 0) + Environment(Environment *parent) : parent(parent) - , count(0) {} + , maxNumberOfArguments(0) + , hasDirectEval(false) + , hasNestedFunctions(false) {} - int findMember(const QStringRef &name) const + int findMember(const QString &name) const { return members.value(name, -1); } - bool lookupMember(const QStringRef &name, Environment **scope, int *index, int *distance) + bool lookupMember(const QString &name, Environment **scope, int *index, int *distance) { Environment *it = this; *distance = 0; @@ -82,36 +87,25 @@ protected: return false; } - void enter(const QStringRef &name) + void enter(const QString &name) { - int idx = members.value(name, -1); - if (idx == -1) - members.insert(name, count++); + if (! name.isEmpty()) { + int idx = members.value(name, -1); + if (idx == -1) { + members.insert(name, vars.count()); + vars.append(name); + } + } } }; - Environment *newEnvironment() - { - Environment *scope = new Environment(_env); - return scope; - } - - Environment *changeEnvironment(Environment *env) + Environment *newEnvironment(AST::Node *node, Environment *parent) { - qSwap(_env, env); + Environment *env = new Environment(parent); + _envMap.insert(node, env); return env; } - struct Scope { - Codegen *cg; - Environment *previous; - inline Scope(Codegen *cg, Environment *env): cg(cg) { previous = cg->changeEnvironment(env); } - inline ~Scope() { cg->changeEnvironment(previous); } - - private: - Q_DISABLE_COPY(Scope) - }; - struct UiMember { }; @@ -126,6 +120,9 @@ protected: : breakBlock(breakBlock), continueBlock(continueBlock) {} }; + void enterEnvironment(AST::Node *node); + void leaveEnvironment(); + IR::Expr *member(IR::Expr *base, const QString *name); IR::Expr *subscript(IR::Expr *base, IR::Expr *index); IR::Expr *argument(IR::Expr *expr); @@ -134,7 +131,8 @@ protected: void move(IR::Expr *target, IR::Expr *source, IR::AluOp op = IR::OpInvalid); void linearize(IR::Function *function); - void defineFunction(AST::FunctionExpression *ast, bool isDeclaration = false); + IR::Function *defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, + AST::SourceElements *body, bool isDeclaration = false); int indexOfLocal(const QStringRef &string) const; int indexOfArgument(const QStringRef &string) const; @@ -284,10 +282,10 @@ private: IR::BasicBlock *_block; IR::BasicBlock *_exitBlock; unsigned _returnAddress; - QVector _allEnvironments; Environment *_env; + QHash _envMap; - struct ScanFunctionBody; + class ScanFunctions; }; } // end of namespace QQmlJS -- cgit v1.2.3 From 086e775da61b6a8464e75199edfea2a6e8ed7acc Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 18 May 2012 10:10:36 +0200 Subject: Some refactoring and some initial work on the Date object. --- main.cpp | 89 +++--- qmljs_objects.cpp | 24 ++ qmljs_objects.h | 6 + qmljs_runtime.h | 20 +- qv4codegen.cpp | 43 ++- qv4codegen_p.h | 2 + qv4ecmaobjects.cpp | 884 +++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4ecmaobjects_p.h | 67 ++++ qv4isel.cpp | 37 ++- 9 files changed, 1114 insertions(+), 58 deletions(-) diff --git a/main.cpp b/main.cpp index 9026a96f07..44cd18dc1e 100644 --- a/main.cpp +++ b/main.cpp @@ -47,78 +47,81 @@ struct Print: FunctionObject } // builtins -void evaluate(QQmlJS::VM::ExecutionEngine *vm, QQmlJS::Engine *engine, const QString &fileName, const QString &code) +void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QString &source) { using namespace QQmlJS; - Lexer lexer(engine); - lexer.setCode(code, 1, false); - Parser parser(engine); + IR::Module module; + IR::Function *globalCode = 0; - const bool parsed = parser.parseProgram(); + const size_t codeSize = 10 * getpagesize(); + uchar *code = (uchar *) malloc(codeSize); + assert(! (size_t(code) & 15)); - foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { - std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn - << ": error: " << qPrintable(m.message) << std::endl; - } + { + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(source, 1, false); + Parser parser(engine); - if (parsed) { - using namespace AST; - Program *program = AST::cast(parser.rootNode()); + const bool parsed = parser.parseProgram(); - Codegen cg; - IR::Module module; - IR::Function *globalCode = cg(program, &module); + foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { + std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn + << ": error: " << qPrintable(m.message) << std::endl; + } - const size_t codeSize = 10 * getpagesize(); - uchar *code = (uchar *) malloc(codeSize); + if (parsed) { + using namespace AST; + Program *program = AST::cast(parser.rootNode()); - x86_64::InstructionSelection isel(vm, &module, code); - foreach (IR::Function *function, module.functions) { - isel(function); + Codegen cg; + globalCode = cg(program, &module); + + x86_64::InstructionSelection isel(vm, &module, code); + foreach (IR::Function *function, module.functions) { + isel(function); + } } + } - if (! protect(code, codeSize)) - Q_UNREACHABLE(); + if (! protect(code, codeSize)) + Q_UNREACHABLE(); - VM::Object *globalObject = vm->globalObject.objectValue; - VM::Context *ctx = vm->rootContext; + VM::Object *globalObject = vm->globalObject.objectValue; + VM::Context *ctx = vm->rootContext; - globalObject->put(vm->identifier(QLatin1String("print")), - VM::Value::fromObject(new builtins::Print(ctx))); + globalObject->put(vm->identifier(QLatin1String("print")), + VM::Value::fromObject(new builtins::Print(ctx))); - ctx->varCount = globalCode->locals.size(); - if (ctx->varCount) { - ctx->locals = new VM::Value[ctx->varCount]; - ctx->vars = new VM::String*[ctx->varCount]; - std::fill(ctx->locals, ctx->locals + ctx->varCount, VM::Value::undefinedValue()); - for (size_t i = 0; i < ctx->varCount; ++i) - ctx->vars[i] = ctx->engine->identifier(*globalCode->locals.at(i)); - } + ctx->varCount = globalCode->locals.size(); + if (ctx->varCount) { + ctx->locals = new VM::Value[ctx->varCount]; + ctx->vars = new VM::String*[ctx->varCount]; + std::fill(ctx->locals, ctx->locals + ctx->varCount, VM::Value::undefinedValue()); + for (size_t i = 0; i < ctx->varCount; ++i) + ctx->vars[i] = ctx->engine->identifier(*globalCode->locals.at(i)); + } - globalCode->code(ctx); + globalCode->code(ctx); - delete[] ctx->locals; - delete[] ctx->vars; - } + delete[] ctx->locals; + delete[] ctx->vars; } int main(int argc, char *argv[]) { - using namespace QQmlJS; - QCoreApplication app(argc, argv); QStringList args = app.arguments(); args.removeFirst(); - VM::ExecutionEngine vm; - Engine engine; + QQmlJS::VM::ExecutionEngine vm; foreach (const QString &fn, args) { QFile file(fn); if (file.open(QFile::ReadOnly)) { const QString code = QString::fromUtf8(file.readAll()); file.close(); - evaluate(&vm, &engine, fn, code); + evaluate(&vm, fn, code); } } } diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 44d8430eb2..0fdf99bc24 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -2,11 +2,15 @@ #include "qmljs_objects.h" #include "qv4ir_p.h" #include "qv4ecmaobjects_p.h" +#include #include #include using namespace QQmlJS::VM; +// +// Object +// Object::~Object() { delete members; @@ -207,16 +211,19 @@ ExecutionEngine::ExecutionEngine() objectCtor = ObjectCtor::create(this); stringCtor = StringCtor::create(this); numberCtor = NumberCtor::create(this); + dateCtor = DateCtor::create(this); String *prototype = identifier(QLatin1String("prototype")); objectCtor.objectValue->get(prototype, &objectPrototype); stringCtor.objectValue->get(prototype, &stringPrototype); numberCtor.objectValue->get(prototype, &numberPrototype); + dateCtor.objectValue->get(prototype, &datePrototype); glo->put(identifier(QLatin1String("Object")), objectCtor); glo->put(identifier(QLatin1String("String")), stringCtor); glo->put(identifier(QLatin1String("Number")), numberCtor); + glo->put(identifier(QLatin1String("Date")), dateCtor); glo->put(identifier(QLatin1String("Math")), Value::fromObject(newMathObject(rootContext))); } @@ -314,6 +321,23 @@ Object *ExecutionEngine::newBooleanPrototype(Context *ctx, FunctionObject *proto return booleanProto; } +Object *ExecutionEngine::newDateObject(const Value &value) +{ + return new Object(); // ### FIXME +} + +FunctionObject *ExecutionEngine::newDateCtor(Context *ctx) +{ + return new DateCtor(ctx); +} + +Object *ExecutionEngine::newDatePrototype(Context *ctx, FunctionObject *proto) +{ + Object *dateProto = new DatePrototype(ctx, proto); + dateProto->prototype = objectPrototype.objectValue; + return dateProto; +} + Object *ExecutionEngine::newErrorObject(const Value &value) { return new ErrorObject(value); diff --git a/qmljs_objects.h b/qmljs_objects.h index 08f9bcbcfc..b2b2ea3738 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -360,10 +360,12 @@ struct ExecutionEngine Value objectCtor; Value stringCtor; Value numberCtor; + Value dateCtor; Value objectPrototype; Value stringPrototype; Value numberPrototype; + Value datePrototype; QHash identifiers; @@ -393,6 +395,10 @@ struct ExecutionEngine FunctionObject *newBooleanCtor(Context *ctx); Object *newBooleanPrototype(Context *ctx, FunctionObject *proto); + Object *newDateObject(const Value &value); + FunctionObject *newDateCtor(Context *ctx); + Object *newDatePrototype(Context *ctx, FunctionObject *proto); + Object *newErrorObject(const Value &value); Object *newMathObject(Context *ctx); Object *newArgumentsObject(Context *ctx); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 467e30215c..223a219753 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -597,18 +597,26 @@ inline void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const V inline void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Value *right) { - __qmljs_compare(ctx, result, left, right, false); + if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) { + __qmljs_init_boolean(result, left->numberValue > right->numberValue); + } else { + __qmljs_compare(ctx, result, left, right, false); - if (result->type == UNDEFINED_TYPE) - __qmljs_init_boolean(result, false); + if (result->type == UNDEFINED_TYPE) + __qmljs_init_boolean(result, false); + } } inline void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Value *right) { - __qmljs_compare(ctx, result, left, right, true); + if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) { + __qmljs_init_boolean(result, left->numberValue < right->numberValue); + } else { + __qmljs_compare(ctx, result, left, right, true); - if (result->type == UNDEFINED_TYPE) - __qmljs_init_boolean(result, false); + if (result->type == UNDEFINED_TYPE) + __qmljs_init_boolean(result, false); + } } inline void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Value *right) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index a5f2b3d537..9226749cc6 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -341,6 +341,16 @@ IR::Expr *Codegen::argument(IR::Expr *expr) return expr; } +IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr) +{ + if (! expr->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + return _block->UNOP(op, expr); +} + IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) { if (left && ! left->asTemp()) { @@ -369,7 +379,14 @@ IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args) void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) { - if (target->asMember()) { + if (op != IR::OpInvalid) { + if (! (source->asTemp() || source->asConst())) { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), source); + _block->MOVE(target, _block->TEMP(t), op); + return; + } + } else if (target->asMember() || target->asSubscript()) { if (! (source->asTemp() || source->asConst())) { const unsigned t = _block->newTemp(); _block->MOVE(_block->TEMP(t), source); @@ -387,6 +404,16 @@ void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) _block->MOVE(target, source, op); } +void Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) +{ + if (! cond->asTemp()) { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), cond); + cond = _block->TEMP(t); + } + _block->CJUMP(cond, iftrue, iffalse); +} + void Codegen::accept(Node *node) { if (node) @@ -419,7 +446,7 @@ void Codegen::condition(ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBl accept(ast); qSwap(_expr, r); if (r.format == ex) { - _block->CJUMP(*r, r.iftrue, r.iffalse); + cjump(*r, r.iftrue, r.iffalse); } } } @@ -851,7 +878,7 @@ bool Codegen::visit(BinaryExpression *ast) const unsigned r = _block->newTemp(); move(_block->TEMP(r), *expression(ast->left)); - _block->CJUMP(_block->TEMP(r), endif, iffalse); + cjump(_block->TEMP(r), endif, iffalse); _block = iffalse; move(_block->TEMP(r), *expression(ast->right)); if (! _block->isTerminated()) @@ -913,7 +940,7 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::StrictEqual: case QSOperator::StrictNotEqual: if (false && _expr.accept(cx)) { - _block->CJUMP(binop(IR::binaryOperator(ast->op), *left, *right), _expr.iftrue, _expr.iffalse); + cjump(binop(IR::binaryOperator(ast->op), *left, *right), _expr.iftrue, _expr.iffalse); } else { IR::Expr *e = binop(IR::binaryOperator(ast->op), *left, *right); if (e->asConst() || e->asString()) @@ -1081,7 +1108,7 @@ bool Codegen::visit(NotExpression *ast) { Result expr = expression(ast->expression); const unsigned r = _block->newTemp(); - move(_block->TEMP(r), _block->UNOP(IR::OpNot, *expr)); + move(_block->TEMP(r), unop(IR::OpNot, *expr)); _expr.code = _block->TEMP(r); return false; } @@ -1186,7 +1213,7 @@ bool Codegen::visit(TildeExpression *ast) { Result expr = expression(ast->expression); const unsigned t = _block->newTemp(); - move(_block->TEMP(t), _block->UNOP(IR::OpCompl, *expr)); + move(_block->TEMP(t), unop(IR::OpCompl, *expr)); _expr.code = _block->TEMP(t); return false; } @@ -1210,7 +1237,7 @@ bool Codegen::visit(UnaryMinusExpression *ast) { Result expr = expression(ast->expression); const unsigned t = _block->newTemp(); - move(_block->TEMP(t), _block->UNOP(IR::OpUMinus, *expr)); + move(_block->TEMP(t), unop(IR::OpUMinus, *expr)); _expr.code = _block->TEMP(t); return false; } @@ -1219,7 +1246,7 @@ bool Codegen::visit(UnaryPlusExpression *ast) { Result expr = expression(ast->expression); const unsigned t = _block->newTemp(); - move(_block->TEMP(t), _block->UNOP(IR::OpUPlus, *expr)); + move(_block->TEMP(t), unop(IR::OpUPlus, *expr)); _expr.code = _block->TEMP(t); return false; } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index f8780016dd..b93dac1068 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -126,9 +126,11 @@ protected: IR::Expr *member(IR::Expr *base, const QString *name); IR::Expr *subscript(IR::Expr *base, IR::Expr *index); IR::Expr *argument(IR::Expr *expr); + IR::Expr *unop(IR::AluOp op, IR::Expr *expr); IR::Expr *binop(IR::AluOp op, IR::Expr *left, IR::Expr *right); IR::Expr *call(IR::Expr *base, IR::ExprList *args); void move(IR::Expr *target, IR::Expr *source, IR::AluOp op = IR::OpInvalid); + void cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse); void linearize(IR::Function *function); IR::Function *defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 506e866d72..46305021ca 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1,13 +1,474 @@ #include "qv4ecmaobjects_p.h" +#include +#include +#include +#include +#include +#include #include #include #include +#ifndef Q_WS_WIN +# include +# ifndef Q_OS_VXWORKS +# include +# else +# include "qplatformdefs.h" +# endif +#else +# include +#endif + using namespace QQmlJS::VM; static const double qt_PI = 2.0 * ::asin(1.0); +static const double HoursPerDay = 24.0; +static const double MinutesPerHour = 60.0; +static const double SecondsPerMinute = 60.0; +static const double msPerSecond = 1000.0; +static const double msPerMinute = 60000.0; +static const double msPerHour = 3600000.0; +static const double msPerDay = 86400000.0; + +static double LocalTZA = 0.0; // initialized at startup + +static inline double TimeWithinDay(double t) +{ + double r = ::fmod(t, msPerDay); + return (r >= 0) ? r : r + msPerDay; +} + +static inline int HourFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerHour), HoursPerDay)); + return (r >= 0) ? r : r + int(HoursPerDay); +} + +static inline int MinFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerMinute), MinutesPerHour)); + return (r >= 0) ? r : r + int(MinutesPerHour); +} + +static inline int SecFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerSecond), SecondsPerMinute)); + return (r >= 0) ? r : r + int(SecondsPerMinute); +} + +static inline int msFromTime(double t) +{ + int r = int(::fmod(t, msPerSecond)); + return (r >= 0) ? r : r + int(msPerSecond); +} + +static inline double Day(double t) +{ + return ::floor(t / msPerDay); +} + +static inline double DaysInYear(double y) +{ + if (::fmod(y, 4)) + return 365; + + else if (::fmod(y, 100)) + return 366; + + else if (::fmod(y, 400)) + return 365; + + return 366; +} + +static inline double DayFromYear(double y) +{ + return 365 * (y - 1970) + + ::floor((y - 1969) / 4) + - ::floor((y - 1901) / 100) + + ::floor((y - 1601) / 400); +} + +static inline double TimeFromYear(double y) +{ + return msPerDay * DayFromYear(y); +} + +static inline double YearFromTime(double t) +{ + int y = 1970; + y += (int) ::floor(t / (msPerDay * 365.2425)); + + double t2 = TimeFromYear(y); + return (t2 > t) ? y - 1 : ((t2 + msPerDay * DaysInYear(y)) <= t) ? y + 1 : y; +} + +static inline bool InLeapYear(double t) +{ + double x = DaysInYear(YearFromTime(t)); + if (x == 365) + return 0; + + Q_ASSERT (x == 366); + return 1; +} + +static inline double DayWithinYear(double t) +{ + return Day(t) - DayFromYear(YearFromTime(t)); +} + +static inline double MonthFromTime(double t) +{ + double d = DayWithinYear(t); + double l = InLeapYear(t); + + if (d < 31.0) + return 0; + + else if (d < 59.0 + l) + return 1; + + else if (d < 90.0 + l) + return 2; + + else if (d < 120.0 + l) + return 3; + + else if (d < 151.0 + l) + return 4; + + else if (d < 181.0 + l) + return 5; + + else if (d < 212.0 + l) + return 6; + + else if (d < 243.0 + l) + return 7; + + else if (d < 273.0 + l) + return 8; + + else if (d < 304.0 + l) + return 9; + + else if (d < 334.0 + l) + return 10; + + else if (d < 365.0 + l) + return 11; + + return qSNaN(); // ### assert? +} + +static inline double DateFromTime(double t) +{ + int m = (int) Value::toInteger(MonthFromTime(t)); + double d = DayWithinYear(t); + double l = InLeapYear(t); + + switch (m) { + case 0: return d + 1.0; + case 1: return d - 30.0; + case 2: return d - 58.0 - l; + case 3: return d - 89.0 - l; + case 4: return d - 119.0 - l; + case 5: return d - 150.0 - l; + case 6: return d - 180.0 - l; + case 7: return d - 211.0 - l; + case 8: return d - 242.0 - l; + case 9: return d - 272.0 - l; + case 10: return d - 303.0 - l; + case 11: return d - 333.0 - l; + } + + return qSNaN(); // ### assert +} + +static inline double WeekDay(double t) +{ + double r = ::fmod (Day(t) + 4.0, 7.0); + return (r >= 0) ? r : r + 7.0; +} + + +static inline double MakeTime(double hour, double min, double sec, double ms) +{ + return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms; +} + +static inline double DayFromMonth(double month, double leap) +{ + switch ((int) month) { + case 0: return 0; + case 1: return 31.0; + case 2: return 59.0 + leap; + case 3: return 90.0 + leap; + case 4: return 120.0 + leap; + case 5: return 151.0 + leap; + case 6: return 181.0 + leap; + case 7: return 212.0 + leap; + case 8: return 243.0 + leap; + case 9: return 273.0 + leap; + case 10: return 304.0 + leap; + case 11: return 334.0 + leap; + } + + return qSNaN(); // ### assert? +} + +static double MakeDay(double year, double month, double day) +{ + year += ::floor(month / 12.0); + + month = ::fmod(month, 12.0); + if (month < 0) + month += 12.0; + + double t = TimeFromYear(year); + double leap = InLeapYear(t); + + day += ::floor(t / msPerDay); + day += DayFromMonth(month, leap); + + return day - 1; +} + +static inline double MakeDate(double day, double time) +{ + return day * msPerDay + time; +} + +static inline double DaylightSavingTA(double t) +{ +#ifndef Q_WS_WIN + long int tt = (long int)(t / msPerSecond); + struct tm *tmtm = localtime((const time_t*)&tt); + if (! tmtm) + return 0; + return (tmtm->tm_isdst > 0) ? msPerHour : 0; +#else + Q_UNUSED(t); + /// ### implement me + return 0; +#endif +} + +static inline double LocalTime(double t) +{ + return t + LocalTZA + DaylightSavingTA(t); +} + +static inline double UTC(double t) +{ + return t - LocalTZA - DaylightSavingTA(t - LocalTZA); +} + +static inline double currentTime() +{ +#ifndef Q_WS_WIN + struct timeval tv; + + gettimeofday(&tv, 0); + return ::floor(tv.tv_sec * msPerSecond + (tv.tv_usec / 1000.0)); +#else + SYSTEMTIME st; + GetSystemTime(&st); + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + LARGE_INTEGER li; + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + return double(li.QuadPart - Q_INT64_C(116444736000000000)) / 10000.0; +#endif +} + +static inline double TimeClip(double t) +{ + if (! qIsFinite(t) || fabs(t) > 8.64e15) + return qSNaN(); + return Value::toInteger(t); +} + +static inline double FromDateTime(const QDateTime &dt) +{ + if (!dt.isValid()) + return qSNaN(); + QDate date = dt.date(); + QTime taim = dt.time(); + int year = date.year(); + int month = date.month() - 1; + int day = date.day(); + int hours = taim.hour(); + int mins = taim.minute(); + int secs = taim.second(); + int ms = taim.msec(); + double t = MakeDate(MakeDay(year, month, day), + MakeTime(hours, mins, secs, ms)); + if (dt.timeSpec() == Qt::LocalTime) + t = UTC(t); + return TimeClip(t); +} + +static inline double ParseString(const QString &s) +{ + QDateTime dt = QDateTime::fromString(s, Qt::TextDate); + if (!dt.isValid()) + dt = QDateTime::fromString(s, Qt::ISODate); + if (!dt.isValid()) { + QStringList formats; + formats << QLatin1String("M/d/yyyy") + << QLatin1String("M/d/yyyy hh:mm") + << QLatin1String("M/d/yyyy hh:mm A") + + << QLatin1String("M/d/yyyy, hh:mm") + << QLatin1String("M/d/yyyy, hh:mm A") + + << QLatin1String("MMM d yyyy") + << QLatin1String("MMM d yyyy hh:mm") + << QLatin1String("MMM d yyyy hh:mm:ss") + << QLatin1String("MMM d yyyy, hh:mm") + << QLatin1String("MMM d yyyy, hh:mm:ss") + + << QLatin1String("MMMM d yyyy") + << QLatin1String("MMMM d yyyy hh:mm") + << QLatin1String("MMMM d yyyy hh:mm:ss") + << QLatin1String("MMMM d yyyy, hh:mm") + << QLatin1String("MMMM d yyyy, hh:mm:ss") + + << QLatin1String("MMM d, yyyy") + << QLatin1String("MMM d, yyyy hh:mm") + << QLatin1String("MMM d, yyyy hh:mm:ss") + + << QLatin1String("MMMM d, yyyy") + << QLatin1String("MMMM d, yyyy hh:mm") + << QLatin1String("MMMM d, yyyy hh:mm:ss") + + << QLatin1String("d MMM yyyy") + << QLatin1String("d MMM yyyy hh:mm") + << QLatin1String("d MMM yyyy hh:mm:ss") + << QLatin1String("d MMM yyyy, hh:mm") + << QLatin1String("d MMM yyyy, hh:mm:ss") + + << QLatin1String("d MMMM yyyy") + << QLatin1String("d MMMM yyyy hh:mm") + << QLatin1String("d MMMM yyyy hh:mm:ss") + << QLatin1String("d MMMM yyyy, hh:mm") + << QLatin1String("d MMMM yyyy, hh:mm:ss") + + << QLatin1String("d MMM, yyyy") + << QLatin1String("d MMM, yyyy hh:mm") + << QLatin1String("d MMM, yyyy hh:mm:ss") + + << QLatin1String("d MMMM, yyyy") + << QLatin1String("d MMMM, yyyy hh:mm") + << QLatin1String("d MMMM, yyyy hh:mm:ss"); + + for (int i = 0; i < formats.size(); ++i) { + dt = QDateTime::fromString(s, formats.at(i)); + if (dt.isValid()) + break; + } + } + return FromDateTime(dt); +} + +/*! + \internal + + Converts the ECMA Date value \tt (in UTC form) to QDateTime + according to \a spec. +*/ +static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) +{ + if (qIsNaN(t)) + return QDateTime(); + if (spec == Qt::LocalTime) + t = LocalTime(t); + int year = int(YearFromTime(t)); + int month = int(MonthFromTime(t) + 1); + int day = int(DateFromTime(t)); + int hours = HourFromTime(t); + int mins = MinFromTime(t); + int secs = SecFromTime(t); + int ms = msFromTime(t); + return QDateTime(QDate(year, month, day), QTime(hours, mins, secs, ms), spec); +} + +static inline QString ToString(double t) +{ + if (qIsNaN(t)) + return QLatin1String("Invalid Date"); + QString str = ToDateTime(t, Qt::LocalTime).toString() + QLatin1String(" GMT"); + double tzoffset = LocalTZA + DaylightSavingTA(t); + if (tzoffset) { + int hours = static_cast(::fabs(tzoffset) / 1000 / 60 / 60); + int mins = int(::fabs(tzoffset) / 1000 / 60) % 60; + str.append(QLatin1Char((tzoffset > 0) ? '+' : '-')); + if (hours < 10) + str.append(QLatin1Char('0')); + str.append(QString::number(hours)); + if (mins < 10) + str.append(QLatin1Char('0')); + str.append(QString::number(mins)); + } + return str; +} + +static inline QString ToUTCString(double t) +{ + if (qIsNaN(t)) + return QLatin1String("Invalid Date"); + return ToDateTime(t, Qt::UTC).toString() + QLatin1String(" GMT"); +} + +static inline QString ToDateString(double t) +{ + return ToDateTime(t, Qt::LocalTime).date().toString(); +} + +static inline QString ToTimeString(double t) +{ + return ToDateTime(t, Qt::LocalTime).time().toString(); +} + +static inline QString ToLocaleString(double t) +{ + return ToDateTime(t, Qt::LocalTime).toString(Qt::LocaleDate); +} + +static inline QString ToLocaleDateString(double t) +{ + return ToDateTime(t, Qt::LocalTime).date().toString(Qt::LocaleDate); +} + +static inline QString ToLocaleTimeString(double t) +{ + return ToDateTime(t, Qt::LocalTime).time().toString(Qt::LocaleDate); +} + +static double getLocalTZA() +{ +#ifndef Q_WS_WIN + struct tm* t; + time_t curr; + time(&curr); + t = localtime(&curr); + time_t locl = mktime(t); + t = gmtime(&curr); + time_t globl = mktime(t); + return double(locl - globl) * 1000.0; +#else + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + return -tzInfo.Bias * 60.0 * 1000.0; +#endif +} + // // Object // @@ -631,6 +1092,429 @@ void BooleanPrototype::method_valueOf(Context *ctx) ctx->result = internalValue; } +// +// Date object +// +Value DateCtor::create(ExecutionEngine *engine) +{ + Context *ctx = engine->rootContext; + FunctionObject *ctor = ctx->engine->newDateCtor(ctx); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newNumberPrototype(ctx, ctor))); + return Value::fromObject(ctor); +} + +DateCtor::DateCtor(Context *scope) + : FunctionObject(scope) +{ +} + +void DateCtor::construct(Context *ctx) +{ + // called as constructor + double t; + + if (ctx->argumentCount == 0) + t = currentTime(); + + else if (ctx->argumentCount == 1) { + Value arg = ctx->argument(0); +// if (arg.isDate()) +// arg = arg.internalValue(); +// else +// __qmljs_to_primitive(ctx, &arg, &arg, PREFERREDTYPE_HINT); + + if (arg.isString()) + t = ParseString(arg.toString(ctx)->toQString()); + else + t = TimeClip(arg.toNumber(ctx)); + } + + else { // context->argumentCount() > 1 + double year = ctx->argument(0).toNumber(ctx); + double month = ctx->argument(1).toNumber(ctx); + double day = ctx->argumentCount >= 3 ? ctx->argument(2).toNumber(ctx) : 1; + double hours = ctx->argumentCount >= 4 ? ctx->argument(3).toNumber(ctx) : 0; + double mins = ctx->argumentCount >= 5 ? ctx->argument(4).toNumber(ctx) : 0; + double secs = ctx->argumentCount >= 6 ? ctx->argument(5).toNumber(ctx) : 0; + double ms = ctx->argumentCount >= 7 ? ctx->argument(6).toNumber(ctx) : 0; + if (year >= 0 && year <= 99) + year += 1900; + t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); + t = TimeClip(UTC(t)); + } + +// Value &obj = ctx->m_thisObject; +// obj.setClassInfo(classInfo()); +// obj.setInternalValue(Value(t)); +// obj.setPrototype(publicPrototype); +// ctx->setReturnValue(obj); +} + +void DateCtor::call(Context *ctx) +{ + double t = currentTime(); + ctx->result = Value::fromString(ctx, ToString(t)); +} + +DatePrototype::DatePrototype(Context *ctx, FunctionObject *ctor) +{ + LocalTZA = getLocalTZA(); + + ctor->setProperty(ctx, QLatin1String("parse"), method_parse, 1); + ctor->setProperty(ctx, QLatin1String("UTC"), method_UTC, 7); + + setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); + setProperty(ctx, QLatin1String("toString"), method_toString, 0); + setProperty(ctx, QLatin1String("toDateString"), method_toDateString, 0); + setProperty(ctx, QLatin1String("toTimeString"), method_toTimeString, 0); + setProperty(ctx, QLatin1String("toLocaleString"), method_toLocaleString, 0); + setProperty(ctx, QLatin1String("toLocaleDateString"), method_toLocaleDateString, 0); + setProperty(ctx, QLatin1String("toLocaleTimeString"), method_toLocaleTimeString, 0); + setProperty(ctx, QLatin1String("valueOf"), method_valueOf, 0); + setProperty(ctx, QLatin1String("getTime"), method_getTime, 0); + setProperty(ctx, QLatin1String("getYear"), method_getYear, 0); + setProperty(ctx, QLatin1String("getFullYear"), method_getFullYear, 0); + setProperty(ctx, QLatin1String("getUTCFullYear"), method_getUTCFullYear, 0); + setProperty(ctx, QLatin1String("getMonth"), method_getMonth, 0); + setProperty(ctx, QLatin1String("getUTCMonth"), method_getUTCMonth, 0); + setProperty(ctx, QLatin1String("getDate"), method_getDate, 0); + setProperty(ctx, QLatin1String("getUTCDate"), method_getUTCDate, 0); + setProperty(ctx, QLatin1String("getDay"), method_getDay, 0); + setProperty(ctx, QLatin1String("getUTCDay"), method_getUTCDay, 0); + setProperty(ctx, QLatin1String("getHours"), method_getHours, 0); + setProperty(ctx, QLatin1String("getUTCHours"), method_getUTCHours, 0); + setProperty(ctx, QLatin1String("getMinutes"), method_getMinutes, 0); + setProperty(ctx, QLatin1String("getUTCMinutes"), method_getUTCMinutes, 0); + setProperty(ctx, QLatin1String("getSeconds"), method_getSeconds, 0); + setProperty(ctx, QLatin1String("getUTCSeconds"), method_getUTCSeconds, 0); + setProperty(ctx, QLatin1String("getMilliseconds"), method_getMilliseconds, 0); + setProperty(ctx, QLatin1String("getUTCMilliseconds"), method_getUTCMilliseconds, 0); + setProperty(ctx, QLatin1String("getTimezoneOffset"), method_getTimezoneOffset, 0); + setProperty(ctx, QLatin1String("setTime"), method_setTime, 1); + setProperty(ctx, QLatin1String("setMilliseconds"), method_setMilliseconds, 1); + setProperty(ctx, QLatin1String("setUTCMilliseconds"), method_setUTCMilliseconds, 1); + setProperty(ctx, QLatin1String("setSeconds"), method_setSeconds, 2); + setProperty(ctx, QLatin1String("setUTCSeconds"), method_setUTCSeconds, 2); + setProperty(ctx, QLatin1String("setMinutes"), method_setMinutes, 3); + setProperty(ctx, QLatin1String("setUTCMinutes"), method_setUTCMinutes, 3); + setProperty(ctx, QLatin1String("setHours"), method_setHours, 4); + setProperty(ctx, QLatin1String("setUTCHours"), method_setUTCHours, 4); + setProperty(ctx, QLatin1String("setDate"), method_setDate, 1); + setProperty(ctx, QLatin1String("setUTCDate"), method_setUTCDate, 1); + setProperty(ctx, QLatin1String("setMonth"), method_setMonth, 2); + setProperty(ctx, QLatin1String("setUTCMonth"), method_setUTCMonth, 2); + setProperty(ctx, QLatin1String("setYear"), method_setYear, 1); + setProperty(ctx, QLatin1String("setFullYear"), method_setFullYear, 3); + setProperty(ctx, QLatin1String("setUTCFullYear"), method_setUTCFullYear, 3); + setProperty(ctx, QLatin1String("toUTCString"), method_toUTCString, 0); + setProperty(ctx, QLatin1String("toGMTString"), method_toUTCString, 0); +} + +double DatePrototype::getThisDate(Context *ctx) +{ + assert(ctx->thisObject.isObject()); + Value internalValue; + ctx->thisObject.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); + assert(internalValue.isNumber()); + return internalValue.numberValue; +} + +void DatePrototype::method_MakeTime(Context *ctx) +{ +} + +void DatePrototype::method_MakeDate(Context *ctx) +{ +} + +void DatePrototype::method_TimeClip(Context *ctx) +{ +} + +void DatePrototype::method_parse(Context *ctx) +{ + ctx->result = Value::fromNumber(ParseString(ctx->argument(0).toString(ctx)->toQString())); +} + +void DatePrototype::method_UTC(Context *ctx) +{ + const int numArgs = ctx->argumentCount; + if (numArgs >= 2) { + double year = ctx->argument(0).toNumber(ctx); + double month = ctx->argument(1).toNumber(ctx); + double day = numArgs >= 3 ? ctx->argument(2).toNumber(ctx) : 1; + double hours = numArgs >= 4 ? ctx->argument(3).toNumber(ctx) : 0; + double mins = numArgs >= 5 ? ctx->argument(4).toNumber(ctx) : 0; + double secs = numArgs >= 6 ? ctx->argument(5).toNumber(ctx) : 0; + double ms = numArgs >= 7 ? ctx->argument(6).toNumber(ctx) : 0; + if (year >= 0 && year <= 99) + year += 1900; + double t = MakeDate(MakeDay(year, month, day), + MakeTime(hours, mins, secs, ms)); + ctx->result = Value::fromNumber(TimeClip(t)); + } +} + +void DatePrototype::method_toString(Context *ctx) +{ + double t = getThisDate(ctx); + ctx->result = Value::fromString(ctx, ToString(t)); +} + +void DatePrototype::method_toDateString(Context *ctx) +{ + double t = getThisDate(ctx); + ctx->result = Value::fromString(ctx, ToDateString(t)); +} + +void DatePrototype::method_toTimeString(Context *ctx) +{ + double t = getThisDate(ctx); + ctx->result = Value::fromString(ctx, ToTimeString(t)); +} + +void DatePrototype::method_toLocaleString(Context *ctx) +{ + double t = getThisDate(ctx); + ctx->result = Value::fromString(ctx, ToLocaleString(t)); +} + +void DatePrototype::method_toLocaleDateString(Context *ctx) +{ + double t = getThisDate(ctx); + ctx->result = Value::fromString(ctx, ToLocaleDateString(t)); +} + +void DatePrototype::method_toLocaleTimeString(Context *ctx) +{ + double t = getThisDate(ctx); + ctx->result = Value::fromString(ctx, ToLocaleTimeString(t)); +} + +void DatePrototype::method_valueOf(Context *ctx) +{ + double t = getThisDate(ctx); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getTime(Context *ctx) +{ + double t = getThisDate(ctx); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getYear(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = YearFromTime(LocalTime(t)) - 1900; + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getFullYear(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = YearFromTime(LocalTime(t)); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getUTCFullYear(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = YearFromTime(t); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getMonth(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = MonthFromTime(LocalTime(t)); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getUTCMonth(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = MonthFromTime(t); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getDate(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = DateFromTime(LocalTime(t)); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getUTCDate(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = DateFromTime(t); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getDay(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = WeekDay(LocalTime(t)); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getUTCDay(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = WeekDay(t); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getHours(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = HourFromTime(LocalTime(t)); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getUTCHours(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = HourFromTime(t); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getMinutes(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = MinFromTime(LocalTime(t)); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getUTCMinutes(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = MinFromTime(t); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getSeconds(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = SecFromTime(LocalTime(t)); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getUTCSeconds(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = SecFromTime(t); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getMilliseconds(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = msFromTime(LocalTime(t)); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getUTCMilliseconds(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = msFromTime(t); + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_getTimezoneOffset(Context *ctx) +{ + double t = getThisDate(ctx); + if (! qIsNaN(t)) + t = (t - LocalTime(t)) / msPerMinute; + ctx->result = Value::fromNumber(t); +} + +void DatePrototype::method_setTime(Context *ctx) +{ +} + +void DatePrototype::method_setMilliseconds(Context *ctx) +{ +} + +void DatePrototype::method_setUTCMilliseconds(Context *ctx) +{ +} + +void DatePrototype::method_setSeconds(Context *ctx) +{ +} + +void DatePrototype::method_setUTCSeconds(Context *ctx) +{ +} + +void DatePrototype::method_setMinutes(Context *ctx) +{ +} + +void DatePrototype::method_setUTCMinutes(Context *ctx) +{ +} + +void DatePrototype::method_setHours(Context *ctx) +{ +} + +void DatePrototype::method_setUTCHours(Context *ctx) +{ +} + +void DatePrototype::method_setDate(Context *ctx) +{ +} + +void DatePrototype::method_setUTCDate(Context *ctx) +{ +} + +void DatePrototype::method_setMonth(Context *ctx) +{ +} + +void DatePrototype::method_setUTCMonth(Context *ctx) +{ +} + +void DatePrototype::method_setYear(Context *ctx) +{ +} + +void DatePrototype::method_setFullYear(Context *ctx) +{ +} + +void DatePrototype::method_setUTCFullYear(Context *ctx) +{ +} + +void DatePrototype::method_toUTCString(Context *ctx) +{ +} + // // Math object // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index d37aefc23f..68c15222cc 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -102,6 +102,73 @@ protected: static void method_valueOf(Context *ctx); }; +struct DateCtor: FunctionObject +{ + static Value create(ExecutionEngine *engine); + + DateCtor(Context *scope); + + virtual void construct(Context *ctx); + virtual void call(Context *ctx); +}; + +struct DatePrototype: Object +{ + DatePrototype(Context *ctx, FunctionObject *ctor); + +protected: + static double getThisDate(Context *ctx); + + static void method_MakeTime(Context *ctx); + static void method_MakeDate(Context *ctx); + static void method_TimeClip(Context *ctx); + static void method_parse(Context *ctx); + static void method_UTC(Context *ctx); + static void method_toString(Context *ctx); + static void method_toDateString(Context *ctx); + static void method_toTimeString(Context *ctx); + static void method_toLocaleString(Context *ctx); + static void method_toLocaleDateString(Context *ctx); + static void method_toLocaleTimeString(Context *ctx); + static void method_valueOf(Context *ctx); + static void method_getTime(Context *ctx); + static void method_getYear(Context *ctx); + static void method_getFullYear(Context *ctx); + static void method_getUTCFullYear(Context *ctx); + static void method_getMonth(Context *ctx); + static void method_getUTCMonth(Context *ctx); + static void method_getDate(Context *ctx); + static void method_getUTCDate(Context *ctx); + static void method_getDay(Context *ctx); + static void method_getUTCDay(Context *ctx); + static void method_getHours(Context *ctx); + static void method_getUTCHours(Context *ctx); + static void method_getMinutes(Context *ctx); + static void method_getUTCMinutes(Context *ctx); + static void method_getSeconds(Context *ctx); + static void method_getUTCSeconds(Context *ctx); + static void method_getMilliseconds(Context *ctx); + static void method_getUTCMilliseconds(Context *ctx); + static void method_getTimezoneOffset(Context *ctx); + static void method_setTime(Context *ctx); + static void method_setMilliseconds(Context *ctx); + static void method_setUTCMilliseconds(Context *ctx); + static void method_setSeconds(Context *ctx); + static void method_setUTCSeconds(Context *ctx); + static void method_setMinutes(Context *ctx); + static void method_setUTCMinutes(Context *ctx); + static void method_setHours(Context *ctx); + static void method_setUTCHours(Context *ctx); + static void method_setDate(Context *ctx); + static void method_setUTCDate(Context *ctx); + static void method_setMonth(Context *ctx); + static void method_setUTCMonth(Context *ctx); + static void method_setYear(Context *ctx); + static void method_setFullYear(Context *ctx); + static void method_setUTCFullYear(Context *ctx); + static void method_toUTCString(Context *ctx); +}; + struct MathObject: Object { MathObject(Context *ctx); diff --git a/qv4isel.cpp b/qv4isel.cpp index 3a85815fe0..e26b72657f 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -506,6 +506,9 @@ void InstructionSelection::visitMove(IR::Move *s) return; } assert(!"todo"); + } else if (IR::Subscript *ss = s->source->asSubscript()) { + qWarning() << "TODO load subscript"; + return; } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); @@ -522,8 +525,9 @@ void InstructionSelection::visitMove(IR::Move *s) } // switch amd64_call_code(_codePtr, op); return; + } else if (IR::Const *c = u->expr->asConst()) { + return; } - } else if (IR::Binop *b = s->source->asBinop()) { IR::Temp *l = b->left->asTemp(); IR::Temp *r = b->right->asTemp(); @@ -660,9 +664,40 @@ void InstructionSelection::visitMove(IR::Move *s) return; } } + } else if (IR::Subscript *ss = s->target->asSubscript()) { + if (IR::Temp *t = s->source->asTemp()) { + return; + } else if (IR::Const *c = s->source->asConst()) { + return; + } } } else { // inplace assignment, e.g. x += 1, ++x, ... + if (IR::Temp *t = s->target->asTemp()) { + if (IR::Const *c = s->source->asConst()) { + return; + } else if (IR::Temp *t2 = s->source->asTemp()) { + return; + } + } else if (IR::Name *n = s->target->asName()) { + if (IR::Const *c = s->source->asConst()) { + return; + } else if (IR::Temp *t = s->source->asTemp()) { + return; + } + } else if (IR::Subscript *ss = s->target->asSubscript()) { + if (IR::Const *c = s->source->asConst()) { + return; + } else if (IR::Temp *t = s->source->asTemp()) { + return; + } + } else if (IR::Member *m = s->target->asMember()) { + if (IR::Const *c = s->source->asConst()) { + return; + } else if (IR::Temp *t = s->source->asTemp()) { + return; + } + } } Q_UNIMPLEMENTED(); -- cgit v1.2.3 From ccebbc06413f5e33aebd54223149526b4bacd719 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 18 May 2012 14:10:02 +0200 Subject: Implemented the ecma Date object. --- qmljs_objects.cpp | 2 +- qmljs_objects.h | 24 +++++- qmljs_runtime.cpp | 79 +++++++++++++++++++ qmljs_runtime.h | 26 +++++++ qv4ecmaobjects.cpp | 221 ++++++++++++++++++++++++++++++++++++++++++++++++----- qv4ecmaobjects_p.h | 3 +- 6 files changed, 331 insertions(+), 24 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 0fdf99bc24..cf5560eaf5 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -323,7 +323,7 @@ Object *ExecutionEngine::newBooleanPrototype(Context *ctx, FunctionObject *proto Object *ExecutionEngine::newDateObject(const Value &value) { - return new Object(); // ### FIXME + return new DateObject(value); } FunctionObject *ExecutionEngine::newDateCtor(Context *ctx) diff --git a/qmljs_objects.h b/qmljs_objects.h index b2b2ea3738..cbbea409bc 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -21,8 +21,10 @@ struct BooleanObject; struct NumberObject; struct StringObject; struct ArrayObject; +struct DateObject; struct FunctionObject; struct ErrorObject; +struct ArgumentsObject; struct Context; struct ExecutionEngine; @@ -194,7 +196,14 @@ struct Object { virtual ~Object(); + virtual BooleanObject *asBooleanObject() { return 0; } + virtual NumberObject *asNumberObject() { return 0; } + virtual StringObject *asStringObject() { return 0; } + virtual DateObject *asDateObject() { return 0; } + virtual ArrayObject *asArrayObject() { return 0; } virtual FunctionObject *asFunctionObject() { return 0; } + virtual ErrorObject *asErrorObject() { return 0; } + virtual ArgumentsObject *asArgumentsObject() { return 0; } bool get(String *name, Value *result); @@ -217,22 +226,33 @@ struct Object { struct BooleanObject: Object { Value value; BooleanObject(const Value &value): value(value) {} + virtual BooleanObject *asBooleanObject() { return this; } virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } }; struct NumberObject: Object { Value value; NumberObject(const Value &value): value(value) {} + virtual NumberObject *asNumberObject() { return this; } virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } }; struct StringObject: Object { Value value; StringObject(const Value &value): value(value) {} + virtual StringObject *asStringObject() { return this; } + virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } +}; + +struct DateObject: Object { + Value value; + DateObject(const Value &value): value(value) {} + virtual DateObject *asDateObject() { return this; } virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } }; struct ArrayObject: Object { + virtual ArrayObject *asArrayObject() { return this; } }; struct FunctionObject: Object { @@ -250,8 +270,8 @@ struct FunctionObject: Object { , varList(0) , varCount(0) , needsActivation(true) {} - virtual FunctionObject *asFunctionObject() { return this; } + virtual FunctionObject *asFunctionObject() { return this; } virtual bool hasInstance(const Value &value) const; virtual void call(Context *ctx); virtual void construct(Context *ctx); @@ -278,11 +298,13 @@ struct ScriptFunction: FunctionObject { struct ErrorObject: Object { Value message; ErrorObject(const Value &message): message(message) {} + virtual ErrorObject *asErrorObject() { return this; } }; struct ArgumentsObject: Object { Context *context; ArgumentsObject(Context *context): context(context) {} + virtual ArgumentsObject *asArgumentsObject() { return this; } virtual Value *getProperty(String *name, PropertyAttributes *attributes); }; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 337f2f7a41..6cb2a931a8 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -69,6 +69,85 @@ String *Value::toString(Context *ctx) const return v.stringValue; } +bool Value::isFunctionObject() const +{ + return type == OBJECT_TYPE ? objectValue->asFunctionObject() != 0 : false; +} + +bool Value::isBooleanObject() const +{ + return type == OBJECT_TYPE ? objectValue->asBooleanObject() != 0 : false; +} + +bool Value::isNumberObject() const +{ + return type == OBJECT_TYPE ? objectValue->asNumberObject() != 0 : false; +} + +bool Value::isStringObject() const +{ + return type == OBJECT_TYPE ? objectValue->asStringObject() != 0 : false; +} + +bool Value::isDateObject() const +{ + return type == OBJECT_TYPE ? objectValue->asDateObject() != 0 : false; +} + +bool Value::isArrayObject() const +{ + return type == OBJECT_TYPE ? objectValue->asArrayObject() != 0 : false; +} + +bool Value::isErrorObject() const +{ + return type == OBJECT_TYPE ? objectValue->asErrorObject() != 0 : false; +} + +bool Value::isArgumentsObject() const +{ + return type == OBJECT_TYPE ? objectValue->asArgumentsObject() != 0 : false; +} + +FunctionObject *Value::asFunctionObject() const +{ + return type == OBJECT_TYPE ? objectValue->asFunctionObject() : 0; +} + +BooleanObject *Value::asBooleanObject() const +{ + return type == OBJECT_TYPE ? objectValue->asBooleanObject() : 0; +} + +NumberObject *Value::asNumberObject() const +{ + return type == OBJECT_TYPE ? objectValue->asNumberObject() : 0; +} + +StringObject *Value::asStringObject() const +{ + return type == OBJECT_TYPE ? objectValue->asStringObject() : 0; +} + +DateObject *Value::asDateObject() const +{ + return type == OBJECT_TYPE ? objectValue->asDateObject() : 0; +} + +ArrayObject *Value::asArrayObject() const +{ + return type == OBJECT_TYPE ? objectValue->asArrayObject() : 0; +} + +ErrorObject *Value::asErrorObject() const +{ + return type == OBJECT_TYPE ? objectValue->asErrorObject() : 0; +} + +ArgumentsObject *Value::asArgumentsObject() const +{ + return type == OBJECT_TYPE ? objectValue->asArgumentsObject() : 0; +} extern "C" { diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 223a219753..a02ceecbaa 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -40,6 +40,14 @@ struct Value; struct Object; struct String; struct Context; +struct FunctionObject; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct DateObject; +struct ArrayObject; +struct ErrorObject; +struct ArgumentsObject; extern "C" { @@ -228,6 +236,24 @@ struct Value { inline bool isBoolean() const { return is(BOOLEAN_TYPE); } inline bool isNumber() const { return is(NUMBER_TYPE); } inline bool isObject() const { return is(OBJECT_TYPE); } + + bool isFunctionObject() const; + bool isBooleanObject() const; + bool isNumberObject() const; + bool isStringObject() const; + bool isDateObject() const; + bool isArrayObject() const; + bool isErrorObject() const; + bool isArgumentsObject() const; + + FunctionObject *asFunctionObject() const; + BooleanObject *asBooleanObject() const; + NumberObject *asNumberObject() const; + StringObject *asStringObject() const; + DateObject *asDateObject() const; + ArrayObject *asArrayObject() const; + ErrorObject *asErrorObject() const; + ArgumentsObject *asArgumentsObject() const; }; extern "C" { diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 46305021ca..0131cbfa37 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -936,7 +936,7 @@ void NumberPrototype::method_toLocaleString(Context *ctx) assert(self.isObject()); // if (self.classInfo() != classInfo) { // return throwThisObjectTypeError( - // context, QLatin1String("Number.prototype.toLocaleString")); + // ctx, QLatin1String("Number.prototype.toLocaleString")); // } Value internalValue; self.objectValue->defaultValue(ctx, &internalValue, STRING_HINT); @@ -950,7 +950,7 @@ void NumberPrototype::method_valueOf(Context *ctx) assert(self.isObject()); // if (self.classInfo() != classInfo) { // return throwThisObjectTypeError( - // context, QLatin1String("Number.prototype.toLocaleString")); + // ctx, QLatin1String("Number.prototype.toLocaleString")); // } Value internalValue; self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); @@ -1068,7 +1068,7 @@ void BooleanPrototype::method_toString(Context *ctx) Value self = ctx->thisObject; // if (self.classInfo() != classInfo) { // return throwThisObjectTypeError( -// context, QLatin1String("Boolean.prototype.toString")); +// ctx, QLatin1String("Boolean.prototype.toString")); // } assert(self.isObject()); Value internalValue; @@ -1083,7 +1083,7 @@ void BooleanPrototype::method_valueOf(Context *ctx) Value self = ctx->thisObject; // if (self.classInfo() != classInfo) { // return throwThisObjectTypeError( -// context, QLatin1String("Boolean.prototype.valueOf")); +// ctx, QLatin1String("Boolean.prototype.valueOf")); // } assert(self.isObject()); Value internalValue; @@ -1099,7 +1099,7 @@ Value DateCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; FunctionObject *ctor = ctx->engine->newDateCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newNumberPrototype(ctx, ctor))); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newDatePrototype(ctx, ctor))); return Value::fromObject(ctor); } @@ -1110,18 +1110,17 @@ DateCtor::DateCtor(Context *scope) void DateCtor::construct(Context *ctx) { - // called as constructor - double t; + double t = 0; if (ctx->argumentCount == 0) t = currentTime(); else if (ctx->argumentCount == 1) { Value arg = ctx->argument(0); -// if (arg.isDate()) -// arg = arg.internalValue(); -// else -// __qmljs_to_primitive(ctx, &arg, &arg, PREFERREDTYPE_HINT); + if (DateObject *d = arg.asDateObject()) + arg = d->value; + else + __qmljs_to_primitive(ctx, &arg, &arg, PREFERREDTYPE_HINT); if (arg.isString()) t = ParseString(arg.toString(ctx)->toQString()); @@ -1129,7 +1128,7 @@ void DateCtor::construct(Context *ctx) t = TimeClip(arg.toNumber(ctx)); } - else { // context->argumentCount() > 1 + else { // ctx->argumentCount() > 1 double year = ctx->argument(0).toNumber(ctx); double month = ctx->argument(1).toNumber(ctx); double day = ctx->argumentCount >= 3 ? ctx->argument(2).toNumber(ctx) : 1; @@ -1143,11 +1142,8 @@ void DateCtor::construct(Context *ctx) t = TimeClip(UTC(t)); } -// Value &obj = ctx->m_thisObject; -// obj.setClassInfo(classInfo()); -// obj.setInternalValue(Value(t)); -// obj.setPrototype(publicPrototype); -// ctx->setReturnValue(obj); + Object *d = ctx->engine->newDateObject(Value::fromNumber(t)); + ctx->thisObject = Value::fromObject(d); } void DateCtor::call(Context *ctx) @@ -1157,6 +1153,7 @@ void DateCtor::call(Context *ctx) } DatePrototype::DatePrototype(Context *ctx, FunctionObject *ctor) + : DateObject(Value::fromNumber(qSNaN())) { LocalTZA = getLocalTZA(); @@ -1210,11 +1207,19 @@ DatePrototype::DatePrototype(Context *ctx, FunctionObject *ctor) setProperty(ctx, QLatin1String("toGMTString"), method_toUTCString, 0); } +DateObject *DatePrototype::getThisDateObject(Context *ctx) +{ + assert(ctx->thisObject.isObject()); + DateObject *date = ctx->thisObject.objectValue->asDateObject(); + return date; +} + double DatePrototype::getThisDate(Context *ctx) { assert(ctx->thisObject.isObject()); - Value internalValue; - ctx->thisObject.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); + DateObject *date = ctx->thisObject.objectValue->asDateObject(); + assert(date); + Value internalValue = date->value; assert(internalValue.isNumber()); return internalValue.numberValue; } @@ -1449,70 +1454,244 @@ void DatePrototype::method_getTimezoneOffset(Context *ctx) void DatePrototype::method_setTime(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + self->value.numberValue = TimeClip(ctx->argument(0).toNumber(ctx)); + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setMilliseconds(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = LocalTime(self->value.numberValue); + double ms = ctx->argument(0).toNumber(ctx); + self->value.numberValue = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setUTCMilliseconds(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + double ms = ctx->argument(0).toNumber(ctx); + self->value.numberValue = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setSeconds(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = LocalTime(self->value.numberValue); + double sec = ctx->argument(0).toNumber(ctx); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setUTCSeconds(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + double sec = ctx->argument(0).toNumber(ctx); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setMinutes(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = LocalTime(self->value.numberValue); + double min = ctx->argument(0).toNumber(ctx); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setUTCMinutes(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + double min = ctx->argument(0).toNumber(ctx); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setHours(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = LocalTime(self->value.numberValue); + double hour = ctx->argument(0).toNumber(ctx); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setUTCHours(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + double hour = ctx->argument(0).toNumber(ctx); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setDate(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = LocalTime(self->value.numberValue); + double date = ctx->argument(0).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setUTCDate(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + double date = ctx->argument(0).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setMonth(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = LocalTime(self->value.numberValue); + double month = ctx->argument(0).toNumber(ctx); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setUTCMonth(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + double month = ctx->argument(0).toNumber(ctx); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setYear(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + if (qIsNaN(t)) + t = 0; + else + t = LocalTime(t); + double year = ctx->argument(0).toNumber(ctx); + double r; + if (qIsNaN(year)) { + r = qSNaN(); + } else { + if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99)) + year += 1900; + r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); + r = UTC(MakeDate(r, TimeWithinDay(t))); + r = TimeClip(r); + } + self->value.numberValue = r; + ctx->result = self->value; + } else { + assert(!"type error"); + } } -void DatePrototype::method_setFullYear(Context *ctx) +void DatePrototype::method_setUTCFullYear(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + double year = ctx->argument(0).toNumber(ctx); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } -void DatePrototype::method_setUTCFullYear(Context *ctx) +void DatePrototype::method_setFullYear(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = LocalTime(self->value.numberValue); + double year = ctx->argument(0).toNumber(ctx); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_toUTCString(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + ctx->result = Value::fromString(ctx, ToUTCString(t)); + } } // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 68c15222cc..d4cbf613d9 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -112,11 +112,12 @@ struct DateCtor: FunctionObject virtual void call(Context *ctx); }; -struct DatePrototype: Object +struct DatePrototype: DateObject { DatePrototype(Context *ctx, FunctionObject *ctor); protected: + static DateObject *getThisDateObject(Context *ctx); static double getThisDate(Context *ctx); static void method_MakeTime(Context *ctx); -- cgit v1.2.3 From d0173b91ecde7a01a7bc8bcfd9b073620e04112c Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 18 May 2012 14:22:37 +0200 Subject: Improve the Number prototype. --- qv4ecmaobjects.cpp | 230 ++++++++++++++++++++++++----------------------------- qv4ecmaobjects_p.h | 2 + 2 files changed, 107 insertions(+), 125 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 0131cbfa37..1f92afe18f 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -865,167 +865,147 @@ NumberPrototype::NumberPrototype(Context *ctx, FunctionObject *ctor) setProperty(ctx, QLatin1String("toPrecision"), method_toPrecision); } +NumberObject *NumberPrototype::getThisNumberObject(Context *ctx) +{ + assert(ctx->thisObject.isObject()); + if (NumberObject *o = ctx->thisObject.objectValue->asNumberObject()) { + return o; + } + return 0; +} + void NumberPrototype::method_toString(Context *ctx) { - Value self = ctx->thisObject; - assert(self.isObject()); - // if (self.classInfo() != classInfo) { - // return throwThisObjectTypeError( - // ctx, QLatin1String("Number.prototype.toString")); - // } - - Value arg = ctx->argument(0); - if (!arg.isUndefined()) { - int radix = arg.toInt32(ctx); - // if (radix < 2 || radix > 36) - // return ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") - // .arg(radix)); - if (radix != 10) { - Value internalValue; - self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); - - QString str; - double num = internalValue.toNumber(ctx); - if (qIsNaN(num)) { - ctx->result = Value::fromString(ctx, QLatin1String("NaN")); - return; - } else if (qIsInf(num)) { - ctx->result = Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); + if (NumberObject *thisObject = getThisNumberObject(ctx)) { + Value arg = ctx->argument(0); + if (!arg.isUndefined()) { + int radix = arg.toInt32(ctx); + if (radix < 2 || radix > 36) { +// return ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") +// .arg(radix)); + assert(!"not a valid redix"); return; } - bool negative = false; - if (num < 0) { - negative = true; - num = -num; - } - double frac = num - ::floor(num); - num = Value::toInteger(num); - do { - char c = (char)::fmod(num, radix); - c = (c < 10) ? (c + '0') : (c - 10 + 'a'); - str.prepend(QLatin1Char(c)); - num = ::floor(num / radix); - } while (num != 0); - if (frac != 0) { - str.append(QLatin1Char('.')); + + if (radix != 10) { + double num = thisObject->value.numberValue; + QString str; + if (qIsNaN(num)) { + ctx->result = Value::fromString(ctx, QLatin1String("NaN")); + return; + } else if (qIsInf(num)) { + ctx->result = Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); + return; + } + bool negative = false; + if (num < 0) { + negative = true; + num = -num; + } + double frac = num - ::floor(num); + num = Value::toInteger(num); do { - frac = frac * radix; - char c = (char)::floor(frac); + char c = (char)::fmod(num, radix); c = (c < 10) ? (c + '0') : (c - 10 + 'a'); - str.append(QLatin1Char(c)); - frac = frac - ::floor(frac); - } while (frac != 0); + str.prepend(QLatin1Char(c)); + num = ::floor(num / radix); + } while (num != 0); + if (frac != 0) { + str.append(QLatin1Char('.')); + do { + frac = frac * radix; + char c = (char)::floor(frac); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.append(QLatin1Char(c)); + frac = frac - ::floor(frac); + } while (frac != 0); + } + if (negative) + str.prepend(QLatin1Char('-')); + ctx->result = Value::fromString(ctx, str); + return; } - if (negative) - str.prepend(QLatin1Char('-')); - ctx->result = Value::fromString(ctx, str); - return; } - } - Value internalValue; - self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); - - String *str = internalValue.toString(ctx); - ctx->result = Value::fromString(str); + Value internalValue = thisObject->value; + String *str = internalValue.toString(ctx); + ctx->result = Value::fromString(str); + } else { + assert(!"type error"); + } } void NumberPrototype::method_toLocaleString(Context *ctx) { - Value self = ctx->thisObject; - assert(self.isObject()); - // if (self.classInfo() != classInfo) { - // return throwThisObjectTypeError( - // ctx, QLatin1String("Number.prototype.toLocaleString")); - // } - Value internalValue; - self.objectValue->defaultValue(ctx, &internalValue, STRING_HINT); - String *str = internalValue.toString(ctx); - ctx->result = Value::fromString(str); + if (NumberObject *thisObject = getThisNumberObject(ctx)) { + String *str = thisObject->value.toString(ctx); + ctx->result = Value::fromString(str); + } else { + assert(!"type error"); + } } void NumberPrototype::method_valueOf(Context *ctx) { - Value self = ctx->thisObject; - assert(self.isObject()); - // if (self.classInfo() != classInfo) { - // return throwThisObjectTypeError( - // ctx, QLatin1String("Number.prototype.toLocaleString")); - // } - Value internalValue; - self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); - ctx->result = internalValue; + if (NumberObject *thisObject = getThisNumberObject(ctx)) { + ctx->result = thisObject->value; + } else { + assert(!"type error"); + } } void NumberPrototype::method_toFixed(Context *ctx) { - Value self = ctx->thisObject; - assert(self.isObject()); - // if (self.classInfo() != classInfo) { - // return throwThisObjectTypeError( - // ctx, QLatin1String("Number.prototype.toFixed")); - // } - double fdigits = 0; + if (NumberObject *thisObject = getThisNumberObject(ctx)) { + double fdigits = 0; - if (ctx->argumentCount > 0) - fdigits = ctx->argument(0).toInteger(ctx); - - if (qIsNaN(fdigits)) - fdigits = 0; + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(ctx); - Value internalValue; - self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); + if (qIsNaN(fdigits)) + fdigits = 0; - double v = internalValue.toNumber(ctx); - QString str; - if (qIsNaN(v)) - str = QString::fromLatin1("NaN"); - else if (qIsInf(v)) - str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); - else - str = QString::number(v, 'f', int (fdigits)); - ctx->result = Value::fromString(ctx, str); + double v = thisObject->value.numberValue; + QString str; + if (qIsNaN(v)) + str = QString::fromLatin1("NaN"); + else if (qIsInf(v)) + str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); + else + str = QString::number(v, 'f', int (fdigits)); + ctx->result = Value::fromString(ctx, str); + } else { + assert(!"type error"); + } } void NumberPrototype::method_toExponential(Context *ctx) { - Value self = ctx->thisObject; - assert(self.isObject()); - // if (self.classInfo() != classInfo) { - // return throwThisObjectTypeError( - // ctx, QLatin1String("Number.prototype.toFixed")); - // } - double fdigits = 0; - - if (ctx->argumentCount > 0) - fdigits = ctx->argument(0).toInteger(ctx); + if (NumberObject *thisObject = getThisNumberObject(ctx)) { + double fdigits = 0; - Value internalValue; - self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(ctx); - double v = internalValue.toNumber(ctx); - QString z = QString::number(v, 'e', int (fdigits)); - ctx->result = Value::fromString(ctx, z); + QString z = QString::number(thisObject->value.numberValue, 'e', int (fdigits)); + ctx->result = Value::fromString(ctx, z); + } else { + assert(!"type error"); + } } void NumberPrototype::method_toPrecision(Context *ctx) { - Value self = ctx->thisObject; - assert(self.isObject()); - // if (self.classInfo() != classInfo) { - // return throwThisObjectTypeError( - // ctx, QLatin1String("Number.prototype.toFixed")); - // } - double fdigits = 0; - - if (ctx->argumentCount > 0) - fdigits = ctx->argument(0).toInteger(ctx); + if (NumberObject *thisObject = getThisNumberObject(ctx)) { + double fdigits = 0; - Value internalValue; - self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(ctx); - double v = internalValue.toNumber(ctx); - ctx->result = Value::fromString(ctx, QString::number(v, 'g', int (fdigits))); + ctx->result = Value::fromString(ctx, QString::number(thisObject->value.numberValue, 'g', int (fdigits))); + } else { + assert(!"type error"); + } } // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index d4cbf613d9..6c7f01f378 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -75,6 +75,8 @@ struct NumberPrototype: Object NumberPrototype(Context *ctx, FunctionObject *ctor); protected: + static NumberObject *getThisNumberObject(Context *ctx); + static void method_toString(Context *ctx); static void method_toLocaleString(Context *ctx); static void method_valueOf(Context *ctx); -- cgit v1.2.3 From bd8495a40499560c18539135f23001dbcd632d9f Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 18 May 2012 14:27:55 +0200 Subject: Improve the Boolean prototype. --- qv4ecmaobjects.cpp | 32 +++++++++++--------------------- qv4ecmaobjects_p.h | 2 +- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 1f92afe18f..8fee3b94d5 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1037,6 +1037,7 @@ void BooleanCtor::call(Context *ctx) } BooleanPrototype::BooleanPrototype(Context *ctx, FunctionObject *ctor) + : BooleanObject(Value::fromBoolean(false)) { ctor->setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); ctor->setProperty(ctx, QLatin1String("toString"), method_toString); @@ -1045,31 +1046,20 @@ BooleanPrototype::BooleanPrototype(Context *ctx, FunctionObject *ctor) void BooleanPrototype::method_toString(Context *ctx) { - Value self = ctx->thisObject; -// if (self.classInfo() != classInfo) { -// return throwThisObjectTypeError( -// ctx, QLatin1String("Boolean.prototype.toString")); -// } - assert(self.isObject()); - Value internalValue; - self.objectValue->defaultValue(ctx, &internalValue, PREFERREDTYPE_HINT); - assert(internalValue.isBoolean()); - bool v = internalValue.booleanValue; - ctx->result = Value::fromString(ctx, QLatin1String(v ? "true" : "false")); + if (BooleanObject *thisObject = ctx->thisObject.asBooleanObject()) { + ctx->result = Value::fromString(ctx, QLatin1String(thisObject->value.booleanValue ? "true" : "false")); + } else { + assert(!"type error"); + } } void BooleanPrototype::method_valueOf(Context *ctx) { - Value self = ctx->thisObject; -// if (self.classInfo() != classInfo) { -// return throwThisObjectTypeError( -// ctx, QLatin1String("Boolean.prototype.valueOf")); -// } - assert(self.isObject()); - Value internalValue; - self.objectValue->defaultValue(ctx, &internalValue, PREFERREDTYPE_HINT); - assert(internalValue.isBoolean()); - ctx->result = internalValue; + if (BooleanObject *thisObject = ctx->thisObject.asBooleanObject()) { + ctx->result = thisObject->value; + } else { + assert(!"type error"); + } } // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 6c7f01f378..16d2c13773 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -95,7 +95,7 @@ struct BooleanCtor: FunctionObject virtual void call(Context *ctx); }; -struct BooleanPrototype: Object +struct BooleanPrototype: BooleanObject { BooleanPrototype(Context *ctx, FunctionObject *ctor); -- cgit v1.2.3 From 2cd0bc7baf4f717bc779b5de308f40a8c56df567 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 18 May 2012 14:32:11 +0200 Subject: Improve the Number prototype. --- qv4ecmaobjects.cpp | 75 ++++++++++++++++++++++-------------------------------- qv4ecmaobjects_p.h | 5 +--- 2 files changed, 31 insertions(+), 49 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 8fee3b94d5..497853e3b0 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -842,6 +842,7 @@ void NumberCtor::call(Context *ctx) } NumberPrototype::NumberPrototype(Context *ctx, FunctionObject *ctor) + : NumberObject(Value::fromNumber(0)) { ctor->setProperty(ctx, QLatin1String("NaN"), Value::fromNumber(qSNaN())); ctor->setProperty(ctx, QLatin1String("NEGATIVE_INFINITY"), Value::fromNumber(-qInf())); @@ -865,18 +866,9 @@ NumberPrototype::NumberPrototype(Context *ctx, FunctionObject *ctor) setProperty(ctx, QLatin1String("toPrecision"), method_toPrecision); } -NumberObject *NumberPrototype::getThisNumberObject(Context *ctx) -{ - assert(ctx->thisObject.isObject()); - if (NumberObject *o = ctx->thisObject.objectValue->asNumberObject()) { - return o; - } - return 0; -} - void NumberPrototype::method_toString(Context *ctx) { - if (NumberObject *thisObject = getThisNumberObject(ctx)) { + if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { Value arg = ctx->argument(0); if (!arg.isUndefined()) { int radix = arg.toInt32(ctx); @@ -937,7 +929,7 @@ void NumberPrototype::method_toString(Context *ctx) void NumberPrototype::method_toLocaleString(Context *ctx) { - if (NumberObject *thisObject = getThisNumberObject(ctx)) { + if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { String *str = thisObject->value.toString(ctx); ctx->result = Value::fromString(str); } else { @@ -947,7 +939,7 @@ void NumberPrototype::method_toLocaleString(Context *ctx) void NumberPrototype::method_valueOf(Context *ctx) { - if (NumberObject *thisObject = getThisNumberObject(ctx)) { + if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { ctx->result = thisObject->value; } else { assert(!"type error"); @@ -956,7 +948,7 @@ void NumberPrototype::method_valueOf(Context *ctx) void NumberPrototype::method_toFixed(Context *ctx) { - if (NumberObject *thisObject = getThisNumberObject(ctx)) { + if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { double fdigits = 0; if (ctx->argumentCount > 0) @@ -981,7 +973,7 @@ void NumberPrototype::method_toFixed(Context *ctx) void NumberPrototype::method_toExponential(Context *ctx) { - if (NumberObject *thisObject = getThisNumberObject(ctx)) { + if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { double fdigits = 0; if (ctx->argumentCount > 0) @@ -996,7 +988,7 @@ void NumberPrototype::method_toExponential(Context *ctx) void NumberPrototype::method_toPrecision(Context *ctx) { - if (NumberObject *thisObject = getThisNumberObject(ctx)) { + if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { double fdigits = 0; if (ctx->argumentCount > 0) @@ -1177,21 +1169,14 @@ DatePrototype::DatePrototype(Context *ctx, FunctionObject *ctor) setProperty(ctx, QLatin1String("toGMTString"), method_toUTCString, 0); } -DateObject *DatePrototype::getThisDateObject(Context *ctx) -{ - assert(ctx->thisObject.isObject()); - DateObject *date = ctx->thisObject.objectValue->asDateObject(); - return date; -} - double DatePrototype::getThisDate(Context *ctx) { - assert(ctx->thisObject.isObject()); - DateObject *date = ctx->thisObject.objectValue->asDateObject(); - assert(date); - Value internalValue = date->value; - assert(internalValue.isNumber()); - return internalValue.numberValue; + if (DateObject *thisObject = ctx->thisObject.asDateObject()) + return thisObject->value.numberValue; + else { + assert(!"type error"); + return 0; + } } void DatePrototype::method_MakeTime(Context *ctx) @@ -1424,7 +1409,7 @@ void DatePrototype::method_getTimezoneOffset(Context *ctx) void DatePrototype::method_setTime(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { self->value.numberValue = TimeClip(ctx->argument(0).toNumber(ctx)); ctx->result = self->value; } else { @@ -1434,7 +1419,7 @@ void DatePrototype::method_setTime(Context *ctx) void DatePrototype::method_setMilliseconds(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.numberValue); double ms = ctx->argument(0).toNumber(ctx); self->value.numberValue = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); @@ -1446,7 +1431,7 @@ void DatePrototype::method_setMilliseconds(Context *ctx) void DatePrototype::method_setUTCMilliseconds(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.numberValue; double ms = ctx->argument(0).toNumber(ctx); self->value.numberValue = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); @@ -1458,7 +1443,7 @@ void DatePrototype::method_setUTCMilliseconds(Context *ctx) void DatePrototype::method_setSeconds(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.numberValue); double sec = ctx->argument(0).toNumber(ctx); double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); @@ -1472,7 +1457,7 @@ void DatePrototype::method_setSeconds(Context *ctx) void DatePrototype::method_setUTCSeconds(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.numberValue; double sec = ctx->argument(0).toNumber(ctx); double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); @@ -1486,7 +1471,7 @@ void DatePrototype::method_setUTCSeconds(Context *ctx) void DatePrototype::method_setMinutes(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.numberValue); double min = ctx->argument(0).toNumber(ctx); double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); @@ -1501,7 +1486,7 @@ void DatePrototype::method_setMinutes(Context *ctx) void DatePrototype::method_setUTCMinutes(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.numberValue; double min = ctx->argument(0).toNumber(ctx); double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); @@ -1516,7 +1501,7 @@ void DatePrototype::method_setUTCMinutes(Context *ctx) void DatePrototype::method_setHours(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.numberValue); double hour = ctx->argument(0).toNumber(ctx); double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); @@ -1532,7 +1517,7 @@ void DatePrototype::method_setHours(Context *ctx) void DatePrototype::method_setUTCHours(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.numberValue; double hour = ctx->argument(0).toNumber(ctx); double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); @@ -1548,7 +1533,7 @@ void DatePrototype::method_setUTCHours(Context *ctx) void DatePrototype::method_setDate(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.numberValue); double date = ctx->argument(0).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); @@ -1561,7 +1546,7 @@ void DatePrototype::method_setDate(Context *ctx) void DatePrototype::method_setUTCDate(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.numberValue; double date = ctx->argument(0).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); @@ -1574,7 +1559,7 @@ void DatePrototype::method_setUTCDate(Context *ctx) void DatePrototype::method_setMonth(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.numberValue); double month = ctx->argument(0).toNumber(ctx); double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); @@ -1588,7 +1573,7 @@ void DatePrototype::method_setMonth(Context *ctx) void DatePrototype::method_setUTCMonth(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.numberValue; double month = ctx->argument(0).toNumber(ctx); double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); @@ -1602,7 +1587,7 @@ void DatePrototype::method_setUTCMonth(Context *ctx) void DatePrototype::method_setYear(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.numberValue; if (qIsNaN(t)) t = 0; @@ -1628,7 +1613,7 @@ void DatePrototype::method_setYear(Context *ctx) void DatePrototype::method_setUTCFullYear(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.numberValue; double year = ctx->argument(0).toNumber(ctx); double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); @@ -1643,7 +1628,7 @@ void DatePrototype::method_setUTCFullYear(Context *ctx) void DatePrototype::method_setFullYear(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.numberValue); double year = ctx->argument(0).toNumber(ctx); double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); @@ -1658,7 +1643,7 @@ void DatePrototype::method_setFullYear(Context *ctx) void DatePrototype::method_toUTCString(Context *ctx) { - if (DateObject *self = getThisDateObject(ctx)) { + if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.numberValue; ctx->result = Value::fromString(ctx, ToUTCString(t)); } diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 16d2c13773..abcf390dfb 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -70,13 +70,11 @@ struct NumberCtor: FunctionObject virtual void call(Context *ctx); }; -struct NumberPrototype: Object +struct NumberPrototype: NumberObject { NumberPrototype(Context *ctx, FunctionObject *ctor); protected: - static NumberObject *getThisNumberObject(Context *ctx); - static void method_toString(Context *ctx); static void method_toLocaleString(Context *ctx); static void method_valueOf(Context *ctx); @@ -119,7 +117,6 @@ struct DatePrototype: DateObject DatePrototype(Context *ctx, FunctionObject *ctor); protected: - static DateObject *getThisDateObject(Context *ctx); static double getThisDate(Context *ctx); static void method_MakeTime(Context *ctx); -- cgit v1.2.3 From 860bd23e4d93fd38a66c15c2276b556f824b8147 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 18 May 2012 15:28:59 +0200 Subject: Improve [[DefaultValue]] --- main.cpp | 5 ++- qmljs_objects.cpp | 13 ------- qmljs_objects.h | 5 --- qmljs_runtime.cpp | 101 +++++++++++++++++++++++++++++++++++++++++++++++++---- qmljs_runtime.h | 8 +++-- qv4ecmaobjects.cpp | 34 +++++++++++------- qv4isel.cpp | 7 ++-- 7 files changed, 126 insertions(+), 47 deletions(-) diff --git a/main.cpp b/main.cpp index 44cd18dc1e..60870d021d 100644 --- a/main.cpp +++ b/main.cpp @@ -34,11 +34,10 @@ struct Print: FunctionObject virtual void call(Context *ctx) { for (size_t i = 0; i < ctx->argumentCount; ++i) { - Value v; - __qmljs_to_string(ctx, &v, &ctx->arguments[i]); + String *s = ctx->argument(i).toString(ctx); if (i) std::cout << ' '; - std::cout << qPrintable(v.stringValue->toQString()); + std::cout << qPrintable(s->toQString()); } std::cout << std::endl; } diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index cf5560eaf5..a9f445b010 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -102,19 +102,6 @@ bool Object::deleteProperty(String *name, bool flag) return false; } -void Object::defaultValue(Context *ctx, Value *result, int typeHint) -{ - if (typeHint == STRING_HINT) { - if (asFunctionObject() != 0) - __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("function"))); - else - __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("object"))); - } else { - __qmljs_init_undefined(result); - } -} - - bool FunctionObject::hasInstance(const Value &value) const { Q_UNUSED(value); diff --git a/qmljs_objects.h b/qmljs_objects.h index cbbea409bc..3a06e7efeb 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -213,7 +213,6 @@ struct Object { virtual bool canPut(String *name); virtual bool hasProperty(String *name) const; virtual bool deleteProperty(String *name, bool flag); - virtual void defaultValue(Context *ctx, Value *result, int typeHint); // ### TODO: defineOwnProperty(name, descriptor, boolean) -> boolean // @@ -227,28 +226,24 @@ struct BooleanObject: Object { Value value; BooleanObject(const Value &value): value(value) {} virtual BooleanObject *asBooleanObject() { return this; } - virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } }; struct NumberObject: Object { Value value; NumberObject(const Value &value): value(value) {} virtual NumberObject *asNumberObject() { return this; } - virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } }; struct StringObject: Object { Value value; StringObject(const Value &value): value(value) {} virtual StringObject *asStringObject() { return this; } - virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } }; struct DateObject: Object { Value value; DateObject(const Value &value): value(value) {} virtual DateObject *asDateObject() { return this; } - virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } }; struct ArrayObject: Object { diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 6cb2a931a8..40f0b96352 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -2,13 +2,63 @@ #include "qmljs_runtime.h" #include "qmljs_objects.h" #include "qv4ir_p.h" +#include +#include #include #include #include +#include namespace QQmlJS { namespace VM { +QString numberToString(double num, int radix = 10) +{ + if (qIsNaN(num)) { + return QLatin1String("NaN"); + } else if (qIsInf(num)) { + return QLatin1String(num < 0 ? "-Infinity" : "Infinity"); + } + + if (radix == 10) + return QString::number(num, 'g', 16); + + QString str; + bool negative = false; + + if (num < 0) { + negative = true; + num = -num; + } + + double frac = num - ::floor(num); + num = Value::toInteger(num); + + do { + char c = (char)::fmod(num, radix); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.prepend(QLatin1Char(c)); + num = ::floor(num / radix); + } while (num != 0); + + if (frac != 0) { + str.append(QLatin1Char('.')); + do { + frac = frac * radix; + char c = (char)::floor(frac); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.append(QLatin1Char(c)); + frac = frac - ::floor(frac); + } while (frac != 0); + } + + if (negative) + str.prepend(QLatin1Char('-')); + + return str; +} + + Value Value::fromString(Context *ctx, const QString &s) { return fromString(ctx->engine->newString(s)); @@ -109,6 +159,11 @@ bool Value::isArgumentsObject() const return type == OBJECT_TYPE ? objectValue->asArgumentsObject() != 0 : false; } +Object *Value::asObject() const +{ + return type == OBJECT_TYPE ? objectValue : 0; +} + FunctionObject *Value::asFunctionObject() const { return type == OBJECT_TYPE ? objectValue->asFunctionObject() : 0; @@ -248,7 +303,7 @@ double __qmljs_string_to_number(Context *, String *string) void __qmljs_string_from_number(Context *ctx, Value *result, double number) { - String *string = ctx->engine->newString(QString::number(number, 'g', 16)); + String *string = ctx->engine->newString(numberToString(number, 10)); __qmljs_init_string(result, string); } @@ -274,10 +329,42 @@ bool __qmljs_is_function(Context *, const Value *value) return value->objectValue->asFunctionObject() != 0; } -void __qmljs_object_default_value(Context *ctx, Value *result, Object *object, int typeHint) +void __qmljs_object_default_value(Context *ctx, Value *result, const Value *object, int typeHint) { - Q_UNUSED(ctx); - object->defaultValue(ctx, result, typeHint); + if (typeHint == PREFERREDTYPE_HINT) { + if (object->isDateObject()) + typeHint = STRING_HINT; + else + typeHint = NUMBER_HINT; + } + + String *meth1 = ctx->engine->identifier("toString"); + String *meth2 = ctx->engine->identifier("valueOf"); + + if (typeHint == NUMBER_HINT) + qSwap(meth1, meth2); + + Value *conv = object->asObject()->getProperty(meth1); + if (conv && conv->isFunctionObject()) { + Value r; + __qmljs_call_value(ctx, &r, object, conv, 0, 0); + if (r.isPrimitive()) { + *result = r; + return; + } + } + + conv = object->asObject()->getProperty(meth2); + if (conv && conv->isFunctionObject()) { + Value r; + __qmljs_call_value(ctx, &r, object, conv, 0, 0); + if (r.isPrimitive()) { + *result = r; + return; + } + } + + assert(!"type error"); } void __qmljs_throw_type_error(Context *ctx, Value *result) @@ -514,7 +601,7 @@ void __qmljs_call_activation_property(Context *context, Value *result, String *n if (! func) assert(!"reference error"); - __qmljs_call_value(context, result, func, args, argc); + __qmljs_call_value(context, result, /*thisObject=*/ 0, func, args, argc); } void __qmljs_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) @@ -548,13 +635,13 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S } } -void __qmljs_call_value(Context *context, Value *result, const Value *func, Value *args, int argc) +void __qmljs_call_value(Context *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) { if (func->type == OBJECT_TYPE) { if (FunctionObject *f = func->objectValue->asFunctionObject()) { Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->initCallContext(context->engine, 0, f, args, argc); + ctx->initCallContext(context->engine, thisObject, f, args, argc); f->call(ctx); ctx->leaveCallContext(f, result); } else { diff --git a/qmljs_runtime.h b/qmljs_runtime.h index a02ceecbaa..5499e398ee 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -54,7 +54,7 @@ extern "C" { // context void __qmljs_call_activation_property(Context *, Value *result, String *name, Value *args, int argc); void __qmljs_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc); -void __qmljs_call_value(Context *context, Value *result, const Value *func, Value *args, int argc); +void __qmljs_call_value(Context *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc); void __qmljs_construct_activation_property(Context *, Value *result, String *name, Value *args, int argc); void __qmljs_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc); @@ -91,7 +91,7 @@ bool __qmljs_string_equal(Context *ctx, String *left, String *right); String *__qmljs_string_concat(Context *ctx, String *first, String *second); // objects -void __qmljs_object_default_value(Context *ctx, Value *result, Object *object, int typeHint); +void __qmljs_object_default_value(Context *ctx, Value *result, const Value *object, int typeHint); void __qmljs_throw_type_error(Context *ctx, Value *result); void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean); void __qmljs_new_number_object(Context *ctx, Value *result, double n); @@ -237,6 +237,7 @@ struct Value { inline bool isNumber() const { return is(NUMBER_TYPE); } inline bool isObject() const { return is(OBJECT_TYPE); } + inline bool isPrimitive() const { return type != OBJECT_TYPE; } bool isFunctionObject() const; bool isBooleanObject() const; bool isNumberObject() const; @@ -246,6 +247,7 @@ struct Value { bool isErrorObject() const; bool isArgumentsObject() const; + Object *asObject() const; FunctionObject *asFunctionObject() const; BooleanObject *asBooleanObject() const; NumberObject *asNumberObject() const; @@ -473,7 +475,7 @@ inline bool __qmljs_is_callable(Context *ctx, const Value *value) inline void __qmljs_default_value(Context *ctx, Value *result, const Value *value, int typeHint) { if (value->type == OBJECT_TYPE) - __qmljs_object_default_value(ctx, result, value->objectValue, typeHint); + __qmljs_object_default_value(ctx, result, value, typeHint); else __qmljs_init_undefined(result); } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 497853e3b0..c253700596 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -562,10 +562,12 @@ StringPrototype::StringPrototype(Context *ctx, FunctionObject *ctor) QString StringPrototype::getThisString(Context *ctx) { - Value v; - __qmljs_to_string(ctx, &v, &ctx->thisObject); - assert(v.is(STRING_TYPE)); - return v.stringValue->toQString(); + if (StringObject *thisObject = ctx->thisObject.asStringObject()) { + return thisObject->value.stringValue->toQString(); + } else { + assert(!"type error"); + return QString(); + } } void StringPrototype::method_toString(Context *ctx) @@ -575,7 +577,11 @@ void StringPrototype::method_toString(Context *ctx) void StringPrototype::method_valueOf(Context *ctx) { - ctx->thisObject.objectValue->defaultValue(ctx, &ctx->result, STRING_HINT); + if (StringObject *o = ctx->thisObject.asStringObject()) { + ctx->result = o->value; + } else { + assert(!"type error"); + } } void StringPrototype::method_charAt(Context *ctx) @@ -868,6 +874,7 @@ NumberPrototype::NumberPrototype(Context *ctx, FunctionObject *ctor) void NumberPrototype::method_toString(Context *ctx) { + assert(!"here"); if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { Value arg = ctx->argument(0); if (!arg.isUndefined()) { @@ -879,16 +886,17 @@ void NumberPrototype::method_toString(Context *ctx) return; } + double num = thisObject->value.numberValue; + if (qIsNaN(num)) { + ctx->result = Value::fromString(ctx, QLatin1String("NaN")); + return; + } else if (qIsInf(num)) { + ctx->result = Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); + return; + } + if (radix != 10) { - double num = thisObject->value.numberValue; QString str; - if (qIsNaN(num)) { - ctx->result = Value::fromString(ctx, QLatin1String("NaN")); - return; - } else if (qIsInf(num)) { - ctx->result = Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); - return; - } bool negative = false; if (num < 0) { negative = true; diff --git a/qv4isel.cpp b/qv4isel.cpp index e26b72657f..42e008465e 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -236,9 +236,10 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) else amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - loadTempAddress(AMD64_RDX, baseTemp); - amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RDX, AMD64_RDX); + loadTempAddress(AMD64_RCX, baseTemp); + amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); amd64_call_code(_codePtr, __qmljs_call_value); } -- cgit v1.2.3 From 0f7d85a2ad7c8a06d45402424b026ffc2c3ba13b Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 18 May 2012 16:31:57 +0200 Subject: Inline common cases. --- qmljs_runtime.h | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 5499e398ee..2dd4f85652 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -649,34 +649,54 @@ inline void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Val inline void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Value *right) { - __qmljs_compare(ctx, result, right, left, false); + if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) { + __qmljs_init_boolean(result, left->numberValue >= right->numberValue); + } else { + __qmljs_compare(ctx, result, right, left, false); - bool r = ! (result->type == UNDEFINED_TYPE || - (result->type == BOOLEAN_TYPE && result->booleanValue == true)); + bool r = ! (result->type == UNDEFINED_TYPE || + (result->type == BOOLEAN_TYPE && result->booleanValue == true)); - __qmljs_init_boolean(result, r); + __qmljs_init_boolean(result, r); + } } inline void __qmljs_le(Context *ctx, Value *result, const Value *left, const Value *right) { - __qmljs_compare(ctx, result, right, left, true); + if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) { + __qmljs_init_boolean(result, left->numberValue <= right->numberValue); + } else { + __qmljs_compare(ctx, result, right, left, true); - bool r = ! (result->type == UNDEFINED_TYPE || - (result->type == BOOLEAN_TYPE && result->booleanValue == true)); + bool r = ! (result->type == UNDEFINED_TYPE || + (result->type == BOOLEAN_TYPE && result->booleanValue == true)); - __qmljs_init_boolean(result, r); + __qmljs_init_boolean(result, r); + } } inline void __qmljs_eq(Context *ctx, Value *result, const Value *left, const Value *right) { - bool r = __qmljs_equal(ctx, left, right); - __qmljs_init_boolean(result, r); + if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) { + __qmljs_init_boolean(result, left->numberValue == right->numberValue); + } else if (left->type == STRING_TYPE && right->type == STRING_TYPE) { + __qmljs_init_boolean(result, __qmljs_string_equal(ctx, left->stringValue, right->stringValue)); + } else { + bool r = __qmljs_equal(ctx, left, right); + __qmljs_init_boolean(result, r); + } } inline void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *right) { - bool r = ! __qmljs_equal(ctx, left, right); - __qmljs_init_boolean(result, r); + if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) { + __qmljs_init_boolean(result, left->numberValue != right->numberValue); + } else if (left->type == STRING_TYPE && right->type != STRING_TYPE) { + __qmljs_init_boolean(result, __qmljs_string_equal(ctx, left->stringValue, right->stringValue)); + } else { + bool r = ! __qmljs_equal(ctx, left, right); + __qmljs_init_boolean(result, r); + } } inline void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right) -- cgit v1.2.3 From dc66a4633491c8f0631342f3141d6549035e7703 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 18 May 2012 17:37:23 +0200 Subject: Import Array from QtScript classic. --- qv4array_p.h | 353 +++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4ecmaobjects.cpp | 1 + v4.pro | 3 +- 3 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 qv4array_p.h diff --git a/qv4array_p.h b/qv4array_p.h new file mode 100644 index 0000000000..10cb683c20 --- /dev/null +++ b/qv4array_p.h @@ -0,0 +1,353 @@ +#ifndef QV4ARRAY_P_H +#define QV4ARRAY_P_H + +#include "qmljs_runtime.h" +#include +#include + +namespace QQmlJS { +namespace VM { + +class Array +{ +public: + inline Array(ExecutionEngine *engine); + inline Array(const Array &other); + inline ~Array(); + + inline Array &operator = (const Array &other); + + inline bool isEmpty() const; + inline uint size() const; + inline uint count() const; + inline Value at(uint index) const; + inline void assign(uint index, const Value &v); + inline void clear(); + inline void resize(uint size); + inline void concat(const Array &other); + inline Value pop(); + inline void sort(Context *context, const Value &comparefn); + inline void splice(double start, double deleteCount, + const QVector &items, + Array &other); + inline QList keys() const; + +private: + enum Mode { + VectorMode, + MapMode + }; + + ExecutionEngine *m_engine; + Mode m_mode; + int m_instances; + + union { + QMap *to_map; + QVector *to_vector; + }; +}; + +class ArrayElementLessThan +{ +public: + inline ArrayElementLessThan(Context *context, const Value &comparefn) + : m_context(context), m_comparefn(comparefn) {} + + inline bool operator()(const Value &v1, const Value &v2) const + { + if (v1.isUndefined()) + return false; + if (v2.isUndefined()) + return true; + if (!m_comparefn.isUndefined()) { + ArrayElementLessThan *that = const_cast(this); + Value args[] = { v1, v2 }; + Value result; + __qmljs_call_value(m_context, &result, 0, &m_comparefn, args, 2); + return result.toNumber(m_context) <= 0; + } + return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); + } + +private: + Context *m_context; + Value m_comparefn; +}; + +inline Array::Array(ExecutionEngine *engine): + m_engine(engine), + m_mode(VectorMode), + m_instances(0) +{ + to_vector = new QVector(); +} + +inline Array::Array(const Array &other): + m_engine(other.m_engine), + m_mode(other.m_mode), + m_instances(other.m_instances) +{ + if (m_mode == VectorMode) + to_vector = new QVector (*other.to_vector); + else + to_map = new QMap (*other.to_map); +} + +inline Array::~Array() +{ + if (m_mode == VectorMode) + delete to_vector; + else + delete to_map; +} + +inline Array &Array::operator = (const Array &other) +{ + m_engine = other.m_engine; + m_instances = other.m_instances; + if (m_mode != other.m_mode) { + if (m_mode == VectorMode) + delete to_vector; + else + delete to_map; + m_mode = other.m_mode; + + if (m_mode == VectorMode) + to_vector = new QVector (*other.to_vector); + else + to_map = new QMap (*other.to_map); + } + + if (m_mode == VectorMode) + *to_vector = *other.to_vector; + else + *to_map = *other.to_map; + + return *this; +} + +inline bool Array::isEmpty() const +{ + if (m_mode == VectorMode) + return to_vector->isEmpty(); + + return to_map->isEmpty(); +} + +inline uint Array::size() const +{ + if (m_mode == VectorMode) + return to_vector->size(); + + if (to_map->isEmpty()) + return 0; + + return (--to_map->constEnd()).key(); +} + +inline uint Array::count() const +{ + return size(); +} + +inline Value Array::at(uint index) const +{ + if (m_mode == VectorMode) { + if (index < uint(to_vector->size())) + return to_vector->at(index); + return Value(); + } else { + return to_map->value(index, Value()); + } +} + +inline void Array::assign(uint index, const Value &v) +{ + if (index >= size()) { + resize(index + 1); + // if (m_engine) + // m_engine->adjustBytesAllocated(sizeof(Value) * (size() - index)); + } + + const Value &oldv = at(index); + if (oldv.isObject() || oldv.isString()) + --m_instances; + + if (v.isObject() || v.isString()) + ++m_instances; + + if (m_mode == VectorMode) { + to_vector->replace(index, v); + } else { + if (v.isUndefined()) + to_map->remove(index); + else + to_map->insert(index, v); + } +} + +inline void Array::clear() +{ + m_instances = 0; + + if (m_mode == VectorMode) + to_vector->clear(); + + else + to_map->clear(); +} + +inline void Array::resize(uint s) +{ + const uint oldSize = size(); + if (oldSize == s) + return; + + const uint N = 10 * 1024; + + if (m_mode == VectorMode) { + if (s < N) { + to_vector->resize (s); + } else { + // switch to MapMode + QMap *m = new QMap(); + for (uint i = 0; i < oldSize; ++i) { + if (! to_vector->at(i).isUndefined()) + m->insert(i, to_vector->at(i)); + } + m->insert(s, Value()); + delete to_vector; + to_map = m; + m_mode = MapMode; + } + } + + else { + if (s < N) { + // switch to VectorMode + QVector *v = new QVector (s); + QMap::const_iterator it = to_map->constBegin(); + for ( ; (it != to_map->constEnd()) && (it.key() < s); ++it) + (*v) [it.key()] = it.value(); + delete to_map; + to_vector = v; + m_mode = VectorMode; + } else { + if (!to_map->isEmpty()) { + QMap::iterator it = --to_map->end(); + if (oldSize > s) { + // shrink + while ((it != to_map->end()) && (it.key() >= s)) { + it = to_map->erase(it); + --it; + } + } else { + if ((it.key() == oldSize) && !it.value().isUndefined()) + to_map->erase(it); + } + } + to_map->insert(s, Value()); + } + } +} + +inline void Array::concat(const Array &other) +{ + uint k = size(); + resize (k + other.size()); + for (uint i = 0; i < other.size(); ++i) { + Value v = other.at(i); + if (! v.isUndefined()) + continue; + + assign(k + i, v); + } +} + +inline Value Array::pop() +{ + if (isEmpty()) + return Value(); + + Value v; + + if (m_mode == VectorMode) + v = to_vector->last(); + else + v = *--to_map->end(); + + resize(size() - 1); + + return v; +} + +inline void Array::sort(Context *context, const Value &comparefn) +{ + ArrayElementLessThan lessThan(context, comparefn); + if (m_mode == VectorMode) { + qSort(to_vector->begin(), to_vector->end(), lessThan); + } else { + QList keys = to_map->keys(); + QList values = to_map->values(); + qStableSort(values.begin(), values.end(), lessThan); + const uint len = keys.size(); + for (uint i = 0; i < len; ++i) + to_map->insert(keys.at(i), values.at(i)); + } +} + +inline void Array::splice(double start, double deleteCount, + const QVector &items, + Array &other) +{ + const double len = size(); + if (start < 0) + start = qMax(len + start, double(0)); + else if (start > len) + start = len; + deleteCount = qMax(qMin(deleteCount, len - start), double(0)); + + const uint st = uint(start); + const uint dc = uint(deleteCount); + other.resize(dc); + + const uint itemsSize = uint(items.size()); + + if (m_mode == VectorMode) { + for (uint i = 0; i < dc; ++i) + other.assign(i, to_vector->at(st + i)); + if (itemsSize > dc) + to_vector->insert(st, itemsSize - dc, Value()); + else if (itemsSize < dc) + to_vector->remove(st, dc - itemsSize); + for (uint i = 0; i < itemsSize; ++i) + to_vector->replace(st + i, items.at(i)); + } else { + for (uint i = 0; i < dc; ++i) + other.assign(i, to_map->take(st + i)); + uint del = itemsSize - dc; + if (del != 0) { + for (uint i = st; i < uint(len); ++i) { + if (to_map->contains(i)) + to_map->insert(i + del, to_map->take(i)); + } + resize(uint(len) + del); + } + for (uint i = 0; i < itemsSize; ++i) + to_map->insert(st + i, items.at(i)); + } +} + +inline QList Array::keys() const +{ + if (m_mode == VectorMode) + return QList(); + else + return to_map->keys(); +} + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ARRAY_P_H diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index c253700596..c018cae691 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1,5 +1,6 @@ #include "qv4ecmaobjects_p.h" +#include "qv4array_p.h" #include #include #include diff --git a/v4.pro b/v4.pro index fc26789e3f..2991698d50 100644 --- a/v4.pro +++ b/v4.pro @@ -25,7 +25,8 @@ HEADERS += \ x86-codegen.h \ amd64-codegen.h \ qv4syntaxchecker_p.h \ - qv4ecmaobjects_p.h + qv4ecmaobjects_p.h \ + qv4array_p.h -- cgit v1.2.3 From e8bd50dd950f9f7a4bfb892cae0e12934419aca3 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 18 May 2012 22:04:26 +0200 Subject: Initial support for inplace assignments. --- qmljs_runtime.cpp | 2 +- qmljs_runtime.h | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4codegen.cpp | 8 +++--- qv4isel.cpp | 57 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 138 insertions(+), 6 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 40f0b96352..b83cc32cb1 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -438,7 +438,7 @@ void __qmljs_set_activation_property(Context *ctx, String *name, Value *value) if (Value *prop = ctx->lookup(name)) { *prop = *value; } else - ctx->activation.objectValue->put(name, *value); + ctx->engine->globalObject.objectValue->put(name, *value); } void __qmljs_copy_activation_property(Context *ctx, String *name, String *other) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 2dd4f85652..9837a4632d 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -555,6 +555,83 @@ inline void __qmljs_bit_and(Context *ctx, Value *result, const Value *left, cons __qmljs_init_number(result, lval & rval); } +inline void __qmljs_inplace_bit_and(Context *ctx, Value *result, double value) +{ + Value v; + __qmljs_init_number(&v, value); + __qmljs_bit_xor(ctx, result, result, &v); +} + +inline void __qmljs_inplace_bit_or(Context *ctx, Value *result, double value) +{ + Value v; + __qmljs_init_number(&v, value); + __qmljs_bit_or(ctx, result, result, &v); +} + +inline void __qmljs_inplace_bit_xor(Context *ctx, Value *result, double value) +{ + Value v; + __qmljs_init_number(&v, value); + __qmljs_bit_xor(ctx, result, result, &v); +} + +inline void __qmljs_inplace_add(Context *ctx, Value *result, double value) +{ + Value v; + __qmljs_init_number(&v, value); + __qmljs_add(ctx, result, result, &v); +} + +inline void __qmljs_inplace_sub(Context *ctx, Value *result, double value) +{ + Value v; + __qmljs_init_number(&v, value); + __qmljs_sub(ctx, result, result, &v); +} + +inline void __qmljs_inplace_mul(Context *ctx, Value *result, double value) +{ + Value v; + __qmljs_init_number(&v, value); + __qmljs_mul(ctx, result, result, &v); +} + +inline void __qmljs_inplace_div(Context *ctx, Value *result, double value) +{ + Value v; + __qmljs_init_number(&v, value); + __qmljs_div(ctx, result, result, &v); +} + +inline void __qmljs_inplace_mod(Context *ctx, Value *result, double value) +{ + Value v; + __qmljs_init_number(&v, value); + __qmljs_mod(ctx, result, result, &v); +} + +inline void __qmljs_inplace_shl(Context *ctx, Value *result, double value) +{ + Value v; + __qmljs_init_number(&v, value); + __qmljs_shl(ctx, result, result, &v); +} + +inline void __qmljs_inplace_shr(Context *ctx, Value *result, double value) +{ + Value v; + __qmljs_init_number(&v, value); + __qmljs_shr(ctx, result, result, &v); +} + +inline void __qmljs_inplace_ushr(Context *ctx, Value *result, double value) +{ + Value v; + __qmljs_init_number(&v, value); + __qmljs_ushr(ctx, result, result, &v); +} + inline void __qmljs_add(Context *ctx, Value *result, const Value *left, const Value *right) { Value pleft, pright; diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 9226749cc6..6abd11486b 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -920,11 +920,13 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::InplaceRightShift: case QSOperator::InplaceURightShift: case QSOperator::InplaceXor: { - move(*left, *right, baseOp(ast->op)); if (_expr.accept(nx)) { - // nothing to do + move(*left, *right, baseOp(ast->op)); } else { - _expr.code = *left; + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), *right); + move(*left, _block->TEMP(t), baseOp(ast->op)); + _expr.code = _block->TEMP(t); } break; } diff --git a/qv4isel.cpp b/qv4isel.cpp index 42e008465e..612060903c 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -676,26 +676,79 @@ void InstructionSelection::visitMove(IR::Move *s) // inplace assignment, e.g. x += 1, ++x, ... if (IR::Temp *t = s->target->asTemp()) { if (IR::Const *c = s->source->asConst()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); + amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); + + void (*op)(Context *, Value *, double); + switch (s->op) { + case QQmlJS::IR::OpBitAnd: op = __qmljs_inplace_bit_and; break; + case QQmlJS::IR::OpBitOr: op = __qmljs_inplace_bit_or; break; + case QQmlJS::IR::OpBitXor: op = __qmljs_inplace_bit_xor; break; + case QQmlJS::IR::OpAdd: op = __qmljs_inplace_add; break; + case QQmlJS::IR::OpSub: op = __qmljs_inplace_sub; break; + case QQmlJS::IR::OpMul: op = __qmljs_inplace_mul; break; + case QQmlJS::IR::OpDiv: op = __qmljs_inplace_div; break; + case QQmlJS::IR::OpMod: op = __qmljs_inplace_mod; break; + case QQmlJS::IR::OpLShift: op = __qmljs_inplace_shl; break; + case QQmlJS::IR::OpRShift: op = __qmljs_inplace_shr; break; + case QQmlJS::IR::OpURShift: op = __qmljs_inplace_ushr; break; + default: + Q_UNREACHABLE(); + break; + } + + amd64_call_code(_codePtr, op); return; } else if (IR::Temp *t2 = s->source->asTemp()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + loadTempAddress(AMD64_RDX, t); + loadTempAddress(AMD64_RCX, t2); + void (*op)(Context *, Value *, const Value *, const Value *); + switch (s->op) { + case QQmlJS::IR::OpBitAnd: op = __qmljs_bit_and; break; + case QQmlJS::IR::OpBitOr: op = __qmljs_bit_or; break; + case QQmlJS::IR::OpBitXor: op = __qmljs_bit_xor; break; + case QQmlJS::IR::OpAdd: op = __qmljs_add; break; + case QQmlJS::IR::OpSub: op = __qmljs_sub; break; + case QQmlJS::IR::OpMul: op = __qmljs_mul; break; + case QQmlJS::IR::OpDiv: op = __qmljs_div; break; + case QQmlJS::IR::OpMod: op = __qmljs_mod; break; + case QQmlJS::IR::OpLShift: op = __qmljs_shl; break; + case QQmlJS::IR::OpRShift: op = __qmljs_shr; break; + case QQmlJS::IR::OpURShift: op = __qmljs_ushr; break; + default: + Q_UNREACHABLE(); + break; + } + + amd64_call_code(_codePtr, op); return; } } else if (IR::Name *n = s->target->asName()) { if (IR::Const *c = s->source->asConst()) { + assert(!"wip"); return; } else if (IR::Temp *t = s->source->asTemp()) { + assert(!"wip"); return; } } else if (IR::Subscript *ss = s->target->asSubscript()) { if (IR::Const *c = s->source->asConst()) { + assert(!"wip"); return; } else if (IR::Temp *t = s->source->asTemp()) { + assert(!"wip"); return; } } else if (IR::Member *m = s->target->asMember()) { if (IR::Const *c = s->source->asConst()) { + assert(!"wip"); return; } else if (IR::Temp *t = s->source->asTemp()) { + assert(!"wip"); return; } } @@ -725,12 +778,12 @@ void InstructionSelection::visitCJump(IR::CJump *s) amd64_alu_reg_imm(_codePtr, X86_CMP, AMD64_RAX, BOOLEAN_TYPE); uchar *label1 = _codePtr; - amd64_branch32(_codePtr, X86_CC_NE, 0, 0); + amd64_branch8(_codePtr, X86_CC_NE, 0, 0); amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(Value, booleanValue), 1); uchar *label2 = _codePtr; - amd64_jump32(_codePtr, 0); + amd64_jump8(_codePtr, 0); amd64_patch(label1, _codePtr); amd64_call_code(_codePtr, __qmljs_to_boolean); -- cgit v1.2.3 From 945c988b4526c2363d80f530770d1595062518a2 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Sun, 20 May 2012 19:59:47 +0200 Subject: Initial support for Array objects. --- qmljs_objects.cpp | 56 ++++++++++++++- qmljs_objects.h | 15 ++++ qmljs_runtime.cpp | 70 +++++++++++++++++- qmljs_runtime.h | 16 ++++- qv4array.cpp | 20 ++++++ qv4array_p.h | 38 +++------- qv4codegen.cpp | 18 +++-- qv4ecmaobjects.cpp | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++- qv4ecmaobjects_p.h | 32 +++++++++ qv4isel.cpp | 25 +++++-- v4.pro | 3 +- 11 files changed, 449 insertions(+), 48 deletions(-) create mode 100644 qv4array.cpp diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index a9f445b010..eb4eb44fc1 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -102,6 +102,16 @@ bool Object::deleteProperty(String *name, bool flag) return false; } +Value *ArrayObject::getOwnProperty(String *name, PropertyAttributes *attributes) +{ + if (name->toQString() == QLatin1String("length")) { + length.numberValue = value.size(); + return &length; + } + + return Object::getOwnProperty(name, attributes); +} + bool FunctionObject::hasInstance(const Value &value) const { Q_UNUSED(value); @@ -188,28 +198,42 @@ ExecutionEngine::ExecutionEngine() rootContext = newContext(); rootContext->init(this); + objectPrototype.type = NULL_TYPE; + stringPrototype.type = NULL_TYPE; + numberPrototype.type = NULL_TYPE; + booleanPrototype.type = NULL_TYPE; + arrayPrototype.type = NULL_TYPE; + datePrototype.type = NULL_TYPE; + //functionPrototype.type = NULL_TYPE; + // // set up the global object // + String *prototype = identifier(QLatin1String("prototype")); + VM::Object *glo = newArgumentsObject(rootContext); __qmljs_init_object(&globalObject, glo); __qmljs_init_object(&rootContext->activation, glo); objectCtor = ObjectCtor::create(this); + objectCtor.objectValue->get(prototype, &objectPrototype); + stringCtor = StringCtor::create(this); numberCtor = NumberCtor::create(this); + booleanCtor = BooleanCtor::create(this); + arrayCtor = ArrayCtor::create(this); dateCtor = DateCtor::create(this); - String *prototype = identifier(QLatin1String("prototype")); - - objectCtor.objectValue->get(prototype, &objectPrototype); stringCtor.objectValue->get(prototype, &stringPrototype); numberCtor.objectValue->get(prototype, &numberPrototype); + booleanCtor.objectValue->get(prototype, &booleanPrototype); + arrayCtor.objectValue->get(prototype, &arrayPrototype); dateCtor.objectValue->get(prototype, &datePrototype); glo->put(identifier(QLatin1String("Object")), objectCtor); glo->put(identifier(QLatin1String("String")), stringCtor); glo->put(identifier(QLatin1String("Number")), numberCtor); + glo->put(identifier(QLatin1String("Array")), arrayCtor); glo->put(identifier(QLatin1String("Date")), dateCtor); glo->put(identifier(QLatin1String("Math")), Value::fromObject(newMathObject(rootContext))); } @@ -308,6 +332,32 @@ Object *ExecutionEngine::newBooleanPrototype(Context *ctx, FunctionObject *proto return booleanProto; } +Object *ExecutionEngine::newArrayObject() +{ + ArrayObject *object = new ArrayObject(); + object->prototype = arrayPrototype.objectValue; + return object; +} + +Object *ExecutionEngine::newArrayObject(const Array &value) +{ + ArrayObject *object = new ArrayObject(value); + object->prototype = arrayPrototype.objectValue; + return object; +} + +FunctionObject *ExecutionEngine::newArrayCtor(Context *ctx) +{ + return new ArrayCtor(ctx); +} + +Object *ExecutionEngine::newArrayPrototype(Context *ctx, FunctionObject *proto) +{ + Object *arrayProto = new ArrayPrototype(ctx, proto); + arrayProto->prototype = objectPrototype.objectValue; + return arrayProto; +} + Object *ExecutionEngine::newDateObject(const Value &value) { return new DateObject(value); diff --git a/qmljs_objects.h b/qmljs_objects.h index 3a06e7efeb..60829d23e0 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -2,6 +2,7 @@ #define QMLJS_OBJECTS_H #include "qmljs_runtime.h" +#include "qv4array_p.h" #include #include @@ -247,7 +248,12 @@ struct DateObject: Object { }; struct ArrayObject: Object { + Array value; + Value length; + ArrayObject() { length.type = NUMBER_TYPE; } + ArrayObject(const Array &value): value(value) { length.type = NUMBER_TYPE; } virtual ArrayObject *asArrayObject() { return this; } + virtual Value *getOwnProperty(String *name, PropertyAttributes *attributes); }; struct FunctionObject: Object { @@ -377,11 +383,15 @@ struct ExecutionEngine Value objectCtor; Value stringCtor; Value numberCtor; + Value booleanCtor; + Value arrayCtor; Value dateCtor; Value objectPrototype; Value stringPrototype; Value numberPrototype; + Value booleanPrototype; + Value arrayPrototype; Value datePrototype; QHash identifiers; @@ -412,6 +422,11 @@ struct ExecutionEngine FunctionObject *newBooleanCtor(Context *ctx); Object *newBooleanPrototype(Context *ctx, FunctionObject *proto); + Object *newArrayObject(); + Object *newArrayObject(const Array &value); + FunctionObject *newArrayCtor(Context *ctx); + Object *newArrayPrototype(Context *ctx, FunctionObject *proto); + Object *newDateObject(const Value &value); FunctionObject *newDateCtor(Context *ctx); Object *newDatePrototype(Context *ctx, FunctionObject *proto); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index b83cc32cb1..91b23edf5c 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -71,6 +71,13 @@ int Value::toInt32(double number) return (int) trunc(number); // ### } +uint Value::toUInt32(double number) +{ + if (! number || isnan(number) || isinf(number)) + return +0; + return (uint) trunc(number); // ### +} + int Value::toInteger(double number) { if (isnan(number)) @@ -344,7 +351,9 @@ void __qmljs_object_default_value(Context *ctx, Value *result, const Value *obje if (typeHint == NUMBER_HINT) qSwap(meth1, meth2); - Value *conv = object->asObject()->getProperty(meth1); + Object *oo = object->asObject(); + assert(oo != 0); + Value *conv = oo->getProperty(meth1); if (conv && conv->isFunctionObject()) { Value r; __qmljs_call_value(ctx, &r, object, conv, 0, 0); @@ -364,7 +373,7 @@ void __qmljs_object_default_value(Context *ctx, Value *result, const Value *obje } } - assert(!"type error"); + __qmljs_init_undefined(result); } void __qmljs_throw_type_error(Context *ctx, Value *result) @@ -433,6 +442,63 @@ void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR: object->objectValue->put(name, value, /*flag*/ 0); } +void __qmljs_get_element(Context *ctx, Value *result, Value *object, Value *index) +{ + if (object->isString() && index->isNumber()) { + const QString s = object->stringValue->toQString().mid(Value::toUInt32(index->numberValue), 1); + if (s.isNull()) + __qmljs_init_undefined(result); + else + *result = Value::fromString(ctx, s); + } else if (object->isArrayObject() && index->isNumber()) { + *result = object->asArrayObject()->value.at(Value::toUInt32(index->numberValue)); + } else { + String *name = index->toString(ctx); + + if (! object->isObject()) + __qmljs_to_object(ctx, object, object); + + object->objectValue->get(name, result); + } +} + +void __qmljs_set_element(Context *ctx, Value *object, Value *index, Value *value) +{ + if (object->isArrayObject() && index->isNumber()) { + object->asArrayObject()->value.assign(Value::toUInt32(index->numberValue), *value); + } else { + String *name = index->toString(ctx); + + if (! object->isObject()) + __qmljs_to_object(ctx, object, object); + + object->objectValue->put(name, *value, /*flags*/ 0); + } +} + +void __qmljs_set_element_number(Context *ctx, Value *object, Value *index, double number) +{ + Value v; + __qmljs_init_number(&v, number); + __qmljs_set_element(ctx, object, index, &v); +} + +void __qmljs_set_activation_element(Context *ctx, String *name, Value *index, Value *value) +{ + if (Value *base = ctx->lookup(name)) { + __qmljs_set_element(ctx, base, index, value); + } else { + assert(!"reference error"); + } +} + +void __qmljs_set_activation_element_number(Context *ctx, String *name, Value *index, double number) +{ + Value v; + __qmljs_init_number(&v, number); + __qmljs_set_activation_element(ctx, name, index, &v); +} + void __qmljs_set_activation_property(Context *ctx, String *name, Value *value) { if (Value *prop = ctx->lookup(name)) { diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 9837a4632d..cd571fd219 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -4,6 +4,7 @@ #include #include #include +#include namespace QQmlJS { @@ -111,6 +112,14 @@ void __qmljs_get_activation_property(Context *ctx, Value *result, String *name); void __qmljs_copy_activation_property(Context *ctx, String *name, String *other); void __qmljs_copy_property(Context *ctx, Value *target, String *name, Value *source, String *other); +void __qmljs_get_element(Context *ctx, Value *result, Value *object, Value *index); + +void __qmljs_set_element(Context *ctx, Value *object, Value *index, Value *value); +void __qmljs_set_element_number(Context *ctx, Value *object, Value *index, double number); + +void __qmljs_set_activation_element(Context *ctx, String *name, Value *index, Value *value); +void __qmljs_set_activation_element_number(Context *ctx, String *name, Value *index, double number); + // context void __qmljs_get_activation(Context *ctx, Value *result); void __qmljs_get_thisObject(Context *ctx, Value *result); @@ -222,6 +231,8 @@ struct Value { static int toInteger(double fromNumber); static int toInt32(double value); + static uint toUInt32(double value); + int toUInt16(Context *ctx); int toInt32(Context *ctx); uint toUInt32(Context *ctx); @@ -420,7 +431,10 @@ inline void __qmljs_to_string(Context *ctx, Value *result, const Value *value) case OBJECT_TYPE: { Value prim; __qmljs_to_primitive(ctx, &prim, value, STRING_HINT); - __qmljs_to_string(ctx, result, &prim); + if (prim.isPrimitive()) + __qmljs_to_string(ctx, result, &prim); + else + assert(!"type error"); break; } diff --git a/qv4array.cpp b/qv4array.cpp new file mode 100644 index 0000000000..9cdd06b23a --- /dev/null +++ b/qv4array.cpp @@ -0,0 +1,20 @@ + +#include "qv4array_p.h" +#include "qmljs_objects.h" + +using namespace QQmlJS::VM; + +bool ArrayElementLessThan::operator()(const Value &v1, const Value &v2) const +{ + if (v1.isUndefined()) + return false; + if (v2.isUndefined()) + return true; + if (!m_comparefn.isUndefined()) { + Value args[] = { v1, v2 }; + Value result; + __qmljs_call_value(m_context, &result, 0, &m_comparefn, args, 2); + return result.toNumber(m_context) <= 0; + } + return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); +} diff --git a/qv4array_p.h b/qv4array_p.h index 10cb683c20..a80c8a622f 100644 --- a/qv4array_p.h +++ b/qv4array_p.h @@ -11,7 +11,7 @@ namespace VM { class Array { public: - inline Array(ExecutionEngine *engine); + inline Array(); inline Array(const Array &other); inline ~Array(); @@ -38,7 +38,6 @@ private: MapMode }; - ExecutionEngine *m_engine; Mode m_mode; int m_instances; @@ -54,37 +53,21 @@ public: inline ArrayElementLessThan(Context *context, const Value &comparefn) : m_context(context), m_comparefn(comparefn) {} - inline bool operator()(const Value &v1, const Value &v2) const - { - if (v1.isUndefined()) - return false; - if (v2.isUndefined()) - return true; - if (!m_comparefn.isUndefined()) { - ArrayElementLessThan *that = const_cast(this); - Value args[] = { v1, v2 }; - Value result; - __qmljs_call_value(m_context, &result, 0, &m_comparefn, args, 2); - return result.toNumber(m_context) <= 0; - } - return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); - } + bool operator()(const Value &v1, const Value &v2) const; private: Context *m_context; Value m_comparefn; }; -inline Array::Array(ExecutionEngine *engine): - m_engine(engine), - m_mode(VectorMode), - m_instances(0) +inline Array::Array() + : m_mode(VectorMode) + , m_instances(0) { to_vector = new QVector(); } inline Array::Array(const Array &other): - m_engine(other.m_engine), m_mode(other.m_mode), m_instances(other.m_instances) { @@ -104,7 +87,6 @@ inline Array::~Array() inline Array &Array::operator = (const Array &other) { - m_engine = other.m_engine; m_instances = other.m_instances; if (m_mode != other.m_mode) { if (m_mode == VectorMode) @@ -166,8 +148,6 @@ inline void Array::assign(uint index, const Value &v) { if (index >= size()) { resize(index + 1); - // if (m_engine) - // m_engine->adjustBytesAllocated(sizeof(Value) * (size() - index)); } const Value &oldv = at(index); @@ -208,7 +188,7 @@ inline void Array::resize(uint s) if (m_mode == VectorMode) { if (s < N) { - to_vector->resize (s); + to_vector->resize (s); // ### init } else { // switch to MapMode QMap *m = new QMap(); @@ -226,7 +206,7 @@ inline void Array::resize(uint s) else { if (s < N) { // switch to VectorMode - QVector *v = new QVector (s); + QVector *v = new QVector (s, Value::undefinedValue()); QMap::const_iterator it = to_map->constBegin(); for ( ; (it != to_map->constEnd()) && (it.key() < s); ++it) (*v) [it.key()] = it.value(); @@ -259,9 +239,7 @@ inline void Array::concat(const Array &other) for (uint i = 0; i < other.size(); ++i) { Value v = other.at(i); if (! v.isUndefined()) - continue; - - assign(k + i, v); + assign(k + i, v); } } diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 6abd11486b..86dfe84084 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -322,13 +322,19 @@ IR::Expr *Codegen::member(IR::Expr *base, const QString *name) IR::Expr *Codegen::subscript(IR::Expr *base, IR::Expr *index) { - if (base->asTemp() || base->asName()) - return _block->SUBSCRIPT(base, index); - else { + if (! base->asTemp()) { const unsigned t = _block->newTemp(); - move(_block->TEMP(t), base); - return _block->SUBSCRIPT(_block->TEMP(t), index); + _block->MOVE(_block->TEMP(t), base); + base = _block->TEMP(t); + } + + if (! index->asTemp()) { + const unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), index); + index = _block->TEMP(t); } + + return _block->SUBSCRIPT(base, index); } IR::Expr *Codegen::argument(IR::Expr *expr) @@ -1056,7 +1062,7 @@ bool Codegen::visit(FunctionExpression *ast) bool Codegen::visit(IdentifierExpression *ast) { - if (! _function->needsActivation()) { + if (! _function->hasDirectEval) { int index = _env->findMember(ast->name.toString()); if (index != -1) { _expr.code = _block->TEMP(index); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index c018cae691..3b4890ce85 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -499,6 +499,12 @@ void ObjectCtor::call(Context *) ObjectPrototype::ObjectPrototype(Context *ctx, FunctionObject *ctor) { setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); + setProperty(ctx, QLatin1String("toString"), method_toString, 0); +} + +void ObjectPrototype::method_toString(Context *ctx) +{ + ctx->result = Value::fromString(ctx, "object"); } // @@ -573,7 +579,11 @@ QString StringPrototype::getThisString(Context *ctx) void StringPrototype::method_toString(Context *ctx) { - __qmljs_to_string(ctx, &ctx->result, &ctx->thisObject); + if (StringObject *o = ctx->thisObject.asStringObject()) { + ctx->result = o->value; + } else { + assert(!"type error"); + } } void StringPrototype::method_valueOf(Context *ctx) @@ -1063,6 +1073,198 @@ void BooleanPrototype::method_valueOf(Context *ctx) } } +// +// Array object +// +// +// Number object +// +Value ArrayCtor::create(ExecutionEngine *engine) +{ + Context *ctx = engine->rootContext; + FunctionObject *ctor = ctx->engine->newArrayCtor(ctx); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newArrayPrototype(ctx, ctor))); + return Value::fromObject(ctor); +} + +ArrayCtor::ArrayCtor(Context *scope) + : FunctionObject(scope) +{ +} + +void ArrayCtor::construct(Context *ctx) +{ + ctx->thisObject = Value::fromObject(ctx->engine->newArrayObject()); +} + +void ArrayCtor::call(Context *ctx) +{ + ctx->result = Value::fromObject(ctx->engine->newArrayObject()); +} + +ArrayPrototype::ArrayPrototype(Context *ctx, FunctionObject *ctor) +{ + setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); + setProperty(ctx, QLatin1String("toString"), method_toString, 0); + setProperty(ctx, QLatin1String("toLocalString"), method_toLocaleString, 0); + setProperty(ctx, QLatin1String("concat"), method_concat, 1); + setProperty(ctx, QLatin1String("join"), method_join, 1); + setProperty(ctx, QLatin1String("pop"), method_pop, 0); + setProperty(ctx, QLatin1String("push"), method_push, 1); + setProperty(ctx, QLatin1String("reverse"), method_reverse, 0); + setProperty(ctx, QLatin1String("shift"), method_shift, 0); + setProperty(ctx, QLatin1String("slice"), method_slice, 2); + setProperty(ctx, QLatin1String("sort"), method_sort, 1); + setProperty(ctx, QLatin1String("splice"), method_splice, 2); + setProperty(ctx, QLatin1String("unshift"), method_unshift, 1); +} + +void ArrayPrototype::method_toString(Context *ctx) +{ + method_join(ctx); +} + +void ArrayPrototype::method_toLocaleString(Context *ctx) +{ + method_toString(ctx); +} + +void ArrayPrototype::method_concat(Context *ctx) +{ + Array result; + + if (ArrayObject *instance = ctx->thisObject.asArrayObject()) + result = instance->value; + else { + QString v = ctx->thisObject.toString(ctx)->toQString(); + result.assign(0, Value::fromString(ctx, v)); + } + + for (uint i = 0; i < ctx->argumentCount; ++i) { + quint32 k = result.size(); + Value arg = ctx->argument(i); + + if (ArrayObject *elt = arg.asArrayObject()) + result.concat(elt->value); + + else + result.assign(k, Value::fromString(arg.toString(ctx))); + } + + ctx->result = Value::fromObject(ctx->engine->newArrayObject(result)); +} + +void ArrayPrototype::method_join(Context *ctx) +{ + Value arg = ctx->argument(0); + + QString r4; + if (arg.isUndefined()) + r4 = QLatin1String(","); + else + r4 = arg.toString(ctx)->toQString(); + + Value self = ctx->thisObject; + + Value *length = self.objectValue->getProperty(ctx->engine->identifier("length")); + double r1 = length ? length->toNumber(ctx) : 0; + quint32 r2 = Value::toUInt32(r1); + + static QSet visitedArrayElements; + + if (! r2 || visitedArrayElements.contains(self.objectValue)) { + ctx->result = Value::fromString(ctx, QString()); + return; + } + + // avoid infinite recursion + visitedArrayElements.insert(self.objectValue); + + QString R; + + if (ArrayObject *a = self.objectValue->asArrayObject()) { + for (uint i = 0; i < a->value.size(); ++i) { + if (! R.isEmpty()) + R += r4; + + Value e = a->value.at(i); + if (! (e.isUndefined() || e.isNull())) + R += e.toString(ctx)->toQString(); + } + } else { + // + // crazy! + // + Value *r6 = self.objectValue->getProperty(ctx->engine->identifier(QLatin1String("0"))); + if (r6 && !(r6->isUndefined() || r6->isNull())) + R = r6->toString(ctx)->toQString(); + + for (quint32 k = 1; k < r2; ++k) { + R += r4; + + String *name = Value::fromNumber(k).toString(ctx); + Value *r12 = self.objectValue->getProperty(name); + + if (r12 && ! (r12->isUndefined() || r12->isNull())) + R += r12->toString(ctx)->toQString(); + } + } + + visitedArrayElements.remove(self.objectValue); + ctx->result = Value::fromString(ctx, R); +} + +void ArrayPrototype::method_pop(Context *ctx) +{ + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + Value elt = instance->value.pop(); + ctx->result = elt; + } else { + String *id_length = ctx->engine->identifier(QLatin1String("length")); + Value *r1 = self.objectValue->getProperty(id_length); + quint32 r2 = r1 ? r1->toUInt32(ctx) : 0; + if (! r2) { + self.objectValue->put(id_length, Value::fromNumber(0)); + } else { + String *r6 = Value::fromNumber(r2 - 1).toString(ctx); + Value *r7 = self.objectValue->getProperty(r6); + self.objectValue->deleteProperty(r6, 0); + self.objectValue->put(id_length, Value::fromNumber(2 - 1)); + if (r7) + ctx->result = *r7; + } + } +} + +void ArrayPrototype::method_push(Context *ctx) +{ +} + +void ArrayPrototype::method_reverse(Context *ctx) +{ +} + +void ArrayPrototype::method_shift(Context *ctx) +{ +} + +void ArrayPrototype::method_slice(Context *ctx) +{ +} + +void ArrayPrototype::method_sort(Context *ctx) +{ +} + +void ArrayPrototype::method_splice(Context *ctx) +{ +} + +void ArrayPrototype::method_unshift(Context *ctx) +{ +} + // // Date object // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index abcf390dfb..cb6339e1fb 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -19,6 +19,9 @@ struct ObjectCtor: FunctionObject struct ObjectPrototype: Object { ObjectPrototype(Context *ctx, FunctionObject *ctor); + +protected: + static void method_toString(Context *ctx); }; struct StringCtor: FunctionObject @@ -102,6 +105,35 @@ protected: static void method_valueOf(Context *ctx); }; +struct ArrayCtor: FunctionObject +{ + static Value create(ExecutionEngine *engine); + + ArrayCtor(Context *scope); + + virtual void construct(Context *ctx); + virtual void call(Context *ctx); +}; + +struct ArrayPrototype: Object +{ + ArrayPrototype(Context *ctx, FunctionObject *ctor); + +protected: + static void method_toString(Context *ctx); + static void method_toLocaleString(Context *ctx); + static void method_concat(Context *ctx); + static void method_join(Context *ctx); + static void method_pop(Context *ctx); + static void method_push(Context *ctx); + static void method_reverse(Context *ctx); + static void method_shift(Context *ctx); + static void method_slice(Context *ctx); + static void method_sort(Context *ctx); + static void method_splice(Context *ctx); + static void method_unshift(Context *ctx); +}; + struct DateCtor: FunctionObject { static Value create(ExecutionEngine *engine); diff --git a/qv4isel.cpp b/qv4isel.cpp index 612060903c..505f765ffb 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -506,9 +506,14 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_call_code(_codePtr, __qmljs_get_property); return; } - assert(!"todo"); + assert(!"wip"); + return; } else if (IR::Subscript *ss = s->source->asSubscript()) { - qWarning() << "TODO load subscript"; + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + loadTempAddress(AMD64_RDX, ss->base->asTemp()); + loadTempAddress(AMD64_RCX, ss->index->asTemp()); + amd64_call_code(_codePtr, __qmljs_get_element); return; } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { @@ -527,6 +532,7 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_call_code(_codePtr, op); return; } else if (IR::Const *c = u->expr->asConst()) { + assert(!"wip"); return; } } else if (IR::Binop *b = s->source->asBinop()) { @@ -666,9 +672,19 @@ void InstructionSelection::visitMove(IR::Move *s) } } } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (IR::Temp *t = s->source->asTemp()) { + if (IR::Temp *t2 = s->source->asTemp()) { + loadTempAddress(AMD64_RSI, ss->base->asTemp()); + loadTempAddress(AMD64_RDX, ss->index->asTemp()); + loadTempAddress(AMD64_RCX, t2); + amd64_call_code(_codePtr, __qmljs_set_element); return; } else if (IR::Const *c = s->source->asConst()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, ss->base->asTemp()); + loadTempAddress(AMD64_RDX, ss->index->asTemp()); + amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); + amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); + amd64_call_code(_codePtr, __qmljs_set_element_number); return; } } @@ -704,7 +720,8 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Temp *t2 = s->source->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); - loadTempAddress(AMD64_RDX, t); + amd64_mov_reg_reg(_codePtr, AMD64_RDX, AMD64_RSI, 8); + // loadTempAddress(AMD64_RDX, t); loadTempAddress(AMD64_RCX, t2); void (*op)(Context *, Value *, const Value *, const Value *); switch (s->op) { diff --git a/v4.pro b/v4.pro index 2991698d50..4198533ae3 100644 --- a/v4.pro +++ b/v4.pro @@ -14,7 +14,8 @@ SOURCES += main.cpp \ qmljs_objects.cpp \ qv4isel.cpp \ qv4syntaxchecker.cpp \ - qv4ecmaobjects.cpp + qv4ecmaobjects.cpp \ + qv4array.cpp HEADERS += \ qv4codegen_p.h \ -- cgit v1.2.3 From 6448ae090f89cf0489b9bc22dbd6782876e6b1d1 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 10:09:41 +0200 Subject: Fix prototypes and implemented isel for constructValue. --- qmljs_objects.cpp | 31 ++++++++++++++++++++++++------- qv4isel.cpp | 36 ++++++++++++++++++++++++++++++++++++ qv4isel_p.h | 1 + 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index eb4eb44fc1..eace571ae4 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -263,7 +263,10 @@ FunctionObject *ExecutionEngine::newScriptFunction(Context *scope, IR::Function Object *ExecutionEngine::newObject() { - return new Object(); + Object *object = new Object(); + if (objectPrototype.isObject()) + object->prototype = objectPrototype.objectValue; + return object; } FunctionObject *ExecutionEngine::newObjectCtor(Context *ctx) @@ -283,7 +286,10 @@ String *ExecutionEngine::newString(const QString &s) Object *ExecutionEngine::newStringObject(const Value &value) { - return new StringObject(value); + StringObject *object = new StringObject(value); + if (stringPrototype.isObject()) + object->prototype = stringPrototype.objectValue; + return object; } FunctionObject *ExecutionEngine::newStringCtor(Context *ctx) @@ -300,7 +306,10 @@ Object *ExecutionEngine::newStringPrototype(Context *ctx, FunctionObject *proto) Object *ExecutionEngine::newNumberObject(const Value &value) { - return new NumberObject(value); + NumberObject *object = new NumberObject(value); + if (numberPrototype.isObject()) + object->prototype = numberPrototype.objectValue; + return object; } FunctionObject *ExecutionEngine::newNumberCtor(Context *ctx) @@ -317,7 +326,10 @@ Object *ExecutionEngine::newNumberPrototype(Context *ctx, FunctionObject *proto) Object *ExecutionEngine::newBooleanObject(const Value &value) { - return new BooleanObject(value); + Object *object = new BooleanObject(value); + if (booleanPrototype.isObject()) + object->prototype = booleanPrototype.objectValue; + return object; } FunctionObject *ExecutionEngine::newBooleanCtor(Context *ctx) @@ -335,14 +347,16 @@ Object *ExecutionEngine::newBooleanPrototype(Context *ctx, FunctionObject *proto Object *ExecutionEngine::newArrayObject() { ArrayObject *object = new ArrayObject(); - object->prototype = arrayPrototype.objectValue; + if (arrayPrototype.isObject()) + object->prototype = arrayPrototype.objectValue; return object; } Object *ExecutionEngine::newArrayObject(const Array &value) { ArrayObject *object = new ArrayObject(value); - object->prototype = arrayPrototype.objectValue; + if (arrayPrototype.isObject()) + object->prototype = arrayPrototype.objectValue; return object; } @@ -360,7 +374,10 @@ Object *ExecutionEngine::newArrayPrototype(Context *ctx, FunctionObject *proto) Object *ExecutionEngine::newDateObject(const Value &value) { - return new DateObject(value); + Object *object = new DateObject(value); + if (datePrototype.isObject()) + object->prototype = datePrototype.objectValue; + return object; } FunctionObject *ExecutionEngine::newDateCtor(Context *ctx) diff --git a/qv4isel.cpp b/qv4isel.cpp index 505f765ffb..47b9a77cb7 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -345,6 +345,39 @@ void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) amd64_call_code(_codePtr, __qmljs_construct_property); } +void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) +{ + IR::Temp *baseTemp = call->base->asTemp(); + assert(baseTemp != 0); + + int argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) { + ++argc; + } + + int i = 0; + for (IR::ExprList *it = call->args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); + loadTempAddress(AMD64_RSI, arg); + amd64_call_code(_codePtr, __qmljs_copy); + } + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context + + if (result) + loadTempAddress(AMD64_RSI, result); + else + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RDX, AMD64_RDX); + loadTempAddress(AMD64_RCX, baseTemp); + amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); + amd64_call_code(_codePtr, __qmljs_construct_value); +} + void InstructionSelection::visitExp(IR::Exp *s) { if (IR::Call *c = s->expr->asCall()) { @@ -495,6 +528,9 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (ctor->base->asMember()) { constructProperty(ctor, t); return; + } else if (ctor->base->asTemp()) { + constructValue(ctor, t); + return; } } else if (IR::Member *m = s->source->asMember()) { //__qmljs_get_property(ctx, result, object, name); diff --git a/qv4isel_p.h b/qv4isel_p.h index ac6092d5a5..c6da581683 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -25,6 +25,7 @@ protected: void constructActivationProperty(IR::New *call, IR::Temp *result); void constructProperty(IR::New *ctor, IR::Temp *result); void callValue(IR::Call *call, IR::Temp *result); + void constructValue(IR::New *call, IR::Temp *result); virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); -- cgit v1.2.3 From 46e69ff9f9a6c9dd5c69995375cd92b0bdbeaf94 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 10:10:37 +0200 Subject: Simple test app for the Array object. --- tests/array.1.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/array.1.js diff --git a/tests/array.1.js b/tests/array.1.js new file mode 100644 index 0000000000..508608871e --- /dev/null +++ b/tests/array.1.js @@ -0,0 +1,32 @@ + +var d1 = new Date() +a = [] + +for (var i = 0; i < 10000; i = i + 1) + a[i] = i + +for (var i = 0; i < 10000; i = i + 1) + if (a[i] != i) + print("KO", i) + +print("done in", new Date - d1) + +var s = "some cool stuff" +for (var i = 0; i < 15; ++i) { + print(s[i]) +} + +var xx = [1,2] +xx[0] = 1 +print(xx.length) +print(xx) + +print("yy", xx.concat(xx)) +var pp = [1,2,3,4] +var aa = pp.concat([5,6,7]); +print(aa[0], aa[1], aa[2], aa[3], aa[4], aa[5], aa.length) +print(Array.prototype.concat("hello", true, 123, null, "world")) +aa.pop() +print(aa) +aa.pop() +print(aa) -- cgit v1.2.3 From 84467cf606fc96ed67e6cc543d30c5d32140e110 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 10:26:13 +0200 Subject: Fix constructor calls --- main.cpp | 2 +- qv4isel.cpp | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/main.cpp b/main.cpp index 60870d021d..354485c2df 100644 --- a/main.cpp +++ b/main.cpp @@ -114,12 +114,12 @@ int main(int argc, char *argv[]) QStringList args = app.arguments(); args.removeFirst(); - QQmlJS::VM::ExecutionEngine vm; foreach (const QString &fn, args) { QFile file(fn); if (file.open(QFile::ReadOnly)) { const QString code = QString::fromUtf8(file.readAll()); file.close(); + QQmlJS::VM::ExecutionEngine vm; evaluate(&vm, fn, code); } } diff --git a/qv4isel.cpp b/qv4isel.cpp index 47b9a77cb7..dbfaad0ae4 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -371,10 +371,9 @@ void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) else amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RDX, AMD64_RDX); - loadTempAddress(AMD64_RCX, baseTemp); - amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); + loadTempAddress(AMD64_RDX, baseTemp); + amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); amd64_call_code(_codePtr, __qmljs_construct_value); } -- cgit v1.2.3 From 04a8406c569d9e9b682cb2e7fcf78d806503f135 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 13:21:35 +0200 Subject: Improve the Array prototype --- qv4array_p.h | 33 ++++++++++++++++----- qv4ecmaobjects.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 107 insertions(+), 10 deletions(-) diff --git a/qv4array_p.h b/qv4array_p.h index a80c8a622f..d820ce591c 100644 --- a/qv4array_p.h +++ b/qv4array_p.h @@ -26,6 +26,7 @@ public: inline void resize(uint size); inline void concat(const Array &other); inline Value pop(); + inline Value takeFirst(); inline void sort(Context *context, const Value &comparefn); inline void splice(double start, double deleteCount, const QVector &items, @@ -138,9 +139,9 @@ inline Value Array::at(uint index) const if (m_mode == VectorMode) { if (index < uint(to_vector->size())) return to_vector->at(index); - return Value(); + return Value::undefinedValue(); } else { - return to_map->value(index, Value()); + return to_map->value(index, Value::undefinedValue()); } } @@ -188,7 +189,9 @@ inline void Array::resize(uint s) if (m_mode == VectorMode) { if (s < N) { - to_vector->resize (s); // ### init + to_vector->resize(s); + for (uint i = oldSize; i < s; ++i) + assign(i, Value::undefinedValue()); } else { // switch to MapMode QMap *m = new QMap(); @@ -196,7 +199,7 @@ inline void Array::resize(uint s) if (! to_vector->at(i).isUndefined()) m->insert(i, to_vector->at(i)); } - m->insert(s, Value()); + m->insert(s, Value::undefinedValue()); delete to_vector; to_map = m; m_mode = MapMode; @@ -227,7 +230,7 @@ inline void Array::resize(uint s) to_map->erase(it); } } - to_map->insert(s, Value()); + to_map->insert(s, Value::undefinedValue()); } } } @@ -246,7 +249,7 @@ inline void Array::concat(const Array &other) inline Value Array::pop() { if (isEmpty()) - return Value(); + return Value::undefinedValue(); Value v; @@ -260,6 +263,22 @@ inline Value Array::pop() return v; } +inline Value Array::takeFirst() +{ + if (isEmpty()) + return Value::undefinedValue(); + + Value v; + if (m_mode == VectorMode) { + v = to_vector->first(); + to_vector->remove(0, 1); + } else { + v = *to_map->begin(); + to_map->erase(to_map->begin()); + } + return v; +} + inline void Array::sort(Context *context, const Value &comparefn) { ArrayElementLessThan lessThan(context, comparefn); @@ -296,7 +315,7 @@ inline void Array::splice(double start, double deleteCount, for (uint i = 0; i < dc; ++i) other.assign(i, to_vector->at(st + i)); if (itemsSize > dc) - to_vector->insert(st, itemsSize - dc, Value()); + to_vector->insert(st, itemsSize - dc, Value::undefinedValue()); else if (itemsSize < dc) to_vector->remove(st, dc - itemsSize); for (uint i = 0; i < itemsSize; ++i) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 3b4890ce85..cb06b4605b 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1094,12 +1094,31 @@ ArrayCtor::ArrayCtor(Context *scope) void ArrayCtor::construct(Context *ctx) { - ctx->thisObject = Value::fromObject(ctx->engine->newArrayObject()); + call(ctx); + ctx->thisObject = ctx->result; } void ArrayCtor::call(Context *ctx) { - ctx->result = Value::fromObject(ctx->engine->newArrayObject()); + Array value; + if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { + double size = ctx->argument(0).numberValue; + quint32 isize = Value::toUInt32(size); + + if (size != double(isize)) { + assert(!"invlaid array length exception"); + return; + } + + qDebug() << "size:" << isize; + value.resize(isize); + } else { + for (size_t i = 0; i < ctx->argumentCount; ++i) { + value.assign(i, ctx->argument(i)); + } + } + + ctx->result = Value::fromObject(ctx->engine->newArrayObject(value)); } ArrayPrototype::ArrayPrototype(Context *ctx, FunctionObject *ctor) @@ -1184,7 +1203,7 @@ void ArrayPrototype::method_join(Context *ctx) if (ArrayObject *a = self.objectValue->asArrayObject()) { for (uint i = 0; i < a->value.size(); ++i) { - if (! R.isEmpty()) + if (i) R += r4; Value e = a->value.at(i); @@ -1239,30 +1258,89 @@ void ArrayPrototype::method_pop(Context *ctx) void ArrayPrototype::method_push(Context *ctx) { + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + uint pos = instance->value.size(); + for (size_t i = 0; i < ctx->argumentCount; ++i) { + Value val = ctx->argument(i); + instance->value.assign(pos++, val); + } + ctx->result = Value::fromNumber(pos); + } else { + String *id_length = ctx->engine->identifier(QLatin1String("length")); + Value *r1 = self.objectValue->getProperty(id_length); + quint32 n = r1 ? r1->toUInt32(ctx) : 0; + for (int index = 0; index < ctx->argumentCount; ++index, ++n) { + Value r3 = ctx->argument(index); + String *name = Value::fromNumber(n).toString(ctx); + self.objectValue->put(name, r3); + } + Value r = Value::fromNumber(n); + self.objectValue->put(id_length, r); + ctx->result = r; + } } void ArrayPrototype::method_reverse(Context *ctx) { + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + int lo = 0, hi = instance->value.count() - 1; + + for (; lo < hi; ++lo, --hi) { + Value tmp = instance->value.at(lo); + instance->value.assign(lo, instance->value.at(hi)); + instance->value.assign(hi, tmp); + } + } else { + assert(!"generic implementation of Array.prototype.reverse"); + } } void ArrayPrototype::method_shift(Context *ctx) { + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + ctx->result = instance->value.takeFirst(); + } else { + assert(!"generic implementation of Array.prototype.reverse"); + } } void ArrayPrototype::method_slice(Context *ctx) { + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + } else { + assert(!"generic implementation of Array.prototype.slice"); + } } void ArrayPrototype::method_sort(Context *ctx) { + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + } else { + assert(!"generic implementation of Array.prototype.sort"); + } } void ArrayPrototype::method_splice(Context *ctx) { + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + } else { + assert(!"generic implementation of Array.prototype.splice"); + } } void ArrayPrototype::method_unshift(Context *ctx) { + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + } else { + assert(!"generic implementation of Array.prototype.unshift"); + } } // -- cgit v1.2.3 From f915c6d5dab5357971891bb083e8a3c5c0b15932 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 13:22:52 +0200 Subject: Remove debug --- qv4ecmaobjects.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index cb06b4605b..daee5e9fc7 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1110,7 +1110,6 @@ void ArrayCtor::call(Context *ctx) return; } - qDebug() << "size:" << isize; value.resize(isize); } else { for (size_t i = 0; i < ctx->argumentCount; ++i) { -- cgit v1.2.3 From 9897c5aeaaea9b8e701254ec3c445fc6c84b00f7 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 13:24:03 +0200 Subject: Made the ArrayPrototype an ArrayObject. --- qv4ecmaobjects_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index cb6339e1fb..0aaeee8970 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -115,7 +115,7 @@ struct ArrayCtor: FunctionObject virtual void call(Context *ctx); }; -struct ArrayPrototype: Object +struct ArrayPrototype: ArrayObject { ArrayPrototype(Context *ctx, FunctionObject *ctor); -- cgit v1.2.3 From d72f4a477c7586d317cc9dc7c1372d67b8f7cece Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 14:11:12 +0200 Subject: Implement Array.prototype.sort --- qv4ecmaobjects.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index daee5e9fc7..a6e923934f 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1318,7 +1318,10 @@ void ArrayPrototype::method_slice(Context *ctx) void ArrayPrototype::method_sort(Context *ctx) { Value self = ctx->thisObject; + Value comparefn = ctx->argument(0); if (ArrayObject *instance = self.asArrayObject()) { + instance->value.sort(ctx, comparefn); + ctx->result = ctx->thisObject; } else { assert(!"generic implementation of Array.prototype.sort"); } -- cgit v1.2.3 From 9ac0a86eeffd27887311ca5150eef8d3472c589d Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 14:21:57 +0200 Subject: Implement Array.prototype.splice --- qv4ecmaobjects.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index a6e923934f..54bd0eb67e 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1329,8 +1329,21 @@ void ArrayPrototype::method_sort(Context *ctx) void ArrayPrototype::method_splice(Context *ctx) { + if (ctx->argumentCount < 2) + return; + + double start = ctx->argument(0).toInteger(ctx); + double deleteCount = ctx->argument(1).toInteger(ctx); + Value a = Value::fromObject(ctx->engine->newArrayObject()); Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { + QVector items; + for (size_t i = 2; i < ctx->argumentCount; ++i) + items << ctx->argument(i); + ArrayObject *otherInstance = a.asArrayObject(); + Q_ASSERT(otherInstance); + instance->value.splice(start, deleteCount, items, otherInstance->value); + ctx->result = a; } else { assert(!"generic implementation of Array.prototype.splice"); } -- cgit v1.2.3 From d5f6dfa8a90f37389ead1e7479bbc175568e3dc9 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 14:27:13 +0200 Subject: Implement Array.prototype.slice --- qv4ecmaobjects.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 54bd0eb67e..35ed781dbf 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1308,11 +1308,28 @@ void ArrayPrototype::method_shift(Context *ctx) void ArrayPrototype::method_slice(Context *ctx) { + // ### TODO implement the fast non-generic version of slice. + + Array result; + Value start = ctx->argument(0); + Value end = ctx->argument(1); Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - } else { - assert(!"generic implementation of Array.prototype.slice"); + String *id_length = ctx->engine->identifier(QLatin1String("length")); + Value *l = self.objectValue->getProperty(id_length); + double r2 = l ? l->toNumber(ctx) : 0; + quint32 r3 = Value::toUInt32(r2); + qint32 r4 = qint32(start.toInteger(ctx)); + quint32 r5 = r4 < 0 ? qMax(quint32(r3 + r4), quint32(0)) : qMin(quint32(r4), r3); + quint32 k = r5; + qint32 r7 = end.isUndefined() ? r3 : qint32 (end.toInteger(ctx)); + quint32 r8 = r7 < 0 ? qMax(quint32(r3 + r7), quint32(0)) : qMin(quint32(r7), r3); + quint32 n = 0; + for (; k < r8; ++k) { + String *r11 = Value::fromNumber(k).toString(ctx); + if (Value *v = self.objectValue->getProperty(r11)) + result.assign(n++, *v); } + ctx->result = Value::fromObject(ctx->engine->newArrayObject(result)); } void ArrayPrototype::method_sort(Context *ctx) -- cgit v1.2.3 From 0388eb704aa7f3e03ef8e4bc1ca0ec4fe85f19d3 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 14:57:02 +0200 Subject: Initial work on the Function prototype. --- qmljs_objects.cpp | 24 ++++++++++++++++ qmljs_objects.h | 6 ++++ qv4ecmaobjects.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- qv4ecmaobjects_p.h | 21 ++++++++++++++ 4 files changed, 130 insertions(+), 3 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index eace571ae4..4f26d2e311 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -218,6 +218,9 @@ ExecutionEngine::ExecutionEngine() objectCtor = ObjectCtor::create(this); objectCtor.objectValue->get(prototype, &objectPrototype); + functionCtor = FunctionCtor::create(this); + functionCtor.objectValue->get(prototype, &functionPrototype); + stringCtor = StringCtor::create(this); numberCtor = NumberCtor::create(this); booleanCtor = BooleanCtor::create(this); @@ -234,6 +237,7 @@ ExecutionEngine::ExecutionEngine() glo->put(identifier(QLatin1String("String")), stringCtor); glo->put(identifier(QLatin1String("Number")), numberCtor); glo->put(identifier(QLatin1String("Array")), arrayCtor); + glo->put(identifier(QLatin1String("Function")), functionCtor); glo->put(identifier(QLatin1String("Date")), dateCtor); glo->put(identifier(QLatin1String("Math")), Value::fromObject(newMathObject(rootContext))); } @@ -344,6 +348,26 @@ Object *ExecutionEngine::newBooleanPrototype(Context *ctx, FunctionObject *proto return booleanProto; } +Object *ExecutionEngine::newFunctionObject(Context *ctx) +{ + Object *object = new FunctionObject(ctx); + if (functionPrototype.isObject()) + object->prototype = functionPrototype.objectValue; + return object; +} + +FunctionObject *ExecutionEngine::newFunctionCtor(Context *ctx) +{ + return new FunctionCtor(ctx); +} + +Object *ExecutionEngine::newFunctionPrototype(Context *ctx, FunctionObject *proto) +{ + Object *functionProto = new FunctionPrototype(ctx, proto); + functionProto->prototype = objectPrototype.objectValue; + return functionProto; +} + Object *ExecutionEngine::newArrayObject() { ArrayObject *object = new ArrayObject(); diff --git a/qmljs_objects.h b/qmljs_objects.h index 60829d23e0..562da2f81c 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -385,6 +385,7 @@ struct ExecutionEngine Value numberCtor; Value booleanCtor; Value arrayCtor; + Value functionCtor; Value dateCtor; Value objectPrototype; @@ -392,6 +393,7 @@ struct ExecutionEngine Value numberPrototype; Value booleanPrototype; Value arrayPrototype; + Value functionPrototype; Value datePrototype; QHash identifiers; @@ -422,6 +424,10 @@ struct ExecutionEngine FunctionObject *newBooleanCtor(Context *ctx); Object *newBooleanPrototype(Context *ctx, FunctionObject *proto); + Object *newFunctionObject(Context *ctx); + FunctionObject *newFunctionCtor(Context *ctx); + Object *newFunctionPrototype(Context *ctx, FunctionObject *proto); + Object *newArrayObject(); Object *newArrayObject(const Array &value); FunctionObject *newArrayCtor(Context *ctx); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 35ed781dbf..4744faa1a6 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1076,9 +1076,6 @@ void BooleanPrototype::method_valueOf(Context *ctx) // // Array object // -// -// Number object -// Value ArrayCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; @@ -1375,6 +1372,85 @@ void ArrayPrototype::method_unshift(Context *ctx) } } +// +// Function object +// +// +// Array object +// +Value FunctionCtor::create(ExecutionEngine *engine) +{ + Context *ctx = engine->rootContext; + FunctionObject *ctor = ctx->engine->newFunctionCtor(ctx); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newFunctionPrototype(ctx, ctor))); + return Value::fromObject(ctor); +} + +FunctionCtor::FunctionCtor(Context *scope) + : FunctionObject(scope) +{ +} + +void FunctionCtor::construct(Context *ctx) +{ + Q_UNIMPLEMENTED(); +} + +void FunctionCtor::call(Context *ctx) +{ + Q_UNIMPLEMENTED(); +} + +FunctionPrototype::FunctionPrototype(Context *ctx, FunctionObject *ctor) + : FunctionObject(ctx) +{ + setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); + setProperty(ctx, QLatin1String("toString"), method_toString, 0); + setProperty(ctx, QLatin1String("apply"), method_apply, 0); + setProperty(ctx, QLatin1String("call"), method_call, 0); + setProperty(ctx, QLatin1String("bind"), method_bind, 0); +} + +void FunctionPrototype::method_toString(Context *ctx) +{ + if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { + ctx->result = Value::fromString(ctx, QLatin1String("function() { [code] }")); + } else { + assert(!"type error"); + } +} + +void FunctionPrototype::method_apply(Context *ctx) +{ + if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { + Q_UNIMPLEMENTED(); + } else { + assert(!"type error"); + } +} + +void FunctionPrototype::method_call(Context *ctx) +{ + if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { + Value thisArg = ctx->argument(0); + QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); + if (ctx->argumentCount) + qCopy(ctx->arguments + 1, ctx->arguments + ctx->argumentCount, args.begin()); + __qmljs_call_value(ctx, &ctx->result, &thisArg, &ctx->thisObject, args.data(), args.size()); + } else { + assert(!"type error"); + } +} + +void FunctionPrototype::method_bind(Context *ctx) +{ + if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { + Q_UNIMPLEMENTED(); + } else { + assert(!"type error"); + } +} + // // Date object // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 0aaeee8970..a1a55284a4 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -134,6 +134,27 @@ protected: static void method_unshift(Context *ctx); }; +struct FunctionCtor: FunctionObject +{ + static Value create(ExecutionEngine *engine); + + FunctionCtor(Context *scope); + + virtual void construct(Context *ctx); + virtual void call(Context *ctx); +}; + +struct FunctionPrototype: FunctionObject +{ + FunctionPrototype(Context *ctx, FunctionObject *ctor); + +protected: + static void method_toString(Context *ctx); + static void method_apply(Context *ctx); + static void method_call(Context *ctx); + static void method_bind(Context *ctx); +}; + struct DateCtor: FunctionObject { static Value create(ExecutionEngine *engine); -- cgit v1.2.3 From 1e6c30d82a46650138f1b05680737244520a21c8 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 15:08:35 +0200 Subject: Test the Function.prototype.call --- tests/fun.4.js | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/fun.4.js diff --git a/tests/fun.4.js b/tests/fun.4.js new file mode 100644 index 0000000000..fe0b7049f8 --- /dev/null +++ b/tests/fun.4.js @@ -0,0 +1,8 @@ + +function foo(a,b,c) { + print("hello",a,b,c) +} + +foo.call = Function.prototype.call +foo.call(null, 1,2,3) + -- cgit v1.2.3 From 88d8a47305c5aea9e5d17ada25df03b8122679aa Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 15:16:19 +0200 Subject: Set the prototype of script and native functions. --- qmljs_objects.cpp | 10 ++++++++-- tests/fun.4.js | 1 - 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 4f26d2e311..35a90ae547 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -257,12 +257,18 @@ String *ExecutionEngine::identifier(const QString &s) FunctionObject *ExecutionEngine::newNativeFunction(Context *scope, void (*code)(Context *)) { - return new NativeFunction(scope, code); + NativeFunction *f = new NativeFunction(scope, code); + if (scope->engine->functionPrototype.isObject()) + f->prototype = scope->engine->functionPrototype.objectValue; + return f; } FunctionObject *ExecutionEngine::newScriptFunction(Context *scope, IR::Function *function) { - return new ScriptFunction(scope, function); + ScriptFunction *f = new ScriptFunction(scope, function); + if (scope->engine->functionPrototype.isObject()) + f->prototype = scope->engine->functionPrototype.objectValue; + return f; } Object *ExecutionEngine::newObject() diff --git a/tests/fun.4.js b/tests/fun.4.js index fe0b7049f8..1333fadf18 100644 --- a/tests/fun.4.js +++ b/tests/fun.4.js @@ -3,6 +3,5 @@ function foo(a,b,c) { print("hello",a,b,c) } -foo.call = Function.prototype.call foo.call(null, 1,2,3) -- cgit v1.2.3 From c5877cbbbee2c9f50231239095b48a85ebd3fa8a Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 15:22:22 +0200 Subject: More work on the Array prototype. --- qv4ecmaobjects.cpp | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4ecmaobjects_p.h | 9 +++++ 2 files changed, 108 insertions(+) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 4744faa1a6..01944f0ca8 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1132,6 +1132,15 @@ ArrayPrototype::ArrayPrototype(Context *ctx, FunctionObject *ctor) setProperty(ctx, QLatin1String("sort"), method_sort, 1); setProperty(ctx, QLatin1String("splice"), method_splice, 2); setProperty(ctx, QLatin1String("unshift"), method_unshift, 1); + setProperty(ctx, QLatin1String("indexOf"), method_indexOf, 0); + setProperty(ctx, QLatin1String("lastIndexOf"), method_lastIndexOf, 0); + setProperty(ctx, QLatin1String("every"), method_every, 0); + setProperty(ctx, QLatin1String("some"), method_some, 0); + setProperty(ctx, QLatin1String("forEach"), method_forEach, 0); + setProperty(ctx, QLatin1String("map"), method_map, 0); + setProperty(ctx, QLatin1String("filter"), method_filter, 0); + setProperty(ctx, QLatin1String("reduce"), method_reduce, 0); + setProperty(ctx, QLatin1String("reduceRight"), method_reduceRight, 0); } void ArrayPrototype::method_toString(Context *ctx) @@ -1372,6 +1381,96 @@ void ArrayPrototype::method_unshift(Context *ctx) } } +void ArrayPrototype::method_indexOf(Context *ctx) +{ + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + Q_UNIMPLEMENTED(); + } else { + assert(!"generic implementation"); + } +} + +void ArrayPrototype::method_lastIndexOf(Context *ctx) +{ + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + Q_UNIMPLEMENTED(); + } else { + assert(!"generic implementation"); + } +} + +void ArrayPrototype::method_every(Context *ctx) +{ + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + Q_UNIMPLEMENTED(); + } else { + assert(!"generic implementation"); + } +} + +void ArrayPrototype::method_some(Context *ctx) +{ + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + Q_UNIMPLEMENTED(); + } else { + assert(!"generic implementation"); + } +} + +void ArrayPrototype::method_forEach(Context *ctx) +{ + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + Q_UNIMPLEMENTED(); + } else { + assert(!"generic implementation"); + } +} + +void ArrayPrototype::method_map(Context *ctx) +{ + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + Q_UNIMPLEMENTED(); + } else { + assert(!"generic implementation"); + } +} + +void ArrayPrototype::method_filter(Context *ctx) +{ + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + Q_UNIMPLEMENTED(); + } else { + assert(!"generic implementation"); + } +} + +void ArrayPrototype::method_reduce(Context *ctx) +{ + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + Q_UNIMPLEMENTED(); + } else { + assert(!"generic implementation"); + } +} + +void ArrayPrototype::method_reduceRight(Context *ctx) +{ + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + Q_UNIMPLEMENTED(); + } else { + assert(!"generic implementation"); + } +} + // // Function object // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index a1a55284a4..b09ef1c55f 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -132,6 +132,15 @@ protected: static void method_sort(Context *ctx); static void method_splice(Context *ctx); static void method_unshift(Context *ctx); + static void method_indexOf(Context *ctx); + static void method_lastIndexOf(Context *ctx); + static void method_every(Context *ctx); + static void method_some(Context *ctx); + static void method_forEach(Context *ctx); + static void method_map(Context *ctx); + static void method_filter(Context *ctx); + static void method_reduce(Context *ctx); + static void method_reduceRight(Context *ctx); }; struct FunctionCtor: FunctionObject -- cgit v1.2.3 From 19f17d6c853f4dcd401fec3c681c781348ffd55f Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 15:40:08 +0200 Subject: Initial work on Array.prototype.forEach. --- qv4ecmaobjects.cpp | 20 ++++++++++++++++++-- tests/fun.4.js | 6 +++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 01944f0ca8..ad1a8ec3b9 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1275,7 +1275,7 @@ void ArrayPrototype::method_push(Context *ctx) String *id_length = ctx->engine->identifier(QLatin1String("length")); Value *r1 = self.objectValue->getProperty(id_length); quint32 n = r1 ? r1->toUInt32(ctx) : 0; - for (int index = 0; index < ctx->argumentCount; ++index, ++n) { + for (size_t index = 0; index < ctx->argumentCount; ++index, ++n) { Value r3 = ctx->argument(index); String *name = Value::fromNumber(n).toString(ctx); self.objectValue->put(name, r3); @@ -1425,7 +1425,23 @@ void ArrayPrototype::method_forEach(Context *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { - Q_UNIMPLEMENTED(); + Value callback = ctx->argument(0); + if (! callback.isFunctionObject()) + assert(!"type error"); + else { + Value thisArg = ctx->argument(1); + for (quint32 k = 0; k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + Value r; + Value args[3]; + args[0] = v; + args[1] = Value::fromNumber(k); + args[2] = ctx->thisObject; + __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); + } + } } else { assert(!"generic implementation"); } diff --git a/tests/fun.4.js b/tests/fun.4.js index 1333fadf18..0edd104306 100644 --- a/tests/fun.4.js +++ b/tests/fun.4.js @@ -3,5 +3,9 @@ function foo(a,b,c) { print("hello",a,b,c) } -foo.call(null, 1,2,3) +foo.call(null, 1,2,3); + +[10,20,30].forEach(function (v,k,o) { print(v,k,o); }) + + -- cgit v1.2.3 From 730302a642abc0224184d8f04672238152800299 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 17:48:39 +0200 Subject: Add non generic version of Array.prototype.every --- qv4ecmaobjects.cpp | 18 +++++++++++++++++- tests/fun.4.js | 4 +++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index ad1a8ec3b9..b32569d9aa 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1405,7 +1405,23 @@ void ArrayPrototype::method_every(Context *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { - Q_UNIMPLEMENTED(); + Value callback = ctx->argument(0); + if (callback.isFunctionObject()) { + Value thisArg = ctx->argument(1); + bool ok = true; + for (uint k = 0; ok && k < instance->value.size(); ++k) { + Value args[3]; + args[0] = instance->value.at(k); + args[1] = Value::fromNumber(k); + args[2] = ctx->thisObject; + Value r; + __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); + ok = __qmljs_to_boolean(ctx, &r); + } + ctx->result = Value::fromBoolean(ok); + } else { + assert(!"type error"); + } } else { assert(!"generic implementation"); } diff --git a/tests/fun.4.js b/tests/fun.4.js index 0edd104306..09b6a39804 100644 --- a/tests/fun.4.js +++ b/tests/fun.4.js @@ -5,7 +5,9 @@ function foo(a,b,c) { foo.call(null, 1,2,3); -[10,20,30].forEach(function (v,k,o) { print(v,k,o); }) +[10,20,30].forEach(function (v,k,o) { print(v,k,o); }); + +print([10, 20, 30].every(function (v,k,o) { return v > 9 })); -- cgit v1.2.3 From dd93bc2b8fc760454e55658d8c6a8a921543c3ac Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 17:51:12 +0200 Subject: Add non generatic implementation of Array.prototype.some --- qv4ecmaobjects.cpp | 18 +++++++++++++++++- tests/fun.4.js | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index b32569d9aa..cebf999a9a 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1431,7 +1431,23 @@ void ArrayPrototype::method_some(Context *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { - Q_UNIMPLEMENTED(); + Value callback = ctx->argument(0); + if (callback.isFunctionObject()) { + Value thisArg = ctx->argument(1); + bool ok = false; + for (uint k = 0; !ok && k < instance->value.size(); ++k) { + Value args[3]; + args[0] = instance->value.at(k); + args[1] = Value::fromNumber(k); + args[2] = ctx->thisObject; + Value r; + __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); + ok = __qmljs_to_boolean(ctx, &r); + } + ctx->result = Value::fromBoolean(ok); + } else { + assert(!"type error"); + } } else { assert(!"generic implementation"); } diff --git a/tests/fun.4.js b/tests/fun.4.js index 09b6a39804..7741b26e2d 100644 --- a/tests/fun.4.js +++ b/tests/fun.4.js @@ -8,6 +8,6 @@ foo.call(null, 1,2,3); [10,20,30].forEach(function (v,k,o) { print(v,k,o); }); print([10, 20, 30].every(function (v,k,o) { return v > 9 })); - +print([10, 20, 30].some(function (v,k,o) { return v == 20 })); -- cgit v1.2.3 From 1bdb1d02c471092991126fe721c6da71a6122635 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 17:59:54 +0200 Subject: Implement Array.prototype.map --- qv4ecmaobjects.cpp | 34 +++++++++++++++++++++++++++++++--- tests/fun.4.js | 1 + 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index cebf999a9a..82d59a2589 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1410,8 +1410,12 @@ void ArrayPrototype::method_every(Context *ctx) Value thisArg = ctx->argument(1); bool ok = true; for (uint k = 0; ok && k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + Value args[3]; - args[0] = instance->value.at(k); + args[0] = v; args[1] = Value::fromNumber(k); args[2] = ctx->thisObject; Value r; @@ -1436,8 +1440,12 @@ void ArrayPrototype::method_some(Context *ctx) Value thisArg = ctx->argument(1); bool ok = false; for (uint k = 0; !ok && k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + Value args[3]; - args[0] = instance->value.at(k); + args[0] = v; args[1] = Value::fromNumber(k); args[2] = ctx->thisObject; Value r; @@ -1483,7 +1491,27 @@ void ArrayPrototype::method_map(Context *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { - Q_UNIMPLEMENTED(); + Value callback = ctx->argument(0); + if (! callback.isFunctionObject()) + assert(!"type error"); + else { + Value thisArg = ctx->argument(1); + ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); + a->value.resize(instance->value.size()); + for (quint32 k = 0; k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + Value r; + Value args[3]; + args[0] = v; + args[1] = Value::fromNumber(k); + args[2] = ctx->thisObject; + __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); + a->value.assign(k, r); + } + ctx->result = Value::fromObject(a); + } } else { assert(!"generic implementation"); } diff --git a/tests/fun.4.js b/tests/fun.4.js index 7741b26e2d..e35c8ac815 100644 --- a/tests/fun.4.js +++ b/tests/fun.4.js @@ -9,5 +9,6 @@ foo.call(null, 1,2,3); print([10, 20, 30].every(function (v,k,o) { return v > 9 })); print([10, 20, 30].some(function (v,k,o) { return v == 20 })); +print([10, 20, 30].map(function (v,k,o) { return v * v })); -- cgit v1.2.3 From 15fc292bdac1389b4aac6e750470f1d5bd1cbeb7 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 18:08:51 +0200 Subject: Implemented Array.prototype.filter --- qv4ecmaobjects.cpp | 25 ++++++++++++++++++++++++- tests/fun.4.js | 1 + 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 82d59a2589..4bb9f50be7 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1521,7 +1521,30 @@ void ArrayPrototype::method_filter(Context *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { - Q_UNIMPLEMENTED(); + Value callback = ctx->argument(0); + if (! callback.isFunctionObject()) + assert(!"type error"); + else { + Value thisArg = ctx->argument(1); + ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); + for (quint32 k = 0; k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + Value r; + Value args[3]; + args[0] = v; + args[1] = Value::fromNumber(k); + args[2] = ctx->thisObject; + __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); + if (__qmljs_to_boolean(ctx, &r)) { + const uint index = a->value.size(); + a->value.resize(index + 1); + a->value.assign(index, v); + } + } + ctx->result = Value::fromObject(a); + } } else { assert(!"generic implementation"); } diff --git a/tests/fun.4.js b/tests/fun.4.js index e35c8ac815..af8bc711f3 100644 --- a/tests/fun.4.js +++ b/tests/fun.4.js @@ -10,5 +10,6 @@ foo.call(null, 1,2,3); print([10, 20, 30].every(function (v,k,o) { return v > 9 })); print([10, 20, 30].some(function (v,k,o) { return v == 20 })); print([10, 20, 30].map(function (v,k,o) { return v * v })); +print([10, 20, 30].filter(function (v,k,o) { return v >= 20 })); -- cgit v1.2.3 From d3649cd9403877ed15bbd5cc5b3425172c6e840b Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 18:19:51 +0200 Subject: Implemented Array.prototype.reduce --- qv4ecmaobjects.cpp | 24 +++++++++++++++++++++++- tests/fun.4.js | 2 ++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 4bb9f50be7..e420e27e00 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1554,7 +1554,29 @@ void ArrayPrototype::method_reduce(Context *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { - Q_UNIMPLEMENTED(); + Value callback = ctx->argument(0); + Value initialValue = ctx->argument(1); + Value acc = initialValue; + for (quint32 k = 0; k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + + if (acc.isUndefined()) { + acc = v; + continue; + } + + Value r; + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromNumber(k); + args[3] = ctx->thisObject; + __qmljs_call_value(ctx, &r, 0, &callback, args, 4); + acc = r; + } + ctx->result = acc; } else { assert(!"generic implementation"); } diff --git a/tests/fun.4.js b/tests/fun.4.js index af8bc711f3..c1b1916e61 100644 --- a/tests/fun.4.js +++ b/tests/fun.4.js @@ -12,4 +12,6 @@ print([10, 20, 30].some(function (v,k,o) { return v == 20 })); print([10, 20, 30].map(function (v,k,o) { return v * v })); print([10, 20, 30].filter(function (v,k,o) { return v >= 20 })); +print([10,20,30].reduce(function(a,v,k,o) { return a + v })); + -- cgit v1.2.3 From 17386967b9d85d2a9c52cdcf13b9f4a76592cae5 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 18:34:26 +0200 Subject: Implemented Array.prototype.reduceRight --- qv4ecmaobjects.cpp | 24 +++++++++++++++++++++++- tests/fun.4.js | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index e420e27e00..2ab83b3278 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1586,7 +1586,29 @@ void ArrayPrototype::method_reduceRight(Context *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { - Q_UNIMPLEMENTED(); + Value callback = ctx->argument(0); + Value initialValue = ctx->argument(1); + Value acc = initialValue; + for (int k = instance->value.size() - 1; k != -1; --k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + + if (acc.isUndefined()) { + acc = v; + continue; + } + + Value r; + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromNumber(k); + args[3] = ctx->thisObject; + __qmljs_call_value(ctx, &r, 0, &callback, args, 4); + acc = r; + } + ctx->result = acc; } else { assert(!"generic implementation"); } diff --git a/tests/fun.4.js b/tests/fun.4.js index c1b1916e61..b130acccd3 100644 --- a/tests/fun.4.js +++ b/tests/fun.4.js @@ -13,5 +13,6 @@ print([10, 20, 30].map(function (v,k,o) { return v * v })); print([10, 20, 30].filter(function (v,k,o) { return v >= 20 })); print([10,20,30].reduce(function(a,v,k,o) { return a + v })); +print([10,20,30].reduceRight(function(a,v,k,o) { return a + v })); -- cgit v1.2.3 From b29b7aaa606e64ae932e003683f5576448a85634 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 21 May 2012 18:44:36 +0200 Subject: Initialize functionPrototype. --- qmljs_objects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 35a90ae547..cd5a0e94de 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -204,7 +204,7 @@ ExecutionEngine::ExecutionEngine() booleanPrototype.type = NULL_TYPE; arrayPrototype.type = NULL_TYPE; datePrototype.type = NULL_TYPE; - //functionPrototype.type = NULL_TYPE; + functionPrototype.type = NULL_TYPE; // // set up the global object -- cgit v1.2.3 From 86183598a3af033cc1ce453f7c3f02252a06d016 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 23 May 2012 18:48:48 +0200 Subject: Throw exceptions --- main.cpp | 3 +++ qmljs_objects.cpp | 2 ++ qmljs_objects.h | 4 +++- qmljs_runtime.cpp | 31 +++++++++++++++++++++++++++++++ qmljs_runtime.h | 3 +++ qv4codegen.cpp | 19 +++++++++++++++---- qv4codegen_p.h | 1 + qv4ir.cpp | 34 +++++++++++++++++++++++++++++++++- qv4ir_p.h | 9 +++++++++ qv4isel.cpp | 36 ++++++++++++++++++++++++++++++++---- tests/exceptions.1.js | 16 ++++++++++++++++ tests/typeof.1.js | 12 ++++++++++++ 12 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 tests/exceptions.1.js create mode 100644 tests/typeof.1.js diff --git a/main.cpp b/main.cpp index 354485c2df..de7cb877a2 100644 --- a/main.cpp +++ b/main.cpp @@ -104,6 +104,9 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS globalCode->code(ctx); + if (ctx->hasUncaughtException) + qWarning() << "Uncaught exception"; // << qPrintable(ctx->result.toString(ctx)->toQString()); + delete[] ctx->locals; delete[] ctx->vars; } diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index cd5a0e94de..4b4d858f24 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -463,6 +463,8 @@ void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionO vars = f->varList; varCount = f->varCount; locals = varCount ? new Value[varCount] : 0; + hasUncaughtException = false; + calledAsConstructor = false; std::fill(locals, locals + varCount, Value::undefinedValue()); } diff --git a/qmljs_objects.h b/qmljs_objects.h index 562da2f81c..1d728a076a 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -322,7 +322,8 @@ struct Context { size_t formalCount; String **vars; size_t varCount; - bool calledAsConstructor; + int calledAsConstructor; + int hasUncaughtException; Value *lookup(String *name) { @@ -366,6 +367,7 @@ struct Context { vars = 0; varCount = 0; calledAsConstructor = false; + hasUncaughtException = false; } void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 91b23edf5c..f00eb294f7 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -692,6 +692,10 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S Context *ctx = f->needsActivation ? context->engine->newContext() : &k; ctx->initCallContext(context->engine, &thisObject, f, args, argc); f->call(ctx); + if (ctx->hasUncaughtException) { + context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception + context->result = ctx->result; + } ctx->leaveCallContext(f, result); } else { assert(!"not a function"); @@ -709,6 +713,10 @@ void __qmljs_call_value(Context *context, Value *result, const Value *thisObject Context *ctx = f->needsActivation ? context->engine->newContext() : &k; ctx->initCallContext(context->engine, thisObject, f, args, argc); f->call(ctx); + if (ctx->hasUncaughtException) { + context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception + context->result = ctx->result; + } ctx->leaveCallContext(f, result); } else { assert(!"not a function"); @@ -736,6 +744,10 @@ void __qmljs_construct_value(Context *context, Value *result, const Value *func, Context *ctx = f->needsActivation ? context->engine->newContext() : &k; ctx->initConstructorContext(context->engine, 0, f, args, argc); f->construct(ctx); + if (ctx->hasUncaughtException) { + context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception + context->result = ctx->result; + } ctx->leaveConstructorContext(f, result); } else { assert(!"not a function"); @@ -761,6 +773,10 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba ctx->initConstructorContext(context->engine, 0, f, args, argc); ctx->calledAsConstructor = true; f->construct(ctx); + if (ctx->hasUncaughtException) { + context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception + context->result = ctx->result; + } ctx->leaveConstructorContext(f, result); } else { assert(!"not a function"); @@ -770,6 +786,21 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba } } +void __qmljs_builtin_typeof(Context *context, Value *result, Value *args, int argc) +{ + Q_UNUSED(argc); + __qmljs_typeof(context, result, &args[0]); +} + +void __qmljs_builtin_throw(Context *context, Value *result, Value *args, int argc) +{ + Q_UNUSED(result); + Q_UNUSED(argc); + context->result = args[0]; + context->hasUncaughtException = true; +} + + } // extern "C" diff --git a/qmljs_runtime.h b/qmljs_runtime.h index cd571fd219..afe3f075ca 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -61,6 +61,9 @@ void __qmljs_construct_activation_property(Context *, Value *result, String *nam void __qmljs_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc); void __qmljs_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc); +void __qmljs_builtin_typeof(Context *context, Value *result, Value *args, int argc); +void __qmljs_builtin_throw(Context *context, Value *result, Value *args, int argc); + // constructors void __qmljs_init_undefined(Value *result); void __qmljs_init_null(Value *result); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 86dfe84084..6d475b3989 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -272,6 +272,7 @@ Codegen::Codegen() , _function(0) , _block(0) , _exitBlock(0) + , _throwBlock(0) , _returnAddress(0) , _env(0) { @@ -1237,7 +1238,7 @@ bool Codegen::visit(TypeOfExpression *ast) Result expr = expression(ast->expression); IR::ExprList *args = _function->New(); args->init(argument(*expr)); - _expr.code = call(_block->NAME(QLatin1String("typeof"), ast->typeofToken.startLine, ast->typeofToken.startColumn), args); + _expr.code = call(_block->NAME(IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args); return false; } @@ -1439,6 +1440,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, IR::Function *function = _module->newFunction(name); IR::BasicBlock *entryBlock = function->newBasicBlock(); IR::BasicBlock *exitBlock = function->newBasicBlock(); + IR::BasicBlock *throwBlock = function->newBasicBlock(); function->hasDirectEval = _env->hasDirectEval; function->hasNestedFunctions = _env->hasNestedFunctions; function->maxNumberOfArguments = _env->maxNumberOfArguments; @@ -1452,10 +1454,14 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(IR::UndefinedType, 0)); exitBlock->RET(exitBlock->TEMP(returnAddress), IR::InvalidType); + IR::ExprList *throwArgs = function->New(); + throwArgs->expr = throwBlock->TEMP(returnAddress); + throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); qSwap(_function, function); qSwap(_block, entryBlock); qSwap(_exitBlock, exitBlock); + qSwap(_throwBlock, throwBlock); qSwap(_returnAddress, returnAddress); for (FormalParameterList *it = formals; it; it = it->next) { @@ -1471,9 +1477,13 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, if (! _block->isTerminated()) _block->JUMP(_exitBlock); + if (! _throwBlock->isTerminated()) + _throwBlock->JUMP(_exitBlock); + qSwap(_function, function); qSwap(_block, entryBlock); qSwap(_exitBlock, exitBlock); + qSwap(_throwBlock, throwBlock); qSwap(_returnAddress, returnAddress); leaveEnvironment(); @@ -1708,10 +1718,11 @@ bool Codegen::visit(SwitchStatement *) return false; } -bool Codegen::visit(ThrowStatement *) +bool Codegen::visit(ThrowStatement *ast) { - //Q_ASSERT(!"not implemented"); - _expr.code = _block->CONST(IR::UndefinedType, 0); + Result expr = expression(ast->expression); + _block->MOVE(_block->TEMP(_returnAddress), *expr); + _block->JUMP(_throwBlock); return false; } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index b93dac1068..9deec764bd 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -283,6 +283,7 @@ private: IR::Function *_function; IR::BasicBlock *_block; IR::BasicBlock *_exitBlock; + IR::BasicBlock *_throwBlock; unsigned _returnAddress; Environment *_env; QHash _envMap; diff --git a/qv4ir.cpp b/qv4ir.cpp index 6bcf6b5b8f..e42f055693 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -230,13 +230,26 @@ void Name::init(Type type, const QString *id, quint32 line, quint32 column) { this->type = type; this->id = id; + this->builtin = builtin_invalid; + this->line = line; + this->column = column; +} + +void Name::init(Type type, Builtin builtin, quint32 line, quint32 column) +{ + this->type = type; + this->id = 0; + this->builtin = builtin; this->line = line; this->column = column; } void Name::dump(QTextStream &out) { - out << *id; + if (id) + out << *id; + else + out << "__qmljs_builtin_%" << (int) builtin; } void Temp::dump(QTextStream &out) @@ -525,6 +538,13 @@ Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) return e; } +Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column) +{ + Name *e = function->New(); + e->init(InvalidType, builtin, line, column); + return e; +} + Closure *BasicBlock::CLOSURE(Function *function) { Closure *clos = function->New(); @@ -622,6 +642,9 @@ Expr *BasicBlock::MEMBER(Expr *base, const QString *name) Stmt *BasicBlock::EXP(Expr *expr) { + if (isTerminated()) + return 0; + Exp *s = function->New(); s->init(expr); statements.append(s); @@ -630,6 +653,9 @@ Stmt *BasicBlock::EXP(Expr *expr) Stmt *BasicBlock::ENTER(Expr *expr) { + if (isTerminated()) + return 0; + Enter *s = function->New(); s->init(expr); statements.append(s); @@ -638,6 +664,9 @@ Stmt *BasicBlock::ENTER(Expr *expr) Stmt *BasicBlock::LEAVE() { + if (isTerminated()) + return 0; + Leave *s = function->New(); s->init(); statements.append(s); @@ -646,6 +675,9 @@ Stmt *BasicBlock::LEAVE() Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op) { + if (isTerminated()) + return 0; + Move *s = function->New(); s->init(target, source, op); statements.append(s); diff --git a/qv4ir_p.h b/qv4ir_p.h index e65b52a689..d35f7e4be8 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -255,11 +255,19 @@ struct String: Expr { }; struct Name: Expr { + enum Builtin { + builtin_invalid, + builtin_typeof, + builtin_throw + }; + const QString *id; + Builtin builtin; quint32 line; quint32 column; void init(Type type, const QString *id, quint32 line, quint32 column); + void init(Type type, Builtin builtin, quint32 line, quint32 column); virtual void accept(ExprVisitor *v) { v->visitName(this); } virtual Name *asName() { return this; } @@ -661,6 +669,7 @@ struct BasicBlock { Expr *STRING(const QString *value); Name *NAME(const QString &id, quint32 line, quint32 column); + Name *NAME(Name::Builtin builtin, quint32 line, quint32 column); Closure *CLOSURE(Function *function); diff --git a/qv4isel.cpp b/qv4isel.cpp index dbfaad0ae4..0ef8268a24 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -203,10 +203,33 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu else amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id)); - amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); - amd64_call_code(_codePtr, __qmljs_call_activation_property); + if (baseName->id) { + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id)); + amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); + amd64_call_code(_codePtr, __qmljs_call_activation_property); + } else { + switch (baseName->builtin) { + case IR::Name::builtin_invalid: + Q_UNREACHABLE(); + break; + case IR::Name::builtin_typeof: + amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); + amd64_call_code(_codePtr, __qmljs_builtin_typeof); + break; + case IR::Name::builtin_throw: + amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); + amd64_call_code(_codePtr, __qmljs_builtin_throw); + break; + } + } + + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R14, offsetof(Context, hasUncaughtException), 4); + amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 1, 1); + _patches[_function->basicBlocks.last()].append(_codePtr); // ### TODO: jump to the exception handler + amd64_branch32(_codePtr, X86_CC_E, /* exception handler */ 0, 1); } @@ -241,6 +264,11 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); amd64_call_code(_codePtr, __qmljs_call_value); + + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R14, offsetof(Context, hasUncaughtException), 4); + amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 1, 1); + _patches[_function->basicBlocks.last()].append(_codePtr); // ### TODO: jump to the exception handler + amd64_branch32(_codePtr, X86_CC_E, /* exception handler */ 0, 1); } void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) diff --git a/tests/exceptions.1.js b/tests/exceptions.1.js new file mode 100644 index 0000000000..1f10d98676 --- /dev/null +++ b/tests/exceptions.1.js @@ -0,0 +1,16 @@ + +function foo(a) { + x = 1 + if (a) + throw 0; + print("unreachable.1") +} + +function bar(a) { + print("reachable"); + foo(a) + print("unreachable.2") +} + +bar(1) +print("unreachable.3") diff --git a/tests/typeof.1.js b/tests/typeof.1.js new file mode 100644 index 0000000000..a24cfc6169 --- /dev/null +++ b/tests/typeof.1.js @@ -0,0 +1,12 @@ + +var o = { + number: 123, + fun: function() {}, + string: "ciao", + array: [], +} + +print(typeof o) +print(typeof o.number) +print(typeof o.fun) +print(typeof o.array) -- cgit v1.2.3 From 3bc997b2e342730ab813ddfef64bec1e92aabe85 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Thu, 24 May 2012 10:01:17 +0200 Subject: Throw exceptions in the ecma library --- qmljs_objects.cpp | 24 +++++++++ qmljs_objects.h | 5 ++ qmljs_runtime.cpp | 4 +- qv4ecmaobjects.cpp | 149 ++++++++++++++++++++++++----------------------------- 4 files changed, 98 insertions(+), 84 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 4b4d858f24..b4625aac2a 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -437,6 +437,30 @@ Object *ExecutionEngine::newArgumentsObject(Context *ctx) return new ArgumentsObject(ctx); } +void Context::throwError(const Value &value) +{ + result = value; + hasUncaughtException = true; +} + +void Context::throwError(const QString &message) +{ + Value v = Value::fromString(this, message); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void Context::throwTypeError() +{ + Value v = Value::fromString(this, QLatin1String("Type error")); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void Context::throwUnimplemented(const QString &message) +{ + Value v = Value::fromString(this, QLatin1String("Unimplemented ") + message); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc) { engine = e; diff --git a/qmljs_objects.h b/qmljs_objects.h index 1d728a076a..7c463f2695 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -370,6 +370,11 @@ struct Context { hasUncaughtException = false; } + void throwError(const Value &value); + void throwError(const QString &message); + void throwTypeError(); + void throwUnimplemented(const QString &message); + void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc); void leaveCallContext(FunctionObject *f, Value *r); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index f00eb294f7..4e68359404 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -378,7 +378,9 @@ void __qmljs_object_default_value(Context *ctx, Value *result, const Value *obje void __qmljs_throw_type_error(Context *ctx, Value *result) { - __qmljs_init_object(result, ctx->engine->newErrorObject(Value::fromString(ctx, QLatin1String("type error")))); + ctx->throwTypeError(); + if (result) + *result = ctx->result; } void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 2ab83b3278..533cf44078 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -572,7 +572,7 @@ QString StringPrototype::getThisString(Context *ctx) if (StringObject *thisObject = ctx->thisObject.asStringObject()) { return thisObject->value.stringValue->toQString(); } else { - assert(!"type error"); + ctx->throwTypeError(); return QString(); } } @@ -582,7 +582,7 @@ void StringPrototype::method_toString(Context *ctx) if (StringObject *o = ctx->thisObject.asStringObject()) { ctx->result = o->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -591,7 +591,7 @@ void StringPrototype::method_valueOf(Context *ctx) if (StringObject *o = ctx->thisObject.asStringObject()) { ctx->result = o->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -691,22 +691,22 @@ void StringPrototype::method_localeCompare(Context *ctx) __qmljs_init_number(&ctx->result, QString::localeAwareCompare(value, that)); } -void StringPrototype::method_match(Context *) +void StringPrototype::method_match(Context *ctx) { // requires Regexp - Q_UNIMPLEMENTED(); + ctx->throwUnimplemented(QLatin1String("String.prototype.match")); } -void StringPrototype::method_replace(Context *) +void StringPrototype::method_replace(Context *ctx) { // requires Regexp - Q_UNIMPLEMENTED(); + ctx->throwUnimplemented(QLatin1String("String.prototype.replace")); } -void StringPrototype::method_search(Context *) +void StringPrototype::method_search(Context *ctx) { // requires Regexp - Q_UNIMPLEMENTED(); + ctx->throwUnimplemented(QLatin1String("String.prototype.search")); } void StringPrototype::method_slice(Context *ctx) @@ -732,10 +732,9 @@ void StringPrototype::method_slice(Context *ctx) ctx->result = Value::fromString(ctx, text.mid(start, count)); } -void StringPrototype::method_split(Context *) +void StringPrototype::method_split(Context *ctx) { - // requires Array - Q_UNIMPLEMENTED(); + ctx->throwUnimplemented(QLatin1String("String.prototype.splt")); } void StringPrototype::method_substr(Context *ctx) @@ -885,15 +884,13 @@ NumberPrototype::NumberPrototype(Context *ctx, FunctionObject *ctor) void NumberPrototype::method_toString(Context *ctx) { - assert(!"here"); if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { Value arg = ctx->argument(0); if (!arg.isUndefined()) { int radix = arg.toInt32(ctx); if (radix < 2 || radix > 36) { -// return ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") -// .arg(radix)); - assert(!"not a valid redix"); + ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") + .arg(radix)); return; } @@ -942,7 +939,7 @@ void NumberPrototype::method_toString(Context *ctx) String *str = internalValue.toString(ctx); ctx->result = Value::fromString(str); } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -952,7 +949,7 @@ void NumberPrototype::method_toLocaleString(Context *ctx) String *str = thisObject->value.toString(ctx); ctx->result = Value::fromString(str); } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -961,7 +958,7 @@ void NumberPrototype::method_valueOf(Context *ctx) if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { ctx->result = thisObject->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -986,7 +983,7 @@ void NumberPrototype::method_toFixed(Context *ctx) str = QString::number(v, 'f', int (fdigits)); ctx->result = Value::fromString(ctx, str); } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -1001,7 +998,7 @@ void NumberPrototype::method_toExponential(Context *ctx) QString z = QString::number(thisObject->value.numberValue, 'e', int (fdigits)); ctx->result = Value::fromString(ctx, z); } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -1015,7 +1012,7 @@ void NumberPrototype::method_toPrecision(Context *ctx) ctx->result = Value::fromString(ctx, QString::number(thisObject->value.numberValue, 'g', int (fdigits))); } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -1060,7 +1057,7 @@ void BooleanPrototype::method_toString(Context *ctx) if (BooleanObject *thisObject = ctx->thisObject.asBooleanObject()) { ctx->result = Value::fromString(ctx, QLatin1String(thisObject->value.booleanValue ? "true" : "false")); } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -1069,7 +1066,7 @@ void BooleanPrototype::method_valueOf(Context *ctx) if (BooleanObject *thisObject = ctx->thisObject.asBooleanObject()) { ctx->result = thisObject->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -1103,7 +1100,7 @@ void ArrayCtor::call(Context *ctx) quint32 isize = Value::toUInt32(size); if (size != double(isize)) { - assert(!"invlaid array length exception"); + ctx->throwError(QLatin1String("Invalid array length")); return; } @@ -1298,7 +1295,7 @@ void ArrayPrototype::method_reverse(Context *ctx) instance->value.assign(hi, tmp); } } else { - assert(!"generic implementation of Array.prototype.reverse"); + ctx->throwUnimplemented(QLatin1String("Array.prototype.reverse")); } } @@ -1308,7 +1305,7 @@ void ArrayPrototype::method_shift(Context *ctx) if (ArrayObject *instance = self.asArrayObject()) { ctx->result = instance->value.takeFirst(); } else { - assert(!"generic implementation of Array.prototype.reverse"); + ctx->throwUnimplemented(QLatin1String("Array.prototype.reverse")); } } @@ -1346,7 +1343,7 @@ void ArrayPrototype::method_sort(Context *ctx) instance->value.sort(ctx, comparefn); ctx->result = ctx->thisObject; } else { - assert(!"generic implementation of Array.prototype.sort"); + ctx->throwUnimplemented(QLatin1String("Array.prototype.sort")); } } @@ -1368,37 +1365,23 @@ void ArrayPrototype::method_splice(Context *ctx) instance->value.splice(start, deleteCount, items, otherInstance->value); ctx->result = a; } else { - assert(!"generic implementation of Array.prototype.splice"); + ctx->throwUnimplemented(QLatin1String("Array.prototype.splice")); } } void ArrayPrototype::method_unshift(Context *ctx) { - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - } else { - assert(!"generic implementation of Array.prototype.unshift"); - } + ctx->throwUnimplemented(QLatin1String("Array.prototype.indexOf")); } void ArrayPrototype::method_indexOf(Context *ctx) { - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - Q_UNIMPLEMENTED(); - } else { - assert(!"generic implementation"); - } + ctx->throwUnimplemented(QLatin1String("Array.prototype.indexOf")); } void ArrayPrototype::method_lastIndexOf(Context *ctx) { - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - Q_UNIMPLEMENTED(); - } else { - assert(!"generic implementation"); - } + ctx->throwUnimplemented(QLatin1String("Array.prototype.indexOf")); } void ArrayPrototype::method_every(Context *ctx) @@ -1424,10 +1407,10 @@ void ArrayPrototype::method_every(Context *ctx) } ctx->result = Value::fromBoolean(ok); } else { - assert(!"type error"); + ctx->throwTypeError(); } } else { - assert(!"generic implementation"); + ctx->throwUnimplemented(QLatin1String("Array.prototype.every")); } } @@ -1454,10 +1437,10 @@ void ArrayPrototype::method_some(Context *ctx) } ctx->result = Value::fromBoolean(ok); } else { - assert(!"type error"); + ctx->throwTypeError(); } } else { - assert(!"generic implementation"); + ctx->throwUnimplemented(QLatin1String("Array.prototype.some")); } } @@ -1467,7 +1450,7 @@ void ArrayPrototype::method_forEach(Context *ctx) if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); if (! callback.isFunctionObject()) - assert(!"type error"); + ctx->throwTypeError(); else { Value thisArg = ctx->argument(1); for (quint32 k = 0; k < instance->value.size(); ++k) { @@ -1483,7 +1466,7 @@ void ArrayPrototype::method_forEach(Context *ctx) } } } else { - assert(!"generic implementation"); + ctx->throwUnimplemented(QLatin1String("Array.prototype.forEach")); } } @@ -1493,7 +1476,7 @@ void ArrayPrototype::method_map(Context *ctx) if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); if (! callback.isFunctionObject()) - assert(!"type error"); + ctx->throwTypeError(); else { Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); @@ -1513,7 +1496,7 @@ void ArrayPrototype::method_map(Context *ctx) ctx->result = Value::fromObject(a); } } else { - assert(!"generic implementation"); + ctx->throwUnimplemented(QLatin1String("Array.prototype.map")); } } @@ -1523,7 +1506,7 @@ void ArrayPrototype::method_filter(Context *ctx) if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); if (! callback.isFunctionObject()) - assert(!"type error"); + ctx->throwTypeError(); else { Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); @@ -1546,7 +1529,7 @@ void ArrayPrototype::method_filter(Context *ctx) ctx->result = Value::fromObject(a); } } else { - assert(!"generic implementation"); + ctx->throwUnimplemented(QLatin1String("Array.prototype.filter")); } } @@ -1578,7 +1561,7 @@ void ArrayPrototype::method_reduce(Context *ctx) } ctx->result = acc; } else { - assert(!"generic implementation"); + ctx->throwUnimplemented(QLatin1String("Array.prototype.reduce")); } } @@ -1610,7 +1593,7 @@ void ArrayPrototype::method_reduceRight(Context *ctx) } ctx->result = acc; } else { - assert(!"generic implementation"); + ctx->throwUnimplemented(QLatin1String("Array.prototype.reduceRight")); } } @@ -1635,12 +1618,12 @@ FunctionCtor::FunctionCtor(Context *scope) void FunctionCtor::construct(Context *ctx) { - Q_UNIMPLEMENTED(); + ctx->throwUnimplemented(QLatin1String("Function.prototype.constructor")); } void FunctionCtor::call(Context *ctx) { - Q_UNIMPLEMENTED(); + ctx->throwUnimplemented(QLatin1String("Function")); } FunctionPrototype::FunctionPrototype(Context *ctx, FunctionObject *ctor) @@ -1658,16 +1641,16 @@ void FunctionPrototype::method_toString(Context *ctx) if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { ctx->result = Value::fromString(ctx, QLatin1String("function() { [code] }")); } else { - assert(!"type error"); + ctx->throwTypeError(); } } void FunctionPrototype::method_apply(Context *ctx) { if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { - Q_UNIMPLEMENTED(); + ctx->throwUnimplemented(QLatin1String("Function.prototype.apply")); } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -1680,16 +1663,16 @@ void FunctionPrototype::method_call(Context *ctx) qCopy(ctx->arguments + 1, ctx->arguments + ctx->argumentCount, args.begin()); __qmljs_call_value(ctx, &ctx->result, &thisArg, &ctx->thisObject, args.data(), args.size()); } else { - assert(!"type error"); + ctx->throwTypeError(); } } void FunctionPrototype::method_bind(Context *ctx) { if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { - Q_UNIMPLEMENTED(); + ctx->throwUnimplemented(QLatin1String("Function.prototype.bind")); } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -1813,7 +1796,7 @@ double DatePrototype::getThisDate(Context *ctx) if (DateObject *thisObject = ctx->thisObject.asDateObject()) return thisObject->value.numberValue; else { - assert(!"type error"); + ctx->throwTypeError(); return 0; } } @@ -2052,7 +2035,7 @@ void DatePrototype::method_setTime(Context *ctx) self->value.numberValue = TimeClip(ctx->argument(0).toNumber(ctx)); ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2064,7 +2047,7 @@ void DatePrototype::method_setMilliseconds(Context *ctx) self->value.numberValue = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2076,7 +2059,7 @@ void DatePrototype::method_setUTCMilliseconds(Context *ctx) self->value.numberValue = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2090,7 +2073,7 @@ void DatePrototype::method_setSeconds(Context *ctx) self->value.numberValue = t; ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2104,7 +2087,7 @@ void DatePrototype::method_setUTCSeconds(Context *ctx) self->value.numberValue = t; ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2119,7 +2102,7 @@ void DatePrototype::method_setMinutes(Context *ctx) self->value.numberValue = t; ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2134,7 +2117,7 @@ void DatePrototype::method_setUTCMinutes(Context *ctx) self->value.numberValue = t; ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2150,7 +2133,7 @@ void DatePrototype::method_setHours(Context *ctx) self->value.numberValue = t; ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2166,7 +2149,7 @@ void DatePrototype::method_setUTCHours(Context *ctx) self->value.numberValue = t; ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2179,7 +2162,7 @@ void DatePrototype::method_setDate(Context *ctx) self->value.numberValue = t; ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2192,7 +2175,7 @@ void DatePrototype::method_setUTCDate(Context *ctx) self->value.numberValue = t; ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2206,7 +2189,7 @@ void DatePrototype::method_setMonth(Context *ctx) self->value.numberValue = t; ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2220,7 +2203,7 @@ void DatePrototype::method_setUTCMonth(Context *ctx) self->value.numberValue = t; ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2246,7 +2229,7 @@ void DatePrototype::method_setYear(Context *ctx) self->value.numberValue = r; ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2261,7 +2244,7 @@ void DatePrototype::method_setUTCFullYear(Context *ctx) self->value.numberValue = t; ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } @@ -2276,7 +2259,7 @@ void DatePrototype::method_setFullYear(Context *ctx) self->value.numberValue = t; ctx->result = self->value; } else { - assert(!"type error"); + ctx->throwTypeError(); } } -- cgit v1.2.3 From a5229e821d84f1756c03ce8fa8377cec1df37458 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 25 May 2012 11:55:50 +0200 Subject: Propagate exceptions --- main.cpp | 5 ++-- qmljs_objects.cpp | 19 +++++++++++-- qmljs_objects.h | 1 + qmljs_runtime.cpp | 49 +++++++++++++++++++-------------- qmljs_runtime.h | 3 +- qv4codegen.cpp | 11 +++++++- qv4codegen_p.h | 1 + qv4ir_p.h | 5 +++- qv4isel.cpp | 76 ++++++++++++++++++++++++++++++++++----------------- qv4isel_p.h | 1 + tests/exceptions.1.js | 2 +- tests/exceptions.2.js | 2 ++ 12 files changed, 121 insertions(+), 54 deletions(-) create mode 100644 tests/exceptions.2.js diff --git a/main.cpp b/main.cpp index de7cb877a2..a7bfd0466d 100644 --- a/main.cpp +++ b/main.cpp @@ -104,8 +104,9 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS globalCode->code(ctx); - if (ctx->hasUncaughtException) - qWarning() << "Uncaught exception"; // << qPrintable(ctx->result.toString(ctx)->toQString()); + if (ctx->hasUncaughtException) { + std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; + } delete[] ctx->locals; delete[] ctx->vars; diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index b4625aac2a..f38187cf55 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -398,6 +398,7 @@ FunctionObject *ExecutionEngine::newArrayCtor(Context *ctx) Object *ExecutionEngine::newArrayPrototype(Context *ctx, FunctionObject *proto) { Object *arrayProto = new ArrayPrototype(ctx, proto); + assert(objectPrototype.isObject()); arrayProto->prototype = objectPrototype.objectValue; return arrayProto; } @@ -418,18 +419,25 @@ FunctionObject *ExecutionEngine::newDateCtor(Context *ctx) Object *ExecutionEngine::newDatePrototype(Context *ctx, FunctionObject *proto) { Object *dateProto = new DatePrototype(ctx, proto); + assert(objectPrototype.isObject()); dateProto->prototype = objectPrototype.objectValue; return dateProto; } Object *ExecutionEngine::newErrorObject(const Value &value) { - return new ErrorObject(value); + ErrorObject *object = new ErrorObject(value); + assert(objectPrototype.isObject()); + object->prototype = objectPrototype.objectValue; + return object; } Object *ExecutionEngine::newMathObject(Context *ctx) { - return new MathObject(ctx); + MathObject *object = new MathObject(ctx); + assert(objectPrototype.isObject()); + object->prototype = objectPrototype.objectValue; + return object; } Object *ExecutionEngine::newArgumentsObject(Context *ctx) @@ -461,6 +469,13 @@ void Context::throwUnimplemented(const QString &message) throwError(Value::fromObject(engine->newErrorObject(v))); } +void Context::throwReferenceError(const Value &value) +{ + String *s = value.toString(this); + QString msg = s->toQString() + QStringLiteral(" is not defined"); + throwError(Value::fromObject(engine->newErrorObject(Value::fromString(this, msg)))); +} + void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc) { engine = e; diff --git a/qmljs_objects.h b/qmljs_objects.h index 7c463f2695..c88bda690c 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -374,6 +374,7 @@ struct Context { void throwError(const QString &message); void throwTypeError(); void throwUnimplemented(const QString &message); + void throwReferenceError(const Value &value); void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc); void leaveCallContext(FunctionObject *f, Value *r); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 4e68359404..28d0bd3a87 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -490,7 +490,7 @@ void __qmljs_set_activation_element(Context *ctx, String *name, Value *index, Va if (Value *base = ctx->lookup(name)) { __qmljs_set_element(ctx, base, index, value); } else { - assert(!"reference error"); + ctx->throwReferenceError(Value::fromString(name)); } } @@ -514,7 +514,7 @@ void __qmljs_copy_activation_property(Context *ctx, String *name, String *other) if (Value *source = ctx->lookup(other)) __qmljs_set_activation_property(ctx, name, source); else - assert(!"reference error"); + ctx->throwReferenceError(Value::fromString(name)); } void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool b) @@ -552,8 +552,10 @@ void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *na } else { Value o; __qmljs_to_object(ctx, &o, object); - assert(o.type == OBJECT_TYPE); - __qmljs_get_property(ctx, result, &o, name); + if (o.isObject()) + __qmljs_get_property(ctx, result, &o, name); + else + ctx->throwTypeError(); } } @@ -562,7 +564,7 @@ void __qmljs_get_activation_property(Context *ctx, Value *result, String *name) if (Value *prop = ctx->lookup(name)) *result = *prop; else - assert(!"reference error"); + ctx->throwReferenceError(Value::fromString(name)); } void __qmljs_get_activation(Context *ctx, Value *result) @@ -667,9 +669,9 @@ void __qmljs_call_activation_property(Context *context, Value *result, String *n { Value *func = context->lookup(name); if (! func) - assert(!"reference error"); - - __qmljs_call_value(context, result, /*thisObject=*/ 0, func, args, argc); + context->throwReferenceError(Value::fromString(name)); + else + __qmljs_call_value(context, result, /*thisObject=*/ 0, func, args, argc); } void __qmljs_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) @@ -700,10 +702,10 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S } ctx->leaveCallContext(f, result); } else { - assert(!"not a function"); + context->throwTypeError(); } } else { - assert(!"not a callable object"); + context->throwTypeError(); } } @@ -721,10 +723,10 @@ void __qmljs_call_value(Context *context, Value *result, const Value *thisObject } ctx->leaveCallContext(f, result); } else { - assert(!"not a function"); + context->throwTypeError(); } } else { - assert(!"not a callable object"); + context->throwTypeError(); } } @@ -732,9 +734,9 @@ void __qmljs_construct_activation_property(Context *context, Value *result, Stri { Value *func = context->lookup(name); if (! func) - assert(!"reference error"); - - __qmljs_construct_value(context, result, func, args, argc); + context->throwReferenceError(Value::fromString(name)); + else + __qmljs_construct_value(context, result, func, args, argc); } void __qmljs_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc) @@ -752,10 +754,10 @@ void __qmljs_construct_value(Context *context, Value *result, const Value *func, } ctx->leaveConstructorContext(f, result); } else { - assert(!"not a function"); + context->throwTypeError(); } } else { - assert(!"not a callable object"); + context->throwTypeError(); } } @@ -781,10 +783,10 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba } ctx->leaveConstructorContext(f, result); } else { - assert(!"not a function"); + context->throwTypeError(); } } else { - assert(!"not a callable object"); + context->throwTypeError(); } } @@ -796,12 +798,17 @@ void __qmljs_builtin_typeof(Context *context, Value *result, Value *args, int ar void __qmljs_builtin_throw(Context *context, Value *result, Value *args, int argc) { - Q_UNUSED(result); Q_UNUSED(argc); - context->result = args[0]; + Q_UNUSED(result); context->hasUncaughtException = true; + context->result = args[0]; } +void __qmljs_builtin_rethrow(Context *context, Value *result, Value *, int) +{ + assert(result); + *result = context->result; +} } // extern "C" diff --git a/qmljs_runtime.h b/qmljs_runtime.h index afe3f075ca..36f3b4cd9c 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -63,6 +63,7 @@ void __qmljs_construct_value(Context *context, Value *result, const Value *func, void __qmljs_builtin_typeof(Context *context, Value *result, Value *args, int argc); void __qmljs_builtin_throw(Context *context, Value *result, Value *args, int argc); +void __qmljs_builtin_rethrow(Context *context, Value *result, Value *args, int argc); // constructors void __qmljs_init_undefined(Value *result); @@ -437,7 +438,7 @@ inline void __qmljs_to_string(Context *ctx, Value *result, const Value *value) if (prim.isPrimitive()) __qmljs_to_string(ctx, result, &prim); else - assert(!"type error"); + __qmljs_throw_type_error(ctx, result); break; } diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 6d475b3989..c9d14f778f 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -273,6 +273,7 @@ Codegen::Codegen() , _block(0) , _exitBlock(0) , _throwBlock(0) + , _handlersBlock(0) , _returnAddress(0) , _env(0) { @@ -1441,9 +1442,11 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, IR::BasicBlock *entryBlock = function->newBasicBlock(); IR::BasicBlock *exitBlock = function->newBasicBlock(); IR::BasicBlock *throwBlock = function->newBasicBlock(); + IR::BasicBlock *handlersBlock = function->newBasicBlock(); function->hasDirectEval = _env->hasDirectEval; function->hasNestedFunctions = _env->hasNestedFunctions; function->maxNumberOfArguments = _env->maxNumberOfArguments; + function->handlersBlock = handlersBlock; for (int i = 0; i < _env->vars.size(); ++i) { unsigned t = entryBlock->newTemp(); @@ -1462,6 +1465,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, qSwap(_block, entryBlock); qSwap(_exitBlock, exitBlock); qSwap(_throwBlock, throwBlock); + qSwap(_handlersBlock, handlersBlock); qSwap(_returnAddress, returnAddress); for (FormalParameterList *it = formals; it; it = it->next) { @@ -1478,12 +1482,17 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, _block->JUMP(_exitBlock); if (! _throwBlock->isTerminated()) - _throwBlock->JUMP(_exitBlock); + _throwBlock->JUMP(_function->handlersBlock); + + _handlersBlock->MOVE(_handlersBlock->TEMP(_returnAddress), + _handlersBlock->CALL(_handlersBlock->NAME(IR::Name::builtin_rethrow, 0, 0), 0)); + _handlersBlock->JUMP(_exitBlock); qSwap(_function, function); qSwap(_block, entryBlock); qSwap(_exitBlock, exitBlock); qSwap(_throwBlock, throwBlock); + qSwap(_handlersBlock, handlersBlock); qSwap(_returnAddress, returnAddress); leaveEnvironment(); diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 9deec764bd..87e967da80 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -284,6 +284,7 @@ private: IR::BasicBlock *_block; IR::BasicBlock *_exitBlock; IR::BasicBlock *_throwBlock; + IR::BasicBlock *_handlersBlock; unsigned _returnAddress; Environment *_env; QHash _envMap; diff --git a/qv4ir_p.h b/qv4ir_p.h index d35f7e4be8..7bac2faf58 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -258,7 +258,8 @@ struct Name: Expr { enum Builtin { builtin_invalid, builtin_typeof, - builtin_throw + builtin_throw, + builtin_rethrow }; const QString *id; @@ -599,6 +600,7 @@ struct Function { QSet strings; QList formals; QList locals; + IR::BasicBlock *handlersBlock; void (*code)(VM::Context *); bool hasDirectEval: 1; bool hasNestedFunctions: 1; @@ -609,6 +611,7 @@ struct Function { : module(module) , pool(&module->pool) , tempCount(0) + , handlersBlock(0) , maxNumberOfArguments(0) , code(0) , hasDirectEval(false) diff --git a/qv4isel.cpp b/qv4isel.cpp index 0ef8268a24..061250239f 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -223,13 +223,15 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); amd64_call_code(_codePtr, __qmljs_builtin_throw); break; + case IR::Name::builtin_rethrow: + amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); + amd64_call_code(_codePtr, __qmljs_builtin_rethrow); + return; // we need to return to avoid checking the exceptions } } - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R14, offsetof(Context, hasUncaughtException), 4); - amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 1, 1); - _patches[_function->basicBlocks.last()].append(_codePtr); // ### TODO: jump to the exception handler - amd64_branch32(_codePtr, X86_CC_E, /* exception handler */ 0, 1); + checkExceptions(); } @@ -265,10 +267,7 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); amd64_call_code(_codePtr, __qmljs_call_value); - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R14, offsetof(Context, hasUncaughtException), 4); - amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 1, 1); - _patches[_function->basicBlocks.last()].append(_codePtr); // ### TODO: jump to the exception handler - amd64_branch32(_codePtr, X86_CC_E, /* exception handler */ 0, 1); + checkExceptions(); } void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) @@ -304,6 +303,8 @@ void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); amd64_call_code(_codePtr, __qmljs_call_property); + + checkExceptions(); } void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) @@ -336,6 +337,8 @@ void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp * amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); amd64_call_code(_codePtr, __qmljs_construct_activation_property); + + checkExceptions(); } void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) @@ -371,6 +374,8 @@ void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); amd64_call_code(_codePtr, __qmljs_construct_property); + + checkExceptions(); } void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) @@ -405,6 +410,14 @@ void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) amd64_call_code(_codePtr, __qmljs_construct_value); } +void InstructionSelection::checkExceptions() +{ + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R14, offsetof(Context, hasUncaughtException), 4); + amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 1, 1); + _patches[_function->handlersBlock].append(_codePtr); + amd64_branch32(_codePtr, X86_CC_E, 0, 1); +} + void InstructionSelection::visitExp(IR::Exp *s) { if (IR::Call *c = s->expr->asCall()) { @@ -462,32 +475,33 @@ void InstructionSelection::visitMove(IR::Move *s) Q_UNIMPLEMENTED(); assert(!"TODO"); } - return; } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); amd64_mov_reg_imm(_codePtr, AMD64_RDX, _engine->newString(*str->value)); amd64_call_code(_codePtr, __qmljs_set_activation_property_string); - return; } else if (IR::Temp *t = s->source->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); loadTempAddress(AMD64_RDX, t); amd64_call_code(_codePtr, __qmljs_set_activation_property); - return; } else if (IR::Name *other = s->source->asName()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*other->id)); amd64_call_code(_codePtr, __qmljs_copy_activation_property); - return; } else if (IR::Closure *clos = s->source->asClosure()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); amd64_mov_reg_imm(_codePtr, AMD64_RDX, clos->value); amd64_call_code(_codePtr, __qmljs_set_activation_property_closure); - return; + } else { + Q_UNIMPLEMENTED(); + assert(!"TODO"); } + + checkExceptions(); + return; } else if (IR::Temp *t = s->target->asTemp()) { if (IR::Name *n = s->source->asName()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); @@ -498,6 +512,7 @@ void InstructionSelection::visitMove(IR::Move *s) String *propertyName = identifier(*n->id); amd64_mov_reg_imm(_codePtr, AMD64_RDX, propertyName); amd64_call_code(_codePtr, __qmljs_get_activation_property); + checkExceptions(); } return; } else if (IR::Const *c = s->source->asConst()) { @@ -567,6 +582,7 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RDX, base); amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*m->name)); amd64_call_code(_codePtr, __qmljs_get_property); + checkExceptions(); return; } assert(!"wip"); @@ -577,6 +593,7 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RDX, ss->base->asTemp()); loadTempAddress(AMD64_RCX, ss->index->asTemp()); amd64_call_code(_codePtr, __qmljs_get_element); + checkExceptions(); return; } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { @@ -709,14 +726,12 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); amd64_call_code(_codePtr, __qmljs_set_property_number); - return; } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, base); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); amd64_mov_reg_imm(_codePtr, AMD64_RCX, _engine->newString(*str->value)); amd64_call_code(_codePtr, __qmljs_set_property_string); - return; } else if (IR::Temp *t = s->source->asTemp()) { // __qmljs_set_property(ctx, object, name, value); amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); @@ -724,15 +739,18 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); loadTempAddress(AMD64_RCX, t); amd64_call_code(_codePtr, __qmljs_set_property); - return; } else if (IR::Closure *clos = s->source->asClosure()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, base); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); amd64_mov_reg_imm(_codePtr, AMD64_RCX, clos->value); amd64_call_code(_codePtr, __qmljs_set_property_closure); - return; + } else { + Q_UNIMPLEMENTED(); + assert(!"TODO"); } + checkExceptions(); + return; } } else if (IR::Subscript *ss = s->target->asSubscript()) { if (IR::Temp *t2 = s->source->asTemp()) { @@ -740,16 +758,24 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RDX, ss->index->asTemp()); loadTempAddress(AMD64_RCX, t2); amd64_call_code(_codePtr, __qmljs_set_element); - return; } else if (IR::Const *c = s->source->asConst()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, ss->base->asTemp()); - loadTempAddress(AMD64_RDX, ss->index->asTemp()); - amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); - amd64_call_code(_codePtr, __qmljs_set_element_number); - return; + if (c->type == IR::NumberType) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, ss->base->asTemp()); + loadTempAddress(AMD64_RDX, ss->index->asTemp()); + amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); + amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); + amd64_call_code(_codePtr, __qmljs_set_element_number); + } else { + Q_UNIMPLEMENTED(); + assert(!"TODO"); + } + } else { + Q_UNIMPLEMENTED(); + assert(!"TODO"); } + checkExceptions(); + return; } } else { // inplace assignment, e.g. x += 1, ++x, ... diff --git a/qv4isel_p.h b/qv4isel_p.h index c6da581683..d53d028c50 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -26,6 +26,7 @@ protected: void constructProperty(IR::New *ctor, IR::Temp *result); void callValue(IR::Call *call, IR::Temp *result); void constructValue(IR::New *call, IR::Temp *result); + void checkExceptions(); virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); diff --git a/tests/exceptions.1.js b/tests/exceptions.1.js index 1f10d98676..5e4e152c19 100644 --- a/tests/exceptions.1.js +++ b/tests/exceptions.1.js @@ -2,7 +2,7 @@ function foo(a) { x = 1 if (a) - throw 0; + throw 100; print("unreachable.1") } diff --git a/tests/exceptions.2.js b/tests/exceptions.2.js new file mode 100644 index 0000000000..16b93b8940 --- /dev/null +++ b/tests/exceptions.2.js @@ -0,0 +1,2 @@ + +print(foo) -- cgit v1.2.3 From fc6f96166fd51233a478a083ca14ca55ea3ca780 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 25 May 2012 12:54:36 +0200 Subject: Refactored Object --- main.cpp | 4 +- qmljs_objects.cpp | 100 +++++------ qmljs_objects.h | 57 +++--- qmljs_runtime.cpp | 183 +++++++++---------- qmljs_runtime.h | 4 +- qv4codegen.cpp | 10 +- qv4ecmaobjects.cpp | 508 +++++++++++++++++++++++++++-------------------------- qv4ir.cpp | 10 +- qv4ir_p.h | 2 +- qv4isel.cpp | 2 +- 10 files changed, 432 insertions(+), 448 deletions(-) diff --git a/main.cpp b/main.cpp index a7bfd0466d..0ea7040a04 100644 --- a/main.cpp +++ b/main.cpp @@ -90,8 +90,8 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS VM::Object *globalObject = vm->globalObject.objectValue; VM::Context *ctx = vm->rootContext; - globalObject->put(vm->identifier(QLatin1String("print")), - VM::Value::fromObject(new builtins::Print(ctx))); + globalObject->setProperty(ctx, vm->identifier(QStringLiteral("print")), + VM::Value::fromObject(new builtins::Print(ctx))); ctx->varCount = globalCode->locals.size(); if (ctx->varCount) { diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index f38187cf55..20477e0fc8 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -18,7 +18,7 @@ Object::~Object() void Object::setProperty(Context *ctx, const QString &name, const Value &value) { - put(ctx->engine->identifier(name), value); + setProperty(ctx, ctx->engine->identifier(name), value); } void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context *), int count) @@ -27,18 +27,7 @@ void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context setProperty(ctx, name, Value::fromObject(ctx->engine->newNativeFunction(ctx, code))); } -bool Object::get(String *name, Value *result) -{ - if (Value *prop = getProperty(name)) { - *result = *prop; - return true; - } - - __qmljs_init_undefined(result); - return false; -} - -Value *Object::getOwnProperty(String *name, PropertyAttributes *attributes) +Value *Object::getOwnProperty(Context *ctx, String *name, PropertyAttributes *attributes) { if (members) { if (Property *prop = members->find(name)) { @@ -50,16 +39,16 @@ Value *Object::getOwnProperty(String *name, PropertyAttributes *attributes) return 0; } -Value *Object::getProperty(String *name, PropertyAttributes *attributes) +Value *Object::getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes) { - if (Value *prop = getOwnProperty(name, attributes)) + if (Value *prop = getOwnProperty(ctx, name, attributes)) return prop; else if (prototype) - return prototype->getProperty(name, attributes); + return prototype->getPropertyDescriptor(ctx, name, attributes); return 0; } -void Object::put(String *name, const Value &value, bool flag) +void Object::setProperty(Context *ctx, String *name, const Value &value, bool flag) { Q_UNUSED(flag); @@ -69,14 +58,14 @@ void Object::put(String *name, const Value &value, bool flag) members->insert(name, value); } -bool Object::canPut(String *name) +bool Object::canSetProperty(Context *ctx, String *name) { PropertyAttributes attrs = PropertyAttributes(); - if (getOwnProperty(name, &attrs)) { + if (getOwnProperty(ctx, name, &attrs)) { return attrs & WritableAttribute; } else if (! prototype) { return extensible; - } else if (prototype->getProperty(name, &attrs)) { + } else if (prototype->getPropertyDescriptor(ctx, name, &attrs)) { return attrs & WritableAttribute; } else { return extensible; @@ -84,7 +73,7 @@ bool Object::canPut(String *name) return true; } -bool Object::hasProperty(String *name) const +bool Object::hasProperty(Context *ctx, String *name) const { if (members) return members->find(name) != 0; @@ -92,7 +81,7 @@ bool Object::hasProperty(String *name) const return false; } -bool Object::deleteProperty(String *name, bool flag) +bool Object::deleteProperty(Context *ctx, String *name, bool flag) { Q_UNUSED(flag); @@ -102,14 +91,19 @@ bool Object::deleteProperty(String *name, bool flag) return false; } -Value *ArrayObject::getOwnProperty(String *name, PropertyAttributes *attributes) +void Object::defineOwnProperty(Context *ctx, const Value &getter, const Value &setter, bool flag) { - if (name->toQString() == QLatin1String("length")) { - length.numberValue = value.size(); - return &length; - } + Q_UNUSED(getter); + Q_UNUSED(setter); + Q_UNUSED(flag); + ctx->throwUnimplemented(QStringLiteral("defineOwnProperty")); +} - return Object::getOwnProperty(name, attributes); +Value ArrayObject::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) +{ + if (name->isEqualTo(ctx->engine->id_length)) + return Value::fromNumber(value.size()); + return Object::getProperty(ctx, name, attributes); } bool FunctionObject::hasInstance(const Value &value) const @@ -168,7 +162,7 @@ void ScriptFunction::construct(VM::Context *ctx) function->code(ctx); } -Value *ArgumentsObject::getProperty(String *name, PropertyAttributes *attributes) +Value *ArgumentsObject::getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes) { if (context) { for (size_t i = 0; i < context->varCount; ++i) { @@ -188,7 +182,7 @@ Value *ArgumentsObject::getProperty(String *name, PropertyAttributes *attributes } } } - if (Value *prop = Object::getProperty(name, attributes)) + if (Value *prop = Object::getPropertyDescriptor(ctx, name, attributes)) return prop; return 0; } @@ -206,20 +200,21 @@ ExecutionEngine::ExecutionEngine() datePrototype.type = NULL_TYPE; functionPrototype.type = NULL_TYPE; + id_length = identifier(QStringLiteral("length")); + id_prototype = identifier(QStringLiteral("prototype")); + // // set up the global object // - String *prototype = identifier(QLatin1String("prototype")); - VM::Object *glo = newArgumentsObject(rootContext); __qmljs_init_object(&globalObject, glo); __qmljs_init_object(&rootContext->activation, glo); objectCtor = ObjectCtor::create(this); - objectCtor.objectValue->get(prototype, &objectPrototype); + objectPrototype = objectCtor.property(rootContext, id_prototype); functionCtor = FunctionCtor::create(this); - functionCtor.objectValue->get(prototype, &functionPrototype); + functionPrototype = functionCtor.property(rootContext, id_prototype); stringCtor = StringCtor::create(this); numberCtor = NumberCtor::create(this); @@ -227,19 +222,19 @@ ExecutionEngine::ExecutionEngine() arrayCtor = ArrayCtor::create(this); dateCtor = DateCtor::create(this); - stringCtor.objectValue->get(prototype, &stringPrototype); - numberCtor.objectValue->get(prototype, &numberPrototype); - booleanCtor.objectValue->get(prototype, &booleanPrototype); - arrayCtor.objectValue->get(prototype, &arrayPrototype); - dateCtor.objectValue->get(prototype, &datePrototype); + stringPrototype = stringCtor.property(rootContext, id_prototype); + numberPrototype = numberCtor.property(rootContext, id_prototype); + booleanPrototype = booleanCtor.property(rootContext, id_prototype); + arrayPrototype = arrayCtor.property(rootContext, id_prototype); + datePrototype = dateCtor.property(rootContext, id_prototype); - glo->put(identifier(QLatin1String("Object")), objectCtor); - glo->put(identifier(QLatin1String("String")), stringCtor); - glo->put(identifier(QLatin1String("Number")), numberCtor); - glo->put(identifier(QLatin1String("Array")), arrayCtor); - glo->put(identifier(QLatin1String("Function")), functionCtor); - glo->put(identifier(QLatin1String("Date")), dateCtor); - glo->put(identifier(QLatin1String("Math")), Value::fromObject(newMathObject(rootContext))); + glo->setProperty(rootContext, identifier(QStringLiteral("Object")), objectCtor); + glo->setProperty(rootContext, identifier(QStringLiteral("String")), stringCtor); + glo->setProperty(rootContext, identifier(QStringLiteral("Number")), numberCtor); + glo->setProperty(rootContext, identifier(QStringLiteral("Array")), arrayCtor); + glo->setProperty(rootContext, identifier(QStringLiteral("Function")), functionCtor); + glo->setProperty(rootContext, identifier(QStringLiteral("Date")), dateCtor); + glo->setProperty(rootContext, identifier(QStringLiteral("Math")), Value::fromObject(newMathObject(rootContext))); } Context *ExecutionEngine::newContext() @@ -459,13 +454,13 @@ void Context::throwError(const QString &message) void Context::throwTypeError() { - Value v = Value::fromString(this, QLatin1String("Type error")); + Value v = Value::fromString(this, QStringLiteral("Type error")); throwError(Value::fromObject(engine->newErrorObject(v))); } void Context::throwUnimplemented(const QString &message) { - Value v = Value::fromString(this, QLatin1String("Unimplemented ") + message); + Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message); throwError(Value::fromObject(engine->newErrorObject(v))); } @@ -529,10 +524,9 @@ void Context::leaveConstructorContext(FunctionObject *f, Value *returnValue) assert(thisObject.is(OBJECT_TYPE)); result = thisObject; - Value proto; - if (f->get(engine->identifier(QLatin1String("prototype")), &proto)) { - if (proto.type == OBJECT_TYPE) - thisObject.objectValue->prototype = proto.objectValue; - } + Value proto = f->getProperty(this, engine->id_prototype); + if (proto.isObject()) + thisObject.objectValue->prototype = proto.objectValue; + leaveCallContext(f, returnValue); } diff --git a/qmljs_objects.h b/qmljs_objects.h index c88bda690c..4683b4e691 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -33,6 +33,12 @@ struct String { String(const QString &text) : _text(text), _hashValue(0) {} + bool isEqualTo(const String *other) const { + if (other && hashValue() == other->hashValue()) + return this == other || toQString() == other->toQString(); + return false; + } + inline const QString &toQString() const { return _text; } @@ -63,18 +69,8 @@ struct Property { inline bool isEnumerable() const { return attributes & EnumerableAttribute; } inline bool isConfigurable() const { return attributes & ConfigurableAttribute; } - inline bool hasName(String *n) const { - if (name == n) { - return true; - } else if (name->hashValue() == n->hashValue() && name->toQString() == n->toQString()) { - return true; - } - return false; - } - - inline unsigned hashValue() const { - return name->hashValue(); - } + inline bool hasName(String *n) const { return name->isEqualTo(n); } + inline unsigned hashValue() const { return name->hashValue(); } }; class Table @@ -206,15 +202,20 @@ struct Object { virtual ErrorObject *asErrorObject() { return 0; } virtual ArgumentsObject *asArgumentsObject() { return 0; } - bool get(String *name, Value *result); + virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes = 0) + { + if (Value *v = getPropertyDescriptor(ctx, name, attributes)) + return *v; + return Value::undefinedValue(); + } - virtual Value *getOwnProperty(String *name, PropertyAttributes *attributes = 0); - virtual Value *getProperty(String *name, PropertyAttributes *attributes = 0); - virtual void put(String *name, const Value &value, bool flag = 0); - virtual bool canPut(String *name); - virtual bool hasProperty(String *name) const; - virtual bool deleteProperty(String *name, bool flag); - // ### TODO: defineOwnProperty(name, descriptor, boolean) -> boolean + virtual Value *getOwnProperty(Context *ctx, String *name, PropertyAttributes *attributes = 0); + virtual Value *getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes = 0); + virtual void setProperty(Context *ctx, String *name, const Value &value, bool flag = false); + virtual bool canSetProperty(Context *ctx, String *name); + virtual bool hasProperty(Context *ctx, String *name) const; + virtual bool deleteProperty(Context *ctx, String *name, bool flag); + virtual void defineOwnProperty(Context *ctx, const Value &getter, const Value &setter, bool flag = false); // // helpers @@ -249,11 +250,10 @@ struct DateObject: Object { struct ArrayObject: Object { Array value; - Value length; - ArrayObject() { length.type = NUMBER_TYPE; } - ArrayObject(const Array &value): value(value) { length.type = NUMBER_TYPE; } + ArrayObject() {} + ArrayObject(const Array &value): value(value) {} virtual ArrayObject *asArrayObject() { return this; } - virtual Value *getOwnProperty(String *name, PropertyAttributes *attributes); + virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes); }; struct FunctionObject: Object { @@ -306,7 +306,7 @@ struct ArgumentsObject: Object { Context *context; ArgumentsObject(Context *context): context(context) {} virtual ArgumentsObject *asArgumentsObject() { return this; } - virtual Value *getProperty(String *name, PropertyAttributes *attributes); + virtual Value *getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes); }; struct Context { @@ -325,11 +325,11 @@ struct Context { int calledAsConstructor; int hasUncaughtException; - Value *lookup(String *name) + Value *lookupPropertyDescriptor(String *name) { for (Context *ctx = this; ctx; ctx = ctx->parent) { if (ctx->activation.is(OBJECT_TYPE)) { - if (Value *prop = ctx->activation.objectValue->getProperty(name)) { + if (Value *prop = ctx->activation.objectValue->getPropertyDescriptor(this, name)) { return prop; } } @@ -406,6 +406,9 @@ struct ExecutionEngine QHash identifiers; + String *id_length; + String *id_prototype; + ExecutionEngine(); Context *newContext(); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 28d0bd3a87..060db22c79 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -15,7 +15,7 @@ namespace VM { QString numberToString(double num, int radix = 10) { if (qIsNaN(num)) { - return QLatin1String("NaN"); + return QStringLiteral("NaN"); } else if (qIsInf(num)) { return QLatin1String(num < 0 ? "-Infinity" : "Infinity"); } @@ -211,6 +211,16 @@ ArgumentsObject *Value::asArgumentsObject() const return type == OBJECT_TYPE ? objectValue->asArgumentsObject() : 0; } +Value Value::property(Context *ctx, String *name) const +{ + return isObject() ? objectValue->getProperty(ctx, name) : undefinedValue(); +} + +Value *Value::getPropertyDescriptor(Context *ctx, String *name) const +{ + return isObject() ? objectValue->getPropertyDescriptor(ctx, name) : 0; +} + extern "C" { void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos) @@ -220,47 +230,47 @@ void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos) void __qmljs_string_literal_undefined(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("undefined"))); + __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("undefined"))); } void __qmljs_string_literal_null(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("null"))); + __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("null"))); } void __qmljs_string_literal_true(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("true"))); + __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("true"))); } void __qmljs_string_literal_false(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("false"))); + __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("false"))); } void __qmljs_string_literal_object(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("object"))); + __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("object"))); } void __qmljs_string_literal_boolean(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("boolean"))); + __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("boolean"))); } void __qmljs_string_literal_number(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("number"))); + __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("number"))); } void __qmljs_string_literal_string(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("string"))); + __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("string"))); } void __qmljs_string_literal_function(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QLatin1String("function"))); + __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("function"))); } void __qmljs_delete(Context *ctx, Value *result, const Value *value) @@ -290,7 +300,7 @@ void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *rig if (right->type == OBJECT_TYPE) { Value s; __qmljs_to_string(ctx, &s, left); - bool r = right->objectValue->hasProperty(s.stringValue); + bool r = right->objectValue->hasProperty(ctx, s.stringValue); __qmljs_init_boolean(result, r); } else { __qmljs_throw_type_error(ctx, result); @@ -353,20 +363,20 @@ void __qmljs_object_default_value(Context *ctx, Value *result, const Value *obje Object *oo = object->asObject(); assert(oo != 0); - Value *conv = oo->getProperty(meth1); - if (conv && conv->isFunctionObject()) { + Value conv = oo->getProperty(ctx, meth1); + if (!conv.isUndefined() && conv.isFunctionObject()) { Value r; - __qmljs_call_value(ctx, &r, object, conv, 0, 0); + __qmljs_call_value(ctx, &r, object, &conv, 0, 0); if (r.isPrimitive()) { *result = r; return; } } - conv = object->asObject()->getProperty(meth2); - if (conv && conv->isFunctionObject()) { + conv = oo->getProperty(ctx, meth2); + if (!conv.isUndefined() && conv.isFunctionObject()) { Value r; - __qmljs_call_value(ctx, &r, object, conv, 0, 0); + __qmljs_call_value(ctx, &r, object, &conv, 0, 0); if (r.isPrimitive()) { *result = r; return; @@ -409,16 +419,14 @@ void __qmljs_new_string_object(Context *ctx, Value *result, String *string) void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value) { - Q_UNUSED(ctx); - object->objectValue->put(name, *value, /*flags*/ 0); + object->objectValue->setProperty(ctx, name, *value, /*flags*/ 0); } void __qmljs_set_property_boolean(Context *ctx, Value *object, String *name, bool number) { - Q_UNUSED(ctx); Value value; __qmljs_init_boolean(&value, number); - object->objectValue->put(name, value, /*flag*/ 0); + object->objectValue->setProperty(ctx, name, value, /*flag*/ 0); } void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double number) @@ -426,7 +434,7 @@ void __qmljs_set_property_number(Context *ctx, Value *object, String *name, doub Q_UNUSED(ctx); Value value; __qmljs_init_number(&value, number); - object->objectValue->put(name, value, /*flag*/ 0); + object->objectValue->setProperty(ctx, name, value, /*flag*/ 0); } void __qmljs_set_property_string(Context *ctx, Value *object, String *name, String *s) @@ -434,14 +442,14 @@ void __qmljs_set_property_string(Context *ctx, Value *object, String *name, Stri Q_UNUSED(ctx); Value value; __qmljs_init_string(&value, s); - object->objectValue->put(name, value, /*flag*/ 0); + object->objectValue->setProperty(ctx, name, value, /*flag*/ 0); } void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR::Function *function) { Value value; __qmljs_init_closure(ctx, &value, function); - object->objectValue->put(name, value, /*flag*/ 0); + object->objectValue->setProperty(ctx, name, value, /*flag*/ 0); } void __qmljs_get_element(Context *ctx, Value *result, Value *object, Value *index) @@ -460,7 +468,7 @@ void __qmljs_get_element(Context *ctx, Value *result, Value *object, Value *inde if (! object->isObject()) __qmljs_to_object(ctx, object, object); - object->objectValue->get(name, result); + *result = object->property(ctx, name); } } @@ -474,7 +482,7 @@ void __qmljs_set_element(Context *ctx, Value *object, Value *index, Value *value if (! object->isObject()) __qmljs_to_object(ctx, object, object); - object->objectValue->put(name, *value, /*flags*/ 0); + object->objectValue->setProperty(ctx, name, *value, /*flags*/ 0); } } @@ -487,7 +495,7 @@ void __qmljs_set_element_number(Context *ctx, Value *object, Value *index, doubl void __qmljs_set_activation_element(Context *ctx, String *name, Value *index, Value *value) { - if (Value *base = ctx->lookup(name)) { + if (Value *base = ctx->lookupPropertyDescriptor(name)) { __qmljs_set_element(ctx, base, index, value); } else { ctx->throwReferenceError(Value::fromString(name)); @@ -503,15 +511,15 @@ void __qmljs_set_activation_element_number(Context *ctx, String *name, Value *in void __qmljs_set_activation_property(Context *ctx, String *name, Value *value) { - if (Value *prop = ctx->lookup(name)) { + if (Value *prop = ctx->lookupPropertyDescriptor(name)) { *prop = *value; } else - ctx->engine->globalObject.objectValue->put(name, *value); + ctx->engine->globalObject.objectValue->setProperty(ctx, name, *value); } void __qmljs_copy_activation_property(Context *ctx, String *name, String *other) { - if (Value *source = ctx->lookup(other)) + if (Value *source = ctx->lookupPropertyDescriptor(other)) __qmljs_set_activation_property(ctx, name, source); else ctx->throwReferenceError(Value::fromString(name)); @@ -548,7 +556,9 @@ void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Fun void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name) { if (object->type == OBJECT_TYPE) { - object->objectValue->get(name, result); + *result = object->property(ctx, name); + } else if (object->type == STRING_TYPE && name->isEqualTo(ctx->engine->id_length)) { + __qmljs_init_number(result, object->stringValue->toQString().length()); } else { Value o; __qmljs_to_object(ctx, &o, object); @@ -561,7 +571,7 @@ void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *na void __qmljs_get_activation_property(Context *ctx, Value *result, String *name) { - if (Value *prop = ctx->lookup(name)) + if (Value *prop = ctx->lookupPropertyDescriptor(name)) *result = *prop; else ctx->throwReferenceError(Value::fromString(name)); @@ -577,14 +587,6 @@ void __qmljs_get_thisObject(Context *ctx, Value *result) *result = ctx->thisObject; } -void __qmljs_copy_property(Context *ctx, Value *target, String *name, Value *source, String *other) -{ - Q_UNUSED(ctx); - Value v; - source->objectValue->get(other, &v); - target->objectValue->put(name, v); -} - void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y, bool leftFirst) { Value px, py; @@ -667,7 +669,7 @@ bool __qmljs_equal(Context *ctx, const Value *x, const Value *y) void __qmljs_call_activation_property(Context *context, Value *result, String *name, Value *args, int argc) { - Value *func = context->lookup(name); + Value *func = context->lookupPropertyDescriptor(name); if (! func) context->throwReferenceError(Value::fromString(name)); else @@ -688,22 +690,17 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S baseObject = context->activation; __qmljs_init_null(&thisObject); } - Value func; - baseObject.objectValue->get(name, &func); - if (func.type == OBJECT_TYPE) { - if (FunctionObject *f = func.objectValue->asFunctionObject()) { - Context k; - Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->initCallContext(context->engine, &thisObject, f, args, argc); - f->call(ctx); - if (ctx->hasUncaughtException) { - context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception - context->result = ctx->result; - } - ctx->leaveCallContext(f, result); - } else { - context->throwTypeError(); + Value func = baseObject.property(context, name); + if (FunctionObject *f = func.asFunctionObject()) { + Context k; + Context *ctx = f->needsActivation ? context->engine->newContext() : &k; + ctx->initCallContext(context->engine, &thisObject, f, args, argc); + f->call(ctx); + if (ctx->hasUncaughtException) { + context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception + context->result = ctx->result; } + ctx->leaveCallContext(f, result); } else { context->throwTypeError(); } @@ -711,20 +708,16 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S void __qmljs_call_value(Context *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) { - if (func->type == OBJECT_TYPE) { - if (FunctionObject *f = func->objectValue->asFunctionObject()) { - Context k; - Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->initCallContext(context->engine, thisObject, f, args, argc); - f->call(ctx); - if (ctx->hasUncaughtException) { - context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception - context->result = ctx->result; - } - ctx->leaveCallContext(f, result); - } else { - context->throwTypeError(); + if (FunctionObject *f = func->asFunctionObject()) { + Context k; + Context *ctx = f->needsActivation ? context->engine->newContext() : &k; + ctx->initCallContext(context->engine, thisObject, f, args, argc); + f->call(ctx); + if (ctx->hasUncaughtException) { + context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception + context->result = ctx->result; } + ctx->leaveCallContext(f, result); } else { context->throwTypeError(); } @@ -732,7 +725,7 @@ void __qmljs_call_value(Context *context, Value *result, const Value *thisObject void __qmljs_construct_activation_property(Context *context, Value *result, String *name, Value *args, int argc) { - Value *func = context->lookup(name); + Value *func = context->lookupPropertyDescriptor(name); if (! func) context->throwReferenceError(Value::fromString(name)); else @@ -741,21 +734,16 @@ void __qmljs_construct_activation_property(Context *context, Value *result, Stri void __qmljs_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc) { - Q_UNUSED(context); - if (func->type == OBJECT_TYPE) { - if (FunctionObject *f = func->objectValue->asFunctionObject()) { - Context k; - Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->initConstructorContext(context->engine, 0, f, args, argc); - f->construct(ctx); - if (ctx->hasUncaughtException) { - context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception - context->result = ctx->result; - } - ctx->leaveConstructorContext(f, result); - } else { - context->throwTypeError(); + if (FunctionObject *f = func->asFunctionObject()) { + Context k; + Context *ctx = f->needsActivation ? context->engine->newContext() : &k; + ctx->initConstructorContext(context->engine, 0, f, args, argc); + f->construct(ctx); + if (ctx->hasUncaughtException) { + context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception + context->result = ctx->result; } + ctx->leaveConstructorContext(f, result); } else { context->throwTypeError(); } @@ -763,28 +751,23 @@ void __qmljs_construct_value(Context *context, Value *result, const Value *func, void __qmljs_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) { - Value func; Value thisObject = *base; if (thisObject.type != OBJECT_TYPE) __qmljs_to_object(context, &thisObject, base); assert(thisObject.type == OBJECT_TYPE); - thisObject.objectValue->get(name, &func); - if (func.type == OBJECT_TYPE) { - if (FunctionObject *f = func.objectValue->asFunctionObject()) { - Context k; - Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->initConstructorContext(context->engine, 0, f, args, argc); - ctx->calledAsConstructor = true; - f->construct(ctx); - if (ctx->hasUncaughtException) { - context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception - context->result = ctx->result; - } - ctx->leaveConstructorContext(f, result); - } else { - context->throwTypeError(); + Value func = thisObject.property(context, name); + if (FunctionObject *f = func.asFunctionObject()) { + Context k; + Context *ctx = f->needsActivation ? context->engine->newContext() : &k; + ctx->initConstructorContext(context->engine, 0, f, args, argc); + ctx->calledAsConstructor = true; + f->construct(ctx); + if (ctx->hasUncaughtException) { + context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception + context->result = ctx->result; } + ctx->leaveConstructorContext(f, result); } else { context->throwTypeError(); } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 36f3b4cd9c..e19309aea9 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -114,7 +114,6 @@ void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Fun void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name); void __qmljs_get_activation_property(Context *ctx, Value *result, String *name); void __qmljs_copy_activation_property(Context *ctx, String *name, String *other); -void __qmljs_copy_property(Context *ctx, Value *target, String *name, Value *source, String *other); void __qmljs_get_element(Context *ctx, Value *result, Value *object, Value *index); @@ -271,6 +270,9 @@ struct Value { ArrayObject *asArrayObject() const; ErrorObject *asErrorObject() const; ArgumentsObject *asArgumentsObject() const; + + Value property(Context *ctx, String *name) const; + Value *getPropertyDescriptor(Context *ctx, String *name) const; }; extern "C" { diff --git a/qv4codegen.cpp b/qv4codegen.cpp index c9d14f778f..a4c5ec92e5 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -209,7 +209,7 @@ protected: { if (! _env->hasDirectEval) { if (IdentifierExpression *id = cast(ast->base)) { - if (id->name == QLatin1String("eval")) { + if (id->name == QStringLiteral("eval")) { _env->hasDirectEval = true; } } @@ -287,7 +287,7 @@ IR::Function *Codegen::operator()(Program *node, IR::Module *module) ScanFunctions scan(this); scan(node); - IR::Function *globalCode = defineFunction(QLatin1String("%entry"), node, 0, node->elements); + IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, node->elements); foreach (IR::Function *function, _module->functions) { linearize(function); @@ -803,7 +803,7 @@ bool Codegen::visit(Expression *ast) bool Codegen::visit(ArrayLiteral *ast) { const unsigned t = _block->newTemp(); - move(_block->TEMP(t), _block->NEW(_block->NAME(QLatin1String("Array"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); + move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Array"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); int index = 0; for (ElementList *it = ast->elements; it; it = it->next) { for (Elision *elision = it->elision; elision; elision = elision->next) @@ -1138,7 +1138,7 @@ bool Codegen::visit(NumericLiteral *ast) bool Codegen::visit(ObjectLiteral *ast) { const unsigned t = _block->newTemp(); - move(_block->TEMP(t), _block->NEW(_block->NAME(QLatin1String("Object"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); + move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Object"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); for (PropertyNameAndValueList *it = ast->properties; it; it = it->next) { QString name = propertyName(it->name); Result value = expression(it->value); @@ -1215,7 +1215,7 @@ bool Codegen::visit(StringLiteral *ast) bool Codegen::visit(ThisExpression *ast) { - _expr.code = _block->NAME(QLatin1String("this"), ast->thisToken.startLine, ast->thisToken.startColumn); + _expr.code = _block->NAME(QStringLiteral("this"), ast->thisToken.startLine, ast->thisToken.startColumn); return false; } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 533cf44078..119e163da3 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -322,52 +322,52 @@ static inline double ParseString(const QString &s) dt = QDateTime::fromString(s, Qt::ISODate); if (!dt.isValid()) { QStringList formats; - formats << QLatin1String("M/d/yyyy") - << QLatin1String("M/d/yyyy hh:mm") - << QLatin1String("M/d/yyyy hh:mm A") - - << QLatin1String("M/d/yyyy, hh:mm") - << QLatin1String("M/d/yyyy, hh:mm A") - - << QLatin1String("MMM d yyyy") - << QLatin1String("MMM d yyyy hh:mm") - << QLatin1String("MMM d yyyy hh:mm:ss") - << QLatin1String("MMM d yyyy, hh:mm") - << QLatin1String("MMM d yyyy, hh:mm:ss") - - << QLatin1String("MMMM d yyyy") - << QLatin1String("MMMM d yyyy hh:mm") - << QLatin1String("MMMM d yyyy hh:mm:ss") - << QLatin1String("MMMM d yyyy, hh:mm") - << QLatin1String("MMMM d yyyy, hh:mm:ss") - - << QLatin1String("MMM d, yyyy") - << QLatin1String("MMM d, yyyy hh:mm") - << QLatin1String("MMM d, yyyy hh:mm:ss") - - << QLatin1String("MMMM d, yyyy") - << QLatin1String("MMMM d, yyyy hh:mm") - << QLatin1String("MMMM d, yyyy hh:mm:ss") - - << QLatin1String("d MMM yyyy") - << QLatin1String("d MMM yyyy hh:mm") - << QLatin1String("d MMM yyyy hh:mm:ss") - << QLatin1String("d MMM yyyy, hh:mm") - << QLatin1String("d MMM yyyy, hh:mm:ss") - - << QLatin1String("d MMMM yyyy") - << QLatin1String("d MMMM yyyy hh:mm") - << QLatin1String("d MMMM yyyy hh:mm:ss") - << QLatin1String("d MMMM yyyy, hh:mm") - << QLatin1String("d MMMM yyyy, hh:mm:ss") - - << QLatin1String("d MMM, yyyy") - << QLatin1String("d MMM, yyyy hh:mm") - << QLatin1String("d MMM, yyyy hh:mm:ss") - - << QLatin1String("d MMMM, yyyy") - << QLatin1String("d MMMM, yyyy hh:mm") - << QLatin1String("d MMMM, yyyy hh:mm:ss"); + formats << QStringLiteral("M/d/yyyy") + << QStringLiteral("M/d/yyyy hh:mm") + << QStringLiteral("M/d/yyyy hh:mm A") + + << QStringLiteral("M/d/yyyy, hh:mm") + << QStringLiteral("M/d/yyyy, hh:mm A") + + << QStringLiteral("MMM d yyyy") + << QStringLiteral("MMM d yyyy hh:mm") + << QStringLiteral("MMM d yyyy hh:mm:ss") + << QStringLiteral("MMM d yyyy, hh:mm") + << QStringLiteral("MMM d yyyy, hh:mm:ss") + + << QStringLiteral("MMMM d yyyy") + << QStringLiteral("MMMM d yyyy hh:mm") + << QStringLiteral("MMMM d yyyy hh:mm:ss") + << QStringLiteral("MMMM d yyyy, hh:mm") + << QStringLiteral("MMMM d yyyy, hh:mm:ss") + + << QStringLiteral("MMM d, yyyy") + << QStringLiteral("MMM d, yyyy hh:mm") + << QStringLiteral("MMM d, yyyy hh:mm:ss") + + << QStringLiteral("MMMM d, yyyy") + << QStringLiteral("MMMM d, yyyy hh:mm") + << QStringLiteral("MMMM d, yyyy hh:mm:ss") + + << QStringLiteral("d MMM yyyy") + << QStringLiteral("d MMM yyyy hh:mm") + << QStringLiteral("d MMM yyyy hh:mm:ss") + << QStringLiteral("d MMM yyyy, hh:mm") + << QStringLiteral("d MMM yyyy, hh:mm:ss") + + << QStringLiteral("d MMMM yyyy") + << QStringLiteral("d MMMM yyyy hh:mm") + << QStringLiteral("d MMMM yyyy hh:mm:ss") + << QStringLiteral("d MMMM yyyy, hh:mm") + << QStringLiteral("d MMMM yyyy, hh:mm:ss") + + << QStringLiteral("d MMM, yyyy") + << QStringLiteral("d MMM, yyyy hh:mm") + << QStringLiteral("d MMM, yyyy hh:mm:ss") + + << QStringLiteral("d MMMM, yyyy") + << QStringLiteral("d MMMM, yyyy hh:mm") + << QStringLiteral("d MMMM, yyyy hh:mm:ss"); for (int i = 0; i < formats.size(); ++i) { dt = QDateTime::fromString(s, formats.at(i)); @@ -403,8 +403,8 @@ static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) static inline QString ToString(double t) { if (qIsNaN(t)) - return QLatin1String("Invalid Date"); - QString str = ToDateTime(t, Qt::LocalTime).toString() + QLatin1String(" GMT"); + return QStringLiteral("Invalid Date"); + QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT"); double tzoffset = LocalTZA + DaylightSavingTA(t); if (tzoffset) { int hours = static_cast(::fabs(tzoffset) / 1000 / 60 / 60); @@ -423,8 +423,8 @@ static inline QString ToString(double t) static inline QString ToUTCString(double t) { if (qIsNaN(t)) - return QLatin1String("Invalid Date"); - return ToDateTime(t, Qt::UTC).toString() + QLatin1String(" GMT"); + return QStringLiteral("Invalid Date"); + return ToDateTime(t, Qt::UTC).toString() + QStringLiteral(" GMT"); } static inline QString ToDateString(double t) @@ -477,7 +477,7 @@ Value ObjectCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; FunctionObject *ctor = ctx->engine->newObjectCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newObjectPrototype(ctx, ctor))); + ctor->setProperty(ctx, QStringLiteral("prototype"), Value::fromObject(ctx->engine->newObjectPrototype(ctx, ctor))); return Value::fromObject(ctor); } @@ -498,8 +498,8 @@ void ObjectCtor::call(Context *) ObjectPrototype::ObjectPrototype(Context *ctx, FunctionObject *ctor) { - setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); - setProperty(ctx, QLatin1String("toString"), method_toString, 0); + setProperty(ctx, QStringLiteral("constructor"), Value::fromObject(ctor)); + setProperty(ctx, QStringLiteral("toString"), method_toString, 0); } void ObjectPrototype::method_toString(Context *ctx) @@ -514,7 +514,7 @@ Value StringCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; FunctionObject *ctor = ctx->engine->newStringCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newStringPrototype(ctx, ctor))); + ctor->setProperty(ctx, QStringLiteral("prototype"), Value::fromObject(ctx->engine->newStringPrototype(ctx, ctor))); return Value::fromObject(ctor); } @@ -544,27 +544,27 @@ void StringCtor::call(Context *ctx) StringPrototype::StringPrototype(Context *ctx, FunctionObject *ctor) { - setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); - setProperty(ctx, QLatin1String("toString"), method_toString); - setProperty(ctx, QLatin1String("valueOf"), method_valueOf); - setProperty(ctx, QLatin1String("charAt"), method_charAt); - setProperty(ctx, QLatin1String("charCodeAt"), method_charCodeAt); - setProperty(ctx, QLatin1String("concat"), method_concat); - setProperty(ctx, QLatin1String("indexOf"), method_indexOf); - setProperty(ctx, QLatin1String("lastIndexOf"), method_lastIndexOf); - setProperty(ctx, QLatin1String("localeCompare"), method_localeCompare); - setProperty(ctx, QLatin1String("match"), method_match); - setProperty(ctx, QLatin1String("replace"), method_replace); - setProperty(ctx, QLatin1String("search"), method_search); - setProperty(ctx, QLatin1String("slice"), method_slice); - setProperty(ctx, QLatin1String("split"), method_split); - setProperty(ctx, QLatin1String("substr"), method_substr); - setProperty(ctx, QLatin1String("substring"), method_substring); - setProperty(ctx, QLatin1String("toLowerCase"), method_toLowerCase); - setProperty(ctx, QLatin1String("toLocaleLowerCase"), method_toLocaleLowerCase); - setProperty(ctx, QLatin1String("toUpperCase"), method_toUpperCase); - setProperty(ctx, QLatin1String("toLocaleUpperCase"), method_toLocaleUpperCase); - setProperty(ctx, QLatin1String("fromCharCode"), method_fromCharCode); + setProperty(ctx, QStringLiteral("constructor"), Value::fromObject(ctor)); + setProperty(ctx, QStringLiteral("toString"), method_toString); + setProperty(ctx, QStringLiteral("valueOf"), method_valueOf); + setProperty(ctx, QStringLiteral("charAt"), method_charAt); + setProperty(ctx, QStringLiteral("charCodeAt"), method_charCodeAt); + setProperty(ctx, QStringLiteral("concat"), method_concat); + setProperty(ctx, QStringLiteral("indexOf"), method_indexOf); + setProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf); + setProperty(ctx, QStringLiteral("localeCompare"), method_localeCompare); + setProperty(ctx, QStringLiteral("match"), method_match); + setProperty(ctx, QStringLiteral("replace"), method_replace); + setProperty(ctx, QStringLiteral("search"), method_search); + setProperty(ctx, QStringLiteral("slice"), method_slice); + setProperty(ctx, QStringLiteral("split"), method_split); + setProperty(ctx, QStringLiteral("substr"), method_substr); + setProperty(ctx, QStringLiteral("substring"), method_substring); + setProperty(ctx, QStringLiteral("toLowerCase"), method_toLowerCase); + setProperty(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); + setProperty(ctx, QStringLiteral("toUpperCase"), method_toUpperCase); + setProperty(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); + setProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode); } QString StringPrototype::getThisString(Context *ctx) @@ -694,19 +694,19 @@ void StringPrototype::method_localeCompare(Context *ctx) void StringPrototype::method_match(Context *ctx) { // requires Regexp - ctx->throwUnimplemented(QLatin1String("String.prototype.match")); + ctx->throwUnimplemented(QStringLiteral("String.prototype.match")); } void StringPrototype::method_replace(Context *ctx) { // requires Regexp - ctx->throwUnimplemented(QLatin1String("String.prototype.replace")); + ctx->throwUnimplemented(QStringLiteral("String.prototype.replace")); } void StringPrototype::method_search(Context *ctx) { // requires Regexp - ctx->throwUnimplemented(QLatin1String("String.prototype.search")); + ctx->throwUnimplemented(QStringLiteral("String.prototype.search")); } void StringPrototype::method_slice(Context *ctx) @@ -734,7 +734,7 @@ void StringPrototype::method_slice(Context *ctx) void StringPrototype::method_split(Context *ctx) { - ctx->throwUnimplemented(QLatin1String("String.prototype.splt")); + ctx->throwUnimplemented(QStringLiteral("String.prototype.splt")); } void StringPrototype::method_substr(Context *ctx) @@ -836,7 +836,7 @@ Value NumberCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; FunctionObject *ctor = ctx->engine->newNumberCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newNumberPrototype(ctx, ctor))); + ctor->setProperty(ctx, QStringLiteral("prototype"), Value::fromObject(ctx->engine->newNumberPrototype(ctx, ctor))); return Value::fromObject(ctor); } @@ -860,26 +860,26 @@ void NumberCtor::call(Context *ctx) NumberPrototype::NumberPrototype(Context *ctx, FunctionObject *ctor) : NumberObject(Value::fromNumber(0)) { - ctor->setProperty(ctx, QLatin1String("NaN"), Value::fromNumber(qSNaN())); - ctor->setProperty(ctx, QLatin1String("NEGATIVE_INFINITY"), Value::fromNumber(-qInf())); - ctor->setProperty(ctx, QLatin1String("POSITIVE_INFINITY"), Value::fromNumber(qInf())); - ctor->setProperty(ctx, QLatin1String("MAX_VALUE"), Value::fromNumber(1.7976931348623158e+308)); + ctor->setProperty(ctx, QStringLiteral("NaN"), Value::fromNumber(qSNaN())); + ctor->setProperty(ctx, QStringLiteral("NEGATIVE_INFINITY"), Value::fromNumber(-qInf())); + ctor->setProperty(ctx, QStringLiteral("POSITIVE_INFINITY"), Value::fromNumber(qInf())); + ctor->setProperty(ctx, QStringLiteral("MAX_VALUE"), Value::fromNumber(1.7976931348623158e+308)); #ifdef __INTEL_COMPILER # pragma warning( push ) # pragma warning(disable: 239) #endif - ctor->setProperty(ctx, QLatin1String("MIN_VALUE"), Value::fromNumber(5e-324)); + ctor->setProperty(ctx, QStringLiteral("MIN_VALUE"), Value::fromNumber(5e-324)); #ifdef __INTEL_COMPILER # pragma warning( pop ) #endif - setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); - setProperty(ctx, QLatin1String("toString"), method_toString); - setProperty(ctx, QLatin1String("toLocalString"), method_toLocaleString); - setProperty(ctx, QLatin1String("valueOf"), method_valueOf); - setProperty(ctx, QLatin1String("toFixed"), method_toFixed); - setProperty(ctx, QLatin1String("toExponential"), method_toExponential); - setProperty(ctx, QLatin1String("toPrecision"), method_toPrecision); + setProperty(ctx, QStringLiteral("constructor"), Value::fromObject(ctor)); + setProperty(ctx, QStringLiteral("toString"), method_toString); + setProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString); + setProperty(ctx, QStringLiteral("valueOf"), method_valueOf); + setProperty(ctx, QStringLiteral("toFixed"), method_toFixed); + setProperty(ctx, QStringLiteral("toExponential"), method_toExponential); + setProperty(ctx, QStringLiteral("toPrecision"), method_toPrecision); } void NumberPrototype::method_toString(Context *ctx) @@ -896,7 +896,7 @@ void NumberPrototype::method_toString(Context *ctx) double num = thisObject->value.numberValue; if (qIsNaN(num)) { - ctx->result = Value::fromString(ctx, QLatin1String("NaN")); + ctx->result = Value::fromString(ctx, QStringLiteral("NaN")); return; } else if (qIsInf(num)) { ctx->result = Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); @@ -1023,7 +1023,7 @@ Value BooleanCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; FunctionObject *ctor = ctx->engine->newBooleanCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newBooleanPrototype(ctx, ctor))); + ctor->setProperty(ctx, QStringLiteral("prototype"), Value::fromObject(ctx->engine->newBooleanPrototype(ctx, ctor))); return Value::fromObject(ctor); } @@ -1047,9 +1047,9 @@ void BooleanCtor::call(Context *ctx) BooleanPrototype::BooleanPrototype(Context *ctx, FunctionObject *ctor) : BooleanObject(Value::fromBoolean(false)) { - ctor->setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); - ctor->setProperty(ctx, QLatin1String("toString"), method_toString); - ctor->setProperty(ctx, QLatin1String("valueOf"), method_valueOf); + ctor->setProperty(ctx, QStringLiteral("constructor"), Value::fromObject(ctor)); + ctor->setProperty(ctx, QStringLiteral("toString"), method_toString); + ctor->setProperty(ctx, QStringLiteral("valueOf"), method_valueOf); } void BooleanPrototype::method_toString(Context *ctx) @@ -1077,7 +1077,7 @@ Value ArrayCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; FunctionObject *ctor = ctx->engine->newArrayCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newArrayPrototype(ctx, ctor))); + ctor->setProperty(ctx, QStringLiteral("prototype"), Value::fromObject(ctx->engine->newArrayPrototype(ctx, ctor))); return Value::fromObject(ctor); } @@ -1100,7 +1100,7 @@ void ArrayCtor::call(Context *ctx) quint32 isize = Value::toUInt32(size); if (size != double(isize)) { - ctx->throwError(QLatin1String("Invalid array length")); + ctx->throwError(QStringLiteral("Invalid array length")); return; } @@ -1116,28 +1116,28 @@ void ArrayCtor::call(Context *ctx) ArrayPrototype::ArrayPrototype(Context *ctx, FunctionObject *ctor) { - setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); - setProperty(ctx, QLatin1String("toString"), method_toString, 0); - setProperty(ctx, QLatin1String("toLocalString"), method_toLocaleString, 0); - setProperty(ctx, QLatin1String("concat"), method_concat, 1); - setProperty(ctx, QLatin1String("join"), method_join, 1); - setProperty(ctx, QLatin1String("pop"), method_pop, 0); - setProperty(ctx, QLatin1String("push"), method_push, 1); - setProperty(ctx, QLatin1String("reverse"), method_reverse, 0); - setProperty(ctx, QLatin1String("shift"), method_shift, 0); - setProperty(ctx, QLatin1String("slice"), method_slice, 2); - setProperty(ctx, QLatin1String("sort"), method_sort, 1); - setProperty(ctx, QLatin1String("splice"), method_splice, 2); - setProperty(ctx, QLatin1String("unshift"), method_unshift, 1); - setProperty(ctx, QLatin1String("indexOf"), method_indexOf, 0); - setProperty(ctx, QLatin1String("lastIndexOf"), method_lastIndexOf, 0); - setProperty(ctx, QLatin1String("every"), method_every, 0); - setProperty(ctx, QLatin1String("some"), method_some, 0); - setProperty(ctx, QLatin1String("forEach"), method_forEach, 0); - setProperty(ctx, QLatin1String("map"), method_map, 0); - setProperty(ctx, QLatin1String("filter"), method_filter, 0); - setProperty(ctx, QLatin1String("reduce"), method_reduce, 0); - setProperty(ctx, QLatin1String("reduceRight"), method_reduceRight, 0); + setProperty(ctx, QStringLiteral("constructor"), Value::fromObject(ctor)); + setProperty(ctx, QStringLiteral("toString"), method_toString, 0); + setProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString, 0); + setProperty(ctx, QStringLiteral("concat"), method_concat, 1); + setProperty(ctx, QStringLiteral("join"), method_join, 1); + setProperty(ctx, QStringLiteral("pop"), method_pop, 0); + setProperty(ctx, QStringLiteral("push"), method_push, 1); + setProperty(ctx, QStringLiteral("reverse"), method_reverse, 0); + setProperty(ctx, QStringLiteral("shift"), method_shift, 0); + setProperty(ctx, QStringLiteral("slice"), method_slice, 2); + setProperty(ctx, QStringLiteral("sort"), method_sort, 1); + setProperty(ctx, QStringLiteral("splice"), method_splice, 2); + setProperty(ctx, QStringLiteral("unshift"), method_unshift, 1); + setProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 0); + setProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 0); + setProperty(ctx, QStringLiteral("every"), method_every, 0); + setProperty(ctx, QStringLiteral("some"), method_some, 0); + setProperty(ctx, QStringLiteral("forEach"), method_forEach, 0); + setProperty(ctx, QStringLiteral("map"), method_map, 0); + setProperty(ctx, QStringLiteral("filter"), method_filter, 0); + setProperty(ctx, QStringLiteral("reduce"), method_reduce, 0); + setProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 0); } void ArrayPrototype::method_toString(Context *ctx) @@ -1181,15 +1181,13 @@ void ArrayPrototype::method_join(Context *ctx) QString r4; if (arg.isUndefined()) - r4 = QLatin1String(","); + r4 = QStringLiteral(","); else r4 = arg.toString(ctx)->toQString(); Value self = ctx->thisObject; - - Value *length = self.objectValue->getProperty(ctx->engine->identifier("length")); - double r1 = length ? length->toNumber(ctx) : 0; - quint32 r2 = Value::toUInt32(r1); + const Value length = self.property(ctx, ctx->engine->id_length); + const quint32 r2 = Value::toUInt32(length.isUndefined() ? 0 : length.toNumber(ctx)); static QSet visitedArrayElements; @@ -1203,7 +1201,7 @@ void ArrayPrototype::method_join(Context *ctx) QString R; - if (ArrayObject *a = self.objectValue->asArrayObject()) { + if (ArrayObject *a = self.asArrayObject()) { for (uint i = 0; i < a->value.size(); ++i) { if (i) R += r4; @@ -1216,18 +1214,18 @@ void ArrayPrototype::method_join(Context *ctx) // // crazy! // - Value *r6 = self.objectValue->getProperty(ctx->engine->identifier(QLatin1String("0"))); - if (r6 && !(r6->isUndefined() || r6->isNull())) - R = r6->toString(ctx)->toQString(); + Value r6 = self.property(ctx, ctx->engine->identifier(QStringLiteral("0"))); + if (!(r6.isUndefined() || r6.isNull())) + R = r6.toString(ctx)->toQString(); for (quint32 k = 1; k < r2; ++k) { R += r4; String *name = Value::fromNumber(k).toString(ctx); - Value *r12 = self.objectValue->getProperty(name); + Value r12 = self.property(ctx, name); - if (r12 && ! (r12->isUndefined() || r12->isNull())) - R += r12->toString(ctx)->toQString(); + if (! (r12.isUndefined() || r12.isNull())) + R += r12.toString(ctx)->toQString(); } } @@ -1242,18 +1240,16 @@ void ArrayPrototype::method_pop(Context *ctx) Value elt = instance->value.pop(); ctx->result = elt; } else { - String *id_length = ctx->engine->identifier(QLatin1String("length")); - Value *r1 = self.objectValue->getProperty(id_length); - quint32 r2 = r1 ? r1->toUInt32(ctx) : 0; + Value r1 = self.property(ctx, ctx->engine->id_length); + quint32 r2 = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; if (! r2) { - self.objectValue->put(id_length, Value::fromNumber(0)); + self.objectValue->setProperty(ctx, ctx->engine->id_length, Value::fromNumber(0)); } else { String *r6 = Value::fromNumber(r2 - 1).toString(ctx); - Value *r7 = self.objectValue->getProperty(r6); - self.objectValue->deleteProperty(r6, 0); - self.objectValue->put(id_length, Value::fromNumber(2 - 1)); - if (r7) - ctx->result = *r7; + Value r7 = self.property(ctx, r6); + self.objectValue->deleteProperty(ctx, r6, 0); + self.objectValue->setProperty(ctx, ctx->engine->id_length, Value::fromNumber(2 - 1)); + ctx->result = r7; } } } @@ -1269,16 +1265,15 @@ void ArrayPrototype::method_push(Context *ctx) } ctx->result = Value::fromNumber(pos); } else { - String *id_length = ctx->engine->identifier(QLatin1String("length")); - Value *r1 = self.objectValue->getProperty(id_length); - quint32 n = r1 ? r1->toUInt32(ctx) : 0; + Value r1 = self.property(ctx, ctx->engine->id_length); + quint32 n = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; for (size_t index = 0; index < ctx->argumentCount; ++index, ++n) { Value r3 = ctx->argument(index); String *name = Value::fromNumber(n).toString(ctx); - self.objectValue->put(name, r3); + self.objectValue->setProperty(ctx, name, r3); } Value r = Value::fromNumber(n); - self.objectValue->put(id_length, r); + self.objectValue->setProperty(ctx, ctx->engine->id_length, r); ctx->result = r; } } @@ -1295,7 +1290,7 @@ void ArrayPrototype::method_reverse(Context *ctx) instance->value.assign(hi, tmp); } } else { - ctx->throwUnimplemented(QLatin1String("Array.prototype.reverse")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.reverse")); } } @@ -1305,7 +1300,7 @@ void ArrayPrototype::method_shift(Context *ctx) if (ArrayObject *instance = self.asArrayObject()) { ctx->result = instance->value.takeFirst(); } else { - ctx->throwUnimplemented(QLatin1String("Array.prototype.reverse")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.reverse")); } } @@ -1317,9 +1312,8 @@ void ArrayPrototype::method_slice(Context *ctx) Value start = ctx->argument(0); Value end = ctx->argument(1); Value self = ctx->thisObject; - String *id_length = ctx->engine->identifier(QLatin1String("length")); - Value *l = self.objectValue->getProperty(id_length); - double r2 = l ? l->toNumber(ctx) : 0; + Value l = self.property(ctx, ctx->engine->id_length); + double r2 = !l.isUndefined() ? l.toNumber(ctx) : 0; quint32 r3 = Value::toUInt32(r2); qint32 r4 = qint32(start.toInteger(ctx)); quint32 r5 = r4 < 0 ? qMax(quint32(r3 + r4), quint32(0)) : qMin(quint32(r4), r3); @@ -1329,8 +1323,9 @@ void ArrayPrototype::method_slice(Context *ctx) quint32 n = 0; for (; k < r8; ++k) { String *r11 = Value::fromNumber(k).toString(ctx); - if (Value *v = self.objectValue->getProperty(r11)) - result.assign(n++, *v); + Value v = self.property(ctx, r11); + if (! v.isUndefined()) + result.assign(n++, v); } ctx->result = Value::fromObject(ctx->engine->newArrayObject(result)); } @@ -1343,7 +1338,7 @@ void ArrayPrototype::method_sort(Context *ctx) instance->value.sort(ctx, comparefn); ctx->result = ctx->thisObject; } else { - ctx->throwUnimplemented(QLatin1String("Array.prototype.sort")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.sort")); } } @@ -1365,23 +1360,23 @@ void ArrayPrototype::method_splice(Context *ctx) instance->value.splice(start, deleteCount, items, otherInstance->value); ctx->result = a; } else { - ctx->throwUnimplemented(QLatin1String("Array.prototype.splice")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.splice")); } } void ArrayPrototype::method_unshift(Context *ctx) { - ctx->throwUnimplemented(QLatin1String("Array.prototype.indexOf")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); } void ArrayPrototype::method_indexOf(Context *ctx) { - ctx->throwUnimplemented(QLatin1String("Array.prototype.indexOf")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); } void ArrayPrototype::method_lastIndexOf(Context *ctx) { - ctx->throwUnimplemented(QLatin1String("Array.prototype.indexOf")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); } void ArrayPrototype::method_every(Context *ctx) @@ -1410,7 +1405,7 @@ void ArrayPrototype::method_every(Context *ctx) ctx->throwTypeError(); } } else { - ctx->throwUnimplemented(QLatin1String("Array.prototype.every")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.every")); } } @@ -1440,7 +1435,7 @@ void ArrayPrototype::method_some(Context *ctx) ctx->throwTypeError(); } } else { - ctx->throwUnimplemented(QLatin1String("Array.prototype.some")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.some")); } } @@ -1466,7 +1461,7 @@ void ArrayPrototype::method_forEach(Context *ctx) } } } else { - ctx->throwUnimplemented(QLatin1String("Array.prototype.forEach")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.forEach")); } } @@ -1496,7 +1491,7 @@ void ArrayPrototype::method_map(Context *ctx) ctx->result = Value::fromObject(a); } } else { - ctx->throwUnimplemented(QLatin1String("Array.prototype.map")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.map")); } } @@ -1529,7 +1524,7 @@ void ArrayPrototype::method_filter(Context *ctx) ctx->result = Value::fromObject(a); } } else { - ctx->throwUnimplemented(QLatin1String("Array.prototype.filter")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.filter")); } } @@ -1561,7 +1556,7 @@ void ArrayPrototype::method_reduce(Context *ctx) } ctx->result = acc; } else { - ctx->throwUnimplemented(QLatin1String("Array.prototype.reduce")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.reduce")); } } @@ -1593,7 +1588,7 @@ void ArrayPrototype::method_reduceRight(Context *ctx) } ctx->result = acc; } else { - ctx->throwUnimplemented(QLatin1String("Array.prototype.reduceRight")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.reduceRight")); } } @@ -1607,7 +1602,7 @@ Value FunctionCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; FunctionObject *ctor = ctx->engine->newFunctionCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newFunctionPrototype(ctx, ctor))); + ctor->setProperty(ctx, QStringLiteral("prototype"), Value::fromObject(ctx->engine->newFunctionPrototype(ctx, ctor))); return Value::fromObject(ctor); } @@ -1618,28 +1613,29 @@ FunctionCtor::FunctionCtor(Context *scope) void FunctionCtor::construct(Context *ctx) { - ctx->throwUnimplemented(QLatin1String("Function.prototype.constructor")); + ctx->throwUnimplemented(QStringLiteral("Function.prototype.constructor")); } void FunctionCtor::call(Context *ctx) { - ctx->throwUnimplemented(QLatin1String("Function")); + ctx->throwUnimplemented(QStringLiteral("Function")); } FunctionPrototype::FunctionPrototype(Context *ctx, FunctionObject *ctor) : FunctionObject(ctx) { - setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); - setProperty(ctx, QLatin1String("toString"), method_toString, 0); - setProperty(ctx, QLatin1String("apply"), method_apply, 0); - setProperty(ctx, QLatin1String("call"), method_call, 0); - setProperty(ctx, QLatin1String("bind"), method_bind, 0); + setProperty(ctx, QStringLiteral("constructor"), Value::fromObject(ctor)); + setProperty(ctx, QStringLiteral("toString"), method_toString, 0); + setProperty(ctx, QStringLiteral("apply"), method_apply, 0); + setProperty(ctx, QStringLiteral("call"), method_call, 0); + setProperty(ctx, QStringLiteral("bind"), method_bind, 0); } void FunctionPrototype::method_toString(Context *ctx) { if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { - ctx->result = Value::fromString(ctx, QLatin1String("function() { [code] }")); + Q_UNUSED(fun); + ctx->result = Value::fromString(ctx, QStringLiteral("function() { [code] }")); } else { ctx->throwTypeError(); } @@ -1648,7 +1644,8 @@ void FunctionPrototype::method_toString(Context *ctx) void FunctionPrototype::method_apply(Context *ctx) { if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { - ctx->throwUnimplemented(QLatin1String("Function.prototype.apply")); + Q_UNUSED(fun); + ctx->throwUnimplemented(QStringLiteral("Function.prototype.apply")); } else { ctx->throwTypeError(); } @@ -1657,6 +1654,7 @@ void FunctionPrototype::method_apply(Context *ctx) void FunctionPrototype::method_call(Context *ctx) { if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { + Q_UNUSED(fun); Value thisArg = ctx->argument(0); QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); if (ctx->argumentCount) @@ -1670,7 +1668,8 @@ void FunctionPrototype::method_call(Context *ctx) void FunctionPrototype::method_bind(Context *ctx) { if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { - ctx->throwUnimplemented(QLatin1String("Function.prototype.bind")); + Q_UNUSED(fun); + ctx->throwUnimplemented(QStringLiteral("Function.prototype.bind")); } else { ctx->throwTypeError(); } @@ -1683,7 +1682,7 @@ Value DateCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; FunctionObject *ctor = ctx->engine->newDateCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newDatePrototype(ctx, ctor))); + ctor->setProperty(ctx, QStringLiteral("prototype"), Value::fromObject(ctx->engine->newDatePrototype(ctx, ctor))); return Value::fromObject(ctor); } @@ -1741,54 +1740,54 @@ DatePrototype::DatePrototype(Context *ctx, FunctionObject *ctor) { LocalTZA = getLocalTZA(); - ctor->setProperty(ctx, QLatin1String("parse"), method_parse, 1); - ctor->setProperty(ctx, QLatin1String("UTC"), method_UTC, 7); - - setProperty(ctx, QLatin1String("constructor"), Value::fromObject(ctor)); - setProperty(ctx, QLatin1String("toString"), method_toString, 0); - setProperty(ctx, QLatin1String("toDateString"), method_toDateString, 0); - setProperty(ctx, QLatin1String("toTimeString"), method_toTimeString, 0); - setProperty(ctx, QLatin1String("toLocaleString"), method_toLocaleString, 0); - setProperty(ctx, QLatin1String("toLocaleDateString"), method_toLocaleDateString, 0); - setProperty(ctx, QLatin1String("toLocaleTimeString"), method_toLocaleTimeString, 0); - setProperty(ctx, QLatin1String("valueOf"), method_valueOf, 0); - setProperty(ctx, QLatin1String("getTime"), method_getTime, 0); - setProperty(ctx, QLatin1String("getYear"), method_getYear, 0); - setProperty(ctx, QLatin1String("getFullYear"), method_getFullYear, 0); - setProperty(ctx, QLatin1String("getUTCFullYear"), method_getUTCFullYear, 0); - setProperty(ctx, QLatin1String("getMonth"), method_getMonth, 0); - setProperty(ctx, QLatin1String("getUTCMonth"), method_getUTCMonth, 0); - setProperty(ctx, QLatin1String("getDate"), method_getDate, 0); - setProperty(ctx, QLatin1String("getUTCDate"), method_getUTCDate, 0); - setProperty(ctx, QLatin1String("getDay"), method_getDay, 0); - setProperty(ctx, QLatin1String("getUTCDay"), method_getUTCDay, 0); - setProperty(ctx, QLatin1String("getHours"), method_getHours, 0); - setProperty(ctx, QLatin1String("getUTCHours"), method_getUTCHours, 0); - setProperty(ctx, QLatin1String("getMinutes"), method_getMinutes, 0); - setProperty(ctx, QLatin1String("getUTCMinutes"), method_getUTCMinutes, 0); - setProperty(ctx, QLatin1String("getSeconds"), method_getSeconds, 0); - setProperty(ctx, QLatin1String("getUTCSeconds"), method_getUTCSeconds, 0); - setProperty(ctx, QLatin1String("getMilliseconds"), method_getMilliseconds, 0); - setProperty(ctx, QLatin1String("getUTCMilliseconds"), method_getUTCMilliseconds, 0); - setProperty(ctx, QLatin1String("getTimezoneOffset"), method_getTimezoneOffset, 0); - setProperty(ctx, QLatin1String("setTime"), method_setTime, 1); - setProperty(ctx, QLatin1String("setMilliseconds"), method_setMilliseconds, 1); - setProperty(ctx, QLatin1String("setUTCMilliseconds"), method_setUTCMilliseconds, 1); - setProperty(ctx, QLatin1String("setSeconds"), method_setSeconds, 2); - setProperty(ctx, QLatin1String("setUTCSeconds"), method_setUTCSeconds, 2); - setProperty(ctx, QLatin1String("setMinutes"), method_setMinutes, 3); - setProperty(ctx, QLatin1String("setUTCMinutes"), method_setUTCMinutes, 3); - setProperty(ctx, QLatin1String("setHours"), method_setHours, 4); - setProperty(ctx, QLatin1String("setUTCHours"), method_setUTCHours, 4); - setProperty(ctx, QLatin1String("setDate"), method_setDate, 1); - setProperty(ctx, QLatin1String("setUTCDate"), method_setUTCDate, 1); - setProperty(ctx, QLatin1String("setMonth"), method_setMonth, 2); - setProperty(ctx, QLatin1String("setUTCMonth"), method_setUTCMonth, 2); - setProperty(ctx, QLatin1String("setYear"), method_setYear, 1); - setProperty(ctx, QLatin1String("setFullYear"), method_setFullYear, 3); - setProperty(ctx, QLatin1String("setUTCFullYear"), method_setUTCFullYear, 3); - setProperty(ctx, QLatin1String("toUTCString"), method_toUTCString, 0); - setProperty(ctx, QLatin1String("toGMTString"), method_toUTCString, 0); + ctor->setProperty(ctx, QStringLiteral("parse"), method_parse, 1); + ctor->setProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); + + setProperty(ctx, QStringLiteral("constructor"), Value::fromObject(ctor)); + setProperty(ctx, QStringLiteral("toString"), method_toString, 0); + setProperty(ctx, QStringLiteral("toDateString"), method_toDateString, 0); + setProperty(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0); + setProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + setProperty(ctx, QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); + setProperty(ctx, QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); + setProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + setProperty(ctx, QStringLiteral("getTime"), method_getTime, 0); + setProperty(ctx, QStringLiteral("getYear"), method_getYear, 0); + setProperty(ctx, QStringLiteral("getFullYear"), method_getFullYear, 0); + setProperty(ctx, QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0); + setProperty(ctx, QStringLiteral("getMonth"), method_getMonth, 0); + setProperty(ctx, QStringLiteral("getUTCMonth"), method_getUTCMonth, 0); + setProperty(ctx, QStringLiteral("getDate"), method_getDate, 0); + setProperty(ctx, QStringLiteral("getUTCDate"), method_getUTCDate, 0); + setProperty(ctx, QStringLiteral("getDay"), method_getDay, 0); + setProperty(ctx, QStringLiteral("getUTCDay"), method_getUTCDay, 0); + setProperty(ctx, QStringLiteral("getHours"), method_getHours, 0); + setProperty(ctx, QStringLiteral("getUTCHours"), method_getUTCHours, 0); + setProperty(ctx, QStringLiteral("getMinutes"), method_getMinutes, 0); + setProperty(ctx, QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0); + setProperty(ctx, QStringLiteral("getSeconds"), method_getSeconds, 0); + setProperty(ctx, QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0); + setProperty(ctx, QStringLiteral("getMilliseconds"), method_getMilliseconds, 0); + setProperty(ctx, QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0); + setProperty(ctx, QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0); + setProperty(ctx, QStringLiteral("setTime"), method_setTime, 1); + setProperty(ctx, QStringLiteral("setMilliseconds"), method_setMilliseconds, 1); + setProperty(ctx, QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1); + setProperty(ctx, QStringLiteral("setSeconds"), method_setSeconds, 2); + setProperty(ctx, QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2); + setProperty(ctx, QStringLiteral("setMinutes"), method_setMinutes, 3); + setProperty(ctx, QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3); + setProperty(ctx, QStringLiteral("setHours"), method_setHours, 4); + setProperty(ctx, QStringLiteral("setUTCHours"), method_setUTCHours, 4); + setProperty(ctx, QStringLiteral("setDate"), method_setDate, 1); + setProperty(ctx, QStringLiteral("setUTCDate"), method_setUTCDate, 1); + setProperty(ctx, QStringLiteral("setMonth"), method_setMonth, 2); + setProperty(ctx, QStringLiteral("setUTCMonth"), method_setUTCMonth, 2); + setProperty(ctx, QStringLiteral("setYear"), method_setYear, 1); + setProperty(ctx, QStringLiteral("setFullYear"), method_setFullYear, 3); + setProperty(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); + setProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); + setProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); } double DatePrototype::getThisDate(Context *ctx) @@ -1803,14 +1802,17 @@ double DatePrototype::getThisDate(Context *ctx) void DatePrototype::method_MakeTime(Context *ctx) { + ctx->throwUnimplemented(QStringLiteral("Data.MakeTime")); } void DatePrototype::method_MakeDate(Context *ctx) { + ctx->throwUnimplemented(QStringLiteral("Data.MakeDate")); } void DatePrototype::method_TimeClip(Context *ctx) { + ctx->throwUnimplemented(QStringLiteral("Data.TimeClip")); } void DatePrototype::method_parse(Context *ctx) @@ -2276,33 +2278,33 @@ void DatePrototype::method_toUTCString(Context *ctx) // MathObject::MathObject(Context *ctx) { - setProperty(ctx, QLatin1String("E"), Value::fromNumber(::exp(1.0))); - setProperty(ctx, QLatin1String("LN2"), Value::fromNumber(::log(2.0))); - setProperty(ctx, QLatin1String("LN10"), Value::fromNumber(::log(10.0))); - setProperty(ctx, QLatin1String("LOG2E"), Value::fromNumber(1.0/::log(2.0))); - setProperty(ctx, QLatin1String("LOG10E"), Value::fromNumber(1.0/::log(10.0))); - setProperty(ctx, QLatin1String("PI"), Value::fromNumber(qt_PI)); - setProperty(ctx, QLatin1String("SQRT1_2"), Value::fromNumber(::sqrt(0.5))); - setProperty(ctx, QLatin1String("SQRT2"), Value::fromNumber(::sqrt(2.0))); - - setProperty(ctx, QLatin1String("abs"), method_abs, 1); - setProperty(ctx, QLatin1String("acos"), method_acos, 1); - setProperty(ctx, QLatin1String("asin"), method_asin, 0); - setProperty(ctx, QLatin1String("atan"), method_atan, 1); - setProperty(ctx, QLatin1String("atan2"), method_atan2, 2); - setProperty(ctx, QLatin1String("ceil"), method_ceil, 1); - setProperty(ctx, QLatin1String("cos"), method_cos, 1); - setProperty(ctx, QLatin1String("exp"), method_exp, 1); - setProperty(ctx, QLatin1String("floor"), method_floor, 1); - setProperty(ctx, QLatin1String("log"), method_log, 1); - setProperty(ctx, QLatin1String("max"), method_max, 2); - setProperty(ctx, QLatin1String("min"), method_min, 2); - setProperty(ctx, QLatin1String("pow"), method_pow, 2); - setProperty(ctx, QLatin1String("random"), method_random, 0); - setProperty(ctx, QLatin1String("round"), method_round, 1); - setProperty(ctx, QLatin1String("sin"), method_sin, 1); - setProperty(ctx, QLatin1String("sqrt"), method_sqrt, 1); - setProperty(ctx, QLatin1String("tan"), method_tan, 1); + setProperty(ctx, QStringLiteral("E"), Value::fromNumber(::exp(1.0))); + setProperty(ctx, QStringLiteral("LN2"), Value::fromNumber(::log(2.0))); + setProperty(ctx, QStringLiteral("LN10"), Value::fromNumber(::log(10.0))); + setProperty(ctx, QStringLiteral("LOG2E"), Value::fromNumber(1.0/::log(2.0))); + setProperty(ctx, QStringLiteral("LOG10E"), Value::fromNumber(1.0/::log(10.0))); + setProperty(ctx, QStringLiteral("PI"), Value::fromNumber(qt_PI)); + setProperty(ctx, QStringLiteral("SQRT1_2"), Value::fromNumber(::sqrt(0.5))); + setProperty(ctx, QStringLiteral("SQRT2"), Value::fromNumber(::sqrt(2.0))); + + setProperty(ctx, QStringLiteral("abs"), method_abs, 1); + setProperty(ctx, QStringLiteral("acos"), method_acos, 1); + setProperty(ctx, QStringLiteral("asin"), method_asin, 0); + setProperty(ctx, QStringLiteral("atan"), method_atan, 1); + setProperty(ctx, QStringLiteral("atan2"), method_atan2, 2); + setProperty(ctx, QStringLiteral("ceil"), method_ceil, 1); + setProperty(ctx, QStringLiteral("cos"), method_cos, 1); + setProperty(ctx, QStringLiteral("exp"), method_exp, 1); + setProperty(ctx, QStringLiteral("floor"), method_floor, 1); + setProperty(ctx, QStringLiteral("log"), method_log, 1); + setProperty(ctx, QStringLiteral("max"), method_max, 2); + setProperty(ctx, QStringLiteral("min"), method_min, 2); + setProperty(ctx, QStringLiteral("pow"), method_pow, 2); + setProperty(ctx, QStringLiteral("random"), method_random, 0); + setProperty(ctx, QStringLiteral("round"), method_round, 1); + setProperty(ctx, QStringLiteral("sin"), method_sin, 1); + setProperty(ctx, QStringLiteral("sqrt"), method_sqrt, 1); + setProperty(ctx, QStringLiteral("tan"), method_tan, 1); } /* copies the sign from y to x and returns the result */ diff --git a/qv4ir.cpp b/qv4ir.cpp index e42f055693..d106721a2f 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -211,15 +211,15 @@ QString String::escape(const QString &s) for (int i = 0; i < s.length(); ++i) { const QChar ch = s.at(i); if (ch == QLatin1Char('\n')) - r += QLatin1String("\\n"); + r += QStringLiteral("\\n"); else if (ch == QLatin1Char('\r')) - r += QLatin1String("\\r"); + r += QStringLiteral("\\r"); else if (ch == QLatin1Char('\\')) - r += QLatin1String("\\\\"); + r += QStringLiteral("\\\\"); else if (ch == QLatin1Char('"')) - r += QLatin1String("\\\""); + r += QStringLiteral("\\\""); else if (ch == QLatin1Char('\'')) - r += QLatin1String("\\'"); + r += QStringLiteral("\\'"); else r += ch; } diff --git a/qv4ir_p.h b/qv4ir_p.h index 7bac2faf58..9669cc95b1 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -611,8 +611,8 @@ struct Function { : module(module) , pool(&module->pool) , tempCount(0) - , handlersBlock(0) , maxNumberOfArguments(0) + , handlersBlock(0) , code(0) , hasDirectEval(false) , hasNestedFunctions(false) diff --git a/qv4isel.cpp b/qv4isel.cpp index 061250239f..3f1ecb6c57 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -506,7 +506,7 @@ void InstructionSelection::visitMove(IR::Move *s) if (IR::Name *n = s->source->asName()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); - if (*n->id == QLatin1String("this")) { // ### `this' should be a builtin. + if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. amd64_call_code(_codePtr, __qmljs_get_thisObject); } else { String *propertyName = identifier(*n->id); -- cgit v1.2.3 From 7e7df11464e7fce2064f0d216787fba0b21a23f5 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 25 May 2012 17:45:15 +0200 Subject: Fix the initialization of the ECMA library. --- main.cpp | 5 +- qmljs_objects.cpp | 159 +++++++++++++++++++---------------------------------- qmljs_objects.h | 33 +++++------ qmljs_runtime.cpp | 4 +- qv4ecmaobjects.cpp | 120 +++++++++++----------------------------- qv4ecmaobjects_p.h | 46 +++++----------- tests/simple.2.js | 18 ++++++ 7 files changed, 143 insertions(+), 242 deletions(-) diff --git a/main.cpp b/main.cpp index 0ea7040a04..216cc81e31 100644 --- a/main.cpp +++ b/main.cpp @@ -105,7 +105,10 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS globalCode->code(ctx); if (ctx->hasUncaughtException) { - std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; + if (VM::ErrorObject *e = ctx->result.asErrorObject()) + std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; + else + std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; } delete[] ctx->locals; diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 20477e0fc8..0858f90be6 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -5,6 +5,7 @@ #include #include #include +#include using namespace QQmlJS::VM; @@ -27,7 +28,7 @@ void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context setProperty(ctx, name, Value::fromObject(ctx->engine->newNativeFunction(ctx, code))); } -Value *Object::getOwnProperty(Context *ctx, String *name, PropertyAttributes *attributes) +Value *Object::getOwnProperty(Context *, String *name, PropertyAttributes *attributes) { if (members) { if (Property *prop = members->find(name)) { @@ -48,7 +49,7 @@ Value *Object::getPropertyDescriptor(Context *ctx, String *name, PropertyAttribu return 0; } -void Object::setProperty(Context *ctx, String *name, const Value &value, bool flag) +void Object::setProperty(Context *, String *name, const Value &value, bool flag) { Q_UNUSED(flag); @@ -73,7 +74,7 @@ bool Object::canSetProperty(Context *ctx, String *name) return true; } -bool Object::hasProperty(Context *ctx, String *name) const +bool Object::hasProperty(Context *, String *name) const { if (members) return members->find(name) != 0; @@ -81,7 +82,7 @@ bool Object::hasProperty(Context *ctx, String *name) const return false; } -bool Object::deleteProperty(Context *ctx, String *name, bool flag) +bool Object::deleteProperty(Context *, String *name, bool flag) { Q_UNUSED(flag); @@ -192,17 +193,47 @@ ExecutionEngine::ExecutionEngine() rootContext = newContext(); rootContext->init(this); - objectPrototype.type = NULL_TYPE; - stringPrototype.type = NULL_TYPE; - numberPrototype.type = NULL_TYPE; - booleanPrototype.type = NULL_TYPE; - arrayPrototype.type = NULL_TYPE; - datePrototype.type = NULL_TYPE; - functionPrototype.type = NULL_TYPE; - id_length = identifier(QStringLiteral("length")); id_prototype = identifier(QStringLiteral("prototype")); + objectPrototype = new ObjectPrototype(); + stringPrototype = new StringPrototype(rootContext); + numberPrototype = new NumberPrototype(); + booleanPrototype = new BooleanPrototype(); + arrayPrototype = new ArrayPrototype(); + datePrototype = new DatePrototype(); + functionPrototype = new FunctionPrototype(rootContext); + + stringPrototype->prototype = objectPrototype; + numberPrototype->prototype = objectPrototype; + booleanPrototype->prototype = objectPrototype; + arrayPrototype->prototype = objectPrototype; + datePrototype->prototype = objectPrototype; + functionPrototype->prototype = objectPrototype; + + Value objectCtor = Value::fromObject(new ObjectCtor(rootContext)); + Value stringCtor = Value::fromObject(new StringCtor(rootContext)); + Value numberCtor = Value::fromObject(new NumberCtor(rootContext)); + Value booleanCtor = Value::fromObject(new BooleanCtor(rootContext)); + Value arrayCtor = Value::fromObject(new ArrayCtor(rootContext)); + Value functionCtor = Value::fromObject(new FunctionCtor(rootContext)); + Value dateCtor = Value::fromObject(new DateCtor(rootContext)); + + stringCtor.objectValue->prototype = functionPrototype; + numberCtor.objectValue->prototype = functionPrototype; + booleanCtor.objectValue->prototype = functionPrototype; + arrayCtor.objectValue->prototype = functionPrototype; + functionCtor.objectValue->prototype = functionPrototype; + dateCtor.objectValue->prototype = functionPrototype; + + objectPrototype->init(rootContext, objectCtor); + stringPrototype->init(rootContext, stringCtor); + numberPrototype->init(rootContext, numberCtor); + booleanPrototype->init(rootContext, booleanCtor); + arrayPrototype->init(rootContext, arrayCtor); + datePrototype->init(rootContext, dateCtor); + functionPrototype->init(rootContext, functionCtor); + // // set up the global object // @@ -210,27 +241,10 @@ ExecutionEngine::ExecutionEngine() __qmljs_init_object(&globalObject, glo); __qmljs_init_object(&rootContext->activation, glo); - objectCtor = ObjectCtor::create(this); - objectPrototype = objectCtor.property(rootContext, id_prototype); - - functionCtor = FunctionCtor::create(this); - functionPrototype = functionCtor.property(rootContext, id_prototype); - - stringCtor = StringCtor::create(this); - numberCtor = NumberCtor::create(this); - booleanCtor = BooleanCtor::create(this); - arrayCtor = ArrayCtor::create(this); - dateCtor = DateCtor::create(this); - - stringPrototype = stringCtor.property(rootContext, id_prototype); - numberPrototype = numberCtor.property(rootContext, id_prototype); - booleanPrototype = booleanCtor.property(rootContext, id_prototype); - arrayPrototype = arrayCtor.property(rootContext, id_prototype); - datePrototype = dateCtor.property(rootContext, id_prototype); - glo->setProperty(rootContext, identifier(QStringLiteral("Object")), objectCtor); glo->setProperty(rootContext, identifier(QStringLiteral("String")), stringCtor); glo->setProperty(rootContext, identifier(QStringLiteral("Number")), numberCtor); + glo->setProperty(rootContext, identifier(QStringLiteral("Boolean")), booleanCtor); glo->setProperty(rootContext, identifier(QStringLiteral("Array")), arrayCtor); glo->setProperty(rootContext, identifier(QStringLiteral("Function")), functionCtor); glo->setProperty(rootContext, identifier(QStringLiteral("Date")), dateCtor); @@ -253,24 +267,21 @@ String *ExecutionEngine::identifier(const QString &s) FunctionObject *ExecutionEngine::newNativeFunction(Context *scope, void (*code)(Context *)) { NativeFunction *f = new NativeFunction(scope, code); - if (scope->engine->functionPrototype.isObject()) - f->prototype = scope->engine->functionPrototype.objectValue; + f->prototype = scope->engine->functionPrototype; return f; } FunctionObject *ExecutionEngine::newScriptFunction(Context *scope, IR::Function *function) { ScriptFunction *f = new ScriptFunction(scope, function); - if (scope->engine->functionPrototype.isObject()) - f->prototype = scope->engine->functionPrototype.objectValue; + f->prototype = scope->engine->functionPrototype; return f; } Object *ExecutionEngine::newObject() { Object *object = new Object(); - if (objectPrototype.isObject()) - object->prototype = objectPrototype.objectValue; + object->prototype = objectPrototype; return object; } @@ -279,11 +290,6 @@ FunctionObject *ExecutionEngine::newObjectCtor(Context *ctx) return new ObjectCtor(ctx); } -Object *ExecutionEngine::newObjectPrototype(Context *ctx, FunctionObject *proto) -{ - return new ObjectPrototype(ctx, proto); -} - String *ExecutionEngine::newString(const QString &s) { return new String(s); @@ -292,8 +298,7 @@ String *ExecutionEngine::newString(const QString &s) Object *ExecutionEngine::newStringObject(const Value &value) { StringObject *object = new StringObject(value); - if (stringPrototype.isObject()) - object->prototype = stringPrototype.objectValue; + object->prototype = stringPrototype; return object; } @@ -302,18 +307,10 @@ FunctionObject *ExecutionEngine::newStringCtor(Context *ctx) return new StringCtor(ctx); } -Object *ExecutionEngine::newStringPrototype(Context *ctx, FunctionObject *proto) -{ - Object *stringProto = new StringPrototype(ctx, proto); - stringProto->prototype = objectPrototype.objectValue; - return stringProto; -} - Object *ExecutionEngine::newNumberObject(const Value &value) { NumberObject *object = new NumberObject(value); - if (numberPrototype.isObject()) - object->prototype = numberPrototype.objectValue; + object->prototype = numberPrototype; return object; } @@ -322,18 +319,10 @@ FunctionObject *ExecutionEngine::newNumberCtor(Context *ctx) return new NumberCtor(ctx); } -Object *ExecutionEngine::newNumberPrototype(Context *ctx, FunctionObject *proto) -{ - Object *numberProto = new NumberPrototype(ctx, proto); - numberProto->prototype = objectPrototype.objectValue; - return numberProto; -} - Object *ExecutionEngine::newBooleanObject(const Value &value) { Object *object = new BooleanObject(value); - if (booleanPrototype.isObject()) - object->prototype = booleanPrototype.objectValue; + object->prototype = booleanPrototype; return object; } @@ -342,18 +331,10 @@ FunctionObject *ExecutionEngine::newBooleanCtor(Context *ctx) return new BooleanCtor(ctx); } -Object *ExecutionEngine::newBooleanPrototype(Context *ctx, FunctionObject *proto) -{ - Object *booleanProto = new BooleanPrototype(ctx, proto); - booleanProto->prototype = objectPrototype.objectValue; - return booleanProto; -} - Object *ExecutionEngine::newFunctionObject(Context *ctx) { Object *object = new FunctionObject(ctx); - if (functionPrototype.isObject()) - object->prototype = functionPrototype.objectValue; + object->prototype = functionPrototype; return object; } @@ -362,26 +343,17 @@ FunctionObject *ExecutionEngine::newFunctionCtor(Context *ctx) return new FunctionCtor(ctx); } -Object *ExecutionEngine::newFunctionPrototype(Context *ctx, FunctionObject *proto) -{ - Object *functionProto = new FunctionPrototype(ctx, proto); - functionProto->prototype = objectPrototype.objectValue; - return functionProto; -} - Object *ExecutionEngine::newArrayObject() { ArrayObject *object = new ArrayObject(); - if (arrayPrototype.isObject()) - object->prototype = arrayPrototype.objectValue; + object->prototype = arrayPrototype; return object; } Object *ExecutionEngine::newArrayObject(const Array &value) { ArrayObject *object = new ArrayObject(value); - if (arrayPrototype.isObject()) - object->prototype = arrayPrototype.objectValue; + object->prototype = arrayPrototype; return object; } @@ -390,19 +362,10 @@ FunctionObject *ExecutionEngine::newArrayCtor(Context *ctx) return new ArrayCtor(ctx); } -Object *ExecutionEngine::newArrayPrototype(Context *ctx, FunctionObject *proto) -{ - Object *arrayProto = new ArrayPrototype(ctx, proto); - assert(objectPrototype.isObject()); - arrayProto->prototype = objectPrototype.objectValue; - return arrayProto; -} - Object *ExecutionEngine::newDateObject(const Value &value) { Object *object = new DateObject(value); - if (datePrototype.isObject()) - object->prototype = datePrototype.objectValue; + object->prototype = datePrototype; return object; } @@ -411,27 +374,17 @@ FunctionObject *ExecutionEngine::newDateCtor(Context *ctx) return new DateCtor(ctx); } -Object *ExecutionEngine::newDatePrototype(Context *ctx, FunctionObject *proto) -{ - Object *dateProto = new DatePrototype(ctx, proto); - assert(objectPrototype.isObject()); - dateProto->prototype = objectPrototype.objectValue; - return dateProto; -} - Object *ExecutionEngine::newErrorObject(const Value &value) { ErrorObject *object = new ErrorObject(value); - assert(objectPrototype.isObject()); - object->prototype = objectPrototype.objectValue; + object->prototype = objectPrototype; return object; } Object *ExecutionEngine::newMathObject(Context *ctx) { MathObject *object = new MathObject(ctx); - assert(objectPrototype.isObject()); - object->prototype = objectPrototype.objectValue; + object->prototype = objectPrototype; return object; } diff --git a/qmljs_objects.h b/qmljs_objects.h index 4683b4e691..0d3e9922ac 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -29,6 +29,14 @@ struct ArgumentsObject; struct Context; struct ExecutionEngine; +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; + struct String { String(const QString &text) : _text(text), _hashValue(0) {} @@ -297,8 +305,8 @@ struct ScriptFunction: FunctionObject { }; struct ErrorObject: Object { - Value message; - ErrorObject(const Value &message): message(message) {} + Value value; + ErrorObject(const Value &message): value(message) {} virtual ErrorObject *asErrorObject() { return this; } }; @@ -396,13 +404,13 @@ struct ExecutionEngine Value functionCtor; Value dateCtor; - Value objectPrototype; - Value stringPrototype; - Value numberPrototype; - Value booleanPrototype; - Value arrayPrototype; - Value functionPrototype; - Value datePrototype; + ObjectPrototype *objectPrototype; + StringPrototype *stringPrototype; + NumberPrototype *numberPrototype; + BooleanPrototype *booleanPrototype; + ArrayPrototype *arrayPrototype; + FunctionPrototype *functionPrototype; + DatePrototype *datePrototype; QHash identifiers; @@ -420,33 +428,26 @@ struct ExecutionEngine Object *newObject(); FunctionObject *newObjectCtor(Context *ctx); - Object *newObjectPrototype(Context *ctx, FunctionObject *proto); String *newString(const QString &s); Object *newStringObject(const Value &value); FunctionObject *newStringCtor(Context *ctx); - Object *newStringPrototype(Context *ctx, FunctionObject *proto); Object *newNumberObject(const Value &value); FunctionObject *newNumberCtor(Context *ctx); - Object *newNumberPrototype(Context *ctx, FunctionObject *proto); Object *newBooleanObject(const Value &value); FunctionObject *newBooleanCtor(Context *ctx); - Object *newBooleanPrototype(Context *ctx, FunctionObject *proto); Object *newFunctionObject(Context *ctx); FunctionObject *newFunctionCtor(Context *ctx); - Object *newFunctionPrototype(Context *ctx, FunctionObject *proto); Object *newArrayObject(); Object *newArrayObject(const Array &value); FunctionObject *newArrayCtor(Context *ctx); - Object *newArrayPrototype(Context *ctx, FunctionObject *proto); Object *newDateObject(const Value &value); FunctionObject *newDateCtor(Context *ctx); - Object *newDatePrototype(Context *ctx, FunctionObject *proto); Object *newErrorObject(const Value &value); Object *newMathObject(Context *ctx); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 060db22c79..6fa33183ff 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -363,6 +363,7 @@ void __qmljs_object_default_value(Context *ctx, Value *result, const Value *obje Object *oo = object->asObject(); assert(oo != 0); + Value conv = oo->getProperty(ctx, meth1); if (!conv.isUndefined() && conv.isFunctionObject()) { Value r; @@ -398,7 +399,6 @@ void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean) Value value; __qmljs_init_boolean(&value, boolean); __qmljs_init_object(result, ctx->engine->newBooleanObject(value)); - result->objectValue->prototype = ctx->engine->objectPrototype.objectValue; } void __qmljs_new_number_object(Context *ctx, Value *result, double number) @@ -406,7 +406,6 @@ void __qmljs_new_number_object(Context *ctx, Value *result, double number) Value value; __qmljs_init_number(&value, number); __qmljs_init_object(result, ctx->engine->newNumberObject(value)); - result->objectValue->prototype = ctx->engine->numberPrototype.objectValue; } void __qmljs_new_string_object(Context *ctx, Value *result, String *string) @@ -414,7 +413,6 @@ void __qmljs_new_string_object(Context *ctx, Value *result, String *string) Value value; __qmljs_init_string(&value, string); __qmljs_init_object(result, ctx->engine->newStringObject(value)); - result->objectValue->prototype = ctx->engine->stringPrototype.objectValue; } void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 119e163da3..30d94d3ed3 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -473,14 +473,6 @@ static double getLocalTZA() // // Object // -Value ObjectCtor::create(ExecutionEngine *engine) -{ - Context *ctx = engine->rootContext; - FunctionObject *ctor = ctx->engine->newObjectCtor(ctx); - ctor->setProperty(ctx, QStringLiteral("prototype"), Value::fromObject(ctx->engine->newObjectPrototype(ctx, ctor))); - return Value::fromObject(ctor); -} - ObjectCtor::ObjectCtor(Context *scope) : FunctionObject(scope) { @@ -491,14 +483,15 @@ void ObjectCtor::construct(Context *ctx) __qmljs_init_object(&ctx->thisObject, ctx->engine->newObject()); } -void ObjectCtor::call(Context *) +void ObjectCtor::call(Context *ctx) { - assert(!"not here"); + __qmljs_init_object(&ctx->result, ctx->engine->newObject()); } -ObjectPrototype::ObjectPrototype(Context *ctx, FunctionObject *ctor) +void ObjectPrototype::init(Context *ctx, const Value &ctor) { - setProperty(ctx, QStringLiteral("constructor"), Value::fromObject(ctor)); + ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("toString"), method_toString, 0); } @@ -510,14 +503,6 @@ void ObjectPrototype::method_toString(Context *ctx) // // String // -Value StringCtor::create(ExecutionEngine *engine) -{ - Context *ctx = engine->rootContext; - FunctionObject *ctor = ctx->engine->newStringCtor(ctx); - ctor->setProperty(ctx, QStringLiteral("prototype"), Value::fromObject(ctx->engine->newStringPrototype(ctx, ctor))); - return Value::fromObject(ctor); -} - StringCtor::StringCtor(Context *scope) : FunctionObject(scope) { @@ -542,9 +527,10 @@ void StringCtor::call(Context *ctx) __qmljs_to_string(ctx, &ctx->result, &arg); } -StringPrototype::StringPrototype(Context *ctx, FunctionObject *ctor) +void StringPrototype::init(Context *ctx, const Value &ctor) { - setProperty(ctx, QStringLiteral("constructor"), Value::fromObject(ctor)); + ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("toString"), method_toString); setProperty(ctx, QStringLiteral("valueOf"), method_valueOf); setProperty(ctx, QStringLiteral("charAt"), method_charAt); @@ -832,14 +818,6 @@ void StringPrototype::method_fromCharCode(Context *ctx) // // Number object // -Value NumberCtor::create(ExecutionEngine *engine) -{ - Context *ctx = engine->rootContext; - FunctionObject *ctor = ctx->engine->newNumberCtor(ctx); - ctor->setProperty(ctx, QStringLiteral("prototype"), Value::fromObject(ctx->engine->newNumberPrototype(ctx, ctor))); - return Value::fromObject(ctor); -} - NumberCtor::NumberCtor(Context *scope) : FunctionObject(scope) { @@ -857,23 +835,23 @@ void NumberCtor::call(Context *ctx) __qmljs_init_number(&ctx->result, value); } -NumberPrototype::NumberPrototype(Context *ctx, FunctionObject *ctor) - : NumberObject(Value::fromNumber(0)) +void NumberPrototype::init(Context *ctx, const Value &ctor) { - ctor->setProperty(ctx, QStringLiteral("NaN"), Value::fromNumber(qSNaN())); - ctor->setProperty(ctx, QStringLiteral("NEGATIVE_INFINITY"), Value::fromNumber(-qInf())); - ctor->setProperty(ctx, QStringLiteral("POSITIVE_INFINITY"), Value::fromNumber(qInf())); - ctor->setProperty(ctx, QStringLiteral("MAX_VALUE"), Value::fromNumber(1.7976931348623158e+308)); + ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue->setProperty(ctx, QStringLiteral("NaN"), Value::fromNumber(qSNaN())); + ctor.objectValue->setProperty(ctx, QStringLiteral("NEGATIVE_INFINITY"), Value::fromNumber(-qInf())); + ctor.objectValue->setProperty(ctx, QStringLiteral("POSITIVE_INFINITY"), Value::fromNumber(qInf())); + ctor.objectValue->setProperty(ctx, QStringLiteral("MAX_VALUE"), Value::fromNumber(1.7976931348623158e+308)); #ifdef __INTEL_COMPILER # pragma warning( push ) # pragma warning(disable: 239) #endif - ctor->setProperty(ctx, QStringLiteral("MIN_VALUE"), Value::fromNumber(5e-324)); + ctor.objectValue->setProperty(ctx, QStringLiteral("MIN_VALUE"), Value::fromNumber(5e-324)); #ifdef __INTEL_COMPILER # pragma warning( pop ) #endif - setProperty(ctx, QStringLiteral("constructor"), Value::fromObject(ctor)); + setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("toString"), method_toString); setProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString); setProperty(ctx, QStringLiteral("valueOf"), method_valueOf); @@ -1019,14 +997,6 @@ void NumberPrototype::method_toPrecision(Context *ctx) // // Boolean object // -Value BooleanCtor::create(ExecutionEngine *engine) -{ - Context *ctx = engine->rootContext; - FunctionObject *ctor = ctx->engine->newBooleanCtor(ctx); - ctor->setProperty(ctx, QStringLiteral("prototype"), Value::fromObject(ctx->engine->newBooleanPrototype(ctx, ctor))); - return Value::fromObject(ctor); -} - BooleanCtor::BooleanCtor(Context *scope) : FunctionObject(scope) { @@ -1044,12 +1014,12 @@ void BooleanCtor::call(Context *ctx) __qmljs_init_boolean(&ctx->result, value); } -BooleanPrototype::BooleanPrototype(Context *ctx, FunctionObject *ctor) - : BooleanObject(Value::fromBoolean(false)) +void BooleanPrototype::init(Context *ctx, const Value &ctor) { - ctor->setProperty(ctx, QStringLiteral("constructor"), Value::fromObject(ctor)); - ctor->setProperty(ctx, QStringLiteral("toString"), method_toString); - ctor->setProperty(ctx, QStringLiteral("valueOf"), method_valueOf); + ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + setProperty(ctx, QStringLiteral("constructor"), ctor); + setProperty(ctx, QStringLiteral("toString"), method_toString); + setProperty(ctx, QStringLiteral("valueOf"), method_valueOf); } void BooleanPrototype::method_toString(Context *ctx) @@ -1073,14 +1043,6 @@ void BooleanPrototype::method_valueOf(Context *ctx) // // Array object // -Value ArrayCtor::create(ExecutionEngine *engine) -{ - Context *ctx = engine->rootContext; - FunctionObject *ctor = ctx->engine->newArrayCtor(ctx); - ctor->setProperty(ctx, QStringLiteral("prototype"), Value::fromObject(ctx->engine->newArrayPrototype(ctx, ctor))); - return Value::fromObject(ctor); -} - ArrayCtor::ArrayCtor(Context *scope) : FunctionObject(scope) { @@ -1114,9 +1076,10 @@ void ArrayCtor::call(Context *ctx) ctx->result = Value::fromObject(ctx->engine->newArrayObject(value)); } -ArrayPrototype::ArrayPrototype(Context *ctx, FunctionObject *ctor) +void ArrayPrototype::init(Context *ctx, const Value &ctor) { - setProperty(ctx, QStringLiteral("constructor"), Value::fromObject(ctor)); + ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("toString"), method_toString, 0); setProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString, 0); setProperty(ctx, QStringLiteral("concat"), method_concat, 1); @@ -1595,17 +1558,6 @@ void ArrayPrototype::method_reduceRight(Context *ctx) // // Function object // -// -// Array object -// -Value FunctionCtor::create(ExecutionEngine *engine) -{ - Context *ctx = engine->rootContext; - FunctionObject *ctor = ctx->engine->newFunctionCtor(ctx); - ctor->setProperty(ctx, QStringLiteral("prototype"), Value::fromObject(ctx->engine->newFunctionPrototype(ctx, ctor))); - return Value::fromObject(ctor); -} - FunctionCtor::FunctionCtor(Context *scope) : FunctionObject(scope) { @@ -1621,10 +1573,10 @@ void FunctionCtor::call(Context *ctx) ctx->throwUnimplemented(QStringLiteral("Function")); } -FunctionPrototype::FunctionPrototype(Context *ctx, FunctionObject *ctor) - : FunctionObject(ctx) +void FunctionPrototype::init(Context *ctx, const Value &ctor) { - setProperty(ctx, QStringLiteral("constructor"), Value::fromObject(ctor)); + ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("toString"), method_toString, 0); setProperty(ctx, QStringLiteral("apply"), method_apply, 0); setProperty(ctx, QStringLiteral("call"), method_call, 0); @@ -1678,14 +1630,6 @@ void FunctionPrototype::method_bind(Context *ctx) // // Date object // -Value DateCtor::create(ExecutionEngine *engine) -{ - Context *ctx = engine->rootContext; - FunctionObject *ctor = ctx->engine->newDateCtor(ctx); - ctor->setProperty(ctx, QStringLiteral("prototype"), Value::fromObject(ctx->engine->newDatePrototype(ctx, ctor))); - return Value::fromObject(ctor); -} - DateCtor::DateCtor(Context *scope) : FunctionObject(scope) { @@ -1735,15 +1679,15 @@ void DateCtor::call(Context *ctx) ctx->result = Value::fromString(ctx, ToString(t)); } -DatePrototype::DatePrototype(Context *ctx, FunctionObject *ctor) - : DateObject(Value::fromNumber(qSNaN())) +void DatePrototype::init(Context *ctx, const Value &ctor) { + ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); LocalTZA = getLocalTZA(); - ctor->setProperty(ctx, QStringLiteral("parse"), method_parse, 1); - ctor->setProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); + ctor.objectValue->setProperty(ctx, QStringLiteral("parse"), method_parse, 1); + ctor.objectValue->setProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); - setProperty(ctx, QStringLiteral("constructor"), Value::fromObject(ctor)); + setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("toString"), method_toString, 0); setProperty(ctx, QStringLiteral("toDateString"), method_toDateString, 0); setProperty(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0); diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index b09ef1c55f..1936a20aa6 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -2,43 +2,39 @@ #define QV4ECMAOBJECTS_P_H #include "qmljs_objects.h" +#include namespace QQmlJS { namespace VM { struct ObjectCtor: FunctionObject { - static Value create(ExecutionEngine *engine); - ObjectCtor(Context *scope); virtual void construct(Context *ctx); - virtual void call(Context *); + virtual void call(Context *ctx); }; struct ObjectPrototype: Object { - ObjectPrototype(Context *ctx, FunctionObject *ctor); + void init(Context *ctx, const Value &ctor); -protected: static void method_toString(Context *ctx); }; struct StringCtor: FunctionObject { - static Value create(ExecutionEngine *engine); - StringCtor(Context *scope); virtual void construct(Context *ctx); virtual void call(Context *ctx); }; -struct StringPrototype: Object +struct StringPrototype: StringObject { - StringPrototype(Context *ctx, FunctionObject *ctor); + StringPrototype(Context *ctx): StringObject(Value::fromString(ctx, QString())) {} + void init(Context *ctx, const Value &ctor); -protected: static QString getThisString(Context *ctx); static void method_toString(Context *ctx); @@ -65,8 +61,6 @@ protected: struct NumberCtor: FunctionObject { - static Value create(ExecutionEngine *engine); - NumberCtor(Context *scope); virtual void construct(Context *ctx); @@ -75,9 +69,9 @@ struct NumberCtor: FunctionObject struct NumberPrototype: NumberObject { - NumberPrototype(Context *ctx, FunctionObject *ctor); + NumberPrototype(): NumberObject(Value::fromNumber(0)) {} + void init(Context *ctx, const Value &ctor); -protected: static void method_toString(Context *ctx); static void method_toLocaleString(Context *ctx); static void method_valueOf(Context *ctx); @@ -88,8 +82,6 @@ protected: struct BooleanCtor: FunctionObject { - static Value create(ExecutionEngine *engine); - BooleanCtor(Context *scope); virtual void construct(Context *ctx); @@ -98,17 +90,15 @@ struct BooleanCtor: FunctionObject struct BooleanPrototype: BooleanObject { - BooleanPrototype(Context *ctx, FunctionObject *ctor); + BooleanPrototype(): BooleanObject(Value::fromBoolean(false)) {} + void init(Context *ctx, const Value &ctor); -protected: static void method_toString(Context *ctx); static void method_valueOf(Context *ctx); }; struct ArrayCtor: FunctionObject { - static Value create(ExecutionEngine *engine); - ArrayCtor(Context *scope); virtual void construct(Context *ctx); @@ -117,9 +107,8 @@ struct ArrayCtor: FunctionObject struct ArrayPrototype: ArrayObject { - ArrayPrototype(Context *ctx, FunctionObject *ctor); + void init(Context *ctx, const Value &ctor); -protected: static void method_toString(Context *ctx); static void method_toLocaleString(Context *ctx); static void method_concat(Context *ctx); @@ -145,8 +134,6 @@ protected: struct FunctionCtor: FunctionObject { - static Value create(ExecutionEngine *engine); - FunctionCtor(Context *scope); virtual void construct(Context *ctx); @@ -155,9 +142,9 @@ struct FunctionCtor: FunctionObject struct FunctionPrototype: FunctionObject { - FunctionPrototype(Context *ctx, FunctionObject *ctor); + FunctionPrototype(Context *ctx): FunctionObject(ctx) {} + void init(Context *ctx, const Value &ctor); -protected: static void method_toString(Context *ctx); static void method_apply(Context *ctx); static void method_call(Context *ctx); @@ -166,8 +153,6 @@ protected: struct DateCtor: FunctionObject { - static Value create(ExecutionEngine *engine); - DateCtor(Context *scope); virtual void construct(Context *ctx); @@ -176,9 +161,9 @@ struct DateCtor: FunctionObject struct DatePrototype: DateObject { - DatePrototype(Context *ctx, FunctionObject *ctor); + DatePrototype(): DateObject(Value::fromNumber(qSNaN())) {} + void init(Context *ctx, const Value &ctor); -protected: static double getThisDate(Context *ctx); static void method_MakeTime(Context *ctx); @@ -235,7 +220,6 @@ struct MathObject: Object { MathObject(Context *ctx); -protected: static void method_abs(Context *ctx); static void method_acos(Context *ctx); static void method_asin(Context *ctx); diff --git a/tests/simple.2.js b/tests/simple.2.js index 4a9f966da1..44f231c64c 100644 --- a/tests/simple.2.js +++ b/tests/simple.2.js @@ -17,3 +17,21 @@ function main() } main() + + +print("Object.prototype", Object.prototype) +print("Boolean.prototype", Boolean.prototype) +print("Number.prototype", Number.prototype) +print("String.prototype", String.prototype) +print("Date.prototype", Date.prototype) +print("Array.prototype", Array.prototype) +print("Function.prototype", Function.prototype) + + +print("typeof Object.prototype", typeof(Object.prototype)) +print("typeof Boolean.prototype", typeof(Boolean.prototype)) +print("typeof Number.prototype", typeof(Number.prototype)) +print("typeof String.prototype", typeof(String.prototype)) +print("typeof Date.prototype", typeof(Date.prototype)) +print("typeof Array.prototype", typeof(Array.prototype)) +print("typeof Function.prototype", typeof(Function.prototype)) -- cgit v1.2.3 From 6421cb208392e756753c3c8874e5dcb42a8e6476 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 25 May 2012 17:46:29 +0200 Subject: Oops --- tests/simple.2.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/tests/simple.2.js b/tests/simple.2.js index 44f231c64c..4a9f966da1 100644 --- a/tests/simple.2.js +++ b/tests/simple.2.js @@ -17,21 +17,3 @@ function main() } main() - - -print("Object.prototype", Object.prototype) -print("Boolean.prototype", Boolean.prototype) -print("Number.prototype", Number.prototype) -print("String.prototype", String.prototype) -print("Date.prototype", Date.prototype) -print("Array.prototype", Array.prototype) -print("Function.prototype", Function.prototype) - - -print("typeof Object.prototype", typeof(Object.prototype)) -print("typeof Boolean.prototype", typeof(Boolean.prototype)) -print("typeof Number.prototype", typeof(Number.prototype)) -print("typeof String.prototype", typeof(String.prototype)) -print("typeof Date.prototype", typeof(Date.prototype)) -print("typeof Array.prototype", typeof(Array.prototype)) -print("typeof Function.prototype", typeof(Function.prototype)) -- cgit v1.2.3 From 81dda7e324497ab607e60f4ed022db9983e33352 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 28 May 2012 17:04:16 +0200 Subject: Naive support for switch statements --- qv4codegen.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index a4c5ec92e5..2cf4a56f3b 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1721,9 +1721,80 @@ bool Codegen::visit(ReturnStatement *ast) return false; } -bool Codegen::visit(SwitchStatement *) +bool Codegen::visit(SwitchStatement *ast) { - Q_ASSERT(!"not implemented"); + IR::BasicBlock *switchend = _function->newBasicBlock(); + + if (ast->block) { + Result lhs = expression(ast->expression); + IR::BasicBlock *switchcond = _block; + + QHash blockMap; + + Loop loop(switchend, 0); + qSwap(_loop, loop); + + for (CaseClauses *it = ast->block->clauses; it; it = it->next) { + CaseClause *clause = it->clause; + + _block = _function->newBasicBlock(); + blockMap[clause] = _block; + + for (StatementList *it2 = clause->statements; it2; it2 = it2->next) + statement(it2->statement); + } + + if (ast->block->defaultClause) { + _block = _function->newBasicBlock(); + blockMap[ast->block->defaultClause] = _block; + + for (StatementList *it2 = ast->block->defaultClause->statements; it2; it2 = it2->next) + statement(it2->statement); + } + + for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { + CaseClause *clause = it->clause; + + _block = _function->newBasicBlock(); + blockMap[clause] = _block; + + for (StatementList *it2 = clause->statements; it2; it2 = it2->next) + statement(it2->statement); + } + + qSwap(_loop, loop); + + if (! _block->isTerminated()) + _block->JUMP(switchend); + + _block = switchcond; + for (CaseClauses *it = ast->block->clauses; it; it = it->next) { + CaseClause *clause = it->clause; + Result rhs = expression(clause->expression); + IR::BasicBlock *iftrue = blockMap[clause]; + IR::BasicBlock *iffalse = _function->newBasicBlock(); + cjump(binop(IR::OpEqual, *lhs, *rhs), iftrue, iffalse); + _block = iffalse; + } + + for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { + CaseClause *clause = it->clause; + Result rhs = expression(clause->expression); + IR::BasicBlock *iftrue = blockMap[clause]; + IR::BasicBlock *iffalse = _function->newBasicBlock(); + cjump(binop(IR::OpEqual, *lhs, *rhs), iftrue, iffalse); + _block = iffalse; + } + + if (ast->block->defaultClause) { + _block->JUMP(blockMap[ast->block->defaultClause]); + } + } + + if (! _block->isTerminated()) + _block->JUMP(switchend); + + _block = switchend; return false; } -- cgit v1.2.3 From 5b0f452f4d5d4b63df6c914131ecef4352af768d Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 28 May 2012 20:17:13 +0200 Subject: Implement instanceof --- qmljs_objects.cpp | 37 +++++++++++++++++++++++++++++++++---- qmljs_objects.h | 11 +++-------- qmljs_runtime.cpp | 10 ++++------ qv4ir.cpp | 12 ++++++++++++ qv4ir_p.h | 3 +++ qv4isel.cpp | 8 +++++++- 6 files changed, 62 insertions(+), 19 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 0858f90be6..5737e48ace 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -28,6 +28,15 @@ void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context setProperty(ctx, name, Value::fromObject(ctx->engine->newNativeFunction(ctx, code))); } +Value Object::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) +{ + if (name->isEqualTo(ctx->engine->id___proto__)) + return Value::fromObject(prototype); + else if (Value *v = getPropertyDescriptor(ctx, name, attributes)) + return *v; + return Value::undefinedValue(); +} + Value *Object::getOwnProperty(Context *, String *name, PropertyAttributes *attributes) { if (members) { @@ -107,9 +116,29 @@ Value ArrayObject::getProperty(Context *ctx, String *name, PropertyAttributes *a return Object::getProperty(ctx, name, attributes); } -bool FunctionObject::hasInstance(const Value &value) const +bool FunctionObject::hasInstance(Context *ctx, const Value &value) { - Q_UNUSED(value); + if (! value.isObject()) { + ctx->throwTypeError(); + return false; + } + + Value o = getProperty(ctx, ctx->engine->id_prototype); + if (! o.isObject()) { + ctx->throwTypeError(); + return false; + } + + Object *v = value.objectValue; + while (v) { + v = v->prototype; + + if (! v) + break; + else if (o.objectValue == v) + return true; + } + return false; } @@ -195,6 +224,7 @@ ExecutionEngine::ExecutionEngine() id_length = identifier(QStringLiteral("length")); id_prototype = identifier(QStringLiteral("prototype")); + id___proto__ = identifier(QStringLiteral("__proto__")); objectPrototype = new ObjectPrototype(); stringPrototype = new StringPrototype(rootContext); @@ -478,8 +508,7 @@ void Context::leaveConstructorContext(FunctionObject *f, Value *returnValue) result = thisObject; Value proto = f->getProperty(this, engine->id_prototype); - if (proto.isObject()) - thisObject.objectValue->prototype = proto.objectValue; + thisObject.objectValue->prototype = proto.objectValue; leaveCallContext(f, returnValue); } diff --git a/qmljs_objects.h b/qmljs_objects.h index 0d3e9922ac..3189828cf2 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -210,13 +210,7 @@ struct Object { virtual ErrorObject *asErrorObject() { return 0; } virtual ArgumentsObject *asArgumentsObject() { return 0; } - virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes = 0) - { - if (Value *v = getPropertyDescriptor(ctx, name, attributes)) - return *v; - return Value::undefinedValue(); - } - + virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes = 0); virtual Value *getOwnProperty(Context *ctx, String *name, PropertyAttributes *attributes = 0); virtual Value *getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes = 0); virtual void setProperty(Context *ctx, String *name, const Value &value, bool flag = false); @@ -281,7 +275,7 @@ struct FunctionObject: Object { , needsActivation(true) {} virtual FunctionObject *asFunctionObject() { return this; } - virtual bool hasInstance(const Value &value) const; + virtual bool hasInstance(Context *ctx, const Value &value); virtual void call(Context *ctx); virtual void construct(Context *ctx); }; @@ -416,6 +410,7 @@ struct ExecutionEngine String *id_length; String *id_prototype; + String *id___proto__; ExecutionEngine(); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 6fa33183ff..77ba2a9850 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -284,12 +284,10 @@ void __qmljs_delete(Context *ctx, Value *result, const Value *value) void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Value *right) { - if (right->type == OBJECT_TYPE) { - if (FunctionObject *function = right->objectValue->asFunctionObject()) { - bool r = function->hasInstance(*left); - __qmljs_init_boolean(result, r); - return; - } + if (FunctionObject *function = right->asFunctionObject()) { + bool r = function->hasInstance(ctx, *left); + __qmljs_init_boolean(result, r); + return; } __qmljs_throw_type_error(ctx, result); diff --git a/qv4ir.cpp b/qv4ir.cpp index d106721a2f..6718c62848 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -143,6 +143,9 @@ const char *opname(AluOp op) case OpStrictEqual: return "==="; case OpStrictNotEqual: return "!=="; + case OpInstanceof: return "instanceof"; + case OpIn: return "in"; + case OpAnd: return "&&"; case OpOr: return "||"; @@ -175,6 +178,8 @@ AluOp binaryOperator(int op) case QSOperator::StrictNotEqual: return OpStrictNotEqual; case QSOperator::Sub: return OpSub; case QSOperator::URShift: return OpURShift; + case QSOperator::InstanceOf: return OpInstanceof; + case QSOperator::In: return OpIn; default: return OpInvalid; } } @@ -351,6 +356,8 @@ Type Binop::typeForOp(AluOp op, Expr *left, Expr *right) case OpNotEqual: case OpStrictEqual: case OpStrictNotEqual: + case OpInstanceof: + case OpIn: return BoolType; } // switch @@ -589,6 +596,11 @@ Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) case OpSub: return CONST(ty, c1->value - c2->value); case OpURShift: return CONST(ty, unsigned(c1->value) >> int(c2->value)); + case OpInstanceof: + case OpIn: + Q_ASSERT(!"unreachabe"); + break; + case OpIfTrue: // unary ops case OpNot: case OpUMinus: diff --git a/qv4ir_p.h b/qv4ir_p.h index 9669cc95b1..c60c5def4b 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -136,6 +136,9 @@ enum AluOp { OpStrictEqual, OpStrictNotEqual, + OpInstanceof, + OpIn, + OpAnd, OpOr }; diff --git a/qv4isel.cpp b/qv4isel.cpp index 3f1ecb6c57..38c46f1e35 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -665,7 +665,7 @@ void InstructionSelection::visitMove(IR::Move *s) void (*op)(Context *, Value *, const Value *, const Value *) = 0; - switch (b->op) { + switch ((IR::AluOp) b->op) { case QQmlJS::IR::OpInvalid: case QQmlJS::IR::OpIfTrue: case QQmlJS::IR::OpNot: @@ -694,6 +694,12 @@ void InstructionSelection::visitMove(IR::Move *s) case QQmlJS::IR::OpNotEqual: op = __qmljs_ne; break; case QQmlJS::IR::OpStrictEqual: op = __qmljs_se; break; case QQmlJS::IR::OpStrictNotEqual: op = __qmljs_sne; break; + case QQmlJS::IR::OpInstanceof: op = __qmljs_instanceof; break; + + case QQmlJS::IR::OpIn: + Q_UNIMPLEMENTED(); + assert(!"TODO"); + break; case QQmlJS::IR::OpAnd: case QQmlJS::IR::OpOr: -- cgit v1.2.3 From b25e575ba0fbd64fd8b81d95dbab1a4ef4b35284 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 28 May 2012 21:49:20 +0200 Subject: Fix [[construct] and Object.prototype.toString --- qmljs_objects.cpp | 12 +++++++++++- qmljs_objects.h | 10 ++++++++++ qmljs_runtime.cpp | 8 ++++++-- qv4ecmaobjects.cpp | 5 ++++- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 5737e48ace..f138c1b634 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -188,7 +188,11 @@ void ScriptFunction::call(VM::Context *ctx) void ScriptFunction::construct(VM::Context *ctx) { - __qmljs_init_object(&ctx->thisObject, ctx->engine->newObject()); + Object *obj = ctx->engine->newObject(); + Value proto = getProperty(ctx, ctx->engine->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue; + __qmljs_init_object(&ctx->thisObject, obj); function->code(ctx); } @@ -224,6 +228,7 @@ ExecutionEngine::ExecutionEngine() id_length = identifier(QStringLiteral("length")); id_prototype = identifier(QStringLiteral("prototype")); + id_constructor = identifier(QStringLiteral("constructor")); id___proto__ = identifier(QStringLiteral("__proto__")); objectPrototype = new ObjectPrototype(); @@ -304,6 +309,9 @@ FunctionObject *ExecutionEngine::newNativeFunction(Context *scope, void (*code)( FunctionObject *ExecutionEngine::newScriptFunction(Context *scope, IR::Function *function) { ScriptFunction *f = new ScriptFunction(scope, function); + Object *proto = scope->engine->newObject(); + proto->setProperty(scope, scope->engine->id_constructor, Value::fromObject(f)); + f->setProperty(scope, scope->engine->id_prototype, Value::fromObject(proto)); f->prototype = scope->engine->functionPrototype; return f; } @@ -509,6 +517,8 @@ void Context::leaveConstructorContext(FunctionObject *f, Value *returnValue) Value proto = f->getProperty(this, engine->id_prototype); thisObject.objectValue->prototype = proto.objectValue; + if (! thisObject.isObject()) + thisObject.objectValue->prototype = engine->objectPrototype; leaveCallContext(f, returnValue); } diff --git a/qmljs_objects.h b/qmljs_objects.h index 3189828cf2..192a206432 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -201,6 +201,7 @@ struct Object { virtual ~Object(); + virtual QString className() { return QStringLiteral("Object"); } virtual BooleanObject *asBooleanObject() { return 0; } virtual NumberObject *asNumberObject() { return 0; } virtual StringObject *asStringObject() { return 0; } @@ -229,24 +230,28 @@ struct Object { struct BooleanObject: Object { Value value; BooleanObject(const Value &value): value(value) {} + virtual QString className() { return QStringLiteral("Boolean"); } virtual BooleanObject *asBooleanObject() { return this; } }; struct NumberObject: Object { Value value; NumberObject(const Value &value): value(value) {} + virtual QString className() { return QStringLiteral("Number"); } virtual NumberObject *asNumberObject() { return this; } }; struct StringObject: Object { Value value; StringObject(const Value &value): value(value) {} + virtual QString className() { return QStringLiteral("String"); } virtual StringObject *asStringObject() { return this; } }; struct DateObject: Object { Value value; DateObject(const Value &value): value(value) {} + virtual QString className() { return QStringLiteral("Date"); } virtual DateObject *asDateObject() { return this; } }; @@ -254,6 +259,7 @@ struct ArrayObject: Object { Array value; ArrayObject() {} ArrayObject(const Array &value): value(value) {} + virtual QString className() { return QStringLiteral("Array"); } virtual ArrayObject *asArrayObject() { return this; } virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes); }; @@ -274,6 +280,7 @@ struct FunctionObject: Object { , varCount(0) , needsActivation(true) {} + virtual QString className() { return QStringLiteral("Function"); } virtual FunctionObject *asFunctionObject() { return this; } virtual bool hasInstance(Context *ctx, const Value &value); virtual void call(Context *ctx); @@ -301,12 +308,14 @@ struct ScriptFunction: FunctionObject { struct ErrorObject: Object { Value value; ErrorObject(const Value &message): value(message) {} + virtual QString className() { return QStringLiteral("Error"); } virtual ErrorObject *asErrorObject() { return this; } }; struct ArgumentsObject: Object { Context *context; ArgumentsObject(Context *context): context(context) {} + virtual QString className() { return QStringLiteral("Arguments"); } virtual ArgumentsObject *asArgumentsObject() { return this; } virtual Value *getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes); }; @@ -410,6 +419,7 @@ struct ExecutionEngine String *id_length; String *id_prototype; + String *id_constructor; String *id___proto__; ExecutionEngine(); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 77ba2a9850..df05ff67ab 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -558,10 +558,11 @@ void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *na } else { Value o; __qmljs_to_object(ctx, &o, object); + if (o.isObject()) __qmljs_get_property(ctx, result, &o, name); else - ctx->throwTypeError(); + ctx->throwTypeError(); // ### not necessary. } } @@ -580,7 +581,10 @@ void __qmljs_get_activation(Context *ctx, Value *result) void __qmljs_get_thisObject(Context *ctx, Value *result) { - *result = ctx->thisObject; + if (ctx->thisObject.isObject()) + *result = ctx->thisObject; + else + *result = ctx->engine->globalObject; } void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y, bool leftFirst) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 30d94d3ed3..fa86f01f8d 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -497,7 +497,10 @@ void ObjectPrototype::init(Context *ctx, const Value &ctor) void ObjectPrototype::method_toString(Context *ctx) { - ctx->result = Value::fromString(ctx, "object"); + if (! ctx->thisObject.isObject()) + ctx->throwTypeError(); + else + ctx->result = Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(ctx->thisObject.objectValue->className())); } // -- cgit v1.2.3 From 57e9010fb9c6838e211238a0ca617ff66e6679b9 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 28 May 2012 22:37:29 +0200 Subject: Fix Array.prototype.concat --- qv4ecmaobjects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index fa86f01f8d..53fdf1732f 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1135,7 +1135,7 @@ void ArrayPrototype::method_concat(Context *ctx) result.concat(elt->value); else - result.assign(k, Value::fromString(arg.toString(ctx))); + result.assign(k, arg); } ctx->result = Value::fromObject(ctx->engine->newArrayObject(result)); -- cgit v1.2.3 From 8fa7463fb20d09f7a663d0d82a45bf1dd1f57f74 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 29 May 2012 10:20:28 +0200 Subject: Tests --- tests/instanceof.1.js | 9 +++++++++ tests/prototype.1.js | 14 ++++++++++++++ tests/prototype.2.js | 13 +++++++++++++ tests/prototype.3.js | 10 ++++++++++ tests/switch.1.js | 16 ++++++++++++++++ 5 files changed, 62 insertions(+) create mode 100644 tests/instanceof.1.js create mode 100644 tests/prototype.1.js create mode 100644 tests/prototype.2.js create mode 100644 tests/prototype.3.js create mode 100644 tests/switch.1.js diff --git a/tests/instanceof.1.js b/tests/instanceof.1.js new file mode 100644 index 0000000000..d991574e66 --- /dev/null +++ b/tests/instanceof.1.js @@ -0,0 +1,9 @@ + +function foo() {} +foo.prototype = {} + +var f = new foo() +print(foo.prototype) +print(f.__proto__) +print(foo.prototype == f.__proto__) +print(f instanceof foo) diff --git a/tests/prototype.1.js b/tests/prototype.1.js new file mode 100644 index 0000000000..40db84a0a9 --- /dev/null +++ b/tests/prototype.1.js @@ -0,0 +1,14 @@ + +function foo() { +} + +print(foo.toString()) +print(foo.__proto__) +print(foo.prototype) + +//foo.prototype.xx = function() { +// print("got xx") +//} + +//f = new foo +//f.xx() diff --git a/tests/prototype.2.js b/tests/prototype.2.js new file mode 100644 index 0000000000..860385d977 --- /dev/null +++ b/tests/prototype.2.js @@ -0,0 +1,13 @@ + +var oo = {} +function mk() { + function zoo() { + } + zoo.prototype = oo + return new zoo() +} + +var a = mk() +var b = mk() +print(a.__proto__ == b.__proto__) + diff --git a/tests/prototype.3.js b/tests/prototype.3.js new file mode 100644 index 0000000000..b76f544ee3 --- /dev/null +++ b/tests/prototype.3.js @@ -0,0 +1,10 @@ + +function foo() { + this.stuff() +} + +foo.prototype.stuff = function() { + print("this is cool stuff") +} + +f = new foo() diff --git a/tests/switch.1.js b/tests/switch.1.js new file mode 100644 index 0000000000..ca29b5f5de --- /dev/null +++ b/tests/switch.1.js @@ -0,0 +1,16 @@ + +function test(a) { + switch (a) { + default: return "cool" + case "xxx": print("got zero"); break; + case "ciao": case "ciao2": return 123 + case "hello": return 321 + } + return 444 +} + +print(test("xxx")) +print(test("hello")) +print(test("ciao")) +print(test("ciao2")) +print(test(9)) -- cgit v1.2.3 From 538ed5c57c547f56266f725adcaafbe38d43f5cf Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 30 May 2012 12:42:27 +0200 Subject: Generate cx-instructions for releational expressionsa. --- main.cpp | 2 +- qmljs_objects.cpp | 3 +- qmljs_objects.h | 10 ++- qmljs_runtime.h | 82 ++++++++++++++++++++++++ qv4codegen.cpp | 4 +- qv4isel.cpp | 184 +++++++++++++++++++++++++++++++++--------------------- 6 files changed, 207 insertions(+), 78 deletions(-) diff --git a/main.cpp b/main.cpp index 216cc81e31..29833a939f 100644 --- a/main.cpp +++ b/main.cpp @@ -53,7 +53,7 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS IR::Module module; IR::Function *globalCode = 0; - const size_t codeSize = 10 * getpagesize(); + const size_t codeSize = 30 * getpagesize(); uchar *code = (uchar *) malloc(codeSize); assert(! (size_t(code) & 15)); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index f138c1b634..52c825f27c 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -490,7 +490,8 @@ void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionO locals = varCount ? new Value[varCount] : 0; hasUncaughtException = false; calledAsConstructor = false; - std::fill(locals, locals + varCount, Value::undefinedValue()); + if (varCount) + std::fill(locals, locals + varCount, Value::undefinedValue()); } void Context::leaveCallContext(FunctionObject *f, Value *returnValue) diff --git a/qmljs_objects.h b/qmljs_objects.h index 192a206432..731adbeafa 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -41,9 +41,13 @@ struct String { String(const QString &text) : _text(text), _hashValue(0) {} - bool isEqualTo(const String *other) const { - if (other && hashValue() == other->hashValue()) - return this == other || toQString() == other->toQString(); + inline bool isEqualTo(const String *other) const { + if (other && hashValue() == other->hashValue()) { + if (this == other) + return true; + else + return toQString() == other->toQString(); + } return false; } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index e19309aea9..d4d9e75e6e 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -180,6 +180,18 @@ void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *rig void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right); void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *right); +bool __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right); +bool __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right); +bool __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right); +bool __qmljs_cmp_le(Context *ctx, const Value *left, const Value *right); +bool __qmljs_cmp_eq(Context *ctx, const Value *left, const Value *right); +bool __qmljs_cmp_ne(Context *ctx, const Value *left, const Value *right); +bool __qmljs_cmp_se(Context *ctx, const Value *left, const Value *right); +bool __qmljs_cmp_sne(Context *ctx, const Value *left, const Value *right); +bool __qmljs_cmp_instanceof(Context *ctx, const Value *left, const Value *right); +bool __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right); + + } // extern "C" struct Value { @@ -808,6 +820,76 @@ inline void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Va __qmljs_init_boolean(result, r); } +inline bool __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right) +{ + Value v; + __qmljs_gt(ctx, &v, left, right); + return __qmljs_to_boolean(ctx, &v); +} + +inline bool __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right) +{ + Value v; + __qmljs_lt(ctx, &v, left, right); + return __qmljs_to_boolean(ctx, &v); +} + +inline bool __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right) +{ + Value v; + __qmljs_ge(ctx, &v, left, right); + return __qmljs_to_boolean(ctx, &v); +} + +inline bool __qmljs_cmp_le(Context *ctx, const Value *left, const Value *right) +{ + Value v; + __qmljs_le(ctx, &v, left, right); + return __qmljs_to_boolean(ctx, &v); +} + +inline bool __qmljs_cmp_eq(Context *ctx, const Value *left, const Value *right) +{ + Value v; + __qmljs_eq(ctx, &v, left, right); + return __qmljs_to_boolean(ctx, &v); +} + +inline bool __qmljs_cmp_ne(Context *ctx, const Value *left, const Value *right) +{ + Value v; + __qmljs_ne(ctx, &v, left, right); + return __qmljs_to_boolean(ctx, &v); +} + +inline bool __qmljs_cmp_se(Context *ctx, const Value *left, const Value *right) +{ + Value v; + __qmljs_se(ctx, &v, left, right); + return __qmljs_to_boolean(ctx, &v); +} + +inline bool __qmljs_cmp_sne(Context *ctx, const Value *left, const Value *right) +{ + Value v; + __qmljs_sne(ctx, &v, left, right); + return __qmljs_to_boolean(ctx, &v); +} + +inline bool __qmljs_cmp_instanceof(Context *ctx, const Value *left, const Value *right) +{ + Value v; + __qmljs_instanceof(ctx, &v, left, right); + return __qmljs_to_boolean(ctx, &v); +} + +inline bool __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right) +{ + Value v; + __qmljs_in(ctx, &v, left, right); + return __qmljs_to_boolean(ctx, &v); +} + inline bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y) { if (x->type != y->type) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 2cf4a56f3b..577ca84112 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -414,7 +414,7 @@ void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) void Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) { - if (! cond->asTemp()) { + if (! (cond->asTemp() || cond->asBinop())) { const unsigned t = _block->newTemp(); _block->MOVE(_block->TEMP(t), cond); cond = _block->TEMP(t); @@ -949,7 +949,7 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::Lt: case QSOperator::StrictEqual: case QSOperator::StrictNotEqual: - if (false && _expr.accept(cx)) { + if (_expr.accept(cx)) { cjump(binop(IR::binaryOperator(ast->op), *left, *right), _expr.iftrue, _expr.iffalse); } else { IR::Expr *e = binop(IR::binaryOperator(ast->op), *left, *right); diff --git a/qv4isel.cpp b/qv4isel.cpp index 38c46f1e35..8407f3f38e 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -4,6 +4,7 @@ #include "qmljs_objects.h" #define TARGET_AMD64 +#define g_assert assert typedef quint64 guint64; typedef qint64 gint64; typedef uchar guint8; @@ -467,7 +468,7 @@ void InstructionSelection::visitMove(IR::Move *s) case IR::NumberType: amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); + amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); amd64_call_code(_codePtr, __qmljs_set_activation_property_number); break; @@ -534,9 +535,9 @@ void InstructionSelection::visitMove(IR::Move *s) case IR::NumberType: amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); + amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NUMBER_TYPE, 4); - amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), X86_XMM0); + amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), AMD64_XMM0); break; default: @@ -602,11 +603,11 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RDX, e); void (*op)(Context *, Value *, const Value *) = 0; switch (u->op) { - case QQmlJS::IR::OpIfTrue: assert(!"unreachable"); break; - case QQmlJS::IR::OpNot: op = __qmljs_not; break; - case QQmlJS::IR::OpUMinus: op = __qmljs_uminus; break; - case QQmlJS::IR::OpUPlus: op = __qmljs_uplus; break; - case QQmlJS::IR::OpCompl: op = __qmljs_compl; break; + case IR::OpIfTrue: assert(!"unreachable"); break; + case IR::OpNot: op = __qmljs_not; break; + case IR::OpUMinus: op = __qmljs_uminus; break; + case IR::OpUPlus: op = __qmljs_uplus; break; + case IR::OpCompl: op = __qmljs_compl; break; default: assert(!"unreachable"); break; } // switch amd64_call_code(_codePtr, op); @@ -633,31 +634,32 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RCX, 0, NUMBER_TYPE, 4); label2 = _codePtr; amd64_branch8(_codePtr, X86_CC_NE, 0, 0); - amd64_movsd_reg_membase(_codePtr, X86_XMM0, AMD64_RDX, offsetof(Value, numberValue)); - amd64_movsd_reg_membase(_codePtr, X86_XMM1, AMD64_RCX, offsetof(Value, numberValue)); + amd64_movsd_reg_membase(_codePtr, AMD64_XMM0, AMD64_RDX, offsetof(Value, numberValue)); + amd64_movsd_reg_membase(_codePtr, AMD64_XMM1, AMD64_RCX, offsetof(Value, numberValue)); switch (b->op) { case IR::OpAdd: - amd64_sse_addsd_reg_reg(_codePtr, X86_XMM0, X86_XMM1); + amd64_sse_addsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); break; case IR::OpSub: - amd64_sse_subsd_reg_reg(_codePtr, X86_XMM0, X86_XMM1); + amd64_sse_subsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); break; case IR::OpMul: - amd64_sse_mulsd_reg_reg(_codePtr, X86_XMM0, X86_XMM1); + amd64_sse_mulsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); break; case IR::OpDiv: - amd64_sse_divsd_reg_reg(_codePtr, X86_XMM0, X86_XMM1); + amd64_sse_divsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); break; default: Q_UNREACHABLE(); } // switch amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NUMBER_TYPE, 4); - amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), X86_XMM0); + amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), AMD64_XMM0); label3 = _codePtr; amd64_jump32(_codePtr, 0); } + if (label1 && label2) { amd64_patch(label1, _codePtr); amd64_patch(label2, _codePtr); @@ -666,43 +668,43 @@ void InstructionSelection::visitMove(IR::Move *s) void (*op)(Context *, Value *, const Value *, const Value *) = 0; switch ((IR::AluOp) b->op) { - case QQmlJS::IR::OpInvalid: - case QQmlJS::IR::OpIfTrue: - case QQmlJS::IR::OpNot: - case QQmlJS::IR::OpUMinus: - case QQmlJS::IR::OpUPlus: - case QQmlJS::IR::OpCompl: + case IR::OpInvalid: + case IR::OpIfTrue: + case IR::OpNot: + case IR::OpUMinus: + case IR::OpUPlus: + case IR::OpCompl: assert(!"unreachable"); break; - case QQmlJS::IR::OpBitAnd: op = __qmljs_bit_and; break; - case QQmlJS::IR::OpBitOr: op = __qmljs_bit_or; break; - case QQmlJS::IR::OpBitXor: op = __qmljs_bit_xor; break; - case QQmlJS::IR::OpAdd: op = __qmljs_add; break; - case QQmlJS::IR::OpSub: op = __qmljs_sub; break; - case QQmlJS::IR::OpMul: op = __qmljs_mul; break; - case QQmlJS::IR::OpDiv: op = __qmljs_div; break; - case QQmlJS::IR::OpMod: op = __qmljs_mod; break; - case QQmlJS::IR::OpLShift: op = __qmljs_shl; break; - case QQmlJS::IR::OpRShift: op = __qmljs_shr; break; - case QQmlJS::IR::OpURShift: op = __qmljs_ushr; break; - case QQmlJS::IR::OpGt: op = __qmljs_gt; break; - case QQmlJS::IR::OpLt: op = __qmljs_lt; break; - case QQmlJS::IR::OpGe: op = __qmljs_ge; break; - case QQmlJS::IR::OpLe: op = __qmljs_le; break; - case QQmlJS::IR::OpEqual: op = __qmljs_eq; break; - case QQmlJS::IR::OpNotEqual: op = __qmljs_ne; break; - case QQmlJS::IR::OpStrictEqual: op = __qmljs_se; break; - case QQmlJS::IR::OpStrictNotEqual: op = __qmljs_sne; break; - case QQmlJS::IR::OpInstanceof: op = __qmljs_instanceof; break; - - case QQmlJS::IR::OpIn: + case IR::OpBitAnd: op = __qmljs_bit_and; break; + case IR::OpBitOr: op = __qmljs_bit_or; break; + case IR::OpBitXor: op = __qmljs_bit_xor; break; + case IR::OpAdd: op = __qmljs_add; break; + case IR::OpSub: op = __qmljs_sub; break; + case IR::OpMul: op = __qmljs_mul; break; + case IR::OpDiv: op = __qmljs_div; break; + case IR::OpMod: op = __qmljs_mod; break; + case IR::OpLShift: op = __qmljs_shl; break; + case IR::OpRShift: op = __qmljs_shr; break; + case IR::OpURShift: op = __qmljs_ushr; break; + case IR::OpGt: op = __qmljs_gt; break; + case IR::OpLt: op = __qmljs_lt; break; + case IR::OpGe: op = __qmljs_ge; break; + case IR::OpLe: op = __qmljs_le; break; + case IR::OpEqual: op = __qmljs_eq; break; + case IR::OpNotEqual: op = __qmljs_ne; break; + case IR::OpStrictEqual: op = __qmljs_se; break; + case IR::OpStrictNotEqual: op = __qmljs_sne; break; + case IR::OpInstanceof: op = __qmljs_instanceof; break; + + case IR::OpIn: Q_UNIMPLEMENTED(); assert(!"TODO"); break; - case QQmlJS::IR::OpAnd: - case QQmlJS::IR::OpOr: + case IR::OpAnd: + case IR::OpOr: assert(!"unreachable"); break; } @@ -730,7 +732,7 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RSI, base); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); + amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); amd64_call_code(_codePtr, __qmljs_set_property_number); } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); @@ -770,7 +772,7 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RSI, ss->base->asTemp()); loadTempAddress(AMD64_RDX, ss->index->asTemp()); amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); + amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); amd64_call_code(_codePtr, __qmljs_set_element_number); } else { Q_UNIMPLEMENTED(); @@ -790,21 +792,21 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); + amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); void (*op)(Context *, Value *, double); switch (s->op) { - case QQmlJS::IR::OpBitAnd: op = __qmljs_inplace_bit_and; break; - case QQmlJS::IR::OpBitOr: op = __qmljs_inplace_bit_or; break; - case QQmlJS::IR::OpBitXor: op = __qmljs_inplace_bit_xor; break; - case QQmlJS::IR::OpAdd: op = __qmljs_inplace_add; break; - case QQmlJS::IR::OpSub: op = __qmljs_inplace_sub; break; - case QQmlJS::IR::OpMul: op = __qmljs_inplace_mul; break; - case QQmlJS::IR::OpDiv: op = __qmljs_inplace_div; break; - case QQmlJS::IR::OpMod: op = __qmljs_inplace_mod; break; - case QQmlJS::IR::OpLShift: op = __qmljs_inplace_shl; break; - case QQmlJS::IR::OpRShift: op = __qmljs_inplace_shr; break; - case QQmlJS::IR::OpURShift: op = __qmljs_inplace_ushr; break; + case IR::OpBitAnd: op = __qmljs_inplace_bit_and; break; + case IR::OpBitOr: op = __qmljs_inplace_bit_or; break; + case IR::OpBitXor: op = __qmljs_inplace_bit_xor; break; + case IR::OpAdd: op = __qmljs_inplace_add; break; + case IR::OpSub: op = __qmljs_inplace_sub; break; + case IR::OpMul: op = __qmljs_inplace_mul; break; + case IR::OpDiv: op = __qmljs_inplace_div; break; + case IR::OpMod: op = __qmljs_inplace_mod; break; + case IR::OpLShift: op = __qmljs_inplace_shl; break; + case IR::OpRShift: op = __qmljs_inplace_shr; break; + case IR::OpURShift: op = __qmljs_inplace_ushr; break; default: Q_UNREACHABLE(); break; @@ -816,21 +818,20 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); amd64_mov_reg_reg(_codePtr, AMD64_RDX, AMD64_RSI, 8); - // loadTempAddress(AMD64_RDX, t); loadTempAddress(AMD64_RCX, t2); void (*op)(Context *, Value *, const Value *, const Value *); switch (s->op) { - case QQmlJS::IR::OpBitAnd: op = __qmljs_bit_and; break; - case QQmlJS::IR::OpBitOr: op = __qmljs_bit_or; break; - case QQmlJS::IR::OpBitXor: op = __qmljs_bit_xor; break; - case QQmlJS::IR::OpAdd: op = __qmljs_add; break; - case QQmlJS::IR::OpSub: op = __qmljs_sub; break; - case QQmlJS::IR::OpMul: op = __qmljs_mul; break; - case QQmlJS::IR::OpDiv: op = __qmljs_div; break; - case QQmlJS::IR::OpMod: op = __qmljs_mod; break; - case QQmlJS::IR::OpLShift: op = __qmljs_shl; break; - case QQmlJS::IR::OpRShift: op = __qmljs_shr; break; - case QQmlJS::IR::OpURShift: op = __qmljs_ushr; break; + case IR::OpBitAnd: op = __qmljs_bit_and; break; + case IR::OpBitOr: op = __qmljs_bit_or; break; + case IR::OpBitXor: op = __qmljs_bit_xor; break; + case IR::OpAdd: op = __qmljs_add; break; + case IR::OpSub: op = __qmljs_sub; break; + case IR::OpMul: op = __qmljs_mul; break; + case IR::OpDiv: op = __qmljs_div; break; + case IR::OpMod: op = __qmljs_mod; break; + case IR::OpLShift: op = __qmljs_shl; break; + case IR::OpRShift: op = __qmljs_shr; break; + case IR::OpURShift: op = __qmljs_ushr; break; default: Q_UNREACHABLE(); break; @@ -910,7 +911,48 @@ void InstructionSelection::visitCJump(IR::CJump *s) amd64_jump32(_codePtr, 0); } return; + } else if (IR::Binop *b = s->cond->asBinop()) { + IR::Temp *l = b->left->asTemp(); + IR::Temp *r = b->right->asTemp(); + if (l && r) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, l); + loadTempAddress(AMD64_RDX, r); + + // ### TODO: instruction selection for common cases (e.g. number1 < number2) + + bool (*op)(Context *, const Value *, const Value *); + switch (b->op) { + default: Q_UNREACHABLE(); assert(!"todo"); break; + case IR::OpGt: op = __qmljs_cmp_gt; break; + case IR::OpLt: op = __qmljs_cmp_lt; break; + case IR::OpGe: op = __qmljs_cmp_ge; break; + case IR::OpLe: op = __qmljs_cmp_le; break; + case IR::OpEqual: op = __qmljs_cmp_eq; break; + case IR::OpNotEqual: op = __qmljs_cmp_ne; break; + case IR::OpStrictEqual: op = __qmljs_cmp_se; break; + case IR::OpStrictNotEqual: op = __qmljs_cmp_sne; break; + case IR::OpInstanceof: op = __qmljs_cmp_instanceof; break; + case IR::OpIn: op = __qmljs_cmp_in; break; + } // switch + + amd64_call_code(_codePtr, op); + x86_alu_reg_imm(_codePtr, X86_CMP, X86_EAX, 0); + + _patches[s->iftrue].append(_codePtr); + amd64_branch32(_codePtr, X86_CC_NZ, 0, 1); + + if (_block->index + 1 != s->iffalse->index) { + _patches[s->iffalse].append(_codePtr); + amd64_jump32(_codePtr, 0); + } + + return; + } else { + assert(!"wip"); + } } + Q_UNIMPLEMENTED(); assert(!"TODO"); } -- cgit v1.2.3 From 8ccb1e0e683b8c047a7ccfa068392873d72ca6c4 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 30 May 2012 12:52:58 +0200 Subject: Use `assert' instead of Q_ASSERT. --- qmljs_runtime.h | 4 +- qv4codegen.cpp | 134 ++++++++++++++++++++++++++--------------------------- qv4ecmaobjects.cpp | 4 +- qv4ir.cpp | 3 +- 4 files changed, 73 insertions(+), 72 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index d4d9e75e6e..71bdd58a2e 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -364,7 +364,7 @@ inline bool __qmljs_to_boolean(Context *ctx, const Value *value) case OBJECT_TYPE: return true; } - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } @@ -908,7 +908,7 @@ inline bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y) case OBJECT_TYPE: return x->objectValue == y->objectValue; } - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 577ca84112..b67c52f615 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -48,7 +48,7 @@ struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor , _stmt(0) {} void operator()(IR::Stmt *s) { - Q_ASSERT(! s->d); + assert(! s->d); s->d = new IR::Stmt::Data; qSwap(_stmt, s); _stmt->accept(this); @@ -302,12 +302,12 @@ IR::Function *Codegen::operator()(Program *node, IR::Module *module) void Codegen::enterEnvironment(Node *node) { _env = _envMap.value(node); - Q_ASSERT(_env); + assert(_env); } void Codegen::leaveEnvironment() { - Q_ASSERT(_env); + assert(_env); _env = _env->parent; } @@ -505,52 +505,52 @@ Codegen::UiMember Codegen::uiObjectMember(UiObjectMember *ast) void Codegen::argumentList(ArgumentList *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::caseBlock(CaseBlock *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::caseClause(CaseClause *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::caseClauses(CaseClauses *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::catchNode(Catch *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::defaultClause(DefaultClause *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::elementList(ElementList *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::elision(Elision *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::finallyNode(Finally *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::formalParameterList(FormalParameterList *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::functionBody(FunctionBody *ast) @@ -568,7 +568,7 @@ void Codegen::program(Program *ast) void Codegen::propertyNameAndValueList(PropertyNameAndValueList *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::sourceElements(SourceElements *ast) @@ -580,57 +580,57 @@ void Codegen::sourceElements(SourceElements *ast) void Codegen::statementList(StatementList *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::uiArrayMemberList(UiArrayMemberList *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::uiImport(UiImport *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::uiImportList(UiImportList *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::uiObjectInitializer(UiObjectInitializer *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::uiObjectMemberList(UiObjectMemberList *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::uiParameterList(UiParameterList *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::uiProgram(UiProgram *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::uiQualifiedId(UiQualifiedId *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); } void Codegen::variableDeclaration(VariableDeclaration *ast) { if (ast->expression) { Result expr = expression(ast->expression); - Q_ASSERT(expr.code); + assert(expr.code); const int index = _env->findMember(ast->name.toString()); - Q_ASSERT(index != -1); + assert(index != -1); move(_block->TEMP(index), *expr); } } @@ -645,151 +645,151 @@ void Codegen::variableDeclarationList(VariableDeclarationList *ast) bool Codegen::visit(ArgumentList *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(CaseBlock *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(CaseClause *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(CaseClauses *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(Catch *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(DefaultClause *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(ElementList *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(Elision *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(Finally *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(FormalParameterList *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(FunctionBody *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(Program *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(PropertyNameAndValueList *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(SourceElements *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(StatementList *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(UiArrayMemberList *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(UiImport *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(UiImportList *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(UiObjectInitializer *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(UiObjectMemberList *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(UiParameterList *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(UiProgram *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(UiQualifiedId *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(VariableDeclaration *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } bool Codegen::visit(VariableDeclarationList *) { - Q_ASSERT(!"unreachable"); + assert(!"unreachable"); return false; } @@ -1032,7 +1032,7 @@ bool Codegen::visit(ConditionalExpression *ast) bool Codegen::visit(DeleteExpression *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); return false; } @@ -1054,7 +1054,7 @@ bool Codegen::visit(FunctionExpression *ast) IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0, false); if (_expr.accept(nx)) { const int index = _env->findMember(ast->name.toString()); - Q_ASSERT(index != -1); + assert(index != -1); move(_block->TEMP(index), _block->CLOSURE(function)); } else { _expr.code = _block->CLOSURE(function); @@ -1203,7 +1203,7 @@ bool Codegen::visit(PreIncrementExpression *ast) bool Codegen::visit(RegExpLiteral *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); return false; } @@ -1272,7 +1272,7 @@ bool Codegen::visit(FunctionDeclaration *ast) IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0, true); if (_expr.accept(nx)) { const int index = _env->findMember(ast->name.toString()); - Q_ASSERT(index != -1); + assert(index != -1); move(_block->TEMP(index), _block->CLOSURE(function)); } else { _expr.code = _block->CLOSURE(function); @@ -1450,7 +1450,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, for (int i = 0; i < _env->vars.size(); ++i) { unsigned t = entryBlock->newTemp(); - Q_ASSERT(t == unsigned(i)); + assert(t == unsigned(i)); } unsigned returnAddress = entryBlock->newTemp(); @@ -1569,7 +1569,7 @@ bool Codegen::visit(ContinueStatement *) bool Codegen::visit(DebuggerStatement *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); return false; } @@ -1605,7 +1605,7 @@ bool Codegen::visit(ExpressionStatement *ast) bool Codegen::visit(ForEachStatement *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); return false; } @@ -1668,13 +1668,13 @@ bool Codegen::visit(IfStatement *ast) bool Codegen::visit(LabelledStatement *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); return false; } bool Codegen::visit(LocalForEachStatement *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); return false; } @@ -1808,7 +1808,7 @@ bool Codegen::visit(ThrowStatement *ast) bool Codegen::visit(TryStatement *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); return false; } @@ -1844,42 +1844,42 @@ bool Codegen::visit(WhileStatement *ast) bool Codegen::visit(WithStatement *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); return false; } bool Codegen::visit(UiArrayBinding *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); return false; } bool Codegen::visit(UiObjectBinding *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); return false; } bool Codegen::visit(UiObjectDefinition *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); return false; } bool Codegen::visit(UiPublicMember *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); return false; } bool Codegen::visit(UiScriptBinding *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); return false; } bool Codegen::visit(UiSourceElement *) { - Q_ASSERT(!"not implemented"); + assert(!"not implemented"); return false; } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 53fdf1732f..82c7e30b9e 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -113,7 +113,7 @@ static inline bool InLeapYear(double t) if (x == 365) return 0; - Q_ASSERT (x == 366); + assert(x == 366); return 1; } @@ -1322,7 +1322,7 @@ void ArrayPrototype::method_splice(Context *ctx) for (size_t i = 2; i < ctx->argumentCount; ++i) items << ctx->argument(i); ArrayObject *otherInstance = a.asArrayObject(); - Q_ASSERT(otherInstance); + assert(otherInstance); instance->value.splice(start, deleteCount, items, otherInstance->value); ctx->result = a; } else { diff --git a/qv4ir.cpp b/qv4ir.cpp index 6718c62848..5f851349cb 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -45,6 +45,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -598,7 +599,7 @@ Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) case OpInstanceof: case OpIn: - Q_ASSERT(!"unreachabe"); + assert(!"unreachabe"); break; case OpIfTrue: // unary ops -- cgit v1.2.3 From d65f9ddff1fd9c1aac037bb1163c6fbc7e9a637c Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Thu, 31 May 2012 17:32:49 +0200 Subject: Generalized instruction selection. This will simplify the instruction selection pass for different architectures. --- main.cpp | 1 + qv4ir_p.h | 36 +- qv4isel.cpp | 969 ---------------------------------------------------- qv4isel_p.h | 401 +++++++++++++++++++--- qv4isel_x86_64.cpp | 971 +++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4isel_x86_64_p.h | 54 +++ v4.pro | 7 +- 7 files changed, 1408 insertions(+), 1031 deletions(-) create mode 100644 qv4isel_x86_64.cpp create mode 100644 qv4isel_x86_64_p.h diff --git a/main.cpp b/main.cpp index 29833a939f..87c9095bb8 100644 --- a/main.cpp +++ b/main.cpp @@ -2,6 +2,7 @@ #include "qmljs_objects.h" #include "qv4codegen_p.h" #include "qv4isel_p.h" +#include "qv4isel_x86_64_p.h" #include "qv4syntaxchecker_p.h" #include "qv4ecmaobjects_p.h" diff --git a/qv4ir_p.h b/qv4ir_p.h index c60c5def4b..31914265cb 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -171,28 +171,28 @@ const char *typeName(IR::Type t); struct ExprVisitor { virtual ~ExprVisitor() {} - virtual void visitConst(Const *) {} - virtual void visitString(String *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *) {} - virtual void visitClosure(Closure *) {} - virtual void visitUnop(Unop *) {} - virtual void visitBinop(Binop *) {} - virtual void visitCall(Call *) {} - virtual void visitNew(New *) {} - virtual void visitSubscript(Subscript *) {} - virtual void visitMember(Member *) {} + virtual void visitConst(Const *) = 0; + virtual void visitString(String *) = 0; + virtual void visitName(Name *) = 0; + virtual void visitTemp(Temp *) = 0; + virtual void visitClosure(Closure *) = 0; + virtual void visitUnop(Unop *) = 0; + virtual void visitBinop(Binop *) = 0; + virtual void visitCall(Call *) = 0; + virtual void visitNew(New *) = 0; + virtual void visitSubscript(Subscript *) = 0; + virtual void visitMember(Member *) = 0; }; struct StmtVisitor { virtual ~StmtVisitor() {} - virtual void visitExp(Exp *) {} - virtual void visitEnter(Enter *) {} - virtual void visitLeave(Leave *) {} - virtual void visitMove(Move *) {} - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *) {} - virtual void visitRet(Ret *) {} + virtual void visitExp(Exp *) = 0; + virtual void visitEnter(Enter *) = 0; + virtual void visitLeave(Leave *) = 0; + virtual void visitMove(Move *) = 0; + virtual void visitJump(Jump *) = 0; + virtual void visitCJump(CJump *) = 0; + virtual void visitRet(Ret *) = 0; }; struct Expr { diff --git a/qv4isel.cpp b/qv4isel.cpp index 8407f3f38e..1e833bb490 100644 --- a/qv4isel.cpp +++ b/qv4isel.cpp @@ -1,971 +1,2 @@ #include "qv4isel_p.h" -#include "qmljs_runtime.h" -#include "qmljs_objects.h" - -#define TARGET_AMD64 -#define g_assert assert -typedef quint64 guint64; -typedef qint64 gint64; -typedef uchar guint8; -typedef uint guint32; -typedef void *gpointer; -#include "amd64-codegen.h" - -#include -#include -#include - -#ifndef NO_UDIS86 -# include -#endif - -using namespace QQmlJS; -using namespace QQmlJS::x86_64; -using namespace QQmlJS::VM; - -namespace { -QTextStream qout(stdout, QIODevice::WriteOnly); -} - -static inline void -amd64_patch (unsigned char* code, gpointer target) -{ - guint8 rex = 0; - -#ifdef __native_client_codegen__ - code = amd64_skip_nops (code); -#endif -#if defined(__native_client_codegen__) && defined(__native_client__) - if (nacl_is_code_address (code)) { - /* For tail calls, code is patched after being installed */ - /* but not through the normal "patch callsite" method. */ - unsigned char buf[kNaClAlignment]; - unsigned char *aligned_code = (uintptr_t)code & ~kNaClAlignmentMask; - int ret; - memcpy (buf, aligned_code, kNaClAlignment); - /* Patch a temp buffer of bundle size, */ - /* then install to actual location. */ - amd64_patch (buf + ((uintptr_t)code - (uintptr_t)aligned_code), target); - ret = nacl_dyncode_modify (aligned_code, buf, kNaClAlignment); - g_assert (ret == 0); - return; - } - target = nacl_modify_patch_target (target); -#endif - - /* Skip REX */ - if ((code [0] >= 0x40) && (code [0] <= 0x4f)) { - rex = code [0]; - code += 1; - } - - if ((code [0] & 0xf8) == 0xb8) { - /* amd64_set_reg_template */ - *(guint64*)(code + 1) = (guint64)target; - } - else if ((code [0] == 0x8b) && rex && x86_modrm_mod (code [1]) == 0 && x86_modrm_rm (code [1]) == 5) { - /* mov 0(%rip), %dreg */ - *(guint32*)(code + 2) = (guint32)(guint64)target - 7; - } - else if ((code [0] == 0xff) && (code [1] == 0x15)) { - /* call *(%rip) */ - *(guint32*)(code + 2) = ((guint32)(guint64)target) - 7; - } - else if (code [0] == 0xe8) { - /* call */ - gint64 disp = (guint8*)target - (guint8*)code; - assert (amd64_is_imm32 (disp)); - x86_patch (code, (unsigned char*)target); - } - else - x86_patch (code, (unsigned char*)target); -} -InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *buffer) - : _engine(engine) - , _module(module) - , _function(0) - , _block(0) - , _buffer(buffer) - , _code(buffer) - , _codePtr(buffer) -{ -} - -InstructionSelection::~InstructionSelection() -{ -} - -void InstructionSelection::operator()(IR::Function *function) -{ - qSwap(_function, function); - - _code = _codePtr; - _code = (uchar *) ((size_t(_code) + 15) & ~15); - _function->code = (void (*)(VM::Context *)) _code; - _codePtr = _code; - - int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) * sizeof(Value); - locals = (locals + 15) & ~15; - - amd64_push_reg(_codePtr, AMD64_RBP); - amd64_push_reg(_codePtr, AMD64_R14); - amd64_push_reg(_codePtr, AMD64_R15); - - amd64_mov_reg_reg(_codePtr, AMD64_RBP, AMD64_RSP, 8); - amd64_mov_reg_reg(_codePtr, AMD64_R14, AMD64_RDI, 8); - amd64_alu_reg_imm(_codePtr, X86_SUB, AMD64_RSP, locals); - - amd64_mov_reg_membase(_codePtr, AMD64_R15, AMD64_R14, offsetof(Context, locals), 8); - - foreach (IR::BasicBlock *block, _function->basicBlocks) { - _block = block; - _addrs[block] = _codePtr; - foreach (IR::Stmt *s, block->statements) { - s->accept(this); - } - } - - QHashIterator > it(_patches); - while (it.hasNext()) { - it.next(); - uchar *target = _addrs[it.key()]; - foreach (uchar *instr, it.value()) { - amd64_patch(instr, target); - } - } - - amd64_alu_reg_imm(_codePtr, X86_ADD, AMD64_RSP, locals); - amd64_pop_reg(_codePtr, AMD64_R15); - amd64_pop_reg(_codePtr, AMD64_R14); - amd64_pop_reg(_codePtr, AMD64_RBP); - amd64_ret(_codePtr); - -#ifndef NO_UDIS86 - static bool showCode = !qgetenv("SHOW_CODE").isNull(); - if (showCode) { - printf("code size: %ld bytes\n", (_codePtr - _code)); - ud_t ud_obj; - - ud_init(&ud_obj); - ud_set_input_buffer(&ud_obj, _code, _codePtr - _code); - ud_set_mode(&ud_obj, 64); - ud_set_syntax(&ud_obj, UD_SYN_ATT); - - while (ud_disassemble(&ud_obj)) { - printf("\t%s\n", ud_insn_asm(&ud_obj)); - } - } -#endif - qSwap(_function, _function); -} - -String *InstructionSelection::identifier(const QString &s) -{ - return _engine->identifier(s); -} - -void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) -{ - if (t->index < 0) { - const int arg = -t->index - 1; - amd64_mov_reg_membase(_codePtr, reg, AMD64_R14, offsetof(Context, arguments), 8); - amd64_lea_membase(_codePtr, reg, reg, sizeof(Value) * arg); - } else if (t->index < _function->locals.size()) { - amd64_lea_membase(_codePtr, reg, AMD64_R15, sizeof(Value) * t->index); - } else { - amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * (_function->maxNumberOfArguments + t->index - _function->locals.size())); - } -} - -void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) -{ - IR::Name *baseName = call->base->asName(); - assert(baseName != 0); - - int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = call->args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); - loadTempAddress(AMD64_RSI, arg); - amd64_call_code(_codePtr, __qmljs_copy); - } - - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context - - if (result) - loadTempAddress(AMD64_RSI, result); - else - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - - if (baseName->id) { - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id)); - amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); - amd64_call_code(_codePtr, __qmljs_call_activation_property); - } else { - switch (baseName->builtin) { - case IR::Name::builtin_invalid: - Q_UNREACHABLE(); - break; - case IR::Name::builtin_typeof: - amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); - amd64_call_code(_codePtr, __qmljs_builtin_typeof); - break; - case IR::Name::builtin_throw: - amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); - amd64_call_code(_codePtr, __qmljs_builtin_throw); - break; - case IR::Name::builtin_rethrow: - amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); - amd64_call_code(_codePtr, __qmljs_builtin_rethrow); - return; // we need to return to avoid checking the exceptions - } - } - - checkExceptions(); -} - - -void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) -{ - IR::Temp *baseTemp = call->base->asTemp(); - assert(baseTemp != 0); - - int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = call->args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); - loadTempAddress(AMD64_RSI, arg); - amd64_call_code(_codePtr, __qmljs_copy); - } - - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context - - if (result) - loadTempAddress(AMD64_RSI, result); - else - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RDX, AMD64_RDX); - loadTempAddress(AMD64_RCX, baseTemp); - amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); - amd64_call_code(_codePtr, __qmljs_call_value); - - checkExceptions(); -} - -void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) -{ - IR::Member *member = call->base->asMember(); - assert(member != 0); - assert(member->base->asTemp()); - - int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = call->args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); - loadTempAddress(AMD64_RSI, arg); - amd64_call_code(_codePtr, __qmljs_copy); - } - - //__qmljs_call_property(ctx, result, base, name, args, argc); - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context - - if (result) - loadTempAddress(AMD64_RSI, result); - else - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - - loadTempAddress(AMD64_RDX, member->base->asTemp()); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name)); - amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); - amd64_call_code(_codePtr, __qmljs_call_property); - - checkExceptions(); -} - -void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) -{ - IR::Name *baseName = call->base->asName(); - assert(baseName != 0); - - int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = call->args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); - loadTempAddress(AMD64_RSI, arg); - amd64_call_code(_codePtr, __qmljs_copy); - } - - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context - - if (result) - loadTempAddress(AMD64_RSI, result); - else - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id)); - amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); - amd64_call_code(_codePtr, __qmljs_construct_activation_property); - - checkExceptions(); -} - -void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) -{ - IR::Member *member = call->base->asMember(); - assert(member != 0); - assert(member->base->asTemp()); - - int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = call->args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); - loadTempAddress(AMD64_RSI, arg); - amd64_call_code(_codePtr, __qmljs_copy); - } - - //__qmljs_call_property(ctx, result, base, name, args, argc); - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context - - if (result) - loadTempAddress(AMD64_RSI, result); - else - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - - loadTempAddress(AMD64_RDX, member->base->asTemp()); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name)); - amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); - amd64_call_code(_codePtr, __qmljs_construct_property); - - checkExceptions(); -} - -void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) -{ - IR::Temp *baseTemp = call->base->asTemp(); - assert(baseTemp != 0); - - int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = call->args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); - loadTempAddress(AMD64_RSI, arg); - amd64_call_code(_codePtr, __qmljs_copy); - } - - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context - - if (result) - loadTempAddress(AMD64_RSI, result); - else - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - - loadTempAddress(AMD64_RDX, baseTemp); - amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); - amd64_call_code(_codePtr, __qmljs_construct_value); -} - -void InstructionSelection::checkExceptions() -{ - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R14, offsetof(Context, hasUncaughtException), 4); - amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 1, 1); - _patches[_function->handlersBlock].append(_codePtr); - amd64_branch32(_codePtr, X86_CC_E, 0, 1); -} - -void InstructionSelection::visitExp(IR::Exp *s) -{ - if (IR::Call *c = s->expr->asCall()) { - if (c->base->asName()) { - callActivationProperty(c, 0); - return; - } else if (c->base->asTemp()) { - callValue(c, 0); - return; - } else if (c->base->asMember()) { - callProperty(c, 0); - return; - } - } - Q_UNIMPLEMENTED(); - assert(!"TODO"); -} - -void InstructionSelection::visitEnter(IR::Enter *) -{ - Q_UNIMPLEMENTED(); - assert(!"TODO"); -} - -void InstructionSelection::visitLeave(IR::Leave *) -{ - Q_UNIMPLEMENTED(); - assert(!"TODO"); -} - -void InstructionSelection::visitMove(IR::Move *s) -{ - // %rdi, %rsi, %rdx, %rcx, %r8 and %r9 - if (s->op == IR::OpInvalid) { - if (IR::Name *n = s->target->asName()) { - String *propertyName = identifier(*n->id); - - if (IR::Const *c = s->source->asConst()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); - - switch (c->type) { - case IR::BoolType: - amd64_mov_reg_imm(_codePtr, AMD64_RDX, c->value != 0); - amd64_call_code(_codePtr, __qmljs_set_activation_property_boolean); - break; - - case IR::NumberType: - amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); - amd64_call_code(_codePtr, __qmljs_set_activation_property_number); - break; - - default: - Q_UNIMPLEMENTED(); - assert(!"TODO"); - } - } else if (IR::String *str = s->source->asString()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, _engine->newString(*str->value)); - amd64_call_code(_codePtr, __qmljs_set_activation_property_string); - } else if (IR::Temp *t = s->source->asTemp()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); - loadTempAddress(AMD64_RDX, t); - amd64_call_code(_codePtr, __qmljs_set_activation_property); - } else if (IR::Name *other = s->source->asName()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*other->id)); - amd64_call_code(_codePtr, __qmljs_copy_activation_property); - } else if (IR::Closure *clos = s->source->asClosure()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, clos->value); - amd64_call_code(_codePtr, __qmljs_set_activation_property_closure); - } else { - Q_UNIMPLEMENTED(); - assert(!"TODO"); - } - - checkExceptions(); - return; - } else if (IR::Temp *t = s->target->asTemp()) { - if (IR::Name *n = s->source->asName()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. - amd64_call_code(_codePtr, __qmljs_get_thisObject); - } else { - String *propertyName = identifier(*n->id); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, propertyName); - amd64_call_code(_codePtr, __qmljs_get_activation_property); - checkExceptions(); - } - return; - } else if (IR::Const *c = s->source->asConst()) { - loadTempAddress(AMD64_RSI, t); - - switch (c->type) { - case IR::NullType: - amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NULL_TYPE, 4); - break; - - case IR::UndefinedType: - amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, UNDEFINED_TYPE, 4); - break; - - case IR::BoolType: - amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, BOOLEAN_TYPE, 4); - amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(Value, booleanValue), c->value != 0, 1); - break; - - case IR::NumberType: - amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); - amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NUMBER_TYPE, 4); - amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), AMD64_XMM0); - break; - - default: - Q_UNIMPLEMENTED(); - assert(!"TODO"); - } - return; - } else if (IR::Temp *t2 = s->source->asTemp()) { - loadTempAddress(AMD64_RDI, t); - loadTempAddress(AMD64_RSI, t2); - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, 0, 4); - amd64_mov_membase_reg(_codePtr, AMD64_RDI, 0, AMD64_RAX, 4); - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(Value, numberValue), 8); - amd64_mov_membase_reg(_codePtr, AMD64_RDI, offsetof(Value, numberValue), AMD64_RAX, 8); - return; - } else if (IR::String *str = s->source->asString()) { - loadTempAddress(AMD64_RDI, t); - amd64_mov_reg_imm(_codePtr, AMD64_RSI, _engine->newString(*str->value)); - amd64_call_code(_codePtr, __qmljs_init_string); - return; - } else if (IR::Closure *clos = s->source->asClosure()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, clos->value); - amd64_call_code(_codePtr, __qmljs_init_closure); - return; - } else if (IR::New *ctor = s->source->asNew()) { - if (ctor->base->asName()) { - constructActivationProperty(ctor, t); - return; - } else if (ctor->base->asMember()) { - constructProperty(ctor, t); - return; - } else if (ctor->base->asTemp()) { - constructValue(ctor, t); - return; - } - } else if (IR::Member *m = s->source->asMember()) { - //__qmljs_get_property(ctx, result, object, name); - if (IR::Temp *base = m->base->asTemp()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - loadTempAddress(AMD64_RDX, base); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*m->name)); - amd64_call_code(_codePtr, __qmljs_get_property); - checkExceptions(); - return; - } - assert(!"wip"); - return; - } else if (IR::Subscript *ss = s->source->asSubscript()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - loadTempAddress(AMD64_RDX, ss->base->asTemp()); - loadTempAddress(AMD64_RCX, ss->index->asTemp()); - amd64_call_code(_codePtr, __qmljs_get_element); - checkExceptions(); - return; - } else if (IR::Unop *u = s->source->asUnop()) { - if (IR::Temp *e = u->expr->asTemp()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - loadTempAddress(AMD64_RDX, e); - void (*op)(Context *, Value *, const Value *) = 0; - switch (u->op) { - case IR::OpIfTrue: assert(!"unreachable"); break; - case IR::OpNot: op = __qmljs_not; break; - case IR::OpUMinus: op = __qmljs_uminus; break; - case IR::OpUPlus: op = __qmljs_uplus; break; - case IR::OpCompl: op = __qmljs_compl; break; - default: assert(!"unreachable"); break; - } // switch - amd64_call_code(_codePtr, op); - return; - } else if (IR::Const *c = u->expr->asConst()) { - assert(!"wip"); - return; - } - } else if (IR::Binop *b = s->source->asBinop()) { - IR::Temp *l = b->left->asTemp(); - IR::Temp *r = b->right->asTemp(); - if (l && r) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - loadTempAddress(AMD64_RDX, l); - loadTempAddress(AMD64_RCX, r); - - uchar *label1 = 0, *label2 = 0, *label3 = 0; - - if (b->op == IR::OpMul || b->op == IR::OpAdd || b->op == IR::OpSub || b->op == IR::OpDiv) { - amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RDX, 0, NUMBER_TYPE, 4); - label1 = _codePtr; - amd64_branch8(_codePtr, X86_CC_NE, 0, 0); - amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RCX, 0, NUMBER_TYPE, 4); - label2 = _codePtr; - amd64_branch8(_codePtr, X86_CC_NE, 0, 0); - amd64_movsd_reg_membase(_codePtr, AMD64_XMM0, AMD64_RDX, offsetof(Value, numberValue)); - amd64_movsd_reg_membase(_codePtr, AMD64_XMM1, AMD64_RCX, offsetof(Value, numberValue)); - switch (b->op) { - case IR::OpAdd: - amd64_sse_addsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); - break; - case IR::OpSub: - amd64_sse_subsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); - break; - case IR::OpMul: - amd64_sse_mulsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); - break; - case IR::OpDiv: - amd64_sse_divsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); - break; - default: - Q_UNREACHABLE(); - } // switch - - amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NUMBER_TYPE, 4); - amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), AMD64_XMM0); - label3 = _codePtr; - amd64_jump32(_codePtr, 0); - } - - - if (label1 && label2) { - amd64_patch(label1, _codePtr); - amd64_patch(label2, _codePtr); - } - - void (*op)(Context *, Value *, const Value *, const Value *) = 0; - - switch ((IR::AluOp) b->op) { - case IR::OpInvalid: - case IR::OpIfTrue: - case IR::OpNot: - case IR::OpUMinus: - case IR::OpUPlus: - case IR::OpCompl: - assert(!"unreachable"); - break; - - case IR::OpBitAnd: op = __qmljs_bit_and; break; - case IR::OpBitOr: op = __qmljs_bit_or; break; - case IR::OpBitXor: op = __qmljs_bit_xor; break; - case IR::OpAdd: op = __qmljs_add; break; - case IR::OpSub: op = __qmljs_sub; break; - case IR::OpMul: op = __qmljs_mul; break; - case IR::OpDiv: op = __qmljs_div; break; - case IR::OpMod: op = __qmljs_mod; break; - case IR::OpLShift: op = __qmljs_shl; break; - case IR::OpRShift: op = __qmljs_shr; break; - case IR::OpURShift: op = __qmljs_ushr; break; - case IR::OpGt: op = __qmljs_gt; break; - case IR::OpLt: op = __qmljs_lt; break; - case IR::OpGe: op = __qmljs_ge; break; - case IR::OpLe: op = __qmljs_le; break; - case IR::OpEqual: op = __qmljs_eq; break; - case IR::OpNotEqual: op = __qmljs_ne; break; - case IR::OpStrictEqual: op = __qmljs_se; break; - case IR::OpStrictNotEqual: op = __qmljs_sne; break; - case IR::OpInstanceof: op = __qmljs_instanceof; break; - - case IR::OpIn: - Q_UNIMPLEMENTED(); - assert(!"TODO"); - break; - - case IR::OpAnd: - case IR::OpOr: - assert(!"unreachable"); - break; - } - amd64_call_code(_codePtr, op); - if (label3) - amd64_patch(label3, _codePtr); - return; - } - } else if (IR::Call *c = s->source->asCall()) { - if (c->base->asName()) { - callActivationProperty(c, t); - return; - } else if (c->base->asMember()) { - callProperty(c, t); - return; - } else if (c->base->asTemp()) { - callValue(c, t); - return; - } - } - } else if (IR::Member *m = s->target->asMember()) { - if (IR::Temp *base = m->base->asTemp()) { - if (IR::Const *c = s->source->asConst()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, base); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); - amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); - amd64_call_code(_codePtr, __qmljs_set_property_number); - } else if (IR::String *str = s->source->asString()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, base); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, _engine->newString(*str->value)); - amd64_call_code(_codePtr, __qmljs_set_property_string); - } else if (IR::Temp *t = s->source->asTemp()) { - // __qmljs_set_property(ctx, object, name, value); - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, base); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); - loadTempAddress(AMD64_RCX, t); - amd64_call_code(_codePtr, __qmljs_set_property); - } else if (IR::Closure *clos = s->source->asClosure()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, base); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, clos->value); - amd64_call_code(_codePtr, __qmljs_set_property_closure); - } else { - Q_UNIMPLEMENTED(); - assert(!"TODO"); - } - checkExceptions(); - return; - } - } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (IR::Temp *t2 = s->source->asTemp()) { - loadTempAddress(AMD64_RSI, ss->base->asTemp()); - loadTempAddress(AMD64_RDX, ss->index->asTemp()); - loadTempAddress(AMD64_RCX, t2); - amd64_call_code(_codePtr, __qmljs_set_element); - } else if (IR::Const *c = s->source->asConst()) { - if (c->type == IR::NumberType) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, ss->base->asTemp()); - loadTempAddress(AMD64_RDX, ss->index->asTemp()); - amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); - amd64_call_code(_codePtr, __qmljs_set_element_number); - } else { - Q_UNIMPLEMENTED(); - assert(!"TODO"); - } - } else { - Q_UNIMPLEMENTED(); - assert(!"TODO"); - } - checkExceptions(); - return; - } - } else { - // inplace assignment, e.g. x += 1, ++x, ... - if (IR::Temp *t = s->target->asTemp()) { - if (IR::Const *c = s->source->asConst()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); - - void (*op)(Context *, Value *, double); - switch (s->op) { - case IR::OpBitAnd: op = __qmljs_inplace_bit_and; break; - case IR::OpBitOr: op = __qmljs_inplace_bit_or; break; - case IR::OpBitXor: op = __qmljs_inplace_bit_xor; break; - case IR::OpAdd: op = __qmljs_inplace_add; break; - case IR::OpSub: op = __qmljs_inplace_sub; break; - case IR::OpMul: op = __qmljs_inplace_mul; break; - case IR::OpDiv: op = __qmljs_inplace_div; break; - case IR::OpMod: op = __qmljs_inplace_mod; break; - case IR::OpLShift: op = __qmljs_inplace_shl; break; - case IR::OpRShift: op = __qmljs_inplace_shr; break; - case IR::OpURShift: op = __qmljs_inplace_ushr; break; - default: - Q_UNREACHABLE(); - break; - } - - amd64_call_code(_codePtr, op); - return; - } else if (IR::Temp *t2 = s->source->asTemp()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - amd64_mov_reg_reg(_codePtr, AMD64_RDX, AMD64_RSI, 8); - loadTempAddress(AMD64_RCX, t2); - void (*op)(Context *, Value *, const Value *, const Value *); - switch (s->op) { - case IR::OpBitAnd: op = __qmljs_bit_and; break; - case IR::OpBitOr: op = __qmljs_bit_or; break; - case IR::OpBitXor: op = __qmljs_bit_xor; break; - case IR::OpAdd: op = __qmljs_add; break; - case IR::OpSub: op = __qmljs_sub; break; - case IR::OpMul: op = __qmljs_mul; break; - case IR::OpDiv: op = __qmljs_div; break; - case IR::OpMod: op = __qmljs_mod; break; - case IR::OpLShift: op = __qmljs_shl; break; - case IR::OpRShift: op = __qmljs_shr; break; - case IR::OpURShift: op = __qmljs_ushr; break; - default: - Q_UNREACHABLE(); - break; - } - - amd64_call_code(_codePtr, op); - return; - } - } else if (IR::Name *n = s->target->asName()) { - if (IR::Const *c = s->source->asConst()) { - assert(!"wip"); - return; - } else if (IR::Temp *t = s->source->asTemp()) { - assert(!"wip"); - return; - } - } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (IR::Const *c = s->source->asConst()) { - assert(!"wip"); - return; - } else if (IR::Temp *t = s->source->asTemp()) { - assert(!"wip"); - return; - } - } else if (IR::Member *m = s->target->asMember()) { - if (IR::Const *c = s->source->asConst()) { - assert(!"wip"); - return; - } else if (IR::Temp *t = s->source->asTemp()) { - assert(!"wip"); - return; - } - } - } - - Q_UNIMPLEMENTED(); - s->dump(qout, IR::Stmt::MIR); - qout << endl; - assert(!"TODO"); -} - -void InstructionSelection::visitJump(IR::Jump *s) -{ - if (_block->index + 1 != s->target->index) { - _patches[s->target].append(_codePtr); - amd64_jump32(_codePtr, 0); - } -} - -void InstructionSelection::visitCJump(IR::CJump *s) -{ - if (IR::Temp *t = s->cond->asTemp()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, 0, 4); - amd64_alu_reg_imm(_codePtr, X86_CMP, AMD64_RAX, BOOLEAN_TYPE); - - uchar *label1 = _codePtr; - amd64_branch8(_codePtr, X86_CC_NE, 0, 0); - - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(Value, booleanValue), 1); - - uchar *label2 = _codePtr; - amd64_jump8(_codePtr, 0); - - amd64_patch(label1, _codePtr); - amd64_call_code(_codePtr, __qmljs_to_boolean); - - amd64_patch(label2, _codePtr); - amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 0, 4); - _patches[s->iftrue].append(_codePtr); - amd64_branch32(_codePtr, X86_CC_NZ, 0, 1); - - if (_block->index + 1 != s->iffalse->index) { - _patches[s->iffalse].append(_codePtr); - amd64_jump32(_codePtr, 0); - } - return; - } else if (IR::Binop *b = s->cond->asBinop()) { - IR::Temp *l = b->left->asTemp(); - IR::Temp *r = b->right->asTemp(); - if (l && r) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, l); - loadTempAddress(AMD64_RDX, r); - - // ### TODO: instruction selection for common cases (e.g. number1 < number2) - - bool (*op)(Context *, const Value *, const Value *); - switch (b->op) { - default: Q_UNREACHABLE(); assert(!"todo"); break; - case IR::OpGt: op = __qmljs_cmp_gt; break; - case IR::OpLt: op = __qmljs_cmp_lt; break; - case IR::OpGe: op = __qmljs_cmp_ge; break; - case IR::OpLe: op = __qmljs_cmp_le; break; - case IR::OpEqual: op = __qmljs_cmp_eq; break; - case IR::OpNotEqual: op = __qmljs_cmp_ne; break; - case IR::OpStrictEqual: op = __qmljs_cmp_se; break; - case IR::OpStrictNotEqual: op = __qmljs_cmp_sne; break; - case IR::OpInstanceof: op = __qmljs_cmp_instanceof; break; - case IR::OpIn: op = __qmljs_cmp_in; break; - } // switch - - amd64_call_code(_codePtr, op); - x86_alu_reg_imm(_codePtr, X86_CMP, X86_EAX, 0); - - _patches[s->iftrue].append(_codePtr); - amd64_branch32(_codePtr, X86_CC_NZ, 0, 1); - - if (_block->index + 1 != s->iffalse->index) { - _patches[s->iffalse].append(_codePtr); - amd64_jump32(_codePtr, 0); - } - - return; - } else { - assert(!"wip"); - } - } - - Q_UNIMPLEMENTED(); - assert(!"TODO"); -} - -void InstructionSelection::visitRet(IR::Ret *s) -{ - if (IR::Temp *t = s->expr->asTemp()) { - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_R14, offsetof(Context, result)); - loadTempAddress(AMD64_RSI, t); - amd64_call_code(_codePtr, __qmljs_copy); - return; - } - Q_UNIMPLEMENTED(); - Q_UNUSED(s); -} - diff --git a/qv4isel_p.h b/qv4isel_p.h index d53d028c50..b12f71646a 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -1,54 +1,373 @@ -#ifndef QV4ISEL_P_H -#define QV4ISEL_P_H +#ifndef QV4ISEL_H +#define QV4ISEL_H #include "qv4ir_p.h" -#include "qmljs_objects.h" - -#include namespace QQmlJS { -namespace x86_64 { -class InstructionSelection: protected IR::StmtVisitor +class BaseInstructionSelection: protected IR::StmtVisitor { +protected: + struct DispatchExp: IR::ExprVisitor { + BaseInstructionSelection *_isel; + IR::Exp *_stmt; + + DispatchExp(BaseInstructionSelection *isel) + : _isel(isel), _stmt(0) {} + + void operator()(IR::Exp *s) + { + qSwap(_stmt, s); + _stmt->expr->accept(this); + qSwap(_stmt, s); + } + + virtual void visitConst(IR::Const *) { _isel->genExpConst(_stmt); } + virtual void visitString(IR::String *) { _isel->genExpString(_stmt); } + virtual void visitName(IR::Name *) { _isel->genExpName(_stmt); } + virtual void visitTemp(IR::Temp *) { _isel->genExpTemp(_stmt); } + virtual void visitClosure(IR::Closure *) { _isel->genExpClosure(_stmt); } + virtual void visitUnop(IR::Unop *) { _isel->genExpUnop(_stmt); } + virtual void visitBinop(IR::Binop *) { _isel->genExpBinop(_stmt); } + virtual void visitCall(IR::Call *) { _isel->genExpCall(_stmt); } + virtual void visitNew(IR::New *) { _isel->genExpNew(_stmt); } + virtual void visitSubscript(IR::Subscript *) { _isel->genExpSubscript(_stmt); } + virtual void visitMember(IR::Member *) { _isel->genExpMember(_stmt); } + }; + + struct DispatchRet: IR::ExprVisitor { + BaseInstructionSelection *_isel; + IR::Ret *_stmt; + + DispatchRet(BaseInstructionSelection *isel) + : _isel(isel), _stmt(0) {} + + void operator()(IR::Ret *s) + { + qSwap(_stmt, s); + _stmt->expr->accept(this); + qSwap(_stmt, s); + } + + virtual void visitConst(IR::Const *) { _isel->genRetConst(_stmt); } + virtual void visitString(IR::String *) { _isel->genRetString(_stmt); } + virtual void visitName(IR::Name *) { _isel->genRetName(_stmt); } + virtual void visitTemp(IR::Temp *) { _isel->genRetTemp(_stmt); } + virtual void visitClosure(IR::Closure *) { _isel->genRetClosure(_stmt); } + virtual void visitUnop(IR::Unop *) { _isel->genRetUnop(_stmt); } + virtual void visitBinop(IR::Binop *) { _isel->genRetBinop(_stmt); } + virtual void visitCall(IR::Call *) { _isel->genRetCall(_stmt); } + virtual void visitNew(IR::New *) { _isel->genRetNew(_stmt); } + virtual void visitSubscript(IR::Subscript *) { _isel->genRetSubscript(_stmt); } + virtual void visitMember(IR::Member *) { _isel->genRetMember(_stmt); } + }; + + struct DispatchMove: IR::ExprVisitor { + BaseInstructionSelection *_isel; + IR::Move *_stmt; + + DispatchMove(BaseInstructionSelection *isel) + : _isel(isel), _stmt(0) {} + + void operator()(IR::Move *stmt) + { + qSwap(_stmt, stmt); + _stmt->target->accept(this); + qSwap(_stmt, stmt); + } + + virtual void visitConst(IR::Const *) { Q_UNREACHABLE(); } + virtual void visitString(IR::String *) { Q_UNREACHABLE(); } + virtual void visitName(IR::Name *) { _isel->moveName(_stmt); } + virtual void visitTemp(IR::Temp *) { _isel->moveTemp(_stmt); } + virtual void visitClosure(IR::Closure *) { Q_UNREACHABLE(); } + virtual void visitUnop(IR::Unop *) { Q_UNREACHABLE(); } + virtual void visitBinop(IR::Binop *) { Q_UNREACHABLE(); } + virtual void visitCall(IR::Call *) { Q_UNREACHABLE(); } + virtual void visitNew(IR::New *) { Q_UNREACHABLE(); } + virtual void visitSubscript(IR::Subscript *) { _isel->moveSubscript(_stmt); } + virtual void visitMember(IR::Member *) { _isel->moveMember(_stmt); } + }; + + struct MoveTemp: IR::ExprVisitor { + BaseInstructionSelection *_isel; + IR::Move *_stmt; + + MoveTemp(BaseInstructionSelection *isel) + : _isel(isel), _stmt(0) {} + + void operator()(IR::Move *stmt) + { + qSwap(_stmt, stmt); + _stmt->source->accept(this); + qSwap(_stmt, stmt); + } + + virtual void visitConst(IR::Const *) { _isel->genMoveTempConst(_stmt); } + virtual void visitString(IR::String *) { _isel->genMoveTempString(_stmt); } + virtual void visitName(IR::Name *) { _isel->genMoveTempName(_stmt); } + virtual void visitTemp(IR::Temp *) { _isel->genMoveTempTemp(_stmt); } + virtual void visitClosure(IR::Closure *) { _isel->genMoveTempClosure(_stmt); } + virtual void visitUnop(IR::Unop *) { _isel->genMoveTempUnop(_stmt); } + virtual void visitBinop(IR::Binop *) { _isel->genMoveTempBinop(_stmt); } + virtual void visitCall(IR::Call *) { _isel->genMoveTempCall(_stmt); } + virtual void visitNew(IR::New *) { _isel->genMoveTempNew(_stmt); } + virtual void visitSubscript(IR::Subscript *) { _isel->genMoveTempSubscript(_stmt); } + virtual void visitMember(IR::Member *) { _isel->genMoveTempMember(_stmt); } + }; + + struct MoveName: IR::ExprVisitor { + BaseInstructionSelection *_isel; + IR::Move *_stmt; + + MoveName(BaseInstructionSelection *isel) + : _isel(isel), _stmt(0) {} + + void operator()(IR::Move *stmt) + { + qSwap(_stmt, stmt); + _stmt->source->accept(this); + qSwap(_stmt, stmt); + } + + virtual void visitConst(IR::Const *) { _isel->genMoveNameConst(_stmt); } + virtual void visitString(IR::String *) { _isel->genMoveNameString(_stmt); } + virtual void visitName(IR::Name *) { _isel->genMoveNameName(_stmt); } + virtual void visitTemp(IR::Temp *) { _isel->genMoveNameTemp(_stmt); } + virtual void visitClosure(IR::Closure *) { _isel->genMoveNameClosure(_stmt); } + virtual void visitUnop(IR::Unop *) { _isel->genMoveNameUnop(_stmt); } + virtual void visitBinop(IR::Binop *) { _isel->genMoveNameBinop(_stmt); } + virtual void visitCall(IR::Call *) { _isel->genMoveNameCall(_stmt); } + virtual void visitNew(IR::New *) { _isel->genMoveNameNew(_stmt); } + virtual void visitSubscript(IR::Subscript *) { _isel->genMoveNameSubscript(_stmt); } + virtual void visitMember(IR::Member *) { _isel->genMoveNameMember(_stmt); } + }; + + struct MoveMember: IR::ExprVisitor { + BaseInstructionSelection *_isel; + IR::Move *_stmt; + + MoveMember(BaseInstructionSelection *isel) + : _isel(isel), _stmt(0) {} + + void operator()(IR::Move *stmt) + { + qSwap(_stmt, stmt); + _stmt->source->accept(this); + qSwap(_stmt, stmt); + } + + virtual void visitConst(IR::Const *) { _isel->genMoveMemberConst(_stmt); } + virtual void visitString(IR::String *) { _isel->genMoveMemberString(_stmt); } + virtual void visitName(IR::Name *) { _isel->genMoveMemberName(_stmt); } + virtual void visitTemp(IR::Temp *) { _isel->genMoveMemberTemp(_stmt); } + virtual void visitClosure(IR::Closure *) { _isel->genMoveMemberClosure(_stmt); } + virtual void visitUnop(IR::Unop *) { _isel->genMoveMemberUnop(_stmt); } + virtual void visitBinop(IR::Binop *) { _isel->genMoveMemberBinop(_stmt); } + virtual void visitCall(IR::Call *) { _isel->genMoveMemberCall(_stmt); } + virtual void visitNew(IR::New *) { _isel->genMoveMemberNew(_stmt); } + virtual void visitSubscript(IR::Subscript *) { _isel->genMoveMemberSubscript(_stmt); } + virtual void visitMember(IR::Member *) { _isel->genMoveMemberMember(_stmt); } + }; + + struct MoveSubscript: IR::ExprVisitor { + BaseInstructionSelection *_isel; + IR::Move *_stmt; + + MoveSubscript(BaseInstructionSelection *isel) + : _isel(isel), _stmt(0) {} + + void operator()(IR::Move *stmt) + { + qSwap(_stmt, stmt); + _stmt->source->accept(this); + qSwap(_stmt, stmt); + } + + virtual void visitConst(IR::Const *) { _isel->genMoveSubscriptConst(_stmt); } + virtual void visitString(IR::String *) { _isel->genMoveSubscriptString(_stmt); } + virtual void visitName(IR::Name *) { _isel->genMoveSubscriptName(_stmt); } + virtual void visitTemp(IR::Temp *) { _isel->genMoveSubscriptTemp(_stmt); } + virtual void visitClosure(IR::Closure *) { _isel->genMoveSubscriptClosure(_stmt); } + virtual void visitUnop(IR::Unop *) { _isel->genMoveSubscriptUnop(_stmt); } + virtual void visitBinop(IR::Binop *) { _isel->genMoveSubscriptBinop(_stmt); } + virtual void visitCall(IR::Call *) { _isel->genMoveSubscriptCall(_stmt); } + virtual void visitNew(IR::New *) { _isel->genMoveSubscriptNew(_stmt); } + virtual void visitSubscript(IR::Subscript *) { _isel->genMoveSubscriptSubscript(_stmt); } + virtual void visitMember(IR::Member *) { _isel->genMoveSubscriptMember(_stmt); } + }; + + struct DispatchCJump: IR::ExprVisitor { + BaseInstructionSelection *_isel; + IR::CJump *_stmt; + + DispatchCJump(BaseInstructionSelection *isel) + : _isel(isel), _stmt(0) {} + + void operator()(IR::CJump *s) + { + qSwap(_stmt, s); + _stmt->cond->accept(this); + qSwap(_stmt, s); + } + + virtual void visitConst(IR::Const *) { _isel->genCJumpConst(_stmt); } + virtual void visitString(IR::String *) { _isel->genCJumpString(_stmt); } + virtual void visitName(IR::Name *) { _isel->genCJumpName(_stmt); } + virtual void visitTemp(IR::Temp *) { _isel->genCJumpTemp(_stmt); } + virtual void visitClosure(IR::Closure *) { _isel->genCJumpClosure(_stmt); } + virtual void visitUnop(IR::Unop *) { _isel->genCJumpUnop(_stmt); } + virtual void visitBinop(IR::Binop *) { _isel->genCJumpBinop(_stmt); } + virtual void visitCall(IR::Call *) { _isel->genCJumpCall(_stmt); } + virtual void visitNew(IR::New *) { _isel->genCJumpNew(_stmt); } + virtual void visitSubscript(IR::Subscript *) { _isel->genCJumpSubscript(_stmt); } + virtual void visitMember(IR::Member *) { _isel->genCJumpMember(_stmt); } + }; + + virtual void visitExp(IR::Exp *s) + { + dispatchExp(s); + } + + virtual void visitEnter(IR::Enter *) + { + Q_UNREACHABLE(); + } + + virtual void visitLeave(IR::Leave *) + { + Q_UNREACHABLE(); + } + + virtual void visitMove(IR::Move *s) + { + dispatchMove(s); + } + + virtual void visitJump(IR::Jump *s) + { + genJump(s); + } + + virtual void visitCJump(IR::CJump *s) + { + dispatchCJump(s); + } + + virtual void visitRet(IR::Ret *s) + { + dispatchRet(s); + } + + DispatchExp dispatchExp; + DispatchRet dispatchRet; + DispatchCJump dispatchCJump; + DispatchMove dispatchMove; + MoveTemp moveTemp; + MoveName moveName; + MoveMember moveMember; + MoveSubscript moveSubscript; + public: - InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *code); - ~InstructionSelection(); + BaseInstructionSelection() + : dispatchExp(this) + , dispatchRet(this) + , dispatchCJump(this) + , dispatchMove(this) + , moveTemp(this) + , moveName(this) + , moveMember(this) + , moveSubscript(this) {} - void operator()(IR::Function *function); + void statement(IR::Stmt *s) { s->accept(this); } -protected: - VM::String *identifier(const QString &s); - void loadTempAddress(int reg, IR::Temp *t); - void callActivationProperty(IR::Call *call, IR::Temp *result); - void callProperty(IR::Call *call, IR::Temp *result); - void constructActivationProperty(IR::New *call, IR::Temp *result); - void constructProperty(IR::New *ctor, IR::Temp *result); - void callValue(IR::Call *call, IR::Temp *result); - void constructValue(IR::New *call, IR::Temp *result); - void checkExceptions(); - - virtual void visitExp(IR::Exp *); - virtual void visitEnter(IR::Enter *); - virtual void visitLeave(IR::Leave *); - virtual void visitMove(IR::Move *); - virtual void visitJump(IR::Jump *); - virtual void visitCJump(IR::CJump *); - virtual void visitRet(IR::Ret *); - -private: - VM::ExecutionEngine *_engine; - IR::Module *_module; - IR::Function *_function; - IR::BasicBlock *_block; - uchar *_buffer; - uchar *_code; - uchar *_codePtr; - QHash > _patches; - QHash _addrs; + virtual void genExpConst(IR::Exp *) { Q_UNIMPLEMENTED(); } + virtual void genExpString(IR::Exp *) { Q_UNIMPLEMENTED(); } + virtual void genExpName(IR::Exp *) { Q_UNIMPLEMENTED(); } + virtual void genExpTemp(IR::Exp *) { Q_UNIMPLEMENTED(); } + virtual void genExpClosure(IR::Exp *) { Q_UNIMPLEMENTED(); } + virtual void genExpUnop(IR::Exp *) { Q_UNIMPLEMENTED(); } + virtual void genExpBinop(IR::Exp *) { Q_UNIMPLEMENTED(); } + virtual void genExpCall(IR::Exp *) { Q_UNIMPLEMENTED(); } + virtual void genExpNew(IR::Exp *) { Q_UNIMPLEMENTED(); } + virtual void genExpSubscript(IR::Exp *) { Q_UNIMPLEMENTED(); } + virtual void genExpMember(IR::Exp *) { Q_UNIMPLEMENTED(); } + + virtual void genMoveTempConst(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveTempString(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveTempName(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveTempTemp(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveTempClosure(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveTempUnop(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveTempBinop(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveTempCall(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveTempNew(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveTempSubscript(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveTempMember(IR::Move *) { Q_UNIMPLEMENTED(); } + + virtual void genMoveNameConst(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveNameString(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveNameName(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveNameTemp(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveNameClosure(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveNameUnop(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveNameBinop(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveNameCall(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveNameNew(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveNameSubscript(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveNameMember(IR::Move *) { Q_UNIMPLEMENTED(); } + + virtual void genMoveMemberConst(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveMemberString(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveMemberName(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveMemberTemp(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveMemberClosure(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveMemberUnop(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveMemberBinop(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveMemberCall(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveMemberNew(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveMemberSubscript(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveMemberMember(IR::Move *) { Q_UNIMPLEMENTED(); } + + virtual void genMoveSubscriptConst(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveSubscriptString(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveSubscriptName(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveSubscriptTemp(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveSubscriptClosure(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveSubscriptUnop(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveSubscriptBinop(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveSubscriptCall(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveSubscriptNew(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveSubscriptSubscript(IR::Move *) { Q_UNIMPLEMENTED(); } + virtual void genMoveSubscriptMember(IR::Move *) { Q_UNIMPLEMENTED(); } + + virtual void genJump(IR::Jump *) { Q_UNIMPLEMENTED(); } + + virtual void genCJumpConst(IR::CJump *) { Q_UNIMPLEMENTED(); } + virtual void genCJumpString(IR::CJump *) { Q_UNIMPLEMENTED(); } + virtual void genCJumpName(IR::CJump *) { Q_UNIMPLEMENTED(); } + virtual void genCJumpTemp(IR::CJump *) { Q_UNIMPLEMENTED(); } + virtual void genCJumpClosure(IR::CJump *) { Q_UNIMPLEMENTED(); } + virtual void genCJumpUnop(IR::CJump *) { Q_UNIMPLEMENTED(); } + virtual void genCJumpBinop(IR::CJump *) { Q_UNIMPLEMENTED(); } + virtual void genCJumpCall(IR::CJump *) { Q_UNIMPLEMENTED(); } + virtual void genCJumpNew(IR::CJump *) { Q_UNIMPLEMENTED(); } + virtual void genCJumpSubscript(IR::CJump *) { Q_UNIMPLEMENTED(); } + virtual void genCJumpMember(IR::CJump *) { Q_UNIMPLEMENTED(); } + + virtual void genRetConst(IR::Ret *) { Q_UNIMPLEMENTED(); } + virtual void genRetString(IR::Ret *) { Q_UNIMPLEMENTED(); } + virtual void genRetName(IR::Ret *) { Q_UNIMPLEMENTED(); } + virtual void genRetTemp(IR::Ret *) { Q_UNIMPLEMENTED(); } + virtual void genRetClosure(IR::Ret *) { Q_UNIMPLEMENTED(); } + virtual void genRetUnop(IR::Ret *) { Q_UNIMPLEMENTED(); } + virtual void genRetBinop(IR::Ret *) { Q_UNIMPLEMENTED(); } + virtual void genRetCall(IR::Ret *) { Q_UNIMPLEMENTED(); } + virtual void genRetNew(IR::Ret *) { Q_UNIMPLEMENTED(); } + virtual void genRetSubscript(IR::Ret *) { Q_UNIMPLEMENTED(); } + virtual void genRetMember(IR::Ret *) { Q_UNIMPLEMENTED(); } }; -} // end of namespace x86_64 } // end of namespace QQmlJS -#endif // QV4ISEL_P_H +#endif // QV4ISEL_H diff --git a/qv4isel_x86_64.cpp b/qv4isel_x86_64.cpp new file mode 100644 index 0000000000..02f53aeff4 --- /dev/null +++ b/qv4isel_x86_64.cpp @@ -0,0 +1,971 @@ + +#include "qv4isel_x86_64_p.h" +#include "qmljs_runtime.h" +#include "qmljs_objects.h" + +#define TARGET_AMD64 +#define g_assert assert +typedef quint64 guint64; +typedef qint64 gint64; +typedef uchar guint8; +typedef uint guint32; +typedef void *gpointer; +#include "amd64-codegen.h" + +#include +#include +#include + +#ifndef NO_UDIS86 +# include +#endif + +using namespace QQmlJS; +using namespace QQmlJS::x86_64; +using namespace QQmlJS::VM; + +namespace { +QTextStream qout(stdout, QIODevice::WriteOnly); +} + +static inline void +amd64_patch (unsigned char* code, gpointer target) +{ + guint8 rex = 0; + +#ifdef __native_client_codegen__ + code = amd64_skip_nops (code); +#endif +#if defined(__native_client_codegen__) && defined(__native_client__) + if (nacl_is_code_address (code)) { + /* For tail calls, code is patched after being installed */ + /* but not through the normal "patch callsite" method. */ + unsigned char buf[kNaClAlignment]; + unsigned char *aligned_code = (uintptr_t)code & ~kNaClAlignmentMask; + int ret; + memcpy (buf, aligned_code, kNaClAlignment); + /* Patch a temp buffer of bundle size, */ + /* then install to actual location. */ + amd64_patch (buf + ((uintptr_t)code - (uintptr_t)aligned_code), target); + ret = nacl_dyncode_modify (aligned_code, buf, kNaClAlignment); + g_assert (ret == 0); + return; + } + target = nacl_modify_patch_target (target); +#endif + + /* Skip REX */ + if ((code [0] >= 0x40) && (code [0] <= 0x4f)) { + rex = code [0]; + code += 1; + } + + if ((code [0] & 0xf8) == 0xb8) { + /* amd64_set_reg_template */ + *(guint64*)(code + 1) = (guint64)target; + } + else if ((code [0] == 0x8b) && rex && x86_modrm_mod (code [1]) == 0 && x86_modrm_rm (code [1]) == 5) { + /* mov 0(%rip), %dreg */ + *(guint32*)(code + 2) = (guint32)(guint64)target - 7; + } + else if ((code [0] == 0xff) && (code [1] == 0x15)) { + /* call *(%rip) */ + *(guint32*)(code + 2) = ((guint32)(guint64)target) - 7; + } + else if (code [0] == 0xe8) { + /* call */ + gint64 disp = (guint8*)target - (guint8*)code; + assert (amd64_is_imm32 (disp)); + x86_patch (code, (unsigned char*)target); + } + else + x86_patch (code, (unsigned char*)target); +} +InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *buffer) + : _engine(engine) + , _module(module) + , _function(0) + , _block(0) + , _buffer(buffer) + , _code(buffer) + , _codePtr(buffer) +{ +} + +InstructionSelection::~InstructionSelection() +{ +} + +void InstructionSelection::operator()(IR::Function *function) +{ + qSwap(_function, function); + + _code = _codePtr; + _code = (uchar *) ((size_t(_code) + 15) & ~15); + _function->code = (void (*)(VM::Context *)) _code; + _codePtr = _code; + + int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) * sizeof(Value); + locals = (locals + 15) & ~15; + + amd64_push_reg(_codePtr, AMD64_RBP); + amd64_push_reg(_codePtr, AMD64_R14); + amd64_push_reg(_codePtr, AMD64_R15); + + amd64_mov_reg_reg(_codePtr, AMD64_RBP, AMD64_RSP, 8); + amd64_mov_reg_reg(_codePtr, AMD64_R14, AMD64_RDI, 8); + amd64_alu_reg_imm(_codePtr, X86_SUB, AMD64_RSP, locals); + + amd64_mov_reg_membase(_codePtr, AMD64_R15, AMD64_R14, offsetof(Context, locals), 8); + + foreach (IR::BasicBlock *block, _function->basicBlocks) { + _block = block; + _addrs[block] = _codePtr; + foreach (IR::Stmt *s, block->statements) { + s->accept(this); + } + } + + QHashIterator > it(_patches); + while (it.hasNext()) { + it.next(); + uchar *target = _addrs[it.key()]; + foreach (uchar *instr, it.value()) { + amd64_patch(instr, target); + } + } + + amd64_alu_reg_imm(_codePtr, X86_ADD, AMD64_RSP, locals); + amd64_pop_reg(_codePtr, AMD64_R15); + amd64_pop_reg(_codePtr, AMD64_R14); + amd64_pop_reg(_codePtr, AMD64_RBP); + amd64_ret(_codePtr); + +#ifndef NO_UDIS86 + static bool showCode = !qgetenv("SHOW_CODE").isNull(); + if (showCode) { + printf("code size: %ld bytes\n", (_codePtr - _code)); + ud_t ud_obj; + + ud_init(&ud_obj); + ud_set_input_buffer(&ud_obj, _code, _codePtr - _code); + ud_set_mode(&ud_obj, 64); + ud_set_syntax(&ud_obj, UD_SYN_ATT); + + while (ud_disassemble(&ud_obj)) { + printf("\t%s\n", ud_insn_asm(&ud_obj)); + } + } +#endif + qSwap(_function, _function); +} + +String *InstructionSelection::identifier(const QString &s) +{ + return _engine->identifier(s); +} + +void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) +{ + if (t->index < 0) { + const int arg = -t->index - 1; + amd64_mov_reg_membase(_codePtr, reg, AMD64_R14, offsetof(Context, arguments), 8); + amd64_lea_membase(_codePtr, reg, reg, sizeof(Value) * arg); + } else if (t->index < _function->locals.size()) { + amd64_lea_membase(_codePtr, reg, AMD64_R15, sizeof(Value) * t->index); + } else { + amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * (_function->maxNumberOfArguments + t->index - _function->locals.size())); + } +} + +void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) +{ + IR::Name *baseName = call->base->asName(); + assert(baseName != 0); + + int argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) { + ++argc; + } + + int i = 0; + for (IR::ExprList *it = call->args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); + loadTempAddress(AMD64_RSI, arg); + amd64_call_code(_codePtr, __qmljs_copy); + } + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context + + if (result) + loadTempAddress(AMD64_RSI, result); + else + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + + if (baseName->id) { + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id)); + amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); + amd64_call_code(_codePtr, __qmljs_call_activation_property); + } else { + switch (baseName->builtin) { + case IR::Name::builtin_invalid: + Q_UNREACHABLE(); + break; + case IR::Name::builtin_typeof: + amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); + amd64_call_code(_codePtr, __qmljs_builtin_typeof); + break; + case IR::Name::builtin_throw: + amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); + amd64_call_code(_codePtr, __qmljs_builtin_throw); + break; + case IR::Name::builtin_rethrow: + amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); + amd64_call_code(_codePtr, __qmljs_builtin_rethrow); + return; // we need to return to avoid checking the exceptions + } + } + + checkExceptions(); +} + + +void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) +{ + IR::Temp *baseTemp = call->base->asTemp(); + assert(baseTemp != 0); + + int argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) { + ++argc; + } + + int i = 0; + for (IR::ExprList *it = call->args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); + loadTempAddress(AMD64_RSI, arg); + amd64_call_code(_codePtr, __qmljs_copy); + } + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context + + if (result) + loadTempAddress(AMD64_RSI, result); + else + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RDX, AMD64_RDX); + loadTempAddress(AMD64_RCX, baseTemp); + amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); + amd64_call_code(_codePtr, __qmljs_call_value); + + checkExceptions(); +} + +void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) +{ + IR::Member *member = call->base->asMember(); + assert(member != 0); + assert(member->base->asTemp()); + + int argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) { + ++argc; + } + + int i = 0; + for (IR::ExprList *it = call->args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); + loadTempAddress(AMD64_RSI, arg); + amd64_call_code(_codePtr, __qmljs_copy); + } + + //__qmljs_call_property(ctx, result, base, name, args, argc); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context + + if (result) + loadTempAddress(AMD64_RSI, result); + else + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + + loadTempAddress(AMD64_RDX, member->base->asTemp()); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name)); + amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); + amd64_call_code(_codePtr, __qmljs_call_property); + + checkExceptions(); +} + +void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) +{ + IR::Name *baseName = call->base->asName(); + assert(baseName != 0); + + int argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) { + ++argc; + } + + int i = 0; + for (IR::ExprList *it = call->args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); + loadTempAddress(AMD64_RSI, arg); + amd64_call_code(_codePtr, __qmljs_copy); + } + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context + + if (result) + loadTempAddress(AMD64_RSI, result); + else + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id)); + amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); + amd64_call_code(_codePtr, __qmljs_construct_activation_property); + + checkExceptions(); +} + +void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) +{ + IR::Member *member = call->base->asMember(); + assert(member != 0); + assert(member->base->asTemp()); + + int argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) { + ++argc; + } + + int i = 0; + for (IR::ExprList *it = call->args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); + loadTempAddress(AMD64_RSI, arg); + amd64_call_code(_codePtr, __qmljs_copy); + } + + //__qmljs_call_property(ctx, result, base, name, args, argc); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context + + if (result) + loadTempAddress(AMD64_RSI, result); + else + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + + loadTempAddress(AMD64_RDX, member->base->asTemp()); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name)); + amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); + amd64_call_code(_codePtr, __qmljs_construct_property); + + checkExceptions(); +} + +void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) +{ + IR::Temp *baseTemp = call->base->asTemp(); + assert(baseTemp != 0); + + int argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) { + ++argc; + } + + int i = 0; + for (IR::ExprList *it = call->args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); + loadTempAddress(AMD64_RSI, arg); + amd64_call_code(_codePtr, __qmljs_copy); + } + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context + + if (result) + loadTempAddress(AMD64_RSI, result); + else + amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); + + loadTempAddress(AMD64_RDX, baseTemp); + amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); + amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); + amd64_call_code(_codePtr, __qmljs_construct_value); +} + +void InstructionSelection::checkExceptions() +{ + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R14, offsetof(Context, hasUncaughtException), 4); + amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 1, 1); + _patches[_function->handlersBlock].append(_codePtr); + amd64_branch32(_codePtr, X86_CC_E, 0, 1); +} + +void InstructionSelection::visitExp(IR::Exp *s) +{ + if (IR::Call *c = s->expr->asCall()) { + if (c->base->asName()) { + callActivationProperty(c, 0); + return; + } else if (c->base->asTemp()) { + callValue(c, 0); + return; + } else if (c->base->asMember()) { + callProperty(c, 0); + return; + } + } + Q_UNIMPLEMENTED(); + assert(!"TODO"); +} + +void InstructionSelection::visitEnter(IR::Enter *) +{ + Q_UNIMPLEMENTED(); + assert(!"TODO"); +} + +void InstructionSelection::visitLeave(IR::Leave *) +{ + Q_UNIMPLEMENTED(); + assert(!"TODO"); +} + +void InstructionSelection::visitMove(IR::Move *s) +{ + // %rdi, %rsi, %rdx, %rcx, %r8 and %r9 + if (s->op == IR::OpInvalid) { + if (IR::Name *n = s->target->asName()) { + String *propertyName = identifier(*n->id); + + if (IR::Const *c = s->source->asConst()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); + + switch (c->type) { + case IR::BoolType: + amd64_mov_reg_imm(_codePtr, AMD64_RDX, c->value != 0); + amd64_call_code(_codePtr, __qmljs_set_activation_property_boolean); + break; + + case IR::NumberType: + amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); + amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); + amd64_call_code(_codePtr, __qmljs_set_activation_property_number); + break; + + default: + Q_UNIMPLEMENTED(); + assert(!"TODO"); + } + } else if (IR::String *str = s->source->asString()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, _engine->newString(*str->value)); + amd64_call_code(_codePtr, __qmljs_set_activation_property_string); + } else if (IR::Temp *t = s->source->asTemp()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); + loadTempAddress(AMD64_RDX, t); + amd64_call_code(_codePtr, __qmljs_set_activation_property); + } else if (IR::Name *other = s->source->asName()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*other->id)); + amd64_call_code(_codePtr, __qmljs_copy_activation_property); + } else if (IR::Closure *clos = s->source->asClosure()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, clos->value); + amd64_call_code(_codePtr, __qmljs_set_activation_property_closure); + } else { + Q_UNIMPLEMENTED(); + assert(!"TODO"); + } + + checkExceptions(); + return; + } else if (IR::Temp *t = s->target->asTemp()) { + if (IR::Name *n = s->source->asName()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. + amd64_call_code(_codePtr, __qmljs_get_thisObject); + } else { + String *propertyName = identifier(*n->id); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, propertyName); + amd64_call_code(_codePtr, __qmljs_get_activation_property); + checkExceptions(); + } + return; + } else if (IR::Const *c = s->source->asConst()) { + loadTempAddress(AMD64_RSI, t); + + switch (c->type) { + case IR::NullType: + amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NULL_TYPE, 4); + break; + + case IR::UndefinedType: + amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, UNDEFINED_TYPE, 4); + break; + + case IR::BoolType: + amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, BOOLEAN_TYPE, 4); + amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(Value, booleanValue), c->value != 0, 1); + break; + + case IR::NumberType: + amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); + amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); + amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NUMBER_TYPE, 4); + amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), AMD64_XMM0); + break; + + default: + Q_UNIMPLEMENTED(); + assert(!"TODO"); + } + return; + } else if (IR::Temp *t2 = s->source->asTemp()) { + loadTempAddress(AMD64_RDI, t); + loadTempAddress(AMD64_RSI, t2); + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, 0, 4); + amd64_mov_membase_reg(_codePtr, AMD64_RDI, 0, AMD64_RAX, 4); + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(Value, numberValue), 8); + amd64_mov_membase_reg(_codePtr, AMD64_RDI, offsetof(Value, numberValue), AMD64_RAX, 8); + return; + } else if (IR::String *str = s->source->asString()) { + loadTempAddress(AMD64_RDI, t); + amd64_mov_reg_imm(_codePtr, AMD64_RSI, _engine->newString(*str->value)); + amd64_call_code(_codePtr, __qmljs_init_string); + return; + } else if (IR::Closure *clos = s->source->asClosure()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, clos->value); + amd64_call_code(_codePtr, __qmljs_init_closure); + return; + } else if (IR::New *ctor = s->source->asNew()) { + if (ctor->base->asName()) { + constructActivationProperty(ctor, t); + return; + } else if (ctor->base->asMember()) { + constructProperty(ctor, t); + return; + } else if (ctor->base->asTemp()) { + constructValue(ctor, t); + return; + } + } else if (IR::Member *m = s->source->asMember()) { + //__qmljs_get_property(ctx, result, object, name); + if (IR::Temp *base = m->base->asTemp()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + loadTempAddress(AMD64_RDX, base); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*m->name)); + amd64_call_code(_codePtr, __qmljs_get_property); + checkExceptions(); + return; + } + assert(!"wip"); + return; + } else if (IR::Subscript *ss = s->source->asSubscript()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + loadTempAddress(AMD64_RDX, ss->base->asTemp()); + loadTempAddress(AMD64_RCX, ss->index->asTemp()); + amd64_call_code(_codePtr, __qmljs_get_element); + checkExceptions(); + return; + } else if (IR::Unop *u = s->source->asUnop()) { + if (IR::Temp *e = u->expr->asTemp()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + loadTempAddress(AMD64_RDX, e); + void (*op)(Context *, Value *, const Value *) = 0; + switch (u->op) { + case IR::OpIfTrue: assert(!"unreachable"); break; + case IR::OpNot: op = __qmljs_not; break; + case IR::OpUMinus: op = __qmljs_uminus; break; + case IR::OpUPlus: op = __qmljs_uplus; break; + case IR::OpCompl: op = __qmljs_compl; break; + default: assert(!"unreachable"); break; + } // switch + amd64_call_code(_codePtr, op); + return; + } else if (IR::Const *c = u->expr->asConst()) { + assert(!"wip"); + return; + } + } else if (IR::Binop *b = s->source->asBinop()) { + IR::Temp *l = b->left->asTemp(); + IR::Temp *r = b->right->asTemp(); + if (l && r) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + loadTempAddress(AMD64_RDX, l); + loadTempAddress(AMD64_RCX, r); + + uchar *label1 = 0, *label2 = 0, *label3 = 0; + + if (b->op == IR::OpMul || b->op == IR::OpAdd || b->op == IR::OpSub || b->op == IR::OpDiv) { + amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RDX, 0, NUMBER_TYPE, 4); + label1 = _codePtr; + amd64_branch8(_codePtr, X86_CC_NE, 0, 0); + amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RCX, 0, NUMBER_TYPE, 4); + label2 = _codePtr; + amd64_branch8(_codePtr, X86_CC_NE, 0, 0); + amd64_movsd_reg_membase(_codePtr, AMD64_XMM0, AMD64_RDX, offsetof(Value, numberValue)); + amd64_movsd_reg_membase(_codePtr, AMD64_XMM1, AMD64_RCX, offsetof(Value, numberValue)); + switch (b->op) { + case IR::OpAdd: + amd64_sse_addsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); + break; + case IR::OpSub: + amd64_sse_subsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); + break; + case IR::OpMul: + amd64_sse_mulsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); + break; + case IR::OpDiv: + amd64_sse_divsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); + break; + default: + Q_UNREACHABLE(); + } // switch + + amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NUMBER_TYPE, 4); + amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), AMD64_XMM0); + label3 = _codePtr; + amd64_jump32(_codePtr, 0); + } + + + if (label1 && label2) { + amd64_patch(label1, _codePtr); + amd64_patch(label2, _codePtr); + } + + void (*op)(Context *, Value *, const Value *, const Value *) = 0; + + switch ((IR::AluOp) b->op) { + case IR::OpInvalid: + case IR::OpIfTrue: + case IR::OpNot: + case IR::OpUMinus: + case IR::OpUPlus: + case IR::OpCompl: + assert(!"unreachable"); + break; + + case IR::OpBitAnd: op = __qmljs_bit_and; break; + case IR::OpBitOr: op = __qmljs_bit_or; break; + case IR::OpBitXor: op = __qmljs_bit_xor; break; + case IR::OpAdd: op = __qmljs_add; break; + case IR::OpSub: op = __qmljs_sub; break; + case IR::OpMul: op = __qmljs_mul; break; + case IR::OpDiv: op = __qmljs_div; break; + case IR::OpMod: op = __qmljs_mod; break; + case IR::OpLShift: op = __qmljs_shl; break; + case IR::OpRShift: op = __qmljs_shr; break; + case IR::OpURShift: op = __qmljs_ushr; break; + case IR::OpGt: op = __qmljs_gt; break; + case IR::OpLt: op = __qmljs_lt; break; + case IR::OpGe: op = __qmljs_ge; break; + case IR::OpLe: op = __qmljs_le; break; + case IR::OpEqual: op = __qmljs_eq; break; + case IR::OpNotEqual: op = __qmljs_ne; break; + case IR::OpStrictEqual: op = __qmljs_se; break; + case IR::OpStrictNotEqual: op = __qmljs_sne; break; + case IR::OpInstanceof: op = __qmljs_instanceof; break; + + case IR::OpIn: + Q_UNIMPLEMENTED(); + assert(!"TODO"); + break; + + case IR::OpAnd: + case IR::OpOr: + assert(!"unreachable"); + break; + } + amd64_call_code(_codePtr, op); + if (label3) + amd64_patch(label3, _codePtr); + return; + } + } else if (IR::Call *c = s->source->asCall()) { + if (c->base->asName()) { + callActivationProperty(c, t); + return; + } else if (c->base->asMember()) { + callProperty(c, t); + return; + } else if (c->base->asTemp()) { + callValue(c, t); + return; + } + } + } else if (IR::Member *m = s->target->asMember()) { + if (IR::Temp *base = m->base->asTemp()) { + if (IR::Const *c = s->source->asConst()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, base); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); + amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); + amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); + amd64_call_code(_codePtr, __qmljs_set_property_number); + } else if (IR::String *str = s->source->asString()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, base); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, _engine->newString(*str->value)); + amd64_call_code(_codePtr, __qmljs_set_property_string); + } else if (IR::Temp *t = s->source->asTemp()) { + // __qmljs_set_property(ctx, object, name, value); + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, base); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); + loadTempAddress(AMD64_RCX, t); + amd64_call_code(_codePtr, __qmljs_set_property); + } else if (IR::Closure *clos = s->source->asClosure()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, base); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); + amd64_mov_reg_imm(_codePtr, AMD64_RCX, clos->value); + amd64_call_code(_codePtr, __qmljs_set_property_closure); + } else { + Q_UNIMPLEMENTED(); + assert(!"TODO"); + } + checkExceptions(); + return; + } + } else if (IR::Subscript *ss = s->target->asSubscript()) { + if (IR::Temp *t2 = s->source->asTemp()) { + loadTempAddress(AMD64_RSI, ss->base->asTemp()); + loadTempAddress(AMD64_RDX, ss->index->asTemp()); + loadTempAddress(AMD64_RCX, t2); + amd64_call_code(_codePtr, __qmljs_set_element); + } else if (IR::Const *c = s->source->asConst()) { + if (c->type == IR::NumberType) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, ss->base->asTemp()); + loadTempAddress(AMD64_RDX, ss->index->asTemp()); + amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); + amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); + amd64_call_code(_codePtr, __qmljs_set_element_number); + } else { + Q_UNIMPLEMENTED(); + assert(!"TODO"); + } + } else { + Q_UNIMPLEMENTED(); + assert(!"TODO"); + } + checkExceptions(); + return; + } + } else { + // inplace assignment, e.g. x += 1, ++x, ... + if (IR::Temp *t = s->target->asTemp()) { + if (IR::Const *c = s->source->asConst()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); + amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); + + void (*op)(Context *, Value *, double); + switch (s->op) { + case IR::OpBitAnd: op = __qmljs_inplace_bit_and; break; + case IR::OpBitOr: op = __qmljs_inplace_bit_or; break; + case IR::OpBitXor: op = __qmljs_inplace_bit_xor; break; + case IR::OpAdd: op = __qmljs_inplace_add; break; + case IR::OpSub: op = __qmljs_inplace_sub; break; + case IR::OpMul: op = __qmljs_inplace_mul; break; + case IR::OpDiv: op = __qmljs_inplace_div; break; + case IR::OpMod: op = __qmljs_inplace_mod; break; + case IR::OpLShift: op = __qmljs_inplace_shl; break; + case IR::OpRShift: op = __qmljs_inplace_shr; break; + case IR::OpURShift: op = __qmljs_inplace_ushr; break; + default: + Q_UNREACHABLE(); + break; + } + + amd64_call_code(_codePtr, op); + return; + } else if (IR::Temp *t2 = s->source->asTemp()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + amd64_mov_reg_reg(_codePtr, AMD64_RDX, AMD64_RSI, 8); + loadTempAddress(AMD64_RCX, t2); + void (*op)(Context *, Value *, const Value *, const Value *); + switch (s->op) { + case IR::OpBitAnd: op = __qmljs_bit_and; break; + case IR::OpBitOr: op = __qmljs_bit_or; break; + case IR::OpBitXor: op = __qmljs_bit_xor; break; + case IR::OpAdd: op = __qmljs_add; break; + case IR::OpSub: op = __qmljs_sub; break; + case IR::OpMul: op = __qmljs_mul; break; + case IR::OpDiv: op = __qmljs_div; break; + case IR::OpMod: op = __qmljs_mod; break; + case IR::OpLShift: op = __qmljs_shl; break; + case IR::OpRShift: op = __qmljs_shr; break; + case IR::OpURShift: op = __qmljs_ushr; break; + default: + Q_UNREACHABLE(); + break; + } + + amd64_call_code(_codePtr, op); + return; + } + } else if (IR::Name *n = s->target->asName()) { + if (IR::Const *c = s->source->asConst()) { + assert(!"wip"); + return; + } else if (IR::Temp *t = s->source->asTemp()) { + assert(!"wip"); + return; + } + } else if (IR::Subscript *ss = s->target->asSubscript()) { + if (IR::Const *c = s->source->asConst()) { + assert(!"wip"); + return; + } else if (IR::Temp *t = s->source->asTemp()) { + assert(!"wip"); + return; + } + } else if (IR::Member *m = s->target->asMember()) { + if (IR::Const *c = s->source->asConst()) { + assert(!"wip"); + return; + } else if (IR::Temp *t = s->source->asTemp()) { + assert(!"wip"); + return; + } + } + } + + Q_UNIMPLEMENTED(); + s->dump(qout, IR::Stmt::MIR); + qout << endl; + assert(!"TODO"); +} + +void InstructionSelection::visitJump(IR::Jump *s) +{ + if (_block->index + 1 != s->target->index) { + _patches[s->target].append(_codePtr); + amd64_jump32(_codePtr, 0); + } +} + +void InstructionSelection::visitCJump(IR::CJump *s) +{ + if (IR::Temp *t = s->cond->asTemp()) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, t); + + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, 0, 4); + amd64_alu_reg_imm(_codePtr, X86_CMP, AMD64_RAX, BOOLEAN_TYPE); + + uchar *label1 = _codePtr; + amd64_branch8(_codePtr, X86_CC_NE, 0, 0); + + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(Value, booleanValue), 1); + + uchar *label2 = _codePtr; + amd64_jump8(_codePtr, 0); + + amd64_patch(label1, _codePtr); + amd64_call_code(_codePtr, __qmljs_to_boolean); + + amd64_patch(label2, _codePtr); + amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 0, 4); + _patches[s->iftrue].append(_codePtr); + amd64_branch32(_codePtr, X86_CC_NZ, 0, 1); + + if (_block->index + 1 != s->iffalse->index) { + _patches[s->iffalse].append(_codePtr); + amd64_jump32(_codePtr, 0); + } + return; + } else if (IR::Binop *b = s->cond->asBinop()) { + IR::Temp *l = b->left->asTemp(); + IR::Temp *r = b->right->asTemp(); + if (l && r) { + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, l); + loadTempAddress(AMD64_RDX, r); + + // ### TODO: instruction selection for common cases (e.g. number1 < number2) + + bool (*op)(Context *, const Value *, const Value *); + switch (b->op) { + default: Q_UNREACHABLE(); assert(!"todo"); break; + case IR::OpGt: op = __qmljs_cmp_gt; break; + case IR::OpLt: op = __qmljs_cmp_lt; break; + case IR::OpGe: op = __qmljs_cmp_ge; break; + case IR::OpLe: op = __qmljs_cmp_le; break; + case IR::OpEqual: op = __qmljs_cmp_eq; break; + case IR::OpNotEqual: op = __qmljs_cmp_ne; break; + case IR::OpStrictEqual: op = __qmljs_cmp_se; break; + case IR::OpStrictNotEqual: op = __qmljs_cmp_sne; break; + case IR::OpInstanceof: op = __qmljs_cmp_instanceof; break; + case IR::OpIn: op = __qmljs_cmp_in; break; + } // switch + + amd64_call_code(_codePtr, op); + x86_alu_reg_imm(_codePtr, X86_CMP, X86_EAX, 0); + + _patches[s->iftrue].append(_codePtr); + amd64_branch32(_codePtr, X86_CC_NZ, 0, 1); + + if (_block->index + 1 != s->iffalse->index) { + _patches[s->iffalse].append(_codePtr); + amd64_jump32(_codePtr, 0); + } + + return; + } else { + assert(!"wip"); + } + } + + Q_UNIMPLEMENTED(); + assert(!"TODO"); +} + +void InstructionSelection::visitRet(IR::Ret *s) +{ + if (IR::Temp *t = s->expr->asTemp()) { + amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_R14, offsetof(Context, result)); + loadTempAddress(AMD64_RSI, t); + amd64_call_code(_codePtr, __qmljs_copy); + return; + } + Q_UNIMPLEMENTED(); + Q_UNUSED(s); +} + diff --git a/qv4isel_x86_64_p.h b/qv4isel_x86_64_p.h new file mode 100644 index 0000000000..d53d028c50 --- /dev/null +++ b/qv4isel_x86_64_p.h @@ -0,0 +1,54 @@ +#ifndef QV4ISEL_P_H +#define QV4ISEL_P_H + +#include "qv4ir_p.h" +#include "qmljs_objects.h" + +#include + +namespace QQmlJS { +namespace x86_64 { + +class InstructionSelection: protected IR::StmtVisitor +{ +public: + InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *code); + ~InstructionSelection(); + + void operator()(IR::Function *function); + +protected: + VM::String *identifier(const QString &s); + void loadTempAddress(int reg, IR::Temp *t); + void callActivationProperty(IR::Call *call, IR::Temp *result); + void callProperty(IR::Call *call, IR::Temp *result); + void constructActivationProperty(IR::New *call, IR::Temp *result); + void constructProperty(IR::New *ctor, IR::Temp *result); + void callValue(IR::Call *call, IR::Temp *result); + void constructValue(IR::New *call, IR::Temp *result); + void checkExceptions(); + + virtual void visitExp(IR::Exp *); + virtual void visitEnter(IR::Enter *); + virtual void visitLeave(IR::Leave *); + virtual void visitMove(IR::Move *); + virtual void visitJump(IR::Jump *); + virtual void visitCJump(IR::CJump *); + virtual void visitRet(IR::Ret *); + +private: + VM::ExecutionEngine *_engine; + IR::Module *_module; + IR::Function *_function; + IR::BasicBlock *_block; + uchar *_buffer; + uchar *_code; + uchar *_codePtr; + QHash > _patches; + QHash _addrs; +}; + +} // end of namespace x86_64 +} // end of namespace QQmlJS + +#endif // QV4ISEL_P_H diff --git a/v4.pro b/v4.pro index 4198533ae3..036043d43b 100644 --- a/v4.pro +++ b/v4.pro @@ -15,7 +15,8 @@ SOURCES += main.cpp \ qv4isel.cpp \ qv4syntaxchecker.cpp \ qv4ecmaobjects.cpp \ - qv4array.cpp + qv4array.cpp \ + qv4isel_x86_64.cpp HEADERS += \ qv4codegen_p.h \ @@ -27,8 +28,8 @@ HEADERS += \ amd64-codegen.h \ qv4syntaxchecker_p.h \ qv4ecmaobjects_p.h \ - qv4array_p.h - + qv4array_p.h \ + qv4isel_x86_64_p.h -- cgit v1.2.3 From 4cd31221ec82c573c85426e6cbc186ab9dca402c Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Thu, 31 May 2012 19:02:05 +0200 Subject: Initial work on the LLVM-based AOT compiler. --- main.cpp | 12 +++ qv4isel_llvm.cpp | 232 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4isel_llvm_p.h | 63 +++++++++++++++ v4.pro | 23 +++++- 4 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 qv4isel_llvm.cpp create mode 100644 qv4isel_llvm_p.h diff --git a/main.cpp b/main.cpp index 87c9095bb8..3c4b353bdd 100644 --- a/main.cpp +++ b/main.cpp @@ -15,6 +15,10 @@ #include #include +#ifdef WITH_LLVM +# include "qv4isel_llvm_p.h" +#endif + static inline bool protect(const void *addr, size_t size) { size_t pageSize = sysconf(_SC_PAGESIZE); @@ -82,6 +86,14 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS foreach (IR::Function *function, module.functions) { isel(function); } + +#ifdef WITH_LLVM + LLVMInstructionSelection llvmIsel(llvm::getGlobalContext()); + if (llvm::Module *llvmModule = llvmIsel.getLLVMModule(&module)) { + llvmModule->dump(); + delete llvmModule; + } +#endif } } diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp new file mode 100644 index 0000000000..982b2db58f --- /dev/null +++ b/qv4isel_llvm.cpp @@ -0,0 +1,232 @@ + +#include "qv4isel_llvm_p.h" +#include "qv4ir_p.h" + +using namespace QQmlJS; + +LLVMInstructionSelection::LLVMInstructionSelection(llvm::LLVMContext &context) + : llvm::IRBuilder<>(context) + , _llvmModule(0) + , _llvmFunction(0) + , _llvmValue(0) + , _numberTy(0) + , _valueTy(0) + , _contextTy(0) + , _functionTy(0) + , _function(0) + , _block(0) +{ + _numberTy = getDoubleTy(); + + { + llvm::StructType *ty = llvm::StructType::create(getContext(), llvm::StringRef("Value")); + ty->setBody(getInt32Ty(), llvm::StructType::get(_numberTy, NULL), NULL); + _valueTy = ty; + } + + { + // ### we use a pointer for now + _contextTy = getInt1Ty()->getPointerTo(); + } + + { + llvm::Type *args[] = { _contextTy }; + _functionTy = llvm::FunctionType::get(getVoidTy(), llvm::makeArrayRef(args), false); + } +} + +llvm::Module *LLVMInstructionSelection::getLLVMModule(IR::Module *module) +{ + llvm::Module *llvmModule = new llvm::Module("a.out", getContext()); + qSwap(_llvmModule, llvmModule); + foreach (IR::Function *function, module->functions) + (void) getLLVMFunction(function); + qSwap(_llvmModule, llvmModule); + return llvmModule; +} + +llvm::Function *LLVMInstructionSelection::getLLVMFunction(IR::Function *function) +{ + llvm::Function *llvmFunction = + llvm::Function::Create(_functionTy, llvm::Function::InternalLinkage, + llvm::Twine(function->name ? qPrintable(*function->name) : 0), _llvmModule); + + QHash blockMap; + QVector tempMap; + + qSwap(_llvmFunction, llvmFunction); + qSwap(_function, function); + qSwap(_tempMap, tempMap); + qSwap(_blockMap, blockMap); + + // entry block + SetInsertPoint(getLLVMBasicBlock(_function->basicBlocks.first())); + for (int i = 0; i < _function->tempCount; ++i) { + llvm::AllocaInst *t = CreateAlloca(_valueTy, 0, llvm::StringRef("t")); + _tempMap.append(t); + } + + foreach (llvm::Value *t, _tempMap) { + CreateStore(llvm::Constant::getNullValue(_valueTy), t); + } + + foreach (IR::BasicBlock *block, _function->basicBlocks) { + qSwap(_block, block); + SetInsertPoint(getLLVMBasicBlock(_block)); + foreach (IR::Stmt *s, _block->statements) + s->accept(this); + qSwap(_block, block); + } + + qSwap(_blockMap, blockMap); + qSwap(_tempMap, tempMap); + qSwap(_function, function); + qSwap(_llvmFunction, llvmFunction); + return llvmFunction; +} + +llvm::BasicBlock *LLVMInstructionSelection::getLLVMBasicBlock(IR::BasicBlock *block) +{ + llvm::BasicBlock *&llvmBlock = _blockMap[block]; + if (! llvmBlock) + llvmBlock = llvm::BasicBlock::Create(getContext(), llvm::Twine(), + _llvmFunction); + return llvmBlock; +} + +llvm::Value *LLVMInstructionSelection::getLLVMValue(IR::Expr *expr) +{ + llvm::Value *llvmValue = 0; + if (expr) { + qSwap(_llvmValue, llvmValue); + expr->accept(this); + qSwap(_llvmValue, llvmValue); + } + if (! llvmValue) { + Q_UNIMPLEMENTED(); + llvmValue = llvm::Constant::getNullValue(_valueTy); + } + return llvmValue; +} + +llvm::Value *LLVMInstructionSelection::getLLVMCondition(IR::Expr *expr) +{ + if (llvm::Value *value = getLLVMValue(expr)) { + if (value->getType() == getInt1Ty()) { + return value; + } + } + + Q_UNIMPLEMENTED(); + return getInt1(false); +} + +llvm::Value *LLVMInstructionSelection::getLLVMTemp(IR::Temp *temp) +{ + if (temp->index < 0) { + // it's an actual argument + Q_UNIMPLEMENTED(); + return 0; + } + + return _tempMap[temp->index]; +} + +void LLVMInstructionSelection::visitExp(IR::Exp *s) +{ + getLLVMValue(s->expr); +} + +void LLVMInstructionSelection::visitEnter(IR::Enter *) +{ +} + +void LLVMInstructionSelection::visitLeave(IR::Leave *) +{ +} + +void LLVMInstructionSelection::visitMove(IR::Move *s) +{ + if (IR::Temp *t = s->target->asTemp()) { + if (llvm::Value *target = getLLVMTemp(t)) { + return; + } + } + Q_UNIMPLEMENTED(); +} + +void LLVMInstructionSelection::visitJump(IR::Jump *s) +{ + CreateBr(getLLVMBasicBlock(s->target)); +} + +void LLVMInstructionSelection::visitCJump(IR::CJump *s) +{ + CreateCondBr(getLLVMCondition(s->cond), + getLLVMBasicBlock(s->iftrue), + getLLVMBasicBlock(_function->basicBlocks.at(_block->index + 1))); +} + +void LLVMInstructionSelection::visitRet(IR::Ret *s) +{ + CreateRet(getLLVMValue(s->expr)); +} + + +void LLVMInstructionSelection::visitConst(IR::Const *e) +{ + _llvmValue = llvm::ConstantFP::get(_numberTy, e->value); +} + +void LLVMInstructionSelection::visitString(IR::String *) +{ + Q_UNIMPLEMENTED(); +} + +void LLVMInstructionSelection::visitName(IR::Name *) +{ + Q_UNIMPLEMENTED(); +} + +void LLVMInstructionSelection::visitTemp(IR::Temp *e) +{ + if (llvm::Value *t = getLLVMTemp(e)) { + _llvmValue = CreateLoad(t); + } +} + +void LLVMInstructionSelection::visitClosure(IR::Closure *) +{ + Q_UNIMPLEMENTED(); +} + +void LLVMInstructionSelection::visitUnop(IR::Unop *) +{ + Q_UNIMPLEMENTED(); +} + +void LLVMInstructionSelection::visitBinop(IR::Binop *e) +{ + Q_UNIMPLEMENTED(); +} + +void LLVMInstructionSelection::visitCall(IR::Call *) +{ + Q_UNIMPLEMENTED(); +} + +void LLVMInstructionSelection::visitNew(IR::New *) +{ + Q_UNIMPLEMENTED(); +} + +void LLVMInstructionSelection::visitSubscript(IR::Subscript *) +{ + Q_UNIMPLEMENTED(); +} + +void LLVMInstructionSelection::visitMember(IR::Member *) +{ + Q_UNIMPLEMENTED(); +} + diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h new file mode 100644 index 0000000000..8c0a5af28f --- /dev/null +++ b/qv4isel_llvm_p.h @@ -0,0 +1,63 @@ +#ifndef QV4ISEL_LLVM_P_H +#define QV4ISEL_LLVM_P_H + +#include "qv4isel_p.h" +#include "qv4ir_p.h" +#include +#include + +namespace QQmlJS { + +class LLVMInstructionSelection: + public llvm::IRBuilder<>, + protected IR::StmtVisitor, + protected IR::ExprVisitor +{ +public: + LLVMInstructionSelection(llvm::LLVMContext &context); + + llvm::Module *getLLVMModule(IR::Module *module); + llvm::Function *getLLVMFunction(IR::Function *function); + llvm::BasicBlock *getLLVMBasicBlock(IR::BasicBlock *block); + llvm::Value *getLLVMValue(IR::Expr *expr); + llvm::Value *getLLVMCondition(IR::Expr *expr); + llvm::Value *getLLVMTemp(IR::Temp *temp); + + virtual void visitExp(IR::Exp *); + virtual void visitEnter(IR::Enter *); + virtual void visitLeave(IR::Leave *); + virtual void visitMove(IR::Move *); + virtual void visitJump(IR::Jump *); + virtual void visitCJump(IR::CJump *); + virtual void visitRet(IR::Ret *); + + virtual void visitConst(IR::Const *); + virtual void visitString(IR::String *); + virtual void visitName(IR::Name *); + virtual void visitTemp(IR::Temp *); + virtual void visitClosure(IR::Closure *); + virtual void visitUnop(IR::Unop *); + virtual void visitBinop(IR::Binop *); + virtual void visitCall(IR::Call *); + virtual void visitNew(IR::New *); + virtual void visitSubscript(IR::Subscript *); + virtual void visitMember(IR::Member *); + +private: + llvm::Module *_llvmModule; + llvm::Function *_llvmFunction; + llvm::Value *_llvmValue; + llvm::Type *_numberTy; + llvm::Type *_valueTy; + llvm::Type *_contextTy; + llvm::FunctionType *_functionTy; + IR::Function *_function; + IR::BasicBlock *_block; + QHash _functionMap; + QHash _blockMap; + QVector _tempMap; +}; + +} // end of namespace QQmlJS + +#endif // QV4ISEL_LLVM_P_H diff --git a/v4.pro b/v4.pro index 036043d43b..bf28d974d8 100644 --- a/v4.pro +++ b/v4.pro @@ -16,7 +16,8 @@ SOURCES += main.cpp \ qv4syntaxchecker.cpp \ qv4ecmaobjects.cpp \ qv4array.cpp \ - qv4isel_x86_64.cpp + qv4isel_x86_64.cpp \ + qv4isel_llvm.cpp HEADERS += \ qv4codegen_p.h \ @@ -29,7 +30,25 @@ HEADERS += \ qv4syntaxchecker_p.h \ qv4ecmaobjects_p.h \ qv4array_p.h \ - qv4isel_x86_64_p.h + qv4isel_x86_64_p.h \ + qv4isel_llvm_p.h +llvm { +DEFINES += \ + WITH_LLVM + +INCLUDEPATH += \ + $$system(llvm-config --includedir) + +DEFINES += \ + __STDC_CONSTANT_MACROS \ + __STDC_FORMAT_MACROS \ + __STDC_LIMIT_MACROS + +LIBS += \ + $$system(llvm-config --ldflags) \ + $$system(llvm-config --libs core) + +} -- cgit v1.2.3 From f95340fb31cbd6f8b079c486e9767427a862d9da Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 1 Jun 2012 14:18:24 +0200 Subject: Fix the pro file --- v4.pro | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/v4.pro b/v4.pro index bf28d974d8..dc05adb17a 100644 --- a/v4.pro +++ b/v4.pro @@ -16,8 +16,7 @@ SOURCES += main.cpp \ qv4syntaxchecker.cpp \ qv4ecmaobjects.cpp \ qv4array.cpp \ - qv4isel_x86_64.cpp \ - qv4isel_llvm.cpp + qv4isel_x86_64.cpp HEADERS += \ qv4codegen_p.h \ @@ -30,12 +29,17 @@ HEADERS += \ qv4syntaxchecker_p.h \ qv4ecmaobjects_p.h \ qv4array_p.h \ - qv4isel_x86_64_p.h \ - qv4isel_llvm_p.h + qv4isel_x86_64_p.h llvm { +SOURCES += \ + qv4isel_llvm.cpp + +HEADERS += \ + qv4isel_llvm_p.h + DEFINES += \ WITH_LLVM -- cgit v1.2.3 From e2a03c55e12f21db5f300934b91e9a4622d0ed20 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Sun, 3 Jun 2012 17:11:04 +0200 Subject: Inline comparisons --- qv4isel_x86_64.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/qv4isel_x86_64.cpp b/qv4isel_x86_64.cpp index 02f53aeff4..cdf50f829f 100644 --- a/qv4isel_x86_64.cpp +++ b/qv4isel_x86_64.cpp @@ -902,9 +902,10 @@ void InstructionSelection::visitCJump(IR::CJump *s) amd64_call_code(_codePtr, __qmljs_to_boolean); amd64_patch(label2, _codePtr); - amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 0, 4); + amd64_mov_reg_imm_size(_codePtr, AMD64_RDX, 1, 1); + amd64_alu_reg8_reg8(_codePtr, X86_CMP, AMD64_RAX, AMD64_RDX, 0, 0); _patches[s->iftrue].append(_codePtr); - amd64_branch32(_codePtr, X86_CC_NZ, 0, 1); + amd64_branch32(_codePtr, X86_CC_E, 0, 1); if (_block->index + 1 != s->iffalse->index) { _patches[s->iffalse].append(_codePtr); @@ -915,11 +916,46 @@ void InstructionSelection::visitCJump(IR::CJump *s) IR::Temp *l = b->left->asTemp(); IR::Temp *r = b->right->asTemp(); if (l && r) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, l); loadTempAddress(AMD64_RDX, r); - // ### TODO: instruction selection for common cases (e.g. number1 < number2) + uchar *label1 = 0, *label2 = 0, *label3 = 0; + if (b->op != IR::OpInstanceof && b->op != IR::OpIn) { + amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RSI, 0, NUMBER_TYPE, 4); + label1 = _codePtr; + amd64_branch8(_codePtr, X86_CC_NE, 0, 0); + amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RDX, 0, NUMBER_TYPE, 4); + label2 = _codePtr; + amd64_branch8(_codePtr, X86_CC_NE, 0, 0); + amd64_movsd_reg_membase(_codePtr, AMD64_XMM0, AMD64_RSI, offsetof(Value, numberValue)); + amd64_movsd_reg_membase(_codePtr, AMD64_XMM1, AMD64_RDX, offsetof(Value, numberValue)); + + int op; + switch (b->op) { + default: Q_UNREACHABLE(); assert(!"todo"); break; + case IR::OpGt: op = X86_CC_GT; break; + case IR::OpLt: op = X86_CC_LT; break; + case IR::OpGe: op = X86_CC_GE; break; + case IR::OpLe: op = X86_CC_LE; break; + case IR::OpEqual: op = X86_CC_EQ; break; + case IR::OpNotEqual: op = X86_CC_NE; break; + case IR::OpStrictEqual: op = X86_CC_EQ; break; + case IR::OpStrictNotEqual: op = X86_CC_NE; break; + } + + amd64_sse_ucomisd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); + amd64_set_reg_size(_codePtr, op, AMD64_RAX, 0, 1); + + label3 = _codePtr; + amd64_jump32(_codePtr, 0); + } + + if (label1 && label2) { + amd64_patch(label1, _codePtr); + amd64_patch(label2, _codePtr); + } + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); bool (*op)(Context *, const Value *, const Value *); switch (b->op) { @@ -937,10 +973,15 @@ void InstructionSelection::visitCJump(IR::CJump *s) } // switch amd64_call_code(_codePtr, op); - x86_alu_reg_imm(_codePtr, X86_CMP, X86_EAX, 0); + + if (label3) + amd64_patch(label3, _codePtr); + + x86_mov_reg_imm(_codePtr, X86_EDX, 1); + x86_alu_reg8_reg8(_codePtr, X86_CMP, X86_EAX, X86_EDX, 0, 0); _patches[s->iftrue].append(_codePtr); - amd64_branch32(_codePtr, X86_CC_NZ, 0, 1); + amd64_branch32(_codePtr, X86_CC_E, 0, 1); if (_block->index + 1 != s->iffalse->index) { _patches[s->iffalse].append(_codePtr); -- cgit v1.2.3 From 40eb19873f8de8432ca3cc827264992086a067b3 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 4 Jun 2012 08:32:06 +0200 Subject: Fix default size for the bucket tables --- qmljs_objects.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qmljs_objects.h b/qmljs_objects.h index 731adbeafa..bdaef3e45d 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -94,7 +94,7 @@ public: : _properties(0) , _buckets(0) , _propertyCount(-1) - , _bucketCount(11) + , _bucketCount(0) , _allocated(0) {} ~Table() @@ -121,7 +121,7 @@ public: { if (_properties) { for (Property *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { - if (prop->hasName(name)) + if (prop->name == name || prop->hasName(name)) return prop; } } @@ -168,10 +168,10 @@ private: { if (_bucketCount) _bucketCount *= 2; // ### next prime + else + _bucketCount = 11; - if (_buckets) - delete[] _buckets; - + delete[] _buckets; _buckets = new Property *[_bucketCount]; std::fill(_buckets, _buckets + _bucketCount, (Property *) 0); -- cgit v1.2.3 From 0b021ba11544c7135d216a82b84474cc8043b2f7 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 4 Jun 2012 12:54:24 +0200 Subject: Reimplemented Array using std::deque as container. --- qv4array_p.h | 242 +++++++++-------------------------------------------------- 1 file changed, 36 insertions(+), 206 deletions(-) diff --git a/qv4array_p.h b/qv4array_p.h index d820ce591c..c12f3ec546 100644 --- a/qv4array_p.h +++ b/qv4array_p.h @@ -2,8 +2,8 @@ #define QV4ARRAY_P_H #include "qmljs_runtime.h" -#include -#include +#include +#include namespace QQmlJS { namespace VM { @@ -31,21 +31,9 @@ public: inline void splice(double start, double deleteCount, const QVector &items, Array &other); - inline QList keys() const; private: - enum Mode { - VectorMode, - MapMode - }; - - Mode m_mode; - int m_instances; - - union { - QMap *to_map; - QVector *to_vector; - }; + std::deque *to_vector; }; class ArrayElementLessThan @@ -62,187 +50,74 @@ private: }; inline Array::Array() - : m_mode(VectorMode) - , m_instances(0) { - to_vector = new QVector(); + to_vector = new std::deque(); } -inline Array::Array(const Array &other): - m_mode(other.m_mode), - m_instances(other.m_instances) +inline Array::Array(const Array &other) { - if (m_mode == VectorMode) - to_vector = new QVector (*other.to_vector); - else - to_map = new QMap (*other.to_map); + to_vector = new std::deque(*other.to_vector); } inline Array::~Array() { - if (m_mode == VectorMode) - delete to_vector; - else - delete to_map; + delete to_vector; } inline Array &Array::operator = (const Array &other) { - m_instances = other.m_instances; - if (m_mode != other.m_mode) { - if (m_mode == VectorMode) - delete to_vector; - else - delete to_map; - m_mode = other.m_mode; - - if (m_mode == VectorMode) - to_vector = new QVector (*other.to_vector); - else - to_map = new QMap (*other.to_map); - } - - if (m_mode == VectorMode) - *to_vector = *other.to_vector; - else - *to_map = *other.to_map; - + *to_vector = *other.to_vector; return *this; } inline bool Array::isEmpty() const { - if (m_mode == VectorMode) - return to_vector->isEmpty(); - - return to_map->isEmpty(); + return to_vector->empty(); } inline uint Array::size() const { - if (m_mode == VectorMode) - return to_vector->size(); - - if (to_map->isEmpty()) - return 0; - - return (--to_map->constEnd()).key(); + return to_vector->size(); } inline uint Array::count() const { - return size(); + return to_vector->size(); } inline Value Array::at(uint index) const { - if (m_mode == VectorMode) { - if (index < uint(to_vector->size())) - return to_vector->at(index); - return Value::undefinedValue(); - } else { - return to_map->value(index, Value::undefinedValue()); - } + return index < to_vector->size() ? to_vector->at(index) : Value::undefinedValue(); } inline void Array::assign(uint index, const Value &v) { - if (index >= size()) { - resize(index + 1); - } - - const Value &oldv = at(index); - if (oldv.isObject() || oldv.isString()) - --m_instances; - - if (v.isObject() || v.isString()) - ++m_instances; + if (index == to_vector->size()) + to_vector->push_back(v); + else { + if (index > to_vector->size()) + resize(index + 1); - if (m_mode == VectorMode) { - to_vector->replace(index, v); - } else { - if (v.isUndefined()) - to_map->remove(index); - else - to_map->insert(index, v); + (*to_vector)[index] = v; } } inline void Array::clear() { - m_instances = 0; - - if (m_mode == VectorMode) - to_vector->clear(); - - else - to_map->clear(); + to_vector->clear(); } inline void Array::resize(uint s) { - const uint oldSize = size(); - if (oldSize == s) - return; - - const uint N = 10 * 1024; - - if (m_mode == VectorMode) { - if (s < N) { - to_vector->resize(s); - for (uint i = oldSize; i < s; ++i) - assign(i, Value::undefinedValue()); - } else { - // switch to MapMode - QMap *m = new QMap(); - for (uint i = 0; i < oldSize; ++i) { - if (! to_vector->at(i).isUndefined()) - m->insert(i, to_vector->at(i)); - } - m->insert(s, Value::undefinedValue()); - delete to_vector; - to_map = m; - m_mode = MapMode; - } - } - - else { - if (s < N) { - // switch to VectorMode - QVector *v = new QVector (s, Value::undefinedValue()); - QMap::const_iterator it = to_map->constBegin(); - for ( ; (it != to_map->constEnd()) && (it.key() < s); ++it) - (*v) [it.key()] = it.value(); - delete to_map; - to_vector = v; - m_mode = VectorMode; - } else { - if (!to_map->isEmpty()) { - QMap::iterator it = --to_map->end(); - if (oldSize > s) { - // shrink - while ((it != to_map->end()) && (it.key() >= s)) { - it = to_map->erase(it); - --it; - } - } else { - if ((it.key() == oldSize) && !it.value().isUndefined()) - to_map->erase(it); - } - } - to_map->insert(s, Value::undefinedValue()); - } - } + to_vector->resize(s, Value::undefinedValue()); } inline void Array::concat(const Array &other) { - uint k = size(); - resize (k + other.size()); - for (uint i = 0; i < other.size(); ++i) { - Value v = other.at(i); + for (std::deque::iterator it = other.to_vector->begin(); it != other.to_vector->end(); ++it) { + const Value &v = *it; if (! v.isUndefined()) - assign(k + i, v); + to_vector->push_back(v); } } @@ -251,15 +126,8 @@ inline Value Array::pop() if (isEmpty()) return Value::undefinedValue(); - Value v; - - if (m_mode == VectorMode) - v = to_vector->last(); - else - v = *--to_map->end(); - - resize(size() - 1); - + Value v = to_vector->back(); + to_vector->pop_back(); return v; } @@ -268,30 +136,15 @@ inline Value Array::takeFirst() if (isEmpty()) return Value::undefinedValue(); - Value v; - if (m_mode == VectorMode) { - v = to_vector->first(); - to_vector->remove(0, 1); - } else { - v = *to_map->begin(); - to_map->erase(to_map->begin()); - } + Value v = to_vector->front(); + to_vector->pop_front(); return v; } inline void Array::sort(Context *context, const Value &comparefn) { ArrayElementLessThan lessThan(context, comparefn); - if (m_mode == VectorMode) { - qSort(to_vector->begin(), to_vector->end(), lessThan); - } else { - QList keys = to_map->keys(); - QList values = to_map->values(); - qStableSort(values.begin(), values.end(), lessThan); - const uint len = keys.size(); - for (uint i = 0; i < len; ++i) - to_map->insert(keys.at(i), values.at(i)); - } + std::sort(to_vector->begin(), to_vector->end(), lessThan); } inline void Array::splice(double start, double deleteCount, @@ -311,37 +164,14 @@ inline void Array::splice(double start, double deleteCount, const uint itemsSize = uint(items.size()); - if (m_mode == VectorMode) { - for (uint i = 0; i < dc; ++i) - other.assign(i, to_vector->at(st + i)); - if (itemsSize > dc) - to_vector->insert(st, itemsSize - dc, Value::undefinedValue()); - else if (itemsSize < dc) - to_vector->remove(st, dc - itemsSize); - for (uint i = 0; i < itemsSize; ++i) - to_vector->replace(st + i, items.at(i)); - } else { - for (uint i = 0; i < dc; ++i) - other.assign(i, to_map->take(st + i)); - uint del = itemsSize - dc; - if (del != 0) { - for (uint i = st; i < uint(len); ++i) { - if (to_map->contains(i)) - to_map->insert(i + del, to_map->take(i)); - } - resize(uint(len) + del); - } - for (uint i = 0; i < itemsSize; ++i) - to_map->insert(st + i, items.at(i)); - } -} - -inline QList Array::keys() const -{ - if (m_mode == VectorMode) - return QList(); - else - return to_map->keys(); + for (uint i = 0; i < dc; ++i) + other.assign(i, to_vector->at(st + i)); + if (itemsSize > dc) + to_vector->insert(to_vector->begin() + st, itemsSize - dc, Value::undefinedValue()); + else if (itemsSize < dc) + to_vector->erase(to_vector->begin() + st, to_vector->begin() + (dc - itemsSize)); + for (uint i = 0; i < itemsSize; ++i) + (*to_vector)[st + i] = items.at(i); } } // end of namespace VM -- cgit v1.2.3 From 968b80267f1084d65c889548fc994f2509b79402 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 4 Jun 2012 13:44:37 +0200 Subject: Initial work on [[HasProperty]] --- qmljs_objects.cpp | 4 ++-- qv4isel_x86_64.cpp | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 52c825f27c..464b9ccf26 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -83,12 +83,12 @@ bool Object::canSetProperty(Context *ctx, String *name) return true; } -bool Object::hasProperty(Context *, String *name) const +bool Object::hasProperty(Context *ctx, String *name) const { if (members) return members->find(name) != 0; - return false; + return prototype ? prototype->hasProperty(ctx, name) : false; } bool Object::deleteProperty(Context *, String *name, bool flag) diff --git a/qv4isel_x86_64.cpp b/qv4isel_x86_64.cpp index cdf50f829f..e60dd5655f 100644 --- a/qv4isel_x86_64.cpp +++ b/qv4isel_x86_64.cpp @@ -697,11 +697,7 @@ void InstructionSelection::visitMove(IR::Move *s) case IR::OpStrictEqual: op = __qmljs_se; break; case IR::OpStrictNotEqual: op = __qmljs_sne; break; case IR::OpInstanceof: op = __qmljs_instanceof; break; - - case IR::OpIn: - Q_UNIMPLEMENTED(); - assert(!"TODO"); - break; + case IR::OpIn: op = __qmljs_in; break; case IR::OpAnd: case IR::OpOr: -- cgit v1.2.3 From e1f20b85545805e1fefe69423a245883e2c93947 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 4 Jun 2012 14:02:40 +0200 Subject: Initial work on the Object prototype --- qv4ecmaobjects.cpp | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4ecmaobjects_p.h | 19 ++++++++++ 2 files changed, 128 insertions(+) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 82c7e30b9e..deab3c1058 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -491,8 +491,92 @@ void ObjectCtor::call(Context *ctx) void ObjectPrototype::init(Context *ctx, const Value &ctor) { ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue->setProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 0); + ctor.objectValue->setProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 0); + ctor.objectValue->setProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 0); + ctor.objectValue->setProperty(ctx, QStringLiteral("create"), method_create, 0); + ctor.objectValue->setProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 0); + ctor.objectValue->setProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 0); + ctor.objectValue->setProperty(ctx, QStringLiteral("seal"), method_seal, 0); + ctor.objectValue->setProperty(ctx, QStringLiteral("freeze"), method_freeze, 0); + ctor.objectValue->setProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 0); + ctor.objectValue->setProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 0); + ctor.objectValue->setProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 0); + ctor.objectValue->setProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 0); + ctor.objectValue->setProperty(ctx, QStringLiteral("keys"), method_keys, 0); + setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("toString"), method_toString, 0); + setProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + setProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + setProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 0); + setProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 0); + setProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 0); +} + +void ObjectPrototype::method_getPrototypeOf(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.getPrototypeOf")); +} + +void ObjectPrototype::method_getOwnPropertyDescriptor(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.getOwnPropertyDescriptors")); +} + +void ObjectPrototype::method_getOwnPropertyNames(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.getOwnPropertyNames")); +} + +void ObjectPrototype::method_create(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.create")); +} + +void ObjectPrototype::method_defineProperty(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.defineProperty")); +} + +void ObjectPrototype::method_defineProperties(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.defineProperties")); +} + +void ObjectPrototype::method_seal(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.seal")); +} + +void ObjectPrototype::method_freeze(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.freeze")); +} + +void ObjectPrototype::method_preventExtensions(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.preventExtensions")); +} + +void ObjectPrototype::method_isSealed(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.isSealed")); +} + +void ObjectPrototype::method_isFrozen(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.isFrozen")); +} + +void ObjectPrototype::method_isExtensible(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.isExtensible")); +} + +void ObjectPrototype::method_keys(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.keys")); } void ObjectPrototype::method_toString(Context *ctx) @@ -503,6 +587,31 @@ void ObjectPrototype::method_toString(Context *ctx) ctx->result = Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(ctx->thisObject.objectValue->className())); } +void ObjectPrototype::method_toLocaleString(Context *ctx) +{ + method_toString(ctx); +} + +void ObjectPrototype::method_valueOf(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.prototype.valueOf")); +} + +void ObjectPrototype::method_hasOwnProperty(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.prototype.hasOwnProperty")); +} + +void ObjectPrototype::method_isPrototypeOf(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.prototype.isPrototypeOf")); +} + +void ObjectPrototype::method_propertyIsEnumerable(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("Object.prototype.propertyIsEnumerable")); +} + // // String // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 1936a20aa6..6517ad1ea8 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -19,7 +19,26 @@ struct ObjectPrototype: Object { void init(Context *ctx, const Value &ctor); + static void method_getPrototypeOf(Context *ctx); + static void method_getOwnPropertyDescriptor(Context *ctx); + static void method_getOwnPropertyNames(Context *ctx); + static void method_create(Context *ctx); + static void method_defineProperty(Context *ctx); + static void method_defineProperties(Context *ctx); + static void method_seal(Context *ctx); + static void method_freeze(Context *ctx); + static void method_preventExtensions(Context *ctx); + static void method_isSealed(Context *ctx); + static void method_isFrozen(Context *ctx); + static void method_isExtensible(Context *ctx); + static void method_keys(Context *ctx); + static void method_toString(Context *ctx); + static void method_toLocaleString(Context *ctx); + static void method_valueOf(Context *ctx); + static void method_hasOwnProperty(Context *ctx); + static void method_isPrototypeOf(Context *ctx); + static void method_propertyIsEnumerable(Context *ctx); }; struct StringCtor: FunctionObject -- cgit v1.2.3 From 08bd3ea09ada7c867255500a3dcf1207b81259ef Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 4 Jun 2012 15:34:50 +0200 Subject: Initial work on the RegExp object --- qmljs_objects.cpp | 46 ++++++++++++++++++++++++++++++++-------------- qmljs_objects.h | 15 +++++++++++++++ qv4ecmaobjects.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ qv4ecmaobjects_p.h | 18 ++++++++++++++++++ 4 files changed, 107 insertions(+), 14 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 464b9ccf26..a9899ec1f8 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -238,6 +238,7 @@ ExecutionEngine::ExecutionEngine() arrayPrototype = new ArrayPrototype(); datePrototype = new DatePrototype(); functionPrototype = new FunctionPrototype(rootContext); + regExpPrototype = new RegExpPrototype(); stringPrototype->prototype = objectPrototype; numberPrototype->prototype = objectPrototype; @@ -245,21 +246,24 @@ ExecutionEngine::ExecutionEngine() arrayPrototype->prototype = objectPrototype; datePrototype->prototype = objectPrototype; functionPrototype->prototype = objectPrototype; - - Value objectCtor = Value::fromObject(new ObjectCtor(rootContext)); - Value stringCtor = Value::fromObject(new StringCtor(rootContext)); - Value numberCtor = Value::fromObject(new NumberCtor(rootContext)); - Value booleanCtor = Value::fromObject(new BooleanCtor(rootContext)); - Value arrayCtor = Value::fromObject(new ArrayCtor(rootContext)); - Value functionCtor = Value::fromObject(new FunctionCtor(rootContext)); - Value dateCtor = Value::fromObject(new DateCtor(rootContext)); - - stringCtor.objectValue->prototype = functionPrototype; - numberCtor.objectValue->prototype = functionPrototype; - booleanCtor.objectValue->prototype = functionPrototype; - arrayCtor.objectValue->prototype = functionPrototype; + regExpPrototype->prototype = objectPrototype; + + objectCtor = Value::fromObject(new ObjectCtor(rootContext)); + stringCtor = Value::fromObject(new StringCtor(rootContext)); + numberCtor = Value::fromObject(new NumberCtor(rootContext)); + booleanCtor = Value::fromObject(new BooleanCtor(rootContext)); + arrayCtor = Value::fromObject(new ArrayCtor(rootContext)); + functionCtor = Value::fromObject(new FunctionCtor(rootContext)); + dateCtor = Value::fromObject(new DateCtor(rootContext)); + regExpCtor = Value::fromObject(new RegExpCtor(rootContext)); + + stringCtor.objectValue->prototype = stringPrototype; + numberCtor.objectValue->prototype = numberPrototype; + booleanCtor.objectValue->prototype = booleanPrototype; + arrayCtor.objectValue->prototype = arrayPrototype; functionCtor.objectValue->prototype = functionPrototype; - dateCtor.objectValue->prototype = functionPrototype; + dateCtor.objectValue->prototype = datePrototype; + regExpCtor.objectValue->prototype = regExpPrototype; objectPrototype->init(rootContext, objectCtor); stringPrototype->init(rootContext, stringCtor); @@ -268,6 +272,7 @@ ExecutionEngine::ExecutionEngine() arrayPrototype->init(rootContext, arrayCtor); datePrototype->init(rootContext, dateCtor); functionPrototype->init(rootContext, functionCtor); + regExpPrototype->init(rootContext, regExpCtor); // // set up the global object @@ -283,6 +288,7 @@ ExecutionEngine::ExecutionEngine() glo->setProperty(rootContext, identifier(QStringLiteral("Array")), arrayCtor); glo->setProperty(rootContext, identifier(QStringLiteral("Function")), functionCtor); glo->setProperty(rootContext, identifier(QStringLiteral("Date")), dateCtor); + glo->setProperty(rootContext, identifier(QStringLiteral("RegExp")), regExpCtor); glo->setProperty(rootContext, identifier(QStringLiteral("Math")), Value::fromObject(newMathObject(rootContext))); } @@ -412,6 +418,18 @@ FunctionObject *ExecutionEngine::newDateCtor(Context *ctx) return new DateCtor(ctx); } +Object *ExecutionEngine::newRegExpObject(const Value &value) +{ + Object *object = new RegExpObject(value); + object->prototype = regExpPrototype; + return object; +} + +FunctionObject *ExecutionEngine::newRegExpCtor(Context *ctx) +{ + return new RegExpCtor(ctx); +} + Object *ExecutionEngine::newErrorObject(const Value &value) { ErrorObject *object = new ErrorObject(value); diff --git a/qmljs_objects.h b/qmljs_objects.h index bdaef3e45d..a1f4d0ed8e 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -24,6 +24,7 @@ struct StringObject; struct ArrayObject; struct DateObject; struct FunctionObject; +struct RegExpObject; struct ErrorObject; struct ArgumentsObject; struct Context; @@ -36,6 +37,7 @@ struct BooleanPrototype; struct ArrayPrototype; struct FunctionPrototype; struct DatePrototype; +struct RegExpPrototype; struct String { String(const QString &text) @@ -212,6 +214,7 @@ struct Object { virtual DateObject *asDateObject() { return 0; } virtual ArrayObject *asArrayObject() { return 0; } virtual FunctionObject *asFunctionObject() { return 0; } + virtual RegExpObject *asRegExpObject() { return 0; } virtual ErrorObject *asErrorObject() { return 0; } virtual ArgumentsObject *asArgumentsObject() { return 0; } @@ -309,6 +312,13 @@ struct ScriptFunction: FunctionObject { virtual void construct(Context *ctx); }; +struct RegExpObject: Object { + Value value; + RegExpObject(const Value &value): value(value) {} + virtual QString className() { return QStringLiteral("RegExp"); } + virtual RegExpObject *asRegExpObject() { return this; } +}; + struct ErrorObject: Object { Value value; ErrorObject(const Value &message): value(message) {} @@ -410,6 +420,7 @@ struct ExecutionEngine Value arrayCtor; Value functionCtor; Value dateCtor; + Value regExpCtor; ObjectPrototype *objectPrototype; StringPrototype *stringPrototype; @@ -418,6 +429,7 @@ struct ExecutionEngine ArrayPrototype *arrayPrototype; FunctionPrototype *functionPrototype; DatePrototype *datePrototype; + RegExpPrototype *regExpPrototype; QHash identifiers; @@ -458,6 +470,9 @@ struct ExecutionEngine Object *newDateObject(const Value &value); FunctionObject *newDateCtor(Context *ctx); + Object *newRegExpObject(const Value &value); + FunctionObject *newRegExpCtor(Context *ctx); + Object *newErrorObject(const Value &value); Object *newMathObject(Context *ctx); Object *newArgumentsObject(Context *ctx); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index deab3c1058..7692ace514 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2329,6 +2329,48 @@ void DatePrototype::method_toUTCString(Context *ctx) } } +// +// RegExp object +// +RegExpCtor::RegExpCtor(Context *scope) + : FunctionObject(scope) +{ +} + +void RegExpCtor::construct(Context *ctx) +{ + __qmljs_init_object(&ctx->thisObject, ctx->engine->newStringObject(Value::undefinedValue())); +} + +void RegExpCtor::call(Context *ctx) +{ + __qmljs_init_object(&ctx->result, ctx->engine->newRegExpObject(Value::undefinedValue())); +} + +void RegExpPrototype::init(Context *ctx, const Value &ctor) +{ + ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + setProperty(ctx, QStringLiteral("constructor"), ctor); + setProperty(ctx, QStringLiteral("exec"), method_exec, 0); + setProperty(ctx, QStringLiteral("test"), method_test, 0); + setProperty(ctx, QStringLiteral("toString"), method_toString, 0); +} + +void RegExpPrototype::method_exec(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("RegExp.prototype.exec")); +} + +void RegExpPrototype::method_test(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("RegExp.prototype.test")); +} + +void RegExpPrototype::method_toString(Context *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("RegExp.prototype.toString")); +} + // // Math object // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 6517ad1ea8..38164fc2aa 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -235,6 +235,24 @@ struct DatePrototype: DateObject static void method_toUTCString(Context *ctx); }; +struct RegExpCtor: FunctionObject +{ + RegExpCtor(Context *scope); + + virtual void construct(Context *ctx); + virtual void call(Context *ctx); +}; + +struct RegExpPrototype: RegExpObject +{ + RegExpPrototype(): RegExpObject(Value::fromNumber(qSNaN())) {} + void init(Context *ctx, const Value &ctor); + + static void method_exec(Context *ctx); + static void method_test(Context *ctx); + static void method_toString(Context *ctx); +}; + struct MathObject: Object { MathObject(Context *ctx); -- cgit v1.2.3 From bf780442955935cc368d013b4dacdc1885e14fde Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 4 Jun 2012 15:40:35 +0200 Subject: Add special property length of the Object constructor. --- qv4ecmaobjects.cpp | 7 +++++++ qv4ecmaobjects_p.h | 1 + 2 files changed, 8 insertions(+) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 7692ace514..ce50436c8b 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -488,6 +488,13 @@ void ObjectCtor::call(Context *ctx) __qmljs_init_object(&ctx->result, ctx->engine->newObject()); } +Value ObjectCtor::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) +{ + if (name == ctx->engine->id_length) + return Value::fromNumber(1); + return Object::getProperty(ctx, name, attributes); +} + void ObjectPrototype::init(Context *ctx, const Value &ctor) { ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 38164fc2aa..92e2898282 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -13,6 +13,7 @@ struct ObjectCtor: FunctionObject virtual void construct(Context *ctx); virtual void call(Context *ctx); + virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes); }; struct ObjectPrototype: Object -- cgit v1.2.3 From 51dcef5cdfedf9c37f46ab8f28328a36ae05ce49 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 4 Jun 2012 15:57:39 +0200 Subject: Implemented Object.getPrototypeOf and Object.prototype.valueOf --- qmljs_objects.cpp | 12 ++++++------ qmljs_runtime.cpp | 7 +++++++ qmljs_runtime.h | 7 ++++++- qv4ecmaobjects.cpp | 10 ++++++++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index a9899ec1f8..ce363ec274 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -257,13 +257,13 @@ ExecutionEngine::ExecutionEngine() dateCtor = Value::fromObject(new DateCtor(rootContext)); regExpCtor = Value::fromObject(new RegExpCtor(rootContext)); - stringCtor.objectValue->prototype = stringPrototype; - numberCtor.objectValue->prototype = numberPrototype; - booleanCtor.objectValue->prototype = booleanPrototype; - arrayCtor.objectValue->prototype = arrayPrototype; + stringCtor.objectValue->prototype = functionPrototype; + numberCtor.objectValue->prototype = functionPrototype; + booleanCtor.objectValue->prototype = functionPrototype; + arrayCtor.objectValue->prototype = functionPrototype; functionCtor.objectValue->prototype = functionPrototype; - dateCtor.objectValue->prototype = datePrototype; - regExpCtor.objectValue->prototype = regExpPrototype; + dateCtor.objectValue->prototype = functionPrototype; + regExpCtor.objectValue->prototype = functionPrototype; objectPrototype->init(rootContext, objectCtor); stringPrototype->init(rootContext, stringCtor); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index df05ff67ab..e081c3e8d8 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -126,6 +126,13 @@ String *Value::toString(Context *ctx) const return v.stringValue; } +Value Value::toObject(Context *ctx) const +{ + Value v; + __qmljs_to_object(ctx, &v, this); + return v; +} + bool Value::isFunctionObject() const { return type == OBJECT_TYPE ? objectValue->asFunctionObject() != 0 : false; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 71bdd58a2e..269ccf0913 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -232,7 +232,11 @@ struct Value { static inline Value fromObject(Object *value) { Value v; - __qmljs_init_object(&v, value); + if (value) { + __qmljs_init_object(&v, value); + } else { + __qmljs_init_null(&v); + } return v; } @@ -255,6 +259,7 @@ struct Value { double toInteger(Context *ctx) const; double toNumber(Context *ctx) const; String *toString(Context *ctx) const; + Value toObject(Context *ctx) const; inline bool isUndefined() const { return is(UNDEFINED_TYPE); } inline bool isNull() const { return is(NULL_TYPE); } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index ce50436c8b..8157d4f8dc 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -523,7 +523,12 @@ void ObjectPrototype::init(Context *ctx, const Value &ctor) void ObjectPrototype::method_getPrototypeOf(Context *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.getPrototypeOf")); + Value o = ctx->argument(0); + if (! o.isObject()) { + ctx->throwTypeError(); + } else { + ctx->result = Value::fromObject(o.objectValue->prototype); + } } void ObjectPrototype::method_getOwnPropertyDescriptor(Context *ctx) @@ -601,7 +606,8 @@ void ObjectPrototype::method_toLocaleString(Context *ctx) void ObjectPrototype::method_valueOf(Context *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.prototype.valueOf")); + Value o = ctx->thisObject.toObject(ctx); + ctx->result = o; } void ObjectPrototype::method_hasOwnProperty(Context *ctx) -- cgit v1.2.3 From 98f217edfaa935148272b21d73c3c74fe69ac3d5 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 4 Jun 2012 16:04:46 +0200 Subject: Implemented Object.prototype.hasOwnProperty. Note that the current implementation of Object::hasOwnProperty() in the object model is not ECMA compliant (yet). --- qv4ecmaobjects.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 8157d4f8dc..59fcdc7b79 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -612,7 +612,10 @@ void ObjectPrototype::method_valueOf(Context *ctx) void ObjectPrototype::method_hasOwnProperty(Context *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.prototype.hasOwnProperty")); + String *P = ctx->argument(0).toString(ctx); + Value O = ctx->thisObject.toObject(ctx); + bool r = O.objectValue->getOwnProperty(ctx, P) != 0; + ctx->result = Value::fromBoolean(r); } void ObjectPrototype::method_isPrototypeOf(Context *ctx) -- cgit v1.2.3 From 66a9d3781340167f07681a8b80010289ca1c1980 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 4 Jun 2012 16:36:00 +0200 Subject: Naive implementation of the Arguments object. --- qmljs_objects.cpp | 36 +++++++++++++++++++++++++++++++----- qmljs_objects.h | 15 ++++++++++++++- qmljs_runtime.cpp | 6 +++--- qmljs_runtime.h | 4 ++-- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index ce363ec274..6f7964d62a 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -196,7 +196,7 @@ void ScriptFunction::construct(VM::Context *ctx) function->code(ctx); } -Value *ArgumentsObject::getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes) +Value *ActivationObject::getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes) { if (context) { for (size_t i = 0; i < context->varCount; ++i) { @@ -215,12 +215,37 @@ Value *ArgumentsObject::getPropertyDescriptor(Context *ctx, String *name, Proper return &context->arguments[i]; } } + if (name->isEqualTo(ctx->engine->id_arguments)) { + if (arguments.isUndefined()) { + arguments = Value::fromObject(new ArgumentsObject(ctx)); + arguments.objectValue->prototype = ctx->engine->objectPrototype; + } + + return &arguments; + } } if (Value *prop = Object::getPropertyDescriptor(ctx, name, attributes)) return prop; return 0; } +Value ArgumentsObject::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) +{ + if (name->isEqualTo(ctx->engine->id_length)) + return Value::fromNumber(context->argumentCount); + return Object::getProperty(ctx, name, attributes); +} + +Value *ArgumentsObject::getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes) +{ + if (context) { + const quint32 i = Value::fromString(name).toUInt32(ctx); + if (i < context->argumentCount) + return &context->arguments[i]; + } + return Object::getPropertyDescriptor(ctx, name, attributes); +} + ExecutionEngine::ExecutionEngine() { rootContext = newContext(); @@ -229,6 +254,7 @@ ExecutionEngine::ExecutionEngine() id_length = identifier(QStringLiteral("length")); id_prototype = identifier(QStringLiteral("prototype")); id_constructor = identifier(QStringLiteral("constructor")); + id_arguments = identifier(QStringLiteral("arguments")); id___proto__ = identifier(QStringLiteral("__proto__")); objectPrototype = new ObjectPrototype(); @@ -277,7 +303,7 @@ ExecutionEngine::ExecutionEngine() // // set up the global object // - VM::Object *glo = newArgumentsObject(rootContext); + VM::Object *glo = newActivationObject(rootContext); __qmljs_init_object(&globalObject, glo); __qmljs_init_object(&rootContext->activation, glo); @@ -444,9 +470,9 @@ Object *ExecutionEngine::newMathObject(Context *ctx) return object; } -Object *ExecutionEngine::newArgumentsObject(Context *ctx) +Object *ExecutionEngine::newActivationObject(Context *ctx) { - return new ArgumentsObject(ctx); + return new ActivationObject(ctx); } void Context::throwError(const Value &value) @@ -486,7 +512,7 @@ void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionO parent = f->scope; if (f->needsActivation) - __qmljs_init_object(&activation, engine->newArgumentsObject(this)); + __qmljs_init_object(&activation, engine->newActivationObject(this)); else __qmljs_init_null(&activation); diff --git a/qmljs_objects.h b/qmljs_objects.h index a1f4d0ed8e..1767a12328 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -26,6 +26,7 @@ struct DateObject; struct FunctionObject; struct RegExpObject; struct ErrorObject; +struct ActivationObject; struct ArgumentsObject; struct Context; struct ExecutionEngine; @@ -216,6 +217,7 @@ struct Object { virtual FunctionObject *asFunctionObject() { return 0; } virtual RegExpObject *asRegExpObject() { return 0; } virtual ErrorObject *asErrorObject() { return 0; } + virtual ActivationObject *asActivationObject() { return 0; } virtual ArgumentsObject *asArgumentsObject() { return 0; } virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes = 0); @@ -326,11 +328,21 @@ struct ErrorObject: Object { virtual ErrorObject *asErrorObject() { return this; } }; +struct ActivationObject: Object { + Context *context; + Value arguments; + ActivationObject(Context *context): context(context), arguments(Value::undefinedValue()) {} + virtual QString className() { return QStringLiteral("Activation"); } + virtual ActivationObject *asActivationObject() { return this; } + virtual Value *getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes); +}; + struct ArgumentsObject: Object { Context *context; ArgumentsObject(Context *context): context(context) {} virtual QString className() { return QStringLiteral("Arguments"); } virtual ArgumentsObject *asArgumentsObject() { return this; } + virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes); virtual Value *getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes); }; @@ -436,6 +448,7 @@ struct ExecutionEngine String *id_length; String *id_prototype; String *id_constructor; + String *id_arguments; String *id___proto__; ExecutionEngine(); @@ -475,7 +488,7 @@ struct ExecutionEngine Object *newErrorObject(const Value &value); Object *newMathObject(Context *ctx); - Object *newArgumentsObject(Context *ctx); + Object *newActivationObject(Context *ctx); }; } // namespace VM diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index e081c3e8d8..5f2411fabe 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -170,7 +170,7 @@ bool Value::isErrorObject() const bool Value::isArgumentsObject() const { - return type == OBJECT_TYPE ? objectValue->asArgumentsObject() != 0 : false; + return type == OBJECT_TYPE ? objectValue->asActivationObject() != 0 : false; } Object *Value::asObject() const @@ -213,9 +213,9 @@ ErrorObject *Value::asErrorObject() const return type == OBJECT_TYPE ? objectValue->asErrorObject() : 0; } -ArgumentsObject *Value::asArgumentsObject() const +ActivationObject *Value::asArgumentsObject() const { - return type == OBJECT_TYPE ? objectValue->asArgumentsObject() : 0; + return type == OBJECT_TYPE ? objectValue->asActivationObject() : 0; } Value Value::property(Context *ctx, String *name) const diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 269ccf0913..7221423f8d 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -48,7 +48,7 @@ struct StringObject; struct DateObject; struct ArrayObject; struct ErrorObject; -struct ArgumentsObject; +struct ActivationObject; extern "C" { @@ -286,7 +286,7 @@ struct Value { DateObject *asDateObject() const; ArrayObject *asArrayObject() const; ErrorObject *asErrorObject() const; - ArgumentsObject *asArgumentsObject() const; + ActivationObject *asArgumentsObject() const; Value property(Context *ctx, String *name) const; Value *getPropertyDescriptor(Context *ctx, String *name) const; -- cgit v1.2.3 From ec4f98b19141243155a80889116a4c229ca7e937 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 4 Jun 2012 16:45:34 +0200 Subject: Implemented Object.prototype.isPrototypeOf --- qv4ecmaobjects.cpp | 9 ++++++++- tests/prototype.4.js | 9 +++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/prototype.4.js diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 59fcdc7b79..798d763d3a 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -620,7 +620,14 @@ void ObjectPrototype::method_hasOwnProperty(Context *ctx) void ObjectPrototype::method_isPrototypeOf(Context *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.prototype.isPrototypeOf")); + Value V = ctx->argument(0); + if (! V.isObject()) + ctx->result = Value::fromBoolean(false); + else { + Value O = ctx->thisObject.toObject(ctx); + Object *proto = V.objectValue->prototype; + ctx->result = Value::fromBoolean(proto && O.objectValue == proto); + } } void ObjectPrototype::method_propertyIsEnumerable(Context *ctx) diff --git a/tests/prototype.4.js b/tests/prototype.4.js new file mode 100644 index 0000000000..9e4facb540 --- /dev/null +++ b/tests/prototype.4.js @@ -0,0 +1,9 @@ + +function Point(x, y) { + this.x = x + this.y = y +} + +var pt = new Point(10, 20) +print(Point.prototype.isPrototypeOf(pt)) + -- cgit v1.2.3 From c3614c1825d7faccd60e9f2e980ce0ad686dc8a2 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 4 Jun 2012 16:57:08 +0200 Subject: Initial work on Object.prototype.getOwnPropertyNames. At the moment getOwnPropertyNames is not compliant, e.g. we need to add support for property accessors. --- qv4array_p.h | 6 ++++++ qv4ecmaobjects.cpp | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/qv4array_p.h b/qv4array_p.h index c12f3ec546..fac460e706 100644 --- a/qv4array_p.h +++ b/qv4array_p.h @@ -31,6 +31,7 @@ public: inline void splice(double start, double deleteCount, const QVector &items, Array &other); + inline void push(const Value &value); private: std::deque *to_vector; @@ -147,6 +148,11 @@ inline void Array::sort(Context *context, const Value &comparefn) std::sort(to_vector->begin(), to_vector->end(), lessThan); } +inline void Array::push(const Value &value) +{ + to_vector->push_back(value); +} + inline void Array::splice(double start, double deleteCount, const QVector &items, Array &other) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 798d763d3a..0d9cc46872 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -538,7 +538,21 @@ void ObjectPrototype::method_getOwnPropertyDescriptor(Context *ctx) void ObjectPrototype::method_getOwnPropertyNames(Context *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.getOwnPropertyNames")); + Value O = ctx->argument(0); + if (! O.isObject()) + ctx->throwTypeError(); + else { + ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); + Array &a = array->value; + if (Table *members = O.objectValue->members) { + for (Property **it = members->begin(), **end = members->end(); it != end; ++it) { + if (Property *prop = *it) { + a.push(Value::fromString(prop->name)); + } + } + } + ctx->result = Value::fromObject(array); + } } void ObjectPrototype::method_create(Context *ctx) -- cgit v1.2.3 From bf46892ccd34d161fc7053799bd8682cb9711197 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 5 Jun 2012 10:47:04 +0200 Subject: Move the defnition of the QML/JS Context to qmljs_runtime.h --- main.cpp | 4 +- qmljs_objects.cpp | 101 ++--------------------------------------- qmljs_objects.h | 78 +------------------------------- qmljs_runtime.cpp | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++- qmljs_runtime.h | 66 +++++++++++++++++++++++++-- qv4array_p.h | 1 + qv4ecmaobjects.cpp | 8 ++-- 7 files changed, 202 insertions(+), 185 deletions(-) diff --git a/main.cpp b/main.cpp index 3c4b353bdd..525324a9d1 100644 --- a/main.cpp +++ b/main.cpp @@ -38,7 +38,7 @@ struct Print: FunctionObject virtual void call(Context *ctx) { - for (size_t i = 0; i < ctx->argumentCount; ++i) { + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { String *s = ctx->argument(i).toString(ctx); if (i) std::cout << ' '; @@ -111,7 +111,7 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS ctx->locals = new VM::Value[ctx->varCount]; ctx->vars = new VM::String*[ctx->varCount]; std::fill(ctx->locals, ctx->locals + ctx->varCount, VM::Value::undefinedValue()); - for (size_t i = 0; i < ctx->varCount; ++i) + for (unsigned int i = 0; i < ctx->varCount; ++i) ctx->vars[i] = ctx->engine->identifier(*globalCode->locals.at(i)); } diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 6f7964d62a..71a14a265e 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -161,7 +161,7 @@ ScriptFunction::ScriptFunction(Context *scope, IR::Function *function) formalParameterCount = function->formals.size(); if (formalParameterCount) { formalParameterList = new String*[formalParameterCount]; - for (size_t i = 0; i < formalParameterCount; ++i) { + for (unsigned int i = 0; i < formalParameterCount; ++i) { formalParameterList[i] = scope->engine->identifier(*function->formals.at(i)); } } @@ -169,7 +169,7 @@ ScriptFunction::ScriptFunction(Context *scope, IR::Function *function) varCount = function->locals.size(); if (varCount) { varList = new String*[varCount]; - for (size_t i = 0; i < varCount; ++i) { + for (unsigned int i = 0; i < varCount; ++i) { varList[i] = scope->engine->identifier(*function->locals.at(i)); } } @@ -199,7 +199,7 @@ void ScriptFunction::construct(VM::Context *ctx) Value *ActivationObject::getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes) { if (context) { - for (size_t i = 0; i < context->varCount; ++i) { + for (unsigned int i = 0; i < context->varCount; ++i) { String *var = context->vars[i]; if (__qmljs_string_equal(context, var, name)) { if (attributes) @@ -207,7 +207,7 @@ Value *ActivationObject::getPropertyDescriptor(Context *ctx, String *name, Prope return &context->locals[i]; } } - for (size_t i = 0; i < context->formalCount; ++i) { + for (unsigned int i = 0; i < context->formalCount; ++i) { String *formal = context->formals[i]; if (__qmljs_string_equal(context, formal, name)) { if (attributes) @@ -474,96 +474,3 @@ Object *ExecutionEngine::newActivationObject(Context *ctx) { return new ActivationObject(ctx); } - -void Context::throwError(const Value &value) -{ - result = value; - hasUncaughtException = true; -} - -void Context::throwError(const QString &message) -{ - Value v = Value::fromString(this, message); - throwError(Value::fromObject(engine->newErrorObject(v))); -} - -void Context::throwTypeError() -{ - Value v = Value::fromString(this, QStringLiteral("Type error")); - throwError(Value::fromObject(engine->newErrorObject(v))); -} - -void Context::throwUnimplemented(const QString &message) -{ - Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message); - throwError(Value::fromObject(engine->newErrorObject(v))); -} - -void Context::throwReferenceError(const Value &value) -{ - String *s = value.toString(this); - QString msg = s->toQString() + QStringLiteral(" is not defined"); - throwError(Value::fromObject(engine->newErrorObject(Value::fromString(this, msg)))); -} - -void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc) -{ - engine = e; - parent = f->scope; - - if (f->needsActivation) - __qmljs_init_object(&activation, engine->newActivationObject(this)); - else - __qmljs_init_null(&activation); - - if (object) - thisObject = *object; - else - __qmljs_init_null(&thisObject); - - formals = f->formalParameterList; - formalCount = f->formalParameterCount; - arguments = args; - argumentCount = argc; - if (argc && f->needsActivation) { - arguments = new Value[argc]; - std::copy(args, args + argc, arguments); - } - vars = f->varList; - varCount = f->varCount; - locals = varCount ? new Value[varCount] : 0; - hasUncaughtException = false; - calledAsConstructor = false; - if (varCount) - std::fill(locals, locals + varCount, Value::undefinedValue()); -} - -void Context::leaveCallContext(FunctionObject *f, Value *returnValue) -{ - if (returnValue) - __qmljs_copy(returnValue, &result); - - if (! f->needsActivation) { - delete[] locals; - locals = 0; - } -} - -void Context::initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc) -{ - initCallContext(e, object, f, args, argc); - calledAsConstructor = true; -} - -void Context::leaveConstructorContext(FunctionObject *f, Value *returnValue) -{ - assert(thisObject.is(OBJECT_TYPE)); - result = thisObject; - - Value proto = f->getProperty(this, engine->id_prototype); - thisObject.objectValue->prototype = proto.objectValue; - if (! thisObject.isObject()) - thisObject.objectValue->prototype = engine->objectPrototype; - - leaveCallContext(f, returnValue); -} diff --git a/qmljs_objects.h b/qmljs_objects.h index 1767a12328..ca6997b66b 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -276,9 +276,9 @@ struct ArrayObject: Object { struct FunctionObject: Object { Context *scope; String **formalParameterList; - size_t formalParameterCount; + unsigned int formalParameterCount; String **varList; - size_t varCount; + unsigned int varCount; bool needsActivation; FunctionObject(Context *scope) @@ -346,80 +346,6 @@ struct ArgumentsObject: Object { virtual Value *getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes); }; -struct Context { - ExecutionEngine *engine; - Context *parent; - Value activation; - Value thisObject; - Value *arguments; - size_t argumentCount; - Value *locals; - Value result; - String **formals; - size_t formalCount; - String **vars; - size_t varCount; - int calledAsConstructor; - int hasUncaughtException; - - Value *lookupPropertyDescriptor(String *name) - { - for (Context *ctx = this; ctx; ctx = ctx->parent) { - if (ctx->activation.is(OBJECT_TYPE)) { - if (Value *prop = ctx->activation.objectValue->getPropertyDescriptor(this, name)) { - return prop; - } - } - } - return 0; - } - - inline Value argument(size_t index = 0) - { - Value arg; - getArgument(&arg, index); - return arg; - } - - inline void getArgument(Value *result, size_t index) - { - if (index < argumentCount) - *result = arguments[index]; - else - __qmljs_init_undefined(result); - } - - void init(ExecutionEngine *eng) - { - engine = eng; - parent = 0; - arguments = 0; - argumentCount = 0; - locals = 0; - activation.type = NULL_TYPE; - thisObject.type = NULL_TYPE; - result.type = UNDEFINED_TYPE; - formals = 0; - formalCount = 0; - vars = 0; - varCount = 0; - calledAsConstructor = false; - hasUncaughtException = false; - } - - void throwError(const Value &value); - void throwError(const QString &message); - void throwTypeError(); - void throwUnimplemented(const QString &message); - void throwReferenceError(const Value &value); - - void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc); - void leaveCallContext(FunctionObject *f, Value *r); - - void initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc); - void leaveConstructorContext(FunctionObject *f, Value *returnValue); -}; - struct ExecutionEngine { Context *rootContext; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 5f2411fabe..5687d9ad9d 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -2,6 +2,8 @@ #include "qmljs_runtime.h" #include "qmljs_objects.h" #include "qv4ir_p.h" +#include "qv4ecmaobjects_p.h" + #include #include #include @@ -71,7 +73,7 @@ int Value::toInt32(double number) return (int) trunc(number); // ### } -uint Value::toUInt32(double number) +unsigned int Value::toUInt32(double number) { if (! number || isnan(number) || isinf(number)) return +0; @@ -98,7 +100,7 @@ int Value::toInt32(Context *ctx) return __qmljs_to_int32(ctx, this); } -uint Value::toUInt32(Context *ctx) +unsigned int Value::toUInt32(Context *ctx) { return __qmljs_to_int32(ctx, this); } @@ -228,6 +230,129 @@ Value *Value::getPropertyDescriptor(Context *ctx, String *name) const return isObject() ? objectValue->getPropertyDescriptor(ctx, name) : 0; } +void Context::init(ExecutionEngine *eng) +{ + engine = eng; + parent = 0; + arguments = 0; + argumentCount = 0; + locals = 0; + activation.type = NULL_TYPE; + thisObject.type = NULL_TYPE; + result.type = UNDEFINED_TYPE; + formals = 0; + formalCount = 0; + vars = 0; + varCount = 0; + calledAsConstructor = false; + hasUncaughtException = false; +} + +Value *Context::lookupPropertyDescriptor(String *name) +{ + for (Context *ctx = this; ctx; ctx = ctx->parent) { + if (ctx->activation.is(OBJECT_TYPE)) { + if (Value *prop = ctx->activation.objectValue->getPropertyDescriptor(this, name)) { + return prop; + } + } + } + return 0; +} + +void Context::throwError(const Value &value) +{ + result = value; + hasUncaughtException = true; +} + +void Context::throwError(const QString &message) +{ + Value v = Value::fromString(this, message); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void Context::throwTypeError() +{ + Value v = Value::fromString(this, QStringLiteral("Type error")); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void Context::throwUnimplemented(const QString &message) +{ + Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void Context::throwReferenceError(const Value &value) +{ + String *s = value.toString(this); + QString msg = s->toQString() + QStringLiteral(" is not defined"); + throwError(Value::fromObject(engine->newErrorObject(Value::fromString(this, msg)))); +} + +void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc) +{ + engine = e; + parent = f->scope; + + if (f->needsActivation) + __qmljs_init_object(&activation, engine->newActivationObject(this)); + else + __qmljs_init_null(&activation); + + if (object) + thisObject = *object; + else + __qmljs_init_null(&thisObject); + + formals = f->formalParameterList; + formalCount = f->formalParameterCount; + arguments = args; + argumentCount = argc; + if (argc && f->needsActivation) { + arguments = new Value[argc]; + std::copy(args, args + argc, arguments); + } + vars = f->varList; + varCount = f->varCount; + locals = varCount ? new Value[varCount] : 0; + hasUncaughtException = false; + calledAsConstructor = false; + if (varCount) + std::fill(locals, locals + varCount, Value::undefinedValue()); +} + +void Context::leaveCallContext(FunctionObject *f, Value *returnValue) +{ + if (returnValue) + __qmljs_copy(returnValue, &result); + + if (! f->needsActivation) { + delete[] locals; + locals = 0; + } +} + +void Context::initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc) +{ + initCallContext(e, object, f, args, argc); + calledAsConstructor = true; +} + +void Context::leaveConstructorContext(FunctionObject *f, Value *returnValue) +{ + assert(thisObject.is(OBJECT_TYPE)); + result = thisObject; + + Value proto = f->getProperty(this, engine->id_prototype); + thisObject.objectValue->prototype = proto.objectValue; + if (! thisObject.isObject()) + thisObject.objectValue->prototype = engine->objectPrototype; + + leaveCallContext(f, returnValue); +} + extern "C" { void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 7221423f8d..daf125b282 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -1,8 +1,10 @@ #ifndef QMLJS_RUNTIME_H #define QMLJS_RUNTIME_H -#include -#include +#ifndef QMLJS_LLVM_RUNTIME +# include +#endif + #include #include @@ -49,6 +51,7 @@ struct DateObject; struct ArrayObject; struct ErrorObject; struct ActivationObject; +struct ExecutionEngine; extern "C" { @@ -246,15 +249,17 @@ struct Value { return v; } +#ifndef QMLJS_LLVM_RUNTIME static Value fromString(Context *ctx, const QString &fromString); +#endif static int toInteger(double fromNumber); static int toInt32(double value); - static uint toUInt32(double value); + static unsigned int toUInt32(double value); int toUInt16(Context *ctx); int toInt32(Context *ctx); - uint toUInt32(Context *ctx); + unsigned int toUInt32(Context *ctx); bool toBoolean(Context *ctx) const; double toInteger(Context *ctx) const; double toNumber(Context *ctx) const; @@ -292,6 +297,59 @@ struct Value { Value *getPropertyDescriptor(Context *ctx, String *name) const; }; +struct Context { + ExecutionEngine *engine; + Context *parent; + Value activation; + Value thisObject; + Value *arguments; + unsigned int argumentCount; + Value *locals; + Value result; + String **formals; + unsigned int formalCount; + String **vars; + unsigned int varCount; + int calledAsConstructor; + int hasUncaughtException; + + Value *lookupPropertyDescriptor(String *name); + + inline Value argument(unsigned int index = 0) + { + Value arg; + getArgument(&arg, index); + return arg; + } + + inline void getArgument(Value *result, unsigned int index) + { + if (index < argumentCount) + *result = arguments[index]; + else + __qmljs_init_undefined(result); + } + + void init(ExecutionEngine *eng); + + void throwError(const Value &value); + void throwTypeError(); + void throwReferenceError(const Value &value); + +#ifndef QMLJS_LLVM_RUNTIME + void throwError(const QString &message); + void throwUnimplemented(const QString &message); +#endif + + void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc); + void leaveCallContext(FunctionObject *f, Value *r); + + void initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc); + void leaveConstructorContext(FunctionObject *f, Value *returnValue); +}; + + + extern "C" { // constructors diff --git a/qv4array_p.h b/qv4array_p.h index fac460e706..01b3402883 100644 --- a/qv4array_p.h +++ b/qv4array_p.h @@ -2,6 +2,7 @@ #define QV4ARRAY_P_H #include "qmljs_runtime.h" +#include #include #include diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 0d9cc46872..78db86b13c 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1217,7 +1217,7 @@ void ArrayCtor::call(Context *ctx) value.resize(isize); } else { - for (size_t i = 0; i < ctx->argumentCount; ++i) { + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { value.assign(i, ctx->argument(i)); } } @@ -1371,7 +1371,7 @@ void ArrayPrototype::method_push(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { uint pos = instance->value.size(); - for (size_t i = 0; i < ctx->argumentCount; ++i) { + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { Value val = ctx->argument(i); instance->value.assign(pos++, val); } @@ -1379,7 +1379,7 @@ void ArrayPrototype::method_push(Context *ctx) } else { Value r1 = self.property(ctx, ctx->engine->id_length); quint32 n = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; - for (size_t index = 0; index < ctx->argumentCount; ++index, ++n) { + for (unsigned int index = 0; index < ctx->argumentCount; ++index, ++n) { Value r3 = ctx->argument(index); String *name = Value::fromNumber(n).toString(ctx); self.objectValue->setProperty(ctx, name, r3); @@ -1465,7 +1465,7 @@ void ArrayPrototype::method_splice(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { QVector items; - for (size_t i = 2; i < ctx->argumentCount; ++i) + for (unsigned int i = 2; i < ctx->argumentCount; ++i) items << ctx->argument(i); ArrayObject *otherInstance = a.asArrayObject(); assert(otherInstance); -- cgit v1.2.3 From 9551c6527abb7da3ed72034b1e9eb366e334b940 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 5 Jun 2012 12:15:41 +0200 Subject: Some more work on the LLVM-based AOT compiler. --- llvm_runtime.cpp | 27 +++++++++++++ main.cpp | 61 +++++++++++++++++++++++----- qmljs_runtime.cpp | 5 +++ qmljs_runtime.h | 1 + qv4isel_llvm.cpp | 116 +++++++++++++++++++++++++++++++++++++++++------------- qv4isel_llvm_p.h | 5 ++- v4.pro | 15 +++++-- 7 files changed, 188 insertions(+), 42 deletions(-) create mode 100644 llvm_runtime.cpp diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp new file mode 100644 index 0000000000..44670b5ecf --- /dev/null +++ b/llvm_runtime.cpp @@ -0,0 +1,27 @@ +#include "qmljs_runtime.h" + +using namespace QQmlJS::VM; + +extern "C" { + +void __qmljs_llvm_return(Context *ctx, Value *result) +{ + ctx->result = *result; +} + +Value *__qmljs_llvm_get_argument(Context *ctx, int index) +{ + return &ctx->arguments[index]; +} + +void __qmljs_llvm_init_number(Value *result, double value) +{ + __qmljs_init_number(result, value); +} + +void __qmljs_llvm_init_string(Context *ctx, Value *result, const char *str) +{ + __qmljs_init_string(result, __qmljs_string_from_utf8(ctx, str)); +} + +} // extern "C" diff --git a/main.cpp b/main.cpp index 525324a9d1..a7ee7b1cd9 100644 --- a/main.cpp +++ b/main.cpp @@ -15,8 +15,15 @@ #include #include -#ifdef WITH_LLVM +#ifndef QMLJS_NO_LLVM # include "qv4isel_llvm_p.h" + +# include +# include +# include +# include +# include +# include #endif static inline bool protect(const void *addr, size_t size) @@ -50,6 +57,46 @@ struct Print: FunctionObject } // builtins +#ifndef QMLJS_NO_LLVM +void compile(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QString &source) +{ + using namespace QQmlJS; + + IR::Module module; + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(source, 1, false); + Parser parser(engine); + + const bool parsed = parser.parseProgram(); + + foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { + std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn + << ": error: " << qPrintable(m.message) << std::endl; + } + + if (parsed) { + using namespace AST; + Program *program = AST::cast(parser.rootNode()); + + Codegen cg; + /*IR::Function *globalCode =*/ cg(program, &module); + + LLVMInstructionSelection llvmIsel(llvm::getGlobalContext()); + if (llvm::Module *llvmModule = llvmIsel.getLLVMModule(&module)) { + llvm::PassManager PM; + PM.add(llvm::createScalarReplAggregatesPass()); + PM.add(llvm::createInstructionCombiningPass()); + PM.add(llvm::createGlobalOptimizerPass()); + PM.add(llvm::createFunctionInliningPass()); + PM.add(llvm::createPrintModulePass(&llvm::outs())); + PM.run(*llvmModule); + //llvmModule->dump(); + delete llvmModule; + } + } +} +#endif void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QString &source) { @@ -86,14 +133,6 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS foreach (IR::Function *function, module.functions) { isel(function); } - -#ifdef WITH_LLVM - LLVMInstructionSelection llvmIsel(llvm::getGlobalContext()); - if (llvm::Module *llvmModule = llvmIsel.getLLVMModule(&module)) { - llvmModule->dump(); - delete llvmModule; - } -#endif } } @@ -140,7 +179,11 @@ int main(int argc, char *argv[]) const QString code = QString::fromUtf8(file.readAll()); file.close(); QQmlJS::VM::ExecutionEngine vm; +#ifndef QMLJS_NO_LLVM + compile(&vm, fn, code); +#else evaluate(&vm, fn, code); +#endif } } } diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 5687d9ad9d..c1b25ccbd3 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -437,6 +437,11 @@ void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *rig } } +String *__qmljs_string_from_utf8(Context *ctx, const char *s) +{ + return ctx->engine->newString(QString::fromUtf8(s)); +} + int __qmljs_string_length(Context *, String *string) { return string->toQString().length(); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index daf125b282..cb9c614a0d 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -91,6 +91,7 @@ void __qmljs_string_literal_string(Context *ctx, Value *result); void __qmljs_string_literal_function(Context *ctx, Value *result); // strings +String *__qmljs_string_from_utf8(Context *ctx, const char *s); int __qmljs_string_length(Context *ctx, String *string); double __qmljs_string_to_number(Context *ctx, String *string); void __qmljs_string_from_number(Context *ctx, Value *result, double number); diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 982b2db58f..2ac0130ba2 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -2,6 +2,11 @@ #include "qv4isel_llvm_p.h" #include "qv4ir_p.h" +#include +#include +#include +#include + using namespace QQmlJS; LLVMInstructionSelection::LLVMInstructionSelection(llvm::LLVMContext &context) @@ -11,34 +16,53 @@ LLVMInstructionSelection::LLVMInstructionSelection(llvm::LLVMContext &context) , _llvmValue(0) , _numberTy(0) , _valueTy(0) - , _contextTy(0) + , _contextPtrTy(0) + , _stringPtrTy(0) , _functionTy(0) , _function(0) , _block(0) { +} + +llvm::Module *LLVMInstructionSelection::getLLVMModule(IR::Module *module) +{ + llvm::Module *llvmModule = new llvm::Module("a.out", getContext()); + qSwap(_llvmModule, llvmModule); + _numberTy = getDoubleTy(); - { - llvm::StructType *ty = llvm::StructType::create(getContext(), llvm::StringRef("Value")); - ty->setBody(getInt32Ty(), llvm::StructType::get(_numberTy, NULL), NULL); - _valueTy = ty; + std::string err; + + llvm::OwningPtr buffer; + llvm::error_code ec = llvm::MemoryBuffer::getFile(llvm::StringRef("llvm_runtime.bc"), buffer); + if (ec) { + qWarning() << ec.message().c_str(); + assert(!"cannot load QML/JS LLVM runtime, you can generate the runtime with the command `make llvm_runtime'"); } - { - // ### we use a pointer for now - _contextTy = getInt1Ty()->getPointerTo(); + llvm::Module *llvmRuntime = llvm::getLazyBitcodeModule(buffer.get(), getContext(), &err); + if (! err.empty()) { + qWarning() << err.c_str(); + assert(!"cannot load QML/JS LLVM runtime"); + } + + err.clear(); + llvm::Linker::LinkModules(_llvmModule, llvmRuntime, llvm::Linker::DestroySource, &err); + if (! err.empty()) { + qWarning() << err.c_str(); + assert(!"cannot link the QML/JS LLVM runtime"); } + _valueTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::Value"); + _contextPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::Context")->getPointerTo(); + _stringPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::String")->getPointerTo(); + { - llvm::Type *args[] = { _contextTy }; + llvm::Type *args[] = { _contextPtrTy }; _functionTy = llvm::FunctionType::get(getVoidTy(), llvm::makeArrayRef(args), false); } -} -llvm::Module *LLVMInstructionSelection::getLLVMModule(IR::Module *module) -{ - llvm::Module *llvmModule = new llvm::Module("a.out", getContext()); - qSwap(_llvmModule, llvmModule); + foreach (IR::Function *function, module->functions) (void) getLLVMFunction(function); qSwap(_llvmModule, llvmModule); @@ -48,7 +72,7 @@ llvm::Module *LLVMInstructionSelection::getLLVMModule(IR::Module *module) llvm::Function *LLVMInstructionSelection::getLLVMFunction(IR::Function *function) { llvm::Function *llvmFunction = - llvm::Function::Create(_functionTy, llvm::Function::InternalLinkage, + llvm::Function::Create(_functionTy, llvm::Function::ExternalLinkage, // ### make it internal llvm::Twine(function->name ? qPrintable(*function->name) : 0), _llvmModule); QHash blockMap; @@ -124,14 +148,25 @@ llvm::Value *LLVMInstructionSelection::getLLVMCondition(IR::Expr *expr) llvm::Value *LLVMInstructionSelection::getLLVMTemp(IR::Temp *temp) { if (temp->index < 0) { - // it's an actual argument - Q_UNIMPLEMENTED(); - return 0; + const int index = -temp->index -1; + return CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_argument"), + _llvmFunction->arg_begin(), getInt32(index)); } return _tempMap[temp->index]; } +llvm::Value *LLVMInstructionSelection::getStringPtr(const QString &s) +{ + llvm::Value *&value = _stringMap[s]; + if (! value) { + const QByteArray bytes = s.toUtf8(); + value = CreateGlobalStringPtr(llvm::StringRef(bytes.constData(), bytes.size())); + _stringMap[s] = value; + } + return value; +} + void LLVMInstructionSelection::visitExp(IR::Exp *s) { getLLVMValue(s->expr); @@ -139,18 +174,26 @@ void LLVMInstructionSelection::visitExp(IR::Exp *s) void LLVMInstructionSelection::visitEnter(IR::Enter *) { + Q_UNREACHABLE(); } void LLVMInstructionSelection::visitLeave(IR::Leave *) { + Q_UNREACHABLE(); } void LLVMInstructionSelection::visitMove(IR::Move *s) { if (IR::Temp *t = s->target->asTemp()) { - if (llvm::Value *target = getLLVMTemp(t)) { - return; + llvm::Value *target = getLLVMTemp(t); + llvm::Value *source = getLLVMValue(s->source); + assert(source); + if (source->getType()->getPointerTo() != target->getType()) { + source->dump(); + assert(!"not cool"); } + CreateStore(source, target); + return; } Q_UNIMPLEMENTED(); } @@ -164,23 +207,35 @@ void LLVMInstructionSelection::visitCJump(IR::CJump *s) { CreateCondBr(getLLVMCondition(s->cond), getLLVMBasicBlock(s->iftrue), - getLLVMBasicBlock(_function->basicBlocks.at(_block->index + 1))); + getLLVMBasicBlock(s->iffalse)); } void LLVMInstructionSelection::visitRet(IR::Ret *s) { - CreateRet(getLLVMValue(s->expr)); + IR::Temp *t = s->expr->asTemp(); + assert(t != 0); + llvm::Value *result = getLLVMTemp(t); + llvm::Value *ctx = _llvmFunction->arg_begin(); + CreateCall2(_llvmModule->getFunction("__qmljs_llvm_return"), ctx, result); + CreateRetVoid(); } void LLVMInstructionSelection::visitConst(IR::Const *e) { - _llvmValue = llvm::ConstantFP::get(_numberTy, e->value); + llvm::Value *k = llvm::ConstantFP::get(_numberTy, e->value); + llvm::Value *tmp = CreateAlloca(_valueTy); + CreateCall2(_llvmModule->getFunction("__qmljs_llvm_init_number"), tmp, k); + _llvmValue = CreateLoad(tmp); } -void LLVMInstructionSelection::visitString(IR::String *) +void LLVMInstructionSelection::visitString(IR::String *e) { - Q_UNIMPLEMENTED(); + llvm::Value *tmp = CreateAlloca(_valueTy); + CreateCall3(_llvmModule->getFunction("__qmljs_llvm_init_string"), + _llvmFunction->arg_begin(), tmp, + getStringPtr(*e->value)); + _llvmValue = CreateLoad(tmp); } void LLVMInstructionSelection::visitName(IR::Name *) @@ -200,23 +255,28 @@ void LLVMInstructionSelection::visitClosure(IR::Closure *) Q_UNIMPLEMENTED(); } -void LLVMInstructionSelection::visitUnop(IR::Unop *) +void LLVMInstructionSelection::visitUnop(IR::Unop *e) { + llvm::Value *expr = getLLVMValue(e->expr); Q_UNIMPLEMENTED(); } void LLVMInstructionSelection::visitBinop(IR::Binop *e) { + llvm::Value *left = getLLVMValue(e->left); + llvm::Value *right = getLLVMValue(e->right); Q_UNIMPLEMENTED(); } -void LLVMInstructionSelection::visitCall(IR::Call *) +void LLVMInstructionSelection::visitCall(IR::Call *e) { + llvm::Value *base = getLLVMValue(e->base); Q_UNIMPLEMENTED(); } -void LLVMInstructionSelection::visitNew(IR::New *) +void LLVMInstructionSelection::visitNew(IR::New *e) { + llvm::Value *base = getLLVMValue(e->base); Q_UNIMPLEMENTED(); } diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 8c0a5af28f..02eb25e151 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -22,6 +22,7 @@ public: llvm::Value *getLLVMValue(IR::Expr *expr); llvm::Value *getLLVMCondition(IR::Expr *expr); llvm::Value *getLLVMTemp(IR::Temp *temp); + llvm::Value *getStringPtr(const QString &s); virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); @@ -49,13 +50,15 @@ private: llvm::Value *_llvmValue; llvm::Type *_numberTy; llvm::Type *_valueTy; - llvm::Type *_contextTy; + llvm::Type *_contextPtrTy; + llvm::Type *_stringPtrTy; llvm::FunctionType *_functionTy; IR::Function *_function; IR::BasicBlock *_block; QHash _functionMap; QHash _blockMap; QVector _tempMap; + QHash _stringMap; }; } // end of namespace QQmlJS diff --git a/v4.pro b/v4.pro index dc05adb17a..d0302fbf04 100644 --- a/v4.pro +++ b/v4.pro @@ -40,9 +40,6 @@ SOURCES += \ HEADERS += \ qv4isel_llvm_p.h -DEFINES += \ - WITH_LLVM - INCLUDEPATH += \ $$system(llvm-config --includedir) @@ -53,6 +50,16 @@ DEFINES += \ LIBS += \ $$system(llvm-config --ldflags) \ - $$system(llvm-config --libs core) + $$system(llvm-config --libs core jit bitreader linker ipo) + +QMAKE_EXTRA_TARGETS += gen_llvm_runtime + +gen_llvm_runtime.target = llvm_runtime +gen_llvm_runtime.commands = clang -emit-llvm -c -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o llvm_runtime.bc + + +} else { + +DEFINES += QMLJS_NO_LLVM } -- cgit v1.2.3 From 437c25fe33f766844ad297fb2e4135c22af042a6 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 5 Jun 2012 13:15:03 +0200 Subject: Generate LLVM code for the conditional jumps. --- llvm_runtime.cpp | 5 +++++ qv4isel_llvm.cpp | 20 +++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 44670b5ecf..76ae784cc7 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -24,4 +24,9 @@ void __qmljs_llvm_init_string(Context *ctx, Value *result, const char *str) __qmljs_init_string(result, __qmljs_string_from_utf8(ctx, str)); } +bool __qmljs_llvm_to_boolean(Context *ctx, const Value *value) +{ + return __qmljs_to_boolean(ctx, value); +} + } // extern "C" diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 2ac0130ba2..8b72cf8239 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -135,14 +135,24 @@ llvm::Value *LLVMInstructionSelection::getLLVMValue(IR::Expr *expr) llvm::Value *LLVMInstructionSelection::getLLVMCondition(IR::Expr *expr) { - if (llvm::Value *value = getLLVMValue(expr)) { - if (value->getType() == getInt1Ty()) { - return value; + llvm::Value *value = 0; + if (IR::Temp *t = expr->asTemp()) { + value = getLLVMTemp(t); + } else { + value = getLLVMValue(expr); + if (! value) { + Q_UNIMPLEMENTED(); + return getInt1(false); } + + llvm::Value *tmp = CreateAlloca(_valueTy); + CreateStore(value, tmp); + value = tmp; } - Q_UNIMPLEMENTED(); - return getInt1(false); + return CreateCall2(_llvmModule->getFunction("__qmljs_llvm_to_boolean"), + _llvmFunction->arg_begin(), + value); } llvm::Value *LLVMInstructionSelection::getLLVMTemp(IR::Temp *temp) -- cgit v1.2.3 From a48ece9a5cfb54a073aae7de8717eea1f1b357fb Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 5 Jun 2012 14:30:04 +0200 Subject: Generate LLVM code for the binary expressions --- llvm_runtime.cpp | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4isel_llvm.cpp | 57 ++++++++++++++++++++++++++++-- qv4isel_llvm_p.h | 1 + 3 files changed, 161 insertions(+), 3 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 76ae784cc7..66fcf3c830 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -29,4 +29,110 @@ bool __qmljs_llvm_to_boolean(Context *ctx, const Value *value) return __qmljs_to_boolean(ctx, value); } +void __qmljs_llvm_bit_and(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_bit_and(ctx, result, left, right); +} + +void __qmljs_llvm_bit_or(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_bit_or(ctx, result, left, right); +} + +void __qmljs_llvm_bit_xor(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_bit_xor(ctx, result, left, right); +} + +void __qmljs_llvm_add(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_add(ctx, result, left, right); +} + +void __qmljs_llvm_sub(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_sub(ctx, result, left, right); +} + +void __qmljs_llvm_mul(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_mul(ctx, result, left, right); +} + +void __qmljs_llvm_div(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_div(ctx, result, left, right); +} + +void __qmljs_llvm_mod(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_mod(ctx, result, left, right); +} + +void __qmljs_llvm_shl(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_shl(ctx, result, left, right); +} + +void __qmljs_llvm_shr(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_shr(ctx, result, left, right); +} + +void __qmljs_llvm_ushr(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_ushr(ctx, result, left, right); +} + +void __qmljs_llvm_gt(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_gt(ctx, result, left, right); +} + +void __qmljs_llvm_lt(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_lt(ctx, result, left, right); +} + +void __qmljs_llvm_ge(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_ge(ctx, result, left, right); +} + +void __qmljs_llvm_le(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_le(ctx, result, left, right); +} + +void __qmljs_llvm_eq(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_eq(ctx, result, left, right); +} + +void __qmljs_llvm_ne(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_ne(ctx, result, left, right); +} + +void __qmljs_llvm_se(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_se(ctx, result, left, right); +} + +void __qmljs_llvm_sne(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_sne(ctx, result, left, right); +} + +void __qmljs_llvm_instanceof(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_instanceof(ctx, result, left, right); +} + +void __qmljs_llvm_in(Context *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_in(ctx, result, left, right); +} + + } // extern "C" diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 8b72cf8239..1557d7ae67 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -273,9 +273,60 @@ void LLVMInstructionSelection::visitUnop(IR::Unop *e) void LLVMInstructionSelection::visitBinop(IR::Binop *e) { - llvm::Value *left = getLLVMValue(e->left); - llvm::Value *right = getLLVMValue(e->right); - Q_UNIMPLEMENTED(); + llvm::Value *result = CreateAlloca(_valueTy); + genBinop(result, e); + _llvmValue = CreateLoad(result); +} + +void LLVMInstructionSelection::genBinop(llvm::Value *result, IR::Binop *e) +{ + IR::Temp *t1 = e->left->asTemp(); + IR::Temp *t2 = e->right->asTemp(); + assert(t1 != 0); + assert(t2 != 0); + + llvm::Value *left = getLLVMTemp(t1); + llvm::Value *right = getLLVMTemp(t2); + llvm::Value *op = 0; + switch (e->op) { + case IR::OpInvalid: + case IR::OpIfTrue: + case IR::OpNot: + case IR::OpUMinus: + case IR::OpUPlus: + case IR::OpCompl: + Q_UNREACHABLE(); + break; + + case IR::OpBitAnd: op = _llvmModule->getFunction("__qmljs_llvm_bit_and"); break; + case IR::OpBitOr: op = _llvmModule->getFunction("__qmljs_llvm_bit_or"); break; + case IR::OpBitXor: op = _llvmModule->getFunction("__qmljs_llvm_bit_xor"); break; + case IR::OpAdd: op = _llvmModule->getFunction("__qmljs_llvm_add"); break; + case IR::OpSub: op = _llvmModule->getFunction("__qmljs_llvm_sub"); break; + case IR::OpMul: op = _llvmModule->getFunction("__qmljs_llvm_mul"); break; + case IR::OpDiv: op = _llvmModule->getFunction("__qmljs_llvm_div"); break; + case IR::OpMod: op = _llvmModule->getFunction("__qmljs_llvm_mod"); break; + case IR::OpLShift: op = _llvmModule->getFunction("__qmljs_llvm_shl"); break; + case IR::OpRShift: op = _llvmModule->getFunction("__qmljs_llvm_shr"); break; + case IR::OpURShift: op = _llvmModule->getFunction("__qmljs_llvm_ushr"); break; + case IR::OpGt: op = _llvmModule->getFunction("__qmljs_llvm_gt"); break; + case IR::OpLt: op = _llvmModule->getFunction("__qmljs_llvm_lt"); break; + case IR::OpGe: op = _llvmModule->getFunction("__qmljs_llvm_ge"); break; + case IR::OpLe: op = _llvmModule->getFunction("__qmljs_llvm_le"); break; + case IR::OpEqual: op = _llvmModule->getFunction("__qmljs_llvm_eq"); break; + case IR::OpNotEqual: op = _llvmModule->getFunction("__qmljs_llvm_ne"); break; + case IR::OpStrictEqual: op = _llvmModule->getFunction("__qmljs_llvm_se"); break; + case IR::OpStrictNotEqual: op = _llvmModule->getFunction("__qmljs_llvm_sne"); break; + case IR::OpInstanceof: op = _llvmModule->getFunction("__qmljs_llvm_instanceof"); break; + case IR::OpIn: op = _llvmModule->getFunction("__qmljs_llvm_in"); break; + + case IR::OpAnd: + case IR::OpOr: + Q_UNREACHABLE(); + break; + } + + CreateCall4(op, _llvmFunction->arg_begin(), result, left, right); } void LLVMInstructionSelection::visitCall(IR::Call *e) diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 02eb25e151..2f89d2e597 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -23,6 +23,7 @@ public: llvm::Value *getLLVMCondition(IR::Expr *expr); llvm::Value *getLLVMTemp(IR::Temp *temp); llvm::Value *getStringPtr(const QString &s); + void genBinop(llvm::Value *result, IR::Binop *e); virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); -- cgit v1.2.3 From 7f3a694adce3c971cca5081ed9b8c737adfb0948 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 5 Jun 2012 15:03:49 +0200 Subject: LLVM code for unary expressions --- llvm_runtime.cpp | 19 +++++++++++++++++++ qv4isel_llvm.cpp | 27 +++++++++++++++++++++++++-- qv4isel_llvm_p.h | 1 + 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 66fcf3c830..7f5593baea 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -134,5 +134,24 @@ void __qmljs_llvm_in(Context *ctx, Value *result, Value *left, Value *right) __qmljs_in(ctx, result, left, right); } +void __qmljs_llvm_uplus(Context *ctx, Value *result, const Value *value) +{ + __qmljs_uplus(ctx, result, value); +} + +void __qmljs_llvm_uminus(Context *ctx, Value *result, const Value *value) +{ + __qmljs_uminus(ctx, result, value); +} + +void __qmljs_llvm_compl(Context *ctx, Value *result, const Value *value) +{ + __qmljs_compl(ctx, result, value); +} + +void __qmljs_llvm_not(Context *ctx, Value *result, const Value *value) +{ + __qmljs_not(ctx, result, value); +} } // extern "C" diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 1557d7ae67..1d4f412f0e 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -267,8 +267,9 @@ void LLVMInstructionSelection::visitClosure(IR::Closure *) void LLVMInstructionSelection::visitUnop(IR::Unop *e) { - llvm::Value *expr = getLLVMValue(e->expr); - Q_UNIMPLEMENTED(); + llvm::Value *result = CreateAlloca(_valueTy); + genUnop(result, e); + _llvmValue = CreateLoad(result); } void LLVMInstructionSelection::visitBinop(IR::Binop *e) @@ -278,6 +279,28 @@ void LLVMInstructionSelection::visitBinop(IR::Binop *e) _llvmValue = CreateLoad(result); } +void LLVMInstructionSelection::genUnop(llvm::Value *result, IR::Unop *e) +{ + IR::Temp *t = e->expr->asTemp(); + assert(t != 0); + + llvm::Value *expr = getLLVMTemp(t); + llvm::Value *op = 0; + + switch (e->op) { + default: + Q_UNREACHABLE(); + break; + + case IR::OpNot: _llvmModule->getFunction("__qmljs_llvm_not"); break; + case IR::OpUMinus: _llvmModule->getFunction("__qmljs_llvm_uminus"); break; + case IR::OpUPlus: _llvmModule->getFunction("__qmljs_llvm_uplus"); break; + case IR::OpCompl: _llvmModule->getFunction("__qmljs_llvm_compl"); break; + } + + CreateCall3(op, _llvmFunction->arg_begin(), result, expr); +} + void LLVMInstructionSelection::genBinop(llvm::Value *result, IR::Binop *e) { IR::Temp *t1 = e->left->asTemp(); diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 2f89d2e597..b343045bc0 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -23,6 +23,7 @@ public: llvm::Value *getLLVMCondition(IR::Expr *expr); llvm::Value *getLLVMTemp(IR::Temp *temp); llvm::Value *getStringPtr(const QString &s); + void genUnop(llvm::Value *result, IR::Unop *e); void genBinop(llvm::Value *result, IR::Binop *e); virtual void visitExp(IR::Exp *); -- cgit v1.2.3 From 89bb85c9c4ddf307050d5cb3e9c4749ede078d13 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 5 Jun 2012 15:26:21 +0200 Subject: Start generating LLVM code for IR::Call nodes. --- llvm_runtime.cpp | 10 ++++++++++ qv4isel_llvm.cpp | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 7f5593baea..288f99a890 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -154,4 +154,14 @@ void __qmljs_llvm_not(Context *ctx, Value *result, const Value *value) __qmljs_not(ctx, result, value); } +String *__qmljs_llvm_get_identifier(Context *ctx, const char *str) +{ + return __qmljs_string_from_utf8(ctx, str); // ### make it unique +} + +void __qmljs_llvm_call_activation_property(Context *context, Value *result, String *name, Value *args, int argc) +{ + __qmljs_call_activation_property(context, result, name, args, argc); +} + } // extern "C" diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 1d4f412f0e..08d2738c8b 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace QQmlJS; @@ -354,8 +355,37 @@ void LLVMInstructionSelection::genBinop(llvm::Value *result, IR::Binop *e) void LLVMInstructionSelection::visitCall(IR::Call *e) { - llvm::Value *base = getLLVMValue(e->base); - Q_UNIMPLEMENTED(); + llvm::Value *func = 0; + llvm::Value *base = 0; + if (IR::Temp *t = e->asTemp()) { + base = getLLVMTemp(t); + func = _llvmModule->getFunction("__qmljs_llvm_call_value"); + } else if (IR::Name *n = e->asName()) { + llvm::Value *str = getStringPtr(*n->id); + base = CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_identifier"), + _llvmFunction->arg_begin(), str); + func = _llvmModule->getFunction("__qmljs_llvm_call_activation_property"); + } + + int argc = 0; + for (IR::ExprList *it = e->args; it; it = it->next) { + ++argc; + } + + llvm::Value *args = argc ? CreateAlloca(_valueTy, getInt32(argc)) : 0; + + int i = 0; + for (IR::ExprList *it = e->args; it; it = it->next) { + llvm::Value *arg = getLLVMValue(it->expr); + CreateStore(arg, CreateConstGEP1_32(args, i++)); + } + + if (func) { + llvm::Value *result = llvm::Constant::getNullValue(_valueTy); + CreateCall5(func, _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); + } else { + Q_UNIMPLEMENTED(); + } } void LLVMInstructionSelection::visitNew(IR::New *e) -- cgit v1.2.3 From 0829b00d5771a32860242063c82bc120fe65c7a9 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 5 Jun 2012 15:54:37 +0200 Subject: Fix code generation for IR::Call nodes. --- qv4isel_llvm.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 08d2738c8b..64ca9a9554 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -357,14 +357,16 @@ void LLVMInstructionSelection::visitCall(IR::Call *e) { llvm::Value *func = 0; llvm::Value *base = 0; - if (IR::Temp *t = e->asTemp()) { + if (IR::Temp *t = e->base->asTemp()) { base = getLLVMTemp(t); func = _llvmModule->getFunction("__qmljs_llvm_call_value"); - } else if (IR::Name *n = e->asName()) { - llvm::Value *str = getStringPtr(*n->id); - base = CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_identifier"), - _llvmFunction->arg_begin(), str); - func = _llvmModule->getFunction("__qmljs_llvm_call_activation_property"); + } else if (IR::Name *n = e->base->asName()) { + if (n->id) { + llvm::Value *str = getStringPtr(*n->id); + base = CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_identifier"), + _llvmFunction->arg_begin(), str); + func = _llvmModule->getFunction("__qmljs_llvm_call_activation_property"); + } } int argc = 0; @@ -381,8 +383,9 @@ void LLVMInstructionSelection::visitCall(IR::Call *e) } if (func) { - llvm::Value *result = llvm::Constant::getNullValue(_valueTy); - CreateCall5(func, _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); + llvm::Value *result = CreateAlloca(_valueTy); + CreateStore(llvm::Constant::getNullValue(_valueTy), result); + _llvmValue = CreateCall5(func, _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); } else { Q_UNIMPLEMENTED(); } -- cgit v1.2.3 From b353942bfa05898dceb0ae6ee4ab08ec76c0fc5b Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 5 Jun 2012 18:32:52 +0200 Subject: More work on the AOT. --- llvm_runtime.cpp | 1 + main.cpp | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++----- v4.pro | 5 +++- 3 files changed, 69 insertions(+), 7 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 288f99a890..36a12b86dd 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -1,4 +1,5 @@ #include "qmljs_runtime.h" +#include using namespace QQmlJS::VM; diff --git a/main.cpp b/main.cpp index a7ee7b1cd9..4438eb878a 100644 --- a/main.cpp +++ b/main.cpp @@ -58,7 +58,7 @@ struct Print: FunctionObject } // builtins #ifndef QMLJS_NO_LLVM -void compile(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QString &source) +void compile(const QString &fileName, const QString &source) { using namespace QQmlJS; @@ -91,11 +91,52 @@ void compile(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QSt PM.add(llvm::createFunctionInliningPass()); PM.add(llvm::createPrintModulePass(&llvm::outs())); PM.run(*llvmModule); - //llvmModule->dump(); delete llvmModule; } } } + +int compileFiles(const QStringList &files) +{ + foreach (const QString &fileName, files) { + QFile file(fileName); + if (file.open(QFile::ReadOnly)) { + QString source = QString::fromUtf8(file.readAll()); + compile(fileName, source); + } + } + return 0; +} + +int evaluateCompiledCode(const QStringList &files) +{ + using namespace QQmlJS; + + foreach (const QString &libName, files) { + QLibrary lib(libName); + lib.load(); + QFunctionPointer ptr = lib.resolve("_25_entry"); + void (*code)(VM::Context *) = (void (*)(VM::Context *)) ptr; + + VM::ExecutionEngine vm; + VM::Context *ctx = vm.rootContext; + + QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue; + globalObject->setProperty(ctx, vm.identifier(QStringLiteral("print")), + QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); + + code(ctx); + + if (ctx->hasUncaughtException) { + if (VM::ErrorObject *e = ctx->result.asErrorObject()) + std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; + else + std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; + } + } + return 0; +} + #endif void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QString &source) @@ -173,17 +214,34 @@ int main(int argc, char *argv[]) QStringList args = app.arguments(); args.removeFirst(); +#ifndef QMLJS_NO_LLVM + if (args.isEmpty()) { + std::cerr << "Usage: v4 [--compile|--aot] file..." << std::endl; + return 0; + } + + if (args.first() == QLatin1String("--compile")) { + args.removeFirst(); + return compileFiles(args); + } else if (args.first() == QLatin1String("--aot")) { + args.removeFirst(); + return evaluateCompiledCode(args); + } +#endif + foreach (const QString &fn, args) { QFile file(fn); if (file.open(QFile::ReadOnly)) { const QString code = QString::fromUtf8(file.readAll()); file.close(); QQmlJS::VM::ExecutionEngine vm; -#ifndef QMLJS_NO_LLVM - compile(&vm, fn, code); -#else + QQmlJS::VM::Context *ctx = vm.rootContext; + + QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue; + globalObject->setProperty(ctx, vm.identifier(QStringLiteral("print")), + QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); + evaluate(&vm, fn, code); -#endif } } } diff --git a/v4.pro b/v4.pro index d0302fbf04..58f37128e3 100644 --- a/v4.pro +++ b/v4.pro @@ -7,6 +7,8 @@ DEFINES += __default_codegen__ udis86:LIBS += -ludis86 else:DEFINES += NO_UDIS86 +LIBS += -rdynamic + SOURCES += main.cpp \ qv4codegen.cpp \ qv4ir.cpp \ @@ -16,7 +18,8 @@ SOURCES += main.cpp \ qv4syntaxchecker.cpp \ qv4ecmaobjects.cpp \ qv4array.cpp \ - qv4isel_x86_64.cpp + qv4isel_x86_64.cpp \ + llvm_runtime.cpp HEADERS += \ qv4codegen_p.h \ -- cgit v1.2.3 From 1dc551ae9b5f79f845d77792bd0d18e0fcae78bc Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 09:37:41 +0200 Subject: Fix the return value of calls. --- qv4isel_llvm.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 64ca9a9554..5f186505b0 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -374,7 +374,11 @@ void LLVMInstructionSelection::visitCall(IR::Call *e) ++argc; } - llvm::Value *args = argc ? CreateAlloca(_valueTy, getInt32(argc)) : 0; + llvm::Value *args = 0; + if (argc) + args = CreateAlloca(_valueTy, getInt32(argc)); + else + args = llvm::Constant::getNullValue(_valueTy->getPointerTo()); int i = 0; for (IR::ExprList *it = e->args; it; it = it->next) { @@ -385,7 +389,8 @@ void LLVMInstructionSelection::visitCall(IR::Call *e) if (func) { llvm::Value *result = CreateAlloca(_valueTy); CreateStore(llvm::Constant::getNullValue(_valueTy), result); - _llvmValue = CreateCall5(func, _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); + CreateCall5(func, _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); + _llvmValue = CreateLoad(result); } else { Q_UNIMPLEMENTED(); } -- cgit v1.2.3 From 3631bac985f2d04f78fd1c1e53d5b5b8625a0084 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 10:00:28 +0200 Subject: Move the Alloca instructions at the beginning of the function. --- qv4isel_llvm.cpp | 32 ++++++++++++++++++++++++-------- qv4isel_llvm_p.h | 2 ++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 5f186505b0..f406862da3 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -20,6 +20,7 @@ LLVMInstructionSelection::LLVMInstructionSelection(llvm::LLVMContext &context) , _contextPtrTy(0) , _stringPtrTy(0) , _functionTy(0) + , _allocaInsertPoint(0) , _function(0) , _block(0) { @@ -86,8 +87,13 @@ llvm::Function *LLVMInstructionSelection::getLLVMFunction(IR::Function *function // entry block SetInsertPoint(getLLVMBasicBlock(_function->basicBlocks.first())); + + llvm::Instruction *allocaInsertPoint = new llvm::BitCastInst(llvm::UndefValue::get(getInt32Ty()), + getInt32Ty(), "", GetInsertBlock()); + qSwap(_allocaInsertPoint, allocaInsertPoint); + for (int i = 0; i < _function->tempCount; ++i) { - llvm::AllocaInst *t = CreateAlloca(_valueTy, 0, llvm::StringRef("t")); + llvm::AllocaInst *t = newLLVMTemp(_valueTy); _tempMap.append(t); } @@ -103,6 +109,10 @@ llvm::Function *LLVMInstructionSelection::getLLVMFunction(IR::Function *function qSwap(_block, block); } + qSwap(_allocaInsertPoint, allocaInsertPoint); + + allocaInsertPoint->eraseFromParent(); + qSwap(_blockMap, blockMap); qSwap(_tempMap, tempMap); qSwap(_function, function); @@ -146,7 +156,7 @@ llvm::Value *LLVMInstructionSelection::getLLVMCondition(IR::Expr *expr) return getInt1(false); } - llvm::Value *tmp = CreateAlloca(_valueTy); + llvm::Value *tmp = newLLVMTemp(_valueTy); CreateStore(value, tmp); value = tmp; } @@ -235,14 +245,14 @@ void LLVMInstructionSelection::visitRet(IR::Ret *s) void LLVMInstructionSelection::visitConst(IR::Const *e) { llvm::Value *k = llvm::ConstantFP::get(_numberTy, e->value); - llvm::Value *tmp = CreateAlloca(_valueTy); + llvm::Value *tmp = newLLVMTemp(_valueTy); CreateCall2(_llvmModule->getFunction("__qmljs_llvm_init_number"), tmp, k); _llvmValue = CreateLoad(tmp); } void LLVMInstructionSelection::visitString(IR::String *e) { - llvm::Value *tmp = CreateAlloca(_valueTy); + llvm::Value *tmp = newLLVMTemp(_valueTy); CreateCall3(_llvmModule->getFunction("__qmljs_llvm_init_string"), _llvmFunction->arg_begin(), tmp, getStringPtr(*e->value)); @@ -268,14 +278,14 @@ void LLVMInstructionSelection::visitClosure(IR::Closure *) void LLVMInstructionSelection::visitUnop(IR::Unop *e) { - llvm::Value *result = CreateAlloca(_valueTy); + llvm::Value *result = newLLVMTemp(_valueTy); genUnop(result, e); _llvmValue = CreateLoad(result); } void LLVMInstructionSelection::visitBinop(IR::Binop *e) { - llvm::Value *result = CreateAlloca(_valueTy); + llvm::Value *result = newLLVMTemp(_valueTy); genBinop(result, e); _llvmValue = CreateLoad(result); } @@ -353,6 +363,12 @@ void LLVMInstructionSelection::genBinop(llvm::Value *result, IR::Binop *e) CreateCall4(op, _llvmFunction->arg_begin(), result, left, right); } +llvm::AllocaInst *LLVMInstructionSelection::newLLVMTemp(llvm::Type *type, llvm::Value *size) +{ + llvm::AllocaInst *addr = new llvm::AllocaInst(type, size, llvm::Twine(), _allocaInsertPoint); + return addr; +} + void LLVMInstructionSelection::visitCall(IR::Call *e) { llvm::Value *func = 0; @@ -376,7 +392,7 @@ void LLVMInstructionSelection::visitCall(IR::Call *e) llvm::Value *args = 0; if (argc) - args = CreateAlloca(_valueTy, getInt32(argc)); + args = newLLVMTemp(_valueTy, getInt32(argc)); else args = llvm::Constant::getNullValue(_valueTy->getPointerTo()); @@ -387,7 +403,7 @@ void LLVMInstructionSelection::visitCall(IR::Call *e) } if (func) { - llvm::Value *result = CreateAlloca(_valueTy); + llvm::Value *result = newLLVMTemp(_valueTy); CreateStore(llvm::Constant::getNullValue(_valueTy), result); CreateCall5(func, _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); _llvmValue = CreateLoad(result); diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index b343045bc0..67cbb7f16f 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -25,6 +25,7 @@ public: llvm::Value *getStringPtr(const QString &s); void genUnop(llvm::Value *result, IR::Unop *e); void genBinop(llvm::Value *result, IR::Binop *e); + llvm::AllocaInst *newLLVMTemp(llvm::Type *type, llvm::Value *size = 0); virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); @@ -55,6 +56,7 @@ private: llvm::Type *_contextPtrTy; llvm::Type *_stringPtrTy; llvm::FunctionType *_functionTy; + llvm::Instruction *_allocaInsertPoint; IR::Function *_function; IR::BasicBlock *_block; QHash _functionMap; -- cgit v1.2.3 From ad9816514fc448af150078a05d37c9a8a4d80f1c Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 10:08:06 +0200 Subject: Compile simple IR::New nodes --- llvm_runtime.cpp | 5 +++++ qv4isel_llvm.cpp | 41 +++++++++++++++++++++++++++++++++++++++-- tests/simple.js | 5 +++-- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 36a12b86dd..e70240484b 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -165,4 +165,9 @@ void __qmljs_llvm_call_activation_property(Context *context, Value *result, Stri __qmljs_call_activation_property(context, result, name, args, argc); } +void __qmljs_llvm_construct_activation_property(Context *context, Value *result, String *name, Value *args, int argc) +{ + __qmljs_construct_activation_property(context, result, name, args, argc); +} + } // extern "C" diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index f406862da3..f193979ea8 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -414,8 +414,45 @@ void LLVMInstructionSelection::visitCall(IR::Call *e) void LLVMInstructionSelection::visitNew(IR::New *e) { - llvm::Value *base = getLLVMValue(e->base); - Q_UNIMPLEMENTED(); + llvm::Value *func = 0; + llvm::Value *base = 0; + if (IR::Temp *t = e->base->asTemp()) { + base = getLLVMTemp(t); + func = _llvmModule->getFunction("__qmljs_llvm_construct_value"); + } else if (IR::Name *n = e->base->asName()) { + if (n->id) { + llvm::Value *str = getStringPtr(*n->id); + base = CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_identifier"), + _llvmFunction->arg_begin(), str); + func = _llvmModule->getFunction("__qmljs_llvm_construct_activation_property"); + } + } + + int argc = 0; + for (IR::ExprList *it = e->args; it; it = it->next) { + ++argc; + } + + llvm::Value *args = 0; + if (argc) + args = newLLVMTemp(_valueTy, getInt32(argc)); + else + args = llvm::Constant::getNullValue(_valueTy->getPointerTo()); + + int i = 0; + for (IR::ExprList *it = e->args; it; it = it->next) { + llvm::Value *arg = getLLVMValue(it->expr); + CreateStore(arg, CreateConstGEP1_32(args, i++)); + } + + if (func) { + llvm::Value *result = newLLVMTemp(_valueTy); + CreateStore(llvm::Constant::getNullValue(_valueTy), result); + CreateCall5(func, _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); + _llvmValue = CreateLoad(result); + } else { + Q_UNIMPLEMENTED(); + } } void LLVMInstructionSelection::visitSubscript(IR::Subscript *) diff --git a/tests/simple.js b/tests/simple.js index e0a3984a3f..8a80bcb4d0 100644 --- a/tests/simple.js +++ b/tests/simple.js @@ -4,11 +4,12 @@ var b = 2 var c = 10 var d = 100 -for (i = 0; i < 1000000; i = i + 1) { +var d1 = new Date +for (var i = 0; i < 1000000; i = i + 1) { if (a == 1) d = d + a + b * c else d = 321 } -print("the result is", d) +print("the result is", d, "done in", new Date - d1) -- cgit v1.2.3 From 0288e49e8cb599307d75e953f5133e8d56e53ba8 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 10:36:59 +0200 Subject: Fix the targets --- main.cpp | 33 ++++++++++++++++++++++++++++++++- v4.pro | 2 +- v4cc | 8 ++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100755 v4cc diff --git a/main.cpp b/main.cpp index 4438eb878a..3ea3a0e714 100644 --- a/main.cpp +++ b/main.cpp @@ -24,6 +24,12 @@ # include # include # include +# include +# include +# include +# include +# include +# include #endif static inline bool protect(const void *addr, size_t size) @@ -85,11 +91,36 @@ void compile(const QString &fileName, const QString &source) LLVMInstructionSelection llvmIsel(llvm::getGlobalContext()); if (llvm::Module *llvmModule = llvmIsel.getLLVMModule(&module)) { llvm::PassManager PM; + + const std::string triple = llvm::sys::getDefaultTargetTriple(); + + LLVMInitializeX86TargetInfo(); + LLVMInitializeX86Target(); + LLVMInitializeX86AsmPrinter(); + LLVMInitializeX86AsmParser(); + LLVMInitializeX86Disassembler(); + LLVMInitializeX86TargetMC(); + + std::string err; + const llvm::Target *target = llvm::TargetRegistry::lookupTarget(triple, err); + if (! err.empty()) { + std::cerr << err << ", triple: " << triple << std::endl; + assert(!"cannot create target for the host triple"); + } + + + std::string cpu; + std::string features; + llvm::TargetOptions options; + llvm::TargetMachine *targetMachine = target->createTargetMachine(triple, cpu, features, options, llvm::Reloc::PIC_); + assert(targetMachine); + + llvm::formatted_raw_ostream out(llvm::outs()); PM.add(llvm::createScalarReplAggregatesPass()); PM.add(llvm::createInstructionCombiningPass()); PM.add(llvm::createGlobalOptimizerPass()); PM.add(llvm::createFunctionInliningPass()); - PM.add(llvm::createPrintModulePass(&llvm::outs())); + targetMachine->addPassesToEmitFile(PM, out, llvm::TargetMachine::CGFT_AssemblyFile); PM.run(*llvmModule); delete llvmModule; } diff --git a/v4.pro b/v4.pro index 58f37128e3..b0b4cb28e9 100644 --- a/v4.pro +++ b/v4.pro @@ -53,7 +53,7 @@ DEFINES += \ LIBS += \ $$system(llvm-config --ldflags) \ - $$system(llvm-config --libs core jit bitreader linker ipo) + $$system(llvm-config --libs core jit bitreader linker ipo target x86 arm) QMAKE_EXTRA_TARGETS += gen_llvm_runtime diff --git a/v4cc b/v4cc new file mode 100755 index 0000000000..5400165e3c --- /dev/null +++ b/v4cc @@ -0,0 +1,8 @@ +#!/bin/sh + +me=$(dirname $0) +out=$(basename $1 .js).so +($me/v4 --compile "$1" | gcc -shared -xassembler -o "$out" -) && \ + (echo "compiled $out"; \ + echo "run with ./v4 --aot $out") + -- cgit v1.2.3 From a9867139d97a09ef68f6458226aebdd91438a814 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 10:58:49 +0200 Subject: Split __qmljs_add --- qmljs_runtime.cpp | 19 +++++++++++++++++++ qmljs_runtime.h | 21 ++++++--------------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index c1b25ccbd3..24072daca1 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -414,6 +414,25 @@ void __qmljs_delete(Context *ctx, Value *result, const Value *value) assert(!"TODO"); } +void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Value *right) +{ + Value pleft, pright; + __qmljs_to_primitive(ctx, &pleft, left, PREFERREDTYPE_HINT); + __qmljs_to_primitive(ctx, &pright, right, PREFERREDTYPE_HINT); + if (pleft.type == STRING_TYPE || pright.type == STRING_TYPE) { + if (pleft.type != STRING_TYPE) + __qmljs_to_string(ctx, &pleft, &pleft); + if (pright.type != STRING_TYPE) + __qmljs_to_string(ctx, &pright, &pright); + String *string = __qmljs_string_concat(ctx, pleft.stringValue, pright.stringValue); + __qmljs_init_string(result, string); + } else { + double x = __qmljs_to_number(ctx, &pleft); + double y = __qmljs_to_number(ctx, &pright); + __qmljs_init_number(result, x + y); + } +} + void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Value *right) { if (FunctionObject *function = right->asFunctionObject()) { diff --git a/qmljs_runtime.h b/qmljs_runtime.h index cb9c614a0d..a2dc5f1ab1 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -184,6 +184,8 @@ void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *rig void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right); void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Value *right); + bool __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right); bool __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right); bool __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right); @@ -730,21 +732,10 @@ inline void __qmljs_inplace_ushr(Context *ctx, Value *result, double value) inline void __qmljs_add(Context *ctx, Value *result, const Value *left, const Value *right) { - Value pleft, pright; - __qmljs_to_primitive(ctx, &pleft, left, PREFERREDTYPE_HINT); - __qmljs_to_primitive(ctx, &pright, right, PREFERREDTYPE_HINT); - if (pleft.type == STRING_TYPE || pright.type == STRING_TYPE) { - if (pleft.type != STRING_TYPE) - __qmljs_to_string(ctx, &pleft, &pleft); - if (pright.type != STRING_TYPE) - __qmljs_to_string(ctx, &pright, &pright); - String *string = __qmljs_string_concat(ctx, pleft.stringValue, pright.stringValue); - __qmljs_init_string(result, string); - } else { - double x = __qmljs_to_number(ctx, &pleft); - double y = __qmljs_to_number(ctx, &pright); - __qmljs_init_number(result, x + y); - } + if (left->type == NUMBER_TYPE == right->type == NUMBER_TYPE) + __qmljs_init_number(result, left->numberValue + right->numberValue); + else + __qmljs_add_helper(ctx, result, left, right); } inline void __qmljs_sub(Context *ctx, Value *result, const Value *left, const Value *right) -- cgit v1.2.3 From ff76b6afa180d8e581ce2f0aeded4bcfc485ff09 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 11:06:00 +0200 Subject: Use O2-level inlining --- main.cpp | 2 +- qmljs_runtime.h | 2 +- v4.pro | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main.cpp b/main.cpp index 3ea3a0e714..671f057472 100644 --- a/main.cpp +++ b/main.cpp @@ -119,7 +119,7 @@ void compile(const QString &fileName, const QString &source) PM.add(llvm::createScalarReplAggregatesPass()); PM.add(llvm::createInstructionCombiningPass()); PM.add(llvm::createGlobalOptimizerPass()); - PM.add(llvm::createFunctionInliningPass()); + PM.add(llvm::createFunctionInliningPass(25)); targetMachine->addPassesToEmitFile(PM, out, llvm::TargetMachine::CGFT_AssemblyFile); PM.run(*llvmModule); delete llvmModule; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index a2dc5f1ab1..4e1560911b 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -732,7 +732,7 @@ inline void __qmljs_inplace_ushr(Context *ctx, Value *result, double value) inline void __qmljs_add(Context *ctx, Value *result, const Value *left, const Value *right) { - if (left->type == NUMBER_TYPE == right->type == NUMBER_TYPE) + if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) __qmljs_init_number(result, left->numberValue + right->numberValue); else __qmljs_add_helper(ctx, result, left, right); diff --git a/v4.pro b/v4.pro index b0b4cb28e9..ae3e85dd32 100644 --- a/v4.pro +++ b/v4.pro @@ -58,7 +58,7 @@ LIBS += \ QMAKE_EXTRA_TARGETS += gen_llvm_runtime gen_llvm_runtime.target = llvm_runtime -gen_llvm_runtime.commands = clang -emit-llvm -c -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o llvm_runtime.bc +gen_llvm_runtime.commands = clang -O2 -emit-llvm -c -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o llvm_runtime.bc } else { -- cgit v1.2.3 From b1e62cc60749e3a55805df7ed493ec84587dc3bd Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 11:30:56 +0200 Subject: Generate LLVM code for member expressions --- llvm_runtime.cpp | 5 +++++ qv4isel_llvm.cpp | 26 ++++++++++++++++++++------ qv4isel_llvm_p.h | 1 + 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index e70240484b..2cb043ff59 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -170,4 +170,9 @@ void __qmljs_llvm_construct_activation_property(Context *context, Value *result, __qmljs_construct_activation_property(context, result, name, args, argc); } +void __qmljs_llvm_get_property(Context *ctx, Value *result, Value *object, String *name) +{ + __qmljs_get_property(ctx, result, object, name); +} + } // extern "C" diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index f193979ea8..4be0da1ebc 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -188,6 +188,14 @@ llvm::Value *LLVMInstructionSelection::getStringPtr(const QString &s) return value; } +llvm::Value *LLVMInstructionSelection::getIdentifier(const QString &s) +{ + llvm::Value *str = getStringPtr(s); + llvm::Value *id = CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_identifier"), + _llvmFunction->arg_begin(), str); + return id; +} + void LLVMInstructionSelection::visitExp(IR::Exp *s) { getLLVMValue(s->expr); @@ -378,9 +386,7 @@ void LLVMInstructionSelection::visitCall(IR::Call *e) func = _llvmModule->getFunction("__qmljs_llvm_call_value"); } else if (IR::Name *n = e->base->asName()) { if (n->id) { - llvm::Value *str = getStringPtr(*n->id); - base = CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_identifier"), - _llvmFunction->arg_begin(), str); + base = getIdentifier(*n->id); func = _llvmModule->getFunction("__qmljs_llvm_call_activation_property"); } } @@ -460,8 +466,16 @@ void LLVMInstructionSelection::visitSubscript(IR::Subscript *) Q_UNIMPLEMENTED(); } -void LLVMInstructionSelection::visitMember(IR::Member *) +void LLVMInstructionSelection::visitMember(IR::Member *e) { - Q_UNIMPLEMENTED(); -} + IR::Temp *t = e->base->asTemp(); + assert(t); + + llvm::Value *result = newLLVMTemp(_valueTy); + llvm::Value *base = getLLVMTemp(t); + llvm::Value *name = getIdentifier(*e->name); + CreateCall4(_llvmModule->getFunction("__qmljs_llvm_get_property"), + _llvmFunction->arg_begin(), result, base, name); + _llvmValue = CreateLoad(result); +} diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 67cbb7f16f..292421eeec 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -23,6 +23,7 @@ public: llvm::Value *getLLVMCondition(IR::Expr *expr); llvm::Value *getLLVMTemp(IR::Temp *temp); llvm::Value *getStringPtr(const QString &s); + llvm::Value *getIdentifier(const QString &s); void genUnop(llvm::Value *result, IR::Unop *e); void genBinop(llvm::Value *result, IR::Binop *e); llvm::AllocaInst *newLLVMTemp(llvm::Type *type, llvm::Value *size = 0); -- cgit v1.2.3 From b3fbdb62f70000a0722cc60720a12baf26690d19 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 11:50:13 +0200 Subject: Lower IR::Call and IR::New --- llvm_runtime.cpp | 10 +++++ qv4isel_llvm.cpp | 114 ++++++++++++++++++++++++++++++++++++++++--------------- qv4isel_llvm_p.h | 3 ++ 3 files changed, 97 insertions(+), 30 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 2cb043ff59..e8db7c9523 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -175,4 +175,14 @@ void __qmljs_llvm_get_property(Context *ctx, Value *result, Value *object, Strin __qmljs_get_property(ctx, result, object, name); } +void __qmljs_llvm_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) +{ + __qmljs_call_property(context, result, base, name, args, argc); +} + +void __qmljs_llvm_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) +{ + __qmljs_construct_property(context, result, base, name, args, argc); +} + } // extern "C" diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 4be0da1ebc..f92e8f05de 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -377,8 +377,85 @@ llvm::AllocaInst *LLVMInstructionSelection::newLLVMTemp(llvm::Type *type, llvm:: return addr; } +llvm::Value * LLVMInstructionSelection::genArguments(IR::ExprList *exprs, int &argc) +{ + llvm::Value *args = 0; + + argc = 0; + for (IR::ExprList *it = exprs; it; it = it->next) + ++argc; + + if (argc) + args = newLLVMTemp(_valueTy, getInt32(argc)); + else + args = llvm::Constant::getNullValue(_valueTy->getPointerTo()); + + int i = 0; + for (IR::ExprList *it = exprs; it; it = it->next) { + llvm::Value *arg = getLLVMValue(it->expr); + CreateStore(arg, CreateConstGEP1_32(args, i++)); + } + + return args; +} + +void LLVMInstructionSelection::genCallMember(IR::Call *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + IR::Member *m = e->base->asMember(); + llvm::Value *thisObject = getLLVMTemp(m->base->asTemp()); + llvm::Value *name = getIdentifier(*m->name); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + thisObject, + name, + args, + getInt32(argc) + }; + + CreateCall(_llvmModule->getFunction("__qmljs_llvm_call_property"), llvm::ArrayRef(actuals)); + _llvmValue = CreateLoad(result); +} + +void LLVMInstructionSelection::genConstructMember(IR::New *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + IR::Member *m = e->base->asMember(); + llvm::Value *thisObject = getLLVMTemp(m->base->asTemp()); + llvm::Value *name = getIdentifier(*m->name); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + thisObject, + name, + args, + getInt32(argc) + }; + + CreateCall(_llvmModule->getFunction("__qmljs_llvm_construct_property"), llvm::ArrayRef(actuals)); + _llvmValue = CreateLoad(result); +} + void LLVMInstructionSelection::visitCall(IR::Call *e) { + if (e->base->asMember()) { + genCallMember(e); + return; + } + llvm::Value *func = 0; llvm::Value *base = 0; if (IR::Temp *t = e->base->asTemp()) { @@ -392,21 +469,7 @@ void LLVMInstructionSelection::visitCall(IR::Call *e) } int argc = 0; - for (IR::ExprList *it = e->args; it; it = it->next) { - ++argc; - } - - llvm::Value *args = 0; - if (argc) - args = newLLVMTemp(_valueTy, getInt32(argc)); - else - args = llvm::Constant::getNullValue(_valueTy->getPointerTo()); - - int i = 0; - for (IR::ExprList *it = e->args; it; it = it->next) { - llvm::Value *arg = getLLVMValue(it->expr); - CreateStore(arg, CreateConstGEP1_32(args, i++)); - } + llvm::Value *args = genArguments(e->args, argc); if (func) { llvm::Value *result = newLLVMTemp(_valueTy); @@ -420,6 +483,11 @@ void LLVMInstructionSelection::visitCall(IR::Call *e) void LLVMInstructionSelection::visitNew(IR::New *e) { + if (e->base->asMember()) { + genConstructMember(e); + return; + } + llvm::Value *func = 0; llvm::Value *base = 0; if (IR::Temp *t = e->base->asTemp()) { @@ -435,21 +503,7 @@ void LLVMInstructionSelection::visitNew(IR::New *e) } int argc = 0; - for (IR::ExprList *it = e->args; it; it = it->next) { - ++argc; - } - - llvm::Value *args = 0; - if (argc) - args = newLLVMTemp(_valueTy, getInt32(argc)); - else - args = llvm::Constant::getNullValue(_valueTy->getPointerTo()); - - int i = 0; - for (IR::ExprList *it = e->args; it; it = it->next) { - llvm::Value *arg = getLLVMValue(it->expr); - CreateStore(arg, CreateConstGEP1_32(args, i++)); - } + llvm::Value *args = genArguments(e->args, argc); if (func) { llvm::Value *result = newLLVMTemp(_valueTy); diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 292421eeec..90a471d86d 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -27,6 +27,9 @@ public: void genUnop(llvm::Value *result, IR::Unop *e); void genBinop(llvm::Value *result, IR::Binop *e); llvm::AllocaInst *newLLVMTemp(llvm::Type *type, llvm::Value *size = 0); + llvm::Value * genArguments(IR::ExprList *args, int &argc); + void genCallMember(IR::Call *e, llvm::Value *result = 0); + void genConstructMember(IR::New *e, llvm::Value *result = 0); virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); -- cgit v1.2.3 From f0ee49c6a46b7474fc24fdb82ae02ad7482d892b Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 13:30:03 +0200 Subject: Lower subscripts --- llvm_runtime.cpp | 10 ++++++++++ qv4isel_llvm.cpp | 40 ++++++++++++++++++++++++++++++++++++++-- qv4isel_llvm_p.h | 2 ++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index e8db7c9523..04fa1d5dee 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -185,4 +185,14 @@ void __qmljs_llvm_construct_property(Context *context, Value *result, const Valu __qmljs_construct_property(context, result, base, name, args, argc); } +void __qmljs_llvm_get_element(Context *ctx, Value *result, Value *object, Value *index) +{ + __qmljs_get_element(ctx, result, object, index); +} + +void __qmljs_llvm_set_element(Context *ctx, Value *object, Value *index, Value *value) +{ + __qmljs_set_element(ctx, object, index, value); +} + } // extern "C" diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index f92e8f05de..3822fb600b 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -144,6 +144,16 @@ llvm::Value *LLVMInstructionSelection::getLLVMValue(IR::Expr *expr) return llvmValue; } +llvm::Value *LLVMInstructionSelection::getLLVMTempReference(IR::Expr *expr) +{ + if (IR::Temp *t = expr->asTemp()) + return getLLVMTemp(t); + + llvm::Value *addr = newLLVMTemp(_valueTy); + CreateStore(getLLVMValue(expr), addr); + return addr; +} + llvm::Value *LLVMInstructionSelection::getLLVMCondition(IR::Expr *expr) { llvm::Value *value = 0; @@ -211,8 +221,22 @@ void LLVMInstructionSelection::visitLeave(IR::Leave *) Q_UNREACHABLE(); } +void LLVMInstructionSelection::genMoveSubscript(IR::Move *s) +{ + IR::Subscript *subscript = s->target->asSubscript(); + llvm::Value *base = getLLVMTempReference(subscript->base); + llvm::Value *index = getLLVMTempReference(subscript->index); + llvm::Value *source = getLLVMTempReference(s->source); + CreateCall4(_llvmModule->getFunction("__qmljs_llvm_set_element"), + _llvmFunction->arg_begin(), base, index, source); +} + void LLVMInstructionSelection::visitMove(IR::Move *s) { + if (s->target->asSubscript()) { + genMoveSubscript(s); + return; + } if (IR::Temp *t = s->target->asTemp()) { llvm::Value *target = getLLVMTemp(t); llvm::Value *source = getLLVMValue(s->source); @@ -515,9 +539,21 @@ void LLVMInstructionSelection::visitNew(IR::New *e) } } -void LLVMInstructionSelection::visitSubscript(IR::Subscript *) +void LLVMInstructionSelection::visitSubscript(IR::Subscript *e) { - Q_UNIMPLEMENTED(); + IR::Temp *t = e->base->asTemp(); + llvm::Value *result = newLLVMTemp(_valueTy); + llvm::Value *base = getLLVMTemp(t); + llvm::Value *index = 0; + if (IR::Temp *i = e->index->asTemp()) + index = getLLVMTemp(i); + else { + index = newLLVMTemp(_valueTy); + CreateStore(getLLVMValue(e->index), index); + } + CreateCall4(_llvmModule->getFunction("__qmljs_llvm_get_element"), + _llvmFunction->arg_begin(), result, base, index); + _llvmValue = CreateLoad(result); } void LLVMInstructionSelection::visitMember(IR::Member *e) diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 90a471d86d..8cb853bb6c 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -20,6 +20,7 @@ public: llvm::Function *getLLVMFunction(IR::Function *function); llvm::BasicBlock *getLLVMBasicBlock(IR::BasicBlock *block); llvm::Value *getLLVMValue(IR::Expr *expr); + llvm::Value *getLLVMTempReference(IR::Expr *expr); llvm::Value *getLLVMCondition(IR::Expr *expr); llvm::Value *getLLVMTemp(IR::Temp *temp); llvm::Value *getStringPtr(const QString &s); @@ -30,6 +31,7 @@ public: llvm::Value * genArguments(IR::ExprList *args, int &argc); void genCallMember(IR::Call *e, llvm::Value *result = 0); void genConstructMember(IR::New *e, llvm::Value *result = 0); + void genMoveSubscript(IR::Move *s); virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); -- cgit v1.2.3 From c3f50fdb95d3b53665271758947fee69ba9e61e1 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 13:43:50 +0200 Subject: Generate LLVM code for MoveMember --- llvm_runtime.cpp | 4 ++-- qv4isel_llvm.cpp | 37 ++++++++++++++++--------------------- qv4isel_llvm_p.h | 1 + 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 04fa1d5dee..0f0ecfd69b 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -190,9 +190,9 @@ void __qmljs_llvm_get_element(Context *ctx, Value *result, Value *object, Value __qmljs_get_element(ctx, result, object, index); } -void __qmljs_llvm_set_element(Context *ctx, Value *object, Value *index, Value *value) +void __qmljs_llvm_set_property(Context *ctx, Value *object, String *name, Value *value) { - __qmljs_set_element(ctx, object, index, value); + __qmljs_set_property(ctx, object, name, value); } } // extern "C" diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 3822fb600b..fbe1568b1a 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -231,20 +231,25 @@ void LLVMInstructionSelection::genMoveSubscript(IR::Move *s) _llvmFunction->arg_begin(), base, index, source); } +void LLVMInstructionSelection::genMoveMember(IR::Move *s) +{ + IR::Member *m = s->target->asMember(); + llvm::Value *base = getLLVMTempReference(m->base); + llvm::Value *name = getIdentifier(*m->name); + llvm::Value *source = getLLVMTempReference(s->source); + CreateCall4(_llvmModule->getFunction("__qmljs_llvm_set_property"), + _llvmFunction->arg_begin(), base, name, source); +} + void LLVMInstructionSelection::visitMove(IR::Move *s) { if (s->target->asSubscript()) { genMoveSubscript(s); - return; - } - if (IR::Temp *t = s->target->asTemp()) { + } else if (s->target->asMember()) { + genMoveMember(s); + } else if (IR::Temp *t = s->target->asTemp()) { llvm::Value *target = getLLVMTemp(t); llvm::Value *source = getLLVMValue(s->source); - assert(source); - if (source->getType()->getPointerTo() != target->getType()) { - source->dump(); - assert(!"not cool"); - } CreateStore(source, target); return; } @@ -541,16 +546,9 @@ void LLVMInstructionSelection::visitNew(IR::New *e) void LLVMInstructionSelection::visitSubscript(IR::Subscript *e) { - IR::Temp *t = e->base->asTemp(); llvm::Value *result = newLLVMTemp(_valueTy); - llvm::Value *base = getLLVMTemp(t); - llvm::Value *index = 0; - if (IR::Temp *i = e->index->asTemp()) - index = getLLVMTemp(i); - else { - index = newLLVMTemp(_valueTy); - CreateStore(getLLVMValue(e->index), index); - } + llvm::Value *base = getLLVMTempReference(e->base); + llvm::Value *index = getLLVMTempReference(e->index); CreateCall4(_llvmModule->getFunction("__qmljs_llvm_get_element"), _llvmFunction->arg_begin(), result, base, index); _llvmValue = CreateLoad(result); @@ -558,11 +556,8 @@ void LLVMInstructionSelection::visitSubscript(IR::Subscript *e) void LLVMInstructionSelection::visitMember(IR::Member *e) { - IR::Temp *t = e->base->asTemp(); - assert(t); - llvm::Value *result = newLLVMTemp(_valueTy); - llvm::Value *base = getLLVMTemp(t); + llvm::Value *base = getLLVMTempReference(e->base); llvm::Value *name = getIdentifier(*e->name); CreateCall4(_llvmModule->getFunction("__qmljs_llvm_get_property"), diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 8cb853bb6c..5abab2f7ac 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -32,6 +32,7 @@ public: void genCallMember(IR::Call *e, llvm::Value *result = 0); void genConstructMember(IR::New *e, llvm::Value *result = 0); void genMoveSubscript(IR::Move *s); + void genMoveMember(IR::Move *s); virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); -- cgit v1.2.3 From 648f1462ab08339f625096f987080687361cfa4b Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 14:11:28 +0200 Subject: Lower IR::Name nodes. --- llvm_runtime.cpp | 30 ++++++++++++++++++++++++++++++ qv4isel_llvm.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 76 insertions(+), 6 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 0f0ecfd69b..e7dab641c0 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -15,6 +15,21 @@ Value *__qmljs_llvm_get_argument(Context *ctx, int index) return &ctx->arguments[index]; } +void __qmljs_llvm_init_undefined(Value *result) +{ + __qmljs_init_undefined(result); +} + +void __qmljs_llvm_init_null(Value *result) +{ + __qmljs_init_null(result); +} + +void __qmljs_llvm_init_boolean(Value *result, bool value) +{ + __qmljs_init_boolean(result, value); +} + void __qmljs_llvm_init_number(Value *result, double value) { __qmljs_init_number(result, value); @@ -170,6 +185,16 @@ void __qmljs_llvm_construct_activation_property(Context *context, Value *result, __qmljs_construct_activation_property(context, result, name, args, argc); } +void __qmljs_llvm_get_activation_property(Context *ctx, Value *result, String *name) +{ + __qmljs_get_activation_property(ctx, result, name); +} + +void __qmljs_llvm_set_activation_property(Context *ctx, String *name, Value *value) +{ + __qmljs_set_activation_property(ctx, name, value); +} + void __qmljs_llvm_get_property(Context *ctx, Value *result, Value *object, String *name) { __qmljs_get_property(ctx, result, object, name); @@ -190,6 +215,11 @@ void __qmljs_llvm_get_element(Context *ctx, Value *result, Value *object, Value __qmljs_get_element(ctx, result, object, index); } +void __qmljs_llvm_set_element(Context *ctx, Value *object, Value *index, Value *value) +{ + __qmljs_set_element(ctx, object, index, value); +} + void __qmljs_llvm_set_property(Context *ctx, Value *object, String *name, Value *value) { __qmljs_set_property(ctx, object, name, value); diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index fbe1568b1a..54623adae3 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -2,6 +2,8 @@ #include "qv4isel_llvm_p.h" #include "qv4ir_p.h" +#include + #include #include #include @@ -10,6 +12,10 @@ using namespace QQmlJS; +namespace { +QTextStream qout(stdout, QIODevice::WriteOnly); +} + LLVMInstructionSelection::LLVMInstructionSelection(llvm::LLVMContext &context) : llvm::IRBuilder<>(context) , _llvmModule(0) @@ -247,13 +253,20 @@ void LLVMInstructionSelection::visitMove(IR::Move *s) genMoveSubscript(s); } else if (s->target->asMember()) { genMoveMember(s); + } else if (IR::Name *n = s->target->asName()) { + llvm::Value *name = getIdentifier(*n->id); + llvm::Value *source = getLLVMTempReference(s->source); + CreateCall3(_llvmModule->getFunction("__qmljs_llvm_set_activation_property"), + _llvmFunction->arg_begin(), name, source); } else if (IR::Temp *t = s->target->asTemp()) { llvm::Value *target = getLLVMTemp(t); llvm::Value *source = getLLVMValue(s->source); CreateStore(source, target); - return; + } else { + s->dump(qout, IR::Stmt::HIR); + qout << endl; + Q_UNIMPLEMENTED(); } - Q_UNIMPLEMENTED(); } void LLVMInstructionSelection::visitJump(IR::Jump *s) @@ -281,9 +294,31 @@ void LLVMInstructionSelection::visitRet(IR::Ret *s) void LLVMInstructionSelection::visitConst(IR::Const *e) { - llvm::Value *k = llvm::ConstantFP::get(_numberTy, e->value); llvm::Value *tmp = newLLVMTemp(_valueTy); - CreateCall2(_llvmModule->getFunction("__qmljs_llvm_init_number"), tmp, k); + + switch (e->type) { + case IR::UndefinedType: + CreateCall(_llvmModule->getFunction("__qmljs_llvm_init_undefined"), tmp); + break; + + case IR::NullType: + CreateCall(_llvmModule->getFunction("__qmljs_llvm_init_null"), tmp); + break; + + case IR::BoolType: + CreateCall2(_llvmModule->getFunction("__qmljs_llvm_init_boolean"), tmp, + getInt1(e->value ? 1 : 0)); + break; + + case IR::NumberType: + CreateCall2(_llvmModule->getFunction("__qmljs_llvm_init_number"), tmp, + llvm::ConstantFP::get(_numberTy, e->value)); + break; + + default: + Q_UNREACHABLE(); + } + _llvmValue = CreateLoad(tmp); } @@ -296,9 +331,14 @@ void LLVMInstructionSelection::visitString(IR::String *e) _llvmValue = CreateLoad(tmp); } -void LLVMInstructionSelection::visitName(IR::Name *) +void LLVMInstructionSelection::visitName(IR::Name *e) { - Q_UNIMPLEMENTED(); + llvm::Value *result = newLLVMTemp(_valueTy); + llvm::Value *name = getIdentifier(*e->id); + CreateCall3(_llvmModule->getFunction("__qmljs_get_activation_property"), + _llvmFunction->arg_begin(), result, name); + _llvmValue = CreateLoad(result); + } void LLVMInstructionSelection::visitTemp(IR::Temp *e) -- cgit v1.2.3 From 01bca93863d61ed25d3d5935a7e150174d68be32 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 14:16:11 +0200 Subject: Warn about unimplement assignments. --- qv4isel_llvm.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 54623adae3..374aeaf5d2 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -13,7 +13,7 @@ using namespace QQmlJS; namespace { -QTextStream qout(stdout, QIODevice::WriteOnly); +QTextStream qerr(stderr, QIODevice::WriteOnly); } LLVMInstructionSelection::LLVMInstructionSelection(llvm::LLVMContext &context) @@ -249,6 +249,13 @@ void LLVMInstructionSelection::genMoveMember(IR::Move *s) void LLVMInstructionSelection::visitMove(IR::Move *s) { + if (s->op != IR::OpInvalid) { + s->dump(qerr, IR::Stmt::HIR); + qerr << endl; + Q_UNIMPLEMENTED(); + return; + } + if (s->target->asSubscript()) { genMoveSubscript(s); } else if (s->target->asMember()) { @@ -263,8 +270,8 @@ void LLVMInstructionSelection::visitMove(IR::Move *s) llvm::Value *source = getLLVMValue(s->source); CreateStore(source, target); } else { - s->dump(qout, IR::Stmt::HIR); - qout << endl; + s->dump(qerr, IR::Stmt::HIR); + qerr << endl; Q_UNIMPLEMENTED(); } } -- cgit v1.2.3 From 9366999ac2b7af81e3e1ce6e89160fc759a8c637 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 14:59:42 +0200 Subject: Generate code for closures. --- llvm_runtime.cpp | 10 ++++++++++ qmljs_runtime.cpp | 5 +++++ qmljs_runtime.h | 1 + qv4isel_llvm.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++++++++------ qv4isel_llvm_p.h | 2 ++ 5 files changed, 72 insertions(+), 6 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index e7dab641c0..98ad023c85 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -40,6 +40,11 @@ void __qmljs_llvm_init_string(Context *ctx, Value *result, const char *str) __qmljs_init_string(result, __qmljs_string_from_utf8(ctx, str)); } +void __qmljs_llvm_init_native_function(Context *ctx, Value *result, void (*code)(Context *)) +{ + __qmljs_init_native_function(ctx, result, code); +} + bool __qmljs_llvm_to_boolean(Context *ctx, const Value *value) { return __qmljs_to_boolean(ctx, value); @@ -180,6 +185,11 @@ void __qmljs_llvm_call_activation_property(Context *context, Value *result, Stri __qmljs_call_activation_property(context, result, name, args, argc); } +void __qmljs_llvm_call_value(Context *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) +{ + __qmljs_call_value(context, result, thisObject, func, args, argc); +} + void __qmljs_llvm_construct_activation_property(Context *context, Value *result, String *name, Value *args, int argc) { __qmljs_construct_activation_property(context, result, name, args, argc); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 24072daca1..6bdb99b28c 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -360,6 +360,11 @@ void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos) __qmljs_init_object(result, ctx->engine->newScriptFunction(ctx, clos)); } +void __qmljs_init_native_function(Context *ctx, Value *result, void (*code)(Context *)) +{ + __qmljs_init_object(result, ctx->engine->newNativeFunction(ctx, code)); +} + void __qmljs_string_literal_undefined(Context *ctx, Value *result) { __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("undefined"))); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 4e1560911b..73a312ca89 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -76,6 +76,7 @@ void __qmljs_init_number(Value *result, double number); void __qmljs_init_string(Value *result, String *string); void __qmljs_init_object(Value *result, Object *object); void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos); +void __qmljs_init_native_function(Context *ctx, Value *result, void (*code)(Context *)); bool __qmljs_is_function(Context *ctx, const Value *value); diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 374aeaf5d2..a997272fa3 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -72,16 +72,31 @@ llvm::Module *LLVMInstructionSelection::getLLVMModule(IR::Module *module) foreach (IR::Function *function, module->functions) - (void) getLLVMFunction(function); + (void) compileLLVMFunction(function); qSwap(_llvmModule, llvmModule); return llvmModule; } llvm::Function *LLVMInstructionSelection::getLLVMFunction(IR::Function *function) { - llvm::Function *llvmFunction = - llvm::Function::Create(_functionTy, llvm::Function::ExternalLinkage, // ### make it internal - llvm::Twine(function->name ? qPrintable(*function->name) : 0), _llvmModule); + llvm::Function *&f = _functionMap[function]; + if (! f) { + QString name = QStringLiteral("__qmljs_native_"); + if (function->name) { + if (*function->name == QStringLiteral("%entry")) + name = *function->name; + else + name += *function->name; + } + f = llvm::Function::Create(_functionTy, llvm::Function::ExternalLinkage, // ### make it internal + qPrintable(name), _llvmModule); + } + return f; +} + +llvm::Function *LLVMInstructionSelection::compileLLVMFunction(IR::Function *function) +{ + llvm::Function *llvmFunction = getLLVMFunction(function); QHash blockMap; QVector tempMap; @@ -355,9 +370,13 @@ void LLVMInstructionSelection::visitTemp(IR::Temp *e) } } -void LLVMInstructionSelection::visitClosure(IR::Closure *) +void LLVMInstructionSelection::visitClosure(IR::Closure *e) { - Q_UNIMPLEMENTED(); + llvm::Value *tmp = newLLVMTemp(_valueTy); + llvm::Value *clos = getLLVMFunction(e->value); + CreateCall3(_llvmModule->getFunction("__qmljs_init_native_function"), + _llvmFunction->arg_begin(), tmp, clos); + _llvmValue = CreateLoad(tmp); } void LLVMInstructionSelection::visitUnop(IR::Unop *e) @@ -525,11 +544,40 @@ void LLVMInstructionSelection::genConstructMember(IR::New *e, llvm::Value *resul _llvmValue = CreateLoad(result); } +void LLVMInstructionSelection::genCallTemp(IR::Call *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + llvm::Value *func = getLLVMTempReference(e->base); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *thisObject = llvm::Constant::getNullValue(_valueTy->getPointerTo()); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + thisObject, + func, + args, + getInt32(argc) + }; + + CreateCall(_llvmModule->getFunction("__qmljs_llvm_call_value"), actuals); + + _llvmValue = CreateLoad(result); +} + void LLVMInstructionSelection::visitCall(IR::Call *e) { if (e->base->asMember()) { genCallMember(e); return; + } else if (e->base->asTemp()) { + genCallTemp(e); + return; } llvm::Value *func = 0; diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 5abab2f7ac..27fd1bd0ff 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -18,6 +18,7 @@ public: llvm::Module *getLLVMModule(IR::Module *module); llvm::Function *getLLVMFunction(IR::Function *function); + llvm::Function *compileLLVMFunction(IR::Function *function); llvm::BasicBlock *getLLVMBasicBlock(IR::BasicBlock *block); llvm::Value *getLLVMValue(IR::Expr *expr); llvm::Value *getLLVMTempReference(IR::Expr *expr); @@ -30,6 +31,7 @@ public: llvm::AllocaInst *newLLVMTemp(llvm::Type *type, llvm::Value *size = 0); llvm::Value * genArguments(IR::ExprList *args, int &argc); void genCallMember(IR::Call *e, llvm::Value *result = 0); + void genCallTemp(IR::Call *e, llvm::Value *result = 0); void genConstructMember(IR::New *e, llvm::Value *result = 0); void genMoveSubscript(IR::Move *s); void genMoveMember(IR::Move *s); -- cgit v1.2.3 From 55287cde510182c95f65a5acefe6b80337b0b744 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 15:30:50 +0200 Subject: Initial work on exceptions for the AOT compiler. --- llvm_runtime.cpp | 15 ++++++++++++ qmljs_runtime.cpp | 17 ++++++++++--- qmljs_runtime.h | 5 +++- qv4isel_llvm.cpp | 73 ++++++++++++++++++++++++++++++++++++++++--------------- qv4isel_llvm_p.h | 1 + v4cc | 2 +- 6 files changed, 88 insertions(+), 25 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 98ad023c85..8a037e7c3a 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -235,4 +235,19 @@ void __qmljs_llvm_set_property(Context *ctx, Value *object, String *name, Value __qmljs_set_property(ctx, object, name, value); } +void __qmljs_llvm_typeof(Context *ctx, Value *result, const Value *value) +{ + __qmljs_typeof(ctx, result, value); +} + +void __qmljs_llvm_throw(Context *context, Value *value) +{ + __qmljs_throw(context, value); +} + +void __qmljs_llvm_rethrow(Context *context, Value *result) +{ + __qmljs_rethrow(context, result); +} + } // extern "C" diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 6bdb99b28c..4ddd4c10c8 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -934,6 +934,17 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba } } +void __qmljs_throw(Context *context, Value *value) +{ + context->hasUncaughtException = true; + context->result = *value; +} + +void __qmljs_rethrow(Context *context, Value *result) +{ + *result = context->result; +} + void __qmljs_builtin_typeof(Context *context, Value *result, Value *args, int argc) { Q_UNUSED(argc); @@ -944,14 +955,12 @@ void __qmljs_builtin_throw(Context *context, Value *result, Value *args, int arg { Q_UNUSED(argc); Q_UNUSED(result); - context->hasUncaughtException = true; - context->result = args[0]; + __qmljs_throw(context, &args[0]); } void __qmljs_builtin_rethrow(Context *context, Value *result, Value *, int) { - assert(result); - *result = context->result; + __qmljs_rethrow(context, result); } } // extern "C" diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 73a312ca89..ae69b23d15 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -152,7 +152,6 @@ bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y); // unary operators void __qmljs_delete(Context *ctx, Value *result, const Value *value); -void __qmljs_typeof(Context *ctx, Value *result, const Value *value); void __qmljs_postincr(Context *ctx, Value *result, const Value *value); void __qmljs_postdecr(Context *ctx, Value *result, const Value *value); void __qmljs_uplus(Context *ctx, Value *result, const Value *value); @@ -162,6 +161,10 @@ void __qmljs_not(Context *ctx, Value *result, const Value *value); void __qmljs_preincr(Context *ctx, Value *result, const Value *value); void __qmljs_predecr(Context *ctx, Value *result, const Value *value); +void __qmljs_typeof(Context *ctx, Value *result, const Value *value); +void __qmljs_throw(Context *context, Value *value); +void __qmljs_rethrow(Context *context, Value *result); + // binary operators void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Value *right); void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *right); diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index a997272fa3..d7829bf034 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -570,35 +570,70 @@ void LLVMInstructionSelection::genCallTemp(IR::Call *e, llvm::Value *result) _llvmValue = CreateLoad(result); } +void LLVMInstructionSelection::genCallName(IR::Call *e, llvm::Value *result) +{ + IR::Name *base = e->base->asName(); + + if (! result) + result = newLLVMTemp(_valueTy); + + if (! base->id) { + switch (base->builtin) { + case IR::Name::builtin_invalid: + break; + + case IR::Name::builtin_typeof: + // inline void __qmljs_typeof(Context *ctx, Value *result, const Value *value) + CreateCall3(_llvmModule->getFunction("__qmljs_llvm_typeof"), + _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + + case IR::Name::builtin_throw: + CreateCall2(_llvmModule->getFunction("__qmljs_llvm_throw"), + _llvmFunction->arg_begin(), getLLVMTempReference(e->args->expr)); + _llvmValue = llvm::UndefValue::get(_valueTy); + return; + + case IR::Name::builtin_rethrow: + CreateCall2(_llvmModule->getFunction("__qmljs_llvm_rethrow"), + _llvmFunction->arg_begin(), result); + _llvmValue = CreateLoad(result); + return; + } + + Q_UNREACHABLE(); + } else { + llvm::Value *name = getIdentifier(*base->id); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + CreateCall5(_llvmModule->getFunction("__qmljs_call_activation_property"), + _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); + + _llvmValue = CreateLoad(result); + } +} + void LLVMInstructionSelection::visitCall(IR::Call *e) { if (e->base->asMember()) { genCallMember(e); - return; } else if (e->base->asTemp()) { genCallTemp(e); - return; - } - - llvm::Value *func = 0; - llvm::Value *base = 0; - if (IR::Temp *t = e->base->asTemp()) { - base = getLLVMTemp(t); - func = _llvmModule->getFunction("__qmljs_llvm_call_value"); - } else if (IR::Name *n = e->base->asName()) { - if (n->id) { - base = getIdentifier(*n->id); - func = _llvmModule->getFunction("__qmljs_llvm_call_activation_property"); - } - } + } else if (e->base->asName()) { + genCallName(e); + } else if (IR::Temp *t = e->base->asTemp()) { + llvm::Value *base = getLLVMTemp(t); - int argc = 0; - llvm::Value *args = genArguments(e->args, argc); + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); - if (func) { llvm::Value *result = newLLVMTemp(_valueTy); CreateStore(llvm::Constant::getNullValue(_valueTy), result); - CreateCall5(func, _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); + CreateCall5(_llvmModule->getFunction("__qmljs_llvm_call_value"), + _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); _llvmValue = CreateLoad(result); } else { Q_UNIMPLEMENTED(); diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 27fd1bd0ff..64511f86c5 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -32,6 +32,7 @@ public: llvm::Value * genArguments(IR::ExprList *args, int &argc); void genCallMember(IR::Call *e, llvm::Value *result = 0); void genCallTemp(IR::Call *e, llvm::Value *result = 0); + void genCallName(IR::Call *e, llvm::Value *result = 0); void genConstructMember(IR::New *e, llvm::Value *result = 0); void genMoveSubscript(IR::Move *s); void genMoveMember(IR::Move *s); diff --git a/v4cc b/v4cc index 5400165e3c..9f9255e2dc 100755 --- a/v4cc +++ b/v4cc @@ -4,5 +4,5 @@ me=$(dirname $0) out=$(basename $1 .js).so ($me/v4 --compile "$1" | gcc -shared -xassembler -o "$out" -) && \ (echo "compiled $out"; \ - echo "run with ./v4 --aot $out") + echo "run with\n ./v4 --aot $out") -- cgit v1.2.3 From cee0095e2062285dc1702578fad2ecbe3050b0be Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 15:47:42 +0200 Subject: Don't use increments for now. --- tests/array.1.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/array.1.js b/tests/array.1.js index 508608871e..015941c2dc 100644 --- a/tests/array.1.js +++ b/tests/array.1.js @@ -12,7 +12,7 @@ for (var i = 0; i < 10000; i = i + 1) print("done in", new Date - d1) var s = "some cool stuff" -for (var i = 0; i < 15; ++i) { +for (var i = 0; i < 15; i = i + 1) { print(s[i]) } -- cgit v1.2.3 From 80188db9d87bc4f31826b8652f8e9a39cc29d5d5 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 15:58:21 +0200 Subject: Generate LLVM code for the `this-expression'. --- llvm_runtime.cpp | 5 +++++ qv4isel_llvm.cpp | 12 +++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 8a037e7c3a..4673f46168 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -250,4 +250,9 @@ void __qmljs_llvm_rethrow(Context *context, Value *result) __qmljs_rethrow(context, result); } +void __qmljs_llvm_get_this_object(Context *ctx, Value *result) +{ + __qmljs_get_thisObject(ctx, result); +} + } // extern "C" diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index d7829bf034..0f882e0a3b 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -356,9 +356,15 @@ void LLVMInstructionSelection::visitString(IR::String *e) void LLVMInstructionSelection::visitName(IR::Name *e) { llvm::Value *result = newLLVMTemp(_valueTy); - llvm::Value *name = getIdentifier(*e->id); - CreateCall3(_llvmModule->getFunction("__qmljs_get_activation_property"), - _llvmFunction->arg_begin(), result, name); + + if (e->id == QStringLiteral("this")) { + CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_this_object"), + _llvmFunction->arg_begin(), result); + } else { + llvm::Value *name = getIdentifier(*e->id); + CreateCall3(_llvmModule->getFunction("__qmljs_get_activation_property"), + _llvmFunction->arg_begin(), result, name); + } _llvmValue = CreateLoad(result); } -- cgit v1.2.3 From 60006079bbf5715a753b4bd3bceb1f27249e1736 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 6 Jun 2012 18:02:34 +0200 Subject: Resolve the library name --- main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 671f057472..389e57deec 100644 --- a/main.cpp +++ b/main.cpp @@ -144,7 +144,8 @@ int evaluateCompiledCode(const QStringList &files) using namespace QQmlJS; foreach (const QString &libName, files) { - QLibrary lib(libName); + QFileInfo libInfo(libName); + QLibrary lib(libInfo.absoluteFilePath()); lib.load(); QFunctionPointer ptr = lib.resolve("_25_entry"); void (*code)(VM::Context *) = (void (*)(VM::Context *)) ptr; -- cgit v1.2.3 From 5c869a3c812e443fe2d6598ddbeafa42634d11ed Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Thu, 7 Jun 2012 14:28:42 +0200 Subject: Delete properties --- llvm_runtime.cpp | 20 ++++++++++++++++++++ qmljs_objects.h | 52 +++++++++++++++++++++++++++++++++++++++++++++------- qmljs_runtime.cpp | 40 ++++++++++++++++++++++++++++++++++------ qmljs_runtime.h | 6 +++++- qv4ir_p.h | 1 + qv4isel_llvm.cpp | 34 ++++++++++++++++++++++++++++++++++ 6 files changed, 139 insertions(+), 14 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 4673f46168..87b9fb04c0 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -255,4 +255,24 @@ void __qmljs_llvm_get_this_object(Context *ctx, Value *result) __qmljs_get_thisObject(ctx, result); } +void __qmljs_llvm_delete_subscript(Context *ctx, Value *result, Value *base, Value *index) +{ + __qmljs_delete_subscript(ctx, result, base, index); +} + +void __qmljs_llvm_delete_member(Context *ctx, Value *result, Value *base, String *name) +{ + __qmljs_delete_member(ctx, result, base, name); +} + +void __qmljs_llvm_delete_property(Context *ctx, Value *result, String *name) +{ + __qmljs_delete_property(ctx, result, name); +} + +void __qmljs_llvm_delete_value(Context *ctx, Value *result, Value *value) +{ + __qmljs_delete_value(ctx, result, value); +} + } // extern "C" diff --git a/qmljs_objects.h b/qmljs_objects.h index ca6997b66b..afab547103 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -6,6 +6,7 @@ #include #include +#include #include namespace QQmlJS { @@ -77,8 +78,17 @@ struct Property { Property *next; int index; - Property(String *name, const Value &value, PropertyAttributes flags = NoAttributes) - : name(name), value(value), attributes(flags), next(0), index(-1) {} + inline Property(String *name, const Value &value, PropertyAttributes flags = NoAttributes) + { init(name, value, flags); } + + inline void init(String *name, const Value &value, PropertyAttributes flags = NoAttributes) + { + this->name = name; + this->value = value; + this->attributes = flags; + this->next = 0; + this->index = -1; + } inline bool isWritable() const { return attributes & WritableAttribute; } inline bool isEnumerable() const { return attributes & EnumerableAttribute; } @@ -96,6 +106,7 @@ public: Table() : _properties(0) , _buckets(0) + , _freeList(0) , _propertyCount(-1) , _bucketCount(0) , _allocated(0) {} @@ -115,16 +126,34 @@ public: bool remove(String *name) { - Q_UNUSED(name); - assert(!"TODO"); - return false; + if (Property *prop = find(name)) { + // ### TODO check if the property can be removed + + Property *bucket = _buckets[prop->hashValue() % _bucketCount]; + if (bucket == prop) { + bucket = bucket->next; + } else { + for (Property *it = bucket; it; it = it->next) { + if (it->next == prop) { + it->next = it->next->next; + break; + } + } + } + + _properties[prop->index] = 0; + prop->next = _freeList; + _freeList = prop; + } + + return true; } Property *find(String *name) const { if (_properties) { for (Property *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { - if (prop->name == name || prop->hasName(name)) + if (prop && (prop->name == name || prop->hasName(name))) return prop; } } @@ -151,7 +180,15 @@ public: _properties = properties; } - Property *prop = new Property(name, value); + Property *prop; + if (_freeList) { + prop = _freeList; + _freeList = _freeList->next; + prop->init(name, value); + } else { + prop = new Property(name, value); + } + prop->index = _propertyCount; _properties[_propertyCount] = prop; @@ -189,6 +226,7 @@ private: private: Property **_properties; Property **_buckets; + Property *_freeList; int _propertyCount; int _bucketCount; int _allocated; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 4ddd4c10c8..4bcef59bfa 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -410,13 +410,41 @@ void __qmljs_string_literal_function(Context *ctx, Value *result) __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("function"))); } -void __qmljs_delete(Context *ctx, Value *result, const Value *value) +void __qmljs_delete_subscript(Context *ctx, Value *result, Value *base, Value *index) +{ + if (ArrayObject *a = base->asArrayObject()) { + if (index->isNumber()) { + const quint32 n = index->numberValue; + if (n < a->value.size()) { + a->value.assign(n, Value::undefinedValue()); + __qmljs_init_boolean(result, true); + return; + } + } + } + + String *name = index->toString(ctx); + __qmljs_delete_member(ctx, result, base, name); +} + +void __qmljs_delete_member(Context *ctx, Value *result, Value *base, String *name) +{ + Value obj = base->toObject(ctx); + __qmljs_init_boolean(result, obj.objectValue->deleteProperty(ctx, name, true)); +} + +void __qmljs_delete_property(Context *ctx, Value *result, String *name) +{ + Value obj = ctx->activation; + if (! obj.isObject()) + obj = ctx->engine->globalObject; + __qmljs_init_boolean(result, obj.objectValue->deleteProperty(ctx, name, true)); +} + +void __qmljs_delete_value(Context *ctx, Value *result, Value *value) { - Q_UNIMPLEMENTED(); - (void) ctx; - (void) result; - (void) value; - assert(!"TODO"); + Q_UNUSED(value); + __qmljs_throw_type_error(ctx, result); // ### throw syntax error } void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Value *right) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index ae69b23d15..642619114a 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -151,7 +151,6 @@ bool __qmljs_equal(Context *ctx, const Value *x, const Value *y); bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y); // unary operators -void __qmljs_delete(Context *ctx, Value *result, const Value *value); void __qmljs_postincr(Context *ctx, Value *result, const Value *value); void __qmljs_postdecr(Context *ctx, Value *result, const Value *value); void __qmljs_uplus(Context *ctx, Value *result, const Value *value); @@ -161,6 +160,11 @@ void __qmljs_not(Context *ctx, Value *result, const Value *value); void __qmljs_preincr(Context *ctx, Value *result, const Value *value); void __qmljs_predecr(Context *ctx, Value *result, const Value *value); +void __qmljs_delete_subscript(Context *ctx, Value *result, Value *base, Value *index); +void __qmljs_delete_member(Context *ctx, Value *result, Value *base, String *name); +void __qmljs_delete_property(Context *ctx, Value *result, String *name); +void __qmljs_delete_value(Context *ctx, Value *result, Value *value); + void __qmljs_typeof(Context *ctx, Value *result, const Value *value); void __qmljs_throw(Context *context, Value *value); void __qmljs_rethrow(Context *context, Value *result); diff --git a/qv4ir_p.h b/qv4ir_p.h index 31914265cb..95cc28d8d9 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -261,6 +261,7 @@ struct Name: Expr { enum Builtin { builtin_invalid, builtin_typeof, + builtin_delete, builtin_throw, builtin_rethrow }; diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 0f882e0a3b..7c666c7fe2 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -595,6 +595,40 @@ void LLVMInstructionSelection::genCallName(IR::Call *e, llvm::Value *result) _llvmValue = CreateLoad(result); return; + case IR::Name::builtin_delete: { + if (IR::Subscript *subscript = e->args->expr->asSubscript()) { + CreateCall4(_llvmModule->getFunction("__qmljs_llvm_delete_subscript"), + _llvmFunction->arg_begin(), + result, + getLLVMTempReference(subscript->base), + getLLVMTempReference(subscript->index)); + _llvmValue = CreateLoad(result); + return; + } else if (IR::Member *member = e->args->expr->asMember()) { + CreateCall4(_llvmModule->getFunction("__qmljs_llvm_delete_member"), + _llvmFunction->arg_begin(), + result, + getLLVMTempReference(member->base), + getIdentifier(*member->name)); + _llvmValue = CreateLoad(result); + return; + } else if (IR::Name *name = e->args->expr->asName()) { + CreateCall3(_llvmModule->getFunction("__qmljs_llvm_delete_property"), + _llvmFunction->arg_begin(), + result, + getIdentifier(*name->id)); + _llvmValue = CreateLoad(result); + return; + } else { + CreateCall3(_llvmModule->getFunction("__qmljs_llvm_delete_value"), + _llvmFunction->arg_begin(), + result, + getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + } + } break; + case IR::Name::builtin_throw: CreateCall2(_llvmModule->getFunction("__qmljs_llvm_throw"), _llvmFunction->arg_begin(), getLLVMTempReference(e->args->expr)); -- cgit v1.2.3 From 56de925f00696de45e2d2132c46a4f6cee563ec9 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Thu, 7 Jun 2012 15:28:45 +0200 Subject: Generate LLVM code for constructor calls --- llvm_runtime.cpp | 15 ++++++++-- qmljs_runtime.cpp | 10 +++++++ qmljs_runtime.h | 2 ++ qv4isel_llvm.cpp | 84 ++++++++++++++++++++++++++++++++++++++++-------------- qv4isel_llvm_p.h | 4 ++- qv4isel_x86_64.cpp | 3 ++ 6 files changed, 94 insertions(+), 24 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 87b9fb04c0..4f7375a64a 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -175,9 +175,9 @@ void __qmljs_llvm_not(Context *ctx, Value *result, const Value *value) __qmljs_not(ctx, result, value); } -String *__qmljs_llvm_get_identifier(Context *ctx, const char *str) +String *__qmljs_llvm_identifier_from_utf8(Context *ctx, const char *str) { - return __qmljs_string_from_utf8(ctx, str); // ### make it unique + return __qmljs_identifier_from_utf8(ctx, str); // ### make it unique } void __qmljs_llvm_call_activation_property(Context *context, Value *result, String *name, Value *args, int argc) @@ -195,6 +195,11 @@ void __qmljs_llvm_construct_activation_property(Context *context, Value *result, __qmljs_construct_activation_property(context, result, name, args, argc); } +void __qmljs_llvm_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc) +{ + __qmljs_construct_value(context, result, func, args, argc); +} + void __qmljs_llvm_get_activation_property(Context *ctx, Value *result, String *name) { __qmljs_get_activation_property(ctx, result, name); @@ -275,4 +280,10 @@ void __qmljs_llvm_delete_value(Context *ctx, Value *result, Value *value) __qmljs_delete_value(ctx, result, value); } +void __qmljs_llvm_init_this_object(Context *ctx) +{ + if (ctx->calledAsConstructor) + __qmljs_new_object(ctx, &ctx->thisObject); +} + } // extern "C" diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 4bcef59bfa..0680955f9c 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -494,6 +494,11 @@ String *__qmljs_string_from_utf8(Context *ctx, const char *s) return ctx->engine->newString(QString::fromUtf8(s)); } +String *__qmljs_identifier_from_utf8(Context *ctx, const char *s) +{ + return ctx->engine->identifier(QString::fromUtf8(s)); +} + int __qmljs_string_length(Context *, String *string) { return string->toQString().length(); @@ -581,6 +586,11 @@ void __qmljs_throw_type_error(Context *ctx, Value *result) *result = ctx->result; } +void __qmljs_new_object(Context *ctx, Value *result) +{ + __qmljs_init_object(result, ctx->engine->newObject()); +} + void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean) { Value value; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 642619114a..d6ab5e20cb 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -99,10 +99,12 @@ void __qmljs_string_from_number(Context *ctx, Value *result, double number); bool __qmljs_string_compare(Context *ctx, String *left, String *right); bool __qmljs_string_equal(Context *ctx, String *left, String *right); String *__qmljs_string_concat(Context *ctx, String *first, String *second); +String *__qmljs_identifier_from_utf8(Context *ctx, const char *s); // objects void __qmljs_object_default_value(Context *ctx, Value *result, const Value *object, int typeHint); void __qmljs_throw_type_error(Context *ctx, Value *result); +void __qmljs_new_object(Context *ctx, Value *result); void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean); void __qmljs_new_number_object(Context *ctx, Value *result, double n); void __qmljs_new_string_object(Context *ctx, Value *result, String *string); diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 7c666c7fe2..7ec7065a16 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -106,6 +106,10 @@ llvm::Function *LLVMInstructionSelection::compileLLVMFunction(IR::Function *func qSwap(_tempMap, tempMap); qSwap(_blockMap, blockMap); + // create the LLVM blocks + foreach (IR::BasicBlock *block, _function->basicBlocks) + (void) getLLVMBasicBlock(block); + // entry block SetInsertPoint(getLLVMBasicBlock(_function->basicBlocks.first())); @@ -122,6 +126,9 @@ llvm::Function *LLVMInstructionSelection::compileLLVMFunction(IR::Function *func CreateStore(llvm::Constant::getNullValue(_valueTy), t); } + CreateCall(_llvmModule->getFunction("__qmljs_llvm_init_this_object"), + _llvmFunction->arg_begin()); + foreach (IR::BasicBlock *block, _function->basicBlocks) { qSwap(_block, block); SetInsertPoint(getLLVMBasicBlock(_block)); @@ -222,7 +229,7 @@ llvm::Value *LLVMInstructionSelection::getStringPtr(const QString &s) llvm::Value *LLVMInstructionSelection::getIdentifier(const QString &s) { llvm::Value *str = getStringPtr(s); - llvm::Value *id = CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_identifier"), + llvm::Value *id = CreateCall2(_llvmModule->getFunction("__qmljs_identifier_from_utf8"), _llvmFunction->arg_begin(), str); return id; } @@ -576,6 +583,29 @@ void LLVMInstructionSelection::genCallTemp(IR::Call *e, llvm::Value *result) _llvmValue = CreateLoad(result); } +void LLVMInstructionSelection::genConstructTemp(IR::New *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + llvm::Value *func = getLLVMTempReference(e->base); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + func, + args, + getInt32(argc) + }; + + CreateCall(_llvmModule->getFunction("__qmljs_llvm_construct_value"), actuals); + + _llvmValue = CreateLoad(result); +} + void LLVMInstructionSelection::genCallName(IR::Call *e, llvm::Value *result) { IR::Name *base = e->base->asName(); @@ -656,6 +686,28 @@ void LLVMInstructionSelection::genCallName(IR::Call *e, llvm::Value *result) } } +void LLVMInstructionSelection::genConstructName(IR::New *e, llvm::Value *result) +{ + IR::Name *base = e->base->asName(); + + if (! result) + result = newLLVMTemp(_valueTy); + + if (! base->id) { + Q_UNREACHABLE(); + } else { + llvm::Value *name = getIdentifier(*base->id); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + CreateCall5(_llvmModule->getFunction("__qmljs_construct_activation_property"), + _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); + + _llvmValue = CreateLoad(result); + } +} + void LLVMInstructionSelection::visitCall(IR::Call *e) { if (e->base->asMember()) { @@ -684,30 +736,20 @@ void LLVMInstructionSelection::visitNew(IR::New *e) { if (e->base->asMember()) { genConstructMember(e); - return; - } - - llvm::Value *func = 0; - llvm::Value *base = 0; - if (IR::Temp *t = e->base->asTemp()) { - base = getLLVMTemp(t); - func = _llvmModule->getFunction("__qmljs_llvm_construct_value"); - } else if (IR::Name *n = e->base->asName()) { - if (n->id) { - llvm::Value *str = getStringPtr(*n->id); - base = CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_identifier"), - _llvmFunction->arg_begin(), str); - func = _llvmModule->getFunction("__qmljs_llvm_construct_activation_property"); - } - } + } else if (e->base->asTemp()) { + genConstructTemp(e); + } else if (e->base->asName()) { + genConstructName(e); + } else if (IR::Temp *t = e->base->asTemp()) { + llvm::Value *base = getLLVMTemp(t); - int argc = 0; - llvm::Value *args = genArguments(e->args, argc); + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); - if (func) { llvm::Value *result = newLLVMTemp(_valueTy); CreateStore(llvm::Constant::getNullValue(_valueTy), result); - CreateCall5(func, _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); + CreateCall5(_llvmModule->getFunction("__qmljs_llvm_construct_value"), + _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); _llvmValue = CreateLoad(result); } else { Q_UNIMPLEMENTED(); diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 64511f86c5..5f67f2d66a 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -30,9 +30,11 @@ public: void genBinop(llvm::Value *result, IR::Binop *e); llvm::AllocaInst *newLLVMTemp(llvm::Type *type, llvm::Value *size = 0); llvm::Value * genArguments(IR::ExprList *args, int &argc); - void genCallMember(IR::Call *e, llvm::Value *result = 0); void genCallTemp(IR::Call *e, llvm::Value *result = 0); void genCallName(IR::Call *e, llvm::Value *result = 0); + void genCallMember(IR::Call *e, llvm::Value *result = 0); + void genConstructTemp(IR::New *e, llvm::Value *result = 0); + void genConstructName(IR::New *e, llvm::Value *result = 0); void genConstructMember(IR::New *e, llvm::Value *result = 0); void genMoveSubscript(IR::Move *s); void genMoveMember(IR::Move *s); diff --git a/qv4isel_x86_64.cpp b/qv4isel_x86_64.cpp index e60dd5655f..a0e3c9dab0 100644 --- a/qv4isel_x86_64.cpp +++ b/qv4isel_x86_64.cpp @@ -219,6 +219,9 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); amd64_call_code(_codePtr, __qmljs_builtin_typeof); break; + case IR::Name::builtin_delete: + Q_UNREACHABLE(); + break; case IR::Name::builtin_throw: amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); -- cgit v1.2.3 From 1e74b19d87d4c18725ab0b9d2c367860416b23ee Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Thu, 7 Jun 2012 15:49:50 +0200 Subject: Fix LLVM code generation for unary expressions --- qv4isel_llvm.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 7ec7065a16..6d8f0017c8 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -419,10 +419,10 @@ void LLVMInstructionSelection::genUnop(llvm::Value *result, IR::Unop *e) Q_UNREACHABLE(); break; - case IR::OpNot: _llvmModule->getFunction("__qmljs_llvm_not"); break; - case IR::OpUMinus: _llvmModule->getFunction("__qmljs_llvm_uminus"); break; - case IR::OpUPlus: _llvmModule->getFunction("__qmljs_llvm_uplus"); break; - case IR::OpCompl: _llvmModule->getFunction("__qmljs_llvm_compl"); break; + case IR::OpNot: op = _llvmModule->getFunction("__qmljs_llvm_not"); break; + case IR::OpUMinus: op = _llvmModule->getFunction("__qmljs_llvm_uminus"); break; + case IR::OpUPlus: op = _llvmModule->getFunction("__qmljs_llvm_uplus"); break; + case IR::OpCompl: op = _llvmModule->getFunction("__qmljs_llvm_compl"); break; } CreateCall3(op, _llvmFunction->arg_begin(), result, expr); -- cgit v1.2.3 From 3b7b0fe81dd90de89f348bda618a9698a7540ab8 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Thu, 7 Jun 2012 16:06:12 +0200 Subject: Import the ARM macro assembler from the mono project. --- amd64-codegen.h | 1831 ---------------------------------- asm/Makefile.am | 32 + asm/amd64-codegen.h | 1831 ++++++++++++++++++++++++++++++++++ asm/arm-codegen.c | 193 ++++ asm/arm-codegen.h | 1103 +++++++++++++++++++++ asm/arm-dis.c | 509 ++++++++++ asm/arm-dis.h | 41 + asm/arm-fpa-codegen.h | 198 ++++ asm/arm-vfp-codegen.h | 235 +++++ asm/arm-wmmx.h | 177 ++++ asm/arm_dpimacros.h | 1603 ++++++++++++++++++++++++++++++ asm/cmp_macros.th | 56 ++ asm/dpi_macros.th | 112 +++ asm/dpiops.sh | 30 + asm/fpa_macros.th | 15 + asm/fpam_macros.th | 14 + asm/fpaops.sh | 24 + asm/mov_macros.th | 121 +++ asm/vfp_macros.th | 15 + asm/vfpm_macros.th | 14 + asm/vfpops.sh | 24 + asm/x86-codegen.h | 2640 +++++++++++++++++++++++++++++++++++++++++++++++++ qv4isel_x86_64.cpp | 2 +- v4.pro | 13 +- x86-codegen.h | 2640 ------------------------------------------------- 25 files changed, 8999 insertions(+), 4474 deletions(-) delete mode 100644 amd64-codegen.h create mode 100644 asm/Makefile.am create mode 100644 asm/amd64-codegen.h create mode 100644 asm/arm-codegen.c create mode 100644 asm/arm-codegen.h create mode 100644 asm/arm-dis.c create mode 100644 asm/arm-dis.h create mode 100644 asm/arm-fpa-codegen.h create mode 100644 asm/arm-vfp-codegen.h create mode 100644 asm/arm-wmmx.h create mode 100644 asm/arm_dpimacros.h create mode 100644 asm/cmp_macros.th create mode 100644 asm/dpi_macros.th create mode 100755 asm/dpiops.sh create mode 100644 asm/fpa_macros.th create mode 100644 asm/fpam_macros.th create mode 100755 asm/fpaops.sh create mode 100644 asm/mov_macros.th create mode 100644 asm/vfp_macros.th create mode 100644 asm/vfpm_macros.th create mode 100755 asm/vfpops.sh create mode 100644 asm/x86-codegen.h delete mode 100644 x86-codegen.h diff --git a/amd64-codegen.h b/amd64-codegen.h deleted file mode 100644 index 3dea9cd306..0000000000 --- a/amd64-codegen.h +++ /dev/null @@ -1,1831 +0,0 @@ -/* - * amd64-codegen.h: Macros for generating amd64 code - * - * Authors: - * Paolo Molaro (lupus@ximian.com) - * Intel Corporation (ORP Project) - * Sergey Chaban (serge@wildwestsoftware.com) - * Dietmar Maurer (dietmar@ximian.com) - * Patrik Torstensson - * Zalman Stern - * - * Copyright (C) 2000 Intel Corporation. All rights reserved. - * Copyright (C) 2001, 2002 Ximian, Inc. - */ - -#ifndef AMD64_H -#define AMD64_H - -//#include - -typedef enum { - AMD64_RAX = 0, - AMD64_RCX = 1, - AMD64_RDX = 2, - AMD64_RBX = 3, - AMD64_RSP = 4, - AMD64_RBP = 5, - AMD64_RSI = 6, - AMD64_RDI = 7, - AMD64_R8 = 8, - AMD64_R9 = 9, - AMD64_R10 = 10, - AMD64_R11 = 11, - AMD64_R12 = 12, - AMD64_R13 = 13, - AMD64_R14 = 14, - AMD64_R15 = 15, - AMD64_RIP = 16, - AMD64_NREG -} AMD64_Reg_No; - -typedef enum { - AMD64_XMM0 = 0, - AMD64_XMM1 = 1, - AMD64_XMM2 = 2, - AMD64_XMM3 = 3, - AMD64_XMM4 = 4, - AMD64_XMM5 = 5, - AMD64_XMM6 = 6, - AMD64_XMM7 = 7, - AMD64_XMM8 = 8, - AMD64_XMM9 = 9, - AMD64_XMM10 = 10, - AMD64_XMM11 = 11, - AMD64_XMM12 = 12, - AMD64_XMM13 = 13, - AMD64_XMM14 = 14, - AMD64_XMM15 = 15, - AMD64_XMM_NREG = 16, -} AMD64_XMM_Reg_No; - -typedef enum -{ - AMD64_REX_B = 1, /* The register in r/m field, base register in SIB byte, or reg in opcode is 8-15 rather than 0-7 */ - AMD64_REX_X = 2, /* The index register in SIB byte is 8-15 rather than 0-7 */ - AMD64_REX_R = 4, /* The reg field of ModRM byte is 8-15 rather than 0-7 */ - AMD64_REX_W = 8 /* Opeartion is 64-bits instead of 32 (default) or 16 (with 0x66 prefix) */ -} AMD64_REX_Bits; - -#if defined(__default_codegen__) - -#define amd64_codegen_pre(inst) -#define amd64_codegen_post(inst) - -#elif defined(__native_client_codegen__) - -#define amd64_codegen_pre(inst) guint8* _codegen_start = (inst); amd64_nacl_instruction_pre(); -#define amd64_codegen_post(inst) (amd64_nacl_instruction_post(&_codegen_start, &(inst)), _codegen_start); - -/* Because of rex prefixes, etc, call sequences are not constant size. */ -/* These pre- and post-sequence hooks remedy this by aligning the call */ -/* sequence after we emit it, since we will know the exact size then. */ -#define amd64_call_sequence_pre(inst) guint8* _code_start = (inst); -#define amd64_call_sequence_post(inst) \ - (mono_nacl_align_call(&_code_start, &(inst)), _code_start); - -/* Native client can load/store using one of the following registers */ -/* as a base: rip, r15, rbp, rsp. Any other base register needs to have */ -/* its upper 32 bits cleared and reference memory using r15 as the base. */ -#define amd64_is_valid_nacl_base(reg) \ - ((reg) == AMD64_RIP || (reg) == AMD64_R15 || \ - (reg) == AMD64_RBP || (reg) == AMD64_RSP) - -#endif /*__native_client_codegen__*/ - -#ifdef TARGET_WIN32 -#define AMD64_ARG_REG1 AMD64_RCX -#define AMD64_ARG_REG2 AMD64_RDX -#define AMD64_ARG_REG3 AMD64_R8 -#define AMD64_ARG_REG4 AMD64_R9 -#else -#define AMD64_ARG_REG1 AMD64_RDI -#define AMD64_ARG_REG2 AMD64_RSI -#define AMD64_ARG_REG3 AMD64_RDX -#define AMD64_ARG_REG4 AMD64_RCX -#endif - -#ifdef TARGET_WIN32 -#define AMD64_CALLEE_REGS ((1< 4) ? AMD64_REX_W : 0) | \ - (((reg_modrm) > 7) ? AMD64_REX_R : 0) | \ - (((reg_index) > 7) ? AMD64_REX_X : 0) | \ - (((reg_rm_base_opcode) > 7) ? AMD64_REX_B : 0); \ - if ((_amd64_rex_bits != 0) || (((width) == 1))) *(inst)++ = AMD64_REX(_amd64_rex_bits); \ - } while (0) -#elif defined(__native_client_codegen__) -#define amd64_emit_rex(inst, width, reg_modrm, reg_index, reg_rm_base_opcode) do \ - { \ - unsigned char _amd64_rex_bits = \ - (((width) > 4) ? AMD64_REX_W : 0) | \ - (((reg_modrm) > 7) ? AMD64_REX_R : 0) | \ - (((reg_index) > 7) ? AMD64_REX_X : 0) | \ - (((reg_rm_base_opcode) > 7) ? AMD64_REX_B : 0); \ - amd64_nacl_tag_rex((inst)); \ - if ((_amd64_rex_bits != 0) || (((width) == 1))) *(inst)++ = AMD64_REX(_amd64_rex_bits); \ - } while (0) -#endif - -typedef union { - guint64 val; - unsigned char b [8]; -} amd64_imm_buf; - -#include "x86-codegen.h" - -/* In 64 bit mode, all registers have a low byte subregister */ -#undef X86_IS_BYTE_REG -#define X86_IS_BYTE_REG(reg) 1 - -#define amd64_modrm_mod(modrm) ((modrm) >> 6) -#define amd64_modrm_reg(modrm) (((modrm) >> 3) & 0x7) -#define amd64_modrm_rm(modrm) ((modrm) & 0x7) - -#define amd64_rex_r(rex) ((((rex) >> 2) & 0x1) << 3) -#define amd64_rex_x(rex) ((((rex) >> 1) & 0x1) << 3) -#define amd64_rex_b(rex) ((((rex) >> 0) & 0x1) << 3) - -#define amd64_sib_scale(sib) ((sib) >> 6) -#define amd64_sib_index(sib) (((sib) >> 3) & 0x7) -#define amd64_sib_base(sib) ((sib) & 0x7) - -#define amd64_is_imm32(val) ((gint64)val >= -((gint64)1<<31) && (gint64)val <= (((gint64)1<<31)-1)) - -#define x86_imm_emit64(inst,imm) \ - do { \ - amd64_imm_buf imb; \ - imb.val = (guint64) (imm); \ - *(inst)++ = imb.b [0]; \ - *(inst)++ = imb.b [1]; \ - *(inst)++ = imb.b [2]; \ - *(inst)++ = imb.b [3]; \ - *(inst)++ = imb.b [4]; \ - *(inst)++ = imb.b [5]; \ - *(inst)++ = imb.b [6]; \ - *(inst)++ = imb.b [7]; \ - } while (0) - -#define amd64_membase_emit(inst,reg,basereg,disp) do { \ - if ((basereg) == AMD64_RIP) { \ - x86_address_byte ((inst), 0, (reg)&0x7, 5); \ - x86_imm_emit32 ((inst), (disp)); \ - } \ - else \ - x86_membase_emit ((inst),(reg)&0x7, (basereg)&0x7, (disp)); \ -} while (0) - -#define amd64_alu_reg_imm_size_body(inst,opc,reg,imm,size) \ - do { \ - if (x86_is_imm8((imm))) { \ - amd64_emit_rex(inst, size, 0, 0, (reg)); \ - *(inst)++ = (unsigned char)0x83; \ - x86_reg_emit ((inst), (opc), (reg)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else if ((reg) == AMD64_RAX) { \ - amd64_emit_rex(inst, size, 0, 0, 0); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ - x86_imm_emit32 ((inst), (imm)); \ - } else { \ - amd64_emit_rex(inst, size, 0, 0, (reg)); \ - *(inst)++ = (unsigned char)0x81; \ - x86_reg_emit ((inst), (opc), (reg)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define amd64_alu_reg_reg_size_body(inst,opc,dreg,reg,size) \ - do { \ - amd64_emit_rex(inst, size, (dreg), 0, (reg)); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#if defined(__default_codegen__) - -#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) \ - amd64_alu_reg_imm_size_body((inst), (opc), (reg), (imm), (size)) - -#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) \ - amd64_alu_reg_reg_size_body((inst), (opc), (dreg), (reg), (size)) - -#elif defined(__native_client_codegen__) -/* NaCl modules may not directly update RSP or RBP other than direct copies */ -/* between them. Instead the lower 4 bytes are updated and then added to R15 */ -#define amd64_is_nacl_stack_reg(reg) (((reg) == AMD64_RSP) || ((reg) == AMD64_RBP)) - -#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) \ - do{ \ - amd64_codegen_pre(inst); \ - if (amd64_is_nacl_stack_reg(reg)) { \ - if (((opc) != X86_ADD) && ((opc) != X86_SUB)) \ - g_assert_not_reached(); \ - amd64_alu_reg_imm_size_body((inst), (opc), (reg), (imm), 4); \ - /* Use LEA instead of ADD to preserve flags */ \ - amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ - } else { \ - amd64_alu_reg_imm_size_body((inst), (opc), (reg), (imm), (size)); \ - } \ - amd64_codegen_post(inst); \ - } while(0) - -#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) \ - do { \ - amd64_codegen_pre(inst); \ - if (amd64_is_nacl_stack_reg((dreg)) && ((reg) != AMD64_R15)) { \ - if (((opc) != X86_ADD && (opc) != X86_SUB)) \ - g_assert_not_reached(); \ - amd64_alu_reg_reg_size_body((inst), (opc), (dreg), (reg), 4); \ - /* Use LEA instead of ADD to preserve flags */ \ - amd64_lea_memindex_size((inst), (dreg), (dreg), 0, AMD64_R15, 0, 8); \ - } else { \ - amd64_alu_reg_reg_size_body((inst), (opc), (dreg), (reg), (size)); \ - } \ - amd64_codegen_post(inst); \ - } while (0) - -#endif /*__native_client_codegen__*/ - -#define amd64_alu_reg_imm(inst,opc,reg,imm) amd64_alu_reg_imm_size((inst),(opc),(reg),(imm),8) - -#define amd64_alu_reg_reg(inst,opc,dreg,reg) amd64_alu_reg_reg_size ((inst),(opc),(dreg),(reg),8) - -#define amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,size) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex ((inst),(size),(reg),0,(basereg)); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ - amd64_membase_emit (inst, reg, basereg, disp); \ - amd64_codegen_post(inst); \ -} while (0) - -#define amd64_mov_regp_reg(inst,regp,reg,size) \ - do { \ - amd64_codegen_pre(inst); \ - if ((size) == 2) \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - amd64_emit_rex(inst, (size), (reg), 0, (regp)); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x88; break; \ - case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ - default: assert (0); \ - } \ - x86_regp_emit ((inst), (reg), (regp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_mov_membase_reg(inst,basereg,disp,reg,size) \ - do { \ - amd64_codegen_pre(inst); \ - if ((size) == 2) \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x88; break; \ - case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ - default: assert (0); \ - } \ - x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_mov_mem_reg(inst,mem,reg,size) \ - do { \ - amd64_codegen_pre(inst); \ - if ((size) == 2) \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - amd64_emit_rex(inst, (size), (reg), 0, 0); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x88; break; \ - case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ - default: assert (0); \ - } \ - x86_address_byte ((inst), 0, (reg), 4); \ - x86_address_byte ((inst), 0, 4, 5); \ - x86_imm_emit32 ((inst), (mem)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_mov_reg_reg(inst,dreg,reg,size) \ - do { \ - amd64_codegen_pre(inst); \ - if ((size) == 2) \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - amd64_emit_rex(inst, (size), (dreg), 0, (reg)); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x8a; break; \ - case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - x86_reg_emit ((inst), (dreg), (reg)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_mov_reg_mem_body(inst,reg,mem,size) \ - do { \ - amd64_codegen_pre(inst); \ - if ((size) == 2) \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - amd64_emit_rex(inst, (size), (reg), 0, 0); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x8a; break; \ - case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - x86_address_byte ((inst), 0, (reg), 4); \ - x86_address_byte ((inst), 0, 4, 5); \ - x86_imm_emit32 ((inst), (mem)); \ - amd64_codegen_post(inst); \ - } while (0) - -#if defined(__default_codegen__) -#define amd64_mov_reg_mem(inst,reg,mem,size) \ - do { \ - amd64_mov_reg_mem_body((inst),(reg),(mem),(size)); \ - } while (0) -#elif defined(__native_client_codegen__) -/* We have to re-base memory reads because memory isn't zero based. */ -#define amd64_mov_reg_mem(inst,reg,mem,size) \ - do { \ - amd64_mov_reg_membase((inst),(reg),AMD64_R15,(mem),(size)); \ - } while (0) -#endif /* __native_client_codegen__ */ - -#define amd64_mov_reg_membase_body(inst,reg,basereg,disp,size) \ - do { \ - if ((size) == 2) \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x8a; break; \ - case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define amd64_mov_reg_memindex_size_body(inst,reg,basereg,disp,indexreg,shift,size) \ - do { \ - amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); \ - x86_mov_reg_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(size) == 8 ? 4 : (size)); \ - } while (0) - -#if defined(__default_codegen__) - -#define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) \ - amd64_mov_reg_memindex_size_body((inst),(reg),(basereg),(disp),(indexreg),(shift),(size)) -#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) \ - do { \ - amd64_mov_reg_membase_body((inst), (reg), (basereg), (disp), (size)); \ - } while (0) - -#elif defined(__native_client_codegen__) - -#define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) \ - do { \ - amd64_codegen_pre(inst); \ - if (amd64_is_nacl_stack_reg((reg))) { \ - /* Clear upper 32 bits with mov of size 4 */ \ - amd64_mov_reg_memindex_size_body((inst), (reg), (basereg), (disp), (indexreg), (shift), 4); \ - /* Add %r15 using LEA to preserve flags */ \ - amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ - } else { \ - amd64_mov_reg_memindex_size_body((inst), (reg), (basereg), (disp), (indexreg), (shift), (size)); \ - } \ - amd64_codegen_post(inst); \ - } while(0) - -#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) \ - do { \ - amd64_codegen_pre(inst); \ - if (amd64_is_nacl_stack_reg((reg))) { \ - /* Clear upper 32 bits with mov of size 4 */ \ - amd64_mov_reg_membase_body((inst), (reg), (basereg), (disp), 4); \ - /* Add %r15 */ \ - amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ - } else { \ - amd64_mov_reg_membase_body((inst), (reg), (basereg), (disp), (size)); \ - } \ - amd64_codegen_post(inst); \ - } while (0) - -#endif /*__native_client_codegen__*/ - -#define amd64_movzx_reg_membase(inst,reg,basereg,disp,size) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb6; break; \ - case 2: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb7; break; \ - case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movsxd_reg_mem(inst,reg,mem) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst,8,(reg),0,0); \ - *(inst)++ = (unsigned char)0x63; \ - x86_mem_emit ((inst), ((reg)&0x7), (mem)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movsxd_reg_membase(inst,reg,basereg,disp) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst,8,(reg),0,(basereg)); \ - *(inst)++ = (unsigned char)0x63; \ - x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movsxd_reg_reg(inst,dreg,reg) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst,8,(dreg),0,(reg)); \ - *(inst)++ = (unsigned char)0x63; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - amd64_codegen_post(inst); \ - } while (0) - -/* Pretty much the only instruction that supports a 64-bit immediate. Optimize for common case of - * 32-bit immediate. Pepper with casts to avoid warnings. - */ -#define amd64_mov_reg_imm_size(inst,reg,imm,size) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst, (size), 0, 0, (reg)); \ - *(inst)++ = (unsigned char)0xb8 + ((reg) & 0x7); \ - if ((size) == 8) \ - x86_imm_emit64 ((inst), (guint64)(imm)); \ - else \ - x86_imm_emit32 ((inst), (int)(guint64)(imm)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_mov_reg_imm(inst,reg,imm) \ - do { \ - int _amd64_width_temp = ((guint64)(imm) == (guint64)(int)(guint64)(imm)); \ - amd64_codegen_pre(inst); \ - amd64_mov_reg_imm_size ((inst), (reg), (imm), (_amd64_width_temp ? 4 : 8)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_set_reg_template(inst,reg) amd64_mov_reg_imm_size ((inst),(reg), 0, 8) - -#define amd64_set_template(inst,reg) amd64_set_reg_template((inst),(reg)) - -#define amd64_mov_membase_imm(inst,basereg,disp,imm,size) \ - do { \ - amd64_codegen_pre(inst); \ - if ((size) == 2) \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - amd64_emit_rex(inst, (size) == 1 ? 0 : (size), 0, 0, (basereg)); \ - if ((size) == 1) { \ - *(inst)++ = (unsigned char)0xc6; \ - x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else if ((size) == 2) { \ - *(inst)++ = (unsigned char)0xc7; \ - x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ - x86_imm_emit16 ((inst), (imm)); \ - } else { \ - *(inst)++ = (unsigned char)0xc7; \ - x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - amd64_codegen_post(inst); \ - } while (0) - - -#define amd64_lea_membase_body(inst,reg,basereg,disp) \ - do { \ - amd64_emit_rex(inst, 8, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)0x8d; \ - amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#if defined(__default_codegen__) -#define amd64_lea_membase(inst,reg,basereg,disp) \ - amd64_lea_membase_body((inst), (reg), (basereg), (disp)) -#elif defined(__native_client_codegen__) -/* NaCl modules may not write directly into RSP/RBP. Instead, use a */ -/* 32-bit LEA and add R15 to the effective address */ -#define amd64_lea_membase(inst,reg,basereg,disp) \ - do { \ - amd64_codegen_pre(inst); \ - if (amd64_is_nacl_stack_reg(reg)) { \ - /* 32-bit LEA */ \ - amd64_emit_rex((inst), 4, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)0x8d; \ - amd64_membase_emit((inst), (reg), (basereg), (disp)); \ - /* Use a 64-bit LEA instead of an ADD to preserve flags */ \ - amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ - } else { \ - amd64_lea_membase_body((inst), (reg), (basereg), (disp)); \ - } \ - amd64_codegen_post(inst); \ - } while (0) -#endif /*__native_client_codegen__*/ - -/* Instruction are implicitly 64-bits so don't generate REX for just the size. */ -#define amd64_push_reg(inst,reg) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst, 0, 0, 0, (reg)); \ - *(inst)++ = (unsigned char)0x50 + ((reg) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -/* Instruction is implicitly 64-bits so don't generate REX for just the size. */ -#define amd64_push_membase(inst,basereg,disp) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst, 0, 0, 0, (basereg)); \ - *(inst)++ = (unsigned char)0xff; \ - x86_membase_emit ((inst), 6, (basereg) & 0x7, (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_pop_reg_body(inst,reg) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst, 0, 0, 0, (reg)); \ - *(inst)++ = (unsigned char)0x58 + ((reg) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -#if defined(__default_codegen__) - -#define amd64_call_reg(inst,reg) \ - do { \ - amd64_emit_rex(inst, 0, 0, 0, (reg)); \ - *(inst)++ = (unsigned char)0xff; \ - x86_reg_emit ((inst), 2, ((reg) & 0x7)); \ - } while (0) - - -#define amd64_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) -#define amd64_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) - -#define amd64_pop_reg(inst,reg) amd64_pop_reg_body((inst), (reg)) - -#elif defined(__native_client_codegen__) - -/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ -#define amd64_jump_reg_size(inst,reg,size) \ - do { \ - amd64_codegen_pre((inst)); \ - amd64_alu_reg_imm_size((inst), X86_AND, (reg), (nacl_align_byte), 4); \ - amd64_alu_reg_reg_size((inst), X86_ADD, (reg), AMD64_R15, 8); \ - amd64_emit_rex ((inst),0,0,0,(reg)); \ - x86_jump_reg((inst),((reg)&0x7)); \ - amd64_codegen_post((inst)); \ - } while (0) - -/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ -#define amd64_jump_mem_size(inst,mem,size) \ - do { \ - amd64_codegen_pre((inst)); \ - amd64_mov_reg_mem((inst), (mem), AMD64_R11, 4); \ - amd64_jump_reg_size((inst), AMD64_R11, 4); \ - amd64_codegen_post((inst)); \ - } while (0) - -#define amd64_call_reg_internal(inst,reg) \ - do { \ - amd64_codegen_pre((inst)); \ - amd64_alu_reg_imm_size((inst), X86_AND, (reg), (nacl_align_byte), 4); \ - amd64_alu_reg_reg_size((inst), X86_ADD, (reg), AMD64_R15, 8); \ - amd64_emit_rex((inst), 0, 0, 0, (reg)); \ - x86_call_reg((inst), ((reg) & 0x7)); \ - amd64_codegen_post((inst)); \ - } while (0) - -#define amd64_call_reg(inst,reg) \ - do { \ - amd64_codegen_pre((inst)); \ - amd64_call_sequence_pre(inst); \ - amd64_call_reg_internal((inst), (reg)); \ - amd64_call_sequence_post(inst); \ - amd64_codegen_post((inst)); \ - } while (0) - - -#define amd64_ret(inst) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_pop_reg_body((inst), AMD64_R11); \ - amd64_jump_reg_size((inst), AMD64_R11, 8); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_leave(inst) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_mov_reg_reg((inst), AMD64_RSP, AMD64_RBP, 8); \ - amd64_pop_reg_body((inst), AMD64_R11); \ - amd64_mov_reg_reg_size((inst), AMD64_RBP, AMD64_R11, 4); \ - amd64_alu_reg_reg_size((inst), X86_ADD, AMD64_RBP, AMD64_R15, 8); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_pop_reg(inst,reg) \ - do { \ - amd64_codegen_pre(inst); \ - if (amd64_is_nacl_stack_reg((reg))) { \ - amd64_pop_reg_body((inst), AMD64_R11); \ - amd64_mov_reg_reg_size((inst), (reg), AMD64_R11, 4); \ - amd64_alu_reg_reg_size((inst), X86_ADD, (reg), AMD64_R15, 8); \ - } else { \ - amd64_pop_reg_body((inst), (reg)); \ - } \ - amd64_codegen_post(inst); \ - } while (0) - -#endif /*__native_client_codegen__*/ - -#define amd64_movsd_reg_regp(inst,reg,regp) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf2); \ - amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x10; \ - x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movsd_regp_reg(inst,regp,reg) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf2); \ - amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x11; \ - x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movss_reg_regp(inst,reg,regp) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf3); \ - amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x10; \ - x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movss_regp_reg(inst,regp,reg) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf3); \ - amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x11; \ - x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movsd_reg_membase(inst,reg,basereg,disp) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf2); \ - amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x10; \ - x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movss_reg_membase(inst,reg,basereg,disp) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf3); \ - amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x10; \ - x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movsd_membase_reg(inst,basereg,disp,reg) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf2); \ - amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x11; \ - x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movss_membase_reg(inst,basereg,disp,reg) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf3); \ - amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x11; \ - x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -/* The original inc_reg opcode is used as the REX prefix */ -#define amd64_inc_reg_size(inst,reg,size) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex ((inst),(size),0,0,(reg)); \ - *(inst)++ = (unsigned char)0xff; \ - x86_reg_emit ((inst),0,(reg) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_dec_reg_size(inst,reg,size) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex ((inst),(size),0,0,(reg)); \ - *(inst)++ = (unsigned char)0xff; \ - x86_reg_emit ((inst),1,(reg) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_fld_membase_size(inst,basereg,disp,is_double,size) do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex ((inst),0,0,0,(basereg)); \ - *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ - amd64_membase_emit ((inst), 0, (basereg), (disp)); \ - amd64_codegen_post(inst); \ -} while (0) - -#if defined (__default_codegen__) - -/* From the AMD64 Software Optimization Manual */ -#define amd64_padding_size(inst,size) \ - do { \ - switch ((size)) { \ - case 1: *(inst)++ = 0x90; break; \ - case 2: *(inst)++ = 0x66; *(inst)++ = 0x90; break; \ - case 3: *(inst)++ = 0x66; *(inst)++ = 0x66; *(inst)++ = 0x90; break; \ - default: amd64_emit_rex ((inst),8,0,0,0); x86_padding ((inst), (size) - 1); \ - }; \ - } while (0) - -#define amd64_call_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); *(inst)++ = (unsigned char)0xff; amd64_membase_emit ((inst),2, (basereg),(disp)); } while (0) -#define amd64_jump_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); *(inst)++ = (unsigned char)0xff; amd64_membase_emit ((inst), 4, (basereg), (disp)); } while (0) - -#define amd64_jump_code_size(inst,target,size) do { \ - if (amd64_is_imm32 ((gint64)(target) - (gint64)(inst))) { \ - x86_jump_code((inst),(target)); \ - } else { \ - amd64_jump_membase ((inst), AMD64_RIP, 0); \ - *(guint64*)(inst) = (guint64)(target); \ - (inst) += 8; \ - } \ -} while (0) - -#elif defined(__native_client_codegen__) - -/* The 3-7 byte NOP sequences in amd64_padding_size below are all illegal in */ -/* 64-bit Native Client because they load into rSP/rBP or use duplicate */ -/* prefixes. Instead we use the NOPs recommended in Section 3.5.1.8 of the */ -/* Intel64 and IA-32 Architectures Optimization Reference Manual and */ -/* Section 4.13 of AMD Software Optimization Guide for Family 10h Processors. */ - -#define amd64_padding_size(inst,size) \ - do { \ - unsigned char *code_start = (inst); \ - switch ((size)) { \ - /* xchg %eax,%eax, recognized by hardware as a NOP */ \ - case 1: *(inst)++ = 0x90; break; \ - /* xchg %ax,%ax */ \ - case 2: *(inst)++ = 0x66; *(inst)++ = 0x90; \ - break; \ - /* nop (%rax) */ \ - case 3: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ - *(inst)++ = 0x00; \ - break; \ - /* nop 0x0(%rax) */ \ - case 4: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ - x86_address_byte ((inst), 1, 0, AMD64_RAX); \ - x86_imm_emit8 ((inst), 0); \ - break; \ - /* nop 0x0(%rax,%rax) */ \ - case 5: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ - x86_address_byte ((inst), 1, 0, 4); \ - x86_address_byte ((inst), 0, AMD64_RAX, AMD64_RAX); \ - x86_imm_emit8 ((inst), 0); \ - break; \ - /* nopw 0x0(%rax,%rax) */ \ - case 6: *(inst)++ = 0x66; *(inst)++ = 0x0f; \ - *(inst)++ = 0x1f; \ - x86_address_byte ((inst), 1, 0, 4); \ - x86_address_byte ((inst), 0, AMD64_RAX, AMD64_RAX); \ - x86_imm_emit8 ((inst), 0); \ - break; \ - /* nop 0x0(%rax) (32-bit displacement) */ \ - case 7: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ - x86_address_byte ((inst), 2, 0, AMD64_RAX); \ - x86_imm_emit32((inst), 0); \ - break; \ - /* nop 0x0(%rax,%rax) (32-bit displacement) */ \ - case 8: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ - x86_address_byte ((inst), 2, 0, 4); \ - x86_address_byte ((inst), 0, AMD64_RAX, AMD64_RAX); \ - x86_imm_emit32 ((inst), 0); \ - break; \ - default: \ - g_assert_not_reached(); \ - } \ - g_assert(code_start + (size) == (unsigned char *)(inst)); \ - } while (0) - - -/* Size is ignored for Native Client calls, we restrict jumping to 32-bits */ -#define amd64_call_membase_size(inst,basereg,disp,size) \ - do { \ - amd64_codegen_pre((inst)); \ - amd64_call_sequence_pre(inst); \ - amd64_mov_reg_membase((inst), AMD64_R11, (basereg), (disp), 4); \ - amd64_call_reg_internal((inst), AMD64_R11); \ - amd64_call_sequence_post(inst); \ - amd64_codegen_post((inst)); \ - } while (0) - -/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ -#define amd64_jump_membase_size(inst,basereg,disp,size) \ - do { \ - amd64_mov_reg_membase((inst), AMD64_R11, (basereg), (disp), 4); \ - amd64_jump_reg_size((inst), AMD64_R11, 4); \ - } while (0) - -/* On Native Client we can't jump more than INT_MAX in either direction */ -#define amd64_jump_code_size(inst,target,size) \ - do { \ - /* x86_jump_code used twice in case of */ \ - /* relocation by amd64_codegen_post */ \ - guint8* jump_start; \ - amd64_codegen_pre(inst); \ - assert(amd64_is_imm32 ((gint64)(target) - (gint64)(inst))); \ - x86_jump_code((inst),(target)); \ - inst = amd64_codegen_post(inst); \ - jump_start = (inst); \ - x86_jump_code((inst),(target)); \ - mono_amd64_patch(jump_start, (target)); \ -} while (0) - -#endif /*__native_client_codegen__*/ - -/* - * SSE - */ - -//TODO Reorganize SSE opcode defines. - -/* Two opcode SSE defines */ - -#define emit_sse_reg_reg_op2_size(inst,dreg,reg,op1,op2,size) do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ - *(inst)++ = (unsigned char)(op1); \ - *(inst)++ = (unsigned char)(op2); \ - x86_reg_emit ((inst), (dreg), (reg)); \ - amd64_codegen_post(inst); \ -} while (0) - -#define emit_sse_reg_reg_op2(inst,dreg,reg,op1,op2) emit_sse_reg_reg_op2_size ((inst), (dreg), (reg), (op1), (op2), 0) - -#define emit_sse_reg_reg_op2_imm(inst,dreg,reg,op1,op2,imm) do { \ - amd64_codegen_pre(inst); \ - emit_sse_reg_reg_op2 ((inst), (dreg), (reg), (op1), (op2)); \ - x86_imm_emit8 ((inst), (imm)); \ - amd64_codegen_post(inst); \ -} while (0) - -#define emit_sse_membase_reg_op2(inst,basereg,disp,reg,op1,op2) do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex ((inst), 0, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)(op1); \ - *(inst)++ = (unsigned char)(op2); \ - amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ - amd64_codegen_post(inst); \ -} while (0) - -#define emit_sse_reg_membase_op2(inst,dreg,basereg,disp,op1,op2) do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex ((inst), 0, (dreg), 0, (basereg) == AMD64_RIP ? 0 : (basereg)); \ - *(inst)++ = (unsigned char)(op1); \ - *(inst)++ = (unsigned char)(op2); \ - amd64_membase_emit ((inst), (dreg), (basereg), (disp)); \ - amd64_codegen_post(inst); \ -} while (0) - -/* Three opcode SSE defines */ - -#define emit_opcode3(inst,op1,op2,op3) do { \ - *(inst)++ = (unsigned char)(op1); \ - *(inst)++ = (unsigned char)(op2); \ - *(inst)++ = (unsigned char)(op3); \ -} while (0) - -#define emit_sse_reg_reg_size(inst,dreg,reg,op1,op2,op3,size) do { \ - amd64_codegen_pre(inst); \ - *(inst)++ = (unsigned char)(op1); \ - amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ - *(inst)++ = (unsigned char)(op2); \ - *(inst)++ = (unsigned char)(op3); \ - x86_reg_emit ((inst), (dreg), (reg)); \ - amd64_codegen_post(inst); \ -} while (0) - -#define emit_sse_reg_reg(inst,dreg,reg,op1,op2,op3) emit_sse_reg_reg_size ((inst), (dreg), (reg), (op1), (op2), (op3), 0) - -#define emit_sse_reg_reg_imm(inst,dreg,reg,op1,op2,op3,imm) do { \ - amd64_codegen_pre(inst); \ - emit_sse_reg_reg ((inst), (dreg), (reg), (op1), (op2), (op3)); \ - x86_imm_emit8 ((inst), (imm)); \ - amd64_codegen_post(inst); \ -} while (0) - -#define emit_sse_membase_reg(inst,basereg,disp,reg,op1,op2,op3) do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), (unsigned char)(op1)); \ - amd64_emit_rex ((inst), 0, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)(op2); \ - *(inst)++ = (unsigned char)(op3); \ - amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ - amd64_codegen_post(inst); \ -} while (0) - -#define emit_sse_reg_membase(inst,dreg,basereg,disp,op1,op2,op3) do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), (unsigned char)(op1)); \ - amd64_emit_rex ((inst), 0, (dreg), 0, (basereg) == AMD64_RIP ? 0 : (basereg)); \ - *(inst)++ = (unsigned char)(op2); \ - *(inst)++ = (unsigned char)(op3); \ - amd64_membase_emit ((inst), (dreg), (basereg), (disp)); \ - amd64_codegen_post(inst); \ -} while (0) - -/* Four opcode SSE defines */ - -#define emit_sse_reg_reg_op4_size(inst,dreg,reg,op1,op2,op3,op4,size) do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), (unsigned char)(op1)); \ - amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ - *(inst)++ = (unsigned char)(op2); \ - *(inst)++ = (unsigned char)(op3); \ - *(inst)++ = (unsigned char)(op4); \ - x86_reg_emit ((inst), (dreg), (reg)); \ - amd64_codegen_post(inst); \ -} while (0) - -#define emit_sse_reg_reg_op4(inst,dreg,reg,op1,op2,op3,op4) emit_sse_reg_reg_op4_size ((inst), (dreg), (reg), (op1), (op2), (op3), (op4), 0) - -/* specific SSE opcode defines */ - -#define amd64_sse_xorpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg), 0x66, 0x0f, 0x57) - -#define amd64_sse_xorpd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst),(dreg),(basereg), (disp), 0x66, 0x0f, 0x57) - -#define amd64_sse_andpd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst),(dreg),(basereg), (disp), 0x66, 0x0f, 0x54) - -#define amd64_sse_movsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x10) - -#define amd64_sse_movsd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0xf2, 0x0f, 0x10) - -#define amd64_sse_movsd_membase_reg(inst,basereg,disp,reg) emit_sse_membase_reg ((inst), (basereg), (disp), (reg), 0xf2, 0x0f, 0x11) - -#define amd64_sse_movss_membase_reg(inst,basereg,disp,reg) emit_sse_membase_reg ((inst), (basereg), (disp), (reg), 0xf3, 0x0f, 0x11) - -#define amd64_sse_movss_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0xf3, 0x0f, 0x10) - -#define amd64_sse_comisd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg),0x66,0x0f,0x2f) - -#define amd64_sse_comisd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0x66, 0x0f, 0x2f) - -#define amd64_sse_ucomisd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg),0x66,0x0f,0x2e) - -#define amd64_sse_cvtsd2si_reg_reg(inst,dreg,reg) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2d, 8) - -#define amd64_sse_cvttsd2si_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2c, (size)) - -#define amd64_sse_cvttsd2si_reg_reg(inst,dreg,reg) amd64_sse_cvttsd2si_reg_reg_size ((inst), (dreg), (reg), 8) - -#define amd64_sse_cvtsi2sd_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2a, (size)) - -#define amd64_sse_cvtsi2sd_reg_reg(inst,dreg,reg) amd64_sse_cvtsi2sd_reg_reg_size ((inst), (dreg), (reg), 8) - -#define amd64_sse_cvtsd2ss_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5a) - -#define amd64_sse_cvtss2sd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf3, 0x0f, 0x5a) - -#define amd64_sse_addsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x58) - -#define amd64_sse_subsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5c) - -#define amd64_sse_mulsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x59) - -#define amd64_sse_divsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5e) - -#define amd64_sse_sqrtsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x51) - - -#define amd64_sse_pinsrw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm ((inst), (dreg), (reg), 0x66, 0x0f, 0xc4, (imm)) - -#define amd64_sse_pextrw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm ((inst), (dreg), (reg), 0x66, 0x0f, 0xc5, (imm)) - - -#define amd64_sse_cvttsd2si_reg_xreg_size(inst,reg,xreg,size) emit_sse_reg_reg_size ((inst), (reg), (xreg), 0xf2, 0x0f, 0x2c, (size)) - - -#define amd64_sse_addps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x58) - -#define amd64_sse_divps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5e) - -#define amd64_sse_mulps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x59) - -#define amd64_sse_subps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5c) - -#define amd64_sse_maxps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5f) - -#define amd64_sse_minps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5d) - -#define amd64_sse_cmpps_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_op2_imm((inst), (dreg), (reg), 0x0f, 0xc2, (imm)) - -#define amd64_sse_andps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x54) - -#define amd64_sse_andnps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x55) - -#define amd64_sse_orps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x56) - -#define amd64_sse_xorps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x57) - -#define amd64_sse_sqrtps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x51) - -#define amd64_sse_rsqrtps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x52) - -#define amd64_sse_rcpps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x53) - -#define amd64_sse_addsubps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0xd0) - -#define amd64_sse_haddps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x7c) - -#define amd64_sse_hsubps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x7d) - -#define amd64_sse_movshdup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf3, 0x0f, 0x16) - -#define amd64_sse_movsldup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf3, 0x0f, 0x12) - - -#define amd64_sse_pshufhw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0xf3, 0x0f, 0x70, (imm)) - -#define amd64_sse_pshuflw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0xf2, 0x0f, 0x70, (imm)) - -#define amd64_sse_pshufd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0x70, (imm)) - -#define amd64_sse_shufps_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_op2_imm((inst), (dreg), (reg), 0x0f, 0xC6, (imm)) - -#define amd64_sse_shufpd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0xC6, (imm)) - - -#define amd64_sse_addpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x58) - -#define amd64_sse_divpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5e) - -#define amd64_sse_mulpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x59) - -#define amd64_sse_subpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5c) - -#define amd64_sse_maxpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5f) - -#define amd64_sse_minpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5d) - -#define amd64_sse_cmppd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0xc2, (imm)) - -#define amd64_sse_andpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x54) - -#define amd64_sse_andnpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x55) - -#define amd64_sse_orpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x56) - -#define amd64_sse_sqrtpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x51) - -#define amd64_sse_rsqrtpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x52) - -#define amd64_sse_rcppd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x53) - -#define amd64_sse_addsubpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd0) - -#define amd64_sse_haddpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x7c) - -#define amd64_sse_hsubpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x7d) - -#define amd64_sse_movddup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x12) - - -#define amd64_sse_pmovmskb_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd7) - - -#define amd64_sse_pand_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdb) - -#define amd64_sse_por_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xeb) - -#define amd64_sse_pxor_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xef) - - -#define amd64_sse_paddb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfc) - -#define amd64_sse_paddw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfd) - -#define amd64_sse_paddd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfe) - -#define amd64_sse_paddq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd4) - - -#define amd64_sse_psubb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf8) - -#define amd64_sse_psubw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf9) - -#define amd64_sse_psubd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfa) - -#define amd64_sse_psubq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfb) - - -#define amd64_sse_pmaxub_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xde) - -#define amd64_sse_pmaxuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3e) - -#define amd64_sse_pmaxud_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3f) - - -#define amd64_sse_pmaxsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3c) - -#define amd64_sse_pmaxsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xee) - -#define amd64_sse_pmaxsd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3d) - - -#define amd64_sse_pavgb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe0) - -#define amd64_sse_pavgw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe3) - - -#define amd64_sse_pminub_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xda) - -#define amd64_sse_pminuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3a) - -#define amd64_sse_pminud_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3b) - - -#define amd64_sse_pminsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x38) - -#define amd64_sse_pminsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xea) - -#define amd64_sse_pminsd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x39) - - -#define amd64_sse_pcmpeqb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x74) - -#define amd64_sse_pcmpeqw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x75) - -#define amd64_sse_pcmpeqd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x76) - -#define amd64_sse_pcmpeqq_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x29) - - -#define amd64_sse_pcmpgtb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x64) - -#define amd64_sse_pcmpgtw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x65) - -#define amd64_sse_pcmpgtd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x66) - -#define amd64_sse_pcmpgtq_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x37) - - -#define amd64_sse_psadbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf6) - - -#define amd64_sse_punpcklbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x60) - -#define amd64_sse_punpcklwd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x61) - -#define amd64_sse_punpckldq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x62) - -#define amd64_sse_punpcklqdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6c) - -#define amd64_sse_unpcklpd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x14) - -#define amd64_sse_unpcklps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x14) - - -#define amd64_sse_punpckhbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x68) - -#define amd64_sse_punpckhwd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x69) - -#define amd64_sse_punpckhdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6a) - -#define amd64_sse_punpckhqdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6d) - -#define amd64_sse_unpckhpd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x15) - -#define amd64_sse_unpckhps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x15) - - -#define amd64_sse_packsswb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x63) - -#define amd64_sse_packssdw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6b) - -#define amd64_sse_packuswb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x67) - -#define amd64_sse_packusdw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x2b) - - -#define amd64_sse_paddusb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdc) - -#define amd64_sse_psubusb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd8) - -#define amd64_sse_paddusw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdd) - -#define amd64_sse_psubusw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd8) - - -#define amd64_sse_paddsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xec) - -#define amd64_sse_psubsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe8) - -#define amd64_sse_paddsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xed) - -#define amd64_sse_psubsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe9) - - -#define amd64_sse_pmullw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd5) - -#define amd64_sse_pmulld_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x40) - -#define amd64_sse_pmuludq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf4) - -#define amd64_sse_pmulhuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe4) - -#define amd64_sse_pmulhw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe5) - - -#define amd64_sse_psrlw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x71, (imm)) - -#define amd64_sse_psrlw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd1) - - -#define amd64_sse_psraw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x71, (imm)) - -#define amd64_sse_psraw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe1) - - -#define amd64_sse_psllw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x71, (imm)) - -#define amd64_sse_psllw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf1) - - -#define amd64_sse_psrld_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x72, (imm)) - -#define amd64_sse_psrld_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd2) - - -#define amd64_sse_psrad_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x72, (imm)) - -#define amd64_sse_psrad_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe2) - - -#define amd64_sse_pslld_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x72, (imm)) - -#define amd64_sse_pslld_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf2) - - -#define amd64_sse_psrlq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x73, (imm)) - -#define amd64_sse_psrlq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd3) - - -#define amd64_sse_psraq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x73, (imm)) - -#define amd64_sse_psraq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe3) - - -#define amd64_sse_psllq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x73, (imm)) - -#define amd64_sse_psllq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf3) - - -#define amd64_sse_cvtdq2pd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF3, 0x0F, 0xE6) - -#define amd64_sse_cvtdq2ps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0F, 0x5B) - -#define amd64_sse_cvtpd2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF2, 0x0F, 0xE6) - -#define amd64_sse_cvtpd2ps_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0x5A) - -#define amd64_sse_cvtps2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0x5B) - -#define amd64_sse_cvtps2pd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0F, 0x5A) - -#define amd64_sse_cvttpd2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0xE6) - -#define amd64_sse_cvttps2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF3, 0x0F, 0x5B) - - -#define amd64_movd_xreg_reg_size(inst,dreg,sreg,size) emit_sse_reg_reg_size((inst), (dreg), (sreg), 0x66, 0x0f, 0x6e, (size)) - -#define amd64_movd_reg_xreg_size(inst,dreg,sreg,size) emit_sse_reg_reg_size((inst), (sreg), (dreg), 0x66, 0x0f, 0x7e, (size)) - -#define amd64_movd_xreg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase((inst), (dreg), (basereg), (disp), 0x66, 0x0f, 0x6e) - - -#define amd64_movlhps_reg_reg(inst,dreg,sreg) emit_sse_reg_reg_op2((inst), (dreg), (sreg), 0x0f, 0x16) - -#define amd64_movhlps_reg_reg(inst,dreg,sreg) emit_sse_reg_reg_op2((inst), (dreg), (sreg), 0x0f, 0x12) - - -#define amd64_sse_movups_membase_reg(inst, basereg, disp, reg) emit_sse_membase_reg_op2((inst), (basereg), (disp), (reg), 0x0f, 0x11) - -#define amd64_sse_movups_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x10) - -#define amd64_sse_movaps_membase_reg(inst, basereg, disp, reg) emit_sse_membase_reg_op2((inst), (basereg), (disp), (reg), 0x0f, 0x29) - -#define amd64_sse_movaps_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x28) - -#define amd64_sse_movaps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x28) - -#define amd64_sse_movntps_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x2b) - -#define amd64_sse_prefetch_reg_membase(inst, arg, basereg, disp) emit_sse_reg_membase_op2((inst), (arg), (basereg), (disp), 0x0f, 0x18) - -/* Generated from x86-codegen.h */ - -#define amd64_breakpoint_size(inst,size) do { x86_breakpoint(inst); } while (0) -#define amd64_cld_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_cld(inst); amd64_codegen_post(inst); } while (0) -#define amd64_stosb_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosb(inst); amd64_codegen_post(inst); } while (0) -#define amd64_stosl_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosl(inst); amd64_codegen_post(inst); } while (0) -#define amd64_stosd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosd(inst); amd64_codegen_post(inst); } while (0) -#define amd64_movsb_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsb(inst); amd64_codegen_post(inst); } while (0) -#define amd64_movsl_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsl(inst); amd64_codegen_post(inst); } while (0) -#define amd64_movsd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsd(inst); amd64_codegen_post(inst); } while (0) -#define amd64_prefix_size(inst,p,size) do { x86_prefix((inst), p); } while (0) -#define amd64_rdtsc_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_rdtsc(inst); amd64_codegen_post(inst); } while (0) -#define amd64_cmpxchg_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_cmpxchg_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_cmpxchg_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_cmpxchg_mem_reg((inst),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_cmpxchg_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_cmpxchg_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_xchg_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_xchg_reg_reg((inst),((dreg)&0x7),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_xchg_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_xchg_mem_reg((inst),(mem),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_xchg_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_xchg_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_inc_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_inc_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_inc_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_inc_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -//#define amd64_inc_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_inc_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_dec_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_dec_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_dec_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_dec_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -//#define amd64_dec_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_dec_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_not_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_not_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_not_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_not_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_not_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_not_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_neg_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_neg_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_neg_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_neg_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_neg_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_neg_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_nop_size(inst,size) do { amd64_codegen_pre(inst); x86_nop(inst); amd64_codegen_post(inst); } while (0) -//#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_reg_imm((inst),(opc),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_alu_mem_imm_size(inst,opc,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_alu_mem_imm((inst),(opc),(mem),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_alu_membase_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_alu_membase_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_alu_membase8_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_alu_membase8_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_alu_mem_reg_size(inst,opc,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_mem_reg((inst),(opc),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_alu_membase_reg_size(inst,opc,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_alu_membase_reg((inst),(opc),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -//#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_alu_reg_reg((inst),(opc),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_alu_reg8_reg8_size(inst,opc,dreg,reg,is_dreg_h,is_reg_h,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_alu_reg8_reg8((inst),(opc),((dreg)&0x7),((reg)&0x7),(is_dreg_h),(is_reg_h)); amd64_codegen_post(inst); } while (0) -#define amd64_alu_reg_mem_size(inst,opc,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_reg_mem((inst),(opc),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) -//#define amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_alu_reg_membase((inst),(opc),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_test_reg_imm_size(inst,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_test_reg_imm((inst),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_test_mem_imm_size(inst,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_test_mem_imm((inst),(mem),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_test_membase_imm_size(inst,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_test_membase_imm((inst),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_test_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_test_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_test_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_test_mem_reg((inst),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_test_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_test_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_shift_reg_imm_size(inst,opc,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_reg_imm((inst),(opc),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_shift_mem_imm_size(inst,opc,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_mem_imm((inst),(opc),(mem),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_shift_membase_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_shift_membase_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_shift_reg_size(inst,opc,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_reg((inst),(opc),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_shift_mem_size(inst,opc,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_mem((inst),(opc),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_shift_membase_size(inst,opc,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_shift_membase((inst),(opc),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_shrd_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shrd_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_shrd_reg_imm_size(inst,dreg,reg,shamt,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shrd_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(shamt)); amd64_codegen_post(inst); } while (0) -#define amd64_shld_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shld_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_shld_reg_imm_size(inst,dreg,reg,shamt,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shld_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(shamt)); amd64_codegen_post(inst); } while (0) -#define amd64_mul_reg_size(inst,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mul_reg((inst),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_mul_mem_size(inst,mem,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_mul_mem((inst),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_mul_membase_size(inst,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_mul_membase((inst),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_imul_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_imul_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_imul_reg_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_imul_reg_mem((inst),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_imul_reg_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_imul_reg_membase((inst),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_imul_reg_reg_imm_size(inst,dreg,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_imul_reg_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_imul_reg_mem_imm_size(inst,reg,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_imul_reg_mem_imm((inst),((reg)&0x7),(mem),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_imul_reg_membase_imm_size(inst,reg,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_imul_reg_membase_imm((inst),((reg)&0x7),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_div_reg_size(inst,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_div_reg((inst),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_div_mem_size(inst,mem,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_div_mem((inst),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_div_membase_size(inst,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_div_membase((inst),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_mov_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_mem_reg((inst),(mem),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -//#define amd64_mov_regp_reg_size(inst,regp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(regp),0,(reg)); x86_mov_regp_reg((inst),(regp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -//#define amd64_mov_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_mov_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_mov_memindex_reg_size(inst,basereg,disp,indexreg,shift,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_mov_memindex_reg((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_mov_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_mov_reg_reg((inst),((dreg)&0x7),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -//#define amd64_mov_reg_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_reg_mem((inst),((reg)&0x7),(mem),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -//#define amd64_mov_reg_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_mov_reg_membase((inst),((reg)&0x7),((basereg)&0x7),(disp),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -//#define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_mov_reg_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_clear_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_clear_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -//#define amd64_mov_reg_imm_size(inst,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_reg_imm((inst),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_mov_mem_imm_size(inst,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_mov_mem_imm((inst),(mem),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -//#define amd64_mov_membase_imm_size(inst,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_mov_membase_imm((inst),((basereg)&0x7),(disp),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_mov_memindex_imm_size(inst,basereg,disp,indexreg,shift,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,(indexreg),(basereg)); x86_mov_memindex_imm((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_lea_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_lea_mem((inst),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) -//#define amd64_lea_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_lea_membase((inst),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_lea_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_lea_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift)); amd64_codegen_post(inst); } while (0) -#define amd64_widen_reg_size(inst,dreg,reg,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_widen_reg((inst),((dreg)&0x7),((reg)&0x7),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) -#define amd64_widen_mem_size(inst,dreg,mem,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,0); x86_widen_mem((inst),((dreg)&0x7),(mem),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) -#define amd64_widen_membase_size(inst,dreg,basereg,disp,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(basereg)); x86_widen_membase((inst),((dreg)&0x7),((basereg)&0x7),(disp),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) -#define amd64_widen_memindex_size(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),(indexreg),(basereg)); x86_widen_memindex((inst),((dreg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) -#define amd64_cdq_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_cdq(inst); amd64_codegen_post(inst); } while (0) -#define amd64_wait_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_wait(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fp_op_mem_size(inst,opc,mem,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op_mem((inst),(opc),(mem),(is_double)); amd64_codegen_post(inst); } while (0) -#define amd64_fp_op_membase_size(inst,opc,basereg,disp,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fp_op_membase((inst),(opc),((basereg)&0x7),(disp),(is_double)); amd64_codegen_post(inst); } while (0) -#define amd64_fp_op_size(inst,opc,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op((inst),(opc),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fp_op_reg_size(inst,opc,index,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op_reg((inst),(opc),(index),(pop_stack)); amd64_codegen_post(inst); } while (0) -#define amd64_fp_int_op_membase_size(inst,opc,basereg,disp,is_int,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fp_int_op_membase((inst),(opc),((basereg)&0x7),(disp),(is_int)); amd64_codegen_post(inst); } while (0) -#define amd64_fstp_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fstp((inst),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fcompp_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcompp(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fucompp_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucompp(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fnstsw_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fnstsw(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fnstcw_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fnstcw((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_fnstcw_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_fnstcw_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_fldcw_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldcw((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_fldcw_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fldcw_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_fchs_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fchs(inst); amd64_codegen_post(inst); } while (0) -#define amd64_frem_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_frem(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fxch_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fxch((inst),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fcomi_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcomi((inst),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fcomip_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcomip((inst),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fucomi_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucomi((inst),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fucomip_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucomip((inst),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fld_size(inst,mem,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld((inst),(mem),(is_double)); amd64_codegen_post(inst); } while (0) -//#define amd64_fld_membase_size(inst,basereg,disp,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fld_membase((inst),((basereg)&0x7),(disp),(is_double)); amd64_codegen_post(inst); } while (0) -#define amd64_fld80_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld80_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_fld80_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_fld80_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_fild_size(inst,mem,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fild((inst),(mem),(is_long)); amd64_codegen_post(inst); } while (0) -#define amd64_fild_membase_size(inst,basereg,disp,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fild_membase((inst),((basereg)&0x7),(disp),(is_long)); amd64_codegen_post(inst); } while (0) -#define amd64_fld_reg_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld_reg((inst),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fldz_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldz(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fld1_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld1(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fldpi_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldpi(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fst_size(inst,mem,is_double,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fst((inst),(mem),(is_double),(pop_stack)); amd64_codegen_post(inst); } while (0) -#define amd64_fst_membase_size(inst,basereg,disp,is_double,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fst_membase((inst),((basereg)&0x7),(disp),(is_double),(pop_stack)); amd64_codegen_post(inst); } while (0) -#define amd64_fst80_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fst80_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_fst80_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fst80_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_fist_pop_size(inst,mem,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fist_pop((inst),(mem),(is_long)); amd64_codegen_post(inst); } while (0) -#define amd64_fist_pop_membase_size(inst,basereg,disp,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fist_pop_membase((inst),((basereg)&0x7),(disp),(is_long)); amd64_codegen_post(inst); } while (0) -#define amd64_fstsw_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_fstsw(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fist_membase_size(inst,basereg,disp,is_int,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fist_membase((inst),((basereg)&0x7),(disp),(is_int)); amd64_codegen_post(inst); } while (0) -//#define amd64_push_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_push_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_push_regp_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_push_regp((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_push_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_push_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -//#define amd64_push_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_push_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_push_memindex_size(inst,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,(indexreg),(basereg)); x86_push_memindex((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift)); amd64_codegen_post(inst); } while (0) -#define amd64_push_imm_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_push_imm((inst),(imm)); amd64_codegen_post(inst); } while (0) -//#define amd64_pop_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_pop_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_pop_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pop_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_pop_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_pop_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_pushad_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pushad(inst); amd64_codegen_post(inst); } while (0) -#define amd64_pushfd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pushfd(inst); amd64_codegen_post(inst); } while (0) -#define amd64_popad_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_popad(inst); amd64_codegen_post(inst); } while (0) -#define amd64_popfd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_popfd(inst); amd64_codegen_post(inst); } while (0) -#define amd64_loop_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loop((inst),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_loope_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loope((inst),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_loopne_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loopne((inst),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_jump32_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_jump32((inst),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_jump8_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_jump8((inst),(imm)); amd64_codegen_post(inst); } while (0) -#if !defined( __native_client_codegen__ ) -/* Defined above for Native Client, so they can be used in other macros */ -#define amd64_jump_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),0,0,0,(reg)); x86_jump_reg((inst),((reg)&0x7)); } while (0) -#define amd64_jump_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_jump_mem((inst),(mem)); } while (0) -#endif -#define amd64_jump_disp_size(inst,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_jump_disp((inst),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_branch8_size(inst,cond,imm,is_signed,size) do { x86_branch8((inst),(cond),(imm),(is_signed)); } while (0) -#define amd64_branch32_size(inst,cond,imm,is_signed,size) do { x86_branch32((inst),(cond),(imm),(is_signed)); } while (0) -#define amd64_branch_size_body(inst,cond,target,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_branch((inst),(cond),(target),(is_signed)); amd64_codegen_post(inst); } while (0) -#if defined(__default_codegen__) -#define amd64_branch_size(inst,cond,target,is_signed,size) do { amd64_branch_size_body((inst),(cond),(target),(is_signed),(size)); } while (0) -#elif defined(__native_client_codegen__) -#define amd64_branch_size(inst,cond,target,is_signed,size) \ - do { \ - /* amd64_branch_size_body used twice in */ \ - /* case of relocation by amd64_codegen_post */ \ - guint8* branch_start; \ - amd64_codegen_pre(inst); \ - amd64_branch_size_body((inst),(cond),(target),(is_signed),(size)); \ - inst = amd64_codegen_post(inst); \ - branch_start = inst; \ - amd64_branch_size_body((inst),(cond),(target),(is_signed),(size)); \ - mono_amd64_patch(branch_start, (target)); \ - } while (0) -#endif - -#define amd64_branch_disp_size(inst,cond,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_branch_disp((inst),(cond),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_set_reg_size(inst,cond,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex((inst),1,0,0,(reg)); x86_set_reg((inst),(cond),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_set_mem_size(inst,cond,mem,is_signed,size) do { amd64_codegen_pre(inst); x86_set_mem((inst),(cond),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_set_membase_size(inst,cond,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_set_membase((inst),(cond),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) -//#define amd64_call_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_call_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_call_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_call_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) - -#if defined(__default_codegen__) - -#define amd64_call_imm_size(inst,disp,size) do { x86_call_imm((inst),(disp)); } while (0) -#define amd64_call_code_size(inst,target,size) do { x86_call_code((inst),(target)); } while (0) - -#elif defined(__native_client_codegen__) -/* Size is ignored for Native Client calls, we restrict jumping to 32-bits */ -#define amd64_call_imm_size(inst,disp,size) \ - do { \ - amd64_codegen_pre((inst)); \ - amd64_call_sequence_pre((inst)); \ - x86_call_imm((inst),(disp)); \ - amd64_call_sequence_post((inst)); \ - amd64_codegen_post((inst)); \ - } while (0) - -/* x86_call_code is called twice below, first so we can get the size of the */ -/* call sequence, and again so the exact offset from "inst" is used, since */ -/* the sequence could have moved from amd64_call_sequence_post. */ -/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ -#define amd64_call_code_size(inst,target,size) \ - do { \ - amd64_codegen_pre((inst)); \ - guint8* adjusted_start; \ - guint8* call_start; \ - amd64_call_sequence_pre((inst)); \ - x86_call_code((inst),(target)); \ - adjusted_start = amd64_call_sequence_post((inst)); \ - call_start = adjusted_start; \ - x86_call_code(adjusted_start, (target)); \ - amd64_codegen_post((inst)); \ - mono_amd64_patch(call_start, (target)); \ - } while (0) - -#endif /*__native_client_codegen__*/ - -//#define amd64_ret_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_ret(inst); amd64_codegen_post(inst); } while (0) -#define amd64_ret_imm_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_ret_imm((inst),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_cmov_reg_size(inst,cond,is_signed,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_cmov_reg((inst),(cond),(is_signed),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_cmov_mem_size(inst,cond,is_signed,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_cmov_mem((inst),(cond),(is_signed),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_cmov_membase_size(inst,cond,is_signed,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_cmov_membase((inst),(cond),(is_signed),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_enter_size(inst,framesize) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_enter((inst),(framesize)); amd64_codegen_post(inst); } while (0) -//#define amd64_leave_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_leave(inst); amd64_codegen_post(inst); } while (0) -#define amd64_sahf_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_sahf(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fsin_size(inst,size) do { amd64_codegen_pre(inst); x86_fsin(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fcos_size(inst,size) do { amd64_codegen_pre(inst); x86_fcos(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fabs_size(inst,size) do { amd64_codegen_pre(inst); x86_fabs(inst); amd64_codegen_post(inst); } while (0) -#define amd64_ftst_size(inst,size) do { amd64_codegen_pre(inst); x86_ftst(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fxam_size(inst,size) do { amd64_codegen_pre(inst); x86_fxam(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fpatan_size(inst,size) do { amd64_codegen_pre(inst); x86_fpatan(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fprem_size(inst,size) do { amd64_codegen_pre(inst); x86_fprem(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fprem1_size(inst,size) do { amd64_codegen_pre(inst); x86_fprem1(inst); amd64_codegen_post(inst); } while (0) -#define amd64_frndint_size(inst,size) do { amd64_codegen_pre(inst); x86_frndint(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fsqrt_size(inst,size) do { amd64_codegen_pre(inst); x86_fsqrt(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fptan_size(inst,size) do { amd64_codegen_pre(inst); x86_fptan(inst); amd64_codegen_post(inst); } while (0) -//#define amd64_padding_size(inst,size) do { amd64_codegen_pre(inst); x86_padding((inst),(size)); amd64_codegen_post(inst); } while (0) -#define amd64_prolog_size(inst,frame_size,reg_mask,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_prolog((inst),(frame_size),(reg_mask)); amd64_codegen_post(inst); } while (0) -#define amd64_epilog_size(inst,reg_mask,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_epilog((inst),(reg_mask)); amd64_codegen_post(inst); } while (0) -#define amd64_xadd_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_xadd_reg_reg ((inst), (dreg), (reg), (size)); amd64_codegen_post(inst); } while (0) -#define amd64_xadd_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_xadd_mem_reg((inst),(mem),((reg)&0x7), (size)); amd64_codegen_post(inst); } while (0) -#define amd64_xadd_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_xadd_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size)); amd64_codegen_post(inst); } while (0) - - - - -#define amd64_breakpoint(inst) amd64_breakpoint_size(inst,8) -#define amd64_cld(inst) amd64_cld_size(inst,8) -#define amd64_stosb(inst) amd64_stosb_size(inst,8) -#define amd64_stosl(inst) amd64_stosl_size(inst,8) -#define amd64_stosd(inst) amd64_stosd_size(inst,8) -#define amd64_movsb(inst) amd64_movsb_size(inst,8) -#define amd64_movsl(inst) amd64_movsl_size(inst,8) -#define amd64_movsd(inst) amd64_movsd_size(inst,8) -#define amd64_prefix(inst,p) amd64_prefix_size(inst,p,8) -#define amd64_rdtsc(inst) amd64_rdtsc_size(inst,8) -#define amd64_cmpxchg_reg_reg(inst,dreg,reg) amd64_cmpxchg_reg_reg_size(inst,dreg,reg,8) -#define amd64_cmpxchg_mem_reg(inst,mem,reg) amd64_cmpxchg_mem_reg_size(inst,mem,reg,8) -#define amd64_cmpxchg_membase_reg(inst,basereg,disp,reg) amd64_cmpxchg_membase_reg_size(inst,basereg,disp,reg,8) -#define amd64_xchg_reg_reg(inst,dreg,reg,size) amd64_xchg_reg_reg_size(inst,dreg,reg,size) -#define amd64_xchg_mem_reg(inst,mem,reg,size) amd64_xchg_mem_reg_size(inst,mem,reg,size) -#define amd64_xchg_membase_reg(inst,basereg,disp,reg,size) amd64_xchg_membase_reg_size(inst,basereg,disp,reg,size) -#define amd64_xadd_reg_reg(inst,dreg,reg,size) amd64_xadd_reg_reg_size(inst,dreg,reg,size) -#define amd64_xadd_mem_reg(inst,mem,reg,size) amd64_xadd_mem_reg_size(inst,mem,reg,size) -#define amd64_xadd_membase_reg(inst,basereg,disp,reg,size) amd64_xadd_membase_reg_size(inst,basereg,disp,reg,size) -#define amd64_inc_mem(inst,mem) amd64_inc_mem_size(inst,mem,8) -#define amd64_inc_membase(inst,basereg,disp) amd64_inc_membase_size(inst,basereg,disp,8) -#define amd64_inc_reg(inst,reg) amd64_inc_reg_size(inst,reg,8) -#define amd64_dec_mem(inst,mem) amd64_dec_mem_size(inst,mem,8) -#define amd64_dec_membase(inst,basereg,disp) amd64_dec_membase_size(inst,basereg,disp,8) -#define amd64_dec_reg(inst,reg) amd64_dec_reg_size(inst,reg,8) -#define amd64_not_mem(inst,mem) amd64_not_mem_size(inst,mem,8) -#define amd64_not_membase(inst,basereg,disp) amd64_not_membase_size(inst,basereg,disp,8) -#define amd64_not_reg(inst,reg) amd64_not_reg_size(inst,reg,8) -#define amd64_neg_mem(inst,mem) amd64_neg_mem_size(inst,mem,8) -#define amd64_neg_membase(inst,basereg,disp) amd64_neg_membase_size(inst,basereg,disp,8) -#define amd64_neg_reg(inst,reg) amd64_neg_reg_size(inst,reg,8) -#define amd64_nop(inst) amd64_nop_size(inst,8) -//#define amd64_alu_reg_imm(inst,opc,reg,imm) amd64_alu_reg_imm_size(inst,opc,reg,imm,8) -#define amd64_alu_mem_imm(inst,opc,mem,imm) amd64_alu_mem_imm_size(inst,opc,mem,imm,8) -#define amd64_alu_membase_imm(inst,opc,basereg,disp,imm) amd64_alu_membase_imm_size(inst,opc,basereg,disp,imm,8) -#define amd64_alu_mem_reg(inst,opc,mem,reg) amd64_alu_mem_reg_size(inst,opc,mem,reg,8) -#define amd64_alu_membase_reg(inst,opc,basereg,disp,reg) amd64_alu_membase_reg_size(inst,opc,basereg,disp,reg,8) -//#define amd64_alu_reg_reg(inst,opc,dreg,reg) amd64_alu_reg_reg_size(inst,opc,dreg,reg,8) -#define amd64_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) amd64_alu_reg8_reg8_size(inst,opc,dreg,reg,is_dreg_h,is_reg_h,8) -#define amd64_alu_reg_mem(inst,opc,reg,mem) amd64_alu_reg_mem_size(inst,opc,reg,mem,8) -#define amd64_alu_reg_membase(inst,opc,reg,basereg,disp) amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,8) -#define amd64_test_reg_imm(inst,reg,imm) amd64_test_reg_imm_size(inst,reg,imm,8) -#define amd64_test_mem_imm(inst,mem,imm) amd64_test_mem_imm_size(inst,mem,imm,8) -#define amd64_test_membase_imm(inst,basereg,disp,imm) amd64_test_membase_imm_size(inst,basereg,disp,imm,8) -#define amd64_test_reg_reg(inst,dreg,reg) amd64_test_reg_reg_size(inst,dreg,reg,8) -#define amd64_test_mem_reg(inst,mem,reg) amd64_test_mem_reg_size(inst,mem,reg,8) -#define amd64_test_membase_reg(inst,basereg,disp,reg) amd64_test_membase_reg_size(inst,basereg,disp,reg,8) -#define amd64_shift_reg_imm(inst,opc,reg,imm) amd64_shift_reg_imm_size(inst,opc,reg,imm,8) -#define amd64_shift_mem_imm(inst,opc,mem,imm) amd64_shift_mem_imm_size(inst,opc,mem,imm,8) -#define amd64_shift_membase_imm(inst,opc,basereg,disp,imm) amd64_shift_membase_imm_size(inst,opc,basereg,disp,imm,8) -#define amd64_shift_reg(inst,opc,reg) amd64_shift_reg_size(inst,opc,reg,8) -#define amd64_shift_mem(inst,opc,mem) amd64_shift_mem_size(inst,opc,mem,8) -#define amd64_shift_membase(inst,opc,basereg,disp) amd64_shift_membase_size(inst,opc,basereg,disp,8) -#define amd64_shrd_reg(inst,dreg,reg) amd64_shrd_reg_size(inst,dreg,reg,8) -#define amd64_shrd_reg_imm(inst,dreg,reg,shamt) amd64_shrd_reg_imm_size(inst,dreg,reg,shamt,8) -#define amd64_shld_reg(inst,dreg,reg) amd64_shld_reg_size(inst,dreg,reg,8) -#define amd64_shld_reg_imm(inst,dreg,reg,shamt) amd64_shld_reg_imm_size(inst,dreg,reg,shamt,8) -#define amd64_mul_reg(inst,reg,is_signed) amd64_mul_reg_size(inst,reg,is_signed,8) -#define amd64_mul_mem(inst,mem,is_signed) amd64_mul_mem_size(inst,mem,is_signed,8) -#define amd64_mul_membase(inst,basereg,disp,is_signed) amd64_mul_membase_size(inst,basereg,disp,is_signed,8) -#define amd64_imul_reg_reg(inst,dreg,reg) amd64_imul_reg_reg_size(inst,dreg,reg,8) -#define amd64_imul_reg_mem(inst,reg,mem) amd64_imul_reg_mem_size(inst,reg,mem,8) -#define amd64_imul_reg_membase(inst,reg,basereg,disp) amd64_imul_reg_membase_size(inst,reg,basereg,disp,8) -#define amd64_imul_reg_reg_imm(inst,dreg,reg,imm) amd64_imul_reg_reg_imm_size(inst,dreg,reg,imm,8) -#define amd64_imul_reg_mem_imm(inst,reg,mem,imm) amd64_imul_reg_mem_imm_size(inst,reg,mem,imm,8) -#define amd64_imul_reg_membase_imm(inst,reg,basereg,disp,imm) amd64_imul_reg_membase_imm_size(inst,reg,basereg,disp,imm,8) -#define amd64_div_reg(inst,reg,is_signed) amd64_div_reg_size(inst,reg,is_signed,8) -#define amd64_div_mem(inst,mem,is_signed) amd64_div_mem_size(inst,mem,is_signed,8) -#define amd64_div_membase(inst,basereg,disp,is_signed) amd64_div_membase_size(inst,basereg,disp,is_signed,8) -//#define amd64_mov_mem_reg(inst,mem,reg,size) amd64_mov_mem_reg_size(inst,mem,reg,size) -//#define amd64_mov_regp_reg(inst,regp,reg,size) amd64_mov_regp_reg_size(inst,regp,reg,size) -//#define amd64_mov_membase_reg(inst,basereg,disp,reg,size) amd64_mov_membase_reg_size(inst,basereg,disp,reg,size) -#define amd64_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) amd64_mov_memindex_reg_size(inst,basereg,disp,indexreg,shift,reg,size) -//#define amd64_mov_reg_reg(inst,dreg,reg,size) amd64_mov_reg_reg_size(inst,dreg,reg,size) -//#define amd64_mov_reg_mem(inst,reg,mem,size) amd64_mov_reg_mem_size(inst,reg,mem,size) -//#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) amd64_mov_reg_membase_size(inst,reg,basereg,disp,size) -#define amd64_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) -#define amd64_clear_reg(inst,reg) amd64_clear_reg_size(inst,reg,8) -//#define amd64_mov_reg_imm(inst,reg,imm) amd64_mov_reg_imm_size(inst,reg,imm,8) -#define amd64_mov_mem_imm(inst,mem,imm,size) amd64_mov_mem_imm_size(inst,mem,imm,size) -//#define amd64_mov_membase_imm(inst,basereg,disp,imm,size) amd64_mov_membase_imm_size(inst,basereg,disp,imm,size) -#define amd64_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) amd64_mov_memindex_imm_size(inst,basereg,disp,indexreg,shift,imm,size) -#define amd64_lea_mem(inst,reg,mem) amd64_lea_mem_size(inst,reg,mem,8) -//#define amd64_lea_membase(inst,reg,basereg,disp) amd64_lea_membase_size(inst,reg,basereg,disp,8) -#define amd64_lea_memindex(inst,reg,basereg,disp,indexreg,shift) amd64_lea_memindex_size(inst,reg,basereg,disp,indexreg,shift,8) -#define amd64_widen_reg(inst,dreg,reg,is_signed,is_half) amd64_widen_reg_size(inst,dreg,reg,is_signed,is_half,8) -#define amd64_widen_mem(inst,dreg,mem,is_signed,is_half) amd64_widen_mem_size(inst,dreg,mem,is_signed,is_half,8) -#define amd64_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) amd64_widen_membase_size(inst,dreg,basereg,disp,is_signed,is_half,8) -#define amd64_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) amd64_widen_memindex_size(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half,8) -#define amd64_cdq(inst) amd64_cdq_size(inst,8) -#define amd64_wait(inst) amd64_wait_size(inst,8) -#define amd64_fp_op_mem(inst,opc,mem,is_double) amd64_fp_op_mem_size(inst,opc,mem,is_double,8) -#define amd64_fp_op_membase(inst,opc,basereg,disp,is_double) amd64_fp_op_membase_size(inst,opc,basereg,disp,is_double,8) -#define amd64_fp_op(inst,opc,index) amd64_fp_op_size(inst,opc,index,8) -#define amd64_fp_op_reg(inst,opc,index,pop_stack) amd64_fp_op_reg_size(inst,opc,index,pop_stack,8) -#define amd64_fp_int_op_membase(inst,opc,basereg,disp,is_int) amd64_fp_int_op_membase_size(inst,opc,basereg,disp,is_int,8) -#define amd64_fstp(inst,index) amd64_fstp_size(inst,index,8) -#define amd64_fcompp(inst) amd64_fcompp_size(inst,8) -#define amd64_fucompp(inst) amd64_fucompp_size(inst,8) -#define amd64_fnstsw(inst) amd64_fnstsw_size(inst,8) -#define amd64_fnstcw(inst,mem) amd64_fnstcw_size(inst,mem,8) -#define amd64_fnstcw_membase(inst,basereg,disp) amd64_fnstcw_membase_size(inst,basereg,disp,8) -#define amd64_fldcw(inst,mem) amd64_fldcw_size(inst,mem,8) -#define amd64_fldcw_membase(inst,basereg,disp) amd64_fldcw_membase_size(inst,basereg,disp,8) -#define amd64_fchs(inst) amd64_fchs_size(inst,8) -#define amd64_frem(inst) amd64_frem_size(inst,8) -#define amd64_fxch(inst,index) amd64_fxch_size(inst,index,8) -#define amd64_fcomi(inst,index) amd64_fcomi_size(inst,index,8) -#define amd64_fcomip(inst,index) amd64_fcomip_size(inst,index,8) -#define amd64_fucomi(inst,index) amd64_fucomi_size(inst,index,8) -#define amd64_fucomip(inst,index) amd64_fucomip_size(inst,index,8) -#define amd64_fld(inst,mem,is_double) amd64_fld_size(inst,mem,is_double,8) -#define amd64_fld_membase(inst,basereg,disp,is_double) amd64_fld_membase_size(inst,basereg,disp,is_double,8) -#define amd64_fld80_mem(inst,mem) amd64_fld80_mem_size(inst,mem,8) -#define amd64_fld80_membase(inst,basereg,disp) amd64_fld80_membase_size(inst,basereg,disp,8) -#define amd64_fild(inst,mem,is_long) amd64_fild_size(inst,mem,is_long,8) -#define amd64_fild_membase(inst,basereg,disp,is_long) amd64_fild_membase_size(inst,basereg,disp,is_long,8) -#define amd64_fld_reg(inst,index) amd64_fld_reg_size(inst,index,8) -#define amd64_fldz(inst) amd64_fldz_size(inst,8) -#define amd64_fld1(inst) amd64_fld1_size(inst,8) -#define amd64_fldpi(inst) amd64_fldpi_size(inst,8) -#define amd64_fst(inst,mem,is_double,pop_stack) amd64_fst_size(inst,mem,is_double,pop_stack,8) -#define amd64_fst_membase(inst,basereg,disp,is_double,pop_stack) amd64_fst_membase_size(inst,basereg,disp,is_double,pop_stack,8) -#define amd64_fst80_mem(inst,mem) amd64_fst80_mem_size(inst,mem,8) -#define amd64_fst80_membase(inst,basereg,disp) amd64_fst80_membase_size(inst,basereg,disp,8) -#define amd64_fist_pop(inst,mem,is_long) amd64_fist_pop_size(inst,mem,is_long,8) -#define amd64_fist_pop_membase(inst,basereg,disp,is_long) amd64_fist_pop_membase_size(inst,basereg,disp,is_long,8) -#define amd64_fstsw(inst) amd64_fstsw_size(inst,8) -#define amd64_fist_membase(inst,basereg,disp,is_int) amd64_fist_membase_size(inst,basereg,disp,is_int,8) -//#define amd64_push_reg(inst,reg) amd64_push_reg_size(inst,reg,8) -#define amd64_push_regp(inst,reg) amd64_push_regp_size(inst,reg,8) -#define amd64_push_mem(inst,mem) amd64_push_mem_size(inst,mem,8) -//#define amd64_push_membase(inst,basereg,disp) amd64_push_membase_size(inst,basereg,disp,8) -#define amd64_push_memindex(inst,basereg,disp,indexreg,shift) amd64_push_memindex_size(inst,basereg,disp,indexreg,shift,8) -#define amd64_push_imm(inst,imm) amd64_push_imm_size(inst,imm,8) -//#define amd64_pop_reg(inst,reg) amd64_pop_reg_size(inst,reg,8) -#define amd64_pop_mem(inst,mem) amd64_pop_mem_size(inst,mem,8) -#define amd64_pop_membase(inst,basereg,disp) amd64_pop_membase_size(inst,basereg,disp,8) -#define amd64_pushad(inst) amd64_pushad_size(inst,8) -#define amd64_pushfd(inst) amd64_pushfd_size(inst,8) -#define amd64_popad(inst) amd64_popad_size(inst,8) -#define amd64_popfd(inst) amd64_popfd_size(inst,8) -#define amd64_loop(inst,imm) amd64_loop_size(inst,imm,8) -#define amd64_loope(inst,imm) amd64_loope_size(inst,imm,8) -#define amd64_loopne(inst,imm) amd64_loopne_size(inst,imm,8) -#define amd64_jump32(inst,imm) amd64_jump32_size(inst,imm,8) -#define amd64_jump8(inst,imm) amd64_jump8_size(inst,imm,8) -#define amd64_jump_reg(inst,reg) amd64_jump_reg_size(inst,reg,8) -#define amd64_jump_mem(inst,mem) amd64_jump_mem_size(inst,mem,8) -#define amd64_jump_membase(inst,basereg,disp) amd64_jump_membase_size(inst,basereg,disp,8) -#define amd64_jump_code(inst,target) amd64_jump_code_size(inst,target,8) -#define amd64_jump_disp(inst,disp) amd64_jump_disp_size(inst,disp,8) -#define amd64_branch8(inst,cond,imm,is_signed) amd64_branch8_size(inst,cond,imm,is_signed,8) -#define amd64_branch32(inst,cond,imm,is_signed) amd64_branch32_size(inst,cond,imm,is_signed,8) -#define amd64_branch(inst,cond,target,is_signed) amd64_branch_size(inst,cond,target,is_signed,8) -#define amd64_branch_disp(inst,cond,disp,is_signed) amd64_branch_disp_size(inst,cond,disp,is_signed,8) -#define amd64_set_reg(inst,cond,reg,is_signed) amd64_set_reg_size(inst,cond,reg,is_signed,8) -#define amd64_set_mem(inst,cond,mem,is_signed) amd64_set_mem_size(inst,cond,mem,is_signed,8) -#define amd64_set_membase(inst,cond,basereg,disp,is_signed) amd64_set_membase_size(inst,cond,basereg,disp,is_signed,8) -#define amd64_call_imm(inst,disp) amd64_call_imm_size(inst,disp,8) -//#define amd64_call_reg(inst,reg) amd64_call_reg_size(inst,reg,8) -#define amd64_call_mem(inst,mem) amd64_call_mem_size(inst,mem,8) -#define amd64_call_membase(inst,basereg,disp) amd64_call_membase_size(inst,basereg,disp,8) -#define amd64_call_code(inst,target) amd64_call_code_size(inst,target,8) -//#define amd64_ret(inst) amd64_ret_size(inst,8) -#define amd64_ret_imm(inst,imm) amd64_ret_imm_size(inst,imm,8) -#define amd64_cmov_reg(inst,cond,is_signed,dreg,reg) amd64_cmov_reg_size(inst,cond,is_signed,dreg,reg,8) -#define amd64_cmov_mem(inst,cond,is_signed,reg,mem) amd64_cmov_mem_size(inst,cond,is_signed,reg,mem,8) -#define amd64_cmov_membase(inst,cond,is_signed,reg,basereg,disp) amd64_cmov_membase_size(inst,cond,is_signed,reg,basereg,disp,8) -#define amd64_enter(inst,framesize) amd64_enter_size(inst,framesize) -//#define amd64_leave(inst) amd64_leave_size(inst,8) -#define amd64_sahf(inst) amd64_sahf_size(inst,8) -#define amd64_fsin(inst) amd64_fsin_size(inst,8) -#define amd64_fcos(inst) amd64_fcos_size(inst,8) -#define amd64_fabs(inst) amd64_fabs_size(inst,8) -#define amd64_ftst(inst) amd64_ftst_size(inst,8) -#define amd64_fxam(inst) amd64_fxam_size(inst,8) -#define amd64_fpatan(inst) amd64_fpatan_size(inst,8) -#define amd64_fprem(inst) amd64_fprem_size(inst,8) -#define amd64_fprem1(inst) amd64_fprem1_size(inst,8) -#define amd64_frndint(inst) amd64_frndint_size(inst,8) -#define amd64_fsqrt(inst) amd64_fsqrt_size(inst,8) -#define amd64_fptan(inst) amd64_fptan_size(inst,8) -#define amd64_padding(inst,size) amd64_padding_size(inst,size) -#define amd64_prolog(inst,frame,reg_mask) amd64_prolog_size(inst,frame,reg_mask,8) -#define amd64_epilog(inst,reg_mask) amd64_epilog_size(inst,reg_mask,8) - -#endif // AMD64_H diff --git a/asm/Makefile.am b/asm/Makefile.am new file mode 100644 index 0000000000..180be53d3d --- /dev/null +++ b/asm/Makefile.am @@ -0,0 +1,32 @@ + +INCLUDES = $(GLIB_CFLAGS) -I$(top_srcdir) + +noinst_LTLIBRARIES = libmonoarch-arm.la + +BUILT_SOURCES = arm_dpimacros.h arm_fpamacros.h arm_vfpmacros.h + + +libmonoarch_arm_la_SOURCES = $(BUILT_SOURCES) \ + arm-codegen.c \ + arm-codegen.h \ + arm-dis.c \ + arm-dis.h + +arm_dpimacros.h: dpiops.sh mov_macros.th dpi_macros.th cmp_macros.th + (cd $(srcdir); bash ./dpiops.sh) > $@t + mv $@t $@ + +arm_fpamacros.h: fpaops.sh fpam_macros.th fpa_macros.th + (cd $(srcdir); bash ./fpaops.sh) > $@t + mv $@t $@ + +arm_vfpmacros.h: vfpops.sh vfpm_macros.th vfp_macros.th + (cd $(srcdir); bash ./vfpops.sh) > $@t + mv $@t $@ + +CLEANFILES = $(BUILT_SOURCES) + +EXTRA_DIST = dpiops.sh mov_macros.th dpi_macros.th cmp_macros.th \ + fpam_macros.th fpa_macros.th arm-fpa-codegen.h fpaops.sh \ + vfpm_macros.th vfp_macros.th arm-vfp-codegen.h vfpops.sh + diff --git a/asm/amd64-codegen.h b/asm/amd64-codegen.h new file mode 100644 index 0000000000..3dea9cd306 --- /dev/null +++ b/asm/amd64-codegen.h @@ -0,0 +1,1831 @@ +/* + * amd64-codegen.h: Macros for generating amd64 code + * + * Authors: + * Paolo Molaro (lupus@ximian.com) + * Intel Corporation (ORP Project) + * Sergey Chaban (serge@wildwestsoftware.com) + * Dietmar Maurer (dietmar@ximian.com) + * Patrik Torstensson + * Zalman Stern + * + * Copyright (C) 2000 Intel Corporation. All rights reserved. + * Copyright (C) 2001, 2002 Ximian, Inc. + */ + +#ifndef AMD64_H +#define AMD64_H + +//#include + +typedef enum { + AMD64_RAX = 0, + AMD64_RCX = 1, + AMD64_RDX = 2, + AMD64_RBX = 3, + AMD64_RSP = 4, + AMD64_RBP = 5, + AMD64_RSI = 6, + AMD64_RDI = 7, + AMD64_R8 = 8, + AMD64_R9 = 9, + AMD64_R10 = 10, + AMD64_R11 = 11, + AMD64_R12 = 12, + AMD64_R13 = 13, + AMD64_R14 = 14, + AMD64_R15 = 15, + AMD64_RIP = 16, + AMD64_NREG +} AMD64_Reg_No; + +typedef enum { + AMD64_XMM0 = 0, + AMD64_XMM1 = 1, + AMD64_XMM2 = 2, + AMD64_XMM3 = 3, + AMD64_XMM4 = 4, + AMD64_XMM5 = 5, + AMD64_XMM6 = 6, + AMD64_XMM7 = 7, + AMD64_XMM8 = 8, + AMD64_XMM9 = 9, + AMD64_XMM10 = 10, + AMD64_XMM11 = 11, + AMD64_XMM12 = 12, + AMD64_XMM13 = 13, + AMD64_XMM14 = 14, + AMD64_XMM15 = 15, + AMD64_XMM_NREG = 16, +} AMD64_XMM_Reg_No; + +typedef enum +{ + AMD64_REX_B = 1, /* The register in r/m field, base register in SIB byte, or reg in opcode is 8-15 rather than 0-7 */ + AMD64_REX_X = 2, /* The index register in SIB byte is 8-15 rather than 0-7 */ + AMD64_REX_R = 4, /* The reg field of ModRM byte is 8-15 rather than 0-7 */ + AMD64_REX_W = 8 /* Opeartion is 64-bits instead of 32 (default) or 16 (with 0x66 prefix) */ +} AMD64_REX_Bits; + +#if defined(__default_codegen__) + +#define amd64_codegen_pre(inst) +#define amd64_codegen_post(inst) + +#elif defined(__native_client_codegen__) + +#define amd64_codegen_pre(inst) guint8* _codegen_start = (inst); amd64_nacl_instruction_pre(); +#define amd64_codegen_post(inst) (amd64_nacl_instruction_post(&_codegen_start, &(inst)), _codegen_start); + +/* Because of rex prefixes, etc, call sequences are not constant size. */ +/* These pre- and post-sequence hooks remedy this by aligning the call */ +/* sequence after we emit it, since we will know the exact size then. */ +#define amd64_call_sequence_pre(inst) guint8* _code_start = (inst); +#define amd64_call_sequence_post(inst) \ + (mono_nacl_align_call(&_code_start, &(inst)), _code_start); + +/* Native client can load/store using one of the following registers */ +/* as a base: rip, r15, rbp, rsp. Any other base register needs to have */ +/* its upper 32 bits cleared and reference memory using r15 as the base. */ +#define amd64_is_valid_nacl_base(reg) \ + ((reg) == AMD64_RIP || (reg) == AMD64_R15 || \ + (reg) == AMD64_RBP || (reg) == AMD64_RSP) + +#endif /*__native_client_codegen__*/ + +#ifdef TARGET_WIN32 +#define AMD64_ARG_REG1 AMD64_RCX +#define AMD64_ARG_REG2 AMD64_RDX +#define AMD64_ARG_REG3 AMD64_R8 +#define AMD64_ARG_REG4 AMD64_R9 +#else +#define AMD64_ARG_REG1 AMD64_RDI +#define AMD64_ARG_REG2 AMD64_RSI +#define AMD64_ARG_REG3 AMD64_RDX +#define AMD64_ARG_REG4 AMD64_RCX +#endif + +#ifdef TARGET_WIN32 +#define AMD64_CALLEE_REGS ((1< 4) ? AMD64_REX_W : 0) | \ + (((reg_modrm) > 7) ? AMD64_REX_R : 0) | \ + (((reg_index) > 7) ? AMD64_REX_X : 0) | \ + (((reg_rm_base_opcode) > 7) ? AMD64_REX_B : 0); \ + if ((_amd64_rex_bits != 0) || (((width) == 1))) *(inst)++ = AMD64_REX(_amd64_rex_bits); \ + } while (0) +#elif defined(__native_client_codegen__) +#define amd64_emit_rex(inst, width, reg_modrm, reg_index, reg_rm_base_opcode) do \ + { \ + unsigned char _amd64_rex_bits = \ + (((width) > 4) ? AMD64_REX_W : 0) | \ + (((reg_modrm) > 7) ? AMD64_REX_R : 0) | \ + (((reg_index) > 7) ? AMD64_REX_X : 0) | \ + (((reg_rm_base_opcode) > 7) ? AMD64_REX_B : 0); \ + amd64_nacl_tag_rex((inst)); \ + if ((_amd64_rex_bits != 0) || (((width) == 1))) *(inst)++ = AMD64_REX(_amd64_rex_bits); \ + } while (0) +#endif + +typedef union { + guint64 val; + unsigned char b [8]; +} amd64_imm_buf; + +#include "x86-codegen.h" + +/* In 64 bit mode, all registers have a low byte subregister */ +#undef X86_IS_BYTE_REG +#define X86_IS_BYTE_REG(reg) 1 + +#define amd64_modrm_mod(modrm) ((modrm) >> 6) +#define amd64_modrm_reg(modrm) (((modrm) >> 3) & 0x7) +#define amd64_modrm_rm(modrm) ((modrm) & 0x7) + +#define amd64_rex_r(rex) ((((rex) >> 2) & 0x1) << 3) +#define amd64_rex_x(rex) ((((rex) >> 1) & 0x1) << 3) +#define amd64_rex_b(rex) ((((rex) >> 0) & 0x1) << 3) + +#define amd64_sib_scale(sib) ((sib) >> 6) +#define amd64_sib_index(sib) (((sib) >> 3) & 0x7) +#define amd64_sib_base(sib) ((sib) & 0x7) + +#define amd64_is_imm32(val) ((gint64)val >= -((gint64)1<<31) && (gint64)val <= (((gint64)1<<31)-1)) + +#define x86_imm_emit64(inst,imm) \ + do { \ + amd64_imm_buf imb; \ + imb.val = (guint64) (imm); \ + *(inst)++ = imb.b [0]; \ + *(inst)++ = imb.b [1]; \ + *(inst)++ = imb.b [2]; \ + *(inst)++ = imb.b [3]; \ + *(inst)++ = imb.b [4]; \ + *(inst)++ = imb.b [5]; \ + *(inst)++ = imb.b [6]; \ + *(inst)++ = imb.b [7]; \ + } while (0) + +#define amd64_membase_emit(inst,reg,basereg,disp) do { \ + if ((basereg) == AMD64_RIP) { \ + x86_address_byte ((inst), 0, (reg)&0x7, 5); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + else \ + x86_membase_emit ((inst),(reg)&0x7, (basereg)&0x7, (disp)); \ +} while (0) + +#define amd64_alu_reg_imm_size_body(inst,opc,reg,imm,size) \ + do { \ + if (x86_is_imm8((imm))) { \ + amd64_emit_rex(inst, size, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0x83; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((reg) == AMD64_RAX) { \ + amd64_emit_rex(inst, size, 0, 0, 0); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ + x86_imm_emit32 ((inst), (imm)); \ + } else { \ + amd64_emit_rex(inst, size, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0x81; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define amd64_alu_reg_reg_size_body(inst,opc,dreg,reg,size) \ + do { \ + amd64_emit_rex(inst, size, (dreg), 0, (reg)); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#if defined(__default_codegen__) + +#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) \ + amd64_alu_reg_imm_size_body((inst), (opc), (reg), (imm), (size)) + +#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) \ + amd64_alu_reg_reg_size_body((inst), (opc), (dreg), (reg), (size)) + +#elif defined(__native_client_codegen__) +/* NaCl modules may not directly update RSP or RBP other than direct copies */ +/* between them. Instead the lower 4 bytes are updated and then added to R15 */ +#define amd64_is_nacl_stack_reg(reg) (((reg) == AMD64_RSP) || ((reg) == AMD64_RBP)) + +#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) \ + do{ \ + amd64_codegen_pre(inst); \ + if (amd64_is_nacl_stack_reg(reg)) { \ + if (((opc) != X86_ADD) && ((opc) != X86_SUB)) \ + g_assert_not_reached(); \ + amd64_alu_reg_imm_size_body((inst), (opc), (reg), (imm), 4); \ + /* Use LEA instead of ADD to preserve flags */ \ + amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ + } else { \ + amd64_alu_reg_imm_size_body((inst), (opc), (reg), (imm), (size)); \ + } \ + amd64_codegen_post(inst); \ + } while(0) + +#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + if (amd64_is_nacl_stack_reg((dreg)) && ((reg) != AMD64_R15)) { \ + if (((opc) != X86_ADD && (opc) != X86_SUB)) \ + g_assert_not_reached(); \ + amd64_alu_reg_reg_size_body((inst), (opc), (dreg), (reg), 4); \ + /* Use LEA instead of ADD to preserve flags */ \ + amd64_lea_memindex_size((inst), (dreg), (dreg), 0, AMD64_R15, 0, 8); \ + } else { \ + amd64_alu_reg_reg_size_body((inst), (opc), (dreg), (reg), (size)); \ + } \ + amd64_codegen_post(inst); \ + } while (0) + +#endif /*__native_client_codegen__*/ + +#define amd64_alu_reg_imm(inst,opc,reg,imm) amd64_alu_reg_imm_size((inst),(opc),(reg),(imm),8) + +#define amd64_alu_reg_reg(inst,opc,dreg,reg) amd64_alu_reg_reg_size ((inst),(opc),(dreg),(reg),8) + +#define amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst),(size),(reg),0,(basereg)); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + amd64_membase_emit (inst, reg, basereg, disp); \ + amd64_codegen_post(inst); \ +} while (0) + +#define amd64_mov_regp_reg(inst,regp,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, (regp)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_regp_emit ((inst), (reg), (regp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_membase_reg(inst,basereg,disp,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_mem_reg(inst,mem,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, 0); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_address_byte ((inst), 0, (reg), 4); \ + x86_address_byte ((inst), 0, 4, 5); \ + x86_imm_emit32 ((inst), (mem)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_reg_reg(inst,dreg,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (dreg), 0, (reg)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_reg_mem_body(inst,reg,mem,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, 0); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_address_byte ((inst), 0, (reg), 4); \ + x86_address_byte ((inst), 0, 4, 5); \ + x86_imm_emit32 ((inst), (mem)); \ + amd64_codegen_post(inst); \ + } while (0) + +#if defined(__default_codegen__) +#define amd64_mov_reg_mem(inst,reg,mem,size) \ + do { \ + amd64_mov_reg_mem_body((inst),(reg),(mem),(size)); \ + } while (0) +#elif defined(__native_client_codegen__) +/* We have to re-base memory reads because memory isn't zero based. */ +#define amd64_mov_reg_mem(inst,reg,mem,size) \ + do { \ + amd64_mov_reg_membase((inst),(reg),AMD64_R15,(mem),(size)); \ + } while (0) +#endif /* __native_client_codegen__ */ + +#define amd64_mov_reg_membase_body(inst,reg,basereg,disp,size) \ + do { \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define amd64_mov_reg_memindex_size_body(inst,reg,basereg,disp,indexreg,shift,size) \ + do { \ + amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); \ + x86_mov_reg_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(size) == 8 ? 4 : (size)); \ + } while (0) + +#if defined(__default_codegen__) + +#define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) \ + amd64_mov_reg_memindex_size_body((inst),(reg),(basereg),(disp),(indexreg),(shift),(size)) +#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) \ + do { \ + amd64_mov_reg_membase_body((inst), (reg), (basereg), (disp), (size)); \ + } while (0) + +#elif defined(__native_client_codegen__) + +#define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) \ + do { \ + amd64_codegen_pre(inst); \ + if (amd64_is_nacl_stack_reg((reg))) { \ + /* Clear upper 32 bits with mov of size 4 */ \ + amd64_mov_reg_memindex_size_body((inst), (reg), (basereg), (disp), (indexreg), (shift), 4); \ + /* Add %r15 using LEA to preserve flags */ \ + amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ + } else { \ + amd64_mov_reg_memindex_size_body((inst), (reg), (basereg), (disp), (indexreg), (shift), (size)); \ + } \ + amd64_codegen_post(inst); \ + } while(0) + +#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) \ + do { \ + amd64_codegen_pre(inst); \ + if (amd64_is_nacl_stack_reg((reg))) { \ + /* Clear upper 32 bits with mov of size 4 */ \ + amd64_mov_reg_membase_body((inst), (reg), (basereg), (disp), 4); \ + /* Add %r15 */ \ + amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ + } else { \ + amd64_mov_reg_membase_body((inst), (reg), (basereg), (disp), (size)); \ + } \ + amd64_codegen_post(inst); \ + } while (0) + +#endif /*__native_client_codegen__*/ + +#define amd64_movzx_reg_membase(inst,reg,basereg,disp,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb6; break; \ + case 2: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb7; break; \ + case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsxd_reg_mem(inst,reg,mem) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst,8,(reg),0,0); \ + *(inst)++ = (unsigned char)0x63; \ + x86_mem_emit ((inst), ((reg)&0x7), (mem)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsxd_reg_membase(inst,reg,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst,8,(reg),0,(basereg)); \ + *(inst)++ = (unsigned char)0x63; \ + x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsxd_reg_reg(inst,dreg,reg) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst,8,(dreg),0,(reg)); \ + *(inst)++ = (unsigned char)0x63; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ + } while (0) + +/* Pretty much the only instruction that supports a 64-bit immediate. Optimize for common case of + * 32-bit immediate. Pepper with casts to avoid warnings. + */ +#define amd64_mov_reg_imm_size(inst,reg,imm,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, (size), 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0xb8 + ((reg) & 0x7); \ + if ((size) == 8) \ + x86_imm_emit64 ((inst), (guint64)(imm)); \ + else \ + x86_imm_emit32 ((inst), (int)(guint64)(imm)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_mov_reg_imm(inst,reg,imm) \ + do { \ + int _amd64_width_temp = ((guint64)(imm) == (guint64)(int)(guint64)(imm)); \ + amd64_codegen_pre(inst); \ + amd64_mov_reg_imm_size ((inst), (reg), (imm), (_amd64_width_temp ? 4 : 8)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_set_reg_template(inst,reg) amd64_mov_reg_imm_size ((inst),(reg), 0, 8) + +#define amd64_set_template(inst,reg) amd64_set_reg_template((inst),(reg)) + +#define amd64_mov_membase_imm(inst,basereg,disp,imm,size) \ + do { \ + amd64_codegen_pre(inst); \ + if ((size) == 2) \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + amd64_emit_rex(inst, (size) == 1 ? 0 : (size), 0, 0, (basereg)); \ + if ((size) == 1) { \ + *(inst)++ = (unsigned char)0xc6; \ + x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + *(inst)++ = (unsigned char)0xc7; \ + x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + *(inst)++ = (unsigned char)0xc7; \ + x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + amd64_codegen_post(inst); \ + } while (0) + + +#define amd64_lea_membase_body(inst,reg,basereg,disp) \ + do { \ + amd64_emit_rex(inst, 8, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x8d; \ + amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#if defined(__default_codegen__) +#define amd64_lea_membase(inst,reg,basereg,disp) \ + amd64_lea_membase_body((inst), (reg), (basereg), (disp)) +#elif defined(__native_client_codegen__) +/* NaCl modules may not write directly into RSP/RBP. Instead, use a */ +/* 32-bit LEA and add R15 to the effective address */ +#define amd64_lea_membase(inst,reg,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + if (amd64_is_nacl_stack_reg(reg)) { \ + /* 32-bit LEA */ \ + amd64_emit_rex((inst), 4, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x8d; \ + amd64_membase_emit((inst), (reg), (basereg), (disp)); \ + /* Use a 64-bit LEA instead of an ADD to preserve flags */ \ + amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ + } else { \ + amd64_lea_membase_body((inst), (reg), (basereg), (disp)); \ + } \ + amd64_codegen_post(inst); \ + } while (0) +#endif /*__native_client_codegen__*/ + +/* Instruction are implicitly 64-bits so don't generate REX for just the size. */ +#define amd64_push_reg(inst,reg) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, 0, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0x50 + ((reg) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +/* Instruction is implicitly 64-bits so don't generate REX for just the size. */ +#define amd64_push_membase(inst,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, 0, 0, 0, (basereg)); \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 6, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_pop_reg_body(inst,reg) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex(inst, 0, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0x58 + ((reg) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#if defined(__default_codegen__) + +#define amd64_call_reg(inst,reg) \ + do { \ + amd64_emit_rex(inst, 0, 0, 0, (reg)); \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst), 2, ((reg) & 0x7)); \ + } while (0) + + +#define amd64_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) +#define amd64_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) + +#define amd64_pop_reg(inst,reg) amd64_pop_reg_body((inst), (reg)) + +#elif defined(__native_client_codegen__) + +/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ +#define amd64_jump_reg_size(inst,reg,size) \ + do { \ + amd64_codegen_pre((inst)); \ + amd64_alu_reg_imm_size((inst), X86_AND, (reg), (nacl_align_byte), 4); \ + amd64_alu_reg_reg_size((inst), X86_ADD, (reg), AMD64_R15, 8); \ + amd64_emit_rex ((inst),0,0,0,(reg)); \ + x86_jump_reg((inst),((reg)&0x7)); \ + amd64_codegen_post((inst)); \ + } while (0) + +/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ +#define amd64_jump_mem_size(inst,mem,size) \ + do { \ + amd64_codegen_pre((inst)); \ + amd64_mov_reg_mem((inst), (mem), AMD64_R11, 4); \ + amd64_jump_reg_size((inst), AMD64_R11, 4); \ + amd64_codegen_post((inst)); \ + } while (0) + +#define amd64_call_reg_internal(inst,reg) \ + do { \ + amd64_codegen_pre((inst)); \ + amd64_alu_reg_imm_size((inst), X86_AND, (reg), (nacl_align_byte), 4); \ + amd64_alu_reg_reg_size((inst), X86_ADD, (reg), AMD64_R15, 8); \ + amd64_emit_rex((inst), 0, 0, 0, (reg)); \ + x86_call_reg((inst), ((reg) & 0x7)); \ + amd64_codegen_post((inst)); \ + } while (0) + +#define amd64_call_reg(inst,reg) \ + do { \ + amd64_codegen_pre((inst)); \ + amd64_call_sequence_pre(inst); \ + amd64_call_reg_internal((inst), (reg)); \ + amd64_call_sequence_post(inst); \ + amd64_codegen_post((inst)); \ + } while (0) + + +#define amd64_ret(inst) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_pop_reg_body((inst), AMD64_R11); \ + amd64_jump_reg_size((inst), AMD64_R11, 8); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_leave(inst) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_mov_reg_reg((inst), AMD64_RSP, AMD64_RBP, 8); \ + amd64_pop_reg_body((inst), AMD64_R11); \ + amd64_mov_reg_reg_size((inst), AMD64_RBP, AMD64_R11, 4); \ + amd64_alu_reg_reg_size((inst), X86_ADD, AMD64_RBP, AMD64_R15, 8); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_pop_reg(inst,reg) \ + do { \ + amd64_codegen_pre(inst); \ + if (amd64_is_nacl_stack_reg((reg))) { \ + amd64_pop_reg_body((inst), AMD64_R11); \ + amd64_mov_reg_reg_size((inst), (reg), AMD64_R11, 4); \ + amd64_alu_reg_reg_size((inst), X86_ADD, (reg), AMD64_R15, 8); \ + } else { \ + amd64_pop_reg_body((inst), (reg)); \ + } \ + amd64_codegen_post(inst); \ + } while (0) + +#endif /*__native_client_codegen__*/ + +#define amd64_movsd_reg_regp(inst,reg,regp) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf2); \ + amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsd_regp_reg(inst,regp,reg) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf2); \ + amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movss_reg_regp(inst,reg,regp) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf3); \ + amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movss_regp_reg(inst,regp,reg) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf3); \ + amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsd_reg_membase(inst,reg,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf2); \ + amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movss_reg_membase(inst,reg,basereg,disp) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf3); \ + amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movsd_membase_reg(inst,basereg,disp,reg) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf2); \ + amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_movss_membase_reg(inst,basereg,disp,reg) \ + do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), 0xf3); \ + amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ + amd64_codegen_post(inst); \ + } while (0) + +/* The original inc_reg opcode is used as the REX prefix */ +#define amd64_inc_reg_size(inst,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst),(size),0,0,(reg)); \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst),0,(reg) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_dec_reg_size(inst,reg,size) \ + do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst),(size),0,0,(reg)); \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst),1,(reg) & 0x7); \ + amd64_codegen_post(inst); \ + } while (0) + +#define amd64_fld_membase_size(inst,basereg,disp,is_double,size) do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst),0,0,0,(basereg)); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ + amd64_membase_emit ((inst), 0, (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +#if defined (__default_codegen__) + +/* From the AMD64 Software Optimization Manual */ +#define amd64_padding_size(inst,size) \ + do { \ + switch ((size)) { \ + case 1: *(inst)++ = 0x90; break; \ + case 2: *(inst)++ = 0x66; *(inst)++ = 0x90; break; \ + case 3: *(inst)++ = 0x66; *(inst)++ = 0x66; *(inst)++ = 0x90; break; \ + default: amd64_emit_rex ((inst),8,0,0,0); x86_padding ((inst), (size) - 1); \ + }; \ + } while (0) + +#define amd64_call_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); *(inst)++ = (unsigned char)0xff; amd64_membase_emit ((inst),2, (basereg),(disp)); } while (0) +#define amd64_jump_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); *(inst)++ = (unsigned char)0xff; amd64_membase_emit ((inst), 4, (basereg), (disp)); } while (0) + +#define amd64_jump_code_size(inst,target,size) do { \ + if (amd64_is_imm32 ((gint64)(target) - (gint64)(inst))) { \ + x86_jump_code((inst),(target)); \ + } else { \ + amd64_jump_membase ((inst), AMD64_RIP, 0); \ + *(guint64*)(inst) = (guint64)(target); \ + (inst) += 8; \ + } \ +} while (0) + +#elif defined(__native_client_codegen__) + +/* The 3-7 byte NOP sequences in amd64_padding_size below are all illegal in */ +/* 64-bit Native Client because they load into rSP/rBP or use duplicate */ +/* prefixes. Instead we use the NOPs recommended in Section 3.5.1.8 of the */ +/* Intel64 and IA-32 Architectures Optimization Reference Manual and */ +/* Section 4.13 of AMD Software Optimization Guide for Family 10h Processors. */ + +#define amd64_padding_size(inst,size) \ + do { \ + unsigned char *code_start = (inst); \ + switch ((size)) { \ + /* xchg %eax,%eax, recognized by hardware as a NOP */ \ + case 1: *(inst)++ = 0x90; break; \ + /* xchg %ax,%ax */ \ + case 2: *(inst)++ = 0x66; *(inst)++ = 0x90; \ + break; \ + /* nop (%rax) */ \ + case 3: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ + *(inst)++ = 0x00; \ + break; \ + /* nop 0x0(%rax) */ \ + case 4: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ + x86_address_byte ((inst), 1, 0, AMD64_RAX); \ + x86_imm_emit8 ((inst), 0); \ + break; \ + /* nop 0x0(%rax,%rax) */ \ + case 5: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ + x86_address_byte ((inst), 1, 0, 4); \ + x86_address_byte ((inst), 0, AMD64_RAX, AMD64_RAX); \ + x86_imm_emit8 ((inst), 0); \ + break; \ + /* nopw 0x0(%rax,%rax) */ \ + case 6: *(inst)++ = 0x66; *(inst)++ = 0x0f; \ + *(inst)++ = 0x1f; \ + x86_address_byte ((inst), 1, 0, 4); \ + x86_address_byte ((inst), 0, AMD64_RAX, AMD64_RAX); \ + x86_imm_emit8 ((inst), 0); \ + break; \ + /* nop 0x0(%rax) (32-bit displacement) */ \ + case 7: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ + x86_address_byte ((inst), 2, 0, AMD64_RAX); \ + x86_imm_emit32((inst), 0); \ + break; \ + /* nop 0x0(%rax,%rax) (32-bit displacement) */ \ + case 8: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ + x86_address_byte ((inst), 2, 0, 4); \ + x86_address_byte ((inst), 0, AMD64_RAX, AMD64_RAX); \ + x86_imm_emit32 ((inst), 0); \ + break; \ + default: \ + g_assert_not_reached(); \ + } \ + g_assert(code_start + (size) == (unsigned char *)(inst)); \ + } while (0) + + +/* Size is ignored for Native Client calls, we restrict jumping to 32-bits */ +#define amd64_call_membase_size(inst,basereg,disp,size) \ + do { \ + amd64_codegen_pre((inst)); \ + amd64_call_sequence_pre(inst); \ + amd64_mov_reg_membase((inst), AMD64_R11, (basereg), (disp), 4); \ + amd64_call_reg_internal((inst), AMD64_R11); \ + amd64_call_sequence_post(inst); \ + amd64_codegen_post((inst)); \ + } while (0) + +/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ +#define amd64_jump_membase_size(inst,basereg,disp,size) \ + do { \ + amd64_mov_reg_membase((inst), AMD64_R11, (basereg), (disp), 4); \ + amd64_jump_reg_size((inst), AMD64_R11, 4); \ + } while (0) + +/* On Native Client we can't jump more than INT_MAX in either direction */ +#define amd64_jump_code_size(inst,target,size) \ + do { \ + /* x86_jump_code used twice in case of */ \ + /* relocation by amd64_codegen_post */ \ + guint8* jump_start; \ + amd64_codegen_pre(inst); \ + assert(amd64_is_imm32 ((gint64)(target) - (gint64)(inst))); \ + x86_jump_code((inst),(target)); \ + inst = amd64_codegen_post(inst); \ + jump_start = (inst); \ + x86_jump_code((inst),(target)); \ + mono_amd64_patch(jump_start, (target)); \ +} while (0) + +#endif /*__native_client_codegen__*/ + +/* + * SSE + */ + +//TODO Reorganize SSE opcode defines. + +/* Two opcode SSE defines */ + +#define emit_sse_reg_reg_op2_size(inst,dreg,reg,op1,op2,size) do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ + *(inst)++ = (unsigned char)(op1); \ + *(inst)++ = (unsigned char)(op2); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_reg_op2(inst,dreg,reg,op1,op2) emit_sse_reg_reg_op2_size ((inst), (dreg), (reg), (op1), (op2), 0) + +#define emit_sse_reg_reg_op2_imm(inst,dreg,reg,op1,op2,imm) do { \ + amd64_codegen_pre(inst); \ + emit_sse_reg_reg_op2 ((inst), (dreg), (reg), (op1), (op2)); \ + x86_imm_emit8 ((inst), (imm)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_membase_reg_op2(inst,basereg,disp,reg,op1,op2) do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst), 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)(op1); \ + *(inst)++ = (unsigned char)(op2); \ + amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_membase_op2(inst,dreg,basereg,disp,op1,op2) do { \ + amd64_codegen_pre(inst); \ + amd64_emit_rex ((inst), 0, (dreg), 0, (basereg) == AMD64_RIP ? 0 : (basereg)); \ + *(inst)++ = (unsigned char)(op1); \ + *(inst)++ = (unsigned char)(op2); \ + amd64_membase_emit ((inst), (dreg), (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +/* Three opcode SSE defines */ + +#define emit_opcode3(inst,op1,op2,op3) do { \ + *(inst)++ = (unsigned char)(op1); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ +} while (0) + +#define emit_sse_reg_reg_size(inst,dreg,reg,op1,op2,op3,size) do { \ + amd64_codegen_pre(inst); \ + *(inst)++ = (unsigned char)(op1); \ + amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_reg(inst,dreg,reg,op1,op2,op3) emit_sse_reg_reg_size ((inst), (dreg), (reg), (op1), (op2), (op3), 0) + +#define emit_sse_reg_reg_imm(inst,dreg,reg,op1,op2,op3,imm) do { \ + amd64_codegen_pre(inst); \ + emit_sse_reg_reg ((inst), (dreg), (reg), (op1), (op2), (op3)); \ + x86_imm_emit8 ((inst), (imm)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_membase_reg(inst,basereg,disp,reg,op1,op2,op3) do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), (unsigned char)(op1)); \ + amd64_emit_rex ((inst), 0, (reg), 0, (basereg)); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ + amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_membase(inst,dreg,basereg,disp,op1,op2,op3) do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), (unsigned char)(op1)); \ + amd64_emit_rex ((inst), 0, (dreg), 0, (basereg) == AMD64_RIP ? 0 : (basereg)); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ + amd64_membase_emit ((inst), (dreg), (basereg), (disp)); \ + amd64_codegen_post(inst); \ +} while (0) + +/* Four opcode SSE defines */ + +#define emit_sse_reg_reg_op4_size(inst,dreg,reg,op1,op2,op3,op4,size) do { \ + amd64_codegen_pre(inst); \ + x86_prefix((inst), (unsigned char)(op1)); \ + amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ + *(inst)++ = (unsigned char)(op2); \ + *(inst)++ = (unsigned char)(op3); \ + *(inst)++ = (unsigned char)(op4); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + amd64_codegen_post(inst); \ +} while (0) + +#define emit_sse_reg_reg_op4(inst,dreg,reg,op1,op2,op3,op4) emit_sse_reg_reg_op4_size ((inst), (dreg), (reg), (op1), (op2), (op3), (op4), 0) + +/* specific SSE opcode defines */ + +#define amd64_sse_xorpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg), 0x66, 0x0f, 0x57) + +#define amd64_sse_xorpd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst),(dreg),(basereg), (disp), 0x66, 0x0f, 0x57) + +#define amd64_sse_andpd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst),(dreg),(basereg), (disp), 0x66, 0x0f, 0x54) + +#define amd64_sse_movsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x10) + +#define amd64_sse_movsd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0xf2, 0x0f, 0x10) + +#define amd64_sse_movsd_membase_reg(inst,basereg,disp,reg) emit_sse_membase_reg ((inst), (basereg), (disp), (reg), 0xf2, 0x0f, 0x11) + +#define amd64_sse_movss_membase_reg(inst,basereg,disp,reg) emit_sse_membase_reg ((inst), (basereg), (disp), (reg), 0xf3, 0x0f, 0x11) + +#define amd64_sse_movss_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0xf3, 0x0f, 0x10) + +#define amd64_sse_comisd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg),0x66,0x0f,0x2f) + +#define amd64_sse_comisd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0x66, 0x0f, 0x2f) + +#define amd64_sse_ucomisd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg),0x66,0x0f,0x2e) + +#define amd64_sse_cvtsd2si_reg_reg(inst,dreg,reg) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2d, 8) + +#define amd64_sse_cvttsd2si_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2c, (size)) + +#define amd64_sse_cvttsd2si_reg_reg(inst,dreg,reg) amd64_sse_cvttsd2si_reg_reg_size ((inst), (dreg), (reg), 8) + +#define amd64_sse_cvtsi2sd_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2a, (size)) + +#define amd64_sse_cvtsi2sd_reg_reg(inst,dreg,reg) amd64_sse_cvtsi2sd_reg_reg_size ((inst), (dreg), (reg), 8) + +#define amd64_sse_cvtsd2ss_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5a) + +#define amd64_sse_cvtss2sd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf3, 0x0f, 0x5a) + +#define amd64_sse_addsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x58) + +#define amd64_sse_subsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5c) + +#define amd64_sse_mulsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x59) + +#define amd64_sse_divsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5e) + +#define amd64_sse_sqrtsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x51) + + +#define amd64_sse_pinsrw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm ((inst), (dreg), (reg), 0x66, 0x0f, 0xc4, (imm)) + +#define amd64_sse_pextrw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm ((inst), (dreg), (reg), 0x66, 0x0f, 0xc5, (imm)) + + +#define amd64_sse_cvttsd2si_reg_xreg_size(inst,reg,xreg,size) emit_sse_reg_reg_size ((inst), (reg), (xreg), 0xf2, 0x0f, 0x2c, (size)) + + +#define amd64_sse_addps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x58) + +#define amd64_sse_divps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5e) + +#define amd64_sse_mulps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x59) + +#define amd64_sse_subps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5c) + +#define amd64_sse_maxps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5f) + +#define amd64_sse_minps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5d) + +#define amd64_sse_cmpps_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_op2_imm((inst), (dreg), (reg), 0x0f, 0xc2, (imm)) + +#define amd64_sse_andps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x54) + +#define amd64_sse_andnps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x55) + +#define amd64_sse_orps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x56) + +#define amd64_sse_xorps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x57) + +#define amd64_sse_sqrtps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x51) + +#define amd64_sse_rsqrtps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x52) + +#define amd64_sse_rcpps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x53) + +#define amd64_sse_addsubps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0xd0) + +#define amd64_sse_haddps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x7c) + +#define amd64_sse_hsubps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x7d) + +#define amd64_sse_movshdup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf3, 0x0f, 0x16) + +#define amd64_sse_movsldup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf3, 0x0f, 0x12) + + +#define amd64_sse_pshufhw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0xf3, 0x0f, 0x70, (imm)) + +#define amd64_sse_pshuflw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0xf2, 0x0f, 0x70, (imm)) + +#define amd64_sse_pshufd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0x70, (imm)) + +#define amd64_sse_shufps_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_op2_imm((inst), (dreg), (reg), 0x0f, 0xC6, (imm)) + +#define amd64_sse_shufpd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0xC6, (imm)) + + +#define amd64_sse_addpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x58) + +#define amd64_sse_divpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5e) + +#define amd64_sse_mulpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x59) + +#define amd64_sse_subpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5c) + +#define amd64_sse_maxpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5f) + +#define amd64_sse_minpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5d) + +#define amd64_sse_cmppd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0xc2, (imm)) + +#define amd64_sse_andpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x54) + +#define amd64_sse_andnpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x55) + +#define amd64_sse_orpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x56) + +#define amd64_sse_sqrtpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x51) + +#define amd64_sse_rsqrtpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x52) + +#define amd64_sse_rcppd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x53) + +#define amd64_sse_addsubpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd0) + +#define amd64_sse_haddpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x7c) + +#define amd64_sse_hsubpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x7d) + +#define amd64_sse_movddup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x12) + + +#define amd64_sse_pmovmskb_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd7) + + +#define amd64_sse_pand_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdb) + +#define amd64_sse_por_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xeb) + +#define amd64_sse_pxor_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xef) + + +#define amd64_sse_paddb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfc) + +#define amd64_sse_paddw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfd) + +#define amd64_sse_paddd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfe) + +#define amd64_sse_paddq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd4) + + +#define amd64_sse_psubb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf8) + +#define amd64_sse_psubw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf9) + +#define amd64_sse_psubd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfa) + +#define amd64_sse_psubq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfb) + + +#define amd64_sse_pmaxub_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xde) + +#define amd64_sse_pmaxuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3e) + +#define amd64_sse_pmaxud_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3f) + + +#define amd64_sse_pmaxsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3c) + +#define amd64_sse_pmaxsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xee) + +#define amd64_sse_pmaxsd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3d) + + +#define amd64_sse_pavgb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe0) + +#define amd64_sse_pavgw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe3) + + +#define amd64_sse_pminub_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xda) + +#define amd64_sse_pminuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3a) + +#define amd64_sse_pminud_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3b) + + +#define amd64_sse_pminsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x38) + +#define amd64_sse_pminsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xea) + +#define amd64_sse_pminsd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x39) + + +#define amd64_sse_pcmpeqb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x74) + +#define amd64_sse_pcmpeqw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x75) + +#define amd64_sse_pcmpeqd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x76) + +#define amd64_sse_pcmpeqq_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x29) + + +#define amd64_sse_pcmpgtb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x64) + +#define amd64_sse_pcmpgtw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x65) + +#define amd64_sse_pcmpgtd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x66) + +#define amd64_sse_pcmpgtq_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x37) + + +#define amd64_sse_psadbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf6) + + +#define amd64_sse_punpcklbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x60) + +#define amd64_sse_punpcklwd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x61) + +#define amd64_sse_punpckldq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x62) + +#define amd64_sse_punpcklqdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6c) + +#define amd64_sse_unpcklpd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x14) + +#define amd64_sse_unpcklps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x14) + + +#define amd64_sse_punpckhbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x68) + +#define amd64_sse_punpckhwd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x69) + +#define amd64_sse_punpckhdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6a) + +#define amd64_sse_punpckhqdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6d) + +#define amd64_sse_unpckhpd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x15) + +#define amd64_sse_unpckhps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x15) + + +#define amd64_sse_packsswb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x63) + +#define amd64_sse_packssdw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6b) + +#define amd64_sse_packuswb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x67) + +#define amd64_sse_packusdw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x2b) + + +#define amd64_sse_paddusb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdc) + +#define amd64_sse_psubusb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd8) + +#define amd64_sse_paddusw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdd) + +#define amd64_sse_psubusw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd8) + + +#define amd64_sse_paddsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xec) + +#define amd64_sse_psubsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe8) + +#define amd64_sse_paddsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xed) + +#define amd64_sse_psubsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe9) + + +#define amd64_sse_pmullw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd5) + +#define amd64_sse_pmulld_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x40) + +#define amd64_sse_pmuludq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf4) + +#define amd64_sse_pmulhuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe4) + +#define amd64_sse_pmulhw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe5) + + +#define amd64_sse_psrlw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x71, (imm)) + +#define amd64_sse_psrlw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd1) + + +#define amd64_sse_psraw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x71, (imm)) + +#define amd64_sse_psraw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe1) + + +#define amd64_sse_psllw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x71, (imm)) + +#define amd64_sse_psllw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf1) + + +#define amd64_sse_psrld_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x72, (imm)) + +#define amd64_sse_psrld_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd2) + + +#define amd64_sse_psrad_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x72, (imm)) + +#define amd64_sse_psrad_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe2) + + +#define amd64_sse_pslld_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x72, (imm)) + +#define amd64_sse_pslld_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf2) + + +#define amd64_sse_psrlq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x73, (imm)) + +#define amd64_sse_psrlq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd3) + + +#define amd64_sse_psraq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x73, (imm)) + +#define amd64_sse_psraq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe3) + + +#define amd64_sse_psllq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x73, (imm)) + +#define amd64_sse_psllq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf3) + + +#define amd64_sse_cvtdq2pd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF3, 0x0F, 0xE6) + +#define amd64_sse_cvtdq2ps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0F, 0x5B) + +#define amd64_sse_cvtpd2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF2, 0x0F, 0xE6) + +#define amd64_sse_cvtpd2ps_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0x5A) + +#define amd64_sse_cvtps2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0x5B) + +#define amd64_sse_cvtps2pd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0F, 0x5A) + +#define amd64_sse_cvttpd2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0xE6) + +#define amd64_sse_cvttps2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF3, 0x0F, 0x5B) + + +#define amd64_movd_xreg_reg_size(inst,dreg,sreg,size) emit_sse_reg_reg_size((inst), (dreg), (sreg), 0x66, 0x0f, 0x6e, (size)) + +#define amd64_movd_reg_xreg_size(inst,dreg,sreg,size) emit_sse_reg_reg_size((inst), (sreg), (dreg), 0x66, 0x0f, 0x7e, (size)) + +#define amd64_movd_xreg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase((inst), (dreg), (basereg), (disp), 0x66, 0x0f, 0x6e) + + +#define amd64_movlhps_reg_reg(inst,dreg,sreg) emit_sse_reg_reg_op2((inst), (dreg), (sreg), 0x0f, 0x16) + +#define amd64_movhlps_reg_reg(inst,dreg,sreg) emit_sse_reg_reg_op2((inst), (dreg), (sreg), 0x0f, 0x12) + + +#define amd64_sse_movups_membase_reg(inst, basereg, disp, reg) emit_sse_membase_reg_op2((inst), (basereg), (disp), (reg), 0x0f, 0x11) + +#define amd64_sse_movups_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x10) + +#define amd64_sse_movaps_membase_reg(inst, basereg, disp, reg) emit_sse_membase_reg_op2((inst), (basereg), (disp), (reg), 0x0f, 0x29) + +#define amd64_sse_movaps_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x28) + +#define amd64_sse_movaps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x28) + +#define amd64_sse_movntps_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x2b) + +#define amd64_sse_prefetch_reg_membase(inst, arg, basereg, disp) emit_sse_reg_membase_op2((inst), (arg), (basereg), (disp), 0x0f, 0x18) + +/* Generated from x86-codegen.h */ + +#define amd64_breakpoint_size(inst,size) do { x86_breakpoint(inst); } while (0) +#define amd64_cld_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_cld(inst); amd64_codegen_post(inst); } while (0) +#define amd64_stosb_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosb(inst); amd64_codegen_post(inst); } while (0) +#define amd64_stosl_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosl(inst); amd64_codegen_post(inst); } while (0) +#define amd64_stosd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosd(inst); amd64_codegen_post(inst); } while (0) +#define amd64_movsb_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsb(inst); amd64_codegen_post(inst); } while (0) +#define amd64_movsl_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsl(inst); amd64_codegen_post(inst); } while (0) +#define amd64_movsd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsd(inst); amd64_codegen_post(inst); } while (0) +#define amd64_prefix_size(inst,p,size) do { x86_prefix((inst), p); } while (0) +#define amd64_rdtsc_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_rdtsc(inst); amd64_codegen_post(inst); } while (0) +#define amd64_cmpxchg_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_cmpxchg_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_cmpxchg_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_cmpxchg_mem_reg((inst),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_cmpxchg_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_cmpxchg_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_xchg_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_xchg_reg_reg((inst),((dreg)&0x7),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_xchg_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_xchg_mem_reg((inst),(mem),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_xchg_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_xchg_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_inc_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_inc_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_inc_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_inc_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +//#define amd64_inc_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_inc_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_dec_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_dec_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_dec_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_dec_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +//#define amd64_dec_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_dec_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_not_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_not_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_not_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_not_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_not_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_not_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_neg_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_neg_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_neg_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_neg_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_neg_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_neg_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_nop_size(inst,size) do { amd64_codegen_pre(inst); x86_nop(inst); amd64_codegen_post(inst); } while (0) +//#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_reg_imm((inst),(opc),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_mem_imm_size(inst,opc,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_alu_mem_imm((inst),(opc),(mem),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_membase_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_alu_membase_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_membase8_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_alu_membase8_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_mem_reg_size(inst,opc,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_mem_reg((inst),(opc),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_membase_reg_size(inst,opc,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_alu_membase_reg((inst),(opc),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +//#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_alu_reg_reg((inst),(opc),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_reg8_reg8_size(inst,opc,dreg,reg,is_dreg_h,is_reg_h,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_alu_reg8_reg8((inst),(opc),((dreg)&0x7),((reg)&0x7),(is_dreg_h),(is_reg_h)); amd64_codegen_post(inst); } while (0) +#define amd64_alu_reg_mem_size(inst,opc,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_reg_mem((inst),(opc),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) +//#define amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_alu_reg_membase((inst),(opc),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_test_reg_imm_size(inst,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_test_reg_imm((inst),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_test_mem_imm_size(inst,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_test_mem_imm((inst),(mem),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_test_membase_imm_size(inst,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_test_membase_imm((inst),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_test_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_test_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_test_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_test_mem_reg((inst),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_test_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_test_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_reg_imm_size(inst,opc,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_reg_imm((inst),(opc),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_mem_imm_size(inst,opc,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_mem_imm((inst),(opc),(mem),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_membase_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_shift_membase_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_reg_size(inst,opc,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_reg((inst),(opc),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_mem_size(inst,opc,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_mem((inst),(opc),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_shift_membase_size(inst,opc,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_shift_membase((inst),(opc),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_shrd_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shrd_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_shrd_reg_imm_size(inst,dreg,reg,shamt,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shrd_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(shamt)); amd64_codegen_post(inst); } while (0) +#define amd64_shld_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shld_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_shld_reg_imm_size(inst,dreg,reg,shamt,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shld_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(shamt)); amd64_codegen_post(inst); } while (0) +#define amd64_mul_reg_size(inst,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mul_reg((inst),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_mul_mem_size(inst,mem,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_mul_mem((inst),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_mul_membase_size(inst,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_mul_membase((inst),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_imul_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_imul_reg_mem((inst),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_imul_reg_membase((inst),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_reg_imm_size(inst,dreg,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_imul_reg_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_mem_imm_size(inst,reg,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_imul_reg_mem_imm((inst),((reg)&0x7),(mem),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_imul_reg_membase_imm_size(inst,reg,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_imul_reg_membase_imm((inst),((reg)&0x7),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_div_reg_size(inst,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_div_reg((inst),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_div_mem_size(inst,mem,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_div_mem((inst),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_div_membase_size(inst,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_div_membase((inst),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_mem_reg((inst),(mem),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_regp_reg_size(inst,regp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(regp),0,(reg)); x86_mov_regp_reg((inst),(regp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_mov_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_memindex_reg_size(inst,basereg,disp,indexreg,shift,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_mov_memindex_reg((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_mov_reg_reg((inst),((dreg)&0x7),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_reg_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_reg_mem((inst),((reg)&0x7),(mem),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_reg_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_mov_reg_membase((inst),((reg)&0x7),((basereg)&0x7),(disp),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_mov_reg_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_clear_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_clear_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_reg_imm_size(inst,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_reg_imm((inst),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_mem_imm_size(inst,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_mov_mem_imm((inst),(mem),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +//#define amd64_mov_membase_imm_size(inst,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_mov_membase_imm((inst),((basereg)&0x7),(disp),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_mov_memindex_imm_size(inst,basereg,disp,indexreg,shift,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,(indexreg),(basereg)); x86_mov_memindex_imm((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) +#define amd64_lea_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_lea_mem((inst),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) +//#define amd64_lea_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_lea_membase((inst),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_lea_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_lea_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift)); amd64_codegen_post(inst); } while (0) +#define amd64_widen_reg_size(inst,dreg,reg,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_widen_reg((inst),((dreg)&0x7),((reg)&0x7),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) +#define amd64_widen_mem_size(inst,dreg,mem,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,0); x86_widen_mem((inst),((dreg)&0x7),(mem),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) +#define amd64_widen_membase_size(inst,dreg,basereg,disp,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(basereg)); x86_widen_membase((inst),((dreg)&0x7),((basereg)&0x7),(disp),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) +#define amd64_widen_memindex_size(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),(indexreg),(basereg)); x86_widen_memindex((inst),((dreg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) +#define amd64_cdq_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_cdq(inst); amd64_codegen_post(inst); } while (0) +#define amd64_wait_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_wait(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fp_op_mem_size(inst,opc,mem,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op_mem((inst),(opc),(mem),(is_double)); amd64_codegen_post(inst); } while (0) +#define amd64_fp_op_membase_size(inst,opc,basereg,disp,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fp_op_membase((inst),(opc),((basereg)&0x7),(disp),(is_double)); amd64_codegen_post(inst); } while (0) +#define amd64_fp_op_size(inst,opc,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op((inst),(opc),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fp_op_reg_size(inst,opc,index,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op_reg((inst),(opc),(index),(pop_stack)); amd64_codegen_post(inst); } while (0) +#define amd64_fp_int_op_membase_size(inst,opc,basereg,disp,is_int,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fp_int_op_membase((inst),(opc),((basereg)&0x7),(disp),(is_int)); amd64_codegen_post(inst); } while (0) +#define amd64_fstp_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fstp((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fcompp_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcompp(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fucompp_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucompp(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fnstsw_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fnstsw(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fnstcw_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fnstcw((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_fnstcw_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_fnstcw_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_fldcw_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldcw((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_fldcw_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fldcw_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_fchs_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fchs(inst); amd64_codegen_post(inst); } while (0) +#define amd64_frem_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_frem(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fxch_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fxch((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fcomi_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcomi((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fcomip_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcomip((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fucomi_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucomi((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fucomip_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucomip((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fld_size(inst,mem,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld((inst),(mem),(is_double)); amd64_codegen_post(inst); } while (0) +//#define amd64_fld_membase_size(inst,basereg,disp,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fld_membase((inst),((basereg)&0x7),(disp),(is_double)); amd64_codegen_post(inst); } while (0) +#define amd64_fld80_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld80_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_fld80_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_fld80_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_fild_size(inst,mem,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fild((inst),(mem),(is_long)); amd64_codegen_post(inst); } while (0) +#define amd64_fild_membase_size(inst,basereg,disp,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fild_membase((inst),((basereg)&0x7),(disp),(is_long)); amd64_codegen_post(inst); } while (0) +#define amd64_fld_reg_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld_reg((inst),(index)); amd64_codegen_post(inst); } while (0) +#define amd64_fldz_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldz(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fld1_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld1(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fldpi_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldpi(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fst_size(inst,mem,is_double,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fst((inst),(mem),(is_double),(pop_stack)); amd64_codegen_post(inst); } while (0) +#define amd64_fst_membase_size(inst,basereg,disp,is_double,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fst_membase((inst),((basereg)&0x7),(disp),(is_double),(pop_stack)); amd64_codegen_post(inst); } while (0) +#define amd64_fst80_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fst80_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_fst80_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fst80_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_fist_pop_size(inst,mem,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fist_pop((inst),(mem),(is_long)); amd64_codegen_post(inst); } while (0) +#define amd64_fist_pop_membase_size(inst,basereg,disp,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fist_pop_membase((inst),((basereg)&0x7),(disp),(is_long)); amd64_codegen_post(inst); } while (0) +#define amd64_fstsw_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_fstsw(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fist_membase_size(inst,basereg,disp,is_int,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fist_membase((inst),((basereg)&0x7),(disp),(is_int)); amd64_codegen_post(inst); } while (0) +//#define amd64_push_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_push_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_push_regp_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_push_regp((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_push_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_push_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +//#define amd64_push_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_push_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_push_memindex_size(inst,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,(indexreg),(basereg)); x86_push_memindex((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift)); amd64_codegen_post(inst); } while (0) +#define amd64_push_imm_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_push_imm((inst),(imm)); amd64_codegen_post(inst); } while (0) +//#define amd64_pop_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_pop_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_pop_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pop_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_pop_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_pop_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_pushad_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pushad(inst); amd64_codegen_post(inst); } while (0) +#define amd64_pushfd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pushfd(inst); amd64_codegen_post(inst); } while (0) +#define amd64_popad_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_popad(inst); amd64_codegen_post(inst); } while (0) +#define amd64_popfd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_popfd(inst); amd64_codegen_post(inst); } while (0) +#define amd64_loop_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loop((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_loope_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loope((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_loopne_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loopne((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_jump32_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_jump32((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_jump8_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_jump8((inst),(imm)); amd64_codegen_post(inst); } while (0) +#if !defined( __native_client_codegen__ ) +/* Defined above for Native Client, so they can be used in other macros */ +#define amd64_jump_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),0,0,0,(reg)); x86_jump_reg((inst),((reg)&0x7)); } while (0) +#define amd64_jump_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_jump_mem((inst),(mem)); } while (0) +#endif +#define amd64_jump_disp_size(inst,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_jump_disp((inst),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_branch8_size(inst,cond,imm,is_signed,size) do { x86_branch8((inst),(cond),(imm),(is_signed)); } while (0) +#define amd64_branch32_size(inst,cond,imm,is_signed,size) do { x86_branch32((inst),(cond),(imm),(is_signed)); } while (0) +#define amd64_branch_size_body(inst,cond,target,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_branch((inst),(cond),(target),(is_signed)); amd64_codegen_post(inst); } while (0) +#if defined(__default_codegen__) +#define amd64_branch_size(inst,cond,target,is_signed,size) do { amd64_branch_size_body((inst),(cond),(target),(is_signed),(size)); } while (0) +#elif defined(__native_client_codegen__) +#define amd64_branch_size(inst,cond,target,is_signed,size) \ + do { \ + /* amd64_branch_size_body used twice in */ \ + /* case of relocation by amd64_codegen_post */ \ + guint8* branch_start; \ + amd64_codegen_pre(inst); \ + amd64_branch_size_body((inst),(cond),(target),(is_signed),(size)); \ + inst = amd64_codegen_post(inst); \ + branch_start = inst; \ + amd64_branch_size_body((inst),(cond),(target),(is_signed),(size)); \ + mono_amd64_patch(branch_start, (target)); \ + } while (0) +#endif + +#define amd64_branch_disp_size(inst,cond,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_branch_disp((inst),(cond),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_set_reg_size(inst,cond,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex((inst),1,0,0,(reg)); x86_set_reg((inst),(cond),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_set_mem_size(inst,cond,mem,is_signed,size) do { amd64_codegen_pre(inst); x86_set_mem((inst),(cond),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) +#define amd64_set_membase_size(inst,cond,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_set_membase((inst),(cond),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) +//#define amd64_call_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_call_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_call_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_call_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) + +#if defined(__default_codegen__) + +#define amd64_call_imm_size(inst,disp,size) do { x86_call_imm((inst),(disp)); } while (0) +#define amd64_call_code_size(inst,target,size) do { x86_call_code((inst),(target)); } while (0) + +#elif defined(__native_client_codegen__) +/* Size is ignored for Native Client calls, we restrict jumping to 32-bits */ +#define amd64_call_imm_size(inst,disp,size) \ + do { \ + amd64_codegen_pre((inst)); \ + amd64_call_sequence_pre((inst)); \ + x86_call_imm((inst),(disp)); \ + amd64_call_sequence_post((inst)); \ + amd64_codegen_post((inst)); \ + } while (0) + +/* x86_call_code is called twice below, first so we can get the size of the */ +/* call sequence, and again so the exact offset from "inst" is used, since */ +/* the sequence could have moved from amd64_call_sequence_post. */ +/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ +#define amd64_call_code_size(inst,target,size) \ + do { \ + amd64_codegen_pre((inst)); \ + guint8* adjusted_start; \ + guint8* call_start; \ + amd64_call_sequence_pre((inst)); \ + x86_call_code((inst),(target)); \ + adjusted_start = amd64_call_sequence_post((inst)); \ + call_start = adjusted_start; \ + x86_call_code(adjusted_start, (target)); \ + amd64_codegen_post((inst)); \ + mono_amd64_patch(call_start, (target)); \ + } while (0) + +#endif /*__native_client_codegen__*/ + +//#define amd64_ret_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_ret(inst); amd64_codegen_post(inst); } while (0) +#define amd64_ret_imm_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_ret_imm((inst),(imm)); amd64_codegen_post(inst); } while (0) +#define amd64_cmov_reg_size(inst,cond,is_signed,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_cmov_reg((inst),(cond),(is_signed),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) +#define amd64_cmov_mem_size(inst,cond,is_signed,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_cmov_mem((inst),(cond),(is_signed),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) +#define amd64_cmov_membase_size(inst,cond,is_signed,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_cmov_membase((inst),(cond),(is_signed),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) +#define amd64_enter_size(inst,framesize) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_enter((inst),(framesize)); amd64_codegen_post(inst); } while (0) +//#define amd64_leave_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_leave(inst); amd64_codegen_post(inst); } while (0) +#define amd64_sahf_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_sahf(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fsin_size(inst,size) do { amd64_codegen_pre(inst); x86_fsin(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fcos_size(inst,size) do { amd64_codegen_pre(inst); x86_fcos(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fabs_size(inst,size) do { amd64_codegen_pre(inst); x86_fabs(inst); amd64_codegen_post(inst); } while (0) +#define amd64_ftst_size(inst,size) do { amd64_codegen_pre(inst); x86_ftst(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fxam_size(inst,size) do { amd64_codegen_pre(inst); x86_fxam(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fpatan_size(inst,size) do { amd64_codegen_pre(inst); x86_fpatan(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fprem_size(inst,size) do { amd64_codegen_pre(inst); x86_fprem(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fprem1_size(inst,size) do { amd64_codegen_pre(inst); x86_fprem1(inst); amd64_codegen_post(inst); } while (0) +#define amd64_frndint_size(inst,size) do { amd64_codegen_pre(inst); x86_frndint(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fsqrt_size(inst,size) do { amd64_codegen_pre(inst); x86_fsqrt(inst); amd64_codegen_post(inst); } while (0) +#define amd64_fptan_size(inst,size) do { amd64_codegen_pre(inst); x86_fptan(inst); amd64_codegen_post(inst); } while (0) +//#define amd64_padding_size(inst,size) do { amd64_codegen_pre(inst); x86_padding((inst),(size)); amd64_codegen_post(inst); } while (0) +#define amd64_prolog_size(inst,frame_size,reg_mask,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_prolog((inst),(frame_size),(reg_mask)); amd64_codegen_post(inst); } while (0) +#define amd64_epilog_size(inst,reg_mask,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_epilog((inst),(reg_mask)); amd64_codegen_post(inst); } while (0) +#define amd64_xadd_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_xadd_reg_reg ((inst), (dreg), (reg), (size)); amd64_codegen_post(inst); } while (0) +#define amd64_xadd_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_xadd_mem_reg((inst),(mem),((reg)&0x7), (size)); amd64_codegen_post(inst); } while (0) +#define amd64_xadd_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_xadd_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size)); amd64_codegen_post(inst); } while (0) + + + + +#define amd64_breakpoint(inst) amd64_breakpoint_size(inst,8) +#define amd64_cld(inst) amd64_cld_size(inst,8) +#define amd64_stosb(inst) amd64_stosb_size(inst,8) +#define amd64_stosl(inst) amd64_stosl_size(inst,8) +#define amd64_stosd(inst) amd64_stosd_size(inst,8) +#define amd64_movsb(inst) amd64_movsb_size(inst,8) +#define amd64_movsl(inst) amd64_movsl_size(inst,8) +#define amd64_movsd(inst) amd64_movsd_size(inst,8) +#define amd64_prefix(inst,p) amd64_prefix_size(inst,p,8) +#define amd64_rdtsc(inst) amd64_rdtsc_size(inst,8) +#define amd64_cmpxchg_reg_reg(inst,dreg,reg) amd64_cmpxchg_reg_reg_size(inst,dreg,reg,8) +#define amd64_cmpxchg_mem_reg(inst,mem,reg) amd64_cmpxchg_mem_reg_size(inst,mem,reg,8) +#define amd64_cmpxchg_membase_reg(inst,basereg,disp,reg) amd64_cmpxchg_membase_reg_size(inst,basereg,disp,reg,8) +#define amd64_xchg_reg_reg(inst,dreg,reg,size) amd64_xchg_reg_reg_size(inst,dreg,reg,size) +#define amd64_xchg_mem_reg(inst,mem,reg,size) amd64_xchg_mem_reg_size(inst,mem,reg,size) +#define amd64_xchg_membase_reg(inst,basereg,disp,reg,size) amd64_xchg_membase_reg_size(inst,basereg,disp,reg,size) +#define amd64_xadd_reg_reg(inst,dreg,reg,size) amd64_xadd_reg_reg_size(inst,dreg,reg,size) +#define amd64_xadd_mem_reg(inst,mem,reg,size) amd64_xadd_mem_reg_size(inst,mem,reg,size) +#define amd64_xadd_membase_reg(inst,basereg,disp,reg,size) amd64_xadd_membase_reg_size(inst,basereg,disp,reg,size) +#define amd64_inc_mem(inst,mem) amd64_inc_mem_size(inst,mem,8) +#define amd64_inc_membase(inst,basereg,disp) amd64_inc_membase_size(inst,basereg,disp,8) +#define amd64_inc_reg(inst,reg) amd64_inc_reg_size(inst,reg,8) +#define amd64_dec_mem(inst,mem) amd64_dec_mem_size(inst,mem,8) +#define amd64_dec_membase(inst,basereg,disp) amd64_dec_membase_size(inst,basereg,disp,8) +#define amd64_dec_reg(inst,reg) amd64_dec_reg_size(inst,reg,8) +#define amd64_not_mem(inst,mem) amd64_not_mem_size(inst,mem,8) +#define amd64_not_membase(inst,basereg,disp) amd64_not_membase_size(inst,basereg,disp,8) +#define amd64_not_reg(inst,reg) amd64_not_reg_size(inst,reg,8) +#define amd64_neg_mem(inst,mem) amd64_neg_mem_size(inst,mem,8) +#define amd64_neg_membase(inst,basereg,disp) amd64_neg_membase_size(inst,basereg,disp,8) +#define amd64_neg_reg(inst,reg) amd64_neg_reg_size(inst,reg,8) +#define amd64_nop(inst) amd64_nop_size(inst,8) +//#define amd64_alu_reg_imm(inst,opc,reg,imm) amd64_alu_reg_imm_size(inst,opc,reg,imm,8) +#define amd64_alu_mem_imm(inst,opc,mem,imm) amd64_alu_mem_imm_size(inst,opc,mem,imm,8) +#define amd64_alu_membase_imm(inst,opc,basereg,disp,imm) amd64_alu_membase_imm_size(inst,opc,basereg,disp,imm,8) +#define amd64_alu_mem_reg(inst,opc,mem,reg) amd64_alu_mem_reg_size(inst,opc,mem,reg,8) +#define amd64_alu_membase_reg(inst,opc,basereg,disp,reg) amd64_alu_membase_reg_size(inst,opc,basereg,disp,reg,8) +//#define amd64_alu_reg_reg(inst,opc,dreg,reg) amd64_alu_reg_reg_size(inst,opc,dreg,reg,8) +#define amd64_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) amd64_alu_reg8_reg8_size(inst,opc,dreg,reg,is_dreg_h,is_reg_h,8) +#define amd64_alu_reg_mem(inst,opc,reg,mem) amd64_alu_reg_mem_size(inst,opc,reg,mem,8) +#define amd64_alu_reg_membase(inst,opc,reg,basereg,disp) amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,8) +#define amd64_test_reg_imm(inst,reg,imm) amd64_test_reg_imm_size(inst,reg,imm,8) +#define amd64_test_mem_imm(inst,mem,imm) amd64_test_mem_imm_size(inst,mem,imm,8) +#define amd64_test_membase_imm(inst,basereg,disp,imm) amd64_test_membase_imm_size(inst,basereg,disp,imm,8) +#define amd64_test_reg_reg(inst,dreg,reg) amd64_test_reg_reg_size(inst,dreg,reg,8) +#define amd64_test_mem_reg(inst,mem,reg) amd64_test_mem_reg_size(inst,mem,reg,8) +#define amd64_test_membase_reg(inst,basereg,disp,reg) amd64_test_membase_reg_size(inst,basereg,disp,reg,8) +#define amd64_shift_reg_imm(inst,opc,reg,imm) amd64_shift_reg_imm_size(inst,opc,reg,imm,8) +#define amd64_shift_mem_imm(inst,opc,mem,imm) amd64_shift_mem_imm_size(inst,opc,mem,imm,8) +#define amd64_shift_membase_imm(inst,opc,basereg,disp,imm) amd64_shift_membase_imm_size(inst,opc,basereg,disp,imm,8) +#define amd64_shift_reg(inst,opc,reg) amd64_shift_reg_size(inst,opc,reg,8) +#define amd64_shift_mem(inst,opc,mem) amd64_shift_mem_size(inst,opc,mem,8) +#define amd64_shift_membase(inst,opc,basereg,disp) amd64_shift_membase_size(inst,opc,basereg,disp,8) +#define amd64_shrd_reg(inst,dreg,reg) amd64_shrd_reg_size(inst,dreg,reg,8) +#define amd64_shrd_reg_imm(inst,dreg,reg,shamt) amd64_shrd_reg_imm_size(inst,dreg,reg,shamt,8) +#define amd64_shld_reg(inst,dreg,reg) amd64_shld_reg_size(inst,dreg,reg,8) +#define amd64_shld_reg_imm(inst,dreg,reg,shamt) amd64_shld_reg_imm_size(inst,dreg,reg,shamt,8) +#define amd64_mul_reg(inst,reg,is_signed) amd64_mul_reg_size(inst,reg,is_signed,8) +#define amd64_mul_mem(inst,mem,is_signed) amd64_mul_mem_size(inst,mem,is_signed,8) +#define amd64_mul_membase(inst,basereg,disp,is_signed) amd64_mul_membase_size(inst,basereg,disp,is_signed,8) +#define amd64_imul_reg_reg(inst,dreg,reg) amd64_imul_reg_reg_size(inst,dreg,reg,8) +#define amd64_imul_reg_mem(inst,reg,mem) amd64_imul_reg_mem_size(inst,reg,mem,8) +#define amd64_imul_reg_membase(inst,reg,basereg,disp) amd64_imul_reg_membase_size(inst,reg,basereg,disp,8) +#define amd64_imul_reg_reg_imm(inst,dreg,reg,imm) amd64_imul_reg_reg_imm_size(inst,dreg,reg,imm,8) +#define amd64_imul_reg_mem_imm(inst,reg,mem,imm) amd64_imul_reg_mem_imm_size(inst,reg,mem,imm,8) +#define amd64_imul_reg_membase_imm(inst,reg,basereg,disp,imm) amd64_imul_reg_membase_imm_size(inst,reg,basereg,disp,imm,8) +#define amd64_div_reg(inst,reg,is_signed) amd64_div_reg_size(inst,reg,is_signed,8) +#define amd64_div_mem(inst,mem,is_signed) amd64_div_mem_size(inst,mem,is_signed,8) +#define amd64_div_membase(inst,basereg,disp,is_signed) amd64_div_membase_size(inst,basereg,disp,is_signed,8) +//#define amd64_mov_mem_reg(inst,mem,reg,size) amd64_mov_mem_reg_size(inst,mem,reg,size) +//#define amd64_mov_regp_reg(inst,regp,reg,size) amd64_mov_regp_reg_size(inst,regp,reg,size) +//#define amd64_mov_membase_reg(inst,basereg,disp,reg,size) amd64_mov_membase_reg_size(inst,basereg,disp,reg,size) +#define amd64_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) amd64_mov_memindex_reg_size(inst,basereg,disp,indexreg,shift,reg,size) +//#define amd64_mov_reg_reg(inst,dreg,reg,size) amd64_mov_reg_reg_size(inst,dreg,reg,size) +//#define amd64_mov_reg_mem(inst,reg,mem,size) amd64_mov_reg_mem_size(inst,reg,mem,size) +//#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) amd64_mov_reg_membase_size(inst,reg,basereg,disp,size) +#define amd64_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) +#define amd64_clear_reg(inst,reg) amd64_clear_reg_size(inst,reg,8) +//#define amd64_mov_reg_imm(inst,reg,imm) amd64_mov_reg_imm_size(inst,reg,imm,8) +#define amd64_mov_mem_imm(inst,mem,imm,size) amd64_mov_mem_imm_size(inst,mem,imm,size) +//#define amd64_mov_membase_imm(inst,basereg,disp,imm,size) amd64_mov_membase_imm_size(inst,basereg,disp,imm,size) +#define amd64_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) amd64_mov_memindex_imm_size(inst,basereg,disp,indexreg,shift,imm,size) +#define amd64_lea_mem(inst,reg,mem) amd64_lea_mem_size(inst,reg,mem,8) +//#define amd64_lea_membase(inst,reg,basereg,disp) amd64_lea_membase_size(inst,reg,basereg,disp,8) +#define amd64_lea_memindex(inst,reg,basereg,disp,indexreg,shift) amd64_lea_memindex_size(inst,reg,basereg,disp,indexreg,shift,8) +#define amd64_widen_reg(inst,dreg,reg,is_signed,is_half) amd64_widen_reg_size(inst,dreg,reg,is_signed,is_half,8) +#define amd64_widen_mem(inst,dreg,mem,is_signed,is_half) amd64_widen_mem_size(inst,dreg,mem,is_signed,is_half,8) +#define amd64_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) amd64_widen_membase_size(inst,dreg,basereg,disp,is_signed,is_half,8) +#define amd64_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) amd64_widen_memindex_size(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half,8) +#define amd64_cdq(inst) amd64_cdq_size(inst,8) +#define amd64_wait(inst) amd64_wait_size(inst,8) +#define amd64_fp_op_mem(inst,opc,mem,is_double) amd64_fp_op_mem_size(inst,opc,mem,is_double,8) +#define amd64_fp_op_membase(inst,opc,basereg,disp,is_double) amd64_fp_op_membase_size(inst,opc,basereg,disp,is_double,8) +#define amd64_fp_op(inst,opc,index) amd64_fp_op_size(inst,opc,index,8) +#define amd64_fp_op_reg(inst,opc,index,pop_stack) amd64_fp_op_reg_size(inst,opc,index,pop_stack,8) +#define amd64_fp_int_op_membase(inst,opc,basereg,disp,is_int) amd64_fp_int_op_membase_size(inst,opc,basereg,disp,is_int,8) +#define amd64_fstp(inst,index) amd64_fstp_size(inst,index,8) +#define amd64_fcompp(inst) amd64_fcompp_size(inst,8) +#define amd64_fucompp(inst) amd64_fucompp_size(inst,8) +#define amd64_fnstsw(inst) amd64_fnstsw_size(inst,8) +#define amd64_fnstcw(inst,mem) amd64_fnstcw_size(inst,mem,8) +#define amd64_fnstcw_membase(inst,basereg,disp) amd64_fnstcw_membase_size(inst,basereg,disp,8) +#define amd64_fldcw(inst,mem) amd64_fldcw_size(inst,mem,8) +#define amd64_fldcw_membase(inst,basereg,disp) amd64_fldcw_membase_size(inst,basereg,disp,8) +#define amd64_fchs(inst) amd64_fchs_size(inst,8) +#define amd64_frem(inst) amd64_frem_size(inst,8) +#define amd64_fxch(inst,index) amd64_fxch_size(inst,index,8) +#define amd64_fcomi(inst,index) amd64_fcomi_size(inst,index,8) +#define amd64_fcomip(inst,index) amd64_fcomip_size(inst,index,8) +#define amd64_fucomi(inst,index) amd64_fucomi_size(inst,index,8) +#define amd64_fucomip(inst,index) amd64_fucomip_size(inst,index,8) +#define amd64_fld(inst,mem,is_double) amd64_fld_size(inst,mem,is_double,8) +#define amd64_fld_membase(inst,basereg,disp,is_double) amd64_fld_membase_size(inst,basereg,disp,is_double,8) +#define amd64_fld80_mem(inst,mem) amd64_fld80_mem_size(inst,mem,8) +#define amd64_fld80_membase(inst,basereg,disp) amd64_fld80_membase_size(inst,basereg,disp,8) +#define amd64_fild(inst,mem,is_long) amd64_fild_size(inst,mem,is_long,8) +#define amd64_fild_membase(inst,basereg,disp,is_long) amd64_fild_membase_size(inst,basereg,disp,is_long,8) +#define amd64_fld_reg(inst,index) amd64_fld_reg_size(inst,index,8) +#define amd64_fldz(inst) amd64_fldz_size(inst,8) +#define amd64_fld1(inst) amd64_fld1_size(inst,8) +#define amd64_fldpi(inst) amd64_fldpi_size(inst,8) +#define amd64_fst(inst,mem,is_double,pop_stack) amd64_fst_size(inst,mem,is_double,pop_stack,8) +#define amd64_fst_membase(inst,basereg,disp,is_double,pop_stack) amd64_fst_membase_size(inst,basereg,disp,is_double,pop_stack,8) +#define amd64_fst80_mem(inst,mem) amd64_fst80_mem_size(inst,mem,8) +#define amd64_fst80_membase(inst,basereg,disp) amd64_fst80_membase_size(inst,basereg,disp,8) +#define amd64_fist_pop(inst,mem,is_long) amd64_fist_pop_size(inst,mem,is_long,8) +#define amd64_fist_pop_membase(inst,basereg,disp,is_long) amd64_fist_pop_membase_size(inst,basereg,disp,is_long,8) +#define amd64_fstsw(inst) amd64_fstsw_size(inst,8) +#define amd64_fist_membase(inst,basereg,disp,is_int) amd64_fist_membase_size(inst,basereg,disp,is_int,8) +//#define amd64_push_reg(inst,reg) amd64_push_reg_size(inst,reg,8) +#define amd64_push_regp(inst,reg) amd64_push_regp_size(inst,reg,8) +#define amd64_push_mem(inst,mem) amd64_push_mem_size(inst,mem,8) +//#define amd64_push_membase(inst,basereg,disp) amd64_push_membase_size(inst,basereg,disp,8) +#define amd64_push_memindex(inst,basereg,disp,indexreg,shift) amd64_push_memindex_size(inst,basereg,disp,indexreg,shift,8) +#define amd64_push_imm(inst,imm) amd64_push_imm_size(inst,imm,8) +//#define amd64_pop_reg(inst,reg) amd64_pop_reg_size(inst,reg,8) +#define amd64_pop_mem(inst,mem) amd64_pop_mem_size(inst,mem,8) +#define amd64_pop_membase(inst,basereg,disp) amd64_pop_membase_size(inst,basereg,disp,8) +#define amd64_pushad(inst) amd64_pushad_size(inst,8) +#define amd64_pushfd(inst) amd64_pushfd_size(inst,8) +#define amd64_popad(inst) amd64_popad_size(inst,8) +#define amd64_popfd(inst) amd64_popfd_size(inst,8) +#define amd64_loop(inst,imm) amd64_loop_size(inst,imm,8) +#define amd64_loope(inst,imm) amd64_loope_size(inst,imm,8) +#define amd64_loopne(inst,imm) amd64_loopne_size(inst,imm,8) +#define amd64_jump32(inst,imm) amd64_jump32_size(inst,imm,8) +#define amd64_jump8(inst,imm) amd64_jump8_size(inst,imm,8) +#define amd64_jump_reg(inst,reg) amd64_jump_reg_size(inst,reg,8) +#define amd64_jump_mem(inst,mem) amd64_jump_mem_size(inst,mem,8) +#define amd64_jump_membase(inst,basereg,disp) amd64_jump_membase_size(inst,basereg,disp,8) +#define amd64_jump_code(inst,target) amd64_jump_code_size(inst,target,8) +#define amd64_jump_disp(inst,disp) amd64_jump_disp_size(inst,disp,8) +#define amd64_branch8(inst,cond,imm,is_signed) amd64_branch8_size(inst,cond,imm,is_signed,8) +#define amd64_branch32(inst,cond,imm,is_signed) amd64_branch32_size(inst,cond,imm,is_signed,8) +#define amd64_branch(inst,cond,target,is_signed) amd64_branch_size(inst,cond,target,is_signed,8) +#define amd64_branch_disp(inst,cond,disp,is_signed) amd64_branch_disp_size(inst,cond,disp,is_signed,8) +#define amd64_set_reg(inst,cond,reg,is_signed) amd64_set_reg_size(inst,cond,reg,is_signed,8) +#define amd64_set_mem(inst,cond,mem,is_signed) amd64_set_mem_size(inst,cond,mem,is_signed,8) +#define amd64_set_membase(inst,cond,basereg,disp,is_signed) amd64_set_membase_size(inst,cond,basereg,disp,is_signed,8) +#define amd64_call_imm(inst,disp) amd64_call_imm_size(inst,disp,8) +//#define amd64_call_reg(inst,reg) amd64_call_reg_size(inst,reg,8) +#define amd64_call_mem(inst,mem) amd64_call_mem_size(inst,mem,8) +#define amd64_call_membase(inst,basereg,disp) amd64_call_membase_size(inst,basereg,disp,8) +#define amd64_call_code(inst,target) amd64_call_code_size(inst,target,8) +//#define amd64_ret(inst) amd64_ret_size(inst,8) +#define amd64_ret_imm(inst,imm) amd64_ret_imm_size(inst,imm,8) +#define amd64_cmov_reg(inst,cond,is_signed,dreg,reg) amd64_cmov_reg_size(inst,cond,is_signed,dreg,reg,8) +#define amd64_cmov_mem(inst,cond,is_signed,reg,mem) amd64_cmov_mem_size(inst,cond,is_signed,reg,mem,8) +#define amd64_cmov_membase(inst,cond,is_signed,reg,basereg,disp) amd64_cmov_membase_size(inst,cond,is_signed,reg,basereg,disp,8) +#define amd64_enter(inst,framesize) amd64_enter_size(inst,framesize) +//#define amd64_leave(inst) amd64_leave_size(inst,8) +#define amd64_sahf(inst) amd64_sahf_size(inst,8) +#define amd64_fsin(inst) amd64_fsin_size(inst,8) +#define amd64_fcos(inst) amd64_fcos_size(inst,8) +#define amd64_fabs(inst) amd64_fabs_size(inst,8) +#define amd64_ftst(inst) amd64_ftst_size(inst,8) +#define amd64_fxam(inst) amd64_fxam_size(inst,8) +#define amd64_fpatan(inst) amd64_fpatan_size(inst,8) +#define amd64_fprem(inst) amd64_fprem_size(inst,8) +#define amd64_fprem1(inst) amd64_fprem1_size(inst,8) +#define amd64_frndint(inst) amd64_frndint_size(inst,8) +#define amd64_fsqrt(inst) amd64_fsqrt_size(inst,8) +#define amd64_fptan(inst) amd64_fptan_size(inst,8) +#define amd64_padding(inst,size) amd64_padding_size(inst,size) +#define amd64_prolog(inst,frame,reg_mask) amd64_prolog_size(inst,frame,reg_mask,8) +#define amd64_epilog(inst,reg_mask) amd64_epilog_size(inst,reg_mask,8) + +#endif // AMD64_H diff --git a/asm/arm-codegen.c b/asm/arm-codegen.c new file mode 100644 index 0000000000..9914ace34f --- /dev/null +++ b/asm/arm-codegen.c @@ -0,0 +1,193 @@ +/* + * arm-codegen.c + * Copyright (c) 2002 Sergey Chaban + */ + +#include "arm-codegen.h" + + +arminstr_t* arm_emit_std_prologue(arminstr_t* p, unsigned int local_size) { + ARM_MOV_REG_REG(p, ARMREG_IP, ARMREG_SP); + + /* save args */ + ARM_PUSH(p, (1 << ARMREG_A1) + | (1 << ARMREG_A2) + | (1 << ARMREG_A3) + | (1 << ARMREG_A4)); + + ARM_PUSH(p, (1U << ARMREG_IP) | (1U << ARMREG_LR)); + + if (local_size != 0) { + if ((local_size & (~0xFF)) == 0) { + ARM_SUB_REG_IMM8(p, ARMREG_SP, ARMREG_SP, local_size); + } else { + /* TODO: optimize */ + p = arm_mov_reg_imm32(p, ARMREG_IP, local_size); + ARM_SUB_REG_REG(p, ARMREG_SP, ARMREG_SP, ARMREG_IP); + ARM_ADD_REG_IMM8(p, ARMREG_IP, ARMREG_IP, sizeof(armword_t)); + ARM_LDR_REG_REG(p, ARMREG_IP, ARMREG_SP, ARMREG_IP); + } + } + + return p; +} + +arminstr_t* arm_emit_std_epilogue(arminstr_t* p, unsigned int local_size, int pop_regs) { + if (local_size != 0) { + if ((local_size & (~0xFF)) == 0) { + ARM_ADD_REG_IMM8(p, ARMREG_SP, ARMREG_SP, local_size); + } else { + /* TODO: optimize */ + p = arm_mov_reg_imm32(p, ARMREG_IP, local_size); + ARM_ADD_REG_REG(p, ARMREG_SP, ARMREG_SP, ARMREG_IP); + } + } + + ARM_POP_NWB(p, (1 << ARMREG_SP) | (1 << ARMREG_PC) | (pop_regs & 0x3FF)); + + return p; +} + + +/* do not push A1-A4 */ +arminstr_t* arm_emit_lean_prologue(arminstr_t* p, unsigned int local_size, int push_regs) { + ARM_MOV_REG_REG(p, ARMREG_IP, ARMREG_SP); + /* push_regs upto R10 will be saved */ + ARM_PUSH(p, (1U << ARMREG_IP) | (1U << ARMREG_LR) | (push_regs & 0x3FF)); + + if (local_size != 0) { + if ((local_size & (~0xFF)) == 0) { + ARM_SUB_REG_IMM8(p, ARMREG_SP, ARMREG_SP, local_size); + } else { + /* TODO: optimize */ + p = arm_mov_reg_imm32(p, ARMREG_IP, local_size); + ARM_SUB_REG_REG(p, ARMREG_SP, ARMREG_SP, ARMREG_IP); + /* restore IP from stack */ + ARM_ADD_REG_IMM8(p, ARMREG_IP, ARMREG_IP, sizeof(armword_t)); + ARM_LDR_REG_REG(p, ARMREG_IP, ARMREG_SP, ARMREG_IP); + } + } + + return p; +} + +/* Bit scan forward. */ +int arm_bsf(armword_t val) { + int i; + armword_t mask; + + if (val == 0) return 0; + for (i=1, mask=1; (i <= 8 * sizeof(armword_t)) && ((val & mask) == 0); ++i, mask<<=1); + + return i; +} + + +int arm_is_power_of_2(armword_t val) { + return ((val & (val-1)) == 0); +} + + +/* + * returns: + * 1 - unable to represent + * positive even number - MOV-representable + * negative even number - MVN-representable + */ +int calc_arm_mov_const_shift(armword_t val) { + armword_t mask; + int res = 1, shift; + + for (shift=0; shift < 32; shift+=2) { + mask = ARM_SCALE(0xFF, shift); + if ((val & (~mask)) == 0) { + res = shift; + break; + } + if (((~val) & (~mask)) == 0) { + res = -shift - 2; + break; + } + } + + return res; +} + + +int is_arm_const(armword_t val) { + int res; + res = arm_is_power_of_2(val); + if (!res) { + res = calc_arm_mov_const_shift(val); + res = !(res < 0 || res == 1); + } + return res; +} + + +int arm_const_steps(armword_t val) { + int shift, steps = 0; + + while (val != 0) { + shift = (arm_bsf(val) - 1) & (~1); + val &= ~(0xFF << shift); + ++steps; + } + return steps; +} + + +/* + * ARM cannot load arbitrary 32-bit constants directly into registers; + * widely used work-around for this is to store constants into a + * PC-addressable pool and use LDR instruction with PC-relative address + * to load constant into register. Easiest way to implement this is to + * embed constant inside a function with unconditional branch around it. + * The above method is not used at the moment. + * This routine always emits sequence of instructions to generate + * requested constant. In the worst case it takes 4 instructions to + * synthesize a constant - 1 MOV and 3 subsequent ORRs. + */ +arminstr_t* arm_mov_reg_imm32_cond(arminstr_t* p, int reg, armword_t imm32, int cond) { + int mov_op; + int step_op; + int snip; + int shift = calc_arm_mov_const_shift(imm32); + + if ((shift & 0x80000001) != 1) { + if (shift >= 0) { + ARM_MOV_REG_IMM_COND(p, reg, imm32 >> ((32 - shift) & 31), shift, cond); + } else { + ARM_MVN_REG_IMM_COND(p, reg, (imm32 ^ (~0)) >> ((32 + 2 + shift) & 31), (-shift - 2), cond); + } + } else { + mov_op = ARMOP_MOV; + step_op = ARMOP_ORR; + + if (arm_const_steps(imm32) > arm_const_steps(~imm32)) { + mov_op = ARMOP_MVN; + step_op = ARMOP_SUB; + imm32 = ~imm32; + } + + shift = (arm_bsf(imm32) - 1) & (~1); + snip = imm32 & (0xFF << shift); + ARM_EMIT(p, ARM_DEF_DPI_IMM_COND((unsigned)snip >> shift, (32 - shift) >> 1, reg, 0, 0, mov_op, cond)); + + while ((imm32 ^= snip) != 0) { + shift = (arm_bsf(imm32) - 1) & (~1); + snip = imm32 & (0xFF << shift); + ARM_EMIT(p, ARM_DEF_DPI_IMM_COND((unsigned)snip >> shift, (32 - shift) >> 1, reg, reg, 0, step_op, cond)); + } + } + + return p; +} + + +arminstr_t* arm_mov_reg_imm32(arminstr_t* p, int reg, armword_t imm32) { + return arm_mov_reg_imm32_cond(p, reg, imm32, ARMCOND_AL); +} + + + diff --git a/asm/arm-codegen.h b/asm/arm-codegen.h new file mode 100644 index 0000000000..5be4e9f955 --- /dev/null +++ b/asm/arm-codegen.h @@ -0,0 +1,1103 @@ +/* + * arm-codegen.h + * Copyright (c) 2002-2003 Sergey Chaban + * Copyright 2005-2011 Novell Inc + * Copyright 2011 Xamarin Inc + */ + + +#ifndef ARM_H +#define ARM_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int arminstr_t; +typedef unsigned int armword_t; + +/* Helper functions */ +arminstr_t* arm_emit_std_prologue(arminstr_t* p, unsigned int local_size); +arminstr_t* arm_emit_std_epilogue(arminstr_t* p, unsigned int local_size, int pop_regs); +arminstr_t* arm_emit_lean_prologue(arminstr_t* p, unsigned int local_size, int push_regs); +int arm_is_power_of_2(armword_t val); +int calc_arm_mov_const_shift(armword_t val); +int is_arm_const(armword_t val); +int arm_bsf(armword_t val); +arminstr_t* arm_mov_reg_imm32_cond(arminstr_t* p, int reg, armword_t imm32, int cond); +arminstr_t* arm_mov_reg_imm32(arminstr_t* p, int reg, armword_t imm32); + + + +#if defined(_MSC_VER) || defined(__CC_NORCROFT) + void __inline _arm_emit(arminstr_t** p, arminstr_t i) {**p = i; (*p)++;} +# define ARM_EMIT(p, i) _arm_emit((arminstr_t**)&p, (arminstr_t)(i)) +#else +# define ARM_EMIT(p, i) do { arminstr_t *__ainstrp = (arminstr_t*)(p); *__ainstrp = (arminstr_t)(i); (p) = (arminstr_t*)(__ainstrp+1);} while (0) +#endif + +#if defined(_MSC_VER) && !defined(ARM_NOIASM) +# define ARM_IASM(_expr) __emit (_expr) +#else +# define ARM_IASM(_expr) +#endif + +/* even_scale = rot << 1 */ +#define ARM_SCALE(imm8, even_scale) ( ((imm8) >> (even_scale)) | ((imm8) << (32 - even_scale)) ) + + + +typedef enum { + ARMREG_R0 = 0, + ARMREG_R1, + ARMREG_R2, + ARMREG_R3, + ARMREG_R4, + ARMREG_R5, + ARMREG_R6, + ARMREG_R7, + ARMREG_R8, + ARMREG_R9, + ARMREG_R10, + ARMREG_R11, + ARMREG_R12, + ARMREG_R13, + ARMREG_R14, + ARMREG_R15, + + + /* aliases */ + /* args */ + ARMREG_A1 = ARMREG_R0, + ARMREG_A2 = ARMREG_R1, + ARMREG_A3 = ARMREG_R2, + ARMREG_A4 = ARMREG_R3, + + /* local vars */ + ARMREG_V1 = ARMREG_R4, + ARMREG_V2 = ARMREG_R5, + ARMREG_V3 = ARMREG_R6, + ARMREG_V4 = ARMREG_R7, + ARMREG_V5 = ARMREG_R8, + ARMREG_V6 = ARMREG_R9, + ARMREG_V7 = ARMREG_R10, + + ARMREG_FP = ARMREG_R11, + ARMREG_IP = ARMREG_R12, + ARMREG_SP = ARMREG_R13, + ARMREG_LR = ARMREG_R14, + ARMREG_PC = ARMREG_R15, + + /* co-processor */ + ARMREG_CR0 = 0, + ARMREG_CR1, + ARMREG_CR2, + ARMREG_CR3, + ARMREG_CR4, + ARMREG_CR5, + ARMREG_CR6, + ARMREG_CR7, + ARMREG_CR8, + ARMREG_CR9, + ARMREG_CR10, + ARMREG_CR11, + ARMREG_CR12, + ARMREG_CR13, + ARMREG_CR14, + ARMREG_CR15, + + /* XScale: acc0 on CP0 */ + ARMREG_ACC0 = ARMREG_CR0, + + ARMREG_MAX = ARMREG_R15 +} ARMReg; + +/* number of argument registers */ +#define ARM_NUM_ARG_REGS 4 + +/* bitvector for all argument regs (A1-A4) */ +#define ARM_ALL_ARG_REGS \ + (1 << ARMREG_A1) | (1 << ARMREG_A2) | (1 << ARMREG_A3) | (1 << ARMREG_A4) + + +typedef enum { + ARMCOND_EQ = 0x0, /* Equal; Z = 1 */ + ARMCOND_NE = 0x1, /* Not equal, or unordered; Z = 0 */ + ARMCOND_CS = 0x2, /* Carry set; C = 1 */ + ARMCOND_HS = ARMCOND_CS, /* Unsigned higher or same; */ + ARMCOND_CC = 0x3, /* Carry clear; C = 0 */ + ARMCOND_LO = ARMCOND_CC, /* Unsigned lower */ + ARMCOND_MI = 0x4, /* Negative; N = 1 */ + ARMCOND_PL = 0x5, /* Positive or zero; N = 0 */ + ARMCOND_VS = 0x6, /* Overflow; V = 1 */ + ARMCOND_VC = 0x7, /* No overflow; V = 0 */ + ARMCOND_HI = 0x8, /* Unsigned higher; C = 1 && Z = 0 */ + ARMCOND_LS = 0x9, /* Unsigned lower or same; C = 0 || Z = 1 */ + ARMCOND_GE = 0xA, /* Signed greater than or equal; N = V */ + ARMCOND_LT = 0xB, /* Signed less than; N != V */ + ARMCOND_GT = 0xC, /* Signed greater than; Z = 0 && N = V */ + ARMCOND_LE = 0xD, /* Signed less than or equal; Z = 1 && N != V */ + ARMCOND_AL = 0xE, /* Always */ + ARMCOND_NV = 0xF, /* Never */ + + ARMCOND_SHIFT = 28 +} ARMCond; + +#define ARMCOND_MASK (ARMCOND_NV << ARMCOND_SHIFT) + +#define ARM_DEF_COND(cond) (((cond) & 0xF) << ARMCOND_SHIFT) + + + +typedef enum { + ARMSHIFT_LSL = 0, + ARMSHIFT_LSR = 1, + ARMSHIFT_ASR = 2, + ARMSHIFT_ROR = 3, + + ARMSHIFT_ASL = ARMSHIFT_LSL + /* rrx = (ror, 1) */ +} ARMShiftType; + + +typedef struct { + armword_t PSR_c : 8; + armword_t PSR_x : 8; + armword_t PSR_s : 8; + armword_t PSR_f : 8; +} ARMPSR; + +typedef enum { + ARMOP_AND = 0x0, + ARMOP_EOR = 0x1, + ARMOP_SUB = 0x2, + ARMOP_RSB = 0x3, + ARMOP_ADD = 0x4, + ARMOP_ADC = 0x5, + ARMOP_SBC = 0x6, + ARMOP_RSC = 0x7, + ARMOP_TST = 0x8, + ARMOP_TEQ = 0x9, + ARMOP_CMP = 0xa, + ARMOP_CMN = 0xb, + ARMOP_ORR = 0xc, + ARMOP_MOV = 0xd, + ARMOP_BIC = 0xe, + ARMOP_MVN = 0xf, + + + /* not really opcodes */ + + ARMOP_STR = 0x0, + ARMOP_LDR = 0x1, + + /* ARM2+ */ + ARMOP_MUL = 0x0, /* Rd := Rm*Rs */ + ARMOP_MLA = 0x1, /* Rd := (Rm*Rs)+Rn */ + + /* ARM3M+ */ + ARMOP_UMULL = 0x4, + ARMOP_UMLAL = 0x5, + ARMOP_SMULL = 0x6, + ARMOP_SMLAL = 0x7, + + /* for data transfers with register offset */ + ARM_UP = 1, + ARM_DOWN = 0 +} ARMOpcode; + +typedef enum { + THUMBOP_AND = 0, + THUMBOP_EOR = 1, + THUMBOP_LSL = 2, + THUMBOP_LSR = 3, + THUMBOP_ASR = 4, + THUMBOP_ADC = 5, + THUMBOP_SBC = 6, + THUMBOP_ROR = 7, + THUMBOP_TST = 8, + THUMBOP_NEG = 9, + THUMBOP_CMP = 10, + THUMBOP_CMN = 11, + THUMBOP_ORR = 12, + THUMBOP_MUL = 13, + THUMBOP_BIC = 14, + THUMBOP_MVN = 15, + THUMBOP_MOV = 16, + THUMBOP_CMPI = 17, + THUMBOP_ADD = 18, + THUMBOP_SUB = 19, + THUMBOP_CMPH = 19, + THUMBOP_MOVH = 20 +} ThumbOpcode; + + +/* Generic form - all ARM instructions are conditional. */ +typedef struct { + arminstr_t icode : 28; + arminstr_t cond : 4; +} ARMInstrGeneric; + + + +/* Branch or Branch with Link instructions. */ +typedef struct { + arminstr_t offset : 24; + arminstr_t link : 1; + arminstr_t tag : 3; /* 1 0 1 */ + arminstr_t cond : 4; +} ARMInstrBR; + +#define ARM_BR_ID 5 +#define ARM_BR_MASK 7 << 25 +#define ARM_BR_TAG ARM_BR_ID << 25 + +#define ARM_DEF_BR(offs, l, cond) ((offs) | ((l) << 24) | (ARM_BR_TAG) | (cond << ARMCOND_SHIFT)) + +/* branch */ +#define ARM_B_COND(p, cond, offset) ARM_EMIT(p, ARM_DEF_BR(offset, 0, cond)) +#define ARM_B(p, offs) ARM_B_COND((p), ARMCOND_AL, (offs)) +/* branch with link */ +#define ARM_BL_COND(p, cond, offset) ARM_EMIT(p, ARM_DEF_BR(offset, 1, cond)) +#define ARM_BL(p, offs) ARM_BL_COND((p), ARMCOND_AL, (offs)) + +#define ARM_DEF_BX(reg,sub,cond) (0x12fff << 8 | (reg) | ((sub) << 4) | ((cond) << ARMCOND_SHIFT)) + +#define ARM_BX_COND(p, cond, reg) ARM_EMIT(p, ARM_DEF_BX(reg, 1, cond)) +#define ARM_BX(p, reg) ARM_BX_COND((p), ARMCOND_AL, (reg)) + +#define ARM_BLX_REG_COND(p, cond, reg) ARM_EMIT(p, ARM_DEF_BX(reg, 3, cond)) +#define ARM_BLX_REG(p, reg) ARM_BLX_REG_COND((p), ARMCOND_AL, (reg)) + +/* Data Processing Instructions - there are 3 types. */ + +typedef struct { + arminstr_t imm : 8; + arminstr_t rot : 4; +} ARMDPI_op2_imm; + +typedef struct { + arminstr_t rm : 4; + arminstr_t tag : 1; /* 0 - immediate shift, 1 - reg shift */ + arminstr_t type : 2; /* shift type - logical, arithmetic, rotate */ +} ARMDPI_op2_reg_shift; + + +/* op2 is reg shift by imm */ +typedef union { + ARMDPI_op2_reg_shift r2; + struct { + arminstr_t _dummy_r2 : 7; + arminstr_t shift : 5; + } imm; +} ARMDPI_op2_reg_imm; + +/* op2 is reg shift by reg */ +typedef union { + ARMDPI_op2_reg_shift r2; + struct { + arminstr_t _dummy_r2 : 7; + arminstr_t pad : 1; /* always 0, to differentiate from HXFER etc. */ + arminstr_t rs : 4; + } reg; +} ARMDPI_op2_reg_reg; + +/* Data processing instrs */ +typedef union { + ARMDPI_op2_imm op2_imm; + + ARMDPI_op2_reg_shift op2_reg; + ARMDPI_op2_reg_imm op2_reg_imm; + ARMDPI_op2_reg_reg op2_reg_reg; + + struct { + arminstr_t op2 : 12; /* raw operand 2 */ + arminstr_t rd : 4; /* destination reg */ + arminstr_t rn : 4; /* first operand reg */ + arminstr_t s : 1; /* S-bit controls PSR update */ + arminstr_t opcode : 4; /* arithmetic/logic operation */ + arminstr_t type : 1; /* type of op2, 0 = register, 1 = immediate */ + arminstr_t tag : 2; /* 0 0 */ + arminstr_t cond : 4; + } all; +} ARMInstrDPI; + +#define ARM_DPI_ID 0 +#define ARM_DPI_MASK 3 << 26 +#define ARM_DPI_TAG ARM_DPI_ID << 26 + +#define ARM_DEF_DPI_IMM_COND(imm8, rot, rd, rn, s, op, cond) \ + ((imm8) & 0xFF) | \ + (((rot) & 0xF) << 8) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((s) << 20) | \ + ((op) << 21) | \ + (1 << 25) | \ + (ARM_DPI_TAG) | \ + ARM_DEF_COND(cond) + + +#define ARM_DEF_DPI_IMM(imm8, rot, rd, rn, s, op) \ + ARM_DEF_DPI_IMM_COND(imm8, rot, rd, rn, s, op, ARMCOND_AL) + +/* codegen */ +#define ARM_DPIOP_REG_IMM8ROT_COND(p, op, rd, rn, imm8, rot, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_IMM_COND((imm8), ((rot) >> 1), (rd), (rn), 0, (op), cond)) +#define ARM_DPIOP_S_REG_IMM8ROT_COND(p, op, rd, rn, imm8, rot, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_IMM_COND((imm8), ((rot) >> 1), (rd), (rn), 1, (op), cond)) + +/* inline */ +#define ARM_IASM_DPIOP_REG_IMM8ROT_COND(p, op, rd, rn, imm8, rot, cond) \ + ARM_IASM(ARM_DEF_DPI_IMM_COND((imm8), ((rot) >> 1), (rd), (rn), 0, (op), cond)) +#define ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(p, op, rd, rn, imm8, rot, cond) \ + ARM_IASM(ARM_DEF_DPI_IMM_COND((imm8), ((rot) >> 1), (rd), (rn), 1, (op), cond)) + + + +#define ARM_DEF_DPI_REG_IMMSHIFT_COND(rm, shift_type, imm_shift, rd, rn, s, op, cond) \ + (rm) | \ + ((shift_type & 3) << 5) | \ + (((imm_shift) & 0x1F) << 7) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((s) << 20) | \ + ((op) << 21) | \ + (ARM_DPI_TAG) | \ + ARM_DEF_COND(cond) + +/* codegen */ +#define ARM_DPIOP_REG_IMMSHIFT_COND(p, op, rd, rn, rm, shift_t, imm_shift, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), shift_t, imm_shift, (rd), (rn), 0, (op), cond)) + +#define ARM_DPIOP_S_REG_IMMSHIFT_COND(p, op, rd, rn, rm, shift_t, imm_shift, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), shift_t, imm_shift, (rd), (rn), 1, (op), cond)) + +#define ARM_DPIOP_REG_REG_COND(p, op, rd, rn, rm, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), ARMSHIFT_LSL, 0, (rd), (rn), 0, (op), cond)) + +#define ARM_DPIOP_S_REG_REG_COND(p, op, rd, rn, rm, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), ARMSHIFT_LSL, 0, (rd), (rn), 1, (op), cond)) + +/* inline */ +#define ARM_IASM_DPIOP_REG_IMMSHIFT_COND(p, op, rd, rn, rm, shift_t, imm_shift, cond) \ + ARM_IASM(ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), shift_t, imm_shift, (rd), (rn), 0, (op), cond)) + +#define ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(p, op, rd, rn, rm, shift_t, imm_shift, cond) \ + ARM_IASM(ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), shift_t, imm_shift, (rd), (rn), 1, (op), cond)) + +#define ARM_IASM_DPIOP_REG_REG_COND(p, op, rd, rn, rm, cond) \ + ARM_IASM(ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), ARMSHIFT_LSL, 0, (rd), (rn), 0, (op), cond)) + +#define ARM_IASM_DPIOP_S_REG_REG_COND(p, op, rd, rn, rm, cond) \ + ARM_IASM_EMIT(ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), ARMSHIFT_LSL, 0, (rd), (rn), 1, (op), cond)) + + +/* Rd := Rn op (Rm shift_type Rs) */ +#define ARM_DEF_DPI_REG_REGSHIFT_COND(rm, shift_type, rs, rd, rn, s, op, cond) \ + (rm) | \ + (1 << 4) | \ + ((shift_type & 3) << 5) | \ + ((rs) << 8) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((s) << 20) | \ + ((op) << 21) | \ + (ARM_DPI_TAG) | \ + ARM_DEF_COND(cond) + +/* codegen */ +#define ARM_DPIOP_REG_REGSHIFT_COND(p, op, rd, rn, rm, shift_t, rs, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_REG_REGSHIFT_COND((rm), shift_t, (rs), (rd), (rn), 0, (op), cond)) + +#define ARM_DPIOP_S_REG_REGSHIFT_COND(p, op, rd, rn, rm, shift_t, rs, cond) \ + ARM_EMIT(p, ARM_DEF_DPI_REG_REGSHIFT_COND((rm), shift_t, (rs), (rd), (rn), 1, (op), cond)) + +/* inline */ +#define ARM_IASM_DPIOP_REG_REGSHIFT_COND(p, op, rd, rn, rm, shift_t, rs, cond) \ + ARM_IASM(ARM_DEF_DPI_REG_REGSHIFT_COND((rm), shift_t, (rs), (rd), (rn), 0, (op), cond)) + +#define ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(p, op, rd, rn, rm, shift_t, rs, cond) \ + ARM_IASM(ARM_DEF_DPI_REG_REGSHIFT_COND((rm), shift_t, (rs), (rd), (rn), 1, (op), cond)) + + + +/* Multiple register transfer. */ +typedef struct { + arminstr_t reg_list : 16; /* bitfield */ + arminstr_t rn : 4; /* base reg */ + arminstr_t ls : 1; /* load(1)/store(0) */ + arminstr_t wb : 1; /* write-back "!" */ + arminstr_t s : 1; /* restore PSR, force user bit */ + arminstr_t u : 1; /* up/down */ + arminstr_t p : 1; /* pre(1)/post(0) index */ + arminstr_t tag : 3; /* 1 0 0 */ + arminstr_t cond : 4; +} ARMInstrMRT; + +#define ARM_MRT_ID 4 +#define ARM_MRT_MASK 7 << 25 +#define ARM_MRT_TAG ARM_MRT_ID << 25 + +#define ARM_DEF_MRT(regs, rn, l, w, s, u, p, cond) \ + (regs) | \ + (rn << 16) | \ + (l << 20) | \ + (w << 21) | \ + (s << 22) | \ + (u << 23) | \ + (p << 24) | \ + (ARM_MRT_TAG) | \ + ARM_DEF_COND(cond) + + +#define ARM_LDM(p, base, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, base, 1, 0, 0, 1, 0, ARMCOND_AL)) +#define ARM_STM(p, base, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, base, 0, 0, 0, 1, 0, ARMCOND_AL)) + +/* stmdb sp!, {regs} */ +#define ARM_PUSH(p, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, ARMREG_SP, 0, 1, 0, 0, 1, ARMCOND_AL)) +#define ARM_IASM_PUSH(regs) ARM_IASM(ARM_DEF_MRT(regs, ARMREG_SP, 0, 1, 0, 0, 1, ARMCOND_AL)) + +/* ldmia sp!, {regs} */ +#define ARM_POP(p, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, ARMREG_SP, 1, 1, 0, 1, 0, ARMCOND_AL)) +#define ARM_IASM_POP(regs) ARM_IASM_EMIT(ARM_DEF_MRT(regs, ARMREG_SP, 1, 1, 0, 1, 0, ARMCOND_AL)) + +/* ldmia sp, {regs} ; (no write-back) */ +#define ARM_POP_NWB(p, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, ARMREG_SP, 1, 0, 0, 1, 0, ARMCOND_AL)) +#define ARM_IASM_POP_NWB(regs) ARM_IASM_EMIT(ARM_DEF_MRT(regs, ARMREG_SP, 1, 0, 0, 1, 0, ARMCOND_AL)) + +#define ARM_PUSH1(p, r1) ARM_PUSH(p, (1 << r1)) +#define ARM_PUSH2(p, r1, r2) ARM_PUSH(p, (1 << r1) | (1 << r2)) +#define ARM_PUSH3(p, r1, r2, r3) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3)) +#define ARM_PUSH4(p, r1, r2, r3, r4) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4)) +#define ARM_PUSH5(p, r1, r2, r3, r4, r5) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5)) +#define ARM_PUSH6(p, r1, r2, r3, r4, r5, r6) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6)) +#define ARM_PUSH7(p, r1, r2, r3, r4, r5, r6, r7) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6) | (1 << r7)) +#define ARM_PUSH8(p, r1, r2, r3, r4, r5, r6, r7, r8) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6) | (1 << r7) | (1 << r8)) + +#define ARM_POP8(p, r1, r2, r3, r4, r5, r6, r7, r8) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6) | (1 << r7) | (1 << r8)) +#define ARM_POP7(p, r1, r2, r3, r4, r5, r6, r7) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6) | (1 << r7)) +#define ARM_POP6(p, r1, r2, r3, r4, r5, r6) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6)) +#define ARM_POP5(p, r1, r2, r3, r4, r5) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5)) +#define ARM_POP4(p, r1, r2, r3, r4) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4)) +#define ARM_POP3(p, r1, r2, r3) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3)) +#define ARM_POP2(p, r1, r2) ARM_POP(p, (1 << r1) | (1 << r2)) +#define ARM_POP1(p, r1) ARM_POP(p, (1 << r1)) + + +/* Multiply instructions */ +typedef struct { + arminstr_t rm : 4; + arminstr_t tag2 : 4; /* 9 */ + arminstr_t rs : 4; + arminstr_t rn : 4; + arminstr_t rd : 4; + arminstr_t s : 1; + arminstr_t opcode : 3; + arminstr_t tag : 4; + arminstr_t cond : 4; +} ARMInstrMul; + +#define ARM_MUL_ID 0 +#define ARM_MUL_ID2 9 +#define ARM_MUL_MASK ((0xF << 24) | (0xF << 4)) +#define ARM_MUL_TAG ((ARM_MUL_ID << 24) | (ARM_MUL_ID2 << 4)) + +#define ARM_DEF_MUL_COND(op, rd, rm, rs, rn, s, cond) \ + (rm) | \ + ((rs) << 8) | \ + ((rn) << 12) | \ + ((rd) << 16) | \ + ((s & 1) << 17) | \ + ((op & 7) << 18) | \ + ARM_MUL_TAG | \ + ARM_DEF_COND(cond) + +/* Rd := (Rm * Rs)[31:0]; 32 x 32 -> 32 */ +#define ARM_MUL_COND(p, rd, rm, rs, cond) \ + ARM_EMIT(p, ARM_DEF_MUL_COND(ARMOP_MUL, rd, rm, rs, 0, 0, cond)) +#define ARM_MUL(p, rd, rm, rs) \ + ARM_MUL_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_MULS_COND(p, rd, rm, rs, cond) \ + ARM_EMIT(p, ARM_DEF_MUL_COND(ARMOP_MUL, rd, rm, rs, 0, 1, cond)) +#define ARM_MULS(p, rd, rm, rs) \ + ARM_MULS_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_MUL_REG_REG(p, rd, rm, rs) ARM_MUL(p, rd, rm, rs) +#define ARM_MULS_REG_REG(p, rd, rm, rs) ARM_MULS(p, rd, rm, rs) + +/* inline */ +#define ARM_IASM_MUL_COND(rd, rm, rs, cond) \ + ARM_IASM_EMIT(ARM_DEF_MUL_COND(ARMOP_MUL, rd, rm, rs, 0, 0, cond)) +#define ARM_IASM_MUL(rd, rm, rs) \ + ARM_IASM_MUL_COND(rd, rm, rs, ARMCOND_AL) +#define ARM_IASM_MULS_COND(rd, rm, rs, cond) \ + ARM_IASM_EMIT(ARM_DEF_MUL_COND(ARMOP_MUL, rd, rm, rs, 0, 1, cond)) +#define ARM_IASM_MULS(rd, rm, rs) \ + ARM_IASM_MULS_COND(rd, rm, rs, ARMCOND_AL) + + +/* Rd := (Rm * Rs) + Rn; 32x32+32->32 */ +#define ARM_MLA_COND(p, rd, rm, rs, rn, cond) \ + ARM_EMIT(p, ARM_DEF_MUL_COND(ARMOP_MLA, rd, rm, rs, rn, 0, cond)) +#define ARM_MLA(p, rd, rm, rs, rn) \ + ARM_MLA_COND(p, rd, rm, rs, rn, ARMCOND_AL) +#define ARM_MLAS_COND(p, rd, rm, rs, rn, cond) \ + ARM_EMIT(p, ARM_DEF_MUL_COND(ARMOP_MLA, rd, rm, rs, rn, 1, cond)) +#define ARM_MLAS(p, rd, rm, rs, rn) \ + ARM_MLAS_COND(p, rd, rm, rs, rn, ARMCOND_AL) + +/* inline */ +#define ARM_IASM_MLA_COND(rd, rm, rs, rn, cond) \ + ARM_IASM_EMIT(ARM_DEF_MUL_COND(ARMOP_MLA, rd, rm, rs, rn, 0, cond)) +#define ARM_IASM_MLA(rd, rm, rs, rn) \ + ARM_IASM_MLA_COND(rd, rm, rs, rn, ARMCOND_AL) +#define ARM_IASM_MLAS_COND(rd, rm, rs, rn, cond) \ + ARM_IASM_EMIT(ARM_DEF_MUL_COND(ARMOP_MLA, rd, rm, rs, rn, 1, cond)) +#define ARM_IASM_MLAS(rd, rm, rs, rn) \ + ARM_IASM_MLAS_COND(rd, rm, rs, rn, ARMCOND_AL) + + + +/* Word/byte transfer */ +typedef union { + ARMDPI_op2_reg_imm op2_reg_imm; + struct { + arminstr_t op2_imm : 12; + arminstr_t rd : 4; + arminstr_t rn : 4; + arminstr_t ls : 1; + arminstr_t wb : 1; + arminstr_t b : 1; + arminstr_t u : 1; /* down(0) / up(1) */ + arminstr_t p : 1; /* post-index(0) / pre-index(1) */ + arminstr_t type : 1; /* imm(0) / register(1) */ + arminstr_t tag : 2; /* 0 1 */ + arminstr_t cond : 4; + } all; +} ARMInstrWXfer; + +#define ARM_WXFER_ID 1 +#define ARM_WXFER_MASK 3 << 26 +#define ARM_WXFER_TAG ARM_WXFER_ID << 26 + + +#define ARM_DEF_WXFER_IMM(imm12, rd, rn, ls, wb, b, p, cond) \ + ((((int)imm12) < 0) ? -(int)(imm12) : (imm12)) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((ls) << 20) | \ + ((wb) << 21) | \ + ((b) << 22) | \ + (((int)(imm12) >= 0) << 23) | \ + ((p) << 24) | \ + ARM_WXFER_TAG | \ + ARM_DEF_COND(cond) + +#define ARM_WXFER_MAX_OFFS 0xFFF + +/* this macro checks for imm12 bounds */ +#define ARM_EMIT_WXFER_IMM(ptr, imm12, rd, rn, ls, wb, b, p, cond) \ + do { \ + int _imm12 = (int)(imm12) < -ARM_WXFER_MAX_OFFS \ + ? -ARM_WXFER_MAX_OFFS \ + : (int)(imm12) > ARM_WXFER_MAX_OFFS \ + ? ARM_WXFER_MAX_OFFS \ + : (int)(imm12); \ + ARM_EMIT((ptr), \ + ARM_DEF_WXFER_IMM(_imm12, (rd), (rn), (ls), (wb), (b), (p), (cond))); \ + } while (0) + + +/* LDRx */ +/* immediate offset, post-index */ +#define ARM_LDR_IMM_POST_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_LDR, 0, 0, 0, cond)) + +#define ARM_LDR_IMM_POST(p, rd, rn, imm) ARM_LDR_IMM_POST_COND(p, rd, rn, imm, ARMCOND_AL) + +#define ARM_LDRB_IMM_POST_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_LDR, 0, 1, 0, cond)) + +#define ARM_LDRB_IMM_POST(p, rd, rn, imm) ARM_LDRB_IMM_POST_COND(p, rd, rn, imm, ARMCOND_AL) + +/* immediate offset, pre-index */ +#define ARM_LDR_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_LDR, 0, 0, 1, cond)) + +#define ARM_LDR_IMM(p, rd, rn, imm) ARM_LDR_IMM_COND(p, rd, rn, imm, ARMCOND_AL) + +#define ARM_LDRB_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_LDR, 0, 1, 1, cond)) + +#define ARM_LDRB_IMM(p, rd, rn, imm) ARM_LDRB_IMM_COND(p, rd, rn, imm, ARMCOND_AL) + +/* STRx */ +/* immediate offset, post-index */ +#define ARM_STR_IMM_POST_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_STR, 0, 0, 0, cond)) + +#define ARM_STR_IMM_POST(p, rd, rn, imm) ARM_STR_IMM_POST_COND(p, rd, rn, imm, ARMCOND_AL) + +#define ARM_STRB_IMM_POST_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_STR, 0, 1, 0, cond)) + +#define ARM_STRB_IMM_POST(p, rd, rn, imm) ARM_STRB_IMM_POST_COND(p, rd, rn, imm, ARMCOND_AL) + +/* immediate offset, pre-index */ +#define ARM_STR_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT_WXFER_IMM(p, imm, rd, rn, ARMOP_STR, 0, 0, 1, cond) +/* ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_STR, 0, 0, 1, cond)) */ + +#define ARM_STR_IMM(p, rd, rn, imm) ARM_STR_IMM_COND(p, rd, rn, imm, ARMCOND_AL) + +#define ARM_STRB_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_STR, 0, 1, 1, cond)) + +#define ARM_STRB_IMM(p, rd, rn, imm) ARM_STRB_IMM_COND(p, rd, rn, imm, ARMCOND_AL) + +/* write-back */ +#define ARM_STR_IMM_WB_COND(p, rd, rn, imm, cond) \ + ARM_EMIT_WXFER_IMM(p, imm, rd, rn, ARMOP_STR, 1, 0, 1, cond) +#define ARM_STR_IMM_WB(p, rd, rn, imm) ARM_STR_IMM_WB_COND(p, rd, rn, imm, ARMCOND_AL) + + +#define ARM_DEF_WXFER_REG_REG_UPDOWN_COND(rm, shift_type, shift, rd, rn, ls, wb, b, u, p, cond) \ + (rm) | \ + ((shift_type) << 5) | \ + ((shift) << 7) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((ls) << 20) | \ + ((wb) << 21) | \ + ((b) << 22) | \ + ((u) << 23) | \ + ((p) << 24) | \ + (1 << 25) | \ + ARM_WXFER_TAG | \ + ARM_DEF_COND(cond) + +#define ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ls, wb, b, p, cond) \ + ARM_DEF_WXFER_REG_REG_UPDOWN_COND(rm, shift_type, shift, rd, rn, ls, wb, b, ARM_UP, p, cond) +#define ARM_DEF_WXFER_REG_MINUS_REG_COND(rm, shift_type, shift, rd, rn, ls, wb, b, p, cond) \ + ARM_DEF_WXFER_REG_REG_UPDOWN_COND(rm, shift_type, shift, rd, rn, ls, wb, b, ARM_DOWN, p, cond) + + +#define ARM_LDR_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ARMOP_LDR, 0, 0, 1, cond)) +#define ARM_LDR_REG_REG_SHIFT(p, rd, rn, rm, shift_type, shift) \ + ARM_LDR_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, ARMCOND_AL) +#define ARM_LDR_REG_REG(p, rd, rn, rm) \ + ARM_LDR_REG_REG_SHIFT(p, rd, rn, rm, ARMSHIFT_LSL, 0) + +#define ARM_LDRB_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ARMOP_LDR, 0, 1, 1, cond)) +#define ARM_LDRB_REG_REG_SHIFT(p, rd, rn, rm, shift_type, shift) \ + ARM_LDRB_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, ARMCOND_AL) +#define ARM_LDRB_REG_REG(p, rd, rn, rm) \ + ARM_LDRB_REG_REG_SHIFT(p, rd, rn, rm, ARMSHIFT_LSL, 0) + +#define ARM_STR_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ARMOP_STR, 0, 0, 1, cond)) +#define ARM_STR_REG_REG_SHIFT(p, rd, rn, rm, shift_type, shift) \ + ARM_STR_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, ARMCOND_AL) +#define ARM_STR_REG_REG(p, rd, rn, rm) \ + ARM_STR_REG_REG_SHIFT(p, rd, rn, rm, ARMSHIFT_LSL, 0) + +/* zero-extend */ +#define ARM_STRB_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, cond) \ + ARM_EMIT(p, ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ARMOP_STR, 0, 1, 1, cond)) +#define ARM_STRB_REG_REG_SHIFT(p, rd, rn, rm, shift_type, shift) \ + ARM_STRB_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, ARMCOND_AL) +#define ARM_STRB_REG_REG(p, rd, rn, rm) \ + ARM_STRB_REG_REG_SHIFT(p, rd, rn, rm, ARMSHIFT_LSL, 0) + + +/* ARMv4+ */ +/* Half-word or byte (signed) transfer. */ +typedef struct { + arminstr_t rm : 4; /* imm_lo */ + arminstr_t tag3 : 1; /* 1 */ + arminstr_t h : 1; /* half-word or byte */ + arminstr_t s : 1; /* sign-extend or zero-extend */ + arminstr_t tag2 : 1; /* 1 */ + arminstr_t imm_hi : 4; + arminstr_t rd : 4; + arminstr_t rn : 4; + arminstr_t ls : 1; + arminstr_t wb : 1; + arminstr_t type : 1; /* imm(1) / reg(0) */ + arminstr_t u : 1; /* +- */ + arminstr_t p : 1; /* pre/post-index */ + arminstr_t tag : 3; + arminstr_t cond : 4; +} ARMInstrHXfer; + +#define ARM_HXFER_ID 0 +#define ARM_HXFER_ID2 1 +#define ARM_HXFER_ID3 1 +#define ARM_HXFER_MASK ((0x7 << 25) | (0x9 << 4)) +#define ARM_HXFER_TAG ((ARM_HXFER_ID << 25) | (ARM_HXFER_ID2 << 7) | (ARM_HXFER_ID3 << 4)) + +#define ARM_DEF_HXFER_IMM_COND(imm, h, s, rd, rn, ls, wb, p, cond) \ + ((imm) < 0?(-(imm)) & 0xF:(imm) & 0xF) | \ + ((h) << 5) | \ + ((s) << 6) | \ + ((imm) < 0?((-(imm)) << 4) & 0xF00:((imm) << 4) & 0xF00) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((ls) << 20) | \ + ((wb) << 21) | \ + (1 << 22) | \ + (((int)(imm) >= 0) << 23) | \ + ((p) << 24) | \ + ARM_HXFER_TAG | \ + ARM_DEF_COND(cond) + +#define ARM_LDRH_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_IMM_COND(imm, 1, 0, rd, rn, ARMOP_LDR, 0, 1, cond)) +#define ARM_LDRH_IMM(p, rd, rn, imm) \ + ARM_LDRH_IMM_COND(p, rd, rn, imm, ARMCOND_AL) +#define ARM_LDRSH_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_IMM_COND(imm, 1, 1, rd, rn, ARMOP_LDR, 0, 1, cond)) +#define ARM_LDRSH_IMM(p, rd, rn, imm) \ + ARM_LDRSH_IMM_COND(p, rd, rn, imm, ARMCOND_AL) +#define ARM_LDRSB_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_IMM_COND(imm, 0, 1, rd, rn, ARMOP_LDR, 0, 1, cond)) +#define ARM_LDRSB_IMM(p, rd, rn, imm) \ + ARM_LDRSB_IMM_COND(p, rd, rn, imm, ARMCOND_AL) + + +#define ARM_STRH_IMM_COND(p, rd, rn, imm, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_IMM_COND(imm, 1, 0, rd, rn, ARMOP_STR, 0, 1, cond)) +#define ARM_STRH_IMM(p, rd, rn, imm) \ + ARM_STRH_IMM_COND(p, rd, rn, imm, ARMCOND_AL) + + +#define ARM_DEF_HXFER_REG_REG_UPDOWN_COND(rm, h, s, rd, rn, ls, wb, u, p, cond) \ + ((rm) & 0xF) | \ + ((h) << 5) | \ + ((s) << 6) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((ls) << 20) | \ + ((wb) << 21) | \ + (0 << 22) | \ + ((u) << 23) | \ + ((p) << 24) | \ + ARM_HXFER_TAG | \ + ARM_DEF_COND(cond) + +#define ARM_DEF_HXFER_REG_REG_COND(rm, h, s, rd, rn, ls, wb, p, cond) \ + ARM_DEF_HXFER_REG_REG_UPDOWN_COND(rm, h, s, rd, rn, ls, wb, ARM_UP, p, cond) +#define ARM_DEF_HXFER_REG_MINUS_REG_COND(rm, h, s, rd, rn, ls, wb, p, cond) \ + ARM_DEF_HXFER_REG_REG_UPDOWN_COND(rm, h, s, rd, rn, ls, wb, ARM_DOWN, p, cond) + +#define ARM_LDRH_REG_REG_COND(p, rd, rm, rn, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_REG_REG_COND(rm, 1, 0, rd, rn, ARMOP_LDR, 0, 1, cond)) +#define ARM_LDRH_REG_REG(p, rd, rm, rn) \ + ARM_LDRH_REG_REG_COND(p, rd, rm, rn, ARMCOND_AL) +#define ARM_LDRSH_REG_REG_COND(p, rd, rm, rn, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_REG_REG_COND(rm, 1, 1, rd, rn, ARMOP_LDR, 0, 1, cond)) +#define ARM_LDRSH_REG_REG(p, rd, rm, rn) \ + ARM_LDRSH_REG_REG_COND(p, rd, rm, rn, ARMCOND_AL) +#define ARM_LDRSB_REG_REG_COND(p, rd, rm, rn, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_REG_REG_COND(rm, 0, 1, rd, rn, ARMOP_LDR, 0, 1, cond)) +#define ARM_LDRSB_REG_REG(p, rd, rm, rn) ARM_LDRSB_REG_REG_COND(p, rd, rm, rn, ARMCOND_AL) + +#define ARM_STRH_REG_REG_COND(p, rd, rm, rn, cond) \ + ARM_EMIT(p, ARM_DEF_HXFER_REG_REG_COND(rm, 1, 0, rd, rn, ARMOP_STR, 0, 1, cond)) +#define ARM_STRH_REG_REG(p, rd, rm, rn) \ + ARM_STRH_REG_REG_COND(p, rd, rm, rn, ARMCOND_AL) + + + +/* Swap */ +typedef struct { + arminstr_t rm : 4; + arminstr_t tag3 : 8; /* 0x9 */ + arminstr_t rd : 4; + arminstr_t rn : 4; + arminstr_t tag2 : 2; + arminstr_t b : 1; + arminstr_t tag : 5; /* 0x2 */ + arminstr_t cond : 4; +} ARMInstrSwap; + +#define ARM_SWP_ID 2 +#define ARM_SWP_ID2 9 +#define ARM_SWP_MASK ((0x1F << 23) | (3 << 20) | (0xFF << 4)) +#define ARM_SWP_TAG ((ARM_SWP_ID << 23) | (ARM_SWP_ID2 << 4)) + + + +/* Software interrupt */ +typedef struct { + arminstr_t num : 24; + arminstr_t tag : 4; + arminstr_t cond : 4; +} ARMInstrSWI; + +#define ARM_SWI_ID 0xF +#define ARM_SWI_MASK (0xF << 24) +#define ARM_SWI_TAG (ARM_SWI_ID << 24) + + + +/* Co-processor Data Processing */ +typedef struct { + arminstr_t crm : 4; + arminstr_t tag2 : 1; /* 0 */ + arminstr_t op2 : 3; + arminstr_t cpn : 4; /* CP number */ + arminstr_t crd : 4; + arminstr_t crn : 4; + arminstr_t op : 4; + arminstr_t tag : 4; /* 0xE */ + arminstr_t cond : 4; +} ARMInstrCDP; + +#define ARM_CDP_ID 0xE +#define ARM_CDP_ID2 0 +#define ARM_CDP_MASK ((0xF << 24) | (1 << 4)) +#define ARM_CDP_TAG ((ARM_CDP_ID << 24) | (ARM_CDP_ID2 << 4)) + + +/* Co-processor Data Transfer (ldc/stc) */ +typedef struct { + arminstr_t offs : 8; + arminstr_t cpn : 4; + arminstr_t crd : 4; + arminstr_t rn : 4; + arminstr_t ls : 1; + arminstr_t wb : 1; + arminstr_t n : 1; + arminstr_t u : 1; + arminstr_t p : 1; + arminstr_t tag : 3; + arminstr_t cond : 4; +} ARMInstrCDT; + +#define ARM_CDT_ID 6 +#define ARM_CDT_MASK (7 << 25) +#define ARM_CDT_TAG (ARM_CDT_ID << 25) + + +/* Co-processor Register Transfer (mcr/mrc) */ +typedef struct { + arminstr_t crm : 4; + arminstr_t tag2 : 1; + arminstr_t op2 : 3; + arminstr_t cpn : 4; + arminstr_t rd : 4; + arminstr_t crn : 4; + arminstr_t ls : 1; + arminstr_t op1 : 3; + arminstr_t tag : 4; + arminstr_t cond : 4; +} ARMInstrCRT; + +#define ARM_CRT_ID 0xE +#define ARM_CRT_ID2 0x1 +#define ARM_CRT_MASK ((0xF << 24) | (1 << 4)) +#define ARM_CRT_TAG ((ARM_CRT_ID << 24) | (ARM_CRT_ID2 << 4)) + +/* Move register to PSR. */ +typedef union { + ARMDPI_op2_imm op2_imm; + struct { + arminstr_t rm : 4; + arminstr_t pad : 8; /* 0 */ + arminstr_t tag4 : 4; /* 0xF */ + arminstr_t fld : 4; + arminstr_t tag3 : 2; /* 0x2 */ + arminstr_t sel : 1; + arminstr_t tag2 : 2; /* 0x2 */ + arminstr_t type : 1; + arminstr_t tag : 2; /* 0 */ + arminstr_t cond : 4; + } all; +} ARMInstrMSR; + +#define ARM_MSR_ID 0 +#define ARM_MSR_ID2 2 +#define ARM_MSR_ID3 2 +#define ARM_MSR_ID4 0xF +#define ARM_MSR_MASK ((3 << 26) | \ + (3 << 23) | \ + (3 << 20) | \ + (0xF << 12)) +#define ARM_MSR_TAG ((ARM_MSR_ID << 26) | \ + (ARM_MSR_ID2 << 23) | \ + (ARM_MSR_ID3 << 20) | \ + (ARM_MSR_ID4 << 12)) + + +/* Move PSR to register. */ +typedef struct { + arminstr_t tag3 : 12; + arminstr_t rd : 4; + arminstr_t tag2 : 6; + arminstr_t sel : 1; /* CPSR | SPSR */ + arminstr_t tag : 5; + arminstr_t cond : 4; +} ARMInstrMRS; + +#define ARM_MRS_ID 2 +#define ARM_MRS_ID2 0xF +#define ARM_MRS_ID3 0 +#define ARM_MRS_MASK ((0x1F << 23) | (0x3F << 16) | 0xFFF) +#define ARM_MRS_TAG ((ARM_MRS_ID << 23) | (ARM_MRS_ID2 << 16) | ARM_MRS_ID3) + + + +#include "arm_dpimacros.h" + +#define ARM_NOP(p) ARM_MOV_REG_REG(p, ARMREG_R0, ARMREG_R0) + + +#define ARM_SHL_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_LSL, imm, cond) +#define ARM_SHL_IMM(p, rd, rm, imm) \ + ARM_SHL_IMM_COND(p, rd, rm, imm, ARMCOND_AL) +#define ARM_SHLS_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_LSL, imm, cond) +#define ARM_SHLS_IMM(p, rd, rm, imm) \ + ARM_SHLS_IMM_COND(p, rd, rm, imm, ARMCOND_AL) + +#define ARM_SHR_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_LSR, imm, cond) +#define ARM_SHR_IMM(p, rd, rm, imm) \ + ARM_SHR_IMM_COND(p, rd, rm, imm, ARMCOND_AL) +#define ARM_SHRS_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_LSR, imm, cond) +#define ARM_SHRS_IMM(p, rd, rm, imm) \ + ARM_SHRS_IMM_COND(p, rd, rm, imm, ARMCOND_AL) + +#define ARM_SAR_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_ASR, imm, cond) +#define ARM_SAR_IMM(p, rd, rm, imm) \ + ARM_SAR_IMM_COND(p, rd, rm, imm, ARMCOND_AL) +#define ARM_SARS_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_ASR, imm, cond) +#define ARM_SARS_IMM(p, rd, rm, imm) \ + ARM_SARS_IMM_COND(p, rd, rm, imm, ARMCOND_AL) + +#define ARM_ROR_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_ROR, imm, cond) +#define ARM_ROR_IMM(p, rd, rm, imm) \ + ARM_ROR_IMM_COND(p, rd, rm, imm, ARMCOND_AL) +#define ARM_RORS_IMM_COND(p, rd, rm, imm, cond) \ + ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_ROR, imm, cond) +#define ARM_RORS_IMM(p, rd, rm, imm) \ + ARM_RORS_IMM_COND(p, rd, rm, imm, ARMCOND_AL) + +#define ARM_SHL_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_LSL, rs, cond) +#define ARM_SHL_REG(p, rd, rm, rs) \ + ARM_SHL_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_SHLS_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_LSL, rs, cond) +#define ARM_SHLS_REG(p, rd, rm, rs) \ + ARM_SHLS_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_SHLS_REG_REG(p, rd, rm, rs) ARM_SHLS_REG(p, rd, rm, rs) + +#define ARM_SHR_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_LSR, rs, cond) +#define ARM_SHR_REG(p, rd, rm, rs) \ + ARM_SHR_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_SHRS_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_LSR, rs, cond) +#define ARM_SHRS_REG(p, rd, rm, rs) \ + ARM_SHRS_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_SHRS_REG_REG(p, rd, rm, rs) ARM_SHRS_REG(p, rd, rm, rs) + +#define ARM_SAR_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_ASR, rs, cond) +#define ARM_SAR_REG(p, rd, rm, rs) \ + ARM_SAR_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_SARS_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_ASR, rs, cond) +#define ARM_SARS_REG(p, rd, rm, rs) \ + ARM_SARS_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_SARS_REG_REG(p, rd, rm, rs) ARM_SARS_REG(p, rd, rm, rs) + +#define ARM_ROR_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_ROR, rs, cond) +#define ARM_ROR_REG(p, rd, rm, rs) \ + ARM_ROR_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_RORS_REG_COND(p, rd, rm, rs, cond) \ + ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_ROR, rs, cond) +#define ARM_RORS_REG(p, rd, rm, rs) \ + ARM_RORS_REG_COND(p, rd, rm, rs, ARMCOND_AL) +#define ARM_RORS_REG_REG(p, rd, rm, rs) ARM_RORS_REG(p, rd, rm, rs) + +#define ARM_DBRK(p) ARM_EMIT(p, 0xE6000010) +#define ARM_IASM_DBRK() ARM_IASM_EMIT(0xE6000010) + +#define ARM_INC(p, reg) ARM_ADD_REG_IMM8(p, reg, reg, 1) +#define ARM_DEC(p, reg) ARM_SUB_REG_IMM8(p, reg, reg, 1) + + +/* ARM V5 */ + +/* Count leading zeros, CLZ{cond} Rd, Rm */ +typedef struct { + arminstr_t rm : 4; + arminstr_t tag2 : 8; + arminstr_t rd : 4; + arminstr_t tag : 12; + arminstr_t cond : 4; +} ARMInstrCLZ; + +#define ARM_CLZ_ID 0x16F +#define ARM_CLZ_ID2 0xF1 +#define ARM_CLZ_MASK ((0xFFF << 16) | (0xFF < 4)) +#define ARM_CLZ_TAG ((ARM_CLZ_ID << 16) | (ARM_CLZ_ID2 << 4)) + + + + +typedef union { + ARMInstrBR br; + ARMInstrDPI dpi; + ARMInstrMRT mrt; + ARMInstrMul mul; + ARMInstrWXfer wxfer; + ARMInstrHXfer hxfer; + ARMInstrSwap swp; + ARMInstrCDP cdp; + ARMInstrCDT cdt; + ARMInstrCRT crt; + ARMInstrSWI swi; + ARMInstrMSR msr; + ARMInstrMRS mrs; + ARMInstrCLZ clz; + + ARMInstrGeneric generic; + arminstr_t raw; +} ARMInstr; + +/* ARMv6t2 */ + +#define ARM_MOVW_REG_IMM_COND(p, rd, imm16, cond) ARM_EMIT(p, (((cond) << 28) | (3 << 24) | (0 << 20) | ((((guint32)(imm16)) >> 12) << 16) | ((rd) << 12) | (((guint32)(imm16)) & 0xfff))) +#define ARM_MOVW_REG_IMM(p, rd, imm16) ARM_MOVW_REG_IMM_COND ((p), (rd), (imm16), ARMCOND_AL) + +#define ARM_MOVT_REG_IMM_COND(p, rd, imm16, cond) ARM_EMIT(p, (((cond) << 28) | (3 << 24) | (4 << 20) | ((((guint32)(imm16)) >> 12) << 16) | ((rd) << 12) | (((guint32)(imm16)) & 0xfff))) +#define ARM_MOVT_REG_IMM(p, rd, imm16) ARM_MOVT_REG_IMM_COND ((p), (rd), (imm16), ARMCOND_AL) + +/* MCR */ +#define ARM_DEF_MCR_COND(coproc, opc1, rt, crn, crm, opc2, cond) \ + ARM_DEF_COND ((cond)) | ((0xe << 24) | (((opc1) & 0x7) << 21) | (0 << 20) | (((crn) & 0xf) << 16) | (((rt) & 0xf) << 12) | (((coproc) & 0xf) << 8) | (((opc2) & 0x7) << 5) | (1 << 4) | (((crm) & 0xf) << 0)) + +#define ARM_MCR_COND(p, coproc, opc1, rt, crn, crm, opc2, cond) \ + ARM_EMIT(p, ARM_DEF_MCR_COND ((coproc), (opc1), (rt), (crn), (crm), (opc2), (cond))) + +#define ARM_MCR(p, coproc, opc1, rt, crn, crm, opc2) \ + ARM_MCR_COND ((p), (coproc), (opc1), (rt), (crn), (crm), (opc2), ARMCOND_AL) + +#ifdef __cplusplus +} +#endif + +#endif /* ARM_H */ + diff --git a/asm/arm-dis.c b/asm/arm-dis.c new file mode 100644 index 0000000000..5074f260d8 --- /dev/null +++ b/asm/arm-dis.c @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2002 Sergey Chaban + */ + + +#include + +#include "arm-dis.h" +#include "arm-codegen.h" + + +static ARMDis* gdisasm = NULL; + +static int use_reg_alias = 1; + +const static char* cond[] = { + "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", "gt", "le", "", "nv" +}; + +const static char* ops[] = { + "and", "eor", "sub", "rsb", "add", "adc", "sbc", "rsc", + "tst", "teq", "cmp", "cmn", "orr", "mov", "bic", "mvn" +}; + +const static char* shift_types[] = {"lsl", "lsr", "asr", "ror"}; + +const static char* mul_ops[] = { + "mul", "mla", "?", "?", "umull", "umlal", "smull", "smlal" +}; + +const static char* reg_alias[] = { + "a1", "a2", "a3", "a4", + "r4", "r5", "r6", "r7", "r8", "r9", "r10", + "fp", "ip", "sp", "lr", "pc" +}; + +const static char* msr_fld[] = {"f", "c", "x", "?", "s"}; + + +/* private functions prototypes (to keep compiler happy) */ +void chk_out(ARMDis* dis); +void dump_reg(ARMDis* dis, int reg); +void dump_creg(ARMDis* dis, int creg); +void dump_reglist(ARMDis* dis, int reg_list); +void init_gdisasm(void); + +void dump_br(ARMDis* dis, ARMInstr i); +void dump_cdp(ARMDis* dis, ARMInstr i); +void dump_cdt(ARMDis* dis, ARMInstr i); +void dump_crt(ARMDis* dis, ARMInstr i); +void dump_dpi(ARMDis* dis, ARMInstr i); +void dump_hxfer(ARMDis* dis, ARMInstr i); +void dump_mrs(ARMDis* dis, ARMInstr i); +void dump_mrt(ARMDis* dis, ARMInstr i); +void dump_msr(ARMDis* dis, ARMInstr i); +void dump_mul(ARMDis* dis, ARMInstr i); +void dump_swi(ARMDis* dis, ARMInstr i); +void dump_swp(ARMDis* dis, ARMInstr i); +void dump_wxfer(ARMDis* dis, ARMInstr i); +void dump_clz(ARMDis* dis, ARMInstr i); + + +/* +void out(ARMDis* dis, const char* format, ...) { + va_list arglist; + va_start(arglist, format); + fprintf(dis->dis_out, format, arglist); + va_end(arglist); +} +*/ + + +void chk_out(ARMDis* dis) { + if (dis != NULL && dis->dis_out == NULL) dis->dis_out = stdout; +} + + +void armdis_set_output(ARMDis* dis, FILE* f) { + if (dis != NULL) { + dis->dis_out = f; + chk_out(dis); + } +} + +FILE* armdis_get_output(ARMDis* dis) { + return (dis != NULL ? dis->dis_out : NULL); +} + + + + +void dump_reg(ARMDis* dis, int reg) { + reg &= 0xF; + if (!use_reg_alias || (reg > 3 && reg < 11)) { + fprintf(dis->dis_out, "r%d", reg); + } else { + fprintf(dis->dis_out, "%s", reg_alias[reg]); + } +} + +void dump_creg(ARMDis* dis, int creg) { + if (dis != NULL) { + creg &= 0xF; + fprintf(dis->dis_out, "c%d", creg); + } +} + +void dump_reglist(ARMDis* dis, int reg_list) { + int i = 0, j, n = 0; + int m1 = 1, m2, rn; + while (i < 16) { + if ((reg_list & m1) != 0) { + if (n != 0) fprintf(dis->dis_out, ", "); + n++; + dump_reg(dis, i); + for (j = i+1, rn = 0, m2 = m1<<1; j < 16; ++j, m2<<=1) { + if ((reg_list & m2) != 0) ++rn; + else break; + } + i+=rn; + if (rn > 1) { + fprintf(dis->dis_out, "-"); + dump_reg(dis, i); + } else if (rn == 1) { + fprintf(dis->dis_out, ", "); + dump_reg(dis, i); + } + m1<<=(rn+1); + i++; + } else { + ++i; + m1<<=1; + } + } +} + + +void dump_br(ARMDis* dis, ARMInstr i) { + fprintf(dis->dis_out, "b%s%s\t%x\t; %p -> %#x", + (i.br.link == 1) ? "l" : "", + cond[i.br.cond], i.br.offset, dis->pi, (int)dis->pi + 4*2 + ((int)(i.br.offset << 8) >> 6)); +} + + +void dump_dpi(ARMDis* dis, ARMInstr i) { + fprintf(dis->dis_out, "%s%s", ops[i.dpi.all.opcode], cond[i.dpi.all.cond]); + + if ((i.dpi.all.opcode < ARMOP_TST || i.dpi.all.opcode > ARMOP_CMN) && (i.dpi.all.s != 0)) { + fprintf(dis->dis_out, "s"); + } + + fprintf(dis->dis_out, "\t"); + + if ((i.dpi.all.opcode < ARMOP_TST) || (i.dpi.all.opcode > ARMOP_CMN)) { + /* for comparison operations Rd is ignored */ + dump_reg(dis, i.dpi.all.rd); + fprintf(dis->dis_out, ", "); + } + + if ((i.dpi.all.opcode != ARMOP_MOV) && (i.dpi.all.opcode != ARMOP_MVN)) { + /* for MOV/MVN Rn is ignored */ + dump_reg(dis, i.dpi.all.rn); + fprintf(dis->dis_out, ", "); + } + + if (i.dpi.all.type == 1) { + /* immediate */ + if (i.dpi.op2_imm.rot != 0) { + fprintf(dis->dis_out, "#%d, %d\t; 0x%x", i.dpi.op2_imm.imm, i.dpi.op2_imm.rot << 1, + ARM_SCALE(i.dpi.op2_imm.imm, (i.dpi.op2_imm.rot << 1)) ); + } else { + fprintf(dis->dis_out, "#%d\t; 0x%x", i.dpi.op2_imm.imm, i.dpi.op2_imm.imm); + } + } else { + /* reg-reg */ + if (i.dpi.op2_reg.tag == 0) { + /* op2 is reg shift by imm */ + dump_reg(dis, i.dpi.op2_reg_imm.r2.rm); + if (i.dpi.op2_reg_imm.imm.shift != 0) { + fprintf(dis->dis_out, " %s #%d", shift_types[i.dpi.op2_reg_imm.r2.type], i.dpi.op2_reg_imm.imm.shift); + } + } else { + /* op2 is reg shift by reg */ + dump_reg(dis, i.dpi.op2_reg_reg.r2.rm); + fprintf(dis->dis_out, " %s ", shift_types[i.dpi.op2_reg_reg.r2.type]); + dump_reg(dis, i.dpi.op2_reg_reg.reg.rs); + } + + } +} + +void dump_wxfer(ARMDis* dis, ARMInstr i) { + fprintf(dis->dis_out, "%s%s%s%s\t", + (i.wxfer.all.ls == 0) ? "str" : "ldr", + cond[i.generic.cond], + (i.wxfer.all.b == 0) ? "" : "b", + (i.wxfer.all.ls != 0 && i.wxfer.all.wb != 0) ? "t" : ""); + dump_reg(dis, i.wxfer.all.rd); + fprintf(dis->dis_out, ", ["); + dump_reg(dis, i.wxfer.all.rn); + fprintf(dis->dis_out, "%s, ", (i.wxfer.all.p == 0) ? "]" : ""); + + if (i.wxfer.all.type == 0) { /* imm */ + fprintf(dis->dis_out, "#%s%d", (i.wxfer.all.u == 0) ? "-" : "", i.wxfer.all.op2_imm); + } else { + dump_reg(dis, i.wxfer.op2_reg_imm.r2.rm); + if (i.wxfer.op2_reg_imm.imm.shift != 0) { + fprintf(dis->dis_out, " %s #%d", shift_types[i.wxfer.op2_reg_imm.r2.type], i.wxfer.op2_reg_imm.imm.shift); + } + } + + if (i.wxfer.all.p != 0) { + /* close pre-index instr, also check for write-back */ + fprintf(dis->dis_out, "]%s", (i.wxfer.all.wb != 0) ? "!" : ""); + } +} + +void dump_hxfer(ARMDis* dis, ARMInstr i) { + fprintf(dis->dis_out, "%s%s%s%s\t", + (i.hxfer.ls == 0) ? "str" : "ldr", + cond[i.generic.cond], + (i.hxfer.s != 0) ? "s" : "", + (i.hxfer.h != 0) ? "h" : "b"); + dump_reg(dis, i.hxfer.rd); + fprintf(dis->dis_out, ", ["); + dump_reg(dis, i.hxfer.rn); + fprintf(dis->dis_out, "%s, ", (i.hxfer.p == 0) ? "]" : ""); + + if (i.hxfer.type != 0) { /* imm */ + fprintf(dis->dis_out, "#%s%d", (i.hxfer.u == 0) ? "-" : "", (i.hxfer.imm_hi << 4) | i.hxfer.rm); + } else { + dump_reg(dis, i.hxfer.rm); + } + + if (i.hxfer.p != 0) { + /* close pre-index instr, also check for write-back */ + fprintf(dis->dis_out, "]%s", (i.hxfer.wb != 0) ? "!" : ""); + } +} + + +void dump_mrt(ARMDis* dis, ARMInstr i) { + fprintf(dis->dis_out, "%s%s%s%s\t", (i.mrt.ls == 0) ? "stm" : "ldm", cond[i.mrt.cond], + (i.mrt.u == 0) ? "d" : "i", (i.mrt.p == 0) ? "a" : "b"); + dump_reg(dis, i.mrt.rn); + fprintf(dis->dis_out, "%s, {", (i.mrt.wb != 0) ? "!" : ""); + dump_reglist(dis, i.mrt.reg_list); + fprintf(dis->dis_out, "}"); +} + + +void dump_swp(ARMDis* dis, ARMInstr i) { + fprintf(dis->dis_out, "swp%s%s ", cond[i.swp.cond], (i.swp.b != 0) ? "b" : ""); + dump_reg(dis, i.swp.rd); + fprintf(dis->dis_out, ", "); + dump_reg(dis, i.swp.rm); + fprintf(dis->dis_out, ", ["); + dump_reg(dis, i.swp.rn); + fprintf(dis->dis_out, "]"); +} + + +void dump_mul(ARMDis* dis, ARMInstr i) { + fprintf(dis->dis_out, "%s%s%s\t", mul_ops[i.mul.opcode], cond[i.mul.cond], (i.mul.s != 0) ? "s" : ""); + switch (i.mul.opcode) { + case ARMOP_MUL: + dump_reg(dis, i.mul.rd); + fprintf(dis->dis_out, ", "); + dump_reg(dis, i.mul.rm); + fprintf(dis->dis_out, ", "); + dump_reg(dis, i.mul.rs); + break; + case ARMOP_MLA: + dump_reg(dis, i.mul.rd); + fprintf(dis->dis_out, ", "); + dump_reg(dis, i.mul.rm); + fprintf(dis->dis_out, ", "); + dump_reg(dis, i.mul.rs); + fprintf(dis->dis_out, ", "); + dump_reg(dis, i.mul.rn); + break; + case ARMOP_UMULL: + case ARMOP_UMLAL: + case ARMOP_SMULL: + case ARMOP_SMLAL: + dump_reg(dis, i.mul.rd); + fprintf(dis->dis_out, ", "); + dump_reg(dis, i.mul.rn); + fprintf(dis->dis_out, ", "); + dump_reg(dis, i.mul.rm); + fprintf(dis->dis_out, ", "); + dump_reg(dis, i.mul.rs); + break; + default: + fprintf(dis->dis_out, "DCD 0x%x\t; ", i.raw); + break; + } +} + + +void dump_cdp(ARMDis* dis, ARMInstr i) { + fprintf(dis->dis_out, "cdp%s\tp%d, %d, ", cond[i.generic.cond], i.cdp.cpn, i.cdp.op); + dump_creg(dis, i.cdp.crd); + fprintf(dis->dis_out, ", "); + dump_creg(dis, i.cdp.crn); + fprintf(dis->dis_out, ", "); + dump_creg(dis, i.cdp.crm); + + if (i.cdp.op2 != 0) { + fprintf(dis->dis_out, ", %d", i.cdp.op2); + } +} + + +void dump_cdt(ARMDis* dis, ARMInstr i) { + fprintf(dis->dis_out, "%s%s%s\tp%d, ", (i.cdt.ls == 0) ? "stc" : "ldc", + cond[i.generic.cond], (i.cdt.n != 0) ? "l" : "", i.cdt.cpn); + dump_creg(dis, i.cdt.crd); + fprintf(dis->dis_out, ", "); + dump_reg(dis, i.cdt.rn); + + if (i.cdt.p == 0) { + fprintf(dis->dis_out, "]"); + } + + if (i.cdt.offs != 0) { + fprintf(dis->dis_out, ", #%d", i.cdt.offs); + } + + if (i.cdt.p != 0) { + fprintf(dis->dis_out, "]%s", (i.cdt.wb != 0) ? "!" : ""); + } +} + + +void dump_crt(ARMDis* dis, ARMInstr i) { + fprintf(dis->dis_out, "%s%s\tp%d, %d, ", (i.crt.ls == 0) ? "mrc" : "mcr", + cond[i.generic.cond], i.crt.cpn, i.crt.op1); + dump_reg(dis, i.crt.rd); + fprintf(dis->dis_out, ", "); + dump_creg(dis, i.crt.crn); + fprintf(dis->dis_out, ", "); + dump_creg(dis, i.crt.crm); + + if (i.crt.op2 != 0) { + fprintf(dis->dis_out, ", %d", i.crt.op2); + } +} + + +void dump_msr(ARMDis* dis, ARMInstr i) { + fprintf(dis->dis_out, "msr%s\t%spsr_, ", cond[i.generic.cond], + (i.msr.all.sel == 0) ? "s" : "c"); + if (i.msr.all.type == 0) { + /* reg */ + fprintf(dis->dis_out, "%s, ", msr_fld[i.msr.all.fld]); + dump_reg(dis, i.msr.all.rm); + } else { + /* imm */ + fprintf(dis->dis_out, "f, #%d", i.msr.op2_imm.imm << i.msr.op2_imm.rot); + } +} + + +void dump_mrs(ARMDis* dis, ARMInstr i) { + fprintf(dis->dis_out, "mrs%s\t", cond[i.generic.cond]); + dump_reg(dis, i.mrs.rd); + fprintf(dis->dis_out, ", %spsr", (i.mrs.sel == 0) ? "s" : "c"); +} + + +void dump_swi(ARMDis* dis, ARMInstr i) { + fprintf(dis->dis_out, "swi%s\t%d", cond[i.generic.cond], i.swi.num); +} + + +void dump_clz(ARMDis* dis, ARMInstr i) { + fprintf(dis->dis_out, "clz\t"); + dump_reg(dis, i.clz.rd); + fprintf(dis->dis_out, ", "); + dump_reg(dis, i.clz.rm); + fprintf(dis->dis_out, "\n"); +} + + + +void armdis_decode(ARMDis* dis, void* p, int size) { + int i; + arminstr_t* pi = (arminstr_t*)p; + ARMInstr instr; + + if (dis == NULL) return; + + chk_out(dis); + + size/=sizeof(arminstr_t); + + for (i=0; idis_out, "%p:\t%08x\t", pi, *pi); + dis->pi = pi; + instr.raw = *pi++; + + if ((instr.raw & ARM_BR_MASK) == ARM_BR_TAG) { + dump_br(dis, instr); + } else if ((instr.raw & ARM_SWP_MASK) == ARM_SWP_TAG) { + dump_swp(dis, instr); + } else if ((instr.raw & ARM_MUL_MASK) == ARM_MUL_TAG) { + dump_mul(dis, instr); + } else if ((instr.raw & ARM_CLZ_MASK) == ARM_CLZ_TAG) { + dump_clz(dis, instr); + } else if ((instr.raw & ARM_WXFER_MASK) == ARM_WXFER_TAG) { + dump_wxfer(dis, instr); + } else if ((instr.raw & ARM_HXFER_MASK) == ARM_HXFER_TAG) { + dump_hxfer(dis, instr); + } else if ((instr.raw & ARM_DPI_MASK) == ARM_DPI_TAG) { + dump_dpi(dis, instr); + } else if ((instr.raw & ARM_MRT_MASK) == ARM_MRT_TAG) { + dump_mrt(dis, instr); + } else if ((instr.raw & ARM_CDP_MASK) == ARM_CDP_TAG) { + dump_cdp(dis, instr); + } else if ((instr.raw & ARM_CDT_MASK) == ARM_CDT_TAG) { + dump_cdt(dis, instr); + } else if ((instr.raw & ARM_CRT_MASK) == ARM_CRT_TAG) { + dump_crt(dis, instr); + } else if ((instr.raw & ARM_MSR_MASK) == ARM_MSR_TAG) { + dump_msr(dis, instr); + } else if ((instr.raw & ARM_MRS_MASK) == ARM_MRS_TAG) { + dump_mrs(dis, instr); + } else if ((instr.raw & ARM_SWI_MASK) == ARM_SWI_TAG) { + dump_swi(dis, instr); + } else { + fprintf(dis->dis_out, "DCD 0x%x\t; ", instr.raw); + } + + fprintf(dis->dis_out, "\n"); + } +} + + +void armdis_open(ARMDis* dis, const char* dump_name) { + if (dis != NULL && dump_name != NULL) { + armdis_set_output(dis, fopen(dump_name, "w")); + } +} + + +void armdis_close(ARMDis* dis) { + if (dis->dis_out != NULL && dis->dis_out != stdout && dis->dis_out != stderr) { + fclose(dis->dis_out); + dis->dis_out = NULL; + } +} + + +void armdis_dump(ARMDis* dis, const char* dump_name, void* p, int size) { + armdis_open(dis, dump_name); + armdis_decode(dis, p, size); + armdis_close(dis); +} + + +void armdis_init(ARMDis* dis) { + if (dis != NULL) { + /* set to stdout */ + armdis_set_output(dis, NULL); + } +} + + + + +void init_gdisasm() { + if (gdisasm == NULL) { + gdisasm = (ARMDis*)malloc(sizeof(ARMDis)); + armdis_init(gdisasm); + } +} + +void _armdis_set_output(FILE* f) { + init_gdisasm(); + armdis_set_output(gdisasm, f); +} + +FILE* _armdis_get_output() { + init_gdisasm(); + return armdis_get_output(gdisasm); +} + +void _armdis_decode(void* p, int size) { + init_gdisasm(); + armdis_decode(gdisasm, p, size); +} + +void _armdis_open(const char* dump_name) { + init_gdisasm(); + armdis_open(gdisasm, dump_name); +} + +void _armdis_close() { + init_gdisasm(); + armdis_close(gdisasm); +} + +void _armdis_dump(const char* dump_name, void* p, int size) { + init_gdisasm(); + armdis_dump(gdisasm, dump_name, p, size); +} + diff --git a/asm/arm-dis.h b/asm/arm-dis.h new file mode 100644 index 0000000000..8019499ce9 --- /dev/null +++ b/asm/arm-dis.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2002 Sergey Chaban + */ + +#ifndef ARM_DIS +#define ARM_DIS + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _ARMDis { + FILE* dis_out; + void* pi; +} ARMDis; + + +void _armdis_set_output(FILE* f); +FILE* _armdis_get_output(void); +void _armdis_decode(void* p, int size); +void _armdis_open(const char* dump_name); +void _armdis_close(void); +void _armdis_dump(const char* dump_name, void* p, int size); + + +void armdis_init(ARMDis* dis); +void armdis_set_output(ARMDis* dis, FILE* f); +FILE* armdis_get_output(ARMDis* dis); +void armdis_decode(ARMDis* dis, void* p, int size); +void armdis_open(ARMDis* dis, const char* dump_name); +void armdis_close(ARMDis* dis); +void armdis_dump(ARMDis* dis, const char* dump_name, void* p, int size); + +#ifdef __cplusplus +} +#endif + +#endif /* ARM_DIS */ diff --git a/asm/arm-fpa-codegen.h b/asm/arm-fpa-codegen.h new file mode 100644 index 0000000000..4389a5e79a --- /dev/null +++ b/asm/arm-fpa-codegen.h @@ -0,0 +1,198 @@ +/* + * Copyright 2005 Novell Inc + * Copyright 2011 Xamarin Inc + */ + +#ifndef __MONO_ARM_FPA_CODEGEN_H__ +#define __MONO_ARM_FPA_CODEGEN_H__ + +#include "arm-codegen.h" + +enum { + /* FPA registers */ + ARM_FPA_F0, + ARM_FPA_F1, + ARM_FPA_F2, + ARM_FPA_F3, + ARM_FPA_F4, + ARM_FPA_F5, + ARM_FPA_F6, + ARM_FPA_F7, + + /* transfer length for LDF/STF (T0/T1), already shifted */ + ARM_FPA_SINGLE = 0, + ARM_FPA_DOUBLE = 1 << 15, + + ARM_FPA_ADF = 0 << 20, + ARM_FPA_MUF = 1 << 20, + ARM_FPA_SUF = 2 << 20, + ARM_FPA_RSF = 3 << 20, + ARM_FPA_DVF = 4 << 20, + ARM_FPA_RDF = 5 << 20, + ARM_FPA_POW = 6 << 20, + ARM_FPA_RPW = 7 << 20, + ARM_FPA_RMF = 8 << 20, + ARM_FPA_FML = 9 << 20, + ARM_FPA_FDV = 10 << 20, + ARM_FPA_FRD = 11 << 20, + ARM_FPA_POL = 12 << 20, + + /* monadic */ + ARM_FPA_MVF = (0 << 20) | (1 << 15), + ARM_FPA_MNF = (1 << 20) | (1 << 15), + ARM_FPA_ABS = (2 << 20) | (1 << 15), + ARM_FPA_RND = (3 << 20) | (1 << 15), + ARM_FPA_SQT = (4 << 20) | (1 << 15), + ARM_FPA_LOG = (5 << 20) | (1 << 15), + ARM_FPA_LGN = (6 << 20) | (1 << 15), + ARM_FPA_EXP = (7 << 20) | (1 << 15), + ARM_FPA_SIN = (8 << 20) | (1 << 15), + ARM_FPA_COS = (9 << 20) | (1 << 15), + ARM_FPA_TAN = (10 << 20) | (1 << 15), + ARM_FPA_ASN = (11 << 20) | (1 << 15), + ARM_FPA_ACS = (12 << 20) | (1 << 15), + ARM_FPA_ATN = (13 << 20) | (1 << 15), + ARM_FPA_URD = (14 << 20) | (1 << 15), + ARM_FPA_NRM = (15 << 20) | (1 << 15), + + /* round modes */ + ARM_FPA_ROUND_NEAREST = 0, + ARM_FPA_ROUND_PINF = 1, + ARM_FPA_ROUND_MINF = 2, + ARM_FPA_ROUND_ZERO = 3, + + /* round precision */ + ARM_FPA_ROUND_SINGLE = 0, + ARM_FPA_ROUND_DOUBLE = 1, + + /* constants */ + ARM_FPA_CONST_0 = 8, + ARM_FPA_CONST_1_0 = 9, + ARM_FPA_CONST_2_0 = 10, + ARM_FPA_CONST_3_0 = 11, + ARM_FPA_CONST_4_0 = 12, + ARM_FPA_CONST_5_0 = 13, + ARM_FPA_CONST_0_5 = 14, + ARM_FPA_CONST_10 = 15, + + /* compares */ + ARM_FPA_CMF = 4, + ARM_FPA_CNF = 5, + ARM_FPA_CMFE = 6, + ARM_FPA_CNFE = 7, + + /* CPRT ops */ + ARM_FPA_FLT = 0, + ARM_FPA_FIX = 1, + ARM_FPA_WFS = 2, + ARM_FPA_RFS = 3, + ARM_FPA_WFC = 4, + ARM_FPA_RFC = 5 +}; + +#define ARM_DEF_FPA_LDF_STF(cond,post,ls,fptype,wback,basereg,fdreg,offset) \ + ((offset) >= 0? (offset)>>2: -(offset)>>2) | \ + ((1 << 8) | (fptype)) | \ + ((fdreg) << 12) | \ + ((basereg) << 16) | \ + ((ls) << 20) | \ + ((wback) << 21) | \ + (((offset) >= 0) << 23) | \ + ((wback) << 21) | \ + ((post) << 24) | \ + (6 << 25) | \ + ARM_DEF_COND(cond) + +/* FP load and stores */ +#define ARM_FPA_LDFS_COND(p,freg,base,offset,cond) \ + ARM_EMIT((p), ARM_DEF_FPA_LDF_STF((cond),1,ARMOP_LDR,ARM_FPA_SINGLE,0,(base),(freg),(offset))) +#define ARM_FPA_LDFS(p,freg,base,offset) \ + ARM_FPA_LDFS_COND(p,freg,base,offset,ARMCOND_AL) + +#define ARM_FPA_LDFD_COND(p,freg,base,offset,cond) \ + ARM_EMIT((p), ARM_DEF_FPA_LDF_STF((cond),1,ARMOP_LDR,ARM_FPA_DOUBLE,0,(base),(freg),(offset))) +#define ARM_FPA_LDFD(p,freg,base,offset) \ + ARM_FPA_LDFD_COND(p,freg,base,offset,ARMCOND_AL) + +#define ARM_FPA_STFS_COND(p,freg,base,offset,cond) \ + ARM_EMIT((p), ARM_DEF_FPA_LDF_STF((cond),1,ARMOP_STR,ARM_FPA_SINGLE,0,(base),(freg),(offset))) +#define ARM_FPA_STFS(p,freg,base,offset) \ + ARM_FPA_STFS_COND(p,freg,base,offset,ARMCOND_AL) + +#define ARM_FPA_STFD_COND(p,freg,base,offset,cond) \ + ARM_EMIT((p), ARM_DEF_FPA_LDF_STF((cond),1,ARMOP_STR,ARM_FPA_DOUBLE,0,(base),(freg),(offset))) +#define ARM_FPA_STFD(p,freg,base,offset) \ + ARM_FPA_STFD_COND(p,freg,base,offset,ARMCOND_AL) + +#define ARM_DEF_FPA_CPDO_MONADIC(cond,op,dreg,sreg,round,prec) \ + (1 << 8) | (14 << 24) | \ + (op) | \ + ((sreg) << 0) | \ + ((round) << 5) | \ + ((dreg) << 12) | \ + ((prec) << 7) | \ + ARM_DEF_COND(cond) + +#define ARM_DEF_FPA_CPDO_DYADIC(cond,op,dreg,sreg1,sreg2,round,prec) \ + (1 << 8) | (14 << 24) | \ + (op) | \ + ((sreg1) << 16) | \ + ((sreg2) << 0) | \ + ((round) << 5) | \ + ((dreg) << 12) | \ + ((prec) << 7) | \ + ARM_DEF_COND(cond) + +#define ARM_DEF_FPA_CMP(cond,op,sreg1,sreg2) \ + (1 << 4) | (1 << 8) | (15 << 12) | \ + (1 << 20) | (14 << 24) | \ + (op) << 21 | \ + (sreg1) << 16 | \ + (sreg2) | \ + ARM_DEF_COND(cond) + +#define ARM_DEF_FPA_CPRT(cond,op,fn,fm,rd,ftype,round) \ + (1 << 4) | (1 << 8) | (14 << 24) | \ + (op) << 20 | \ + (fm) | \ + (fn) << 16 | \ + (rd) << 12 | \ + ((round) << 5) | \ + ((ftype) << 7) | \ + ARM_DEF_COND(cond) + + +#include "arm_fpamacros.h" + +#define ARM_FPA_RNDDZ_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_FPA_CPDO_MONADIC((cond),ARM_FPA_RND,(dreg),(sreg),ARM_FPA_ROUND_ZERO,ARM_FPA_ROUND_DOUBLE)) +#define ARM_FPA_RNDDZ(p,dreg,sreg) ARM_FPA_RNDD_COND(p,dreg,sreg,ARMCOND_AL) + +/* compares */ +#define ARM_FPA_FCMP_COND(p,op,sreg1,sreg2,cond) \ + ARM_EMIT(p, ARM_DEF_FPA_CMP(cond,op,sreg1,sreg2)) +#define ARM_FPA_FCMP(p,op,sreg1,sreg2) ARM_FPA_FCMP_COND(p,op,sreg1,sreg2,ARMCOND_AL) + +/* coprocessor register transfer */ +#define ARM_FPA_FLTD(p,fn,rd) \ + ARM_EMIT(p, ARM_DEF_FPA_CPRT(ARMCOND_AL,ARM_FPA_FLT,(fn),0,(rd),ARM_FPA_ROUND_DOUBLE,ARM_FPA_ROUND_NEAREST)) +#define ARM_FPA_FLTS(p,fn,rd) \ + ARM_EMIT(p, ARM_DEF_FPA_CPRT(ARMCOND_AL,ARM_FPA_FLT,(fn),0,(rd),ARM_FPA_ROUND_SINGLE,ARM_FPA_ROUND_NEAREST)) + +#define ARM_FPA_FIXZ(p,rd,fm) \ + ARM_EMIT(p, ARM_DEF_FPA_CPRT(ARMCOND_AL,ARM_FPA_FIX,0,(fm),(rd),0,ARM_FPA_ROUND_ZERO)) + +#define ARM_FPA_WFS(p,rd) \ + ARM_EMIT(p, ARM_DEF_FPA_CPRT(ARMCOND_AL,ARM_FPA_WFS,0,0,(rd),0,ARM_FPA_ROUND_NEAREST)) + +#define ARM_FPA_RFS(p,rd) \ + ARM_EMIT(p, ARM_DEF_FPA_CPRT(ARMCOND_AL,ARM_FPA_RFS,0,0,(rd),0,ARM_FPA_ROUND_NEAREST)) + +#define ARM_FPA_WFC(p,rd) \ + ARM_EMIT(p, ARM_DEF_FPA_CPRT(ARMCOND_AL,ARM_FPA_WFC,0,0,(rd),0,ARM_FPA_ROUND_NEAREST)) + +#define ARM_FPA_RFC(p,rd) \ + ARM_EMIT(p, ARM_DEF_FPA_CPRT(ARMCOND_AL,ARM_FPA_RFC,0,0,(rd),0,ARM_FPA_ROUND_NEAREST)) + +#endif /* __MONO_ARM_FPA_CODEGEN_H__ */ + diff --git a/asm/arm-vfp-codegen.h b/asm/arm-vfp-codegen.h new file mode 100644 index 0000000000..8056f7bccc --- /dev/null +++ b/asm/arm-vfp-codegen.h @@ -0,0 +1,235 @@ +// +// Copyright 2011 Xamarin Inc +// + +#ifndef __MONO_ARM_VFP_CODEGEN_H__ +#define __MONO_ARM_VFP_CODEGEN_H__ + +#include "arm-codegen.h" + +enum { + /* FPA registers */ + ARM_VFP_F0, + ARM_VFP_F1, + ARM_VFP_F2, + ARM_VFP_F3, + ARM_VFP_F4, + ARM_VFP_F5, + ARM_VFP_F6, + ARM_VFP_F7, + ARM_VFP_F8, + ARM_VFP_F9, + ARM_VFP_F10, + ARM_VFP_F11, + ARM_VFP_F12, + ARM_VFP_F13, + ARM_VFP_F14, + ARM_VFP_F15, + ARM_VFP_F16, + ARM_VFP_F17, + ARM_VFP_F18, + ARM_VFP_F19, + ARM_VFP_F20, + ARM_VFP_F21, + ARM_VFP_F22, + ARM_VFP_F23, + ARM_VFP_F24, + ARM_VFP_F25, + ARM_VFP_F26, + ARM_VFP_F27, + ARM_VFP_F28, + ARM_VFP_F29, + ARM_VFP_F30, + ARM_VFP_F31, + + ARM_VFP_D0 = ARM_VFP_F0, + ARM_VFP_D1 = ARM_VFP_F2, + ARM_VFP_D2 = ARM_VFP_F4, + ARM_VFP_D3 = ARM_VFP_F6, + ARM_VFP_D4 = ARM_VFP_F8, + ARM_VFP_D5 = ARM_VFP_F10, + ARM_VFP_D6 = ARM_VFP_F12, + ARM_VFP_D7 = ARM_VFP_F14, + ARM_VFP_D8 = ARM_VFP_F16, + ARM_VFP_D9 = ARM_VFP_F18, + ARM_VFP_D10 = ARM_VFP_F20, + ARM_VFP_D11 = ARM_VFP_F22, + ARM_VFP_D12 = ARM_VFP_F24, + ARM_VFP_D13 = ARM_VFP_F26, + ARM_VFP_D14 = ARM_VFP_F28, + ARM_VFP_D15 = ARM_VFP_F30, + + ARM_VFP_COPROC_SINGLE = 10, + ARM_VFP_COPROC_DOUBLE = 11, + +#define ARM_VFP_OP(p,q,r,s) (((p) << 23) | ((q) << 21) | ((r) << 20) | ((s) << 6)) +#define ARM_VFP_OP2(Fn,N) (ARM_VFP_OP (1,1,1,1) | ((Fn) << 16) | ((N) << 7)) + + ARM_VFP_MUL = ARM_VFP_OP (0,1,0,0), + ARM_VFP_NMUL = ARM_VFP_OP (0,1,0,1), + ARM_VFP_ADD = ARM_VFP_OP (0,1,1,0), + ARM_VFP_SUB = ARM_VFP_OP (0,1,1,1), + ARM_VFP_DIV = ARM_VFP_OP (1,0,0,0), + + ARM_VFP_CPY = ARM_VFP_OP2 (0,0), + ARM_VFP_ABS = ARM_VFP_OP2 (0,1), + ARM_VFP_NEG = ARM_VFP_OP2 (1,0), + ARM_VFP_SQRT = ARM_VFP_OP2 (1,1), + ARM_VFP_CMP = ARM_VFP_OP2 (4,0), + ARM_VFP_CMPE = ARM_VFP_OP2 (4,1), + ARM_VFP_CMPZ = ARM_VFP_OP2 (5,0), + ARM_VFP_CMPEZ = ARM_VFP_OP2 (5,1), + ARM_VFP_CVT = ARM_VFP_OP2 (7,1), + ARM_VFP_UITO = ARM_VFP_OP2 (8,0), + ARM_VFP_SITO = ARM_VFP_OP2 (8,1), + ARM_VFP_TOUI = ARM_VFP_OP2 (12,0), + ARM_VFP_TOSI = ARM_VFP_OP2 (13,0), + ARM_VFP_TOUIZ = ARM_VFP_OP2 (12,1), + ARM_VFP_TOSIZ = ARM_VFP_OP2 (13,1), + + ARM_VFP_SID = 0, + ARM_VFP_SCR = 1 << 1, + ARM_VFP_EXC = 8 << 1 +}; + +#define ARM_DEF_VFP_DYADIC(cond,cp,op,Fd,Fn,Fm) \ + (14 << 24) | \ + ((cp) << 8) | \ + (op) | \ + (((Fd) >> 1) << 12) | \ + (((Fd) & 1) << 22) | \ + (((Fn) >> 1) << 16) | \ + (((Fn) & 1) << 7) | \ + (((Fm) >> 1) << 0) | \ + (((Fm) & 1) << 5) | \ + ARM_DEF_COND(cond) + +#define ARM_DEF_VFP_MONADIC(cond,cp,op,Fd,Fm) \ + (14 << 24) | \ + ((cp) << 8) | \ + (op) | \ + (((Fd) >> 1) << 12) | \ + (((Fd) & 1) << 22) | \ + (((Fm) >> 1) << 0) | \ + (((Fm) & 1) << 5) | \ + ARM_DEF_COND(cond) + +#define ARM_DEF_VFP_LSF(cond,cp,post,ls,wback,basereg,Fd,offset) \ + ((offset) >= 0? (offset)>>2: -(offset)>>2) | \ + (6 << 25) | \ + ((cp) << 8) | \ + (((Fd) >> 1) << 12) | \ + (((Fd) & 1) << 22) | \ + ((basereg) << 16) | \ + ((ls) << 20) | \ + ((wback) << 21) | \ + (((offset) >= 0) << 23) | \ + ((wback) << 21) | \ + ((post) << 24) | \ + ARM_DEF_COND(cond) + +#define ARM_DEF_VFP_CPT(cond,cp,op,L,Fn,Rd) \ + (14 << 24) | \ + (1 << 4) | \ + ((cp) << 8) | \ + ((op) << 21) | \ + ((L) << 20) | \ + ((Rd) << 12) | \ + (((Fn) >> 1) << 16) | \ + (((Fn) & 1) << 7) | \ + ARM_DEF_COND(cond) + +/* FP load and stores */ +#define ARM_FLDS_COND(p,freg,base,offset,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_LSF((cond),ARM_VFP_COPROC_SINGLE,1,ARMOP_LDR,0,(base),(freg),(offset))) +#define ARM_FLDS(p,freg,base,offset) \ + ARM_FLDS_COND(p,freg,base,offset,ARMCOND_AL) + +#define ARM_FLDD_COND(p,freg,base,offset,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_LSF((cond),ARM_VFP_COPROC_DOUBLE,1,ARMOP_LDR,0,(base),(freg),(offset))) +#define ARM_FLDD(p,freg,base,offset) \ + ARM_FLDD_COND(p,freg,base,offset,ARMCOND_AL) + +#define ARM_FSTS_COND(p,freg,base,offset,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_LSF((cond),ARM_VFP_COPROC_SINGLE,1,ARMOP_STR,0,(base),(freg),(offset))) +#define ARM_FSTS(p,freg,base,offset) \ + ARM_FSTS_COND(p,freg,base,offset,ARMCOND_AL) + +#define ARM_FSTD_COND(p,freg,base,offset,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_LSF((cond),ARM_VFP_COPROC_DOUBLE,1,ARMOP_STR,0,(base),(freg),(offset))) +#define ARM_FSTD(p,freg,base,offset) \ + ARM_FSTD_COND(p,freg,base,offset,ARMCOND_AL) + +#include "arm_vfpmacros.h" + +/* coprocessor register transfer */ +#define ARM_FMSR(p,freg,reg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_SINGLE,0,0,(freg),(reg))) +#define ARM_FMRS(p,reg,freg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_SINGLE,0,1,(freg),(reg))) + +#define ARM_FMDLR(p,freg,reg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,0,0,(freg),(reg))) +#define ARM_FMRDL(p,reg,freg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,0,1,(freg),(reg))) +#define ARM_FMDHR(p,freg,reg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,1,0,(freg),(reg))) +#define ARM_FMRDH(p,reg,freg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,1,1,(freg),(reg))) + +#define ARM_FMXR(p,freg,reg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_SINGLE,7,0,(freg),(reg))) +#define ARM_FMRX(p,reg,fcreg) \ + ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_SINGLE,7,1,(fcreg),(reg))) + +#define ARM_FMSTAT(p) \ + ARM_FMRX((p),ARMREG_R15,ARM_VFP_SCR) + +#define ARM_DEF_MCRR(cond,cp,rn,rd,Fm,M) \ + ((Fm) << 0) | \ + (1 << 4) | \ + ((M) << 5) | \ + ((cp) << 8) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((2) << 21) | \ + (12 << 24) | \ + ARM_DEF_COND(cond) + +#define ARM_FMDRR(p,rd,rn,dm) \ + ARM_EMIT((p), ARM_DEF_MCRR(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,(rn),(rd),(dm) >> 1, (dm) & 1)) + +#define ARM_DEF_FMRRD(cond,cp,rn,rd,Dm,D) \ + ((Dm) << 0) | \ + (1 << 4) | \ + ((cp) << 8) | \ + ((rd) << 12) | \ + ((rn) << 16) | \ + ((0xc5) << 20) | \ + ARM_DEF_COND(cond) + +#define ARM_FMRRD(p,rd,rn,dm) \ + ARM_EMIT((p), ARM_DEF_FMRRD(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,(rn),(rd),(dm) >> 1, (dm) & 1)) + +#define ARM_DEF_FUITOS(cond,Dd,D,Fm,M) ((cond) << 28) | ((0x1d) << 23) | ((D) << 22) | ((0x3) << 20) | ((8) << 16) | ((Dd) << 12) | ((0xa) << 8) | ((1) << 6) | ((M) << 5) | ((Fm) << 0) + +#define ARM_FUITOS(p,dreg,sreg) \ + ARM_EMIT((p), ARM_DEF_FUITOS (ARMCOND_AL, (dreg) >> 1, (dreg) & 1, (sreg) >> 1, (sreg) & 1)) + +#define ARM_DEF_FUITOD(cond,Dd,D,Fm,M) ((cond) << 28) | ((0x1d) << 23) | ((D) << 22) | ((0x3) << 20) | ((8) << 16) | ((Dd) << 12) | ((0xb) << 8) | ((1) << 6) | ((M) << 5) | ((Fm) << 0) + +#define ARM_FUITOD(p,dreg,sreg) \ + ARM_EMIT((p), ARM_DEF_FUITOD (ARMCOND_AL, (dreg) >> 1, (dreg) & 1, (sreg) >> 1, (sreg) & 1)) + +#define ARM_DEF_FSITOS(cond,Dd,D,Fm,M) ((cond) << 28) | ((0x1d) << 23) | ((D) << 22) | ((0x3) << 20) | ((8) << 16) | ((Dd) << 12) | ((0xa) << 8) | ((1) << 7) | ((1) << 6) | ((M) << 5) | ((Fm) << 0) + +#define ARM_FSITOS(p,dreg,sreg) \ + ARM_EMIT((p), ARM_DEF_FSITOS (ARMCOND_AL, (dreg) >> 1, (dreg) & 1, (sreg) >> 1, (sreg) & 1)) + +#define ARM_DEF_FSITOD(cond,Dd,D,Fm,M) ((cond) << 28) | ((0x1d) << 23) | ((D) << 22) | ((0x3) << 20) | ((8) << 16) | ((Dd) << 12) | ((0xb) << 8) | ((1) << 7) | ((1) << 6) | ((M) << 5) | ((Fm) << 0) + +#define ARM_FSITOD(p,dreg,sreg) \ + ARM_EMIT((p), ARM_DEF_FSITOD (ARMCOND_AL, (dreg) >> 1, (dreg) & 1, (sreg) >> 1, (sreg) & 1)) + +#endif /* __MONO_ARM_VFP_CODEGEN_H__ */ + diff --git a/asm/arm-wmmx.h b/asm/arm-wmmx.h new file mode 100644 index 0000000000..427c4fc9f6 --- /dev/null +++ b/asm/arm-wmmx.h @@ -0,0 +1,177 @@ +/* + * ARM CodeGen + * XScale WirelessMMX extensions + * Copyright 2002 Wild West Software + */ + +#ifndef __WMMX_H__ +#define __WMMX_H__ 1 + +#if 0 +#include +#endif + +#if defined(ARM_IASM) +# define WM_ASM(_expr) ARM_IASM(_expr) +#else +# define WM_ASM(_expr) __emit (_expr) +#endif + +#if defined(ARM_EMIT) +# define WM_EMIT(p, i) ARM_EMIT(p, i) +#else +# define WM_EMIT(p, i) +#endif + +enum { + WM_CC_EQ = 0x0, + WM_CC_NE = 0x1, + WM_CC_CS = 0x2, + WM_CC_HS = WM_CC_CS, + WM_CC_CC = 0x3, + WM_CC_LO = WM_CC_CC, + WM_CC_MI = 0x4, + WM_CC_PL = 0x5, + WM_CC_VS = 0x6, + WM_CC_VC = 0x7, + WM_CC_HI = 0x8, + WM_CC_LS = 0x9, + WM_CC_GE = 0xA, + WM_CC_LT = 0xB, + WM_CC_GT = 0xC, + WM_CC_LE = 0xD, + WM_CC_AL = 0xE, + WM_CC_NV = 0xF, + WM_CC_SHIFT = 28 +}; + +#if defined(ARM_DEF_COND) +# define WM_DEF_CC(_cc) ARM_DEF_COND(_cc) +#else +# define WM_DEF_CC(_cc) ((_cc & 0xF) << WM_CC_SHIFT) +#endif + + +enum { + WM_R0 = 0x0, + WM_R1 = 0x1, + WM_R2 = 0x2, + WM_R3 = 0x3, + WM_R4 = 0x4, + WM_R5 = 0x5, + WM_R6 = 0x6, + WM_R7 = 0x7, + WM_R8 = 0x8, + WM_R9 = 0x9, + WM_R10 = 0xA, + WM_R11 = 0xB, + WM_R12 = 0xC, + WM_R13 = 0xD, + WM_R14 = 0xE, + WM_R15 = 0xF, + + WM_wR0 = 0x0, + WM_wR1 = 0x1, + WM_wR2 = 0x2, + WM_wR3 = 0x3, + WM_wR4 = 0x4, + WM_wR5 = 0x5, + WM_wR6 = 0x6, + WM_wR7 = 0x7, + WM_wR8 = 0x8, + WM_wR9 = 0x9, + WM_wR10 = 0xA, + WM_wR11 = 0xB, + WM_wR12 = 0xC, + WM_wR13 = 0xD, + WM_wR14 = 0xE, + WM_wR15 = 0xF +}; + + +/* + * Qualifiers: + * H - 16-bit (HalfWord) SIMD + * W - 32-bit (Word) SIMD + * D - 64-bit (Double) + */ +enum { + WM_B = 0, + WM_H = 1, + WM_D = 2 +}; + +/* + * B.2.3 Transfers From Coprocessor Register (MRC) + * Table B-5 + */ +enum { + WM_TMRC_OP2 = 0, + WM_TMRC_CPNUM = 1, + + WM_TMOVMSK_OP2 = 1, + WM_TMOVMSK_CPNUM = 0, + + WM_TANDC_OP2 = 1, + WM_TANDC_CPNUM = 1, + + WM_TORC_OP2 = 2, + WM_TORC_CPNUM = 1, + + WM_TEXTRC_OP2 = 3, + WM_TEXTRC_CPNUM = 1, + + WM_TEXTRM_OP2 = 3, + WM_TEXTRM_CPNUM = 0 +}; + + +/* + * TANDC{Cond} R15 + * Performs AND across the fields of the SIMD PSR register (wCASF) and sends the result + * to CPSR; can be performed after a Byte, Half-word or Word operation that sets the flags. + * NOTE: R15 is omitted from the macro declaration; + */ +#define DEF_WM_TNADC_CC(_q, _cc) WM_DEF_CC((_cc)) + ((_q) << 0x16) + 0xE13F130 + +#define _WM_TNADC_CC(_q, _cc) WM_ASM(DEF_WM_TNADC_CC(_q, _cc)) +#define ARM_WM_TNADC_CC(_p, _q, _cc) WM_EMIT(_p, DEF_WM_TNADC_CC(_q, _cc)) + +/* inline assembly */ +#define _WM_TNADC(_q) _WM_TNADC_CC((_q), WM_CC_AL) +#define _WM_TNADCB() _WM_TNADC(WM_B) +#define _WM_TNADCH() _WM_TNADC(WM_H) +#define _WM_TNADCD() _WM_TNADC(WM_D) + +/* codegen */ +#define ARM_WM_TNADC(_p, _q) ARM_WM_TNADC_CC((_p), (_q), WM_CC_AL) +#define ARM_WM_TNADCB(_p) ARM_WM_TNADC(_p, WM_B) +#define ARM_WM_TNADCH(_p) ARM_WM_TNADC(_p, WM_H) +#define ARM_WM_TNADCD(_p) ARM_WM_TNADC(_p, WM_D) + + +/* + * TBCST{Cond} wRd, Rn + * Broadcasts a value from the ARM Source reg (Rn) to every SIMD position + * in the WMMX Destination reg (wRd). + */ +#define DEF_WM_TBCST_CC(_q, _cc, _wrd, _rn) \ + WM_DEF_CC((_cc)) + ((_q) << 6) + ((_wrd) << 16) + ((_rn) << 12) + 0xE200010 + +#define _WM_TBCST_CC(_q, _cc, _wrd, _rn) WM_ASM(DEF_WM_TBCST_CC(_q, _cc, _wrd, _rn)) +#define ARM_WM_TBCST_CC(_p, _q, _cc, _wrd, _rn) WM_EMIT(_p, DEF_WM_TBCST_CC(_q, _cc, _wrd, _rn)) + +/* inline */ +#define _WM_TBCST(_q, _wrd, _rn) _WM_TBCST_CC(_q, WM_CC_AL, _wrd, _rn) +#define _WM_TBCSTB(_wrd, _rn) _WM_TBCST(WM_B) +#define _WM_TBCSTH(_wrd, _rn) _WM_TBCST(WM_H) +#define _WM_TBCSTD(_wrd, _rn) _WM_TBCST(WM_D) + +/* codegen */ +#define ARM_WM_TBCST(_p, _q, _wrd, _rn) ARM_WM_TBCST_CC(_p, _q, WM_CC_AL, _wrd, _rn) +#define ARM_WM_TBCSTB(_p, _wrd, _rn) _WM_TBCST(_p, WM_B) +#define ARM_WM_TBCSTH(_p, _wrd, _rn) _WM_TBCST(_p, WM_H) +#define ARM_WM_TBCSTD(_p, _wrd, _rn) _WM_TBCST(_p, WM_D) + + +#endif /* __WMMX_H__ */ diff --git a/asm/arm_dpimacros.h b/asm/arm_dpimacros.h new file mode 100644 index 0000000000..d8ff666997 --- /dev/null +++ b/asm/arm_dpimacros.h @@ -0,0 +1,1603 @@ +/* Macros for DPI ops, auto-generated from template */ + + +/* mov/mvn */ + +/* Rd := imm8 ROR rot */ +#define ARM_MOV_REG_IMM_COND(p, reg, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_MOV, reg, 0, imm8, rot, cond) +#define ARM_MOV_REG_IMM(p, reg, imm8, rot) \ + ARM_MOV_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) +/* S */ +#define ARM_MOVS_REG_IMM_COND(p, reg, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_MOV, reg, 0, imm8, rot, cond) +#define ARM_MOVS_REG_IMM(p, reg, imm8, rot) \ + ARM_MOVS_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MOV_REG_IMM_COND(reg, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_MOV, reg, 0, imm8, rot, cond) +#define _MOV_REG_IMM(reg, imm8, rot) \ + _MOV_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) +/* S */ +#define _MOVS_REG_IMM_COND(reg, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_MOV, reg, 0, imm8, rot, cond) +#define _MOVS_REG_IMM(reg, imm8, rot) \ + _MOVS_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := imm8 */ +#define ARM_MOV_REG_IMM8_COND(p, reg, imm8, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_MOV, reg, 0, imm8, 0, cond) +#define ARM_MOV_REG_IMM8(p, reg, imm8) \ + ARM_MOV_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) +/* S */ +#define ARM_MOVS_REG_IMM8_COND(p, reg, imm8, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_MOV, reg, 0, imm8, 0, cond) +#define ARM_MOVS_REG_IMM8(p, reg, imm8) \ + ARM_MOVS_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MOV_REG_IMM8_COND(reg, imm8, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_MOV, reg, 0, imm8, 0, cond) +#define _MOV_REG_IMM8(reg, imm8) \ + _MOV_REG_IMM8_COND(reg, imm8, ARMCOND_AL) +/* S */ +#define _MOVS_REG_IMM8_COND(reg, imm8, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_MOV, reg, 0, imm8, 0, cond) +#define _MOVS_REG_IMM8(reg, imm8) \ + _MOVS_REG_IMM8_COND(reg, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rm */ +#define ARM_MOV_REG_REG_COND(p, rd, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_MOV, rd, 0, rm, cond) +#define ARM_MOV_REG_REG(p, rd, rm) \ + ARM_MOV_REG_REG_COND(p, rd, rm, ARMCOND_AL) +/* S */ +#define ARM_MOVS_REG_REG_COND(p, rd, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_MOV, rd, 0, rm, cond) +#define ARM_MOVS_REG_REG(p, rd, rm) \ + ARM_MOVS_REG_REG_COND(p, rd, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MOV_REG_REG_COND(rd, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_MOV, rd, 0, rm, cond) +#define _MOV_REG_REG(rd, rm) \ + _MOV_REG_REG_COND(rd, rm, ARMCOND_AL) +/* S */ +#define _MOVS_REG_REG_COND(rd, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_MOV, rd, 0, rm, cond) +#define _MOVS_REG_REG(rd, rm) \ + _MOVS_REG_REG_COND(rd, rm, ARMCOND_AL) +#endif + + +/* Rd := Rm imm_shift */ +#define ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_MOV, rd, 0, rm, shift_type, imm_shift, cond) +#define ARM_MOV_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ + ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) +/* S */ +#define ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_MOV, rd, 0, rm, shift_type, imm_shift, cond) +#define ARM_MOVS_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ + ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MOV_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_MOV, rd, 0, rm, shift_type, imm_shift, cond) +#define _MOV_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ + _MOV_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) +/* S */ +#define _MOVS_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_MOV, rd, 0, rm, shift_type, imm_shift, cond) +#define _MOVS_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ + _MOVS_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + + +/* Rd := (Rm Rs) */ +#define ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_MOV, rd, 0, rm, shift_type, rs, cond) +#define ARM_MOV_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ + ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) +/* S */ +#define ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_MOV, rd, 0, rm, shift_type, rs, cond) +#define ARM_MOVS_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ + ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MOV_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_MOV, rd, 0, rm, shift_type, rs, cond) +#define _MOV_REG_REGSHIFT(rd, rm, shift_type, rs) \ + _MOV_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) +/* S */ +#define _MOVS_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_MOV, rd, 0, rm, shift_type, rs, cond) +#define _MOVS_REG_REGSHIFT(rd, rm, shift_type, rs) \ + _MOVS_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* Rd := imm8 ROR rot */ +#define ARM_MVN_REG_IMM_COND(p, reg, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_MVN, reg, 0, imm8, rot, cond) +#define ARM_MVN_REG_IMM(p, reg, imm8, rot) \ + ARM_MVN_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) +/* S */ +#define ARM_MVNS_REG_IMM_COND(p, reg, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_MVN, reg, 0, imm8, rot, cond) +#define ARM_MVNS_REG_IMM(p, reg, imm8, rot) \ + ARM_MVNS_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MVN_REG_IMM_COND(reg, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_MVN, reg, 0, imm8, rot, cond) +#define _MVN_REG_IMM(reg, imm8, rot) \ + _MVN_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) +/* S */ +#define _MVNS_REG_IMM_COND(reg, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_MVN, reg, 0, imm8, rot, cond) +#define _MVNS_REG_IMM(reg, imm8, rot) \ + _MVNS_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := imm8 */ +#define ARM_MVN_REG_IMM8_COND(p, reg, imm8, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_MVN, reg, 0, imm8, 0, cond) +#define ARM_MVN_REG_IMM8(p, reg, imm8) \ + ARM_MVN_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) +/* S */ +#define ARM_MVNS_REG_IMM8_COND(p, reg, imm8, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_MVN, reg, 0, imm8, 0, cond) +#define ARM_MVNS_REG_IMM8(p, reg, imm8) \ + ARM_MVNS_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MVN_REG_IMM8_COND(reg, imm8, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_MVN, reg, 0, imm8, 0, cond) +#define _MVN_REG_IMM8(reg, imm8) \ + _MVN_REG_IMM8_COND(reg, imm8, ARMCOND_AL) +/* S */ +#define _MVNS_REG_IMM8_COND(reg, imm8, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_MVN, reg, 0, imm8, 0, cond) +#define _MVNS_REG_IMM8(reg, imm8) \ + _MVNS_REG_IMM8_COND(reg, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rm */ +#define ARM_MVN_REG_REG_COND(p, rd, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_MVN, rd, 0, rm, cond) +#define ARM_MVN_REG_REG(p, rd, rm) \ + ARM_MVN_REG_REG_COND(p, rd, rm, ARMCOND_AL) +/* S */ +#define ARM_MVNS_REG_REG_COND(p, rd, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_MVN, rd, 0, rm, cond) +#define ARM_MVNS_REG_REG(p, rd, rm) \ + ARM_MVNS_REG_REG_COND(p, rd, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MVN_REG_REG_COND(rd, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_MVN, rd, 0, rm, cond) +#define _MVN_REG_REG(rd, rm) \ + _MVN_REG_REG_COND(rd, rm, ARMCOND_AL) +/* S */ +#define _MVNS_REG_REG_COND(rd, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_MVN, rd, 0, rm, cond) +#define _MVNS_REG_REG(rd, rm) \ + _MVNS_REG_REG_COND(rd, rm, ARMCOND_AL) +#endif + + +/* Rd := Rm imm_shift */ +#define ARM_MVN_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_MVN, rd, 0, rm, shift_type, imm_shift, cond) +#define ARM_MVN_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ + ARM_MVN_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) +/* S */ +#define ARM_MVNS_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_MVN, rd, 0, rm, shift_type, imm_shift, cond) +#define ARM_MVNS_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ + ARM_MVNS_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MVN_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_MVN, rd, 0, rm, shift_type, imm_shift, cond) +#define _MVN_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ + _MVN_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) +/* S */ +#define _MVNS_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_MVN, rd, 0, rm, shift_type, imm_shift, cond) +#define _MVNS_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ + _MVNS_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + + +/* Rd := (Rm Rs) */ +#define ARM_MVN_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_MVN, rd, 0, rm, shift_type, rs, cond) +#define ARM_MVN_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ + ARM_MVN_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) +/* S */ +#define ARM_MVNS_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_MVN, rd, 0, rm, shift_type, rs, cond) +#define ARM_MVNS_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ + ARM_MVNS_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _MVN_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_MVN, rd, 0, rm, shift_type, rs, cond) +#define _MVN_REG_REGSHIFT(rd, rm, shift_type, rs) \ + _MVN_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) +/* S */ +#define _MVNS_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_MVN, rd, 0, rm, shift_type, rs, cond) +#define _MVNS_REG_REGSHIFT(rd, rm, shift_type, rs) \ + _MVNS_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) +#endif + + + +/* DPIs, arithmetic and logical */ + +/* -- AND -- */ + +/* Rd := Rn AND (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_AND_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_AND, rd, rn, imm8, rot, cond) +#define ARM_AND_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_AND_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_ANDS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_AND, rd, rn, imm8, rot, cond) +#define ARM_ANDS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_ANDS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _AND_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_AND, rd, rn, imm8, rot, cond) +#define _AND_REG_IMM(rd, rn, imm8, rot) \ + _AND_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _ANDS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_AND, rd, rn, imm8, rot, cond) +#define _ANDS_REG_IMM(rd, rn, imm8, rot) \ + _ANDS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn AND imm8 */ +#define ARM_AND_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_AND_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_AND_REG_IMM8(p, rd, rn, imm8) \ + ARM_AND_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_ANDS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_ANDS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_ANDS_REG_IMM8(p, rd, rn, imm8) \ + ARM_ANDS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _AND_REG_IMM8_COND(rd, rn, imm8, cond) \ + _AND_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _AND_REG_IMM8(rd, rn, imm8) \ + _AND_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _ANDS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _ANDS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _ANDS_REG_IMM8(rd, rn, imm8) \ + _ANDS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn AND Rm */ +#define ARM_AND_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_AND, rd, rn, rm, cond) +#define ARM_AND_REG_REG(p, rd, rn, rm) \ + ARM_AND_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_ANDS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_AND, rd, rn, rm, cond) +#define ARM_ANDS_REG_REG(p, rd, rn, rm) \ + ARM_ANDS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _AND_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_AND, rd, rn, rm, cond) +#define _AND_REG_REG(rd, rn, rm) \ + _AND_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _ANDS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_AND, rd, rn, rm, cond) +#define _ANDS_REG_REG(rd, rn, rm) \ + _ANDS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn AND (Rm imm_shift) */ +#define ARM_AND_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_AND, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_AND_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_AND_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_ANDS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_AND, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_ANDS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_ANDS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _AND_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_AND, rd, rn, rm, shift_type, imm_shift, cond) +#define _AND_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _AND_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _ANDS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_AND, rd, rn, rm, shift_type, imm_shift, cond) +#define _ANDS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _ANDS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn AND (Rm Rs) */ +#define ARM_AND_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_AND, rd, rn, rm, shift_t, rs, cond) +#define ARM_AND_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_AND_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_ANDS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_AND, rd, rn, rm, shift_t, rs, cond) +#define ARM_ANDS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_ANDS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _AND_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_AND, rd, rn, rm, shift_t, rs, cond) +#define _AND_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _AND_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _ANDS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_AND, rd, rn, rm, shift_t, rs, cond) +#define _ANDS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _ANDS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- EOR -- */ + +/* Rd := Rn EOR (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_EOR_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_EOR, rd, rn, imm8, rot, cond) +#define ARM_EOR_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_EOR_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_EORS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_EOR, rd, rn, imm8, rot, cond) +#define ARM_EORS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_EORS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _EOR_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_EOR, rd, rn, imm8, rot, cond) +#define _EOR_REG_IMM(rd, rn, imm8, rot) \ + _EOR_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _EORS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_EOR, rd, rn, imm8, rot, cond) +#define _EORS_REG_IMM(rd, rn, imm8, rot) \ + _EORS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn EOR imm8 */ +#define ARM_EOR_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_EOR_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_EOR_REG_IMM8(p, rd, rn, imm8) \ + ARM_EOR_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_EORS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_EORS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_EORS_REG_IMM8(p, rd, rn, imm8) \ + ARM_EORS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _EOR_REG_IMM8_COND(rd, rn, imm8, cond) \ + _EOR_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _EOR_REG_IMM8(rd, rn, imm8) \ + _EOR_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _EORS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _EORS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _EORS_REG_IMM8(rd, rn, imm8) \ + _EORS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn EOR Rm */ +#define ARM_EOR_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_EOR, rd, rn, rm, cond) +#define ARM_EOR_REG_REG(p, rd, rn, rm) \ + ARM_EOR_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_EORS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_EOR, rd, rn, rm, cond) +#define ARM_EORS_REG_REG(p, rd, rn, rm) \ + ARM_EORS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _EOR_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_EOR, rd, rn, rm, cond) +#define _EOR_REG_REG(rd, rn, rm) \ + _EOR_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _EORS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_EOR, rd, rn, rm, cond) +#define _EORS_REG_REG(rd, rn, rm) \ + _EORS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn EOR (Rm imm_shift) */ +#define ARM_EOR_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_EOR, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_EOR_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_EOR_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_EORS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_EOR, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_EORS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_EORS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _EOR_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_EOR, rd, rn, rm, shift_type, imm_shift, cond) +#define _EOR_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _EOR_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _EORS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_EOR, rd, rn, rm, shift_type, imm_shift, cond) +#define _EORS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _EORS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn EOR (Rm Rs) */ +#define ARM_EOR_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_EOR, rd, rn, rm, shift_t, rs, cond) +#define ARM_EOR_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_EOR_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_EORS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_EOR, rd, rn, rm, shift_t, rs, cond) +#define ARM_EORS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_EORS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _EOR_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_EOR, rd, rn, rm, shift_t, rs, cond) +#define _EOR_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _EOR_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _EORS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_EOR, rd, rn, rm, shift_t, rs, cond) +#define _EORS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _EORS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- SUB -- */ + +/* Rd := Rn SUB (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_SUB_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_SUB, rd, rn, imm8, rot, cond) +#define ARM_SUB_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_SUB_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_SUBS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_SUB, rd, rn, imm8, rot, cond) +#define ARM_SUBS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_SUBS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SUB_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_SUB, rd, rn, imm8, rot, cond) +#define _SUB_REG_IMM(rd, rn, imm8, rot) \ + _SUB_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _SUBS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_SUB, rd, rn, imm8, rot, cond) +#define _SUBS_REG_IMM(rd, rn, imm8, rot) \ + _SUBS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn SUB imm8 */ +#define ARM_SUB_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_SUB_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_SUB_REG_IMM8(p, rd, rn, imm8) \ + ARM_SUB_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_SUBS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_SUBS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_SUBS_REG_IMM8(p, rd, rn, imm8) \ + ARM_SUBS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SUB_REG_IMM8_COND(rd, rn, imm8, cond) \ + _SUB_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _SUB_REG_IMM8(rd, rn, imm8) \ + _SUB_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _SUBS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _SUBS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _SUBS_REG_IMM8(rd, rn, imm8) \ + _SUBS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn SUB Rm */ +#define ARM_SUB_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_SUB, rd, rn, rm, cond) +#define ARM_SUB_REG_REG(p, rd, rn, rm) \ + ARM_SUB_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_SUBS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_SUB, rd, rn, rm, cond) +#define ARM_SUBS_REG_REG(p, rd, rn, rm) \ + ARM_SUBS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SUB_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_SUB, rd, rn, rm, cond) +#define _SUB_REG_REG(rd, rn, rm) \ + _SUB_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _SUBS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_SUB, rd, rn, rm, cond) +#define _SUBS_REG_REG(rd, rn, rm) \ + _SUBS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn SUB (Rm imm_shift) */ +#define ARM_SUB_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_SUB, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_SUB_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_SUB_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_SUBS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_SUB, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_SUBS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_SUBS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SUB_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_SUB, rd, rn, rm, shift_type, imm_shift, cond) +#define _SUB_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _SUB_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _SUBS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_SUB, rd, rn, rm, shift_type, imm_shift, cond) +#define _SUBS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _SUBS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn SUB (Rm Rs) */ +#define ARM_SUB_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_SUB, rd, rn, rm, shift_t, rs, cond) +#define ARM_SUB_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_SUB_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_SUBS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_SUB, rd, rn, rm, shift_t, rs, cond) +#define ARM_SUBS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_SUBS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SUB_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_SUB, rd, rn, rm, shift_t, rs, cond) +#define _SUB_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _SUB_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _SUBS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_SUB, rd, rn, rm, shift_t, rs, cond) +#define _SUBS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _SUBS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- RSB -- */ + +/* Rd := Rn RSB (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_RSB_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_RSB, rd, rn, imm8, rot, cond) +#define ARM_RSB_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_RSB_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_RSBS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_RSB, rd, rn, imm8, rot, cond) +#define ARM_RSBS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_RSBS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSB_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_RSB, rd, rn, imm8, rot, cond) +#define _RSB_REG_IMM(rd, rn, imm8, rot) \ + _RSB_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _RSBS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_RSB, rd, rn, imm8, rot, cond) +#define _RSBS_REG_IMM(rd, rn, imm8, rot) \ + _RSBS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn RSB imm8 */ +#define ARM_RSB_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_RSB_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_RSB_REG_IMM8(p, rd, rn, imm8) \ + ARM_RSB_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_RSBS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_RSBS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_RSBS_REG_IMM8(p, rd, rn, imm8) \ + ARM_RSBS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSB_REG_IMM8_COND(rd, rn, imm8, cond) \ + _RSB_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _RSB_REG_IMM8(rd, rn, imm8) \ + _RSB_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _RSBS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _RSBS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _RSBS_REG_IMM8(rd, rn, imm8) \ + _RSBS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn RSB Rm */ +#define ARM_RSB_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_RSB, rd, rn, rm, cond) +#define ARM_RSB_REG_REG(p, rd, rn, rm) \ + ARM_RSB_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_RSBS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_RSB, rd, rn, rm, cond) +#define ARM_RSBS_REG_REG(p, rd, rn, rm) \ + ARM_RSBS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSB_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_RSB, rd, rn, rm, cond) +#define _RSB_REG_REG(rd, rn, rm) \ + _RSB_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _RSBS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_RSB, rd, rn, rm, cond) +#define _RSBS_REG_REG(rd, rn, rm) \ + _RSBS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn RSB (Rm imm_shift) */ +#define ARM_RSB_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_RSB, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_RSB_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_RSB_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_RSBS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_RSB, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_RSBS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_RSBS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSB_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_RSB, rd, rn, rm, shift_type, imm_shift, cond) +#define _RSB_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _RSB_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _RSBS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_RSB, rd, rn, rm, shift_type, imm_shift, cond) +#define _RSBS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _RSBS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn RSB (Rm Rs) */ +#define ARM_RSB_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_RSB, rd, rn, rm, shift_t, rs, cond) +#define ARM_RSB_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_RSB_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_RSBS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_RSB, rd, rn, rm, shift_t, rs, cond) +#define ARM_RSBS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_RSBS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSB_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_RSB, rd, rn, rm, shift_t, rs, cond) +#define _RSB_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _RSB_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _RSBS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_RSB, rd, rn, rm, shift_t, rs, cond) +#define _RSBS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _RSBS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- ADD -- */ + +/* Rd := Rn ADD (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_ADD_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_ADD, rd, rn, imm8, rot, cond) +#define ARM_ADD_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_ADD_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_ADDS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_ADD, rd, rn, imm8, rot, cond) +#define ARM_ADDS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_ADDS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADD_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_ADD, rd, rn, imm8, rot, cond) +#define _ADD_REG_IMM(rd, rn, imm8, rot) \ + _ADD_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _ADDS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_ADD, rd, rn, imm8, rot, cond) +#define _ADDS_REG_IMM(rd, rn, imm8, rot) \ + _ADDS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn ADD imm8 */ +#define ARM_ADD_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_ADD_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_ADD_REG_IMM8(p, rd, rn, imm8) \ + ARM_ADD_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_ADDS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_ADDS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_ADDS_REG_IMM8(p, rd, rn, imm8) \ + ARM_ADDS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADD_REG_IMM8_COND(rd, rn, imm8, cond) \ + _ADD_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _ADD_REG_IMM8(rd, rn, imm8) \ + _ADD_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _ADDS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _ADDS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _ADDS_REG_IMM8(rd, rn, imm8) \ + _ADDS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn ADD Rm */ +#define ARM_ADD_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_ADD, rd, rn, rm, cond) +#define ARM_ADD_REG_REG(p, rd, rn, rm) \ + ARM_ADD_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_ADDS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_ADD, rd, rn, rm, cond) +#define ARM_ADDS_REG_REG(p, rd, rn, rm) \ + ARM_ADDS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADD_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_ADD, rd, rn, rm, cond) +#define _ADD_REG_REG(rd, rn, rm) \ + _ADD_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _ADDS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_ADD, rd, rn, rm, cond) +#define _ADDS_REG_REG(rd, rn, rm) \ + _ADDS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn ADD (Rm imm_shift) */ +#define ARM_ADD_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_ADD, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_ADD_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_ADD_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_ADDS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_ADD, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_ADDS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_ADDS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADD_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_ADD, rd, rn, rm, shift_type, imm_shift, cond) +#define _ADD_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _ADD_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _ADDS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_ADD, rd, rn, rm, shift_type, imm_shift, cond) +#define _ADDS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _ADDS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn ADD (Rm Rs) */ +#define ARM_ADD_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_ADD, rd, rn, rm, shift_t, rs, cond) +#define ARM_ADD_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_ADD_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_ADDS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_ADD, rd, rn, rm, shift_t, rs, cond) +#define ARM_ADDS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_ADDS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADD_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_ADD, rd, rn, rm, shift_t, rs, cond) +#define _ADD_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _ADD_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _ADDS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_ADD, rd, rn, rm, shift_t, rs, cond) +#define _ADDS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _ADDS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- ADC -- */ + +/* Rd := Rn ADC (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_ADC_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_ADC, rd, rn, imm8, rot, cond) +#define ARM_ADC_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_ADC_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_ADCS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_ADC, rd, rn, imm8, rot, cond) +#define ARM_ADCS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_ADCS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADC_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_ADC, rd, rn, imm8, rot, cond) +#define _ADC_REG_IMM(rd, rn, imm8, rot) \ + _ADC_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _ADCS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_ADC, rd, rn, imm8, rot, cond) +#define _ADCS_REG_IMM(rd, rn, imm8, rot) \ + _ADCS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn ADC imm8 */ +#define ARM_ADC_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_ADC_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_ADC_REG_IMM8(p, rd, rn, imm8) \ + ARM_ADC_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_ADCS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_ADCS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_ADCS_REG_IMM8(p, rd, rn, imm8) \ + ARM_ADCS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADC_REG_IMM8_COND(rd, rn, imm8, cond) \ + _ADC_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _ADC_REG_IMM8(rd, rn, imm8) \ + _ADC_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _ADCS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _ADCS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _ADCS_REG_IMM8(rd, rn, imm8) \ + _ADCS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn ADC Rm */ +#define ARM_ADC_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_ADC, rd, rn, rm, cond) +#define ARM_ADC_REG_REG(p, rd, rn, rm) \ + ARM_ADC_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_ADCS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_ADC, rd, rn, rm, cond) +#define ARM_ADCS_REG_REG(p, rd, rn, rm) \ + ARM_ADCS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADC_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_ADC, rd, rn, rm, cond) +#define _ADC_REG_REG(rd, rn, rm) \ + _ADC_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _ADCS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_ADC, rd, rn, rm, cond) +#define _ADCS_REG_REG(rd, rn, rm) \ + _ADCS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn ADC (Rm imm_shift) */ +#define ARM_ADC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_ADC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_ADC_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_ADC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_ADCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_ADC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_ADCS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_ADCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_ADC, rd, rn, rm, shift_type, imm_shift, cond) +#define _ADC_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _ADC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _ADCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_ADC, rd, rn, rm, shift_type, imm_shift, cond) +#define _ADCS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _ADCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn ADC (Rm Rs) */ +#define ARM_ADC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_ADC, rd, rn, rm, shift_t, rs, cond) +#define ARM_ADC_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_ADC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_ADCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_ADC, rd, rn, rm, shift_t, rs, cond) +#define ARM_ADCS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_ADCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ADC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_ADC, rd, rn, rm, shift_t, rs, cond) +#define _ADC_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _ADC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _ADCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_ADC, rd, rn, rm, shift_t, rs, cond) +#define _ADCS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _ADCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- SBC -- */ + +/* Rd := Rn SBC (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_SBC_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_SBC, rd, rn, imm8, rot, cond) +#define ARM_SBC_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_SBC_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_SBCS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_SBC, rd, rn, imm8, rot, cond) +#define ARM_SBCS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_SBCS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SBC_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_SBC, rd, rn, imm8, rot, cond) +#define _SBC_REG_IMM(rd, rn, imm8, rot) \ + _SBC_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _SBCS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_SBC, rd, rn, imm8, rot, cond) +#define _SBCS_REG_IMM(rd, rn, imm8, rot) \ + _SBCS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn SBC imm8 */ +#define ARM_SBC_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_SBC_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_SBC_REG_IMM8(p, rd, rn, imm8) \ + ARM_SBC_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_SBCS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_SBCS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_SBCS_REG_IMM8(p, rd, rn, imm8) \ + ARM_SBCS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SBC_REG_IMM8_COND(rd, rn, imm8, cond) \ + _SBC_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _SBC_REG_IMM8(rd, rn, imm8) \ + _SBC_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _SBCS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _SBCS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _SBCS_REG_IMM8(rd, rn, imm8) \ + _SBCS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn SBC Rm */ +#define ARM_SBC_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_SBC, rd, rn, rm, cond) +#define ARM_SBC_REG_REG(p, rd, rn, rm) \ + ARM_SBC_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_SBCS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_SBC, rd, rn, rm, cond) +#define ARM_SBCS_REG_REG(p, rd, rn, rm) \ + ARM_SBCS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SBC_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_SBC, rd, rn, rm, cond) +#define _SBC_REG_REG(rd, rn, rm) \ + _SBC_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _SBCS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_SBC, rd, rn, rm, cond) +#define _SBCS_REG_REG(rd, rn, rm) \ + _SBCS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn SBC (Rm imm_shift) */ +#define ARM_SBC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_SBC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_SBC_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_SBC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_SBCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_SBC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_SBCS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_SBCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SBC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_SBC, rd, rn, rm, shift_type, imm_shift, cond) +#define _SBC_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _SBC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _SBCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_SBC, rd, rn, rm, shift_type, imm_shift, cond) +#define _SBCS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _SBCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn SBC (Rm Rs) */ +#define ARM_SBC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_SBC, rd, rn, rm, shift_t, rs, cond) +#define ARM_SBC_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_SBC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_SBCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_SBC, rd, rn, rm, shift_t, rs, cond) +#define ARM_SBCS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_SBCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _SBC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_SBC, rd, rn, rm, shift_t, rs, cond) +#define _SBC_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _SBC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _SBCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_SBC, rd, rn, rm, shift_t, rs, cond) +#define _SBCS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _SBCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- RSC -- */ + +/* Rd := Rn RSC (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_RSC_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_RSC, rd, rn, imm8, rot, cond) +#define ARM_RSC_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_RSC_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_RSCS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_RSC, rd, rn, imm8, rot, cond) +#define ARM_RSCS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_RSCS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSC_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_RSC, rd, rn, imm8, rot, cond) +#define _RSC_REG_IMM(rd, rn, imm8, rot) \ + _RSC_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _RSCS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_RSC, rd, rn, imm8, rot, cond) +#define _RSCS_REG_IMM(rd, rn, imm8, rot) \ + _RSCS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn RSC imm8 */ +#define ARM_RSC_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_RSC_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_RSC_REG_IMM8(p, rd, rn, imm8) \ + ARM_RSC_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_RSCS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_RSCS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_RSCS_REG_IMM8(p, rd, rn, imm8) \ + ARM_RSCS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSC_REG_IMM8_COND(rd, rn, imm8, cond) \ + _RSC_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _RSC_REG_IMM8(rd, rn, imm8) \ + _RSC_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _RSCS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _RSCS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _RSCS_REG_IMM8(rd, rn, imm8) \ + _RSCS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn RSC Rm */ +#define ARM_RSC_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_RSC, rd, rn, rm, cond) +#define ARM_RSC_REG_REG(p, rd, rn, rm) \ + ARM_RSC_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_RSCS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_RSC, rd, rn, rm, cond) +#define ARM_RSCS_REG_REG(p, rd, rn, rm) \ + ARM_RSCS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSC_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_RSC, rd, rn, rm, cond) +#define _RSC_REG_REG(rd, rn, rm) \ + _RSC_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _RSCS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_RSC, rd, rn, rm, cond) +#define _RSCS_REG_REG(rd, rn, rm) \ + _RSCS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn RSC (Rm imm_shift) */ +#define ARM_RSC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_RSC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_RSC_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_RSC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_RSCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_RSC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_RSCS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_RSCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_RSC, rd, rn, rm, shift_type, imm_shift, cond) +#define _RSC_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _RSC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _RSCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_RSC, rd, rn, rm, shift_type, imm_shift, cond) +#define _RSCS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _RSCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn RSC (Rm Rs) */ +#define ARM_RSC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_RSC, rd, rn, rm, shift_t, rs, cond) +#define ARM_RSC_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_RSC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_RSCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_RSC, rd, rn, rm, shift_t, rs, cond) +#define ARM_RSCS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_RSCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _RSC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_RSC, rd, rn, rm, shift_t, rs, cond) +#define _RSC_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _RSC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _RSCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_RSC, rd, rn, rm, shift_t, rs, cond) +#define _RSCS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _RSCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- ORR -- */ + +/* Rd := Rn ORR (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_ORR_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_ORR, rd, rn, imm8, rot, cond) +#define ARM_ORR_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_ORR_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_ORRS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_ORR, rd, rn, imm8, rot, cond) +#define ARM_ORRS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_ORRS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ORR_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_ORR, rd, rn, imm8, rot, cond) +#define _ORR_REG_IMM(rd, rn, imm8, rot) \ + _ORR_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _ORRS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_ORR, rd, rn, imm8, rot, cond) +#define _ORRS_REG_IMM(rd, rn, imm8, rot) \ + _ORRS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn ORR imm8 */ +#define ARM_ORR_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_ORR_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_ORR_REG_IMM8(p, rd, rn, imm8) \ + ARM_ORR_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_ORRS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_ORRS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_ORRS_REG_IMM8(p, rd, rn, imm8) \ + ARM_ORRS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ORR_REG_IMM8_COND(rd, rn, imm8, cond) \ + _ORR_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _ORR_REG_IMM8(rd, rn, imm8) \ + _ORR_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _ORRS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _ORRS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _ORRS_REG_IMM8(rd, rn, imm8) \ + _ORRS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn ORR Rm */ +#define ARM_ORR_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_ORR, rd, rn, rm, cond) +#define ARM_ORR_REG_REG(p, rd, rn, rm) \ + ARM_ORR_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_ORRS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_ORR, rd, rn, rm, cond) +#define ARM_ORRS_REG_REG(p, rd, rn, rm) \ + ARM_ORRS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ORR_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_ORR, rd, rn, rm, cond) +#define _ORR_REG_REG(rd, rn, rm) \ + _ORR_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _ORRS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_ORR, rd, rn, rm, cond) +#define _ORRS_REG_REG(rd, rn, rm) \ + _ORRS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn ORR (Rm imm_shift) */ +#define ARM_ORR_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_ORR, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_ORR_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_ORR_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_ORRS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_ORR, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_ORRS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_ORRS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ORR_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_ORR, rd, rn, rm, shift_type, imm_shift, cond) +#define _ORR_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _ORR_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _ORRS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_ORR, rd, rn, rm, shift_type, imm_shift, cond) +#define _ORRS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _ORRS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn ORR (Rm Rs) */ +#define ARM_ORR_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_ORR, rd, rn, rm, shift_t, rs, cond) +#define ARM_ORR_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_ORR_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_ORRS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_ORR, rd, rn, rm, shift_t, rs, cond) +#define ARM_ORRS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_ORRS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _ORR_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_ORR, rd, rn, rm, shift_t, rs, cond) +#define _ORR_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _ORR_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _ORRS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_ORR, rd, rn, rm, shift_t, rs, cond) +#define _ORRS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _ORRS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + +/* -- BIC -- */ + +/* Rd := Rn BIC (imm8 ROR rot) ; rot is power of 2 */ +#define ARM_BIC_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_BIC, rd, rn, imm8, rot, cond) +#define ARM_BIC_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_BIC_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_BICS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_BIC, rd, rn, imm8, rot, cond) +#define ARM_BICS_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_BICS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _BIC_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_BIC, rd, rn, imm8, rot, cond) +#define _BIC_REG_IMM(rd, rn, imm8, rot) \ + _BIC_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _BICS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_BIC, rd, rn, imm8, rot, cond) +#define _BICS_REG_IMM(rd, rn, imm8, rot) \ + _BICS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn BIC imm8 */ +#define ARM_BIC_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_BIC_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_BIC_REG_IMM8(p, rd, rn, imm8) \ + ARM_BIC_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_BICS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_BICS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_BICS_REG_IMM8(p, rd, rn, imm8) \ + ARM_BICS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _BIC_REG_IMM8_COND(rd, rn, imm8, cond) \ + _BIC_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _BIC_REG_IMM8(rd, rn, imm8) \ + _BIC_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _BICS_REG_IMM8_COND(rd, rn, imm8, cond) \ + _BICS_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _BICS_REG_IMM8(rd, rn, imm8) \ + _BICS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn BIC Rm */ +#define ARM_BIC_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_BIC, rd, rn, rm, cond) +#define ARM_BIC_REG_REG(p, rd, rn, rm) \ + ARM_BIC_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_BICS_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_BIC, rd, rn, rm, cond) +#define ARM_BICS_REG_REG(p, rd, rn, rm) \ + ARM_BICS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _BIC_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_BIC, rd, rn, rm, cond) +#define _BIC_REG_REG(rd, rn, rm) \ + _BIC_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _BICS_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_BIC, rd, rn, rm, cond) +#define _BICS_REG_REG(rd, rn, rm) \ + _BICS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn BIC (Rm imm_shift) */ +#define ARM_BIC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_BIC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_BIC_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_BIC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_BICS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_BIC, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_BICS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_BICS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _BIC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_BIC, rd, rn, rm, shift_type, imm_shift, cond) +#define _BIC_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _BIC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _BICS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_BIC, rd, rn, rm, shift_type, imm_shift, cond) +#define _BICS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _BICS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn BIC (Rm Rs) */ +#define ARM_BIC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_BIC, rd, rn, rm, shift_t, rs, cond) +#define ARM_BIC_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_BIC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_BICS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_BIC, rd, rn, rm, shift_t, rs, cond) +#define ARM_BICS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_BICS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _BIC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_BIC, rd, rn, rm, shift_t, rs, cond) +#define _BIC_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _BIC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _BICS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_BIC, rd, rn, rm, shift_t, rs, cond) +#define _BICS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _BICS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + + + + + +/* DPIs, comparison */ + +/* PSR := TST Rn, (imm8 ROR 2*rot) */ +#define ARM_TST_REG_IMM_COND(p, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_TST, 0, rn, imm8, rot, cond) +#define ARM_TST_REG_IMM(p, rn, imm8, rot) \ + ARM_TST_REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TST_REG_IMM_COND(rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_TST, 0, rn, imm8, rot, cond) +#define _TST_REG_IMM(rn, imm8, rot) \ + _TST_REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) +#endif + + +/* PSR := TST Rn, imm8 */ +#define ARM_TST_REG_IMM8_COND(p, rn, imm8, cond) \ + ARM_TST_REG_IMM_COND(p, rn, imm8, 0, cond) +#define ARM_TST_REG_IMM8(p, rn, imm8) \ + ARM_TST_REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TST_REG_IMM8_COND(rn, imm8, cond) \ + _TST_REG_IMM_COND(rn, imm8, 0, cond) +#define _TST_REG_IMM8(rn, imm8) \ + _TST_REG_IMM8_COND(rn, imm8, ARMCOND_AL) +#endif + + +/* PSR := TST Rn, Rm */ +#define ARM_TST_REG_REG_COND(p, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_TST, 0, rn, rm, cond) +#define ARM_TST_REG_REG(p, rn, rm) \ + ARM_TST_REG_REG_COND(p, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TST_REG_REG_COND(rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_TST, 0, rn, rm, cond) +#define _TST_REG_REG(rn, rm) \ + _TST_REG_REG_COND(rn, rm, ARMCOND_AL) +#endif + + +/* PSR := TST Rn, (Rm imm8) */ +#define ARM_TST_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_TST, 0, rn, rm, shift_type, imm_shift, cond) +#define ARM_TST_REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ + ARM_TST_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TST_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_TST, 0, rn, rm, shift_type, imm_shift, cond) +#define _TST_REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ + _TST_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* PSR := TEQ Rn, (imm8 ROR 2*rot) */ +#define ARM_TEQ_REG_IMM_COND(p, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_TEQ, 0, rn, imm8, rot, cond) +#define ARM_TEQ_REG_IMM(p, rn, imm8, rot) \ + ARM_TEQ_REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TEQ_REG_IMM_COND(rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_TEQ, 0, rn, imm8, rot, cond) +#define _TEQ_REG_IMM(rn, imm8, rot) \ + _TEQ_REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) +#endif + + +/* PSR := TEQ Rn, imm8 */ +#define ARM_TEQ_REG_IMM8_COND(p, rn, imm8, cond) \ + ARM_TEQ_REG_IMM_COND(p, rn, imm8, 0, cond) +#define ARM_TEQ_REG_IMM8(p, rn, imm8) \ + ARM_TEQ_REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TEQ_REG_IMM8_COND(rn, imm8, cond) \ + _TEQ_REG_IMM_COND(rn, imm8, 0, cond) +#define _TEQ_REG_IMM8(rn, imm8) \ + _TEQ_REG_IMM8_COND(rn, imm8, ARMCOND_AL) +#endif + + +/* PSR := TEQ Rn, Rm */ +#define ARM_TEQ_REG_REG_COND(p, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_TEQ, 0, rn, rm, cond) +#define ARM_TEQ_REG_REG(p, rn, rm) \ + ARM_TEQ_REG_REG_COND(p, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TEQ_REG_REG_COND(rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_TEQ, 0, rn, rm, cond) +#define _TEQ_REG_REG(rn, rm) \ + _TEQ_REG_REG_COND(rn, rm, ARMCOND_AL) +#endif + + +/* PSR := TEQ Rn, (Rm imm8) */ +#define ARM_TEQ_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_TEQ, 0, rn, rm, shift_type, imm_shift, cond) +#define ARM_TEQ_REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ + ARM_TEQ_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _TEQ_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_TEQ, 0, rn, rm, shift_type, imm_shift, cond) +#define _TEQ_REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ + _TEQ_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* PSR := CMP Rn, (imm8 ROR 2*rot) */ +#define ARM_CMP_REG_IMM_COND(p, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_CMP, 0, rn, imm8, rot, cond) +#define ARM_CMP_REG_IMM(p, rn, imm8, rot) \ + ARM_CMP_REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMP_REG_IMM_COND(rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_CMP, 0, rn, imm8, rot, cond) +#define _CMP_REG_IMM(rn, imm8, rot) \ + _CMP_REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) +#endif + + +/* PSR := CMP Rn, imm8 */ +#define ARM_CMP_REG_IMM8_COND(p, rn, imm8, cond) \ + ARM_CMP_REG_IMM_COND(p, rn, imm8, 0, cond) +#define ARM_CMP_REG_IMM8(p, rn, imm8) \ + ARM_CMP_REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMP_REG_IMM8_COND(rn, imm8, cond) \ + _CMP_REG_IMM_COND(rn, imm8, 0, cond) +#define _CMP_REG_IMM8(rn, imm8) \ + _CMP_REG_IMM8_COND(rn, imm8, ARMCOND_AL) +#endif + + +/* PSR := CMP Rn, Rm */ +#define ARM_CMP_REG_REG_COND(p, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_CMP, 0, rn, rm, cond) +#define ARM_CMP_REG_REG(p, rn, rm) \ + ARM_CMP_REG_REG_COND(p, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMP_REG_REG_COND(rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_CMP, 0, rn, rm, cond) +#define _CMP_REG_REG(rn, rm) \ + _CMP_REG_REG_COND(rn, rm, ARMCOND_AL) +#endif + + +/* PSR := CMP Rn, (Rm imm8) */ +#define ARM_CMP_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_CMP, 0, rn, rm, shift_type, imm_shift, cond) +#define ARM_CMP_REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ + ARM_CMP_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMP_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_CMP, 0, rn, rm, shift_type, imm_shift, cond) +#define _CMP_REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ + _CMP_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* PSR := CMN Rn, (imm8 ROR 2*rot) */ +#define ARM_CMN_REG_IMM_COND(p, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_CMN, 0, rn, imm8, rot, cond) +#define ARM_CMN_REG_IMM(p, rn, imm8, rot) \ + ARM_CMN_REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMN_REG_IMM_COND(rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_CMN, 0, rn, imm8, rot, cond) +#define _CMN_REG_IMM(rn, imm8, rot) \ + _CMN_REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) +#endif + + +/* PSR := CMN Rn, imm8 */ +#define ARM_CMN_REG_IMM8_COND(p, rn, imm8, cond) \ + ARM_CMN_REG_IMM_COND(p, rn, imm8, 0, cond) +#define ARM_CMN_REG_IMM8(p, rn, imm8) \ + ARM_CMN_REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMN_REG_IMM8_COND(rn, imm8, cond) \ + _CMN_REG_IMM_COND(rn, imm8, 0, cond) +#define _CMN_REG_IMM8(rn, imm8) \ + _CMN_REG_IMM8_COND(rn, imm8, ARMCOND_AL) +#endif + + +/* PSR := CMN Rn, Rm */ +#define ARM_CMN_REG_REG_COND(p, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_CMN, 0, rn, rm, cond) +#define ARM_CMN_REG_REG(p, rn, rm) \ + ARM_CMN_REG_REG_COND(p, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMN_REG_REG_COND(rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_CMN, 0, rn, rm, cond) +#define _CMN_REG_REG(rn, rm) \ + _CMN_REG_REG_COND(rn, rm, ARMCOND_AL) +#endif + + +/* PSR := CMN Rn, (Rm imm8) */ +#define ARM_CMN_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_CMN, 0, rn, rm, shift_type, imm_shift, cond) +#define ARM_CMN_REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ + ARM_CMN_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define _CMN_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_CMN, 0, rn, rm, shift_type, imm_shift, cond) +#define _CMN_REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ + _CMN_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + + +/* end generated */ + diff --git a/asm/cmp_macros.th b/asm/cmp_macros.th new file mode 100644 index 0000000000..cb2639dec1 --- /dev/null +++ b/asm/cmp_macros.th @@ -0,0 +1,56 @@ +/* PSR := Rn, (imm8 ROR 2*rot) */ +#define ARM__REG_IMM_COND(p, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_, 0, rn, imm8, rot, cond) +#define ARM__REG_IMM(p, rn, imm8, rot) \ + ARM__REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMM_COND(rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_, 0, rn, imm8, rot, cond) +#define __REG_IMM(rn, imm8, rot) \ + __REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) +#endif + + +/* PSR := Rn, imm8 */ +#define ARM__REG_IMM8_COND(p, rn, imm8, cond) \ + ARM__REG_IMM_COND(p, rn, imm8, 0, cond) +#define ARM__REG_IMM8(p, rn, imm8) \ + ARM__REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMM8_COND(rn, imm8, cond) \ + __REG_IMM_COND(rn, imm8, 0, cond) +#define __REG_IMM8(rn, imm8) \ + __REG_IMM8_COND(rn, imm8, ARMCOND_AL) +#endif + + +/* PSR := Rn, Rm */ +#define ARM__REG_REG_COND(p, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_, 0, rn, rm, cond) +#define ARM__REG_REG(p, rn, rm) \ + ARM__REG_REG_COND(p, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_REG_COND(rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_, 0, rn, rm, cond) +#define __REG_REG(rn, rm) \ + __REG_REG_COND(rn, rm, ARMCOND_AL) +#endif + + +/* PSR := Rn, (Rm imm8) */ +#define ARM__REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_, 0, rn, rm, shift_type, imm_shift, cond) +#define ARM__REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ + ARM__REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_, 0, rn, rm, shift_type, imm_shift, cond) +#define __REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ + __REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + diff --git a/asm/dpi_macros.th b/asm/dpi_macros.th new file mode 100644 index 0000000000..be43d1fe72 --- /dev/null +++ b/asm/dpi_macros.th @@ -0,0 +1,112 @@ +/* -- -- */ + +/* Rd := Rn (imm8 ROR rot) ; rot is power of 2 */ +#define ARM__REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_, rd, rn, imm8, rot, cond) +#define ARM__REG_IMM(p, rd, rn, imm8, rot) \ + ARM__REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) +#define ARM_S_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_, rd, rn, imm8, rot, cond) +#define ARM_S_REG_IMM(p, rd, rn, imm8, rot) \ + ARM_S_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_, rd, rn, imm8, rot, cond) +#define __REG_IMM(rd, rn, imm8, rot) \ + __REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#define _S_REG_IMM_COND(rd, rn, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_, rd, rn, imm8, rot, cond) +#define _S_REG_IMM(rd, rn, imm8, rot) \ + _S_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := Rn imm8 */ +#define ARM__REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM__REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM__REG_IMM8(p, rd, rn, imm8) \ + ARM__REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) +#define ARM_S_REG_IMM8_COND(p, rd, rn, imm8, cond) \ + ARM_S_REG_IMM_COND(p, rd, rn, imm8, 0, cond) +#define ARM_S_REG_IMM8(p, rd, rn, imm8) \ + ARM_S_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMM8_COND(rd, rn, imm8, cond) \ + __REG_IMM_COND(rd, rn, imm8, 0, cond) +#define __REG_IMM8(rd, rn, imm8) \ + __REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#define _S_REG_IMM8_COND(rd, rn, imm8, cond) \ + _S_REG_IMM_COND(rd, rn, imm8, 0, cond) +#define _S_REG_IMM8(rd, rn, imm8) \ + _S_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rn Rm */ +#define ARM__REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_, rd, rn, rm, cond) +#define ARM__REG_REG(p, rd, rn, rm) \ + ARM__REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) +#define ARM_S_REG_REG_COND(p, rd, rn, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_, rd, rn, rm, cond) +#define ARM_S_REG_REG(p, rd, rn, rm) \ + ARM_S_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_, rd, rn, rm, cond) +#define __REG_REG(rd, rn, rm) \ + __REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#define _S_REG_REG_COND(rd, rn, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_, rd, rn, rm, cond) +#define _S_REG_REG(rd, rn, rm) \ + _S_REG_REG_COND(rd, rn, rm, ARMCOND_AL) +#endif + + +/* Rd := Rn (Rm imm_shift) */ +#define ARM__REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM__REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM__REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define ARM_S_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_, rd, rn, rm, shift_type, imm_shift, cond) +#define ARM_S_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ + ARM_S_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_, rd, rn, rm, shift_type, imm_shift, cond) +#define __REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + __REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#define _S_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_, rd, rn, rm, shift_type, imm_shift, cond) +#define _S_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ + _S_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + +/* Rd := Rn (Rm Rs) */ +#define ARM__REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_, rd, rn, rm, shift_t, rs, cond) +#define ARM__REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM__REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define ARM_S_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_, rd, rn, rm, shift_t, rs, cond) +#define ARM_S_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ + ARM_S_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_, rd, rn, rm, shift_t, rs, cond) +#define __REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + __REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#define _S_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_, rd, rn, rm, shift_t, rs, cond) +#define _S_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ + _S_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) +#endif + + diff --git a/asm/dpiops.sh b/asm/dpiops.sh new file mode 100755 index 0000000000..d3b93ff52a --- /dev/null +++ b/asm/dpiops.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +OPCODES="AND EOR SUB RSB ADD ADC SBC RSC ORR BIC" +CMP_OPCODES="TST TEQ CMP CMN" +MOV_OPCODES="MOV MVN" + +# $1: opcode list +# $2: template +gen() { + for i in $1; do + sed "s//$i/g" $2.th + done +} + + + +echo -e "/* Macros for DPI ops, auto-generated from template */\n" + +echo -e "\n/* mov/mvn */\n" +gen "$MOV_OPCODES" mov_macros + +echo -e "\n/* DPIs, arithmetic and logical */\n" +gen "$OPCODES" dpi_macros + +echo -e "\n\n" + +echo -e "\n/* DPIs, comparison */\n" +gen "$CMP_OPCODES" cmp_macros + +echo -e "\n/* end generated */\n" diff --git a/asm/fpa_macros.th b/asm/fpa_macros.th new file mode 100644 index 0000000000..036b2a00b9 --- /dev/null +++ b/asm/fpa_macros.th @@ -0,0 +1,15 @@ +/* -- -- */ + + +/* Fd := Rn Rm */ +#define ARM_FPA_D_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_FPA_CPDO_DYADIC(cond,ARM_FPA_,rd,rn,rm,ARM_FPA_ROUND_NEAREST,ARM_FPA_ROUND_DOUBLE)) +#define ARM_FPA_D(p, rd, rn, rm) \ + ARM_FPA_D_COND(p, rd, rn, rm, ARMCOND_AL) + +#define ARM_FPA_S_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_FPA_CPDO_DYADIC(cond,ARM_FPA_,rd,rn,rm,ARM_FPA_ROUND_NEAREST,ARM_FPA_ROUND_SINGLE)) +#define ARM_FPA_S(p, rd, rn, rm) \ + ARM_FPA_S_COND(p, rd, rn, rm, ARMCOND_AL) + + diff --git a/asm/fpam_macros.th b/asm/fpam_macros.th new file mode 100644 index 0000000000..15183c393e --- /dev/null +++ b/asm/fpam_macros.th @@ -0,0 +1,14 @@ +/* -- -- */ + + +/* Fd := Rm */ + +#define ARM_FPA_D_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_FPA_CPDO_MONADIC((cond),ARM_FPA_,(dreg),(sreg),ARM_FPA_ROUND_NEAREST,ARM_FPA_ROUND_DOUBLE)) +#define ARM_FPA_D(p,dreg,sreg) ARM_FPA_D_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_FPA_S_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_FPA_CPDO_MONADIC((cond),ARM_FPA_,(dreg),(sreg),ARM_FPA_ROUND_NEAREST,ARM_FPA_ROUND_SINGLE)) +#define ARM_FPA_S(p,dreg,sreg) ARM_FPA_S_COND(p,dreg,sreg,ARMCOND_AL) + + diff --git a/asm/fpaops.sh b/asm/fpaops.sh new file mode 100755 index 0000000000..be1987683a --- /dev/null +++ b/asm/fpaops.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +DYADIC="ADF MUF SUF RSF DVF RDF POW RPW RMF FML FDV FRD POL" +MONADIC="MVF MNF ABS RND SQT LOG EXP SIN COS TAN ASN ACS ATN URD NRM" + +# $1: opcode list +# $2: template +gen() { + for i in $1; do + sed "s//$i/g" $2.th + done +} + +echo -e "/* Macros for FPA ops, auto-generated from template */\n" + +echo -e "\n/* dyadic */\n" +gen "$DYADIC" fpa_macros + +echo -e "\n/* monadic */\n" +gen "$MONADIC" fpam_macros + +echo -e "\n\n" + +echo -e "\n/* end generated */\n" diff --git a/asm/mov_macros.th b/asm/mov_macros.th new file mode 100644 index 0000000000..6bac29003a --- /dev/null +++ b/asm/mov_macros.th @@ -0,0 +1,121 @@ +/* Rd := imm8 ROR rot */ +#define ARM__REG_IMM_COND(p, reg, imm8, rot, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_, reg, 0, imm8, rot, cond) +#define ARM__REG_IMM(p, reg, imm8, rot) \ + ARM__REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) +/* S */ +#define ARM_S_REG_IMM_COND(p, reg, imm8, rot, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_, reg, 0, imm8, rot, cond) +#define ARM_S_REG_IMM(p, reg, imm8, rot) \ + ARM_S_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMM_COND(reg, imm8, rot, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_, reg, 0, imm8, rot, cond) +#define __REG_IMM(reg, imm8, rot) \ + __REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) +/* S */ +#define _S_REG_IMM_COND(reg, imm8, rot, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_, reg, 0, imm8, rot, cond) +#define _S_REG_IMM(reg, imm8, rot) \ + _S_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) +#endif + + +/* Rd := imm8 */ +#define ARM__REG_IMM8_COND(p, reg, imm8, cond) \ + ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_, reg, 0, imm8, 0, cond) +#define ARM__REG_IMM8(p, reg, imm8) \ + ARM__REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) +/* S */ +#define ARM_S_REG_IMM8_COND(p, reg, imm8, cond) \ + ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_, reg, 0, imm8, 0, cond) +#define ARM_S_REG_IMM8(p, reg, imm8) \ + ARM_S_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMM8_COND(reg, imm8, cond) \ + ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_, reg, 0, imm8, 0, cond) +#define __REG_IMM8(reg, imm8) \ + __REG_IMM8_COND(reg, imm8, ARMCOND_AL) +/* S */ +#define _S_REG_IMM8_COND(reg, imm8, cond) \ + ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_, reg, 0, imm8, 0, cond) +#define _S_REG_IMM8(reg, imm8) \ + _S_REG_IMM8_COND(reg, imm8, ARMCOND_AL) +#endif + + +/* Rd := Rm */ +#define ARM__REG_REG_COND(p, rd, rm, cond) \ + ARM_DPIOP_REG_REG_COND(p, ARMOP_, rd, 0, rm, cond) +#define ARM__REG_REG(p, rd, rm) \ + ARM__REG_REG_COND(p, rd, rm, ARMCOND_AL) +/* S */ +#define ARM_S_REG_REG_COND(p, rd, rm, cond) \ + ARM_DPIOP_S_REG_REG_COND(p, ARMOP_, rd, 0, rm, cond) +#define ARM_S_REG_REG(p, rd, rm) \ + ARM_S_REG_REG_COND(p, rd, rm, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_REG_COND(rd, rm, cond) \ + ARM_IASM_DPIOP_REG_REG_COND(ARMOP_, rd, 0, rm, cond) +#define __REG_REG(rd, rm) \ + __REG_REG_COND(rd, rm, ARMCOND_AL) +/* S */ +#define _S_REG_REG_COND(rd, rm, cond) \ + ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_, rd, 0, rm, cond) +#define _S_REG_REG(rd, rm) \ + _S_REG_REG_COND(rd, rm, ARMCOND_AL) +#endif + + +/* Rd := Rm imm_shift */ +#define ARM__REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_, rd, 0, rm, shift_type, imm_shift, cond) +#define ARM__REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ + ARM__REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) +/* S */ +#define ARM_S_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ + ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_, rd, 0, rm, shift_type, imm_shift, cond) +#define ARM_S_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ + ARM_S_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_, rd, 0, rm, shift_type, imm_shift, cond) +#define __REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ + __REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) +/* S */ +#define _S_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ + ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_, rd, 0, rm, shift_type, imm_shift, cond) +#define _S_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ + _S_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) +#endif + + + +/* Rd := (Rm Rs) */ +#define ARM__REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ + ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_, rd, 0, rm, shift_type, rs, cond) +#define ARM__REG_REGSHIFT(p, rd, rm, shift_type, rs) \ + ARM__REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) +/* S */ +#define ARM_S_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ + ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_, rd, 0, rm, shift_type, rs, cond) +#define ARM_S_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ + ARM_S_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) + +#ifndef ARM_NOIASM +#define __REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_, rd, 0, rm, shift_type, rs, cond) +#define __REG_REGSHIFT(rd, rm, shift_type, rs) \ + __REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) +/* S */ +#define _S_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ + ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_, rd, 0, rm, shift_type, rs, cond) +#define _S_REG_REGSHIFT(rd, rm, shift_type, rs) \ + _S_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) +#endif + + diff --git a/asm/vfp_macros.th b/asm/vfp_macros.th new file mode 100644 index 0000000000..cca67dc8f2 --- /dev/null +++ b/asm/vfp_macros.th @@ -0,0 +1,15 @@ +/* -- -- */ + + +/* Fd := Fn Fm */ +#define ARM_VFP_D_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_DOUBLE,ARM_VFP_,rd,rn,rm)) +#define ARM_VFP_D(p, rd, rn, rm) \ + ARM_VFP_D_COND(p, rd, rn, rm, ARMCOND_AL) + +#define ARM_VFP_S_COND(p, rd, rn, rm, cond) \ + ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_SINGLE,ARM_VFP_,rd,rn,rm)) +#define ARM_VFP_S(p, rd, rn, rm) \ + ARM_VFP_S_COND(p, rd, rn, rm, ARMCOND_AL) + + diff --git a/asm/vfpm_macros.th b/asm/vfpm_macros.th new file mode 100644 index 0000000000..25ad721581 --- /dev/null +++ b/asm/vfpm_macros.th @@ -0,0 +1,14 @@ +/* -- -- */ + + +/* Fd := Fm */ + +#define ARM_D_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_,(dreg),(sreg))) +#define ARM_D(p,dreg,sreg) ARM_D_COND(p,dreg,sreg,ARMCOND_AL) + +#define ARM_S_COND(p,dreg,sreg,cond) \ + ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_,(dreg),(sreg))) +#define ARM_S(p,dreg,sreg) ARM_S_COND(p,dreg,sreg,ARMCOND_AL) + + diff --git a/asm/vfpops.sh b/asm/vfpops.sh new file mode 100755 index 0000000000..bed4a9c80e --- /dev/null +++ b/asm/vfpops.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +DYADIC="ADD SUB MUL NMUL DIV" +MONADIC="CPY ABS NEG SQRT CMP CMPE CMPZ CMPEZ CVT UITO SITO TOUI TOSI TOUIZ TOSIZ" + +# $1: opcode list +# $2: template +gen() { + for i in $1; do + sed "s//$i/g" $2.th + done +} + +echo -e "/* Macros for VFP ops, auto-generated from template */\n" + +echo -e "\n/* dyadic */\n" +gen "$DYADIC" vfp_macros + +echo -e "\n/* monadic */\n" +gen "$MONADIC" vfpm_macros + +echo -e "\n\n" + +echo -e "\n/* end generated */\n" diff --git a/asm/x86-codegen.h b/asm/x86-codegen.h new file mode 100644 index 0000000000..fd2c528c86 --- /dev/null +++ b/asm/x86-codegen.h @@ -0,0 +1,2640 @@ +/* + * x86-codegen.h: Macros for generating x86 code + * + * Authors: + * Paolo Molaro (lupus@ximian.com) + * Intel Corporation (ORP Project) + * Sergey Chaban (serge@wildwestsoftware.com) + * Dietmar Maurer (dietmar@ximian.com) + * Patrik Torstensson + * + * Copyright (C) 2000 Intel Corporation. All rights reserved. + * Copyright (C) 2001, 2002 Ximian, Inc. + */ + +#ifndef X86_H +#define X86_H +#include + +#ifdef __native_client_codegen__ +extern gint8 nacl_align_byte; +#endif /* __native_client_codegen__ */ + + +#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) +#define x86_codegen_pre(inst_ptr_ptr, inst_len) do { mono_nacl_align_inst(inst_ptr_ptr, inst_len); } while (0) +#define x86_call_sequence_pre_val(inst) guint8* _code_start = (inst); +#define x86_call_sequence_post_val(inst) \ + (mono_nacl_align_call(&_code_start, &(inst)), _code_start); +#define x86_call_sequence_pre(inst) x86_call_sequence_pre_val((inst)) +#define x86_call_sequence_post(inst) x86_call_sequence_post_val((inst)) +#else +#define x86_codegen_pre(inst_ptr_ptr, inst_len) do {} while (0) +/* Two variants are needed to avoid warnings */ +#define x86_call_sequence_pre_val(inst) guint8* _code_start = (inst); +#define x86_call_sequence_post_val(inst) _code_start +#define x86_call_sequence_pre(inst) +#define x86_call_sequence_post(inst) +#endif /* __native_client_codegen__ */ + + +/* +// x86 register numbers +*/ +typedef enum { + X86_EAX = 0, + X86_ECX = 1, + X86_EDX = 2, + X86_EBX = 3, + X86_ESP = 4, + X86_EBP = 5, + X86_ESI = 6, + X86_EDI = 7, + X86_NREG +} X86_Reg_No; + +typedef enum { + X86_XMM0, + X86_XMM1, + X86_XMM2, + X86_XMM3, + X86_XMM4, + X86_XMM5, + X86_XMM6, + X86_XMM7, + X86_XMM_NREG +} X86_XMM_Reg_No; + +/* +// opcodes for alu instructions +*/ +typedef enum { + X86_ADD = 0, + X86_OR = 1, + X86_ADC = 2, + X86_SBB = 3, + X86_AND = 4, + X86_SUB = 5, + X86_XOR = 6, + X86_CMP = 7, + X86_NALU +} X86_ALU_Opcode; +/* +// opcodes for shift instructions +*/ +typedef enum { + X86_SHLD, + X86_SHLR, + X86_ROL = 0, + X86_ROR = 1, + X86_RCL = 2, + X86_RCR = 3, + X86_SHL = 4, + X86_SHR = 5, + X86_SAR = 7, + X86_NSHIFT = 8 +} X86_Shift_Opcode; +/* +// opcodes for floating-point instructions +*/ +typedef enum { + X86_FADD = 0, + X86_FMUL = 1, + X86_FCOM = 2, + X86_FCOMP = 3, + X86_FSUB = 4, + X86_FSUBR = 5, + X86_FDIV = 6, + X86_FDIVR = 7, + X86_NFP = 8 +} X86_FP_Opcode; +/* +// integer conditions codes +*/ +typedef enum { + X86_CC_EQ = 0, X86_CC_E = 0, X86_CC_Z = 0, + X86_CC_NE = 1, X86_CC_NZ = 1, + X86_CC_LT = 2, X86_CC_B = 2, X86_CC_C = 2, X86_CC_NAE = 2, + X86_CC_LE = 3, X86_CC_BE = 3, X86_CC_NA = 3, + X86_CC_GT = 4, X86_CC_A = 4, X86_CC_NBE = 4, + X86_CC_GE = 5, X86_CC_AE = 5, X86_CC_NB = 5, X86_CC_NC = 5, + X86_CC_LZ = 6, X86_CC_S = 6, + X86_CC_GEZ = 7, X86_CC_NS = 7, + X86_CC_P = 8, X86_CC_PE = 8, + X86_CC_NP = 9, X86_CC_PO = 9, + X86_CC_O = 10, + X86_CC_NO = 11, + X86_NCC +} X86_CC; + +/* FP status */ +enum { + X86_FP_C0 = 0x100, + X86_FP_C1 = 0x200, + X86_FP_C2 = 0x400, + X86_FP_C3 = 0x4000, + X86_FP_CC_MASK = 0x4500 +}; + +/* FP control word */ +enum { + X86_FPCW_INVOPEX_MASK = 0x1, + X86_FPCW_DENOPEX_MASK = 0x2, + X86_FPCW_ZERODIV_MASK = 0x4, + X86_FPCW_OVFEX_MASK = 0x8, + X86_FPCW_UNDFEX_MASK = 0x10, + X86_FPCW_PRECEX_MASK = 0x20, + X86_FPCW_PRECC_MASK = 0x300, + X86_FPCW_ROUNDC_MASK = 0xc00, + + /* values for precision control */ + X86_FPCW_PREC_SINGLE = 0, + X86_FPCW_PREC_DOUBLE = 0x200, + X86_FPCW_PREC_EXTENDED = 0x300, + + /* values for rounding control */ + X86_FPCW_ROUND_NEAREST = 0, + X86_FPCW_ROUND_DOWN = 0x400, + X86_FPCW_ROUND_UP = 0x800, + X86_FPCW_ROUND_TOZERO = 0xc00 +}; + +/* +// prefix code +*/ +typedef enum { + X86_LOCK_PREFIX = 0xF0, + X86_REPNZ_PREFIX = 0xF2, + X86_REPZ_PREFIX = 0xF3, + X86_REP_PREFIX = 0xF3, + X86_CS_PREFIX = 0x2E, + X86_SS_PREFIX = 0x36, + X86_DS_PREFIX = 0x3E, + X86_ES_PREFIX = 0x26, + X86_FS_PREFIX = 0x64, + X86_GS_PREFIX = 0x65, + X86_UNLIKELY_PREFIX = 0x2E, + X86_LIKELY_PREFIX = 0x3E, + X86_OPERAND_PREFIX = 0x66, + X86_ADDRESS_PREFIX = 0x67 +} X86_Prefix; + +static const unsigned char +x86_cc_unsigned_map [X86_NCC] = { + 0x74, /* eq */ + 0x75, /* ne */ + 0x72, /* lt */ + 0x76, /* le */ + 0x77, /* gt */ + 0x73, /* ge */ + 0x78, /* lz */ + 0x79, /* gez */ + 0x7a, /* p */ + 0x7b, /* np */ + 0x70, /* o */ + 0x71, /* no */ +}; + +static const unsigned char +x86_cc_signed_map [X86_NCC] = { + 0x74, /* eq */ + 0x75, /* ne */ + 0x7c, /* lt */ + 0x7e, /* le */ + 0x7f, /* gt */ + 0x7d, /* ge */ + 0x78, /* lz */ + 0x79, /* gez */ + 0x7a, /* p */ + 0x7b, /* np */ + 0x70, /* o */ + 0x71, /* no */ +}; + +typedef union { + int val; + unsigned char b [4]; +} x86_imm_buf; + +#define X86_NOBASEREG (-1) + +/* +// bitvector mask for callee-saved registers +*/ +#define X86_ESI_MASK (1<> 6) +#define x86_modrm_reg(modrm) (((modrm) >> 3) & 0x7) +#define x86_modrm_rm(modrm) ((modrm) & 0x7) + +#define x86_address_byte(inst,m,o,r) do { *(inst)++ = ((((m)&0x03)<<6)|(((o)&0x07)<<3)|(((r)&0x07))); } while (0) +#define x86_imm_emit32(inst,imm) \ + do { \ + x86_imm_buf imb; imb.val = (int) (imm); \ + *(inst)++ = imb.b [0]; \ + *(inst)++ = imb.b [1]; \ + *(inst)++ = imb.b [2]; \ + *(inst)++ = imb.b [3]; \ + } while (0) +#define x86_imm_emit16(inst,imm) do { *(short*)(inst) = (imm); (inst) += 2; } while (0) +#define x86_imm_emit8(inst,imm) do { *(inst) = (unsigned char)((imm) & 0xff); ++(inst); } while (0) +#define x86_is_imm8(imm) (((int)(imm) >= -128 && (int)(imm) <= 127)) +#define x86_is_imm16(imm) (((int)(imm) >= -(1<<16) && (int)(imm) <= ((1<<16)-1))) + +#define x86_reg_emit(inst,r,regno) do { x86_address_byte ((inst), 3, (r), (regno)); } while (0) +#define x86_reg8_emit(inst,r,regno,is_rh,is_rnoh) do {x86_address_byte ((inst), 3, (is_rh)?((r)|4):(r), (is_rnoh)?((regno)|4):(regno));} while (0) +#define x86_regp_emit(inst,r,regno) do { x86_address_byte ((inst), 0, (r), (regno)); } while (0) +#define x86_mem_emit(inst,r,disp) do { x86_address_byte ((inst), 0, (r), 5); x86_imm_emit32((inst), (disp)); } while (0) + +#define kMaxMembaseEmitPadding 6 + +#define x86_membase_emit_body(inst,r,basereg,disp) do {\ + if ((basereg) == X86_ESP) { \ + if ((disp) == 0) { \ + x86_address_byte ((inst), 0, (r), X86_ESP); \ + x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ + } else if (x86_is_imm8((disp))) { \ + x86_address_byte ((inst), 1, (r), X86_ESP); \ + x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ + x86_imm_emit8 ((inst), (disp)); \ + } else { \ + x86_address_byte ((inst), 2, (r), X86_ESP); \ + x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + break; \ + } \ + if ((disp) == 0 && (basereg) != X86_EBP) { \ + x86_address_byte ((inst), 0, (r), (basereg)); \ + break; \ + } \ + if (x86_is_imm8((disp))) { \ + x86_address_byte ((inst), 1, (r), (basereg)); \ + x86_imm_emit8 ((inst), (disp)); \ + } else { \ + x86_address_byte ((inst), 2, (r), (basereg)); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + } while (0) + +#if defined(__native_client_codegen__) && defined(TARGET_AMD64) +#define x86_membase_emit(inst,r,basereg,disp) \ + do { \ + amd64_nacl_membase_handler(&(inst), (basereg), (disp), (r)) ; \ + } while (0) +#else /* __default_codegen__ || 32-bit NaCl codegen */ +#define x86_membase_emit(inst,r,basereg,disp) \ + do { \ + x86_membase_emit_body((inst),(r),(basereg),(disp)); \ + } while (0) +#endif + +#define kMaxMemindexEmitPadding 6 + +#define x86_memindex_emit(inst,r,basereg,disp,indexreg,shift) \ + do { \ + if ((basereg) == X86_NOBASEREG) { \ + x86_address_byte ((inst), 0, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), 5); \ + x86_imm_emit32 ((inst), (disp)); \ + } else if ((disp) == 0 && (basereg) != X86_EBP) { \ + x86_address_byte ((inst), 0, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ + } else if (x86_is_imm8((disp))) { \ + x86_address_byte ((inst), 1, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ + x86_imm_emit8 ((inst), (disp)); \ + } else { \ + x86_address_byte ((inst), 2, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + } while (0) + +/* + * target is the position in the code where to jump to: + * target = code; + * .. output loop code... + * x86_mov_reg_imm (code, X86_EAX, 0); + * loop = code; + * x86_loop (code, -1); + * ... finish method + * + * patch displacement + * x86_patch (loop, target); + * + * ins should point at the start of the instruction that encodes a target. + * the instruction is inspected for validity and the correct displacement + * is inserted. + */ +#define x86_do_patch(ins,target) \ + do { \ + unsigned char* pos = (ins) + 1; \ + int disp, size = 0; \ + switch (*(unsigned char*)(ins)) { \ + case 0xe8: case 0xe9: ++size; break; /* call, jump32 */ \ + case 0x0f: if (!(*pos >= 0x70 && *pos <= 0x8f)) assert (0); \ + ++size; ++pos; break; /* prefix for 32-bit disp */ \ + case 0xe0: case 0xe1: case 0xe2: /* loop */ \ + case 0xeb: /* jump8 */ \ + /* conditional jump opcodes */ \ + case 0x70: case 0x71: case 0x72: case 0x73: \ + case 0x74: case 0x75: case 0x76: case 0x77: \ + case 0x78: case 0x79: case 0x7a: case 0x7b: \ + case 0x7c: case 0x7d: case 0x7e: case 0x7f: \ + break; \ + default: assert (0); \ + } \ + disp = (target) - pos; \ + if (size) x86_imm_emit32 (pos, disp - 4); \ + else if (x86_is_imm8 (disp - 1)) x86_imm_emit8 (pos, disp - 1); \ + else assert (0); \ + } while (0) + +#if defined( __native_client_codegen__ ) && defined(TARGET_X86) + +#define x86_skip_nops(inst) \ + do { \ + int in_nop = 0; \ + do { \ + in_nop = 0; \ + if (inst[0] == 0x90) { \ + in_nop = 1; \ + inst += 1; \ + } \ + if (inst[0] == 0x8b && inst[1] == 0xc0) { \ + in_nop = 1; \ + inst += 2; \ + } \ + if (inst[0] == 0x8d && inst[1] == 0x6d \ + && inst[2] == 0x00) { \ + in_nop = 1; \ + inst += 3; \ + } \ + if (inst[0] == 0x8d && inst[1] == 0x64 \ + && inst[2] == 0x24 && inst[3] == 0x00) { \ + in_nop = 1; \ + inst += 4; \ + } \ + /* skip inst+=5 case because it's the 4-byte + 1-byte case */ \ + if (inst[0] == 0x8d && inst[1] == 0xad \ + && inst[2] == 0x00 && inst[3] == 0x00 \ + && inst[4] == 0x00 && inst[5] == 0x00) { \ + in_nop = 1; \ + inst += 6; \ + } \ + if (inst[0] == 0x8d && inst[1] == 0xa4 \ + && inst[2] == 0x24 && inst[3] == 0x00 \ + && inst[4] == 0x00 && inst[5] == 0x00 \ + && inst[6] == 0x00 ) { \ + in_nop = 1; \ + inst += 7; \ + } \ + } while ( in_nop ); \ + } while (0) + +#if defined(__native_client__) +#define x86_patch(ins,target) \ + do { \ + unsigned char* inst = (ins); \ + guint8* new_target = nacl_modify_patch_target((target)); \ + x86_skip_nops((inst)); \ + x86_do_patch((inst), new_target); \ + } while (0) +#else /* __native_client__ */ +#define x86_patch(ins,target) \ + do { \ + unsigned char* inst = (ins); \ + guint8* new_target = (target); \ + x86_skip_nops((inst)); \ + x86_do_patch((inst), new_target); \ + } while (0) +#endif /* __native_client__ */ + +#else +#define x86_patch(ins,target) do { x86_do_patch((ins), (target)); } while (0) +#endif /* __native_client_codegen__ */ + +#ifdef __native_client_codegen__ +/* The breakpoint instruction is illegal in Native Client, although the HALT */ +/* instruction is allowed. The breakpoint is used several places in mini-x86.c */ +/* and exceptions-x86.c. */ +#define x86_breakpoint(inst) \ + do { \ + *(inst)++ = 0xf4; \ + } while (0) +#else +#define x86_breakpoint(inst) \ + do { \ + *(inst)++ = 0xcc; \ + } while (0) +#endif + +#define x86_cld(inst) do { *(inst)++ =(unsigned char)0xfc; } while (0) +#define x86_stosb(inst) do { *(inst)++ =(unsigned char)0xaa; } while (0) +#define x86_stosl(inst) do { *(inst)++ =(unsigned char)0xab; } while (0) +#define x86_stosd(inst) x86_stosl((inst)) +#define x86_movsb(inst) do { *(inst)++ =(unsigned char)0xa4; } while (0) +#define x86_movsl(inst) do { *(inst)++ =(unsigned char)0xa5; } while (0) +#define x86_movsd(inst) x86_movsl((inst)) + +#if defined(__default_codegen__) +#define x86_prefix(inst,p) \ + do { \ + *(inst)++ =(unsigned char) (p); \ + } while (0) +#elif defined(__native_client_codegen__) +#if defined(TARGET_X86) +/* kNaClAlignment - 1 is the max value we can pass into x86_codegen_pre. */ +/* This keeps us from having to call x86_codegen_pre with specific */ +/* knowledge of the size of the instruction that follows it, and */ +/* localizes the alignment requirement to this spot. */ +#define x86_prefix(inst,p) \ + do { \ + x86_codegen_pre(&(inst), kNaClAlignment - 1); \ + *(inst)++ =(unsigned char) (p); \ + } while (0) +#elif defined(TARGET_AMD64) +/* We need to tag any prefixes so we can perform proper membase sandboxing */ +/* See: mini-amd64.c:amd64_nacl_membase_handler for verbose details */ +#define x86_prefix(inst,p) \ + do { \ + amd64_nacl_tag_legacy_prefix((inst)); \ + *(inst)++ =(unsigned char) (p); \ + } while (0) + +#endif /* TARGET_AMD64 */ + +#endif /* __native_client_codegen__ */ + +#define x86_rdtsc(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = 0x0f; \ + *(inst)++ = 0x31; \ + } while (0) + +#define x86_cmpxchg_reg_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xb1; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_cmpxchg_mem_reg(inst,mem,reg) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xb1; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_cmpxchg_membase_reg(inst,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xb1; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_xchg_reg_reg(inst,dreg,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0x86; \ + else \ + *(inst)++ = (unsigned char)0x87; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_xchg_mem_reg(inst,mem,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0x86; \ + else \ + *(inst)++ = (unsigned char)0x87; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_xchg_membase_reg(inst,basereg,disp,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0x86; \ + else \ + *(inst)++ = (unsigned char)0x87; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_xadd_reg_reg(inst,dreg,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0F; \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0xC0; \ + else \ + *(inst)++ = (unsigned char)0xC1; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_xadd_mem_reg(inst,mem,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0F; \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0xC0; \ + else \ + *(inst)++ = (unsigned char)0xC1; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_xadd_membase_reg(inst,basereg,disp,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0F; \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0xC0; \ + else \ + *(inst)++ = (unsigned char)0xC1; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_inc_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_inc_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_inc_reg(inst,reg) do { *(inst)++ = (unsigned char)0x40 + (reg); } while (0) + +#define x86_dec_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 1, (mem)); \ + } while (0) + +#define x86_dec_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 1, (basereg), (disp)); \ + } while (0) + +#define x86_dec_reg(inst,reg) do { *(inst)++ = (unsigned char)0x48 + (reg); } while (0) + +#define x86_not_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 2, (mem)); \ + } while (0) + +#define x86_not_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } while (0) + +#define x86_not_reg(inst,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 2, (reg)); \ + } while (0) + +#define x86_neg_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 3, (mem)); \ + } while (0) + +#define x86_neg_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 3, (basereg), (disp)); \ + } while (0) + +#define x86_neg_reg(inst,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 3, (reg)); \ + } while (0) + +#define x86_nop(inst) do { *(inst)++ = (unsigned char)0x90; } while (0) + +#define x86_alu_reg_imm(inst,opc,reg,imm) \ + do { \ + if ((reg) == X86_EAX) { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ + x86_imm_emit32 ((inst), (imm)); \ + break; \ + } \ + if (x86_is_imm8((imm))) { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x83; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x81; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_alu_mem_imm(inst,opc,mem,imm) \ + do { \ + if (x86_is_imm8((imm))) { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x83; \ + x86_mem_emit ((inst), (opc), (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 10); \ + *(inst)++ = (unsigned char)0x81; \ + x86_mem_emit ((inst), (opc), (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_alu_membase_imm(inst,opc,basereg,disp,imm) \ + do { \ + if (x86_is_imm8((imm))) { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x83; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x81; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_alu_membase8_imm(inst,opc,basereg,disp,imm) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x80; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_alu_mem_reg(inst,opc,mem,reg) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_alu_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_alu_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +/** + * @x86_alu_reg8_reg8: + * Supports ALU operations between two 8-bit registers. + * dreg := dreg opc reg + * X86_Reg_No enum is used to specify the registers. + * Additionally is_*_h flags are used to specify what part + * of a given 32-bit register is used - high (TRUE) or low (FALSE). + * For example: dreg = X86_EAX, is_dreg_h = TRUE -> use AH + */ +#define x86_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 2; \ + x86_reg8_emit ((inst), (dreg), (reg), (is_dreg_h), (is_reg_h)); \ + } while (0) + +#define x86_alu_reg_mem(inst,opc,reg,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_alu_reg_membase(inst,opc,reg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_test_reg_imm(inst,reg,imm) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + if ((reg) == X86_EAX) { \ + *(inst)++ = (unsigned char)0xa9; \ + } else { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 0, (reg)); \ + } \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_test_mem_imm(inst,mem,imm) \ + do { \ + x86_codegen_pre(&(inst), 10); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_test_membase_imm(inst,basereg,disp,imm) \ + do { \ + x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_test_reg_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0x85; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_test_mem_reg(inst,mem,reg) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x85; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_test_membase_reg(inst,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x85; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_shift_reg_imm(inst,opc,reg,imm) \ + do { \ + if ((imm) == 1) { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd1; \ + x86_reg_emit ((inst), (opc), (reg)); \ + } else { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0xc1; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_shift_mem_imm(inst,opc,mem,imm) \ + do { \ + if ((imm) == 1) { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xd1; \ + x86_mem_emit ((inst), (opc), (mem)); \ + } else { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0xc1; \ + x86_mem_emit ((inst), (opc), (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_shift_membase_imm(inst,opc,basereg,disp,imm) \ + do { \ + if ((imm) == 1) { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xd1; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + } else { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xc1; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_shift_reg(inst,opc,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd3; \ + x86_reg_emit ((inst), (opc), (reg)); \ + } while (0) + +#define x86_shift_mem(inst,opc,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xd3; \ + x86_mem_emit ((inst), (opc), (mem)); \ + } while (0) + +#define x86_shift_membase(inst,opc,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xd3; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + } while (0) + +/* + * Multi op shift missing. + */ + +#define x86_shrd_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xad; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_shrd_reg_imm(inst,dreg,reg,shamt) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xac; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + x86_imm_emit8 ((inst), (shamt)); \ + } while (0) + +#define x86_shld_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xa5; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_shld_reg_imm(inst,dreg,reg,shamt) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xa4; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + x86_imm_emit8 ((inst), (shamt)); \ + } while (0) + +/* + * EDX:EAX = EAX * rm + */ +#define x86_mul_reg(inst,reg,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 4 + ((is_signed) ? 1 : 0), (reg)); \ + } while (0) + +#define x86_mul_mem(inst,mem,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 4 + ((is_signed) ? 1 : 0), (mem)); \ + } while (0) + +#define x86_mul_membase(inst,basereg,disp,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 4 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ + } while (0) + +/* + * r *= rm + */ +#define x86_imul_reg_reg(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xaf; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_imul_reg_mem(inst,reg,mem) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xaf; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_imul_reg_membase(inst,reg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xaf; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +/* + * dreg = rm * imm + */ +#define x86_imul_reg_reg_imm(inst,dreg,reg,imm) \ + do { \ + if (x86_is_imm8 ((imm))) { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x6b; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x69; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_imul_reg_mem_imm(inst,reg,mem,imm) \ + do { \ + if (x86_is_imm8 ((imm))) { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x6b; \ + x86_mem_emit ((inst), (reg), (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x69; \ + x86_reg_emit ((inst), (reg), (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_imul_reg_membase_imm(inst,reg,basereg,disp,imm) \ + do { \ + if (x86_is_imm8 ((imm))) { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x6b; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x69; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +/* + * divide EDX:EAX by rm; + * eax = quotient, edx = remainder + */ + +#define x86_div_reg(inst,reg,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 6 + ((is_signed) ? 1 : 0), (reg)); \ + } while (0) + +#define x86_div_mem(inst,mem,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 6 + ((is_signed) ? 1 : 0), (mem)); \ + } while (0) + +#define x86_div_membase(inst,basereg,disp,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 6 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ + } while (0) + +#define x86_mov_mem_reg(inst,mem,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_mov_regp_reg(inst,regp,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_regp_emit ((inst), (reg), (regp)); \ + } while (0) + +#define x86_mov_membase_reg(inst,basereg,disp,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: assert (0); \ + } \ + x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_mov_reg_reg(inst,dreg,reg,size) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_mov_reg_mem(inst,reg,mem,size) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define kMovRegMembasePadding (2 + kMaxMembaseEmitPadding) + +#define x86_mov_reg_membase(inst,reg,basereg,disp,size) \ + do { \ + x86_codegen_pre(&(inst), kMovRegMembasePadding); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: assert (0); \ + } \ + x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +/* + * Note: x86_clear_reg () chacnges the condition code! + */ +#define x86_clear_reg(inst,reg) x86_alu_reg_reg((inst), X86_XOR, (reg), (reg)) + +#define x86_mov_reg_imm(inst,reg,imm) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0xb8 + (reg); \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_mov_mem_imm(inst,mem,imm,size) \ + do { \ + if ((size) == 1) { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0xc6; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + x86_codegen_pre(&(inst), 9); \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 10); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_mov_membase_imm(inst,basereg,disp,imm,size) \ + do { \ + if ((size) == 1) { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xc6; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + x86_codegen_pre(&(inst), 4 + kMaxMembaseEmitPadding); \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) \ + do { \ + if ((size) == 1) { \ + x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0xc6; \ + x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + x86_codegen_pre(&(inst), 4 + kMaxMemindexEmitPadding); \ + x86_prefix((inst), X86_OPERAND_PREFIX); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0xc7; \ + x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_lea_mem(inst,reg,mem) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x8d; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_lea_membase(inst,reg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x8d; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_lea_memindex(inst,reg,basereg,disp,indexreg,shift) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0x8d; \ + x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_widen_reg(inst,dreg,reg,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + g_assert (is_half || X86_IS_BYTE_REG (reg)); \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_widen_mem(inst,dreg,mem,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_mem_emit ((inst), (dreg), (mem)); \ + } while (0) + +#define x86_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_membase_emit ((inst), (dreg), (basereg), (disp)); \ + } while (0) + +#define x86_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_memindex_emit ((inst), (dreg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_cdq(inst) do { *(inst)++ = (unsigned char)0x99; } while (0) +#define x86_wait(inst) do { *(inst)++ = (unsigned char)0x9b; } while (0) + +#define x86_fp_op_mem(inst,opc,mem,is_double) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ + x86_mem_emit ((inst), (opc), (mem)); \ + } while (0) + +#define x86_fp_op_membase(inst,opc,basereg,disp,is_double) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + } while (0) + +#define x86_fp_op(inst,opc,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd8; \ + *(inst)++ = (unsigned char)0xc0+((opc)<<3)+((index)&0x07); \ + } while (0) + +#define x86_fp_op_reg(inst,opc,index,pop_stack) \ + do { \ + static const unsigned char map[] = { 0, 1, 2, 3, 5, 4, 7, 6, 8}; \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (pop_stack) ? (unsigned char)0xde : (unsigned char)0xdc; \ + *(inst)++ = (unsigned char)0xc0+(map[(opc)]<<3)+((index)&0x07); \ + } while (0) + +/** + * @x86_fp_int_op_membase + * Supports FPU operations between ST(0) and integer operand in memory. + * Operation encoded using X86_FP_Opcode enum. + * Operand is addressed by [basereg + disp]. + * is_int specifies whether operand is int32 (TRUE) or int16 (FALSE). + */ +#define x86_fp_int_op_membase(inst,opc,basereg,disp,is_int) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (is_int) ? (unsigned char)0xda : (unsigned char)0xde; \ + x86_membase_emit ((inst), opc, (basereg), (disp)); \ + } while (0) + +#define x86_fstp(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdd; \ + *(inst)++ = (unsigned char)0xd8+(index); \ + } while (0) + +#define x86_fcompp(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xde; \ + *(inst)++ = (unsigned char)0xd9; \ + } while (0) + +#define x86_fucompp(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xda; \ + *(inst)++ = (unsigned char)0xe9; \ + } while (0) + +#define x86_fnstsw(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xe0; \ + } while (0) + +#define x86_fnstcw(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xd9; \ + x86_mem_emit ((inst), 7, (mem)); \ + } while (0) + +#define x86_fnstcw_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xd9; \ + x86_membase_emit ((inst), 7, (basereg), (disp)); \ + } while (0) + +#define x86_fldcw(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xd9; \ + x86_mem_emit ((inst), 5, (mem)); \ + } while (0) + +#define x86_fldcw_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xd9; \ + x86_membase_emit ((inst), 5, (basereg), (disp)); \ + } while (0) + +#define x86_fchs(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xe0; \ + } while (0) + +#define x86_frem(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xf8; \ + } while (0) + +#define x86_fxch(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xc8 + ((index) & 0x07); \ + } while (0) + +#define x86_fcomi(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdb; \ + *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ + } while (0) + +#define x86_fcomip(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ + } while (0) + +#define x86_fucomi(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdb; \ + *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ + } while (0) + +#define x86_fucomip(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ + } while (0) + +#define x86_fld(inst,mem,is_double) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_fld_membase(inst,basereg,disp,is_double) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_fld80_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 5, (mem)); \ + } while (0) + +#define x86_fld80_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 5, (basereg), (disp)); \ + } while (0) + +#define x86_fild(inst,mem,is_long) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_mem_emit ((inst), 5, (mem)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 0, (mem)); \ + } \ + } while (0) + +#define x86_fild_membase(inst,basereg,disp,is_long) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_membase_emit ((inst), 5, (basereg), (disp)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } \ + } while (0) + +#define x86_fld_reg(inst,index) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xc0 + ((index) & 0x07); \ + } while (0) + +#define x86_fldz(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xee; \ + } while (0) + +#define x86_fld1(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xe8; \ + } while (0) + +#define x86_fldpi(inst) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xeb; \ + } while (0) + +#define x86_fst(inst,mem,is_double,pop_stack) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ + x86_mem_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (mem)); \ + } while (0) + +#define x86_fst_membase(inst,basereg,disp,is_double,pop_stack) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ + x86_membase_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (basereg), (disp)); \ + } while (0) + +#define x86_fst80_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 7, (mem)); \ + } while (0) + + +#define x86_fst80_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 7, (basereg), (disp)); \ + } while (0) + + +#define x86_fist_pop(inst,mem,is_long) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_mem_emit ((inst), 7, (mem)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 3, (mem)); \ + } \ + } while (0) + +#define x86_fist_pop_membase(inst,basereg,disp,is_long) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_membase_emit ((inst), 7, (basereg), (disp)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 3, (basereg), (disp)); \ + } \ + } while (0) + +#define x86_fstsw(inst) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x9b; \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xe0; \ + } while (0) + +/** + * @x86_fist_membase + * Converts content of ST(0) to integer and stores it at memory location + * addressed by [basereg + disp]. + * is_int specifies whether destination is int32 (TRUE) or int16 (FALSE). + */ +#define x86_fist_membase(inst,basereg,disp,is_int) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + if ((is_int)) { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } else { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } \ + } while (0) + + +#define x86_push_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0x50 + (reg); \ + } while (0) + +#define x86_push_regp(inst,reg) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xff; \ + x86_regp_emit ((inst), 6, (reg)); \ + } while (0) + +#define x86_push_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 6, (mem)); \ + } while (0) + +#define x86_push_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 6, (basereg), (disp)); \ + } while (0) + +#define x86_push_memindex(inst,basereg,disp,indexreg,shift) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMemindexEmitPadding); \ + *(inst)++ = (unsigned char)0xff; \ + x86_memindex_emit ((inst), 6, (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_push_imm_template(inst) x86_push_imm (inst, 0xf0f0f0f0) + +#define x86_push_imm(inst,imm) \ + do { \ + int _imm = (int) (imm); \ + if (x86_is_imm8 (_imm)) { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0x6A; \ + x86_imm_emit8 ((inst), (_imm)); \ + } else { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x68; \ + x86_imm_emit32 ((inst), (_imm)); \ + } \ + } while (0) + +#define x86_pop_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0x58 + (reg); \ + } while (0) + +#define x86_pop_mem(inst,mem) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x87; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_pop_membase(inst,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x87; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_pushad(inst) do { *(inst)++ = (unsigned char)0x60; } while (0) +#define x86_pushfd(inst) do { *(inst)++ = (unsigned char)0x9c; } while (0) +#define x86_popad(inst) do { *(inst)++ = (unsigned char)0x61; } while (0) +#define x86_popfd(inst) do { *(inst)++ = (unsigned char)0x9d; } while (0) + +#define x86_loop(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xe2; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_loope(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xe1; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_loopne(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xe0; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#if defined(TARGET_X86) +#define x86_jump32(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0xe9; \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_jump8(inst,imm) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + *(inst)++ = (unsigned char)0xeb; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) +#elif defined(TARGET_AMD64) +/* These macros are used directly from mini-amd64.c and other */ +/* amd64 specific files, so they need to be instrumented directly. */ +#define x86_jump32(inst,imm) \ + do { \ + amd64_codegen_pre(inst); \ + *(inst)++ = (unsigned char)0xe9; \ + x86_imm_emit32 ((inst), (imm)); \ + amd64_codegen_post(inst); \ + } while (0) + +#define x86_jump8(inst,imm) \ + do { \ + amd64_codegen_pre(inst); \ + *(inst)++ = (unsigned char)0xeb; \ + x86_imm_emit8 ((inst), (imm)); \ + amd64_codegen_post(inst); \ + } while (0) +#endif + +#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) +#define x86_jump_reg(inst,reg) do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x83; /* and */ \ + x86_reg_emit ((inst), 4, (reg)); /* reg */ \ + *(inst)++ = (unsigned char)nacl_align_byte; \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst), 4, (reg)); \ + } while (0) + +/* Let's hope ECX is available for these... */ +#define x86_jump_mem(inst,mem) do { \ + x86_mov_reg_mem(inst, (X86_ECX), (mem), 4); \ + x86_jump_reg(inst, (X86_ECX)); \ + } while (0) + +#define x86_jump_membase(inst,basereg,disp) do { \ + x86_mov_reg_membase(inst, (X86_ECX), basereg, disp, 4); \ + x86_jump_reg(inst, (X86_ECX)); \ + } while (0) + +/* like x86_jump_membase, but force a 32-bit displacement */ +#define x86_jump_membase32(inst,basereg,disp) do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x8b; \ + x86_address_byte ((inst), 2, X86_ECX, (basereg)); \ + x86_imm_emit32 ((inst), (disp)); \ + x86_jump_reg(inst, (X86_ECX)); \ + } while (0) +#else /* __native_client_codegen__ */ +#define x86_jump_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst), 4, (reg)); \ + } while (0) + +#define x86_jump_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 4, (mem)); \ + } while (0) + +#define x86_jump_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 4, (basereg), (disp)); \ + } while (0) +#endif /* __native_client_codegen__ */ +/* + * target is a pointer in our buffer. + */ +#define x86_jump_code_body(inst,target) \ + do { \ + int t; \ + x86_codegen_pre(&(inst), 2); \ + t = (unsigned char*)(target) - (inst) - 2; \ + if (x86_is_imm8(t)) { \ + x86_jump8 ((inst), t); \ + } else { \ + x86_codegen_pre(&(inst), 5); \ + t = (unsigned char*)(target) - (inst) - 5; \ + x86_jump32 ((inst), t); \ + } \ + } while (0) + +#if defined(__default_codegen__) +#define x86_jump_code(inst,target) \ + do { \ + x86_jump_code_body((inst),(target)); \ + } while (0) +#elif defined(__native_client_codegen__) && defined(TARGET_X86) +#define x86_jump_code(inst,target) \ + do { \ + guint8* jump_start = (inst); \ + x86_jump_code_body((inst),(target)); \ + x86_patch(jump_start, (target)); \ + } while (0) +#elif defined(__native_client_codegen__) && defined(TARGET_AMD64) +#define x86_jump_code(inst,target) \ + do { \ + /* jump_code_body is used twice because there are offsets */ \ + /* calculated based on the IP, which can change after the */ \ + /* call to amd64_codegen_post */ \ + amd64_codegen_pre(inst); \ + x86_jump_code_body((inst),(target)); \ + inst = amd64_codegen_post(inst); \ + x86_jump_code_body((inst),(target)); \ + } while (0) +#endif /* __native_client_codegen__ */ + +#define x86_jump_disp(inst,disp) \ + do { \ + int t = (disp) - 2; \ + if (x86_is_imm8(t)) { \ + x86_jump8 ((inst), t); \ + } else { \ + t -= 3; \ + x86_jump32 ((inst), t); \ + } \ + } while (0) + +#if defined(TARGET_X86) +#define x86_branch8(inst,cond,imm,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 2); \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)]; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)]; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_branch32(inst,cond,imm,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 6); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x10; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x10; \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) +#elif defined(TARGET_AMD64) +/* These macros are used directly from mini-amd64.c and other */ +/* amd64 specific files, so they need to be instrumented directly. */ +#define x86_branch8(inst,cond,imm,is_signed) \ + do { \ + amd64_codegen_pre(inst); \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)]; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)]; \ + x86_imm_emit8 ((inst), (imm)); \ + amd64_codegen_post(inst); \ + } while (0) +#define x86_branch32(inst,cond,imm,is_signed) \ + do { \ + amd64_codegen_pre(inst); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x10; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x10; \ + x86_imm_emit32 ((inst), (imm)); \ + amd64_codegen_post(inst); \ + } while (0) +#endif + +#if defined(TARGET_X86) +#define x86_branch(inst,cond,target,is_signed) \ + do { \ + int offset; \ + guint8* branch_start; \ + x86_codegen_pre(&(inst), 2); \ + offset = (target) - (inst) - 2; \ + branch_start = (inst); \ + if (x86_is_imm8 ((offset))) \ + x86_branch8 ((inst), (cond), offset, (is_signed)); \ + else { \ + x86_codegen_pre(&(inst), 6); \ + offset = (target) - (inst) - 6; \ + x86_branch32 ((inst), (cond), offset, (is_signed)); \ + } \ + x86_patch(branch_start, (target)); \ + } while (0) +#elif defined(TARGET_AMD64) +/* This macro is used directly from mini-amd64.c and other */ +/* amd64 specific files, so it needs to be instrumented directly. */ + +#define x86_branch_body(inst,cond,target,is_signed) \ + do { \ + int offset = (target) - (inst) - 2; \ + if (x86_is_imm8 ((offset))) \ + x86_branch8 ((inst), (cond), offset, (is_signed)); \ + else { \ + offset = (target) - (inst) - 6; \ + x86_branch32 ((inst), (cond), offset, (is_signed)); \ + } \ + } while (0) + +#if defined(__default_codegen__) +#define x86_branch(inst,cond,target,is_signed) \ + do { \ + x86_branch_body((inst),(cond),(target),(is_signed)); \ + } while (0) +#elif defined(__native_client_codegen__) +#define x86_branch(inst,cond,target,is_signed) \ + do { \ + /* branch_body is used twice because there are offsets */ \ + /* calculated based on the IP, which can change after */ \ + /* the call to amd64_codegen_post */ \ + amd64_codegen_pre(inst); \ + x86_branch_body((inst),(cond),(target),(is_signed)); \ + inst = amd64_codegen_post(inst); \ + x86_branch_body((inst),(cond),(target),(is_signed)); \ + } while (0) +#endif /* __native_client_codegen__ */ + +#endif /* TARGET_AMD64 */ + +#define x86_branch_disp(inst,cond,disp,is_signed) \ + do { \ + int offset = (disp) - 2; \ + if (x86_is_imm8 ((offset))) \ + x86_branch8 ((inst), (cond), offset, (is_signed)); \ + else { \ + offset -= 4; \ + x86_branch32 ((inst), (cond), offset, (is_signed)); \ + } \ + } while (0) + +#define x86_set_reg(inst,cond,reg,is_signed) \ + do { \ + g_assert (X86_IS_BYTE_REG (reg)); \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ + x86_reg_emit ((inst), 0, (reg)); \ + } while (0) + +#define x86_set_mem(inst,cond,mem,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_set_membase(inst,cond,basereg,disp,is_signed) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_call_imm_body(inst,disp) \ + do { \ + *(inst)++ = (unsigned char)0xe8; \ + x86_imm_emit32 ((inst), (int)(disp)); \ + } while (0) + +#define x86_call_imm(inst,disp) \ + do { \ + x86_call_sequence_pre((inst)); \ + x86_call_imm_body((inst), (disp)); \ + x86_call_sequence_post((inst)); \ + } while (0) + + +#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) +#define x86_call_reg_internal(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0x83; /* and */ \ + x86_reg_emit ((inst), 4, (reg)); /* reg */ \ + *(inst)++ = (unsigned char)nacl_align_byte; \ + *(inst)++ = (unsigned char)0xff; /* call */ \ + x86_reg_emit ((inst), 2, (reg)); /* reg */ \ + } while (0) + +#define x86_call_reg(inst, reg) do { \ + x86_call_sequence_pre((inst)); \ + x86_call_reg_internal(inst, reg); \ + x86_call_sequence_post((inst)); \ + } while (0) + + +/* It appears that x86_call_mem() is never used, so I'm leaving it out. */ +#define x86_call_membase(inst,basereg,disp) do { \ + x86_call_sequence_pre((inst)); \ + /* x86_mov_reg_membase() inlined so its fixed size */ \ + *(inst)++ = (unsigned char)0x8b; \ + x86_address_byte ((inst), 2, (X86_ECX), (basereg)); \ + x86_imm_emit32 ((inst), (disp)); \ + x86_call_reg_internal(inst, X86_ECX); \ + x86_call_sequence_post((inst)); \ + } while (0) +#else /* __native_client_codegen__ */ +#define x86_call_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst), 2, (reg)); \ + } while (0) + +#define x86_call_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 2, (mem)); \ + } while (0) + +#define x86_call_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } while (0) +#endif /* __native_client_codegen__ */ + + +#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) + +#define x86_call_code(inst,target) \ + do { \ + int _x86_offset; \ + guint8* call_start; \ + guint8* _aligned_start; \ + x86_call_sequence_pre_val((inst)); \ + _x86_offset = (unsigned char*)(target) - (inst); \ + _x86_offset -= 5; \ + x86_call_imm_body ((inst), _x86_offset); \ + _aligned_start = x86_call_sequence_post_val((inst)); \ + call_start = _aligned_start; \ + _x86_offset = (unsigned char*)(target) - (_aligned_start); \ + _x86_offset -= 5; \ + x86_call_imm_body ((_aligned_start), _x86_offset); \ + x86_patch(call_start, (target)); \ + } while (0) + +#define SIZE_OF_RET 6 +#define x86_ret(inst) do { \ + *(inst)++ = (unsigned char)0x59; /* pop ecx */ \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x83; /* and 0xffffffff, ecx */ \ + *(inst)++ = (unsigned char)0xe1; \ + *(inst)++ = (unsigned char)nacl_align_byte; \ + *(inst)++ = (unsigned char)0xff; /* jmp ecx */ \ + *(inst)++ = (unsigned char)0xe1; \ + } while (0) + +/* pop return address */ +/* pop imm bytes from stack */ +/* return */ +#define x86_ret_imm(inst,imm) do { \ + *(inst)++ = (unsigned char)0x59; /* pop ecx */ \ + x86_alu_reg_imm ((inst), X86_ADD, X86_ESP, imm); \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x83; /* and 0xffffffff, ecx */ \ + *(inst)++ = (unsigned char)0xe1; \ + *(inst)++ = (unsigned char)nacl_align_byte; \ + *(inst)++ = (unsigned char)0xff; /* jmp ecx */ \ + *(inst)++ = (unsigned char)0xe1; \ +} while (0) +#else /* __native_client_codegen__ */ + +#define x86_call_code(inst,target) \ + do { \ + int _x86_offset; \ + _x86_offset = (unsigned char*)(target) - (inst); \ + _x86_offset -= 5; \ + x86_call_imm_body ((inst), _x86_offset); \ + } while (0) + +#define x86_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) + +#define x86_ret_imm(inst,imm) \ + do { \ + if ((imm) == 0) { \ + x86_ret ((inst)); \ + } else { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0xc2; \ + x86_imm_emit16 ((inst), (imm)); \ + } \ + } while (0) +#endif /* __native_client_codegen__ */ + +#define x86_cmov_reg(inst,cond,is_signed,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char) 0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_cmov_mem(inst,cond,is_signed,reg,mem) \ + do { \ + x86_codegen_pre(&(inst), 7); \ + *(inst)++ = (unsigned char) 0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_cmov_membase(inst,cond,is_signed,reg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char) 0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_enter(inst,framesize) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0xc8; \ + x86_imm_emit16 ((inst), (framesize)); \ + *(inst)++ = 0; \ + } while (0) + +#define x86_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) +#define x86_sahf(inst) do { *(inst)++ = (unsigned char)0x9e; } while (0) + +#define x86_fsin(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfe; } while (0) +#define x86_fcos(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xff; } while (0) +#define x86_fabs(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe1; } while (0) +#define x86_ftst(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe4; } while (0) +#define x86_fxam(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe5; } while (0) +#define x86_fpatan(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf3; } while (0) +#define x86_fprem(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf8; } while (0) +#define x86_fprem1(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf5; } while (0) +#define x86_frndint(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfc; } while (0) +#define x86_fsqrt(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfa; } while (0) +#define x86_fptan(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf2; } while (0) + +#define x86_padding(inst,size) \ + do { \ + switch ((size)) { \ + case 1: x86_nop ((inst)); break; \ + case 2: *(inst)++ = 0x8b; \ + *(inst)++ = 0xc0; break; \ + case 3: *(inst)++ = 0x8d; *(inst)++ = 0x6d; \ + *(inst)++ = 0x00; break; \ + case 4: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ + *(inst)++ = 0x24; *(inst)++ = 0x00; \ + break; \ + case 5: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ + *(inst)++ = 0x24; *(inst)++ = 0x00; \ + x86_nop ((inst)); break; \ + case 6: *(inst)++ = 0x8d; *(inst)++ = 0xad; \ + *(inst)++ = 0x00; *(inst)++ = 0x00; \ + *(inst)++ = 0x00; *(inst)++ = 0x00; \ + break; \ + case 7: *(inst)++ = 0x8d; *(inst)++ = 0xa4; \ + *(inst)++ = 0x24; *(inst)++ = 0x00; \ + *(inst)++ = 0x00; *(inst)++ = 0x00; \ + *(inst)++ = 0x00; break; \ + default: assert (0); \ + } \ + } while (0) + +#ifdef __native_client_codegen__ + +#define kx86NaClLengthOfCallReg 5 +#define kx86NaClLengthOfCallImm 5 +#define kx86NaClLengthOfCallMembase (kx86NaClLengthOfCallReg + 6) + +#endif /* __native_client_codegen__ */ + +#define x86_prolog(inst,frame_size,reg_mask) \ + do { \ + unsigned i, m = 1; \ + x86_enter ((inst), (frame_size)); \ + for (i = 0; i < X86_NREG; ++i, m <<= 1) { \ + if ((reg_mask) & m) \ + x86_push_reg ((inst), i); \ + } \ + } while (0) + +#define x86_epilog(inst,reg_mask) \ + do { \ + unsigned i, m = 1 << X86_EDI; \ + for (i = X86_EDI; m != 0; i--, m=m>>1) { \ + if ((reg_mask) & m) \ + x86_pop_reg ((inst), i); \ + } \ + x86_leave ((inst)); \ + x86_ret ((inst)); \ + } while (0) + + +typedef enum { + X86_SSE_SQRT = 0x51, + X86_SSE_RSQRT = 0x52, + X86_SSE_RCP = 0x53, + X86_SSE_ADD = 0x58, + X86_SSE_DIV = 0x5E, + X86_SSE_MUL = 0x59, + X86_SSE_SUB = 0x5C, + X86_SSE_MIN = 0x5D, + X86_SSE_MAX = 0x5F, + X86_SSE_COMP = 0xC2, + X86_SSE_AND = 0x54, + X86_SSE_ANDN = 0x55, + X86_SSE_OR = 0x56, + X86_SSE_XOR = 0x57, + X86_SSE_UNPCKL = 0x14, + X86_SSE_UNPCKH = 0x15, + + X86_SSE_ADDSUB = 0xD0, + X86_SSE_HADD = 0x7C, + X86_SSE_HSUB = 0x7D, + X86_SSE_MOVSHDUP = 0x16, + X86_SSE_MOVSLDUP = 0x12, + X86_SSE_MOVDDUP = 0x12, + + X86_SSE_PAND = 0xDB, + X86_SSE_POR = 0xEB, + X86_SSE_PXOR = 0xEF, + + X86_SSE_PADDB = 0xFC, + X86_SSE_PADDW = 0xFD, + X86_SSE_PADDD = 0xFE, + X86_SSE_PADDQ = 0xD4, + + X86_SSE_PSUBB = 0xF8, + X86_SSE_PSUBW = 0xF9, + X86_SSE_PSUBD = 0xFA, + X86_SSE_PSUBQ = 0xFB, + + X86_SSE_PMAXSB = 0x3C, /*sse41*/ + X86_SSE_PMAXSW = 0xEE, + X86_SSE_PMAXSD = 0x3D, /*sse41*/ + + X86_SSE_PMAXUB = 0xDE, + X86_SSE_PMAXUW = 0x3E, /*sse41*/ + X86_SSE_PMAXUD = 0x3F, /*sse41*/ + + X86_SSE_PMINSB = 0x38, /*sse41*/ + X86_SSE_PMINSW = 0xEA, + X86_SSE_PMINSD = 0x39,/*sse41*/ + + X86_SSE_PMINUB = 0xDA, + X86_SSE_PMINUW = 0x3A, /*sse41*/ + X86_SSE_PMINUD = 0x3B, /*sse41*/ + + X86_SSE_PAVGB = 0xE0, + X86_SSE_PAVGW = 0xE3, + + X86_SSE_PCMPEQB = 0x74, + X86_SSE_PCMPEQW = 0x75, + X86_SSE_PCMPEQD = 0x76, + X86_SSE_PCMPEQQ = 0x29, /*sse41*/ + + X86_SSE_PCMPGTB = 0x64, + X86_SSE_PCMPGTW = 0x65, + X86_SSE_PCMPGTD = 0x66, + X86_SSE_PCMPGTQ = 0x37, /*sse42*/ + + X86_SSE_PSADBW = 0xf6, + + X86_SSE_PSHUFD = 0x70, + + X86_SSE_PUNPCKLBW = 0x60, + X86_SSE_PUNPCKLWD = 0x61, + X86_SSE_PUNPCKLDQ = 0x62, + X86_SSE_PUNPCKLQDQ = 0x6C, + + X86_SSE_PUNPCKHBW = 0x68, + X86_SSE_PUNPCKHWD = 0x69, + X86_SSE_PUNPCKHDQ = 0x6A, + X86_SSE_PUNPCKHQDQ = 0x6D, + + X86_SSE_PACKSSWB = 0x63, + X86_SSE_PACKSSDW = 0x6B, + + X86_SSE_PACKUSWB = 0x67, + X86_SSE_PACKUSDW = 0x2B,/*sse41*/ + + X86_SSE_PADDUSB = 0xDC, + X86_SSE_PADDUSW = 0xDD, + X86_SSE_PSUBUSB = 0xD8, + X86_SSE_PSUBUSW = 0xD9, + + X86_SSE_PADDSB = 0xEC, + X86_SSE_PADDSW = 0xED, + X86_SSE_PSUBSB = 0xE8, + X86_SSE_PSUBSW = 0xE9, + + X86_SSE_PMULLW = 0xD5, + X86_SSE_PMULLD = 0x40,/*sse41*/ + X86_SSE_PMULHUW = 0xE4, + X86_SSE_PMULHW = 0xE5, + X86_SSE_PMULUDQ = 0xF4, + + X86_SSE_PMOVMSKB = 0xD7, + + X86_SSE_PSHIFTW = 0x71, + X86_SSE_PSHIFTD = 0x72, + X86_SSE_PSHIFTQ = 0x73, + X86_SSE_SHR = 2, + X86_SSE_SAR = 4, + X86_SSE_SHL = 6, + + X86_SSE_PSRLW_REG = 0xD1, + X86_SSE_PSRAW_REG = 0xE1, + X86_SSE_PSLLW_REG = 0xF1, + + X86_SSE_PSRLD_REG = 0xD2, + X86_SSE_PSRAD_REG = 0xE2, + X86_SSE_PSLLD_REG = 0xF2, + + X86_SSE_PSRLQ_REG = 0xD3, + X86_SSE_PSLLQ_REG = 0xF3, + + X86_SSE_PREFETCH = 0x18, + X86_SSE_MOVNTPS = 0x2B, + X86_SSE_MOVHPD_REG_MEMBASE = 0x16, + X86_SSE_MOVHPD_MEMBASE_REG = 0x17, + + X86_SSE_MOVSD_REG_MEMBASE = 0x10, + X86_SSE_MOVSD_MEMBASE_REG = 0x11, + + X86_SSE_PINSRB = 0x20,/*sse41*/ + X86_SSE_PINSRW = 0xC4, + X86_SSE_PINSRD = 0x22,/*sse41*/ + + X86_SSE_PEXTRB = 0x14,/*sse41*/ + X86_SSE_PEXTRW = 0xC5, + X86_SSE_PEXTRD = 0x16,/*sse41*/ + + X86_SSE_SHUFP = 0xC6, + + X86_SSE_CVTDQ2PD = 0xE6, + X86_SSE_CVTDQ2PS = 0x5B, + X86_SSE_CVTPD2DQ = 0xE6, + X86_SSE_CVTPD2PS = 0x5A, + X86_SSE_CVTPS2DQ = 0x5B, + X86_SSE_CVTPS2PD = 0x5A, + X86_SSE_CVTTPD2DQ = 0xE6, + X86_SSE_CVTTPS2DQ = 0x5B, +} X86_SSE_Opcode; + + +/* minimal SSE* support */ +#define x86_movsd_reg_membase(inst,dreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xf2; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_membase_emit ((inst), (dreg), (basereg), (disp)); \ + } while (0) + +#define x86_cvttsd2si(inst,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0xf2; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x2c; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0F; \ + *(inst)++ = (unsigned char)(opc); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_reg_membase(inst,opc,sreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)(opc); \ + x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ + } while (0) + +#define x86_sse_alu_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0F; \ + *(inst)++ = (unsigned char)(opc); \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_sse_alu_reg_reg_imm8(inst,opc,dreg,reg, imm8) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x0F; \ + *(inst)++ = (unsigned char)(opc); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + *(inst)++ = (unsigned char)(imm8); \ + } while (0) + +#define x86_sse_alu_pd_reg_reg_imm8(inst,opc,dreg,reg, imm8) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x66; \ + x86_sse_alu_reg_reg_imm8 ((inst), (opc), (dreg), (reg), (imm8)); \ + } while (0) + +#define x86_sse_alu_pd_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x66; \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_pd_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x66; \ + x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ + } while (0) + +#define x86_sse_alu_pd_reg_membase(inst,opc,dreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x66; \ + x86_sse_alu_reg_membase ((inst), (opc), (dreg),(basereg), (disp)); \ + } while (0) + +#define x86_sse_alu_pd_reg_reg_imm(inst,opc,dreg,reg,imm) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + x86_sse_alu_pd_reg_reg ((inst), (opc), (dreg), (reg)); \ + *(inst)++ = (unsigned char)(imm); \ + } while (0) + +#define x86_sse_alu_pd_reg_membase_imm(inst,opc,dreg,basereg,disp,imm) \ + do { \ + x86_codegen_pre(&(inst), 4 + kMaxMembaseEmitPadding); \ + x86_sse_alu_pd_reg_membase ((inst), (opc), (dreg),(basereg), (disp)); \ + *(inst)++ = (unsigned char)(imm); \ + } while (0) + + +#define x86_sse_alu_ps_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_ps_reg_reg_imm(inst,opc,dreg,reg, imm) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + *(inst)++ = (unsigned char)imm; \ + } while (0) + + +#define x86_sse_alu_sd_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0xF2; \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_sd_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xF2; \ + x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ + } while (0) + + +#define x86_sse_alu_ss_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0xF3; \ + x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ + } while (0) + +#define x86_sse_alu_ss_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0xF3; \ + x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ + } while (0) + + + +#define x86_sse_alu_sse41_reg_reg(inst,opc,dreg,reg) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0x0F; \ + *(inst)++ = (unsigned char)0x38; \ + *(inst)++ = (unsigned char)(opc); \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_movups_reg_membase(inst,sreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x10; \ + x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ + } while (0) + +#define x86_movups_membase_reg(inst,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x11; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_movaps_reg_membase(inst,sreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x28; \ + x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ + } while (0) + +#define x86_movaps_membase_reg(inst,basereg,disp,reg) \ + do { \ + x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x29; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_movaps_reg_reg(inst,dreg,sreg) \ + do { \ + x86_codegen_pre(&(inst), 3); \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x28; \ + x86_reg_emit ((inst), (dreg), (sreg)); \ + } while (0) + + +#define x86_movd_reg_xreg(inst,dreg,sreg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x7e; \ + x86_reg_emit ((inst), (sreg), (dreg)); \ + } while (0) + +#define x86_movd_xreg_reg(inst,dreg,sreg) \ + do { \ + x86_codegen_pre(&(inst), 4); \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x6e; \ + x86_reg_emit ((inst), (dreg), (sreg)); \ + } while (0) + +#define x86_movd_xreg_membase(inst,sreg,basereg,disp) \ + do { \ + x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x6e; \ + x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ + } while (0) + +#define x86_pshufw_reg_reg(inst,dreg,sreg,mask,high_words) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + *(inst)++ = (unsigned char)(high_words) ? 0xF3 : 0xF2; \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0x70; \ + x86_reg_emit ((inst), (dreg), (sreg)); \ + *(inst)++ = (unsigned char)mask; \ + } while (0) + +#define x86_sse_shift_reg_imm(inst,opc,mode, dreg,imm) \ + do { \ + x86_codegen_pre(&(inst), 5); \ + x86_sse_alu_pd_reg_reg (inst, opc, mode, dreg); \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_sse_shift_reg_reg(inst,opc,dreg,sreg) \ + do { \ + x86_sse_alu_pd_reg_reg (inst, opc, dreg, sreg); \ + } while (0) + + + +#endif // X86_H + diff --git a/qv4isel_x86_64.cpp b/qv4isel_x86_64.cpp index a0e3c9dab0..5d6da888e2 100644 --- a/qv4isel_x86_64.cpp +++ b/qv4isel_x86_64.cpp @@ -10,7 +10,7 @@ typedef qint64 gint64; typedef uchar guint8; typedef uint guint32; typedef void *gpointer; -#include "amd64-codegen.h" +#include "asm/amd64-codegen.h" #include #include diff --git a/v4.pro b/v4.pro index ae3e85dd32..f55d2520a0 100644 --- a/v4.pro +++ b/v4.pro @@ -27,13 +27,22 @@ HEADERS += \ qmljs_runtime.h \ qmljs_objects.h \ qv4isel_p.h \ - x86-codegen.h \ - amd64-codegen.h \ qv4syntaxchecker_p.h \ qv4ecmaobjects_p.h \ qv4array_p.h \ qv4isel_x86_64_p.h +HEADERS += \ + asm/x86-codegen.h \ + asm/amd64-codegen.h \ + asm/arm-codegen.h \ + asm/arm-dis.h \ + asm/arm_dpimacros.h \ + asm/arm-wmmx.h + +SOURCES += \ + asm/arm-codegen.c \ + asm/arm-dis.c llvm { diff --git a/x86-codegen.h b/x86-codegen.h deleted file mode 100644 index fd2c528c86..0000000000 --- a/x86-codegen.h +++ /dev/null @@ -1,2640 +0,0 @@ -/* - * x86-codegen.h: Macros for generating x86 code - * - * Authors: - * Paolo Molaro (lupus@ximian.com) - * Intel Corporation (ORP Project) - * Sergey Chaban (serge@wildwestsoftware.com) - * Dietmar Maurer (dietmar@ximian.com) - * Patrik Torstensson - * - * Copyright (C) 2000 Intel Corporation. All rights reserved. - * Copyright (C) 2001, 2002 Ximian, Inc. - */ - -#ifndef X86_H -#define X86_H -#include - -#ifdef __native_client_codegen__ -extern gint8 nacl_align_byte; -#endif /* __native_client_codegen__ */ - - -#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) -#define x86_codegen_pre(inst_ptr_ptr, inst_len) do { mono_nacl_align_inst(inst_ptr_ptr, inst_len); } while (0) -#define x86_call_sequence_pre_val(inst) guint8* _code_start = (inst); -#define x86_call_sequence_post_val(inst) \ - (mono_nacl_align_call(&_code_start, &(inst)), _code_start); -#define x86_call_sequence_pre(inst) x86_call_sequence_pre_val((inst)) -#define x86_call_sequence_post(inst) x86_call_sequence_post_val((inst)) -#else -#define x86_codegen_pre(inst_ptr_ptr, inst_len) do {} while (0) -/* Two variants are needed to avoid warnings */ -#define x86_call_sequence_pre_val(inst) guint8* _code_start = (inst); -#define x86_call_sequence_post_val(inst) _code_start -#define x86_call_sequence_pre(inst) -#define x86_call_sequence_post(inst) -#endif /* __native_client_codegen__ */ - - -/* -// x86 register numbers -*/ -typedef enum { - X86_EAX = 0, - X86_ECX = 1, - X86_EDX = 2, - X86_EBX = 3, - X86_ESP = 4, - X86_EBP = 5, - X86_ESI = 6, - X86_EDI = 7, - X86_NREG -} X86_Reg_No; - -typedef enum { - X86_XMM0, - X86_XMM1, - X86_XMM2, - X86_XMM3, - X86_XMM4, - X86_XMM5, - X86_XMM6, - X86_XMM7, - X86_XMM_NREG -} X86_XMM_Reg_No; - -/* -// opcodes for alu instructions -*/ -typedef enum { - X86_ADD = 0, - X86_OR = 1, - X86_ADC = 2, - X86_SBB = 3, - X86_AND = 4, - X86_SUB = 5, - X86_XOR = 6, - X86_CMP = 7, - X86_NALU -} X86_ALU_Opcode; -/* -// opcodes for shift instructions -*/ -typedef enum { - X86_SHLD, - X86_SHLR, - X86_ROL = 0, - X86_ROR = 1, - X86_RCL = 2, - X86_RCR = 3, - X86_SHL = 4, - X86_SHR = 5, - X86_SAR = 7, - X86_NSHIFT = 8 -} X86_Shift_Opcode; -/* -// opcodes for floating-point instructions -*/ -typedef enum { - X86_FADD = 0, - X86_FMUL = 1, - X86_FCOM = 2, - X86_FCOMP = 3, - X86_FSUB = 4, - X86_FSUBR = 5, - X86_FDIV = 6, - X86_FDIVR = 7, - X86_NFP = 8 -} X86_FP_Opcode; -/* -// integer conditions codes -*/ -typedef enum { - X86_CC_EQ = 0, X86_CC_E = 0, X86_CC_Z = 0, - X86_CC_NE = 1, X86_CC_NZ = 1, - X86_CC_LT = 2, X86_CC_B = 2, X86_CC_C = 2, X86_CC_NAE = 2, - X86_CC_LE = 3, X86_CC_BE = 3, X86_CC_NA = 3, - X86_CC_GT = 4, X86_CC_A = 4, X86_CC_NBE = 4, - X86_CC_GE = 5, X86_CC_AE = 5, X86_CC_NB = 5, X86_CC_NC = 5, - X86_CC_LZ = 6, X86_CC_S = 6, - X86_CC_GEZ = 7, X86_CC_NS = 7, - X86_CC_P = 8, X86_CC_PE = 8, - X86_CC_NP = 9, X86_CC_PO = 9, - X86_CC_O = 10, - X86_CC_NO = 11, - X86_NCC -} X86_CC; - -/* FP status */ -enum { - X86_FP_C0 = 0x100, - X86_FP_C1 = 0x200, - X86_FP_C2 = 0x400, - X86_FP_C3 = 0x4000, - X86_FP_CC_MASK = 0x4500 -}; - -/* FP control word */ -enum { - X86_FPCW_INVOPEX_MASK = 0x1, - X86_FPCW_DENOPEX_MASK = 0x2, - X86_FPCW_ZERODIV_MASK = 0x4, - X86_FPCW_OVFEX_MASK = 0x8, - X86_FPCW_UNDFEX_MASK = 0x10, - X86_FPCW_PRECEX_MASK = 0x20, - X86_FPCW_PRECC_MASK = 0x300, - X86_FPCW_ROUNDC_MASK = 0xc00, - - /* values for precision control */ - X86_FPCW_PREC_SINGLE = 0, - X86_FPCW_PREC_DOUBLE = 0x200, - X86_FPCW_PREC_EXTENDED = 0x300, - - /* values for rounding control */ - X86_FPCW_ROUND_NEAREST = 0, - X86_FPCW_ROUND_DOWN = 0x400, - X86_FPCW_ROUND_UP = 0x800, - X86_FPCW_ROUND_TOZERO = 0xc00 -}; - -/* -// prefix code -*/ -typedef enum { - X86_LOCK_PREFIX = 0xF0, - X86_REPNZ_PREFIX = 0xF2, - X86_REPZ_PREFIX = 0xF3, - X86_REP_PREFIX = 0xF3, - X86_CS_PREFIX = 0x2E, - X86_SS_PREFIX = 0x36, - X86_DS_PREFIX = 0x3E, - X86_ES_PREFIX = 0x26, - X86_FS_PREFIX = 0x64, - X86_GS_PREFIX = 0x65, - X86_UNLIKELY_PREFIX = 0x2E, - X86_LIKELY_PREFIX = 0x3E, - X86_OPERAND_PREFIX = 0x66, - X86_ADDRESS_PREFIX = 0x67 -} X86_Prefix; - -static const unsigned char -x86_cc_unsigned_map [X86_NCC] = { - 0x74, /* eq */ - 0x75, /* ne */ - 0x72, /* lt */ - 0x76, /* le */ - 0x77, /* gt */ - 0x73, /* ge */ - 0x78, /* lz */ - 0x79, /* gez */ - 0x7a, /* p */ - 0x7b, /* np */ - 0x70, /* o */ - 0x71, /* no */ -}; - -static const unsigned char -x86_cc_signed_map [X86_NCC] = { - 0x74, /* eq */ - 0x75, /* ne */ - 0x7c, /* lt */ - 0x7e, /* le */ - 0x7f, /* gt */ - 0x7d, /* ge */ - 0x78, /* lz */ - 0x79, /* gez */ - 0x7a, /* p */ - 0x7b, /* np */ - 0x70, /* o */ - 0x71, /* no */ -}; - -typedef union { - int val; - unsigned char b [4]; -} x86_imm_buf; - -#define X86_NOBASEREG (-1) - -/* -// bitvector mask for callee-saved registers -*/ -#define X86_ESI_MASK (1<> 6) -#define x86_modrm_reg(modrm) (((modrm) >> 3) & 0x7) -#define x86_modrm_rm(modrm) ((modrm) & 0x7) - -#define x86_address_byte(inst,m,o,r) do { *(inst)++ = ((((m)&0x03)<<6)|(((o)&0x07)<<3)|(((r)&0x07))); } while (0) -#define x86_imm_emit32(inst,imm) \ - do { \ - x86_imm_buf imb; imb.val = (int) (imm); \ - *(inst)++ = imb.b [0]; \ - *(inst)++ = imb.b [1]; \ - *(inst)++ = imb.b [2]; \ - *(inst)++ = imb.b [3]; \ - } while (0) -#define x86_imm_emit16(inst,imm) do { *(short*)(inst) = (imm); (inst) += 2; } while (0) -#define x86_imm_emit8(inst,imm) do { *(inst) = (unsigned char)((imm) & 0xff); ++(inst); } while (0) -#define x86_is_imm8(imm) (((int)(imm) >= -128 && (int)(imm) <= 127)) -#define x86_is_imm16(imm) (((int)(imm) >= -(1<<16) && (int)(imm) <= ((1<<16)-1))) - -#define x86_reg_emit(inst,r,regno) do { x86_address_byte ((inst), 3, (r), (regno)); } while (0) -#define x86_reg8_emit(inst,r,regno,is_rh,is_rnoh) do {x86_address_byte ((inst), 3, (is_rh)?((r)|4):(r), (is_rnoh)?((regno)|4):(regno));} while (0) -#define x86_regp_emit(inst,r,regno) do { x86_address_byte ((inst), 0, (r), (regno)); } while (0) -#define x86_mem_emit(inst,r,disp) do { x86_address_byte ((inst), 0, (r), 5); x86_imm_emit32((inst), (disp)); } while (0) - -#define kMaxMembaseEmitPadding 6 - -#define x86_membase_emit_body(inst,r,basereg,disp) do {\ - if ((basereg) == X86_ESP) { \ - if ((disp) == 0) { \ - x86_address_byte ((inst), 0, (r), X86_ESP); \ - x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ - } else if (x86_is_imm8((disp))) { \ - x86_address_byte ((inst), 1, (r), X86_ESP); \ - x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ - x86_imm_emit8 ((inst), (disp)); \ - } else { \ - x86_address_byte ((inst), 2, (r), X86_ESP); \ - x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ - x86_imm_emit32 ((inst), (disp)); \ - } \ - break; \ - } \ - if ((disp) == 0 && (basereg) != X86_EBP) { \ - x86_address_byte ((inst), 0, (r), (basereg)); \ - break; \ - } \ - if (x86_is_imm8((disp))) { \ - x86_address_byte ((inst), 1, (r), (basereg)); \ - x86_imm_emit8 ((inst), (disp)); \ - } else { \ - x86_address_byte ((inst), 2, (r), (basereg)); \ - x86_imm_emit32 ((inst), (disp)); \ - } \ - } while (0) - -#if defined(__native_client_codegen__) && defined(TARGET_AMD64) -#define x86_membase_emit(inst,r,basereg,disp) \ - do { \ - amd64_nacl_membase_handler(&(inst), (basereg), (disp), (r)) ; \ - } while (0) -#else /* __default_codegen__ || 32-bit NaCl codegen */ -#define x86_membase_emit(inst,r,basereg,disp) \ - do { \ - x86_membase_emit_body((inst),(r),(basereg),(disp)); \ - } while (0) -#endif - -#define kMaxMemindexEmitPadding 6 - -#define x86_memindex_emit(inst,r,basereg,disp,indexreg,shift) \ - do { \ - if ((basereg) == X86_NOBASEREG) { \ - x86_address_byte ((inst), 0, (r), 4); \ - x86_address_byte ((inst), (shift), (indexreg), 5); \ - x86_imm_emit32 ((inst), (disp)); \ - } else if ((disp) == 0 && (basereg) != X86_EBP) { \ - x86_address_byte ((inst), 0, (r), 4); \ - x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ - } else if (x86_is_imm8((disp))) { \ - x86_address_byte ((inst), 1, (r), 4); \ - x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ - x86_imm_emit8 ((inst), (disp)); \ - } else { \ - x86_address_byte ((inst), 2, (r), 4); \ - x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ - x86_imm_emit32 ((inst), (disp)); \ - } \ - } while (0) - -/* - * target is the position in the code where to jump to: - * target = code; - * .. output loop code... - * x86_mov_reg_imm (code, X86_EAX, 0); - * loop = code; - * x86_loop (code, -1); - * ... finish method - * - * patch displacement - * x86_patch (loop, target); - * - * ins should point at the start of the instruction that encodes a target. - * the instruction is inspected for validity and the correct displacement - * is inserted. - */ -#define x86_do_patch(ins,target) \ - do { \ - unsigned char* pos = (ins) + 1; \ - int disp, size = 0; \ - switch (*(unsigned char*)(ins)) { \ - case 0xe8: case 0xe9: ++size; break; /* call, jump32 */ \ - case 0x0f: if (!(*pos >= 0x70 && *pos <= 0x8f)) assert (0); \ - ++size; ++pos; break; /* prefix for 32-bit disp */ \ - case 0xe0: case 0xe1: case 0xe2: /* loop */ \ - case 0xeb: /* jump8 */ \ - /* conditional jump opcodes */ \ - case 0x70: case 0x71: case 0x72: case 0x73: \ - case 0x74: case 0x75: case 0x76: case 0x77: \ - case 0x78: case 0x79: case 0x7a: case 0x7b: \ - case 0x7c: case 0x7d: case 0x7e: case 0x7f: \ - break; \ - default: assert (0); \ - } \ - disp = (target) - pos; \ - if (size) x86_imm_emit32 (pos, disp - 4); \ - else if (x86_is_imm8 (disp - 1)) x86_imm_emit8 (pos, disp - 1); \ - else assert (0); \ - } while (0) - -#if defined( __native_client_codegen__ ) && defined(TARGET_X86) - -#define x86_skip_nops(inst) \ - do { \ - int in_nop = 0; \ - do { \ - in_nop = 0; \ - if (inst[0] == 0x90) { \ - in_nop = 1; \ - inst += 1; \ - } \ - if (inst[0] == 0x8b && inst[1] == 0xc0) { \ - in_nop = 1; \ - inst += 2; \ - } \ - if (inst[0] == 0x8d && inst[1] == 0x6d \ - && inst[2] == 0x00) { \ - in_nop = 1; \ - inst += 3; \ - } \ - if (inst[0] == 0x8d && inst[1] == 0x64 \ - && inst[2] == 0x24 && inst[3] == 0x00) { \ - in_nop = 1; \ - inst += 4; \ - } \ - /* skip inst+=5 case because it's the 4-byte + 1-byte case */ \ - if (inst[0] == 0x8d && inst[1] == 0xad \ - && inst[2] == 0x00 && inst[3] == 0x00 \ - && inst[4] == 0x00 && inst[5] == 0x00) { \ - in_nop = 1; \ - inst += 6; \ - } \ - if (inst[0] == 0x8d && inst[1] == 0xa4 \ - && inst[2] == 0x24 && inst[3] == 0x00 \ - && inst[4] == 0x00 && inst[5] == 0x00 \ - && inst[6] == 0x00 ) { \ - in_nop = 1; \ - inst += 7; \ - } \ - } while ( in_nop ); \ - } while (0) - -#if defined(__native_client__) -#define x86_patch(ins,target) \ - do { \ - unsigned char* inst = (ins); \ - guint8* new_target = nacl_modify_patch_target((target)); \ - x86_skip_nops((inst)); \ - x86_do_patch((inst), new_target); \ - } while (0) -#else /* __native_client__ */ -#define x86_patch(ins,target) \ - do { \ - unsigned char* inst = (ins); \ - guint8* new_target = (target); \ - x86_skip_nops((inst)); \ - x86_do_patch((inst), new_target); \ - } while (0) -#endif /* __native_client__ */ - -#else -#define x86_patch(ins,target) do { x86_do_patch((ins), (target)); } while (0) -#endif /* __native_client_codegen__ */ - -#ifdef __native_client_codegen__ -/* The breakpoint instruction is illegal in Native Client, although the HALT */ -/* instruction is allowed. The breakpoint is used several places in mini-x86.c */ -/* and exceptions-x86.c. */ -#define x86_breakpoint(inst) \ - do { \ - *(inst)++ = 0xf4; \ - } while (0) -#else -#define x86_breakpoint(inst) \ - do { \ - *(inst)++ = 0xcc; \ - } while (0) -#endif - -#define x86_cld(inst) do { *(inst)++ =(unsigned char)0xfc; } while (0) -#define x86_stosb(inst) do { *(inst)++ =(unsigned char)0xaa; } while (0) -#define x86_stosl(inst) do { *(inst)++ =(unsigned char)0xab; } while (0) -#define x86_stosd(inst) x86_stosl((inst)) -#define x86_movsb(inst) do { *(inst)++ =(unsigned char)0xa4; } while (0) -#define x86_movsl(inst) do { *(inst)++ =(unsigned char)0xa5; } while (0) -#define x86_movsd(inst) x86_movsl((inst)) - -#if defined(__default_codegen__) -#define x86_prefix(inst,p) \ - do { \ - *(inst)++ =(unsigned char) (p); \ - } while (0) -#elif defined(__native_client_codegen__) -#if defined(TARGET_X86) -/* kNaClAlignment - 1 is the max value we can pass into x86_codegen_pre. */ -/* This keeps us from having to call x86_codegen_pre with specific */ -/* knowledge of the size of the instruction that follows it, and */ -/* localizes the alignment requirement to this spot. */ -#define x86_prefix(inst,p) \ - do { \ - x86_codegen_pre(&(inst), kNaClAlignment - 1); \ - *(inst)++ =(unsigned char) (p); \ - } while (0) -#elif defined(TARGET_AMD64) -/* We need to tag any prefixes so we can perform proper membase sandboxing */ -/* See: mini-amd64.c:amd64_nacl_membase_handler for verbose details */ -#define x86_prefix(inst,p) \ - do { \ - amd64_nacl_tag_legacy_prefix((inst)); \ - *(inst)++ =(unsigned char) (p); \ - } while (0) - -#endif /* TARGET_AMD64 */ - -#endif /* __native_client_codegen__ */ - -#define x86_rdtsc(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = 0x0f; \ - *(inst)++ = 0x31; \ - } while (0) - -#define x86_cmpxchg_reg_reg(inst,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xb1; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - } while (0) - -#define x86_cmpxchg_mem_reg(inst,mem,reg) \ - do { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xb1; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_cmpxchg_membase_reg(inst,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xb1; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_xchg_reg_reg(inst,dreg,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - if ((size) == 1) \ - *(inst)++ = (unsigned char)0x86; \ - else \ - *(inst)++ = (unsigned char)0x87; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - } while (0) - -#define x86_xchg_mem_reg(inst,mem,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - if ((size) == 1) \ - *(inst)++ = (unsigned char)0x86; \ - else \ - *(inst)++ = (unsigned char)0x87; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_xchg_membase_reg(inst,basereg,disp,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - if ((size) == 1) \ - *(inst)++ = (unsigned char)0x86; \ - else \ - *(inst)++ = (unsigned char)0x87; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_xadd_reg_reg(inst,dreg,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0F; \ - if ((size) == 1) \ - *(inst)++ = (unsigned char)0xC0; \ - else \ - *(inst)++ = (unsigned char)0xC1; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - } while (0) - -#define x86_xadd_mem_reg(inst,mem,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0x0F; \ - if ((size) == 1) \ - *(inst)++ = (unsigned char)0xC0; \ - else \ - *(inst)++ = (unsigned char)0xC1; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_xadd_membase_reg(inst,basereg,disp,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0F; \ - if ((size) == 1) \ - *(inst)++ = (unsigned char)0xC0; \ - else \ - *(inst)++ = (unsigned char)0xC1; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_inc_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xff; \ - x86_mem_emit ((inst), 0, (mem)); \ - } while (0) - -#define x86_inc_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xff; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - } while (0) - -#define x86_inc_reg(inst,reg) do { *(inst)++ = (unsigned char)0x40 + (reg); } while (0) - -#define x86_dec_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xff; \ - x86_mem_emit ((inst), 1, (mem)); \ - } while (0) - -#define x86_dec_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xff; \ - x86_membase_emit ((inst), 1, (basereg), (disp)); \ - } while (0) - -#define x86_dec_reg(inst,reg) do { *(inst)++ = (unsigned char)0x48 + (reg); } while (0) - -#define x86_not_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_mem_emit ((inst), 2, (mem)); \ - } while (0) - -#define x86_not_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_membase_emit ((inst), 2, (basereg), (disp)); \ - } while (0) - -#define x86_not_reg(inst,reg) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_reg_emit ((inst), 2, (reg)); \ - } while (0) - -#define x86_neg_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_mem_emit ((inst), 3, (mem)); \ - } while (0) - -#define x86_neg_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_membase_emit ((inst), 3, (basereg), (disp)); \ - } while (0) - -#define x86_neg_reg(inst,reg) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_reg_emit ((inst), 3, (reg)); \ - } while (0) - -#define x86_nop(inst) do { *(inst)++ = (unsigned char)0x90; } while (0) - -#define x86_alu_reg_imm(inst,opc,reg,imm) \ - do { \ - if ((reg) == X86_EAX) { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ - x86_imm_emit32 ((inst), (imm)); \ - break; \ - } \ - if (x86_is_imm8((imm))) { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x83; \ - x86_reg_emit ((inst), (opc), (reg)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0x81; \ - x86_reg_emit ((inst), (opc), (reg)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_alu_mem_imm(inst,opc,mem,imm) \ - do { \ - if (x86_is_imm8((imm))) { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0x83; \ - x86_mem_emit ((inst), (opc), (mem)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 10); \ - *(inst)++ = (unsigned char)0x81; \ - x86_mem_emit ((inst), (opc), (mem)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_alu_membase_imm(inst,opc,basereg,disp,imm) \ - do { \ - if (x86_is_imm8((imm))) { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x83; \ - x86_membase_emit ((inst), (opc), (basereg), (disp)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x81; \ - x86_membase_emit ((inst), (opc), (basereg), (disp)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_alu_membase8_imm(inst,opc,basereg,disp,imm) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x80; \ - x86_membase_emit ((inst), (opc), (basereg), (disp)); \ - x86_imm_emit8 ((inst), (imm)); \ - } while (0) - -#define x86_alu_mem_reg(inst,opc,mem,reg) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_alu_membase_reg(inst,opc,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_alu_reg_reg(inst,opc,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -/** - * @x86_alu_reg8_reg8: - * Supports ALU operations between two 8-bit registers. - * dreg := dreg opc reg - * X86_Reg_No enum is used to specify the registers. - * Additionally is_*_h flags are used to specify what part - * of a given 32-bit register is used - high (TRUE) or low (FALSE). - * For example: dreg = X86_EAX, is_dreg_h = TRUE -> use AH - */ -#define x86_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 2; \ - x86_reg8_emit ((inst), (dreg), (reg), (is_dreg_h), (is_reg_h)); \ - } while (0) - -#define x86_alu_reg_mem(inst,opc,reg,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_alu_reg_membase(inst,opc,reg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_test_reg_imm(inst,reg,imm) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - if ((reg) == X86_EAX) { \ - *(inst)++ = (unsigned char)0xa9; \ - } else { \ - *(inst)++ = (unsigned char)0xf7; \ - x86_reg_emit ((inst), 0, (reg)); \ - } \ - x86_imm_emit32 ((inst), (imm)); \ - } while (0) - -#define x86_test_mem_imm(inst,mem,imm) \ - do { \ - x86_codegen_pre(&(inst), 10); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_mem_emit ((inst), 0, (mem)); \ - x86_imm_emit32 ((inst), (imm)); \ - } while (0) - -#define x86_test_membase_imm(inst,basereg,disp,imm) \ - do { \ - x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - x86_imm_emit32 ((inst), (imm)); \ - } while (0) - -#define x86_test_reg_reg(inst,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0x85; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - } while (0) - -#define x86_test_mem_reg(inst,mem,reg) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0x85; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_test_membase_reg(inst,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x85; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_shift_reg_imm(inst,opc,reg,imm) \ - do { \ - if ((imm) == 1) { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd1; \ - x86_reg_emit ((inst), (opc), (reg)); \ - } else { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0xc1; \ - x86_reg_emit ((inst), (opc), (reg)); \ - x86_imm_emit8 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_shift_mem_imm(inst,opc,mem,imm) \ - do { \ - if ((imm) == 1) { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xd1; \ - x86_mem_emit ((inst), (opc), (mem)); \ - } else { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0xc1; \ - x86_mem_emit ((inst), (opc), (mem)); \ - x86_imm_emit8 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_shift_membase_imm(inst,opc,basereg,disp,imm) \ - do { \ - if ((imm) == 1) { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xd1; \ - x86_membase_emit ((inst), (opc), (basereg), (disp)); \ - } else { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xc1; \ - x86_membase_emit ((inst), (opc), (basereg), (disp)); \ - x86_imm_emit8 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_shift_reg(inst,opc,reg) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd3; \ - x86_reg_emit ((inst), (opc), (reg)); \ - } while (0) - -#define x86_shift_mem(inst,opc,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xd3; \ - x86_mem_emit ((inst), (opc), (mem)); \ - } while (0) - -#define x86_shift_membase(inst,opc,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xd3; \ - x86_membase_emit ((inst), (opc), (basereg), (disp)); \ - } while (0) - -/* - * Multi op shift missing. - */ - -#define x86_shrd_reg(inst,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xad; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - } while (0) - -#define x86_shrd_reg_imm(inst,dreg,reg,shamt) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xac; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - x86_imm_emit8 ((inst), (shamt)); \ - } while (0) - -#define x86_shld_reg(inst,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xa5; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - } while (0) - -#define x86_shld_reg_imm(inst,dreg,reg,shamt) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xa4; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - x86_imm_emit8 ((inst), (shamt)); \ - } while (0) - -/* - * EDX:EAX = EAX * rm - */ -#define x86_mul_reg(inst,reg,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_reg_emit ((inst), 4 + ((is_signed) ? 1 : 0), (reg)); \ - } while (0) - -#define x86_mul_mem(inst,mem,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_mem_emit ((inst), 4 + ((is_signed) ? 1 : 0), (mem)); \ - } while (0) - -#define x86_mul_membase(inst,basereg,disp,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_membase_emit ((inst), 4 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ - } while (0) - -/* - * r *= rm - */ -#define x86_imul_reg_reg(inst,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xaf; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#define x86_imul_reg_mem(inst,reg,mem) \ - do { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xaf; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_imul_reg_membase(inst,reg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xaf; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -/* - * dreg = rm * imm - */ -#define x86_imul_reg_reg_imm(inst,dreg,reg,imm) \ - do { \ - if (x86_is_imm8 ((imm))) { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x6b; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0x69; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_imul_reg_mem_imm(inst,reg,mem,imm) \ - do { \ - if (x86_is_imm8 ((imm))) { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0x6b; \ - x86_mem_emit ((inst), (reg), (mem)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0x69; \ - x86_reg_emit ((inst), (reg), (mem)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_imul_reg_membase_imm(inst,reg,basereg,disp,imm) \ - do { \ - if (x86_is_imm8 ((imm))) { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x6b; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x69; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -/* - * divide EDX:EAX by rm; - * eax = quotient, edx = remainder - */ - -#define x86_div_reg(inst,reg,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_reg_emit ((inst), 6 + ((is_signed) ? 1 : 0), (reg)); \ - } while (0) - -#define x86_div_mem(inst,mem,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_mem_emit ((inst), 6 + ((is_signed) ? 1 : 0), (mem)); \ - } while (0) - -#define x86_div_membase(inst,basereg,disp,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_membase_emit ((inst), 6 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ - } while (0) - -#define x86_mov_mem_reg(inst,mem,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 7); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x88; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x89; break; \ - default: assert (0); \ - } \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_mov_regp_reg(inst,regp,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x88; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x89; break; \ - default: assert (0); \ - } \ - x86_regp_emit ((inst), (reg), (regp)); \ - } while (0) - -#define x86_mov_membase_reg(inst,basereg,disp,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x88; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x89; break; \ - default: assert (0); \ - } \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x88; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x89; break; \ - default: assert (0); \ - } \ - x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ - } while (0) - -#define x86_mov_reg_reg(inst,dreg,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x8a; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#define x86_mov_reg_mem(inst,reg,mem,size) \ - do { \ - x86_codegen_pre(&(inst), 7); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x8a; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define kMovRegMembasePadding (2 + kMaxMembaseEmitPadding) - -#define x86_mov_reg_membase(inst,reg,basereg,disp,size) \ - do { \ - x86_codegen_pre(&(inst), kMovRegMembasePadding); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x8a; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x8a; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ - } while (0) - -/* - * Note: x86_clear_reg () chacnges the condition code! - */ -#define x86_clear_reg(inst,reg) x86_alu_reg_reg((inst), X86_XOR, (reg), (reg)) - -#define x86_mov_reg_imm(inst,reg,imm) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0xb8 + (reg); \ - x86_imm_emit32 ((inst), (imm)); \ - } while (0) - -#define x86_mov_mem_imm(inst,mem,imm,size) \ - do { \ - if ((size) == 1) { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0xc6; \ - x86_mem_emit ((inst), 0, (mem)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else if ((size) == 2) { \ - x86_codegen_pre(&(inst), 9); \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - *(inst)++ = (unsigned char)0xc7; \ - x86_mem_emit ((inst), 0, (mem)); \ - x86_imm_emit16 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 10); \ - *(inst)++ = (unsigned char)0xc7; \ - x86_mem_emit ((inst), 0, (mem)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_mov_membase_imm(inst,basereg,disp,imm,size) \ - do { \ - if ((size) == 1) { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xc6; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else if ((size) == 2) { \ - x86_codegen_pre(&(inst), 4 + kMaxMembaseEmitPadding); \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - *(inst)++ = (unsigned char)0xc7; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - x86_imm_emit16 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xc7; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) \ - do { \ - if ((size) == 1) { \ - x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ - *(inst)++ = (unsigned char)0xc6; \ - x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else if ((size) == 2) { \ - x86_codegen_pre(&(inst), 4 + kMaxMemindexEmitPadding); \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - *(inst)++ = (unsigned char)0xc7; \ - x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ - x86_imm_emit16 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 5 + kMaxMemindexEmitPadding); \ - *(inst)++ = (unsigned char)0xc7; \ - x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_lea_mem(inst,reg,mem) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0x8d; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_lea_membase(inst,reg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x8d; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_lea_memindex(inst,reg,basereg,disp,indexreg,shift) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMemindexEmitPadding); \ - *(inst)++ = (unsigned char)0x8d; \ - x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ - } while (0) - -#define x86_widen_reg(inst,dreg,reg,is_signed,is_half) \ - do { \ - unsigned char op = 0xb6; \ - g_assert (is_half || X86_IS_BYTE_REG (reg)); \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) op += 0x08; \ - if ((is_half)) op += 0x01; \ - *(inst)++ = op; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#define x86_widen_mem(inst,dreg,mem,is_signed,is_half) \ - do { \ - unsigned char op = 0xb6; \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) op += 0x08; \ - if ((is_half)) op += 0x01; \ - *(inst)++ = op; \ - x86_mem_emit ((inst), (dreg), (mem)); \ - } while (0) - -#define x86_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) \ - do { \ - unsigned char op = 0xb6; \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) op += 0x08; \ - if ((is_half)) op += 0x01; \ - *(inst)++ = op; \ - x86_membase_emit ((inst), (dreg), (basereg), (disp)); \ - } while (0) - -#define x86_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) \ - do { \ - unsigned char op = 0xb6; \ - x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) op += 0x08; \ - if ((is_half)) op += 0x01; \ - *(inst)++ = op; \ - x86_memindex_emit ((inst), (dreg), (basereg), (disp), (indexreg), (shift)); \ - } while (0) - -#define x86_cdq(inst) do { *(inst)++ = (unsigned char)0x99; } while (0) -#define x86_wait(inst) do { *(inst)++ = (unsigned char)0x9b; } while (0) - -#define x86_fp_op_mem(inst,opc,mem,is_double) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ - x86_mem_emit ((inst), (opc), (mem)); \ - } while (0) - -#define x86_fp_op_membase(inst,opc,basereg,disp,is_double) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ - x86_membase_emit ((inst), (opc), (basereg), (disp)); \ - } while (0) - -#define x86_fp_op(inst,opc,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd8; \ - *(inst)++ = (unsigned char)0xc0+((opc)<<3)+((index)&0x07); \ - } while (0) - -#define x86_fp_op_reg(inst,opc,index,pop_stack) \ - do { \ - static const unsigned char map[] = { 0, 1, 2, 3, 5, 4, 7, 6, 8}; \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (pop_stack) ? (unsigned char)0xde : (unsigned char)0xdc; \ - *(inst)++ = (unsigned char)0xc0+(map[(opc)]<<3)+((index)&0x07); \ - } while (0) - -/** - * @x86_fp_int_op_membase - * Supports FPU operations between ST(0) and integer operand in memory. - * Operation encoded using X86_FP_Opcode enum. - * Operand is addressed by [basereg + disp]. - * is_int specifies whether operand is int32 (TRUE) or int16 (FALSE). - */ -#define x86_fp_int_op_membase(inst,opc,basereg,disp,is_int) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (is_int) ? (unsigned char)0xda : (unsigned char)0xde; \ - x86_membase_emit ((inst), opc, (basereg), (disp)); \ - } while (0) - -#define x86_fstp(inst,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xdd; \ - *(inst)++ = (unsigned char)0xd8+(index); \ - } while (0) - -#define x86_fcompp(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xde; \ - *(inst)++ = (unsigned char)0xd9; \ - } while (0) - -#define x86_fucompp(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xda; \ - *(inst)++ = (unsigned char)0xe9; \ - } while (0) - -#define x86_fnstsw(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xdf; \ - *(inst)++ = (unsigned char)0xe0; \ - } while (0) - -#define x86_fnstcw(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xd9; \ - x86_mem_emit ((inst), 7, (mem)); \ - } while (0) - -#define x86_fnstcw_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xd9; \ - x86_membase_emit ((inst), 7, (basereg), (disp)); \ - } while (0) - -#define x86_fldcw(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xd9; \ - x86_mem_emit ((inst), 5, (mem)); \ - } while (0) - -#define x86_fldcw_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xd9; \ - x86_membase_emit ((inst), 5, (basereg), (disp)); \ - } while (0) - -#define x86_fchs(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd9; \ - *(inst)++ = (unsigned char)0xe0; \ - } while (0) - -#define x86_frem(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd9; \ - *(inst)++ = (unsigned char)0xf8; \ - } while (0) - -#define x86_fxch(inst,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd9; \ - *(inst)++ = (unsigned char)0xc8 + ((index) & 0x07); \ - } while (0) - -#define x86_fcomi(inst,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xdb; \ - *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ - } while (0) - -#define x86_fcomip(inst,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xdf; \ - *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ - } while (0) - -#define x86_fucomi(inst,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xdb; \ - *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ - } while (0) - -#define x86_fucomip(inst,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xdf; \ - *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ - } while (0) - -#define x86_fld(inst,mem,is_double) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ - x86_mem_emit ((inst), 0, (mem)); \ - } while (0) - -#define x86_fld_membase(inst,basereg,disp,is_double) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - } while (0) - -#define x86_fld80_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xdb; \ - x86_mem_emit ((inst), 5, (mem)); \ - } while (0) - -#define x86_fld80_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xdb; \ - x86_membase_emit ((inst), 5, (basereg), (disp)); \ - } while (0) - -#define x86_fild(inst,mem,is_long) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - if ((is_long)) { \ - *(inst)++ = (unsigned char)0xdf; \ - x86_mem_emit ((inst), 5, (mem)); \ - } else { \ - *(inst)++ = (unsigned char)0xdb; \ - x86_mem_emit ((inst), 0, (mem)); \ - } \ - } while (0) - -#define x86_fild_membase(inst,basereg,disp,is_long) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - if ((is_long)) { \ - *(inst)++ = (unsigned char)0xdf; \ - x86_membase_emit ((inst), 5, (basereg), (disp)); \ - } else { \ - *(inst)++ = (unsigned char)0xdb; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - } \ - } while (0) - -#define x86_fld_reg(inst,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd9; \ - *(inst)++ = (unsigned char)0xc0 + ((index) & 0x07); \ - } while (0) - -#define x86_fldz(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd9; \ - *(inst)++ = (unsigned char)0xee; \ - } while (0) - -#define x86_fld1(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd9; \ - *(inst)++ = (unsigned char)0xe8; \ - } while (0) - -#define x86_fldpi(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd9; \ - *(inst)++ = (unsigned char)0xeb; \ - } while (0) - -#define x86_fst(inst,mem,is_double,pop_stack) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ - x86_mem_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (mem)); \ - } while (0) - -#define x86_fst_membase(inst,basereg,disp,is_double,pop_stack) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ - x86_membase_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (basereg), (disp)); \ - } while (0) - -#define x86_fst80_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xdb; \ - x86_mem_emit ((inst), 7, (mem)); \ - } while (0) - - -#define x86_fst80_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xdb; \ - x86_membase_emit ((inst), 7, (basereg), (disp)); \ - } while (0) - - -#define x86_fist_pop(inst,mem,is_long) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - if ((is_long)) { \ - *(inst)++ = (unsigned char)0xdf; \ - x86_mem_emit ((inst), 7, (mem)); \ - } else { \ - *(inst)++ = (unsigned char)0xdb; \ - x86_mem_emit ((inst), 3, (mem)); \ - } \ - } while (0) - -#define x86_fist_pop_membase(inst,basereg,disp,is_long) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - if ((is_long)) { \ - *(inst)++ = (unsigned char)0xdf; \ - x86_membase_emit ((inst), 7, (basereg), (disp)); \ - } else { \ - *(inst)++ = (unsigned char)0xdb; \ - x86_membase_emit ((inst), 3, (basereg), (disp)); \ - } \ - } while (0) - -#define x86_fstsw(inst) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x9b; \ - *(inst)++ = (unsigned char)0xdf; \ - *(inst)++ = (unsigned char)0xe0; \ - } while (0) - -/** - * @x86_fist_membase - * Converts content of ST(0) to integer and stores it at memory location - * addressed by [basereg + disp]. - * is_int specifies whether destination is int32 (TRUE) or int16 (FALSE). - */ -#define x86_fist_membase(inst,basereg,disp,is_int) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - if ((is_int)) { \ - *(inst)++ = (unsigned char)0xdb; \ - x86_membase_emit ((inst), 2, (basereg), (disp)); \ - } else { \ - *(inst)++ = (unsigned char)0xdf; \ - x86_membase_emit ((inst), 2, (basereg), (disp)); \ - } \ - } while (0) - - -#define x86_push_reg(inst,reg) \ - do { \ - *(inst)++ = (unsigned char)0x50 + (reg); \ - } while (0) - -#define x86_push_regp(inst,reg) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xff; \ - x86_regp_emit ((inst), 6, (reg)); \ - } while (0) - -#define x86_push_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xff; \ - x86_mem_emit ((inst), 6, (mem)); \ - } while (0) - -#define x86_push_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xff; \ - x86_membase_emit ((inst), 6, (basereg), (disp)); \ - } while (0) - -#define x86_push_memindex(inst,basereg,disp,indexreg,shift) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMemindexEmitPadding); \ - *(inst)++ = (unsigned char)0xff; \ - x86_memindex_emit ((inst), 6, (basereg), (disp), (indexreg), (shift)); \ - } while (0) - -#define x86_push_imm_template(inst) x86_push_imm (inst, 0xf0f0f0f0) - -#define x86_push_imm(inst,imm) \ - do { \ - int _imm = (int) (imm); \ - if (x86_is_imm8 (_imm)) { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0x6A; \ - x86_imm_emit8 ((inst), (_imm)); \ - } else { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0x68; \ - x86_imm_emit32 ((inst), (_imm)); \ - } \ - } while (0) - -#define x86_pop_reg(inst,reg) \ - do { \ - *(inst)++ = (unsigned char)0x58 + (reg); \ - } while (0) - -#define x86_pop_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0x87; \ - x86_mem_emit ((inst), 0, (mem)); \ - } while (0) - -#define x86_pop_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x87; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - } while (0) - -#define x86_pushad(inst) do { *(inst)++ = (unsigned char)0x60; } while (0) -#define x86_pushfd(inst) do { *(inst)++ = (unsigned char)0x9c; } while (0) -#define x86_popad(inst) do { *(inst)++ = (unsigned char)0x61; } while (0) -#define x86_popfd(inst) do { *(inst)++ = (unsigned char)0x9d; } while (0) - -#define x86_loop(inst,imm) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xe2; \ - x86_imm_emit8 ((inst), (imm)); \ - } while (0) - -#define x86_loope(inst,imm) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xe1; \ - x86_imm_emit8 ((inst), (imm)); \ - } while (0) - -#define x86_loopne(inst,imm) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xe0; \ - x86_imm_emit8 ((inst), (imm)); \ - } while (0) - -#if defined(TARGET_X86) -#define x86_jump32(inst,imm) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0xe9; \ - x86_imm_emit32 ((inst), (imm)); \ - } while (0) - -#define x86_jump8(inst,imm) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xeb; \ - x86_imm_emit8 ((inst), (imm)); \ - } while (0) -#elif defined(TARGET_AMD64) -/* These macros are used directly from mini-amd64.c and other */ -/* amd64 specific files, so they need to be instrumented directly. */ -#define x86_jump32(inst,imm) \ - do { \ - amd64_codegen_pre(inst); \ - *(inst)++ = (unsigned char)0xe9; \ - x86_imm_emit32 ((inst), (imm)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define x86_jump8(inst,imm) \ - do { \ - amd64_codegen_pre(inst); \ - *(inst)++ = (unsigned char)0xeb; \ - x86_imm_emit8 ((inst), (imm)); \ - amd64_codegen_post(inst); \ - } while (0) -#endif - -#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) -#define x86_jump_reg(inst,reg) do { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0x83; /* and */ \ - x86_reg_emit ((inst), 4, (reg)); /* reg */ \ - *(inst)++ = (unsigned char)nacl_align_byte; \ - *(inst)++ = (unsigned char)0xff; \ - x86_reg_emit ((inst), 4, (reg)); \ - } while (0) - -/* Let's hope ECX is available for these... */ -#define x86_jump_mem(inst,mem) do { \ - x86_mov_reg_mem(inst, (X86_ECX), (mem), 4); \ - x86_jump_reg(inst, (X86_ECX)); \ - } while (0) - -#define x86_jump_membase(inst,basereg,disp) do { \ - x86_mov_reg_membase(inst, (X86_ECX), basereg, disp, 4); \ - x86_jump_reg(inst, (X86_ECX)); \ - } while (0) - -/* like x86_jump_membase, but force a 32-bit displacement */ -#define x86_jump_membase32(inst,basereg,disp) do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0x8b; \ - x86_address_byte ((inst), 2, X86_ECX, (basereg)); \ - x86_imm_emit32 ((inst), (disp)); \ - x86_jump_reg(inst, (X86_ECX)); \ - } while (0) -#else /* __native_client_codegen__ */ -#define x86_jump_reg(inst,reg) \ - do { \ - *(inst)++ = (unsigned char)0xff; \ - x86_reg_emit ((inst), 4, (reg)); \ - } while (0) - -#define x86_jump_mem(inst,mem) \ - do { \ - *(inst)++ = (unsigned char)0xff; \ - x86_mem_emit ((inst), 4, (mem)); \ - } while (0) - -#define x86_jump_membase(inst,basereg,disp) \ - do { \ - *(inst)++ = (unsigned char)0xff; \ - x86_membase_emit ((inst), 4, (basereg), (disp)); \ - } while (0) -#endif /* __native_client_codegen__ */ -/* - * target is a pointer in our buffer. - */ -#define x86_jump_code_body(inst,target) \ - do { \ - int t; \ - x86_codegen_pre(&(inst), 2); \ - t = (unsigned char*)(target) - (inst) - 2; \ - if (x86_is_imm8(t)) { \ - x86_jump8 ((inst), t); \ - } else { \ - x86_codegen_pre(&(inst), 5); \ - t = (unsigned char*)(target) - (inst) - 5; \ - x86_jump32 ((inst), t); \ - } \ - } while (0) - -#if defined(__default_codegen__) -#define x86_jump_code(inst,target) \ - do { \ - x86_jump_code_body((inst),(target)); \ - } while (0) -#elif defined(__native_client_codegen__) && defined(TARGET_X86) -#define x86_jump_code(inst,target) \ - do { \ - guint8* jump_start = (inst); \ - x86_jump_code_body((inst),(target)); \ - x86_patch(jump_start, (target)); \ - } while (0) -#elif defined(__native_client_codegen__) && defined(TARGET_AMD64) -#define x86_jump_code(inst,target) \ - do { \ - /* jump_code_body is used twice because there are offsets */ \ - /* calculated based on the IP, which can change after the */ \ - /* call to amd64_codegen_post */ \ - amd64_codegen_pre(inst); \ - x86_jump_code_body((inst),(target)); \ - inst = amd64_codegen_post(inst); \ - x86_jump_code_body((inst),(target)); \ - } while (0) -#endif /* __native_client_codegen__ */ - -#define x86_jump_disp(inst,disp) \ - do { \ - int t = (disp) - 2; \ - if (x86_is_imm8(t)) { \ - x86_jump8 ((inst), t); \ - } else { \ - t -= 3; \ - x86_jump32 ((inst), t); \ - } \ - } while (0) - -#if defined(TARGET_X86) -#define x86_branch8(inst,cond,imm,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)]; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)]; \ - x86_imm_emit8 ((inst), (imm)); \ - } while (0) - -#define x86_branch32(inst,cond,imm,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] + 0x10; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x10; \ - x86_imm_emit32 ((inst), (imm)); \ - } while (0) -#elif defined(TARGET_AMD64) -/* These macros are used directly from mini-amd64.c and other */ -/* amd64 specific files, so they need to be instrumented directly. */ -#define x86_branch8(inst,cond,imm,is_signed) \ - do { \ - amd64_codegen_pre(inst); \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)]; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)]; \ - x86_imm_emit8 ((inst), (imm)); \ - amd64_codegen_post(inst); \ - } while (0) -#define x86_branch32(inst,cond,imm,is_signed) \ - do { \ - amd64_codegen_pre(inst); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] + 0x10; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x10; \ - x86_imm_emit32 ((inst), (imm)); \ - amd64_codegen_post(inst); \ - } while (0) -#endif - -#if defined(TARGET_X86) -#define x86_branch(inst,cond,target,is_signed) \ - do { \ - int offset; \ - guint8* branch_start; \ - x86_codegen_pre(&(inst), 2); \ - offset = (target) - (inst) - 2; \ - branch_start = (inst); \ - if (x86_is_imm8 ((offset))) \ - x86_branch8 ((inst), (cond), offset, (is_signed)); \ - else { \ - x86_codegen_pre(&(inst), 6); \ - offset = (target) - (inst) - 6; \ - x86_branch32 ((inst), (cond), offset, (is_signed)); \ - } \ - x86_patch(branch_start, (target)); \ - } while (0) -#elif defined(TARGET_AMD64) -/* This macro is used directly from mini-amd64.c and other */ -/* amd64 specific files, so it needs to be instrumented directly. */ - -#define x86_branch_body(inst,cond,target,is_signed) \ - do { \ - int offset = (target) - (inst) - 2; \ - if (x86_is_imm8 ((offset))) \ - x86_branch8 ((inst), (cond), offset, (is_signed)); \ - else { \ - offset = (target) - (inst) - 6; \ - x86_branch32 ((inst), (cond), offset, (is_signed)); \ - } \ - } while (0) - -#if defined(__default_codegen__) -#define x86_branch(inst,cond,target,is_signed) \ - do { \ - x86_branch_body((inst),(cond),(target),(is_signed)); \ - } while (0) -#elif defined(__native_client_codegen__) -#define x86_branch(inst,cond,target,is_signed) \ - do { \ - /* branch_body is used twice because there are offsets */ \ - /* calculated based on the IP, which can change after */ \ - /* the call to amd64_codegen_post */ \ - amd64_codegen_pre(inst); \ - x86_branch_body((inst),(cond),(target),(is_signed)); \ - inst = amd64_codegen_post(inst); \ - x86_branch_body((inst),(cond),(target),(is_signed)); \ - } while (0) -#endif /* __native_client_codegen__ */ - -#endif /* TARGET_AMD64 */ - -#define x86_branch_disp(inst,cond,disp,is_signed) \ - do { \ - int offset = (disp) - 2; \ - if (x86_is_imm8 ((offset))) \ - x86_branch8 ((inst), (cond), offset, (is_signed)); \ - else { \ - offset -= 4; \ - x86_branch32 ((inst), (cond), offset, (is_signed)); \ - } \ - } while (0) - -#define x86_set_reg(inst,cond,reg,is_signed) \ - do { \ - g_assert (X86_IS_BYTE_REG (reg)); \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ - x86_reg_emit ((inst), 0, (reg)); \ - } while (0) - -#define x86_set_mem(inst,cond,mem,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ - x86_mem_emit ((inst), 0, (mem)); \ - } while (0) - -#define x86_set_membase(inst,cond,basereg,disp,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - } while (0) - -#define x86_call_imm_body(inst,disp) \ - do { \ - *(inst)++ = (unsigned char)0xe8; \ - x86_imm_emit32 ((inst), (int)(disp)); \ - } while (0) - -#define x86_call_imm(inst,disp) \ - do { \ - x86_call_sequence_pre((inst)); \ - x86_call_imm_body((inst), (disp)); \ - x86_call_sequence_post((inst)); \ - } while (0) - - -#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) -#define x86_call_reg_internal(inst,reg) \ - do { \ - *(inst)++ = (unsigned char)0x83; /* and */ \ - x86_reg_emit ((inst), 4, (reg)); /* reg */ \ - *(inst)++ = (unsigned char)nacl_align_byte; \ - *(inst)++ = (unsigned char)0xff; /* call */ \ - x86_reg_emit ((inst), 2, (reg)); /* reg */ \ - } while (0) - -#define x86_call_reg(inst, reg) do { \ - x86_call_sequence_pre((inst)); \ - x86_call_reg_internal(inst, reg); \ - x86_call_sequence_post((inst)); \ - } while (0) - - -/* It appears that x86_call_mem() is never used, so I'm leaving it out. */ -#define x86_call_membase(inst,basereg,disp) do { \ - x86_call_sequence_pre((inst)); \ - /* x86_mov_reg_membase() inlined so its fixed size */ \ - *(inst)++ = (unsigned char)0x8b; \ - x86_address_byte ((inst), 2, (X86_ECX), (basereg)); \ - x86_imm_emit32 ((inst), (disp)); \ - x86_call_reg_internal(inst, X86_ECX); \ - x86_call_sequence_post((inst)); \ - } while (0) -#else /* __native_client_codegen__ */ -#define x86_call_reg(inst,reg) \ - do { \ - *(inst)++ = (unsigned char)0xff; \ - x86_reg_emit ((inst), 2, (reg)); \ - } while (0) - -#define x86_call_mem(inst,mem) \ - do { \ - *(inst)++ = (unsigned char)0xff; \ - x86_mem_emit ((inst), 2, (mem)); \ - } while (0) - -#define x86_call_membase(inst,basereg,disp) \ - do { \ - *(inst)++ = (unsigned char)0xff; \ - x86_membase_emit ((inst), 2, (basereg), (disp)); \ - } while (0) -#endif /* __native_client_codegen__ */ - - -#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) - -#define x86_call_code(inst,target) \ - do { \ - int _x86_offset; \ - guint8* call_start; \ - guint8* _aligned_start; \ - x86_call_sequence_pre_val((inst)); \ - _x86_offset = (unsigned char*)(target) - (inst); \ - _x86_offset -= 5; \ - x86_call_imm_body ((inst), _x86_offset); \ - _aligned_start = x86_call_sequence_post_val((inst)); \ - call_start = _aligned_start; \ - _x86_offset = (unsigned char*)(target) - (_aligned_start); \ - _x86_offset -= 5; \ - x86_call_imm_body ((_aligned_start), _x86_offset); \ - x86_patch(call_start, (target)); \ - } while (0) - -#define SIZE_OF_RET 6 -#define x86_ret(inst) do { \ - *(inst)++ = (unsigned char)0x59; /* pop ecx */ \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0x83; /* and 0xffffffff, ecx */ \ - *(inst)++ = (unsigned char)0xe1; \ - *(inst)++ = (unsigned char)nacl_align_byte; \ - *(inst)++ = (unsigned char)0xff; /* jmp ecx */ \ - *(inst)++ = (unsigned char)0xe1; \ - } while (0) - -/* pop return address */ -/* pop imm bytes from stack */ -/* return */ -#define x86_ret_imm(inst,imm) do { \ - *(inst)++ = (unsigned char)0x59; /* pop ecx */ \ - x86_alu_reg_imm ((inst), X86_ADD, X86_ESP, imm); \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0x83; /* and 0xffffffff, ecx */ \ - *(inst)++ = (unsigned char)0xe1; \ - *(inst)++ = (unsigned char)nacl_align_byte; \ - *(inst)++ = (unsigned char)0xff; /* jmp ecx */ \ - *(inst)++ = (unsigned char)0xe1; \ -} while (0) -#else /* __native_client_codegen__ */ - -#define x86_call_code(inst,target) \ - do { \ - int _x86_offset; \ - _x86_offset = (unsigned char*)(target) - (inst); \ - _x86_offset -= 5; \ - x86_call_imm_body ((inst), _x86_offset); \ - } while (0) - -#define x86_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) - -#define x86_ret_imm(inst,imm) \ - do { \ - if ((imm) == 0) { \ - x86_ret ((inst)); \ - } else { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0xc2; \ - x86_imm_emit16 ((inst), (imm)); \ - } \ - } while (0) -#endif /* __native_client_codegen__ */ - -#define x86_cmov_reg(inst,cond,is_signed,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char) 0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#define x86_cmov_mem(inst,cond,is_signed,reg,mem) \ - do { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char) 0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_cmov_membase(inst,cond,is_signed,reg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char) 0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_enter(inst,framesize) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0xc8; \ - x86_imm_emit16 ((inst), (framesize)); \ - *(inst)++ = 0; \ - } while (0) - -#define x86_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) -#define x86_sahf(inst) do { *(inst)++ = (unsigned char)0x9e; } while (0) - -#define x86_fsin(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfe; } while (0) -#define x86_fcos(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xff; } while (0) -#define x86_fabs(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe1; } while (0) -#define x86_ftst(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe4; } while (0) -#define x86_fxam(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe5; } while (0) -#define x86_fpatan(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf3; } while (0) -#define x86_fprem(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf8; } while (0) -#define x86_fprem1(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf5; } while (0) -#define x86_frndint(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfc; } while (0) -#define x86_fsqrt(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfa; } while (0) -#define x86_fptan(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf2; } while (0) - -#define x86_padding(inst,size) \ - do { \ - switch ((size)) { \ - case 1: x86_nop ((inst)); break; \ - case 2: *(inst)++ = 0x8b; \ - *(inst)++ = 0xc0; break; \ - case 3: *(inst)++ = 0x8d; *(inst)++ = 0x6d; \ - *(inst)++ = 0x00; break; \ - case 4: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ - *(inst)++ = 0x24; *(inst)++ = 0x00; \ - break; \ - case 5: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ - *(inst)++ = 0x24; *(inst)++ = 0x00; \ - x86_nop ((inst)); break; \ - case 6: *(inst)++ = 0x8d; *(inst)++ = 0xad; \ - *(inst)++ = 0x00; *(inst)++ = 0x00; \ - *(inst)++ = 0x00; *(inst)++ = 0x00; \ - break; \ - case 7: *(inst)++ = 0x8d; *(inst)++ = 0xa4; \ - *(inst)++ = 0x24; *(inst)++ = 0x00; \ - *(inst)++ = 0x00; *(inst)++ = 0x00; \ - *(inst)++ = 0x00; break; \ - default: assert (0); \ - } \ - } while (0) - -#ifdef __native_client_codegen__ - -#define kx86NaClLengthOfCallReg 5 -#define kx86NaClLengthOfCallImm 5 -#define kx86NaClLengthOfCallMembase (kx86NaClLengthOfCallReg + 6) - -#endif /* __native_client_codegen__ */ - -#define x86_prolog(inst,frame_size,reg_mask) \ - do { \ - unsigned i, m = 1; \ - x86_enter ((inst), (frame_size)); \ - for (i = 0; i < X86_NREG; ++i, m <<= 1) { \ - if ((reg_mask) & m) \ - x86_push_reg ((inst), i); \ - } \ - } while (0) - -#define x86_epilog(inst,reg_mask) \ - do { \ - unsigned i, m = 1 << X86_EDI; \ - for (i = X86_EDI; m != 0; i--, m=m>>1) { \ - if ((reg_mask) & m) \ - x86_pop_reg ((inst), i); \ - } \ - x86_leave ((inst)); \ - x86_ret ((inst)); \ - } while (0) - - -typedef enum { - X86_SSE_SQRT = 0x51, - X86_SSE_RSQRT = 0x52, - X86_SSE_RCP = 0x53, - X86_SSE_ADD = 0x58, - X86_SSE_DIV = 0x5E, - X86_SSE_MUL = 0x59, - X86_SSE_SUB = 0x5C, - X86_SSE_MIN = 0x5D, - X86_SSE_MAX = 0x5F, - X86_SSE_COMP = 0xC2, - X86_SSE_AND = 0x54, - X86_SSE_ANDN = 0x55, - X86_SSE_OR = 0x56, - X86_SSE_XOR = 0x57, - X86_SSE_UNPCKL = 0x14, - X86_SSE_UNPCKH = 0x15, - - X86_SSE_ADDSUB = 0xD0, - X86_SSE_HADD = 0x7C, - X86_SSE_HSUB = 0x7D, - X86_SSE_MOVSHDUP = 0x16, - X86_SSE_MOVSLDUP = 0x12, - X86_SSE_MOVDDUP = 0x12, - - X86_SSE_PAND = 0xDB, - X86_SSE_POR = 0xEB, - X86_SSE_PXOR = 0xEF, - - X86_SSE_PADDB = 0xFC, - X86_SSE_PADDW = 0xFD, - X86_SSE_PADDD = 0xFE, - X86_SSE_PADDQ = 0xD4, - - X86_SSE_PSUBB = 0xF8, - X86_SSE_PSUBW = 0xF9, - X86_SSE_PSUBD = 0xFA, - X86_SSE_PSUBQ = 0xFB, - - X86_SSE_PMAXSB = 0x3C, /*sse41*/ - X86_SSE_PMAXSW = 0xEE, - X86_SSE_PMAXSD = 0x3D, /*sse41*/ - - X86_SSE_PMAXUB = 0xDE, - X86_SSE_PMAXUW = 0x3E, /*sse41*/ - X86_SSE_PMAXUD = 0x3F, /*sse41*/ - - X86_SSE_PMINSB = 0x38, /*sse41*/ - X86_SSE_PMINSW = 0xEA, - X86_SSE_PMINSD = 0x39,/*sse41*/ - - X86_SSE_PMINUB = 0xDA, - X86_SSE_PMINUW = 0x3A, /*sse41*/ - X86_SSE_PMINUD = 0x3B, /*sse41*/ - - X86_SSE_PAVGB = 0xE0, - X86_SSE_PAVGW = 0xE3, - - X86_SSE_PCMPEQB = 0x74, - X86_SSE_PCMPEQW = 0x75, - X86_SSE_PCMPEQD = 0x76, - X86_SSE_PCMPEQQ = 0x29, /*sse41*/ - - X86_SSE_PCMPGTB = 0x64, - X86_SSE_PCMPGTW = 0x65, - X86_SSE_PCMPGTD = 0x66, - X86_SSE_PCMPGTQ = 0x37, /*sse42*/ - - X86_SSE_PSADBW = 0xf6, - - X86_SSE_PSHUFD = 0x70, - - X86_SSE_PUNPCKLBW = 0x60, - X86_SSE_PUNPCKLWD = 0x61, - X86_SSE_PUNPCKLDQ = 0x62, - X86_SSE_PUNPCKLQDQ = 0x6C, - - X86_SSE_PUNPCKHBW = 0x68, - X86_SSE_PUNPCKHWD = 0x69, - X86_SSE_PUNPCKHDQ = 0x6A, - X86_SSE_PUNPCKHQDQ = 0x6D, - - X86_SSE_PACKSSWB = 0x63, - X86_SSE_PACKSSDW = 0x6B, - - X86_SSE_PACKUSWB = 0x67, - X86_SSE_PACKUSDW = 0x2B,/*sse41*/ - - X86_SSE_PADDUSB = 0xDC, - X86_SSE_PADDUSW = 0xDD, - X86_SSE_PSUBUSB = 0xD8, - X86_SSE_PSUBUSW = 0xD9, - - X86_SSE_PADDSB = 0xEC, - X86_SSE_PADDSW = 0xED, - X86_SSE_PSUBSB = 0xE8, - X86_SSE_PSUBSW = 0xE9, - - X86_SSE_PMULLW = 0xD5, - X86_SSE_PMULLD = 0x40,/*sse41*/ - X86_SSE_PMULHUW = 0xE4, - X86_SSE_PMULHW = 0xE5, - X86_SSE_PMULUDQ = 0xF4, - - X86_SSE_PMOVMSKB = 0xD7, - - X86_SSE_PSHIFTW = 0x71, - X86_SSE_PSHIFTD = 0x72, - X86_SSE_PSHIFTQ = 0x73, - X86_SSE_SHR = 2, - X86_SSE_SAR = 4, - X86_SSE_SHL = 6, - - X86_SSE_PSRLW_REG = 0xD1, - X86_SSE_PSRAW_REG = 0xE1, - X86_SSE_PSLLW_REG = 0xF1, - - X86_SSE_PSRLD_REG = 0xD2, - X86_SSE_PSRAD_REG = 0xE2, - X86_SSE_PSLLD_REG = 0xF2, - - X86_SSE_PSRLQ_REG = 0xD3, - X86_SSE_PSLLQ_REG = 0xF3, - - X86_SSE_PREFETCH = 0x18, - X86_SSE_MOVNTPS = 0x2B, - X86_SSE_MOVHPD_REG_MEMBASE = 0x16, - X86_SSE_MOVHPD_MEMBASE_REG = 0x17, - - X86_SSE_MOVSD_REG_MEMBASE = 0x10, - X86_SSE_MOVSD_MEMBASE_REG = 0x11, - - X86_SSE_PINSRB = 0x20,/*sse41*/ - X86_SSE_PINSRW = 0xC4, - X86_SSE_PINSRD = 0x22,/*sse41*/ - - X86_SSE_PEXTRB = 0x14,/*sse41*/ - X86_SSE_PEXTRW = 0xC5, - X86_SSE_PEXTRD = 0x16,/*sse41*/ - - X86_SSE_SHUFP = 0xC6, - - X86_SSE_CVTDQ2PD = 0xE6, - X86_SSE_CVTDQ2PS = 0x5B, - X86_SSE_CVTPD2DQ = 0xE6, - X86_SSE_CVTPD2PS = 0x5A, - X86_SSE_CVTPS2DQ = 0x5B, - X86_SSE_CVTPS2PD = 0x5A, - X86_SSE_CVTTPD2DQ = 0xE6, - X86_SSE_CVTTPS2DQ = 0x5B, -} X86_SSE_Opcode; - - -/* minimal SSE* support */ -#define x86_movsd_reg_membase(inst,dreg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xf2; \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x10; \ - x86_membase_emit ((inst), (dreg), (basereg), (disp)); \ - } while (0) - -#define x86_cvttsd2si(inst,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0xf2; \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x2c; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#define x86_sse_alu_reg_reg(inst,opc,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0F; \ - *(inst)++ = (unsigned char)(opc); \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#define x86_sse_alu_reg_membase(inst,opc,sreg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)(opc); \ - x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ - } while (0) - -#define x86_sse_alu_membase_reg(inst,opc,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0F; \ - *(inst)++ = (unsigned char)(opc); \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_sse_alu_reg_reg_imm8(inst,opc,dreg,reg, imm8) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0x0F; \ - *(inst)++ = (unsigned char)(opc); \ - x86_reg_emit ((inst), (dreg), (reg)); \ - *(inst)++ = (unsigned char)(imm8); \ - } while (0) - -#define x86_sse_alu_pd_reg_reg_imm8(inst,opc,dreg,reg, imm8) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0x66; \ - x86_sse_alu_reg_reg_imm8 ((inst), (opc), (dreg), (reg), (imm8)); \ - } while (0) - -#define x86_sse_alu_pd_reg_reg(inst,opc,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0x66; \ - x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ - } while (0) - -#define x86_sse_alu_pd_membase_reg(inst,opc,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x66; \ - x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ - } while (0) - -#define x86_sse_alu_pd_reg_membase(inst,opc,dreg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x66; \ - x86_sse_alu_reg_membase ((inst), (opc), (dreg),(basereg), (disp)); \ - } while (0) - -#define x86_sse_alu_pd_reg_reg_imm(inst,opc,dreg,reg,imm) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - x86_sse_alu_pd_reg_reg ((inst), (opc), (dreg), (reg)); \ - *(inst)++ = (unsigned char)(imm); \ - } while (0) - -#define x86_sse_alu_pd_reg_membase_imm(inst,opc,dreg,basereg,disp,imm) \ - do { \ - x86_codegen_pre(&(inst), 4 + kMaxMembaseEmitPadding); \ - x86_sse_alu_pd_reg_membase ((inst), (opc), (dreg),(basereg), (disp)); \ - *(inst)++ = (unsigned char)(imm); \ - } while (0) - - -#define x86_sse_alu_ps_reg_reg(inst,opc,dreg,reg) \ - do { \ - x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ - } while (0) - -#define x86_sse_alu_ps_reg_reg_imm(inst,opc,dreg,reg, imm) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ - *(inst)++ = (unsigned char)imm; \ - } while (0) - - -#define x86_sse_alu_sd_reg_reg(inst,opc,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0xF2; \ - x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ - } while (0) - -#define x86_sse_alu_sd_membase_reg(inst,opc,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xF2; \ - x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ - } while (0) - - -#define x86_sse_alu_ss_reg_reg(inst,opc,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0xF3; \ - x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ - } while (0) - -#define x86_sse_alu_ss_membase_reg(inst,opc,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xF3; \ - x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ - } while (0) - - - -#define x86_sse_alu_sse41_reg_reg(inst,opc,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0x66; \ - *(inst)++ = (unsigned char)0x0F; \ - *(inst)++ = (unsigned char)0x38; \ - *(inst)++ = (unsigned char)(opc); \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#define x86_movups_reg_membase(inst,sreg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x10; \ - x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ - } while (0) - -#define x86_movups_membase_reg(inst,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x11; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_movaps_reg_membase(inst,sreg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x28; \ - x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ - } while (0) - -#define x86_movaps_membase_reg(inst,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x29; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_movaps_reg_reg(inst,dreg,sreg) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x28; \ - x86_reg_emit ((inst), (dreg), (sreg)); \ - } while (0) - - -#define x86_movd_reg_xreg(inst,dreg,sreg) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0x66; \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x7e; \ - x86_reg_emit ((inst), (sreg), (dreg)); \ - } while (0) - -#define x86_movd_xreg_reg(inst,dreg,sreg) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0x66; \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x6e; \ - x86_reg_emit ((inst), (dreg), (sreg)); \ - } while (0) - -#define x86_movd_xreg_membase(inst,sreg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x66; \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x6e; \ - x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ - } while (0) - -#define x86_pshufw_reg_reg(inst,dreg,sreg,mask,high_words) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)(high_words) ? 0xF3 : 0xF2; \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x70; \ - x86_reg_emit ((inst), (dreg), (sreg)); \ - *(inst)++ = (unsigned char)mask; \ - } while (0) - -#define x86_sse_shift_reg_imm(inst,opc,mode, dreg,imm) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - x86_sse_alu_pd_reg_reg (inst, opc, mode, dreg); \ - x86_imm_emit8 ((inst), (imm)); \ - } while (0) - -#define x86_sse_shift_reg_reg(inst,opc,dreg,sreg) \ - do { \ - x86_sse_alu_pd_reg_reg (inst, opc, dreg, sreg); \ - } while (0) - - - -#endif // X86_H - -- cgit v1.2.3 From 27f94974780b907373517d62a0954132b902af88 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Thu, 7 Jun 2012 16:21:21 +0200 Subject: Add dummy functions for the assignment operators. --- qmljs_runtime.cpp | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ qmljs_runtime.h | 36 ++++++++++++++++++ 2 files changed, 146 insertions(+) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 0680955f9c..88e62eed1e 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -489,6 +489,116 @@ void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *rig } } +void __qmljs_inplace_bit_and_element(Context *ctx, Value *base, Value *index, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_bit_or_element(Context *ctx, Value *base, Value *index, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_add_element(Context *ctx, Value *base, Value *index, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_sub_element(Context *ctx, Value *base, Value *index, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_mul_element(Context *ctx, Value *base, Value *index, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_div_element(Context *ctx, Value *base, Value *index, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_mod_element(Context *ctx, Value *base, Value *index, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_shl_element(Context *ctx, Value *base, Value *index, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_shr_element(Context *ctx, Value *base, Value *index, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_ushr_element(Context *ctx, Value *base, Value *index, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_bit_and_member(Context *ctx, Value *base, String *name, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_bit_or_member(Context *ctx, Value *base, String *name, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_bit_xor_member(Context *ctx, Value *base, String *name, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_add_member(Context *ctx, Value *base, String *name, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_sub_member(Context *ctx, Value *base, String *name, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_mul_member(Context *ctx, Value *base, String *name, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_div_member(Context *ctx, Value *base, String *name, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_mod_member(Context *ctx, Value *base, String *name, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_shl_member(Context *ctx, Value *base, String *name, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_shr_member(Context *ctx, Value *base, String *name, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_ushr_member(Context *ctx, Value *base, String *name, double value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + String *__qmljs_string_from_utf8(Context *ctx, const char *s) { return ctx->engine->newString(QString::fromUtf8(s)); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index d6ab5e20cb..f2c5414790 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -196,6 +196,42 @@ void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *ri void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_inplace_bit_and(Context *ctx, Value *result, double value); +void __qmljs_inplace_bit_or(Context *ctx, Value *result, double value); +void __qmljs_inplace_bit_xor(Context *ctx, Value *result, double value); +void __qmljs_inplace_add(Context *ctx, Value *result, double value); +void __qmljs_inplace_sub(Context *ctx, Value *result, double value); +void __qmljs_inplace_mul(Context *ctx, Value *result, double value); +void __qmljs_inplace_div(Context *ctx, Value *result, double value); +void __qmljs_inplace_mod(Context *ctx, Value *result, double value); +void __qmljs_inplace_shl(Context *ctx, Value *result, double value); +void __qmljs_inplace_shr(Context *ctx, Value *result, double value); +void __qmljs_inplace_ushr(Context *ctx, Value *result, double value); + +void __qmljs_inplace_bit_and_element(Context *ctx, Value *base, Value *index, double value); +void __qmljs_inplace_bit_or_element(Context *ctx, Value *base, Value *index, double value); +void __qmljs_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, double value); +void __qmljs_inplace_add_element(Context *ctx, Value *base, Value *index, double value); +void __qmljs_inplace_sub_element(Context *ctx, Value *base, Value *index, double value); +void __qmljs_inplace_mul_element(Context *ctx, Value *base, Value *index, double value); +void __qmljs_inplace_div_element(Context *ctx, Value *base, Value *index, double value); +void __qmljs_inplace_mod_element(Context *ctx, Value *base, Value *index, double value); +void __qmljs_inplace_shl_element(Context *ctx, Value *base, Value *index, double value); +void __qmljs_inplace_shr_element(Context *ctx, Value *base, Value *index, double value); +void __qmljs_inplace_ushr_element(Context *ctx, Value *base, Value *index, double value); + +void __qmljs_inplace_bit_and_member(Context *ctx, Value *base, String *name, double value); +void __qmljs_inplace_bit_or_member(Context *ctx, Value *base, String *name, double value); +void __qmljs_inplace_bit_xor_member(Context *ctx, Value *base, String *name, double value); +void __qmljs_inplace_add_member(Context *ctx, Value *base, String *name, double value); +void __qmljs_inplace_sub_member(Context *ctx, Value *base, String *name, double value); +void __qmljs_inplace_mul_member(Context *ctx, Value *base, String *name, double value); +void __qmljs_inplace_div_member(Context *ctx, Value *base, String *name, double value); +void __qmljs_inplace_mod_member(Context *ctx, Value *base, String *name, double value); +void __qmljs_inplace_shl_member(Context *ctx, Value *base, String *name, double value); +void __qmljs_inplace_shr_member(Context *ctx, Value *base, String *name, double value); +void __qmljs_inplace_ushr_member(Context *ctx, Value *base, String *name, double value); + bool __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right); bool __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right); bool __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right); -- cgit v1.2.3 From 6b79f27580114c92051b8c9571a2af2955be79c8 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 8 Jun 2012 11:35:16 +0100 Subject: Remove dead code --- main.cpp | 5 - qv4isel.cpp | 2 - qv4isel_llvm_p.h | 1 - qv4isel_p.h | 373 ------------------------------------------------------- v4.pro | 2 - 5 files changed, 383 deletions(-) delete mode 100644 qv4isel.cpp delete mode 100644 qv4isel_p.h diff --git a/main.cpp b/main.cpp index 389e57deec..06e9f2d903 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,6 @@ #include "qmljs_objects.h" #include "qv4codegen_p.h" -#include "qv4isel_p.h" #include "qv4isel_x86_64_p.h" #include "qv4syntaxchecker_p.h" #include "qv4ecmaobjects_p.h" @@ -212,12 +211,8 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS if (! protect(code, codeSize)) Q_UNREACHABLE(); - VM::Object *globalObject = vm->globalObject.objectValue; VM::Context *ctx = vm->rootContext; - globalObject->setProperty(ctx, vm->identifier(QStringLiteral("print")), - VM::Value::fromObject(new builtins::Print(ctx))); - ctx->varCount = globalCode->locals.size(); if (ctx->varCount) { ctx->locals = new VM::Value[ctx->varCount]; diff --git a/qv4isel.cpp b/qv4isel.cpp deleted file mode 100644 index 1e833bb490..0000000000 --- a/qv4isel.cpp +++ /dev/null @@ -1,2 +0,0 @@ - -#include "qv4isel_p.h" diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 5f67f2d66a..259cf46449 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -1,7 +1,6 @@ #ifndef QV4ISEL_LLVM_P_H #define QV4ISEL_LLVM_P_H -#include "qv4isel_p.h" #include "qv4ir_p.h" #include #include diff --git a/qv4isel_p.h b/qv4isel_p.h deleted file mode 100644 index b12f71646a..0000000000 --- a/qv4isel_p.h +++ /dev/null @@ -1,373 +0,0 @@ -#ifndef QV4ISEL_H -#define QV4ISEL_H - -#include "qv4ir_p.h" - -namespace QQmlJS { - -class BaseInstructionSelection: protected IR::StmtVisitor -{ -protected: - struct DispatchExp: IR::ExprVisitor { - BaseInstructionSelection *_isel; - IR::Exp *_stmt; - - DispatchExp(BaseInstructionSelection *isel) - : _isel(isel), _stmt(0) {} - - void operator()(IR::Exp *s) - { - qSwap(_stmt, s); - _stmt->expr->accept(this); - qSwap(_stmt, s); - } - - virtual void visitConst(IR::Const *) { _isel->genExpConst(_stmt); } - virtual void visitString(IR::String *) { _isel->genExpString(_stmt); } - virtual void visitName(IR::Name *) { _isel->genExpName(_stmt); } - virtual void visitTemp(IR::Temp *) { _isel->genExpTemp(_stmt); } - virtual void visitClosure(IR::Closure *) { _isel->genExpClosure(_stmt); } - virtual void visitUnop(IR::Unop *) { _isel->genExpUnop(_stmt); } - virtual void visitBinop(IR::Binop *) { _isel->genExpBinop(_stmt); } - virtual void visitCall(IR::Call *) { _isel->genExpCall(_stmt); } - virtual void visitNew(IR::New *) { _isel->genExpNew(_stmt); } - virtual void visitSubscript(IR::Subscript *) { _isel->genExpSubscript(_stmt); } - virtual void visitMember(IR::Member *) { _isel->genExpMember(_stmt); } - }; - - struct DispatchRet: IR::ExprVisitor { - BaseInstructionSelection *_isel; - IR::Ret *_stmt; - - DispatchRet(BaseInstructionSelection *isel) - : _isel(isel), _stmt(0) {} - - void operator()(IR::Ret *s) - { - qSwap(_stmt, s); - _stmt->expr->accept(this); - qSwap(_stmt, s); - } - - virtual void visitConst(IR::Const *) { _isel->genRetConst(_stmt); } - virtual void visitString(IR::String *) { _isel->genRetString(_stmt); } - virtual void visitName(IR::Name *) { _isel->genRetName(_stmt); } - virtual void visitTemp(IR::Temp *) { _isel->genRetTemp(_stmt); } - virtual void visitClosure(IR::Closure *) { _isel->genRetClosure(_stmt); } - virtual void visitUnop(IR::Unop *) { _isel->genRetUnop(_stmt); } - virtual void visitBinop(IR::Binop *) { _isel->genRetBinop(_stmt); } - virtual void visitCall(IR::Call *) { _isel->genRetCall(_stmt); } - virtual void visitNew(IR::New *) { _isel->genRetNew(_stmt); } - virtual void visitSubscript(IR::Subscript *) { _isel->genRetSubscript(_stmt); } - virtual void visitMember(IR::Member *) { _isel->genRetMember(_stmt); } - }; - - struct DispatchMove: IR::ExprVisitor { - BaseInstructionSelection *_isel; - IR::Move *_stmt; - - DispatchMove(BaseInstructionSelection *isel) - : _isel(isel), _stmt(0) {} - - void operator()(IR::Move *stmt) - { - qSwap(_stmt, stmt); - _stmt->target->accept(this); - qSwap(_stmt, stmt); - } - - virtual void visitConst(IR::Const *) { Q_UNREACHABLE(); } - virtual void visitString(IR::String *) { Q_UNREACHABLE(); } - virtual void visitName(IR::Name *) { _isel->moveName(_stmt); } - virtual void visitTemp(IR::Temp *) { _isel->moveTemp(_stmt); } - virtual void visitClosure(IR::Closure *) { Q_UNREACHABLE(); } - virtual void visitUnop(IR::Unop *) { Q_UNREACHABLE(); } - virtual void visitBinop(IR::Binop *) { Q_UNREACHABLE(); } - virtual void visitCall(IR::Call *) { Q_UNREACHABLE(); } - virtual void visitNew(IR::New *) { Q_UNREACHABLE(); } - virtual void visitSubscript(IR::Subscript *) { _isel->moveSubscript(_stmt); } - virtual void visitMember(IR::Member *) { _isel->moveMember(_stmt); } - }; - - struct MoveTemp: IR::ExprVisitor { - BaseInstructionSelection *_isel; - IR::Move *_stmt; - - MoveTemp(BaseInstructionSelection *isel) - : _isel(isel), _stmt(0) {} - - void operator()(IR::Move *stmt) - { - qSwap(_stmt, stmt); - _stmt->source->accept(this); - qSwap(_stmt, stmt); - } - - virtual void visitConst(IR::Const *) { _isel->genMoveTempConst(_stmt); } - virtual void visitString(IR::String *) { _isel->genMoveTempString(_stmt); } - virtual void visitName(IR::Name *) { _isel->genMoveTempName(_stmt); } - virtual void visitTemp(IR::Temp *) { _isel->genMoveTempTemp(_stmt); } - virtual void visitClosure(IR::Closure *) { _isel->genMoveTempClosure(_stmt); } - virtual void visitUnop(IR::Unop *) { _isel->genMoveTempUnop(_stmt); } - virtual void visitBinop(IR::Binop *) { _isel->genMoveTempBinop(_stmt); } - virtual void visitCall(IR::Call *) { _isel->genMoveTempCall(_stmt); } - virtual void visitNew(IR::New *) { _isel->genMoveTempNew(_stmt); } - virtual void visitSubscript(IR::Subscript *) { _isel->genMoveTempSubscript(_stmt); } - virtual void visitMember(IR::Member *) { _isel->genMoveTempMember(_stmt); } - }; - - struct MoveName: IR::ExprVisitor { - BaseInstructionSelection *_isel; - IR::Move *_stmt; - - MoveName(BaseInstructionSelection *isel) - : _isel(isel), _stmt(0) {} - - void operator()(IR::Move *stmt) - { - qSwap(_stmt, stmt); - _stmt->source->accept(this); - qSwap(_stmt, stmt); - } - - virtual void visitConst(IR::Const *) { _isel->genMoveNameConst(_stmt); } - virtual void visitString(IR::String *) { _isel->genMoveNameString(_stmt); } - virtual void visitName(IR::Name *) { _isel->genMoveNameName(_stmt); } - virtual void visitTemp(IR::Temp *) { _isel->genMoveNameTemp(_stmt); } - virtual void visitClosure(IR::Closure *) { _isel->genMoveNameClosure(_stmt); } - virtual void visitUnop(IR::Unop *) { _isel->genMoveNameUnop(_stmt); } - virtual void visitBinop(IR::Binop *) { _isel->genMoveNameBinop(_stmt); } - virtual void visitCall(IR::Call *) { _isel->genMoveNameCall(_stmt); } - virtual void visitNew(IR::New *) { _isel->genMoveNameNew(_stmt); } - virtual void visitSubscript(IR::Subscript *) { _isel->genMoveNameSubscript(_stmt); } - virtual void visitMember(IR::Member *) { _isel->genMoveNameMember(_stmt); } - }; - - struct MoveMember: IR::ExprVisitor { - BaseInstructionSelection *_isel; - IR::Move *_stmt; - - MoveMember(BaseInstructionSelection *isel) - : _isel(isel), _stmt(0) {} - - void operator()(IR::Move *stmt) - { - qSwap(_stmt, stmt); - _stmt->source->accept(this); - qSwap(_stmt, stmt); - } - - virtual void visitConst(IR::Const *) { _isel->genMoveMemberConst(_stmt); } - virtual void visitString(IR::String *) { _isel->genMoveMemberString(_stmt); } - virtual void visitName(IR::Name *) { _isel->genMoveMemberName(_stmt); } - virtual void visitTemp(IR::Temp *) { _isel->genMoveMemberTemp(_stmt); } - virtual void visitClosure(IR::Closure *) { _isel->genMoveMemberClosure(_stmt); } - virtual void visitUnop(IR::Unop *) { _isel->genMoveMemberUnop(_stmt); } - virtual void visitBinop(IR::Binop *) { _isel->genMoveMemberBinop(_stmt); } - virtual void visitCall(IR::Call *) { _isel->genMoveMemberCall(_stmt); } - virtual void visitNew(IR::New *) { _isel->genMoveMemberNew(_stmt); } - virtual void visitSubscript(IR::Subscript *) { _isel->genMoveMemberSubscript(_stmt); } - virtual void visitMember(IR::Member *) { _isel->genMoveMemberMember(_stmt); } - }; - - struct MoveSubscript: IR::ExprVisitor { - BaseInstructionSelection *_isel; - IR::Move *_stmt; - - MoveSubscript(BaseInstructionSelection *isel) - : _isel(isel), _stmt(0) {} - - void operator()(IR::Move *stmt) - { - qSwap(_stmt, stmt); - _stmt->source->accept(this); - qSwap(_stmt, stmt); - } - - virtual void visitConst(IR::Const *) { _isel->genMoveSubscriptConst(_stmt); } - virtual void visitString(IR::String *) { _isel->genMoveSubscriptString(_stmt); } - virtual void visitName(IR::Name *) { _isel->genMoveSubscriptName(_stmt); } - virtual void visitTemp(IR::Temp *) { _isel->genMoveSubscriptTemp(_stmt); } - virtual void visitClosure(IR::Closure *) { _isel->genMoveSubscriptClosure(_stmt); } - virtual void visitUnop(IR::Unop *) { _isel->genMoveSubscriptUnop(_stmt); } - virtual void visitBinop(IR::Binop *) { _isel->genMoveSubscriptBinop(_stmt); } - virtual void visitCall(IR::Call *) { _isel->genMoveSubscriptCall(_stmt); } - virtual void visitNew(IR::New *) { _isel->genMoveSubscriptNew(_stmt); } - virtual void visitSubscript(IR::Subscript *) { _isel->genMoveSubscriptSubscript(_stmt); } - virtual void visitMember(IR::Member *) { _isel->genMoveSubscriptMember(_stmt); } - }; - - struct DispatchCJump: IR::ExprVisitor { - BaseInstructionSelection *_isel; - IR::CJump *_stmt; - - DispatchCJump(BaseInstructionSelection *isel) - : _isel(isel), _stmt(0) {} - - void operator()(IR::CJump *s) - { - qSwap(_stmt, s); - _stmt->cond->accept(this); - qSwap(_stmt, s); - } - - virtual void visitConst(IR::Const *) { _isel->genCJumpConst(_stmt); } - virtual void visitString(IR::String *) { _isel->genCJumpString(_stmt); } - virtual void visitName(IR::Name *) { _isel->genCJumpName(_stmt); } - virtual void visitTemp(IR::Temp *) { _isel->genCJumpTemp(_stmt); } - virtual void visitClosure(IR::Closure *) { _isel->genCJumpClosure(_stmt); } - virtual void visitUnop(IR::Unop *) { _isel->genCJumpUnop(_stmt); } - virtual void visitBinop(IR::Binop *) { _isel->genCJumpBinop(_stmt); } - virtual void visitCall(IR::Call *) { _isel->genCJumpCall(_stmt); } - virtual void visitNew(IR::New *) { _isel->genCJumpNew(_stmt); } - virtual void visitSubscript(IR::Subscript *) { _isel->genCJumpSubscript(_stmt); } - virtual void visitMember(IR::Member *) { _isel->genCJumpMember(_stmt); } - }; - - virtual void visitExp(IR::Exp *s) - { - dispatchExp(s); - } - - virtual void visitEnter(IR::Enter *) - { - Q_UNREACHABLE(); - } - - virtual void visitLeave(IR::Leave *) - { - Q_UNREACHABLE(); - } - - virtual void visitMove(IR::Move *s) - { - dispatchMove(s); - } - - virtual void visitJump(IR::Jump *s) - { - genJump(s); - } - - virtual void visitCJump(IR::CJump *s) - { - dispatchCJump(s); - } - - virtual void visitRet(IR::Ret *s) - { - dispatchRet(s); - } - - DispatchExp dispatchExp; - DispatchRet dispatchRet; - DispatchCJump dispatchCJump; - DispatchMove dispatchMove; - MoveTemp moveTemp; - MoveName moveName; - MoveMember moveMember; - MoveSubscript moveSubscript; - -public: - BaseInstructionSelection() - : dispatchExp(this) - , dispatchRet(this) - , dispatchCJump(this) - , dispatchMove(this) - , moveTemp(this) - , moveName(this) - , moveMember(this) - , moveSubscript(this) {} - - void statement(IR::Stmt *s) { s->accept(this); } - - virtual void genExpConst(IR::Exp *) { Q_UNIMPLEMENTED(); } - virtual void genExpString(IR::Exp *) { Q_UNIMPLEMENTED(); } - virtual void genExpName(IR::Exp *) { Q_UNIMPLEMENTED(); } - virtual void genExpTemp(IR::Exp *) { Q_UNIMPLEMENTED(); } - virtual void genExpClosure(IR::Exp *) { Q_UNIMPLEMENTED(); } - virtual void genExpUnop(IR::Exp *) { Q_UNIMPLEMENTED(); } - virtual void genExpBinop(IR::Exp *) { Q_UNIMPLEMENTED(); } - virtual void genExpCall(IR::Exp *) { Q_UNIMPLEMENTED(); } - virtual void genExpNew(IR::Exp *) { Q_UNIMPLEMENTED(); } - virtual void genExpSubscript(IR::Exp *) { Q_UNIMPLEMENTED(); } - virtual void genExpMember(IR::Exp *) { Q_UNIMPLEMENTED(); } - - virtual void genMoveTempConst(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveTempString(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveTempName(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveTempTemp(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveTempClosure(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveTempUnop(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveTempBinop(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveTempCall(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveTempNew(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveTempSubscript(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveTempMember(IR::Move *) { Q_UNIMPLEMENTED(); } - - virtual void genMoveNameConst(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveNameString(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveNameName(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveNameTemp(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveNameClosure(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveNameUnop(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveNameBinop(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveNameCall(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveNameNew(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveNameSubscript(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveNameMember(IR::Move *) { Q_UNIMPLEMENTED(); } - - virtual void genMoveMemberConst(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveMemberString(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveMemberName(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveMemberTemp(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveMemberClosure(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveMemberUnop(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveMemberBinop(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveMemberCall(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveMemberNew(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveMemberSubscript(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveMemberMember(IR::Move *) { Q_UNIMPLEMENTED(); } - - virtual void genMoveSubscriptConst(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveSubscriptString(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveSubscriptName(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveSubscriptTemp(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveSubscriptClosure(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveSubscriptUnop(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveSubscriptBinop(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveSubscriptCall(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveSubscriptNew(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveSubscriptSubscript(IR::Move *) { Q_UNIMPLEMENTED(); } - virtual void genMoveSubscriptMember(IR::Move *) { Q_UNIMPLEMENTED(); } - - virtual void genJump(IR::Jump *) { Q_UNIMPLEMENTED(); } - - virtual void genCJumpConst(IR::CJump *) { Q_UNIMPLEMENTED(); } - virtual void genCJumpString(IR::CJump *) { Q_UNIMPLEMENTED(); } - virtual void genCJumpName(IR::CJump *) { Q_UNIMPLEMENTED(); } - virtual void genCJumpTemp(IR::CJump *) { Q_UNIMPLEMENTED(); } - virtual void genCJumpClosure(IR::CJump *) { Q_UNIMPLEMENTED(); } - virtual void genCJumpUnop(IR::CJump *) { Q_UNIMPLEMENTED(); } - virtual void genCJumpBinop(IR::CJump *) { Q_UNIMPLEMENTED(); } - virtual void genCJumpCall(IR::CJump *) { Q_UNIMPLEMENTED(); } - virtual void genCJumpNew(IR::CJump *) { Q_UNIMPLEMENTED(); } - virtual void genCJumpSubscript(IR::CJump *) { Q_UNIMPLEMENTED(); } - virtual void genCJumpMember(IR::CJump *) { Q_UNIMPLEMENTED(); } - - virtual void genRetConst(IR::Ret *) { Q_UNIMPLEMENTED(); } - virtual void genRetString(IR::Ret *) { Q_UNIMPLEMENTED(); } - virtual void genRetName(IR::Ret *) { Q_UNIMPLEMENTED(); } - virtual void genRetTemp(IR::Ret *) { Q_UNIMPLEMENTED(); } - virtual void genRetClosure(IR::Ret *) { Q_UNIMPLEMENTED(); } - virtual void genRetUnop(IR::Ret *) { Q_UNIMPLEMENTED(); } - virtual void genRetBinop(IR::Ret *) { Q_UNIMPLEMENTED(); } - virtual void genRetCall(IR::Ret *) { Q_UNIMPLEMENTED(); } - virtual void genRetNew(IR::Ret *) { Q_UNIMPLEMENTED(); } - virtual void genRetSubscript(IR::Ret *) { Q_UNIMPLEMENTED(); } - virtual void genRetMember(IR::Ret *) { Q_UNIMPLEMENTED(); } -}; - -} // end of namespace QQmlJS - -#endif // QV4ISEL_H diff --git a/v4.pro b/v4.pro index f55d2520a0..e22ea44572 100644 --- a/v4.pro +++ b/v4.pro @@ -14,7 +14,6 @@ SOURCES += main.cpp \ qv4ir.cpp \ qmljs_runtime.cpp \ qmljs_objects.cpp \ - qv4isel.cpp \ qv4syntaxchecker.cpp \ qv4ecmaobjects.cpp \ qv4array.cpp \ @@ -26,7 +25,6 @@ HEADERS += \ qv4ir_p.h \ qmljs_runtime.h \ qmljs_objects.h \ - qv4isel_p.h \ qv4syntaxchecker_p.h \ qv4ecmaobjects_p.h \ qv4array_p.h \ -- cgit v1.2.3 From bd545574f2fd3b644d61d35f349f736f00dadb02 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 8 Jun 2012 13:29:21 +0200 Subject: Ensure that basic blocks are terminated. Also make sure that the special BasicBlock `exitBlock' is always the last block of the function. --- qv4codegen.cpp | 19 +++++++++---------- qv4ir.cpp | 5 +++-- qv4ir_p.h | 9 +++++++-- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index b67c52f615..3e0a9e47ba 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1282,10 +1282,9 @@ bool Codegen::visit(FunctionDeclaration *ast) void Codegen::linearize(IR::Function *function) { - IR::BasicBlock *entryBlock = function->basicBlocks.at(0); - IR::BasicBlock *exitBlock = function->basicBlocks.at(1); - - Q_UNUSED(entryBlock); + IR::BasicBlock *exitBlock = function->basicBlocks.last(); + assert(exitBlock->isTerminated()); + assert(exitBlock->terminator()->asRet()); QSet V; V.insert(exitBlock); @@ -1294,11 +1293,9 @@ void Codegen::linearize(IR::Function *function) for (int i = 0; i < function->basicBlocks.size(); ++i) { IR::BasicBlock *block = function->basicBlocks.at(i); - if (block->statements.isEmpty()) { - if ((i + 1) < function->basicBlocks.size()) { - IR::BasicBlock *next = function->basicBlocks.at(i + 1); - block->JUMP(next); - } + if (!block->isTerminated() && (i + 1) < function->basicBlocks.size()) { + IR::BasicBlock *next = function->basicBlocks.at(i + 1); + block->JUMP(next); } } @@ -1440,7 +1437,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, enterEnvironment(ast); IR::Function *function = _module->newFunction(name); IR::BasicBlock *entryBlock = function->newBasicBlock(); - IR::BasicBlock *exitBlock = function->newBasicBlock(); + IR::BasicBlock *exitBlock = function->newBasicBlock(IR::Function::DontInsertBlock); IR::BasicBlock *throwBlock = function->newBasicBlock(); IR::BasicBlock *handlersBlock = function->newBasicBlock(); function->hasDirectEval = _env->hasDirectEval; @@ -1478,6 +1475,8 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, sourceElements(body); + _function->insertBasicBlock(_exitBlock); + if (! _block->isTerminated()) _block->JUMP(_exitBlock); diff --git a/qv4ir.cpp b/qv4ir.cpp index 5f851349cb..f27d318f4c 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -493,9 +493,10 @@ const QString *Function::newString(const QString &text) return &*strings.insert(text); } -BasicBlock *Function::newBasicBlock() +BasicBlock *Function::newBasicBlock(BasicBlockInsertMode mode) { - return i(new BasicBlock(this)); + BasicBlock *block = new BasicBlock(this); + return mode == InsertBlock ? insertBasicBlock(block) : block; } void Function::dump(QTextStream &out, Stmt::Mode mode) diff --git a/qv4ir_p.h b/qv4ir_p.h index 95cc28d8d9..f6467c98d3 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -624,13 +624,18 @@ struct Function { ~Function(); - BasicBlock *newBasicBlock(); + enum BasicBlockInsertMode { + InsertBlock, + DontInsertBlock + }; + + BasicBlock *newBasicBlock(BasicBlockInsertMode mode = InsertBlock); const QString *newString(const QString &text); void RECEIVE(const QString &name) { formals.append(newString(name)); } void LOCAL(const QString &name) { locals.append(newString(name)); } - inline BasicBlock *i(BasicBlock *block) { basicBlocks.append(block); return block; } + inline BasicBlock *insertBasicBlock(BasicBlock *block) { basicBlocks.append(block); return block; } void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); -- cgit v1.2.3 From a36259350dcfd0d9b29dc1a3a69d29f894783135 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 8 Jun 2012 15:44:43 +0200 Subject: Remove dead code --- qv4codegen.cpp | 100 --------------------------------------------------------- qv4codegen_p.h | 20 ------------ 2 files changed, 120 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 3e0a9e47ba..a7a872189f 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -503,56 +503,6 @@ Codegen::UiMember Codegen::uiObjectMember(UiObjectMember *ast) return m; } -void Codegen::argumentList(ArgumentList *) -{ - assert(!"not implemented"); -} - -void Codegen::caseBlock(CaseBlock *) -{ - assert(!"not implemented"); -} - -void Codegen::caseClause(CaseClause *) -{ - assert(!"not implemented"); -} - -void Codegen::caseClauses(CaseClauses *) -{ - assert(!"not implemented"); -} - -void Codegen::catchNode(Catch *) -{ - assert(!"not implemented"); -} - -void Codegen::defaultClause(DefaultClause *) -{ - assert(!"not implemented"); -} - -void Codegen::elementList(ElementList *) -{ - assert(!"not implemented"); -} - -void Codegen::elision(Elision *) -{ - assert(!"not implemented"); -} - -void Codegen::finallyNode(Finally *) -{ - assert(!"not implemented"); -} - -void Codegen::formalParameterList(FormalParameterList *) -{ - assert(!"not implemented"); -} - void Codegen::functionBody(FunctionBody *ast) { if (ast) @@ -566,11 +516,6 @@ void Codegen::program(Program *ast) } } -void Codegen::propertyNameAndValueList(PropertyNameAndValueList *) -{ - assert(!"not implemented"); -} - void Codegen::sourceElements(SourceElements *ast) { for (SourceElements *it = ast; it; it = it->next) { @@ -578,51 +523,6 @@ void Codegen::sourceElements(SourceElements *ast) } } -void Codegen::statementList(StatementList *) -{ - assert(!"not implemented"); -} - -void Codegen::uiArrayMemberList(UiArrayMemberList *) -{ - assert(!"not implemented"); -} - -void Codegen::uiImport(UiImport *) -{ - assert(!"not implemented"); -} - -void Codegen::uiImportList(UiImportList *) -{ - assert(!"not implemented"); -} - -void Codegen::uiObjectInitializer(UiObjectInitializer *) -{ - assert(!"not implemented"); -} - -void Codegen::uiObjectMemberList(UiObjectMemberList *) -{ - assert(!"not implemented"); -} - -void Codegen::uiParameterList(UiParameterList *) -{ - assert(!"not implemented"); -} - -void Codegen::uiProgram(UiProgram *) -{ - assert(!"not implemented"); -} - -void Codegen::uiQualifiedId(UiQualifiedId *) -{ - assert(!"not implemented"); -} - void Codegen::variableDeclaration(VariableDeclaration *ast) { if (ast->expression) { diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 87e967da80..c077d3daf9 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -148,29 +148,9 @@ protected: void accept(AST::Node *node); - void argumentList(AST::ArgumentList *ast); - void caseBlock(AST::CaseBlock *ast); - void caseClause(AST::CaseClause *ast); - void caseClauses(AST::CaseClauses *ast); - void catchNode(AST::Catch *ast); - void defaultClause(AST::DefaultClause *ast); - void elementList(AST::ElementList *ast); - void elision(AST::Elision *ast); - void finallyNode(AST::Finally *ast); - void formalParameterList(AST::FormalParameterList *ast); void functionBody(AST::FunctionBody *ast); void program(AST::Program *ast); - void propertyNameAndValueList(AST::PropertyNameAndValueList *ast); void sourceElements(AST::SourceElements *ast); - void statementList(AST::StatementList *ast); - void uiArrayMemberList(AST::UiArrayMemberList *ast); - void uiImport(AST::UiImport *ast); - void uiImportList(AST::UiImportList *ast); - void uiObjectInitializer(AST::UiObjectInitializer *ast); - void uiObjectMemberList(AST::UiObjectMemberList *ast); - void uiParameterList(AST::UiParameterList *ast); - void uiProgram(AST::UiProgram *ast); - void uiQualifiedId(AST::UiQualifiedId *ast); void variableDeclaration(AST::VariableDeclaration *ast); void variableDeclarationList(AST::VariableDeclarationList *ast); -- cgit v1.2.3 From 75684e677a93447dc7a4358ab0655eb9f1572286 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 8 Jun 2012 16:26:08 +0200 Subject: Add support for labelled statements. --- qv4codegen.cpp | 96 +++++++++++++++++++++++++++++++++++++++++++------------- qv4codegen_p.h | 16 ++++++---- tests/label.1.js | 8 +++++ tests/label.2.js | 2 ++ tests/label.3.js | 6 ++++ 5 files changed, 101 insertions(+), 27 deletions(-) create mode 100644 tests/label.1.js create mode 100644 tests/label.2.js create mode 100644 tests/label.3.js diff --git a/qv4codegen.cpp b/qv4codegen.cpp index a7a872189f..27d0f60d26 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -276,6 +276,8 @@ Codegen::Codegen() , _handlersBlock(0) , _returnAddress(0) , _env(0) + , _loop(0) + , _labelledStatement(0) { } @@ -311,6 +313,20 @@ void Codegen::leaveEnvironment() _env = _env->parent; } +void Codegen::enterLoop(Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock) +{ + _loop = new Loop(node, breakBlock, continueBlock, _loop); + _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement + _labelledStatement = 0; +} + +void Codegen::leaveLoop() +{ + Loop *current = _loop; + _loop = _loop->parent; + delete current; +} + IR::Expr *Codegen::member(IR::Expr *base, const QString *name) { if (base->asTemp() /*|| base->asName()*/) @@ -1454,15 +1470,39 @@ bool Codegen::visit(Block *ast) return false; } -bool Codegen::visit(BreakStatement *) +bool Codegen::visit(BreakStatement *ast) { - _block->JUMP(_loop.breakBlock); + if (ast->label.isEmpty()) + _block->JUMP(_loop->breakBlock); + else { + for (Loop *loop = _loop; _loop; _loop = _loop->parent) { + if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { + _block->JUMP(loop->breakBlock); + return false; + } + } + assert(!"throw syntax error"); + } + return false; } -bool Codegen::visit(ContinueStatement *) +bool Codegen::visit(ContinueStatement *ast) { - _block->JUMP(_loop.continueBlock); + if (ast->label.isEmpty()) + _block->JUMP(_loop->continueBlock); + else { + for (Loop *loop = _loop; _loop; _loop = _loop->parent) { + if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { + if (! loop->continueBlock) + break; + + _block->JUMP(loop->continueBlock); + return false; + } + } + assert(!"throw syntax error"); + } return false; } @@ -1477,8 +1517,7 @@ bool Codegen::visit(DoWhileStatement *ast) IR::BasicBlock *loopbody = _function->newBasicBlock(); IR::BasicBlock *loopend = _function->newBasicBlock(); - Loop loop(loopend, loopbody); - qSwap(_loop, loop); + enterLoop(ast, loopend, loopbody); if (_block->isTerminated()) _block->JUMP(loopbody); @@ -1487,7 +1526,8 @@ bool Codegen::visit(DoWhileStatement *ast) condition(ast->expression, loopbody, loopend); _block = loopend; - qSwap(_loop, loop); + leaveLoop(); + return false; } @@ -1515,8 +1555,7 @@ bool Codegen::visit(ForStatement *ast) IR::BasicBlock *forstep = _function->newBasicBlock(); IR::BasicBlock *forend = _function->newBasicBlock(); - Loop loop(forend, forstep); - qSwap(_loop, loop); + enterLoop(ast, forend, forstep); statement(ast->initialiser); if (! _block->isTerminated()) @@ -1536,7 +1575,7 @@ bool Codegen::visit(ForStatement *ast) _block->JUMP(forcond); _block = forend; - qSwap(_loop, loop); + leaveLoop(); return false; } @@ -1565,9 +1604,26 @@ bool Codegen::visit(IfStatement *ast) return false; } -bool Codegen::visit(LabelledStatement *) +bool Codegen::visit(LabelledStatement *ast) { - assert(!"not implemented"); + _labelledStatement = ast; + + if (AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement)) { + statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. + } else { + IR::BasicBlock *breakBlock = _function->newBasicBlock(); + enterLoop(ast->statement, breakBlock, /*continueBlock*/ 0); + statement(ast->statement); + leaveLoop(); + _block = breakBlock; + } + return false; } @@ -1584,8 +1640,7 @@ bool Codegen::visit(LocalForStatement *ast) IR::BasicBlock *forstep = _function->newBasicBlock(); IR::BasicBlock *forend = _function->newBasicBlock(); - Loop loop(forend, forstep); - qSwap(_loop, loop); + enterLoop(ast, forend, forstep); variableDeclarationList(ast->declarations); if (! _block->isTerminated()) @@ -1605,7 +1660,8 @@ bool Codegen::visit(LocalForStatement *ast) _block->JUMP(forcond); _block = forend; - qSwap(_loop, loop); + leaveLoop(); + return false; } @@ -1630,8 +1686,7 @@ bool Codegen::visit(SwitchStatement *ast) QHash blockMap; - Loop loop(switchend, 0); - qSwap(_loop, loop); + enterLoop(ast, switchend, 0); for (CaseClauses *it = ast->block->clauses; it; it = it->next) { CaseClause *clause = it->clause; @@ -1661,7 +1716,7 @@ bool Codegen::visit(SwitchStatement *ast) statement(it2->statement); } - qSwap(_loop, loop); + leaveLoop(); if (! _block->isTerminated()) _block->JUMP(switchend); @@ -1723,8 +1778,7 @@ bool Codegen::visit(WhileStatement *ast) IR::BasicBlock *whilebody = _function->newBasicBlock(); IR::BasicBlock *whileend = _function->newBasicBlock(); - Loop loop(whileend, whilecond); - qSwap(_loop, loop); + enterLoop(ast, whileend, whilecond); _block->JUMP(whilecond); _block = whilecond; @@ -1736,7 +1790,7 @@ bool Codegen::visit(WhileStatement *ast) _block->JUMP(whilecond); _block = whileend; - qSwap(_loop, loop); + leaveLoop(); return false; } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index c077d3daf9..41f40844e9 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -110,19 +110,22 @@ protected: }; struct Loop { + AST::LabelledStatement *labelledStatement; + AST::Statement *node; IR::BasicBlock *breakBlock; IR::BasicBlock *continueBlock; + Loop *parent; - Loop() - : breakBlock(0), continueBlock(0) {} - - Loop(IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock) - : breakBlock(breakBlock), continueBlock(continueBlock) {} + Loop(AST::Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock, Loop *parent) + : labelledStatement(0), node(node), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {} }; void enterEnvironment(AST::Node *node); void leaveEnvironment(); + void enterLoop(AST::Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock); + void leaveLoop(); + IR::Expr *member(IR::Expr *base, const QString *name); IR::Expr *subscript(IR::Expr *base, IR::Expr *index); IR::Expr *argument(IR::Expr *expr); @@ -258,7 +261,6 @@ private: Result _expr; QString _property; UiMember _uiMember; - Loop _loop; IR::Module *_module; IR::Function *_function; IR::BasicBlock *_block; @@ -267,6 +269,8 @@ private: IR::BasicBlock *_handlersBlock; unsigned _returnAddress; Environment *_env; + Loop *_loop; + AST::LabelledStatement *_labelledStatement; QHash _envMap; class ScanFunctions; diff --git a/tests/label.1.js b/tests/label.1.js new file mode 100644 index 0000000000..79921cb5aa --- /dev/null +++ b/tests/label.1.js @@ -0,0 +1,8 @@ + +L: { + var x = 1 + print(x) + break L + x = 2 + print(x) +} diff --git a/tests/label.2.js b/tests/label.2.js new file mode 100644 index 0000000000..fcb3d3e0e9 --- /dev/null +++ b/tests/label.2.js @@ -0,0 +1,2 @@ + +L: break L diff --git a/tests/label.3.js b/tests/label.3.js new file mode 100644 index 0000000000..9940a33aff --- /dev/null +++ b/tests/label.3.js @@ -0,0 +1,6 @@ + +L: for (var i = 0; i < 10; ++i) { + print(i) + if (i == 5) + continue L +} -- cgit v1.2.3 From 57e5488977ccd843fbd58a5874b6b272101762ce Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 8 Jun 2012 16:33:24 +0200 Subject: Ensure the block created by the labelled statement is terminated --- qv4codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 27d0f60d26..06ebc3a6be 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1620,7 +1620,7 @@ bool Codegen::visit(LabelledStatement *ast) IR::BasicBlock *breakBlock = _function->newBasicBlock(); enterLoop(ast->statement, breakBlock, /*continueBlock*/ 0); statement(ast->statement); - leaveLoop(); + _block->JUMP(breakBlock); _block = breakBlock; } -- cgit v1.2.3 From 89380058198e0b8653dbc9365468f1b6e61bbc9b Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 8 Jun 2012 16:35:30 +0200 Subject: Remove useless calls to BasicBlock::isTerminated() --- qv4codegen.cpp | 48 ++++++++++++++++-------------------------------- 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 06ebc3a6be..13290e8afe 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -783,8 +783,7 @@ bool Codegen::visit(BinaryExpression *ast) _block = iftrue; move(_block->TEMP(r), *expression(ast->right)); - if (! _block->isTerminated()) - _block->JUMP(endif); + _block->JUMP(endif); _expr.code = _block->TEMP(r); _block = endif; @@ -805,8 +804,7 @@ bool Codegen::visit(BinaryExpression *ast) cjump(_block->TEMP(r), endif, iffalse); _block = iffalse; move(_block->TEMP(r), *expression(ast->right)); - if (! _block->isTerminated()) - _block->JUMP(endif); + _block->JUMP(endif); _block = endif; _expr.code = _block->TEMP(r); @@ -1393,11 +1391,9 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, _function->insertBasicBlock(_exitBlock); - if (! _block->isTerminated()) - _block->JUMP(_exitBlock); + _block->JUMP(_exitBlock); - if (! _throwBlock->isTerminated()) - _throwBlock->JUMP(_function->handlersBlock); + _throwBlock->JUMP(_function->handlersBlock); _handlersBlock->MOVE(_handlersBlock->TEMP(_returnAddress), _handlersBlock->CALL(_handlersBlock->NAME(IR::Name::builtin_rethrow, 0, 0), 0)); @@ -1519,8 +1515,7 @@ bool Codegen::visit(DoWhileStatement *ast) enterLoop(ast, loopend, loopbody); - if (_block->isTerminated()) - _block->JUMP(loopbody); + _block->JUMP(loopbody); _block = loopbody; statement(ast->statement); condition(ast->expression, loopbody, loopend); @@ -1558,21 +1553,18 @@ bool Codegen::visit(ForStatement *ast) enterLoop(ast, forend, forstep); statement(ast->initialiser); - if (! _block->isTerminated()) - _block->JUMP(forcond); + _block->JUMP(forcond); _block = forcond; condition(ast->condition, forbody, forend); _block = forbody; statement(ast->statement); - if (! _block->isTerminated()) - _block->JUMP(forstep); + _block->JUMP(forstep); _block = forstep; statement(ast->expression); - if (! _block->isTerminated()) - _block->JUMP(forcond); + _block->JUMP(forcond); _block = forend; leaveLoop(); @@ -1589,14 +1581,12 @@ bool Codegen::visit(IfStatement *ast) _block = iftrue; statement(ast->ok); - if (! _block->isTerminated()) - _block->JUMP(endif); + _block->JUMP(endif); if (ast->ko) { _block = iffalse; statement(ast->ko); - if (! _block->isTerminated()) - _block->JUMP(endif); + _block->JUMP(endif); } _block = endif; @@ -1643,21 +1633,18 @@ bool Codegen::visit(LocalForStatement *ast) enterLoop(ast, forend, forstep); variableDeclarationList(ast->declarations); - if (! _block->isTerminated()) - _block->JUMP(forcond); + _block->JUMP(forcond); _block = forcond; condition(ast->condition, forbody, forend); _block = forbody; statement(ast->statement); - if (! _block->isTerminated()) - _block->JUMP(forstep); + _block->JUMP(forstep); _block = forstep; statement(ast->expression); - if (! _block->isTerminated()) - _block->JUMP(forcond); + _block->JUMP(forcond); _block = forend; leaveLoop(); @@ -1718,8 +1705,7 @@ bool Codegen::visit(SwitchStatement *ast) leaveLoop(); - if (! _block->isTerminated()) - _block->JUMP(switchend); + _block->JUMP(switchend); _block = switchcond; for (CaseClauses *it = ast->block->clauses; it; it = it->next) { @@ -1745,8 +1731,7 @@ bool Codegen::visit(SwitchStatement *ast) } } - if (! _block->isTerminated()) - _block->JUMP(switchend); + _block->JUMP(switchend); _block = switchend; return false; @@ -1786,8 +1771,7 @@ bool Codegen::visit(WhileStatement *ast) _block = whilebody; statement(ast->statement); - if (! _block->isTerminated()) - _block->JUMP(whilecond); + _block->JUMP(whilecond); _block = whileend; leaveLoop(); -- cgit v1.2.3 From d54b082e6e3ac27d199bcd16074c2b26439966dd Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 8 Jun 2012 16:56:14 +0200 Subject: Incrementally update the control flow graph. --- qv4codegen.cpp | 14 -------------- qv4ir.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 13290e8afe..6bc608d755 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -108,20 +108,6 @@ void liveness(IR::Function *function) QSet V; QVector blocks; - // - // compute the CFG - // - foreach (IR::BasicBlock *block, function->basicBlocks) { - if (IR::Stmt *term = block->terminator()) { - if (IR::Jump *j = term->asJump()) - edge(block, j->target); - else if (IR::CJump *cj = term->asCJump()) { - edge(block, cj->iftrue); - edge(block, cj->iffalse); - } - } - } - ComputeUseDef computeUseDef(function); foreach (IR::BasicBlock *block, function->basicBlocks) { foreach (IR::Stmt *s, block->statements) diff --git a/qv4ir.cpp b/qv4ir.cpp index f27d318f4c..00779a9fe8 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -706,6 +706,13 @@ Stmt *BasicBlock::JUMP(BasicBlock *target) Jump *s = function->New(); s->init(target); statements.append(s); + + assert(! out.contains(target)); + out.append(target); + + assert(! target->in.contains(this)); + target->in.append(this); + return s; } @@ -714,9 +721,27 @@ Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) if (isTerminated()) return 0; + if (iftrue == iffalse) { + MOVE(TEMP(newTemp()), cond); + return JUMP(iftrue); + } + CJump *s = function->New(); s->init(cond, iftrue, iffalse); statements.append(s); + + assert(! out.contains(iftrue)); + out.append(iftrue); + + assert(! iftrue->in.contains(this)); + iftrue->in.append(this); + + assert(! out.contains(iffalse)); + out.append(iffalse); + + assert(! iffalse->in.contains(this)); + iffalse->in.append(this); + return s; } -- cgit v1.2.3 From 4677f7442072018096c309991680877937585e67 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 8 Jun 2012 16:59:58 +0200 Subject: Improve ex to nx conversion. Only IR::Call has an nx conversion. --- qv4codegen.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 6bc608d755..7b4600ff69 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -443,7 +443,14 @@ void Codegen::statement(ExpressionNode *ast) accept(ast); qSwap(_expr, r); if (r.format == ex) { - _block->EXP(*r); + if (r->asCall()) { + _block->EXP(*r); // the nest nx representation for calls is EXP(CALL(c..)) + } else if (r->asTemp()) { + // there is nothing to do + } else { + int t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), *r); + } } } } -- cgit v1.2.3 From 29b0d595713c65075b36c056b92d5277c91c8f22 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 8 Jun 2012 16:22:02 +0100 Subject: Fast track true and false conditionals. --- qv4codegen.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 7b4600ff69..418ae68d0a 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -945,7 +945,11 @@ bool Codegen::visit(DeleteExpression *) bool Codegen::visit(FalseLiteral *) { - _expr.code = _block->CONST(IR::BoolType, 0); + if (_expr.accept(cx)) { + _block->JUMP(_expr.iffalse); + } else { + _expr.code = _block->CONST(IR::BoolType, 0); + } return false; } @@ -1137,7 +1141,11 @@ bool Codegen::visit(TildeExpression *ast) bool Codegen::visit(TrueLiteral *) { - _expr.code = _block->CONST(IR::BoolType, 1); + if (_expr.accept(cx)) { + _block->JUMP(_expr.iftrue); + } else { + _expr.code = _block->CONST(IR::BoolType, 1); + } return false; } -- cgit v1.2.3 From d2e1786a8b999a3e09bcdb33388671cc3594d882 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 8 Jun 2012 17:09:27 +0100 Subject: Trim unreachable blocks --- qv4codegen.cpp | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 418ae68d0a..0b89de7270 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1214,29 +1214,35 @@ void Codegen::linearize(IR::Function *function) } } - foreach (IR::BasicBlock *block, function->basicBlocks) { - while (block) { - if (V.contains(block)) - break; + struct I { static void trace(IR::BasicBlock *block, QSet *V, + QVector *output) { + if (block == 0 || V->contains(block)) + return; - V.insert(block); - block->index = trace.size(); - trace.append(block); + V->insert(block); + block->index = output->size(); + output->append(block); if (IR::Stmt *term = block->terminator()) { - block = 0; - if (IR::Jump *j = term->asJump()) { - block = j->target; + trace(j->target, V, output); } else if (IR::CJump *cj = term->asCJump()) { - if (! V.contains(cj->iffalse)) - block = cj->iffalse; + if (! V->contains(cj->iffalse)) + trace(cj->iffalse, V, output); else - block = cj->iftrue; + trace(cj->iftrue, V, output); } } + + // We could do this for each type above, but it is safer to have a + // "catchall" here + for (int ii = 0; ii < block->out.count(); ++ii) + trace(block->out.at(ii), V, output); } - } + }; + + I::trace(function->basicBlocks.first(), &V, &trace); + exitBlock->index = trace.size(); trace.append(exitBlock); function->basicBlocks = trace; -- cgit v1.2.3 From cfb709326f3689dc633f1a0c44bb7df9489014f5 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 8 Jun 2012 17:09:44 +0100 Subject: Shortcut more constant conditionals --- qv4codegen.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 0b89de7270..0db6a8a995 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1036,13 +1036,20 @@ bool Codegen::visit(NotExpression *ast) bool Codegen::visit(NullExpression *) { - _expr.code = _block->CONST(IR::NullType, 0); + if (_expr.accept(cx)) _block->JUMP(_expr.iffalse); + else _expr.code = _block->CONST(IR::NullType, 0); + return false; } bool Codegen::visit(NumericLiteral *ast) { - _expr.code = _block->CONST(IR::NumberType, ast->value); + if (_expr.accept(cx)) { + if (ast->value) _block->JUMP(_expr.iftrue); + else _block->JUMP(_expr.iffalse); + } else { + _expr.code = _block->CONST(IR::NumberType, ast->value); + } return false; } -- cgit v1.2.3 From 427114d74023c817cd4b24890fab7b3e228a9f7c Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 11 Jun 2012 09:32:00 +0200 Subject: Increase the size of the code cache. This is just a temporary hack, we can't continue to use a contiguous region of memory to store the code. --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 06e9f2d903..4d277bc539 100644 --- a/main.cpp +++ b/main.cpp @@ -177,7 +177,7 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS IR::Module module; IR::Function *globalCode = 0; - const size_t codeSize = 30 * getpagesize(); + const size_t codeSize = 400 * getpagesize(); uchar *code = (uchar *) malloc(codeSize); assert(! (size_t(code) & 15)); -- cgit v1.2.3 From d43ebabc54baa0ebce1390c52950cbb43b5520fb Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 11 Jun 2012 09:34:56 +0200 Subject: Fix the code generator This change fixes a few typos in the code generator (e.g. usages of _loop instead of loop), also it ensures that all function declarations are processed before the statements. --- qv4codegen.cpp | 37 +++++++++++++++++++++++-------------- qv4codegen_p.h | 2 ++ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 0db6a8a995..277bfd126f 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -237,6 +237,7 @@ protected: virtual bool visit(FunctionDeclaration *ast) { + _env->functions.append(ast); _env->hasNestedFunctions = true; _env->enter(ast->name.toString()); enterEnvironment(ast); @@ -390,14 +391,14 @@ IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args) void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) { if (op != IR::OpInvalid) { - if (! (source->asTemp() || source->asConst())) { + if (! (source->asTemp() /*|| source->asConst()*/)) { const unsigned t = _block->newTemp(); _block->MOVE(_block->TEMP(t), source); _block->MOVE(target, _block->TEMP(t), op); return; } } else if (target->asMember() || target->asSubscript()) { - if (! (source->asTemp() || source->asConst())) { + if (! (source->asTemp() /*|| source->asConst()*/)) { const unsigned t = _block->newTemp(); _block->MOVE(_block->TEMP(t), source); _block->MOVE(target, _block->TEMP(t), op); @@ -814,13 +815,17 @@ bool Codegen::visit(BinaryExpression *ast) break; case QSOperator::Assign: + if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) { + assert(!"syntax error"); + } + if (_expr.accept(nx)) { move(*left, *right); } else { const unsigned t = _block->newTemp(); move(_block->TEMP(t), *right); move(*left, _block->TEMP(t)); - _expr.code = _block->TEMP(t); + _expr.code = *left; } break; @@ -835,13 +840,17 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::InplaceRightShift: case QSOperator::InplaceURightShift: case QSOperator::InplaceXor: { + if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) { + assert(!"syntax error"); + } + if (_expr.accept(nx)) { move(*left, *right, baseOp(ast->op)); } else { const unsigned t = _block->newTemp(); move(_block->TEMP(t), *right); move(*left, _block->TEMP(t), baseOp(ast->op)); - _expr.code = _block->TEMP(t); + _expr.code = *left; } break; } @@ -1191,14 +1200,7 @@ bool Codegen::visit(VoidExpression *ast) bool Codegen::visit(FunctionDeclaration *ast) { - IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0, true); - if (_expr.accept(nx)) { - const int index = _env->findMember(ast->name.toString()); - assert(index != -1); - move(_block->TEMP(index), _block->CLOSURE(function)); - } else { - _expr.code = _block->CLOSURE(function); - } + _expr.accept(nx); return false; } @@ -1401,6 +1403,13 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, _function->LOCAL(local); } + foreach (AST::FunctionDeclaration *f, _env->functions) { + IR::Function *function = defineFunction(f->name.toString(), f, f->formals, + f->body ? f->body->elements : 0, true); + _block->MOVE(_block->TEMP(_env->findMember(f->name.toString())), + _block->CLOSURE(function)); + } + sourceElements(body); _function->insertBasicBlock(_exitBlock); @@ -1485,7 +1494,7 @@ bool Codegen::visit(BreakStatement *ast) if (ast->label.isEmpty()) _block->JUMP(_loop->breakBlock); else { - for (Loop *loop = _loop; _loop; _loop = _loop->parent) { + for (Loop *loop = _loop; loop; loop = loop->parent) { if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { _block->JUMP(loop->breakBlock); return false; @@ -1502,7 +1511,7 @@ bool Codegen::visit(ContinueStatement *ast) if (ast->label.isEmpty()) _block->JUMP(_loop->continueBlock); else { - for (Loop *loop = _loop; _loop; _loop = _loop->parent) { + for (Loop *loop = _loop; loop; loop = loop->parent) { if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { if (! loop->continueBlock) break; diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 41f40844e9..ebebe65033 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -57,6 +57,7 @@ protected: Environment *parent; QHash members; QVector vars; + QVector functions; int maxNumberOfArguments; bool hasDirectEval; bool hasNestedFunctions; @@ -272,6 +273,7 @@ private: Loop *_loop; AST::LabelledStatement *_labelledStatement; QHash _envMap; + QHash _functionMap; class ScanFunctions; }; -- cgit v1.2.3 From 810011f45a38585283eee893226c87ac838c72e7 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 11 Jun 2012 09:38:52 +0200 Subject: Improve support for the assignment operators. Also, use indirect calls in the amd64 code generator. --- qmljs_objects.cpp | 2 + qmljs_objects.h | 14 ++-- qmljs_runtime.cpp | 168 +++++++++++++++++++++++++++++++------- qmljs_runtime.h | 212 ++++++++++++++++++++++++++--------------------- qv4ecmaobjects.cpp | 3 +- qv4isel_x86_64.cpp | 235 +++++++++++++++++++++++++++++++---------------------- 6 files changed, 405 insertions(+), 229 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 71a14a265e..94df898277 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -157,6 +157,8 @@ ScriptFunction::ScriptFunction(Context *scope, IR::Function *function) : FunctionObject(scope) , function(function) { + if (function->name) + name = scope->engine->identifier(*function->name); needsActivation = function->needsActivation(); formalParameterCount = function->formals.size(); if (formalParameterCount) { diff --git a/qmljs_objects.h b/qmljs_objects.h index afab547103..49c0fe2289 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -46,12 +46,10 @@ struct String { : _text(text), _hashValue(0) {} inline bool isEqualTo(const String *other) const { - if (other && hashValue() == other->hashValue()) { - if (this == other) - return true; - else - return toQString() == other->toQString(); - } + if (this == other) + return true; + else if (other && hashValue() == other->hashValue()) + return toQString() == other->toQString(); return false; } @@ -313,6 +311,7 @@ struct ArrayObject: Object { struct FunctionObject: Object { Context *scope; + String *name; String **formalParameterList; unsigned int formalParameterCount; String **varList; @@ -321,11 +320,12 @@ struct FunctionObject: Object { FunctionObject(Context *scope) : scope(scope) + , name(0) , formalParameterList(0) , formalParameterCount(0) , varList(0) , varCount(0) - , needsActivation(true) {} + , needsActivation(false) {} virtual QString className() { return QStringLiteral("Function"); } virtual FunctionObject *asFunctionObject() { return this; } diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 88e62eed1e..05cdea1843 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -291,7 +291,7 @@ void Context::throwReferenceError(const Value &value) throwError(Value::fromObject(engine->newErrorObject(Value::fromString(this, msg)))); } -void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc) +void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc) { engine = e; parent = f->scope; @@ -310,9 +310,12 @@ void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionO formalCount = f->formalParameterCount; arguments = args; argumentCount = argc; - if (argc && f->needsActivation) { - arguments = new Value[argc]; - std::copy(args, args + argc, arguments); + if (f->needsActivation || argc < formalCount){ + arguments = new Value[qMax(argc, formalCount)]; + if (argc) + std::copy(args, args + argc, arguments); + if (argc < formalCount) + std::fill(arguments + argc, arguments + formalCount, Value::undefinedValue()); } vars = f->varList; varCount = f->varCount; @@ -334,7 +337,7 @@ void Context::leaveCallContext(FunctionObject *f, Value *returnValue) } } -void Context::initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc) +void Context::initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc) { initCallContext(e, object, f, args, argc); calledAsConstructor = true; @@ -489,112 +492,214 @@ void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *rig } } -void __qmljs_inplace_bit_and_element(Context *ctx, Value *base, Value *index, double value) +void __qmljs_inplace_bit_and_name(Context *ctx, String *name, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_bit_or_element(Context *ctx, Value *base, Value *index, double value) +void __qmljs_inplace_bit_or_name(Context *ctx, String *name, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, double value) +void __qmljs_inplace_bit_xor_name(Context *ctx, String *name, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_add_element(Context *ctx, Value *base, Value *index, double value) +void __qmljs_inplace_add_name(Context *ctx, String *name, Value *value) +{ + if (Value *prop = ctx->lookupPropertyDescriptor(name)) + __qmljs_add(ctx, prop, prop, value); + else + ctx->throwReferenceError(Value::fromString(name)); +} + +void __qmljs_inplace_sub_name(Context *ctx, String *name, Value *value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_mul_name(Context *ctx, String *name, Value *value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_div_name(Context *ctx, String *name, Value *value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_mod_name(Context *ctx, String *name, Value *value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_shl_name(Context *ctx, String *name, Value *value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_shr_name(Context *ctx, String *name, Value *value) +{ + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_ushr_name(Context *ctx, String *name, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_sub_element(Context *ctx, Value *base, Value *index, double value) +void __qmljs_inplace_bit_and_element(Context *ctx, Value *base, Value *index, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_mul_element(Context *ctx, Value *base, Value *index, double value) +void __qmljs_inplace_bit_or_element(Context *ctx, Value *base, Value *index, Value *value) { + Object *obj = base->toObject(ctx).objectValue; + if (ArrayObject *a = obj->asArrayObject()) { + if (index->isNumber()) { + const quint32 idx = index->toUInt32(ctx); + Value v = a->value.at(idx); + __qmljs_bit_or(ctx, &v, &v, value); + a->value.assign(idx, v); + return; + } + } + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, Value *value) +{ + Object *obj = base->toObject(ctx).objectValue; + if (ArrayObject *a = obj->asArrayObject()) { + if (index->isNumber()) { + const quint32 idx = index->toUInt32(ctx); + Value v = a->value.at(idx); + __qmljs_bit_xor(ctx, &v, &v, value); + a->value.assign(idx, v); + return; + } + } + ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); +} + +void __qmljs_inplace_add_element(Context *ctx, Value *base, Value *index, Value *value) +{ + Object *obj = base->toObject(ctx).objectValue; + if (ArrayObject *a = obj->asArrayObject()) { + if (index->isNumber()) { + const quint32 idx = index->toUInt32(ctx); + Value v = a->value.at(idx); + __qmljs_add(ctx, &v, &v, value); + a->value.assign(idx, v); + return; + } + } ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_div_element(Context *ctx, Value *base, Value *index, double value) +void __qmljs_inplace_sub_element(Context *ctx, Value *base, Value *index, Value *value) { + Object *obj = base->toObject(ctx).objectValue; + if (ArrayObject *a = obj->asArrayObject()) { + if (index->isNumber()) { + const quint32 idx = index->toUInt32(ctx); + Value v = a->value.at(idx); + __qmljs_sub(ctx, &v, &v, value); + a->value.assign(idx, v); + return; + } + } ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_mod_element(Context *ctx, Value *base, Value *index, double value) +void __qmljs_inplace_mul_element(Context *ctx, Value *base, Value *index, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_shl_element(Context *ctx, Value *base, Value *index, double value) +void __qmljs_inplace_div_element(Context *ctx, Value *base, Value *index, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_shr_element(Context *ctx, Value *base, Value *index, double value) +void __qmljs_inplace_mod_element(Context *ctx, Value *base, Value *index, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_ushr_element(Context *ctx, Value *base, Value *index, double value) +void __qmljs_inplace_shl_element(Context *ctx, Value *base, Value *index, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_bit_and_member(Context *ctx, Value *base, String *name, double value) +void __qmljs_inplace_shr_element(Context *ctx, Value *base, Value *index, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_bit_or_member(Context *ctx, Value *base, String *name, double value) +void __qmljs_inplace_ushr_element(Context *ctx, Value *base, Value *index, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_bit_xor_member(Context *ctx, Value *base, String *name, double value) +void __qmljs_inplace_bit_and_member(Context *ctx, Value *base, String *name, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_add_member(Context *ctx, Value *base, String *name, double value) +void __qmljs_inplace_bit_or_member(Context *ctx, Value *base, String *name, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_sub_member(Context *ctx, Value *base, String *name, double value) +void __qmljs_inplace_bit_xor_member(Context *ctx, Value *base, String *name, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_mul_member(Context *ctx, Value *base, String *name, double value) +void __qmljs_inplace_add_member(Context *ctx, Value *base, String *name, Value *value) +{ + Value prop = base->objectValue->getProperty(ctx, name); + __qmljs_add(ctx, &prop, &prop, value); + base->objectValue->setProperty(ctx, name, prop); +} + +void __qmljs_inplace_sub_member(Context *ctx, Value *base, String *name, Value *value) +{ + Value prop = base->objectValue->getProperty(ctx, name); + __qmljs_sub(ctx, &prop, &prop, value); + base->objectValue->setProperty(ctx, name, prop); +} + +void __qmljs_inplace_mul_member(Context *ctx, Value *base, String *name, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_div_member(Context *ctx, Value *base, String *name, double value) +void __qmljs_inplace_div_member(Context *ctx, Value *base, String *name, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_mod_member(Context *ctx, Value *base, String *name, double value) +void __qmljs_inplace_mod_member(Context *ctx, Value *base, String *name, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_shl_member(Context *ctx, Value *base, String *name, double value) +void __qmljs_inplace_shl_member(Context *ctx, Value *base, String *name, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_shr_member(Context *ctx, Value *base, String *name, double value) +void __qmljs_inplace_shr_member(Context *ctx, Value *base, String *name, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_ushr_member(Context *ctx, Value *base, String *name, double value) +void __qmljs_inplace_ushr_member(Context *ctx, Value *base, String *name, Value *value) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } @@ -616,7 +721,10 @@ int __qmljs_string_length(Context *, String *string) double __qmljs_string_to_number(Context *, String *string) { - bool ok; + const QString s = string->toQString(); + if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) + return s.toLong(0, 16); + bool ok = false; return string->toQString().toDouble(&ok); // ### TODO } @@ -633,9 +741,7 @@ bool __qmljs_string_compare(Context *, String *left, String *right) bool __qmljs_string_equal(Context *, String *left, String *right) { - return left == right || - (left->hashValue() == right->hashValue() && - left->toQString() == right->toQString()); + return left == right || left->isEqualTo(right); } String *__qmljs_string_concat(Context *ctx, String *first, String *second) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index f2c5414790..a4740d5926 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -3,6 +3,7 @@ #ifndef QMLJS_LLVM_RUNTIME # include +# include #endif #include @@ -196,41 +197,53 @@ void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *ri void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_inplace_bit_and(Context *ctx, Value *result, double value); -void __qmljs_inplace_bit_or(Context *ctx, Value *result, double value); -void __qmljs_inplace_bit_xor(Context *ctx, Value *result, double value); -void __qmljs_inplace_add(Context *ctx, Value *result, double value); -void __qmljs_inplace_sub(Context *ctx, Value *result, double value); -void __qmljs_inplace_mul(Context *ctx, Value *result, double value); -void __qmljs_inplace_div(Context *ctx, Value *result, double value); -void __qmljs_inplace_mod(Context *ctx, Value *result, double value); -void __qmljs_inplace_shl(Context *ctx, Value *result, double value); -void __qmljs_inplace_shr(Context *ctx, Value *result, double value); -void __qmljs_inplace_ushr(Context *ctx, Value *result, double value); - -void __qmljs_inplace_bit_and_element(Context *ctx, Value *base, Value *index, double value); -void __qmljs_inplace_bit_or_element(Context *ctx, Value *base, Value *index, double value); -void __qmljs_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, double value); -void __qmljs_inplace_add_element(Context *ctx, Value *base, Value *index, double value); -void __qmljs_inplace_sub_element(Context *ctx, Value *base, Value *index, double value); -void __qmljs_inplace_mul_element(Context *ctx, Value *base, Value *index, double value); -void __qmljs_inplace_div_element(Context *ctx, Value *base, Value *index, double value); -void __qmljs_inplace_mod_element(Context *ctx, Value *base, Value *index, double value); -void __qmljs_inplace_shl_element(Context *ctx, Value *base, Value *index, double value); -void __qmljs_inplace_shr_element(Context *ctx, Value *base, Value *index, double value); -void __qmljs_inplace_ushr_element(Context *ctx, Value *base, Value *index, double value); - -void __qmljs_inplace_bit_and_member(Context *ctx, Value *base, String *name, double value); -void __qmljs_inplace_bit_or_member(Context *ctx, Value *base, String *name, double value); -void __qmljs_inplace_bit_xor_member(Context *ctx, Value *base, String *name, double value); -void __qmljs_inplace_add_member(Context *ctx, Value *base, String *name, double value); -void __qmljs_inplace_sub_member(Context *ctx, Value *base, String *name, double value); -void __qmljs_inplace_mul_member(Context *ctx, Value *base, String *name, double value); -void __qmljs_inplace_div_member(Context *ctx, Value *base, String *name, double value); -void __qmljs_inplace_mod_member(Context *ctx, Value *base, String *name, double value); -void __qmljs_inplace_shl_member(Context *ctx, Value *base, String *name, double value); -void __qmljs_inplace_shr_member(Context *ctx, Value *base, String *name, double value); -void __qmljs_inplace_ushr_member(Context *ctx, Value *base, String *name, double value); +void __qmljs_inplace_bit_and(Context *ctx, Value *result, Value *value); +void __qmljs_inplace_bit_or(Context *ctx, Value *result, Value *value); +void __qmljs_inplace_bit_xor(Context *ctx, Value *result, Value *value); +void __qmljs_inplace_add(Context *ctx, Value *result, Value *value); +void __qmljs_inplace_sub(Context *ctx, Value *result, Value *value); +void __qmljs_inplace_mul(Context *ctx, Value *result, Value *value); +void __qmljs_inplace_div(Context *ctx, Value *result, Value *value); +void __qmljs_inplace_mod(Context *ctx, Value *result, Value *value); +void __qmljs_inplace_shl(Context *ctx, Value *result, Value *value); +void __qmljs_inplace_shr(Context *ctx, Value *result, Value *value); +void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value); + +void __qmljs_inplace_bit_and_name(Context *ctx, String *name, Value *value); +void __qmljs_inplace_bit_or_name(Context *ctx, String *name, Value *value); +void __qmljs_inplace_bit_xor_name(Context *ctx, String *name, Value *value); +void __qmljs_inplace_add_name(Context *ctx, String *name, Value *value); +void __qmljs_inplace_sub_name(Context *ctx, String *name, Value *value); +void __qmljs_inplace_mul_name(Context *ctx, String *name, Value *value); +void __qmljs_inplace_div_name(Context *ctx, String *name, Value *value); +void __qmljs_inplace_mod_name(Context *ctx, String *name, Value *value); +void __qmljs_inplace_shl_name(Context *ctx, String *name, Value *value); +void __qmljs_inplace_shr_name(Context *ctx, String *name, Value *value); +void __qmljs_inplace_ushr_name(Context *ctx, String *name, Value *value); + +void __qmljs_inplace_bit_and_element(Context *ctx, Value *base, Value *index, Value *value); +void __qmljs_inplace_bit_or_element(Context *ctx, Value *base, Value *index, Value *value); +void __qmljs_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, Value *value); +void __qmljs_inplace_add_element(Context *ctx, Value *base, Value *index, Value *value); +void __qmljs_inplace_sub_element(Context *ctx, Value *base, Value *index, Value *value); +void __qmljs_inplace_mul_element(Context *ctx, Value *base, Value *index, Value *value); +void __qmljs_inplace_div_element(Context *ctx, Value *base, Value *index, Value *value); +void __qmljs_inplace_mod_element(Context *ctx, Value *base, Value *index, Value *value); +void __qmljs_inplace_shl_element(Context *ctx, Value *base, Value *index, Value *value); +void __qmljs_inplace_shr_element(Context *ctx, Value *base, Value *index, Value *value); +void __qmljs_inplace_ushr_element(Context *ctx, Value *base, Value *index, Value *value); + +void __qmljs_inplace_bit_and_member(Context *ctx, Value *base, String *name, Value *value); +void __qmljs_inplace_bit_or_member(Context *ctx, Value *base, String *name, Value *value); +void __qmljs_inplace_bit_xor_member(Context *ctx, Value *base, String *name, Value *value); +void __qmljs_inplace_add_member(Context *ctx, Value *base, String *name, Value *value); +void __qmljs_inplace_sub_member(Context *ctx, Value *base, String *name, Value *value); +void __qmljs_inplace_mul_member(Context *ctx, Value *base, String *name, Value *value); +void __qmljs_inplace_div_member(Context *ctx, Value *base, String *name, Value *value); +void __qmljs_inplace_mod_member(Context *ctx, Value *base, String *name, Value *value); +void __qmljs_inplace_shl_member(Context *ctx, Value *base, String *name, Value *value); +void __qmljs_inplace_shr_member(Context *ctx, Value *base, String *name, Value *value); +void __qmljs_inplace_ushr_member(Context *ctx, Value *base, String *name, Value *value); bool __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right); bool __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right); @@ -390,10 +403,10 @@ struct Context { void throwUnimplemented(const QString &message); #endif - void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc); + void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc); void leaveCallContext(FunctionObject *f, Value *r); - void initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, int argc); + void initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc); void leaveConstructorContext(FunctionObject *f, Value *returnValue); }; @@ -515,26 +528,65 @@ inline double __qmljs_to_integer(Context *ctx, const Value *value) inline int __qmljs_to_int32(Context *ctx, const Value *value) { - const double number = __qmljs_to_number(ctx, value); + double number = __qmljs_to_number(ctx, value); + + if ((number >= -2147483648.0 && number < 2147483648.0)) { + return static_cast(number); + } + if (! number || isnan(number) || isinf(number)) - return +0; - return (int) trunc(number); // ### + return 0; + + double D32 = 4294967296.0; + double sign = (number < 0) ? -1.0 : 1.0; + double abs_n = fabs(number); + + number = ::fmod(sign * ::floor(abs_n), D32); + const double D31 = D32 / 2.0; + + if (sign == -1 && number < -D31) + number += D32; + + else if (sign != -1 && number >= D31) + number -= D32; + + return qint32(number); } inline unsigned __qmljs_to_uint32(Context *ctx, const Value *value) { - const double number = __qmljs_to_number(ctx, value); + double number = __qmljs_to_number(ctx, value); if (! number || isnan(number) || isinf(number)) return +0; - return (unsigned) trunc(number); // ### + + double sign = (number < 0) ? -1.0 : 1.0; + double abs_n = ::fabs(number); + + const double D32 = 4294967296.0; + number = ::fmod(sign * ::floor(abs_n), D32); + + if (number < 0) + number += D32; + + return unsigned(number); } inline unsigned short __qmljs_to_uint16(Context *ctx, const Value *value) { - const double number = __qmljs_to_number(ctx, value); + double number = __qmljs_to_number(ctx, value); if (! number || isnan(number) || isinf(number)) return +0; - return (unsigned short) trunc(number); // ### + + double sign = (number < 0) ? -1.0 : 1.0; + double abs_n = ::fabs(number); + + double D16 = 65536.0; + number = ::fmod(sign * ::floor(abs_n), D16); + + if (number < 0) + number += D16; + + return (unsigned short)number; } inline void __qmljs_to_string(Context *ctx, Value *result, const Value *value) @@ -699,81 +751,59 @@ inline void __qmljs_bit_and(Context *ctx, Value *result, const Value *left, cons __qmljs_init_number(result, lval & rval); } -inline void __qmljs_inplace_bit_and(Context *ctx, Value *result, double value) +inline void __qmljs_inplace_bit_and(Context *ctx, Value *result, Value *value) { - Value v; - __qmljs_init_number(&v, value); - __qmljs_bit_xor(ctx, result, result, &v); + __qmljs_bit_xor(ctx, result, result, value); } -inline void __qmljs_inplace_bit_or(Context *ctx, Value *result, double value) +inline void __qmljs_inplace_bit_or(Context *ctx, Value *result, Value *value) { - Value v; - __qmljs_init_number(&v, value); - __qmljs_bit_or(ctx, result, result, &v); + __qmljs_bit_or(ctx, result, result, value); } -inline void __qmljs_inplace_bit_xor(Context *ctx, Value *result, double value) +inline void __qmljs_inplace_bit_xor(Context *ctx, Value *result, Value *value) { - Value v; - __qmljs_init_number(&v, value); - __qmljs_bit_xor(ctx, result, result, &v); + __qmljs_bit_xor(ctx, result, result, value); } -inline void __qmljs_inplace_add(Context *ctx, Value *result, double value) +inline void __qmljs_inplace_add(Context *ctx, Value *result, Value *value) { - Value v; - __qmljs_init_number(&v, value); - __qmljs_add(ctx, result, result, &v); + __qmljs_add(ctx, result, result, value); } -inline void __qmljs_inplace_sub(Context *ctx, Value *result, double value) +inline void __qmljs_inplace_sub(Context *ctx, Value *result, Value *value) { - Value v; - __qmljs_init_number(&v, value); - __qmljs_sub(ctx, result, result, &v); + __qmljs_sub(ctx, result, result, value); } -inline void __qmljs_inplace_mul(Context *ctx, Value *result, double value) +inline void __qmljs_inplace_mul(Context *ctx, Value *result, Value *value) { - Value v; - __qmljs_init_number(&v, value); - __qmljs_mul(ctx, result, result, &v); + __qmljs_mul(ctx, result, result, value); } -inline void __qmljs_inplace_div(Context *ctx, Value *result, double value) +inline void __qmljs_inplace_div(Context *ctx, Value *result, Value *value) { - Value v; - __qmljs_init_number(&v, value); - __qmljs_div(ctx, result, result, &v); + __qmljs_div(ctx, result, result, value); } -inline void __qmljs_inplace_mod(Context *ctx, Value *result, double value) +inline void __qmljs_inplace_mod(Context *ctx, Value *result, Value *value) { - Value v; - __qmljs_init_number(&v, value); - __qmljs_mod(ctx, result, result, &v); + __qmljs_mod(ctx, result, result, value); } -inline void __qmljs_inplace_shl(Context *ctx, Value *result, double value) +inline void __qmljs_inplace_shl(Context *ctx, Value *result, Value *value) { - Value v; - __qmljs_init_number(&v, value); - __qmljs_shl(ctx, result, result, &v); + __qmljs_shl(ctx, result, result, value); } -inline void __qmljs_inplace_shr(Context *ctx, Value *result, double value) +inline void __qmljs_inplace_shr(Context *ctx, Value *result, Value *value) { - Value v; - __qmljs_init_number(&v, value); - __qmljs_shr(ctx, result, result, &v); + __qmljs_shr(ctx, result, result, value); } -inline void __qmljs_inplace_ushr(Context *ctx, Value *result, double value) +inline void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value) { - Value v; - __qmljs_init_number(&v, value); - __qmljs_ushr(ctx, result, result, &v); + __qmljs_ushr(ctx, result, result, value); } inline void __qmljs_add(Context *ctx, Value *result, const Value *left, const Value *right) @@ -830,7 +860,7 @@ inline void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const V { unsigned lval = __qmljs_to_uint32(ctx, left); unsigned rval = __qmljs_to_uint32(ctx, right); - __qmljs_init_number(result, lval << rval); + __qmljs_init_number(result, lval >> rval); } inline void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Value *right) @@ -901,8 +931,8 @@ inline void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Val { if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) { __qmljs_init_boolean(result, left->numberValue != right->numberValue); - } else if (left->type == STRING_TYPE && right->type != STRING_TYPE) { - __qmljs_init_boolean(result, __qmljs_string_equal(ctx, left->stringValue, right->stringValue)); + } else if (left->type == STRING_TYPE && right->type == STRING_TYPE) { + __qmljs_init_boolean(result, !__qmljs_string_equal(ctx, left->stringValue, right->stringValue)); } else { bool r = ! __qmljs_equal(ctx, left, right); __qmljs_init_boolean(result, r); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 78db86b13c..11bc92f8d0 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -679,6 +679,8 @@ void StringCtor::call(Context *ctx) void StringPrototype::init(Context *ctx, const Value &ctor) { ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue->setProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode); + setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("toString"), method_toString); setProperty(ctx, QStringLiteral("valueOf"), method_valueOf); @@ -699,7 +701,6 @@ void StringPrototype::init(Context *ctx, const Value &ctor) setProperty(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); setProperty(ctx, QStringLiteral("toUpperCase"), method_toUpperCase); setProperty(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); - setProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode); } QString StringPrototype::getThisString(Context *ctx) diff --git a/qv4isel_x86_64.cpp b/qv4isel_x86_64.cpp index 5d6da888e2..e8fe6d4517 100644 --- a/qv4isel_x86_64.cpp +++ b/qv4isel_x86_64.cpp @@ -20,6 +20,12 @@ typedef void *gpointer; # include #endif +#define qmljs_call_code(ptr, code) \ + do { \ + amd64_mov_reg_imm(ptr, AMD64_RAX, code); \ + amd64_call_reg(ptr, AMD64_RAX); \ + } while (0) + using namespace QQmlJS; using namespace QQmlJS::x86_64; using namespace QQmlJS::VM; @@ -194,7 +200,7 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu assert(arg != 0); amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); loadTempAddress(AMD64_RSI, arg); - amd64_call_code(_codePtr, __qmljs_copy); + qmljs_call_code(_codePtr, __qmljs_copy); } amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context @@ -208,7 +214,7 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id)); amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); - amd64_call_code(_codePtr, __qmljs_call_activation_property); + qmljs_call_code(_codePtr, __qmljs_call_activation_property); } else { switch (baseName->builtin) { case IR::Name::builtin_invalid: @@ -217,7 +223,7 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu case IR::Name::builtin_typeof: amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); - amd64_call_code(_codePtr, __qmljs_builtin_typeof); + qmljs_call_code(_codePtr, __qmljs_builtin_typeof); break; case IR::Name::builtin_delete: Q_UNREACHABLE(); @@ -225,12 +231,12 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu case IR::Name::builtin_throw: amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); - amd64_call_code(_codePtr, __qmljs_builtin_throw); + qmljs_call_code(_codePtr, __qmljs_builtin_throw); break; case IR::Name::builtin_rethrow: amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); - amd64_call_code(_codePtr, __qmljs_builtin_rethrow); + qmljs_call_code(_codePtr, __qmljs_builtin_rethrow); return; // we need to return to avoid checking the exceptions } } @@ -255,7 +261,7 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) assert(arg != 0); amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); loadTempAddress(AMD64_RSI, arg); - amd64_call_code(_codePtr, __qmljs_copy); + qmljs_call_code(_codePtr, __qmljs_copy); } amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context @@ -269,7 +275,7 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) loadTempAddress(AMD64_RCX, baseTemp); amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); - amd64_call_code(_codePtr, __qmljs_call_value); + qmljs_call_code(_codePtr, __qmljs_call_value); checkExceptions(); } @@ -291,7 +297,7 @@ void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) assert(arg != 0); amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); loadTempAddress(AMD64_RSI, arg); - amd64_call_code(_codePtr, __qmljs_copy); + qmljs_call_code(_codePtr, __qmljs_copy); } //__qmljs_call_property(ctx, result, base, name, args, argc); @@ -306,7 +312,7 @@ void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name)); amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); - amd64_call_code(_codePtr, __qmljs_call_property); + qmljs_call_code(_codePtr, __qmljs_call_property); checkExceptions(); } @@ -327,7 +333,7 @@ void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp * assert(arg != 0); amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); loadTempAddress(AMD64_RSI, arg); - amd64_call_code(_codePtr, __qmljs_copy); + qmljs_call_code(_codePtr, __qmljs_copy); } amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context @@ -340,7 +346,7 @@ void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp * amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id)); amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); - amd64_call_code(_codePtr, __qmljs_construct_activation_property); + qmljs_call_code(_codePtr, __qmljs_construct_activation_property); checkExceptions(); } @@ -362,7 +368,7 @@ void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) assert(arg != 0); amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); loadTempAddress(AMD64_RSI, arg); - amd64_call_code(_codePtr, __qmljs_copy); + qmljs_call_code(_codePtr, __qmljs_copy); } //__qmljs_call_property(ctx, result, base, name, args, argc); @@ -377,7 +383,7 @@ void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name)); amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); - amd64_call_code(_codePtr, __qmljs_construct_property); + qmljs_call_code(_codePtr, __qmljs_construct_property); checkExceptions(); } @@ -398,7 +404,7 @@ void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) assert(arg != 0); amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); loadTempAddress(AMD64_RSI, arg); - amd64_call_code(_codePtr, __qmljs_copy); + qmljs_call_code(_codePtr, __qmljs_copy); } amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context @@ -411,7 +417,7 @@ void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) loadTempAddress(AMD64_RDX, baseTemp); amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); - amd64_call_code(_codePtr, __qmljs_construct_value); + qmljs_call_code(_codePtr, __qmljs_construct_value); } void InstructionSelection::checkExceptions() @@ -419,7 +425,7 @@ void InstructionSelection::checkExceptions() amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R14, offsetof(Context, hasUncaughtException), 4); amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 1, 1); _patches[_function->handlersBlock].append(_codePtr); - amd64_branch32(_codePtr, X86_CC_E, 0, 1); + x86_branch32(_codePtr, X86_CC_E, 0, 1); } void InstructionSelection::visitExp(IR::Exp *s) @@ -466,13 +472,13 @@ void InstructionSelection::visitMove(IR::Move *s) switch (c->type) { case IR::BoolType: amd64_mov_reg_imm(_codePtr, AMD64_RDX, c->value != 0); - amd64_call_code(_codePtr, __qmljs_set_activation_property_boolean); + qmljs_call_code(_codePtr, __qmljs_set_activation_property_boolean); break; case IR::NumberType: amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); - amd64_call_code(_codePtr, __qmljs_set_activation_property_number); + qmljs_call_code(_codePtr, __qmljs_set_activation_property_number); break; default: @@ -483,22 +489,22 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); amd64_mov_reg_imm(_codePtr, AMD64_RDX, _engine->newString(*str->value)); - amd64_call_code(_codePtr, __qmljs_set_activation_property_string); + qmljs_call_code(_codePtr, __qmljs_set_activation_property_string); } else if (IR::Temp *t = s->source->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); loadTempAddress(AMD64_RDX, t); - amd64_call_code(_codePtr, __qmljs_set_activation_property); + qmljs_call_code(_codePtr, __qmljs_set_activation_property); } else if (IR::Name *other = s->source->asName()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*other->id)); - amd64_call_code(_codePtr, __qmljs_copy_activation_property); + qmljs_call_code(_codePtr, __qmljs_copy_activation_property); } else if (IR::Closure *clos = s->source->asClosure()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); amd64_mov_reg_imm(_codePtr, AMD64_RDX, clos->value); - amd64_call_code(_codePtr, __qmljs_set_activation_property_closure); + qmljs_call_code(_codePtr, __qmljs_set_activation_property_closure); } else { Q_UNIMPLEMENTED(); assert(!"TODO"); @@ -511,11 +517,11 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. - amd64_call_code(_codePtr, __qmljs_get_thisObject); + qmljs_call_code(_codePtr, __qmljs_get_thisObject); } else { String *propertyName = identifier(*n->id); amd64_mov_reg_imm(_codePtr, AMD64_RDX, propertyName); - amd64_call_code(_codePtr, __qmljs_get_activation_property); + qmljs_call_code(_codePtr, __qmljs_get_activation_property); checkExceptions(); } return; @@ -559,13 +565,13 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::String *str = s->source->asString()) { loadTempAddress(AMD64_RDI, t); amd64_mov_reg_imm(_codePtr, AMD64_RSI, _engine->newString(*str->value)); - amd64_call_code(_codePtr, __qmljs_init_string); + qmljs_call_code(_codePtr, __qmljs_init_string); return; } else if (IR::Closure *clos = s->source->asClosure()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); amd64_mov_reg_imm(_codePtr, AMD64_RDX, clos->value); - amd64_call_code(_codePtr, __qmljs_init_closure); + qmljs_call_code(_codePtr, __qmljs_init_closure); return; } else if (IR::New *ctor = s->source->asNew()) { if (ctor->base->asName()) { @@ -585,7 +591,7 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RSI, t); loadTempAddress(AMD64_RDX, base); amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*m->name)); - amd64_call_code(_codePtr, __qmljs_get_property); + qmljs_call_code(_codePtr, __qmljs_get_property); checkExceptions(); return; } @@ -596,7 +602,7 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RSI, t); loadTempAddress(AMD64_RDX, ss->base->asTemp()); loadTempAddress(AMD64_RCX, ss->index->asTemp()); - amd64_call_code(_codePtr, __qmljs_get_element); + qmljs_call_code(_codePtr, __qmljs_get_element); checkExceptions(); return; } else if (IR::Unop *u = s->source->asUnop()) { @@ -613,10 +619,7 @@ void InstructionSelection::visitMove(IR::Move *s) case IR::OpCompl: op = __qmljs_compl; break; default: assert(!"unreachable"); break; } // switch - amd64_call_code(_codePtr, op); - return; - } else if (IR::Const *c = u->expr->asConst()) { - assert(!"wip"); + qmljs_call_code(_codePtr, op); return; } } else if (IR::Binop *b = s->source->asBinop()) { @@ -633,10 +636,10 @@ void InstructionSelection::visitMove(IR::Move *s) if (b->op == IR::OpMul || b->op == IR::OpAdd || b->op == IR::OpSub || b->op == IR::OpDiv) { amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RDX, 0, NUMBER_TYPE, 4); label1 = _codePtr; - amd64_branch8(_codePtr, X86_CC_NE, 0, 0); + x86_branch8(_codePtr, X86_CC_NE, 0, 0); amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RCX, 0, NUMBER_TYPE, 4); label2 = _codePtr; - amd64_branch8(_codePtr, X86_CC_NE, 0, 0); + x86_branch8(_codePtr, X86_CC_NE, 0, 0); amd64_movsd_reg_membase(_codePtr, AMD64_XMM0, AMD64_RDX, offsetof(Value, numberValue)); amd64_movsd_reg_membase(_codePtr, AMD64_XMM1, AMD64_RCX, offsetof(Value, numberValue)); switch (b->op) { @@ -659,7 +662,7 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NUMBER_TYPE, 4); amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), AMD64_XMM0); label3 = _codePtr; - amd64_jump32(_codePtr, 0); + x86_jump32(_codePtr, 0); } @@ -707,7 +710,7 @@ void InstructionSelection::visitMove(IR::Move *s) assert(!"unreachable"); break; } - amd64_call_code(_codePtr, op); + qmljs_call_code(_codePtr, op); if (label3) amd64_patch(label3, _codePtr); return; @@ -732,26 +735,26 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); - amd64_call_code(_codePtr, __qmljs_set_property_number); + qmljs_call_code(_codePtr, __qmljs_set_property_number); } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, base); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); amd64_mov_reg_imm(_codePtr, AMD64_RCX, _engine->newString(*str->value)); - amd64_call_code(_codePtr, __qmljs_set_property_string); + qmljs_call_code(_codePtr, __qmljs_set_property_string); } else if (IR::Temp *t = s->source->asTemp()) { // __qmljs_set_property(ctx, object, name, value); amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, base); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); loadTempAddress(AMD64_RCX, t); - amd64_call_code(_codePtr, __qmljs_set_property); + qmljs_call_code(_codePtr, __qmljs_set_property); } else if (IR::Closure *clos = s->source->asClosure()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, base); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); amd64_mov_reg_imm(_codePtr, AMD64_RCX, clos->value); - amd64_call_code(_codePtr, __qmljs_set_property_closure); + qmljs_call_code(_codePtr, __qmljs_set_property_closure); } else { Q_UNIMPLEMENTED(); assert(!"TODO"); @@ -764,7 +767,7 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RSI, ss->base->asTemp()); loadTempAddress(AMD64_RDX, ss->index->asTemp()); loadTempAddress(AMD64_RCX, t2); - amd64_call_code(_codePtr, __qmljs_set_element); + qmljs_call_code(_codePtr, __qmljs_set_element); } else if (IR::Const *c = s->source->asConst()) { if (c->type == IR::NumberType) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); @@ -772,8 +775,10 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RDX, ss->index->asTemp()); amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); - amd64_call_code(_codePtr, __qmljs_set_element_number); + qmljs_call_code(_codePtr, __qmljs_set_element_number); } else { + s->dump(qout, IR::Stmt::MIR); + qout << endl; Q_UNIMPLEMENTED(); assert(!"TODO"); } @@ -787,38 +792,12 @@ void InstructionSelection::visitMove(IR::Move *s) } else { // inplace assignment, e.g. x += 1, ++x, ... if (IR::Temp *t = s->target->asTemp()) { - if (IR::Const *c = s->source->asConst()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); - - void (*op)(Context *, Value *, double); - switch (s->op) { - case IR::OpBitAnd: op = __qmljs_inplace_bit_and; break; - case IR::OpBitOr: op = __qmljs_inplace_bit_or; break; - case IR::OpBitXor: op = __qmljs_inplace_bit_xor; break; - case IR::OpAdd: op = __qmljs_inplace_add; break; - case IR::OpSub: op = __qmljs_inplace_sub; break; - case IR::OpMul: op = __qmljs_inplace_mul; break; - case IR::OpDiv: op = __qmljs_inplace_div; break; - case IR::OpMod: op = __qmljs_inplace_mod; break; - case IR::OpLShift: op = __qmljs_inplace_shl; break; - case IR::OpRShift: op = __qmljs_inplace_shr; break; - case IR::OpURShift: op = __qmljs_inplace_ushr; break; - default: - Q_UNREACHABLE(); - break; - } - - amd64_call_code(_codePtr, op); - return; - } else if (IR::Temp *t2 = s->source->asTemp()) { + if (IR::Temp *t2 = s->source->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); amd64_mov_reg_reg(_codePtr, AMD64_RDX, AMD64_RSI, 8); loadTempAddress(AMD64_RCX, t2); - void (*op)(Context *, Value *, const Value *, const Value *); + void (*op)(Context *, Value *, const Value *, const Value *) = 0; switch (s->op) { case IR::OpBitAnd: op = __qmljs_bit_and; break; case IR::OpBitOr: op = __qmljs_bit_or; break; @@ -836,31 +815,89 @@ void InstructionSelection::visitMove(IR::Move *s) break; } - amd64_call_code(_codePtr, op); + qmljs_call_code(_codePtr, op); return; } } else if (IR::Name *n = s->target->asName()) { - if (IR::Const *c = s->source->asConst()) { - assert(!"wip"); - return; - } else if (IR::Temp *t = s->source->asTemp()) { - assert(!"wip"); + if (IR::Temp *t = s->source->asTemp()) { + void (*op)(Context *, String *, Value *) = 0; + switch (s->op) { + case IR::OpBitAnd: op = __qmljs_inplace_bit_and_name; break; + case IR::OpBitOr: op = __qmljs_inplace_bit_or_name; break; + case IR::OpBitXor: op = __qmljs_inplace_bit_xor_name; break; + case IR::OpAdd: op = __qmljs_inplace_add_name; break; + case IR::OpSub: op = __qmljs_inplace_sub_name; break; + case IR::OpMul: op = __qmljs_inplace_mul_name; break; + case IR::OpDiv: op = __qmljs_inplace_div_name; break; + case IR::OpMod: op = __qmljs_inplace_mod_name; break; + case IR::OpLShift: op = __qmljs_inplace_shl_name; break; + case IR::OpRShift: op = __qmljs_inplace_shr_name; break; + case IR::OpURShift: op = __qmljs_inplace_ushr_name; break; + default: + Q_UNREACHABLE(); + break; + } + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + amd64_mov_reg_imm(_codePtr, AMD64_RSI, identifier(*n->id)); + loadTempAddress(AMD64_RDX, t); + qmljs_call_code(_codePtr, op); + checkExceptions(); return; } } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (IR::Const *c = s->source->asConst()) { - assert(!"wip"); - return; - } else if (IR::Temp *t = s->source->asTemp()) { - assert(!"wip"); + if (IR::Temp *t = s->source->asTemp()) { + void (*op)(Context *, Value *, Value *, Value *) = 0; + switch (s->op) { + case IR::OpBitAnd: op = __qmljs_inplace_bit_and_element; break; + case IR::OpBitOr: op = __qmljs_inplace_bit_or_element; break; + case IR::OpBitXor: op = __qmljs_inplace_bit_xor_element; break; + case IR::OpAdd: op = __qmljs_inplace_add_element; break; + case IR::OpSub: op = __qmljs_inplace_sub_element; break; + case IR::OpMul: op = __qmljs_inplace_mul_element; break; + case IR::OpDiv: op = __qmljs_inplace_div_element; break; + case IR::OpMod: op = __qmljs_inplace_mod_element; break; + case IR::OpLShift: op = __qmljs_inplace_shl_element; break; + case IR::OpRShift: op = __qmljs_inplace_shr_element; break; + case IR::OpURShift: op = __qmljs_inplace_ushr_element; break; + default: + Q_UNREACHABLE(); + break; + } + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, ss->base->asTemp()); + loadTempAddress(AMD64_RDX, ss->index->asTemp()); + loadTempAddress(AMD64_RCX, t); + qmljs_call_code(_codePtr, op); + checkExceptions(); return; } } else if (IR::Member *m = s->target->asMember()) { - if (IR::Const *c = s->source->asConst()) { - assert(!"wip"); - return; - } else if (IR::Temp *t = s->source->asTemp()) { - assert(!"wip"); + if (IR::Temp *t = s->source->asTemp()) { + void (*op)(Context *, Value *, String *, Value *) = 0; + switch (s->op) { + case IR::OpBitAnd: op = __qmljs_inplace_bit_and_member; break; + case IR::OpBitOr: op = __qmljs_inplace_bit_or_member; break; + case IR::OpBitXor: op = __qmljs_inplace_bit_xor_member; break; + case IR::OpAdd: op = __qmljs_inplace_add_member; break; + case IR::OpSub: op = __qmljs_inplace_sub_member; break; + case IR::OpMul: op = __qmljs_inplace_mul_member; break; + case IR::OpDiv: op = __qmljs_inplace_div_member; break; + case IR::OpMod: op = __qmljs_inplace_mod_member; break; + case IR::OpLShift: op = __qmljs_inplace_shl_member; break; + case IR::OpRShift: op = __qmljs_inplace_shr_member; break; + case IR::OpURShift: op = __qmljs_inplace_ushr_member; break; + default: + Q_UNREACHABLE(); + break; + } + + amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); + loadTempAddress(AMD64_RSI, m->base->asTemp()); + amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); + loadTempAddress(AMD64_RCX, t); + qmljs_call_code(_codePtr, op); + checkExceptions(); return; } } @@ -876,7 +913,7 @@ void InstructionSelection::visitJump(IR::Jump *s) { if (_block->index + 1 != s->target->index) { _patches[s->target].append(_codePtr); - amd64_jump32(_codePtr, 0); + x86_jump32(_codePtr, 0); } } @@ -890,25 +927,25 @@ void InstructionSelection::visitCJump(IR::CJump *s) amd64_alu_reg_imm(_codePtr, X86_CMP, AMD64_RAX, BOOLEAN_TYPE); uchar *label1 = _codePtr; - amd64_branch8(_codePtr, X86_CC_NE, 0, 0); + x86_branch8(_codePtr, X86_CC_NE, 0, 0); amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(Value, booleanValue), 1); uchar *label2 = _codePtr; - amd64_jump8(_codePtr, 0); + x86_jump8(_codePtr, 0); amd64_patch(label1, _codePtr); - amd64_call_code(_codePtr, __qmljs_to_boolean); + qmljs_call_code(_codePtr, __qmljs_to_boolean); amd64_patch(label2, _codePtr); amd64_mov_reg_imm_size(_codePtr, AMD64_RDX, 1, 1); amd64_alu_reg8_reg8(_codePtr, X86_CMP, AMD64_RAX, AMD64_RDX, 0, 0); _patches[s->iftrue].append(_codePtr); - amd64_branch32(_codePtr, X86_CC_E, 0, 1); + x86_branch32(_codePtr, X86_CC_E, 0, 1); if (_block->index + 1 != s->iffalse->index) { _patches[s->iffalse].append(_codePtr); - amd64_jump32(_codePtr, 0); + x86_jump32(_codePtr, 0); } return; } else if (IR::Binop *b = s->cond->asBinop()) { @@ -922,10 +959,10 @@ void InstructionSelection::visitCJump(IR::CJump *s) if (b->op != IR::OpInstanceof && b->op != IR::OpIn) { amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RSI, 0, NUMBER_TYPE, 4); label1 = _codePtr; - amd64_branch8(_codePtr, X86_CC_NE, 0, 0); + x86_branch8(_codePtr, X86_CC_NE, 0, 0); amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RDX, 0, NUMBER_TYPE, 4); label2 = _codePtr; - amd64_branch8(_codePtr, X86_CC_NE, 0, 0); + x86_branch8(_codePtr, X86_CC_NE, 0, 0); amd64_movsd_reg_membase(_codePtr, AMD64_XMM0, AMD64_RSI, offsetof(Value, numberValue)); amd64_movsd_reg_membase(_codePtr, AMD64_XMM1, AMD64_RDX, offsetof(Value, numberValue)); @@ -946,7 +983,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) amd64_set_reg_size(_codePtr, op, AMD64_RAX, 0, 1); label3 = _codePtr; - amd64_jump32(_codePtr, 0); + x86_jump32(_codePtr, 0); } if (label1 && label2) { @@ -971,7 +1008,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) case IR::OpIn: op = __qmljs_cmp_in; break; } // switch - amd64_call_code(_codePtr, op); + qmljs_call_code(_codePtr, op); if (label3) amd64_patch(label3, _codePtr); @@ -980,11 +1017,11 @@ void InstructionSelection::visitCJump(IR::CJump *s) x86_alu_reg8_reg8(_codePtr, X86_CMP, X86_EAX, X86_EDX, 0, 0); _patches[s->iftrue].append(_codePtr); - amd64_branch32(_codePtr, X86_CC_E, 0, 1); + x86_branch32(_codePtr, X86_CC_E, 0, 1); if (_block->index + 1 != s->iffalse->index) { _patches[s->iffalse].append(_codePtr); - amd64_jump32(_codePtr, 0); + x86_jump32(_codePtr, 0); } return; @@ -1002,7 +1039,7 @@ void InstructionSelection::visitRet(IR::Ret *s) if (IR::Temp *t = s->expr->asTemp()) { amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_R14, offsetof(Context, result)); loadTempAddress(AMD64_RSI, t); - amd64_call_code(_codePtr, __qmljs_copy); + qmljs_call_code(_codePtr, __qmljs_copy); return; } Q_UNIMPLEMENTED(); -- cgit v1.2.3 From d63aeca410fc6bd745d744fa9800389115737a12 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 11 Jun 2012 11:50:10 +0200 Subject: Check addresses associated with basic blocks --- qv4isel_x86_64.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qv4isel_x86_64.cpp b/qv4isel_x86_64.cpp index e8fe6d4517..8c5049cfea 100644 --- a/qv4isel_x86_64.cpp +++ b/qv4isel_x86_64.cpp @@ -110,6 +110,8 @@ void InstructionSelection::operator()(IR::Function *function) _code = (uchar *) ((size_t(_code) + 15) & ~15); _function->code = (void (*)(VM::Context *)) _code; _codePtr = _code; + _patches.clear(); + _addrs.clear(); int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) * sizeof(Value); locals = (locals + 15) & ~15; @@ -135,7 +137,8 @@ void InstructionSelection::operator()(IR::Function *function) QHashIterator > it(_patches); while (it.hasNext()) { it.next(); - uchar *target = _addrs[it.key()]; + uchar *target = _addrs.value(it.key()); + assert(target != 0); foreach (uchar *instr, it.value()) { amd64_patch(instr, target); } -- cgit v1.2.3 From 25176cbb1e15e602346d21c5af0d1f9d8a56f544 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 11 Jun 2012 11:51:14 +0200 Subject: Import crypto.js from the V8 benchmark tests --- tests/crypto.js | 1708 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1708 insertions(+) create mode 100644 tests/crypto.js diff --git a/tests/crypto.js b/tests/crypto.js new file mode 100644 index 0000000000..d76e22ef0d --- /dev/null +++ b/tests/crypto.js @@ -0,0 +1,1708 @@ +/* + * Copyright (c) 2003-2005 Tom Wu + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * In addition, the following condition applies: + * + * All redistributions must retain an intact copy of this copyright notice + * and disclaimer. + */ + + +// The code has been adapted for use as a benchmark by Google. +//var Crypto = new BenchmarkSuite('Crypto', 266181, [ +// new Benchmark("Encrypt", encrypt), +// new Benchmark("Decrypt", decrypt) +//]); + + +// Basic JavaScript BN library - subset useful for RSA encryption. + +// Bits per digit +var dbits; +var BI_DB; +var BI_DM; +var BI_DV; + +var BI_FP; +var BI_FV; +var BI_F1; +var BI_F2; + +// JavaScript engine analysis +var canary = 0xdeadbeefcafe; +var j_lm = ((canary&0xffffff)==0xefcafe); + +function parseInt(n,b) { + if (b == 16) return Number("0x" + String(n)) + return Number(n) +} + +// (public) Constructor +function BigInteger(a,b,c) { + this.array = new Array(); + if(a != null) + if("number" == typeof a) this.fromNumber(a,b,c); + else if(b == null && "string" != typeof a) this.fromString(a,256); + else this.fromString(a,b); +} + +// return new, unset BigInteger +function nbi() { return new BigInteger(null,uu,uu); } + +// am: Compute w_j += (x*this_i), propagate carries, +// c is initial carry, returns final carry. +// c < 3*dvalue, x < 2*dvalue, this_i < dvalue +// We need to select the fastest one that works in this environment. + +// am1: use a single mult and divide to get the high bits, +// max digit bits should be 26 because +// max internal value = 2*dvalue^2-2*dvalue (< 2^53) +function am1(i,x,w,j,c,n) { + var this_array = this.array; + var w_array = w.array; + while(--n >= 0) { + var v = x*this_array[i++]+w_array[j]+c; + c = Math.floor(v/0x4000000); + w_array[j++] = v&0x3ffffff; + } + return c; +} + +// am2 avoids a big mult-and-extract completely. +// Max digit bits should be <= 30 because we do bitwise ops +// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) +function am2(i,x,w,j,c,n) { + var this_array = this.array; + var w_array = w.array; + var xl = x&0x7fff, xh = x>>15; + while(--n >= 0) { + var l = this_array[i]&0x7fff; + var h = this_array[i++]>>15; + var m = xh*l+h*xl; + l = xl*l+((m&0x7fff)<<15)+w_array[j]+(c&0x3fffffff); + c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); + w_array[j++] = l&0x3fffffff; + } + return c; +} + +// Alternately, set max digit bits to 28 since some +// browsers slow down when dealing with 32-bit numbers. +function am3(i,x,w,j,c,n) { + var this_array = this.array; + var w_array = w.array; + + var xl = x&0x3fff, xh = x>>14; + while(--n >= 0) { + var l = this_array[i]&0x3fff; + var h = this_array[i++]>>14; + var m = xh*l+h*xl; + l = xl*l+((m&0x3fff)<<14)+w_array[j]+c; + c = (l>>28)+(m>>14)+xh*h; + w_array[j++] = l&0xfffffff; + } + return c; +} + +// This is tailored to VMs with 2-bit tagging. It makes sure +// that all the computations stay within the 29 bits available. +function am4(i,x,w,j,c,n) { + var this_array = this.array; + var w_array = w.array; + + var xl = x&0x1fff, xh = x>>13; + while(--n >= 0) { + var l = this_array[i]&0x1fff; + var h = this_array[i++]>>13; + var m = xh*l+h*xl; + l = xl*l+((m&0x1fff)<<13)+w_array[j]+c; + c = (l>>26)+(m>>13)+xh*h; + w_array[j++] = l&0x3ffffff; + } + return c; +} + +// am3/28 is best for SM, Rhino, but am4/26 is best for v8. +// Kestrel (Opera 9.5) gets its best result with am4/26. +// IE7 does 9% better with am3/28 than with am4/26. +// Firefox (SM) gets 10% faster with am3/28 than with am4/26. + +setupEngine = function(fn, bits) { + BigInteger.prototype.am = fn; + dbits = bits; + + BI_DB = dbits; + BI_DM = ((1<= 0; --i) r_array[i] = this_array[i]; + r.t = this.t; + r.s = this.s; +} + +// (protected) set from integer value x, -DV <= x < DV +function bnpFromInt(x) { + var this_array = this.array; + this.t = 1; + this.s = (x<0)?-1:0; + if(x > 0) this_array[0] = x; + else if(x < -1) this_array[0] = x+DV; + else this.t = 0; +} + +// return bigint initialized to value +function nbv(i) { var r = nbi(); r.fromInt(i); return r; } + +// (protected) set from string and radix +function bnpFromString(s,b) { + var this_array = this.array; + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 256) k = 8; // byte array + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else { this.fromRadix(s,b); return; } + this.t = 0; + this.s = 0; + var i = s.length, mi = false, sh = 0; + while(--i >= 0) { + var x = (k==8)?s[i]&0xff:intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-") mi = true; + continue; + } + mi = false; + if(sh == 0) + this_array[this.t++] = x; + else if(sh+k > BI_DB) { + this_array[this.t-1] |= (x&((1<<(BI_DB-sh))-1))<>(BI_DB-sh)); + } + else + this_array[this.t-1] |= x<= BI_DB) sh -= BI_DB; + } + if(k == 8 && (s[0]&0x80) != 0) { + this.s = -1; + if(sh > 0) this_array[this.t-1] |= ((1<<(BI_DB-sh))-1)< 0 && this_array[this.t-1] == c) --this.t; +} + +// (public) return string representation in given radix +function bnToString(b) { + var this_array = this.array; + if(this.s < 0) return "-"+this.negate().toString(b); + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else return this.toRadix(b); + var km = (1< 0) { + if(p < BI_DB && (d = this_array[i]>>p) > 0) { m = true; r = int2char(d); } + while(i >= 0) { + if(p < k) { + d = (this_array[i]&((1<>(p+=BI_DB-k); + } + else { + d = (this_array[i]>>(p-=k))&km; + if(p <= 0) { p += BI_DB; --i; } + } + if(d > 0) m = true; + if(m) r += int2char(d); + } + } + return m?r:"0"; +} + +// (public) -this +function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } + +// (public) |this| +function bnAbs() { return (this.s<0)?this.negate():this; } + +// (public) return + if this > a, - if this < a, 0 if equal +function bnCompareTo(a) { + var this_array = this.array; + var a_array = a.array; + + var r = this.s-a.s; + if(r != 0) return r; + var i = this.t; + r = i-a.t; + if(r != 0) return r; + while(--i >= 0) if((r=this_array[i]-a_array[i]) != 0) return r; + return 0; +} + +// returns bit length of the integer x +function nbits(x) { + var r = 1, t; + if((t=x>>>16) != 0) { x = t; r += 16; } + if((t=x>>8) != 0) { x = t; r += 8; } + if((t=x>>4) != 0) { x = t; r += 4; } + if((t=x>>2) != 0) { x = t; r += 2; } + if((t=x>>1) != 0) { x = t; r += 1; } + return r; +} + +// (public) return the number of bits in "this" +function bnBitLength() { + var this_array = this.array; + if(this.t <= 0) return 0; + return BI_DB*(this.t-1)+nbits(this_array[this.t-1]^(this.s&BI_DM)); +} + +// (protected) r = this << n*DB +function bnpDLShiftTo(n,r) { + var this_array = this.array; + var r_array = r.array; + var i; + for(i = this.t-1; i >= 0; --i) r_array[i+n] = this_array[i]; + for(i = n-1; i >= 0; --i) r_array[i] = 0; + r.t = this.t+n; + r.s = this.s; +} + +// (protected) r = this >> n*DB +function bnpDRShiftTo(n,r) { + var this_array = this.array; + var r_array = r.array; + for(var i = n; i < this.t; ++i) r_array[i-n] = this_array[i]; + r.t = Math.max(this.t-n,0); + r.s = this.s; +} + +// (protected) r = this << n +function bnpLShiftTo(n,r) { + var this_array = this.array; + var r_array = r.array; + var bs = n%BI_DB; + var cbs = BI_DB-bs; + var bm = (1<= 0; --i) { + r_array[i+ds+1] = (this_array[i]>>cbs)|c; + c = (this_array[i]&bm)<= 0; --i) r_array[i] = 0; + r_array[ds] = c; + r.t = this.t+ds+1; + r.s = this.s; + r.clamp(); +} + +// (protected) r = this >> n +function bnpRShiftTo(n,r) { + var this_array = this.array; + var r_array = r.array; + r.s = this.s; + var ds = Math.floor(n/BI_DB); + if(ds >= this.t) { r.t = 0; return; } + var bs = n%BI_DB; + var cbs = BI_DB-bs; + var bm = (1<>bs; + for(var i = ds+1; i < this.t; ++i) { + r_array[i-ds-1] |= (this_array[i]&bm)<>bs; + } + if(bs > 0) r_array[this.t-ds-1] |= (this.s&bm)<>= BI_DB; + } + if(a.t < this.t) { + c -= a.s; + while(i < this.t) { + c += this_array[i]; + r_array[i++] = c&BI_DM; + c >>= BI_DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c -= a_array[i]; + r_array[i++] = c&BI_DM; + c >>= BI_DB; + } + c -= a.s; + } + r.s = (c<0)?-1:0; + if(c < -1) r_array[i++] = BI_DV+c; + else if(c > 0) r_array[i++] = c; + r.t = i; + r.clamp(); +} + +// (protected) r = this * a, r != this,a (HAC 14.12) +// "this" should be the larger one if appropriate. +function bnpMultiplyTo(a,r) { + var this_array = this.array; + var r_array = r.array; + var x = this.abs(), y = a.abs(); + var y_array = y.array; + + var i = x.t; + r.t = i+y.t; + while(--i >= 0) r_array[i] = 0; + for(i = 0; i < y.t; ++i) r_array[i+x.t] = x.am(0,y_array[i],r,i,0,x.t); + r.s = 0; + r.clamp(); + if(this.s != a.s) BigInteger.ZERO.subTo(r,r); +} + +// (protected) r = this^2, r != this (HAC 14.16) +function bnpSquareTo(r) { + var x = this.abs(); + var x_array = x.array; + var r_array = r.array; + + var i = r.t = 2*x.t; + while(--i >= 0) r_array[i] = 0; + for(i = 0; i < x.t-1; ++i) { + var c = x.am(i,x_array[i],r,2*i,0,1); + if((r_array[i+x.t]+=x.am(i+1,2*x_array[i],r,2*i+1,c,x.t-i-1)) >= BI_DV) { + r_array[i+x.t] -= BI_DV; + r_array[i+x.t+1] = 1; + } + } + if(r.t > 0) r_array[r.t-1] += x.am(i,x_array[i],r,2*i,0,1); + r.s = 0; + r.clamp(); +} + +// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) +// r != q, this != m. q or r may be null. +function bnpDivRemTo(m,q,r) { + var pm = m.abs(); + if(pm.t <= 0) return; + var pt = this.abs(); + if(pt.t < pm.t) { + if(q != null) q.fromInt(0); + if(r != null) this.copyTo(r); + return; + } + if(r == null) r = nbi(); + var y = nbi(), ts = this.s, ms = m.s; + var pm_array = pm.array; + var nsh = BI_DB-nbits(pm_array[pm.t-1]); // normalize modulus + if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } + else { pm.copyTo(y); pt.copyTo(r); } + var ys = y.t; + + var y_array = y.array; + var y0 = y_array[ys-1]; + if(y0 == 0) return; + var yt = y0*(1<1)?y_array[ys-2]>>BI_F2:0); + var d1 = BI_FV/yt, d2 = (1<= 0) { + r_array[r.t++] = 1; + r.subTo(t,r); + } + BigInteger.ONE.dlShiftTo(ys,t); + t.subTo(y,y); // "negative" y so we can replace sub with am later + while(y.t < ys) y_array[y.t++] = 0; + while(--j >= 0) { + // Estimate quotient digit + var qd = (r_array[--i]==y0)?BI_DM:Math.floor(r_array[i]*d1+(r_array[i-1]+e)*d2); + if((r_array[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out + y.dlShiftTo(j,t); + r.subTo(t,r); + while(r_array[i] < --qd) r.subTo(t,r); + } + } + if(q != null) { + r.drShiftTo(ys,q); + if(ts != ms) BigInteger.ZERO.subTo(q,q); + } + r.t = ys; + r.clamp(); + if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder + if(ts < 0) BigInteger.ZERO.subTo(r,r); +} + +// (public) this mod a +function bnMod(a) { + var r = nbi(); + this.abs().divRemTo(a,null,r); + if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); + return r; +} + +// Modular reduction using "classic" algorithm +function Classic(m) { this.m = m; } +function cConvert(x) { + if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; +} +function cRevert(x) { return x; } +function cReduce(x) { x.divRemTo(this.m,null,x); } +function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } +function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +Classic.prototype.convert = cConvert; +Classic.prototype.revert = cRevert; +Classic.prototype.reduce = cReduce; +Classic.prototype.mulTo = cMulTo; +Classic.prototype.sqrTo = cSqrTo; + +// (protected) return "-1/this % 2^DB"; useful for Mont. reduction +// justification: +// xy == 1 (mod m) +// xy = 1+km +// xy(2-xy) = (1+km)(1-km) +// x[y(2-xy)] = 1-k^2m^2 +// x[y(2-xy)] == 1 (mod m^2) +// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 +// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. +// JS multiply "overflows" differently from C/C++, so care is needed here. +function bnpInvDigit() { + var this_array = this.array; + if(this.t < 1) return 0; + var x = this_array[0]; + if((x&1) == 0) return 0; + var y = x&3; // y == 1/x mod 2^2 + y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 + y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 + y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y*(2-x*y%BI_DV))%BI_DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y>0)?BI_DV-y:-y; +} + +// Montgomery reduction +function Montgomery(m) { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp&0x7fff; + this.mph = this.mp>>15; + this.um = (1<<(BI_DB-15))-1; + this.mt2 = 2*m.t; +} + +// xR mod m +function montConvert(x) { + var r = nbi(); + x.abs().dlShiftTo(this.m.t,r); + r.divRemTo(this.m,null,r); + if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); + return r; +} + +// x/R mod m +function montRevert(x) { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; +} + +// x = x/R mod m (HAC 14.32) +function montReduce(x) { + var x_array = x.array; + while(x.t <= this.mt2) // pad x so am has enough room later + x_array[x.t++] = 0; + for(var i = 0; i < this.m.t; ++i) { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x_array[i]&0x7fff; + var u0 = (j*this.mpl+(((j*this.mph+(x_array[i]>>15)*this.mpl)&this.um)<<15))&BI_DM; + // use am to combine the multiply-shift-add into one call + j = i+this.m.t; + x_array[j] += this.m.am(0,u0,x,i,0,this.m.t); + // propagate carry + while(x_array[j] >= BI_DV) { x_array[j] -= BI_DV; x_array[++j]++; } + } + x.clamp(); + x.drShiftTo(this.m.t,x); + if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = "x^2/R mod m"; x != r +function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = "xy/R mod m"; x,y != r +function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Montgomery.prototype.convert = montConvert; +Montgomery.prototype.revert = montRevert; +Montgomery.prototype.reduce = montReduce; +Montgomery.prototype.mulTo = montMulTo; +Montgomery.prototype.sqrTo = montSqrTo; + +// (protected) true iff this is even +function bnpIsEven() { + var this_array = this.array; + return ((this.t>0)?(this_array[0]&1):this.s) == 0; +} + +// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) +function bnpExp(e,z) { + if(e > 0xffffffff || e < 1) return BigInteger.ONE; + var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; + g.copyTo(r); + while(--i >= 0) { + z.sqrTo(r,r2); + if((e&(1< 0) z.mulTo(r2,g,r); + else { var t = r; r = r2; r2 = t; } + } + return z.revert(r); +} + +// (public) this^e % m, 0 <= e < 2^32 +function bnModPowInt(e,m) { + var z; + if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); + return this.exp(e,z); +} + +// protected +BigInteger.prototype.copyTo = bnpCopyTo; +BigInteger.prototype.fromInt = bnpFromInt; +BigInteger.prototype.fromString = bnpFromString; +BigInteger.prototype.clamp = bnpClamp; +BigInteger.prototype.dlShiftTo = bnpDLShiftTo; +BigInteger.prototype.drShiftTo = bnpDRShiftTo; +BigInteger.prototype.lShiftTo = bnpLShiftTo; +BigInteger.prototype.rShiftTo = bnpRShiftTo; +BigInteger.prototype.subTo = bnpSubTo; +BigInteger.prototype.multiplyTo = bnpMultiplyTo; +BigInteger.prototype.squareTo = bnpSquareTo; +BigInteger.prototype.divRemTo = bnpDivRemTo; +BigInteger.prototype.invDigit = bnpInvDigit; +BigInteger.prototype.isEven = bnpIsEven; +BigInteger.prototype.exp = bnpExp; + +// public +BigInteger.prototype.toString = bnToString; +BigInteger.prototype.negate = bnNegate; +BigInteger.prototype.abs = bnAbs; +BigInteger.prototype.compareTo = bnCompareTo; +BigInteger.prototype.bitLength = bnBitLength; +BigInteger.prototype.mod = bnMod; +BigInteger.prototype.modPowInt = bnModPowInt; + +// "constants" +BigInteger.ZERO = nbv(0); +BigInteger.ONE = nbv(1); +// Copyright (c) 2005 Tom Wu +// All Rights Reserved. +// See "LICENSE" for details. + +// Extended JavaScript BN functions, required for RSA private ops. + +// (public) +function bnClone() { var r = nbi(); this.copyTo(r); return r; } + +// (public) return value as integer +function bnIntValue() { + var this_array = this.array; + if(this.s < 0) { + if(this.t == 1) return this_array[0]-BI_DV; + else if(this.t == 0) return -1; + } + else if(this.t == 1) return this_array[0]; + else if(this.t == 0) return 0; + // assumes 16 < DB < 32 + return ((this_array[1]&((1<<(32-BI_DB))-1))<>24; +} + +// (public) return value as short (assumes DB>=16) +function bnShortValue() { + var this_array = this.array; + return (this.t==0)?this.s:(this_array[0]<<16)>>16; +} + +// (protected) return x s.t. r^x < DV +function bnpChunkSize(r) { return Math.floor(Math.LN2*BI_DB/Math.log(r)); } + +// (public) 0 if this == 0, 1 if this > 0 +function bnSigNum() { + var this_array = this.array; + if(this.s < 0) return -1; + else if(this.t <= 0 || (this.t == 1 && this_array[0] <= 0)) return 0; + else return 1; +} + +// (protected) convert to radix string +function bnpToRadix(b) { + if(b == null) b = 10; + if(this.signum() == 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b,cs); + var d = nbv(a), y = nbi(), z = nbi(), r = ""; + this.divRemTo(d,y,z); + while(y.signum() > 0) { + r = (a+z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d,y,z); + } + return z.intValue().toString(b) + r; +} + +// (protected) convert from radix string +function bnpFromRadix(s,b) { + this.fromInt(0); + if(b == null) b = 10; + var cs = this.chunkSize(b); + var d = Math.pow(b,cs), mi = false, j = 0, w = 0; + for(var i = 0; i < s.length; ++i) { + var x = intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-" && this.signum() == 0) mi = true; + continue; + } + w = b*w+x; + if(++j >= cs) { + this.dMultiply(d); + this.dAddOffset(w,0); + j = 0; + w = 0; + } + } + if(j > 0) { + this.dMultiply(Math.pow(b,j)); + this.dAddOffset(w,0); + } + if(mi) BigInteger.ZERO.subTo(this,this); +} + +// (protected) alternate constructor +function bnpFromNumber(a,b,c) { + if("number" == typeof b) { + // new BigInteger(int,int,RNG) + if(a < 2) this.fromInt(1); + else { + this.fromNumber(a,c); + if(!this.testBit(a-1)) // force MSB set + this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); + if(this.isEven()) this.dAddOffset(1,0); // force odd + while(!this.isProbablePrime(b)) { + this.dAddOffset(2,0); + if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); + } + } + } + else { + // new BigInteger(int,RNG) + var x = new Array(), t = a&7; + x.length = (a>>3)+1; + b.nextBytes(x); + if(t > 0) x[0] &= ((1< 0) { + if(p < BI_DB && (d = this_array[i]>>p) != (this.s&BI_DM)>>p) + r[k++] = d|(this.s<<(BI_DB-p)); + while(i >= 0) { + if(p < 8) { + d = (this_array[i]&((1<>(p+=BI_DB-8); + } + else { + d = (this_array[i]>>(p-=8))&0xff; + if(p <= 0) { p += BI_DB; --i; } + } + if((d&0x80) != 0) d |= -256; + if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; + if(k > 0 || d != this.s) r[k++] = d; + } + } + return r; +} + +function bnEquals(a) { return(this.compareTo(a)==0); } +function bnMin(a) { return(this.compareTo(a)<0)?this:a; } +function bnMax(a) { return(this.compareTo(a)>0)?this:a; } + +// (protected) r = this op a (bitwise) +function bnpBitwiseTo(a,op,r) { + var this_array = this.array; + var a_array = a.array; + var r_array = r.array; + var i, f, m = Math.min(a.t,this.t); + for(i = 0; i < m; ++i) r_array[i] = op(this_array[i],a_array[i]); + if(a.t < this.t) { + f = a.s&BI_DM; + for(i = m; i < this.t; ++i) r_array[i] = op(this_array[i],f); + r.t = this.t; + } + else { + f = this.s&BI_DM; + for(i = m; i < a.t; ++i) r_array[i] = op(f,a_array[i]); + r.t = a.t; + } + r.s = op(this.s,a.s); + r.clamp(); +} + +// (public) this & a +function op_and(x,y) { return x&y; } +function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } + +// (public) this | a +function op_or(x,y) { return x|y; } +function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } + +// (public) this ^ a +function op_xor(x,y) { return x^y; } +function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } + +// (public) this & ~a +function op_andnot(x,y) { return x&~y; } +function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } + +// (public) ~this +function bnNot() { + var this_array = this.array; + var r = nbi(); + var r_array = r.array; + + for(var i = 0; i < this.t; ++i) r_array[i] = BI_DM&~this_array[i]; + r.t = this.t; + r.s = ~this.s; + return r; +} + +// (public) this << n +function bnShiftLeft(n) { + var r = nbi(); + if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); + return r; +} + +// (public) this >> n +function bnShiftRight(n) { + var r = nbi(); + if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); + return r; +} + +// return index of lowest 1-bit in x, x < 2^31 +function lbit(x) { + if(x == 0) return -1; + var r = 0; + if((x&0xffff) == 0) { x >>= 16; r += 16; } + if((x&0xff) == 0) { x >>= 8; r += 8; } + if((x&0xf) == 0) { x >>= 4; r += 4; } + if((x&3) == 0) { x >>= 2; r += 2; } + if((x&1) == 0) ++r; + return r; +} + +// (public) returns index of lowest 1-bit (or -1 if none) +function bnGetLowestSetBit() { + var this_array = this.array; + for(var i = 0; i < this.t; ++i) + if(this_array[i] != 0) return i*BI_DB+lbit(this_array[i]); + if(this.s < 0) return this.t*BI_DB; + return -1; +} + +// return number of 1 bits in x +function cbit(x) { + var r = 0; + while(x != 0) { x &= x-1; ++r; } + return r; +} + +// (public) return number of set bits +function bnBitCount() { + var r = 0, x = this.s&BI_DM; + for(var i = 0; i < this.t; ++i) r += cbit(this_array[i]^x); + return r; +} + +// (public) true iff nth bit is set +function bnTestBit(n) { + var this_array = this.array; + var j = Math.floor(n/BI_DB); + if(j >= this.t) return(this.s!=0); + return((this_array[j]&(1<<(n%BI_DB)))!=0); +} + +// (protected) this op (1<>= BI_DB; + } + if(a.t < this.t) { + c += a.s; + while(i < this.t) { + c += this_array[i]; + r_array[i++] = c&BI_DM; + c >>= BI_DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c += a_array[i]; + r_array[i++] = c&BI_DM; + c >>= BI_DB; + } + c += a.s; + } + r.s = (c<0)?-1:0; + if(c > 0) r_array[i++] = c; + else if(c < -1) r_array[i++] = BI_DV+c; + r.t = i; + r.clamp(); +} + +// (public) this + a +function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } + +// (public) this - a +function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } + +// (public) this * a +function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } + +// (public) this / a +function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } + +// (public) this % a +function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } + +// (public) [this/a,this%a] +function bnDivideAndRemainder(a) { + var q = nbi(), r = nbi(); + this.divRemTo(a,q,r); + return new Array(q,r); +} + +// (protected) this *= n, this >= 0, 1 < n < DV +function bnpDMultiply(n) { + var this_array = this.array; + this_array[this.t] = this.am(0,n-1,this,0,0,this.t); + ++this.t; + this.clamp(); +} + +// (protected) this += n << w words, this >= 0 +function bnpDAddOffset(n,w) { + var this_array = this.array; + while(this.t <= w) this_array[this.t++] = 0; + this_array[w] += n; + while(this_array[w] >= BI_DV) { + this_array[w] -= BI_DV; + if(++w >= this.t) this_array[this.t++] = 0; + ++this_array[w]; + } +} + +// A "null" reducer +function NullExp() {} +function nNop(x) { return x; } +function nMulTo(x,y,r) { x.multiplyTo(y,r); } +function nSqrTo(x,r) { x.squareTo(r); } + +NullExp.prototype.convert = nNop; +NullExp.prototype.revert = nNop; +NullExp.prototype.mulTo = nMulTo; +NullExp.prototype.sqrTo = nSqrTo; + +// (public) this^e +function bnPow(e) { return this.exp(e,new NullExp()); } + +// (protected) r = lower n words of "this * a", a.t <= n +// "this" should be the larger one if appropriate. +function bnpMultiplyLowerTo(a,n,r) { + var r_array = r.array; + var a_array = a.array; + var i = Math.min(this.t+a.t,n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while(i > 0) r_array[--i] = 0; + var j; + for(j = r.t-this.t; i < j; ++i) r_array[i+this.t] = this.am(0,a_array[i],r,i,0,this.t); + for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a_array[i],r,i,0,n-i); + r.clamp(); +} + +// (protected) r = "this * a" without lower n words, n > 0 +// "this" should be the larger one if appropriate. +function bnpMultiplyUpperTo(a,n,r) { + var r_array = r.array; + var a_array = a.array; + --n; + var i = r.t = this.t+a.t-n; + r.s = 0; // assumes a,this >= 0 + while(--i >= 0) r_array[i] = 0; + for(i = Math.max(n-this.t,0); i < a.t; ++i) + r_array[this.t+i-n] = this.am(n-i,a_array[i],r,0,0,this.t+i-n); + r.clamp(); + r.drShiftTo(1,r); +} + +// Barrett modular reduction +function Barrett(m) { + // setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2*m.t,this.r2); + this.mu = this.r2.divide(m); + this.m = m; +} + +function barrettConvert(x) { + if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); + else if(x.compareTo(this.m) < 0) return x; + else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } +} + +function barrettRevert(x) { return x; } + +// x = x mod m (HAC 14.42) +function barrettReduce(x) { + x.drShiftTo(this.m.t-1,this.r2); + if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } + this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); + this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); + while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); + x.subTo(this.r2,x); + while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = x^2 mod m; x != r +function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = x*y mod m; x,y != r +function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Barrett.prototype.convert = barrettConvert; +Barrett.prototype.revert = barrettRevert; +Barrett.prototype.reduce = barrettReduce; +Barrett.prototype.mulTo = barrettMulTo; +Barrett.prototype.sqrTo = barrettSqrTo; + +// (public) this^e % m (HAC 14.85) +function bnModPow(e,m) { + var e_array = e.array; + var i = e.bitLength(), k, r = nbv(1), z; + if(i <= 0) return r; + else if(i < 18) k = 1; + else if(i < 48) k = 3; + else if(i < 144) k = 4; + else if(i < 768) k = 5; + else k = 6; + if(i < 8) + z = new Classic(m); + else if(m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); + + // precomputation + var g = new Array(), n = 3, k1 = k-1, km = (1< 1) { + var g2 = nbi(); + z.sqrTo(g[1],g2); + while(n <= km) { + g[n] = nbi(); + z.mulTo(g2,g[n-2],g[n]); + n += 2; + } + } + + var j = e.t-1, w, is1 = true, r2 = nbi(), t; + i = nbits(e_array[j])-1; + while(j >= 0) { + if(i >= k1) w = (e_array[j]>>(i-k1))&km; + else { + w = (e_array[j]&((1<<(i+1))-1))<<(k1-i); + if(j > 0) w |= e_array[j-1]>>(BI_DB+i-k1); + } + + n = k; + while((w&1) == 0) { w >>= 1; --n; } + if((i -= n) < 0) { i += BI_DB; --j; } + if(is1) { // ret == 1, don't bother squaring or multiplying it + g[w].copyTo(r); + is1 = false; + } + else { + while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } + if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } + z.mulTo(r2,g[w],r); + } + + while(j >= 0 && (e_array[j]&(1< 0) { + x.rShiftTo(g,x); + y.rShiftTo(g,y); + } + while(x.signum() > 0) { + if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); + if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); + if(x.compareTo(y) >= 0) { + x.subTo(y,x); + x.rShiftTo(1,x); + } + else { + y.subTo(x,y); + y.rShiftTo(1,y); + } + } + if(g > 0) y.lShiftTo(g,y); + return y; +} + +// (protected) this % n, n < 2^26 +function bnpModInt(n) { + var this_array = this.array; + if(n <= 0) return 0; + var d = BI_DV%n, r = (this.s<0)?n-1:0; + if(this.t > 0) + if(d == 0) r = this_array[0]%n; + else for(var i = this.t-1; i >= 0; --i) r = (d*r+this_array[i])%n; + return r; +} + +// (public) 1/this % m (HAC 14.61) +function bnModInverse(m) { + var ac = m.isEven(); + if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; + var u = m.clone(), v = this.clone(); + var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); + while(u.signum() != 0) { + while(u.isEven()) { + u.rShiftTo(1,u); + if(ac) { + if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } + a.rShiftTo(1,a); + } + else if(!b.isEven()) b.subTo(m,b); + b.rShiftTo(1,b); + } + while(v.isEven()) { + v.rShiftTo(1,v); + if(ac) { + if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } + c.rShiftTo(1,c); + } + else if(!d.isEven()) d.subTo(m,d); + d.rShiftTo(1,d); + } + if(u.compareTo(v) >= 0) { + u.subTo(v,u); + if(ac) a.subTo(c,a); + b.subTo(d,b); + } + else { + v.subTo(u,v); + if(ac) c.subTo(a,c); + d.subTo(b,d); + } + } + if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; + if(d.compareTo(m) >= 0) return d.subtract(m); + if(d.signum() < 0) d.addTo(m,d); else return d; + if(d.signum() < 0) return d.add(m); else return d; +} + +var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509]; +var lplim = (1<<26)/lowprimes[lowprimes.length-1]; + +// (public) test primality with certainty >= 1-.5^t +function bnIsProbablePrime(t) { + var i, x = this.abs(); + var x_array = x.array; + if(x.t == 1 && x_array[0] <= lowprimes[lowprimes.length-1]) { + for(i = 0; i < lowprimes.length; ++i) + if(x_array[0] == lowprimes[i]) return true; + return false; + } + if(x.isEven()) return false; + i = 1; + while(i < lowprimes.length) { + var m = lowprimes[i], j = i+1; + while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; + m = x.modInt(m); + while(i < j) if(m%lowprimes[i++] == 0) return false; + } + return x.millerRabin(t); +} + +// (protected) true if probably prime (HAC 4.24, Miller-Rabin) +function bnpMillerRabin(t) { + var n1 = this.subtract(BigInteger.ONE); + var k = n1.getLowestSetBit(); + if(k <= 0) return false; + var r = n1.shiftRight(k); + t = (t+1)>>1; + if(t > lowprimes.length) t = lowprimes.length; + var a = nbi(); + for(var i = 0; i < t; ++i) { + a.fromInt(lowprimes[i]); + var y = a.modPow(r,this); + if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { + var j = 1; + while(j++ < k && y.compareTo(n1) != 0) { + y = y.modPowInt(2,this); + if(y.compareTo(BigInteger.ONE) == 0) return false; + } + if(y.compareTo(n1) != 0) return false; + } + } + return true; +} + +// protected +BigInteger.prototype.chunkSize = bnpChunkSize; +BigInteger.prototype.toRadix = bnpToRadix; +BigInteger.prototype.fromRadix = bnpFromRadix; +BigInteger.prototype.fromNumber = bnpFromNumber; +BigInteger.prototype.bitwiseTo = bnpBitwiseTo; +BigInteger.prototype.changeBit = bnpChangeBit; +BigInteger.prototype.addTo = bnpAddTo; +BigInteger.prototype.dMultiply = bnpDMultiply; +BigInteger.prototype.dAddOffset = bnpDAddOffset; +BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; +BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; +BigInteger.prototype.modInt = bnpModInt; +BigInteger.prototype.millerRabin = bnpMillerRabin; + +// public +BigInteger.prototype.clone = bnClone; +BigInteger.prototype.intValue = bnIntValue; +BigInteger.prototype.byteValue = bnByteValue; +BigInteger.prototype.shortValue = bnShortValue; +BigInteger.prototype.signum = bnSigNum; +BigInteger.prototype.toByteArray = bnToByteArray; +BigInteger.prototype.equals = bnEquals; +BigInteger.prototype.min = bnMin; +BigInteger.prototype.max = bnMax; +BigInteger.prototype.and = bnAnd; +BigInteger.prototype.or = bnOr; +BigInteger.prototype.xor = bnXor; +BigInteger.prototype.andNot = bnAndNot; +BigInteger.prototype.not = bnNot; +BigInteger.prototype.shiftLeft = bnShiftLeft; +BigInteger.prototype.shiftRight = bnShiftRight; +BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; +BigInteger.prototype.bitCount = bnBitCount; +BigInteger.prototype.testBit = bnTestBit; +BigInteger.prototype.setBit = bnSetBit; +BigInteger.prototype.clearBit = bnClearBit; +BigInteger.prototype.flipBit = bnFlipBit; +BigInteger.prototype.add = bnAdd; +BigInteger.prototype.subtract = bnSubtract; +BigInteger.prototype.multiply = bnMultiply; +BigInteger.prototype.divide = bnDivide; +BigInteger.prototype.remainder = bnRemainder; +BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; +BigInteger.prototype.modPow = bnModPow; +BigInteger.prototype.modInverse = bnModInverse; +BigInteger.prototype.pow = bnPow; +BigInteger.prototype.gcd = bnGCD; +BigInteger.prototype.isProbablePrime = bnIsProbablePrime; + +// BigInteger interfaces not implemented in jsbn: + +// BigInteger(int signum, byte[] magnitude) +// double doubleValue() +// float floatValue() +// int hashCode() +// long longValue() +// static BigInteger valueOf(long val) +// prng4.js - uses Arcfour as a PRNG + +function Arcfour() { + this.i = 0; + this.j = 0; + this.S = new Array(); +} + +// Initialize arcfour context from key, an array of ints, each from [0..255] +function ARC4init(key) { + var i, j, t; + for(i = 0; i < 256; ++i) + this.S[i] = i; + j = 0; + for(i = 0; i < 256; ++i) { + j = (j + this.S[i] + key[i % key.length]) & 255; + t = this.S[i]; + this.S[i] = this.S[j]; + this.S[j] = t; + } + this.i = 0; + this.j = 0; +} + +function ARC4next() { + var t; + this.i = (this.i + 1) & 255; + this.j = (this.j + this.S[this.i]) & 255; + t = this.S[this.i]; + this.S[this.i] = this.S[this.j]; + this.S[this.j] = t; + return this.S[(t + this.S[this.i]) & 255]; +} + +Arcfour.prototype.init = ARC4init; +Arcfour.prototype.next = ARC4next; + +// Plug in your RNG constructor here +function prng_newstate() { + return new Arcfour(); +} + +// Pool size must be a multiple of 4 and greater than 32. +// An array of bytes the size of the pool will be passed to init() +var rng_psize = 256; +// Random number generator - requires a PRNG backend, e.g. prng4.js + +// For best results, put code like +// +// in your main HTML document. + +var rng_state; +var rng_pool; +var rng_pptr; + +// Mix in a 32-bit integer into the pool +function rng_seed_int(x) { + rng_pool[rng_pptr++] ^= x & 255; + rng_pool[rng_pptr++] ^= (x >> 8) & 255; + rng_pool[rng_pptr++] ^= (x >> 16) & 255; + rng_pool[rng_pptr++] ^= (x >> 24) & 255; + if(rng_pptr >= rng_psize) rng_pptr -= rng_psize; +} + +// Mix in the current time (w/milliseconds) into the pool +function rng_seed_time() { + // Use pre-computed date to avoid making the benchmark + // results dependent on the current date. + rng_seed_int(1122926989487); +} + +// Initialize the pool with junk if needed. +if(rng_pool == null) { + rng_pool = new Array(); + rng_pptr = 0; + var t; + while(rng_pptr < rng_psize) { // extract some randomness from Math.random() + t = Math.floor(65536 * Math.random()); + rng_pool[rng_pptr++] = t >>> 8; + rng_pool[rng_pptr++] = t & 255; + } + rng_pptr = 0; + rng_seed_time(); + //rng_seed_int(window.screenX); + //rng_seed_int(window.screenY); +} + +function rng_get_byte() { + if(rng_state == null) { + rng_seed_time(); + rng_state = prng_newstate(); + rng_state.init(rng_pool); + for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) + rng_pool[rng_pptr] = 0; + rng_pptr = 0; + //rng_pool = null; + } + // TODO: allow reseeding after first request + return rng_state.next(); +} + +function rng_get_bytes(ba) { + var i; + for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte(); +} + +function SecureRandom() {} + +SecureRandom.prototype.nextBytes = rng_get_bytes; +// Depends on jsbn.js and rng.js +var uu +// convert a (hex) string to a bignum object +function parseBigInt(str,r) { + return new BigInteger(str,r,uu); +} + +function linebrk(s,n) { + var ret = ""; + var i = 0; + while(i + n < s.length) { + ret += s.substring(i,i+n) + "\n"; + i += n; + } + return ret + s.substring(i,s.length); +} + +function byte2Hex(b) { + if(b < 0x10) + return "0" + b.toString(16); + else + return b.toString(16); +} + +// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint +function pkcs1pad2(s,n) { + if(n < s.length + 11) { + print("Message too long for RSA"); + return null; + } + var ba = new Array(); + var i = s.length - 1; + while(i >= 0 && n > 0) ba[--n] = s.charCodeAt(i--); + ba[--n] = 0; + var rng = new SecureRandom(); + var x = new Array(); + while(n > 2) { // random non-zero pad + x[0] = 0; + while(x[0] == 0) rng.nextBytes(x); + ba[--n] = x[0]; + } + ba[--n] = 2; + ba[--n] = 0; + return new BigInteger(ba,uu,uu); +} + +// "empty" RSA key constructor +function RSAKey() { + this.n = null; + this.e = 0; + this.d = null; + this.p = null; + this.q = null; + this.dmp1 = null; + this.dmq1 = null; + this.coeff = null; +} + +// Set the public key fields N and e from hex strings +function RSASetPublic(N,E) { + if(N != null && E != null && N.length > 0 && E.length > 0) { + this.n = parseBigInt(N,16); + this.e = parseInt(E,16); + } + else + print("Invalid RSA public key"); +} + +// Perform raw public operation on "x": return x^e (mod n) +function RSADoPublic(x) { + return x.modPowInt(this.e, this.n); +} + +// Return the PKCS#1 RSA encryption of "text" as an even-length hex string +function RSAEncrypt(text) { + var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3); + if(m == null) return null; + var c = this.doPublic(m); + if(c == null) return null; + var h = c.toString(16); + if((h.length & 1) == 0) return h; else return "0" + h; +} + +// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string +//function RSAEncryptB64(text) { +// var h = this.encrypt(text); +// if(h) return hex2b64(h); else return null; +//} + +// protected +RSAKey.prototype.doPublic = RSADoPublic; + +// public +RSAKey.prototype.setPublic = RSASetPublic; +RSAKey.prototype.encrypt = RSAEncrypt; +//RSAKey.prototype.encrypt_b64 = RSAEncryptB64; +// Depends on rsa.js and jsbn2.js + +// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext +function pkcs1unpad2(d,n) { + var b = d.toByteArray(); + var i = 0; + while(i < b.length && b[i] == 0) ++i; + if(b.length-i != n-1 || b[i] != 2) + return null; + ++i; + while(b[i] != 0) + if(++i >= b.length) return null; + var ret = ""; + while(++i < b.length) + ret += String.fromCharCode(b[i]); + return ret; +} + +// Set the private key fields N, e, and d from hex strings +function RSASetPrivate(N,E,D) { + if(N != null && E != null && N.length > 0 && E.length > 0) { + this.n = parseBigInt(N,16); + this.e = parseInt(E,16); + this.d = parseBigInt(D,16); + } + else + print("Invalid RSA private key"); +} + +// Set the private key fields N, e, d and CRT params from hex strings +function RSASetPrivateEx(N,E,D,P,Q,DP,DQ,C) { + if(N != null && E != null && N.length > 0 && E.length > 0) { + this.n = parseBigInt(N,16); + this.e = parseInt(E,16); + this.d = parseBigInt(D,16); + this.p = parseBigInt(P,16); + this.q = parseBigInt(Q,16); + this.dmp1 = parseBigInt(DP,16); + this.dmq1 = parseBigInt(DQ,16); + this.coeff = parseBigInt(C,16); + } + else + print("Invalid RSA private key"); +} + +// Generate a new random private key B bits long, using public expt E +function RSAGenerate(B,E) { + var rng = new SecureRandom(); + var qs = B>>1; + this.e = parseInt(E,16); + var ee = new BigInteger(E,16,uu); + for(;;) { + for(;;) { + this.p = new BigInteger(B-qs,1,rng); + if(this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break; + } + for(;;) { + this.q = new BigInteger(qs,1,rng); + if(this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break; + } + if(this.p.compareTo(this.q) <= 0) { + var t = this.p; + this.p = this.q; + this.q = t; + } + var p1 = this.p.subtract(BigInteger.ONE); + var q1 = this.q.subtract(BigInteger.ONE); + var phi = p1.multiply(q1); + if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) { + this.n = this.p.multiply(this.q); + this.d = ee.modInverse(phi); + this.dmp1 = this.d.mod(p1); + this.dmq1 = this.d.mod(q1); + this.coeff = this.q.modInverse(this.p); + break; + } + } +} + +// Perform raw private operation on "x": return x^d (mod n) +function RSADoPrivate(x) { + if(this.p == null || this.q == null) + return x.modPow(this.d, this.n); + + // TODO: re-calculate any missing CRT params + var xp = x.mod(this.p).modPow(this.dmp1, this.p); + var xq = x.mod(this.q).modPow(this.dmq1, this.q); + + while(xp.compareTo(xq) < 0) + xp = xp.add(this.p); + return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq); +} + +// Return the PKCS#1 RSA decryption of "ctext". +// "ctext" is an even-length hex string and the output is a plain string. +function RSADecrypt(ctext) { + var c = parseBigInt(ctext, 16); + var m = this.doPrivate(c); + if(m == null) return null; + return pkcs1unpad2(m, (this.n.bitLength()+7)>>3); +} + +// Return the PKCS#1 RSA decryption of "ctext". +// "ctext" is a Base64-encoded string and the output is a plain string. +//function RSAB64Decrypt(ctext) { +// var h = b64tohex(ctext); +// if(h) return this.decrypt(h); else return null; +//} + +// protected +RSAKey.prototype.doPrivate = RSADoPrivate; + +// public +RSAKey.prototype.setPrivate = RSASetPrivate; +RSAKey.prototype.setPrivateEx = RSASetPrivateEx; +RSAKey.prototype.generate = RSAGenerate; +RSAKey.prototype.decrypt = RSADecrypt; +//RSAKey.prototype.b64_decrypt = RSAB64Decrypt; + + +nValue="a5261939975948bb7a58dffe5ff54e65f0498f9175f5a09288810b8975871e99af3b5dd94057b0fc07535f5f97444504fa35169d461d0d30cf0192e307727c065168c788771c561a9400fb49175e9e6aa4e23fe11af69e9412dd23b0cb6684c4c2429bce139e848ab26d0829073351f4acd36074eafd036a5eb83359d2a698d3"; +eValue="10001"; +dValue="8e9912f6d3645894e8d38cb58c0db81ff516cf4c7e5a14c7f1eddb1459d2cded4d8d293fc97aee6aefb861859c8b6a3d1dfe710463e1f9ddc72048c09751971c4a580aa51eb523357a3cc48d31cfad1d4a165066ed92d4748fb6571211da5cb14bc11b6e2df7c1a559e6d5ac1cd5c94703a22891464fba23d0d965086277a161"; +pValue="d090ce58a92c75233a6486cb0a9209bf3583b64f540c76f5294bb97d285eed33aec220bde14b2417951178ac152ceab6da7090905b478195498b352048f15e7d"; +qValue="cab575dc652bb66df15a0359609d51d1db184750c00c6698b90ef3465c99655103edbf0d54c56aec0ce3c4d22592338092a126a0cc49f65a4a30d222b411e58f"; +dmp1Value="1a24bca8e273df2f0e47c199bbf678604e7df7215480c77c8db39f49b000ce2cf7500038acfff5433b7d582a01f1826e6f4d42e1c57f5e1fef7b12aabc59fd25"; +dmq1Value="3d06982efbbe47339e1f6d36b1216b8a741d410b0c662f54f7118b27b9a4ec9d914337eb39841d8666f3034408cf94f5b62f11c402fc994fe15a05493150d9fd"; +coeffValue="3a3e731acd8960b7ff9eb81a7ff93bd1cfa74cbd56987db58b4594fb09c09084db1734c8143f98b602b981aaa9243ca28deb69b5b280ee8dcee0fd2625e53250"; + +setupEngine(am3, 28); + +var TEXT = "The quick brown fox jumped over the extremely lazy frog! " + + "Now is the time for all good men to come to the party."; +var encrypted; + +function encrypt() { + var RSA = new RSAKey(); + RSA.setPublic(nValue, eValue); + RSA.setPrivateEx(nValue, eValue, dValue, pValue, qValue, dmp1Value, dmq1Value, coeffValue); + encrypted = RSA.encrypt(TEXT); +} + +function decrypt() { + var RSA = new RSAKey(); + RSA.setPublic(nValue, eValue); + RSA.setPrivateEx(nValue, eValue, dValue, pValue, qValue, dmp1Value, dmq1Value, coeffValue); + var decrypted = RSA.decrypt(encrypted); + if (decrypted != TEXT) { + throw new Error("Crypto operation failed"); + } +} + +var d1 = +new Date +encrypt() +decrypt() +print("done in", new Date - d1) -- cgit v1.2.3 From 416167c0af6808f374c5dd0ca6696cbeb69a2acb Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 8 Jun 2012 20:09:13 +0100 Subject: Make IR types stricter We can always loosen these again if we need to. --- qv4codegen.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ qv4ir.cpp | 63 ++++++-------------------------------------------------- qv4ir_p.h | 44 +++++++++++++++++++-------------------- 3 files changed, 87 insertions(+), 85 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 277bfd126f..eb7037e369 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -317,7 +318,7 @@ void Codegen::leaveLoop() IR::Expr *Codegen::member(IR::Expr *base, const QString *name) { if (base->asTemp() /*|| base->asName()*/) - return _block->MEMBER(base, name); + return _block->MEMBER(base->asTemp(), name); else { const unsigned t = _block->newTemp(); move(_block->TEMP(t), base); @@ -339,7 +340,8 @@ IR::Expr *Codegen::subscript(IR::Expr *base, IR::Expr *index) index = _block->TEMP(t); } - return _block->SUBSCRIPT(base, index); + assert(base->asTemp() && index->asTemp()); + return _block->SUBSCRIPT(base->asTemp(), index->asTemp()); } IR::Expr *Codegen::argument(IR::Expr *expr) @@ -359,24 +361,75 @@ IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr) move(_block->TEMP(t), expr); expr = _block->TEMP(t); } - return _block->UNOP(op, expr); + assert(expr->asTemp()); + return _block->UNOP(op, expr->asTemp()); } IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) { - if (left && ! left->asTemp()) { + if (IR::Const *c1 = left->asConst()) { + if (IR::Const *c2 = right->asConst()) { + const IR::Type ty = IR::Binop::typeForOp(op, left, right); + + switch (op) { + case IR::OpAdd: return _block->CONST(ty, c1->value + c2->value); + case IR::OpAnd: return _block->CONST(ty, c1->value ? c2->value : 0); + case IR::OpBitAnd: return _block->CONST(ty, int(c1->value) & int(c2->value)); + case IR::OpBitOr: return _block->CONST(ty, int(c1->value) | int(c2->value)); + case IR::OpBitXor: return _block->CONST(ty, int(c1->value) ^ int(c2->value)); + case IR::OpDiv: return _block->CONST(ty, c1->value / c2->value); + case IR::OpEqual: return _block->CONST(ty, c1->value == c2->value); + case IR::OpGe: return _block->CONST(ty, c1->value >= c2->value); + case IR::OpGt: return _block->CONST(ty, c1->value > c2->value); + case IR::OpLe: return _block->CONST(ty, c1->value <= c2->value); + case IR::OpLShift: return _block->CONST(ty, int(c1->value) << int(c2->value)); + case IR::OpLt: return _block->CONST(ty, c1->value < c2->value); + case IR::OpMod: return _block->CONST(ty, ::fmod(c1->value, c2->value)); + case IR::OpMul: return _block->CONST(ty, c1->value * c2->value); + case IR::OpNotEqual: return _block->CONST(ty, c1->value != c2->value); + case IR::OpOr: return _block->CONST(ty, c1->value ? c1->value : c2->value); + case IR::OpRShift: return _block->CONST(ty, int(c1->value) >> int(c2->value)); + case IR::OpStrictEqual: return _block->CONST(ty, c1->value == c2->value); + case IR::OpStrictNotEqual: return _block->CONST(ty, c1->value != c2->value); + case IR::OpSub: return _block->CONST(ty, c1->value - c2->value); + case IR::OpURShift: return _block->CONST(ty, unsigned(c1->value) >> int(c2->value)); + + case IR::OpInstanceof: + case IR::OpIn: + assert(!"unreachabe"); + break; + + case IR::OpIfTrue: // unary ops + case IR::OpNot: + case IR::OpUMinus: + case IR::OpUPlus: + case IR::OpCompl: + case IR::OpInvalid: + break; + } + } + } else if (op == IR::OpAdd) { + if (IR::String *s1 = left->asString()) { + if (IR::String *s2 = right->asString()) { + return _block->STRING(_function->newString(*s1->value + *s2->value)); + } + } + } + + if (!left->asTemp()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), left); left = _block->TEMP(t); } - if (right && ! right->asTemp()) { + if (!right->asTemp()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), right); right = _block->TEMP(t); } - return _block->BINOP(op, left, right); + assert(left->asTemp() && right->asTemp()); + return _block->BINOP(op, left->asTemp(), right->asTemp()); } IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args) diff --git a/qv4ir.cpp b/qv4ir.cpp index 00779a9fe8..c915c9ad90 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -561,66 +561,15 @@ Closure *BasicBlock::CLOSURE(Function *function) return clos; } -Expr *BasicBlock::UNOP(AluOp op, Expr *expr) +Expr *BasicBlock::UNOP(AluOp op, Temp *expr) { Unop *e = function->New(); e->init(op, expr); return e; } -Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) -{ - if (left && right) { - if (Const *c1 = left->asConst()) { - if (Const *c2 = right->asConst()) { - const IR::Type ty = Binop::typeForOp(op, left, right); - - switch (op) { - case OpAdd: return CONST(ty, c1->value + c2->value); - case OpAnd: return CONST(ty, c1->value ? c2->value : 0); - case OpBitAnd: return CONST(ty, int(c1->value) & int(c2->value)); - case OpBitOr: return CONST(ty, int(c1->value) | int(c2->value)); - case OpBitXor: return CONST(ty, int(c1->value) ^ int(c2->value)); - case OpDiv: return CONST(ty, c1->value / c2->value); - case OpEqual: return CONST(ty, c1->value == c2->value); - case OpGe: return CONST(ty, c1->value >= c2->value); - case OpGt: return CONST(ty, c1->value > c2->value); - case OpLe: return CONST(ty, c1->value <= c2->value); - case OpLShift: return CONST(ty, int(c1->value) << int(c2->value)); - case OpLt: return CONST(ty, c1->value < c2->value); - case OpMod: return CONST(ty, ::fmod(c1->value, c2->value)); - case OpMul: return CONST(ty, c1->value * c2->value); - case OpNotEqual: return CONST(ty, c1->value != c2->value); - case OpOr: return CONST(ty, c1->value ? c1->value : c2->value); - case OpRShift: return CONST(ty, int(c1->value) >> int(c2->value)); - case OpStrictEqual: return CONST(ty, c1->value == c2->value); - case OpStrictNotEqual: return CONST(ty, c1->value != c2->value); - case OpSub: return CONST(ty, c1->value - c2->value); - case OpURShift: return CONST(ty, unsigned(c1->value) >> int(c2->value)); - - case OpInstanceof: - case OpIn: - assert(!"unreachabe"); - break; - - case OpIfTrue: // unary ops - case OpNot: - case OpUMinus: - case OpUPlus: - case OpCompl: - case OpInvalid: - break; - } - } - } else if (op == OpAdd) { - if (String *s1 = left->asString()) { - if (String *s2 = right->asString()) { - return STRING(function->newString(*s1->value + *s2->value)); - } - } - } - } - +Expr *BasicBlock::BINOP(AluOp op, Temp *left, Temp *right) +{ Binop *e = function->New(); e->init(op, left, right); return e; @@ -640,14 +589,14 @@ Expr *BasicBlock::NEW(Expr *base, ExprList *args) return e; } -Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index) +Expr *BasicBlock::SUBSCRIPT(Temp *base, Temp *index) { Subscript *e = function->New(); e->init(base, index); return e; } -Expr *BasicBlock::MEMBER(Expr *base, const QString *name) +Expr *BasicBlock::MEMBER(Temp *base, const QString *name) { Member*e = function->New(); e->init(base, name); @@ -745,7 +694,7 @@ Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) return s; } -Stmt *BasicBlock::RET(Expr *expr, Type type) +Stmt *BasicBlock::RET(Temp *expr, Type type) { if (isTerminated()) return 0; diff --git a/qv4ir_p.h b/qv4ir_p.h index f6467c98d3..c5e2af9e1c 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -312,9 +312,9 @@ struct Closure: Expr { struct Unop: Expr { AluOp op; - Expr *expr; + Temp *expr; - void init(AluOp op, Expr *expr) + void init(AluOp op, Temp *expr) { this->type = this->typeForOp(op, expr); this->op = op; @@ -332,10 +332,10 @@ private: struct Binop: Expr { AluOp op; - Expr *left; - Expr *right; + Temp *left; + Temp *right; - void init(AluOp op, Expr *left, Expr *right) + void init(AluOp op, Temp *left, Temp *right) { this->type = typeForOp(op, left, right); this->op = op; @@ -352,8 +352,8 @@ struct Binop: Expr { }; struct Call: Expr { - Expr *base; - ExprList *args; + Expr *base; // Name, Member, Temp + ExprList *args; // List of Temps void init(Expr *base, ExprList *args) { @@ -378,8 +378,8 @@ private: }; struct New: Expr { - Expr *base; - ExprList *args; + Expr *base; // Name, Member, Temp + ExprList *args; // List of Temps void init(Expr *base, ExprList *args) { @@ -404,10 +404,10 @@ private: }; struct Subscript: Expr { - Expr *base; - Expr *index; + Temp *base; + Temp *index; - void init(Expr *base, Expr *index) + void init(Temp *base, Temp *index) { this->type = typeForFunction(base); this->base = base; @@ -424,10 +424,10 @@ private: }; struct Member: Expr { - Expr *base; + Temp *base; const QString *name; - void init(Expr *base, const QString *name) + void init(Temp *base, const QString *name) { this->type = typeForFunction(base); this->base = base; @@ -493,7 +493,7 @@ struct Exp: Stmt { }; struct Move: Stmt { - Expr *target; + Expr *target; // LHS - Temp, Name, Member or Subscript Expr *source; AluOp op; @@ -570,10 +570,10 @@ struct CJump: Stmt { }; struct Ret: Stmt { - Expr *expr; + Temp *expr; Type type; - void init(Expr *expr, Type type) + void init(Temp *expr, Type type) { this->expr = expr; this->type = type; @@ -685,12 +685,12 @@ struct BasicBlock { Closure *CLOSURE(Function *function); - Expr *UNOP(AluOp op, Expr *expr); - Expr *BINOP(AluOp op, Expr *left, Expr *right); + Expr *UNOP(AluOp op, Temp *expr); + Expr *BINOP(AluOp op, Temp *left, Temp *right); Expr *CALL(Expr *base, ExprList *args = 0); Expr *NEW(Expr *base, ExprList *args = 0); - Expr *SUBSCRIPT(Expr *base, Expr *index); - Expr *MEMBER(Expr *base, const QString *name); + Expr *SUBSCRIPT(Temp *base, Temp *index); + Expr *MEMBER(Temp *base, const QString *name); Stmt *EXP(Expr *expr); Stmt *ENTER(Expr *expr); @@ -700,7 +700,7 @@ struct BasicBlock { Stmt *JUMP(BasicBlock *target); Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); - Stmt *RET(Expr *expr, Type type); + Stmt *RET(Temp *expr, Type type); void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); }; -- cgit v1.2.3 From ac7f4805c16cc3e61d24437bc570124ea0ca263c Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 8 Jun 2012 20:09:13 +0100 Subject: Add moth skeleton --- main.cpp | 17 ++++++++++--- moth/moth.pri | 10 ++++++++ moth/qv4instr_moth.cpp | 15 ++++++++++++ moth/qv4instr_moth_p.h | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ moth/qv4isel_moth.cpp | 61 ++++++++++++++++++++++++++++++++++++++++++++++ moth/qv4isel_moth_p.h | 35 +++++++++++++++++++++++++++ v4.pro | 2 ++ 7 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 moth/moth.pri create mode 100644 moth/qv4instr_moth.cpp create mode 100644 moth/qv4instr_moth_p.h create mode 100644 moth/qv4isel_moth.cpp create mode 100644 moth/qv4isel_moth_p.h diff --git a/main.cpp b/main.cpp index 4d277bc539..b4fd7cdc78 100644 --- a/main.cpp +++ b/main.cpp @@ -2,6 +2,7 @@ #include "qmljs_objects.h" #include "qv4codegen_p.h" #include "qv4isel_x86_64_p.h" +#include "qv4isel_moth_p.h" #include "qv4syntaxchecker_p.h" #include "qv4ecmaobjects_p.h" @@ -181,6 +182,8 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS uchar *code = (uchar *) malloc(codeSize); assert(! (size_t(code) & 15)); + static bool useMoth = !qgetenv("USE_MOTH").isNull(); + { QQmlJS::Engine ee, *engine = ⅇ Lexer lexer(engine); @@ -201,13 +204,21 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS Codegen cg; globalCode = cg(program, &module); - x86_64::InstructionSelection isel(vm, &module, code); - foreach (IR::Function *function, module.functions) { - isel(function); + if (useMoth) { + Moth::InstructionSelection isel(vm, &module, code); + foreach (IR::Function *function, module.functions) + isel(function); + } else { + x86_64::InstructionSelection isel(vm, &module, code); + foreach (IR::Function *function, module.functions) + isel(function); } } } + if (useMoth) + return; + if (! protect(code, codeSize)) Q_UNREACHABLE(); diff --git a/moth/moth.pri b/moth/moth.pri new file mode 100644 index 0000000000..859c4bf8f4 --- /dev/null +++ b/moth/moth.pri @@ -0,0 +1,10 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qv4isel_moth_p.h \ + $$PWD/qv4instr_moth_p.h \ + +SOURCES += \ + $$PWD/qv4isel_moth.cpp \ + $$PWD/qv4instr_moth.cpp \ + diff --git a/moth/qv4instr_moth.cpp b/moth/qv4instr_moth.cpp new file mode 100644 index 0000000000..a2bad39e00 --- /dev/null +++ b/moth/qv4instr_moth.cpp @@ -0,0 +1,15 @@ +#include "qv4instr_moth_p.h" + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +int Instr::size(Type type) +{ +#define MOTH_RETURN_INSTR_SIZE(I, FMT) case I: return InstrMeta<(int)I>::Size; + switch (type) { + FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE) + default: return 0; + } +#undef MOTH_RETURN_INSTR_SIZE +} + diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h new file mode 100644 index 0000000000..6e95fc0a07 --- /dev/null +++ b/moth/qv4instr_moth_p.h @@ -0,0 +1,65 @@ +#ifndef QV4INSTR_MOTH_P_H +#define QV4INSTR_MOTH_P_H + +#include + +#define FOR_EACH_MOTH_INSTR(F) \ + F(Nop, nop) \ + +#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) +# define MOTH_THREADED_INTERPRETER +#endif + +#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QQmlJS::Moth::Instr) - 1) + +#ifdef MOTH_THREADED_INTERPRETER +# define MOTH_INSTR_HEADER void *code; +#else +# define MOTH_INSTR_HEADER quint8 instructionType; +#endif + +#define MOTH_INSTR_ENUM(I, FMT) I, +#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QQmlJS::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK) + + +namespace QQmlJS { +namespace Moth { + +union Instr +{ + enum Type { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) + }; + + struct instr_nop { + MOTH_INSTR_HEADER + }; + + instr_nop nop; + + static int size(Type type); +}; + +template +struct InstrMeta { +}; + +#define MOTH_INSTR_META_TEMPLATE(I, FMT) \ + template<> struct InstrMeta<(int)Instr::I> { \ + enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \ + typedef Instr::instr_##FMT DataType; \ + static const DataType &data(const Instr &instr) { return instr.FMT; } \ + static void setData(Instr &instr, const DataType &v) { instr.FMT = v; } \ + }; +FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); +#undef MOTH_INSTR_META_TEMPLATE + +template +class InstrData : public InstrMeta::DataType +{ +}; + +} // namespace Moth +} // namespace QQmlJS + +#endif // QV4INSTR_MOTH_P_H diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp new file mode 100644 index 0000000000..b37d0c7e7d --- /dev/null +++ b/moth/qv4isel_moth.cpp @@ -0,0 +1,61 @@ +#include "qv4isel_moth_p.h" + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, + uchar *code) +{ +} + +InstructionSelection::~InstructionSelection() +{ +} + +void InstructionSelection::operator()(IR::Function *function) +{ + qSwap(_function, function); + + foreach (_block, _function->basicBlocks) { + foreach (IR::Stmt *s, _block->statements) + s->accept(this); + } + + qSwap(_function, function); +} + +void InstructionSelection::visitExp(IR::Exp *) +{ + qWarning(__PRETTY_FUNCTION__); +} + +void InstructionSelection::visitEnter(IR::Enter *) +{ + qWarning(__PRETTY_FUNCTION__); +} + +void InstructionSelection::visitLeave(IR::Leave *) +{ + qWarning(__PRETTY_FUNCTION__); +} + +void InstructionSelection::visitMove(IR::Move *) +{ + qWarning(__PRETTY_FUNCTION__); +} + +void InstructionSelection::visitJump(IR::Jump *) +{ + qWarning(__PRETTY_FUNCTION__); +} + +void InstructionSelection::visitCJump(IR::CJump *) +{ + qWarning(__PRETTY_FUNCTION__); +} + +void InstructionSelection::visitRet(IR::Ret *) +{ + qWarning(__PRETTY_FUNCTION__); +} + diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h new file mode 100644 index 0000000000..2d08f457bd --- /dev/null +++ b/moth/qv4isel_moth_p.h @@ -0,0 +1,35 @@ +#ifndef QV4ISEL_MOTH_P_H +#define QV4ISEL_MOTH_P_H + +#include "qv4ir_p.h" +#include "qmljs_objects.h" + +namespace QQmlJS { +namespace Moth { + +class InstructionSelection : public IR::StmtVisitor +{ +public: + InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *code); + ~InstructionSelection(); + + void operator()(IR::Function *function); + +protected: + virtual void visitExp(IR::Exp *); + virtual void visitEnter(IR::Enter *); + virtual void visitLeave(IR::Leave *); + virtual void visitMove(IR::Move *); + virtual void visitJump(IR::Jump *); + virtual void visitCJump(IR::CJump *); + virtual void visitRet(IR::Ret *); + +private: + IR::Function *_function; + IR::BasicBlock *_block; +}; + +} // namespace Moth +} // namespace QQmlJS + +#endif // QV4ISEL_MOTH_P_H diff --git a/v4.pro b/v4.pro index e22ea44572..6cac5950e0 100644 --- a/v4.pro +++ b/v4.pro @@ -73,3 +73,5 @@ gen_llvm_runtime.commands = clang -O2 -emit-llvm -c -DQMLJS_LLVM_RUNTIME llvm_ru DEFINES += QMLJS_NO_LLVM } + +include(moth/moth.pri) -- cgit v1.2.3 From 3f0dba95a4fea0babd129ba464d7198e56dcc63d Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 11 Jun 2012 13:59:42 +0200 Subject: Fix make llvm_runtime --- qmljs_runtime.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index a4740d5926..63034ecca8 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -550,7 +550,7 @@ inline int __qmljs_to_int32(Context *ctx, const Value *value) else if (sign != -1 && number >= D31) number -= D32; - return qint32(number); + return int(number); } inline unsigned __qmljs_to_uint32(Context *ctx, const Value *value) -- cgit v1.2.3 From b9aa2286443fd315d0d2c1eb0bfb17bd2eba358a Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 11 Jun 2012 14:21:02 +0200 Subject: Remove dead code. --- qv4codegen.cpp | 78 ++++++++++++++-------------- qv4ir.cpp | 157 ++++----------------------------------------------------- qv4ir_p.h | 63 +++-------------------- 3 files changed, 57 insertions(+), 241 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index eb7037e369..962b87c0d2 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -369,43 +369,45 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) { if (IR::Const *c1 = left->asConst()) { if (IR::Const *c2 = right->asConst()) { - const IR::Type ty = IR::Binop::typeForOp(op, left, right); - - switch (op) { - case IR::OpAdd: return _block->CONST(ty, c1->value + c2->value); - case IR::OpAnd: return _block->CONST(ty, c1->value ? c2->value : 0); - case IR::OpBitAnd: return _block->CONST(ty, int(c1->value) & int(c2->value)); - case IR::OpBitOr: return _block->CONST(ty, int(c1->value) | int(c2->value)); - case IR::OpBitXor: return _block->CONST(ty, int(c1->value) ^ int(c2->value)); - case IR::OpDiv: return _block->CONST(ty, c1->value / c2->value); - case IR::OpEqual: return _block->CONST(ty, c1->value == c2->value); - case IR::OpGe: return _block->CONST(ty, c1->value >= c2->value); - case IR::OpGt: return _block->CONST(ty, c1->value > c2->value); - case IR::OpLe: return _block->CONST(ty, c1->value <= c2->value); - case IR::OpLShift: return _block->CONST(ty, int(c1->value) << int(c2->value)); - case IR::OpLt: return _block->CONST(ty, c1->value < c2->value); - case IR::OpMod: return _block->CONST(ty, ::fmod(c1->value, c2->value)); - case IR::OpMul: return _block->CONST(ty, c1->value * c2->value); - case IR::OpNotEqual: return _block->CONST(ty, c1->value != c2->value); - case IR::OpOr: return _block->CONST(ty, c1->value ? c1->value : c2->value); - case IR::OpRShift: return _block->CONST(ty, int(c1->value) >> int(c2->value)); - case IR::OpStrictEqual: return _block->CONST(ty, c1->value == c2->value); - case IR::OpStrictNotEqual: return _block->CONST(ty, c1->value != c2->value); - case IR::OpSub: return _block->CONST(ty, c1->value - c2->value); - case IR::OpURShift: return _block->CONST(ty, unsigned(c1->value) >> int(c2->value)); - - case IR::OpInstanceof: - case IR::OpIn: - assert(!"unreachabe"); - break; - - case IR::OpIfTrue: // unary ops - case IR::OpNot: - case IR::OpUMinus: - case IR::OpUPlus: - case IR::OpCompl: - case IR::OpInvalid: - break; + if (c1->type == IR::NumberType && c2->type == IR::NumberType) { + const IR::Type ty = IR::NumberType; + + switch (op) { + case IR::OpAdd: return _block->CONST(ty, c1->value + c2->value); + case IR::OpAnd: return _block->CONST(ty, c1->value ? c2->value : 0); + case IR::OpBitAnd: return _block->CONST(ty, int(c1->value) & int(c2->value)); + case IR::OpBitOr: return _block->CONST(ty, int(c1->value) | int(c2->value)); + case IR::OpBitXor: return _block->CONST(ty, int(c1->value) ^ int(c2->value)); + case IR::OpDiv: return _block->CONST(ty, c1->value / c2->value); + case IR::OpEqual: return _block->CONST(ty, c1->value == c2->value); + case IR::OpGe: return _block->CONST(ty, c1->value >= c2->value); + case IR::OpGt: return _block->CONST(ty, c1->value > c2->value); + case IR::OpLe: return _block->CONST(ty, c1->value <= c2->value); + case IR::OpLShift: return _block->CONST(ty, int(c1->value) << int(c2->value)); + case IR::OpLt: return _block->CONST(ty, c1->value < c2->value); + case IR::OpMod: return _block->CONST(ty, ::fmod(c1->value, c2->value)); + case IR::OpMul: return _block->CONST(ty, c1->value * c2->value); + case IR::OpNotEqual: return _block->CONST(ty, c1->value != c2->value); + case IR::OpOr: return _block->CONST(ty, c1->value ? c1->value : c2->value); + case IR::OpRShift: return _block->CONST(ty, int(c1->value) >> int(c2->value)); + case IR::OpStrictEqual: return _block->CONST(ty, c1->value == c2->value); + case IR::OpStrictNotEqual: return _block->CONST(ty, c1->value != c2->value); + case IR::OpSub: return _block->CONST(ty, c1->value - c2->value); + case IR::OpURShift: return _block->CONST(ty, unsigned(c1->value) >> int(c2->value)); + + case IR::OpInstanceof: + case IR::OpIn: + assert(!"unreachabe"); + break; + + case IR::OpIfTrue: // unary ops + case IR::OpNot: + case IR::OpUMinus: + case IR::OpUPlus: + case IR::OpCompl: + case IR::OpInvalid: + break; + } } } } else if (op == IR::OpAdd) { @@ -1436,7 +1438,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, unsigned returnAddress = entryBlock->newTemp(); entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(IR::UndefinedType, 0)); - exitBlock->RET(exitBlock->TEMP(returnAddress), IR::InvalidType); + exitBlock->RET(exitBlock->TEMP(returnAddress)); IR::ExprList *throwArgs = function->New(); throwArgs->expr = throwBlock->TEMP(returnAddress); throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); diff --git a/qv4ir.cpp b/qv4ir.cpp index c915c9ad90..79d5f113eb 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -55,61 +55,14 @@ namespace IR { const char *typeName(Type t) { switch (t) { - case InvalidType: return "invalid"; case UndefinedType: return "undefined"; case NullType: return "null"; - case VoidType: return "void"; - case StringType: return "string"; - case UrlType: return "QUrl"; - case ColorType: return "QColor"; - case SGAnchorLineType: return "SGAnchorLine"; - case AttachType: return "AttachType"; - case ObjectType: return "object"; - case VariantType: return "variant"; - case VarType: return "var"; case BoolType: return "bool"; - case IntType: return "int"; - case FloatType: return "float"; case NumberType: return "number"; default: return "invalid"; } } -inline bool isNumberType(IR::Type ty) -{ - return ty >= IR::FirstNumberType; -} - -inline bool isStringType(IR::Type ty) -{ - return ty == IR::StringType || ty == IR::UrlType || ty == IR::ColorType; -} - -IR::Type maxType(IR::Type left, IR::Type right) -{ - if (isStringType(left) && isStringType(right)) { - // String promotions (url to string) are more specific than - // identity conversions (AKA left == right). That's because - // we want to ensure we convert urls to strings in binary - // expressions. - return IR::StringType; - } else if (left == right) - return left; - else if (isNumberType(left) && isNumberType(right)) { - IR::Type ty = qMax(left, right); - return ty == FloatType ? NumberType : ty; // promote floats - } else if ((isNumberType(left) && isStringType(right)) || - (isNumberType(right) && isStringType(left))) - return IR::StringType; - else - return IR::InvalidType; -} - -bool isRealType(IR::Type type) -{ - return type == IR::NumberType || type == IR::FloatType; -} - const char *opname(AluOp op) { switch (op) { @@ -194,9 +147,6 @@ void Const::dump(QTextStream &out) case QQmlJS::IR::NullType: out << "null"; break; - case QQmlJS::IR::VoidType: - out << "void"; - break; case QQmlJS::IR::BoolType: out << (value ? "true" : "false"); break; @@ -232,18 +182,16 @@ QString String::escape(const QString &s) return r; } -void Name::init(Type type, const QString *id, quint32 line, quint32 column) +void Name::init(const QString *id, quint32 line, quint32 column) { - this->type = type; this->id = id; this->builtin = builtin_invalid; this->line = line; this->column = column; } -void Name::init(Type type, Builtin builtin, quint32 line, quint32 column) +void Name::init(Builtin builtin, quint32 line, quint32 column) { - this->type = type; this->id = 0; this->builtin = builtin; this->line = line; @@ -281,24 +229,6 @@ void Unop::dump(QTextStream &out) expr->dump(out); } -Type Unop::typeForOp(AluOp op, Expr *expr) -{ - switch (op) { - case OpIfTrue: return BoolType; - case OpNot: return BoolType; - - case OpUMinus: - case OpUPlus: - case OpCompl: - return maxType(expr->type, NumberType); - - default: - break; - } - - return InvalidType; -} - void Binop::dump(QTextStream &out) { left->dump(out); @@ -306,65 +236,6 @@ void Binop::dump(QTextStream &out) right->dump(out); } -Type Binop::typeForOp(AluOp op, Expr *left, Expr *right) -{ - if (! (left && right)) - return InvalidType; - - switch (op) { - case OpInvalid: - return InvalidType; - - // unary operators - case OpIfTrue: - case OpNot: - case OpUMinus: - case OpUPlus: - case OpCompl: - return InvalidType; - - // bit fields - case OpBitAnd: - case OpBitOr: - case OpBitXor: - return IntType; - - case OpAdd: - if (left->type == StringType) - return StringType; - return NumberType; - - case OpSub: - case OpMul: - case OpDiv: - case OpMod: - return NumberType; - - case OpLShift: - case OpRShift: - case OpURShift: - return IntType; - - case OpAnd: - case OpOr: - return BoolType; - - case OpGt: - case OpLt: - case OpGe: - case OpLe: - case OpEqual: - case OpNotEqual: - case OpStrictEqual: - case OpStrictNotEqual: - case OpInstanceof: - case OpIn: - return BoolType; - } // switch - - return InvalidType; -} - void Call::dump(QTextStream &out) { base->dump(out); @@ -377,11 +248,6 @@ void Call::dump(QTextStream &out) out << ')'; } -Type Call::typeForFunction(Expr *) -{ - return InvalidType; -} - void New::dump(QTextStream &out) { out << "new "; @@ -395,11 +261,6 @@ void New::dump(QTextStream &out) out << ')'; } -Type New::typeForFunction(Expr *) -{ - return InvalidType; -} - void Subscript::dump(QTextStream &out) { base->dump(out); @@ -522,11 +383,11 @@ unsigned BasicBlock::newTemp() Temp *BasicBlock::TEMP(int index) { Temp *e = function->New(); - e->init(IR::InvalidType, index); + e->init(index); return e; } -Expr *BasicBlock::CONST(Type type, double value) +Expr *BasicBlock::CONST(Type type, double value) { Const *e = function->New(); e->init(type, value); @@ -543,21 +404,21 @@ Expr *BasicBlock::STRING(const QString *value) Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) { Name *e = function->New(); - e->init(InvalidType, function->newString(id), line, column); + e->init(function->newString(id), line, column); return e; } Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column) { Name *e = function->New(); - e->init(InvalidType, builtin, line, column); + e->init(builtin, line, column); return e; } Closure *BasicBlock::CLOSURE(Function *function) { Closure *clos = function->New(); - clos->init(IR::InvalidType, function); + clos->init(function); return clos; } @@ -694,13 +555,13 @@ Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) return s; } -Stmt *BasicBlock::RET(Temp *expr, Type type) +Stmt *BasicBlock::RET(Temp *expr) { if (isTerminated()) return 0; Ret *s = function->New(); - s->init(expr, type); + s->init(expr); statements.append(s); return s; } diff --git a/qv4ir_p.h b/qv4ir_p.h index c5e2af9e1c..ef9d801310 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -146,28 +146,11 @@ AluOp binaryOperator(int op); const char *opname(IR::AluOp op); enum Type { - InvalidType, UndefinedType, NullType, - VoidType, - StringType, - UrlType, - ColorType, - SGAnchorLineType, - AttachType, - ObjectType, - VariantType, - VarType, - - FirstNumberType, - BoolType = FirstNumberType, - IntType, - FloatType, + BoolType, NumberType }; -Type maxType(IR::Type left, IR::Type right); -bool isRealType(IR::Type type); -const char *typeName(IR::Type t); struct ExprVisitor { virtual ~ExprVisitor() {} @@ -196,9 +179,6 @@ struct StmtVisitor { }; struct Expr { - Type type; - - Expr(): type(InvalidType) {} virtual ~Expr() {} virtual void accept(ExprVisitor *) = 0; virtual Const *asConst() { return 0; } @@ -227,6 +207,7 @@ struct ExprList { }; struct Const: Expr { + Type type; double value; void init(Type type, double value) @@ -246,7 +227,6 @@ struct String: Expr { void init(const QString *value) { - this->type = StringType; this->value = value; } @@ -271,8 +251,8 @@ struct Name: Expr { quint32 line; quint32 column; - void init(Type type, const QString *id, quint32 line, quint32 column); - void init(Type type, Builtin builtin, quint32 line, quint32 column); + void init(const QString *id, quint32 line, quint32 column); + void init(Builtin builtin, quint32 line, quint32 column); virtual void accept(ExprVisitor *v) { v->visitName(this); } virtual Name *asName() { return this; } @@ -283,9 +263,8 @@ struct Name: Expr { struct Temp: Expr { int index; - void init(Type type, int index) + void init(int index) { - this->type = type; this->index = index; } @@ -298,9 +277,8 @@ struct Temp: Expr { struct Closure: Expr { Function *value; - void init(Type type, Function *value) + void init(Function *value) { - this->type = type; this->value = value; } @@ -316,7 +294,6 @@ struct Unop: Expr { void init(AluOp op, Temp *expr) { - this->type = this->typeForOp(op, expr); this->op = op; this->expr = expr; } @@ -325,9 +302,6 @@ struct Unop: Expr { virtual Unop *asUnop() { return this; } virtual void dump(QTextStream &out); - -private: - static Type typeForOp(AluOp op, Expr *expr); }; struct Binop: Expr { @@ -337,7 +311,6 @@ struct Binop: Expr { void init(AluOp op, Temp *left, Temp *right) { - this->type = typeForOp(op, left, right); this->op = op; this->left = left; this->right = right; @@ -347,8 +320,6 @@ struct Binop: Expr { virtual Binop *asBinop() { return this; } virtual void dump(QTextStream &out); - - static Type typeForOp(AluOp op, Expr *left, Expr *right); }; struct Call: Expr { @@ -357,7 +328,6 @@ struct Call: Expr { void init(Expr *base, ExprList *args) { - this->type = typeForFunction(base); this->base = base; this->args = args; } @@ -372,9 +342,6 @@ struct Call: Expr { virtual Call *asCall() { return this; } virtual void dump(QTextStream &out); - -private: - static Type typeForFunction(Expr *base); }; struct New: Expr { @@ -383,7 +350,6 @@ struct New: Expr { void init(Expr *base, ExprList *args) { - this->type = typeForFunction(base); this->base = base; this->args = args; } @@ -398,9 +364,6 @@ struct New: Expr { virtual New *asNew() { return this; } virtual void dump(QTextStream &out); - -private: - static Type typeForFunction(Expr *base); }; struct Subscript: Expr { @@ -409,7 +372,6 @@ struct Subscript: Expr { void init(Temp *base, Temp *index) { - this->type = typeForFunction(base); this->base = base; this->index = index; } @@ -418,9 +380,6 @@ struct Subscript: Expr { virtual Subscript *asSubscript() { return this; } virtual void dump(QTextStream &out); - -private: - static Type typeForFunction(Expr *) { return IR::InvalidType; } }; struct Member: Expr { @@ -429,7 +388,6 @@ struct Member: Expr { void init(Temp *base, const QString *name) { - this->type = typeForFunction(base); this->base = base; this->name = name; } @@ -438,9 +396,6 @@ struct Member: Expr { virtual Member *asMember() { return this; } virtual void dump(QTextStream &out); - -private: - static Type typeForFunction(Expr *) { return IR::InvalidType; } }; struct Stmt { @@ -571,12 +526,10 @@ struct CJump: Stmt { struct Ret: Stmt { Temp *expr; - Type type; - void init(Temp *expr, Type type) + void init(Temp *expr) { this->expr = expr; - this->type = type; } virtual Stmt *asTerminator() { return this; } @@ -700,7 +653,7 @@ struct BasicBlock { Stmt *JUMP(BasicBlock *target); Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); - Stmt *RET(Temp *expr, Type type); + Stmt *RET(Temp *expr); void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); }; -- cgit v1.2.3 From 2ebc4b30b2cefccbad0c27ba1f38d8e600533bb0 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Mon, 11 Jun 2012 14:38:56 +0200 Subject: Keep alive the exception handler block. --- qv4codegen.cpp | 5 +++++ qv4isel_x86_64.cpp | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 962b87c0d2..d3a1cea4f9 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1266,6 +1266,7 @@ void Codegen::linearize(IR::Function *function) assert(exitBlock->terminator()->asRet()); QSet V; + V.insert(function->handlersBlock); V.insert(exitBlock); QVector trace; @@ -1307,8 +1308,12 @@ void Codegen::linearize(IR::Function *function) I::trace(function->basicBlocks.first(), &V, &trace); + function->handlersBlock->index = trace.size(); + trace.append(function->handlersBlock); + exitBlock->index = trace.size(); trace.append(exitBlock); + function->basicBlocks = trace; #ifndef QV4_NO_LIVENESS diff --git a/qv4isel_x86_64.cpp b/qv4isel_x86_64.cpp index 8c5049cfea..2a18ba962e 100644 --- a/qv4isel_x86_64.cpp +++ b/qv4isel_x86_64.cpp @@ -137,7 +137,8 @@ void InstructionSelection::operator()(IR::Function *function) QHashIterator > it(_patches); while (it.hasNext()) { it.next(); - uchar *target = _addrs.value(it.key()); + IR::BasicBlock *block = it.key(); + uchar *target = _addrs.value(block); assert(target != 0); foreach (uchar *instr, it.value()) { amd64_patch(instr, target); -- cgit v1.2.3 From b759b5f984d35d5ce95097572bd8524cfd2210fc Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Mon, 11 Jun 2012 17:58:52 +0100 Subject: Add skeleton moth vme --- main.cpp | 17 ++++++--- moth/moth.pri | 2 + moth/qv4instr_moth_p.h | 7 ++-- moth/qv4isel_moth.cpp | 36 ++++++++++++++---- moth/qv4isel_moth_p.h | 24 ++++++++++++ moth/qv4vme_moth.cpp | 100 +++++++++++++++++++++++++++++++++++++++++++++++++ moth/qv4vme_moth_p.h | 27 +++++++++++++ 7 files changed, 197 insertions(+), 16 deletions(-) create mode 100644 moth/qv4vme_moth.cpp create mode 100644 moth/qv4vme_moth_p.h diff --git a/main.cpp b/main.cpp index b4fd7cdc78..28a92935a6 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,7 @@ #include "qv4codegen_p.h" #include "qv4isel_x86_64_p.h" #include "qv4isel_moth_p.h" +#include "qv4vme_moth_p.h" #include "qv4syntaxchecker_p.h" #include "qv4ecmaobjects_p.h" @@ -216,11 +217,10 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS } } - if (useMoth) - return; - - if (! protect(code, codeSize)) - Q_UNREACHABLE(); + if (!useMoth) { + if (! protect(code, codeSize)) + Q_UNREACHABLE(); + } VM::Context *ctx = vm->rootContext; @@ -233,7 +233,12 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS ctx->vars[i] = ctx->engine->identifier(*globalCode->locals.at(i)); } - globalCode->code(ctx); + if (useMoth) { + Moth::VME vme; + vme(ctx, code); + } else { + globalCode->code(ctx); + } if (ctx->hasUncaughtException) { if (VM::ErrorObject *e = ctx->result.asErrorObject()) diff --git a/moth/moth.pri b/moth/moth.pri index 859c4bf8f4..d91d13fca8 100644 --- a/moth/moth.pri +++ b/moth/moth.pri @@ -3,8 +3,10 @@ INCLUDEPATH += $$PWD HEADERS += \ $$PWD/qv4isel_moth_p.h \ $$PWD/qv4instr_moth_p.h \ + $$PWD/qv4vme_moth_p.h \ SOURCES += \ $$PWD/qv4isel_moth.cpp \ $$PWD/qv4instr_moth.cpp \ + $$PWD/qv4vme_moth.cpp \ diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 6e95fc0a07..e582bb4ab6 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -4,7 +4,8 @@ #include #define FOR_EACH_MOTH_INSTR(F) \ - F(Nop, nop) \ + F(Nop, common) \ + F(Done, common) \ #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) # define MOTH_THREADED_INTERPRETER @@ -31,11 +32,11 @@ union Instr FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) }; - struct instr_nop { + struct instr_common { MOTH_INSTR_HEADER }; - instr_nop nop; + instr_common common; static int size(Type type); }; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index b37d0c7e7d..628e2f0a91 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -1,10 +1,12 @@ #include "qv4isel_moth_p.h" +#include "qv4vme_moth_p.h" using namespace QQmlJS; using namespace QQmlJS::Moth; InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *code) +: _code(code), _ccode(code) { } @@ -21,41 +23,61 @@ void InstructionSelection::operator()(IR::Function *function) s->accept(this); } + addInstruction(Instruction::Done()); + qSwap(_function, function); } void InstructionSelection::visitExp(IR::Exp *) { - qWarning(__PRETTY_FUNCTION__); + qWarning("%s", __PRETTY_FUNCTION__); } void InstructionSelection::visitEnter(IR::Enter *) { - qWarning(__PRETTY_FUNCTION__); + qWarning("%s", __PRETTY_FUNCTION__); } void InstructionSelection::visitLeave(IR::Leave *) { - qWarning(__PRETTY_FUNCTION__); + qWarning("%s", __PRETTY_FUNCTION__); } void InstructionSelection::visitMove(IR::Move *) { - qWarning(__PRETTY_FUNCTION__); + qWarning("%s", __PRETTY_FUNCTION__); } void InstructionSelection::visitJump(IR::Jump *) { - qWarning(__PRETTY_FUNCTION__); + qWarning("%s", __PRETTY_FUNCTION__); } void InstructionSelection::visitCJump(IR::CJump *) { - qWarning(__PRETTY_FUNCTION__); + qWarning("%s", __PRETTY_FUNCTION__); } void InstructionSelection::visitRet(IR::Ret *) { - qWarning(__PRETTY_FUNCTION__); + qWarning("%s", __PRETTY_FUNCTION__); +} + +int InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) +{ +#ifdef MOTH_THREADED_INTERPRETER + instr.common.code = VME::instructionJumpTable()[static_cast(type)]; +#else + instr.common.instructionType = type; +#endif + + // XXX - int is wrong size for both of these + int ptrOffset = (int)(_ccode - _code); + int size = Instr::size(type); + + ::memcpy(_ccode, reinterpret_cast(&instr), size); + _ccode += size; + + return ptrOffset; } diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 2d08f457bd..d8e56cac70 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -3,6 +3,7 @@ #include "qv4ir_p.h" #include "qmljs_objects.h" +#include "qv4instr_moth_p.h" namespace QQmlJS { namespace Moth { @@ -25,10 +26,33 @@ protected: virtual void visitRet(IR::Ret *); private: + struct Instruction { +#define MOTH_INSTR_DATA_TYPEDEF(I, FMT) typedef InstrData I; + FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF) +#undef MOTH_INSTR_DATA_TYPEDEF + private: + Instruction(); + }; + + template + inline int addInstruction(const InstrData &data); + int addInstructionHelper(Instr::Type type, Instr &instr); + IR::Function *_function; IR::BasicBlock *_block; + + uchar *_code; + uchar *_ccode; }; +template +int InstructionSelection::addInstruction(const InstrData &data) +{ + Instr genericInstr; + InstrMeta::setData(genericInstr, data); + return addInstructionHelper(static_cast(InstrT), genericInstr); +} + } // namespace Moth } // namespace QQmlJS diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp new file mode 100644 index 0000000000..c14a61cb9c --- /dev/null +++ b/moth/qv4vme_moth.cpp @@ -0,0 +1,100 @@ +#include "qv4vme_moth_p.h" +#include "qv4instr_moth_p.h" + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +#define MOTH_BEGIN_INSTR_COMMON(I) { \ + const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ + code += InstrMeta<(int)Instr::I>::Size; \ + Q_UNUSED(instr); + +#ifdef MOTH_THREADED_INTERPRETER + +# define MOTH_BEGIN_INSTR(I) op_##I: \ + MOTH_BEGIN_INSTR_COMMON(I) + +# define MOTH_NEXT_INSTR(I) { \ + genericInstr = reinterpret_cast(code); \ + goto *genericInstr->common.code; \ + } + +# define MOTH_END_INSTR(I) } \ + genericInstr = reinterpret_cast(code); \ + goto *genericInstr->common.code; \ + +#else + +# define MOTH_BEGIN_INSTR(I) \ + case Instr::I: \ + MOTH_BEGIN_INSTR_COMMON(I) + +# define MOTH_NEXT_INSTR(I) { \ + break; \ + } + +# define MOTH_END_INSTR(I) } \ + break; + +#endif + +void VME::operator()(QQmlJS::VM::Context *, const uchar *code +#ifdef MOTH_THREADED_INTERPRETER + , void ***storeJumpTable +#endif + ) +{ + +#ifdef MOTH_THREADED_INTERPRETER + if (storeJumpTable) { +#define MOTH_INSTR_ADDR(I, FMT) &&op_##I, + static void *jumpTable[] = { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) + }; +#undef MOTH_INSTR_ADDR + *storeJumpTable = jumpTable; + return; + } +#endif + +#ifdef MOTH_THREADED_INTERPRETER + const Instr *genericInstr = reinterpret_cast(code); + goto *genericInstr->common.code; +#else + for (;;) { + const Instr *genericInstr = reinterpret_cast(code); + switch (genericInstr->common.instructionType) { +#endif + + MOTH_BEGIN_INSTR(Nop) + qWarning("NOP"); + MOTH_END_INSTR(Nop) + + MOTH_BEGIN_INSTR(Done) + return; + MOTH_END_INSTR(Done) + +#ifdef MOTH_THREADED_INTERPRETER + // nothing to do +#else + default: + qFatal("QQmlJS::Moth::VME: Internal error - unknown instruction %d", genericInstr->common.instructionType); + break; + } + } +#endif + +} + +#ifdef MOTH_THREADED_INTERPRETER +void **VME::instructionJumpTable() +{ + static void **jumpTable = 0; + if (!jumpTable) { + VME dummy; + dummy(0, 0, &jumpTable); + } + return jumpTable; +} +#endif + diff --git a/moth/qv4vme_moth_p.h b/moth/qv4vme_moth_p.h new file mode 100644 index 0000000000..5055407ca2 --- /dev/null +++ b/moth/qv4vme_moth_p.h @@ -0,0 +1,27 @@ +#ifndef QV4VME_MOTH_P_H +#define QV4VME_MOTH_P_H + +#include "qmljs_runtime.h" +#include "qv4instr_moth_p.h" + +namespace QQmlJS { +namespace Moth { + +class VME +{ +public: + void operator()(QQmlJS::VM::Context *, const uchar *code +#ifdef MOTH_THREADED_INTERPRETER + , void ***storeJumpTable = 0 +#endif + ); + +#ifdef MOTH_THREADED_INTERPRETER + static void **instructionJumpTable(); +#endif +}; + +} // namespace Moth +} // namespace QQmlJS + +#endif // QV4VME_MOTH_P_H -- cgit v1.2.3 From f3933836fbaba174471b2504588ade874634b67d Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 12 Jun 2012 12:10:33 +0200 Subject: Fix possible crash when executing invalid code. --- main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main.cpp b/main.cpp index 28a92935a6..ed4985feac 100644 --- a/main.cpp +++ b/main.cpp @@ -215,6 +215,9 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS isel(function); } } + + if (! globalCode) + return; } if (!useMoth) { -- cgit v1.2.3 From 684ed0dc14de1bfba249a322b14e16c586740626 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 12 Jun 2012 12:35:36 +0200 Subject: Implemented Function.prototype.apply --- qv4ecmaobjects.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 11bc92f8d0..e044d11c64 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1746,8 +1746,27 @@ void FunctionPrototype::method_toString(Context *ctx) void FunctionPrototype::method_apply(Context *ctx) { if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { - Q_UNUSED(fun); - ctx->throwUnimplemented(QStringLiteral("Function.prototype.apply")); + + Value thisObject = ctx->argument(0).toObject(ctx); + if (thisObject.isNull() || thisObject.isUndefined()) + thisObject = ctx->engine->globalObject; + + Value arg = ctx->argument(1); + QVector args; + + if (ArrayObject *arr = arg.asArrayObject()) { + const Array &actuals = arr->value; + + for (quint32 i = 0; i < actuals.count(); ++i) { + Value a = actuals.at(i); + args.append(a); + } + } else if (!(arg.isUndefined() || arg.isNull())) { + ctx->throwError(QLatin1String("Function.prototype.apply: second argument is not an array")); + return; + } + + __qmljs_call_value(ctx, &ctx->result, &thisObject, &ctx->thisObject, args.data(), args.size()); } else { ctx->throwTypeError(); } -- cgit v1.2.3 From 6ce83a38c9ba4b5b76ed1bdf223d471b886b5c72 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Tue, 12 Jun 2012 18:13:55 +0100 Subject: Begin to implement moth vm --- main.cpp | 10 +- moth/qv4instr_moth_p.h | 84 ++++++++++++++- moth/qv4isel_moth.cpp | 282 ++++++++++++++++++++++++++++++++++++++++++++++--- moth/qv4isel_moth_p.h | 13 ++- moth/qv4vme_moth.cpp | 100 ++++++++++++++++-- moth/qv4vme_moth_p.h | 2 + qmljs_objects.cpp | 4 +- qv4ir_p.h | 8 +- qv4isel_x86_64.cpp | 2 +- 9 files changed, 468 insertions(+), 37 deletions(-) diff --git a/main.cpp b/main.cpp index ed4985feac..777c8999c3 100644 --- a/main.cpp +++ b/main.cpp @@ -213,6 +213,9 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS x86_64::InstructionSelection isel(vm, &module, code); foreach (IR::Function *function, module.functions) isel(function); + + if (! protect(code, codeSize)) + Q_UNREACHABLE(); } } @@ -220,11 +223,6 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS return; } - if (!useMoth) { - if (! protect(code, codeSize)) - Q_UNREACHABLE(); - } - VM::Context *ctx = vm->rootContext; ctx->varCount = globalCode->locals.size(); @@ -240,7 +238,7 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS Moth::VME vme; vme(ctx, code); } else { - globalCode->code(ctx); + globalCode->code(ctx, globalCode->codeData); } if (ctx->hasUncaughtException) { diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index e582bb4ab6..d9f2c11cdd 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -2,10 +2,26 @@ #define QV4INSTR_MOTH_P_H #include +#include "qmljs_objects.h" #define FOR_EACH_MOTH_INSTR(F) \ - F(Nop, common) \ - F(Done, common) \ + F(Ret, ret) \ + F(LoadUndefined, common) \ + F(LoadNull, common) \ + F(LoadFalse, common) \ + F(LoadTrue, common) \ + F(LoadNumber, loadNumber) \ + F(LoadString, loadString) \ + F(LoadClosure, loadClosure) \ + F(StoreTemp, storeTemp) \ + F(LoadTemp, loadTemp) \ + F(MoveTemp, moveTemp) \ + F(LoadName, loadName) \ + F(Push, push) \ + F(Call, call) \ + F(Jump, jump) \ + F(CJump, jump) \ + F(Binop, binop) \ #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) # define MOTH_THREADED_INTERPRETER @@ -35,8 +51,72 @@ union Instr struct instr_common { MOTH_INSTR_HEADER }; + struct instr_ret { + MOTH_INSTR_HEADER + int tempIndex; + }; + struct instr_storeTemp { + MOTH_INSTR_HEADER + int tempIndex; + }; + struct instr_loadTemp { + MOTH_INSTR_HEADER + int tempIndex; + }; + struct instr_moveTemp { + MOTH_INSTR_HEADER + int fromTempIndex; + int toTempIndex; + }; + struct instr_loadNumber { + MOTH_INSTR_HEADER + double value; + }; + struct instr_loadString { + MOTH_INSTR_HEADER + VM::String *value; + }; + struct instr_loadClosure { + MOTH_INSTR_HEADER + IR::Function *value; + }; + struct instr_loadName { + MOTH_INSTR_HEADER + VM::String *value; + }; + struct instr_push { + MOTH_INSTR_HEADER + quint32 value; + }; + struct instr_call { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + }; + struct instr_jump { + MOTH_INSTR_HEADER + ptrdiff_t offset; + }; + struct instr_binop { + MOTH_INSTR_HEADER + int lhsTempIndex; + int rhsTempIndex; + void (*alu)(VM::Context *, VM::Value *, const VM::Value *, const VM::Value *); + }; instr_common common; + instr_ret ret; + instr_storeTemp storeTemp; + instr_loadTemp loadTemp; + instr_moveTemp moveTemp; + instr_loadNumber loadNumber; + instr_loadString loadString; + instr_loadClosure loadClosure; + instr_loadName loadName; + instr_push push; + instr_call call; + instr_jump jump; + instr_binop binop; static int size(Type type); }; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 628e2f0a91..95db21620d 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -6,7 +6,7 @@ using namespace QQmlJS::Moth; InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *code) -: _code(code), _ccode(code) +: _engine(engine), _code(code), _ccode(code) { } @@ -18,52 +18,305 @@ void InstructionSelection::operator()(IR::Function *function) { qSwap(_function, function); + _function->code = VME::exec; + _function->codeData = _ccode; + + int locals = _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments; + assert(locals >= 0); + + Instruction::Push push; + push.value = quint32(locals); + addInstruction(push); + foreach (_block, _function->basicBlocks) { + _addrs.insert(_block, _ccode - _code); + foreach (IR::Stmt *s, _block->statements) s->accept(this); } - addInstruction(Instruction::Done()); + for (QHash >::ConstIterator iter = _patches.begin(); + iter != _patches.end(); ++iter) { + + Q_ASSERT(_addrs.contains(iter.key())); + ptrdiff_t target = _addrs.value(iter.key()); + + const QVector &patchList = iter.value(); + for (int ii = 0; ii < patchList.count(); ++ii) { + ptrdiff_t patch = patchList.at(ii); + + *((ptrdiff_t *)(_code + patch)) = target; + } + } qSwap(_function, function); } -void InstructionSelection::visitExp(IR::Exp *) +void InstructionSelection::visitExp(IR::Exp *s) { - qWarning("%s", __PRETTY_FUNCTION__); + if (IR::Call *c = s->expr->asCall()) { + if (IR::Name *n = c->base->asName()) { + if (n->builtin == IR::Name::builtin_invalid) { + Instruction::LoadName load; + load.value = _engine->newString(*n->id); + addInstruction(load); + } else { + Q_UNIMPLEMENTED(); + } + } else if (IR::Member *m = c->base->asMember()) { + Q_UNIMPLEMENTED(); + } else if (IR::Temp *t = c->base->asTemp()) { + Instruction::LoadTemp load; + load.tempIndex = t->index; + addInstruction(load); + } else { + Q_UNREACHABLE(); + } + + call(c->args); + } else { + Q_UNREACHABLE(); + } +} + +void InstructionSelection::call(IR::ExprList *e) +{ + Instruction::Call call; + call.argc = 0; + call.args = 0; + + int locals = _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments; + + if (e && e->next == 0 && e->expr->asTemp()->index >= 0 && e->expr->asTemp()->index < locals) { + // We pass single arguments as references to the stack + call.argc = 1; + call.args = e->expr->asTemp()->index; + } else if (e) { + // We need to move all the temps into the function arg array + int argLocation = _function->tempCount - _function->locals.size(); + assert(argLocation >= 0); + call.args = argLocation; + while (e) { + Instruction::MoveTemp move; + move.fromTempIndex = e->expr->asTemp()->index; + move.toTempIndex = argLocation; + addInstruction(move); + ++argLocation; + ++call.argc; + e = e->next; + } + } + + addInstruction(call); } void InstructionSelection::visitEnter(IR::Enter *) { qWarning("%s", __PRETTY_FUNCTION__); + Q_UNREACHABLE(); } void InstructionSelection::visitLeave(IR::Leave *) { qWarning("%s", __PRETTY_FUNCTION__); + Q_UNREACHABLE(); } -void InstructionSelection::visitMove(IR::Move *) +void InstructionSelection::visitMove(IR::Move *s) { - qWarning("%s", __PRETTY_FUNCTION__); + if (s->op == IR::OpInvalid) + simpleMove(s); + else + qWarning("UNKNOWN MOVE"); } -void InstructionSelection::visitJump(IR::Jump *) +typedef void (*ALUFunction)(VM::Context*, VM::Value*, const VM::Value*, const VM::Value *); +ALUFunction aluOpFunction(IR::AluOp op) { - qWarning("%s", __PRETTY_FUNCTION__); + switch (op) { + case IR::OpInvalid: + return 0; + case IR::OpIfTrue: + return 0; + case IR::OpNot: + return 0; + case IR::OpUMinus: + return 0; + case IR::OpUPlus: + return 0; + case IR::OpCompl: + return 0; + case IR::OpBitAnd: + return 0; + case IR::OpBitOr: + return 0; + case IR::OpBitXor: + return 0; + case IR::OpAdd: + return VM::__qmljs_add; + case IR::OpSub: + return VM::__qmljs_sub; + case IR::OpMul: + return VM::__qmljs_mul; + case IR::OpDiv: + return VM::__qmljs_div; + case IR::OpMod: + return VM::__qmljs_mod; + case IR::OpLShift: + return VM::__qmljs_shl; + case IR::OpRShift: + return VM::__qmljs_shr; + case IR::OpURShift: + return VM::__qmljs_ushr; + case IR::OpGt: + return VM::__qmljs_gt; + case IR::OpLt: + return VM::__qmljs_lt; + case IR::OpGe: + return VM::__qmljs_ge; + case IR::OpLe: + return VM::__qmljs_le; + case IR::OpEqual: + return VM::__qmljs_eq; + case IR::OpNotEqual: + return VM::__qmljs_ne; + case IR::OpStrictEqual: + return VM::__qmljs_se; + case IR::OpStrictNotEqual: + return VM::__qmljs_sne; + case IR::OpInstanceof: + return 0; + case IR::OpIn: + return 0; + case IR::OpAnd: + return 0; + case IR::OpOr: + return 0; + default: + assert(!"Unknown AluOp"); + return 0; + } +}; + +// A move that doesn't involve an inplace operation +void InstructionSelection::simpleMove(IR::Move *s) +{ + if (IR::Name *n = s->target->asName()) { + qWarning("NAME"); + } else if (IR::Temp *t = s->target->asTemp()) { + + if (IR::Name *n = s->source->asName()) { + qWarning(" NAME"); + } else if (IR::Const *c = s->source->asConst()) { + switch (c->type) { + case IR::UndefinedType: + addInstruction(Instruction::LoadUndefined()); + break; + case IR::NullType: + addInstruction(Instruction::LoadNull()); + break; + case IR::BoolType: + if (c->value) addInstruction(Instruction::LoadTrue()); + else addInstruction(Instruction::LoadFalse()); + break; + case IR::NumberType: { + Instruction::LoadNumber load; + load.value = c->value; + addInstruction(load); + } break; + default: + Q_UNREACHABLE(); + break; + } + } else if (IR::Temp *t2 = s->source->asTemp()) { + Instruction::LoadTemp load; + load.tempIndex = t2->index; + addInstruction(load); + } else if (IR::String *str = s->source->asString()) { + Instruction::LoadString load; + load.value = _engine->newString(*str->value); + addInstruction(load); + } else if (IR::Closure *clos = s->source->asClosure()) { + Instruction::LoadClosure load; + load.value = clos->value; + addInstruction(load); + } else if (IR::New *ctor = s->source->asNew()) { + qWarning(" NEW"); + } else if (IR::Member *m = s->source->asMember()) { + qWarning(" MEMBER"); + } else if (IR::Subscript *ss = s->source->asSubscript()) { + qWarning(" SUBSCRIPT"); + } else if (IR::Unop *u = s->source->asUnop()) { + qWarning(" UNOP"); + } else if (IR::Binop *b = s->source->asBinop()) { + Instruction::Binop binop; + binop.alu = aluOpFunction(b->op); + binop.lhsTempIndex = b->left->index; + binop.rhsTempIndex = b->right->index; + addInstruction(binop); + } else if (IR::Call *c = s->source->asCall()) { + qWarning(" CALL"); + } + + Instruction::StoreTemp st; + st.tempIndex = t->index; + addInstruction(st); + + } else if (IR::Member *m = s->target->asMember()) { + qWarning("MEMBER"); + } else if (IR::Subscript *ss = s->target->asSubscript()) { + qWarning("SUBSCRIPT"); + } else { + Q_UNREACHABLE(); + } } -void InstructionSelection::visitCJump(IR::CJump *) +void InstructionSelection::visitJump(IR::Jump *s) { - qWarning("%s", __PRETTY_FUNCTION__); + Instruction::Jump jump; + jump.offset = 0; + ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + + _patches[s->target].append(loc); } -void InstructionSelection::visitRet(IR::Ret *) +void InstructionSelection::visitCJump(IR::CJump *s) { - qWarning("%s", __PRETTY_FUNCTION__); + if (IR::Temp *t = s->cond->asTemp()) { + Instruction::LoadTemp load; + load.tempIndex = t->index; + addInstruction(load); + } else if (IR::Binop *b = s->cond->asBinop()) { + Instruction::Binop binop; + binop.alu = aluOpFunction(b->op); + binop.lhsTempIndex = b->left->index; + binop.rhsTempIndex = b->right->index; + addInstruction(binop); + } else { + Q_UNREACHABLE(); + } + + Instruction::CJump jump; + jump.offset = 0; + ptrdiff_t tl = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + _patches[s->iftrue].append(tl); + + if (_block->index + 1 != s->iffalse->index) { + Instruction::Jump jump; + jump.offset = 0; + ptrdiff_t fl = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + _patches[s->iffalse].append(fl); + } +} + +void InstructionSelection::visitRet(IR::Ret *s) +{ + Instruction::Ret ret; + ret.tempIndex = s->expr->index; + addInstruction(ret); } -int InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) +ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) { #ifdef MOTH_THREADED_INTERPRETER instr.common.code = VME::instructionJumpTable()[static_cast(type)]; @@ -71,8 +324,7 @@ int InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) instr.common.instructionType = type; #endif - // XXX - int is wrong size for both of these - int ptrOffset = (int)(_ccode - _code); + ptrdiff_t ptrOffset = _ccode - _code; int size = Instr::size(type); ::memcpy(_ccode, reinterpret_cast(&instr), size); diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index d8e56cac70..009d0b3a36 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -34,19 +34,26 @@ private: Instruction(); }; + void simpleMove(IR::Move *); + void call(IR::ExprList *); + template - inline int addInstruction(const InstrData &data); - int addInstructionHelper(Instr::Type type, Instr &instr); + inline ptrdiff_t addInstruction(const InstrData &data); + ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); + VM::ExecutionEngine *_engine; IR::Function *_function; IR::BasicBlock *_block; + QHash > _patches; + QHash _addrs; + uchar *_code; uchar *_ccode; }; template -int InstructionSelection::addInstruction(const InstrData &data) +ptrdiff_t InstructionSelection::addInstruction(const InstrData &data) { Instr genericInstr; InstrMeta::setData(genericInstr, data); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index c14a61cb9c..98ea922e7e 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -38,7 +38,21 @@ using namespace QQmlJS::Moth; #endif -void VME::operator()(QQmlJS::VM::Context *, const uchar *code +static inline VM::Value *tempValue(QQmlJS::VM::Context *context, QVector &stack, int index) +{ + if (index < 0) { + const int arg = -index - 1; + return context->arguments + arg; + } else if (index < stack.count()) { + return stack.data() + index; + } else { + return context->locals + index - stack.count(); + } +} + +#define TEMP(index) *tempValue(context, stack, index) + +void VME::operator()(QQmlJS::VM::Context *context, const uchar *basecode #ifdef MOTH_THREADED_INTERPRETER , void ***storeJumpTable #endif @@ -57,6 +71,11 @@ void VME::operator()(QQmlJS::VM::Context *, const uchar *code } #endif + const uchar *code = basecode; + + QVector stack; + VM::Value tempRegister; + #ifdef MOTH_THREADED_INTERPRETER const Instr *genericInstr = reinterpret_cast(code); goto *genericInstr->common.code; @@ -66,13 +85,76 @@ void VME::operator()(QQmlJS::VM::Context *, const uchar *code switch (genericInstr->common.instructionType) { #endif - MOTH_BEGIN_INSTR(Nop) - qWarning("NOP"); - MOTH_END_INSTR(Nop) + MOTH_BEGIN_INSTR(StoreTemp) + TEMP(instr.tempIndex) = tempRegister; + MOTH_END_INSTR(StoreTemp) + + MOTH_BEGIN_INSTR(LoadTemp) + tempRegister = TEMP(instr.tempIndex); + MOTH_END_INSTR(LoadTemp) + + MOTH_BEGIN_INSTR(MoveTemp) + TEMP(instr.toTempIndex) = TEMP(instr.fromTempIndex); + MOTH_END_INSTR(MoveTemp) + + MOTH_BEGIN_INSTR(LoadUndefined) + tempRegister = VM::Value::undefinedValue(); + MOTH_END_INSTR(LoadUndefined) + + MOTH_BEGIN_INSTR(LoadNull) + tempRegister = VM::Value::nullValue(); + MOTH_END_INSTR(LoadNull) + + MOTH_BEGIN_INSTR(LoadTrue) + tempRegister = VM::Value::fromBoolean(true); + MOTH_END_INSTR(LoadTrue) + + MOTH_BEGIN_INSTR(LoadFalse) + tempRegister = VM::Value::fromBoolean(false); + MOTH_END_INSTR(LoadFalse) - MOTH_BEGIN_INSTR(Done) + MOTH_BEGIN_INSTR(LoadNumber) + tempRegister = VM::Value::fromNumber(instr.value); + MOTH_END_INSTR(LoadNumber) + + MOTH_BEGIN_INSTR(LoadString) + tempRegister = VM::Value::fromString(instr.value); + MOTH_END_INSTR(LoadString) + + MOTH_BEGIN_INSTR(LoadClosure) + __qmljs_init_closure(context, &tempRegister, instr.value); + MOTH_END_INSTR(LoadClosure) + + MOTH_BEGIN_INSTR(LoadName) + __qmljs_get_activation_property(context, &tempRegister, instr.value); + MOTH_END_INSTR(LoadName) + + MOTH_BEGIN_INSTR(Push) + stack.resize(instr.value); + MOTH_END_INSTR(Push) + + MOTH_BEGIN_INSTR(Call) + VM::Value *args = stack.data() + instr.args; + __qmljs_call_value(context, &tempRegister, /*thisObject=*/0, &tempRegister, args, instr.argc); + MOTH_END_INSTR(Call) + + MOTH_BEGIN_INSTR(Jump) + code = basecode + instr.offset; + MOTH_END_INSTR(Jump) + + MOTH_BEGIN_INSTR(CJump) + if (__qmljs_to_boolean(context, &tempRegister)) + code = basecode + instr.offset; + MOTH_END_INSTR(CJump) + + MOTH_BEGIN_INSTR(Binop) + instr.alu(context, &tempRegister, &TEMP(instr.lhsTempIndex), &TEMP(instr.rhsTempIndex)); + MOTH_END_INSTR(Binop) + + MOTH_BEGIN_INSTR(Ret) + context->result = TEMP(instr.tempIndex); return; - MOTH_END_INSTR(Done) + MOTH_END_INSTR(Ret) #ifdef MOTH_THREADED_INTERPRETER // nothing to do @@ -98,3 +180,9 @@ void **VME::instructionJumpTable() } #endif +void VME::exec(VM::Context *ctxt, const uchar *code) +{ + VME vme; + vme(ctxt, code); +} + diff --git a/moth/qv4vme_moth_p.h b/moth/qv4vme_moth_p.h index 5055407ca2..db1507d84d 100644 --- a/moth/qv4vme_moth_p.h +++ b/moth/qv4vme_moth_p.h @@ -10,6 +10,8 @@ namespace Moth { class VME { public: + static void exec(VM::Context *, const uchar *); + void operator()(QQmlJS::VM::Context *, const uchar *code #ifdef MOTH_THREADED_INTERPRETER , void ***storeJumpTable = 0 diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 94df898277..9746d8d358 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -185,7 +185,7 @@ ScriptFunction::~ScriptFunction() void ScriptFunction::call(VM::Context *ctx) { - function->code(ctx); + function->code(ctx, function->codeData); } void ScriptFunction::construct(VM::Context *ctx) @@ -195,7 +195,7 @@ void ScriptFunction::construct(VM::Context *ctx) if (proto.isObject()) obj->prototype = proto.objectValue; __qmljs_init_object(&ctx->thisObject, obj); - function->code(ctx); + function->code(ctx, function->codeData); } Value *ActivationObject::getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes) diff --git a/qv4ir_p.h b/qv4ir_p.h index ef9d801310..67b26d556a 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -505,7 +505,7 @@ struct Jump: Stmt { }; struct CJump: Stmt { - Expr *cond; + Expr *cond; // Temp, Binop BasicBlock *iftrue; BasicBlock *iffalse; @@ -558,7 +558,10 @@ struct Function { QList formals; QList locals; IR::BasicBlock *handlersBlock; - void (*code)(VM::Context *); + + void (*code)(VM::Context *, const uchar *); + const uchar *codeData; + bool hasDirectEval: 1; bool hasNestedFunctions: 1; @@ -571,6 +574,7 @@ struct Function { , maxNumberOfArguments(0) , handlersBlock(0) , code(0) + , codeData(0) , hasDirectEval(false) , hasNestedFunctions(false) { this->name = newString(name); } diff --git a/qv4isel_x86_64.cpp b/qv4isel_x86_64.cpp index 2a18ba962e..8846c29004 100644 --- a/qv4isel_x86_64.cpp +++ b/qv4isel_x86_64.cpp @@ -108,7 +108,7 @@ void InstructionSelection::operator()(IR::Function *function) _code = _codePtr; _code = (uchar *) ((size_t(_code) + 15) & ~15); - _function->code = (void (*)(VM::Context *)) _code; + _function->code = (void (*)(VM::Context *, const uchar *)) _code; _codePtr = _code; _patches.clear(); _addrs.clear(); -- cgit v1.2.3 From 4cf98a8c41a484eb75d1e88ec68b25c5dd8ff7db Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Tue, 12 Jun 2012 18:26:29 +0100 Subject: Fix bug in jump logic Make the jump relative to the offset value in the instruction stream. --- moth/qv4isel_moth.cpp | 2 +- moth/qv4vme_moth.cpp | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 95db21620d..2c1f5aff4b 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -45,7 +45,7 @@ void InstructionSelection::operator()(IR::Function *function) for (int ii = 0; ii < patchList.count(); ++ii) { ptrdiff_t patch = patchList.at(ii); - *((ptrdiff_t *)(_code + patch)) = target; + *((ptrdiff_t *)(_code + patch)) = target - patch; } } diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 98ea922e7e..8ad65a2f66 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -52,7 +52,7 @@ static inline VM::Value *tempValue(QQmlJS::VM::Context *context, QVector stack; VM::Value tempRegister; @@ -139,12 +137,12 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *basecode MOTH_END_INSTR(Call) MOTH_BEGIN_INSTR(Jump) - code = basecode + instr.offset; + code = ((uchar *)&instr.offset) + instr.offset; MOTH_END_INSTR(Jump) MOTH_BEGIN_INSTR(CJump) if (__qmljs_to_boolean(context, &tempRegister)) - code = basecode + instr.offset; + code = ((uchar *)&instr.offset) + instr.offset; MOTH_END_INSTR(CJump) MOTH_BEGIN_INSTR(Binop) -- cgit v1.2.3 From 9c9740fcdac50de0e64189e3092220588b598e78 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 13 Jun 2012 10:26:10 +0200 Subject: Remove dead code --- qmljs_runtime.cpp | 7 ------- qmljs_runtime.h | 1 - 2 files changed, 8 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 05cdea1843..1c63e5c690 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -913,13 +913,6 @@ void __qmljs_set_activation_element(Context *ctx, String *name, Value *index, Va } } -void __qmljs_set_activation_element_number(Context *ctx, String *name, Value *index, double number) -{ - Value v; - __qmljs_init_number(&v, number); - __qmljs_set_activation_element(ctx, name, index, &v); -} - void __qmljs_set_activation_property(Context *ctx, String *name, Value *value) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) { diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 63034ecca8..0cbc7d95c1 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -129,7 +129,6 @@ void __qmljs_set_element(Context *ctx, Value *object, Value *index, Value *value void __qmljs_set_element_number(Context *ctx, Value *object, Value *index, double number); void __qmljs_set_activation_element(Context *ctx, String *name, Value *index, Value *value); -void __qmljs_set_activation_element_number(Context *ctx, String *name, Value *index, double number); // context void __qmljs_get_activation(Context *ctx, Value *result); -- cgit v1.2.3 From d07b5b6cc9af0609931c78828f68a6391d7b7202 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 13 Jun 2012 10:45:12 +0200 Subject: Remove specialized version of __qmljs_set --- qmljs_runtime.h | 12 +------ qv4codegen.cpp | 28 ++++------------ qv4ir_p.h | 5 +++ qv4isel_x86_64.cpp | 93 ++++++------------------------------------------------ 4 files changed, 23 insertions(+), 115 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 0cbc7d95c1..8a266b395e 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -109,24 +109,14 @@ void __qmljs_new_object(Context *ctx, Value *result); void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean); void __qmljs_new_number_object(Context *ctx, Value *result, double n); void __qmljs_new_string_object(Context *ctx, Value *result, String *string); -void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value); -void __qmljs_set_property_boolean(Context *ctx, Value *object, String *name, bool value); -void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double value); -void __qmljs_set_property_string(Context *ctx, Value *object, String *name, String *value); -void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR::Function *function); void __qmljs_set_activation_property(Context *ctx, String *name, Value *value); -void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool value); -void __qmljs_set_activation_property_number(Context *ctx, String *name, double value); -void __qmljs_set_activation_property_string(Context *ctx, String *name, String *value); -void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Function *function); +void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value); void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name); void __qmljs_get_activation_property(Context *ctx, Value *result, String *name); void __qmljs_copy_activation_property(Context *ctx, String *name, String *other); void __qmljs_get_element(Context *ctx, Value *result, Value *object, Value *index); - void __qmljs_set_element(Context *ctx, Value *object, Value *index, Value *value); -void __qmljs_set_element_number(Context *ctx, Value *object, Value *index, double number); void __qmljs_set_activation_element(Context *ctx, String *name, Value *index, Value *value); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index d3a1cea4f9..5c47764320 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -445,28 +445,14 @@ IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args) void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) { - if (op != IR::OpInvalid) { - if (! (source->asTemp() /*|| source->asConst()*/)) { - const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), source); - _block->MOVE(target, _block->TEMP(t), op); - return; - } - } else if (target->asMember() || target->asSubscript()) { - if (! (source->asTemp() /*|| source->asConst()*/)) { - const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), source); - _block->MOVE(target, _block->TEMP(t), op); - return; - } - } else if (! target->asTemp()) { - if (! (source->asConst() || source->asTemp() || source->asName() || source->asSubscript())) { - const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), source); - _block->MOVE(target, _block->TEMP(t), op); - return; - } + assert(target->isLValue()); + + if (! source->asTemp()) { + unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), source); + source = _block->TEMP(t); } + _block->MOVE(target, source, op); } diff --git a/qv4ir_p.h b/qv4ir_p.h index 67b26d556a..15cf7ff17c 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -181,6 +181,7 @@ struct StmtVisitor { struct Expr { virtual ~Expr() {} virtual void accept(ExprVisitor *) = 0; + virtual bool isLValue() { return false; } virtual Const *asConst() { return 0; } virtual String *asString() { return 0; } virtual Name *asName() { return 0; } @@ -255,6 +256,7 @@ struct Name: Expr { void init(Builtin builtin, quint32 line, quint32 column); virtual void accept(ExprVisitor *v) { v->visitName(this); } + virtual bool isLValue() { return true; } virtual Name *asName() { return this; } virtual void dump(QTextStream &out); @@ -269,6 +271,7 @@ struct Temp: Expr { } virtual void accept(ExprVisitor *v) { v->visitTemp(this); } + virtual bool isLValue() { return true; } virtual Temp *asTemp() { return this; } virtual void dump(QTextStream &out); @@ -377,6 +380,7 @@ struct Subscript: Expr { } virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } + virtual bool isLValue() { return true; } virtual Subscript *asSubscript() { return this; } virtual void dump(QTextStream &out); @@ -393,6 +397,7 @@ struct Member: Expr { } virtual void accept(ExprVisitor *v) { v->visitMember(this); } + virtual bool isLValue() { return true; } virtual Member *asMember() { return this; } virtual void dump(QTextStream &out); diff --git a/qv4isel_x86_64.cpp b/qv4isel_x86_64.cpp index 8846c29004..c78d9ecbd0 100644 --- a/qv4isel_x86_64.cpp +++ b/qv4isel_x86_64.cpp @@ -469,53 +469,16 @@ void InstructionSelection::visitMove(IR::Move *s) if (IR::Name *n = s->target->asName()) { String *propertyName = identifier(*n->id); - if (IR::Const *c = s->source->asConst()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); - - switch (c->type) { - case IR::BoolType: - amd64_mov_reg_imm(_codePtr, AMD64_RDX, c->value != 0); - qmljs_call_code(_codePtr, __qmljs_set_activation_property_boolean); - break; - - case IR::NumberType: - amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); - qmljs_call_code(_codePtr, __qmljs_set_activation_property_number); - break; - - default: - Q_UNIMPLEMENTED(); - assert(!"TODO"); - } - } else if (IR::String *str = s->source->asString()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, _engine->newString(*str->value)); - qmljs_call_code(_codePtr, __qmljs_set_activation_property_string); - } else if (IR::Temp *t = s->source->asTemp()) { + if (IR::Temp *t = s->source->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); loadTempAddress(AMD64_RDX, t); qmljs_call_code(_codePtr, __qmljs_set_activation_property); - } else if (IR::Name *other = s->source->asName()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*other->id)); - qmljs_call_code(_codePtr, __qmljs_copy_activation_property); - } else if (IR::Closure *clos = s->source->asClosure()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, clos->value); - qmljs_call_code(_codePtr, __qmljs_set_activation_property_closure); + checkExceptions(); + return; } else { - Q_UNIMPLEMENTED(); - assert(!"TODO"); + Q_UNREACHABLE(); } - - checkExceptions(); - return; } else if (IR::Temp *t = s->target->asTemp()) { if (IR::Name *n = s->source->asName()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); @@ -733,38 +696,17 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Member *m = s->target->asMember()) { if (IR::Temp *base = m->base->asTemp()) { - if (IR::Const *c = s->source->asConst()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, base); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); - amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); - qmljs_call_code(_codePtr, __qmljs_set_property_number); - } else if (IR::String *str = s->source->asString()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, base); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, _engine->newString(*str->value)); - qmljs_call_code(_codePtr, __qmljs_set_property_string); - } else if (IR::Temp *t = s->source->asTemp()) { - // __qmljs_set_property(ctx, object, name, value); + if (IR::Temp *t = s->source->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, base); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); loadTempAddress(AMD64_RCX, t); qmljs_call_code(_codePtr, __qmljs_set_property); - } else if (IR::Closure *clos = s->source->asClosure()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, base); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, clos->value); - qmljs_call_code(_codePtr, __qmljs_set_property_closure); + checkExceptions(); + return; } else { - Q_UNIMPLEMENTED(); - assert(!"TODO"); + Q_UNREACHABLE(); } - checkExceptions(); - return; } } else if (IR::Subscript *ss = s->target->asSubscript()) { if (IR::Temp *t2 = s->source->asTemp()) { @@ -772,26 +714,11 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RDX, ss->index->asTemp()); loadTempAddress(AMD64_RCX, t2); qmljs_call_code(_codePtr, __qmljs_set_element); - } else if (IR::Const *c = s->source->asConst()) { - if (c->type == IR::NumberType) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, ss->base->asTemp()); - loadTempAddress(AMD64_RDX, ss->index->asTemp()); - amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); - qmljs_call_code(_codePtr, __qmljs_set_element_number); - } else { - s->dump(qout, IR::Stmt::MIR); - qout << endl; - Q_UNIMPLEMENTED(); - assert(!"TODO"); - } + checkExceptions(); + return; } else { Q_UNIMPLEMENTED(); - assert(!"TODO"); } - checkExceptions(); - return; } } else { // inplace assignment, e.g. x += 1, ++x, ... -- cgit v1.2.3 From 93e43fb3280906904e9659550a23c372f7a7abb3 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 13 Jun 2012 11:02:23 +0200 Subject: Add support for global, eval and function code. --- main.cpp | 3 +++ qmljs_runtime.cpp | 1 + qv4codegen.cpp | 34 ++++++++++++++++++++-------------- qv4codegen_p.h | 11 +++++++++-- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/main.cpp b/main.cpp index 777c8999c3..924a7a95dc 100644 --- a/main.cpp +++ b/main.cpp @@ -246,6 +246,9 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; else std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; + } else if (! ctx->result.isUndefined()) { + if (! qgetenv("SHOW_EXIT_VALUE").isNull()) + std::cout << "exit value: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; } delete[] ctx->locals; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 1c63e5c690..b4dfcd4704 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -295,6 +295,7 @@ void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionO { engine = e; parent = f->scope; + __qmljs_init_undefined(&result); if (f->needsActivation) __qmljs_init_object(&activation, engine->newActivationObject(this)); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 5c47764320..20bb81f2ef 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -263,13 +263,14 @@ Codegen::Codegen() , _throwBlock(0) , _handlersBlock(0) , _returnAddress(0) + , _mode(GlobalCode) , _env(0) , _loop(0) , _labelledStatement(0) { } -IR::Function *Codegen::operator()(Program *node, IR::Module *module) +IR::Function *Codegen::operator()(Program *node, IR::Module *module, Mode mode) { _module = module; _env = 0; @@ -277,7 +278,7 @@ IR::Function *Codegen::operator()(Program *node, IR::Module *module) ScanFunctions scan(this); scan(node); - IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, node->elements); + IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, node->elements, mode); foreach (IR::Function *function, _module->functions) { linearize(function); @@ -479,7 +480,13 @@ void Codegen::statement(Statement *ast) void Codegen::statement(ExpressionNode *ast) { - if (ast) { + if (! ast) { + return; + } else if (_mode != FunctionCode) { + Result e = expression(ast); + if (*e) + _block->MOVE(_block->TEMP(_returnAddress), *e); + } else { Result r(nx); qSwap(_expr, r); accept(ast); @@ -490,7 +497,7 @@ void Codegen::statement(ExpressionNode *ast) } else if (r->asTemp()) { // there is nothing to do } else { - int t = _block->newTemp(); + unsigned t = _block->newTemp(); _block->MOVE(_block->TEMP(t), *r); } } @@ -1012,14 +1019,8 @@ bool Codegen::visit(FieldMemberExpression *ast) bool Codegen::visit(FunctionExpression *ast) { - IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0, false); - if (_expr.accept(nx)) { - const int index = _env->findMember(ast->name.toString()); - assert(index != -1); - move(_block->TEMP(index), _block->CLOSURE(function)); - } else { - _expr.code = _block->CLOSURE(function); - } + IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + _expr.code = _block->CLOSURE(function); return false; } @@ -1408,8 +1409,10 @@ void Codegen::linearize(IR::Function *function) IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, - AST::SourceElements *body, bool /*isDeclaration*/) + AST::SourceElements *body, Mode mode) { + qSwap(_mode, mode); // enter function code. + enterEnvironment(ast); IR::Function *function = _module->newFunction(name); IR::BasicBlock *entryBlock = function->newBasicBlock(); @@ -1451,7 +1454,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, foreach (AST::FunctionDeclaration *f, _env->functions) { IR::Function *function = defineFunction(f->name.toString(), f, f->formals, - f->body ? f->body->elements : 0, true); + f->body ? f->body->elements : 0); _block->MOVE(_block->TEMP(_env->findMember(f->name.toString())), _block->CLOSURE(function)); } @@ -1476,6 +1479,9 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, qSwap(_returnAddress, returnAddress); leaveEnvironment(); + + qSwap(_mode, mode); + return function; } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index ebebe65033..abbb11c20f 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -15,7 +15,13 @@ class Codegen: protected AST::Visitor public: Codegen(); - IR::Function *operator()(AST::Program *ast, IR::Module *module); + enum Mode { + GlobalCode, + EvalCode, + FunctionCode + }; + + IR::Function *operator()(AST::Program *ast, IR::Module *module, Mode mode = GlobalCode); protected: enum Format { ex, cx, nx }; @@ -138,7 +144,7 @@ protected: void linearize(IR::Function *function); IR::Function *defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, - AST::SourceElements *body, bool isDeclaration = false); + AST::SourceElements *body, Mode mode = FunctionCode); int indexOfLocal(const QStringRef &string) const; int indexOfArgument(const QStringRef &string) const; @@ -269,6 +275,7 @@ private: IR::BasicBlock *_throwBlock; IR::BasicBlock *_handlersBlock; unsigned _returnAddress; + Mode _mode; Environment *_env; Loop *_loop; AST::LabelledStatement *_labelledStatement; -- cgit v1.2.3 From ad84511216fbf3f6062046dc632f0ab18c0b80a4 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 13 Jun 2012 11:19:15 +0200 Subject: Remember the value of the last executed expression only in EvalMode --- qv4codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 20bb81f2ef..d108a42c15 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -482,7 +482,7 @@ void Codegen::statement(ExpressionNode *ast) { if (! ast) { return; - } else if (_mode != FunctionCode) { + } else if (_mode == EvalCode) { Result e = expression(ast); if (*e) _block->MOVE(_block->TEMP(_returnAddress), *e); -- cgit v1.2.3 From 332c5987c49a28445d6688e43f98fdd87124791f Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 26 Jun 2012 13:36:37 +0200 Subject: Reduce the number of register-to-register copies. --- qv4codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index d108a42c15..c1c2ca3436 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -448,7 +448,7 @@ void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) { assert(target->isLValue()); - if (! source->asTemp()) { + if (! source->asTemp() && (op != IR::OpInvalid || ! target->asTemp())) { unsigned t = _block->newTemp(); _block->MOVE(_block->TEMP(t), source); source = _block->TEMP(t); -- cgit v1.2.3 From 6053a2d22f7b605a98050ba1b9c1a18d1ecb0b92 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 26 Jun 2012 13:43:00 +0200 Subject: Remove dead code. --- qv4codegen.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index c1c2ca3436..661d7800fd 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -16,15 +16,6 @@ using namespace AST; namespace { QTextStream qout(stdout, QIODevice::WriteOnly); -void edge(IR::BasicBlock *source, IR::BasicBlock *target) -{ - if (! source->out.contains(target)) - source->out.append(target); - - if (! target->in.contains(source)) - target->in.append(source); -} - void dfs(IR::BasicBlock *block, QSet *V, QVector *blocks) -- cgit v1.2.3 From 48a0b3c96b67ab2afd8c7f23c0afd946dabdca73 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 26 Jun 2012 15:17:31 +0200 Subject: Do not try to optimize the `global object'. This should simplify the implementation of `eval' and `with'. --- main.cpp | 26 ++++++++------------------ qmljs_objects.cpp | 2 +- qv4codegen.cpp | 45 +++++++++++++++++++++++++-------------------- qv4codegen_p.h | 1 - tests/crypto.js | 10 +++++----- 5 files changed, 39 insertions(+), 45 deletions(-) diff --git a/main.cpp b/main.cpp index 924a7a95dc..c9afd49914 100644 --- a/main.cpp +++ b/main.cpp @@ -224,15 +224,7 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS } VM::Context *ctx = vm->rootContext; - - ctx->varCount = globalCode->locals.size(); - if (ctx->varCount) { - ctx->locals = new VM::Value[ctx->varCount]; - ctx->vars = new VM::String*[ctx->varCount]; - std::fill(ctx->locals, ctx->locals + ctx->varCount, VM::Value::undefinedValue()); - for (unsigned int i = 0; i < ctx->varCount; ++i) - ctx->vars[i] = ctx->engine->identifier(*globalCode->locals.at(i)); - } + ctx->hasUncaughtException = false; if (useMoth) { Moth::VME vme; @@ -250,9 +242,6 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS if (! qgetenv("SHOW_EXIT_VALUE").isNull()) std::cout << "exit value: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; } - - delete[] ctx->locals; - delete[] ctx->vars; } int main(int argc, char *argv[]) @@ -276,17 +265,18 @@ int main(int argc, char *argv[]) } #endif + QQmlJS::VM::ExecutionEngine vm; + QQmlJS::VM::Context *ctx = vm.rootContext; + + QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue; + globalObject->setProperty(ctx, vm.identifier(QStringLiteral("print")), + QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); + foreach (const QString &fn, args) { QFile file(fn); if (file.open(QFile::ReadOnly)) { const QString code = QString::fromUtf8(file.readAll()); file.close(); - QQmlJS::VM::ExecutionEngine vm; - QQmlJS::VM::Context *ctx = vm.rootContext; - - QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue; - globalObject->setProperty(ctx, vm.identifier(QStringLiteral("print")), - QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); evaluate(&vm, fn, code); } diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 9746d8d358..e21e7d007f 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -305,7 +305,7 @@ ExecutionEngine::ExecutionEngine() // // set up the global object // - VM::Object *glo = newActivationObject(rootContext); + VM::Object *glo = newObject(/*rootContext*/); __qmljs_init_object(&globalObject, glo); __qmljs_init_object(&rootContext->activation, glo); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 661d7800fd..f976202c11 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -322,13 +322,13 @@ IR::Expr *Codegen::subscript(IR::Expr *base, IR::Expr *index) { if (! base->asTemp()) { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), base); + move(_block->TEMP(t), base); base = _block->TEMP(t); } if (! index->asTemp()) { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), index); + move(_block->TEMP(t), index); index = _block->TEMP(t); } @@ -431,7 +431,7 @@ IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args) if (base->asMember() || base->asName() || base->asTemp()) return _block->CALL(base, args); const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), base); + move(_block->TEMP(t), base); return _block->CALL(_block->TEMP(t), args); } @@ -452,7 +452,7 @@ void Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffa { if (! (cond->asTemp() || cond->asBinop())) { const unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), cond); + move(_block->TEMP(t), cond); cond = _block->TEMP(t); } _block->CJUMP(cond, iftrue, iffalse); @@ -476,7 +476,7 @@ void Codegen::statement(ExpressionNode *ast) } else if (_mode == EvalCode) { Result e = expression(ast); if (*e) - _block->MOVE(_block->TEMP(_returnAddress), *e); + move(_block->TEMP(_returnAddress), *e); } else { Result r(nx); qSwap(_expr, r); @@ -489,7 +489,7 @@ void Codegen::statement(ExpressionNode *ast) // there is nothing to do } else { unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), *r); + move(_block->TEMP(t), *r); } } } @@ -574,13 +574,23 @@ void Codegen::sourceElements(SourceElements *ast) void Codegen::variableDeclaration(VariableDeclaration *ast) { + IR::Expr *initializer = 0; if (ast->expression) { Result expr = expression(ast->expression); assert(expr.code); + initializer = *expr; + } + + if (! initializer) + initializer = _block->CONST(IR::UndefinedType, 0); + if (! _env->parent) { + // it's global code. + move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), initializer); + } else { const int index = _env->findMember(ast->name.toString()); assert(index != -1); - move(_block->TEMP(index), *expr); + move(_block->TEMP(index), initializer); } } @@ -1017,7 +1027,7 @@ bool Codegen::visit(FunctionExpression *ast) bool Codegen::visit(IdentifierExpression *ast) { - if (! _function->hasDirectEval) { + if (! _function->hasDirectEval && _env->parent) { int index = _env->findMember(ast->name.toString()); if (index != -1) { _expr.code = _block->TEMP(index); @@ -1446,8 +1456,12 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, foreach (AST::FunctionDeclaration *f, _env->functions) { IR::Function *function = defineFunction(f->name.toString(), f, f->formals, f->body ? f->body->elements : 0); - _block->MOVE(_block->TEMP(_env->findMember(f->name.toString())), - _block->CLOSURE(function)); + if (! _env->parent) + move(_block->NAME(f->name.toString(), f->identifierToken.startLine, f->identifierToken.startColumn), + _block->CLOSURE(function)); + else + move(_block->TEMP(_env->findMember(f->name.toString())), + _block->CLOSURE(function)); } sourceElements(body); @@ -1476,15 +1490,6 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, return function; } -int Codegen::indexOfLocal(const QStringRef &string) const -{ - for (int i = 0; i < _function->locals.size(); ++i) { - if (*_function->locals.at(i) == string) - return i; - } - return -1; -} - int Codegen::indexOfArgument(const QStringRef &string) const { for (int i = 0; i < _function->formals.size(); ++i) { @@ -1806,7 +1811,7 @@ bool Codegen::visit(SwitchStatement *ast) bool Codegen::visit(ThrowStatement *ast) { Result expr = expression(ast->expression); - _block->MOVE(_block->TEMP(_returnAddress), *expr); + move(_block->TEMP(_returnAddress), *expr); _block->JUMP(_throwBlock); return false; } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index abbb11c20f..7e0d5c798f 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -145,7 +145,6 @@ protected: void linearize(IR::Function *function); IR::Function *defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::SourceElements *body, Mode mode = FunctionCode); - int indexOfLocal(const QStringRef &string) const; int indexOfArgument(const QStringRef &string) const; void statement(AST::Statement *ast); diff --git a/tests/crypto.js b/tests/crypto.js index d76e22ef0d..a7d9d9f587 100644 --- a/tests/crypto.js +++ b/tests/crypto.js @@ -69,7 +69,7 @@ function BigInteger(a,b,c) { } // return new, unset BigInteger -function nbi() { return new BigInteger(null,uu,uu); } +function nbi() { return new BigInteger(null); } // am: Compute w_j += (x*this_i), propagate carries, // c is initial carry, returns final carry. @@ -1455,10 +1455,10 @@ function SecureRandom() {} SecureRandom.prototype.nextBytes = rng_get_bytes; // Depends on jsbn.js and rng.js -var uu + // convert a (hex) string to a bignum object function parseBigInt(str,r) { - return new BigInteger(str,r,uu); + return new BigInteger(str,r); } function linebrk(s,n) { @@ -1497,7 +1497,7 @@ function pkcs1pad2(s,n) { } ba[--n] = 2; ba[--n] = 0; - return new BigInteger(ba,uu,uu); + return new BigInteger(ba); } // "empty" RSA key constructor @@ -1600,7 +1600,7 @@ function RSAGenerate(B,E) { var rng = new SecureRandom(); var qs = B>>1; this.e = parseInt(E,16); - var ee = new BigInteger(E,16,uu); + var ee = new BigInteger(E,16); for(;;) { for(;;) { this.p = new BigInteger(B-qs,1,rng); -- cgit v1.2.3 From a1eb099a4a7fda46cc1b5e0ecb34cbde27ebaf30 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 26 Jun 2012 15:36:03 +0200 Subject: Initial work on `eval'. --- main.cpp | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/main.cpp b/main.cpp index c9afd49914..6f337178a9 100644 --- a/main.cpp +++ b/main.cpp @@ -42,6 +42,9 @@ static inline bool protect(const void *addr, size_t size) return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; } +static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QString &source, + QQmlJS::Codegen::Mode mode = QQmlJS::Codegen::GlobalCode); + namespace builtins { using namespace QQmlJS::VM; @@ -62,6 +65,17 @@ struct Print: FunctionObject } }; +struct Eval: FunctionObject +{ + Eval(Context *scope): FunctionObject(scope) {} + + virtual void call(Context *ctx) + { + const QString code = ctx->argument(0).toString(ctx)->toQString(); + evaluate(ctx, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode); + } +}; + } // builtins #ifndef QMLJS_NO_LLVM @@ -172,10 +186,12 @@ int evaluateCompiledCode(const QStringList &files) #endif -void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QString &source) +static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QString &source, + QQmlJS::Codegen::Mode mode) { using namespace QQmlJS; + VM::ExecutionEngine *vm = ctx->engine; IR::Module module; IR::Function *globalCode = 0; @@ -203,7 +219,7 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS Program *program = AST::cast(parser.rootNode()); Codegen cg; - globalCode = cg(program, &module); + globalCode = cg(program, &module, mode); if (useMoth) { Moth::InstructionSelection isel(vm, &module, code); @@ -223,8 +239,13 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS return; } - VM::Context *ctx = vm->rootContext; ctx->hasUncaughtException = false; + if (! ctx->activation.isObject()) + __qmljs_init_object(&ctx->activation, new QQmlJS::VM::Object()); + + foreach (const QString *local, globalCode->locals) { + ctx->activation.objectValue->setProperty(ctx, *local, QQmlJS::VM::Value::undefinedValue()); + } if (useMoth) { Moth::VME vme; @@ -272,13 +293,16 @@ int main(int argc, char *argv[]) globalObject->setProperty(ctx, vm.identifier(QStringLiteral("print")), QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); + globalObject->setProperty(ctx, vm.identifier(QStringLiteral("eval")), + QQmlJS::VM::Value::fromObject(new builtins::Eval(ctx))); + foreach (const QString &fn, args) { QFile file(fn); if (file.open(QFile::ReadOnly)) { const QString code = QString::fromUtf8(file.readAll()); file.close(); - evaluate(&vm, fn, code); + evaluate(vm.rootContext, fn, code); } } } -- cgit v1.2.3 From aaea2a9a964a587372c8dc2a249f4a7e7ccea4f3 Mon Sep 17 00:00:00 2001 From: laknoll Date: Wed, 19 Sep 2012 21:08:47 +0200 Subject: Proper NaN boxing for Value All JS types are now encoded in a 8 byte data structure. We use the 52 bits that are unused when a double is a NaN to encode all other types that can be stored inside a double. This is being done by using a few bits to determine the type, and up to 48 bits for data. This works even on x64, as addresses (ie. pointers) are limited to 48 bits on these platforms. For most other types (except doubles), we store the data in the lower 32 bits of the double. --- main.cpp | 6 +- moth/qv4vme_moth.cpp | 2 +- qmljs_objects.cpp | 26 +-- qmljs_runtime.cpp | 178 ++++++++--------- qmljs_runtime.h | 525 +++++++++++++++++++++++++++++++++------------------ qv4ecmaobjects.cpp | 342 ++++++++++++++++----------------- qv4ecmaobjects_p.h | 6 +- qv4isel_x86_64.cpp | 61 +++--- 8 files changed, 660 insertions(+), 486 deletions(-) diff --git a/main.cpp b/main.cpp index 6f337178a9..08917aee04 100644 --- a/main.cpp +++ b/main.cpp @@ -168,7 +168,7 @@ int evaluateCompiledCode(const QStringList &files) VM::ExecutionEngine vm; VM::Context *ctx = vm.rootContext; - QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue; + QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); globalObject->setProperty(ctx, vm.identifier(QStringLiteral("print")), QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); @@ -244,7 +244,7 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS __qmljs_init_object(&ctx->activation, new QQmlJS::VM::Object()); foreach (const QString *local, globalCode->locals) { - ctx->activation.objectValue->setProperty(ctx, *local, QQmlJS::VM::Value::undefinedValue()); + ctx->activation.objectValue()->setProperty(ctx, *local, QQmlJS::VM::Value::undefinedValue()); } if (useMoth) { @@ -289,7 +289,7 @@ int main(int argc, char *argv[]) QQmlJS::VM::ExecutionEngine vm; QQmlJS::VM::Context *ctx = vm.rootContext; - QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue; + QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); globalObject->setProperty(ctx, vm.identifier(QStringLiteral("print")), QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 8ad65a2f66..0b3ae0a7b3 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -112,7 +112,7 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_END_INSTR(LoadFalse) MOTH_BEGIN_INSTR(LoadNumber) - tempRegister = VM::Value::fromNumber(instr.value); + tempRegister = VM::Value::fromDouble(instr.value); MOTH_END_INSTR(LoadNumber) MOTH_BEGIN_INSTR(LoadString) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index e21e7d007f..280dbaaf98 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -112,7 +112,7 @@ void Object::defineOwnProperty(Context *ctx, const Value &getter, const Value &s Value ArrayObject::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) { if (name->isEqualTo(ctx->engine->id_length)) - return Value::fromNumber(value.size()); + return Value::fromDouble(value.size()); return Object::getProperty(ctx, name, attributes); } @@ -129,13 +129,13 @@ bool FunctionObject::hasInstance(Context *ctx, const Value &value) return false; } - Object *v = value.objectValue; + Object *v = value.objectValue(); while (v) { v = v->prototype; if (! v) break; - else if (o.objectValue == v) + else if (o.objectValue() == v) return true; } @@ -193,7 +193,7 @@ void ScriptFunction::construct(VM::Context *ctx) Object *obj = ctx->engine->newObject(); Value proto = getProperty(ctx, ctx->engine->id_prototype); if (proto.isObject()) - obj->prototype = proto.objectValue; + obj->prototype = proto.objectValue(); __qmljs_init_object(&ctx->thisObject, obj); function->code(ctx, function->codeData); } @@ -220,7 +220,7 @@ Value *ActivationObject::getPropertyDescriptor(Context *ctx, String *name, Prope if (name->isEqualTo(ctx->engine->id_arguments)) { if (arguments.isUndefined()) { arguments = Value::fromObject(new ArgumentsObject(ctx)); - arguments.objectValue->prototype = ctx->engine->objectPrototype; + arguments.objectValue()->prototype = ctx->engine->objectPrototype; } return &arguments; @@ -234,7 +234,7 @@ Value *ActivationObject::getPropertyDescriptor(Context *ctx, String *name, Prope Value ArgumentsObject::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) { if (name->isEqualTo(ctx->engine->id_length)) - return Value::fromNumber(context->argumentCount); + return Value::fromDouble(context->argumentCount); return Object::getProperty(ctx, name, attributes); } @@ -285,13 +285,13 @@ ExecutionEngine::ExecutionEngine() dateCtor = Value::fromObject(new DateCtor(rootContext)); regExpCtor = Value::fromObject(new RegExpCtor(rootContext)); - stringCtor.objectValue->prototype = functionPrototype; - numberCtor.objectValue->prototype = functionPrototype; - booleanCtor.objectValue->prototype = functionPrototype; - arrayCtor.objectValue->prototype = functionPrototype; - functionCtor.objectValue->prototype = functionPrototype; - dateCtor.objectValue->prototype = functionPrototype; - regExpCtor.objectValue->prototype = functionPrototype; + stringCtor.objectValue()->prototype = functionPrototype; + numberCtor.objectValue()->prototype = functionPrototype; + booleanCtor.objectValue()->prototype = functionPrototype; + arrayCtor.objectValue()->prototype = functionPrototype; + functionCtor.objectValue()->prototype = functionPrototype; + dateCtor.objectValue()->prototype = functionPrototype; + regExpCtor.objectValue()->prototype = functionPrototype; objectPrototype->init(rootContext, objectCtor); stringPrototype->init(rootContext, stringCtor); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index b4dfcd4704..2ca6f8a579 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -124,8 +124,8 @@ String *Value::toString(Context *ctx) const { Value v; __qmljs_to_string(ctx, &v, this); - assert(v.is(STRING_TYPE)); - return v.stringValue; + assert(v.is(Value::String_Type)); + return v.stringValue(); } Value Value::toObject(Context *ctx) const @@ -137,97 +137,97 @@ Value Value::toObject(Context *ctx) const bool Value::isFunctionObject() const { - return type == OBJECT_TYPE ? objectValue->asFunctionObject() != 0 : false; + return isObject() ? objectValue()->asFunctionObject() != 0 : false; } bool Value::isBooleanObject() const { - return type == OBJECT_TYPE ? objectValue->asBooleanObject() != 0 : false; + return isObject() ? objectValue()->asBooleanObject() != 0 : false; } bool Value::isNumberObject() const { - return type == OBJECT_TYPE ? objectValue->asNumberObject() != 0 : false; + return isObject() ? objectValue()->asNumberObject() != 0 : false; } bool Value::isStringObject() const { - return type == OBJECT_TYPE ? objectValue->asStringObject() != 0 : false; + return isObject() ? objectValue()->asStringObject() != 0 : false; } bool Value::isDateObject() const { - return type == OBJECT_TYPE ? objectValue->asDateObject() != 0 : false; + return isObject() ? objectValue()->asDateObject() != 0 : false; } bool Value::isArrayObject() const { - return type == OBJECT_TYPE ? objectValue->asArrayObject() != 0 : false; + return isObject() ? objectValue()->asArrayObject() != 0 : false; } bool Value::isErrorObject() const { - return type == OBJECT_TYPE ? objectValue->asErrorObject() != 0 : false; + return isObject() ? objectValue()->asErrorObject() != 0 : false; } bool Value::isArgumentsObject() const { - return type == OBJECT_TYPE ? objectValue->asActivationObject() != 0 : false; + return isObject() ? objectValue()->asActivationObject() != 0 : false; } Object *Value::asObject() const { - return type == OBJECT_TYPE ? objectValue : 0; + return isObject() ? objectValue() : 0; } FunctionObject *Value::asFunctionObject() const { - return type == OBJECT_TYPE ? objectValue->asFunctionObject() : 0; + return isObject() ? objectValue()->asFunctionObject() : 0; } BooleanObject *Value::asBooleanObject() const { - return type == OBJECT_TYPE ? objectValue->asBooleanObject() : 0; + return isObject() ? objectValue()->asBooleanObject() : 0; } NumberObject *Value::asNumberObject() const { - return type == OBJECT_TYPE ? objectValue->asNumberObject() : 0; + return isObject() ? objectValue()->asNumberObject() : 0; } StringObject *Value::asStringObject() const { - return type == OBJECT_TYPE ? objectValue->asStringObject() : 0; + return isObject() ? objectValue()->asStringObject() : 0; } DateObject *Value::asDateObject() const { - return type == OBJECT_TYPE ? objectValue->asDateObject() : 0; + return isObject() ? objectValue()->asDateObject() : 0; } ArrayObject *Value::asArrayObject() const { - return type == OBJECT_TYPE ? objectValue->asArrayObject() : 0; + return isObject() ? objectValue()->asArrayObject() : 0; } ErrorObject *Value::asErrorObject() const { - return type == OBJECT_TYPE ? objectValue->asErrorObject() : 0; + return isObject() ? objectValue()->asErrorObject() : 0; } ActivationObject *Value::asArgumentsObject() const { - return type == OBJECT_TYPE ? objectValue->asActivationObject() : 0; + return isObject() ? objectValue()->asActivationObject() : 0; } Value Value::property(Context *ctx, String *name) const { - return isObject() ? objectValue->getProperty(ctx, name) : undefinedValue(); + return isObject() ? objectValue()->getProperty(ctx, name) : undefinedValue(); } Value *Value::getPropertyDescriptor(Context *ctx, String *name) const { - return isObject() ? objectValue->getPropertyDescriptor(ctx, name) : 0; + return isObject() ? objectValue()->getPropertyDescriptor(ctx, name) : 0; } void Context::init(ExecutionEngine *eng) @@ -237,9 +237,9 @@ void Context::init(ExecutionEngine *eng) arguments = 0; argumentCount = 0; locals = 0; - activation.type = NULL_TYPE; - thisObject.type = NULL_TYPE; - result.type = UNDEFINED_TYPE; + activation = Value::nullValue(); + thisObject = Value::nullValue(); + result = Value::undefinedValue(); formals = 0; formalCount = 0; vars = 0; @@ -251,8 +251,8 @@ void Context::init(ExecutionEngine *eng) Value *Context::lookupPropertyDescriptor(String *name) { for (Context *ctx = this; ctx; ctx = ctx->parent) { - if (ctx->activation.is(OBJECT_TYPE)) { - if (Value *prop = ctx->activation.objectValue->getPropertyDescriptor(this, name)) { + if (ctx->activation.is(Value::Object_Type)) { + if (Value *prop = ctx->activation.objectValue()->getPropertyDescriptor(this, name)) { return prop; } } @@ -346,13 +346,13 @@ void Context::initConstructorContext(ExecutionEngine *e, const Value *object, Fu void Context::leaveConstructorContext(FunctionObject *f, Value *returnValue) { - assert(thisObject.is(OBJECT_TYPE)); + assert(thisObject.is(Value::Object_Type)); result = thisObject; Value proto = f->getProperty(this, engine->id_prototype); - thisObject.objectValue->prototype = proto.objectValue; + thisObject.objectValue()->prototype = proto.objectValue(); if (! thisObject.isObject()) - thisObject.objectValue->prototype = engine->objectPrototype; + thisObject.objectValue()->prototype = engine->objectPrototype; leaveCallContext(f, returnValue); } @@ -418,7 +418,7 @@ void __qmljs_delete_subscript(Context *ctx, Value *result, Value *base, Value *i { if (ArrayObject *a = base->asArrayObject()) { if (index->isNumber()) { - const quint32 n = index->numberValue; + const quint32 n = index->doubleValue(); if (n < a->value.size()) { a->value.assign(n, Value::undefinedValue()); __qmljs_init_boolean(result, true); @@ -434,7 +434,7 @@ void __qmljs_delete_subscript(Context *ctx, Value *result, Value *base, Value *i void __qmljs_delete_member(Context *ctx, Value *result, Value *base, String *name) { Value obj = base->toObject(ctx); - __qmljs_init_boolean(result, obj.objectValue->deleteProperty(ctx, name, true)); + __qmljs_init_boolean(result, obj.objectValue()->deleteProperty(ctx, name, true)); } void __qmljs_delete_property(Context *ctx, Value *result, String *name) @@ -442,7 +442,7 @@ void __qmljs_delete_property(Context *ctx, Value *result, String *name) Value obj = ctx->activation; if (! obj.isObject()) obj = ctx->engine->globalObject; - __qmljs_init_boolean(result, obj.objectValue->deleteProperty(ctx, name, true)); + __qmljs_init_boolean(result, obj.objectValue()->deleteProperty(ctx, name, true)); } void __qmljs_delete_value(Context *ctx, Value *result, Value *value) @@ -456,12 +456,12 @@ void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Va Value pleft, pright; __qmljs_to_primitive(ctx, &pleft, left, PREFERREDTYPE_HINT); __qmljs_to_primitive(ctx, &pright, right, PREFERREDTYPE_HINT); - if (pleft.type == STRING_TYPE || pright.type == STRING_TYPE) { - if (pleft.type != STRING_TYPE) + if (pleft.isString() || pright.isString()) { + if (!pleft.isString()) __qmljs_to_string(ctx, &pleft, &pleft); - if (pright.type != STRING_TYPE) + if (!pright.isString()) __qmljs_to_string(ctx, &pright, &pright); - String *string = __qmljs_string_concat(ctx, pleft.stringValue, pright.stringValue); + String *string = __qmljs_string_concat(ctx, pleft.stringValue(), pright.stringValue()); __qmljs_init_string(result, string); } else { double x = __qmljs_to_number(ctx, &pleft); @@ -483,10 +483,10 @@ void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Va void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *right) { - if (right->type == OBJECT_TYPE) { + if (right->isObject()) { Value s; __qmljs_to_string(ctx, &s, left); - bool r = right->objectValue->hasProperty(ctx, s.stringValue); + bool r = right->objectValue()->hasProperty(ctx, s.stringValue()); __qmljs_init_boolean(result, r); } else { __qmljs_throw_type_error(ctx, result); @@ -558,7 +558,7 @@ void __qmljs_inplace_bit_and_element(Context *ctx, Value *base, Value *index, Va void __qmljs_inplace_bit_or_element(Context *ctx, Value *base, Value *index, Value *value) { - Object *obj = base->toObject(ctx).objectValue; + Object *obj = base->toObject(ctx).objectValue(); if (ArrayObject *a = obj->asArrayObject()) { if (index->isNumber()) { const quint32 idx = index->toUInt32(ctx); @@ -573,7 +573,7 @@ void __qmljs_inplace_bit_or_element(Context *ctx, Value *base, Value *index, Val void __qmljs_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, Value *value) { - Object *obj = base->toObject(ctx).objectValue; + Object *obj = base->toObject(ctx).objectValue(); if (ArrayObject *a = obj->asArrayObject()) { if (index->isNumber()) { const quint32 idx = index->toUInt32(ctx); @@ -588,7 +588,7 @@ void __qmljs_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, Va void __qmljs_inplace_add_element(Context *ctx, Value *base, Value *index, Value *value) { - Object *obj = base->toObject(ctx).objectValue; + Object *obj = base->toObject(ctx).objectValue(); if (ArrayObject *a = obj->asArrayObject()) { if (index->isNumber()) { const quint32 idx = index->toUInt32(ctx); @@ -603,7 +603,7 @@ void __qmljs_inplace_add_element(Context *ctx, Value *base, Value *index, Value void __qmljs_inplace_sub_element(Context *ctx, Value *base, Value *index, Value *value) { - Object *obj = base->toObject(ctx).objectValue; + Object *obj = base->toObject(ctx).objectValue(); if (ArrayObject *a = obj->asArrayObject()) { if (index->isNumber()) { const quint32 idx = index->toUInt32(ctx); @@ -663,16 +663,16 @@ void __qmljs_inplace_bit_xor_member(Context *ctx, Value *base, String *name, Val void __qmljs_inplace_add_member(Context *ctx, Value *base, String *name, Value *value) { - Value prop = base->objectValue->getProperty(ctx, name); + Value prop = base->objectValue()->getProperty(ctx, name); __qmljs_add(ctx, &prop, &prop, value); - base->objectValue->setProperty(ctx, name, prop); + base->objectValue()->setProperty(ctx, name, prop); } void __qmljs_inplace_sub_member(Context *ctx, Value *base, String *name, Value *value) { - Value prop = base->objectValue->getProperty(ctx, name); + Value prop = base->objectValue()->getProperty(ctx, name); __qmljs_sub(ctx, &prop, &prop, value); - base->objectValue->setProperty(ctx, name, prop); + base->objectValue()->setProperty(ctx, name, prop); } void __qmljs_inplace_mul_member(Context *ctx, Value *base, String *name, Value *value) @@ -752,7 +752,7 @@ String *__qmljs_string_concat(Context *ctx, String *first, String *second) bool __qmljs_is_function(Context *, const Value *value) { - return value->objectValue->asFunctionObject() != 0; + return value->objectValue()->asFunctionObject() != 0; } void __qmljs_object_default_value(Context *ctx, Value *result, const Value *object, int typeHint) @@ -831,14 +831,14 @@ void __qmljs_new_string_object(Context *ctx, Value *result, String *string) void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value) { - object->objectValue->setProperty(ctx, name, *value, /*flags*/ 0); + object->objectValue()->setProperty(ctx, name, *value, /*flags*/ 0); } void __qmljs_set_property_boolean(Context *ctx, Value *object, String *name, bool number) { Value value; __qmljs_init_boolean(&value, number); - object->objectValue->setProperty(ctx, name, value, /*flag*/ 0); + object->objectValue()->setProperty(ctx, name, value, /*flag*/ 0); } void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double number) @@ -846,7 +846,7 @@ void __qmljs_set_property_number(Context *ctx, Value *object, String *name, doub Q_UNUSED(ctx); Value value; __qmljs_init_number(&value, number); - object->objectValue->setProperty(ctx, name, value, /*flag*/ 0); + object->objectValue()->setProperty(ctx, name, value, /*flag*/ 0); } void __qmljs_set_property_string(Context *ctx, Value *object, String *name, String *s) @@ -854,26 +854,26 @@ void __qmljs_set_property_string(Context *ctx, Value *object, String *name, Stri Q_UNUSED(ctx); Value value; __qmljs_init_string(&value, s); - object->objectValue->setProperty(ctx, name, value, /*flag*/ 0); + object->objectValue()->setProperty(ctx, name, value, /*flag*/ 0); } void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR::Function *function) { Value value; __qmljs_init_closure(ctx, &value, function); - object->objectValue->setProperty(ctx, name, value, /*flag*/ 0); + object->objectValue()->setProperty(ctx, name, value, /*flag*/ 0); } void __qmljs_get_element(Context *ctx, Value *result, Value *object, Value *index) { if (object->isString() && index->isNumber()) { - const QString s = object->stringValue->toQString().mid(Value::toUInt32(index->numberValue), 1); + const QString s = object->stringValue()->toQString().mid(Value::toUInt32(index->doubleValue()), 1); if (s.isNull()) __qmljs_init_undefined(result); else *result = Value::fromString(ctx, s); } else if (object->isArrayObject() && index->isNumber()) { - *result = object->asArrayObject()->value.at(Value::toUInt32(index->numberValue)); + *result = object->asArrayObject()->value.at(Value::toUInt32(index->doubleValue())); } else { String *name = index->toString(ctx); @@ -887,14 +887,14 @@ void __qmljs_get_element(Context *ctx, Value *result, Value *object, Value *inde void __qmljs_set_element(Context *ctx, Value *object, Value *index, Value *value) { if (object->isArrayObject() && index->isNumber()) { - object->asArrayObject()->value.assign(Value::toUInt32(index->numberValue), *value); + object->asArrayObject()->value.assign(Value::toUInt32(index->doubleValue()), *value); } else { String *name = index->toString(ctx); if (! object->isObject()) __qmljs_to_object(ctx, object, object); - object->objectValue->setProperty(ctx, name, *value, /*flags*/ 0); + object->objectValue()->setProperty(ctx, name, *value, /*flags*/ 0); } } @@ -919,7 +919,7 @@ void __qmljs_set_activation_property(Context *ctx, String *name, Value *value) if (Value *prop = ctx->lookupPropertyDescriptor(name)) { *prop = *value; } else - ctx->engine->globalObject.objectValue->setProperty(ctx, name, *value); + ctx->engine->globalObject.objectValue()->setProperty(ctx, name, *value); } void __qmljs_copy_activation_property(Context *ctx, String *name, String *other) @@ -960,10 +960,10 @@ void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Fun void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name) { - if (object->type == OBJECT_TYPE) { + if (object->isObject()) { *result = object->property(ctx, name); - } else if (object->type == STRING_TYPE && name->isEqualTo(ctx->engine->id_length)) { - __qmljs_init_number(result, object->stringValue->toQString().length()); + } else if (object->isString() && name->isEqualTo(ctx->engine->id_length)) { + __qmljs_init_number(result, object->stringValue()->toQString().length()); } else { Value o; __qmljs_to_object(ctx, &o, object); @@ -1008,8 +1008,8 @@ void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y __qmljs_to_primitive(ctx, &px, y, NUMBER_HINT); } - if (px.type == STRING_TYPE && py.type == STRING_TYPE) { - bool r = __qmljs_string_compare(ctx, px.stringValue, py.stringValue); + if (px.isString() && py.isString()) { + bool r = __qmljs_string_compare(ctx, px.stringValue(), py.stringValue()); __qmljs_init_boolean(result, r); } else { double nx = __qmljs_to_number(ctx, &px); @@ -1024,49 +1024,51 @@ void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y bool __qmljs_equal(Context *ctx, const Value *x, const Value *y) { - if (x->type == y->type) { - switch ((ValueType) x->type) { - case UNDEFINED_TYPE: + if (x->type() == y->type()) { + switch (x->type()) { + case Value::Undefined_Type: return true; - case NULL_TYPE: + case Value::Null_Type: return true; - case BOOLEAN_TYPE: - return x->booleanValue == y->booleanValue; + case Value::Boolean_Type: + return x->booleanValue() == y->booleanValue(); break; - case NUMBER_TYPE: - return x->numberValue == y->numberValue; - case STRING_TYPE: - return __qmljs_string_equal(ctx, x->stringValue, y->stringValue); - case OBJECT_TYPE: - return x->objectValue == y->objectValue; + case Value::String_Type: + return __qmljs_string_equal(ctx, x->stringValue(), y->stringValue()); + case Value::Object_Type: + return x->objectValue() == y->objectValue(); + default: // double + return x->doubleValue() == y->doubleValue(); } // unreachable } else { - if (x->type == NULL_TYPE && y->type == UNDEFINED_TYPE) { + if (x->isNumber() && y->isNumber()) + return x == y; + if (x->isNull() && y->isUndefined()) { return true; - } else if (x->type == UNDEFINED_TYPE && y->type == NULL_TYPE) { + } else if (x->isUndefined() && y->isNull()) { return true; - } else if (x->type == NUMBER_TYPE && y->type == STRING_TYPE) { + } else if (x->isNumber() && y->isString()) { Value ny; __qmljs_init_number(&ny, __qmljs_to_number(ctx, y)); return __qmljs_equal(ctx, x, &ny); - } else if (x->type == STRING_TYPE && y->type == NUMBER_TYPE) { + } else if (x->isString() && y->isNumber()) { Value nx; __qmljs_init_number(&nx, __qmljs_to_number(ctx, x)); return __qmljs_equal(ctx, &nx, y); - } else if (x->type == BOOLEAN_TYPE) { + } else if (x->isBoolean()) { Value nx; - __qmljs_init_number(&nx, (double) x->booleanValue); + __qmljs_init_number(&nx, (double) x->booleanValue()); return __qmljs_equal(ctx, &nx, y); - } else if (y->type == BOOLEAN_TYPE) { + } else if (y->isBoolean()) { Value ny; - __qmljs_init_number(&ny, (double) y->booleanValue); + __qmljs_init_number(&ny, (double) y->booleanValue()); return __qmljs_equal(ctx, x, &ny); - } else if ((x->type == NUMBER_TYPE || x->type == STRING_TYPE) && y->type == OBJECT_TYPE) { + } else if ((x->isNumber() || x->isString()) && y->isObject()) { Value py; __qmljs_to_primitive(ctx, &py, y, PREFERREDTYPE_HINT); return __qmljs_equal(ctx, x, &py); - } else if (x->type == OBJECT_TYPE && (y->type == NUMBER_TYPE || y->type == STRING_TYPE)) { + } else if (x->isObject() && (y->isNumber() || y->isString())) { Value px; __qmljs_to_primitive(ctx, &px, x, PREFERREDTYPE_HINT); return __qmljs_equal(ctx, &px, y); @@ -1091,9 +1093,9 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S Value thisObject; if (base) { baseObject = *base; - if (baseObject.type != OBJECT_TYPE) + if (!baseObject.isObject()) __qmljs_to_object(context, &baseObject, &baseObject); - assert(baseObject.type == OBJECT_TYPE); + assert(baseObject.isObject()); thisObject = baseObject; } else { baseObject = context->activation; @@ -1161,10 +1163,10 @@ void __qmljs_construct_value(Context *context, Value *result, const Value *func, void __qmljs_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) { Value thisObject = *base; - if (thisObject.type != OBJECT_TYPE) + if (!thisObject.isObject()) __qmljs_to_object(context, &thisObject, base); - assert(thisObject.type == OBJECT_TYPE); + assert(thisObject.isObject()); Value func = thisObject.property(context, name); if (FunctionObject *f = func.asFunctionObject()) { Context k; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 8a266b395e..d4a31cb480 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -17,15 +17,6 @@ struct Function; namespace VM { -enum ValueType { - UNDEFINED_TYPE, - NULL_TYPE, - BOOLEAN_TYPE, - NUMBER_TYPE, - STRING_TYPE, - OBJECT_TYPE -}; - enum TypeHint { PREFERREDTYPE_HINT, NUMBER_HINT, @@ -248,60 +239,149 @@ bool __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right); } // extern "C" -struct Value { - int type; +struct ValueData { union { - bool booleanValue; - double numberValue; - Object *objectValue; - String *stringValue; + quint64 val; + double dbl; + struct { +#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN + uint tag; +#endif + union { + uint uint_32; + int int_32; + bool b; + }; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + uint tag; +#endif + }; + }; +}; + +struct Value; +template struct ValueBase; +template <> struct ValueBase<4> : public ValueData +{ + // we have all 4 bytes on 32 bit to specify the type + enum Masks { + NaN_Mask = 0xfff80000, + Type_Mask = 0xffffffff }; - inline bool is(ValueType t) const { return type == t; } - inline bool isNot(ValueType t) const { return type != t; } + enum ValueType { + Undefined_Type = NaN_Mask | 0x7ffff, // all 1's + Null_Type = NaN_Mask | 0x0, + Boolean_Type = NaN_Mask | 0x1, +// Integer_Type = NaN_Mask | 0x2, + Object_Type = NaN_Mask | 0x2d59b, // give it randomness to avoid accidental collisions (for gc) + String_Type = NaN_Mask | 0x2d5ba, + Double_Type = 0 + }; - static inline Value undefinedValue() { - Value v; - v.type = UNDEFINED_TYPE; - return v; + inline bool is(ValueType type) const { + if (type == Double_Type) + return (tag & NaN_Mask) != NaN_Mask; + return tag == type; + } + inline ValueType type() const { + return (ValueType)tag; } - static inline Value nullValue() { - Value v; - v.type = NULL_TYPE; - return v; + bool booleanValue() const { + return b; + } + double doubleValue() const { + return dbl; + } + void setDouble(double d) { + dbl = d; } - static inline Value fromBoolean(bool value) { - Value v; - __qmljs_init_boolean(&v, value); - return v; + String *stringValue() const { + return (String *)(quintptr) uint_32; + } + Object *objectValue() const { + return (Object *)(quintptr) uint_32; } + quint64 rawValue() const { + return val; + } + + static Value undefinedValue(); + static Value nullValue(); + static Value fromBoolean(bool b); + static Value fromDouble(double d); + static Value fromInt32(int i); + static Value fromString(String *s); + static Value fromObject(Object *o); +}; + +template <> struct ValueBase<8> : public ValueData +{ + enum Masks { + NaN_Mask = 0x7ff80000, + Type_Mask = 0x7fff0000, + Tag_Shift = 32 + }; + enum ValueType { + Undefined_Type = NaN_Mask | 0x70000, + Null_Type = NaN_Mask | 0x00000, + Boolean_Type = NaN_Mask | 0x10000, +// Integer_Type = NaN_Mask | 0x20000, + Object_Type = NaN_Mask | 0x30000, + String_Type = NaN_Mask | 0x40000, + Double_Type = 0 + }; - static inline Value fromNumber(double value) { - Value v; - __qmljs_init_number(&v, value); - return v; + inline bool is(ValueType type) const { + if (type == Double_Type) + return (tag & NaN_Mask) != NaN_Mask; + return (tag & Type_Mask) == type; + } + inline bool isNot(ValueType type) { + return !is(type); + } + inline ValueType type() const { + return (ValueType)(tag & Type_Mask); } - static inline Value fromObject(Object *value) { - Value v; - if (value) { - __qmljs_init_object(&v, value); - } else { - __qmljs_init_null(&v); - } - return v; + bool booleanValue() const { + return b; + } + double doubleValue() const { + return dbl; + } + void setDouble(double d) { + dbl = d; } - static inline Value fromString(String *value) { - Value v; - __qmljs_init_string(&v, value); - return v; + String *stringValue() const { + return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift)); } + Object *objectValue() const { + return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } + quint64 rawValue() const { + return val; + } + + static Value undefinedValue(); + static Value nullValue(); + static Value fromBoolean(bool b); + static Value fromDouble(double d); + static Value fromInt32(int i); + static Value fromString(String *s); + static Value fromObject(Object *o); +}; + + +struct Value : public ValueBase +{ #ifndef QMLJS_LLVM_RUNTIME static Value fromString(Context *ctx, const QString &fromString); + using ValueBase::fromString; #endif static int toInteger(double fromNumber); @@ -317,14 +397,14 @@ struct Value { String *toString(Context *ctx) const; Value toObject(Context *ctx) const; - inline bool isUndefined() const { return is(UNDEFINED_TYPE); } - inline bool isNull() const { return is(NULL_TYPE); } - inline bool isString() const { return is(STRING_TYPE); } - inline bool isBoolean() const { return is(BOOLEAN_TYPE); } - inline bool isNumber() const { return is(NUMBER_TYPE); } - inline bool isObject() const { return is(OBJECT_TYPE); } + inline bool isUndefined() const { return is(Value::Undefined_Type); } + inline bool isNull() const { return is(Value::Null_Type); } + inline bool isString() const { return is(Value::String_Type); } + inline bool isBoolean() const { return type() == Value::Boolean_Type; } + inline bool isNumber() const { return is(Value::Double_Type) /*|| is(Value::Integer_Type)*/; } + inline bool isObject() const { return type() == Value::Object_Type; } - inline bool isPrimitive() const { return type != OBJECT_TYPE; } + inline bool isPrimitive() const { ValueType t = type(); return t != Value::Object_Type; } bool isFunctionObject() const; bool isBooleanObject() const; bool isNumberObject() const; @@ -348,6 +428,116 @@ struct Value { Value *getPropertyDescriptor(Context *ctx, String *name) const; }; +inline Value ValueBase<4>::undefinedValue() +{ + Value v; + v.tag = Undefined_Type; + v.uint_32 = 0; + return v; +} + +inline Value ValueBase<4>::nullValue() +{ + Value v; + v.tag = Null_Type; + v.uint_32 = 0; + return v; +} + +inline Value ValueBase<4>::fromBoolean(bool b) +{ + Value v; + v.tag = Boolean_Type; + v.b = b; + return v; +} + +inline Value ValueBase<4>::fromDouble(double d) +{ + Value v; + v.dbl = d; + return v; +} + +inline Value ValueBase<4>::fromInt32(int i) +{ + Value v; + // ### + v.dbl = i; + return v; +} + +inline Value ValueBase<4>::fromString(String *s) +{ + Value v; + v.tag = String_Type; + v.uint_32 = (quint32)(quintptr) s; + return v; +} + +inline Value ValueBase<4>::fromObject(Object *o) +{ + Value v; + v.tag = Object_Type; + v.uint_32 = (quint32)(quintptr) o; + return v; +} + + +inline Value ValueBase<8>::undefinedValue() +{ + Value v; + v.val = quint64(Undefined_Type) << Tag_Shift; + return v; +} + +inline Value ValueBase<8>::nullValue() +{ + Value v; + v.val = quint64(Null_Type) << Tag_Shift; + return v; +} + +inline Value ValueBase<8>::fromBoolean(bool b) +{ + Value v; + v.tag = Boolean_Type; + v.b = b; + return v; +} + +inline Value ValueBase<8>::fromDouble(double d) +{ + Value v; + v.dbl = d; + return v; +} + +inline Value ValueBase<8>::fromInt32(int i) +{ + Value v; + // ### + v.dbl = i; + return v; +} + +inline Value ValueBase<8>::fromString(String *s) +{ + Value v; + v.val = (quint64)s; + v.val |= quint64(String_Type) << Tag_Shift; + return v; +} + +inline Value ValueBase<8>::fromObject(Object *o) +{ + Value v; + v.val = (quint64)o; + v.val |= quint64(Object_Type) << Tag_Shift; + return v; +} + + struct Context { ExecutionEngine *engine; Context *parent; @@ -406,102 +596,90 @@ extern "C" { // constructors inline void __qmljs_init_undefined(Value *result) { - result->type = UNDEFINED_TYPE; + *result = Value::undefinedValue(); } inline void __qmljs_init_null(Value *result) { - result->type = NULL_TYPE; + *result = Value::nullValue(); } inline void __qmljs_init_boolean(Value *result, bool value) { - result->type = BOOLEAN_TYPE; - result->booleanValue = value; + *result = Value::fromBoolean(value); } inline void __qmljs_init_number(Value *result, double value) { - result->type = NUMBER_TYPE; - result->numberValue = value; + *result = Value::fromDouble(value); } inline void __qmljs_init_string(Value *result, String *value) { - result->type = STRING_TYPE; - result->stringValue = value; + *result = Value::fromString(value); } inline void __qmljs_init_object(Value *result, Object *object) { - result->type = OBJECT_TYPE; - result->objectValue = object; + *result = Value::fromObject(object); } inline void __qmljs_copy(Value *result, Value *source) { - result->type = source->type; - result->numberValue = source->numberValue; + *result = *source; } // type conversion and testing inline void __qmljs_to_primitive(Context *ctx, Value *result, const Value *value, int typeHint) { - switch ((ValueType) value->type) { - case UNDEFINED_TYPE: - case NULL_TYPE: - case BOOLEAN_TYPE: - case NUMBER_TYPE: - case STRING_TYPE: + if (!value->isObject()) *result = *value; - break; - case OBJECT_TYPE: + else __qmljs_default_value(ctx, result, value, typeHint); - break; - } } inline bool __qmljs_to_boolean(Context *ctx, const Value *value) { - switch ((ValueType) value->type) { - case UNDEFINED_TYPE: - case NULL_TYPE: + switch (value->type()) { + case Value::Undefined_Type: + case Value::Null_Type: return false; - case BOOLEAN_TYPE: - return value->booleanValue; - case NUMBER_TYPE: - if (! value->numberValue || isnan(value->numberValue)) - return false; + case Value::Boolean_Type: + return value->booleanValue(); +// case Value::Integer_Type: +// return value->data; + case Value::String_Type: + return __qmljs_string_length(ctx, value->stringValue()) > 0; + case Value::Object_Type: return true; - case STRING_TYPE: - return __qmljs_string_length(ctx, value->stringValue) > 0; - case OBJECT_TYPE: + default: // double + if (! value->doubleValue() || isnan(value->doubleValue())) + return false; return true; } - assert(!"unreachable"); - return false; } inline double __qmljs_to_number(Context *ctx, const Value *value) { - switch ((ValueType) value->type) { - case UNDEFINED_TYPE: + switch (value->type()) { + case Value::Undefined_Type: return nan(""); - case NULL_TYPE: + case Value::Null_Type: return 0; - case BOOLEAN_TYPE: - return (double) value->booleanValue; - case NUMBER_TYPE: - return value->numberValue; - case STRING_TYPE: - return __qmljs_string_to_number(ctx, value->stringValue); - case OBJECT_TYPE: { + case Value::Boolean_Type: + return (value->booleanValue() ? 1. : 0.); +// case Value::Integer_Type: +// return value->data; + case Value::String_Type: + return __qmljs_string_to_number(ctx, value->stringValue()); + case Value::Object_Type: { Value prim; __qmljs_to_primitive(ctx, &prim, value, NUMBER_HINT); return __qmljs_to_number(ctx, &prim); } - } // switch - return 0; // unreachable + default: // double + return value->doubleValue(); + } } inline double __qmljs_to_integer(Context *ctx, const Value *value) @@ -580,26 +758,23 @@ inline unsigned short __qmljs_to_uint16(Context *ctx, const Value *value) inline void __qmljs_to_string(Context *ctx, Value *result, const Value *value) { - switch ((ValueType) value->type) { - case UNDEFINED_TYPE: + switch (value->type()) { + case Value::Undefined_Type: __qmljs_string_literal_undefined(ctx, result); break; - case NULL_TYPE: + case Value::Null_Type: __qmljs_string_literal_null(ctx, result); break; - case BOOLEAN_TYPE: - if (value->booleanValue) + case Value::Boolean_Type: + if (value->booleanValue()) __qmljs_string_literal_true(ctx, result); else __qmljs_string_literal_false(ctx, result); break; - case NUMBER_TYPE: - __qmljs_string_from_number(ctx, result, value->numberValue); - break; - case STRING_TYPE: + case Value::String_Type: *result = *value; break; - case OBJECT_TYPE: { + case Value::Object_Type: { Value prim; __qmljs_to_primitive(ctx, &prim, value, STRING_HINT); if (prim.isPrimitive()) @@ -608,50 +783,56 @@ inline void __qmljs_to_string(Context *ctx, Value *result, const Value *value) __qmljs_throw_type_error(ctx, result); break; } +// case Value::Integer_Type: +// __qmljs_string_from_number(ctx, result, value->data); +// break; + default: // double + __qmljs_string_from_number(ctx, result, value->doubleValue()); + break; } // switch } inline void __qmljs_to_object(Context *ctx, Value *result, const Value *value) { - switch ((ValueType) value->type) { - case UNDEFINED_TYPE: - case NULL_TYPE: + switch (value->type()) { + case Value::Undefined_Type: + case Value::Null_Type: __qmljs_throw_type_error(ctx, result); break; - case BOOLEAN_TYPE: - __qmljs_new_boolean_object(ctx, result, value->booleanValue); - break; - case NUMBER_TYPE: - __qmljs_new_number_object(ctx, result, value->numberValue); + case Value::Boolean_Type: + __qmljs_new_boolean_object(ctx, result, value->booleanValue()); break; - case STRING_TYPE: - __qmljs_new_string_object(ctx, result, value->stringValue); + case Value::String_Type: + __qmljs_new_string_object(ctx, result, value->stringValue()); break; - case OBJECT_TYPE: + case Value::Object_Type: *result = *value; break; +// case Value::Integer_Type: +// __qmljs_new_number_object(ctx, result, value->data); +// break; + default: + __qmljs_new_number_object(ctx, result, value->doubleValue()); + break; } } inline bool __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value) { - switch ((ValueType) value->type) { - case UNDEFINED_TYPE: - case NULL_TYPE: + switch (value->type()) { + case Value::Undefined_Type: + case Value::Null_Type: __qmljs_throw_type_error(ctx, result); return false; - case BOOLEAN_TYPE: - case NUMBER_TYPE: - case STRING_TYPE: - case OBJECT_TYPE: + default: return true; } } inline bool __qmljs_is_callable(Context *ctx, const Value *value) { - if (value->type == OBJECT_TYPE) + if (value->isObject()) return __qmljs_is_function(ctx, value); else return false; @@ -659,7 +840,7 @@ inline bool __qmljs_is_callable(Context *ctx, const Value *value) inline void __qmljs_default_value(Context *ctx, Value *result, const Value *value, int typeHint) { - if (value->type == OBJECT_TYPE) + if (value->isObject()) __qmljs_object_default_value(ctx, result, value, typeHint); else __qmljs_init_undefined(result); @@ -669,28 +850,29 @@ inline void __qmljs_default_value(Context *ctx, Value *result, const Value *valu // unary operators inline void __qmljs_typeof(Context *ctx, Value *result, const Value *value) { - switch ((ValueType) value->type) { - case UNDEFINED_TYPE: + switch (value->type()) { + case Value::Undefined_Type: __qmljs_string_literal_undefined(ctx, result); break; - case NULL_TYPE: + case Value::Null_Type: __qmljs_string_literal_object(ctx, result); break; - case BOOLEAN_TYPE: + case Value::Boolean_Type: __qmljs_string_literal_boolean(ctx, result); break; - case NUMBER_TYPE: - __qmljs_string_literal_number(ctx, result); - break; - case STRING_TYPE: + case Value::String_Type: __qmljs_string_literal_string(ctx, result); break; - case OBJECT_TYPE: + case Value::Object_Type: if (__qmljs_is_callable(ctx, value)) __qmljs_string_literal_function(ctx, result); else __qmljs_string_literal_object(ctx, result); // ### implementation-defined break; +// case Value::Integer_Type: + case Value::Double_Type: + __qmljs_string_literal_number(ctx, result); + break; } } @@ -797,8 +979,8 @@ inline void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value) inline void __qmljs_add(Context *ctx, Value *result, const Value *left, const Value *right) { - if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) - __qmljs_init_number(result, left->numberValue + right->numberValue); + if (left->isNumber() && right->isNumber()) + __qmljs_init_number(result, left->doubleValue() + right->doubleValue()); else __qmljs_add_helper(ctx, result, left, right); } @@ -854,62 +1036,58 @@ inline void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const V inline void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Value *right) { - if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) { - __qmljs_init_boolean(result, left->numberValue > right->numberValue); + if (left->isNumber() && right->isNumber()) { + __qmljs_init_boolean(result, left->doubleValue() > right->doubleValue()); } else { __qmljs_compare(ctx, result, left, right, false); - if (result->type == UNDEFINED_TYPE) + if (result->isUndefined()) __qmljs_init_boolean(result, false); } } inline void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Value *right) { - if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) { - __qmljs_init_boolean(result, left->numberValue < right->numberValue); + if (left->isNumber() && right->isNumber()) { + __qmljs_init_boolean(result, left->doubleValue() < right->doubleValue()); } else { __qmljs_compare(ctx, result, left, right, true); - if (result->type == UNDEFINED_TYPE) + if (result->isUndefined()) __qmljs_init_boolean(result, false); } } inline void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Value *right) { - if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) { - __qmljs_init_boolean(result, left->numberValue >= right->numberValue); + if (left->isNumber() && right->isNumber()) { + __qmljs_init_boolean(result, left->doubleValue() >= right->doubleValue()); } else { __qmljs_compare(ctx, result, right, left, false); - bool r = ! (result->type == UNDEFINED_TYPE || - (result->type == BOOLEAN_TYPE && result->booleanValue == true)); - + bool r = ! (result->isUndefined() || (result->isBoolean() && result->booleanValue())); __qmljs_init_boolean(result, r); } } inline void __qmljs_le(Context *ctx, Value *result, const Value *left, const Value *right) { - if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) { - __qmljs_init_boolean(result, left->numberValue <= right->numberValue); + if (left->isNumber() && right->isNumber()) { + __qmljs_init_boolean(result, left->doubleValue() <= right->doubleValue()); } else { __qmljs_compare(ctx, result, right, left, true); - bool r = ! (result->type == UNDEFINED_TYPE || - (result->type == BOOLEAN_TYPE && result->booleanValue == true)); - + bool r = ! (result->isUndefined() || (result->isBoolean() && result->booleanValue())); __qmljs_init_boolean(result, r); } } inline void __qmljs_eq(Context *ctx, Value *result, const Value *left, const Value *right) { - if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) { - __qmljs_init_boolean(result, left->numberValue == right->numberValue); - } else if (left->type == STRING_TYPE && right->type == STRING_TYPE) { - __qmljs_init_boolean(result, __qmljs_string_equal(ctx, left->stringValue, right->stringValue)); + if (left->isNumber() && right->isNumber()) { + __qmljs_init_boolean(result, left->doubleValue() == right->doubleValue()); + } else if (left->isString() && right->isString()) { + __qmljs_init_boolean(result, __qmljs_string_equal(ctx, left->stringValue(), right->stringValue())); } else { bool r = __qmljs_equal(ctx, left, right); __qmljs_init_boolean(result, r); @@ -918,10 +1096,10 @@ inline void __qmljs_eq(Context *ctx, Value *result, const Value *left, const Val inline void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *right) { - if (left->type == NUMBER_TYPE && right->type == NUMBER_TYPE) { - __qmljs_init_boolean(result, left->numberValue != right->numberValue); - } else if (left->type == STRING_TYPE && right->type == STRING_TYPE) { - __qmljs_init_boolean(result, !__qmljs_string_equal(ctx, left->stringValue, right->stringValue)); + if (left->isNumber() && right->isNumber()) { + __qmljs_init_boolean(result, left->doubleValue() != right->doubleValue()); + } else if (left->isString() && right->isString()) { + __qmljs_init_boolean(result, !__qmljs_string_equal(ctx, left->stringValue(), right->stringValue())); } else { bool r = ! __qmljs_equal(ctx, left, right); __qmljs_init_boolean(result, r); @@ -1012,23 +1190,10 @@ inline bool __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right) inline bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y) { - if (x->type != y->type) - return false; - - switch ((ValueType) x->type) { - case UNDEFINED_TYPE: - case NULL_TYPE: + if (x->rawValue() == y->rawValue()) return true; - case BOOLEAN_TYPE: - return x->booleanValue == y->booleanValue; - case NUMBER_TYPE: - return x->numberValue == y->numberValue; - case STRING_TYPE: - return __qmljs_string_equal(ctx, x->stringValue, y->stringValue); - case OBJECT_TYPE: - return x->objectValue == y->objectValue; - } - assert(!"unreachable"); + if (x->isString() && y->isString()) + return __qmljs_string_equal(ctx, x->stringValue(), y->stringValue()); return false; } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index e044d11c64..93775b249a 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -491,26 +491,26 @@ void ObjectCtor::call(Context *ctx) Value ObjectCtor::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) { if (name == ctx->engine->id_length) - return Value::fromNumber(1); + return Value::fromDouble(1); return Object::getProperty(ctx, name, attributes); } void ObjectPrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue->setProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 0); - ctor.objectValue->setProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 0); - ctor.objectValue->setProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 0); - ctor.objectValue->setProperty(ctx, QStringLiteral("create"), method_create, 0); - ctor.objectValue->setProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 0); - ctor.objectValue->setProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 0); - ctor.objectValue->setProperty(ctx, QStringLiteral("seal"), method_seal, 0); - ctor.objectValue->setProperty(ctx, QStringLiteral("freeze"), method_freeze, 0); - ctor.objectValue->setProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 0); - ctor.objectValue->setProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 0); - ctor.objectValue->setProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 0); - ctor.objectValue->setProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 0); - ctor.objectValue->setProperty(ctx, QStringLiteral("keys"), method_keys, 0); + ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->setProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 0); + ctor.objectValue()->setProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 0); + ctor.objectValue()->setProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 0); + ctor.objectValue()->setProperty(ctx, QStringLiteral("create"), method_create, 0); + ctor.objectValue()->setProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 0); + ctor.objectValue()->setProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 0); + ctor.objectValue()->setProperty(ctx, QStringLiteral("seal"), method_seal, 0); + ctor.objectValue()->setProperty(ctx, QStringLiteral("freeze"), method_freeze, 0); + ctor.objectValue()->setProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 0); + ctor.objectValue()->setProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 0); + ctor.objectValue()->setProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 0); + ctor.objectValue()->setProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 0); + ctor.objectValue()->setProperty(ctx, QStringLiteral("keys"), method_keys, 0); setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("toString"), method_toString, 0); @@ -527,7 +527,7 @@ void ObjectPrototype::method_getPrototypeOf(Context *ctx) if (! o.isObject()) { ctx->throwTypeError(); } else { - ctx->result = Value::fromObject(o.objectValue->prototype); + ctx->result = Value::fromObject(o.objectValue()->prototype); } } @@ -544,7 +544,7 @@ void ObjectPrototype::method_getOwnPropertyNames(Context *ctx) else { ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); Array &a = array->value; - if (Table *members = O.objectValue->members) { + if (Table *members = O.objectValue()->members) { for (Property **it = members->begin(), **end = members->end(); it != end; ++it) { if (Property *prop = *it) { a.push(Value::fromString(prop->name)); @@ -610,7 +610,7 @@ void ObjectPrototype::method_toString(Context *ctx) if (! ctx->thisObject.isObject()) ctx->throwTypeError(); else - ctx->result = Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(ctx->thisObject.objectValue->className())); + ctx->result = Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(ctx->thisObject.objectValue()->className())); } void ObjectPrototype::method_toLocaleString(Context *ctx) @@ -628,7 +628,7 @@ void ObjectPrototype::method_hasOwnProperty(Context *ctx) { String *P = ctx->argument(0).toString(ctx); Value O = ctx->thisObject.toObject(ctx); - bool r = O.objectValue->getOwnProperty(ctx, P) != 0; + bool r = O.objectValue()->getOwnProperty(ctx, P) != 0; ctx->result = Value::fromBoolean(r); } @@ -639,8 +639,8 @@ void ObjectPrototype::method_isPrototypeOf(Context *ctx) ctx->result = Value::fromBoolean(false); else { Value O = ctx->thisObject.toObject(ctx); - Object *proto = V.objectValue->prototype; - ctx->result = Value::fromBoolean(proto && O.objectValue == proto); + Object *proto = V.objectValue()->prototype; + ctx->result = Value::fromBoolean(proto && O.objectValue() == proto); } } @@ -670,7 +670,7 @@ void StringCtor::construct(Context *ctx) void StringCtor::call(Context *ctx) { const Value arg = ctx->argument(0); - if (arg.is(UNDEFINED_TYPE)) + if (arg.is(Value::Undefined_Type)) __qmljs_init_string(&ctx->result, ctx->engine->newString(QString())); else __qmljs_to_string(ctx, &ctx->result, &arg); @@ -678,8 +678,8 @@ void StringCtor::call(Context *ctx) void StringPrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue->setProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode); + ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->setProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode); setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("toString"), method_toString); @@ -706,7 +706,7 @@ void StringPrototype::init(Context *ctx, const Value &ctor) QString StringPrototype::getThisString(Context *ctx) { if (StringObject *thisObject = ctx->thisObject.asStringObject()) { - return thisObject->value.stringValue->toQString(); + return thisObject->value.stringValue()->toQString(); } else { ctx->throwTypeError(); return QString(); @@ -769,8 +769,8 @@ void StringPrototype::method_concat(Context *ctx) for (unsigned i = 0; i < ctx->argumentCount; ++i) { Value v; __qmljs_to_string(ctx, &v, &ctx->arguments[i]); - assert(v.is(STRING_TYPE)); - value += v.stringValue->toQString(); + assert(v.is(Value::String_Type)); + value += v.stringValue()->toQString(); } ctx->result = Value::fromString(ctx, value); @@ -803,7 +803,7 @@ void StringPrototype::method_lastIndexOf(Context *ctx) if (ctx->argumentCount) { Value v; __qmljs_to_string(ctx, &v, &ctx->arguments[0]); - searchString = v.stringValue->toQString(); + searchString = v.stringValue()->toQString(); } Value posArg = ctx->argument(1); @@ -976,7 +976,7 @@ NumberCtor::NumberCtor(Context *scope) void NumberCtor::construct(Context *ctx) { const double n = ctx->argument(0).toNumber(ctx); - __qmljs_init_object(&ctx->thisObject, ctx->engine->newNumberObject(Value::fromNumber(n))); + __qmljs_init_object(&ctx->thisObject, ctx->engine->newNumberObject(Value::fromDouble(n))); } void NumberCtor::call(Context *ctx) @@ -987,16 +987,16 @@ void NumberCtor::call(Context *ctx) void NumberPrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue->setProperty(ctx, QStringLiteral("NaN"), Value::fromNumber(qSNaN())); - ctor.objectValue->setProperty(ctx, QStringLiteral("NEGATIVE_INFINITY"), Value::fromNumber(-qInf())); - ctor.objectValue->setProperty(ctx, QStringLiteral("POSITIVE_INFINITY"), Value::fromNumber(qInf())); - ctor.objectValue->setProperty(ctx, QStringLiteral("MAX_VALUE"), Value::fromNumber(1.7976931348623158e+308)); + ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->setProperty(ctx, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); + ctor.objectValue()->setProperty(ctx, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); + ctor.objectValue()->setProperty(ctx, QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf())); + ctor.objectValue()->setProperty(ctx, QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308)); #ifdef __INTEL_COMPILER # pragma warning( push ) # pragma warning(disable: 239) #endif - ctor.objectValue->setProperty(ctx, QStringLiteral("MIN_VALUE"), Value::fromNumber(5e-324)); + ctor.objectValue()->setProperty(ctx, QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324)); #ifdef __INTEL_COMPILER # pragma warning( pop ) #endif @@ -1022,7 +1022,7 @@ void NumberPrototype::method_toString(Context *ctx) return; } - double num = thisObject->value.numberValue; + double num = thisObject->value.doubleValue(); if (qIsNaN(num)) { ctx->result = Value::fromString(ctx, QStringLiteral("NaN")); return; @@ -1101,7 +1101,7 @@ void NumberPrototype::method_toFixed(Context *ctx) if (qIsNaN(fdigits)) fdigits = 0; - double v = thisObject->value.numberValue; + double v = thisObject->value.doubleValue(); QString str; if (qIsNaN(v)) str = QString::fromLatin1("NaN"); @@ -1123,7 +1123,7 @@ void NumberPrototype::method_toExponential(Context *ctx) if (ctx->argumentCount > 0) fdigits = ctx->argument(0).toInteger(ctx); - QString z = QString::number(thisObject->value.numberValue, 'e', int (fdigits)); + QString z = QString::number(thisObject->value.doubleValue(), 'e', int (fdigits)); ctx->result = Value::fromString(ctx, z); } else { ctx->throwTypeError(); @@ -1138,7 +1138,7 @@ void NumberPrototype::method_toPrecision(Context *ctx) if (ctx->argumentCount > 0) fdigits = ctx->argument(0).toInteger(ctx); - ctx->result = Value::fromString(ctx, QString::number(thisObject->value.numberValue, 'g', int (fdigits))); + ctx->result = Value::fromString(ctx, QString::number(thisObject->value.doubleValue(), 'g', int (fdigits))); } else { ctx->throwTypeError(); } @@ -1166,7 +1166,7 @@ void BooleanCtor::call(Context *ctx) void BooleanPrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("toString"), method_toString); setProperty(ctx, QStringLiteral("valueOf"), method_valueOf); @@ -1175,7 +1175,7 @@ void BooleanPrototype::init(Context *ctx, const Value &ctor) void BooleanPrototype::method_toString(Context *ctx) { if (BooleanObject *thisObject = ctx->thisObject.asBooleanObject()) { - ctx->result = Value::fromString(ctx, QLatin1String(thisObject->value.booleanValue ? "true" : "false")); + ctx->result = Value::fromString(ctx, QLatin1String(thisObject->value.booleanValue() ? "true" : "false")); } else { ctx->throwTypeError(); } @@ -1208,7 +1208,7 @@ void ArrayCtor::call(Context *ctx) { Array value; if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { - double size = ctx->argument(0).numberValue; + double size = ctx->argument(0).doubleValue(); quint32 isize = Value::toUInt32(size); if (size != double(isize)) { @@ -1228,7 +1228,7 @@ void ArrayCtor::call(Context *ctx) void ArrayPrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("toString"), method_toString, 0); setProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString, 0); @@ -1304,13 +1304,13 @@ void ArrayPrototype::method_join(Context *ctx) static QSet visitedArrayElements; - if (! r2 || visitedArrayElements.contains(self.objectValue)) { + if (! r2 || visitedArrayElements.contains(self.objectValue())) { ctx->result = Value::fromString(ctx, QString()); return; } // avoid infinite recursion - visitedArrayElements.insert(self.objectValue); + visitedArrayElements.insert(self.objectValue()); QString R; @@ -1334,7 +1334,7 @@ void ArrayPrototype::method_join(Context *ctx) for (quint32 k = 1; k < r2; ++k) { R += r4; - String *name = Value::fromNumber(k).toString(ctx); + String *name = Value::fromDouble(k).toString(ctx); Value r12 = self.property(ctx, name); if (! (r12.isUndefined() || r12.isNull())) @@ -1342,7 +1342,7 @@ void ArrayPrototype::method_join(Context *ctx) } } - visitedArrayElements.remove(self.objectValue); + visitedArrayElements.remove(self.objectValue()); ctx->result = Value::fromString(ctx, R); } @@ -1356,12 +1356,12 @@ void ArrayPrototype::method_pop(Context *ctx) Value r1 = self.property(ctx, ctx->engine->id_length); quint32 r2 = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; if (! r2) { - self.objectValue->setProperty(ctx, ctx->engine->id_length, Value::fromNumber(0)); + self.objectValue()->setProperty(ctx, ctx->engine->id_length, Value::fromDouble(0)); } else { - String *r6 = Value::fromNumber(r2 - 1).toString(ctx); + String *r6 = Value::fromDouble(r2 - 1).toString(ctx); Value r7 = self.property(ctx, r6); - self.objectValue->deleteProperty(ctx, r6, 0); - self.objectValue->setProperty(ctx, ctx->engine->id_length, Value::fromNumber(2 - 1)); + self.objectValue()->deleteProperty(ctx, r6, 0); + self.objectValue()->setProperty(ctx, ctx->engine->id_length, Value::fromDouble(2 - 1)); ctx->result = r7; } } @@ -1376,17 +1376,17 @@ void ArrayPrototype::method_push(Context *ctx) Value val = ctx->argument(i); instance->value.assign(pos++, val); } - ctx->result = Value::fromNumber(pos); + ctx->result = Value::fromDouble(pos); } else { Value r1 = self.property(ctx, ctx->engine->id_length); quint32 n = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; for (unsigned int index = 0; index < ctx->argumentCount; ++index, ++n) { Value r3 = ctx->argument(index); - String *name = Value::fromNumber(n).toString(ctx); - self.objectValue->setProperty(ctx, name, r3); + String *name = Value::fromDouble(n).toString(ctx); + self.objectValue()->setProperty(ctx, name, r3); } - Value r = Value::fromNumber(n); - self.objectValue->setProperty(ctx, ctx->engine->id_length, r); + Value r = Value::fromDouble(n); + self.objectValue()->setProperty(ctx, ctx->engine->id_length, r); ctx->result = r; } } @@ -1435,7 +1435,7 @@ void ArrayPrototype::method_slice(Context *ctx) quint32 r8 = r7 < 0 ? qMax(quint32(r3 + r7), quint32(0)) : qMin(quint32(r7), r3); quint32 n = 0; for (; k < r8; ++k) { - String *r11 = Value::fromNumber(k).toString(ctx); + String *r11 = Value::fromDouble(k).toString(ctx); Value v = self.property(ctx, r11); if (! v.isUndefined()) result.assign(n++, v); @@ -1507,7 +1507,7 @@ void ArrayPrototype::method_every(Context *ctx) Value args[3]; args[0] = v; - args[1] = Value::fromNumber(k); + args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; Value r; __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); @@ -1537,7 +1537,7 @@ void ArrayPrototype::method_some(Context *ctx) Value args[3]; args[0] = v; - args[1] = Value::fromNumber(k); + args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; Value r; __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); @@ -1568,7 +1568,7 @@ void ArrayPrototype::method_forEach(Context *ctx) Value r; Value args[3]; args[0] = v; - args[1] = Value::fromNumber(k); + args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); } @@ -1596,7 +1596,7 @@ void ArrayPrototype::method_map(Context *ctx) Value r; Value args[3]; args[0] = v; - args[1] = Value::fromNumber(k); + args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); a->value.assign(k, r); @@ -1625,7 +1625,7 @@ void ArrayPrototype::method_filter(Context *ctx) Value r; Value args[3]; args[0] = v; - args[1] = Value::fromNumber(k); + args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); if (__qmljs_to_boolean(ctx, &r)) { @@ -1662,7 +1662,7 @@ void ArrayPrototype::method_reduce(Context *ctx) Value args[4]; args[0] = acc; args[1] = v; - args[2] = Value::fromNumber(k); + args[2] = Value::fromDouble(k); args[3] = ctx->thisObject; __qmljs_call_value(ctx, &r, 0, &callback, args, 4); acc = r; @@ -1694,7 +1694,7 @@ void ArrayPrototype::method_reduceRight(Context *ctx) Value args[4]; args[0] = acc; args[1] = v; - args[2] = Value::fromNumber(k); + args[2] = Value::fromDouble(k); args[3] = ctx->thisObject; __qmljs_call_value(ctx, &r, 0, &callback, args, 4); acc = r; @@ -1725,7 +1725,7 @@ void FunctionCtor::call(Context *ctx) void FunctionPrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("toString"), method_toString, 0); setProperty(ctx, QStringLiteral("apply"), method_apply, 0); @@ -1838,7 +1838,7 @@ void DateCtor::construct(Context *ctx) t = TimeClip(UTC(t)); } - Object *d = ctx->engine->newDateObject(Value::fromNumber(t)); + Object *d = ctx->engine->newDateObject(Value::fromDouble(t)); ctx->thisObject = Value::fromObject(d); } @@ -1850,11 +1850,11 @@ void DateCtor::call(Context *ctx) void DatePrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); LocalTZA = getLocalTZA(); - ctor.objectValue->setProperty(ctx, QStringLiteral("parse"), method_parse, 1); - ctor.objectValue->setProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); + ctor.objectValue()->setProperty(ctx, QStringLiteral("parse"), method_parse, 1); + ctor.objectValue()->setProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("toString"), method_toString, 0); @@ -1906,7 +1906,7 @@ void DatePrototype::init(Context *ctx, const Value &ctor) double DatePrototype::getThisDate(Context *ctx) { if (DateObject *thisObject = ctx->thisObject.asDateObject()) - return thisObject->value.numberValue; + return thisObject->value.doubleValue(); else { ctx->throwTypeError(); return 0; @@ -1930,7 +1930,7 @@ void DatePrototype::method_TimeClip(Context *ctx) void DatePrototype::method_parse(Context *ctx) { - ctx->result = Value::fromNumber(ParseString(ctx->argument(0).toString(ctx)->toQString())); + ctx->result = Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString())); } void DatePrototype::method_UTC(Context *ctx) @@ -1948,7 +1948,7 @@ void DatePrototype::method_UTC(Context *ctx) year += 1900; double t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); - ctx->result = Value::fromNumber(TimeClip(t)); + ctx->result = Value::fromDouble(TimeClip(t)); } } @@ -1991,13 +1991,13 @@ void DatePrototype::method_toLocaleTimeString(Context *ctx) void DatePrototype::method_valueOf(Context *ctx) { double t = getThisDate(ctx); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getTime(Context *ctx) { double t = getThisDate(ctx); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getYear(Context *ctx) @@ -2005,7 +2005,7 @@ void DatePrototype::method_getYear(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = YearFromTime(LocalTime(t)) - 1900; - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getFullYear(Context *ctx) @@ -2013,7 +2013,7 @@ void DatePrototype::method_getFullYear(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = YearFromTime(LocalTime(t)); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getUTCFullYear(Context *ctx) @@ -2021,7 +2021,7 @@ void DatePrototype::method_getUTCFullYear(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = YearFromTime(t); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getMonth(Context *ctx) @@ -2029,7 +2029,7 @@ void DatePrototype::method_getMonth(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = MonthFromTime(LocalTime(t)); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getUTCMonth(Context *ctx) @@ -2037,7 +2037,7 @@ void DatePrototype::method_getUTCMonth(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = MonthFromTime(t); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getDate(Context *ctx) @@ -2045,7 +2045,7 @@ void DatePrototype::method_getDate(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = DateFromTime(LocalTime(t)); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getUTCDate(Context *ctx) @@ -2053,7 +2053,7 @@ void DatePrototype::method_getUTCDate(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = DateFromTime(t); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getDay(Context *ctx) @@ -2061,7 +2061,7 @@ void DatePrototype::method_getDay(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = WeekDay(LocalTime(t)); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getUTCDay(Context *ctx) @@ -2069,7 +2069,7 @@ void DatePrototype::method_getUTCDay(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = WeekDay(t); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getHours(Context *ctx) @@ -2077,7 +2077,7 @@ void DatePrototype::method_getHours(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = HourFromTime(LocalTime(t)); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getUTCHours(Context *ctx) @@ -2085,7 +2085,7 @@ void DatePrototype::method_getUTCHours(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = HourFromTime(t); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getMinutes(Context *ctx) @@ -2093,7 +2093,7 @@ void DatePrototype::method_getMinutes(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = MinFromTime(LocalTime(t)); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getUTCMinutes(Context *ctx) @@ -2101,7 +2101,7 @@ void DatePrototype::method_getUTCMinutes(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = MinFromTime(t); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getSeconds(Context *ctx) @@ -2109,7 +2109,7 @@ void DatePrototype::method_getSeconds(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = SecFromTime(LocalTime(t)); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getUTCSeconds(Context *ctx) @@ -2117,7 +2117,7 @@ void DatePrototype::method_getUTCSeconds(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = SecFromTime(t); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getMilliseconds(Context *ctx) @@ -2125,7 +2125,7 @@ void DatePrototype::method_getMilliseconds(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = msFromTime(LocalTime(t)); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getUTCMilliseconds(Context *ctx) @@ -2133,7 +2133,7 @@ void DatePrototype::method_getUTCMilliseconds(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = msFromTime(t); - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_getTimezoneOffset(Context *ctx) @@ -2141,13 +2141,13 @@ void DatePrototype::method_getTimezoneOffset(Context *ctx) double t = getThisDate(ctx); if (! qIsNaN(t)) t = (t - LocalTime(t)) / msPerMinute; - ctx->result = Value::fromNumber(t); + ctx->result = Value::fromDouble(t); } void DatePrototype::method_setTime(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - self->value.numberValue = TimeClip(ctx->argument(0).toNumber(ctx)); + self->value.setDouble(TimeClip(ctx->argument(0).toNumber(ctx))); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2157,9 +2157,9 @@ void DatePrototype::method_setTime(Context *ctx) void DatePrototype::method_setMilliseconds(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.numberValue); + double t = LocalTime(self->value.doubleValue()); double ms = ctx->argument(0).toNumber(ctx); - self->value.numberValue = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); + self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2169,9 +2169,9 @@ void DatePrototype::method_setMilliseconds(Context *ctx) void DatePrototype::method_setUTCMilliseconds(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.numberValue; + double t = self->value.doubleValue(); double ms = ctx->argument(0).toNumber(ctx); - self->value.numberValue = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); + self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2181,11 +2181,11 @@ void DatePrototype::method_setUTCMilliseconds(Context *ctx) void DatePrototype::method_setSeconds(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.numberValue); + double t = LocalTime(self->value.doubleValue()); double sec = ctx->argument(0).toNumber(ctx); double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); - self->value.numberValue = t; + self->value.setDouble(t); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2195,11 +2195,11 @@ void DatePrototype::method_setSeconds(Context *ctx) void DatePrototype::method_setUTCSeconds(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.numberValue; + double t = self->value.doubleValue(); double sec = ctx->argument(0).toNumber(ctx); double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); - self->value.numberValue = t; + self->value.setDouble(t); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2209,12 +2209,12 @@ void DatePrototype::method_setUTCSeconds(Context *ctx) void DatePrototype::method_setMinutes(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.numberValue); + double t = LocalTime(self->value.doubleValue()); double min = ctx->argument(0).toNumber(ctx); double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); - self->value.numberValue = t; + self->value.setDouble(t); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2224,12 +2224,12 @@ void DatePrototype::method_setMinutes(Context *ctx) void DatePrototype::method_setUTCMinutes(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.numberValue; + double t = self->value.doubleValue(); double min = ctx->argument(0).toNumber(ctx); double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); - self->value.numberValue = t; + self->value.setDouble(t); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2239,13 +2239,13 @@ void DatePrototype::method_setUTCMinutes(Context *ctx) void DatePrototype::method_setHours(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.numberValue); + double t = LocalTime(self->value.doubleValue()); double hour = ctx->argument(0).toNumber(ctx); double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); - self->value.numberValue = t; + self->value.setDouble(t); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2255,13 +2255,13 @@ void DatePrototype::method_setHours(Context *ctx) void DatePrototype::method_setUTCHours(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.numberValue; + double t = self->value.doubleValue(); double hour = ctx->argument(0).toNumber(ctx); double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); - self->value.numberValue = t; + self->value.setDouble(t); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2271,10 +2271,10 @@ void DatePrototype::method_setUTCHours(Context *ctx) void DatePrototype::method_setDate(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.numberValue); + double t = LocalTime(self->value.doubleValue()); double date = ctx->argument(0).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); - self->value.numberValue = t; + self->value.setDouble(t); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2284,10 +2284,10 @@ void DatePrototype::method_setDate(Context *ctx) void DatePrototype::method_setUTCDate(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.numberValue; + double t = self->value.doubleValue(); double date = ctx->argument(0).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); - self->value.numberValue = t; + self->value.setDouble(t); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2297,11 +2297,11 @@ void DatePrototype::method_setUTCDate(Context *ctx) void DatePrototype::method_setMonth(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.numberValue); + double t = LocalTime(self->value.doubleValue()); double month = ctx->argument(0).toNumber(ctx); double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); - self->value.numberValue = t; + self->value.setDouble(t); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2311,11 +2311,11 @@ void DatePrototype::method_setMonth(Context *ctx) void DatePrototype::method_setUTCMonth(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.numberValue; + double t = self->value.doubleValue(); double month = ctx->argument(0).toNumber(ctx); double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); - self->value.numberValue = t; + self->value.setDouble(t); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2325,7 +2325,7 @@ void DatePrototype::method_setUTCMonth(Context *ctx) void DatePrototype::method_setYear(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.numberValue; + double t = self->value.doubleValue(); if (qIsNaN(t)) t = 0; else @@ -2341,7 +2341,7 @@ void DatePrototype::method_setYear(Context *ctx) r = UTC(MakeDate(r, TimeWithinDay(t))); r = TimeClip(r); } - self->value.numberValue = r; + self->value.setDouble(r); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2351,12 +2351,12 @@ void DatePrototype::method_setYear(Context *ctx) void DatePrototype::method_setUTCFullYear(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.numberValue; + double t = self->value.doubleValue(); double year = ctx->argument(0).toNumber(ctx); double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); - self->value.numberValue = t; + self->value.setDouble(t); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2366,12 +2366,12 @@ void DatePrototype::method_setUTCFullYear(Context *ctx) void DatePrototype::method_setFullYear(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.numberValue); + double t = LocalTime(self->value.doubleValue()); double year = ctx->argument(0).toNumber(ctx); double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); - self->value.numberValue = t; + self->value.setDouble(t); ctx->result = self->value; } else { ctx->throwTypeError(); @@ -2381,7 +2381,7 @@ void DatePrototype::method_setFullYear(Context *ctx) void DatePrototype::method_toUTCString(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.numberValue; + double t = self->value.doubleValue(); ctx->result = Value::fromString(ctx, ToUTCString(t)); } } @@ -2406,7 +2406,7 @@ void RegExpCtor::call(Context *ctx) void RegExpPrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); setProperty(ctx, QStringLiteral("constructor"), ctor); setProperty(ctx, QStringLiteral("exec"), method_exec, 0); setProperty(ctx, QStringLiteral("test"), method_test, 0); @@ -2433,14 +2433,14 @@ void RegExpPrototype::method_toString(Context *ctx) // MathObject::MathObject(Context *ctx) { - setProperty(ctx, QStringLiteral("E"), Value::fromNumber(::exp(1.0))); - setProperty(ctx, QStringLiteral("LN2"), Value::fromNumber(::log(2.0))); - setProperty(ctx, QStringLiteral("LN10"), Value::fromNumber(::log(10.0))); - setProperty(ctx, QStringLiteral("LOG2E"), Value::fromNumber(1.0/::log(2.0))); - setProperty(ctx, QStringLiteral("LOG10E"), Value::fromNumber(1.0/::log(10.0))); - setProperty(ctx, QStringLiteral("PI"), Value::fromNumber(qt_PI)); - setProperty(ctx, QStringLiteral("SQRT1_2"), Value::fromNumber(::sqrt(0.5))); - setProperty(ctx, QStringLiteral("SQRT2"), Value::fromNumber(::sqrt(2.0))); + setProperty(ctx, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); + setProperty(ctx, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); + setProperty(ctx, QStringLiteral("LN10"), Value::fromDouble(::log(10.0))); + setProperty(ctx, QStringLiteral("LOG2E"), Value::fromDouble(1.0/::log(2.0))); + setProperty(ctx, QStringLiteral("LOG10E"), Value::fromDouble(1.0/::log(10.0))); + setProperty(ctx, QStringLiteral("PI"), Value::fromDouble(qt_PI)); + setProperty(ctx, QStringLiteral("SQRT1_2"), Value::fromDouble(::sqrt(0.5))); + setProperty(ctx, QStringLiteral("SQRT2"), Value::fromDouble(::sqrt(2.0))); setProperty(ctx, QStringLiteral("abs"), method_abs, 1); setProperty(ctx, QStringLiteral("acos"), method_acos, 1); @@ -2478,36 +2478,36 @@ void MathObject::method_abs(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v == 0) // 0 | -0 - ctx->result = Value::fromNumber(0); + ctx->result = Value::fromDouble(0); else - ctx->result = Value::fromNumber(v < 0 ? -v : v); + ctx->result = Value::fromDouble(v < 0 ? -v : v); } void MathObject::method_acos(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v > 1) - ctx->result = Value::fromNumber(qSNaN()); + ctx->result = Value::fromDouble(qSNaN()); else - ctx->result = Value::fromNumber(::acos(v)); + ctx->result = Value::fromDouble(::acos(v)); } void MathObject::method_asin(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v > 1) - ctx->result = Value::fromNumber(qSNaN()); + ctx->result = Value::fromDouble(qSNaN()); else - ctx->result = Value::fromNumber(::asin(v)); + ctx->result = Value::fromDouble(::asin(v)); } void MathObject::method_atan(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v == 0.0) - ctx->result = Value::fromNumber(v); + ctx->result = Value::fromDouble(v); else - ctx->result = Value::fromNumber(::atan(v)); + ctx->result = Value::fromDouble(::atan(v)); } void MathObject::method_atan2(Context *ctx) @@ -2515,34 +2515,34 @@ void MathObject::method_atan2(Context *ctx) double v1 = ctx->argument(0).toNumber(ctx); double v2 = ctx->argument(1).toNumber(ctx); if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) { - ctx->result = Value::fromNumber(copySign(0, -1.0)); + ctx->result = Value::fromDouble(copySign(0, -1.0)); return; } if ((v1 == 0.0) && (v2 == 0.0)) { if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) { - ctx->result = Value::fromNumber(qt_PI); + ctx->result = Value::fromDouble(qt_PI); return; } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) { - ctx->result = Value::fromNumber(-qt_PI); + ctx->result = Value::fromDouble(-qt_PI); return; } } - ctx->result = Value::fromNumber(::atan2(v1, v2)); + ctx->result = Value::fromDouble(::atan2(v1, v2)); } void MathObject::method_ceil(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v < 0.0 && v > -1.0) - ctx->result = Value::fromNumber(copySign(0, -1.0)); + ctx->result = Value::fromDouble(copySign(0, -1.0)); else - ctx->result = Value::fromNumber(::ceil(v)); + ctx->result = Value::fromDouble(::ceil(v)); } void MathObject::method_cos(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); - ctx->result = Value::fromNumber(::cos(v)); + ctx->result = Value::fromDouble(::cos(v)); } void MathObject::method_exp(Context *ctx) @@ -2550,27 +2550,27 @@ void MathObject::method_exp(Context *ctx) double v = ctx->argument(0).toNumber(ctx); if (qIsInf(v)) { if (copySign(1.0, v) == -1.0) - ctx->result = Value::fromNumber(0); + ctx->result = Value::fromDouble(0); else - ctx->result = Value::fromNumber(qInf()); + ctx->result = Value::fromDouble(qInf()); } else { - ctx->result = Value::fromNumber(::exp(v)); + ctx->result = Value::fromDouble(::exp(v)); } } void MathObject::method_floor(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); - ctx->result = Value::fromNumber(::floor(v)); + ctx->result = Value::fromDouble(::floor(v)); } void MathObject::method_log(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v < 0) - ctx->result = Value::fromNumber(qSNaN()); + ctx->result = Value::fromDouble(qSNaN()); else - ctx->result = Value::fromNumber(::log(v)); + ctx->result = Value::fromDouble(::log(v)); } void MathObject::method_max(Context *ctx) @@ -2581,7 +2581,7 @@ void MathObject::method_max(Context *ctx) if (x > mx || qIsNaN(x)) mx = x; } - ctx->result = Value::fromNumber(mx); + ctx->result = Value::fromDouble(mx); } void MathObject::method_min(Context *ctx) @@ -2594,7 +2594,7 @@ void MathObject::method_min(Context *ctx) mx = x; } } - ctx->result = Value::fromNumber(mx); + ctx->result = Value::fromDouble(mx); } void MathObject::method_pow(Context *ctx) @@ -2603,27 +2603,27 @@ void MathObject::method_pow(Context *ctx) double y = ctx->argument(1).toNumber(ctx); if (qIsNaN(y)) { - ctx->result = Value::fromNumber(qSNaN()); + ctx->result = Value::fromDouble(qSNaN()); return; } if (y == 0) { - ctx->result = Value::fromNumber(1); + ctx->result = Value::fromDouble(1); } else if (((x == 1) || (x == -1)) && qIsInf(y)) { - ctx->result = Value::fromNumber(qSNaN()); + ctx->result = Value::fromDouble(qSNaN()); } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { - ctx->result = Value::fromNumber(qInf()); + ctx->result = Value::fromDouble(qInf()); } else if ((x == 0) && copySign(1.0, x) == -1.0) { if (y < 0) { if (::fmod(-y, 2.0) == 1.0) - ctx->result = Value::fromNumber(-qInf()); + ctx->result = Value::fromDouble(-qInf()); else - ctx->result = Value::fromNumber(qInf()); + ctx->result = Value::fromDouble(qInf()); } else if (y > 0) { if (::fmod(y, 2.0) == 1.0) - ctx->result = Value::fromNumber(copySign(0, -1.0)); + ctx->result = Value::fromDouble(copySign(0, -1.0)); else - ctx->result = Value::fromNumber(0); + ctx->result = Value::fromDouble(0); } } @@ -2643,39 +2643,39 @@ void MathObject::method_pow(Context *ctx) } #endif else { - ctx->result = Value::fromNumber(::pow(x, y)); + ctx->result = Value::fromDouble(::pow(x, y)); } } void MathObject::method_random(Context *ctx) { - ctx->result = Value::fromNumber(qrand() / (double) RAND_MAX); + ctx->result = Value::fromDouble(qrand() / (double) RAND_MAX); } void MathObject::method_round(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); v = copySign(::floor(v + 0.5), v); - ctx->result = Value::fromNumber(v); + ctx->result = Value::fromDouble(v); } void MathObject::method_sin(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); - ctx->result = Value::fromNumber(::sin(v)); + ctx->result = Value::fromDouble(::sin(v)); } void MathObject::method_sqrt(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); - ctx->result = Value::fromNumber(::sqrt(v)); + ctx->result = Value::fromDouble(::sqrt(v)); } void MathObject::method_tan(Context *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v == 0.0) - ctx->result = Value::fromNumber(v); + ctx->result = Value::fromDouble(v); else - ctx->result = Value::fromNumber(::tan(v)); + ctx->result = Value::fromDouble(::tan(v)); } diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 92e2898282..ac9551d243 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -89,7 +89,7 @@ struct NumberCtor: FunctionObject struct NumberPrototype: NumberObject { - NumberPrototype(): NumberObject(Value::fromNumber(0)) {} + NumberPrototype(): NumberObject(Value::fromDouble(0)) {} void init(Context *ctx, const Value &ctor); static void method_toString(Context *ctx); @@ -181,7 +181,7 @@ struct DateCtor: FunctionObject struct DatePrototype: DateObject { - DatePrototype(): DateObject(Value::fromNumber(qSNaN())) {} + DatePrototype(): DateObject(Value::fromDouble(qSNaN())) {} void init(Context *ctx, const Value &ctor); static double getThisDate(Context *ctx); @@ -246,7 +246,7 @@ struct RegExpCtor: FunctionObject struct RegExpPrototype: RegExpObject { - RegExpPrototype(): RegExpObject(Value::fromNumber(qSNaN())) {} + RegExpPrototype(): RegExpObject(Value::fromDouble(qSNaN())) {} void init(Context *ctx, const Value &ctor); static void method_exec(Context *ctx); diff --git a/qv4isel_x86_64.cpp b/qv4isel_x86_64.cpp index c78d9ecbd0..e975a03221 100644 --- a/qv4isel_x86_64.cpp +++ b/qv4isel_x86_64.cpp @@ -497,23 +497,25 @@ void InstructionSelection::visitMove(IR::Move *s) switch (c->type) { case IR::NullType: - amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NULL_TYPE, 4); + amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(ValueData, tag), Value::Null_Type, 4); + amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(ValueData, uint_32), 0, 4); break; case IR::UndefinedType: - amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, UNDEFINED_TYPE, 4); + amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(ValueData, tag), Value::Undefined_Type, 4); + amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(ValueData, uint_32), 0, 4); break; case IR::BoolType: - amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, BOOLEAN_TYPE, 4); - amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(Value, booleanValue), c->value != 0, 1); + amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(ValueData, tag), Value::Boolean_Type, 4); + amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(ValueData, b), c->value != 0, 1); break; case IR::NumberType: amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); + // ### why go through XMM0 here? amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); - amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NUMBER_TYPE, 4); - amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), AMD64_XMM0); + amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(ValueData, dbl), AMD64_XMM0); break; default: @@ -526,8 +528,8 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RSI, t2); amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, 0, 4); amd64_mov_membase_reg(_codePtr, AMD64_RDI, 0, AMD64_RAX, 4); - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(Value, numberValue), 8); - amd64_mov_membase_reg(_codePtr, AMD64_RDI, offsetof(Value, numberValue), AMD64_RAX, 8); + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(ValueData, dbl), 8); + amd64_mov_membase_reg(_codePtr, AMD64_RDI, offsetof(ValueData, dbl), AMD64_RAX, 8); return; } else if (IR::String *str = s->source->asString()) { loadTempAddress(AMD64_RDI, t); @@ -597,18 +599,19 @@ void InstructionSelection::visitMove(IR::Move *s) loadTempAddress(AMD64_RSI, t); loadTempAddress(AMD64_RDX, l); loadTempAddress(AMD64_RCX, r); - +#if 0 + // ### needs adjustments uchar *label1 = 0, *label2 = 0, *label3 = 0; if (b->op == IR::OpMul || b->op == IR::OpAdd || b->op == IR::OpSub || b->op == IR::OpDiv) { - amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RDX, 0, NUMBER_TYPE, 4); + amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RDX, 0, Value::Double_Type, 4); label1 = _codePtr; x86_branch8(_codePtr, X86_CC_NE, 0, 0); - amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RCX, 0, NUMBER_TYPE, 4); + amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RCX, 0, Value::Double_Type, 4); label2 = _codePtr; x86_branch8(_codePtr, X86_CC_NE, 0, 0); - amd64_movsd_reg_membase(_codePtr, AMD64_XMM0, AMD64_RDX, offsetof(Value, numberValue)); - amd64_movsd_reg_membase(_codePtr, AMD64_XMM1, AMD64_RCX, offsetof(Value, numberValue)); + amd64_movsd_reg_membase(_codePtr, AMD64_XMM0, AMD64_RDX, offsetof(ValueData, dbl)); + amd64_movsd_reg_membase(_codePtr, AMD64_XMM1, AMD64_RCX, offsetof(ValueData, dbl)); switch (b->op) { case IR::OpAdd: amd64_sse_addsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); @@ -626,8 +629,8 @@ void InstructionSelection::visitMove(IR::Move *s) Q_UNREACHABLE(); } // switch - amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NUMBER_TYPE, 4); - amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), AMD64_XMM0); + amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, Value::Double_Type, 4); + amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(ValueData, dbl), AMD64_XMM0); label3 = _codePtr; x86_jump32(_codePtr, 0); } @@ -637,6 +640,7 @@ void InstructionSelection::visitMove(IR::Move *s) amd64_patch(label1, _codePtr); amd64_patch(label2, _codePtr); } +#endif void (*op)(Context *, Value *, const Value *, const Value *) = 0; @@ -678,8 +682,9 @@ void InstructionSelection::visitMove(IR::Move *s) break; } qmljs_call_code(_codePtr, op); - if (label3) - amd64_patch(label3, _codePtr); + // ### +// if (label3) +// amd64_patch(label3, _codePtr); return; } } else if (IR::Call *c = s->source->asCall()) { @@ -854,13 +859,13 @@ void InstructionSelection::visitCJump(IR::CJump *s) amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, 0, 4); - amd64_alu_reg_imm(_codePtr, X86_CMP, AMD64_RAX, BOOLEAN_TYPE); + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(ValueData, tag), 4); + amd64_alu_reg_imm(_codePtr, X86_CMP, AMD64_RAX, Value::Boolean_Type); uchar *label1 = _codePtr; x86_branch8(_codePtr, X86_CC_NE, 0, 0); - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(Value, booleanValue), 1); + amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(ValueData, b), 1); uchar *label2 = _codePtr; x86_jump8(_codePtr, 0); @@ -886,16 +891,17 @@ void InstructionSelection::visitCJump(IR::CJump *s) loadTempAddress(AMD64_RSI, l); loadTempAddress(AMD64_RDX, r); +#if 0 // ### FIXME uchar *label1 = 0, *label2 = 0, *label3 = 0; if (b->op != IR::OpInstanceof && b->op != IR::OpIn) { - amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RSI, 0, NUMBER_TYPE, 4); + amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RSI, 0, Value::Double_Type, 4); label1 = _codePtr; x86_branch8(_codePtr, X86_CC_NE, 0, 0); - amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RDX, 0, NUMBER_TYPE, 4); + amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RDX, 0, Value::Double_Type, 4); label2 = _codePtr; x86_branch8(_codePtr, X86_CC_NE, 0, 0); - amd64_movsd_reg_membase(_codePtr, AMD64_XMM0, AMD64_RSI, offsetof(Value, numberValue)); - amd64_movsd_reg_membase(_codePtr, AMD64_XMM1, AMD64_RDX, offsetof(Value, numberValue)); + amd64_movsd_reg_membase(_codePtr, AMD64_XMM0, AMD64_RSI, offsetof(ValueData, dbl)); + amd64_movsd_reg_membase(_codePtr, AMD64_XMM1, AMD64_RDX, offsetof(ValueData, dbl)); int op; switch (b->op) { @@ -921,7 +927,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) amd64_patch(label1, _codePtr); amd64_patch(label2, _codePtr); } - +#endif amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); bool (*op)(Context *, const Value *, const Value *); @@ -941,8 +947,9 @@ void InstructionSelection::visitCJump(IR::CJump *s) qmljs_call_code(_codePtr, op); - if (label3) - amd64_patch(label3, _codePtr); + // ### +// if (label3) +// amd64_patch(label3, _codePtr); x86_mov_reg_imm(_codePtr, X86_EDX, 1); x86_alu_reg8_reg8(_codePtr, X86_CMP, X86_EAX, X86_EDX, 0, 0); -- cgit v1.2.3 From 2b59b61ebf90aa5fadec0ec216d58f2ed3a8f28b Mon Sep 17 00:00:00 2001 From: laknoll Date: Thu, 20 Sep 2012 10:28:43 +0200 Subject: Fix division operation --- qmljs_runtime.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index d4a31cb480..638399f8c9 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -1003,7 +1003,7 @@ inline void __qmljs_div(Context *ctx, Value *result, const Value *left, const Va { double lval = __qmljs_to_number(ctx, left); double rval = __qmljs_to_number(ctx, right); - __qmljs_init_number(result, lval * rval); + __qmljs_init_number(result, lval / rval); } inline void __qmljs_mod(Context *ctx, Value *result, const Value *left, const Value *right) -- cgit v1.2.3 From fd29ac57f8bcdd74d62c6a83a4c73f824ae08ef4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 23 Sep 2012 10:28:13 +0200 Subject: Initial import of MASM --- .gitignore | 3 + main.cpp | 3 +- masm/WeakRandom.h | 11 + masm/assembler/ARMAssembler.cpp | 428 ++ masm/assembler/ARMAssembler.h | 1101 +++ masm/assembler/ARMv7Assembler.cpp | 36 + masm/assembler/ARMv7Assembler.h | 2738 +++++++ masm/assembler/AbstractMacroAssembler.h | 734 ++ masm/assembler/AssemblerBuffer.h | 181 + masm/assembler/AssemblerBufferWithConstantPool.h | 342 + masm/assembler/CodeLocation.h | 218 + masm/assembler/LinkBuffer.cpp | 231 + masm/assembler/LinkBuffer.h | 294 + masm/assembler/MIPSAssembler.h | 997 +++ masm/assembler/MacroAssembler.h | 1046 +++ masm/assembler/MacroAssemblerARM.cpp | 99 + masm/assembler/MacroAssemblerARM.h | 1296 ++++ masm/assembler/MacroAssemblerARMv7.h | 1823 +++++ masm/assembler/MacroAssemblerCodeRef.h | 399 + masm/assembler/MacroAssemblerMIPS.h | 1943 +++++ masm/assembler/MacroAssemblerSH4.cpp | 52 + masm/assembler/MacroAssemblerSH4.h | 2044 +++++ masm/assembler/MacroAssemblerX86.h | 252 + masm/assembler/MacroAssemblerX86Common.h | 1529 ++++ masm/assembler/MacroAssemblerX86_64.h | 603 ++ masm/assembler/RepatchBuffer.h | 153 + masm/assembler/SH4Assembler.h | 2121 +++++ masm/assembler/X86Assembler.h | 2462 ++++++ masm/config.h | 4 + masm/disassembler/Disassembler.h | 49 + masm/disassembler/UDis86Disassembler.cpp | 63 + masm/disassembler/udis86/differences.txt | 22 + masm/disassembler/udis86/itab.py | 354 + masm/disassembler/udis86/optable.xml | 8959 ++++++++++++++++++++++ masm/disassembler/udis86/ud_opcode.py | 235 + masm/disassembler/udis86/ud_optable.py | 103 + masm/disassembler/udis86/udis86.c | 183 + masm/disassembler/udis86/udis86.h | 33 + masm/disassembler/udis86/udis86_decode.c | 1142 +++ masm/disassembler/udis86/udis86_decode.h | 258 + masm/disassembler/udis86/udis86_extern.h | 88 + masm/disassembler/udis86/udis86_input.c | 263 + masm/disassembler/udis86/udis86_input.h | 67 + masm/disassembler/udis86/udis86_itab_holder.c | 34 + masm/disassembler/udis86/udis86_syn-att.c | 253 + masm/disassembler/udis86/udis86_syn-intel.c | 279 + masm/disassembler/udis86/udis86_syn.c | 87 + masm/disassembler/udis86/udis86_syn.h | 47 + masm/disassembler/udis86/udis86_types.h | 238 + masm/jit/JITCompilationEffort.h | 39 + masm/masm.pri | 17 + masm/stubs/ExecutableAllocator.h | 46 + masm/stubs/JSGlobalData.h | 15 + masm/stubs/LLIntData.h | 0 masm/stubs/Options.h | 1 + masm/stubs/WTFStubs.cpp | 39 + masm/wtf/Alignment.h | 70 + masm/wtf/AlwaysInline.h | 23 + masm/wtf/Assertions.h | 393 + masm/wtf/Atomics.h | 237 + masm/wtf/CheckedArithmetic.h | 708 ++ masm/wtf/Compiler.h | 284 + masm/wtf/CryptographicallyRandomNumber.h | 45 + masm/wtf/DataLog.h | 48 + masm/wtf/DynamicAnnotations.h | 96 + masm/wtf/FastAllocBase.h | 427 ++ masm/wtf/FastMalloc.h | 281 + masm/wtf/Locker.h | 48 + masm/wtf/Noncopyable.h | 42 + masm/wtf/NotFound.h | 37 + masm/wtf/NullPtr.h | 56 + masm/wtf/OwnPtr.h | 220 + masm/wtf/OwnPtrCommon.h | 80 + masm/wtf/PassRefPtr.h | 188 + masm/wtf/Platform.h | 1195 +++ masm/wtf/PossiblyNull.h | 59 + masm/wtf/RefCounted.h | 241 + masm/wtf/RefPtr.h | 207 + masm/wtf/StdLibExtras.h | 320 + masm/wtf/ThreadRestrictionVerifier.h | 209 + masm/wtf/ThreadSafeRefCounted.h | 150 + masm/wtf/Threading.h | 117 + masm/wtf/ThreadingPrimitives.h | 169 + masm/wtf/TypeTraits.h | 435 ++ masm/wtf/UnusedParam.h | 44 + masm/wtf/ValueCheck.h | 53 + masm/wtf/Vector.h | 1263 +++ masm/wtf/VectorTraits.h | 104 + qv4isel_masm.cpp | 128 + qv4isel_masm_p.h | 58 + v4.pro | 5 +- 91 files changed, 44095 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 masm/WeakRandom.h create mode 100644 masm/assembler/ARMAssembler.cpp create mode 100644 masm/assembler/ARMAssembler.h create mode 100644 masm/assembler/ARMv7Assembler.cpp create mode 100644 masm/assembler/ARMv7Assembler.h create mode 100644 masm/assembler/AbstractMacroAssembler.h create mode 100644 masm/assembler/AssemblerBuffer.h create mode 100644 masm/assembler/AssemblerBufferWithConstantPool.h create mode 100644 masm/assembler/CodeLocation.h create mode 100644 masm/assembler/LinkBuffer.cpp create mode 100644 masm/assembler/LinkBuffer.h create mode 100644 masm/assembler/MIPSAssembler.h create mode 100644 masm/assembler/MacroAssembler.h create mode 100644 masm/assembler/MacroAssemblerARM.cpp create mode 100644 masm/assembler/MacroAssemblerARM.h create mode 100644 masm/assembler/MacroAssemblerARMv7.h create mode 100644 masm/assembler/MacroAssemblerCodeRef.h create mode 100644 masm/assembler/MacroAssemblerMIPS.h create mode 100644 masm/assembler/MacroAssemblerSH4.cpp create mode 100644 masm/assembler/MacroAssemblerSH4.h create mode 100644 masm/assembler/MacroAssemblerX86.h create mode 100644 masm/assembler/MacroAssemblerX86Common.h create mode 100644 masm/assembler/MacroAssemblerX86_64.h create mode 100644 masm/assembler/RepatchBuffer.h create mode 100644 masm/assembler/SH4Assembler.h create mode 100644 masm/assembler/X86Assembler.h create mode 100644 masm/config.h create mode 100644 masm/disassembler/Disassembler.h create mode 100644 masm/disassembler/UDis86Disassembler.cpp create mode 100644 masm/disassembler/udis86/differences.txt create mode 100644 masm/disassembler/udis86/itab.py create mode 100644 masm/disassembler/udis86/optable.xml create mode 100644 masm/disassembler/udis86/ud_opcode.py create mode 100644 masm/disassembler/udis86/ud_optable.py create mode 100644 masm/disassembler/udis86/udis86.c create mode 100644 masm/disassembler/udis86/udis86.h create mode 100644 masm/disassembler/udis86/udis86_decode.c create mode 100644 masm/disassembler/udis86/udis86_decode.h create mode 100644 masm/disassembler/udis86/udis86_extern.h create mode 100644 masm/disassembler/udis86/udis86_input.c create mode 100644 masm/disassembler/udis86/udis86_input.h create mode 100644 masm/disassembler/udis86/udis86_itab_holder.c create mode 100644 masm/disassembler/udis86/udis86_syn-att.c create mode 100644 masm/disassembler/udis86/udis86_syn-intel.c create mode 100644 masm/disassembler/udis86/udis86_syn.c create mode 100644 masm/disassembler/udis86/udis86_syn.h create mode 100644 masm/disassembler/udis86/udis86_types.h create mode 100644 masm/jit/JITCompilationEffort.h create mode 100644 masm/masm.pri create mode 100644 masm/stubs/ExecutableAllocator.h create mode 100644 masm/stubs/JSGlobalData.h create mode 100644 masm/stubs/LLIntData.h create mode 100644 masm/stubs/Options.h create mode 100644 masm/stubs/WTFStubs.cpp create mode 100644 masm/wtf/Alignment.h create mode 100644 masm/wtf/AlwaysInline.h create mode 100644 masm/wtf/Assertions.h create mode 100644 masm/wtf/Atomics.h create mode 100644 masm/wtf/CheckedArithmetic.h create mode 100644 masm/wtf/Compiler.h create mode 100644 masm/wtf/CryptographicallyRandomNumber.h create mode 100644 masm/wtf/DataLog.h create mode 100644 masm/wtf/DynamicAnnotations.h create mode 100644 masm/wtf/FastAllocBase.h create mode 100644 masm/wtf/FastMalloc.h create mode 100644 masm/wtf/Locker.h create mode 100644 masm/wtf/Noncopyable.h create mode 100644 masm/wtf/NotFound.h create mode 100644 masm/wtf/NullPtr.h create mode 100644 masm/wtf/OwnPtr.h create mode 100644 masm/wtf/OwnPtrCommon.h create mode 100644 masm/wtf/PassRefPtr.h create mode 100644 masm/wtf/Platform.h create mode 100644 masm/wtf/PossiblyNull.h create mode 100644 masm/wtf/RefCounted.h create mode 100644 masm/wtf/RefPtr.h create mode 100644 masm/wtf/StdLibExtras.h create mode 100644 masm/wtf/ThreadRestrictionVerifier.h create mode 100644 masm/wtf/ThreadSafeRefCounted.h create mode 100644 masm/wtf/Threading.h create mode 100644 masm/wtf/ThreadingPrimitives.h create mode 100644 masm/wtf/TypeTraits.h create mode 100644 masm/wtf/UnusedParam.h create mode 100644 masm/wtf/ValueCheck.h create mode 100644 masm/wtf/Vector.h create mode 100644 masm/wtf/VectorTraits.h create mode 100644 qv4isel_masm.cpp create mode 100644 qv4isel_masm_p.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..15858ed0c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +Makefile +v4 diff --git a/main.cpp b/main.cpp index 08917aee04..460663a059 100644 --- a/main.cpp +++ b/main.cpp @@ -2,6 +2,7 @@ #include "qmljs_objects.h" #include "qv4codegen_p.h" #include "qv4isel_x86_64_p.h" +#include "qv4isel_masm_p.h" #include "qv4isel_moth_p.h" #include "qv4vme_moth_p.h" #include "qv4syntaxchecker_p.h" @@ -226,7 +227,7 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS foreach (IR::Function *function, module.functions) isel(function); } else { - x86_64::InstructionSelection isel(vm, &module, code); + MASM::InstructionSelection isel(vm, &module, code); foreach (IR::Function *function, module.functions) isel(function); diff --git a/masm/WeakRandom.h b/masm/WeakRandom.h new file mode 100644 index 0000000000..65b8a22a97 --- /dev/null +++ b/masm/WeakRandom.h @@ -0,0 +1,11 @@ +#ifndef MASM_WEAKRANDOM_H +#define MASM_WEAKRANDOM_H + +#include + +struct WeakRandom { + WeakRandom(int) {} + uint32_t getUint32() { return 0; } +}; + +#endif // MASM_WEAKRANDOM_H diff --git a/masm/assembler/ARMAssembler.cpp b/masm/assembler/ARMAssembler.cpp new file mode 100644 index 0000000000..9655557a5d --- /dev/null +++ b/masm/assembler/ARMAssembler.cpp @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2009 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#include "ARMAssembler.h" + +namespace JSC { + +// Patching helpers + +void ARMAssembler::patchConstantPoolLoad(void* loadAddr, void* constPoolAddr) +{ + ARMWord *ldr = reinterpret_cast(loadAddr); + ARMWord diff = reinterpret_cast(constPoolAddr) - ldr; + ARMWord index = (*ldr & 0xfff) >> 1; + + ASSERT(diff >= 1); + if (diff >= 2 || index > 0) { + diff = (diff + index - 2) * sizeof(ARMWord); + ASSERT(diff <= 0xfff); + *ldr = (*ldr & ~0xfff) | diff; + } else + *ldr = (*ldr & ~(0xfff | ARMAssembler::DataTransferUp)) | sizeof(ARMWord); +} + +// Handle immediates + +ARMWord ARMAssembler::getOp2(ARMWord imm) +{ + int rol; + + if (imm <= 0xff) + return Op2Immediate | imm; + + if ((imm & 0xff000000) == 0) { + imm <<= 8; + rol = 8; + } + else { + imm = (imm << 24) | (imm >> 8); + rol = 0; + } + + if ((imm & 0xff000000) == 0) { + imm <<= 8; + rol += 4; + } + + if ((imm & 0xf0000000) == 0) { + imm <<= 4; + rol += 2; + } + + if ((imm & 0xc0000000) == 0) { + imm <<= 2; + rol += 1; + } + + if ((imm & 0x00ffffff) == 0) + return Op2Immediate | (imm >> 24) | (rol << 8); + + return InvalidImmediate; +} + +int ARMAssembler::genInt(int reg, ARMWord imm, bool positive) +{ + // Step1: Search a non-immediate part + ARMWord mask; + ARMWord imm1; + ARMWord imm2; + int rol; + + mask = 0xff000000; + rol = 8; + while(1) { + if ((imm & mask) == 0) { + imm = (imm << rol) | (imm >> (32 - rol)); + rol = 4 + (rol >> 1); + break; + } + rol += 2; + mask >>= 2; + if (mask & 0x3) { + // rol 8 + imm = (imm << 8) | (imm >> 24); + mask = 0xff00; + rol = 24; + while (1) { + if ((imm & mask) == 0) { + imm = (imm << rol) | (imm >> (32 - rol)); + rol = (rol >> 1) - 8; + break; + } + rol += 2; + mask >>= 2; + if (mask & 0x3) + return 0; + } + break; + } + } + + ASSERT((imm & 0xff) == 0); + + if ((imm & 0xff000000) == 0) { + imm1 = Op2Immediate | ((imm >> 16) & 0xff) | (((rol + 4) & 0xf) << 8); + imm2 = Op2Immediate | ((imm >> 8) & 0xff) | (((rol + 8) & 0xf) << 8); + } else if (imm & 0xc0000000) { + imm1 = Op2Immediate | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8); + imm <<= 8; + rol += 4; + + if ((imm & 0xff000000) == 0) { + imm <<= 8; + rol += 4; + } + + if ((imm & 0xf0000000) == 0) { + imm <<= 4; + rol += 2; + } + + if ((imm & 0xc0000000) == 0) { + imm <<= 2; + rol += 1; + } + + if ((imm & 0x00ffffff) == 0) + imm2 = Op2Immediate | (imm >> 24) | ((rol & 0xf) << 8); + else + return 0; + } else { + if ((imm & 0xf0000000) == 0) { + imm <<= 4; + rol += 2; + } + + if ((imm & 0xc0000000) == 0) { + imm <<= 2; + rol += 1; + } + + imm1 = Op2Immediate | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8); + imm <<= 8; + rol += 4; + + if ((imm & 0xf0000000) == 0) { + imm <<= 4; + rol += 2; + } + + if ((imm & 0xc0000000) == 0) { + imm <<= 2; + rol += 1; + } + + if ((imm & 0x00ffffff) == 0) + imm2 = Op2Immediate | (imm >> 24) | ((rol & 0xf) << 8); + else + return 0; + } + + if (positive) { + mov(reg, imm1); + orr(reg, reg, imm2); + } else { + mvn(reg, imm1); + bic(reg, reg, imm2); + } + + return 1; +} + +ARMWord ARMAssembler::getImm(ARMWord imm, int tmpReg, bool invert) +{ + ARMWord tmp; + + // Do it by 1 instruction + tmp = getOp2(imm); + if (tmp != InvalidImmediate) + return tmp; + + tmp = getOp2(~imm); + if (tmp != InvalidImmediate) { + if (invert) + return tmp | Op2InvertedImmediate; + mvn(tmpReg, tmp); + return tmpReg; + } + + return encodeComplexImm(imm, tmpReg); +} + +void ARMAssembler::moveImm(ARMWord imm, int dest) +{ + ARMWord tmp; + + // Do it by 1 instruction + tmp = getOp2(imm); + if (tmp != InvalidImmediate) { + mov(dest, tmp); + return; + } + + tmp = getOp2(~imm); + if (tmp != InvalidImmediate) { + mvn(dest, tmp); + return; + } + + encodeComplexImm(imm, dest); +} + +ARMWord ARMAssembler::encodeComplexImm(ARMWord imm, int dest) +{ +#if WTF_ARM_ARCH_AT_LEAST(7) + ARMWord tmp = getImm16Op2(imm); + if (tmp != InvalidImmediate) { + movw(dest, tmp); + return dest; + } + movw(dest, getImm16Op2(imm & 0xffff)); + movt(dest, getImm16Op2(imm >> 16)); + return dest; +#else + // Do it by 2 instruction + if (genInt(dest, imm, true)) + return dest; + if (genInt(dest, ~imm, false)) + return dest; + + ldrImmediate(dest, imm); + return dest; +#endif +} + +// Memory load/store helpers + +void ARMAssembler::dataTransfer32(DataTransferTypeA transferType, RegisterID srcDst, RegisterID base, int32_t offset) +{ + if (offset >= 0) { + if (offset <= 0xfff) + dtrUp(transferType, srcDst, base, offset); + else if (offset <= 0xfffff) { + add(ARMRegisters::S0, base, Op2Immediate | (offset >> 12) | (10 << 8)); + dtrUp(transferType, srcDst, ARMRegisters::S0, (offset & 0xfff)); + } else { + moveImm(offset, ARMRegisters::S0); + dtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); + } + } else { + if (offset >= -0xfff) + dtrDown(transferType, srcDst, base, -offset); + else if (offset >= -0xfffff) { + sub(ARMRegisters::S0, base, Op2Immediate | (-offset >> 12) | (10 << 8)); + dtrDown(transferType, srcDst, ARMRegisters::S0, (-offset & 0xfff)); + } else { + moveImm(offset, ARMRegisters::S0); + dtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); + } + } +} + +void ARMAssembler::baseIndexTransfer32(DataTransferTypeA transferType, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) +{ + ASSERT(scale >= 0 && scale <= 3); + ARMWord op2 = lsl(index, scale); + + if (!offset) { + dtrUpRegister(transferType, srcDst, base, op2); + return; + } + + add(ARMRegisters::S1, base, op2); + dataTransfer32(transferType, srcDst, ARMRegisters::S1, offset); +} + +void ARMAssembler::dataTransfer16(DataTransferTypeB transferType, RegisterID srcDst, RegisterID base, int32_t offset) +{ + if (offset >= 0) { + if (offset <= 0xff) + halfDtrUp(transferType, srcDst, base, getOp2Half(offset)); + else if (offset <= 0xffff) { + add(ARMRegisters::S0, base, Op2Immediate | (offset >> 8) | (12 << 8)); + halfDtrUp(transferType, srcDst, ARMRegisters::S0, getOp2Half(offset & 0xff)); + } else { + moveImm(offset, ARMRegisters::S0); + halfDtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); + } + } else { + if (offset >= -0xff) + halfDtrDown(transferType, srcDst, base, getOp2Half(-offset)); + else if (offset >= -0xffff) { + sub(ARMRegisters::S0, base, Op2Immediate | (-offset >> 8) | (12 << 8)); + halfDtrDown(transferType, srcDst, ARMRegisters::S0, getOp2Half(-offset & 0xff)); + } else { + moveImm(offset, ARMRegisters::S0); + halfDtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); + } + } +} + +void ARMAssembler::baseIndexTransfer16(DataTransferTypeB transferType, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) +{ + if (!scale && !offset) { + halfDtrUpRegister(transferType, srcDst, base, index); + return; + } + + add(ARMRegisters::S1, base, lsl(index, scale)); + dataTransfer16(transferType, srcDst, ARMRegisters::S1, offset); +} + +void ARMAssembler::dataTransferFloat(DataTransferTypeFloat transferType, FPRegisterID srcDst, RegisterID base, int32_t offset) +{ + // VFP cannot directly access memory that is not four-byte-aligned + if (!(offset & 0x3)) { + if (offset <= 0x3ff && offset >= 0) { + doubleDtrUp(transferType, srcDst, base, offset >> 2); + return; + } + if (offset <= 0x3ffff && offset >= 0) { + add(ARMRegisters::S0, base, Op2Immediate | (offset >> 10) | (11 << 8)); + doubleDtrUp(transferType, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff); + return; + } + offset = -offset; + + if (offset <= 0x3ff && offset >= 0) { + doubleDtrDown(transferType, srcDst, base, offset >> 2); + return; + } + if (offset <= 0x3ffff && offset >= 0) { + sub(ARMRegisters::S0, base, Op2Immediate | (offset >> 10) | (11 << 8)); + doubleDtrDown(transferType, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff); + return; + } + offset = -offset; + } + + moveImm(offset, ARMRegisters::S0); + add(ARMRegisters::S0, ARMRegisters::S0, base); + doubleDtrUp(transferType, srcDst, ARMRegisters::S0, 0); +} + +void ARMAssembler::baseIndexTransferFloat(DataTransferTypeFloat transferType, FPRegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) +{ + add(ARMRegisters::S1, base, lsl(index, scale)); + dataTransferFloat(transferType, srcDst, ARMRegisters::S1, offset); +} + +PassRefPtr ARMAssembler::executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) +{ + // 64-bit alignment is required for next constant pool and JIT code as well + m_buffer.flushWithoutBarrier(true); + if (!m_buffer.isAligned(8)) + bkpt(0); + + RefPtr result = m_buffer.executableCopy(globalData, ownerUID, effort); + char* data = reinterpret_cast(result->start()); + + for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) { + // The last bit is set if the constant must be placed on constant pool. + int pos = (iter->m_offset) & (~0x1); + ARMWord* ldrAddr = reinterpret_cast_ptr(data + pos); + ARMWord* addr = getLdrImmAddress(ldrAddr); + if (*addr != InvalidBranchTarget) { + if (!(iter->m_offset & 1)) { + intptr_t difference = reinterpret_cast_ptr(data + *addr) - (ldrAddr + DefaultPrefetchOffset); + + if ((difference <= MaximumBranchOffsetDistance && difference >= MinimumBranchOffsetDistance)) { + *ldrAddr = B | getConditionalField(*ldrAddr) | (difference & BranchOffsetMask); + continue; + } + } + *addr = reinterpret_cast(data + *addr); + } + } + + return result; +} + +#if OS(LINUX) && COMPILER(RVCT) + +__asm void ARMAssembler::cacheFlush(void* code, size_t size) +{ + ARM + push {r7} + add r1, r1, r0 + mov r7, #0xf0000 + add r7, r7, #0x2 + mov r2, #0x0 + svc #0x0 + pop {r7} + bx lr +} + +#endif + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) diff --git a/masm/assembler/ARMAssembler.h b/masm/assembler/ARMAssembler.h new file mode 100644 index 0000000000..38d0c5e6d2 --- /dev/null +++ b/masm/assembler/ARMAssembler.h @@ -0,0 +1,1101 @@ +/* + * Copyright (C) 2009, 2010 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ARMAssembler_h +#define ARMAssembler_h + +#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#include "AssemblerBufferWithConstantPool.h" +#include "JITCompilationEffort.h" +#include +namespace JSC { + + typedef uint32_t ARMWord; + + namespace ARMRegisters { + typedef enum { + r0 = 0, + r1, + r2, + r3, S0 = r3, /* Same as thumb assembler. */ + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, S1 = r12, + r13, sp = r13, + r14, lr = r14, + r15, pc = r15 + } RegisterID; + + typedef enum { + d0, + d1, + d2, + d3, + d4, + d5, + d6, + d7, SD0 = d7, /* Same as thumb assembler. */ + d8, + d9, + d10, + d11, + d12, + d13, + d14, + d15, + d16, + d17, + d18, + d19, + d20, + d21, + d22, + d23, + d24, + d25, + d26, + d27, + d28, + d29, + d30, + d31 + } FPRegisterID; + + } // namespace ARMRegisters + + class ARMAssembler { + public: + typedef ARMRegisters::RegisterID RegisterID; + typedef ARMRegisters::FPRegisterID FPRegisterID; + typedef AssemblerBufferWithConstantPool<2048, 4, 4, ARMAssembler> ARMBuffer; + typedef SegmentedVector Jumps; + + ARMAssembler() + : m_indexOfTailOfLastWatchpoint(1) + { + } + + // ARM conditional constants + typedef enum { + EQ = 0x00000000, // Zero + NE = 0x10000000, // Non-zero + CS = 0x20000000, + CC = 0x30000000, + MI = 0x40000000, + PL = 0x50000000, + VS = 0x60000000, + VC = 0x70000000, + HI = 0x80000000, + LS = 0x90000000, + GE = 0xa0000000, + LT = 0xb0000000, + GT = 0xc0000000, + LE = 0xd0000000, + AL = 0xe0000000 + } Condition; + + // ARM instruction constants + enum { + AND = (0x0 << 21), + EOR = (0x1 << 21), + SUB = (0x2 << 21), + RSB = (0x3 << 21), + ADD = (0x4 << 21), + ADC = (0x5 << 21), + SBC = (0x6 << 21), + RSC = (0x7 << 21), + TST = (0x8 << 21), + TEQ = (0x9 << 21), + CMP = (0xa << 21), + CMN = (0xb << 21), + ORR = (0xc << 21), + MOV = (0xd << 21), + BIC = (0xe << 21), + MVN = (0xf << 21), + MUL = 0x00000090, + MULL = 0x00c00090, + VMOV_F64 = 0x0eb00b40, + VADD_F64 = 0x0e300b00, + VDIV_F64 = 0x0e800b00, + VSUB_F64 = 0x0e300b40, + VMUL_F64 = 0x0e200b00, + VCMP_F64 = 0x0eb40b40, + VSQRT_F64 = 0x0eb10bc0, + VABS_F64 = 0x0eb00bc0, + VNEG_F64 = 0x0eb10b40, + STMDB = 0x09200000, + LDMIA = 0x08b00000, + B = 0x0a000000, + BL = 0x0b000000, + BX = 0x012fff10, + VMOV_VFP64 = 0x0c400a10, + VMOV_ARM64 = 0x0c500a10, + VMOV_VFP32 = 0x0e000a10, + VMOV_ARM32 = 0x0e100a10, + VCVT_F64_S32 = 0x0eb80bc0, + VCVT_S32_F64 = 0x0ebd0b40, + VCVT_U32_F64 = 0x0ebc0b40, + VCVT_F32_F64 = 0x0eb70bc0, + VCVT_F64_F32 = 0x0eb70ac0, + VMRS_APSR = 0x0ef1fa10, + CLZ = 0x016f0f10, + BKPT = 0xe1200070, + BLX = 0x012fff30, +#if WTF_ARM_ARCH_AT_LEAST(7) + MOVW = 0x03000000, + MOVT = 0x03400000, +#endif + NOP = 0xe1a00000, + }; + + enum { + Op2Immediate = (1 << 25), + ImmediateForHalfWordTransfer = (1 << 22), + Op2InvertedImmediate = (1 << 26), + SetConditionalCodes = (1 << 20), + Op2IsRegisterArgument = (1 << 25), + // Data transfer flags. + DataTransferUp = (1 << 23), + DataTransferWriteBack = (1 << 21), + DataTransferPostUpdate = (1 << 24), + DataTransferLoad = (1 << 20), + ByteDataTransfer = (1 << 22), + }; + + enum DataTransferTypeA { + LoadUint32 = 0x05000000 | DataTransferLoad, + LoadUint8 = 0x05400000 | DataTransferLoad, + StoreUint32 = 0x05000000, + StoreUint8 = 0x05400000, + }; + + enum DataTransferTypeB { + LoadUint16 = 0x010000b0 | DataTransferLoad, + LoadInt16 = 0x010000f0 | DataTransferLoad, + LoadInt8 = 0x010000d0 | DataTransferLoad, + StoreUint16 = 0x010000b0, + }; + + enum DataTransferTypeFloat { + LoadFloat = 0x0d000a00 | DataTransferLoad, + LoadDouble = 0x0d000b00 | DataTransferLoad, + StoreFloat = 0x0d000a00, + StoreDouble = 0x0d000b00, + }; + + // Masks of ARM instructions + enum { + BranchOffsetMask = 0x00ffffff, + ConditionalFieldMask = 0xf0000000, + DataTransferOffsetMask = 0xfff, + }; + + enum { + MinimumBranchOffsetDistance = -0x00800000, + MaximumBranchOffsetDistance = 0x007fffff, + }; + + enum { + padForAlign8 = 0x00, + padForAlign16 = 0x0000, + padForAlign32 = 0xe12fff7f // 'bkpt 0xffff' instruction. + }; + + static const ARMWord InvalidImmediate = 0xf0000000; + static const ARMWord InvalidBranchTarget = 0xffffffff; + static const int DefaultPrefetchOffset = 2; + + static const ARMWord BlxInstructionMask = 0x012fff30; + static const ARMWord LdrOrAddInstructionMask = 0x0ff00000; + static const ARMWord LdrPcImmediateInstructionMask = 0x0f7f0000; + + static const ARMWord AddImmediateInstruction = 0x02800000; + static const ARMWord BlxInstruction = 0x012fff30; + static const ARMWord LdrImmediateInstruction = 0x05900000; + static const ARMWord LdrPcImmediateInstruction = 0x051f0000; + + // Instruction formating + + void emitInstruction(ARMWord op, int rd, int rn, ARMWord op2) + { + ASSERT(((op2 & ~Op2Immediate) <= 0xfff) || (((op2 & ~ImmediateForHalfWordTransfer) <= 0xfff))); + m_buffer.putInt(op | RN(rn) | RD(rd) | op2); + } + + void emitDoublePrecisionInstruction(ARMWord op, int dd, int dn, int dm) + { + ASSERT((dd >= 0 && dd <= 31) && (dn >= 0 && dn <= 31) && (dm >= 0 && dm <= 31)); + m_buffer.putInt(op | ((dd & 0xf) << 12) | ((dd & 0x10) << (22 - 4)) + | ((dn & 0xf) << 16) | ((dn & 0x10) << (7 - 4)) + | (dm & 0xf) | ((dm & 0x10) << (5 - 4))); + } + + void emitSinglePrecisionInstruction(ARMWord op, int sd, int sn, int sm) + { + ASSERT((sd >= 0 && sd <= 31) && (sn >= 0 && sn <= 31) && (sm >= 0 && sm <= 31)); + m_buffer.putInt(op | ((sd >> 1) << 12) | ((sd & 0x1) << 22) + | ((sn >> 1) << 16) | ((sn & 0x1) << 7) + | (sm >> 1) | ((sm & 0x1) << 5)); + } + + void bitAnd(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | AND, rd, rn, op2); + } + + void bitAnds(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | AND | SetConditionalCodes, rd, rn, op2); + } + + void eor(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | EOR, rd, rn, op2); + } + + void eors(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | EOR | SetConditionalCodes, rd, rn, op2); + } + + void sub(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | SUB, rd, rn, op2); + } + + void subs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | SUB | SetConditionalCodes, rd, rn, op2); + } + + void rsb(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | RSB, rd, rn, op2); + } + + void rsbs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | RSB | SetConditionalCodes, rd, rn, op2); + } + + void add(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ADD, rd, rn, op2); + } + + void adds(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ADD | SetConditionalCodes, rd, rn, op2); + } + + void adc(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ADC, rd, rn, op2); + } + + void adcs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ADC | SetConditionalCodes, rd, rn, op2); + } + + void sbc(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | SBC, rd, rn, op2); + } + + void sbcs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | SBC | SetConditionalCodes, rd, rn, op2); + } + + void rsc(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | RSC, rd, rn, op2); + } + + void rscs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | RSC | SetConditionalCodes, rd, rn, op2); + } + + void tst(int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | TST | SetConditionalCodes, 0, rn, op2); + } + + void teq(int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | TEQ | SetConditionalCodes, 0, rn, op2); + } + + void cmp(int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | CMP | SetConditionalCodes, 0, rn, op2); + } + + void cmn(int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | CMN | SetConditionalCodes, 0, rn, op2); + } + + void orr(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ORR, rd, rn, op2); + } + + void orrs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ORR | SetConditionalCodes, rd, rn, op2); + } + + void mov(int rd, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | MOV, rd, ARMRegisters::r0, op2); + } + +#if WTF_ARM_ARCH_AT_LEAST(7) + void movw(int rd, ARMWord op2, Condition cc = AL) + { + ASSERT((op2 | 0xf0fff) == 0xf0fff); + m_buffer.putInt(toARMWord(cc) | MOVW | RD(rd) | op2); + } + + void movt(int rd, ARMWord op2, Condition cc = AL) + { + ASSERT((op2 | 0xf0fff) == 0xf0fff); + m_buffer.putInt(toARMWord(cc) | MOVT | RD(rd) | op2); + } +#endif + + void movs(int rd, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | MOV | SetConditionalCodes, rd, ARMRegisters::r0, op2); + } + + void bic(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | BIC, rd, rn, op2); + } + + void bics(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | BIC | SetConditionalCodes, rd, rn, op2); + } + + void mvn(int rd, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | MVN, rd, ARMRegisters::r0, op2); + } + + void mvns(int rd, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | MVN | SetConditionalCodes, rd, ARMRegisters::r0, op2); + } + + void mul(int rd, int rn, int rm, Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | MUL | RN(rd) | RS(rn) | RM(rm)); + } + + void muls(int rd, int rn, int rm, Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | MUL | SetConditionalCodes | RN(rd) | RS(rn) | RM(rm)); + } + + void mull(int rdhi, int rdlo, int rn, int rm, Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | MULL | RN(rdhi) | RD(rdlo) | RS(rn) | RM(rm)); + } + + void vmov_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VMOV_F64, dd, 0, dm); + } + + void vadd_f64(int dd, int dn, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VADD_F64, dd, dn, dm); + } + + void vdiv_f64(int dd, int dn, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VDIV_F64, dd, dn, dm); + } + + void vsub_f64(int dd, int dn, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VSUB_F64, dd, dn, dm); + } + + void vmul_f64(int dd, int dn, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VMUL_F64, dd, dn, dm); + } + + void vcmp_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VCMP_F64, dd, 0, dm); + } + + void vsqrt_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VSQRT_F64, dd, 0, dm); + } + + void vabs_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VABS_F64, dd, 0, dm); + } + + void vneg_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VNEG_F64, dd, 0, dm); + } + + void ldrImmediate(int rd, ARMWord imm, Condition cc = AL) + { + m_buffer.putIntWithConstantInt(toARMWord(cc) | LoadUint32 | DataTransferUp | RN(ARMRegisters::pc) | RD(rd), imm, true); + } + + void ldrUniqueImmediate(int rd, ARMWord imm, Condition cc = AL) + { + m_buffer.putIntWithConstantInt(toARMWord(cc) | LoadUint32 | DataTransferUp | RN(ARMRegisters::pc) | RD(rd), imm); + } + + void dtrUp(DataTransferTypeA transferType, int rd, int rb, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rb, op2); + } + + void dtrUpRegister(DataTransferTypeA transferType, int rd, int rb, int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | DataTransferUp | Op2IsRegisterArgument, rd, rb, rm); + } + + void dtrDown(DataTransferTypeA transferType, int rd, int rb, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType, rd, rb, op2); + } + + void dtrDownRegister(DataTransferTypeA transferType, int rd, int rb, int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | Op2IsRegisterArgument, rd, rb, rm); + } + + void halfDtrUp(DataTransferTypeB transferType, int rd, int rb, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rb, op2); + } + + void halfDtrUpRegister(DataTransferTypeB transferType, int rd, int rn, int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rn, rm); + } + + void halfDtrDown(DataTransferTypeB transferType, int rd, int rb, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType, rd, rb, op2); + } + + void halfDtrDownRegister(DataTransferTypeB transferType, int rd, int rn, int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType, rd, rn, rm); + } + + void doubleDtrUp(DataTransferTypeFloat type, int rd, int rb, ARMWord op2, Condition cc = AL) + { + ASSERT(op2 <= 0xff && rd <= 15); + /* Only d0-d15 and s0, s2, s4 ... s30 are supported. */ + m_buffer.putInt(toARMWord(cc) | DataTransferUp | type | (rd << 12) | RN(rb) | op2); + } + + void doubleDtrDown(DataTransferTypeFloat type, int rd, int rb, ARMWord op2, Condition cc = AL) + { + ASSERT(op2 <= 0xff && rd <= 15); + /* Only d0-d15 and s0, s2, s4 ... s30 are supported. */ + m_buffer.putInt(toARMWord(cc) | type | (rd << 12) | RN(rb) | op2); + } + + void push(int reg, Condition cc = AL) + { + ASSERT(ARMWord(reg) <= 0xf); + m_buffer.putInt(toARMWord(cc) | StoreUint32 | DataTransferWriteBack | RN(ARMRegisters::sp) | RD(reg) | 0x4); + } + + void pop(int reg, Condition cc = AL) + { + ASSERT(ARMWord(reg) <= 0xf); + m_buffer.putInt(toARMWord(cc) | (LoadUint32 ^ DataTransferPostUpdate) | DataTransferUp | RN(ARMRegisters::sp) | RD(reg) | 0x4); + } + + inline void poke(int reg, Condition cc = AL) + { + dtrDown(StoreUint32, ARMRegisters::sp, 0, reg, cc); + } + + inline void peek(int reg, Condition cc = AL) + { + dtrUp(LoadUint32, reg, ARMRegisters::sp, 0, cc); + } + + void vmov_vfp64(int sm, int rt, int rt2, Condition cc = AL) + { + ASSERT(rt != rt2); + m_buffer.putInt(toARMWord(cc) | VMOV_VFP64 | RN(rt2) | RD(rt) | (sm & 0xf) | ((sm & 0x10) << (5 - 4))); + } + + void vmov_arm64(int rt, int rt2, int sm, Condition cc = AL) + { + ASSERT(rt != rt2); + m_buffer.putInt(toARMWord(cc) | VMOV_ARM64 | RN(rt2) | RD(rt) | (sm & 0xf) | ((sm & 0x10) << (5 - 4))); + } + + void vmov_vfp32(int sn, int rt, Condition cc = AL) + { + ASSERT(rt <= 15); + emitSinglePrecisionInstruction(toARMWord(cc) | VMOV_VFP32, rt << 1, sn, 0); + } + + void vmov_arm32(int rt, int sn, Condition cc = AL) + { + ASSERT(rt <= 15); + emitSinglePrecisionInstruction(toARMWord(cc) | VMOV_ARM32, rt << 1, sn, 0); + } + + void vcvt_f64_s32(int dd, int sm, Condition cc = AL) + { + ASSERT(!(sm & 0x1)); // sm must be divisible by 2 + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F64_S32, dd, 0, (sm >> 1)); + } + + void vcvt_s32_f64(int sd, int dm, Condition cc = AL) + { + ASSERT(!(sd & 0x1)); // sd must be divisible by 2 + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_S32_F64, (sd >> 1), 0, dm); + } + + void vcvt_u32_f64(int sd, int dm, Condition cc = AL) + { + ASSERT(!(sd & 0x1)); // sd must be divisible by 2 + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_U32_F64, (sd >> 1), 0, dm); + } + + void vcvt_f64_f32(int dd, int sm, Condition cc = AL) + { + ASSERT(dd <= 15 && sm <= 15); + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F64_F32, dd, 0, sm); + } + + void vcvt_f32_f64(int dd, int sm, Condition cc = AL) + { + ASSERT(dd <= 15 && sm <= 15); + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F32_F64, dd, 0, sm); + } + + void vmrs_apsr(Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | VMRS_APSR); + } + + void clz(int rd, int rm, Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | CLZ | RD(rd) | RM(rm)); + } + + void bkpt(ARMWord value) + { + m_buffer.putInt(BKPT | ((value & 0xff0) << 4) | (value & 0xf)); + } + + void nop() + { + m_buffer.putInt(NOP); + } + + void bx(int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | BX, 0, 0, RM(rm)); + } + + AssemblerLabel blx(int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | BLX, 0, 0, RM(rm)); + return m_buffer.label(); + } + + static ARMWord lsl(int reg, ARMWord value) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(value <= 0x1f); + return reg | (value << 7) | 0x00; + } + + static ARMWord lsr(int reg, ARMWord value) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(value <= 0x1f); + return reg | (value << 7) | 0x20; + } + + static ARMWord asr(int reg, ARMWord value) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(value <= 0x1f); + return reg | (value << 7) | 0x40; + } + + static ARMWord lslRegister(int reg, int shiftReg) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(shiftReg <= ARMRegisters::pc); + return reg | (shiftReg << 8) | 0x10; + } + + static ARMWord lsrRegister(int reg, int shiftReg) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(shiftReg <= ARMRegisters::pc); + return reg | (shiftReg << 8) | 0x30; + } + + static ARMWord asrRegister(int reg, int shiftReg) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(shiftReg <= ARMRegisters::pc); + return reg | (shiftReg << 8) | 0x50; + } + + // General helpers + + size_t codeSize() const + { + return m_buffer.codeSize(); + } + + void ensureSpace(int insnSpace, int constSpace) + { + m_buffer.ensureSpace(insnSpace, constSpace); + } + + int sizeOfConstantPool() + { + return m_buffer.sizeOfConstantPool(); + } + + AssemblerLabel labelIgnoringWatchpoints() + { + m_buffer.ensureSpaceForAnyInstruction(); + return m_buffer.label(); + } + + AssemblerLabel labelForWatchpoint() + { + m_buffer.ensureSpaceForAnyInstruction(maxJumpReplacementSize() / sizeof(ARMWord)); + AssemblerLabel result = m_buffer.label(); + if (result.m_offset != (m_indexOfTailOfLastWatchpoint - maxJumpReplacementSize())) + result = label(); + m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); + return label(); + } + + AssemblerLabel label() + { + AssemblerLabel result = labelIgnoringWatchpoints(); + while (result.m_offset + 1 < m_indexOfTailOfLastWatchpoint) { + nop(); + // The available number of instructions are ensured by labelForWatchpoint. + result = m_buffer.label(); + } + return result; + } + + AssemblerLabel align(int alignment) + { + while (!m_buffer.isAligned(alignment)) + mov(ARMRegisters::r0, ARMRegisters::r0); + + return label(); + } + + AssemblerLabel loadBranchTarget(int rd, Condition cc = AL, int useConstantPool = 0) + { + ensureSpace(sizeof(ARMWord), sizeof(ARMWord)); + m_jumps.append(m_buffer.codeSize() | (useConstantPool & 0x1)); + ldrUniqueImmediate(rd, InvalidBranchTarget, cc); + return m_buffer.label(); + } + + AssemblerLabel jmp(Condition cc = AL, int useConstantPool = 0) + { + return loadBranchTarget(ARMRegisters::pc, cc, useConstantPool); + } + + PassRefPtr executableCopy(JSGlobalData&, void* ownerUID, JITCompilationEffort); + + unsigned debugOffset() { return m_buffer.debugOffset(); } + + // DFG assembly helpers for moving data between fp and registers. + void vmov(RegisterID rd1, RegisterID rd2, FPRegisterID rn) + { + vmov_arm64(rd1, rd2, rn); + } + + void vmov(FPRegisterID rd, RegisterID rn1, RegisterID rn2) + { + vmov_vfp64(rd, rn1, rn2); + } + + // Patching helpers + + static ARMWord* getLdrImmAddress(ARMWord* insn) + { + // Check for call + if ((*insn & LdrPcImmediateInstructionMask) != LdrPcImmediateInstruction) { + // Must be BLX + ASSERT((*insn & BlxInstructionMask) == BlxInstruction); + insn--; + } + + // Must be an ldr ..., [pc +/- imm] + ASSERT((*insn & LdrPcImmediateInstructionMask) == LdrPcImmediateInstruction); + + ARMWord addr = reinterpret_cast(insn) + DefaultPrefetchOffset * sizeof(ARMWord); + if (*insn & DataTransferUp) + return reinterpret_cast(addr + (*insn & DataTransferOffsetMask)); + return reinterpret_cast(addr - (*insn & DataTransferOffsetMask)); + } + + static ARMWord* getLdrImmAddressOnPool(ARMWord* insn, uint32_t* constPool) + { + // Must be an ldr ..., [pc +/- imm] + ASSERT((*insn & LdrPcImmediateInstructionMask) == LdrPcImmediateInstruction); + + if (*insn & 0x1) + return reinterpret_cast(constPool + ((*insn & DataTransferOffsetMask) >> 1)); + return getLdrImmAddress(insn); + } + + static void patchPointerInternal(intptr_t from, void* to) + { + ARMWord* insn = reinterpret_cast(from); + ARMWord* addr = getLdrImmAddress(insn); + *addr = reinterpret_cast(to); + } + + static ARMWord patchConstantPoolLoad(ARMWord load, ARMWord value) + { + value = (value << 1) + 1; + ASSERT(!(value & ~DataTransferOffsetMask)); + return (load & ~DataTransferOffsetMask) | value; + } + + static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr); + + // Read pointers + static void* readPointer(void* from) + { + ARMWord* instruction = reinterpret_cast(from); + ARMWord* address = getLdrImmAddress(instruction); + return *reinterpret_cast(address); + } + + // Patch pointers + + static void linkPointer(void* code, AssemblerLabel from, void* to) + { + patchPointerInternal(reinterpret_cast(code) + from.m_offset, to); + } + + static void repatchInt32(void* where, int32_t to) + { + patchPointerInternal(reinterpret_cast(where), reinterpret_cast(to)); + } + + static void repatchCompact(void* where, int32_t value) + { + ARMWord* instruction = reinterpret_cast(where); + ASSERT((*instruction & 0x0f700000) == LoadUint32); + if (value >= 0) + *instruction = (*instruction & 0xff7ff000) | DataTransferUp | value; + else + *instruction = (*instruction & 0xff7ff000) | -value; + cacheFlush(instruction, sizeof(ARMWord)); + } + + static void repatchPointer(void* from, void* to) + { + patchPointerInternal(reinterpret_cast(from), to); + } + + // Linkers + static intptr_t getAbsoluteJumpAddress(void* base, int offset = 0) + { + return reinterpret_cast(base) + offset - sizeof(ARMWord); + } + + void linkJump(AssemblerLabel from, AssemblerLabel to) + { + ARMWord* insn = reinterpret_cast(getAbsoluteJumpAddress(m_buffer.data(), from.m_offset)); + ARMWord* addr = getLdrImmAddressOnPool(insn, m_buffer.poolAddress()); + *addr = toARMWord(to.m_offset); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + patchPointerInternal(getAbsoluteJumpAddress(code, from.m_offset), to); + } + + static void relinkJump(void* from, void* to) + { + patchPointerInternal(getAbsoluteJumpAddress(from), to); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + patchPointerInternal(getAbsoluteJumpAddress(code, from.m_offset), to); + } + + static void relinkCall(void* from, void* to) + { + patchPointerInternal(getAbsoluteJumpAddress(from), to); + } + + static void* readCallTarget(void* from) + { + return reinterpret_cast(readPointer(reinterpret_cast(getAbsoluteJumpAddress(from)))); + } + + static void replaceWithJump(void* instructionStart, void* to) + { + ARMWord* instruction = reinterpret_cast(instructionStart) - 1; + intptr_t difference = reinterpret_cast(to) - (reinterpret_cast(instruction) + DefaultPrefetchOffset * sizeof(ARMWord)); + + if (!(difference & 1)) { + difference >>= 2; + if ((difference <= MaximumBranchOffsetDistance && difference >= MinimumBranchOffsetDistance)) { + // Direct branch. + instruction[0] = B | AL | (difference & BranchOffsetMask); + cacheFlush(instruction, sizeof(ARMWord)); + return; + } + } + + // Load target. + instruction[0] = LoadUint32 | AL | RN(ARMRegisters::pc) | RD(ARMRegisters::pc) | 4; + instruction[1] = reinterpret_cast(to); + cacheFlush(instruction, sizeof(ARMWord) * 2); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return sizeof(ARMWord) * 2; + } + + static void replaceWithLoad(void* instructionStart) + { + ARMWord* instruction = reinterpret_cast(instructionStart); + cacheFlush(instruction, sizeof(ARMWord)); + + ASSERT((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction || (*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction); + if ((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction) { + *instruction = (*instruction & ~LdrOrAddInstructionMask) | LdrImmediateInstruction; + cacheFlush(instruction, sizeof(ARMWord)); + } + } + + static void replaceWithAddressComputation(void* instructionStart) + { + ARMWord* instruction = reinterpret_cast(instructionStart); + cacheFlush(instruction, sizeof(ARMWord)); + + ASSERT((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction || (*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction); + if ((*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction) { + *instruction = (*instruction & ~LdrOrAddInstructionMask) | AddImmediateInstruction; + cacheFlush(instruction, sizeof(ARMWord)); + } + } + + // Address operations + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + // Address differences + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + return call.m_offset; + } + + // Handle immediates + + static ARMWord getOp2(ARMWord imm); + + // Fast case if imm is known to be between 0 and 0xff + static ARMWord getOp2Byte(ARMWord imm) + { + ASSERT(imm <= 0xff); + return Op2Immediate | imm; + } + + static ARMWord getOp2Half(ARMWord imm) + { + ASSERT(imm <= 0xff); + return ImmediateForHalfWordTransfer | (imm & 0x0f) | ((imm & 0xf0) << 4); + } + +#if WTF_ARM_ARCH_AT_LEAST(7) + static ARMWord getImm16Op2(ARMWord imm) + { + if (imm <= 0xffff) + return (imm & 0xf000) << 4 | (imm & 0xfff); + return InvalidImmediate; + } +#endif + ARMWord getImm(ARMWord imm, int tmpReg, bool invert = false); + void moveImm(ARMWord imm, int dest); + ARMWord encodeComplexImm(ARMWord imm, int dest); + + // Memory load/store helpers + + void dataTransfer32(DataTransferTypeA, RegisterID srcDst, RegisterID base, int32_t offset); + void baseIndexTransfer32(DataTransferTypeA, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); + void dataTransfer16(DataTransferTypeB, RegisterID srcDst, RegisterID base, int32_t offset); + void baseIndexTransfer16(DataTransferTypeB, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); + void dataTransferFloat(DataTransferTypeFloat, FPRegisterID srcDst, RegisterID base, int32_t offset); + void baseIndexTransferFloat(DataTransferTypeFloat, FPRegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); + + // Constant pool hnadlers + + static ARMWord placeConstantPoolBarrier(int offset) + { + offset = (offset - sizeof(ARMWord)) >> 2; + ASSERT((offset <= MaximumBranchOffsetDistance && offset >= MinimumBranchOffsetDistance)); + return AL | B | (offset & BranchOffsetMask); + } + +#if OS(LINUX) && COMPILER(RVCT) + static __asm void cacheFlush(void* code, size_t); +#else + static void cacheFlush(void* code, size_t size) + { +#if OS(LINUX) && COMPILER(GCC) + uintptr_t currentPage = reinterpret_cast(code) & ~(pageSize() - 1); + uintptr_t lastPage = (reinterpret_cast(code) + size) & ~(pageSize() - 1); + do { + asm volatile( + "push {r7}\n" + "mov r0, %0\n" + "mov r1, %1\n" + "mov r7, #0xf0000\n" + "add r7, r7, #0x2\n" + "mov r2, #0x0\n" + "svc 0x0\n" + "pop {r7}\n" + : + : "r" (currentPage), "r" (currentPage + pageSize()) + : "r0", "r1", "r2"); + currentPage += pageSize(); + } while (lastPage >= currentPage); +#elif OS(WINCE) + CacheRangeFlush(code, size, CACHE_SYNC_ALL); +#elif OS(QNX) && ENABLE(ASSEMBLER_WX_EXCLUSIVE) + UNUSED_PARAM(code); + UNUSED_PARAM(size); +#elif OS(QNX) + msync(code, size, MS_INVALIDATE_ICACHE); +#else +#error "The cacheFlush support is missing on this platform." +#endif + } +#endif + + private: + static ARMWord RM(int reg) + { + ASSERT(reg <= ARMRegisters::pc); + return reg; + } + + static ARMWord RS(int reg) + { + ASSERT(reg <= ARMRegisters::pc); + return reg << 8; + } + + static ARMWord RD(int reg) + { + ASSERT(reg <= ARMRegisters::pc); + return reg << 12; + } + + static ARMWord RN(int reg) + { + ASSERT(reg <= ARMRegisters::pc); + return reg << 16; + } + + static ARMWord getConditionalField(ARMWord i) + { + return i & ConditionalFieldMask; + } + + static ARMWord toARMWord(Condition cc) + { + return static_cast(cc); + } + + static ARMWord toARMWord(uint32_t u) + { + return static_cast(u); + } + + int genInt(int reg, ARMWord imm, bool positive); + + ARMBuffer m_buffer; + Jumps m_jumps; + uint32_t m_indexOfTailOfLastWatchpoint; + }; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#endif // ARMAssembler_h diff --git a/masm/assembler/ARMv7Assembler.cpp b/masm/assembler/ARMv7Assembler.cpp new file mode 100644 index 0000000000..faca66421b --- /dev/null +++ b/masm/assembler/ARMv7Assembler.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) + +#include "ARMv7Assembler.h" + +namespace JSC { + +} + +#endif diff --git a/masm/assembler/ARMv7Assembler.h b/masm/assembler/ARMv7Assembler.h new file mode 100644 index 0000000000..e9b9fcc50e --- /dev/null +++ b/masm/assembler/ARMv7Assembler.h @@ -0,0 +1,2738 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ARMAssembler_h +#define ARMAssembler_h + +#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) + +#include "AssemblerBuffer.h" +#include +#include +#include + +namespace JSC { + +namespace ARMRegisters { + typedef enum { + r0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, wr = r7, // thumb work register + r8, + r9, sb = r9, // static base + r10, sl = r10, // stack limit + r11, fp = r11, // frame pointer + r12, ip = r12, + r13, sp = r13, + r14, lr = r14, + r15, pc = r15, + } RegisterID; + + typedef enum { + s0, + s1, + s2, + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + s12, + s13, + s14, + s15, + s16, + s17, + s18, + s19, + s20, + s21, + s22, + s23, + s24, + s25, + s26, + s27, + s28, + s29, + s30, + s31, + } FPSingleRegisterID; + + typedef enum { + d0, + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + d14, + d15, + d16, + d17, + d18, + d19, + d20, + d21, + d22, + d23, + d24, + d25, + d26, + d27, + d28, + d29, + d30, + d31, + } FPDoubleRegisterID; + + typedef enum { + q0, + q1, + q2, + q3, + q4, + q5, + q6, + q7, + q8, + q9, + q10, + q11, + q12, + q13, + q14, + q15, + q16, + q17, + q18, + q19, + q20, + q21, + q22, + q23, + q24, + q25, + q26, + q27, + q28, + q29, + q30, + q31, + } FPQuadRegisterID; + + inline FPSingleRegisterID asSingle(FPDoubleRegisterID reg) + { + ASSERT(reg < d16); + return (FPSingleRegisterID)(reg << 1); + } + + inline FPDoubleRegisterID asDouble(FPSingleRegisterID reg) + { + ASSERT(!(reg & 1)); + return (FPDoubleRegisterID)(reg >> 1); + } +} + +class ARMv7Assembler; +class ARMThumbImmediate { + friend class ARMv7Assembler; + + typedef uint8_t ThumbImmediateType; + static const ThumbImmediateType TypeInvalid = 0; + static const ThumbImmediateType TypeEncoded = 1; + static const ThumbImmediateType TypeUInt16 = 2; + + typedef union { + int16_t asInt; + struct { + unsigned imm8 : 8; + unsigned imm3 : 3; + unsigned i : 1; + unsigned imm4 : 4; + }; + // If this is an encoded immediate, then it may describe a shift, or a pattern. + struct { + unsigned shiftValue7 : 7; + unsigned shiftAmount : 5; + }; + struct { + unsigned immediate : 8; + unsigned pattern : 4; + }; + } ThumbImmediateValue; + + // byte0 contains least significant bit; not using an array to make client code endian agnostic. + typedef union { + int32_t asInt; + struct { + uint8_t byte0; + uint8_t byte1; + uint8_t byte2; + uint8_t byte3; + }; + } PatternBytes; + + ALWAYS_INLINE static void countLeadingZerosPartial(uint32_t& value, int32_t& zeros, const int N) + { + if (value & ~((1 << N) - 1)) /* check for any of the top N bits (of 2N bits) are set */ + value >>= N; /* if any were set, lose the bottom N */ + else /* if none of the top N bits are set, */ + zeros += N; /* then we have identified N leading zeros */ + } + + static int32_t countLeadingZeros(uint32_t value) + { + if (!value) + return 32; + + int32_t zeros = 0; + countLeadingZerosPartial(value, zeros, 16); + countLeadingZerosPartial(value, zeros, 8); + countLeadingZerosPartial(value, zeros, 4); + countLeadingZerosPartial(value, zeros, 2); + countLeadingZerosPartial(value, zeros, 1); + return zeros; + } + + ARMThumbImmediate() + : m_type(TypeInvalid) + { + m_value.asInt = 0; + } + + ARMThumbImmediate(ThumbImmediateType type, ThumbImmediateValue value) + : m_type(type) + , m_value(value) + { + } + + ARMThumbImmediate(ThumbImmediateType type, uint16_t value) + : m_type(TypeUInt16) + { + // Make sure this constructor is only reached with type TypeUInt16; + // this extra parameter makes the code a little clearer by making it + // explicit at call sites which type is being constructed + ASSERT_UNUSED(type, type == TypeUInt16); + + m_value.asInt = value; + } + +public: + static ARMThumbImmediate makeEncodedImm(uint32_t value) + { + ThumbImmediateValue encoding; + encoding.asInt = 0; + + // okay, these are easy. + if (value < 256) { + encoding.immediate = value; + encoding.pattern = 0; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + int32_t leadingZeros = countLeadingZeros(value); + // if there were 24 or more leading zeros, then we'd have hit the (value < 256) case. + ASSERT(leadingZeros < 24); + + // Given a number with bit fields Z:B:C, where count(Z)+count(B)+count(C) == 32, + // Z are the bits known zero, B is the 8-bit immediate, C are the bits to check for + // zero. count(B) == 8, so the count of bits to be checked is 24 - count(Z). + int32_t rightShiftAmount = 24 - leadingZeros; + if (value == ((value >> rightShiftAmount) << rightShiftAmount)) { + // Shift the value down to the low byte position. The assign to + // shiftValue7 drops the implicit top bit. + encoding.shiftValue7 = value >> rightShiftAmount; + // The endoded shift amount is the magnitude of a right rotate. + encoding.shiftAmount = 8 + leadingZeros; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + PatternBytes bytes; + bytes.asInt = value; + + if ((bytes.byte0 == bytes.byte1) && (bytes.byte0 == bytes.byte2) && (bytes.byte0 == bytes.byte3)) { + encoding.immediate = bytes.byte0; + encoding.pattern = 3; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + if ((bytes.byte0 == bytes.byte2) && !(bytes.byte1 | bytes.byte3)) { + encoding.immediate = bytes.byte0; + encoding.pattern = 1; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + if ((bytes.byte1 == bytes.byte3) && !(bytes.byte0 | bytes.byte2)) { + encoding.immediate = bytes.byte1; + encoding.pattern = 2; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + return ARMThumbImmediate(); + } + + static ARMThumbImmediate makeUInt12(int32_t value) + { + return (!(value & 0xfffff000)) + ? ARMThumbImmediate(TypeUInt16, (uint16_t)value) + : ARMThumbImmediate(); + } + + static ARMThumbImmediate makeUInt12OrEncodedImm(int32_t value) + { + // If this is not a 12-bit unsigned it, try making an encoded immediate. + return (!(value & 0xfffff000)) + ? ARMThumbImmediate(TypeUInt16, (uint16_t)value) + : makeEncodedImm(value); + } + + // The 'make' methods, above, return a !isValid() value if the argument + // cannot be represented as the requested type. This methods is called + // 'get' since the argument can always be represented. + static ARMThumbImmediate makeUInt16(uint16_t value) + { + return ARMThumbImmediate(TypeUInt16, value); + } + + bool isValid() + { + return m_type != TypeInvalid; + } + + uint16_t asUInt16() const { return m_value.asInt; } + + // These methods rely on the format of encoded byte values. + bool isUInt3() { return !(m_value.asInt & 0xfff8); } + bool isUInt4() { return !(m_value.asInt & 0xfff0); } + bool isUInt5() { return !(m_value.asInt & 0xffe0); } + bool isUInt6() { return !(m_value.asInt & 0xffc0); } + bool isUInt7() { return !(m_value.asInt & 0xff80); } + bool isUInt8() { return !(m_value.asInt & 0xff00); } + bool isUInt9() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xfe00); } + bool isUInt10() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xfc00); } + bool isUInt12() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xf000); } + bool isUInt16() { return m_type == TypeUInt16; } + uint8_t getUInt3() { ASSERT(isUInt3()); return m_value.asInt; } + uint8_t getUInt4() { ASSERT(isUInt4()); return m_value.asInt; } + uint8_t getUInt5() { ASSERT(isUInt5()); return m_value.asInt; } + uint8_t getUInt6() { ASSERT(isUInt6()); return m_value.asInt; } + uint8_t getUInt7() { ASSERT(isUInt7()); return m_value.asInt; } + uint8_t getUInt8() { ASSERT(isUInt8()); return m_value.asInt; } + uint16_t getUInt9() { ASSERT(isUInt9()); return m_value.asInt; } + uint16_t getUInt10() { ASSERT(isUInt10()); return m_value.asInt; } + uint16_t getUInt12() { ASSERT(isUInt12()); return m_value.asInt; } + uint16_t getUInt16() { ASSERT(isUInt16()); return m_value.asInt; } + + bool isEncodedImm() { return m_type == TypeEncoded; } + +private: + ThumbImmediateType m_type; + ThumbImmediateValue m_value; +}; + +typedef enum { + SRType_LSL, + SRType_LSR, + SRType_ASR, + SRType_ROR, + + SRType_RRX = SRType_ROR +} ARMShiftType; + +class ShiftTypeAndAmount { + friend class ARMv7Assembler; + +public: + ShiftTypeAndAmount() + { + m_u.type = (ARMShiftType)0; + m_u.amount = 0; + } + + ShiftTypeAndAmount(ARMShiftType type, unsigned amount) + { + m_u.type = type; + m_u.amount = amount & 31; + } + + unsigned lo4() { return m_u.lo4; } + unsigned hi4() { return m_u.hi4; } + +private: + union { + struct { + unsigned lo4 : 4; + unsigned hi4 : 4; + }; + struct { + unsigned type : 2; + unsigned amount : 6; + }; + } m_u; +}; + +class ARMv7Assembler { +public: + typedef ARMRegisters::RegisterID RegisterID; + typedef ARMRegisters::FPSingleRegisterID FPSingleRegisterID; + typedef ARMRegisters::FPDoubleRegisterID FPDoubleRegisterID; + typedef ARMRegisters::FPQuadRegisterID FPQuadRegisterID; + + // (HS, LO, HI, LS) -> (AE, B, A, BE) + // (VS, VC) -> (O, NO) + typedef enum { + ConditionEQ, + ConditionNE, + ConditionHS, ConditionCS = ConditionHS, + ConditionLO, ConditionCC = ConditionLO, + ConditionMI, + ConditionPL, + ConditionVS, + ConditionVC, + ConditionHI, + ConditionLS, + ConditionGE, + ConditionLT, + ConditionGT, + ConditionLE, + ConditionAL, + ConditionInvalid + } Condition; + +#define JUMP_ENUM_WITH_SIZE(index, value) (((value) << 3) | (index)) +#define JUMP_ENUM_SIZE(jump) ((jump) >> 3) + enum JumpType { JumpFixed = JUMP_ENUM_WITH_SIZE(0, 0), + JumpNoCondition = JUMP_ENUM_WITH_SIZE(1, 5 * sizeof(uint16_t)), + JumpCondition = JUMP_ENUM_WITH_SIZE(2, 6 * sizeof(uint16_t)), + JumpNoConditionFixedSize = JUMP_ENUM_WITH_SIZE(3, 5 * sizeof(uint16_t)), + JumpConditionFixedSize = JUMP_ENUM_WITH_SIZE(4, 6 * sizeof(uint16_t)) + }; + enum JumpLinkType { + LinkInvalid = JUMP_ENUM_WITH_SIZE(0, 0), + LinkJumpT1 = JUMP_ENUM_WITH_SIZE(1, sizeof(uint16_t)), + LinkJumpT2 = JUMP_ENUM_WITH_SIZE(2, sizeof(uint16_t)), + LinkJumpT3 = JUMP_ENUM_WITH_SIZE(3, 2 * sizeof(uint16_t)), + LinkJumpT4 = JUMP_ENUM_WITH_SIZE(4, 2 * sizeof(uint16_t)), + LinkConditionalJumpT4 = JUMP_ENUM_WITH_SIZE(5, 3 * sizeof(uint16_t)), + LinkBX = JUMP_ENUM_WITH_SIZE(6, 5 * sizeof(uint16_t)), + LinkConditionalBX = JUMP_ENUM_WITH_SIZE(7, 6 * sizeof(uint16_t)) + }; + + class LinkRecord { + public: + LinkRecord(intptr_t from, intptr_t to, JumpType type, Condition condition) + { + data.realTypes.m_from = from; + data.realTypes.m_to = to; + data.realTypes.m_type = type; + data.realTypes.m_linkType = LinkInvalid; + data.realTypes.m_condition = condition; + } + void operator=(const LinkRecord& other) + { + data.copyTypes.content[0] = other.data.copyTypes.content[0]; + data.copyTypes.content[1] = other.data.copyTypes.content[1]; + data.copyTypes.content[2] = other.data.copyTypes.content[2]; + } + intptr_t from() const { return data.realTypes.m_from; } + void setFrom(intptr_t from) { data.realTypes.m_from = from; } + intptr_t to() const { return data.realTypes.m_to; } + JumpType type() const { return data.realTypes.m_type; } + JumpLinkType linkType() const { return data.realTypes.m_linkType; } + void setLinkType(JumpLinkType linkType) { ASSERT(data.realTypes.m_linkType == LinkInvalid); data.realTypes.m_linkType = linkType; } + Condition condition() const { return data.realTypes.m_condition; } + private: + union { + struct RealTypes { + intptr_t m_from : 31; + intptr_t m_to : 31; + JumpType m_type : 8; + JumpLinkType m_linkType : 8; + Condition m_condition : 16; + } realTypes; + struct CopyTypes { + uint32_t content[3]; + } copyTypes; + COMPILE_ASSERT(sizeof(RealTypes) == sizeof(CopyTypes), LinkRecordCopyStructSizeEqualsRealStruct); + } data; + }; + + ARMv7Assembler() + : m_indexOfLastWatchpoint(INT_MIN) + , m_indexOfTailOfLastWatchpoint(INT_MIN) + { + } + +private: + + // ARMv7, Appx-A.6.3 + bool BadReg(RegisterID reg) + { + return (reg == ARMRegisters::sp) || (reg == ARMRegisters::pc); + } + + uint32_t singleRegisterMask(FPSingleRegisterID rdNum, int highBitsShift, int lowBitShift) + { + uint32_t rdMask = (rdNum >> 1) << highBitsShift; + if (rdNum & 1) + rdMask |= 1 << lowBitShift; + return rdMask; + } + + uint32_t doubleRegisterMask(FPDoubleRegisterID rdNum, int highBitShift, int lowBitsShift) + { + uint32_t rdMask = (rdNum & 0xf) << lowBitsShift; + if (rdNum & 16) + rdMask |= 1 << highBitShift; + return rdMask; + } + + typedef enum { + OP_ADD_reg_T1 = 0x1800, + OP_SUB_reg_T1 = 0x1A00, + OP_ADD_imm_T1 = 0x1C00, + OP_SUB_imm_T1 = 0x1E00, + OP_MOV_imm_T1 = 0x2000, + OP_CMP_imm_T1 = 0x2800, + OP_ADD_imm_T2 = 0x3000, + OP_SUB_imm_T2 = 0x3800, + OP_AND_reg_T1 = 0x4000, + OP_EOR_reg_T1 = 0x4040, + OP_TST_reg_T1 = 0x4200, + OP_RSB_imm_T1 = 0x4240, + OP_CMP_reg_T1 = 0x4280, + OP_ORR_reg_T1 = 0x4300, + OP_MVN_reg_T1 = 0x43C0, + OP_ADD_reg_T2 = 0x4400, + OP_MOV_reg_T1 = 0x4600, + OP_BLX = 0x4700, + OP_BX = 0x4700, + OP_STR_reg_T1 = 0x5000, + OP_STRH_reg_T1 = 0x5200, + OP_STRB_reg_T1 = 0x5400, + OP_LDRSB_reg_T1 = 0x5600, + OP_LDR_reg_T1 = 0x5800, + OP_LDRH_reg_T1 = 0x5A00, + OP_LDRB_reg_T1 = 0x5C00, + OP_LDRSH_reg_T1 = 0x5E00, + OP_STR_imm_T1 = 0x6000, + OP_LDR_imm_T1 = 0x6800, + OP_STRB_imm_T1 = 0x7000, + OP_LDRB_imm_T1 = 0x7800, + OP_STRH_imm_T1 = 0x8000, + OP_LDRH_imm_T1 = 0x8800, + OP_STR_imm_T2 = 0x9000, + OP_LDR_imm_T2 = 0x9800, + OP_ADD_SP_imm_T1 = 0xA800, + OP_ADD_SP_imm_T2 = 0xB000, + OP_SUB_SP_imm_T1 = 0xB080, + OP_BKPT = 0xBE00, + OP_IT = 0xBF00, + OP_NOP_T1 = 0xBF00, + } OpcodeID; + + typedef enum { + OP_B_T1 = 0xD000, + OP_B_T2 = 0xE000, + OP_AND_reg_T2 = 0xEA00, + OP_TST_reg_T2 = 0xEA10, + OP_ORR_reg_T2 = 0xEA40, + OP_ORR_S_reg_T2 = 0xEA50, + OP_ASR_imm_T1 = 0xEA4F, + OP_LSL_imm_T1 = 0xEA4F, + OP_LSR_imm_T1 = 0xEA4F, + OP_ROR_imm_T1 = 0xEA4F, + OP_MVN_reg_T2 = 0xEA6F, + OP_EOR_reg_T2 = 0xEA80, + OP_ADD_reg_T3 = 0xEB00, + OP_ADD_S_reg_T3 = 0xEB10, + OP_SUB_reg_T2 = 0xEBA0, + OP_SUB_S_reg_T2 = 0xEBB0, + OP_CMP_reg_T2 = 0xEBB0, + OP_VMOV_CtoD = 0xEC00, + OP_VMOV_DtoC = 0xEC10, + OP_FSTS = 0xED00, + OP_VSTR = 0xED00, + OP_FLDS = 0xED10, + OP_VLDR = 0xED10, + OP_VMOV_CtoS = 0xEE00, + OP_VMOV_StoC = 0xEE10, + OP_VMUL_T2 = 0xEE20, + OP_VADD_T2 = 0xEE30, + OP_VSUB_T2 = 0xEE30, + OP_VDIV = 0xEE80, + OP_VABS_T2 = 0xEEB0, + OP_VCMP = 0xEEB0, + OP_VCVT_FPIVFP = 0xEEB0, + OP_VMOV_T2 = 0xEEB0, + OP_VMOV_IMM_T2 = 0xEEB0, + OP_VMRS = 0xEEB0, + OP_VNEG_T2 = 0xEEB0, + OP_VSQRT_T1 = 0xEEB0, + OP_VCVTSD_T1 = 0xEEB0, + OP_VCVTDS_T1 = 0xEEB0, + OP_B_T3a = 0xF000, + OP_B_T4a = 0xF000, + OP_AND_imm_T1 = 0xF000, + OP_TST_imm = 0xF010, + OP_ORR_imm_T1 = 0xF040, + OP_MOV_imm_T2 = 0xF040, + OP_MVN_imm = 0xF060, + OP_EOR_imm_T1 = 0xF080, + OP_ADD_imm_T3 = 0xF100, + OP_ADD_S_imm_T3 = 0xF110, + OP_CMN_imm = 0xF110, + OP_ADC_imm = 0xF140, + OP_SUB_imm_T3 = 0xF1A0, + OP_SUB_S_imm_T3 = 0xF1B0, + OP_CMP_imm_T2 = 0xF1B0, + OP_RSB_imm_T2 = 0xF1C0, + OP_RSB_S_imm_T2 = 0xF1D0, + OP_ADD_imm_T4 = 0xF200, + OP_MOV_imm_T3 = 0xF240, + OP_SUB_imm_T4 = 0xF2A0, + OP_MOVT = 0xF2C0, + OP_UBFX_T1 = 0xF3C0, + OP_NOP_T2a = 0xF3AF, + OP_STRB_imm_T3 = 0xF800, + OP_STRB_reg_T2 = 0xF800, + OP_LDRB_imm_T3 = 0xF810, + OP_LDRB_reg_T2 = 0xF810, + OP_STRH_imm_T3 = 0xF820, + OP_STRH_reg_T2 = 0xF820, + OP_LDRH_reg_T2 = 0xF830, + OP_LDRH_imm_T3 = 0xF830, + OP_STR_imm_T4 = 0xF840, + OP_STR_reg_T2 = 0xF840, + OP_LDR_imm_T4 = 0xF850, + OP_LDR_reg_T2 = 0xF850, + OP_STRB_imm_T2 = 0xF880, + OP_LDRB_imm_T2 = 0xF890, + OP_STRH_imm_T2 = 0xF8A0, + OP_LDRH_imm_T2 = 0xF8B0, + OP_STR_imm_T3 = 0xF8C0, + OP_LDR_imm_T3 = 0xF8D0, + OP_LDRSB_reg_T2 = 0xF910, + OP_LDRSH_reg_T2 = 0xF930, + OP_LSL_reg_T2 = 0xFA00, + OP_LSR_reg_T2 = 0xFA20, + OP_ASR_reg_T2 = 0xFA40, + OP_ROR_reg_T2 = 0xFA60, + OP_CLZ = 0xFAB0, + OP_SMULL_T1 = 0xFB80, + } OpcodeID1; + + typedef enum { + OP_VADD_T2b = 0x0A00, + OP_VDIVb = 0x0A00, + OP_FLDSb = 0x0A00, + OP_VLDRb = 0x0A00, + OP_VMOV_IMM_T2b = 0x0A00, + OP_VMOV_T2b = 0x0A40, + OP_VMUL_T2b = 0x0A00, + OP_FSTSb = 0x0A00, + OP_VSTRb = 0x0A00, + OP_VMOV_StoCb = 0x0A10, + OP_VMOV_CtoSb = 0x0A10, + OP_VMOV_DtoCb = 0x0A10, + OP_VMOV_CtoDb = 0x0A10, + OP_VMRSb = 0x0A10, + OP_VABS_T2b = 0x0A40, + OP_VCMPb = 0x0A40, + OP_VCVT_FPIVFPb = 0x0A40, + OP_VNEG_T2b = 0x0A40, + OP_VSUB_T2b = 0x0A40, + OP_VSQRT_T1b = 0x0A40, + OP_VCVTSD_T1b = 0x0A40, + OP_VCVTDS_T1b = 0x0A40, + OP_NOP_T2b = 0x8000, + OP_B_T3b = 0x8000, + OP_B_T4b = 0x9000, + } OpcodeID2; + + struct FourFours { + FourFours(unsigned f3, unsigned f2, unsigned f1, unsigned f0) + { + m_u.f0 = f0; + m_u.f1 = f1; + m_u.f2 = f2; + m_u.f3 = f3; + } + + union { + unsigned value; + struct { + unsigned f0 : 4; + unsigned f1 : 4; + unsigned f2 : 4; + unsigned f3 : 4; + }; + } m_u; + }; + + class ARMInstructionFormatter; + + // false means else! + bool ifThenElseConditionBit(Condition condition, bool isIf) + { + return isIf ? (condition & 1) : !(condition & 1); + } + uint8_t ifThenElse(Condition condition, bool inst2if, bool inst3if, bool inst4if) + { + int mask = (ifThenElseConditionBit(condition, inst2if) << 3) + | (ifThenElseConditionBit(condition, inst3if) << 2) + | (ifThenElseConditionBit(condition, inst4if) << 1) + | 1; + ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); + return (condition << 4) | mask; + } + uint8_t ifThenElse(Condition condition, bool inst2if, bool inst3if) + { + int mask = (ifThenElseConditionBit(condition, inst2if) << 3) + | (ifThenElseConditionBit(condition, inst3if) << 2) + | 2; + ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); + return (condition << 4) | mask; + } + uint8_t ifThenElse(Condition condition, bool inst2if) + { + int mask = (ifThenElseConditionBit(condition, inst2if) << 3) + | 4; + ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); + return (condition << 4) | mask; + } + + uint8_t ifThenElse(Condition condition) + { + int mask = 8; + return (condition << 4) | mask; + } + +public: + + void adc(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isEncodedImm()); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADC_imm, rn, rd, imm); + } + + void add(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + + if (rn == ARMRegisters::sp) { + ASSERT(!(imm.getUInt16() & 3)); + if (!(rd & 8) && imm.isUInt10()) { + m_formatter.oneWordOp5Reg3Imm8(OP_ADD_SP_imm_T1, rd, static_cast(imm.getUInt10() >> 2)); + return; + } else if ((rd == ARMRegisters::sp) && imm.isUInt9()) { + m_formatter.oneWordOp9Imm7(OP_ADD_SP_imm_T2, static_cast(imm.getUInt9() >> 2)); + return; + } + } else if (!((rd | rn) & 8)) { + if (imm.isUInt3()) { + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); + return; + } else if ((rd == rn) && imm.isUInt8()) { + m_formatter.oneWordOp5Reg3Imm8(OP_ADD_imm_T2, rd, imm.getUInt8()); + return; + } + } + + if (imm.isEncodedImm()) + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_imm_T3, rn, rd, imm); + else { + ASSERT(imm.isUInt12()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_imm_T4, rn, rd, imm); + } + } + + ALWAYS_INLINE void add(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ADD_reg_T3, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // NOTE: In an IT block, add doesn't modify the flags register. + ALWAYS_INLINE void add(RegisterID rd, RegisterID rn, RegisterID rm) + { + if (rd == rn) + m_formatter.oneWordOp8RegReg143(OP_ADD_reg_T2, rm, rd); + else if (rd == rm) + m_formatter.oneWordOp8RegReg143(OP_ADD_reg_T2, rn, rd); + else if (!((rd | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_reg_T1, rm, rn, rd); + else + add(rd, rn, rm, ShiftTypeAndAmount()); + } + + // Not allowed in an IT (if then) block. + ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isEncodedImm()); + + if (!((rd | rn) & 8)) { + if (imm.isUInt3()) { + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); + return; + } else if ((rd == rn) && imm.isUInt8()) { + m_formatter.oneWordOp5Reg3Imm8(OP_ADD_imm_T2, rd, imm.getUInt8()); + return; + } + } + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_S_imm_T3, rn, rd, imm); + } + + // Not allowed in an IT (if then) block? + ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ADD_S_reg_T3, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // Not allowed in an IT (if then) block. + ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, RegisterID rm) + { + if (!((rd | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_reg_T1, rm, rn, rd); + else + add_S(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(imm.isEncodedImm()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_AND_imm_T1, rn, rd, imm); + } + + ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_AND_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, RegisterID rm) + { + if ((rd == rn) && !((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_AND_reg_T1, rm, rd); + else if ((rd == rm) && !((rd | rn) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_AND_reg_T1, rn, rd); + else + ARM_and(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void asr(RegisterID rd, RegisterID rm, int32_t shiftAmount) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + ShiftTypeAndAmount shift(SRType_ASR, shiftAmount); + m_formatter.twoWordOp16FourFours(OP_ASR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void asr(RegisterID rd, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ASR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); + } + + // Only allowed in IT (if then) block if last instruction. + ALWAYS_INLINE AssemblerLabel b() + { + m_formatter.twoWordOp16Op16(OP_B_T4a, OP_B_T4b); + return m_formatter.label(); + } + + // Only allowed in IT (if then) block if last instruction. + ALWAYS_INLINE AssemblerLabel blx(RegisterID rm) + { + ASSERT(rm != ARMRegisters::pc); + m_formatter.oneWordOp8RegReg143(OP_BLX, rm, (RegisterID)8); + return m_formatter.label(); + } + + // Only allowed in IT (if then) block if last instruction. + ALWAYS_INLINE AssemblerLabel bx(RegisterID rm) + { + m_formatter.oneWordOp8RegReg143(OP_BX, rm, (RegisterID)0); + return m_formatter.label(); + } + + void bkpt(uint8_t imm = 0) + { + m_formatter.oneWordOp8Imm8(OP_BKPT, imm); + } + + ALWAYS_INLINE void clz(RegisterID rd, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_CLZ, rm, FourFours(0xf, rd, 8, rm)); + } + + ALWAYS_INLINE void cmn(RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isEncodedImm()); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_CMN_imm, rn, (RegisterID)0xf, imm); + } + + ALWAYS_INLINE void cmp(RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isEncodedImm()); + + if (!(rn & 8) && imm.isUInt8()) + m_formatter.oneWordOp5Reg3Imm8(OP_CMP_imm_T1, rn, imm.getUInt8()); + else + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_CMP_imm_T2, rn, (RegisterID)0xf, imm); + } + + ALWAYS_INLINE void cmp(RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_CMP_reg_T2, rn, FourFours(shift.hi4(), 0xf, shift.lo4(), rm)); + } + + ALWAYS_INLINE void cmp(RegisterID rn, RegisterID rm) + { + if ((rn | rm) & 8) + cmp(rn, rm, ShiftTypeAndAmount()); + else + m_formatter.oneWordOp10Reg3Reg3(OP_CMP_reg_T1, rm, rn); + } + + // xor is not spelled with an 'e'. :-( + ALWAYS_INLINE void eor(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(imm.isEncodedImm()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_EOR_imm_T1, rn, rd, imm); + } + + // xor is not spelled with an 'e'. :-( + ALWAYS_INLINE void eor(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_EOR_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // xor is not spelled with an 'e'. :-( + void eor(RegisterID rd, RegisterID rn, RegisterID rm) + { + if ((rd == rn) && !((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_EOR_reg_T1, rm, rd); + else if ((rd == rm) && !((rd | rn) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_EOR_reg_T1, rn, rd); + else + eor(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void it(Condition cond) + { + m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond)); + } + + ALWAYS_INLINE void it(Condition cond, bool inst2if) + { + m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if)); + } + + ALWAYS_INLINE void it(Condition cond, bool inst2if, bool inst3if) + { + m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if, inst3if)); + } + + ALWAYS_INLINE void it(Condition cond, bool inst2if, bool inst3if, bool inst4if) + { + m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if, inst3if, inst4if)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt7()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDR_imm_T1, imm.getUInt7() >> 2, rn, rt); + else if ((rn == ARMRegisters::sp) && !(rt & 8) && imm.isUInt10()) + m_formatter.oneWordOp5Reg3Imm8(OP_LDR_imm_T2, rt, static_cast(imm.getUInt10() >> 2)); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T3, rn, rt, imm.getUInt12()); + } + + ALWAYS_INLINE void ldrWide8BitImmediate(RegisterID rt, RegisterID rn, uint8_t immediate) + { + ASSERT(rn != ARMRegisters::pc); + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T3, rn, rt, immediate); + } + + ALWAYS_INLINE void ldrCompact(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(imm.isUInt7()); + ASSERT(!((rt | rn) & 8)); + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDR_imm_T1, imm.getUInt7() >> 2, rn, rt); + } + + // If index is set, this is a regular offset or a pre-indexed load; + // if index is not set then is is a post-index load. + // + // If wback is set rn is updated - this is a pre or post index load, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT((offset & ~0xff) == 0); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T4, rn, rt, offset); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDR_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDR_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt6()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDRH_imm_T1, imm.getUInt6() >> 2, rn, rt); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRH_imm_T2, rn, rt, imm.getUInt12()); + } + + // If index is set, this is a regular offset or a pre-indexed load; + // if index is not set then is is a post-index load. + // + // If wback is set rn is updated - this is a pre or post index load, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT((offset & ~0xff) == 0); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRH_imm_T3, rn, rt, offset); + } + + ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(!BadReg(rt)); // Memory hint + ASSERT(rn != ARMRegisters::pc); // LDRH (literal) + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRH_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDRH_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + void ldrb(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt5()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDRB_imm_T1, imm.getUInt5(), rn, rt); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRB_imm_T2, rn, rt, imm.getUInt12()); + } + + void ldrb(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + + ASSERT(!(offset & ~0xff)); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRB_imm_T3, rn, rt, offset); + } + + ALWAYS_INLINE void ldrb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRB_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDRB_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + void ldrsb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRSB_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDRSB_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + void ldrsh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRSH_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDRSH_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + void lsl(RegisterID rd, RegisterID rm, int32_t shiftAmount) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + ShiftTypeAndAmount shift(SRType_LSL, shiftAmount); + m_formatter.twoWordOp16FourFours(OP_LSL_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void lsl(RegisterID rd, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_LSL_reg_T2, rn, FourFours(0xf, rd, 0, rm)); + } + + ALWAYS_INLINE void lsr(RegisterID rd, RegisterID rm, int32_t shiftAmount) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + ShiftTypeAndAmount shift(SRType_LSR, shiftAmount); + m_formatter.twoWordOp16FourFours(OP_LSR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void lsr(RegisterID rd, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_LSR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); + } + + ALWAYS_INLINE void movT3(RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isValid()); + ASSERT(!imm.isEncodedImm()); + ASSERT(!BadReg(rd)); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOV_imm_T3, imm.m_value.imm4, rd, imm); + } + + ALWAYS_INLINE void mov(RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isValid()); + ASSERT(!BadReg(rd)); + + if ((rd < 8) && imm.isUInt8()) + m_formatter.oneWordOp5Reg3Imm8(OP_MOV_imm_T1, rd, imm.getUInt8()); + else if (imm.isEncodedImm()) + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOV_imm_T2, 0xf, rd, imm); + else + movT3(rd, imm); + } + + ALWAYS_INLINE void mov(RegisterID rd, RegisterID rm) + { + m_formatter.oneWordOp8RegReg143(OP_MOV_reg_T1, rm, rd); + } + + ALWAYS_INLINE void movt(RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isUInt16()); + ASSERT(!BadReg(rd)); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOVT, imm.m_value.imm4, rd, imm); + } + + ALWAYS_INLINE void mvn(RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isEncodedImm()); + ASSERT(!BadReg(rd)); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MVN_imm, 0xf, rd, imm); + } + + ALWAYS_INLINE void mvn(RegisterID rd, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp16FourFours(OP_MVN_reg_T2, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void mvn(RegisterID rd, RegisterID rm) + { + if (!((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_MVN_reg_T1, rm, rd); + else + mvn(rd, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void neg(RegisterID rd, RegisterID rm) + { + ARMThumbImmediate zero = ARMThumbImmediate::makeUInt12(0); + sub(rd, zero, rm); + } + + ALWAYS_INLINE void orr(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(imm.isEncodedImm()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ORR_imm_T1, rn, rd, imm); + } + + ALWAYS_INLINE void orr(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ORR_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + void orr(RegisterID rd, RegisterID rn, RegisterID rm) + { + if ((rd == rn) && !((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rm, rd); + else if ((rd == rm) && !((rd | rn) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rn, rd); + else + orr(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void orr_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ORR_S_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + void orr_S(RegisterID rd, RegisterID rn, RegisterID rm) + { + if ((rd == rn) && !((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rm, rd); + else if ((rd == rm) && !((rd | rn) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rn, rd); + else + orr_S(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void ror(RegisterID rd, RegisterID rm, int32_t shiftAmount) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + ShiftTypeAndAmount shift(SRType_ROR, shiftAmount); + m_formatter.twoWordOp16FourFours(OP_ROR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void ror(RegisterID rd, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ROR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); + } + + ALWAYS_INLINE void smull(RegisterID rdLo, RegisterID rdHi, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rdLo)); + ASSERT(!BadReg(rdHi)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + ASSERT(rdLo != rdHi); + m_formatter.twoWordOp12Reg4FourFours(OP_SMULL_T1, rn, FourFours(rdLo, rdHi, 0, rm)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt7()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STR_imm_T1, imm.getUInt7() >> 2, rn, rt); + else if ((rn == ARMRegisters::sp) && !(rt & 8) && imm.isUInt10()) + m_formatter.oneWordOp5Reg3Imm8(OP_STR_imm_T2, rt, static_cast(imm.getUInt10() >> 2)); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STR_imm_T3, rn, rt, imm.getUInt12()); + } + + // If index is set, this is a regular offset or a pre-indexed store; + // if index is not set then is is a post-index store. + // + // If wback is set rn is updated - this is a pre or post index store, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT((offset & ~0xff) == 0); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STR_imm_T4, rn, rt, offset); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STR_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_STR_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt7()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STRB_imm_T1, imm.getUInt7() >> 2, rn, rt); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRB_imm_T2, rn, rt, imm.getUInt12()); + } + + // If index is set, this is a regular offset or a pre-indexed store; + // if index is not set then is is a post-index store. + // + // If wback is set rn is updated - this is a pre or post index store, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT((offset & ~0xff) == 0); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRB_imm_T3, rn, rt, offset); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STRB_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_STRB_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt7()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STRH_imm_T1, imm.getUInt7() >> 2, rn, rt); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRH_imm_T2, rn, rt, imm.getUInt12()); + } + + // If index is set, this is a regular offset or a pre-indexed store; + // if index is not set then is is a post-index store. + // + // If wback is set rn is updated - this is a pre or post index store, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT(!(offset & ~0xff)); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRH_imm_T3, rn, rt, offset); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STRH_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_STRH_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + + if ((rn == ARMRegisters::sp) && (rd == ARMRegisters::sp) && imm.isUInt9()) { + ASSERT(!(imm.getUInt16() & 3)); + m_formatter.oneWordOp9Imm7(OP_SUB_SP_imm_T1, static_cast(imm.getUInt9() >> 2)); + return; + } else if (!((rd | rn) & 8)) { + if (imm.isUInt3()) { + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); + return; + } else if ((rd == rn) && imm.isUInt8()) { + m_formatter.oneWordOp5Reg3Imm8(OP_SUB_imm_T2, rd, imm.getUInt8()); + return; + } + } + + if (imm.isEncodedImm()) + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_imm_T3, rn, rd, imm); + else { + ASSERT(imm.isUInt12()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_imm_T4, rn, rd, imm); + } + } + + ALWAYS_INLINE void sub(RegisterID rd, ARMThumbImmediate imm, RegisterID rn) + { + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + ASSERT(imm.isUInt12()); + + if (!((rd | rn) & 8) && !imm.getUInt12()) + m_formatter.oneWordOp10Reg3Reg3(OP_RSB_imm_T1, rn, rd); + else + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_RSB_imm_T2, rn, rd, imm); + } + + ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_SUB_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // NOTE: In an IT block, add doesn't modify the flags register. + ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, RegisterID rm) + { + if (!((rd | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_reg_T1, rm, rn, rd); + else + sub(rd, rn, rm, ShiftTypeAndAmount()); + } + + // Not allowed in an IT (if then) block. + void sub_S(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + + if ((rn == ARMRegisters::sp) && (rd == ARMRegisters::sp) && imm.isUInt9()) { + ASSERT(!(imm.getUInt16() & 3)); + m_formatter.oneWordOp9Imm7(OP_SUB_SP_imm_T1, static_cast(imm.getUInt9() >> 2)); + return; + } else if (!((rd | rn) & 8)) { + if (imm.isUInt3()) { + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); + return; + } else if ((rd == rn) && imm.isUInt8()) { + m_formatter.oneWordOp5Reg3Imm8(OP_SUB_imm_T2, rd, imm.getUInt8()); + return; + } + } + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_S_imm_T3, rn, rd, imm); + } + + ALWAYS_INLINE void sub_S(RegisterID rd, ARMThumbImmediate imm, RegisterID rn) + { + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + ASSERT(imm.isUInt12()); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_RSB_S_imm_T2, rn, rd, imm); + } + + // Not allowed in an IT (if then) block? + ALWAYS_INLINE void sub_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_SUB_S_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // Not allowed in an IT (if then) block. + ALWAYS_INLINE void sub_S(RegisterID rd, RegisterID rn, RegisterID rm) + { + if (!((rd | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_reg_T1, rm, rn, rd); + else + sub_S(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void tst(RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(!BadReg(rn)); + ASSERT(imm.isEncodedImm()); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_TST_imm, rn, (RegisterID)0xf, imm); + } + + ALWAYS_INLINE void tst(RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_TST_reg_T2, rn, FourFours(shift.hi4(), 0xf, shift.lo4(), rm)); + } + + ALWAYS_INLINE void tst(RegisterID rn, RegisterID rm) + { + if ((rn | rm) & 8) + tst(rn, rm, ShiftTypeAndAmount()); + else + m_formatter.oneWordOp10Reg3Reg3(OP_TST_reg_T1, rm, rn); + } + + ALWAYS_INLINE void ubfx(RegisterID rd, RegisterID rn, unsigned lsb, unsigned width) + { + ASSERT(lsb < 32); + ASSERT((width >= 1) && (width <= 32)); + ASSERT((lsb + width) <= 32); + m_formatter.twoWordOp12Reg40Imm3Reg4Imm20Imm5(OP_UBFX_T1, rd, rn, (lsb & 0x1c) << 10, (lsb & 0x3) << 6, (width - 1) & 0x1f); + } + + void vadd(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VADD_T2, OP_VADD_T2b, true, rn, rd, rm); + } + + void vcmp(FPDoubleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VCMP, OP_VCMPb, true, VFPOperand(4), rd, rm); + } + + void vcmpz(FPDoubleRegisterID rd) + { + m_formatter.vfpOp(OP_VCMP, OP_VCMPb, true, VFPOperand(5), rd, VFPOperand(0)); + } + + void vcvt_signedToFloatingPoint(FPDoubleRegisterID rd, FPSingleRegisterID rm) + { + // boolean values are 64bit (toInt, unsigned, roundZero) + m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(false, false, false), rd, rm); + } + + void vcvt_floatingPointToSigned(FPSingleRegisterID rd, FPDoubleRegisterID rm) + { + // boolean values are 64bit (toInt, unsigned, roundZero) + m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(true, false, true), rd, rm); + } + + void vcvt_floatingPointToUnsigned(FPSingleRegisterID rd, FPDoubleRegisterID rm) + { + // boolean values are 64bit (toInt, unsigned, roundZero) + m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(true, true, true), rd, rm); + } + + void vdiv(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VDIV, OP_VDIVb, true, rn, rd, rm); + } + + void vldr(FPDoubleRegisterID rd, RegisterID rn, int32_t imm) + { + m_formatter.vfpMemOp(OP_VLDR, OP_VLDRb, true, rn, rd, imm); + } + + void flds(FPSingleRegisterID rd, RegisterID rn, int32_t imm) + { + m_formatter.vfpMemOp(OP_FLDS, OP_FLDSb, false, rn, rd, imm); + } + + void vmov(RegisterID rd, FPSingleRegisterID rn) + { + ASSERT(!BadReg(rd)); + m_formatter.vfpOp(OP_VMOV_StoC, OP_VMOV_StoCb, false, rn, rd, VFPOperand(0)); + } + + void vmov(FPSingleRegisterID rd, RegisterID rn) + { + ASSERT(!BadReg(rn)); + m_formatter.vfpOp(OP_VMOV_CtoS, OP_VMOV_CtoSb, false, rd, rn, VFPOperand(0)); + } + + void vmov(RegisterID rd1, RegisterID rd2, FPDoubleRegisterID rn) + { + ASSERT(!BadReg(rd1)); + ASSERT(!BadReg(rd2)); + m_formatter.vfpOp(OP_VMOV_DtoC, OP_VMOV_DtoCb, true, rd2, VFPOperand(rd1 | 16), rn); + } + + void vmov(FPDoubleRegisterID rd, RegisterID rn1, RegisterID rn2) + { + ASSERT(!BadReg(rn1)); + ASSERT(!BadReg(rn2)); + m_formatter.vfpOp(OP_VMOV_CtoD, OP_VMOV_CtoDb, true, rn2, VFPOperand(rn1 | 16), rd); + } + + void vmov(FPDoubleRegisterID rd, FPDoubleRegisterID rn) + { + m_formatter.vfpOp(OP_VMOV_T2, OP_VMOV_T2b, true, VFPOperand(0), rd, rn); + } + + void vmrs(RegisterID reg = ARMRegisters::pc) + { + ASSERT(reg != ARMRegisters::sp); + m_formatter.vfpOp(OP_VMRS, OP_VMRSb, false, VFPOperand(1), VFPOperand(0x10 | reg), VFPOperand(0)); + } + + void vmul(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VMUL_T2, OP_VMUL_T2b, true, rn, rd, rm); + } + + void vstr(FPDoubleRegisterID rd, RegisterID rn, int32_t imm) + { + m_formatter.vfpMemOp(OP_VSTR, OP_VSTRb, true, rn, rd, imm); + } + + void fsts(FPSingleRegisterID rd, RegisterID rn, int32_t imm) + { + m_formatter.vfpMemOp(OP_FSTS, OP_FSTSb, false, rn, rd, imm); + } + + void vsub(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VSUB_T2, OP_VSUB_T2b, true, rn, rd, rm); + } + + void vabs(FPDoubleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VABS_T2, OP_VABS_T2b, true, VFPOperand(16), rd, rm); + } + + void vneg(FPDoubleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VNEG_T2, OP_VNEG_T2b, true, VFPOperand(1), rd, rm); + } + + void vsqrt(FPDoubleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VSQRT_T1, OP_VSQRT_T1b, true, VFPOperand(17), rd, rm); + } + + void vcvtds(FPDoubleRegisterID rd, FPSingleRegisterID rm) + { + m_formatter.vfpOp(OP_VCVTDS_T1, OP_VCVTDS_T1b, false, VFPOperand(23), rd, rm); + } + + void vcvtsd(FPSingleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VCVTSD_T1, OP_VCVTSD_T1b, true, VFPOperand(23), rd, rm); + } + + void nop() + { + m_formatter.oneWordOp8Imm8(OP_NOP_T1, 0); + } + + AssemblerLabel labelIgnoringWatchpoints() + { + return m_formatter.label(); + } + + AssemblerLabel labelForWatchpoint() + { + AssemblerLabel result = m_formatter.label(); + if (static_cast(result.m_offset) != m_indexOfLastWatchpoint) + result = label(); + m_indexOfLastWatchpoint = result.m_offset; + m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); + return result; + } + + AssemblerLabel label() + { + AssemblerLabel result = m_formatter.label(); + while (UNLIKELY(static_cast(result.m_offset) < m_indexOfTailOfLastWatchpoint)) { + nop(); + result = m_formatter.label(); + } + return result; + } + + AssemblerLabel align(int alignment) + { + while (!m_formatter.isAligned(alignment)) + bkpt(); + + return label(); + } + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + ASSERT(label.isSet()); + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + int executableOffsetFor(int location) + { + if (!location) + return 0; + return static_cast(m_formatter.data())[location / sizeof(int32_t) - 1]; + } + + int jumpSizeDelta(JumpType jumpType, JumpLinkType jumpLinkType) { return JUMP_ENUM_SIZE(jumpType) - JUMP_ENUM_SIZE(jumpLinkType); } + + // Assembler admin methods: + + static ALWAYS_INLINE bool linkRecordSourceComparator(const LinkRecord& a, const LinkRecord& b) + { + return a.from() < b.from(); + } + + bool canCompact(JumpType jumpType) + { + // The following cannot be compacted: + // JumpFixed: represents custom jump sequence + // JumpNoConditionFixedSize: represents unconditional jump that must remain a fixed size + // JumpConditionFixedSize: represents conditional jump that must remain a fixed size + return (jumpType == JumpNoCondition) || (jumpType == JumpCondition); + } + + JumpLinkType computeJumpType(JumpType jumpType, const uint8_t* from, const uint8_t* to) + { + if (jumpType == JumpFixed) + return LinkInvalid; + + // for patchable jump we must leave space for the longest code sequence + if (jumpType == JumpNoConditionFixedSize) + return LinkBX; + if (jumpType == JumpConditionFixedSize) + return LinkConditionalBX; + + const int paddingSize = JUMP_ENUM_SIZE(jumpType); + bool mayTriggerErrata = false; + + if (jumpType == JumpCondition) { + // 2-byte conditional T1 + const uint16_t* jumpT1Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT1))); + if (canBeJumpT1(jumpT1Location, to)) + return LinkJumpT1; + // 4-byte conditional T3 + const uint16_t* jumpT3Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT3))); + if (canBeJumpT3(jumpT3Location, to, mayTriggerErrata)) { + if (!mayTriggerErrata) + return LinkJumpT3; + } + // 4-byte conditional T4 with IT + const uint16_t* conditionalJumpT4Location = + reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkConditionalJumpT4))); + if (canBeJumpT4(conditionalJumpT4Location, to, mayTriggerErrata)) { + if (!mayTriggerErrata) + return LinkConditionalJumpT4; + } + } else { + // 2-byte unconditional T2 + const uint16_t* jumpT2Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT2))); + if (canBeJumpT2(jumpT2Location, to)) + return LinkJumpT2; + // 4-byte unconditional T4 + const uint16_t* jumpT4Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT4))); + if (canBeJumpT4(jumpT4Location, to, mayTriggerErrata)) { + if (!mayTriggerErrata) + return LinkJumpT4; + } + // use long jump sequence + return LinkBX; + } + + ASSERT(jumpType == JumpCondition); + return LinkConditionalBX; + } + + JumpLinkType computeJumpType(LinkRecord& record, const uint8_t* from, const uint8_t* to) + { + JumpLinkType linkType = computeJumpType(record.type(), from, to); + record.setLinkType(linkType); + return linkType; + } + + void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset) + { + int32_t ptr = regionStart / sizeof(int32_t); + const int32_t end = regionEnd / sizeof(int32_t); + int32_t* offsets = static_cast(m_formatter.data()); + while (ptr < end) + offsets[ptr++] = offset; + } + + Vector& jumpsToLink() + { + std::sort(m_jumpsToLink.begin(), m_jumpsToLink.end(), linkRecordSourceComparator); + return m_jumpsToLink; + } + + void ALWAYS_INLINE link(LinkRecord& record, uint8_t* from, uint8_t* to) + { + switch (record.linkType()) { + case LinkJumpT1: + linkJumpT1(record.condition(), reinterpret_cast_ptr(from), to); + break; + case LinkJumpT2: + linkJumpT2(reinterpret_cast_ptr(from), to); + break; + case LinkJumpT3: + linkJumpT3(record.condition(), reinterpret_cast_ptr(from), to); + break; + case LinkJumpT4: + linkJumpT4(reinterpret_cast_ptr(from), to); + break; + case LinkConditionalJumpT4: + linkConditionalJumpT4(record.condition(), reinterpret_cast_ptr(from), to); + break; + case LinkConditionalBX: + linkConditionalBX(record.condition(), reinterpret_cast_ptr(from), to); + break; + case LinkBX: + linkBX(reinterpret_cast_ptr(from), to); + break; + default: + ASSERT_NOT_REACHED(); + break; + } + } + + void* unlinkedCode() { return m_formatter.data(); } + size_t codeSize() const { return m_formatter.codeSize(); } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + ASSERT(call.isSet()); + return call.m_offset; + } + + // Linking & patching: + // + // 'link' and 'patch' methods are for use on unprotected code - such as the code + // within the AssemblerBuffer, and code being patched by the patch buffer. Once + // code has been finalized it is (platform support permitting) within a non- + // writable region of memory; to modify the code in an execute-only execuable + // pool the 'repatch' and 'relink' methods should be used. + + void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type, Condition condition) + { + ASSERT(to.isSet()); + ASSERT(from.isSet()); + m_jumpsToLink.append(LinkRecord(from.m_offset, to.m_offset, type, condition)); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + + uint16_t* location = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + linkJumpAbsolute(location, to); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + ASSERT(!(reinterpret_cast(code) & 1)); + ASSERT(from.isSet()); + ASSERT(reinterpret_cast(to) & 1); + + setPointer(reinterpret_cast(reinterpret_cast(code) + from.m_offset) - 1, to); + } + + static void linkPointer(void* code, AssemblerLabel where, void* value) + { + setPointer(reinterpret_cast(code) + where.m_offset, value); + } + + static void relinkJump(void* from, void* to) + { + ASSERT(!(reinterpret_cast(from) & 1)); + ASSERT(!(reinterpret_cast(to) & 1)); + + linkJumpAbsolute(reinterpret_cast(from), to); + + cacheFlush(reinterpret_cast(from) - 5, 5 * sizeof(uint16_t)); + } + + static void relinkCall(void* from, void* to) + { + ASSERT(!(reinterpret_cast(from) & 1)); + ASSERT(reinterpret_cast(to) & 1); + + setPointer(reinterpret_cast(from) - 1, to); + } + + static void* readCallTarget(void* from) + { + return readPointer(reinterpret_cast(from) - 1); + } + + static void repatchInt32(void* where, int32_t value) + { + ASSERT(!(reinterpret_cast(where) & 1)); + + setInt32(where, value); + } + + static void repatchCompact(void* where, int32_t offset) + { + ASSERT(offset >= -255 && offset <= 255); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + + offset |= (add << 9); + offset |= (1 << 10); + offset |= (1 << 11); + + uint16_t* location = reinterpret_cast(where); + location[1] &= ~((1 << 12) - 1); + location[1] |= offset; + cacheFlush(location, sizeof(uint16_t) * 2); + } + + static void repatchPointer(void* where, void* value) + { + ASSERT(!(reinterpret_cast(where) & 1)); + + setPointer(where, value); + } + + static void* readPointer(void* where) + { + return reinterpret_cast(readInt32(where)); + } + + static void replaceWithJump(void* instructionStart, void* to) + { + ASSERT(!(bitwise_cast(instructionStart) & 1)); + ASSERT(!(bitwise_cast(to) & 1)); + uint16_t* ptr = reinterpret_cast(instructionStart) + 2; + + // Ensure that we're not in one of those errata-triggering thingies. If we are, then + // prepend a nop. + bool spansTwo4K = ((reinterpret_cast(ptr) & 0xfff) == 0x002); + + if (spansTwo4K) { + ptr[-2] = OP_NOP_T1; + ptr++; + } + + linkJumpT4(ptr, to); + cacheFlush(ptr - 2, sizeof(uint16_t) * 2); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return 6; + } + + static void replaceWithLoad(void* instructionStart) + { + ASSERT(!(bitwise_cast(instructionStart) & 1)); + uint16_t* ptr = reinterpret_cast(instructionStart); + switch (ptr[0] & 0xFFF0) { + case OP_LDR_imm_T3: + break; + case OP_ADD_imm_T3: + ASSERT(!(ptr[1] & 0xF000)); + ptr[0] &= 0x000F; + ptr[0] |= OP_LDR_imm_T3; + ptr[1] |= (ptr[1] & 0x0F00) << 4; + ptr[1] &= 0xF0FF; + break; + default: + ASSERT_NOT_REACHED(); + } + cacheFlush(ptr, sizeof(uint16_t) * 2); + } + + static void replaceWithAddressComputation(void* instructionStart) + { + ASSERT(!(bitwise_cast(instructionStart) & 1)); + uint16_t* ptr = reinterpret_cast(instructionStart); + switch (ptr[0] & 0xFFF0) { + case OP_LDR_imm_T3: + ASSERT(!(ptr[1] & 0x0F00)); + ptr[0] &= 0x000F; + ptr[0] |= OP_ADD_imm_T3; + ptr[1] |= (ptr[1] & 0xF000) >> 4; + ptr[1] &= 0x0FFF; + break; + case OP_ADD_imm_T3: + break; + default: + ASSERT_NOT_REACHED(); + } + cacheFlush(ptr, sizeof(uint16_t) * 2); + } + + unsigned debugOffset() { return m_formatter.debugOffset(); } + + static void cacheFlush(void* code, size_t size) + { +#if OS(IOS) + sys_cache_control(kCacheFunctionPrepareForExecution, code, size); +#elif OS(LINUX) + asm volatile( + "push {r7}\n" + "mov r0, %0\n" + "mov r1, %1\n" + "movw r7, #0x2\n" + "movt r7, #0xf\n" + "movs r2, #0x0\n" + "svc 0x0\n" + "pop {r7}\n" + : + : "r" (code), "r" (reinterpret_cast(code) + size) + : "r0", "r1", "r2"); +#elif OS(WINCE) + CacheRangeFlush(code, size, CACHE_SYNC_ALL); +#elif OS(QNX) +#if !ENABLE(ASSEMBLER_WX_EXCLUSIVE) + msync(code, size, MS_INVALIDATE_ICACHE); +#else + UNUSED_PARAM(code); + UNUSED_PARAM(size); +#endif +#else +#error "The cacheFlush support is missing on this platform." +#endif + } + +private: + // VFP operations commonly take one or more 5-bit operands, typically representing a + // floating point register number. This will commonly be encoded in the instruction + // in two parts, with one single bit field, and one 4-bit field. In the case of + // double precision operands the high bit of the register number will be encoded + // separately, and for single precision operands the high bit of the register number + // will be encoded individually. + // VFPOperand encapsulates a 5-bit VFP operand, with bits 0..3 containing the 4-bit + // field to be encoded together in the instruction (the low 4-bits of a double + // register number, or the high 4-bits of a single register number), and bit 4 + // contains the bit value to be encoded individually. + struct VFPOperand { + explicit VFPOperand(uint32_t value) + : m_value(value) + { + ASSERT(!(m_value & ~0x1f)); + } + + VFPOperand(FPDoubleRegisterID reg) + : m_value(reg) + { + } + + VFPOperand(RegisterID reg) + : m_value(reg) + { + } + + VFPOperand(FPSingleRegisterID reg) + : m_value(((reg & 1) << 4) | (reg >> 1)) // rotate the lowest bit of 'reg' to the top. + { + } + + uint32_t bits1() + { + return m_value >> 4; + } + + uint32_t bits4() + { + return m_value & 0xf; + } + + uint32_t m_value; + }; + + VFPOperand vcvtOp(bool toInteger, bool isUnsigned, bool isRoundZero) + { + // Cannot specify rounding when converting to float. + ASSERT(toInteger || !isRoundZero); + + uint32_t op = 0x8; + if (toInteger) { + // opc2 indicates both toInteger & isUnsigned. + op |= isUnsigned ? 0x4 : 0x5; + // 'op' field in instruction is isRoundZero + if (isRoundZero) + op |= 0x10; + } else { + ASSERT(!isRoundZero); + // 'op' field in instruction is isUnsigned + if (!isUnsigned) + op |= 0x10; + } + return VFPOperand(op); + } + + static void setInt32(void* code, uint32_t value) + { + uint16_t* location = reinterpret_cast(code); + ASSERT(isMOV_imm_T3(location - 4) && isMOVT(location - 2)); + + ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(value)); + ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(value >> 16)); + location[-4] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); + location[-3] = twoWordOp5i6Imm4Reg4EncodedImmSecond((location[-3] >> 8) & 0xf, lo16); + location[-2] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); + location[-1] = twoWordOp5i6Imm4Reg4EncodedImmSecond((location[-1] >> 8) & 0xf, hi16); + + cacheFlush(location - 4, 4 * sizeof(uint16_t)); + } + + static int32_t readInt32(void* code) + { + uint16_t* location = reinterpret_cast(code); + ASSERT(isMOV_imm_T3(location - 4) && isMOVT(location - 2)); + + ARMThumbImmediate lo16; + ARMThumbImmediate hi16; + decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(lo16, location[-4]); + decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(lo16, location[-3]); + decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(hi16, location[-2]); + decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(hi16, location[-1]); + uint32_t result = hi16.asUInt16(); + result <<= 16; + result |= lo16.asUInt16(); + return static_cast(result); + } + + static void setUInt7ForLoad(void* code, ARMThumbImmediate imm) + { + // Requires us to have planted a LDR_imm_T1 + ASSERT(imm.isValid()); + ASSERT(imm.isUInt7()); + uint16_t* location = reinterpret_cast(code); + location[0] &= ~((static_cast(0x7f) >> 2) << 6); + location[0] |= (imm.getUInt7() >> 2) << 6; + cacheFlush(location, sizeof(uint16_t)); + } + + static void setPointer(void* code, void* value) + { + setInt32(code, reinterpret_cast(value)); + } + + static bool isB(void* address) + { + uint16_t* instruction = static_cast(address); + return ((instruction[0] & 0xf800) == OP_B_T4a) && ((instruction[1] & 0xd000) == OP_B_T4b); + } + + static bool isBX(void* address) + { + uint16_t* instruction = static_cast(address); + return (instruction[0] & 0xff87) == OP_BX; + } + + static bool isMOV_imm_T3(void* address) + { + uint16_t* instruction = static_cast(address); + return ((instruction[0] & 0xFBF0) == OP_MOV_imm_T3) && ((instruction[1] & 0x8000) == 0); + } + + static bool isMOVT(void* address) + { + uint16_t* instruction = static_cast(address); + return ((instruction[0] & 0xFBF0) == OP_MOVT) && ((instruction[1] & 0x8000) == 0); + } + + static bool isNOP_T1(void* address) + { + uint16_t* instruction = static_cast(address); + return instruction[0] == OP_NOP_T1; + } + + static bool isNOP_T2(void* address) + { + uint16_t* instruction = static_cast(address); + return (instruction[0] == OP_NOP_T2a) && (instruction[1] == OP_NOP_T2b); + } + + static bool canBeJumpT1(const uint16_t* instruction, const void* target) + { + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T1 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + return ((relative << 23) >> 23) == relative; + } + + static bool canBeJumpT2(const uint16_t* instruction, const void* target) + { + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T2 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + return ((relative << 20) >> 20) == relative; + } + + static bool canBeJumpT3(const uint16_t* instruction, const void* target, bool& mayTriggerErrata) + { + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // From Cortex-A8 errata: + // If the 32-bit Thumb-2 branch instruction spans two 4KiB regions and + // the target of the branch falls within the first region it is + // possible for the processor to incorrectly determine the branch + // instruction, and it is also possible in some cases for the processor + // to enter a deadlock state. + // The instruction is spanning two pages if it ends at an address ending 0x002 + bool spansTwo4K = ((reinterpret_cast(instruction) & 0xfff) == 0x002); + mayTriggerErrata = spansTwo4K; + // The target is in the first page if the jump branch back by [3..0x1002] bytes + bool targetInFirstPage = (relative >= -0x1002) && (relative < -2); + bool wouldTriggerA8Errata = spansTwo4K && targetInFirstPage; + return ((relative << 11) >> 11) == relative && !wouldTriggerA8Errata; + } + + static bool canBeJumpT4(const uint16_t* instruction, const void* target, bool& mayTriggerErrata) + { + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // From Cortex-A8 errata: + // If the 32-bit Thumb-2 branch instruction spans two 4KiB regions and + // the target of the branch falls within the first region it is + // possible for the processor to incorrectly determine the branch + // instruction, and it is also possible in some cases for the processor + // to enter a deadlock state. + // The instruction is spanning two pages if it ends at an address ending 0x002 + bool spansTwo4K = ((reinterpret_cast(instruction) & 0xfff) == 0x002); + mayTriggerErrata = spansTwo4K; + // The target is in the first page if the jump branch back by [3..0x1002] bytes + bool targetInFirstPage = (relative >= -0x1002) && (relative < -2); + bool wouldTriggerA8Errata = spansTwo4K && targetInFirstPage; + return ((relative << 7) >> 7) == relative && !wouldTriggerA8Errata; + } + + void linkJumpT1(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + ASSERT(canBeJumpT1(instruction, target)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T1 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-1] = OP_B_T1 | ((cond & 0xf) << 8) | ((relative & 0x1fe) >> 1); + } + + static void linkJumpT2(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + ASSERT(canBeJumpT2(instruction, target)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T2 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-1] = OP_B_T2 | ((relative & 0xffe) >> 1); + } + + void linkJumpT3(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + bool scratch; + UNUSED_PARAM(scratch); + ASSERT(canBeJumpT3(instruction, target, scratch)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-2] = OP_B_T3a | ((relative & 0x100000) >> 10) | ((cond & 0xf) << 6) | ((relative & 0x3f000) >> 12); + instruction[-1] = OP_B_T3b | ((relative & 0x80000) >> 8) | ((relative & 0x40000) >> 5) | ((relative & 0xffe) >> 1); + } + + static void linkJumpT4(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + bool scratch; + UNUSED_PARAM(scratch); + ASSERT(canBeJumpT4(instruction, target, scratch)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // ARM encoding for the top two bits below the sign bit is 'peculiar'. + if (relative >= 0) + relative ^= 0xC00000; + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-2] = OP_B_T4a | ((relative & 0x1000000) >> 14) | ((relative & 0x3ff000) >> 12); + instruction[-1] = OP_B_T4b | ((relative & 0x800000) >> 10) | ((relative & 0x400000) >> 11) | ((relative & 0xffe) >> 1); + } + + void linkConditionalJumpT4(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + instruction[-3] = ifThenElse(cond) | OP_IT; + linkJumpT4(instruction, target); + } + + static void linkBX(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; + ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) + 1)); + ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) >> 16)); + instruction[-5] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); + instruction[-4] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, lo16); + instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); + instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); + instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); + } + + void linkConditionalBX(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + linkBX(instruction, target); + instruction[-6] = ifThenElse(cond, true, true) | OP_IT; + } + + static void linkJumpAbsolute(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + ASSERT((isMOV_imm_T3(instruction - 5) && isMOVT(instruction - 3) && isBX(instruction - 1)) + || (isNOP_T1(instruction - 5) && isNOP_T2(instruction - 4) && isB(instruction - 2))); + + bool scratch; + if (canBeJumpT4(instruction, target, scratch)) { + // There may be a better way to fix this, but right now put the NOPs first, since in the + // case of an conditional branch this will be coming after an ITTT predicating *three* + // instructions! Looking backwards to modify the ITTT to an IT is not easy, due to + // variable wdith encoding - the previous instruction might *look* like an ITTT but + // actually be the second half of a 2-word op. + instruction[-5] = OP_NOP_T1; + instruction[-4] = OP_NOP_T2a; + instruction[-3] = OP_NOP_T2b; + linkJumpT4(instruction, target); + } else { + const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; + ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) + 1)); + ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) >> 16)); + instruction[-5] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); + instruction[-4] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, lo16); + instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); + instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); + instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); + } + } + + static uint16_t twoWordOp5i6Imm4Reg4EncodedImmFirst(uint16_t op, ARMThumbImmediate imm) + { + return op | (imm.m_value.i << 10) | imm.m_value.imm4; + } + + static void decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(ARMThumbImmediate& result, uint16_t value) + { + result.m_value.i = (value >> 10) & 1; + result.m_value.imm4 = value & 15; + } + + static uint16_t twoWordOp5i6Imm4Reg4EncodedImmSecond(uint16_t rd, ARMThumbImmediate imm) + { + return (imm.m_value.imm3 << 12) | (rd << 8) | imm.m_value.imm8; + } + + static void decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(ARMThumbImmediate& result, uint16_t value) + { + result.m_value.imm3 = (value >> 12) & 7; + result.m_value.imm8 = value & 255; + } + + class ARMInstructionFormatter { + public: + ALWAYS_INLINE void oneWordOp5Reg3Imm8(OpcodeID op, RegisterID rd, uint8_t imm) + { + m_buffer.putShort(op | (rd << 8) | imm); + } + + ALWAYS_INLINE void oneWordOp5Imm5Reg3Reg3(OpcodeID op, uint8_t imm, RegisterID reg1, RegisterID reg2) + { + m_buffer.putShort(op | (imm << 6) | (reg1 << 3) | reg2); + } + + ALWAYS_INLINE void oneWordOp7Reg3Reg3Reg3(OpcodeID op, RegisterID reg1, RegisterID reg2, RegisterID reg3) + { + m_buffer.putShort(op | (reg1 << 6) | (reg2 << 3) | reg3); + } + + ALWAYS_INLINE void oneWordOp8Imm8(OpcodeID op, uint8_t imm) + { + m_buffer.putShort(op | imm); + } + + ALWAYS_INLINE void oneWordOp8RegReg143(OpcodeID op, RegisterID reg1, RegisterID reg2) + { + m_buffer.putShort(op | ((reg2 & 8) << 4) | (reg1 << 3) | (reg2 & 7)); + } + + ALWAYS_INLINE void oneWordOp9Imm7(OpcodeID op, uint8_t imm) + { + m_buffer.putShort(op | imm); + } + + ALWAYS_INLINE void oneWordOp10Reg3Reg3(OpcodeID op, RegisterID reg1, RegisterID reg2) + { + m_buffer.putShort(op | (reg1 << 3) | reg2); + } + + ALWAYS_INLINE void twoWordOp12Reg4FourFours(OpcodeID1 op, RegisterID reg, FourFours ff) + { + m_buffer.putShort(op | reg); + m_buffer.putShort(ff.m_u.value); + } + + ALWAYS_INLINE void twoWordOp16FourFours(OpcodeID1 op, FourFours ff) + { + m_buffer.putShort(op); + m_buffer.putShort(ff.m_u.value); + } + + ALWAYS_INLINE void twoWordOp16Op16(OpcodeID1 op1, OpcodeID2 op2) + { + m_buffer.putShort(op1); + m_buffer.putShort(op2); + } + + ALWAYS_INLINE void twoWordOp5i6Imm4Reg4EncodedImm(OpcodeID1 op, int imm4, RegisterID rd, ARMThumbImmediate imm) + { + ARMThumbImmediate newImm = imm; + newImm.m_value.imm4 = imm4; + + m_buffer.putShort(ARMv7Assembler::twoWordOp5i6Imm4Reg4EncodedImmFirst(op, newImm)); + m_buffer.putShort(ARMv7Assembler::twoWordOp5i6Imm4Reg4EncodedImmSecond(rd, newImm)); + } + + ALWAYS_INLINE void twoWordOp12Reg4Reg4Imm12(OpcodeID1 op, RegisterID reg1, RegisterID reg2, uint16_t imm) + { + m_buffer.putShort(op | reg1); + m_buffer.putShort((reg2 << 12) | imm); + } + + ALWAYS_INLINE void twoWordOp12Reg40Imm3Reg4Imm20Imm5(OpcodeID1 op, RegisterID reg1, RegisterID reg2, uint16_t imm1, uint16_t imm2, uint16_t imm3) + { + m_buffer.putShort(op | reg1); + m_buffer.putShort((imm1 << 12) | (reg2 << 8) | (imm2 << 6) | imm3); + } + + // Formats up instructions of the pattern: + // 111111111B11aaaa:bbbb222SA2C2cccc + // Where 1s in the pattern come from op1, 2s in the pattern come from op2, S is the provided size bit. + // Operands provide 5 bit values of the form Aaaaa, Bbbbb, Ccccc. + ALWAYS_INLINE void vfpOp(OpcodeID1 op1, OpcodeID2 op2, bool size, VFPOperand a, VFPOperand b, VFPOperand c) + { + ASSERT(!(op1 & 0x004f)); + ASSERT(!(op2 & 0xf1af)); + m_buffer.putShort(op1 | b.bits1() << 6 | a.bits4()); + m_buffer.putShort(op2 | b.bits4() << 12 | size << 8 | a.bits1() << 7 | c.bits1() << 5 | c.bits4()); + } + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + // (i.e. +/-(0..255) 32-bit words) + ALWAYS_INLINE void vfpMemOp(OpcodeID1 op1, OpcodeID2 op2, bool size, RegisterID rn, VFPOperand rd, int32_t imm) + { + bool up = true; + if (imm < 0) { + imm = -imm; + up = false; + } + + uint32_t offset = imm; + ASSERT(!(offset & ~0x3fc)); + offset >>= 2; + + m_buffer.putShort(op1 | (up << 7) | rd.bits1() << 6 | rn); + m_buffer.putShort(op2 | rd.bits4() << 12 | size << 8 | offset); + } + + // Administrative methods: + + size_t codeSize() const { return m_buffer.codeSize(); } + AssemblerLabel label() const { return m_buffer.label(); } + bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); } + void* data() const { return m_buffer.data(); } + + unsigned debugOffset() { return m_buffer.debugOffset(); } + + private: + AssemblerBuffer m_buffer; + } m_formatter; + + Vector m_jumpsToLink; + Vector m_offsets; + int m_indexOfLastWatchpoint; + int m_indexOfTailOfLastWatchpoint; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) + +#endif // ARMAssembler_h diff --git a/masm/assembler/AbstractMacroAssembler.h b/masm/assembler/AbstractMacroAssembler.h new file mode 100644 index 0000000000..e6a9df9944 --- /dev/null +++ b/masm/assembler/AbstractMacroAssembler.h @@ -0,0 +1,734 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AbstractMacroAssembler_h +#define AbstractMacroAssembler_h + +#include "AssemblerBuffer.h" +#include "CodeLocation.h" +#include "MacroAssemblerCodeRef.h" +#include +#include +#include + +#if ENABLE(ASSEMBLER) + + +#if PLATFORM(QT) +#define ENABLE_JIT_CONSTANT_BLINDING 0 +#endif + +#ifndef ENABLE_JIT_CONSTANT_BLINDING +#define ENABLE_JIT_CONSTANT_BLINDING 1 +#endif + +namespace JSC { + +class JumpReplacementWatchpoint; +class LinkBuffer; +class RepatchBuffer; +class Watchpoint; +namespace DFG { +class CorrectableJumpPoint; +} + +template +class AbstractMacroAssembler { +public: + friend class JITWriteBarrierBase; + typedef AssemblerType AssemblerType_T; + + typedef MacroAssemblerCodePtr CodePtr; + typedef MacroAssemblerCodeRef CodeRef; + + class Jump; + + typedef typename AssemblerType::RegisterID RegisterID; + + // Section 1: MacroAssembler operand types + // + // The following types are used as operands to MacroAssembler operations, + // describing immediate and memory operands to the instructions to be planted. + + enum Scale { + TimesOne, + TimesTwo, + TimesFour, + TimesEight, + }; + + // Address: + // + // Describes a simple base-offset address. + struct Address { + explicit Address(RegisterID base, int32_t offset = 0) + : base(base) + , offset(offset) + { + } + + RegisterID base; + int32_t offset; + }; + + struct ExtendedAddress { + explicit ExtendedAddress(RegisterID base, intptr_t offset = 0) + : base(base) + , offset(offset) + { + } + + RegisterID base; + intptr_t offset; + }; + + // ImplicitAddress: + // + // This class is used for explicit 'load' and 'store' operations + // (as opposed to situations in which a memory operand is provided + // to a generic operation, such as an integer arithmetic instruction). + // + // In the case of a load (or store) operation we want to permit + // addresses to be implicitly constructed, e.g. the two calls: + // + // load32(Address(addrReg), destReg); + // load32(addrReg, destReg); + // + // Are equivalent, and the explicit wrapping of the Address in the former + // is unnecessary. + struct ImplicitAddress { + ImplicitAddress(RegisterID base) + : base(base) + , offset(0) + { + } + + ImplicitAddress(Address address) + : base(address.base) + , offset(address.offset) + { + } + + RegisterID base; + int32_t offset; + }; + + // BaseIndex: + // + // Describes a complex addressing mode. + struct BaseIndex { + BaseIndex(RegisterID base, RegisterID index, Scale scale, int32_t offset = 0) + : base(base) + , index(index) + , scale(scale) + , offset(offset) + { + } + + RegisterID base; + RegisterID index; + Scale scale; + int32_t offset; + }; + + // AbsoluteAddress: + // + // Describes an memory operand given by a pointer. For regular load & store + // operations an unwrapped void* will be used, rather than using this. + struct AbsoluteAddress { + explicit AbsoluteAddress(const void* ptr) + : m_ptr(ptr) + { + } + + const void* m_ptr; + }; + + // TrustedImmPtr: + // + // A pointer sized immediate operand to an instruction - this is wrapped + // in a class requiring explicit construction in order to differentiate + // from pointers used as absolute addresses to memory operations + struct TrustedImmPtr { + TrustedImmPtr() { } + + explicit TrustedImmPtr(const void* value) + : m_value(value) + { + } + + // This is only here so that TrustedImmPtr(0) does not confuse the C++ + // overload handling rules. + explicit TrustedImmPtr(int value) + : m_value(0) + { + ASSERT_UNUSED(value, !value); + } + + explicit TrustedImmPtr(size_t value) + : m_value(reinterpret_cast(value)) + { + } + + intptr_t asIntptr() + { + return reinterpret_cast(m_value); + } + + const void* m_value; + }; + + struct ImmPtr : +#if ENABLE(JIT_CONSTANT_BLINDING) + private TrustedImmPtr +#else + public TrustedImmPtr +#endif + { + explicit ImmPtr(const void* value) + : TrustedImmPtr(value) + { + } + + TrustedImmPtr asTrustedImmPtr() { return *this; } + }; + + // TrustedImm32: + // + // A 32bit immediate operand to an instruction - this is wrapped in a + // class requiring explicit construction in order to prevent RegisterIDs + // (which are implemented as an enum) from accidentally being passed as + // immediate values. + struct TrustedImm32 { + TrustedImm32() { } + + explicit TrustedImm32(int32_t value) + : m_value(value) + { + } + +#if !CPU(X86_64) + explicit TrustedImm32(TrustedImmPtr ptr) + : m_value(ptr.asIntptr()) + { + } +#endif + + int32_t m_value; + }; + + + struct Imm32 : +#if ENABLE(JIT_CONSTANT_BLINDING) + private TrustedImm32 +#else + public TrustedImm32 +#endif + { + explicit Imm32(int32_t value) + : TrustedImm32(value) + { + } +#if !CPU(X86_64) + explicit Imm32(TrustedImmPtr ptr) + : TrustedImm32(ptr) + { + } +#endif + const TrustedImm32& asTrustedImm32() const { return *this; } + + }; + + // Section 2: MacroAssembler code buffer handles + // + // The following types are used to reference items in the code buffer + // during JIT code generation. For example, the type Jump is used to + // track the location of a jump instruction so that it may later be + // linked to a label marking its destination. + + + // Label: + // + // A Label records a point in the generated instruction stream, typically such that + // it may be used as a destination for a jump. + class Label { + template + friend class AbstractMacroAssembler; + friend class DFG::CorrectableJumpPoint; + friend class Jump; + friend class JumpReplacementWatchpoint; + friend class MacroAssemblerCodeRef; + friend class LinkBuffer; + friend class Watchpoint; + + public: + Label() + { + } + + Label(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.label()) + { + } + + bool isSet() const { return m_label.isSet(); } + private: + AssemblerLabel m_label; + }; + + // ConvertibleLoadLabel: + // + // A ConvertibleLoadLabel records a loadPtr instruction that can be patched to an addPtr + // so that: + // + // loadPtr(Address(a, i), b) + // + // becomes: + // + // addPtr(TrustedImmPtr(i), a, b) + class ConvertibleLoadLabel { + template + friend class AbstractMacroAssembler; + friend class LinkBuffer; + + public: + ConvertibleLoadLabel() + { + } + + ConvertibleLoadLabel(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.labelIgnoringWatchpoints()) + { + } + + bool isSet() const { return m_label.isSet(); } + private: + AssemblerLabel m_label; + }; + + // DataLabelPtr: + // + // A DataLabelPtr is used to refer to a location in the code containing a pointer to be + // patched after the code has been generated. + class DataLabelPtr { + template + friend class AbstractMacroAssembler; + friend class LinkBuffer; + public: + DataLabelPtr() + { + } + + DataLabelPtr(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.label()) + { + } + + bool isSet() const { return m_label.isSet(); } + + private: + AssemblerLabel m_label; + }; + + // DataLabel32: + // + // A DataLabelPtr is used to refer to a location in the code containing a pointer to be + // patched after the code has been generated. + class DataLabel32 { + template + friend class AbstractMacroAssembler; + friend class LinkBuffer; + public: + DataLabel32() + { + } + + DataLabel32(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.label()) + { + } + + AssemblerLabel label() const { return m_label; } + + private: + AssemblerLabel m_label; + }; + + // DataLabelCompact: + // + // A DataLabelCompact is used to refer to a location in the code containing a + // compact immediate to be patched after the code has been generated. + class DataLabelCompact { + template + friend class AbstractMacroAssembler; + friend class LinkBuffer; + public: + DataLabelCompact() + { + } + + DataLabelCompact(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.label()) + { + } + + DataLabelCompact(AssemblerLabel label) + : m_label(label) + { + } + + private: + AssemblerLabel m_label; + }; + + // Call: + // + // A Call object is a reference to a call instruction that has been planted + // into the code buffer - it is typically used to link the call, setting the + // relative offset such that when executed it will call to the desired + // destination. + class Call { + template + friend class AbstractMacroAssembler; + + public: + enum Flags { + None = 0x0, + Linkable = 0x1, + Near = 0x2, + LinkableNear = 0x3, + }; + + Call() + : m_flags(None) + { + } + + Call(AssemblerLabel jmp, Flags flags) + : m_label(jmp) + , m_flags(flags) + { + } + + bool isFlagSet(Flags flag) + { + return m_flags & flag; + } + + static Call fromTailJump(Jump jump) + { + return Call(jump.m_label, Linkable); + } + + AssemblerLabel m_label; + private: + Flags m_flags; + }; + + // Jump: + // + // A jump object is a reference to a jump instruction that has been planted + // into the code buffer - it is typically used to link the jump, setting the + // relative offset such that when executed it will jump to the desired + // destination. + class Jump { + template + friend class AbstractMacroAssembler; + friend class Call; + friend class DFG::CorrectableJumpPoint; + friend class LinkBuffer; + public: + Jump() + { + } + +#if CPU(ARM_THUMB2) + // Fixme: this information should be stored in the instruction stream, not in the Jump object. + Jump(AssemblerLabel jmp, ARMv7Assembler::JumpType type, ARMv7Assembler::Condition condition = ARMv7Assembler::ConditionInvalid) + : m_label(jmp) + , m_type(type) + , m_condition(condition) + { + } +#elif CPU(SH4) + Jump(AssemblerLabel jmp, SH4Assembler::JumpType type = SH4Assembler::JumpFar) + : m_label(jmp) + , m_type(type) + { + } +#else + Jump(AssemblerLabel jmp) + : m_label(jmp) + { + } +#endif + + void link(AbstractMacroAssembler* masm) const + { +#if CPU(ARM_THUMB2) + masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type, m_condition); +#elif CPU(SH4) + masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type); +#else + masm->m_assembler.linkJump(m_label, masm->m_assembler.label()); +#endif + } + + void linkTo(Label label, AbstractMacroAssembler* masm) const + { +#if CPU(ARM_THUMB2) + masm->m_assembler.linkJump(m_label, label.m_label, m_type, m_condition); +#else + masm->m_assembler.linkJump(m_label, label.m_label); +#endif + } + + bool isSet() const { return m_label.isSet(); } + + private: + AssemblerLabel m_label; +#if CPU(ARM_THUMB2) + ARMv7Assembler::JumpType m_type; + ARMv7Assembler::Condition m_condition; +#endif +#if CPU(SH4) + SH4Assembler::JumpType m_type; +#endif + }; + + struct PatchableJump { + PatchableJump() + { + } + + explicit PatchableJump(Jump jump) + : m_jump(jump) + { + } + + operator Jump&() { return m_jump; } + + Jump m_jump; + }; + + // JumpList: + // + // A JumpList is a set of Jump objects. + // All jumps in the set will be linked to the same destination. + class JumpList { + friend class LinkBuffer; + + public: + typedef Vector JumpVector; + + void link(AbstractMacroAssembler* masm) + { + size_t size = m_jumps.size(); + for (size_t i = 0; i < size; ++i) + m_jumps[i].link(masm); + m_jumps.clear(); + } + + void linkTo(Label label, AbstractMacroAssembler* masm) + { + size_t size = m_jumps.size(); + for (size_t i = 0; i < size; ++i) + m_jumps[i].linkTo(label, masm); + m_jumps.clear(); + } + + void append(Jump jump) + { + m_jumps.append(jump); + } + + void append(JumpList& other) + { + m_jumps.append(other.m_jumps.begin(), other.m_jumps.size()); + } + + bool empty() + { + return !m_jumps.size(); + } + + void clear() + { + m_jumps.clear(); + } + + const JumpVector& jumps() { return m_jumps; } + + private: + JumpVector m_jumps; + }; + + + // Section 3: Misc admin methods +#if ENABLE(DFG_JIT) + Label labelIgnoringWatchpoints() + { + Label result; + result.m_label = m_assembler.labelIgnoringWatchpoints(); + return result; + } +#else + Label labelIgnoringWatchpoints() + { + return label(); + } +#endif + + Label label() + { + return Label(this); + } + + void padBeforePatch() + { + // Rely on the fact that asking for a label already does the padding. + (void)label(); + } + + Label watchpointLabel() + { + Label result; + result.m_label = m_assembler.labelForWatchpoint(); + return result; + } + + Label align() + { + m_assembler.align(16); + return Label(this); + } + + template + static ptrdiff_t differenceBetween(T from, U to) + { + return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_label); + } + + static ptrdiff_t differenceBetweenCodePtr(const MacroAssemblerCodePtr& a, const MacroAssemblerCodePtr& b) + { + return reinterpret_cast(b.executableAddress()) - reinterpret_cast(a.executableAddress()); + } + + unsigned debugOffset() { return m_assembler.debugOffset(); } + + ALWAYS_INLINE static void cacheFlush(void* code, size_t size) + { + AssemblerType::cacheFlush(code, size); + } +protected: + AbstractMacroAssembler() + : m_randomSource(cryptographicallyRandomNumber()) + { + } + + AssemblerType m_assembler; + + uint32_t random() + { + return m_randomSource.getUint32(); + } + + WeakRandom m_randomSource; + +#if ENABLE(JIT_CONSTANT_BLINDING) + static bool scratchRegisterForBlinding() { return false; } + static bool shouldBlindForSpecificArch(uint32_t) { return true; } + static bool shouldBlindForSpecificArch(uint64_t) { return true; } +#endif + + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkJump(void* code, Jump jump, CodeLocationLabel target) + { + AssemblerType::linkJump(code, jump.m_label, target.dataLocation()); + } + + static void linkPointer(void* code, AssemblerLabel label, void* value) + { + AssemblerType::linkPointer(code, label, value); + } + + static void* getLinkerAddress(void* code, AssemblerLabel label) + { + return AssemblerType::getRelocatedAddress(code, label); + } + + static unsigned getLinkerCallReturnOffset(Call call) + { + return AssemblerType::getCallReturnOffset(call.m_label); + } + + static void repatchJump(CodeLocationJump jump, CodeLocationLabel destination) + { + AssemblerType::relinkJump(jump.dataLocation(), destination.dataLocation()); + } + + static void repatchNearCall(CodeLocationNearCall nearCall, CodeLocationLabel destination) + { + AssemblerType::relinkCall(nearCall.dataLocation(), destination.executableAddress()); + } + + static void repatchCompact(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) + { + AssemblerType::repatchCompact(dataLabelCompact.dataLocation(), value); + } + + static void repatchInt32(CodeLocationDataLabel32 dataLabel32, int32_t value) + { + AssemblerType::repatchInt32(dataLabel32.dataLocation(), value); + } + + static void repatchPointer(CodeLocationDataLabelPtr dataLabelPtr, void* value) + { + AssemblerType::repatchPointer(dataLabelPtr.dataLocation(), value); + } + + static void* readPointer(CodeLocationDataLabelPtr dataLabelPtr) + { + return AssemblerType::readPointer(dataLabelPtr.dataLocation()); + } + + static void replaceWithLoad(CodeLocationConvertibleLoad label) + { + AssemblerType::replaceWithLoad(label.dataLocation()); + } + + static void replaceWithAddressComputation(CodeLocationConvertibleLoad label) + { + AssemblerType::replaceWithAddressComputation(label.dataLocation()); + } +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // AbstractMacroAssembler_h diff --git a/masm/assembler/AssemblerBuffer.h b/masm/assembler/AssemblerBuffer.h new file mode 100644 index 0000000000..6bc1b3924d --- /dev/null +++ b/masm/assembler/AssemblerBuffer.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AssemblerBuffer_h +#define AssemblerBuffer_h + +#if ENABLE(ASSEMBLER) + +#include "ExecutableAllocator.h" +#include "JITCompilationEffort.h" +#include "JSGlobalData.h" +#include "stdint.h" +#include +#include +#include +#include + +namespace JSC { + + struct AssemblerLabel { + AssemblerLabel() + : m_offset(std::numeric_limits::max()) + { + } + + explicit AssemblerLabel(uint32_t offset) + : m_offset(offset) + { + } + + bool isSet() const { return (m_offset != std::numeric_limits::max()); } + + AssemblerLabel labelAtOffset(int offset) const + { + return AssemblerLabel(m_offset + offset); + } + + uint32_t m_offset; + }; + + class AssemblerBuffer { + static const int inlineCapacity = 128; + public: + AssemblerBuffer() + : m_storage(inlineCapacity) + , m_buffer(m_storage.begin()) + , m_capacity(inlineCapacity) + , m_index(0) + { + } + + ~AssemblerBuffer() + { + } + + bool isAvailable(int space) + { + return m_index + space <= m_capacity; + } + + void ensureSpace(int space) + { + if (!isAvailable(space)) + grow(); + } + + bool isAligned(int alignment) const + { + return !(m_index & (alignment - 1)); + } + + template + void putIntegral(IntegralType value) + { + ensureSpace(sizeof(IntegralType)); + putIntegralUnchecked(value); + } + + template + void putIntegralUnchecked(IntegralType value) + { + ASSERT(isAvailable(sizeof(IntegralType))); + *reinterpret_cast_ptr(m_buffer + m_index) = value; + m_index += sizeof(IntegralType); + } + + void putByteUnchecked(int8_t value) { putIntegralUnchecked(value); } + void putByte(int8_t value) { putIntegral(value); } + void putShortUnchecked(int16_t value) { putIntegralUnchecked(value); } + void putShort(int16_t value) { putIntegral(value); } + void putIntUnchecked(int32_t value) { putIntegralUnchecked(value); } + void putInt(int32_t value) { putIntegral(value); } + void putInt64Unchecked(int64_t value) { putIntegralUnchecked(value); } + void putInt64(int64_t value) { putIntegral(value); } + + void* data() const + { + return m_buffer; + } + + size_t codeSize() const + { + return m_index; + } + + AssemblerLabel label() const + { + return AssemblerLabel(m_index); + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + if (!m_index) + return 0; + + RefPtr result = globalData.executableAllocator.allocate(globalData, m_index, ownerUID, effort); + + if (!result) + return 0; + + ExecutableAllocator::makeWritable(result->start(), result->sizeInBytes()); + + memcpy(result->start(), m_buffer, m_index); + + return result.release(); + } + + unsigned debugOffset() { return m_index; } + + protected: + void append(const char* data, int size) + { + if (!isAvailable(size)) + grow(size); + + memcpy(m_buffer + m_index, data, size); + m_index += size; + } + + void grow(int extraCapacity = 0) + { + m_capacity += m_capacity / 2 + extraCapacity; + + m_storage.grow(m_capacity); + m_buffer = m_storage.begin(); + } + + private: + Vector m_storage; + char* m_buffer; + int m_capacity; + int m_index; + }; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // AssemblerBuffer_h diff --git a/masm/assembler/AssemblerBufferWithConstantPool.h b/masm/assembler/AssemblerBufferWithConstantPool.h new file mode 100644 index 0000000000..5377ef0c7a --- /dev/null +++ b/masm/assembler/AssemblerBufferWithConstantPool.h @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2009 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AssemblerBufferWithConstantPool_h +#define AssemblerBufferWithConstantPool_h + +#if ENABLE(ASSEMBLER) + +#include "AssemblerBuffer.h" +#include + +#define ASSEMBLER_HAS_CONSTANT_POOL 1 + +namespace JSC { + +/* + On a constant pool 4 or 8 bytes data can be stored. The values can be + constants or addresses. The addresses should be 32 or 64 bits. The constants + should be double-precisions float or integer numbers which are hard to be + encoded as few machine instructions. + + TODO: The pool is desinged to handle both 32 and 64 bits values, but + currently only the 4 bytes constants are implemented and tested. + + The AssemblerBuffer can contain multiple constant pools. Each pool is inserted + into the instruction stream - protected by a jump instruction from the + execution flow. + + The flush mechanism is called when no space remain to insert the next instruction + into the pool. Three values are used to determine when the constant pool itself + have to be inserted into the instruction stream (Assembler Buffer): + + - maxPoolSize: size of the constant pool in bytes, this value cannot be + larger than the maximum offset of a PC relative memory load + + - barrierSize: size of jump instruction in bytes which protects the + constant pool from execution + + - maxInstructionSize: maximum length of a machine instruction in bytes + + There are some callbacks which solve the target architecture specific + address handling: + + - TYPE patchConstantPoolLoad(TYPE load, int value): + patch the 'load' instruction with the index of the constant in the + constant pool and return the patched instruction. + + - void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr): + patch the a PC relative load instruction at 'loadAddr' address with the + final relative offset. The offset can be computed with help of + 'constPoolAddr' (the address of the constant pool) and index of the + constant (which is stored previously in the load instruction itself). + + - TYPE placeConstantPoolBarrier(int size): + return with a constant pool barrier instruction which jumps over the + constant pool. + + The 'put*WithConstant*' functions should be used to place a data into the + constant pool. +*/ + +template +class AssemblerBufferWithConstantPool : public AssemblerBuffer { + typedef SegmentedVector LoadOffsets; + using AssemblerBuffer::putIntegral; + using AssemblerBuffer::putIntegralUnchecked; +public: + typedef struct { + short high; + short low; + } TwoShorts; + + enum { + UniqueConst, + ReusableConst, + UnusedEntry, + }; + + AssemblerBufferWithConstantPool() + : AssemblerBuffer() + , m_numConsts(0) + , m_maxDistance(maxPoolSize) + , m_lastConstDelta(0) + { + m_pool = static_cast(fastMalloc(maxPoolSize)); + m_mask = static_cast(fastMalloc(maxPoolSize / sizeof(uint32_t))); + } + + ~AssemblerBufferWithConstantPool() + { + fastFree(m_mask); + fastFree(m_pool); + } + + void ensureSpace(int space) + { + flushIfNoSpaceFor(space); + AssemblerBuffer::ensureSpace(space); + } + + void ensureSpace(int insnSpace, int constSpace) + { + flushIfNoSpaceFor(insnSpace, constSpace); + AssemblerBuffer::ensureSpace(insnSpace); + } + + void ensureSpaceForAnyInstruction(int amount = 1) + { + flushIfNoSpaceFor(amount * maxInstructionSize, amount * sizeof(uint64_t)); + } + + bool isAligned(int alignment) + { + flushIfNoSpaceFor(alignment); + return AssemblerBuffer::isAligned(alignment); + } + + void putByteUnchecked(int value) + { + AssemblerBuffer::putByteUnchecked(value); + correctDeltas(1); + } + + void putByte(int value) + { + flushIfNoSpaceFor(1); + AssemblerBuffer::putByte(value); + correctDeltas(1); + } + + void putShortUnchecked(int value) + { + AssemblerBuffer::putShortUnchecked(value); + correctDeltas(2); + } + + void putShort(int value) + { + flushIfNoSpaceFor(2); + AssemblerBuffer::putShort(value); + correctDeltas(2); + } + + void putIntUnchecked(int value) + { + AssemblerBuffer::putIntUnchecked(value); + correctDeltas(4); + } + + void putInt(int value) + { + flushIfNoSpaceFor(4); + AssemblerBuffer::putInt(value); + correctDeltas(4); + } + + void putInt64Unchecked(int64_t value) + { + AssemblerBuffer::putInt64Unchecked(value); + correctDeltas(8); + } + + void putIntegral(TwoShorts value) + { + putIntegral(value.high); + putIntegral(value.low); + } + + void putIntegralUnchecked(TwoShorts value) + { + putIntegralUnchecked(value.high); + putIntegralUnchecked(value.low); + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + flushConstantPool(false); + return AssemblerBuffer::executableCopy(globalData, ownerUID, effort); + } + + void putShortWithConstantInt(uint16_t insn, uint32_t constant, bool isReusable = false) + { + putIntegralWithConstantInt(insn, constant, isReusable); + } + + void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false) + { + putIntegralWithConstantInt(insn, constant, isReusable); + } + + // This flushing mechanism can be called after any unconditional jumps. + void flushWithoutBarrier(bool isForced = false) + { + // Flush if constant pool is more than 60% full to avoid overuse of this function. + if (isForced || 5 * static_cast(m_numConsts) > 3 * maxPoolSize / sizeof(uint32_t)) + flushConstantPool(false); + } + + uint32_t* poolAddress() + { + return m_pool; + } + + int sizeOfConstantPool() + { + return m_numConsts; + } + +private: + void correctDeltas(int insnSize) + { + m_maxDistance -= insnSize; + m_lastConstDelta -= insnSize; + if (m_lastConstDelta < 0) + m_lastConstDelta = 0; + } + + void correctDeltas(int insnSize, int constSize) + { + correctDeltas(insnSize); + + m_maxDistance -= m_lastConstDelta; + m_lastConstDelta = constSize; + } + + template + void putIntegralWithConstantInt(IntegralType insn, uint32_t constant, bool isReusable) + { + if (!m_numConsts) + m_maxDistance = maxPoolSize; + flushIfNoSpaceFor(sizeof(IntegralType), 4); + + m_loadOffsets.append(codeSize()); + if (isReusable) { + for (int i = 0; i < m_numConsts; ++i) { + if (m_mask[i] == ReusableConst && m_pool[i] == constant) { + putIntegral(static_cast(AssemblerType::patchConstantPoolLoad(insn, i))); + correctDeltas(sizeof(IntegralType)); + return; + } + } + } + + m_pool[m_numConsts] = constant; + m_mask[m_numConsts] = static_cast(isReusable ? ReusableConst : UniqueConst); + + putIntegral(static_cast(AssemblerType::patchConstantPoolLoad(insn, m_numConsts))); + ++m_numConsts; + + correctDeltas(sizeof(IntegralType), 4); + } + + void flushConstantPool(bool useBarrier = true) + { + if (m_numConsts == 0) + return; + int alignPool = (codeSize() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1); + + if (alignPool) + alignPool = sizeof(uint64_t) - alignPool; + + // Callback to protect the constant pool from execution + if (useBarrier) + putIntegral(AssemblerType::placeConstantPoolBarrier(m_numConsts * sizeof(uint32_t) + alignPool)); + + if (alignPool) { + if (alignPool & 1) + AssemblerBuffer::putByte(AssemblerType::padForAlign8); + if (alignPool & 2) + AssemblerBuffer::putShort(AssemblerType::padForAlign16); + if (alignPool & 4) + AssemblerBuffer::putInt(AssemblerType::padForAlign32); + } + + int constPoolOffset = codeSize(); + append(reinterpret_cast(m_pool), m_numConsts * sizeof(uint32_t)); + + // Patch each PC relative load + for (LoadOffsets::Iterator iter = m_loadOffsets.begin(); iter != m_loadOffsets.end(); ++iter) { + void* loadAddr = reinterpret_cast(data()) + *iter; + AssemblerType::patchConstantPoolLoad(loadAddr, reinterpret_cast(data()) + constPoolOffset); + } + + m_loadOffsets.clear(); + m_numConsts = 0; + } + + void flushIfNoSpaceFor(int nextInsnSize) + { + if (m_numConsts == 0) + return; + int lastConstDelta = m_lastConstDelta > nextInsnSize ? m_lastConstDelta - nextInsnSize : 0; + if ((m_maxDistance < nextInsnSize + lastConstDelta + barrierSize + (int)sizeof(uint32_t))) + flushConstantPool(); + } + + void flushIfNoSpaceFor(int nextInsnSize, int nextConstSize) + { + if (m_numConsts == 0) + return; + if ((m_maxDistance < nextInsnSize + m_lastConstDelta + nextConstSize + barrierSize + (int)sizeof(uint32_t)) || + (m_numConsts * sizeof(uint32_t) + nextConstSize >= maxPoolSize)) + flushConstantPool(); + } + + uint32_t* m_pool; + char* m_mask; + LoadOffsets m_loadOffsets; + + int m_numConsts; + int m_maxDistance; + int m_lastConstDelta; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // AssemblerBufferWithConstantPool_h diff --git a/masm/assembler/CodeLocation.h b/masm/assembler/CodeLocation.h new file mode 100644 index 0000000000..86d1f2b755 --- /dev/null +++ b/masm/assembler/CodeLocation.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CodeLocation_h +#define CodeLocation_h + +#include "MacroAssemblerCodeRef.h" + +#if ENABLE(ASSEMBLER) + +namespace JSC { + +class CodeLocationInstruction; +class CodeLocationLabel; +class CodeLocationJump; +class CodeLocationCall; +class CodeLocationNearCall; +class CodeLocationDataLabelCompact; +class CodeLocationDataLabel32; +class CodeLocationDataLabelPtr; +class CodeLocationConvertibleLoad; + +// The CodeLocation* types are all pretty much do-nothing wrappers around +// CodePtr (or MacroAssemblerCodePtr, to give it its full name). These +// classes only exist to provide type-safety when linking and patching code. +// +// The one new piece of functionallity introduced by these classes is the +// ability to create (or put another way, to re-discover) another CodeLocation +// at an offset from one you already know. When patching code to optimize it +// we often want to patch a number of instructions that are short, fixed +// offsets apart. To reduce memory overhead we will only retain a pointer to +// one of the instructions, and we will use the *AtOffset methods provided by +// CodeLocationCommon to find the other points in the code to modify. +class CodeLocationCommon : public MacroAssemblerCodePtr { +public: + CodeLocationInstruction instructionAtOffset(int offset); + CodeLocationLabel labelAtOffset(int offset); + CodeLocationJump jumpAtOffset(int offset); + CodeLocationCall callAtOffset(int offset); + CodeLocationNearCall nearCallAtOffset(int offset); + CodeLocationDataLabelPtr dataLabelPtrAtOffset(int offset); + CodeLocationDataLabel32 dataLabel32AtOffset(int offset); + CodeLocationDataLabelCompact dataLabelCompactAtOffset(int offset); + CodeLocationConvertibleLoad convertibleLoadAtOffset(int offset); + +protected: + CodeLocationCommon() + { + } + + CodeLocationCommon(MacroAssemblerCodePtr location) + : MacroAssemblerCodePtr(location) + { + } +}; + +class CodeLocationInstruction : public CodeLocationCommon { +public: + CodeLocationInstruction() {} + explicit CodeLocationInstruction(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationInstruction(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationLabel : public CodeLocationCommon { +public: + CodeLocationLabel() {} + explicit CodeLocationLabel(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationLabel(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationJump : public CodeLocationCommon { +public: + CodeLocationJump() {} + explicit CodeLocationJump(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationJump(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationCall : public CodeLocationCommon { +public: + CodeLocationCall() {} + explicit CodeLocationCall(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationCall(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationNearCall : public CodeLocationCommon { +public: + CodeLocationNearCall() {} + explicit CodeLocationNearCall(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationNearCall(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationDataLabel32 : public CodeLocationCommon { +public: + CodeLocationDataLabel32() {} + explicit CodeLocationDataLabel32(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationDataLabel32(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationDataLabelCompact : public CodeLocationCommon { +public: + CodeLocationDataLabelCompact() { } + explicit CodeLocationDataLabelCompact(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) { } + explicit CodeLocationDataLabelCompact(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) { } +}; + +class CodeLocationDataLabelPtr : public CodeLocationCommon { +public: + CodeLocationDataLabelPtr() {} + explicit CodeLocationDataLabelPtr(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationDataLabelPtr(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationConvertibleLoad : public CodeLocationCommon { +public: + CodeLocationConvertibleLoad() { } + explicit CodeLocationConvertibleLoad(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) { } + explicit CodeLocationConvertibleLoad(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) { } +}; + +inline CodeLocationInstruction CodeLocationCommon::instructionAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationInstruction(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationLabel CodeLocationCommon::labelAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationLabel(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationJump CodeLocationCommon::jumpAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationJump(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationCall CodeLocationCommon::callAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationCall(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationNearCall CodeLocationCommon::nearCallAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationNearCall(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationDataLabelPtr CodeLocationCommon::dataLabelPtrAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationDataLabelPtr(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationDataLabel32 CodeLocationCommon::dataLabel32AtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationDataLabel32(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationDataLabelCompact CodeLocationCommon::dataLabelCompactAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationDataLabelCompact(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationConvertibleLoad CodeLocationCommon::convertibleLoadAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationConvertibleLoad(reinterpret_cast(dataLocation()) + offset); +} + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // CodeLocation_h diff --git a/masm/assembler/LinkBuffer.cpp b/masm/assembler/LinkBuffer.cpp new file mode 100644 index 0000000000..0176e4307a --- /dev/null +++ b/masm/assembler/LinkBuffer.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "LinkBuffer.h" + +#if ENABLE(ASSEMBLER) + +#include "Options.h" + +namespace JSC { + +LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithoutDisassembly() +{ + performFinalization(); + + return CodeRef(m_executableMemory); +} + +LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithDisassembly(const char* format, ...) +{ + ASSERT(Options::showDisassembly() || Options::showDFGDisassembly()); + + CodeRef result = finalizeCodeWithoutDisassembly(); + + dataLog("Generated JIT code for "); + va_list argList; + va_start(argList, format); + WTF::dataLogV(format, argList); + va_end(argList); + dataLog(":\n"); + + dataLog(" Code at [%p, %p):\n", result.code().executableAddress(), static_cast(result.code().executableAddress()) + result.size()); + if (!tryToDisassemble(result.code(), m_size, " ", WTF::dataFile())) + dataLog(" \n"); + + return result; +} + +void LinkBuffer::linkCode(void* ownerUID, JITCompilationEffort effort) +{ + ASSERT(!m_code); +#if !ENABLE(BRANCH_COMPACTION) + m_executableMemory = m_assembler->m_assembler.executableCopy(*m_globalData, ownerUID, effort); + if (!m_executableMemory) + return; + m_code = m_executableMemory->start(); + m_size = m_assembler->m_assembler.codeSize(); + ASSERT(m_code); +#else + m_initialSize = m_assembler->m_assembler.codeSize(); + m_executableMemory = m_globalData->executableAllocator.allocate(*m_globalData, m_initialSize, ownerUID, effort); + if (!m_executableMemory) + return; + m_code = (uint8_t*)m_executableMemory->start(); + ASSERT(m_code); + ExecutableAllocator::makeWritable(m_code, m_initialSize); + uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode(); + uint8_t* outData = reinterpret_cast(m_code); + int readPtr = 0; + int writePtr = 0; + Vector& jumpsToLink = m_assembler->jumpsToLink(); + unsigned jumpCount = jumpsToLink.size(); + for (unsigned i = 0; i < jumpCount; ++i) { + int offset = readPtr - writePtr; + ASSERT(!(offset & 1)); + + // Copy the instructions from the last jump to the current one. + size_t regionSize = jumpsToLink[i].from() - readPtr; + uint16_t* copySource = reinterpret_cast_ptr(inData + readPtr); + uint16_t* copyEnd = reinterpret_cast_ptr(inData + readPtr + regionSize); + uint16_t* copyDst = reinterpret_cast_ptr(outData + writePtr); + ASSERT(!(regionSize % 2)); + ASSERT(!(readPtr % 2)); + ASSERT(!(writePtr % 2)); + while (copySource != copyEnd) + *copyDst++ = *copySource++; + m_assembler->recordLinkOffsets(readPtr, jumpsToLink[i].from(), offset); + readPtr += regionSize; + writePtr += regionSize; + + // Calculate absolute address of the jump target, in the case of backwards + // branches we need to be precise, forward branches we are pessimistic + const uint8_t* target; + if (jumpsToLink[i].to() >= jumpsToLink[i].from()) + target = outData + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far + else + target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); + + JumpLinkType jumpLinkType = m_assembler->computeJumpType(jumpsToLink[i], outData + writePtr, target); + // Compact branch if we can... + if (m_assembler->canCompact(jumpsToLink[i].type())) { + // Step back in the write stream + int32_t delta = m_assembler->jumpSizeDelta(jumpsToLink[i].type(), jumpLinkType); + if (delta) { + writePtr -= delta; + m_assembler->recordLinkOffsets(jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr); + } + } + jumpsToLink[i].setFrom(writePtr); + } + // Copy everything after the last jump + memcpy(outData + writePtr, inData + readPtr, m_initialSize - readPtr); + m_assembler->recordLinkOffsets(readPtr, m_initialSize, readPtr - writePtr); + + for (unsigned i = 0; i < jumpCount; ++i) { + uint8_t* location = outData + jumpsToLink[i].from(); + uint8_t* target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); + m_assembler->link(jumpsToLink[i], location, target); + } + + jumpsToLink.clear(); + m_size = writePtr + m_initialSize - readPtr; + m_executableMemory->shrink(m_size); + +#if DUMP_LINK_STATISTICS + dumpLinkStatistics(m_code, m_initialSize, m_size); +#endif +#if DUMP_CODE + dumpCode(m_code, m_size); +#endif +#endif +} + +void LinkBuffer::performFinalization() +{ +#ifndef NDEBUG + ASSERT(!m_completed); + ASSERT(isValid()); + m_completed = true; +#endif + +#if ENABLE(BRANCH_COMPACTION) + ExecutableAllocator::makeExecutable(code(), m_initialSize); +#else + ExecutableAllocator::makeExecutable(code(), m_size); +#endif + MacroAssembler::cacheFlush(code(), m_size); +} + +#if DUMP_LINK_STATISTICS +void LinkBuffer::dumpLinkStatistics(void* code, size_t initializeSize, size_t finalSize) +{ + static unsigned linkCount = 0; + static unsigned totalInitialSize = 0; + static unsigned totalFinalSize = 0; + linkCount++; + totalInitialSize += initialSize; + totalFinalSize += finalSize; + dataLog("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", + code, static_cast(initialSize), static_cast(finalSize), + static_cast(initialSize - finalSize), + 100.0 * (initialSize - finalSize) / initialSize); + dataLog("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", + linkCount, totalInitialSize, totalFinalSize, totalInitialSize - totalFinalSize, + 100.0 * (totalInitialSize - totalFinalSize) / totalInitialSize); +} +#endif + +#if DUMP_CODE +void LinkBuffer::dumpCode(void* code, size_t size) +{ +#if CPU(ARM_THUMB2) + // Dump the generated code in an asm file format that can be assembled and then disassembled + // for debugging purposes. For example, save this output as jit.s: + // gcc -arch armv7 -c jit.s + // otool -tv jit.o + static unsigned codeCount = 0; + unsigned short* tcode = static_cast(code); + size_t tsize = size / sizeof(short); + char nameBuf[128]; + snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); + dataLog("\t.syntax unified\n" + "\t.section\t__TEXT,__text,regular,pure_instructions\n" + "\t.globl\t%s\n" + "\t.align 2\n" + "\t.code 16\n" + "\t.thumb_func\t%s\n" + "# %p\n" + "%s:\n", nameBuf, nameBuf, code, nameBuf); + + for (unsigned i = 0; i < tsize; i++) + dataLog("\t.short\t0x%x\n", tcode[i]); +#elif CPU(ARM_TRADITIONAL) + // gcc -c jit.s + // objdump -D jit.o + static unsigned codeCount = 0; + unsigned int* tcode = static_cast(code); + size_t tsize = size / sizeof(unsigned int); + char nameBuf[128]; + snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); + dataLog("\t.globl\t%s\n" + "\t.align 4\n" + "\t.code 32\n" + "\t.text\n" + "# %p\n" + "%s:\n", nameBuf, code, nameBuf); + + for (unsigned i = 0; i < tsize; i++) + dataLog("\t.long\t0x%x\n", tcode[i]); +#endif +} +#endif + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + + diff --git a/masm/assembler/LinkBuffer.h b/masm/assembler/LinkBuffer.h new file mode 100644 index 0000000000..484d3a73fb --- /dev/null +++ b/masm/assembler/LinkBuffer.h @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2009, 2010, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LinkBuffer_h +#define LinkBuffer_h + +#if ENABLE(ASSEMBLER) + +#define DUMP_LINK_STATISTICS 0 +#define DUMP_CODE 0 + +#define GLOBAL_THUNK_ID reinterpret_cast(static_cast(-1)) +#define REGEXP_CODE_ID reinterpret_cast(static_cast(-2)) + +#include "JITCompilationEffort.h" +#include "MacroAssembler.h" +#include +#include + +namespace JSC { + +class JSGlobalData; + +// LinkBuffer: +// +// This class assists in linking code generated by the macro assembler, once code generation +// has been completed, and the code has been copied to is final location in memory. At this +// time pointers to labels within the code may be resolved, and relative offsets to external +// addresses may be fixed. +// +// Specifically: +// * Jump objects may be linked to external targets, +// * The address of Jump objects may taken, such that it can later be relinked. +// * The return address of a Call may be acquired. +// * The address of a Label pointing into the code may be resolved. +// * The value referenced by a DataLabel may be set. +// +class LinkBuffer { + WTF_MAKE_NONCOPYABLE(LinkBuffer); + typedef MacroAssemblerCodeRef CodeRef; + typedef MacroAssemblerCodePtr CodePtr; + typedef MacroAssembler::Label Label; + typedef MacroAssembler::Jump Jump; + typedef MacroAssembler::PatchableJump PatchableJump; + typedef MacroAssembler::JumpList JumpList; + typedef MacroAssembler::Call Call; + typedef MacroAssembler::DataLabelCompact DataLabelCompact; + typedef MacroAssembler::DataLabel32 DataLabel32; + typedef MacroAssembler::DataLabelPtr DataLabelPtr; + typedef MacroAssembler::ConvertibleLoadLabel ConvertibleLoadLabel; +#if ENABLE(BRANCH_COMPACTION) + typedef MacroAssembler::LinkRecord LinkRecord; + typedef MacroAssembler::JumpLinkType JumpLinkType; +#endif + +public: + LinkBuffer(JSGlobalData& globalData, MacroAssembler* masm, void* ownerUID, JITCompilationEffort effort = JITCompilationMustSucceed) + : m_size(0) +#if ENABLE(BRANCH_COMPACTION) + , m_initialSize(0) +#endif + , m_code(0) + , m_assembler(masm) + , m_globalData(&globalData) +#ifndef NDEBUG + , m_completed(false) + , m_effort(effort) +#endif + { + linkCode(ownerUID, effort); + } + + ~LinkBuffer() + { + ASSERT(m_completed || (!m_executableMemory && m_effort == JITCompilationCanFail)); + } + + bool didFailToAllocate() const + { + return !m_executableMemory; + } + + bool isValid() const + { + return !didFailToAllocate(); + } + + // These methods are used to link or set values at code generation time. + + void link(Call call, FunctionPtr function) + { + ASSERT(call.isFlagSet(Call::Linkable)); + call.m_label = applyOffset(call.m_label); + MacroAssembler::linkCall(code(), call, function); + } + + void link(Jump jump, CodeLocationLabel label) + { + jump.m_label = applyOffset(jump.m_label); + MacroAssembler::linkJump(code(), jump, label); + } + + void link(JumpList list, CodeLocationLabel label) + { + for (unsigned i = 0; i < list.m_jumps.size(); ++i) + link(list.m_jumps[i], label); + } + + void patch(DataLabelPtr label, void* value) + { + AssemblerLabel target = applyOffset(label.m_label); + MacroAssembler::linkPointer(code(), target, value); + } + + void patch(DataLabelPtr label, CodeLocationLabel value) + { + AssemblerLabel target = applyOffset(label.m_label); + MacroAssembler::linkPointer(code(), target, value.executableAddress()); + } + + // These methods are used to obtain handles to allow the code to be relinked / repatched later. + + CodeLocationCall locationOf(Call call) + { + ASSERT(call.isFlagSet(Call::Linkable)); + ASSERT(!call.isFlagSet(Call::Near)); + return CodeLocationCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_label))); + } + + CodeLocationNearCall locationOfNearCall(Call call) + { + ASSERT(call.isFlagSet(Call::Linkable)); + ASSERT(call.isFlagSet(Call::Near)); + return CodeLocationNearCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_label))); + } + + CodeLocationLabel locationOf(PatchableJump jump) + { + return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(jump.m_jump.m_label))); + } + + CodeLocationLabel locationOf(Label label) + { + return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + CodeLocationDataLabelPtr locationOf(DataLabelPtr label) + { + return CodeLocationDataLabelPtr(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + CodeLocationDataLabel32 locationOf(DataLabel32 label) + { + return CodeLocationDataLabel32(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + CodeLocationDataLabelCompact locationOf(DataLabelCompact label) + { + return CodeLocationDataLabelCompact(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + CodeLocationConvertibleLoad locationOf(ConvertibleLoadLabel label) + { + return CodeLocationConvertibleLoad(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + // This method obtains the return address of the call, given as an offset from + // the start of the code. + unsigned returnAddressOffset(Call call) + { + call.m_label = applyOffset(call.m_label); + return MacroAssembler::getLinkerCallReturnOffset(call); + } + + uint32_t offsetOf(Label label) + { + return applyOffset(label.m_label).m_offset; + } + + // Upon completion of all patching 'FINALIZE_CODE()' should be called once to + // complete generation of the code. Alternatively, call + // finalizeCodeWithoutDisassembly() directly if you have your own way of + // displaying disassembly. + + CodeRef finalizeCodeWithoutDisassembly(); + CodeRef finalizeCodeWithDisassembly(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); + + CodePtr trampolineAt(Label label) + { + return CodePtr(MacroAssembler::AssemblerType_T::getRelocatedAddress(code(), applyOffset(label.m_label))); + } + + void* debugAddress() + { + return m_code; + } + + size_t debugSize() + { + return m_size; + } + +private: + template T applyOffset(T src) + { +#if ENABLE(BRANCH_COMPACTION) + src.m_offset -= m_assembler->executableOffsetFor(src.m_offset); +#endif + return src; + } + + // Keep this private! - the underlying code should only be obtained externally via finalizeCode(). + void* code() + { + return m_code; + } + + void linkCode(void* ownerUID, JITCompilationEffort); + + void performFinalization(); + +#if DUMP_LINK_STATISTICS + static void dumpLinkStatistics(void* code, size_t initialSize, size_t finalSize); +#endif + +#if DUMP_CODE + static void dumpCode(void* code, size_t); +#endif + + RefPtr m_executableMemory; + size_t m_size; +#if ENABLE(BRANCH_COMPACTION) + size_t m_initialSize; +#endif + void* m_code; + MacroAssembler* m_assembler; + JSGlobalData* m_globalData; +#ifndef NDEBUG + bool m_completed; + JITCompilationEffort m_effort; +#endif +}; + +#define FINALIZE_CODE_IF(condition, linkBufferReference, dataLogArgumentsForHeading) \ + (UNLIKELY((condition)) \ + ? ((linkBufferReference).finalizeCodeWithDisassembly dataLogArgumentsForHeading) \ + : (linkBufferReference).finalizeCodeWithoutDisassembly()) + +// Use this to finalize code, like so: +// +// CodeRef code = FINALIZE_CODE(linkBuffer, ("my super thingy number %d", number)); +// +// Which, in disassembly mode, will print: +// +// Generated JIT code for my super thingy number 42: +// Code at [0x123456, 0x234567]: +// 0x123456: mov $0, 0 +// 0x12345a: ret +// +// ... and so on. +// +// Note that the dataLogArgumentsForHeading are only evaluated when showDisassembly +// is true, so you can hide expensive disassembly-only computations inside there. + +#define FINALIZE_CODE(linkBufferReference, dataLogArgumentsForHeading) \ + FINALIZE_CODE_IF(Options::showDisassembly(), linkBufferReference, dataLogArgumentsForHeading) + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // LinkBuffer_h diff --git a/masm/assembler/MIPSAssembler.h b/masm/assembler/MIPSAssembler.h new file mode 100644 index 0000000000..65307d9508 --- /dev/null +++ b/masm/assembler/MIPSAssembler.h @@ -0,0 +1,997 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 University of Szeged + * All rights reserved. + * Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY MIPS TECHNOLOGIES, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MIPS TECHNOLOGIES, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MIPSAssembler_h +#define MIPSAssembler_h + +#if ENABLE(ASSEMBLER) && CPU(MIPS) + +#include "AssemblerBuffer.h" +#include "JITCompilationEffort.h" +#include +#include + +namespace JSC { + +typedef uint32_t MIPSWord; + +namespace MIPSRegisters { +typedef enum { + r0 = 0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, + r16, + r17, + r18, + r19, + r20, + r21, + r22, + r23, + r24, + r25, + r26, + r27, + r28, + r29, + r30, + r31, + zero = r0, + at = r1, + v0 = r2, + v1 = r3, + a0 = r4, + a1 = r5, + a2 = r6, + a3 = r7, + t0 = r8, + t1 = r9, + t2 = r10, + t3 = r11, + t4 = r12, + t5 = r13, + t6 = r14, + t7 = r15, + s0 = r16, + s1 = r17, + s2 = r18, + s3 = r19, + s4 = r20, + s5 = r21, + s6 = r22, + s7 = r23, + t8 = r24, + t9 = r25, + k0 = r26, + k1 = r27, + gp = r28, + sp = r29, + fp = r30, + ra = r31 +} RegisterID; + +typedef enum { + f0, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + f22, + f23, + f24, + f25, + f26, + f27, + f28, + f29, + f30, + f31 +} FPRegisterID; + +} // namespace MIPSRegisters + +class MIPSAssembler { +public: + typedef MIPSRegisters::RegisterID RegisterID; + typedef MIPSRegisters::FPRegisterID FPRegisterID; + typedef SegmentedVector Jumps; + + MIPSAssembler() + { + } + + // MIPS instruction opcode field position + enum { + OP_SH_RD = 11, + OP_SH_RT = 16, + OP_SH_RS = 21, + OP_SH_SHAMT = 6, + OP_SH_CODE = 16, + OP_SH_FD = 6, + OP_SH_FS = 11, + OP_SH_FT = 16 + }; + + void emitInst(MIPSWord op) + { + void* oldBase = m_buffer.data(); + + m_buffer.putInt(op); + + void* newBase = m_buffer.data(); + if (oldBase != newBase) + relocateJumps(oldBase, newBase); + } + + void nop() + { + emitInst(0x00000000); + } + + /* Need to insert one load data delay nop for mips1. */ + void loadDelayNop() + { +#if WTF_MIPS_ISA(1) + nop(); +#endif + } + + /* Need to insert one coprocessor access delay nop for mips1. */ + void copDelayNop() + { +#if WTF_MIPS_ISA(1) + nop(); +#endif + } + + void move(RegisterID rd, RegisterID rs) + { + /* addu */ + emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS)); + } + + /* Set an immediate value to a register. This may generate 1 or 2 + instructions. */ + void li(RegisterID dest, int imm) + { + if (imm >= -32768 && imm <= 32767) + addiu(dest, MIPSRegisters::zero, imm); + else if (imm >= 0 && imm < 65536) + ori(dest, MIPSRegisters::zero, imm); + else { + lui(dest, imm >> 16); + if (imm & 0xffff) + ori(dest, dest, imm); + } + } + + void lui(RegisterID rt, int imm) + { + emitInst(0x3c000000 | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void addiu(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x24000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void addu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void subu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000023 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void mult(RegisterID rs, RegisterID rt) + { + emitInst(0x00000018 | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void div(RegisterID rs, RegisterID rt) + { + emitInst(0x0000001a | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void mfhi(RegisterID rd) + { + emitInst(0x00000010 | (rd << OP_SH_RD)); + } + + void mflo(RegisterID rd) + { + emitInst(0x00000012 | (rd << OP_SH_RD)); + } + + void mul(RegisterID rd, RegisterID rs, RegisterID rt) + { +#if WTF_MIPS_ISA_AT_LEAST(32) + emitInst(0x70000002 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); +#else + mult(rs, rt); + mflo(rd); +#endif + } + + void andInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000024 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void andi(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x30000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void nor(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000027 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void orInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000025 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void ori(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x34000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void xorInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000026 | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void xori(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x38000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void slt(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x0000002a | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void sltu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x0000002b | (rd << OP_SH_RD) | (rs << OP_SH_RS) + | (rt << OP_SH_RT)); + } + + void sltiu(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x2c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (imm & 0xffff)); + } + + void sll(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000000 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void sllv(RegisterID rd, RegisterID rt, int rs) + { + emitInst(0x00000004 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | (rs << OP_SH_RS)); + } + + void sra(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000003 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void srav(RegisterID rd, RegisterID rt, RegisterID rs) + { + emitInst(0x00000007 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | (rs << OP_SH_RS)); + } + + void srl(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000002 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void srlv(RegisterID rd, RegisterID rt, RegisterID rs) + { + emitInst(0x00000006 | (rd << OP_SH_RD) | (rt << OP_SH_RT) + | (rs << OP_SH_RS)); + } + + void lbu(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x90000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void lw(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x8c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void lwl(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x88000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void lwr(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x98000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void lhu(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x94000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + loadDelayNop(); + } + + void sw(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0xac000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + } + + void jr(RegisterID rs) + { + emitInst(0x00000008 | (rs << OP_SH_RS)); + } + + void jalr(RegisterID rs) + { + emitInst(0x0000f809 | (rs << OP_SH_RS)); + } + + void jal() + { + emitInst(0x0c000000); + } + + void bkpt() + { + int value = 512; /* BRK_BUG */ + emitInst(0x0000000d | ((value & 0x3ff) << OP_SH_CODE)); + } + + void bgez(RegisterID rs, int imm) + { + emitInst(0x04010000 | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void bltz(RegisterID rs, int imm) + { + emitInst(0x04000000 | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void beq(RegisterID rs, RegisterID rt, int imm) + { + emitInst(0x10000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void bne(RegisterID rs, RegisterID rt, int imm) + { + emitInst(0x14000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void bc1t() + { + emitInst(0x45010000); + } + + void bc1f() + { + emitInst(0x45000000); + } + + void appendJump() + { + m_jumps.append(m_buffer.label()); + } + + void addd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200000 | (fd << OP_SH_FD) | (fs << OP_SH_FS) + | (ft << OP_SH_FT)); + } + + void subd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200001 | (fd << OP_SH_FD) | (fs << OP_SH_FS) + | (ft << OP_SH_FT)); + } + + void muld(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200002 | (fd << OP_SH_FD) | (fs << OP_SH_FS) + | (ft << OP_SH_FT)); + } + + void divd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200003 | (fd << OP_SH_FD) | (fs << OP_SH_FS) + | (ft << OP_SH_FT)); + } + + void lwc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xc4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + copDelayNop(); + } + + void ldc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xd4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + } + + void swc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xe4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + } + + void sdc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xf4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) + | (offset & 0xffff)); + } + + void mtc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44800000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void mthc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44e00000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void mfc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44000000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void sqrtd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200004 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void truncwd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x4620000d | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtdw(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46800021 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtwd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200024 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void ceqd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200032 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cngtd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003f | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cnged(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003d | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cltd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003c | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cled(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003e | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cueqd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200033 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void coled(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200036 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void coltd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200034 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void culed(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200037 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cultd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200035 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + // General helpers + + AssemblerLabel labelIgnoringWatchpoints() + { + return m_buffer.label(); + } + + AssemblerLabel label() + { + return m_buffer.label(); + } + + AssemblerLabel align(int alignment) + { + while (!m_buffer.isAligned(alignment)) + bkpt(); + + return label(); + } + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + // Assembler admin methods: + + size_t codeSize() const + { + return m_buffer.codeSize(); + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + RefPtr result = m_buffer.executableCopy(globalData, ownerUID, effort); + if (!result) + return 0; + + relocateJumps(m_buffer.data(), result->start()); + return result.release(); + } + + unsigned debugOffset() { return m_buffer.debugOffset(); } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + // The return address is after a call and a delay slot instruction + return call.m_offset; + } + + // Linking & patching: + // + // 'link' and 'patch' methods are for use on unprotected code - such as the code + // within the AssemblerBuffer, and code being patched by the patch buffer. Once + // code has been finalized it is (platform support permitting) within a non- + // writable region of memory; to modify the code in an execute-only execuable + // pool the 'repatch' and 'relink' methods should be used. + + void linkJump(AssemblerLabel from, AssemblerLabel to) + { + ASSERT(to.isSet()); + ASSERT(from.isSet()); + MIPSWord* insn = reinterpret_cast(reinterpret_cast(m_buffer.data()) + from.m_offset); + MIPSWord* toPos = reinterpret_cast(reinterpret_cast(m_buffer.data()) + to.m_offset); + + ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5))); + insn = insn - 6; + linkWithOffset(insn, toPos); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + + ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5))); + insn = insn - 6; + linkWithOffset(insn, to); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + linkCallInternal(insn, to); + } + + static void linkPointer(void* code, AssemblerLabel from, void* to) + { + MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + *insn = (*insn & 0xffff0000) | ((reinterpret_cast(to) >> 16) & 0xffff); + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + *insn = (*insn & 0xffff0000) | (reinterpret_cast(to) & 0xffff); + } + + static void relinkJump(void* from, void* to) + { + MIPSWord* insn = reinterpret_cast(from); + + ASSERT(!(*(insn - 1)) && !(*(insn - 5))); + insn = insn - 6; + int flushSize = linkWithOffset(insn, to); + + cacheFlush(insn, flushSize); + } + + static void relinkCall(void* from, void* to) + { + void* start; + int size = linkCallInternal(from, to); + if (size == sizeof(MIPSWord)) + start = reinterpret_cast(reinterpret_cast(from) - 2 * sizeof(MIPSWord)); + else + start = reinterpret_cast(reinterpret_cast(from) - 4 * sizeof(MIPSWord)); + + cacheFlush(start, size); + } + + static void repatchInt32(void* from, int32_t to) + { + MIPSWord* insn = reinterpret_cast(from); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + *insn = (*insn & 0xffff0000) | ((to >> 16) & 0xffff); + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + *insn = (*insn & 0xffff0000) | (to & 0xffff); + insn--; + cacheFlush(insn, 2 * sizeof(MIPSWord)); + } + + static int32_t readInt32(void* from) + { + MIPSWord* insn = reinterpret_cast(from); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + int32_t result = (*insn & 0x0000ffff) << 16; + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + result |= *insn & 0x0000ffff; + return result; + } + + static void repatchCompact(void* where, int32_t value) + { + repatchInt32(where, value); + } + + static void repatchPointer(void* from, void* to) + { + repatchInt32(from, reinterpret_cast(to)); + } + + static void* readPointer(void* from) + { + return reinterpret_cast(readInt32(from)); + } + + static void* readCallTarget(void* from) + { + MIPSWord* insn = reinterpret_cast(from); + insn -= 4; + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + int32_t result = (*insn & 0x0000ffff) << 16; + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + result |= *insn & 0x0000ffff; + return reinterpret_cast(result); + } + + static void cacheFlush(void* code, size_t size) + { +#if GCC_VERSION_AT_LEAST(4, 3, 0) +#if WTF_MIPS_ISA_REV(2) && !GCC_VERSION_AT_LEAST(4, 4, 3) + int lineSize; + asm("rdhwr %0, $1" : "=r" (lineSize)); + // + // Modify "start" and "end" to avoid GCC 4.3.0-4.4.2 bug in + // mips_expand_synci_loop that may execute synci one more time. + // "start" points to the fisrt byte of the cache line. + // "end" points to the last byte of the line before the last cache line. + // Because size is always a multiple of 4, this is safe to set + // "end" to the last byte. + // + intptr_t start = reinterpret_cast(code) & (-lineSize); + intptr_t end = ((reinterpret_cast(code) + size - 1) & (-lineSize)) - 1; + __builtin___clear_cache(reinterpret_cast(start), reinterpret_cast(end)); +#else + intptr_t end = reinterpret_cast(code) + size; + __builtin___clear_cache(reinterpret_cast(code), reinterpret_cast(end)); +#endif +#else + _flush_cache(reinterpret_cast(code), size, BCACHE); +#endif + } + + static void replaceWithLoad(void* instructionStart) + { + MIPSWord* insn = reinterpret_cast(instructionStart); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + insn++; + ASSERT((*insn & 0xfc0007ff) == 0x00000021); // addu + insn++; + *insn = 0x8c000000 | ((*insn) & 0x3ffffff); // lw + cacheFlush(insn, 4); + } + + static void replaceWithAddressComputation(void* instructionStart) + { + MIPSWord* insn = reinterpret_cast(instructionStart); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + insn++; + ASSERT((*insn & 0xfc0007ff) == 0x00000021); // addu + insn++; + *insn = 0x24000000 | ((*insn) & 0x3ffffff); // addiu + cacheFlush(insn, 4); + } + +private: + /* Update each jump in the buffer of newBase. */ + void relocateJumps(void* oldBase, void* newBase) + { + // Check each jump + for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) { + int pos = iter->m_offset; + MIPSWord* insn = reinterpret_cast(reinterpret_cast(newBase) + pos); + insn = insn + 2; + // Need to make sure we have 5 valid instructions after pos + if ((unsigned int)pos >= m_buffer.codeSize() - 5 * sizeof(MIPSWord)) + continue; + + if ((*insn & 0xfc000000) == 0x08000000) { // j + int offset = *insn & 0x03ffffff; + int oldInsnAddress = (int)insn - (int)newBase + (int)oldBase; + int topFourBits = (oldInsnAddress + 4) >> 28; + int oldTargetAddress = (topFourBits << 28) | (offset << 2); + int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase; + int newInsnAddress = (int)insn; + if (((newInsnAddress + 4) >> 28) == (newTargetAddress >> 28)) + *insn = 0x08000000 | ((newTargetAddress >> 2) & 0x3ffffff); + else { + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + /* jr */ + *(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); + } + } else if ((*insn & 0xffe00000) == 0x3c000000) { // lui + int high = (*insn & 0xffff) << 16; + int low = *(insn + 1) & 0xffff; + int oldTargetAddress = high | low; + int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase; + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + } + } + } + + static int linkWithOffset(MIPSWord* insn, void* to) + { + ASSERT((*insn & 0xfc000000) == 0x10000000 // beq + || (*insn & 0xfc000000) == 0x14000000 // bne + || (*insn & 0xffff0000) == 0x45010000 // bc1t + || (*insn & 0xffff0000) == 0x45000000); // bc1f + intptr_t diff = (reinterpret_cast(to) + - reinterpret_cast(insn) - 4) >> 2; + + if (diff < -32768 || diff > 32767 || *(insn + 2) != 0x10000003) { + /* + Convert the sequence: + beq $2, $3, target + nop + b 1f + nop + nop + nop + 1: + + to the new sequence if possible: + bne $2, $3, 1f + nop + j target + nop + nop + nop + 1: + + OR to the new sequence: + bne $2, $3, 1f + nop + lui $25, target >> 16 + ori $25, $25, target & 0xffff + jr $25 + nop + 1: + + Note: beq/bne/bc1t/bc1f are converted to bne/beq/bc1f/bc1t. + */ + + if (*(insn + 2) == 0x10000003) { + if ((*insn & 0xfc000000) == 0x10000000) // beq + *insn = (*insn & 0x03ff0000) | 0x14000005; // bne + else if ((*insn & 0xfc000000) == 0x14000000) // bne + *insn = (*insn & 0x03ff0000) | 0x10000005; // beq + else if ((*insn & 0xffff0000) == 0x45010000) // bc1t + *insn = 0x45000005; // bc1f + else if ((*insn & 0xffff0000) == 0x45000000) // bc1f + *insn = 0x45010005; // bc1t + else + ASSERT(0); + } + + insn = insn + 2; + if ((reinterpret_cast(insn) + 4) >> 28 + == reinterpret_cast(to) >> 28) { + *insn = 0x08000000 | ((reinterpret_cast(to) >> 2) & 0x3ffffff); + *(insn + 1) = 0; + return 4 * sizeof(MIPSWord); + } + + intptr_t newTargetAddress = reinterpret_cast(to); + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + /* jr */ + *(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); + return 5 * sizeof(MIPSWord); + } + + *insn = (*insn & 0xffff0000) | (diff & 0xffff); + return sizeof(MIPSWord); + } + + static int linkCallInternal(void* from, void* to) + { + MIPSWord* insn = reinterpret_cast(from); + insn = insn - 4; + + if ((*(insn + 2) & 0xfc000000) == 0x0c000000) { // jal + if ((reinterpret_cast(from) - 4) >> 28 + == reinterpret_cast(to) >> 28) { + *(insn + 2) = 0x0c000000 | ((reinterpret_cast(to) >> 2) & 0x3ffffff); + return sizeof(MIPSWord); + } + + /* lui $25, (to >> 16) & 0xffff */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((reinterpret_cast(to) >> 16) & 0xffff); + /* ori $25, $25, to & 0xffff */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (reinterpret_cast(to) & 0xffff); + /* jalr $25 */ + *(insn + 2) = 0x0000f809 | (MIPSRegisters::t9 << OP_SH_RS); + return 3 * sizeof(MIPSWord); + } + + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + ASSERT((*(insn + 1) & 0xfc000000) == 0x34000000); // ori + + /* lui */ + *insn = (*insn & 0xffff0000) | ((reinterpret_cast(to) >> 16) & 0xffff); + /* ori */ + *(insn + 1) = (*(insn + 1) & 0xffff0000) | (reinterpret_cast(to) & 0xffff); + return 2 * sizeof(MIPSWord); + } + + AssemblerBuffer m_buffer; + Jumps m_jumps; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(MIPS) + +#endif // MIPSAssembler_h diff --git a/masm/assembler/MacroAssembler.h b/masm/assembler/MacroAssembler.h new file mode 100644 index 0000000000..1a9af2989b --- /dev/null +++ b/masm/assembler/MacroAssembler.h @@ -0,0 +1,1046 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssembler_h +#define MacroAssembler_h + +#if ENABLE(ASSEMBLER) + +#if CPU(ARM_THUMB2) +#include "MacroAssemblerARMv7.h" +namespace JSC { typedef MacroAssemblerARMv7 MacroAssemblerBase; }; + +#elif CPU(ARM_TRADITIONAL) +#include "MacroAssemblerARM.h" +namespace JSC { typedef MacroAssemblerARM MacroAssemblerBase; }; + +#elif CPU(MIPS) +#include "MacroAssemblerMIPS.h" +namespace JSC { +typedef MacroAssemblerMIPS MacroAssemblerBase; +}; + +#elif CPU(X86) +#include "MacroAssemblerX86.h" +namespace JSC { typedef MacroAssemblerX86 MacroAssemblerBase; }; + +#elif CPU(X86_64) +#include "MacroAssemblerX86_64.h" +namespace JSC { typedef MacroAssemblerX86_64 MacroAssemblerBase; }; + +#elif CPU(SH4) +#include "MacroAssemblerSH4.h" +namespace JSC { +typedef MacroAssemblerSH4 MacroAssemblerBase; +}; + +#else +#error "The MacroAssembler is not supported on this platform." +#endif + +namespace JSC { + +class MacroAssembler : public MacroAssemblerBase { +public: + + using MacroAssemblerBase::pop; + using MacroAssemblerBase::jump; + using MacroAssemblerBase::branch32; +#if CPU(X86_64) + using MacroAssemblerBase::branchPtr; + using MacroAssemblerBase::branchTestPtr; +#endif + using MacroAssemblerBase::move; + +#if ENABLE(JIT_CONSTANT_BLINDING) + using MacroAssemblerBase::add32; + using MacroAssemblerBase::and32; + using MacroAssemblerBase::branchAdd32; + using MacroAssemblerBase::branchMul32; + using MacroAssemblerBase::branchSub32; + using MacroAssemblerBase::lshift32; + using MacroAssemblerBase::or32; + using MacroAssemblerBase::rshift32; + using MacroAssemblerBase::store32; + using MacroAssemblerBase::sub32; + using MacroAssemblerBase::urshift32; + using MacroAssemblerBase::xor32; +#endif + + // Utilities used by the DFG JIT. +#if ENABLE(DFG_JIT) + using MacroAssemblerBase::invert; + + static DoubleCondition invert(DoubleCondition cond) + { + switch (cond) { + case DoubleEqual: + return DoubleNotEqualOrUnordered; + case DoubleNotEqual: + return DoubleEqualOrUnordered; + case DoubleGreaterThan: + return DoubleLessThanOrEqualOrUnordered; + case DoubleGreaterThanOrEqual: + return DoubleLessThanOrUnordered; + case DoubleLessThan: + return DoubleGreaterThanOrEqualOrUnordered; + case DoubleLessThanOrEqual: + return DoubleGreaterThanOrUnordered; + case DoubleEqualOrUnordered: + return DoubleNotEqual; + case DoubleNotEqualOrUnordered: + return DoubleEqual; + case DoubleGreaterThanOrUnordered: + return DoubleLessThanOrEqual; + case DoubleGreaterThanOrEqualOrUnordered: + return DoubleLessThan; + case DoubleLessThanOrUnordered: + return DoubleGreaterThanOrEqual; + case DoubleLessThanOrEqualOrUnordered: + return DoubleGreaterThan; + default: + ASSERT_NOT_REACHED(); + return DoubleEqual; // make compiler happy + } + } + + static bool isInvertible(ResultCondition cond) + { + switch (cond) { + case Zero: + case NonZero: + return true; + default: + return false; + } + } + + static ResultCondition invert(ResultCondition cond) + { + switch (cond) { + case Zero: + return NonZero; + case NonZero: + return Zero; + default: + ASSERT_NOT_REACHED(); + return Zero; // Make compiler happy for release builds. + } + } +#endif + + // Platform agnostic onvenience functions, + // described in terms of other macro assembly methods. + void pop() + { + addPtr(TrustedImm32(sizeof(void*)), stackPointerRegister); + } + + void peek(RegisterID dest, int index = 0) + { + loadPtr(Address(stackPointerRegister, (index * sizeof(void*))), dest); + } + + Address addressForPoke(int index) + { + return Address(stackPointerRegister, (index * sizeof(void*))); + } + + void poke(RegisterID src, int index = 0) + { + storePtr(src, addressForPoke(index)); + } + + void poke(TrustedImm32 value, int index = 0) + { + store32(value, addressForPoke(index)); + } + + void poke(TrustedImmPtr imm, int index = 0) + { + storePtr(imm, addressForPoke(index)); + } + + + // Backwards banches, these are currently all implemented using existing forwards branch mechanisms. + void branchPtr(RelationalCondition cond, RegisterID op1, TrustedImmPtr imm, Label target) + { + branchPtr(cond, op1, imm).linkTo(target, this); + } + void branchPtr(RelationalCondition cond, RegisterID op1, ImmPtr imm, Label target) + { + branchPtr(cond, op1, imm).linkTo(target, this); + } + + void branch32(RelationalCondition cond, RegisterID op1, RegisterID op2, Label target) + { + branch32(cond, op1, op2).linkTo(target, this); + } + + void branch32(RelationalCondition cond, RegisterID op1, TrustedImm32 imm, Label target) + { + branch32(cond, op1, imm).linkTo(target, this); + } + + void branch32(RelationalCondition cond, RegisterID op1, Imm32 imm, Label target) + { + branch32(cond, op1, imm).linkTo(target, this); + } + + void branch32(RelationalCondition cond, RegisterID left, Address right, Label target) + { + branch32(cond, left, right).linkTo(target, this); + } + + Jump branch32(RelationalCondition cond, TrustedImm32 left, RegisterID right) + { + return branch32(commute(cond), right, left); + } + + Jump branch32(RelationalCondition cond, Imm32 left, RegisterID right) + { + return branch32(commute(cond), right, left); + } + + void branchTestPtr(ResultCondition cond, RegisterID reg, Label target) + { + branchTestPtr(cond, reg).linkTo(target, this); + } + +#if !CPU(ARM_THUMB2) + PatchableJump patchableBranchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + return PatchableJump(branchPtrWithPatch(cond, left, dataLabel, initialRightValue)); + } + + PatchableJump patchableJump() + { + return PatchableJump(jump()); + } +#endif + + void jump(Label target) + { + jump().linkTo(target, this); + } + + // Commute a relational condition, returns a new condition that will produce + // the same results given the same inputs but with their positions exchanged. + static RelationalCondition commute(RelationalCondition condition) + { + switch (condition) { + case Above: + return Below; + case AboveOrEqual: + return BelowOrEqual; + case Below: + return Above; + case BelowOrEqual: + return AboveOrEqual; + case GreaterThan: + return LessThan; + case GreaterThanOrEqual: + return LessThanOrEqual; + case LessThan: + return GreaterThan; + case LessThanOrEqual: + return GreaterThanOrEqual; + default: + break; + } + + ASSERT(condition == Equal || condition == NotEqual); + return condition; + } + + + // Ptr methods + // On 32-bit platforms (i.e. x86), these methods directly map onto their 32-bit equivalents. + // FIXME: should this use a test for 32-bitness instead of this specific exception? +#if !CPU(X86_64) + void addPtr(Address src, RegisterID dest) + { + add32(src, dest); + } + + void addPtr(AbsoluteAddress src, RegisterID dest) + { + add32(src, dest); + } + + void addPtr(RegisterID src, RegisterID dest) + { + add32(src, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID srcDest) + { + add32(imm, srcDest); + } + + void addPtr(TrustedImmPtr imm, RegisterID dest) + { + add32(TrustedImm32(imm), dest); + } + + void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + add32(imm, src, dest); + } + + void addPtr(TrustedImm32 imm, AbsoluteAddress address) + { + add32(imm, address); + } + + void andPtr(RegisterID src, RegisterID dest) + { + and32(src, dest); + } + + void andPtr(TrustedImm32 imm, RegisterID srcDest) + { + and32(imm, srcDest); + } + + void negPtr(RegisterID dest) + { + neg32(dest); + } + + void orPtr(RegisterID src, RegisterID dest) + { + or32(src, dest); + } + + void orPtr(RegisterID op1, RegisterID op2, RegisterID dest) + { + or32(op1, op2, dest); + } + + void orPtr(TrustedImmPtr imm, RegisterID dest) + { + or32(TrustedImm32(imm), dest); + } + + void orPtr(TrustedImm32 imm, RegisterID dest) + { + or32(imm, dest); + } + + void subPtr(RegisterID src, RegisterID dest) + { + sub32(src, dest); + } + + void subPtr(TrustedImm32 imm, RegisterID dest) + { + sub32(imm, dest); + } + + void subPtr(TrustedImmPtr imm, RegisterID dest) + { + sub32(TrustedImm32(imm), dest); + } + + void xorPtr(RegisterID src, RegisterID dest) + { + xor32(src, dest); + } + + void xorPtr(TrustedImm32 imm, RegisterID srcDest) + { + xor32(imm, srcDest); + } + + + void loadPtr(ImplicitAddress address, RegisterID dest) + { + load32(address, dest); + } + + void loadPtr(BaseIndex address, RegisterID dest) + { + load32(address, dest); + } + + void loadPtr(const void* address, RegisterID dest) + { + load32(address, dest); + } + + DataLabel32 loadPtrWithAddressOffsetPatch(Address address, RegisterID dest) + { + return load32WithAddressOffsetPatch(address, dest); + } + + DataLabelCompact loadPtrWithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + return load32WithCompactAddressOffsetPatch(address, dest); + } + + void move(ImmPtr imm, RegisterID dest) + { + move(Imm32(imm.asTrustedImmPtr()), dest); + } + + void comparePtr(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + compare32(cond, left, right, dest); + } + + void storePtr(RegisterID src, ImplicitAddress address) + { + store32(src, address); + } + + void storePtr(RegisterID src, BaseIndex address) + { + store32(src, address); + } + + void storePtr(RegisterID src, void* address) + { + store32(src, address); + } + + void storePtr(TrustedImmPtr imm, ImplicitAddress address) + { + store32(TrustedImm32(imm), address); + } + + void storePtr(ImmPtr imm, Address address) + { + store32(Imm32(imm.asTrustedImmPtr()), address); + } + + void storePtr(TrustedImmPtr imm, void* address) + { + store32(TrustedImm32(imm), address); + } + + DataLabel32 storePtrWithAddressOffsetPatch(RegisterID src, Address address) + { + return store32WithAddressOffsetPatch(src, address); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, RegisterID right) + { + return branch32(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, TrustedImmPtr right) + { + return branch32(cond, left, TrustedImm32(right)); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, ImmPtr right) + { + return branch32(cond, left, Imm32(right.asTrustedImmPtr())); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, Address right) + { + return branch32(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, Address left, RegisterID right) + { + return branch32(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + return branch32(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, Address left, TrustedImmPtr right) + { + return branch32(cond, left, TrustedImm32(right)); + } + + Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, TrustedImmPtr right) + { + return branch32(cond, left, TrustedImm32(right)); + } + + Jump branchTestPtr(ResultCondition cond, RegisterID reg, RegisterID mask) + { + return branchTest32(cond, reg, mask); + } + + Jump branchTestPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest32(cond, reg, mask); + } + + Jump branchTestPtr(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest32(cond, address, mask); + } + + Jump branchTestPtr(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest32(cond, address, mask); + } + + Jump branchAddPtr(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchAdd32(cond, src, dest); + } + + Jump branchSubPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchSub32(cond, imm, dest); + } + using MacroAssemblerBase::branchTest8; + Jump branchTest8(ResultCondition cond, ExtendedAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + return MacroAssemblerBase::branchTest8(cond, Address(address.base, address.offset), mask); + } +#else + +#if ENABLE(JIT_CONSTANT_BLINDING) + using MacroAssemblerBase::addPtr; + using MacroAssemblerBase::andPtr; + using MacroAssemblerBase::branchSubPtr; + using MacroAssemblerBase::convertInt32ToDouble; + using MacroAssemblerBase::storePtr; + using MacroAssemblerBase::subPtr; + using MacroAssemblerBase::xorPtr; + + bool shouldBlindDouble(double value) + { + // Don't trust NaN or +/-Infinity + if (!isfinite(value)) + return true; + + // Try to force normalisation, and check that there's no change + // in the bit pattern + if (bitwise_cast(value * 1.0) != bitwise_cast(value)) + return true; + + value = abs(value); + // Only allow a limited set of fractional components + double scaledValue = value * 8; + if (scaledValue / 8 != value) + return true; + double frac = scaledValue - floor(scaledValue); + if (frac != 0.0) + return true; + + return value > 0xff; + } + + bool shouldBlind(ImmPtr imm) + { +#if !defined(NDEBUG) + UNUSED_PARAM(imm); + // Debug always blind all constants, if only so we know + // if we've broken blinding during patch development. + return true; +#endif + + // First off we'll special case common, "safe" values to avoid hurting + // performance too much + uintptr_t value = imm.asTrustedImmPtr().asIntptr(); + switch (value) { + case 0xffff: + case 0xffffff: + case 0xffffffffL: + case 0xffffffffffL: + case 0xffffffffffffL: + case 0xffffffffffffffL: + case 0xffffffffffffffffL: + return false; + default: { + if (value <= 0xff) + return false; +#if CPU(X86_64) + JSValue jsValue = JSValue::decode(reinterpret_cast(value)); + if (jsValue.isInt32()) + return shouldBlind(Imm32(jsValue.asInt32())); + if (jsValue.isDouble() && !shouldBlindDouble(jsValue.asDouble())) + return false; + + if (!shouldBlindDouble(bitwise_cast(value))) + return false; +#endif + } + } + return shouldBlindForSpecificArch(value); + } + + struct RotatedImmPtr { + RotatedImmPtr(uintptr_t v1, uint8_t v2) + : value(v1) + , rotation(v2) + { + } + TrustedImmPtr value; + TrustedImm32 rotation; + }; + + RotatedImmPtr rotationBlindConstant(ImmPtr imm) + { + uint8_t rotation = random() % (sizeof(void*) * 8); + uintptr_t value = imm.asTrustedImmPtr().asIntptr(); + value = (value << rotation) | (value >> (sizeof(void*) * 8 - rotation)); + return RotatedImmPtr(value, rotation); + } + + void loadRotationBlindedConstant(RotatedImmPtr constant, RegisterID dest) + { + move(constant.value, dest); + rotateRightPtr(constant.rotation, dest); + } + + void convertInt32ToDouble(Imm32 imm, FPRegisterID dest) + { + if (shouldBlind(imm)) { + RegisterID scratchRegister = scratchRegisterForBlinding(); + loadXorBlindedConstant(xorBlindConstant(imm), scratchRegister); + convertInt32ToDouble(scratchRegister, dest); + } else + convertInt32ToDouble(imm.asTrustedImm32(), dest); + } + + void move(ImmPtr imm, RegisterID dest) + { + if (shouldBlind(imm)) + loadRotationBlindedConstant(rotationBlindConstant(imm), dest); + else + move(imm.asTrustedImmPtr(), dest); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, ImmPtr right) + { + if (shouldBlind(right)) { + RegisterID scratchRegister = scratchRegisterForBlinding(); + loadRotationBlindedConstant(rotationBlindConstant(right), scratchRegister); + return branchPtr(cond, left, scratchRegister); + } + return branchPtr(cond, left, right.asTrustedImmPtr()); + } + + void storePtr(ImmPtr imm, Address dest) + { + if (shouldBlind(imm)) { + RegisterID scratchRegister = scratchRegisterForBlinding(); + loadRotationBlindedConstant(rotationBlindConstant(imm), scratchRegister); + storePtr(scratchRegister, dest); + } else + storePtr(imm.asTrustedImmPtr(), dest); + } + +#endif + +#endif // !CPU(X86_64) + +#if ENABLE(JIT_CONSTANT_BLINDING) + bool shouldBlind(Imm32 imm) + { +#if !defined(NDEBUG) + UNUSED_PARAM(imm); + // Debug always blind all constants, if only so we know + // if we've broken blinding during patch development. + return true; +#else + + // First off we'll special case common, "safe" values to avoid hurting + // performance too much + uint32_t value = imm.asTrustedImm32().m_value; + switch (value) { + case 0xffff: + case 0xffffff: + case 0xffffffff: + return false; + default: + if (value <= 0xff) + return false; + } + return shouldBlindForSpecificArch(value); +#endif + } + + struct BlindedImm32 { + BlindedImm32(int32_t v1, int32_t v2) + : value1(v1) + , value2(v2) + { + } + TrustedImm32 value1; + TrustedImm32 value2; + }; + + uint32_t keyForConstant(uint32_t value, uint32_t& mask) + { + uint32_t key = random(); + if (value <= 0xff) + mask = 0xff; + else if (value <= 0xffff) + mask = 0xffff; + else if (value <= 0xffffff) + mask = 0xffffff; + else + mask = 0xffffffff; + return key & mask; + } + + uint32_t keyForConstant(uint32_t value) + { + uint32_t mask = 0; + return keyForConstant(value, mask); + } + + BlindedImm32 xorBlindConstant(Imm32 imm) + { + uint32_t baseValue = imm.asTrustedImm32().m_value; + uint32_t key = keyForConstant(baseValue); + return BlindedImm32(baseValue ^ key, key); + } + + BlindedImm32 additionBlindedConstant(Imm32 imm) + { + // The addition immediate may be used as a pointer offset. Keep aligned based on "imm". + static uint32_t maskTable[4] = { 0xfffffffc, 0xffffffff, 0xfffffffe, 0xffffffff }; + + uint32_t baseValue = imm.asTrustedImm32().m_value; + uint32_t key = keyForConstant(baseValue) & maskTable[baseValue & 3]; + if (key > baseValue) + key = key - baseValue; + return BlindedImm32(baseValue - key, key); + } + + BlindedImm32 andBlindedConstant(Imm32 imm) + { + uint32_t baseValue = imm.asTrustedImm32().m_value; + uint32_t mask = 0; + uint32_t key = keyForConstant(baseValue, mask); + ASSERT((baseValue & mask) == baseValue); + return BlindedImm32(((baseValue & key) | ~key) & mask, ((baseValue & ~key) | key) & mask); + } + + BlindedImm32 orBlindedConstant(Imm32 imm) + { + uint32_t baseValue = imm.asTrustedImm32().m_value; + uint32_t mask = 0; + uint32_t key = keyForConstant(baseValue, mask); + ASSERT((baseValue & mask) == baseValue); + return BlindedImm32((baseValue & key) & mask, (baseValue & ~key) & mask); + } + + void loadXorBlindedConstant(BlindedImm32 constant, RegisterID dest) + { + move(constant.value1, dest); + xor32(constant.value2, dest); + } + + void add32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = additionBlindedConstant(imm); + add32(key.value1, dest); + add32(key.value2, dest); + } else + add32(imm.asTrustedImm32(), dest); + } + + void addPtr(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = additionBlindedConstant(imm); + addPtr(key.value1, dest); + addPtr(key.value2, dest); + } else + addPtr(imm.asTrustedImm32(), dest); + } + + void and32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = andBlindedConstant(imm); + and32(key.value1, dest); + and32(key.value2, dest); + } else + and32(imm.asTrustedImm32(), dest); + } + + void andPtr(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = andBlindedConstant(imm); + andPtr(key.value1, dest); + andPtr(key.value2, dest); + } else + andPtr(imm.asTrustedImm32(), dest); + } + + void and32(Imm32 imm, RegisterID src, RegisterID dest) + { + if (shouldBlind(imm)) { + if (src == dest) + return and32(imm.asTrustedImm32(), dest); + loadXorBlindedConstant(xorBlindConstant(imm), dest); + and32(src, dest); + } else + and32(imm.asTrustedImm32(), src, dest); + } + + void move(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) + loadXorBlindedConstant(xorBlindConstant(imm), dest); + else + move(imm.asTrustedImm32(), dest); + } + + void or32(Imm32 imm, RegisterID src, RegisterID dest) + { + if (shouldBlind(imm)) { + if (src == dest) + return or32(imm, dest); + loadXorBlindedConstant(xorBlindConstant(imm), dest); + or32(src, dest); + } else + or32(imm.asTrustedImm32(), src, dest); + } + + void or32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = orBlindedConstant(imm); + or32(key.value1, dest); + or32(key.value2, dest); + } else + or32(imm.asTrustedImm32(), dest); + } + + void poke(Imm32 value, int index = 0) + { + store32(value, addressForPoke(index)); + } + + void poke(ImmPtr value, int index = 0) + { + storePtr(value, addressForPoke(index)); + } + + void store32(Imm32 imm, Address dest) + { + if (shouldBlind(imm)) { +#if CPU(X86) || CPU(X86_64) + BlindedImm32 blind = xorBlindConstant(imm); + store32(blind.value1, dest); + xor32(blind.value2, dest); +#else + if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { + loadXorBlindedConstant(xorBlindConstant(imm), scratchRegister); + store32(scratchRegister, dest); + } else { + // If we don't have a scratch register available for use, we'll just + // place a random number of nops. + uint32_t nopCount = random() & 3; + while (nopCount--) + nop(); + store32(imm.asTrustedImm32(), dest); + } +#endif + } else + store32(imm.asTrustedImm32(), dest); + } + + void sub32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = additionBlindedConstant(imm); + sub32(key.value1, dest); + sub32(key.value2, dest); + } else + sub32(imm.asTrustedImm32(), dest); + } + + void subPtr(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = additionBlindedConstant(imm); + subPtr(key.value1, dest); + subPtr(key.value2, dest); + } else + subPtr(imm.asTrustedImm32(), dest); + } + + void xor32(Imm32 imm, RegisterID src, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 blind = xorBlindConstant(imm); + xor32(blind.value1, src, dest); + xor32(blind.value2, dest); + } else + xor32(imm.asTrustedImm32(), src, dest); + } + + void xor32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 blind = xorBlindConstant(imm); + xor32(blind.value1, dest); + xor32(blind.value2, dest); + } else + xor32(imm.asTrustedImm32(), dest); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Imm32 right) + { + if (shouldBlind(right)) { + if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { + loadXorBlindedConstant(xorBlindConstant(right), scratchRegister); + return branch32(cond, left, scratchRegister); + } + // If we don't have a scratch register available for use, we'll just + // place a random number of nops. + uint32_t nopCount = random() & 3; + while (nopCount--) + nop(); + return branch32(cond, left, right.asTrustedImm32()); + } + + return branch32(cond, left, right.asTrustedImm32()); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, Imm32 imm, RegisterID dest) + { + if (src == dest) { + if (!scratchRegisterForBlinding()) { + // Release mode ASSERT, if this fails we will perform incorrect codegen. + CRASH(); + } + } + if (shouldBlind(imm)) { + if (src == dest) { + if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { + move(src, scratchRegister); + src = scratchRegister; + } + } + loadXorBlindedConstant(xorBlindConstant(imm), dest); + return branchAdd32(cond, src, dest); + } + return branchAdd32(cond, src, imm.asTrustedImm32(), dest); + } + + Jump branchMul32(ResultCondition cond, Imm32 imm, RegisterID src, RegisterID dest) + { + if (src == dest) { + if (!scratchRegisterForBlinding()) { + // Release mode ASSERT, if this fails we will perform incorrect codegen. + CRASH(); + } + } + if (shouldBlind(imm)) { + if (src == dest) { + if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { + move(src, scratchRegister); + src = scratchRegister; + } + } + loadXorBlindedConstant(xorBlindConstant(imm), dest); + return branchMul32(cond, src, dest); + } + return branchMul32(cond, imm.asTrustedImm32(), src, dest); + } + + // branchSub32 takes a scratch register as 32 bit platforms make use of this, + // with src == dst, and on x86-32 we don't have a platform scratch register. + Jump branchSub32(ResultCondition cond, RegisterID src, Imm32 imm, RegisterID dest, RegisterID scratch) + { + if (shouldBlind(imm)) { + ASSERT(scratch != dest); + ASSERT(scratch != src); + loadXorBlindedConstant(xorBlindConstant(imm), scratch); + return branchSub32(cond, src, scratch, dest); + } + return branchSub32(cond, src, imm.asTrustedImm32(), dest); + } + + // Immediate shifts only have 5 controllable bits + // so we'll consider them safe for now. + TrustedImm32 trustedImm32ForShift(Imm32 imm) + { + return TrustedImm32(imm.asTrustedImm32().m_value & 31); + } + + void lshift32(Imm32 imm, RegisterID dest) + { + lshift32(trustedImm32ForShift(imm), dest); + } + + void lshift32(RegisterID src, Imm32 amount, RegisterID dest) + { + lshift32(src, trustedImm32ForShift(amount), dest); + } + + void rshift32(Imm32 imm, RegisterID dest) + { + rshift32(trustedImm32ForShift(imm), dest); + } + + void rshift32(RegisterID src, Imm32 amount, RegisterID dest) + { + rshift32(src, trustedImm32ForShift(amount), dest); + } + + void urshift32(Imm32 imm, RegisterID dest) + { + urshift32(trustedImm32ForShift(imm), dest); + } + + void urshift32(RegisterID src, Imm32 amount, RegisterID dest) + { + urshift32(src, trustedImm32ForShift(amount), dest); + } +#endif +}; + +} // namespace JSC + +#else // ENABLE(ASSEMBLER) + +// If there is no assembler for this platform, at least allow code to make references to +// some of the things it would otherwise define, albeit without giving that code any way +// of doing anything useful. +class MacroAssembler { +private: + MacroAssembler() { } + +public: + + enum RegisterID { NoRegister }; + enum FPRegisterID { NoFPRegister }; +}; + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssembler_h diff --git a/masm/assembler/MacroAssemblerARM.cpp b/masm/assembler/MacroAssemblerARM.cpp new file mode 100644 index 0000000000..98dc3e9879 --- /dev/null +++ b/masm/assembler/MacroAssemblerARM.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2009 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#include "MacroAssemblerARM.h" + +#if OS(LINUX) +#include +#include +#include +#include +#include +#include +#endif + +namespace JSC { + +static bool isVFPPresent() +{ +#if OS(LINUX) + int fd = open("/proc/self/auxv", O_RDONLY); + if (fd > 0) { + Elf32_auxv_t aux; + while (read(fd, &aux, sizeof(Elf32_auxv_t))) { + if (aux.a_type == AT_HWCAP) { + close(fd); + return aux.a_un.a_val & HWCAP_VFP; + } + } + close(fd); + } +#endif + +#if (COMPILER(RVCT) && defined(__TARGET_FPU_VFP)) || (COMPILER(GCC) && defined(__VFP_FP__)) + return true; +#else + return false; +#endif +} + +const bool MacroAssemblerARM::s_isVFPPresent = isVFPPresent(); + +#if CPU(ARMV5_OR_LOWER) +/* On ARMv5 and below, natural alignment is required. */ +void MacroAssemblerARM::load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) +{ + ARMWord op2; + + ASSERT(address.scale >= 0 && address.scale <= 3); + op2 = m_assembler.lsl(address.index, static_cast(address.scale)); + + if (address.offset >= 0 && address.offset + 0x2 <= 0xff) { + m_assembler.add(ARMRegisters::S0, address.base, op2); + m_assembler.halfDtrUp(ARMAssembler::LoadUint16, dest, ARMRegisters::S0, ARMAssembler::getOp2Half(address.offset)); + m_assembler.halfDtrUp(ARMAssembler::LoadUint16, ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Half(address.offset + 0x2)); + } else if (address.offset < 0 && address.offset >= -0xff) { + m_assembler.add(ARMRegisters::S0, address.base, op2); + m_assembler.halfDtrDown(ARMAssembler::LoadUint16, dest, ARMRegisters::S0, ARMAssembler::getOp2Half(-address.offset)); + m_assembler.halfDtrDown(ARMAssembler::LoadUint16, ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Half(-address.offset - 0x2)); + } else { + m_assembler.moveImm(address.offset, ARMRegisters::S0); + m_assembler.add(ARMRegisters::S0, ARMRegisters::S0, op2); + m_assembler.halfDtrUpRegister(ARMAssembler::LoadUint16, dest, address.base, ARMRegisters::S0); + m_assembler.add(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::Op2Immediate | 0x2); + m_assembler.halfDtrUpRegister(ARMAssembler::LoadUint16, ARMRegisters::S0, address.base, ARMRegisters::S0); + } + m_assembler.orr(dest, dest, m_assembler.lsl(ARMRegisters::S0, 16)); +} +#endif + +} + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) diff --git a/masm/assembler/MacroAssemblerARM.h b/masm/assembler/MacroAssemblerARM.h new file mode 100644 index 0000000000..e3b0be9daa --- /dev/null +++ b/masm/assembler/MacroAssemblerARM.h @@ -0,0 +1,1296 @@ +/* + * Copyright (C) 2008 Apple Inc. + * Copyright (C) 2009, 2010 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerARM_h +#define MacroAssemblerARM_h + +#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#include "ARMAssembler.h" +#include "AbstractMacroAssembler.h" + +namespace JSC { + +class MacroAssemblerARM : public AbstractMacroAssembler { + static const int DoubleConditionMask = 0x0f; + static const int DoubleConditionBitSpecial = 0x10; + COMPILE_ASSERT(!(DoubleConditionBitSpecial & DoubleConditionMask), DoubleConditionBitSpecial_should_not_interfere_with_ARMAssembler_Condition_codes); +public: + typedef ARMRegisters::FPRegisterID FPRegisterID; + + enum RelationalCondition { + Equal = ARMAssembler::EQ, + NotEqual = ARMAssembler::NE, + Above = ARMAssembler::HI, + AboveOrEqual = ARMAssembler::CS, + Below = ARMAssembler::CC, + BelowOrEqual = ARMAssembler::LS, + GreaterThan = ARMAssembler::GT, + GreaterThanOrEqual = ARMAssembler::GE, + LessThan = ARMAssembler::LT, + LessThanOrEqual = ARMAssembler::LE + }; + + enum ResultCondition { + Overflow = ARMAssembler::VS, + Signed = ARMAssembler::MI, + Zero = ARMAssembler::EQ, + NonZero = ARMAssembler::NE + }; + + enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleEqual = ARMAssembler::EQ, + DoubleNotEqual = ARMAssembler::NE | DoubleConditionBitSpecial, + DoubleGreaterThan = ARMAssembler::GT, + DoubleGreaterThanOrEqual = ARMAssembler::GE, + DoubleLessThan = ARMAssembler::CC, + DoubleLessThanOrEqual = ARMAssembler::LS, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = ARMAssembler::EQ | DoubleConditionBitSpecial, + DoubleNotEqualOrUnordered = ARMAssembler::NE, + DoubleGreaterThanOrUnordered = ARMAssembler::HI, + DoubleGreaterThanOrEqualOrUnordered = ARMAssembler::CS, + DoubleLessThanOrUnordered = ARMAssembler::LT, + DoubleLessThanOrEqualOrUnordered = ARMAssembler::LE, + }; + + static const RegisterID stackPointerRegister = ARMRegisters::sp; + static const RegisterID linkRegister = ARMRegisters::lr; + + static const Scale ScalePtr = TimesFour; + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.adds(dest, dest, src); + } + + void add32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.adds(dest, op1, op2); + } + + void add32(TrustedImm32 imm, Address address) + { + load32(address, ARMRegisters::S1); + add32(imm, ARMRegisters::S1); + store32(ARMRegisters::S1, address); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.adds(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void add32(AbsoluteAddress src, RegisterID dest) + { + move(TrustedImmPtr(src.m_ptr), ARMRegisters::S1); + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); + add32(ARMRegisters::S1, dest); + } + + void add32(Address src, RegisterID dest) + { + load32(src, ARMRegisters::S1); + add32(ARMRegisters::S1, dest); + } + + void add32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.adds(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void and32(RegisterID src, RegisterID dest) + { + m_assembler.bitAnds(dest, dest, src); + } + + void and32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.bitAnds(dest, op1, op2); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + ARMWord w = m_assembler.getImm(imm.m_value, ARMRegisters::S0, true); + if (w & ARMAssembler::Op2InvertedImmediate) + m_assembler.bics(dest, dest, w & ~ARMAssembler::Op2InvertedImmediate); + else + m_assembler.bitAnds(dest, dest, w); + } + + void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ARMWord w = m_assembler.getImm(imm.m_value, ARMRegisters::S0, true); + if (w & ARMAssembler::Op2InvertedImmediate) + m_assembler.bics(dest, src, w & ~ARMAssembler::Op2InvertedImmediate); + else + m_assembler.bitAnds(dest, src, w); + } + + void lshift32(RegisterID shiftAmount, RegisterID dest) + { + lshift32(dest, shiftAmount, dest); + } + + void lshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + ARMWord w = ARMAssembler::getOp2Byte(0x1f); + m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); + + m_assembler.movs(dest, m_assembler.lslRegister(src, ARMRegisters::S0)); + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.lsl(dest, imm.m_value & 0x1f)); + } + + void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.lsl(src, imm.m_value & 0x1f)); + } + + void mul32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op2 == dest) { + if (op1 == dest) { + move(op2, ARMRegisters::S0); + op2 = ARMRegisters::S0; + } else { + // Swap the operands. + RegisterID tmp = op1; + op1 = op2; + op2 = tmp; + } + } + m_assembler.muls(dest, op1, op2); + } + + void mul32(RegisterID src, RegisterID dest) + { + mul32(src, dest, dest); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(imm, ARMRegisters::S0); + m_assembler.muls(dest, src, ARMRegisters::S0); + } + + void neg32(RegisterID srcDest) + { + m_assembler.rsbs(srcDest, srcDest, ARMAssembler::getOp2Byte(0)); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orrs(dest, dest, src); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.orrs(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.orrs(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.orrs(dest, op1, op2); + } + + void rshift32(RegisterID shiftAmount, RegisterID dest) + { + rshift32(dest, shiftAmount, dest); + } + + void rshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + ARMWord w = ARMAssembler::getOp2Byte(0x1f); + m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); + + m_assembler.movs(dest, m_assembler.asrRegister(src, ARMRegisters::S0)); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + rshift32(dest, imm, dest); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.asr(src, imm.m_value & 0x1f)); + } + + void urshift32(RegisterID shiftAmount, RegisterID dest) + { + urshift32(dest, shiftAmount, dest); + } + + void urshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + ARMWord w = ARMAssembler::getOp2Byte(0x1f); + m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); + + m_assembler.movs(dest, m_assembler.lsrRegister(src, ARMRegisters::S0)); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.lsr(dest, imm.m_value & 0x1f)); + } + + void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.lsr(src, imm.m_value & 0x1f)); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.subs(dest, dest, src); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.subs(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void sub32(TrustedImm32 imm, Address address) + { + load32(address, ARMRegisters::S1); + sub32(imm, ARMRegisters::S1); + store32(ARMRegisters::S1, address); + } + + void sub32(Address src, RegisterID dest) + { + load32(src, ARMRegisters::S1); + sub32(ARMRegisters::S1, dest); + } + + void sub32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.subs(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void xor32(RegisterID src, RegisterID dest) + { + m_assembler.eors(dest, dest, src); + } + + void xor32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.eors(dest, op1, op2); + } + + void xor32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value == -1) + m_assembler.mvns(dest, dest); + else + m_assembler.eors(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (imm.m_value == -1) + m_assembler.mvns(dest, src); + else + m_assembler.eors(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void countLeadingZeros32(RegisterID src, RegisterID dest) + { +#if WTF_ARM_ARCH_AT_LEAST(5) + m_assembler.clz(dest, src); +#else + UNUSED_PARAM(src); + UNUSED_PARAM(dest); + ASSERT_NOT_REACHED(); +#endif + } + + void load8(ImplicitAddress address, RegisterID dest) + { + m_assembler.dataTransfer32(ARMAssembler::LoadUint8, dest, address.base, address.offset); + } + + void load8(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer32(ARMAssembler::LoadUint8, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void load8Signed(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer16(ARMAssembler::LoadInt8, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void load16(ImplicitAddress address, RegisterID dest) + { + m_assembler.dataTransfer16(ARMAssembler::LoadUint16, dest, address.base, address.offset); + } + + void load16(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer16(ARMAssembler::LoadUint16, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void load16Signed(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer16(ARMAssembler::LoadInt16, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void load32(ImplicitAddress address, RegisterID dest) + { + m_assembler.dataTransfer32(ARMAssembler::LoadUint32, dest, address.base, address.offset); + } + + void load32(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer32(ARMAssembler::LoadUint32, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + +#if CPU(ARMV5_OR_LOWER) + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest); +#else + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + load32(address, dest); + } +#endif + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + load16(address, dest); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result(this); + ASSERT(address.offset >= 0 && address.offset <= 255); + m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, address.base, address.offset); + return result; + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabel32 dataLabel(this); + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, 0); + m_assembler.dtrUpRegister(ARMAssembler::LoadUint32, dest, address.base, ARMRegisters::S0); + return dataLabel; + } + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return value >= -4095 && value <= 4095; + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabelCompact dataLabel(this); + ASSERT(isCompactPtrAlignedAddressOffset(address.offset)); + if (address.offset >= 0) + m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, address.base, address.offset); + else + m_assembler.dtrDown(ARMAssembler::LoadUint32, dest, address.base, address.offset); + return dataLabel; + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + DataLabel32 dataLabel(this); + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, 0); + m_assembler.dtrUpRegister(ARMAssembler::StoreUint32, src, address.base, ARMRegisters::S0); + return dataLabel; + } + + void store8(RegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint8, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void store16(RegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransfer16(ARMAssembler::StoreUint16, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void store32(RegisterID src, ImplicitAddress address) + { + m_assembler.dataTransfer32(ARMAssembler::StoreUint32, src, address.base, address.offset); + } + + void store32(RegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint32, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + move(imm, ARMRegisters::S1); + store32(ARMRegisters::S1, address); + } + + void store32(TrustedImm32 imm, BaseIndex address) + { + move(imm, ARMRegisters::S1); + m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint32, ARMRegisters::S1, address.base, address.index, static_cast(address.scale), address.offset); + } + + void store32(RegisterID src, void* address) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); + m_assembler.dtrUp(ARMAssembler::StoreUint32, src, ARMRegisters::S0, 0); + } + + void store32(TrustedImm32 imm, void* address) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); + m_assembler.moveImm(imm.m_value, ARMRegisters::S1); + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); + } + + void pop(RegisterID dest) + { + m_assembler.pop(dest); + } + + void push(RegisterID src) + { + m_assembler.push(src); + } + + void push(Address address) + { + load32(address, ARMRegisters::S1); + push(ARMRegisters::S1); + } + + void push(TrustedImm32 imm) + { + move(imm, ARMRegisters::S0); + push(ARMRegisters::S0); + } + + void move(TrustedImm32 imm, RegisterID dest) + { + m_assembler.moveImm(imm.m_value, dest); + } + + void move(RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.mov(dest, src); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + move(TrustedImm32(imm), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + m_assembler.mov(ARMRegisters::S0, reg1); + m_assembler.mov(reg1, reg2); + m_assembler.mov(reg2, ARMRegisters::S0); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest) + move(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest) + move(src, dest); + } + + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + load8(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(right.m_value & 0xFFFFFF00)); + load8(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right, int useConstantPool = 0) + { + m_assembler.cmp(left, right); + return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right, int useConstantPool = 0) + { + ARMWord tmp = (static_cast(right.m_value) == 0x80000000) ? ARMAssembler::InvalidImmediate : m_assembler.getOp2(-right.m_value); + if (tmp != ARMAssembler::InvalidImmediate) + m_assembler.cmn(left, tmp); + else + m_assembler.cmp(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); + return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + load32(right, ARMRegisters::S1); + return branch32(cond, left, ARMRegisters::S1); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + load32(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + load32(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + load32(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + load32WithUnalignedHalfWords(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + load8(address, ARMRegisters::S1); + return branchTest32(cond, ARMRegisters::S1, mask); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); + load8(Address(ARMRegisters::S1), ARMRegisters::S1); + return branchTest32(cond, ARMRegisters::S1, mask); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + ASSERT((cond == Zero) || (cond == NonZero)); + m_assembler.tst(reg, mask); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + ARMWord w = m_assembler.getImm(mask.m_value, ARMRegisters::S0, true); + if (w & ARMAssembler::Op2InvertedImmediate) + m_assembler.bics(ARMRegisters::S0, reg, w & ~ARMAssembler::Op2InvertedImmediate); + else + m_assembler.tst(reg, w); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + load32(address, ARMRegisters::S1); + return branchTest32(cond, ARMRegisters::S1, mask); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + load32(address, ARMRegisters::S1); + return branchTest32(cond, ARMRegisters::S1, mask); + } + + Jump jump() + { + return Jump(m_assembler.jmp()); + } + + void jump(RegisterID target) + { + m_assembler.bx(target); + } + + void jump(Address address) + { + load32(address, ARMRegisters::pc); + } + + void jump(AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), ARMRegisters::S0); + load32(Address(ARMRegisters::S0, 0), ARMRegisters::pc); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(src, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchAdd32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(op1, op2, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(src, imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + void mull32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op2 == dest) { + if (op1 == dest) { + move(op2, ARMRegisters::S0); + op2 = ARMRegisters::S0; + } else { + // Swap the operands. + RegisterID tmp = op1; + op1 = op2; + op2 = tmp; + } + } + m_assembler.mull(ARMRegisters::S1, dest, op1, op2); + m_assembler.cmp(ARMRegisters::S1, m_assembler.asr(dest, 31)); + } + + Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + mull32(src1, src2, dest); + cond = NonZero; + } + else + mul32(src1, src2, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchMul32(cond, src, dest, dest); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + move(imm, ARMRegisters::S0); + mull32(ARMRegisters::S0, src, dest); + cond = NonZero; + } + else + mul32(imm, src, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + sub32(src, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + sub32(imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + sub32(src, imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + m_assembler.subs(dest, op1, op2); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchNeg32(ResultCondition cond, RegisterID srcDest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + neg32(srcDest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); + or32(src, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + void breakpoint() + { + m_assembler.bkpt(0); + } + + Call nearCall() + { + m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true); + return Call(m_assembler.blx(ARMRegisters::S1), Call::LinkableNear); + } + + Call call(RegisterID target) + { + return Call(m_assembler.blx(target), Call::None); + } + + void call(Address address) + { + call32(address.base, address.offset); + } + + void ret() + { + m_assembler.bx(linkRegister); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmp(left, right); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + m_assembler.cmp(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + load8(left, ARMRegisters::S1); + compare32(cond, ARMRegisters::S1, right, dest); + } + + void test32(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) + { + if (mask.m_value == -1) + m_assembler.cmp(0, reg); + else + m_assembler.tst(reg, m_assembler.getImm(mask.m_value, ARMRegisters::S0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); + } + + void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + load32(address, ARMRegisters::S1); + test32(cond, ARMRegisters::S1, mask, dest); + } + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + load8(address, ARMRegisters::S1); + test32(cond, ARMRegisters::S1, mask, dest); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.add(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S1, reinterpret_cast(address.m_ptr)); + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); + add32(imm, ARMRegisters::S1); + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address.m_ptr)); + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S1, reinterpret_cast(address.m_ptr)); + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); + sub32(imm, ARMRegisters::S1); + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address.m_ptr)); + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); + } + + void load32(const void* address, RegisterID dest) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); + m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, ARMRegisters::S0, 0); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + load32(left.m_ptr, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + load32(left.m_ptr, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + void relativeTableJump(RegisterID index, int scale) + { + ASSERT(scale >= 0 && scale <= 31); + m_assembler.add(ARMRegisters::pc, ARMRegisters::pc, m_assembler.lsl(index, scale)); + + // NOP the default prefetching + m_assembler.mov(ARMRegisters::r0, ARMRegisters::r0); + } + + Call call() + { + ensureSpace(2 * sizeof(ARMWord), sizeof(ARMWord)); + m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true); + return Call(m_assembler.blx(ARMRegisters::S1), Call::Linkable); + } + + Call tailRecursiveCall() + { + return Call::fromTailJump(jump()); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + return Call::fromTailJump(oldJump); + } + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + DataLabelPtr dataLabel(this); + m_assembler.ldrUniqueImmediate(dest, reinterpret_cast(initialValue.m_value)); + return dataLabel; + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S1); + Jump jump = branch32(cond, left, ARMRegisters::S1, true); + return jump; + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + load32(left, ARMRegisters::S1); + dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S0); + Jump jump = branch32(cond, ARMRegisters::S0, ARMRegisters::S1, true); + return jump; + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + DataLabelPtr dataLabel = moveWithPatch(initialValue, ARMRegisters::S1); + store32(ARMRegisters::S1, address); + return dataLabel; + } + + DataLabelPtr storePtrWithPatch(ImplicitAddress address) + { + return storePtrWithPatch(TrustedImmPtr(0), address); + } + + // Floating point operators + static bool supportsFloatingPoint() + { + return s_isVFPPresent; + } + + static bool supportsFloatingPointTruncate() + { + return false; + } + + static bool supportsFloatingPointSqrt() + { + return s_isVFPPresent; + } + static bool supportsFloatingPointAbs() { return false; } + + void loadFloat(BaseIndex address, FPRegisterID dest) + { + m_assembler.baseIndexTransferFloat(ARMAssembler::LoadFloat, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { + m_assembler.dataTransferFloat(ARMAssembler::LoadDouble, dest, address.base, address.offset); + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { + m_assembler.baseIndexTransferFloat(ARMAssembler::LoadDouble, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void loadDouble(const void* address, FPRegisterID dest) + { + move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); + m_assembler.doubleDtrUp(ARMAssembler::LoadDouble, dest, ARMRegisters::S0, 0); + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransferFloat(ARMAssembler::StoreFloat, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { + m_assembler.dataTransferFloat(ARMAssembler::StoreDouble, src, address.base, address.offset); + } + + void storeDouble(FPRegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransferFloat(ARMAssembler::StoreDouble, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void storeDouble(FPRegisterID src, const void* address) + { + move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); + m_assembler.dataTransferFloat(ARMAssembler::StoreDouble, src, ARMRegisters::S0, 0); + } + + void moveDouble(FPRegisterID src, FPRegisterID dest) + { + if (src != dest) + m_assembler.vmov_f64(dest, src); + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vadd_f64(dest, dest, src); + } + + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vadd_f64(dest, op1, op2); + } + + void addDouble(Address src, FPRegisterID dest) + { + loadDouble(src, ARMRegisters::SD0); + addDouble(ARMRegisters::SD0, dest); + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + loadDouble(address.m_ptr, ARMRegisters::SD0); + addDouble(ARMRegisters::SD0, dest); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vdiv_f64(dest, dest, src); + } + + void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vdiv_f64(dest, op1, op2); + } + + void divDouble(Address src, FPRegisterID dest) + { + ASSERT_NOT_REACHED(); // Untested + loadDouble(src, ARMRegisters::SD0); + divDouble(ARMRegisters::SD0, dest); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vsub_f64(dest, dest, src); + } + + void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vsub_f64(dest, op1, op2); + } + + void subDouble(Address src, FPRegisterID dest) + { + loadDouble(src, ARMRegisters::SD0); + subDouble(ARMRegisters::SD0, dest); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vmul_f64(dest, dest, src); + } + + void mulDouble(Address src, FPRegisterID dest) + { + loadDouble(src, ARMRegisters::SD0); + mulDouble(ARMRegisters::SD0, dest); + } + + void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vmul_f64(dest, op1, op2); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vsqrt_f64(dest, src); + } + + void absDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vabs_f64(dest, src); + } + + void negateDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vneg_f64(dest, src); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.vmov_vfp32(dest << 1, src); + m_assembler.vcvt_f64_s32(dest, dest << 1); + } + + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + load32(src, ARMRegisters::S1); + convertInt32ToDouble(ARMRegisters::S1, dest); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + move(TrustedImmPtr(src.m_ptr), ARMRegisters::S1); + load32(Address(ARMRegisters::S1), ARMRegisters::S1); + convertInt32ToDouble(ARMRegisters::S1, dest); + } + + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.vcvt_f64_f32(dst, src); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.vcvt_f32_f64(dst, src); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + m_assembler.vcmp_f64(left, right); + m_assembler.vmrs_apsr(); + if (cond & DoubleConditionBitSpecial) + m_assembler.cmp(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::VS); + return Jump(m_assembler.jmp(static_cast(cond & ~DoubleConditionMask))); + } + + // Truncates 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, INT_MIN). + enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + truncateDoubleToInt32(src, dest); + + m_assembler.add(ARMRegisters::S0, dest, ARMAssembler::getOp2Byte(1)); + m_assembler.bic(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(1)); + + ARMWord w = ARMAssembler::getOp2(0x80000000); + ASSERT(w != ARMAssembler::InvalidImmediate); + m_assembler.cmp(ARMRegisters::S0, w); + return Jump(m_assembler.jmp(branchType == BranchIfTruncateFailed ? ARMAssembler::EQ : ARMAssembler::NE)); + } + + Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + truncateDoubleToUint32(src, dest); + + m_assembler.add(ARMRegisters::S0, dest, ARMAssembler::getOp2Byte(1)); + m_assembler.bic(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(1)); + + m_assembler.cmp(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + return Jump(m_assembler.jmp(branchType == BranchIfTruncateFailed ? ARMAssembler::EQ : ARMAssembler::NE)); + } + + // Result is undefined if the value is outside of the integer range. + void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + m_assembler.vcvt_s32_f64(ARMRegisters::SD0 << 1, src); + m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); + } + + void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) + { + m_assembler.vcvt_u32_f64(ARMRegisters::SD0 << 1, src); + m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); + } + + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + m_assembler.vcvt_s32_f64(ARMRegisters::SD0 << 1, src); + m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + m_assembler.vcvt_f64_s32(ARMRegisters::SD0, ARMRegisters::SD0 << 1); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, ARMRegisters::SD0)); + + // If the result is zero, it might have been -0.0, and 0.0 equals to -0.0 + failureCases.append(branchTest32(Zero, dest)); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) + { + m_assembler.mov(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + convertInt32ToDouble(ARMRegisters::S0, scratch); + return branchDouble(DoubleNotEqual, reg, scratch); + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) + { + m_assembler.mov(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + convertInt32ToDouble(ARMRegisters::S0, scratch); + return branchDouble(DoubleEqualOrUnordered, reg, scratch); + } + + // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. + static RelationalCondition invert(RelationalCondition cond) + { + ASSERT((static_cast(cond & 0x0fffffff)) == 0 && static_cast(cond) < static_cast(ARMAssembler::AL)); + return static_cast(cond ^ 0x10000000); + } + + void nop() + { + m_assembler.nop(); + } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(reinterpret_cast(ARMAssembler::readCallTarget(call.dataLocation()))); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + ARMAssembler::replaceWithJump(instructionStart.dataLocation(), destination.dataLocation()); + } + + static ptrdiff_t maxJumpReplacementSize() + { + ARMAssembler::maxJumpReplacementSize(); + return 0; + } + +protected: + ARMAssembler::Condition ARMCondition(RelationalCondition cond) + { + return static_cast(cond); + } + + ARMAssembler::Condition ARMCondition(ResultCondition cond) + { + return static_cast(cond); + } + + void ensureSpace(int insnSpace, int constSpace) + { + m_assembler.ensureSpace(insnSpace, constSpace); + } + + int sizeOfConstantPool() + { + return m_assembler.sizeOfConstantPool(); + } + + void call32(RegisterID base, int32_t offset) + { + load32(Address(base, offset), ARMRegisters::S1); + m_assembler.blx(ARMRegisters::S1); + } + +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + ARMAssembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + ARMAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + ARMAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static const bool s_isVFPPresent; +}; + +} + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#endif // MacroAssemblerARM_h diff --git a/masm/assembler/MacroAssemblerARMv7.h b/masm/assembler/MacroAssemblerARMv7.h new file mode 100644 index 0000000000..d2da886c27 --- /dev/null +++ b/masm/assembler/MacroAssemblerARMv7.h @@ -0,0 +1,1823 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerARMv7_h +#define MacroAssemblerARMv7_h + +#if ENABLE(ASSEMBLER) + +#include "ARMv7Assembler.h" +#include "AbstractMacroAssembler.h" + +namespace JSC { + +class MacroAssemblerARMv7 : public AbstractMacroAssembler { + // FIXME: switch dataTempRegister & addressTempRegister, or possibly use r7? + // - dTR is likely used more than aTR, and we'll get better instruction + // encoding if it's in the low 8 registers. + static const RegisterID dataTempRegister = ARMRegisters::ip; + static const RegisterID addressTempRegister = ARMRegisters::r3; + + static const ARMRegisters::FPDoubleRegisterID fpTempRegister = ARMRegisters::d7; + inline ARMRegisters::FPSingleRegisterID fpTempRegisterAsSingle() { return ARMRegisters::asSingle(fpTempRegister); } + +public: + MacroAssemblerARMv7() + : m_makeJumpPatchable(false) + { + } + + typedef ARMv7Assembler::LinkRecord LinkRecord; + typedef ARMv7Assembler::JumpType JumpType; + typedef ARMv7Assembler::JumpLinkType JumpLinkType; + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return value >= -255 && value <= 255; + } + + Vector& jumpsToLink() { return m_assembler.jumpsToLink(); } + void* unlinkedCode() { return m_assembler.unlinkedCode(); } + bool canCompact(JumpType jumpType) { return m_assembler.canCompact(jumpType); } + JumpLinkType computeJumpType(JumpType jumpType, const uint8_t* from, const uint8_t* to) { return m_assembler.computeJumpType(jumpType, from, to); } + JumpLinkType computeJumpType(LinkRecord& record, const uint8_t* from, const uint8_t* to) { return m_assembler.computeJumpType(record, from, to); } + void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset) {return m_assembler.recordLinkOffsets(regionStart, regionEnd, offset); } + int jumpSizeDelta(JumpType jumpType, JumpLinkType jumpLinkType) { return m_assembler.jumpSizeDelta(jumpType, jumpLinkType); } + void link(LinkRecord& record, uint8_t* from, uint8_t* to) { return m_assembler.link(record, from, to); } + + struct ArmAddress { + enum AddressType { + HasOffset, + HasIndex, + } type; + RegisterID base; + union { + int32_t offset; + struct { + RegisterID index; + Scale scale; + }; + } u; + + explicit ArmAddress(RegisterID base, int32_t offset = 0) + : type(HasOffset) + , base(base) + { + u.offset = offset; + } + + explicit ArmAddress(RegisterID base, RegisterID index, Scale scale = TimesOne) + : type(HasIndex) + , base(base) + { + u.index = index; + u.scale = scale; + } + }; + +public: + typedef ARMRegisters::FPDoubleRegisterID FPRegisterID; + + static const Scale ScalePtr = TimesFour; + + enum RelationalCondition { + Equal = ARMv7Assembler::ConditionEQ, + NotEqual = ARMv7Assembler::ConditionNE, + Above = ARMv7Assembler::ConditionHI, + AboveOrEqual = ARMv7Assembler::ConditionHS, + Below = ARMv7Assembler::ConditionLO, + BelowOrEqual = ARMv7Assembler::ConditionLS, + GreaterThan = ARMv7Assembler::ConditionGT, + GreaterThanOrEqual = ARMv7Assembler::ConditionGE, + LessThan = ARMv7Assembler::ConditionLT, + LessThanOrEqual = ARMv7Assembler::ConditionLE + }; + + enum ResultCondition { + Overflow = ARMv7Assembler::ConditionVS, + Signed = ARMv7Assembler::ConditionMI, + Zero = ARMv7Assembler::ConditionEQ, + NonZero = ARMv7Assembler::ConditionNE + }; + + enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleEqual = ARMv7Assembler::ConditionEQ, + DoubleNotEqual = ARMv7Assembler::ConditionVC, // Not the right flag! check for this & handle differently. + DoubleGreaterThan = ARMv7Assembler::ConditionGT, + DoubleGreaterThanOrEqual = ARMv7Assembler::ConditionGE, + DoubleLessThan = ARMv7Assembler::ConditionLO, + DoubleLessThanOrEqual = ARMv7Assembler::ConditionLS, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = ARMv7Assembler::ConditionVS, // Not the right flag! check for this & handle differently. + DoubleNotEqualOrUnordered = ARMv7Assembler::ConditionNE, + DoubleGreaterThanOrUnordered = ARMv7Assembler::ConditionHI, + DoubleGreaterThanOrEqualOrUnordered = ARMv7Assembler::ConditionHS, + DoubleLessThanOrUnordered = ARMv7Assembler::ConditionLT, + DoubleLessThanOrEqualOrUnordered = ARMv7Assembler::ConditionLE, + }; + + static const RegisterID stackPointerRegister = ARMRegisters::sp; + static const RegisterID linkRegister = ARMRegisters::lr; + + // Integer arithmetic operations: + // + // Operations are typically two operand - operation(source, srcDst) + // For many operations the source may be an TrustedImm32, the srcDst operand + // may often be a memory location (explictly described using an Address + // object). + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.add(dest, dest, src); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + add32(imm, dest, dest); + } + + void add32(AbsoluteAddress src, RegisterID dest) + { + load32(src.m_ptr, dataTempRegister); + add32(dataTempRegister, dest); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add(dest, src, armImm); + else { + move(imm, dataTempRegister); + m_assembler.add(dest, src, dataTempRegister); + } + } + + void add32(TrustedImm32 imm, Address address) + { + load32(address, dataTempRegister); + + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add(dataTempRegister, dataTempRegister, armImm); + else { + // Hrrrm, since dataTempRegister holds the data loaded, + // use addressTempRegister to hold the immediate. + move(imm, addressTempRegister); + m_assembler.add(dataTempRegister, dataTempRegister, addressTempRegister); + } + + store32(dataTempRegister, address); + } + + void add32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + add32(dataTempRegister, dest); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + load32(address.m_ptr, dataTempRegister); + + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add(dataTempRegister, dataTempRegister, armImm); + else { + // Hrrrm, since dataTempRegister holds the data loaded, + // use addressTempRegister to hold the immediate. + move(imm, addressTempRegister); + m_assembler.add(dataTempRegister, dataTempRegister, addressTempRegister); + } + + store32(dataTempRegister, address.m_ptr); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), addressTempRegister); + + m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(0)); + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add_S(dataTempRegister, dataTempRegister, armImm); + else { + move(imm, addressTempRegister); + m_assembler.add_S(dataTempRegister, dataTempRegister, addressTempRegister); + move(TrustedImmPtr(address.m_ptr), addressTempRegister); + } + m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(0)); + + m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(4)); + m_assembler.adc(dataTempRegister, dataTempRegister, ARMThumbImmediate::makeEncodedImm(imm.m_value >> 31)); + m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(4)); + } + + void and32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.ARM_and(dest, op1, op2); + } + + void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.ARM_and(dest, src, armImm); + else { + move(imm, dataTempRegister); + m_assembler.ARM_and(dest, src, dataTempRegister); + } + } + + void and32(RegisterID src, RegisterID dest) + { + and32(dest, src, dest); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + and32(imm, dest, dest); + } + + void countLeadingZeros32(RegisterID src, RegisterID dest) + { + m_assembler.clz(dest, src); + } + + void lshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + // Clamp the shift to the range 0..31 + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); + ASSERT(armImm.isValid()); + m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); + + m_assembler.lsl(dest, src, dataTempRegister); + } + + void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.lsl(dest, src, imm.m_value & 0x1f); + } + + void lshift32(RegisterID shiftAmount, RegisterID dest) + { + lshift32(dest, shiftAmount, dest); + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + lshift32(dest, imm, dest); + } + + void mul32(RegisterID src, RegisterID dest) + { + m_assembler.smull(dest, dataTempRegister, dest, src); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(imm, dataTempRegister); + m_assembler.smull(dest, dataTempRegister, src, dataTempRegister); + } + + void neg32(RegisterID srcDest) + { + m_assembler.neg(srcDest, srcDest); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orr(dest, dest, src); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + or32(imm, dest, dest); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.orr(dest, op1, op2); + } + + void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.orr(dest, src, armImm); + else { + move(imm, dataTempRegister); + m_assembler.orr(dest, src, dataTempRegister); + } + } + + void rshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + // Clamp the shift to the range 0..31 + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); + ASSERT(armImm.isValid()); + m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); + + m_assembler.asr(dest, src, dataTempRegister); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.asr(dest, src, imm.m_value & 0x1f); + } + + void rshift32(RegisterID shiftAmount, RegisterID dest) + { + rshift32(dest, shiftAmount, dest); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + rshift32(dest, imm, dest); + } + + void urshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + // Clamp the shift to the range 0..31 + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); + ASSERT(armImm.isValid()); + m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); + + m_assembler.lsr(dest, src, dataTempRegister); + } + + void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.lsr(dest, src, imm.m_value & 0x1f); + } + + void urshift32(RegisterID shiftAmount, RegisterID dest) + { + urshift32(dest, shiftAmount, dest); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + urshift32(dest, imm, dest); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.sub(dest, dest, src); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.sub(dest, dest, armImm); + else { + move(imm, dataTempRegister); + m_assembler.sub(dest, dest, dataTempRegister); + } + } + + void sub32(TrustedImm32 imm, Address address) + { + load32(address, dataTempRegister); + + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.sub(dataTempRegister, dataTempRegister, armImm); + else { + // Hrrrm, since dataTempRegister holds the data loaded, + // use addressTempRegister to hold the immediate. + move(imm, addressTempRegister); + m_assembler.sub(dataTempRegister, dataTempRegister, addressTempRegister); + } + + store32(dataTempRegister, address); + } + + void sub32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + sub32(dataTempRegister, dest); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + load32(address.m_ptr, dataTempRegister); + + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.sub(dataTempRegister, dataTempRegister, armImm); + else { + // Hrrrm, since dataTempRegister holds the data loaded, + // use addressTempRegister to hold the immediate. + move(imm, addressTempRegister); + m_assembler.sub(dataTempRegister, dataTempRegister, addressTempRegister); + } + + store32(dataTempRegister, address.m_ptr); + } + + void xor32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.eor(dest, op1, op2); + } + + void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (imm.m_value == -1) { + m_assembler.mvn(dest, src); + return; + } + + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.eor(dest, src, armImm); + else { + move(imm, dataTempRegister); + m_assembler.eor(dest, src, dataTempRegister); + } + } + + void xor32(RegisterID src, RegisterID dest) + { + xor32(dest, src, dest); + } + + void xor32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value == -1) + m_assembler.mvn(dest, dest); + else + xor32(imm, dest, dest); + } + + + // Memory access operations: + // + // Loads are of the form load(address, destination) and stores of the form + // store(source, address). The source for a store may be an TrustedImm32. Address + // operand objects to loads and store will be implicitly constructed if a + // register is passed. + +private: + void load32(ArmAddress address, RegisterID dest) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.ldr(dest, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.ldr(dest, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.ldr(dest, address.base, address.u.offset, true, false); + } + } + + void load16(ArmAddress address, RegisterID dest) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.ldrh(dest, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.ldrh(dest, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.ldrh(dest, address.base, address.u.offset, true, false); + } + } + + void load16Signed(ArmAddress address, RegisterID dest) + { + ASSERT(address.type == ArmAddress::HasIndex); + m_assembler.ldrsh(dest, address.base, address.u.index, address.u.scale); + } + + void load8(ArmAddress address, RegisterID dest) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.ldrb(dest, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.ldrb(dest, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.ldrb(dest, address.base, address.u.offset, true, false); + } + } + + void load8Signed(ArmAddress address, RegisterID dest) + { + ASSERT(address.type == ArmAddress::HasIndex); + m_assembler.ldrsb(dest, address.base, address.u.index, address.u.scale); + } + +protected: + void store32(RegisterID src, ArmAddress address) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.str(src, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.str(src, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.str(src, address.base, address.u.offset, true, false); + } + } + +private: + void store8(RegisterID src, ArmAddress address) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.strb(src, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.strb(src, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.strb(src, address.base, address.u.offset, true, false); + } + } + + void store16(RegisterID src, ArmAddress address) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.strh(src, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.strh(src, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.strh(src, address.base, address.u.offset, true, false); + } + } + +public: + void load32(ImplicitAddress address, RegisterID dest) + { + load32(setupArmAddress(address), dest); + } + + void load32(BaseIndex address, RegisterID dest) + { + load32(setupArmAddress(address), dest); + } + + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + load32(setupArmAddress(address), dest); + } + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + load16(setupArmAddress(address), dest); + } + + void load32(const void* address, RegisterID dest) + { + move(TrustedImmPtr(address), addressTempRegister); + m_assembler.ldr(dest, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result(this); + ASSERT(address.offset >= 0 && address.offset <= 255); + m_assembler.ldrWide8BitImmediate(dest, address.base, address.offset); + return result; + } + + void load8(ImplicitAddress address, RegisterID dest) + { + load8(setupArmAddress(address), dest); + } + + void load8Signed(ImplicitAddress, RegisterID) + { + UNREACHABLE_FOR_PLATFORM(); + } + + void load8(BaseIndex address, RegisterID dest) + { + load8(setupArmAddress(address), dest); + } + + void load8Signed(BaseIndex address, RegisterID dest) + { + load8Signed(setupArmAddress(address), dest); + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabel32 label = moveWithPatch(TrustedImm32(address.offset), dataTempRegister); + load32(ArmAddress(address.base, dataTempRegister), dest); + return label; + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + + RegisterID base = address.base; + + DataLabelCompact label(this); + ASSERT(isCompactPtrAlignedAddressOffset(address.offset)); + + m_assembler.ldr(dest, base, address.offset, true, false); + return label; + } + + void load16(BaseIndex address, RegisterID dest) + { + m_assembler.ldrh(dest, makeBaseIndexBase(address), address.index, address.scale); + } + + void load16Signed(BaseIndex address, RegisterID dest) + { + load16Signed(setupArmAddress(address), dest); + } + + void load16(ImplicitAddress address, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.offset); + if (armImm.isValid()) + m_assembler.ldrh(dest, address.base, armImm); + else { + move(TrustedImm32(address.offset), dataTempRegister); + m_assembler.ldrh(dest, address.base, dataTempRegister); + } + } + + void load16Signed(ImplicitAddress, RegisterID) + { + UNREACHABLE_FOR_PLATFORM(); + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + DataLabel32 label = moveWithPatch(TrustedImm32(address.offset), dataTempRegister); + store32(src, ArmAddress(address.base, dataTempRegister)); + return label; + } + + void store32(RegisterID src, ImplicitAddress address) + { + store32(src, setupArmAddress(address)); + } + + void store32(RegisterID src, BaseIndex address) + { + store32(src, setupArmAddress(address)); + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + move(imm, dataTempRegister); + store32(dataTempRegister, setupArmAddress(address)); + } + + void store32(TrustedImm32 imm, BaseIndex address) + { + move(imm, dataTempRegister); + store32(dataTempRegister, setupArmAddress(address)); + } + + void store32(RegisterID src, const void* address) + { + move(TrustedImmPtr(address), addressTempRegister); + m_assembler.str(src, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); + } + + void store32(TrustedImm32 imm, const void* address) + { + move(imm, dataTempRegister); + store32(dataTempRegister, address); + } + + void store8(RegisterID src, BaseIndex address) + { + store8(src, setupArmAddress(address)); + } + + void store16(RegisterID src, BaseIndex address) + { + store16(src, setupArmAddress(address)); + } + +#if ENABLE(JIT_CONSTANT_BLINDING) + static bool shouldBlindForSpecificArch(uint32_t value) + { + ARMThumbImmediate immediate = ARMThumbImmediate::makeEncodedImm(value); + + // Couldn't be encoded as an immediate, so assume it's untrusted. + if (!immediate.isValid()) + return true; + + // If we can encode the immediate, we have less than 16 attacker + // controlled bits. + if (immediate.isEncodedImm()) + return false; + + // Don't let any more than 12 bits of an instruction word + // be controlled by an attacker. + return !immediate.isUInt12(); + } +#endif + + // Floating-point operations: + + static bool supportsFloatingPoint() { return true; } + static bool supportsFloatingPointTruncate() { return true; } + static bool supportsFloatingPointSqrt() { return true; } + static bool supportsFloatingPointAbs() { return true; } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { + RegisterID base = address.base; + int32_t offset = address.offset; + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { + add32(TrustedImm32(offset), base, addressTempRegister); + base = addressTempRegister; + offset = 0; + } + + m_assembler.vldr(dest, base, offset); + } + + void loadFloat(ImplicitAddress address, FPRegisterID dest) + { + RegisterID base = address.base; + int32_t offset = address.offset; + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { + add32(TrustedImm32(offset), base, addressTempRegister); + base = addressTempRegister; + offset = 0; + } + + m_assembler.flds(ARMRegisters::asSingle(dest), base, offset); + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { + move(address.index, addressTempRegister); + lshift32(TrustedImm32(address.scale), addressTempRegister); + add32(address.base, addressTempRegister); + loadDouble(Address(addressTempRegister, address.offset), dest); + } + + void loadFloat(BaseIndex address, FPRegisterID dest) + { + move(address.index, addressTempRegister); + lshift32(TrustedImm32(address.scale), addressTempRegister); + add32(address.base, addressTempRegister); + loadFloat(Address(addressTempRegister, address.offset), dest); + } + + void moveDouble(FPRegisterID src, FPRegisterID dest) + { + if (src != dest) + m_assembler.vmov(dest, src); + } + + void loadDouble(const void* address, FPRegisterID dest) + { + move(TrustedImmPtr(address), addressTempRegister); + m_assembler.vldr(dest, addressTempRegister, 0); + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { + RegisterID base = address.base; + int32_t offset = address.offset; + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { + add32(TrustedImm32(offset), base, addressTempRegister); + base = addressTempRegister; + offset = 0; + } + + m_assembler.vstr(src, base, offset); + } + + void storeFloat(FPRegisterID src, ImplicitAddress address) + { + RegisterID base = address.base; + int32_t offset = address.offset; + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { + add32(TrustedImm32(offset), base, addressTempRegister); + base = addressTempRegister; + offset = 0; + } + + m_assembler.fsts(ARMRegisters::asSingle(src), base, offset); + } + + void storeDouble(FPRegisterID src, const void* address) + { + move(TrustedImmPtr(address), addressTempRegister); + storeDouble(src, addressTempRegister); + } + + void storeDouble(FPRegisterID src, BaseIndex address) + { + move(address.index, addressTempRegister); + lshift32(TrustedImm32(address.scale), addressTempRegister); + add32(address.base, addressTempRegister); + storeDouble(src, Address(addressTempRegister, address.offset)); + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + move(address.index, addressTempRegister); + lshift32(TrustedImm32(address.scale), addressTempRegister); + add32(address.base, addressTempRegister); + storeFloat(src, Address(addressTempRegister, address.offset)); + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vadd(dest, dest, src); + } + + void addDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + addDouble(fpTempRegister, dest); + } + + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vadd(dest, op1, op2); + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + loadDouble(address.m_ptr, fpTempRegister); + m_assembler.vadd(dest, dest, fpTempRegister); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vdiv(dest, dest, src); + } + + void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vdiv(dest, op1, op2); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vsub(dest, dest, src); + } + + void subDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + subDouble(fpTempRegister, dest); + } + + void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vsub(dest, op1, op2); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vmul(dest, dest, src); + } + + void mulDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + mulDouble(fpTempRegister, dest); + } + + void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vmul(dest, op1, op2); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vsqrt(dest, src); + } + + void absDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vabs(dest, src); + } + + void negateDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vneg(dest, src); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.vmov(fpTempRegister, src, src); + m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); + } + + void convertInt32ToDouble(Address address, FPRegisterID dest) + { + // Fixme: load directly into the fpr! + load32(address, dataTempRegister); + m_assembler.vmov(fpTempRegister, dataTempRegister, dataTempRegister); + m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); + } + + void convertInt32ToDouble(AbsoluteAddress address, FPRegisterID dest) + { + // Fixme: load directly into the fpr! + load32(address.m_ptr, dataTempRegister); + m_assembler.vmov(fpTempRegister, dataTempRegister, dataTempRegister); + m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); + } + + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.vcvtds(dst, ARMRegisters::asSingle(src)); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.vcvtsd(ARMRegisters::asSingle(dst), src); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + m_assembler.vcmp(left, right); + m_assembler.vmrs(); + + if (cond == DoubleNotEqual) { + // ConditionNE jumps if NotEqual *or* unordered - force the unordered cases not to jump. + Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); + Jump result = makeBranch(ARMv7Assembler::ConditionNE); + unordered.link(this); + return result; + } + if (cond == DoubleEqualOrUnordered) { + Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); + Jump notEqual = makeBranch(ARMv7Assembler::ConditionNE); + unordered.link(this); + // We get here if either unordered or equal. + Jump result = jump(); + notEqual.link(this); + return result; + } + return makeBranch(cond); + } + + enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + // Convert into dest. + m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + + // Calculate 2x dest. If the value potentially underflowed, it will have + // clamped to 0x80000000, so 2x dest is zero in this case. In the case of + // overflow the result will be equal to -2. + Jump underflow = branchAdd32(Zero, dest, dest, dataTempRegister); + Jump noOverflow = branch32(NotEqual, dataTempRegister, TrustedImm32(-2)); + + // For BranchIfTruncateSuccessful, we branch if 'noOverflow' jumps. + underflow.link(this); + if (branchType == BranchIfTruncateSuccessful) + return noOverflow; + + // We'll reach the current point in the code on failure, so plant a + // jump here & link the success case. + Jump failure = jump(); + noOverflow.link(this); + return failure; + } + + Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + + Jump overflow = branch32(Equal, dest, TrustedImm32(0x7fffffff)); + Jump success = branch32(GreaterThanOrEqual, dest, TrustedImm32(0)); + overflow.link(this); + + if (branchType == BranchIfTruncateSuccessful) + return success; + + Jump failure = jump(); + success.link(this); + return failure; + } + + // Result is undefined if the value is outside of the integer range. + void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + } + + void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) + { + m_assembler.vcvt_floatingPointToUnsigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + } + + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID) + { + m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + m_assembler.vcvt_signedToFloatingPoint(fpTempRegister, fpTempRegisterAsSingle()); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, fpTempRegister)); + + // If the result is zero, it might have been -0.0, and the double comparison won't catch this! + failureCases.append(branchTest32(Zero, dest)); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID) + { + m_assembler.vcmpz(reg); + m_assembler.vmrs(); + Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); + Jump result = makeBranch(ARMv7Assembler::ConditionNE); + unordered.link(this); + return result; + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID) + { + m_assembler.vcmpz(reg); + m_assembler.vmrs(); + Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); + Jump notEqual = makeBranch(ARMv7Assembler::ConditionNE); + unordered.link(this); + // We get here if either unordered or equal. + Jump result = jump(); + notEqual.link(this); + return result; + } + + // Stack manipulation operations: + // + // The ABI is assumed to provide a stack abstraction to memory, + // containing machine word sized units of data. Push and pop + // operations add and remove a single register sized unit of data + // to or from the stack. Peek and poke operations read or write + // values on the stack, without moving the current stack position. + + void pop(RegisterID dest) + { + // store postindexed with writeback + m_assembler.ldr(dest, ARMRegisters::sp, sizeof(void*), false, true); + } + + void push(RegisterID src) + { + // store preindexed with writeback + m_assembler.str(src, ARMRegisters::sp, -sizeof(void*), true, true); + } + + void push(Address address) + { + load32(address, dataTempRegister); + push(dataTempRegister); + } + + void push(TrustedImm32 imm) + { + move(imm, dataTempRegister); + push(dataTempRegister); + } + + // Register move operations: + // + // Move values in registers. + + void move(TrustedImm32 imm, RegisterID dest) + { + uint32_t value = imm.m_value; + + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(value); + + if (armImm.isValid()) + m_assembler.mov(dest, armImm); + else if ((armImm = ARMThumbImmediate::makeEncodedImm(~value)).isValid()) + m_assembler.mvn(dest, armImm); + else { + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(value)); + if (value & 0xffff0000) + m_assembler.movt(dest, ARMThumbImmediate::makeUInt16(value >> 16)); + } + } + + void move(RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.mov(dest, src); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + move(TrustedImm32(imm), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + move(reg1, dataTempRegister); + move(reg2, reg1); + move(dataTempRegister, reg2); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + move(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + move(src, dest); + } + + // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. + static RelationalCondition invert(RelationalCondition cond) + { + return static_cast(cond ^ 1); + } + + void nop() + { + m_assembler.nop(); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + ARMv7Assembler::replaceWithJump(instructionStart.dataLocation(), destination.dataLocation()); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return ARMv7Assembler::maxJumpReplacementSize(); + } + + // Forwards / external control flow operations: + // + // This set of jump and conditional branch operations return a Jump + // object which may linked at a later point, allow forwards jump, + // or jumps that will require external linkage (after the code has been + // relocated). + // + // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge + // respecitvely, for unsigned comparisons the names b, a, be, and ae are + // used (representing the names 'below' and 'above'). + // + // Operands to the comparision are provided in the expected order, e.g. + // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when + // treated as a signed 32bit value, is less than or equal to 5. + // + // jz and jnz test whether the first operand is equal to zero, and take + // an optional second operand of a mask under which to perform the test. +private: + + // Should we be using TEQ for equal/not-equal? + void compare32(RegisterID left, TrustedImm32 right) + { + int32_t imm = right.m_value; + if (!imm) + m_assembler.tst(left, left); + else { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm); + if (armImm.isValid()) + m_assembler.cmp(left, armImm); + else if ((armImm = ARMThumbImmediate::makeEncodedImm(-imm)).isValid()) + m_assembler.cmn(left, armImm); + else { + move(TrustedImm32(imm), dataTempRegister); + m_assembler.cmp(left, dataTempRegister); + } + } + } + + void test32(RegisterID reg, TrustedImm32 mask) + { + int32_t imm = mask.m_value; + + if (imm == -1) + m_assembler.tst(reg, reg); + else { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm); + if (armImm.isValid()) + m_assembler.tst(reg, armImm); + else { + move(mask, dataTempRegister); + m_assembler.tst(reg, dataTempRegister); + } + } + } + +public: + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) + { + m_assembler.cmp(left, right); + return Jump(makeBranch(cond)); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + compare32(left, right); + return Jump(makeBranch(cond)); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + load32(right, dataTempRegister); + return branch32(cond, left, dataTempRegister); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + load32(left, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load32(left, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load32(left, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load32WithUnalignedHalfWords(left, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + load32(left.m_ptr, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load32(left.m_ptr, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branch8(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + compare32(left, right); + return Jump(makeBranch(cond)); + } + + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + ASSERT(!(0xffffff00 & right.m_value)); + // use addressTempRegister incase the branch8 we call uses dataTempRegister. :-/ + load8(left, addressTempRegister); + return branch8(cond, addressTempRegister, right); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(0xffffff00 & right.m_value)); + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load8(left, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + m_assembler.tst(reg, mask); + return Jump(makeBranch(cond)); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + test32(reg, mask); + return Jump(makeBranch(cond)); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + // use addressTempRegister incase the branchTest32 we call uses dataTempRegister. :-/ + load32(address, addressTempRegister); + return branchTest32(cond, addressTempRegister, mask); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + // use addressTempRegister incase the branchTest32 we call uses dataTempRegister. :-/ + load32(address, addressTempRegister); + return branchTest32(cond, addressTempRegister, mask); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + // use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/ + load8(address, addressTempRegister); + return branchTest32(cond, addressTempRegister, mask); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + // use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/ + move(TrustedImmPtr(address.m_ptr), addressTempRegister); + load8(Address(addressTempRegister), addressTempRegister); + return branchTest32(cond, addressTempRegister, mask); + } + + void jump(RegisterID target) + { + m_assembler.bx(target); + } + + // Address is a memory location containing the address to jump to + void jump(Address address) + { + load32(address, dataTempRegister); + m_assembler.bx(dataTempRegister); + } + + void jump(AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), dataTempRegister); + load32(Address(dataTempRegister), dataTempRegister); + m_assembler.bx(dataTempRegister); + } + + + // Arithmetic control flow operations: + // + // This set of conditional branch operations branch based + // on the result of an arithmetic operation. The operation + // is performed as normal, storing the result. + // + // * jz operations branch if the result is zero. + // * jo operations branch if the (signed) arithmetic + // operation caused an overflow to occur. + + Jump branchAdd32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.add_S(dest, op1, op2); + return Jump(makeBranch(cond)); + } + + Jump branchAdd32(ResultCondition cond, RegisterID op1, TrustedImm32 imm, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add_S(dest, op1, armImm); + else { + move(imm, dataTempRegister); + m_assembler.add_S(dest, op1, dataTempRegister); + } + return Jump(makeBranch(cond)); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchAdd32(cond, dest, src, dest); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchAdd32(cond, dest, imm, dest); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) + { + // Move the high bits of the address into addressTempRegister, + // and load the value into dataTempRegister. + move(TrustedImmPtr(dest.m_ptr), addressTempRegister); + m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); + + // Do the add. + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add_S(dataTempRegister, dataTempRegister, armImm); + else { + // If the operand does not fit into an immediate then load it temporarily + // into addressTempRegister; since we're overwriting addressTempRegister + // we'll need to reload it with the high bits of the address afterwards. + move(imm, addressTempRegister); + m_assembler.add_S(dataTempRegister, dataTempRegister, addressTempRegister); + move(TrustedImmPtr(dest.m_ptr), addressTempRegister); + } + + // Store the result. + m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); + + return Jump(makeBranch(cond)); + } + + Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + m_assembler.smull(dest, dataTempRegister, src1, src2); + + if (cond == Overflow) { + m_assembler.asr(addressTempRegister, dest, 31); + return branch32(NotEqual, addressTempRegister, dataTempRegister); + } + + return branchTest32(cond, dest); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchMul32(cond, src, dest, dest); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(imm, dataTempRegister); + return branchMul32(cond, dataTempRegister, src, dest); + } + + Jump branchNeg32(ResultCondition cond, RegisterID srcDest) + { + ARMThumbImmediate zero = ARMThumbImmediate::makeUInt12(0); + m_assembler.sub_S(srcDest, zero, srcDest); + return Jump(makeBranch(cond)); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + m_assembler.orr_S(dest, dest, src); + return Jump(makeBranch(cond)); + } + + Jump branchSub32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.sub_S(dest, op1, op2); + return Jump(makeBranch(cond)); + } + + Jump branchSub32(ResultCondition cond, RegisterID op1, TrustedImm32 imm, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.sub_S(dest, op1, armImm); + else { + move(imm, dataTempRegister); + m_assembler.sub_S(dest, op1, dataTempRegister); + } + return Jump(makeBranch(cond)); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchSub32(cond, dest, src, dest); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchSub32(cond, dest, imm, dest); + } + + void relativeTableJump(RegisterID index, int scale) + { + ASSERT(scale >= 0 && scale <= 31); + + // dataTempRegister will point after the jump if index register contains zero + move(ARMRegisters::pc, dataTempRegister); + m_assembler.add(dataTempRegister, dataTempRegister, ARMThumbImmediate::makeEncodedImm(9)); + + ShiftTypeAndAmount shift(SRType_LSL, scale); + m_assembler.add(dataTempRegister, dataTempRegister, index, shift); + jump(dataTempRegister); + } + + // Miscellaneous operations: + + void breakpoint(uint8_t imm = 0) + { + m_assembler.bkpt(imm); + } + + ALWAYS_INLINE Call nearCall() + { + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Call(m_assembler.blx(dataTempRegister), Call::LinkableNear); + } + + ALWAYS_INLINE Call call() + { + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Call(m_assembler.blx(dataTempRegister), Call::Linkable); + } + + ALWAYS_INLINE Call call(RegisterID target) + { + return Call(m_assembler.blx(target), Call::None); + } + + ALWAYS_INLINE Call call(Address address) + { + load32(address, dataTempRegister); + return Call(m_assembler.blx(dataTempRegister), Call::None); + } + + ALWAYS_INLINE void ret() + { + m_assembler.bx(linkRegister); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmp(left, right); + m_assembler.it(armV7Condition(cond), false); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); + } + + void compare32(RelationalCondition cond, Address left, RegisterID right, RegisterID dest) + { + load32(left, dataTempRegister); + compare32(cond, dataTempRegister, right, dest); + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + load8(left, addressTempRegister); + compare32(cond, addressTempRegister, right, dest); + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + compare32(left, right); + m_assembler.it(armV7Condition(cond), false); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); + } + + // FIXME: + // The mask should be optional... paerhaps the argument order should be + // dest-src, operations always have a dest? ... possibly not true, considering + // asm ops like test, or pseudo ops like pop(). + void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + load32(address, dataTempRegister); + test32(dataTempRegister, mask); + m_assembler.it(armV7Condition(cond), false); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); + } + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + load8(address, dataTempRegister); + test32(dataTempRegister, mask); + m_assembler.it(armV7Condition(cond), false); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); + } + + ALWAYS_INLINE DataLabel32 moveWithPatch(TrustedImm32 imm, RegisterID dst) + { + padBeforePatch(); + moveFixedWidthEncoding(imm, dst); + return DataLabel32(this); + } + + ALWAYS_INLINE DataLabelPtr moveWithPatch(TrustedImmPtr imm, RegisterID dst) + { + padBeforePatch(); + moveFixedWidthEncoding(TrustedImm32(imm), dst); + return DataLabelPtr(this); + } + + ALWAYS_INLINE Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + dataLabel = moveWithPatch(initialRightValue, dataTempRegister); + return branch32(cond, left, dataTempRegister); + } + + ALWAYS_INLINE Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + load32(left, addressTempRegister); + dataLabel = moveWithPatch(initialRightValue, dataTempRegister); + return branch32(cond, addressTempRegister, dataTempRegister); + } + + PatchableJump patchableBranchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + m_makeJumpPatchable = true; + Jump result = branchPtrWithPatch(cond, left, dataLabel, initialRightValue); + m_makeJumpPatchable = false; + return PatchableJump(result); + } + + PatchableJump patchableJump() + { + padBeforePatch(); + m_makeJumpPatchable = true; + Jump result = jump(); + m_makeJumpPatchable = false; + return PatchableJump(result); + } + + ALWAYS_INLINE DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + DataLabelPtr label = moveWithPatch(initialValue, dataTempRegister); + store32(dataTempRegister, address); + return label; + } + ALWAYS_INLINE DataLabelPtr storePtrWithPatch(ImplicitAddress address) { return storePtrWithPatch(TrustedImmPtr(0), address); } + + + ALWAYS_INLINE Call tailRecursiveCall() + { + // Like a normal call, but don't link. + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Call(m_assembler.bx(dataTempRegister), Call::Linkable); + } + + ALWAYS_INLINE Call makeTailRecursiveCall(Jump oldJump) + { + oldJump.link(this); + return tailRecursiveCall(); + } + + + int executableOffsetFor(int location) + { + return m_assembler.executableOffsetFor(location); + } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(reinterpret_cast(ARMv7Assembler::readCallTarget(call.dataLocation()))); + } + +protected: + ALWAYS_INLINE Jump jump() + { + m_assembler.label(); // Force nop-padding if we're in the middle of a watchpoint. + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Jump(m_assembler.bx(dataTempRegister), m_makeJumpPatchable ? ARMv7Assembler::JumpNoConditionFixedSize : ARMv7Assembler::JumpNoCondition); + } + + ALWAYS_INLINE Jump makeBranch(ARMv7Assembler::Condition cond) + { + m_assembler.label(); // Force nop-padding if we're in the middle of a watchpoint. + m_assembler.it(cond, true, true); + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Jump(m_assembler.bx(dataTempRegister), m_makeJumpPatchable ? ARMv7Assembler::JumpConditionFixedSize : ARMv7Assembler::JumpCondition, cond); + } + ALWAYS_INLINE Jump makeBranch(RelationalCondition cond) { return makeBranch(armV7Condition(cond)); } + ALWAYS_INLINE Jump makeBranch(ResultCondition cond) { return makeBranch(armV7Condition(cond)); } + ALWAYS_INLINE Jump makeBranch(DoubleCondition cond) { return makeBranch(armV7Condition(cond)); } + + ArmAddress setupArmAddress(BaseIndex address) + { + if (address.offset) { + ARMThumbImmediate imm = ARMThumbImmediate::makeUInt12OrEncodedImm(address.offset); + if (imm.isValid()) + m_assembler.add(addressTempRegister, address.base, imm); + else { + move(TrustedImm32(address.offset), addressTempRegister); + m_assembler.add(addressTempRegister, addressTempRegister, address.base); + } + + return ArmAddress(addressTempRegister, address.index, address.scale); + } else + return ArmAddress(address.base, address.index, address.scale); + } + + ArmAddress setupArmAddress(Address address) + { + if ((address.offset >= -0xff) && (address.offset <= 0xfff)) + return ArmAddress(address.base, address.offset); + + move(TrustedImm32(address.offset), addressTempRegister); + return ArmAddress(address.base, addressTempRegister); + } + + ArmAddress setupArmAddress(ImplicitAddress address) + { + if ((address.offset >= -0xff) && (address.offset <= 0xfff)) + return ArmAddress(address.base, address.offset); + + move(TrustedImm32(address.offset), addressTempRegister); + return ArmAddress(address.base, addressTempRegister); + } + + RegisterID makeBaseIndexBase(BaseIndex address) + { + if (!address.offset) + return address.base; + + ARMThumbImmediate imm = ARMThumbImmediate::makeUInt12OrEncodedImm(address.offset); + if (imm.isValid()) + m_assembler.add(addressTempRegister, address.base, imm); + else { + move(TrustedImm32(address.offset), addressTempRegister); + m_assembler.add(addressTempRegister, addressTempRegister, address.base); + } + + return addressTempRegister; + } + + void moveFixedWidthEncoding(TrustedImm32 imm, RegisterID dst) + { + uint32_t value = imm.m_value; + m_assembler.movT3(dst, ARMThumbImmediate::makeUInt16(value & 0xffff)); + m_assembler.movt(dst, ARMThumbImmediate::makeUInt16(value >> 16)); + } + + ARMv7Assembler::Condition armV7Condition(RelationalCondition cond) + { + return static_cast(cond); + } + + ARMv7Assembler::Condition armV7Condition(ResultCondition cond) + { + return static_cast(cond); + } + + ARMv7Assembler::Condition armV7Condition(DoubleCondition cond) + { + return static_cast(cond); + } + +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + ARMv7Assembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + bool m_makeJumpPatchable; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerARMv7_h diff --git a/masm/assembler/MacroAssemblerCodeRef.h b/masm/assembler/MacroAssemblerCodeRef.h new file mode 100644 index 0000000000..c2af24060a --- /dev/null +++ b/masm/assembler/MacroAssemblerCodeRef.h @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2009, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerCodeRef_h +#define MacroAssemblerCodeRef_h + +#include "Disassembler.h" +#include "ExecutableAllocator.h" +#include "LLIntData.h" +#include +#include +#include +#include + +// ASSERT_VALID_CODE_POINTER checks that ptr is a non-null pointer, and that it is a valid +// instruction address on the platform (for example, check any alignment requirements). +#if CPU(ARM_THUMB2) +// ARM/thumb instructions must be 16-bit aligned, but all code pointers to be loaded +// into the processor are decorated with the bottom bit set, indicating that this is +// thumb code (as oposed to 32-bit traditional ARM). The first test checks for both +// decorated and undectorated null, and the second test ensures that the pointer is +// decorated. +#define ASSERT_VALID_CODE_POINTER(ptr) \ + ASSERT(reinterpret_cast(ptr) & ~1); \ + ASSERT(reinterpret_cast(ptr) & 1) +#define ASSERT_VALID_CODE_OFFSET(offset) \ + ASSERT(!(offset & 1)) // Must be multiple of 2. +#else +#define ASSERT_VALID_CODE_POINTER(ptr) \ + ASSERT(ptr) +#define ASSERT_VALID_CODE_OFFSET(offset) // Anything goes! +#endif + +#if CPU(X86) && OS(WINDOWS) +#define CALLING_CONVENTION_IS_STDCALL 1 +#ifndef CDECL +#if COMPILER(MSVC) +#define CDECL __cdecl +#else +#define CDECL __attribute__ ((__cdecl)) +#endif // COMPILER(MSVC) +#endif // CDECL +#else +#define CALLING_CONVENTION_IS_STDCALL 0 +#endif + +#if CPU(X86) +#define HAS_FASTCALL_CALLING_CONVENTION 1 +#ifndef FASTCALL +#if COMPILER(MSVC) +#define FASTCALL __fastcall +#else +#define FASTCALL __attribute__ ((fastcall)) +#endif // COMPILER(MSVC) +#endif // FASTCALL +#else +#define HAS_FASTCALL_CALLING_CONVENTION 0 +#endif // CPU(X86) + +namespace JSC { + +// FunctionPtr: +// +// FunctionPtr should be used to wrap pointers to C/C++ functions in JSC +// (particularly, the stub functions). +class FunctionPtr { +public: + FunctionPtr() + : m_value(0) + { + } + + template + FunctionPtr(returnType(*value)()) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1, argType2)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1, argType2, argType3)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1, argType2, argType3, argType4)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1, argType2, argType3, argType4, argType5)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + +// MSVC doesn't seem to treat functions with different calling conventions as +// different types; these methods already defined for fastcall, below. +#if CALLING_CONVENTION_IS_STDCALL && !OS(WINDOWS) + + template + FunctionPtr(returnType (CDECL *value)()) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (CDECL *value)(argType1)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (CDECL *value)(argType1, argType2)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (CDECL *value)(argType1, argType2, argType3)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (CDECL *value)(argType1, argType2, argType3, argType4)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } +#endif + +#if HAS_FASTCALL_CALLING_CONVENTION + + template + FunctionPtr(returnType (FASTCALL *value)()) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (FASTCALL *value)(argType1)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (FASTCALL *value)(argType1, argType2)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (FASTCALL *value)(argType1, argType2, argType3)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (FASTCALL *value)(argType1, argType2, argType3, argType4)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } +#endif + + template + explicit FunctionPtr(FunctionType* value) + // Using a C-ctyle cast here to avoid compiler error on RVTC: + // Error: #694: reinterpret_cast cannot cast away const or other type qualifiers + // (I guess on RVTC function pointers have a different constness to GCC/MSVC?) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + void* value() const { return m_value; } + void* executableAddress() const { return m_value; } + + +private: + void* m_value; +}; + +// ReturnAddressPtr: +// +// ReturnAddressPtr should be used to wrap return addresses generated by processor +// 'call' instructions exectued in JIT code. We use return addresses to look up +// exception and optimization information, and to repatch the call instruction +// that is the source of the return address. +class ReturnAddressPtr { +public: + ReturnAddressPtr() + : m_value(0) + { + } + + explicit ReturnAddressPtr(void* value) + : m_value(value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + explicit ReturnAddressPtr(FunctionPtr function) + : m_value(function.value()) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + void* value() const { return m_value; } + +private: + void* m_value; +}; + +// MacroAssemblerCodePtr: +// +// MacroAssemblerCodePtr should be used to wrap pointers to JIT generated code. +class MacroAssemblerCodePtr { +public: + MacroAssemblerCodePtr() + : m_value(0) + { + } + + explicit MacroAssemblerCodePtr(void* value) +#if CPU(ARM_THUMB2) + // Decorate the pointer as a thumb code pointer. + : m_value(reinterpret_cast(value) + 1) +#else + : m_value(value) +#endif + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + static MacroAssemblerCodePtr createFromExecutableAddress(void* value) + { + ASSERT_VALID_CODE_POINTER(value); + MacroAssemblerCodePtr result; + result.m_value = value; + return result; + } + +#if ENABLE(LLINT) + static MacroAssemblerCodePtr createLLIntCodePtr(LLIntCode codeId) + { + return createFromExecutableAddress(LLInt::getCodePtr(codeId)); + } +#endif + + explicit MacroAssemblerCodePtr(ReturnAddressPtr ra) + : m_value(ra.value()) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + void* executableAddress() const { return m_value; } +#if CPU(ARM_THUMB2) + // To use this pointer as a data address remove the decoration. + void* dataLocation() const { ASSERT_VALID_CODE_POINTER(m_value); return reinterpret_cast(m_value) - 1; } +#else + void* dataLocation() const { ASSERT_VALID_CODE_POINTER(m_value); return m_value; } +#endif + + bool operator!() const + { + return !m_value; + } + +private: + void* m_value; +}; + +// MacroAssemblerCodeRef: +// +// A reference to a section of JIT generated code. A CodeRef consists of a +// pointer to the code, and a ref pointer to the pool from within which it +// was allocated. +class MacroAssemblerCodeRef { +private: + // This is private because it's dangerous enough that we want uses of it + // to be easy to find - hence the static create method below. + explicit MacroAssemblerCodeRef(MacroAssemblerCodePtr codePtr) + : m_codePtr(codePtr) + { + ASSERT(m_codePtr); + } + +public: + MacroAssemblerCodeRef() + { + } + + MacroAssemblerCodeRef(PassRefPtr executableMemory) + : m_codePtr(executableMemory->start()) + , m_executableMemory(executableMemory) + { + ASSERT(m_executableMemory->isManaged()); + ASSERT(m_executableMemory->start()); + ASSERT(m_codePtr); + } + + // Use this only when you know that the codePtr refers to code that is + // already being kept alive through some other means. Typically this means + // that codePtr is immortal. + static MacroAssemblerCodeRef createSelfManagedCodeRef(MacroAssemblerCodePtr codePtr) + { + return MacroAssemblerCodeRef(codePtr); + } + +#if ENABLE(LLINT) + // Helper for creating self-managed code refs from LLInt. + static MacroAssemblerCodeRef createLLIntCodeRef(LLIntCode codeId) + { + return createSelfManagedCodeRef(MacroAssemblerCodePtr::createFromExecutableAddress(LLInt::getCodePtr(codeId))); + } +#endif + + ExecutableMemoryHandle* executableMemory() const + { + return m_executableMemory.get(); + } + + MacroAssemblerCodePtr code() const + { + return m_codePtr; + } + + size_t size() const + { + if (!m_executableMemory) + return 0; + return m_executableMemory->sizeInBytes(); + } + + bool tryToDisassemble(const char* prefix) const + { + return JSC::tryToDisassemble(m_codePtr, size(), prefix, WTF::dataFile()); + } + + bool operator!() const { return !m_codePtr; } + +private: + MacroAssemblerCodePtr m_codePtr; + RefPtr m_executableMemory; +}; + +} // namespace JSC + +#endif // MacroAssemblerCodeRef_h diff --git a/masm/assembler/MacroAssemblerMIPS.h b/masm/assembler/MacroAssemblerMIPS.h new file mode 100644 index 0000000000..8b3ce9f034 --- /dev/null +++ b/masm/assembler/MacroAssemblerMIPS.h @@ -0,0 +1,1943 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY MIPS TECHNOLOGIES, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MIPS TECHNOLOGIES, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerMIPS_h +#define MacroAssemblerMIPS_h + +#if ENABLE(ASSEMBLER) && CPU(MIPS) + +#include "MIPSAssembler.h" +#include "AbstractMacroAssembler.h" + +namespace JSC { + +class MacroAssemblerMIPS : public AbstractMacroAssembler { +public: + typedef MIPSRegisters::FPRegisterID FPRegisterID; + + MacroAssemblerMIPS() + : m_fixedWidth(false) + { + } + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return value >= -2147483647 - 1 && value <= 2147483647; + } + + static const Scale ScalePtr = TimesFour; + + // For storing immediate number + static const RegisterID immTempRegister = MIPSRegisters::t0; + // For storing data loaded from the memory + static const RegisterID dataTempRegister = MIPSRegisters::t1; + // For storing address base + static const RegisterID addrTempRegister = MIPSRegisters::t2; + // For storing compare result + static const RegisterID cmpTempRegister = MIPSRegisters::t3; + + // FP temp register + static const FPRegisterID fpTempRegister = MIPSRegisters::f16; + + static const int MaximumCompactPtrAlignedAddressOffset = 0x7FFFFFFF; + + enum RelationalCondition { + Equal, + NotEqual, + Above, + AboveOrEqual, + Below, + BelowOrEqual, + GreaterThan, + GreaterThanOrEqual, + LessThan, + LessThanOrEqual + }; + + enum ResultCondition { + Overflow, + Signed, + Zero, + NonZero + }; + + enum DoubleCondition { + DoubleEqual, + DoubleNotEqual, + DoubleGreaterThan, + DoubleGreaterThanOrEqual, + DoubleLessThan, + DoubleLessThanOrEqual, + DoubleEqualOrUnordered, + DoubleNotEqualOrUnordered, + DoubleGreaterThanOrUnordered, + DoubleGreaterThanOrEqualOrUnordered, + DoubleLessThanOrUnordered, + DoubleLessThanOrEqualOrUnordered + }; + + static const RegisterID stackPointerRegister = MIPSRegisters::sp; + static const RegisterID returnAddressRegister = MIPSRegisters::ra; + + // Integer arithmetic operations: + // + // Operations are typically two operand - operation(source, srcDst) + // For many operations the source may be an TrustedImm32, the srcDst operand + // may often be a memory location (explictly described using an Address + // object). + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.addu(dest, dest, src); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + add32(imm, dest, dest); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (imm.m_value >= -32768 && imm.m_value <= 32767 + && !m_fixedWidth) { + /* + addiu dest, src, imm + */ + m_assembler.addiu(dest, src, imm.m_value); + } else { + /* + li immTemp, imm + addu dest, src, immTemp + */ + move(imm, immTempRegister); + m_assembler.addu(dest, src, immTempRegister); + } + } + + void add32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + add32(imm, src, dest); + } + + void add32(TrustedImm32 imm, Address address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + li immTemp, imm + addu dataTemp, dataTemp, immTemp + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, address.base, address.offset); + if (imm.m_value >= -32768 && imm.m_value <= 32767 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, + imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, + immTempRegister); + } + m_assembler.sw(dataTempRegister, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + li immtemp, imm + addu dataTemp, dataTemp, immTemp + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); + + if (imm.m_value >= -32768 && imm.m_value <= 32767 && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, + imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, + immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); + } + } + + void add32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + add32(dataTempRegister, dest); + } + + void add32(AbsoluteAddress src, RegisterID dest) + { + load32(src.m_ptr, dataTempRegister); + add32(dataTempRegister, dest); + } + + void add32(RegisterID src, Address dest) + { + if (dest.offset >= -32768 && dest.offset <= 32767 && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + addu dataTemp, dataTemp, src + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, dest.base, dest.offset); + m_assembler.addu(dataTempRegister, dataTempRegister, src); + m_assembler.sw(dataTempRegister, dest.base, dest.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + addu dataTemp, dataTemp, src + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (dest.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, dest.base); + m_assembler.lw(dataTempRegister, addrTempRegister, dest.offset); + m_assembler.addu(dataTempRegister, dataTempRegister, src); + m_assembler.sw(dataTempRegister, addrTempRegister, dest.offset); + } + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + /* + li addrTemp, address + li immTemp, imm + lw dataTemp, 0(addrTemp) + addu dataTemp, dataTemp, immTemp + sw dataTemp, 0(addrTemp) + */ + move(TrustedImmPtr(address.m_ptr), addrTempRegister); + m_assembler.lw(dataTempRegister, addrTempRegister, 0); + if (imm.m_value >= -32768 && imm.m_value <= 32767 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, 0); + } + + void and32(RegisterID src, RegisterID dest) + { + m_assembler.andInsn(dest, dest, src); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (imm.m_value > 0 && imm.m_value < 65535 + && !m_fixedWidth) + m_assembler.andi(dest, dest, imm.m_value); + else { + /* + li immTemp, imm + and dest, dest, immTemp + */ + move(imm, immTempRegister); + m_assembler.andInsn(dest, dest, immTempRegister); + } + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.sll(dest, dest, imm.m_value); + } + + void lshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.sllv(dest, dest, shiftAmount); + } + + void mul32(RegisterID src, RegisterID dest) + { + m_assembler.mul(dest, dest, src); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (!imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (imm.m_value == 1 && !m_fixedWidth) + move(src, dest); + else { + /* + li dataTemp, imm + mul dest, src, dataTemp + */ + move(imm, dataTempRegister); + m_assembler.mul(dest, src, dataTempRegister); + } + } + + void neg32(RegisterID srcDest) + { + m_assembler.subu(srcDest, MIPSRegisters::zero, srcDest); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orInsn(dest, dest, src); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.orInsn(dest, op1, op2); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_value && !m_fixedWidth) + return; + + if (imm.m_value > 0 && imm.m_value < 65535 + && !m_fixedWidth) { + m_assembler.ori(dest, dest, imm.m_value); + return; + } + + /* + li dataTemp, imm + or dest, dest, dataTemp + */ + move(imm, dataTempRegister); + m_assembler.orInsn(dest, dest, dataTempRegister); + } + + void rshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.srav(dest, dest, shiftAmount); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.sra(dest, dest, imm.m_value); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.sra(dest, src, imm.m_value); + } + + void urshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.srlv(dest, dest, shiftAmount); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.srl(dest, dest, imm.m_value); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.subu(dest, dest, src); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) { + /* + addiu dest, src, imm + */ + m_assembler.addiu(dest, dest, -imm.m_value); + } else { + /* + li immTemp, imm + subu dest, src, immTemp + */ + move(imm, immTempRegister); + m_assembler.subu(dest, dest, immTempRegister); + } + } + + void sub32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) { + /* + addiu dest, src, imm + */ + m_assembler.addiu(dest, src, -imm.m_value); + } else { + /* + li immTemp, imm + subu dest, src, immTemp + */ + move(imm, immTempRegister); + m_assembler.subu(dest, src, immTempRegister); + } + } + + void sub32(TrustedImm32 imm, Address address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + li immTemp, imm + subu dataTemp, dataTemp, immTemp + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, address.base, address.offset); + if (imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, + -imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, + immTempRegister); + } + m_assembler.sw(dataTempRegister, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + li immtemp, imm + subu dataTemp, dataTemp, immTemp + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); + + if (imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, + -imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, + immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); + } + } + + void sub32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + sub32(dataTempRegister, dest); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + /* + li addrTemp, address + li immTemp, imm + lw dataTemp, 0(addrTemp) + subu dataTemp, dataTemp, immTemp + sw dataTemp, 0(addrTemp) + */ + move(TrustedImmPtr(address.m_ptr), addrTempRegister); + m_assembler.lw(dataTempRegister, addrTempRegister, 0); + + if (imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) { + m_assembler.addiu(dataTempRegister, dataTempRegister, + -imm.m_value); + } else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, 0); + } + + void xor32(RegisterID src, RegisterID dest) + { + m_assembler.xorInsn(dest, dest, src); + } + + void xor32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value == -1) { + m_assembler.nor(dest, dest, MIPSRegisters::zero); + return; + } + + /* + li immTemp, imm + xor dest, dest, immTemp + */ + move(imm, immTempRegister); + m_assembler.xorInsn(dest, dest, immTempRegister); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.sqrtd(dst, src); + } + + void absDouble(FPRegisterID, FPRegisterID) + { + ASSERT_NOT_REACHED(); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result(this); + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, address.offset); + return result; + } + + // Memory access operations: + // + // Loads are of the form load(address, destination) and stores of the form + // store(source, address). The source for a store may be an TrustedImm32. Address + // operand objects to loads and store will be implicitly constructed if a + // register is passed. + + /* Need to use zero-extened load byte for load8. */ + void load8(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lbu(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lbu dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lbu(dest, addrTempRegister, address.offset); + } + } + + void load8(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lbu dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lbu(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lbu dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.lbu(dest, addrTempRegister, address.offset); + } + } + + void load32(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lw(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, address.offset); + } + } + + void load32(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lw dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lw dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.lw(dest, addrTempRegister, address.offset); + } + } + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + load16(address, dest); + } + + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32764 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + (Big-Endian) + lwl dest, address.offset(addrTemp) + lwr dest, address.offset+3(addrTemp) + (Little-Endian) + lwl dest, address.offset+3(addrTemp) + lwr dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); +#if CPU(BIG_ENDIAN) + m_assembler.lwl(dest, addrTempRegister, address.offset); + m_assembler.lwr(dest, addrTempRegister, address.offset + 3); +#else + m_assembler.lwl(dest, addrTempRegister, address.offset + 3); + m_assembler.lwr(dest, addrTempRegister, address.offset); + +#endif + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, address.offset >> 16 + ori immTemp, immTemp, address.offset & 0xffff + addu addrTemp, addrTemp, immTemp + (Big-Endian) + lw dest, 0(at) + lw dest, 3(at) + (Little-Endian) + lw dest, 3(at) + lw dest, 0(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, address.offset >> 16); + m_assembler.ori(immTempRegister, immTempRegister, address.offset); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); +#if CPU(BIG_ENDIAN) + m_assembler.lwl(dest, addrTempRegister, 0); + m_assembler.lwr(dest, addrTempRegister, 3); +#else + m_assembler.lwl(dest, addrTempRegister, 3); + m_assembler.lwr(dest, addrTempRegister, 0); +#endif + } + } + + void load32(const void* address, RegisterID dest) + { + /* + li addrTemp, address + lw dest, 0(addrTemp) + */ + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.lw(dest, addrTempRegister, 0); + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + lw dest, 0(addrTemp) + */ + DataLabel32 dataLabel(this); + move(TrustedImm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, 0); + m_fixedWidth = false; + return dataLabel; + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabelCompact dataLabel(this); + load32WithAddressOffsetPatch(address, dest); + return dataLabel; + } + + /* Need to use zero-extened load half-word for load16. */ + void load16(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lhu(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lhu dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } + } + + /* Need to use zero-extened load half-word for load16. */ + void load16(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lhu dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lhu dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + sw src, 0(addrTemp) + */ + DataLabel32 dataLabel(this); + move(TrustedImm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, 0); + m_fixedWidth = false; + return dataLabel; + } + + void store32(RegisterID src, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.sw(src, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sw src, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, address.offset); + } + } + + void store32(RegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sw src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sw src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, + immTempRegister); + m_assembler.sw(src, addrTempRegister, address.offset); + } + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + if (!imm.m_value) + m_assembler.sw(MIPSRegisters::zero, address.base, + address.offset); + else { + move(imm, immTempRegister); + m_assembler.sw(immTempRegister, address.base, address.offset); + } + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sw immTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + if (!imm.m_value && !m_fixedWidth) + m_assembler.sw(MIPSRegisters::zero, addrTempRegister, + address.offset); + else { + move(imm, immTempRegister); + m_assembler.sw(immTempRegister, addrTempRegister, + address.offset); + } + } + } + + void store32(RegisterID src, const void* address) + { + /* + li addrTemp, address + sw src, 0(addrTemp) + */ + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sw(src, addrTempRegister, 0); + } + + void store32(TrustedImm32 imm, const void* address) + { + /* + li immTemp, imm + li addrTemp, address + sw src, 0(addrTemp) + */ + if (!imm.m_value && !m_fixedWidth) { + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sw(MIPSRegisters::zero, addrTempRegister, 0); + } else { + move(imm, immTempRegister); + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sw(immTempRegister, addrTempRegister, 0); + } + } + + // Floating-point operations: + + static bool supportsFloatingPoint() + { +#if WTF_MIPS_DOUBLE_FLOAT + return true; +#else + return false; +#endif + } + + static bool supportsFloatingPointTruncate() + { +#if WTF_MIPS_DOUBLE_FLOAT && WTF_MIPS_ISA_AT_LEAST(2) + return true; +#else + return false; +#endif + } + + static bool supportsFloatingPointSqrt() + { +#if WTF_MIPS_DOUBLE_FLOAT && WTF_MIPS_ISA_AT_LEAST(2) + return true; +#else + return false; +#endif + } + static bool supportsFloatingPointAbs() { return false; } + + // Stack manipulation operations: + // + // The ABI is assumed to provide a stack abstraction to memory, + // containing machine word sized units of data. Push and pop + // operations add and remove a single register sized unit of data + // to or from the stack. Peek and poke operations read or write + // values on the stack, without moving the current stack position. + + void pop(RegisterID dest) + { + m_assembler.lw(dest, MIPSRegisters::sp, 0); + m_assembler.addiu(MIPSRegisters::sp, MIPSRegisters::sp, 4); + } + + void push(RegisterID src) + { + m_assembler.addiu(MIPSRegisters::sp, MIPSRegisters::sp, -4); + m_assembler.sw(src, MIPSRegisters::sp, 0); + } + + void push(Address address) + { + load32(address, dataTempRegister); + push(dataTempRegister); + } + + void push(TrustedImm32 imm) + { + move(imm, immTempRegister); + push(immTempRegister); + } + + // Register move operations: + // + // Move values in registers. + + void move(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (m_fixedWidth) { + m_assembler.lui(dest, imm.m_value >> 16); + m_assembler.ori(dest, dest, imm.m_value); + } else + m_assembler.li(dest, imm.m_value); + } + + void move(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + m_assembler.move(dest, src); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + move(TrustedImm32(imm), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + move(reg1, immTempRegister); + move(reg2, reg1); + move(immTempRegister, reg2); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + move(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + move(src, dest); + } + + // Forwards / external control flow operations: + // + // This set of jump and conditional branch operations return a Jump + // object which may linked at a later point, allow forwards jump, + // or jumps that will require external linkage (after the code has been + // relocated). + // + // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge + // respecitvely, for unsigned comparisons the names b, a, be, and ae are + // used (representing the names 'below' and 'above'). + // + // Operands to the comparision are provided in the expected order, e.g. + // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when + // treated as a signed 32bit value, is less than or equal to 5. + // + // jz and jnz test whether the first operand is equal to zero, and take + // an optional second operand of a mask under which to perform the test. + + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + // Make sure the immediate value is unsigned 8 bits. + ASSERT(!(right.m_value & 0xFFFFFF00)); + load8(left, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + // Make sure the immediate value is unsigned 8 bits. + ASSERT(!(right.m_value & 0xFFFFFF00)); + load8(left, dataTempRegister); + move(right, immTempRegister); + compare32(cond, dataTempRegister, immTempRegister, dest); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(right.m_value & 0xFFFFFF00)); + load8(left, dataTempRegister); + // Be careful that the previous load8() uses immTempRegister. + // So, we need to put move() after load8(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) + { + if (cond == Equal) + return branchEqual(left, right); + if (cond == NotEqual) + return branchNotEqual(left, right); + if (cond == Above) { + m_assembler.sltu(cmpTempRegister, right, left); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == AboveOrEqual) { + m_assembler.sltu(cmpTempRegister, left, right); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Below) { + m_assembler.sltu(cmpTempRegister, left, right); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == BelowOrEqual) { + m_assembler.sltu(cmpTempRegister, right, left); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == GreaterThan) { + m_assembler.slt(cmpTempRegister, right, left); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == GreaterThanOrEqual) { + m_assembler.slt(cmpTempRegister, left, right); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == LessThan) { + m_assembler.slt(cmpTempRegister, left, right); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == LessThanOrEqual) { + m_assembler.slt(cmpTempRegister, right, left); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + ASSERT(0); + + return Jump(); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + move(right, immTempRegister); + return branch32(cond, left, immTempRegister); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + load32(right, dataTempRegister); + return branch32(cond, left, dataTempRegister); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + load32(left, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + load32(left, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + load32(left, dataTempRegister); + // Be careful that the previous load32() uses immTempRegister. + // So, we need to put move() after load32(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + load32WithUnalignedHalfWords(left, dataTempRegister); + // Be careful that the previous load32WithUnalignedHalfWords() + // uses immTempRegister. + // So, we need to put move() after load32WithUnalignedHalfWords(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + load32(left.m_ptr, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + load32(left.m_ptr, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + ASSERT((cond == Zero) || (cond == NonZero)); + m_assembler.andInsn(cmpTempRegister, reg, mask); + if (cond == Zero) + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + return branchEqual(reg, MIPSRegisters::zero); + return branchNotEqual(reg, MIPSRegisters::zero); + } + move(mask, immTempRegister); + return branchTest32(cond, reg, immTempRegister); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + load32(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + load32(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + load8(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + move(TrustedImmPtr(address.m_ptr), dataTempRegister); + load8(Address(dataTempRegister), dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump jump() + { + return branchEqual(MIPSRegisters::zero, MIPSRegisters::zero); + } + + void jump(RegisterID target) + { + m_assembler.jr(target); + m_assembler.nop(); + } + + void jump(Address address) + { + m_fixedWidth = true; + load32(address, MIPSRegisters::t9); + m_assembler.jr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + } + + // Arithmetic control flow operations: + // + // This set of conditional branch operations branch based + // on the result of an arithmetic operation. The operation + // is performed as normal, storing the result. + // + // * jz operations branch if the result is zero. + // * jo operations branch if the (signed) arithmetic + // operation caused an overflow to occur. + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + move dest, dataTemp + xor cmpTemp, dataTemp, src + bltz cmpTemp, No_overflow # diff sign bit -> no overflow + addu dest, dataTemp, src + xor cmpTemp, dest, dataTemp + bgez cmpTemp, No_overflow # same sign big -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + move(dest, dataTempRegister); + m_assembler.xorInsn(cmpTempRegister, dataTempRegister, src); + m_assembler.bltz(cmpTempRegister, 10); + m_assembler.addu(dest, dataTempRegister, src); + m_assembler.xorInsn(cmpTempRegister, dest, dataTempRegister); + m_assembler.bgez(cmpTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + add32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + add32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + add32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + return branchAdd32(cond, immTempRegister, dest); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + move(src, dest); + return branchAdd32(cond, immTempRegister, dest); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + mult src, dest + mfhi dataTemp + mflo dest + sra addrTemp, dest, 31 + beq dataTemp, addrTemp, No_overflow # all sign bits (bit 63 to bit 31) are the same -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + m_assembler.mult(src, dest); + m_assembler.mfhi(dataTempRegister); + m_assembler.mflo(dest); + m_assembler.sra(addrTempRegister, dest, 31); + m_assembler.beq(dataTempRegister, addrTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + mul32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + mul32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + mul32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(imm, immTempRegister); + move(src, dest); + return branchMul32(cond, immTempRegister, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + move dest, dataTemp + xor cmpTemp, dataTemp, src + bgez cmpTemp, No_overflow # same sign bit -> no overflow + subu dest, dataTemp, src + xor cmpTemp, dest, dataTemp + bgez cmpTemp, No_overflow # same sign bit -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + move(dest, dataTempRegister); + m_assembler.xorInsn(cmpTempRegister, dataTempRegister, src); + m_assembler.bgez(cmpTempRegister, 10); + m_assembler.subu(dest, dataTempRegister, src); + m_assembler.xorInsn(cmpTempRegister, dest, dataTempRegister); + m_assembler.bgez(cmpTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + sub32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + sub32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + sub32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + return branchSub32(cond, immTempRegister, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + move(src, dest); + return branchSub32(cond, immTempRegister, dest); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Signed) { + or32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + or32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + or32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + // Miscellaneous operations: + + void breakpoint() + { + m_assembler.bkpt(); + } + + Call nearCall() + { + /* We need two words for relaxation. */ + m_assembler.nop(); + m_assembler.nop(); + m_assembler.jal(); + m_assembler.nop(); + return Call(m_assembler.label(), Call::LinkableNear); + } + + Call call() + { + m_assembler.lui(MIPSRegisters::t9, 0); + m_assembler.ori(MIPSRegisters::t9, MIPSRegisters::t9, 0); + m_assembler.jalr(MIPSRegisters::t9); + m_assembler.nop(); + return Call(m_assembler.label(), Call::Linkable); + } + + Call call(RegisterID target) + { + m_assembler.jalr(target); + m_assembler.nop(); + return Call(m_assembler.label(), Call::None); + } + + Call call(Address address) + { + m_fixedWidth = true; + load32(address, MIPSRegisters::t9); + m_assembler.jalr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + return Call(m_assembler.label(), Call::None); + } + + void ret() + { + m_assembler.jr(MIPSRegisters::ra); + m_assembler.nop(); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + if (cond == Equal) { + m_assembler.xorInsn(dest, left, right); + m_assembler.sltiu(dest, dest, 1); + } else if (cond == NotEqual) { + m_assembler.xorInsn(dest, left, right); + m_assembler.sltu(dest, MIPSRegisters::zero, dest); + } else if (cond == Above) + m_assembler.sltu(dest, right, left); + else if (cond == AboveOrEqual) { + m_assembler.sltu(dest, left, right); + m_assembler.xori(dest, dest, 1); + } else if (cond == Below) + m_assembler.sltu(dest, left, right); + else if (cond == BelowOrEqual) { + m_assembler.sltu(dest, right, left); + m_assembler.xori(dest, dest, 1); + } else if (cond == GreaterThan) + m_assembler.slt(dest, right, left); + else if (cond == GreaterThanOrEqual) { + m_assembler.slt(dest, left, right); + m_assembler.xori(dest, dest, 1); + } else if (cond == LessThan) + m_assembler.slt(dest, left, right); + else if (cond == LessThanOrEqual) { + m_assembler.slt(dest, right, left); + m_assembler.xori(dest, dest, 1); + } + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + move(right, immTempRegister); + compare32(cond, left, immTempRegister, dest); + } + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + ASSERT((cond == Zero) || (cond == NonZero)); + load8(address, dataTempRegister); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + m_assembler.sltiu(dest, dataTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); + } else { + move(mask, immTempRegister); + m_assembler.andInsn(cmpTempRegister, dataTempRegister, + immTempRegister); + if (cond == Zero) + m_assembler.sltiu(dest, cmpTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, cmpTempRegister); + } + } + + void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + ASSERT((cond == Zero) || (cond == NonZero)); + load32(address, dataTempRegister); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + m_assembler.sltiu(dest, dataTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); + } else { + move(mask, immTempRegister); + m_assembler.andInsn(cmpTempRegister, dataTempRegister, + immTempRegister); + if (cond == Zero) + m_assembler.sltiu(dest, cmpTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, cmpTempRegister); + } + } + + DataLabel32 moveWithPatch(TrustedImm32 imm, RegisterID dest) + { + m_fixedWidth = true; + DataLabel32 label(this); + move(imm, dest); + m_fixedWidth = false; + return label; + } + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + m_fixedWidth = true; + DataLabelPtr label(this); + move(initialValue, dest); + m_fixedWidth = false; + return label; + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + m_fixedWidth = true; + dataLabel = moveWithPatch(initialRightValue, immTempRegister); + Jump temp = branch32(cond, left, immTempRegister); + m_fixedWidth = false; + return temp; + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + m_fixedWidth = true; + load32(left, dataTempRegister); + dataLabel = moveWithPatch(initialRightValue, immTempRegister); + Jump temp = branch32(cond, dataTempRegister, immTempRegister); + m_fixedWidth = false; + return temp; + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + m_fixedWidth = true; + DataLabelPtr dataLabel = moveWithPatch(initialValue, dataTempRegister); + store32(dataTempRegister, address); + m_fixedWidth = false; + return dataLabel; + } + + DataLabelPtr storePtrWithPatch(ImplicitAddress address) + { + return storePtrWithPatch(TrustedImmPtr(0), address); + } + + Call tailRecursiveCall() + { + // Like a normal call, but don't update the returned address register + m_fixedWidth = true; + move(TrustedImm32(0), MIPSRegisters::t9); + m_assembler.jr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + return Call(m_assembler.label(), Call::Linkable); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + oldJump.link(this); + return tailRecursiveCall(); + } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { +#if WTF_MIPS_ISA(1) + /* + li addrTemp, address.offset + addu addrTemp, addrTemp, base + lwc1 dest, 0(addrTemp) + lwc1 dest+1, 4(addrTemp) + */ + move(TrustedImm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lwc1(dest, addrTempRegister, 0); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + m_assembler.ldc1(dest, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + ldc1 dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.ldc1(dest, addrTempRegister, address.offset); + } +#endif + } + + void loadDouble(const void* address, FPRegisterID dest) + { +#if WTF_MIPS_ISA(1) + /* + li addrTemp, address + lwc1 dest, 0(addrTemp) + lwc1 dest+1, 4(addrTemp) + */ + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.lwc1(dest, addrTempRegister, 0); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); +#else + /* + li addrTemp, address + ldc1 dest, 0(addrTemp) + */ + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.ldc1(dest, addrTempRegister, 0); +#endif + } + + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { +#if WTF_MIPS_ISA(1) + /* + li addrTemp, address.offset + addu addrTemp, addrTemp, base + swc1 dest, 0(addrTemp) + swc1 dest+1, 4(addrTemp) + */ + move(TrustedImm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.swc1(src, addrTempRegister, 0); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, 4); +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.sdc1(src, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sdc1 src, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sdc1(src, addrTempRegister, address.offset); + } +#endif + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.addd(dest, dest, src); + } + + void addDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.addd(dest, dest, fpTempRegister); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.subd(dest, dest, src); + } + + void subDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.subd(dest, dest, fpTempRegister); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.muld(dest, dest, src); + } + + void mulDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.muld(dest, dest, fpTempRegister); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.divd(dest, dest, src); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.mtc1(src, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + load32(src, dataTempRegister); + m_assembler.mtc1(dataTempRegister, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + load32(src.m_ptr, dataTempRegister); + m_assembler.mtc1(dataTempRegister, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void insertRelaxationWords() + { + /* We need four words for relaxation. */ + m_assembler.beq(MIPSRegisters::zero, MIPSRegisters::zero, 3); // Jump over nops; + m_assembler.nop(); + m_assembler.nop(); + m_assembler.nop(); + } + + Jump branchTrue() + { + m_assembler.appendJump(); + m_assembler.bc1t(); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.label()); + } + + Jump branchFalse() + { + m_assembler.appendJump(); + m_assembler.bc1f(); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.label()); + } + + Jump branchEqual(RegisterID rs, RegisterID rt) + { + m_assembler.appendJump(); + m_assembler.beq(rs, rt, 0); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.label()); + } + + Jump branchNotEqual(RegisterID rs, RegisterID rt) + { + m_assembler.appendJump(); + m_assembler.bne(rs, rt, 0); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.label()); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + if (cond == DoubleEqual) { + m_assembler.ceqd(left, right); + return branchTrue(); + } + if (cond == DoubleNotEqual) { + m_assembler.cueqd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThan) { + m_assembler.cngtd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrEqual) { + m_assembler.cnged(left, right); + return branchFalse(); // false + } + if (cond == DoubleLessThan) { + m_assembler.cltd(left, right); + return branchTrue(); + } + if (cond == DoubleLessThanOrEqual) { + m_assembler.cled(left, right); + return branchTrue(); + } + if (cond == DoubleEqualOrUnordered) { + m_assembler.cueqd(left, right); + return branchTrue(); + } + if (cond == DoubleNotEqualOrUnordered) { + m_assembler.ceqd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrUnordered) { + m_assembler.coled(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrEqualOrUnordered) { + m_assembler.coltd(left, right); + return branchFalse(); // false + } + if (cond == DoubleLessThanOrUnordered) { + m_assembler.cultd(left, right); + return branchTrue(); + } + if (cond == DoubleLessThanOrEqualOrUnordered) { + m_assembler.culed(left, right); + return branchTrue(); + } + ASSERT(0); + + return Jump(); + } + + // Truncates 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, INT_MAX 0x7fffffff). + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + m_assembler.truncwd(fpTempRegister, src); + m_assembler.mfc1(dest, fpTempRegister); + return branch32(Equal, dest, TrustedImm32(0x7fffffff)); + } + + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + m_assembler.cvtwd(fpTempRegister, src); + m_assembler.mfc1(dest, fpTempRegister); + + // If the result is zero, it might have been -0.0, and the double comparison won't catch this! + failureCases.append(branch32(Equal, dest, MIPSRegisters::zero)); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + convertInt32ToDouble(dest, fpTemp); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, fpTemp, src)); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) + { +#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 + m_assembler.mtc1(MIPSRegisters::zero, scratch); + m_assembler.mthc1(MIPSRegisters::zero, scratch); +#else + m_assembler.mtc1(MIPSRegisters::zero, scratch); + m_assembler.mtc1(MIPSRegisters::zero, FPRegisterID(scratch + 1)); +#endif + return branchDouble(DoubleNotEqual, reg, scratch); + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) + { +#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 + m_assembler.mtc1(MIPSRegisters::zero, scratch); + m_assembler.mthc1(MIPSRegisters::zero, scratch); +#else + m_assembler.mtc1(MIPSRegisters::zero, scratch); + m_assembler.mtc1(MIPSRegisters::zero, FPRegisterID(scratch + 1)); +#endif + return branchDouble(DoubleEqualOrUnordered, reg, scratch); + } + + void nop() + { + m_assembler.nop(); + } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(reinterpret_cast(MIPSAssembler::readCallTarget(call.dataLocation()))); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + ASSERT_NOT_REACHED(); + } + + static ptrdiff_t maxJumpReplacementSize() + { + ASSERT_NOT_REACHED(); + return 0; + } + +private: + // If m_fixedWidth is true, we will generate a fixed number of instructions. + // Otherwise, we can emit any number of instructions. + bool m_fixedWidth; + + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + MIPSAssembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + +}; + +} + +#endif // ENABLE(ASSEMBLER) && CPU(MIPS) + +#endif // MacroAssemblerMIPS_h diff --git a/masm/assembler/MacroAssemblerSH4.cpp b/masm/assembler/MacroAssemblerSH4.cpp new file mode 100644 index 0000000000..59de3ff48c --- /dev/null +++ b/masm/assembler/MacroAssemblerSH4.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 STMicroelectronics. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "config.h" + +#if ENABLE(ASSEMBLER) && CPU(SH4) + +#include "MacroAssemblerSH4.h" + +namespace JSC { + +void MacroAssemblerSH4::linkCall(void* code, Call call, FunctionPtr function) +{ + SH4Assembler::linkCall(code, call.m_label, function.value()); +} + +void MacroAssemblerSH4::repatchCall(CodeLocationCall call, CodeLocationLabel destination) +{ + SH4Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); +} + +void MacroAssemblerSH4::repatchCall(CodeLocationCall call, FunctionPtr destination) +{ + SH4Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); +} + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) diff --git a/masm/assembler/MacroAssemblerSH4.h b/masm/assembler/MacroAssemblerSH4.h new file mode 100644 index 0000000000..ca410afa8c --- /dev/null +++ b/masm/assembler/MacroAssemblerSH4.h @@ -0,0 +1,2044 @@ +/* + * Copyright (C) 2009-2011 STMicroelectronics. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MacroAssemblerSH4_h +#define MacroAssemblerSH4_h + +#if ENABLE(ASSEMBLER) && CPU(SH4) + +#include "SH4Assembler.h" +#include "AbstractMacroAssembler.h" +#include + +namespace JSC { + +class MacroAssemblerSH4 : public AbstractMacroAssembler { +public: + typedef SH4Assembler::FPRegisterID FPRegisterID; + + static const Scale ScalePtr = TimesFour; + static const FPRegisterID fscratch = SH4Registers::fr10; + static const RegisterID stackPointerRegister = SH4Registers::sp; + static const RegisterID linkRegister = SH4Registers::pr; + static const RegisterID scratchReg3 = SH4Registers::r13; + + static const int MaximumCompactPtrAlignedAddressOffset = 60; + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return (value >= 0) && (value <= MaximumCompactPtrAlignedAddressOffset); + } + + enum RelationalCondition { + Equal = SH4Assembler::EQ, + NotEqual = SH4Assembler::NE, + Above = SH4Assembler::HI, + AboveOrEqual = SH4Assembler::HS, + Below = SH4Assembler::LI, + BelowOrEqual = SH4Assembler::LS, + GreaterThan = SH4Assembler::GT, + GreaterThanOrEqual = SH4Assembler::GE, + LessThan = SH4Assembler::LT, + LessThanOrEqual = SH4Assembler::LE + }; + + enum ResultCondition { + Overflow = SH4Assembler::OF, + Signed = SH4Assembler::SI, + Zero = SH4Assembler::EQ, + NonZero = SH4Assembler::NE + }; + + enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleEqual = SH4Assembler::EQ, + DoubleNotEqual = SH4Assembler::NE, + DoubleGreaterThan = SH4Assembler::GT, + DoubleGreaterThanOrEqual = SH4Assembler::GE, + DoubleLessThan = SH4Assembler::LT, + DoubleLessThanOrEqual = SH4Assembler::LE, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = SH4Assembler::EQU, + DoubleNotEqualOrUnordered = SH4Assembler::NEU, + DoubleGreaterThanOrUnordered = SH4Assembler::GTU, + DoubleGreaterThanOrEqualOrUnordered = SH4Assembler::GEU, + DoubleLessThanOrUnordered = SH4Assembler::LTU, + DoubleLessThanOrEqualOrUnordered = SH4Assembler::LEU, + }; + + RegisterID claimScratch() + { + return m_assembler.claimScratch(); + } + + void releaseScratch(RegisterID reg) + { + m_assembler.releaseScratch(reg); + } + + // Integer arithmetic operations + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.addlRegReg(src, dest); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + if (m_assembler.isImmediate(imm.m_value)) { + m_assembler.addlImm8r(imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm.m_value, scr); + m_assembler.addlRegReg(scr, dest); + releaseScratch(scr); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.movlRegReg(src, dest); + add32(imm, dest); + } + + void add32(TrustedImm32 imm, Address address) + { + RegisterID scr = claimScratch(); + load32(address, scr); + add32(imm, scr); + store32(scr, address); + releaseScratch(scr); + } + + void add32(Address src, RegisterID dest) + { + RegisterID scr = claimScratch(); + load32(src, scr); + m_assembler.addlRegReg(scr, dest); + releaseScratch(scr); + } + + void add32(AbsoluteAddress src, RegisterID dest) + { + RegisterID scr = claimScratch(); + load32(src.m_ptr, scr); + m_assembler.addlRegReg(scr, dest); + releaseScratch(scr); + } + + void and32(RegisterID src, RegisterID dest) + { + m_assembler.andlRegReg(src, dest); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + if ((imm.m_value <= 255) && (imm.m_value >= 0) && (dest == SH4Registers::r0)) { + m_assembler.andlImm8r(imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((imm.m_value), scr); + m_assembler.andlRegReg(scr, dest); + releaseScratch(scr); + } + + void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (src != dest) { + move(imm, dest); + and32(src, dest); + return; + } + + and32(imm, dest); + } + + void lshift32(RegisterID shiftamount, RegisterID dest) + { + if (shiftamount == SH4Registers::r0) + m_assembler.andlImm8r(0x1f, shiftamount); + else { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(0x1f, scr); + m_assembler.andlRegReg(scr, shiftamount); + releaseScratch(scr); + } + m_assembler.shllRegReg(dest, shiftamount); + } + + void rshift32(int imm, RegisterID dest) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(-imm, scr); + m_assembler.shaRegReg(dest, scr); + releaseScratch(scr); + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_value) + return; + + if ((imm.m_value == 1) || (imm.m_value == 2) || (imm.m_value == 8) || (imm.m_value == 16)) { + m_assembler.shllImm8r(imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((imm.m_value & 0x1f) , scr); + m_assembler.shllRegReg(dest, scr); + releaseScratch(scr); + } + + void lshift32(RegisterID src, TrustedImm32 shiftamount, RegisterID dest) + { + if (src != dest) + move(src, dest); + + lshift32(shiftamount, dest); + } + + void mul32(RegisterID src, RegisterID dest) + { + m_assembler.imullRegReg(src, dest); + m_assembler.stsmacl(dest); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + RegisterID scr = claimScratch(); + move(imm, scr); + if (src != dest) + move(src, dest); + mul32(scr, dest); + releaseScratch(scr); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orlRegReg(src, dest); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + if ((imm.m_value <= 255) && (imm.m_value >= 0) && (dest == SH4Registers::r0)) { + m_assembler.orlImm8r(imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm.m_value, scr); + m_assembler.orlRegReg(scr, dest); + releaseScratch(scr); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + move(op1, dest); + else if (op1 == dest) + or32(op2, dest); + else { + move(op2, dest); + or32(op1, dest); + } + } + + +void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (src != dest) { + move(imm, dest); + or32(src, dest); + return; + } + + or32(imm, dest); + } + + void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (src != dest) { + move(imm, dest); + xor32(src, dest); + return; + } + + xor32(imm, dest); + } + + void rshift32(RegisterID shiftamount, RegisterID dest) + { + if (shiftamount == SH4Registers::r0) + m_assembler.andlImm8r(0x1f, shiftamount); + else { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(0x1f, scr); + m_assembler.andlRegReg(scr, shiftamount); + releaseScratch(scr); + } + m_assembler.neg(shiftamount, shiftamount); + m_assembler.shaRegReg(dest, shiftamount); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value & 0x1f) + rshift32(imm.m_value & 0x1f, dest); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (src != dest) + move(src, dest); + rshift32(imm, dest); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.sublRegReg(src, dest); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address, RegisterID scratchReg) + { + RegisterID result = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); + m_assembler.movlMemReg(scratchReg, result); + + if (m_assembler.isImmediate(-imm.m_value)) + m_assembler.addlImm8r(-imm.m_value, result); + else { + m_assembler.loadConstant(imm.m_value, scratchReg3); + m_assembler.sublRegReg(scratchReg3, result); + } + + store32(result, scratchReg); + releaseScratch(result); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + RegisterID result = claimScratch(); + RegisterID scratchReg = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); + m_assembler.movlMemReg(scratchReg, result); + + if (m_assembler.isImmediate(-imm.m_value)) + m_assembler.addlImm8r(-imm.m_value, result); + else { + m_assembler.loadConstant(imm.m_value, scratchReg3); + m_assembler.sublRegReg(scratchReg3, result); + } + + store32(result, scratchReg); + releaseScratch(result); + releaseScratch(scratchReg); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address, RegisterID scratchReg) + { + RegisterID result = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); + m_assembler.movlMemReg(scratchReg, result); + + if (m_assembler.isImmediate(imm.m_value)) + m_assembler.addlImm8r(imm.m_value, result); + else { + m_assembler.loadConstant(imm.m_value, scratchReg3); + m_assembler.addlRegReg(scratchReg3, result); + } + + store32(result, scratchReg); + releaseScratch(result); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + RegisterID result = claimScratch(); + RegisterID scratchReg = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); + m_assembler.movlMemReg(scratchReg, result); + + if (m_assembler.isImmediate(imm.m_value)) + m_assembler.addlImm8r(imm.m_value, result); + else { + m_assembler.loadConstant(imm.m_value, scratchReg3); + m_assembler.addlRegReg(scratchReg3, result); + } + + store32(result, scratchReg); + releaseScratch(result); + releaseScratch(scratchReg); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + if (m_assembler.isImmediate(-imm.m_value)) { + m_assembler.addlImm8r(-imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm.m_value, scr); + m_assembler.sublRegReg(scr, dest); + releaseScratch(scr); + } + + void sub32(Address src, RegisterID dest) + { + RegisterID scr = claimScratch(); + load32(src, scr); + m_assembler.sublRegReg(scr, dest); + releaseScratch(scr); + } + + void xor32(RegisterID src, RegisterID dest) + { + m_assembler.xorlRegReg(src, dest); + } + + void xor32(TrustedImm32 imm, RegisterID srcDest) + { + if (imm.m_value == -1) { + m_assembler.notlReg(srcDest, srcDest); + return; + } + + if ((srcDest != SH4Registers::r0) || (imm.m_value > 255) || (imm.m_value < 0)) { + RegisterID scr = claimScratch(); + m_assembler.loadConstant((imm.m_value), scr); + m_assembler.xorlRegReg(scr, srcDest); + releaseScratch(scr); + return; + } + + m_assembler.xorlImm8r(imm.m_value, srcDest); + } + + void compare32(int imm, RegisterID dst, RelationalCondition cond) + { + if (((cond == Equal) || (cond == NotEqual)) && (dst == SH4Registers::r0) && m_assembler.isImmediate(imm)) { + m_assembler.cmpEqImmR0(imm, dst); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm, scr); + m_assembler.cmplRegReg(scr, dst, SH4Condition(cond)); + releaseScratch(scr); + } + + void compare32(int offset, RegisterID base, RegisterID left, RelationalCondition cond) + { + RegisterID scr = claimScratch(); + if (!offset) { + m_assembler.movlMemReg(base, scr); + m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); + releaseScratch(scr); + return; + } + + if ((offset < 0) || (offset >= 64)) { + m_assembler.loadConstant(offset, scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, scr); + m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); + releaseScratch(scr); + return; + } + + m_assembler.movlMemReg(offset >> 2, base, scr); + m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); + releaseScratch(scr); + } + + void testImm(int imm, int offset, RegisterID base) + { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + + if ((offset < 0) || (offset >= 64)) { + m_assembler.loadConstant(offset, scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, scr); + } else if (offset) + m_assembler.movlMemReg(offset >> 2, base, scr); + else + m_assembler.movlMemReg(base, scr); + if (m_assembler.isImmediate(imm)) + m_assembler.movImm8(imm, scr1); + else + m_assembler.loadConstant(imm, scr1); + + m_assembler.testlRegReg(scr, scr1); + releaseScratch(scr); + releaseScratch(scr1); + } + + void testlImm(int imm, RegisterID dst) + { + if ((dst == SH4Registers::r0) && (imm <= 255) && (imm >= 0)) { + m_assembler.testlImm8r(imm, dst); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm, scr); + m_assembler.testlRegReg(scr, dst); + releaseScratch(scr); + } + + void compare32(RegisterID right, int offset, RegisterID base, RelationalCondition cond) + { + if (!offset) { + RegisterID scr = claimScratch(); + m_assembler.movlMemReg(base, scr); + m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); + releaseScratch(scr); + return; + } + + if ((offset < 0) || (offset >= 64)) { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(offset, scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, scr); + m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); + releaseScratch(scr); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.movlMemReg(offset >> 2, base, scr); + m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); + releaseScratch(scr); + } + + void compare32(int imm, int offset, RegisterID base, RelationalCondition cond) + { + if (!offset) { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.movlMemReg(base, scr); + m_assembler.loadConstant(imm, scr1); + m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); + releaseScratch(scr1); + releaseScratch(scr); + return; + } + + if ((offset < 0) || (offset >= 64)) { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.loadConstant(offset, scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, scr); + m_assembler.loadConstant(imm, scr1); + m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); + releaseScratch(scr1); + releaseScratch(scr); + return; + } + + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.movlMemReg(offset >> 2, base, scr); + m_assembler.loadConstant(imm, scr1); + m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); + releaseScratch(scr1); + releaseScratch(scr); + } + + // Memory access operation + + void load32(ImplicitAddress address, RegisterID dest) + { + load32(address.base, address.offset, dest); + } + + void load8(ImplicitAddress address, RegisterID dest) + { + load8(address.base, address.offset, dest); + } + + void load8(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + load8(scr, address.offset, dest); + releaseScratch(scr); + } + + void load32(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + load32(scr, address.offset, dest); + releaseScratch(scr); + } + + void load32(const void* address, RegisterID dest) + { + m_assembler.loadConstant(reinterpret_cast(const_cast(address)), dest); + m_assembler.movlMemReg(dest, dest); + } + + void load32(RegisterID base, int offset, RegisterID dest) + { + if (!offset) { + m_assembler.movlMemReg(base, dest); + return; + } + + if ((offset >= 0) && (offset < 64)) { + m_assembler.movlMemReg(offset >> 2, base, dest); + return; + } + + if ((dest == SH4Registers::r0) && (dest != base)) { + m_assembler.loadConstant((offset), dest); + m_assembler.movlR0mr(base, dest); + return; + } + + RegisterID scr; + if (dest == base) + scr = claimScratch(); + else + scr = dest; + m_assembler.loadConstant((offset), scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, dest); + + if (dest == base) + releaseScratch(scr); + } + + void load8(RegisterID base, int offset, RegisterID dest) + { + if (!offset) { + m_assembler.movbMemReg(base, dest); + m_assembler.extub(dest, dest); + return; + } + + if ((offset > 0) && (offset < 64) && (dest == SH4Registers::r0)) { + m_assembler.movbMemReg(offset, base, dest); + m_assembler.extub(dest, dest); + return; + } + + if (base != dest) { + m_assembler.loadConstant((offset), dest); + m_assembler.addlRegReg(base, dest); + m_assembler.movbMemReg(dest, dest); + m_assembler.extub(dest, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((offset), scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movbMemReg(scr, dest); + m_assembler.extub(dest, dest); + releaseScratch(scr); + } + + void load32(RegisterID r0, RegisterID src, RegisterID dst) + { + ASSERT(r0 == SH4Registers::r0); + m_assembler.movlR0mr(src, dst); + } + + void load32(RegisterID src, RegisterID dst) + { + m_assembler.movlMemReg(src, dst); + } + + void load16(ImplicitAddress address, RegisterID dest) + { + if (!address.offset) { + m_assembler.movwMemReg(address.base, dest); + extuw(dest, dest); + return; + } + + if ((address.offset > 0) && (address.offset < 64) && (dest == SH4Registers::r0)) { + m_assembler.movwMemReg(address.offset, address.base, dest); + extuw(dest, dest); + return; + } + + if (address.base != dest) { + m_assembler.loadConstant((address.offset), dest); + m_assembler.addlRegReg(address.base, dest); + m_assembler.movwMemReg(dest, dest); + extuw(dest, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((address.offset), scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movwMemReg(scr, dest); + extuw(dest, dest); + releaseScratch(scr); + } + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + add32(address.base, scr); + load8(scr, scr1); + add32(TrustedImm32(1), scr); + load8(scr, dest); + m_assembler.shllImm8r(8, dest); + or32(scr1, dest); + + releaseScratch(scr); + releaseScratch(scr1); + } + + void load16(RegisterID src, RegisterID dest) + { + m_assembler.movwMemReg(src, dest); + extuw(dest, dest); + } + + void load16(RegisterID r0, RegisterID src, RegisterID dest) + { + ASSERT(r0 == SH4Registers::r0); + m_assembler.movwR0mr(src, dest); + extuw(dest, dest); + } + + void load16(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + + if (address.offset) + add32(TrustedImm32(address.offset), scr); + if (address.base == SH4Registers::r0) + load16(address.base, scr, dest); + else { + add32(address.base, scr); + load16(scr, dest); + } + + releaseScratch(scr); + } + + void store32(RegisterID src, ImplicitAddress address) + { + RegisterID scr = claimScratch(); + store32(src, address.offset, address.base, scr); + releaseScratch(scr); + } + + void store32(RegisterID src, int offset, RegisterID base, RegisterID scr) + { + if (!offset) { + m_assembler.movlRegMem(src, base); + return; + } + + if ((offset >=0) && (offset < 64)) { + m_assembler.movlRegMem(src, offset >> 2, base); + return; + } + + m_assembler.loadConstant((offset), scr); + if (scr == SH4Registers::r0) { + m_assembler.movlRegMemr0(src, base); + return; + } + + m_assembler.addlRegReg(base, scr); + m_assembler.movlRegMem(src, scr); + } + + void store32(RegisterID src, RegisterID offset, RegisterID base) + { + ASSERT(offset == SH4Registers::r0); + m_assembler.movlRegMemr0(src, base); + } + + void store32(RegisterID src, RegisterID dst) + { + m_assembler.movlRegMem(src, dst); + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.loadConstant((imm.m_value), scr); + store32(scr, address.offset, address.base, scr1); + releaseScratch(scr); + releaseScratch(scr1); + } + + void store32(RegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + store32(src, Address(scr, address.offset)); + + releaseScratch(scr); + } + + void store32(TrustedImm32 imm, void* address) + { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.loadConstant((imm.m_value), scr); + m_assembler.loadConstant(reinterpret_cast(address), scr1); + m_assembler.movlRegMem(scr, scr1); + releaseScratch(scr); + releaseScratch(scr1); + } + + void store32(RegisterID src, void* address) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(reinterpret_cast(address), scr); + m_assembler.movlRegMem(src, scr); + releaseScratch(scr); + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + RegisterID scr = claimScratch(); + DataLabel32 label(this); + m_assembler.loadConstantUnReusable(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlMemReg(scr, dest); + releaseScratch(scr); + return label; + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + RegisterID scr = claimScratch(); + DataLabel32 label(this); + m_assembler.loadConstantUnReusable(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlRegMem(src, scr); + releaseScratch(scr); + return label; + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabelCompact dataLabel(this); + ASSERT(address.offset <= MaximumCompactPtrAlignedAddressOffset); + ASSERT(address.offset >= 0); + m_assembler.movlMemRegCompact(address.offset >> 2, address.base, dest); + return dataLabel; + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result(this); + + RegisterID scr = claimScratch(); + m_assembler.movImm8(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlMemReg(scr, dest); + releaseScratch(scr); + + return result; + } + + // Floating-point operations + + static bool supportsFloatingPoint() { return true; } + static bool supportsFloatingPointTruncate() { return true; } + static bool supportsFloatingPointSqrt() { return true; } + static bool supportsFloatingPointAbs() { return false; } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + + m_assembler.loadConstant(address.offset, scr); + if (address.base == SH4Registers::r0) { + m_assembler.fmovsReadr0r(scr, (FPRegisterID)(dest + 1)); + m_assembler.addlImm8r(4, scr); + m_assembler.fmovsReadr0r(scr, dest); + releaseScratch(scr); + return; + } + + m_assembler.addlRegReg(address.base, scr); + m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); + m_assembler.fmovsReadrm(scr, dest); + releaseScratch(scr); + } + + void loadDouble(const void* address, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(reinterpret_cast(address), scr); + m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); + m_assembler.fmovsReadrm(scr, dest); + releaseScratch(scr); + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.fmovsWriterm((FPRegisterID)(src + 1), scr); + m_assembler.addlImm8r(4, scr); + m_assembler.fmovsWriterm(src, scr); + releaseScratch(scr); + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.daddRegReg(src, dest); + } + + void addDouble(Address address, FPRegisterID dest) + { + loadDouble(address, fscratch); + addDouble(fscratch, dest); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.dsubRegReg(src, dest); + } + + void subDouble(Address address, FPRegisterID dest) + { + loadDouble(address, fscratch); + subDouble(fscratch, dest); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.dmulRegReg(src, dest); + } + + void mulDouble(Address address, FPRegisterID dest) + { + loadDouble(address, fscratch); + mulDouble(fscratch, dest); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.ddivRegReg(src, dest); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.ldsrmfpul(src); + m_assembler.floatfpulDreg(dest); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(reinterpret_cast(src.m_ptr), scr); + convertInt32ToDouble(scr, dest); + releaseScratch(scr); + } + + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + load32(src, scr); + convertInt32ToDouble(scr, dest); + releaseScratch(scr); + } + + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + Jump m_jump; + JumpList end; + + if (dest != SH4Registers::r0) + move(SH4Registers::r0, scr1); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 68, sizeof(uint32_t)); + move(scr, SH4Registers::r0); + m_assembler.andlImm8r(0x3, SH4Registers::r0); + m_assembler.cmpEqImmR0(0x0, SH4Registers::r0); + m_jump = Jump(m_assembler.jne(), SH4Assembler::JumpNear); + if (dest != SH4Registers::r0) + move(scr1, SH4Registers::r0); + + load32(scr, dest); + end.append(Jump(m_assembler.bra(), SH4Assembler::JumpNear)); + m_assembler.nop(); + m_jump.link(this); + m_assembler.andlImm8r(0x1, SH4Registers::r0); + m_assembler.cmpEqImmR0(0x0, SH4Registers::r0); + + if (dest != SH4Registers::r0) + move(scr1, SH4Registers::r0); + + m_jump = Jump(m_assembler.jne(), SH4Assembler::JumpNear); + load16(scr, scr1); + add32(TrustedImm32(2), scr); + load16(scr, dest); + m_assembler.shllImm8r(16, dest); + or32(scr1, dest); + end.append(Jump(m_assembler.bra(), SH4Assembler::JumpNear)); + m_assembler.nop(); + m_jump.link(this); + load8(scr, scr1); + add32(TrustedImm32(1), scr); + load16(scr, dest); + m_assembler.shllImm8r(8, dest); + or32(dest, scr1); + add32(TrustedImm32(2), scr); + load8(scr, dest); + m_assembler.shllImm8r(8, dest); + m_assembler.shllImm8r(16, dest); + or32(scr1, dest); + end.link(this); + + releaseScratch(scr); + releaseScratch(scr1); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + RegisterID scr = scratchReg3; + load32WithUnalignedHalfWords(left, scr); + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testlRegReg(scr, scr); + else + compare32(right.m_value, scr, cond); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) + { + m_assembler.movImm8(0, scratchReg3); + convertInt32ToDouble(scratchReg3, scratch); + return branchDouble(DoubleNotEqual, reg, scratch); + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) + { + m_assembler.movImm8(0, scratchReg3); + convertInt32ToDouble(scratchReg3, scratch); + return branchDouble(DoubleEqualOrUnordered, reg, scratch); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + if (cond == DoubleEqual) { + m_assembler.dcmppeq(right, left); + return branchTrue(); + } + + if (cond == DoubleNotEqual) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppeq(right, left); + releaseScratch(scr); + Jump m_jump = branchFalse(); + end.link(this); + return m_jump; + } + + if (cond == DoubleGreaterThan) { + m_assembler.dcmppgt(right, left); + return branchTrue(); + } + + if (cond == DoubleGreaterThanOrEqual) { + m_assembler.dcmppgt(left, right); + return branchFalse(); + } + + if (cond == DoubleLessThan) { + m_assembler.dcmppgt(left, right); + return branchTrue(); + } + + if (cond == DoubleLessThanOrEqual) { + m_assembler.dcmppgt(right, left); + return branchFalse(); + } + + if (cond == DoubleEqualOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppeq(left, right); + Jump m_jump = Jump(m_assembler.je()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + if (cond == DoubleGreaterThanOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppgt(right, left); + Jump m_jump = Jump(m_assembler.je()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + if (cond == DoubleGreaterThanOrEqualOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppgt(left, right); + Jump m_jump = Jump(m_assembler.jne()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + if (cond == DoubleLessThanOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppgt(left, right); + Jump m_jump = Jump(m_assembler.je()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + if (cond == DoubleLessThanOrEqualOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppgt(right, left); + Jump m_jump = Jump(m_assembler.jne()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + ASSERT(cond == DoubleNotEqualOrUnordered); + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppeq(right, left); + Jump m_jump = Jump(m_assembler.jne()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + Jump branchTrue() + { + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 6, sizeof(uint32_t)); + Jump m_jump = Jump(m_assembler.je()); + m_assembler.extraInstrForBranch(scratchReg3); + return m_jump; + } + + Jump branchFalse() + { + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 6, sizeof(uint32_t)); + Jump m_jump = Jump(m_assembler.jne()); + m_assembler.extraInstrForBranch(scratchReg3); + return m_jump; + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + RegisterID scr = claimScratch(); + move(left.index, scr); + lshift32(TrustedImm32(left.scale), scr); + add32(left.base, scr); + load32(scr, left.offset, scr); + compare32(right.m_value, scr, cond); + releaseScratch(scr); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dest) + { + if (dest != src) + m_assembler.dmovRegReg(src, dest); + m_assembler.dsqrt(dest); + } + + void absDouble(FPRegisterID, FPRegisterID) + { + ASSERT_NOT_REACHED(); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + RegisterID addressTempRegister = claimScratch(); + load8(address, addressTempRegister); + Jump jmp = branchTest32(cond, addressTempRegister, mask); + releaseScratch(addressTempRegister); + return jmp; + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + RegisterID addressTempRegister = claimScratch(); + move(TrustedImmPtr(address.m_ptr), addressTempRegister); + load8(Address(addressTempRegister), addressTempRegister); + Jump jmp = branchTest32(cond, addressTempRegister, mask); + releaseScratch(addressTempRegister); + return jmp; + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest) + move(src, dest); + } + + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + RegisterID addressTempRegister = claimScratch(); + load8(left, addressTempRegister); + Jump jmp = branch32(cond, addressTempRegister, right); + releaseScratch(addressTempRegister); + return jmp; + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + RegisterID addressTempRegister = claimScratch(); + load8(left, addressTempRegister); + compare32(cond, addressTempRegister, right, dest); + releaseScratch(addressTempRegister); + } + + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + m_assembler.ftrcdrmfpul(src); + m_assembler.stsfpulReg(dest); + m_assembler.loadConstant(0x7fffffff, scratchReg3); + m_assembler.cmplRegReg(dest, scratchReg3, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 14, sizeof(uint32_t)); + m_assembler.branch(BT_OPCODE, 2); + m_assembler.addlImm8r(1, scratchReg3); + m_assembler.cmplRegReg(dest, scratchReg3, SH4Condition(Equal)); + return branchTrue(); + } + + // Stack manipulation operations + + void pop(RegisterID dest) + { + m_assembler.popReg(dest); + } + + void push(RegisterID src) + { + m_assembler.pushReg(src); + } + + void push(Address address) + { + if (!address.offset) { + push(address.base); + return; + } + + if ((address.offset < 0) || (address.offset >= 64)) { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlMemReg(scr, SH4Registers::sp); + m_assembler.addlImm8r(-4, SH4Registers::sp); + releaseScratch(scr); + return; + } + + m_assembler.movlMemReg(address.offset >> 2, address.base, SH4Registers::sp); + m_assembler.addlImm8r(-4, SH4Registers::sp); + } + + void push(TrustedImm32 imm) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm.m_value, scr); + push(scr); + releaseScratch(scr); + } + + // Register move operations + + void move(TrustedImm32 imm, RegisterID dest) + { + m_assembler.loadConstant(imm.m_value, dest); + } + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + m_assembler.ensureSpace(m_assembler.maxInstructionSize, sizeof(uint32_t)); + DataLabelPtr dataLabel(this); + m_assembler.loadConstantUnReusable(reinterpret_cast(initialValue.m_value), dest); + return dataLabel; + } + + void move(RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.movlRegReg(src, dest); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + m_assembler.loadConstant(imm.asIntptr(), dest); + } + + void extuw(RegisterID src, RegisterID dst) + { + m_assembler.extuw(src, dst); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmplRegReg(right, left, SH4Condition(cond)); + if (cond != NotEqual) { + m_assembler.movt(dest); + return; + } + + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 4); + m_assembler.movImm8(0, dest); + m_assembler.branch(BT_OPCODE, 0); + m_assembler.movImm8(1, dest); + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + if (left != dest) { + move(right, dest); + compare32(cond, left, dest, dest); + return; + } + + RegisterID scr = claimScratch(); + move(right, scr); + compare32(cond, left, scr, dest); + releaseScratch(scr); + } + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + ASSERT((cond == Zero) || (cond == NonZero)); + + load8(address, dest); + if (mask.m_value == -1) + compare32(0, dest, static_cast(cond)); + else + testlImm(mask.m_value, dest); + if (cond != NonZero) { + m_assembler.movt(dest); + return; + } + + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 4); + m_assembler.movImm8(0, dest); + m_assembler.branch(BT_OPCODE, 0); + m_assembler.movImm8(1, dest); + } + + void loadPtrLinkReg(ImplicitAddress address) + { + RegisterID scr = claimScratch(); + load32(address, scr); + m_assembler.ldspr(scr); + releaseScratch(scr); + } + + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) + { + m_assembler.cmplRegReg(right, left, SH4Condition(cond)); + /* BT label => BF off + nop LDR reg + nop braf @reg + nop nop + */ + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testlRegReg(left, left); + else + compare32(right.m_value, left, cond); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + compare32(right.offset, right.base, left, cond); + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + compare32(right, left.offset, left.base, cond); + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + compare32(right.m_value, left.offset, left.base, cond); + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + RegisterID scr = claimScratch(); + + move(TrustedImm32(reinterpret_cast(left.m_ptr)), scr); + m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); + releaseScratch(scr); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + RegisterID addressTempRegister = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(left.m_ptr), addressTempRegister); + m_assembler.movlMemReg(addressTempRegister, addressTempRegister); + compare32(right.m_value, addressTempRegister, cond); + releaseScratch(addressTempRegister); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(right.m_value & 0xFFFFFF00)); + RegisterID scr = claimScratch(); + + move(left.index, scr); + lshift32(TrustedImm32(left.scale), scr); + + if (left.offset) + add32(TrustedImm32(left.offset), scr); + add32(left.base, scr); + load8(scr, scr); + RegisterID scr1 = claimScratch(); + m_assembler.loadConstant(right.m_value, scr1); + releaseScratch(scr); + releaseScratch(scr1); + + return branch32(cond, scr, scr1); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + ASSERT((cond == Zero) || (cond == NonZero)); + + m_assembler.testlRegReg(reg, mask); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + + if (mask.m_value == -1) + m_assembler.testlRegReg(reg, reg); + else + testlImm(mask.m_value, reg); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + + if (mask.m_value == -1) + compare32(0, address.offset, address.base, static_cast(cond)); + else + testImm(mask.m_value, address.offset, address.base); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + load32(scr, address.offset, scr); + + if (mask.m_value == -1) + m_assembler.testlRegReg(scr, scr); + else + testlImm(mask.m_value, scr); + + releaseScratch(scr); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump jump() + { + return Jump(m_assembler.jmp()); + } + + void jump(RegisterID target) + { + m_assembler.jmpReg(target); + } + + void jump(Address address) + { + RegisterID scr = claimScratch(); + + if ((address.offset < 0) || (address.offset >= 64)) { + m_assembler.loadConstant(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlMemReg(scr, scr); + } else if (address.offset) + m_assembler.movlMemReg(address.offset >> 2, address.base, scr); + else + m_assembler.movlMemReg(address.base, scr); + m_assembler.jmpReg(scr); + + releaseScratch(scr); + } + + // Arithmetic control flow operations + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (cond == Overflow) { + m_assembler.addvlRegReg(src, dest); + return branchTrue(); + } + + if (cond == Signed) { + m_assembler.addlRegReg(src, dest); + // Check if dest is negative + m_assembler.cmppz(dest); + return branchFalse(); + } + + m_assembler.addlRegReg(src, dest); + compare32(0, dest, Equal); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + move(imm, scratchReg3); + return branchAdd32(cond, scratchReg3, dest); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (src != dest) + move(src, dest); + + if (cond == Overflow) { + move(imm, scratchReg3); + m_assembler.addvlRegReg(scratchReg3, dest); + return branchTrue(); + } + + add32(imm, dest); + + if (cond == Signed) { + m_assembler.cmppz(dest); + return branchFalse(); + } + + compare32(0, dest, Equal); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (cond == Overflow) { + RegisterID scr1 = claimScratch(); + RegisterID scr = claimScratch(); + m_assembler.dmullRegReg(src, dest); + m_assembler.stsmacl(dest); + m_assembler.movImm8(-31, scr); + m_assembler.movlRegReg(dest, scr1); + m_assembler.shaRegReg(scr1, scr); + m_assembler.stsmach(scr); + m_assembler.cmplRegReg(scr, scr1, SH4Condition(Equal)); + releaseScratch(scr1); + releaseScratch(scr); + return branchFalse(); + } + + m_assembler.imullRegReg(src, dest); + m_assembler.stsmacl(dest); + if (cond == Signed) { + // Check if dest is negative + m_assembler.cmppz(dest); + return branchFalse(); + } + + compare32(0, dest, static_cast(cond)); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + move(imm, scratchReg3); + if (src != dest) + move(src, dest); + + return branchMul32(cond, scratchReg3, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (cond == Overflow) { + m_assembler.subvlRegReg(src, dest); + return branchTrue(); + } + + if (cond == Signed) { + // Check if dest is negative + m_assembler.sublRegReg(src, dest); + compare32(0, dest, LessThan); + return branchTrue(); + } + + sub32(src, dest); + compare32(0, dest, static_cast(cond)); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + move(imm, scratchReg3); + return branchSub32(cond, scratchReg3, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + move(imm, scratchReg3); + if (src != dest) + move(src, dest); + return branchSub32(cond, scratchReg3, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + if (src1 != dest) + move(src1, dest); + return branchSub32(cond, src2, dest); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (cond == Signed) { + or32(src, dest); + compare32(0, dest, static_cast(LessThan)); + return branchTrue(); + } + + or32(src, dest); + compare32(0, dest, static_cast(cond)); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + m_assembler.ftrcdrmfpul(src); + m_assembler.stsfpulReg(dest); + convertInt32ToDouble(dest, fscratch); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, fscratch, src)); + + if (dest == SH4Registers::r0) + m_assembler.cmpEqImmR0(0, dest); + else { + m_assembler.movImm8(0, scratchReg3); + m_assembler.cmplRegReg(scratchReg3, dest, SH4Condition(Equal)); + } + failureCases.append(branchTrue()); + } + + void neg32(RegisterID dst) + { + m_assembler.neg(dst, dst); + } + + void urshift32(RegisterID shiftamount, RegisterID dest) + { + if (shiftamount == SH4Registers::r0) + m_assembler.andlImm8r(0x1f, shiftamount); + else { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(0x1f, scr); + m_assembler.andlRegReg(scr, shiftamount); + releaseScratch(scr); + } + m_assembler.neg(shiftamount, shiftamount); + m_assembler.shllRegReg(dest, shiftamount); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(-(imm.m_value & 0x1f), scr); + m_assembler.shaRegReg(dest, scr); + releaseScratch(scr); + } + + void urshift32(RegisterID src, TrustedImm32 shiftamount, RegisterID dest) + { + if (src != dest) + move(src, dest); + + urshift32(shiftamount, dest); + } + + Call call() + { + return Call(m_assembler.call(), Call::Linkable); + } + + Call nearCall() + { + return Call(m_assembler.call(), Call::LinkableNear); + } + + Call call(RegisterID target) + { + return Call(m_assembler.call(target), Call::None); + } + + void call(Address address, RegisterID target) + { + load32(address.base, address.offset, target); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 2); + m_assembler.branch(JSR_OPCODE, target); + m_assembler.nop(); + } + + void breakpoint() + { + m_assembler.bkpt(); + m_assembler.nop(); + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + RegisterID dataTempRegister = claimScratch(); + + dataLabel = moveWithPatch(initialRightValue, dataTempRegister); + m_assembler.cmplRegReg(dataTempRegister, left, SH4Condition(cond)); + releaseScratch(dataTempRegister); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + RegisterID scr = claimScratch(); + + m_assembler.loadConstant(left.offset, scr); + m_assembler.addlRegReg(left.base, scr); + m_assembler.movlMemReg(scr, scr); + RegisterID scr1 = claimScratch(); + dataLabel = moveWithPatch(initialRightValue, scr1); + m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); + releaseScratch(scr); + releaseScratch(scr1); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + void ret() + { + m_assembler.ret(); + m_assembler.nop(); + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + RegisterID scr = claimScratch(); + DataLabelPtr label = moveWithPatch(initialValue, scr); + store32(scr, address); + releaseScratch(scr); + return label; + } + + DataLabelPtr storePtrWithPatch(ImplicitAddress address) { return storePtrWithPatch(TrustedImmPtr(0), address); } + + int sizeOfConstantPool() + { + return m_assembler.sizeOfConstantPool(); + } + + Call tailRecursiveCall() + { + RegisterID scr = claimScratch(); + + m_assembler.loadConstantUnReusable(0x0, scr, true); + Jump m_jump = Jump(m_assembler.jmp(scr)); + releaseScratch(scr); + + return Call::fromTailJump(m_jump); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + oldJump.link(this); + return tailRecursiveCall(); + } + + void nop() + { + m_assembler.nop(); + } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(reinterpret_cast(SH4Assembler::readCallTarget(call.dataLocation()))); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + ASSERT_NOT_REACHED(); + } + + static ptrdiff_t maxJumpReplacementSize() + { + ASSERT_NOT_REACHED(); + return 0; + } + +protected: + SH4Assembler::Condition SH4Condition(RelationalCondition cond) + { + return static_cast(cond); + } + + SH4Assembler::Condition SH4Condition(ResultCondition cond) + { + return static_cast(cond); + } +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void*, Call, FunctionPtr); + static void repatchCall(CodeLocationCall, CodeLocationLabel); + static void repatchCall(CodeLocationCall, FunctionPtr); +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerSH4_h diff --git a/masm/assembler/MacroAssemblerX86.h b/masm/assembler/MacroAssemblerX86.h new file mode 100644 index 0000000000..d1a4ff3c4f --- /dev/null +++ b/masm/assembler/MacroAssemblerX86.h @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerX86_h +#define MacroAssemblerX86_h + +#if ENABLE(ASSEMBLER) && CPU(X86) + +#include "MacroAssemblerX86Common.h" + +namespace JSC { + +class MacroAssemblerX86 : public MacroAssemblerX86Common { +public: + static const Scale ScalePtr = TimesFour; + + using MacroAssemblerX86Common::add32; + using MacroAssemblerX86Common::and32; + using MacroAssemblerX86Common::branchAdd32; + using MacroAssemblerX86Common::branchSub32; + using MacroAssemblerX86Common::sub32; + using MacroAssemblerX86Common::or32; + using MacroAssemblerX86Common::load32; + using MacroAssemblerX86Common::store32; + using MacroAssemblerX86Common::branch32; + using MacroAssemblerX86Common::call; + using MacroAssemblerX86Common::jump; + using MacroAssemblerX86Common::addDouble; + using MacroAssemblerX86Common::loadDouble; + using MacroAssemblerX86Common::storeDouble; + using MacroAssemblerX86Common::convertInt32ToDouble; + using MacroAssemblerX86Common::branchTest8; + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.leal_mr(imm.m_value, src, dest); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.addl_im(imm.m_value, address.m_ptr); + } + + void add32(AbsoluteAddress address, RegisterID dest) + { + m_assembler.addl_mr(address.m_ptr, dest); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.addl_im(imm.m_value, address.m_ptr); + m_assembler.adcl_im(imm.m_value >> 31, reinterpret_cast(address.m_ptr) + sizeof(int32_t)); + } + + void and32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.andl_im(imm.m_value, address.m_ptr); + } + + void or32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.orl_im(imm.m_value, address.m_ptr); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.subl_im(imm.m_value, address.m_ptr); + } + + void load32(const void* address, RegisterID dest) + { + m_assembler.movl_mr(address, dest); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result = ConvertibleLoadLabel(this); + m_assembler.movl_mr(address.offset, address.base, dest); + return result; + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + m_assembler.addsd_mr(address.m_ptr, dest); + } + + void storeDouble(FPRegisterID src, const void* address) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_rm(src, address); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + m_assembler.cvtsi2sd_mr(src.m_ptr, dest); + } + + void store32(TrustedImm32 imm, void* address) + { + m_assembler.movl_i32m(imm.m_value, address); + } + + void store32(RegisterID src, void* address) + { + m_assembler.movl_rm(src, address); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) + { + m_assembler.addl_im(imm.m_value, dest.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) + { + m_assembler.subl_im(imm.m_value, dest.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + m_assembler.cmpl_rm(right, left.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + m_assembler.cmpl_im(right.m_value, left.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Call call() + { + return Call(m_assembler.call(), Call::Linkable); + } + + // Address is a memory location containing the address to jump to + void jump(AbsoluteAddress address) + { + m_assembler.jmp_m(address.m_ptr); + } + + Call tailRecursiveCall() + { + return Call::fromTailJump(jump()); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + return Call::fromTailJump(oldJump); + } + + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + padBeforePatch(); + m_assembler.movl_i32r(initialValue.asIntptr(), dest); + return DataLabelPtr(this); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT(mask.m_value >= -128 && mask.m_value <= 255); + if (mask.m_value == -1) + m_assembler.cmpb_im(0, address.m_ptr); + else + m_assembler.testb_im(mask.m_value, address.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + padBeforePatch(); + m_assembler.cmpl_ir_force32(initialRightValue.asIntptr(), left); + dataLabel = DataLabelPtr(this); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + padBeforePatch(); + m_assembler.cmpl_im_force32(initialRightValue.asIntptr(), left.offset, left.base); + dataLabel = DataLabelPtr(this); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + padBeforePatch(); + m_assembler.movl_i32m(initialValue.asIntptr(), address.offset, address.base); + return DataLabelPtr(this); + } + + static bool supportsFloatingPoint() { return isSSE2Present(); } + // See comment on MacroAssemblerARMv7::supportsFloatingPointTruncate() + static bool supportsFloatingPointTruncate() { return isSSE2Present(); } + static bool supportsFloatingPointSqrt() { return isSSE2Present(); } + static bool supportsFloatingPointAbs() { return isSSE2Present(); } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + intptr_t offset = reinterpret_cast(call.dataLocation())[-1]; + return FunctionPtr(reinterpret_cast(reinterpret_cast(call.dataLocation()) + offset)); + } + +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + X86Assembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + X86Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + X86Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerX86_h diff --git a/masm/assembler/MacroAssemblerX86Common.h b/masm/assembler/MacroAssemblerX86Common.h new file mode 100644 index 0000000000..905c094267 --- /dev/null +++ b/masm/assembler/MacroAssemblerX86Common.h @@ -0,0 +1,1529 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerX86Common_h +#define MacroAssemblerX86Common_h + +#if ENABLE(ASSEMBLER) + +#include "X86Assembler.h" +#include "AbstractMacroAssembler.h" + +namespace JSC { + +class MacroAssemblerX86Common : public AbstractMacroAssembler { +protected: +#if CPU(X86_64) + static const X86Registers::RegisterID scratchRegister = X86Registers::r11; +#endif + + static const int DoubleConditionBitInvert = 0x10; + static const int DoubleConditionBitSpecial = 0x20; + static const int DoubleConditionBits = DoubleConditionBitInvert | DoubleConditionBitSpecial; + +public: + typedef X86Assembler::FPRegisterID FPRegisterID; + typedef X86Assembler::XMMRegisterID XMMRegisterID; + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return value >= -128 && value <= 127; + } + + enum RelationalCondition { + Equal = X86Assembler::ConditionE, + NotEqual = X86Assembler::ConditionNE, + Above = X86Assembler::ConditionA, + AboveOrEqual = X86Assembler::ConditionAE, + Below = X86Assembler::ConditionB, + BelowOrEqual = X86Assembler::ConditionBE, + GreaterThan = X86Assembler::ConditionG, + GreaterThanOrEqual = X86Assembler::ConditionGE, + LessThan = X86Assembler::ConditionL, + LessThanOrEqual = X86Assembler::ConditionLE + }; + + enum ResultCondition { + Overflow = X86Assembler::ConditionO, + Signed = X86Assembler::ConditionS, + Zero = X86Assembler::ConditionE, + NonZero = X86Assembler::ConditionNE + }; + + enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleEqual = X86Assembler::ConditionE | DoubleConditionBitSpecial, + DoubleNotEqual = X86Assembler::ConditionNE, + DoubleGreaterThan = X86Assembler::ConditionA, + DoubleGreaterThanOrEqual = X86Assembler::ConditionAE, + DoubleLessThan = X86Assembler::ConditionA | DoubleConditionBitInvert, + DoubleLessThanOrEqual = X86Assembler::ConditionAE | DoubleConditionBitInvert, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = X86Assembler::ConditionE, + DoubleNotEqualOrUnordered = X86Assembler::ConditionNE | DoubleConditionBitSpecial, + DoubleGreaterThanOrUnordered = X86Assembler::ConditionB | DoubleConditionBitInvert, + DoubleGreaterThanOrEqualOrUnordered = X86Assembler::ConditionBE | DoubleConditionBitInvert, + DoubleLessThanOrUnordered = X86Assembler::ConditionB, + DoubleLessThanOrEqualOrUnordered = X86Assembler::ConditionBE, + }; + COMPILE_ASSERT( + !((X86Assembler::ConditionE | X86Assembler::ConditionNE | X86Assembler::ConditionA | X86Assembler::ConditionAE | X86Assembler::ConditionB | X86Assembler::ConditionBE) & DoubleConditionBits), + DoubleConditionBits_should_not_interfere_with_X86Assembler_Condition_codes); + + static const RegisterID stackPointerRegister = X86Registers::esp; + +#if ENABLE(JIT_CONSTANT_BLINDING) + static bool shouldBlindForSpecificArch(uint32_t value) { return value >= 0x00ffffff; } +#if CPU(X86_64) + static bool shouldBlindForSpecificArch(uintptr_t value) { return value >= 0x00ffffff; } +#endif +#endif + + // Integer arithmetic operations: + // + // Operations are typically two operand - operation(source, srcDst) + // For many operations the source may be an TrustedImm32, the srcDst operand + // may often be a memory location (explictly described using an Address + // object). + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.addl_rr(src, dest); + } + + void add32(TrustedImm32 imm, Address address) + { + m_assembler.addl_im(imm.m_value, address.offset, address.base); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.addl_ir(imm.m_value, dest); + } + + void add32(Address src, RegisterID dest) + { + m_assembler.addl_mr(src.offset, src.base, dest); + } + + void add32(RegisterID src, Address dest) + { + m_assembler.addl_rm(src, dest.offset, dest.base); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.leal_mr(imm.m_value, src, dest); + } + + void and32(RegisterID src, RegisterID dest) + { + m_assembler.andl_rr(src, dest); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.andl_ir(imm.m_value, dest); + } + + void and32(RegisterID src, Address dest) + { + m_assembler.andl_rm(src, dest.offset, dest.base); + } + + void and32(Address src, RegisterID dest) + { + m_assembler.andl_mr(src.offset, src.base, dest); + } + + void and32(TrustedImm32 imm, Address address) + { + m_assembler.andl_im(imm.m_value, address.offset, address.base); + } + + void and32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + zeroExtend32ToPtr(op1, dest); + else if (op1 == dest) + and32(op2, dest); + else { + move(op2, dest); + and32(op1, dest); + } + } + + void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(src, dest); + and32(imm, dest); + } + + void lshift32(RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (shift_amount == X86Registers::ecx) + m_assembler.shll_CLr(dest); + else { + // On x86 we can only shift by ecx; if asked to shift by another register we'll + // need rejig the shift amount into ecx first, and restore the registers afterwards. + // If we dest is ecx, then shift the swapped register! + swap(shift_amount, X86Registers::ecx); + m_assembler.shll_CLr(dest == X86Registers::ecx ? shift_amount : dest); + swap(shift_amount, X86Registers::ecx); + } + } + + void lshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (src != dest) + move(src, dest); + lshift32(shift_amount, dest); + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.shll_i8r(imm.m_value, dest); + } + + void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (src != dest) + move(src, dest); + lshift32(imm, dest); + } + + void mul32(RegisterID src, RegisterID dest) + { + m_assembler.imull_rr(src, dest); + } + + void mul32(Address src, RegisterID dest) + { + m_assembler.imull_mr(src.offset, src.base, dest); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.imull_i32r(src, imm.m_value, dest); + } + + void neg32(RegisterID srcDest) + { + m_assembler.negl_r(srcDest); + } + + void neg32(Address srcDest) + { + m_assembler.negl_m(srcDest.offset, srcDest.base); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orl_rr(src, dest); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.orl_ir(imm.m_value, dest); + } + + void or32(RegisterID src, Address dest) + { + m_assembler.orl_rm(src, dest.offset, dest.base); + } + + void or32(Address src, RegisterID dest) + { + m_assembler.orl_mr(src.offset, src.base, dest); + } + + void or32(TrustedImm32 imm, Address address) + { + m_assembler.orl_im(imm.m_value, address.offset, address.base); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + zeroExtend32ToPtr(op1, dest); + else if (op1 == dest) + or32(op2, dest); + else { + move(op2, dest); + or32(op1, dest); + } + } + + void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(src, dest); + or32(imm, dest); + } + + void rshift32(RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (shift_amount == X86Registers::ecx) + m_assembler.sarl_CLr(dest); + else { + // On x86 we can only shift by ecx; if asked to shift by another register we'll + // need rejig the shift amount into ecx first, and restore the registers afterwards. + // If we dest is ecx, then shift the swapped register! + swap(shift_amount, X86Registers::ecx); + m_assembler.sarl_CLr(dest == X86Registers::ecx ? shift_amount : dest); + swap(shift_amount, X86Registers::ecx); + } + } + + void rshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (src != dest) + move(src, dest); + rshift32(shift_amount, dest); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.sarl_i8r(imm.m_value, dest); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (src != dest) + move(src, dest); + rshift32(imm, dest); + } + + void urshift32(RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (shift_amount == X86Registers::ecx) + m_assembler.shrl_CLr(dest); + else { + // On x86 we can only shift by ecx; if asked to shift by another register we'll + // need rejig the shift amount into ecx first, and restore the registers afterwards. + // If we dest is ecx, then shift the swapped register! + swap(shift_amount, X86Registers::ecx); + m_assembler.shrl_CLr(dest == X86Registers::ecx ? shift_amount : dest); + swap(shift_amount, X86Registers::ecx); + } + } + + void urshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (src != dest) + move(src, dest); + urshift32(shift_amount, dest); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.shrl_i8r(imm.m_value, dest); + } + + void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (src != dest) + move(src, dest); + urshift32(imm, dest); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.subl_rr(src, dest); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.subl_ir(imm.m_value, dest); + } + + void sub32(TrustedImm32 imm, Address address) + { + m_assembler.subl_im(imm.m_value, address.offset, address.base); + } + + void sub32(Address src, RegisterID dest) + { + m_assembler.subl_mr(src.offset, src.base, dest); + } + + void sub32(RegisterID src, Address dest) + { + m_assembler.subl_rm(src, dest.offset, dest.base); + } + + void xor32(RegisterID src, RegisterID dest) + { + m_assembler.xorl_rr(src, dest); + } + + void xor32(TrustedImm32 imm, Address dest) + { + if (imm.m_value == -1) + m_assembler.notl_m(dest.offset, dest.base); + else + m_assembler.xorl_im(imm.m_value, dest.offset, dest.base); + } + + void xor32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value == -1) + m_assembler.notl_r(dest); + else + m_assembler.xorl_ir(imm.m_value, dest); + } + + void xor32(RegisterID src, Address dest) + { + m_assembler.xorl_rm(src, dest.offset, dest.base); + } + + void xor32(Address src, RegisterID dest) + { + m_assembler.xorl_mr(src.offset, src.base, dest); + } + + void xor32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + move(TrustedImm32(0), dest); + else if (op1 == dest) + xor32(op2, dest); + else { + move(op2, dest); + xor32(op1, dest); + } + } + + void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(src, dest); + xor32(imm, dest); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.sqrtsd_rr(src, dst); + } + + void absDouble(FPRegisterID src, FPRegisterID dst) + { + ASSERT(src != dst); + static const double negativeZeroConstant = -0.0; + loadDouble(&negativeZeroConstant, dst); + m_assembler.andnpd_rr(src, dst); + } + + void negateDouble(FPRegisterID src, FPRegisterID dst) + { + ASSERT(src != dst); + static const double negativeZeroConstant = -0.0; + loadDouble(&negativeZeroConstant, dst); + m_assembler.xorpd_rr(src, dst); + } + + + // Memory access operations: + // + // Loads are of the form load(address, destination) and stores of the form + // store(source, address). The source for a store may be an TrustedImm32. Address + // operand objects to loads and store will be implicitly constructed if a + // register is passed. + + void load32(ImplicitAddress address, RegisterID dest) + { + m_assembler.movl_mr(address.offset, address.base, dest); + } + + void load32(BaseIndex address, RegisterID dest) + { + m_assembler.movl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + load32(address, dest); + } + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + load16(address, dest); + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movl_mr_disp32(address.offset, address.base, dest); + return DataLabel32(this); + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movl_mr_disp8(address.offset, address.base, dest); + return DataLabelCompact(this); + } + + static void repatchCompact(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) + { + ASSERT(isCompactPtrAlignedAddressOffset(value)); + AssemblerType_T::repatchCompact(dataLabelCompact.dataLocation(), value); + } + + DataLabelCompact loadCompactWithAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movl_mr_disp8(address.offset, address.base, dest); + return DataLabelCompact(this); + } + + void load8(BaseIndex address, RegisterID dest) + { + m_assembler.movzbl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load8(ImplicitAddress address, RegisterID dest) + { + m_assembler.movzbl_mr(address.offset, address.base, dest); + } + + void load8Signed(BaseIndex address, RegisterID dest) + { + m_assembler.movsbl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load8Signed(ImplicitAddress address, RegisterID dest) + { + m_assembler.movsbl_mr(address.offset, address.base, dest); + } + + void load16(BaseIndex address, RegisterID dest) + { + m_assembler.movzwl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load16(Address address, RegisterID dest) + { + m_assembler.movzwl_mr(address.offset, address.base, dest); + } + + void load16Signed(BaseIndex address, RegisterID dest) + { + m_assembler.movswl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load16Signed(Address address, RegisterID dest) + { + m_assembler.movswl_mr(address.offset, address.base, dest); + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + padBeforePatch(); + m_assembler.movl_rm_disp32(src, address.offset, address.base); + return DataLabel32(this); + } + + void store32(RegisterID src, ImplicitAddress address) + { + m_assembler.movl_rm(src, address.offset, address.base); + } + + void store32(RegisterID src, BaseIndex address) + { + m_assembler.movl_rm(src, address.offset, address.base, address.index, address.scale); + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + m_assembler.movl_i32m(imm.m_value, address.offset, address.base); + } + + void store32(TrustedImm32 imm, BaseIndex address) + { + m_assembler.movl_i32m(imm.m_value, address.offset, address.base, address.index, address.scale); + } + + void store8(TrustedImm32 imm, Address address) + { + ASSERT(-128 <= imm.m_value && imm.m_value < 128); + m_assembler.movb_i8m(imm.m_value, address.offset, address.base); + } + + void store8(TrustedImm32 imm, BaseIndex address) + { + ASSERT(-128 <= imm.m_value && imm.m_value < 128); + m_assembler.movb_i8m(imm.m_value, address.offset, address.base, address.index, address.scale); + } + + void store8(RegisterID src, BaseIndex address) + { +#if CPU(X86) + // On 32-bit x86 we can only store from the first 4 registers; + // esp..edi are mapped to the 'h' registers! + if (src >= 4) { + // Pick a temporary register. + RegisterID temp; + if (address.base != X86Registers::eax && address.index != X86Registers::eax) + temp = X86Registers::eax; + else if (address.base != X86Registers::ebx && address.index != X86Registers::ebx) + temp = X86Registers::ebx; + else { + ASSERT(address.base != X86Registers::ecx && address.index != X86Registers::ecx); + temp = X86Registers::ecx; + } + + // Swap to the temporary register to perform the store. + swap(src, temp); + m_assembler.movb_rm(temp, address.offset, address.base, address.index, address.scale); + swap(src, temp); + return; + } +#endif + m_assembler.movb_rm(src, address.offset, address.base, address.index, address.scale); + } + + void store16(RegisterID src, BaseIndex address) + { +#if CPU(X86) + // On 32-bit x86 we can only store from the first 4 registers; + // esp..edi are mapped to the 'h' registers! + if (src >= 4) { + // Pick a temporary register. + RegisterID temp; + if (address.base != X86Registers::eax && address.index != X86Registers::eax) + temp = X86Registers::eax; + else if (address.base != X86Registers::ebx && address.index != X86Registers::ebx) + temp = X86Registers::ebx; + else { + ASSERT(address.base != X86Registers::ecx && address.index != X86Registers::ecx); + temp = X86Registers::ecx; + } + + // Swap to the temporary register to perform the store. + swap(src, temp); + m_assembler.movw_rm(temp, address.offset, address.base, address.index, address.scale); + swap(src, temp); + return; + } +#endif + m_assembler.movw_rm(src, address.offset, address.base, address.index, address.scale); + } + + + // Floating-point operation: + // + // Presently only supports SSE, not x87 floating point. + + void moveDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (src != dest) + m_assembler.movsd_rr(src, dest); + } + + void loadDouble(const void* address, FPRegisterID dest) + { +#if CPU(X86) + ASSERT(isSSE2Present()); + m_assembler.movsd_mr(address, dest); +#else + move(TrustedImmPtr(address), scratchRegister); + loadDouble(scratchRegister, dest); +#endif + } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_mr(address.offset, address.base, dest); + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_mr(address.offset, address.base, address.index, address.scale, dest); + } + void loadFloat(BaseIndex address, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.movss_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_rm(src, address.offset, address.base); + } + + void storeDouble(FPRegisterID src, BaseIndex address) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_rm(src, address.offset, address.base, address.index, address.scale); + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + ASSERT(isSSE2Present()); + m_assembler.movss_rm(src, address.offset, address.base, address.index, address.scale); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.cvtsd2ss_rr(src, dst); + } + + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.cvtss2sd_rr(src, dst); + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.addsd_rr(src, dest); + } + + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op1 == dest) + addDouble(op2, dest); + else { + moveDouble(op2, dest); + addDouble(op1, dest); + } + } + + void addDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.addsd_mr(src.offset, src.base, dest); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.divsd_rr(src, dest); + } + + void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + // B := A / B is invalid. + ASSERT(op1 == dest || op2 != dest); + + moveDouble(op1, dest); + divDouble(op2, dest); + } + + void divDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.divsd_mr(src.offset, src.base, dest); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.subsd_rr(src, dest); + } + + void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + // B := A - B is invalid. + ASSERT(op1 == dest || op2 != dest); + + moveDouble(op1, dest); + subDouble(op2, dest); + } + + void subDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.subsd_mr(src.offset, src.base, dest); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.mulsd_rr(src, dest); + } + + void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op1 == dest) + mulDouble(op2, dest); + else { + moveDouble(op2, dest); + mulDouble(op1, dest); + } + } + + void mulDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.mulsd_mr(src.offset, src.base, dest); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.cvtsi2sd_rr(src, dest); + } + + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.cvtsi2sd_mr(src.offset, src.base, dest); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + ASSERT(isSSE2Present()); + + if (cond & DoubleConditionBitInvert) + m_assembler.ucomisd_rr(left, right); + else + m_assembler.ucomisd_rr(right, left); + + if (cond == DoubleEqual) { + Jump isUnordered(m_assembler.jp()); + Jump result = Jump(m_assembler.je()); + isUnordered.link(this); + return result; + } else if (cond == DoubleNotEqualOrUnordered) { + Jump isUnordered(m_assembler.jp()); + Jump isEqual(m_assembler.je()); + isUnordered.link(this); + Jump result = jump(); + isEqual.link(this); + return result; + } + + ASSERT(!(cond & DoubleConditionBitSpecial)); + return Jump(m_assembler.jCC(static_cast(cond & ~DoubleConditionBits))); + } + + // Truncates 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, INT_MIN). + enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2si_rr(src, dest); + return branch32(branchType ? NotEqual : Equal, dest, TrustedImm32(0x80000000)); + } + + Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2si_rr(src, dest); + return branch32(branchType ? GreaterThanOrEqual : LessThan, dest, TrustedImm32(0)); + } + + void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2si_rr(src, dest); + } + +#if CPU(X86_64) + void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2siq_rr(src, dest); + } +#endif + + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2si_rr(src, dest); + + // If the result is zero, it might have been -0.0, and the double comparison won't catch this! + failureCases.append(branchTest32(Zero, dest)); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + convertInt32ToDouble(dest, fpTemp); + m_assembler.ucomisd_rr(fpTemp, src); + failureCases.append(m_assembler.jp()); + failureCases.append(m_assembler.jne()); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) + { + ASSERT(isSSE2Present()); + m_assembler.xorpd_rr(scratch, scratch); + return branchDouble(DoubleNotEqual, reg, scratch); + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) + { + ASSERT(isSSE2Present()); + m_assembler.xorpd_rr(scratch, scratch); + return branchDouble(DoubleEqualOrUnordered, reg, scratch); + } + + void lshiftPacked(TrustedImm32 imm, XMMRegisterID reg) + { + ASSERT(isSSE2Present()); + m_assembler.psllq_i8r(imm.m_value, reg); + } + + void rshiftPacked(TrustedImm32 imm, XMMRegisterID reg) + { + ASSERT(isSSE2Present()); + m_assembler.psrlq_i8r(imm.m_value, reg); + } + + void orPacked(XMMRegisterID src, XMMRegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.por_rr(src, dst); + } + + void moveInt32ToPacked(RegisterID src, XMMRegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.movd_rr(src, dst); + } + + void movePackedToInt32(XMMRegisterID src, RegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.movd_rr(src, dst); + } + + // Stack manipulation operations: + // + // The ABI is assumed to provide a stack abstraction to memory, + // containing machine word sized units of data. Push and pop + // operations add and remove a single register sized unit of data + // to or from the stack. Peek and poke operations read or write + // values on the stack, without moving the current stack position. + + void pop(RegisterID dest) + { + m_assembler.pop_r(dest); + } + + void push(RegisterID src) + { + m_assembler.push_r(src); + } + + void push(Address address) + { + m_assembler.push_m(address.offset, address.base); + } + + void push(TrustedImm32 imm) + { + m_assembler.push_i32(imm.m_value); + } + + + // Register move operations: + // + // Move values in registers. + + void move(TrustedImm32 imm, RegisterID dest) + { + // Note: on 64-bit the TrustedImm32 value is zero extended into the register, it + // may be useful to have a separate version that sign extends the value? + if (!imm.m_value) + m_assembler.xorl_rr(dest, dest); + else + m_assembler.movl_i32r(imm.m_value, dest); + } + +#if CPU(X86_64) + void move(RegisterID src, RegisterID dest) + { + // Note: on 64-bit this is is a full register move; perhaps it would be + // useful to have separate move32 & movePtr, with move32 zero extending? + if (src != dest) + m_assembler.movq_rr(src, dest); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + m_assembler.movq_i64r(imm.asIntptr(), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + if (reg1 != reg2) + m_assembler.xchgq_rr(reg1, reg2); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + m_assembler.movsxd_rr(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + m_assembler.movl_rr(src, dest); + } +#else + void move(RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.movl_rr(src, dest); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + m_assembler.movl_i32r(imm.asIntptr(), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + if (reg1 != reg2) + m_assembler.xchgl_rr(reg1, reg2); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + move(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + move(src, dest); + } +#endif + + + // Forwards / external control flow operations: + // + // This set of jump and conditional branch operations return a Jump + // object which may linked at a later point, allow forwards jump, + // or jumps that will require external linkage (after the code has been + // relocated). + // + // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge + // respecitvely, for unsigned comparisons the names b, a, be, and ae are + // used (representing the names 'below' and 'above'). + // + // Operands to the comparision are provided in the expected order, e.g. + // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when + // treated as a signed 32bit value, is less than or equal to 5. + // + // jz and jnz test whether the first operand is equal to zero, and take + // an optional second operand of a mask under which to perform the test. + +public: + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + m_assembler.cmpb_im(right.m_value, left.offset, left.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) + { + m_assembler.cmpl_rr(right, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testl_rr(left, left); + else + m_assembler.cmpl_ir(right.m_value, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + m_assembler.cmpl_mr(right.offset, right.base, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + m_assembler.cmpl_rm(right, left.offset, left.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + m_assembler.cmpl_im(right.m_value, left.offset, left.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + m_assembler.cmpl_im(right.m_value, left.offset, left.base, left.index, left.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + return branch32(cond, left, right); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + m_assembler.testl_rr(reg, mask); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + // if we are only interested in the low seven bits, this can be tested with a testb + if (mask.m_value == -1) + m_assembler.testl_rr(reg, reg); + else + m_assembler.testl_i32r(mask.m_value, reg); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + if (mask.m_value == -1) + m_assembler.cmpl_im(0, address.offset, address.base); + else + m_assembler.testl_i32m(mask.m_value, address.offset, address.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + if (mask.m_value == -1) + m_assembler.cmpl_im(0, address.offset, address.base, address.index, address.scale); + else + m_assembler.testl_i32m(mask.m_value, address.offset, address.base, address.index, address.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + // Byte in TrustedImm32 is not well defined, so be a little permisive here, but don't accept nonsense values. + ASSERT(mask.m_value >= -128 && mask.m_value <= 255); + if (mask.m_value == -1) + m_assembler.cmpb_im(0, address.offset, address.base); + else + m_assembler.testb_im(mask.m_value, address.offset, address.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest8(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + // Byte in TrustedImm32 is not well defined, so be a little permisive here, but don't accept nonsense values. + ASSERT(mask.m_value >= -128 && mask.m_value <= 255); + if (mask.m_value == -1) + m_assembler.cmpb_im(0, address.offset, address.base, address.index, address.scale); + else + m_assembler.testb_im(mask.m_value, address.offset, address.base, address.index, address.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(right.m_value & 0xFFFFFF00)); + + m_assembler.cmpb_im(right.m_value, left.offset, left.base, left.index, left.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump jump() + { + return Jump(m_assembler.jmp()); + } + + void jump(RegisterID target) + { + m_assembler.jmp_r(target); + } + + // Address is a memory location containing the address to jump to + void jump(Address address) + { + m_assembler.jmp_m(address.offset, address.base); + } + + + // Arithmetic control flow operations: + // + // This set of conditional branch operations branch based + // on the result of an arithmetic operation. The operation + // is performed as normal, storing the result. + // + // * jz operations branch if the result is zero. + // * jo operations branch if the (signed) arithmetic + // operation caused an overflow to occur. + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + add32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + add32(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 src, Address dest) + { + add32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, Address dest) + { + add32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, Address src, RegisterID dest) + { + add32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + if (src1 == dest) + return branchAdd32(cond, src2, dest); + move(src2, dest); + return branchAdd32(cond, src1, dest); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + move(src, dest); + return branchAdd32(cond, imm, dest); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + mul32(src, dest); + if (cond != Overflow) + m_assembler.testl_rr(dest, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchMul32(ResultCondition cond, Address src, RegisterID dest) + { + mul32(src, dest); + if (cond != Overflow) + m_assembler.testl_rr(dest, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + mul32(imm, src, dest); + if (cond != Overflow) + m_assembler.testl_rr(dest, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + if (src1 == dest) + return branchMul32(cond, src2, dest); + move(src2, dest); + return branchMul32(cond, src1, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + sub32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + sub32(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, Address dest) + { + sub32(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, Address dest) + { + sub32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, Address src, RegisterID dest) + { + sub32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + // B := A - B is invalid. + ASSERT(src1 == dest || src2 != dest); + + move(src1, dest); + return branchSub32(cond, src2, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) + { + move(src1, dest); + return branchSub32(cond, src2, dest); + } + + Jump branchNeg32(ResultCondition cond, RegisterID srcDest) + { + neg32(srcDest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + or32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + + // Miscellaneous operations: + + void breakpoint() + { + m_assembler.int3(); + } + + Call nearCall() + { + return Call(m_assembler.call(), Call::LinkableNear); + } + + Call call(RegisterID target) + { + return Call(m_assembler.call(target), Call::None); + } + + void call(Address address) + { + m_assembler.call_m(address.offset, address.base); + } + + void ret() + { + m_assembler.ret(); + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + m_assembler.cmpb_im(right.m_value, left.offset, left.base); + set32(x86Condition(cond), dest); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmpl_rr(right, left); + set32(x86Condition(cond), dest); + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testl_rr(left, left); + else + m_assembler.cmpl_ir(right.m_value, left); + set32(x86Condition(cond), dest); + } + + // FIXME: + // The mask should be optional... perhaps the argument order should be + // dest-src, operations always have a dest? ... possibly not true, considering + // asm ops like test, or pseudo ops like pop(). + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + if (mask.m_value == -1) + m_assembler.cmpb_im(0, address.offset, address.base); + else + m_assembler.testb_im(mask.m_value, address.offset, address.base); + set32(x86Condition(cond), dest); + } + + void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + if (mask.m_value == -1) + m_assembler.cmpl_im(0, address.offset, address.base); + else + m_assembler.testl_i32m(mask.m_value, address.offset, address.base); + set32(x86Condition(cond), dest); + } + + // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. + static RelationalCondition invert(RelationalCondition cond) + { + return static_cast(cond ^ 1); + } + + void nop() + { + m_assembler.nop(); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + X86Assembler::replaceWithJump(instructionStart.executableAddress(), destination.executableAddress()); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return X86Assembler::maxJumpReplacementSize(); + } + +protected: + X86Assembler::Condition x86Condition(RelationalCondition cond) + { + return static_cast(cond); + } + + X86Assembler::Condition x86Condition(ResultCondition cond) + { + return static_cast(cond); + } + + void set32(X86Assembler::Condition cond, RegisterID dest) + { +#if CPU(X86) + // On 32-bit x86 we can only set the first 4 registers; + // esp..edi are mapped to the 'h' registers! + if (dest >= 4) { + m_assembler.xchgl_rr(dest, X86Registers::eax); + m_assembler.setCC_r(cond, X86Registers::eax); + m_assembler.movzbl_rr(X86Registers::eax, X86Registers::eax); + m_assembler.xchgl_rr(dest, X86Registers::eax); + return; + } +#endif + m_assembler.setCC_r(cond, dest); + m_assembler.movzbl_rr(dest, dest); + } + +private: + // Only MacroAssemblerX86 should be using the following method; SSE2 is always available on + // x86_64, and clients & subclasses of MacroAssembler should be using 'supportsFloatingPoint()'. + friend class MacroAssemblerX86; + +#if CPU(X86) +#if OS(MAC_OS_X) + + // All X86 Macs are guaranteed to support at least SSE2, + static bool isSSE2Present() + { + return true; + } + +#else // OS(MAC_OS_X) + + enum SSE2CheckState { + NotCheckedSSE2, + HasSSE2, + NoSSE2 + }; + + static bool isSSE2Present() + { + if (s_sse2CheckState == NotCheckedSSE2) { + // Default the flags value to zero; if the compiler is + // not MSVC or GCC we will read this as SSE2 not present. + int flags = 0; +#if COMPILER(MSVC) + _asm { + mov eax, 1 // cpuid function 1 gives us the standard feature set + cpuid; + mov flags, edx; + } +#elif COMPILER(GCC) + asm ( + "movl $0x1, %%eax;" + "pushl %%ebx;" + "cpuid;" + "popl %%ebx;" + "movl %%edx, %0;" + : "=g" (flags) + : + : "%eax", "%ecx", "%edx" + ); +#endif + static const int SSE2FeatureBit = 1 << 26; + s_sse2CheckState = (flags & SSE2FeatureBit) ? HasSSE2 : NoSSE2; + } + // Only check once. + ASSERT(s_sse2CheckState != NotCheckedSSE2); + + return s_sse2CheckState == HasSSE2; + } + + static SSE2CheckState s_sse2CheckState; + +#endif // OS(MAC_OS_X) +#elif !defined(NDEBUG) // CPU(X86) + + // On x86-64 we should never be checking for SSE2 in a non-debug build, + // but non debug add this method to keep the asserts above happy. + static bool isSSE2Present() + { + return true; + } + +#endif +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerX86Common_h diff --git a/masm/assembler/MacroAssemblerX86_64.h b/masm/assembler/MacroAssemblerX86_64.h new file mode 100644 index 0000000000..ac90516f41 --- /dev/null +++ b/masm/assembler/MacroAssemblerX86_64.h @@ -0,0 +1,603 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerX86_64_h +#define MacroAssemblerX86_64_h + +#if ENABLE(ASSEMBLER) && CPU(X86_64) + +#include "MacroAssemblerX86Common.h" + +#define REPTACH_OFFSET_CALL_R11 3 + +namespace JSC { + +class MacroAssemblerX86_64 : public MacroAssemblerX86Common { +public: + static const Scale ScalePtr = TimesEight; + + using MacroAssemblerX86Common::add32; + using MacroAssemblerX86Common::and32; + using MacroAssemblerX86Common::branchAdd32; + using MacroAssemblerX86Common::or32; + using MacroAssemblerX86Common::sub32; + using MacroAssemblerX86Common::load32; + using MacroAssemblerX86Common::store32; + using MacroAssemblerX86Common::call; + using MacroAssemblerX86Common::jump; + using MacroAssemblerX86Common::addDouble; + using MacroAssemblerX86Common::loadDouble; + using MacroAssemblerX86Common::convertInt32ToDouble; + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + add32(imm, Address(scratchRegister)); + } + + void and32(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + and32(imm, Address(scratchRegister)); + } + + void add32(AbsoluteAddress address, RegisterID dest) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + add32(Address(scratchRegister), dest); + } + + void or32(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + or32(imm, Address(scratchRegister)); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + sub32(imm, Address(scratchRegister)); + } + + void load32(const void* address, RegisterID dest) + { + if (dest == X86Registers::eax) + m_assembler.movl_mEAX(address); + else { + move(TrustedImmPtr(address), dest); + load32(dest, dest); + } + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + m_assembler.addsd_mr(0, scratchRegister, dest); + } + + void convertInt32ToDouble(TrustedImm32 imm, FPRegisterID dest) + { + move(imm, scratchRegister); + m_assembler.cvtsi2sd_rr(scratchRegister, dest); + } + + void store32(TrustedImm32 imm, void* address) + { + move(TrustedImmPtr(address), scratchRegister); + store32(imm, scratchRegister); + } + + Call call() + { + DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); + Call result = Call(m_assembler.call(scratchRegister), Call::Linkable); + ASSERT_UNUSED(label, differenceBetween(label, result) == REPTACH_OFFSET_CALL_R11); + return result; + } + + // Address is a memory location containing the address to jump to + void jump(AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + jump(Address(scratchRegister)); + } + + Call tailRecursiveCall() + { + DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); + Jump newJump = Jump(m_assembler.jmp_r(scratchRegister)); + ASSERT_UNUSED(label, differenceBetween(label, newJump) == REPTACH_OFFSET_CALL_R11); + return Call::fromTailJump(newJump); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + oldJump.link(this); + DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); + Jump newJump = Jump(m_assembler.jmp_r(scratchRegister)); + ASSERT_UNUSED(label, differenceBetween(label, newJump) == REPTACH_OFFSET_CALL_R11); + return Call::fromTailJump(newJump); + } + + + void addPtr(RegisterID src, RegisterID dest) + { + m_assembler.addq_rr(src, dest); + } + + void addPtr(Address src, RegisterID dest) + { + m_assembler.addq_mr(src.offset, src.base, dest); + } + + void addPtr(AbsoluteAddress src, RegisterID dest) + { + move(TrustedImmPtr(src.m_ptr), scratchRegister); + addPtr(Address(scratchRegister), dest); + } + + void addPtr(TrustedImm32 imm, RegisterID srcDest) + { + m_assembler.addq_ir(imm.m_value, srcDest); + } + + void addPtr(TrustedImmPtr imm, RegisterID dest) + { + move(imm, scratchRegister); + m_assembler.addq_rr(scratchRegister, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.leaq_mr(imm.m_value, src, dest); + } + + void addPtr(TrustedImm32 imm, Address address) + { + m_assembler.addq_im(imm.m_value, address.offset, address.base); + } + + void addPtr(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + addPtr(imm, Address(scratchRegister)); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + addPtr(imm, address); + } + + void andPtr(RegisterID src, RegisterID dest) + { + m_assembler.andq_rr(src, dest); + } + + void andPtr(TrustedImm32 imm, RegisterID srcDest) + { + m_assembler.andq_ir(imm.m_value, srcDest); + } + + void negPtr(RegisterID dest) + { + m_assembler.negq_r(dest); + } + + void orPtr(RegisterID src, RegisterID dest) + { + m_assembler.orq_rr(src, dest); + } + + void orPtr(TrustedImmPtr imm, RegisterID dest) + { + move(imm, scratchRegister); + m_assembler.orq_rr(scratchRegister, dest); + } + + void orPtr(TrustedImm32 imm, RegisterID dest) + { + m_assembler.orq_ir(imm.m_value, dest); + } + + void orPtr(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + move(op1, dest); + else if (op1 == dest) + orPtr(op2, dest); + else { + move(op2, dest); + orPtr(op1, dest); + } + } + + void orPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(src, dest); + orPtr(imm, dest); + } + + void rotateRightPtr(TrustedImm32 imm, RegisterID srcDst) + { + m_assembler.rorq_i8r(imm.m_value, srcDst); + } + + void subPtr(RegisterID src, RegisterID dest) + { + m_assembler.subq_rr(src, dest); + } + + void subPtr(TrustedImm32 imm, RegisterID dest) + { + m_assembler.subq_ir(imm.m_value, dest); + } + + void subPtr(TrustedImmPtr imm, RegisterID dest) + { + move(imm, scratchRegister); + m_assembler.subq_rr(scratchRegister, dest); + } + + void xorPtr(RegisterID src, RegisterID dest) + { + m_assembler.xorq_rr(src, dest); + } + + void xorPtr(RegisterID src, Address dest) + { + m_assembler.xorq_rm(src, dest.offset, dest.base); + } + + void xorPtr(TrustedImm32 imm, RegisterID srcDest) + { + m_assembler.xorq_ir(imm.m_value, srcDest); + } + + void loadPtr(ImplicitAddress address, RegisterID dest) + { + m_assembler.movq_mr(address.offset, address.base, dest); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result = ConvertibleLoadLabel(this); + m_assembler.movq_mr(address.offset, address.base, dest); + return result; + } + + void loadPtr(BaseIndex address, RegisterID dest) + { + m_assembler.movq_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void loadPtr(const void* address, RegisterID dest) + { + if (dest == X86Registers::eax) + m_assembler.movq_mEAX(address); + else { + move(TrustedImmPtr(address), dest); + loadPtr(dest, dest); + } + } + + DataLabel32 loadPtrWithAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movq_mr_disp32(address.offset, address.base, dest); + return DataLabel32(this); + } + + DataLabelCompact loadPtrWithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movq_mr_disp8(address.offset, address.base, dest); + return DataLabelCompact(this); + } + + void storePtr(RegisterID src, ImplicitAddress address) + { + m_assembler.movq_rm(src, address.offset, address.base); + } + + void storePtr(RegisterID src, BaseIndex address) + { + m_assembler.movq_rm(src, address.offset, address.base, address.index, address.scale); + } + + void storePtr(RegisterID src, void* address) + { + if (src == X86Registers::eax) + m_assembler.movq_EAXm(address); + else { + move(TrustedImmPtr(address), scratchRegister); + storePtr(src, scratchRegister); + } + } + + void storePtr(TrustedImmPtr imm, ImplicitAddress address) + { + move(imm, scratchRegister); + storePtr(scratchRegister, address); + } + + void storePtr(TrustedImmPtr imm, BaseIndex address) + { + move(imm, scratchRegister); + m_assembler.movq_rm(scratchRegister, address.offset, address.base, address.index, address.scale); + } + + DataLabel32 storePtrWithAddressOffsetPatch(RegisterID src, Address address) + { + padBeforePatch(); + m_assembler.movq_rm_disp32(src, address.offset, address.base); + return DataLabel32(this); + } + + void movePtrToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.movq_rr(src, dest); + } + + void moveDoubleToPtr(FPRegisterID src, RegisterID dest) + { + m_assembler.movq_rr(src, dest); + } + + void comparePtr(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testq_rr(left, left); + else + m_assembler.cmpq_ir(right.m_value, left); + m_assembler.setCC_r(x86Condition(cond), dest); + m_assembler.movzbl_rr(dest, dest); + } + + void comparePtr(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmpq_rr(right, left); + m_assembler.setCC_r(x86Condition(cond), dest); + m_assembler.movzbl_rr(dest, dest); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 src, AbsoluteAddress dest) + { + move(TrustedImmPtr(dest.m_ptr), scratchRegister); + add32(src, Address(scratchRegister)); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, RegisterID right) + { + m_assembler.cmpq_rr(right, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, TrustedImmPtr right) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) { + m_assembler.testq_rr(left, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + move(right, scratchRegister); + return branchPtr(cond, left, scratchRegister); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, Address right) + { + m_assembler.cmpq_mr(right.offset, right.base, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + move(TrustedImmPtr(left.m_ptr), scratchRegister); + return branchPtr(cond, Address(scratchRegister), right); + } + + Jump branchPtr(RelationalCondition cond, Address left, RegisterID right) + { + m_assembler.cmpq_rm(right, left.offset, left.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchPtr(RelationalCondition cond, Address left, TrustedImmPtr right) + { + move(right, scratchRegister); + return branchPtr(cond, left, scratchRegister); + } + + Jump branchTestPtr(ResultCondition cond, RegisterID reg, RegisterID mask) + { + m_assembler.testq_rr(reg, mask); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTestPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + // if we are only interested in the low seven bits, this can be tested with a testb + if (mask.m_value == -1) + m_assembler.testq_rr(reg, reg); + else if ((mask.m_value & ~0x7f) == 0) + m_assembler.testb_i8r(mask.m_value, reg); + else + m_assembler.testq_i32r(mask.m_value, reg); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + void testPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) + { + if (mask.m_value == -1) + m_assembler.testq_rr(reg, reg); + else if ((mask.m_value & ~0x7f) == 0) + m_assembler.testb_i8r(mask.m_value, reg); + else + m_assembler.testq_i32r(mask.m_value, reg); + set32(x86Condition(cond), dest); + } + + void testPtr(ResultCondition cond, RegisterID reg, RegisterID mask, RegisterID dest) + { + m_assembler.testq_rr(reg, mask); + set32(x86Condition(cond), dest); + } + + Jump branchTestPtr(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + loadPtr(address.m_ptr, scratchRegister); + return branchTestPtr(cond, scratchRegister, mask); + } + + Jump branchTestPtr(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + if (mask.m_value == -1) + m_assembler.cmpq_im(0, address.offset, address.base); + else + m_assembler.testq_i32m(mask.m_value, address.offset, address.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTestPtr(ResultCondition cond, Address address, RegisterID reg) + { + m_assembler.testq_rm(reg, address.offset, address.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTestPtr(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + if (mask.m_value == -1) + m_assembler.cmpq_im(0, address.offset, address.base, address.index, address.scale); + else + m_assembler.testq_i32m(mask.m_value, address.offset, address.base, address.index, address.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + + Jump branchAddPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + addPtr(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAddPtr(ResultCondition cond, RegisterID src, RegisterID dest) + { + addPtr(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSubPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + subPtr(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSubPtr(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) + { + move(src1, dest); + return branchSubPtr(cond, src2, dest); + } + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + padBeforePatch(); + m_assembler.movq_i64r(initialValue.asIntptr(), dest); + return DataLabelPtr(this); + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + dataLabel = moveWithPatch(initialRightValue, scratchRegister); + return branchPtr(cond, left, scratchRegister); + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + dataLabel = moveWithPatch(initialRightValue, scratchRegister); + return branchPtr(cond, left, scratchRegister); + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + DataLabelPtr label = moveWithPatch(initialValue, scratchRegister); + storePtr(scratchRegister, address); + return label; + } + + using MacroAssemblerX86Common::branchTest8; + Jump branchTest8(ResultCondition cond, ExtendedAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + TrustedImmPtr addr(reinterpret_cast(address.offset)); + MacroAssemblerX86Common::move(addr, scratchRegister); + return MacroAssemblerX86Common::branchTest8(cond, BaseIndex(scratchRegister, address.base, TimesOne), mask); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + MacroAssemblerX86Common::move(TrustedImmPtr(address.m_ptr), scratchRegister); + return MacroAssemblerX86Common::branchTest8(cond, Address(scratchRegister), mask); + } + + static bool supportsFloatingPoint() { return true; } + // See comment on MacroAssemblerARMv7::supportsFloatingPointTruncate() + static bool supportsFloatingPointTruncate() { return true; } + static bool supportsFloatingPointSqrt() { return true; } + static bool supportsFloatingPointAbs() { return true; } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(X86Assembler::readPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation())); + } + + static RegisterID scratchRegisterForBlinding() { return scratchRegister; } + +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + if (!call.isFlagSet(Call::Near)) + X86Assembler::linkPointer(code, call.m_label.labelAtOffset(-REPTACH_OFFSET_CALL_R11), function.value()); + else + X86Assembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + X86Assembler::repatchPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + X86Assembler::repatchPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation(), destination.executableAddress()); + } + +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerX86_64_h diff --git a/masm/assembler/RepatchBuffer.h b/masm/assembler/RepatchBuffer.h new file mode 100644 index 0000000000..531dda934b --- /dev/null +++ b/masm/assembler/RepatchBuffer.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RepatchBuffer_h +#define RepatchBuffer_h + +#if ENABLE(JIT) + +#include "CodeBlock.h" +#include +#include + +namespace JSC { + +// RepatchBuffer: +// +// This class is used to modify code after code generation has been completed, +// and after the code has potentially already been executed. This mechanism is +// used to apply optimizations to the code. +// +class RepatchBuffer { + typedef MacroAssemblerCodePtr CodePtr; + +public: + RepatchBuffer(CodeBlock* codeBlock) + { + JITCode& code = codeBlock->getJITCode(); + m_start = code.start(); + m_size = code.size(); + + ExecutableAllocator::makeWritable(m_start, m_size); + } + + ~RepatchBuffer() + { + ExecutableAllocator::makeExecutable(m_start, m_size); + } + + void relink(CodeLocationJump jump, CodeLocationLabel destination) + { + MacroAssembler::repatchJump(jump, destination); + } + + void relink(CodeLocationCall call, CodeLocationLabel destination) + { + MacroAssembler::repatchCall(call, destination); + } + + void relink(CodeLocationCall call, FunctionPtr destination) + { + MacroAssembler::repatchCall(call, destination); + } + + void relink(CodeLocationNearCall nearCall, CodePtr destination) + { + MacroAssembler::repatchNearCall(nearCall, CodeLocationLabel(destination)); + } + + void relink(CodeLocationNearCall nearCall, CodeLocationLabel destination) + { + MacroAssembler::repatchNearCall(nearCall, destination); + } + + void repatch(CodeLocationDataLabel32 dataLabel32, int32_t value) + { + MacroAssembler::repatchInt32(dataLabel32, value); + } + + void repatch(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) + { + MacroAssembler::repatchCompact(dataLabelCompact, value); + } + + void repatch(CodeLocationDataLabelPtr dataLabelPtr, void* value) + { + MacroAssembler::repatchPointer(dataLabelPtr, value); + } + + void relinkCallerToTrampoline(ReturnAddressPtr returnAddress, CodeLocationLabel label) + { + relink(CodeLocationCall(CodePtr(returnAddress)), label); + } + + void relinkCallerToTrampoline(ReturnAddressPtr returnAddress, CodePtr newCalleeFunction) + { + relinkCallerToTrampoline(returnAddress, CodeLocationLabel(newCalleeFunction)); + } + + void relinkCallerToFunction(ReturnAddressPtr returnAddress, FunctionPtr function) + { + relink(CodeLocationCall(CodePtr(returnAddress)), function); + } + + void relinkNearCallerToTrampoline(ReturnAddressPtr returnAddress, CodeLocationLabel label) + { + relink(CodeLocationNearCall(CodePtr(returnAddress)), label); + } + + void relinkNearCallerToTrampoline(ReturnAddressPtr returnAddress, CodePtr newCalleeFunction) + { + relinkNearCallerToTrampoline(returnAddress, CodeLocationLabel(newCalleeFunction)); + } + + void replaceWithLoad(CodeLocationConvertibleLoad label) + { + MacroAssembler::replaceWithLoad(label); + } + + void replaceWithAddressComputation(CodeLocationConvertibleLoad label) + { + MacroAssembler::replaceWithAddressComputation(label); + } + + void setLoadInstructionIsActive(CodeLocationConvertibleLoad label, bool isActive) + { + if (isActive) + replaceWithLoad(label); + else + replaceWithAddressComputation(label); + } + +private: + void* m_start; + size_t m_size; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // RepatchBuffer_h diff --git a/masm/assembler/SH4Assembler.h b/masm/assembler/SH4Assembler.h new file mode 100644 index 0000000000..373f469dcc --- /dev/null +++ b/masm/assembler/SH4Assembler.h @@ -0,0 +1,2121 @@ +/* + * Copyright (C) 2009-2011 STMicroelectronics. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SH4Assembler_h +#define SH4Assembler_h + +#if ENABLE(ASSEMBLER) && CPU(SH4) + +#include "AssemblerBuffer.h" +#include "AssemblerBufferWithConstantPool.h" +#include "JITCompilationEffort.h" +#include +#include +#include +#include +#include +#include + +#ifndef NDEBUG +#define SH4_ASSEMBLER_TRACING +#endif + +namespace JSC { +typedef uint16_t SH4Word; + +enum { + INVALID_OPCODE = 0xffff, + ADD_OPCODE = 0x300c, + ADDIMM_OPCODE = 0x7000, + ADDC_OPCODE = 0x300e, + ADDV_OPCODE = 0x300f, + AND_OPCODE = 0x2009, + ANDIMM_OPCODE = 0xc900, + DIV0_OPCODE = 0x2007, + DIV1_OPCODE = 0x3004, + BF_OPCODE = 0x8b00, + BFS_OPCODE = 0x8f00, + BRA_OPCODE = 0xa000, + BRAF_OPCODE = 0x0023, + NOP_OPCODE = 0x0009, + BSR_OPCODE = 0xb000, + RTS_OPCODE = 0x000b, + BT_OPCODE = 0x8900, + BTS_OPCODE = 0x8d00, + BSRF_OPCODE = 0x0003, + BRK_OPCODE = 0x003b, + FTRC_OPCODE = 0xf03d, + CMPEQ_OPCODE = 0x3000, + CMPEQIMM_OPCODE = 0x8800, + CMPGE_OPCODE = 0x3003, + CMPGT_OPCODE = 0x3007, + CMPHI_OPCODE = 0x3006, + CMPHS_OPCODE = 0x3002, + CMPPL_OPCODE = 0x4015, + CMPPZ_OPCODE = 0x4011, + CMPSTR_OPCODE = 0x200c, + DT_OPCODE = 0x4010, + FCMPEQ_OPCODE = 0xf004, + FCMPGT_OPCODE = 0xf005, + FMOV_OPCODE = 0xf00c, + FADD_OPCODE = 0xf000, + FMUL_OPCODE = 0xf002, + FSUB_OPCODE = 0xf001, + FDIV_OPCODE = 0xf003, + FNEG_OPCODE = 0xf04d, + JMP_OPCODE = 0x402b, + JSR_OPCODE = 0x400b, + LDSPR_OPCODE = 0x402a, + LDSLPR_OPCODE = 0x4026, + MOV_OPCODE = 0x6003, + MOVIMM_OPCODE = 0xe000, + MOVB_WRITE_RN_OPCODE = 0x2000, + MOVB_WRITE_RNDEC_OPCODE = 0x2004, + MOVB_WRITE_R0RN_OPCODE = 0x0004, + MOVB_WRITE_OFFGBR_OPCODE = 0xc000, + MOVB_WRITE_OFFRN_OPCODE = 0x8000, + MOVB_READ_RM_OPCODE = 0x6000, + MOVB_READ_RMINC_OPCODE = 0x6004, + MOVB_READ_R0RM_OPCODE = 0x000c, + MOVB_READ_OFFGBR_OPCODE = 0xc400, + MOVB_READ_OFFRM_OPCODE = 0x8400, + MOVL_WRITE_RN_OPCODE = 0x2002, + MOVL_WRITE_RNDEC_OPCODE = 0x2006, + MOVL_WRITE_R0RN_OPCODE = 0x0006, + MOVL_WRITE_OFFGBR_OPCODE = 0xc200, + MOVL_WRITE_OFFRN_OPCODE = 0x1000, + MOVL_READ_RM_OPCODE = 0x6002, + MOVL_READ_RMINC_OPCODE = 0x6006, + MOVL_READ_R0RM_OPCODE = 0x000e, + MOVL_READ_OFFGBR_OPCODE = 0xc600, + MOVL_READ_OFFPC_OPCODE = 0xd000, + MOVL_READ_OFFRM_OPCODE = 0x5000, + MOVW_WRITE_RN_OPCODE = 0x2001, + MOVW_READ_RM_OPCODE = 0x6001, + MOVW_READ_R0RM_OPCODE = 0x000d, + MOVW_READ_OFFRM_OPCODE = 0x8500, + MOVW_READ_OFFPC_OPCODE = 0x9000, + MOVA_READ_OFFPC_OPCODE = 0xc700, + MOVT_OPCODE = 0x0029, + MULL_OPCODE = 0x0007, + DMULL_L_OPCODE = 0x3005, + STSMACL_OPCODE = 0x001a, + STSMACH_OPCODE = 0x000a, + DMULSL_OPCODE = 0x300d, + NEG_OPCODE = 0x600b, + NEGC_OPCODE = 0x600a, + NOT_OPCODE = 0x6007, + OR_OPCODE = 0x200b, + ORIMM_OPCODE = 0xcb00, + ORBIMM_OPCODE = 0xcf00, + SETS_OPCODE = 0x0058, + SETT_OPCODE = 0x0018, + SHAD_OPCODE = 0x400c, + SHAL_OPCODE = 0x4020, + SHAR_OPCODE = 0x4021, + SHLD_OPCODE = 0x400d, + SHLL_OPCODE = 0x4000, + SHLL2_OPCODE = 0x4008, + SHLL8_OPCODE = 0x4018, + SHLL16_OPCODE = 0x4028, + SHLR_OPCODE = 0x4001, + SHLR2_OPCODE = 0x4009, + SHLR8_OPCODE = 0x4019, + SHLR16_OPCODE = 0x4029, + STSPR_OPCODE = 0x002a, + STSLPR_OPCODE = 0x4022, + FLOAT_OPCODE = 0xf02d, + SUB_OPCODE = 0x3008, + SUBC_OPCODE = 0x300a, + SUBV_OPCODE = 0x300b, + TST_OPCODE = 0x2008, + TSTIMM_OPCODE = 0xc800, + TSTB_OPCODE = 0xcc00, + EXTUB_OPCODE = 0x600c, + EXTUW_OPCODE = 0x600d, + XOR_OPCODE = 0x200a, + XORIMM_OPCODE = 0xca00, + XORB_OPCODE = 0xce00, + FMOVS_READ_RM_INC_OPCODE = 0xf009, + FMOVS_READ_RM_OPCODE = 0xf008, + FMOVS_READ_R0RM_OPCODE = 0xf006, + FMOVS_WRITE_RN_OPCODE = 0xf00a, + FMOVS_WRITE_RN_DEC_OPCODE = 0xf00b, + FMOVS_WRITE_R0RN_OPCODE = 0xf007, + FCNVDS_DRM_FPUL_OPCODE = 0xf0bd, + LDS_RM_FPUL_OPCODE = 0x405a, + FLDS_FRM_FPUL_OPCODE = 0xf01d, + STS_FPUL_RN_OPCODE = 0x005a, + FSTS_FPUL_FRN_OPCODE = 0xF00d, + LDSFPSCR_OPCODE = 0x406a, + STSFPSCR_OPCODE = 0x006a, + LDSRMFPUL_OPCODE = 0x405a, + FSTSFPULFRN_OPCODE = 0xf00d, + FSQRT_OPCODE = 0xf06d, + FSCHG_OPCODE = 0xf3fd, + CLRT_OPCODE = 8, +}; + +namespace SH4Registers { +typedef enum { + r0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + r13, + r14, fp = r14, + r15, sp = r15, + pc, + pr, +} RegisterID; + +typedef enum { + fr0, dr0 = fr0, + fr1, + fr2, dr2 = fr2, + fr3, + fr4, dr4 = fr4, + fr5, + fr6, dr6 = fr6, + fr7, + fr8, dr8 = fr8, + fr9, + fr10, dr10 = fr10, + fr11, + fr12, dr12 = fr12, + fr13, + fr14, dr14 = fr14, + fr15, +} FPRegisterID; +} + +inline uint16_t getOpcodeGroup1(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0xf) << 8) | ((rn & 0xf) << 4)); +} + +inline uint16_t getOpcodeGroup2(uint16_t opc, int rm) +{ + return (opc | ((rm & 0xf) << 8)); +} + +inline uint16_t getOpcodeGroup3(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0xf) << 8) | (rn & 0xff)); +} + +inline uint16_t getOpcodeGroup4(uint16_t opc, int rm, int rn, int offset) +{ + return (opc | ((rm & 0xf) << 8) | ((rn & 0xf) << 4) | (offset & 0xf)); +} + +inline uint16_t getOpcodeGroup5(uint16_t opc, int rm) +{ + return (opc | (rm & 0xff)); +} + +inline uint16_t getOpcodeGroup6(uint16_t opc, int rm) +{ + return (opc | (rm & 0xfff)); +} + +inline uint16_t getOpcodeGroup7(uint16_t opc, int rm) +{ + return (opc | ((rm & 0x7) << 9)); +} + +inline uint16_t getOpcodeGroup8(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0x7) << 9) | ((rn & 0x7) << 5)); +} + +inline uint16_t getOpcodeGroup9(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0xf) << 8) | ((rn & 0x7) << 5)); +} + +inline uint16_t getOpcodeGroup10(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0x7) << 9) | ((rn & 0xf) << 4)); +} + +inline uint16_t getOpcodeGroup11(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0xf) << 4) | (rn & 0xf)); +} + +inline uint16_t getRn(uint16_t x) +{ + return ((x & 0xf00) >> 8); +} + +inline uint16_t getRm(uint16_t x) +{ + return ((x & 0xf0) >> 4); +} + +inline uint16_t getDisp(uint16_t x) +{ + return (x & 0xf); +} + +inline uint16_t getImm8(uint16_t x) +{ + return (x & 0xff); +} + +inline uint16_t getImm12(uint16_t x) +{ + return (x & 0xfff); +} + +inline uint16_t getDRn(uint16_t x) +{ + return ((x & 0xe00) >> 9); +} + +inline uint16_t getDRm(uint16_t x) +{ + return ((x & 0xe0) >> 5); +} + +class SH4Assembler { +public: + typedef SH4Registers::RegisterID RegisterID; + typedef SH4Registers::FPRegisterID FPRegisterID; + typedef AssemblerBufferWithConstantPool<512, 4, 2, SH4Assembler> SH4Buffer; + static const RegisterID scratchReg1 = SH4Registers::r3; + static const RegisterID scratchReg2 = SH4Registers::r11; + static const uint32_t maxInstructionSize = 16; + + enum { + padForAlign8 = 0x00, + padForAlign16 = 0x0009, + padForAlign32 = 0x00090009, + }; + + enum JumpType { JumpFar, + JumpNear + }; + + SH4Assembler() + { + m_claimscratchReg = 0x0; + } + + // SH4 condition codes + typedef enum { + EQ = 0x0, // Equal + NE = 0x1, // Not Equal + HS = 0x2, // Unsigend Greater Than equal + HI = 0x3, // Unsigend Greater Than + LS = 0x4, // Unsigend Lower or Same + LI = 0x5, // Unsigend Lower + GE = 0x6, // Greater or Equal + LT = 0x7, // Less Than + GT = 0x8, // Greater Than + LE = 0x9, // Less or Equal + OF = 0xa, // OverFlow + SI = 0xb, // Signed + EQU= 0xc, // Equal or unordered(NaN) + NEU= 0xd, + GTU= 0xe, + GEU= 0xf, + LTU= 0x10, + LEU= 0x11, + } Condition; + + // Opaque label types +public: + bool isImmediate(int constant) + { + return ((constant <= 127) && (constant >= -128)); + } + + RegisterID claimScratch() + { + ASSERT((m_claimscratchReg != 0x3)); + + if (!(m_claimscratchReg & 0x1)) { + m_claimscratchReg = (m_claimscratchReg | 0x1); + return scratchReg1; + } + + m_claimscratchReg = (m_claimscratchReg | 0x2); + return scratchReg2; + } + + void releaseScratch(RegisterID scratchR) + { + if (scratchR == scratchReg1) + m_claimscratchReg = (m_claimscratchReg & 0x2); + else + m_claimscratchReg = (m_claimscratchReg & 0x1); + } + + // Stack operations + + void pushReg(RegisterID reg) + { + if (reg == SH4Registers::pr) { + oneShortOp(getOpcodeGroup2(STSLPR_OPCODE, SH4Registers::sp)); + return; + } + + oneShortOp(getOpcodeGroup1(MOVL_WRITE_RNDEC_OPCODE, SH4Registers::sp, reg)); + } + + void popReg(RegisterID reg) + { + if (reg == SH4Registers::pr) { + oneShortOp(getOpcodeGroup2(LDSLPR_OPCODE, SH4Registers::sp)); + return; + } + + oneShortOp(getOpcodeGroup1(MOVL_READ_RMINC_OPCODE, reg, SH4Registers::sp)); + } + + void movt(RegisterID dst) + { + uint16_t opc = getOpcodeGroup2(MOVT_OPCODE, dst); + oneShortOp(opc); + } + + // Arithmetic operations + + void addlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(ADD_OPCODE, dst, src); + oneShortOp(opc); + } + + void addclRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(ADDC_OPCODE, dst, src); + oneShortOp(opc); + } + + void addvlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(ADDV_OPCODE, dst, src); + oneShortOp(opc); + } + + void addlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 127) && (imm8 >= -128)); + + uint16_t opc = getOpcodeGroup3(ADDIMM_OPCODE, dst, imm8); + oneShortOp(opc); + } + + void andlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(AND_OPCODE, dst, src); + oneShortOp(opc); + } + + void andlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 255) && (imm8 >= 0)); + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup5(ANDIMM_OPCODE, imm8); + oneShortOp(opc); + } + + void div1lRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(DIV1_OPCODE, dst, src); + oneShortOp(opc); + } + + void div0lRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(DIV0_OPCODE, dst, src); + oneShortOp(opc); + } + + void notlReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(NOT_OPCODE, dst, src); + oneShortOp(opc); + } + + void orlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(OR_OPCODE, dst, src); + oneShortOp(opc); + } + + void orlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 255) && (imm8 >= 0)); + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup5(ORIMM_OPCODE, imm8); + oneShortOp(opc); + } + + void sublRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(SUB_OPCODE, dst, src); + oneShortOp(opc); + } + + void subvlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(SUBV_OPCODE, dst, src); + oneShortOp(opc); + } + + void xorlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(XOR_OPCODE, dst, src); + oneShortOp(opc); + } + + void xorlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 255) && (imm8 >= 0)); + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup5(XORIMM_OPCODE, imm8); + oneShortOp(opc); + } + + void shllImm8r(int imm, RegisterID dst) + { + switch (imm) { + case 1: + oneShortOp(getOpcodeGroup2(SHLL_OPCODE, dst)); + break; + case 2: + oneShortOp(getOpcodeGroup2(SHLL2_OPCODE, dst)); + break; + case 8: + oneShortOp(getOpcodeGroup2(SHLL8_OPCODE, dst)); + break; + case 16: + oneShortOp(getOpcodeGroup2(SHLL16_OPCODE, dst)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void neg(RegisterID dst, RegisterID src) + { + uint16_t opc = getOpcodeGroup1(NEG_OPCODE, dst, src); + oneShortOp(opc); + } + + void shllRegReg(RegisterID dst, RegisterID rShift) + { + uint16_t opc = getOpcodeGroup1(SHLD_OPCODE, dst, rShift); + oneShortOp(opc); + } + + void shlrRegReg(RegisterID dst, RegisterID rShift) + { + neg(rShift, rShift); + shllRegReg(dst, rShift); + } + + void sharRegReg(RegisterID dst, RegisterID rShift) + { + neg(rShift, rShift); + shaRegReg(dst, rShift); + } + + void shaRegReg(RegisterID dst, RegisterID rShift) + { + uint16_t opc = getOpcodeGroup1(SHAD_OPCODE, dst, rShift); + oneShortOp(opc); + } + + void shlrImm8r(int imm, RegisterID dst) + { + switch (imm) { + case 1: + oneShortOp(getOpcodeGroup2(SHLR_OPCODE, dst)); + break; + case 2: + oneShortOp(getOpcodeGroup2(SHLR2_OPCODE, dst)); + break; + case 8: + oneShortOp(getOpcodeGroup2(SHLR8_OPCODE, dst)); + break; + case 16: + oneShortOp(getOpcodeGroup2(SHLR16_OPCODE, dst)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void imullRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MULL_OPCODE, dst, src); + oneShortOp(opc); + } + + void dmullRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(DMULL_L_OPCODE, dst, src); + oneShortOp(opc); + } + + void dmulslRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(DMULSL_OPCODE, dst, src); + oneShortOp(opc); + } + + void stsmacl(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(STSMACL_OPCODE, reg); + oneShortOp(opc); + } + + void stsmach(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(STSMACH_OPCODE, reg); + oneShortOp(opc); + } + + // Comparisons + + void cmplRegReg(RegisterID left, RegisterID right, Condition cond) + { + switch (cond) { + case NE: + oneShortOp(getOpcodeGroup1(CMPEQ_OPCODE, right, left)); + break; + case GT: + oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, right, left)); + break; + case EQ: + oneShortOp(getOpcodeGroup1(CMPEQ_OPCODE, right, left)); + break; + case GE: + oneShortOp(getOpcodeGroup1(CMPGE_OPCODE, right, left)); + break; + case HS: + oneShortOp(getOpcodeGroup1(CMPHS_OPCODE, right, left)); + break; + case HI: + oneShortOp(getOpcodeGroup1(CMPHI_OPCODE, right, left)); + break; + case LI: + oneShortOp(getOpcodeGroup1(CMPHI_OPCODE, left, right)); + break; + case LS: + oneShortOp(getOpcodeGroup1(CMPHS_OPCODE, left, right)); + break; + case LE: + oneShortOp(getOpcodeGroup1(CMPGE_OPCODE, left, right)); + break; + case LT: + oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, left, right)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void cmppl(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(CMPPL_OPCODE, reg); + oneShortOp(opc); + } + + void cmppz(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(CMPPZ_OPCODE, reg); + oneShortOp(opc); + } + + void cmpEqImmR0(int imm, RegisterID dst) + { + uint16_t opc = getOpcodeGroup5(CMPEQIMM_OPCODE, imm); + oneShortOp(opc); + } + + void testlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(TST_OPCODE, dst, src); + oneShortOp(opc); + } + + void testlImm8r(int imm, RegisterID dst) + { + ASSERT((dst == SH4Registers::r0) && (imm <= 255) && (imm >= 0)); + + uint16_t opc = getOpcodeGroup5(TSTIMM_OPCODE, imm); + oneShortOp(opc); + } + + void nop() + { + oneShortOp(NOP_OPCODE, false); + } + + void sett() + { + oneShortOp(SETT_OPCODE); + } + + void clrt() + { + oneShortOp(CLRT_OPCODE); + } + + void fschg() + { + oneShortOp(FSCHG_OPCODE); + } + + void bkpt() + { + oneShortOp(BRK_OPCODE, false); + } + + void branch(uint16_t opc, int label) + { + switch (opc) { + case BT_OPCODE: + ASSERT((label <= 127) && (label >= -128)); + oneShortOp(getOpcodeGroup5(BT_OPCODE, label)); + break; + case BRA_OPCODE: + ASSERT((label <= 2047) && (label >= -2048)); + oneShortOp(getOpcodeGroup6(BRA_OPCODE, label)); + break; + case BF_OPCODE: + ASSERT((label <= 127) && (label >= -128)); + oneShortOp(getOpcodeGroup5(BF_OPCODE, label)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void branch(uint16_t opc, RegisterID reg) + { + switch (opc) { + case BRAF_OPCODE: + oneShortOp(getOpcodeGroup2(BRAF_OPCODE, reg)); + break; + case JMP_OPCODE: + oneShortOp(getOpcodeGroup2(JMP_OPCODE, reg)); + break; + case JSR_OPCODE: + oneShortOp(getOpcodeGroup2(JSR_OPCODE, reg)); + break; + case BSRF_OPCODE: + oneShortOp(getOpcodeGroup2(BSRF_OPCODE, reg)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void ldspr(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(LDSPR_OPCODE, reg); + oneShortOp(opc); + } + + void stspr(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(STSPR_OPCODE, reg); + oneShortOp(opc); + } + + void extub(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(EXTUB_OPCODE, dst, src); + oneShortOp(opc); + } + + void extuw(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(EXTUW_OPCODE, dst, src); + oneShortOp(opc); + } + + // float operations + + void ldsrmfpul(RegisterID src) + { + uint16_t opc = getOpcodeGroup2(LDS_RM_FPUL_OPCODE, src); + oneShortOp(opc); + } + + void fneg(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup2(FNEG_OPCODE, dst); + oneShortOp(opc, true, false); + } + + void fsqrt(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup2(FSQRT_OPCODE, dst); + oneShortOp(opc, true, false); + } + + void stsfpulReg(RegisterID src) + { + uint16_t opc = getOpcodeGroup2(STS_FPUL_RN_OPCODE, src); + oneShortOp(opc); + } + + void floatfpulfrn(RegisterID src) + { + uint16_t opc = getOpcodeGroup2(FLOAT_OPCODE, src); + oneShortOp(opc, true, false); + } + + void fmull(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMUL_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsReadrm(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsWriterm(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_RN_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsWriter0r(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_R0RN_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsReadr0r(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_READ_R0RM_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsReadrminc(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_INC_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsWriterndec(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_RN_DEC_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void ftrcRegfpul(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup2(FTRC_OPCODE, src); + oneShortOp(opc, true, false); + } + + void fldsfpul(RegisterID src) + { + uint16_t opc = getOpcodeGroup2(FLDS_FRM_FPUL_OPCODE, src); + oneShortOp(opc); + } + + void fstsfpul(RegisterID src) + { + uint16_t opc = getOpcodeGroup2(FSTS_FPUL_FRN_OPCODE, src); + oneShortOp(opc); + } + + void ldsfpscr(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(LDSFPSCR_OPCODE, reg); + oneShortOp(opc); + } + + void stsfpscr(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(STSFPSCR_OPCODE, reg); + oneShortOp(opc); + } + + // double operations + + void dcnvds(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup7(FCNVDS_DRM_FPUL_OPCODE, src >> 1); + oneShortOp(opc); + } + + void dcmppeq(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FCMPEQ_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dcmppgt(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FCMPGT_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dmulRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FMUL_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dsubRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FSUB_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void daddRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FADD_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dmovRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FMOV_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void ddivRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FDIV_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dsqrt(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup7(FSQRT_OPCODE, dst >> 1); + oneShortOp(opc); + } + + void dneg(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup7(FNEG_OPCODE, dst >> 1); + oneShortOp(opc); + } + + void fmovReadrm(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup10(FMOVS_READ_RM_OPCODE, dst >> 1, src); + oneShortOp(opc); + } + + void fmovWriterm(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_RN_OPCODE, dst, src >> 1); + oneShortOp(opc); + } + + void fmovWriter0r(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_R0RN_OPCODE, dst, src >> 1); + oneShortOp(opc); + } + + void fmovReadr0r(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup10(FMOVS_READ_R0RM_OPCODE, dst >> 1, src); + oneShortOp(opc); + } + + void fmovReadrminc(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup10(FMOVS_READ_RM_INC_OPCODE, dst >> 1, src); + oneShortOp(opc); + } + + void fmovWriterndec(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_RN_DEC_OPCODE, dst, src >> 1); + oneShortOp(opc); + } + + void floatfpulDreg(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup7(FLOAT_OPCODE, src >> 1); + oneShortOp(opc); + } + + void ftrcdrmfpul(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup7(FTRC_OPCODE, src >> 1); + oneShortOp(opc); + } + + // Various move ops + + void movImm8(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 127) && (imm8 >= -128)); + + uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, imm8); + oneShortOp(opc); + } + + void movlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOV_OPCODE, dst, src); + oneShortOp(opc); + } + + void movwRegMem(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVW_WRITE_RN_OPCODE, dst, src); + oneShortOp(opc); + } + + void movwMemReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVW_READ_RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movwPCReg(int offset, RegisterID base, RegisterID dst) + { + ASSERT(base == SH4Registers::pc); + ASSERT((offset <= 255) && (offset >= 0)); + + uint16_t opc = getOpcodeGroup3(MOVW_READ_OFFPC_OPCODE, dst, offset); + oneShortOp(opc); + } + + void movwMemReg(int offset, RegisterID base, RegisterID dst) + { + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup11(MOVW_READ_OFFRM_OPCODE, base, offset); + oneShortOp(opc); + } + + void movwR0mr(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVW_READ_R0RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movlRegMem(RegisterID src, int offset, RegisterID base) + { + ASSERT((offset <= 15) && (offset >= 0)); + + if (!offset) { + oneShortOp(getOpcodeGroup1(MOVL_WRITE_RN_OPCODE, base, src)); + return; + } + + oneShortOp(getOpcodeGroup4(MOVL_WRITE_OFFRN_OPCODE, base, src, offset)); + } + + void movlRegMem(RegisterID src, RegisterID base) + { + uint16_t opc = getOpcodeGroup1(MOVL_WRITE_RN_OPCODE, base, src); + oneShortOp(opc); + } + + void movlMemReg(int offset, RegisterID base, RegisterID dst) + { + if (base == SH4Registers::pc) { + ASSERT((offset <= 255) && (offset >= 0)); + oneShortOp(getOpcodeGroup3(MOVL_READ_OFFPC_OPCODE, dst, offset)); + return; + } + + ASSERT((offset <= 15) && (offset >= 0)); + if (!offset) { + oneShortOp(getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base)); + return; + } + + oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset)); + } + + void movlMemRegCompact(int offset, RegisterID base, RegisterID dst) + { + oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset)); + } + + void movbMemReg(int offset, RegisterID base, RegisterID dst) + { + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup11(MOVB_READ_OFFRM_OPCODE, base, offset); + oneShortOp(opc); + } + + void movbR0mr(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVB_READ_R0RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movbMemReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVB_READ_RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movlMemReg(RegisterID base, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base); + oneShortOp(opc); + } + + void movlMemRegIn(RegisterID base, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVL_READ_RMINC_OPCODE, dst, base); + oneShortOp(opc); + } + + void movlR0mr(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVL_READ_R0RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movlRegMemr0(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVL_WRITE_R0RN_OPCODE, dst, src); + oneShortOp(opc); + } + + void movlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 127) && (imm8 >= -128)); + + uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, imm8); + oneShortOp(opc); + } + + void loadConstant(uint32_t constant, RegisterID dst) + { + if (((int)constant <= 0x7f) && ((int)constant >= -0x80)) { + movImm8(constant, dst); + return; + } + + uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, 0); + + m_buffer.ensureSpace(maxInstructionSize, sizeof(uint32_t)); + printInstr(getOpcodeGroup3(MOVIMM_OPCODE, dst, constant), m_buffer.codeSize()); + m_buffer.putShortWithConstantInt(opc, constant, true); + } + + void loadConstantUnReusable(uint32_t constant, RegisterID dst, bool ensureSpace = false) + { + uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, 0); + + if (ensureSpace) + m_buffer.ensureSpace(maxInstructionSize, sizeof(uint32_t)); + + printInstr(getOpcodeGroup3(MOVIMM_OPCODE, dst, constant), m_buffer.codeSize()); + m_buffer.putShortWithConstantInt(opc, constant); + } + + // Flow control + + AssemblerLabel call() + { + RegisterID scr = claimScratch(); + m_buffer.ensureSpace(maxInstructionSize + 4, sizeof(uint32_t)); + loadConstantUnReusable(0x0, scr); + branch(JSR_OPCODE, scr); + nop(); + releaseScratch(scr); + return m_buffer.label(); + } + + AssemblerLabel call(RegisterID dst) + { + m_buffer.ensureSpace(maxInstructionSize + 2); + branch(JSR_OPCODE, dst); + nop(); + return m_buffer.label(); + } + + AssemblerLabel jmp() + { + RegisterID scr = claimScratch(); + m_buffer.ensureSpace(maxInstructionSize + 4, sizeof(uint32_t)); + AssemblerLabel label = m_buffer.label(); + loadConstantUnReusable(0x0, scr); + branch(BRAF_OPCODE, scr); + nop(); + releaseScratch(scr); + return label; + } + + void extraInstrForBranch(RegisterID dst) + { + loadConstantUnReusable(0x0, dst); + nop(); + nop(); + } + + AssemblerLabel jmp(RegisterID dst) + { + jmpReg(dst); + return m_buffer.label(); + } + + void jmpReg(RegisterID dst) + { + m_buffer.ensureSpace(maxInstructionSize + 2); + branch(JMP_OPCODE, dst); + nop(); + } + + AssemblerLabel jne() + { + AssemblerLabel label = m_buffer.label(); + branch(BF_OPCODE, 0); + return label; + } + + AssemblerLabel je() + { + AssemblerLabel label = m_buffer.label(); + branch(BT_OPCODE, 0); + return label; + } + + AssemblerLabel bra() + { + AssemblerLabel label = m_buffer.label(); + branch(BRA_OPCODE, 0); + return label; + } + + void ret() + { + m_buffer.ensureSpace(maxInstructionSize + 2); + oneShortOp(RTS_OPCODE, false); + } + + AssemblerLabel labelIgnoringWatchpoints() + { + m_buffer.ensureSpaceForAnyInstruction(); + return m_buffer.label(); + } + + AssemblerLabel label() + { + m_buffer.ensureSpaceForAnyInstruction(); + return m_buffer.label(); + } + + int sizeOfConstantPool() + { + return m_buffer.sizeOfConstantPool(); + } + + AssemblerLabel align(int alignment) + { + m_buffer.ensureSpace(maxInstructionSize + 2); + while (!m_buffer.isAligned(alignment)) { + nop(); + m_buffer.ensureSpace(maxInstructionSize + 2); + } + return label(); + } + + static void changePCrelativeAddress(int offset, uint16_t* instructionPtr, uint32_t newAddress) + { + uint32_t address = (offset << 2) + ((reinterpret_cast(instructionPtr) + 4) &(~0x3)); + *reinterpret_cast(address) = newAddress; + } + + static uint32_t readPCrelativeAddress(int offset, uint16_t* instructionPtr) + { + uint32_t address = (offset << 2) + ((reinterpret_cast(instructionPtr) + 4) &(~0x3)); + return *reinterpret_cast(address); + } + + static uint16_t* getInstructionPtr(void* code, int offset) + { + return reinterpret_cast (reinterpret_cast(code) + offset); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + + uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset); + uint16_t instruction = *instructionPtr; + int offsetBits = (reinterpret_cast(to) - reinterpret_cast(code)) - from.m_offset; + + if (((instruction & 0xff00) == BT_OPCODE) || ((instruction & 0xff00) == BF_OPCODE)) { + /* BT label ==> BF 2 + nop LDR reg + nop braf @reg + nop nop + */ + offsetBits -= 8; + instruction ^= 0x0202; + *instructionPtr++ = instruction; + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); + instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); + *instructionPtr = instruction; + printBlockInstr(instructionPtr - 2, from.m_offset, 3); + return; + } + + /* MOV #imm, reg => LDR reg + braf @reg braf @reg + nop nop + */ + ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); + + offsetBits -= 4; + if (offsetBits >= -4096 && offsetBits <= 4094) { + *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); + *(++instructionPtr) = NOP_OPCODE; + printBlockInstr(instructionPtr - 1, from.m_offset, 2); + return; + } + + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); + printInstr(*instructionPtr, from.m_offset + 2); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset); + instructionPtr -= 3; + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(to)); + } + + static void linkPointer(void* code, AssemblerLabel where, void* value) + { + uint16_t* instructionPtr = getInstructionPtr(code, where.m_offset); + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(value)); + } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + ASSERT(call.isSet()); + return call.m_offset; + } + + static uint32_t* getLdrImmAddressOnPool(SH4Word* insn, uint32_t* constPool) + { + return (constPool + (*insn & 0xff)); + } + + static SH4Word patchConstantPoolLoad(SH4Word load, int value) + { + return ((load & ~0xff) | value); + } + + static SH4Buffer::TwoShorts placeConstantPoolBarrier(int offset) + { + ASSERT(((offset >> 1) <=2047) && ((offset >> 1) >= -2048)); + + SH4Buffer::TwoShorts m_barrier; + m_barrier.high = (BRA_OPCODE | (offset >> 1)); + m_barrier.low = NOP_OPCODE; + printInstr(((BRA_OPCODE | (offset >> 1))), 0); + printInstr(NOP_OPCODE, 0); + return m_barrier; + } + + static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr) + { + SH4Word* instructionPtr = reinterpret_cast(loadAddr); + SH4Word instruction = *instructionPtr; + SH4Word index = instruction & 0xff; + + if ((instruction & 0xf000) != MOVIMM_OPCODE) + return; + + ASSERT((((reinterpret_cast(constPoolAddr) - reinterpret_cast(loadAddr)) + index * 4)) < 1024); + + int offset = reinterpret_cast(constPoolAddr) + (index * 4) - ((reinterpret_cast(instructionPtr) & ~0x03) + 4); + instruction &=0xf00; + instruction |= 0xd000; + offset &= 0x03ff; + instruction |= (offset >> 2); + *instructionPtr = instruction; + printInstr(instruction, reinterpret_cast(loadAddr)); + } + + static void repatchPointer(void* where, void* value) + { + patchPointer(where, value); + } + + static void* readPointer(void* code) + { + return reinterpret_cast(readInt32(code)); + } + + static void repatchInt32(void* where, int32_t value) + { + uint16_t* instructionPtr = reinterpret_cast(where); + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, value); + } + + static void repatchCompact(void* where, int32_t value) + { + ASSERT(value >= 0); + ASSERT(value <= 60); + *reinterpret_cast(where) = ((*reinterpret_cast(where) & 0xfff0) | (value >> 2)); + cacheFlush(reinterpret_cast(where), sizeof(uint16_t)); + } + + static void relinkCall(void* from, void* to) + { + uint16_t* instructionPtr = reinterpret_cast(from); + instructionPtr -= 3; + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(to)); + } + + static void relinkJump(void* from, void* to) + { + uint16_t* instructionPtr = reinterpret_cast (from); + uint16_t instruction = *instructionPtr; + int32_t offsetBits = (reinterpret_cast(to) - reinterpret_cast(from)); + + if (((*instructionPtr & 0xff00) == BT_OPCODE) || ((*instructionPtr & 0xff00) == BF_OPCODE)) { + offsetBits -= 8; + instructionPtr++; + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); + instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); + *instructionPtr = instruction; + printBlockInstr(instructionPtr, reinterpret_cast(from) + 1, 3); + return; + } + + ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); + offsetBits -= 4; + if (offsetBits >= -4096 && offsetBits <= 4094) { + *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); + *(++instructionPtr) = NOP_OPCODE; + printBlockInstr(instructionPtr - 2, reinterpret_cast(from), 2); + return; + } + + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); + printInstr(*instructionPtr, reinterpret_cast(from)); + } + + // Linking & patching + + void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type = JumpFar) + { + ASSERT(to.isSet()); + ASSERT(from.isSet()); + + uint16_t* instructionPtr = getInstructionPtr(data(), from.m_offset); + uint16_t instruction = *instructionPtr; + int offsetBits; + + if (type == JumpNear) { + ASSERT((instruction == BT_OPCODE) || (instruction == BF_OPCODE) || (instruction == BRA_OPCODE)); + int offset = (codeSize() - from.m_offset) - 4; + *instructionPtr++ = instruction | (offset >> 1); + printInstr(*instructionPtr, from.m_offset + 2); + return; + } + + if (((instruction & 0xff00) == BT_OPCODE) || ((instruction & 0xff00) == BF_OPCODE)) { + /* BT label => BF 2 + nop LDR reg + nop braf @reg + nop nop + */ + offsetBits = (to.m_offset - from.m_offset) - 8; + instruction ^= 0x0202; + *instructionPtr++ = instruction; + if ((*instructionPtr & 0xf000) == 0xe000) { + uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress()); + *addr = offsetBits; + } else + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); + instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); + *instructionPtr = instruction; + printBlockInstr(instructionPtr - 2, from.m_offset, 3); + return; + } + + /* MOV # imm, reg => LDR reg + braf @reg braf @reg + nop nop + */ + ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); + offsetBits = (to.m_offset - from.m_offset) - 4; + if (offsetBits >= -4096 && offsetBits <= 4094) { + *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); + *(++instructionPtr) = NOP_OPCODE; + printBlockInstr(instructionPtr - 1, from.m_offset, 2); + return; + } + + instruction = *instructionPtr; + if ((instruction & 0xf000) == 0xe000) { + uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress()); + *addr = offsetBits - 2; + printInstr(*instructionPtr, from.m_offset + 2); + return; + } + + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); + printInstr(*instructionPtr, from.m_offset + 2); + } + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + static void patchPointer(void* code, AssemblerLabel where, void* value) + { + patchPointer(reinterpret_cast(code) + where.m_offset, value); + } + + static void patchPointer(void* code, void* value) + { + patchInt32(code, reinterpret_cast(value)); + } + + static void patchInt32(void* code, uint32_t value) + { + changePCrelativeAddress((*(reinterpret_cast(code)) & 0xff), reinterpret_cast(code), value); + } + + static uint32_t readInt32(void* code) + { + return readPCrelativeAddress((*(reinterpret_cast(code)) & 0xff), reinterpret_cast(code)); + } + + static void* readCallTarget(void* from) + { + uint16_t* instructionPtr = static_cast(from); + instructionPtr -= 3; + return reinterpret_cast(readPCrelativeAddress((*instructionPtr & 0xff), instructionPtr)); + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + return m_buffer.executableCopy(globalData, ownerUID, effort); + } + + static void cacheFlush(void* code, size_t size) + { +#if !OS(LINUX) +#error "The cacheFlush support is missing on this platform." +#elif defined CACHEFLUSH_D_L2 + syscall(__NR_cacheflush, reinterpret_cast(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I | CACHEFLUSH_D_L2); +#else + syscall(__NR_cacheflush, reinterpret_cast(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I); +#endif + } + + void prefix(uint16_t pre) + { + m_buffer.putByte(pre); + } + + void oneShortOp(uint16_t opcode, bool checksize = true, bool isDouble = true) + { + printInstr(opcode, m_buffer.codeSize(), isDouble); + if (checksize) + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putShortUnchecked(opcode); + } + + void ensureSpace(int space) + { + m_buffer.ensureSpace(space); + } + + void ensureSpace(int insnSpace, int constSpace) + { + m_buffer.ensureSpace(insnSpace, constSpace); + } + + // Administrative methods + + void* data() const { return m_buffer.data(); } + size_t codeSize() const { return m_buffer.codeSize(); } + +#ifdef SH4_ASSEMBLER_TRACING + static void printInstr(uint16_t opc, unsigned int size, bool isdoubleInst = true) + { + if (!getenv("JavaScriptCoreDumpJIT")) + return; + + const char *format = 0; + printfStdoutInstr("offset: 0x%8.8x\t", size); + switch (opc) { + case BRK_OPCODE: + format = " BRK\n"; + break; + case NOP_OPCODE: + format = " NOP\n"; + break; + case RTS_OPCODE: + format =" *RTS\n"; + break; + case SETS_OPCODE: + format = " SETS\n"; + break; + case SETT_OPCODE: + format = " SETT\n"; + break; + case CLRT_OPCODE: + format = " CLRT\n"; + break; + case FSCHG_OPCODE: + format = " FSCHG\n"; + break; + } + if (format) { + printfStdoutInstr(format); + return; + } + switch (opc & 0xf0ff) { + case BRAF_OPCODE: + format = " *BRAF R%d\n"; + break; + case DT_OPCODE: + format = " DT R%d\n"; + break; + case CMPPL_OPCODE: + format = " CMP/PL R%d\n"; + break; + case CMPPZ_OPCODE: + format = " CMP/PZ R%d\n"; + break; + case JMP_OPCODE: + format = " *JMP @R%d\n"; + break; + case JSR_OPCODE: + format = " *JSR @R%d\n"; + break; + case LDSPR_OPCODE: + format = " LDS R%d, PR\n"; + break; + case LDSLPR_OPCODE: + format = " LDS.L @R%d+, PR\n"; + break; + case MOVT_OPCODE: + format = " MOVT R%d\n"; + break; + case SHAL_OPCODE: + format = " SHAL R%d\n"; + break; + case SHAR_OPCODE: + format = " SHAR R%d\n"; + break; + case SHLL_OPCODE: + format = " SHLL R%d\n"; + break; + case SHLL2_OPCODE: + format = " SHLL2 R%d\n"; + break; + case SHLL8_OPCODE: + format = " SHLL8 R%d\n"; + break; + case SHLL16_OPCODE: + format = " SHLL16 R%d\n"; + break; + case SHLR_OPCODE: + format = " SHLR R%d\n"; + break; + case SHLR2_OPCODE: + format = " SHLR2 R%d\n"; + break; + case SHLR8_OPCODE: + format = " SHLR8 R%d\n"; + break; + case SHLR16_OPCODE: + format = " SHLR16 R%d\n"; + break; + case STSPR_OPCODE: + format = " STS PR, R%d\n"; + break; + case STSLPR_OPCODE: + format = " STS.L PR, @-R%d\n"; + break; + case LDS_RM_FPUL_OPCODE: + format = " LDS R%d, FPUL\n"; + break; + case STS_FPUL_RN_OPCODE: + format = " STS FPUL, R%d \n"; + break; + case FLDS_FRM_FPUL_OPCODE: + format = " FLDS FR%d, FPUL\n"; + break; + case FSTS_FPUL_FRN_OPCODE: + format = " FSTS FPUL, R%d \n"; + break; + case LDSFPSCR_OPCODE: + format = " LDS R%d, FPSCR \n"; + break; + case STSFPSCR_OPCODE: + format = " STS FPSCR, R%d \n"; + break; + case STSMACL_OPCODE: + format = " STS MACL, R%d \n"; + break; + case STSMACH_OPCODE: + format = " STS MACH, R%d \n"; + break; + case BSRF_OPCODE: + format = " *BSRF R%d"; + break; + case FTRC_OPCODE: + format = " FTRC FR%d, FPUL\n"; + break; + } + if (format) { + printfStdoutInstr(format, getRn(opc)); + return; + } + switch (opc & 0xf0ff) { + case FNEG_OPCODE: + format = " FNEG DR%d\n"; + break; + case FLOAT_OPCODE: + format = " FLOAT DR%d\n"; + break; + case FTRC_OPCODE: + format = " FTRC FR%d, FPUL\n"; + break; + case FSQRT_OPCODE: + format = " FSQRT FR%d\n"; + break; + case FCNVDS_DRM_FPUL_OPCODE: + format = " FCNVDS FR%d, FPUL\n"; + break; + } + if (format) { + if (isdoubleInst) + printfStdoutInstr(format, getDRn(opc) << 1); + else + printfStdoutInstr(format, getRn(opc)); + return; + } + switch (opc & 0xf00f) { + case ADD_OPCODE: + format = " ADD R%d, R%d\n"; + break; + case ADDC_OPCODE: + format = " ADDC R%d, R%d\n"; + break; + case ADDV_OPCODE: + format = " ADDV R%d, R%d\n"; + break; + case AND_OPCODE: + format = " AND R%d, R%d\n"; + break; + case DIV1_OPCODE: + format = " DIV1 R%d, R%d\n"; + break; + case CMPEQ_OPCODE: + format = " CMP/EQ R%d, R%d\n"; + break; + case CMPGE_OPCODE: + format = " CMP/GE R%d, R%d\n"; + break; + case CMPGT_OPCODE: + format = " CMP/GT R%d, R%d\n"; + break; + case CMPHI_OPCODE: + format = " CMP/HI R%d, R%d\n"; + break; + case CMPHS_OPCODE: + format = " CMP/HS R%d, R%d\n"; + break; + case MOV_OPCODE: + format = " MOV R%d, R%d\n"; + break; + case MOVB_WRITE_RN_OPCODE: + format = " MOV.B R%d, @R%d\n"; + break; + case MOVB_WRITE_RNDEC_OPCODE: + format = " MOV.B R%d, @-R%d\n"; + break; + case MOVB_WRITE_R0RN_OPCODE: + format = " MOV.B R%d, @(R0, R%d)\n"; + break; + case MOVB_READ_RM_OPCODE: + format = " MOV.B @R%d, R%d\n"; + break; + case MOVB_READ_RMINC_OPCODE: + format = " MOV.B @R%d+, R%d\n"; + break; + case MOVB_READ_R0RM_OPCODE: + format = " MOV.B @(R0, R%d), R%d\n"; + break; + case MOVL_WRITE_RN_OPCODE: + format = " MOV.L R%d, @R%d\n"; + break; + case MOVL_WRITE_RNDEC_OPCODE: + format = " MOV.L R%d, @-R%d\n"; + break; + case MOVL_WRITE_R0RN_OPCODE: + format = " MOV.L R%d, @(R0, R%d)\n"; + break; + case MOVL_READ_RM_OPCODE: + format = " MOV.L @R%d, R%d\n"; + break; + case MOVL_READ_RMINC_OPCODE: + format = " MOV.L @R%d+, R%d\n"; + break; + case MOVL_READ_R0RM_OPCODE: + format = " MOV.L @(R0, R%d), R%d\n"; + break; + case MULL_OPCODE: + format = " MUL.L R%d, R%d\n"; + break; + case DMULL_L_OPCODE: + format = " DMULU.L R%d, R%d\n"; + break; + case DMULSL_OPCODE: + format = " DMULS.L R%d, R%d\n"; + break; + case NEG_OPCODE: + format = " NEG R%d, R%d\n"; + break; + case NEGC_OPCODE: + format = " NEGC R%d, R%d\n"; + break; + case NOT_OPCODE: + format = " NOT R%d, R%d\n"; + break; + case OR_OPCODE: + format = " OR R%d, R%d\n"; + break; + case SHAD_OPCODE: + format = " SHAD R%d, R%d\n"; + break; + case SHLD_OPCODE: + format = " SHLD R%d, R%d\n"; + break; + case SUB_OPCODE: + format = " SUB R%d, R%d\n"; + break; + case SUBC_OPCODE: + format = " SUBC R%d, R%d\n"; + break; + case SUBV_OPCODE: + format = " SUBV R%d, R%d\n"; + break; + case TST_OPCODE: + format = " TST R%d, R%d\n"; + break; + case XOR_OPCODE: + format = " XOR R%d, R%d\n";break; + case MOVW_WRITE_RN_OPCODE: + format = " MOV.W R%d, @R%d\n"; + break; + case MOVW_READ_RM_OPCODE: + format = " MOV.W @R%d, R%d\n"; + break; + case MOVW_READ_R0RM_OPCODE: + format = " MOV.W @(R0, R%d), R%d\n"; + break; + case EXTUB_OPCODE: + format = " EXTU.B R%d, R%d\n"; + break; + case EXTUW_OPCODE: + format = " EXTU.W R%d, R%d\n"; + break; + } + if (format) { + printfStdoutInstr(format, getRm(opc), getRn(opc)); + return; + } + switch (opc & 0xf00f) { + case FSUB_OPCODE: + format = " FSUB FR%d, FR%d\n"; + break; + case FADD_OPCODE: + format = " FADD FR%d, FR%d\n"; + break; + case FDIV_OPCODE: + format = " FDIV FR%d, FR%d\n"; + break; + case FMUL_OPCODE: + format = " DMULL FR%d, FR%d\n"; + break; + case FMOV_OPCODE: + format = " FMOV FR%d, FR%d\n"; + break; + case FCMPEQ_OPCODE: + format = " FCMP/EQ FR%d, FR%d\n"; + break; + case FCMPGT_OPCODE: + format = " FCMP/GT FR%d, FR%d\n"; + break; + } + if (format) { + if (isdoubleInst) + printfStdoutInstr(format, getDRm(opc) << 1, getDRn(opc) << 1); + else + printfStdoutInstr(format, getRm(opc), getRn(opc)); + return; + } + switch (opc & 0xf00f) { + case FMOVS_WRITE_RN_DEC_OPCODE: + format = " %s FR%d, @-R%d\n"; + break; + case FMOVS_WRITE_RN_OPCODE: + format = " %s FR%d, @R%d\n"; + break; + case FMOVS_WRITE_R0RN_OPCODE: + format = " %s FR%d, @(R0, R%d)\n"; + break; + } + if (format) { + if (isdoubleInst) + printfStdoutInstr(format, "FMOV", getDRm(opc) << 1, getDRn(opc)); + else + printfStdoutInstr(format, "FMOV.S", getRm(opc), getRn(opc)); + return; + } + switch (opc & 0xf00f) { + case FMOVS_READ_RM_OPCODE: + format = " %s @R%d, FR%d\n"; + break; + case FMOVS_READ_RM_INC_OPCODE: + format = " %s @R%d+, FR%d\n"; + break; + case FMOVS_READ_R0RM_OPCODE: + format = " %s @(R0, R%d), FR%d\n"; + break; + } + if (format) { + if (isdoubleInst) + printfStdoutInstr(format, "FMOV", getDRm(opc), getDRn(opc) << 1); + else + printfStdoutInstr(format, "FMOV.S", getRm(opc), getRn(opc)); + return; + } + switch (opc & 0xff00) { + case BF_OPCODE: + format = " BF %d\n"; + break; + case BFS_OPCODE: + format = " *BF/S %d\n"; + break; + case ANDIMM_OPCODE: + format = " AND #%d, R0\n"; + break; + case BT_OPCODE: + format = " BT %d\n"; + break; + case BTS_OPCODE: + format = " *BT/S %d\n"; + break; + case CMPEQIMM_OPCODE: + format = " CMP/EQ #%d, R0\n"; + break; + case MOVB_WRITE_OFFGBR_OPCODE: + format = " MOV.B R0, @(%d, GBR)\n"; + break; + case MOVB_READ_OFFGBR_OPCODE: + format = " MOV.B @(%d, GBR), R0\n"; + break; + case MOVL_WRITE_OFFGBR_OPCODE: + format = " MOV.L R0, @(%d, GBR)\n"; + break; + case MOVL_READ_OFFGBR_OPCODE: + format = " MOV.L @(%d, GBR), R0\n"; + break; + case MOVA_READ_OFFPC_OPCODE: + format = " MOVA @(%d, PC), R0\n"; + break; + case ORIMM_OPCODE: + format = " OR #%d, R0\n"; + break; + case ORBIMM_OPCODE: + format = " OR.B #%d, @(R0, GBR)\n"; + break; + case TSTIMM_OPCODE: + format = " TST #%d, R0\n"; + break; + case TSTB_OPCODE: + format = " TST.B %d, @(R0, GBR)\n"; + break; + case XORIMM_OPCODE: + format = " XOR #%d, R0\n"; + break; + case XORB_OPCODE: + format = " XOR.B %d, @(R0, GBR)\n"; + break; + } + if (format) { + printfStdoutInstr(format, getImm8(opc)); + return; + } + switch (opc & 0xff00) { + case MOVB_WRITE_OFFRN_OPCODE: + format = " MOV.B R0, @(%d, R%d)\n"; + break; + case MOVB_READ_OFFRM_OPCODE: + format = " MOV.B @(%d, R%d), R0\n"; + break; + } + if (format) { + printfStdoutInstr(format, getDisp(opc), getRm(opc)); + return; + } + switch (opc & 0xf000) { + case BRA_OPCODE: + format = " *BRA %d\n"; + break; + case BSR_OPCODE: + format = " *BSR %d\n"; + break; + } + if (format) { + printfStdoutInstr(format, getImm12(opc)); + return; + } + switch (opc & 0xf000) { + case MOVL_READ_OFFPC_OPCODE: + format = " MOV.L @(%d, PC), R%d\n"; + break; + case ADDIMM_OPCODE: + format = " ADD #%d, R%d\n"; + break; + case MOVIMM_OPCODE: + format = " MOV #%d, R%d\n"; + break; + case MOVW_READ_OFFPC_OPCODE: + format = " MOV.W @(%d, PC), R%d\n"; + break; + } + if (format) { + printfStdoutInstr(format, getImm8(opc), getRn(opc)); + return; + } + switch (opc & 0xf000) { + case MOVL_WRITE_OFFRN_OPCODE: + format = " MOV.L R%d, @(%d, R%d)\n"; + printfStdoutInstr(format, getRm(opc), getDisp(opc), getRn(opc)); + break; + case MOVL_READ_OFFRM_OPCODE: + format = " MOV.L @(%d, R%d), R%d\n"; + printfStdoutInstr(format, getDisp(opc), getRm(opc), getRn(opc)); + break; + } + } + + static void printfStdoutInstr(const char* format, ...) + { + if (getenv("JavaScriptCoreDumpJIT")) { + va_list args; + va_start(args, format); + vprintfStdoutInstr(format, args); + va_end(args); + } + } + + static void vprintfStdoutInstr(const char* format, va_list args) + { + if (getenv("JavaScriptCoreDumpJIT")) + WTF::dataLogV(format, args); + } + + static void printBlockInstr(uint16_t* first, unsigned int offset, int nbInstr) + { + printfStdoutInstr(">> repatch instructions after link\n"); + for (int i = 0; i <= nbInstr; i++) + printInstr(*(first + i), offset + i); + printfStdoutInstr(">> end repatch\n"); + } +#else + static void printInstr(uint16_t opc, unsigned int size, bool isdoubleInst = true) {}; + static void printBlockInstr(uint16_t* first, unsigned int offset, int nbInstr) {}; +#endif + + static void replaceWithLoad(void* instructionStart) + { + SH4Word* insPtr = reinterpret_cast(instructionStart); + + insPtr += 2; // skip MOV and ADD opcodes + + if (((*insPtr) & 0xf00f) != MOVL_READ_RM_OPCODE) { + *insPtr = MOVL_READ_RM_OPCODE | (*insPtr & 0x0ff0); + cacheFlush(insPtr, sizeof(SH4Word)); + } + } + + static void replaceWithAddressComputation(void* instructionStart) + { + SH4Word* insPtr = reinterpret_cast(instructionStart); + + insPtr += 2; // skip MOV and ADD opcodes + + if (((*insPtr) & 0xf00f) != MOV_OPCODE) { + *insPtr = MOV_OPCODE | (*insPtr & 0x0ff0); + cacheFlush(insPtr, sizeof(SH4Word)); + } + } + +private: + SH4Buffer m_buffer; + int m_claimscratchReg; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(SH4) + +#endif // SH4Assembler_h diff --git a/masm/assembler/X86Assembler.h b/masm/assembler/X86Assembler.h new file mode 100644 index 0000000000..adaee4bc07 --- /dev/null +++ b/masm/assembler/X86Assembler.h @@ -0,0 +1,2462 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef X86Assembler_h +#define X86Assembler_h + +#if ENABLE(ASSEMBLER) && (CPU(X86) || CPU(X86_64)) + +#include "AssemblerBuffer.h" +#include "JITCompilationEffort.h" +#include +#include +#include + +namespace JSC { + +inline bool CAN_SIGN_EXTEND_8_32(int32_t value) { return value == (int32_t)(signed char)value; } + +namespace X86Registers { + typedef enum { + eax, + ecx, + edx, + ebx, + esp, + ebp, + esi, + edi, + +#if CPU(X86_64) + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, +#endif + } RegisterID; + + typedef enum { + xmm0, + xmm1, + xmm2, + xmm3, + xmm4, + xmm5, + xmm6, + xmm7, + } XMMRegisterID; +} + +class X86Assembler { +public: + typedef X86Registers::RegisterID RegisterID; + typedef X86Registers::XMMRegisterID XMMRegisterID; + typedef XMMRegisterID FPRegisterID; + + typedef enum { + ConditionO, + ConditionNO, + ConditionB, + ConditionAE, + ConditionE, + ConditionNE, + ConditionBE, + ConditionA, + ConditionS, + ConditionNS, + ConditionP, + ConditionNP, + ConditionL, + ConditionGE, + ConditionLE, + ConditionG, + + ConditionC = ConditionB, + ConditionNC = ConditionAE, + } Condition; + +private: + typedef enum { + OP_ADD_EvGv = 0x01, + OP_ADD_GvEv = 0x03, + OP_OR_EvGv = 0x09, + OP_OR_GvEv = 0x0B, + OP_2BYTE_ESCAPE = 0x0F, + OP_AND_EvGv = 0x21, + OP_AND_GvEv = 0x23, + OP_SUB_EvGv = 0x29, + OP_SUB_GvEv = 0x2B, + PRE_PREDICT_BRANCH_NOT_TAKEN = 0x2E, + OP_XOR_EvGv = 0x31, + OP_XOR_GvEv = 0x33, + OP_CMP_EvGv = 0x39, + OP_CMP_GvEv = 0x3B, +#if CPU(X86_64) + PRE_REX = 0x40, +#endif + OP_PUSH_EAX = 0x50, + OP_POP_EAX = 0x58, +#if CPU(X86_64) + OP_MOVSXD_GvEv = 0x63, +#endif + PRE_OPERAND_SIZE = 0x66, + PRE_SSE_66 = 0x66, + OP_PUSH_Iz = 0x68, + OP_IMUL_GvEvIz = 0x69, + OP_GROUP1_EbIb = 0x80, + OP_GROUP1_EvIz = 0x81, + OP_GROUP1_EvIb = 0x83, + OP_TEST_EbGb = 0x84, + OP_TEST_EvGv = 0x85, + OP_XCHG_EvGv = 0x87, + OP_MOV_EbGb = 0x88, + OP_MOV_EvGv = 0x89, + OP_MOV_GvEv = 0x8B, + OP_LEA = 0x8D, + OP_GROUP1A_Ev = 0x8F, + OP_NOP = 0x90, + OP_CDQ = 0x99, + OP_MOV_EAXOv = 0xA1, + OP_MOV_OvEAX = 0xA3, + OP_MOV_EAXIv = 0xB8, + OP_GROUP2_EvIb = 0xC1, + OP_RET = 0xC3, + OP_GROUP11_EvIb = 0xC6, + OP_GROUP11_EvIz = 0xC7, + OP_INT3 = 0xCC, + OP_GROUP2_Ev1 = 0xD1, + OP_GROUP2_EvCL = 0xD3, + OP_ESCAPE_DD = 0xDD, + OP_CALL_rel32 = 0xE8, + OP_JMP_rel32 = 0xE9, + PRE_SSE_F2 = 0xF2, + PRE_SSE_F3 = 0xF3, + OP_HLT = 0xF4, + OP_GROUP3_EbIb = 0xF6, + OP_GROUP3_Ev = 0xF7, + OP_GROUP3_EvIz = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test. + OP_GROUP5_Ev = 0xFF, + } OneByteOpcodeID; + + typedef enum { + OP2_MOVSD_VsdWsd = 0x10, + OP2_MOVSD_WsdVsd = 0x11, + OP2_MOVSS_VsdWsd = 0x10, + OP2_MOVSS_WsdVsd = 0x11, + OP2_CVTSI2SD_VsdEd = 0x2A, + OP2_CVTTSD2SI_GdWsd = 0x2C, + OP2_UCOMISD_VsdWsd = 0x2E, + OP2_ADDSD_VsdWsd = 0x58, + OP2_MULSD_VsdWsd = 0x59, + OP2_CVTSD2SS_VsdWsd = 0x5A, + OP2_CVTSS2SD_VsdWsd = 0x5A, + OP2_SUBSD_VsdWsd = 0x5C, + OP2_DIVSD_VsdWsd = 0x5E, + OP2_SQRTSD_VsdWsd = 0x51, + OP2_ANDNPD_VpdWpd = 0x55, + OP2_XORPD_VpdWpd = 0x57, + OP2_MOVD_VdEd = 0x6E, + OP2_MOVD_EdVd = 0x7E, + OP2_JCC_rel32 = 0x80, + OP_SETCC = 0x90, + OP2_IMUL_GvEv = 0xAF, + OP2_MOVZX_GvEb = 0xB6, + OP2_MOVSX_GvEb = 0xBE, + OP2_MOVZX_GvEw = 0xB7, + OP2_MOVSX_GvEw = 0xBF, + OP2_PEXTRW_GdUdIb = 0xC5, + OP2_PSLLQ_UdqIb = 0x73, + OP2_PSRLQ_UdqIb = 0x73, + OP2_POR_VdqWdq = 0XEB, + } TwoByteOpcodeID; + + TwoByteOpcodeID jccRel32(Condition cond) + { + return (TwoByteOpcodeID)(OP2_JCC_rel32 + cond); + } + + TwoByteOpcodeID setccOpcode(Condition cond) + { + return (TwoByteOpcodeID)(OP_SETCC + cond); + } + + typedef enum { + GROUP1_OP_ADD = 0, + GROUP1_OP_OR = 1, + GROUP1_OP_ADC = 2, + GROUP1_OP_AND = 4, + GROUP1_OP_SUB = 5, + GROUP1_OP_XOR = 6, + GROUP1_OP_CMP = 7, + + GROUP1A_OP_POP = 0, + + GROUP2_OP_ROL = 0, + GROUP2_OP_ROR = 1, + GROUP2_OP_RCL = 2, + GROUP2_OP_RCR = 3, + + GROUP2_OP_SHL = 4, + GROUP2_OP_SHR = 5, + GROUP2_OP_SAR = 7, + + GROUP3_OP_TEST = 0, + GROUP3_OP_NOT = 2, + GROUP3_OP_NEG = 3, + GROUP3_OP_IDIV = 7, + + GROUP5_OP_CALLN = 2, + GROUP5_OP_JMPN = 4, + GROUP5_OP_PUSH = 6, + + GROUP11_MOV = 0, + + GROUP14_OP_PSLLQ = 6, + GROUP14_OP_PSRLQ = 2, + + ESCAPE_DD_FSTP_doubleReal = 3, + } GroupOpcodeID; + + class X86InstructionFormatter; +public: + + X86Assembler() + : m_indexOfLastWatchpoint(INT_MIN) + , m_indexOfTailOfLastWatchpoint(INT_MIN) + { + } + + // Stack operations: + + void push_r(RegisterID reg) + { + m_formatter.oneByteOp(OP_PUSH_EAX, reg); + } + + void pop_r(RegisterID reg) + { + m_formatter.oneByteOp(OP_POP_EAX, reg); + } + + void push_i32(int imm) + { + m_formatter.oneByteOp(OP_PUSH_Iz); + m_formatter.immediate32(imm); + } + + void push_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_PUSH, base, offset); + } + + void pop_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP1A_Ev, GROUP1A_OP_POP, base, offset); + } + + // Arithmetic operations: + +#if !CPU(X86_64) + void adcl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADC, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADC, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void addl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_ADD_EvGv, src, dst); + } + + void addl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_ADD_GvEv, dst, base, offset); + } + +#if !CPU(X86_64) + void addl_mr(const void* addr, RegisterID dst) + { + m_formatter.oneByteOp(OP_ADD_GvEv, dst, addr); + } +#endif + + void addl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_ADD_EvGv, src, base, offset); + } + + void addl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, dst); + m_formatter.immediate32(imm); + } + } + + void addl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, offset); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void addq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_ADD_EvGv, src, dst); + } + + void addq_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64(OP_ADD_GvEv, dst, base, offset); + } + + void addq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_ADD, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_ADD, dst); + m_formatter.immediate32(imm); + } + } + + void addq_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, offset); + m_formatter.immediate32(imm); + } + } +#else + void addl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void andl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_AND_EvGv, src, dst); + } + + void andl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_AND_GvEv, dst, base, offset); + } + + void andl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_AND_EvGv, src, base, offset); + } + + void andl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, dst); + m_formatter.immediate32(imm); + } + } + + void andl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, base, offset); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void andq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_AND_EvGv, src, dst); + } + + void andq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_AND, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_AND, dst); + m_formatter.immediate32(imm); + } + } +#else + void andl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void negl_r(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, dst); + } + +#if CPU(X86_64) + void negq_r(RegisterID dst) + { + m_formatter.oneByteOp64(OP_GROUP3_Ev, GROUP3_OP_NEG, dst); + } +#endif + + void negl_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, base, offset); + } + + void notl_r(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NOT, dst); + } + + void notl_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NOT, base, offset); + } + + void orl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_OR_EvGv, src, dst); + } + + void orl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_OR_GvEv, dst, base, offset); + } + + void orl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_OR_EvGv, src, base, offset); + } + + void orl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, dst); + m_formatter.immediate32(imm); + } + } + + void orl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, base, offset); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void orq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_OR_EvGv, src, dst); + } + + void orq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_OR, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_OR, dst); + m_formatter.immediate32(imm); + } + } +#else + void orl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void subl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_SUB_EvGv, src, dst); + } + + void subl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_SUB_GvEv, dst, base, offset); + } + + void subl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_SUB_EvGv, src, base, offset); + } + + void subl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, dst); + m_formatter.immediate32(imm); + } + } + + void subl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, base, offset); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void subq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_SUB_EvGv, src, dst); + } + + void subq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_SUB, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_SUB, dst); + m_formatter.immediate32(imm); + } + } +#else + void subl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void xorl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_XOR_EvGv, src, dst); + } + + void xorl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_XOR_GvEv, dst, base, offset); + } + + void xorl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_XOR_EvGv, src, base, offset); + } + + void xorl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_XOR, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_XOR, base, offset); + m_formatter.immediate32(imm); + } + } + + void xorl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_XOR, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_XOR, dst); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void xorq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_XOR_EvGv, src, dst); + } + + void xorq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_XOR, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_XOR, dst); + m_formatter.immediate32(imm); + } + } + + void xorq_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_XOR_EvGv, src, base, offset); + } + + void rorq_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_ROR, dst); + else { + m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_ROR, dst); + m_formatter.immediate8(imm); + } + } + +#endif + + void sarl_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SAR, dst); + else { + m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SAR, dst); + m_formatter.immediate8(imm); + } + } + + void sarl_CLr(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst); + } + + void shrl_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHR, dst); + else { + m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHR, dst); + m_formatter.immediate8(imm); + } + } + + void shrl_CLr(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHR, dst); + } + + void shll_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHL, dst); + else { + m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHL, dst); + m_formatter.immediate8(imm); + } + } + + void shll_CLr(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHL, dst); + } + +#if CPU(X86_64) + void sarq_CLr(RegisterID dst) + { + m_formatter.oneByteOp64(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst); + } + + void sarq_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_SAR, dst); + else { + m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_SAR, dst); + m_formatter.immediate8(imm); + } + } +#endif + + void imull_rr(RegisterID src, RegisterID dst) + { + m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, src); + } + + void imull_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, base, offset); + } + + void imull_i32r(RegisterID src, int32_t value, RegisterID dst) + { + m_formatter.oneByteOp(OP_IMUL_GvEvIz, dst, src); + m_formatter.immediate32(value); + } + + void idivl_r(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_IDIV, dst); + } + + // Comparisons: + + void cmpl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_CMP_EvGv, src, dst); + } + + void cmpl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_CMP_EvGv, src, base, offset); + } + + void cmpl_mr(int offset, RegisterID base, RegisterID src) + { + m_formatter.oneByteOp(OP_CMP_GvEv, src, base, offset); + } + + void cmpl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); + m_formatter.immediate32(imm); + } + } + + void cmpl_ir_force32(int imm, RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); + m_formatter.immediate32(imm); + } + + void cmpl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); + m_formatter.immediate32(imm); + } + } + + void cmpb_im(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, offset); + m_formatter.immediate8(imm); + } + + void cmpb_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate8(imm); + } + +#if CPU(X86) + void cmpb_im(int imm, const void* addr) + { + m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, addr); + m_formatter.immediate8(imm); + } +#endif + + void cmpl_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate32(imm); + } + } + + void cmpl_im_force32(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); + m_formatter.immediate32(imm); + } + +#if CPU(X86_64) + void cmpq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_CMP_EvGv, src, dst); + } + + void cmpq_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_CMP_EvGv, src, base, offset); + } + + void cmpq_mr(int offset, RegisterID base, RegisterID src) + { + m_formatter.oneByteOp64(OP_CMP_GvEv, src, base, offset); + } + + void cmpq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); + m_formatter.immediate32(imm); + } + } + + void cmpq_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); + m_formatter.immediate32(imm); + } + } + + void cmpq_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate32(imm); + } + } +#else + void cmpl_rm(RegisterID reg, const void* addr) + { + m_formatter.oneByteOp(OP_CMP_EvGv, reg, addr); + } + + void cmpl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void cmpw_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); + m_formatter.immediate16(imm); + } + } + + void cmpw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_CMP_EvGv, src, base, index, scale, offset); + } + + void cmpw_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate16(imm); + } + } + + void testl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_TEST_EvGv, src, dst); + } + + void testl_i32r(int imm, RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, dst); + m_formatter.immediate32(imm); + } + + void testl_i32m(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, offset); + m_formatter.immediate32(imm); + } + + void testb_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp8(OP_TEST_EbGb, src, dst); + } + + void testb_im(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, offset); + m_formatter.immediate8(imm); + } + + void testb_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, index, scale, offset); + m_formatter.immediate8(imm); + } + +#if CPU(X86) + void testb_im(int imm, const void* addr) + { + m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, addr); + m_formatter.immediate8(imm); + } +#endif + + void testl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset); + m_formatter.immediate32(imm); + } + +#if CPU(X86_64) + void testq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_TEST_EvGv, src, dst); + } + + void testq_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_TEST_EvGv, src, base, offset); + } + + void testq_i32r(int imm, RegisterID dst) + { + m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, dst); + m_formatter.immediate32(imm); + } + + void testq_i32m(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, offset); + m_formatter.immediate32(imm); + } + + void testq_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset); + m_formatter.immediate32(imm); + } +#endif + + void testw_rr(RegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_TEST_EvGv, src, dst); + } + + void testb_i8r(int imm, RegisterID dst) + { + m_formatter.oneByteOp8(OP_GROUP3_EbIb, GROUP3_OP_TEST, dst); + m_formatter.immediate8(imm); + } + + void setCC_r(Condition cond, RegisterID dst) + { + m_formatter.twoByteOp8(setccOpcode(cond), (GroupOpcodeID)0, dst); + } + + void sete_r(RegisterID dst) + { + m_formatter.twoByteOp8(setccOpcode(ConditionE), (GroupOpcodeID)0, dst); + } + + void setz_r(RegisterID dst) + { + sete_r(dst); + } + + void setne_r(RegisterID dst) + { + m_formatter.twoByteOp8(setccOpcode(ConditionNE), (GroupOpcodeID)0, dst); + } + + void setnz_r(RegisterID dst) + { + setne_r(dst); + } + + // Various move ops: + + void cdq() + { + m_formatter.oneByteOp(OP_CDQ); + } + + void fstpl(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_ESCAPE_DD, ESCAPE_DD_FSTP_doubleReal, base, offset); + } + + void xchgl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_XCHG_EvGv, src, dst); + } + +#if CPU(X86_64) + void xchgq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_XCHG_EvGv, src, dst); + } +#endif + + void movl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_MOV_EvGv, src, dst); + } + + void movl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_MOV_EvGv, src, base, offset); + } + + void movl_rm_disp32(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset); + } + + void movl_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset); + } + + void movl_mEAX(const void* addr) + { + m_formatter.oneByteOp(OP_MOV_EAXOv); +#if CPU(X86_64) + m_formatter.immediate64(reinterpret_cast(addr)); +#else + m_formatter.immediate32(reinterpret_cast(addr)); +#endif + } + + void movl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, offset); + } + + void movl_mr_disp32(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp_disp32(OP_MOV_GvEv, dst, base, offset); + } + + void movl_mr_disp8(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp_disp8(OP_MOV_GvEv, dst, base, offset); + } + + void movl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, index, scale, offset); + } + + void movl_i32r(int imm, RegisterID dst) + { + m_formatter.oneByteOp(OP_MOV_EAXIv, dst); + m_formatter.immediate32(imm); + } + + void movl_i32m(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, offset); + m_formatter.immediate32(imm); + } + + void movl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, index, scale, offset); + m_formatter.immediate32(imm); + } + + void movb_i8m(int imm, int offset, RegisterID base) + { + ASSERT(-128 <= imm && imm < 128); + m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, offset); + m_formatter.immediate8(imm); + } + + void movb_i8m(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + ASSERT(-128 <= imm && imm < 128); + m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, index, scale, offset); + m_formatter.immediate8(imm); + } + + void movb_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp8(OP_MOV_EbGb, src, base, index, scale, offset); + } + + void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp8(OP_MOV_EvGv, src, base, index, scale, offset); + } + + void movl_EAXm(const void* addr) + { + m_formatter.oneByteOp(OP_MOV_OvEAX); +#if CPU(X86_64) + m_formatter.immediate64(reinterpret_cast(addr)); +#else + m_formatter.immediate32(reinterpret_cast(addr)); +#endif + } + +#if CPU(X86_64) + void movq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOV_EvGv, src, dst); + } + + void movq_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_MOV_EvGv, src, base, offset); + } + + void movq_rm_disp32(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64_disp32(OP_MOV_EvGv, src, base, offset); + } + + void movq_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp64(OP_MOV_EvGv, src, base, index, scale, offset); + } + + void movq_mEAX(const void* addr) + { + m_formatter.oneByteOp64(OP_MOV_EAXOv); + m_formatter.immediate64(reinterpret_cast(addr)); + } + + void movq_EAXm(const void* addr) + { + m_formatter.oneByteOp64(OP_MOV_OvEAX); + m_formatter.immediate64(reinterpret_cast(addr)); + } + + void movq_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOV_GvEv, dst, base, offset); + } + + void movq_mr_disp32(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64_disp32(OP_MOV_GvEv, dst, base, offset); + } + + void movq_mr_disp8(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64_disp8(OP_MOV_GvEv, dst, base, offset); + } + + void movq_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOV_GvEv, dst, base, index, scale, offset); + } + + void movq_i32m(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_GROUP11_EvIz, GROUP11_MOV, base, offset); + m_formatter.immediate32(imm); + } + + void movq_i64r(int64_t imm, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOV_EAXIv, dst); + m_formatter.immediate64(imm); + } + + void movsxd_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOVSXD_GvEv, dst, src); + } + + +#else + void movl_rm(RegisterID src, const void* addr) + { + if (src == X86Registers::eax) + movl_EAXm(addr); + else + m_formatter.oneByteOp(OP_MOV_EvGv, src, addr); + } + + void movl_mr(const void* addr, RegisterID dst) + { + if (dst == X86Registers::eax) + movl_mEAX(addr); + else + m_formatter.oneByteOp(OP_MOV_GvEv, dst, addr); + } + + void movl_i32m(int imm, const void* addr) + { + m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, addr); + m_formatter.immediate32(imm); + } +#endif + + void movzwl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, offset); + } + + void movzwl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, index, scale, offset); + } + + void movswl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, offset); + } + + void movswl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, index, scale, offset); + } + + void movzbl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, offset); + } + + void movzbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, index, scale, offset); + } + + void movsbl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, offset); + } + + void movsbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, index, scale, offset); + } + + void movzbl_rr(RegisterID src, RegisterID dst) + { + // In 64-bit, this may cause an unnecessary REX to be planted (if the dst register + // is in the range ESP-EDI, and the src would not have required a REX). Unneeded + // REX prefixes are defined to be silently ignored by the processor. + m_formatter.twoByteOp8(OP2_MOVZX_GvEb, dst, src); + } + + void leal_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_LEA, dst, base, offset); + } +#if CPU(X86_64) + void leaq_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64(OP_LEA, dst, base, offset); + } +#endif + + // Flow control: + + AssemblerLabel call() + { + m_formatter.oneByteOp(OP_CALL_rel32); + return m_formatter.immediateRel32(); + } + + AssemblerLabel call(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, dst); + return m_formatter.label(); + } + + void call_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, base, offset); + } + + AssemblerLabel jmp() + { + m_formatter.oneByteOp(OP_JMP_rel32); + return m_formatter.immediateRel32(); + } + + // Return a AssemblerLabel so we have a label to the jump, so we can use this + // To make a tail recursive call on x86-64. The MacroAssembler + // really shouldn't wrap this as a Jump, since it can't be linked. :-/ + AssemblerLabel jmp_r(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, dst); + return m_formatter.label(); + } + + void jmp_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, base, offset); + } + +#if !CPU(X86_64) + void jmp_m(const void* address) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, address); + } +#endif + + AssemblerLabel jne() + { + m_formatter.twoByteOp(jccRel32(ConditionNE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jnz() + { + return jne(); + } + + AssemblerLabel je() + { + m_formatter.twoByteOp(jccRel32(ConditionE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jz() + { + return je(); + } + + AssemblerLabel jl() + { + m_formatter.twoByteOp(jccRel32(ConditionL)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jb() + { + m_formatter.twoByteOp(jccRel32(ConditionB)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jle() + { + m_formatter.twoByteOp(jccRel32(ConditionLE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jbe() + { + m_formatter.twoByteOp(jccRel32(ConditionBE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jge() + { + m_formatter.twoByteOp(jccRel32(ConditionGE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jg() + { + m_formatter.twoByteOp(jccRel32(ConditionG)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel ja() + { + m_formatter.twoByteOp(jccRel32(ConditionA)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jae() + { + m_formatter.twoByteOp(jccRel32(ConditionAE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jo() + { + m_formatter.twoByteOp(jccRel32(ConditionO)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jp() + { + m_formatter.twoByteOp(jccRel32(ConditionP)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel js() + { + m_formatter.twoByteOp(jccRel32(ConditionS)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jCC(Condition cond) + { + m_formatter.twoByteOp(jccRel32(cond)); + return m_formatter.immediateRel32(); + } + + // SSE operations: + + void addsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void addsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, base, offset); + } + +#if !CPU(X86_64) + void addsd_mr(const void* address, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, address); + } +#endif + + void cvtsi2sd_rr(RegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src); + } + + void cvtsi2sd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, offset); + } + +#if !CPU(X86_64) + void cvtsi2sd_mr(const void* address, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, address); + } +#endif + + void cvttsd2si_rr(XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src); + } + + void cvtsd2ss_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTSD2SS_VsdWsd, dst, (RegisterID)src); + } + + void cvtss2sd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F3); + m_formatter.twoByteOp(OP2_CVTSS2SD_VsdWsd, dst, (RegisterID)src); + } + +#if CPU(X86_64) + void cvttsd2siq_rr(XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp64(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src); + } +#endif + + void movd_rr(XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_MOVD_EdVd, (RegisterID)src, dst); + } + + void movd_rr(RegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_MOVD_VdEd, (RegisterID)dst, src); + } + +#if CPU(X86_64) + void movq_rr(XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp64(OP2_MOVD_EdVd, (RegisterID)src, dst); + } + + void movq_rr(RegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp64(OP2_MOVD_VdEd, (RegisterID)dst, src); + } +#endif + + void movsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void movsd_rm(XMMRegisterID src, int offset, RegisterID base) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset); + } + + void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset); + } + + void movss_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.prefix(PRE_SSE_F3); + m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset); + } + + void movsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset); + } + + void movsd_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, dst, base, index, scale, offset); + } + + void movss_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F3); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, dst, base, index, scale, offset); + } + +#if !CPU(X86_64) + void movsd_mr(const void* address, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address); + } + void movsd_rm(XMMRegisterID src, const void* address) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address); + } +#endif + + void mulsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void mulsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, base, offset); + } + + void pextrw_irr(int whichWord, XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_PEXTRW_GdUdIb, (RegisterID)dst, (RegisterID)src); + m_formatter.immediate8(whichWord); + } + + void psllq_i8r(int imm, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp8(OP2_PSLLQ_UdqIb, GROUP14_OP_PSLLQ, (RegisterID)dst); + m_formatter.immediate8(imm); + } + + void psrlq_i8r(int imm, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp8(OP2_PSRLQ_UdqIb, GROUP14_OP_PSRLQ, (RegisterID)dst); + m_formatter.immediate8(imm); + } + + void por_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_POR_VdqWdq, (RegisterID)dst, (RegisterID)src); + } + + void subsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void subsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, base, offset); + } + + void ucomisd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void ucomisd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, base, offset); + } + + void divsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void divsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, base, offset); + } + + void xorpd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src); + } + + void andnpd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_ANDNPD_VpdWpd, (RegisterID)dst, (RegisterID)src); + } + + void sqrtsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_SQRTSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + // Misc instructions: + + void int3() + { + m_formatter.oneByteOp(OP_INT3); + } + + void ret() + { + m_formatter.oneByteOp(OP_RET); + } + + void predictNotTaken() + { + m_formatter.prefix(PRE_PREDICT_BRANCH_NOT_TAKEN); + } + + // Assembler admin methods: + + size_t codeSize() const + { + return m_formatter.codeSize(); + } + + AssemblerLabel labelForWatchpoint() + { + AssemblerLabel result = m_formatter.label(); + if (static_cast(result.m_offset) != m_indexOfLastWatchpoint) + result = label(); + m_indexOfLastWatchpoint = result.m_offset; + m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); + return result; + } + + AssemblerLabel labelIgnoringWatchpoints() + { + return m_formatter.label(); + } + + AssemblerLabel label() + { + AssemblerLabel result = m_formatter.label(); + while (UNLIKELY(static_cast(result.m_offset) < m_indexOfTailOfLastWatchpoint)) { + nop(); + result = m_formatter.label(); + } + return result; + } + + AssemblerLabel align(int alignment) + { + while (!m_formatter.isAligned(alignment)) + m_formatter.oneByteOp(OP_HLT); + + return label(); + } + + // Linking & patching: + // + // 'link' and 'patch' methods are for use on unprotected code - such as the code + // within the AssemblerBuffer, and code being patched by the patch buffer. Once + // code has been finalized it is (platform support permitting) within a non- + // writable region of memory; to modify the code in an execute-only execuable + // pool the 'repatch' and 'relink' methods should be used. + + void linkJump(AssemblerLabel from, AssemblerLabel to) + { + ASSERT(from.isSet()); + ASSERT(to.isSet()); + + char* code = reinterpret_cast(m_formatter.data()); + ASSERT(!reinterpret_cast(code + from.m_offset)[-1]); + setRel32(code + from.m_offset, code + to.m_offset); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + + setRel32(reinterpret_cast(code) + from.m_offset, to); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + + setRel32(reinterpret_cast(code) + from.m_offset, to); + } + + static void linkPointer(void* code, AssemblerLabel where, void* value) + { + ASSERT(where.isSet()); + + setPointer(reinterpret_cast(code) + where.m_offset, value); + } + + static void relinkJump(void* from, void* to) + { + setRel32(from, to); + } + + static void relinkCall(void* from, void* to) + { + setRel32(from, to); + } + + static void repatchCompact(void* where, int32_t value) + { + ASSERT(value >= std::numeric_limits::min()); + ASSERT(value <= std::numeric_limits::max()); + setInt8(where, value); + } + + static void repatchInt32(void* where, int32_t value) + { + setInt32(where, value); + } + + static void repatchPointer(void* where, void* value) + { + setPointer(where, value); + } + + static void* readPointer(void* where) + { + return reinterpret_cast(where)[-1]; + } + + static void replaceWithJump(void* instructionStart, void* to) + { + uint8_t* ptr = reinterpret_cast(instructionStart); + uint8_t* dstPtr = reinterpret_cast(to); + intptr_t distance = (intptr_t)(dstPtr - (ptr + 5)); + ptr[0] = static_cast(OP_JMP_rel32); + *reinterpret_cast(ptr + 1) = static_cast(distance); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return 5; + } + + static void replaceWithLoad(void* instructionStart) + { + uint8_t* ptr = reinterpret_cast(instructionStart); +#if CPU(X86_64) + if ((*ptr & ~15) == PRE_REX) + ptr++; +#endif + switch (*ptr) { + case OP_MOV_GvEv: + break; + case OP_LEA: + *ptr = OP_MOV_GvEv; + break; + default: + ASSERT_NOT_REACHED(); + } + } + + static void replaceWithAddressComputation(void* instructionStart) + { + uint8_t* ptr = reinterpret_cast(instructionStart); +#if CPU(X86_64) + if ((*ptr & ~15) == PRE_REX) + ptr++; +#endif + switch (*ptr) { + case OP_MOV_GvEv: + *ptr = OP_LEA; + break; + case OP_LEA: + break; + default: + ASSERT_NOT_REACHED(); + } + } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + ASSERT(call.isSet()); + return call.m_offset; + } + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + ASSERT(label.isSet()); + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + return m_formatter.executableCopy(globalData, ownerUID, effort); + } + + unsigned debugOffset() { return m_formatter.debugOffset(); } + + void nop() + { + m_formatter.oneByteOp(OP_NOP); + } + + // This is a no-op on x86 + ALWAYS_INLINE static void cacheFlush(void*, size_t) { } + +private: + + static void setPointer(void* where, void* value) + { + reinterpret_cast(where)[-1] = value; + } + + static void setInt32(void* where, int32_t value) + { + reinterpret_cast(where)[-1] = value; + } + + static void setInt8(void* where, int8_t value) + { + reinterpret_cast(where)[-1] = value; + } + + static void setRel32(void* from, void* to) + { + intptr_t offset = reinterpret_cast(to) - reinterpret_cast(from); + ASSERT(offset == static_cast(offset)); + + setInt32(from, offset); + } + + class X86InstructionFormatter { + + static const int maxInstructionSize = 16; + + public: + + // Legacy prefix bytes: + // + // These are emmitted prior to the instruction. + + void prefix(OneByteOpcodeID pre) + { + m_buffer.putByte(pre); + } + + // Word-sized operands / no operand instruction formatters. + // + // In addition to the opcode, the following operand permutations are supported: + // * None - instruction takes no operands. + // * One register - the low three bits of the RegisterID are added into the opcode. + // * Two registers - encode a register form ModRm (for all ModRm formats, the reg field is passed first, and a GroupOpcodeID may be passed in its place). + // * Three argument ModRM - a register, and a register and an offset describing a memory operand. + // * Five argument ModRM - a register, and a base register, an index, scale, and offset describing a memory operand. + // + // For 32-bit x86 targets, the address operand may also be provided as a void*. + // On 64-bit targets REX prefixes will be planted as necessary, where high numbered registers are used. + // + // The twoByteOp methods plant two-byte Intel instructions sequences (first opcode byte 0x0F). + + void oneByteOp(OneByteOpcodeID opcode) + { + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putByteUnchecked(opcode); + } + + void oneByteOp(OneByteOpcodeID opcode, RegisterID reg) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(0, 0, reg); + m_buffer.putByteUnchecked(opcode + (reg & 7)); + } + + void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, rm); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, offset); + } + + void oneByteOp_disp32(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp32(reg, base, offset); + } + + void oneByteOp_disp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp8(reg, base, offset); + } + + void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, index, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, index, scale, offset); + } + +#if !CPU(X86_64) + void oneByteOp(OneByteOpcodeID opcode, int reg, const void* address) + { + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, address); + } +#endif + + void twoByteOp(TwoByteOpcodeID opcode) + { + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + } + + void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, rm); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, base); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, offset); + } + + void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, index, base); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, index, scale, offset); + } + +#if !CPU(X86_64) + void twoByteOp(TwoByteOpcodeID opcode, int reg, const void* address) + { + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, address); + } +#endif + +#if CPU(X86_64) + // Quad-word-sized operands: + // + // Used to format 64-bit operantions, planting a REX.w prefix. + // When planting d64 or f64 instructions, not requiring a REX.w prefix, + // the normal (non-'64'-postfixed) formatters should be used. + + void oneByteOp64(OneByteOpcodeID opcode) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(0, 0, 0); + m_buffer.putByteUnchecked(opcode); + } + + void oneByteOp64(OneByteOpcodeID opcode, RegisterID reg) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(0, 0, reg); + m_buffer.putByteUnchecked(opcode + (reg & 7)); + } + + void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, rm); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, offset); + } + + void oneByteOp64_disp32(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp32(reg, base, offset); + } + + void oneByteOp64_disp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp8(reg, base, offset); + } + + void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, index, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, index, scale, offset); + } + + void twoByteOp64(TwoByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, rm); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } +#endif + + // Byte-operands: + // + // These methods format byte operations. Byte operations differ from the normal + // formatters in the circumstances under which they will decide to emit REX prefixes. + // These should be used where any register operand signifies a byte register. + // + // The disctinction is due to the handling of register numbers in the range 4..7 on + // x86-64. These register numbers may either represent the second byte of the first + // four registers (ah..bh) or the first byte of the second four registers (spl..dil). + // + // Since ah..bh cannot be used in all permutations of operands (specifically cannot + // be accessed where a REX prefix is present), these are likely best treated as + // deprecated. In order to ensure the correct registers spl..dil are selected a + // REX prefix will be emitted for any byte register operand in the range 4..15. + // + // These formatters may be used in instructions where a mix of operand sizes, in which + // case an unnecessary REX will be emitted, for example: + // movzbl %al, %edi + // In this case a REX will be planted since edi is 7 (and were this a byte operand + // a REX would be required to specify dil instead of bh). Unneeded REX prefixes will + // be silently ignored by the processor. + // + // Address operands should still be checked using regRequiresRex(), while byteRegRequiresRex() + // is provided to check byte register operands. + + void oneByteOp8(OneByteOpcodeID opcode, GroupOpcodeID groupOp, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(rm), 0, 0, rm); + m_buffer.putByteUnchecked(opcode); + registerModRM(groupOp, rm); + } + + void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(reg) || byteRegRequiresRex(rm), reg, 0, rm); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(reg) || regRequiresRex(index) || regRequiresRex(base), reg, index, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, index, scale, offset); + } + + void twoByteOp8(TwoByteOpcodeID opcode, RegisterID reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void twoByteOp8(TwoByteOpcodeID opcode, GroupOpcodeID groupOp, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(rm), 0, 0, rm); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + registerModRM(groupOp, rm); + } + + // Immediates: + // + // An immedaite should be appended where appropriate after an op has been emitted. + // The writes are unchecked since the opcode formatters above will have ensured space. + + void immediate8(int imm) + { + m_buffer.putByteUnchecked(imm); + } + + void immediate16(int imm) + { + m_buffer.putShortUnchecked(imm); + } + + void immediate32(int imm) + { + m_buffer.putIntUnchecked(imm); + } + + void immediate64(int64_t imm) + { + m_buffer.putInt64Unchecked(imm); + } + + AssemblerLabel immediateRel32() + { + m_buffer.putIntUnchecked(0); + return label(); + } + + // Administrative methods: + + size_t codeSize() const { return m_buffer.codeSize(); } + AssemblerLabel label() const { return m_buffer.label(); } + bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); } + void* data() const { return m_buffer.data(); } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + return m_buffer.executableCopy(globalData, ownerUID, effort); + } + + unsigned debugOffset() { return m_buffer.debugOffset(); } + + private: + + // Internals; ModRm and REX formatters. + + static const RegisterID noBase = X86Registers::ebp; + static const RegisterID hasSib = X86Registers::esp; + static const RegisterID noIndex = X86Registers::esp; +#if CPU(X86_64) + static const RegisterID noBase2 = X86Registers::r13; + static const RegisterID hasSib2 = X86Registers::r12; + + // Registers r8 & above require a REX prefixe. + inline bool regRequiresRex(int reg) + { + return (reg >= X86Registers::r8); + } + + // Byte operand register spl & above require a REX prefix (to prevent the 'H' registers be accessed). + inline bool byteRegRequiresRex(int reg) + { + return (reg >= X86Registers::esp); + } + + // Format a REX prefix byte. + inline void emitRex(bool w, int r, int x, int b) + { + m_buffer.putByteUnchecked(PRE_REX | ((int)w << 3) | ((r>>3)<<2) | ((x>>3)<<1) | (b>>3)); + } + + // Used to plant a REX byte with REX.w set (for 64-bit operations). + inline void emitRexW(int r, int x, int b) + { + emitRex(true, r, x, b); + } + + // Used for operations with byte operands - use byteRegRequiresRex() to check register operands, + // regRequiresRex() to check other registers (i.e. address base & index). + inline void emitRexIf(bool condition, int r, int x, int b) + { + if (condition) emitRex(false, r, x, b); + } + + // Used for word sized operations, will plant a REX prefix if necessary (if any register is r8 or above). + inline void emitRexIfNeeded(int r, int x, int b) + { + emitRexIf(regRequiresRex(r) || regRequiresRex(x) || regRequiresRex(b), r, x, b); + } +#else + // No REX prefix bytes on 32-bit x86. + inline bool regRequiresRex(int) { return false; } + inline bool byteRegRequiresRex(int) { return false; } + inline void emitRexIf(bool, int, int, int) {} + inline void emitRexIfNeeded(int, int, int) {} +#endif + + enum ModRmMode { + ModRmMemoryNoDisp, + ModRmMemoryDisp8, + ModRmMemoryDisp32, + ModRmRegister, + }; + + void putModRm(ModRmMode mode, int reg, RegisterID rm) + { + m_buffer.putByteUnchecked((mode << 6) | ((reg & 7) << 3) | (rm & 7)); + } + + void putModRmSib(ModRmMode mode, int reg, RegisterID base, RegisterID index, int scale) + { + ASSERT(mode != ModRmRegister); + + putModRm(mode, reg, hasSib); + m_buffer.putByteUnchecked((scale << 6) | ((index & 7) << 3) | (base & 7)); + } + + void registerModRM(int reg, RegisterID rm) + { + putModRm(ModRmRegister, reg, rm); + } + + void memoryModRM(int reg, RegisterID base, int offset) + { + // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. +#if CPU(X86_64) + if ((base == hasSib) || (base == hasSib2)) { +#else + if (base == hasSib) { +#endif + if (!offset) // No need to check if the base is noBase, since we know it is hasSib! + putModRmSib(ModRmMemoryNoDisp, reg, base, noIndex, 0); + else if (CAN_SIGN_EXTEND_8_32(offset)) { + putModRmSib(ModRmMemoryDisp8, reg, base, noIndex, 0); + m_buffer.putByteUnchecked(offset); + } else { + putModRmSib(ModRmMemoryDisp32, reg, base, noIndex, 0); + m_buffer.putIntUnchecked(offset); + } + } else { +#if CPU(X86_64) + if (!offset && (base != noBase) && (base != noBase2)) +#else + if (!offset && (base != noBase)) +#endif + putModRm(ModRmMemoryNoDisp, reg, base); + else if (CAN_SIGN_EXTEND_8_32(offset)) { + putModRm(ModRmMemoryDisp8, reg, base); + m_buffer.putByteUnchecked(offset); + } else { + putModRm(ModRmMemoryDisp32, reg, base); + m_buffer.putIntUnchecked(offset); + } + } + } + + void memoryModRM_disp8(int reg, RegisterID base, int offset) + { + // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. + ASSERT(CAN_SIGN_EXTEND_8_32(offset)); +#if CPU(X86_64) + if ((base == hasSib) || (base == hasSib2)) { +#else + if (base == hasSib) { +#endif + putModRmSib(ModRmMemoryDisp8, reg, base, noIndex, 0); + m_buffer.putByteUnchecked(offset); + } else { + putModRm(ModRmMemoryDisp8, reg, base); + m_buffer.putByteUnchecked(offset); + } + } + + void memoryModRM_disp32(int reg, RegisterID base, int offset) + { + // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. +#if CPU(X86_64) + if ((base == hasSib) || (base == hasSib2)) { +#else + if (base == hasSib) { +#endif + putModRmSib(ModRmMemoryDisp32, reg, base, noIndex, 0); + m_buffer.putIntUnchecked(offset); + } else { + putModRm(ModRmMemoryDisp32, reg, base); + m_buffer.putIntUnchecked(offset); + } + } + + void memoryModRM(int reg, RegisterID base, RegisterID index, int scale, int offset) + { + ASSERT(index != noIndex); + +#if CPU(X86_64) + if (!offset && (base != noBase) && (base != noBase2)) +#else + if (!offset && (base != noBase)) +#endif + putModRmSib(ModRmMemoryNoDisp, reg, base, index, scale); + else if (CAN_SIGN_EXTEND_8_32(offset)) { + putModRmSib(ModRmMemoryDisp8, reg, base, index, scale); + m_buffer.putByteUnchecked(offset); + } else { + putModRmSib(ModRmMemoryDisp32, reg, base, index, scale); + m_buffer.putIntUnchecked(offset); + } + } + +#if !CPU(X86_64) + void memoryModRM(int reg, const void* address) + { + // noBase + ModRmMemoryNoDisp means noBase + ModRmMemoryDisp32! + putModRm(ModRmMemoryNoDisp, reg, noBase); + m_buffer.putIntUnchecked(reinterpret_cast(address)); + } +#endif + + AssemblerBuffer m_buffer; + } m_formatter; + int m_indexOfLastWatchpoint; + int m_indexOfTailOfLastWatchpoint; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(X86) + +#endif // X86Assembler_h diff --git a/masm/config.h b/masm/config.h new file mode 100644 index 0000000000..298bcb101d --- /dev/null +++ b/masm/config.h @@ -0,0 +1,4 @@ + +#include +#include +#include diff --git a/masm/disassembler/Disassembler.h b/masm/disassembler/Disassembler.h new file mode 100644 index 0000000000..7d7400ac81 --- /dev/null +++ b/masm/disassembler/Disassembler.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Disassembler_h +#define Disassembler_h + +#include +#include +#include + +namespace JSC { + +class MacroAssemblerCodePtr; + +#if ENABLE(DISASSEMBLER) +bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, FILE* out); +#else +inline bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char*, FILE*) +{ + return false; +} +#endif + +} // namespace JSC + +#endif // Disassembler_h + diff --git a/masm/disassembler/UDis86Disassembler.cpp b/masm/disassembler/UDis86Disassembler.cpp new file mode 100644 index 0000000000..b6baed4a27 --- /dev/null +++ b/masm/disassembler/UDis86Disassembler.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Disassembler.h" + +#if USE(UDIS86) + +#include "MacroAssemblerCodeRef.h" +#include "udis86.h" + +namespace JSC { + +bool tryToDisassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const char* prefix, FILE* out) +{ + ud_t disassembler; + ud_init(&disassembler); + ud_set_input_buffer(&disassembler, static_cast(codePtr.executableAddress()), size); +#if CPU(X86_64) + ud_set_mode(&disassembler, 64); +#else + ud_set_mode(&disassembler, 32); +#endif + ud_set_pc(&disassembler, bitwise_cast(codePtr.executableAddress())); + ud_set_syntax(&disassembler, UD_SYN_ATT); + + uint64_t currentPC = disassembler.pc; + while (ud_disassemble(&disassembler)) { + char pcString[20]; + snprintf(pcString, sizeof(pcString), "0x%lx", static_cast(currentPC)); + fprintf(out, "%s%16s: %s\n", prefix, pcString, ud_insn_asm(&disassembler)); + currentPC = disassembler.pc; + } + + return true; +} + +} // namespace JSC + +#endif // USE(UDIS86) + diff --git a/masm/disassembler/udis86/differences.txt b/masm/disassembler/udis86/differences.txt new file mode 100644 index 0000000000..3ef51efcfc --- /dev/null +++ b/masm/disassembler/udis86/differences.txt @@ -0,0 +1,22 @@ +This documents the differences between the stock version of udis86 and the one found +here: + +- All files not named "udis86" were prefixed with "udis86". + +- assert() has been changed to ASSERT() + +- Mass rename of udis86_input.h inp_ prefixed functions and macros to ud_inp_ to + avoid namespace pollution. + +- Removal of KERNEL checks. + +- Added #include of udis86_extern.h in udis86_decode.c. + +- Removed s_ie__pause and s_ie__nop from udis86_decode.c, since they weren't used. + +- Made udis86_syn.h use WTF_ATTRIBUTE_PRINTF. This required making a bunch of little + fixes to make the compiler's format string warnings go away. + +- Made the code in udis86_syn.h use vsnprintf() instead of vsprintf(). + +- Fixed udis86_syn-att.c's jump destination printing to work correctly in 64-bit mode. diff --git a/masm/disassembler/udis86/itab.py b/masm/disassembler/udis86/itab.py new file mode 100644 index 0000000000..27fa9b3f37 --- /dev/null +++ b/masm/disassembler/udis86/itab.py @@ -0,0 +1,354 @@ +# udis86 - scripts/itab.py +# +# Copyright (c) 2009 Vivek Thampi +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import sys + +sys.path.append( '../scripts' ); + +import ud_optable +import ud_opcode + +class UdItabGenerator( ud_opcode.UdOpcodeTables ): + + OperandDict = { + "Ap" : [ "OP_A" , "SZ_P" ], + "E" : [ "OP_E" , "SZ_NA" ], + "Eb" : [ "OP_E" , "SZ_B" ], + "Ew" : [ "OP_E" , "SZ_W" ], + "Ev" : [ "OP_E" , "SZ_V" ], + "Ed" : [ "OP_E" , "SZ_D" ], + "Eq" : [ "OP_E" , "SZ_Q" ], + "Ez" : [ "OP_E" , "SZ_Z" ], + "Ex" : [ "OP_E" , "SZ_MDQ" ], + "Ep" : [ "OP_E" , "SZ_P" ], + "G" : [ "OP_G" , "SZ_NA" ], + "Gb" : [ "OP_G" , "SZ_B" ], + "Gw" : [ "OP_G" , "SZ_W" ], + "Gv" : [ "OP_G" , "SZ_V" ], + "Gy" : [ "OP_G" , "SZ_MDQ" ], + "Gy" : [ "OP_G" , "SZ_MDQ" ], + "Gd" : [ "OP_G" , "SZ_D" ], + "Gq" : [ "OP_G" , "SZ_Q" ], + "Gx" : [ "OP_G" , "SZ_MDQ" ], + "Gz" : [ "OP_G" , "SZ_Z" ], + "M" : [ "OP_M" , "SZ_NA" ], + "Mb" : [ "OP_M" , "SZ_B" ], + "Mw" : [ "OP_M" , "SZ_W" ], + "Ms" : [ "OP_M" , "SZ_W" ], + "Md" : [ "OP_M" , "SZ_D" ], + "Mq" : [ "OP_M" , "SZ_Q" ], + "Mt" : [ "OP_M" , "SZ_T" ], + "Mo" : [ "OP_M" , "SZ_O" ], + "MwRv" : [ "OP_MR" , "SZ_WV" ], + "MdRy" : [ "OP_MR" , "SZ_DY" ], + "MbRv" : [ "OP_MR" , "SZ_BV" ], + "I1" : [ "OP_I1" , "SZ_NA" ], + "I3" : [ "OP_I3" , "SZ_NA" ], + "Ib" : [ "OP_I" , "SZ_B" ], + "Isb" : [ "OP_I" , "SZ_SB" ], + "Iw" : [ "OP_I" , "SZ_W" ], + "Iv" : [ "OP_I" , "SZ_V" ], + "Iz" : [ "OP_I" , "SZ_Z" ], + "Jv" : [ "OP_J" , "SZ_V" ], + "Jz" : [ "OP_J" , "SZ_Z" ], + "Jb" : [ "OP_J" , "SZ_B" ], + "R" : [ "OP_R" , "SZ_RDQ" ], + "C" : [ "OP_C" , "SZ_NA" ], + "D" : [ "OP_D" , "SZ_NA" ], + "S" : [ "OP_S" , "SZ_NA" ], + "Ob" : [ "OP_O" , "SZ_B" ], + "Ow" : [ "OP_O" , "SZ_W" ], + "Ov" : [ "OP_O" , "SZ_V" ], + "V" : [ "OP_V" , "SZ_O" ], + "W" : [ "OP_W" , "SZ_O" ], + "Wsd" : [ "OP_W" , "SZ_O" ], + "Wss" : [ "OP_W" , "SZ_O" ], + "P" : [ "OP_P" , "SZ_Q" ], + "Q" : [ "OP_Q" , "SZ_Q" ], + "VR" : [ "OP_VR" , "SZ_O" ], + "PR" : [ "OP_PR" , "SZ_Q" ], + "AL" : [ "OP_AL" , "SZ_NA" ], + "CL" : [ "OP_CL" , "SZ_NA" ], + "DL" : [ "OP_DL" , "SZ_NA" ], + "BL" : [ "OP_BL" , "SZ_NA" ], + "AH" : [ "OP_AH" , "SZ_NA" ], + "CH" : [ "OP_CH" , "SZ_NA" ], + "DH" : [ "OP_DH" , "SZ_NA" ], + "BH" : [ "OP_BH" , "SZ_NA" ], + "AX" : [ "OP_AX" , "SZ_NA" ], + "CX" : [ "OP_CX" , "SZ_NA" ], + "DX" : [ "OP_DX" , "SZ_NA" ], + "BX" : [ "OP_BX" , "SZ_NA" ], + "SI" : [ "OP_SI" , "SZ_NA" ], + "DI" : [ "OP_DI" , "SZ_NA" ], + "SP" : [ "OP_SP" , "SZ_NA" ], + "BP" : [ "OP_BP" , "SZ_NA" ], + "eAX" : [ "OP_eAX" , "SZ_NA" ], + "eCX" : [ "OP_eCX" , "SZ_NA" ], + "eDX" : [ "OP_eDX" , "SZ_NA" ], + "eBX" : [ "OP_eBX" , "SZ_NA" ], + "eSI" : [ "OP_eSI" , "SZ_NA" ], + "eDI" : [ "OP_eDI" , "SZ_NA" ], + "eSP" : [ "OP_eSP" , "SZ_NA" ], + "eBP" : [ "OP_eBP" , "SZ_NA" ], + "rAX" : [ "OP_rAX" , "SZ_NA" ], + "rCX" : [ "OP_rCX" , "SZ_NA" ], + "rBX" : [ "OP_rBX" , "SZ_NA" ], + "rDX" : [ "OP_rDX" , "SZ_NA" ], + "rSI" : [ "OP_rSI" , "SZ_NA" ], + "rDI" : [ "OP_rDI" , "SZ_NA" ], + "rSP" : [ "OP_rSP" , "SZ_NA" ], + "rBP" : [ "OP_rBP" , "SZ_NA" ], + "ES" : [ "OP_ES" , "SZ_NA" ], + "CS" : [ "OP_CS" , "SZ_NA" ], + "DS" : [ "OP_DS" , "SZ_NA" ], + "SS" : [ "OP_SS" , "SZ_NA" ], + "GS" : [ "OP_GS" , "SZ_NA" ], + "FS" : [ "OP_FS" , "SZ_NA" ], + "ST0" : [ "OP_ST0" , "SZ_NA" ], + "ST1" : [ "OP_ST1" , "SZ_NA" ], + "ST2" : [ "OP_ST2" , "SZ_NA" ], + "ST3" : [ "OP_ST3" , "SZ_NA" ], + "ST4" : [ "OP_ST4" , "SZ_NA" ], + "ST5" : [ "OP_ST5" , "SZ_NA" ], + "ST6" : [ "OP_ST6" , "SZ_NA" ], + "ST7" : [ "OP_ST7" , "SZ_NA" ], + "NONE" : [ "OP_NONE" , "SZ_NA" ], + "ALr8b" : [ "OP_ALr8b" , "SZ_NA" ], + "CLr9b" : [ "OP_CLr9b" , "SZ_NA" ], + "DLr10b" : [ "OP_DLr10b" , "SZ_NA" ], + "BLr11b" : [ "OP_BLr11b" , "SZ_NA" ], + "AHr12b" : [ "OP_AHr12b" , "SZ_NA" ], + "CHr13b" : [ "OP_CHr13b" , "SZ_NA" ], + "DHr14b" : [ "OP_DHr14b" , "SZ_NA" ], + "BHr15b" : [ "OP_BHr15b" , "SZ_NA" ], + "rAXr8" : [ "OP_rAXr8" , "SZ_NA" ], + "rCXr9" : [ "OP_rCXr9" , "SZ_NA" ], + "rDXr10" : [ "OP_rDXr10" , "SZ_NA" ], + "rBXr11" : [ "OP_rBXr11" , "SZ_NA" ], + "rSPr12" : [ "OP_rSPr12" , "SZ_NA" ], + "rBPr13" : [ "OP_rBPr13" , "SZ_NA" ], + "rSIr14" : [ "OP_rSIr14" , "SZ_NA" ], + "rDIr15" : [ "OP_rDIr15" , "SZ_NA" ], + "jWP" : [ "OP_J" , "SZ_WP" ], + "jDP" : [ "OP_J" , "SZ_DP" ], + + } + + # + # opcode prefix dictionary + # + PrefixDict = { + "aso" : "P_aso", + "oso" : "P_oso", + "rexw" : "P_rexw", + "rexb" : "P_rexb", + "rexx" : "P_rexx", + "rexr" : "P_rexr", + "seg" : "P_seg", + "inv64" : "P_inv64", + "def64" : "P_def64", + "depM" : "P_depM", + "cast1" : "P_c1", + "cast2" : "P_c2", + "cast3" : "P_c3", + "cast" : "P_cast", + "sext" : "P_sext" + } + + InvalidEntryIdx = 0 + InvalidEntry = { 'type' : 'invalid', + 'mnemonic' : 'invalid', + 'operands' : '', + 'prefixes' : '', + 'meta' : '' } + + Itab = [] # instruction table + ItabIdx = 1 # instruction table index + GtabIdx = 0 # group table index + GtabMeta = [] + + ItabLookup = {} + + MnemonicAliases = ( "invalid", "3dnow", "none", "db", "pause" ) + + def __init__( self ): + # first itab entry (0) is Invalid + self.Itab.append( self.InvalidEntry ) + self.MnemonicsTable.extend( self.MnemonicAliases ) + + def toGroupId( self, id ): + return 0x8000 | id + + def genLookupTable( self, table, scope = '' ): + idxArray = [ ] + ( tabIdx, self.GtabIdx ) = ( self.GtabIdx, self.GtabIdx + 1 ) + self.GtabMeta.append( { 'type' : table[ 'type' ], 'meta' : table[ 'meta' ] } ) + + for _idx in range( self.sizeOfTable( table[ 'type' ] ) ): + idx = "%02x" % _idx + + e = self.InvalidEntry + i = self.InvalidEntryIdx + + if idx in table[ 'entries' ].keys(): + e = table[ 'entries' ][ idx ] + + # leaf node (insn) + if e[ 'type' ] == 'insn': + ( i, self.ItabIdx ) = ( self.ItabIdx, self.ItabIdx + 1 ) + self.Itab.append( e ) + elif e[ 'type' ] != 'invalid': + i = self.genLookupTable( e, 'static' ) + + idxArray.append( i ) + + name = "ud_itab__%s" % tabIdx + self.ItabLookup[ tabIdx ] = name + + self.ItabC.write( "\n" ); + if len( scope ): + self.ItabC.write( scope + ' ' ) + self.ItabC.write( "const uint16_t %s[] = {\n" % name ) + for i in range( len( idxArray ) ): + if i > 0 and i % 4 == 0: + self.ItabC.write( "\n" ) + if ( i%4 == 0 ): + self.ItabC.write( " /* %2x */" % i) + if idxArray[ i ] >= 0x8000: + self.ItabC.write( "%12s," % ("GROUP(%d)" % ( ~0x8000 & idxArray[ i ] ))) + else: + self.ItabC.write( "%12d," % ( idxArray[ i ] )) + self.ItabC.write( "\n" ) + self.ItabC.write( "};\n" ) + + return self.toGroupId( tabIdx ) + + def genLookupTableList( self ): + self.ItabC.write( "\n\n" ); + self.ItabC.write( "struct ud_lookup_table_list_entry ud_lookup_table_list[] = {\n" ) + for i in range( len( self.GtabMeta ) ): + f0 = self.ItabLookup[ i ] + "," + f1 = ( self.nameOfTable( self.GtabMeta[ i ][ 'type' ] ) ) + "," + f2 = "\"%s\"" % self.GtabMeta[ i ][ 'meta' ] + self.ItabC.write( " /* %03d */ { %s %s %s },\n" % ( i, f0, f1, f2 ) ) + self.ItabC.write( "};" ) + + def genInsnTable( self ): + self.ItabC.write( "struct ud_itab_entry ud_itab[] = {\n" ); + idx = 0 + for e in self.Itab: + opr_c = [ "O_NONE", "O_NONE", "O_NONE" ] + pfx_c = [] + opr = e[ 'operands' ] + for i in range(len(opr)): + if not (opr[i] in self.OperandDict.keys()): + print "error: invalid operand declaration: %s\n" % opr[i] + opr_c[i] = "O_" + opr[i] + opr = "%s %s %s" % (opr_c[0] + ",", opr_c[1] + ",", opr_c[2]) + + for p in e['prefixes']: + if not ( p in self.PrefixDict.keys() ): + print "error: invalid prefix specification: %s \n" % pfx + pfx_c.append( self.PrefixDict[p] ) + if len(e['prefixes']) == 0: + pfx_c.append( "P_none" ) + pfx = "|".join( pfx_c ) + + self.ItabC.write( " /* %04d */ { UD_I%s %s, %s },\n" \ + % ( idx, e[ 'mnemonic' ] + ',', opr, pfx ) ) + idx += 1 + self.ItabC.write( "};\n" ) + + self.ItabC.write( "\n\n" ); + self.ItabC.write( "const char * ud_mnemonics_str[] = {\n" ) + self.ItabC.write( ",\n ".join( [ "\"%s\"" % m for m in self.MnemonicsTable ] ) ) + self.ItabC.write( "\n};\n" ) + + + def genItabH( self ): + self.ItabH = open( "udis86_itab.h", "w" ) + + # Generate Table Type Enumeration + self.ItabH.write( "#ifndef UD_ITAB_H\n" ) + self.ItabH.write( "#define UD_ITAB_H\n\n" ) + + # table type enumeration + self.ItabH.write( "/* ud_table_type -- lookup table types (see lookup.c) */\n" ) + self.ItabH.write( "enum ud_table_type {\n " ) + enum = [ self.TableInfo[ k ][ 'name' ] for k in self.TableInfo.keys() ] + self.ItabH.write( ",\n ".join( enum ) ) + self.ItabH.write( "\n};\n\n" ); + + # mnemonic enumeration + self.ItabH.write( "/* ud_mnemonic -- mnemonic constants */\n" ) + enum = "enum ud_mnemonic_code {\n " + enum += ",\n ".join( [ "UD_I%s" % m for m in self.MnemonicsTable ] ) + enum += "\n} UD_ATTR_PACKED;\n" + self.ItabH.write( enum ) + self.ItabH.write( "\n" ) + + self.ItabH.write("\n/* itab entry operand definitions */\n"); + operands = self.OperandDict.keys() + operands.sort() + for o in operands: + self.ItabH.write("#define O_%-7s { %-12s %-8s }\n" % + (o, self.OperandDict[o][0] + ",", self.OperandDict[o][1])); + self.ItabH.write("\n\n"); + + self.ItabH.write( "extern const char * ud_mnemonics_str[];\n" ) + + self.ItabH.write( "#define GROUP(n) (0x8000 | (n))" ) + + self.ItabH.write( "\n#endif /* UD_ITAB_H */\n" ) + + self.ItabH.close() + + + def genItabC( self ): + self.ItabC = open( "udis86_itab.c", "w" ) + self.ItabC.write( "/* itab.c -- generated by itab.py, do no edit" ) + self.ItabC.write( " */\n" ); + self.ItabC.write( "#include \"udis86_decode.h\"\n\n" ); + + self.genLookupTable( self.OpcodeTable0 ) + self.genLookupTableList() + self.genInsnTable() + + self.ItabC.close() + + def genItab( self ): + self.genItabC() + self.genItabH() + +def main(): + generator = UdItabGenerator() + optableXmlParser = ud_optable.UdOptableXmlParser() + optableXmlParser.parse( sys.argv[ 1 ], generator.addInsnDef ) + + generator.genItab() + +if __name__ == '__main__': + main() diff --git a/masm/disassembler/udis86/optable.xml b/masm/disassembler/udis86/optable.xml new file mode 100644 index 0000000000..14b4ac5935 --- /dev/null +++ b/masm/disassembler/udis86/optable.xml @@ -0,0 +1,8959 @@ + + + + + + aaa + + 37 + inv64 + + + + + aad + + d5 + Ib + inv64 + + + + + aam + + d4 + Ib + inv64 + + + + + aas + + 3f + inv64 + + + + + adc + + aso rexr rexx rexb + 10 + Eb Gb + + + aso oso rexw rexr rexx rexb + 11 + Ev Gv + + + aso rexr rexx rexb + 12 + Gb Eb + + + aso oso rexw rexr rexx rexb + 13 + Gv Ev + + + 14 + AL Ib + + + oso rexw + 15 + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=2 + Eb Ib + + + aso rexr rexx rexb + 82 /reg=2 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 81 /reg=2 + Ev Iz + sext + + + aso oso rexw rexr rexx rexb + 83 /reg=2 + Ev Ib + sext + + + + + add + + aso rexr rexx rexb + 00 + Eb Gb + + + aso oso rexw rexr rexx rexb + 01 + Ev Gv + + + aso rexr rexx rexb + 02 + Gb Eb + + + aso oso rexw rexr rexx rexb + 03 + Gv Ev + + + 04 + AL Ib + + + oso rexw + 05 + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=0 + Eb Ib + + + aso rexr rexx rexb + 82 /reg=0 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 81 /reg=0 + Ev Iz + sext + + + aso oso rexw rexr rexx rexb + 83 /reg=0 + Ev Ib + sext + + + + + + + addpd + + aso rexr rexx rexb + sse66 0f 58 + V W + + + + + addps + + aso rexr rexx rexb + 0f 58 + V W + + + + + addsd + + aso rexr rexx rexb + ssef2 0f 58 + V W + + + + + addss + + aso rexr rexx rexb + ssef3 0f 58 + V W + + + + + and + + aso rexr rexx rexb + 20 + Eb Gb + + + aso oso rexw rexr rexx rexb + 21 + Ev Gv + + + aso rexr rexx rexb + 22 + Gb Eb + + + aso oso rexw rexr rexx rexb + 23 + Gv Ev + + + 24 + AL Ib + + + oso rexw + 25 + rAX Iz + sext + + + aso rexw rexr rexx rexb + 80 /reg=4 + Eb Ib + + + aso rexr rexx rexb + 82 /reg=4 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 81 /reg=4 + Ev Iz + sext + + + aso oso rexw rexr rexx rexb + 83 /reg=4 + Ev Ib + sext + + + + + andpd + + aso rexr rexx rexb + sse66 0f 54 + V W + + + + + andps + + aso rexr rexx rexb + 0f 54 + V W + + + + + andnpd + + aso rexr rexx rexb + sse66 0f 55 + V W + + + + + andnps + + aso rexr rexx rexb + 0f 55 + V W + + + + + arpl + + aso + 63 /m=16 + Ew Gw + inv64 + + + aso + 63 /m=32 + Ew Gw + inv64 + + + + + movsxd + + aso oso rexw rexx rexr rexb + 63 /m=64 + Gv Ed + + + + + bound + + aso oso + 62 + Gv M + inv64 + + + + + bsf + + aso oso rexw rexr rexx rexb + 0f bc + Gv Ev + + + + + bsr + + aso oso rexw rexr rexx rexb + 0f bd + Gv Ev + + + + + bswap + + oso rexw rexb + 0f c8 + rAXr8 + + + oso rexw rexb + 0f c9 + rCXr9 + + + oso rexw rexb + 0f ca + rDXr10 + + + oso rexw rexb + 0f cb + rBXr11 + + + oso rexw rexb + 0f cc + rSPr12 + + + oso rexw rexb + 0f cd + rBPr13 + + + oso rexw rexb + 0f ce + rSIr14 + + + oso rexw rexb + 0f cf + rDIr15 + + + + + bt + + aso oso rexw rexr rexx rexb + 0f ba /reg=4 + Ev Ib + + + aso oso rexw rexr rexx rexb + 0f a3 + Ev Gv + + + + + btc + + aso oso rexw rexr rexx rexb + 0f bb + Ev Gv + + + aso oso rexw rexr rexx rexb + 0f ba /reg=7 + Ev Ib + + + + + btr + + aso oso rexw rexr rexx rexb + 0f b3 + Ev Gv + + + aso oso rexw rexr rexx rexb + 0f ba /reg=6 + Ev Ib + + + + + bts + + aso oso rexw rexr rexx rexb + 0f ab + Ev Gv + + + aso oso rexw rexr rexx rexb + 0f ba /reg=5 + Ev Ib + + + + + call + + aso oso rexw rexr rexx rexb + ff /reg=2 + Ev + def64 + + + aso oso rexw rexr rexx rexb + ff /reg=3 + Ep + + + oso + e8 + Jz + def64 + + + oso + 9a + Ap + inv64 + + + + + cbw + + oso rexw + 98 /o=16 + + + + + cwde + + oso rexw + 98 /o=32 + + + + + cdqe + + oso rexw + 98 /o=64 + + + + + clc + + f8 + + + + + cld + + fc + + + + + clflush + + aso rexw rexr rexx rexb + 0f ae /reg=7 /mod=!11 + M + + + + + clgi + amd + + 0f 01 /reg=3 /mod=11 /rm=5 + + + + + cli + + fa + + + + + clts + + 0f 06 + + + + + cmc + + f5 + + + + + cmovo + + aso oso rexw rexr rexx rexb + 0f 40 + Gv Ev + + + + + cmovno + + aso oso rexw rexr rexx rexb + 0f 41 + Gv Ev + + + + + cmovb + + aso oso rexw rexr rexx rexb + 0f 42 + Gv Ev + + + + + cmovae + + aso oso rexw rexr rexx rexb + 0f 43 + Gv Ev + + + + + cmovz + + aso oso rexw rexr rexx rexb + 0f 44 + Gv Ev + + + + + cmovnz + + aso oso rexw rexr rexx rexb + 0f 45 + Gv Ev + + + + + cmovbe + + aso oso rexw rexr rexx rexb + 0f 46 + Gv Ev + + + + + cmova + + aso oso rexw rexr rexx rexb + 0f 47 + Gv Ev + + + + + cmovs + + aso oso rexw rexr rexx rexb + 0f 48 + Gv Ev + + + + + cmovns + + aso oso rexw rexr rexx rexb + 0f 49 + Gv Ev + + + + + cmovp + + aso oso rexw rexr rexx rexb + 0f 4a + Gv Ev + + + + + cmovnp + + aso oso rexw rexr rexx rexb + 0f 4b + Gv Ev + + + + + cmovl + + aso oso rexw rexr rexx rexb + 0f 4c + Gv Ev + + + + + cmovge + + aso oso rexw rexr rexx rexb + 0f 4d + Gv Ev + + + + + cmovle + + aso oso rexw rexr rexx rexb + 0f 4e + Gv Ev + + + + + cmovg + + aso oso rexw rexr rexx rexb + 0f 4f + Gv Ev + + + + + cmp + + aso rexr rexx rexb + 38 + Eb Gb + + + aso oso rexw rexr rexx rexb + 39 + Ev Gv + + + aso rexr rexx rexb + 3a + Gb Eb + + + aso oso rexw rexr rexx rexb + 3b + Gv Ev + + + 3c + AL Ib + + + oso rexw + 3d + rAX Iz + + + aso rexr rexx rexb + 80 /reg=7 + Eb Ib + + + aso rexr rexx rexb + 82 /reg=7 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 81 /reg=7 + Ev Iz + + + aso oso rexw rexr rexx rexb + 83 /reg=7 + Ev Ib + + + + + cmppd + + aso rexr rexx rexb + sse66 0f c2 + V W Ib + + + + + cmpps + + aso rexr rexx rexb + 0f c2 + V W Ib + + + + + cmpsb + + a6 + + + + + cmpsw + + oso rexw + a7 /o=16 + + + + + cmpsd + + oso rexw + a7 /o=32 + + + aso rexr rexx rexb + ssef2 0f c2 + V W Ib + + + + + cmpsq + + oso rexw + a7 /o=64 + + + + + cmpss + + aso rexr rexx rexb + ssef3 0f c2 + V W Ib + + + + + cmpxchg + + aso rexr rexx rexb + 0f b0 + Eb Gb + + + aso oso rexw rexr rexx rexb + 0f b1 + Ev Gv + + + + + cmpxchg8b + + aso rexr rexx rexb + 0f c7 /reg=1 + M + + + + + comisd + + aso rexr rexx rexb + sse66 0f 2f + V W + + + + + comiss + + aso rexr rexx rexb + 0f 2f + V W + + + + + cpuid + + 0f a2 + + + + + cvtdq2pd + + aso rexr rexx rexb + ssef3 0f e6 + V W + + + + + cvtdq2ps + + aso rexr rexx rexb + 0f 5b + V W + + + + + cvtpd2dq + + aso rexr rexx rexb + ssef2 0f e6 + V W + + + + + cvtpd2pi + + aso rexr rexx rexb + sse66 0f 2d + P W + + + + + cvtpd2ps + + aso rexr rexx rexb + sse66 0f 5a + V W + + + + + cvtpi2ps + + aso rexr rexx rexb + 0f 2a + V Q + + + + + cvtpi2pd + + aso rexr rexx rexb + sse66 0f 2a + V Q + + + + + cvtps2dq + + aso rexr rexx rexb + sse66 0f 5b + V W + + + + + cvtps2pi + + aso rexr rexx rexb + 0f 2d + P W + + + + + cvtps2pd + + aso rexr rexx rexb + 0f 5a + V W + + + + + cvtsd2si + + aso rexw rexr rexx rexb + ssef2 0f 2d + Gy W + + + + + cvtsd2ss + + aso rexr rexx rexb + ssef2 0f 5a + V W + + + + + cvtsi2ss + + aso rexw rexr rexx rexb + ssef3 0f 2a + V Ex + + + + + cvtss2si + + aso rexw rexr rexx rexb + ssef3 0f 2d + Gy W + + + + + cvtss2sd + + aso rexr rexx rexb + ssef3 0f 5a + V W + + + + + cvttpd2pi + + aso rexr rexx rexb + sse66 0f 2c + P W + + + + + cvttpd2dq + + aso rexr rexx rexb + sse66 0f e6 + V W + + + + + cvttps2dq + + aso rexr rexx rexb + ssef3 0f 5b + V W + + + + + cvttps2pi + + aso rexr rexx rexb + 0f 2c + P W + + + + + cvttsd2si + + aso rexw rexr rexx rexb + ssef2 0f 2c + Gy Wsd + + + + + cvtsi2sd + + aso rexw rexr rexx rexb + ssef2 0f 2a + V Ex + + + + + cvttss2si + + aso rexw rexr rexx rexb + ssef3 0f 2c + Gy Wsd + + + + + cwd + + oso rexw + 99 /o=16 + + + + + cdq + + oso rexw + 99 /o=32 + + + + + cqo + + oso rexw + 99 /o=64 + + + + + daa + + 27 + inv64 + + + + + das + + 2f + inv64 + + + + + dec + + oso + 48 + eAX + + + oso + 49 + eCX + + + oso + 4a + eDX + + + oso + 4b + eBX + + + oso + 4c + eSP + + + oso + 4d + eBP + + + oso + 4e + eSI + + + oso + 4f + eDI + + + aso rexw rexr rexx rexb + fe /reg=1 + Eb + + + aso oso rexw rexr rexx rexb + ff /reg=1 + Ev + + + + + div + + aso oso rexw rexr rexx rexb + f7 /reg=6 + Ev + + + aso rexw rexr rexx rexb + f6 /reg=6 + Eb + + + + + divpd + + aso rexr rexx rexb + sse66 0f 5e + V W + + + + + divps + + aso rexr rexx rexb + 0f 5e + V W + + + + + divsd + + aso rexr rexx rexb + ssef2 0f 5e + V W + + + + + divss + + aso rexr rexx rexb + ssef3 0f 5e + V W + + + + + emms + + 0f 77 + + + + + enter + + c8 + Iw Ib + def64 depM + + + + + f2xm1 + X87 + + d9 /mod=11 /x87=30 + + + + + fabs + X87 + + d9 /mod=11 /x87=21 + + + + + fadd + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=0 + Mq + + + aso rexr rexx rexb + d8 /mod=!11 /reg=0 + Md + + + dc /mod=11 /x87=00 + ST0 ST0 + + + dc /mod=11 /x87=01 + ST1 ST0 + + + dc /mod=11 /x87=02 + ST2 ST0 + + + dc /mod=11 /x87=03 + ST3 ST0 + + + dc /mod=11 /x87=04 + ST4 ST0 + + + dc /mod=11 /x87=05 + ST5 ST0 + + + dc /mod=11 /x87=06 + ST6 ST0 + + + dc /mod=11 /x87=07 + ST7 ST0 + + + d8 /mod=11 /x87=00 + ST0 ST0 + + + d8 /mod=11 /x87=01 + ST0 ST1 + + + d8 /mod=11 /x87=02 + ST0 ST2 + + + d8 /mod=11 /x87=03 + ST0 ST3 + + + d8 /mod=11 /x87=04 + ST0 ST4 + + + d8 /mod=11 /x87=05 + ST0 ST5 + + + d8 /mod=11 /x87=06 + ST0 ST6 + + + d8 /mod=11 /x87=07 + ST0 ST7 + + + + + faddp + X87 + + de /mod=11 /x87=00 + ST0 ST0 + + + de /mod=11 /x87=01 + ST1 ST0 + + + de /mod=11 /x87=02 + ST2 ST0 + + + de /mod=11 /x87=03 + ST3 ST0 + + + de /mod=11 /x87=04 + ST4 ST0 + + + de /mod=11 /x87=05 + ST5 ST0 + + + de /mod=11 /x87=06 + ST6 ST0 + + + de /mod=11 /x87=07 + ST7 ST0 + + + + + fbld + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=4 + Mt + + + + + fbstp + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=6 + Mt + + + + + fchs + X87 + + d9 /mod=11 /x87=20 + + + + + fclex + X87 + + db /mod=11 /x87=22 + + + + + fcmovb + X87 + + da /mod=11 /x87=00 + ST0 ST0 + + + da /mod=11 /x87=01 + ST0 ST1 + + + da /mod=11 /x87=02 + ST0 ST2 + + + da /mod=11 /x87=03 + ST0 ST3 + + + da /mod=11 /x87=04 + ST0 ST4 + + + da /mod=11 /x87=05 + ST0 ST5 + + + da /mod=11 /x87=06 + ST0 ST6 + + + da /mod=11 /x87=07 + ST0 ST7 + + + + + fcmove + X87 + + da /mod=11 /x87=08 + ST0 ST0 + + + da /mod=11 /x87=09 + ST0 ST1 + + + da /mod=11 /x87=0a + ST0 ST2 + + + da /mod=11 /x87=0b + ST0 ST3 + + + da /mod=11 /x87=0c + ST0 ST4 + + + da /mod=11 /x87=0d + ST0 ST5 + + + da /mod=11 /x87=0e + ST0 ST6 + + + da /mod=11 /x87=0f + ST0 ST7 + + + + + fcmovbe + X87 + + da /mod=11 /x87=10 + ST0 ST0 + + + da /mod=11 /x87=11 + ST0 ST1 + + + da /mod=11 /x87=12 + ST0 ST2 + + + da /mod=11 /x87=13 + ST0 ST3 + + + da /mod=11 /x87=14 + ST0 ST4 + + + da /mod=11 /x87=15 + ST0 ST5 + + + da /mod=11 /x87=16 + ST0 ST6 + + + da /mod=11 /x87=17 + ST0 ST7 + + + + + fcmovu + X87 + + da /mod=11 /x87=18 + ST0 ST0 + + + da /mod=11 /x87=19 + ST0 ST1 + + + da /mod=11 /x87=1a + ST0 ST2 + + + da /mod=11 /x87=1b + ST0 ST3 + + + da /mod=11 /x87=1c + ST0 ST4 + + + da /mod=11 /x87=1d + ST0 ST5 + + + da /mod=11 /x87=1e + ST0 ST6 + + + da /mod=11 /x87=1f + ST0 ST7 + + + + + fcmovnb + X87 + + db /mod=11 /x87=00 + ST0 ST0 + + + db /mod=11 /x87=01 + ST0 ST1 + + + db /mod=11 /x87=02 + ST0 ST2 + + + db /mod=11 /x87=03 + ST0 ST3 + + + db /mod=11 /x87=04 + ST0 ST4 + + + db /mod=11 /x87=05 + ST0 ST5 + + + db /mod=11 /x87=06 + ST0 ST6 + + + db /mod=11 /x87=07 + ST0 ST7 + + + + + fcmovne + X87 + + db /mod=11 /x87=08 + ST0 ST0 + + + db /mod=11 /x87=09 + ST0 ST1 + + + db /mod=11 /x87=0a + ST0 ST2 + + + db /mod=11 /x87=0b + ST0 ST3 + + + db /mod=11 /x87=0c + ST0 ST4 + + + db /mod=11 /x87=0d + ST0 ST5 + + + db /mod=11 /x87=0e + ST0 ST6 + + + db /mod=11 /x87=0f + ST0 ST7 + + + + + fcmovnbe + X87 + + db /mod=11 /x87=10 + ST0 ST0 + + + db /mod=11 /x87=11 + ST0 ST1 + + + db /mod=11 /x87=12 + ST0 ST2 + + + db /mod=11 /x87=13 + ST0 ST3 + + + db /mod=11 /x87=14 + ST0 ST4 + + + db /mod=11 /x87=15 + ST0 ST5 + + + db /mod=11 /x87=16 + ST0 ST6 + + + db /mod=11 /x87=17 + ST0 ST7 + + + + + fcmovnu + X87 + + db /mod=11 /x87=18 + ST0 ST0 + + + db /mod=11 /x87=19 + ST0 ST1 + + + db /mod=11 /x87=1a + ST0 ST2 + + + db /mod=11 /x87=1b + ST0 ST3 + + + db /mod=11 /x87=1c + ST0 ST4 + + + db /mod=11 /x87=1d + ST0 ST5 + + + db /mod=11 /x87=1e + ST0 ST6 + + + db /mod=11 /x87=1f + ST0 ST7 + + + + + fucomi + X87 + + db /mod=11 /x87=28 + ST0 ST0 + + + db /mod=11 /x87=29 + ST0 ST1 + + + db /mod=11 /x87=2a + ST0 ST2 + + + db /mod=11 /x87=2b + ST0 ST3 + + + db /mod=11 /x87=2c + ST0 ST4 + + + db /mod=11 /x87=2d + ST0 ST5 + + + db /mod=11 /x87=2e + ST0 ST6 + + + db /mod=11 /x87=2f + ST0 ST7 + + + + + fcom + X87 + + aso rexr rexx rexb + d8 /mod=!11 /reg=2 + Md + + + aso rexr rexx rexb + dc /mod=!11 /reg=2 + Mq + + + d8 /mod=11 /x87=10 + ST0 ST0 + + + d8 /mod=11 /x87=11 + ST0 ST1 + + + d8 /mod=11 /x87=12 + ST0 ST2 + + + d8 /mod=11 /x87=13 + ST0 ST3 + + + d8 /mod=11 /x87=14 + ST0 ST4 + + + d8 /mod=11 /x87=15 + ST0 ST5 + + + d8 /mod=11 /x87=16 + ST0 ST6 + + + d8 /mod=11 /x87=17 + ST0 ST7 + + + + + fcom2 + X87 UNDOC + + dc /mod=11 /x87=10 + ST0 + + + dc /mod=11 /x87=11 + ST1 + + + dc /mod=11 /x87=12 + ST2 + + + dc /mod=11 /x87=13 + ST3 + + + dc /mod=11 /x87=14 + ST4 + + + dc /mod=11 /x87=15 + ST5 + + + dc /mod=11 /x87=16 + ST6 + + + dc /mod=11 /x87=17 + ST7 + + + + + fcomp3 + X87 UNDOC + + dc /mod=11 /x87=18 + ST0 + + + dc /mod=11 /x87=19 + ST1 + + + dc /mod=11 /x87=1a + ST2 + + + dc /mod=11 /x87=1b + ST3 + + + dc /mod=11 /x87=1c + ST4 + + + dc /mod=11 /x87=1d + ST5 + + + dc /mod=11 /x87=1e + ST6 + + + dc /mod=11 /x87=1f + ST7 + + + + + fcomi + X87 + + db /mod=11 /x87=30 + ST0 ST0 + + + db /mod=11 /x87=31 + ST0 ST1 + + + db /mod=11 /x87=32 + ST0 ST2 + + + db /mod=11 /x87=33 + ST0 ST3 + + + db /mod=11 /x87=34 + ST0 ST4 + + + db /mod=11 /x87=35 + ST0 ST5 + + + db /mod=11 /x87=36 + ST0 ST6 + + + db /mod=11 /x87=37 + ST0 ST7 + + + + + fucomip + X87 + + df /mod=11 /x87=28 + ST0 ST0 + + + df /mod=11 /x87=29 + ST0 ST1 + + + df /mod=11 /x87=2a + ST0 ST2 + + + df /mod=11 /x87=2b + ST0 ST3 + + + df /mod=11 /x87=2c + ST0 ST4 + + + df /mod=11 /x87=2d + ST0 ST5 + + + df /mod=11 /x87=2e + ST0 ST6 + + + df /mod=11 /x87=2f + ST0 ST7 + + + + + fcomip + X87 + + df /mod=11 /x87=30 + ST0 ST0 + + + df /mod=11 /x87=31 + ST0 ST1 + + + df /mod=11 /x87=32 + ST0 ST2 + + + df /mod=11 /x87=33 + ST0 ST3 + + + df /mod=11 /x87=34 + ST0 ST4 + + + df /mod=11 /x87=35 + ST0 ST5 + + + df /mod=11 /x87=36 + ST0 ST6 + + + df /mod=11 /x87=37 + ST0 ST7 + + + + + fcomp + X87 + + aso rexr rexx rexb + d8 /mod=!11 /reg=3 + Md + + + aso rexr rexx rexb + dc /mod=!11 /reg=3 + Mq + + + d8 /mod=11 /x87=18 + ST0 ST0 + + + d8 /mod=11 /x87=19 + ST0 ST1 + + + d8 /mod=11 /x87=1a + ST0 ST2 + + + d8 /mod=11 /x87=1b + ST0 ST3 + + + d8 /mod=11 /x87=1c + ST0 ST4 + + + d8 /mod=11 /x87=1d + ST0 ST5 + + + d8 /mod=11 /x87=1e + ST0 ST6 + + + d8 /mod=11 /x87=1f + ST0 ST7 + + + + + fcomp5 + X87 UNDOC + + de /mod=11 /x87=10 + ST0 + + + de /mod=11 /x87=11 + ST1 + + + de /mod=11 /x87=12 + ST2 + + + de /mod=11 /x87=13 + ST3 + + + de /mod=11 /x87=14 + ST4 + + + de /mod=11 /x87=15 + ST5 + + + de /mod=11 /x87=16 + ST6 + + + de /mod=11 /x87=17 + ST7 + + + + + fcompp + X87 + + de /mod=11 /x87=19 + + + + + fcos + X87 + + d9 /mod=11 /x87=3f + + + + + fdecstp + X87 + + d9 /mod=11 /x87=36 + + + + + fdiv + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=6 + Mq + + + dc /mod=11 /x87=38 + ST0 ST0 + + + dc /mod=11 /x87=39 + ST1 ST0 + + + dc /mod=11 /x87=3a + ST2 ST0 + + + dc /mod=11 /x87=3b + ST3 ST0 + + + dc /mod=11 /x87=3c + ST4 ST0 + + + dc /mod=11 /x87=3d + ST5 ST0 + + + dc /mod=11 /x87=3e + ST6 ST0 + + + dc /mod=11 /x87=3f + ST7 ST0 + + + aso rexr rexx rexb + d8 /mod=!11 /reg=6 + Md + + + d8 /mod=11 /x87=30 + ST0 ST0 + + + d8 /mod=11 /x87=31 + ST0 ST1 + + + d8 /mod=11 /x87=32 + ST0 ST2 + + + d8 /mod=11 /x87=33 + ST0 ST3 + + + d8 /mod=11 /x87=34 + ST0 ST4 + + + d8 /mod=11 /x87=35 + ST0 ST5 + + + d8 /mod=11 /x87=36 + ST0 ST6 + + + d8 /mod=11 /x87=37 + ST0 ST7 + + + + + fdivp + X87 + + de /mod=11 /x87=38 + ST0 ST0 + + + de /mod=11 /x87=39 + ST1 ST0 + + + de /mod=11 /x87=3a + ST2 ST0 + + + de /mod=11 /x87=3b + ST3 ST0 + + + de /mod=11 /x87=3c + ST4 ST0 + + + de /mod=11 /x87=3d + ST5 ST0 + + + de /mod=11 /x87=3e + ST6 ST0 + + + de /mod=11 /x87=3f + ST7 ST0 + + + + + fdivr + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=7 + Mq + + + dc /mod=11 /x87=30 + ST0 ST0 + + + dc /mod=11 /x87=31 + ST1 ST0 + + + dc /mod=11 /x87=32 + ST2 ST0 + + + dc /mod=11 /x87=33 + ST3 ST0 + + + dc /mod=11 /x87=34 + ST4 ST0 + + + dc /mod=11 /x87=35 + ST5 ST0 + + + dc /mod=11 /x87=36 + ST6 ST0 + + + dc /mod=11 /x87=37 + ST7 ST0 + + + aso rexr rexx rexb + d8 /mod=!11 /reg=7 + Md + + + d8 /mod=11 /x87=38 + ST0 ST0 + + + d8 /mod=11 /x87=39 + ST0 ST1 + + + d8 /mod=11 /x87=3a + ST0 ST2 + + + d8 /mod=11 /x87=3b + ST0 ST3 + + + d8 /mod=11 /x87=3c + ST0 ST4 + + + d8 /mod=11 /x87=3d + ST0 ST5 + + + d8 /mod=11 /x87=3e + ST0 ST6 + + + d8 /mod=11 /x87=3f + ST0 ST7 + + + + + fdivrp + X87 + + de /mod=11 /x87=30 + ST0 ST0 + + + de /mod=11 /x87=31 + ST1 ST0 + + + de /mod=11 /x87=32 + ST2 ST0 + + + de /mod=11 /x87=33 + ST3 ST0 + + + de /mod=11 /x87=34 + ST4 ST0 + + + de /mod=11 /x87=35 + ST5 ST0 + + + de /mod=11 /x87=36 + ST6 ST0 + + + de /mod=11 /x87=37 + ST7 ST0 + + + + + femms + + 0f 0e + + + + + ffree + X87 + + dd /mod=11 /x87=00 + ST0 + + + dd /mod=11 /x87=01 + ST1 + + + dd /mod=11 /x87=02 + ST2 + + + dd /mod=11 /x87=03 + ST3 + + + dd /mod=11 /x87=04 + ST4 + + + dd /mod=11 /x87=05 + ST5 + + + dd /mod=11 /x87=06 + ST6 + + + dd /mod=11 /x87=07 + ST7 + + + + + ffreep + X87 + + df /mod=11 /x87=00 + ST0 + + + df /mod=11 /x87=01 + ST1 + + + df /mod=11 /x87=02 + ST2 + + + df /mod=11 /x87=03 + ST3 + + + df /mod=11 /x87=04 + ST4 + + + df /mod=11 /x87=05 + ST5 + + + df /mod=11 /x87=06 + ST6 + + + df /mod=11 /x87=07 + ST7 + + + + + ficom + X87 + + aso rexr rexx rexb + de /mod=!11 /reg=2 + Mw + + + aso rexr rexx rexb + da /mod=!11 /reg=2 + Md + + + + + ficomp + X87 + + aso rexr rexx rexb + de /mod=!11 /reg=3 + Mw + + + aso rexr rexx rexb + da /mod=!11 /reg=3 + Md + + + + + fild + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=0 + Mw + + + aso rexr rexx rexb + df /mod=!11 /reg=5 + Mq + + + aso rexr rexx rexb + db /mod=!11 /reg=0 + Md + + + + + fncstp + X87 + + d9 /mod=11 /x87=37 + + + + + fninit + X87 + + db /mod=11 /x87=23 + + + + + fiadd + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=0 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=0 + Mw + + + + + fidivr + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=7 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=7 + Mw + + + + + fidiv + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=6 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=6 + Mw + + + + + fisub + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=4 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=4 + Mw + + + + + fisubr + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=5 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=5 + Mw + + + + + fist + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=2 + Mw + + + aso rexr rexx rexb + db /mod=!11 /reg=2 + Md + + + + + fistp + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=3 + Mw + + + aso rexr rexx rexb + df /mod=!11 /reg=7 + Mq + + + aso rexr rexx rexb + db /mod=!11 /reg=3 + Md + + + + + fisttp + X87 + + aso rexr rexx rexb + db /mod=!11 /reg=1 + Md + + + aso rexr rexx rexb + dd /mod=!11 /reg=1 + Mq + + + aso rexr rexx rexb + df /mod=!11 /reg=1 + Mw + + + + + fld + X87 + + aso rexr rexx rexb + db /mod=!11 /reg=5 + Mt + + + aso rexr rexx rexb + dd /mod=!11 /reg=0 + Mq + + + aso rexr rexx rexb + d9 /mod=!11 /reg=0 + Md + + + d9 /mod=11 /x87=00 + ST0 + + + d9 /mod=11 /x87=01 + ST1 + + + d9 /mod=11 /x87=02 + ST2 + + + d9 /mod=11 /x87=03 + ST3 + + + d9 /mod=11 /x87=04 + ST4 + + + d9 /mod=11 /x87=05 + ST5 + + + d9 /mod=11 /x87=06 + ST6 + + + d9 /mod=11 /x87=07 + ST7 + + + + + fld1 + X87 + + d9 /mod=11 /x87=28 + + + + + fldl2t + X87 + + d9 /mod=11 /x87=29 + + + + + fldl2e + X87 + + d9 /mod=11 /x87=2a + + + + + fldlpi + X87 + + d9 /mod=11 /x87=2b + + + + + fldlg2 + X87 + + d9 /mod=11 /x87=2c + + + + + fldln2 + X87 + + d9 /mod=11 /x87=2d + + + + + fldz + X87 + + d9 /mod=11 /x87=2e + + + + + fldcw + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=5 + Mw + + + + + fldenv + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=4 + M + + + + + fmul + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=1 + Mq + + + dc /mod=11 /x87=08 + ST0 ST0 + + + dc /mod=11 /x87=09 + ST1 ST0 + + + dc /mod=11 /x87=0a + ST2 ST0 + + + dc /mod=11 /x87=0b + ST3 ST0 + + + dc /mod=11 /x87=0c + ST4 ST0 + + + dc /mod=11 /x87=0d + ST5 ST0 + + + dc /mod=11 /x87=0e + ST6 ST0 + + + dc /mod=11 /x87=0f + ST7 ST0 + + + aso rexr rexx rexb + d8 /mod=!11 /reg=1 + Md + + + d8 /mod=11 /x87=08 + ST0 ST0 + + + d8 /mod=11 /x87=09 + ST0 ST1 + + + d8 /mod=11 /x87=0a + ST0 ST2 + + + d8 /mod=11 /x87=0b + ST0 ST3 + + + d8 /mod=11 /x87=0c + ST0 ST4 + + + d8 /mod=11 /x87=0d + ST0 ST5 + + + d8 /mod=11 /x87=0e + ST0 ST6 + + + d8 /mod=11 /x87=0f + ST0 ST7 + + + + + fmulp + X87 + + de /mod=11 /x87=08 + ST0 ST0 + + + de /mod=11 /x87=09 + ST1 ST0 + + + de /mod=11 /x87=0a + ST2 ST0 + + + de /mod=11 /x87=0b + ST3 ST0 + + + de /mod=11 /x87=0c + ST4 ST0 + + + de /mod=11 /x87=0d + ST5 ST0 + + + de /mod=11 /x87=0e + ST6 ST0 + + + de /mod=11 /x87=0f + ST7 ST0 + + + + + fimul + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=1 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=1 + Mw + + + + + fnop + X87 + + d9 /mod=11 /x87=10 + + + + + fpatan + X87 + + d9 /mod=11 /x87=33 + + + + + fprem + X87 + + d9 /mod=11 /x87=38 + + + + + fprem1 + X87 + + d9 /mod=11 /x87=35 + + + + + fptan + X87 + + d9 /mod=11 /x87=32 + + + + + frndint + X87 + + d9 /mod=11 /x87=3c + + + + + frstor + X87 + + aso rexr rexx rexb + dd /mod=!11 /reg=4 + M + + + + + fnsave + X87 + + aso rexr rexx rexb + dd /mod=!11 /reg=6 + M + + + + + fscale + X87 + + d9 /mod=11 /x87=3d + + + + + fsin + X87 + + d9 /mod=11 /x87=3e + + + + + fsincos + X87 + + d9 /mod=11 /x87=3b + + + + + fsqrt + X87 + + d9 /mod=11 /x87=3a + + + + + fstp + X87 + + aso rexr rexx rexb + db /mod=!11 /reg=7 + Mt + + + aso rexr rexx rexb + dd /mod=!11 /reg=3 + Mq + + + aso rexr rexx rexb + d9 /mod=!11 /reg=3 + Md + + + dd /mod=11 /x87=18 + ST0 + + + dd /mod=11 /x87=19 + ST1 + + + dd /mod=11 /x87=1a + ST2 + + + dd /mod=11 /x87=1b + ST3 + + + dd /mod=11 /x87=1c + ST4 + + + dd /mod=11 /x87=1d + ST5 + + + dd /mod=11 /x87=1e + ST6 + + + dd /mod=11 /x87=1f + ST7 + + + + + fstp1 + + d9 /mod=11 /x87=18 + ST0 + + + d9 /mod=11 /x87=19 + ST1 + + + d9 /mod=11 /x87=1a + ST2 + + + d9 /mod=11 /x87=1b + ST3 + + + d9 /mod=11 /x87=1c + ST4 + + + d9 /mod=11 /x87=1d + ST5 + + + d9 /mod=11 /x87=1e + ST6 + + + d9 /mod=11 /x87=1f + ST7 + + + + + fstp8 + + df /mod=11 /x87=10 + ST0 + + + df /mod=11 /x87=11 + ST1 + + + df /mod=11 /x87=12 + ST2 + + + df /mod=11 /x87=13 + ST3 + + + df /mod=11 /x87=14 + ST4 + + + df /mod=11 /x87=15 + ST5 + + + df /mod=11 /x87=16 + ST6 + + + df /mod=11 /x87=17 + ST7 + + + + + fstp9 + + df /mod=11 /x87=18 + ST0 + + + df /mod=11 /x87=19 + ST1 + + + df /mod=11 /x87=1a + ST2 + + + df /mod=11 /x87=1b + ST3 + + + df /mod=11 /x87=1c + ST4 + + + df /mod=11 /x87=1d + ST5 + + + df /mod=11 /x87=1e + ST6 + + + df /mod=11 /x87=1f + ST7 + + + + + fst + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=2 + Md + + + aso rexr rexx rexb + dd /mod=!11 /reg=2 + Mq + + + dd /mod=11 /x87=10 + ST0 + + + dd /mod=11 /x87=11 + ST1 + + + dd /mod=11 /x87=12 + ST2 + + + dd /mod=11 /x87=13 + ST3 + + + dd /mod=11 /x87=14 + ST4 + + + dd /mod=11 /x87=15 + ST5 + + + dd /mod=11 /x87=16 + ST6 + + + dd /mod=11 /x87=17 + ST7 + + + + + fnstcw + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=7 + Mw + + + + + fnstenv + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=6 + M + + + + + fnstsw + X87 + + aso rexr rexx rexb + dd /mod=!11 /reg=7 + Mw + + + df /mod=11 /x87=20 + AX + + + + + fsub + X87 + + aso rexr rexx rexb + d8 /mod=!11 /reg=4 + Md + + + aso rexr rexx rexb + dc /mod=!11 /reg=4 + Mq + + + d8 /mod=11 /x87=20 + ST0 ST0 + + + d8 /mod=11 /x87=21 + ST0 ST1 + + + d8 /mod=11 /x87=22 + ST0 ST2 + + + d8 /mod=11 /x87=23 + ST0 ST3 + + + d8 /mod=11 /x87=24 + ST0 ST4 + + + d8 /mod=11 /x87=25 + ST0 ST5 + + + d8 /mod=11 /x87=26 + ST0 ST6 + + + d8 /mod=11 /x87=27 + ST0 ST7 + + + dc /mod=11 /x87=28 + ST0 ST0 + + + dc /mod=11 /x87=29 + ST1 ST0 + + + dc /mod=11 /x87=2a + ST2 ST0 + + + dc /mod=11 /x87=2b + ST3 ST0 + + + dc /mod=11 /x87=2c + ST4 ST0 + + + dc /mod=11 /x87=2d + ST5 ST0 + + + dc /mod=11 /x87=2e + ST6 ST0 + + + dc /mod=11 /x87=2f + ST7 ST0 + + + + + fsubp + X87 + + de /mod=11 /x87=28 + ST0 ST0 + + + de /mod=11 /x87=29 + ST1 ST0 + + + de /mod=11 /x87=2a + ST2 ST0 + + + de /mod=11 /x87=2b + ST3 ST0 + + + de /mod=11 /x87=2c + ST4 ST0 + + + de /mod=11 /x87=2d + ST5 ST0 + + + de /mod=11 /x87=2e + ST6 ST0 + + + de /mod=11 /x87=2f + ST7 ST0 + + + + + fsubr + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=5 + Mq + + + d8 /mod=11 /x87=28 + ST0 ST0 + + + d8 /mod=11 /x87=29 + ST0 ST1 + + + d8 /mod=11 /x87=2a + ST0 ST2 + + + d8 /mod=11 /x87=2b + ST0 ST3 + + + d8 /mod=11 /x87=2c + ST0 ST4 + + + d8 /mod=11 /x87=2d + ST0 ST5 + + + d8 /mod=11 /x87=2e + ST0 ST6 + + + d8 /mod=11 /x87=2f + ST0 ST7 + + + dc /mod=11 /x87=20 + ST0 ST0 + + + dc /mod=11 /x87=21 + ST1 ST0 + + + dc /mod=11 /x87=22 + ST2 ST0 + + + dc /mod=11 /x87=23 + ST3 ST0 + + + dc /mod=11 /x87=24 + ST4 ST0 + + + dc /mod=11 /x87=25 + ST5 ST0 + + + dc /mod=11 /x87=26 + ST6 ST0 + + + dc /mod=11 /x87=27 + ST7 ST0 + + + aso rexr rexx rexb + d8 /mod=!11 /reg=5 + Md + + + + + fsubrp + X87 + + de /mod=11 /x87=20 + ST0 ST0 + + + de /mod=11 /x87=21 + ST1 ST0 + + + de /mod=11 /x87=22 + ST2 ST0 + + + de /mod=11 /x87=23 + ST3 ST0 + + + de /mod=11 /x87=24 + ST4 ST0 + + + de /mod=11 /x87=25 + ST5 ST0 + + + de /mod=11 /x87=26 + ST6 ST0 + + + de /mod=11 /x87=27 + ST7 ST0 + + + + + ftst + X87 + + d9 /mod=11 /x87=24 + + + + + fucom + X87 + + dd /mod=11 /x87=20 + ST0 + + + dd /mod=11 /x87=21 + ST1 + + + dd /mod=11 /x87=22 + ST2 + + + dd /mod=11 /x87=23 + ST3 + + + dd /mod=11 /x87=24 + ST4 + + + dd /mod=11 /x87=25 + ST5 + + + dd /mod=11 /x87=26 + ST6 + + + dd /mod=11 /x87=27 + ST7 + + + + + fucomp + X87 + + dd /mod=11 /x87=28 + ST0 + + + dd /mod=11 /x87=29 + ST1 + + + dd /mod=11 /x87=2a + ST2 + + + dd /mod=11 /x87=2b + ST3 + + + dd /mod=11 /x87=2c + ST4 + + + dd /mod=11 /x87=2d + ST5 + + + dd /mod=11 /x87=2e + ST6 + + + dd /mod=11 /x87=2f + ST7 + + + + + fucompp + X87 + + da /mod=11 /x87=29 + + + + + fxam + X87 + + d9 /mod=11 /x87=25 + + + + + fxch + X87 + + d9 /mod=11 /x87=08 + ST0 ST0 + + + d9 /mod=11 /x87=09 + ST0 ST1 + + + d9 /mod=11 /x87=0a + ST0 ST2 + + + d9 /mod=11 /x87=0b + ST0 ST3 + + + d9 /mod=11 /x87=0c + ST0 ST4 + + + d9 /mod=11 /x87=0d + ST0 ST5 + + + d9 /mod=11 /x87=0e + ST0 ST6 + + + d9 /mod=11 /x87=0f + ST0 ST7 + + + + + fxch4 + X87 + + dd /mod=11 /x87=08 + ST0 + + + dd /mod=11 /x87=09 + ST1 + + + dd /mod=11 /x87=0a + ST2 + + + dd /mod=11 /x87=0b + ST3 + + + dd /mod=11 /x87=0c + ST4 + + + dd /mod=11 /x87=0d + ST5 + + + dd /mod=11 /x87=0e + ST6 + + + dd /mod=11 /x87=0f + ST7 + + + + + fxch7 + X87 + + df /mod=11 /x87=08 + ST0 + + + df /mod=11 /x87=09 + ST1 + + + df /mod=11 /x87=0a + ST2 + + + df /mod=11 /x87=0b + ST3 + + + df /mod=11 /x87=0c + ST4 + + + df /mod=11 /x87=0d + ST5 + + + df /mod=11 /x87=0e + ST6 + + + df /mod=11 /x87=0f + ST7 + + + + + fxrstor + + aso rexw rexr rexx rexb + 0f ae /mod=11 /reg=1 + M + + + + + fxsave + + aso rexw rexr rexx rexb + 0f ae /mod=11 /reg=0 + M + + + + + fpxtract + X87 + + d9 /mod=11 /x87=34 + + + + + fyl2x + X87 + + d9 /mod=11 /x87=31 + + + + + fyl2xp1 + X87 + + d9 /mod=11 /x87=39 + + + + + hlt + + f4 + + + + + idiv + + aso oso rexw rexr rexx rexb + f7 /reg=7 + Ev + + + aso rexw rexr rexx rexb + f6 /reg=7 + Eb + + + + + in + + e4 + AL Ib + + + oso + e5 + eAX Ib + + + ec + AL DX + + + oso + ed + eAX DX + + + + + imul + + aso oso rexw rexr rexx rexb + 0f af + Gv Ev + + + aso rexw rexr rexx rexb + f6 /reg=5 + Eb + + + aso oso rexw rexr rexx rexb + f7 /reg=5 + Ev + + + aso oso rexw rexr rexx rexb + 69 + Gv Ev Iz + sext + + + aso oso rexw rexr rexx rexb + 6b + Gv Ev Ib + sext + + + + + inc + + oso + 40 + eAX + + + oso + 41 + eCX + + + oso + 42 + eDX + + + oso + 43 + eBX + + + oso + 44 + eSP + + + oso + 45 + eBP + + + oso + 46 + eSI + + + oso + 47 + eDI + + + aso oso rexw rexr rexx rexb + ff /reg=0 + Ev + + + aso rexw rexr rexx rexb + fe /reg=0 + Eb + + + + + insb + + 6c + + + + + insw + + oso + 6d /o=16 + + + + + insd + + oso + 6d /o=32 + + + + + int1 + + f1 + + + + + int3 + + cc + + + + + int + + cd + Ib + + + + + into + + ce + inv64 + + + + + invd + + 0f 08 + + + + + invept + intel + + sse66 0f 38 80 /m=32 + Gd Mo + + + sse66 0f 38 80 /m=64 + Gq Mo + + + + + invlpg + + aso rexr rexx rexb + 0f 01 /reg=7 /mod=!11 + M + + + + + invlpga + amd + + 0f 01 /reg=3 /mod=11 /rm=7 + + + + + invvpid + intel + + sse66 0f 38 81 /m=32 + Gd Mo + + + sse66 0f 38 81 /m=64 + Gq Mo + + + + + iretw + + oso rexw + cf /o=16 + + + + + iretd + + oso rexw + cf /o=32 + + + + + iretq + + oso rexw + cf /o=64 + + + + + jo + + 70 + Jb + + + oso + 0f 80 + Jz + def64 depM + + + + + jno + + 71 + Jb + + + oso + 0f 81 + Jz + def64 depM + + + + + jb + + 72 + Jb + + + oso + 0f 82 + Jz + def64 depM + + + + + jae + + 73 + Jb + + + oso + 0f 83 + Jz + def64 depM + + + + + jz + + 74 + Jb + + + oso + 0f 84 + Jz + def64 depM + + + + + jnz + + 75 + Jb + + + oso + 0f 85 + Jz + def64 depM + + + + + jbe + + 76 + Jb + + + oso + 0f 86 + Jz + def64 depM + + + + + ja + + 77 + Jb + + + oso + 0f 87 + Jz + def64 depM + + + + + js + + 78 + Jb + + + oso + 0f 88 + Jz + def64 depM + + + + + jns + + 79 + Jb + + + oso + 0f 89 + Jz + def64 depM + + + + + jp + + 7a + Jb + + + oso + 0f 8a + Jz + def64 depM + + + + + jnp + + 7b + Jb + + + oso + 0f 8b + Jz + def64 depM + + + + + jl + + 7c + Jb + + + oso + 0f 8c + Jz + def64 depM + + + + + jge + + 7d + Jb + + + oso + 0f 8d + Jz + def64 depM + + + + + jle + + 7e + Jb + + + oso + 0f 8e + Jz + def64 depM + + + + + jg + + 7f + Jb + + + oso + 0f 8f + Jz + def64 depM + + + + + jcxz + + aso + e3 /a=16 + Jb + + + + + jecxz + + aso + e3 /a=32 + Jb + + + + + jrcxz + + aso + e3 /a=64 + Jb + + + + + jmp + + aso oso rexw rexr rexx rexb + ff /reg=4 + Ev + def64 depM + + + aso oso rexw rexr rexx rexb + ff /reg=5 + Ep + + + oso + e9 + Jz + def64 depM + cast + + + ea + Ap + inv64 + + + eb + Jb + + + + + lahf + + 9f + + + + + lar + + aso oso rexw rexr rexx rexb + 0f 02 + Gv Ew + + + + + lddqu + + aso rexr rexx rexb + ssef2 0f f0 + V M + + + + + ldmxcsr + + aso rexw rexr rexx rexb + 0f ae /reg=2 /mod=11 + Md + + + + + lds + + aso oso + c5 + Gv M + inv64 + + + + + lea + + aso oso rexw rexr rexx rexb + 8d + Gv M + + + + + les + + aso oso + c4 + Gv M + inv64 + + + + + lfs + + aso oso rexw rexr rexx rexb + 0f b4 + Gz M + + + + + lgs + + aso oso rexw rexr rexx rexb + 0f b5 + Gz M + + + + + lidt + + aso rexr rexx rexb + 0f 01 /reg=3 /mod=!11 + M + + + + + lss + + aso oso rexw rexr rexx rexb + 0f b2 + Gz M + + + + + leave + + c9 + + + + + lfence + + 0f ae /reg=5 /mod=11 /rm=0 + + + 0f ae /reg=5 /mod=11 /rm=1 + + + 0f ae /reg=5 /mod=11 /rm=2 + + + 0f ae /reg=5 /mod=11 /rm=3 + + + 0f ae /reg=5 /mod=11 /rm=4 + + + 0f ae /reg=5 /mod=11 /rm=5 + + + 0f ae /reg=5 /mod=11 /rm=6 + + + 0f ae /reg=5 /mod=11 /rm=7 + + + + + lgdt + + aso rexr rexx rexb + 0f 01 /reg=2 /mod=!11 + M + + + + + lldt + + aso rexr rexx rexb + 0f 00 /reg=2 + Ew + + + + + lmsw + + aso rexr rexx rexb + 0f 01 /reg=6 /mod=!11 + Ew + + + + + lock + + f0 + + + + + lodsb + + seg + ac + + + + + lodsw + + seg oso rexw + ad /o=16 + + + + + lodsd + + seg oso rexw + ad /o=32 + + + + + lodsq + + seg oso rexw + ad /o=64 + + + + + loopnz + + e0 + Jb + + + + + loope + + e1 + Jb + + + + + loop + + e2 + Jb + + + + + lsl + + aso oso rexw rexr rexx rexb + 0f 03 + Gv Ew + + + + + ltr + + aso rexr rexx rexb + 0f 00 /reg=3 + Ew + + + + + maskmovq + + aso rexr rexx rexb + 0f f7 + P PR + + + + + maxpd + + aso rexr rexx rexb + sse66 0f 5f + V W + + + + + maxps + + aso rexr rexx rexb + 0f 5f + V W + + + + + maxsd + + aso rexr rexx rexb + ssef2 0f 5f + V W + + + + + maxss + + aso rexr rexx rexb + ssef3 0f 5f + V W + + + + + mfence + + 0f ae /reg=6 /mod=11 /rm=0 + + + 0f ae /reg=6 /mod=11 /rm=1 + + + 0f ae /reg=6 /mod=11 /rm=2 + + + 0f ae /reg=6 /mod=11 /rm=3 + + + 0f ae /reg=6 /mod=11 /rm=4 + + + 0f ae /reg=6 /mod=11 /rm=5 + + + 0f ae /reg=6 /mod=11 /rm=6 + + + 0f ae /reg=6 /mod=11 /rm=7 + + + + + minpd + + aso rexr rexx rexb + sse66 0f 5d + V W + + + + + minps + + aso rexr rexx rexb + 0f 5d + V W + + + + + minsd + + aso rexr rexx rexb + ssef2 0f 5d + V W + + + + + minss + + aso rexr rexx rexb + ssef3 0f 5d + V W + + + + + monitor + + 0f 01 /reg=1 /mod=11 /rm=0 + + + + + montmul + + 0f a6 /mod=11 /rm=0 /reg=0 + + + + + mov + + aso rexw rexr rexx rexb + c6 /reg=0 + Eb Ib + + + aso oso rexw rexr rexx rexb + c7 /reg=0 + Ev Iz + + + aso rexr rexx rexb + 88 + Eb Gb + + + aso oso rexw rexr rexx rexb + 89 + Ev Gv + + + aso rexr rexx rexb + 8a + Gb Eb + + + aso oso rexw rexr rexx rexb + 8b + Gv Ev + + + aso oso rexr rexx rexb + 8c + Ev S + + + aso oso rexr rexx rexb + 8e + S Ev + + + a0 + AL Ob + + + aso oso rexw + a1 + rAX Ov + + + a2 + Ob AL + + + aso oso rexw + a3 + Ov rAX + + + rexb + b0 + ALr8b Ib + + + rexb + b1 + CLr9b Ib + + + rexb + b2 + DLr10b Ib + + + rexb + b3 + BLr11b Ib + + + rexb + b4 + AHr12b Ib + + + rexb + b5 + CHr13b Ib + + + rexb + b6 + DHr14b Ib + + + rexb + b7 + BHr15b Ib + + + oso rexw rexb + b8 + rAXr8 Iv + + + oso rexw rexb + b9 + rCXr9 Iv + + + oso rexw rexb + ba + rDXr10 Iv + + + oso rexw rexb + bb + rBXr11 Iv + + + oso rexw rexb + bc + rSPr12 Iv + + + oso rexw rexb + bd + rBPr13 Iv + + + oso rexw rexb + be + rSIr14 Iv + + + oso rexw rexb + bf + rDIr15 Iv + + + rexr + 0f 20 + R C + + + rexr + 0f 21 + R D + + + rexr + 0f 22 + C R + + + rexr + 0f 23 + D R + + + + + movapd + + aso rexr rexx rexb + sse66 0f 28 + V W + + + aso rexr rexx rexb + sse66 0f 29 + W V + + + + + movaps + + aso rexr rexx rexb + 0f 28 + V W + + + aso rexr rexx rexb + 0f 29 + W V + + + + + movd + + aso rexw rexr rexx rexb + sse66 0f 6e + V Ex + + + aso rexr rexx rexb + 0f 6e + P Ex + + + aso rexw rexr rexx rexb + sse66 0f 7e + Ex V + + + aso rexr rexx rexb + 0f 7e + Ex P + + + + + movhpd + + aso rexr rexx rexb + sse66 0f 16 /mod=!11 + V M + + + aso rexr rexx rexb + sse66 0f 17 + M V + + + + + movhps + + aso rexr rexx rexb + 0f 16 /mod=!11 + V M + + + aso rexr rexx rexb + 0f 17 + M V + + + + + movlhps + + aso rexr rexx rexb + 0f 16 /mod=11 + V VR + + + + + movlpd + + aso rexr rexx rexb + sse66 0f 12 /mod=!11 + V M + + + aso rexr rexx rexb + sse66 0f 13 + M V + + + + + movlps + + aso rexr rexx rexb + 0f 12 /mod=!11 + V M + + + aso rexr rexx rexb + 0f 13 + M V + + + + + movhlps + + aso rexr rexx rexb + 0f 12 /mod=11 + V VR + + + + + movmskpd + + oso rexr rexb + sse66 0f 50 + Gd VR + + + + + movmskps + + oso rexr rexb + 0f 50 + Gd VR + + + + + movntdq + + aso rexr rexx rexb + sse66 0f e7 + M V + + + + + movnti + + aso rexw rexr rexx rexb + 0f c3 + M Gy + + + + + movntpd + + aso rexr rexx rexb + sse66 0f 2b + M V + + + + + movntps + + aso rexr rexx rexb + 0f 2b + M V + + + + + movntq + + 0f e7 + M P + + + + + movq + + aso rexr rexx rexb + 0f 6f + P Q + + + aso rexr rexx rexb + sse66 0f d6 + W V + + + aso rexr rexx rexb + ssef3 0f 7e + V W + + + aso rexr rexx rexb + 0f 7f + Q P + + + + + movsb + + seg + a4 + + + + + movsw + + seg oso rexw + a5 /o=16 + + + + + movsd + + seg oso rexw + a5 /o=32 + + + aso rexr rexx rexb + ssef2 0f 10 + V W + + + aso rexr rexx rexb + ssef2 0f 11 + W V + + + + + movsq + + seg oso rexw + a5 /o=64 + + + + + movss + + aso rexr rexx rexb + ssef3 0f 10 + V W + + + aso rexr rexx rexb + ssef3 0f 11 + W V + + + + + movsx + + aso oso rexw rexr rexx rexb + 0f be + Gv Eb + + + aso oso rexw rexr rexx rexb + 0f bf + Gv Ew + + + + + movupd + + aso rexr rexx rexb + sse66 0f 10 + V W + + + aso rexr rexx rexb + sse66 0f 11 + W V + + + + + movups + + aso rexr rexx rexb + 0f 10 + V W + + + aso rexr rexx rexb + 0f 11 + W V + + + + + movzx + + aso oso rexw rexr rexx rexb + 0f b6 + Gv Eb + + + aso oso rexw rexr rexx rexb + 0f b7 + Gv Ew + + + + + mul + + aso rexw rexr rexx rexb + f6 /reg=4 + Eb + + + aso oso rexw rexr rexx rexb + f7 /reg=4 + Ev + + + + + mulpd + + aso rexr rexx rexb + sse66 0f 59 + V W + + + + + mulps + + aso rexr rexx rexb + 0f 59 + V W + + + + + mulsd + + aso rexr rexx rexb + ssef2 0f 59 + V W + + + + + mulss + + aso rexr rexx rexb + ssef3 0f 59 + V W + + + + + mwait + + 0f 01 /reg=1 /mod=11 /rm=1 + + + + + neg + + aso rexw rexr rexx rexb + f6 /reg=3 + Eb + + + aso oso rexw rexr rexx rexb + f7 /reg=3 + Ev + + + + + nop + + 90 + + + aso rexr rexx rexb + 0f 19 + M + + + aso rexr rexx rexb + 0f 1a + M + + + aso rexr rexx rexb + 0f 1b + M + + + aso rexr rexx rexb + 0f 1c + M + + + aso rexr rexx rexb + 0f 1d + M + + + aso rexr rexx rexb + 0f 1e + M + + + aso rexr rexx rexb + 0f 1f + M + + + + + not + + aso rexw rexr rexx rexb + f6 /reg=2 + Eb + + + aso oso rexw rexr rexx rexb + f7 /reg=2 + Ev + + + + + or + + aso rexr rexx rexb + 08 + Eb Gb + + + aso oso rexw rexr rexx rexb + 09 + Ev Gv + + + aso rexr rexx rexb + 0a + Gb Eb + + + aso oso rexw rexr rexx rexb + 0b + Gv Ev + + + 0c + AL Ib + + + oso rexw + 0d + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=1 + Eb Ib + + + aso oso rexw rexr rexx rexb + 81 /reg=1 + Ev Iz + sext + + + aso rexr rexx rexb + 82 /reg=1 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 83 /reg=1 + Ev Ib + sext + + + + + orpd + + aso rexr rexx rexb + sse66 0f 56 + V W + + + + + orps + + aso rexr rexx rexb + 0f 56 + V W + + + + + out + + e6 + Ib AL + + + oso + e7 + Ib eAX + + + ee + DX AL + + + oso + ef + DX eAX + + + + + outsb + + 6e + + + + + outsw + + oso + 6f /o=16 + + + + + outsd + + oso + 6f /o=32 + + + + + outsq + + oso + 6f /o=64 + + + + + packsswb + + aso rexr rexx rexb + sse66 0f 63 + V W + + + aso rexr rexx rexb + 0f 63 + P Q + + + + + packssdw + + aso rexr rexx rexb + sse66 0f 6b + V W + + + aso rexr rexx rexb + 0f 6b + P Q + + + + + packuswb + + aso rexr rexx rexb + sse66 0f 67 + V W + + + aso rexr rexx rexb + 0f 67 + P Q + + + + + paddb + + aso rexr rexx rexb + sse66 0f fc + V W + + + aso rexr rexx rexb + 0f fc + P Q + + + + + paddw + + aso rexr rexx rexb + 0f fd + P Q + + + aso rexr rexx rexb + sse66 0f fd + V W + + + + + paddd + + aso rexr rexx rexb + 0f fe + P Q + + + aso rexr rexx rexb + sse66 0f fe + V W + + + + + + paddsb + + aso rexr rexx rexb + 0f ec + P Q + + + aso rexr rexx rexb + sse66 0f ec + V W + + + + + paddsw + + aso rexr rexx rexb + 0f ed + P Q + + + aso rexr rexx rexb + sse66 0f ed + V W + + + + + paddusb + + aso rexr rexx rexb + 0f dc + P Q + + + aso rexr rexx rexb + sse66 0f dc + V W + + + + + paddusw + + aso rexr rexx rexb + 0f dd + P Q + + + aso rexr rexx rexb + sse66 0f dd + V W + + + + + pand + + aso rexr rexx rexb + sse66 0f db + V W + + + aso rexr rexx rexb + 0f db + P Q + + + + + pandn + + aso rexr rexx rexb + sse66 0f df + V W + + + aso rexr rexx rexb + 0f df + P Q + + + + + pavgb + + aso rexr rexx rexb + sse66 0f e0 + V W + + + aso rexr rexx rexb + 0f e0 + P Q + + + + + pavgw + + aso rexr rexx rexb + sse66 0f e3 + V W + + + aso rexr rexx rexb + 0f e3 + P Q + + + + + pcmpeqb + + aso rexr rexx rexb + 0f 74 + P Q + + + aso rexr rexx rexb + sse66 0f 74 + V W + + + + + pcmpeqw + + aso rexr rexx rexb + 0f 75 + P Q + + + aso rexr rexx rexb + sse66 0f 75 + V W + + + + + pcmpeqd + + aso rexr rexx rexb + 0f 76 + P Q + + + aso rexr rexx rexb + sse66 0f 76 + V W + + + + + pcmpgtb + + aso rexr rexx rexb + sse66 0f 64 + V W + + + aso rexr rexx rexb + 0f 64 + P Q + + + + + pcmpgtw + + aso rexr rexx rexb + sse66 0f 65 + V W + + + aso rexr rexx rexb + 0f 65 + P Q + + + + + pcmpgtd + + aso rexr rexx rexb + sse66 0f 66 + V W + + + aso rexr rexx rexb + 0f 66 + P Q + + + + + pextrb + + aso rexr rexb + sse66 0f 3a 14 + MbRv V Ib + def64 + + + + + pextrd + + aso rexr rexw rexb + sse66 0f 3a 16 /o=16 + Ev V Ib + + + aso rexr rexw rexb + sse66 0f 3a 16 /o=32 + Ev V Ib + + + + + pextrq + + aso rexr rexw rexb + sse66 0f 3a 16 /o=64 + Ev V Ib + def64 + + + + + pextrw + + aso rexr rexb + sse66 0f c5 + Gd VR Ib + + + aso oso rexw rexr rexx rexb + 0f c5 + Gd PR Ib + + + + + pinsrw + + aso oso rexw rexr rexx rexb + 0f c4 + P Ew Ib + + + aso rexw rexr rexx rexb + sse66 0f c4 + V Ew Ib + + + + + pmaddwd + + aso rexr rexx rexb + 0f f5 + P Q + + + aso rexr rexx rexb + sse66 0f f5 + V W + + + + + pmaxsw + + aso rexr rexx rexb + sse66 0f ee + V W + + + aso rexr rexx rexb + 0f ee + P Q + + + + + pmaxub + + aso rexr rexx rexb + 0f de + P Q + + + aso rexr rexx rexb + sse66 0f de + V W + + + + + pminsw + + aso rexr rexx rexb + sse66 0f ea + V W + + + aso rexr rexx rexb + 0f ea + P Q + + + + + pminub + + aso rexr rexx rexb + sse66 0f da + V W + + + aso rexr rexx rexb + 0f da + P Q + + + + + pmovmskb + + rexr rexb + sse66 0f d7 + Gd VR + + + oso rexr rexb + 0f d7 + Gd PR + + + + + pmulhuw + + aso rexr rexx rexb + 0f e4 + P Q + + + aso rexr rexx rexb + sse66 0f e4 + V W + + + + + pmulhw + + aso rexr rexx rexb + sse66 0f e5 + V W + + + aso rexr rexx rexb + 0f e5 + P Q + + + + + pmullw + + aso rexr rexx rexb + 0f d5 + P Q + + + aso rexr rexx rexb + sse66 0f d5 + V W + + + + + pop + + 07 + ES + inv64 + + + 17 + SS + inv64 + + + 1f + DS + inv64 + + + 0f a9 + GS + + + 0f a1 + FS + + + oso rexb + 58 + rAXr8 + def64 depM + + + oso rexb + 59 + rCXr9 + def64 depM + + + oso rexb + 5a + rDXr10 + def64 depM + + + oso rexb + 5b + rBXr11 + def64 depM + + + oso rexb + 5c + rSPr12 + def64 depM + + + oso rexb + 5d + rBPr13 + def64 depM + + + oso rexb + 5e + rSIr14 + def64 depM + + + oso rexb + 5f + rDIr15 + def64 depM + + + aso oso rexw rexr rexx rexb + 8f /reg=0 + Ev + def64 depM + + + + + popa + + oso + 61 /o=16 + inv64 + + + + + popad + + oso + 61 /o=32 + inv64 + + + + + popfw + + oso + 9d /m=32 /o=16 + def64 depM + + + oso + 9d /m=16 /o=16 + def64 depM + + + + + popfd + + oso + 9d /m=16 /o=32 + def64 depM + + + oso + 9d /m=32 /o=32 + def64 depM + + + + + popfq + + oso + 9d /m=64 /o=64 + def64 depM + + + + + por + + aso rexr rexx rexb + sse66 0f eb + V W + + + aso rexr rexx rexb + 0f eb + P Q + + + + + prefetch + + aso rexw rexr rexx rexb + 0f 0d /reg=0 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=1 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=2 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=3 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=4 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=5 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=6 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=7 + M + + + + + prefetchnta + + aso rexw rexr rexx rexb + 0f 18 /reg=0 + M + + + + + prefetcht0 + + aso rexw rexr rexx rexb + 0f 18 /reg=1 + M + + + + + prefetcht1 + + aso rexw rexr rexx rexb + 0f 18 /reg=2 + M + + + + + prefetcht2 + + aso rexw rexr rexx rexb + 0f 18 /reg=3 + M + + + + + psadbw + + aso rexr rexx rexb + sse66 0f f6 + V W + + + aso rexr rexx rexb + 0f f6 + P Q + + + + + pshufw + + aso rexr rexx rexb + 0f 70 + P Q Ib + + + + + psllw + + aso rexr rexx rexb + sse66 0f f1 + V W + + + aso rexr rexx rexb + 0f f1 + P Q + + + rexb + sse66 0f 71 /reg=6 + VR Ib + + + 0f 71 /reg=6 + PR Ib + + + + + pslld + + aso rexr rexx rexb + sse66 0f f2 + V W + + + aso rexr rexx rexb + 0f f2 + P Q + + + rexb + sse66 0f 72 /reg=6 + VR Ib + + + 0f 72 /reg=6 + PR Ib + + + + + psllq + + aso rexr rexx rexb + sse66 0f f3 + V W + + + aso rexr rexx rexb + 0f f3 + P Q + + + rexb + sse66 0f 73 /reg=6 + VR Ib + + + 0f 73 /reg=6 + PR Ib + + + + + psraw + + aso rexr rexx rexb + 0f e1 + P Q + + + aso rexr rexx rexb + sse66 0f e1 + V W + + + rexb + sse66 0f 71 /reg=4 + VR Ib + + + 0f 71 /reg=4 + PR Ib + + + + + psrad + + 0f 72 /reg=4 + PR Ib + + + aso rexr rexx rexb + sse66 0f e2 + V W + + + aso rexr rexx rexb + 0f e2 + P Q + + + rexb + sse66 0f 72 /reg=4 + VR Ib + + + + + psrlw + + 0f 71 /reg=2 + PR Ib + + + aso rexr rexx rexb + 0f d1 + P Q + + + aso rexr rexx rexb + sse66 0f d1 + V W + + + rexb + sse66 0f 71 /reg=2 + VR Ib + + + + + psrld + + 0f 72 /reg=2 + PR Ib + + + aso rexr rexx rexb + 0f d2 + P Q + + + aso rexr rexx rexb + sse66 0f d2 + V W + + + rexb + sse66 0f 72 /reg=2 + VR Ib + + + + + psrlq + + 0f 73 /reg=2 + PR Ib + + + aso rexr rexx rexb + 0f d3 + P Q + + + aso rexr rexx rexb + sse66 0f d3 + V W + + + rexb + sse66 0f 73 /reg=2 + VR Ib + + + + + psubb + + aso rexr rexx rexb + sse66 0f f8 + V W + + + aso rexr rexx rexb + 0f f8 + P Q + + + + + psubw + + aso rexr rexx rexb + sse66 0f f9 + V W + + + aso rexr rexx rexb + 0f f9 + P Q + + + + + psubd + + aso rexr rexx rexb + 0f fa + P Q + + + aso rexr rexx rexb + sse66 0f fa + V W + + + + + psubsb + + aso rexr rexx rexb + 0f e8 + P Q + + + aso rexr rexx rexb + sse66 0f e8 + V W + + + + + psubsw + + aso rexr rexx rexb + 0f e9 + P Q + + + aso rexr rexx rexb + sse66 0f e9 + V W + + + + + psubusb + + aso rexr rexx rexb + 0f d8 + P Q + + + aso rexr rexx rexb + sse66 0f d8 + V W + + + + + psubusw + + aso rexr rexx rexb + 0f d9 + P Q + + + aso rexr rexx rexb + sse66 0f d9 + V W + + + + + punpckhbw + + aso rexr rexx rexb + sse66 0f 68 + V W + + + aso rexr rexx rexb + 0f 68 + P Q + + + + + punpckhwd + + aso rexr rexx rexb + sse66 0f 69 + V W + + + aso rexr rexx rexb + 0f 69 + P Q + + + + + punpckhdq + + aso rexr rexx rexb + sse66 0f 6a + V W + + + aso rexr rexx rexb + 0f 6a + P Q + + + + + punpcklbw + + aso rexr rexx rexb + sse66 0f 60 + V W + + + aso rexr rexx rexb + 0f 60 + P Q + + + + + punpcklwd + + aso rexr rexx rexb + sse66 0f 61 + V W + + + aso rexr rexx rexb + 0f 61 + P Q + + + + + punpckldq + + aso rexr rexx rexb + sse66 0f 62 + V W + + + aso rexr rexx rexb + 0f 62 + P Q + + + + + pi2fw + + 0f 0f /3dnow=0c + P Q + + + + + pi2fd + + 0f 0f /3dnow=0d + P Q + + + + + pf2iw + + 0f 0f /3dnow=1c + P Q + + + + + pf2id + + 0f 0f /3dnow=1d + P Q + + + + + pfnacc + + 0f 0f /3dnow=8a + P Q + + + + + pfpnacc + + 0f 0f /3dnow=8e + P Q + + + + + pfcmpge + + 0f 0f /3dnow=90 + P Q + + + + + pfmin + + 0f 0f /3dnow=94 + P Q + + + + + pfrcp + + 0f 0f /3dnow=96 + P Q + + + + + pfrsqrt + + 0f 0f /3dnow=97 + P Q + + + + + pfsub + + 0f 0f /3dnow=9a + P Q + + + + + pfadd + + 0f 0f /3dnow=9e + P Q + + + + + pfcmpgt + + 0f 0f /3dnow=a0 + P Q + + + + + pfmax + + 0f 0f /3dnow=a4 + P Q + + + + + pfrcpit1 + + 0f 0f /3dnow=a6 + P Q + + + + + pfrsqit1 + + 0f 0f /3dnow=a7 + P Q + + + + + pfsubr + + 0f 0f /3dnow=aa + P Q + + + + + pfacc + + 0f 0f /3dnow=ae + P Q + + + + + pfcmpeq + + 0f 0f /3dnow=b0 + P Q + + + + + pfmul + + 0f 0f /3dnow=b4 + P Q + + + + + pfrcpit2 + + 0f 0f /3dnow=b6 + P Q + + + + + pmulhrw + + 0f 0f /3dnow=b7 + P Q + + + + + pswapd + + 0f 0f /3dnow=bb + P Q + + + + + pavgusb + + 0f 0f /3dnow=bf + P Q + + + + + push + + 06 + ES + inv64 + + + 0e + CS + inv64 + + + 16 + SS + inv64 + + + 1e + DS + inv64 + + + 0f a8 + GS + + + 0f a0 + FS + + + oso rexb + 50 + rAXr8 + def64 depM + + + oso rexb + 51 + rCXr9 + def64 depM + + + oso rexb + 52 + rDXr10 + def64 depM + + + oso rexb + 53 + rBXr11 + def64 depM + + + oso rexb + 54 + rSPr12 + def64 depM + + + oso rexb + 55 + rBPr13 + def64 depM + + + oso rexb + 56 + rSIr14 + def64 depM + + + oso rexb + 57 + rDIr15 + def64 depM + + + oso + 68 + Iz + cast + + + aso oso rexw rexr rexx rexb + ff /reg=6 + Ev + def64 + + + 6a + Ib + sext + + + + + pusha + + oso + 60 /o=16 + inv64 + + + + + pushad + + oso + 60 /o=32 + inv64 + + + + + pushfw + + oso + 9c /m=32 /o=16 + def64 + + + oso + 9c /m=16 /o=16 + def64 + + + oso rexw + 9c /m=64 /o=16 + def64 + + + + + pushfd + + oso + 9c /m=16 /o=32 + def64 + + + oso + 9c /m=32 /o=32 + def64 + + + + + pushfq + + oso rexw + 9c /m=64 /o=32 + def64 + + + oso rexw + 9c /m=64 /o=64 + def64 + + + + + pxor + + aso rexr rexx rexb + sse66 0f ef + V W + + + aso rexr rexx rexb + 0f ef + P Q + + + + + rcl + + aso rexw rexr rexx rexb + c0 /reg=2 + Eb Ib + + + aso oso rexw rexr rexx rexb + c1 /reg=2 + Ev Ib + + + aso rexw rexr rexx rexb + d0 /reg=2 + Eb I1 + + + aso rexw rexr rexx rexb + d2 /reg=2 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=2 + Ev CL + cast + + + aso oso rexw rexr rexx rexb + d1 /reg=2 + Ev I1 + + + + + rcr + + aso rexw rexr rexx rexb + d0 /reg=3 + Eb I1 + + + aso oso rexw rexr rexx rexb + c1 /reg=3 + Ev Ib + + + aso rexw rexr rexx rexb + c0 /reg=3 + Eb Ib + + + aso oso rexw rexr rexx rexb + d1 /reg=3 + Ev I1 + + + aso rexw rexr rexx rexb + d2 /reg=3 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=3 + Ev CL + cast + + + + + rol + + aso rexw rexr rexx rexb + c0 /reg=0 + Eb Ib + + + aso rexw rexr rexx rexb + d0 /reg=0 + Eb I1 + + + aso oso rexw rexr rexx rexb + d1 /reg=0 + Ev I1 + + + aso rexw rexr rexx rexb + d2 /reg=0 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=0 + Ev CL + cast + + + aso oso rexw rexr rexx rexb + c1 /reg=0 + Ev Ib + + + + + ror + + aso rexw rexr rexx rexb + d0 /reg=1 + Eb I1 + + + aso rexw rexr rexx rexb + c0 /reg=1 + Eb Ib + + + aso oso rexw rexr rexx rexb + c1 /reg=1 + Ev Ib + + + aso oso rexw rexr rexx rexb + d1 /reg=1 + Ev I1 + + + aso rexw rexr rexx rexb + d2 /reg=1 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=1 + Ev CL + cast + + + + + rcpps + + aso rexr rexx rexb + 0f 53 + V W + + + + + rcpss + + aso rexr rexx rexb + ssef3 0f 53 + V W + + + + + rdmsr + + 0f 32 + + + + + rdpmc + + 0f 33 + + + + + rdtsc + + 0f 31 + + + + + rdtscp + amd + + 0f 01 /reg=7 /mod=11 /rm=1 + + + + + repne + + f2 + + + + + rep + + f3 + + + + + ret + + c2 + Iw + + + c3 + + + + + retf + + ca + Iw + + + cb + + + + + rsm + + 0f aa + + + + + rsqrtps + + aso rexr rexx rexb + 0f 52 + V W + + + + + rsqrtss + + aso rexr rexx rexb + ssef3 0f 52 + V W + + + + + sahf + + 9e + + + + + sal + + + + salc + + d6 + inv64 + + + + + sar + + aso oso rexw rexr rexx rexb + d1 /reg=7 + Ev I1 + + + aso rexw rexr rexx rexb + c0 /reg=7 + Eb Ib + + + aso rexw rexr rexx rexb + d0 /reg=7 + Eb I1 + + + aso oso rexw rexr rexx rexb + c1 /reg=7 + Ev Ib + + + aso rexw rexr rexx rexb + d2 /reg=7 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=7 + Ev CL + cast + + + + + shl + + aso rexw rexr rexx rexb + c0 /reg=6 + Eb Ib + + + aso oso rexw rexr rexx rexb + c1 /reg=6 + Ev Ib + + + aso rexw rexr rexx rexb + d0 /reg=6 + Eb I1 + + + aso rexw rexr rexx rexb + d2 /reg=6 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=6 + Ev CL + cast + + + aso oso rexw rexr rexx rexb + c1 /reg=4 + Ev Ib + + + aso rexr rexx rexb + d2 /reg=4 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d1 /reg=4 + Ev I1 + + + aso rexw rexr rexx rexb + d0 /reg=4 + Eb I1 + + + aso rexw rexr rexx rexb + c0 /reg=4 + Eb Ib + + + aso oso rexw rexr rexx rexb + d3 /reg=4 + Ev CL + + + aso oso rexw rexr rexx rexb + d1 /reg=6 + Ev I1 + + + + + shr + + aso oso rexw rexr rexx rexb + c1 /reg=5 + Ev Ib + + + aso rexw rexr rexx rexb + d2 /reg=5 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d1 /reg=5 + Ev I1 + + + aso rexw rexr rexx rexb + d0 /reg=5 + Eb I1 + + + aso rexw rexr rexx rexb + c0 /reg=5 + Eb Ib + + + aso oso rexw rexr rexx rexb + d3 /reg=5 + Ev CL + cast + + + + + sbb + + aso rexr rexx rexb + 18 + Eb Gb + + + aso oso rexw rexr rexx rexb + 19 + Ev Gv + + + aso rexr rexx rexb + 1a + Gb Eb + + + aso oso rexw rexr rexx rexb + 1b + Gv Ev + + + 1c + AL Ib + + + oso rexw + 1d + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=3 + Eb Ib + + + aso oso rexw rexr rexx rexb + 81 /reg=3 + Ev Iz + sext + + + aso rexr rexx rexb + 82 /reg=3 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 83 /reg=3 + Ev Ib + sext + + + + + scasb + + ae + + + + + scasw + + oso rexw + af /o=16 + + + + + scasd + + oso rexw + af /o=32 + + + + + scasq + + oso rexw + af /o=64 + + + + + seto + + aso rexr rexx rexb + 0f 90 + Eb + + + + + setno + + aso rexr rexx rexb + 0f 91 + Eb + + + + + setb + + aso rexr rexx rexb + 0f 92 + Eb + + + + + setnb + + aso rexr rexx rexb + 0f 93 + Eb + + + + + setz + + aso rexr rexx rexb + 0f 94 + Eb + + + + + setnz + + aso rexr rexx rexb + 0f 95 + Eb + + + + + setbe + + aso rexr rexx rexb + 0f 96 + Eb + + + + + seta + + aso rexr rexx rexb + 0f 97 + Eb + + + + + sets + + aso rexr rexx rexb + 0f 98 + Eb + + + + + setns + + aso rexr rexx rexb + 0f 99 + Eb + + + + + setp + + aso rexr rexx rexb + 0f 9a + Eb + + + + + setnp + + aso rexr rexx rexb + 0f 9b + Eb + + + + + setl + + aso rexr rexx rexb + 0f 9c + Eb + + + + + setge + + aso rexr rexx rexb + 0f 9d + Eb + + + + + setle + + aso rexr rexx rexb + 0f 9e + Eb + + + + + setg + + aso rexr rexx rexb + 0f 9f + Eb + + + + + sfence + + 0f ae /reg=7 /mod=11 /rm=0 + + + 0f ae /reg=7 /mod=11 /rm=1 + + + 0f ae /reg=7 /mod=11 /rm=2 + + + 0f ae /reg=7 /mod=11 /rm=3 + + + 0f ae /reg=7 /mod=11 /rm=4 + + + 0f ae /reg=7 /mod=11 /rm=5 + + + 0f ae /reg=7 /mod=11 /rm=6 + + + 0f ae /reg=7 /mod=11 /rm=7 + + + + + sgdt + + aso rexr rexx rexb + 0f 01 /reg=0 /mod=!11 + M + + + + + shld + + aso oso rexw rexr rexx rexb + 0f a4 + Ev Gv Ib + + + aso oso rexw rexr rexx rexb + 0f a5 + Ev Gv CL + + + + + shrd + + aso oso rexw rexr rexx rexb + 0f ac + Ev Gv Ib + + + aso oso rexw rexr rexx rexb + 0f ad + Ev Gv CL + + + + + shufpd + + aso rexr rexx rexb + sse66 0f c6 + V W Ib + + + + + shufps + + aso rexr rexx rexb + 0f c6 + V W Ib + + + + + sidt + + aso rexr rexx rexb + 0f 01 /reg=1 /mod=!11 + M + + + + + sldt + + aso oso rexr rexx rexb + 0f 00 /reg=0 + MwRv + + + + + smsw + + aso rexr rexx rexb + 0f 01 /reg=4 /mod=!11 + M + + + + + sqrtps + + aso rexr rexx rexb + 0f 51 + V W + + + + + sqrtpd + + aso rexr rexx rexb + sse66 0f 51 + V W + + + + + sqrtsd + + aso rexr rexx rexb + ssef2 0f 51 + V W + + + + + sqrtss + + aso rexr rexx rexb + ssef3 0f 51 + V W + + + + + stc + + f9 + + + + + std + + fd + + + + + stgi + amd + + 0f 01 /reg=3 /mod=11 /rm=4 + + + + + sti + + fb + + + + + skinit + amd + + 0f 01 /reg=3 /mod=11 /rm=6 + + + + + stmxcsr + + aso rexw rexr rexx rexb + 0f ae /mod=11 /reg=3 + Md + + + + + stosb + + seg + aa + + + + + stosw + + seg oso rexw + ab /o=16 + + + + + stosd + + seg oso rexw + ab /o=32 + + + + + stosq + + seg oso rexw + ab /o=64 + + + + + str + + aso oso rexr rexx rexb + 0f 00 /reg=1 + Ev + + + + + sub + + aso rexr rexx rexb + 28 + Eb Gb + + + aso oso rexw rexr rexx rexb + 29 + Ev Gv + + + aso rexr rexx rexb + 2a + Gb Eb + + + aso oso rexw rexr rexx rexb + 2b + Gv Ev + + + 2c + AL Ib + + + oso rexw + 2d + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=5 + Eb Ib + + + aso oso rexw rexr rexx rexb + 81 /reg=5 + Ev Iz + sext + + + aso rexr rexx rexb + 82 /reg=5 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 83 /reg=5 + Ev Ib + sext + + + + + subpd + + aso rexr rexx rexb + sse66 0f 5c + V W + + + + + subps + + aso rexr rexx rexb + 0f 5c + V W + + + + + subsd + + aso rexr rexx rexb + ssef2 0f 5c + V W + + + + + subss + + aso rexr rexx rexb + ssef3 0f 5c + V W + + + + + swapgs + + 0f 01 /reg=7 /mod=11 /rm=0 + + + + + syscall + + 0f 05 + + + + + sysenter + + 0f 34 + inv64 + + + + + sysexit + + 0f 35 + + + + + sysret + + 0f 07 + + + + + test + + aso rexw rexr rexx rexb + f6 /reg=0 + Eb Ib + + + aso rexr rexx rexb + 84 + Eb Gb + + + aso oso rexw rexr rexx rexb + 85 + Ev Gv + + + a8 + AL Ib + + + oso rexw + a9 + rAX Iz + sext + + + aso rexw rexr rexx rexb + f6 /reg=1 + Eb Ib + + + aso oso rexw rexr rexx rexb + f7 /reg=0 + Ev Iz + sext + + + aso oso rexw rexr rexx rexb + f7 /reg=1 + Ev Iz + sext + + + + + ucomisd + + aso rexr rexx rexb + sse66 0f 2e + V W + + + + + ucomiss + + aso rexr rexx rexb + 0f 2e + V W + + + + + ud2 + + 0f 0b + + + + + unpckhpd + + aso rexr rexx rexb + sse66 0f 15 + V W + + + + + unpckhps + + aso rexr rexx rexb + 0f 15 + V W + + + + + unpcklps + + aso rexr rexx rexb + 0f 14 + V W + + + + + unpcklpd + + aso rexr rexx rexb + sse66 0f 14 + V W + + + + + verr + + aso rexr rexx rexb + 0f 00 /reg=4 + Ew + + + + + verw + + aso rexr rexx rexb + 0f 00 /reg=5 + Ew + + + + + vmcall + intel + + 0f 01 /reg=0 /mod=11 /rm=1 + + + + + vmclear + intel + + aso rexr rexx rexb + sse66 0f c7 /reg=6 + Mq + + + + + vmxon + intel + + aso rexr rexx rexb + ssef3 0f c7 /reg=6 + Mq + + + + + vmptrld + intel + + aso rexr rexx rexb + 0f c7 /reg=6 + Mq + + + + + vmptrst + intel + + aso rexr rexx rexb + 0f c7 /reg=7 + Mq + + + + + vmlaunch + intel + + 0f 01 /reg=0 /mod=11 /rm=2 + + + + + vmresume + intel + + 0f 01 /reg=0 /mod=11 /rm=3 + + + + + vmxoff + intel + + 0f 01 /reg=0 /mod=11 /rm=4 + + + + + vmread + intel + + aso rexr rexx rexb + 0f 78 /m=16 + Ed Gd + def64 + + + aso rexr rexx rexb + 0f 78 /m=32 + Ed Gd + def64 + + + aso rexr rexx rexb + 0f 78 /m=64 + Eq Gq + def64 + + + + + vmwrite + intel + + aso rexr rexx rexb + 0f 79 /m=16 + Gd Ed + def64 + + + aso rexr rexx rexb + 0f 79 /m=32 + Gd Ed + def64 + + + aso rexr rexx rexb + 0f 79 /m=64 + Gq Eq + def64 + + + + + vmrun + amd + + 0f 01 /reg=3 /mod=11 /rm=0 + + + + + vmmcall + amd + + 0f 01 /reg=3 /mod=11 /rm=1 + + + + + vmload + amd + + 0f 01 /reg=3 /mod=11 /rm=2 + + + + + vmsave + amd + + 0f 01 /reg=3 /mod=11 /rm=3 + + + + + wait + + 9b + + + + + wbinvd + + 0f 09 + + + + + wrmsr + + 0f 30 + + + + + xadd + + aso oso rexr rexx rexb + 0f c0 + Eb Gb + + + aso oso rexw rexr rexx rexb + 0f c1 + Ev Gv + + + + + xchg + + aso rexr rexx rexb + 86 + Eb Gb + + + aso oso rexw rexr rexx rexb + 87 + Ev Gv + + + oso rexw rexb + 90 + rAXr8 rAX + + + oso rexw rexb + 91 + rCXr9 rAX + + + oso rexw rexb + 92 + rDXr10 rAX + + + oso rexw rexb + 93 + rBXr11 rAX + + + oso rexw rexb + 94 + rSPr12 rAX + + + oso rexw rexb + 95 + rBPr13 rAX + + + oso rexw rexb + 96 + rSIr14 rAX + + + oso rexw rexb + 97 + rDIr15 rAX + + + + + xlatb + + rexw + d7 + + + + + xor + + aso rexr rexx rexb + 30 + Eb Gb + + + aso oso rexw rexr rexx rexb + 31 + Ev Gv + + + aso rexr rexx rexb + 32 + Gb Eb + + + aso oso rexw rexr rexx rexb + 33 + Gv Ev + + + 34 + AL Ib + + + oso rexw + 35 + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=6 + Eb Ib + + + aso oso rexw rexr rexx rexb + 81 /reg=6 + Ev Iz + sext + + + aso rexr rexx rexb + 82 /reg=6 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 83 /reg=6 + Ev Ib + sext + + + + + xorpd + + aso rexr rexx rexb + sse66 0f 57 + V W + + + + + xorps + + aso rexr rexx rexb + 0f 57 + V W + + + + + xcryptecb + + 0f a7 /mod=11 /rm=0 /reg=1 + + + + + xcryptcbc + + 0f a7 /mod=11 /rm=0 /reg=2 + + + + + xcryptctr + + 0f a7 /mod=11 /rm=0 /reg=3 + + + + + xcryptcfb + + 0f a7 /mod=11 /rm=0 /reg=4 + + + + + xcryptofb + + 0f a7 /mod=11 /rm=0 /reg=5 + + + + + xsha1 + + 0f a6 /mod=11 /rm=0 /reg=1 + + + + + xsha256 + + 0f a6 /mod=11 /rm=0 /reg=2 + + + + + xstore + + 0f a7 /mod=11 /rm=0 /reg=0 + + + + + db + + + + + + movdqa + + aso rexr rexx rexb + sse66 0f 7f + W V + + + aso rexr rexx rexb + sse66 0f 6f + V W + + + + + movdq2q + + aso rexb + ssef2 0f d6 + P VR + + + + + movdqu + + aso rexr rexx rexb + ssef3 0f 6f + V W + + + aso rexr rexx rexb + ssef3 0f 7f + W V + + + + + movq2dq + + aso + ssef3 0f d6 + V PR + + + + + paddq + + aso rexr rexx rexb + 0f d4 + P Q + + + aso rexr rexx rexb + sse66 0f d4 + V W + + + + + psubq + + aso rexr rexx rexb + sse66 0f fb + V W + + + aso rexr rexx rexb + 0f fb + P Q + + + + + pmuludq + + aso rexr rexx rexb + 0f f4 + P Q + + + aso rexr rexx rexb + sse66 0f f4 + V W + + + + + pshufhw + + aso rexr rexx rexb + ssef3 0f 70 + V W Ib + + + + + pshuflw + + aso rexr rexx rexb + ssef2 0f 70 + V W Ib + + + + + pshufd + + aso rexr rexx rexb + sse66 0f 70 + V W Ib + + + + + pslldq + + rexb + sse66 0f 73 /reg=7 + VR Ib + + + + + psrldq + + rexb + sse66 0f 73 /reg=3 + VR Ib + + + + + punpckhqdq + + aso rexr rexx rexb + sse66 0f 6d + V W + + + + + punpcklqdq + + aso rexr rexx rexb + sse66 0f 6c + V W + + + + + + + addsubpd + + aso rexr rexx rexb + sse66 0f d0 + V W + + + + + addsubps + + aso rexr rexx rexb + ssef2 0f d0 + V W + + + + + haddpd + + aso rexr rexx rexb + sse66 0f 7c + V W + + + + + haddps + + aso rexr rexx rexb + ssef2 0f 7c + V W + + + + + hsubpd + + aso rexr rexx rexb + sse66 0f 7d + V W + + + + + hsubps + + aso rexr rexx rexb + ssef2 0f 7d + V W + + + + + movddup + + aso rexr rexx rexb + ssef2 0f 12 /mod=11 + V W + + + aso rexr rexx rexb + ssef2 0f 12 /mod=!11 + V W + + + + + movshdup + + aso rexr rexx rexb + ssef3 0f 16 /mod=11 + V W + + + aso rexr rexx rexb + ssef3 0f 16 /mod=!11 + V W + + + + + movsldup + + aso rexr rexx rexb + ssef3 0f 12 /mod=11 + V W + + + aso rexr rexx rexb + ssef3 0f 12 /mod=!11 + V W + + + + + + + pabsb + + aso rexr rexx rexb + 0f 38 1c + P Q + + + aso rexr rexx rexb + sse66 0f 38 1c + V W + + + + + pabsw + + aso rexr rexx rexb + 0f 38 1d + P Q + + + aso rexr rexx rexb + sse66 0f 38 1d + V W + + + + + pabsd + + aso rexr rexx rexb + 0f 38 1e + P Q + + + aso rexr rexx rexb + sse66 0f 38 1e + V W + + + + + psignb + + aso rexr rexx rexb + 0f 38 00 + P Q + + + aso rexr rexx rexb + sse66 0f 38 00 + V W + + + + + phaddw + + aso rexr rexx rexb + 0f 38 01 + P Q + + + aso rexr rexx rexb + sse66 0f 38 01 + V W + + + + + phaddd + + aso rexr rexx rexb + 0f 38 02 + P Q + + + aso rexr rexx rexb + sse66 0f 38 02 + V W + + + + + phaddsw + + aso rexr rexx rexb + 0f 38 03 + P Q + + + aso rexr rexx rexb + sse66 0f 38 03 + V W + + + + + pmaddubsw + + aso rexr rexx rexb + 0f 38 04 + P Q + + + aso rexr rexx rexb + sse66 0f 38 04 + V W + + + + + phsubw + + aso rexr rexx rexb + 0f 38 05 + P Q + + + aso rexr rexx rexb + sse66 0f 38 05 + V W + + + + + phsubd + + aso rexr rexx rexb + 0f 38 06 + P Q + + + aso rexr rexx rexb + sse66 0f 38 06 + V W + + + + + phsubsw + + aso rexr rexx rexb + 0f 38 07 + P Q + + + aso rexr rexx rexb + sse66 0f 38 07 + V W + + + + + psignb + + aso rexr rexx rexb + 0f 38 08 + P Q + + + aso rexr rexx rexb + sse66 0f 38 08 + V W + + + + + psignd + + aso rexr rexx rexb + 0f 38 0a + P Q + + + aso rexr rexx rexb + sse66 0f 38 0a + V W + + + + + psignw + + aso rexr rexx rexb + 0f 38 09 + P Q + + + aso rexr rexx rexb + sse66 0f 38 09 + V W + + + + + pmulhrsw + + aso rexr rexx rexb + 0f 38 0b + P Q + + + aso rexr rexx rexb + sse66 0f 38 0b + V W + + + + + palignr + + aso rexr rexx rexb + 0f 3a 0f + P Q Ib + + + aso rexr rexx rexb + sse66 0f 3a 0f + V W Ib + + + + + + + pblendvb + + aso rexr rexx rexb + sse66 0f 38 10 + V W + + + + + pmuldq + + aso rexr rexx rexb + sse66 0f 38 28 + V W + + + + + pminsb + + aso rexr rexx rexb + sse66 0f 38 38 + V W + + + + + pminsd + + aso rexr rexx rexb + sse66 0f 38 39 + V W + + + + + pminuw + + aso rexr rexx rexb + sse66 0f 38 3a + V W + + + + + pminud + + aso rexr rexx rexb + sse66 0f 38 3b + V W + + + + + pmaxsb + + aso rexr rexx rexb + sse66 0f 38 3c + V W + + + + + pmaxsd + + aso rexr rexx rexb + sse66 0f 38 3d + V W + + + + + pmaxud + + aso rexr rexx rexb + sse66 0f 38 3f + V W + + + + + pmulld + + aso rexr rexx rexb + sse66 0f 38 40 + V W + + + + + phminposuw + + aso rexr rexx rexb + sse66 0f 38 41 + V W + + + + + roundps + + aso rexr rexx rexb + sse66 0f 3a 08 + V W Ib + + + + + roundpd + + aso rexr rexx rexb + sse66 0f 3a 09 + V W Ib + + + + + roundss + + aso rexr rexx rexb + sse66 0f 3a 0a + V W Ib + + + + + roundsd + + aso rexr rexx rexb + sse66 0f 3a 0b + V W Ib + + + + + blendpd + + aso rexr rexx rexb + sse66 0f 3a 0d + V W Ib + + + + + pblendw + + aso rexr rexx rexb + sse66 0f 3a 0e + V W Ib + + + + + blendps + + aso rexr rexx rexb + sse66 0f 3a 0c + V W Ib + + + + + blendvpd + + aso rexr rexx rexb + sse66 0f 38 15 + V W + + + + + blendvps + + aso rexr rexx rexb + sse66 0f 38 14 + V W + + + + + dpps + + aso rexr rexx rexb + sse66 0f 3a 40 + V W Ib + + + + + dppd + + aso rexr rexx rexb + sse66 0f 3a 41 + V W Ib + + + + + mpsadbw + + aso rexr rexx rexb + sse66 0f 3a 42 + V W Ib + + + + + extractps + + aso rexr rexw rexb + sse66 0f 3a 17 + MdRy V Ib + + + + + invalid + + + diff --git a/masm/disassembler/udis86/ud_opcode.py b/masm/disassembler/udis86/ud_opcode.py new file mode 100644 index 0000000000..f301b52461 --- /dev/null +++ b/masm/disassembler/udis86/ud_opcode.py @@ -0,0 +1,235 @@ +# udis86 - scripts/ud_opcode.py +# +# Copyright (c) 2009 Vivek Thampi +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +class UdOpcodeTables: + + TableInfo = { + 'opctbl' : { 'name' : 'UD_TAB__OPC_TABLE', 'size' : 256 }, + '/sse' : { 'name' : 'UD_TAB__OPC_SSE', 'size' : 4 }, + '/reg' : { 'name' : 'UD_TAB__OPC_REG', 'size' : 8 }, + '/rm' : { 'name' : 'UD_TAB__OPC_RM', 'size' : 8 }, + '/mod' : { 'name' : 'UD_TAB__OPC_MOD', 'size' : 2 }, + '/m' : { 'name' : 'UD_TAB__OPC_MODE', 'size' : 3 }, + '/x87' : { 'name' : 'UD_TAB__OPC_X87', 'size' : 64 }, + '/a' : { 'name' : 'UD_TAB__OPC_ASIZE', 'size' : 3 }, + '/o' : { 'name' : 'UD_TAB__OPC_OSIZE', 'size' : 3 }, + '/3dnow' : { 'name' : 'UD_TAB__OPC_3DNOW', 'size' : 256 }, + 'vendor' : { 'name' : 'UD_TAB__OPC_VENDOR', 'size' : 3 }, + } + + OpcodeTable0 = { + 'type' : 'opctbl', + 'entries' : {}, + 'meta' : 'table0' + } + + OpcExtIndex = { + + # ssef2, ssef3, sse66 + 'sse': { + 'none' : '00', + 'f2' : '01', + 'f3' : '02', + '66' : '03' + }, + + # /mod= + 'mod': { + '!11' : '00', + '11' : '01' + }, + + # /m=, /o=, /a= + 'mode': { + '16' : '00', + '32' : '01', + '64' : '02' + }, + + 'vendor' : { + 'amd' : '00', + 'intel' : '01', + 'any' : '02' + } + } + + InsnTable = [] + MnemonicsTable = [] + + ThreeDNowTable = {} + + def sizeOfTable( self, t ): + return self.TableInfo[ t ][ 'size' ] + + def nameOfTable( self, t ): + return self.TableInfo[ t ][ 'name' ] + + # + # Updates a table entry: If the entry doesn't exist + # it will create the entry, otherwise, it will walk + # while validating the path. + # + def updateTable( self, table, index, type, meta ): + if not index in table[ 'entries' ]: + table[ 'entries' ][ index ] = { 'type' : type, 'entries' : {}, 'meta' : meta } + if table[ 'entries' ][ index ][ 'type' ] != type: + raise NameError( "error: violation in opcode mapping (overwrite) %s with %s." % + ( table[ 'entries' ][ index ][ 'type' ], type) ) + return table[ 'entries' ][ index ] + + class Insn: + """An abstract type representing an instruction in the opcode map. + """ + + # A mapping of opcode extensions to their representational + # values used in the opcode map. + OpcExtMap = { + '/rm' : lambda v: "%02x" % int(v, 16), + '/x87' : lambda v: "%02x" % int(v, 16), + '/3dnow' : lambda v: "%02x" % int(v, 16), + '/reg' : lambda v: "%02x" % int(v, 16), + # modrm.mod + # (!11, 11) => (00, 01) + '/mod' : lambda v: '00' if v == '!11' else '01', + # Mode extensions: + # (16, 32, 64) => (00, 01, 02) + '/o' : lambda v: "%02x" % (int(v) / 32), + '/a' : lambda v: "%02x" % (int(v) / 32), + '/m' : lambda v: "%02x" % (int(v) / 32), + '/sse' : lambda v: UdOpcodeTables.OpcExtIndex['sse'][v] + } + + def __init__(self, prefixes, mnemonic, opcodes, operands, vendor): + self.opcodes = opcodes + self.prefixes = prefixes + self.mnemonic = mnemonic + self.operands = operands + self.vendor = vendor + self.opcext = {} + + ssePrefix = None + if self.opcodes[0] in ('ssef2', 'ssef3', 'sse66'): + ssePrefix = self.opcodes[0][3:] + self.opcodes.pop(0) + + # do some preliminary decoding of the instruction type + # 1byte, 2byte or 3byte instruction? + self.nByteInsn = 1 + if self.opcodes[0] == '0f': # 2byte + # 2+ byte opcodes are always disambiguated by an + # sse prefix, unless it is a 3d now instruction + # which is 0f 0f ... + if self.opcodes[1] != '0f' and ssePrefix is None: + ssePrefix = 'none' + if self.opcodes[1] in ('38', '3a'): # 3byte + self.nByteInsn = 3 + else: + self.nByteInsn = 2 + + # The opcode that indexes into the opcode table. + self.opcode = self.opcodes[self.nByteInsn - 1] + + # Record opcode extensions + for opcode in self.opcodes[self.nByteInsn:]: + arg, val = opcode.split('=') + self.opcext[arg] = self.OpcExtMap[arg](val) + + # Record sse extension: the reason sse extension is handled + # separately is that historically sse was handled as a first + # class opcode, not as an extension. Now that sse is handled + # as an extension, we do the manual conversion here, as opposed + # to modifying the opcode xml file. + if ssePrefix is not None: + self.opcext['/sse'] = self.OpcExtMap['/sse'](ssePrefix) + + def parse(self, table, insn): + index = insn.opcodes[0]; + if insn.nByteInsn > 1: + assert index == '0f' + table = self.updateTable(table, index, 'opctbl', '0f') + index = insn.opcodes[1] + + if insn.nByteInsn == 3: + table = self.updateTable(table, index, 'opctbl', index) + index = insn.opcodes[2] + + # Walk down the tree, create levels as needed, for opcode + # extensions. The order is important, and determines how + # well the opcode table is packed. Also note, /sse must be + # before /o, because /sse may consume operand size prefix + # affect the outcome of /o. + for ext in ('/mod', '/x87', '/reg', '/rm', '/sse', + '/o', '/a', '/m', '/3dnow'): + if ext in insn.opcext: + table = self.updateTable(table, index, ext, ext) + index = insn.opcext[ext] + + # additional table for disambiguating vendor + if len(insn.vendor): + table = self.updateTable(table, index, 'vendor', insn.vendor) + index = self.OpcExtIndex['vendor'][insn.vendor] + + # make leaf node entries + leaf = self.updateTable(table, index, 'insn', '') + + leaf['mnemonic'] = insn.mnemonic + leaf['prefixes'] = insn.prefixes + leaf['operands'] = insn.operands + + # add instruction to linear table of instruction forms + self.InsnTable.append({ 'prefixes' : insn.prefixes, + 'mnemonic' : insn.mnemonic, + 'operands' : insn.operands }) + + # add mnemonic to mnemonic table + if not insn.mnemonic in self.MnemonicsTable: + self.MnemonicsTable.append(insn.mnemonic) + + + # Adds an instruction definition to the opcode tables + def addInsnDef( self, prefixes, mnemonic, opcodes, operands, vendor ): + insn = self.Insn(prefixes=prefixes, + mnemonic=mnemonic, + opcodes=opcodes, + operands=operands, + vendor=vendor) + self.parse(self.OpcodeTable0, insn) + + def print_table( self, table, pfxs ): + print "%s |" % pfxs + keys = table[ 'entries' ].keys() + if ( len( keys ) ): + keys.sort() + for idx in keys: + e = table[ 'entries' ][ idx ] + if e[ 'type' ] == 'insn': + print "%s |-<%s>" % ( pfxs, idx ), + print "%s %s" % ( e[ 'mnemonic' ], ' '.join( e[ 'operands'] ) ) + else: + print "%s |-<%s> %s" % ( pfxs, idx, e['type'] ) + self.print_table( e, pfxs + ' |' ) + + def print_tree( self ): + self.print_table( self.OpcodeTable0, '' ) diff --git a/masm/disassembler/udis86/ud_optable.py b/masm/disassembler/udis86/ud_optable.py new file mode 100644 index 0000000000..5b5c55d3b8 --- /dev/null +++ b/masm/disassembler/udis86/ud_optable.py @@ -0,0 +1,103 @@ +# udis86 - scripts/ud_optable.py (optable.xml parser) +# +# Copyright (c) 2009 Vivek Thampi +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import os +import sys +from xml.dom import minidom + +class UdOptableXmlParser: + + def parseDef( self, node ): + ven = '' + pfx = [] + opc = [] + opr = [] + for def_node in node.childNodes: + if not def_node.localName: + continue + if def_node.localName == 'pfx': + pfx = def_node.firstChild.data.split(); + elif def_node.localName == 'opc': + opc = def_node.firstChild.data.split(); + elif def_node.localName == 'opr': + opr = def_node.firstChild.data.split(); + elif def_node.localName == 'mode': + pfx.extend( def_node.firstChild.data.split() ); + elif def_node.localName == 'syn': + pfx.extend( def_node.firstChild.data.split() ); + elif def_node.localName == 'vendor': + ven = ( def_node.firstChild.data ); + else: + print "warning: invalid node - %s" % def_node.localName + continue + return ( pfx, opc, opr, ven ) + + def parse( self, xml, fn ): + xmlDoc = minidom.parse( xml ) + self.TlNode = xmlDoc.firstChild + + while self.TlNode and self.TlNode.localName != "x86optable": + self.TlNode = self.TlNode.nextSibling + + for insnNode in self.TlNode.childNodes: + if not insnNode.localName: + continue + if insnNode.localName != "instruction": + print "warning: invalid insn node - %s" % insnNode.localName + continue + + mnemonic = insnNode.getElementsByTagName( 'mnemonic' )[ 0 ].firstChild.data + vendor = '' + + for node in insnNode.childNodes: + if node.localName == 'vendor': + vendor = node.firstChild.data + elif node.localName == 'def': + ( prefixes, opcodes, operands, local_vendor ) = \ + self.parseDef( node ) + if ( len( local_vendor ) ): + vendor = local_vendor + # callback + fn( prefixes, mnemonic, opcodes, operands, vendor ) + + +def printFn( pfx, mnm, opc, opr, ven ): + print 'def: ', + if len( pfx ): + print ' '.join( pfx ), + print "%s %s %s %s" % \ + ( mnm, ' '.join( opc ), ' '.join( opr ), ven ) + + +def parse( xml, callback ): + parser = UdOptableXmlParser() + parser.parse( xml, callback ) + +def main(): + parser = UdOptableXmlParser() + parser.parse( sys.argv[ 1 ], printFn ) + +if __name__ == "__main__": + main() diff --git a/masm/disassembler/udis86/udis86.c b/masm/disassembler/udis86/udis86.c new file mode 100644 index 0000000000..fbf76707a0 --- /dev/null +++ b/masm/disassembler/udis86/udis86.c @@ -0,0 +1,183 @@ +/* udis86 - libudis86/udis86.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_input.h" +#include "udis86_extern.h" + +#ifndef __UD_STANDALONE__ +# include +# include +#endif /* __UD_STANDALONE__ */ + +/* ============================================================================= + * ud_init() - Initializes ud_t object. + * ============================================================================= + */ +extern void +ud_init(struct ud* u) +{ + memset((void*)u, 0, sizeof(struct ud)); + ud_set_mode(u, 16); + u->mnemonic = UD_Iinvalid; + ud_set_pc(u, 0); +#ifndef __UD_STANDALONE__ + ud_set_input_file(u, stdin); +#endif /* __UD_STANDALONE__ */ +} + +/* ============================================================================= + * ud_disassemble() - disassembles one instruction and returns the number of + * bytes disassembled. A zero means end of disassembly. + * ============================================================================= + */ +extern unsigned int +ud_disassemble(struct ud* u) +{ + if (ud_input_end(u)) + return 0; + + + u->insn_buffer[0] = u->insn_hexcode[0] = 0; + + + if (ud_decode(u) == 0) + return 0; + if (u->translator) + u->translator(u); + return ud_insn_len(u); +} + +/* ============================================================================= + * ud_set_mode() - Set Disassemly Mode. + * ============================================================================= + */ +extern void +ud_set_mode(struct ud* u, uint8_t m) +{ + switch(m) { + case 16: + case 32: + case 64: u->dis_mode = m ; return; + default: u->dis_mode = 16; return; + } +} + +/* ============================================================================= + * ud_set_vendor() - Set vendor. + * ============================================================================= + */ +extern void +ud_set_vendor(struct ud* u, unsigned v) +{ + switch(v) { + case UD_VENDOR_INTEL: + u->vendor = v; + break; + case UD_VENDOR_ANY: + u->vendor = v; + break; + default: + u->vendor = UD_VENDOR_AMD; + } +} + +/* ============================================================================= + * ud_set_pc() - Sets code origin. + * ============================================================================= + */ +extern void +ud_set_pc(struct ud* u, uint64_t o) +{ + u->pc = o; +} + +/* ============================================================================= + * ud_set_syntax() - Sets the output syntax. + * ============================================================================= + */ +extern void +ud_set_syntax(struct ud* u, void (*t)(struct ud*)) +{ + u->translator = t; +} + +/* ============================================================================= + * ud_insn() - returns the disassembled instruction + * ============================================================================= + */ +extern char* +ud_insn_asm(struct ud* u) +{ + return u->insn_buffer; +} + +/* ============================================================================= + * ud_insn_offset() - Returns the offset. + * ============================================================================= + */ +extern uint64_t +ud_insn_off(struct ud* u) +{ + return u->insn_offset; +} + + +/* ============================================================================= + * ud_insn_hex() - Returns hex form of disassembled instruction. + * ============================================================================= + */ +extern char* +ud_insn_hex(struct ud* u) +{ + return u->insn_hexcode; +} + +/* ============================================================================= + * ud_insn_ptr() - Returns code disassembled. + * ============================================================================= + */ +extern uint8_t* +ud_insn_ptr(struct ud* u) +{ + return u->inp_sess; +} + +/* ============================================================================= + * ud_insn_len() - Returns the count of bytes disassembled. + * ============================================================================= + */ +extern unsigned int +ud_insn_len(struct ud* u) +{ + return u->inp_ctr; +} + +#endif // USE(UDIS86) diff --git a/masm/disassembler/udis86/udis86.h b/masm/disassembler/udis86/udis86.h new file mode 100644 index 0000000000..baaf495e04 --- /dev/null +++ b/masm/disassembler/udis86/udis86.h @@ -0,0 +1,33 @@ +/* udis86 - udis86.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UDIS86_H +#define UDIS86_H + +#include "udis86_types.h" +#include "udis86_extern.h" +#include "udis86_itab.h" + +#endif diff --git a/masm/disassembler/udis86/udis86_decode.c b/masm/disassembler/udis86/udis86_decode.c new file mode 100644 index 0000000000..a3fd576655 --- /dev/null +++ b/masm/disassembler/udis86/udis86_decode.c @@ -0,0 +1,1142 @@ +/* udis86 - libudis86/decode.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_extern.h" +#include "udis86_types.h" +#include "udis86_input.h" +#include "udis86_decode.h" +#include + +#define dbg(x, n...) +/* #define dbg printf */ + +#ifndef __UD_STANDALONE__ +# include +#endif /* __UD_STANDALONE__ */ + +/* The max number of prefixes to an instruction */ +#define MAX_PREFIXES 15 + +/* instruction aliases and special cases */ +static struct ud_itab_entry s_ie__invalid = + { UD_Iinvalid, O_NONE, O_NONE, O_NONE, P_none }; + +static int +decode_ext(struct ud *u, uint16_t ptr); + + +static inline int +eff_opr_mode(int dis_mode, int rex_w, int pfx_opr) +{ + if (dis_mode == 64) { + return rex_w ? 64 : (pfx_opr ? 16 : 32); + } else if (dis_mode == 32) { + return pfx_opr ? 16 : 32; + } else { + ASSERT(dis_mode == 16); + return pfx_opr ? 32 : 16; + } +} + + +static inline int +eff_adr_mode(int dis_mode, int pfx_adr) +{ + if (dis_mode == 64) { + return pfx_adr ? 32 : 64; + } else if (dis_mode == 32) { + return pfx_adr ? 16 : 32; + } else { + ASSERT(dis_mode == 16); + return pfx_adr ? 32 : 16; + } +} + + +/* Looks up mnemonic code in the mnemonic string table + * Returns NULL if the mnemonic code is invalid + */ +const char * ud_lookup_mnemonic( enum ud_mnemonic_code c ) +{ + return ud_mnemonics_str[ c ]; +} + + +/* + * decode_prefixes + * + * Extracts instruction prefixes. + */ +static int +decode_prefixes(struct ud *u) +{ + unsigned int have_pfx = 1; + unsigned int i; + uint8_t curr; + + /* if in error state, bail out */ + if ( u->error ) + return -1; + + /* keep going as long as there are prefixes available */ + for ( i = 0; have_pfx ; ++i ) { + + /* Get next byte. */ + ud_inp_next(u); + if ( u->error ) + return -1; + curr = ud_inp_curr( u ); + + /* rex prefixes in 64bit mode */ + if ( u->dis_mode == 64 && ( curr & 0xF0 ) == 0x40 ) { + u->pfx_rex = curr; + } else { + switch ( curr ) + { + case 0x2E : + u->pfx_seg = UD_R_CS; + u->pfx_rex = 0; + break; + case 0x36 : + u->pfx_seg = UD_R_SS; + u->pfx_rex = 0; + break; + case 0x3E : + u->pfx_seg = UD_R_DS; + u->pfx_rex = 0; + break; + case 0x26 : + u->pfx_seg = UD_R_ES; + u->pfx_rex = 0; + break; + case 0x64 : + u->pfx_seg = UD_R_FS; + u->pfx_rex = 0; + break; + case 0x65 : + u->pfx_seg = UD_R_GS; + u->pfx_rex = 0; + break; + case 0x67 : /* adress-size override prefix */ + u->pfx_adr = 0x67; + u->pfx_rex = 0; + break; + case 0xF0 : + u->pfx_lock = 0xF0; + u->pfx_rex = 0; + break; + case 0x66: + /* the 0x66 sse prefix is only effective if no other sse prefix + * has already been specified. + */ + if ( !u->pfx_insn ) u->pfx_insn = 0x66; + u->pfx_opr = 0x66; + u->pfx_rex = 0; + break; + case 0xF2: + u->pfx_insn = 0xF2; + u->pfx_repne = 0xF2; + u->pfx_rex = 0; + break; + case 0xF3: + u->pfx_insn = 0xF3; + u->pfx_rep = 0xF3; + u->pfx_repe = 0xF3; + u->pfx_rex = 0; + break; + default : + /* No more prefixes */ + have_pfx = 0; + break; + } + } + + /* check if we reached max instruction length */ + if ( i + 1 == MAX_INSN_LENGTH ) { + u->error = 1; + break; + } + } + + /* return status */ + if ( u->error ) + return -1; + + /* rewind back one byte in stream, since the above loop + * stops with a non-prefix byte. + */ + ud_inp_back(u); + return 0; +} + + +static inline unsigned int modrm( struct ud * u ) +{ + if ( !u->have_modrm ) { + u->modrm = ud_inp_next( u ); + u->have_modrm = 1; + } + return u->modrm; +} + + +static unsigned int resolve_operand_size( const struct ud * u, unsigned int s ) +{ + switch ( s ) + { + case SZ_V: + return ( u->opr_mode ); + case SZ_Z: + return ( u->opr_mode == 16 ) ? 16 : 32; + case SZ_P: + return ( u->opr_mode == 16 ) ? SZ_WP : SZ_DP; + case SZ_MDQ: + return ( u->opr_mode == 16 ) ? 32 : u->opr_mode; + case SZ_RDQ: + return ( u->dis_mode == 64 ) ? 64 : 32; + default: + return s; + } +} + + +static int resolve_mnemonic( struct ud* u ) +{ + /* far/near flags */ + u->br_far = 0; + u->br_near = 0; + /* readjust operand sizes for call/jmp instrcutions */ + if ( u->mnemonic == UD_Icall || u->mnemonic == UD_Ijmp ) { + /* WP: 16:16 pointer */ + if ( u->operand[ 0 ].size == SZ_WP ) { + u->operand[ 0 ].size = 16; + u->br_far = 1; + u->br_near= 0; + /* DP: 32:32 pointer */ + } else if ( u->operand[ 0 ].size == SZ_DP ) { + u->operand[ 0 ].size = 32; + u->br_far = 1; + u->br_near= 0; + } else { + u->br_far = 0; + u->br_near= 1; + } + /* resolve 3dnow weirdness. */ + } else if ( u->mnemonic == UD_I3dnow ) { + u->mnemonic = ud_itab[ u->le->table[ ud_inp_curr( u ) ] ].mnemonic; + } + /* SWAPGS is only valid in 64bits mode */ + if ( u->mnemonic == UD_Iswapgs && u->dis_mode != 64 ) { + u->error = 1; + return -1; + } + + if (u->mnemonic == UD_Ixchg) { + if ((u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_AX && + u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_AX) || + (u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_EAX && + u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_EAX)) { + u->operand[0].type = UD_NONE; + u->operand[1].type = UD_NONE; + u->mnemonic = UD_Inop; + } + } + + if (u->mnemonic == UD_Inop && u->pfx_rep) { + u->pfx_rep = 0; + u->mnemonic = UD_Ipause; + } + return 0; +} + + +/* ----------------------------------------------------------------------------- + * decode_a()- Decodes operands of the type seg:offset + * ----------------------------------------------------------------------------- + */ +static void +decode_a(struct ud* u, struct ud_operand *op) +{ + if (u->opr_mode == 16) { + /* seg16:off16 */ + op->type = UD_OP_PTR; + op->size = 32; + op->lval.ptr.off = ud_inp_uint16(u); + op->lval.ptr.seg = ud_inp_uint16(u); + } else { + /* seg16:off32 */ + op->type = UD_OP_PTR; + op->size = 48; + op->lval.ptr.off = ud_inp_uint32(u); + op->lval.ptr.seg = ud_inp_uint16(u); + } +} + +/* ----------------------------------------------------------------------------- + * decode_gpr() - Returns decoded General Purpose Register + * ----------------------------------------------------------------------------- + */ +static enum ud_type +decode_gpr(register struct ud* u, unsigned int s, unsigned char rm) +{ + s = resolve_operand_size(u, s); + + switch (s) { + case 64: + return UD_R_RAX + rm; + case SZ_DP: + case 32: + return UD_R_EAX + rm; + case SZ_WP: + case 16: + return UD_R_AX + rm; + case 8: + if (u->dis_mode == 64 && u->pfx_rex) { + if (rm >= 4) + return UD_R_SPL + (rm-4); + return UD_R_AL + rm; + } else return UD_R_AL + rm; + default: + return 0; + } +} + +/* ----------------------------------------------------------------------------- + * resolve_gpr64() - 64bit General Purpose Register-Selection. + * ----------------------------------------------------------------------------- + */ +static enum ud_type +resolve_gpr64(struct ud* u, enum ud_operand_code gpr_op, enum ud_operand_size * size) +{ + if (gpr_op >= OP_rAXr8 && gpr_op <= OP_rDIr15) + gpr_op = (gpr_op - OP_rAXr8) | (REX_B(u->pfx_rex) << 3); + else gpr_op = (gpr_op - OP_rAX); + + if (u->opr_mode == 16) { + *size = 16; + return gpr_op + UD_R_AX; + } + if (u->dis_mode == 32 || + (u->opr_mode == 32 && ! (REX_W(u->pfx_rex) || u->default64))) { + *size = 32; + return gpr_op + UD_R_EAX; + } + + *size = 64; + return gpr_op + UD_R_RAX; +} + +/* ----------------------------------------------------------------------------- + * resolve_gpr32 () - 32bit General Purpose Register-Selection. + * ----------------------------------------------------------------------------- + */ +static enum ud_type +resolve_gpr32(struct ud* u, enum ud_operand_code gpr_op) +{ + gpr_op = gpr_op - OP_eAX; + + if (u->opr_mode == 16) + return gpr_op + UD_R_AX; + + return gpr_op + UD_R_EAX; +} + +/* ----------------------------------------------------------------------------- + * resolve_reg() - Resolves the register type + * ----------------------------------------------------------------------------- + */ +static enum ud_type +resolve_reg(struct ud* u, unsigned int type, unsigned char i) +{ + switch (type) { + case T_MMX : return UD_R_MM0 + (i & 7); + case T_XMM : return UD_R_XMM0 + i; + case T_CRG : return UD_R_CR0 + i; + case T_DBG : return UD_R_DR0 + i; + case T_SEG : { + /* + * Only 6 segment registers, anything else is an error. + */ + if ((i & 7) > 5) { + u->error = 1; + } else { + return UD_R_ES + (i & 7); + } + } + case T_NONE: + default: return UD_NONE; + } +} + +/* ----------------------------------------------------------------------------- + * decode_imm() - Decodes Immediate values. + * ----------------------------------------------------------------------------- + */ +static void +decode_imm(struct ud* u, unsigned int s, struct ud_operand *op) +{ + op->size = resolve_operand_size(u, s); + op->type = UD_OP_IMM; + + switch (op->size) { + case 8: op->lval.sbyte = ud_inp_uint8(u); break; + case 16: op->lval.uword = ud_inp_uint16(u); break; + case 32: op->lval.udword = ud_inp_uint32(u); break; + case 64: op->lval.uqword = ud_inp_uint64(u); break; + default: return; + } +} + + +/* + * decode_modrm_reg + * + * Decodes reg field of mod/rm byte + * + */ +static void +decode_modrm_reg(struct ud *u, + struct ud_operand *operand, + unsigned int type, + unsigned int size) +{ + uint8_t reg = (REX_R(u->pfx_rex) << 3) | MODRM_REG(modrm(u)); + operand->type = UD_OP_REG; + operand->size = resolve_operand_size(u, size); + + if (type == T_GPR) { + operand->base = decode_gpr(u, operand->size, reg); + } else { + operand->base = resolve_reg(u, type, reg); + } +} + + +/* + * decode_modrm_rm + * + * Decodes rm field of mod/rm byte + * + */ +static void +decode_modrm_rm(struct ud *u, + struct ud_operand *op, + unsigned char type, + unsigned int size) + +{ + unsigned char mod, rm, reg; + + /* get mod, r/m and reg fields */ + mod = MODRM_MOD(modrm(u)); + rm = (REX_B(u->pfx_rex) << 3) | MODRM_RM(modrm(u)); + reg = (REX_R(u->pfx_rex) << 3) | MODRM_REG(modrm(u)); + + op->size = resolve_operand_size(u, size); + + /* + * If mod is 11b, then the modrm.rm specifies a register. + * + */ + if (mod == 3) { + op->type = UD_OP_REG; + if (type == T_GPR) { + op->base = decode_gpr(u, op->size, rm); + } else { + op->base = resolve_reg(u, type, (REX_B(u->pfx_rex) << 3) | (rm & 7)); + } + return; + } + + + /* + * !11 => Memory Address + */ + op->type = UD_OP_MEM; + + if (u->adr_mode == 64) { + op->base = UD_R_RAX + rm; + if (mod == 1) { + op->offset = 8; + } else if (mod == 2) { + op->offset = 32; + } else if (mod == 0 && (rm & 7) == 5) { + op->base = UD_R_RIP; + op->offset = 32; + } else { + op->offset = 0; + } + /* + * Scale-Index-Base (SIB) + */ + if ((rm & 7) == 4) { + ud_inp_next(u); + + op->scale = (1 << SIB_S(ud_inp_curr(u))) & ~1; + op->index = UD_R_RAX + (SIB_I(ud_inp_curr(u)) | (REX_X(u->pfx_rex) << 3)); + op->base = UD_R_RAX + (SIB_B(ud_inp_curr(u)) | (REX_B(u->pfx_rex) << 3)); + + /* special conditions for base reference */ + if (op->index == UD_R_RSP) { + op->index = UD_NONE; + op->scale = UD_NONE; + } + + if (op->base == UD_R_RBP || op->base == UD_R_R13) { + if (mod == 0) { + op->base = UD_NONE; + } + if (mod == 1) { + op->offset = 8; + } else { + op->offset = 32; + } + } + } + } else if (u->adr_mode == 32) { + op->base = UD_R_EAX + rm; + if (mod == 1) { + op->offset = 8; + } else if (mod == 2) { + op->offset = 32; + } else if (mod == 0 && rm == 5) { + op->base = UD_NONE; + op->offset = 32; + } else { + op->offset = 0; + } + + /* Scale-Index-Base (SIB) */ + if ((rm & 7) == 4) { + ud_inp_next(u); + + op->scale = (1 << SIB_S(ud_inp_curr(u))) & ~1; + op->index = UD_R_EAX + (SIB_I(ud_inp_curr(u)) | (REX_X(u->pfx_rex) << 3)); + op->base = UD_R_EAX + (SIB_B(ud_inp_curr(u)) | (REX_B(u->pfx_rex) << 3)); + + if (op->index == UD_R_ESP) { + op->index = UD_NONE; + op->scale = UD_NONE; + } + + /* special condition for base reference */ + if (op->base == UD_R_EBP) { + if (mod == 0) { + op->base = UD_NONE; + } + if (mod == 1) { + op->offset = 8; + } else { + op->offset = 32; + } + } + } + } else { + const unsigned int bases[] = { UD_R_BX, UD_R_BX, UD_R_BP, UD_R_BP, + UD_R_SI, UD_R_DI, UD_R_BP, UD_R_BX }; + const unsigned int indices[] = { UD_R_SI, UD_R_DI, UD_R_SI, UD_R_DI, + UD_NONE, UD_NONE, UD_NONE, UD_NONE }; + op->base = bases[rm & 7]; + op->index = indices[rm & 7]; + if (mod == 0 && rm == 6) { + op->offset= 16; + op->base = UD_NONE; + } else if (mod == 1) { + op->offset = 8; + } else if (mod == 2) { + op->offset = 16; + } + } + + /* + * extract offset, if any + */ + switch (op->offset) { + case 8 : op->lval.ubyte = ud_inp_uint8(u); break; + case 16: op->lval.uword = ud_inp_uint16(u); break; + case 32: op->lval.udword = ud_inp_uint32(u); break; + case 64: op->lval.uqword = ud_inp_uint64(u); break; + default: break; + } +} + +/* ----------------------------------------------------------------------------- + * decode_o() - Decodes offset + * ----------------------------------------------------------------------------- + */ +static void +decode_o(struct ud* u, unsigned int s, struct ud_operand *op) +{ + switch (u->adr_mode) { + case 64: + op->offset = 64; + op->lval.uqword = ud_inp_uint64(u); + break; + case 32: + op->offset = 32; + op->lval.udword = ud_inp_uint32(u); + break; + case 16: + op->offset = 16; + op->lval.uword = ud_inp_uint16(u); + break; + default: + return; + } + op->type = UD_OP_MEM; + op->size = resolve_operand_size(u, s); +} + +/* ----------------------------------------------------------------------------- + * decode_operands() - Disassembles Operands. + * ----------------------------------------------------------------------------- + */ +static int +decode_operand(struct ud *u, + struct ud_operand *operand, + enum ud_operand_code type, + unsigned int size) +{ + switch (type) { + case OP_A : + decode_a(u, operand); + break; + case OP_MR: + if (MODRM_MOD(modrm(u)) == 3) { + decode_modrm_rm(u, operand, T_GPR, + size == SZ_DY ? SZ_MDQ : SZ_V); + } else if (size == SZ_WV) { + decode_modrm_rm( u, operand, T_GPR, SZ_W); + } else if (size == SZ_BV) { + decode_modrm_rm( u, operand, T_GPR, SZ_B); + } else if (size == SZ_DY) { + decode_modrm_rm( u, operand, T_GPR, SZ_D); + } else { + ASSERT(!"unexpected size"); + } + break; + case OP_M: + if (MODRM_MOD(modrm(u)) == 3) { + u->error = 1; + } + /* intended fall through */ + case OP_E: + decode_modrm_rm(u, operand, T_GPR, size); + break; + break; + case OP_G: + decode_modrm_reg(u, operand, T_GPR, size); + break; + case OP_I: + decode_imm(u, size, operand); + break; + case OP_I1: + operand->type = UD_OP_CONST; + operand->lval.udword = 1; + break; + case OP_PR: + if (MODRM_MOD(modrm(u)) != 3) { + u->error = 1; + } + decode_modrm_rm(u, operand, T_MMX, size); + break; + case OP_P: + decode_modrm_reg(u, operand, T_MMX, size); + break; + case OP_VR: + if (MODRM_MOD(modrm(u)) != 3) { + u->error = 1; + } + /* intended fall through */ + case OP_W: + decode_modrm_rm(u, operand, T_XMM, size); + break; + case OP_V: + decode_modrm_reg(u, operand, T_XMM, size); + break; + case OP_S: + decode_modrm_reg(u, operand, T_SEG, size); + break; + case OP_AL: + case OP_CL: + case OP_DL: + case OP_BL: + case OP_AH: + case OP_CH: + case OP_DH: + case OP_BH: + operand->type = UD_OP_REG; + operand->base = UD_R_AL + (type - OP_AL); + operand->size = 8; + break; + case OP_DX: + operand->type = UD_OP_REG; + operand->base = UD_R_DX; + operand->size = 16; + break; + case OP_O: + decode_o(u, size, operand); + break; + case OP_rAXr8: + case OP_rCXr9: + case OP_rDXr10: + case OP_rBXr11: + case OP_rSPr12: + case OP_rBPr13: + case OP_rSIr14: + case OP_rDIr15: + case OP_rAX: + case OP_rCX: + case OP_rDX: + case OP_rBX: + case OP_rSP: + case OP_rBP: + case OP_rSI: + case OP_rDI: + operand->type = UD_OP_REG; + operand->base = resolve_gpr64(u, type, &operand->size); + break; + case OP_ALr8b: + case OP_CLr9b: + case OP_DLr10b: + case OP_BLr11b: + case OP_AHr12b: + case OP_CHr13b: + case OP_DHr14b: + case OP_BHr15b: { + ud_type_t gpr = (type - OP_ALr8b) + UD_R_AL + + (REX_B(u->pfx_rex) << 3); + if (UD_R_AH <= gpr && u->pfx_rex) { + gpr = gpr + 4; + } + operand->type = UD_OP_REG; + operand->base = gpr; + break; + } + case OP_eAX: + case OP_eCX: + case OP_eDX: + case OP_eBX: + case OP_eSP: + case OP_eBP: + case OP_eSI: + case OP_eDI: + operand->type = UD_OP_REG; + operand->base = resolve_gpr32(u, type); + operand->size = u->opr_mode == 16 ? 16 : 32; + break; + case OP_ES: + case OP_CS: + case OP_DS: + case OP_SS: + case OP_FS: + case OP_GS: + /* in 64bits mode, only fs and gs are allowed */ + if (u->dis_mode == 64) { + if (type != OP_FS && type != OP_GS) { + u->error= 1; + } + } + operand->type = UD_OP_REG; + operand->base = (type - OP_ES) + UD_R_ES; + operand->size = 16; + break; + case OP_J : + decode_imm(u, size, operand); + operand->type = UD_OP_JIMM; + break ; + case OP_Q: + decode_modrm_rm(u, operand, T_MMX, size); + break; + case OP_R : + decode_modrm_rm(u, operand, T_GPR, size); + break; + case OP_C: + decode_modrm_reg(u, operand, T_CRG, size); + break; + case OP_D: + decode_modrm_reg(u, operand, T_DBG, size); + break; + case OP_I3 : + operand->type = UD_OP_CONST; + operand->lval.sbyte = 3; + break; + case OP_ST0: + case OP_ST1: + case OP_ST2: + case OP_ST3: + case OP_ST4: + case OP_ST5: + case OP_ST6: + case OP_ST7: + operand->type = UD_OP_REG; + operand->base = (type - OP_ST0) + UD_R_ST0; + operand->size = 0; + break; + case OP_AX: + operand->type = UD_OP_REG; + operand->base = UD_R_AX; + operand->size = 16; + break; + default : + operand->type = UD_NONE; + break; + } + return 0; +} + + +/* + * decode_operands + * + * Disassemble upto 3 operands of the current instruction being + * disassembled. By the end of the function, the operand fields + * of the ud structure will have been filled. + */ +static int +decode_operands(struct ud* u) +{ + decode_operand(u, &u->operand[0], + u->itab_entry->operand1.type, + u->itab_entry->operand1.size); + decode_operand(u, &u->operand[1], + u->itab_entry->operand2.type, + u->itab_entry->operand2.size); + decode_operand(u, &u->operand[2], + u->itab_entry->operand3.type, + u->itab_entry->operand3.size); + return 0; +} + +/* ----------------------------------------------------------------------------- + * clear_insn() - clear instruction structure + * ----------------------------------------------------------------------------- + */ +static void +clear_insn(register struct ud* u) +{ + u->error = 0; + u->pfx_seg = 0; + u->pfx_opr = 0; + u->pfx_adr = 0; + u->pfx_lock = 0; + u->pfx_repne = 0; + u->pfx_rep = 0; + u->pfx_repe = 0; + u->pfx_rex = 0; + u->pfx_insn = 0; + u->mnemonic = UD_Inone; + u->itab_entry = NULL; + u->have_modrm = 0; + + memset( &u->operand[ 0 ], 0, sizeof( struct ud_operand ) ); + memset( &u->operand[ 1 ], 0, sizeof( struct ud_operand ) ); + memset( &u->operand[ 2 ], 0, sizeof( struct ud_operand ) ); +} + +static int +resolve_mode( struct ud* u ) +{ + /* if in error state, bail out */ + if ( u->error ) return -1; + + /* propagate prefix effects */ + if ( u->dis_mode == 64 ) { /* set 64bit-mode flags */ + + /* Check validity of instruction m64 */ + if ( P_INV64( u->itab_entry->prefix ) ) { + u->error = 1; + return -1; + } + + /* effective rex prefix is the effective mask for the + * instruction hard-coded in the opcode map. + */ + u->pfx_rex = ( u->pfx_rex & 0x40 ) | + ( u->pfx_rex & REX_PFX_MASK( u->itab_entry->prefix ) ); + + /* whether this instruction has a default operand size of + * 64bit, also hardcoded into the opcode map. + */ + u->default64 = P_DEF64( u->itab_entry->prefix ); + /* calculate effective operand size */ + if ( REX_W( u->pfx_rex ) ) { + u->opr_mode = 64; + } else if ( u->pfx_opr ) { + u->opr_mode = 16; + } else { + /* unless the default opr size of instruction is 64, + * the effective operand size in the absence of rex.w + * prefix is 32. + */ + u->opr_mode = ( u->default64 ) ? 64 : 32; + } + + /* calculate effective address size */ + u->adr_mode = (u->pfx_adr) ? 32 : 64; + } else if ( u->dis_mode == 32 ) { /* set 32bit-mode flags */ + u->opr_mode = ( u->pfx_opr ) ? 16 : 32; + u->adr_mode = ( u->pfx_adr ) ? 16 : 32; + } else if ( u->dis_mode == 16 ) { /* set 16bit-mode flags */ + u->opr_mode = ( u->pfx_opr ) ? 32 : 16; + u->adr_mode = ( u->pfx_adr ) ? 32 : 16; + } + + /* These flags determine which operand to apply the operand size + * cast to. + */ + u->c1 = ( P_C1( u->itab_entry->prefix ) ) ? 1 : 0; + u->c2 = ( P_C2( u->itab_entry->prefix ) ) ? 1 : 0; + u->c3 = ( P_C3( u->itab_entry->prefix ) ) ? 1 : 0; + + /* set flags for implicit addressing */ + u->implicit_addr = P_IMPADDR( u->itab_entry->prefix ); + + return 0; +} + +static int gen_hex( struct ud *u ) +{ + unsigned int i; + unsigned char *src_ptr = ud_inp_sess( u ); + char* src_hex; + + /* bail out if in error stat. */ + if ( u->error ) return -1; + /* output buffer pointe */ + src_hex = ( char* ) u->insn_hexcode; + /* for each byte used to decode instruction */ + for ( i = 0; i < u->inp_ctr; ++i, ++src_ptr) { + sprintf( src_hex, "%02x", *src_ptr & 0xFF ); + src_hex += 2; + } + return 0; +} + + +static inline int +decode_insn(struct ud *u, uint16_t ptr) +{ + ASSERT((ptr & 0x8000) == 0); + u->itab_entry = &ud_itab[ ptr ]; + u->mnemonic = u->itab_entry->mnemonic; + return (resolve_mode(u) == 0 && + decode_operands(u) == 0 && + resolve_mnemonic(u) == 0) ? 0 : -1; +} + + +/* + * decode_3dnow() + * + * Decoding 3dnow is a little tricky because of its strange opcode + * structure. The final opcode disambiguation depends on the last + * byte that comes after the operands have been decoded. Fortunately, + * all 3dnow instructions have the same set of operand types. So we + * go ahead and decode the instruction by picking an arbitrarily chosen + * valid entry in the table, decode the operands, and read the final + * byte to resolve the menmonic. + */ +static inline int +decode_3dnow(struct ud* u) +{ + uint16_t ptr; + ASSERT(u->le->type == UD_TAB__OPC_3DNOW); + ASSERT(u->le->table[0xc] != 0); + decode_insn(u, u->le->table[0xc]); + ud_inp_next(u); + if (u->error) { + return -1; + } + ptr = u->le->table[ud_inp_curr(u)]; + ASSERT((ptr & 0x8000) == 0); + u->mnemonic = ud_itab[ptr].mnemonic; + return 0; +} + + +static int +decode_ssepfx(struct ud *u) +{ + uint8_t idx = ((u->pfx_insn & 0xf) + 1) / 2; + if (u->le->table[idx] == 0) { + idx = 0; + } + if (idx && u->le->table[idx] != 0) { + /* + * "Consume" the prefix as a part of the opcode, so it is no + * longer exported as an instruction prefix. + */ + switch (u->pfx_insn) { + case 0xf2: + u->pfx_repne = 0; + break; + case 0xf3: + u->pfx_rep = 0; + u->pfx_repe = 0; + break; + case 0x66: + u->pfx_opr = 0; + break; + } + } + return decode_ext(u, u->le->table[idx]); +} + + +/* + * decode_ext() + * + * Decode opcode extensions (if any) + */ +static int +decode_ext(struct ud *u, uint16_t ptr) +{ + uint8_t idx = 0; + if ((ptr & 0x8000) == 0) { + return decode_insn(u, ptr); + } + u->le = &ud_lookup_table_list[(~0x8000 & ptr)]; + if (u->le->type == UD_TAB__OPC_3DNOW) { + return decode_3dnow(u); + } + + switch (u->le->type) { + case UD_TAB__OPC_MOD: + /* !11 = 0, 11 = 1 */ + idx = (MODRM_MOD(modrm(u)) + 1) / 4; + break; + /* disassembly mode/operand size/address size based tables. + * 16 = 0,, 32 = 1, 64 = 2 + */ + case UD_TAB__OPC_MODE: + idx = u->dis_mode / 32; + break; + case UD_TAB__OPC_OSIZE: + idx = eff_opr_mode(u->dis_mode, REX_W(u->pfx_rex), u->pfx_opr) / 32; + break; + case UD_TAB__OPC_ASIZE: + idx = eff_adr_mode(u->dis_mode, u->pfx_adr) / 32; + break; + case UD_TAB__OPC_X87: + idx = modrm(u) - 0xC0; + break; + case UD_TAB__OPC_VENDOR: + if (u->vendor == UD_VENDOR_ANY) { + /* choose a valid entry */ + idx = (u->le->table[idx] != 0) ? 0 : 1; + } else if (u->vendor == UD_VENDOR_AMD) { + idx = 0; + } else { + idx = 1; + } + break; + case UD_TAB__OPC_RM: + idx = MODRM_RM(modrm(u)); + break; + case UD_TAB__OPC_REG: + idx = MODRM_REG(modrm(u)); + break; + case UD_TAB__OPC_SSE: + return decode_ssepfx(u); + default: + ASSERT(!"not reached"); + break; + } + + return decode_ext(u, u->le->table[idx]); +} + + +static inline int +decode_opcode(struct ud *u) +{ + uint16_t ptr; + ASSERT(u->le->type == UD_TAB__OPC_TABLE); + ud_inp_next(u); + if (u->error) { + return -1; + } + ptr = u->le->table[ud_inp_curr(u)]; + if (ptr & 0x8000) { + u->le = &ud_lookup_table_list[ptr & ~0x8000]; + if (u->le->type == UD_TAB__OPC_TABLE) { + return decode_opcode(u); + } + } + return decode_ext(u, ptr); +} + + +/* ============================================================================= + * ud_decode() - Instruction decoder. Returns the number of bytes decoded. + * ============================================================================= + */ +unsigned int +ud_decode(struct ud *u) +{ + ud_inp_start(u); + clear_insn(u); + u->le = &ud_lookup_table_list[0]; + u->error = decode_prefixes(u) == -1 || + decode_opcode(u) == -1 || + u->error; + /* Handle decode error. */ + if (u->error) { + /* clear out the decode data. */ + clear_insn(u); + /* mark the sequence of bytes as invalid. */ + u->itab_entry = & s_ie__invalid; + u->mnemonic = u->itab_entry->mnemonic; + } + + /* maybe this stray segment override byte + * should be spewed out? + */ + if ( !P_SEG( u->itab_entry->prefix ) && + u->operand[0].type != UD_OP_MEM && + u->operand[1].type != UD_OP_MEM ) + u->pfx_seg = 0; + + u->insn_offset = u->pc; /* set offset of instruction */ + u->insn_fill = 0; /* set translation buffer index to 0 */ + u->pc += u->inp_ctr; /* move program counter by bytes decoded */ + gen_hex( u ); /* generate hex code */ + + /* return number of bytes disassembled. */ + return u->inp_ctr; +} + +/* +vim: set ts=2 sw=2 expandtab +*/ + +#endif // USE(UDIS86) diff --git a/masm/disassembler/udis86/udis86_decode.h b/masm/disassembler/udis86/udis86_decode.h new file mode 100644 index 0000000000..940ed5ad6f --- /dev/null +++ b/masm/disassembler/udis86/udis86_decode.h @@ -0,0 +1,258 @@ +/* udis86 - libudis86/decode.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_DECODE_H +#define UD_DECODE_H + +#include "udis86_types.h" +#include "udis86_itab.h" + +#define MAX_INSN_LENGTH 15 + +/* register classes */ +#define T_NONE 0 +#define T_GPR 1 +#define T_MMX 2 +#define T_CRG 3 +#define T_DBG 4 +#define T_SEG 5 +#define T_XMM 6 + +/* itab prefix bits */ +#define P_none ( 0 ) +#define P_cast ( 1 << 0 ) +#define P_CAST(n) ( ( n >> 0 ) & 1 ) +#define P_c1 ( 1 << 0 ) +#define P_C1(n) ( ( n >> 0 ) & 1 ) +#define P_rexb ( 1 << 1 ) +#define P_REXB(n) ( ( n >> 1 ) & 1 ) +#define P_depM ( 1 << 2 ) +#define P_DEPM(n) ( ( n >> 2 ) & 1 ) +#define P_c3 ( 1 << 3 ) +#define P_C3(n) ( ( n >> 3 ) & 1 ) +#define P_inv64 ( 1 << 4 ) +#define P_INV64(n) ( ( n >> 4 ) & 1 ) +#define P_rexw ( 1 << 5 ) +#define P_REXW(n) ( ( n >> 5 ) & 1 ) +#define P_c2 ( 1 << 6 ) +#define P_C2(n) ( ( n >> 6 ) & 1 ) +#define P_def64 ( 1 << 7 ) +#define P_DEF64(n) ( ( n >> 7 ) & 1 ) +#define P_rexr ( 1 << 8 ) +#define P_REXR(n) ( ( n >> 8 ) & 1 ) +#define P_oso ( 1 << 9 ) +#define P_OSO(n) ( ( n >> 9 ) & 1 ) +#define P_aso ( 1 << 10 ) +#define P_ASO(n) ( ( n >> 10 ) & 1 ) +#define P_rexx ( 1 << 11 ) +#define P_REXX(n) ( ( n >> 11 ) & 1 ) +#define P_ImpAddr ( 1 << 12 ) +#define P_IMPADDR(n) ( ( n >> 12 ) & 1 ) +#define P_seg ( 1 << 13 ) +#define P_SEG(n) ( ( n >> 13 ) & 1 ) +#define P_sext ( 1 << 14 ) +#define P_SEXT(n) ( ( n >> 14 ) & 1 ) + +/* rex prefix bits */ +#define REX_W(r) ( ( 0xF & ( r ) ) >> 3 ) +#define REX_R(r) ( ( 0x7 & ( r ) ) >> 2 ) +#define REX_X(r) ( ( 0x3 & ( r ) ) >> 1 ) +#define REX_B(r) ( ( 0x1 & ( r ) ) >> 0 ) +#define REX_PFX_MASK(n) ( ( P_REXW(n) << 3 ) | \ + ( P_REXR(n) << 2 ) | \ + ( P_REXX(n) << 1 ) | \ + ( P_REXB(n) << 0 ) ) + +/* scable-index-base bits */ +#define SIB_S(b) ( ( b ) >> 6 ) +#define SIB_I(b) ( ( ( b ) >> 3 ) & 7 ) +#define SIB_B(b) ( ( b ) & 7 ) + +/* modrm bits */ +#define MODRM_REG(b) ( ( ( b ) >> 3 ) & 7 ) +#define MODRM_NNN(b) ( ( ( b ) >> 3 ) & 7 ) +#define MODRM_MOD(b) ( ( ( b ) >> 6 ) & 3 ) +#define MODRM_RM(b) ( ( b ) & 7 ) + +/* operand type constants -- order is important! */ + +enum ud_operand_code { + OP_NONE, + + OP_A, OP_E, OP_M, OP_G, + OP_I, + + OP_AL, OP_CL, OP_DL, OP_BL, + OP_AH, OP_CH, OP_DH, OP_BH, + + OP_ALr8b, OP_CLr9b, OP_DLr10b, OP_BLr11b, + OP_AHr12b, OP_CHr13b, OP_DHr14b, OP_BHr15b, + + OP_AX, OP_CX, OP_DX, OP_BX, + OP_SI, OP_DI, OP_SP, OP_BP, + + OP_rAX, OP_rCX, OP_rDX, OP_rBX, + OP_rSP, OP_rBP, OP_rSI, OP_rDI, + + OP_rAXr8, OP_rCXr9, OP_rDXr10, OP_rBXr11, + OP_rSPr12, OP_rBPr13, OP_rSIr14, OP_rDIr15, + + OP_eAX, OP_eCX, OP_eDX, OP_eBX, + OP_eSP, OP_eBP, OP_eSI, OP_eDI, + + OP_ES, OP_CS, OP_SS, OP_DS, + OP_FS, OP_GS, + + OP_ST0, OP_ST1, OP_ST2, OP_ST3, + OP_ST4, OP_ST5, OP_ST6, OP_ST7, + + OP_J, OP_S, OP_O, + OP_I1, OP_I3, + + OP_V, OP_W, OP_Q, OP_P, + + OP_R, OP_C, OP_D, OP_VR, OP_PR, + + OP_MR +} UD_ATTR_PACKED; + + +/* operand size constants */ + +enum ud_operand_size { + SZ_NA = 0, + SZ_Z = 1, + SZ_V = 2, + SZ_P = 3, + SZ_WP = 4, + SZ_DP = 5, + SZ_MDQ = 6, + SZ_RDQ = 7, + + /* the following values are used as is, + * and thus hard-coded. changing them + * will break internals + */ + SZ_B = 8, + SZ_W = 16, + SZ_D = 32, + SZ_Q = 64, + SZ_T = 80, + SZ_O = 128, + + SZ_WV = 17, + SZ_BV = 18, + SZ_DY = 19 + +} UD_ATTR_PACKED; + + +/* A single operand of an entry in the instruction table. + * (internal use only) + */ +struct ud_itab_entry_operand +{ + enum ud_operand_code type; + enum ud_operand_size size; +}; + + +/* A single entry in an instruction table. + *(internal use only) + */ +struct ud_itab_entry +{ + enum ud_mnemonic_code mnemonic; + struct ud_itab_entry_operand operand1; + struct ud_itab_entry_operand operand2; + struct ud_itab_entry_operand operand3; + uint32_t prefix; +}; + +struct ud_lookup_table_list_entry { + const uint16_t *table; + enum ud_table_type type; + const char *meta; +}; + + +static inline unsigned int sse_pfx_idx( const unsigned int pfx ) +{ + /* 00 = 0 + * f2 = 1 + * f3 = 2 + * 66 = 3 + */ + return ( ( pfx & 0xf ) + 1 ) / 2; +} + +static inline unsigned int mode_idx( const unsigned int mode ) +{ + /* 16 = 0 + * 32 = 1 + * 64 = 2 + */ + return ( mode / 32 ); +} + +static inline unsigned int modrm_mod_idx( const unsigned int mod ) +{ + /* !11 = 0 + * 11 = 1 + */ + return ( mod + 1 ) / 4; +} + +static inline unsigned int vendor_idx( const unsigned int vendor ) +{ + switch ( vendor ) { + case UD_VENDOR_AMD: return 0; + case UD_VENDOR_INTEL: return 1; + case UD_VENDOR_ANY: return 2; + default: return 2; + } +} + +static inline unsigned int is_group_ptr( uint16_t ptr ) +{ + return ( 0x8000 & ptr ); +} + +static inline unsigned int group_idx( uint16_t ptr ) +{ + return ( ~0x8000 & ptr ); +} + + +extern struct ud_itab_entry ud_itab[]; +extern struct ud_lookup_table_list_entry ud_lookup_table_list[]; + +#endif /* UD_DECODE_H */ + +/* vim:cindent + * vim:expandtab + * vim:ts=4 + * vim:sw=4 + */ diff --git a/masm/disassembler/udis86/udis86_extern.h b/masm/disassembler/udis86/udis86_extern.h new file mode 100644 index 0000000000..8e87721e8c --- /dev/null +++ b/masm/disassembler/udis86/udis86_extern.h @@ -0,0 +1,88 @@ +/* udis86 - libudis86/extern.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_EXTERN_H +#define UD_EXTERN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "udis86_types.h" + +/* ============================= PUBLIC API ================================= */ + +extern void ud_init(struct ud*); + +extern void ud_set_mode(struct ud*, uint8_t); + +extern void ud_set_pc(struct ud*, uint64_t); + +extern void ud_set_input_hook(struct ud*, int (*)(struct ud*)); + +extern void ud_set_input_buffer(struct ud*, uint8_t*, size_t); + +#ifndef __UD_STANDALONE__ +extern void ud_set_input_file(struct ud*, FILE*); +#endif /* __UD_STANDALONE__ */ + +extern void ud_set_vendor(struct ud*, unsigned); + +extern void ud_set_syntax(struct ud*, void (*)(struct ud*)); + +extern void ud_input_skip(struct ud*, size_t); + +extern int ud_input_end(struct ud*); + +extern unsigned int ud_decode(struct ud*); + +extern unsigned int ud_disassemble(struct ud*); + +extern void ud_translate_intel(struct ud*); + +extern void ud_translate_att(struct ud*); + +extern char* ud_insn_asm(struct ud* u); + +extern uint8_t* ud_insn_ptr(struct ud* u); + +extern uint64_t ud_insn_off(struct ud*); + +extern char* ud_insn_hex(struct ud*); + +extern unsigned int ud_insn_len(struct ud* u); + +extern const char* ud_lookup_mnemonic(enum ud_mnemonic_code c); + +extern void ud_set_user_opaque_data(struct ud*, void*); + +extern void *ud_get_user_opaque_data(struct ud*); + +/* ========================================================================== */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/masm/disassembler/udis86/udis86_input.c b/masm/disassembler/udis86/udis86_input.c new file mode 100644 index 0000000000..76c6cccf36 --- /dev/null +++ b/masm/disassembler/udis86/udis86_input.c @@ -0,0 +1,263 @@ +/* udis86 - libudis86/input.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_extern.h" +#include "udis86_types.h" +#include "udis86_input.h" + +/* ----------------------------------------------------------------------------- + * inp_buff_hook() - Hook for buffered inputs. + * ----------------------------------------------------------------------------- + */ +static int +inp_buff_hook(struct ud* u) +{ + if (u->inp_buff < u->inp_buff_end) + return *u->inp_buff++; + else return -1; +} + +#ifndef __UD_STANDALONE__ +/* ----------------------------------------------------------------------------- + * inp_file_hook() - Hook for FILE inputs. + * ----------------------------------------------------------------------------- + */ +static int +inp_file_hook(struct ud* u) +{ + return fgetc(u->inp_file); +} +#endif /* __UD_STANDALONE__*/ + +/* ============================================================================= + * ud_inp_set_hook() - Sets input hook. + * ============================================================================= + */ +extern void +ud_set_input_hook(register struct ud* u, int (*hook)(struct ud*)) +{ + u->inp_hook = hook; + ud_inp_init(u); +} + +extern void +ud_set_user_opaque_data( struct ud * u, void * opaque ) +{ + u->user_opaque_data = opaque; +} + +extern void * +ud_get_user_opaque_data( struct ud * u ) +{ + return u->user_opaque_data; +} + +/* ============================================================================= + * ud_inp_set_buffer() - Set buffer as input. + * ============================================================================= + */ +extern void +ud_set_input_buffer(register struct ud* u, uint8_t* buf, size_t len) +{ + u->inp_hook = inp_buff_hook; + u->inp_buff = buf; + u->inp_buff_end = buf + len; + ud_inp_init(u); +} + +#ifndef __UD_STANDALONE__ +/* ============================================================================= + * ud_input_set_file() - Set buffer as input. + * ============================================================================= + */ +extern void +ud_set_input_file(register struct ud* u, FILE* f) +{ + u->inp_hook = inp_file_hook; + u->inp_file = f; + ud_inp_init(u); +} +#endif /* __UD_STANDALONE__ */ + +/* ============================================================================= + * ud_input_skip() - Skip n input bytes. + * ============================================================================= + */ +extern void +ud_input_skip(struct ud* u, size_t n) +{ + while (n--) { + u->inp_hook(u); + } +} + +/* ============================================================================= + * ud_input_end() - Test for end of input. + * ============================================================================= + */ +extern int +ud_input_end(struct ud* u) +{ + return (u->inp_curr == u->inp_fill) && u->inp_end; +} + +/* ----------------------------------------------------------------------------- + * ud_inp_next() - Loads and returns the next byte from input. + * + * inp_curr and inp_fill are pointers to the cache. The program is written based + * on the property that they are 8-bits in size, and will eventually wrap around + * forming a circular buffer. So, the size of the cache is 256 in size, kind of + * unnecessary yet optimized. + * + * A buffer inp_sess stores the bytes disassembled for a single session. + * ----------------------------------------------------------------------------- + */ +extern uint8_t ud_inp_next(struct ud* u) +{ + int c = -1; + /* if current pointer is not upto the fill point in the + * input cache. + */ + if ( u->inp_curr != u->inp_fill ) { + c = u->inp_cache[ ++u->inp_curr ]; + /* if !end-of-input, call the input hook and get a byte */ + } else if ( u->inp_end || ( c = u->inp_hook( u ) ) == -1 ) { + /* end-of-input, mark it as an error, since the decoder, + * expected a byte more. + */ + u->error = 1; + /* flag end of input */ + u->inp_end = 1; + return 0; + } else { + /* increment pointers, we have a new byte. */ + u->inp_curr = ++u->inp_fill; + /* add the byte to the cache */ + u->inp_cache[ u->inp_fill ] = c; + } + /* record bytes input per decode-session. */ + u->inp_sess[ u->inp_ctr++ ] = c; + /* return byte */ + return ( uint8_t ) c; +} + +/* ----------------------------------------------------------------------------- + * ud_inp_back() - Move back a single byte in the stream. + * ----------------------------------------------------------------------------- + */ +extern void +ud_inp_back(struct ud* u) +{ + if ( u->inp_ctr > 0 ) { + --u->inp_curr; + --u->inp_ctr; + } +} + +/* ----------------------------------------------------------------------------- + * ud_inp_peek() - Peek into the next byte in source. + * ----------------------------------------------------------------------------- + */ +extern uint8_t +ud_inp_peek(struct ud* u) +{ + uint8_t r = ud_inp_next(u); + if ( !u->error ) ud_inp_back(u); /* Don't backup if there was an error */ + return r; +} + +/* ----------------------------------------------------------------------------- + * ud_inp_move() - Move ahead n input bytes. + * ----------------------------------------------------------------------------- + */ +extern void +ud_inp_move(struct ud* u, size_t n) +{ + while (n--) + ud_inp_next(u); +} + +/*------------------------------------------------------------------------------ + * ud_inp_uintN() - return uintN from source. + *------------------------------------------------------------------------------ + */ +extern uint8_t +ud_inp_uint8(struct ud* u) +{ + return ud_inp_next(u); +} + +extern uint16_t +ud_inp_uint16(struct ud* u) +{ + uint16_t r, ret; + + ret = ud_inp_next(u); + r = ud_inp_next(u); + return ret | (r << 8); +} + +extern uint32_t +ud_inp_uint32(struct ud* u) +{ + uint32_t r, ret; + + ret = ud_inp_next(u); + r = ud_inp_next(u); + ret = ret | (r << 8); + r = ud_inp_next(u); + ret = ret | (r << 16); + r = ud_inp_next(u); + return ret | (r << 24); +} + +extern uint64_t +ud_inp_uint64(struct ud* u) +{ + uint64_t r, ret; + + ret = ud_inp_next(u); + r = ud_inp_next(u); + ret = ret | (r << 8); + r = ud_inp_next(u); + ret = ret | (r << 16); + r = ud_inp_next(u); + ret = ret | (r << 24); + r = ud_inp_next(u); + ret = ret | (r << 32); + r = ud_inp_next(u); + ret = ret | (r << 40); + r = ud_inp_next(u); + ret = ret | (r << 48); + r = ud_inp_next(u); + return ret | (r << 56); +} + +#endif // USE(UDIS86) diff --git a/masm/disassembler/udis86/udis86_input.h b/masm/disassembler/udis86/udis86_input.h new file mode 100644 index 0000000000..96865a88b5 --- /dev/null +++ b/masm/disassembler/udis86/udis86_input.h @@ -0,0 +1,67 @@ +/* udis86 - libudis86/input.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_INPUT_H +#define UD_INPUT_H + +#include "udis86_types.h" + +uint8_t ud_inp_next(struct ud*); +uint8_t ud_inp_peek(struct ud*); +uint8_t ud_inp_uint8(struct ud*); +uint16_t ud_inp_uint16(struct ud*); +uint32_t ud_inp_uint32(struct ud*); +uint64_t ud_inp_uint64(struct ud*); +void ud_inp_move(struct ud*, size_t); +void ud_inp_back(struct ud*); + +/* ud_inp_init() - Initializes the input system. */ +#define ud_inp_init(u) \ +do { \ + u->inp_curr = 0; \ + u->inp_fill = 0; \ + u->inp_ctr = 0; \ + u->inp_end = 0; \ +} while (0) + +/* ud_inp_start() - Should be called before each de-code operation. */ +#define ud_inp_start(u) u->inp_ctr = 0 + +/* ud_inp_back() - Resets the current pointer to its position before the current + * instruction disassembly was started. + */ +#define ud_inp_reset(u) \ +do { \ + u->inp_curr -= u->inp_ctr; \ + u->inp_ctr = 0; \ +} while (0) + +/* ud_inp_sess() - Returns the pointer to current session. */ +#define ud_inp_sess(u) (u->inp_sess) + +/* inp_cur() - Returns the current input byte. */ +#define ud_inp_curr(u) ((u)->inp_cache[(u)->inp_curr]) + +#endif diff --git a/masm/disassembler/udis86/udis86_itab_holder.c b/masm/disassembler/udis86/udis86_itab_holder.c new file mode 100644 index 0000000000..d5d8726d6a --- /dev/null +++ b/masm/disassembler/udis86/udis86_itab_holder.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_itab.c" + +#endif + diff --git a/masm/disassembler/udis86/udis86_syn-att.c b/masm/disassembler/udis86/udis86_syn-att.c new file mode 100644 index 0000000000..155a34ca29 --- /dev/null +++ b/masm/disassembler/udis86/udis86_syn-att.c @@ -0,0 +1,253 @@ +/* udis86 - libudis86/syn-att.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_types.h" +#include "udis86_extern.h" +#include "udis86_decode.h" +#include "udis86_itab.h" +#include "udis86_syn.h" + +/* ----------------------------------------------------------------------------- + * opr_cast() - Prints an operand cast. + * ----------------------------------------------------------------------------- + */ +static void +opr_cast(struct ud* u, struct ud_operand* op) +{ + switch(op->size) { + case 16 : case 32 : + mkasm(u, "*"); break; + default: break; + } +} + +/* ----------------------------------------------------------------------------- + * gen_operand() - Generates assembly output for each operand. + * ----------------------------------------------------------------------------- + */ +static void +gen_operand(struct ud* u, struct ud_operand* op) +{ + switch(op->type) { + case UD_OP_REG: + mkasm(u, "%%%s", ud_reg_tab[op->base - UD_R_AL]); + break; + + case UD_OP_MEM: + if (u->br_far) opr_cast(u, op); + if (u->pfx_seg) + mkasm(u, "%%%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); + if (op->offset == 8) { + if (op->lval.sbyte < 0) + mkasm(u, "-0x%x", (-op->lval.sbyte) & 0xff); + else mkasm(u, "0x%x", op->lval.sbyte); + } + else if (op->offset == 16) + mkasm(u, "0x%x", op->lval.uword); + else if (op->offset == 32) + mkasm(u, "0x%lx", (unsigned long)op->lval.udword); + else if (op->offset == 64) + mkasm(u, "0x" FMT64 "x", op->lval.uqword); + + if (op->base) + mkasm(u, "(%%%s", ud_reg_tab[op->base - UD_R_AL]); + if (op->index) { + if (op->base) + mkasm(u, ","); + else mkasm(u, "("); + mkasm(u, "%%%s", ud_reg_tab[op->index - UD_R_AL]); + } + if (op->scale) + mkasm(u, ",%d", op->scale); + if (op->base || op->index) + mkasm(u, ")"); + break; + + case UD_OP_IMM: { + int64_t imm = 0; + uint64_t sext_mask = 0xffffffffffffffffull; + unsigned sext_size = op->size; + + switch (op->size) { + case 8: imm = op->lval.sbyte; break; + case 16: imm = op->lval.sword; break; + case 32: imm = op->lval.sdword; break; + case 64: imm = op->lval.sqword; break; + } + if ( P_SEXT( u->itab_entry->prefix ) ) { + sext_size = u->operand[ 0 ].size; + if ( u->mnemonic == UD_Ipush ) + /* push sign-extends to operand size */ + sext_size = u->opr_mode; + } + if ( sext_size < 64 ) + sext_mask = ( 1ull << sext_size ) - 1; + mkasm( u, "$0x" FMT64 "x", imm & sext_mask ); + + break; + } + + case UD_OP_JIMM: + switch (op->size) { + case 8: + mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sbyte); + break; + case 16: + mkasm(u, "0x" FMT64 "x", (u->pc + op->lval.sword) & 0xffff ); + break; + case 32: + if (u->dis_mode == 32) + mkasm(u, "0x" FMT64 "x", (u->pc + op->lval.sdword) & 0xffffffff); + else + mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sdword); + break; + default:break; + } + break; + + case UD_OP_PTR: + switch (op->size) { + case 32: + mkasm(u, "$0x%x, $0x%x", op->lval.ptr.seg, + op->lval.ptr.off & 0xFFFF); + break; + case 48: + mkasm(u, "$0x%x, $0x%lx", op->lval.ptr.seg, + (unsigned long)op->lval.ptr.off); + break; + } + break; + + default: return; + } +} + +/* ============================================================================= + * translates to AT&T syntax + * ============================================================================= + */ +extern void +ud_translate_att(struct ud *u) +{ + int size = 0; + + /* check if P_OSO prefix is used */ + if (! P_OSO(u->itab_entry->prefix) && u->pfx_opr) { + switch (u->dis_mode) { + case 16: + mkasm(u, "o32 "); + break; + case 32: + case 64: + mkasm(u, "o16 "); + break; + } + } + + /* check if P_ASO prefix was used */ + if (! P_ASO(u->itab_entry->prefix) && u->pfx_adr) { + switch (u->dis_mode) { + case 16: + mkasm(u, "a32 "); + break; + case 32: + mkasm(u, "a16 "); + break; + case 64: + mkasm(u, "a32 "); + break; + } + } + + if (u->pfx_lock) + mkasm(u, "lock "); + if (u->pfx_rep) + mkasm(u, "rep "); + if (u->pfx_repne) + mkasm(u, "repne "); + + /* special instructions */ + switch (u->mnemonic) { + case UD_Iretf: + mkasm(u, "lret "); + break; + case UD_Idb: + mkasm(u, ".byte 0x%x", u->operand[0].lval.ubyte); + return; + case UD_Ijmp: + case UD_Icall: + if (u->br_far) mkasm(u, "l"); + mkasm(u, "%s", ud_lookup_mnemonic(u->mnemonic)); + break; + case UD_Ibound: + case UD_Ienter: + if (u->operand[0].type != UD_NONE) + gen_operand(u, &u->operand[0]); + if (u->operand[1].type != UD_NONE) { + mkasm(u, ","); + gen_operand(u, &u->operand[1]); + } + return; + default: + mkasm(u, "%s", ud_lookup_mnemonic(u->mnemonic)); + } + + if (u->c1) + size = u->operand[0].size; + else if (u->c2) + size = u->operand[1].size; + else if (u->c3) + size = u->operand[2].size; + + if (size == 8) + mkasm(u, "b"); + else if (size == 16) + mkasm(u, "w"); + else if (size == 64) + mkasm(u, "q"); + + mkasm(u, " "); + + if (u->operand[2].type != UD_NONE) { + gen_operand(u, &u->operand[2]); + mkasm(u, ", "); + } + + if (u->operand[1].type != UD_NONE) { + gen_operand(u, &u->operand[1]); + mkasm(u, ", "); + } + + if (u->operand[0].type != UD_NONE) + gen_operand(u, &u->operand[0]); +} + +#endif // USE(UDIS86) + diff --git a/masm/disassembler/udis86/udis86_syn-intel.c b/masm/disassembler/udis86/udis86_syn-intel.c new file mode 100644 index 0000000000..d250bd449c --- /dev/null +++ b/masm/disassembler/udis86/udis86_syn-intel.c @@ -0,0 +1,279 @@ +/* udis86 - libudis86/syn-intel.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_types.h" +#include "udis86_extern.h" +#include "udis86_decode.h" +#include "udis86_itab.h" +#include "udis86_syn.h" + +/* ----------------------------------------------------------------------------- + * opr_cast() - Prints an operand cast. + * ----------------------------------------------------------------------------- + */ +static void +opr_cast(struct ud* u, struct ud_operand* op) +{ + switch(op->size) { + case 8: mkasm(u, "byte " ); break; + case 16: mkasm(u, "word " ); break; + case 32: mkasm(u, "dword "); break; + case 64: mkasm(u, "qword "); break; + case 80: mkasm(u, "tword "); break; + default: break; + } + if (u->br_far) + mkasm(u, "far "); +} + +/* ----------------------------------------------------------------------------- + * gen_operand() - Generates assembly output for each operand. + * ----------------------------------------------------------------------------- + */ +static void gen_operand(struct ud* u, struct ud_operand* op, int syn_cast) +{ + switch(op->type) { + case UD_OP_REG: + mkasm(u, "%s", ud_reg_tab[op->base - UD_R_AL]); + break; + + case UD_OP_MEM: { + + int op_f = 0; + + if (syn_cast) + opr_cast(u, op); + + mkasm(u, "["); + + if (u->pfx_seg) + mkasm(u, "%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); + + if (op->base) { + mkasm(u, "%s", ud_reg_tab[op->base - UD_R_AL]); + op_f = 1; + } + + if (op->index) { + if (op_f) + mkasm(u, "+"); + mkasm(u, "%s", ud_reg_tab[op->index - UD_R_AL]); + op_f = 1; + } + + if (op->scale) + mkasm(u, "*%d", op->scale); + + if (op->offset == 8) { + if (op->lval.sbyte < 0) + mkasm(u, "-0x%x", -op->lval.sbyte); + else mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.sbyte); + } + else if (op->offset == 16) + mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.uword); + else if (op->offset == 32) { + if (u->adr_mode == 64) { + if (op->lval.sdword < 0) + mkasm(u, "-0x%x", -op->lval.sdword); + else mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.sdword); + } + else mkasm(u, "%s0x%lx", (op_f) ? "+" : "", (unsigned long)op->lval.udword); + } + else if (op->offset == 64) + mkasm(u, "%s0x" FMT64 "x", (op_f) ? "+" : "", op->lval.uqword); + + mkasm(u, "]"); + break; + } + + case UD_OP_IMM: { + int64_t imm = 0; + uint64_t sext_mask = 0xffffffffffffffffull; + unsigned sext_size = op->size; + + if (syn_cast) + opr_cast(u, op); + switch (op->size) { + case 8: imm = op->lval.sbyte; break; + case 16: imm = op->lval.sword; break; + case 32: imm = op->lval.sdword; break; + case 64: imm = op->lval.sqword; break; + } + if ( P_SEXT( u->itab_entry->prefix ) ) { + sext_size = u->operand[ 0 ].size; + if ( u->mnemonic == UD_Ipush ) + /* push sign-extends to operand size */ + sext_size = u->opr_mode; + } + if ( sext_size < 64 ) + sext_mask = ( 1ull << sext_size ) - 1; + mkasm( u, "0x" FMT64 "x", imm & sext_mask ); + + break; + } + + + case UD_OP_JIMM: + if (syn_cast) opr_cast(u, op); + switch (op->size) { + case 8: + mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sbyte); + break; + case 16: + mkasm(u, "0x" FMT64 "x", ( u->pc + op->lval.sword ) & 0xffff ); + break; + case 32: + mkasm(u, "0x" FMT64 "x", ( u->pc + op->lval.sdword ) & 0xfffffffful ); + break; + default:break; + } + break; + + case UD_OP_PTR: + switch (op->size) { + case 32: + mkasm(u, "word 0x%x:0x%x", op->lval.ptr.seg, + op->lval.ptr.off & 0xFFFF); + break; + case 48: + mkasm(u, "dword 0x%x:0x%lx", op->lval.ptr.seg, + (unsigned long)op->lval.ptr.off); + break; + } + break; + + case UD_OP_CONST: + if (syn_cast) opr_cast(u, op); + mkasm(u, "%d", op->lval.udword); + break; + + default: return; + } +} + +/* ============================================================================= + * translates to intel syntax + * ============================================================================= + */ +extern void ud_translate_intel(struct ud* u) +{ + /* -- prefixes -- */ + + /* check if P_OSO prefix is used */ + if (! P_OSO(u->itab_entry->prefix) && u->pfx_opr) { + switch (u->dis_mode) { + case 16: + mkasm(u, "o32 "); + break; + case 32: + case 64: + mkasm(u, "o16 "); + break; + } + } + + /* check if P_ASO prefix was used */ + if (! P_ASO(u->itab_entry->prefix) && u->pfx_adr) { + switch (u->dis_mode) { + case 16: + mkasm(u, "a32 "); + break; + case 32: + mkasm(u, "a16 "); + break; + case 64: + mkasm(u, "a32 "); + break; + } + } + + if ( u->pfx_seg && + u->operand[0].type != UD_OP_MEM && + u->operand[1].type != UD_OP_MEM ) { + mkasm(u, "%s ", ud_reg_tab[u->pfx_seg - UD_R_AL]); + } + if (u->pfx_lock) + mkasm(u, "lock "); + if (u->pfx_rep) + mkasm(u, "rep "); + if (u->pfx_repne) + mkasm(u, "repne "); + + /* print the instruction mnemonic */ + mkasm(u, "%s ", ud_lookup_mnemonic(u->mnemonic)); + + /* operand 1 */ + if (u->operand[0].type != UD_NONE) { + int cast = 0; + if ( u->operand[0].type == UD_OP_IMM && + u->operand[1].type == UD_NONE ) + cast = u->c1; + if ( u->operand[0].type == UD_OP_MEM ) { + cast = u->c1; + if ( u->operand[1].type == UD_OP_IMM || + u->operand[1].type == UD_OP_CONST ) + cast = 1; + if ( u->operand[1].type == UD_NONE ) + cast = 1; + if ( ( u->operand[0].size != u->operand[1].size ) && u->operand[1].size ) + cast = 1; + } else if ( u->operand[ 0 ].type == UD_OP_JIMM ) { + if ( u->operand[ 0 ].size > 8 ) cast = 1; + } + gen_operand(u, &u->operand[0], cast); + } + /* operand 2 */ + if (u->operand[1].type != UD_NONE) { + int cast = 0; + mkasm(u, ", "); + if ( u->operand[1].type == UD_OP_MEM ) { + cast = u->c1; + + if ( u->operand[0].type != UD_OP_REG ) + cast = 1; + if ( u->operand[0].size != u->operand[1].size && u->operand[1].size ) + cast = 1; + if ( u->operand[0].type == UD_OP_REG && + u->operand[0].base >= UD_R_ES && + u->operand[0].base <= UD_R_GS ) + cast = 0; + } + gen_operand(u, &u->operand[1], cast ); + } + + /* operand 3 */ + if (u->operand[2].type != UD_NONE) { + mkasm(u, ", "); + gen_operand(u, &u->operand[2], u->c3); + } +} + +#endif // USE(UDIS86) + diff --git a/masm/disassembler/udis86/udis86_syn.c b/masm/disassembler/udis86/udis86_syn.c new file mode 100644 index 0000000000..80391b4a08 --- /dev/null +++ b/masm/disassembler/udis86/udis86_syn.c @@ -0,0 +1,87 @@ +/* udis86 - libudis86/syn.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include + +#if USE(UDIS86) + +/* ----------------------------------------------------------------------------- + * Intel Register Table - Order Matters (types.h)! + * ----------------------------------------------------------------------------- + */ +const char* ud_reg_tab[] = +{ + "al", "cl", "dl", "bl", + "ah", "ch", "dh", "bh", + "spl", "bpl", "sil", "dil", + "r8b", "r9b", "r10b", "r11b", + "r12b", "r13b", "r14b", "r15b", + + "ax", "cx", "dx", "bx", + "sp", "bp", "si", "di", + "r8w", "r9w", "r10w", "r11w", + "r12w", "r13W" , "r14w", "r15w", + + "eax", "ecx", "edx", "ebx", + "esp", "ebp", "esi", "edi", + "r8d", "r9d", "r10d", "r11d", + "r12d", "r13d", "r14d", "r15d", + + "rax", "rcx", "rdx", "rbx", + "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", + "r12", "r13", "r14", "r15", + + "es", "cs", "ss", "ds", + "fs", "gs", + + "cr0", "cr1", "cr2", "cr3", + "cr4", "cr5", "cr6", "cr7", + "cr8", "cr9", "cr10", "cr11", + "cr12", "cr13", "cr14", "cr15", + + "dr0", "dr1", "dr2", "dr3", + "dr4", "dr5", "dr6", "dr7", + "dr8", "dr9", "dr10", "dr11", + "dr12", "dr13", "dr14", "dr15", + + "mm0", "mm1", "mm2", "mm3", + "mm4", "mm5", "mm6", "mm7", + + "st0", "st1", "st2", "st3", + "st4", "st5", "st6", "st7", + + "xmm0", "xmm1", "xmm2", "xmm3", + "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10", "xmm11", + "xmm12", "xmm13", "xmm14", "xmm15", + + "rip" +}; + +#endif // USE(UDIS86) + diff --git a/masm/disassembler/udis86/udis86_syn.h b/masm/disassembler/udis86/udis86_syn.h new file mode 100644 index 0000000000..e8636163ef --- /dev/null +++ b/masm/disassembler/udis86/udis86_syn.h @@ -0,0 +1,47 @@ +/* udis86 - libudis86/syn.h + * + * Copyright (c) 2002-2009 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_SYN_H +#define UD_SYN_H + +#include "udis86_types.h" +#include + +#ifndef __UD_STANDALONE__ +# include +#endif /* __UD_STANDALONE__ */ + +extern const char* ud_reg_tab[]; + +static void mkasm(struct ud* u, const char* fmt, ...) WTF_ATTRIBUTE_PRINTF(2, 3); +static void mkasm(struct ud* u, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + u->insn_fill += vsnprintf((char*) u->insn_buffer + u->insn_fill, UD_STRING_BUFFER_SIZE - u->insn_fill, fmt, ap); + va_end(ap); +} + +#endif diff --git a/masm/disassembler/udis86/udis86_types.h b/masm/disassembler/udis86/udis86_types.h new file mode 100644 index 0000000000..320d1ca491 --- /dev/null +++ b/masm/disassembler/udis86/udis86_types.h @@ -0,0 +1,238 @@ +/* udis86 - libudis86/types.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_TYPES_H +#define UD_TYPES_H + +#ifndef __UD_STANDALONE__ +# include +#endif /* __UD_STANDALONE__ */ + +/* gcc specific extensions */ +#ifdef __GNUC__ +# define UD_ATTR_PACKED __attribute__((packed)) +#else +# define UD_ATTR_PACKED +#endif /* UD_ATTR_PACKED */ + +#ifdef _MSC_VER +# define FMT64 "%I64" + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; +#else +# define FMT64 "%ll" +# ifndef __UD_STANDALONE__ +# include +# endif /* __UD_STANDALONE__ */ +#endif + +/* ----------------------------------------------------------------------------- + * All possible "types" of objects in udis86. Order is Important! + * ----------------------------------------------------------------------------- + */ +enum ud_type +{ + UD_NONE, + + /* 8 bit GPRs */ + UD_R_AL, UD_R_CL, UD_R_DL, UD_R_BL, + UD_R_AH, UD_R_CH, UD_R_DH, UD_R_BH, + UD_R_SPL, UD_R_BPL, UD_R_SIL, UD_R_DIL, + UD_R_R8B, UD_R_R9B, UD_R_R10B, UD_R_R11B, + UD_R_R12B, UD_R_R13B, UD_R_R14B, UD_R_R15B, + + /* 16 bit GPRs */ + UD_R_AX, UD_R_CX, UD_R_DX, UD_R_BX, + UD_R_SP, UD_R_BP, UD_R_SI, UD_R_DI, + UD_R_R8W, UD_R_R9W, UD_R_R10W, UD_R_R11W, + UD_R_R12W, UD_R_R13W, UD_R_R14W, UD_R_R15W, + + /* 32 bit GPRs */ + UD_R_EAX, UD_R_ECX, UD_R_EDX, UD_R_EBX, + UD_R_ESP, UD_R_EBP, UD_R_ESI, UD_R_EDI, + UD_R_R8D, UD_R_R9D, UD_R_R10D, UD_R_R11D, + UD_R_R12D, UD_R_R13D, UD_R_R14D, UD_R_R15D, + + /* 64 bit GPRs */ + UD_R_RAX, UD_R_RCX, UD_R_RDX, UD_R_RBX, + UD_R_RSP, UD_R_RBP, UD_R_RSI, UD_R_RDI, + UD_R_R8, UD_R_R9, UD_R_R10, UD_R_R11, + UD_R_R12, UD_R_R13, UD_R_R14, UD_R_R15, + + /* segment registers */ + UD_R_ES, UD_R_CS, UD_R_SS, UD_R_DS, + UD_R_FS, UD_R_GS, + + /* control registers*/ + UD_R_CR0, UD_R_CR1, UD_R_CR2, UD_R_CR3, + UD_R_CR4, UD_R_CR5, UD_R_CR6, UD_R_CR7, + UD_R_CR8, UD_R_CR9, UD_R_CR10, UD_R_CR11, + UD_R_CR12, UD_R_CR13, UD_R_CR14, UD_R_CR15, + + /* debug registers */ + UD_R_DR0, UD_R_DR1, UD_R_DR2, UD_R_DR3, + UD_R_DR4, UD_R_DR5, UD_R_DR6, UD_R_DR7, + UD_R_DR8, UD_R_DR9, UD_R_DR10, UD_R_DR11, + UD_R_DR12, UD_R_DR13, UD_R_DR14, UD_R_DR15, + + /* mmx registers */ + UD_R_MM0, UD_R_MM1, UD_R_MM2, UD_R_MM3, + UD_R_MM4, UD_R_MM5, UD_R_MM6, UD_R_MM7, + + /* x87 registers */ + UD_R_ST0, UD_R_ST1, UD_R_ST2, UD_R_ST3, + UD_R_ST4, UD_R_ST5, UD_R_ST6, UD_R_ST7, + + /* extended multimedia registers */ + UD_R_XMM0, UD_R_XMM1, UD_R_XMM2, UD_R_XMM3, + UD_R_XMM4, UD_R_XMM5, UD_R_XMM6, UD_R_XMM7, + UD_R_XMM8, UD_R_XMM9, UD_R_XMM10, UD_R_XMM11, + UD_R_XMM12, UD_R_XMM13, UD_R_XMM14, UD_R_XMM15, + + UD_R_RIP, + + /* Operand Types */ + UD_OP_REG, UD_OP_MEM, UD_OP_PTR, UD_OP_IMM, + UD_OP_JIMM, UD_OP_CONST +}; + +#include "udis86_itab.h" + +/* ----------------------------------------------------------------------------- + * struct ud_operand - Disassembled instruction Operand. + * ----------------------------------------------------------------------------- + */ +struct ud_operand +{ + enum ud_type type; + uint8_t size; + union { + int8_t sbyte; + uint8_t ubyte; + int16_t sword; + uint16_t uword; + int32_t sdword; + uint32_t udword; + int64_t sqword; + uint64_t uqword; + + struct { + uint16_t seg; + uint32_t off; + } ptr; + } lval; + + enum ud_type base; + enum ud_type index; + uint8_t offset; + uint8_t scale; +}; + +#define UD_STRING_BUFFER_SIZE 64 + +/* ----------------------------------------------------------------------------- + * struct ud - The udis86 object. + * ----------------------------------------------------------------------------- + */ +struct ud +{ + int (*inp_hook) (struct ud*); + uint8_t inp_curr; + uint8_t inp_fill; +#ifndef __UD_STANDALONE__ + FILE* inp_file; +#endif + uint8_t inp_ctr; + uint8_t* inp_buff; + uint8_t* inp_buff_end; + uint8_t inp_end; + void (*translator)(struct ud*); + uint64_t insn_offset; + char insn_hexcode[32]; + char insn_buffer[UD_STRING_BUFFER_SIZE]; + unsigned int insn_fill; + uint8_t dis_mode; + uint64_t pc; + uint8_t vendor; + struct map_entry* mapen; + enum ud_mnemonic_code mnemonic; + struct ud_operand operand[3]; + uint8_t error; + uint8_t pfx_rex; + uint8_t pfx_seg; + uint8_t pfx_opr; + uint8_t pfx_adr; + uint8_t pfx_lock; + uint8_t pfx_rep; + uint8_t pfx_repe; + uint8_t pfx_repne; + uint8_t pfx_insn; + uint8_t default64; + uint8_t opr_mode; + uint8_t adr_mode; + uint8_t br_far; + uint8_t br_near; + uint8_t implicit_addr; + uint8_t c1; + uint8_t c2; + uint8_t c3; + uint8_t inp_cache[256]; + uint8_t inp_sess[64]; + uint8_t have_modrm; + uint8_t modrm; + void * user_opaque_data; + struct ud_itab_entry * itab_entry; + struct ud_lookup_table_list_entry *le; +}; + +/* ----------------------------------------------------------------------------- + * Type-definitions + * ----------------------------------------------------------------------------- + */ +typedef enum ud_type ud_type_t; +typedef enum ud_mnemonic_code ud_mnemonic_code_t; + +typedef struct ud ud_t; +typedef struct ud_operand ud_operand_t; + +#define UD_SYN_INTEL ud_translate_intel +#define UD_SYN_ATT ud_translate_att +#define UD_EOI -1 +#define UD_INP_CACHE_SZ 32 +#define UD_VENDOR_AMD 0 +#define UD_VENDOR_INTEL 1 +#define UD_VENDOR_ANY 2 + +#define bail_out(ud,error_code) longjmp( (ud)->bailout, error_code ) +#define try_decode(ud) if ( setjmp( (ud)->bailout ) == 0 ) +#define catch_error() else + +#endif diff --git a/masm/jit/JITCompilationEffort.h b/masm/jit/JITCompilationEffort.h new file mode 100644 index 0000000000..5eb6801789 --- /dev/null +++ b/masm/jit/JITCompilationEffort.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JITCompilationEffort_h +#define JITCompilationEffort_h + +namespace JSC { + +enum JITCompilationEffort { + JITCompilationCanFail, + JITCompilationMustSucceed +}; + +} // namespace JSC + +#endif // JITCompilationEffort_h + diff --git a/masm/masm.pri b/masm/masm.pri new file mode 100644 index 0000000000..1f05e11f20 --- /dev/null +++ b/masm/masm.pri @@ -0,0 +1,17 @@ + +HEADERS += $$PWD/assembler/*.h +SOURCES += $$PWD/assembler/ARMAssembler.cpp +SOURCES += $$PWD/assembler/ARMv7Assembler.cpp +SOURCES += $$PWD/assembler/MacroAssemblerARM.cpp +SOURCES += $$PWD/assembler/MacroAssemblerSH4.cpp +SOURCES += $$PWD/assembler/LinkBuffer.cpp +SOURCES += $$PWD/stubs/WTFStubs.cpp + +DEFINES += NDEBUG +DEFINES += WTF_EXPORT_PRIVATE="" + +INCLUDEPATH += $$PWD/jit +INCLUDEPATH += $$PWD/disassembler +INCLUDEPATH += $$PWD/wtf +INCLUDEPATH += $$PWD/stubs +INCLUDEPATH += $$PWD diff --git a/masm/stubs/ExecutableAllocator.h b/masm/stubs/ExecutableAllocator.h new file mode 100644 index 0000000000..8058a45ba3 --- /dev/null +++ b/masm/stubs/ExecutableAllocator.h @@ -0,0 +1,46 @@ +#ifndef MASM_EXECUTABLEALLOCATOR_H +#define MASM_EXECUTABLEALLOCATOR_H + +#include +#include + +namespace JSC { + +struct JSGlobalData; + +struct ExecutableMemoryHandle : public RefCounted { + ExecutableMemoryHandle(int size) + : m_size(size) + { + m_data = malloc(m_size); + } + ~ExecutableMemoryHandle() + { + free(m_data); + } + + void* start() { return m_data; } + int sizeInBytes() { return m_size; } + + void* m_data; + int m_size; +}; + +struct ExecutableAllocator { + PassRefPtr allocate(JSGlobalData&, int size, void*, int) + { + return adoptRef(new ExecutableMemoryHandle(size)); + } + + static void makeWritable(void*, int) + { + } + + static void makeExecutable(void*, int) + { + } +}; + +} + +#endif // MASM_EXECUTABLEALLOCATOR_H diff --git a/masm/stubs/JSGlobalData.h b/masm/stubs/JSGlobalData.h new file mode 100644 index 0000000000..72bd63f5a0 --- /dev/null +++ b/masm/stubs/JSGlobalData.h @@ -0,0 +1,15 @@ +#ifndef MASM_JSGLOBALDATA_H +#define MASM_JSGLOBALDATA_H + +#include "ExecutableAllocator.h" +#include "WeakRandom.h" + +namespace JSC { + +struct JSGlobalData { + ExecutableAllocator executableAllocator; +}; + +} + +#endif // MASM_JSGLOBALDATA_H diff --git a/masm/stubs/LLIntData.h b/masm/stubs/LLIntData.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/masm/stubs/Options.h b/masm/stubs/Options.h new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/masm/stubs/Options.h @@ -0,0 +1 @@ + diff --git a/masm/stubs/WTFStubs.cpp b/masm/stubs/WTFStubs.cpp new file mode 100644 index 0000000000..68424293c4 --- /dev/null +++ b/masm/stubs/WTFStubs.cpp @@ -0,0 +1,39 @@ +#include +#include +#include + +namespace WTF { + +void* fastMalloc(unsigned int size) +{ + return malloc(size); +} + +void fastFree(void* ptr) +{ + free(ptr); +} + +int cryptographicallyRandomNumber() +{ + return 0; +} + +FILE* dataFile() +{ + return 0; +} + +void dataLogV(const char* format, va_list) +{ +} + +void dataLog(const char* format, ...) +{ +} + +void dataLogString(const char*) +{ +} + +} diff --git a/masm/wtf/Alignment.h b/masm/wtf/Alignment.h new file mode 100644 index 0000000000..7dfda209a8 --- /dev/null +++ b/masm/wtf/Alignment.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_Alignment_h +#define WTF_Alignment_h + +#include +#include +#include + + +namespace WTF { + +#if COMPILER(GCC) || COMPILER(MINGW) || COMPILER(RVCT) || COMPILER(GCCE) || (COMPILER(SUNCC) && __SUNPRO_CC > 0x590) + #define WTF_ALIGN_OF(type) __alignof__(type) + #define WTF_ALIGNED(variable_type, variable, n) variable_type variable __attribute__((__aligned__(n))) +#elif COMPILER(MSVC) + #define WTF_ALIGN_OF(type) __alignof(type) + #define WTF_ALIGNED(variable_type, variable, n) __declspec(align(n)) variable_type variable +#else + #error WTF_ALIGN macros need alignment control. +#endif + +#if COMPILER(GCC) && !COMPILER(INTEL) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 303) + typedef char __attribute__((__may_alias__)) AlignedBufferChar; +#else + typedef char AlignedBufferChar; +#endif + + template struct AlignedBuffer; + template struct AlignedBuffer { AlignedBufferChar buffer[size]; }; + template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 2); }; + template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 4); }; + template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 8); }; + template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 16); }; + template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 32); }; + template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 64); }; + + template + void swap(AlignedBuffer& a, AlignedBuffer& b) + { + for (size_t i = 0; i < size; ++i) + std::swap(a.buffer[i], b.buffer[i]); + } + + template + inline bool isAlignedTo(const void* pointer) + { + return !(reinterpret_cast(pointer) & mask); + } +} + +#endif // WTF_Alignment_h diff --git a/masm/wtf/AlwaysInline.h b/masm/wtf/AlwaysInline.h new file mode 100644 index 0000000000..68b7ae1a80 --- /dev/null +++ b/masm/wtf/AlwaysInline.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2005, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +/* This file is no longer necessary, since all the functionality has been moved to Compiler.h. */ + +#include diff --git a/masm/wtf/Assertions.h b/masm/wtf/Assertions.h new file mode 100644 index 0000000000..7e079ab187 --- /dev/null +++ b/masm/wtf/Assertions.h @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_Assertions_h +#define WTF_Assertions_h + +/* + no namespaces because this file has to be includable from C and Objective-C + + Note, this file uses many GCC extensions, but it should be compatible with + C, Objective C, C++, and Objective C++. + + For non-debug builds, everything is disabled by default. + Defining any of the symbols explicitly prevents this from having any effect. + + MSVC7 note: variadic macro support was added in MSVC8, so for now we disable + those macros in MSVC7. For more info, see the MSDN document on variadic + macros here: + + http://msdn2.microsoft.com/en-us/library/ms177415(VS.80).aspx +*/ + +#include + +#include + +#if !COMPILER(MSVC) +#include +#endif + +#ifdef NDEBUG +/* Disable ASSERT* macros in release mode. */ +#define ASSERTIONS_DISABLED_DEFAULT 1 +#else +#define ASSERTIONS_DISABLED_DEFAULT 0 +#endif + +#if COMPILER(MSVC7_OR_LOWER) +#define HAVE_VARIADIC_MACRO 0 +#else +#define HAVE_VARIADIC_MACRO 1 +#endif + +#ifndef BACKTRACE_DISABLED +#define BACKTRACE_DISABLED ASSERTIONS_DISABLED_DEFAULT +#endif + +#ifndef ASSERT_DISABLED +#define ASSERT_DISABLED ASSERTIONS_DISABLED_DEFAULT +#endif + +#ifndef ASSERT_MSG_DISABLED +#if HAVE(VARIADIC_MACRO) +#define ASSERT_MSG_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define ASSERT_MSG_DISABLED 1 +#endif +#endif + +#ifndef ASSERT_ARG_DISABLED +#define ASSERT_ARG_DISABLED ASSERTIONS_DISABLED_DEFAULT +#endif + +#ifndef FATAL_DISABLED +#if HAVE(VARIADIC_MACRO) +#define FATAL_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define FATAL_DISABLED 1 +#endif +#endif + +#ifndef ERROR_DISABLED +#if HAVE(VARIADIC_MACRO) +#define ERROR_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define ERROR_DISABLED 1 +#endif +#endif + +#ifndef LOG_DISABLED +#if HAVE(VARIADIC_MACRO) +#define LOG_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define LOG_DISABLED 1 +#endif +#endif + +#if COMPILER(GCC) +#define WTF_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#define WTF_PRETTY_FUNCTION __FUNCTION__ +#endif + +/* WTF logging functions can process %@ in the format string to log a NSObject* but the printf format attribute + emits a warning when %@ is used in the format string. Until is resolved we can't include + the attribute when being used from Objective-C code in case it decides to use %@. */ +#if COMPILER(GCC) && !defined(__OBJC__) +#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) __attribute__((__format__(printf, formatStringArgument, extraArguments))) +#else +#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) +#endif + +/* These helper functions are always declared, but not necessarily always defined if the corresponding function is disabled. */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { WTFLogChannelOff, WTFLogChannelOn } WTFLogChannelState; + +typedef struct { + unsigned mask; + const char *defaultName; + WTFLogChannelState state; +} WTFLogChannel; + +WTF_EXPORT_PRIVATE void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion); +WTF_EXPORT_PRIVATE void WTFReportAssertionFailureWithMessage(const char* file, int line, const char* function, const char* assertion, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6); +WTF_EXPORT_PRIVATE void WTFReportArgumentAssertionFailure(const char* file, int line, const char* function, const char* argName, const char* assertion); +WTF_EXPORT_PRIVATE void WTFReportFatalError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5); +WTF_EXPORT_PRIVATE void WTFReportError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5); +WTF_EXPORT_PRIVATE void WTFLog(WTFLogChannel*, const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); +WTF_EXPORT_PRIVATE void WTFLogVerbose(const char* file, int line, const char* function, WTFLogChannel*, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6); +WTF_EXPORT_PRIVATE void WTFLogAlways(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); + +WTF_EXPORT_PRIVATE void WTFGetBacktrace(void** stack, int* size); +WTF_EXPORT_PRIVATE void WTFReportBacktrace(); +WTF_EXPORT_PRIVATE void WTFPrintBacktrace(void** stack, int size); + +typedef void (*WTFCrashHookFunction)(); +WTF_EXPORT_PRIVATE void WTFSetCrashHook(WTFCrashHookFunction); +WTF_EXPORT_PRIVATE void WTFInvokeCrashHook(); +WTF_EXPORT_PRIVATE void WTFInstallReportBacktraceOnCrashHook(); + +#ifdef __cplusplus +} +#endif + +/* CRASH() - Raises a fatal error resulting in program termination and triggering either the debugger or the crash reporter. + + Use CRASH() in response to known, unrecoverable errors like out-of-memory. + Macro is enabled in both debug and release mode. + To test for unknown errors and verify assumptions, use ASSERT instead, to avoid impacting performance in release builds. + + Signals are ignored by the crash reporter on OS X so we must do better. +*/ +#ifndef CRASH +#if COMPILER(CLANG) +#define CRASH() \ + (WTFReportBacktrace(), \ + WTFInvokeCrashHook(), \ + (*(int *)(uintptr_t)0xbbadbeef = 0), \ + __builtin_trap()) +#else +#define CRASH() \ + (WTFReportBacktrace(), \ + WTFInvokeCrashHook(), \ + (*(int *)(uintptr_t)0xbbadbeef = 0), \ + ((void(*)())0)() /* More reliable, but doesn't say BBADBEEF */ \ + ) +#endif +#endif + +#if COMPILER(CLANG) +#define NO_RETURN_DUE_TO_CRASH NO_RETURN +#else +#define NO_RETURN_DUE_TO_CRASH +#endif + + +/* BACKTRACE + + Print a backtrace to the same location as ASSERT messages. +*/ + +#if BACKTRACE_DISABLED + +#define BACKTRACE() ((void)0) + +#else + +#define BACKTRACE() do { \ + WTFReportBacktrace(); \ +} while(false) + +#endif + +/* ASSERT, ASSERT_NOT_REACHED, ASSERT_UNUSED + + These macros are compiled out of release builds. + Expressions inside them are evaluated in debug builds only. +*/ + +#if OS(WINCE) && !PLATFORM(TORCHMOBILE) +/* FIXME: We include this here only to avoid a conflict with the ASSERT macro. */ +#include +#undef min +#undef max +#undef ERROR +#endif + +#if OS(WINDOWS) +/* FIXME: Change to use something other than ASSERT to avoid this conflict with the underlying platform */ +#undef ASSERT +#endif + +#if ASSERT_DISABLED + +#define ASSERT(assertion) ((void)0) +#define ASSERT_AT(assertion, file, line, function) ((void)0) +#define ASSERT_NOT_REACHED() ((void)0) +#define NO_RETURN_DUE_TO_ASSERT + +#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) +template +inline void assertUnused(T& x) { (void)x; } +#define ASSERT_UNUSED(variable, assertion) (assertUnused(variable)) +#else +#define ASSERT_UNUSED(variable, assertion) ((void)variable) +#endif + +#else + +#define ASSERT(assertion) \ + (!(assertion) ? \ + (WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion), \ + CRASH()) : \ + (void)0) + +#define ASSERT_AT(assertion, file, line, function) \ + (!(assertion) ? \ + (WTFReportAssertionFailure(file, line, function, #assertion), \ + CRASH()) : \ + (void)0) + +#define ASSERT_NOT_REACHED() do { \ + WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, 0); \ + CRASH(); \ +} while (0) + +#define ASSERT_UNUSED(variable, assertion) ASSERT(assertion) + +#define NO_RETURN_DUE_TO_ASSERT NO_RETURN_DUE_TO_CRASH + +#endif + +/* ASSERT_WITH_MESSAGE */ + +#if COMPILER(MSVC7_OR_LOWER) +#define ASSERT_WITH_MESSAGE(assertion) ((void)0) +#elif ASSERT_MSG_DISABLED +#define ASSERT_WITH_MESSAGE(assertion, ...) ((void)0) +#else +#define ASSERT_WITH_MESSAGE(assertion, ...) do \ + if (!(assertion)) { \ + WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ + CRASH(); \ + } \ +while (0) +#endif + +/* ASSERT_WITH_MESSAGE_UNUSED */ + +#if COMPILER(MSVC7_OR_LOWER) +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion) ((void)0) +#elif ASSERT_MSG_DISABLED +#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) +template +inline void assertWithMessageUnused(T& x) { (void)x; } +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) (assertWithMessageUnused(variable)) +#else +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) ((void)variable) +#endif +#else +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) do \ + if (!(assertion)) { \ + WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ + CRASH(); \ + } \ +while (0) +#endif + + +/* ASSERT_ARG */ + +#if ASSERT_ARG_DISABLED + +#define ASSERT_ARG(argName, assertion) ((void)0) + +#else + +#define ASSERT_ARG(argName, assertion) do \ + if (!(assertion)) { \ + WTFReportArgumentAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #argName, #assertion); \ + CRASH(); \ + } \ +while (0) + +#endif + +/* COMPILE_ASSERT */ +#ifndef COMPILE_ASSERT +#if COMPILER_SUPPORTS(C_STATIC_ASSERT) +#define COMPILE_ASSERT(exp, name) _Static_assert((exp), #name) +#else +#define COMPILE_ASSERT(exp, name) typedef int dummy##name [(exp) ? 1 : -1] +#endif +#endif + +/* FATAL */ + +#if COMPILER(MSVC7_OR_LOWER) +#define FATAL() ((void)0) +#elif FATAL_DISABLED +#define FATAL(...) ((void)0) +#else +#define FATAL(...) do { \ + WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__); \ + CRASH(); \ +} while (0) +#endif + +/* LOG_ERROR */ + +#if COMPILER(MSVC7_OR_LOWER) +#define LOG_ERROR() ((void)0) +#elif ERROR_DISABLED +#define LOG_ERROR(...) ((void)0) +#else +#define LOG_ERROR(...) WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__) +#endif + +/* LOG */ + +#if COMPILER(MSVC7_OR_LOWER) +#define LOG() ((void)0) +#elif LOG_DISABLED +#define LOG(channel, ...) ((void)0) +#else +#define LOG(channel, ...) WTFLog(&JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__) +#define JOIN_LOG_CHANNEL_WITH_PREFIX(prefix, channel) JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) +#define JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) prefix ## channel +#endif + +/* LOG_VERBOSE */ + +#if COMPILER(MSVC7_OR_LOWER) +#define LOG_VERBOSE(channel) ((void)0) +#elif LOG_DISABLED +#define LOG_VERBOSE(channel, ...) ((void)0) +#else +#define LOG_VERBOSE(channel, ...) WTFLogVerbose(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, &JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__) +#endif + +/* UNREACHABLE_FOR_PLATFORM */ + +#if COMPILER(CLANG) +// This would be a macro except that its use of #pragma works best around +// a function. Hence it uses macro naming convention. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-noreturn" +static inline void UNREACHABLE_FOR_PLATFORM() +{ + ASSERT_NOT_REACHED(); +} +#pragma clang diagnostic pop +#else +#define UNREACHABLE_FOR_PLATFORM() ASSERT_NOT_REACHED() +#endif + +#endif /* WTF_Assertions_h */ diff --git a/masm/wtf/Atomics.h b/masm/wtf/Atomics.h new file mode 100644 index 0000000000..9f89df1409 --- /dev/null +++ b/masm/wtf/Atomics.h @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Note: The implementations of InterlockedIncrement and InterlockedDecrement are based + * on atomic_increment and atomic_exchange_and_add from the Boost C++ Library. The license + * is virtually identical to the Apple license above but is included here for completeness. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef Atomics_h +#define Atomics_h + +#include +#include +#include + +#if OS(WINDOWS) +#include +#elif OS(DARWIN) +#include +#elif OS(QNX) +#include +#elif OS(ANDROID) +#include +#elif COMPILER(GCC) +#if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2))) && !defined(__LSB_VERSION__) +#include +#else +#include +#endif +#endif + +namespace WTF { + +#if OS(WINDOWS) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +#if COMPILER(MINGW) || COMPILER(MSVC7_OR_LOWER) || OS(WINCE) +inline int atomicIncrement(int* addend) { return InterlockedIncrement(reinterpret_cast(addend)); } +inline int atomicDecrement(int* addend) { return InterlockedDecrement(reinterpret_cast(addend)); } +#else +inline int atomicIncrement(int volatile* addend) { return InterlockedIncrement(reinterpret_cast(addend)); } +inline int atomicDecrement(int volatile* addend) { return InterlockedDecrement(reinterpret_cast(addend)); } +#endif + +#elif OS(DARWIN) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +inline int atomicIncrement(int volatile* addend) { return OSAtomicIncrement32Barrier(const_cast(addend)); } +inline int atomicDecrement(int volatile* addend) { return OSAtomicDecrement32Barrier(const_cast(addend)); } + +#elif OS(QNX) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +// Note, atomic_{add, sub}_value() return the previous value of addend's content. +inline int atomicIncrement(int volatile* addend) { return static_cast(atomic_add_value(reinterpret_cast(addend), 1)) + 1; } +inline int atomicDecrement(int volatile* addend) { return static_cast(atomic_sub_value(reinterpret_cast(addend), 1)) - 1; } + +#elif OS(ANDROID) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +// Note, __atomic_{inc, dec}() return the previous value of addend's content. +inline int atomicIncrement(int volatile* addend) { return __atomic_inc(addend) + 1; } +inline int atomicDecrement(int volatile* addend) { return __atomic_dec(addend) - 1; } + +#elif COMPILER(GCC) && !CPU(SPARC64) // sizeof(_Atomic_word) != sizeof(int) on sparc64 gcc +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +inline int atomicIncrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, 1) + 1; } +inline int atomicDecrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, -1) - 1; } + +#endif + +#if OS(WINDOWS) +inline bool weakCompareAndSwap(volatile unsigned* location, unsigned expected, unsigned newValue) +{ +#if OS(WINCE) + return InterlockedCompareExchange(reinterpret_cast(const_cast(location)), static_cast(newValue), static_cast(expected)) == static_cast(expected); +#else + return InterlockedCompareExchange(reinterpret_cast(location), static_cast(newValue), static_cast(expected)) == static_cast(expected); +#endif +} + +inline bool weakCompareAndSwap(void*volatile* location, void* expected, void* newValue) +{ + return InterlockedCompareExchangePointer(location, newValue, expected) == expected; +} +#else // OS(WINDOWS) --> not windows +#if COMPILER(GCC) && !COMPILER(CLANG) // Work around a gcc bug +inline bool weakCompareAndSwap(volatile unsigned* location, unsigned expected, unsigned newValue) +#else +inline bool weakCompareAndSwap(unsigned* location, unsigned expected, unsigned newValue) +#endif +{ +#if ENABLE(COMPARE_AND_SWAP) + bool result; +#if CPU(X86) || CPU(X86_64) + asm volatile( + "lock; cmpxchgl %3, %2\n\t" + "sete %1" + : "+a"(expected), "=q"(result), "+m"(*location) + : "r"(newValue) + : "memory" + ); +#elif CPU(ARM_THUMB2) + unsigned tmp; + asm volatile( + "movw %1, #1\n\t" + "ldrex %2, %0\n\t" + "cmp %3, %2\n\t" + "bne.n 0f\n\t" + "strex %1, %4, %0\n\t" + "0:" + : "+Q"(*location), "=&r"(result), "=&r"(tmp) + : "r"(expected), "r"(newValue) + : "memory"); + result = !result; +#else +#error "Bad architecture for compare and swap." +#endif + return result; +#else + UNUSED_PARAM(location); + UNUSED_PARAM(expected); + UNUSED_PARAM(newValue); + CRASH(); + return 0; +#endif +} + +inline bool weakCompareAndSwap(void*volatile* location, void* expected, void* newValue) +{ +#if ENABLE(COMPARE_AND_SWAP) +#if CPU(X86_64) + bool result; + asm volatile( + "lock; cmpxchgq %3, %2\n\t" + "sete %1" + : "+a"(expected), "=q"(result), "+m"(*location) + : "r"(newValue) + : "memory" + ); + return result; +#else + return weakCompareAndSwap(bitwise_cast(location), bitwise_cast(expected), bitwise_cast(newValue)); +#endif +#else // ENABLE(COMPARE_AND_SWAP) + UNUSED_PARAM(location); + UNUSED_PARAM(expected); + UNUSED_PARAM(newValue); + CRASH(); + return 0; +#endif // ENABLE(COMPARE_AND_SWAP) +} +#endif // OS(WINDOWS) (end of the not-windows case) + +inline bool weakCompareAndSwapUIntPtr(volatile uintptr_t* location, uintptr_t expected, uintptr_t newValue) +{ + return weakCompareAndSwap(reinterpret_cast(location), reinterpret_cast(expected), reinterpret_cast(newValue)); +} + +#if CPU(ARM_THUMB2) + +inline void memoryBarrierAfterLock() +{ + asm volatile("dmb" ::: "memory"); +} + +inline void memoryBarrierBeforeUnlock() +{ + asm volatile("dmb" ::: "memory"); +} + +#else + +inline void memoryBarrierAfterLock() { } +inline void memoryBarrierBeforeUnlock() { } + +#endif + +} // namespace WTF + +#if USE(LOCKFREE_THREADSAFEREFCOUNTED) +using WTF::atomicDecrement; +using WTF::atomicIncrement; +#endif + +#endif // Atomics_h diff --git a/masm/wtf/CheckedArithmetic.h b/masm/wtf/CheckedArithmetic.h new file mode 100644 index 0000000000..ef8a03695e --- /dev/null +++ b/masm/wtf/CheckedArithmetic.h @@ -0,0 +1,708 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CheckedArithmetic_h +#define CheckedArithmetic_h + +#include +#include + +#include +#include + +/* Checked + * + * This class provides a mechanism to perform overflow-safe integer arithmetic + * without having to manually ensure that you have all the required bounds checks + * directly in your code. + * + * There are two modes of operation: + * - The default is Checked, and crashes at the point + * and overflow has occurred. + * - The alternative is Checked, which uses an additional + * byte of storage to track whether an overflow has occurred, subsequent + * unchecked operations will crash if an overflow has occured + * + * It is possible to provide a custom overflow handler, in which case you need + * to support these functions: + * - void overflowed(); + * This function is called when an operation has produced an overflow. + * - bool hasOverflowed(); + * This function must return true if overflowed() has been called on an + * instance and false if it has not. + * - void clearOverflow(); + * Used to reset overflow tracking when a value is being overwritten with + * a new value. + * + * Checked works for all integer types, with the following caveats: + * - Mixing signedness of operands is only supported for types narrower than + * 64bits. + * - It does have a performance impact, so tight loops may want to be careful + * when using it. + * + */ + +namespace WTF { + +class CrashOnOverflow { +protected: + NO_RETURN_DUE_TO_CRASH void overflowed() + { + CRASH(); + } + + void clearOverflow() { } + +public: + bool hasOverflowed() const { return false; } +}; + +class RecordOverflow { +protected: + RecordOverflow() + : m_overflowed(false) + { + } + + void overflowed() + { + m_overflowed = true; + } + + void clearOverflow() + { + m_overflowed = false; + } + +public: + bool hasOverflowed() const { return m_overflowed; } + +private: + unsigned char m_overflowed; +}; + +template class Checked; +template struct RemoveChecked; +template struct RemoveChecked >; + +template ::is_signed, bool sourceSigned = std::numeric_limits::is_signed> struct BoundsChecker; +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Same signedness so implicit type conversion will always increase precision + // to widest type + return value <= std::numeric_limits::max(); + } +}; + +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Same signedness so implicit type conversion will always increase precision + // to widest type + return std::numeric_limits::min() <= value && value <= std::numeric_limits::max(); + } +}; + +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Target is unsigned so any value less than zero is clearly unsafe + if (value < 0) + return false; + // If our (unsigned) Target is the same or greater width we can + // convert value to type Target without losing precision + if (sizeof(Target) >= sizeof(Source)) + return static_cast(value) <= std::numeric_limits::max(); + // The signed Source type has greater precision than the target so + // max(Target) -> Source will widen. + return value <= static_cast(std::numeric_limits::max()); + } +}; + +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Signed target with an unsigned source + if (sizeof(Target) <= sizeof(Source)) + return value <= static_cast(std::numeric_limits::max()); + // Target is Wider than Source so we're guaranteed to fit any value in + // unsigned Source + return true; + } +}; + +template ::value> struct BoundsCheckElider; +template struct BoundsCheckElider { + static bool inBounds(Source) { return true; } +}; +template struct BoundsCheckElider : public BoundsChecker { +}; + +template static inline bool isInBounds(Source value) +{ + return BoundsCheckElider::inBounds(value); +} + +template struct RemoveChecked { + typedef T CleanType; + static const CleanType DefaultValue = 0; +}; + +template struct RemoveChecked > { + typedef typename RemoveChecked::CleanType CleanType; + static const CleanType DefaultValue = 0; +}; + +template struct RemoveChecked > { + typedef typename RemoveChecked::CleanType CleanType; + static const CleanType DefaultValue = 0; +}; + +// The ResultBase and SignednessSelector are used to workaround typeof not being +// available in MSVC +template sizeof(V)), bool sameSize = (sizeof(U) == sizeof(V))> struct ResultBase; +template struct ResultBase { + typedef U ResultType; +}; + +template struct ResultBase { + typedef V ResultType; +}; + +template struct ResultBase { + typedef U ResultType; +}; + +template ::is_signed, bool vIsSigned = std::numeric_limits::is_signed> struct SignednessSelector; +template struct SignednessSelector { + typedef U ResultType; +}; + +template struct SignednessSelector { + typedef U ResultType; +}; + +template struct SignednessSelector { + typedef V ResultType; +}; + +template struct SignednessSelector { + typedef U ResultType; +}; + +template struct ResultBase { + typedef typename SignednessSelector::ResultType ResultType; +}; + +template struct Result : ResultBase::CleanType, typename RemoveChecked::CleanType> { +}; + +template ::ResultType, + bool lhsSigned = std::numeric_limits::is_signed, bool rhsSigned = std::numeric_limits::is_signed> struct ArithmeticOperations; + +template struct ArithmeticOperations { + // LHS and RHS are signed types + + // Helper function + static inline bool signsMatch(LHS lhs, RHS rhs) + { + return (lhs ^ rhs) >= 0; + } + + static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if ((std::numeric_limits::max() - rhs) < lhs) + return false; + } else { + ResultType temp = lhs - std::numeric_limits::min(); + if (rhs < -temp) + return false; + } + } // if the signs do not match this operation can't overflow + result = lhs + rhs; + return true; + } + + static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (!signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if (lhs > std::numeric_limits::max() + rhs) + return false; + } else { + if (rhs > std::numeric_limits::max() + lhs) + return false; + } + } // if the signs match this operation can't overflow + result = lhs - rhs; + return true; + } + + static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if (lhs && (std::numeric_limits::max() / lhs) < rhs) + return false; + } else { + if (lhs == std::numeric_limits::min() || rhs == std::numeric_limits::min()) + return false; + if ((std::numeric_limits::max() / -lhs) < -rhs) + return false; + } + } else { + if (lhs < 0) { + if (rhs && lhs < (std::numeric_limits::min() / rhs)) + return false; + } else { + if (lhs && rhs < (std::numeric_limits::min() / lhs)) + return false; + } + } + result = lhs * rhs; + return true; + } + + static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } + +}; + +template struct ArithmeticOperations { + // LHS and RHS are unsigned types so bounds checks are nice and easy + static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs + rhs; + if (temp < lhs) + return false; + result = temp; + return true; + } + + static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs - rhs; + if (temp > lhs) + return false; + result = temp; + return true; + } + + static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs * rhs; + if (temp < lhs) + return false; + result = temp; + return true; + } + + static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } + +}; + +template struct ArithmeticOperations { + static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs + rhs; + if (temp < std::numeric_limits::min()) + return false; + if (temp > std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs - rhs; + if (temp < std::numeric_limits::min()) + return false; + if (temp > std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs * rhs; + if (temp < std::numeric_limits::min()) + return false; + if (temp > std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool equals(int lhs, unsigned rhs) + { + return static_cast(lhs) == static_cast(rhs); + } +}; + +template struct ArithmeticOperations { + static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations::add(rhs, lhs, result); + } + + static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations::sub(lhs, rhs, result); + } + + static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations::multiply(rhs, lhs, result); + } + + static inline bool equals(unsigned lhs, int rhs) + { + return ArithmeticOperations::equals(rhs, lhs); + } +}; + +template static inline bool safeAdd(U lhs, V rhs, R& result) +{ + return ArithmeticOperations::add(lhs, rhs, result); +} + +template static inline bool safeSub(U lhs, V rhs, R& result) +{ + return ArithmeticOperations::sub(lhs, rhs, result); +} + +template static inline bool safeMultiply(U lhs, V rhs, R& result) +{ + return ArithmeticOperations::multiply(lhs, rhs, result); +} + +template static inline bool safeEquals(U lhs, V rhs) +{ + return ArithmeticOperations::equals(lhs, rhs); +} + +enum ResultOverflowedTag { ResultOverflowed }; + +// FIXME: Needed to workaround http://llvm.org/bugs/show_bug.cgi?id=10801 +static inline bool workAroundClangBug() { return true; } + +template class Checked : public OverflowHandler { +public: + template friend class Checked; + Checked() + : m_value(0) + { + } + + Checked(ResultOverflowedTag) + : m_value(0) + { + // FIXME: Remove this when clang fixes http://llvm.org/bugs/show_bug.cgi?id=10801 + if (workAroundClangBug()) + this->overflowed(); + } + + template Checked(U value) + { + if (!isInBounds(value)) + this->overflowed(); + m_value = static_cast(value); + } + + template Checked(const Checked& rhs) + : m_value(rhs.m_value) + { + if (rhs.hasOverflowed()) + this->overflowed(); + } + + template Checked(const Checked& rhs) + : OverflowHandler(rhs) + { + if (!isInBounds(rhs.m_value)) + this->overflowed(); + m_value = static_cast(rhs.m_value); + } + + template Checked(const Checked& rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + if (!isInBounds(rhs.m_value)) + this->overflowed(); + m_value = static_cast(rhs.m_value); + } + + const Checked& operator=(Checked rhs) + { + this->clearOverflow(); + if (rhs.hasOverflowed()) + this->overflowed(); + m_value = static_cast(rhs.m_value); + return *this; + } + + template const Checked& operator=(U value) + { + return *this = Checked(value); + } + + template const Checked& operator=(const Checked& rhs) + { + return *this = Checked(rhs); + } + + // prefix + const Checked& operator++() + { + if (m_value == std::numeric_limits::max()) + this->overflowed(); + m_value++; + return *this; + } + + const Checked& operator--() + { + if (m_value == std::numeric_limits::min()) + this->overflowed(); + m_value--; + return *this; + } + + // postfix operators + const Checked operator++(int) + { + if (m_value == std::numeric_limits::max()) + this->overflowed(); + return Checked(m_value++); + } + + const Checked operator--(int) + { + if (m_value == std::numeric_limits::min()) + this->overflowed(); + return Checked(m_value--); + } + + // Boolean operators + bool operator!() const + { + if (this->hasOverflowed()) + CRASH(); + return !m_value; + } + + typedef void* (Checked::*UnspecifiedBoolType); + operator UnspecifiedBoolType*() const + { + if (this->hasOverflowed()) + CRASH(); + return (m_value) ? reinterpret_cast(1) : 0; + } + + // Value accessors. unsafeGet() will crash if there's been an overflow. + T unsafeGet() const + { + if (this->hasOverflowed()) + CRASH(); + return m_value; + } + + bool safeGet(T& value) const WARN_UNUSED_RETURN + { + value = m_value; + return this->hasOverflowed(); + } + + // Mutating assignment + template const Checked operator+=(U rhs) + { + if (!safeAdd(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + template const Checked operator-=(U rhs) + { + if (!safeSub(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + template const Checked operator*=(U rhs) + { + if (!safeMultiply(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + const Checked operator*=(double rhs) + { + double result = rhs * m_value; + // Handle +/- infinity and NaN + if (!(std::numeric_limits::min() <= result && std::numeric_limits::max() >= result)) + this->overflowed(); + m_value = (T)result; + return *this; + } + + const Checked operator*=(float rhs) + { + return *this *= (double)rhs; + } + + template const Checked operator+=(Checked rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this += rhs.m_value; + } + + template const Checked operator-=(Checked rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this -= rhs.m_value; + } + + template const Checked operator*=(Checked rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this *= rhs.m_value; + } + + // Equality comparisons + template bool operator==(Checked rhs) + { + return unsafeGet() == rhs.unsafeGet(); + } + + template bool operator==(U rhs) + { + if (this->hasOverflowed()) + this->overflowed(); + return safeEquals(m_value, rhs); + } + + template const Checked operator==(Checked rhs) + { + return unsafeGet() == Checked(rhs.unsafeGet()); + } + + template bool operator!=(U rhs) + { + return !(*this == rhs); + } + +private: + // Disallow implicit conversion of floating point to integer types + Checked(float); + Checked(double); + void operator=(float); + void operator=(double); + void operator+=(float); + void operator+=(double); + void operator-=(float); + void operator-=(double); + T m_value; +}; + +template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, Checked rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result::ResultType result = 0; + overflowed |= !safeAdd(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, Checked rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result::ResultType result = 0; + overflowed |= !safeSub(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, Checked rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result::ResultType result = 0; + overflowed |= !safeMultiply(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, V rhs) +{ + return lhs + Checked(rhs); +} + +template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, V rhs) +{ + return lhs - Checked(rhs); +} + +template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, V rhs) +{ + return lhs * Checked(rhs); +} + +template static inline Checked::ResultType, OverflowHandler> operator+(U lhs, Checked rhs) +{ + return Checked(lhs) + rhs; +} + +template static inline Checked::ResultType, OverflowHandler> operator-(U lhs, Checked rhs) +{ + return Checked(lhs) - rhs; +} + +template static inline Checked::ResultType, OverflowHandler> operator*(U lhs, Checked rhs) +{ + return Checked(lhs) * rhs; +} + +} + +using WTF::Checked; +using WTF::RecordOverflow; + +#endif diff --git a/masm/wtf/Compiler.h b/masm/wtf/Compiler.h new file mode 100644 index 0000000000..f40e15e601 --- /dev/null +++ b/masm/wtf/Compiler.h @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_Compiler_h +#define WTF_Compiler_h + +/* COMPILER() - the compiler being used to build the project */ +#define COMPILER(WTF_FEATURE) (defined WTF_COMPILER_##WTF_FEATURE && WTF_COMPILER_##WTF_FEATURE) + +/* COMPILER_SUPPORTS() - whether the compiler being used to build the project supports the given feature. */ +#define COMPILER_SUPPORTS(WTF_COMPILER_FEATURE) (defined WTF_COMPILER_SUPPORTS_##WTF_COMPILER_FEATURE && WTF_COMPILER_SUPPORTS_##WTF_COMPILER_FEATURE) + +/* COMPILER_QUIRK() - whether the compiler being used to build the project requires a given quirk. */ +#define COMPILER_QUIRK(WTF_COMPILER_QUIRK) (defined WTF_COMPILER_QUIRK_##WTF_COMPILER_QUIRK && WTF_COMPILER_QUIRK_##WTF_COMPILER_QUIRK) + +/* ==== COMPILER() - the compiler being used to build the project ==== */ + +/* COMPILER(CLANG) - Clang */ +#if defined(__clang__) +#define WTF_COMPILER_CLANG 1 + +#ifndef __has_extension +#define __has_extension __has_feature /* Compatibility with older versions of clang */ +#endif + +#define CLANG_PRAGMA(PRAGMA) _Pragma(PRAGMA) + +/* Specific compiler features */ +#define WTF_COMPILER_SUPPORTS_CXX_VARIADIC_TEMPLATES __has_extension(cxx_variadic_templates) + +/* There is a bug in clang that comes with Xcode 4.2 where AtomicStrings can't be implicitly converted to Strings + in the presence of move constructors and/or move assignment operators. This bug has been fixed in Xcode 4.3 clang, so we + check for both cxx_rvalue_references as well as the unrelated cxx_nonstatic_member_init feature which we know was added in 4.3 */ +#define WTF_COMPILER_SUPPORTS_CXX_RVALUE_REFERENCES __has_extension(cxx_rvalue_references) && __has_extension(cxx_nonstatic_member_init) + +#define WTF_COMPILER_SUPPORTS_CXX_DELETED_FUNCTIONS __has_extension(cxx_deleted_functions) +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR __has_feature(cxx_nullptr) +#define WTF_COMPILER_SUPPORTS_BLOCKS __has_feature(blocks) +#define WTF_COMPILER_SUPPORTS_C_STATIC_ASSERT __has_extension(c_static_assert) +#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL __has_extension(cxx_override_control) + +#define WTF_COMPILER_SUPPORTS_HAS_TRIVIAL_DESTRUCTOR __has_extension(has_trivial_destructor) + +#endif + +#ifndef CLANG_PRAGMA +#define CLANG_PRAGMA(PRAGMA) +#endif + +/* COMPILER(MSVC) - Microsoft Visual C++ */ +/* COMPILER(MSVC7_OR_LOWER) - Microsoft Visual C++ 2003 or lower*/ +/* COMPILER(MSVC9_OR_LOWER) - Microsoft Visual C++ 2008 or lower*/ +#if defined(_MSC_VER) +#define WTF_COMPILER_MSVC 1 +#if _MSC_VER < 1400 +#define WTF_COMPILER_MSVC7_OR_LOWER 1 +#elif _MSC_VER < 1600 +#define WTF_COMPILER_MSVC9_OR_LOWER 1 +#endif + +/* Specific compiler features */ +#if !COMPILER(CLANG) && _MSC_VER >= 1600 +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 +#endif + +#if !COMPILER(CLANG) +#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL 1 +#define WTF_COMPILER_QUIRK_FINAL_IS_CALLED_SEALED 1 +#endif + +#endif + +/* COMPILER(RVCT) - ARM RealView Compilation Tools */ +/* COMPILER(RVCT4_OR_GREATER) - ARM RealView Compilation Tools 4.0 or greater */ +#if defined(__CC_ARM) || defined(__ARMCC__) +#define WTF_COMPILER_RVCT 1 +#define RVCT_VERSION_AT_LEAST(major, minor, patch, build) (__ARMCC_VERSION >= (major * 100000 + minor * 10000 + patch * 1000 + build)) +#else +/* Define this for !RVCT compilers, just so we can write things like RVCT_VERSION_AT_LEAST(3, 0, 0, 0). */ +#define RVCT_VERSION_AT_LEAST(major, minor, patch, build) 0 +#endif + +/* COMPILER(GCCE) - GNU Compiler Collection for Embedded */ +#if defined(__GCCE__) +#define WTF_COMPILER_GCCE 1 +#define GCCE_VERSION (__GCCE__ * 10000 + __GCCE_MINOR__ * 100 + __GCCE_PATCHLEVEL__) +#define GCCE_VERSION_AT_LEAST(major, minor, patch) (GCCE_VERSION >= (major * 10000 + minor * 100 + patch)) +#endif + +/* COMPILER(GCC) - GNU Compiler Collection */ +/* --gnu option of the RVCT compiler also defines __GNUC__ */ +#if defined(__GNUC__) && !COMPILER(RVCT) +#define WTF_COMPILER_GCC 1 +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#define GCC_VERSION_AT_LEAST(major, minor, patch) (GCC_VERSION >= (major * 10000 + minor * 100 + patch)) +#else +/* Define this for !GCC compilers, just so we can write things like GCC_VERSION_AT_LEAST(4, 1, 0). */ +#define GCC_VERSION_AT_LEAST(major, minor, patch) 0 +#endif + +/* Specific compiler features */ +#if COMPILER(GCC) && !COMPILER(CLANG) +#if GCC_VERSION_AT_LEAST(4, 7, 0) && __cplusplus >= 201103L +#define WTF_COMPILER_SUPPORTS_CXX_RVALUE_REFERENCES 1 +#define WTF_COMPILER_SUPPORTS_CXX_DELETED_FUNCTIONS 1 +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 +#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL 1 +#define WTF_COMPILER_QUIRK_GCC11_GLOBAL_ISINF_ISNAN 1 + +#elif GCC_VERSION_AT_LEAST(4, 6, 0) && defined(__GXX_EXPERIMENTAL_CXX0X__) +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 +#define WTF_COMPILER_QUIRK_GCC11_GLOBAL_ISINF_ISNAN 1 +#endif + +#endif + +/* COMPILER(MINGW) - MinGW GCC */ +/* COMPILER(MINGW64) - mingw-w64 GCC - only used as additional check to exclude mingw.org specific functions */ +#if defined(__MINGW32__) +#define WTF_COMPILER_MINGW 1 +#include <_mingw.h> /* private MinGW header */ + #if defined(__MINGW64_VERSION_MAJOR) /* best way to check for mingw-w64 vs mingw.org */ + #define WTF_COMPILER_MINGW64 1 + #endif /* __MINGW64_VERSION_MAJOR */ +#endif /* __MINGW32__ */ + +/* COMPILER(INTEL) - Intel C++ Compiler */ +#if defined(__INTEL_COMPILER) +#define WTF_COMPILER_INTEL 1 +#endif + +/* COMPILER(SUNCC) */ +#if defined(__SUNPRO_CC) || defined(__SUNPRO_C) +#define WTF_COMPILER_SUNCC 1 +#endif + +/* ==== Compiler features ==== */ + + +/* ALWAYS_INLINE */ + +#ifndef ALWAYS_INLINE +#if COMPILER(GCC) && defined(NDEBUG) && !COMPILER(MINGW) +#define ALWAYS_INLINE inline __attribute__((__always_inline__)) +#elif (COMPILER(MSVC) || COMPILER(RVCT)) && defined(NDEBUG) +#define ALWAYS_INLINE __forceinline +#else +#define ALWAYS_INLINE inline +#endif +#endif + + +/* NEVER_INLINE */ + +#ifndef NEVER_INLINE +#if COMPILER(GCC) +#define NEVER_INLINE __attribute__((__noinline__)) +#elif COMPILER(RVCT) +#define NEVER_INLINE __declspec(noinline) +#else +#define NEVER_INLINE +#endif +#endif + + +/* UNLIKELY */ + +#ifndef UNLIKELY +#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(3, 0, 0, 0) && defined(__GNUC__)) +#define UNLIKELY(x) __builtin_expect((x), 0) +#else +#define UNLIKELY(x) (x) +#endif +#endif + + +/* LIKELY */ + +#ifndef LIKELY +#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(3, 0, 0, 0) && defined(__GNUC__)) +#define LIKELY(x) __builtin_expect((x), 1) +#else +#define LIKELY(x) (x) +#endif +#endif + + +/* NO_RETURN */ + + +#ifndef NO_RETURN +#if COMPILER(GCC) +#define NO_RETURN __attribute((__noreturn__)) +#elif COMPILER(MSVC) || COMPILER(RVCT) +#define NO_RETURN __declspec(noreturn) +#else +#define NO_RETURN +#endif +#endif + + +/* NO_RETURN_WITH_VALUE */ + +#ifndef NO_RETURN_WITH_VALUE +#if !COMPILER(MSVC) +#define NO_RETURN_WITH_VALUE NO_RETURN +#else +#define NO_RETURN_WITH_VALUE +#endif +#endif + + +/* WARN_UNUSED_RETURN */ + +#if COMPILER(GCC) +#define WARN_UNUSED_RETURN __attribute__ ((warn_unused_result)) +#else +#define WARN_UNUSED_RETURN +#endif + +/* OVERRIDE and FINAL */ + +#if COMPILER_SUPPORTS(CXX_OVERRIDE_CONTROL) +#define OVERRIDE override + +#if COMPILER_QUIRK(FINAL_IS_CALLED_SEALED) +#define FINAL sealed +#else +#define FINAL final +#endif + +#else +#define OVERRIDE +#define FINAL +#endif + +/* REFERENCED_FROM_ASM */ + +#ifndef REFERENCED_FROM_ASM +#if COMPILER(GCC) +#define REFERENCED_FROM_ASM __attribute__((used)) +#else +#define REFERENCED_FROM_ASM +#endif +#endif + +/* OBJC_CLASS */ + +#ifndef OBJC_CLASS +#ifdef __OBJC__ +#define OBJC_CLASS @class +#else +#define OBJC_CLASS class +#endif +#endif + +/* ABI */ +#if defined(__ARM_EABI__) || defined(__EABI__) +#define WTF_COMPILER_SUPPORTS_EABI 1 +#endif + +#endif /* WTF_Compiler_h */ diff --git a/masm/wtf/CryptographicallyRandomNumber.h b/masm/wtf/CryptographicallyRandomNumber.h new file mode 100644 index 0000000000..2262b6c3b3 --- /dev/null +++ b/masm/wtf/CryptographicallyRandomNumber.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_CryptographicallyRandomNumber_h +#define WTF_CryptographicallyRandomNumber_h + +#include + +namespace WTF { + +#if USE(OS_RANDOMNESS) +WTF_EXPORT_PRIVATE uint32_t cryptographicallyRandomNumber(); +WTF_EXPORT_PRIVATE void cryptographicallyRandomValues(void* buffer, size_t length); +#endif + +} + +#if USE(OS_RANDOMNESS) +using WTF::cryptographicallyRandomNumber; +using WTF::cryptographicallyRandomValues; +#endif + +#endif diff --git a/masm/wtf/DataLog.h b/masm/wtf/DataLog.h new file mode 100644 index 0000000000..11e1d3e55d --- /dev/null +++ b/masm/wtf/DataLog.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DataLog_h +#define DataLog_h + +#include +#include +#include +#include + +namespace WTF { + +WTF_EXPORT_PRIVATE FILE* dataFile(); + +WTF_EXPORT_PRIVATE void dataLogV(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(1, 0); +WTF_EXPORT_PRIVATE void dataLog(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); +WTF_EXPORT_PRIVATE void dataLogString(const char*); + +} // namespace WTF + +using WTF::dataLog; +using WTF::dataLogString; + +#endif // DataLog_h + diff --git a/masm/wtf/DynamicAnnotations.h b/masm/wtf/DynamicAnnotations.h new file mode 100644 index 0000000000..38acce35e6 --- /dev/null +++ b/masm/wtf/DynamicAnnotations.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_DynamicAnnotations_h +#define WTF_DynamicAnnotations_h + +/* This file defines dynamic annotations for use with dynamic analysis + * tool such as ThreadSanitizer, Valgrind, etc. + * + * Dynamic annotation is a source code annotation that affects + * the generated code (that is, the annotation is not a comment). + * Each such annotation is attached to a particular + * instruction and/or to a particular object (address) in the program. + * + * By using dynamic annotations a developer can give more details to the dynamic + * analysis tool to improve its precision. + * + * In C/C++ program the annotations are represented as C macros. + * With the default build flags, these macros are empty, hence don't affect + * performance of a compiled binary. + * If dynamic annotations are enabled, they just call no-op functions. + * The dynamic analysis tools can intercept these functions and replace them + * with their own implementations. + * + * See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations for more information. + */ + +#if USE(DYNAMIC_ANNOTATIONS) +/* Tell data race detector that we're not interested in reports on the given address range. */ +#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) WTFAnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description) +#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) WTFAnnotateBenignRaceSized(__FILE__, __LINE__, pointer, sizeof(*(pointer)), description) + +/* Annotations for user-defined synchronization mechanisms. + * These annotations can be used to define happens-before arcs in user-defined + * synchronization mechanisms: the race detector will infer an arc from + * the former to the latter when they share the same argument pointer. + * + * The most common case requiring annotations is atomic reference counting: + * bool deref() { + * ANNOTATE_HAPPENS_BEFORE(&m_refCount); + * if (!atomicDecrement(&m_refCount)) { + * // m_refCount is now 0 + * ANNOTATE_HAPPENS_AFTER(&m_refCount); + * // "return true; happens-after each atomicDecrement of m_refCount" + * return true; + * } + * return false; + * } + */ +#define WTF_ANNOTATE_HAPPENS_BEFORE(address) WTFAnnotateHappensBefore(__FILE__, __LINE__, address) +#define WTF_ANNOTATE_HAPPENS_AFTER(address) WTFAnnotateHappensAfter(__FILE__, __LINE__, address) + +#ifdef __cplusplus +extern "C" { +#endif +/* Don't use these directly, use the above macros instead. */ +void WTFAnnotateBenignRaceSized(const char* file, int line, const volatile void* memory, long size, const char* description); +void WTFAnnotateHappensBefore(const char* file, int line, const volatile void* address); +void WTFAnnotateHappensAfter(const char* file, int line, const volatile void* address); +#ifdef __cplusplus +} // extern "C" +#endif + +#else // USE(DYNAMIC_ANNOTATIONS) +/* These macros are empty when dynamic annotations are not enabled so you can + * use them without affecting the performance of release binaries. */ +#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) +#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) +#define WTF_ANNOTATE_HAPPENS_BEFORE(address) +#define WTF_ANNOTATE_HAPPENS_AFTER(address) +#endif // USE(DYNAMIC_ANNOTATIONS) + +#endif // WTF_DynamicAnnotations_h diff --git a/masm/wtf/FastAllocBase.h b/masm/wtf/FastAllocBase.h new file mode 100644 index 0000000000..a0804ad3d7 --- /dev/null +++ b/masm/wtf/FastAllocBase.h @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2008, 2009 Paul Pedriana . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FastAllocBase_h +#define FastAllocBase_h + +// Provides customizable overrides of fastMalloc/fastFree and operator new/delete +// +// Provided functionality: +// Macro: WTF_MAKE_FAST_ALLOCATED +// namespace WTF { +// +// T* fastNew(); +// T* fastNew(arg); +// T* fastNew(arg, arg); +// T* fastNewArray(count); +// void fastDelete(T* p); +// void fastDeleteArray(T* p); +// void fastNonNullDelete(T* p); +// void fastNonNullDeleteArray(T* p); +// } +// +// FastDelete assumes that the underlying +// +// Example usage: +// class Widget { +// WTF_MAKE_FAST_ALLOCATED +// ... +// }; +// +// struct Data { +// WTF_MAKE_FAST_ALLOCATED +// public: +// ... +// }; +// +// char* charPtr = fastNew(); +// fastDelete(charPtr); +// +// char* charArrayPtr = fastNewArray(37); +// fastDeleteArray(charArrayPtr); +// +// void** voidPtrPtr = fastNew(); +// fastDelete(voidPtrPtr); +// +// void** voidPtrArrayPtr = fastNewArray(37); +// fastDeleteArray(voidPtrArrayPtr); +// +// POD* podPtr = fastNew(); +// fastDelete(podPtr); +// +// POD* podArrayPtr = fastNewArray(37); +// fastDeleteArray(podArrayPtr); +// +// Object* objectPtr = fastNew(); +// fastDelete(objectPtr); +// +// Object* objectArrayPtr = fastNewArray(37); +// fastDeleteArray(objectArrayPtr); +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#define WTF_MAKE_FAST_ALLOCATED \ +public: \ + void* operator new(size_t, void* p) { return p; } \ + void* operator new[](size_t, void* p) { return p; } \ + \ + void* operator new(size_t size) \ + { \ + void* p = ::WTF::fastMalloc(size); \ + ::WTF::fastMallocMatchValidateMalloc(p, ::WTF::Internal::AllocTypeClassNew); \ + return p; \ + } \ + \ + void operator delete(void* p) \ + { \ + ::WTF::fastMallocMatchValidateFree(p, ::WTF::Internal::AllocTypeClassNew); \ + ::WTF::fastFree(p); \ + } \ + \ + void* operator new[](size_t size) \ + { \ + void* p = ::WTF::fastMalloc(size); \ + ::WTF::fastMallocMatchValidateMalloc(p, ::WTF::Internal::AllocTypeClassNewArray); \ + return p; \ + } \ + \ + void operator delete[](void* p) \ + { \ + ::WTF::fastMallocMatchValidateFree(p, ::WTF::Internal::AllocTypeClassNewArray); \ + ::WTF::fastFree(p); \ + } \ + void* operator new(size_t, NotNullTag, void* location) \ + { \ + ASSERT(location); \ + return location; \ + } \ +private: \ +typedef int __thisIsHereToForceASemicolonAfterThisMacro + +namespace WTF { + + // fastNew / fastDelete + + template + inline T* fastNew() + { + void* p = fastMalloc(sizeof(T)); + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); + return ::new (p) T; + } + + template + inline T* fastNew(Arg1 arg1) + { + void* p = fastMalloc(sizeof(T)); + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); + return ::new (p) T(arg1); + } + + template + inline T* fastNew(Arg1 arg1, Arg2 arg2) + { + void* p = fastMalloc(sizeof(T)); + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); + return ::new (p) T(arg1, arg2); + } + + template + inline T* fastNew(Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + void* p = fastMalloc(sizeof(T)); + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); + return ::new (p) T(arg1, arg2, arg3); + } + + template + inline T* fastNew(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + void* p = fastMalloc(sizeof(T)); + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); + return ::new (p) T(arg1, arg2, arg3, arg4); + } + + template + inline T* fastNew(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + { + void* p = fastMalloc(sizeof(T)); + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); + return ::new (p) T(arg1, arg2, arg3, arg4, arg5); + } + + namespace Internal { + + // We define a union of pointer to an integer and pointer to T. + // When non-POD arrays are allocated we add a few leading bytes to tell what + // the size of the array is. We return to the user the pointer to T. + // The way to think of it is as if we allocate a struct like so: + // struct Array { + // AllocAlignmentInteger m_size; + // T m_T[array count]; + // }; + + template + union ArraySize { + AllocAlignmentInteger* size; + T* t; + }; + + // This is a support template for fastNewArray. + // This handles the case wherein T has a trivial ctor and a trivial dtor. + template + struct NewArrayImpl { + static T* fastNewArray(size_t count) + { + T* p = static_cast(fastMalloc(sizeof(T) * count)); + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNewArray); + return p; + } + }; + + // This is a support template for fastNewArray. + // This handles the case wherein T has a non-trivial ctor and a trivial dtor. + template + struct NewArrayImpl { + static T* fastNewArray(size_t count) + { + T* p = static_cast(fastMalloc(sizeof(T) * count)); + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNewArray); + + for (T* pObject = p, *pObjectEnd = pObject + count; pObject != pObjectEnd; ++pObject) + ::new (pObject) T; + + return p; + } + }; + + // This is a support template for fastNewArray. + // This handles the case wherein T has a trivial ctor and a non-trivial dtor. + template + struct NewArrayImpl { + static T* fastNewArray(size_t count) + { + void* p = fastMalloc(sizeof(AllocAlignmentInteger) + (sizeof(T) * count)); + ArraySize a = { static_cast(p) }; + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNewArray); + *a.size++ = count; + // No need to construct the objects in this case. + + return a.t; + } + }; + + // This is a support template for fastNewArray. + // This handles the case wherein T has a non-trivial ctor and a non-trivial dtor. + template + struct NewArrayImpl { + static T* fastNewArray(size_t count) + { + void* p = fastMalloc(sizeof(AllocAlignmentInteger) + (sizeof(T) * count)); + ArraySize a = { static_cast(p) }; + + if (!p) + return 0; + + fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNewArray); + *a.size++ = count; + + for (T* pT = a.t, *pTEnd = pT + count; pT != pTEnd; ++pT) + ::new (pT) T; + + return a.t; + } + }; + } // namespace Internal + + template + inline T* fastNewArray(size_t count) + { + return Internal::NewArrayImpl::value, WTF::HasTrivialDestructor::value>::fastNewArray(count); + } + + template + inline void fastDelete(T* p) + { + if (!p) + return; + + fastMallocMatchValidateFree(p, Internal::AllocTypeFastNew); + p->~T(); + fastFree(p); + } + + template + inline void fastDeleteSkippingDestructor(T* p) + { + if (!p) + return; + + fastMallocMatchValidateFree(p, Internal::AllocTypeFastNew); + fastFree(p); + } + + namespace Internal { + // This is a support template for fastDeleteArray. + // This handles the case wherein T has a trivial dtor. + template + struct DeleteArrayImpl { + static void fastDeleteArray(void* p) + { + // No need to destruct the objects in this case. + // We expect that fastFree checks for null. + fastMallocMatchValidateFree(p, Internal::AllocTypeFastNewArray); + fastFree(p); + } + }; + + // This is a support template for fastDeleteArray. + // This handles the case wherein T has a non-trivial dtor. + template + struct DeleteArrayImpl { + static void fastDeleteArray(T* p) + { + if (!p) + return; + + ArraySize a; + a.t = p; + a.size--; // Decrement size pointer + + T* pEnd = p + *a.size; + while (pEnd-- != p) + pEnd->~T(); + + fastMallocMatchValidateFree(a.size, Internal::AllocTypeFastNewArray); + fastFree(a.size); + } + }; + + } // namespace Internal + + template + void fastDeleteArray(T* p) + { + Internal::DeleteArrayImpl::value>::fastDeleteArray(p); + } + + + template + inline void fastNonNullDelete(T* p) + { + fastMallocMatchValidateFree(p, Internal::AllocTypeFastNew); + p->~T(); + fastFree(p); + } + + namespace Internal { + // This is a support template for fastDeleteArray. + // This handles the case wherein T has a trivial dtor. + template + struct NonNullDeleteArrayImpl { + static void fastNonNullDeleteArray(void* p) + { + fastMallocMatchValidateFree(p, Internal::AllocTypeFastNewArray); + // No need to destruct the objects in this case. + fastFree(p); + } + }; + + // This is a support template for fastDeleteArray. + // This handles the case wherein T has a non-trivial dtor. + template + struct NonNullDeleteArrayImpl { + static void fastNonNullDeleteArray(T* p) + { + ArraySize a; + a.t = p; + a.size--; + + T* pEnd = p + *a.size; + while (pEnd-- != p) + pEnd->~T(); + + fastMallocMatchValidateFree(a.size, Internal::AllocTypeFastNewArray); + fastFree(a.size); + } + }; + + } // namespace Internal + + template + void fastNonNullDeleteArray(T* p) + { + Internal::NonNullDeleteArrayImpl::value>::fastNonNullDeleteArray(p); + } + + +} // namespace WTF + +using WTF::fastDeleteSkippingDestructor; + +#endif // FastAllocBase_h diff --git a/masm/wtf/FastMalloc.h b/masm/wtf/FastMalloc.h new file mode 100644 index 0000000000..1300a8ed60 --- /dev/null +++ b/masm/wtf/FastMalloc.h @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_FastMalloc_h +#define WTF_FastMalloc_h + +#include +#include +#include +#include + +namespace WTF { + + // These functions call CRASH() if an allocation fails. + WTF_EXPORT_PRIVATE void* fastMalloc(size_t); + WTF_EXPORT_PRIVATE void* fastZeroedMalloc(size_t); + WTF_EXPORT_PRIVATE void* fastCalloc(size_t numElements, size_t elementSize); + WTF_EXPORT_PRIVATE void* fastRealloc(void*, size_t); + WTF_EXPORT_PRIVATE char* fastStrDup(const char*); + WTF_EXPORT_PRIVATE size_t fastMallocSize(const void*); + + struct TryMallocReturnValue { + TryMallocReturnValue(void* data) + : m_data(data) + { + } + TryMallocReturnValue(const TryMallocReturnValue& source) + : m_data(source.m_data) + { + source.m_data = 0; + } + ~TryMallocReturnValue() { ASSERT(!m_data); } + template bool getValue(T& data) WARN_UNUSED_RETURN; + template operator PossiblyNull() + { + T value; + getValue(value); + return PossiblyNull(value); + } + private: + mutable void* m_data; + }; + + template bool TryMallocReturnValue::getValue(T& data) + { + union u { void* data; T target; } res; + res.data = m_data; + data = res.target; + bool returnValue = !!m_data; + m_data = 0; + return returnValue; + } + + WTF_EXPORT_PRIVATE TryMallocReturnValue tryFastMalloc(size_t n); + TryMallocReturnValue tryFastZeroedMalloc(size_t n); + WTF_EXPORT_PRIVATE TryMallocReturnValue tryFastCalloc(size_t n_elements, size_t element_size); + WTF_EXPORT_PRIVATE TryMallocReturnValue tryFastRealloc(void* p, size_t n); + + WTF_EXPORT_PRIVATE void fastFree(void*); + +#ifndef NDEBUG + WTF_EXPORT_PRIVATE void fastMallocForbid(); + WTF_EXPORT_PRIVATE void fastMallocAllow(); +#endif + + WTF_EXPORT_PRIVATE void releaseFastMallocFreeMemory(); + + struct FastMallocStatistics { + size_t reservedVMBytes; + size_t committedVMBytes; + size_t freeListBytes; + }; + WTF_EXPORT_PRIVATE FastMallocStatistics fastMallocStatistics(); + + // This defines a type which holds an unsigned integer and is the same + // size as the minimally aligned memory allocation. + typedef unsigned long long AllocAlignmentInteger; + + namespace Internal { + enum AllocType { // Start with an unusual number instead of zero, because zero is common. + AllocTypeMalloc = 0x375d6750, // Encompasses fastMalloc, fastZeroedMalloc, fastCalloc, fastRealloc. + AllocTypeClassNew, // Encompasses class operator new from FastAllocBase. + AllocTypeClassNewArray, // Encompasses class operator new[] from FastAllocBase. + AllocTypeFastNew, // Encompasses fastNew. + AllocTypeFastNewArray, // Encompasses fastNewArray. + AllocTypeNew, // Encompasses global operator new. + AllocTypeNewArray // Encompasses global operator new[]. + }; + + enum { + ValidationPrefix = 0xf00df00d, + ValidationSuffix = 0x0badf00d + }; + + typedef unsigned ValidationTag; + + struct ValidationHeader { + AllocType m_type; + unsigned m_size; + ValidationTag m_prefix; + unsigned m_alignment; + }; + + static const int ValidationBufferSize = sizeof(ValidationHeader) + sizeof(ValidationTag); + } + +#if ENABLE(WTF_MALLOC_VALIDATION) + + // Malloc validation is a scheme whereby a tag is attached to an + // allocation which identifies how it was originally allocated. + // This allows us to verify that the freeing operation matches the + // allocation operation. If memory is allocated with operator new[] + // but freed with free or delete, this system would detect that. + // In the implementation here, the tag is an integer prepended to + // the allocation memory which is assigned one of the AllocType + // enumeration values. An alternative implementation of this + // scheme could store the tag somewhere else or ignore it. + // Users of FastMalloc don't need to know or care how this tagging + // is implemented. + + namespace Internal { + + // Handle a detected alloc/free mismatch. By default this calls CRASH(). + void fastMallocMatchFailed(void* p); + + inline ValidationHeader* fastMallocValidationHeader(void* p) + { + return reinterpret_cast(static_cast(p) - sizeof(ValidationHeader)); + } + + inline ValidationTag* fastMallocValidationSuffix(void* p) + { + ValidationHeader* header = fastMallocValidationHeader(p); + if (header->m_prefix != static_cast(ValidationPrefix)) + fastMallocMatchFailed(p); + + return reinterpret_cast(static_cast(p) + header->m_size); + } + + // Return the AllocType tag associated with the allocated block p. + inline AllocType fastMallocMatchValidationType(void* p) + { + return fastMallocValidationHeader(p)->m_type; + } + + // Set the AllocType tag to be associaged with the allocated block p. + inline void setFastMallocMatchValidationType(void* p, AllocType allocType) + { + fastMallocValidationHeader(p)->m_type = allocType; + } + + } // namespace Internal + + // This is a higher level function which is used by FastMalloc-using code. + inline void fastMallocMatchValidateMalloc(void* p, Internal::AllocType allocType) + { + if (!p) + return; + + Internal::setFastMallocMatchValidationType(p, allocType); + } + + // This is a higher level function which is used by FastMalloc-using code. + inline void fastMallocMatchValidateFree(void* p, Internal::AllocType) + { + if (!p) + return; + + Internal::ValidationHeader* header = Internal::fastMallocValidationHeader(p); + if (header->m_prefix != static_cast(Internal::ValidationPrefix)) + Internal::fastMallocMatchFailed(p); + + if (*Internal::fastMallocValidationSuffix(p) != Internal::ValidationSuffix) + Internal::fastMallocMatchFailed(p); + + Internal::setFastMallocMatchValidationType(p, Internal::AllocTypeMalloc); // Set it to this so that fastFree thinks it's OK. + } + + inline void fastMallocValidate(void* p) + { + if (!p) + return; + + Internal::ValidationHeader* header = Internal::fastMallocValidationHeader(p); + if (header->m_prefix != static_cast(Internal::ValidationPrefix)) + Internal::fastMallocMatchFailed(p); + + if (*Internal::fastMallocValidationSuffix(p) != Internal::ValidationSuffix) + Internal::fastMallocMatchFailed(p); + } + +#else + + inline void fastMallocMatchValidateMalloc(void*, Internal::AllocType) + { + } + + inline void fastMallocMatchValidateFree(void*, Internal::AllocType) + { + } + +#endif + +} // namespace WTF + +using WTF::fastCalloc; +using WTF::fastFree; +using WTF::fastMalloc; +using WTF::fastMallocSize; +using WTF::fastRealloc; +using WTF::fastStrDup; +using WTF::fastZeroedMalloc; +using WTF::tryFastCalloc; +using WTF::tryFastMalloc; +using WTF::tryFastRealloc; +using WTF::tryFastZeroedMalloc; + +#ifndef NDEBUG +using WTF::fastMallocForbid; +using WTF::fastMallocAllow; +#endif + +#if COMPILER(GCC) && OS(DARWIN) +#define WTF_PRIVATE_INLINE __private_extern__ inline __attribute__((always_inline)) +#elif COMPILER(GCC) +#define WTF_PRIVATE_INLINE inline __attribute__((always_inline)) +#elif COMPILER(MSVC) || COMPILER(RVCT) +#define WTF_PRIVATE_INLINE __forceinline +#else +#define WTF_PRIVATE_INLINE inline +#endif + +#if !defined(_CRTDBG_MAP_ALLOC) && !(defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC) + +// The nothrow functions here are actually not all that helpful, because fastMalloc will +// call CRASH() rather than returning 0, and returning 0 is what nothrow is all about. +// But since WebKit code never uses exceptions or nothrow at all, this is probably OK. +// Long term we will adopt FastAllocBase.h everywhere, and and replace this with +// debug-only code to make sure we don't use the system malloc via the default operator +// new by accident. + +#if ENABLE(GLOBAL_FASTMALLOC_NEW) + +#if COMPILER(MSVC) +#pragma warning(push) +#pragma warning(disable: 4290) // Disable the C++ exception specification ignored warning. +#endif +WTF_PRIVATE_INLINE void* operator new(size_t size) throw (std::bad_alloc) { return fastMalloc(size); } +WTF_PRIVATE_INLINE void* operator new(size_t size, const std::nothrow_t&) throw() { return fastMalloc(size); } +WTF_PRIVATE_INLINE void operator delete(void* p) throw() { fastFree(p); } +WTF_PRIVATE_INLINE void operator delete(void* p, const std::nothrow_t&) throw() { fastFree(p); } +WTF_PRIVATE_INLINE void* operator new[](size_t size) throw (std::bad_alloc) { return fastMalloc(size); } +WTF_PRIVATE_INLINE void* operator new[](size_t size, const std::nothrow_t&) throw() { return fastMalloc(size); } +WTF_PRIVATE_INLINE void operator delete[](void* p) throw() { fastFree(p); } +WTF_PRIVATE_INLINE void operator delete[](void* p, const std::nothrow_t&) throw() { fastFree(p); } +#if COMPILER(MSVC) +#pragma warning(pop) +#endif + +#endif + +#endif + +#endif /* WTF_FastMalloc_h */ diff --git a/masm/wtf/Locker.h b/masm/wtf/Locker.h new file mode 100644 index 0000000000..c465b99ea4 --- /dev/null +++ b/masm/wtf/Locker.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef Locker_h +#define Locker_h + +#include + +namespace WTF { + +template class Locker { + WTF_MAKE_NONCOPYABLE(Locker); +public: + Locker(T& lockable) : m_lockable(lockable) { m_lockable.lock(); } + ~Locker() { m_lockable.unlock(); } +private: + T& m_lockable; +}; + +} + +using WTF::Locker; + +#endif diff --git a/masm/wtf/Noncopyable.h b/masm/wtf/Noncopyable.h new file mode 100644 index 0000000000..a88cefc34e --- /dev/null +++ b/masm/wtf/Noncopyable.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_Noncopyable_h +#define WTF_Noncopyable_h + +#include + +#if COMPILER_SUPPORTS(CXX_DELETED_FUNCTIONS) + #define WTF_MAKE_NONCOPYABLE(ClassName) \ + CLANG_PRAGMA("clang diagnostic push") \ + CLANG_PRAGMA("clang diagnostic ignored \"-Wunknown-pragmas\"") \ + CLANG_PRAGMA("clang diagnostic ignored \"-Wc++0x-extensions\"") \ + private: \ + ClassName(const ClassName&) = delete; \ + ClassName& operator=(const ClassName&) = delete; \ + CLANG_PRAGMA("clang diagnostic pop") +#else + #define WTF_MAKE_NONCOPYABLE(ClassName) \ + private: \ + ClassName(const ClassName&); \ + ClassName& operator=(const ClassName&) +#endif + +#endif // WTF_Noncopyable_h diff --git a/masm/wtf/NotFound.h b/masm/wtf/NotFound.h new file mode 100644 index 0000000000..4263bcecab --- /dev/null +++ b/masm/wtf/NotFound.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NotFound_h +#define NotFound_h + +namespace WTF { + + const size_t notFound = static_cast(-1); + +} // namespace WTF + +using WTF::notFound; + +#endif // NotFound_h diff --git a/masm/wtf/NullPtr.h b/masm/wtf/NullPtr.h new file mode 100644 index 0000000000..98c05140d8 --- /dev/null +++ b/masm/wtf/NullPtr.h @@ -0,0 +1,56 @@ +/* + +Copyright (C) 2010 Apple Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef NullPtr_h +#define NullPtr_h + +// For compilers and standard libraries that do not yet include it, this adds the +// nullptr_t type and nullptr object. They are defined in the same namespaces they +// would be in compiler and library that had the support. + +#include + +#if COMPILER_SUPPORTS(CXX_NULLPTR) || defined(_LIBCPP_VERSION) + +#include + +// libstdc++ supports nullptr_t starting with gcc 4.6. +#if defined(__GLIBCXX__) && __GLIBCXX__ < 20110325 +namespace std { +typedef decltype(nullptr) nullptr_t; +} +#endif + +#else + +namespace std { +class WTF_EXPORT_PRIVATE nullptr_t { }; +} +extern WTF_EXPORT_PRIVATE std::nullptr_t nullptr; + +#endif + +#endif diff --git a/masm/wtf/OwnPtr.h b/masm/wtf/OwnPtr.h new file mode 100644 index 0000000000..fa79aa1e3a --- /dev/null +++ b/masm/wtf/OwnPtr.h @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_OwnPtr_h +#define WTF_OwnPtr_h + +#include +#include +#include +#include +#include +#include +#include + +namespace WTF { + + // Unlike most of our smart pointers, OwnPtr can take either the pointer type or the pointed-to type. + + template class PassOwnPtr; + template PassOwnPtr adoptPtr(T*); + + template class OwnPtr { +#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) + // If rvalue references are not supported, the copy constructor is + // public so OwnPtr cannot be marked noncopyable. See note below. + WTF_MAKE_NONCOPYABLE(OwnPtr); +#endif + public: + typedef typename RemovePointer::Type ValueType; + typedef ValueType* PtrType; + + OwnPtr() : m_ptr(0) { } + OwnPtr(std::nullptr_t) : m_ptr(0) { } + + // See comment in PassOwnPtr.h for why this takes a const reference. + template OwnPtr(const PassOwnPtr& o); + +#if !COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) + // This copy constructor is used implicitly by gcc when it generates + // transients for assigning a PassOwnPtr object to a stack-allocated + // OwnPtr object. It should never be called explicitly and gcc + // should optimize away the constructor when generating code. + OwnPtr(const OwnPtr&); +#endif + + ~OwnPtr() { deleteOwnedPtr(m_ptr); } + + PtrType get() const { return m_ptr; } + + void clear(); + PassOwnPtr release(); + PtrType leakPtr() WARN_UNUSED_RETURN; + + ValueType& operator*() const { ASSERT(m_ptr); return *m_ptr; } + PtrType operator->() const { ASSERT(m_ptr); return m_ptr; } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef PtrType OwnPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_ptr ? &OwnPtr::m_ptr : 0; } + + OwnPtr& operator=(const PassOwnPtr&); + OwnPtr& operator=(std::nullptr_t) { clear(); return *this; } + template OwnPtr& operator=(const PassOwnPtr&); + +#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) + OwnPtr(OwnPtr&&); + template OwnPtr(OwnPtr&&); + + OwnPtr& operator=(OwnPtr&&); + template OwnPtr& operator=(OwnPtr&&); +#endif + + void swap(OwnPtr& o) { std::swap(m_ptr, o.m_ptr); } + + private: +#if !COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) + // If rvalue references are supported, noncopyable takes care of this. + OwnPtr& operator=(const OwnPtr&); +#endif + + // We should never have two OwnPtrs for the same underlying object (otherwise we'll get + // double-destruction), so these equality operators should never be needed. + template bool operator==(const OwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + template bool operator!=(const OwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + template bool operator==(const PassOwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + template bool operator!=(const PassOwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + + PtrType m_ptr; + }; + + template template inline OwnPtr::OwnPtr(const PassOwnPtr& o) + : m_ptr(o.leakPtr()) + { + } + + template inline void OwnPtr::clear() + { + PtrType ptr = m_ptr; + m_ptr = 0; + deleteOwnedPtr(ptr); + } + + template inline PassOwnPtr OwnPtr::release() + { + PtrType ptr = m_ptr; + m_ptr = 0; + return adoptPtr(ptr); + } + + template inline typename OwnPtr::PtrType OwnPtr::leakPtr() + { + PtrType ptr = m_ptr; + m_ptr = 0; + return ptr; + } + + template inline OwnPtr& OwnPtr::operator=(const PassOwnPtr& o) + { + PtrType ptr = m_ptr; + m_ptr = o.leakPtr(); + ASSERT(!ptr || m_ptr != ptr); + deleteOwnedPtr(ptr); + return *this; + } + + template template inline OwnPtr& OwnPtr::operator=(const PassOwnPtr& o) + { + PtrType ptr = m_ptr; + m_ptr = o.leakPtr(); + ASSERT(!ptr || m_ptr != ptr); + deleteOwnedPtr(ptr); + return *this; + } + +#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) + template inline OwnPtr::OwnPtr(OwnPtr&& o) + : m_ptr(o.leakPtr()) + { + } + + template template inline OwnPtr::OwnPtr(OwnPtr&& o) + : m_ptr(o.leakPtr()) + { + } + + template inline OwnPtr& OwnPtr::operator=(OwnPtr&& o) + { + PtrType ptr = m_ptr; + m_ptr = o.leakPtr(); + ASSERT(!ptr || m_ptr != ptr); + deleteOwnedPtr(ptr); + + return *this; + } + + template template inline OwnPtr& OwnPtr::operator=(OwnPtr&& o) + { + PtrType ptr = m_ptr; + m_ptr = o.leakPtr(); + ASSERT(!ptr || m_ptr != ptr); + deleteOwnedPtr(ptr); + + return *this; + } +#endif + + template inline void swap(OwnPtr& a, OwnPtr& b) + { + a.swap(b); + } + + template inline bool operator==(const OwnPtr& a, U* b) + { + return a.get() == b; + } + + template inline bool operator==(T* a, const OwnPtr& b) + { + return a == b.get(); + } + + template inline bool operator!=(const OwnPtr& a, U* b) + { + return a.get() != b; + } + + template inline bool operator!=(T* a, const OwnPtr& b) + { + return a != b.get(); + } + + template inline typename OwnPtr::PtrType getPtr(const OwnPtr& p) + { + return p.get(); + } + +} // namespace WTF + +using WTF::OwnPtr; + +#endif // WTF_OwnPtr_h diff --git a/masm/wtf/OwnPtrCommon.h b/masm/wtf/OwnPtrCommon.h new file mode 100644 index 0000000000..43a3cb7de9 --- /dev/null +++ b/masm/wtf/OwnPtrCommon.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. + * Copyright (C) 2010 Company 100 Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_OwnPtrCommon_h +#define WTF_OwnPtrCommon_h + +#if OS(WINDOWS) +typedef struct HBITMAP__* HBITMAP; +typedef struct HBRUSH__* HBRUSH; +typedef struct HDC__* HDC; +typedef struct HFONT__* HFONT; +typedef struct HPALETTE__* HPALETTE; +typedef struct HPEN__* HPEN; +typedef struct HRGN__* HRGN; +#endif + +#if PLATFORM(EFL) +typedef struct _Ecore_Evas Ecore_Evas; +typedef struct _Ecore_Pipe Ecore_Pipe; +typedef struct _Ecore_Timer Ecore_Timer; +typedef struct _Eina_Hash Eina_Hash; +typedef struct _Eina_Module Eina_Module; +typedef struct _Evas_Object Evas_Object; +#endif + +namespace WTF { + + template inline void deleteOwnedPtr(T* ptr) + { + typedef char known[sizeof(T) ? 1 : -1]; + if (sizeof(known)) + delete ptr; + } + +#if OS(WINDOWS) + WTF_EXPORT_PRIVATE void deleteOwnedPtr(HBITMAP); + WTF_EXPORT_PRIVATE void deleteOwnedPtr(HBRUSH); + WTF_EXPORT_PRIVATE void deleteOwnedPtr(HDC); + WTF_EXPORT_PRIVATE void deleteOwnedPtr(HFONT); + WTF_EXPORT_PRIVATE void deleteOwnedPtr(HPALETTE); + WTF_EXPORT_PRIVATE void deleteOwnedPtr(HPEN); + WTF_EXPORT_PRIVATE void deleteOwnedPtr(HRGN); +#endif + +#if PLATFORM(EFL) + WTF_EXPORT_PRIVATE void deleteOwnedPtr(Ecore_Evas*); + WTF_EXPORT_PRIVATE void deleteOwnedPtr(Ecore_Pipe*); + WTF_EXPORT_PRIVATE void deleteOwnedPtr(Ecore_Timer*); + WTF_EXPORT_PRIVATE void deleteOwnedPtr(Eina_Hash*); + WTF_EXPORT_PRIVATE void deleteOwnedPtr(Eina_Module*); + WTF_EXPORT_PRIVATE void deleteOwnedPtr(Evas_Object*); +#endif + +} // namespace WTF + +#endif // WTF_OwnPtrCommon_h diff --git a/masm/wtf/PassRefPtr.h b/masm/wtf/PassRefPtr.h new file mode 100644 index 0000000000..5f50671135 --- /dev/null +++ b/masm/wtf/PassRefPtr.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_PassRefPtr_h +#define WTF_PassRefPtr_h + +#include +#include +#include + +namespace WTF { + + template class RefPtr; + template class PassRefPtr; + template PassRefPtr adoptRef(T*); + + inline void adopted(const void*) { } + +#if !(PLATFORM(QT) && CPU(ARM)) + #define REF_DEREF_INLINE ALWAYS_INLINE +#else + // Older version of gcc used by Harmattan SDK fails to build with ALWAYS_INLINE. + // See https://bugs.webkit.org/show_bug.cgi?id=37253 for details. + #define REF_DEREF_INLINE inline +#endif + + template REF_DEREF_INLINE void refIfNotNull(T* ptr) + { + if (LIKELY(ptr != 0)) + ptr->ref(); + } + + template REF_DEREF_INLINE void derefIfNotNull(T* ptr) + { + if (LIKELY(ptr != 0)) + ptr->deref(); + } + + #undef REF_DEREF_INLINE + + template class PassRefPtr { + public: + PassRefPtr() : m_ptr(0) { } + PassRefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); } + // It somewhat breaks the type system to allow transfer of ownership out of + // a const PassRefPtr. However, it makes it much easier to work with PassRefPtr + // temporaries, and we don't have a need to use real const PassRefPtrs anyway. + PassRefPtr(const PassRefPtr& o) : m_ptr(o.leakRef()) { } + template PassRefPtr(const PassRefPtr& o) : m_ptr(o.leakRef()) { } + + ALWAYS_INLINE ~PassRefPtr() { derefIfNotNull(m_ptr); } + + template PassRefPtr(const RefPtr&); + + T* get() const { return m_ptr; } + + T* leakRef() const WARN_UNUSED_RETURN; + + T& operator*() const { return *m_ptr; } + T* operator->() const { return m_ptr; } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef T* (PassRefPtr::*UnspecifiedBoolType); + operator UnspecifiedBoolType() const { return m_ptr ? &PassRefPtr::m_ptr : 0; } + + PassRefPtr& operator=(const PassRefPtr&) { COMPILE_ASSERT(!sizeof(T*), PassRefPtr_should_never_be_assigned_to); return *this; } + + friend PassRefPtr adoptRef(T*); + + private: + // adopting constructor + PassRefPtr(T* ptr, bool) : m_ptr(ptr) { } + + mutable T* m_ptr; + }; + + template template inline PassRefPtr::PassRefPtr(const RefPtr& o) + : m_ptr(o.get()) + { + T* ptr = m_ptr; + refIfNotNull(ptr); + } + + template inline T* PassRefPtr::leakRef() const + { + T* ptr = m_ptr; + m_ptr = 0; + return ptr; + } + + template inline bool operator==(const PassRefPtr& a, const PassRefPtr& b) + { + return a.get() == b.get(); + } + + template inline bool operator==(const PassRefPtr& a, const RefPtr& b) + { + return a.get() == b.get(); + } + + template inline bool operator==(const RefPtr& a, const PassRefPtr& b) + { + return a.get() == b.get(); + } + + template inline bool operator==(const PassRefPtr& a, U* b) + { + return a.get() == b; + } + + template inline bool operator==(T* a, const PassRefPtr& b) + { + return a == b.get(); + } + + template inline bool operator!=(const PassRefPtr& a, const PassRefPtr& b) + { + return a.get() != b.get(); + } + + template inline bool operator!=(const PassRefPtr& a, const RefPtr& b) + { + return a.get() != b.get(); + } + + template inline bool operator!=(const RefPtr& a, const PassRefPtr& b) + { + return a.get() != b.get(); + } + + template inline bool operator!=(const PassRefPtr& a, U* b) + { + return a.get() != b; + } + + template inline bool operator!=(T* a, const PassRefPtr& b) + { + return a != b.get(); + } + + template inline PassRefPtr adoptRef(T* p) + { + adopted(p); + return PassRefPtr(p, true); + } + + template inline PassRefPtr static_pointer_cast(const PassRefPtr& p) + { + return adoptRef(static_cast(p.leakRef())); + } + + template inline PassRefPtr const_pointer_cast(const PassRefPtr& p) + { + return adoptRef(const_cast(p.leakRef())); + } + + template inline T* getPtr(const PassRefPtr& p) + { + return p.get(); + } + +} // namespace WTF + +using WTF::PassRefPtr; +using WTF::adoptRef; +using WTF::static_pointer_cast; +using WTF::const_pointer_cast; + +#endif // WTF_PassRefPtr_h diff --git a/masm/wtf/Platform.h b/masm/wtf/Platform.h new file mode 100644 index 0000000000..0be4bb07dc --- /dev/null +++ b/masm/wtf/Platform.h @@ -0,0 +1,1195 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_Platform_h +#define WTF_Platform_h + +/* Include compiler specific macros */ +#include + +/* ==== PLATFORM handles OS, operating environment, graphics API, and + CPU. This macro will be phased out in favor of platform adaptation + macros, policy decision macros, and top-level port definitions. ==== */ +#define PLATFORM(WTF_FEATURE) (defined WTF_PLATFORM_##WTF_FEATURE && WTF_PLATFORM_##WTF_FEATURE) + + +/* ==== Platform adaptation macros: these describe properties of the target environment. ==== */ + +/* CPU() - the target CPU architecture */ +#define CPU(WTF_FEATURE) (defined WTF_CPU_##WTF_FEATURE && WTF_CPU_##WTF_FEATURE) +/* HAVE() - specific system features (headers, functions or similar) that are present or not */ +#define HAVE(WTF_FEATURE) (defined HAVE_##WTF_FEATURE && HAVE_##WTF_FEATURE) +/* OS() - underlying operating system; only to be used for mandated low-level services like + virtual memory, not to choose a GUI toolkit */ +#define OS(WTF_FEATURE) (defined WTF_OS_##WTF_FEATURE && WTF_OS_##WTF_FEATURE) + + +/* ==== Policy decision macros: these define policy choices for a particular port. ==== */ + +/* USE() - use a particular third-party library or optional OS service */ +#define USE(WTF_FEATURE) (defined WTF_USE_##WTF_FEATURE && WTF_USE_##WTF_FEATURE) +/* ENABLE() - turn on a specific feature of WebKit */ +#define ENABLE(WTF_FEATURE) (defined ENABLE_##WTF_FEATURE && ENABLE_##WTF_FEATURE) + + +/* ==== CPU() - the target CPU architecture ==== */ + +/* This also defines CPU(BIG_ENDIAN) or CPU(MIDDLE_ENDIAN) or neither, as appropriate. */ + +/* CPU(ALPHA) - DEC Alpha */ +#if defined(__alpha__) +#define WTF_CPU_ALPHA 1 +#endif + +/* CPU(IA64) - Itanium / IA-64 */ +#if defined(__ia64__) +#define WTF_CPU_IA64 1 +/* 32-bit mode on Itanium */ +#if !defined(__LP64__) +#define WTF_CPU_IA64_32 1 +#endif +#endif + +/* CPU(MIPS) - MIPS 32-bit */ +/* Note: Only O32 ABI is tested, so we enable it for O32 ABI for now. */ +#if (defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_)) \ + && defined(_ABIO32) +#define WTF_CPU_MIPS 1 +#if defined(__MIPSEB__) +#define WTF_CPU_BIG_ENDIAN 1 +#endif +#define WTF_MIPS_PIC (defined __PIC__) +#define WTF_MIPS_ARCH __mips +#define WTF_MIPS_ISA(v) (defined WTF_MIPS_ARCH && WTF_MIPS_ARCH == v) +#define WTF_MIPS_ISA_AT_LEAST(v) (defined WTF_MIPS_ARCH && WTF_MIPS_ARCH >= v) +#define WTF_MIPS_ARCH_REV __mips_isa_rev +#define WTF_MIPS_ISA_REV(v) (defined WTF_MIPS_ARCH_REV && WTF_MIPS_ARCH_REV == v) +#define WTF_MIPS_DOUBLE_FLOAT (defined __mips_hard_float && !defined __mips_single_float) +#define WTF_MIPS_FP64 (defined __mips_fpr && __mips_fpr == 64) +/* MIPS requires allocators to use aligned memory */ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 +#endif /* MIPS */ + +/* CPU(PPC) - PowerPC 32-bit */ +#if defined(__ppc__) \ + || defined(__PPC__) \ + || defined(__powerpc__) \ + || defined(__powerpc) \ + || defined(__POWERPC__) \ + || defined(_M_PPC) \ + || defined(__PPC) +#define WTF_CPU_PPC 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(PPC64) - PowerPC 64-bit */ +#if defined(__ppc64__) \ + || defined(__PPC64__) +#define WTF_CPU_PPC64 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(SH4) - SuperH SH-4 */ +#if defined(__SH4__) +#define WTF_CPU_SH4 1 +#endif + +/* CPU(SPARC32) - SPARC 32-bit */ +#if defined(__sparc) && !defined(__arch64__) || defined(__sparcv8) +#define WTF_CPU_SPARC32 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(SPARC64) - SPARC 64-bit */ +#if defined(__sparc__) && defined(__arch64__) || defined (__sparcv9) +#define WTF_CPU_SPARC64 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(SPARC) - any SPARC, true for CPU(SPARC32) and CPU(SPARC64) */ +#if CPU(SPARC32) || CPU(SPARC64) +#define WTF_CPU_SPARC 1 +#endif + +/* CPU(S390X) - S390 64-bit */ +#if defined(__s390x__) +#define WTF_CPU_S390X 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(S390) - S390 32-bit */ +#if defined(__s390__) +#define WTF_CPU_S390 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(X86) - i386 / x86 32-bit */ +#if defined(__i386__) \ + || defined(i386) \ + || defined(_M_IX86) \ + || defined(_X86_) \ + || defined(__THW_INTEL) +#define WTF_CPU_X86 1 +#endif + +/* CPU(X86_64) - AMD64 / Intel64 / x86_64 64-bit */ +#if defined(__x86_64__) \ + || defined(_M_X64) +#define WTF_CPU_X86_64 1 +#endif + +/* CPU(ARM) - ARM, any version*/ +#if defined(arm) \ + || defined(__arm__) \ + || defined(ARM) \ + || defined(_ARM_) +#define WTF_CPU_ARM 1 + +#if defined(__ARM_PCS_VFP) +#define WTF_CPU_ARM_HARDFP 1 +#endif + +#if defined(__ARMEB__) || (COMPILER(RVCT) && defined(__BIG_ENDIAN)) +#define WTF_CPU_BIG_ENDIAN 1 + +#elif !defined(__ARM_EABI__) \ + && !defined(__EABI__) \ + && !defined(__VFP_FP__) \ + && !defined(_WIN32_WCE) \ + && !defined(ANDROID) +#define WTF_CPU_MIDDLE_ENDIAN 1 + +#endif + +#define WTF_ARM_ARCH_AT_LEAST(N) (CPU(ARM) && WTF_ARM_ARCH_VERSION >= N) + +/* Set WTF_ARM_ARCH_VERSION */ +#if defined(__ARM_ARCH_4__) \ + || defined(__ARM_ARCH_4T__) \ + || defined(__MARM_ARMV4__) \ + || defined(_ARMV4I_) +#define WTF_ARM_ARCH_VERSION 4 + +#elif defined(__ARM_ARCH_5__) \ + || defined(__ARM_ARCH_5T__) \ + || defined(__MARM_ARMV5__) +#define WTF_ARM_ARCH_VERSION 5 + +#elif defined(__ARM_ARCH_5E__) \ + || defined(__ARM_ARCH_5TE__) \ + || defined(__ARM_ARCH_5TEJ__) +#define WTF_ARM_ARCH_VERSION 5 +/*ARMv5TE requires allocators to use aligned memory*/ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 + +#elif defined(__ARM_ARCH_6__) \ + || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6T2__) \ + || defined(__ARMV6__) +#define WTF_ARM_ARCH_VERSION 6 + +#elif defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) +#define WTF_ARM_ARCH_VERSION 7 + +/* RVCT sets _TARGET_ARCH_ARM */ +#elif defined(__TARGET_ARCH_ARM) +#define WTF_ARM_ARCH_VERSION __TARGET_ARCH_ARM + +#if defined(__TARGET_ARCH_5E) \ + || defined(__TARGET_ARCH_5TE) \ + || defined(__TARGET_ARCH_5TEJ) +/*ARMv5TE requires allocators to use aligned memory*/ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 +#endif + +#else +#define WTF_ARM_ARCH_VERSION 0 + +#endif + +/* Set WTF_THUMB_ARCH_VERSION */ +#if defined(__ARM_ARCH_4T__) +#define WTF_THUMB_ARCH_VERSION 1 + +#elif defined(__ARM_ARCH_5T__) \ + || defined(__ARM_ARCH_5TE__) \ + || defined(__ARM_ARCH_5TEJ__) +#define WTF_THUMB_ARCH_VERSION 2 + +#elif defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6M__) +#define WTF_THUMB_ARCH_VERSION 3 + +#elif defined(__ARM_ARCH_6T2__) \ + || defined(__ARM_ARCH_7__) \ + || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) \ + || defined(__ARM_ARCH_7M__) +#define WTF_THUMB_ARCH_VERSION 4 + +/* RVCT sets __TARGET_ARCH_THUMB */ +#elif defined(__TARGET_ARCH_THUMB) +#define WTF_THUMB_ARCH_VERSION __TARGET_ARCH_THUMB + +#else +#define WTF_THUMB_ARCH_VERSION 0 +#endif + + +/* CPU(ARMV5_OR_LOWER) - ARM instruction set v5 or earlier */ +/* On ARMv5 and below the natural alignment is required. + And there are some other differences for v5 or earlier. */ +#if !defined(ARMV5_OR_LOWER) && !WTF_ARM_ARCH_AT_LEAST(6) +#define WTF_CPU_ARMV5_OR_LOWER 1 +#endif + + +/* CPU(ARM_TRADITIONAL) - Thumb2 is not available, only traditional ARM (v4 or greater) */ +/* CPU(ARM_THUMB2) - Thumb2 instruction set is available */ +/* Only one of these will be defined. */ +#if !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) +# if defined(thumb2) || defined(__thumb2__) \ + || ((defined(__thumb) || defined(__thumb__)) && WTF_THUMB_ARCH_VERSION == 4) +# define WTF_CPU_ARM_TRADITIONAL 0 +# define WTF_CPU_ARM_THUMB2 1 +# elif WTF_ARM_ARCH_AT_LEAST(4) +# define WTF_CPU_ARM_TRADITIONAL 1 +# define WTF_CPU_ARM_THUMB2 0 +# else +# error "Not supported ARM architecture" +# endif +#elif CPU(ARM_TRADITIONAL) && CPU(ARM_THUMB2) /* Sanity Check */ +# error "Cannot use both of WTF_CPU_ARM_TRADITIONAL and WTF_CPU_ARM_THUMB2 platforms" +#endif /* !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) */ + +#if defined(__ARM_NEON__) && !defined(WTF_CPU_ARM_NEON) +#define WTF_CPU_ARM_NEON 1 +#endif + +#if CPU(ARM_NEON) && (!COMPILER(GCC) || GCC_VERSION_AT_LEAST(4, 7, 0)) +// All NEON intrinsics usage can be disabled by this macro. +#define HAVE_ARM_NEON_INTRINSICS 1 +#endif + +#endif /* ARM */ + +#if CPU(ARM) || CPU(MIPS) || CPU(SH4) || CPU(SPARC) +#define WTF_CPU_NEEDS_ALIGNED_ACCESS 1 +#endif + +/* ==== OS() - underlying operating system; only to be used for mandated low-level services like + virtual memory, not to choose a GUI toolkit ==== */ + +/* OS(ANDROID) - Android */ +#ifdef ANDROID +#define WTF_OS_ANDROID 1 +#endif + +/* OS(AIX) - AIX */ +#ifdef _AIX +#define WTF_OS_AIX 1 +#endif + +/* OS(DARWIN) - Any Darwin-based OS, including Mac OS X and iPhone OS */ +#ifdef __APPLE__ +#define WTF_OS_DARWIN 1 + +#include +#include +#include +#endif + +/* OS(IOS) - iOS */ +/* OS(MAC_OS_X) - Mac OS X (not including iOS) */ +#if OS(DARWIN) && ((defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED) \ + || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) \ + || (defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR)) +#define WTF_OS_IOS 1 +#elif OS(DARWIN) && defined(TARGET_OS_MAC) && TARGET_OS_MAC +#define WTF_OS_MAC_OS_X 1 + +/* FIXME: These can be removed after sufficient time has passed since the removal of BUILDING_ON / TARGETING macros. */ + +#define ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED 0 / 0 +#define ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED 0 / 0 + +#define BUILDING_ON_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED +#define BUILDING_ON_SNOW_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED +#define BUILDING_ON_LION ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED + +#define TARGETING_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED +#define TARGETING_SNOW_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED +#define TARGETING_LION ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED +#endif + +/* OS(FREEBSD) - FreeBSD */ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +#define WTF_OS_FREEBSD 1 +#endif + +/* OS(HURD) - GNU/Hurd */ +#ifdef __GNU__ +#define WTF_OS_HURD 1 +#endif + +/* OS(LINUX) - Linux */ +#ifdef __linux__ +#define WTF_OS_LINUX 1 +#endif + +/* OS(NETBSD) - NetBSD */ +#if defined(__NetBSD__) +#define WTF_OS_NETBSD 1 +#endif + +/* OS(OPENBSD) - OpenBSD */ +#ifdef __OpenBSD__ +#define WTF_OS_OPENBSD 1 +#endif + +/* OS(QNX) - QNX */ +#if defined(__QNXNTO__) +#define WTF_OS_QNX 1 +#endif + +/* OS(SOLARIS) - Solaris */ +#if defined(sun) || defined(__sun) +#define WTF_OS_SOLARIS 1 +#endif + +/* OS(WINCE) - Windows CE; note that for this platform OS(WINDOWS) is also defined */ +#if defined(_WIN32_WCE) +#define WTF_OS_WINCE 1 +#endif + +/* OS(WINDOWS) - Any version of Windows */ +#if defined(WIN32) || defined(_WIN32) +#define WTF_OS_WINDOWS 1 +#endif + +#define WTF_OS_WIN ERROR "USE WINDOWS WITH OS NOT WIN" +#define WTF_OS_MAC ERROR "USE MAC_OS_X WITH OS NOT MAC" + +/* OS(UNIX) - Any Unix-like system */ +#if OS(AIX) \ + || OS(ANDROID) \ + || OS(DARWIN) \ + || OS(FREEBSD) \ + || OS(HURD) \ + || OS(LINUX) \ + || OS(NETBSD) \ + || OS(OPENBSD) \ + || OS(QNX) \ + || OS(SOLARIS) \ + || defined(unix) \ + || defined(__unix) \ + || defined(__unix__) +#define WTF_OS_UNIX 1 +#endif + +/* Operating environments */ + +/* FIXME: these are all mixes of OS, operating environment and policy choices. */ +/* PLATFORM(CHROMIUM) */ +/* PLATFORM(QT) */ +/* PLATFORM(WX) */ +/* PLATFORM(GTK) */ +/* PLATFORM(BLACKBERRY) */ +/* PLATFORM(MAC) */ +/* PLATFORM(WIN) */ +#if defined(BUILDING_CHROMIUM__) +#define WTF_PLATFORM_CHROMIUM 1 +#elif defined(BUILDING_QT__) +#define WTF_PLATFORM_QT 1 +#elif defined(BUILDING_WX__) +#define WTF_PLATFORM_WX 1 +#elif defined(BUILDING_GTK__) +#define WTF_PLATFORM_GTK 1 +#elif defined(BUILDING_BLACKBERRY__) +#define WTF_PLATFORM_BLACKBERRY 1 +#elif OS(DARWIN) +#define WTF_PLATFORM_MAC 1 +#elif OS(WINDOWS) +#define WTF_PLATFORM_WIN 1 +#endif + +/* PLATFORM(IOS) */ +/* FIXME: this is sometimes used as an OS switch and sometimes for higher-level things */ +#if (defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) +#define WTF_PLATFORM_IOS 1 +#endif + +/* PLATFORM(IOS_SIMULATOR) */ +#if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR +#define WTF_PLATFORM_IOS 1 +#define WTF_PLATFORM_IOS_SIMULATOR 1 +#endif + +/* Graphics engines */ + +/* USE(CG) and PLATFORM(CI) */ +#if PLATFORM(MAC) || PLATFORM(IOS) +#define WTF_USE_CG 1 +#endif +#if PLATFORM(MAC) || PLATFORM(IOS) || (PLATFORM(WIN) && USE(CG)) +#define WTF_USE_CA 1 +#endif + +/* USE(SKIA) for Win/Linux/Mac/Android */ +#if PLATFORM(CHROMIUM) +#if OS(DARWIN) +#define WTF_USE_SKIA 1 +#define WTF_USE_ICCJPEG 1 +#define WTF_USE_QCMSLIB 1 +#elif OS(ANDROID) +#define WTF_USE_SKIA 1 +#define WTF_USE_LOW_QUALITY_IMAGE_INTERPOLATION 1 +#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_DITHERING 1 +#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_FANCY_UPSAMPLING 1 +#else +#define WTF_USE_SKIA 1 +#define WTF_USE_ICCJPEG 1 +#define WTF_USE_QCMSLIB 1 +#endif +#endif + +#if OS(QNX) +#define USE_SYSTEM_MALLOC 1 +#endif + +#if PLATFORM(BLACKBERRY) +#define WTF_USE_MERSENNE_TWISTER_19937 1 +#define WTF_USE_SKIA 1 +#define WTF_USE_LOW_QUALITY_IMAGE_INTERPOLATION 1 +#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_DITHERING 1 +#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_FANCY_UPSAMPLING 1 +#endif + +#if PLATFORM(GTK) +#define WTF_USE_CAIRO 1 +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#endif + + +#if OS(WINCE) +#define WTF_USE_MERSENNE_TWISTER_19937 1 +#endif + +/* On Windows, use QueryPerformanceCounter by default */ +#if OS(WINDOWS) +#define WTF_USE_QUERY_PERFORMANCE_COUNTER 1 +#endif + +#if OS(WINCE) && !PLATFORM(QT) +#define NOSHLWAPI /* shlwapi.h not available on WinCe */ + +/* MSDN documentation says these functions are provided with uspce.lib. But we cannot find this file. */ +#define __usp10__ /* disable "usp10.h" */ + +#define _INC_ASSERT /* disable "assert.h" */ +#define assert(x) + +#endif /* OS(WINCE) && !PLATFORM(QT) */ + +#if OS(WINCE) && !PLATFORM(QT) +#define WTF_USE_WCHAR_UNICODE 1 +#elif PLATFORM(GTK) +/* The GTK+ Unicode backend is configurable */ +#else +#define WTF_USE_ICU_UNICODE 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) +#if CPU(X86_64) +#define WTF_USE_PLUGIN_HOST_PROCESS 1 +#endif +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 +#define ENABLE_GESTURE_EVENTS 1 +#define ENABLE_RUBBER_BANDING 1 +#define WTF_USE_SCROLLBAR_PAINTER 1 +#define HAVE_XPC 1 +#endif +#if !defined(ENABLE_JAVA_BRIDGE) +#define ENABLE_JAVA_BRIDGE 1 +#endif +#if !defined(ENABLE_DASHBOARD_SUPPORT) +#define ENABLE_DASHBOARD_SUPPORT 1 +#endif +#define WTF_USE_CF 1 +#define WTF_USE_PTHREADS 1 +#define HAVE_PTHREAD_RWLOCK 1 +#define HAVE_READLINE 1 +#define HAVE_RUNLOOP_TIMER 1 +#define ENABLE_FULLSCREEN_API 1 +#define ENABLE_SMOOTH_SCROLLING 1 +#define ENABLE_WEB_ARCHIVE 1 +#define ENABLE_WEB_AUDIO 1 +#if defined(ENABLE_VIDEO) +#define ENABLE_VIDEO_TRACK 1 +#endif +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 +#define HAVE_LAYER_HOSTING_IN_WINDOW_SERVER 1 +#endif +#define WTF_USE_APPKIT 1 +#define WTF_USE_SECURITY_FRAMEWORK 1 +#endif /* PLATFORM(MAC) && !PLATFORM(IOS) */ + +#if PLATFORM(CHROMIUM) && OS(DARWIN) +#define WTF_USE_CF 1 +#define WTF_USE_PTHREADS 1 +#define HAVE_PTHREAD_RWLOCK 1 + +#define WTF_USE_WK_SCROLLBAR_PAINTER 1 +#endif + +#if PLATFORM(IOS) +#define DONT_FINALIZE_ON_MAIN_THREAD 1 +#endif + +#if PLATFORM(QT) && OS(DARWIN) +#define WTF_USE_CF 1 +#endif + +#if OS(DARWIN) && !PLATFORM(GTK) && !PLATFORM(QT) +#define ENABLE_PURGEABLE_MEMORY 1 +#endif + +#if PLATFORM(IOS) +#define ENABLE_CONTEXT_MENUS 0 +#define ENABLE_DRAG_SUPPORT 0 +#define ENABLE_GEOLOCATION 1 +#define ENABLE_ICONDATABASE 0 +#define ENABLE_INSPECTOR 1 +#define ENABLE_JAVA_BRIDGE 0 +#define ENABLE_NETSCAPE_PLUGIN_API 0 +#define ENABLE_ORIENTATION_EVENTS 1 +#define ENABLE_REPAINT_THROTTLING 1 +#define ENABLE_WEB_ARCHIVE 1 +#define HAVE_NETWORK_CFDATA_ARRAY_CALLBACK 1 +#define HAVE_PTHREAD_RWLOCK 1 +#define HAVE_READLINE 1 +#define WTF_USE_CF 1 +#define WTF_USE_CFNETWORK 1 +#define WTF_USE_PTHREADS 1 + +#if PLATFORM(IOS_SIMULATOR) + #define ENABLE_CLASSIC_INTERPRETER 1 + #define ENABLE_JIT 0 + #define ENABLE_YARR_JIT 0 +#else + #define ENABLE_CLASSIC_INTERPRETER 0 + #define ENABLE_JIT 1 + #define ENABLE_LLINT 1 + #define ENABLE_YARR_JIT 1 +#endif + +#define WTF_USE_APPKIT 0 +#define WTF_USE_SECURITY_FRAMEWORK 0 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) +#define WTF_USE_CF 1 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(WIN_CAIRO) +#define WTF_USE_CFNETWORK 1 +#endif + +#if USE(CFNETWORK) || PLATFORM(MAC) || PLATFORM(IOS) +#define WTF_USE_CFURLCACHE 1 +#define WTF_USE_CFURLSTORAGESESSIONS 1 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(QT) +#define ENABLE_WEB_ARCHIVE 1 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(WIN_CAIRO) && !PLATFORM(QT) +#define ENABLE_FULLSCREEN_API 1 +#endif + +#if PLATFORM(WX) +#if !CPU(PPC) +#define ENABLE_ASSEMBLER 1 +#define ENABLE_JIT 1 +#endif +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#define ENABLE_LLINT 0 +#if OS(DARWIN) +#define WTF_USE_CF 1 +#define ENABLE_WEB_ARCHIVE 1 +#endif +#endif + +#if OS(UNIX) && (PLATFORM(GTK) || PLATFORM(QT)) +#define WTF_USE_PTHREADS 1 +#define HAVE_PTHREAD_RWLOCK 1 +#endif + +#if !defined(HAVE_ACCESSIBILITY) +#if PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && !OS(ANDROID)) +#define HAVE_ACCESSIBILITY 1 +#endif +#endif /* !defined(HAVE_ACCESSIBILITY) */ + +#if OS(UNIX) +#define HAVE_SIGNAL_H 1 +#define WTF_USE_OS_RANDOMNESS 1 +#endif + +#if (OS(FREEBSD) || OS(OPENBSD)) && !defined(__GLIBC__) +#define HAVE_PTHREAD_NP_H 1 +#endif + +#if !defined(HAVE_VASPRINTF) +#if !COMPILER(MSVC) && !COMPILER(RVCT) && !COMPILER(MINGW) && !(COMPILER(GCC) && OS(QNX)) +#define HAVE_VASPRINTF 1 +#endif +#endif + +#if !defined(HAVE_STRNSTR) +#if OS(DARWIN) || (OS(FREEBSD) && !defined(__GLIBC__)) +#define HAVE_STRNSTR 1 +#endif +#endif + +#if !OS(WINDOWS) && !OS(SOLARIS) \ + && !OS(RVCT) \ + && !OS(ANDROID) +#define HAVE_TM_GMTOFF 1 +#define HAVE_TM_ZONE 1 +#define HAVE_TIMEGM 1 +#endif + +#if OS(DARWIN) + +#define HAVE_ERRNO_H 1 +#define HAVE_LANGINFO_H 1 +#define HAVE_MMAP 1 +#define HAVE_MERGESORT 1 +#define HAVE_SBRK 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_SYS_TIMEB_H 1 +#define WTF_USE_ACCELERATE 1 + +#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + +#define HAVE_DISPATCH_H 1 +#define HAVE_HOSTED_CORE_ANIMATION 1 + +#if !PLATFORM(IOS) +#define HAVE_MADV_FREE_REUSE 1 +#define HAVE_MADV_FREE 1 +#define HAVE_PTHREAD_SETNAME_NP 1 +#endif + +#endif + +#if PLATFORM(IOS) +#define HAVE_MADV_FREE 1 +#define HAVE_PTHREAD_SETNAME_NP 1 +#endif + +#elif OS(WINDOWS) + +#if !OS(WINCE) +#define HAVE_SYS_TIMEB_H 1 +#define HAVE_ALIGNED_MALLOC 1 +#define HAVE_ISDEBUGGERPRESENT 1 +#endif +#define HAVE_VIRTUALALLOC 1 +#define WTF_USE_OS_RANDOMNESS 1 + +#elif OS(QNX) + +#define HAVE_ERRNO_H 1 +#define HAVE_MMAP 1 +#define HAVE_MADV_FREE_REUSE 1 +#define HAVE_MADV_FREE 1 +#define HAVE_SBRK 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 +#define WTF_USE_PTHREADS 1 + +#elif OS(ANDROID) + +#define HAVE_ERRNO_H 1 +#define HAVE_NMAP 1 +#define HAVE_SBRK 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 + +#else + +/* FIXME: is this actually used or do other platforms generate their own config.h? */ + +#define HAVE_ERRNO_H 1 +#define HAVE_LANGINFO_H 1 +#define HAVE_MMAP 1 +#define HAVE_SBRK 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 + +#endif + +/* ENABLE macro defaults */ + +#if PLATFORM(QT) +/* We must not customize the global operator new and delete for the Qt port. */ +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#if !OS(UNIX) +#define USE_SYSTEM_MALLOC 1 +#endif +#endif + +#if !defined(ENABLE_ICONDATABASE) +#define ENABLE_ICONDATABASE 1 +#endif + +#if !defined(ENABLE_SQL_DATABASE) +#define ENABLE_SQL_DATABASE 1 +#endif + +#if !defined(ENABLE_JAVASCRIPT_DEBUGGER) +#define ENABLE_JAVASCRIPT_DEBUGGER 1 +#endif + +#if !defined(ENABLE_FTPDIR) +#define ENABLE_FTPDIR 1 +#endif + +#if !defined(ENABLE_CONTEXT_MENUS) +#define ENABLE_CONTEXT_MENUS 1 +#endif + +#if !defined(ENABLE_DRAG_SUPPORT) +#define ENABLE_DRAG_SUPPORT 1 +#endif + +#if !defined(ENABLE_INSPECTOR) +#define ENABLE_INSPECTOR 1 +#endif + +#if !defined(ENABLE_NETSCAPE_PLUGIN_API) +#define ENABLE_NETSCAPE_PLUGIN_API 1 +#endif + +#if !defined(ENABLE_GLOBAL_FASTMALLOC_NEW) +#define ENABLE_GLOBAL_FASTMALLOC_NEW 1 +#endif + +#if !defined(ENABLE_PARSED_STYLE_SHEET_CACHING) +#define ENABLE_PARSED_STYLE_SHEET_CACHING 1 +#endif + +#if !defined(ENABLE_SUBPIXEL_LAYOUT) +#if PLATFORM(CHROMIUM) +#define ENABLE_SUBPIXEL_LAYOUT 1 +#else +#define ENABLE_SUBPIXEL_LAYOUT 0 +#endif +#endif + +#if !defined(ENABLE_GESTURE_ANIMATION) +#if PLATFORM(QT) || !ENABLE(SMOOTH_SCROLLING) +#define ENABLE_GESTURE_ANIMATION 0 +#else +#define ENABLE_GESTURE_ANIMATION 1 +#endif +#endif + +#if !defined(ENABLE_SATURATED_LAYOUT_ARITHMETIC) +#define ENABLE_SATURATED_LAYOUT_ARITHMETIC 0 +#endif + +#if ENABLE(ENABLE_SATURATED_LAYOUT_ARITHMETIC) && !ENABLE(ENABLE_SUBPIXEL_LAYOUT) +#error "ENABLE_SATURATED_LAYOUT_ARITHMETIC requires ENABLE_SUBPIXEL_LAYOUT" +#endif + +#define ENABLE_DEBUG_WITH_BREAKPOINT 0 +#define ENABLE_SAMPLING_COUNTERS 0 +#define ENABLE_SAMPLING_FLAGS 0 +#define ENABLE_SAMPLING_REGIONS 0 +#define ENABLE_OPCODE_SAMPLING 0 +#define ENABLE_CODEBLOCK_SAMPLING 0 +#if ENABLE(CODEBLOCK_SAMPLING) && !ENABLE(OPCODE_SAMPLING) +#error "CODEBLOCK_SAMPLING requires OPCODE_SAMPLING" +#endif +#if ENABLE(OPCODE_SAMPLING) || ENABLE(SAMPLING_FLAGS) || ENABLE(SAMPLING_REGIONS) +#define ENABLE_SAMPLING_THREAD 1 +#endif + +#if !defined(ENABLE_TEXT_CARET) && !PLATFORM(IOS) +#define ENABLE_TEXT_CARET 1 +#endif + +#if !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32_64) +#if (CPU(X86_64) && (OS(UNIX) || OS(WINDOWS))) \ + || (CPU(IA64) && !CPU(IA64_32)) \ + || CPU(ALPHA) \ + || CPU(SPARC64) \ + || CPU(S390X) \ + || CPU(PPC64) +#define WTF_USE_JSVALUE64 1 +#else +#define WTF_USE_JSVALUE32_64 1 +#endif +#endif /* !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32_64) */ + +/* Disable the JIT on versions of GCC prior to 4.1 */ +#if !defined(ENABLE_JIT) && COMPILER(GCC) && !GCC_VERSION_AT_LEAST(4, 1, 0) +#define ENABLE_JIT 0 +#endif + +/* JIT is not implemented for Windows 64-bit */ +#if !defined(ENABLE_JIT) && OS(WINDOWS) && CPU(X86_64) +#define ENABLE_JIT 0 +#endif + +#if !defined(ENABLE_JIT) && CPU(SH4) && PLATFORM(QT) +#define ENABLE_JIT 1 +#endif + +/* The JIT is enabled by default on all x86, x86-64, ARM & MIPS platforms. */ +#if !defined(ENABLE_JIT) \ + && (CPU(X86) || CPU(X86_64) || CPU(ARM) || CPU(MIPS)) \ + && (OS(DARWIN) || !COMPILER(GCC) || GCC_VERSION_AT_LEAST(4, 1, 0)) \ + && !OS(WINCE) \ + && !OS(QNX) +#define ENABLE_JIT 1 +#endif + +/* If possible, try to enable a disassembler. This is optional. We proceed in two + steps: first we try to find some disassembler that we can use, and then we + decide if the high-level disassembler API can be enabled. */ +#if !defined(WTF_USE_UDIS86) && ENABLE(JIT) && PLATFORM(MAC) && (CPU(X86) || CPU(X86_64)) +#define WTF_USE_UDIS86 1 +#endif + +#if !defined(ENABLE_DISASSEMBLER) && USE(UDIS86) +#define ENABLE_DISASSEMBLER 1 +#endif + +/* On some of the platforms where we have a JIT, we want to also have the + low-level interpreter. */ +#if !defined(ENABLE_LLINT) \ + && ENABLE(JIT) \ + && (OS(DARWIN) || OS(LINUX)) \ + && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(GTK)) \ + && (CPU(X86) || CPU(X86_64) || CPU(ARM_THUMB2)) +#define ENABLE_LLINT 1 +#endif + +#if !defined(ENABLE_DFG_JIT) && ENABLE(JIT) +/* Enable the DFG JIT on X86 and X86_64. Only tested on Mac and GNU/Linux. */ +#if (CPU(X86) || CPU(X86_64)) && (PLATFORM(MAC) || OS(LINUX)) +#define ENABLE_DFG_JIT 1 +#endif +/* Enable the DFG JIT on ARMv7. Only tested on iOS. */ +#if CPU(ARM_THUMB2) && (PLATFORM(IOS) || PLATFORM(BLACKBERRY)) +#define ENABLE_DFG_JIT 1 +#endif +/* Enable the DFG JIT on ARM. */ +#if CPU(ARM_TRADITIONAL) +#define ENABLE_DFG_JIT 1 +#endif +#endif + +/* Profiling of types and values used by JIT code. DFG_JIT depends on it, but you + can enable it manually with DFG turned off if you want to use it as a standalone + profiler. In that case, you probably want to also enable VERBOSE_VALUE_PROFILE + below. */ +#if !defined(ENABLE_VALUE_PROFILER) && ENABLE(DFG_JIT) +#define ENABLE_VALUE_PROFILER 1 +#endif + +#if !defined(ENABLE_VERBOSE_VALUE_PROFILE) && ENABLE(VALUE_PROFILER) +#define ENABLE_VERBOSE_VALUE_PROFILE 0 +#endif + +#if !defined(ENABLE_SIMPLE_HEAP_PROFILING) +#define ENABLE_SIMPLE_HEAP_PROFILING 0 +#endif + +/* Counts uses of write barriers using sampling counters. Be sure to also + set ENABLE_SAMPLING_COUNTERS to 1. */ +#if !defined(ENABLE_WRITE_BARRIER_PROFILING) +#define ENABLE_WRITE_BARRIER_PROFILING 0 +#endif + +/* Ensure that either the JIT or the interpreter has been enabled. */ +#if !defined(ENABLE_CLASSIC_INTERPRETER) && !ENABLE(JIT) && !ENABLE(LLINT) +#define ENABLE_CLASSIC_INTERPRETER 1 +#endif + +/* If the jit and classic interpreter is not available, enable the LLInt C Loop: */ +#if !ENABLE(JIT) && !ENABLE(CLASSIC_INTERPRETER) + #define ENABLE_LLINT 1 + #define ENABLE_LLINT_C_LOOP 1 + #define ENABLE_DFG_JIT 0 +#endif + +/* Do a sanity check to make sure that we at least have one execution engine in + use: */ +#if !(ENABLE(JIT) || ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT)) +#error You have to have at least one execution model enabled to build JSC +#endif +/* Do a sanity check to make sure that we don't have both the classic interpreter + and the llint C loop in use at the same time: */ +#if ENABLE(CLASSIC_INTERPRETER) && ENABLE(LLINT_C_LOOP) +#error You cannot build both the classic interpreter and the llint C loop together +#endif + +/* Configure the JIT */ +#if CPU(X86) && COMPILER(MSVC) +#define JSC_HOST_CALL __fastcall +#elif CPU(X86) && COMPILER(GCC) +#define JSC_HOST_CALL __attribute__ ((fastcall)) +#else +#define JSC_HOST_CALL +#endif + +/* Configure the interpreter */ +#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(4, 0, 0, 0) && defined(__GNUC__)) +#define HAVE_COMPUTED_GOTO 1 +#endif +#if HAVE(COMPUTED_GOTO) && ENABLE(CLASSIC_INTERPRETER) +#define ENABLE_COMPUTED_GOTO_CLASSIC_INTERPRETER 1 +#endif + +/* Determine if we need to enable Computed Goto Opcodes or not: */ +#if (HAVE(COMPUTED_GOTO) && ENABLE(LLINT)) || ENABLE(COMPUTED_GOTO_CLASSIC_INTERPRETER) +#define ENABLE_COMPUTED_GOTO_OPCODES 1 +#endif + +/* Regular Expression Tracing - Set to 1 to trace RegExp's in jsc. Results dumped at exit */ +#define ENABLE_REGEXP_TRACING 0 + +/* Yet Another Regex Runtime - turned on by default for JIT enabled ports. */ +#if !defined(ENABLE_YARR_JIT) && (ENABLE(JIT) || ENABLE(LLINT_C_LOOP)) && !PLATFORM(CHROMIUM) +#define ENABLE_YARR_JIT 1 + +/* Setting this flag compares JIT results with interpreter results. */ +#define ENABLE_YARR_JIT_DEBUG 0 +#endif + +#if ENABLE(JIT) || ENABLE(YARR_JIT) +#define ENABLE_ASSEMBLER 1 +#endif + +/* Pick which allocator to use; we only need an executable allocator if the assembler is compiled in. + On x86-64 we use a single fixed mmap, on other platforms we mmap on demand. */ +#if ENABLE(ASSEMBLER) +#if CPU(X86_64) || PLATFORM(IOS) +#define ENABLE_EXECUTABLE_ALLOCATOR_FIXED 1 +#else +#define ENABLE_EXECUTABLE_ALLOCATOR_DEMAND 1 +#endif +#endif + +#if !defined(ENABLE_PAN_SCROLLING) && OS(WINDOWS) +#define ENABLE_PAN_SCROLLING 1 +#endif + +/* Use the QXmlStreamReader implementation for XMLDocumentParser */ +/* Use the QXmlQuery implementation for XSLTProcessor */ +#if PLATFORM(QT) +#if !USE(LIBXML2) +#define WTF_USE_QXMLSTREAM 1 +#define WTF_USE_QXMLQUERY 1 +#endif +#endif + +/* Accelerated compositing */ +#if PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(QT) || (PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(WIN_CAIRO)) +#define WTF_USE_ACCELERATED_COMPOSITING 1 +#endif + +#if PLATFORM(MAC) || PLATFORM(IOS) +#define ENABLE_CSS_IMAGE_SET 1 +#endif + + +/* Qt always uses Texture Mapper */ +#if PLATFORM(QT) +#define WTF_USE_TEXTURE_MAPPER 1 +#if USE(3D_GRAPHICS) +#define WTF_USE_TEXTURE_MAPPER_GL 1 +#endif +#endif + +#if ENABLE(WEBGL) && !defined(WTF_USE_3D_GRAPHICS) +#define WTF_USE_3D_GRAPHICS 1 +#endif + +/* Compositing on the UI-process in WebKit2 */ +#if PLATFORM(QT) +#define WTF_USE_COORDINATED_GRAPHICS 1 +#endif + +#if PLATFORM(MAC) || PLATFORM(IOS) +#define WTF_USE_PROTECTION_SPACE_AUTH_CALLBACK 1 +#endif + +#if !ENABLE(NETSCAPE_PLUGIN_API) || (ENABLE(NETSCAPE_PLUGIN_API) && ((OS(UNIX) && (PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(WX))) || PLATFORM(EFL))) +#define ENABLE_PLUGIN_PACKAGE_SIMPLE_HASH 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 +#define ENABLE_THREADED_SCROLLING 1 +#endif + +/* Set up a define for a common error that is intended to cause a build error -- thus the space after Error. */ +#define WTF_PLATFORM_CFNETWORK Error USE_macro_should_be_used_with_CFNETWORK + +/* FIXME: Eventually we should enable this for all platforms and get rid of the define. */ +#if PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL) +#define WTF_USE_PLATFORM_STRATEGIES 1 +#endif + +#if PLATFORM(WIN) +#define WTF_USE_CROSS_PLATFORM_CONTEXT_MENUS 1 +#endif + +#if PLATFORM(MAC) && HAVE(ACCESSIBILITY) +#define WTF_USE_ACCESSIBILITY_CONTEXT_MENUS 1 +#endif + +#if CPU(ARM_THUMB2) +#define ENABLE_BRANCH_COMPACTION 1 +#endif + +#if !defined(ENABLE_THREADING_LIBDISPATCH) && HAVE(DISPATCH_H) +#define ENABLE_THREADING_LIBDISPATCH 1 +#elif !defined(ENABLE_THREADING_OPENMP) && defined(_OPENMP) +#define ENABLE_THREADING_OPENMP 1 +#elif !defined(THREADING_GENERIC) +#define ENABLE_THREADING_GENERIC 1 +#endif + +#if ENABLE(GLIB_SUPPORT) +#include +#endif + +/* FIXME: This define won't be needed once #27551 is fully landed. However, + since most ports try to support sub-project independence, adding new headers + to WTF causes many ports to break, and so this way we can address the build + breakages one port at a time. */ +#if !defined(WTF_USE_EXPORT_MACROS) && (PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(WX) || PLATFORM(BLACKBERRY)) +#define WTF_USE_EXPORT_MACROS 1 +#endif + +#if !defined(WTF_USE_EXPORT_MACROS_FOR_TESTING) && (PLATFORM(GTK) || PLATFORM(WIN)) +#define WTF_USE_EXPORT_MACROS_FOR_TESTING 1 +#endif + +#if (PLATFORM(QT) && !OS(DARWIN) && !OS(WINDOWS)) || PLATFORM(GTK) || PLATFORM(EFL) +#define WTF_USE_UNIX_DOMAIN_SOCKETS 1 +#endif + +#if !defined(ENABLE_COMPARE_AND_SWAP) && (OS(WINDOWS) || (COMPILER(GCC) && (CPU(X86) || CPU(X86_64) || CPU(ARM_THUMB2)))) +#define ENABLE_COMPARE_AND_SWAP 1 +#endif + +#define ENABLE_OBJECT_MARK_LOGGING 0 + +#if !defined(ENABLE_PARALLEL_GC) && !ENABLE(OBJECT_MARK_LOGGING) && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(BLACKBERRY) || PLATFORM(GTK)) && ENABLE(COMPARE_AND_SWAP) +#define ENABLE_PARALLEL_GC 1 +#elif PLATFORM(QT) +// Parallel GC is temporarily disabled on Qt because of regular crashes, see https://bugs.webkit.org/show_bug.cgi?id=90957 for details +#define ENABLE_PARALLEL_GC 0 +#endif + +#if !defined(ENABLE_GC_VALIDATION) && !defined(NDEBUG) +#define ENABLE_GC_VALIDATION 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 +#define WTF_USE_AVFOUNDATION 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 +#define WTF_USE_COREMEDIA 1 +#endif + +#if PLATFORM(MAC) || PLATFORM(GTK) || PLATFORM(EFL) || (PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(WIN_CAIRO)) || PLATFORM(BLACKBERRY) +#define WTF_USE_REQUEST_ANIMATION_FRAME_TIMER 1 +#endif + +#if PLATFORM(MAC) || PLATFORM(BLACKBERRY) +#define WTF_USE_REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR 1 +#endif + +#if PLATFORM(MAC) && (PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) +#define HAVE_INVERTED_WHEEL_EVENTS 1 +#endif + +#if PLATFORM(MAC) +#define WTF_USE_COREAUDIO 1 +#endif + +#if !defined(WTF_USE_V8) && PLATFORM(CHROMIUM) +#define WTF_USE_V8 1 +#endif + +/* Not using V8 implies using JSC and vice versa */ +#if !USE(V8) +#define WTF_USE_JSC 1 +#endif + +#if ENABLE(NOTIFICATIONS) && PLATFORM(MAC) +#define ENABLE_TEXT_NOTIFICATIONS_ONLY 1 +#endif + +#if !defined(WTF_USE_ZLIB) && !PLATFORM(QT) +#define WTF_USE_ZLIB 1 +#endif + +#if PLATFORM(GTK) +#define WTF_DEPRECATED_STRING_OPERATORS +#endif + +#if PLATFORM(QT) +#include +#if defined(QT_OPENGL_ES_2) && !defined(WTF_USE_OPENGL_ES_2) +#define WTF_USE_OPENGL_ES_2 1 +#endif +#endif + +#endif /* WTF_Platform_h */ diff --git a/masm/wtf/PossiblyNull.h b/masm/wtf/PossiblyNull.h new file mode 100644 index 0000000000..46a7d713be --- /dev/null +++ b/masm/wtf/PossiblyNull.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PossiblyNull_h +#define PossiblyNull_h + +#include + +namespace WTF { + +template struct PossiblyNull { + PossiblyNull(T data) + : m_data(data) + { + } + PossiblyNull(const PossiblyNull& source) + : m_data(source.m_data) + { + source.m_data = 0; + } + ~PossiblyNull() { ASSERT(!m_data); } + bool getValue(T& out) WARN_UNUSED_RETURN; +private: + mutable T m_data; +}; + +template bool PossiblyNull::getValue(T& out) +{ + out = m_data; + bool result = !!m_data; + m_data = 0; + return result; +} + +} + +#endif diff --git a/masm/wtf/RefCounted.h b/masm/wtf/RefCounted.h new file mode 100644 index 0000000000..cea1434e1b --- /dev/null +++ b/masm/wtf/RefCounted.h @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RefCounted_h +#define RefCounted_h + +#include +#include +#include +#include +#include +#include + +namespace WTF { + +// This base class holds the non-template methods and attributes. +// The RefCounted class inherits from it reducing the template bloat +// generated by the compiler (technique called template hoisting). +class RefCountedBase { +public: + void ref() + { +#ifndef NDEBUG + // Start thread verification as soon as the ref count gets to 2. This + // heuristic reflects the fact that items are often created on one thread + // and then given to another thread to be used. + // FIXME: Make this restriction tigher. Especially as we move to more + // common methods for sharing items across threads like CrossThreadCopier.h + // We should be able to add a "detachFromThread" method to make this explicit. + if (m_refCount == 1) + m_verifier.setShared(true); +#endif + // If this assert fires, it either indicates a thread safety issue or + // that the verification needs to change. See ThreadRestrictionVerifier for + // the different modes. + ASSERT(m_verifier.isSafeToUse()); + ASSERT(!m_deletionHasBegun); + ASSERT(!m_adoptionIsRequired); + ++m_refCount; + } + + bool hasOneRef() const + { + ASSERT(m_verifier.isSafeToUse()); + ASSERT(!m_deletionHasBegun); + return m_refCount == 1; + } + + int refCount() const + { + ASSERT(m_verifier.isSafeToUse()); + return m_refCount; + } + + void setMutexForVerifier(Mutex&); + +#if HAVE(DISPATCH_H) + void setDispatchQueueForVerifier(dispatch_queue_t); +#endif + + // Turns off verification. Use of this method is discouraged (instead extend + // ThreadRestrictionVerifier to verify your case). + // NB. It is necessary to call this in the constructor of many objects in + // JavaScriptCore, because JavaScriptCore objects may be used from multiple + // threads even if the reference counting is done in a racy manner. This is + // because a JSC instance may be used from multiple threads so long as all + // accesses into that instance are protected by a per-instance lock. It would + // be absolutely wrong to prohibit this pattern, and it would be a disastrous + // regression to require that the objects within that instance use a thread- + // safe version of reference counting. + void turnOffVerifier() + { +#ifndef NDEBUG + m_verifier.turnOffVerification(); +#endif + } + + void relaxAdoptionRequirement() + { +#ifndef NDEBUG + ASSERT(!m_deletionHasBegun); + ASSERT(m_adoptionIsRequired); + m_adoptionIsRequired = false; +#endif + } + + // Helper for generating JIT code. Please do not use for non-JIT purposes. + const int* addressOfCount() const + { + return &m_refCount; + } + +protected: + RefCountedBase() + : m_refCount(1) +#ifndef NDEBUG + , m_deletionHasBegun(false) + , m_adoptionIsRequired(true) +#endif + { + } + + ~RefCountedBase() + { + ASSERT(m_deletionHasBegun); + ASSERT(!m_adoptionIsRequired); + } + + // Returns whether the pointer should be freed or not. + bool derefBase() + { + ASSERT(m_verifier.isSafeToUse()); + ASSERT(!m_deletionHasBegun); + ASSERT(!m_adoptionIsRequired); + + ASSERT(m_refCount > 0); + if (m_refCount == 1) { +#ifndef NDEBUG + m_deletionHasBegun = true; +#endif + return true; + } + + --m_refCount; +#ifndef NDEBUG + // Stop thread verification when the ref goes to 1 because it + // is safe to be passed to another thread at this point. + if (m_refCount == 1) + m_verifier.setShared(false); +#endif + return false; + } + +#ifndef NDEBUG + bool deletionHasBegun() const + { + return m_deletionHasBegun; + } +#endif + +private: + +#ifndef NDEBUG + friend void adopted(RefCountedBase*); +#endif + + int m_refCount; +#ifndef NDEBUG + bool m_deletionHasBegun; + bool m_adoptionIsRequired; + ThreadRestrictionVerifier m_verifier; +#endif +}; + +#ifndef NDEBUG + +inline void adopted(RefCountedBase* object) +{ + if (!object) + return; + ASSERT(!object->m_deletionHasBegun); + object->m_adoptionIsRequired = false; +} + +#endif + +template class RefCounted : public RefCountedBase { + WTF_MAKE_NONCOPYABLE(RefCounted); WTF_MAKE_FAST_ALLOCATED; +public: + void deref() + { + if (derefBase()) + delete static_cast(this); + } + +protected: + RefCounted() { } + ~RefCounted() + { + } +}; + +template class RefCountedCustomAllocated : public RefCountedBase { + WTF_MAKE_NONCOPYABLE(RefCountedCustomAllocated); + +public: + void deref() + { + if (derefBase()) + delete static_cast(this); + } + +protected: + ~RefCountedCustomAllocated() + { + } +}; + +#ifdef NDEBUG +inline void RefCountedBase::setMutexForVerifier(Mutex&) { } +#else +inline void RefCountedBase::setMutexForVerifier(Mutex& mutex) +{ + m_verifier.setMutexMode(mutex); +} +#endif + +#if HAVE(DISPATCH_H) +#ifdef NDEBUG +inline void RefCountedBase::setDispatchQueueForVerifier(dispatch_queue_t) { } +#else +inline void RefCountedBase::setDispatchQueueForVerifier(dispatch_queue_t queue) +{ + m_verifier.setDispatchQueueMode(queue); +} +#endif // NDEBUG +#endif // HAVE(DISPATCH_H) + +} // namespace WTF + +using WTF::RefCounted; +using WTF::RefCountedCustomAllocated; + +#endif // RefCounted_h diff --git a/masm/wtf/RefPtr.h b/masm/wtf/RefPtr.h new file mode 100644 index 0000000000..322cbd6f8f --- /dev/null +++ b/masm/wtf/RefPtr.h @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +// RefPtr and PassRefPtr are documented at http://webkit.org/coding/RefPtr.html + +#ifndef WTF_RefPtr_h +#define WTF_RefPtr_h + +#include +#include +#include + +namespace WTF { + + enum PlacementNewAdoptType { PlacementNewAdopt }; + + template class PassRefPtr; + + enum HashTableDeletedValueType { HashTableDeletedValue }; + + template class RefPtr { + WTF_MAKE_FAST_ALLOCATED; + public: + ALWAYS_INLINE RefPtr() : m_ptr(0) { } + ALWAYS_INLINE RefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); } + ALWAYS_INLINE RefPtr(const RefPtr& o) : m_ptr(o.m_ptr) { refIfNotNull(m_ptr); } + template RefPtr(const RefPtr& o) : m_ptr(o.get()) { refIfNotNull(m_ptr); } + + // See comments in PassRefPtr.h for an explanation of why this takes a const reference. + template RefPtr(const PassRefPtr&); + + // Special constructor for cases where we overwrite an object in place. + ALWAYS_INLINE RefPtr(PlacementNewAdoptType) { } + + // Hash table deleted values, which are only constructed and never copied or destroyed. + RefPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) { } + bool isHashTableDeletedValue() const { return m_ptr == hashTableDeletedValue(); } + + ALWAYS_INLINE ~RefPtr() { derefIfNotNull(m_ptr); } + + T* get() const { return m_ptr; } + + void clear(); + PassRefPtr release() { PassRefPtr tmp = adoptRef(m_ptr); m_ptr = 0; return tmp; } + + T& operator*() const { return *m_ptr; } + ALWAYS_INLINE T* operator->() const { return m_ptr; } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef T* (RefPtr::*UnspecifiedBoolType); + operator UnspecifiedBoolType() const { return m_ptr ? &RefPtr::m_ptr : 0; } + + RefPtr& operator=(const RefPtr&); + RefPtr& operator=(T*); + RefPtr& operator=(const PassRefPtr&); +#if !COMPILER_SUPPORTS(CXX_NULLPTR) + RefPtr& operator=(std::nullptr_t) { clear(); return *this; } +#endif + template RefPtr& operator=(const RefPtr&); + template RefPtr& operator=(const PassRefPtr&); + + void swap(RefPtr&); + + static T* hashTableDeletedValue() { return reinterpret_cast(-1); } + + private: + T* m_ptr; + }; + + template template inline RefPtr::RefPtr(const PassRefPtr& o) + : m_ptr(o.leakRef()) + { + } + + template inline void RefPtr::clear() + { + T* ptr = m_ptr; + m_ptr = 0; + derefIfNotNull(ptr); + } + + template inline RefPtr& RefPtr::operator=(const RefPtr& o) + { + T* optr = o.get(); + refIfNotNull(optr); + T* ptr = m_ptr; + m_ptr = optr; + derefIfNotNull(ptr); + return *this; + } + + template template inline RefPtr& RefPtr::operator=(const RefPtr& o) + { + T* optr = o.get(); + refIfNotNull(optr); + T* ptr = m_ptr; + m_ptr = optr; + derefIfNotNull(ptr); + return *this; + } + + template inline RefPtr& RefPtr::operator=(T* optr) + { + refIfNotNull(optr); + T* ptr = m_ptr; + m_ptr = optr; + derefIfNotNull(ptr); + return *this; + } + + template inline RefPtr& RefPtr::operator=(const PassRefPtr& o) + { + T* ptr = m_ptr; + m_ptr = o.leakRef(); + derefIfNotNull(ptr); + return *this; + } + + template template inline RefPtr& RefPtr::operator=(const PassRefPtr& o) + { + T* ptr = m_ptr; + m_ptr = o.leakRef(); + derefIfNotNull(ptr); + return *this; + } + + template inline void RefPtr::swap(RefPtr& o) + { + std::swap(m_ptr, o.m_ptr); + } + + template inline void swap(RefPtr& a, RefPtr& b) + { + a.swap(b); + } + + template inline bool operator==(const RefPtr& a, const RefPtr& b) + { + return a.get() == b.get(); + } + + template inline bool operator==(const RefPtr& a, U* b) + { + return a.get() == b; + } + + template inline bool operator==(T* a, const RefPtr& b) + { + return a == b.get(); + } + + template inline bool operator!=(const RefPtr& a, const RefPtr& b) + { + return a.get() != b.get(); + } + + template inline bool operator!=(const RefPtr& a, U* b) + { + return a.get() != b; + } + + template inline bool operator!=(T* a, const RefPtr& b) + { + return a != b.get(); + } + + template inline RefPtr static_pointer_cast(const RefPtr& p) + { + return RefPtr(static_cast(p.get())); + } + + template inline RefPtr const_pointer_cast(const RefPtr& p) + { + return RefPtr(const_cast(p.get())); + } + + template inline T* getPtr(const RefPtr& p) + { + return p.get(); + } + +} // namespace WTF + +using WTF::RefPtr; +using WTF::static_pointer_cast; +using WTF::const_pointer_cast; + +#endif // WTF_RefPtr_h diff --git a/masm/wtf/StdLibExtras.h b/masm/wtf/StdLibExtras.h new file mode 100644 index 0000000000..e66df2c310 --- /dev/null +++ b/masm/wtf/StdLibExtras.h @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_StdLibExtras_h +#define WTF_StdLibExtras_h + +#include +#include + +// Use these to declare and define a static local variable (static T;) so that +// it is leaked so that its destructors are not called at exit. Using this +// macro also allows workarounds a compiler bug present in Apple's version of GCC 4.0.1. +#ifndef DEFINE_STATIC_LOCAL +#if COMPILER(GCC) && defined(__APPLE_CC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 1 +#define DEFINE_STATIC_LOCAL(type, name, arguments) \ + static type* name##Ptr = new type arguments; \ + type& name = *name##Ptr +#else +#define DEFINE_STATIC_LOCAL(type, name, arguments) \ + static type& name = *new type arguments +#endif +#endif + +// Use this macro to declare and define a debug-only global variable that may have a +// non-trivial constructor and destructor. When building with clang, this will suppress +// warnings about global constructors and exit-time destructors. +#ifndef NDEBUG +#if COMPILER(CLANG) +#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \ + _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \ + static type name arguments; \ + _Pragma("clang diagnostic pop") +#else +#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ + static type name arguments; +#endif // COMPILER(CLANG) +#else +#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) +#endif // NDEBUG + +// OBJECT_OFFSETOF: Like the C++ offsetof macro, but you can use it with classes. +// The magic number 0x4000 is insignificant. We use it to avoid using NULL, since +// NULL can cause compiler problems, especially in cases of multiple inheritance. +#define OBJECT_OFFSETOF(class, field) (reinterpret_cast(&(reinterpret_cast(0x4000)->field)) - 0x4000) + +// STRINGIZE: Can convert any value to quoted string, even expandable macros +#define STRINGIZE(exp) #exp +#define STRINGIZE_VALUE_OF(exp) STRINGIZE(exp) + +/* + * The reinterpret_cast([pointer to Type2]) expressions - where + * sizeof(Type1) > sizeof(Type2) - cause the following warning on ARM with GCC: + * increases required alignment of target type. + * + * An implicit or an extra static_cast bypasses the warning. + * For more info see the following bugzilla entries: + * - https://bugs.webkit.org/show_bug.cgi?id=38045 + * - http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43976 + */ +#if (CPU(ARM) || CPU(MIPS)) && COMPILER(GCC) +template +bool isPointerTypeAlignmentOkay(Type* ptr) +{ + return !(reinterpret_cast(ptr) % __alignof__(Type)); +} + +template +TypePtr reinterpret_cast_ptr(void* ptr) +{ + ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast(ptr))); + return reinterpret_cast(ptr); +} + +template +TypePtr reinterpret_cast_ptr(const void* ptr) +{ + ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast(ptr))); + return reinterpret_cast(ptr); +} +#else +template +bool isPointerTypeAlignmentOkay(Type*) +{ + return true; +} +#define reinterpret_cast_ptr reinterpret_cast +#endif + +namespace WTF { + +static const size_t KB = 1024; +static const size_t MB = 1024 * 1024; + +inline bool isPointerAligned(void* p) +{ + return !((intptr_t)(p) & (sizeof(char*) - 1)); +} + +inline bool is8ByteAligned(void* p) +{ + return !((uintptr_t)(p) & (sizeof(double) - 1)); +} + +/* + * C++'s idea of a reinterpret_cast lacks sufficient cojones. + */ +template +inline TO bitwise_cast(FROM from) +{ + COMPILE_ASSERT(sizeof(TO) == sizeof(FROM), WTF_bitwise_cast_sizeof_casted_types_is_equal); + union { + FROM from; + TO to; + } u; + u.from = from; + return u.to; +} + +template +inline To safeCast(From value) +{ + ASSERT(isInBounds(value)); + return static_cast(value); +} + +// Returns a count of the number of bits set in 'bits'. +inline size_t bitCount(unsigned bits) +{ + bits = bits - ((bits >> 1) & 0x55555555); + bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); + return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; +} + +// Macro that returns a compile time constant with the length of an array, but gives an error if passed a non-array. +template char (&ArrayLengthHelperFunction(T (&)[Size]))[Size]; +// GCC needs some help to deduce a 0 length array. +#if COMPILER(GCC) +template char (&ArrayLengthHelperFunction(T (&)[0]))[0]; +#endif +#define WTF_ARRAY_LENGTH(array) sizeof(::WTF::ArrayLengthHelperFunction(array)) + +// Efficient implementation that takes advantage of powers of two. +inline size_t roundUpToMultipleOf(size_t divisor, size_t x) +{ + ASSERT(divisor && !(divisor & (divisor - 1))); + size_t remainderMask = divisor - 1; + return (x + remainderMask) & ~remainderMask; +} +template inline size_t roundUpToMultipleOf(size_t x) +{ + COMPILE_ASSERT(divisor && !(divisor & (divisor - 1)), divisor_is_a_power_of_two); + return roundUpToMultipleOf(divisor, x); +} + +enum BinarySearchMode { + KeyMustBePresentInArray, + KeyMustNotBePresentInArray +}; + +// Binary search algorithm, calls extractKey on pre-sorted elements in array, +// compares result with key (KeyTypes should be comparable with '--', '<', '>'). +template +inline ArrayElementType* binarySearch(ArrayElementType* array, size_t size, KeyType key, BinarySearchMode mode = KeyMustBePresentInArray) +{ + // The array must contain at least one element (pre-condition, array does contain key). + // If the array contains only one element, no need to do the comparison. + while (size > 1) { + // Pick an element to check, half way through the array, and read the value. + int pos = (size - 1) >> 1; + KeyType val = extractKey(&array[pos]); + + // If the key matches, success! + if (val == key) + return &array[pos]; + // The item we are looking for is smaller than the item being check; reduce the value of 'size', + // chopping off the right hand half of the array. + else if (key < val) + size = pos; + // Discard all values in the left hand half of the array, up to and including the item at pos. + else { + size -= (pos + 1); + array += (pos + 1); + } + + // In case of BinarySearchMode = KeyMustBePresentInArray 'size' should never reach zero. + if (mode == KeyMustBePresentInArray) + ASSERT(size); + } + + // In case of BinarySearchMode = KeyMustBePresentInArray if we reach this point + // we've chopped down to one element, no need to check it matches + if (mode == KeyMustBePresentInArray) { + ASSERT(size == 1); + ASSERT(key == extractKey(&array[0])); + } + + return &array[0]; +} + +// Modified binary search algorithm that uses a functor. Note that this is strictly +// more powerful than the above, but results in somewhat less template specialization. +// Hence, depending on inlining heuristics, it might be slower. +template +inline ArrayElementType* binarySearchWithFunctor(ArrayElementType* array, size_t size, KeyType key, BinarySearchMode mode = KeyMustBePresentInArray, const ExtractKey& extractKey = ExtractKey()) +{ + // The array must contain at least one element (pre-condition, array does contain key). + // If the array contains only one element, no need to do the comparison. + while (size > 1) { + // Pick an element to check, half way through the array, and read the value. + int pos = (size - 1) >> 1; + KeyType val = extractKey(&array[pos]); + + // If the key matches, success! + if (val == key) + return &array[pos]; + // The item we are looking for is smaller than the item being check; reduce the value of 'size', + // chopping off the right hand half of the array. + else if (key < val) + size = pos; + // Discard all values in the left hand half of the array, up to and including the item at pos. + else { + size -= (pos + 1); + array += (pos + 1); + } + + // In case of BinarySearchMode = KeyMustBePresentInArray 'size' should never reach zero. + if (mode == KeyMustBePresentInArray) + ASSERT(size); + } + + // In case of BinarySearchMode = KeyMustBePresentInArray if we reach this point + // we've chopped down to one element, no need to check it matches + if (mode == KeyMustBePresentInArray) { + ASSERT(size == 1); + ASSERT(key == extractKey(&array[0])); + } + + return &array[0]; +} + +// Modified binarySearch() algorithm designed for array-like classes that support +// operator[] but not operator+=. One example of a class that qualifies is +// SegmentedVector. +template +inline ArrayElementType* genericBinarySearch(ArrayType& array, size_t size, KeyType key) +{ + // The array must contain at least one element (pre-condition, array does conatin key). + // If the array only contains one element, no need to do the comparison. + size_t offset = 0; + while (size > 1) { + // Pick an element to check, half way through the array, and read the value. + int pos = (size - 1) >> 1; + KeyType val = extractKey(&array[offset + pos]); + + // If the key matches, success! + if (val == key) + return &array[offset + pos]; + // The item we are looking for is smaller than the item being check; reduce the value of 'size', + // chopping off the right hand half of the array. + if (key < val) + size = pos; + // Discard all values in the left hand half of the array, up to and including the item at pos. + else { + size -= (pos + 1); + offset += (pos + 1); + } + + // 'size' should never reach zero. + ASSERT(size); + } + + // If we reach this point we've chopped down to one element, no need to check it matches + ASSERT(size == 1); + ASSERT(key == extractKey(&array[offset])); + return &array[offset]; +} + +} // namespace WTF + +// This version of placement new omits a 0 check. +enum NotNullTag { NotNull }; +inline void* operator new(size_t, NotNullTag, void* location) +{ + ASSERT(location); + return location; +} + +using WTF::KB; +using WTF::MB; +using WTF::isPointerAligned; +using WTF::is8ByteAligned; +using WTF::binarySearch; +using WTF::bitwise_cast; +using WTF::safeCast; + +#endif // WTF_StdLibExtras_h diff --git a/masm/wtf/ThreadRestrictionVerifier.h b/masm/wtf/ThreadRestrictionVerifier.h new file mode 100644 index 0000000000..cff49d3184 --- /dev/null +++ b/masm/wtf/ThreadRestrictionVerifier.h @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ThreadRestrictionVerifier_h +#define ThreadRestrictionVerifier_h + +#include +#include +#include + +#if HAVE(DISPATCH_H) +#include +#endif + +#ifndef NDEBUG + +namespace WTF { + +// Verifies that a class is used in a way that respects its lack of thread-safety. +// The default mode is to verify that the object will only be used on a single thread. The +// thread gets captured when setShared(true) is called. +// The mode may be changed by calling useMutexMode (or turnOffVerification). +#if !USE(JSC) // This verifier is completely wrong for JavaScript implementations that use threads +class ThreadRestrictionVerifier { +public: + ThreadRestrictionVerifier() + : m_mode(SingleThreadVerificationMode) + , m_shared(false) + , m_owningThread(0) + , m_mutex(0) +#if HAVE(DISPATCH_H) + , m_owningQueue(0) +#endif + { + } + +#if HAVE(DISPATCH_H) + ~ThreadRestrictionVerifier() + { + if (m_owningQueue) + dispatch_release(m_owningQueue); + } +#endif + + void setMutexMode(Mutex& mutex) + { + m_mode = MutexVerificationMode; + m_mutex = &mutex; + } + +#if HAVE(DISPATCH_H) + void setDispatchQueueMode(dispatch_queue_t queue) + { + m_mode = SingleDispatchQueueVerificationMode; + m_owningQueue = queue; + dispatch_retain(m_owningQueue); + } +#endif + + void turnOffVerification() + { + m_mode = NoVerificationMode; + } + + // Indicates that the object may (or may not) be owned by more than one place. + void setShared(bool shared) + { +#if !ASSERT_DISABLED + bool previouslyShared = m_shared; +#endif + m_shared = shared; + + if (!m_shared) + return; + + switch (m_mode) { + case SingleThreadVerificationMode: + ASSERT(shared != previouslyShared); + // Capture the current thread to verify that subsequent ref/deref happen on this thread. + m_owningThread = currentThread(); + return; + +#if HAVE(DISPATCH_H) + case SingleDispatchQueueVerificationMode: +#endif + case MutexVerificationMode: + case NoVerificationMode: + return; + } + ASSERT_NOT_REACHED(); + } + + // Is it OK to use the object at this moment on the current thread? + bool isSafeToUse() const + { + if (!m_shared) + return true; + + switch (m_mode) { + case SingleThreadVerificationMode: + return m_owningThread == currentThread(); + + case MutexVerificationMode: + if (!m_mutex->tryLock()) + return true; + m_mutex->unlock(); + return false; + +#if HAVE(DISPATCH_H) + case SingleDispatchQueueVerificationMode: + return m_owningQueue == dispatch_get_current_queue(); +#endif + + case NoVerificationMode: + return true; + } + ASSERT_NOT_REACHED(); + return true; + } + +private: + enum VerificationMode { + SingleThreadVerificationMode, + MutexVerificationMode, + NoVerificationMode, +#if HAVE(DISPATCH_H) + SingleDispatchQueueVerificationMode, +#endif + }; + + VerificationMode m_mode; + bool m_shared; + + // Used by SingleThreadVerificationMode + ThreadIdentifier m_owningThread; + + // Used by MutexVerificationMode. + Mutex* m_mutex; + +#if HAVE(DISPATCH_H) + // Used by SingleDispatchQueueVerificationMode. + dispatch_queue_t m_owningQueue; +#endif +}; +#else // !USE(JSC) => so the JSC case +class ThreadRestrictionVerifier { +public: + ThreadRestrictionVerifier() + { + } + + void setMutexMode(Mutex&) + { + } + +#if HAVE(DISPATCH_H) + void setDispatchQueueMode(dispatch_queue_t) + { + } +#endif + + void turnOffVerification() + { + } + + // Indicates that the object may (or may not) be owned by more than one place. + void setShared(bool) + { + } + + // Is it OK to use the object at this moment on the current thread? + bool isSafeToUse() const + { + return true; + } +}; +#endif + +} + +#endif +#endif diff --git a/masm/wtf/ThreadSafeRefCounted.h b/masm/wtf/ThreadSafeRefCounted.h new file mode 100644 index 0000000000..44035e5474 --- /dev/null +++ b/masm/wtf/ThreadSafeRefCounted.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Note: The implementations of InterlockedIncrement and InterlockedDecrement are based + * on atomic_increment and atomic_exchange_and_add from the Boost C++ Library. The license + * is virtually identical to the Apple license above but is included here for completeness. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef ThreadSafeRefCounted_h +#define ThreadSafeRefCounted_h + +#include + +#include +#include +#include + +namespace WTF { + +class ThreadSafeRefCountedBase { + WTF_MAKE_NONCOPYABLE(ThreadSafeRefCountedBase); + WTF_MAKE_FAST_ALLOCATED; +public: + ThreadSafeRefCountedBase(int initialRefCount = 1) + : m_refCount(initialRefCount) + { + } + + void ref() + { +#if USE(LOCKFREE_THREADSAFEREFCOUNTED) + atomicIncrement(&m_refCount); +#else + MutexLocker locker(m_mutex); + ++m_refCount; +#endif + } + + bool hasOneRef() + { + return refCount() == 1; + } + + int refCount() const + { +#if !USE(LOCKFREE_THREADSAFEREFCOUNTED) + MutexLocker locker(m_mutex); +#endif + return static_cast(m_refCount); + } + +protected: + // Returns whether the pointer should be freed or not. + bool derefBase() + { +#if USE(LOCKFREE_THREADSAFEREFCOUNTED) + WTF_ANNOTATE_HAPPENS_BEFORE(&m_refCount); + if (atomicDecrement(&m_refCount) <= 0) { + WTF_ANNOTATE_HAPPENS_AFTER(&m_refCount); + return true; + } +#else + int refCount; + { + MutexLocker locker(m_mutex); + --m_refCount; + refCount = m_refCount; + } + if (refCount <= 0) + return true; +#endif + return false; + } + +private: + int m_refCount; +#if !USE(LOCKFREE_THREADSAFEREFCOUNTED) + mutable Mutex m_mutex; +#endif +}; + +template class ThreadSafeRefCounted : public ThreadSafeRefCountedBase { +public: + void deref() + { + if (derefBase()) + delete static_cast(this); + } + +protected: + ThreadSafeRefCounted() + { + } +}; + +} // namespace WTF + +using WTF::ThreadSafeRefCounted; + +#endif // ThreadSafeRefCounted_h diff --git a/masm/wtf/Threading.h b/masm/wtf/Threading.h new file mode 100644 index 0000000000..3e558fc681 --- /dev/null +++ b/masm/wtf/Threading.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Note: The implementations of InterlockedIncrement and InterlockedDecrement are based + * on atomic_increment and atomic_exchange_and_add from the Boost C++ Library. The license + * is virtually identical to the Apple license above but is included here for completeness. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef Threading_h +#define Threading_h + +#include + +#include +#include +#include +#include +#include +#include +#include + +// For portability, we do not use thread-safe statics natively supported by some compilers (e.g. gcc). +#define AtomicallyInitializedStatic(T, name) \ + WTF::lockAtomicallyInitializedStaticMutex(); \ + static T name; \ + WTF::unlockAtomicallyInitializedStaticMutex(); + +namespace WTF { + +typedef uint32_t ThreadIdentifier; +typedef void (*ThreadFunction)(void* argument); + +// This function must be called from the main thread. It is safe to call it repeatedly. +// Darwin is an exception to this rule: it is OK to call it from any thread, the only +// requirement is that the calls are not reentrant. +WTF_EXPORT_PRIVATE void initializeThreading(); + +// Returns 0 if thread creation failed. +// The thread name must be a literal since on some platforms it's passed in to the thread. +WTF_EXPORT_PRIVATE ThreadIdentifier createThread(ThreadFunction, void*, const char* threadName); + +// Internal platform-specific createThread implementation. +ThreadIdentifier createThreadInternal(ThreadFunction, void*, const char* threadName); + +// Called in the thread during initialization. +// Helpful for platforms where the thread name must be set from within the thread. +void initializeCurrentThreadInternal(const char* threadName); + +WTF_EXPORT_PRIVATE ThreadIdentifier currentThread(); +WTF_EXPORT_PRIVATE int waitForThreadCompletion(ThreadIdentifier); +WTF_EXPORT_PRIVATE void detachThread(ThreadIdentifier); + +WTF_EXPORT_PRIVATE void yield(); + +WTF_EXPORT_PRIVATE void lockAtomicallyInitializedStaticMutex(); +WTF_EXPORT_PRIVATE void unlockAtomicallyInitializedStaticMutex(); + +} // namespace WTF + +using WTF::ThreadIdentifier; +using WTF::createThread; +using WTF::currentThread; +using WTF::detachThread; +using WTF::waitForThreadCompletion; +using WTF::yield; + +#endif // Threading_h diff --git a/masm/wtf/ThreadingPrimitives.h b/masm/wtf/ThreadingPrimitives.h new file mode 100644 index 0000000000..ab7dc36ff7 --- /dev/null +++ b/masm/wtf/ThreadingPrimitives.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef ThreadingPrimitives_h +#define ThreadingPrimitives_h + +#include + +#include +#include +#include +#include + +#if OS(WINDOWS) +#include +#endif + +#if USE(PTHREADS) +#include +#endif + +namespace WTF { + +#if USE(PTHREADS) +typedef pthread_mutex_t PlatformMutex; +#if HAVE(PTHREAD_RWLOCK) +typedef pthread_rwlock_t PlatformReadWriteLock; +#else +typedef void* PlatformReadWriteLock; +#endif +typedef pthread_cond_t PlatformCondition; +#elif OS(WINDOWS) +struct PlatformMutex { + CRITICAL_SECTION m_internalMutex; + size_t m_recursionCount; +}; +typedef void* PlatformReadWriteLock; // FIXME: Implement. +struct PlatformCondition { + size_t m_waitersGone; + size_t m_waitersBlocked; + size_t m_waitersToUnblock; + HANDLE m_blockLock; + HANDLE m_blockQueue; + HANDLE m_unblockLock; + + bool timedWait(PlatformMutex&, DWORD durationMilliseconds); + void signal(bool unblockAll); +}; +#else +typedef void* PlatformMutex; +typedef void* PlatformReadWriteLock; +typedef void* PlatformCondition; +#endif + +class Mutex { + WTF_MAKE_NONCOPYABLE(Mutex); WTF_MAKE_FAST_ALLOCATED; +public: + WTF_EXPORT_PRIVATE Mutex(); + WTF_EXPORT_PRIVATE ~Mutex(); + + WTF_EXPORT_PRIVATE void lock(); + WTF_EXPORT_PRIVATE bool tryLock(); + WTF_EXPORT_PRIVATE void unlock(); + +public: + PlatformMutex& impl() { return m_mutex; } +private: + PlatformMutex m_mutex; +}; + +typedef Locker MutexLocker; + +class MutexTryLocker { + WTF_MAKE_NONCOPYABLE(MutexTryLocker); +public: + MutexTryLocker(Mutex& mutex) : m_mutex(mutex), m_locked(mutex.tryLock()) { } + ~MutexTryLocker() + { + if (m_locked) + m_mutex.unlock(); + } + + bool locked() const { return m_locked; } + +private: + Mutex& m_mutex; + bool m_locked; +}; + +class ReadWriteLock { + WTF_MAKE_NONCOPYABLE(ReadWriteLock); +public: + ReadWriteLock(); + ~ReadWriteLock(); + + void readLock(); + bool tryReadLock(); + + void writeLock(); + bool tryWriteLock(); + + void unlock(); + +private: + PlatformReadWriteLock m_readWriteLock; +}; + +class ThreadCondition { + WTF_MAKE_NONCOPYABLE(ThreadCondition); +public: + WTF_EXPORT_PRIVATE ThreadCondition(); + WTF_EXPORT_PRIVATE ~ThreadCondition(); + + WTF_EXPORT_PRIVATE void wait(Mutex& mutex); + // Returns true if the condition was signaled before absoluteTime, false if the absoluteTime was reached or is in the past. + // The absoluteTime is in seconds, starting on January 1, 1970. The time is assumed to use the same time zone as WTF::currentTime(). + WTF_EXPORT_PRIVATE bool timedWait(Mutex&, double absoluteTime); + WTF_EXPORT_PRIVATE void signal(); + WTF_EXPORT_PRIVATE void broadcast(); + +private: + PlatformCondition m_condition; +}; + +#if OS(WINDOWS) +// The absoluteTime is in seconds, starting on January 1, 1970. The time is assumed to use the same time zone as WTF::currentTime(). +// Returns an interval in milliseconds suitable for passing to one of the Win32 wait functions (e.g., ::WaitForSingleObject). +WTF_EXPORT_PRIVATE DWORD absoluteTimeToWaitTimeoutInterval(double absoluteTime); +#endif + +} // namespace WTF + +using WTF::Mutex; +using WTF::MutexLocker; +using WTF::MutexTryLocker; +using WTF::ThreadCondition; + +#if OS(WINDOWS) +using WTF::absoluteTimeToWaitTimeoutInterval; +#endif + +#endif // ThreadingPrimitives_h diff --git a/masm/wtf/TypeTraits.h b/masm/wtf/TypeTraits.h new file mode 100644 index 0000000000..097b2fd790 --- /dev/null +++ b/masm/wtf/TypeTraits.h @@ -0,0 +1,435 @@ + /* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2010 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef TypeTraits_h +#define TypeTraits_h + +#include + +#if (defined(__GLIBCXX__) && (__GLIBCXX__ >= 20070724) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || (defined(_MSC_VER) && (_MSC_VER >= 1600)) +#include +#if defined(__GLIBCXX__) && (__GLIBCXX__ >= 20070724) && defined(__GXX_EXPERIMENTAL_CXX0X__) +#include +#endif +#endif + +namespace WTF { + + // The following are provided in this file: + // + // Conditional::Type + // + // IsInteger::value + // IsPod::value, see the definition for a note about its limitations + // IsConvertibleToInteger::value + // + // IsArray::value + // + // IsSameType::value + // + // RemovePointer::Type + // RemoveReference::Type + // RemoveConst::Type + // RemoveVolatile::Type + // RemoveConstVolatile::Type + // RemoveExtent::Type + // + // DecayArray::Type + // + // COMPILE_ASSERT's in TypeTraits.cpp illustrate their usage and what they do. + + template struct Conditional { typedef If Type; }; + template struct Conditional { typedef Then Type; }; + + template struct IsInteger { static const bool value = false; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; +#if !COMPILER(MSVC) || defined(_NATIVE_WCHAR_T_DEFINED) + template<> struct IsInteger { static const bool value = true; }; +#endif + + template struct IsFloatingPoint { static const bool value = false; }; + template<> struct IsFloatingPoint { static const bool value = true; }; + template<> struct IsFloatingPoint { static const bool value = true; }; + template<> struct IsFloatingPoint { static const bool value = true; }; + + template struct IsArithmetic { static const bool value = IsInteger::value || IsFloatingPoint::value; }; + + // IsPod is misnamed as it doesn't cover all plain old data (pod) types. + // Specifically, it doesn't allow for enums or for structs. + template struct IsPod { static const bool value = IsArithmetic::value; }; + template struct IsPod { static const bool value = true; }; + + template class IsConvertibleToInteger { + // Avoid "possible loss of data" warning when using Microsoft's C++ compiler + // by not converting int's to doubles. + template class IsConvertibleToDouble; + template class IsConvertibleToDouble { + public: + static const bool value = false; + }; + + template class IsConvertibleToDouble { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + static YesType floatCheck(long double); + static NoType floatCheck(...); + static T& t; + public: + static const bool value = sizeof(floatCheck(t)) == sizeof(YesType); + }; + + public: + static const bool value = IsInteger::value || IsConvertibleToDouble::value, T>::value; + }; + + + template struct IsArray { + static const bool value = false; + }; + + template struct IsArray { + static const bool value = true; + }; + + template struct IsArray { + static const bool value = true; + }; + + + template struct IsSameType { + static const bool value = false; + }; + + template struct IsSameType { + static const bool value = true; + }; + + template class IsSubclass { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + static YesType subclassCheck(U*); + static NoType subclassCheck(...); + static T* t; + public: + static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); + }; + + template class U> class IsSubclassOfTemplate { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + template static YesType subclassCheck(U*); + static NoType subclassCheck(...); + static T* t; + public: + static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); + }; + + template class OuterTemplate> struct RemoveTemplate { + typedef T Type; + }; + + template class OuterTemplate> struct RemoveTemplate, OuterTemplate> { + typedef T Type; + }; + + template struct RemoveConst { + typedef T Type; + }; + + template struct RemoveConst { + typedef T Type; + }; + + template struct RemoveVolatile { + typedef T Type; + }; + + template struct RemoveVolatile { + typedef T Type; + }; + + template struct RemoveConstVolatile { + typedef typename RemoveVolatile::Type>::Type Type; + }; + + template struct RemovePointer { + typedef T Type; + }; + + template struct RemovePointer { + typedef T Type; + }; + + template struct RemoveReference { + typedef T Type; + }; + + template struct RemoveReference { + typedef T Type; + }; + + template struct RemoveExtent { + typedef T Type; + }; + + template struct RemoveExtent { + typedef T Type; + }; + + template struct RemoveExtent { + typedef T Type; + }; + + template struct DecayArray { + typedef typename RemoveReference::Type U; + public: + typedef typename Conditional< + IsArray::value, + typename RemoveExtent::Type*, + typename RemoveConstVolatile::Type + >::Type Type; + }; + +#if (defined(__GLIBCXX__) && (__GLIBCXX__ >= 20070724) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || (defined(_MSC_VER) && (_MSC_VER >= 1600)) + + // GCC's libstdc++ 20070724 and later supports C++ TR1 type_traits in the std namespace. + // VC10 (VS2010) and later support C++ TR1 type_traits in the std::tr1 namespace. + template struct HasTrivialConstructor : public std::tr1::has_trivial_constructor { }; + template struct HasTrivialDestructor : public std::tr1::has_trivial_destructor { }; + +#else + + // This compiler doesn't provide type traits, so we provide basic HasTrivialConstructor + // and HasTrivialDestructor definitions. The definitions here include most built-in + // scalar types but do not include POD structs and classes. For the intended purposes of + // type_traits this results correct but potentially less efficient code. + template + struct IntegralConstant { + static const T value = v; + typedef T value_type; + typedef IntegralConstant type; + }; + + typedef IntegralConstant true_type; + typedef IntegralConstant false_type; + +#if COMPILER(CLANG) || (defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(__INTEL_COMPILER)) + // VC8 (VS2005) and later have built-in compiler support for HasTrivialConstructor / HasTrivialDestructor, + // but for some unexplained reason it doesn't work on built-in types. + template struct HasTrivialConstructor : public IntegralConstant{ }; + template struct HasTrivialDestructor : public IntegralConstant{ }; +#else + template struct HasTrivialConstructor : public false_type{ }; + template struct HasTrivialDestructor : public false_type{ }; +#endif + + template struct HasTrivialConstructor : public true_type{ }; + template struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + + #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + template <> struct HasTrivialConstructor : public true_type{ }; + #endif + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + + #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + template <> struct HasTrivialDestructor : public true_type{ }; + #endif + +#endif // __GLIBCXX__, etc. + +} // namespace WTF + +#endif // TypeTraits_h diff --git a/masm/wtf/UnusedParam.h b/masm/wtf/UnusedParam.h new file mode 100644 index 0000000000..31d70e7364 --- /dev/null +++ b/masm/wtf/UnusedParam.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_UnusedParam_h +#define WTF_UnusedParam_h + +#include + +#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) +template +inline void unusedParam(T& x) { (void)x; } +#define UNUSED_PARAM(variable) unusedParam(variable) +#else +#define UNUSED_PARAM(variable) (void)variable +#endif + +/* This is to keep the compiler from complaining when for local labels are + declared but not referenced. For example, this can happen with code that + works with auto-generated code. +*/ +#if COMPILER(MSVC) +#define UNUSED_LABEL(label) if (false) goto label +#else +#define UNUSED_LABEL(label) UNUSED_PARAM(&& label) +#endif + +#endif /* WTF_UnusedParam_h */ diff --git a/masm/wtf/ValueCheck.h b/masm/wtf/ValueCheck.h new file mode 100644 index 0000000000..2a86eb0f23 --- /dev/null +++ b/masm/wtf/ValueCheck.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ValueCheck_h +#define ValueCheck_h + +#include + +namespace WTF { + +template struct ValueCheck { + typedef T TraitType; + static void checkConsistency(const T&) { } +}; + +#if !ASSERT_DISABLED +template struct ValueCheck { + typedef P* TraitType; + static void checkConsistency(const P* p) + { + if (!p) + return; + ASSERT(fastMallocSize(p)); + ValueCheck

::checkConsistency(*p); + } +}; +#endif + +} + +#endif // ValueCheck_h diff --git a/masm/wtf/Vector.h b/masm/wtf/Vector.h new file mode 100644 index 0000000000..8a5eaf6213 --- /dev/null +++ b/masm/wtf/Vector.h @@ -0,0 +1,1263 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_Vector_h +#define WTF_Vector_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if PLATFORM(QT) +#include +#endif + +namespace WTF { + + using std::min; + using std::max; + + template + struct VectorDestructor; + + template + struct VectorDestructor + { + static void destruct(T*, T*) {} + }; + + template + struct VectorDestructor + { + static void destruct(T* begin, T* end) + { + for (T* cur = begin; cur != end; ++cur) + cur->~T(); + } + }; + + template + struct VectorInitializer; + + template + struct VectorInitializer + { + static void initialize(T*, T*) {} + }; + + template + struct VectorInitializer + { + static void initialize(T* begin, T* end) + { + for (T* cur = begin; cur != end; ++cur) + new (NotNull, cur) T; + } + }; + + template + struct VectorInitializer + { + static void initialize(T* begin, T* end) + { + memset(begin, 0, reinterpret_cast(end) - reinterpret_cast(begin)); + } + }; + + template + struct VectorMover; + + template + struct VectorMover + { + static void move(const T* src, const T* srcEnd, T* dst) + { + while (src != srcEnd) { + new (NotNull, dst) T(*src); +#if COMPILER(SUNCC) && __SUNPRO_CC <= 0x590 + const_cast(src)->~T(); // Work around obscure SunCC 12 compiler bug. +#else + src->~T(); +#endif + ++dst; + ++src; + } + } + static void moveOverlapping(const T* src, const T* srcEnd, T* dst) + { + if (src > dst) + move(src, srcEnd, dst); + else { + T* dstEnd = dst + (srcEnd - src); + while (src != srcEnd) { + --srcEnd; + --dstEnd; + new (NotNull, dstEnd) T(*srcEnd); + srcEnd->~T(); + } + } + } + }; + + template + struct VectorMover + { + static void move(const T* src, const T* srcEnd, T* dst) + { + memcpy(dst, src, reinterpret_cast(srcEnd) - reinterpret_cast(src)); + } + static void moveOverlapping(const T* src, const T* srcEnd, T* dst) + { + memmove(dst, src, reinterpret_cast(srcEnd) - reinterpret_cast(src)); + } + }; + + template + struct VectorCopier; + + template + struct VectorCopier + { + static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) + { + while (src != srcEnd) { + new (NotNull, dst) T(*src); + ++dst; + ++src; + } + } + }; + + template + struct VectorCopier + { + static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) + { + memcpy(dst, src, reinterpret_cast(srcEnd) - reinterpret_cast(src)); + } + }; + + template + struct VectorFiller; + + template + struct VectorFiller + { + static void uninitializedFill(T* dst, T* dstEnd, const T& val) + { + while (dst != dstEnd) { + new (NotNull, dst) T(val); + ++dst; + } + } + }; + + template + struct VectorFiller + { + static void uninitializedFill(T* dst, T* dstEnd, const T& val) + { + ASSERT(sizeof(T) == sizeof(char)); +#if COMPILER(GCC) && defined(_FORTIFY_SOURCE) + if (!__builtin_constant_p(dstEnd - dst) || (!(dstEnd - dst))) +#endif + memset(dst, val, dstEnd - dst); + } + }; + + template + struct VectorComparer; + + template + struct VectorComparer + { + static bool compare(const T* a, const T* b, size_t size) + { + for (size_t i = 0; i < size; ++i) + if (!(a[i] == b[i])) + return false; + return true; + } + }; + + template + struct VectorComparer + { + static bool compare(const T* a, const T* b, size_t size) + { + return memcmp(a, b, sizeof(T) * size) == 0; + } + }; + + template + struct VectorTypeOperations + { + static void destruct(T* begin, T* end) + { + VectorDestructor::needsDestruction, T>::destruct(begin, end); + } + + static void initialize(T* begin, T* end) + { + VectorInitializer::needsInitialization, VectorTraits::canInitializeWithMemset, T>::initialize(begin, end); + } + + static void move(const T* src, const T* srcEnd, T* dst) + { + VectorMover::canMoveWithMemcpy, T>::move(src, srcEnd, dst); + } + + static void moveOverlapping(const T* src, const T* srcEnd, T* dst) + { + VectorMover::canMoveWithMemcpy, T>::moveOverlapping(src, srcEnd, dst); + } + + static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) + { + VectorCopier::canCopyWithMemcpy, T>::uninitializedCopy(src, srcEnd, dst); + } + + static void uninitializedFill(T* dst, T* dstEnd, const T& val) + { + VectorFiller::canFillWithMemset, T>::uninitializedFill(dst, dstEnd, val); + } + + static bool compare(const T* a, const T* b, size_t size) + { + return VectorComparer::canCompareWithMemcmp, T>::compare(a, b, size); + } + }; + + template + class VectorBufferBase { + WTF_MAKE_NONCOPYABLE(VectorBufferBase); + public: + void allocateBuffer(size_t newCapacity) + { + ASSERT(newCapacity); + m_capacity = newCapacity; + if (newCapacity > std::numeric_limits::max() / sizeof(T)) + CRASH(); + m_buffer = static_cast(fastMalloc(newCapacity * sizeof(T))); + } + + bool tryAllocateBuffer(size_t newCapacity) + { + ASSERT(newCapacity); + if (newCapacity > std::numeric_limits::max() / sizeof(T)) + return false; + + T* newBuffer; + if (tryFastMalloc(newCapacity * sizeof(T)).getValue(newBuffer)) { + m_capacity = newCapacity; + m_buffer = newBuffer; + return true; + } + return false; + } + + bool shouldReallocateBuffer(size_t newCapacity) const + { +#if PLATFORM(BLACKBERRY) + // Tested on BlackBerry. + return VectorTraits::canMoveWithMemcpy && m_capacity && newCapacity; +#else + // FIXME: Return true on the platforms where realloc() gives better performance. + UNUSED_PARAM(newCapacity); + return false; +#endif + } + + void reallocateBuffer(size_t newCapacity) + { + ASSERT(shouldReallocateBuffer(newCapacity)); + m_capacity = newCapacity; + if (newCapacity > std::numeric_limits::max() / sizeof(T)) + CRASH(); + m_buffer = static_cast(fastRealloc(m_buffer, newCapacity * sizeof(T))); + } + + void deallocateBuffer(T* bufferToDeallocate) + { + if (!bufferToDeallocate) + return; + + if (m_buffer == bufferToDeallocate) { + m_buffer = 0; + m_capacity = 0; + } + + fastFree(bufferToDeallocate); + } + + T* buffer() { return m_buffer; } + const T* buffer() const { return m_buffer; } + T** bufferSlot() { return &m_buffer; } + size_t capacity() const { return m_capacity; } + + T* releaseBuffer() + { + T* buffer = m_buffer; + m_buffer = 0; + m_capacity = 0; + return buffer; + } + + protected: + VectorBufferBase() + : m_buffer(0) + , m_capacity(0) + { + } + + VectorBufferBase(T* buffer, size_t capacity) + : m_buffer(buffer) + , m_capacity(capacity) + { + } + + ~VectorBufferBase() + { + // FIXME: It would be nice to find a way to ASSERT that m_buffer hasn't leaked here. + } + + T* m_buffer; + size_t m_capacity; + }; + + template + class VectorBuffer; + + template + class VectorBuffer : private VectorBufferBase { + private: + typedef VectorBufferBase Base; + public: + VectorBuffer() + { + } + + VectorBuffer(size_t capacity) + { + // Calling malloc(0) might take a lock and may actually do an + // allocation on some systems. + if (capacity) + allocateBuffer(capacity); + } + + ~VectorBuffer() + { + deallocateBuffer(buffer()); + } + + void swap(VectorBuffer& other) + { + std::swap(m_buffer, other.m_buffer); + std::swap(m_capacity, other.m_capacity); + } + + void restoreInlineBufferIfNeeded() { } + + using Base::allocateBuffer; + using Base::tryAllocateBuffer; + using Base::shouldReallocateBuffer; + using Base::reallocateBuffer; + using Base::deallocateBuffer; + + using Base::buffer; + using Base::bufferSlot; + using Base::capacity; + + using Base::releaseBuffer; + private: + using Base::m_buffer; + using Base::m_capacity; + }; + + template + class VectorBuffer : private VectorBufferBase { + WTF_MAKE_NONCOPYABLE(VectorBuffer); + private: + typedef VectorBufferBase Base; + public: + VectorBuffer() + : Base(inlineBuffer(), inlineCapacity) + { + } + + VectorBuffer(size_t capacity) + : Base(inlineBuffer(), inlineCapacity) + { + if (capacity > inlineCapacity) + Base::allocateBuffer(capacity); + } + + ~VectorBuffer() + { + deallocateBuffer(buffer()); + } + + void allocateBuffer(size_t newCapacity) + { + // FIXME: This should ASSERT(!m_buffer) to catch misuse/leaks. + if (newCapacity > inlineCapacity) + Base::allocateBuffer(newCapacity); + else { + m_buffer = inlineBuffer(); + m_capacity = inlineCapacity; + } + } + + bool tryAllocateBuffer(size_t newCapacity) + { + if (newCapacity > inlineCapacity) + return Base::tryAllocateBuffer(newCapacity); + m_buffer = inlineBuffer(); + m_capacity = inlineCapacity; + return true; + } + + void deallocateBuffer(T* bufferToDeallocate) + { + if (bufferToDeallocate == inlineBuffer()) + return; + Base::deallocateBuffer(bufferToDeallocate); + } + + bool shouldReallocateBuffer(size_t newCapacity) const + { + // We cannot reallocate the inline buffer. + return Base::shouldReallocateBuffer(newCapacity) && std::min(m_capacity, newCapacity) > inlineCapacity; + } + + void reallocateBuffer(size_t newCapacity) + { + ASSERT(shouldReallocateBuffer(newCapacity)); + Base::reallocateBuffer(newCapacity); + } + + void swap(VectorBuffer& other) + { + if (buffer() == inlineBuffer() && other.buffer() == other.inlineBuffer()) { + WTF::swap(m_inlineBuffer, other.m_inlineBuffer); + std::swap(m_capacity, other.m_capacity); + } else if (buffer() == inlineBuffer()) { + m_buffer = other.m_buffer; + other.m_buffer = other.inlineBuffer(); + WTF::swap(m_inlineBuffer, other.m_inlineBuffer); + std::swap(m_capacity, other.m_capacity); + } else if (other.buffer() == other.inlineBuffer()) { + other.m_buffer = m_buffer; + m_buffer = inlineBuffer(); + WTF::swap(m_inlineBuffer, other.m_inlineBuffer); + std::swap(m_capacity, other.m_capacity); + } else { + std::swap(m_buffer, other.m_buffer); + std::swap(m_capacity, other.m_capacity); + } + } + + void restoreInlineBufferIfNeeded() + { + if (m_buffer) + return; + m_buffer = inlineBuffer(); + m_capacity = inlineCapacity; + } + + using Base::buffer; + using Base::bufferSlot; + using Base::capacity; + + T* releaseBuffer() + { + if (buffer() == inlineBuffer()) + return 0; + return Base::releaseBuffer(); + } + + private: + using Base::m_buffer; + using Base::m_capacity; + + static const size_t m_inlineBufferSize = inlineCapacity * sizeof(T); + T* inlineBuffer() { return reinterpret_cast_ptr(m_inlineBuffer.buffer); } + const T* inlineBuffer() const { return reinterpret_cast_ptr(m_inlineBuffer.buffer); } + + AlignedBuffer m_inlineBuffer; + }; + + template + class Vector { + WTF_MAKE_FAST_ALLOCATED; + private: + typedef VectorBuffer Buffer; + typedef VectorTypeOperations TypeOperations; + + class VectorReverseProxy; + + public: + typedef T ValueType; + + typedef T* iterator; + typedef const T* const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + Vector() + : m_size(0) + { + } + + explicit Vector(size_t size) + : m_size(size) + , m_buffer(size) + { + if (begin()) + TypeOperations::initialize(begin(), end()); + } + + ~Vector() + { + if (m_size) + shrink(0); + } + + Vector(const Vector&); + template + Vector(const Vector&); + + Vector& operator=(const Vector&); + template + Vector& operator=(const Vector&); + +#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) + Vector(Vector&&); + Vector& operator=(Vector&&); +#endif + + size_t size() const { return m_size; } + size_t capacity() const { return m_buffer.capacity(); } + bool isEmpty() const { return !size(); } + + T& at(size_t i) + { + ASSERT(i < size()); + return m_buffer.buffer()[i]; + } + const T& at(size_t i) const + { + ASSERT(i < size()); + return m_buffer.buffer()[i]; + } + + T& operator[](size_t i) { return at(i); } + const T& operator[](size_t i) const { return at(i); } + + T* data() { return m_buffer.buffer(); } + const T* data() const { return m_buffer.buffer(); } + T** dataSlot() { return m_buffer.bufferSlot(); } + + iterator begin() { return data(); } + iterator end() { return begin() + m_size; } + const_iterator begin() const { return data(); } + const_iterator end() const { return begin() + m_size; } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + VectorReverseProxy& reversed() { return static_cast(*this); } + const VectorReverseProxy& reversed() const { return static_cast(*this); } + + T& first() { return at(0); } + const T& first() const { return at(0); } + T& last() { return at(size() - 1); } + const T& last() const { return at(size() - 1); } + + template bool contains(const U&) const; + template size_t find(const U&) const; + template size_t reverseFind(const U&) const; + + void shrink(size_t size); + void grow(size_t size); + void resize(size_t size); + void reserveCapacity(size_t newCapacity); + bool tryReserveCapacity(size_t newCapacity); + void reserveInitialCapacity(size_t initialCapacity); + void shrinkCapacity(size_t newCapacity); + void shrinkToFit() { shrinkCapacity(size()); } + + void clear() { shrinkCapacity(0); } + + template void append(const U*, size_t); + template void append(const U&); + template void uncheckedAppend(const U& val); + template void append(const Vector&); + template bool tryAppend(const U*, size_t); + + template void insert(size_t position, const U*, size_t); + template void insert(size_t position, const U&); + template void insert(size_t position, const Vector&); + + template void prepend(const U*, size_t); + template void prepend(const U&); + template void prepend(const Vector&); + + void remove(size_t position); + void remove(size_t position, size_t length); + + void removeLast() + { + ASSERT(!isEmpty()); + shrink(size() - 1); + } + + Vector(size_t size, const T& val) + : m_size(size) + , m_buffer(size) + { + if (begin()) + TypeOperations::uninitializedFill(begin(), end(), val); + } + + void fill(const T&, size_t); + void fill(const T& val) { fill(val, size()); } + + template void appendRange(Iterator start, Iterator end); + + T* releaseBuffer(); + + void swap(Vector& other) + { + std::swap(m_size, other.m_size); + m_buffer.swap(other.m_buffer); + } + + void reverse(); + + void checkConsistency(); + + private: + void expandCapacity(size_t newMinCapacity); + const T* expandCapacity(size_t newMinCapacity, const T*); + bool tryExpandCapacity(size_t newMinCapacity); + const T* tryExpandCapacity(size_t newMinCapacity, const T*); + template U* expandCapacity(size_t newMinCapacity, U*); + template void appendSlowCase(const U&); + + class VectorReverseProxy : private Vector { + public: + typedef typename Vector::reverse_iterator iterator; + typedef typename Vector::const_reverse_iterator const_iterator; + + iterator begin() { return Vector::rbegin(); } + iterator end() { return Vector::rend(); } + const_iterator begin() const { return Vector::rbegin(); } + const_iterator end() const { return Vector::rend(); } + + private: + friend class Vector; + + // These are intentionally not implemented. + VectorReverseProxy(); + VectorReverseProxy(const VectorReverseProxy&); + VectorReverseProxy& operator=(const VectorReverseProxy&); + ~VectorReverseProxy(); + }; + + size_t m_size; + Buffer m_buffer; + }; + +#if PLATFORM(QT) + template + QDataStream& operator<<(QDataStream& stream, const Vector& data) + { + stream << qint64(data.size()); + foreach (const T& i, data) + stream << i; + return stream; + } + + template + QDataStream& operator>>(QDataStream& stream, Vector& data) + { + data.clear(); + qint64 count; + T item; + stream >> count; + data.reserveCapacity(count); + for (qint64 i = 0; i < count; ++i) { + stream >> item; + data.append(item); + } + return stream; + } +#endif + + template + Vector::Vector(const Vector& other) + : m_size(other.size()) + , m_buffer(other.capacity()) + { + if (begin()) + TypeOperations::uninitializedCopy(other.begin(), other.end(), begin()); + } + + template + template + Vector::Vector(const Vector& other) + : m_size(other.size()) + , m_buffer(other.capacity()) + { + if (begin()) + TypeOperations::uninitializedCopy(other.begin(), other.end(), begin()); + } + + template + Vector& Vector::operator=(const Vector& other) + { + if (&other == this) + return *this; + + if (size() > other.size()) + shrink(other.size()); + else if (other.size() > capacity()) { + clear(); + reserveCapacity(other.size()); + if (!begin()) + return *this; + } + +// Works around an assert in VS2010. See https://connect.microsoft.com/VisualStudio/feedback/details/558044/std-copy-should-not-check-dest-when-first-last +#if COMPILER(MSVC) && defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL + if (!begin()) + return *this; +#endif + + std::copy(other.begin(), other.begin() + size(), begin()); + TypeOperations::uninitializedCopy(other.begin() + size(), other.end(), end()); + m_size = other.size(); + + return *this; + } + + inline bool typelessPointersAreEqual(const void* a, const void* b) { return a == b; } + + template + template + Vector& Vector::operator=(const Vector& other) + { + // If the inline capacities match, we should call the more specific + // template. If the inline capacities don't match, the two objects + // shouldn't be allocated the same address. + ASSERT(!typelessPointersAreEqual(&other, this)); + + if (size() > other.size()) + shrink(other.size()); + else if (other.size() > capacity()) { + clear(); + reserveCapacity(other.size()); + if (!begin()) + return *this; + } + +// Works around an assert in VS2010. See https://connect.microsoft.com/VisualStudio/feedback/details/558044/std-copy-should-not-check-dest-when-first-last +#if COMPILER(MSVC) && defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL + if (!begin()) + return *this; +#endif + + std::copy(other.begin(), other.begin() + size(), begin()); + TypeOperations::uninitializedCopy(other.begin() + size(), other.end(), end()); + m_size = other.size(); + + return *this; + } + +#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) + template + Vector::Vector(Vector&& other) + : m_size(0) + { + // It's a little weird to implement a move constructor using swap but this way we + // don't have to add a move constructor to VectorBuffer. + swap(other); + } + + template + Vector& Vector::operator=(Vector&& other) + { + swap(other); + return *this; + } +#endif + + template + template + bool Vector::contains(const U& value) const + { + return find(value) != notFound; + } + + template + template + size_t Vector::find(const U& value) const + { + for (size_t i = 0; i < size(); ++i) { + if (at(i) == value) + return i; + } + return notFound; + } + + template + template + size_t Vector::reverseFind(const U& value) const + { + for (size_t i = 1; i <= size(); ++i) { + const size_t index = size() - i; + if (at(index) == value) + return index; + } + return notFound; + } + + template + void Vector::fill(const T& val, size_t newSize) + { + if (size() > newSize) + shrink(newSize); + else if (newSize > capacity()) { + clear(); + reserveCapacity(newSize); + if (!begin()) + return; + } + + std::fill(begin(), end(), val); + TypeOperations::uninitializedFill(end(), begin() + newSize, val); + m_size = newSize; + } + + template + template + void Vector::appendRange(Iterator start, Iterator end) + { + for (Iterator it = start; it != end; ++it) + append(*it); + } + + template + void Vector::expandCapacity(size_t newMinCapacity) + { + reserveCapacity(max(newMinCapacity, max(static_cast(16), capacity() + capacity() / 4 + 1))); + } + + template + const T* Vector::expandCapacity(size_t newMinCapacity, const T* ptr) + { + if (ptr < begin() || ptr >= end()) { + expandCapacity(newMinCapacity); + return ptr; + } + size_t index = ptr - begin(); + expandCapacity(newMinCapacity); + return begin() + index; + } + + template + bool Vector::tryExpandCapacity(size_t newMinCapacity) + { + return tryReserveCapacity(max(newMinCapacity, max(static_cast(16), capacity() + capacity() / 4 + 1))); + } + + template + const T* Vector::tryExpandCapacity(size_t newMinCapacity, const T* ptr) + { + if (ptr < begin() || ptr >= end()) { + if (!tryExpandCapacity(newMinCapacity)) + return 0; + return ptr; + } + size_t index = ptr - begin(); + if (!tryExpandCapacity(newMinCapacity)) + return 0; + return begin() + index; + } + + template template + inline U* Vector::expandCapacity(size_t newMinCapacity, U* ptr) + { + expandCapacity(newMinCapacity); + return ptr; + } + + template + inline void Vector::resize(size_t size) + { + if (size <= m_size) + TypeOperations::destruct(begin() + size, end()); + else { + if (size > capacity()) + expandCapacity(size); + if (begin()) + TypeOperations::initialize(end(), begin() + size); + } + + m_size = size; + } + + template + void Vector::shrink(size_t size) + { + ASSERT(size <= m_size); + TypeOperations::destruct(begin() + size, end()); + m_size = size; + } + + template + void Vector::grow(size_t size) + { + ASSERT(size >= m_size); + if (size > capacity()) + expandCapacity(size); + if (begin()) + TypeOperations::initialize(end(), begin() + size); + m_size = size; + } + + template + void Vector::reserveCapacity(size_t newCapacity) + { + if (newCapacity <= capacity()) + return; + T* oldBuffer = begin(); + T* oldEnd = end(); + m_buffer.allocateBuffer(newCapacity); + if (begin()) + TypeOperations::move(oldBuffer, oldEnd, begin()); + m_buffer.deallocateBuffer(oldBuffer); + } + + template + bool Vector::tryReserveCapacity(size_t newCapacity) + { + if (newCapacity <= capacity()) + return true; + T* oldBuffer = begin(); + T* oldEnd = end(); + if (!m_buffer.tryAllocateBuffer(newCapacity)) + return false; + ASSERT(begin()); + TypeOperations::move(oldBuffer, oldEnd, begin()); + m_buffer.deallocateBuffer(oldBuffer); + return true; + } + + template + inline void Vector::reserveInitialCapacity(size_t initialCapacity) + { + ASSERT(!m_size); + ASSERT(capacity() == inlineCapacity); + if (initialCapacity > inlineCapacity) + m_buffer.allocateBuffer(initialCapacity); + } + + template + void Vector::shrinkCapacity(size_t newCapacity) + { + if (newCapacity >= capacity()) + return; + + if (newCapacity < size()) + shrink(newCapacity); + + T* oldBuffer = begin(); + if (newCapacity > 0) { + if (m_buffer.shouldReallocateBuffer(newCapacity)) { + m_buffer.reallocateBuffer(newCapacity); + return; + } + + T* oldEnd = end(); + m_buffer.allocateBuffer(newCapacity); + if (begin() != oldBuffer) + TypeOperations::move(oldBuffer, oldEnd, begin()); + } + + m_buffer.deallocateBuffer(oldBuffer); + m_buffer.restoreInlineBufferIfNeeded(); + } + + // Templatizing these is better than just letting the conversion happen implicitly, + // because for instance it allows a PassRefPtr to be appended to a RefPtr vector + // without refcount thrash. + + template template + void Vector::append(const U* data, size_t dataSize) + { + size_t newSize = m_size + dataSize; + if (newSize > capacity()) { + data = expandCapacity(newSize, data); + if (!begin()) + return; + } + if (newSize < m_size) + CRASH(); + T* dest = end(); + for (size_t i = 0; i < dataSize; ++i) + new (NotNull, &dest[i]) T(data[i]); + m_size = newSize; + } + + template template + bool Vector::tryAppend(const U* data, size_t dataSize) + { + size_t newSize = m_size + dataSize; + if (newSize > capacity()) { + data = tryExpandCapacity(newSize, data); + if (!data) + return false; + ASSERT(begin()); + } + if (newSize < m_size) + return false; + T* dest = end(); + for (size_t i = 0; i < dataSize; ++i) + new (NotNull, &dest[i]) T(data[i]); + m_size = newSize; + return true; + } + + template template + ALWAYS_INLINE void Vector::append(const U& val) + { + if (size() != capacity()) { + new (NotNull, end()) T(val); + ++m_size; + return; + } + + appendSlowCase(val); + } + + template template + void Vector::appendSlowCase(const U& val) + { + ASSERT(size() == capacity()); + + const U* ptr = &val; + ptr = expandCapacity(size() + 1, ptr); + if (!begin()) + return; + + new (NotNull, end()) T(*ptr); + ++m_size; + } + + // This version of append saves a branch in the case where you know that the + // vector's capacity is large enough for the append to succeed. + + template template + inline void Vector::uncheckedAppend(const U& val) + { + ASSERT(size() < capacity()); + const U* ptr = &val; + new (NotNull, end()) T(*ptr); + ++m_size; + } + + // This method should not be called append, a better name would be appendElements. + // It could also be eliminated entirely, and call sites could just use + // appendRange(val.begin(), val.end()). + template template + inline void Vector::append(const Vector& val) + { + append(val.begin(), val.size()); + } + + template template + void Vector::insert(size_t position, const U* data, size_t dataSize) + { + ASSERT(position <= size()); + size_t newSize = m_size + dataSize; + if (newSize > capacity()) { + data = expandCapacity(newSize, data); + if (!begin()) + return; + } + if (newSize < m_size) + CRASH(); + T* spot = begin() + position; + TypeOperations::moveOverlapping(spot, end(), spot + dataSize); + for (size_t i = 0; i < dataSize; ++i) + new (NotNull, &spot[i]) T(data[i]); + m_size = newSize; + } + + template template + inline void Vector::insert(size_t position, const U& val) + { + ASSERT(position <= size()); + const U* data = &val; + if (size() == capacity()) { + data = expandCapacity(size() + 1, data); + if (!begin()) + return; + } + T* spot = begin() + position; + TypeOperations::moveOverlapping(spot, end(), spot + 1); + new (NotNull, spot) T(*data); + ++m_size; + } + + template template + inline void Vector::insert(size_t position, const Vector& val) + { + insert(position, val.begin(), val.size()); + } + + template template + void Vector::prepend(const U* data, size_t dataSize) + { + insert(0, data, dataSize); + } + + template template + inline void Vector::prepend(const U& val) + { + insert(0, val); + } + + template template + inline void Vector::prepend(const Vector& val) + { + insert(0, val.begin(), val.size()); + } + + template + inline void Vector::remove(size_t position) + { + ASSERT(position < size()); + T* spot = begin() + position; + spot->~T(); + TypeOperations::moveOverlapping(spot + 1, end(), spot); + --m_size; + } + + template + inline void Vector::remove(size_t position, size_t length) + { + ASSERT(position <= size()); + ASSERT(position + length <= size()); + T* beginSpot = begin() + position; + T* endSpot = beginSpot + length; + TypeOperations::destruct(beginSpot, endSpot); + TypeOperations::moveOverlapping(endSpot, end(), beginSpot); + m_size -= length; + } + + template + inline void Vector::reverse() + { + for (size_t i = 0; i < m_size / 2; ++i) + std::swap(at(i), at(m_size - 1 - i)); + } + + template + inline T* Vector::releaseBuffer() + { + T* buffer = m_buffer.releaseBuffer(); + if (inlineCapacity && !buffer && m_size) { + // If the vector had some data, but no buffer to release, + // that means it was using the inline buffer. In that case, + // we create a brand new buffer so the caller always gets one. + size_t bytes = m_size * sizeof(T); + buffer = static_cast(fastMalloc(bytes)); + memcpy(buffer, data(), bytes); + } + m_size = 0; + return buffer; + } + + template + inline void Vector::checkConsistency() + { +#if !ASSERT_DISABLED + for (size_t i = 0; i < size(); ++i) + ValueCheck::checkConsistency(at(i)); +#endif + } + + template + void deleteAllValues(const Vector& collection) + { + typedef typename Vector::const_iterator iterator; + iterator end = collection.end(); + for (iterator it = collection.begin(); it != end; ++it) + delete *it; + } + + template + inline void swap(Vector& a, Vector& b) + { + a.swap(b); + } + + template + bool operator==(const Vector& a, const Vector& b) + { + if (a.size() != b.size()) + return false; + + return VectorTypeOperations::compare(a.data(), b.data(), a.size()); + } + + template + inline bool operator!=(const Vector& a, const Vector& b) + { + return !(a == b); + } + +#if !ASSERT_DISABLED + template struct ValueCheck > { + typedef Vector TraitType; + static void checkConsistency(const Vector& v) + { + v.checkConsistency(); + } + }; +#endif + +} // namespace WTF + +using WTF::Vector; + +#endif // WTF_Vector_h diff --git a/masm/wtf/VectorTraits.h b/masm/wtf/VectorTraits.h new file mode 100644 index 0000000000..678225034a --- /dev/null +++ b/masm/wtf/VectorTraits.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_VectorTraits_h +#define WTF_VectorTraits_h + +#include +#include +#include +#include +#include + +using std::pair; + +namespace WTF { + + class AtomicString; + + template + struct VectorTraitsBase; + + template + struct VectorTraitsBase + { + static const bool needsDestruction = true; + static const bool needsInitialization = true; + static const bool canInitializeWithMemset = false; + static const bool canMoveWithMemcpy = false; + static const bool canCopyWithMemcpy = false; + static const bool canFillWithMemset = false; + static const bool canCompareWithMemcmp = false; + }; + + template + struct VectorTraitsBase + { + static const bool needsDestruction = false; + static const bool needsInitialization = false; + static const bool canInitializeWithMemset = false; + static const bool canMoveWithMemcpy = true; + static const bool canCopyWithMemcpy = true; + static const bool canFillWithMemset = sizeof(T) == sizeof(char); + static const bool canCompareWithMemcmp = true; + }; + + template + struct VectorTraits : VectorTraitsBase::value, T> { }; + + struct SimpleClassVectorTraits : VectorTraitsBase + { + static const bool canInitializeWithMemset = true; + static const bool canMoveWithMemcpy = true; + static const bool canCompareWithMemcmp = true; + }; + + // we know OwnPtr and RefPtr are simple enough that initializing to 0 and moving with memcpy + // (and then not destructing the original) will totally work + template + struct VectorTraits > : SimpleClassVectorTraits { }; + + template + struct VectorTraits > : SimpleClassVectorTraits { }; + + template<> + struct VectorTraits : SimpleClassVectorTraits { }; + + template + struct VectorTraits > + { + typedef VectorTraits FirstTraits; + typedef VectorTraits SecondTraits; + + static const bool needsDestruction = FirstTraits::needsDestruction || SecondTraits::needsDestruction; + static const bool needsInitialization = FirstTraits::needsInitialization || SecondTraits::needsInitialization; + static const bool canInitializeWithMemset = FirstTraits::canInitializeWithMemset && SecondTraits::canInitializeWithMemset; + static const bool canMoveWithMemcpy = FirstTraits::canMoveWithMemcpy && SecondTraits::canMoveWithMemcpy; + static const bool canCopyWithMemcpy = FirstTraits::canCopyWithMemcpy && SecondTraits::canCopyWithMemcpy; + static const bool canFillWithMemset = false; + static const bool canCompareWithMemcmp = FirstTraits::canCompareWithMemcmp && SecondTraits::canCompareWithMemcmp; + }; + +} // namespace WTF + +using WTF::VectorTraits; +using WTF::SimpleClassVectorTraits; + +#endif // WTF_VectorTraits_h diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp new file mode 100644 index 0000000000..ae78144f63 --- /dev/null +++ b/qv4isel_masm.cpp @@ -0,0 +1,128 @@ + +#include "qv4isel_masm_p.h" +#include "qmljs_runtime.h" +#include "qmljs_objects.h" + +#include + +#include +#include +#include + +#ifndef NO_UDIS86 +# include +#endif + +using namespace QQmlJS; +using namespace QQmlJS::MASM; +using namespace QQmlJS::VM; + +namespace { +QTextStream qout(stdout, QIODevice::WriteOnly); +} + +InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *buffer) + : _engine(engine) + , _module(module) + , _function(0) + , _block(0) + , _buffer(buffer) + , _code(buffer) + , _codePtr(buffer) +{ +} + +InstructionSelection::~InstructionSelection() +{ +} + +void InstructionSelection::operator()(IR::Function *function) +{ + _assembler.ret(); + JSC::JSGlobalData dummy; + JSC::LinkBuffer linkBuffer(dummy, &_assembler, 0); + JSC::MacroAssembler::CodeRef code = linkBuffer.finalizeCodeWithoutDisassembly(); + memcpy(_buffer, code.executableMemory()->start(), code.size()); + function->code = (void (*)(VM::Context *, const uchar *)) _buffer; +} + +String *InstructionSelection::identifier(const QString &s) +{ + return _engine->identifier(s); +} + +void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) +{ +} + +void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) +{ +} + + +void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) +{ +} + +void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) +{ +} + +void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) +{ +} + +void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) +{ +} + +void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) +{ +} + +void InstructionSelection::checkExceptions() +{ +} + +void InstructionSelection::visitExp(IR::Exp *s) +{ + Q_UNIMPLEMENTED(); + assert(!"TODO"); +} + +void InstructionSelection::visitEnter(IR::Enter *) +{ + Q_UNIMPLEMENTED(); + assert(!"TODO"); +} + +void InstructionSelection::visitLeave(IR::Leave *) +{ + Q_UNIMPLEMENTED(); + assert(!"TODO"); +} + +void InstructionSelection::visitMove(IR::Move *s) +{ + Q_UNIMPLEMENTED(); + s->dump(qout, IR::Stmt::MIR); + qout << endl; + assert(!"TODO"); +} + +void InstructionSelection::visitJump(IR::Jump *s) +{ +} + +void InstructionSelection::visitCJump(IR::CJump *s) +{ + Q_UNIMPLEMENTED(); + assert(!"TODO"); +} + +void InstructionSelection::visitRet(IR::Ret *s) +{ + Q_UNIMPLEMENTED(); + Q_UNUSED(s); +} + diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h new file mode 100644 index 0000000000..1289c4b24b --- /dev/null +++ b/qv4isel_masm_p.h @@ -0,0 +1,58 @@ +#ifndef QV4ISEL_MASM_P_H +#define QV4ISEL_MASM_P_H + +#include "qv4ir_p.h" +#include "qmljs_objects.h" + +#include +#include +#include +#include + +namespace QQmlJS { +namespace MASM { + +class InstructionSelection: protected IR::StmtVisitor +{ +public: + InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *code); + ~InstructionSelection(); + + void operator()(IR::Function *function); + +protected: + VM::String *identifier(const QString &s); + void loadTempAddress(int reg, IR::Temp *t); + void callActivationProperty(IR::Call *call, IR::Temp *result); + void callProperty(IR::Call *call, IR::Temp *result); + void constructActivationProperty(IR::New *call, IR::Temp *result); + void constructProperty(IR::New *ctor, IR::Temp *result); + void callValue(IR::Call *call, IR::Temp *result); + void constructValue(IR::New *call, IR::Temp *result); + void checkExceptions(); + + virtual void visitExp(IR::Exp *); + virtual void visitEnter(IR::Enter *); + virtual void visitLeave(IR::Leave *); + virtual void visitMove(IR::Move *); + virtual void visitJump(IR::Jump *); + virtual void visitCJump(IR::CJump *); + virtual void visitRet(IR::Ret *); + +private: + VM::ExecutionEngine *_engine; + IR::Module *_module; + IR::Function *_function; + IR::BasicBlock *_block; + uchar *_buffer; + uchar *_code; + uchar *_codePtr; + QHash > _patches; + QHash _addrs; + JSC::MacroAssembler _assembler; +}; + +} // end of namespace MASM +} // end of namespace QQmlJS + +#endif // QV4ISEL_MASM_P_H diff --git a/v4.pro b/v4.pro index 6cac5950e0..0e0ab82e27 100644 --- a/v4.pro +++ b/v4.pro @@ -18,6 +18,7 @@ SOURCES += main.cpp \ qv4ecmaobjects.cpp \ qv4array.cpp \ qv4isel_x86_64.cpp \ + qv4isel_masm.cpp \ llvm_runtime.cpp HEADERS += \ @@ -28,7 +29,8 @@ HEADERS += \ qv4syntaxchecker_p.h \ qv4ecmaobjects_p.h \ qv4array_p.h \ - qv4isel_x86_64_p.h + qv4isel_x86_64_p.h \ + qv4isel_masm_p.h HEADERS += \ asm/x86-codegen.h \ @@ -75,3 +77,4 @@ DEFINES += QMLJS_NO_LLVM } include(moth/moth.pri) +include(masm/masm.pri) -- cgit v1.2.3 From 757318b77d5ef29c63cdc108962925ad218c90ac Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 23 Sep 2012 10:44:12 +0200 Subject: Add udis86 support --- masm/config.h | 2 ++ masm/disassembler/udis86/ud_opcode.pyc | Bin 0 -> 7170 bytes masm/disassembler/udis86/ud_optable.pyc | Bin 0 -> 2793 bytes masm/masm.pri | 21 ++++++++++++++++++++- masm/stubs/WTFStubs.cpp | 14 +++++++++++--- qv4isel_masm.cpp | 2 +- 6 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 masm/disassembler/udis86/ud_opcode.pyc create mode 100644 masm/disassembler/udis86/ud_optable.pyc diff --git a/masm/config.h b/masm/config.h index 298bcb101d..5c67d30146 100644 --- a/masm/config.h +++ b/masm/config.h @@ -1,4 +1,6 @@ #include +#ifdef __cplusplus #include +#endif #include diff --git a/masm/disassembler/udis86/ud_opcode.pyc b/masm/disassembler/udis86/ud_opcode.pyc new file mode 100644 index 0000000000..0643b37eb8 Binary files /dev/null and b/masm/disassembler/udis86/ud_opcode.pyc differ diff --git a/masm/disassembler/udis86/ud_optable.pyc b/masm/disassembler/udis86/ud_optable.pyc new file mode 100644 index 0000000000..741467979e Binary files /dev/null and b/masm/disassembler/udis86/ud_optable.pyc differ diff --git a/masm/masm.pri b/masm/masm.pri index 1f05e11f20..502df9132c 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -11,7 +11,26 @@ DEFINES += NDEBUG DEFINES += WTF_EXPORT_PRIVATE="" INCLUDEPATH += $$PWD/jit -INCLUDEPATH += $$PWD/disassembler +INCLUDEPATH += $$PWD/assembler INCLUDEPATH += $$PWD/wtf INCLUDEPATH += $$PWD/stubs INCLUDEPATH += $$PWD + +DEFINES += WTF_USE_UDIS86=1 +INCLUDEPATH += $$PWD/disassembler +INCLUDEPATH += $$PWD/disassembler/udis86 +SOURCES += $$PWD/disassembler/UDis86Disassembler.cpp +SOURCES += $$PWD/disassembler/udis86/udis86.c +SOURCES += $$PWD/disassembler/udis86/udis86_decode.c +SOURCES += $$PWD/disassembler/udis86/udis86_input.c +SOURCES += $$PWD/disassembler/udis86/udis86_itab_holder.c +SOURCES += $$PWD/disassembler/udis86/udis86_syn-att.c +SOURCES += $$PWD/disassembler/udis86/udis86_syn.c +SOURCES += $$PWD/disassembler/udis86/udis86_syn-intel.c + + +ITAB = $$PWD/disassembler/udis86/optable.xml +udis86.output = udis86_itab.h +udis86.input = ITAB +udis86.commands = python $$PWD/disassembler/udis86/itab.py ${QMAKE_FILE_IN} +QMAKE_EXTRA_COMPILERS += udis86 diff --git a/masm/stubs/WTFStubs.cpp b/masm/stubs/WTFStubs.cpp index 68424293c4..c649e7f025 100644 --- a/masm/stubs/WTFStubs.cpp +++ b/masm/stubs/WTFStubs.cpp @@ -2,6 +2,8 @@ #include #include +#include + namespace WTF { void* fastMalloc(unsigned int size) @@ -21,19 +23,25 @@ int cryptographicallyRandomNumber() FILE* dataFile() { - return 0; + return stdout; } -void dataLogV(const char* format, va_list) +void dataLogV(const char* format, va_list args) { + qDebug(format, args); } void dataLog(const char* format, ...) { + va_list args; + va_start(args, format); + qDebug(format, args); + va_end(args); } -void dataLogString(const char*) +void dataLogString(const char* str) { + qDebug("%s", str); } } diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index ae78144f63..0007f319e4 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -41,7 +41,7 @@ void InstructionSelection::operator()(IR::Function *function) _assembler.ret(); JSC::JSGlobalData dummy; JSC::LinkBuffer linkBuffer(dummy, &_assembler, 0); - JSC::MacroAssembler::CodeRef code = linkBuffer.finalizeCodeWithoutDisassembly(); + JSC::MacroAssembler::CodeRef code = linkBuffer.finalizeCodeWithDisassembly("operator()(IR::Function*)"); memcpy(_buffer, code.executableMemory()->start(), code.size()); function->code = (void (*)(VM::Context *, const uchar *)) _buffer; } -- cgit v1.2.3 From e0d9d58e5d028b16afcfcb94df86783df1516c64 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 26 Sep 2012 21:46:30 +0200 Subject: Fix code ownership --- masm/config.h | 4 ++++ masm/stubs/ExecutableAllocator.h | 9 ++++++++- qv4ir_p.h | 3 +++ qv4isel_masm.cpp | 5 ++--- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/masm/config.h b/masm/config.h index 5c67d30146..d462525611 100644 --- a/masm/config.h +++ b/masm/config.h @@ -1,6 +1,10 @@ +#ifndef MASM_CONFIG_H +#define MASM_CONFIG_H #include #ifdef __cplusplus #include #endif #include + +#endif // MASM_CONFIG_H diff --git a/masm/stubs/ExecutableAllocator.h b/masm/stubs/ExecutableAllocator.h index 8058a45ba3..72605e0411 100644 --- a/masm/stubs/ExecutableAllocator.h +++ b/masm/stubs/ExecutableAllocator.h @@ -4,6 +4,8 @@ #include #include +#include + namespace JSC { struct JSGlobalData; @@ -36,8 +38,13 @@ struct ExecutableAllocator { { } - static void makeExecutable(void*, int) + static void makeExecutable(void* addr, int size) { + size_t pageSize = sysconf(_SC_PAGESIZE); + size_t iaddr = reinterpret_cast(addr); + size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); + int mode = PROT_READ | PROT_WRITE | PROT_EXEC; + mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode); } }; diff --git a/qv4ir_p.h b/qv4ir_p.h index 15cf7ff17c..53c75f797a 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -59,6 +59,8 @@ #include #include +#include + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -566,6 +568,7 @@ struct Function { void (*code)(VM::Context *, const uchar *); const uchar *codeData; + JSC::MacroAssemblerCodeRef codeRef; bool hasDirectEval: 1; bool hasNestedFunctions: 1; diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 0007f319e4..932c3a60e4 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -41,9 +41,8 @@ void InstructionSelection::operator()(IR::Function *function) _assembler.ret(); JSC::JSGlobalData dummy; JSC::LinkBuffer linkBuffer(dummy, &_assembler, 0); - JSC::MacroAssembler::CodeRef code = linkBuffer.finalizeCodeWithDisassembly("operator()(IR::Function*)"); - memcpy(_buffer, code.executableMemory()->start(), code.size()); - function->code = (void (*)(VM::Context *, const uchar *)) _buffer; + function->codeRef = linkBuffer.finalizeCodeWithDisassembly("operator()(IR::Function*)"); + function->code = (void (*)(VM::Context *, const uchar *)) function->codeRef.code().executableAddress(); } String *InstructionSelection::identifier(const QString &s) -- cgit v1.2.3 From e4865c5068d419f8cb59f28257733ec00ac7b93f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 1 Oct 2012 09:29:20 +0200 Subject: Cleanups, some rudimentary stubs so that "return 42" can be compiled :) --- qv4isel_masm.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- qv4isel_masm_p.h | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 119 insertions(+), 6 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 932c3a60e4..b885ffa1a9 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -38,11 +38,40 @@ InstructionSelection::~InstructionSelection() void InstructionSelection::operator()(IR::Function *function) { - _assembler.ret(); + qSwap(_function, function); + + enterStandardStackFrame(); + + int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) * sizeof(Value); + locals = (locals + 15) & ~15; + sub32(TrustedImm32(locals), StackPointerRegister); + + push(ContextRegister); + loadPtr(addressForArgument(0), ContextRegister); + + foreach (IR::BasicBlock *block, _function->basicBlocks) { + _block = block; + // _addrs[block] = _codePtr; + foreach (IR::Stmt *s, block->statements) { + s->accept(this); + } + } + + pop(ContextRegister); + + add32(TrustedImm32(locals), StackPointerRegister); + + leaveStandardStackFrame(); + ret(); + JSC::JSGlobalData dummy; - JSC::LinkBuffer linkBuffer(dummy, &_assembler, 0); - function->codeRef = linkBuffer.finalizeCodeWithDisassembly("operator()(IR::Function*)"); - function->code = (void (*)(VM::Context *, const uchar *)) function->codeRef.code().executableAddress(); + JSC::LinkBuffer linkBuffer(dummy, this, 0); + foreach (CallToLink ctl, _callsToLink) + linkBuffer.link(ctl.call, ctl.externalFunction); + _function->codeRef = linkBuffer.finalizeCodeWithDisassembly("operator()(IR::Function*)"); + _function->code = (void (*)(VM::Context *, const uchar *)) _function->codeRef.code().executableAddress(); + + qSwap(_function, function); } String *InstructionSelection::identifier(const QString &s) @@ -103,6 +132,20 @@ void InstructionSelection::visitLeave(IR::Leave *) void InstructionSelection::visitMove(IR::Move *s) { + if (s->op == IR::OpInvalid) { + if (IR::Temp *t = s->target->asTemp()) { + if (IR::Const *c = s->source->asConst()) { + Address dest = addressForLocal(t->index); + switch (c->type) { + case IR::NumberType: + // ### Taking address of pointer inside IR. + loadDouble(&c->value, FPGpr0); + storeDouble(FPGpr0, dest); + break; + } + } + } + } Q_UNIMPLEMENTED(); s->dump(qout, IR::Stmt::MIR); qout << endl; @@ -121,6 +164,18 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { + if (IR::Temp *t = s->expr->asTemp()) { + add32(TrustedImm32(stackOffsetForLocal(t->index)), StackFrameRegister, Gpr0); + push(Gpr0); + + add32(TrustedImm32(offsetof(Context, result)), ContextRegister, Gpr0); + push(Gpr0); + + callAbsolute(__qmljs_copy); + + add32(TrustedImm32(2 * sizeof(void*)), StackPointerRegister); + return; + } Q_UNIMPLEMENTED(); Q_UNUSED(s); } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 1289c4b24b..21109f7878 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -12,7 +12,7 @@ namespace QQmlJS { namespace MASM { -class InstructionSelection: protected IR::StmtVisitor +class InstructionSelection: protected IR::StmtVisitor, public JSC::MacroAssembler { public: InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *code); @@ -21,6 +21,50 @@ public: void operator()(IR::Function *function); protected: +#if CPU(X86) + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID ContextRegister = JSC::X86Registers::esi; + static const RegisterID Gpr0 = JSC::X86Registers::eax; + static const RegisterID Gpr1 = JSC::X86Registers::ecx; + static const RegisterID Gpr2 = JSC::X86Registers::edx; + static const RegisterID Gpr3 = JSC::X86Registers::edi; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; +#else +#error Argh. +#endif + +#if CPU(X86) || CPU(X86_64) + void enterStandardStackFrame() + { + push(StackFrameRegister); + move(StackPointerRegister, StackFrameRegister); + } + void leaveStandardStackFrame() + { + pop(StackFrameRegister); + } +#else +#error Argh. +#endif + + Address addressForArgument(int index) const + { + // ### CPU specific: on x86/x86_64 we need +2 to jump over ebp and the return address + // on the stack. Maybe same on arm if we save lr on stack on enter. + return Address(StackFrameRegister, (index + 2) * sizeof(void*)); + } + + int stackOffsetForLocal(int index) const + { + return -(index + 1) * sizeof(void*); + } + + Address addressForLocal(int index) const + { + return Address(StackFrameRegister, stackOffsetForLocal(index)); + } + VM::String *identifier(const QString &s); void loadTempAddress(int reg, IR::Temp *t); void callActivationProperty(IR::Call *call, IR::Temp *result); @@ -40,6 +84,20 @@ protected: virtual void visitRet(IR::Ret *); private: + typedef JSC::FunctionPtr FunctionPtr; + + void callAbsolute(FunctionPtr function) { + CallToLink ctl; + ctl.call = call(); + ctl.externalFunction = function; + _callsToLink.append(ctl); + } + + struct CallToLink { + Call call; + FunctionPtr externalFunction; + }; + VM::ExecutionEngine *_engine; IR::Module *_module; IR::Function *_function; @@ -49,7 +107,7 @@ private: uchar *_codePtr; QHash > _patches; QHash _addrs; - JSC::MacroAssembler _assembler; + QList _callsToLink; }; } // end of namespace MASM -- cgit v1.2.3 From 1bde565ac6a98fb752423d3581ab502db77dde30 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 1 Oct 2012 21:39:25 +0200 Subject: correct temp handling --- qv4isel_masm.cpp | 25 +++++++++++++++++++++---- qv4isel_masm_p.h | 12 +----------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index b885ffa1a9..71e58eb022 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -79,8 +79,24 @@ String *InstructionSelection::identifier(const QString &s) return _engine->identifier(s); } -void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) -{ +JSC::MacroAssembler::Address InstructionSelection::loadTempAddress(RegisterID reg, IR::Temp *t) +{ + fprintf(stderr, "loadtempAddress %d -- locals %d\n", t->index, _function->locals.size()); + int32_t offset = 0; + if (t->index < 0) { + const int arg = -t->index - 1; + loadPtr(Address(ContextRegister, offsetof(Context, arguments)), reg); + offset = arg * sizeof(Value); + } else if (t->index < _function->locals.size()) { + loadPtr(Address(ContextRegister, offsetof(Context, locals)), reg); + offset = t->index * sizeof(Value); + } else { + const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size(); + offset = sizeof(Value) * (-arg) + - sizeof(void*); // size of ebp + reg = StackFrameRegister; + } + return Address(reg, offset); } void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) @@ -135,7 +151,7 @@ void InstructionSelection::visitMove(IR::Move *s) if (s->op == IR::OpInvalid) { if (IR::Temp *t = s->target->asTemp()) { if (IR::Const *c = s->source->asConst()) { - Address dest = addressForLocal(t->index); + Address dest = loadTempAddress(Gpr0, t); switch (c->type) { case IR::NumberType: // ### Taking address of pointer inside IR. @@ -165,7 +181,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { if (IR::Temp *t = s->expr->asTemp()) { - add32(TrustedImm32(stackOffsetForLocal(t->index)), StackFrameRegister, Gpr0); + Address addr = loadTempAddress(Gpr0, t); + add32(TrustedImm32(addr.offset), addr.base, Gpr0); push(Gpr0); add32(TrustedImm32(offsetof(Context, result)), ContextRegister, Gpr0); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 21109f7878..d4f4c440b1 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -55,18 +55,8 @@ protected: return Address(StackFrameRegister, (index + 2) * sizeof(void*)); } - int stackOffsetForLocal(int index) const - { - return -(index + 1) * sizeof(void*); - } - - Address addressForLocal(int index) const - { - return Address(StackFrameRegister, stackOffsetForLocal(index)); - } - VM::String *identifier(const QString &s); - void loadTempAddress(int reg, IR::Temp *t); + Address loadTempAddress(RegisterID reg, IR::Temp *t); void callActivationProperty(IR::Call *call, IR::Temp *result); void callProperty(IR::Call *call, IR::Temp *result); void constructActivationProperty(IR::New *call, IR::Temp *result); -- cgit v1.2.3 From 4f7ed073ac5cafec94d7689e6451173f9ec92259 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 1 Oct 2012 21:59:09 +0200 Subject: Call helper --- qv4isel_masm.cpp | 12 +++--------- qv4isel_masm_p.h | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 71e58eb022..953b8faa34 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -181,16 +181,10 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { if (IR::Temp *t = s->expr->asTemp()) { - Address addr = loadTempAddress(Gpr0, t); - add32(TrustedImm32(addr.offset), addr.base, Gpr0); - push(Gpr0); - + Address addr = loadTempAddress(Gpr1, t); + add32(TrustedImm32(addr.offset), addr.base, Gpr1); add32(TrustedImm32(offsetof(Context, result)), ContextRegister, Gpr0); - push(Gpr0); - - callAbsolute(__qmljs_copy); - - add32(TrustedImm32(2 * sizeof(void*)), StackPointerRegister); + callHelper(__qmljs_copy, Gpr1, Gpr0); return; } Q_UNIMPLEMENTED(); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index d4f4c440b1..34e96ed1de 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -83,6 +83,30 @@ private: _callsToLink.append(ctl); } + template + void callHelper(FunctionPtr function, Arg1 arg1) { + push(arg1); + callAbsolute(function); + add32(TrustedImm32(1 * sizeof(void*)), StackPointerRegister); + } + + template + void callHelper(FunctionPtr function, Arg1 arg1, Arg2 arg2) { + push(arg2); + push(arg1); + callAbsolute(function); + add32(TrustedImm32(2 * sizeof(void*)), StackPointerRegister); + } + + template + void callHelper(FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) { + push(arg3); + push(arg2); + push(arg1); + callAbsolute(function); + add32(TrustedImm32(3 * sizeof(void*)), StackPointerRegister); + } + struct CallToLink { Call call; FunctionPtr externalFunction; -- cgit v1.2.3 From 4f81e6f854ac23a36bf509fc56285dd88118ee8a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 1 Oct 2012 22:02:20 +0200 Subject: cleanup, squash --- qv4isel_masm.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 953b8faa34..80cd083422 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -81,7 +81,6 @@ String *InstructionSelection::identifier(const QString &s) JSC::MacroAssembler::Address InstructionSelection::loadTempAddress(RegisterID reg, IR::Temp *t) { - fprintf(stderr, "loadtempAddress %d -- locals %d\n", t->index, _function->locals.size()); int32_t offset = 0; if (t->index < 0) { const int arg = -t->index - 1; -- cgit v1.2.3 From 0c0e5481710f221127940e283557a99d75011614 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 1 Oct 2012 22:03:59 +0200 Subject: Fix ret argumet passing --- qv4isel_masm.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 80cd083422..bce509ab73 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -180,9 +180,9 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { if (IR::Temp *t = s->expr->asTemp()) { - Address addr = loadTempAddress(Gpr1, t); - add32(TrustedImm32(addr.offset), addr.base, Gpr1); - add32(TrustedImm32(offsetof(Context, result)), ContextRegister, Gpr0); + Address addr = loadTempAddress(Gpr0, t); + add32(TrustedImm32(addr.offset), addr.base, Gpr0); + add32(TrustedImm32(offsetof(Context, result)), ContextRegister, Gpr1); callHelper(__qmljs_copy, Gpr1, Gpr0); return; } -- cgit v1.2.3 From aabf1f637063407b664960a03f7b3452abfbc8b6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 1 Oct 2012 22:35:53 +0200 Subject: Implement simple value storage --- qmljs_runtime.h | 15 +++++++++++++++ qv4isel_masm.cpp | 12 ++++++++++++ qv4isel_masm_p.h | 11 +++++++++++ 3 files changed, 38 insertions(+) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 638399f8c9..657602bcfe 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -537,6 +537,21 @@ inline Value ValueBase<8>::fromObject(Object *o) return v; } +template struct ValueOffsetHelper; +template <> struct ValueOffsetHelper +{ + enum { Offset = offsetof(ValueData, b) }; +}; + +template <> struct ValueOffsetHelper +{ + enum { Offset = offsetof(ValueData, uint_32) }; +}; + +template <> struct ValueOffsetHelper +{ + enum { Offset = offsetof(ValueData, uint_32) }; +}; struct Context { ExecutionEngine *engine; diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index bce509ab73..5407f5717d 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -152,11 +152,23 @@ void InstructionSelection::visitMove(IR::Move *s) if (IR::Const *c = s->source->asConst()) { Address dest = loadTempAddress(Gpr0, t); switch (c->type) { + case IR::NullType: + storeValue(TrustedImm32(0), dest); + break; + case IR::UndefinedType: + storeValue(TrustedImm32(0), dest); + break; + case IR::BoolType: + storeValue(TrustedImm32(c->value != 0), dest); + break; case IR::NumberType: // ### Taking address of pointer inside IR. loadDouble(&c->value, FPGpr0); storeDouble(FPGpr0, dest); break; + default: + Q_UNIMPLEMENTED(); + assert(!"TODO"); } } } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 34e96ed1de..88a2c7a88b 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -3,6 +3,7 @@ #include "qv4ir_p.h" #include "qmljs_objects.h" +#include "qmljs_runtime.h" #include #include @@ -112,6 +113,16 @@ private: FunctionPtr externalFunction; }; + template + void storeValue(TrustedImm32 value, Address destination) + { + destination.offset += offsetof(VM::ValueData, tag); + store32(TrustedImm32(type), destination); + destination.offset -= offsetof(VM::ValueData, tag); + destination.offset += VM::ValueOffsetHelper::Offset; + store32(value, destination); + } + VM::ExecutionEngine *_engine; IR::Module *_module; IR::Function *_function; -- cgit v1.2.3 From 1c80510fc5171ff0135a90bf40ae16381fd6fbed Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 1 Oct 2012 22:52:05 +0200 Subject: Implement basic name lookup --- qv4isel_masm.cpp | 16 +++++++++++++++- qv4isel_masm_p.h | 13 +++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 5407f5717d..3157a210f2 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -149,7 +149,20 @@ void InstructionSelection::visitMove(IR::Move *s) { if (s->op == IR::OpInvalid) { if (IR::Temp *t = s->target->asTemp()) { - if (IR::Const *c = s->source->asConst()) { + if (IR::Name *n = s->source->asName()) { + Address temp = loadTempAddress(Gpr0, t); + add32(TrustedImm32(temp.offset), temp.base, Gpr0); + + if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. + callRuntimeMethod(__qmljs_get_thisObject, Gpr0); + } else { + String *propertyName = identifier(*n->id); + move(TrustedImmPtr(propertyName), Gpr1); + callRuntimeMethod(__qmljs_get_activation_property, Gpr0, Gpr1); + checkExceptions(); + } + return; + } else if (IR::Const *c = s->source->asConst()) { Address dest = loadTempAddress(Gpr0, t); switch (c->type) { case IR::NullType: @@ -170,6 +183,7 @@ void InstructionSelection::visitMove(IR::Move *s) Q_UNIMPLEMENTED(); assert(!"TODO"); } + return; } } } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 88a2c7a88b..21138c11a5 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -108,6 +108,19 @@ private: add32(TrustedImm32(3 * sizeof(void*)), StackPointerRegister); } + template + void callRuntimeMethod(FunctionPtr function, Arg1 arg1) + { + callHelper(function, ContextRegister, arg1); + } + + template + void callRuntimeMethod(FunctionPtr function, Arg1 arg1, Arg2 arg2) + { + callHelper(function, ContextRegister, arg1, arg2); + } + + struct CallToLink { Call call; FunctionPtr externalFunction; -- cgit v1.2.3 From b21875c0d59ba727420035784a5725d840f702b3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 2 Oct 2012 06:49:49 +0200 Subject: Prospective jump support --- qv4isel_masm.cpp | 14 +++++++++++++- qv4isel_masm_p.h | 4 ++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 3157a210f2..12a2306bd8 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -51,7 +51,7 @@ void InstructionSelection::operator()(IR::Function *function) foreach (IR::BasicBlock *block, _function->basicBlocks) { _block = block; - // _addrs[block] = _codePtr; + _addrs[block] = label(); foreach (IR::Stmt *s, block->statements) { s->accept(this); } @@ -64,6 +64,16 @@ void InstructionSelection::operator()(IR::Function *function) leaveStandardStackFrame(); ret(); + QHashIterator > it(_patches); + while (it.hasNext()) { + it.next(); + IR::BasicBlock *block = it.key(); + Label target = _addrs.value(block); + assert(target.isSet()); + foreach (Jump jump, it.value()) + jump.linkTo(target, this); + } + JSC::JSGlobalData dummy; JSC::LinkBuffer linkBuffer(dummy, this, 0); foreach (CallToLink ctl, _callsToLink) @@ -195,6 +205,8 @@ void InstructionSelection::visitMove(IR::Move *s) void InstructionSelection::visitJump(IR::Jump *s) { + if (_block->index + 1 != s->target->index) + _patches[s->target].append(jump()); } void InstructionSelection::visitCJump(IR::CJump *s) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 21138c11a5..4b16581525 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -143,8 +143,8 @@ private: uchar *_buffer; uchar *_code; uchar *_codePtr; - QHash > _patches; - QHash _addrs; + QHash > _patches; + QHash _addrs; QList _callsToLink; }; -- cgit v1.2.3 From 59f6f4d1be3b78858ac0da6752c6f4110c7ad42c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 2 Oct 2012 07:21:38 +0200 Subject: Simplified function call helpers --- qv4isel_masm.cpp | 21 ++++++---- qv4isel_masm_p.h | 117 ++++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 96 insertions(+), 42 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 12a2306bd8..191cd1e5e8 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -161,14 +161,18 @@ void InstructionSelection::visitMove(IR::Move *s) if (IR::Temp *t = s->target->asTemp()) { if (IR::Name *n = s->source->asName()) { Address temp = loadTempAddress(Gpr0, t); - add32(TrustedImm32(temp.offset), temp.base, Gpr0); + + FunctionCall fc(this); + fc.addArgumentFromRegister(ContextRegister); + fc.addArgumentAsAddress(temp); if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. - callRuntimeMethod(__qmljs_get_thisObject, Gpr0); + fc.call(__qmljs_get_thisObject); } else { String *propertyName = identifier(*n->id); move(TrustedImmPtr(propertyName), Gpr1); - callRuntimeMethod(__qmljs_get_activation_property, Gpr0, Gpr1); + fc.addArgumentFromRegister(Gpr1); + fc.call(__qmljs_get_activation_property); checkExceptions(); } return; @@ -218,10 +222,13 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { if (IR::Temp *t = s->expr->asTemp()) { - Address addr = loadTempAddress(Gpr0, t); - add32(TrustedImm32(addr.offset), addr.base, Gpr0); - add32(TrustedImm32(offsetof(Context, result)), ContextRegister, Gpr1); - callHelper(__qmljs_copy, Gpr1, Gpr0); + Address source = loadTempAddress(Gpr0, t); + Address result = Address(ContextRegister, offsetof(Context, result)); + + FunctionCall fc(this); + fc.addArgumentAsAddress(result); + fc.addArgumentAsAddress(source); + fc.call(__qmljs_copy); return; } Q_UNIMPLEMENTED(); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 4b16581525..a15fcd9477 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -84,42 +84,89 @@ private: _callsToLink.append(ctl); } - template - void callHelper(FunctionPtr function, Arg1 arg1) { - push(arg1); - callAbsolute(function); - add32(TrustedImm32(1 * sizeof(void*)), StackPointerRegister); - } - - template - void callHelper(FunctionPtr function, Arg1 arg1, Arg2 arg2) { - push(arg2); - push(arg1); - callAbsolute(function); - add32(TrustedImm32(2 * sizeof(void*)), StackPointerRegister); - } - - template - void callHelper(FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) { - push(arg3); - push(arg2); - push(arg1); - callAbsolute(function); - add32(TrustedImm32(3 * sizeof(void*)), StackPointerRegister); - } - - template - void callRuntimeMethod(FunctionPtr function, Arg1 arg1) + class FunctionCall { - callHelper(function, ContextRegister, arg1); - } - - template - void callRuntimeMethod(FunctionPtr function, Arg1 arg1, Arg2 arg2) - { - callHelper(function, ContextRegister, arg1, arg2); - } - + public: + FunctionCall(InstructionSelection* selection) + : isel(selection) + {} + + void addArgumentFromRegister(RegisterID reg) + { + Argument arg; + arg.setRegister(reg); + arguments << arg; + } + void addArgumentFromMemory(const Address& address) + { + Argument arg; + arg.setMemoryValue(address); + arguments << arg; + } + void addArgumentAsAddress(const Address& address) + { + Argument arg; + arg.setAddress(address); + arguments << arg; + } + + void call(FunctionPtr function) + { + for (int i = arguments.count() - 1; i >= 0; --i) + arguments.at(i).push(isel, /*scratch register*/ Gpr0); + isel->callAbsolute(function); + isel->add32(TrustedImm32(arguments.count() * sizeof(void*)), StackPointerRegister); + } + + private: + class Argument { + public: + Argument() + : type(None) + , address(InstructionSelection::Gpr0, 0) + {} + + void setRegister(RegisterID regi) + { + reg = regi; + type = Register; + } + void setMemoryValue(const Address& address) + { + this->address = address; + type = MemoryValue; + } + void setAddress(const Address& address) + { + this->address = address; + type = Address; + } + + void push(JSC::MacroAssembler* assembler, RegisterID scratchRegister) const + { + switch (type) { + case None: break; + case Register: assembler->push(reg); break; + case MemoryValue: assembler->push(address); break; + case Address: + assembler->add32(TrustedImm32(address.offset), address.base, scratchRegister); + assembler->push(scratchRegister); + break; + } + } + private: + enum Type { + None, + Register, + MemoryValue, + Address + } type; + RegisterID reg; + JSC::MacroAssembler::Address address; + }; + QList arguments; + InstructionSelection* isel; + }; struct CallToLink { Call call; -- cgit v1.2.3 From 011f4878eabffad2430284c6d18ba66bcba6f1b7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 2 Oct 2012 08:52:38 +0200 Subject: Fix stupid off-by-one in address calculation of temporaries on the stack --- qv4isel_masm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 191cd1e5e8..22ce885432 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -101,7 +101,7 @@ JSC::MacroAssembler::Address InstructionSelection::loadTempAddress(RegisterID re offset = t->index * sizeof(Value); } else { const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size(); - offset = sizeof(Value) * (-arg) + offset = sizeof(Value) * (-arg - 1) - sizeof(void*); // size of ebp reg = StackFrameRegister; } -- cgit v1.2.3 From 062c43beedcae4beb1c1e3667178c3dfa56f38de Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 2 Oct 2012 08:53:47 +0200 Subject: Fix generation of multiple functions Sine we can't reset the MacroAssembler properly right now, reconstruct the isel for every function. --- main.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index 460663a059..fa03e08179 100644 --- a/main.cpp +++ b/main.cpp @@ -227,9 +227,10 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS foreach (IR::Function *function, module.functions) isel(function); } else { - MASM::InstructionSelection isel(vm, &module, code); - foreach (IR::Function *function, module.functions) + foreach (IR::Function *function, module.functions) { + MASM::InstructionSelection isel(vm, &module, code); isel(function); + } if (! protect(code, codeSize)) Q_UNREACHABLE(); -- cgit v1.2.3 From f0167b4c17ed69d2a6f409b4cef7c84fdce134e0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 2 Oct 2012 08:55:18 +0200 Subject: Implement callActivationProperty, mov with target as name and closure init --- qv4isel_masm.cpp | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- qv4isel_masm_p.h | 29 +++++++++++++++- 2 files changed, 130 insertions(+), 2 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 22ce885432..4b81df9859 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -110,6 +110,70 @@ JSC::MacroAssembler::Address InstructionSelection::loadTempAddress(RegisterID re void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) { + IR::Name *baseName = call->base->asName(); + assert(baseName != 0); + + int argc = 0; + for (IR::ExprList *it = call->args; it; it = it->next) { + ++argc; + } + + int i = 0; + for (IR::ExprList *it = call->args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + + Address tempAddress = loadTempAddress(Gpr0, arg); + FunctionCall fc(this); + fc.addArgumentAsAddress(argumentAddressForCall(i)); + fc.addArgumentAsAddress(tempAddress); + fc.call(__qmljs_copy); + } + + FunctionCall activationCall(this); + + activationCall.addArgumentFromRegister(ContextRegister); + + if (result) { + activationCall.addArgumentAsAddress(loadTempAddress(Gpr0, result)); + } else { + xor32(Gpr0, Gpr0); + activationCall.addArgumentFromRegister(Gpr0); + } + + if (baseName->id) { + move(TrustedImmPtr(identifier(*baseName->id)), Gpr1); + activationCall.addArgumentFromRegister(Gpr1); + activationCall.addArgumentAsAddress(baseAddressForCallArguments()); + activationCall.addArgumentByValue(TrustedImm32(argc)); + activationCall.call(__qmljs_call_activation_property); + } else { + switch (baseName->builtin) { + case IR::Name::builtin_invalid: + Q_UNREACHABLE(); + break; + case IR::Name::builtin_typeof: + activationCall.addArgumentAsAddress(baseAddressForCallArguments()); + activationCall.addArgumentByValue(TrustedImm32(argc)); + activationCall.call(__qmljs_builtin_typeof); + break; + case IR::Name::builtin_delete: + Q_UNREACHABLE(); + break; + case IR::Name::builtin_throw: + activationCall.addArgumentAsAddress(baseAddressForCallArguments()); + activationCall.addArgumentByValue(TrustedImm32(argc)); + activationCall.call(__qmljs_builtin_throw); + break; + case IR::Name::builtin_rethrow: + activationCall.addArgumentAsAddress(baseAddressForCallArguments()); + activationCall.addArgumentByValue(TrustedImm32(argc)); + activationCall.call(__qmljs_builtin_rethrow); + return; // we need to return to avoid checking the exceptions + } + } + + checkExceptions(); } @@ -135,6 +199,9 @@ void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) void InstructionSelection::checkExceptions() { + Address addr(ContextRegister, offsetof(Context, hasUncaughtException)); + Jump jmp = branch8(Equal, addr, TrustedImm32(1)); + _patches[_function->handlersBlock].append(jmp); } void InstructionSelection::visitExp(IR::Exp *s) @@ -158,7 +225,22 @@ void InstructionSelection::visitLeave(IR::Leave *) void InstructionSelection::visitMove(IR::Move *s) { if (s->op == IR::OpInvalid) { - if (IR::Temp *t = s->target->asTemp()) { + if (IR::Name *n = s->target->asName()) { + String *propertyName = identifier(*n->id); + + if (IR::Temp *t = s->source->asTemp()) { + FunctionCall fct(this); + fct.addArgumentFromRegister(ContextRegister); + move(TrustedImmPtr(propertyName), Gpr1); + fct.addArgumentFromRegister(Gpr1); + fct.addArgumentAsAddress(loadTempAddress(Gpr2, t)); + fct.call(__qmljs_set_activation_property); + checkExceptions(); + return; + } else { + Q_UNREACHABLE(); + } + } else if (IR::Temp *t = s->target->asTemp()) { if (IR::Name *n = s->source->asName()) { Address temp = loadTempAddress(Gpr0, t); @@ -198,6 +280,25 @@ void InstructionSelection::visitMove(IR::Move *s) assert(!"TODO"); } return; + } else if (IR::Closure *clos = s->source->asClosure()) { + FunctionCall fct(this); + fct.addArgumentFromRegister(ContextRegister); + fct.addArgumentAsAddress(loadTempAddress(Gpr0, t)); + move(TrustedImmPtr(clos->value), Gpr1); + fct.addArgumentFromRegister(Gpr1); + fct.call(__qmljs_init_closure); + return; + } else if (IR::Call *c = s->source->asCall()) { + if (c->base->asName()) { + callActivationProperty(c, t); + return; + } else if (c->base->asMember()) { + callProperty(c, t); + return; + } else if (c->base->asTemp()) { + callValue(c, t); + return; + } } } } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index a15fcd9477..a1f29c759e 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -29,7 +29,6 @@ protected: static const RegisterID Gpr0 = JSC::X86Registers::eax; static const RegisterID Gpr1 = JSC::X86Registers::ecx; static const RegisterID Gpr2 = JSC::X86Registers::edx; - static const RegisterID Gpr3 = JSC::X86Registers::edi; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; #else #error Argh. @@ -56,6 +55,19 @@ protected: return Address(StackFrameRegister, (index + 2) * sizeof(void*)); } + // Some run-time functions take (Value* args, int argc). This function is for populating + // the args. + static Address argumentAddressForCall(int argument) + { + return Address(StackFrameRegister, sizeof(VM::Value) * (-argument) + - sizeof(void*) // size + ); + } + static Address baseAddressForCallArguments() + { + return argumentAddressForCall(0); + } + VM::String *identifier(const QString &s); Address loadTempAddress(RegisterID reg, IR::Temp *t); void callActivationProperty(IR::Call *call, IR::Temp *result); @@ -109,6 +121,12 @@ private: arg.setAddress(address); arguments << arg; } + void addArgumentByValue(TrustedImm32 value) + { + Argument arg; + arg.setValue(value); + arguments << arg; + } void call(FunctionPtr function) { @@ -141,12 +159,18 @@ private: this->address = address; type = Address; } + void setValue(TrustedImm32 value) + { + this->value = value; + type = Value; + } void push(JSC::MacroAssembler* assembler, RegisterID scratchRegister) const { switch (type) { case None: break; case Register: assembler->push(reg); break; + case Value: assembler->push(value); break; case MemoryValue: assembler->push(address); break; case Address: assembler->add32(TrustedImm32(address.offset), address.base, scratchRegister); @@ -158,10 +182,12 @@ private: enum Type { None, Register, + Value, MemoryValue, Address } type; RegisterID reg; + TrustedImm32 value; JSC::MacroAssembler::Address address; }; QList arguments; @@ -179,6 +205,7 @@ private: destination.offset += offsetof(VM::ValueData, tag); store32(TrustedImm32(type), destination); destination.offset -= offsetof(VM::ValueData, tag); + // ### Rename ::Ofset to ::DataOffset destination.offset += VM::ValueOffsetHelper::Offset; store32(value, destination); } -- cgit v1.2.3 From 595714e965cfc2867faf8050ffc8b78f4ea9d56f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 2 Oct 2012 08:56:49 +0200 Subject: Cleanup --- qmljs_runtime.h | 6 +++--- qv4isel_masm_p.h | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 657602bcfe..38e7fcb3f2 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -540,17 +540,17 @@ inline Value ValueBase<8>::fromObject(Object *o) template struct ValueOffsetHelper; template <> struct ValueOffsetHelper { - enum { Offset = offsetof(ValueData, b) }; + enum { DataOffset = offsetof(ValueData, b) }; }; template <> struct ValueOffsetHelper { - enum { Offset = offsetof(ValueData, uint_32) }; + enum { DataOffset = offsetof(ValueData, uint_32) }; }; template <> struct ValueOffsetHelper { - enum { Offset = offsetof(ValueData, uint_32) }; + enum { DataOffset = offsetof(ValueData, uint_32) }; }; struct Context { diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index a1f29c759e..9d5aadc167 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -205,8 +205,7 @@ private: destination.offset += offsetof(VM::ValueData, tag); store32(TrustedImm32(type), destination); destination.offset -= offsetof(VM::ValueData, tag); - // ### Rename ::Ofset to ::DataOffset - destination.offset += VM::ValueOffsetHelper::Offset; + destination.offset += VM::ValueOffsetHelper::DataOffset; store32(value, destination); } -- cgit v1.2.3 From 8b64f6e68d646cdf7ff5bedbf715aeaf51e9b2f1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 4 Oct 2012 19:24:22 +0200 Subject: Implemented part of visitCJump --- qv4isel_masm.cpp | 37 +++++++++++++++++++++++++++++++++++-- qv4isel_masm_p.h | 3 +++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 4b81df9859..1e2a29c7dd 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -310,12 +310,45 @@ void InstructionSelection::visitMove(IR::Move *s) void InstructionSelection::visitJump(IR::Jump *s) { - if (_block->index + 1 != s->target->index) - _patches[s->target].append(jump()); + jumpToBlock(s->target); +} + +void InstructionSelection::jumpToBlock(IR::BasicBlock *target) +{ + if (_block->index + 1 != target->index) + _patches[target].append(jump()); } void InstructionSelection::visitCJump(IR::CJump *s) { + if (IR::Temp *t = s->cond->asTemp()) { + Address temp = loadTempAddress(Gpr1, t); + Address tag = temp; + tag.offset += offsetof(VM::ValueData, tag); + Jump booleanConversion = branch32(NotEqual, tag, TrustedImm32(VM::Value::Boolean_Type)); + + Address data = temp; + data.offset += offsetof(VM::ValueData, b); + load32(data, Gpr1); + Jump testBoolean = jump(); + + booleanConversion.link(this); + { + FunctionCall fct(this); + fct.addArgumentFromRegister(ContextRegister); + fct.addArgumentAsAddress(temp); + fct.call(__qmljs_to_boolean); + move(ReturnValueRegister, Gpr1); + } + + testBoolean.link(this); + move(TrustedImm32(1), Gpr0); + Jump target = branch32(Equal, Gpr1, Gpr0); + _patches[s->iftrue].append(target); + + jumpToBlock(s->iffalse); + return; + } Q_UNIMPLEMENTED(); assert(!"TODO"); } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 9d5aadc167..cccf82a291 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -26,6 +26,7 @@ protected: static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; static const RegisterID StackPointerRegister = JSC::X86Registers::esp; static const RegisterID ContextRegister = JSC::X86Registers::esi; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; static const RegisterID Gpr0 = JSC::X86Registers::eax; static const RegisterID Gpr1 = JSC::X86Registers::ecx; static const RegisterID Gpr2 = JSC::X86Registers::edx; @@ -87,6 +88,8 @@ protected: virtual void visitRet(IR::Ret *); private: + void jumpToBlock(IR::BasicBlock *target); + typedef JSC::FunctionPtr FunctionPtr; void callAbsolute(FunctionPtr function) { -- cgit v1.2.3 From 2c62348148fe69edc957af98f092bcfd9433a9a3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 4 Oct 2012 21:40:17 +0200 Subject: Fix move temp -> temp --- qv4isel_masm.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 1e2a29c7dd..2d4352b2ca 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -280,6 +280,13 @@ void InstructionSelection::visitMove(IR::Move *s) assert(!"TODO"); } return; + } else if (IR::Temp *t2 = s->source->asTemp()) { + Address dest = loadTempAddress(Gpr1, t); + Address source = loadTempAddress(Gpr2, t2); + FunctionCall fc(this); + fc.addArgumentAsAddress(dest); + fc.addArgumentAsAddress(source); + fc.call(__qmljs_copy); } else if (IR::Closure *clos = s->source->asClosure()) { FunctionCall fct(this); fct.addArgumentFromRegister(ContextRegister); -- cgit v1.2.3 From fa690200df747cabc82a6145508d70fb8e8c5a05 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 4 Oct 2012 21:40:33 +0200 Subject: Fix argument addressing to be in the right order --- qv4isel_masm.cpp | 2 +- qv4isel_masm_p.h | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 2d4352b2ca..f215f686b2 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -123,7 +123,7 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu IR::Temp *arg = it->expr->asTemp(); assert(arg != 0); - Address tempAddress = loadTempAddress(Gpr0, arg); + Address tempAddress = loadTempAddress(Gpr1, arg); FunctionCall fc(this); fc.addArgumentAsAddress(argumentAddressForCall(i)); fc.addArgumentAsAddress(tempAddress); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index cccf82a291..f30254afb4 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -58,13 +58,14 @@ protected: // Some run-time functions take (Value* args, int argc). This function is for populating // the args. - static Address argumentAddressForCall(int argument) + Address argumentAddressForCall(int argument) { - return Address(StackFrameRegister, sizeof(VM::Value) * (-argument) - - sizeof(void*) // size + const int index = _function->maxNumberOfArguments - argument; + return Address(StackFrameRegister, sizeof(VM::Value) * (-index) + - sizeof(void*) // size of ebp ); } - static Address baseAddressForCallArguments() + Address baseAddressForCallArguments() { return argumentAddressForCall(0); } -- cgit v1.2.3 From 50bd54fd53dde5d055a07be2216de1c3dfac6b1f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 4 Oct 2012 21:48:38 +0200 Subject: Add missing returns --- qv4isel_masm.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index f215f686b2..c957f4e821 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -287,6 +287,7 @@ void InstructionSelection::visitMove(IR::Move *s) fc.addArgumentAsAddress(dest); fc.addArgumentAsAddress(source); fc.call(__qmljs_copy); + return; } else if (IR::Closure *clos = s->source->asClosure()) { FunctionCall fct(this); fct.addArgumentFromRegister(ContextRegister); @@ -307,7 +308,13 @@ void InstructionSelection::visitMove(IR::Move *s) return; } } + } else if (IR::Member *m = s->target->asMember()) { + Q_UNIMPLEMENTED(); + } else if (IR::Subscript *ss = s->target->asSubscript()) { + Q_UNIMPLEMENTED(); } + } else { + Q_UNIMPLEMENTED(); } Q_UNIMPLEMENTED(); s->dump(qout, IR::Stmt::MIR); -- cgit v1.2.3 From cddf3a4ab139add15211ee4bccafda3eaa16154a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 4 Oct 2012 22:02:43 +0200 Subject: Implement more of cjump --- qv4isel_masm.cpp | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index c957f4e821..ca2c7179e9 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -362,6 +362,45 @@ void InstructionSelection::visitCJump(IR::CJump *s) jumpToBlock(s->iffalse); return; + } else if (IR::Binop *b = s->cond->asBinop()) { + IR::Temp *l = b->left->asTemp(); + IR::Temp *r = b->right->asTemp(); + if (l && r) { + Address lhs = loadTempAddress(Gpr1, l); + Address rhs = loadTempAddress(Gpr2, r); + + bool (*op)(Context *, const Value *, const Value *); + switch (b->op) { + default: Q_UNREACHABLE(); assert(!"todo"); break; + case IR::OpGt: op = __qmljs_cmp_gt; break; + case IR::OpLt: op = __qmljs_cmp_lt; break; + case IR::OpGe: op = __qmljs_cmp_ge; break; + case IR::OpLe: op = __qmljs_cmp_le; break; + case IR::OpEqual: op = __qmljs_cmp_eq; break; + case IR::OpNotEqual: op = __qmljs_cmp_ne; break; + case IR::OpStrictEqual: op = __qmljs_cmp_se; break; + case IR::OpStrictNotEqual: op = __qmljs_cmp_sne; break; + case IR::OpInstanceof: op = __qmljs_cmp_instanceof; break; + case IR::OpIn: op = __qmljs_cmp_in; break; + } // switch + + FunctionCall fct(this); + fct.addArgumentFromRegister(ContextRegister); + fct.addArgumentAsAddress(lhs); + fct.addArgumentAsAddress(rhs); + fct.call(op); + move(ReturnValueRegister, Gpr0); + + move(TrustedImm32(1), Gpr1); + Jump target = branch32(Equal, Gpr0, Gpr1); + _patches[s->iftrue].append(target); + + jumpToBlock(s->iffalse); + return; + } else { + assert(!"wip"); + } + Q_UNIMPLEMENTED(); } Q_UNIMPLEMENTED(); assert(!"TODO"); -- cgit v1.2.3 From fa0f7c85b33a09ac98478baa2ed83d72288b1422 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 4 Oct 2012 22:08:22 +0200 Subject: Sprinkle a few more notImplemented() --- qv4isel_masm.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index ca2c7179e9..64aee9b193 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -288,6 +288,8 @@ void InstructionSelection::visitMove(IR::Move *s) fc.addArgumentAsAddress(source); fc.call(__qmljs_copy); return; + } else if (IR::String *str = s->source->asString()) { + Q_UNIMPLEMENTED(); } else if (IR::Closure *clos = s->source->asClosure()) { FunctionCall fct(this); fct.addArgumentFromRegister(ContextRegister); @@ -296,6 +298,16 @@ void InstructionSelection::visitMove(IR::Move *s) fct.addArgumentFromRegister(Gpr1); fct.call(__qmljs_init_closure); return; + } else if (IR::New *ctor = s->source->asNew()) { + Q_UNIMPLEMENTED(); + } else if (IR::Member *m = s->source->asMember()) { + Q_UNIMPLEMENTED(); + } else if (IR::Subscript *ss = s->source->asSubscript()) { + Q_UNIMPLEMENTED(); + } else if (IR::Unop *u = s->source->asUnop()) { + Q_UNIMPLEMENTED(); + } else if (IR::Binop *b = s->source->asBinop()) { + Q_UNIMPLEMENTED(); } else if (IR::Call *c = s->source->asCall()) { if (c->base->asName()) { callActivationProperty(c, t); -- cgit v1.2.3 From fb654f9eeabd61e39d65b4822e205184e5bc7cae Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 4 Oct 2012 22:13:34 +0200 Subject: Implement mov as string --- qv4isel_masm.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 64aee9b193..abd63f710e 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -289,7 +289,12 @@ void InstructionSelection::visitMove(IR::Move *s) fc.call(__qmljs_copy); return; } else if (IR::String *str = s->source->asString()) { - Q_UNIMPLEMENTED(); + FunctionCall fct(this); + Address temp = loadTempAddress(Gpr0, t); + fct.addArgumentAsAddress(temp); + move(TrustedImmPtr(_engine->newString(*str->value)), Gpr1); + fct.addArgumentFromRegister(Gpr1); + fct.call(__qmljs_init_string); } else if (IR::Closure *clos = s->source->asClosure()) { FunctionCall fct(this); fct.addArgumentFromRegister(ContextRegister); -- cgit v1.2.3 From d29a5f9f9fc8a0e7707a5736992f125f723cd5ef Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 4 Oct 2012 22:46:42 +0200 Subject: Implement binop mov --- qv4isel_masm.cpp | 64 +++++++++++++++++++++++++++++++++++++++++++++++++------- qv4isel_masm_p.h | 10 +++++++-- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index abd63f710e..d3ac19b73e 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -40,11 +40,9 @@ void InstructionSelection::operator()(IR::Function *function) { qSwap(_function, function); - enterStandardStackFrame(); - int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) * sizeof(Value); locals = (locals + 15) & ~15; - sub32(TrustedImm32(locals), StackPointerRegister); + enterStandardStackFrame(locals); push(ContextRegister); loadPtr(addressForArgument(0), ContextRegister); @@ -59,9 +57,7 @@ void InstructionSelection::operator()(IR::Function *function) pop(ContextRegister); - add32(TrustedImm32(locals), StackPointerRegister); - - leaveStandardStackFrame(); + leaveStandardStackFrame(locals); ret(); QHashIterator > it(_patches); @@ -312,7 +308,61 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Unop *u = s->source->asUnop()) { Q_UNIMPLEMENTED(); } else if (IR::Binop *b = s->source->asBinop()) { - Q_UNIMPLEMENTED(); + IR::Temp *l = b->left->asTemp(); + IR::Temp *r = b->right->asTemp(); + if (l && r) { + FunctionCall fct(this); + fct.addArgumentFromRegister(ContextRegister); + + Address result = loadTempAddress(Gpr1, t); + Address lhs = loadTempAddress(Gpr2, l); + Address rhs = loadTempAddress(Gpr3, r); + fct.addArgumentAsAddress(result); + fct.addArgumentAsAddress(lhs); + fct.addArgumentAsAddress(rhs); + + void (*op)(Context *, Value *, const Value *, const Value *) = 0; + + switch ((IR::AluOp) b->op) { + case IR::OpInvalid: + case IR::OpIfTrue: + case IR::OpNot: + case IR::OpUMinus: + case IR::OpUPlus: + case IR::OpCompl: + assert(!"unreachable"); + break; + + case IR::OpBitAnd: op = __qmljs_bit_and; break; + case IR::OpBitOr: op = __qmljs_bit_or; break; + case IR::OpBitXor: op = __qmljs_bit_xor; break; + case IR::OpAdd: op = __qmljs_add; break; + case IR::OpSub: op = __qmljs_sub; break; + case IR::OpMul: op = __qmljs_mul; break; + case IR::OpDiv: op = __qmljs_div; break; + case IR::OpMod: op = __qmljs_mod; break; + case IR::OpLShift: op = __qmljs_shl; break; + case IR::OpRShift: op = __qmljs_shr; break; + case IR::OpURShift: op = __qmljs_ushr; break; + case IR::OpGt: op = __qmljs_gt; break; + case IR::OpLt: op = __qmljs_lt; break; + case IR::OpGe: op = __qmljs_ge; break; + case IR::OpLe: op = __qmljs_le; break; + case IR::OpEqual: op = __qmljs_eq; break; + case IR::OpNotEqual: op = __qmljs_ne; break; + case IR::OpStrictEqual: op = __qmljs_se; break; + case IR::OpStrictNotEqual: op = __qmljs_sne; break; + case IR::OpInstanceof: op = __qmljs_instanceof; break; + case IR::OpIn: op = __qmljs_in; break; + + case IR::OpAnd: + case IR::OpOr: + assert(!"unreachable"); + break; + } + fct.call(op); + return; + } } else if (IR::Call *c = s->source->asCall()) { if (c->base->asName()) { callActivationProperty(c, t); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index f30254afb4..f7582e7bb0 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -30,19 +30,25 @@ protected: static const RegisterID Gpr0 = JSC::X86Registers::eax; static const RegisterID Gpr1 = JSC::X86Registers::ecx; static const RegisterID Gpr2 = JSC::X86Registers::edx; + static const RegisterID Gpr3 = JSC::X86Registers::esi; + static const RegisterID CalleeSavedGpr = Gpr3; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; #else #error Argh. #endif #if CPU(X86) || CPU(X86_64) - void enterStandardStackFrame() + void enterStandardStackFrame(int locals) { push(StackFrameRegister); move(StackPointerRegister, StackFrameRegister); + sub32(TrustedImm32(locals), StackPointerRegister); + push(CalleeSavedGpr); } - void leaveStandardStackFrame() + void leaveStandardStackFrame(int locals) { + pop(CalleeSavedGpr); + add32(TrustedImm32(locals), StackPointerRegister); pop(StackFrameRegister); } #else -- cgit v1.2.3 From 674a64ef7667858634006c92348166af99c5ea5b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 4 Oct 2012 22:50:01 +0200 Subject: Implement mov unop --- qv4isel_masm.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index d3ac19b73e..2f8539daa8 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -306,7 +306,25 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Subscript *ss = s->source->asSubscript()) { Q_UNIMPLEMENTED(); } else if (IR::Unop *u = s->source->asUnop()) { - Q_UNIMPLEMENTED(); + if (IR::Temp *e = u->expr->asTemp()) { + FunctionCall fct(this); + fct.addArgumentFromRegister(ContextRegister); + Address result = loadTempAddress(Gpr1, t); + Address value = loadTempAddress(Gpr2, e); + fct.addArgumentAsAddress(result); + fct.addArgumentAsAddress(value); + void (*op)(Context *, Value *, const Value *) = 0; + switch (u->op) { + case IR::OpIfTrue: assert(!"unreachable"); break; + case IR::OpNot: op = __qmljs_not; break; + case IR::OpUMinus: op = __qmljs_uminus; break; + case IR::OpUPlus: op = __qmljs_uplus; break; + case IR::OpCompl: op = __qmljs_compl; break; + default: assert(!"unreachable"); break; + } // switch + fct.call(op); + return; + } } else if (IR::Binop *b = s->source->asBinop()) { IR::Temp *l = b->left->asTemp(); IR::Temp *r = b->right->asTemp(); -- cgit v1.2.3 From 1a1ea8b7a3a2154639a22c479855c07819a20b71 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 4 Oct 2012 23:23:50 +0200 Subject: Implement constructActivationProperty --- qv4isel_masm.cpp | 132 +++++++++++++++++++++++++++++++++++-------------------- qv4isel_masm_p.h | 7 +++ 2 files changed, 92 insertions(+), 47 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 2f8539daa8..9059ada9c2 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -109,67 +109,28 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu IR::Name *baseName = call->base->asName(); assert(baseName != 0); - int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = call->args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - - Address tempAddress = loadTempAddress(Gpr1, arg); - FunctionCall fc(this); - fc.addArgumentAsAddress(argumentAddressForCall(i)); - fc.addArgumentAsAddress(tempAddress); - fc.call(__qmljs_copy); - } - - FunctionCall activationCall(this); - - activationCall.addArgumentFromRegister(ContextRegister); - - if (result) { - activationCall.addArgumentAsAddress(loadTempAddress(Gpr0, result)); - } else { - xor32(Gpr0, Gpr0); - activationCall.addArgumentFromRegister(Gpr0); - } - - if (baseName->id) { - move(TrustedImmPtr(identifier(*baseName->id)), Gpr1); - activationCall.addArgumentFromRegister(Gpr1); - activationCall.addArgumentAsAddress(baseAddressForCallArguments()); - activationCall.addArgumentByValue(TrustedImm32(argc)); - activationCall.call(__qmljs_call_activation_property); - } else { + FunctionCall fct(this); + if (baseName->id) + fct.callRuntimeMethod(__qmljs_call_activation_property, result, call->base, call->args); + else { switch (baseName->builtin) { case IR::Name::builtin_invalid: Q_UNREACHABLE(); break; case IR::Name::builtin_typeof: - activationCall.addArgumentAsAddress(baseAddressForCallArguments()); - activationCall.addArgumentByValue(TrustedImm32(argc)); - activationCall.call(__qmljs_builtin_typeof); + fct.callRuntimeMethod(__qmljs_builtin_typeof, result, call->args); break; case IR::Name::builtin_delete: Q_UNREACHABLE(); break; case IR::Name::builtin_throw: - activationCall.addArgumentAsAddress(baseAddressForCallArguments()); - activationCall.addArgumentByValue(TrustedImm32(argc)); - activationCall.call(__qmljs_builtin_throw); + fct.callRuntimeMethod(__qmljs_builtin_throw, result, call->args); break; case IR::Name::builtin_rethrow: - activationCall.addArgumentAsAddress(baseAddressForCallArguments()); - activationCall.addArgumentByValue(TrustedImm32(argc)); - activationCall.call(__qmljs_builtin_rethrow); + fct.callRuntimeMethod(__qmljs_builtin_rethrow, result, call->args); return; // we need to return to avoid checking the exceptions } } - - checkExceptions(); } @@ -183,14 +144,21 @@ void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) { + IR::Name *baseName = call->base->asName(); + assert(baseName != 0); + + FunctionCall fct(this); + fct.callRuntimeMethod(__qmljs_construct_activation_property, result, call->base, call->args); } void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) { + Q_UNIMPLEMENTED(); } void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) { + Q_UNIMPLEMENTED(); } void InstructionSelection::checkExceptions() @@ -300,7 +268,16 @@ void InstructionSelection::visitMove(IR::Move *s) fct.call(__qmljs_init_closure); return; } else if (IR::New *ctor = s->source->asNew()) { - Q_UNIMPLEMENTED(); + if (ctor->base->asName()) { + constructActivationProperty(ctor, t); + return; + } else if (ctor->base->asMember()) { + constructProperty(ctor, t); + return; + } else if (ctor->base->asTemp()) { + constructValue(ctor, t); + return; + } } else if (IR::Member *m = s->source->asMember()) { Q_UNIMPLEMENTED(); } else if (IR::Subscript *ss = s->source->asSubscript()) { @@ -507,3 +484,64 @@ void InstructionSelection::visitRet(IR::Ret *s) Q_UNUSED(s); } +void InstructionSelection::FunctionCall::addVariableArguments(IR::ExprList* args) +{ + int argc = 0; + for (IR::ExprList *it = args; it; it = it->next) { + ++argc; + } + + int i = 0; + for (IR::ExprList *it = args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + + Address tempAddress = isel->loadTempAddress(Gpr0, arg); + FunctionCall fc(isel); + fc.addArgumentAsAddress(isel->argumentAddressForCall(i)); + fc.addArgumentAsAddress(tempAddress); + fc.call(__qmljs_copy); + } + + addArgumentAsAddress(isel->baseAddressForCallArguments()); + addArgumentByValue(TrustedImm32(argc)); +} + +void InstructionSelection::FunctionCall::callRuntimeMethod(ActivationMethod method, IR::Temp *result, IR::Expr *base, IR::ExprList *args) +{ + IR::Name *baseName = base->asName(); + assert(baseName != 0); + + addArgumentFromRegister(ContextRegister); + + if (result) { + addArgumentAsAddress(isel->loadTempAddress(Gpr1, result)); + } else { + isel->xor32(Gpr1, Gpr1); + addArgumentFromRegister(Gpr1); + } + + isel->move(TrustedImmPtr(isel->identifier(*baseName->id)), Gpr2); + addArgumentFromRegister(Gpr2); + addVariableArguments(args); + call(method); + + isel->checkExceptions(); +} + +void InstructionSelection::FunctionCall::callRuntimeMethod(BuiltinMethod method, IR::Temp *result, IR::ExprList *args) +{ + addArgumentFromRegister(ContextRegister); + + if (result) { + addArgumentAsAddress(isel->loadTempAddress(Gpr1, result)); + } else { + isel->xor32(Gpr1, Gpr1); + addArgumentFromRegister(Gpr1); + } + + addVariableArguments(args); + call(method); + + isel->checkExceptions(); +} diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index f7582e7bb0..21fb8bc79e 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -138,6 +138,8 @@ private: arguments << arg; } + void addVariableArguments(IR::ExprList* args); + void call(FunctionPtr function) { for (int i = arguments.count() - 1; i >= 0; --i) @@ -146,6 +148,11 @@ private: isel->add32(TrustedImm32(arguments.count() * sizeof(void*)), StackPointerRegister); } + typedef void (*ActivationMethod)(VM::Context *, VM::Value *result, VM::String *name, VM::Value *args, int argc); + typedef void (*BuiltinMethod)(VM::Context *, VM::Value *result, VM::Value *args, int argc); + void callRuntimeMethod(ActivationMethod method, IR::Temp *result, IR::Expr *base, IR::ExprList *args); + void callRuntimeMethod(BuiltinMethod method, IR::Temp *result, IR::ExprList *args); + private: class Argument { public: -- cgit v1.2.3 From ee9c4b976b874c736578ffe9a2d2d9c3b8da35f0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 5 Oct 2012 07:55:05 +0200 Subject: Generate the code for copying the argments earlier, to avoid register clobbering between __qmljs_copy calls --- qv4isel_masm.cpp | 15 ++++++++++----- qv4isel_masm_p.h | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 9059ada9c2..5175da865f 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -484,7 +484,7 @@ void InstructionSelection::visitRet(IR::Ret *s) Q_UNUSED(s); } -void InstructionSelection::FunctionCall::addVariableArguments(IR::ExprList* args) +int InstructionSelection::FunctionCall::prepareVariableArguments(IR::ExprList* args) { int argc = 0; for (IR::ExprList *it = args; it; it = it->next) { @@ -503,8 +503,7 @@ void InstructionSelection::FunctionCall::addVariableArguments(IR::ExprList* args fc.call(__qmljs_copy); } - addArgumentAsAddress(isel->baseAddressForCallArguments()); - addArgumentByValue(TrustedImm32(argc)); + return argc; } void InstructionSelection::FunctionCall::callRuntimeMethod(ActivationMethod method, IR::Temp *result, IR::Expr *base, IR::ExprList *args) @@ -512,6 +511,8 @@ void InstructionSelection::FunctionCall::callRuntimeMethod(ActivationMethod meth IR::Name *baseName = base->asName(); assert(baseName != 0); + int argc = prepareVariableArguments(args); + addArgumentFromRegister(ContextRegister); if (result) { @@ -523,7 +524,8 @@ void InstructionSelection::FunctionCall::callRuntimeMethod(ActivationMethod meth isel->move(TrustedImmPtr(isel->identifier(*baseName->id)), Gpr2); addArgumentFromRegister(Gpr2); - addVariableArguments(args); + addArgumentAsAddress(isel->baseAddressForCallArguments()); + addArgumentByValue(TrustedImm32(argc)); call(method); isel->checkExceptions(); @@ -531,6 +533,8 @@ void InstructionSelection::FunctionCall::callRuntimeMethod(ActivationMethod meth void InstructionSelection::FunctionCall::callRuntimeMethod(BuiltinMethod method, IR::Temp *result, IR::ExprList *args) { + int argc = prepareVariableArguments(args); + addArgumentFromRegister(ContextRegister); if (result) { @@ -540,7 +544,8 @@ void InstructionSelection::FunctionCall::callRuntimeMethod(BuiltinMethod method, addArgumentFromRegister(Gpr1); } - addVariableArguments(args); + addArgumentAsAddress(isel->baseAddressForCallArguments()); + addArgumentByValue(TrustedImm32(argc)); call(method); isel->checkExceptions(); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 21fb8bc79e..f9b5729f87 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -138,7 +138,7 @@ private: arguments << arg; } - void addVariableArguments(IR::ExprList* args); + int prepareVariableArguments(IR::ExprList* args); void call(FunctionPtr function) { -- cgit v1.2.3 From 3bda6fb4183242fa5f842ae68537e3769acb79b1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 5 Oct 2012 08:09:12 +0200 Subject: A bit more inplace assignment implemented --- qv4isel_masm.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 5175da865f..50740b00cb 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -376,6 +376,66 @@ void InstructionSelection::visitMove(IR::Move *s) Q_UNIMPLEMENTED(); } } else { + // inplace assignment, e.g. x += 1, ++x, ... + if (IR::Temp *t = s->target->asTemp()) { + if (IR::Temp *t2 = s->source->asTemp()) { + FunctionCall fct(this); + fct.addArgumentFromRegister(ContextRegister); + Address target = loadTempAddress(Gpr1, t); + fct.addArgumentAsAddress(target); + Address source = loadTempAddress(Gpr2, t2); + fct.addArgumentAsAddress(source); + void (*op)(Context *, Value *, const Value *, const Value *) = 0; + switch (s->op) { + case IR::OpBitAnd: op = __qmljs_bit_and; break; + case IR::OpBitOr: op = __qmljs_bit_or; break; + case IR::OpBitXor: op = __qmljs_bit_xor; break; + case IR::OpAdd: op = __qmljs_add; break; + case IR::OpSub: op = __qmljs_sub; break; + case IR::OpMul: op = __qmljs_mul; break; + case IR::OpDiv: op = __qmljs_div; break; + case IR::OpMod: op = __qmljs_mod; break; + case IR::OpLShift: op = __qmljs_shl; break; + case IR::OpRShift: op = __qmljs_shr; break; + case IR::OpURShift: op = __qmljs_ushr; break; + default: + Q_UNREACHABLE(); + break; + } + + fct.call(op); + return; + } + } else if (IR::Name *n = s->target->asName()) { + if (IR::Temp *t = s->source->asTemp()) { + void (*op)(Context *, String *, Value *) = 0; + switch (s->op) { + case IR::OpBitAnd: op = __qmljs_inplace_bit_and_name; break; + case IR::OpBitOr: op = __qmljs_inplace_bit_or_name; break; + case IR::OpBitXor: op = __qmljs_inplace_bit_xor_name; break; + case IR::OpAdd: op = __qmljs_inplace_add_name; break; + case IR::OpSub: op = __qmljs_inplace_sub_name; break; + case IR::OpMul: op = __qmljs_inplace_mul_name; break; + case IR::OpDiv: op = __qmljs_inplace_div_name; break; + case IR::OpMod: op = __qmljs_inplace_mod_name; break; + case IR::OpLShift: op = __qmljs_inplace_shl_name; break; + case IR::OpRShift: op = __qmljs_inplace_shr_name; break; + case IR::OpURShift: op = __qmljs_inplace_ushr_name; break; + default: + Q_UNREACHABLE(); + break; + } + FunctionCall fct(this); + fct.addArgumentFromRegister(ContextRegister); + move(TrustedImmPtr(identifier(*n->id)), Gpr1); + fct.addArgumentFromRegister(Gpr1); + Address target = loadTempAddress(Gpr2, t); + fct.addArgumentAsAddress(target); + fct.call(op); + checkExceptions(); + return; + } + } Q_UNIMPLEMENTED(); } Q_UNIMPLEMENTED(); -- cgit v1.2.3 From a97b4212b9c16928f297cf1fa4b3b9750ec9540f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 5 Oct 2012 11:02:54 +0200 Subject: Simplify passing temps as method arguments --- qv4isel_masm.cpp | 9 +++------ qv4isel_masm_p.h | 24 ++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 50740b00cb..667662dc88 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -381,10 +381,8 @@ void InstructionSelection::visitMove(IR::Move *s) if (IR::Temp *t2 = s->source->asTemp()) { FunctionCall fct(this); fct.addArgumentFromRegister(ContextRegister); - Address target = loadTempAddress(Gpr1, t); - fct.addArgumentAsAddress(target); - Address source = loadTempAddress(Gpr2, t2); - fct.addArgumentAsAddress(source); + fct.addArgument(t); + fct.addArgument(t2); void (*op)(Context *, Value *, const Value *, const Value *) = 0; switch (s->op) { case IR::OpBitAnd: op = __qmljs_bit_and; break; @@ -429,8 +427,7 @@ void InstructionSelection::visitMove(IR::Move *s) fct.addArgumentFromRegister(ContextRegister); move(TrustedImmPtr(identifier(*n->id)), Gpr1); fct.addArgumentFromRegister(Gpr1); - Address target = loadTempAddress(Gpr2, t); - fct.addArgumentAsAddress(target); + fct.addArgument(t); fct.call(op); checkExceptions(); return; diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index f9b5729f87..281ed1d709 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -138,6 +138,13 @@ private: arguments << arg; } + void addArgument(IR::Temp* temp) + { + Argument arg; + arg.setValue(temp); + arguments << arg; + } + int prepareVariableArguments(IR::ExprList* args); void call(FunctionPtr function) @@ -181,8 +188,13 @@ private: this->value = value; type = Value; } + void setValue(IR::Temp *temp) + { + this->temp = temp; + type = Temp; + } - void push(JSC::MacroAssembler* assembler, RegisterID scratchRegister) const + void push(InstructionSelection* assembler, RegisterID scratchRegister) const { switch (type) { case None: break; @@ -193,6 +205,12 @@ private: assembler->add32(TrustedImm32(address.offset), address.base, scratchRegister); assembler->push(scratchRegister); break; + case Temp: { + JSC::MacroAssembler::Address tempAddr = assembler->loadTempAddress(scratchRegister, temp); + assembler->add32(TrustedImm32(tempAddr.offset), tempAddr.base, scratchRegister); + assembler->push(scratchRegister); + break; + } } } private: @@ -201,11 +219,13 @@ private: Register, Value, MemoryValue, - Address + Address, + Temp } type; RegisterID reg; TrustedImm32 value; JSC::MacroAssembler::Address address; + IR::Temp* temp; }; QList arguments; InstructionSelection* isel; -- cgit v1.2.3 From 392cf477a1b59ab7920d33ed11874d5c2729b26d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 5 Oct 2012 11:13:54 +0200 Subject: Simpler approach to function calls --- qv4isel_masm.cpp | 7 +------ qv4isel_masm_p.h | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 667662dc88..6600aaca1b 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -423,12 +423,7 @@ void InstructionSelection::visitMove(IR::Move *s) Q_UNREACHABLE(); break; } - FunctionCall fct(this); - fct.addArgumentFromRegister(ContextRegister); - move(TrustedImmPtr(identifier(*n->id)), Gpr1); - fct.addArgumentFromRegister(Gpr1); - fct.addArgument(t); - fct.call(op); + generateFunctionCall(op, ContextRegister, TrustedImmPtr(identifier(*n->id)), t); checkExceptions(); return; } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 281ed1d709..7d3f07a331 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -231,6 +231,30 @@ private: InstructionSelection* isel; }; + using JSC::MacroAssembler::push; + void push(IR::Temp* temp) + { + Address addr = loadTempAddress(Gpr0, temp); + add32(TrustedImm32(addr.offset), addr.base, Gpr0); + push(Gpr0); + } + void push(TrustedImmPtr ptr) + { + move(ptr, Gpr0); + push(Gpr0); + } + + template + void generateFunctionCall(FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + // Reverse order + push(arg3); + push(arg2); + push(arg1); + callAbsolute(function); + add32(TrustedImm32(3 * sizeof(void*)), StackPointerRegister); + } + struct CallToLink { Call call; FunctionPtr externalFunction; -- cgit v1.2.3 From 0c6fd16edbf57d4b7746064d5049f57a26db3799 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 5 Oct 2012 11:35:46 +0200 Subject: further call simplification --- qv4isel_masm.cpp | 21 ++++----------------- qv4isel_masm_p.h | 15 +++++++++++++-- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 6600aaca1b..873048949b 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -193,12 +193,7 @@ void InstructionSelection::visitMove(IR::Move *s) String *propertyName = identifier(*n->id); if (IR::Temp *t = s->source->asTemp()) { - FunctionCall fct(this); - fct.addArgumentFromRegister(ContextRegister); - move(TrustedImmPtr(propertyName), Gpr1); - fct.addArgumentFromRegister(Gpr1); - fct.addArgumentAsAddress(loadTempAddress(Gpr2, t)); - fct.call(__qmljs_set_activation_property); + generateFunctionCall(__qmljs_set_activation_property, ContextRegister, propertyName, t); checkExceptions(); return; } else { @@ -206,19 +201,11 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Temp *t = s->target->asTemp()) { if (IR::Name *n = s->source->asName()) { - Address temp = loadTempAddress(Gpr0, t); - - FunctionCall fc(this); - fc.addArgumentFromRegister(ContextRegister); - fc.addArgumentAsAddress(temp); - if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. - fc.call(__qmljs_get_thisObject); + generateFunctionCall(__qmljs_get_thisObject, ContextRegister, t); } else { String *propertyName = identifier(*n->id); - move(TrustedImmPtr(propertyName), Gpr1); - fc.addArgumentFromRegister(Gpr1); - fc.call(__qmljs_get_activation_property); + generateFunctionCall(__qmljs_get_activation_property, ContextRegister, t, propertyName); checkExceptions(); } return; @@ -423,7 +410,7 @@ void InstructionSelection::visitMove(IR::Move *s) Q_UNREACHABLE(); break; } - generateFunctionCall(op, ContextRegister, TrustedImmPtr(identifier(*n->id)), t); + generateFunctionCall(op, ContextRegister, identifier(*n->id), t); checkExceptions(); return; } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 7d3f07a331..e516db677a 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -238,12 +238,13 @@ private: add32(TrustedImm32(addr.offset), addr.base, Gpr0); push(Gpr0); } - void push(TrustedImmPtr ptr) + void push(VM::String* name) { - move(ptr, Gpr0); + move(TrustedImmPtr(name), Gpr0); push(Gpr0); } + template void generateFunctionCall(FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) { @@ -255,6 +256,16 @@ private: add32(TrustedImm32(3 * sizeof(void*)), StackPointerRegister); } + template + void generateFunctionCall(FunctionPtr function, Arg1 arg1, Arg2 arg2) + { + // Reverse order + push(arg2); + push(arg1); + callAbsolute(function); + add32(TrustedImm32(2 * sizeof(void*)), StackPointerRegister); + } + struct CallToLink { Call call; FunctionPtr externalFunction; -- cgit v1.2.3 From a08a3220b17b54890ccfd0199c9d8d809806ad16 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 5 Oct 2012 11:48:05 +0200 Subject: Simplify more function calls --- qv4isel_masm.cpp | 70 ++++++++------------------------------------------------ qv4isel_masm_p.h | 20 ++++++++++++++-- 2 files changed, 28 insertions(+), 62 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 873048949b..dd5f224dc7 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -232,27 +232,12 @@ void InstructionSelection::visitMove(IR::Move *s) } return; } else if (IR::Temp *t2 = s->source->asTemp()) { - Address dest = loadTempAddress(Gpr1, t); - Address source = loadTempAddress(Gpr2, t2); - FunctionCall fc(this); - fc.addArgumentAsAddress(dest); - fc.addArgumentAsAddress(source); - fc.call(__qmljs_copy); + generateFunctionCall(__qmljs_copy, t, t2); return; } else if (IR::String *str = s->source->asString()) { - FunctionCall fct(this); - Address temp = loadTempAddress(Gpr0, t); - fct.addArgumentAsAddress(temp); - move(TrustedImmPtr(_engine->newString(*str->value)), Gpr1); - fct.addArgumentFromRegister(Gpr1); - fct.call(__qmljs_init_string); + generateFunctionCall(__qmljs_init_string, t, _engine->newString(*str->value)); } else if (IR::Closure *clos = s->source->asClosure()) { - FunctionCall fct(this); - fct.addArgumentFromRegister(ContextRegister); - fct.addArgumentAsAddress(loadTempAddress(Gpr0, t)); - move(TrustedImmPtr(clos->value), Gpr1); - fct.addArgumentFromRegister(Gpr1); - fct.call(__qmljs_init_closure); + generateFunctionCall(__qmljs_init_closure, ContextRegister, TrustedImmPtr(clos->value)); return; } else if (IR::New *ctor = s->source->asNew()) { if (ctor->base->asName()) { @@ -271,12 +256,6 @@ void InstructionSelection::visitMove(IR::Move *s) Q_UNIMPLEMENTED(); } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { - FunctionCall fct(this); - fct.addArgumentFromRegister(ContextRegister); - Address result = loadTempAddress(Gpr1, t); - Address value = loadTempAddress(Gpr2, e); - fct.addArgumentAsAddress(result); - fct.addArgumentAsAddress(value); void (*op)(Context *, Value *, const Value *) = 0; switch (u->op) { case IR::OpIfTrue: assert(!"unreachable"); break; @@ -286,23 +265,13 @@ void InstructionSelection::visitMove(IR::Move *s) case IR::OpCompl: op = __qmljs_compl; break; default: assert(!"unreachable"); break; } // switch - fct.call(op); + generateFunctionCall(op, ContextRegister, t, e); return; } } else if (IR::Binop *b = s->source->asBinop()) { IR::Temp *l = b->left->asTemp(); IR::Temp *r = b->right->asTemp(); if (l && r) { - FunctionCall fct(this); - fct.addArgumentFromRegister(ContextRegister); - - Address result = loadTempAddress(Gpr1, t); - Address lhs = loadTempAddress(Gpr2, l); - Address rhs = loadTempAddress(Gpr3, r); - fct.addArgumentAsAddress(result); - fct.addArgumentAsAddress(lhs); - fct.addArgumentAsAddress(rhs); - void (*op)(Context *, Value *, const Value *, const Value *) = 0; switch ((IR::AluOp) b->op) { @@ -342,7 +311,7 @@ void InstructionSelection::visitMove(IR::Move *s) assert(!"unreachable"); break; } - fct.call(op); + generateFunctionCall(op, ContextRegister, t, l, r); return; } } else if (IR::Call *c = s->source->asCall()) { @@ -366,10 +335,6 @@ void InstructionSelection::visitMove(IR::Move *s) // inplace assignment, e.g. x += 1, ++x, ... if (IR::Temp *t = s->target->asTemp()) { if (IR::Temp *t2 = s->source->asTemp()) { - FunctionCall fct(this); - fct.addArgumentFromRegister(ContextRegister); - fct.addArgument(t); - fct.addArgument(t2); void (*op)(Context *, Value *, const Value *, const Value *) = 0; switch (s->op) { case IR::OpBitAnd: op = __qmljs_bit_and; break; @@ -388,7 +353,7 @@ void InstructionSelection::visitMove(IR::Move *s) break; } - fct.call(op); + generateFunctionCall(op, ContextRegister, t, t, t2); return; } } else if (IR::Name *n = s->target->asName()) { @@ -449,10 +414,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) booleanConversion.link(this); { - FunctionCall fct(this); - fct.addArgumentFromRegister(ContextRegister); - fct.addArgumentAsAddress(temp); - fct.call(__qmljs_to_boolean); + generateFunctionCall(__qmljs_to_boolean, ContextRegister, t); move(ReturnValueRegister, Gpr1); } @@ -467,9 +429,6 @@ void InstructionSelection::visitCJump(IR::CJump *s) IR::Temp *l = b->left->asTemp(); IR::Temp *r = b->right->asTemp(); if (l && r) { - Address lhs = loadTempAddress(Gpr1, l); - Address rhs = loadTempAddress(Gpr2, r); - bool (*op)(Context *, const Value *, const Value *); switch (b->op) { default: Q_UNREACHABLE(); assert(!"todo"); break; @@ -485,11 +444,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) case IR::OpIn: op = __qmljs_cmp_in; break; } // switch - FunctionCall fct(this); - fct.addArgumentFromRegister(ContextRegister); - fct.addArgumentAsAddress(lhs); - fct.addArgumentAsAddress(rhs); - fct.call(op); + generateFunctionCall(op, ContextRegister, l, r); move(ReturnValueRegister, Gpr0); move(TrustedImm32(1), Gpr1); @@ -510,13 +465,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { if (IR::Temp *t = s->expr->asTemp()) { - Address source = loadTempAddress(Gpr0, t); - Address result = Address(ContextRegister, offsetof(Context, result)); - - FunctionCall fc(this); - fc.addArgumentAsAddress(result); - fc.addArgumentAsAddress(source); - fc.call(__qmljs_copy); + add32(TrustedImm32(offsetof(Context, result)), ContextRegister, Gpr1); + generateFunctionCall(__qmljs_copy, Gpr1, t); return; } Q_UNIMPLEMENTED(); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index e516db677a..d6aad918a2 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -238,11 +238,27 @@ private: add32(TrustedImm32(addr.offset), addr.base, Gpr0); push(Gpr0); } - void push(VM::String* name) + void push(TrustedImmPtr ptr) { - move(TrustedImmPtr(name), Gpr0); + move(TrustedImmPtr(ptr), Gpr0); push(Gpr0); } + void push(VM::String* name) + { + push(TrustedImmPtr(name)); + } + + template + void generateFunctionCall(FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + // Reverse order + push(arg4); + push(arg3); + push(arg2); + push(arg1); + callAbsolute(function); + add32(TrustedImm32(4 * sizeof(void*)), StackPointerRegister); + } template -- cgit v1.2.3 From 8235683a6ba0aa616f6a761f62ca5540739581f8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 7 Oct 2012 19:45:40 +0200 Subject: Fix closure init --- masm/disassembler/udis86/ud_opcode.pyc | Bin 7170 -> 7170 bytes masm/disassembler/udis86/ud_optable.pyc | Bin 2793 -> 2793 bytes qv4isel_masm.cpp | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/masm/disassembler/udis86/ud_opcode.pyc b/masm/disassembler/udis86/ud_opcode.pyc index 0643b37eb8..cade0c2172 100644 Binary files a/masm/disassembler/udis86/ud_opcode.pyc and b/masm/disassembler/udis86/ud_opcode.pyc differ diff --git a/masm/disassembler/udis86/ud_optable.pyc b/masm/disassembler/udis86/ud_optable.pyc index 741467979e..8f36a9c0b1 100644 Binary files a/masm/disassembler/udis86/ud_optable.pyc and b/masm/disassembler/udis86/ud_optable.pyc differ diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index dd5f224dc7..748a1112b9 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -237,7 +237,7 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::String *str = s->source->asString()) { generateFunctionCall(__qmljs_init_string, t, _engine->newString(*str->value)); } else if (IR::Closure *clos = s->source->asClosure()) { - generateFunctionCall(__qmljs_init_closure, ContextRegister, TrustedImmPtr(clos->value)); + generateFunctionCall(__qmljs_init_closure, ContextRegister, t, TrustedImmPtr(clos->value)); return; } else if (IR::New *ctor = s->source->asNew()) { if (ctor->base->asName()) { -- cgit v1.2.3 From 17c0f2df08dc526770f7eb40815f6eb74becdbb7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 7 Oct 2012 21:43:29 +0200 Subject: Port remaining code to better function call mechanism --- qv4isel_masm.cpp | 69 +++++---------------- qv4isel_masm_p.h | 184 ++++++++++++++++--------------------------------------- 2 files changed, 70 insertions(+), 183 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 748a1112b9..7499bd014e 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -85,7 +85,7 @@ String *InstructionSelection::identifier(const QString &s) return _engine->identifier(s); } -JSC::MacroAssembler::Address InstructionSelection::loadTempAddress(RegisterID reg, IR::Temp *t) +InstructionSelection::Pointer InstructionSelection::loadTempAddress(RegisterID reg, IR::Temp *t) { int32_t offset = 0; if (t->index < 0) { @@ -101,7 +101,7 @@ JSC::MacroAssembler::Address InstructionSelection::loadTempAddress(RegisterID re - sizeof(void*); // size of ebp reg = StackFrameRegister; } - return Address(reg, offset); + return Pointer(reg, offset); } void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) @@ -109,25 +109,24 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu IR::Name *baseName = call->base->asName(); assert(baseName != 0); - FunctionCall fct(this); if (baseName->id) - fct.callRuntimeMethod(__qmljs_call_activation_property, result, call->base, call->args); + callRuntimeMethod(__qmljs_call_activation_property, result, call->base, call->args); else { switch (baseName->builtin) { case IR::Name::builtin_invalid: Q_UNREACHABLE(); break; case IR::Name::builtin_typeof: - fct.callRuntimeMethod(__qmljs_builtin_typeof, result, call->args); + callRuntimeMethod(__qmljs_builtin_typeof, result, call->args); break; case IR::Name::builtin_delete: Q_UNREACHABLE(); break; case IR::Name::builtin_throw: - fct.callRuntimeMethod(__qmljs_builtin_throw, result, call->args); + callRuntimeMethod(__qmljs_builtin_throw, result, call->args); break; case IR::Name::builtin_rethrow: - fct.callRuntimeMethod(__qmljs_builtin_rethrow, result, call->args); + callRuntimeMethod(__qmljs_builtin_rethrow, result, call->args); return; // we need to return to avoid checking the exceptions } } @@ -147,8 +146,7 @@ void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp * IR::Name *baseName = call->base->asName(); assert(baseName != 0); - FunctionCall fct(this); - fct.callRuntimeMethod(__qmljs_construct_activation_property, result, call->base, call->args); + callRuntimeMethod(__qmljs_construct_activation_property, result, call->base, call->args); } void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) @@ -465,15 +463,14 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { if (IR::Temp *t = s->expr->asTemp()) { - add32(TrustedImm32(offsetof(Context, result)), ContextRegister, Gpr1); - generateFunctionCall(__qmljs_copy, Gpr1, t); + generateFunctionCall(__qmljs_copy, Pointer(ContextRegister, offsetof(Context, result)), t); return; } Q_UNIMPLEMENTED(); Q_UNUSED(s); } -int InstructionSelection::FunctionCall::prepareVariableArguments(IR::ExprList* args) +int InstructionSelection::prepareVariableArguments(IR::ExprList* args) { int argc = 0; for (IR::ExprList *it = args; it; it = it->next) { @@ -484,58 +481,26 @@ int InstructionSelection::FunctionCall::prepareVariableArguments(IR::ExprList* a for (IR::ExprList *it = args; it; it = it->next, ++i) { IR::Temp *arg = it->expr->asTemp(); assert(arg != 0); - - Address tempAddress = isel->loadTempAddress(Gpr0, arg); - FunctionCall fc(isel); - fc.addArgumentAsAddress(isel->argumentAddressForCall(i)); - fc.addArgumentAsAddress(tempAddress); - fc.call(__qmljs_copy); + generateFunctionCall(__qmljs_copy, argumentAddressForCall(i), arg); } return argc; } -void InstructionSelection::FunctionCall::callRuntimeMethod(ActivationMethod method, IR::Temp *result, IR::Expr *base, IR::ExprList *args) +void InstructionSelection::callRuntimeMethod(ActivationMethod method, IR::Temp *result, IR::Expr *base, IR::ExprList *args) { IR::Name *baseName = base->asName(); assert(baseName != 0); int argc = prepareVariableArguments(args); - - addArgumentFromRegister(ContextRegister); - - if (result) { - addArgumentAsAddress(isel->loadTempAddress(Gpr1, result)); - } else { - isel->xor32(Gpr1, Gpr1); - addArgumentFromRegister(Gpr1); - } - - isel->move(TrustedImmPtr(isel->identifier(*baseName->id)), Gpr2); - addArgumentFromRegister(Gpr2); - addArgumentAsAddress(isel->baseAddressForCallArguments()); - addArgumentByValue(TrustedImm32(argc)); - call(method); - - isel->checkExceptions(); + generateFunctionCall(method, ContextRegister, result, identifier(*baseName->id), baseAddressForCallArguments(), TrustedImm32(argc)); + checkExceptions(); } -void InstructionSelection::FunctionCall::callRuntimeMethod(BuiltinMethod method, IR::Temp *result, IR::ExprList *args) +void InstructionSelection::callRuntimeMethod(BuiltinMethod method, IR::Temp *result, IR::ExprList *args) { int argc = prepareVariableArguments(args); - - addArgumentFromRegister(ContextRegister); - - if (result) { - addArgumentAsAddress(isel->loadTempAddress(Gpr1, result)); - } else { - isel->xor32(Gpr1, Gpr1); - addArgumentFromRegister(Gpr1); - } - - addArgumentAsAddress(isel->baseAddressForCallArguments()); - addArgumentByValue(TrustedImm32(argc)); - call(method); - - isel->checkExceptions(); + generateFunctionCall(method, ContextRegister, result, baseAddressForCallArguments(), TrustedImm32(argc)); + checkExceptions(); } + diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index d6aad918a2..2dde989b56 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -37,6 +37,19 @@ protected: #error Argh. #endif + // Explicit type to allow distinguishing between + // pushing an address itself or the value it points + // to onto the stack when calling functions. + struct Pointer : public Address + { + explicit Pointer(const Address& addr) + : Address(addr) + {} + explicit Pointer(RegisterID reg, int32_t offset) + : Address(reg, offset) + {} + }; + #if CPU(X86) || CPU(X86_64) void enterStandardStackFrame(int locals) { @@ -64,20 +77,20 @@ protected: // Some run-time functions take (Value* args, int argc). This function is for populating // the args. - Address argumentAddressForCall(int argument) + Pointer argumentAddressForCall(int argument) { const int index = _function->maxNumberOfArguments - argument; - return Address(StackFrameRegister, sizeof(VM::Value) * (-index) + return Pointer(StackFrameRegister, sizeof(VM::Value) * (-index) - sizeof(void*) // size of ebp ); } - Address baseAddressForCallArguments() + Pointer baseAddressForCallArguments() { return argumentAddressForCall(0); } VM::String *identifier(const QString &s); - Address loadTempAddress(RegisterID reg, IR::Temp *t); + Pointer loadTempAddress(RegisterID reg, IR::Temp *t); void callActivationProperty(IR::Call *call, IR::Temp *result); void callProperty(IR::Call *call, IR::Temp *result); void constructActivationProperty(IR::New *call, IR::Temp *result); @@ -106,148 +119,50 @@ private: _callsToLink.append(ctl); } - class FunctionCall - { - public: - FunctionCall(InstructionSelection* selection) - : isel(selection) - {} - - void addArgumentFromRegister(RegisterID reg) - { - Argument arg; - arg.setRegister(reg); - arguments << arg; - } - void addArgumentFromMemory(const Address& address) - { - Argument arg; - arg.setMemoryValue(address); - arguments << arg; - } - void addArgumentAsAddress(const Address& address) - { - Argument arg; - arg.setAddress(address); - arguments << arg; - } - void addArgumentByValue(TrustedImm32 value) - { - Argument arg; - arg.setValue(value); - arguments << arg; - } - - void addArgument(IR::Temp* temp) - { - Argument arg; - arg.setValue(temp); - arguments << arg; - } - - int prepareVariableArguments(IR::ExprList* args); - - void call(FunctionPtr function) - { - for (int i = arguments.count() - 1; i >= 0; --i) - arguments.at(i).push(isel, /*scratch register*/ Gpr0); - isel->callAbsolute(function); - isel->add32(TrustedImm32(arguments.count() * sizeof(void*)), StackPointerRegister); - } + using JSC::MacroAssembler::push; - typedef void (*ActivationMethod)(VM::Context *, VM::Value *result, VM::String *name, VM::Value *args, int argc); - typedef void (*BuiltinMethod)(VM::Context *, VM::Value *result, VM::Value *args, int argc); - void callRuntimeMethod(ActivationMethod method, IR::Temp *result, IR::Expr *base, IR::ExprList *args); - void callRuntimeMethod(BuiltinMethod method, IR::Temp *result, IR::ExprList *args); - - private: - class Argument { - public: - Argument() - : type(None) - , address(InstructionSelection::Gpr0, 0) - {} - - void setRegister(RegisterID regi) - { - reg = regi; - type = Register; - } - void setMemoryValue(const Address& address) - { - this->address = address; - type = MemoryValue; - } - void setAddress(const Address& address) - { - this->address = address; - type = Address; - } - void setValue(TrustedImm32 value) - { - this->value = value; - type = Value; - } - void setValue(IR::Temp *temp) - { - this->temp = temp; - type = Temp; - } - - void push(InstructionSelection* assembler, RegisterID scratchRegister) const - { - switch (type) { - case None: break; - case Register: assembler->push(reg); break; - case Value: assembler->push(value); break; - case MemoryValue: assembler->push(address); break; - case Address: - assembler->add32(TrustedImm32(address.offset), address.base, scratchRegister); - assembler->push(scratchRegister); - break; - case Temp: { - JSC::MacroAssembler::Address tempAddr = assembler->loadTempAddress(scratchRegister, temp); - assembler->add32(TrustedImm32(tempAddr.offset), tempAddr.base, scratchRegister); - assembler->push(scratchRegister); - break; - } - } - } - private: - enum Type { - None, - Register, - Value, - MemoryValue, - Address, - Temp - } type; - RegisterID reg; - TrustedImm32 value; - JSC::MacroAssembler::Address address; - IR::Temp* temp; - }; - QList arguments; - InstructionSelection* isel; - }; + void push(const Pointer& ptr) + { + add32(TrustedImm32(ptr.offset), ptr.base, Gpr0); + push(Gpr0); + } - using JSC::MacroAssembler::push; void push(IR::Temp* temp) { - Address addr = loadTempAddress(Gpr0, temp); - add32(TrustedImm32(addr.offset), addr.base, Gpr0); - push(Gpr0); + if (temp) { + Pointer addr = loadTempAddress(Gpr0, temp); + push(addr); + } else { + xor32(Gpr0, Gpr0); + push(Gpr0); + } } + void push(TrustedImmPtr ptr) { move(TrustedImmPtr(ptr), Gpr0); push(Gpr0); } + void push(VM::String* name) { push(TrustedImmPtr(name)); } + template + void generateFunctionCall(FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + { + // Reverse order + push(arg5); + push(arg4); + push(arg3); + push(arg2); + push(arg1); + callAbsolute(function); + add32(TrustedImm32(5 * sizeof(void*)), StackPointerRegister); + } + + template void generateFunctionCall(FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { @@ -282,6 +197,13 @@ private: add32(TrustedImm32(2 * sizeof(void*)), StackPointerRegister); } + int prepareVariableArguments(IR::ExprList* args); + + typedef void (*ActivationMethod)(VM::Context *, VM::Value *result, VM::String *name, VM::Value *args, int argc); + typedef void (*BuiltinMethod)(VM::Context *, VM::Value *result, VM::Value *args, int argc); + void callRuntimeMethod(ActivationMethod method, IR::Temp *result, IR::Expr *base, IR::ExprList *args); + void callRuntimeMethod(BuiltinMethod method, IR::Temp *result, IR::ExprList *args); + struct CallToLink { Call call; FunctionPtr externalFunction; -- cgit v1.2.3 From a4d038e965297f154d58627b155ec7b8d48e3f9f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 7 Oct 2012 21:57:10 +0200 Subject: Implement call value and property --- masm/assembler/MacroAssemblerCodeRef.h | 8 ++++++++ qv4isel_masm.cpp | 15 +++++++++++++++ qv4isel_masm_p.h | 13 +++++++++++++ 3 files changed, 36 insertions(+) diff --git a/masm/assembler/MacroAssemblerCodeRef.h b/masm/assembler/MacroAssemblerCodeRef.h index c2af24060a..2978f10f85 100644 --- a/masm/assembler/MacroAssemblerCodeRef.h +++ b/masm/assembler/MacroAssemblerCodeRef.h @@ -134,6 +134,14 @@ public: ASSERT_VALID_CODE_POINTER(m_value); } + template + FunctionPtr(returnType(*value)(argType1, argType2, argType3, argType4, argType5, argType6)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + // MSVC doesn't seem to treat functions with different calling conventions as // different types; these methods already defined for fastcall, below. #if CALLING_CONVENTION_IS_STDCALL && !OS(WINDOWS) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 7499bd014e..9c9984f45d 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -135,10 +135,25 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) { + IR::Temp *baseTemp = call->base->asTemp(); + assert(baseTemp != 0); + + int argc = prepareVariableArguments(call->args); + IR::Temp* thisObject = 0; + generateFunctionCall(__qmljs_call_value, ContextRegister, result, baseTemp, thisObject, baseAddressForCallArguments(), TrustedImm32(argc)); + checkExceptions(); } void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) { + IR::Member *member = call->base->asMember(); + assert(member != 0); + assert(member->base->asTemp() != 0); + + int argc = prepareVariableArguments(call->args); + IR::Temp* thisObject = 0; + generateFunctionCall(__qmljs_call_property, ContextRegister, result, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); + checkExceptions(); } void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 2dde989b56..8a4af26b22 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -149,6 +149,19 @@ private: push(TrustedImmPtr(name)); } + template + void generateFunctionCall(FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) + { + // Reverse order + push(arg6); + push(arg5); + push(arg4); + push(arg3); + push(arg2); + push(arg1); + callAbsolute(function); + add32(TrustedImm32(6 * sizeof(void*)), StackPointerRegister); + } template void generateFunctionCall(FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { -- cgit v1.2.3 From 8f87666256dea2507a54c05bb8472f0fc478d10f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 7 Oct 2012 22:02:06 +0200 Subject: Implement construct property/value --- qv4isel_masm.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 9c9984f45d..43f5b6d3da 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -166,12 +166,24 @@ void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp * void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) { - Q_UNIMPLEMENTED(); + IR::Member *member = call->base->asMember(); + assert(member != 0); + assert(member->base->asTemp() != 0); + + int argc = prepareVariableArguments(call->args); + IR::Temp* thisObject = 0; + generateFunctionCall(__qmljs_construct_property, ContextRegister, result, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); + checkExceptions(); } void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) { - Q_UNIMPLEMENTED(); + IR::Temp *baseTemp = call->base->asTemp(); + assert(baseTemp != 0); + + int argc = prepareVariableArguments(call->args); + generateFunctionCall(__qmljs_construct_value, ContextRegister, result, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc)); + checkExceptions(); } void InstructionSelection::checkExceptions() -- cgit v1.2.3 From 33d90f1ed0f80c937bcf225be153a8ab0ec7b7ae Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 7 Oct 2012 22:02:41 +0200 Subject: implement visitExp --- qv4isel_masm.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 43f5b6d3da..8af9b9fb5f 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -195,7 +195,18 @@ void InstructionSelection::checkExceptions() void InstructionSelection::visitExp(IR::Exp *s) { - Q_UNIMPLEMENTED(); + if (IR::Call *c = s->expr->asCall()) { + if (c->base->asName()) { + callActivationProperty(c, 0); + return; + } else if (c->base->asTemp()) { + callValue(c, 0); + return; + } else if (c->base->asMember()) { + callProperty(c, 0); + return; + } + } assert(!"TODO"); } -- cgit v1.2.3 From 9047b986ead3651bebc896b7258f387f3e46d34e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 7 Oct 2012 22:10:27 +0200 Subject: Implement missing movs --- qv4isel_masm.cpp | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 5 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 8af9b9fb5f..f075dcb161 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -287,9 +287,18 @@ void InstructionSelection::visitMove(IR::Move *s) return; } } else if (IR::Member *m = s->source->asMember()) { - Q_UNIMPLEMENTED(); + //__qmljs_get_property(ctx, result, object, name); + if (IR::Temp *base = m->base->asTemp()) { + generateFunctionCall(__qmljs_get_property, ContextRegister, t, base, identifier(*m->name)); + checkExceptions(); + return; + } + assert(!"wip"); + return; } else if (IR::Subscript *ss = s->source->asSubscript()) { - Q_UNIMPLEMENTED(); + generateFunctionCall(__qmljs_get_element, ContextRegister, t, ss->base->asTemp(), ss->index->asTemp()); + checkExceptions(); + return; } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { void (*op)(Context *, Value *, const Value *) = 0; @@ -363,9 +372,23 @@ void InstructionSelection::visitMove(IR::Move *s) } } } else if (IR::Member *m = s->target->asMember()) { - Q_UNIMPLEMENTED(); + if (IR::Temp *base = m->base->asTemp()) { + if (IR::Temp *t = s->source->asTemp()) { + generateFunctionCall(__qmljs_set_property, ContextRegister, base, identifier(*m->name), t); + checkExceptions(); + return; + } else { + Q_UNREACHABLE(); + } + } } else if (IR::Subscript *ss = s->target->asSubscript()) { - Q_UNIMPLEMENTED(); + if (IR::Temp *t2 = s->source->asTemp()) { + generateFunctionCall(__qmljs_set_element, ss->base->asTemp(), ss->index->asTemp(), t2); + checkExceptions(); + return; + } else { + Q_UNIMPLEMENTED(); + } } } else { // inplace assignment, e.g. x += 1, ++x, ... @@ -415,8 +438,55 @@ void InstructionSelection::visitMove(IR::Move *s) checkExceptions(); return; } + } else if (IR::Subscript *ss = s->target->asSubscript()) { + if (IR::Temp *t = s->source->asTemp()) { + void (*op)(Context *, Value *, Value *, Value *) = 0; + switch (s->op) { + case IR::OpBitAnd: op = __qmljs_inplace_bit_and_element; break; + case IR::OpBitOr: op = __qmljs_inplace_bit_or_element; break; + case IR::OpBitXor: op = __qmljs_inplace_bit_xor_element; break; + case IR::OpAdd: op = __qmljs_inplace_add_element; break; + case IR::OpSub: op = __qmljs_inplace_sub_element; break; + case IR::OpMul: op = __qmljs_inplace_mul_element; break; + case IR::OpDiv: op = __qmljs_inplace_div_element; break; + case IR::OpMod: op = __qmljs_inplace_mod_element; break; + case IR::OpLShift: op = __qmljs_inplace_shl_element; break; + case IR::OpRShift: op = __qmljs_inplace_shr_element; break; + case IR::OpURShift: op = __qmljs_inplace_ushr_element; break; + default: + Q_UNREACHABLE(); + break; + } + + generateFunctionCall(op, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t); + checkExceptions(); + return; + } + } else if (IR::Member *m = s->target->asMember()) { + if (IR::Temp *t = s->source->asTemp()) { + void (*op)(Context *, Value *, String *, Value *) = 0; + switch (s->op) { + case IR::OpBitAnd: op = __qmljs_inplace_bit_and_member; break; + case IR::OpBitOr: op = __qmljs_inplace_bit_or_member; break; + case IR::OpBitXor: op = __qmljs_inplace_bit_xor_member; break; + case IR::OpAdd: op = __qmljs_inplace_add_member; break; + case IR::OpSub: op = __qmljs_inplace_sub_member; break; + case IR::OpMul: op = __qmljs_inplace_mul_member; break; + case IR::OpDiv: op = __qmljs_inplace_div_member; break; + case IR::OpMod: op = __qmljs_inplace_mod_member; break; + case IR::OpLShift: op = __qmljs_inplace_shl_member; break; + case IR::OpRShift: op = __qmljs_inplace_shr_member; break; + case IR::OpURShift: op = __qmljs_inplace_ushr_member; break; + default: + Q_UNREACHABLE(); + break; + } + + generateFunctionCall(op, ContextRegister, m->base->asTemp(), identifier(*m->name), t); + checkExceptions(); + return; + } } - Q_UNIMPLEMENTED(); } Q_UNIMPLEMENTED(); s->dump(qout, IR::Stmt::MIR); -- cgit v1.2.3 From 183d1c4e39aca586fb58cc7effb25d1281459254 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 7 Oct 2012 22:14:01 +0200 Subject: Add missing return --- qv4isel_masm.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index f075dcb161..2fcdf0ddca 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -272,6 +272,7 @@ void InstructionSelection::visitMove(IR::Move *s) return; } else if (IR::String *str = s->source->asString()) { generateFunctionCall(__qmljs_init_string, t, _engine->newString(*str->value)); + return; } else if (IR::Closure *clos = s->source->asClosure()) { generateFunctionCall(__qmljs_init_closure, ContextRegister, t, TrustedImmPtr(clos->value)); return; -- cgit v1.2.3 From db7ceaa25ccc98edb63fe832075ff16cb6bac86d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 8 Oct 2012 10:25:21 +0200 Subject: Fix non-release builds --- masm/masm.pri | 1 - masm/stubs/ExecutableAllocator.h | 2 ++ masm/stubs/Options.h | 12 ++++++++++++ masm/stubs/WTFStubs.cpp | 27 ++++++++++++++++++++++++++- qv4ir_p.h | 1 + 5 files changed, 41 insertions(+), 2 deletions(-) diff --git a/masm/masm.pri b/masm/masm.pri index 502df9132c..b349172d8d 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -7,7 +7,6 @@ SOURCES += $$PWD/assembler/MacroAssemblerSH4.cpp SOURCES += $$PWD/assembler/LinkBuffer.cpp SOURCES += $$PWD/stubs/WTFStubs.cpp -DEFINES += NDEBUG DEFINES += WTF_EXPORT_PRIVATE="" INCLUDEPATH += $$PWD/jit diff --git a/masm/stubs/ExecutableAllocator.h b/masm/stubs/ExecutableAllocator.h index 72605e0411..6928b3b87d 100644 --- a/masm/stubs/ExecutableAllocator.h +++ b/masm/stubs/ExecutableAllocator.h @@ -21,6 +21,8 @@ struct ExecutableMemoryHandle : public RefCounted { free(m_data); } + inline bool isManaged() const { return true; } + void* start() { return m_data; } int sizeInBytes() { return m_size; } diff --git a/masm/stubs/Options.h b/masm/stubs/Options.h index 8b13789179..3d40363531 100644 --- a/masm/stubs/Options.h +++ b/masm/stubs/Options.h @@ -1 +1,13 @@ +#ifndef OPTIONS_H +#define OPTIONS_H +namespace JSC { + +struct Options { + static bool showDisassembly() { return true; } + static bool showDFGDisassembly() { return true; } +}; + +} + +#endif // MASM_STUBS/OPTIONS_H diff --git a/masm/stubs/WTFStubs.cpp b/masm/stubs/WTFStubs.cpp index c649e7f025..93cfbf2095 100644 --- a/masm/stubs/WTFStubs.cpp +++ b/masm/stubs/WTFStubs.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -16,7 +17,7 @@ void fastFree(void* ptr) free(ptr); } -int cryptographicallyRandomNumber() +uint32_t cryptographicallyRandomNumber() { return 0; } @@ -45,3 +46,27 @@ void dataLogString(const char* str) } } + +extern "C" { + +void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion) +{ +} + +void WTFReportBacktrace() +{ +} + +void WTFInvokeCrashHook() +{ +} + +} + + +#if ENABLE(ASSEMBLER) && CPU(X86) && !OS(MAC_OS_X) +#include + +JSC::MacroAssemblerX86Common::SSE2CheckState JSC::MacroAssemblerX86Common::s_sse2CheckState = JSC::MacroAssemblerX86Common::NotCheckedSSE2; +#endif + diff --git a/qv4ir_p.h b/qv4ir_p.h index 53c75f797a..120f0dfe3f 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -59,6 +59,7 @@ #include #include +#include #include QT_BEGIN_HEADER -- cgit v1.2.3 From 7a6c3c2b9b4ebfc33e7acec052b720f5926aa46b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 8 Oct 2012 20:21:40 +0200 Subject: Implement executalble memory using mmap --- masm/stubs/ExecutableAllocator.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/masm/stubs/ExecutableAllocator.h b/masm/stubs/ExecutableAllocator.h index 6928b3b87d..6f78fe1f3c 100644 --- a/masm/stubs/ExecutableAllocator.h +++ b/masm/stubs/ExecutableAllocator.h @@ -14,11 +14,13 @@ struct ExecutableMemoryHandle : public RefCounted { ExecutableMemoryHandle(int size) : m_size(size) { - m_data = malloc(m_size); + static size_t pageSize = sysconf(_SC_PAGESIZE); + m_size = (m_size + pageSize - 1) & ~(pageSize - 1); + m_data = mmap(0, m_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); } ~ExecutableMemoryHandle() { - free(m_data); + munmap(m_data, m_size); } inline bool isManaged() const { return true; } @@ -42,7 +44,7 @@ struct ExecutableAllocator { static void makeExecutable(void* addr, int size) { - size_t pageSize = sysconf(_SC_PAGESIZE); + static size_t pageSize = sysconf(_SC_PAGESIZE); size_t iaddr = reinterpret_cast(addr); size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); int mode = PROT_READ | PROT_WRITE | PROT_EXEC; -- cgit v1.2.3 From af2ff3464fffe9d296ee961414bf8a6dafed5ace Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 8 Oct 2012 20:22:31 +0200 Subject: Use posix_memalign to ensure assert(!(code & 15)) --- main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index fa03e08179..623338f939 100644 --- a/main.cpp +++ b/main.cpp @@ -197,7 +197,9 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS IR::Function *globalCode = 0; const size_t codeSize = 400 * getpagesize(); - uchar *code = (uchar *) malloc(codeSize); + uchar *code = 0; + posix_memalign((void**)&code, 16, codeSize); + assert(code); assert(! (size_t(code) & 15)); static bool useMoth = !qgetenv("USE_MOTH").isNull(); -- cgit v1.2.3 From ea408f812e26109065c3d932732bb67dec1d0f28 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 8 Oct 2012 21:58:24 +0200 Subject: Fix array subscript write: add missing context pointer argument to qmljs_set_element call --- qv4isel_masm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 2fcdf0ddca..66d9da69e2 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -384,7 +384,7 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Subscript *ss = s->target->asSubscript()) { if (IR::Temp *t2 = s->source->asTemp()) { - generateFunctionCall(__qmljs_set_element, ss->base->asTemp(), ss->index->asTemp(), t2); + generateFunctionCall(__qmljs_set_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t2); checkExceptions(); return; } else { -- cgit v1.2.3 From b8b706e089af8fbcd4a9f7ed84c545d15af4540b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 9 Oct 2012 07:41:36 +0200 Subject: Remember linked function names in prep for future debug help --- qv4isel_masm_p.h | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 8a4af26b22..20c3647eed 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -112,10 +112,11 @@ private: typedef JSC::FunctionPtr FunctionPtr; - void callAbsolute(FunctionPtr function) { + void callAbsolute(const char* functionName, FunctionPtr function) { CallToLink ctl; ctl.call = call(); ctl.externalFunction = function; + ctl.functionName = functionName; _callsToLink.append(ctl); } @@ -149,8 +150,14 @@ private: push(TrustedImmPtr(name)); } + #define isel_stringIfyx(s) #s + #define isel_stringIfy(s) isel_stringIfyx(s) + + #define generateFunctionCall(function, ...) \ + generateFunctionCallImp(isel_stringIfy(function), function, __VA_ARGS__) + template - void generateFunctionCall(FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) { // Reverse order push(arg6); @@ -159,11 +166,11 @@ private: push(arg3); push(arg2); push(arg1); - callAbsolute(function); + callAbsolute(functionName, function); add32(TrustedImm32(6 * sizeof(void*)), StackPointerRegister); } template - void generateFunctionCall(FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { // Reverse order push(arg5); @@ -171,42 +178,42 @@ private: push(arg3); push(arg2); push(arg1); - callAbsolute(function); + callAbsolute(functionName, function); add32(TrustedImm32(5 * sizeof(void*)), StackPointerRegister); } template - void generateFunctionCall(FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { // Reverse order push(arg4); push(arg3); push(arg2); push(arg1); - callAbsolute(function); + callAbsolute(functionName, function); add32(TrustedImm32(4 * sizeof(void*)), StackPointerRegister); } template - void generateFunctionCall(FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) { // Reverse order push(arg3); push(arg2); push(arg1); - callAbsolute(function); + callAbsolute(functionName, function); add32(TrustedImm32(3 * sizeof(void*)), StackPointerRegister); } template - void generateFunctionCall(FunctionPtr function, Arg1 arg1, Arg2 arg2) + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) { // Reverse order push(arg2); push(arg1); - callAbsolute(function); + callAbsolute(functionName, function); add32(TrustedImm32(2 * sizeof(void*)), StackPointerRegister); } @@ -220,6 +227,7 @@ private: struct CallToLink { Call call; FunctionPtr externalFunction; + const char* functionName; }; template -- cgit v1.2.3 From a084733c143be17bec5e66ea91afacb814804305 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 9 Oct 2012 08:17:47 +0200 Subject: First shot at more readable call instructions in output --- masm/masm.pri | 2 ++ masm/stubs/WTFStubs.cpp | 9 ++++++++- masm/stubs/WTFStubs.h | 10 ++++++++++ qv4isel_masm.cpp | 30 +++++++++++++++++++++++++++++- 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 masm/stubs/WTFStubs.h diff --git a/masm/masm.pri b/masm/masm.pri index b349172d8d..457c3810da 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -5,7 +5,9 @@ SOURCES += $$PWD/assembler/ARMv7Assembler.cpp SOURCES += $$PWD/assembler/MacroAssemblerARM.cpp SOURCES += $$PWD/assembler/MacroAssemblerSH4.cpp SOURCES += $$PWD/assembler/LinkBuffer.cpp + SOURCES += $$PWD/stubs/WTFStubs.cpp +HEADERS += $$PWD/stubs/WTFStubs.h DEFINES += WTF_EXPORT_PRIVATE="" diff --git a/masm/stubs/WTFStubs.cpp b/masm/stubs/WTFStubs.cpp index 93cfbf2095..072bfdb84f 100644 --- a/masm/stubs/WTFStubs.cpp +++ b/masm/stubs/WTFStubs.cpp @@ -22,9 +22,16 @@ uint32_t cryptographicallyRandomNumber() return 0; } +static FILE* s_dataFile = stdout; + +void setDataFile(FILE* f) +{ + s_dataFile = f; +} + FILE* dataFile() { - return stdout; + return s_dataFile; } void dataLogV(const char* format, va_list args) diff --git a/masm/stubs/WTFStubs.h b/masm/stubs/WTFStubs.h new file mode 100644 index 0000000000..d1587dd53d --- /dev/null +++ b/masm/stubs/WTFStubs.h @@ -0,0 +1,10 @@ +#ifndef WTFSTUBS_H +#define WTFSTUBS_H + +namespace WTF { + +void setDataFile(FILE* f); + +} + +#endif // WTFSTUBS_H diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 66d9da69e2..b83cac52bf 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -4,6 +4,7 @@ #include "qmljs_objects.h" #include +#include #include #include @@ -21,6 +22,18 @@ namespace { QTextStream qout(stdout, QIODevice::WriteOnly); } +static void printDisassmbleOutputWithCalls(const char* output, const QHash& functions) +{ + QByteArray processedOutput(output); + for (QHash::ConstIterator it = functions.begin(), end = functions.end(); + it != end; ++it) { + QByteArray ptrString = QByteArray::number(qlonglong(it.key()), 16); + ptrString.prepend("0x"); + processedOutput = processedOutput.replace(ptrString, it.value()); + } + fprintf(stdout, "%s\n", processedOutput.constData()); +} + InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *buffer) : _engine(engine) , _module(module) @@ -72,9 +85,24 @@ void InstructionSelection::operator()(IR::Function *function) JSC::JSGlobalData dummy; JSC::LinkBuffer linkBuffer(dummy, this, 0); - foreach (CallToLink ctl, _callsToLink) + QHash functions; + foreach (CallToLink ctl, _callsToLink) { linkBuffer.link(ctl.call, ctl.externalFunction); + functions[ctl.externalFunction.value()] = ctl.functionName; + } + + char* disasmOutput = 0; + size_t disasmLength = 0; + FILE* disasmStream = open_memstream(&disasmOutput, &disasmLength); + WTF::setDataFile(disasmStream); + _function->codeRef = linkBuffer.finalizeCodeWithDisassembly("operator()(IR::Function*)"); + + WTF::setDataFile(stdout); + fclose(disasmStream); + printDisassmbleOutputWithCalls(disasmOutput, functions); + free(disasmOutput); + _function->code = (void (*)(VM::Context *, const uchar *)) _function->codeRef.code().executableAddress(); qSwap(_function, function); -- cgit v1.2.3 From 115d570a86089deae0f34a982930fef3e7860637 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 9 Oct 2012 09:06:12 +0200 Subject: More debug info --- qv4isel_masm.cpp | 8 ++++---- qv4isel_masm_p.h | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index b83cac52bf..7ac0080397 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -624,20 +624,20 @@ int InstructionSelection::prepareVariableArguments(IR::ExprList* args) return argc; } -void InstructionSelection::callRuntimeMethod(ActivationMethod method, IR::Temp *result, IR::Expr *base, IR::ExprList *args) +void InstructionSelection::callRuntimeMethodImp(const char* name, ActivationMethod method, IR::Temp *result, IR::Expr *base, IR::ExprList *args) { IR::Name *baseName = base->asName(); assert(baseName != 0); int argc = prepareVariableArguments(args); - generateFunctionCall(method, ContextRegister, result, identifier(*baseName->id), baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCallImp(name, method, ContextRegister, result, identifier(*baseName->id), baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } -void InstructionSelection::callRuntimeMethod(BuiltinMethod method, IR::Temp *result, IR::ExprList *args) +void InstructionSelection::callRuntimeMethodImp(const char* name, BuiltinMethod method, IR::Temp *result, IR::ExprList *args) { int argc = prepareVariableArguments(args); - generateFunctionCall(method, ContextRegister, result, baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCallImp(name, method, ContextRegister, result, baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 20c3647eed..1b5b7b3e11 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -221,8 +221,10 @@ private: typedef void (*ActivationMethod)(VM::Context *, VM::Value *result, VM::String *name, VM::Value *args, int argc); typedef void (*BuiltinMethod)(VM::Context *, VM::Value *result, VM::Value *args, int argc); - void callRuntimeMethod(ActivationMethod method, IR::Temp *result, IR::Expr *base, IR::ExprList *args); - void callRuntimeMethod(BuiltinMethod method, IR::Temp *result, IR::ExprList *args); + void callRuntimeMethodImp(const char* name, ActivationMethod method, IR::Temp *result, IR::Expr *base, IR::ExprList *args); + void callRuntimeMethodImp(const char* name, BuiltinMethod method, IR::Temp *result, IR::ExprList *args); +#define callRuntimeMethod(function, ...) \ + callRuntimeMethodImp(isel_stringIfy(function), function, __VA_ARGS__) struct CallToLink { Call call; -- cgit v1.2.3 From 1f28ba475e8ef01c7a7d58ad156a17e049b1e960 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 9 Oct 2012 09:59:02 +0200 Subject: Save and restore context register before calls --- qv4isel_masm.cpp | 9 ++++++--- qv4isel_masm_p.h | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 7ac0080397..28886d0b87 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -441,7 +441,8 @@ void InstructionSelection::visitMove(IR::Move *s) break; } - generateFunctionCall(op, ContextRegister, t, t, t2); + if (op) + generateFunctionCall(op, ContextRegister, t, t, t2); return; } } else if (IR::Name *n = s->target->asName()) { @@ -487,8 +488,10 @@ void InstructionSelection::visitMove(IR::Move *s) break; } - generateFunctionCall(op, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t); - checkExceptions(); + if (op) { + generateFunctionCall(op, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t); + checkExceptions(); + } return; } } else if (IR::Member *m = s->target->asMember()) { diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 1b5b7b3e11..c309d3925b 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -150,6 +150,16 @@ private: push(TrustedImmPtr(name)); } + void callFunctionPrologue() + { + // Callee might clobber it :( + push(ContextRegister); + } + void callFunctionEpilogue() + { + pop(ContextRegister); + } + #define isel_stringIfyx(s) #s #define isel_stringIfy(s) isel_stringIfyx(s) @@ -159,6 +169,7 @@ private: template void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) { + callFunctionPrologue(); // Reverse order push(arg6); push(arg5); @@ -168,10 +179,12 @@ private: push(arg1); callAbsolute(functionName, function); add32(TrustedImm32(6 * sizeof(void*)), StackPointerRegister); + callFunctionEpilogue(); } template void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { + callFunctionPrologue(); // Reverse order push(arg5); push(arg4); @@ -180,12 +193,14 @@ private: push(arg1); callAbsolute(functionName, function); add32(TrustedImm32(5 * sizeof(void*)), StackPointerRegister); + callFunctionEpilogue(); } template void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { + callFunctionPrologue(); // Reverse order push(arg4); push(arg3); @@ -193,28 +208,33 @@ private: push(arg1); callAbsolute(functionName, function); add32(TrustedImm32(4 * sizeof(void*)), StackPointerRegister); + callFunctionEpilogue(); } template void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) { + callFunctionPrologue(); // Reverse order push(arg3); push(arg2); push(arg1); callAbsolute(functionName, function); add32(TrustedImm32(3 * sizeof(void*)), StackPointerRegister); + callFunctionEpilogue(); } template void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) { + callFunctionPrologue(); // Reverse order push(arg2); push(arg1); callAbsolute(functionName, function); add32(TrustedImm32(2 * sizeof(void*)), StackPointerRegister); + callFunctionEpilogue(); } int prepareVariableArguments(IR::ExprList* args); -- cgit v1.2.3 From d4d1b00f8b9a685141ba18282e6bd906e9a9aee0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 9 Oct 2012 10:05:27 +0200 Subject: Centralize value copying --- qv4isel_masm.cpp | 11 ++++++++--- qv4isel_masm_p.h | 3 +++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 28886d0b87..04b904dc2f 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -296,7 +296,7 @@ void InstructionSelection::visitMove(IR::Move *s) } return; } else if (IR::Temp *t2 = s->source->asTemp()) { - generateFunctionCall(__qmljs_copy, t, t2); + copyValue(t, t2); return; } else if (IR::String *str = s->source->asString()) { generateFunctionCall(__qmljs_init_string, t, _engine->newString(*str->value)); @@ -603,7 +603,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { if (IR::Temp *t = s->expr->asTemp()) { - generateFunctionCall(__qmljs_copy, Pointer(ContextRegister, offsetof(Context, result)), t); + copyValue(Pointer(ContextRegister, offsetof(Context, result)), t); return; } Q_UNIMPLEMENTED(); @@ -621,7 +621,7 @@ int InstructionSelection::prepareVariableArguments(IR::ExprList* args) for (IR::ExprList *it = args; it; it = it->next, ++i) { IR::Temp *arg = it->expr->asTemp(); assert(arg != 0); - generateFunctionCall(__qmljs_copy, argumentAddressForCall(i), arg); + copyValue(argumentAddressForCall(i), arg); } return argc; @@ -644,3 +644,8 @@ void InstructionSelection::callRuntimeMethodImp(const char* name, BuiltinMethod checkExceptions(); } +template +void InstructionSelection::copyValue(Result result, Source source) +{ + generateFunctionCall(__qmljs_copy, result, source); +} diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index c309d3925b..910040e974 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -246,6 +246,9 @@ private: #define callRuntimeMethod(function, ...) \ callRuntimeMethodImp(isel_stringIfy(function), function, __VA_ARGS__) + template + void copyValue(Result result, Source source); + struct CallToLink { Call call; FunctionPtr externalFunction; -- cgit v1.2.3 From aecc6ad8b78226ed54ca362dc357f37db78c4025 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 9 Oct 2012 10:28:59 +0200 Subject: Fix compilation on amd64 (not working yet, just building) --- masm/assembler/MacroAssembler.h | 2 +- masm/config.h | 1 + masm/disassembler/udis86/ud_opcode.pyc | Bin 7170 -> 7230 bytes masm/disassembler/udis86/ud_optable.pyc | Bin 2793 -> 2814 bytes masm/stubs/WTFStubs.cpp | 2 +- qv4isel_masm_p.h | 11 +++++++++++ 6 files changed, 14 insertions(+), 2 deletions(-) diff --git a/masm/assembler/MacroAssembler.h b/masm/assembler/MacroAssembler.h index 1a9af2989b..43c9b470f7 100644 --- a/masm/assembler/MacroAssembler.h +++ b/masm/assembler/MacroAssembler.h @@ -578,7 +578,7 @@ public: default: { if (value <= 0xff) return false; -#if CPU(X86_64) +#if CPU(X86_64) && 0 JSValue jsValue = JSValue::decode(reinterpret_cast(value)); if (jsValue.isInt32()) return shouldBlind(Imm32(jsValue.asInt32())); diff --git a/masm/config.h b/masm/config.h index d462525611..7e0779fc45 100644 --- a/masm/config.h +++ b/masm/config.h @@ -6,5 +6,6 @@ #include #endif #include +#include #endif // MASM_CONFIG_H diff --git a/masm/disassembler/udis86/ud_opcode.pyc b/masm/disassembler/udis86/ud_opcode.pyc index cade0c2172..75a1d62f26 100644 Binary files a/masm/disassembler/udis86/ud_opcode.pyc and b/masm/disassembler/udis86/ud_opcode.pyc differ diff --git a/masm/disassembler/udis86/ud_optable.pyc b/masm/disassembler/udis86/ud_optable.pyc index 8f36a9c0b1..76896dc409 100644 Binary files a/masm/disassembler/udis86/ud_optable.pyc and b/masm/disassembler/udis86/ud_optable.pyc differ diff --git a/masm/stubs/WTFStubs.cpp b/masm/stubs/WTFStubs.cpp index 072bfdb84f..7e16bbc4d8 100644 --- a/masm/stubs/WTFStubs.cpp +++ b/masm/stubs/WTFStubs.cpp @@ -7,7 +7,7 @@ namespace WTF { -void* fastMalloc(unsigned int size) +void* fastMalloc(size_t size) { return malloc(size); } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 910040e974..a177f9ce33 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -33,6 +33,17 @@ protected: static const RegisterID Gpr3 = JSC::X86Registers::esi; static const RegisterID CalleeSavedGpr = Gpr3; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; +#elif CPU(X86_64) + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID ContextRegister = JSC::X86Registers::esi; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID Gpr0 = JSC::X86Registers::eax; + static const RegisterID Gpr1 = JSC::X86Registers::ecx; + static const RegisterID Gpr2 = JSC::X86Registers::edx; + static const RegisterID Gpr3 = JSC::X86Registers::esi; + static const RegisterID CalleeSavedGpr = Gpr3; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; #else #error Argh. #endif -- cgit v1.2.3 From a7c59f7df666dfa571020cb64a3b5db1fd5a282c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 9 Oct 2012 10:31:54 +0200 Subject: Remove generated code --- masm/disassembler/udis86/ud_opcode.pyc | Bin 7230 -> 0 bytes masm/disassembler/udis86/ud_optable.pyc | Bin 2814 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 masm/disassembler/udis86/ud_opcode.pyc delete mode 100644 masm/disassembler/udis86/ud_optable.pyc diff --git a/masm/disassembler/udis86/ud_opcode.pyc b/masm/disassembler/udis86/ud_opcode.pyc deleted file mode 100644 index 75a1d62f26..0000000000 Binary files a/masm/disassembler/udis86/ud_opcode.pyc and /dev/null differ diff --git a/masm/disassembler/udis86/ud_optable.pyc b/masm/disassembler/udis86/ud_optable.pyc deleted file mode 100644 index 76896dc409..0000000000 Binary files a/masm/disassembler/udis86/ud_optable.pyc and /dev/null differ -- cgit v1.2.3 From 91fe97626d20328d643ef83de06a94b48505010f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 10 Oct 2012 10:27:04 +0200 Subject: Fix rethrow --- qv4isel_masm.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 04b904dc2f..7fffae78f1 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -153,10 +153,12 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu case IR::Name::builtin_throw: callRuntimeMethod(__qmljs_builtin_throw, result, call->args); break; - case IR::Name::builtin_rethrow: - callRuntimeMethod(__qmljs_builtin_rethrow, result, call->args); + case IR::Name::builtin_rethrow: { + int argc = prepareVariableArguments(call->args); + generateFunctionCall(__qmljs_builtin_rethrow, ContextRegister, result, baseAddressForCallArguments(), TrustedImm32(argc)); return; // we need to return to avoid checking the exceptions } + } } } -- cgit v1.2.3 From bf23818b1b06ed0d917891d22e0b32a4b38e81eb Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 10 Oct 2012 11:04:53 +0200 Subject: Rudimentary amd64 port --- qv4isel_masm_p.h | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 133 insertions(+), 7 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index a177f9ce33..45102494e2 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -36,14 +36,21 @@ protected: #elif CPU(X86_64) static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; static const RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const RegisterID ContextRegister = JSC::X86Registers::esi; + static const RegisterID ContextRegister = JSC::X86Registers::r14; static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; static const RegisterID Gpr0 = JSC::X86Registers::eax; static const RegisterID Gpr1 = JSC::X86Registers::ecx; static const RegisterID Gpr2 = JSC::X86Registers::edx; static const RegisterID Gpr3 = JSC::X86Registers::esi; - static const RegisterID CalleeSavedGpr = Gpr3; + static const RegisterID CalleeSavedGpr = ContextRegister; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + + static const RegisterID RegisterArgument1 = JSC::X86Registers::edi; + static const RegisterID RegisterArgument2 = JSC::X86Registers::esi; + static const RegisterID RegisterArgument3 = JSC::X86Registers::edx; + static const RegisterID RegisterArgument4 = JSC::X86Registers::ecx; + static const RegisterID RegisterArgument5 = JSC::X86Registers::r8; + static const RegisterID RegisterArgument6 = JSC::X86Registers::r9; #else #error Argh. #endif @@ -66,25 +73,45 @@ protected: { push(StackFrameRegister); move(StackPointerRegister, StackFrameRegister); - sub32(TrustedImm32(locals), StackPointerRegister); + // #### + subPtr(TrustedImmPtr((void*)locals), StackPointerRegister); push(CalleeSavedGpr); } void leaveStandardStackFrame(int locals) { pop(CalleeSavedGpr); - add32(TrustedImm32(locals), StackPointerRegister); + // #### + addPtr(TrustedImmPtr((void*)locals), StackPointerRegister); pop(StackFrameRegister); } #else #error Argh. #endif +#if CPU(X86) Address addressForArgument(int index) const { - // ### CPU specific: on x86/x86_64 we need +2 to jump over ebp and the return address - // on the stack. Maybe same on arm if we save lr on stack on enter. return Address(StackFrameRegister, (index + 2) * sizeof(void*)); } +#else + Address addressForArgument(int index) const + { + static RegisterID args[6] = { + RegisterArgument1, + RegisterArgument2, + RegisterArgument3, + RegisterArgument4, + RegisterArgument5, + RegisterArgument6 + }; + if (index < 6) + return Address(args[index], 0); + else { + Q_UNREACHABLE(); + assert(!"TODO"); + } + } +#endif // Some run-time functions take (Value* args, int argc). This function is for populating // the args. @@ -135,7 +162,7 @@ private: void push(const Pointer& ptr) { - add32(TrustedImm32(ptr.offset), ptr.base, Gpr0); + addPtr(TrustedImm32(ptr.offset), ptr.base, Gpr0); push(Gpr0); } @@ -177,6 +204,7 @@ private: #define generateFunctionCall(function, ...) \ generateFunctionCallImp(isel_stringIfy(function), function, __VA_ARGS__) +#if CPU(X86) template void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) { @@ -247,6 +275,104 @@ private: add32(TrustedImm32(2 * sizeof(void*)), StackPointerRegister); callFunctionEpilogue(); } +#elif CPU(X86_64) + void loadArgument(RegisterID source, RegisterID dest) + { + move(source, dest); + } + + void loadArgument(const Pointer& ptr, RegisterID dest) + { + addPtr(TrustedImm32(ptr.offset), ptr.base, dest); + } + + void loadArgument(IR::Temp* temp, RegisterID dest) + { + if (temp) { + Pointer addr = loadTempAddress(dest, temp); + loadArgument(addr, dest); + } else { + xor32(dest, dest); + } + } + + void loadArgument(TrustedImmPtr ptr, RegisterID dest) + { + move(TrustedImmPtr(ptr), dest); + } + + void loadArgument(VM::String* string, RegisterID dest) + { + loadArgument(TrustedImmPtr(string), dest); + } + + void loadArgument(TrustedImm32 imm32, RegisterID dest) + { + move(imm32, dest); + } + + + template + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) + { + callFunctionPrologue(); + loadArgument(arg1, RegisterArgument1); + loadArgument(arg2, RegisterArgument2); + loadArgument(arg3, RegisterArgument3); + loadArgument(arg4, RegisterArgument4); + loadArgument(arg5, RegisterArgument5); + loadArgument(arg6, RegisterArgument6); + callAbsolute(functionName, function); + callFunctionEpilogue(); + } + template + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + { + callFunctionPrologue(); + loadArgument(arg1, RegisterArgument1); + loadArgument(arg2, RegisterArgument2); + loadArgument(arg3, RegisterArgument3); + loadArgument(arg4, RegisterArgument4); + loadArgument(arg5, RegisterArgument5); + callAbsolute(functionName, function); + callFunctionEpilogue(); + } + + + template + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + callFunctionPrologue(); + loadArgument(arg1, RegisterArgument1); + loadArgument(arg2, RegisterArgument2); + loadArgument(arg3, RegisterArgument3); + loadArgument(arg4, RegisterArgument4); + callAbsolute(functionName, function); + callFunctionEpilogue(); + } + + + template + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + callFunctionPrologue(); + loadArgument(arg1, RegisterArgument1); + loadArgument(arg2, RegisterArgument2); + loadArgument(arg3, RegisterArgument3); + callAbsolute(functionName, function); + callFunctionEpilogue(); + } + + template + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) + { + callFunctionPrologue(); + loadArgument(arg1, RegisterArgument1); + loadArgument(arg2, RegisterArgument2); + callAbsolute(functionName, function); + callFunctionEpilogue(); + } +#endif int prepareVariableArguments(IR::ExprList* args); -- cgit v1.2.3 From ce08033a746b0f8ab2e8b2bbd1dff3eb0f0cf5a6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 10 Oct 2012 11:42:38 +0200 Subject: More 64-bit fixes --- qv4isel_masm_p.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 45102494e2..833ceb11d8 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -172,7 +172,7 @@ private: Pointer addr = loadTempAddress(Gpr0, temp); push(addr); } else { - xor32(Gpr0, Gpr0); + xorPtr(Gpr0, Gpr0); push(Gpr0); } } @@ -292,7 +292,7 @@ private: Pointer addr = loadTempAddress(dest, temp); loadArgument(addr, dest); } else { - xor32(dest, dest); + xorPtr(dest, dest); } } @@ -308,6 +308,7 @@ private: void loadArgument(TrustedImm32 imm32, RegisterID dest) { + xorPtr(dest, dest); move(imm32, dest); } -- cgit v1.2.3 From dcfb63f566de0dbc6ab22073042a83e6afa81ebb Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 10 Oct 2012 13:10:07 +0200 Subject: Make it really work for 64 bit. crypto.js passes now :) --- qv4isel_masm.cpp | 6 ++++++ qv4isel_masm_p.h | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 7fffae78f1..cbf6f7053e 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -58,7 +58,13 @@ void InstructionSelection::operator()(IR::Function *function) enterStandardStackFrame(locals); push(ContextRegister); +#if CPU(X86) loadPtr(addressForArgument(0), ContextRegister); +#elif CPU(X86_64) + move(RegisterArgument1, ContextRegister); +#else + assert(!"TODO"); +#endif foreach (IR::BasicBlock *block, _function->basicBlocks) { _block = block; diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 833ceb11d8..7ac8df6056 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -69,7 +69,7 @@ protected: }; #if CPU(X86) || CPU(X86_64) - void enterStandardStackFrame(int locals) + void enterStandardStackFrame(quintptr locals) { push(StackFrameRegister); move(StackPointerRegister, StackFrameRegister); @@ -77,7 +77,7 @@ protected: subPtr(TrustedImmPtr((void*)locals), StackPointerRegister); push(CalleeSavedGpr); } - void leaveStandardStackFrame(int locals) + void leaveStandardStackFrame(quintptr locals) { pop(CalleeSavedGpr); // #### -- cgit v1.2.3 From 9b9fd3acb98aacc847040cb731424fd7552a6231 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 10 Oct 2012 13:19:17 +0200 Subject: Remove old assembler --- asm/Makefile.am | 32 - asm/amd64-codegen.h | 1831 ---------------------------------- asm/arm-codegen.c | 193 ---- asm/arm-codegen.h | 1103 --------------------- asm/arm-dis.c | 509 ---------- asm/arm-dis.h | 41 - asm/arm-fpa-codegen.h | 198 ---- asm/arm-vfp-codegen.h | 235 ----- asm/arm-wmmx.h | 177 ---- asm/arm_dpimacros.h | 1603 ------------------------------ asm/cmp_macros.th | 56 -- asm/dpi_macros.th | 112 --- asm/dpiops.sh | 30 - asm/fpa_macros.th | 15 - asm/fpam_macros.th | 14 - asm/fpaops.sh | 24 - asm/mov_macros.th | 121 --- asm/vfp_macros.th | 15 - asm/vfpm_macros.th | 14 - asm/vfpops.sh | 24 - asm/x86-codegen.h | 2640 ------------------------------------------------- main.cpp | 1 - qv4isel_x86_64.cpp | 986 ------------------ qv4isel_x86_64_p.h | 54 - v4.pro | 19 - 25 files changed, 10047 deletions(-) delete mode 100644 asm/Makefile.am delete mode 100644 asm/amd64-codegen.h delete mode 100644 asm/arm-codegen.c delete mode 100644 asm/arm-codegen.h delete mode 100644 asm/arm-dis.c delete mode 100644 asm/arm-dis.h delete mode 100644 asm/arm-fpa-codegen.h delete mode 100644 asm/arm-vfp-codegen.h delete mode 100644 asm/arm-wmmx.h delete mode 100644 asm/arm_dpimacros.h delete mode 100644 asm/cmp_macros.th delete mode 100644 asm/dpi_macros.th delete mode 100755 asm/dpiops.sh delete mode 100644 asm/fpa_macros.th delete mode 100644 asm/fpam_macros.th delete mode 100755 asm/fpaops.sh delete mode 100644 asm/mov_macros.th delete mode 100644 asm/vfp_macros.th delete mode 100644 asm/vfpm_macros.th delete mode 100755 asm/vfpops.sh delete mode 100644 asm/x86-codegen.h delete mode 100644 qv4isel_x86_64.cpp delete mode 100644 qv4isel_x86_64_p.h diff --git a/asm/Makefile.am b/asm/Makefile.am deleted file mode 100644 index 180be53d3d..0000000000 --- a/asm/Makefile.am +++ /dev/null @@ -1,32 +0,0 @@ - -INCLUDES = $(GLIB_CFLAGS) -I$(top_srcdir) - -noinst_LTLIBRARIES = libmonoarch-arm.la - -BUILT_SOURCES = arm_dpimacros.h arm_fpamacros.h arm_vfpmacros.h - - -libmonoarch_arm_la_SOURCES = $(BUILT_SOURCES) \ - arm-codegen.c \ - arm-codegen.h \ - arm-dis.c \ - arm-dis.h - -arm_dpimacros.h: dpiops.sh mov_macros.th dpi_macros.th cmp_macros.th - (cd $(srcdir); bash ./dpiops.sh) > $@t - mv $@t $@ - -arm_fpamacros.h: fpaops.sh fpam_macros.th fpa_macros.th - (cd $(srcdir); bash ./fpaops.sh) > $@t - mv $@t $@ - -arm_vfpmacros.h: vfpops.sh vfpm_macros.th vfp_macros.th - (cd $(srcdir); bash ./vfpops.sh) > $@t - mv $@t $@ - -CLEANFILES = $(BUILT_SOURCES) - -EXTRA_DIST = dpiops.sh mov_macros.th dpi_macros.th cmp_macros.th \ - fpam_macros.th fpa_macros.th arm-fpa-codegen.h fpaops.sh \ - vfpm_macros.th vfp_macros.th arm-vfp-codegen.h vfpops.sh - diff --git a/asm/amd64-codegen.h b/asm/amd64-codegen.h deleted file mode 100644 index 3dea9cd306..0000000000 --- a/asm/amd64-codegen.h +++ /dev/null @@ -1,1831 +0,0 @@ -/* - * amd64-codegen.h: Macros for generating amd64 code - * - * Authors: - * Paolo Molaro (lupus@ximian.com) - * Intel Corporation (ORP Project) - * Sergey Chaban (serge@wildwestsoftware.com) - * Dietmar Maurer (dietmar@ximian.com) - * Patrik Torstensson - * Zalman Stern - * - * Copyright (C) 2000 Intel Corporation. All rights reserved. - * Copyright (C) 2001, 2002 Ximian, Inc. - */ - -#ifndef AMD64_H -#define AMD64_H - -//#include - -typedef enum { - AMD64_RAX = 0, - AMD64_RCX = 1, - AMD64_RDX = 2, - AMD64_RBX = 3, - AMD64_RSP = 4, - AMD64_RBP = 5, - AMD64_RSI = 6, - AMD64_RDI = 7, - AMD64_R8 = 8, - AMD64_R9 = 9, - AMD64_R10 = 10, - AMD64_R11 = 11, - AMD64_R12 = 12, - AMD64_R13 = 13, - AMD64_R14 = 14, - AMD64_R15 = 15, - AMD64_RIP = 16, - AMD64_NREG -} AMD64_Reg_No; - -typedef enum { - AMD64_XMM0 = 0, - AMD64_XMM1 = 1, - AMD64_XMM2 = 2, - AMD64_XMM3 = 3, - AMD64_XMM4 = 4, - AMD64_XMM5 = 5, - AMD64_XMM6 = 6, - AMD64_XMM7 = 7, - AMD64_XMM8 = 8, - AMD64_XMM9 = 9, - AMD64_XMM10 = 10, - AMD64_XMM11 = 11, - AMD64_XMM12 = 12, - AMD64_XMM13 = 13, - AMD64_XMM14 = 14, - AMD64_XMM15 = 15, - AMD64_XMM_NREG = 16, -} AMD64_XMM_Reg_No; - -typedef enum -{ - AMD64_REX_B = 1, /* The register in r/m field, base register in SIB byte, or reg in opcode is 8-15 rather than 0-7 */ - AMD64_REX_X = 2, /* The index register in SIB byte is 8-15 rather than 0-7 */ - AMD64_REX_R = 4, /* The reg field of ModRM byte is 8-15 rather than 0-7 */ - AMD64_REX_W = 8 /* Opeartion is 64-bits instead of 32 (default) or 16 (with 0x66 prefix) */ -} AMD64_REX_Bits; - -#if defined(__default_codegen__) - -#define amd64_codegen_pre(inst) -#define amd64_codegen_post(inst) - -#elif defined(__native_client_codegen__) - -#define amd64_codegen_pre(inst) guint8* _codegen_start = (inst); amd64_nacl_instruction_pre(); -#define amd64_codegen_post(inst) (amd64_nacl_instruction_post(&_codegen_start, &(inst)), _codegen_start); - -/* Because of rex prefixes, etc, call sequences are not constant size. */ -/* These pre- and post-sequence hooks remedy this by aligning the call */ -/* sequence after we emit it, since we will know the exact size then. */ -#define amd64_call_sequence_pre(inst) guint8* _code_start = (inst); -#define amd64_call_sequence_post(inst) \ - (mono_nacl_align_call(&_code_start, &(inst)), _code_start); - -/* Native client can load/store using one of the following registers */ -/* as a base: rip, r15, rbp, rsp. Any other base register needs to have */ -/* its upper 32 bits cleared and reference memory using r15 as the base. */ -#define amd64_is_valid_nacl_base(reg) \ - ((reg) == AMD64_RIP || (reg) == AMD64_R15 || \ - (reg) == AMD64_RBP || (reg) == AMD64_RSP) - -#endif /*__native_client_codegen__*/ - -#ifdef TARGET_WIN32 -#define AMD64_ARG_REG1 AMD64_RCX -#define AMD64_ARG_REG2 AMD64_RDX -#define AMD64_ARG_REG3 AMD64_R8 -#define AMD64_ARG_REG4 AMD64_R9 -#else -#define AMD64_ARG_REG1 AMD64_RDI -#define AMD64_ARG_REG2 AMD64_RSI -#define AMD64_ARG_REG3 AMD64_RDX -#define AMD64_ARG_REG4 AMD64_RCX -#endif - -#ifdef TARGET_WIN32 -#define AMD64_CALLEE_REGS ((1< 4) ? AMD64_REX_W : 0) | \ - (((reg_modrm) > 7) ? AMD64_REX_R : 0) | \ - (((reg_index) > 7) ? AMD64_REX_X : 0) | \ - (((reg_rm_base_opcode) > 7) ? AMD64_REX_B : 0); \ - if ((_amd64_rex_bits != 0) || (((width) == 1))) *(inst)++ = AMD64_REX(_amd64_rex_bits); \ - } while (0) -#elif defined(__native_client_codegen__) -#define amd64_emit_rex(inst, width, reg_modrm, reg_index, reg_rm_base_opcode) do \ - { \ - unsigned char _amd64_rex_bits = \ - (((width) > 4) ? AMD64_REX_W : 0) | \ - (((reg_modrm) > 7) ? AMD64_REX_R : 0) | \ - (((reg_index) > 7) ? AMD64_REX_X : 0) | \ - (((reg_rm_base_opcode) > 7) ? AMD64_REX_B : 0); \ - amd64_nacl_tag_rex((inst)); \ - if ((_amd64_rex_bits != 0) || (((width) == 1))) *(inst)++ = AMD64_REX(_amd64_rex_bits); \ - } while (0) -#endif - -typedef union { - guint64 val; - unsigned char b [8]; -} amd64_imm_buf; - -#include "x86-codegen.h" - -/* In 64 bit mode, all registers have a low byte subregister */ -#undef X86_IS_BYTE_REG -#define X86_IS_BYTE_REG(reg) 1 - -#define amd64_modrm_mod(modrm) ((modrm) >> 6) -#define amd64_modrm_reg(modrm) (((modrm) >> 3) & 0x7) -#define amd64_modrm_rm(modrm) ((modrm) & 0x7) - -#define amd64_rex_r(rex) ((((rex) >> 2) & 0x1) << 3) -#define amd64_rex_x(rex) ((((rex) >> 1) & 0x1) << 3) -#define amd64_rex_b(rex) ((((rex) >> 0) & 0x1) << 3) - -#define amd64_sib_scale(sib) ((sib) >> 6) -#define amd64_sib_index(sib) (((sib) >> 3) & 0x7) -#define amd64_sib_base(sib) ((sib) & 0x7) - -#define amd64_is_imm32(val) ((gint64)val >= -((gint64)1<<31) && (gint64)val <= (((gint64)1<<31)-1)) - -#define x86_imm_emit64(inst,imm) \ - do { \ - amd64_imm_buf imb; \ - imb.val = (guint64) (imm); \ - *(inst)++ = imb.b [0]; \ - *(inst)++ = imb.b [1]; \ - *(inst)++ = imb.b [2]; \ - *(inst)++ = imb.b [3]; \ - *(inst)++ = imb.b [4]; \ - *(inst)++ = imb.b [5]; \ - *(inst)++ = imb.b [6]; \ - *(inst)++ = imb.b [7]; \ - } while (0) - -#define amd64_membase_emit(inst,reg,basereg,disp) do { \ - if ((basereg) == AMD64_RIP) { \ - x86_address_byte ((inst), 0, (reg)&0x7, 5); \ - x86_imm_emit32 ((inst), (disp)); \ - } \ - else \ - x86_membase_emit ((inst),(reg)&0x7, (basereg)&0x7, (disp)); \ -} while (0) - -#define amd64_alu_reg_imm_size_body(inst,opc,reg,imm,size) \ - do { \ - if (x86_is_imm8((imm))) { \ - amd64_emit_rex(inst, size, 0, 0, (reg)); \ - *(inst)++ = (unsigned char)0x83; \ - x86_reg_emit ((inst), (opc), (reg)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else if ((reg) == AMD64_RAX) { \ - amd64_emit_rex(inst, size, 0, 0, 0); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ - x86_imm_emit32 ((inst), (imm)); \ - } else { \ - amd64_emit_rex(inst, size, 0, 0, (reg)); \ - *(inst)++ = (unsigned char)0x81; \ - x86_reg_emit ((inst), (opc), (reg)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define amd64_alu_reg_reg_size_body(inst,opc,dreg,reg,size) \ - do { \ - amd64_emit_rex(inst, size, (dreg), 0, (reg)); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#if defined(__default_codegen__) - -#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) \ - amd64_alu_reg_imm_size_body((inst), (opc), (reg), (imm), (size)) - -#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) \ - amd64_alu_reg_reg_size_body((inst), (opc), (dreg), (reg), (size)) - -#elif defined(__native_client_codegen__) -/* NaCl modules may not directly update RSP or RBP other than direct copies */ -/* between them. Instead the lower 4 bytes are updated and then added to R15 */ -#define amd64_is_nacl_stack_reg(reg) (((reg) == AMD64_RSP) || ((reg) == AMD64_RBP)) - -#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) \ - do{ \ - amd64_codegen_pre(inst); \ - if (amd64_is_nacl_stack_reg(reg)) { \ - if (((opc) != X86_ADD) && ((opc) != X86_SUB)) \ - g_assert_not_reached(); \ - amd64_alu_reg_imm_size_body((inst), (opc), (reg), (imm), 4); \ - /* Use LEA instead of ADD to preserve flags */ \ - amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ - } else { \ - amd64_alu_reg_imm_size_body((inst), (opc), (reg), (imm), (size)); \ - } \ - amd64_codegen_post(inst); \ - } while(0) - -#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) \ - do { \ - amd64_codegen_pre(inst); \ - if (amd64_is_nacl_stack_reg((dreg)) && ((reg) != AMD64_R15)) { \ - if (((opc) != X86_ADD && (opc) != X86_SUB)) \ - g_assert_not_reached(); \ - amd64_alu_reg_reg_size_body((inst), (opc), (dreg), (reg), 4); \ - /* Use LEA instead of ADD to preserve flags */ \ - amd64_lea_memindex_size((inst), (dreg), (dreg), 0, AMD64_R15, 0, 8); \ - } else { \ - amd64_alu_reg_reg_size_body((inst), (opc), (dreg), (reg), (size)); \ - } \ - amd64_codegen_post(inst); \ - } while (0) - -#endif /*__native_client_codegen__*/ - -#define amd64_alu_reg_imm(inst,opc,reg,imm) amd64_alu_reg_imm_size((inst),(opc),(reg),(imm),8) - -#define amd64_alu_reg_reg(inst,opc,dreg,reg) amd64_alu_reg_reg_size ((inst),(opc),(dreg),(reg),8) - -#define amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,size) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex ((inst),(size),(reg),0,(basereg)); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ - amd64_membase_emit (inst, reg, basereg, disp); \ - amd64_codegen_post(inst); \ -} while (0) - -#define amd64_mov_regp_reg(inst,regp,reg,size) \ - do { \ - amd64_codegen_pre(inst); \ - if ((size) == 2) \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - amd64_emit_rex(inst, (size), (reg), 0, (regp)); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x88; break; \ - case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ - default: assert (0); \ - } \ - x86_regp_emit ((inst), (reg), (regp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_mov_membase_reg(inst,basereg,disp,reg,size) \ - do { \ - amd64_codegen_pre(inst); \ - if ((size) == 2) \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x88; break; \ - case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ - default: assert (0); \ - } \ - x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_mov_mem_reg(inst,mem,reg,size) \ - do { \ - amd64_codegen_pre(inst); \ - if ((size) == 2) \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - amd64_emit_rex(inst, (size), (reg), 0, 0); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x88; break; \ - case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ - default: assert (0); \ - } \ - x86_address_byte ((inst), 0, (reg), 4); \ - x86_address_byte ((inst), 0, 4, 5); \ - x86_imm_emit32 ((inst), (mem)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_mov_reg_reg(inst,dreg,reg,size) \ - do { \ - amd64_codegen_pre(inst); \ - if ((size) == 2) \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - amd64_emit_rex(inst, (size), (dreg), 0, (reg)); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x8a; break; \ - case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - x86_reg_emit ((inst), (dreg), (reg)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_mov_reg_mem_body(inst,reg,mem,size) \ - do { \ - amd64_codegen_pre(inst); \ - if ((size) == 2) \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - amd64_emit_rex(inst, (size), (reg), 0, 0); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x8a; break; \ - case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - x86_address_byte ((inst), 0, (reg), 4); \ - x86_address_byte ((inst), 0, 4, 5); \ - x86_imm_emit32 ((inst), (mem)); \ - amd64_codegen_post(inst); \ - } while (0) - -#if defined(__default_codegen__) -#define amd64_mov_reg_mem(inst,reg,mem,size) \ - do { \ - amd64_mov_reg_mem_body((inst),(reg),(mem),(size)); \ - } while (0) -#elif defined(__native_client_codegen__) -/* We have to re-base memory reads because memory isn't zero based. */ -#define amd64_mov_reg_mem(inst,reg,mem,size) \ - do { \ - amd64_mov_reg_membase((inst),(reg),AMD64_R15,(mem),(size)); \ - } while (0) -#endif /* __native_client_codegen__ */ - -#define amd64_mov_reg_membase_body(inst,reg,basereg,disp,size) \ - do { \ - if ((size) == 2) \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x8a; break; \ - case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define amd64_mov_reg_memindex_size_body(inst,reg,basereg,disp,indexreg,shift,size) \ - do { \ - amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); \ - x86_mov_reg_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(size) == 8 ? 4 : (size)); \ - } while (0) - -#if defined(__default_codegen__) - -#define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) \ - amd64_mov_reg_memindex_size_body((inst),(reg),(basereg),(disp),(indexreg),(shift),(size)) -#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) \ - do { \ - amd64_mov_reg_membase_body((inst), (reg), (basereg), (disp), (size)); \ - } while (0) - -#elif defined(__native_client_codegen__) - -#define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) \ - do { \ - amd64_codegen_pre(inst); \ - if (amd64_is_nacl_stack_reg((reg))) { \ - /* Clear upper 32 bits with mov of size 4 */ \ - amd64_mov_reg_memindex_size_body((inst), (reg), (basereg), (disp), (indexreg), (shift), 4); \ - /* Add %r15 using LEA to preserve flags */ \ - amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ - } else { \ - amd64_mov_reg_memindex_size_body((inst), (reg), (basereg), (disp), (indexreg), (shift), (size)); \ - } \ - amd64_codegen_post(inst); \ - } while(0) - -#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) \ - do { \ - amd64_codegen_pre(inst); \ - if (amd64_is_nacl_stack_reg((reg))) { \ - /* Clear upper 32 bits with mov of size 4 */ \ - amd64_mov_reg_membase_body((inst), (reg), (basereg), (disp), 4); \ - /* Add %r15 */ \ - amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ - } else { \ - amd64_mov_reg_membase_body((inst), (reg), (basereg), (disp), (size)); \ - } \ - amd64_codegen_post(inst); \ - } while (0) - -#endif /*__native_client_codegen__*/ - -#define amd64_movzx_reg_membase(inst,reg,basereg,disp,size) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb6; break; \ - case 2: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb7; break; \ - case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movsxd_reg_mem(inst,reg,mem) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst,8,(reg),0,0); \ - *(inst)++ = (unsigned char)0x63; \ - x86_mem_emit ((inst), ((reg)&0x7), (mem)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movsxd_reg_membase(inst,reg,basereg,disp) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst,8,(reg),0,(basereg)); \ - *(inst)++ = (unsigned char)0x63; \ - x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movsxd_reg_reg(inst,dreg,reg) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst,8,(dreg),0,(reg)); \ - *(inst)++ = (unsigned char)0x63; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - amd64_codegen_post(inst); \ - } while (0) - -/* Pretty much the only instruction that supports a 64-bit immediate. Optimize for common case of - * 32-bit immediate. Pepper with casts to avoid warnings. - */ -#define amd64_mov_reg_imm_size(inst,reg,imm,size) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst, (size), 0, 0, (reg)); \ - *(inst)++ = (unsigned char)0xb8 + ((reg) & 0x7); \ - if ((size) == 8) \ - x86_imm_emit64 ((inst), (guint64)(imm)); \ - else \ - x86_imm_emit32 ((inst), (int)(guint64)(imm)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_mov_reg_imm(inst,reg,imm) \ - do { \ - int _amd64_width_temp = ((guint64)(imm) == (guint64)(int)(guint64)(imm)); \ - amd64_codegen_pre(inst); \ - amd64_mov_reg_imm_size ((inst), (reg), (imm), (_amd64_width_temp ? 4 : 8)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_set_reg_template(inst,reg) amd64_mov_reg_imm_size ((inst),(reg), 0, 8) - -#define amd64_set_template(inst,reg) amd64_set_reg_template((inst),(reg)) - -#define amd64_mov_membase_imm(inst,basereg,disp,imm,size) \ - do { \ - amd64_codegen_pre(inst); \ - if ((size) == 2) \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - amd64_emit_rex(inst, (size) == 1 ? 0 : (size), 0, 0, (basereg)); \ - if ((size) == 1) { \ - *(inst)++ = (unsigned char)0xc6; \ - x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else if ((size) == 2) { \ - *(inst)++ = (unsigned char)0xc7; \ - x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ - x86_imm_emit16 ((inst), (imm)); \ - } else { \ - *(inst)++ = (unsigned char)0xc7; \ - x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - amd64_codegen_post(inst); \ - } while (0) - - -#define amd64_lea_membase_body(inst,reg,basereg,disp) \ - do { \ - amd64_emit_rex(inst, 8, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)0x8d; \ - amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#if defined(__default_codegen__) -#define amd64_lea_membase(inst,reg,basereg,disp) \ - amd64_lea_membase_body((inst), (reg), (basereg), (disp)) -#elif defined(__native_client_codegen__) -/* NaCl modules may not write directly into RSP/RBP. Instead, use a */ -/* 32-bit LEA and add R15 to the effective address */ -#define amd64_lea_membase(inst,reg,basereg,disp) \ - do { \ - amd64_codegen_pre(inst); \ - if (amd64_is_nacl_stack_reg(reg)) { \ - /* 32-bit LEA */ \ - amd64_emit_rex((inst), 4, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)0x8d; \ - amd64_membase_emit((inst), (reg), (basereg), (disp)); \ - /* Use a 64-bit LEA instead of an ADD to preserve flags */ \ - amd64_lea_memindex_size((inst), (reg), (reg), 0, AMD64_R15, 0, 8); \ - } else { \ - amd64_lea_membase_body((inst), (reg), (basereg), (disp)); \ - } \ - amd64_codegen_post(inst); \ - } while (0) -#endif /*__native_client_codegen__*/ - -/* Instruction are implicitly 64-bits so don't generate REX for just the size. */ -#define amd64_push_reg(inst,reg) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst, 0, 0, 0, (reg)); \ - *(inst)++ = (unsigned char)0x50 + ((reg) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -/* Instruction is implicitly 64-bits so don't generate REX for just the size. */ -#define amd64_push_membase(inst,basereg,disp) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst, 0, 0, 0, (basereg)); \ - *(inst)++ = (unsigned char)0xff; \ - x86_membase_emit ((inst), 6, (basereg) & 0x7, (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_pop_reg_body(inst,reg) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex(inst, 0, 0, 0, (reg)); \ - *(inst)++ = (unsigned char)0x58 + ((reg) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -#if defined(__default_codegen__) - -#define amd64_call_reg(inst,reg) \ - do { \ - amd64_emit_rex(inst, 0, 0, 0, (reg)); \ - *(inst)++ = (unsigned char)0xff; \ - x86_reg_emit ((inst), 2, ((reg) & 0x7)); \ - } while (0) - - -#define amd64_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) -#define amd64_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) - -#define amd64_pop_reg(inst,reg) amd64_pop_reg_body((inst), (reg)) - -#elif defined(__native_client_codegen__) - -/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ -#define amd64_jump_reg_size(inst,reg,size) \ - do { \ - amd64_codegen_pre((inst)); \ - amd64_alu_reg_imm_size((inst), X86_AND, (reg), (nacl_align_byte), 4); \ - amd64_alu_reg_reg_size((inst), X86_ADD, (reg), AMD64_R15, 8); \ - amd64_emit_rex ((inst),0,0,0,(reg)); \ - x86_jump_reg((inst),((reg)&0x7)); \ - amd64_codegen_post((inst)); \ - } while (0) - -/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ -#define amd64_jump_mem_size(inst,mem,size) \ - do { \ - amd64_codegen_pre((inst)); \ - amd64_mov_reg_mem((inst), (mem), AMD64_R11, 4); \ - amd64_jump_reg_size((inst), AMD64_R11, 4); \ - amd64_codegen_post((inst)); \ - } while (0) - -#define amd64_call_reg_internal(inst,reg) \ - do { \ - amd64_codegen_pre((inst)); \ - amd64_alu_reg_imm_size((inst), X86_AND, (reg), (nacl_align_byte), 4); \ - amd64_alu_reg_reg_size((inst), X86_ADD, (reg), AMD64_R15, 8); \ - amd64_emit_rex((inst), 0, 0, 0, (reg)); \ - x86_call_reg((inst), ((reg) & 0x7)); \ - amd64_codegen_post((inst)); \ - } while (0) - -#define amd64_call_reg(inst,reg) \ - do { \ - amd64_codegen_pre((inst)); \ - amd64_call_sequence_pre(inst); \ - amd64_call_reg_internal((inst), (reg)); \ - amd64_call_sequence_post(inst); \ - amd64_codegen_post((inst)); \ - } while (0) - - -#define amd64_ret(inst) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_pop_reg_body((inst), AMD64_R11); \ - amd64_jump_reg_size((inst), AMD64_R11, 8); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_leave(inst) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_mov_reg_reg((inst), AMD64_RSP, AMD64_RBP, 8); \ - amd64_pop_reg_body((inst), AMD64_R11); \ - amd64_mov_reg_reg_size((inst), AMD64_RBP, AMD64_R11, 4); \ - amd64_alu_reg_reg_size((inst), X86_ADD, AMD64_RBP, AMD64_R15, 8); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_pop_reg(inst,reg) \ - do { \ - amd64_codegen_pre(inst); \ - if (amd64_is_nacl_stack_reg((reg))) { \ - amd64_pop_reg_body((inst), AMD64_R11); \ - amd64_mov_reg_reg_size((inst), (reg), AMD64_R11, 4); \ - amd64_alu_reg_reg_size((inst), X86_ADD, (reg), AMD64_R15, 8); \ - } else { \ - amd64_pop_reg_body((inst), (reg)); \ - } \ - amd64_codegen_post(inst); \ - } while (0) - -#endif /*__native_client_codegen__*/ - -#define amd64_movsd_reg_regp(inst,reg,regp) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf2); \ - amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x10; \ - x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movsd_regp_reg(inst,regp,reg) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf2); \ - amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x11; \ - x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movss_reg_regp(inst,reg,regp) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf3); \ - amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x10; \ - x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movss_regp_reg(inst,regp,reg) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf3); \ - amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x11; \ - x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movsd_reg_membase(inst,reg,basereg,disp) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf2); \ - amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x10; \ - x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movss_reg_membase(inst,reg,basereg,disp) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf3); \ - amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x10; \ - x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movsd_membase_reg(inst,basereg,disp,reg) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf2); \ - amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x11; \ - x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_movss_membase_reg(inst,basereg,disp,reg) \ - do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), 0xf3); \ - amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x11; \ - x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ - amd64_codegen_post(inst); \ - } while (0) - -/* The original inc_reg opcode is used as the REX prefix */ -#define amd64_inc_reg_size(inst,reg,size) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex ((inst),(size),0,0,(reg)); \ - *(inst)++ = (unsigned char)0xff; \ - x86_reg_emit ((inst),0,(reg) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_dec_reg_size(inst,reg,size) \ - do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex ((inst),(size),0,0,(reg)); \ - *(inst)++ = (unsigned char)0xff; \ - x86_reg_emit ((inst),1,(reg) & 0x7); \ - amd64_codegen_post(inst); \ - } while (0) - -#define amd64_fld_membase_size(inst,basereg,disp,is_double,size) do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex ((inst),0,0,0,(basereg)); \ - *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ - amd64_membase_emit ((inst), 0, (basereg), (disp)); \ - amd64_codegen_post(inst); \ -} while (0) - -#if defined (__default_codegen__) - -/* From the AMD64 Software Optimization Manual */ -#define amd64_padding_size(inst,size) \ - do { \ - switch ((size)) { \ - case 1: *(inst)++ = 0x90; break; \ - case 2: *(inst)++ = 0x66; *(inst)++ = 0x90; break; \ - case 3: *(inst)++ = 0x66; *(inst)++ = 0x66; *(inst)++ = 0x90; break; \ - default: amd64_emit_rex ((inst),8,0,0,0); x86_padding ((inst), (size) - 1); \ - }; \ - } while (0) - -#define amd64_call_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); *(inst)++ = (unsigned char)0xff; amd64_membase_emit ((inst),2, (basereg),(disp)); } while (0) -#define amd64_jump_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); *(inst)++ = (unsigned char)0xff; amd64_membase_emit ((inst), 4, (basereg), (disp)); } while (0) - -#define amd64_jump_code_size(inst,target,size) do { \ - if (amd64_is_imm32 ((gint64)(target) - (gint64)(inst))) { \ - x86_jump_code((inst),(target)); \ - } else { \ - amd64_jump_membase ((inst), AMD64_RIP, 0); \ - *(guint64*)(inst) = (guint64)(target); \ - (inst) += 8; \ - } \ -} while (0) - -#elif defined(__native_client_codegen__) - -/* The 3-7 byte NOP sequences in amd64_padding_size below are all illegal in */ -/* 64-bit Native Client because they load into rSP/rBP or use duplicate */ -/* prefixes. Instead we use the NOPs recommended in Section 3.5.1.8 of the */ -/* Intel64 and IA-32 Architectures Optimization Reference Manual and */ -/* Section 4.13 of AMD Software Optimization Guide for Family 10h Processors. */ - -#define amd64_padding_size(inst,size) \ - do { \ - unsigned char *code_start = (inst); \ - switch ((size)) { \ - /* xchg %eax,%eax, recognized by hardware as a NOP */ \ - case 1: *(inst)++ = 0x90; break; \ - /* xchg %ax,%ax */ \ - case 2: *(inst)++ = 0x66; *(inst)++ = 0x90; \ - break; \ - /* nop (%rax) */ \ - case 3: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ - *(inst)++ = 0x00; \ - break; \ - /* nop 0x0(%rax) */ \ - case 4: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ - x86_address_byte ((inst), 1, 0, AMD64_RAX); \ - x86_imm_emit8 ((inst), 0); \ - break; \ - /* nop 0x0(%rax,%rax) */ \ - case 5: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ - x86_address_byte ((inst), 1, 0, 4); \ - x86_address_byte ((inst), 0, AMD64_RAX, AMD64_RAX); \ - x86_imm_emit8 ((inst), 0); \ - break; \ - /* nopw 0x0(%rax,%rax) */ \ - case 6: *(inst)++ = 0x66; *(inst)++ = 0x0f; \ - *(inst)++ = 0x1f; \ - x86_address_byte ((inst), 1, 0, 4); \ - x86_address_byte ((inst), 0, AMD64_RAX, AMD64_RAX); \ - x86_imm_emit8 ((inst), 0); \ - break; \ - /* nop 0x0(%rax) (32-bit displacement) */ \ - case 7: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ - x86_address_byte ((inst), 2, 0, AMD64_RAX); \ - x86_imm_emit32((inst), 0); \ - break; \ - /* nop 0x0(%rax,%rax) (32-bit displacement) */ \ - case 8: *(inst)++ = 0x0f; *(inst)++ = 0x1f; \ - x86_address_byte ((inst), 2, 0, 4); \ - x86_address_byte ((inst), 0, AMD64_RAX, AMD64_RAX); \ - x86_imm_emit32 ((inst), 0); \ - break; \ - default: \ - g_assert_not_reached(); \ - } \ - g_assert(code_start + (size) == (unsigned char *)(inst)); \ - } while (0) - - -/* Size is ignored for Native Client calls, we restrict jumping to 32-bits */ -#define amd64_call_membase_size(inst,basereg,disp,size) \ - do { \ - amd64_codegen_pre((inst)); \ - amd64_call_sequence_pre(inst); \ - amd64_mov_reg_membase((inst), AMD64_R11, (basereg), (disp), 4); \ - amd64_call_reg_internal((inst), AMD64_R11); \ - amd64_call_sequence_post(inst); \ - amd64_codegen_post((inst)); \ - } while (0) - -/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ -#define amd64_jump_membase_size(inst,basereg,disp,size) \ - do { \ - amd64_mov_reg_membase((inst), AMD64_R11, (basereg), (disp), 4); \ - amd64_jump_reg_size((inst), AMD64_R11, 4); \ - } while (0) - -/* On Native Client we can't jump more than INT_MAX in either direction */ -#define amd64_jump_code_size(inst,target,size) \ - do { \ - /* x86_jump_code used twice in case of */ \ - /* relocation by amd64_codegen_post */ \ - guint8* jump_start; \ - amd64_codegen_pre(inst); \ - assert(amd64_is_imm32 ((gint64)(target) - (gint64)(inst))); \ - x86_jump_code((inst),(target)); \ - inst = amd64_codegen_post(inst); \ - jump_start = (inst); \ - x86_jump_code((inst),(target)); \ - mono_amd64_patch(jump_start, (target)); \ -} while (0) - -#endif /*__native_client_codegen__*/ - -/* - * SSE - */ - -//TODO Reorganize SSE opcode defines. - -/* Two opcode SSE defines */ - -#define emit_sse_reg_reg_op2_size(inst,dreg,reg,op1,op2,size) do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ - *(inst)++ = (unsigned char)(op1); \ - *(inst)++ = (unsigned char)(op2); \ - x86_reg_emit ((inst), (dreg), (reg)); \ - amd64_codegen_post(inst); \ -} while (0) - -#define emit_sse_reg_reg_op2(inst,dreg,reg,op1,op2) emit_sse_reg_reg_op2_size ((inst), (dreg), (reg), (op1), (op2), 0) - -#define emit_sse_reg_reg_op2_imm(inst,dreg,reg,op1,op2,imm) do { \ - amd64_codegen_pre(inst); \ - emit_sse_reg_reg_op2 ((inst), (dreg), (reg), (op1), (op2)); \ - x86_imm_emit8 ((inst), (imm)); \ - amd64_codegen_post(inst); \ -} while (0) - -#define emit_sse_membase_reg_op2(inst,basereg,disp,reg,op1,op2) do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex ((inst), 0, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)(op1); \ - *(inst)++ = (unsigned char)(op2); \ - amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ - amd64_codegen_post(inst); \ -} while (0) - -#define emit_sse_reg_membase_op2(inst,dreg,basereg,disp,op1,op2) do { \ - amd64_codegen_pre(inst); \ - amd64_emit_rex ((inst), 0, (dreg), 0, (basereg) == AMD64_RIP ? 0 : (basereg)); \ - *(inst)++ = (unsigned char)(op1); \ - *(inst)++ = (unsigned char)(op2); \ - amd64_membase_emit ((inst), (dreg), (basereg), (disp)); \ - amd64_codegen_post(inst); \ -} while (0) - -/* Three opcode SSE defines */ - -#define emit_opcode3(inst,op1,op2,op3) do { \ - *(inst)++ = (unsigned char)(op1); \ - *(inst)++ = (unsigned char)(op2); \ - *(inst)++ = (unsigned char)(op3); \ -} while (0) - -#define emit_sse_reg_reg_size(inst,dreg,reg,op1,op2,op3,size) do { \ - amd64_codegen_pre(inst); \ - *(inst)++ = (unsigned char)(op1); \ - amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ - *(inst)++ = (unsigned char)(op2); \ - *(inst)++ = (unsigned char)(op3); \ - x86_reg_emit ((inst), (dreg), (reg)); \ - amd64_codegen_post(inst); \ -} while (0) - -#define emit_sse_reg_reg(inst,dreg,reg,op1,op2,op3) emit_sse_reg_reg_size ((inst), (dreg), (reg), (op1), (op2), (op3), 0) - -#define emit_sse_reg_reg_imm(inst,dreg,reg,op1,op2,op3,imm) do { \ - amd64_codegen_pre(inst); \ - emit_sse_reg_reg ((inst), (dreg), (reg), (op1), (op2), (op3)); \ - x86_imm_emit8 ((inst), (imm)); \ - amd64_codegen_post(inst); \ -} while (0) - -#define emit_sse_membase_reg(inst,basereg,disp,reg,op1,op2,op3) do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), (unsigned char)(op1)); \ - amd64_emit_rex ((inst), 0, (reg), 0, (basereg)); \ - *(inst)++ = (unsigned char)(op2); \ - *(inst)++ = (unsigned char)(op3); \ - amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ - amd64_codegen_post(inst); \ -} while (0) - -#define emit_sse_reg_membase(inst,dreg,basereg,disp,op1,op2,op3) do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), (unsigned char)(op1)); \ - amd64_emit_rex ((inst), 0, (dreg), 0, (basereg) == AMD64_RIP ? 0 : (basereg)); \ - *(inst)++ = (unsigned char)(op2); \ - *(inst)++ = (unsigned char)(op3); \ - amd64_membase_emit ((inst), (dreg), (basereg), (disp)); \ - amd64_codegen_post(inst); \ -} while (0) - -/* Four opcode SSE defines */ - -#define emit_sse_reg_reg_op4_size(inst,dreg,reg,op1,op2,op3,op4,size) do { \ - amd64_codegen_pre(inst); \ - x86_prefix((inst), (unsigned char)(op1)); \ - amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ - *(inst)++ = (unsigned char)(op2); \ - *(inst)++ = (unsigned char)(op3); \ - *(inst)++ = (unsigned char)(op4); \ - x86_reg_emit ((inst), (dreg), (reg)); \ - amd64_codegen_post(inst); \ -} while (0) - -#define emit_sse_reg_reg_op4(inst,dreg,reg,op1,op2,op3,op4) emit_sse_reg_reg_op4_size ((inst), (dreg), (reg), (op1), (op2), (op3), (op4), 0) - -/* specific SSE opcode defines */ - -#define amd64_sse_xorpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg), 0x66, 0x0f, 0x57) - -#define amd64_sse_xorpd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst),(dreg),(basereg), (disp), 0x66, 0x0f, 0x57) - -#define amd64_sse_andpd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst),(dreg),(basereg), (disp), 0x66, 0x0f, 0x54) - -#define amd64_sse_movsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x10) - -#define amd64_sse_movsd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0xf2, 0x0f, 0x10) - -#define amd64_sse_movsd_membase_reg(inst,basereg,disp,reg) emit_sse_membase_reg ((inst), (basereg), (disp), (reg), 0xf2, 0x0f, 0x11) - -#define amd64_sse_movss_membase_reg(inst,basereg,disp,reg) emit_sse_membase_reg ((inst), (basereg), (disp), (reg), 0xf3, 0x0f, 0x11) - -#define amd64_sse_movss_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0xf3, 0x0f, 0x10) - -#define amd64_sse_comisd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg),0x66,0x0f,0x2f) - -#define amd64_sse_comisd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0x66, 0x0f, 0x2f) - -#define amd64_sse_ucomisd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg),0x66,0x0f,0x2e) - -#define amd64_sse_cvtsd2si_reg_reg(inst,dreg,reg) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2d, 8) - -#define amd64_sse_cvttsd2si_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2c, (size)) - -#define amd64_sse_cvttsd2si_reg_reg(inst,dreg,reg) amd64_sse_cvttsd2si_reg_reg_size ((inst), (dreg), (reg), 8) - -#define amd64_sse_cvtsi2sd_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2a, (size)) - -#define amd64_sse_cvtsi2sd_reg_reg(inst,dreg,reg) amd64_sse_cvtsi2sd_reg_reg_size ((inst), (dreg), (reg), 8) - -#define amd64_sse_cvtsd2ss_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5a) - -#define amd64_sse_cvtss2sd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf3, 0x0f, 0x5a) - -#define amd64_sse_addsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x58) - -#define amd64_sse_subsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5c) - -#define amd64_sse_mulsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x59) - -#define amd64_sse_divsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5e) - -#define amd64_sse_sqrtsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x51) - - -#define amd64_sse_pinsrw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm ((inst), (dreg), (reg), 0x66, 0x0f, 0xc4, (imm)) - -#define amd64_sse_pextrw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm ((inst), (dreg), (reg), 0x66, 0x0f, 0xc5, (imm)) - - -#define amd64_sse_cvttsd2si_reg_xreg_size(inst,reg,xreg,size) emit_sse_reg_reg_size ((inst), (reg), (xreg), 0xf2, 0x0f, 0x2c, (size)) - - -#define amd64_sse_addps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x58) - -#define amd64_sse_divps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5e) - -#define amd64_sse_mulps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x59) - -#define amd64_sse_subps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5c) - -#define amd64_sse_maxps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5f) - -#define amd64_sse_minps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x5d) - -#define amd64_sse_cmpps_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_op2_imm((inst), (dreg), (reg), 0x0f, 0xc2, (imm)) - -#define amd64_sse_andps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x54) - -#define amd64_sse_andnps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x55) - -#define amd64_sse_orps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x56) - -#define amd64_sse_xorps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x57) - -#define amd64_sse_sqrtps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x51) - -#define amd64_sse_rsqrtps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x52) - -#define amd64_sse_rcpps_reg_reg(inst,dreg,reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x53) - -#define amd64_sse_addsubps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0xd0) - -#define amd64_sse_haddps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x7c) - -#define amd64_sse_hsubps_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x7d) - -#define amd64_sse_movshdup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf3, 0x0f, 0x16) - -#define amd64_sse_movsldup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf3, 0x0f, 0x12) - - -#define amd64_sse_pshufhw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0xf3, 0x0f, 0x70, (imm)) - -#define amd64_sse_pshuflw_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0xf2, 0x0f, 0x70, (imm)) - -#define amd64_sse_pshufd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0x70, (imm)) - -#define amd64_sse_shufps_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_op2_imm((inst), (dreg), (reg), 0x0f, 0xC6, (imm)) - -#define amd64_sse_shufpd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0xC6, (imm)) - - -#define amd64_sse_addpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x58) - -#define amd64_sse_divpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5e) - -#define amd64_sse_mulpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x59) - -#define amd64_sse_subpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5c) - -#define amd64_sse_maxpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5f) - -#define amd64_sse_minpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x5d) - -#define amd64_sse_cmppd_reg_reg_imm(inst,dreg,reg,imm) emit_sse_reg_reg_imm((inst), (dreg), (reg), 0x66, 0x0f, 0xc2, (imm)) - -#define amd64_sse_andpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x54) - -#define amd64_sse_andnpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x55) - -#define amd64_sse_orpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x56) - -#define amd64_sse_sqrtpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x51) - -#define amd64_sse_rsqrtpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x52) - -#define amd64_sse_rcppd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x53) - -#define amd64_sse_addsubpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd0) - -#define amd64_sse_haddpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x7c) - -#define amd64_sse_hsubpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x7d) - -#define amd64_sse_movddup_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xf2, 0x0f, 0x12) - - -#define amd64_sse_pmovmskb_reg_reg(inst,dreg,reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd7) - - -#define amd64_sse_pand_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdb) - -#define amd64_sse_por_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xeb) - -#define amd64_sse_pxor_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xef) - - -#define amd64_sse_paddb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfc) - -#define amd64_sse_paddw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfd) - -#define amd64_sse_paddd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfe) - -#define amd64_sse_paddq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd4) - - -#define amd64_sse_psubb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf8) - -#define amd64_sse_psubw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf9) - -#define amd64_sse_psubd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfa) - -#define amd64_sse_psubq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xfb) - - -#define amd64_sse_pmaxub_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xde) - -#define amd64_sse_pmaxuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3e) - -#define amd64_sse_pmaxud_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3f) - - -#define amd64_sse_pmaxsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3c) - -#define amd64_sse_pmaxsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xee) - -#define amd64_sse_pmaxsd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3d) - - -#define amd64_sse_pavgb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe0) - -#define amd64_sse_pavgw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe3) - - -#define amd64_sse_pminub_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xda) - -#define amd64_sse_pminuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3a) - -#define amd64_sse_pminud_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x3b) - - -#define amd64_sse_pminsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x38) - -#define amd64_sse_pminsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xea) - -#define amd64_sse_pminsd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x39) - - -#define amd64_sse_pcmpeqb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x74) - -#define amd64_sse_pcmpeqw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x75) - -#define amd64_sse_pcmpeqd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x76) - -#define amd64_sse_pcmpeqq_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x29) - - -#define amd64_sse_pcmpgtb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x64) - -#define amd64_sse_pcmpgtw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x65) - -#define amd64_sse_pcmpgtd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x66) - -#define amd64_sse_pcmpgtq_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x37) - - -#define amd64_sse_psadbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf6) - - -#define amd64_sse_punpcklbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x60) - -#define amd64_sse_punpcklwd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x61) - -#define amd64_sse_punpckldq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x62) - -#define amd64_sse_punpcklqdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6c) - -#define amd64_sse_unpcklpd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x14) - -#define amd64_sse_unpcklps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x14) - - -#define amd64_sse_punpckhbw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x68) - -#define amd64_sse_punpckhwd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x69) - -#define amd64_sse_punpckhdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6a) - -#define amd64_sse_punpckhqdq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6d) - -#define amd64_sse_unpckhpd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x15) - -#define amd64_sse_unpckhps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x15) - - -#define amd64_sse_packsswb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x63) - -#define amd64_sse_packssdw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x6b) - -#define amd64_sse_packuswb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0x67) - -#define amd64_sse_packusdw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x2b) - - -#define amd64_sse_paddusb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdc) - -#define amd64_sse_psubusb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd8) - -#define amd64_sse_paddusw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xdd) - -#define amd64_sse_psubusw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd8) - - -#define amd64_sse_paddsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xec) - -#define amd64_sse_psubsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe8) - -#define amd64_sse_paddsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xed) - -#define amd64_sse_psubsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe9) - - -#define amd64_sse_pmullw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd5) - -#define amd64_sse_pmulld_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x40) - -#define amd64_sse_pmuludq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf4) - -#define amd64_sse_pmulhuw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe4) - -#define amd64_sse_pmulhw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe5) - - -#define amd64_sse_psrlw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x71, (imm)) - -#define amd64_sse_psrlw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd1) - - -#define amd64_sse_psraw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x71, (imm)) - -#define amd64_sse_psraw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe1) - - -#define amd64_sse_psllw_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x71, (imm)) - -#define amd64_sse_psllw_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf1) - - -#define amd64_sse_psrld_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x72, (imm)) - -#define amd64_sse_psrld_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd2) - - -#define amd64_sse_psrad_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x72, (imm)) - -#define amd64_sse_psrad_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe2) - - -#define amd64_sse_pslld_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x72, (imm)) - -#define amd64_sse_pslld_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf2) - - -#define amd64_sse_psrlq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHR, (reg), 0x66, 0x0f, 0x73, (imm)) - -#define amd64_sse_psrlq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xd3) - - -#define amd64_sse_psraq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SAR, (reg), 0x66, 0x0f, 0x73, (imm)) - -#define amd64_sse_psraq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xe3) - - -#define amd64_sse_psllq_reg_imm(inst, reg, imm) emit_sse_reg_reg_imm((inst), X86_SSE_SHL, (reg), 0x66, 0x0f, 0x73, (imm)) - -#define amd64_sse_psllq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0f, 0xf3) - - -#define amd64_sse_cvtdq2pd_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF3, 0x0F, 0xE6) - -#define amd64_sse_cvtdq2ps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0F, 0x5B) - -#define amd64_sse_cvtpd2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF2, 0x0F, 0xE6) - -#define amd64_sse_cvtpd2ps_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0x5A) - -#define amd64_sse_cvtps2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0x5B) - -#define amd64_sse_cvtps2pd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0F, 0x5A) - -#define amd64_sse_cvttpd2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0x66, 0x0F, 0xE6) - -#define amd64_sse_cvttps2dq_reg_reg(inst, dreg, reg) emit_sse_reg_reg((inst), (dreg), (reg), 0xF3, 0x0F, 0x5B) - - -#define amd64_movd_xreg_reg_size(inst,dreg,sreg,size) emit_sse_reg_reg_size((inst), (dreg), (sreg), 0x66, 0x0f, 0x6e, (size)) - -#define amd64_movd_reg_xreg_size(inst,dreg,sreg,size) emit_sse_reg_reg_size((inst), (sreg), (dreg), 0x66, 0x0f, 0x7e, (size)) - -#define amd64_movd_xreg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase((inst), (dreg), (basereg), (disp), 0x66, 0x0f, 0x6e) - - -#define amd64_movlhps_reg_reg(inst,dreg,sreg) emit_sse_reg_reg_op2((inst), (dreg), (sreg), 0x0f, 0x16) - -#define amd64_movhlps_reg_reg(inst,dreg,sreg) emit_sse_reg_reg_op2((inst), (dreg), (sreg), 0x0f, 0x12) - - -#define amd64_sse_movups_membase_reg(inst, basereg, disp, reg) emit_sse_membase_reg_op2((inst), (basereg), (disp), (reg), 0x0f, 0x11) - -#define amd64_sse_movups_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x10) - -#define amd64_sse_movaps_membase_reg(inst, basereg, disp, reg) emit_sse_membase_reg_op2((inst), (basereg), (disp), (reg), 0x0f, 0x29) - -#define amd64_sse_movaps_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x28) - -#define amd64_sse_movaps_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op2((inst), (dreg), (reg), 0x0f, 0x28) - -#define amd64_sse_movntps_reg_membase(inst, dreg, basereg, disp) emit_sse_reg_membase_op2((inst), (dreg), (basereg), (disp), 0x0f, 0x2b) - -#define amd64_sse_prefetch_reg_membase(inst, arg, basereg, disp) emit_sse_reg_membase_op2((inst), (arg), (basereg), (disp), 0x0f, 0x18) - -/* Generated from x86-codegen.h */ - -#define amd64_breakpoint_size(inst,size) do { x86_breakpoint(inst); } while (0) -#define amd64_cld_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_cld(inst); amd64_codegen_post(inst); } while (0) -#define amd64_stosb_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosb(inst); amd64_codegen_post(inst); } while (0) -#define amd64_stosl_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosl(inst); amd64_codegen_post(inst); } while (0) -#define amd64_stosd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_stosd(inst); amd64_codegen_post(inst); } while (0) -#define amd64_movsb_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsb(inst); amd64_codegen_post(inst); } while (0) -#define amd64_movsl_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsl(inst); amd64_codegen_post(inst); } while (0) -#define amd64_movsd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_movsd(inst); amd64_codegen_post(inst); } while (0) -#define amd64_prefix_size(inst,p,size) do { x86_prefix((inst), p); } while (0) -#define amd64_rdtsc_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_rdtsc(inst); amd64_codegen_post(inst); } while (0) -#define amd64_cmpxchg_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_cmpxchg_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_cmpxchg_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_cmpxchg_mem_reg((inst),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_cmpxchg_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_cmpxchg_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_xchg_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_xchg_reg_reg((inst),((dreg)&0x7),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_xchg_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_xchg_mem_reg((inst),(mem),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_xchg_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_xchg_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_inc_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_inc_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_inc_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_inc_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -//#define amd64_inc_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_inc_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_dec_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_dec_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_dec_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_dec_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -//#define amd64_dec_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_dec_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_not_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_not_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_not_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_not_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_not_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_not_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_neg_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_neg_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_neg_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_neg_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_neg_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_neg_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_nop_size(inst,size) do { amd64_codegen_pre(inst); x86_nop(inst); amd64_codegen_post(inst); } while (0) -//#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_reg_imm((inst),(opc),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_alu_mem_imm_size(inst,opc,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_alu_mem_imm((inst),(opc),(mem),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_alu_membase_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_alu_membase_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_alu_membase8_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_alu_membase8_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_alu_mem_reg_size(inst,opc,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_mem_reg((inst),(opc),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_alu_membase_reg_size(inst,opc,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_alu_membase_reg((inst),(opc),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -//#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_alu_reg_reg((inst),(opc),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_alu_reg8_reg8_size(inst,opc,dreg,reg,is_dreg_h,is_reg_h,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_alu_reg8_reg8((inst),(opc),((dreg)&0x7),((reg)&0x7),(is_dreg_h),(is_reg_h)); amd64_codegen_post(inst); } while (0) -#define amd64_alu_reg_mem_size(inst,opc,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_reg_mem((inst),(opc),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) -//#define amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_alu_reg_membase((inst),(opc),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_test_reg_imm_size(inst,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_test_reg_imm((inst),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_test_mem_imm_size(inst,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_test_mem_imm((inst),(mem),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_test_membase_imm_size(inst,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_test_membase_imm((inst),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_test_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_test_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_test_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_test_mem_reg((inst),(mem),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_test_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_test_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_shift_reg_imm_size(inst,opc,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_reg_imm((inst),(opc),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_shift_mem_imm_size(inst,opc,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_mem_imm((inst),(opc),(mem),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_shift_membase_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_shift_membase_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_shift_reg_size(inst,opc,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_reg((inst),(opc),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_shift_mem_size(inst,opc,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_mem((inst),(opc),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_shift_membase_size(inst,opc,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_shift_membase((inst),(opc),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_shrd_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shrd_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_shrd_reg_imm_size(inst,dreg,reg,shamt,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shrd_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(shamt)); amd64_codegen_post(inst); } while (0) -#define amd64_shld_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shld_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_shld_reg_imm_size(inst,dreg,reg,shamt,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shld_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(shamt)); amd64_codegen_post(inst); } while (0) -#define amd64_mul_reg_size(inst,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mul_reg((inst),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_mul_mem_size(inst,mem,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_mul_mem((inst),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_mul_membase_size(inst,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_mul_membase((inst),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_imul_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_imul_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_imul_reg_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_imul_reg_mem((inst),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_imul_reg_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_imul_reg_membase((inst),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_imul_reg_reg_imm_size(inst,dreg,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_imul_reg_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_imul_reg_mem_imm_size(inst,reg,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_imul_reg_mem_imm((inst),((reg)&0x7),(mem),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_imul_reg_membase_imm_size(inst,reg,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_imul_reg_membase_imm((inst),((reg)&0x7),((basereg)&0x7),(disp),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_div_reg_size(inst,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_div_reg((inst),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_div_mem_size(inst,mem,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_div_mem((inst),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_div_membase_size(inst,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_div_membase((inst),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_mov_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_mem_reg((inst),(mem),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -//#define amd64_mov_regp_reg_size(inst,regp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(regp),0,(reg)); x86_mov_regp_reg((inst),(regp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -//#define amd64_mov_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_mov_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_mov_memindex_reg_size(inst,basereg,disp,indexreg,shift,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_mov_memindex_reg((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_mov_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_mov_reg_reg((inst),((dreg)&0x7),((reg)&0x7),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -//#define amd64_mov_reg_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_reg_mem((inst),((reg)&0x7),(mem),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -//#define amd64_mov_reg_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_mov_reg_membase((inst),((reg)&0x7),((basereg)&0x7),(disp),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -//#define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_mov_reg_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_clear_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_clear_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -//#define amd64_mov_reg_imm_size(inst,reg,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_reg_imm((inst),((reg)&0x7),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_mov_mem_imm_size(inst,mem,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_mov_mem_imm((inst),(mem),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -//#define amd64_mov_membase_imm_size(inst,basereg,disp,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_mov_membase_imm((inst),((basereg)&0x7),(disp),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_mov_memindex_imm_size(inst,basereg,disp,indexreg,shift,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,(indexreg),(basereg)); x86_mov_memindex_imm((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(imm),(size) == 8 ? 4 : (size)); amd64_codegen_post(inst); } while (0) -#define amd64_lea_mem_size(inst,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_lea_mem((inst),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) -//#define amd64_lea_membase_size(inst,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_lea_membase((inst),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_lea_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_lea_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift)); amd64_codegen_post(inst); } while (0) -#define amd64_widen_reg_size(inst,dreg,reg,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_widen_reg((inst),((dreg)&0x7),((reg)&0x7),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) -#define amd64_widen_mem_size(inst,dreg,mem,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,0); x86_widen_mem((inst),((dreg)&0x7),(mem),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) -#define amd64_widen_membase_size(inst,dreg,basereg,disp,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(basereg)); x86_widen_membase((inst),((dreg)&0x7),((basereg)&0x7),(disp),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) -#define amd64_widen_memindex_size(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),(indexreg),(basereg)); x86_widen_memindex((inst),((dreg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(is_signed),(is_half)); amd64_codegen_post(inst); } while (0) -#define amd64_cdq_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_cdq(inst); amd64_codegen_post(inst); } while (0) -#define amd64_wait_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_wait(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fp_op_mem_size(inst,opc,mem,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op_mem((inst),(opc),(mem),(is_double)); amd64_codegen_post(inst); } while (0) -#define amd64_fp_op_membase_size(inst,opc,basereg,disp,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fp_op_membase((inst),(opc),((basereg)&0x7),(disp),(is_double)); amd64_codegen_post(inst); } while (0) -#define amd64_fp_op_size(inst,opc,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op((inst),(opc),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fp_op_reg_size(inst,opc,index,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fp_op_reg((inst),(opc),(index),(pop_stack)); amd64_codegen_post(inst); } while (0) -#define amd64_fp_int_op_membase_size(inst,opc,basereg,disp,is_int,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fp_int_op_membase((inst),(opc),((basereg)&0x7),(disp),(is_int)); amd64_codegen_post(inst); } while (0) -#define amd64_fstp_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fstp((inst),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fcompp_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcompp(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fucompp_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucompp(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fnstsw_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fnstsw(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fnstcw_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fnstcw((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_fnstcw_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_fnstcw_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_fldcw_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldcw((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_fldcw_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fldcw_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_fchs_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fchs(inst); amd64_codegen_post(inst); } while (0) -#define amd64_frem_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_frem(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fxch_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fxch((inst),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fcomi_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcomi((inst),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fcomip_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fcomip((inst),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fucomi_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucomi((inst),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fucomip_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fucomip((inst),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fld_size(inst,mem,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld((inst),(mem),(is_double)); amd64_codegen_post(inst); } while (0) -//#define amd64_fld_membase_size(inst,basereg,disp,is_double,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fld_membase((inst),((basereg)&0x7),(disp),(is_double)); amd64_codegen_post(inst); } while (0) -#define amd64_fld80_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld80_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_fld80_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_fld80_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_fild_size(inst,mem,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fild((inst),(mem),(is_long)); amd64_codegen_post(inst); } while (0) -#define amd64_fild_membase_size(inst,basereg,disp,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fild_membase((inst),((basereg)&0x7),(disp),(is_long)); amd64_codegen_post(inst); } while (0) -#define amd64_fld_reg_size(inst,index,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld_reg((inst),(index)); amd64_codegen_post(inst); } while (0) -#define amd64_fldz_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldz(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fld1_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fld1(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fldpi_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fldpi(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fst_size(inst,mem,is_double,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fst((inst),(mem),(is_double),(pop_stack)); amd64_codegen_post(inst); } while (0) -#define amd64_fst_membase_size(inst,basereg,disp,is_double,pop_stack,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fst_membase((inst),((basereg)&0x7),(disp),(is_double),(pop_stack)); amd64_codegen_post(inst); } while (0) -#define amd64_fst80_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fst80_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_fst80_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fst80_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_fist_pop_size(inst,mem,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_fist_pop((inst),(mem),(is_long)); amd64_codegen_post(inst); } while (0) -#define amd64_fist_pop_membase_size(inst,basereg,disp,is_long,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fist_pop_membase((inst),((basereg)&0x7),(disp),(is_long)); amd64_codegen_post(inst); } while (0) -#define amd64_fstsw_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_fstsw(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fist_membase_size(inst,basereg,disp,is_int,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fist_membase((inst),((basereg)&0x7),(disp),(is_int)); amd64_codegen_post(inst); } while (0) -//#define amd64_push_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_push_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_push_regp_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_push_regp((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_push_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_push_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -//#define amd64_push_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_push_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_push_memindex_size(inst,basereg,disp,indexreg,shift,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,(indexreg),(basereg)); x86_push_memindex((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift)); amd64_codegen_post(inst); } while (0) -#define amd64_push_imm_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_push_imm((inst),(imm)); amd64_codegen_post(inst); } while (0) -//#define amd64_pop_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_pop_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_pop_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pop_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_pop_membase_size(inst,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_pop_membase((inst),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_pushad_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pushad(inst); amd64_codegen_post(inst); } while (0) -#define amd64_pushfd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_pushfd(inst); amd64_codegen_post(inst); } while (0) -#define amd64_popad_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_popad(inst); amd64_codegen_post(inst); } while (0) -#define amd64_popfd_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_popfd(inst); amd64_codegen_post(inst); } while (0) -#define amd64_loop_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loop((inst),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_loope_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loope((inst),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_loopne_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_loopne((inst),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_jump32_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_jump32((inst),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_jump8_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_jump8((inst),(imm)); amd64_codegen_post(inst); } while (0) -#if !defined( __native_client_codegen__ ) -/* Defined above for Native Client, so they can be used in other macros */ -#define amd64_jump_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),0,0,0,(reg)); x86_jump_reg((inst),((reg)&0x7)); } while (0) -#define amd64_jump_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_jump_mem((inst),(mem)); } while (0) -#endif -#define amd64_jump_disp_size(inst,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,0); x86_jump_disp((inst),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_branch8_size(inst,cond,imm,is_signed,size) do { x86_branch8((inst),(cond),(imm),(is_signed)); } while (0) -#define amd64_branch32_size(inst,cond,imm,is_signed,size) do { x86_branch32((inst),(cond),(imm),(is_signed)); } while (0) -#define amd64_branch_size_body(inst,cond,target,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_branch((inst),(cond),(target),(is_signed)); amd64_codegen_post(inst); } while (0) -#if defined(__default_codegen__) -#define amd64_branch_size(inst,cond,target,is_signed,size) do { amd64_branch_size_body((inst),(cond),(target),(is_signed),(size)); } while (0) -#elif defined(__native_client_codegen__) -#define amd64_branch_size(inst,cond,target,is_signed,size) \ - do { \ - /* amd64_branch_size_body used twice in */ \ - /* case of relocation by amd64_codegen_post */ \ - guint8* branch_start; \ - amd64_codegen_pre(inst); \ - amd64_branch_size_body((inst),(cond),(target),(is_signed),(size)); \ - inst = amd64_codegen_post(inst); \ - branch_start = inst; \ - amd64_branch_size_body((inst),(cond),(target),(is_signed),(size)); \ - mono_amd64_patch(branch_start, (target)); \ - } while (0) -#endif - -#define amd64_branch_disp_size(inst,cond,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_branch_disp((inst),(cond),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_set_reg_size(inst,cond,reg,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex((inst),1,0,0,(reg)); x86_set_reg((inst),(cond),((reg)&0x7),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_set_mem_size(inst,cond,mem,is_signed,size) do { amd64_codegen_pre(inst); x86_set_mem((inst),(cond),(mem),(is_signed)); amd64_codegen_post(inst); } while (0) -#define amd64_set_membase_size(inst,cond,basereg,disp,is_signed,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),0,0,0,(basereg)); x86_set_membase((inst),(cond),((basereg)&0x7),(disp),(is_signed)); amd64_codegen_post(inst); } while (0) -//#define amd64_call_reg_size(inst,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_call_reg((inst),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_call_mem_size(inst,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_call_mem((inst),(mem)); amd64_codegen_post(inst); } while (0) - -#if defined(__default_codegen__) - -#define amd64_call_imm_size(inst,disp,size) do { x86_call_imm((inst),(disp)); } while (0) -#define amd64_call_code_size(inst,target,size) do { x86_call_code((inst),(target)); } while (0) - -#elif defined(__native_client_codegen__) -/* Size is ignored for Native Client calls, we restrict jumping to 32-bits */ -#define amd64_call_imm_size(inst,disp,size) \ - do { \ - amd64_codegen_pre((inst)); \ - amd64_call_sequence_pre((inst)); \ - x86_call_imm((inst),(disp)); \ - amd64_call_sequence_post((inst)); \ - amd64_codegen_post((inst)); \ - } while (0) - -/* x86_call_code is called twice below, first so we can get the size of the */ -/* call sequence, and again so the exact offset from "inst" is used, since */ -/* the sequence could have moved from amd64_call_sequence_post. */ -/* Size is ignored for Native Client jumps, we restrict jumping to 32-bits */ -#define amd64_call_code_size(inst,target,size) \ - do { \ - amd64_codegen_pre((inst)); \ - guint8* adjusted_start; \ - guint8* call_start; \ - amd64_call_sequence_pre((inst)); \ - x86_call_code((inst),(target)); \ - adjusted_start = amd64_call_sequence_post((inst)); \ - call_start = adjusted_start; \ - x86_call_code(adjusted_start, (target)); \ - amd64_codegen_post((inst)); \ - mono_amd64_patch(call_start, (target)); \ - } while (0) - -#endif /*__native_client_codegen__*/ - -//#define amd64_ret_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_ret(inst); amd64_codegen_post(inst); } while (0) -#define amd64_ret_imm_size(inst,imm,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_ret_imm((inst),(imm)); amd64_codegen_post(inst); } while (0) -#define amd64_cmov_reg_size(inst,cond,is_signed,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_cmov_reg((inst),(cond),(is_signed),((dreg)&0x7),((reg)&0x7)); amd64_codegen_post(inst); } while (0) -#define amd64_cmov_mem_size(inst,cond,is_signed,reg,mem,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_cmov_mem((inst),(cond),(is_signed),((reg)&0x7),(mem)); amd64_codegen_post(inst); } while (0) -#define amd64_cmov_membase_size(inst,cond,is_signed,reg,basereg,disp,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_cmov_membase((inst),(cond),(is_signed),((reg)&0x7),((basereg)&0x7),(disp)); amd64_codegen_post(inst); } while (0) -#define amd64_enter_size(inst,framesize) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_enter((inst),(framesize)); amd64_codegen_post(inst); } while (0) -//#define amd64_leave_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_leave(inst); amd64_codegen_post(inst); } while (0) -#define amd64_sahf_size(inst,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_sahf(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fsin_size(inst,size) do { amd64_codegen_pre(inst); x86_fsin(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fcos_size(inst,size) do { amd64_codegen_pre(inst); x86_fcos(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fabs_size(inst,size) do { amd64_codegen_pre(inst); x86_fabs(inst); amd64_codegen_post(inst); } while (0) -#define amd64_ftst_size(inst,size) do { amd64_codegen_pre(inst); x86_ftst(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fxam_size(inst,size) do { amd64_codegen_pre(inst); x86_fxam(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fpatan_size(inst,size) do { amd64_codegen_pre(inst); x86_fpatan(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fprem_size(inst,size) do { amd64_codegen_pre(inst); x86_fprem(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fprem1_size(inst,size) do { amd64_codegen_pre(inst); x86_fprem1(inst); amd64_codegen_post(inst); } while (0) -#define amd64_frndint_size(inst,size) do { amd64_codegen_pre(inst); x86_frndint(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fsqrt_size(inst,size) do { amd64_codegen_pre(inst); x86_fsqrt(inst); amd64_codegen_post(inst); } while (0) -#define amd64_fptan_size(inst,size) do { amd64_codegen_pre(inst); x86_fptan(inst); amd64_codegen_post(inst); } while (0) -//#define amd64_padding_size(inst,size) do { amd64_codegen_pre(inst); x86_padding((inst),(size)); amd64_codegen_post(inst); } while (0) -#define amd64_prolog_size(inst,frame_size,reg_mask,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_prolog((inst),(frame_size),(reg_mask)); amd64_codegen_post(inst); } while (0) -#define amd64_epilog_size(inst,reg_mask,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,0); x86_epilog((inst),(reg_mask)); amd64_codegen_post(inst); } while (0) -#define amd64_xadd_reg_reg_size(inst,dreg,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_xadd_reg_reg ((inst), (dreg), (reg), (size)); amd64_codegen_post(inst); } while (0) -#define amd64_xadd_mem_reg_size(inst,mem,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),0,0,(reg)); x86_xadd_mem_reg((inst),(mem),((reg)&0x7), (size)); amd64_codegen_post(inst); } while (0) -#define amd64_xadd_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_codegen_pre(inst); amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_xadd_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size)); amd64_codegen_post(inst); } while (0) - - - - -#define amd64_breakpoint(inst) amd64_breakpoint_size(inst,8) -#define amd64_cld(inst) amd64_cld_size(inst,8) -#define amd64_stosb(inst) amd64_stosb_size(inst,8) -#define amd64_stosl(inst) amd64_stosl_size(inst,8) -#define amd64_stosd(inst) amd64_stosd_size(inst,8) -#define amd64_movsb(inst) amd64_movsb_size(inst,8) -#define amd64_movsl(inst) amd64_movsl_size(inst,8) -#define amd64_movsd(inst) amd64_movsd_size(inst,8) -#define amd64_prefix(inst,p) amd64_prefix_size(inst,p,8) -#define amd64_rdtsc(inst) amd64_rdtsc_size(inst,8) -#define amd64_cmpxchg_reg_reg(inst,dreg,reg) amd64_cmpxchg_reg_reg_size(inst,dreg,reg,8) -#define amd64_cmpxchg_mem_reg(inst,mem,reg) amd64_cmpxchg_mem_reg_size(inst,mem,reg,8) -#define amd64_cmpxchg_membase_reg(inst,basereg,disp,reg) amd64_cmpxchg_membase_reg_size(inst,basereg,disp,reg,8) -#define amd64_xchg_reg_reg(inst,dreg,reg,size) amd64_xchg_reg_reg_size(inst,dreg,reg,size) -#define amd64_xchg_mem_reg(inst,mem,reg,size) amd64_xchg_mem_reg_size(inst,mem,reg,size) -#define amd64_xchg_membase_reg(inst,basereg,disp,reg,size) amd64_xchg_membase_reg_size(inst,basereg,disp,reg,size) -#define amd64_xadd_reg_reg(inst,dreg,reg,size) amd64_xadd_reg_reg_size(inst,dreg,reg,size) -#define amd64_xadd_mem_reg(inst,mem,reg,size) amd64_xadd_mem_reg_size(inst,mem,reg,size) -#define amd64_xadd_membase_reg(inst,basereg,disp,reg,size) amd64_xadd_membase_reg_size(inst,basereg,disp,reg,size) -#define amd64_inc_mem(inst,mem) amd64_inc_mem_size(inst,mem,8) -#define amd64_inc_membase(inst,basereg,disp) amd64_inc_membase_size(inst,basereg,disp,8) -#define amd64_inc_reg(inst,reg) amd64_inc_reg_size(inst,reg,8) -#define amd64_dec_mem(inst,mem) amd64_dec_mem_size(inst,mem,8) -#define amd64_dec_membase(inst,basereg,disp) amd64_dec_membase_size(inst,basereg,disp,8) -#define amd64_dec_reg(inst,reg) amd64_dec_reg_size(inst,reg,8) -#define amd64_not_mem(inst,mem) amd64_not_mem_size(inst,mem,8) -#define amd64_not_membase(inst,basereg,disp) amd64_not_membase_size(inst,basereg,disp,8) -#define amd64_not_reg(inst,reg) amd64_not_reg_size(inst,reg,8) -#define amd64_neg_mem(inst,mem) amd64_neg_mem_size(inst,mem,8) -#define amd64_neg_membase(inst,basereg,disp) amd64_neg_membase_size(inst,basereg,disp,8) -#define amd64_neg_reg(inst,reg) amd64_neg_reg_size(inst,reg,8) -#define amd64_nop(inst) amd64_nop_size(inst,8) -//#define amd64_alu_reg_imm(inst,opc,reg,imm) amd64_alu_reg_imm_size(inst,opc,reg,imm,8) -#define amd64_alu_mem_imm(inst,opc,mem,imm) amd64_alu_mem_imm_size(inst,opc,mem,imm,8) -#define amd64_alu_membase_imm(inst,opc,basereg,disp,imm) amd64_alu_membase_imm_size(inst,opc,basereg,disp,imm,8) -#define amd64_alu_mem_reg(inst,opc,mem,reg) amd64_alu_mem_reg_size(inst,opc,mem,reg,8) -#define amd64_alu_membase_reg(inst,opc,basereg,disp,reg) amd64_alu_membase_reg_size(inst,opc,basereg,disp,reg,8) -//#define amd64_alu_reg_reg(inst,opc,dreg,reg) amd64_alu_reg_reg_size(inst,opc,dreg,reg,8) -#define amd64_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) amd64_alu_reg8_reg8_size(inst,opc,dreg,reg,is_dreg_h,is_reg_h,8) -#define amd64_alu_reg_mem(inst,opc,reg,mem) amd64_alu_reg_mem_size(inst,opc,reg,mem,8) -#define amd64_alu_reg_membase(inst,opc,reg,basereg,disp) amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,8) -#define amd64_test_reg_imm(inst,reg,imm) amd64_test_reg_imm_size(inst,reg,imm,8) -#define amd64_test_mem_imm(inst,mem,imm) amd64_test_mem_imm_size(inst,mem,imm,8) -#define amd64_test_membase_imm(inst,basereg,disp,imm) amd64_test_membase_imm_size(inst,basereg,disp,imm,8) -#define amd64_test_reg_reg(inst,dreg,reg) amd64_test_reg_reg_size(inst,dreg,reg,8) -#define amd64_test_mem_reg(inst,mem,reg) amd64_test_mem_reg_size(inst,mem,reg,8) -#define amd64_test_membase_reg(inst,basereg,disp,reg) amd64_test_membase_reg_size(inst,basereg,disp,reg,8) -#define amd64_shift_reg_imm(inst,opc,reg,imm) amd64_shift_reg_imm_size(inst,opc,reg,imm,8) -#define amd64_shift_mem_imm(inst,opc,mem,imm) amd64_shift_mem_imm_size(inst,opc,mem,imm,8) -#define amd64_shift_membase_imm(inst,opc,basereg,disp,imm) amd64_shift_membase_imm_size(inst,opc,basereg,disp,imm,8) -#define amd64_shift_reg(inst,opc,reg) amd64_shift_reg_size(inst,opc,reg,8) -#define amd64_shift_mem(inst,opc,mem) amd64_shift_mem_size(inst,opc,mem,8) -#define amd64_shift_membase(inst,opc,basereg,disp) amd64_shift_membase_size(inst,opc,basereg,disp,8) -#define amd64_shrd_reg(inst,dreg,reg) amd64_shrd_reg_size(inst,dreg,reg,8) -#define amd64_shrd_reg_imm(inst,dreg,reg,shamt) amd64_shrd_reg_imm_size(inst,dreg,reg,shamt,8) -#define amd64_shld_reg(inst,dreg,reg) amd64_shld_reg_size(inst,dreg,reg,8) -#define amd64_shld_reg_imm(inst,dreg,reg,shamt) amd64_shld_reg_imm_size(inst,dreg,reg,shamt,8) -#define amd64_mul_reg(inst,reg,is_signed) amd64_mul_reg_size(inst,reg,is_signed,8) -#define amd64_mul_mem(inst,mem,is_signed) amd64_mul_mem_size(inst,mem,is_signed,8) -#define amd64_mul_membase(inst,basereg,disp,is_signed) amd64_mul_membase_size(inst,basereg,disp,is_signed,8) -#define amd64_imul_reg_reg(inst,dreg,reg) amd64_imul_reg_reg_size(inst,dreg,reg,8) -#define amd64_imul_reg_mem(inst,reg,mem) amd64_imul_reg_mem_size(inst,reg,mem,8) -#define amd64_imul_reg_membase(inst,reg,basereg,disp) amd64_imul_reg_membase_size(inst,reg,basereg,disp,8) -#define amd64_imul_reg_reg_imm(inst,dreg,reg,imm) amd64_imul_reg_reg_imm_size(inst,dreg,reg,imm,8) -#define amd64_imul_reg_mem_imm(inst,reg,mem,imm) amd64_imul_reg_mem_imm_size(inst,reg,mem,imm,8) -#define amd64_imul_reg_membase_imm(inst,reg,basereg,disp,imm) amd64_imul_reg_membase_imm_size(inst,reg,basereg,disp,imm,8) -#define amd64_div_reg(inst,reg,is_signed) amd64_div_reg_size(inst,reg,is_signed,8) -#define amd64_div_mem(inst,mem,is_signed) amd64_div_mem_size(inst,mem,is_signed,8) -#define amd64_div_membase(inst,basereg,disp,is_signed) amd64_div_membase_size(inst,basereg,disp,is_signed,8) -//#define amd64_mov_mem_reg(inst,mem,reg,size) amd64_mov_mem_reg_size(inst,mem,reg,size) -//#define amd64_mov_regp_reg(inst,regp,reg,size) amd64_mov_regp_reg_size(inst,regp,reg,size) -//#define amd64_mov_membase_reg(inst,basereg,disp,reg,size) amd64_mov_membase_reg_size(inst,basereg,disp,reg,size) -#define amd64_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) amd64_mov_memindex_reg_size(inst,basereg,disp,indexreg,shift,reg,size) -//#define amd64_mov_reg_reg(inst,dreg,reg,size) amd64_mov_reg_reg_size(inst,dreg,reg,size) -//#define amd64_mov_reg_mem(inst,reg,mem,size) amd64_mov_reg_mem_size(inst,reg,mem,size) -//#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) amd64_mov_reg_membase_size(inst,reg,basereg,disp,size) -#define amd64_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) -#define amd64_clear_reg(inst,reg) amd64_clear_reg_size(inst,reg,8) -//#define amd64_mov_reg_imm(inst,reg,imm) amd64_mov_reg_imm_size(inst,reg,imm,8) -#define amd64_mov_mem_imm(inst,mem,imm,size) amd64_mov_mem_imm_size(inst,mem,imm,size) -//#define amd64_mov_membase_imm(inst,basereg,disp,imm,size) amd64_mov_membase_imm_size(inst,basereg,disp,imm,size) -#define amd64_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) amd64_mov_memindex_imm_size(inst,basereg,disp,indexreg,shift,imm,size) -#define amd64_lea_mem(inst,reg,mem) amd64_lea_mem_size(inst,reg,mem,8) -//#define amd64_lea_membase(inst,reg,basereg,disp) amd64_lea_membase_size(inst,reg,basereg,disp,8) -#define amd64_lea_memindex(inst,reg,basereg,disp,indexreg,shift) amd64_lea_memindex_size(inst,reg,basereg,disp,indexreg,shift,8) -#define amd64_widen_reg(inst,dreg,reg,is_signed,is_half) amd64_widen_reg_size(inst,dreg,reg,is_signed,is_half,8) -#define amd64_widen_mem(inst,dreg,mem,is_signed,is_half) amd64_widen_mem_size(inst,dreg,mem,is_signed,is_half,8) -#define amd64_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) amd64_widen_membase_size(inst,dreg,basereg,disp,is_signed,is_half,8) -#define amd64_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) amd64_widen_memindex_size(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half,8) -#define amd64_cdq(inst) amd64_cdq_size(inst,8) -#define amd64_wait(inst) amd64_wait_size(inst,8) -#define amd64_fp_op_mem(inst,opc,mem,is_double) amd64_fp_op_mem_size(inst,opc,mem,is_double,8) -#define amd64_fp_op_membase(inst,opc,basereg,disp,is_double) amd64_fp_op_membase_size(inst,opc,basereg,disp,is_double,8) -#define amd64_fp_op(inst,opc,index) amd64_fp_op_size(inst,opc,index,8) -#define amd64_fp_op_reg(inst,opc,index,pop_stack) amd64_fp_op_reg_size(inst,opc,index,pop_stack,8) -#define amd64_fp_int_op_membase(inst,opc,basereg,disp,is_int) amd64_fp_int_op_membase_size(inst,opc,basereg,disp,is_int,8) -#define amd64_fstp(inst,index) amd64_fstp_size(inst,index,8) -#define amd64_fcompp(inst) amd64_fcompp_size(inst,8) -#define amd64_fucompp(inst) amd64_fucompp_size(inst,8) -#define amd64_fnstsw(inst) amd64_fnstsw_size(inst,8) -#define amd64_fnstcw(inst,mem) amd64_fnstcw_size(inst,mem,8) -#define amd64_fnstcw_membase(inst,basereg,disp) amd64_fnstcw_membase_size(inst,basereg,disp,8) -#define amd64_fldcw(inst,mem) amd64_fldcw_size(inst,mem,8) -#define amd64_fldcw_membase(inst,basereg,disp) amd64_fldcw_membase_size(inst,basereg,disp,8) -#define amd64_fchs(inst) amd64_fchs_size(inst,8) -#define amd64_frem(inst) amd64_frem_size(inst,8) -#define amd64_fxch(inst,index) amd64_fxch_size(inst,index,8) -#define amd64_fcomi(inst,index) amd64_fcomi_size(inst,index,8) -#define amd64_fcomip(inst,index) amd64_fcomip_size(inst,index,8) -#define amd64_fucomi(inst,index) amd64_fucomi_size(inst,index,8) -#define amd64_fucomip(inst,index) amd64_fucomip_size(inst,index,8) -#define amd64_fld(inst,mem,is_double) amd64_fld_size(inst,mem,is_double,8) -#define amd64_fld_membase(inst,basereg,disp,is_double) amd64_fld_membase_size(inst,basereg,disp,is_double,8) -#define amd64_fld80_mem(inst,mem) amd64_fld80_mem_size(inst,mem,8) -#define amd64_fld80_membase(inst,basereg,disp) amd64_fld80_membase_size(inst,basereg,disp,8) -#define amd64_fild(inst,mem,is_long) amd64_fild_size(inst,mem,is_long,8) -#define amd64_fild_membase(inst,basereg,disp,is_long) amd64_fild_membase_size(inst,basereg,disp,is_long,8) -#define amd64_fld_reg(inst,index) amd64_fld_reg_size(inst,index,8) -#define amd64_fldz(inst) amd64_fldz_size(inst,8) -#define amd64_fld1(inst) amd64_fld1_size(inst,8) -#define amd64_fldpi(inst) amd64_fldpi_size(inst,8) -#define amd64_fst(inst,mem,is_double,pop_stack) amd64_fst_size(inst,mem,is_double,pop_stack,8) -#define amd64_fst_membase(inst,basereg,disp,is_double,pop_stack) amd64_fst_membase_size(inst,basereg,disp,is_double,pop_stack,8) -#define amd64_fst80_mem(inst,mem) amd64_fst80_mem_size(inst,mem,8) -#define amd64_fst80_membase(inst,basereg,disp) amd64_fst80_membase_size(inst,basereg,disp,8) -#define amd64_fist_pop(inst,mem,is_long) amd64_fist_pop_size(inst,mem,is_long,8) -#define amd64_fist_pop_membase(inst,basereg,disp,is_long) amd64_fist_pop_membase_size(inst,basereg,disp,is_long,8) -#define amd64_fstsw(inst) amd64_fstsw_size(inst,8) -#define amd64_fist_membase(inst,basereg,disp,is_int) amd64_fist_membase_size(inst,basereg,disp,is_int,8) -//#define amd64_push_reg(inst,reg) amd64_push_reg_size(inst,reg,8) -#define amd64_push_regp(inst,reg) amd64_push_regp_size(inst,reg,8) -#define amd64_push_mem(inst,mem) amd64_push_mem_size(inst,mem,8) -//#define amd64_push_membase(inst,basereg,disp) amd64_push_membase_size(inst,basereg,disp,8) -#define amd64_push_memindex(inst,basereg,disp,indexreg,shift) amd64_push_memindex_size(inst,basereg,disp,indexreg,shift,8) -#define amd64_push_imm(inst,imm) amd64_push_imm_size(inst,imm,8) -//#define amd64_pop_reg(inst,reg) amd64_pop_reg_size(inst,reg,8) -#define amd64_pop_mem(inst,mem) amd64_pop_mem_size(inst,mem,8) -#define amd64_pop_membase(inst,basereg,disp) amd64_pop_membase_size(inst,basereg,disp,8) -#define amd64_pushad(inst) amd64_pushad_size(inst,8) -#define amd64_pushfd(inst) amd64_pushfd_size(inst,8) -#define amd64_popad(inst) amd64_popad_size(inst,8) -#define amd64_popfd(inst) amd64_popfd_size(inst,8) -#define amd64_loop(inst,imm) amd64_loop_size(inst,imm,8) -#define amd64_loope(inst,imm) amd64_loope_size(inst,imm,8) -#define amd64_loopne(inst,imm) amd64_loopne_size(inst,imm,8) -#define amd64_jump32(inst,imm) amd64_jump32_size(inst,imm,8) -#define amd64_jump8(inst,imm) amd64_jump8_size(inst,imm,8) -#define amd64_jump_reg(inst,reg) amd64_jump_reg_size(inst,reg,8) -#define amd64_jump_mem(inst,mem) amd64_jump_mem_size(inst,mem,8) -#define amd64_jump_membase(inst,basereg,disp) amd64_jump_membase_size(inst,basereg,disp,8) -#define amd64_jump_code(inst,target) amd64_jump_code_size(inst,target,8) -#define amd64_jump_disp(inst,disp) amd64_jump_disp_size(inst,disp,8) -#define amd64_branch8(inst,cond,imm,is_signed) amd64_branch8_size(inst,cond,imm,is_signed,8) -#define amd64_branch32(inst,cond,imm,is_signed) amd64_branch32_size(inst,cond,imm,is_signed,8) -#define amd64_branch(inst,cond,target,is_signed) amd64_branch_size(inst,cond,target,is_signed,8) -#define amd64_branch_disp(inst,cond,disp,is_signed) amd64_branch_disp_size(inst,cond,disp,is_signed,8) -#define amd64_set_reg(inst,cond,reg,is_signed) amd64_set_reg_size(inst,cond,reg,is_signed,8) -#define amd64_set_mem(inst,cond,mem,is_signed) amd64_set_mem_size(inst,cond,mem,is_signed,8) -#define amd64_set_membase(inst,cond,basereg,disp,is_signed) amd64_set_membase_size(inst,cond,basereg,disp,is_signed,8) -#define amd64_call_imm(inst,disp) amd64_call_imm_size(inst,disp,8) -//#define amd64_call_reg(inst,reg) amd64_call_reg_size(inst,reg,8) -#define amd64_call_mem(inst,mem) amd64_call_mem_size(inst,mem,8) -#define amd64_call_membase(inst,basereg,disp) amd64_call_membase_size(inst,basereg,disp,8) -#define amd64_call_code(inst,target) amd64_call_code_size(inst,target,8) -//#define amd64_ret(inst) amd64_ret_size(inst,8) -#define amd64_ret_imm(inst,imm) amd64_ret_imm_size(inst,imm,8) -#define amd64_cmov_reg(inst,cond,is_signed,dreg,reg) amd64_cmov_reg_size(inst,cond,is_signed,dreg,reg,8) -#define amd64_cmov_mem(inst,cond,is_signed,reg,mem) amd64_cmov_mem_size(inst,cond,is_signed,reg,mem,8) -#define amd64_cmov_membase(inst,cond,is_signed,reg,basereg,disp) amd64_cmov_membase_size(inst,cond,is_signed,reg,basereg,disp,8) -#define amd64_enter(inst,framesize) amd64_enter_size(inst,framesize) -//#define amd64_leave(inst) amd64_leave_size(inst,8) -#define amd64_sahf(inst) amd64_sahf_size(inst,8) -#define amd64_fsin(inst) amd64_fsin_size(inst,8) -#define amd64_fcos(inst) amd64_fcos_size(inst,8) -#define amd64_fabs(inst) amd64_fabs_size(inst,8) -#define amd64_ftst(inst) amd64_ftst_size(inst,8) -#define amd64_fxam(inst) amd64_fxam_size(inst,8) -#define amd64_fpatan(inst) amd64_fpatan_size(inst,8) -#define amd64_fprem(inst) amd64_fprem_size(inst,8) -#define amd64_fprem1(inst) amd64_fprem1_size(inst,8) -#define amd64_frndint(inst) amd64_frndint_size(inst,8) -#define amd64_fsqrt(inst) amd64_fsqrt_size(inst,8) -#define amd64_fptan(inst) amd64_fptan_size(inst,8) -#define amd64_padding(inst,size) amd64_padding_size(inst,size) -#define amd64_prolog(inst,frame,reg_mask) amd64_prolog_size(inst,frame,reg_mask,8) -#define amd64_epilog(inst,reg_mask) amd64_epilog_size(inst,reg_mask,8) - -#endif // AMD64_H diff --git a/asm/arm-codegen.c b/asm/arm-codegen.c deleted file mode 100644 index 9914ace34f..0000000000 --- a/asm/arm-codegen.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * arm-codegen.c - * Copyright (c) 2002 Sergey Chaban - */ - -#include "arm-codegen.h" - - -arminstr_t* arm_emit_std_prologue(arminstr_t* p, unsigned int local_size) { - ARM_MOV_REG_REG(p, ARMREG_IP, ARMREG_SP); - - /* save args */ - ARM_PUSH(p, (1 << ARMREG_A1) - | (1 << ARMREG_A2) - | (1 << ARMREG_A3) - | (1 << ARMREG_A4)); - - ARM_PUSH(p, (1U << ARMREG_IP) | (1U << ARMREG_LR)); - - if (local_size != 0) { - if ((local_size & (~0xFF)) == 0) { - ARM_SUB_REG_IMM8(p, ARMREG_SP, ARMREG_SP, local_size); - } else { - /* TODO: optimize */ - p = arm_mov_reg_imm32(p, ARMREG_IP, local_size); - ARM_SUB_REG_REG(p, ARMREG_SP, ARMREG_SP, ARMREG_IP); - ARM_ADD_REG_IMM8(p, ARMREG_IP, ARMREG_IP, sizeof(armword_t)); - ARM_LDR_REG_REG(p, ARMREG_IP, ARMREG_SP, ARMREG_IP); - } - } - - return p; -} - -arminstr_t* arm_emit_std_epilogue(arminstr_t* p, unsigned int local_size, int pop_regs) { - if (local_size != 0) { - if ((local_size & (~0xFF)) == 0) { - ARM_ADD_REG_IMM8(p, ARMREG_SP, ARMREG_SP, local_size); - } else { - /* TODO: optimize */ - p = arm_mov_reg_imm32(p, ARMREG_IP, local_size); - ARM_ADD_REG_REG(p, ARMREG_SP, ARMREG_SP, ARMREG_IP); - } - } - - ARM_POP_NWB(p, (1 << ARMREG_SP) | (1 << ARMREG_PC) | (pop_regs & 0x3FF)); - - return p; -} - - -/* do not push A1-A4 */ -arminstr_t* arm_emit_lean_prologue(arminstr_t* p, unsigned int local_size, int push_regs) { - ARM_MOV_REG_REG(p, ARMREG_IP, ARMREG_SP); - /* push_regs upto R10 will be saved */ - ARM_PUSH(p, (1U << ARMREG_IP) | (1U << ARMREG_LR) | (push_regs & 0x3FF)); - - if (local_size != 0) { - if ((local_size & (~0xFF)) == 0) { - ARM_SUB_REG_IMM8(p, ARMREG_SP, ARMREG_SP, local_size); - } else { - /* TODO: optimize */ - p = arm_mov_reg_imm32(p, ARMREG_IP, local_size); - ARM_SUB_REG_REG(p, ARMREG_SP, ARMREG_SP, ARMREG_IP); - /* restore IP from stack */ - ARM_ADD_REG_IMM8(p, ARMREG_IP, ARMREG_IP, sizeof(armword_t)); - ARM_LDR_REG_REG(p, ARMREG_IP, ARMREG_SP, ARMREG_IP); - } - } - - return p; -} - -/* Bit scan forward. */ -int arm_bsf(armword_t val) { - int i; - armword_t mask; - - if (val == 0) return 0; - for (i=1, mask=1; (i <= 8 * sizeof(armword_t)) && ((val & mask) == 0); ++i, mask<<=1); - - return i; -} - - -int arm_is_power_of_2(armword_t val) { - return ((val & (val-1)) == 0); -} - - -/* - * returns: - * 1 - unable to represent - * positive even number - MOV-representable - * negative even number - MVN-representable - */ -int calc_arm_mov_const_shift(armword_t val) { - armword_t mask; - int res = 1, shift; - - for (shift=0; shift < 32; shift+=2) { - mask = ARM_SCALE(0xFF, shift); - if ((val & (~mask)) == 0) { - res = shift; - break; - } - if (((~val) & (~mask)) == 0) { - res = -shift - 2; - break; - } - } - - return res; -} - - -int is_arm_const(armword_t val) { - int res; - res = arm_is_power_of_2(val); - if (!res) { - res = calc_arm_mov_const_shift(val); - res = !(res < 0 || res == 1); - } - return res; -} - - -int arm_const_steps(armword_t val) { - int shift, steps = 0; - - while (val != 0) { - shift = (arm_bsf(val) - 1) & (~1); - val &= ~(0xFF << shift); - ++steps; - } - return steps; -} - - -/* - * ARM cannot load arbitrary 32-bit constants directly into registers; - * widely used work-around for this is to store constants into a - * PC-addressable pool and use LDR instruction with PC-relative address - * to load constant into register. Easiest way to implement this is to - * embed constant inside a function with unconditional branch around it. - * The above method is not used at the moment. - * This routine always emits sequence of instructions to generate - * requested constant. In the worst case it takes 4 instructions to - * synthesize a constant - 1 MOV and 3 subsequent ORRs. - */ -arminstr_t* arm_mov_reg_imm32_cond(arminstr_t* p, int reg, armword_t imm32, int cond) { - int mov_op; - int step_op; - int snip; - int shift = calc_arm_mov_const_shift(imm32); - - if ((shift & 0x80000001) != 1) { - if (shift >= 0) { - ARM_MOV_REG_IMM_COND(p, reg, imm32 >> ((32 - shift) & 31), shift, cond); - } else { - ARM_MVN_REG_IMM_COND(p, reg, (imm32 ^ (~0)) >> ((32 + 2 + shift) & 31), (-shift - 2), cond); - } - } else { - mov_op = ARMOP_MOV; - step_op = ARMOP_ORR; - - if (arm_const_steps(imm32) > arm_const_steps(~imm32)) { - mov_op = ARMOP_MVN; - step_op = ARMOP_SUB; - imm32 = ~imm32; - } - - shift = (arm_bsf(imm32) - 1) & (~1); - snip = imm32 & (0xFF << shift); - ARM_EMIT(p, ARM_DEF_DPI_IMM_COND((unsigned)snip >> shift, (32 - shift) >> 1, reg, 0, 0, mov_op, cond)); - - while ((imm32 ^= snip) != 0) { - shift = (arm_bsf(imm32) - 1) & (~1); - snip = imm32 & (0xFF << shift); - ARM_EMIT(p, ARM_DEF_DPI_IMM_COND((unsigned)snip >> shift, (32 - shift) >> 1, reg, reg, 0, step_op, cond)); - } - } - - return p; -} - - -arminstr_t* arm_mov_reg_imm32(arminstr_t* p, int reg, armword_t imm32) { - return arm_mov_reg_imm32_cond(p, reg, imm32, ARMCOND_AL); -} - - - diff --git a/asm/arm-codegen.h b/asm/arm-codegen.h deleted file mode 100644 index 5be4e9f955..0000000000 --- a/asm/arm-codegen.h +++ /dev/null @@ -1,1103 +0,0 @@ -/* - * arm-codegen.h - * Copyright (c) 2002-2003 Sergey Chaban - * Copyright 2005-2011 Novell Inc - * Copyright 2011 Xamarin Inc - */ - - -#ifndef ARM_H -#define ARM_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef unsigned int arminstr_t; -typedef unsigned int armword_t; - -/* Helper functions */ -arminstr_t* arm_emit_std_prologue(arminstr_t* p, unsigned int local_size); -arminstr_t* arm_emit_std_epilogue(arminstr_t* p, unsigned int local_size, int pop_regs); -arminstr_t* arm_emit_lean_prologue(arminstr_t* p, unsigned int local_size, int push_regs); -int arm_is_power_of_2(armword_t val); -int calc_arm_mov_const_shift(armword_t val); -int is_arm_const(armword_t val); -int arm_bsf(armword_t val); -arminstr_t* arm_mov_reg_imm32_cond(arminstr_t* p, int reg, armword_t imm32, int cond); -arminstr_t* arm_mov_reg_imm32(arminstr_t* p, int reg, armword_t imm32); - - - -#if defined(_MSC_VER) || defined(__CC_NORCROFT) - void __inline _arm_emit(arminstr_t** p, arminstr_t i) {**p = i; (*p)++;} -# define ARM_EMIT(p, i) _arm_emit((arminstr_t**)&p, (arminstr_t)(i)) -#else -# define ARM_EMIT(p, i) do { arminstr_t *__ainstrp = (arminstr_t*)(p); *__ainstrp = (arminstr_t)(i); (p) = (arminstr_t*)(__ainstrp+1);} while (0) -#endif - -#if defined(_MSC_VER) && !defined(ARM_NOIASM) -# define ARM_IASM(_expr) __emit (_expr) -#else -# define ARM_IASM(_expr) -#endif - -/* even_scale = rot << 1 */ -#define ARM_SCALE(imm8, even_scale) ( ((imm8) >> (even_scale)) | ((imm8) << (32 - even_scale)) ) - - - -typedef enum { - ARMREG_R0 = 0, - ARMREG_R1, - ARMREG_R2, - ARMREG_R3, - ARMREG_R4, - ARMREG_R5, - ARMREG_R6, - ARMREG_R7, - ARMREG_R8, - ARMREG_R9, - ARMREG_R10, - ARMREG_R11, - ARMREG_R12, - ARMREG_R13, - ARMREG_R14, - ARMREG_R15, - - - /* aliases */ - /* args */ - ARMREG_A1 = ARMREG_R0, - ARMREG_A2 = ARMREG_R1, - ARMREG_A3 = ARMREG_R2, - ARMREG_A4 = ARMREG_R3, - - /* local vars */ - ARMREG_V1 = ARMREG_R4, - ARMREG_V2 = ARMREG_R5, - ARMREG_V3 = ARMREG_R6, - ARMREG_V4 = ARMREG_R7, - ARMREG_V5 = ARMREG_R8, - ARMREG_V6 = ARMREG_R9, - ARMREG_V7 = ARMREG_R10, - - ARMREG_FP = ARMREG_R11, - ARMREG_IP = ARMREG_R12, - ARMREG_SP = ARMREG_R13, - ARMREG_LR = ARMREG_R14, - ARMREG_PC = ARMREG_R15, - - /* co-processor */ - ARMREG_CR0 = 0, - ARMREG_CR1, - ARMREG_CR2, - ARMREG_CR3, - ARMREG_CR4, - ARMREG_CR5, - ARMREG_CR6, - ARMREG_CR7, - ARMREG_CR8, - ARMREG_CR9, - ARMREG_CR10, - ARMREG_CR11, - ARMREG_CR12, - ARMREG_CR13, - ARMREG_CR14, - ARMREG_CR15, - - /* XScale: acc0 on CP0 */ - ARMREG_ACC0 = ARMREG_CR0, - - ARMREG_MAX = ARMREG_R15 -} ARMReg; - -/* number of argument registers */ -#define ARM_NUM_ARG_REGS 4 - -/* bitvector for all argument regs (A1-A4) */ -#define ARM_ALL_ARG_REGS \ - (1 << ARMREG_A1) | (1 << ARMREG_A2) | (1 << ARMREG_A3) | (1 << ARMREG_A4) - - -typedef enum { - ARMCOND_EQ = 0x0, /* Equal; Z = 1 */ - ARMCOND_NE = 0x1, /* Not equal, or unordered; Z = 0 */ - ARMCOND_CS = 0x2, /* Carry set; C = 1 */ - ARMCOND_HS = ARMCOND_CS, /* Unsigned higher or same; */ - ARMCOND_CC = 0x3, /* Carry clear; C = 0 */ - ARMCOND_LO = ARMCOND_CC, /* Unsigned lower */ - ARMCOND_MI = 0x4, /* Negative; N = 1 */ - ARMCOND_PL = 0x5, /* Positive or zero; N = 0 */ - ARMCOND_VS = 0x6, /* Overflow; V = 1 */ - ARMCOND_VC = 0x7, /* No overflow; V = 0 */ - ARMCOND_HI = 0x8, /* Unsigned higher; C = 1 && Z = 0 */ - ARMCOND_LS = 0x9, /* Unsigned lower or same; C = 0 || Z = 1 */ - ARMCOND_GE = 0xA, /* Signed greater than or equal; N = V */ - ARMCOND_LT = 0xB, /* Signed less than; N != V */ - ARMCOND_GT = 0xC, /* Signed greater than; Z = 0 && N = V */ - ARMCOND_LE = 0xD, /* Signed less than or equal; Z = 1 && N != V */ - ARMCOND_AL = 0xE, /* Always */ - ARMCOND_NV = 0xF, /* Never */ - - ARMCOND_SHIFT = 28 -} ARMCond; - -#define ARMCOND_MASK (ARMCOND_NV << ARMCOND_SHIFT) - -#define ARM_DEF_COND(cond) (((cond) & 0xF) << ARMCOND_SHIFT) - - - -typedef enum { - ARMSHIFT_LSL = 0, - ARMSHIFT_LSR = 1, - ARMSHIFT_ASR = 2, - ARMSHIFT_ROR = 3, - - ARMSHIFT_ASL = ARMSHIFT_LSL - /* rrx = (ror, 1) */ -} ARMShiftType; - - -typedef struct { - armword_t PSR_c : 8; - armword_t PSR_x : 8; - armword_t PSR_s : 8; - armword_t PSR_f : 8; -} ARMPSR; - -typedef enum { - ARMOP_AND = 0x0, - ARMOP_EOR = 0x1, - ARMOP_SUB = 0x2, - ARMOP_RSB = 0x3, - ARMOP_ADD = 0x4, - ARMOP_ADC = 0x5, - ARMOP_SBC = 0x6, - ARMOP_RSC = 0x7, - ARMOP_TST = 0x8, - ARMOP_TEQ = 0x9, - ARMOP_CMP = 0xa, - ARMOP_CMN = 0xb, - ARMOP_ORR = 0xc, - ARMOP_MOV = 0xd, - ARMOP_BIC = 0xe, - ARMOP_MVN = 0xf, - - - /* not really opcodes */ - - ARMOP_STR = 0x0, - ARMOP_LDR = 0x1, - - /* ARM2+ */ - ARMOP_MUL = 0x0, /* Rd := Rm*Rs */ - ARMOP_MLA = 0x1, /* Rd := (Rm*Rs)+Rn */ - - /* ARM3M+ */ - ARMOP_UMULL = 0x4, - ARMOP_UMLAL = 0x5, - ARMOP_SMULL = 0x6, - ARMOP_SMLAL = 0x7, - - /* for data transfers with register offset */ - ARM_UP = 1, - ARM_DOWN = 0 -} ARMOpcode; - -typedef enum { - THUMBOP_AND = 0, - THUMBOP_EOR = 1, - THUMBOP_LSL = 2, - THUMBOP_LSR = 3, - THUMBOP_ASR = 4, - THUMBOP_ADC = 5, - THUMBOP_SBC = 6, - THUMBOP_ROR = 7, - THUMBOP_TST = 8, - THUMBOP_NEG = 9, - THUMBOP_CMP = 10, - THUMBOP_CMN = 11, - THUMBOP_ORR = 12, - THUMBOP_MUL = 13, - THUMBOP_BIC = 14, - THUMBOP_MVN = 15, - THUMBOP_MOV = 16, - THUMBOP_CMPI = 17, - THUMBOP_ADD = 18, - THUMBOP_SUB = 19, - THUMBOP_CMPH = 19, - THUMBOP_MOVH = 20 -} ThumbOpcode; - - -/* Generic form - all ARM instructions are conditional. */ -typedef struct { - arminstr_t icode : 28; - arminstr_t cond : 4; -} ARMInstrGeneric; - - - -/* Branch or Branch with Link instructions. */ -typedef struct { - arminstr_t offset : 24; - arminstr_t link : 1; - arminstr_t tag : 3; /* 1 0 1 */ - arminstr_t cond : 4; -} ARMInstrBR; - -#define ARM_BR_ID 5 -#define ARM_BR_MASK 7 << 25 -#define ARM_BR_TAG ARM_BR_ID << 25 - -#define ARM_DEF_BR(offs, l, cond) ((offs) | ((l) << 24) | (ARM_BR_TAG) | (cond << ARMCOND_SHIFT)) - -/* branch */ -#define ARM_B_COND(p, cond, offset) ARM_EMIT(p, ARM_DEF_BR(offset, 0, cond)) -#define ARM_B(p, offs) ARM_B_COND((p), ARMCOND_AL, (offs)) -/* branch with link */ -#define ARM_BL_COND(p, cond, offset) ARM_EMIT(p, ARM_DEF_BR(offset, 1, cond)) -#define ARM_BL(p, offs) ARM_BL_COND((p), ARMCOND_AL, (offs)) - -#define ARM_DEF_BX(reg,sub,cond) (0x12fff << 8 | (reg) | ((sub) << 4) | ((cond) << ARMCOND_SHIFT)) - -#define ARM_BX_COND(p, cond, reg) ARM_EMIT(p, ARM_DEF_BX(reg, 1, cond)) -#define ARM_BX(p, reg) ARM_BX_COND((p), ARMCOND_AL, (reg)) - -#define ARM_BLX_REG_COND(p, cond, reg) ARM_EMIT(p, ARM_DEF_BX(reg, 3, cond)) -#define ARM_BLX_REG(p, reg) ARM_BLX_REG_COND((p), ARMCOND_AL, (reg)) - -/* Data Processing Instructions - there are 3 types. */ - -typedef struct { - arminstr_t imm : 8; - arminstr_t rot : 4; -} ARMDPI_op2_imm; - -typedef struct { - arminstr_t rm : 4; - arminstr_t tag : 1; /* 0 - immediate shift, 1 - reg shift */ - arminstr_t type : 2; /* shift type - logical, arithmetic, rotate */ -} ARMDPI_op2_reg_shift; - - -/* op2 is reg shift by imm */ -typedef union { - ARMDPI_op2_reg_shift r2; - struct { - arminstr_t _dummy_r2 : 7; - arminstr_t shift : 5; - } imm; -} ARMDPI_op2_reg_imm; - -/* op2 is reg shift by reg */ -typedef union { - ARMDPI_op2_reg_shift r2; - struct { - arminstr_t _dummy_r2 : 7; - arminstr_t pad : 1; /* always 0, to differentiate from HXFER etc. */ - arminstr_t rs : 4; - } reg; -} ARMDPI_op2_reg_reg; - -/* Data processing instrs */ -typedef union { - ARMDPI_op2_imm op2_imm; - - ARMDPI_op2_reg_shift op2_reg; - ARMDPI_op2_reg_imm op2_reg_imm; - ARMDPI_op2_reg_reg op2_reg_reg; - - struct { - arminstr_t op2 : 12; /* raw operand 2 */ - arminstr_t rd : 4; /* destination reg */ - arminstr_t rn : 4; /* first operand reg */ - arminstr_t s : 1; /* S-bit controls PSR update */ - arminstr_t opcode : 4; /* arithmetic/logic operation */ - arminstr_t type : 1; /* type of op2, 0 = register, 1 = immediate */ - arminstr_t tag : 2; /* 0 0 */ - arminstr_t cond : 4; - } all; -} ARMInstrDPI; - -#define ARM_DPI_ID 0 -#define ARM_DPI_MASK 3 << 26 -#define ARM_DPI_TAG ARM_DPI_ID << 26 - -#define ARM_DEF_DPI_IMM_COND(imm8, rot, rd, rn, s, op, cond) \ - ((imm8) & 0xFF) | \ - (((rot) & 0xF) << 8) | \ - ((rd) << 12) | \ - ((rn) << 16) | \ - ((s) << 20) | \ - ((op) << 21) | \ - (1 << 25) | \ - (ARM_DPI_TAG) | \ - ARM_DEF_COND(cond) - - -#define ARM_DEF_DPI_IMM(imm8, rot, rd, rn, s, op) \ - ARM_DEF_DPI_IMM_COND(imm8, rot, rd, rn, s, op, ARMCOND_AL) - -/* codegen */ -#define ARM_DPIOP_REG_IMM8ROT_COND(p, op, rd, rn, imm8, rot, cond) \ - ARM_EMIT(p, ARM_DEF_DPI_IMM_COND((imm8), ((rot) >> 1), (rd), (rn), 0, (op), cond)) -#define ARM_DPIOP_S_REG_IMM8ROT_COND(p, op, rd, rn, imm8, rot, cond) \ - ARM_EMIT(p, ARM_DEF_DPI_IMM_COND((imm8), ((rot) >> 1), (rd), (rn), 1, (op), cond)) - -/* inline */ -#define ARM_IASM_DPIOP_REG_IMM8ROT_COND(p, op, rd, rn, imm8, rot, cond) \ - ARM_IASM(ARM_DEF_DPI_IMM_COND((imm8), ((rot) >> 1), (rd), (rn), 0, (op), cond)) -#define ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(p, op, rd, rn, imm8, rot, cond) \ - ARM_IASM(ARM_DEF_DPI_IMM_COND((imm8), ((rot) >> 1), (rd), (rn), 1, (op), cond)) - - - -#define ARM_DEF_DPI_REG_IMMSHIFT_COND(rm, shift_type, imm_shift, rd, rn, s, op, cond) \ - (rm) | \ - ((shift_type & 3) << 5) | \ - (((imm_shift) & 0x1F) << 7) | \ - ((rd) << 12) | \ - ((rn) << 16) | \ - ((s) << 20) | \ - ((op) << 21) | \ - (ARM_DPI_TAG) | \ - ARM_DEF_COND(cond) - -/* codegen */ -#define ARM_DPIOP_REG_IMMSHIFT_COND(p, op, rd, rn, rm, shift_t, imm_shift, cond) \ - ARM_EMIT(p, ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), shift_t, imm_shift, (rd), (rn), 0, (op), cond)) - -#define ARM_DPIOP_S_REG_IMMSHIFT_COND(p, op, rd, rn, rm, shift_t, imm_shift, cond) \ - ARM_EMIT(p, ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), shift_t, imm_shift, (rd), (rn), 1, (op), cond)) - -#define ARM_DPIOP_REG_REG_COND(p, op, rd, rn, rm, cond) \ - ARM_EMIT(p, ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), ARMSHIFT_LSL, 0, (rd), (rn), 0, (op), cond)) - -#define ARM_DPIOP_S_REG_REG_COND(p, op, rd, rn, rm, cond) \ - ARM_EMIT(p, ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), ARMSHIFT_LSL, 0, (rd), (rn), 1, (op), cond)) - -/* inline */ -#define ARM_IASM_DPIOP_REG_IMMSHIFT_COND(p, op, rd, rn, rm, shift_t, imm_shift, cond) \ - ARM_IASM(ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), shift_t, imm_shift, (rd), (rn), 0, (op), cond)) - -#define ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(p, op, rd, rn, rm, shift_t, imm_shift, cond) \ - ARM_IASM(ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), shift_t, imm_shift, (rd), (rn), 1, (op), cond)) - -#define ARM_IASM_DPIOP_REG_REG_COND(p, op, rd, rn, rm, cond) \ - ARM_IASM(ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), ARMSHIFT_LSL, 0, (rd), (rn), 0, (op), cond)) - -#define ARM_IASM_DPIOP_S_REG_REG_COND(p, op, rd, rn, rm, cond) \ - ARM_IASM_EMIT(ARM_DEF_DPI_REG_IMMSHIFT_COND((rm), ARMSHIFT_LSL, 0, (rd), (rn), 1, (op), cond)) - - -/* Rd := Rn op (Rm shift_type Rs) */ -#define ARM_DEF_DPI_REG_REGSHIFT_COND(rm, shift_type, rs, rd, rn, s, op, cond) \ - (rm) | \ - (1 << 4) | \ - ((shift_type & 3) << 5) | \ - ((rs) << 8) | \ - ((rd) << 12) | \ - ((rn) << 16) | \ - ((s) << 20) | \ - ((op) << 21) | \ - (ARM_DPI_TAG) | \ - ARM_DEF_COND(cond) - -/* codegen */ -#define ARM_DPIOP_REG_REGSHIFT_COND(p, op, rd, rn, rm, shift_t, rs, cond) \ - ARM_EMIT(p, ARM_DEF_DPI_REG_REGSHIFT_COND((rm), shift_t, (rs), (rd), (rn), 0, (op), cond)) - -#define ARM_DPIOP_S_REG_REGSHIFT_COND(p, op, rd, rn, rm, shift_t, rs, cond) \ - ARM_EMIT(p, ARM_DEF_DPI_REG_REGSHIFT_COND((rm), shift_t, (rs), (rd), (rn), 1, (op), cond)) - -/* inline */ -#define ARM_IASM_DPIOP_REG_REGSHIFT_COND(p, op, rd, rn, rm, shift_t, rs, cond) \ - ARM_IASM(ARM_DEF_DPI_REG_REGSHIFT_COND((rm), shift_t, (rs), (rd), (rn), 0, (op), cond)) - -#define ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(p, op, rd, rn, rm, shift_t, rs, cond) \ - ARM_IASM(ARM_DEF_DPI_REG_REGSHIFT_COND((rm), shift_t, (rs), (rd), (rn), 1, (op), cond)) - - - -/* Multiple register transfer. */ -typedef struct { - arminstr_t reg_list : 16; /* bitfield */ - arminstr_t rn : 4; /* base reg */ - arminstr_t ls : 1; /* load(1)/store(0) */ - arminstr_t wb : 1; /* write-back "!" */ - arminstr_t s : 1; /* restore PSR, force user bit */ - arminstr_t u : 1; /* up/down */ - arminstr_t p : 1; /* pre(1)/post(0) index */ - arminstr_t tag : 3; /* 1 0 0 */ - arminstr_t cond : 4; -} ARMInstrMRT; - -#define ARM_MRT_ID 4 -#define ARM_MRT_MASK 7 << 25 -#define ARM_MRT_TAG ARM_MRT_ID << 25 - -#define ARM_DEF_MRT(regs, rn, l, w, s, u, p, cond) \ - (regs) | \ - (rn << 16) | \ - (l << 20) | \ - (w << 21) | \ - (s << 22) | \ - (u << 23) | \ - (p << 24) | \ - (ARM_MRT_TAG) | \ - ARM_DEF_COND(cond) - - -#define ARM_LDM(p, base, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, base, 1, 0, 0, 1, 0, ARMCOND_AL)) -#define ARM_STM(p, base, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, base, 0, 0, 0, 1, 0, ARMCOND_AL)) - -/* stmdb sp!, {regs} */ -#define ARM_PUSH(p, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, ARMREG_SP, 0, 1, 0, 0, 1, ARMCOND_AL)) -#define ARM_IASM_PUSH(regs) ARM_IASM(ARM_DEF_MRT(regs, ARMREG_SP, 0, 1, 0, 0, 1, ARMCOND_AL)) - -/* ldmia sp!, {regs} */ -#define ARM_POP(p, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, ARMREG_SP, 1, 1, 0, 1, 0, ARMCOND_AL)) -#define ARM_IASM_POP(regs) ARM_IASM_EMIT(ARM_DEF_MRT(regs, ARMREG_SP, 1, 1, 0, 1, 0, ARMCOND_AL)) - -/* ldmia sp, {regs} ; (no write-back) */ -#define ARM_POP_NWB(p, regs) ARM_EMIT(p, ARM_DEF_MRT(regs, ARMREG_SP, 1, 0, 0, 1, 0, ARMCOND_AL)) -#define ARM_IASM_POP_NWB(regs) ARM_IASM_EMIT(ARM_DEF_MRT(regs, ARMREG_SP, 1, 0, 0, 1, 0, ARMCOND_AL)) - -#define ARM_PUSH1(p, r1) ARM_PUSH(p, (1 << r1)) -#define ARM_PUSH2(p, r1, r2) ARM_PUSH(p, (1 << r1) | (1 << r2)) -#define ARM_PUSH3(p, r1, r2, r3) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3)) -#define ARM_PUSH4(p, r1, r2, r3, r4) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4)) -#define ARM_PUSH5(p, r1, r2, r3, r4, r5) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5)) -#define ARM_PUSH6(p, r1, r2, r3, r4, r5, r6) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6)) -#define ARM_PUSH7(p, r1, r2, r3, r4, r5, r6, r7) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6) | (1 << r7)) -#define ARM_PUSH8(p, r1, r2, r3, r4, r5, r6, r7, r8) ARM_PUSH(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6) | (1 << r7) | (1 << r8)) - -#define ARM_POP8(p, r1, r2, r3, r4, r5, r6, r7, r8) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6) | (1 << r7) | (1 << r8)) -#define ARM_POP7(p, r1, r2, r3, r4, r5, r6, r7) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6) | (1 << r7)) -#define ARM_POP6(p, r1, r2, r3, r4, r5, r6) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5) | (1 << r6)) -#define ARM_POP5(p, r1, r2, r3, r4, r5) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4) | (1 << r5)) -#define ARM_POP4(p, r1, r2, r3, r4) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3) | (1 << r4)) -#define ARM_POP3(p, r1, r2, r3) ARM_POP(p, (1 << r1) | (1 << r2) | (1 << r3)) -#define ARM_POP2(p, r1, r2) ARM_POP(p, (1 << r1) | (1 << r2)) -#define ARM_POP1(p, r1) ARM_POP(p, (1 << r1)) - - -/* Multiply instructions */ -typedef struct { - arminstr_t rm : 4; - arminstr_t tag2 : 4; /* 9 */ - arminstr_t rs : 4; - arminstr_t rn : 4; - arminstr_t rd : 4; - arminstr_t s : 1; - arminstr_t opcode : 3; - arminstr_t tag : 4; - arminstr_t cond : 4; -} ARMInstrMul; - -#define ARM_MUL_ID 0 -#define ARM_MUL_ID2 9 -#define ARM_MUL_MASK ((0xF << 24) | (0xF << 4)) -#define ARM_MUL_TAG ((ARM_MUL_ID << 24) | (ARM_MUL_ID2 << 4)) - -#define ARM_DEF_MUL_COND(op, rd, rm, rs, rn, s, cond) \ - (rm) | \ - ((rs) << 8) | \ - ((rn) << 12) | \ - ((rd) << 16) | \ - ((s & 1) << 17) | \ - ((op & 7) << 18) | \ - ARM_MUL_TAG | \ - ARM_DEF_COND(cond) - -/* Rd := (Rm * Rs)[31:0]; 32 x 32 -> 32 */ -#define ARM_MUL_COND(p, rd, rm, rs, cond) \ - ARM_EMIT(p, ARM_DEF_MUL_COND(ARMOP_MUL, rd, rm, rs, 0, 0, cond)) -#define ARM_MUL(p, rd, rm, rs) \ - ARM_MUL_COND(p, rd, rm, rs, ARMCOND_AL) -#define ARM_MULS_COND(p, rd, rm, rs, cond) \ - ARM_EMIT(p, ARM_DEF_MUL_COND(ARMOP_MUL, rd, rm, rs, 0, 1, cond)) -#define ARM_MULS(p, rd, rm, rs) \ - ARM_MULS_COND(p, rd, rm, rs, ARMCOND_AL) -#define ARM_MUL_REG_REG(p, rd, rm, rs) ARM_MUL(p, rd, rm, rs) -#define ARM_MULS_REG_REG(p, rd, rm, rs) ARM_MULS(p, rd, rm, rs) - -/* inline */ -#define ARM_IASM_MUL_COND(rd, rm, rs, cond) \ - ARM_IASM_EMIT(ARM_DEF_MUL_COND(ARMOP_MUL, rd, rm, rs, 0, 0, cond)) -#define ARM_IASM_MUL(rd, rm, rs) \ - ARM_IASM_MUL_COND(rd, rm, rs, ARMCOND_AL) -#define ARM_IASM_MULS_COND(rd, rm, rs, cond) \ - ARM_IASM_EMIT(ARM_DEF_MUL_COND(ARMOP_MUL, rd, rm, rs, 0, 1, cond)) -#define ARM_IASM_MULS(rd, rm, rs) \ - ARM_IASM_MULS_COND(rd, rm, rs, ARMCOND_AL) - - -/* Rd := (Rm * Rs) + Rn; 32x32+32->32 */ -#define ARM_MLA_COND(p, rd, rm, rs, rn, cond) \ - ARM_EMIT(p, ARM_DEF_MUL_COND(ARMOP_MLA, rd, rm, rs, rn, 0, cond)) -#define ARM_MLA(p, rd, rm, rs, rn) \ - ARM_MLA_COND(p, rd, rm, rs, rn, ARMCOND_AL) -#define ARM_MLAS_COND(p, rd, rm, rs, rn, cond) \ - ARM_EMIT(p, ARM_DEF_MUL_COND(ARMOP_MLA, rd, rm, rs, rn, 1, cond)) -#define ARM_MLAS(p, rd, rm, rs, rn) \ - ARM_MLAS_COND(p, rd, rm, rs, rn, ARMCOND_AL) - -/* inline */ -#define ARM_IASM_MLA_COND(rd, rm, rs, rn, cond) \ - ARM_IASM_EMIT(ARM_DEF_MUL_COND(ARMOP_MLA, rd, rm, rs, rn, 0, cond)) -#define ARM_IASM_MLA(rd, rm, rs, rn) \ - ARM_IASM_MLA_COND(rd, rm, rs, rn, ARMCOND_AL) -#define ARM_IASM_MLAS_COND(rd, rm, rs, rn, cond) \ - ARM_IASM_EMIT(ARM_DEF_MUL_COND(ARMOP_MLA, rd, rm, rs, rn, 1, cond)) -#define ARM_IASM_MLAS(rd, rm, rs, rn) \ - ARM_IASM_MLAS_COND(rd, rm, rs, rn, ARMCOND_AL) - - - -/* Word/byte transfer */ -typedef union { - ARMDPI_op2_reg_imm op2_reg_imm; - struct { - arminstr_t op2_imm : 12; - arminstr_t rd : 4; - arminstr_t rn : 4; - arminstr_t ls : 1; - arminstr_t wb : 1; - arminstr_t b : 1; - arminstr_t u : 1; /* down(0) / up(1) */ - arminstr_t p : 1; /* post-index(0) / pre-index(1) */ - arminstr_t type : 1; /* imm(0) / register(1) */ - arminstr_t tag : 2; /* 0 1 */ - arminstr_t cond : 4; - } all; -} ARMInstrWXfer; - -#define ARM_WXFER_ID 1 -#define ARM_WXFER_MASK 3 << 26 -#define ARM_WXFER_TAG ARM_WXFER_ID << 26 - - -#define ARM_DEF_WXFER_IMM(imm12, rd, rn, ls, wb, b, p, cond) \ - ((((int)imm12) < 0) ? -(int)(imm12) : (imm12)) | \ - ((rd) << 12) | \ - ((rn) << 16) | \ - ((ls) << 20) | \ - ((wb) << 21) | \ - ((b) << 22) | \ - (((int)(imm12) >= 0) << 23) | \ - ((p) << 24) | \ - ARM_WXFER_TAG | \ - ARM_DEF_COND(cond) - -#define ARM_WXFER_MAX_OFFS 0xFFF - -/* this macro checks for imm12 bounds */ -#define ARM_EMIT_WXFER_IMM(ptr, imm12, rd, rn, ls, wb, b, p, cond) \ - do { \ - int _imm12 = (int)(imm12) < -ARM_WXFER_MAX_OFFS \ - ? -ARM_WXFER_MAX_OFFS \ - : (int)(imm12) > ARM_WXFER_MAX_OFFS \ - ? ARM_WXFER_MAX_OFFS \ - : (int)(imm12); \ - ARM_EMIT((ptr), \ - ARM_DEF_WXFER_IMM(_imm12, (rd), (rn), (ls), (wb), (b), (p), (cond))); \ - } while (0) - - -/* LDRx */ -/* immediate offset, post-index */ -#define ARM_LDR_IMM_POST_COND(p, rd, rn, imm, cond) \ - ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_LDR, 0, 0, 0, cond)) - -#define ARM_LDR_IMM_POST(p, rd, rn, imm) ARM_LDR_IMM_POST_COND(p, rd, rn, imm, ARMCOND_AL) - -#define ARM_LDRB_IMM_POST_COND(p, rd, rn, imm, cond) \ - ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_LDR, 0, 1, 0, cond)) - -#define ARM_LDRB_IMM_POST(p, rd, rn, imm) ARM_LDRB_IMM_POST_COND(p, rd, rn, imm, ARMCOND_AL) - -/* immediate offset, pre-index */ -#define ARM_LDR_IMM_COND(p, rd, rn, imm, cond) \ - ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_LDR, 0, 0, 1, cond)) - -#define ARM_LDR_IMM(p, rd, rn, imm) ARM_LDR_IMM_COND(p, rd, rn, imm, ARMCOND_AL) - -#define ARM_LDRB_IMM_COND(p, rd, rn, imm, cond) \ - ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_LDR, 0, 1, 1, cond)) - -#define ARM_LDRB_IMM(p, rd, rn, imm) ARM_LDRB_IMM_COND(p, rd, rn, imm, ARMCOND_AL) - -/* STRx */ -/* immediate offset, post-index */ -#define ARM_STR_IMM_POST_COND(p, rd, rn, imm, cond) \ - ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_STR, 0, 0, 0, cond)) - -#define ARM_STR_IMM_POST(p, rd, rn, imm) ARM_STR_IMM_POST_COND(p, rd, rn, imm, ARMCOND_AL) - -#define ARM_STRB_IMM_POST_COND(p, rd, rn, imm, cond) \ - ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_STR, 0, 1, 0, cond)) - -#define ARM_STRB_IMM_POST(p, rd, rn, imm) ARM_STRB_IMM_POST_COND(p, rd, rn, imm, ARMCOND_AL) - -/* immediate offset, pre-index */ -#define ARM_STR_IMM_COND(p, rd, rn, imm, cond) \ - ARM_EMIT_WXFER_IMM(p, imm, rd, rn, ARMOP_STR, 0, 0, 1, cond) -/* ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_STR, 0, 0, 1, cond)) */ - -#define ARM_STR_IMM(p, rd, rn, imm) ARM_STR_IMM_COND(p, rd, rn, imm, ARMCOND_AL) - -#define ARM_STRB_IMM_COND(p, rd, rn, imm, cond) \ - ARM_EMIT(p, ARM_DEF_WXFER_IMM(imm, rd, rn, ARMOP_STR, 0, 1, 1, cond)) - -#define ARM_STRB_IMM(p, rd, rn, imm) ARM_STRB_IMM_COND(p, rd, rn, imm, ARMCOND_AL) - -/* write-back */ -#define ARM_STR_IMM_WB_COND(p, rd, rn, imm, cond) \ - ARM_EMIT_WXFER_IMM(p, imm, rd, rn, ARMOP_STR, 1, 0, 1, cond) -#define ARM_STR_IMM_WB(p, rd, rn, imm) ARM_STR_IMM_WB_COND(p, rd, rn, imm, ARMCOND_AL) - - -#define ARM_DEF_WXFER_REG_REG_UPDOWN_COND(rm, shift_type, shift, rd, rn, ls, wb, b, u, p, cond) \ - (rm) | \ - ((shift_type) << 5) | \ - ((shift) << 7) | \ - ((rd) << 12) | \ - ((rn) << 16) | \ - ((ls) << 20) | \ - ((wb) << 21) | \ - ((b) << 22) | \ - ((u) << 23) | \ - ((p) << 24) | \ - (1 << 25) | \ - ARM_WXFER_TAG | \ - ARM_DEF_COND(cond) - -#define ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ls, wb, b, p, cond) \ - ARM_DEF_WXFER_REG_REG_UPDOWN_COND(rm, shift_type, shift, rd, rn, ls, wb, b, ARM_UP, p, cond) -#define ARM_DEF_WXFER_REG_MINUS_REG_COND(rm, shift_type, shift, rd, rn, ls, wb, b, p, cond) \ - ARM_DEF_WXFER_REG_REG_UPDOWN_COND(rm, shift_type, shift, rd, rn, ls, wb, b, ARM_DOWN, p, cond) - - -#define ARM_LDR_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, cond) \ - ARM_EMIT(p, ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ARMOP_LDR, 0, 0, 1, cond)) -#define ARM_LDR_REG_REG_SHIFT(p, rd, rn, rm, shift_type, shift) \ - ARM_LDR_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, ARMCOND_AL) -#define ARM_LDR_REG_REG(p, rd, rn, rm) \ - ARM_LDR_REG_REG_SHIFT(p, rd, rn, rm, ARMSHIFT_LSL, 0) - -#define ARM_LDRB_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, cond) \ - ARM_EMIT(p, ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ARMOP_LDR, 0, 1, 1, cond)) -#define ARM_LDRB_REG_REG_SHIFT(p, rd, rn, rm, shift_type, shift) \ - ARM_LDRB_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, ARMCOND_AL) -#define ARM_LDRB_REG_REG(p, rd, rn, rm) \ - ARM_LDRB_REG_REG_SHIFT(p, rd, rn, rm, ARMSHIFT_LSL, 0) - -#define ARM_STR_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, cond) \ - ARM_EMIT(p, ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ARMOP_STR, 0, 0, 1, cond)) -#define ARM_STR_REG_REG_SHIFT(p, rd, rn, rm, shift_type, shift) \ - ARM_STR_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, ARMCOND_AL) -#define ARM_STR_REG_REG(p, rd, rn, rm) \ - ARM_STR_REG_REG_SHIFT(p, rd, rn, rm, ARMSHIFT_LSL, 0) - -/* zero-extend */ -#define ARM_STRB_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, cond) \ - ARM_EMIT(p, ARM_DEF_WXFER_REG_REG_COND(rm, shift_type, shift, rd, rn, ARMOP_STR, 0, 1, 1, cond)) -#define ARM_STRB_REG_REG_SHIFT(p, rd, rn, rm, shift_type, shift) \ - ARM_STRB_REG_REG_SHIFT_COND(p, rd, rn, rm, shift_type, shift, ARMCOND_AL) -#define ARM_STRB_REG_REG(p, rd, rn, rm) \ - ARM_STRB_REG_REG_SHIFT(p, rd, rn, rm, ARMSHIFT_LSL, 0) - - -/* ARMv4+ */ -/* Half-word or byte (signed) transfer. */ -typedef struct { - arminstr_t rm : 4; /* imm_lo */ - arminstr_t tag3 : 1; /* 1 */ - arminstr_t h : 1; /* half-word or byte */ - arminstr_t s : 1; /* sign-extend or zero-extend */ - arminstr_t tag2 : 1; /* 1 */ - arminstr_t imm_hi : 4; - arminstr_t rd : 4; - arminstr_t rn : 4; - arminstr_t ls : 1; - arminstr_t wb : 1; - arminstr_t type : 1; /* imm(1) / reg(0) */ - arminstr_t u : 1; /* +- */ - arminstr_t p : 1; /* pre/post-index */ - arminstr_t tag : 3; - arminstr_t cond : 4; -} ARMInstrHXfer; - -#define ARM_HXFER_ID 0 -#define ARM_HXFER_ID2 1 -#define ARM_HXFER_ID3 1 -#define ARM_HXFER_MASK ((0x7 << 25) | (0x9 << 4)) -#define ARM_HXFER_TAG ((ARM_HXFER_ID << 25) | (ARM_HXFER_ID2 << 7) | (ARM_HXFER_ID3 << 4)) - -#define ARM_DEF_HXFER_IMM_COND(imm, h, s, rd, rn, ls, wb, p, cond) \ - ((imm) < 0?(-(imm)) & 0xF:(imm) & 0xF) | \ - ((h) << 5) | \ - ((s) << 6) | \ - ((imm) < 0?((-(imm)) << 4) & 0xF00:((imm) << 4) & 0xF00) | \ - ((rd) << 12) | \ - ((rn) << 16) | \ - ((ls) << 20) | \ - ((wb) << 21) | \ - (1 << 22) | \ - (((int)(imm) >= 0) << 23) | \ - ((p) << 24) | \ - ARM_HXFER_TAG | \ - ARM_DEF_COND(cond) - -#define ARM_LDRH_IMM_COND(p, rd, rn, imm, cond) \ - ARM_EMIT(p, ARM_DEF_HXFER_IMM_COND(imm, 1, 0, rd, rn, ARMOP_LDR, 0, 1, cond)) -#define ARM_LDRH_IMM(p, rd, rn, imm) \ - ARM_LDRH_IMM_COND(p, rd, rn, imm, ARMCOND_AL) -#define ARM_LDRSH_IMM_COND(p, rd, rn, imm, cond) \ - ARM_EMIT(p, ARM_DEF_HXFER_IMM_COND(imm, 1, 1, rd, rn, ARMOP_LDR, 0, 1, cond)) -#define ARM_LDRSH_IMM(p, rd, rn, imm) \ - ARM_LDRSH_IMM_COND(p, rd, rn, imm, ARMCOND_AL) -#define ARM_LDRSB_IMM_COND(p, rd, rn, imm, cond) \ - ARM_EMIT(p, ARM_DEF_HXFER_IMM_COND(imm, 0, 1, rd, rn, ARMOP_LDR, 0, 1, cond)) -#define ARM_LDRSB_IMM(p, rd, rn, imm) \ - ARM_LDRSB_IMM_COND(p, rd, rn, imm, ARMCOND_AL) - - -#define ARM_STRH_IMM_COND(p, rd, rn, imm, cond) \ - ARM_EMIT(p, ARM_DEF_HXFER_IMM_COND(imm, 1, 0, rd, rn, ARMOP_STR, 0, 1, cond)) -#define ARM_STRH_IMM(p, rd, rn, imm) \ - ARM_STRH_IMM_COND(p, rd, rn, imm, ARMCOND_AL) - - -#define ARM_DEF_HXFER_REG_REG_UPDOWN_COND(rm, h, s, rd, rn, ls, wb, u, p, cond) \ - ((rm) & 0xF) | \ - ((h) << 5) | \ - ((s) << 6) | \ - ((rd) << 12) | \ - ((rn) << 16) | \ - ((ls) << 20) | \ - ((wb) << 21) | \ - (0 << 22) | \ - ((u) << 23) | \ - ((p) << 24) | \ - ARM_HXFER_TAG | \ - ARM_DEF_COND(cond) - -#define ARM_DEF_HXFER_REG_REG_COND(rm, h, s, rd, rn, ls, wb, p, cond) \ - ARM_DEF_HXFER_REG_REG_UPDOWN_COND(rm, h, s, rd, rn, ls, wb, ARM_UP, p, cond) -#define ARM_DEF_HXFER_REG_MINUS_REG_COND(rm, h, s, rd, rn, ls, wb, p, cond) \ - ARM_DEF_HXFER_REG_REG_UPDOWN_COND(rm, h, s, rd, rn, ls, wb, ARM_DOWN, p, cond) - -#define ARM_LDRH_REG_REG_COND(p, rd, rm, rn, cond) \ - ARM_EMIT(p, ARM_DEF_HXFER_REG_REG_COND(rm, 1, 0, rd, rn, ARMOP_LDR, 0, 1, cond)) -#define ARM_LDRH_REG_REG(p, rd, rm, rn) \ - ARM_LDRH_REG_REG_COND(p, rd, rm, rn, ARMCOND_AL) -#define ARM_LDRSH_REG_REG_COND(p, rd, rm, rn, cond) \ - ARM_EMIT(p, ARM_DEF_HXFER_REG_REG_COND(rm, 1, 1, rd, rn, ARMOP_LDR, 0, 1, cond)) -#define ARM_LDRSH_REG_REG(p, rd, rm, rn) \ - ARM_LDRSH_REG_REG_COND(p, rd, rm, rn, ARMCOND_AL) -#define ARM_LDRSB_REG_REG_COND(p, rd, rm, rn, cond) \ - ARM_EMIT(p, ARM_DEF_HXFER_REG_REG_COND(rm, 0, 1, rd, rn, ARMOP_LDR, 0, 1, cond)) -#define ARM_LDRSB_REG_REG(p, rd, rm, rn) ARM_LDRSB_REG_REG_COND(p, rd, rm, rn, ARMCOND_AL) - -#define ARM_STRH_REG_REG_COND(p, rd, rm, rn, cond) \ - ARM_EMIT(p, ARM_DEF_HXFER_REG_REG_COND(rm, 1, 0, rd, rn, ARMOP_STR, 0, 1, cond)) -#define ARM_STRH_REG_REG(p, rd, rm, rn) \ - ARM_STRH_REG_REG_COND(p, rd, rm, rn, ARMCOND_AL) - - - -/* Swap */ -typedef struct { - arminstr_t rm : 4; - arminstr_t tag3 : 8; /* 0x9 */ - arminstr_t rd : 4; - arminstr_t rn : 4; - arminstr_t tag2 : 2; - arminstr_t b : 1; - arminstr_t tag : 5; /* 0x2 */ - arminstr_t cond : 4; -} ARMInstrSwap; - -#define ARM_SWP_ID 2 -#define ARM_SWP_ID2 9 -#define ARM_SWP_MASK ((0x1F << 23) | (3 << 20) | (0xFF << 4)) -#define ARM_SWP_TAG ((ARM_SWP_ID << 23) | (ARM_SWP_ID2 << 4)) - - - -/* Software interrupt */ -typedef struct { - arminstr_t num : 24; - arminstr_t tag : 4; - arminstr_t cond : 4; -} ARMInstrSWI; - -#define ARM_SWI_ID 0xF -#define ARM_SWI_MASK (0xF << 24) -#define ARM_SWI_TAG (ARM_SWI_ID << 24) - - - -/* Co-processor Data Processing */ -typedef struct { - arminstr_t crm : 4; - arminstr_t tag2 : 1; /* 0 */ - arminstr_t op2 : 3; - arminstr_t cpn : 4; /* CP number */ - arminstr_t crd : 4; - arminstr_t crn : 4; - arminstr_t op : 4; - arminstr_t tag : 4; /* 0xE */ - arminstr_t cond : 4; -} ARMInstrCDP; - -#define ARM_CDP_ID 0xE -#define ARM_CDP_ID2 0 -#define ARM_CDP_MASK ((0xF << 24) | (1 << 4)) -#define ARM_CDP_TAG ((ARM_CDP_ID << 24) | (ARM_CDP_ID2 << 4)) - - -/* Co-processor Data Transfer (ldc/stc) */ -typedef struct { - arminstr_t offs : 8; - arminstr_t cpn : 4; - arminstr_t crd : 4; - arminstr_t rn : 4; - arminstr_t ls : 1; - arminstr_t wb : 1; - arminstr_t n : 1; - arminstr_t u : 1; - arminstr_t p : 1; - arminstr_t tag : 3; - arminstr_t cond : 4; -} ARMInstrCDT; - -#define ARM_CDT_ID 6 -#define ARM_CDT_MASK (7 << 25) -#define ARM_CDT_TAG (ARM_CDT_ID << 25) - - -/* Co-processor Register Transfer (mcr/mrc) */ -typedef struct { - arminstr_t crm : 4; - arminstr_t tag2 : 1; - arminstr_t op2 : 3; - arminstr_t cpn : 4; - arminstr_t rd : 4; - arminstr_t crn : 4; - arminstr_t ls : 1; - arminstr_t op1 : 3; - arminstr_t tag : 4; - arminstr_t cond : 4; -} ARMInstrCRT; - -#define ARM_CRT_ID 0xE -#define ARM_CRT_ID2 0x1 -#define ARM_CRT_MASK ((0xF << 24) | (1 << 4)) -#define ARM_CRT_TAG ((ARM_CRT_ID << 24) | (ARM_CRT_ID2 << 4)) - -/* Move register to PSR. */ -typedef union { - ARMDPI_op2_imm op2_imm; - struct { - arminstr_t rm : 4; - arminstr_t pad : 8; /* 0 */ - arminstr_t tag4 : 4; /* 0xF */ - arminstr_t fld : 4; - arminstr_t tag3 : 2; /* 0x2 */ - arminstr_t sel : 1; - arminstr_t tag2 : 2; /* 0x2 */ - arminstr_t type : 1; - arminstr_t tag : 2; /* 0 */ - arminstr_t cond : 4; - } all; -} ARMInstrMSR; - -#define ARM_MSR_ID 0 -#define ARM_MSR_ID2 2 -#define ARM_MSR_ID3 2 -#define ARM_MSR_ID4 0xF -#define ARM_MSR_MASK ((3 << 26) | \ - (3 << 23) | \ - (3 << 20) | \ - (0xF << 12)) -#define ARM_MSR_TAG ((ARM_MSR_ID << 26) | \ - (ARM_MSR_ID2 << 23) | \ - (ARM_MSR_ID3 << 20) | \ - (ARM_MSR_ID4 << 12)) - - -/* Move PSR to register. */ -typedef struct { - arminstr_t tag3 : 12; - arminstr_t rd : 4; - arminstr_t tag2 : 6; - arminstr_t sel : 1; /* CPSR | SPSR */ - arminstr_t tag : 5; - arminstr_t cond : 4; -} ARMInstrMRS; - -#define ARM_MRS_ID 2 -#define ARM_MRS_ID2 0xF -#define ARM_MRS_ID3 0 -#define ARM_MRS_MASK ((0x1F << 23) | (0x3F << 16) | 0xFFF) -#define ARM_MRS_TAG ((ARM_MRS_ID << 23) | (ARM_MRS_ID2 << 16) | ARM_MRS_ID3) - - - -#include "arm_dpimacros.h" - -#define ARM_NOP(p) ARM_MOV_REG_REG(p, ARMREG_R0, ARMREG_R0) - - -#define ARM_SHL_IMM_COND(p, rd, rm, imm, cond) \ - ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_LSL, imm, cond) -#define ARM_SHL_IMM(p, rd, rm, imm) \ - ARM_SHL_IMM_COND(p, rd, rm, imm, ARMCOND_AL) -#define ARM_SHLS_IMM_COND(p, rd, rm, imm, cond) \ - ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_LSL, imm, cond) -#define ARM_SHLS_IMM(p, rd, rm, imm) \ - ARM_SHLS_IMM_COND(p, rd, rm, imm, ARMCOND_AL) - -#define ARM_SHR_IMM_COND(p, rd, rm, imm, cond) \ - ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_LSR, imm, cond) -#define ARM_SHR_IMM(p, rd, rm, imm) \ - ARM_SHR_IMM_COND(p, rd, rm, imm, ARMCOND_AL) -#define ARM_SHRS_IMM_COND(p, rd, rm, imm, cond) \ - ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_LSR, imm, cond) -#define ARM_SHRS_IMM(p, rd, rm, imm) \ - ARM_SHRS_IMM_COND(p, rd, rm, imm, ARMCOND_AL) - -#define ARM_SAR_IMM_COND(p, rd, rm, imm, cond) \ - ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_ASR, imm, cond) -#define ARM_SAR_IMM(p, rd, rm, imm) \ - ARM_SAR_IMM_COND(p, rd, rm, imm, ARMCOND_AL) -#define ARM_SARS_IMM_COND(p, rd, rm, imm, cond) \ - ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_ASR, imm, cond) -#define ARM_SARS_IMM(p, rd, rm, imm) \ - ARM_SARS_IMM_COND(p, rd, rm, imm, ARMCOND_AL) - -#define ARM_ROR_IMM_COND(p, rd, rm, imm, cond) \ - ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_ROR, imm, cond) -#define ARM_ROR_IMM(p, rd, rm, imm) \ - ARM_ROR_IMM_COND(p, rd, rm, imm, ARMCOND_AL) -#define ARM_RORS_IMM_COND(p, rd, rm, imm, cond) \ - ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, ARMSHIFT_ROR, imm, cond) -#define ARM_RORS_IMM(p, rd, rm, imm) \ - ARM_RORS_IMM_COND(p, rd, rm, imm, ARMCOND_AL) - -#define ARM_SHL_REG_COND(p, rd, rm, rs, cond) \ - ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_LSL, rs, cond) -#define ARM_SHL_REG(p, rd, rm, rs) \ - ARM_SHL_REG_COND(p, rd, rm, rs, ARMCOND_AL) -#define ARM_SHLS_REG_COND(p, rd, rm, rs, cond) \ - ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_LSL, rs, cond) -#define ARM_SHLS_REG(p, rd, rm, rs) \ - ARM_SHLS_REG_COND(p, rd, rm, rs, ARMCOND_AL) -#define ARM_SHLS_REG_REG(p, rd, rm, rs) ARM_SHLS_REG(p, rd, rm, rs) - -#define ARM_SHR_REG_COND(p, rd, rm, rs, cond) \ - ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_LSR, rs, cond) -#define ARM_SHR_REG(p, rd, rm, rs) \ - ARM_SHR_REG_COND(p, rd, rm, rs, ARMCOND_AL) -#define ARM_SHRS_REG_COND(p, rd, rm, rs, cond) \ - ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_LSR, rs, cond) -#define ARM_SHRS_REG(p, rd, rm, rs) \ - ARM_SHRS_REG_COND(p, rd, rm, rs, ARMCOND_AL) -#define ARM_SHRS_REG_REG(p, rd, rm, rs) ARM_SHRS_REG(p, rd, rm, rs) - -#define ARM_SAR_REG_COND(p, rd, rm, rs, cond) \ - ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_ASR, rs, cond) -#define ARM_SAR_REG(p, rd, rm, rs) \ - ARM_SAR_REG_COND(p, rd, rm, rs, ARMCOND_AL) -#define ARM_SARS_REG_COND(p, rd, rm, rs, cond) \ - ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_ASR, rs, cond) -#define ARM_SARS_REG(p, rd, rm, rs) \ - ARM_SARS_REG_COND(p, rd, rm, rs, ARMCOND_AL) -#define ARM_SARS_REG_REG(p, rd, rm, rs) ARM_SARS_REG(p, rd, rm, rs) - -#define ARM_ROR_REG_COND(p, rd, rm, rs, cond) \ - ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_ROR, rs, cond) -#define ARM_ROR_REG(p, rd, rm, rs) \ - ARM_ROR_REG_COND(p, rd, rm, rs, ARMCOND_AL) -#define ARM_RORS_REG_COND(p, rd, rm, rs, cond) \ - ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, ARMSHIFT_ROR, rs, cond) -#define ARM_RORS_REG(p, rd, rm, rs) \ - ARM_RORS_REG_COND(p, rd, rm, rs, ARMCOND_AL) -#define ARM_RORS_REG_REG(p, rd, rm, rs) ARM_RORS_REG(p, rd, rm, rs) - -#define ARM_DBRK(p) ARM_EMIT(p, 0xE6000010) -#define ARM_IASM_DBRK() ARM_IASM_EMIT(0xE6000010) - -#define ARM_INC(p, reg) ARM_ADD_REG_IMM8(p, reg, reg, 1) -#define ARM_DEC(p, reg) ARM_SUB_REG_IMM8(p, reg, reg, 1) - - -/* ARM V5 */ - -/* Count leading zeros, CLZ{cond} Rd, Rm */ -typedef struct { - arminstr_t rm : 4; - arminstr_t tag2 : 8; - arminstr_t rd : 4; - arminstr_t tag : 12; - arminstr_t cond : 4; -} ARMInstrCLZ; - -#define ARM_CLZ_ID 0x16F -#define ARM_CLZ_ID2 0xF1 -#define ARM_CLZ_MASK ((0xFFF << 16) | (0xFF < 4)) -#define ARM_CLZ_TAG ((ARM_CLZ_ID << 16) | (ARM_CLZ_ID2 << 4)) - - - - -typedef union { - ARMInstrBR br; - ARMInstrDPI dpi; - ARMInstrMRT mrt; - ARMInstrMul mul; - ARMInstrWXfer wxfer; - ARMInstrHXfer hxfer; - ARMInstrSwap swp; - ARMInstrCDP cdp; - ARMInstrCDT cdt; - ARMInstrCRT crt; - ARMInstrSWI swi; - ARMInstrMSR msr; - ARMInstrMRS mrs; - ARMInstrCLZ clz; - - ARMInstrGeneric generic; - arminstr_t raw; -} ARMInstr; - -/* ARMv6t2 */ - -#define ARM_MOVW_REG_IMM_COND(p, rd, imm16, cond) ARM_EMIT(p, (((cond) << 28) | (3 << 24) | (0 << 20) | ((((guint32)(imm16)) >> 12) << 16) | ((rd) << 12) | (((guint32)(imm16)) & 0xfff))) -#define ARM_MOVW_REG_IMM(p, rd, imm16) ARM_MOVW_REG_IMM_COND ((p), (rd), (imm16), ARMCOND_AL) - -#define ARM_MOVT_REG_IMM_COND(p, rd, imm16, cond) ARM_EMIT(p, (((cond) << 28) | (3 << 24) | (4 << 20) | ((((guint32)(imm16)) >> 12) << 16) | ((rd) << 12) | (((guint32)(imm16)) & 0xfff))) -#define ARM_MOVT_REG_IMM(p, rd, imm16) ARM_MOVT_REG_IMM_COND ((p), (rd), (imm16), ARMCOND_AL) - -/* MCR */ -#define ARM_DEF_MCR_COND(coproc, opc1, rt, crn, crm, opc2, cond) \ - ARM_DEF_COND ((cond)) | ((0xe << 24) | (((opc1) & 0x7) << 21) | (0 << 20) | (((crn) & 0xf) << 16) | (((rt) & 0xf) << 12) | (((coproc) & 0xf) << 8) | (((opc2) & 0x7) << 5) | (1 << 4) | (((crm) & 0xf) << 0)) - -#define ARM_MCR_COND(p, coproc, opc1, rt, crn, crm, opc2, cond) \ - ARM_EMIT(p, ARM_DEF_MCR_COND ((coproc), (opc1), (rt), (crn), (crm), (opc2), (cond))) - -#define ARM_MCR(p, coproc, opc1, rt, crn, crm, opc2) \ - ARM_MCR_COND ((p), (coproc), (opc1), (rt), (crn), (crm), (opc2), ARMCOND_AL) - -#ifdef __cplusplus -} -#endif - -#endif /* ARM_H */ - diff --git a/asm/arm-dis.c b/asm/arm-dis.c deleted file mode 100644 index 5074f260d8..0000000000 --- a/asm/arm-dis.c +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright (c) 2002 Sergey Chaban - */ - - -#include - -#include "arm-dis.h" -#include "arm-codegen.h" - - -static ARMDis* gdisasm = NULL; - -static int use_reg_alias = 1; - -const static char* cond[] = { - "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", - "hi", "ls", "ge", "lt", "gt", "le", "", "nv" -}; - -const static char* ops[] = { - "and", "eor", "sub", "rsb", "add", "adc", "sbc", "rsc", - "tst", "teq", "cmp", "cmn", "orr", "mov", "bic", "mvn" -}; - -const static char* shift_types[] = {"lsl", "lsr", "asr", "ror"}; - -const static char* mul_ops[] = { - "mul", "mla", "?", "?", "umull", "umlal", "smull", "smlal" -}; - -const static char* reg_alias[] = { - "a1", "a2", "a3", "a4", - "r4", "r5", "r6", "r7", "r8", "r9", "r10", - "fp", "ip", "sp", "lr", "pc" -}; - -const static char* msr_fld[] = {"f", "c", "x", "?", "s"}; - - -/* private functions prototypes (to keep compiler happy) */ -void chk_out(ARMDis* dis); -void dump_reg(ARMDis* dis, int reg); -void dump_creg(ARMDis* dis, int creg); -void dump_reglist(ARMDis* dis, int reg_list); -void init_gdisasm(void); - -void dump_br(ARMDis* dis, ARMInstr i); -void dump_cdp(ARMDis* dis, ARMInstr i); -void dump_cdt(ARMDis* dis, ARMInstr i); -void dump_crt(ARMDis* dis, ARMInstr i); -void dump_dpi(ARMDis* dis, ARMInstr i); -void dump_hxfer(ARMDis* dis, ARMInstr i); -void dump_mrs(ARMDis* dis, ARMInstr i); -void dump_mrt(ARMDis* dis, ARMInstr i); -void dump_msr(ARMDis* dis, ARMInstr i); -void dump_mul(ARMDis* dis, ARMInstr i); -void dump_swi(ARMDis* dis, ARMInstr i); -void dump_swp(ARMDis* dis, ARMInstr i); -void dump_wxfer(ARMDis* dis, ARMInstr i); -void dump_clz(ARMDis* dis, ARMInstr i); - - -/* -void out(ARMDis* dis, const char* format, ...) { - va_list arglist; - va_start(arglist, format); - fprintf(dis->dis_out, format, arglist); - va_end(arglist); -} -*/ - - -void chk_out(ARMDis* dis) { - if (dis != NULL && dis->dis_out == NULL) dis->dis_out = stdout; -} - - -void armdis_set_output(ARMDis* dis, FILE* f) { - if (dis != NULL) { - dis->dis_out = f; - chk_out(dis); - } -} - -FILE* armdis_get_output(ARMDis* dis) { - return (dis != NULL ? dis->dis_out : NULL); -} - - - - -void dump_reg(ARMDis* dis, int reg) { - reg &= 0xF; - if (!use_reg_alias || (reg > 3 && reg < 11)) { - fprintf(dis->dis_out, "r%d", reg); - } else { - fprintf(dis->dis_out, "%s", reg_alias[reg]); - } -} - -void dump_creg(ARMDis* dis, int creg) { - if (dis != NULL) { - creg &= 0xF; - fprintf(dis->dis_out, "c%d", creg); - } -} - -void dump_reglist(ARMDis* dis, int reg_list) { - int i = 0, j, n = 0; - int m1 = 1, m2, rn; - while (i < 16) { - if ((reg_list & m1) != 0) { - if (n != 0) fprintf(dis->dis_out, ", "); - n++; - dump_reg(dis, i); - for (j = i+1, rn = 0, m2 = m1<<1; j < 16; ++j, m2<<=1) { - if ((reg_list & m2) != 0) ++rn; - else break; - } - i+=rn; - if (rn > 1) { - fprintf(dis->dis_out, "-"); - dump_reg(dis, i); - } else if (rn == 1) { - fprintf(dis->dis_out, ", "); - dump_reg(dis, i); - } - m1<<=(rn+1); - i++; - } else { - ++i; - m1<<=1; - } - } -} - - -void dump_br(ARMDis* dis, ARMInstr i) { - fprintf(dis->dis_out, "b%s%s\t%x\t; %p -> %#x", - (i.br.link == 1) ? "l" : "", - cond[i.br.cond], i.br.offset, dis->pi, (int)dis->pi + 4*2 + ((int)(i.br.offset << 8) >> 6)); -} - - -void dump_dpi(ARMDis* dis, ARMInstr i) { - fprintf(dis->dis_out, "%s%s", ops[i.dpi.all.opcode], cond[i.dpi.all.cond]); - - if ((i.dpi.all.opcode < ARMOP_TST || i.dpi.all.opcode > ARMOP_CMN) && (i.dpi.all.s != 0)) { - fprintf(dis->dis_out, "s"); - } - - fprintf(dis->dis_out, "\t"); - - if ((i.dpi.all.opcode < ARMOP_TST) || (i.dpi.all.opcode > ARMOP_CMN)) { - /* for comparison operations Rd is ignored */ - dump_reg(dis, i.dpi.all.rd); - fprintf(dis->dis_out, ", "); - } - - if ((i.dpi.all.opcode != ARMOP_MOV) && (i.dpi.all.opcode != ARMOP_MVN)) { - /* for MOV/MVN Rn is ignored */ - dump_reg(dis, i.dpi.all.rn); - fprintf(dis->dis_out, ", "); - } - - if (i.dpi.all.type == 1) { - /* immediate */ - if (i.dpi.op2_imm.rot != 0) { - fprintf(dis->dis_out, "#%d, %d\t; 0x%x", i.dpi.op2_imm.imm, i.dpi.op2_imm.rot << 1, - ARM_SCALE(i.dpi.op2_imm.imm, (i.dpi.op2_imm.rot << 1)) ); - } else { - fprintf(dis->dis_out, "#%d\t; 0x%x", i.dpi.op2_imm.imm, i.dpi.op2_imm.imm); - } - } else { - /* reg-reg */ - if (i.dpi.op2_reg.tag == 0) { - /* op2 is reg shift by imm */ - dump_reg(dis, i.dpi.op2_reg_imm.r2.rm); - if (i.dpi.op2_reg_imm.imm.shift != 0) { - fprintf(dis->dis_out, " %s #%d", shift_types[i.dpi.op2_reg_imm.r2.type], i.dpi.op2_reg_imm.imm.shift); - } - } else { - /* op2 is reg shift by reg */ - dump_reg(dis, i.dpi.op2_reg_reg.r2.rm); - fprintf(dis->dis_out, " %s ", shift_types[i.dpi.op2_reg_reg.r2.type]); - dump_reg(dis, i.dpi.op2_reg_reg.reg.rs); - } - - } -} - -void dump_wxfer(ARMDis* dis, ARMInstr i) { - fprintf(dis->dis_out, "%s%s%s%s\t", - (i.wxfer.all.ls == 0) ? "str" : "ldr", - cond[i.generic.cond], - (i.wxfer.all.b == 0) ? "" : "b", - (i.wxfer.all.ls != 0 && i.wxfer.all.wb != 0) ? "t" : ""); - dump_reg(dis, i.wxfer.all.rd); - fprintf(dis->dis_out, ", ["); - dump_reg(dis, i.wxfer.all.rn); - fprintf(dis->dis_out, "%s, ", (i.wxfer.all.p == 0) ? "]" : ""); - - if (i.wxfer.all.type == 0) { /* imm */ - fprintf(dis->dis_out, "#%s%d", (i.wxfer.all.u == 0) ? "-" : "", i.wxfer.all.op2_imm); - } else { - dump_reg(dis, i.wxfer.op2_reg_imm.r2.rm); - if (i.wxfer.op2_reg_imm.imm.shift != 0) { - fprintf(dis->dis_out, " %s #%d", shift_types[i.wxfer.op2_reg_imm.r2.type], i.wxfer.op2_reg_imm.imm.shift); - } - } - - if (i.wxfer.all.p != 0) { - /* close pre-index instr, also check for write-back */ - fprintf(dis->dis_out, "]%s", (i.wxfer.all.wb != 0) ? "!" : ""); - } -} - -void dump_hxfer(ARMDis* dis, ARMInstr i) { - fprintf(dis->dis_out, "%s%s%s%s\t", - (i.hxfer.ls == 0) ? "str" : "ldr", - cond[i.generic.cond], - (i.hxfer.s != 0) ? "s" : "", - (i.hxfer.h != 0) ? "h" : "b"); - dump_reg(dis, i.hxfer.rd); - fprintf(dis->dis_out, ", ["); - dump_reg(dis, i.hxfer.rn); - fprintf(dis->dis_out, "%s, ", (i.hxfer.p == 0) ? "]" : ""); - - if (i.hxfer.type != 0) { /* imm */ - fprintf(dis->dis_out, "#%s%d", (i.hxfer.u == 0) ? "-" : "", (i.hxfer.imm_hi << 4) | i.hxfer.rm); - } else { - dump_reg(dis, i.hxfer.rm); - } - - if (i.hxfer.p != 0) { - /* close pre-index instr, also check for write-back */ - fprintf(dis->dis_out, "]%s", (i.hxfer.wb != 0) ? "!" : ""); - } -} - - -void dump_mrt(ARMDis* dis, ARMInstr i) { - fprintf(dis->dis_out, "%s%s%s%s\t", (i.mrt.ls == 0) ? "stm" : "ldm", cond[i.mrt.cond], - (i.mrt.u == 0) ? "d" : "i", (i.mrt.p == 0) ? "a" : "b"); - dump_reg(dis, i.mrt.rn); - fprintf(dis->dis_out, "%s, {", (i.mrt.wb != 0) ? "!" : ""); - dump_reglist(dis, i.mrt.reg_list); - fprintf(dis->dis_out, "}"); -} - - -void dump_swp(ARMDis* dis, ARMInstr i) { - fprintf(dis->dis_out, "swp%s%s ", cond[i.swp.cond], (i.swp.b != 0) ? "b" : ""); - dump_reg(dis, i.swp.rd); - fprintf(dis->dis_out, ", "); - dump_reg(dis, i.swp.rm); - fprintf(dis->dis_out, ", ["); - dump_reg(dis, i.swp.rn); - fprintf(dis->dis_out, "]"); -} - - -void dump_mul(ARMDis* dis, ARMInstr i) { - fprintf(dis->dis_out, "%s%s%s\t", mul_ops[i.mul.opcode], cond[i.mul.cond], (i.mul.s != 0) ? "s" : ""); - switch (i.mul.opcode) { - case ARMOP_MUL: - dump_reg(dis, i.mul.rd); - fprintf(dis->dis_out, ", "); - dump_reg(dis, i.mul.rm); - fprintf(dis->dis_out, ", "); - dump_reg(dis, i.mul.rs); - break; - case ARMOP_MLA: - dump_reg(dis, i.mul.rd); - fprintf(dis->dis_out, ", "); - dump_reg(dis, i.mul.rm); - fprintf(dis->dis_out, ", "); - dump_reg(dis, i.mul.rs); - fprintf(dis->dis_out, ", "); - dump_reg(dis, i.mul.rn); - break; - case ARMOP_UMULL: - case ARMOP_UMLAL: - case ARMOP_SMULL: - case ARMOP_SMLAL: - dump_reg(dis, i.mul.rd); - fprintf(dis->dis_out, ", "); - dump_reg(dis, i.mul.rn); - fprintf(dis->dis_out, ", "); - dump_reg(dis, i.mul.rm); - fprintf(dis->dis_out, ", "); - dump_reg(dis, i.mul.rs); - break; - default: - fprintf(dis->dis_out, "DCD 0x%x\t; ", i.raw); - break; - } -} - - -void dump_cdp(ARMDis* dis, ARMInstr i) { - fprintf(dis->dis_out, "cdp%s\tp%d, %d, ", cond[i.generic.cond], i.cdp.cpn, i.cdp.op); - dump_creg(dis, i.cdp.crd); - fprintf(dis->dis_out, ", "); - dump_creg(dis, i.cdp.crn); - fprintf(dis->dis_out, ", "); - dump_creg(dis, i.cdp.crm); - - if (i.cdp.op2 != 0) { - fprintf(dis->dis_out, ", %d", i.cdp.op2); - } -} - - -void dump_cdt(ARMDis* dis, ARMInstr i) { - fprintf(dis->dis_out, "%s%s%s\tp%d, ", (i.cdt.ls == 0) ? "stc" : "ldc", - cond[i.generic.cond], (i.cdt.n != 0) ? "l" : "", i.cdt.cpn); - dump_creg(dis, i.cdt.crd); - fprintf(dis->dis_out, ", "); - dump_reg(dis, i.cdt.rn); - - if (i.cdt.p == 0) { - fprintf(dis->dis_out, "]"); - } - - if (i.cdt.offs != 0) { - fprintf(dis->dis_out, ", #%d", i.cdt.offs); - } - - if (i.cdt.p != 0) { - fprintf(dis->dis_out, "]%s", (i.cdt.wb != 0) ? "!" : ""); - } -} - - -void dump_crt(ARMDis* dis, ARMInstr i) { - fprintf(dis->dis_out, "%s%s\tp%d, %d, ", (i.crt.ls == 0) ? "mrc" : "mcr", - cond[i.generic.cond], i.crt.cpn, i.crt.op1); - dump_reg(dis, i.crt.rd); - fprintf(dis->dis_out, ", "); - dump_creg(dis, i.crt.crn); - fprintf(dis->dis_out, ", "); - dump_creg(dis, i.crt.crm); - - if (i.crt.op2 != 0) { - fprintf(dis->dis_out, ", %d", i.crt.op2); - } -} - - -void dump_msr(ARMDis* dis, ARMInstr i) { - fprintf(dis->dis_out, "msr%s\t%spsr_, ", cond[i.generic.cond], - (i.msr.all.sel == 0) ? "s" : "c"); - if (i.msr.all.type == 0) { - /* reg */ - fprintf(dis->dis_out, "%s, ", msr_fld[i.msr.all.fld]); - dump_reg(dis, i.msr.all.rm); - } else { - /* imm */ - fprintf(dis->dis_out, "f, #%d", i.msr.op2_imm.imm << i.msr.op2_imm.rot); - } -} - - -void dump_mrs(ARMDis* dis, ARMInstr i) { - fprintf(dis->dis_out, "mrs%s\t", cond[i.generic.cond]); - dump_reg(dis, i.mrs.rd); - fprintf(dis->dis_out, ", %spsr", (i.mrs.sel == 0) ? "s" : "c"); -} - - -void dump_swi(ARMDis* dis, ARMInstr i) { - fprintf(dis->dis_out, "swi%s\t%d", cond[i.generic.cond], i.swi.num); -} - - -void dump_clz(ARMDis* dis, ARMInstr i) { - fprintf(dis->dis_out, "clz\t"); - dump_reg(dis, i.clz.rd); - fprintf(dis->dis_out, ", "); - dump_reg(dis, i.clz.rm); - fprintf(dis->dis_out, "\n"); -} - - - -void armdis_decode(ARMDis* dis, void* p, int size) { - int i; - arminstr_t* pi = (arminstr_t*)p; - ARMInstr instr; - - if (dis == NULL) return; - - chk_out(dis); - - size/=sizeof(arminstr_t); - - for (i=0; idis_out, "%p:\t%08x\t", pi, *pi); - dis->pi = pi; - instr.raw = *pi++; - - if ((instr.raw & ARM_BR_MASK) == ARM_BR_TAG) { - dump_br(dis, instr); - } else if ((instr.raw & ARM_SWP_MASK) == ARM_SWP_TAG) { - dump_swp(dis, instr); - } else if ((instr.raw & ARM_MUL_MASK) == ARM_MUL_TAG) { - dump_mul(dis, instr); - } else if ((instr.raw & ARM_CLZ_MASK) == ARM_CLZ_TAG) { - dump_clz(dis, instr); - } else if ((instr.raw & ARM_WXFER_MASK) == ARM_WXFER_TAG) { - dump_wxfer(dis, instr); - } else if ((instr.raw & ARM_HXFER_MASK) == ARM_HXFER_TAG) { - dump_hxfer(dis, instr); - } else if ((instr.raw & ARM_DPI_MASK) == ARM_DPI_TAG) { - dump_dpi(dis, instr); - } else if ((instr.raw & ARM_MRT_MASK) == ARM_MRT_TAG) { - dump_mrt(dis, instr); - } else if ((instr.raw & ARM_CDP_MASK) == ARM_CDP_TAG) { - dump_cdp(dis, instr); - } else if ((instr.raw & ARM_CDT_MASK) == ARM_CDT_TAG) { - dump_cdt(dis, instr); - } else if ((instr.raw & ARM_CRT_MASK) == ARM_CRT_TAG) { - dump_crt(dis, instr); - } else if ((instr.raw & ARM_MSR_MASK) == ARM_MSR_TAG) { - dump_msr(dis, instr); - } else if ((instr.raw & ARM_MRS_MASK) == ARM_MRS_TAG) { - dump_mrs(dis, instr); - } else if ((instr.raw & ARM_SWI_MASK) == ARM_SWI_TAG) { - dump_swi(dis, instr); - } else { - fprintf(dis->dis_out, "DCD 0x%x\t; ", instr.raw); - } - - fprintf(dis->dis_out, "\n"); - } -} - - -void armdis_open(ARMDis* dis, const char* dump_name) { - if (dis != NULL && dump_name != NULL) { - armdis_set_output(dis, fopen(dump_name, "w")); - } -} - - -void armdis_close(ARMDis* dis) { - if (dis->dis_out != NULL && dis->dis_out != stdout && dis->dis_out != stderr) { - fclose(dis->dis_out); - dis->dis_out = NULL; - } -} - - -void armdis_dump(ARMDis* dis, const char* dump_name, void* p, int size) { - armdis_open(dis, dump_name); - armdis_decode(dis, p, size); - armdis_close(dis); -} - - -void armdis_init(ARMDis* dis) { - if (dis != NULL) { - /* set to stdout */ - armdis_set_output(dis, NULL); - } -} - - - - -void init_gdisasm() { - if (gdisasm == NULL) { - gdisasm = (ARMDis*)malloc(sizeof(ARMDis)); - armdis_init(gdisasm); - } -} - -void _armdis_set_output(FILE* f) { - init_gdisasm(); - armdis_set_output(gdisasm, f); -} - -FILE* _armdis_get_output() { - init_gdisasm(); - return armdis_get_output(gdisasm); -} - -void _armdis_decode(void* p, int size) { - init_gdisasm(); - armdis_decode(gdisasm, p, size); -} - -void _armdis_open(const char* dump_name) { - init_gdisasm(); - armdis_open(gdisasm, dump_name); -} - -void _armdis_close() { - init_gdisasm(); - armdis_close(gdisasm); -} - -void _armdis_dump(const char* dump_name, void* p, int size) { - init_gdisasm(); - armdis_dump(gdisasm, dump_name, p, size); -} - diff --git a/asm/arm-dis.h b/asm/arm-dis.h deleted file mode 100644 index 8019499ce9..0000000000 --- a/asm/arm-dis.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2002 Sergey Chaban - */ - -#ifndef ARM_DIS -#define ARM_DIS - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct _ARMDis { - FILE* dis_out; - void* pi; -} ARMDis; - - -void _armdis_set_output(FILE* f); -FILE* _armdis_get_output(void); -void _armdis_decode(void* p, int size); -void _armdis_open(const char* dump_name); -void _armdis_close(void); -void _armdis_dump(const char* dump_name, void* p, int size); - - -void armdis_init(ARMDis* dis); -void armdis_set_output(ARMDis* dis, FILE* f); -FILE* armdis_get_output(ARMDis* dis); -void armdis_decode(ARMDis* dis, void* p, int size); -void armdis_open(ARMDis* dis, const char* dump_name); -void armdis_close(ARMDis* dis); -void armdis_dump(ARMDis* dis, const char* dump_name, void* p, int size); - -#ifdef __cplusplus -} -#endif - -#endif /* ARM_DIS */ diff --git a/asm/arm-fpa-codegen.h b/asm/arm-fpa-codegen.h deleted file mode 100644 index 4389a5e79a..0000000000 --- a/asm/arm-fpa-codegen.h +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2005 Novell Inc - * Copyright 2011 Xamarin Inc - */ - -#ifndef __MONO_ARM_FPA_CODEGEN_H__ -#define __MONO_ARM_FPA_CODEGEN_H__ - -#include "arm-codegen.h" - -enum { - /* FPA registers */ - ARM_FPA_F0, - ARM_FPA_F1, - ARM_FPA_F2, - ARM_FPA_F3, - ARM_FPA_F4, - ARM_FPA_F5, - ARM_FPA_F6, - ARM_FPA_F7, - - /* transfer length for LDF/STF (T0/T1), already shifted */ - ARM_FPA_SINGLE = 0, - ARM_FPA_DOUBLE = 1 << 15, - - ARM_FPA_ADF = 0 << 20, - ARM_FPA_MUF = 1 << 20, - ARM_FPA_SUF = 2 << 20, - ARM_FPA_RSF = 3 << 20, - ARM_FPA_DVF = 4 << 20, - ARM_FPA_RDF = 5 << 20, - ARM_FPA_POW = 6 << 20, - ARM_FPA_RPW = 7 << 20, - ARM_FPA_RMF = 8 << 20, - ARM_FPA_FML = 9 << 20, - ARM_FPA_FDV = 10 << 20, - ARM_FPA_FRD = 11 << 20, - ARM_FPA_POL = 12 << 20, - - /* monadic */ - ARM_FPA_MVF = (0 << 20) | (1 << 15), - ARM_FPA_MNF = (1 << 20) | (1 << 15), - ARM_FPA_ABS = (2 << 20) | (1 << 15), - ARM_FPA_RND = (3 << 20) | (1 << 15), - ARM_FPA_SQT = (4 << 20) | (1 << 15), - ARM_FPA_LOG = (5 << 20) | (1 << 15), - ARM_FPA_LGN = (6 << 20) | (1 << 15), - ARM_FPA_EXP = (7 << 20) | (1 << 15), - ARM_FPA_SIN = (8 << 20) | (1 << 15), - ARM_FPA_COS = (9 << 20) | (1 << 15), - ARM_FPA_TAN = (10 << 20) | (1 << 15), - ARM_FPA_ASN = (11 << 20) | (1 << 15), - ARM_FPA_ACS = (12 << 20) | (1 << 15), - ARM_FPA_ATN = (13 << 20) | (1 << 15), - ARM_FPA_URD = (14 << 20) | (1 << 15), - ARM_FPA_NRM = (15 << 20) | (1 << 15), - - /* round modes */ - ARM_FPA_ROUND_NEAREST = 0, - ARM_FPA_ROUND_PINF = 1, - ARM_FPA_ROUND_MINF = 2, - ARM_FPA_ROUND_ZERO = 3, - - /* round precision */ - ARM_FPA_ROUND_SINGLE = 0, - ARM_FPA_ROUND_DOUBLE = 1, - - /* constants */ - ARM_FPA_CONST_0 = 8, - ARM_FPA_CONST_1_0 = 9, - ARM_FPA_CONST_2_0 = 10, - ARM_FPA_CONST_3_0 = 11, - ARM_FPA_CONST_4_0 = 12, - ARM_FPA_CONST_5_0 = 13, - ARM_FPA_CONST_0_5 = 14, - ARM_FPA_CONST_10 = 15, - - /* compares */ - ARM_FPA_CMF = 4, - ARM_FPA_CNF = 5, - ARM_FPA_CMFE = 6, - ARM_FPA_CNFE = 7, - - /* CPRT ops */ - ARM_FPA_FLT = 0, - ARM_FPA_FIX = 1, - ARM_FPA_WFS = 2, - ARM_FPA_RFS = 3, - ARM_FPA_WFC = 4, - ARM_FPA_RFC = 5 -}; - -#define ARM_DEF_FPA_LDF_STF(cond,post,ls,fptype,wback,basereg,fdreg,offset) \ - ((offset) >= 0? (offset)>>2: -(offset)>>2) | \ - ((1 << 8) | (fptype)) | \ - ((fdreg) << 12) | \ - ((basereg) << 16) | \ - ((ls) << 20) | \ - ((wback) << 21) | \ - (((offset) >= 0) << 23) | \ - ((wback) << 21) | \ - ((post) << 24) | \ - (6 << 25) | \ - ARM_DEF_COND(cond) - -/* FP load and stores */ -#define ARM_FPA_LDFS_COND(p,freg,base,offset,cond) \ - ARM_EMIT((p), ARM_DEF_FPA_LDF_STF((cond),1,ARMOP_LDR,ARM_FPA_SINGLE,0,(base),(freg),(offset))) -#define ARM_FPA_LDFS(p,freg,base,offset) \ - ARM_FPA_LDFS_COND(p,freg,base,offset,ARMCOND_AL) - -#define ARM_FPA_LDFD_COND(p,freg,base,offset,cond) \ - ARM_EMIT((p), ARM_DEF_FPA_LDF_STF((cond),1,ARMOP_LDR,ARM_FPA_DOUBLE,0,(base),(freg),(offset))) -#define ARM_FPA_LDFD(p,freg,base,offset) \ - ARM_FPA_LDFD_COND(p,freg,base,offset,ARMCOND_AL) - -#define ARM_FPA_STFS_COND(p,freg,base,offset,cond) \ - ARM_EMIT((p), ARM_DEF_FPA_LDF_STF((cond),1,ARMOP_STR,ARM_FPA_SINGLE,0,(base),(freg),(offset))) -#define ARM_FPA_STFS(p,freg,base,offset) \ - ARM_FPA_STFS_COND(p,freg,base,offset,ARMCOND_AL) - -#define ARM_FPA_STFD_COND(p,freg,base,offset,cond) \ - ARM_EMIT((p), ARM_DEF_FPA_LDF_STF((cond),1,ARMOP_STR,ARM_FPA_DOUBLE,0,(base),(freg),(offset))) -#define ARM_FPA_STFD(p,freg,base,offset) \ - ARM_FPA_STFD_COND(p,freg,base,offset,ARMCOND_AL) - -#define ARM_DEF_FPA_CPDO_MONADIC(cond,op,dreg,sreg,round,prec) \ - (1 << 8) | (14 << 24) | \ - (op) | \ - ((sreg) << 0) | \ - ((round) << 5) | \ - ((dreg) << 12) | \ - ((prec) << 7) | \ - ARM_DEF_COND(cond) - -#define ARM_DEF_FPA_CPDO_DYADIC(cond,op,dreg,sreg1,sreg2,round,prec) \ - (1 << 8) | (14 << 24) | \ - (op) | \ - ((sreg1) << 16) | \ - ((sreg2) << 0) | \ - ((round) << 5) | \ - ((dreg) << 12) | \ - ((prec) << 7) | \ - ARM_DEF_COND(cond) - -#define ARM_DEF_FPA_CMP(cond,op,sreg1,sreg2) \ - (1 << 4) | (1 << 8) | (15 << 12) | \ - (1 << 20) | (14 << 24) | \ - (op) << 21 | \ - (sreg1) << 16 | \ - (sreg2) | \ - ARM_DEF_COND(cond) - -#define ARM_DEF_FPA_CPRT(cond,op,fn,fm,rd,ftype,round) \ - (1 << 4) | (1 << 8) | (14 << 24) | \ - (op) << 20 | \ - (fm) | \ - (fn) << 16 | \ - (rd) << 12 | \ - ((round) << 5) | \ - ((ftype) << 7) | \ - ARM_DEF_COND(cond) - - -#include "arm_fpamacros.h" - -#define ARM_FPA_RNDDZ_COND(p,dreg,sreg,cond) \ - ARM_EMIT((p), ARM_DEF_FPA_CPDO_MONADIC((cond),ARM_FPA_RND,(dreg),(sreg),ARM_FPA_ROUND_ZERO,ARM_FPA_ROUND_DOUBLE)) -#define ARM_FPA_RNDDZ(p,dreg,sreg) ARM_FPA_RNDD_COND(p,dreg,sreg,ARMCOND_AL) - -/* compares */ -#define ARM_FPA_FCMP_COND(p,op,sreg1,sreg2,cond) \ - ARM_EMIT(p, ARM_DEF_FPA_CMP(cond,op,sreg1,sreg2)) -#define ARM_FPA_FCMP(p,op,sreg1,sreg2) ARM_FPA_FCMP_COND(p,op,sreg1,sreg2,ARMCOND_AL) - -/* coprocessor register transfer */ -#define ARM_FPA_FLTD(p,fn,rd) \ - ARM_EMIT(p, ARM_DEF_FPA_CPRT(ARMCOND_AL,ARM_FPA_FLT,(fn),0,(rd),ARM_FPA_ROUND_DOUBLE,ARM_FPA_ROUND_NEAREST)) -#define ARM_FPA_FLTS(p,fn,rd) \ - ARM_EMIT(p, ARM_DEF_FPA_CPRT(ARMCOND_AL,ARM_FPA_FLT,(fn),0,(rd),ARM_FPA_ROUND_SINGLE,ARM_FPA_ROUND_NEAREST)) - -#define ARM_FPA_FIXZ(p,rd,fm) \ - ARM_EMIT(p, ARM_DEF_FPA_CPRT(ARMCOND_AL,ARM_FPA_FIX,0,(fm),(rd),0,ARM_FPA_ROUND_ZERO)) - -#define ARM_FPA_WFS(p,rd) \ - ARM_EMIT(p, ARM_DEF_FPA_CPRT(ARMCOND_AL,ARM_FPA_WFS,0,0,(rd),0,ARM_FPA_ROUND_NEAREST)) - -#define ARM_FPA_RFS(p,rd) \ - ARM_EMIT(p, ARM_DEF_FPA_CPRT(ARMCOND_AL,ARM_FPA_RFS,0,0,(rd),0,ARM_FPA_ROUND_NEAREST)) - -#define ARM_FPA_WFC(p,rd) \ - ARM_EMIT(p, ARM_DEF_FPA_CPRT(ARMCOND_AL,ARM_FPA_WFC,0,0,(rd),0,ARM_FPA_ROUND_NEAREST)) - -#define ARM_FPA_RFC(p,rd) \ - ARM_EMIT(p, ARM_DEF_FPA_CPRT(ARMCOND_AL,ARM_FPA_RFC,0,0,(rd),0,ARM_FPA_ROUND_NEAREST)) - -#endif /* __MONO_ARM_FPA_CODEGEN_H__ */ - diff --git a/asm/arm-vfp-codegen.h b/asm/arm-vfp-codegen.h deleted file mode 100644 index 8056f7bccc..0000000000 --- a/asm/arm-vfp-codegen.h +++ /dev/null @@ -1,235 +0,0 @@ -// -// Copyright 2011 Xamarin Inc -// - -#ifndef __MONO_ARM_VFP_CODEGEN_H__ -#define __MONO_ARM_VFP_CODEGEN_H__ - -#include "arm-codegen.h" - -enum { - /* FPA registers */ - ARM_VFP_F0, - ARM_VFP_F1, - ARM_VFP_F2, - ARM_VFP_F3, - ARM_VFP_F4, - ARM_VFP_F5, - ARM_VFP_F6, - ARM_VFP_F7, - ARM_VFP_F8, - ARM_VFP_F9, - ARM_VFP_F10, - ARM_VFP_F11, - ARM_VFP_F12, - ARM_VFP_F13, - ARM_VFP_F14, - ARM_VFP_F15, - ARM_VFP_F16, - ARM_VFP_F17, - ARM_VFP_F18, - ARM_VFP_F19, - ARM_VFP_F20, - ARM_VFP_F21, - ARM_VFP_F22, - ARM_VFP_F23, - ARM_VFP_F24, - ARM_VFP_F25, - ARM_VFP_F26, - ARM_VFP_F27, - ARM_VFP_F28, - ARM_VFP_F29, - ARM_VFP_F30, - ARM_VFP_F31, - - ARM_VFP_D0 = ARM_VFP_F0, - ARM_VFP_D1 = ARM_VFP_F2, - ARM_VFP_D2 = ARM_VFP_F4, - ARM_VFP_D3 = ARM_VFP_F6, - ARM_VFP_D4 = ARM_VFP_F8, - ARM_VFP_D5 = ARM_VFP_F10, - ARM_VFP_D6 = ARM_VFP_F12, - ARM_VFP_D7 = ARM_VFP_F14, - ARM_VFP_D8 = ARM_VFP_F16, - ARM_VFP_D9 = ARM_VFP_F18, - ARM_VFP_D10 = ARM_VFP_F20, - ARM_VFP_D11 = ARM_VFP_F22, - ARM_VFP_D12 = ARM_VFP_F24, - ARM_VFP_D13 = ARM_VFP_F26, - ARM_VFP_D14 = ARM_VFP_F28, - ARM_VFP_D15 = ARM_VFP_F30, - - ARM_VFP_COPROC_SINGLE = 10, - ARM_VFP_COPROC_DOUBLE = 11, - -#define ARM_VFP_OP(p,q,r,s) (((p) << 23) | ((q) << 21) | ((r) << 20) | ((s) << 6)) -#define ARM_VFP_OP2(Fn,N) (ARM_VFP_OP (1,1,1,1) | ((Fn) << 16) | ((N) << 7)) - - ARM_VFP_MUL = ARM_VFP_OP (0,1,0,0), - ARM_VFP_NMUL = ARM_VFP_OP (0,1,0,1), - ARM_VFP_ADD = ARM_VFP_OP (0,1,1,0), - ARM_VFP_SUB = ARM_VFP_OP (0,1,1,1), - ARM_VFP_DIV = ARM_VFP_OP (1,0,0,0), - - ARM_VFP_CPY = ARM_VFP_OP2 (0,0), - ARM_VFP_ABS = ARM_VFP_OP2 (0,1), - ARM_VFP_NEG = ARM_VFP_OP2 (1,0), - ARM_VFP_SQRT = ARM_VFP_OP2 (1,1), - ARM_VFP_CMP = ARM_VFP_OP2 (4,0), - ARM_VFP_CMPE = ARM_VFP_OP2 (4,1), - ARM_VFP_CMPZ = ARM_VFP_OP2 (5,0), - ARM_VFP_CMPEZ = ARM_VFP_OP2 (5,1), - ARM_VFP_CVT = ARM_VFP_OP2 (7,1), - ARM_VFP_UITO = ARM_VFP_OP2 (8,0), - ARM_VFP_SITO = ARM_VFP_OP2 (8,1), - ARM_VFP_TOUI = ARM_VFP_OP2 (12,0), - ARM_VFP_TOSI = ARM_VFP_OP2 (13,0), - ARM_VFP_TOUIZ = ARM_VFP_OP2 (12,1), - ARM_VFP_TOSIZ = ARM_VFP_OP2 (13,1), - - ARM_VFP_SID = 0, - ARM_VFP_SCR = 1 << 1, - ARM_VFP_EXC = 8 << 1 -}; - -#define ARM_DEF_VFP_DYADIC(cond,cp,op,Fd,Fn,Fm) \ - (14 << 24) | \ - ((cp) << 8) | \ - (op) | \ - (((Fd) >> 1) << 12) | \ - (((Fd) & 1) << 22) | \ - (((Fn) >> 1) << 16) | \ - (((Fn) & 1) << 7) | \ - (((Fm) >> 1) << 0) | \ - (((Fm) & 1) << 5) | \ - ARM_DEF_COND(cond) - -#define ARM_DEF_VFP_MONADIC(cond,cp,op,Fd,Fm) \ - (14 << 24) | \ - ((cp) << 8) | \ - (op) | \ - (((Fd) >> 1) << 12) | \ - (((Fd) & 1) << 22) | \ - (((Fm) >> 1) << 0) | \ - (((Fm) & 1) << 5) | \ - ARM_DEF_COND(cond) - -#define ARM_DEF_VFP_LSF(cond,cp,post,ls,wback,basereg,Fd,offset) \ - ((offset) >= 0? (offset)>>2: -(offset)>>2) | \ - (6 << 25) | \ - ((cp) << 8) | \ - (((Fd) >> 1) << 12) | \ - (((Fd) & 1) << 22) | \ - ((basereg) << 16) | \ - ((ls) << 20) | \ - ((wback) << 21) | \ - (((offset) >= 0) << 23) | \ - ((wback) << 21) | \ - ((post) << 24) | \ - ARM_DEF_COND(cond) - -#define ARM_DEF_VFP_CPT(cond,cp,op,L,Fn,Rd) \ - (14 << 24) | \ - (1 << 4) | \ - ((cp) << 8) | \ - ((op) << 21) | \ - ((L) << 20) | \ - ((Rd) << 12) | \ - (((Fn) >> 1) << 16) | \ - (((Fn) & 1) << 7) | \ - ARM_DEF_COND(cond) - -/* FP load and stores */ -#define ARM_FLDS_COND(p,freg,base,offset,cond) \ - ARM_EMIT((p), ARM_DEF_VFP_LSF((cond),ARM_VFP_COPROC_SINGLE,1,ARMOP_LDR,0,(base),(freg),(offset))) -#define ARM_FLDS(p,freg,base,offset) \ - ARM_FLDS_COND(p,freg,base,offset,ARMCOND_AL) - -#define ARM_FLDD_COND(p,freg,base,offset,cond) \ - ARM_EMIT((p), ARM_DEF_VFP_LSF((cond),ARM_VFP_COPROC_DOUBLE,1,ARMOP_LDR,0,(base),(freg),(offset))) -#define ARM_FLDD(p,freg,base,offset) \ - ARM_FLDD_COND(p,freg,base,offset,ARMCOND_AL) - -#define ARM_FSTS_COND(p,freg,base,offset,cond) \ - ARM_EMIT((p), ARM_DEF_VFP_LSF((cond),ARM_VFP_COPROC_SINGLE,1,ARMOP_STR,0,(base),(freg),(offset))) -#define ARM_FSTS(p,freg,base,offset) \ - ARM_FSTS_COND(p,freg,base,offset,ARMCOND_AL) - -#define ARM_FSTD_COND(p,freg,base,offset,cond) \ - ARM_EMIT((p), ARM_DEF_VFP_LSF((cond),ARM_VFP_COPROC_DOUBLE,1,ARMOP_STR,0,(base),(freg),(offset))) -#define ARM_FSTD(p,freg,base,offset) \ - ARM_FSTD_COND(p,freg,base,offset,ARMCOND_AL) - -#include "arm_vfpmacros.h" - -/* coprocessor register transfer */ -#define ARM_FMSR(p,freg,reg) \ - ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_SINGLE,0,0,(freg),(reg))) -#define ARM_FMRS(p,reg,freg) \ - ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_SINGLE,0,1,(freg),(reg))) - -#define ARM_FMDLR(p,freg,reg) \ - ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,0,0,(freg),(reg))) -#define ARM_FMRDL(p,reg,freg) \ - ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,0,1,(freg),(reg))) -#define ARM_FMDHR(p,freg,reg) \ - ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,1,0,(freg),(reg))) -#define ARM_FMRDH(p,reg,freg) \ - ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,1,1,(freg),(reg))) - -#define ARM_FMXR(p,freg,reg) \ - ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_SINGLE,7,0,(freg),(reg))) -#define ARM_FMRX(p,reg,fcreg) \ - ARM_EMIT((p), ARM_DEF_VFP_CPT(ARMCOND_AL,ARM_VFP_COPROC_SINGLE,7,1,(fcreg),(reg))) - -#define ARM_FMSTAT(p) \ - ARM_FMRX((p),ARMREG_R15,ARM_VFP_SCR) - -#define ARM_DEF_MCRR(cond,cp,rn,rd,Fm,M) \ - ((Fm) << 0) | \ - (1 << 4) | \ - ((M) << 5) | \ - ((cp) << 8) | \ - ((rd) << 12) | \ - ((rn) << 16) | \ - ((2) << 21) | \ - (12 << 24) | \ - ARM_DEF_COND(cond) - -#define ARM_FMDRR(p,rd,rn,dm) \ - ARM_EMIT((p), ARM_DEF_MCRR(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,(rn),(rd),(dm) >> 1, (dm) & 1)) - -#define ARM_DEF_FMRRD(cond,cp,rn,rd,Dm,D) \ - ((Dm) << 0) | \ - (1 << 4) | \ - ((cp) << 8) | \ - ((rd) << 12) | \ - ((rn) << 16) | \ - ((0xc5) << 20) | \ - ARM_DEF_COND(cond) - -#define ARM_FMRRD(p,rd,rn,dm) \ - ARM_EMIT((p), ARM_DEF_FMRRD(ARMCOND_AL,ARM_VFP_COPROC_DOUBLE,(rn),(rd),(dm) >> 1, (dm) & 1)) - -#define ARM_DEF_FUITOS(cond,Dd,D,Fm,M) ((cond) << 28) | ((0x1d) << 23) | ((D) << 22) | ((0x3) << 20) | ((8) << 16) | ((Dd) << 12) | ((0xa) << 8) | ((1) << 6) | ((M) << 5) | ((Fm) << 0) - -#define ARM_FUITOS(p,dreg,sreg) \ - ARM_EMIT((p), ARM_DEF_FUITOS (ARMCOND_AL, (dreg) >> 1, (dreg) & 1, (sreg) >> 1, (sreg) & 1)) - -#define ARM_DEF_FUITOD(cond,Dd,D,Fm,M) ((cond) << 28) | ((0x1d) << 23) | ((D) << 22) | ((0x3) << 20) | ((8) << 16) | ((Dd) << 12) | ((0xb) << 8) | ((1) << 6) | ((M) << 5) | ((Fm) << 0) - -#define ARM_FUITOD(p,dreg,sreg) \ - ARM_EMIT((p), ARM_DEF_FUITOD (ARMCOND_AL, (dreg) >> 1, (dreg) & 1, (sreg) >> 1, (sreg) & 1)) - -#define ARM_DEF_FSITOS(cond,Dd,D,Fm,M) ((cond) << 28) | ((0x1d) << 23) | ((D) << 22) | ((0x3) << 20) | ((8) << 16) | ((Dd) << 12) | ((0xa) << 8) | ((1) << 7) | ((1) << 6) | ((M) << 5) | ((Fm) << 0) - -#define ARM_FSITOS(p,dreg,sreg) \ - ARM_EMIT((p), ARM_DEF_FSITOS (ARMCOND_AL, (dreg) >> 1, (dreg) & 1, (sreg) >> 1, (sreg) & 1)) - -#define ARM_DEF_FSITOD(cond,Dd,D,Fm,M) ((cond) << 28) | ((0x1d) << 23) | ((D) << 22) | ((0x3) << 20) | ((8) << 16) | ((Dd) << 12) | ((0xb) << 8) | ((1) << 7) | ((1) << 6) | ((M) << 5) | ((Fm) << 0) - -#define ARM_FSITOD(p,dreg,sreg) \ - ARM_EMIT((p), ARM_DEF_FSITOD (ARMCOND_AL, (dreg) >> 1, (dreg) & 1, (sreg) >> 1, (sreg) & 1)) - -#endif /* __MONO_ARM_VFP_CODEGEN_H__ */ - diff --git a/asm/arm-wmmx.h b/asm/arm-wmmx.h deleted file mode 100644 index 427c4fc9f6..0000000000 --- a/asm/arm-wmmx.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * ARM CodeGen - * XScale WirelessMMX extensions - * Copyright 2002 Wild West Software - */ - -#ifndef __WMMX_H__ -#define __WMMX_H__ 1 - -#if 0 -#include -#endif - -#if defined(ARM_IASM) -# define WM_ASM(_expr) ARM_IASM(_expr) -#else -# define WM_ASM(_expr) __emit (_expr) -#endif - -#if defined(ARM_EMIT) -# define WM_EMIT(p, i) ARM_EMIT(p, i) -#else -# define WM_EMIT(p, i) -#endif - -enum { - WM_CC_EQ = 0x0, - WM_CC_NE = 0x1, - WM_CC_CS = 0x2, - WM_CC_HS = WM_CC_CS, - WM_CC_CC = 0x3, - WM_CC_LO = WM_CC_CC, - WM_CC_MI = 0x4, - WM_CC_PL = 0x5, - WM_CC_VS = 0x6, - WM_CC_VC = 0x7, - WM_CC_HI = 0x8, - WM_CC_LS = 0x9, - WM_CC_GE = 0xA, - WM_CC_LT = 0xB, - WM_CC_GT = 0xC, - WM_CC_LE = 0xD, - WM_CC_AL = 0xE, - WM_CC_NV = 0xF, - WM_CC_SHIFT = 28 -}; - -#if defined(ARM_DEF_COND) -# define WM_DEF_CC(_cc) ARM_DEF_COND(_cc) -#else -# define WM_DEF_CC(_cc) ((_cc & 0xF) << WM_CC_SHIFT) -#endif - - -enum { - WM_R0 = 0x0, - WM_R1 = 0x1, - WM_R2 = 0x2, - WM_R3 = 0x3, - WM_R4 = 0x4, - WM_R5 = 0x5, - WM_R6 = 0x6, - WM_R7 = 0x7, - WM_R8 = 0x8, - WM_R9 = 0x9, - WM_R10 = 0xA, - WM_R11 = 0xB, - WM_R12 = 0xC, - WM_R13 = 0xD, - WM_R14 = 0xE, - WM_R15 = 0xF, - - WM_wR0 = 0x0, - WM_wR1 = 0x1, - WM_wR2 = 0x2, - WM_wR3 = 0x3, - WM_wR4 = 0x4, - WM_wR5 = 0x5, - WM_wR6 = 0x6, - WM_wR7 = 0x7, - WM_wR8 = 0x8, - WM_wR9 = 0x9, - WM_wR10 = 0xA, - WM_wR11 = 0xB, - WM_wR12 = 0xC, - WM_wR13 = 0xD, - WM_wR14 = 0xE, - WM_wR15 = 0xF -}; - - -/* - * Qualifiers: - * H - 16-bit (HalfWord) SIMD - * W - 32-bit (Word) SIMD - * D - 64-bit (Double) - */ -enum { - WM_B = 0, - WM_H = 1, - WM_D = 2 -}; - -/* - * B.2.3 Transfers From Coprocessor Register (MRC) - * Table B-5 - */ -enum { - WM_TMRC_OP2 = 0, - WM_TMRC_CPNUM = 1, - - WM_TMOVMSK_OP2 = 1, - WM_TMOVMSK_CPNUM = 0, - - WM_TANDC_OP2 = 1, - WM_TANDC_CPNUM = 1, - - WM_TORC_OP2 = 2, - WM_TORC_CPNUM = 1, - - WM_TEXTRC_OP2 = 3, - WM_TEXTRC_CPNUM = 1, - - WM_TEXTRM_OP2 = 3, - WM_TEXTRM_CPNUM = 0 -}; - - -/* - * TANDC{Cond} R15 - * Performs AND across the fields of the SIMD PSR register (wCASF) and sends the result - * to CPSR; can be performed after a Byte, Half-word or Word operation that sets the flags. - * NOTE: R15 is omitted from the macro declaration; - */ -#define DEF_WM_TNADC_CC(_q, _cc) WM_DEF_CC((_cc)) + ((_q) << 0x16) + 0xE13F130 - -#define _WM_TNADC_CC(_q, _cc) WM_ASM(DEF_WM_TNADC_CC(_q, _cc)) -#define ARM_WM_TNADC_CC(_p, _q, _cc) WM_EMIT(_p, DEF_WM_TNADC_CC(_q, _cc)) - -/* inline assembly */ -#define _WM_TNADC(_q) _WM_TNADC_CC((_q), WM_CC_AL) -#define _WM_TNADCB() _WM_TNADC(WM_B) -#define _WM_TNADCH() _WM_TNADC(WM_H) -#define _WM_TNADCD() _WM_TNADC(WM_D) - -/* codegen */ -#define ARM_WM_TNADC(_p, _q) ARM_WM_TNADC_CC((_p), (_q), WM_CC_AL) -#define ARM_WM_TNADCB(_p) ARM_WM_TNADC(_p, WM_B) -#define ARM_WM_TNADCH(_p) ARM_WM_TNADC(_p, WM_H) -#define ARM_WM_TNADCD(_p) ARM_WM_TNADC(_p, WM_D) - - -/* - * TBCST{Cond} wRd, Rn - * Broadcasts a value from the ARM Source reg (Rn) to every SIMD position - * in the WMMX Destination reg (wRd). - */ -#define DEF_WM_TBCST_CC(_q, _cc, _wrd, _rn) \ - WM_DEF_CC((_cc)) + ((_q) << 6) + ((_wrd) << 16) + ((_rn) << 12) + 0xE200010 - -#define _WM_TBCST_CC(_q, _cc, _wrd, _rn) WM_ASM(DEF_WM_TBCST_CC(_q, _cc, _wrd, _rn)) -#define ARM_WM_TBCST_CC(_p, _q, _cc, _wrd, _rn) WM_EMIT(_p, DEF_WM_TBCST_CC(_q, _cc, _wrd, _rn)) - -/* inline */ -#define _WM_TBCST(_q, _wrd, _rn) _WM_TBCST_CC(_q, WM_CC_AL, _wrd, _rn) -#define _WM_TBCSTB(_wrd, _rn) _WM_TBCST(WM_B) -#define _WM_TBCSTH(_wrd, _rn) _WM_TBCST(WM_H) -#define _WM_TBCSTD(_wrd, _rn) _WM_TBCST(WM_D) - -/* codegen */ -#define ARM_WM_TBCST(_p, _q, _wrd, _rn) ARM_WM_TBCST_CC(_p, _q, WM_CC_AL, _wrd, _rn) -#define ARM_WM_TBCSTB(_p, _wrd, _rn) _WM_TBCST(_p, WM_B) -#define ARM_WM_TBCSTH(_p, _wrd, _rn) _WM_TBCST(_p, WM_H) -#define ARM_WM_TBCSTD(_p, _wrd, _rn) _WM_TBCST(_p, WM_D) - - -#endif /* __WMMX_H__ */ diff --git a/asm/arm_dpimacros.h b/asm/arm_dpimacros.h deleted file mode 100644 index d8ff666997..0000000000 --- a/asm/arm_dpimacros.h +++ /dev/null @@ -1,1603 +0,0 @@ -/* Macros for DPI ops, auto-generated from template */ - - -/* mov/mvn */ - -/* Rd := imm8 ROR rot */ -#define ARM_MOV_REG_IMM_COND(p, reg, imm8, rot, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_MOV, reg, 0, imm8, rot, cond) -#define ARM_MOV_REG_IMM(p, reg, imm8, rot) \ - ARM_MOV_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) -/* S */ -#define ARM_MOVS_REG_IMM_COND(p, reg, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_MOV, reg, 0, imm8, rot, cond) -#define ARM_MOVS_REG_IMM(p, reg, imm8, rot) \ - ARM_MOVS_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _MOV_REG_IMM_COND(reg, imm8, rot, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_MOV, reg, 0, imm8, rot, cond) -#define _MOV_REG_IMM(reg, imm8, rot) \ - _MOV_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) -/* S */ -#define _MOVS_REG_IMM_COND(reg, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_MOV, reg, 0, imm8, rot, cond) -#define _MOVS_REG_IMM(reg, imm8, rot) \ - _MOVS_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) -#endif - - -/* Rd := imm8 */ -#define ARM_MOV_REG_IMM8_COND(p, reg, imm8, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_MOV, reg, 0, imm8, 0, cond) -#define ARM_MOV_REG_IMM8(p, reg, imm8) \ - ARM_MOV_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) -/* S */ -#define ARM_MOVS_REG_IMM8_COND(p, reg, imm8, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_MOV, reg, 0, imm8, 0, cond) -#define ARM_MOVS_REG_IMM8(p, reg, imm8) \ - ARM_MOVS_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _MOV_REG_IMM8_COND(reg, imm8, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_MOV, reg, 0, imm8, 0, cond) -#define _MOV_REG_IMM8(reg, imm8) \ - _MOV_REG_IMM8_COND(reg, imm8, ARMCOND_AL) -/* S */ -#define _MOVS_REG_IMM8_COND(reg, imm8, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_MOV, reg, 0, imm8, 0, cond) -#define _MOVS_REG_IMM8(reg, imm8) \ - _MOVS_REG_IMM8_COND(reg, imm8, ARMCOND_AL) -#endif - - -/* Rd := Rm */ -#define ARM_MOV_REG_REG_COND(p, rd, rm, cond) \ - ARM_DPIOP_REG_REG_COND(p, ARMOP_MOV, rd, 0, rm, cond) -#define ARM_MOV_REG_REG(p, rd, rm) \ - ARM_MOV_REG_REG_COND(p, rd, rm, ARMCOND_AL) -/* S */ -#define ARM_MOVS_REG_REG_COND(p, rd, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_MOV, rd, 0, rm, cond) -#define ARM_MOVS_REG_REG(p, rd, rm) \ - ARM_MOVS_REG_REG_COND(p, rd, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _MOV_REG_REG_COND(rd, rm, cond) \ - ARM_IASM_DPIOP_REG_REG_COND(ARMOP_MOV, rd, 0, rm, cond) -#define _MOV_REG_REG(rd, rm) \ - _MOV_REG_REG_COND(rd, rm, ARMCOND_AL) -/* S */ -#define _MOVS_REG_REG_COND(rd, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_MOV, rd, 0, rm, cond) -#define _MOVS_REG_REG(rd, rm) \ - _MOVS_REG_REG_COND(rd, rm, ARMCOND_AL) -#endif - - -/* Rd := Rm imm_shift */ -#define ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_MOV, rd, 0, rm, shift_type, imm_shift, cond) -#define ARM_MOV_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ - ARM_MOV_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) -/* S */ -#define ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_MOV, rd, 0, rm, shift_type, imm_shift, cond) -#define ARM_MOVS_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ - ARM_MOVS_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _MOV_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_MOV, rd, 0, rm, shift_type, imm_shift, cond) -#define _MOV_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ - _MOV_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) -/* S */ -#define _MOVS_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_MOV, rd, 0, rm, shift_type, imm_shift, cond) -#define _MOVS_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ - _MOVS_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - - -/* Rd := (Rm Rs) */ -#define ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ - ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_MOV, rd, 0, rm, shift_type, rs, cond) -#define ARM_MOV_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ - ARM_MOV_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) -/* S */ -#define ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ - ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_MOV, rd, 0, rm, shift_type, rs, cond) -#define ARM_MOVS_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ - ARM_MOVS_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _MOV_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_MOV, rd, 0, rm, shift_type, rs, cond) -#define _MOV_REG_REGSHIFT(rd, rm, shift_type, rs) \ - _MOV_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) -/* S */ -#define _MOVS_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_MOV, rd, 0, rm, shift_type, rs, cond) -#define _MOVS_REG_REGSHIFT(rd, rm, shift_type, rs) \ - _MOVS_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) -#endif - - -/* Rd := imm8 ROR rot */ -#define ARM_MVN_REG_IMM_COND(p, reg, imm8, rot, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_MVN, reg, 0, imm8, rot, cond) -#define ARM_MVN_REG_IMM(p, reg, imm8, rot) \ - ARM_MVN_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) -/* S */ -#define ARM_MVNS_REG_IMM_COND(p, reg, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_MVN, reg, 0, imm8, rot, cond) -#define ARM_MVNS_REG_IMM(p, reg, imm8, rot) \ - ARM_MVNS_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _MVN_REG_IMM_COND(reg, imm8, rot, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_MVN, reg, 0, imm8, rot, cond) -#define _MVN_REG_IMM(reg, imm8, rot) \ - _MVN_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) -/* S */ -#define _MVNS_REG_IMM_COND(reg, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_MVN, reg, 0, imm8, rot, cond) -#define _MVNS_REG_IMM(reg, imm8, rot) \ - _MVNS_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) -#endif - - -/* Rd := imm8 */ -#define ARM_MVN_REG_IMM8_COND(p, reg, imm8, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_MVN, reg, 0, imm8, 0, cond) -#define ARM_MVN_REG_IMM8(p, reg, imm8) \ - ARM_MVN_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) -/* S */ -#define ARM_MVNS_REG_IMM8_COND(p, reg, imm8, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_MVN, reg, 0, imm8, 0, cond) -#define ARM_MVNS_REG_IMM8(p, reg, imm8) \ - ARM_MVNS_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _MVN_REG_IMM8_COND(reg, imm8, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_MVN, reg, 0, imm8, 0, cond) -#define _MVN_REG_IMM8(reg, imm8) \ - _MVN_REG_IMM8_COND(reg, imm8, ARMCOND_AL) -/* S */ -#define _MVNS_REG_IMM8_COND(reg, imm8, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_MVN, reg, 0, imm8, 0, cond) -#define _MVNS_REG_IMM8(reg, imm8) \ - _MVNS_REG_IMM8_COND(reg, imm8, ARMCOND_AL) -#endif - - -/* Rd := Rm */ -#define ARM_MVN_REG_REG_COND(p, rd, rm, cond) \ - ARM_DPIOP_REG_REG_COND(p, ARMOP_MVN, rd, 0, rm, cond) -#define ARM_MVN_REG_REG(p, rd, rm) \ - ARM_MVN_REG_REG_COND(p, rd, rm, ARMCOND_AL) -/* S */ -#define ARM_MVNS_REG_REG_COND(p, rd, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_MVN, rd, 0, rm, cond) -#define ARM_MVNS_REG_REG(p, rd, rm) \ - ARM_MVNS_REG_REG_COND(p, rd, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _MVN_REG_REG_COND(rd, rm, cond) \ - ARM_IASM_DPIOP_REG_REG_COND(ARMOP_MVN, rd, 0, rm, cond) -#define _MVN_REG_REG(rd, rm) \ - _MVN_REG_REG_COND(rd, rm, ARMCOND_AL) -/* S */ -#define _MVNS_REG_REG_COND(rd, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_MVN, rd, 0, rm, cond) -#define _MVNS_REG_REG(rd, rm) \ - _MVNS_REG_REG_COND(rd, rm, ARMCOND_AL) -#endif - - -/* Rd := Rm imm_shift */ -#define ARM_MVN_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_MVN, rd, 0, rm, shift_type, imm_shift, cond) -#define ARM_MVN_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ - ARM_MVN_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) -/* S */ -#define ARM_MVNS_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_MVN, rd, 0, rm, shift_type, imm_shift, cond) -#define ARM_MVNS_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ - ARM_MVNS_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _MVN_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_MVN, rd, 0, rm, shift_type, imm_shift, cond) -#define _MVN_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ - _MVN_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) -/* S */ -#define _MVNS_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_MVN, rd, 0, rm, shift_type, imm_shift, cond) -#define _MVNS_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ - _MVNS_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - - -/* Rd := (Rm Rs) */ -#define ARM_MVN_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ - ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_MVN, rd, 0, rm, shift_type, rs, cond) -#define ARM_MVN_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ - ARM_MVN_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) -/* S */ -#define ARM_MVNS_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ - ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_MVN, rd, 0, rm, shift_type, rs, cond) -#define ARM_MVNS_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ - ARM_MVNS_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _MVN_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_MVN, rd, 0, rm, shift_type, rs, cond) -#define _MVN_REG_REGSHIFT(rd, rm, shift_type, rs) \ - _MVN_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) -/* S */ -#define _MVNS_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_MVN, rd, 0, rm, shift_type, rs, cond) -#define _MVNS_REG_REGSHIFT(rd, rm, shift_type, rs) \ - _MVNS_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) -#endif - - - -/* DPIs, arithmetic and logical */ - -/* -- AND -- */ - -/* Rd := Rn AND (imm8 ROR rot) ; rot is power of 2 */ -#define ARM_AND_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_AND, rd, rn, imm8, rot, cond) -#define ARM_AND_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_AND_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) -#define ARM_ANDS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_AND, rd, rn, imm8, rot, cond) -#define ARM_ANDS_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_ANDS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _AND_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_AND, rd, rn, imm8, rot, cond) -#define _AND_REG_IMM(rd, rn, imm8, rot) \ - _AND_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#define _ANDS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_AND, rd, rn, imm8, rot, cond) -#define _ANDS_REG_IMM(rd, rn, imm8, rot) \ - _ANDS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#endif - - -/* Rd := Rn AND imm8 */ -#define ARM_AND_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_AND_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_AND_REG_IMM8(p, rd, rn, imm8) \ - ARM_AND_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) -#define ARM_ANDS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_ANDS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_ANDS_REG_IMM8(p, rd, rn, imm8) \ - ARM_ANDS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _AND_REG_IMM8_COND(rd, rn, imm8, cond) \ - _AND_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _AND_REG_IMM8(rd, rn, imm8) \ - _AND_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#define _ANDS_REG_IMM8_COND(rd, rn, imm8, cond) \ - _ANDS_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _ANDS_REG_IMM8(rd, rn, imm8) \ - _ANDS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#endif - - -/* Rd := Rn AND Rm */ -#define ARM_AND_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_REG_REG_COND(p, ARMOP_AND, rd, rn, rm, cond) -#define ARM_AND_REG_REG(p, rd, rn, rm) \ - ARM_AND_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) -#define ARM_ANDS_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_AND, rd, rn, rm, cond) -#define ARM_ANDS_REG_REG(p, rd, rn, rm) \ - ARM_ANDS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _AND_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_REG_REG_COND(ARMOP_AND, rd, rn, rm, cond) -#define _AND_REG_REG(rd, rn, rm) \ - _AND_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#define _ANDS_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_AND, rd, rn, rm, cond) -#define _ANDS_REG_REG(rd, rn, rm) \ - _ANDS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#endif - - -/* Rd := Rn AND (Rm imm_shift) */ -#define ARM_AND_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_AND, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_AND_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_AND_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define ARM_ANDS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_AND, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_ANDS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_ANDS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _AND_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_AND, rd, rn, rm, shift_type, imm_shift, cond) -#define _AND_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _AND_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define _ANDS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_AND, rd, rn, rm, shift_type, imm_shift, cond) -#define _ANDS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _ANDS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - -/* Rd := Rn AND (Rm Rs) */ -#define ARM_AND_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_AND, rd, rn, rm, shift_t, rs, cond) -#define ARM_AND_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_AND_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define ARM_ANDS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_AND, rd, rn, rm, shift_t, rs, cond) -#define ARM_ANDS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_ANDS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _AND_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_AND, rd, rn, rm, shift_t, rs, cond) -#define _AND_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _AND_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define _ANDS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_AND, rd, rn, rm, shift_t, rs, cond) -#define _ANDS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _ANDS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#endif - - -/* -- EOR -- */ - -/* Rd := Rn EOR (imm8 ROR rot) ; rot is power of 2 */ -#define ARM_EOR_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_EOR, rd, rn, imm8, rot, cond) -#define ARM_EOR_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_EOR_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) -#define ARM_EORS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_EOR, rd, rn, imm8, rot, cond) -#define ARM_EORS_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_EORS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _EOR_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_EOR, rd, rn, imm8, rot, cond) -#define _EOR_REG_IMM(rd, rn, imm8, rot) \ - _EOR_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#define _EORS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_EOR, rd, rn, imm8, rot, cond) -#define _EORS_REG_IMM(rd, rn, imm8, rot) \ - _EORS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#endif - - -/* Rd := Rn EOR imm8 */ -#define ARM_EOR_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_EOR_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_EOR_REG_IMM8(p, rd, rn, imm8) \ - ARM_EOR_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) -#define ARM_EORS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_EORS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_EORS_REG_IMM8(p, rd, rn, imm8) \ - ARM_EORS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _EOR_REG_IMM8_COND(rd, rn, imm8, cond) \ - _EOR_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _EOR_REG_IMM8(rd, rn, imm8) \ - _EOR_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#define _EORS_REG_IMM8_COND(rd, rn, imm8, cond) \ - _EORS_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _EORS_REG_IMM8(rd, rn, imm8) \ - _EORS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#endif - - -/* Rd := Rn EOR Rm */ -#define ARM_EOR_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_REG_REG_COND(p, ARMOP_EOR, rd, rn, rm, cond) -#define ARM_EOR_REG_REG(p, rd, rn, rm) \ - ARM_EOR_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) -#define ARM_EORS_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_EOR, rd, rn, rm, cond) -#define ARM_EORS_REG_REG(p, rd, rn, rm) \ - ARM_EORS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _EOR_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_REG_REG_COND(ARMOP_EOR, rd, rn, rm, cond) -#define _EOR_REG_REG(rd, rn, rm) \ - _EOR_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#define _EORS_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_EOR, rd, rn, rm, cond) -#define _EORS_REG_REG(rd, rn, rm) \ - _EORS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#endif - - -/* Rd := Rn EOR (Rm imm_shift) */ -#define ARM_EOR_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_EOR, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_EOR_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_EOR_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define ARM_EORS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_EOR, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_EORS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_EORS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _EOR_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_EOR, rd, rn, rm, shift_type, imm_shift, cond) -#define _EOR_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _EOR_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define _EORS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_EOR, rd, rn, rm, shift_type, imm_shift, cond) -#define _EORS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _EORS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - -/* Rd := Rn EOR (Rm Rs) */ -#define ARM_EOR_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_EOR, rd, rn, rm, shift_t, rs, cond) -#define ARM_EOR_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_EOR_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define ARM_EORS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_EOR, rd, rn, rm, shift_t, rs, cond) -#define ARM_EORS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_EORS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _EOR_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_EOR, rd, rn, rm, shift_t, rs, cond) -#define _EOR_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _EOR_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define _EORS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_EOR, rd, rn, rm, shift_t, rs, cond) -#define _EORS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _EORS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#endif - - -/* -- SUB -- */ - -/* Rd := Rn SUB (imm8 ROR rot) ; rot is power of 2 */ -#define ARM_SUB_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_SUB, rd, rn, imm8, rot, cond) -#define ARM_SUB_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_SUB_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) -#define ARM_SUBS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_SUB, rd, rn, imm8, rot, cond) -#define ARM_SUBS_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_SUBS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _SUB_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_SUB, rd, rn, imm8, rot, cond) -#define _SUB_REG_IMM(rd, rn, imm8, rot) \ - _SUB_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#define _SUBS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_SUB, rd, rn, imm8, rot, cond) -#define _SUBS_REG_IMM(rd, rn, imm8, rot) \ - _SUBS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#endif - - -/* Rd := Rn SUB imm8 */ -#define ARM_SUB_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_SUB_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_SUB_REG_IMM8(p, rd, rn, imm8) \ - ARM_SUB_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) -#define ARM_SUBS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_SUBS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_SUBS_REG_IMM8(p, rd, rn, imm8) \ - ARM_SUBS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _SUB_REG_IMM8_COND(rd, rn, imm8, cond) \ - _SUB_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _SUB_REG_IMM8(rd, rn, imm8) \ - _SUB_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#define _SUBS_REG_IMM8_COND(rd, rn, imm8, cond) \ - _SUBS_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _SUBS_REG_IMM8(rd, rn, imm8) \ - _SUBS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#endif - - -/* Rd := Rn SUB Rm */ -#define ARM_SUB_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_REG_REG_COND(p, ARMOP_SUB, rd, rn, rm, cond) -#define ARM_SUB_REG_REG(p, rd, rn, rm) \ - ARM_SUB_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) -#define ARM_SUBS_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_SUB, rd, rn, rm, cond) -#define ARM_SUBS_REG_REG(p, rd, rn, rm) \ - ARM_SUBS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _SUB_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_REG_REG_COND(ARMOP_SUB, rd, rn, rm, cond) -#define _SUB_REG_REG(rd, rn, rm) \ - _SUB_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#define _SUBS_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_SUB, rd, rn, rm, cond) -#define _SUBS_REG_REG(rd, rn, rm) \ - _SUBS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#endif - - -/* Rd := Rn SUB (Rm imm_shift) */ -#define ARM_SUB_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_SUB, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_SUB_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_SUB_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define ARM_SUBS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_SUB, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_SUBS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_SUBS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _SUB_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_SUB, rd, rn, rm, shift_type, imm_shift, cond) -#define _SUB_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _SUB_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define _SUBS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_SUB, rd, rn, rm, shift_type, imm_shift, cond) -#define _SUBS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _SUBS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - -/* Rd := Rn SUB (Rm Rs) */ -#define ARM_SUB_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_SUB, rd, rn, rm, shift_t, rs, cond) -#define ARM_SUB_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_SUB_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define ARM_SUBS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_SUB, rd, rn, rm, shift_t, rs, cond) -#define ARM_SUBS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_SUBS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _SUB_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_SUB, rd, rn, rm, shift_t, rs, cond) -#define _SUB_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _SUB_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define _SUBS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_SUB, rd, rn, rm, shift_t, rs, cond) -#define _SUBS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _SUBS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#endif - - -/* -- RSB -- */ - -/* Rd := Rn RSB (imm8 ROR rot) ; rot is power of 2 */ -#define ARM_RSB_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_RSB, rd, rn, imm8, rot, cond) -#define ARM_RSB_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_RSB_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) -#define ARM_RSBS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_RSB, rd, rn, imm8, rot, cond) -#define ARM_RSBS_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_RSBS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _RSB_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_RSB, rd, rn, imm8, rot, cond) -#define _RSB_REG_IMM(rd, rn, imm8, rot) \ - _RSB_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#define _RSBS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_RSB, rd, rn, imm8, rot, cond) -#define _RSBS_REG_IMM(rd, rn, imm8, rot) \ - _RSBS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#endif - - -/* Rd := Rn RSB imm8 */ -#define ARM_RSB_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_RSB_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_RSB_REG_IMM8(p, rd, rn, imm8) \ - ARM_RSB_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) -#define ARM_RSBS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_RSBS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_RSBS_REG_IMM8(p, rd, rn, imm8) \ - ARM_RSBS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _RSB_REG_IMM8_COND(rd, rn, imm8, cond) \ - _RSB_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _RSB_REG_IMM8(rd, rn, imm8) \ - _RSB_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#define _RSBS_REG_IMM8_COND(rd, rn, imm8, cond) \ - _RSBS_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _RSBS_REG_IMM8(rd, rn, imm8) \ - _RSBS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#endif - - -/* Rd := Rn RSB Rm */ -#define ARM_RSB_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_REG_REG_COND(p, ARMOP_RSB, rd, rn, rm, cond) -#define ARM_RSB_REG_REG(p, rd, rn, rm) \ - ARM_RSB_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) -#define ARM_RSBS_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_RSB, rd, rn, rm, cond) -#define ARM_RSBS_REG_REG(p, rd, rn, rm) \ - ARM_RSBS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _RSB_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_REG_REG_COND(ARMOP_RSB, rd, rn, rm, cond) -#define _RSB_REG_REG(rd, rn, rm) \ - _RSB_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#define _RSBS_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_RSB, rd, rn, rm, cond) -#define _RSBS_REG_REG(rd, rn, rm) \ - _RSBS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#endif - - -/* Rd := Rn RSB (Rm imm_shift) */ -#define ARM_RSB_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_RSB, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_RSB_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_RSB_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define ARM_RSBS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_RSB, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_RSBS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_RSBS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _RSB_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_RSB, rd, rn, rm, shift_type, imm_shift, cond) -#define _RSB_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _RSB_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define _RSBS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_RSB, rd, rn, rm, shift_type, imm_shift, cond) -#define _RSBS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _RSBS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - -/* Rd := Rn RSB (Rm Rs) */ -#define ARM_RSB_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_RSB, rd, rn, rm, shift_t, rs, cond) -#define ARM_RSB_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_RSB_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define ARM_RSBS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_RSB, rd, rn, rm, shift_t, rs, cond) -#define ARM_RSBS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_RSBS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _RSB_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_RSB, rd, rn, rm, shift_t, rs, cond) -#define _RSB_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _RSB_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define _RSBS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_RSB, rd, rn, rm, shift_t, rs, cond) -#define _RSBS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _RSBS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#endif - - -/* -- ADD -- */ - -/* Rd := Rn ADD (imm8 ROR rot) ; rot is power of 2 */ -#define ARM_ADD_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_ADD, rd, rn, imm8, rot, cond) -#define ARM_ADD_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_ADD_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) -#define ARM_ADDS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_ADD, rd, rn, imm8, rot, cond) -#define ARM_ADDS_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_ADDS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ADD_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_ADD, rd, rn, imm8, rot, cond) -#define _ADD_REG_IMM(rd, rn, imm8, rot) \ - _ADD_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#define _ADDS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_ADD, rd, rn, imm8, rot, cond) -#define _ADDS_REG_IMM(rd, rn, imm8, rot) \ - _ADDS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#endif - - -/* Rd := Rn ADD imm8 */ -#define ARM_ADD_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_ADD_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_ADD_REG_IMM8(p, rd, rn, imm8) \ - ARM_ADD_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) -#define ARM_ADDS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_ADDS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_ADDS_REG_IMM8(p, rd, rn, imm8) \ - ARM_ADDS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ADD_REG_IMM8_COND(rd, rn, imm8, cond) \ - _ADD_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _ADD_REG_IMM8(rd, rn, imm8) \ - _ADD_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#define _ADDS_REG_IMM8_COND(rd, rn, imm8, cond) \ - _ADDS_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _ADDS_REG_IMM8(rd, rn, imm8) \ - _ADDS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#endif - - -/* Rd := Rn ADD Rm */ -#define ARM_ADD_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_REG_REG_COND(p, ARMOP_ADD, rd, rn, rm, cond) -#define ARM_ADD_REG_REG(p, rd, rn, rm) \ - ARM_ADD_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) -#define ARM_ADDS_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_ADD, rd, rn, rm, cond) -#define ARM_ADDS_REG_REG(p, rd, rn, rm) \ - ARM_ADDS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ADD_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_REG_REG_COND(ARMOP_ADD, rd, rn, rm, cond) -#define _ADD_REG_REG(rd, rn, rm) \ - _ADD_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#define _ADDS_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_ADD, rd, rn, rm, cond) -#define _ADDS_REG_REG(rd, rn, rm) \ - _ADDS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#endif - - -/* Rd := Rn ADD (Rm imm_shift) */ -#define ARM_ADD_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_ADD, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_ADD_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_ADD_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define ARM_ADDS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_ADD, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_ADDS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_ADDS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ADD_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_ADD, rd, rn, rm, shift_type, imm_shift, cond) -#define _ADD_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _ADD_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define _ADDS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_ADD, rd, rn, rm, shift_type, imm_shift, cond) -#define _ADDS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _ADDS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - -/* Rd := Rn ADD (Rm Rs) */ -#define ARM_ADD_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_ADD, rd, rn, rm, shift_t, rs, cond) -#define ARM_ADD_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_ADD_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define ARM_ADDS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_ADD, rd, rn, rm, shift_t, rs, cond) -#define ARM_ADDS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_ADDS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ADD_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_ADD, rd, rn, rm, shift_t, rs, cond) -#define _ADD_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _ADD_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define _ADDS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_ADD, rd, rn, rm, shift_t, rs, cond) -#define _ADDS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _ADDS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#endif - - -/* -- ADC -- */ - -/* Rd := Rn ADC (imm8 ROR rot) ; rot is power of 2 */ -#define ARM_ADC_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_ADC, rd, rn, imm8, rot, cond) -#define ARM_ADC_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_ADC_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) -#define ARM_ADCS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_ADC, rd, rn, imm8, rot, cond) -#define ARM_ADCS_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_ADCS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ADC_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_ADC, rd, rn, imm8, rot, cond) -#define _ADC_REG_IMM(rd, rn, imm8, rot) \ - _ADC_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#define _ADCS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_ADC, rd, rn, imm8, rot, cond) -#define _ADCS_REG_IMM(rd, rn, imm8, rot) \ - _ADCS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#endif - - -/* Rd := Rn ADC imm8 */ -#define ARM_ADC_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_ADC_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_ADC_REG_IMM8(p, rd, rn, imm8) \ - ARM_ADC_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) -#define ARM_ADCS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_ADCS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_ADCS_REG_IMM8(p, rd, rn, imm8) \ - ARM_ADCS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ADC_REG_IMM8_COND(rd, rn, imm8, cond) \ - _ADC_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _ADC_REG_IMM8(rd, rn, imm8) \ - _ADC_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#define _ADCS_REG_IMM8_COND(rd, rn, imm8, cond) \ - _ADCS_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _ADCS_REG_IMM8(rd, rn, imm8) \ - _ADCS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#endif - - -/* Rd := Rn ADC Rm */ -#define ARM_ADC_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_REG_REG_COND(p, ARMOP_ADC, rd, rn, rm, cond) -#define ARM_ADC_REG_REG(p, rd, rn, rm) \ - ARM_ADC_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) -#define ARM_ADCS_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_ADC, rd, rn, rm, cond) -#define ARM_ADCS_REG_REG(p, rd, rn, rm) \ - ARM_ADCS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ADC_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_REG_REG_COND(ARMOP_ADC, rd, rn, rm, cond) -#define _ADC_REG_REG(rd, rn, rm) \ - _ADC_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#define _ADCS_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_ADC, rd, rn, rm, cond) -#define _ADCS_REG_REG(rd, rn, rm) \ - _ADCS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#endif - - -/* Rd := Rn ADC (Rm imm_shift) */ -#define ARM_ADC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_ADC, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_ADC_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_ADC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define ARM_ADCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_ADC, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_ADCS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_ADCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ADC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_ADC, rd, rn, rm, shift_type, imm_shift, cond) -#define _ADC_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _ADC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define _ADCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_ADC, rd, rn, rm, shift_type, imm_shift, cond) -#define _ADCS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _ADCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - -/* Rd := Rn ADC (Rm Rs) */ -#define ARM_ADC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_ADC, rd, rn, rm, shift_t, rs, cond) -#define ARM_ADC_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_ADC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define ARM_ADCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_ADC, rd, rn, rm, shift_t, rs, cond) -#define ARM_ADCS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_ADCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ADC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_ADC, rd, rn, rm, shift_t, rs, cond) -#define _ADC_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _ADC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define _ADCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_ADC, rd, rn, rm, shift_t, rs, cond) -#define _ADCS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _ADCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#endif - - -/* -- SBC -- */ - -/* Rd := Rn SBC (imm8 ROR rot) ; rot is power of 2 */ -#define ARM_SBC_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_SBC, rd, rn, imm8, rot, cond) -#define ARM_SBC_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_SBC_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) -#define ARM_SBCS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_SBC, rd, rn, imm8, rot, cond) -#define ARM_SBCS_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_SBCS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _SBC_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_SBC, rd, rn, imm8, rot, cond) -#define _SBC_REG_IMM(rd, rn, imm8, rot) \ - _SBC_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#define _SBCS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_SBC, rd, rn, imm8, rot, cond) -#define _SBCS_REG_IMM(rd, rn, imm8, rot) \ - _SBCS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#endif - - -/* Rd := Rn SBC imm8 */ -#define ARM_SBC_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_SBC_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_SBC_REG_IMM8(p, rd, rn, imm8) \ - ARM_SBC_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) -#define ARM_SBCS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_SBCS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_SBCS_REG_IMM8(p, rd, rn, imm8) \ - ARM_SBCS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _SBC_REG_IMM8_COND(rd, rn, imm8, cond) \ - _SBC_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _SBC_REG_IMM8(rd, rn, imm8) \ - _SBC_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#define _SBCS_REG_IMM8_COND(rd, rn, imm8, cond) \ - _SBCS_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _SBCS_REG_IMM8(rd, rn, imm8) \ - _SBCS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#endif - - -/* Rd := Rn SBC Rm */ -#define ARM_SBC_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_REG_REG_COND(p, ARMOP_SBC, rd, rn, rm, cond) -#define ARM_SBC_REG_REG(p, rd, rn, rm) \ - ARM_SBC_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) -#define ARM_SBCS_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_SBC, rd, rn, rm, cond) -#define ARM_SBCS_REG_REG(p, rd, rn, rm) \ - ARM_SBCS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _SBC_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_REG_REG_COND(ARMOP_SBC, rd, rn, rm, cond) -#define _SBC_REG_REG(rd, rn, rm) \ - _SBC_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#define _SBCS_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_SBC, rd, rn, rm, cond) -#define _SBCS_REG_REG(rd, rn, rm) \ - _SBCS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#endif - - -/* Rd := Rn SBC (Rm imm_shift) */ -#define ARM_SBC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_SBC, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_SBC_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_SBC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define ARM_SBCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_SBC, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_SBCS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_SBCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _SBC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_SBC, rd, rn, rm, shift_type, imm_shift, cond) -#define _SBC_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _SBC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define _SBCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_SBC, rd, rn, rm, shift_type, imm_shift, cond) -#define _SBCS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _SBCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - -/* Rd := Rn SBC (Rm Rs) */ -#define ARM_SBC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_SBC, rd, rn, rm, shift_t, rs, cond) -#define ARM_SBC_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_SBC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define ARM_SBCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_SBC, rd, rn, rm, shift_t, rs, cond) -#define ARM_SBCS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_SBCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _SBC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_SBC, rd, rn, rm, shift_t, rs, cond) -#define _SBC_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _SBC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define _SBCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_SBC, rd, rn, rm, shift_t, rs, cond) -#define _SBCS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _SBCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#endif - - -/* -- RSC -- */ - -/* Rd := Rn RSC (imm8 ROR rot) ; rot is power of 2 */ -#define ARM_RSC_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_RSC, rd, rn, imm8, rot, cond) -#define ARM_RSC_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_RSC_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) -#define ARM_RSCS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_RSC, rd, rn, imm8, rot, cond) -#define ARM_RSCS_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_RSCS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _RSC_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_RSC, rd, rn, imm8, rot, cond) -#define _RSC_REG_IMM(rd, rn, imm8, rot) \ - _RSC_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#define _RSCS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_RSC, rd, rn, imm8, rot, cond) -#define _RSCS_REG_IMM(rd, rn, imm8, rot) \ - _RSCS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#endif - - -/* Rd := Rn RSC imm8 */ -#define ARM_RSC_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_RSC_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_RSC_REG_IMM8(p, rd, rn, imm8) \ - ARM_RSC_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) -#define ARM_RSCS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_RSCS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_RSCS_REG_IMM8(p, rd, rn, imm8) \ - ARM_RSCS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _RSC_REG_IMM8_COND(rd, rn, imm8, cond) \ - _RSC_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _RSC_REG_IMM8(rd, rn, imm8) \ - _RSC_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#define _RSCS_REG_IMM8_COND(rd, rn, imm8, cond) \ - _RSCS_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _RSCS_REG_IMM8(rd, rn, imm8) \ - _RSCS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#endif - - -/* Rd := Rn RSC Rm */ -#define ARM_RSC_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_REG_REG_COND(p, ARMOP_RSC, rd, rn, rm, cond) -#define ARM_RSC_REG_REG(p, rd, rn, rm) \ - ARM_RSC_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) -#define ARM_RSCS_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_RSC, rd, rn, rm, cond) -#define ARM_RSCS_REG_REG(p, rd, rn, rm) \ - ARM_RSCS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _RSC_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_REG_REG_COND(ARMOP_RSC, rd, rn, rm, cond) -#define _RSC_REG_REG(rd, rn, rm) \ - _RSC_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#define _RSCS_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_RSC, rd, rn, rm, cond) -#define _RSCS_REG_REG(rd, rn, rm) \ - _RSCS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#endif - - -/* Rd := Rn RSC (Rm imm_shift) */ -#define ARM_RSC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_RSC, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_RSC_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_RSC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define ARM_RSCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_RSC, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_RSCS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_RSCS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _RSC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_RSC, rd, rn, rm, shift_type, imm_shift, cond) -#define _RSC_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _RSC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define _RSCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_RSC, rd, rn, rm, shift_type, imm_shift, cond) -#define _RSCS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _RSCS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - -/* Rd := Rn RSC (Rm Rs) */ -#define ARM_RSC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_RSC, rd, rn, rm, shift_t, rs, cond) -#define ARM_RSC_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_RSC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define ARM_RSCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_RSC, rd, rn, rm, shift_t, rs, cond) -#define ARM_RSCS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_RSCS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _RSC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_RSC, rd, rn, rm, shift_t, rs, cond) -#define _RSC_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _RSC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define _RSCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_RSC, rd, rn, rm, shift_t, rs, cond) -#define _RSCS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _RSCS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#endif - - -/* -- ORR -- */ - -/* Rd := Rn ORR (imm8 ROR rot) ; rot is power of 2 */ -#define ARM_ORR_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_ORR, rd, rn, imm8, rot, cond) -#define ARM_ORR_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_ORR_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) -#define ARM_ORRS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_ORR, rd, rn, imm8, rot, cond) -#define ARM_ORRS_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_ORRS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ORR_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_ORR, rd, rn, imm8, rot, cond) -#define _ORR_REG_IMM(rd, rn, imm8, rot) \ - _ORR_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#define _ORRS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_ORR, rd, rn, imm8, rot, cond) -#define _ORRS_REG_IMM(rd, rn, imm8, rot) \ - _ORRS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#endif - - -/* Rd := Rn ORR imm8 */ -#define ARM_ORR_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_ORR_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_ORR_REG_IMM8(p, rd, rn, imm8) \ - ARM_ORR_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) -#define ARM_ORRS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_ORRS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_ORRS_REG_IMM8(p, rd, rn, imm8) \ - ARM_ORRS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ORR_REG_IMM8_COND(rd, rn, imm8, cond) \ - _ORR_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _ORR_REG_IMM8(rd, rn, imm8) \ - _ORR_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#define _ORRS_REG_IMM8_COND(rd, rn, imm8, cond) \ - _ORRS_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _ORRS_REG_IMM8(rd, rn, imm8) \ - _ORRS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#endif - - -/* Rd := Rn ORR Rm */ -#define ARM_ORR_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_REG_REG_COND(p, ARMOP_ORR, rd, rn, rm, cond) -#define ARM_ORR_REG_REG(p, rd, rn, rm) \ - ARM_ORR_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) -#define ARM_ORRS_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_ORR, rd, rn, rm, cond) -#define ARM_ORRS_REG_REG(p, rd, rn, rm) \ - ARM_ORRS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ORR_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_REG_REG_COND(ARMOP_ORR, rd, rn, rm, cond) -#define _ORR_REG_REG(rd, rn, rm) \ - _ORR_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#define _ORRS_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_ORR, rd, rn, rm, cond) -#define _ORRS_REG_REG(rd, rn, rm) \ - _ORRS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#endif - - -/* Rd := Rn ORR (Rm imm_shift) */ -#define ARM_ORR_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_ORR, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_ORR_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_ORR_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define ARM_ORRS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_ORR, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_ORRS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_ORRS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ORR_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_ORR, rd, rn, rm, shift_type, imm_shift, cond) -#define _ORR_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _ORR_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define _ORRS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_ORR, rd, rn, rm, shift_type, imm_shift, cond) -#define _ORRS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _ORRS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - -/* Rd := Rn ORR (Rm Rs) */ -#define ARM_ORR_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_ORR, rd, rn, rm, shift_t, rs, cond) -#define ARM_ORR_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_ORR_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define ARM_ORRS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_ORR, rd, rn, rm, shift_t, rs, cond) -#define ARM_ORRS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_ORRS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _ORR_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_ORR, rd, rn, rm, shift_t, rs, cond) -#define _ORR_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _ORR_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define _ORRS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_ORR, rd, rn, rm, shift_t, rs, cond) -#define _ORRS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _ORRS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#endif - - -/* -- BIC -- */ - -/* Rd := Rn BIC (imm8 ROR rot) ; rot is power of 2 */ -#define ARM_BIC_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_BIC, rd, rn, imm8, rot, cond) -#define ARM_BIC_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_BIC_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) -#define ARM_BICS_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_BIC, rd, rn, imm8, rot, cond) -#define ARM_BICS_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_BICS_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _BIC_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_BIC, rd, rn, imm8, rot, cond) -#define _BIC_REG_IMM(rd, rn, imm8, rot) \ - _BIC_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#define _BICS_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_BIC, rd, rn, imm8, rot, cond) -#define _BICS_REG_IMM(rd, rn, imm8, rot) \ - _BICS_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#endif - - -/* Rd := Rn BIC imm8 */ -#define ARM_BIC_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_BIC_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_BIC_REG_IMM8(p, rd, rn, imm8) \ - ARM_BIC_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) -#define ARM_BICS_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_BICS_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_BICS_REG_IMM8(p, rd, rn, imm8) \ - ARM_BICS_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _BIC_REG_IMM8_COND(rd, rn, imm8, cond) \ - _BIC_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _BIC_REG_IMM8(rd, rn, imm8) \ - _BIC_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#define _BICS_REG_IMM8_COND(rd, rn, imm8, cond) \ - _BICS_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _BICS_REG_IMM8(rd, rn, imm8) \ - _BICS_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#endif - - -/* Rd := Rn BIC Rm */ -#define ARM_BIC_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_REG_REG_COND(p, ARMOP_BIC, rd, rn, rm, cond) -#define ARM_BIC_REG_REG(p, rd, rn, rm) \ - ARM_BIC_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) -#define ARM_BICS_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_BIC, rd, rn, rm, cond) -#define ARM_BICS_REG_REG(p, rd, rn, rm) \ - ARM_BICS_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _BIC_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_REG_REG_COND(ARMOP_BIC, rd, rn, rm, cond) -#define _BIC_REG_REG(rd, rn, rm) \ - _BIC_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#define _BICS_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_BIC, rd, rn, rm, cond) -#define _BICS_REG_REG(rd, rn, rm) \ - _BICS_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#endif - - -/* Rd := Rn BIC (Rm imm_shift) */ -#define ARM_BIC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_BIC, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_BIC_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_BIC_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define ARM_BICS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_BIC, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_BICS_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_BICS_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _BIC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_BIC, rd, rn, rm, shift_type, imm_shift, cond) -#define _BIC_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _BIC_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define _BICS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_BIC, rd, rn, rm, shift_type, imm_shift, cond) -#define _BICS_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _BICS_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - -/* Rd := Rn BIC (Rm Rs) */ -#define ARM_BIC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_BIC, rd, rn, rm, shift_t, rs, cond) -#define ARM_BIC_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_BIC_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define ARM_BICS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_BIC, rd, rn, rm, shift_t, rs, cond) -#define ARM_BICS_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_BICS_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _BIC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_BIC, rd, rn, rm, shift_t, rs, cond) -#define _BIC_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _BIC_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define _BICS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_BIC, rd, rn, rm, shift_t, rs, cond) -#define _BICS_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _BICS_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#endif - - - - - - -/* DPIs, comparison */ - -/* PSR := TST Rn, (imm8 ROR 2*rot) */ -#define ARM_TST_REG_IMM_COND(p, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_TST, 0, rn, imm8, rot, cond) -#define ARM_TST_REG_IMM(p, rn, imm8, rot) \ - ARM_TST_REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _TST_REG_IMM_COND(rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_TST, 0, rn, imm8, rot, cond) -#define _TST_REG_IMM(rn, imm8, rot) \ - _TST_REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) -#endif - - -/* PSR := TST Rn, imm8 */ -#define ARM_TST_REG_IMM8_COND(p, rn, imm8, cond) \ - ARM_TST_REG_IMM_COND(p, rn, imm8, 0, cond) -#define ARM_TST_REG_IMM8(p, rn, imm8) \ - ARM_TST_REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _TST_REG_IMM8_COND(rn, imm8, cond) \ - _TST_REG_IMM_COND(rn, imm8, 0, cond) -#define _TST_REG_IMM8(rn, imm8) \ - _TST_REG_IMM8_COND(rn, imm8, ARMCOND_AL) -#endif - - -/* PSR := TST Rn, Rm */ -#define ARM_TST_REG_REG_COND(p, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_TST, 0, rn, rm, cond) -#define ARM_TST_REG_REG(p, rn, rm) \ - ARM_TST_REG_REG_COND(p, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _TST_REG_REG_COND(rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_TST, 0, rn, rm, cond) -#define _TST_REG_REG(rn, rm) \ - _TST_REG_REG_COND(rn, rm, ARMCOND_AL) -#endif - - -/* PSR := TST Rn, (Rm imm8) */ -#define ARM_TST_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_TST, 0, rn, rm, shift_type, imm_shift, cond) -#define ARM_TST_REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ - ARM_TST_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _TST_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_TST, 0, rn, rm, shift_type, imm_shift, cond) -#define _TST_REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ - _TST_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - -/* PSR := TEQ Rn, (imm8 ROR 2*rot) */ -#define ARM_TEQ_REG_IMM_COND(p, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_TEQ, 0, rn, imm8, rot, cond) -#define ARM_TEQ_REG_IMM(p, rn, imm8, rot) \ - ARM_TEQ_REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _TEQ_REG_IMM_COND(rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_TEQ, 0, rn, imm8, rot, cond) -#define _TEQ_REG_IMM(rn, imm8, rot) \ - _TEQ_REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) -#endif - - -/* PSR := TEQ Rn, imm8 */ -#define ARM_TEQ_REG_IMM8_COND(p, rn, imm8, cond) \ - ARM_TEQ_REG_IMM_COND(p, rn, imm8, 0, cond) -#define ARM_TEQ_REG_IMM8(p, rn, imm8) \ - ARM_TEQ_REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _TEQ_REG_IMM8_COND(rn, imm8, cond) \ - _TEQ_REG_IMM_COND(rn, imm8, 0, cond) -#define _TEQ_REG_IMM8(rn, imm8) \ - _TEQ_REG_IMM8_COND(rn, imm8, ARMCOND_AL) -#endif - - -/* PSR := TEQ Rn, Rm */ -#define ARM_TEQ_REG_REG_COND(p, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_TEQ, 0, rn, rm, cond) -#define ARM_TEQ_REG_REG(p, rn, rm) \ - ARM_TEQ_REG_REG_COND(p, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _TEQ_REG_REG_COND(rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_TEQ, 0, rn, rm, cond) -#define _TEQ_REG_REG(rn, rm) \ - _TEQ_REG_REG_COND(rn, rm, ARMCOND_AL) -#endif - - -/* PSR := TEQ Rn, (Rm imm8) */ -#define ARM_TEQ_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_TEQ, 0, rn, rm, shift_type, imm_shift, cond) -#define ARM_TEQ_REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ - ARM_TEQ_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _TEQ_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_TEQ, 0, rn, rm, shift_type, imm_shift, cond) -#define _TEQ_REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ - _TEQ_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - -/* PSR := CMP Rn, (imm8 ROR 2*rot) */ -#define ARM_CMP_REG_IMM_COND(p, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_CMP, 0, rn, imm8, rot, cond) -#define ARM_CMP_REG_IMM(p, rn, imm8, rot) \ - ARM_CMP_REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _CMP_REG_IMM_COND(rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_CMP, 0, rn, imm8, rot, cond) -#define _CMP_REG_IMM(rn, imm8, rot) \ - _CMP_REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) -#endif - - -/* PSR := CMP Rn, imm8 */ -#define ARM_CMP_REG_IMM8_COND(p, rn, imm8, cond) \ - ARM_CMP_REG_IMM_COND(p, rn, imm8, 0, cond) -#define ARM_CMP_REG_IMM8(p, rn, imm8) \ - ARM_CMP_REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _CMP_REG_IMM8_COND(rn, imm8, cond) \ - _CMP_REG_IMM_COND(rn, imm8, 0, cond) -#define _CMP_REG_IMM8(rn, imm8) \ - _CMP_REG_IMM8_COND(rn, imm8, ARMCOND_AL) -#endif - - -/* PSR := CMP Rn, Rm */ -#define ARM_CMP_REG_REG_COND(p, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_CMP, 0, rn, rm, cond) -#define ARM_CMP_REG_REG(p, rn, rm) \ - ARM_CMP_REG_REG_COND(p, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _CMP_REG_REG_COND(rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_CMP, 0, rn, rm, cond) -#define _CMP_REG_REG(rn, rm) \ - _CMP_REG_REG_COND(rn, rm, ARMCOND_AL) -#endif - - -/* PSR := CMP Rn, (Rm imm8) */ -#define ARM_CMP_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_CMP, 0, rn, rm, shift_type, imm_shift, cond) -#define ARM_CMP_REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ - ARM_CMP_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _CMP_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_CMP, 0, rn, rm, shift_type, imm_shift, cond) -#define _CMP_REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ - _CMP_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - -/* PSR := CMN Rn, (imm8 ROR 2*rot) */ -#define ARM_CMN_REG_IMM_COND(p, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_CMN, 0, rn, imm8, rot, cond) -#define ARM_CMN_REG_IMM(p, rn, imm8, rot) \ - ARM_CMN_REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _CMN_REG_IMM_COND(rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_CMN, 0, rn, imm8, rot, cond) -#define _CMN_REG_IMM(rn, imm8, rot) \ - _CMN_REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) -#endif - - -/* PSR := CMN Rn, imm8 */ -#define ARM_CMN_REG_IMM8_COND(p, rn, imm8, cond) \ - ARM_CMN_REG_IMM_COND(p, rn, imm8, 0, cond) -#define ARM_CMN_REG_IMM8(p, rn, imm8) \ - ARM_CMN_REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _CMN_REG_IMM8_COND(rn, imm8, cond) \ - _CMN_REG_IMM_COND(rn, imm8, 0, cond) -#define _CMN_REG_IMM8(rn, imm8) \ - _CMN_REG_IMM8_COND(rn, imm8, ARMCOND_AL) -#endif - - -/* PSR := CMN Rn, Rm */ -#define ARM_CMN_REG_REG_COND(p, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_CMN, 0, rn, rm, cond) -#define ARM_CMN_REG_REG(p, rn, rm) \ - ARM_CMN_REG_REG_COND(p, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _CMN_REG_REG_COND(rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_CMN, 0, rn, rm, cond) -#define _CMN_REG_REG(rn, rm) \ - _CMN_REG_REG_COND(rn, rm, ARMCOND_AL) -#endif - - -/* PSR := CMN Rn, (Rm imm8) */ -#define ARM_CMN_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_CMN, 0, rn, rm, shift_type, imm_shift, cond) -#define ARM_CMN_REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ - ARM_CMN_REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define _CMN_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_CMN, 0, rn, rm, shift_type, imm_shift, cond) -#define _CMN_REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ - _CMN_REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - - -/* end generated */ - diff --git a/asm/cmp_macros.th b/asm/cmp_macros.th deleted file mode 100644 index cb2639dec1..0000000000 --- a/asm/cmp_macros.th +++ /dev/null @@ -1,56 +0,0 @@ -/* PSR := Rn, (imm8 ROR 2*rot) */ -#define ARM__REG_IMM_COND(p, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_, 0, rn, imm8, rot, cond) -#define ARM__REG_IMM(p, rn, imm8, rot) \ - ARM__REG_IMM_COND(p, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define __REG_IMM_COND(rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_, 0, rn, imm8, rot, cond) -#define __REG_IMM(rn, imm8, rot) \ - __REG_IMM_COND(rn, imm8, rot, ARMCOND_AL) -#endif - - -/* PSR := Rn, imm8 */ -#define ARM__REG_IMM8_COND(p, rn, imm8, cond) \ - ARM__REG_IMM_COND(p, rn, imm8, 0, cond) -#define ARM__REG_IMM8(p, rn, imm8) \ - ARM__REG_IMM8_COND(p, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define __REG_IMM8_COND(rn, imm8, cond) \ - __REG_IMM_COND(rn, imm8, 0, cond) -#define __REG_IMM8(rn, imm8) \ - __REG_IMM8_COND(rn, imm8, ARMCOND_AL) -#endif - - -/* PSR := Rn, Rm */ -#define ARM__REG_REG_COND(p, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_, 0, rn, rm, cond) -#define ARM__REG_REG(p, rn, rm) \ - ARM__REG_REG_COND(p, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define __REG_REG_COND(rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_, 0, rn, rm, cond) -#define __REG_REG(rn, rm) \ - __REG_REG_COND(rn, rm, ARMCOND_AL) -#endif - - -/* PSR := Rn, (Rm imm8) */ -#define ARM__REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_, 0, rn, rm, shift_type, imm_shift, cond) -#define ARM__REG_IMMSHIFT(p, rn, rm, shift_type, imm_shift) \ - ARM__REG_IMMSHIFT_COND(p, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define __REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_, 0, rn, rm, shift_type, imm_shift, cond) -#define __REG_IMMSHIFT(rn, rm, shift_type, imm_shift) \ - __REG_IMMSHIFT_COND(rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - diff --git a/asm/dpi_macros.th b/asm/dpi_macros.th deleted file mode 100644 index be43d1fe72..0000000000 --- a/asm/dpi_macros.th +++ /dev/null @@ -1,112 +0,0 @@ -/* -- -- */ - -/* Rd := Rn (imm8 ROR rot) ; rot is power of 2 */ -#define ARM__REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_, rd, rn, imm8, rot, cond) -#define ARM__REG_IMM(p, rd, rn, imm8, rot) \ - ARM__REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) -#define ARM_S_REG_IMM_COND(p, rd, rn, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_, rd, rn, imm8, rot, cond) -#define ARM_S_REG_IMM(p, rd, rn, imm8, rot) \ - ARM_S_REG_IMM_COND(p, rd, rn, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define __REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_, rd, rn, imm8, rot, cond) -#define __REG_IMM(rd, rn, imm8, rot) \ - __REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#define _S_REG_IMM_COND(rd, rn, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_, rd, rn, imm8, rot, cond) -#define _S_REG_IMM(rd, rn, imm8, rot) \ - _S_REG_IMM_COND(rd, rn, imm8, rot, ARMCOND_AL) -#endif - - -/* Rd := Rn imm8 */ -#define ARM__REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM__REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM__REG_IMM8(p, rd, rn, imm8) \ - ARM__REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) -#define ARM_S_REG_IMM8_COND(p, rd, rn, imm8, cond) \ - ARM_S_REG_IMM_COND(p, rd, rn, imm8, 0, cond) -#define ARM_S_REG_IMM8(p, rd, rn, imm8) \ - ARM_S_REG_IMM8_COND(p, rd, rn, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define __REG_IMM8_COND(rd, rn, imm8, cond) \ - __REG_IMM_COND(rd, rn, imm8, 0, cond) -#define __REG_IMM8(rd, rn, imm8) \ - __REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#define _S_REG_IMM8_COND(rd, rn, imm8, cond) \ - _S_REG_IMM_COND(rd, rn, imm8, 0, cond) -#define _S_REG_IMM8(rd, rn, imm8) \ - _S_REG_IMM8_COND(rd, rn, imm8, ARMCOND_AL) -#endif - - -/* Rd := Rn Rm */ -#define ARM__REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_REG_REG_COND(p, ARMOP_, rd, rn, rm, cond) -#define ARM__REG_REG(p, rd, rn, rm) \ - ARM__REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) -#define ARM_S_REG_REG_COND(p, rd, rn, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_, rd, rn, rm, cond) -#define ARM_S_REG_REG(p, rd, rn, rm) \ - ARM_S_REG_REG_COND(p, rd, rn, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define __REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_REG_REG_COND(ARMOP_, rd, rn, rm, cond) -#define __REG_REG(rd, rn, rm) \ - __REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#define _S_REG_REG_COND(rd, rn, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_, rd, rn, rm, cond) -#define _S_REG_REG(rd, rn, rm) \ - _S_REG_REG_COND(rd, rn, rm, ARMCOND_AL) -#endif - - -/* Rd := Rn (Rm imm_shift) */ -#define ARM__REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM__REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM__REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define ARM_S_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_, rd, rn, rm, shift_type, imm_shift, cond) -#define ARM_S_REG_IMMSHIFT(p, rd, rn, rm, shift_type, imm_shift) \ - ARM_S_REG_IMMSHIFT_COND(p, rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define __REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_, rd, rn, rm, shift_type, imm_shift, cond) -#define __REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - __REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#define _S_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_, rd, rn, rm, shift_type, imm_shift, cond) -#define _S_REG_IMMSHIFT(rd, rn, rm, shift_type, imm_shift) \ - _S_REG_IMMSHIFT_COND(rd, rn, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - -/* Rd := Rn (Rm Rs) */ -#define ARM__REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_, rd, rn, rm, shift_t, rs, cond) -#define ARM__REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM__REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define ARM_S_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, cond) \ - ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_, rd, rn, rm, shift_t, rs, cond) -#define ARM_S_REG_REGSHIFT(p, rd, rn, rm, shift_type, rs) \ - ARM_S_REG_REGSHIFT_COND(p, rd, rn, rm, shift_type, rs, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define __REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_, rd, rn, rm, shift_t, rs, cond) -#define __REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - __REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#define _S_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_, rd, rn, rm, shift_t, rs, cond) -#define _S_REG_REGSHIFT(rd, rn, rm, shift_type, rs) \ - _S_REG_REGSHIFT_COND(rd, rn, rm, shift_type, rs, ARMCOND_AL) -#endif - - diff --git a/asm/dpiops.sh b/asm/dpiops.sh deleted file mode 100755 index d3b93ff52a..0000000000 --- a/asm/dpiops.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh - -OPCODES="AND EOR SUB RSB ADD ADC SBC RSC ORR BIC" -CMP_OPCODES="TST TEQ CMP CMN" -MOV_OPCODES="MOV MVN" - -# $1: opcode list -# $2: template -gen() { - for i in $1; do - sed "s//$i/g" $2.th - done -} - - - -echo -e "/* Macros for DPI ops, auto-generated from template */\n" - -echo -e "\n/* mov/mvn */\n" -gen "$MOV_OPCODES" mov_macros - -echo -e "\n/* DPIs, arithmetic and logical */\n" -gen "$OPCODES" dpi_macros - -echo -e "\n\n" - -echo -e "\n/* DPIs, comparison */\n" -gen "$CMP_OPCODES" cmp_macros - -echo -e "\n/* end generated */\n" diff --git a/asm/fpa_macros.th b/asm/fpa_macros.th deleted file mode 100644 index 036b2a00b9..0000000000 --- a/asm/fpa_macros.th +++ /dev/null @@ -1,15 +0,0 @@ -/* -- -- */ - - -/* Fd := Rn Rm */ -#define ARM_FPA_D_COND(p, rd, rn, rm, cond) \ - ARM_EMIT((p), ARM_DEF_FPA_CPDO_DYADIC(cond,ARM_FPA_,rd,rn,rm,ARM_FPA_ROUND_NEAREST,ARM_FPA_ROUND_DOUBLE)) -#define ARM_FPA_D(p, rd, rn, rm) \ - ARM_FPA_D_COND(p, rd, rn, rm, ARMCOND_AL) - -#define ARM_FPA_S_COND(p, rd, rn, rm, cond) \ - ARM_EMIT((p), ARM_DEF_FPA_CPDO_DYADIC(cond,ARM_FPA_,rd,rn,rm,ARM_FPA_ROUND_NEAREST,ARM_FPA_ROUND_SINGLE)) -#define ARM_FPA_S(p, rd, rn, rm) \ - ARM_FPA_S_COND(p, rd, rn, rm, ARMCOND_AL) - - diff --git a/asm/fpam_macros.th b/asm/fpam_macros.th deleted file mode 100644 index 15183c393e..0000000000 --- a/asm/fpam_macros.th +++ /dev/null @@ -1,14 +0,0 @@ -/* -- -- */ - - -/* Fd := Rm */ - -#define ARM_FPA_D_COND(p,dreg,sreg,cond) \ - ARM_EMIT((p), ARM_DEF_FPA_CPDO_MONADIC((cond),ARM_FPA_,(dreg),(sreg),ARM_FPA_ROUND_NEAREST,ARM_FPA_ROUND_DOUBLE)) -#define ARM_FPA_D(p,dreg,sreg) ARM_FPA_D_COND(p,dreg,sreg,ARMCOND_AL) - -#define ARM_FPA_S_COND(p,dreg,sreg,cond) \ - ARM_EMIT((p), ARM_DEF_FPA_CPDO_MONADIC((cond),ARM_FPA_,(dreg),(sreg),ARM_FPA_ROUND_NEAREST,ARM_FPA_ROUND_SINGLE)) -#define ARM_FPA_S(p,dreg,sreg) ARM_FPA_S_COND(p,dreg,sreg,ARMCOND_AL) - - diff --git a/asm/fpaops.sh b/asm/fpaops.sh deleted file mode 100755 index be1987683a..0000000000 --- a/asm/fpaops.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh - -DYADIC="ADF MUF SUF RSF DVF RDF POW RPW RMF FML FDV FRD POL" -MONADIC="MVF MNF ABS RND SQT LOG EXP SIN COS TAN ASN ACS ATN URD NRM" - -# $1: opcode list -# $2: template -gen() { - for i in $1; do - sed "s//$i/g" $2.th - done -} - -echo -e "/* Macros for FPA ops, auto-generated from template */\n" - -echo -e "\n/* dyadic */\n" -gen "$DYADIC" fpa_macros - -echo -e "\n/* monadic */\n" -gen "$MONADIC" fpam_macros - -echo -e "\n\n" - -echo -e "\n/* end generated */\n" diff --git a/asm/mov_macros.th b/asm/mov_macros.th deleted file mode 100644 index 6bac29003a..0000000000 --- a/asm/mov_macros.th +++ /dev/null @@ -1,121 +0,0 @@ -/* Rd := imm8 ROR rot */ -#define ARM__REG_IMM_COND(p, reg, imm8, rot, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_, reg, 0, imm8, rot, cond) -#define ARM__REG_IMM(p, reg, imm8, rot) \ - ARM__REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) -/* S */ -#define ARM_S_REG_IMM_COND(p, reg, imm8, rot, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_, reg, 0, imm8, rot, cond) -#define ARM_S_REG_IMM(p, reg, imm8, rot) \ - ARM_S_REG_IMM_COND(p, reg, imm8, rot, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define __REG_IMM_COND(reg, imm8, rot, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_, reg, 0, imm8, rot, cond) -#define __REG_IMM(reg, imm8, rot) \ - __REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) -/* S */ -#define _S_REG_IMM_COND(reg, imm8, rot, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_, reg, 0, imm8, rot, cond) -#define _S_REG_IMM(reg, imm8, rot) \ - _S_REG_IMM_COND(reg, imm8, rot, ARMCOND_AL) -#endif - - -/* Rd := imm8 */ -#define ARM__REG_IMM8_COND(p, reg, imm8, cond) \ - ARM_DPIOP_REG_IMM8ROT_COND(p, ARMOP_, reg, 0, imm8, 0, cond) -#define ARM__REG_IMM8(p, reg, imm8) \ - ARM__REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) -/* S */ -#define ARM_S_REG_IMM8_COND(p, reg, imm8, cond) \ - ARM_DPIOP_S_REG_IMM8ROT_COND(p, ARMOP_, reg, 0, imm8, 0, cond) -#define ARM_S_REG_IMM8(p, reg, imm8) \ - ARM_S_REG_IMM8_COND(p, reg, imm8, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define __REG_IMM8_COND(reg, imm8, cond) \ - ARM_IASM_DPIOP_REG_IMM8ROT_COND(ARMOP_, reg, 0, imm8, 0, cond) -#define __REG_IMM8(reg, imm8) \ - __REG_IMM8_COND(reg, imm8, ARMCOND_AL) -/* S */ -#define _S_REG_IMM8_COND(reg, imm8, cond) \ - ARM_IASM_DPIOP_S_REG_IMM8ROT_COND(ARMOP_, reg, 0, imm8, 0, cond) -#define _S_REG_IMM8(reg, imm8) \ - _S_REG_IMM8_COND(reg, imm8, ARMCOND_AL) -#endif - - -/* Rd := Rm */ -#define ARM__REG_REG_COND(p, rd, rm, cond) \ - ARM_DPIOP_REG_REG_COND(p, ARMOP_, rd, 0, rm, cond) -#define ARM__REG_REG(p, rd, rm) \ - ARM__REG_REG_COND(p, rd, rm, ARMCOND_AL) -/* S */ -#define ARM_S_REG_REG_COND(p, rd, rm, cond) \ - ARM_DPIOP_S_REG_REG_COND(p, ARMOP_, rd, 0, rm, cond) -#define ARM_S_REG_REG(p, rd, rm) \ - ARM_S_REG_REG_COND(p, rd, rm, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define __REG_REG_COND(rd, rm, cond) \ - ARM_IASM_DPIOP_REG_REG_COND(ARMOP_, rd, 0, rm, cond) -#define __REG_REG(rd, rm) \ - __REG_REG_COND(rd, rm, ARMCOND_AL) -/* S */ -#define _S_REG_REG_COND(rd, rm, cond) \ - ARM_IASM_DPIOP_S_REG_REG_COND(ARMOP_, rd, 0, rm, cond) -#define _S_REG_REG(rd, rm) \ - _S_REG_REG_COND(rd, rm, ARMCOND_AL) -#endif - - -/* Rd := Rm imm_shift */ -#define ARM__REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_REG_IMMSHIFT_COND(p, ARMOP_, rd, 0, rm, shift_type, imm_shift, cond) -#define ARM__REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ - ARM__REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) -/* S */ -#define ARM_S_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, cond) \ - ARM_DPIOP_S_REG_IMMSHIFT_COND(p, ARMOP_, rd, 0, rm, shift_type, imm_shift, cond) -#define ARM_S_REG_IMMSHIFT(p, rd, rm, shift_type, imm_shift) \ - ARM_S_REG_IMMSHIFT_COND(p, rd, rm, shift_type, imm_shift, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define __REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_REG_IMMSHIFT_COND(ARMOP_, rd, 0, rm, shift_type, imm_shift, cond) -#define __REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ - __REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) -/* S */ -#define _S_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, cond) \ - ARM_IASM_DPIOP_S_REG_IMMSHIFT_COND(ARMOP_, rd, 0, rm, shift_type, imm_shift, cond) -#define _S_REG_IMMSHIFT(rd, rm, shift_type, imm_shift) \ - _S_REG_IMMSHIFT_COND(rd, rm, shift_type, imm_shift, ARMCOND_AL) -#endif - - - -/* Rd := (Rm Rs) */ -#define ARM__REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ - ARM_DPIOP_REG_REGSHIFT_COND(p, ARMOP_, rd, 0, rm, shift_type, rs, cond) -#define ARM__REG_REGSHIFT(p, rd, rm, shift_type, rs) \ - ARM__REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) -/* S */ -#define ARM_S_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, cond) \ - ARM_DPIOP_S_REG_REGSHIFT_COND(p, ARMOP_, rd, 0, rm, shift_type, rs, cond) -#define ARM_S_REG_REGSHIFT(p, rd, rm, shift_type, rs) \ - ARM_S_REG_REGSHIFT_COND(p, rd, rm, shift_type, rs, ARMCOND_AL) - -#ifndef ARM_NOIASM -#define __REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_REG_REGSHIFT_COND(ARMOP_, rd, 0, rm, shift_type, rs, cond) -#define __REG_REGSHIFT(rd, rm, shift_type, rs) \ - __REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) -/* S */ -#define _S_REG_REGSHIFT_COND(rd, rm, shift_type, rs, cond) \ - ARM_IASM_DPIOP_S_REG_REGSHIFT_COND(ARMOP_, rd, 0, rm, shift_type, rs, cond) -#define _S_REG_REGSHIFT(rd, rm, shift_type, rs) \ - _S_REG_REGSHIFT_COND(rd, rm, shift_type, rs, ARMCOND_AL) -#endif - - diff --git a/asm/vfp_macros.th b/asm/vfp_macros.th deleted file mode 100644 index cca67dc8f2..0000000000 --- a/asm/vfp_macros.th +++ /dev/null @@ -1,15 +0,0 @@ -/* -- -- */ - - -/* Fd := Fn Fm */ -#define ARM_VFP_D_COND(p, rd, rn, rm, cond) \ - ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_DOUBLE,ARM_VFP_,rd,rn,rm)) -#define ARM_VFP_D(p, rd, rn, rm) \ - ARM_VFP_D_COND(p, rd, rn, rm, ARMCOND_AL) - -#define ARM_VFP_S_COND(p, rd, rn, rm, cond) \ - ARM_EMIT((p), ARM_DEF_VFP_DYADIC(cond,ARM_VFP_COPROC_SINGLE,ARM_VFP_,rd,rn,rm)) -#define ARM_VFP_S(p, rd, rn, rm) \ - ARM_VFP_S_COND(p, rd, rn, rm, ARMCOND_AL) - - diff --git a/asm/vfpm_macros.th b/asm/vfpm_macros.th deleted file mode 100644 index 25ad721581..0000000000 --- a/asm/vfpm_macros.th +++ /dev/null @@ -1,14 +0,0 @@ -/* -- -- */ - - -/* Fd := Fm */ - -#define ARM_D_COND(p,dreg,sreg,cond) \ - ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_DOUBLE,ARM_VFP_,(dreg),(sreg))) -#define ARM_D(p,dreg,sreg) ARM_D_COND(p,dreg,sreg,ARMCOND_AL) - -#define ARM_S_COND(p,dreg,sreg,cond) \ - ARM_EMIT((p), ARM_DEF_VFP_MONADIC((cond),ARM_VFP_COPROC_SINGLE,ARM_VFP_,(dreg),(sreg))) -#define ARM_S(p,dreg,sreg) ARM_S_COND(p,dreg,sreg,ARMCOND_AL) - - diff --git a/asm/vfpops.sh b/asm/vfpops.sh deleted file mode 100755 index bed4a9c80e..0000000000 --- a/asm/vfpops.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh - -DYADIC="ADD SUB MUL NMUL DIV" -MONADIC="CPY ABS NEG SQRT CMP CMPE CMPZ CMPEZ CVT UITO SITO TOUI TOSI TOUIZ TOSIZ" - -# $1: opcode list -# $2: template -gen() { - for i in $1; do - sed "s//$i/g" $2.th - done -} - -echo -e "/* Macros for VFP ops, auto-generated from template */\n" - -echo -e "\n/* dyadic */\n" -gen "$DYADIC" vfp_macros - -echo -e "\n/* monadic */\n" -gen "$MONADIC" vfpm_macros - -echo -e "\n\n" - -echo -e "\n/* end generated */\n" diff --git a/asm/x86-codegen.h b/asm/x86-codegen.h deleted file mode 100644 index fd2c528c86..0000000000 --- a/asm/x86-codegen.h +++ /dev/null @@ -1,2640 +0,0 @@ -/* - * x86-codegen.h: Macros for generating x86 code - * - * Authors: - * Paolo Molaro (lupus@ximian.com) - * Intel Corporation (ORP Project) - * Sergey Chaban (serge@wildwestsoftware.com) - * Dietmar Maurer (dietmar@ximian.com) - * Patrik Torstensson - * - * Copyright (C) 2000 Intel Corporation. All rights reserved. - * Copyright (C) 2001, 2002 Ximian, Inc. - */ - -#ifndef X86_H -#define X86_H -#include - -#ifdef __native_client_codegen__ -extern gint8 nacl_align_byte; -#endif /* __native_client_codegen__ */ - - -#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) -#define x86_codegen_pre(inst_ptr_ptr, inst_len) do { mono_nacl_align_inst(inst_ptr_ptr, inst_len); } while (0) -#define x86_call_sequence_pre_val(inst) guint8* _code_start = (inst); -#define x86_call_sequence_post_val(inst) \ - (mono_nacl_align_call(&_code_start, &(inst)), _code_start); -#define x86_call_sequence_pre(inst) x86_call_sequence_pre_val((inst)) -#define x86_call_sequence_post(inst) x86_call_sequence_post_val((inst)) -#else -#define x86_codegen_pre(inst_ptr_ptr, inst_len) do {} while (0) -/* Two variants are needed to avoid warnings */ -#define x86_call_sequence_pre_val(inst) guint8* _code_start = (inst); -#define x86_call_sequence_post_val(inst) _code_start -#define x86_call_sequence_pre(inst) -#define x86_call_sequence_post(inst) -#endif /* __native_client_codegen__ */ - - -/* -// x86 register numbers -*/ -typedef enum { - X86_EAX = 0, - X86_ECX = 1, - X86_EDX = 2, - X86_EBX = 3, - X86_ESP = 4, - X86_EBP = 5, - X86_ESI = 6, - X86_EDI = 7, - X86_NREG -} X86_Reg_No; - -typedef enum { - X86_XMM0, - X86_XMM1, - X86_XMM2, - X86_XMM3, - X86_XMM4, - X86_XMM5, - X86_XMM6, - X86_XMM7, - X86_XMM_NREG -} X86_XMM_Reg_No; - -/* -// opcodes for alu instructions -*/ -typedef enum { - X86_ADD = 0, - X86_OR = 1, - X86_ADC = 2, - X86_SBB = 3, - X86_AND = 4, - X86_SUB = 5, - X86_XOR = 6, - X86_CMP = 7, - X86_NALU -} X86_ALU_Opcode; -/* -// opcodes for shift instructions -*/ -typedef enum { - X86_SHLD, - X86_SHLR, - X86_ROL = 0, - X86_ROR = 1, - X86_RCL = 2, - X86_RCR = 3, - X86_SHL = 4, - X86_SHR = 5, - X86_SAR = 7, - X86_NSHIFT = 8 -} X86_Shift_Opcode; -/* -// opcodes for floating-point instructions -*/ -typedef enum { - X86_FADD = 0, - X86_FMUL = 1, - X86_FCOM = 2, - X86_FCOMP = 3, - X86_FSUB = 4, - X86_FSUBR = 5, - X86_FDIV = 6, - X86_FDIVR = 7, - X86_NFP = 8 -} X86_FP_Opcode; -/* -// integer conditions codes -*/ -typedef enum { - X86_CC_EQ = 0, X86_CC_E = 0, X86_CC_Z = 0, - X86_CC_NE = 1, X86_CC_NZ = 1, - X86_CC_LT = 2, X86_CC_B = 2, X86_CC_C = 2, X86_CC_NAE = 2, - X86_CC_LE = 3, X86_CC_BE = 3, X86_CC_NA = 3, - X86_CC_GT = 4, X86_CC_A = 4, X86_CC_NBE = 4, - X86_CC_GE = 5, X86_CC_AE = 5, X86_CC_NB = 5, X86_CC_NC = 5, - X86_CC_LZ = 6, X86_CC_S = 6, - X86_CC_GEZ = 7, X86_CC_NS = 7, - X86_CC_P = 8, X86_CC_PE = 8, - X86_CC_NP = 9, X86_CC_PO = 9, - X86_CC_O = 10, - X86_CC_NO = 11, - X86_NCC -} X86_CC; - -/* FP status */ -enum { - X86_FP_C0 = 0x100, - X86_FP_C1 = 0x200, - X86_FP_C2 = 0x400, - X86_FP_C3 = 0x4000, - X86_FP_CC_MASK = 0x4500 -}; - -/* FP control word */ -enum { - X86_FPCW_INVOPEX_MASK = 0x1, - X86_FPCW_DENOPEX_MASK = 0x2, - X86_FPCW_ZERODIV_MASK = 0x4, - X86_FPCW_OVFEX_MASK = 0x8, - X86_FPCW_UNDFEX_MASK = 0x10, - X86_FPCW_PRECEX_MASK = 0x20, - X86_FPCW_PRECC_MASK = 0x300, - X86_FPCW_ROUNDC_MASK = 0xc00, - - /* values for precision control */ - X86_FPCW_PREC_SINGLE = 0, - X86_FPCW_PREC_DOUBLE = 0x200, - X86_FPCW_PREC_EXTENDED = 0x300, - - /* values for rounding control */ - X86_FPCW_ROUND_NEAREST = 0, - X86_FPCW_ROUND_DOWN = 0x400, - X86_FPCW_ROUND_UP = 0x800, - X86_FPCW_ROUND_TOZERO = 0xc00 -}; - -/* -// prefix code -*/ -typedef enum { - X86_LOCK_PREFIX = 0xF0, - X86_REPNZ_PREFIX = 0xF2, - X86_REPZ_PREFIX = 0xF3, - X86_REP_PREFIX = 0xF3, - X86_CS_PREFIX = 0x2E, - X86_SS_PREFIX = 0x36, - X86_DS_PREFIX = 0x3E, - X86_ES_PREFIX = 0x26, - X86_FS_PREFIX = 0x64, - X86_GS_PREFIX = 0x65, - X86_UNLIKELY_PREFIX = 0x2E, - X86_LIKELY_PREFIX = 0x3E, - X86_OPERAND_PREFIX = 0x66, - X86_ADDRESS_PREFIX = 0x67 -} X86_Prefix; - -static const unsigned char -x86_cc_unsigned_map [X86_NCC] = { - 0x74, /* eq */ - 0x75, /* ne */ - 0x72, /* lt */ - 0x76, /* le */ - 0x77, /* gt */ - 0x73, /* ge */ - 0x78, /* lz */ - 0x79, /* gez */ - 0x7a, /* p */ - 0x7b, /* np */ - 0x70, /* o */ - 0x71, /* no */ -}; - -static const unsigned char -x86_cc_signed_map [X86_NCC] = { - 0x74, /* eq */ - 0x75, /* ne */ - 0x7c, /* lt */ - 0x7e, /* le */ - 0x7f, /* gt */ - 0x7d, /* ge */ - 0x78, /* lz */ - 0x79, /* gez */ - 0x7a, /* p */ - 0x7b, /* np */ - 0x70, /* o */ - 0x71, /* no */ -}; - -typedef union { - int val; - unsigned char b [4]; -} x86_imm_buf; - -#define X86_NOBASEREG (-1) - -/* -// bitvector mask for callee-saved registers -*/ -#define X86_ESI_MASK (1<> 6) -#define x86_modrm_reg(modrm) (((modrm) >> 3) & 0x7) -#define x86_modrm_rm(modrm) ((modrm) & 0x7) - -#define x86_address_byte(inst,m,o,r) do { *(inst)++ = ((((m)&0x03)<<6)|(((o)&0x07)<<3)|(((r)&0x07))); } while (0) -#define x86_imm_emit32(inst,imm) \ - do { \ - x86_imm_buf imb; imb.val = (int) (imm); \ - *(inst)++ = imb.b [0]; \ - *(inst)++ = imb.b [1]; \ - *(inst)++ = imb.b [2]; \ - *(inst)++ = imb.b [3]; \ - } while (0) -#define x86_imm_emit16(inst,imm) do { *(short*)(inst) = (imm); (inst) += 2; } while (0) -#define x86_imm_emit8(inst,imm) do { *(inst) = (unsigned char)((imm) & 0xff); ++(inst); } while (0) -#define x86_is_imm8(imm) (((int)(imm) >= -128 && (int)(imm) <= 127)) -#define x86_is_imm16(imm) (((int)(imm) >= -(1<<16) && (int)(imm) <= ((1<<16)-1))) - -#define x86_reg_emit(inst,r,regno) do { x86_address_byte ((inst), 3, (r), (regno)); } while (0) -#define x86_reg8_emit(inst,r,regno,is_rh,is_rnoh) do {x86_address_byte ((inst), 3, (is_rh)?((r)|4):(r), (is_rnoh)?((regno)|4):(regno));} while (0) -#define x86_regp_emit(inst,r,regno) do { x86_address_byte ((inst), 0, (r), (regno)); } while (0) -#define x86_mem_emit(inst,r,disp) do { x86_address_byte ((inst), 0, (r), 5); x86_imm_emit32((inst), (disp)); } while (0) - -#define kMaxMembaseEmitPadding 6 - -#define x86_membase_emit_body(inst,r,basereg,disp) do {\ - if ((basereg) == X86_ESP) { \ - if ((disp) == 0) { \ - x86_address_byte ((inst), 0, (r), X86_ESP); \ - x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ - } else if (x86_is_imm8((disp))) { \ - x86_address_byte ((inst), 1, (r), X86_ESP); \ - x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ - x86_imm_emit8 ((inst), (disp)); \ - } else { \ - x86_address_byte ((inst), 2, (r), X86_ESP); \ - x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ - x86_imm_emit32 ((inst), (disp)); \ - } \ - break; \ - } \ - if ((disp) == 0 && (basereg) != X86_EBP) { \ - x86_address_byte ((inst), 0, (r), (basereg)); \ - break; \ - } \ - if (x86_is_imm8((disp))) { \ - x86_address_byte ((inst), 1, (r), (basereg)); \ - x86_imm_emit8 ((inst), (disp)); \ - } else { \ - x86_address_byte ((inst), 2, (r), (basereg)); \ - x86_imm_emit32 ((inst), (disp)); \ - } \ - } while (0) - -#if defined(__native_client_codegen__) && defined(TARGET_AMD64) -#define x86_membase_emit(inst,r,basereg,disp) \ - do { \ - amd64_nacl_membase_handler(&(inst), (basereg), (disp), (r)) ; \ - } while (0) -#else /* __default_codegen__ || 32-bit NaCl codegen */ -#define x86_membase_emit(inst,r,basereg,disp) \ - do { \ - x86_membase_emit_body((inst),(r),(basereg),(disp)); \ - } while (0) -#endif - -#define kMaxMemindexEmitPadding 6 - -#define x86_memindex_emit(inst,r,basereg,disp,indexreg,shift) \ - do { \ - if ((basereg) == X86_NOBASEREG) { \ - x86_address_byte ((inst), 0, (r), 4); \ - x86_address_byte ((inst), (shift), (indexreg), 5); \ - x86_imm_emit32 ((inst), (disp)); \ - } else if ((disp) == 0 && (basereg) != X86_EBP) { \ - x86_address_byte ((inst), 0, (r), 4); \ - x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ - } else if (x86_is_imm8((disp))) { \ - x86_address_byte ((inst), 1, (r), 4); \ - x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ - x86_imm_emit8 ((inst), (disp)); \ - } else { \ - x86_address_byte ((inst), 2, (r), 4); \ - x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ - x86_imm_emit32 ((inst), (disp)); \ - } \ - } while (0) - -/* - * target is the position in the code where to jump to: - * target = code; - * .. output loop code... - * x86_mov_reg_imm (code, X86_EAX, 0); - * loop = code; - * x86_loop (code, -1); - * ... finish method - * - * patch displacement - * x86_patch (loop, target); - * - * ins should point at the start of the instruction that encodes a target. - * the instruction is inspected for validity and the correct displacement - * is inserted. - */ -#define x86_do_patch(ins,target) \ - do { \ - unsigned char* pos = (ins) + 1; \ - int disp, size = 0; \ - switch (*(unsigned char*)(ins)) { \ - case 0xe8: case 0xe9: ++size; break; /* call, jump32 */ \ - case 0x0f: if (!(*pos >= 0x70 && *pos <= 0x8f)) assert (0); \ - ++size; ++pos; break; /* prefix for 32-bit disp */ \ - case 0xe0: case 0xe1: case 0xe2: /* loop */ \ - case 0xeb: /* jump8 */ \ - /* conditional jump opcodes */ \ - case 0x70: case 0x71: case 0x72: case 0x73: \ - case 0x74: case 0x75: case 0x76: case 0x77: \ - case 0x78: case 0x79: case 0x7a: case 0x7b: \ - case 0x7c: case 0x7d: case 0x7e: case 0x7f: \ - break; \ - default: assert (0); \ - } \ - disp = (target) - pos; \ - if (size) x86_imm_emit32 (pos, disp - 4); \ - else if (x86_is_imm8 (disp - 1)) x86_imm_emit8 (pos, disp - 1); \ - else assert (0); \ - } while (0) - -#if defined( __native_client_codegen__ ) && defined(TARGET_X86) - -#define x86_skip_nops(inst) \ - do { \ - int in_nop = 0; \ - do { \ - in_nop = 0; \ - if (inst[0] == 0x90) { \ - in_nop = 1; \ - inst += 1; \ - } \ - if (inst[0] == 0x8b && inst[1] == 0xc0) { \ - in_nop = 1; \ - inst += 2; \ - } \ - if (inst[0] == 0x8d && inst[1] == 0x6d \ - && inst[2] == 0x00) { \ - in_nop = 1; \ - inst += 3; \ - } \ - if (inst[0] == 0x8d && inst[1] == 0x64 \ - && inst[2] == 0x24 && inst[3] == 0x00) { \ - in_nop = 1; \ - inst += 4; \ - } \ - /* skip inst+=5 case because it's the 4-byte + 1-byte case */ \ - if (inst[0] == 0x8d && inst[1] == 0xad \ - && inst[2] == 0x00 && inst[3] == 0x00 \ - && inst[4] == 0x00 && inst[5] == 0x00) { \ - in_nop = 1; \ - inst += 6; \ - } \ - if (inst[0] == 0x8d && inst[1] == 0xa4 \ - && inst[2] == 0x24 && inst[3] == 0x00 \ - && inst[4] == 0x00 && inst[5] == 0x00 \ - && inst[6] == 0x00 ) { \ - in_nop = 1; \ - inst += 7; \ - } \ - } while ( in_nop ); \ - } while (0) - -#if defined(__native_client__) -#define x86_patch(ins,target) \ - do { \ - unsigned char* inst = (ins); \ - guint8* new_target = nacl_modify_patch_target((target)); \ - x86_skip_nops((inst)); \ - x86_do_patch((inst), new_target); \ - } while (0) -#else /* __native_client__ */ -#define x86_patch(ins,target) \ - do { \ - unsigned char* inst = (ins); \ - guint8* new_target = (target); \ - x86_skip_nops((inst)); \ - x86_do_patch((inst), new_target); \ - } while (0) -#endif /* __native_client__ */ - -#else -#define x86_patch(ins,target) do { x86_do_patch((ins), (target)); } while (0) -#endif /* __native_client_codegen__ */ - -#ifdef __native_client_codegen__ -/* The breakpoint instruction is illegal in Native Client, although the HALT */ -/* instruction is allowed. The breakpoint is used several places in mini-x86.c */ -/* and exceptions-x86.c. */ -#define x86_breakpoint(inst) \ - do { \ - *(inst)++ = 0xf4; \ - } while (0) -#else -#define x86_breakpoint(inst) \ - do { \ - *(inst)++ = 0xcc; \ - } while (0) -#endif - -#define x86_cld(inst) do { *(inst)++ =(unsigned char)0xfc; } while (0) -#define x86_stosb(inst) do { *(inst)++ =(unsigned char)0xaa; } while (0) -#define x86_stosl(inst) do { *(inst)++ =(unsigned char)0xab; } while (0) -#define x86_stosd(inst) x86_stosl((inst)) -#define x86_movsb(inst) do { *(inst)++ =(unsigned char)0xa4; } while (0) -#define x86_movsl(inst) do { *(inst)++ =(unsigned char)0xa5; } while (0) -#define x86_movsd(inst) x86_movsl((inst)) - -#if defined(__default_codegen__) -#define x86_prefix(inst,p) \ - do { \ - *(inst)++ =(unsigned char) (p); \ - } while (0) -#elif defined(__native_client_codegen__) -#if defined(TARGET_X86) -/* kNaClAlignment - 1 is the max value we can pass into x86_codegen_pre. */ -/* This keeps us from having to call x86_codegen_pre with specific */ -/* knowledge of the size of the instruction that follows it, and */ -/* localizes the alignment requirement to this spot. */ -#define x86_prefix(inst,p) \ - do { \ - x86_codegen_pre(&(inst), kNaClAlignment - 1); \ - *(inst)++ =(unsigned char) (p); \ - } while (0) -#elif defined(TARGET_AMD64) -/* We need to tag any prefixes so we can perform proper membase sandboxing */ -/* See: mini-amd64.c:amd64_nacl_membase_handler for verbose details */ -#define x86_prefix(inst,p) \ - do { \ - amd64_nacl_tag_legacy_prefix((inst)); \ - *(inst)++ =(unsigned char) (p); \ - } while (0) - -#endif /* TARGET_AMD64 */ - -#endif /* __native_client_codegen__ */ - -#define x86_rdtsc(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = 0x0f; \ - *(inst)++ = 0x31; \ - } while (0) - -#define x86_cmpxchg_reg_reg(inst,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xb1; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - } while (0) - -#define x86_cmpxchg_mem_reg(inst,mem,reg) \ - do { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xb1; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_cmpxchg_membase_reg(inst,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xb1; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_xchg_reg_reg(inst,dreg,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - if ((size) == 1) \ - *(inst)++ = (unsigned char)0x86; \ - else \ - *(inst)++ = (unsigned char)0x87; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - } while (0) - -#define x86_xchg_mem_reg(inst,mem,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - if ((size) == 1) \ - *(inst)++ = (unsigned char)0x86; \ - else \ - *(inst)++ = (unsigned char)0x87; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_xchg_membase_reg(inst,basereg,disp,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - if ((size) == 1) \ - *(inst)++ = (unsigned char)0x86; \ - else \ - *(inst)++ = (unsigned char)0x87; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_xadd_reg_reg(inst,dreg,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0F; \ - if ((size) == 1) \ - *(inst)++ = (unsigned char)0xC0; \ - else \ - *(inst)++ = (unsigned char)0xC1; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - } while (0) - -#define x86_xadd_mem_reg(inst,mem,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0x0F; \ - if ((size) == 1) \ - *(inst)++ = (unsigned char)0xC0; \ - else \ - *(inst)++ = (unsigned char)0xC1; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_xadd_membase_reg(inst,basereg,disp,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0F; \ - if ((size) == 1) \ - *(inst)++ = (unsigned char)0xC0; \ - else \ - *(inst)++ = (unsigned char)0xC1; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_inc_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xff; \ - x86_mem_emit ((inst), 0, (mem)); \ - } while (0) - -#define x86_inc_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xff; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - } while (0) - -#define x86_inc_reg(inst,reg) do { *(inst)++ = (unsigned char)0x40 + (reg); } while (0) - -#define x86_dec_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xff; \ - x86_mem_emit ((inst), 1, (mem)); \ - } while (0) - -#define x86_dec_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xff; \ - x86_membase_emit ((inst), 1, (basereg), (disp)); \ - } while (0) - -#define x86_dec_reg(inst,reg) do { *(inst)++ = (unsigned char)0x48 + (reg); } while (0) - -#define x86_not_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_mem_emit ((inst), 2, (mem)); \ - } while (0) - -#define x86_not_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_membase_emit ((inst), 2, (basereg), (disp)); \ - } while (0) - -#define x86_not_reg(inst,reg) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_reg_emit ((inst), 2, (reg)); \ - } while (0) - -#define x86_neg_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_mem_emit ((inst), 3, (mem)); \ - } while (0) - -#define x86_neg_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_membase_emit ((inst), 3, (basereg), (disp)); \ - } while (0) - -#define x86_neg_reg(inst,reg) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_reg_emit ((inst), 3, (reg)); \ - } while (0) - -#define x86_nop(inst) do { *(inst)++ = (unsigned char)0x90; } while (0) - -#define x86_alu_reg_imm(inst,opc,reg,imm) \ - do { \ - if ((reg) == X86_EAX) { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ - x86_imm_emit32 ((inst), (imm)); \ - break; \ - } \ - if (x86_is_imm8((imm))) { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x83; \ - x86_reg_emit ((inst), (opc), (reg)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0x81; \ - x86_reg_emit ((inst), (opc), (reg)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_alu_mem_imm(inst,opc,mem,imm) \ - do { \ - if (x86_is_imm8((imm))) { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0x83; \ - x86_mem_emit ((inst), (opc), (mem)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 10); \ - *(inst)++ = (unsigned char)0x81; \ - x86_mem_emit ((inst), (opc), (mem)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_alu_membase_imm(inst,opc,basereg,disp,imm) \ - do { \ - if (x86_is_imm8((imm))) { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x83; \ - x86_membase_emit ((inst), (opc), (basereg), (disp)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x81; \ - x86_membase_emit ((inst), (opc), (basereg), (disp)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_alu_membase8_imm(inst,opc,basereg,disp,imm) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x80; \ - x86_membase_emit ((inst), (opc), (basereg), (disp)); \ - x86_imm_emit8 ((inst), (imm)); \ - } while (0) - -#define x86_alu_mem_reg(inst,opc,mem,reg) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_alu_membase_reg(inst,opc,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_alu_reg_reg(inst,opc,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -/** - * @x86_alu_reg8_reg8: - * Supports ALU operations between two 8-bit registers. - * dreg := dreg opc reg - * X86_Reg_No enum is used to specify the registers. - * Additionally is_*_h flags are used to specify what part - * of a given 32-bit register is used - high (TRUE) or low (FALSE). - * For example: dreg = X86_EAX, is_dreg_h = TRUE -> use AH - */ -#define x86_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 2; \ - x86_reg8_emit ((inst), (dreg), (reg), (is_dreg_h), (is_reg_h)); \ - } while (0) - -#define x86_alu_reg_mem(inst,opc,reg,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_alu_reg_membase(inst,opc,reg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_test_reg_imm(inst,reg,imm) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - if ((reg) == X86_EAX) { \ - *(inst)++ = (unsigned char)0xa9; \ - } else { \ - *(inst)++ = (unsigned char)0xf7; \ - x86_reg_emit ((inst), 0, (reg)); \ - } \ - x86_imm_emit32 ((inst), (imm)); \ - } while (0) - -#define x86_test_mem_imm(inst,mem,imm) \ - do { \ - x86_codegen_pre(&(inst), 10); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_mem_emit ((inst), 0, (mem)); \ - x86_imm_emit32 ((inst), (imm)); \ - } while (0) - -#define x86_test_membase_imm(inst,basereg,disp,imm) \ - do { \ - x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - x86_imm_emit32 ((inst), (imm)); \ - } while (0) - -#define x86_test_reg_reg(inst,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0x85; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - } while (0) - -#define x86_test_mem_reg(inst,mem,reg) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0x85; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_test_membase_reg(inst,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x85; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_shift_reg_imm(inst,opc,reg,imm) \ - do { \ - if ((imm) == 1) { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd1; \ - x86_reg_emit ((inst), (opc), (reg)); \ - } else { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0xc1; \ - x86_reg_emit ((inst), (opc), (reg)); \ - x86_imm_emit8 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_shift_mem_imm(inst,opc,mem,imm) \ - do { \ - if ((imm) == 1) { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xd1; \ - x86_mem_emit ((inst), (opc), (mem)); \ - } else { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0xc1; \ - x86_mem_emit ((inst), (opc), (mem)); \ - x86_imm_emit8 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_shift_membase_imm(inst,opc,basereg,disp,imm) \ - do { \ - if ((imm) == 1) { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xd1; \ - x86_membase_emit ((inst), (opc), (basereg), (disp)); \ - } else { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xc1; \ - x86_membase_emit ((inst), (opc), (basereg), (disp)); \ - x86_imm_emit8 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_shift_reg(inst,opc,reg) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd3; \ - x86_reg_emit ((inst), (opc), (reg)); \ - } while (0) - -#define x86_shift_mem(inst,opc,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xd3; \ - x86_mem_emit ((inst), (opc), (mem)); \ - } while (0) - -#define x86_shift_membase(inst,opc,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xd3; \ - x86_membase_emit ((inst), (opc), (basereg), (disp)); \ - } while (0) - -/* - * Multi op shift missing. - */ - -#define x86_shrd_reg(inst,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xad; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - } while (0) - -#define x86_shrd_reg_imm(inst,dreg,reg,shamt) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xac; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - x86_imm_emit8 ((inst), (shamt)); \ - } while (0) - -#define x86_shld_reg(inst,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xa5; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - } while (0) - -#define x86_shld_reg_imm(inst,dreg,reg,shamt) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xa4; \ - x86_reg_emit ((inst), (reg), (dreg)); \ - x86_imm_emit8 ((inst), (shamt)); \ - } while (0) - -/* - * EDX:EAX = EAX * rm - */ -#define x86_mul_reg(inst,reg,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_reg_emit ((inst), 4 + ((is_signed) ? 1 : 0), (reg)); \ - } while (0) - -#define x86_mul_mem(inst,mem,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_mem_emit ((inst), 4 + ((is_signed) ? 1 : 0), (mem)); \ - } while (0) - -#define x86_mul_membase(inst,basereg,disp,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_membase_emit ((inst), 4 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ - } while (0) - -/* - * r *= rm - */ -#define x86_imul_reg_reg(inst,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xaf; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#define x86_imul_reg_mem(inst,reg,mem) \ - do { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xaf; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_imul_reg_membase(inst,reg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0xaf; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -/* - * dreg = rm * imm - */ -#define x86_imul_reg_reg_imm(inst,dreg,reg,imm) \ - do { \ - if (x86_is_imm8 ((imm))) { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x6b; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0x69; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_imul_reg_mem_imm(inst,reg,mem,imm) \ - do { \ - if (x86_is_imm8 ((imm))) { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0x6b; \ - x86_mem_emit ((inst), (reg), (mem)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0x69; \ - x86_reg_emit ((inst), (reg), (mem)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_imul_reg_membase_imm(inst,reg,basereg,disp,imm) \ - do { \ - if (x86_is_imm8 ((imm))) { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x6b; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x69; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -/* - * divide EDX:EAX by rm; - * eax = quotient, edx = remainder - */ - -#define x86_div_reg(inst,reg,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_reg_emit ((inst), 6 + ((is_signed) ? 1 : 0), (reg)); \ - } while (0) - -#define x86_div_mem(inst,mem,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_mem_emit ((inst), 6 + ((is_signed) ? 1 : 0), (mem)); \ - } while (0) - -#define x86_div_membase(inst,basereg,disp,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xf7; \ - x86_membase_emit ((inst), 6 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ - } while (0) - -#define x86_mov_mem_reg(inst,mem,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 7); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x88; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x89; break; \ - default: assert (0); \ - } \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_mov_regp_reg(inst,regp,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x88; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x89; break; \ - default: assert (0); \ - } \ - x86_regp_emit ((inst), (reg), (regp)); \ - } while (0) - -#define x86_mov_membase_reg(inst,basereg,disp,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x88; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x89; break; \ - default: assert (0); \ - } \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x88; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x89; break; \ - default: assert (0); \ - } \ - x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ - } while (0) - -#define x86_mov_reg_reg(inst,dreg,reg,size) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x8a; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#define x86_mov_reg_mem(inst,reg,mem,size) \ - do { \ - x86_codegen_pre(&(inst), 7); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x8a; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define kMovRegMembasePadding (2 + kMaxMembaseEmitPadding) - -#define x86_mov_reg_membase(inst,reg,basereg,disp,size) \ - do { \ - x86_codegen_pre(&(inst), kMovRegMembasePadding); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x8a; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ - switch ((size)) { \ - case 1: *(inst)++ = (unsigned char)0x8a; break; \ - case 2: x86_prefix((inst), X86_OPERAND_PREFIX); /* fall through */ \ - case 4: *(inst)++ = (unsigned char)0x8b; break; \ - default: assert (0); \ - } \ - x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ - } while (0) - -/* - * Note: x86_clear_reg () chacnges the condition code! - */ -#define x86_clear_reg(inst,reg) x86_alu_reg_reg((inst), X86_XOR, (reg), (reg)) - -#define x86_mov_reg_imm(inst,reg,imm) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0xb8 + (reg); \ - x86_imm_emit32 ((inst), (imm)); \ - } while (0) - -#define x86_mov_mem_imm(inst,mem,imm,size) \ - do { \ - if ((size) == 1) { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0xc6; \ - x86_mem_emit ((inst), 0, (mem)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else if ((size) == 2) { \ - x86_codegen_pre(&(inst), 9); \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - *(inst)++ = (unsigned char)0xc7; \ - x86_mem_emit ((inst), 0, (mem)); \ - x86_imm_emit16 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 10); \ - *(inst)++ = (unsigned char)0xc7; \ - x86_mem_emit ((inst), 0, (mem)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_mov_membase_imm(inst,basereg,disp,imm,size) \ - do { \ - if ((size) == 1) { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xc6; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else if ((size) == 2) { \ - x86_codegen_pre(&(inst), 4 + kMaxMembaseEmitPadding); \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - *(inst)++ = (unsigned char)0xc7; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - x86_imm_emit16 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 5 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xc7; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) \ - do { \ - if ((size) == 1) { \ - x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ - *(inst)++ = (unsigned char)0xc6; \ - x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ - x86_imm_emit8 ((inst), (imm)); \ - } else if ((size) == 2) { \ - x86_codegen_pre(&(inst), 4 + kMaxMemindexEmitPadding); \ - x86_prefix((inst), X86_OPERAND_PREFIX); \ - *(inst)++ = (unsigned char)0xc7; \ - x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ - x86_imm_emit16 ((inst), (imm)); \ - } else { \ - x86_codegen_pre(&(inst), 5 + kMaxMemindexEmitPadding); \ - *(inst)++ = (unsigned char)0xc7; \ - x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ - x86_imm_emit32 ((inst), (imm)); \ - } \ - } while (0) - -#define x86_lea_mem(inst,reg,mem) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0x8d; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_lea_membase(inst,reg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x8d; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_lea_memindex(inst,reg,basereg,disp,indexreg,shift) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMemindexEmitPadding); \ - *(inst)++ = (unsigned char)0x8d; \ - x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ - } while (0) - -#define x86_widen_reg(inst,dreg,reg,is_signed,is_half) \ - do { \ - unsigned char op = 0xb6; \ - g_assert (is_half || X86_IS_BYTE_REG (reg)); \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) op += 0x08; \ - if ((is_half)) op += 0x01; \ - *(inst)++ = op; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#define x86_widen_mem(inst,dreg,mem,is_signed,is_half) \ - do { \ - unsigned char op = 0xb6; \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) op += 0x08; \ - if ((is_half)) op += 0x01; \ - *(inst)++ = op; \ - x86_mem_emit ((inst), (dreg), (mem)); \ - } while (0) - -#define x86_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) \ - do { \ - unsigned char op = 0xb6; \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) op += 0x08; \ - if ((is_half)) op += 0x01; \ - *(inst)++ = op; \ - x86_membase_emit ((inst), (dreg), (basereg), (disp)); \ - } while (0) - -#define x86_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) \ - do { \ - unsigned char op = 0xb6; \ - x86_codegen_pre(&(inst), 2 + kMaxMemindexEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) op += 0x08; \ - if ((is_half)) op += 0x01; \ - *(inst)++ = op; \ - x86_memindex_emit ((inst), (dreg), (basereg), (disp), (indexreg), (shift)); \ - } while (0) - -#define x86_cdq(inst) do { *(inst)++ = (unsigned char)0x99; } while (0) -#define x86_wait(inst) do { *(inst)++ = (unsigned char)0x9b; } while (0) - -#define x86_fp_op_mem(inst,opc,mem,is_double) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ - x86_mem_emit ((inst), (opc), (mem)); \ - } while (0) - -#define x86_fp_op_membase(inst,opc,basereg,disp,is_double) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ - x86_membase_emit ((inst), (opc), (basereg), (disp)); \ - } while (0) - -#define x86_fp_op(inst,opc,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd8; \ - *(inst)++ = (unsigned char)0xc0+((opc)<<3)+((index)&0x07); \ - } while (0) - -#define x86_fp_op_reg(inst,opc,index,pop_stack) \ - do { \ - static const unsigned char map[] = { 0, 1, 2, 3, 5, 4, 7, 6, 8}; \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (pop_stack) ? (unsigned char)0xde : (unsigned char)0xdc; \ - *(inst)++ = (unsigned char)0xc0+(map[(opc)]<<3)+((index)&0x07); \ - } while (0) - -/** - * @x86_fp_int_op_membase - * Supports FPU operations between ST(0) and integer operand in memory. - * Operation encoded using X86_FP_Opcode enum. - * Operand is addressed by [basereg + disp]. - * is_int specifies whether operand is int32 (TRUE) or int16 (FALSE). - */ -#define x86_fp_int_op_membase(inst,opc,basereg,disp,is_int) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (is_int) ? (unsigned char)0xda : (unsigned char)0xde; \ - x86_membase_emit ((inst), opc, (basereg), (disp)); \ - } while (0) - -#define x86_fstp(inst,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xdd; \ - *(inst)++ = (unsigned char)0xd8+(index); \ - } while (0) - -#define x86_fcompp(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xde; \ - *(inst)++ = (unsigned char)0xd9; \ - } while (0) - -#define x86_fucompp(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xda; \ - *(inst)++ = (unsigned char)0xe9; \ - } while (0) - -#define x86_fnstsw(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xdf; \ - *(inst)++ = (unsigned char)0xe0; \ - } while (0) - -#define x86_fnstcw(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xd9; \ - x86_mem_emit ((inst), 7, (mem)); \ - } while (0) - -#define x86_fnstcw_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xd9; \ - x86_membase_emit ((inst), 7, (basereg), (disp)); \ - } while (0) - -#define x86_fldcw(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xd9; \ - x86_mem_emit ((inst), 5, (mem)); \ - } while (0) - -#define x86_fldcw_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xd9; \ - x86_membase_emit ((inst), 5, (basereg), (disp)); \ - } while (0) - -#define x86_fchs(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd9; \ - *(inst)++ = (unsigned char)0xe0; \ - } while (0) - -#define x86_frem(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd9; \ - *(inst)++ = (unsigned char)0xf8; \ - } while (0) - -#define x86_fxch(inst,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd9; \ - *(inst)++ = (unsigned char)0xc8 + ((index) & 0x07); \ - } while (0) - -#define x86_fcomi(inst,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xdb; \ - *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ - } while (0) - -#define x86_fcomip(inst,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xdf; \ - *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ - } while (0) - -#define x86_fucomi(inst,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xdb; \ - *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ - } while (0) - -#define x86_fucomip(inst,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xdf; \ - *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ - } while (0) - -#define x86_fld(inst,mem,is_double) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ - x86_mem_emit ((inst), 0, (mem)); \ - } while (0) - -#define x86_fld_membase(inst,basereg,disp,is_double) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - } while (0) - -#define x86_fld80_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xdb; \ - x86_mem_emit ((inst), 5, (mem)); \ - } while (0) - -#define x86_fld80_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xdb; \ - x86_membase_emit ((inst), 5, (basereg), (disp)); \ - } while (0) - -#define x86_fild(inst,mem,is_long) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - if ((is_long)) { \ - *(inst)++ = (unsigned char)0xdf; \ - x86_mem_emit ((inst), 5, (mem)); \ - } else { \ - *(inst)++ = (unsigned char)0xdb; \ - x86_mem_emit ((inst), 0, (mem)); \ - } \ - } while (0) - -#define x86_fild_membase(inst,basereg,disp,is_long) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - if ((is_long)) { \ - *(inst)++ = (unsigned char)0xdf; \ - x86_membase_emit ((inst), 5, (basereg), (disp)); \ - } else { \ - *(inst)++ = (unsigned char)0xdb; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - } \ - } while (0) - -#define x86_fld_reg(inst,index) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd9; \ - *(inst)++ = (unsigned char)0xc0 + ((index) & 0x07); \ - } while (0) - -#define x86_fldz(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd9; \ - *(inst)++ = (unsigned char)0xee; \ - } while (0) - -#define x86_fld1(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd9; \ - *(inst)++ = (unsigned char)0xe8; \ - } while (0) - -#define x86_fldpi(inst) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xd9; \ - *(inst)++ = (unsigned char)0xeb; \ - } while (0) - -#define x86_fst(inst,mem,is_double,pop_stack) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ - x86_mem_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (mem)); \ - } while (0) - -#define x86_fst_membase(inst,basereg,disp,is_double,pop_stack) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ - x86_membase_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (basereg), (disp)); \ - } while (0) - -#define x86_fst80_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xdb; \ - x86_mem_emit ((inst), 7, (mem)); \ - } while (0) - - -#define x86_fst80_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xdb; \ - x86_membase_emit ((inst), 7, (basereg), (disp)); \ - } while (0) - - -#define x86_fist_pop(inst,mem,is_long) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - if ((is_long)) { \ - *(inst)++ = (unsigned char)0xdf; \ - x86_mem_emit ((inst), 7, (mem)); \ - } else { \ - *(inst)++ = (unsigned char)0xdb; \ - x86_mem_emit ((inst), 3, (mem)); \ - } \ - } while (0) - -#define x86_fist_pop_membase(inst,basereg,disp,is_long) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - if ((is_long)) { \ - *(inst)++ = (unsigned char)0xdf; \ - x86_membase_emit ((inst), 7, (basereg), (disp)); \ - } else { \ - *(inst)++ = (unsigned char)0xdb; \ - x86_membase_emit ((inst), 3, (basereg), (disp)); \ - } \ - } while (0) - -#define x86_fstsw(inst) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x9b; \ - *(inst)++ = (unsigned char)0xdf; \ - *(inst)++ = (unsigned char)0xe0; \ - } while (0) - -/** - * @x86_fist_membase - * Converts content of ST(0) to integer and stores it at memory location - * addressed by [basereg + disp]. - * is_int specifies whether destination is int32 (TRUE) or int16 (FALSE). - */ -#define x86_fist_membase(inst,basereg,disp,is_int) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - if ((is_int)) { \ - *(inst)++ = (unsigned char)0xdb; \ - x86_membase_emit ((inst), 2, (basereg), (disp)); \ - } else { \ - *(inst)++ = (unsigned char)0xdf; \ - x86_membase_emit ((inst), 2, (basereg), (disp)); \ - } \ - } while (0) - - -#define x86_push_reg(inst,reg) \ - do { \ - *(inst)++ = (unsigned char)0x50 + (reg); \ - } while (0) - -#define x86_push_regp(inst,reg) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xff; \ - x86_regp_emit ((inst), 6, (reg)); \ - } while (0) - -#define x86_push_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0xff; \ - x86_mem_emit ((inst), 6, (mem)); \ - } while (0) - -#define x86_push_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xff; \ - x86_membase_emit ((inst), 6, (basereg), (disp)); \ - } while (0) - -#define x86_push_memindex(inst,basereg,disp,indexreg,shift) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMemindexEmitPadding); \ - *(inst)++ = (unsigned char)0xff; \ - x86_memindex_emit ((inst), 6, (basereg), (disp), (indexreg), (shift)); \ - } while (0) - -#define x86_push_imm_template(inst) x86_push_imm (inst, 0xf0f0f0f0) - -#define x86_push_imm(inst,imm) \ - do { \ - int _imm = (int) (imm); \ - if (x86_is_imm8 (_imm)) { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0x6A; \ - x86_imm_emit8 ((inst), (_imm)); \ - } else { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0x68; \ - x86_imm_emit32 ((inst), (_imm)); \ - } \ - } while (0) - -#define x86_pop_reg(inst,reg) \ - do { \ - *(inst)++ = (unsigned char)0x58 + (reg); \ - } while (0) - -#define x86_pop_mem(inst,mem) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0x87; \ - x86_mem_emit ((inst), 0, (mem)); \ - } while (0) - -#define x86_pop_membase(inst,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 1 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x87; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - } while (0) - -#define x86_pushad(inst) do { *(inst)++ = (unsigned char)0x60; } while (0) -#define x86_pushfd(inst) do { *(inst)++ = (unsigned char)0x9c; } while (0) -#define x86_popad(inst) do { *(inst)++ = (unsigned char)0x61; } while (0) -#define x86_popfd(inst) do { *(inst)++ = (unsigned char)0x9d; } while (0) - -#define x86_loop(inst,imm) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xe2; \ - x86_imm_emit8 ((inst), (imm)); \ - } while (0) - -#define x86_loope(inst,imm) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xe1; \ - x86_imm_emit8 ((inst), (imm)); \ - } while (0) - -#define x86_loopne(inst,imm) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xe0; \ - x86_imm_emit8 ((inst), (imm)); \ - } while (0) - -#if defined(TARGET_X86) -#define x86_jump32(inst,imm) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0xe9; \ - x86_imm_emit32 ((inst), (imm)); \ - } while (0) - -#define x86_jump8(inst,imm) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - *(inst)++ = (unsigned char)0xeb; \ - x86_imm_emit8 ((inst), (imm)); \ - } while (0) -#elif defined(TARGET_AMD64) -/* These macros are used directly from mini-amd64.c and other */ -/* amd64 specific files, so they need to be instrumented directly. */ -#define x86_jump32(inst,imm) \ - do { \ - amd64_codegen_pre(inst); \ - *(inst)++ = (unsigned char)0xe9; \ - x86_imm_emit32 ((inst), (imm)); \ - amd64_codegen_post(inst); \ - } while (0) - -#define x86_jump8(inst,imm) \ - do { \ - amd64_codegen_pre(inst); \ - *(inst)++ = (unsigned char)0xeb; \ - x86_imm_emit8 ((inst), (imm)); \ - amd64_codegen_post(inst); \ - } while (0) -#endif - -#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) -#define x86_jump_reg(inst,reg) do { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0x83; /* and */ \ - x86_reg_emit ((inst), 4, (reg)); /* reg */ \ - *(inst)++ = (unsigned char)nacl_align_byte; \ - *(inst)++ = (unsigned char)0xff; \ - x86_reg_emit ((inst), 4, (reg)); \ - } while (0) - -/* Let's hope ECX is available for these... */ -#define x86_jump_mem(inst,mem) do { \ - x86_mov_reg_mem(inst, (X86_ECX), (mem), 4); \ - x86_jump_reg(inst, (X86_ECX)); \ - } while (0) - -#define x86_jump_membase(inst,basereg,disp) do { \ - x86_mov_reg_membase(inst, (X86_ECX), basereg, disp, 4); \ - x86_jump_reg(inst, (X86_ECX)); \ - } while (0) - -/* like x86_jump_membase, but force a 32-bit displacement */ -#define x86_jump_membase32(inst,basereg,disp) do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0x8b; \ - x86_address_byte ((inst), 2, X86_ECX, (basereg)); \ - x86_imm_emit32 ((inst), (disp)); \ - x86_jump_reg(inst, (X86_ECX)); \ - } while (0) -#else /* __native_client_codegen__ */ -#define x86_jump_reg(inst,reg) \ - do { \ - *(inst)++ = (unsigned char)0xff; \ - x86_reg_emit ((inst), 4, (reg)); \ - } while (0) - -#define x86_jump_mem(inst,mem) \ - do { \ - *(inst)++ = (unsigned char)0xff; \ - x86_mem_emit ((inst), 4, (mem)); \ - } while (0) - -#define x86_jump_membase(inst,basereg,disp) \ - do { \ - *(inst)++ = (unsigned char)0xff; \ - x86_membase_emit ((inst), 4, (basereg), (disp)); \ - } while (0) -#endif /* __native_client_codegen__ */ -/* - * target is a pointer in our buffer. - */ -#define x86_jump_code_body(inst,target) \ - do { \ - int t; \ - x86_codegen_pre(&(inst), 2); \ - t = (unsigned char*)(target) - (inst) - 2; \ - if (x86_is_imm8(t)) { \ - x86_jump8 ((inst), t); \ - } else { \ - x86_codegen_pre(&(inst), 5); \ - t = (unsigned char*)(target) - (inst) - 5; \ - x86_jump32 ((inst), t); \ - } \ - } while (0) - -#if defined(__default_codegen__) -#define x86_jump_code(inst,target) \ - do { \ - x86_jump_code_body((inst),(target)); \ - } while (0) -#elif defined(__native_client_codegen__) && defined(TARGET_X86) -#define x86_jump_code(inst,target) \ - do { \ - guint8* jump_start = (inst); \ - x86_jump_code_body((inst),(target)); \ - x86_patch(jump_start, (target)); \ - } while (0) -#elif defined(__native_client_codegen__) && defined(TARGET_AMD64) -#define x86_jump_code(inst,target) \ - do { \ - /* jump_code_body is used twice because there are offsets */ \ - /* calculated based on the IP, which can change after the */ \ - /* call to amd64_codegen_post */ \ - amd64_codegen_pre(inst); \ - x86_jump_code_body((inst),(target)); \ - inst = amd64_codegen_post(inst); \ - x86_jump_code_body((inst),(target)); \ - } while (0) -#endif /* __native_client_codegen__ */ - -#define x86_jump_disp(inst,disp) \ - do { \ - int t = (disp) - 2; \ - if (x86_is_imm8(t)) { \ - x86_jump8 ((inst), t); \ - } else { \ - t -= 3; \ - x86_jump32 ((inst), t); \ - } \ - } while (0) - -#if defined(TARGET_X86) -#define x86_branch8(inst,cond,imm,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 2); \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)]; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)]; \ - x86_imm_emit8 ((inst), (imm)); \ - } while (0) - -#define x86_branch32(inst,cond,imm,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 6); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] + 0x10; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x10; \ - x86_imm_emit32 ((inst), (imm)); \ - } while (0) -#elif defined(TARGET_AMD64) -/* These macros are used directly from mini-amd64.c and other */ -/* amd64 specific files, so they need to be instrumented directly. */ -#define x86_branch8(inst,cond,imm,is_signed) \ - do { \ - amd64_codegen_pre(inst); \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)]; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)]; \ - x86_imm_emit8 ((inst), (imm)); \ - amd64_codegen_post(inst); \ - } while (0) -#define x86_branch32(inst,cond,imm,is_signed) \ - do { \ - amd64_codegen_pre(inst); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] + 0x10; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x10; \ - x86_imm_emit32 ((inst), (imm)); \ - amd64_codegen_post(inst); \ - } while (0) -#endif - -#if defined(TARGET_X86) -#define x86_branch(inst,cond,target,is_signed) \ - do { \ - int offset; \ - guint8* branch_start; \ - x86_codegen_pre(&(inst), 2); \ - offset = (target) - (inst) - 2; \ - branch_start = (inst); \ - if (x86_is_imm8 ((offset))) \ - x86_branch8 ((inst), (cond), offset, (is_signed)); \ - else { \ - x86_codegen_pre(&(inst), 6); \ - offset = (target) - (inst) - 6; \ - x86_branch32 ((inst), (cond), offset, (is_signed)); \ - } \ - x86_patch(branch_start, (target)); \ - } while (0) -#elif defined(TARGET_AMD64) -/* This macro is used directly from mini-amd64.c and other */ -/* amd64 specific files, so it needs to be instrumented directly. */ - -#define x86_branch_body(inst,cond,target,is_signed) \ - do { \ - int offset = (target) - (inst) - 2; \ - if (x86_is_imm8 ((offset))) \ - x86_branch8 ((inst), (cond), offset, (is_signed)); \ - else { \ - offset = (target) - (inst) - 6; \ - x86_branch32 ((inst), (cond), offset, (is_signed)); \ - } \ - } while (0) - -#if defined(__default_codegen__) -#define x86_branch(inst,cond,target,is_signed) \ - do { \ - x86_branch_body((inst),(cond),(target),(is_signed)); \ - } while (0) -#elif defined(__native_client_codegen__) -#define x86_branch(inst,cond,target,is_signed) \ - do { \ - /* branch_body is used twice because there are offsets */ \ - /* calculated based on the IP, which can change after */ \ - /* the call to amd64_codegen_post */ \ - amd64_codegen_pre(inst); \ - x86_branch_body((inst),(cond),(target),(is_signed)); \ - inst = amd64_codegen_post(inst); \ - x86_branch_body((inst),(cond),(target),(is_signed)); \ - } while (0) -#endif /* __native_client_codegen__ */ - -#endif /* TARGET_AMD64 */ - -#define x86_branch_disp(inst,cond,disp,is_signed) \ - do { \ - int offset = (disp) - 2; \ - if (x86_is_imm8 ((offset))) \ - x86_branch8 ((inst), (cond), offset, (is_signed)); \ - else { \ - offset -= 4; \ - x86_branch32 ((inst), (cond), offset, (is_signed)); \ - } \ - } while (0) - -#define x86_set_reg(inst,cond,reg,is_signed) \ - do { \ - g_assert (X86_IS_BYTE_REG (reg)); \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ - x86_reg_emit ((inst), 0, (reg)); \ - } while (0) - -#define x86_set_mem(inst,cond,mem,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ - x86_mem_emit ((inst), 0, (mem)); \ - } while (0) - -#define x86_set_membase(inst,cond,basereg,disp,is_signed) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ - x86_membase_emit ((inst), 0, (basereg), (disp)); \ - } while (0) - -#define x86_call_imm_body(inst,disp) \ - do { \ - *(inst)++ = (unsigned char)0xe8; \ - x86_imm_emit32 ((inst), (int)(disp)); \ - } while (0) - -#define x86_call_imm(inst,disp) \ - do { \ - x86_call_sequence_pre((inst)); \ - x86_call_imm_body((inst), (disp)); \ - x86_call_sequence_post((inst)); \ - } while (0) - - -#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) -#define x86_call_reg_internal(inst,reg) \ - do { \ - *(inst)++ = (unsigned char)0x83; /* and */ \ - x86_reg_emit ((inst), 4, (reg)); /* reg */ \ - *(inst)++ = (unsigned char)nacl_align_byte; \ - *(inst)++ = (unsigned char)0xff; /* call */ \ - x86_reg_emit ((inst), 2, (reg)); /* reg */ \ - } while (0) - -#define x86_call_reg(inst, reg) do { \ - x86_call_sequence_pre((inst)); \ - x86_call_reg_internal(inst, reg); \ - x86_call_sequence_post((inst)); \ - } while (0) - - -/* It appears that x86_call_mem() is never used, so I'm leaving it out. */ -#define x86_call_membase(inst,basereg,disp) do { \ - x86_call_sequence_pre((inst)); \ - /* x86_mov_reg_membase() inlined so its fixed size */ \ - *(inst)++ = (unsigned char)0x8b; \ - x86_address_byte ((inst), 2, (X86_ECX), (basereg)); \ - x86_imm_emit32 ((inst), (disp)); \ - x86_call_reg_internal(inst, X86_ECX); \ - x86_call_sequence_post((inst)); \ - } while (0) -#else /* __native_client_codegen__ */ -#define x86_call_reg(inst,reg) \ - do { \ - *(inst)++ = (unsigned char)0xff; \ - x86_reg_emit ((inst), 2, (reg)); \ - } while (0) - -#define x86_call_mem(inst,mem) \ - do { \ - *(inst)++ = (unsigned char)0xff; \ - x86_mem_emit ((inst), 2, (mem)); \ - } while (0) - -#define x86_call_membase(inst,basereg,disp) \ - do { \ - *(inst)++ = (unsigned char)0xff; \ - x86_membase_emit ((inst), 2, (basereg), (disp)); \ - } while (0) -#endif /* __native_client_codegen__ */ - - -#if defined( __native_client_codegen__ ) && defined( TARGET_X86 ) - -#define x86_call_code(inst,target) \ - do { \ - int _x86_offset; \ - guint8* call_start; \ - guint8* _aligned_start; \ - x86_call_sequence_pre_val((inst)); \ - _x86_offset = (unsigned char*)(target) - (inst); \ - _x86_offset -= 5; \ - x86_call_imm_body ((inst), _x86_offset); \ - _aligned_start = x86_call_sequence_post_val((inst)); \ - call_start = _aligned_start; \ - _x86_offset = (unsigned char*)(target) - (_aligned_start); \ - _x86_offset -= 5; \ - x86_call_imm_body ((_aligned_start), _x86_offset); \ - x86_patch(call_start, (target)); \ - } while (0) - -#define SIZE_OF_RET 6 -#define x86_ret(inst) do { \ - *(inst)++ = (unsigned char)0x59; /* pop ecx */ \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0x83; /* and 0xffffffff, ecx */ \ - *(inst)++ = (unsigned char)0xe1; \ - *(inst)++ = (unsigned char)nacl_align_byte; \ - *(inst)++ = (unsigned char)0xff; /* jmp ecx */ \ - *(inst)++ = (unsigned char)0xe1; \ - } while (0) - -/* pop return address */ -/* pop imm bytes from stack */ -/* return */ -#define x86_ret_imm(inst,imm) do { \ - *(inst)++ = (unsigned char)0x59; /* pop ecx */ \ - x86_alu_reg_imm ((inst), X86_ADD, X86_ESP, imm); \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0x83; /* and 0xffffffff, ecx */ \ - *(inst)++ = (unsigned char)0xe1; \ - *(inst)++ = (unsigned char)nacl_align_byte; \ - *(inst)++ = (unsigned char)0xff; /* jmp ecx */ \ - *(inst)++ = (unsigned char)0xe1; \ -} while (0) -#else /* __native_client_codegen__ */ - -#define x86_call_code(inst,target) \ - do { \ - int _x86_offset; \ - _x86_offset = (unsigned char*)(target) - (inst); \ - _x86_offset -= 5; \ - x86_call_imm_body ((inst), _x86_offset); \ - } while (0) - -#define x86_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) - -#define x86_ret_imm(inst,imm) \ - do { \ - if ((imm) == 0) { \ - x86_ret ((inst)); \ - } else { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0xc2; \ - x86_imm_emit16 ((inst), (imm)); \ - } \ - } while (0) -#endif /* __native_client_codegen__ */ - -#define x86_cmov_reg(inst,cond,is_signed,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char) 0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#define x86_cmov_mem(inst,cond,is_signed,reg,mem) \ - do { \ - x86_codegen_pre(&(inst), 7); \ - *(inst)++ = (unsigned char) 0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ - x86_mem_emit ((inst), (reg), (mem)); \ - } while (0) - -#define x86_cmov_membase(inst,cond,is_signed,reg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char) 0x0f; \ - if ((is_signed)) \ - *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ - else \ - *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_enter(inst,framesize) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0xc8; \ - x86_imm_emit16 ((inst), (framesize)); \ - *(inst)++ = 0; \ - } while (0) - -#define x86_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) -#define x86_sahf(inst) do { *(inst)++ = (unsigned char)0x9e; } while (0) - -#define x86_fsin(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfe; } while (0) -#define x86_fcos(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xff; } while (0) -#define x86_fabs(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe1; } while (0) -#define x86_ftst(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe4; } while (0) -#define x86_fxam(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe5; } while (0) -#define x86_fpatan(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf3; } while (0) -#define x86_fprem(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf8; } while (0) -#define x86_fprem1(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf5; } while (0) -#define x86_frndint(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfc; } while (0) -#define x86_fsqrt(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfa; } while (0) -#define x86_fptan(inst) do { x86_codegen_pre(&(inst), 2); *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf2; } while (0) - -#define x86_padding(inst,size) \ - do { \ - switch ((size)) { \ - case 1: x86_nop ((inst)); break; \ - case 2: *(inst)++ = 0x8b; \ - *(inst)++ = 0xc0; break; \ - case 3: *(inst)++ = 0x8d; *(inst)++ = 0x6d; \ - *(inst)++ = 0x00; break; \ - case 4: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ - *(inst)++ = 0x24; *(inst)++ = 0x00; \ - break; \ - case 5: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ - *(inst)++ = 0x24; *(inst)++ = 0x00; \ - x86_nop ((inst)); break; \ - case 6: *(inst)++ = 0x8d; *(inst)++ = 0xad; \ - *(inst)++ = 0x00; *(inst)++ = 0x00; \ - *(inst)++ = 0x00; *(inst)++ = 0x00; \ - break; \ - case 7: *(inst)++ = 0x8d; *(inst)++ = 0xa4; \ - *(inst)++ = 0x24; *(inst)++ = 0x00; \ - *(inst)++ = 0x00; *(inst)++ = 0x00; \ - *(inst)++ = 0x00; break; \ - default: assert (0); \ - } \ - } while (0) - -#ifdef __native_client_codegen__ - -#define kx86NaClLengthOfCallReg 5 -#define kx86NaClLengthOfCallImm 5 -#define kx86NaClLengthOfCallMembase (kx86NaClLengthOfCallReg + 6) - -#endif /* __native_client_codegen__ */ - -#define x86_prolog(inst,frame_size,reg_mask) \ - do { \ - unsigned i, m = 1; \ - x86_enter ((inst), (frame_size)); \ - for (i = 0; i < X86_NREG; ++i, m <<= 1) { \ - if ((reg_mask) & m) \ - x86_push_reg ((inst), i); \ - } \ - } while (0) - -#define x86_epilog(inst,reg_mask) \ - do { \ - unsigned i, m = 1 << X86_EDI; \ - for (i = X86_EDI; m != 0; i--, m=m>>1) { \ - if ((reg_mask) & m) \ - x86_pop_reg ((inst), i); \ - } \ - x86_leave ((inst)); \ - x86_ret ((inst)); \ - } while (0) - - -typedef enum { - X86_SSE_SQRT = 0x51, - X86_SSE_RSQRT = 0x52, - X86_SSE_RCP = 0x53, - X86_SSE_ADD = 0x58, - X86_SSE_DIV = 0x5E, - X86_SSE_MUL = 0x59, - X86_SSE_SUB = 0x5C, - X86_SSE_MIN = 0x5D, - X86_SSE_MAX = 0x5F, - X86_SSE_COMP = 0xC2, - X86_SSE_AND = 0x54, - X86_SSE_ANDN = 0x55, - X86_SSE_OR = 0x56, - X86_SSE_XOR = 0x57, - X86_SSE_UNPCKL = 0x14, - X86_SSE_UNPCKH = 0x15, - - X86_SSE_ADDSUB = 0xD0, - X86_SSE_HADD = 0x7C, - X86_SSE_HSUB = 0x7D, - X86_SSE_MOVSHDUP = 0x16, - X86_SSE_MOVSLDUP = 0x12, - X86_SSE_MOVDDUP = 0x12, - - X86_SSE_PAND = 0xDB, - X86_SSE_POR = 0xEB, - X86_SSE_PXOR = 0xEF, - - X86_SSE_PADDB = 0xFC, - X86_SSE_PADDW = 0xFD, - X86_SSE_PADDD = 0xFE, - X86_SSE_PADDQ = 0xD4, - - X86_SSE_PSUBB = 0xF8, - X86_SSE_PSUBW = 0xF9, - X86_SSE_PSUBD = 0xFA, - X86_SSE_PSUBQ = 0xFB, - - X86_SSE_PMAXSB = 0x3C, /*sse41*/ - X86_SSE_PMAXSW = 0xEE, - X86_SSE_PMAXSD = 0x3D, /*sse41*/ - - X86_SSE_PMAXUB = 0xDE, - X86_SSE_PMAXUW = 0x3E, /*sse41*/ - X86_SSE_PMAXUD = 0x3F, /*sse41*/ - - X86_SSE_PMINSB = 0x38, /*sse41*/ - X86_SSE_PMINSW = 0xEA, - X86_SSE_PMINSD = 0x39,/*sse41*/ - - X86_SSE_PMINUB = 0xDA, - X86_SSE_PMINUW = 0x3A, /*sse41*/ - X86_SSE_PMINUD = 0x3B, /*sse41*/ - - X86_SSE_PAVGB = 0xE0, - X86_SSE_PAVGW = 0xE3, - - X86_SSE_PCMPEQB = 0x74, - X86_SSE_PCMPEQW = 0x75, - X86_SSE_PCMPEQD = 0x76, - X86_SSE_PCMPEQQ = 0x29, /*sse41*/ - - X86_SSE_PCMPGTB = 0x64, - X86_SSE_PCMPGTW = 0x65, - X86_SSE_PCMPGTD = 0x66, - X86_SSE_PCMPGTQ = 0x37, /*sse42*/ - - X86_SSE_PSADBW = 0xf6, - - X86_SSE_PSHUFD = 0x70, - - X86_SSE_PUNPCKLBW = 0x60, - X86_SSE_PUNPCKLWD = 0x61, - X86_SSE_PUNPCKLDQ = 0x62, - X86_SSE_PUNPCKLQDQ = 0x6C, - - X86_SSE_PUNPCKHBW = 0x68, - X86_SSE_PUNPCKHWD = 0x69, - X86_SSE_PUNPCKHDQ = 0x6A, - X86_SSE_PUNPCKHQDQ = 0x6D, - - X86_SSE_PACKSSWB = 0x63, - X86_SSE_PACKSSDW = 0x6B, - - X86_SSE_PACKUSWB = 0x67, - X86_SSE_PACKUSDW = 0x2B,/*sse41*/ - - X86_SSE_PADDUSB = 0xDC, - X86_SSE_PADDUSW = 0xDD, - X86_SSE_PSUBUSB = 0xD8, - X86_SSE_PSUBUSW = 0xD9, - - X86_SSE_PADDSB = 0xEC, - X86_SSE_PADDSW = 0xED, - X86_SSE_PSUBSB = 0xE8, - X86_SSE_PSUBSW = 0xE9, - - X86_SSE_PMULLW = 0xD5, - X86_SSE_PMULLD = 0x40,/*sse41*/ - X86_SSE_PMULHUW = 0xE4, - X86_SSE_PMULHW = 0xE5, - X86_SSE_PMULUDQ = 0xF4, - - X86_SSE_PMOVMSKB = 0xD7, - - X86_SSE_PSHIFTW = 0x71, - X86_SSE_PSHIFTD = 0x72, - X86_SSE_PSHIFTQ = 0x73, - X86_SSE_SHR = 2, - X86_SSE_SAR = 4, - X86_SSE_SHL = 6, - - X86_SSE_PSRLW_REG = 0xD1, - X86_SSE_PSRAW_REG = 0xE1, - X86_SSE_PSLLW_REG = 0xF1, - - X86_SSE_PSRLD_REG = 0xD2, - X86_SSE_PSRAD_REG = 0xE2, - X86_SSE_PSLLD_REG = 0xF2, - - X86_SSE_PSRLQ_REG = 0xD3, - X86_SSE_PSLLQ_REG = 0xF3, - - X86_SSE_PREFETCH = 0x18, - X86_SSE_MOVNTPS = 0x2B, - X86_SSE_MOVHPD_REG_MEMBASE = 0x16, - X86_SSE_MOVHPD_MEMBASE_REG = 0x17, - - X86_SSE_MOVSD_REG_MEMBASE = 0x10, - X86_SSE_MOVSD_MEMBASE_REG = 0x11, - - X86_SSE_PINSRB = 0x20,/*sse41*/ - X86_SSE_PINSRW = 0xC4, - X86_SSE_PINSRD = 0x22,/*sse41*/ - - X86_SSE_PEXTRB = 0x14,/*sse41*/ - X86_SSE_PEXTRW = 0xC5, - X86_SSE_PEXTRD = 0x16,/*sse41*/ - - X86_SSE_SHUFP = 0xC6, - - X86_SSE_CVTDQ2PD = 0xE6, - X86_SSE_CVTDQ2PS = 0x5B, - X86_SSE_CVTPD2DQ = 0xE6, - X86_SSE_CVTPD2PS = 0x5A, - X86_SSE_CVTPS2DQ = 0x5B, - X86_SSE_CVTPS2PD = 0x5A, - X86_SSE_CVTTPD2DQ = 0xE6, - X86_SSE_CVTTPS2DQ = 0x5B, -} X86_SSE_Opcode; - - -/* minimal SSE* support */ -#define x86_movsd_reg_membase(inst,dreg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xf2; \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x10; \ - x86_membase_emit ((inst), (dreg), (basereg), (disp)); \ - } while (0) - -#define x86_cvttsd2si(inst,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0xf2; \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x2c; \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#define x86_sse_alu_reg_reg(inst,opc,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0F; \ - *(inst)++ = (unsigned char)(opc); \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#define x86_sse_alu_reg_membase(inst,opc,sreg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)(opc); \ - x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ - } while (0) - -#define x86_sse_alu_membase_reg(inst,opc,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0F; \ - *(inst)++ = (unsigned char)(opc); \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_sse_alu_reg_reg_imm8(inst,opc,dreg,reg, imm8) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0x0F; \ - *(inst)++ = (unsigned char)(opc); \ - x86_reg_emit ((inst), (dreg), (reg)); \ - *(inst)++ = (unsigned char)(imm8); \ - } while (0) - -#define x86_sse_alu_pd_reg_reg_imm8(inst,opc,dreg,reg, imm8) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0x66; \ - x86_sse_alu_reg_reg_imm8 ((inst), (opc), (dreg), (reg), (imm8)); \ - } while (0) - -#define x86_sse_alu_pd_reg_reg(inst,opc,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0x66; \ - x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ - } while (0) - -#define x86_sse_alu_pd_membase_reg(inst,opc,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x66; \ - x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ - } while (0) - -#define x86_sse_alu_pd_reg_membase(inst,opc,dreg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x66; \ - x86_sse_alu_reg_membase ((inst), (opc), (dreg),(basereg), (disp)); \ - } while (0) - -#define x86_sse_alu_pd_reg_reg_imm(inst,opc,dreg,reg,imm) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - x86_sse_alu_pd_reg_reg ((inst), (opc), (dreg), (reg)); \ - *(inst)++ = (unsigned char)(imm); \ - } while (0) - -#define x86_sse_alu_pd_reg_membase_imm(inst,opc,dreg,basereg,disp,imm) \ - do { \ - x86_codegen_pre(&(inst), 4 + kMaxMembaseEmitPadding); \ - x86_sse_alu_pd_reg_membase ((inst), (opc), (dreg),(basereg), (disp)); \ - *(inst)++ = (unsigned char)(imm); \ - } while (0) - - -#define x86_sse_alu_ps_reg_reg(inst,opc,dreg,reg) \ - do { \ - x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ - } while (0) - -#define x86_sse_alu_ps_reg_reg_imm(inst,opc,dreg,reg, imm) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ - *(inst)++ = (unsigned char)imm; \ - } while (0) - - -#define x86_sse_alu_sd_reg_reg(inst,opc,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0xF2; \ - x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ - } while (0) - -#define x86_sse_alu_sd_membase_reg(inst,opc,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xF2; \ - x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ - } while (0) - - -#define x86_sse_alu_ss_reg_reg(inst,opc,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0xF3; \ - x86_sse_alu_reg_reg ((inst), (opc), (dreg), (reg)); \ - } while (0) - -#define x86_sse_alu_ss_membase_reg(inst,opc,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0xF3; \ - x86_sse_alu_membase_reg ((inst), (opc), (basereg), (disp), (reg)); \ - } while (0) - - - -#define x86_sse_alu_sse41_reg_reg(inst,opc,dreg,reg) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)0x66; \ - *(inst)++ = (unsigned char)0x0F; \ - *(inst)++ = (unsigned char)0x38; \ - *(inst)++ = (unsigned char)(opc); \ - x86_reg_emit ((inst), (dreg), (reg)); \ - } while (0) - -#define x86_movups_reg_membase(inst,sreg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x10; \ - x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ - } while (0) - -#define x86_movups_membase_reg(inst,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x11; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_movaps_reg_membase(inst,sreg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x28; \ - x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ - } while (0) - -#define x86_movaps_membase_reg(inst,basereg,disp,reg) \ - do { \ - x86_codegen_pre(&(inst), 2 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x29; \ - x86_membase_emit ((inst), (reg), (basereg), (disp)); \ - } while (0) - -#define x86_movaps_reg_reg(inst,dreg,sreg) \ - do { \ - x86_codegen_pre(&(inst), 3); \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x28; \ - x86_reg_emit ((inst), (dreg), (sreg)); \ - } while (0) - - -#define x86_movd_reg_xreg(inst,dreg,sreg) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0x66; \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x7e; \ - x86_reg_emit ((inst), (sreg), (dreg)); \ - } while (0) - -#define x86_movd_xreg_reg(inst,dreg,sreg) \ - do { \ - x86_codegen_pre(&(inst), 4); \ - *(inst)++ = (unsigned char)0x66; \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x6e; \ - x86_reg_emit ((inst), (dreg), (sreg)); \ - } while (0) - -#define x86_movd_xreg_membase(inst,sreg,basereg,disp) \ - do { \ - x86_codegen_pre(&(inst), 3 + kMaxMembaseEmitPadding); \ - *(inst)++ = (unsigned char)0x66; \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x6e; \ - x86_membase_emit ((inst), (sreg), (basereg), (disp)); \ - } while (0) - -#define x86_pshufw_reg_reg(inst,dreg,sreg,mask,high_words) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - *(inst)++ = (unsigned char)(high_words) ? 0xF3 : 0xF2; \ - *(inst)++ = (unsigned char)0x0f; \ - *(inst)++ = (unsigned char)0x70; \ - x86_reg_emit ((inst), (dreg), (sreg)); \ - *(inst)++ = (unsigned char)mask; \ - } while (0) - -#define x86_sse_shift_reg_imm(inst,opc,mode, dreg,imm) \ - do { \ - x86_codegen_pre(&(inst), 5); \ - x86_sse_alu_pd_reg_reg (inst, opc, mode, dreg); \ - x86_imm_emit8 ((inst), (imm)); \ - } while (0) - -#define x86_sse_shift_reg_reg(inst,opc,dreg,sreg) \ - do { \ - x86_sse_alu_pd_reg_reg (inst, opc, dreg, sreg); \ - } while (0) - - - -#endif // X86_H - diff --git a/main.cpp b/main.cpp index 623338f939..9704aa5d95 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,6 @@ #include "qmljs_objects.h" #include "qv4codegen_p.h" -#include "qv4isel_x86_64_p.h" #include "qv4isel_masm_p.h" #include "qv4isel_moth_p.h" #include "qv4vme_moth_p.h" diff --git a/qv4isel_x86_64.cpp b/qv4isel_x86_64.cpp deleted file mode 100644 index e975a03221..0000000000 --- a/qv4isel_x86_64.cpp +++ /dev/null @@ -1,986 +0,0 @@ - -#include "qv4isel_x86_64_p.h" -#include "qmljs_runtime.h" -#include "qmljs_objects.h" - -#define TARGET_AMD64 -#define g_assert assert -typedef quint64 guint64; -typedef qint64 gint64; -typedef uchar guint8; -typedef uint guint32; -typedef void *gpointer; -#include "asm/amd64-codegen.h" - -#include -#include -#include - -#ifndef NO_UDIS86 -# include -#endif - -#define qmljs_call_code(ptr, code) \ - do { \ - amd64_mov_reg_imm(ptr, AMD64_RAX, code); \ - amd64_call_reg(ptr, AMD64_RAX); \ - } while (0) - -using namespace QQmlJS; -using namespace QQmlJS::x86_64; -using namespace QQmlJS::VM; - -namespace { -QTextStream qout(stdout, QIODevice::WriteOnly); -} - -static inline void -amd64_patch (unsigned char* code, gpointer target) -{ - guint8 rex = 0; - -#ifdef __native_client_codegen__ - code = amd64_skip_nops (code); -#endif -#if defined(__native_client_codegen__) && defined(__native_client__) - if (nacl_is_code_address (code)) { - /* For tail calls, code is patched after being installed */ - /* but not through the normal "patch callsite" method. */ - unsigned char buf[kNaClAlignment]; - unsigned char *aligned_code = (uintptr_t)code & ~kNaClAlignmentMask; - int ret; - memcpy (buf, aligned_code, kNaClAlignment); - /* Patch a temp buffer of bundle size, */ - /* then install to actual location. */ - amd64_patch (buf + ((uintptr_t)code - (uintptr_t)aligned_code), target); - ret = nacl_dyncode_modify (aligned_code, buf, kNaClAlignment); - g_assert (ret == 0); - return; - } - target = nacl_modify_patch_target (target); -#endif - - /* Skip REX */ - if ((code [0] >= 0x40) && (code [0] <= 0x4f)) { - rex = code [0]; - code += 1; - } - - if ((code [0] & 0xf8) == 0xb8) { - /* amd64_set_reg_template */ - *(guint64*)(code + 1) = (guint64)target; - } - else if ((code [0] == 0x8b) && rex && x86_modrm_mod (code [1]) == 0 && x86_modrm_rm (code [1]) == 5) { - /* mov 0(%rip), %dreg */ - *(guint32*)(code + 2) = (guint32)(guint64)target - 7; - } - else if ((code [0] == 0xff) && (code [1] == 0x15)) { - /* call *(%rip) */ - *(guint32*)(code + 2) = ((guint32)(guint64)target) - 7; - } - else if (code [0] == 0xe8) { - /* call */ - gint64 disp = (guint8*)target - (guint8*)code; - assert (amd64_is_imm32 (disp)); - x86_patch (code, (unsigned char*)target); - } - else - x86_patch (code, (unsigned char*)target); -} -InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *buffer) - : _engine(engine) - , _module(module) - , _function(0) - , _block(0) - , _buffer(buffer) - , _code(buffer) - , _codePtr(buffer) -{ -} - -InstructionSelection::~InstructionSelection() -{ -} - -void InstructionSelection::operator()(IR::Function *function) -{ - qSwap(_function, function); - - _code = _codePtr; - _code = (uchar *) ((size_t(_code) + 15) & ~15); - _function->code = (void (*)(VM::Context *, const uchar *)) _code; - _codePtr = _code; - _patches.clear(); - _addrs.clear(); - - int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) * sizeof(Value); - locals = (locals + 15) & ~15; - - amd64_push_reg(_codePtr, AMD64_RBP); - amd64_push_reg(_codePtr, AMD64_R14); - amd64_push_reg(_codePtr, AMD64_R15); - - amd64_mov_reg_reg(_codePtr, AMD64_RBP, AMD64_RSP, 8); - amd64_mov_reg_reg(_codePtr, AMD64_R14, AMD64_RDI, 8); - amd64_alu_reg_imm(_codePtr, X86_SUB, AMD64_RSP, locals); - - amd64_mov_reg_membase(_codePtr, AMD64_R15, AMD64_R14, offsetof(Context, locals), 8); - - foreach (IR::BasicBlock *block, _function->basicBlocks) { - _block = block; - _addrs[block] = _codePtr; - foreach (IR::Stmt *s, block->statements) { - s->accept(this); - } - } - - QHashIterator > it(_patches); - while (it.hasNext()) { - it.next(); - IR::BasicBlock *block = it.key(); - uchar *target = _addrs.value(block); - assert(target != 0); - foreach (uchar *instr, it.value()) { - amd64_patch(instr, target); - } - } - - amd64_alu_reg_imm(_codePtr, X86_ADD, AMD64_RSP, locals); - amd64_pop_reg(_codePtr, AMD64_R15); - amd64_pop_reg(_codePtr, AMD64_R14); - amd64_pop_reg(_codePtr, AMD64_RBP); - amd64_ret(_codePtr); - -#ifndef NO_UDIS86 - static bool showCode = !qgetenv("SHOW_CODE").isNull(); - if (showCode) { - printf("code size: %ld bytes\n", (_codePtr - _code)); - ud_t ud_obj; - - ud_init(&ud_obj); - ud_set_input_buffer(&ud_obj, _code, _codePtr - _code); - ud_set_mode(&ud_obj, 64); - ud_set_syntax(&ud_obj, UD_SYN_ATT); - - while (ud_disassemble(&ud_obj)) { - printf("\t%s\n", ud_insn_asm(&ud_obj)); - } - } -#endif - qSwap(_function, _function); -} - -String *InstructionSelection::identifier(const QString &s) -{ - return _engine->identifier(s); -} - -void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) -{ - if (t->index < 0) { - const int arg = -t->index - 1; - amd64_mov_reg_membase(_codePtr, reg, AMD64_R14, offsetof(Context, arguments), 8); - amd64_lea_membase(_codePtr, reg, reg, sizeof(Value) * arg); - } else if (t->index < _function->locals.size()) { - amd64_lea_membase(_codePtr, reg, AMD64_R15, sizeof(Value) * t->index); - } else { - amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * (_function->maxNumberOfArguments + t->index - _function->locals.size())); - } -} - -void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) -{ - IR::Name *baseName = call->base->asName(); - assert(baseName != 0); - - int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = call->args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); - loadTempAddress(AMD64_RSI, arg); - qmljs_call_code(_codePtr, __qmljs_copy); - } - - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context - - if (result) - loadTempAddress(AMD64_RSI, result); - else - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - - if (baseName->id) { - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id)); - amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); - qmljs_call_code(_codePtr, __qmljs_call_activation_property); - } else { - switch (baseName->builtin) { - case IR::Name::builtin_invalid: - Q_UNREACHABLE(); - break; - case IR::Name::builtin_typeof: - amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); - qmljs_call_code(_codePtr, __qmljs_builtin_typeof); - break; - case IR::Name::builtin_delete: - Q_UNREACHABLE(); - break; - case IR::Name::builtin_throw: - amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); - qmljs_call_code(_codePtr, __qmljs_builtin_throw); - break; - case IR::Name::builtin_rethrow: - amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); - qmljs_call_code(_codePtr, __qmljs_builtin_rethrow); - return; // we need to return to avoid checking the exceptions - } - } - - checkExceptions(); -} - - -void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) -{ - IR::Temp *baseTemp = call->base->asTemp(); - assert(baseTemp != 0); - - int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = call->args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); - loadTempAddress(AMD64_RSI, arg); - qmljs_call_code(_codePtr, __qmljs_copy); - } - - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context - - if (result) - loadTempAddress(AMD64_RSI, result); - else - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RDX, AMD64_RDX); - loadTempAddress(AMD64_RCX, baseTemp); - amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); - qmljs_call_code(_codePtr, __qmljs_call_value); - - checkExceptions(); -} - -void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) -{ - IR::Member *member = call->base->asMember(); - assert(member != 0); - assert(member->base->asTemp()); - - int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = call->args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); - loadTempAddress(AMD64_RSI, arg); - qmljs_call_code(_codePtr, __qmljs_copy); - } - - //__qmljs_call_property(ctx, result, base, name, args, argc); - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context - - if (result) - loadTempAddress(AMD64_RSI, result); - else - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - - loadTempAddress(AMD64_RDX, member->base->asTemp()); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name)); - amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); - qmljs_call_code(_codePtr, __qmljs_call_property); - - checkExceptions(); -} - -void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) -{ - IR::Name *baseName = call->base->asName(); - assert(baseName != 0); - - int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = call->args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); - loadTempAddress(AMD64_RSI, arg); - qmljs_call_code(_codePtr, __qmljs_copy); - } - - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context - - if (result) - loadTempAddress(AMD64_RSI, result); - else - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id)); - amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); - qmljs_call_code(_codePtr, __qmljs_construct_activation_property); - - checkExceptions(); -} - -void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) -{ - IR::Member *member = call->base->asMember(); - assert(member != 0); - assert(member->base->asTemp()); - - int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = call->args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); - loadTempAddress(AMD64_RSI, arg); - qmljs_call_code(_codePtr, __qmljs_copy); - } - - //__qmljs_call_property(ctx, result, base, name, args, argc); - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context - - if (result) - loadTempAddress(AMD64_RSI, result); - else - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - - loadTempAddress(AMD64_RDX, member->base->asTemp()); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name)); - amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); - qmljs_call_code(_codePtr, __qmljs_construct_property); - - checkExceptions(); -} - -void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) -{ - IR::Temp *baseTemp = call->base->asTemp(); - assert(baseTemp != 0); - - int argc = 0; - for (IR::ExprList *it = call->args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = call->args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); - loadTempAddress(AMD64_RSI, arg); - qmljs_call_code(_codePtr, __qmljs_copy); - } - - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context - - if (result) - loadTempAddress(AMD64_RSI, result); - else - amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); - - loadTempAddress(AMD64_RDX, baseTemp); - amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); - amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); - qmljs_call_code(_codePtr, __qmljs_construct_value); -} - -void InstructionSelection::checkExceptions() -{ - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R14, offsetof(Context, hasUncaughtException), 4); - amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 1, 1); - _patches[_function->handlersBlock].append(_codePtr); - x86_branch32(_codePtr, X86_CC_E, 0, 1); -} - -void InstructionSelection::visitExp(IR::Exp *s) -{ - if (IR::Call *c = s->expr->asCall()) { - if (c->base->asName()) { - callActivationProperty(c, 0); - return; - } else if (c->base->asTemp()) { - callValue(c, 0); - return; - } else if (c->base->asMember()) { - callProperty(c, 0); - return; - } - } - Q_UNIMPLEMENTED(); - assert(!"TODO"); -} - -void InstructionSelection::visitEnter(IR::Enter *) -{ - Q_UNIMPLEMENTED(); - assert(!"TODO"); -} - -void InstructionSelection::visitLeave(IR::Leave *) -{ - Q_UNIMPLEMENTED(); - assert(!"TODO"); -} - -void InstructionSelection::visitMove(IR::Move *s) -{ - // %rdi, %rsi, %rdx, %rcx, %r8 and %r9 - if (s->op == IR::OpInvalid) { - if (IR::Name *n = s->target->asName()) { - String *propertyName = identifier(*n->id); - - if (IR::Temp *t = s->source->asTemp()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); - loadTempAddress(AMD64_RDX, t); - qmljs_call_code(_codePtr, __qmljs_set_activation_property); - checkExceptions(); - return; - } else { - Q_UNREACHABLE(); - } - } else if (IR::Temp *t = s->target->asTemp()) { - if (IR::Name *n = s->source->asName()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. - qmljs_call_code(_codePtr, __qmljs_get_thisObject); - } else { - String *propertyName = identifier(*n->id); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, propertyName); - qmljs_call_code(_codePtr, __qmljs_get_activation_property); - checkExceptions(); - } - return; - } else if (IR::Const *c = s->source->asConst()) { - loadTempAddress(AMD64_RSI, t); - - switch (c->type) { - case IR::NullType: - amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(ValueData, tag), Value::Null_Type, 4); - amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(ValueData, uint_32), 0, 4); - break; - - case IR::UndefinedType: - amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(ValueData, tag), Value::Undefined_Type, 4); - amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(ValueData, uint_32), 0, 4); - break; - - case IR::BoolType: - amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(ValueData, tag), Value::Boolean_Type, 4); - amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(ValueData, b), c->value != 0, 1); - break; - - case IR::NumberType: - amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); - // ### why go through XMM0 here? - amd64_movsd_reg_regp(_codePtr, AMD64_XMM0, AMD64_RAX); - amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(ValueData, dbl), AMD64_XMM0); - break; - - default: - Q_UNIMPLEMENTED(); - assert(!"TODO"); - } - return; - } else if (IR::Temp *t2 = s->source->asTemp()) { - loadTempAddress(AMD64_RDI, t); - loadTempAddress(AMD64_RSI, t2); - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, 0, 4); - amd64_mov_membase_reg(_codePtr, AMD64_RDI, 0, AMD64_RAX, 4); - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(ValueData, dbl), 8); - amd64_mov_membase_reg(_codePtr, AMD64_RDI, offsetof(ValueData, dbl), AMD64_RAX, 8); - return; - } else if (IR::String *str = s->source->asString()) { - loadTempAddress(AMD64_RDI, t); - amd64_mov_reg_imm(_codePtr, AMD64_RSI, _engine->newString(*str->value)); - qmljs_call_code(_codePtr, __qmljs_init_string); - return; - } else if (IR::Closure *clos = s->source->asClosure()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, clos->value); - qmljs_call_code(_codePtr, __qmljs_init_closure); - return; - } else if (IR::New *ctor = s->source->asNew()) { - if (ctor->base->asName()) { - constructActivationProperty(ctor, t); - return; - } else if (ctor->base->asMember()) { - constructProperty(ctor, t); - return; - } else if (ctor->base->asTemp()) { - constructValue(ctor, t); - return; - } - } else if (IR::Member *m = s->source->asMember()) { - //__qmljs_get_property(ctx, result, object, name); - if (IR::Temp *base = m->base->asTemp()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - loadTempAddress(AMD64_RDX, base); - amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*m->name)); - qmljs_call_code(_codePtr, __qmljs_get_property); - checkExceptions(); - return; - } - assert(!"wip"); - return; - } else if (IR::Subscript *ss = s->source->asSubscript()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - loadTempAddress(AMD64_RDX, ss->base->asTemp()); - loadTempAddress(AMD64_RCX, ss->index->asTemp()); - qmljs_call_code(_codePtr, __qmljs_get_element); - checkExceptions(); - return; - } else if (IR::Unop *u = s->source->asUnop()) { - if (IR::Temp *e = u->expr->asTemp()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - loadTempAddress(AMD64_RDX, e); - void (*op)(Context *, Value *, const Value *) = 0; - switch (u->op) { - case IR::OpIfTrue: assert(!"unreachable"); break; - case IR::OpNot: op = __qmljs_not; break; - case IR::OpUMinus: op = __qmljs_uminus; break; - case IR::OpUPlus: op = __qmljs_uplus; break; - case IR::OpCompl: op = __qmljs_compl; break; - default: assert(!"unreachable"); break; - } // switch - qmljs_call_code(_codePtr, op); - return; - } - } else if (IR::Binop *b = s->source->asBinop()) { - IR::Temp *l = b->left->asTemp(); - IR::Temp *r = b->right->asTemp(); - if (l && r) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - loadTempAddress(AMD64_RDX, l); - loadTempAddress(AMD64_RCX, r); -#if 0 - // ### needs adjustments - uchar *label1 = 0, *label2 = 0, *label3 = 0; - - if (b->op == IR::OpMul || b->op == IR::OpAdd || b->op == IR::OpSub || b->op == IR::OpDiv) { - amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RDX, 0, Value::Double_Type, 4); - label1 = _codePtr; - x86_branch8(_codePtr, X86_CC_NE, 0, 0); - amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RCX, 0, Value::Double_Type, 4); - label2 = _codePtr; - x86_branch8(_codePtr, X86_CC_NE, 0, 0); - amd64_movsd_reg_membase(_codePtr, AMD64_XMM0, AMD64_RDX, offsetof(ValueData, dbl)); - amd64_movsd_reg_membase(_codePtr, AMD64_XMM1, AMD64_RCX, offsetof(ValueData, dbl)); - switch (b->op) { - case IR::OpAdd: - amd64_sse_addsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); - break; - case IR::OpSub: - amd64_sse_subsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); - break; - case IR::OpMul: - amd64_sse_mulsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); - break; - case IR::OpDiv: - amd64_sse_divsd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); - break; - default: - Q_UNREACHABLE(); - } // switch - - amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, Value::Double_Type, 4); - amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(ValueData, dbl), AMD64_XMM0); - label3 = _codePtr; - x86_jump32(_codePtr, 0); - } - - - if (label1 && label2) { - amd64_patch(label1, _codePtr); - amd64_patch(label2, _codePtr); - } -#endif - - void (*op)(Context *, Value *, const Value *, const Value *) = 0; - - switch ((IR::AluOp) b->op) { - case IR::OpInvalid: - case IR::OpIfTrue: - case IR::OpNot: - case IR::OpUMinus: - case IR::OpUPlus: - case IR::OpCompl: - assert(!"unreachable"); - break; - - case IR::OpBitAnd: op = __qmljs_bit_and; break; - case IR::OpBitOr: op = __qmljs_bit_or; break; - case IR::OpBitXor: op = __qmljs_bit_xor; break; - case IR::OpAdd: op = __qmljs_add; break; - case IR::OpSub: op = __qmljs_sub; break; - case IR::OpMul: op = __qmljs_mul; break; - case IR::OpDiv: op = __qmljs_div; break; - case IR::OpMod: op = __qmljs_mod; break; - case IR::OpLShift: op = __qmljs_shl; break; - case IR::OpRShift: op = __qmljs_shr; break; - case IR::OpURShift: op = __qmljs_ushr; break; - case IR::OpGt: op = __qmljs_gt; break; - case IR::OpLt: op = __qmljs_lt; break; - case IR::OpGe: op = __qmljs_ge; break; - case IR::OpLe: op = __qmljs_le; break; - case IR::OpEqual: op = __qmljs_eq; break; - case IR::OpNotEqual: op = __qmljs_ne; break; - case IR::OpStrictEqual: op = __qmljs_se; break; - case IR::OpStrictNotEqual: op = __qmljs_sne; break; - case IR::OpInstanceof: op = __qmljs_instanceof; break; - case IR::OpIn: op = __qmljs_in; break; - - case IR::OpAnd: - case IR::OpOr: - assert(!"unreachable"); - break; - } - qmljs_call_code(_codePtr, op); - // ### -// if (label3) -// amd64_patch(label3, _codePtr); - return; - } - } else if (IR::Call *c = s->source->asCall()) { - if (c->base->asName()) { - callActivationProperty(c, t); - return; - } else if (c->base->asMember()) { - callProperty(c, t); - return; - } else if (c->base->asTemp()) { - callValue(c, t); - return; - } - } - } else if (IR::Member *m = s->target->asMember()) { - if (IR::Temp *base = m->base->asTemp()) { - if (IR::Temp *t = s->source->asTemp()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, base); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); - loadTempAddress(AMD64_RCX, t); - qmljs_call_code(_codePtr, __qmljs_set_property); - checkExceptions(); - return; - } else { - Q_UNREACHABLE(); - } - } - } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (IR::Temp *t2 = s->source->asTemp()) { - loadTempAddress(AMD64_RSI, ss->base->asTemp()); - loadTempAddress(AMD64_RDX, ss->index->asTemp()); - loadTempAddress(AMD64_RCX, t2); - qmljs_call_code(_codePtr, __qmljs_set_element); - checkExceptions(); - return; - } else { - Q_UNIMPLEMENTED(); - } - } - } else { - // inplace assignment, e.g. x += 1, ++x, ... - if (IR::Temp *t = s->target->asTemp()) { - if (IR::Temp *t2 = s->source->asTemp()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - amd64_mov_reg_reg(_codePtr, AMD64_RDX, AMD64_RSI, 8); - loadTempAddress(AMD64_RCX, t2); - void (*op)(Context *, Value *, const Value *, const Value *) = 0; - switch (s->op) { - case IR::OpBitAnd: op = __qmljs_bit_and; break; - case IR::OpBitOr: op = __qmljs_bit_or; break; - case IR::OpBitXor: op = __qmljs_bit_xor; break; - case IR::OpAdd: op = __qmljs_add; break; - case IR::OpSub: op = __qmljs_sub; break; - case IR::OpMul: op = __qmljs_mul; break; - case IR::OpDiv: op = __qmljs_div; break; - case IR::OpMod: op = __qmljs_mod; break; - case IR::OpLShift: op = __qmljs_shl; break; - case IR::OpRShift: op = __qmljs_shr; break; - case IR::OpURShift: op = __qmljs_ushr; break; - default: - Q_UNREACHABLE(); - break; - } - - qmljs_call_code(_codePtr, op); - return; - } - } else if (IR::Name *n = s->target->asName()) { - if (IR::Temp *t = s->source->asTemp()) { - void (*op)(Context *, String *, Value *) = 0; - switch (s->op) { - case IR::OpBitAnd: op = __qmljs_inplace_bit_and_name; break; - case IR::OpBitOr: op = __qmljs_inplace_bit_or_name; break; - case IR::OpBitXor: op = __qmljs_inplace_bit_xor_name; break; - case IR::OpAdd: op = __qmljs_inplace_add_name; break; - case IR::OpSub: op = __qmljs_inplace_sub_name; break; - case IR::OpMul: op = __qmljs_inplace_mul_name; break; - case IR::OpDiv: op = __qmljs_inplace_div_name; break; - case IR::OpMod: op = __qmljs_inplace_mod_name; break; - case IR::OpLShift: op = __qmljs_inplace_shl_name; break; - case IR::OpRShift: op = __qmljs_inplace_shr_name; break; - case IR::OpURShift: op = __qmljs_inplace_ushr_name; break; - default: - Q_UNREACHABLE(); - break; - } - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - amd64_mov_reg_imm(_codePtr, AMD64_RSI, identifier(*n->id)); - loadTempAddress(AMD64_RDX, t); - qmljs_call_code(_codePtr, op); - checkExceptions(); - return; - } - } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (IR::Temp *t = s->source->asTemp()) { - void (*op)(Context *, Value *, Value *, Value *) = 0; - switch (s->op) { - case IR::OpBitAnd: op = __qmljs_inplace_bit_and_element; break; - case IR::OpBitOr: op = __qmljs_inplace_bit_or_element; break; - case IR::OpBitXor: op = __qmljs_inplace_bit_xor_element; break; - case IR::OpAdd: op = __qmljs_inplace_add_element; break; - case IR::OpSub: op = __qmljs_inplace_sub_element; break; - case IR::OpMul: op = __qmljs_inplace_mul_element; break; - case IR::OpDiv: op = __qmljs_inplace_div_element; break; - case IR::OpMod: op = __qmljs_inplace_mod_element; break; - case IR::OpLShift: op = __qmljs_inplace_shl_element; break; - case IR::OpRShift: op = __qmljs_inplace_shr_element; break; - case IR::OpURShift: op = __qmljs_inplace_ushr_element; break; - default: - Q_UNREACHABLE(); - break; - } - - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, ss->base->asTemp()); - loadTempAddress(AMD64_RDX, ss->index->asTemp()); - loadTempAddress(AMD64_RCX, t); - qmljs_call_code(_codePtr, op); - checkExceptions(); - return; - } - } else if (IR::Member *m = s->target->asMember()) { - if (IR::Temp *t = s->source->asTemp()) { - void (*op)(Context *, Value *, String *, Value *) = 0; - switch (s->op) { - case IR::OpBitAnd: op = __qmljs_inplace_bit_and_member; break; - case IR::OpBitOr: op = __qmljs_inplace_bit_or_member; break; - case IR::OpBitXor: op = __qmljs_inplace_bit_xor_member; break; - case IR::OpAdd: op = __qmljs_inplace_add_member; break; - case IR::OpSub: op = __qmljs_inplace_sub_member; break; - case IR::OpMul: op = __qmljs_inplace_mul_member; break; - case IR::OpDiv: op = __qmljs_inplace_div_member; break; - case IR::OpMod: op = __qmljs_inplace_mod_member; break; - case IR::OpLShift: op = __qmljs_inplace_shl_member; break; - case IR::OpRShift: op = __qmljs_inplace_shr_member; break; - case IR::OpURShift: op = __qmljs_inplace_ushr_member; break; - default: - Q_UNREACHABLE(); - break; - } - - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, m->base->asTemp()); - amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); - loadTempAddress(AMD64_RCX, t); - qmljs_call_code(_codePtr, op); - checkExceptions(); - return; - } - } - } - - Q_UNIMPLEMENTED(); - s->dump(qout, IR::Stmt::MIR); - qout << endl; - assert(!"TODO"); -} - -void InstructionSelection::visitJump(IR::Jump *s) -{ - if (_block->index + 1 != s->target->index) { - _patches[s->target].append(_codePtr); - x86_jump32(_codePtr, 0); - } -} - -void InstructionSelection::visitCJump(IR::CJump *s) -{ - if (IR::Temp *t = s->cond->asTemp()) { - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - loadTempAddress(AMD64_RSI, t); - - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(ValueData, tag), 4); - amd64_alu_reg_imm(_codePtr, X86_CMP, AMD64_RAX, Value::Boolean_Type); - - uchar *label1 = _codePtr; - x86_branch8(_codePtr, X86_CC_NE, 0, 0); - - amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(ValueData, b), 1); - - uchar *label2 = _codePtr; - x86_jump8(_codePtr, 0); - - amd64_patch(label1, _codePtr); - qmljs_call_code(_codePtr, __qmljs_to_boolean); - - amd64_patch(label2, _codePtr); - amd64_mov_reg_imm_size(_codePtr, AMD64_RDX, 1, 1); - amd64_alu_reg8_reg8(_codePtr, X86_CMP, AMD64_RAX, AMD64_RDX, 0, 0); - _patches[s->iftrue].append(_codePtr); - x86_branch32(_codePtr, X86_CC_E, 0, 1); - - if (_block->index + 1 != s->iffalse->index) { - _patches[s->iffalse].append(_codePtr); - x86_jump32(_codePtr, 0); - } - return; - } else if (IR::Binop *b = s->cond->asBinop()) { - IR::Temp *l = b->left->asTemp(); - IR::Temp *r = b->right->asTemp(); - if (l && r) { - loadTempAddress(AMD64_RSI, l); - loadTempAddress(AMD64_RDX, r); - -#if 0 // ### FIXME - uchar *label1 = 0, *label2 = 0, *label3 = 0; - if (b->op != IR::OpInstanceof && b->op != IR::OpIn) { - amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RSI, 0, Value::Double_Type, 4); - label1 = _codePtr; - x86_branch8(_codePtr, X86_CC_NE, 0, 0); - amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RDX, 0, Value::Double_Type, 4); - label2 = _codePtr; - x86_branch8(_codePtr, X86_CC_NE, 0, 0); - amd64_movsd_reg_membase(_codePtr, AMD64_XMM0, AMD64_RSI, offsetof(ValueData, dbl)); - amd64_movsd_reg_membase(_codePtr, AMD64_XMM1, AMD64_RDX, offsetof(ValueData, dbl)); - - int op; - switch (b->op) { - default: Q_UNREACHABLE(); assert(!"todo"); break; - case IR::OpGt: op = X86_CC_GT; break; - case IR::OpLt: op = X86_CC_LT; break; - case IR::OpGe: op = X86_CC_GE; break; - case IR::OpLe: op = X86_CC_LE; break; - case IR::OpEqual: op = X86_CC_EQ; break; - case IR::OpNotEqual: op = X86_CC_NE; break; - case IR::OpStrictEqual: op = X86_CC_EQ; break; - case IR::OpStrictNotEqual: op = X86_CC_NE; break; - } - - amd64_sse_ucomisd_reg_reg(_codePtr, AMD64_XMM0, AMD64_XMM1); - amd64_set_reg_size(_codePtr, op, AMD64_RAX, 0, 1); - - label3 = _codePtr; - x86_jump32(_codePtr, 0); - } - - if (label1 && label2) { - amd64_patch(label1, _codePtr); - amd64_patch(label2, _codePtr); - } -#endif - amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); - - bool (*op)(Context *, const Value *, const Value *); - switch (b->op) { - default: Q_UNREACHABLE(); assert(!"todo"); break; - case IR::OpGt: op = __qmljs_cmp_gt; break; - case IR::OpLt: op = __qmljs_cmp_lt; break; - case IR::OpGe: op = __qmljs_cmp_ge; break; - case IR::OpLe: op = __qmljs_cmp_le; break; - case IR::OpEqual: op = __qmljs_cmp_eq; break; - case IR::OpNotEqual: op = __qmljs_cmp_ne; break; - case IR::OpStrictEqual: op = __qmljs_cmp_se; break; - case IR::OpStrictNotEqual: op = __qmljs_cmp_sne; break; - case IR::OpInstanceof: op = __qmljs_cmp_instanceof; break; - case IR::OpIn: op = __qmljs_cmp_in; break; - } // switch - - qmljs_call_code(_codePtr, op); - - // ### -// if (label3) -// amd64_patch(label3, _codePtr); - - x86_mov_reg_imm(_codePtr, X86_EDX, 1); - x86_alu_reg8_reg8(_codePtr, X86_CMP, X86_EAX, X86_EDX, 0, 0); - - _patches[s->iftrue].append(_codePtr); - x86_branch32(_codePtr, X86_CC_E, 0, 1); - - if (_block->index + 1 != s->iffalse->index) { - _patches[s->iffalse].append(_codePtr); - x86_jump32(_codePtr, 0); - } - - return; - } else { - assert(!"wip"); - } - } - - Q_UNIMPLEMENTED(); - assert(!"TODO"); -} - -void InstructionSelection::visitRet(IR::Ret *s) -{ - if (IR::Temp *t = s->expr->asTemp()) { - amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_R14, offsetof(Context, result)); - loadTempAddress(AMD64_RSI, t); - qmljs_call_code(_codePtr, __qmljs_copy); - return; - } - Q_UNIMPLEMENTED(); - Q_UNUSED(s); -} - diff --git a/qv4isel_x86_64_p.h b/qv4isel_x86_64_p.h deleted file mode 100644 index d53d028c50..0000000000 --- a/qv4isel_x86_64_p.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef QV4ISEL_P_H -#define QV4ISEL_P_H - -#include "qv4ir_p.h" -#include "qmljs_objects.h" - -#include - -namespace QQmlJS { -namespace x86_64 { - -class InstructionSelection: protected IR::StmtVisitor -{ -public: - InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *code); - ~InstructionSelection(); - - void operator()(IR::Function *function); - -protected: - VM::String *identifier(const QString &s); - void loadTempAddress(int reg, IR::Temp *t); - void callActivationProperty(IR::Call *call, IR::Temp *result); - void callProperty(IR::Call *call, IR::Temp *result); - void constructActivationProperty(IR::New *call, IR::Temp *result); - void constructProperty(IR::New *ctor, IR::Temp *result); - void callValue(IR::Call *call, IR::Temp *result); - void constructValue(IR::New *call, IR::Temp *result); - void checkExceptions(); - - virtual void visitExp(IR::Exp *); - virtual void visitEnter(IR::Enter *); - virtual void visitLeave(IR::Leave *); - virtual void visitMove(IR::Move *); - virtual void visitJump(IR::Jump *); - virtual void visitCJump(IR::CJump *); - virtual void visitRet(IR::Ret *); - -private: - VM::ExecutionEngine *_engine; - IR::Module *_module; - IR::Function *_function; - IR::BasicBlock *_block; - uchar *_buffer; - uchar *_code; - uchar *_codePtr; - QHash > _patches; - QHash _addrs; -}; - -} // end of namespace x86_64 -} // end of namespace QQmlJS - -#endif // QV4ISEL_P_H diff --git a/v4.pro b/v4.pro index 0e0ab82e27..e688d34bac 100644 --- a/v4.pro +++ b/v4.pro @@ -2,11 +2,6 @@ QT = core qmldevtools-private CONFIG -= app_bundle CONFIG += console -DEFINES += __default_codegen__ - -udis86:LIBS += -ludis86 -else:DEFINES += NO_UDIS86 - LIBS += -rdynamic SOURCES += main.cpp \ @@ -17,7 +12,6 @@ SOURCES += main.cpp \ qv4syntaxchecker.cpp \ qv4ecmaobjects.cpp \ qv4array.cpp \ - qv4isel_x86_64.cpp \ qv4isel_masm.cpp \ llvm_runtime.cpp @@ -29,21 +23,8 @@ HEADERS += \ qv4syntaxchecker_p.h \ qv4ecmaobjects_p.h \ qv4array_p.h \ - qv4isel_x86_64_p.h \ qv4isel_masm_p.h -HEADERS += \ - asm/x86-codegen.h \ - asm/amd64-codegen.h \ - asm/arm-codegen.h \ - asm/arm-dis.h \ - asm/arm_dpimacros.h \ - asm/arm-wmmx.h - -SOURCES += \ - asm/arm-codegen.c \ - asm/arm-dis.c - llvm { SOURCES += \ -- cgit v1.2.3 From 52d0992610851563bc62fc571d90ab484eb1c3a3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 10 Oct 2012 13:19:32 +0200 Subject: Ignore generated files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 15858ed0c2..dfa1cc38a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.o Makefile v4 +udis86_itab.* +*.pyc -- cgit v1.2.3 From 85dc29103f45f7817ab996acb7db06d2ed74e223 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 10 Oct 2012 13:22:47 +0200 Subject: Work around gcc bug on amd64 with function pointer assignment --- qv4isel_masm.cpp | 161 +++++++++++++++++++++++++------------------------------ 1 file changed, 74 insertions(+), 87 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index cbf6f7053e..7ce009bd54 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -338,24 +338,20 @@ void InstructionSelection::visitMove(IR::Move *s) return; } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { - void (*op)(Context *, Value *, const Value *) = 0; switch (u->op) { case IR::OpIfTrue: assert(!"unreachable"); break; - case IR::OpNot: op = __qmljs_not; break; - case IR::OpUMinus: op = __qmljs_uminus; break; - case IR::OpUPlus: op = __qmljs_uplus; break; - case IR::OpCompl: op = __qmljs_compl; break; + case IR::OpNot: generateFunctionCall(__qmljs_not, ContextRegister, t, e); break; + case IR::OpUMinus: generateFunctionCall(__qmljs_uminus, ContextRegister, t, e); break; + case IR::OpUPlus: generateFunctionCall(__qmljs_uplus, ContextRegister, t, e); break; + case IR::OpCompl: generateFunctionCall(__qmljs_compl, ContextRegister, t, e); break; default: assert(!"unreachable"); break; } // switch - generateFunctionCall(op, ContextRegister, t, e); return; } } else if (IR::Binop *b = s->source->asBinop()) { IR::Temp *l = b->left->asTemp(); IR::Temp *r = b->right->asTemp(); if (l && r) { - void (*op)(Context *, Value *, const Value *, const Value *) = 0; - switch ((IR::AluOp) b->op) { case IR::OpInvalid: case IR::OpIfTrue: @@ -366,34 +362,33 @@ void InstructionSelection::visitMove(IR::Move *s) assert(!"unreachable"); break; - case IR::OpBitAnd: op = __qmljs_bit_and; break; - case IR::OpBitOr: op = __qmljs_bit_or; break; - case IR::OpBitXor: op = __qmljs_bit_xor; break; - case IR::OpAdd: op = __qmljs_add; break; - case IR::OpSub: op = __qmljs_sub; break; - case IR::OpMul: op = __qmljs_mul; break; - case IR::OpDiv: op = __qmljs_div; break; - case IR::OpMod: op = __qmljs_mod; break; - case IR::OpLShift: op = __qmljs_shl; break; - case IR::OpRShift: op = __qmljs_shr; break; - case IR::OpURShift: op = __qmljs_ushr; break; - case IR::OpGt: op = __qmljs_gt; break; - case IR::OpLt: op = __qmljs_lt; break; - case IR::OpGe: op = __qmljs_ge; break; - case IR::OpLe: op = __qmljs_le; break; - case IR::OpEqual: op = __qmljs_eq; break; - case IR::OpNotEqual: op = __qmljs_ne; break; - case IR::OpStrictEqual: op = __qmljs_se; break; - case IR::OpStrictNotEqual: op = __qmljs_sne; break; - case IR::OpInstanceof: op = __qmljs_instanceof; break; - case IR::OpIn: op = __qmljs_in; break; + case IR::OpBitAnd: generateFunctionCall(__qmljs_bit_and, ContextRegister, t, l, r); break; + case IR::OpBitOr: generateFunctionCall(__qmljs_bit_or, ContextRegister, t, l, r); break; + case IR::OpBitXor: generateFunctionCall(__qmljs_bit_xor, ContextRegister, t, l, r); break; + case IR::OpAdd: generateFunctionCall(__qmljs_add, ContextRegister, t, l, r); break; + case IR::OpSub: generateFunctionCall(__qmljs_sub, ContextRegister, t, l, r); break; + case IR::OpMul: generateFunctionCall(__qmljs_mul, ContextRegister, t, l, r); break; + case IR::OpDiv: generateFunctionCall(__qmljs_div, ContextRegister, t, l, r); break; + case IR::OpMod: generateFunctionCall(__qmljs_mod, ContextRegister, t, l, r); break; + case IR::OpLShift: generateFunctionCall(__qmljs_shl, ContextRegister, t, l, r); break; + case IR::OpRShift: generateFunctionCall(__qmljs_shr, ContextRegister, t, l, r); break; + case IR::OpURShift: generateFunctionCall(__qmljs_ushr, ContextRegister, t, l, r); break; + case IR::OpGt: generateFunctionCall(__qmljs_gt, ContextRegister, t, l, r); break; + case IR::OpLt: generateFunctionCall(__qmljs_lt, ContextRegister, t, l, r); break; + case IR::OpGe: generateFunctionCall(__qmljs_ge, ContextRegister, t, l, r); break; + case IR::OpLe: generateFunctionCall(__qmljs_le, ContextRegister, t, l, r); break; + case IR::OpEqual: generateFunctionCall(__qmljs_eq, ContextRegister, t, l, r); break; + case IR::OpNotEqual: generateFunctionCall(__qmljs_ne, ContextRegister, t, l, r); break; + case IR::OpStrictEqual: generateFunctionCall(__qmljs_se, ContextRegister, t, l, r); break; + case IR::OpStrictNotEqual: generateFunctionCall(__qmljs_sne, ContextRegister, t, l, r); break; + case IR::OpInstanceof: generateFunctionCall(__qmljs_instanceof, ContextRegister, t, l, r); break; + case IR::OpIn: generateFunctionCall(__qmljs_in, ContextRegister, t, l, r); break; case IR::OpAnd: case IR::OpOr: assert(!"unreachable"); break; } - generateFunctionCall(op, ContextRegister, t, l, r); return; } } else if (IR::Call *c = s->source->asCall()) { @@ -431,98 +426,90 @@ void InstructionSelection::visitMove(IR::Move *s) // inplace assignment, e.g. x += 1, ++x, ... if (IR::Temp *t = s->target->asTemp()) { if (IR::Temp *t2 = s->source->asTemp()) { - void (*op)(Context *, Value *, const Value *, const Value *) = 0; switch (s->op) { - case IR::OpBitAnd: op = __qmljs_bit_and; break; - case IR::OpBitOr: op = __qmljs_bit_or; break; - case IR::OpBitXor: op = __qmljs_bit_xor; break; - case IR::OpAdd: op = __qmljs_add; break; - case IR::OpSub: op = __qmljs_sub; break; - case IR::OpMul: op = __qmljs_mul; break; - case IR::OpDiv: op = __qmljs_div; break; - case IR::OpMod: op = __qmljs_mod; break; - case IR::OpLShift: op = __qmljs_shl; break; - case IR::OpRShift: op = __qmljs_shr; break; - case IR::OpURShift: op = __qmljs_ushr; break; + case IR::OpBitAnd: generateFunctionCall(__qmljs_bit_and, ContextRegister, t, t, t2); break; + case IR::OpBitOr: generateFunctionCall(__qmljs_bit_or, ContextRegister, t, t, t2); break; + case IR::OpBitXor: generateFunctionCall(__qmljs_bit_xor, ContextRegister, t, t, t2); break; + case IR::OpAdd: generateFunctionCall(__qmljs_add, ContextRegister, t, t, t2); break; + case IR::OpSub: generateFunctionCall(__qmljs_sub, ContextRegister, t, t, t2); break; + case IR::OpMul: generateFunctionCall(__qmljs_mul, ContextRegister, t, t, t2); break; + case IR::OpDiv: generateFunctionCall(__qmljs_div, ContextRegister, t, t, t2); break; + case IR::OpMod: generateFunctionCall(__qmljs_mod, ContextRegister, t, t, t2); break; + case IR::OpLShift: generateFunctionCall(__qmljs_shl, ContextRegister, t, t, t2); break; + case IR::OpRShift: generateFunctionCall(__qmljs_shr, ContextRegister, t, t, t2); break; + case IR::OpURShift: generateFunctionCall(__qmljs_ushr, ContextRegister, t, t, t2); break; default: Q_UNREACHABLE(); break; } - - if (op) - generateFunctionCall(op, ContextRegister, t, t, t2); return; } } else if (IR::Name *n = s->target->asName()) { if (IR::Temp *t = s->source->asTemp()) { - void (*op)(Context *, String *, Value *) = 0; switch (s->op) { - case IR::OpBitAnd: op = __qmljs_inplace_bit_and_name; break; - case IR::OpBitOr: op = __qmljs_inplace_bit_or_name; break; - case IR::OpBitXor: op = __qmljs_inplace_bit_xor_name; break; - case IR::OpAdd: op = __qmljs_inplace_add_name; break; - case IR::OpSub: op = __qmljs_inplace_sub_name; break; - case IR::OpMul: op = __qmljs_inplace_mul_name; break; - case IR::OpDiv: op = __qmljs_inplace_div_name; break; - case IR::OpMod: op = __qmljs_inplace_mod_name; break; - case IR::OpLShift: op = __qmljs_inplace_shl_name; break; - case IR::OpRShift: op = __qmljs_inplace_shr_name; break; - case IR::OpURShift: op = __qmljs_inplace_ushr_name; break; + case IR::OpBitAnd: generateFunctionCall(__qmljs_inplace_bit_and_name, ContextRegister, identifier(*n->id), t); break; + case IR::OpBitOr: generateFunctionCall(__qmljs_inplace_bit_or_name, ContextRegister, identifier(*n->id), t); break; + case IR::OpBitXor: generateFunctionCall(__qmljs_inplace_bit_xor_name, ContextRegister, identifier(*n->id), t); break; + case IR::OpAdd: generateFunctionCall(__qmljs_inplace_add_name, ContextRegister, identifier(*n->id), t); break; + case IR::OpSub: generateFunctionCall(__qmljs_inplace_sub_name, ContextRegister, identifier(*n->id), t); break; + case IR::OpMul: generateFunctionCall(__qmljs_inplace_mul_name, ContextRegister, identifier(*n->id), t); break; + case IR::OpDiv: generateFunctionCall(__qmljs_inplace_div_name, ContextRegister, identifier(*n->id), t); break; + case IR::OpMod: generateFunctionCall(__qmljs_inplace_mod_name, ContextRegister, identifier(*n->id), t); break; + case IR::OpLShift: generateFunctionCall(__qmljs_inplace_shl_name, ContextRegister, identifier(*n->id), t); break; + case IR::OpRShift: generateFunctionCall(__qmljs_inplace_shr_name, ContextRegister, identifier(*n->id), t); break; + case IR::OpURShift: generateFunctionCall(__qmljs_inplace_ushr_name, ContextRegister, identifier(*n->id), t); break; default: Q_UNREACHABLE(); break; } - generateFunctionCall(op, ContextRegister, identifier(*n->id), t); checkExceptions(); return; } } else if (IR::Subscript *ss = s->target->asSubscript()) { if (IR::Temp *t = s->source->asTemp()) { - void (*op)(Context *, Value *, Value *, Value *) = 0; + IR::Temp* base = ss->base->asTemp(); + IR::Temp* index = ss->index->asTemp(); switch (s->op) { - case IR::OpBitAnd: op = __qmljs_inplace_bit_and_element; break; - case IR::OpBitOr: op = __qmljs_inplace_bit_or_element; break; - case IR::OpBitXor: op = __qmljs_inplace_bit_xor_element; break; - case IR::OpAdd: op = __qmljs_inplace_add_element; break; - case IR::OpSub: op = __qmljs_inplace_sub_element; break; - case IR::OpMul: op = __qmljs_inplace_mul_element; break; - case IR::OpDiv: op = __qmljs_inplace_div_element; break; - case IR::OpMod: op = __qmljs_inplace_mod_element; break; - case IR::OpLShift: op = __qmljs_inplace_shl_element; break; - case IR::OpRShift: op = __qmljs_inplace_shr_element; break; - case IR::OpURShift: op = __qmljs_inplace_ushr_element; break; + case IR::OpBitAnd: generateFunctionCall(__qmljs_inplace_bit_and_element, ContextRegister, base, index, t); break; + case IR::OpBitOr: generateFunctionCall(__qmljs_inplace_bit_or_element, ContextRegister, base, index, t); break; + case IR::OpBitXor: generateFunctionCall(__qmljs_inplace_bit_xor_element, ContextRegister, base, index, t); break; + case IR::OpAdd: generateFunctionCall(__qmljs_inplace_add_element, ContextRegister, base, index, t); break; + case IR::OpSub: generateFunctionCall(__qmljs_inplace_sub_element, ContextRegister, base, index, t); break; + case IR::OpMul: generateFunctionCall(__qmljs_inplace_mul_element, ContextRegister, base, index, t); break; + case IR::OpDiv: generateFunctionCall(__qmljs_inplace_div_element, ContextRegister, base, index, t); break; + case IR::OpMod: generateFunctionCall(__qmljs_inplace_mod_element, ContextRegister, base, index, t); break; + case IR::OpLShift: generateFunctionCall(__qmljs_inplace_shl_element, ContextRegister, base, index, t); break; + case IR::OpRShift: generateFunctionCall(__qmljs_inplace_shr_element, ContextRegister, base, index, t); break; + case IR::OpURShift: generateFunctionCall(__qmljs_inplace_ushr_element, ContextRegister, base, index, t); break; default: Q_UNREACHABLE(); break; } - if (op) { - generateFunctionCall(op, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t); - checkExceptions(); - } + checkExceptions(); return; } } else if (IR::Member *m = s->target->asMember()) { if (IR::Temp *t = s->source->asTemp()) { - void (*op)(Context *, Value *, String *, Value *) = 0; + IR::Temp* base = m->base->asTemp(); + String* member = identifier(*m->name); switch (s->op) { - case IR::OpBitAnd: op = __qmljs_inplace_bit_and_member; break; - case IR::OpBitOr: op = __qmljs_inplace_bit_or_member; break; - case IR::OpBitXor: op = __qmljs_inplace_bit_xor_member; break; - case IR::OpAdd: op = __qmljs_inplace_add_member; break; - case IR::OpSub: op = __qmljs_inplace_sub_member; break; - case IR::OpMul: op = __qmljs_inplace_mul_member; break; - case IR::OpDiv: op = __qmljs_inplace_div_member; break; - case IR::OpMod: op = __qmljs_inplace_mod_member; break; - case IR::OpLShift: op = __qmljs_inplace_shl_member; break; - case IR::OpRShift: op = __qmljs_inplace_shr_member; break; - case IR::OpURShift: op = __qmljs_inplace_ushr_member; break; + case IR::OpBitAnd: generateFunctionCall(__qmljs_inplace_bit_and_member, ContextRegister, base, member, t); break; + case IR::OpBitOr: generateFunctionCall(__qmljs_inplace_bit_or_member, ContextRegister, base, member, t); break; + case IR::OpBitXor: generateFunctionCall(__qmljs_inplace_bit_xor_member, ContextRegister, base, member, t); break; + case IR::OpAdd: generateFunctionCall(__qmljs_inplace_add_member, ContextRegister, base, member, t); break; + case IR::OpSub: generateFunctionCall(__qmljs_inplace_sub_member, ContextRegister, base, member, t); break; + case IR::OpMul: generateFunctionCall(__qmljs_inplace_mul_member, ContextRegister, base, member, t); break; + case IR::OpDiv: generateFunctionCall(__qmljs_inplace_div_member, ContextRegister, base, member, t); break; + case IR::OpMod: generateFunctionCall(__qmljs_inplace_mod_member, ContextRegister, base, member, t); break; + case IR::OpLShift: generateFunctionCall(__qmljs_inplace_shl_member, ContextRegister, base, member, t); break; + case IR::OpRShift: generateFunctionCall(__qmljs_inplace_shr_member, ContextRegister, base, member, t); break; + case IR::OpURShift: generateFunctionCall(__qmljs_inplace_ushr_member, ContextRegister, base, member, t); break; default: Q_UNREACHABLE(); break; } - generateFunctionCall(op, ContextRegister, m->base->asTemp(), identifier(*m->name), t); checkExceptions(); return; } -- cgit v1.2.3 From 7a9ece649ef84b22308e17926d4a4e6013c75091 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 10 Oct 2012 13:46:12 +0200 Subject: Emit code for copying values Remove __qmljs_copy in the process. --- qmljs_runtime.cpp | 2 +- qmljs_runtime.h | 5 ----- qv4isel_masm.cpp | 6 +++++- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 2ca6f8a579..230f13c22c 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -330,7 +330,7 @@ void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionO void Context::leaveCallContext(FunctionObject *f, Value *returnValue) { if (returnValue) - __qmljs_copy(returnValue, &result); + *returnValue = result; if (! f->needsActivation) { delete[] locals; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 38e7fcb3f2..a21269361d 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -639,11 +639,6 @@ inline void __qmljs_init_object(Value *result, Object *object) *result = Value::fromObject(object); } -inline void __qmljs_copy(Value *result, Value *source) -{ - *result = *source; -} - // type conversion and testing inline void __qmljs_to_primitive(Context *ctx, Value *result, const Value *value, int typeHint) { diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 7ce009bd54..8129d62907 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -642,5 +642,9 @@ void InstructionSelection::callRuntimeMethodImp(const char* name, BuiltinMethod template void InstructionSelection::copyValue(Result result, Source source) { - generateFunctionCall(__qmljs_copy, result, source); + // ### could avoid using Gpr0 in many cases + loadArgument(source, Gpr0); + loadDouble(Gpr0, FPGpr0); + loadArgument(result, Gpr0); + storeDouble(FPGpr0, Gpr0); } -- cgit v1.2.3 From 8d34c1712ac84a9bf27c45c514200ddcbdbfd7a3 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 10 Oct 2012 14:02:50 +0200 Subject: Simplify function header and footer on x86-64 Generate a few instructions less per function call. --- qv4isel_masm_p.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 7ac8df6056..ea3aae39aa 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -73,15 +73,17 @@ protected: { push(StackFrameRegister); move(StackPointerRegister, StackFrameRegister); - // #### - subPtr(TrustedImmPtr((void*)locals), StackPointerRegister); + subPtr(TrustedImm32(locals), StackPointerRegister); +#if CPU(X86) push(CalleeSavedGpr); +#endif } void leaveStandardStackFrame(quintptr locals) { +#if CPU(X86) pop(CalleeSavedGpr); - // #### - addPtr(TrustedImmPtr((void*)locals), StackPointerRegister); +#endif + addPtr(TrustedImm32(locals), StackPointerRegister); pop(StackFrameRegister); } #else -- cgit v1.2.3 From a9ed16acf30e5964a8c6308362910ac98d887912 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 10 Oct 2012 21:15:43 +0200 Subject: Fix stack corruption The position of temporaries on the stack was off by 1, leading to us overwriting other r14, that gets pushed to the stack after we have allocated space for temps. --- qv4isel_masm.cpp | 7 +++---- qv4isel_masm_p.h | 12 ++++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 8129d62907..f37bc8880a 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -53,8 +53,8 @@ void InstructionSelection::operator()(IR::Function *function) { qSwap(_function, function); - int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) * sizeof(Value); - locals = (locals + 15) & ~15; + int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments); + locals = (locals + 1) & ~1; enterStandardStackFrame(locals); push(ContextRegister); @@ -131,8 +131,7 @@ InstructionSelection::Pointer InstructionSelection::loadTempAddress(RegisterID r offset = t->index * sizeof(Value); } else { const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size(); - offset = sizeof(Value) * (-arg - 1) - - sizeof(void*); // size of ebp + offset = - sizeof(Value) * arg - sizeof(void*); // size of ebp reg = StackFrameRegister; } return Pointer(reg, offset); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index ea3aae39aa..d3d09f4f85 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -69,21 +69,21 @@ protected: }; #if CPU(X86) || CPU(X86_64) - void enterStandardStackFrame(quintptr locals) + void enterStandardStackFrame(int locals) { push(StackFrameRegister); move(StackPointerRegister, StackFrameRegister); - subPtr(TrustedImm32(locals), StackPointerRegister); + subPtr(TrustedImm32(locals*sizeof(QQmlJS::VM::Value)), StackPointerRegister); #if CPU(X86) push(CalleeSavedGpr); #endif } - void leaveStandardStackFrame(quintptr locals) + void leaveStandardStackFrame(int locals) { #if CPU(X86) pop(CalleeSavedGpr); #endif - addPtr(TrustedImm32(locals), StackPointerRegister); + addPtr(TrustedImm32(locals*sizeof(QQmlJS::VM::Value)), StackPointerRegister); pop(StackFrameRegister); } #else @@ -192,12 +192,16 @@ private: void callFunctionPrologue() { +#if CPU(X86) // Callee might clobber it :( push(ContextRegister); +#endif } void callFunctionEpilogue() { +#if CPU(X86) pop(ContextRegister); +#endif } #define isel_stringIfyx(s) #s -- cgit v1.2.3 From 609ef74cd80c8916b7b2e2eb1f1e6037ef20edcc Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 10 Oct 2012 21:22:34 +0200 Subject: Fix ia32 build: make loadArgument available on all archs for copyValue --- qv4isel_masm_p.h | 73 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index d3d09f4f85..fa3630c752 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -160,6 +160,43 @@ private: _callsToLink.append(ctl); } + void loadArgument(RegisterID source, RegisterID dest) + { + move(source, dest); + } + + void loadArgument(const Pointer& ptr, RegisterID dest) + { + addPtr(TrustedImm32(ptr.offset), ptr.base, dest); + } + + void loadArgument(IR::Temp* temp, RegisterID dest) + { + if (temp) { + Pointer addr = loadTempAddress(dest, temp); + loadArgument(addr, dest); + } else { + xorPtr(dest, dest); + } + } + + void loadArgument(TrustedImmPtr ptr, RegisterID dest) + { + move(TrustedImmPtr(ptr), dest); + } + + void loadArgument(VM::String* string, RegisterID dest) + { + loadArgument(TrustedImmPtr(string), dest); + } + + void loadArgument(TrustedImm32 imm32, RegisterID dest) + { + xorPtr(dest, dest); + move(imm32, dest); + } + + using JSC::MacroAssembler::push; void push(const Pointer& ptr) @@ -282,42 +319,6 @@ private: callFunctionEpilogue(); } #elif CPU(X86_64) - void loadArgument(RegisterID source, RegisterID dest) - { - move(source, dest); - } - - void loadArgument(const Pointer& ptr, RegisterID dest) - { - addPtr(TrustedImm32(ptr.offset), ptr.base, dest); - } - - void loadArgument(IR::Temp* temp, RegisterID dest) - { - if (temp) { - Pointer addr = loadTempAddress(dest, temp); - loadArgument(addr, dest); - } else { - xorPtr(dest, dest); - } - } - - void loadArgument(TrustedImmPtr ptr, RegisterID dest) - { - move(TrustedImmPtr(ptr), dest); - } - - void loadArgument(VM::String* string, RegisterID dest) - { - loadArgument(TrustedImmPtr(string), dest); - } - - void loadArgument(TrustedImm32 imm32, RegisterID dest) - { - xorPtr(dest, dest); - move(imm32, dest); - } - template void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) -- cgit v1.2.3 From a1e4d16244325d2d244bf9459732428934fd30aa Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 10 Oct 2012 21:25:40 +0200 Subject: Make conditional jumps more readable --- qv4isel_masm.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index f37bc8880a..2f33cd3e9e 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -561,22 +561,20 @@ void InstructionSelection::visitCJump(IR::CJump *s) IR::Temp *l = b->left->asTemp(); IR::Temp *r = b->right->asTemp(); if (l && r) { - bool (*op)(Context *, const Value *, const Value *); switch (b->op) { default: Q_UNREACHABLE(); assert(!"todo"); break; - case IR::OpGt: op = __qmljs_cmp_gt; break; - case IR::OpLt: op = __qmljs_cmp_lt; break; - case IR::OpGe: op = __qmljs_cmp_ge; break; - case IR::OpLe: op = __qmljs_cmp_le; break; - case IR::OpEqual: op = __qmljs_cmp_eq; break; - case IR::OpNotEqual: op = __qmljs_cmp_ne; break; - case IR::OpStrictEqual: op = __qmljs_cmp_se; break; - case IR::OpStrictNotEqual: op = __qmljs_cmp_sne; break; - case IR::OpInstanceof: op = __qmljs_cmp_instanceof; break; - case IR::OpIn: op = __qmljs_cmp_in; break; + case IR::OpGt: generateFunctionCall(__qmljs_cmp_gt, ContextRegister, l, r); break; + case IR::OpLt: generateFunctionCall(__qmljs_cmp_lt, ContextRegister, l, r); break; + case IR::OpGe: generateFunctionCall(__qmljs_cmp_ge, ContextRegister, l, r); break; + case IR::OpLe: generateFunctionCall(__qmljs_cmp_le, ContextRegister, l, r); break; + case IR::OpEqual: generateFunctionCall(__qmljs_cmp_eq, ContextRegister, l, r); break; + case IR::OpNotEqual: generateFunctionCall(__qmljs_cmp_ne, ContextRegister, l, r); break; + case IR::OpStrictEqual: generateFunctionCall(__qmljs_cmp_se, ContextRegister, l, r); break; + case IR::OpStrictNotEqual: generateFunctionCall(__qmljs_cmp_sne, ContextRegister, l, r); break; + case IR::OpInstanceof: generateFunctionCall(__qmljs_cmp_instanceof, ContextRegister, l, r); break; + case IR::OpIn: generateFunctionCall(__qmljs_cmp_in, ContextRegister, l, r); break; } // switch - generateFunctionCall(op, ContextRegister, l, r); move(ReturnValueRegister, Gpr0); move(TrustedImm32(1), Gpr1); -- cgit v1.2.3 From a13f5069a95350ced896650c3467ce94f6d91303 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 10 Oct 2012 21:28:13 +0200 Subject: Simplify boolean conditional jumps * Expect the boolean value to be in Gpr0, which means after the __qmljs_to_boolean call the move(ReturnValueRegister, Gpr0) becomes a noop * Instead of loading the true/1 into a register, use it as immediate value in the cmp --- qv4isel_masm.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 2f33cd3e9e..f7393f8757 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -534,25 +534,24 @@ void InstructionSelection::jumpToBlock(IR::BasicBlock *target) void InstructionSelection::visitCJump(IR::CJump *s) { if (IR::Temp *t = s->cond->asTemp()) { - Address temp = loadTempAddress(Gpr1, t); + Address temp = loadTempAddress(Gpr0, t); Address tag = temp; tag.offset += offsetof(VM::ValueData, tag); Jump booleanConversion = branch32(NotEqual, tag, TrustedImm32(VM::Value::Boolean_Type)); Address data = temp; data.offset += offsetof(VM::ValueData, b); - load32(data, Gpr1); + load32(data, Gpr0); Jump testBoolean = jump(); booleanConversion.link(this); { generateFunctionCall(__qmljs_to_boolean, ContextRegister, t); - move(ReturnValueRegister, Gpr1); + move(ReturnValueRegister, Gpr0); } testBoolean.link(this); - move(TrustedImm32(1), Gpr0); - Jump target = branch32(Equal, Gpr1, Gpr0); + Jump target = branch32(Equal, Gpr0, TrustedImm32(1)); _patches[s->iftrue].append(target); jumpToBlock(s->iffalse); @@ -577,8 +576,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) move(ReturnValueRegister, Gpr0); - move(TrustedImm32(1), Gpr1); - Jump target = branch32(Equal, Gpr0, Gpr1); + Jump target = branch32(Equal, Gpr0, TrustedImm32(1)); _patches[s->iftrue].append(target); jumpToBlock(s->iffalse); -- cgit v1.2.3 From ca56a7a16382496f4e5b0a4cdf23d2f95c9995bb Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 10 Oct 2012 21:48:27 +0200 Subject: Implement copyValue using a single load/store pair in the common case of copying temps --- qv4isel_masm.cpp | 7 ++----- qv4isel_masm_p.h | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index f7393f8757..998f11342c 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -637,9 +637,6 @@ void InstructionSelection::callRuntimeMethodImp(const char* name, BuiltinMethod template void InstructionSelection::copyValue(Result result, Source source) { - // ### could avoid using Gpr0 in many cases - loadArgument(source, Gpr0); - loadDouble(Gpr0, FPGpr0); - loadArgument(result, Gpr0); - storeDouble(FPGpr0, Gpr0); + loadDouble(source, FPGpr0); + storeDouble(FPGpr0, result); } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index fa3630c752..5ba275d151 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -391,6 +391,20 @@ private: #define callRuntimeMethod(function, ...) \ callRuntimeMethodImp(isel_stringIfy(function), function, __VA_ARGS__) + using JSC::MacroAssembler::loadDouble; + void loadDouble(IR::Temp* temp, FPRegisterID dest) + { + Pointer ptr = loadTempAddress(Gpr0, temp); + loadDouble(ptr, dest); + } + + using JSC::MacroAssembler::storeDouble; + void storeDouble(FPRegisterID source, IR::Temp* temp) + { + Pointer ptr = loadTempAddress(Gpr0, temp); + storeDouble(source, ptr); + } + template void copyValue(Result result, Source source); -- cgit v1.2.3 From 9d11aa9f445ba8a972b8d811a23ed4a7e081a935 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 11 Oct 2012 09:14:14 +0200 Subject: Fix a crash when generating optimised code The base operation for OpInplaceAnd is OpBitAnd, not OpAnd. --- qv4codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index f976202c11..0b7a072353 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -788,7 +788,7 @@ bool Codegen::visit(ArrayMemberExpression *ast) static IR::AluOp baseOp(int op) { switch ((QSOperator::Op) op) { - case QSOperator::InplaceAnd: return IR::OpAnd; + case QSOperator::InplaceAnd: return IR::OpBitAnd; case QSOperator::InplaceSub: return IR::OpSub; case QSOperator::InplaceDiv: return IR::OpDiv; case QSOperator::InplaceAdd: return IR::OpAdd; -- cgit v1.2.3 From 993eedc43e37ae47b48dd22c6e6f118d5246c4a1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 10 Oct 2012 22:40:16 +0200 Subject: Initial build fixes for arm --- masm/stubs/ExecutableAllocator.h | 4 ++ masm/stubs/WTFStubs.cpp | 5 ++ qv4isel_masm.cpp | 4 +- qv4isel_masm_p.h | 125 +++++++++++++++++++++++++++++++++++---- 4 files changed, 126 insertions(+), 12 deletions(-) diff --git a/masm/stubs/ExecutableAllocator.h b/masm/stubs/ExecutableAllocator.h index 6f78fe1f3c..6c0efaf639 100644 --- a/masm/stubs/ExecutableAllocator.h +++ b/masm/stubs/ExecutableAllocator.h @@ -23,6 +23,10 @@ struct ExecutableMemoryHandle : public RefCounted { munmap(m_data, m_size); } + inline void shrink(size_t) { + // ### TODO. + } + inline bool isManaged() const { return true; } void* start() { return m_data; } diff --git a/masm/stubs/WTFStubs.cpp b/masm/stubs/WTFStubs.cpp index 7e16bbc4d8..2c642ac986 100644 --- a/masm/stubs/WTFStubs.cpp +++ b/masm/stubs/WTFStubs.cpp @@ -12,6 +12,11 @@ void* fastMalloc(size_t size) return malloc(size); } +void* fastRealloc(void* ptr, size_t size) +{ + return realloc(ptr, size); +} + void fastFree(void* ptr) { free(ptr); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 998f11342c..d11bde2890 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -60,7 +60,7 @@ void InstructionSelection::operator()(IR::Function *function) push(ContextRegister); #if CPU(X86) loadPtr(addressForArgument(0), ContextRegister); -#elif CPU(X86_64) +#elif CPU(X86_64) || CPU(ARM) move(RegisterArgument1, ContextRegister); #else assert(!"TODO"); @@ -106,7 +106,9 @@ void InstructionSelection::operator()(IR::Function *function) WTF::setDataFile(stdout); fclose(disasmStream); +#if CPU(X86) || CPU(X86_64) printDisassmbleOutputWithCalls(disasmOutput, functions); +#endif free(disasmOutput); _function->code = (void (*)(VM::Context *, const uchar *)) _function->codeRef.code().executableAddress(); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 5ba275d151..b18ead6c87 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -42,7 +42,6 @@ protected: static const RegisterID Gpr1 = JSC::X86Registers::ecx; static const RegisterID Gpr2 = JSC::X86Registers::edx; static const RegisterID Gpr3 = JSC::X86Registers::esi; - static const RegisterID CalleeSavedGpr = ContextRegister; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; static const RegisterID RegisterArgument1 = JSC::X86Registers::edi; @@ -51,6 +50,21 @@ protected: static const RegisterID RegisterArgument4 = JSC::X86Registers::ecx; static const RegisterID RegisterArgument5 = JSC::X86Registers::r8; static const RegisterID RegisterArgument6 = JSC::X86Registers::r9; +#elif CPU(ARM) + static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4; + static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; + static const RegisterID ContextRegister = JSC::ARMRegisters::r5; + static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; + static const RegisterID Gpr0 = JSC::ARMRegisters::r6; + static const RegisterID Gpr1 = JSC::ARMRegisters::r7; + static const RegisterID Gpr2 = JSC::ARMRegisters::r8; + static const RegisterID Gpr3 = JSC::ARMRegisters::r10; + static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; + + static const RegisterID RegisterArgument1 = JSC::ARMRegisters::r0; + static const RegisterID RegisterArgument2 = JSC::ARMRegisters::r1; + static const RegisterID RegisterArgument3 = JSC::ARMRegisters::r2; + static const RegisterID RegisterArgument4 = JSC::ARMRegisters::r3; #else #error Argh. #endif @@ -68,34 +82,46 @@ protected: {} }; -#if CPU(X86) || CPU(X86_64) void enterStandardStackFrame(int locals) { +#if CPU(ARM) + push(JSC::ARMRegisters::lr); +#endif push(StackFrameRegister); move(StackPointerRegister, StackFrameRegister); subPtr(TrustedImm32(locals*sizeof(QQmlJS::VM::Value)), StackPointerRegister); #if CPU(X86) push(CalleeSavedGpr); +#elif CPU(ARM) + for (int saveReg = JSC::ARMRegisters::r4; saveReg <= JSC::ARMRegisters::r11; ++saveReg) + push(static_cast(saveReg)); #endif } void leaveStandardStackFrame(int locals) { #if CPU(X86) pop(CalleeSavedGpr); +#elif CPU(ARM) + for (int saveReg = JSC::ARMRegisters::r11; saveReg >= JSC::ARMRegisters::r4; --saveReg) + pop(static_cast(saveReg)); #endif addPtr(TrustedImm32(locals*sizeof(QQmlJS::VM::Value)), StackPointerRegister); pop(StackFrameRegister); - } -#else -#error Argh. +#if CPU(ARM) + pop(JSC::ARMRegisters::lr); #endif + } + Address stackAddressForArgument(int index) const + { + return Address(StackFrameRegister, (index + 2) * sizeof(void*)); + } #if CPU(X86) Address addressForArgument(int index) const { - return Address(StackFrameRegister, (index + 2) * sizeof(void*)); + return stackAddressForArgument(index); } -#else +#elif CPU(X86_64) Address addressForArgument(int index) const { static RegisterID args[6] = { @@ -108,10 +134,22 @@ protected: }; if (index < 6) return Address(args[index], 0); - else { - Q_UNREACHABLE(); - assert(!"TODO"); - } + else + return stackAddressForArgument(index - 6); + } +#elif CPU(ARM) + Address addressForArgument(int index) const + { + static RegisterID args[4] = { + RegisterArgument1, + RegisterArgument2, + RegisterArgument3, + RegisterArgument4, + }; + if (index < 4) + return Address(args[index], 0); + else + return stackAddressForArgument(index - 4); } #endif @@ -380,6 +418,71 @@ private: callAbsolute(functionName, function); callFunctionEpilogue(); } +#elif CPU(ARM) + + template + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) + { + callFunctionPrologue(); + loadArgument(arg1, RegisterArgument1); + loadArgument(arg2, RegisterArgument2); + loadArgument(arg3, RegisterArgument3); + loadArgument(arg4, RegisterArgument4); + push(arg5); + push(arg6); + callAbsolute(functionName, function); + add32(TrustedImm32(2 * sizeof(void*)), StackPointerRegister); + callFunctionEpilogue(); + } + template + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + { + callFunctionPrologue(); + loadArgument(arg1, RegisterArgument1); + loadArgument(arg2, RegisterArgument2); + loadArgument(arg3, RegisterArgument3); + loadArgument(arg4, RegisterArgument4); + push(arg5); + callAbsolute(functionName, function); + add32(TrustedImm32(1 * sizeof(void*)), StackPointerRegister); + callFunctionEpilogue(); + } + + + template + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + callFunctionPrologue(); + loadArgument(arg1, RegisterArgument1); + loadArgument(arg2, RegisterArgument2); + loadArgument(arg3, RegisterArgument3); + loadArgument(arg4, RegisterArgument4); + callAbsolute(functionName, function); + callFunctionEpilogue(); + } + + + template + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + callFunctionPrologue(); + loadArgument(arg1, RegisterArgument1); + loadArgument(arg2, RegisterArgument2); + loadArgument(arg3, RegisterArgument3); + callAbsolute(functionName, function); + callFunctionEpilogue(); + } + + template + void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) + { + callFunctionPrologue(); + loadArgument(arg1, RegisterArgument1); + loadArgument(arg2, RegisterArgument2); + callAbsolute(functionName, function); + callFunctionEpilogue(); + } + #endif int prepareVariableArguments(IR::ExprList* args); -- cgit v1.2.3 From 54fb3b7af10f3fb8fab7d7fc0ba283dfe9b5f94c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 12 Oct 2012 08:25:02 +0200 Subject: Fix stack corruption on ia32 On ia32 sizeof(void*) < sizeof(Value), so we need to subtract sizeof(Value) instead of sizeof(void*) before reaching the first temp. Added comments to explain the offsets. Change-Id: Iefbb1dc762fdf36d73002c52dc9c471ec15ff403 Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 4 +++- qv4isel_masm_p.h | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index d11bde2890..ec526a3f1e 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -133,7 +133,9 @@ InstructionSelection::Pointer InstructionSelection::loadTempAddress(RegisterID r offset = t->index * sizeof(Value); } else { const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size(); - offset = - sizeof(Value) * arg - sizeof(void*); // size of ebp + // StackFrameRegister points to its old value on the stack, so even for the first temp we need to + // subtract at least sizeof(Value). + offset = - sizeof(Value) * (arg + 1); reg = StackFrameRegister; } return Pointer(reg, offset); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index b18ead6c87..8969306e4b 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -114,6 +114,9 @@ protected: Address stackAddressForArgument(int index) const { + // StackFrameRegister points to its old value on the stack, and above + // it we have the return address, hence the need to step over two + // values before reaching the first argument. return Address(StackFrameRegister, (index + 2) * sizeof(void*)); } #if CPU(X86) -- cgit v1.2.3 From e3142fee0fd6235a9d69118d31343d1a013852c2 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 12 Oct 2012 08:58:29 +0200 Subject: Fix: make it possible to change the used LLVM installation. If either the LLVM_INSTALL_DIR environment variable or the qmake variable are set, use that to find the llvm-config utility. Changing those is easier than modifying your path. Can be used if you have more than one LLVM installation installed. Change-Id: I24265b397e21f468ece61392a112de9a50c67c5a Reviewed-by: Lars Knoll --- v4.pro | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/v4.pro b/v4.pro index e688d34bac..d13cc429e8 100644 --- a/v4.pro +++ b/v4.pro @@ -2,6 +2,12 @@ QT = core qmldevtools-private CONFIG -= app_bundle CONFIG += console +LLVM_CONFIG=llvm-config + +# Pick up the qmake variable or environment variable for LLVM_INSTALL_DIR. If either was set, change the LLVM_CONFIG to use that. +isEmpty(LLVM_INSTALL_DIR):LLVM_INSTALL_DIR=$$(LLVM_INSTALL_DIR) +!isEmpty(LLVM_INSTALL_DIR):LLVM_CONFIG=$$LLVM_INSTALL_DIR/bin/llvm-config + LIBS += -rdynamic SOURCES += main.cpp \ @@ -34,7 +40,7 @@ HEADERS += \ qv4isel_llvm_p.h INCLUDEPATH += \ - $$system(llvm-config --includedir) + $$system($$LLVM_CONFIG --includedir) DEFINES += \ __STDC_CONSTANT_MACROS \ @@ -42,8 +48,8 @@ DEFINES += \ __STDC_LIMIT_MACROS LIBS += \ - $$system(llvm-config --ldflags) \ - $$system(llvm-config --libs core jit bitreader linker ipo target x86 arm) + $$system($$LLVM_CONFIG --ldflags) \ + $$system($$LLVM_CONFIG --libs core jit bitreader linker ipo target x86 arm) QMAKE_EXTRA_TARGETS += gen_llvm_runtime -- cgit v1.2.3 From e28e4e1ac68ce480daa539d62c3631a5782542a7 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 11 Oct 2012 16:39:17 +0200 Subject: Introduce an integer type for Value The type is not being used yet, but all runtime methods should be updated and able to deal with integer Values correctly. In addition started to clean up the use of some of __qmljs_* Value constructors. Change-Id: I1cb2169a5731f6adcc990dcbc92bc96aa008079f Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 4 +- qmljs_runtime.cpp | 48 ++++++++-------- qmljs_runtime.h | 163 ++++++++++++++++++++++++++++++++--------------------- qv4ecmaobjects.cpp | 44 +++++++-------- 4 files changed, 149 insertions(+), 110 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 4f7375a64a..36ef7f7895 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -17,12 +17,12 @@ Value *__qmljs_llvm_get_argument(Context *ctx, int index) void __qmljs_llvm_init_undefined(Value *result) { - __qmljs_init_undefined(result); + *result = Value::undefinedValue(); } void __qmljs_llvm_init_null(Value *result) { - __qmljs_init_null(result); + *result = Value::nullValue(); } void __qmljs_llvm_init_boolean(Value *result, bool value) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 230f13c22c..9cdf037017 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -295,17 +295,17 @@ void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionO { engine = e; parent = f->scope; - __qmljs_init_undefined(&result); + result = Value::undefinedValue(); if (f->needsActivation) __qmljs_init_object(&activation, engine->newActivationObject(this)); else - __qmljs_init_null(&activation); + activation = Value::nullValue(); if (object) thisObject = *object; else - __qmljs_init_null(&thisObject); + thisObject = Value::nullValue(); formals = f->formalParameterList; formalCount = f->formalParameterCount; @@ -417,11 +417,15 @@ void __qmljs_string_literal_function(Context *ctx, Value *result) void __qmljs_delete_subscript(Context *ctx, Value *result, Value *base, Value *index) { if (ArrayObject *a = base->asArrayObject()) { - if (index->isNumber()) { - const quint32 n = index->doubleValue(); + int n = -1; + if (index->isInteger()) + n = index->integerValue(); + else if (index->isDouble()) + n = index->doubleValue(); + if (n >= 0) { if (n < a->value.size()) { a->value.assign(n, Value::undefinedValue()); - __qmljs_init_boolean(result, true); + *result = Value::fromBoolean(true); return; } } @@ -434,7 +438,7 @@ void __qmljs_delete_subscript(Context *ctx, Value *result, Value *base, Value *i void __qmljs_delete_member(Context *ctx, Value *result, Value *base, String *name) { Value obj = base->toObject(ctx); - __qmljs_init_boolean(result, obj.objectValue()->deleteProperty(ctx, name, true)); + *result = Value::fromBoolean(obj.objectValue()->deleteProperty(ctx, name, true)); } void __qmljs_delete_property(Context *ctx, Value *result, String *name) @@ -442,7 +446,7 @@ void __qmljs_delete_property(Context *ctx, Value *result, String *name) Value obj = ctx->activation; if (! obj.isObject()) obj = ctx->engine->globalObject; - __qmljs_init_boolean(result, obj.objectValue()->deleteProperty(ctx, name, true)); + *result = Value::fromBoolean(obj.objectValue()->deleteProperty(ctx, name, true)); } void __qmljs_delete_value(Context *ctx, Value *result, Value *value) @@ -474,7 +478,7 @@ void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Va { if (FunctionObject *function = right->asFunctionObject()) { bool r = function->hasInstance(ctx, *left); - __qmljs_init_boolean(result, r); + *result = Value::fromBoolean(r); return; } @@ -487,7 +491,7 @@ void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *rig Value s; __qmljs_to_string(ctx, &s, left); bool r = right->objectValue()->hasProperty(ctx, s.stringValue()); - __qmljs_init_boolean(result, r); + *result = Value::fromBoolean(r); } else { __qmljs_throw_type_error(ctx, result); } @@ -793,7 +797,7 @@ void __qmljs_object_default_value(Context *ctx, Value *result, const Value *obje } } - __qmljs_init_undefined(result); + *result = Value::undefinedValue(); } void __qmljs_throw_type_error(Context *ctx, Value *result) @@ -867,13 +871,13 @@ void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR: void __qmljs_get_element(Context *ctx, Value *result, Value *object, Value *index) { if (object->isString() && index->isNumber()) { - const QString s = object->stringValue()->toQString().mid(Value::toUInt32(index->doubleValue()), 1); + const QString s = object->stringValue()->toQString().mid(index->toUInt32(ctx), 1); if (s.isNull()) - __qmljs_init_undefined(result); + *result = Value::undefinedValue(); else *result = Value::fromString(ctx, s); } else if (object->isArrayObject() && index->isNumber()) { - *result = object->asArrayObject()->value.at(Value::toUInt32(index->doubleValue())); + *result = object->asArrayObject()->value.at(index->toUInt32(ctx)); } else { String *name = index->toString(ctx); @@ -887,7 +891,7 @@ void __qmljs_get_element(Context *ctx, Value *result, Value *object, Value *inde void __qmljs_set_element(Context *ctx, Value *object, Value *index, Value *value) { if (object->isArrayObject() && index->isNumber()) { - object->asArrayObject()->value.assign(Value::toUInt32(index->doubleValue()), *value); + object->asArrayObject()->value.assign(index->toUInt32(ctx), *value); } else { String *name = index->toString(ctx); @@ -900,8 +904,7 @@ void __qmljs_set_element(Context *ctx, Value *object, Value *index, Value *value void __qmljs_set_element_number(Context *ctx, Value *object, Value *index, double number) { - Value v; - __qmljs_init_number(&v, number); + Value v = Value::fromDouble(number); __qmljs_set_element(ctx, object, index, &v); } @@ -939,8 +942,7 @@ void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool b) void __qmljs_set_activation_property_number(Context *ctx, String *name, double number) { - Value value; - __qmljs_init_number(&value, number); + Value value = Value::fromDouble(number); __qmljs_set_activation_property(ctx, name, &value); } @@ -1010,14 +1012,14 @@ void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y if (px.isString() && py.isString()) { bool r = __qmljs_string_compare(ctx, px.stringValue(), py.stringValue()); - __qmljs_init_boolean(result, r); + *result = Value::fromBoolean(r); } else { double nx = __qmljs_to_number(ctx, &px); double ny = __qmljs_to_number(ctx, &py); if (isnan(nx) || isnan(ny)) { - __qmljs_init_undefined(result); + *result = Value::undefinedValue(); } else { - __qmljs_init_boolean(result, nx < ny); + *result = Value::fromBoolean(nx < ny); } } } @@ -1099,7 +1101,7 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S thisObject = baseObject; } else { baseObject = context->activation; - __qmljs_init_null(&thisObject); + thisObject = Value::nullValue(); } Value func = baseObject.property(context, name); if (FunctionObject *f = func.asFunctionObject()) { diff --git a/qmljs_runtime.h b/qmljs_runtime.h index a21269361d..6094c37283 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -3,6 +3,7 @@ #ifndef QMLJS_LLVM_RUNTIME # include +# include # include #endif @@ -61,8 +62,6 @@ void __qmljs_builtin_throw(Context *context, Value *result, Value *args, int arg void __qmljs_builtin_rethrow(Context *context, Value *result, Value *args, int argc); // constructors -void __qmljs_init_undefined(Value *result); -void __qmljs_init_null(Value *result); void __qmljs_init_boolean(Value *result, bool value); void __qmljs_init_number(Value *result, double number); void __qmljs_init_string(Value *result, String *string); @@ -273,7 +272,7 @@ template <> struct ValueBase<4> : public ValueData Undefined_Type = NaN_Mask | 0x7ffff, // all 1's Null_Type = NaN_Mask | 0x0, Boolean_Type = NaN_Mask | 0x1, -// Integer_Type = NaN_Mask | 0x2, + Integer_Type = NaN_Mask | 0x2, Object_Type = NaN_Mask | 0x2d59b, // give it randomness to avoid accidental collisions (for gc) String_Type = NaN_Mask | 0x2d5ba, Double_Type = 0 @@ -297,6 +296,14 @@ template <> struct ValueBase<4> : public ValueData void setDouble(double d) { dbl = d; } + double asDouble() const { + if (tag == Integer_Type) + return int_32; + return dbl; + } + double integerValue() const { + return int_32; + } String *stringValue() const { return (String *)(quintptr) uint_32; @@ -328,7 +335,7 @@ template <> struct ValueBase<8> : public ValueData Undefined_Type = NaN_Mask | 0x70000, Null_Type = NaN_Mask | 0x00000, Boolean_Type = NaN_Mask | 0x10000, -// Integer_Type = NaN_Mask | 0x20000, + Integer_Type = NaN_Mask | 0x20000, Object_Type = NaN_Mask | 0x30000, String_Type = NaN_Mask | 0x40000, Double_Type = 0 @@ -355,6 +362,14 @@ template <> struct ValueBase<8> : public ValueData void setDouble(double d) { dbl = d; } + double asDouble() const { + if (tag == Integer_Type) + return int_32; + return dbl; + } + double integerValue() const { + return int_32; + } String *stringValue() const { return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift)); @@ -401,10 +416,12 @@ struct Value : public ValueBase inline bool isNull() const { return is(Value::Null_Type); } inline bool isString() const { return is(Value::String_Type); } inline bool isBoolean() const { return type() == Value::Boolean_Type; } - inline bool isNumber() const { return is(Value::Double_Type) /*|| is(Value::Integer_Type)*/; } + inline bool isNumber() const { return is(Value::Integer_Type) || is(Value::Double_Type); } + inline bool isDouble() const { return is(Value::Double_Type); } + inline bool isInteger() const { return type() == Value::Integer_Type; } inline bool isObject() const { return type() == Value::Object_Type; } - inline bool isPrimitive() const { ValueType t = type(); return t != Value::Object_Type; } + inline bool isPrimitive() const { return type() != Value::Object_Type; } bool isFunctionObject() const; bool isBooleanObject() const; bool isNumberObject() const; @@ -462,8 +479,8 @@ inline Value ValueBase<4>::fromDouble(double d) inline Value ValueBase<4>::fromInt32(int i) { Value v; - // ### - v.dbl = i; + v.tag = Integer_Type; + v.int_32 = i; return v; } @@ -516,8 +533,8 @@ inline Value ValueBase<8>::fromDouble(double d) inline Value ValueBase<8>::fromInt32(int i) { Value v; - // ### - v.dbl = i; + v.tag = Integer_Type; + v.int_32 = i; return v; } @@ -583,7 +600,7 @@ struct Context { if (index < argumentCount) *result = arguments[index]; else - __qmljs_init_undefined(result); + *result = Value::undefinedValue(); } void init(ExecutionEngine *eng); @@ -609,21 +626,12 @@ struct Context { extern "C" { // constructors -inline void __qmljs_init_undefined(Value *result) -{ - *result = Value::undefinedValue(); -} - -inline void __qmljs_init_null(Value *result) -{ - *result = Value::nullValue(); -} - inline void __qmljs_init_boolean(Value *result, bool value) { *result = Value::fromBoolean(value); } +// ### remove me inline void __qmljs_init_number(Value *result, double value) { *result = Value::fromDouble(value); @@ -656,14 +664,14 @@ inline bool __qmljs_to_boolean(Context *ctx, const Value *value) return false; case Value::Boolean_Type: return value->booleanValue(); -// case Value::Integer_Type: -// return value->data; + case Value::Integer_Type: + return value->int_32; case Value::String_Type: return __qmljs_string_length(ctx, value->stringValue()) > 0; case Value::Object_Type: return true; default: // double - if (! value->doubleValue() || isnan(value->doubleValue())) + if (! value->doubleValue() || qIsNaN(value->doubleValue())) return false; return true; } @@ -678,8 +686,8 @@ inline double __qmljs_to_number(Context *ctx, const Value *value) return 0; case Value::Boolean_Type: return (value->booleanValue() ? 1. : 0.); -// case Value::Integer_Type: -// return value->data; + case Value::Integer_Type: + return value->int_32; case Value::String_Type: return __qmljs_string_to_number(ctx, value->stringValue()); case Value::Object_Type: { @@ -694,8 +702,10 @@ inline double __qmljs_to_number(Context *ctx, const Value *value) inline double __qmljs_to_integer(Context *ctx, const Value *value) { + if (value->isInteger()) + return value->int_32; const double number = __qmljs_to_number(ctx, value); - if (isnan(number)) + if (qIsNaN(number)) return +0; else if (! number || isinf(number)) return number; @@ -705,13 +715,15 @@ inline double __qmljs_to_integer(Context *ctx, const Value *value) inline int __qmljs_to_int32(Context *ctx, const Value *value) { + if (value->isInteger()) + return value->int_32; double number = __qmljs_to_number(ctx, value); if ((number >= -2147483648.0 && number < 2147483648.0)) { return static_cast(number); } - if (! number || isnan(number) || isinf(number)) + if (! number || qIsNaN(number) || isinf(number)) return 0; double D32 = 4294967296.0; @@ -732,8 +744,11 @@ inline int __qmljs_to_int32(Context *ctx, const Value *value) inline unsigned __qmljs_to_uint32(Context *ctx, const Value *value) { + if (value->isInteger()) + return (unsigned) value->int_32; + double number = __qmljs_to_number(ctx, value); - if (! number || isnan(number) || isinf(number)) + if (! number || qIsNaN(number) || isinf(number)) return +0; double sign = (number < 0) ? -1.0 : 1.0; @@ -751,7 +766,7 @@ inline unsigned __qmljs_to_uint32(Context *ctx, const Value *value) inline unsigned short __qmljs_to_uint16(Context *ctx, const Value *value) { double number = __qmljs_to_number(ctx, value); - if (! number || isnan(number) || isinf(number)) + if (! number || qIsNaN(number) || isinf(number)) return +0; double sign = (number < 0) ? -1.0 : 1.0; @@ -793,9 +808,9 @@ inline void __qmljs_to_string(Context *ctx, Value *result, const Value *value) __qmljs_throw_type_error(ctx, result); break; } -// case Value::Integer_Type: -// __qmljs_string_from_number(ctx, result, value->data); -// break; + case Value::Integer_Type: + __qmljs_string_from_number(ctx, result, value->int_32); + break; default: // double __qmljs_string_from_number(ctx, result, value->doubleValue()); break; @@ -819,10 +834,10 @@ inline void __qmljs_to_object(Context *ctx, Value *result, const Value *value) case Value::Object_Type: *result = *value; break; -// case Value::Integer_Type: -// __qmljs_new_number_object(ctx, result, value->data); -// break; - default: + case Value::Integer_Type: + __qmljs_new_number_object(ctx, result, value->int_32); + break; + default: // double __qmljs_new_number_object(ctx, result, value->doubleValue()); break; } @@ -853,7 +868,7 @@ inline void __qmljs_default_value(Context *ctx, Value *result, const Value *valu if (value->isObject()) __qmljs_object_default_value(ctx, result, value, typeHint); else - __qmljs_init_undefined(result); + *result = Value::undefinedValue(); } @@ -879,7 +894,7 @@ inline void __qmljs_typeof(Context *ctx, Value *result, const Value *value) else __qmljs_string_literal_object(ctx, result); // ### implementation-defined break; -// case Value::Integer_Type: + case Value::Integer_Type: case Value::Double_Type: __qmljs_string_literal_number(ctx, result); break; @@ -987,10 +1002,28 @@ inline void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value) __qmljs_ushr(ctx, result, result, value); } +static inline Value add_int32(int a, int b) +{ + quint8 overflow; + + asm ("addl %1, %2\n" + "seto %0" + : "=r" (overflow) + : "r" (b), "r" (a) + : + ); + if (!overflow) + return Value::fromInt32(a); + return Value::fromDouble((double)a * b); +} + inline void __qmljs_add(Context *ctx, Value *result, const Value *left, const Value *right) { + if (left->isInteger() && right->isInteger()) + *result = add_int32(left->integerValue(), right->integerValue()); + if (left->isNumber() && right->isNumber()) - __qmljs_init_number(result, left->doubleValue() + right->doubleValue()); + *result = Value::fromDouble(left->asDouble() + right->asDouble()); else __qmljs_add_helper(ctx, result, left, right); } @@ -1046,100 +1079,104 @@ inline void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const V inline void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Value *right) { + if (left->isInteger() && right->isInteger()) + *result = Value::fromBoolean(left->integerValue() > right->integerValue()); if (left->isNumber() && right->isNumber()) { - __qmljs_init_boolean(result, left->doubleValue() > right->doubleValue()); + *result = Value::fromBoolean(left->asDouble() > right->asDouble()); } else { __qmljs_compare(ctx, result, left, right, false); if (result->isUndefined()) - __qmljs_init_boolean(result, false); + *result = Value::fromBoolean(false); } } inline void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Value *right) { + if (left->isInteger() && right->isInteger()) + *result = Value::fromBoolean(left->integerValue() < right->integerValue()); if (left->isNumber() && right->isNumber()) { - __qmljs_init_boolean(result, left->doubleValue() < right->doubleValue()); + *result = Value::fromBoolean(left->asDouble() < right->asDouble()); } else { __qmljs_compare(ctx, result, left, right, true); if (result->isUndefined()) - __qmljs_init_boolean(result, false); + *result = Value::fromBoolean(false); } } inline void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Value *right) { + if (left->isInteger() && right->isInteger()) + *result = Value::fromBoolean(left->integerValue() >= right->integerValue()); if (left->isNumber() && right->isNumber()) { - __qmljs_init_boolean(result, left->doubleValue() >= right->doubleValue()); + *result = Value::fromBoolean(left->asDouble() >= right->asDouble()); } else { __qmljs_compare(ctx, result, right, left, false); bool r = ! (result->isUndefined() || (result->isBoolean() && result->booleanValue())); - __qmljs_init_boolean(result, r); + *result = Value::fromBoolean(r); } } inline void __qmljs_le(Context *ctx, Value *result, const Value *left, const Value *right) { + if (left->isInteger() && right->isInteger()) + *result = Value::fromBoolean(left->integerValue() <= right->integerValue()); if (left->isNumber() && right->isNumber()) { - __qmljs_init_boolean(result, left->doubleValue() <= right->doubleValue()); + *result = Value::fromBoolean(left->asDouble() <= right->asDouble()); } else { __qmljs_compare(ctx, result, right, left, true); bool r = ! (result->isUndefined() || (result->isBoolean() && result->booleanValue())); - __qmljs_init_boolean(result, r); + *result = Value::fromBoolean(r); } } inline void __qmljs_eq(Context *ctx, Value *result, const Value *left, const Value *right) { + if (left->val == right->val) + *result = Value::fromBoolean(true); if (left->isNumber() && right->isNumber()) { - __qmljs_init_boolean(result, left->doubleValue() == right->doubleValue()); + *result = Value::fromBoolean(left->asDouble() == right->asDouble()); } else if (left->isString() && right->isString()) { - __qmljs_init_boolean(result, __qmljs_string_equal(ctx, left->stringValue(), right->stringValue())); + *result = Value::fromBoolean(__qmljs_string_equal(ctx, left->stringValue(), right->stringValue())); } else { bool r = __qmljs_equal(ctx, left, right); - __qmljs_init_boolean(result, r); + *result = Value::fromBoolean(r); } } inline void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *right) { - if (left->isNumber() && right->isNumber()) { - __qmljs_init_boolean(result, left->doubleValue() != right->doubleValue()); - } else if (left->isString() && right->isString()) { - __qmljs_init_boolean(result, !__qmljs_string_equal(ctx, left->stringValue(), right->stringValue())); - } else { - bool r = ! __qmljs_equal(ctx, left, right); - __qmljs_init_boolean(result, r); - } + __qmljs_eq(ctx, result, left, right); + result->b = !result->b; } inline void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right) { bool r = __qmljs_strict_equal(ctx, left, right); - __qmljs_init_boolean(result, r); + *result = Value::fromBoolean(r); } inline void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *right) { bool r = ! __qmljs_strict_equal(ctx, left, right); - __qmljs_init_boolean(result, r); + *result = Value::fromBoolean(r); } inline bool __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_gt(ctx, &v, left, right); - return __qmljs_to_boolean(ctx, &v); + return v.booleanValue(); } inline bool __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_lt(ctx, &v, left, right); - return __qmljs_to_boolean(ctx, &v); + return v.booleanValue(); } inline bool __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 93775b249a..e86132f408 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1022,7 +1022,7 @@ void NumberPrototype::method_toString(Context *ctx) return; } - double num = thisObject->value.doubleValue(); + double num = thisObject->value.asDouble(); if (qIsNaN(num)) { ctx->result = Value::fromString(ctx, QStringLiteral("NaN")); return; @@ -1101,7 +1101,7 @@ void NumberPrototype::method_toFixed(Context *ctx) if (qIsNaN(fdigits)) fdigits = 0; - double v = thisObject->value.doubleValue(); + double v = thisObject->value.asDouble(); QString str; if (qIsNaN(v)) str = QString::fromLatin1("NaN"); @@ -1123,7 +1123,7 @@ void NumberPrototype::method_toExponential(Context *ctx) if (ctx->argumentCount > 0) fdigits = ctx->argument(0).toInteger(ctx); - QString z = QString::number(thisObject->value.doubleValue(), 'e', int (fdigits)); + QString z = QString::number(thisObject->value.asDouble(), 'e', int (fdigits)); ctx->result = Value::fromString(ctx, z); } else { ctx->throwTypeError(); @@ -1138,7 +1138,7 @@ void NumberPrototype::method_toPrecision(Context *ctx) if (ctx->argumentCount > 0) fdigits = ctx->argument(0).toInteger(ctx); - ctx->result = Value::fromString(ctx, QString::number(thisObject->value.doubleValue(), 'g', int (fdigits))); + ctx->result = Value::fromString(ctx, QString::number(thisObject->value.asDouble(), 'g', int (fdigits))); } else { ctx->throwTypeError(); } @@ -1208,7 +1208,7 @@ void ArrayCtor::call(Context *ctx) { Array value; if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { - double size = ctx->argument(0).doubleValue(); + double size = ctx->argument(0).asDouble(); quint32 isize = Value::toUInt32(size); if (size != double(isize)) { @@ -1906,7 +1906,7 @@ void DatePrototype::init(Context *ctx, const Value &ctor) double DatePrototype::getThisDate(Context *ctx) { if (DateObject *thisObject = ctx->thisObject.asDateObject()) - return thisObject->value.doubleValue(); + return thisObject->value.asDouble(); else { ctx->throwTypeError(); return 0; @@ -2157,7 +2157,7 @@ void DatePrototype::method_setTime(Context *ctx) void DatePrototype::method_setMilliseconds(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.doubleValue()); + double t = LocalTime(self->value.asDouble()); double ms = ctx->argument(0).toNumber(ctx); self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); ctx->result = self->value; @@ -2169,7 +2169,7 @@ void DatePrototype::method_setMilliseconds(Context *ctx) void DatePrototype::method_setUTCMilliseconds(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.doubleValue(); + double t = self->value.asDouble(); double ms = ctx->argument(0).toNumber(ctx); self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); ctx->result = self->value; @@ -2181,7 +2181,7 @@ void DatePrototype::method_setUTCMilliseconds(Context *ctx) void DatePrototype::method_setSeconds(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.doubleValue()); + double t = LocalTime(self->value.asDouble()); double sec = ctx->argument(0).toNumber(ctx); double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); @@ -2195,7 +2195,7 @@ void DatePrototype::method_setSeconds(Context *ctx) void DatePrototype::method_setUTCSeconds(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.doubleValue(); + double t = self->value.asDouble(); double sec = ctx->argument(0).toNumber(ctx); double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); @@ -2209,7 +2209,7 @@ void DatePrototype::method_setUTCSeconds(Context *ctx) void DatePrototype::method_setMinutes(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.doubleValue()); + double t = LocalTime(self->value.asDouble()); double min = ctx->argument(0).toNumber(ctx); double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); @@ -2224,7 +2224,7 @@ void DatePrototype::method_setMinutes(Context *ctx) void DatePrototype::method_setUTCMinutes(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.doubleValue(); + double t = self->value.asDouble(); double min = ctx->argument(0).toNumber(ctx); double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); @@ -2239,7 +2239,7 @@ void DatePrototype::method_setUTCMinutes(Context *ctx) void DatePrototype::method_setHours(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.doubleValue()); + double t = LocalTime(self->value.asDouble()); double hour = ctx->argument(0).toNumber(ctx); double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); @@ -2255,7 +2255,7 @@ void DatePrototype::method_setHours(Context *ctx) void DatePrototype::method_setUTCHours(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.doubleValue(); + double t = self->value.asDouble(); double hour = ctx->argument(0).toNumber(ctx); double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); @@ -2271,7 +2271,7 @@ void DatePrototype::method_setUTCHours(Context *ctx) void DatePrototype::method_setDate(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.doubleValue()); + double t = LocalTime(self->value.asDouble()); double date = ctx->argument(0).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); self->value.setDouble(t); @@ -2284,7 +2284,7 @@ void DatePrototype::method_setDate(Context *ctx) void DatePrototype::method_setUTCDate(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.doubleValue(); + double t = self->value.asDouble(); double date = ctx->argument(0).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); self->value.setDouble(t); @@ -2297,7 +2297,7 @@ void DatePrototype::method_setUTCDate(Context *ctx) void DatePrototype::method_setMonth(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.doubleValue()); + double t = LocalTime(self->value.asDouble()); double month = ctx->argument(0).toNumber(ctx); double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); @@ -2311,7 +2311,7 @@ void DatePrototype::method_setMonth(Context *ctx) void DatePrototype::method_setUTCMonth(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.doubleValue(); + double t = self->value.asDouble(); double month = ctx->argument(0).toNumber(ctx); double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); @@ -2325,7 +2325,7 @@ void DatePrototype::method_setUTCMonth(Context *ctx) void DatePrototype::method_setYear(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.doubleValue(); + double t = self->value.asDouble(); if (qIsNaN(t)) t = 0; else @@ -2351,7 +2351,7 @@ void DatePrototype::method_setYear(Context *ctx) void DatePrototype::method_setUTCFullYear(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.doubleValue(); + double t = self->value.asDouble(); double year = ctx->argument(0).toNumber(ctx); double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); @@ -2366,7 +2366,7 @@ void DatePrototype::method_setUTCFullYear(Context *ctx) void DatePrototype::method_setFullYear(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.doubleValue()); + double t = LocalTime(self->value.asDouble()); double year = ctx->argument(0).toNumber(ctx); double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); @@ -2381,7 +2381,7 @@ void DatePrototype::method_setFullYear(Context *ctx) void DatePrototype::method_toUTCString(Context *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.doubleValue(); + double t = self->value.asDouble(); ctx->result = Value::fromString(ctx, ToUTCString(t)); } } -- cgit v1.2.3 From 6961f98a85377632b4a84d50458bb1f4de399150 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 11 Oct 2012 16:50:37 +0200 Subject: Remove __qmljs_init_(number|boolean) Change-Id: Iae59dfea39e44931bb2f7a6436bd2f1491bbf843 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 4 ++-- qmljs_runtime.cpp | 33 ++++++++++++--------------------- qmljs_runtime.h | 54 +++++++++++++++++++++--------------------------------- qv4ecmaobjects.cpp | 14 +++++++------- 4 files changed, 42 insertions(+), 63 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 36ef7f7895..7fc3fc5575 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -27,12 +27,12 @@ void __qmljs_llvm_init_null(Value *result) void __qmljs_llvm_init_boolean(Value *result, bool value) { - __qmljs_init_boolean(result, value); + *result = Value::fromBoolean(value); } void __qmljs_llvm_init_number(Value *result, double value) { - __qmljs_init_number(result, value); + *result = Value::fromDouble(value); } void __qmljs_llvm_init_string(Context *ctx, Value *result, const char *str) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 9cdf037017..a947e360dd 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -470,7 +470,7 @@ void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Va } else { double x = __qmljs_to_number(ctx, &pleft); double y = __qmljs_to_number(ctx, &pright); - __qmljs_init_number(result, x + y); + *result = Value::fromDouble(x + y); } } @@ -814,15 +814,13 @@ void __qmljs_new_object(Context *ctx, Value *result) void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean) { - Value value; - __qmljs_init_boolean(&value, boolean); + Value value = Value::fromBoolean(boolean); __qmljs_init_object(result, ctx->engine->newBooleanObject(value)); } void __qmljs_new_number_object(Context *ctx, Value *result, double number) { - Value value; - __qmljs_init_number(&value, number); + Value value = Value::fromDouble(number); __qmljs_init_object(result, ctx->engine->newNumberObject(value)); } @@ -838,18 +836,16 @@ void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *valu object->objectValue()->setProperty(ctx, name, *value, /*flags*/ 0); } -void __qmljs_set_property_boolean(Context *ctx, Value *object, String *name, bool number) +void __qmljs_set_property_boolean(Context *ctx, Value *object, String *name, bool b) { - Value value; - __qmljs_init_boolean(&value, number); + Value value = Value::fromBoolean(b); object->objectValue()->setProperty(ctx, name, value, /*flag*/ 0); } void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double number) { Q_UNUSED(ctx); - Value value; - __qmljs_init_number(&value, number); + Value value = Value::fromDouble(number); object->objectValue()->setProperty(ctx, name, value, /*flag*/ 0); } @@ -935,8 +931,7 @@ void __qmljs_copy_activation_property(Context *ctx, String *name, String *other) void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool b) { - Value value; - __qmljs_init_boolean(&value, b); + Value value = Value::fromBoolean(b); __qmljs_set_activation_property(ctx, name, &value); } @@ -965,7 +960,7 @@ void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *na if (object->isObject()) { *result = object->property(ctx, name); } else if (object->isString() && name->isEqualTo(ctx->engine->id_length)) { - __qmljs_init_number(result, object->stringValue()->toQString().length()); + *result = Value::fromDouble(object->stringValue()->toQString().length()); } else { Value o; __qmljs_to_object(ctx, &o, object); @@ -1051,20 +1046,16 @@ bool __qmljs_equal(Context *ctx, const Value *x, const Value *y) } else if (x->isUndefined() && y->isNull()) { return true; } else if (x->isNumber() && y->isString()) { - Value ny; - __qmljs_init_number(&ny, __qmljs_to_number(ctx, y)); + Value ny = Value::fromDouble(__qmljs_to_number(ctx, y)); return __qmljs_equal(ctx, x, &ny); } else if (x->isString() && y->isNumber()) { - Value nx; - __qmljs_init_number(&nx, __qmljs_to_number(ctx, x)); + Value nx = Value::fromDouble(__qmljs_to_number(ctx, x)); return __qmljs_equal(ctx, &nx, y); } else if (x->isBoolean()) { - Value nx; - __qmljs_init_number(&nx, (double) x->booleanValue()); + Value nx = Value::fromDouble((double) x->booleanValue()); return __qmljs_equal(ctx, &nx, y); } else if (y->isBoolean()) { - Value ny; - __qmljs_init_number(&ny, (double) y->booleanValue()); + Value ny = Value::fromDouble((double) y->booleanValue()); return __qmljs_equal(ctx, x, &ny); } else if ((x->isNumber() || x->isString()) && y->isObject()) { Value py; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 6094c37283..6505f89f75 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -62,8 +62,6 @@ void __qmljs_builtin_throw(Context *context, Value *result, Value *args, int arg void __qmljs_builtin_rethrow(Context *context, Value *result, Value *args, int argc); // constructors -void __qmljs_init_boolean(Value *result, bool value); -void __qmljs_init_number(Value *result, double number); void __qmljs_init_string(Value *result, String *string); void __qmljs_init_object(Value *result, Object *object); void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos); @@ -315,13 +313,13 @@ template <> struct ValueBase<4> : public ValueData return val; } - static Value undefinedValue(); - static Value nullValue(); - static Value fromBoolean(bool b); - static Value fromDouble(double d); - static Value fromInt32(int i); - static Value fromString(String *s); - static Value fromObject(Object *o); + static inline Value undefinedValue(); + static inline Value nullValue(); + static inline Value fromBoolean(bool b); + static inline Value fromDouble(double d); + static inline Value fromInt32(int i); + static inline Value fromString(String *s); + static inline Value fromObject(Object *o); }; template <> struct ValueBase<8> : public ValueData @@ -626,16 +624,6 @@ struct Context { extern "C" { // constructors -inline void __qmljs_init_boolean(Value *result, bool value) -{ - *result = Value::fromBoolean(value); -} - -// ### remove me -inline void __qmljs_init_number(Value *result, double value) -{ - *result = Value::fromDouble(value); -} inline void __qmljs_init_string(Value *result, String *value) { @@ -904,25 +892,25 @@ inline void __qmljs_typeof(Context *ctx, Value *result, const Value *value) inline void __qmljs_uplus(Context *ctx, Value *result, const Value *value) { double n = __qmljs_to_number(ctx, value); - __qmljs_init_number(result, n); + *result = Value::fromDouble(n); } inline void __qmljs_uminus(Context *ctx, Value *result, const Value *value) { double n = __qmljs_to_number(ctx, value); - __qmljs_init_number(result, -n); + *result = Value::fromDouble(-n); } inline void __qmljs_compl(Context *ctx, Value *result, const Value *value) { int n = __qmljs_to_int32(ctx, value); - __qmljs_init_number(result, ~n); + *result = Value::fromDouble(~n); } inline void __qmljs_not(Context *ctx, Value *result, const Value *value) { bool b = __qmljs_to_boolean(ctx, value); - __qmljs_init_number(result, !b); + *result = Value::fromDouble(!b); } // binary operators @@ -930,21 +918,21 @@ inline void __qmljs_bit_or(Context *ctx, Value *result, const Value *left, const { int lval = __qmljs_to_int32(ctx, left); int rval = __qmljs_to_int32(ctx, right); - __qmljs_init_number(result, lval | rval); + *result = Value::fromDouble(lval | rval); } inline void __qmljs_bit_xor(Context *ctx, Value *result, const Value *left, const Value *right) { int lval = __qmljs_to_int32(ctx, left); int rval = __qmljs_to_int32(ctx, right); - __qmljs_init_number(result, lval ^ rval); + *result = Value::fromDouble(lval ^ rval); } inline void __qmljs_bit_and(Context *ctx, Value *result, const Value *left, const Value *right) { int lval = __qmljs_to_int32(ctx, left); int rval = __qmljs_to_int32(ctx, right); - __qmljs_init_number(result, lval & rval); + *result = Value::fromDouble(lval & rval); } inline void __qmljs_inplace_bit_and(Context *ctx, Value *result, Value *value) @@ -1032,49 +1020,49 @@ inline void __qmljs_sub(Context *ctx, Value *result, const Value *left, const Va { double lval = __qmljs_to_number(ctx, left); double rval = __qmljs_to_number(ctx, right); - __qmljs_init_number(result, lval - rval); + *result = Value::fromDouble(lval - rval); } inline void __qmljs_mul(Context *ctx, Value *result, const Value *left, const Value *right) { double lval = __qmljs_to_number(ctx, left); double rval = __qmljs_to_number(ctx, right); - __qmljs_init_number(result, lval * rval); + *result = Value::fromDouble(lval * rval); } inline void __qmljs_div(Context *ctx, Value *result, const Value *left, const Value *right) { double lval = __qmljs_to_number(ctx, left); double rval = __qmljs_to_number(ctx, right); - __qmljs_init_number(result, lval / rval); + *result = Value::fromDouble(lval / rval); } inline void __qmljs_mod(Context *ctx, Value *result, const Value *left, const Value *right) { double lval = __qmljs_to_number(ctx, left); double rval = __qmljs_to_number(ctx, right); - __qmljs_init_number(result, fmod(lval, rval)); + *result = Value::fromDouble(fmod(lval, rval)); } inline void __qmljs_shl(Context *ctx, Value *result, const Value *left, const Value *right) { int lval = __qmljs_to_int32(ctx, left); unsigned rval = __qmljs_to_uint32(ctx, right); - __qmljs_init_number(result, lval << rval); + *result = Value::fromDouble(lval << rval); } inline void __qmljs_shr(Context *ctx, Value *result, const Value *left, const Value *right) { int lval = __qmljs_to_int32(ctx, left); unsigned rval = __qmljs_to_uint32(ctx, right); - __qmljs_init_number(result, lval >> rval); + *result = Value::fromDouble(lval >> rval); } inline void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const Value *right) { unsigned lval = __qmljs_to_uint32(ctx, left); unsigned rval = __qmljs_to_uint32(ctx, right); - __qmljs_init_number(result, lval >> rval); + *result = Value::fromDouble(lval >> rval); } inline void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Value *right) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index e86132f408..bf6574cdf7 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -759,7 +759,7 @@ void StringPrototype::method_charCodeAt(Context *ctx) if (pos >= 0 && pos < str.length()) result = str.at(pos).unicode(); - __qmljs_init_number(&ctx->result, result); + ctx->result = Value::fromDouble(result); } void StringPrototype::method_concat(Context *ctx) @@ -792,7 +792,7 @@ void StringPrototype::method_indexOf(Context *ctx) if (! value.isEmpty()) index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); - __qmljs_init_number(&ctx->result, index); + ctx->result = Value::fromDouble(index); } void StringPrototype::method_lastIndexOf(Context *ctx) @@ -817,14 +817,14 @@ void StringPrototype::method_lastIndexOf(Context *ctx) if (!searchString.isEmpty() && pos == value.length()) --pos; int index = value.lastIndexOf(searchString, pos); - __qmljs_init_number(&ctx->result, index); + ctx->result = Value::fromDouble(index); } void StringPrototype::method_localeCompare(Context *ctx) { const QString value = getThisString(ctx); const QString that = ctx->argument(0).toString(ctx)->toQString(); - __qmljs_init_number(&ctx->result, QString::localeAwareCompare(value, that)); + ctx->result = Value::fromDouble(QString::localeAwareCompare(value, that)); } void StringPrototype::method_match(Context *ctx) @@ -982,7 +982,7 @@ void NumberCtor::construct(Context *ctx) void NumberCtor::call(Context *ctx) { double value = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; - __qmljs_init_number(&ctx->result, value); + ctx->result = Value::fromDouble(value); } void NumberPrototype::init(Context *ctx, const Value &ctor) @@ -1160,8 +1160,8 @@ void BooleanCtor::construct(Context *ctx) void BooleanCtor::call(Context *ctx) { - double value = ctx->argumentCount ? ctx->argument(0).toBoolean(ctx) : 0; - __qmljs_init_boolean(&ctx->result, value); + bool value = ctx->argumentCount ? ctx->argument(0).toBoolean(ctx) : 0; + ctx->result = Value::fromBoolean(value); } void BooleanPrototype::init(Context *ctx, const Value &ctor) -- cgit v1.2.3 From bd0ff1b50d1dcb30c58d0d8c4232f89ddd63224c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 11 Oct 2012 21:19:08 +0200 Subject: Remove almost all uses of __qmljs_init_string Change-Id: I88130ec39958c75b70d898a352423908ac2ef1d4 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 2 +- qmljs_runtime.cpp | 31 ++++++++++++++----------------- qv4ecmaobjects.cpp | 2 +- qv4isel_masm.cpp | 1 + 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 7fc3fc5575..f1767b7c62 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -37,7 +37,7 @@ void __qmljs_llvm_init_number(Value *result, double value) void __qmljs_llvm_init_string(Context *ctx, Value *result, const char *str) { - __qmljs_init_string(result, __qmljs_string_from_utf8(ctx, str)); + *result = Value::fromString(__qmljs_string_from_utf8(ctx, str)); } void __qmljs_llvm_init_native_function(Context *ctx, Value *result, void (*code)(Context *)) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index a947e360dd..f64ffba735 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -371,47 +371,47 @@ void __qmljs_init_native_function(Context *ctx, Value *result, void (*code)(Cont void __qmljs_string_literal_undefined(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("undefined"))); + *result = Value::fromString(ctx->engine->identifier(QStringLiteral("undefined"))); } void __qmljs_string_literal_null(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("null"))); + *result = Value::fromString(ctx->engine->identifier(QStringLiteral("null"))); } void __qmljs_string_literal_true(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("true"))); + *result = Value::fromString(ctx->engine->identifier(QStringLiteral("true"))); } void __qmljs_string_literal_false(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("false"))); + *result = Value::fromString(ctx->engine->identifier(QStringLiteral("false"))); } void __qmljs_string_literal_object(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("object"))); + *result = Value::fromString(ctx->engine->identifier(QStringLiteral("object"))); } void __qmljs_string_literal_boolean(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("boolean"))); + *result = Value::fromString(ctx->engine->identifier(QStringLiteral("boolean"))); } void __qmljs_string_literal_number(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("number"))); + *result = Value::fromString(ctx->engine->identifier(QStringLiteral("number"))); } void __qmljs_string_literal_string(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("string"))); + *result = Value::fromString(ctx->engine->identifier(QStringLiteral("string"))); } void __qmljs_string_literal_function(Context *ctx, Value *result) { - __qmljs_init_string(result, ctx->engine->identifier(QStringLiteral("function"))); + *result = Value::fromString(ctx->engine->identifier(QStringLiteral("function"))); } void __qmljs_delete_subscript(Context *ctx, Value *result, Value *base, Value *index) @@ -466,7 +466,7 @@ void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Va if (!pright.isString()) __qmljs_to_string(ctx, &pright, &pright); String *string = __qmljs_string_concat(ctx, pleft.stringValue(), pright.stringValue()); - __qmljs_init_string(result, string); + *result = Value::fromString(string); } else { double x = __qmljs_to_number(ctx, &pleft); double y = __qmljs_to_number(ctx, &pright); @@ -736,7 +736,7 @@ double __qmljs_string_to_number(Context *, String *string) void __qmljs_string_from_number(Context *ctx, Value *result, double number) { String *string = ctx->engine->newString(numberToString(number, 10)); - __qmljs_init_string(result, string); + *result = Value::fromString(string); } bool __qmljs_string_compare(Context *, String *left, String *right) @@ -826,8 +826,7 @@ void __qmljs_new_number_object(Context *ctx, Value *result, double number) void __qmljs_new_string_object(Context *ctx, Value *result, String *string) { - Value value; - __qmljs_init_string(&value, string); + Value value = Value::fromString(string); __qmljs_init_object(result, ctx->engine->newStringObject(value)); } @@ -852,8 +851,7 @@ void __qmljs_set_property_number(Context *ctx, Value *object, String *name, doub void __qmljs_set_property_string(Context *ctx, Value *object, String *name, String *s) { Q_UNUSED(ctx); - Value value; - __qmljs_init_string(&value, s); + Value value = Value::fromString(s); object->objectValue()->setProperty(ctx, name, value, /*flag*/ 0); } @@ -943,8 +941,7 @@ void __qmljs_set_activation_property_number(Context *ctx, String *name, double n void __qmljs_set_activation_property_string(Context *ctx, String *name, String *string) { - Value value; - __qmljs_init_string(&value, string); + Value value = Value::fromString(string); __qmljs_set_activation_property(ctx, name, &value); } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index bf6574cdf7..116daf9334 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -671,7 +671,7 @@ void StringCtor::call(Context *ctx) { const Value arg = ctx->argument(0); if (arg.is(Value::Undefined_Type)) - __qmljs_init_string(&ctx->result, ctx->engine->newString(QString())); + ctx->result = Value::fromString(ctx->engine->newString(QString())); else __qmljs_to_string(ctx, &ctx->result, &arg); } diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index ec526a3f1e..e6334970fd 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -310,6 +310,7 @@ void InstructionSelection::visitMove(IR::Move *s) copyValue(t, t2); return; } else if (IR::String *str = s->source->asString()) { + // ### inline generateFunctionCall(__qmljs_init_string, t, _engine->newString(*str->value)); return; } else if (IR::Closure *clos = s->source->asClosure()) { -- cgit v1.2.3 From 3c02228bf36cceef1499c2b8bdf2477daf1e2c0b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 12 Oct 2012 08:48:33 +0200 Subject: Handle Integer_Type in a switch statement Change-Id: I8e00636f0896cec7d18d645dd70db8a100bf5560 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index f64ffba735..e4295a5554 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -1027,6 +1027,8 @@ bool __qmljs_equal(Context *ctx, const Value *x, const Value *y) case Value::Boolean_Type: return x->booleanValue() == y->booleanValue(); break; + case Value::Integer_Type: + return x->integerValue() == y->integerValue(); case Value::String_Type: return __qmljs_string_equal(ctx, x->stringValue(), y->stringValue()); case Value::Object_Type: -- cgit v1.2.3 From f17248441dbdfb6b0d5986cec2fb8fbfc3f9ef56 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 12 Oct 2012 08:51:02 +0200 Subject: Send all debug output to stderr qDebug() was used for part of the logging, which uses stderr. Send everything else there as well. Change-Id: I363dff3d2e4dd6f95bf0347f1df589ae2528fd32 Reviewed-by: Simon Hausmann --- masm/stubs/WTFStubs.cpp | 2 +- qv4isel_masm.cpp | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/masm/stubs/WTFStubs.cpp b/masm/stubs/WTFStubs.cpp index 2c642ac986..e4009b8315 100644 --- a/masm/stubs/WTFStubs.cpp +++ b/masm/stubs/WTFStubs.cpp @@ -27,7 +27,7 @@ uint32_t cryptographicallyRandomNumber() return 0; } -static FILE* s_dataFile = stdout; +static FILE* s_dataFile = stderr; void setDataFile(FILE* f) { diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index e6334970fd..98b9a71f50 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -19,7 +19,7 @@ using namespace QQmlJS::MASM; using namespace QQmlJS::VM; namespace { -QTextStream qout(stdout, QIODevice::WriteOnly); +QTextStream qout(stderr, QIODevice::WriteOnly); } static void printDisassmbleOutputWithCalls(const char* output, const QHash& functions) @@ -31,7 +31,7 @@ static void printDisassmbleOutputWithCalls(const char* output, const QHashcodeRef = linkBuffer.finalizeCodeWithDisassembly("operator()(IR::Function*)"); + QByteArray name = _function->name->toUtf8(); + if (name.startsWith('%')) + name.prepend('%'); + _function->codeRef = linkBuffer.finalizeCodeWithDisassembly(name.data()); - WTF::setDataFile(stdout); + WTF::setDataFile(stderr); fclose(disasmStream); #if CPU(X86) || CPU(X86_64) printDisassmbleOutputWithCalls(disasmOutput, functions); -- cgit v1.2.3 From 67c0513d2550ceec53385f1b738a996ce935d06f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 12 Oct 2012 08:52:51 +0200 Subject: Store booleans as 32 bit integers The old code was giving valgrind warnings, because we loaded the boolean (1 byte) as a 32 bit integer. Instead simply store all booleans as 32 bit ints to avoid the warnings. Change-Id: I46c7f9fc9d8dfe52afd9bab2dbab8923aaa630f8 Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 17 ++++++++--------- qv4isel_masm.cpp | 4 ++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 6505f89f75..2d205ebf74 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -247,7 +247,6 @@ struct ValueData { union { uint uint_32; int int_32; - bool b; }; #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN uint tag; @@ -286,7 +285,7 @@ template <> struct ValueBase<4> : public ValueData } bool booleanValue() const { - return b; + return int_32; } double doubleValue() const { return dbl; @@ -352,7 +351,7 @@ template <> struct ValueBase<8> : public ValueData } bool booleanValue() const { - return b; + return int_32; } double doubleValue() const { return dbl; @@ -463,7 +462,7 @@ inline Value ValueBase<4>::fromBoolean(bool b) { Value v; v.tag = Boolean_Type; - v.b = b; + v.int_32 = b; return v; } @@ -517,7 +516,7 @@ inline Value ValueBase<8>::fromBoolean(bool b) { Value v; v.tag = Boolean_Type; - v.b = b; + v.int_32 = b; return v; } @@ -555,7 +554,7 @@ inline Value ValueBase<8>::fromObject(Object *o) template struct ValueOffsetHelper; template <> struct ValueOffsetHelper { - enum { DataOffset = offsetof(ValueData, b) }; + enum { DataOffset = offsetof(ValueData, int_32) }; }; template <> struct ValueOffsetHelper @@ -799,8 +798,8 @@ inline void __qmljs_to_string(Context *ctx, Value *result, const Value *value) case Value::Integer_Type: __qmljs_string_from_number(ctx, result, value->int_32); break; - default: // double - __qmljs_string_from_number(ctx, result, value->doubleValue()); + default: // number + __qmljs_string_from_number(ctx, result, value->asDouble()); break; } // switch @@ -1138,7 +1137,7 @@ inline void __qmljs_eq(Context *ctx, Value *result, const Value *left, const Val inline void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *right) { __qmljs_eq(ctx, result, left, right); - result->b = !result->b; + result->int_32 = !(bool)result->int_32; } inline void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 98b9a71f50..0623d9aea8 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -548,7 +548,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) Jump booleanConversion = branch32(NotEqual, tag, TrustedImm32(VM::Value::Boolean_Type)); Address data = temp; - data.offset += offsetof(VM::ValueData, b); + data.offset += offsetof(VM::ValueData, int_32); load32(data, Gpr0); Jump testBoolean = jump(); @@ -559,7 +559,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) } testBoolean.link(this); - Jump target = branch32(Equal, Gpr0, TrustedImm32(1)); + Jump target = branch32(NotEqual, Gpr0, TrustedImm32(0)); _patches[s->iftrue].append(target); jumpToBlock(s->iffalse); -- cgit v1.2.3 From f9b181e4ea83c9bc8380a5bc03d2635d60c97683 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 12 Oct 2012 09:23:17 +0200 Subject: Clean up arithmetic operation calls Don't repeat calls to generateFunctionCall but use a helper macro to get the name. Change-Id: I394b1980cbd67f3ca94fc41402de2d5b99c45016 Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 188 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 112 insertions(+), 76 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 0623d9aea8..ea1e5b0e13 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -266,6 +266,9 @@ void InstructionSelection::visitLeave(IR::Leave *) void InstructionSelection::visitMove(IR::Move *s) { + #define setOp(op, opName, operation) \ + do { op = operation; opName = isel_stringIfy(operation); } while (0) + if (s->op == IR::OpInvalid) { if (IR::Name *n = s->target->asName()) { String *propertyName = identifier(*n->id); @@ -345,20 +348,28 @@ void InstructionSelection::visitMove(IR::Move *s) return; } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { + void (*op)(Context *ctx, Value *result, const Value *value) = 0; + const char *opName = 0; switch (u->op) { case IR::OpIfTrue: assert(!"unreachable"); break; - case IR::OpNot: generateFunctionCall(__qmljs_not, ContextRegister, t, e); break; - case IR::OpUMinus: generateFunctionCall(__qmljs_uminus, ContextRegister, t, e); break; - case IR::OpUPlus: generateFunctionCall(__qmljs_uplus, ContextRegister, t, e); break; - case IR::OpCompl: generateFunctionCall(__qmljs_compl, ContextRegister, t, e); break; + case IR::OpNot: setOp(op, opName, __qmljs_not); break; + case IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break; + case IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break; + case IR::OpCompl: setOp(op, opName, __qmljs_compl); break; default: assert(!"unreachable"); break; } // switch + + if (op) + generateFunctionCallImp(opName, op, ContextRegister, t, e); return; } } else if (IR::Binop *b = s->source->asBinop()) { IR::Temp *l = b->left->asTemp(); IR::Temp *r = b->right->asTemp(); if (l && r) { + void (*op)(Context *, Value *, const Value *, const Value *) = 0; + const char* opName = 0; + switch ((IR::AluOp) b->op) { case IR::OpInvalid: case IR::OpIfTrue: @@ -369,33 +380,37 @@ void InstructionSelection::visitMove(IR::Move *s) assert(!"unreachable"); break; - case IR::OpBitAnd: generateFunctionCall(__qmljs_bit_and, ContextRegister, t, l, r); break; - case IR::OpBitOr: generateFunctionCall(__qmljs_bit_or, ContextRegister, t, l, r); break; - case IR::OpBitXor: generateFunctionCall(__qmljs_bit_xor, ContextRegister, t, l, r); break; - case IR::OpAdd: generateFunctionCall(__qmljs_add, ContextRegister, t, l, r); break; - case IR::OpSub: generateFunctionCall(__qmljs_sub, ContextRegister, t, l, r); break; - case IR::OpMul: generateFunctionCall(__qmljs_mul, ContextRegister, t, l, r); break; - case IR::OpDiv: generateFunctionCall(__qmljs_div, ContextRegister, t, l, r); break; - case IR::OpMod: generateFunctionCall(__qmljs_mod, ContextRegister, t, l, r); break; - case IR::OpLShift: generateFunctionCall(__qmljs_shl, ContextRegister, t, l, r); break; - case IR::OpRShift: generateFunctionCall(__qmljs_shr, ContextRegister, t, l, r); break; - case IR::OpURShift: generateFunctionCall(__qmljs_ushr, ContextRegister, t, l, r); break; - case IR::OpGt: generateFunctionCall(__qmljs_gt, ContextRegister, t, l, r); break; - case IR::OpLt: generateFunctionCall(__qmljs_lt, ContextRegister, t, l, r); break; - case IR::OpGe: generateFunctionCall(__qmljs_ge, ContextRegister, t, l, r); break; - case IR::OpLe: generateFunctionCall(__qmljs_le, ContextRegister, t, l, r); break; - case IR::OpEqual: generateFunctionCall(__qmljs_eq, ContextRegister, t, l, r); break; - case IR::OpNotEqual: generateFunctionCall(__qmljs_ne, ContextRegister, t, l, r); break; - case IR::OpStrictEqual: generateFunctionCall(__qmljs_se, ContextRegister, t, l, r); break; - case IR::OpStrictNotEqual: generateFunctionCall(__qmljs_sne, ContextRegister, t, l, r); break; - case IR::OpInstanceof: generateFunctionCall(__qmljs_instanceof, ContextRegister, t, l, r); break; - case IR::OpIn: generateFunctionCall(__qmljs_in, ContextRegister, t, l, r); break; + case IR::OpBitAnd: setOp(op, opName, __qmljs_bit_and); break; + case IR::OpBitOr: setOp(op, opName, __qmljs_bit_or); break; + case IR::OpBitXor: setOp(op, opName, __qmljs_bit_xor); break; + case IR::OpAdd: setOp(op, opName, __qmljs_add); break; + case IR::OpSub: setOp(op, opName, __qmljs_sub); break; + case IR::OpMul: setOp(op, opName, __qmljs_mul); break; + case IR::OpDiv: setOp(op, opName, __qmljs_div); break; + case IR::OpMod: setOp(op, opName, __qmljs_mod); break; + case IR::OpLShift: setOp(op, opName, __qmljs_shl); break; + case IR::OpRShift: setOp(op, opName, __qmljs_shr); break; + case IR::OpURShift: setOp(op, opName, __qmljs_ushr); break; + case IR::OpGt: setOp(op, opName, __qmljs_gt); break; + case IR::OpLt: setOp(op, opName, __qmljs_lt); break; + case IR::OpGe: setOp(op, opName, __qmljs_ge); break; + case IR::OpLe: setOp(op, opName, __qmljs_le); break; + case IR::OpEqual: setOp(op, opName, __qmljs_eq); break; + case IR::OpNotEqual: setOp(op, opName, __qmljs_ne); break; + case IR::OpStrictEqual: setOp(op, opName, __qmljs_se); break; + case IR::OpStrictNotEqual: setOp(op, opName, __qmljs_sne); break; + case IR::OpInstanceof: setOp(op, opName, __qmljs_instanceof); break; + case IR::OpIn: setOp(op, opName, __qmljs_in); case IR::OpAnd: case IR::OpOr: assert(!"unreachable"); break; } + + if (op) { + generateFunctionCallImp(opName, op, ContextRegister, t, l, r); + } return; } } else if (IR::Call *c = s->source->asCall()) { @@ -433,91 +448,110 @@ void InstructionSelection::visitMove(IR::Move *s) // inplace assignment, e.g. x += 1, ++x, ... if (IR::Temp *t = s->target->asTemp()) { if (IR::Temp *t2 = s->source->asTemp()) { + void (*op)(Context *ctx, Value *result, const Value *left,const Value *right) = 0; + const char *opName = 0; switch (s->op) { - case IR::OpBitAnd: generateFunctionCall(__qmljs_bit_and, ContextRegister, t, t, t2); break; - case IR::OpBitOr: generateFunctionCall(__qmljs_bit_or, ContextRegister, t, t, t2); break; - case IR::OpBitXor: generateFunctionCall(__qmljs_bit_xor, ContextRegister, t, t, t2); break; - case IR::OpAdd: generateFunctionCall(__qmljs_add, ContextRegister, t, t, t2); break; - case IR::OpSub: generateFunctionCall(__qmljs_sub, ContextRegister, t, t, t2); break; - case IR::OpMul: generateFunctionCall(__qmljs_mul, ContextRegister, t, t, t2); break; - case IR::OpDiv: generateFunctionCall(__qmljs_div, ContextRegister, t, t, t2); break; - case IR::OpMod: generateFunctionCall(__qmljs_mod, ContextRegister, t, t, t2); break; - case IR::OpLShift: generateFunctionCall(__qmljs_shl, ContextRegister, t, t, t2); break; - case IR::OpRShift: generateFunctionCall(__qmljs_shr, ContextRegister, t, t, t2); break; - case IR::OpURShift: generateFunctionCall(__qmljs_ushr, ContextRegister, t, t, t2); break; + case IR::OpBitAnd: setOp(op, opName, __qmljs_bit_and); break; + case IR::OpBitOr: setOp(op, opName, __qmljs_bit_or); break; + case IR::OpBitXor: setOp(op, opName, __qmljs_bit_xor); break; + case IR::OpAdd: setOp(op, opName, __qmljs_add); break; + case IR::OpSub: setOp(op, opName, __qmljs_sub); break; + case IR::OpMul: setOp(op, opName, __qmljs_mul); break; + case IR::OpDiv: setOp(op, opName, __qmljs_div); break; + case IR::OpMod: setOp(op, opName, __qmljs_mod); break; + case IR::OpLShift: setOp(op, opName, __qmljs_shl); break; + case IR::OpRShift: setOp(op, opName, __qmljs_shr); break; + case IR::OpURShift: setOp(op, opName, __qmljs_ushr); break; default: Q_UNREACHABLE(); break; } + if (op) + generateFunctionCallImp(opName, op, ContextRegister, t, t, t2); return; } } else if (IR::Name *n = s->target->asName()) { if (IR::Temp *t = s->source->asTemp()) { + void (*op)(Context *ctx, String *name, Value *value) = 0; + const char *opName = 0; switch (s->op) { - case IR::OpBitAnd: generateFunctionCall(__qmljs_inplace_bit_and_name, ContextRegister, identifier(*n->id), t); break; - case IR::OpBitOr: generateFunctionCall(__qmljs_inplace_bit_or_name, ContextRegister, identifier(*n->id), t); break; - case IR::OpBitXor: generateFunctionCall(__qmljs_inplace_bit_xor_name, ContextRegister, identifier(*n->id), t); break; - case IR::OpAdd: generateFunctionCall(__qmljs_inplace_add_name, ContextRegister, identifier(*n->id), t); break; - case IR::OpSub: generateFunctionCall(__qmljs_inplace_sub_name, ContextRegister, identifier(*n->id), t); break; - case IR::OpMul: generateFunctionCall(__qmljs_inplace_mul_name, ContextRegister, identifier(*n->id), t); break; - case IR::OpDiv: generateFunctionCall(__qmljs_inplace_div_name, ContextRegister, identifier(*n->id), t); break; - case IR::OpMod: generateFunctionCall(__qmljs_inplace_mod_name, ContextRegister, identifier(*n->id), t); break; - case IR::OpLShift: generateFunctionCall(__qmljs_inplace_shl_name, ContextRegister, identifier(*n->id), t); break; - case IR::OpRShift: generateFunctionCall(__qmljs_inplace_shr_name, ContextRegister, identifier(*n->id), t); break; - case IR::OpURShift: generateFunctionCall(__qmljs_inplace_ushr_name, ContextRegister, identifier(*n->id), t); break; + case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break; + case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_name); break; + case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_name); break; + case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_name); break; + case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_name); break; + case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_name); break; + case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_name); break; + case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_name); break; + case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_name); break; + case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_name); break; + case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_name); break; default: Q_UNREACHABLE(); break; } - checkExceptions(); + if (op) { + generateFunctionCallImp(opName, op, ContextRegister, identifier(*n->id), t); + checkExceptions(); + } return; } } else if (IR::Subscript *ss = s->target->asSubscript()) { if (IR::Temp *t = s->source->asTemp()) { - IR::Temp* base = ss->base->asTemp(); - IR::Temp* index = ss->index->asTemp(); + void (*op)(Context *ctx, Value *base, Value *index, Value *value) = 0; + const char *opName = 0; switch (s->op) { - case IR::OpBitAnd: generateFunctionCall(__qmljs_inplace_bit_and_element, ContextRegister, base, index, t); break; - case IR::OpBitOr: generateFunctionCall(__qmljs_inplace_bit_or_element, ContextRegister, base, index, t); break; - case IR::OpBitXor: generateFunctionCall(__qmljs_inplace_bit_xor_element, ContextRegister, base, index, t); break; - case IR::OpAdd: generateFunctionCall(__qmljs_inplace_add_element, ContextRegister, base, index, t); break; - case IR::OpSub: generateFunctionCall(__qmljs_inplace_sub_element, ContextRegister, base, index, t); break; - case IR::OpMul: generateFunctionCall(__qmljs_inplace_mul_element, ContextRegister, base, index, t); break; - case IR::OpDiv: generateFunctionCall(__qmljs_inplace_div_element, ContextRegister, base, index, t); break; - case IR::OpMod: generateFunctionCall(__qmljs_inplace_mod_element, ContextRegister, base, index, t); break; - case IR::OpLShift: generateFunctionCall(__qmljs_inplace_shl_element, ContextRegister, base, index, t); break; - case IR::OpRShift: generateFunctionCall(__qmljs_inplace_shr_element, ContextRegister, base, index, t); break; - case IR::OpURShift: generateFunctionCall(__qmljs_inplace_ushr_element, ContextRegister, base, index, t); break; + case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; + case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_element); break; + case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_element); break; + case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_element); break; + case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_element); break; + case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_element); break; + case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_element); break; + case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_element); break; + case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_element); break; + case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_element); break; + case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_element); break; default: Q_UNREACHABLE(); break; } - checkExceptions(); + if (op) { + IR::Temp* base = ss->base->asTemp(); + IR::Temp* index = ss->index->asTemp(); + generateFunctionCallImp(opName, op, ContextRegister, base, index, t); + checkExceptions(); + } return; } } else if (IR::Member *m = s->target->asMember()) { if (IR::Temp *t = s->source->asTemp()) { - IR::Temp* base = m->base->asTemp(); - String* member = identifier(*m->name); + void (*op)(Context *ctx, Value *base, String *name, Value *value) = 0; + const char *opName = 0; switch (s->op) { - case IR::OpBitAnd: generateFunctionCall(__qmljs_inplace_bit_and_member, ContextRegister, base, member, t); break; - case IR::OpBitOr: generateFunctionCall(__qmljs_inplace_bit_or_member, ContextRegister, base, member, t); break; - case IR::OpBitXor: generateFunctionCall(__qmljs_inplace_bit_xor_member, ContextRegister, base, member, t); break; - case IR::OpAdd: generateFunctionCall(__qmljs_inplace_add_member, ContextRegister, base, member, t); break; - case IR::OpSub: generateFunctionCall(__qmljs_inplace_sub_member, ContextRegister, base, member, t); break; - case IR::OpMul: generateFunctionCall(__qmljs_inplace_mul_member, ContextRegister, base, member, t); break; - case IR::OpDiv: generateFunctionCall(__qmljs_inplace_div_member, ContextRegister, base, member, t); break; - case IR::OpMod: generateFunctionCall(__qmljs_inplace_mod_member, ContextRegister, base, member, t); break; - case IR::OpLShift: generateFunctionCall(__qmljs_inplace_shl_member, ContextRegister, base, member, t); break; - case IR::OpRShift: generateFunctionCall(__qmljs_inplace_shr_member, ContextRegister, base, member, t); break; - case IR::OpURShift: generateFunctionCall(__qmljs_inplace_ushr_member, ContextRegister, base, member, t); break; + case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; + case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_member); break; + case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_member); break; + case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_member); break; + case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_member); break; + case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_member); break; + case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_member); break; + case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_member); break; + case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_member); break; + case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_member); break; + case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_member); break; default: Q_UNREACHABLE(); break; } - checkExceptions(); + if (op) { + IR::Temp* base = m->base->asTemp(); + String* member = identifier(*m->name); + generateFunctionCallImp(opName, op, ContextRegister, base, member, t); + checkExceptions(); + } return; } } @@ -526,6 +560,8 @@ void InstructionSelection::visitMove(IR::Move *s) s->dump(qout, IR::Stmt::MIR); qout << endl; assert(!"TODO"); + + #undef setOp } void InstructionSelection::visitJump(IR::Jump *s) -- cgit v1.2.3 From c13a95361aed606dd87d3c320dbe219ec8eb17ed Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 12 Oct 2012 10:12:24 +0200 Subject: Add missing license headers Change-Id: I59d602a0f2c1fefb03994ed32a3d697b176791ff Reviewed-by: Lars Knoll --- llvm_runtime.cpp | 41 ++++++++++++++++++++++++++++++++ main.cpp | 40 +++++++++++++++++++++++++++++++ masm/WeakRandom.h | 41 ++++++++++++++++++++++++++++++++ masm/config.h | 41 ++++++++++++++++++++++++++++++++ masm/stubs/ExecutableAllocator.h | 40 +++++++++++++++++++++++++++++++ masm/stubs/JSGlobalData.h | 40 +++++++++++++++++++++++++++++++ masm/stubs/Options.h | 40 +++++++++++++++++++++++++++++++ masm/stubs/WTFStubs.cpp | 40 +++++++++++++++++++++++++++++++ masm/stubs/WTFStubs.h | 40 +++++++++++++++++++++++++++++++ qmljs_objects.cpp | 41 ++++++++++++++++++++++++++++++++ qmljs_objects.h | 40 +++++++++++++++++++++++++++++++ qmljs_runtime.cpp | 41 ++++++++++++++++++++++++++++++++ qmljs_runtime.h | 40 +++++++++++++++++++++++++++++++ qv4array.cpp | 41 ++++++++++++++++++++++++++++++++ qv4array_p.h | 40 +++++++++++++++++++++++++++++++ qv4codegen.cpp | 41 ++++++++++++++++++++++++++++++++ qv4codegen_p.h | 40 +++++++++++++++++++++++++++++++ qv4ecmaobjects.cpp | 41 ++++++++++++++++++++++++++++++++ qv4ecmaobjects_p.h | 40 +++++++++++++++++++++++++++++++ qv4ir.cpp | 50 +++++++++++++++++++-------------------- qv4ir_p.h | 51 ++++++++++++++++++++-------------------- qv4isel_llvm.cpp | 40 +++++++++++++++++++++++++++++++ qv4isel_llvm_p.h | 40 +++++++++++++++++++++++++++++++ qv4isel_masm.cpp | 40 +++++++++++++++++++++++++++++++ qv4isel_masm_p.h | 40 +++++++++++++++++++++++++++++++ qv4syntaxchecker.cpp | 40 +++++++++++++++++++++++++++++++ qv4syntaxchecker_p.h | 40 +++++++++++++++++++++++++++++++ 27 files changed, 1058 insertions(+), 51 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index f1767b7c62..bbc4e582bb 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #include "qmljs_runtime.h" #include diff --git a/main.cpp b/main.cpp index 9704aa5d95..c40e1d0ebb 100644 --- a/main.cpp +++ b/main.cpp @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #include "qmljs_objects.h" #include "qv4codegen_p.h" diff --git a/masm/WeakRandom.h b/masm/WeakRandom.h index 65b8a22a97..325d1f6ac6 100644 --- a/masm/WeakRandom.h +++ b/masm/WeakRandom.h @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #ifndef MASM_WEAKRANDOM_H #define MASM_WEAKRANDOM_H diff --git a/masm/config.h b/masm/config.h index 7e0779fc45..2f4d9f4925 100644 --- a/masm/config.h +++ b/masm/config.h @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #ifndef MASM_CONFIG_H #define MASM_CONFIG_H diff --git a/masm/stubs/ExecutableAllocator.h b/masm/stubs/ExecutableAllocator.h index 6c0efaf639..1adce8eb10 100644 --- a/masm/stubs/ExecutableAllocator.h +++ b/masm/stubs/ExecutableAllocator.h @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #ifndef MASM_EXECUTABLEALLOCATOR_H #define MASM_EXECUTABLEALLOCATOR_H diff --git a/masm/stubs/JSGlobalData.h b/masm/stubs/JSGlobalData.h index 72bd63f5a0..79d78ad13b 100644 --- a/masm/stubs/JSGlobalData.h +++ b/masm/stubs/JSGlobalData.h @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #ifndef MASM_JSGLOBALDATA_H #define MASM_JSGLOBALDATA_H diff --git a/masm/stubs/Options.h b/masm/stubs/Options.h index 3d40363531..b95e4354e2 100644 --- a/masm/stubs/Options.h +++ b/masm/stubs/Options.h @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #ifndef OPTIONS_H #define OPTIONS_H diff --git a/masm/stubs/WTFStubs.cpp b/masm/stubs/WTFStubs.cpp index e4009b8315..d421e9094f 100644 --- a/masm/stubs/WTFStubs.cpp +++ b/masm/stubs/WTFStubs.cpp @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #include #include #include diff --git a/masm/stubs/WTFStubs.h b/masm/stubs/WTFStubs.h index d1587dd53d..ec77d25da7 100644 --- a/masm/stubs/WTFStubs.h +++ b/masm/stubs/WTFStubs.h @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #ifndef WTFSTUBS_H #define WTFSTUBS_H diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 280dbaaf98..2be16c6f27 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #include "qmljs_objects.h" #include "qv4ir_p.h" diff --git a/qmljs_objects.h b/qmljs_objects.h index 49c0fe2289..39a03bacba 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #ifndef QMLJS_OBJECTS_H #define QMLJS_OBJECTS_H diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index e4295a5554..9f5e61158a 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #include "qmljs_runtime.h" #include "qmljs_objects.h" diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 2d205ebf74..cc39e945a8 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #ifndef QMLJS_RUNTIME_H #define QMLJS_RUNTIME_H diff --git a/qv4array.cpp b/qv4array.cpp index 9cdd06b23a..a2f2f9eb35 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #include "qv4array_p.h" #include "qmljs_objects.h" diff --git a/qv4array_p.h b/qv4array_p.h index 01b3402883..2bce001c53 100644 --- a/qv4array_p.h +++ b/qv4array_p.h @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #ifndef QV4ARRAY_P_H #define QV4ARRAY_P_H diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 0b7a072353..892638fe88 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #include "qv4codegen_p.h" #include diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 7e0d5c798f..8e9f505c87 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #ifndef QV4CODEGEN_P_H #define QV4CODEGEN_P_H diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 116daf9334..dce563c4c4 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #include "qv4ecmaobjects_p.h" #include "qv4array_p.h" diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index ac9551d243..c65ab81a84 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #ifndef QV4ECMAOBJECTS_P_H #define QV4ECMAOBJECTS_P_H diff --git a/qv4ir.cpp b/qv4ir.cpp index 79d5f113eb..4805f62ee9 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -1,38 +1,38 @@ /**************************************************************************** ** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the V4VM module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** ** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ diff --git a/qv4ir_p.h b/qv4ir_p.h index 120f0dfe3f..a457aa8fc0 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -1,44 +1,43 @@ /**************************************************************************** ** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the V4VM module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** ** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ - #ifndef QV4IR_P_H #define QV4IR_P_H diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 6d8f0017c8..7971133948 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #include "qv4isel_llvm_p.h" #include "qv4ir_p.h" diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 259cf46449..26b13fe333 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #ifndef QV4ISEL_LLVM_P_H #define QV4ISEL_LLVM_P_H diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index ea1e5b0e13..6e417ca226 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #include "qv4isel_masm_p.h" #include "qmljs_runtime.h" diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 8969306e4b..b18cc041c6 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #ifndef QV4ISEL_MASM_P_H #define QV4ISEL_MASM_P_H diff --git a/qv4syntaxchecker.cpp b/qv4syntaxchecker.cpp index 64247d6954..fcda486af2 100644 --- a/qv4syntaxchecker.cpp +++ b/qv4syntaxchecker.cpp @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #include "qv4syntaxchecker_p.h" diff --git a/qv4syntaxchecker_p.h b/qv4syntaxchecker_p.h index 508e9f8abb..38e123762e 100644 --- a/qv4syntaxchecker_p.h +++ b/qv4syntaxchecker_p.h @@ -1,3 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ #ifndef QV4SYNTAXCHECKER_P_H #define QV4SYNTAXCHECKER_P_H -- cgit v1.2.3 From 54e5b42950c91ef0da46d6ab1f826afd9dad5276 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 12 Oct 2012 12:15:10 +0200 Subject: Prospective Mac OS X build fix open_memstream is not supported beyond Linux it seems, so do the symbol replacing disassembly printing only on Linux. Change-Id: I1eff6db9e10bd0ebbc06e967a61f998ee42438a8 Reviewed-by: Erik Verbruggen --- qv4isel_masm.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 6e417ca226..285025526b 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -62,7 +62,8 @@ namespace { QTextStream qout(stderr, QIODevice::WriteOnly); } -static void printDisassmbleOutputWithCalls(const char* output, const QHash& functions) +#if OS(LINUX) +static void printDisassembledOutputWithCalls(const char* output, const QHash& functions) { QByteArray processedOutput(output); for (QHash::ConstIterator it = functions.begin(), end = functions.end(); @@ -73,6 +74,7 @@ static void printDisassmbleOutputWithCalls(const char* output, const QHashname->toUtf8(); if (name.startsWith('%')) @@ -148,11 +152,13 @@ void InstructionSelection::operator()(IR::Function *function) _function->codeRef = linkBuffer.finalizeCodeWithDisassembly(name.data()); WTF::setDataFile(stderr); +#if OS(LINUX) fclose(disasmStream); #if CPU(X86) || CPU(X86_64) - printDisassmbleOutputWithCalls(disasmOutput, functions); + printDisassembledOutputWithCalls(disasmOutput, functions); #endif free(disasmOutput); +#endif _function->code = (void (*)(VM::Context *, const uchar *)) _function->codeRef.code().executableAddress(); -- cgit v1.2.3 From 84223345dc8286d733b08443d9a25d456e9a15ae Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 12 Oct 2012 13:45:35 +0200 Subject: Fix LLVM build Change-Id: I7ae00a5e90087182d9f7d939db0a036c120e3b9b Reviewed-by: Simon Hausmann --- main.cpp | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/main.cpp b/main.cpp index c40e1d0ebb..c173f47f21 100644 --- a/main.cpp +++ b/main.cpp @@ -39,6 +39,25 @@ ** ****************************************************************************/ +#ifndef QMLJS_NO_LLVM +// These includes have to come first, because WTF/Platform.h defines some macros +// with very unfriendly names that collide with class fields in LLVM. +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +# include "qv4isel_llvm_p.h" +#endif + #include "qmljs_objects.h" #include "qv4codegen_p.h" #include "qv4isel_masm_p.h" @@ -56,23 +75,6 @@ #include #include -#ifndef QMLJS_NO_LLVM -# include "qv4isel_llvm_p.h" - -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -#endif - static inline bool protect(const void *addr, size_t size) { size_t pageSize = sysconf(_SC_PAGESIZE); -- cgit v1.2.3 From c4107e12ac17ed3dd8bb307b0d165c5abef396ab Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 12 Oct 2012 14:24:45 +0200 Subject: Prospective Mac build fix Define ahead of time which "feature" set we want, i.e. no LLINT/DFG/JIT code but only ENABLE(ASSEMBLER). Otherwise Platform.h will make a choice for us depending on the platform. Change-Id: I372dc690ecbc624e9d589f6f10078a7647fcb570 Reviewed-by: Erik Verbruggen --- masm/masm.pri | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/masm/masm.pri b/masm/masm.pri index 457c3810da..b17a3e9c60 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -11,6 +11,11 @@ HEADERS += $$PWD/stubs/WTFStubs.h DEFINES += WTF_EXPORT_PRIVATE="" +DEFINES += ENABLE_LLINT=0 +DEFINES += ENABLE_DFG_JIT=0 +DEFINES += ENABLE_JIT=0 +DEFINES += ENABLE_ASSEMBLER=1 + INCLUDEPATH += $$PWD/jit INCLUDEPATH += $$PWD/assembler INCLUDEPATH += $$PWD/wtf -- cgit v1.2.3 From b40ef7591a5a28c899f245eaf619a8a46e6289c2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 12 Oct 2012 14:30:52 +0200 Subject: Silence some compiler warnings Tweaks taken from upstream :) Change-Id: Iae73ec0aa35c2e2a0e78422e0636ac7d99c8303c Reviewed-by: Erik Verbruggen --- masm/masm.pri | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/masm/masm.pri b/masm/masm.pri index b17a3e9c60..f8740e054f 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -40,3 +40,20 @@ udis86.output = udis86_itab.h udis86.input = ITAB udis86.commands = python $$PWD/disassembler/udis86/itab.py ${QMAKE_FILE_IN} QMAKE_EXTRA_COMPILERS += udis86 + +# Taken from WebKit/Tools/qmake/mkspecs/features/unix/default_post.prf +linux-g++* { + greaterThan(QT_GCC_MAJOR_VERSION, 3):greaterThan(QT_GCC_MINOR_VERSION, 5) { + !contains(QMAKE_CXXFLAGS, -std=(c|gnu)\\+\\+(0x|11)) { + # We need to deactivate those warnings because some names conflicts with upcoming c++0x types (e.g.nullptr). + QMAKE_CXXFLAGS_WARN_ON += -Wno-c++0x-compat + QMAKE_CXXFLAGS += -Wno-c++0x-compat + } + } +} + +# Don't warn about OVERRIDE and FINAL, since they are feature-checked anyways +*clang:!contains(QMAKE_CXXFLAGS, -std=c++11) { + QMAKE_CXXFLAGS += -Wno-c++11-extensions + QMAKE_OBJECTIVE_CFLAGS += -Wno-c++11-extensions +} -- cgit v1.2.3 From 0925c451150e9837047c30139fca4c22e171419f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 13 Oct 2012 15:45:35 +0200 Subject: Fix build on ia32 Use the q constraint instead of r to ensure we get a register valid for seto. This should also work on amd64, but I haven't tested. Change-Id: I6da5916db0f35a05a709a127c0dd5cb134284a79 Reviewed-by: Lars Knoll --- qmljs_runtime.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index cc39e945a8..b2d0f71ae7 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -1035,7 +1035,7 @@ static inline Value add_int32(int a, int b) asm ("addl %1, %2\n" "seto %0" - : "=r" (overflow) + : "=q" (overflow) : "r" (b), "r" (a) : ); -- cgit v1.2.3 From 49f19ebc87ed5e1e9cfe359e31322946657e34e5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 13 Oct 2012 15:49:44 +0200 Subject: [moth] Add missing IR::AluOp -> ALUFunction mappings Change-Id: Icf5d15579ef05a6b6fb46fbcb40abd69fadb7898 Reviewed-by: Lars Knoll --- moth/qv4isel_moth.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 2c1f5aff4b..bfa9a0624b 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -147,11 +147,11 @@ ALUFunction aluOpFunction(IR::AluOp op) case IR::OpCompl: return 0; case IR::OpBitAnd: - return 0; + return VM::__qmljs_bit_and; case IR::OpBitOr: - return 0; + return VM::__qmljs_bit_or; case IR::OpBitXor: - return 0; + return VM::__qmljs_bit_xor; case IR::OpAdd: return VM::__qmljs_add; case IR::OpSub: @@ -185,9 +185,9 @@ ALUFunction aluOpFunction(IR::AluOp op) case IR::OpStrictNotEqual: return VM::__qmljs_sne; case IR::OpInstanceof: - return 0; + return VM::__qmljs_instanceof; case IR::OpIn: - return 0; + return VM::__qmljs_in; case IR::OpAnd: return 0; case IR::OpOr: -- cgit v1.2.3 From f1f1b48dc7d6855406a59e8fc2ffbad81970ee88 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 12 Oct 2012 14:31:29 +0200 Subject: Fix darwin-ism to make it compile on Macs. Change-Id: I7a018ad552764914085e3c89e9ea7e582ca230a7 Reviewed-by: Simon Hausmann --- masm/stubs/ExecutableAllocator.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/masm/stubs/ExecutableAllocator.h b/masm/stubs/ExecutableAllocator.h index 1adce8eb10..d0a43e4cda 100644 --- a/masm/stubs/ExecutableAllocator.h +++ b/masm/stubs/ExecutableAllocator.h @@ -56,6 +56,9 @@ struct ExecutableMemoryHandle : public RefCounted { { static size_t pageSize = sysconf(_SC_PAGESIZE); m_size = (m_size + pageSize - 1) & ~(pageSize - 1); +#if OS(DARWIN) +# define MAP_ANONYMOUS MAP_ANON +#endif m_data = mmap(0, m_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); } ~ExecutableMemoryHandle() -- cgit v1.2.3 From 418f074a27ac726f72c3f6a99cf060af132e5180 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 12 Oct 2012 14:31:50 +0200 Subject: Fix for LLVM 3.2. Change-Id: I79dfe5e41267fabbddea7a753685a864de5979cd Reviewed-by: Simon Hausmann --- qv4isel_llvm_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 26b13fe333..6c661ca16d 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -43,7 +43,7 @@ #include "qv4ir_p.h" #include -#include +#include namespace QQmlJS { -- cgit v1.2.3 From 2b10a497f7156e80e16cdf7a30250d3f16ccfad5 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 12 Oct 2012 14:56:21 +0200 Subject: Fix: change struct JSGlobalData into a class and fix forward decls. Change-Id: I3d414282cafd51e813dc9e53cc2f5b1d9163efb3 Reviewed-by: Simon Hausmann --- masm/stubs/ExecutableAllocator.h | 2 +- masm/stubs/JSGlobalData.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/masm/stubs/ExecutableAllocator.h b/masm/stubs/ExecutableAllocator.h index d0a43e4cda..37700c27ba 100644 --- a/masm/stubs/ExecutableAllocator.h +++ b/masm/stubs/ExecutableAllocator.h @@ -48,7 +48,7 @@ namespace JSC { -struct JSGlobalData; +class JSGlobalData; struct ExecutableMemoryHandle : public RefCounted { ExecutableMemoryHandle(int size) diff --git a/masm/stubs/JSGlobalData.h b/masm/stubs/JSGlobalData.h index 79d78ad13b..112bedddd7 100644 --- a/masm/stubs/JSGlobalData.h +++ b/masm/stubs/JSGlobalData.h @@ -46,7 +46,8 @@ namespace JSC { -struct JSGlobalData { +class JSGlobalData { +public: ExecutableAllocator executableAllocator; }; -- cgit v1.2.3 From 5e7da8a1380748325cb48d768d035ea496fac980 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 15 Oct 2012 12:45:48 +0200 Subject: Disable fastmalloc Change-Id: I9b65c0b237463b81b04d8745476a5c002ea9af12 Reviewed-by: Lars Knoll --- masm/masm.pri | 1 + 1 file changed, 1 insertion(+) diff --git a/masm/masm.pri b/masm/masm.pri index f8740e054f..a721933d40 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -15,6 +15,7 @@ DEFINES += ENABLE_LLINT=0 DEFINES += ENABLE_DFG_JIT=0 DEFINES += ENABLE_JIT=0 DEFINES += ENABLE_ASSEMBLER=1 +DEFINES += USE_SYSTEM_MALLOC=1 INCLUDEPATH += $$PWD/jit INCLUDEPATH += $$PWD/assembler -- cgit v1.2.3 From 5e9140815c1698d8c614ce5f3603c10f5019f860 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 15 Oct 2012 20:34:21 +0200 Subject: Clean up callee save register push/pop code to have less #ifdefs Change-Id: Id4286de7716d7093473ab9e5d2e5d95be8f69efe Reviewed-by: Lars Knoll --- qv4isel_masm_p.h | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index b18cc041c6..d609193fc5 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -71,7 +71,8 @@ protected: static const RegisterID Gpr1 = JSC::X86Registers::ecx; static const RegisterID Gpr2 = JSC::X86Registers::edx; static const RegisterID Gpr3 = JSC::X86Registers::esi; - static const RegisterID CalleeSavedGpr = Gpr3; + static const RegisterID CalleeSavedFirstRegister = Gpr3; + static const RegisterID CalleeSavedLastRegister = Gpr3; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; #elif CPU(X86_64) static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; @@ -99,6 +100,8 @@ protected: static const RegisterID Gpr1 = JSC::ARMRegisters::r7; static const RegisterID Gpr2 = JSC::ARMRegisters::r8; static const RegisterID Gpr3 = JSC::ARMRegisters::r10; + static const RegisterID CalleeSavedFirstRegister = JSC::ARMRegisters::r4; + static const RegisterID CalleeSavedLastRegister = JSC::ARMRegisters::r11; static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; static const RegisterID RegisterArgument1 = JSC::ARMRegisters::r0; @@ -130,20 +133,16 @@ protected: push(StackFrameRegister); move(StackPointerRegister, StackFrameRegister); subPtr(TrustedImm32(locals*sizeof(QQmlJS::VM::Value)), StackPointerRegister); -#if CPU(X86) - push(CalleeSavedGpr); -#elif CPU(ARM) - for (int saveReg = JSC::ARMRegisters::r4; saveReg <= JSC::ARMRegisters::r11; ++saveReg) - push(static_cast(saveReg)); +#if CPU(X86) || CPU(ARM) + for (int saveReg = CalleeSavedFirstRegister; saveReg <= CalleeSavedLastRegister; ++saveReg) + push(static_cast(saveReg)); #endif } void leaveStandardStackFrame(int locals) { -#if CPU(X86) - pop(CalleeSavedGpr); -#elif CPU(ARM) - for (int saveReg = JSC::ARMRegisters::r11; saveReg >= JSC::ARMRegisters::r4; --saveReg) - pop(static_cast(saveReg)); +#if CPU(X86) || CPU(ARM) + for (int saveReg = CalleeSavedLastRegister; saveReg >= CalleeSavedFirstRegister; --saveReg) + pop(static_cast(saveReg)); #endif addPtr(TrustedImm32(locals*sizeof(QQmlJS::VM::Value)), StackPointerRegister); pop(StackFrameRegister); -- cgit v1.2.3 From bf98bd92567c7da86219e852d93db7057e2d51c0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 15 Oct 2012 22:32:06 +0200 Subject: Do not return booleans from methods called by JIT generated code For a boolean, only one byte of the the value returned in the register is defined. But the compiled code afterwards does a 32 bit comparison with 0 or 1, leading to random failures depending on compiler optimisation settings. Instead we now consistently return uints, and all boolean tests in the JIT generated code have to be done as branch32((Not)Equal, register, TrustedImm32(0)). Never compare against 1 to avoid potential problems. Change-Id: I9d7545dd6705bca82dc49f1eedb329fffb86cdb7 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 8 +++---- qmljs_runtime.h | 64 +++++++++++++++++++++++++++---------------------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 9f5e61158a..acc59d4895 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -780,12 +780,12 @@ void __qmljs_string_from_number(Context *ctx, Value *result, double number) *result = Value::fromString(string); } -bool __qmljs_string_compare(Context *, String *left, String *right) +uint __qmljs_string_compare(Context *, String *left, String *right) { return left->toQString() < right->toQString(); } -bool __qmljs_string_equal(Context *, String *left, String *right) +uint __qmljs_string_equal(Context *, String *left, String *right) { return left == right || left->isEqualTo(right); } @@ -795,7 +795,7 @@ String *__qmljs_string_concat(Context *ctx, String *first, String *second) return ctx->engine->newString(first->toQString() + second->toQString()); } -bool __qmljs_is_function(Context *, const Value *value) +uint __qmljs_is_function(Context *, const Value *value) { return value->objectValue()->asFunctionObject() != 0; } @@ -1057,7 +1057,7 @@ void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y } } -bool __qmljs_equal(Context *ctx, const Value *x, const Value *y) +uint __qmljs_equal(Context *ctx, const Value *x, const Value *y) { if (x->type() == y->type()) { switch (x->type()) { diff --git a/qmljs_runtime.h b/qmljs_runtime.h index b2d0f71ae7..9d64473032 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -107,7 +107,7 @@ void __qmljs_init_object(Value *result, Object *object); void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos); void __qmljs_init_native_function(Context *ctx, Value *result, void (*code)(Context *)); -bool __qmljs_is_function(Context *ctx, const Value *value); +uint __qmljs_is_function(Context *ctx, const Value *value); // string literals void __qmljs_string_literal_undefined(Context *ctx, Value *result); @@ -125,8 +125,8 @@ String *__qmljs_string_from_utf8(Context *ctx, const char *s); int __qmljs_string_length(Context *ctx, String *string); double __qmljs_string_to_number(Context *ctx, String *string); void __qmljs_string_from_number(Context *ctx, Value *result, double number); -bool __qmljs_string_compare(Context *ctx, String *left, String *right); -bool __qmljs_string_equal(Context *ctx, String *left, String *right); +uint __qmljs_string_compare(Context *ctx, String *left, String *right); +uint __qmljs_string_equal(Context *ctx, String *left, String *right); String *__qmljs_string_concat(Context *ctx, String *first, String *second); String *__qmljs_identifier_from_utf8(Context *ctx, const char *s); @@ -154,7 +154,7 @@ void __qmljs_get_thisObject(Context *ctx, Value *result); // type conversion and testing void __qmljs_to_primitive(Context *ctx, Value *result, const Value *value, int typeHint); -bool __qmljs_to_boolean(Context *ctx, const Value *value); +uint __qmljs_to_boolean(Context *ctx, const Value *value); double __qmljs_to_number(Context *ctx, const Value *value); double __qmljs_to_integer(Context *ctx, const Value *value); int __qmljs_to_int32(Context *ctx, const Value *value); @@ -162,13 +162,13 @@ unsigned __qmljs_to_uint32(Context *ctx, const Value *value); unsigned short __qmljs_to_uint16(Context *ctx, const Value *value); void __qmljs_to_string(Context *ctx, Value *result, const Value *value); void __qmljs_to_object(Context *ctx, Value *result, const Value *value); -bool __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value); -bool __qmljs_is_callable(Context *ctx, const Value *value); +uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value); +uint __qmljs_is_callable(Context *ctx, const Value *value); void __qmljs_default_value(Context *ctx, Value *result, const Value *value, int typeHint); void __qmljs_compare(Context *ctx, Value *result, const Value *left, const Value *right, bool leftFlag); -bool __qmljs_equal(Context *ctx, const Value *x, const Value *y); -bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y); +uint __qmljs_equal(Context *ctx, const Value *x, const Value *y); +uint __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y); // unary operators void __qmljs_postincr(Context *ctx, Value *result, const Value *value); @@ -262,16 +262,16 @@ void __qmljs_inplace_shl_member(Context *ctx, Value *base, String *name, Value * void __qmljs_inplace_shr_member(Context *ctx, Value *base, String *name, Value *value); void __qmljs_inplace_ushr_member(Context *ctx, Value *base, String *name, Value *value); -bool __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right); -bool __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right); -bool __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right); -bool __qmljs_cmp_le(Context *ctx, const Value *left, const Value *right); -bool __qmljs_cmp_eq(Context *ctx, const Value *left, const Value *right); -bool __qmljs_cmp_ne(Context *ctx, const Value *left, const Value *right); -bool __qmljs_cmp_se(Context *ctx, const Value *left, const Value *right); -bool __qmljs_cmp_sne(Context *ctx, const Value *left, const Value *right); -bool __qmljs_cmp_instanceof(Context *ctx, const Value *left, const Value *right); -bool __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right); +uint __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right); +uint __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right); +uint __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right); +uint __qmljs_cmp_le(Context *ctx, const Value *left, const Value *right); +uint __qmljs_cmp_eq(Context *ctx, const Value *left, const Value *right); +uint __qmljs_cmp_ne(Context *ctx, const Value *left, const Value *right); +uint __qmljs_cmp_se(Context *ctx, const Value *left, const Value *right); +uint __qmljs_cmp_sne(Context *ctx, const Value *left, const Value *right); +uint __qmljs_cmp_instanceof(Context *ctx, const Value *left, const Value *right); +uint __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right); } // extern "C" @@ -683,7 +683,7 @@ inline void __qmljs_to_primitive(Context *ctx, Value *result, const Value *value __qmljs_default_value(ctx, result, value, typeHint); } -inline bool __qmljs_to_boolean(Context *ctx, const Value *value) +inline uint __qmljs_to_boolean(Context *ctx, const Value *value) { switch (value->type()) { case Value::Undefined_Type: @@ -870,7 +870,7 @@ inline void __qmljs_to_object(Context *ctx, Value *result, const Value *value) } } -inline bool __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value) +inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value) { switch (value->type()) { case Value::Undefined_Type: @@ -882,7 +882,7 @@ inline bool __qmljs_check_object_coercible(Context *ctx, Value *result, const Va } } -inline bool __qmljs_is_callable(Context *ctx, const Value *value) +inline uint __qmljs_is_callable(Context *ctx, const Value *value) { if (value->isObject()) return __qmljs_is_function(ctx, value); @@ -1192,77 +1192,77 @@ inline void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Va *result = Value::fromBoolean(r); } -inline bool __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right) +inline uint __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_gt(ctx, &v, left, right); return v.booleanValue(); } -inline bool __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right) +inline uint __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_lt(ctx, &v, left, right); return v.booleanValue(); } -inline bool __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right) +inline uint __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_ge(ctx, &v, left, right); return __qmljs_to_boolean(ctx, &v); } -inline bool __qmljs_cmp_le(Context *ctx, const Value *left, const Value *right) +inline uint __qmljs_cmp_le(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_le(ctx, &v, left, right); return __qmljs_to_boolean(ctx, &v); } -inline bool __qmljs_cmp_eq(Context *ctx, const Value *left, const Value *right) +inline uint __qmljs_cmp_eq(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_eq(ctx, &v, left, right); return __qmljs_to_boolean(ctx, &v); } -inline bool __qmljs_cmp_ne(Context *ctx, const Value *left, const Value *right) +inline uint __qmljs_cmp_ne(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_ne(ctx, &v, left, right); return __qmljs_to_boolean(ctx, &v); } -inline bool __qmljs_cmp_se(Context *ctx, const Value *left, const Value *right) +inline uint __qmljs_cmp_se(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_se(ctx, &v, left, right); return __qmljs_to_boolean(ctx, &v); } -inline bool __qmljs_cmp_sne(Context *ctx, const Value *left, const Value *right) +inline uint __qmljs_cmp_sne(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_sne(ctx, &v, left, right); return __qmljs_to_boolean(ctx, &v); } -inline bool __qmljs_cmp_instanceof(Context *ctx, const Value *left, const Value *right) +inline uint __qmljs_cmp_instanceof(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_instanceof(ctx, &v, left, right); return __qmljs_to_boolean(ctx, &v); } -inline bool __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right) +inline uint __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_in(ctx, &v, left, right); return __qmljs_to_boolean(ctx, &v); } -inline bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y) +inline uint __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y) { if (x->rawValue() == y->rawValue()) return true; -- cgit v1.2.3 From 55d128451ccbb726e5fd664700ac0c3c322406ff Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 13:11:59 +0200 Subject: Get rid of compiler warning. Change-Id: I3b92819ac1bf8e4f98c266d4136ed05235e06faf Reviewed-by: Simon Hausmann --- main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index c173f47f21..a5c148a99a 100644 --- a/main.cpp +++ b/main.cpp @@ -239,7 +239,8 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS const size_t codeSize = 400 * getpagesize(); uchar *code = 0; - posix_memalign((void**)&code, 16, codeSize); + if (posix_memalign((void**)&code, 16, codeSize)) + assert(!"memalign failed"); assert(code); assert(! (size_t(code) & 15)); -- cgit v1.2.3 From 76dbe13caf04db2b508311dfcd2b518c8633e652 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 16 Oct 2012 10:19:23 +0200 Subject: Optimize boolean test Use != 0 instead of == 1, as it allows for a more efficient encoding on ia32/amd64 (using test eax, eax) Change-Id: I7a29139d8e6e9b843bf8df6f20f5ef7cc9b2e8cc Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 285025526b..39f66ff9c1 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -666,7 +666,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) move(ReturnValueRegister, Gpr0); - Jump target = branch32(Equal, Gpr0, TrustedImm32(1)); + Jump target = branch32(NotEqual, Gpr0, TrustedImm32(0)); _patches[s->iftrue].append(target); jumpToBlock(s->iffalse); -- cgit v1.2.3 From d4e6330737d0743e2704ff6c10a27bf612312119 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 12:27:14 +0200 Subject: Start changing signatures of the runtime to being value based. Value's should be passed by value and returned by value between JIT and the JS runtime. This should give us better performance on x86_64 and ARM. Change-Id: I4fa286c6164f0143adb72d07c737fa273c8a5eb6 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 8 ++++---- qmljs_runtime.h | 36 ++++++++++++++++-------------------- qv4isel_masm.cpp | 4 ++-- qv4isel_masm_p.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 26 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index bbc4e582bb..9eed766c09 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -198,22 +198,22 @@ void __qmljs_llvm_in(Context *ctx, Value *result, Value *left, Value *right) void __qmljs_llvm_uplus(Context *ctx, Value *result, const Value *value) { - __qmljs_uplus(ctx, result, value); + *result = __qmljs_uplus(*value, ctx); } void __qmljs_llvm_uminus(Context *ctx, Value *result, const Value *value) { - __qmljs_uminus(ctx, result, value); + *result = __qmljs_uminus(*value, ctx); } void __qmljs_llvm_compl(Context *ctx, Value *result, const Value *value) { - __qmljs_compl(ctx, result, value); + *result = __qmljs_compl(*value, ctx); } void __qmljs_llvm_not(Context *ctx, Value *result, const Value *value) { - __qmljs_not(ctx, result, value); + *result = __qmljs_not(*value, ctx); } String *__qmljs_llvm_identifier_from_utf8(Context *ctx, const char *str) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 9d64473032..f27f97ba16 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -171,14 +171,10 @@ uint __qmljs_equal(Context *ctx, const Value *x, const Value *y); uint __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y); // unary operators -void __qmljs_postincr(Context *ctx, Value *result, const Value *value); -void __qmljs_postdecr(Context *ctx, Value *result, const Value *value); -void __qmljs_uplus(Context *ctx, Value *result, const Value *value); -void __qmljs_uminus(Context *ctx, Value *result, const Value *value); -void __qmljs_compl(Context *ctx, Value *result, const Value *value); -void __qmljs_not(Context *ctx, Value *result, const Value *value); -void __qmljs_preincr(Context *ctx, Value *result, const Value *value); -void __qmljs_predecr(Context *ctx, Value *result, const Value *value); +Value __qmljs_uplus(const Value value, Context *ctx); +Value __qmljs_uminus(const Value value, Context *ctx); +Value __qmljs_compl(const Value value, Context *ctx); +Value __qmljs_not(const Value value, Context *ctx); void __qmljs_delete_subscript(Context *ctx, Value *result, Value *base, Value *index); void __qmljs_delete_member(Context *ctx, Value *result, Value *base, String *name); @@ -928,28 +924,28 @@ inline void __qmljs_typeof(Context *ctx, Value *result, const Value *value) } } -inline void __qmljs_uplus(Context *ctx, Value *result, const Value *value) +inline Value __qmljs_uplus(const Value value, Context *ctx) { - double n = __qmljs_to_number(ctx, value); - *result = Value::fromDouble(n); + double n = __qmljs_to_number(ctx, &value); + return Value::fromDouble(n); } -inline void __qmljs_uminus(Context *ctx, Value *result, const Value *value) +inline Value __qmljs_uminus(const Value value, Context *ctx) { - double n = __qmljs_to_number(ctx, value); - *result = Value::fromDouble(-n); + double n = __qmljs_to_number(ctx, &value); + return Value::fromDouble(-n); } -inline void __qmljs_compl(Context *ctx, Value *result, const Value *value) +inline Value __qmljs_compl(const Value value, Context *ctx) { - int n = __qmljs_to_int32(ctx, value); - *result = Value::fromDouble(~n); + int n = __qmljs_to_int32(ctx, &value); + return Value::fromDouble(~n); } -inline void __qmljs_not(Context *ctx, Value *result, const Value *value) +inline Value __qmljs_not(const Value value, Context *ctx) { - bool b = __qmljs_to_boolean(ctx, value); - *result = Value::fromDouble(!b); + bool b = __qmljs_to_boolean(ctx, &value); + return Value::fromBoolean(!b); } // binary operators diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 39f66ff9c1..d723fd646c 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -394,7 +394,7 @@ void InstructionSelection::visitMove(IR::Move *s) return; } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { - void (*op)(Context *ctx, Value *result, const Value *value) = 0; + Value (*op)(const Value value, Context *ctx) = 0; const char *opName = 0; switch (u->op) { case IR::OpIfTrue: assert(!"unreachable"); break; @@ -406,7 +406,7 @@ void InstructionSelection::visitMove(IR::Move *s) } // switch if (op) - generateFunctionCallImp(opName, op, ContextRegister, t, e); + generateFunctionCallImp2(t, opName, op, e, ContextRegister); return; } } else if (IR::Binop *b = s->source->asBinop()) { diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index d609193fc5..baa8140ef9 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -276,6 +276,30 @@ private: move(imm32, dest); } + void loadArgument1(RegisterID source, RegisterID dest) + { + move(source, dest); + } + + void loadArgument1(const Pointer& ptr, RegisterID dest) + { + loadPtr(ptr, dest); + } + + void loadArgument1(IR::Temp* temp, RegisterID dest) + { + assert(temp); + Pointer addr = loadTempAddress(dest, temp); + loadArgument1(addr, dest); + } + + void storeArgument(RegisterID src, IR::Temp *temp) + { + assert(temp); + // ### Should use some ScratchRegister here + Pointer addr = loadTempAddress(Gpr3, temp); + storePtr(src, addr); + } using JSC::MacroAssembler::push; @@ -326,6 +350,8 @@ private: #define generateFunctionCall(function, ...) \ generateFunctionCallImp(isel_stringIfy(function), function, __VA_ARGS__) + #define generateFunctionCall2(t, function, ...) \ + generateFunctionCallImp2(t, isel_stringIfy(function), function, __VA_ARGS__) #if CPU(X86) template @@ -460,6 +486,28 @@ private: callAbsolute(functionName, function); callFunctionEpilogue(); } + + template + void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) + { + callFunctionPrologue(); + loadArgument1(arg1, RegisterArgument1); + loadArgument1(arg2, RegisterArgument2); + callAbsolute(functionName, function); + storeArgument(ReturnValueRegister, r); + callFunctionEpilogue(); + } + + template + void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1) + { + callFunctionPrologue(); + loadArgument1(arg1, RegisterArgument1); + callAbsolute(functionName, function); + storeArgument(ReturnValueRegister, r); + callFunctionEpilogue(); + } + #elif CPU(ARM) template -- cgit v1.2.3 From 013098cae859222e95920246894009467e9e0dc3 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 12:56:50 +0200 Subject: Further convert runtime methods to the new calling convention Value bassed argument passing. Change-Id: I95e49ae301221e36946c2ce4f25081461513a6bb Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 2 +- moth/qv4vme_moth.cpp | 2 +- qmljs_runtime.cpp | 24 ++++----- qmljs_runtime.h | 135 +++++++++++++++++++++++++-------------------------- qv4ecmaobjects.cpp | 8 +-- qv4isel_masm.cpp | 3 +- qv4isel_masm_p.h | 5 ++ 7 files changed, 89 insertions(+), 90 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 9eed766c09..871375d306 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -88,7 +88,7 @@ void __qmljs_llvm_init_native_function(Context *ctx, Value *result, void (*code) bool __qmljs_llvm_to_boolean(Context *ctx, const Value *value) { - return __qmljs_to_boolean(ctx, value); + return __qmljs_to_boolean(*value, ctx); } void __qmljs_llvm_bit_and(Context *ctx, Value *result, Value *left, Value *right) diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 0b3ae0a7b3..d78d885dcc 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -141,7 +141,7 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_END_INSTR(Jump) MOTH_BEGIN_INSTR(CJump) - if (__qmljs_to_boolean(context, &tempRegister)) + if (__qmljs_to_boolean(tempRegister, context)) code = ((uchar *)&instr.offset) + instr.offset; MOTH_END_INSTR(CJump) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index acc59d4895..16ec991960 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -133,32 +133,32 @@ int Value::toInteger(double number) int Value::toUInt16(Context *ctx) { - return __qmljs_to_uint16(ctx, this); + return __qmljs_to_uint16(*this, ctx); } int Value::toInt32(Context *ctx) { - return __qmljs_to_int32(ctx, this); + return __qmljs_to_int32(*this, ctx); } unsigned int Value::toUInt32(Context *ctx) { - return __qmljs_to_int32(ctx, this); + return __qmljs_to_uint32(*this, ctx); } bool Value::toBoolean(Context *ctx) const { - return __qmljs_to_boolean(ctx, this); + return __qmljs_to_boolean(*this, ctx); } double Value::toInteger(Context *ctx) const { - return __qmljs_to_integer(ctx, this); + return __qmljs_to_integer(*this, ctx); } double Value::toNumber(Context *ctx) const { - return __qmljs_to_number(ctx, this); + return __qmljs_to_number(*this, ctx); } String *Value::toString(Context *ctx) const @@ -509,8 +509,8 @@ void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Va String *string = __qmljs_string_concat(ctx, pleft.stringValue(), pright.stringValue()); *result = Value::fromString(string); } else { - double x = __qmljs_to_number(ctx, &pleft); - double y = __qmljs_to_number(ctx, &pright); + double x = __qmljs_to_number(pleft, ctx); + double y = __qmljs_to_number(pright, ctx); *result = Value::fromDouble(x + y); } } @@ -1047,8 +1047,8 @@ void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y bool r = __qmljs_string_compare(ctx, px.stringValue(), py.stringValue()); *result = Value::fromBoolean(r); } else { - double nx = __qmljs_to_number(ctx, &px); - double ny = __qmljs_to_number(ctx, &py); + double nx = __qmljs_to_number(px, ctx); + double ny = __qmljs_to_number(py, ctx); if (isnan(nx) || isnan(ny)) { *result = Value::undefinedValue(); } else { @@ -1086,10 +1086,10 @@ uint __qmljs_equal(Context *ctx, const Value *x, const Value *y) } else if (x->isUndefined() && y->isNull()) { return true; } else if (x->isNumber() && y->isString()) { - Value ny = Value::fromDouble(__qmljs_to_number(ctx, y)); + Value ny = Value::fromDouble(__qmljs_to_number(*y, ctx)); return __qmljs_equal(ctx, x, &ny); } else if (x->isString() && y->isNumber()) { - Value nx = Value::fromDouble(__qmljs_to_number(ctx, x)); + Value nx = Value::fromDouble(__qmljs_to_number(*x, ctx)); return __qmljs_equal(ctx, &nx, y); } else if (x->isBoolean()) { Value nx = Value::fromDouble((double) x->booleanValue()); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index f27f97ba16..5d9b8f6ed3 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -154,12 +154,12 @@ void __qmljs_get_thisObject(Context *ctx, Value *result); // type conversion and testing void __qmljs_to_primitive(Context *ctx, Value *result, const Value *value, int typeHint); -uint __qmljs_to_boolean(Context *ctx, const Value *value); -double __qmljs_to_number(Context *ctx, const Value *value); -double __qmljs_to_integer(Context *ctx, const Value *value); -int __qmljs_to_int32(Context *ctx, const Value *value); -unsigned __qmljs_to_uint32(Context *ctx, const Value *value); -unsigned short __qmljs_to_uint16(Context *ctx, const Value *value); +uint __qmljs_to_boolean(const Value value, Context *ctx); +double __qmljs_to_number(const Value value, Context *ctx); +double __qmljs_to_integer(const Value value, Context *ctx); +int __qmljs_to_int32(const Value value, Context *ctx); +unsigned __qmljs_to_uint32(const Value value, Context *ctx); +unsigned short __qmljs_to_uint16(const Value value, Context *ctx); void __qmljs_to_string(Context *ctx, Value *result, const Value *value); void __qmljs_to_object(Context *ctx, Value *result, const Value *value); uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value); @@ -679,55 +679,54 @@ inline void __qmljs_to_primitive(Context *ctx, Value *result, const Value *value __qmljs_default_value(ctx, result, value, typeHint); } -inline uint __qmljs_to_boolean(Context *ctx, const Value *value) +inline uint __qmljs_to_boolean(const Value value, Context *ctx) { - switch (value->type()) { + switch (value.type()) { case Value::Undefined_Type: case Value::Null_Type: return false; case Value::Boolean_Type: - return value->booleanValue(); case Value::Integer_Type: - return value->int_32; + return value.int_32; case Value::String_Type: - return __qmljs_string_length(ctx, value->stringValue()) > 0; + return __qmljs_string_length(ctx, value.stringValue()) > 0; case Value::Object_Type: return true; default: // double - if (! value->doubleValue() || qIsNaN(value->doubleValue())) + if (! value.doubleValue() || qIsNaN(value.doubleValue())) return false; return true; } } -inline double __qmljs_to_number(Context *ctx, const Value *value) +inline double __qmljs_to_number(const Value value, Context *ctx) { - switch (value->type()) { + switch (value.type()) { case Value::Undefined_Type: return nan(""); case Value::Null_Type: return 0; case Value::Boolean_Type: - return (value->booleanValue() ? 1. : 0.); + return (value.booleanValue() ? 1. : 0.); case Value::Integer_Type: - return value->int_32; + return value.int_32; case Value::String_Type: - return __qmljs_string_to_number(ctx, value->stringValue()); + return __qmljs_string_to_number(ctx, value.stringValue()); case Value::Object_Type: { Value prim; - __qmljs_to_primitive(ctx, &prim, value, NUMBER_HINT); - return __qmljs_to_number(ctx, &prim); + __qmljs_to_primitive(ctx, &prim, &value, NUMBER_HINT); + return __qmljs_to_number(prim, ctx); } default: // double - return value->doubleValue(); + return value.doubleValue(); } } -inline double __qmljs_to_integer(Context *ctx, const Value *value) +inline double __qmljs_to_integer(const Value value, Context *ctx) { - if (value->isInteger()) - return value->int_32; - const double number = __qmljs_to_number(ctx, value); + if (value.isInteger()) + return value.int_32; + const double number = __qmljs_to_number(value, ctx); if (qIsNaN(number)) return +0; else if (! number || isinf(number)) @@ -736,11 +735,11 @@ inline double __qmljs_to_integer(Context *ctx, const Value *value) return signbit(number) ? -v : v; } -inline int __qmljs_to_int32(Context *ctx, const Value *value) +inline int __qmljs_to_int32(const Value value, Context *ctx) { - if (value->isInteger()) - return value->int_32; - double number = __qmljs_to_number(ctx, value); + if (value.isInteger()) + return value.int_32; + double number = __qmljs_to_number(value, ctx); if ((number >= -2147483648.0 && number < 2147483648.0)) { return static_cast(number); @@ -765,12 +764,12 @@ inline int __qmljs_to_int32(Context *ctx, const Value *value) return int(number); } -inline unsigned __qmljs_to_uint32(Context *ctx, const Value *value) +inline unsigned __qmljs_to_uint32(const Value value, Context *ctx) { - if (value->isInteger()) - return (unsigned) value->int_32; + if (value.isInteger()) + return (unsigned) value.int_32; - double number = __qmljs_to_number(ctx, value); + double number = __qmljs_to_number(value, ctx); if (! number || qIsNaN(number) || isinf(number)) return +0; @@ -786,9 +785,9 @@ inline unsigned __qmljs_to_uint32(Context *ctx, const Value *value) return unsigned(number); } -inline unsigned short __qmljs_to_uint16(Context *ctx, const Value *value) +inline unsigned short __qmljs_to_uint16(const Value value, Context *ctx) { - double number = __qmljs_to_number(ctx, value); + double number = __qmljs_to_number(value, ctx); if (! number || qIsNaN(number) || isinf(number)) return +0; @@ -926,47 +925,47 @@ inline void __qmljs_typeof(Context *ctx, Value *result, const Value *value) inline Value __qmljs_uplus(const Value value, Context *ctx) { - double n = __qmljs_to_number(ctx, &value); + double n = __qmljs_to_number(value, ctx); return Value::fromDouble(n); } inline Value __qmljs_uminus(const Value value, Context *ctx) { - double n = __qmljs_to_number(ctx, &value); + double n = __qmljs_to_number(value, ctx); return Value::fromDouble(-n); } inline Value __qmljs_compl(const Value value, Context *ctx) { - int n = __qmljs_to_int32(ctx, &value); + int n = __qmljs_to_int32(value, ctx); return Value::fromDouble(~n); } inline Value __qmljs_not(const Value value, Context *ctx) { - bool b = __qmljs_to_boolean(ctx, &value); + bool b = __qmljs_to_boolean(value, ctx); return Value::fromBoolean(!b); } // binary operators inline void __qmljs_bit_or(Context *ctx, Value *result, const Value *left, const Value *right) { - int lval = __qmljs_to_int32(ctx, left); - int rval = __qmljs_to_int32(ctx, right); + int lval = __qmljs_to_int32(*left, ctx); + int rval = __qmljs_to_int32(*right, ctx); *result = Value::fromDouble(lval | rval); } inline void __qmljs_bit_xor(Context *ctx, Value *result, const Value *left, const Value *right) { - int lval = __qmljs_to_int32(ctx, left); - int rval = __qmljs_to_int32(ctx, right); + int lval = __qmljs_to_int32(*left, ctx); + int rval = __qmljs_to_int32(*right, ctx); *result = Value::fromDouble(lval ^ rval); } inline void __qmljs_bit_and(Context *ctx, Value *result, const Value *left, const Value *right) { - int lval = __qmljs_to_int32(ctx, left); - int rval = __qmljs_to_int32(ctx, right); + int lval = __qmljs_to_int32(*left, ctx); + int rval = __qmljs_to_int32(*right, ctx); *result = Value::fromDouble(lval & rval); } @@ -1053,50 +1052,50 @@ inline void __qmljs_add(Context *ctx, Value *result, const Value *left, const Va inline void __qmljs_sub(Context *ctx, Value *result, const Value *left, const Value *right) { - double lval = __qmljs_to_number(ctx, left); - double rval = __qmljs_to_number(ctx, right); + double lval = __qmljs_to_number(*left, ctx); + double rval = __qmljs_to_number(*right, ctx); *result = Value::fromDouble(lval - rval); } inline void __qmljs_mul(Context *ctx, Value *result, const Value *left, const Value *right) { - double lval = __qmljs_to_number(ctx, left); - double rval = __qmljs_to_number(ctx, right); + double lval = __qmljs_to_number(*left, ctx); + double rval = __qmljs_to_number(*right, ctx); *result = Value::fromDouble(lval * rval); } inline void __qmljs_div(Context *ctx, Value *result, const Value *left, const Value *right) { - double lval = __qmljs_to_number(ctx, left); - double rval = __qmljs_to_number(ctx, right); + double lval = __qmljs_to_number(*left, ctx); + double rval = __qmljs_to_number(*right, ctx); *result = Value::fromDouble(lval / rval); } inline void __qmljs_mod(Context *ctx, Value *result, const Value *left, const Value *right) { - double lval = __qmljs_to_number(ctx, left); - double rval = __qmljs_to_number(ctx, right); + double lval = __qmljs_to_number(*left, ctx); + double rval = __qmljs_to_number(*right, ctx); *result = Value::fromDouble(fmod(lval, rval)); } inline void __qmljs_shl(Context *ctx, Value *result, const Value *left, const Value *right) { - int lval = __qmljs_to_int32(ctx, left); - unsigned rval = __qmljs_to_uint32(ctx, right); + int lval = __qmljs_to_int32(*left, ctx); + unsigned rval = __qmljs_to_uint32(*right, ctx); *result = Value::fromDouble(lval << rval); } inline void __qmljs_shr(Context *ctx, Value *result, const Value *left, const Value *right) { - int lval = __qmljs_to_int32(ctx, left); - unsigned rval = __qmljs_to_uint32(ctx, right); + int lval = __qmljs_to_int32(*left, ctx); + unsigned rval = __qmljs_to_uint32(*right, ctx); *result = Value::fromDouble(lval >> rval); } inline void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const Value *right) { - unsigned lval = __qmljs_to_uint32(ctx, left); - unsigned rval = __qmljs_to_uint32(ctx, right); + unsigned lval = __qmljs_to_uint32(*left, ctx); + unsigned rval = __qmljs_to_uint32(*right, ctx); *result = Value::fromDouble(lval >> rval); } @@ -1206,56 +1205,52 @@ inline uint __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_ge(ctx, &v, left, right); - return __qmljs_to_boolean(ctx, &v); + return v.booleanValue(); } inline uint __qmljs_cmp_le(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_le(ctx, &v, left, right); - return __qmljs_to_boolean(ctx, &v); + return v.booleanValue(); } inline uint __qmljs_cmp_eq(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_eq(ctx, &v, left, right); - return __qmljs_to_boolean(ctx, &v); + return v.booleanValue(); } inline uint __qmljs_cmp_ne(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_ne(ctx, &v, left, right); - return __qmljs_to_boolean(ctx, &v); + return v.booleanValue(); } inline uint __qmljs_cmp_se(Context *ctx, const Value *left, const Value *right) { - Value v; - __qmljs_se(ctx, &v, left, right); - return __qmljs_to_boolean(ctx, &v); + return __qmljs_strict_equal(ctx, left, right); } inline uint __qmljs_cmp_sne(Context *ctx, const Value *left, const Value *right) { - Value v; - __qmljs_sne(ctx, &v, left, right); - return __qmljs_to_boolean(ctx, &v); + return ! __qmljs_strict_equal(ctx, left, right); } inline uint __qmljs_cmp_instanceof(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_instanceof(ctx, &v, left, right); - return __qmljs_to_boolean(ctx, &v); + return v.booleanValue(); } inline uint __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_in(ctx, &v, left, right); - return __qmljs_to_boolean(ctx, &v); + return v.booleanValue(); } inline uint __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index dce563c4c4..9e9b81bc46 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -848,7 +848,7 @@ void StringPrototype::method_lastIndexOf(Context *ctx) } Value posArg = ctx->argument(1); - double position = __qmljs_to_number(ctx, &posArg); + double position = __qmljs_to_number(posArg, ctx); if (qIsNaN(position)) position = +qInf(); else @@ -1552,7 +1552,7 @@ void ArrayPrototype::method_every(Context *ctx) args[2] = ctx->thisObject; Value r; __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); - ok = __qmljs_to_boolean(ctx, &r); + ok = __qmljs_to_boolean(r, ctx); } ctx->result = Value::fromBoolean(ok); } else { @@ -1582,7 +1582,7 @@ void ArrayPrototype::method_some(Context *ctx) args[2] = ctx->thisObject; Value r; __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); - ok = __qmljs_to_boolean(ctx, &r); + ok = __qmljs_to_boolean(r, ctx); } ctx->result = Value::fromBoolean(ok); } else { @@ -1669,7 +1669,7 @@ void ArrayPrototype::method_filter(Context *ctx) args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); - if (__qmljs_to_boolean(ctx, &r)) { + if (__qmljs_to_boolean(r, ctx)) { const uint index = a->value.size(); a->value.resize(index + 1); a->value.assign(index, v); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index d723fd646c..fd8a09921d 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -636,8 +636,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) booleanConversion.link(this); { - generateFunctionCall(__qmljs_to_boolean, ContextRegister, t); - move(ReturnValueRegister, Gpr0); + generateFunctionCall2(Gpr0, __qmljs_to_boolean, t, ContextRegister); } testBoolean.link(this); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index baa8140ef9..a8d0195783 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -301,6 +301,11 @@ private: storePtr(src, addr); } + void storeArgument(RegisterID src, RegisterID dest) + { + move(src, dest); + } + using JSC::MacroAssembler::push; void push(const Pointer& ptr) -- cgit v1.2.3 From a0690ab1ebc844675f2eb4ed9f1087497c92768e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 13:13:44 +0200 Subject: Convert string literals to new calling convention Change-Id: I8dd6b1975351c36230ed512c55944c9b748ca38b Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 36 ++++++++++++++++++------------------ qmljs_runtime.h | 40 ++++++++++++++++++++-------------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 16ec991960..c4b56deb14 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -410,49 +410,49 @@ void __qmljs_init_native_function(Context *ctx, Value *result, void (*code)(Cont __qmljs_init_object(result, ctx->engine->newNativeFunction(ctx, code)); } -void __qmljs_string_literal_undefined(Context *ctx, Value *result) +Value __qmljs_string_literal_undefined(Context *ctx) { - *result = Value::fromString(ctx->engine->identifier(QStringLiteral("undefined"))); + return Value::fromString(ctx->engine->identifier(QStringLiteral("undefined"))); } -void __qmljs_string_literal_null(Context *ctx, Value *result) +Value __qmljs_string_literal_null(Context *ctx) { - *result = Value::fromString(ctx->engine->identifier(QStringLiteral("null"))); + return Value::fromString(ctx->engine->identifier(QStringLiteral("null"))); } -void __qmljs_string_literal_true(Context *ctx, Value *result) +Value __qmljs_string_literal_true(Context *ctx) { - *result = Value::fromString(ctx->engine->identifier(QStringLiteral("true"))); + return Value::fromString(ctx->engine->identifier(QStringLiteral("true"))); } -void __qmljs_string_literal_false(Context *ctx, Value *result) +Value __qmljs_string_literal_false(Context *ctx) { - *result = Value::fromString(ctx->engine->identifier(QStringLiteral("false"))); + return Value::fromString(ctx->engine->identifier(QStringLiteral("false"))); } -void __qmljs_string_literal_object(Context *ctx, Value *result) +Value __qmljs_string_literal_object(Context *ctx) { - *result = Value::fromString(ctx->engine->identifier(QStringLiteral("object"))); + return Value::fromString(ctx->engine->identifier(QStringLiteral("object"))); } -void __qmljs_string_literal_boolean(Context *ctx, Value *result) +Value __qmljs_string_literal_boolean(Context *ctx) { - *result = Value::fromString(ctx->engine->identifier(QStringLiteral("boolean"))); + return Value::fromString(ctx->engine->identifier(QStringLiteral("boolean"))); } -void __qmljs_string_literal_number(Context *ctx, Value *result) +Value __qmljs_string_literal_number(Context *ctx) { - *result = Value::fromString(ctx->engine->identifier(QStringLiteral("number"))); + return Value::fromString(ctx->engine->identifier(QStringLiteral("number"))); } -void __qmljs_string_literal_string(Context *ctx, Value *result) +Value __qmljs_string_literal_string(Context *ctx) { - *result = Value::fromString(ctx->engine->identifier(QStringLiteral("string"))); + return Value::fromString(ctx->engine->identifier(QStringLiteral("string"))); } -void __qmljs_string_literal_function(Context *ctx, Value *result) +Value __qmljs_string_literal_function(Context *ctx) { - *result = Value::fromString(ctx->engine->identifier(QStringLiteral("function"))); + return Value::fromString(ctx->engine->identifier(QStringLiteral("function"))); } void __qmljs_delete_subscript(Context *ctx, Value *result, Value *base, Value *index) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 5d9b8f6ed3..47021f9d51 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -110,15 +110,15 @@ void __qmljs_init_native_function(Context *ctx, Value *result, void (*code)(Cont uint __qmljs_is_function(Context *ctx, const Value *value); // string literals -void __qmljs_string_literal_undefined(Context *ctx, Value *result); -void __qmljs_string_literal_null(Context *ctx, Value *result); -void __qmljs_string_literal_true(Context *ctx, Value *result); -void __qmljs_string_literal_false(Context *ctx, Value *result); -void __qmljs_string_literal_object(Context *ctx, Value *result); -void __qmljs_string_literal_boolean(Context *ctx, Value *result); -void __qmljs_string_literal_number(Context *ctx, Value *result); -void __qmljs_string_literal_string(Context *ctx, Value *result); -void __qmljs_string_literal_function(Context *ctx, Value *result); +Value __qmljs_string_literal_undefined(Context *ctx); +Value __qmljs_string_literal_null(Context *ctx); +Value __qmljs_string_literal_true(Context *ctx); +Value __qmljs_string_literal_false(Context *ctx); +Value __qmljs_string_literal_object(Context *ctx); +Value __qmljs_string_literal_boolean(Context *ctx); +Value __qmljs_string_literal_number(Context *ctx); +Value __qmljs_string_literal_string(Context *ctx); +Value __qmljs_string_literal_function(Context *ctx); // strings String *__qmljs_string_from_utf8(Context *ctx, const char *s); @@ -807,16 +807,16 @@ inline void __qmljs_to_string(Context *ctx, Value *result, const Value *value) { switch (value->type()) { case Value::Undefined_Type: - __qmljs_string_literal_undefined(ctx, result); + *result = __qmljs_string_literal_undefined(ctx); break; case Value::Null_Type: - __qmljs_string_literal_null(ctx, result); + *result = __qmljs_string_literal_null(ctx); break; case Value::Boolean_Type: if (value->booleanValue()) - __qmljs_string_literal_true(ctx, result); + *result = __qmljs_string_literal_true(ctx); else - __qmljs_string_literal_false(ctx, result); + *result = __qmljs_string_literal_false(ctx); break; case Value::String_Type: *result = *value; @@ -899,26 +899,26 @@ inline void __qmljs_typeof(Context *ctx, Value *result, const Value *value) { switch (value->type()) { case Value::Undefined_Type: - __qmljs_string_literal_undefined(ctx, result); + *result = __qmljs_string_literal_undefined(ctx); break; case Value::Null_Type: - __qmljs_string_literal_object(ctx, result); + *result = __qmljs_string_literal_object(ctx); break; case Value::Boolean_Type: - __qmljs_string_literal_boolean(ctx, result); + *result = __qmljs_string_literal_boolean(ctx); break; case Value::String_Type: - __qmljs_string_literal_string(ctx, result); + *result = __qmljs_string_literal_string(ctx); break; case Value::Object_Type: if (__qmljs_is_callable(ctx, value)) - __qmljs_string_literal_function(ctx, result); + *result = __qmljs_string_literal_function(ctx); else - __qmljs_string_literal_object(ctx, result); // ### implementation-defined + *result = __qmljs_string_literal_object(ctx); // ### implementation-defined break; case Value::Integer_Type: case Value::Double_Type: - __qmljs_string_literal_number(ctx, result); + *result = __qmljs_string_literal_number(ctx); break; } } -- cgit v1.2.3 From c63e799fc233c74564a2873806187973f398f5a8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 13:40:02 +0200 Subject: Convert Value constructors to new calling convention Change-Id: I433f72666499e660618b061cfcf3407f5f9bb166 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 2 +- main.cpp | 2 +- moth/qv4vme_moth.cpp | 2 +- qmljs_objects.cpp | 8 ++++---- qmljs_runtime.cpp | 24 +++++++++++------------- qmljs_runtime.h | 16 ++++++++-------- qv4ecmaobjects.cpp | 14 +++++++------- qv4isel_masm.cpp | 4 ++-- qv4isel_masm_p.h | 16 ++++++++++++++++ 9 files changed, 51 insertions(+), 37 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 871375d306..bea01d2dd9 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -83,7 +83,7 @@ void __qmljs_llvm_init_string(Context *ctx, Value *result, const char *str) void __qmljs_llvm_init_native_function(Context *ctx, Value *result, void (*code)(Context *)) { - __qmljs_init_native_function(ctx, result, code); + *result = __qmljs_init_native_function(code, ctx); } bool __qmljs_llvm_to_boolean(Context *ctx, const Value *value) diff --git a/main.cpp b/main.cpp index a5c148a99a..5110e48153 100644 --- a/main.cpp +++ b/main.cpp @@ -287,7 +287,7 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS ctx->hasUncaughtException = false; if (! ctx->activation.isObject()) - __qmljs_init_object(&ctx->activation, new QQmlJS::VM::Object()); + ctx->activation = __qmljs_init_object(new QQmlJS::VM::Object()); foreach (const QString *local, globalCode->locals) { ctx->activation.objectValue()->setProperty(ctx, *local, QQmlJS::VM::Value::undefinedValue()); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index d78d885dcc..f244f1cb1a 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -120,7 +120,7 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_END_INSTR(LoadString) MOTH_BEGIN_INSTR(LoadClosure) - __qmljs_init_closure(context, &tempRegister, instr.value); + tempRegister = __qmljs_init_closure(instr.value, context); MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 2be16c6f27..34e223aff5 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -190,7 +190,7 @@ void FunctionObject::call(Context *ctx) void FunctionObject::construct(Context *ctx) { - __qmljs_init_object(&ctx->thisObject, ctx->engine->newObject()); + ctx->thisObject = __qmljs_init_object(ctx->engine->newObject()); call(ctx); } @@ -235,7 +235,7 @@ void ScriptFunction::construct(VM::Context *ctx) Value proto = getProperty(ctx, ctx->engine->id_prototype); if (proto.isObject()) obj->prototype = proto.objectValue(); - __qmljs_init_object(&ctx->thisObject, obj); + ctx->thisObject = __qmljs_init_object(obj); function->code(ctx, function->codeData); } @@ -347,8 +347,8 @@ ExecutionEngine::ExecutionEngine() // set up the global object // VM::Object *glo = newObject(/*rootContext*/); - __qmljs_init_object(&globalObject, glo); - __qmljs_init_object(&rootContext->activation, glo); + globalObject = __qmljs_init_object(glo); + rootContext->activation = __qmljs_init_object(glo); glo->setProperty(rootContext, identifier(QStringLiteral("Object")), objectCtor); glo->setProperty(rootContext, identifier(QStringLiteral("String")), stringCtor); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index c4b56deb14..eb2038b266 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -339,7 +339,7 @@ void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionO result = Value::undefinedValue(); if (f->needsActivation) - __qmljs_init_object(&activation, engine->newActivationObject(this)); + activation = __qmljs_init_object(engine->newActivationObject(this)); else activation = Value::nullValue(); @@ -400,14 +400,14 @@ void Context::leaveConstructorContext(FunctionObject *f, Value *returnValue) extern "C" { -void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos) +Value __qmljs_init_closure(IR::Function *clos, Context *ctx) { - __qmljs_init_object(result, ctx->engine->newScriptFunction(ctx, clos)); + return __qmljs_init_object(ctx->engine->newScriptFunction(ctx, clos)); } -void __qmljs_init_native_function(Context *ctx, Value *result, void (*code)(Context *)) +Value __qmljs_init_native_function(void (*code)(Context *), Context *ctx) { - __qmljs_init_object(result, ctx->engine->newNativeFunction(ctx, code)); + return __qmljs_init_object(ctx->engine->newNativeFunction(ctx, code)); } Value __qmljs_string_literal_undefined(Context *ctx) @@ -850,25 +850,25 @@ void __qmljs_throw_type_error(Context *ctx, Value *result) void __qmljs_new_object(Context *ctx, Value *result) { - __qmljs_init_object(result, ctx->engine->newObject()); + *result = __qmljs_init_object(ctx->engine->newObject()); } void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean) { Value value = Value::fromBoolean(boolean); - __qmljs_init_object(result, ctx->engine->newBooleanObject(value)); + *result = __qmljs_init_object(ctx->engine->newBooleanObject(value)); } void __qmljs_new_number_object(Context *ctx, Value *result, double number) { Value value = Value::fromDouble(number); - __qmljs_init_object(result, ctx->engine->newNumberObject(value)); + *result = __qmljs_init_object(ctx->engine->newNumberObject(value)); } void __qmljs_new_string_object(Context *ctx, Value *result, String *string) { Value value = Value::fromString(string); - __qmljs_init_object(result, ctx->engine->newStringObject(value)); + *result = __qmljs_init_object(ctx->engine->newStringObject(value)); } void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value) @@ -898,8 +898,7 @@ void __qmljs_set_property_string(Context *ctx, Value *object, String *name, Stri void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR::Function *function) { - Value value; - __qmljs_init_closure(ctx, &value, function); + Value value = __qmljs_init_closure(function, ctx); object->objectValue()->setProperty(ctx, name, value, /*flag*/ 0); } @@ -988,8 +987,7 @@ void __qmljs_set_activation_property_string(Context *ctx, String *name, String * void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Function *clos) { - Value value; - __qmljs_init_closure(ctx, &value, clos); + Value value = __qmljs_init_closure(clos, ctx); __qmljs_set_activation_property(ctx, name, &value); } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 47021f9d51..371675ab78 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -102,10 +102,10 @@ void __qmljs_builtin_throw(Context *context, Value *result, Value *args, int arg void __qmljs_builtin_rethrow(Context *context, Value *result, Value *args, int argc); // constructors -void __qmljs_init_string(Value *result, String *string); -void __qmljs_init_object(Value *result, Object *object); -void __qmljs_init_closure(Context *ctx, Value *result, IR::Function *clos); -void __qmljs_init_native_function(Context *ctx, Value *result, void (*code)(Context *)); +Value __qmljs_init_string(String *string); +Value __qmljs_init_object(Object *object); +Value __qmljs_init_closure(IR::Function *clos, Context *ctx); +Value __qmljs_init_native_function(void (*code)(Context *), Context *ctx); uint __qmljs_is_function(Context *ctx, const Value *value); @@ -660,14 +660,14 @@ extern "C" { // constructors -inline void __qmljs_init_string(Value *result, String *value) +inline Value __qmljs_init_string(String *value) { - *result = Value::fromString(value); + return Value::fromString(value); } -inline void __qmljs_init_object(Value *result, Object *object) +inline Value __qmljs_init_object(Object *object) { - *result = Value::fromObject(object); + return Value::fromObject(object); } // type conversion and testing diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 9e9b81bc46..16476a2d89 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -521,12 +521,12 @@ ObjectCtor::ObjectCtor(Context *scope) void ObjectCtor::construct(Context *ctx) { - __qmljs_init_object(&ctx->thisObject, ctx->engine->newObject()); + ctx->thisObject = __qmljs_init_object(ctx->engine->newObject()); } void ObjectCtor::call(Context *ctx) { - __qmljs_init_object(&ctx->result, ctx->engine->newObject()); + ctx->result = __qmljs_init_object(ctx->engine->newObject()); } Value ObjectCtor::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) @@ -705,7 +705,7 @@ void StringCtor::construct(Context *ctx) value = Value::fromString(ctx->argument(0).toString(ctx)); else value = Value::fromString(ctx, QString()); - __qmljs_init_object(&ctx->thisObject, ctx->engine->newStringObject(value)); + ctx->thisObject = __qmljs_init_object(ctx->engine->newStringObject(value)); } void StringCtor::call(Context *ctx) @@ -1017,7 +1017,7 @@ NumberCtor::NumberCtor(Context *scope) void NumberCtor::construct(Context *ctx) { const double n = ctx->argument(0).toNumber(ctx); - __qmljs_init_object(&ctx->thisObject, ctx->engine->newNumberObject(Value::fromDouble(n))); + ctx->thisObject = __qmljs_init_object(ctx->engine->newNumberObject(Value::fromDouble(n))); } void NumberCtor::call(Context *ctx) @@ -1196,7 +1196,7 @@ BooleanCtor::BooleanCtor(Context *scope) void BooleanCtor::construct(Context *ctx) { const double n = ctx->argument(0).toBoolean(ctx); - __qmljs_init_object(&ctx->thisObject, ctx->engine->newBooleanObject(Value::fromBoolean(n))); + ctx->thisObject = __qmljs_init_object(ctx->engine->newBooleanObject(Value::fromBoolean(n))); } void BooleanCtor::call(Context *ctx) @@ -2437,12 +2437,12 @@ RegExpCtor::RegExpCtor(Context *scope) void RegExpCtor::construct(Context *ctx) { - __qmljs_init_object(&ctx->thisObject, ctx->engine->newStringObject(Value::undefinedValue())); + ctx->thisObject = __qmljs_init_object(ctx->engine->newStringObject(Value::undefinedValue())); } void RegExpCtor::call(Context *ctx) { - __qmljs_init_object(&ctx->result, ctx->engine->newRegExpObject(Value::undefinedValue())); + ctx->result = __qmljs_init_object(ctx->engine->newRegExpObject(Value::undefinedValue())); } void RegExpPrototype::init(Context *ctx, const Value &ctor) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index fd8a09921d..a7163d5918 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -363,10 +363,10 @@ void InstructionSelection::visitMove(IR::Move *s) return; } else if (IR::String *str = s->source->asString()) { // ### inline - generateFunctionCall(__qmljs_init_string, t, _engine->newString(*str->value)); + generateFunctionCall2(t, __qmljs_init_string, _engine->newString(*str->value)); return; } else if (IR::Closure *clos = s->source->asClosure()) { - generateFunctionCall(__qmljs_init_closure, ContextRegister, t, TrustedImmPtr(clos->value)); + generateFunctionCall2(t, __qmljs_init_closure, TrustedImmPtr(clos->value), ContextRegister); return; } else if (IR::New *ctor = s->source->asNew()) { if (ctor->base->asName()) { diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index a8d0195783..3f9c1f43c9 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -281,6 +281,11 @@ private: move(source, dest); } + void loadArgument1(TrustedImmPtr ptr, RegisterID dest) + { + move(TrustedImmPtr(ptr), dest); + } + void loadArgument1(const Pointer& ptr, RegisterID dest) { loadPtr(ptr, dest); @@ -293,6 +298,17 @@ private: loadArgument1(addr, dest); } + void loadArgument1(VM::String* string, RegisterID dest) + { + loadArgument1(TrustedImmPtr(string), dest); + } + + void loadArgument1(TrustedImm32 imm32, RegisterID dest) + { + xorPtr(dest, dest); + move(imm32, dest); + } + void storeArgument(RegisterID src, IR::Temp *temp) { assert(temp); -- cgit v1.2.3 From a1475002d6f3171905594106d74bb8604abdf4f1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 14:17:36 +0200 Subject: Converting more runtime methods. Change-Id: I9e5d73004e377cff2819cb82240d50e73c8bda94 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 4 ++-- qmljs_runtime.cpp | 41 +++++++++++++++++++++++++---------------- qmljs_runtime.h | 18 +++++++++--------- qv4isel_masm.cpp | 16 ++++++++-------- qv4isel_masm_p.h | 50 ++++++++++++++++++++++++++++++++++++++------------ 5 files changed, 82 insertions(+), 47 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index bea01d2dd9..b69c8835a8 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -223,7 +223,7 @@ String *__qmljs_llvm_identifier_from_utf8(Context *ctx, const char *str) void __qmljs_llvm_call_activation_property(Context *context, Value *result, String *name, Value *args, int argc) { - __qmljs_call_activation_property(context, result, name, args, argc); + *result = __qmljs_call_activation_property(context, name, args, argc); } void __qmljs_llvm_call_value(Context *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) @@ -233,7 +233,7 @@ void __qmljs_llvm_call_value(Context *context, Value *result, const Value *thisO void __qmljs_llvm_construct_activation_property(Context *context, Value *result, String *name, Value *args, int argc) { - __qmljs_construct_activation_property(context, result, name, args, argc); + *result = __qmljs_construct_activation_property(context, name, args, argc); } void __qmljs_llvm_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index eb2038b266..55fbc9fd9a 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -774,10 +774,10 @@ double __qmljs_string_to_number(Context *, String *string) return string->toQString().toDouble(&ok); // ### TODO } -void __qmljs_string_from_number(Context *ctx, Value *result, double number) +Value __qmljs_string_from_number(Context *ctx, double number) { String *string = ctx->engine->newString(numberToString(number, 10)); - *result = Value::fromString(string); + return Value::fromString(string); } uint __qmljs_string_compare(Context *, String *left, String *right) @@ -795,9 +795,9 @@ String *__qmljs_string_concat(Context *ctx, String *first, String *second) return ctx->engine->newString(first->toQString() + second->toQString()); } -uint __qmljs_is_function(Context *, const Value *value) +uint __qmljs_is_function(const Value value) { - return value->objectValue()->asFunctionObject() != 0; + return value.objectValue()->asFunctionObject() != 0; } void __qmljs_object_default_value(Context *ctx, Value *result, const Value *object, int typeHint) @@ -1109,13 +1109,16 @@ uint __qmljs_equal(Context *ctx, const Value *x, const Value *y) return false; } -void __qmljs_call_activation_property(Context *context, Value *result, String *name, Value *args, int argc) +Value __qmljs_call_activation_property(Context *context, String *name, Value *args, int argc) { Value *func = context->lookupPropertyDescriptor(name); - if (! func) + if (! func) { context->throwReferenceError(Value::fromString(name)); - else - __qmljs_call_value(context, result, /*thisObject=*/ 0, func, args, argc); + return Value::undefinedValue(); + } + Value result; + __qmljs_call_value(context, &result, /*thisObject=*/ 0, func, args, argc); + return result; } void __qmljs_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) @@ -1165,13 +1168,16 @@ void __qmljs_call_value(Context *context, Value *result, const Value *thisObject } } -void __qmljs_construct_activation_property(Context *context, Value *result, String *name, Value *args, int argc) +Value __qmljs_construct_activation_property(Context *context, String *name, Value *args, int argc) { Value *func = context->lookupPropertyDescriptor(name); - if (! func) + if (! func) { context->throwReferenceError(Value::fromString(name)); - else - __qmljs_construct_value(context, result, func, args, argc); + return Value::undefinedValue(); + } + Value result; + __qmljs_construct_value(context, &result, func, args, argc); + return result; } void __qmljs_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc) @@ -1226,17 +1232,20 @@ void __qmljs_rethrow(Context *context, Value *result) *result = context->result; } -void __qmljs_builtin_typeof(Context *context, Value *result, Value *args, int argc) +Value __qmljs_builtin_typeof(Context *context, Value *args, int argc) { Q_UNUSED(argc); - __qmljs_typeof(context, result, &args[0]); + Value result; + __qmljs_typeof(context, &result, &args[0]); + return result; } -void __qmljs_builtin_throw(Context *context, Value *result, Value *args, int argc) +Value __qmljs_builtin_throw(Context *context, Value *args, int argc) { Q_UNUSED(argc); - Q_UNUSED(result); __qmljs_throw(context, &args[0]); + // ### change to void return value + return Value::undefinedValue(); } void __qmljs_builtin_rethrow(Context *context, Value *result, Value *, int) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 371675ab78..fbcca89530 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -89,16 +89,16 @@ struct ExecutionEngine; extern "C" { // context -void __qmljs_call_activation_property(Context *, Value *result, String *name, Value *args, int argc); +Value __qmljs_call_activation_property(Context *, String *name, Value *args, int argc); void __qmljs_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc); void __qmljs_call_value(Context *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc); -void __qmljs_construct_activation_property(Context *, Value *result, String *name, Value *args, int argc); +Value __qmljs_construct_activation_property(Context *, String *name, Value *args, int argc); void __qmljs_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc); void __qmljs_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc); -void __qmljs_builtin_typeof(Context *context, Value *result, Value *args, int argc); -void __qmljs_builtin_throw(Context *context, Value *result, Value *args, int argc); +Value __qmljs_builtin_typeof(Context *context, Value *args, int argc); +Value __qmljs_builtin_throw(Context *context, Value *args, int argc); void __qmljs_builtin_rethrow(Context *context, Value *result, Value *args, int argc); // constructors @@ -107,7 +107,7 @@ Value __qmljs_init_object(Object *object); Value __qmljs_init_closure(IR::Function *clos, Context *ctx); Value __qmljs_init_native_function(void (*code)(Context *), Context *ctx); -uint __qmljs_is_function(Context *ctx, const Value *value); +uint __qmljs_is_function(const Value value); // string literals Value __qmljs_string_literal_undefined(Context *ctx); @@ -124,7 +124,7 @@ Value __qmljs_string_literal_function(Context *ctx); String *__qmljs_string_from_utf8(Context *ctx, const char *s); int __qmljs_string_length(Context *ctx, String *string); double __qmljs_string_to_number(Context *ctx, String *string); -void __qmljs_string_from_number(Context *ctx, Value *result, double number); +Value __qmljs_string_from_number(Context *ctx, double number); uint __qmljs_string_compare(Context *ctx, String *left, String *right); uint __qmljs_string_equal(Context *ctx, String *left, String *right); String *__qmljs_string_concat(Context *ctx, String *first, String *second); @@ -831,10 +831,10 @@ inline void __qmljs_to_string(Context *ctx, Value *result, const Value *value) break; } case Value::Integer_Type: - __qmljs_string_from_number(ctx, result, value->int_32); + *result = __qmljs_string_from_number(ctx, value->int_32); break; default: // number - __qmljs_string_from_number(ctx, result, value->asDouble()); + *result = __qmljs_string_from_number(ctx, value->doubleValue()); break; } // switch @@ -880,7 +880,7 @@ inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Va inline uint __qmljs_is_callable(Context *ctx, const Value *value) { if (value->isObject()) - return __qmljs_is_function(ctx, value); + return __qmljs_is_function(*value); else return false; } diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index a7163d5918..fc5c743ac4 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -196,20 +196,20 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu assert(baseName != 0); if (baseName->id) - callRuntimeMethod(__qmljs_call_activation_property, result, call->base, call->args); + callRuntimeMethod(result, __qmljs_call_activation_property, call->base, call->args); else { switch (baseName->builtin) { case IR::Name::builtin_invalid: Q_UNREACHABLE(); break; case IR::Name::builtin_typeof: - callRuntimeMethod(__qmljs_builtin_typeof, result, call->args); + callRuntimeMethod(result, __qmljs_builtin_typeof, call->args); break; case IR::Name::builtin_delete: Q_UNREACHABLE(); break; case IR::Name::builtin_throw: - callRuntimeMethod(__qmljs_builtin_throw, result, call->args); + callRuntimeMethod(result, __qmljs_builtin_throw, call->args); break; case IR::Name::builtin_rethrow: { int argc = prepareVariableArguments(call->args); @@ -249,7 +249,7 @@ void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp * IR::Name *baseName = call->base->asName(); assert(baseName != 0); - callRuntimeMethod(__qmljs_construct_activation_property, result, call->base, call->args); + callRuntimeMethod(result, __qmljs_construct_activation_property, call->base, call->args); } void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) @@ -706,20 +706,20 @@ int InstructionSelection::prepareVariableArguments(IR::ExprList* args) return argc; } -void InstructionSelection::callRuntimeMethodImp(const char* name, ActivationMethod method, IR::Temp *result, IR::Expr *base, IR::ExprList *args) +void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, ActivationMethod method, IR::Expr *base, IR::ExprList *args) { IR::Name *baseName = base->asName(); assert(baseName != 0); int argc = prepareVariableArguments(args); - generateFunctionCallImp(name, method, ContextRegister, result, identifier(*baseName->id), baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCallImp2(result, name, method, ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } -void InstructionSelection::callRuntimeMethodImp(const char* name, BuiltinMethod method, IR::Temp *result, IR::ExprList *args) +void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args) { int argc = prepareVariableArguments(args); - generateFunctionCallImp(name, method, ContextRegister, result, baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCallImp2(result, name, method, ContextRegister, baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 3f9c1f43c9..b8a133eaba 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -288,14 +288,14 @@ private: void loadArgument1(const Pointer& ptr, RegisterID dest) { - loadPtr(ptr, dest); + addPtr(TrustedImm32(ptr.offset), ptr.base, dest); } void loadArgument1(IR::Temp* temp, RegisterID dest) { assert(temp); Pointer addr = loadTempAddress(dest, temp); - loadArgument1(addr, dest); + loadPtr(addr, dest); } void loadArgument1(VM::String* string, RegisterID dest) @@ -311,10 +311,11 @@ private: void storeArgument(RegisterID src, IR::Temp *temp) { - assert(temp); - // ### Should use some ScratchRegister here - Pointer addr = loadTempAddress(Gpr3, temp); - storePtr(src, addr); + if (temp) { + // ### Should use some ScratchRegister here + Pointer addr = loadTempAddress(Gpr3, temp); + storePtr(src, addr); + } } void storeArgument(RegisterID src, RegisterID dest) @@ -508,6 +509,31 @@ private: callFunctionEpilogue(); } + template + void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + callFunctionPrologue(); + loadArgument1(arg1, RegisterArgument1); + loadArgument1(arg2, RegisterArgument2); + loadArgument1(arg3, RegisterArgument3); + callAbsolute(functionName, function); + storeArgument(ReturnValueRegister, r); + callFunctionEpilogue(); + } + + template + void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + callFunctionPrologue(); + loadArgument1(arg1, RegisterArgument1); + loadArgument1(arg2, RegisterArgument2); + loadArgument1(arg3, RegisterArgument3); + loadArgument1(arg4, RegisterArgument4); + callAbsolute(functionName, function); + storeArgument(ReturnValueRegister, r); + callFunctionEpilogue(); + } + template void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) { @@ -598,12 +624,12 @@ private: int prepareVariableArguments(IR::ExprList* args); - typedef void (*ActivationMethod)(VM::Context *, VM::Value *result, VM::String *name, VM::Value *args, int argc); - typedef void (*BuiltinMethod)(VM::Context *, VM::Value *result, VM::Value *args, int argc); - void callRuntimeMethodImp(const char* name, ActivationMethod method, IR::Temp *result, IR::Expr *base, IR::ExprList *args); - void callRuntimeMethodImp(const char* name, BuiltinMethod method, IR::Temp *result, IR::ExprList *args); -#define callRuntimeMethod(function, ...) \ - callRuntimeMethodImp(isel_stringIfy(function), function, __VA_ARGS__) + typedef VM::Value (*ActivationMethod)(VM::Context *, VM::String *name, VM::Value *args, int argc); + typedef VM::Value (*BuiltinMethod)(VM::Context *, VM::Value *args, int argc); + void callRuntimeMethodImp(IR::Temp *result, const char* name, ActivationMethod method, IR::Expr *base, IR::ExprList *args); + void callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args); +#define callRuntimeMethod(result, function, ...) \ + callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__) using JSC::MacroAssembler::loadDouble; void loadDouble(IR::Temp* temp, FPRegisterID dest) -- cgit v1.2.3 From 3cdb661ee00dee0394df833d37d01592ce5b1ef2 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 15:10:47 +0200 Subject: Converting more methods to the new calling convention Change-Id: I5199a9e5439644cc259f131c8b0ba6a941989615 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 5 +++-- moth/qv4vme_moth.cpp | 2 +- qmljs_runtime.cpp | 38 ++++++++++++++++++++------------------ qmljs_runtime.h | 6 +++--- qv4array.cpp | 3 +-- qv4ecmaobjects.cpp | 25 +++++++++---------------- qv4isel_masm.cpp | 6 ++---- qv4isel_masm_p.h | 32 +++++++++++++++++++++++++------- 8 files changed, 64 insertions(+), 53 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index b69c8835a8..31bdd907e0 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -228,7 +228,8 @@ void __qmljs_llvm_call_activation_property(Context *context, Value *result, Stri void __qmljs_llvm_call_value(Context *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) { - __qmljs_call_value(context, result, thisObject, func, args, argc); + Value that = thisObject ? *thisObject : Value::undefinedValue(); + *result = __qmljs_call_value(context, that, func, args, argc); } void __qmljs_llvm_construct_activation_property(Context *context, Value *result, String *name, Value *args, int argc) @@ -258,7 +259,7 @@ void __qmljs_llvm_get_property(Context *ctx, Value *result, Value *object, Strin void __qmljs_llvm_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) { - __qmljs_call_property(context, result, base, name, args, argc); + *result = __qmljs_call_property(context, *base, name, args, argc); } void __qmljs_llvm_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index f244f1cb1a..3bbbfe6f25 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -133,7 +133,7 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_BEGIN_INSTR(Call) VM::Value *args = stack.data() + instr.args; - __qmljs_call_value(context, &tempRegister, /*thisObject=*/0, &tempRegister, args, instr.argc); + tempRegister = __qmljs_call_value(context, VM::Value::undefinedValue(), &tempRegister, args, instr.argc); MOTH_END_INSTR(Call) MOTH_BEGIN_INSTR(Jump) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 55fbc9fd9a..45de64afad 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -368,11 +368,8 @@ void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionO std::fill(locals, locals + varCount, Value::undefinedValue()); } -void Context::leaveCallContext(FunctionObject *f, Value *returnValue) +void Context::leaveCallContext(FunctionObject *f) { - if (returnValue) - *returnValue = result; - if (! f->needsActivation) { delete[] locals; locals = 0; @@ -395,7 +392,8 @@ void Context::leaveConstructorContext(FunctionObject *f, Value *returnValue) if (! thisObject.isObject()) thisObject.objectValue()->prototype = engine->objectPrototype; - leaveCallContext(f, returnValue); + leaveCallContext(f); + *returnValue = result; } extern "C" { @@ -820,8 +818,7 @@ void __qmljs_object_default_value(Context *ctx, Value *result, const Value *obje Value conv = oo->getProperty(ctx, meth1); if (!conv.isUndefined() && conv.isFunctionObject()) { - Value r; - __qmljs_call_value(ctx, &r, object, &conv, 0, 0); + Value r = __qmljs_call_value(ctx, *object, &conv, 0, 0); if (r.isPrimitive()) { *result = r; return; @@ -830,8 +827,7 @@ void __qmljs_object_default_value(Context *ctx, Value *result, const Value *obje conv = oo->getProperty(ctx, meth2); if (!conv.isUndefined() && conv.isFunctionObject()) { - Value r; - __qmljs_call_value(ctx, &r, object, &conv, 0, 0); + Value r = __qmljs_call_value(ctx, *object, &conv, 0, 0); if (r.isPrimitive()) { *result = r; return; @@ -1116,17 +1112,16 @@ Value __qmljs_call_activation_property(Context *context, String *name, Value *ar context->throwReferenceError(Value::fromString(name)); return Value::undefinedValue(); } - Value result; - __qmljs_call_value(context, &result, /*thisObject=*/ 0, func, args, argc); + Value result = __qmljs_call_value(context, Value::undefinedValue(), func, args, argc); return result; } -void __qmljs_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) +Value __qmljs_call_property(Context *context, const Value base, String *name, Value *args, int argc) { Value baseObject; Value thisObject; - if (base) { - baseObject = *base; + if (!base.isUndefined()) { + baseObject = base; if (!baseObject.isObject()) __qmljs_to_object(context, &baseObject, &baseObject); assert(baseObject.isObject()); @@ -1136,6 +1131,7 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S thisObject = Value::nullValue(); } Value func = baseObject.property(context, name); + Value result; if (FunctionObject *f = func.asFunctionObject()) { Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; @@ -1145,27 +1141,33 @@ void __qmljs_call_property(Context *context, Value *result, const Value *base, S context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception context->result = ctx->result; } - ctx->leaveCallContext(f, result); + ctx->leaveCallContext(f); + result = ctx->result; } else { context->throwTypeError(); } + return result; } -void __qmljs_call_value(Context *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) +Value __qmljs_call_value(Context *context, const Value thisObject, const Value *func, Value *args, int argc) { + Value result; if (FunctionObject *f = func->asFunctionObject()) { Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->initCallContext(context->engine, thisObject, f, args, argc); + const Value *that = thisObject.isUndefined() ? 0 : &thisObject; + ctx->initCallContext(context->engine, that, f, args, argc); f->call(ctx); if (ctx->hasUncaughtException) { context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception context->result = ctx->result; } - ctx->leaveCallContext(f, result); + ctx->leaveCallContext(f); + result = ctx->result; } else { context->throwTypeError(); } + return result; } Value __qmljs_construct_activation_property(Context *context, String *name, Value *args, int argc) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index fbcca89530..9c5c7cf7f1 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -90,8 +90,8 @@ extern "C" { // context Value __qmljs_call_activation_property(Context *, String *name, Value *args, int argc); -void __qmljs_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc); -void __qmljs_call_value(Context *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc); +Value __qmljs_call_property(Context *context, const Value base, String *name, Value *args, int argc); +Value __qmljs_call_value(Context *context, const Value thisObject, const Value *func, Value *args, int argc); Value __qmljs_construct_activation_property(Context *, String *name, Value *args, int argc); void __qmljs_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc); @@ -648,7 +648,7 @@ struct Context { #endif void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc); - void leaveCallContext(FunctionObject *f, Value *r); + void leaveCallContext(FunctionObject *f); void initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc); void leaveConstructorContext(FunctionObject *f, Value *returnValue); diff --git a/qv4array.cpp b/qv4array.cpp index a2f2f9eb35..7923b1e3a9 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -53,8 +53,7 @@ bool ArrayElementLessThan::operator()(const Value &v1, const Value &v2) const return true; if (!m_comparefn.isUndefined()) { Value args[] = { v1, v2 }; - Value result; - __qmljs_call_value(m_context, &result, 0, &m_comparefn, args, 2); + Value result = __qmljs_call_value(m_context, Value::undefinedValue(), &m_comparefn, args, 2); return result.toNumber(m_context) <= 0; } return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 16476a2d89..1f0f82fcb2 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1550,8 +1550,7 @@ void ArrayPrototype::method_every(Context *ctx) args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r; - __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); + Value r = __qmljs_call_value(ctx, thisArg, &callback, args, 3); ok = __qmljs_to_boolean(r, ctx); } ctx->result = Value::fromBoolean(ok); @@ -1580,8 +1579,7 @@ void ArrayPrototype::method_some(Context *ctx) args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r; - __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); + Value r = __qmljs_call_value(ctx, thisArg, &callback, args, 3); ok = __qmljs_to_boolean(r, ctx); } ctx->result = Value::fromBoolean(ok); @@ -1606,12 +1604,11 @@ void ArrayPrototype::method_forEach(Context *ctx) Value v = instance->value.at(k); if (v.isUndefined()) continue; - Value r; Value args[3]; args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); + Value r = __qmljs_call_value(ctx, thisArg, &callback, args, 3); } } } else { @@ -1634,12 +1631,11 @@ void ArrayPrototype::method_map(Context *ctx) Value v = instance->value.at(k); if (v.isUndefined()) continue; - Value r; Value args[3]; args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); + Value r = __qmljs_call_value(ctx, thisArg, &callback, args, 3); a->value.assign(k, r); } ctx->result = Value::fromObject(a); @@ -1663,12 +1659,11 @@ void ArrayPrototype::method_filter(Context *ctx) Value v = instance->value.at(k); if (v.isUndefined()) continue; - Value r; Value args[3]; args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - __qmljs_call_value(ctx, &r, &thisArg, &callback, args, 3); + Value r = __qmljs_call_value(ctx, thisArg, &callback, args, 3); if (__qmljs_to_boolean(r, ctx)) { const uint index = a->value.size(); a->value.resize(index + 1); @@ -1699,13 +1694,12 @@ void ArrayPrototype::method_reduce(Context *ctx) continue; } - Value r; Value args[4]; args[0] = acc; args[1] = v; args[2] = Value::fromDouble(k); args[3] = ctx->thisObject; - __qmljs_call_value(ctx, &r, 0, &callback, args, 4); + Value r = __qmljs_call_value(ctx, Value::undefinedValue(), &callback, args, 4); acc = r; } ctx->result = acc; @@ -1731,13 +1725,12 @@ void ArrayPrototype::method_reduceRight(Context *ctx) continue; } - Value r; Value args[4]; args[0] = acc; args[1] = v; args[2] = Value::fromDouble(k); args[3] = ctx->thisObject; - __qmljs_call_value(ctx, &r, 0, &callback, args, 4); + Value r = __qmljs_call_value(ctx, Value::undefinedValue(), &callback, args, 4); acc = r; } ctx->result = acc; @@ -1807,7 +1800,7 @@ void FunctionPrototype::method_apply(Context *ctx) return; } - __qmljs_call_value(ctx, &ctx->result, &thisObject, &ctx->thisObject, args.data(), args.size()); + ctx->result = __qmljs_call_value(ctx, thisObject, &ctx->thisObject, args.data(), args.size()); } else { ctx->throwTypeError(); } @@ -1821,7 +1814,7 @@ void FunctionPrototype::method_call(Context *ctx) QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); if (ctx->argumentCount) qCopy(ctx->arguments + 1, ctx->arguments + ctx->argumentCount, args.begin()); - __qmljs_call_value(ctx, &ctx->result, &thisArg, &ctx->thisObject, args.data(), args.size()); + ctx->result = __qmljs_call_value(ctx, thisArg, &ctx->thisObject, args.data(), args.size()); } else { ctx->throwTypeError(); } diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index fc5c743ac4..47f9f88ac8 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -228,7 +228,7 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) int argc = prepareVariableArguments(call->args); IR::Temp* thisObject = 0; - generateFunctionCall(__qmljs_call_value, ContextRegister, result, baseTemp, thisObject, baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall2(result, __qmljs_call_value, ContextRegister, baseTemp, thisObject, baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } @@ -239,8 +239,7 @@ void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) assert(member->base->asTemp() != 0); int argc = prepareVariableArguments(call->args); - IR::Temp* thisObject = 0; - generateFunctionCall(__qmljs_call_property, ContextRegister, result, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall2(result, __qmljs_call_property, ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } @@ -259,7 +258,6 @@ void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) assert(member->base->asTemp() != 0); int argc = prepareVariableArguments(call->args); - IR::Temp* thisObject = 0; generateFunctionCall(__qmljs_construct_property, ContextRegister, result, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index b8a133eaba..580ecea741 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -293,9 +293,13 @@ private: void loadArgument1(IR::Temp* temp, RegisterID dest) { - assert(temp); - Pointer addr = loadTempAddress(dest, temp); - loadPtr(addr, dest); + if (!temp) { + VM::Value undefined = VM::Value::undefinedValue(); + move(TrustedImmPtr((const void *)undefined.val), dest); + } else { + Pointer addr = loadTempAddress(dest, temp); + loadPtr(addr, dest); + } } void loadArgument1(VM::String* string, RegisterID dest) @@ -509,26 +513,40 @@ private: callFunctionEpilogue(); } - template - void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + template + void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { callFunctionPrologue(); loadArgument1(arg1, RegisterArgument1); loadArgument1(arg2, RegisterArgument2); loadArgument1(arg3, RegisterArgument3); + loadArgument1(arg4, RegisterArgument4); callAbsolute(functionName, function); storeArgument(ReturnValueRegister, r); callFunctionEpilogue(); } - template - void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + template + void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { callFunctionPrologue(); loadArgument1(arg1, RegisterArgument1); loadArgument1(arg2, RegisterArgument2); loadArgument1(arg3, RegisterArgument3); loadArgument1(arg4, RegisterArgument4); + loadArgument1(arg5, RegisterArgument5); + callAbsolute(functionName, function); + storeArgument(ReturnValueRegister, r); + callFunctionEpilogue(); + } + + template + void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + callFunctionPrologue(); + loadArgument1(arg1, RegisterArgument1); + loadArgument1(arg2, RegisterArgument2); + loadArgument1(arg3, RegisterArgument3); callAbsolute(functionName, function); storeArgument(ReturnValueRegister, r); callFunctionEpilogue(); -- cgit v1.2.3 From 644804d6b82cf4832b659881997d313ad9d84aa5 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 15:37:31 +0200 Subject: More conversions to new calling convention Change-Id: I6002715cc3f5c22d90a9ade6ae2152c2c3c8ebb2 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 10 +++++----- qmljs_runtime.cpp | 49 +++++++++++++++++++++++-------------------------- qmljs_runtime.h | 16 ++++++++-------- qv4isel_masm.cpp | 6 +++--- 4 files changed, 39 insertions(+), 42 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 31bdd907e0..586bca4d15 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -239,7 +239,7 @@ void __qmljs_llvm_construct_activation_property(Context *context, Value *result, void __qmljs_llvm_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc) { - __qmljs_construct_value(context, result, func, args, argc); + *result = __qmljs_construct_value(context, *func, args, argc); } void __qmljs_llvm_get_activation_property(Context *ctx, Value *result, String *name) @@ -264,7 +264,7 @@ void __qmljs_llvm_call_property(Context *context, Value *result, const Value *ba void __qmljs_llvm_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) { - __qmljs_construct_property(context, result, base, name, args, argc); + *result = __qmljs_construct_property(context, *base, name, args, argc); } void __qmljs_llvm_get_element(Context *ctx, Value *result, Value *object, Value *index) @@ -284,17 +284,17 @@ void __qmljs_llvm_set_property(Context *ctx, Value *object, String *name, Value void __qmljs_llvm_typeof(Context *ctx, Value *result, const Value *value) { - __qmljs_typeof(ctx, result, value); + *result = __qmljs_typeof(ctx, value); } void __qmljs_llvm_throw(Context *context, Value *value) { - __qmljs_throw(context, value); + __qmljs_throw(context, *value); } void __qmljs_llvm_rethrow(Context *context, Value *result) { - __qmljs_rethrow(context, result); + *result = __qmljs_rethrow(context); } void __qmljs_llvm_get_this_object(Context *ctx, Value *result) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 45de64afad..b892eb4854 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -382,7 +382,7 @@ void Context::initConstructorContext(ExecutionEngine *e, const Value *object, Fu calledAsConstructor = true; } -void Context::leaveConstructorContext(FunctionObject *f, Value *returnValue) +void Context::leaveConstructorContext(FunctionObject *f) { assert(thisObject.is(Value::Object_Type)); result = thisObject; @@ -393,7 +393,6 @@ void Context::leaveConstructorContext(FunctionObject *f, Value *returnValue) thisObject.objectValue()->prototype = engine->objectPrototype; leaveCallContext(f); - *returnValue = result; } extern "C" { @@ -1177,14 +1176,12 @@ Value __qmljs_construct_activation_property(Context *context, String *name, Valu context->throwReferenceError(Value::fromString(name)); return Value::undefinedValue(); } - Value result; - __qmljs_construct_value(context, &result, func, args, argc); - return result; + return __qmljs_construct_value(context, *func, args, argc); } -void __qmljs_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc) +Value __qmljs_construct_value(Context *context, const Value func, Value *args, int argc) { - if (FunctionObject *f = func->asFunctionObject()) { + if (FunctionObject *f = func.asFunctionObject()) { Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; ctx->initConstructorContext(context->engine, 0, f, args, argc); @@ -1193,17 +1190,18 @@ void __qmljs_construct_value(Context *context, Value *result, const Value *func, context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception context->result = ctx->result; } - ctx->leaveConstructorContext(f, result); - } else { - context->throwTypeError(); + ctx->leaveConstructorContext(f); + return ctx->result; } + context->throwTypeError(); + return Value::undefinedValue(); } -void __qmljs_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) +Value __qmljs_construct_property(Context *context, const Value base, String *name, Value *args, int argc) { - Value thisObject = *base; + Value thisObject = base; if (!thisObject.isObject()) - __qmljs_to_object(context, &thisObject, base); + __qmljs_to_object(context, &thisObject, &base); assert(thisObject.isObject()); Value func = thisObject.property(context, name); @@ -1217,42 +1215,41 @@ void __qmljs_construct_property(Context *context, Value *result, const Value *ba context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception context->result = ctx->result; } - ctx->leaveConstructorContext(f, result); - } else { - context->throwTypeError(); + ctx->leaveConstructorContext(f); + return ctx->result; } + context->throwTypeError(); + return Value::undefinedValue(); } -void __qmljs_throw(Context *context, Value *value) +void __qmljs_throw(Context *context, Value value) { context->hasUncaughtException = true; - context->result = *value; + context->result = value; } -void __qmljs_rethrow(Context *context, Value *result) +Value __qmljs_rethrow(Context *context) { - *result = context->result; + return context->result; } Value __qmljs_builtin_typeof(Context *context, Value *args, int argc) { Q_UNUSED(argc); - Value result; - __qmljs_typeof(context, &result, &args[0]); - return result; + return __qmljs_typeof(context, &args[0]); } Value __qmljs_builtin_throw(Context *context, Value *args, int argc) { Q_UNUSED(argc); - __qmljs_throw(context, &args[0]); + __qmljs_throw(context, args[0]); // ### change to void return value return Value::undefinedValue(); } -void __qmljs_builtin_rethrow(Context *context, Value *result, Value *, int) +Value __qmljs_builtin_rethrow(Context *context, Value *, int) { - __qmljs_rethrow(context, result); + return context->result; } } // extern "C" diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 9c5c7cf7f1..b494b6eba1 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -94,12 +94,12 @@ Value __qmljs_call_property(Context *context, const Value base, String *name, Va Value __qmljs_call_value(Context *context, const Value thisObject, const Value *func, Value *args, int argc); Value __qmljs_construct_activation_property(Context *, String *name, Value *args, int argc); -void __qmljs_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc); -void __qmljs_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc); +Value __qmljs_construct_property(Context *context, const Value base, String *name, Value *args, int argc); +Value __qmljs_construct_value(Context *context, const Value func, Value *args, int argc); Value __qmljs_builtin_typeof(Context *context, Value *args, int argc); Value __qmljs_builtin_throw(Context *context, Value *args, int argc); -void __qmljs_builtin_rethrow(Context *context, Value *result, Value *args, int argc); +Value __qmljs_builtin_rethrow(Context *context, Value *args, int argc); // constructors Value __qmljs_init_string(String *string); @@ -181,9 +181,9 @@ void __qmljs_delete_member(Context *ctx, Value *result, Value *base, String *nam void __qmljs_delete_property(Context *ctx, Value *result, String *name); void __qmljs_delete_value(Context *ctx, Value *result, Value *value); -void __qmljs_typeof(Context *ctx, Value *result, const Value *value); -void __qmljs_throw(Context *context, Value *value); -void __qmljs_rethrow(Context *context, Value *result); +Value __qmljs_typeof(Context *ctx, const Value *value); +void __qmljs_throw(Context *context, Value value); +Value __qmljs_rethrow(Context *context); // binary operators void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Value *right); @@ -651,7 +651,7 @@ struct Context { void leaveCallContext(FunctionObject *f); void initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc); - void leaveConstructorContext(FunctionObject *f, Value *returnValue); + void leaveConstructorContext(FunctionObject *f); }; @@ -895,7 +895,7 @@ inline void __qmljs_default_value(Context *ctx, Value *result, const Value *valu // unary operators -inline void __qmljs_typeof(Context *ctx, Value *result, const Value *value) +inline Value __qmljs_typeof(Context *ctx, const Value *value) { switch (value->type()) { case Value::Undefined_Type: diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 47f9f88ac8..19d61146d6 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -213,7 +213,7 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu break; case IR::Name::builtin_rethrow: { int argc = prepareVariableArguments(call->args); - generateFunctionCall(__qmljs_builtin_rethrow, ContextRegister, result, baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall2(result, __qmljs_builtin_rethrow, ContextRegister, baseAddressForCallArguments(), TrustedImm32(argc)); return; // we need to return to avoid checking the exceptions } } @@ -258,7 +258,7 @@ void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) assert(member->base->asTemp() != 0); int argc = prepareVariableArguments(call->args); - generateFunctionCall(__qmljs_construct_property, ContextRegister, result, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall2(result, __qmljs_construct_property, ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } @@ -268,7 +268,7 @@ void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) assert(baseTemp != 0); int argc = prepareVariableArguments(call->args); - generateFunctionCall(__qmljs_construct_value, ContextRegister, result, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall2(result, __qmljs_construct_value, ContextRegister, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } -- cgit v1.2.3 From f730f7526b89a7add27d93454bddffe6410d5542 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 15:47:46 +0200 Subject: More conversions to new calling convention Change-Id: Iac67c9923bc3d7af76bb490cc8ca7cd3e922e192 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 2 +- qmljs_runtime.cpp | 26 +++++++++++--------------- qmljs_runtime.h | 40 +++++++++++++++++++--------------------- 3 files changed, 31 insertions(+), 37 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 586bca4d15..ba178a0158 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -284,7 +284,7 @@ void __qmljs_llvm_set_property(Context *ctx, Value *object, String *name, Value void __qmljs_llvm_typeof(Context *ctx, Value *result, const Value *value) { - *result = __qmljs_typeof(ctx, value); + *result = __qmljs_typeof(ctx, *value); } void __qmljs_llvm_throw(Context *context, Value *value) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index b892eb4854..01080bec8b 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -797,10 +797,10 @@ uint __qmljs_is_function(const Value value) return value.objectValue()->asFunctionObject() != 0; } -void __qmljs_object_default_value(Context *ctx, Value *result, const Value *object, int typeHint) +Value __qmljs_object_default_value(Context *ctx, const Value object, int typeHint) { if (typeHint == PREFERREDTYPE_HINT) { - if (object->isDateObject()) + if (object.isDateObject()) typeHint = STRING_HINT; else typeHint = NUMBER_HINT; @@ -812,28 +812,24 @@ void __qmljs_object_default_value(Context *ctx, Value *result, const Value *obje if (typeHint == NUMBER_HINT) qSwap(meth1, meth2); - Object *oo = object->asObject(); + Object *oo = object.asObject(); assert(oo != 0); Value conv = oo->getProperty(ctx, meth1); if (!conv.isUndefined() && conv.isFunctionObject()) { - Value r = __qmljs_call_value(ctx, *object, &conv, 0, 0); - if (r.isPrimitive()) { - *result = r; - return; - } + Value r = __qmljs_call_value(ctx, object, &conv, 0, 0); + if (r.isPrimitive()) + return r; } conv = oo->getProperty(ctx, meth2); if (!conv.isUndefined() && conv.isFunctionObject()) { - Value r = __qmljs_call_value(ctx, *object, &conv, 0, 0); - if (r.isPrimitive()) { - *result = r; - return; - } + Value r = __qmljs_call_value(ctx, object, &conv, 0, 0); + if (r.isPrimitive()) + return r; } - *result = Value::undefinedValue(); + return Value::undefinedValue(); } void __qmljs_throw_type_error(Context *ctx, Value *result) @@ -1236,7 +1232,7 @@ Value __qmljs_rethrow(Context *context) Value __qmljs_builtin_typeof(Context *context, Value *args, int argc) { Q_UNUSED(argc); - return __qmljs_typeof(context, &args[0]); + return __qmljs_typeof(context, args[0]); } Value __qmljs_builtin_throw(Context *context, Value *args, int argc) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index b494b6eba1..dfbf35e0d6 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -131,7 +131,7 @@ String *__qmljs_string_concat(Context *ctx, String *first, String *second); String *__qmljs_identifier_from_utf8(Context *ctx, const char *s); // objects -void __qmljs_object_default_value(Context *ctx, Value *result, const Value *object, int typeHint); +Value __qmljs_object_default_value(Context *ctx, const Value object, int typeHint); void __qmljs_throw_type_error(Context *ctx, Value *result); void __qmljs_new_object(Context *ctx, Value *result); void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean); @@ -164,7 +164,7 @@ void __qmljs_to_string(Context *ctx, Value *result, const Value *value); void __qmljs_to_object(Context *ctx, Value *result, const Value *value); uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value); uint __qmljs_is_callable(Context *ctx, const Value *value); -void __qmljs_default_value(Context *ctx, Value *result, const Value *value, int typeHint); +Value __qmljs_default_value(Context *ctx, const Value value, int typeHint); void __qmljs_compare(Context *ctx, Value *result, const Value *left, const Value *right, bool leftFlag); uint __qmljs_equal(Context *ctx, const Value *x, const Value *y); @@ -181,7 +181,7 @@ void __qmljs_delete_member(Context *ctx, Value *result, Value *base, String *nam void __qmljs_delete_property(Context *ctx, Value *result, String *name); void __qmljs_delete_value(Context *ctx, Value *result, Value *value); -Value __qmljs_typeof(Context *ctx, const Value *value); +Value __qmljs_typeof(Context *ctx, const Value value); void __qmljs_throw(Context *context, Value value); Value __qmljs_rethrow(Context *context); @@ -676,7 +676,7 @@ inline void __qmljs_to_primitive(Context *ctx, Value *result, const Value *value if (!value->isObject()) *result = *value; else - __qmljs_default_value(ctx, result, value, typeHint); + *result = __qmljs_default_value(ctx, *value, typeHint); } inline uint __qmljs_to_boolean(const Value value, Context *ctx) @@ -885,40 +885,38 @@ inline uint __qmljs_is_callable(Context *ctx, const Value *value) return false; } -inline void __qmljs_default_value(Context *ctx, Value *result, const Value *value, int typeHint) +inline Value __qmljs_default_value(Context *ctx, const Value value, int typeHint) { - if (value->isObject()) - __qmljs_object_default_value(ctx, result, value, typeHint); - else - *result = Value::undefinedValue(); + if (value.isObject()) + return __qmljs_object_default_value(ctx, value, typeHint); + return Value::undefinedValue(); } // unary operators -inline Value __qmljs_typeof(Context *ctx, const Value *value) +inline Value __qmljs_typeof(Context *ctx, const Value value) { - switch (value->type()) { + switch (value.type()) { case Value::Undefined_Type: - *result = __qmljs_string_literal_undefined(ctx); + return __qmljs_string_literal_undefined(ctx); break; case Value::Null_Type: - *result = __qmljs_string_literal_object(ctx); + return __qmljs_string_literal_object(ctx); break; case Value::Boolean_Type: - *result = __qmljs_string_literal_boolean(ctx); + return __qmljs_string_literal_boolean(ctx); break; case Value::String_Type: - *result = __qmljs_string_literal_string(ctx); + return __qmljs_string_literal_string(ctx); break; case Value::Object_Type: - if (__qmljs_is_callable(ctx, value)) - *result = __qmljs_string_literal_function(ctx); + if (__qmljs_is_callable(ctx, &value)) + return __qmljs_string_literal_function(ctx); else - *result = __qmljs_string_literal_object(ctx); // ### implementation-defined + return __qmljs_string_literal_object(ctx); // ### implementation-defined break; - case Value::Integer_Type: - case Value::Double_Type: - *result = __qmljs_string_literal_number(ctx); + default: + return __qmljs_string_literal_number(ctx); break; } } -- cgit v1.2.3 From 537c5fa10d1fe59fb7eb24c564fa3cd92f518b6a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 16:21:00 +0200 Subject: Move conversions to new calling convention Change-Id: Iec01835b4fd27d8b71b7e709b05bcc757d09e417 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 6 +++--- qmljs_runtime.cpp | 51 +++++++++++++++++++++++++-------------------------- qmljs_runtime.h | 28 ++++++++++++++-------------- qv4isel_masm.cpp | 6 ++++-- 4 files changed, 46 insertions(+), 45 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index ba178a0158..43d09a0758 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -249,7 +249,7 @@ void __qmljs_llvm_get_activation_property(Context *ctx, Value *result, String *n void __qmljs_llvm_set_activation_property(Context *ctx, String *name, Value *value) { - __qmljs_set_activation_property(ctx, name, value); + __qmljs_set_activation_property(ctx, name, *value); } void __qmljs_llvm_get_property(Context *ctx, Value *result, Value *object, String *name) @@ -279,7 +279,7 @@ void __qmljs_llvm_set_element(Context *ctx, Value *object, Value *index, Value * void __qmljs_llvm_set_property(Context *ctx, Value *object, String *name, Value *value) { - __qmljs_set_property(ctx, object, name, value); + __qmljs_set_property(ctx, *object, name, *value); } void __qmljs_llvm_typeof(Context *ctx, Value *result, const Value *value) @@ -325,7 +325,7 @@ void __qmljs_llvm_delete_value(Context *ctx, Value *result, Value *value) void __qmljs_llvm_init_this_object(Context *ctx) { if (ctx->calledAsConstructor) - __qmljs_new_object(ctx, &ctx->thisObject); + ctx->thisObject = __qmljs_new_object(ctx); } } // extern "C" diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 01080bec8b..cf54bc63c3 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -490,7 +490,7 @@ void __qmljs_delete_property(Context *ctx, Value *result, String *name) void __qmljs_delete_value(Context *ctx, Value *result, Value *value) { Q_UNUSED(value); - __qmljs_throw_type_error(ctx, result); // ### throw syntax error + *result = __qmljs_throw_type_error(ctx); // ### throw syntax error } void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Value *right) @@ -520,7 +520,7 @@ void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Va return; } - __qmljs_throw_type_error(ctx, result); + *result = __qmljs_throw_type_error(ctx); } void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *right) @@ -531,7 +531,7 @@ void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *rig bool r = right->objectValue()->hasProperty(ctx, s.stringValue()); *result = Value::fromBoolean(r); } else { - __qmljs_throw_type_error(ctx, result); + *result = __qmljs_throw_type_error(ctx); } } @@ -832,39 +832,38 @@ Value __qmljs_object_default_value(Context *ctx, const Value object, int typeHin return Value::undefinedValue(); } -void __qmljs_throw_type_error(Context *ctx, Value *result) +Value __qmljs_throw_type_error(Context *ctx) { ctx->throwTypeError(); - if (result) - *result = ctx->result; + return ctx->result; } -void __qmljs_new_object(Context *ctx, Value *result) +Value __qmljs_new_object(Context *ctx) { - *result = __qmljs_init_object(ctx->engine->newObject()); + return __qmljs_init_object(ctx->engine->newObject()); } -void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean) +Value __qmljs_new_boolean_object(Context *ctx, bool boolean) { Value value = Value::fromBoolean(boolean); - *result = __qmljs_init_object(ctx->engine->newBooleanObject(value)); + return __qmljs_init_object(ctx->engine->newBooleanObject(value)); } -void __qmljs_new_number_object(Context *ctx, Value *result, double number) +Value __qmljs_new_number_object(Context *ctx, double number) { Value value = Value::fromDouble(number); - *result = __qmljs_init_object(ctx->engine->newNumberObject(value)); + return __qmljs_init_object(ctx->engine->newNumberObject(value)); } -void __qmljs_new_string_object(Context *ctx, Value *result, String *string) +Value __qmljs_new_string_object(Context *ctx, String *string) { Value value = Value::fromString(string); - *result = __qmljs_init_object(ctx->engine->newStringObject(value)); + return __qmljs_init_object(ctx->engine->newStringObject(value)); } -void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value) +void __qmljs_set_property(Context *ctx, Value object, String *name, Value value) { - object->objectValue()->setProperty(ctx, name, *value, /*flags*/ 0); + object.objectValue()->setProperty(ctx, name, value, /*flags*/ 0); } void __qmljs_set_property_boolean(Context *ctx, Value *object, String *name, bool b) @@ -942,18 +941,18 @@ void __qmljs_set_activation_element(Context *ctx, String *name, Value *index, Va } } -void __qmljs_set_activation_property(Context *ctx, String *name, Value *value) +void __qmljs_set_activation_property(Context *ctx, String *name, Value value) { - if (Value *prop = ctx->lookupPropertyDescriptor(name)) { - *prop = *value; - } else - ctx->engine->globalObject.objectValue()->setProperty(ctx, name, *value); + if (Value *prop = ctx->lookupPropertyDescriptor(name)) + *prop = value; + else + ctx->engine->globalObject.objectValue()->setProperty(ctx, name, value); } void __qmljs_copy_activation_property(Context *ctx, String *name, String *other) { if (Value *source = ctx->lookupPropertyDescriptor(other)) - __qmljs_set_activation_property(ctx, name, source); + __qmljs_set_activation_property(ctx, name, *source); else ctx->throwReferenceError(Value::fromString(name)); } @@ -961,25 +960,25 @@ void __qmljs_copy_activation_property(Context *ctx, String *name, String *other) void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool b) { Value value = Value::fromBoolean(b); - __qmljs_set_activation_property(ctx, name, &value); + __qmljs_set_activation_property(ctx, name, value); } void __qmljs_set_activation_property_number(Context *ctx, String *name, double number) { Value value = Value::fromDouble(number); - __qmljs_set_activation_property(ctx, name, &value); + __qmljs_set_activation_property(ctx, name, value); } void __qmljs_set_activation_property_string(Context *ctx, String *name, String *string) { Value value = Value::fromString(string); - __qmljs_set_activation_property(ctx, name, &value); + __qmljs_set_activation_property(ctx, name, value); } void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Function *clos) { Value value = __qmljs_init_closure(clos, ctx); - __qmljs_set_activation_property(ctx, name, &value); + __qmljs_set_activation_property(ctx, name, value); } void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index dfbf35e0d6..517ebeb40b 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -132,13 +132,13 @@ String *__qmljs_identifier_from_utf8(Context *ctx, const char *s); // objects Value __qmljs_object_default_value(Context *ctx, const Value object, int typeHint); -void __qmljs_throw_type_error(Context *ctx, Value *result); -void __qmljs_new_object(Context *ctx, Value *result); -void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean); -void __qmljs_new_number_object(Context *ctx, Value *result, double n); -void __qmljs_new_string_object(Context *ctx, Value *result, String *string); -void __qmljs_set_activation_property(Context *ctx, String *name, Value *value); -void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value); +Value __qmljs_throw_type_error(Context *ctx); +Value __qmljs_new_object(Context *ctx); +Value __qmljs_new_boolean_object(Context *ctx, bool boolean); +Value __qmljs_new_number_object(Context *ctx, double n); +Value __qmljs_new_string_object(Context *ctx, String *string); +void __qmljs_set_activation_property(Context *ctx, String *name, Value value); +void __qmljs_set_property(Context *ctx, Value object, String *name, Value value); void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name); void __qmljs_get_activation_property(Context *ctx, Value *result, String *name); void __qmljs_copy_activation_property(Context *ctx, String *name, String *other); @@ -827,7 +827,7 @@ inline void __qmljs_to_string(Context *ctx, Value *result, const Value *value) if (prim.isPrimitive()) __qmljs_to_string(ctx, result, &prim); else - __qmljs_throw_type_error(ctx, result); + *result = __qmljs_throw_type_error(ctx); break; } case Value::Integer_Type: @@ -845,22 +845,22 @@ inline void __qmljs_to_object(Context *ctx, Value *result, const Value *value) switch (value->type()) { case Value::Undefined_Type: case Value::Null_Type: - __qmljs_throw_type_error(ctx, result); + *result = __qmljs_throw_type_error(ctx); break; case Value::Boolean_Type: - __qmljs_new_boolean_object(ctx, result, value->booleanValue()); + *result = __qmljs_new_boolean_object(ctx, value->booleanValue()); break; case Value::String_Type: - __qmljs_new_string_object(ctx, result, value->stringValue()); + *result = __qmljs_new_string_object(ctx, value->stringValue()); break; case Value::Object_Type: *result = *value; break; case Value::Integer_Type: - __qmljs_new_number_object(ctx, result, value->int_32); + *result = __qmljs_new_number_object(ctx, value->int_32); break; default: // double - __qmljs_new_number_object(ctx, result, value->doubleValue()); + *result = __qmljs_new_number_object(ctx, value->doubleValue()); break; } } @@ -870,7 +870,7 @@ inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Va switch (value->type()) { case Value::Undefined_Type: case Value::Null_Type: - __qmljs_throw_type_error(ctx, result); + *result = __qmljs_throw_type_error(ctx); return false; default: return true; diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 19d61146d6..f60510d15d 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -318,7 +318,8 @@ void InstructionSelection::visitMove(IR::Move *s) String *propertyName = identifier(*n->id); if (IR::Temp *t = s->source->asTemp()) { - generateFunctionCall(__qmljs_set_activation_property, ContextRegister, propertyName, t); + IR::Temp *retval = 0; + generateFunctionCall2(retval, __qmljs_set_activation_property, ContextRegister, propertyName, t); checkExceptions(); return; } else { @@ -472,7 +473,8 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Member *m = s->target->asMember()) { if (IR::Temp *base = m->base->asTemp()) { if (IR::Temp *t = s->source->asTemp()) { - generateFunctionCall(__qmljs_set_property, ContextRegister, base, identifier(*m->name), t); + IR::Temp *retVal = 0; + generateFunctionCall2(retVal, __qmljs_set_property, ContextRegister, base, identifier(*m->name), t); checkExceptions(); return; } else { -- cgit v1.2.3 From bb09c83bcf8d8c38b0a3a07177aedf532942dd92 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 17:02:28 +0200 Subject: Converting more runtime methods to the new calling convention Change-Id: I331e7ebe6479bc5b6bac9cf4e3e6506a1cae1053 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 10 ++--- moth/qv4vme_moth.cpp | 2 +- qmljs_runtime.cpp | 114 ++++++++++++++++++++------------------------------- qmljs_runtime.h | 31 ++++++-------- qv4ecmaobjects.cpp | 2 +- qv4isel_masm.cpp | 16 ++++---- qv4isel_masm_p.h | 6 +++ 7 files changed, 77 insertions(+), 104 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 43d09a0758..fb7e3f629a 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -244,7 +244,7 @@ void __qmljs_llvm_construct_value(Context *context, Value *result, const Value * void __qmljs_llvm_get_activation_property(Context *ctx, Value *result, String *name) { - __qmljs_get_activation_property(ctx, result, name); + *result = __qmljs_get_activation_property(ctx, name); } void __qmljs_llvm_set_activation_property(Context *ctx, String *name, Value *value) @@ -254,7 +254,7 @@ void __qmljs_llvm_set_activation_property(Context *ctx, String *name, Value *val void __qmljs_llvm_get_property(Context *ctx, Value *result, Value *object, String *name) { - __qmljs_get_property(ctx, result, object, name); + *result = __qmljs_get_property(ctx, *object, name); } void __qmljs_llvm_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) @@ -269,12 +269,12 @@ void __qmljs_llvm_construct_property(Context *context, Value *result, const Valu void __qmljs_llvm_get_element(Context *ctx, Value *result, Value *object, Value *index) { - __qmljs_get_element(ctx, result, object, index); + *result = __qmljs_get_element(ctx, *object, *index); } void __qmljs_llvm_set_element(Context *ctx, Value *object, Value *index, Value *value) { - __qmljs_set_element(ctx, object, index, value); + __qmljs_set_element(ctx, *object, *index, *value); } void __qmljs_llvm_set_property(Context *ctx, Value *object, String *name, Value *value) @@ -299,7 +299,7 @@ void __qmljs_llvm_rethrow(Context *context, Value *result) void __qmljs_llvm_get_this_object(Context *ctx, Value *result) { - __qmljs_get_thisObject(ctx, result); + *result = __qmljs_get_thisObject(ctx); } void __qmljs_llvm_delete_subscript(Context *ctx, Value *result, Value *base, Value *index) diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 3bbbfe6f25..bc8c9b5f97 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -124,7 +124,7 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) - __qmljs_get_activation_property(context, &tempRegister, instr.value); + tempRegister = __qmljs_get_activation_property(context, instr.value); MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(Push) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index cf54bc63c3..223c0a3d0f 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -495,9 +495,8 @@ void __qmljs_delete_value(Context *ctx, Value *result, Value *value) void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Value *right) { - Value pleft, pright; - __qmljs_to_primitive(ctx, &pleft, left, PREFERREDTYPE_HINT); - __qmljs_to_primitive(ctx, &pright, right, PREFERREDTYPE_HINT); + Value pleft = __qmljs_to_primitive(ctx, *left, PREFERREDTYPE_HINT); + Value pright = __qmljs_to_primitive(ctx, *right, PREFERREDTYPE_HINT); if (pleft.isString() || pright.isString()) { if (!pleft.isString()) __qmljs_to_string(ctx, &pleft, &pleft); @@ -892,53 +891,44 @@ void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR: object->objectValue()->setProperty(ctx, name, value, /*flag*/ 0); } -void __qmljs_get_element(Context *ctx, Value *result, Value *object, Value *index) +Value __qmljs_get_element(Context *ctx, Value object, Value index) { - if (object->isString() && index->isNumber()) { - const QString s = object->stringValue()->toQString().mid(index->toUInt32(ctx), 1); + if (object.isString() && index.isNumber()) { + const QString s = object.stringValue()->toQString().mid(index.toUInt32(ctx), 1); if (s.isNull()) - *result = Value::undefinedValue(); + return Value::undefinedValue(); else - *result = Value::fromString(ctx, s); - } else if (object->isArrayObject() && index->isNumber()) { - *result = object->asArrayObject()->value.at(index->toUInt32(ctx)); + return Value::fromString(ctx, s); + } else if (object.isArrayObject() && index.isNumber()) { + return object.asArrayObject()->value.at(index.toUInt32(ctx)); } else { - String *name = index->toString(ctx); + String *name = index.toString(ctx); - if (! object->isObject()) - __qmljs_to_object(ctx, object, object); + if (! object.isObject()) + __qmljs_to_object(ctx, &object, &object); - *result = object->property(ctx, name); + return object.property(ctx, name); } } -void __qmljs_set_element(Context *ctx, Value *object, Value *index, Value *value) +void __qmljs_set_element(Context *ctx, Value object, Value index, Value value) { - if (object->isArrayObject() && index->isNumber()) { - object->asArrayObject()->value.assign(index->toUInt32(ctx), *value); + if (object.isArrayObject() && index.isNumber()) { + object.asArrayObject()->value.assign(index.toUInt32(ctx), value); } else { - String *name = index->toString(ctx); + String *name = index.toString(ctx); - if (! object->isObject()) - __qmljs_to_object(ctx, object, object); + if (! object.isObject()) + __qmljs_to_object(ctx, &object, &object); - object->objectValue()->setProperty(ctx, name, *value, /*flags*/ 0); + object.objectValue()->setProperty(ctx, name, value, /*flags*/ 0); } } void __qmljs_set_element_number(Context *ctx, Value *object, Value *index, double number) { Value v = Value::fromDouble(number); - __qmljs_set_element(ctx, object, index, &v); -} - -void __qmljs_set_activation_element(Context *ctx, String *name, Value *index, Value *value) -{ - if (Value *base = ctx->lookupPropertyDescriptor(name)) { - __qmljs_set_element(ctx, base, index, value); - } else { - ctx->throwReferenceError(Value::fromString(name)); - } + __qmljs_set_element(ctx, *object, *index, v); } void __qmljs_set_activation_property(Context *ctx, String *name, Value value) @@ -949,14 +939,6 @@ void __qmljs_set_activation_property(Context *ctx, String *name, Value value) ctx->engine->globalObject.objectValue()->setProperty(ctx, name, value); } -void __qmljs_copy_activation_property(Context *ctx, String *name, String *other) -{ - if (Value *source = ctx->lookupPropertyDescriptor(other)) - __qmljs_set_activation_property(ctx, name, *source); - else - ctx->throwReferenceError(Value::fromString(name)); -} - void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool b) { Value value = Value::fromBoolean(b); @@ -981,42 +963,38 @@ void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Fun __qmljs_set_activation_property(ctx, name, value); } -void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name) +Value __qmljs_get_property(Context *ctx, Value object, String *name) { - if (object->isObject()) { - *result = object->property(ctx, name); - } else if (object->isString() && name->isEqualTo(ctx->engine->id_length)) { - *result = Value::fromDouble(object->stringValue()->toQString().length()); + if (object.isObject()) { + return object.property(ctx, name); + } else if (object.isString() && name->isEqualTo(ctx->engine->id_length)) { + return Value::fromDouble(object.stringValue()->toQString().length()); } else { Value o; - __qmljs_to_object(ctx, &o, object); + __qmljs_to_object(ctx, &o, &object); - if (o.isObject()) - __qmljs_get_property(ctx, result, &o, name); - else + if (o.isObject()) { + return __qmljs_get_property(ctx, o, name); + } else { ctx->throwTypeError(); // ### not necessary. + return Value::undefinedValue(); + } } } -void __qmljs_get_activation_property(Context *ctx, Value *result, String *name) +Value __qmljs_get_activation_property(Context *ctx, String *name) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) - *result = *prop; - else - ctx->throwReferenceError(Value::fromString(name)); -} - -void __qmljs_get_activation(Context *ctx, Value *result) -{ - *result = ctx->activation; + return *prop; + ctx->throwReferenceError(Value::fromString(name)); + return Value::undefinedValue(); } -void __qmljs_get_thisObject(Context *ctx, Value *result) +Value __qmljs_get_thisObject(Context *ctx) { if (ctx->thisObject.isObject()) - *result = ctx->thisObject; - else - *result = ctx->engine->globalObject; + return ctx->thisObject; + return ctx->engine->globalObject; } void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y, bool leftFirst) @@ -1024,11 +1002,11 @@ void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y Value px, py; if (leftFirst) { - __qmljs_to_primitive(ctx, &px, x, NUMBER_HINT); - __qmljs_to_primitive(ctx, &py, y, NUMBER_HINT); + px = __qmljs_to_primitive(ctx, *x, NUMBER_HINT); + py = __qmljs_to_primitive(ctx, *y, NUMBER_HINT); } else { - __qmljs_to_primitive(ctx, &py, x, NUMBER_HINT); - __qmljs_to_primitive(ctx, &px, y, NUMBER_HINT); + px = __qmljs_to_primitive(ctx, *x, NUMBER_HINT); + py = __qmljs_to_primitive(ctx, *y, NUMBER_HINT); } if (px.isString() && py.isString()) { @@ -1086,12 +1064,10 @@ uint __qmljs_equal(Context *ctx, const Value *x, const Value *y) Value ny = Value::fromDouble((double) y->booleanValue()); return __qmljs_equal(ctx, x, &ny); } else if ((x->isNumber() || x->isString()) && y->isObject()) { - Value py; - __qmljs_to_primitive(ctx, &py, y, PREFERREDTYPE_HINT); + Value py = __qmljs_to_primitive(ctx, *y, PREFERREDTYPE_HINT); return __qmljs_equal(ctx, x, &py); } else if (x->isObject() && (y->isNumber() || y->isString())) { - Value px; - __qmljs_to_primitive(ctx, &px, x, PREFERREDTYPE_HINT); + Value px = __qmljs_to_primitive(ctx, *x, PREFERREDTYPE_HINT); return __qmljs_equal(ctx, &px, y); } } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 517ebeb40b..fb62e663f5 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -139,21 +139,17 @@ Value __qmljs_new_number_object(Context *ctx, double n); Value __qmljs_new_string_object(Context *ctx, String *string); void __qmljs_set_activation_property(Context *ctx, String *name, Value value); void __qmljs_set_property(Context *ctx, Value object, String *name, Value value); -void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name); -void __qmljs_get_activation_property(Context *ctx, Value *result, String *name); -void __qmljs_copy_activation_property(Context *ctx, String *name, String *other); +Value __qmljs_get_property(Context *ctx, Value object, String *name); +Value __qmljs_get_activation_property(Context *ctx, String *name); -void __qmljs_get_element(Context *ctx, Value *result, Value *object, Value *index); -void __qmljs_set_element(Context *ctx, Value *object, Value *index, Value *value); - -void __qmljs_set_activation_element(Context *ctx, String *name, Value *index, Value *value); +Value __qmljs_get_element(Context *ctx, Value object, Value index); +void __qmljs_set_element(Context *ctx, Value object, Value index, Value value); // context -void __qmljs_get_activation(Context *ctx, Value *result); -void __qmljs_get_thisObject(Context *ctx, Value *result); +Value __qmljs_get_thisObject(Context *ctx); // type conversion and testing -void __qmljs_to_primitive(Context *ctx, Value *result, const Value *value, int typeHint); +Value __qmljs_to_primitive(Context *ctx, const Value value, int typeHint); uint __qmljs_to_boolean(const Value value, Context *ctx); double __qmljs_to_number(const Value value, Context *ctx); double __qmljs_to_integer(const Value value, Context *ctx); @@ -671,12 +667,11 @@ inline Value __qmljs_init_object(Object *object) } // type conversion and testing -inline void __qmljs_to_primitive(Context *ctx, Value *result, const Value *value, int typeHint) +inline Value __qmljs_to_primitive(Context *ctx, const Value value, int typeHint) { - if (!value->isObject()) - *result = *value; - else - *result = __qmljs_default_value(ctx, *value, typeHint); + if (!value.isObject()) + return value; + return __qmljs_default_value(ctx, value, typeHint); } inline uint __qmljs_to_boolean(const Value value, Context *ctx) @@ -713,8 +708,7 @@ inline double __qmljs_to_number(const Value value, Context *ctx) case Value::String_Type: return __qmljs_string_to_number(ctx, value.stringValue()); case Value::Object_Type: { - Value prim; - __qmljs_to_primitive(ctx, &prim, &value, NUMBER_HINT); + Value prim = __qmljs_to_primitive(ctx, value, NUMBER_HINT); return __qmljs_to_number(prim, ctx); } default: // double @@ -822,8 +816,7 @@ inline void __qmljs_to_string(Context *ctx, Value *result, const Value *value) *result = *value; break; case Value::Object_Type: { - Value prim; - __qmljs_to_primitive(ctx, &prim, value, STRING_HINT); + Value prim = __qmljs_to_primitive(ctx, *value, STRING_HINT); if (prim.isPrimitive()) __qmljs_to_string(ctx, result, &prim); else diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 1f0f82fcb2..b77b7f3eef 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1850,7 +1850,7 @@ void DateCtor::construct(Context *ctx) if (DateObject *d = arg.asDateObject()) arg = d->value; else - __qmljs_to_primitive(ctx, &arg, &arg, PREFERREDTYPE_HINT); + arg = __qmljs_to_primitive(ctx, arg, PREFERREDTYPE_HINT); if (arg.isString()) t = ParseString(arg.toString(ctx)->toQString()); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index f60510d15d..ec8c6444eb 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -318,8 +318,7 @@ void InstructionSelection::visitMove(IR::Move *s) String *propertyName = identifier(*n->id); if (IR::Temp *t = s->source->asTemp()) { - IR::Temp *retval = 0; - generateFunctionCall2(retval, __qmljs_set_activation_property, ContextRegister, propertyName, t); + generateFunctionCall2(Void, __qmljs_set_activation_property, ContextRegister, propertyName, t); checkExceptions(); return; } else { @@ -328,10 +327,10 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Temp *t = s->target->asTemp()) { if (IR::Name *n = s->source->asName()) { if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. - generateFunctionCall(__qmljs_get_thisObject, ContextRegister, t); + generateFunctionCall2(t, __qmljs_get_thisObject, ContextRegister); } else { String *propertyName = identifier(*n->id); - generateFunctionCall(__qmljs_get_activation_property, ContextRegister, t, propertyName); + generateFunctionCall2(t, __qmljs_get_activation_property, ContextRegister, propertyName); checkExceptions(); } return; @@ -381,14 +380,14 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Member *m = s->source->asMember()) { //__qmljs_get_property(ctx, result, object, name); if (IR::Temp *base = m->base->asTemp()) { - generateFunctionCall(__qmljs_get_property, ContextRegister, t, base, identifier(*m->name)); + generateFunctionCall2(t, __qmljs_get_property, ContextRegister, base, identifier(*m->name)); checkExceptions(); return; } assert(!"wip"); return; } else if (IR::Subscript *ss = s->source->asSubscript()) { - generateFunctionCall(__qmljs_get_element, ContextRegister, t, ss->base->asTemp(), ss->index->asTemp()); + generateFunctionCall2(t, __qmljs_get_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp()); checkExceptions(); return; } else if (IR::Unop *u = s->source->asUnop()) { @@ -473,8 +472,7 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Member *m = s->target->asMember()) { if (IR::Temp *base = m->base->asTemp()) { if (IR::Temp *t = s->source->asTemp()) { - IR::Temp *retVal = 0; - generateFunctionCall2(retVal, __qmljs_set_property, ContextRegister, base, identifier(*m->name), t); + generateFunctionCall2(Void, __qmljs_set_property, ContextRegister, base, identifier(*m->name), t); checkExceptions(); return; } else { @@ -483,7 +481,7 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Subscript *ss = s->target->asSubscript()) { if (IR::Temp *t2 = s->source->asTemp()) { - generateFunctionCall(__qmljs_set_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t2); + generateFunctionCall2(Void, __qmljs_set_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t2); checkExceptions(); return; } else { diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 580ecea741..ceb64a372a 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -111,6 +111,8 @@ protected: #else #error Argh. #endif + struct VoidType {}; + static const VoidType Void; // Explicit type to allow distinguishing between // pushing an address itself or the value it points @@ -327,6 +329,10 @@ private: move(src, dest); } + void storeArgument(RegisterID, VoidType) + { + } + using JSC::MacroAssembler::push; void push(const Pointer& ptr) -- cgit v1.2.3 From b010e46d039d066758198336a39fe5f7c8a20edf Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 20:38:11 +0200 Subject: Convert some more methods to the new calling convention Change-Id: I73a0e0e12b75828c83c5422d1c04a4ade4718324 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 29 +++++++++++--------------- qmljs_runtime.h | 61 ++++++++++++++++++++++++++++-------------------------- qv4ecmaobjects.cpp | 8 +++---- 3 files changed, 47 insertions(+), 51 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 223c0a3d0f..f1f565505a 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -163,17 +163,14 @@ double Value::toNumber(Context *ctx) const String *Value::toString(Context *ctx) const { - Value v; - __qmljs_to_string(ctx, &v, this); + Value v = __qmljs_to_string(ctx, *this); assert(v.is(Value::String_Type)); return v.stringValue(); } Value Value::toObject(Context *ctx) const { - Value v; - __qmljs_to_object(ctx, &v, this); - return v; + return __qmljs_to_object(ctx, *this); } bool Value::isFunctionObject() const @@ -499,9 +496,9 @@ void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Va Value pright = __qmljs_to_primitive(ctx, *right, PREFERREDTYPE_HINT); if (pleft.isString() || pright.isString()) { if (!pleft.isString()) - __qmljs_to_string(ctx, &pleft, &pleft); + pleft = __qmljs_to_string(ctx, pleft); if (!pright.isString()) - __qmljs_to_string(ctx, &pright, &pright); + pright = __qmljs_to_string(ctx, pright); String *string = __qmljs_string_concat(ctx, pleft.stringValue(), pright.stringValue()); *result = Value::fromString(string); } else { @@ -525,8 +522,7 @@ void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Va void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *right) { if (right->isObject()) { - Value s; - __qmljs_to_string(ctx, &s, left); + Value s = __qmljs_to_string(ctx, *left); bool r = right->objectValue()->hasProperty(ctx, s.stringValue()); *result = Value::fromBoolean(r); } else { @@ -905,7 +901,7 @@ Value __qmljs_get_element(Context *ctx, Value object, Value index) String *name = index.toString(ctx); if (! object.isObject()) - __qmljs_to_object(ctx, &object, &object); + object = __qmljs_to_object(ctx, object); return object.property(ctx, name); } @@ -919,7 +915,7 @@ void __qmljs_set_element(Context *ctx, Value object, Value index, Value value) String *name = index.toString(ctx); if (! object.isObject()) - __qmljs_to_object(ctx, &object, &object); + object = __qmljs_to_object(ctx, object); object.objectValue()->setProperty(ctx, name, value, /*flags*/ 0); } @@ -970,11 +966,10 @@ Value __qmljs_get_property(Context *ctx, Value object, String *name) } else if (object.isString() && name->isEqualTo(ctx->engine->id_length)) { return Value::fromDouble(object.stringValue()->toQString().length()); } else { - Value o; - __qmljs_to_object(ctx, &o, &object); + object = __qmljs_to_object(ctx, object); - if (o.isObject()) { - return __qmljs_get_property(ctx, o, name); + if (object.isObject()) { + return __qmljs_get_property(ctx, object, name); } else { ctx->throwTypeError(); // ### not necessary. return Value::undefinedValue(); @@ -1093,7 +1088,7 @@ Value __qmljs_call_property(Context *context, const Value base, String *name, Va if (!base.isUndefined()) { baseObject = base; if (!baseObject.isObject()) - __qmljs_to_object(context, &baseObject, &baseObject); + baseObject = __qmljs_to_object(context, baseObject); assert(baseObject.isObject()); thisObject = baseObject; } else { @@ -1172,7 +1167,7 @@ Value __qmljs_construct_property(Context *context, const Value base, String *nam { Value thisObject = base; if (!thisObject.isObject()) - __qmljs_to_object(context, &thisObject, &base); + thisObject = __qmljs_to_object(context, base); assert(thisObject.isObject()); Value func = thisObject.property(context, name); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index fb62e663f5..35e56651c1 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -86,6 +86,7 @@ struct ErrorObject; struct ActivationObject; struct ExecutionEngine; + extern "C" { // context @@ -156,10 +157,10 @@ double __qmljs_to_integer(const Value value, Context *ctx); int __qmljs_to_int32(const Value value, Context *ctx); unsigned __qmljs_to_uint32(const Value value, Context *ctx); unsigned short __qmljs_to_uint16(const Value value, Context *ctx); -void __qmljs_to_string(Context *ctx, Value *result, const Value *value); -void __qmljs_to_object(Context *ctx, Value *result, const Value *value); -uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value); -uint __qmljs_is_callable(Context *ctx, const Value *value); +Value __qmljs_to_string(Context *ctx, const Value value); +Value __qmljs_to_object(Context *ctx, const Value value); +//uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value); +uint __qmljs_is_callable(Context *ctx, const Value value); Value __qmljs_default_value(Context *ctx, const Value value, int typeHint); void __qmljs_compare(Context *ctx, Value *result, const Value *left, const Value *right, bool leftFlag); @@ -797,67 +798,68 @@ inline unsigned short __qmljs_to_uint16(const Value value, Context *ctx) return (unsigned short)number; } -inline void __qmljs_to_string(Context *ctx, Value *result, const Value *value) +inline Value __qmljs_to_string(Context *ctx, const Value value) { - switch (value->type()) { + switch (value.type()) { case Value::Undefined_Type: - *result = __qmljs_string_literal_undefined(ctx); + return __qmljs_string_literal_undefined(ctx); break; case Value::Null_Type: - *result = __qmljs_string_literal_null(ctx); + return __qmljs_string_literal_null(ctx); break; case Value::Boolean_Type: - if (value->booleanValue()) - *result = __qmljs_string_literal_true(ctx); + if (value.booleanValue()) + return __qmljs_string_literal_true(ctx); else - *result = __qmljs_string_literal_false(ctx); + return __qmljs_string_literal_false(ctx); break; case Value::String_Type: - *result = *value; + return value; break; case Value::Object_Type: { - Value prim = __qmljs_to_primitive(ctx, *value, STRING_HINT); + Value prim = __qmljs_to_primitive(ctx, value, STRING_HINT); if (prim.isPrimitive()) - __qmljs_to_string(ctx, result, &prim); + return __qmljs_to_string(ctx, prim); else - *result = __qmljs_throw_type_error(ctx); + return __qmljs_throw_type_error(ctx); break; } case Value::Integer_Type: - *result = __qmljs_string_from_number(ctx, value->int_32); + return __qmljs_string_from_number(ctx, value.int_32); break; default: // number - *result = __qmljs_string_from_number(ctx, value->doubleValue()); + return __qmljs_string_from_number(ctx, value.doubleValue()); break; } // switch } -inline void __qmljs_to_object(Context *ctx, Value *result, const Value *value) +inline Value __qmljs_to_object(Context *ctx, const Value value) { - switch (value->type()) { + switch (value.type()) { case Value::Undefined_Type: case Value::Null_Type: - *result = __qmljs_throw_type_error(ctx); + return __qmljs_throw_type_error(ctx); break; case Value::Boolean_Type: - *result = __qmljs_new_boolean_object(ctx, value->booleanValue()); + return __qmljs_new_boolean_object(ctx, value.booleanValue()); break; case Value::String_Type: - *result = __qmljs_new_string_object(ctx, value->stringValue()); + return __qmljs_new_string_object(ctx, value.stringValue()); break; case Value::Object_Type: - *result = *value; + return value; break; case Value::Integer_Type: - *result = __qmljs_new_number_object(ctx, value->int_32); + return __qmljs_new_number_object(ctx, value.int_32); break; default: // double - *result = __qmljs_new_number_object(ctx, value->doubleValue()); + return __qmljs_new_number_object(ctx, value.doubleValue()); break; } } +/* inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value) { switch (value->type()) { @@ -869,11 +871,12 @@ inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Va return true; } } +*/ -inline uint __qmljs_is_callable(Context *ctx, const Value *value) +inline uint __qmljs_is_callable(Context *ctx, const Value value) { - if (value->isObject()) - return __qmljs_is_function(*value); + if (value.isObject()) + return __qmljs_is_function(value); else return false; } @@ -903,7 +906,7 @@ inline Value __qmljs_typeof(Context *ctx, const Value value) return __qmljs_string_literal_string(ctx); break; case Value::Object_Type: - if (__qmljs_is_callable(ctx, &value)) + if (__qmljs_is_callable(ctx, value)) return __qmljs_string_literal_function(ctx); else return __qmljs_string_literal_object(ctx); // ### implementation-defined diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index b77b7f3eef..426ab0fedd 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -714,7 +714,7 @@ void StringCtor::call(Context *ctx) if (arg.is(Value::Undefined_Type)) ctx->result = Value::fromString(ctx->engine->newString(QString())); else - __qmljs_to_string(ctx, &ctx->result, &arg); + ctx->result = __qmljs_to_string(ctx, arg); } void StringPrototype::init(Context *ctx, const Value &ctor) @@ -808,8 +808,7 @@ void StringPrototype::method_concat(Context *ctx) QString value = getThisString(ctx); for (unsigned i = 0; i < ctx->argumentCount; ++i) { - Value v; - __qmljs_to_string(ctx, &v, &ctx->arguments[i]); + Value v = __qmljs_to_string(ctx, ctx->arguments[i]); assert(v.is(Value::String_Type)); value += v.stringValue()->toQString(); } @@ -842,8 +841,7 @@ void StringPrototype::method_lastIndexOf(Context *ctx) QString searchString; if (ctx->argumentCount) { - Value v; - __qmljs_to_string(ctx, &v, &ctx->arguments[0]); + Value v = __qmljs_to_string(ctx, ctx->arguments[0]); searchString = v.stringValue()->toQString(); } -- cgit v1.2.3 From 7efad31f6739b5a9284b17493516c404006372fa Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 20:43:43 +0200 Subject: Introduce a Bool typedef for boolean return values This helps in the interpretation of the type returned by a method. It also makes it easier to later on change the type back to a real bool if it makes sense. Bool is currently typedef'ed to uint. Change-Id: I905d828f4f7b0f7e778b7f197c4ed61117ce5d16 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 8 +++---- qmljs_runtime.h | 69 ++++++++++++++++++++++++++++--------------------------- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index f1f565505a..b27a7f1d9d 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -146,7 +146,7 @@ unsigned int Value::toUInt32(Context *ctx) return __qmljs_to_uint32(*this, ctx); } -bool Value::toBoolean(Context *ctx) const +Bool Value::toBoolean(Context *ctx) const { return __qmljs_to_boolean(*this, ctx); } @@ -772,12 +772,12 @@ Value __qmljs_string_from_number(Context *ctx, double number) return Value::fromString(string); } -uint __qmljs_string_compare(Context *, String *left, String *right) +Bool __qmljs_string_compare(Context *, String *left, String *right) { return left->toQString() < right->toQString(); } -uint __qmljs_string_equal(Context *, String *left, String *right) +Bool __qmljs_string_equal(Context *, String *left, String *right) { return left == right || left->isEqualTo(right); } @@ -787,7 +787,7 @@ String *__qmljs_string_concat(Context *ctx, String *first, String *second) return ctx->engine->newString(first->toQString() + second->toQString()); } -uint __qmljs_is_function(const Value value) +Bool __qmljs_is_function(const Value value) { return value.objectValue()->asFunctionObject() != 0; } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 35e56651c1..c4a41e334b 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -86,6 +86,7 @@ struct ErrorObject; struct ActivationObject; struct ExecutionEngine; +typedef uint Bool; extern "C" { @@ -108,7 +109,7 @@ Value __qmljs_init_object(Object *object); Value __qmljs_init_closure(IR::Function *clos, Context *ctx); Value __qmljs_init_native_function(void (*code)(Context *), Context *ctx); -uint __qmljs_is_function(const Value value); +Bool __qmljs_is_function(const Value value); // string literals Value __qmljs_string_literal_undefined(Context *ctx); @@ -126,8 +127,8 @@ String *__qmljs_string_from_utf8(Context *ctx, const char *s); int __qmljs_string_length(Context *ctx, String *string); double __qmljs_string_to_number(Context *ctx, String *string); Value __qmljs_string_from_number(Context *ctx, double number); -uint __qmljs_string_compare(Context *ctx, String *left, String *right); -uint __qmljs_string_equal(Context *ctx, String *left, String *right); +Bool __qmljs_string_compare(Context *ctx, String *left, String *right); +Bool __qmljs_string_equal(Context *ctx, String *left, String *right); String *__qmljs_string_concat(Context *ctx, String *first, String *second); String *__qmljs_identifier_from_utf8(Context *ctx, const char *s); @@ -151,7 +152,7 @@ Value __qmljs_get_thisObject(Context *ctx); // type conversion and testing Value __qmljs_to_primitive(Context *ctx, const Value value, int typeHint); -uint __qmljs_to_boolean(const Value value, Context *ctx); +Bool __qmljs_to_boolean(const Value value, Context *ctx); double __qmljs_to_number(const Value value, Context *ctx); double __qmljs_to_integer(const Value value, Context *ctx); int __qmljs_to_int32(const Value value, Context *ctx); @@ -160,12 +161,12 @@ unsigned short __qmljs_to_uint16(const Value value, Context *ctx); Value __qmljs_to_string(Context *ctx, const Value value); Value __qmljs_to_object(Context *ctx, const Value value); //uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value); -uint __qmljs_is_callable(Context *ctx, const Value value); +Bool __qmljs_is_callable(Context *ctx, const Value value); Value __qmljs_default_value(Context *ctx, const Value value, int typeHint); void __qmljs_compare(Context *ctx, Value *result, const Value *left, const Value *right, bool leftFlag); -uint __qmljs_equal(Context *ctx, const Value *x, const Value *y); -uint __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y); +Bool __qmljs_equal(Context *ctx, const Value *x, const Value *y); +Bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y); // unary operators Value __qmljs_uplus(const Value value, Context *ctx); @@ -255,16 +256,16 @@ void __qmljs_inplace_shl_member(Context *ctx, Value *base, String *name, Value * void __qmljs_inplace_shr_member(Context *ctx, Value *base, String *name, Value *value); void __qmljs_inplace_ushr_member(Context *ctx, Value *base, String *name, Value *value); -uint __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right); -uint __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right); -uint __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right); -uint __qmljs_cmp_le(Context *ctx, const Value *left, const Value *right); -uint __qmljs_cmp_eq(Context *ctx, const Value *left, const Value *right); -uint __qmljs_cmp_ne(Context *ctx, const Value *left, const Value *right); -uint __qmljs_cmp_se(Context *ctx, const Value *left, const Value *right); -uint __qmljs_cmp_sne(Context *ctx, const Value *left, const Value *right); -uint __qmljs_cmp_instanceof(Context *ctx, const Value *left, const Value *right); -uint __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_le(Context *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_eq(Context *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_ne(Context *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_se(Context *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_sne(Context *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_instanceof(Context *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right); } // extern "C" @@ -347,7 +348,7 @@ template <> struct ValueBase<4> : public ValueData static inline Value undefinedValue(); static inline Value nullValue(); - static inline Value fromBoolean(bool b); + static inline Value fromBoolean(Bool b); static inline Value fromDouble(double d); static inline Value fromInt32(int i); static inline Value fromString(String *s); @@ -413,7 +414,7 @@ template <> struct ValueBase<8> : public ValueData static Value undefinedValue(); static Value nullValue(); - static Value fromBoolean(bool b); + static Value fromBoolean(Bool b); static Value fromDouble(double d); static Value fromInt32(int i); static Value fromString(String *s); @@ -436,7 +437,7 @@ struct Value : public ValueBase int toUInt16(Context *ctx); int toInt32(Context *ctx); unsigned int toUInt32(Context *ctx); - bool toBoolean(Context *ctx) const; + Bool toBoolean(Context *ctx) const; double toInteger(Context *ctx) const; double toNumber(Context *ctx) const; String *toString(Context *ctx) const; @@ -491,7 +492,7 @@ inline Value ValueBase<4>::nullValue() return v; } -inline Value ValueBase<4>::fromBoolean(bool b) +inline Value ValueBase<4>::fromBoolean(Bool b) { Value v; v.tag = Boolean_Type; @@ -545,7 +546,7 @@ inline Value ValueBase<8>::nullValue() return v; } -inline Value ValueBase<8>::fromBoolean(bool b) +inline Value ValueBase<8>::fromBoolean(Bool b) { Value v; v.tag = Boolean_Type; @@ -675,7 +676,7 @@ inline Value __qmljs_to_primitive(Context *ctx, const Value value, int typeHint) return __qmljs_default_value(ctx, value, typeHint); } -inline uint __qmljs_to_boolean(const Value value, Context *ctx) +inline Bool __qmljs_to_boolean(const Value value, Context *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -873,7 +874,7 @@ inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Va } */ -inline uint __qmljs_is_callable(Context *ctx, const Value value) +inline Bool __qmljs_is_callable(Context *ctx, const Value value) { if (value.isObject()) return __qmljs_is_function(value); @@ -1181,59 +1182,59 @@ inline void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Va *result = Value::fromBoolean(r); } -inline uint __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_gt(ctx, &v, left, right); return v.booleanValue(); } -inline uint __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_lt(ctx, &v, left, right); return v.booleanValue(); } -inline uint __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_ge(ctx, &v, left, right); return v.booleanValue(); } -inline uint __qmljs_cmp_le(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_le(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_le(ctx, &v, left, right); return v.booleanValue(); } -inline uint __qmljs_cmp_eq(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_eq(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_eq(ctx, &v, left, right); return v.booleanValue(); } -inline uint __qmljs_cmp_ne(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_ne(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_ne(ctx, &v, left, right); return v.booleanValue(); } -inline uint __qmljs_cmp_se(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_se(Context *ctx, const Value *left, const Value *right) { return __qmljs_strict_equal(ctx, left, right); } -inline uint __qmljs_cmp_sne(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_sne(Context *ctx, const Value *left, const Value *right) { return ! __qmljs_strict_equal(ctx, left, right); } -inline uint __qmljs_cmp_instanceof(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_instanceof(Context *ctx, const Value *left, const Value *right) { Value v; __qmljs_instanceof(ctx, &v, left, right); @@ -1247,7 +1248,7 @@ inline uint __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right) return v.booleanValue(); } -inline uint __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y) +inline Bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y) { if (x->rawValue() == y->rawValue()) return true; -- cgit v1.2.3 From 69e7dfe5f4fcb0ae5be697ae7d87c761f7f77498 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 21:08:52 +0200 Subject: Convert some more methods to the new calling convention Change-Id: Ieeeb86d94d1094213430a1b4afa0d3a91785ac45 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 8 ++-- qmljs_runtime.cpp | 109 +++++++++++++++++++++++++++--------------------------- qmljs_runtime.h | 41 ++++++++++---------- 3 files changed, 79 insertions(+), 79 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index fb7e3f629a..713968d5cd 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -304,22 +304,22 @@ void __qmljs_llvm_get_this_object(Context *ctx, Value *result) void __qmljs_llvm_delete_subscript(Context *ctx, Value *result, Value *base, Value *index) { - __qmljs_delete_subscript(ctx, result, base, index); + *result = __qmljs_delete_subscript(ctx, *base, *index); } void __qmljs_llvm_delete_member(Context *ctx, Value *result, Value *base, String *name) { - __qmljs_delete_member(ctx, result, base, name); + *result = __qmljs_delete_member(ctx, *base, name); } void __qmljs_llvm_delete_property(Context *ctx, Value *result, String *name) { - __qmljs_delete_property(ctx, result, name); + *result = __qmljs_delete_property(ctx, name); } void __qmljs_llvm_delete_value(Context *ctx, Value *result, Value *value) { - __qmljs_delete_value(ctx, result, value); + *result = __qmljs_delete_value(ctx, *value); } void __qmljs_llvm_init_this_object(Context *ctx) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index b27a7f1d9d..8b2c7c9f5a 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -449,45 +449,44 @@ Value __qmljs_string_literal_function(Context *ctx) return Value::fromString(ctx->engine->identifier(QStringLiteral("function"))); } -void __qmljs_delete_subscript(Context *ctx, Value *result, Value *base, Value *index) +Value __qmljs_delete_subscript(Context *ctx, Value base, Value index) { - if (ArrayObject *a = base->asArrayObject()) { + if (ArrayObject *a = base.asArrayObject()) { int n = -1; - if (index->isInteger()) - n = index->integerValue(); - else if (index->isDouble()) - n = index->doubleValue(); + if (index.isInteger()) + n = index.integerValue(); + else if (index.isDouble()) + n = index.doubleValue(); if (n >= 0) { if (n < a->value.size()) { a->value.assign(n, Value::undefinedValue()); - *result = Value::fromBoolean(true); - return; + return Value::fromBoolean(true); } } } - String *name = index->toString(ctx); - __qmljs_delete_member(ctx, result, base, name); + String *name = index.toString(ctx); + return __qmljs_delete_member(ctx, base, name); } -void __qmljs_delete_member(Context *ctx, Value *result, Value *base, String *name) +Value __qmljs_delete_member(Context *ctx, Value base, String *name) { - Value obj = base->toObject(ctx); - *result = Value::fromBoolean(obj.objectValue()->deleteProperty(ctx, name, true)); + Value obj = base.toObject(ctx); + return Value::fromBoolean(obj.objectValue()->deleteProperty(ctx, name, true)); } -void __qmljs_delete_property(Context *ctx, Value *result, String *name) +Value __qmljs_delete_property(Context *ctx, String *name) { Value obj = ctx->activation; if (! obj.isObject()) obj = ctx->engine->globalObject; - *result = Value::fromBoolean(obj.objectValue()->deleteProperty(ctx, name, true)); + return Value::fromBoolean(obj.objectValue()->deleteProperty(ctx, name, true)); } -void __qmljs_delete_value(Context *ctx, Value *result, Value *value) +Value __qmljs_delete_value(Context *ctx, Value value) { Q_UNUSED(value); - *result = __qmljs_throw_type_error(ctx); // ### throw syntax error + return __qmljs_throw_type_error(ctx); // ### throw syntax error } void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Value *right) @@ -992,78 +991,78 @@ Value __qmljs_get_thisObject(Context *ctx) return ctx->engine->globalObject; } -void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y, bool leftFirst) +Value __qmljs_compare(Context *ctx, const Value x, const Value y, bool leftFirst) { Value px, py; if (leftFirst) { - px = __qmljs_to_primitive(ctx, *x, NUMBER_HINT); - py = __qmljs_to_primitive(ctx, *y, NUMBER_HINT); + px = __qmljs_to_primitive(ctx, x, NUMBER_HINT); + py = __qmljs_to_primitive(ctx, y, NUMBER_HINT); } else { - px = __qmljs_to_primitive(ctx, *x, NUMBER_HINT); - py = __qmljs_to_primitive(ctx, *y, NUMBER_HINT); + px = __qmljs_to_primitive(ctx, x, NUMBER_HINT); + py = __qmljs_to_primitive(ctx, y, NUMBER_HINT); } if (px.isString() && py.isString()) { bool r = __qmljs_string_compare(ctx, px.stringValue(), py.stringValue()); - *result = Value::fromBoolean(r); + return Value::fromBoolean(r); } else { double nx = __qmljs_to_number(px, ctx); double ny = __qmljs_to_number(py, ctx); if (isnan(nx) || isnan(ny)) { - *result = Value::undefinedValue(); + return Value::undefinedValue(); } else { - *result = Value::fromBoolean(nx < ny); + return Value::fromBoolean(nx < ny); } } } -uint __qmljs_equal(Context *ctx, const Value *x, const Value *y) +uint __qmljs_equal(Context *ctx, const Value x, const Value y) { - if (x->type() == y->type()) { - switch (x->type()) { + if (x.type() == y.type()) { + switch (x.type()) { case Value::Undefined_Type: return true; case Value::Null_Type: return true; case Value::Boolean_Type: - return x->booleanValue() == y->booleanValue(); + return x.booleanValue() == y.booleanValue(); break; case Value::Integer_Type: - return x->integerValue() == y->integerValue(); + return x.integerValue() == y.integerValue(); case Value::String_Type: - return __qmljs_string_equal(ctx, x->stringValue(), y->stringValue()); + return __qmljs_string_equal(ctx, x.stringValue(), y.stringValue()); case Value::Object_Type: - return x->objectValue() == y->objectValue(); + return x.objectValue() == y.objectValue(); default: // double - return x->doubleValue() == y->doubleValue(); + return x.doubleValue() == y.doubleValue(); } // unreachable } else { - if (x->isNumber() && y->isNumber()) - return x == y; - if (x->isNull() && y->isUndefined()) { + if (x.isNumber() && y.isNumber()) + return x.asDouble() == y.asDouble(); + if (x.isNull() && y.isUndefined()) { return true; - } else if (x->isUndefined() && y->isNull()) { + } else if (x.isUndefined() && y.isNull()) { return true; - } else if (x->isNumber() && y->isString()) { - Value ny = Value::fromDouble(__qmljs_to_number(*y, ctx)); - return __qmljs_equal(ctx, x, &ny); - } else if (x->isString() && y->isNumber()) { - Value nx = Value::fromDouble(__qmljs_to_number(*x, ctx)); - return __qmljs_equal(ctx, &nx, y); - } else if (x->isBoolean()) { - Value nx = Value::fromDouble((double) x->booleanValue()); - return __qmljs_equal(ctx, &nx, y); - } else if (y->isBoolean()) { - Value ny = Value::fromDouble((double) y->booleanValue()); - return __qmljs_equal(ctx, x, &ny); - } else if ((x->isNumber() || x->isString()) && y->isObject()) { - Value py = __qmljs_to_primitive(ctx, *y, PREFERREDTYPE_HINT); - return __qmljs_equal(ctx, x, &py); - } else if (x->isObject() && (y->isNumber() || y->isString())) { - Value px = __qmljs_to_primitive(ctx, *x, PREFERREDTYPE_HINT); - return __qmljs_equal(ctx, &px, y); + } else if (x.isNumber() && y.isString()) { + Value ny = Value::fromDouble(__qmljs_to_number(y, ctx)); + return __qmljs_equal(ctx, x, ny); + } else if (x.isString() && y.isNumber()) { + Value nx = Value::fromDouble(__qmljs_to_number(x, ctx)); + return __qmljs_equal(ctx, nx, y); + } else if (x.isBoolean()) { + Value nx = Value::fromDouble((double) x.booleanValue()); + return __qmljs_equal(ctx, nx, y); + } else if (y.isBoolean()) { + Value ny = Value::fromDouble((double) y.booleanValue()); + return __qmljs_equal(ctx, x, ny); + } else if ((x.isNumber() || x.isString()) && y.isObject()) { + Value py = __qmljs_to_primitive(ctx, y, PREFERREDTYPE_HINT); + return __qmljs_equal(ctx, x, py); + } else if (x.isObject() && (y.isNumber() || y.isString())) { + Value px = __qmljs_to_primitive(ctx, x, PREFERREDTYPE_HINT); + return __qmljs_equal(ctx, px, y); } } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index c4a41e334b..b69e8b98fd 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -164,9 +164,9 @@ Value __qmljs_to_object(Context *ctx, const Value value); Bool __qmljs_is_callable(Context *ctx, const Value value); Value __qmljs_default_value(Context *ctx, const Value value, int typeHint); -void __qmljs_compare(Context *ctx, Value *result, const Value *left, const Value *right, bool leftFlag); -Bool __qmljs_equal(Context *ctx, const Value *x, const Value *y); -Bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y); +Value __qmljs_compare(Context *ctx, const Value left, const Value right, bool leftFlag); +Bool __qmljs_equal(Context *ctx, const Value x, const Value y); +Bool __qmljs_strict_equal(Context *ctx, const Value x, const Value y); // unary operators Value __qmljs_uplus(const Value value, Context *ctx); @@ -174,10 +174,11 @@ Value __qmljs_uminus(const Value value, Context *ctx); Value __qmljs_compl(const Value value, Context *ctx); Value __qmljs_not(const Value value, Context *ctx); -void __qmljs_delete_subscript(Context *ctx, Value *result, Value *base, Value *index); -void __qmljs_delete_member(Context *ctx, Value *result, Value *base, String *name); -void __qmljs_delete_property(Context *ctx, Value *result, String *name); -void __qmljs_delete_value(Context *ctx, Value *result, Value *value); +/* ### these 4 methods are apparently unused right now */ +Value __qmljs_delete_subscript(Context *ctx, Value base, Value index); +Value __qmljs_delete_member(Context *ctx, Value base, String *name); +Value __qmljs_delete_property(Context *ctx, String *name); +Value __qmljs_delete_value(Context *ctx, Value value); Value __qmljs_typeof(Context *ctx, const Value value); void __qmljs_throw(Context *context, Value value); @@ -1101,7 +1102,7 @@ inline void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Val if (left->isNumber() && right->isNumber()) { *result = Value::fromBoolean(left->asDouble() > right->asDouble()); } else { - __qmljs_compare(ctx, result, left, right, false); + *result = __qmljs_compare(ctx, *left, *right, false); if (result->isUndefined()) *result = Value::fromBoolean(false); @@ -1115,7 +1116,7 @@ inline void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Val if (left->isNumber() && right->isNumber()) { *result = Value::fromBoolean(left->asDouble() < right->asDouble()); } else { - __qmljs_compare(ctx, result, left, right, true); + *result = __qmljs_compare(ctx, *left, *right, true); if (result->isUndefined()) *result = Value::fromBoolean(false); @@ -1129,7 +1130,7 @@ inline void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Val if (left->isNumber() && right->isNumber()) { *result = Value::fromBoolean(left->asDouble() >= right->asDouble()); } else { - __qmljs_compare(ctx, result, right, left, false); + *result = __qmljs_compare(ctx, *right, *left, false); bool r = ! (result->isUndefined() || (result->isBoolean() && result->booleanValue())); *result = Value::fromBoolean(r); @@ -1143,7 +1144,7 @@ inline void __qmljs_le(Context *ctx, Value *result, const Value *left, const Val if (left->isNumber() && right->isNumber()) { *result = Value::fromBoolean(left->asDouble() <= right->asDouble()); } else { - __qmljs_compare(ctx, result, right, left, true); + *result = __qmljs_compare(ctx, *right, *left, true); bool r = ! (result->isUndefined() || (result->isBoolean() && result->booleanValue())); *result = Value::fromBoolean(r); @@ -1159,7 +1160,7 @@ inline void __qmljs_eq(Context *ctx, Value *result, const Value *left, const Val } else if (left->isString() && right->isString()) { *result = Value::fromBoolean(__qmljs_string_equal(ctx, left->stringValue(), right->stringValue())); } else { - bool r = __qmljs_equal(ctx, left, right); + bool r = __qmljs_equal(ctx, *left, *right); *result = Value::fromBoolean(r); } } @@ -1172,13 +1173,13 @@ inline void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Val inline void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right) { - bool r = __qmljs_strict_equal(ctx, left, right); + bool r = __qmljs_strict_equal(ctx, *left, *right); *result = Value::fromBoolean(r); } inline void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *right) { - bool r = ! __qmljs_strict_equal(ctx, left, right); + bool r = ! __qmljs_strict_equal(ctx, *left, *right); *result = Value::fromBoolean(r); } @@ -1226,12 +1227,12 @@ inline Bool __qmljs_cmp_ne(Context *ctx, const Value *left, const Value *right) inline Bool __qmljs_cmp_se(Context *ctx, const Value *left, const Value *right) { - return __qmljs_strict_equal(ctx, left, right); + return __qmljs_strict_equal(ctx, *left, *right); } inline Bool __qmljs_cmp_sne(Context *ctx, const Value *left, const Value *right) { - return ! __qmljs_strict_equal(ctx, left, right); + return ! __qmljs_strict_equal(ctx, *left, *right); } inline Bool __qmljs_cmp_instanceof(Context *ctx, const Value *left, const Value *right) @@ -1248,12 +1249,12 @@ inline uint __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right) return v.booleanValue(); } -inline Bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y) +inline Bool __qmljs_strict_equal(Context *ctx, const Value x, const Value y) { - if (x->rawValue() == y->rawValue()) + if (x.rawValue() == y.rawValue()) return true; - if (x->isString() && y->isString()) - return __qmljs_string_equal(ctx, x->stringValue(), y->stringValue()); + if (x.isString() && y.isString()) + return __qmljs_string_equal(ctx, x.stringValue(), y.stringValue()); return false; } -- cgit v1.2.3 From 35bb45863d92413a5ed5223e4e2399134ef155fb Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 22:19:33 +0200 Subject: Small cleanup Change-Id: I81f9f49df0ecd94081d111e691494ec746ca58ee Reviewed-by: Simon Hausmann --- qv4isel_masm.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index ec8c6444eb..f266886d62 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -348,8 +348,7 @@ void InstructionSelection::visitMove(IR::Move *s) break; case IR::NumberType: // ### Taking address of pointer inside IR. - loadDouble(&c->value, FPGpr0); - storeDouble(FPGpr0, dest); + copyValue(dest, &c->value); break; default: Q_UNIMPLEMENTED(); -- cgit v1.2.3 From bd0e6209dd7490c93f2b9a4ac85e6315ca003b87 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 22:00:49 +0200 Subject: Convert most operations to the new calling convention Change-Id: I2dbf61b215a03a3c44c9c6bc431859bb9d067625 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 42 +++---- moth/qv4instr_moth_p.h | 2 +- moth/qv4isel_moth.cpp | 2 +- moth/qv4vme_moth.cpp | 2 +- qmljs_runtime.cpp | 52 ++++---- qmljs_runtime.h | 331 ++++++++++++++++++++++++------------------------- qv4isel_masm.cpp | 37 +++--- 7 files changed, 232 insertions(+), 236 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 713968d5cd..96d5e36a08 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -93,107 +93,107 @@ bool __qmljs_llvm_to_boolean(Context *ctx, const Value *value) void __qmljs_llvm_bit_and(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_bit_and(ctx, result, left, right); + *result = __qmljs_bit_and(*left, *right, ctx); } void __qmljs_llvm_bit_or(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_bit_or(ctx, result, left, right); + *result = __qmljs_bit_or(*left, *right, ctx); } void __qmljs_llvm_bit_xor(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_bit_xor(ctx, result, left, right); + *result = __qmljs_bit_xor(*left, *right, ctx); } void __qmljs_llvm_add(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_add(ctx, result, left, right); + *result = __qmljs_add(*left, *right, ctx); } void __qmljs_llvm_sub(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_sub(ctx, result, left, right); + *result = __qmljs_sub(*left, *right, ctx); } void __qmljs_llvm_mul(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_mul(ctx, result, left, right); + *result = __qmljs_mul(*left, *right, ctx); } void __qmljs_llvm_div(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_div(ctx, result, left, right); + *result = __qmljs_div(*left, *right, ctx); } void __qmljs_llvm_mod(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_mod(ctx, result, left, right); + *result = __qmljs_mod(*left, *right, ctx); } void __qmljs_llvm_shl(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_shl(ctx, result, left, right); + *result = __qmljs_shl(*left, *right, ctx); } void __qmljs_llvm_shr(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_shr(ctx, result, left, right); + *result = __qmljs_shr(*left, *right, ctx); } void __qmljs_llvm_ushr(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_ushr(ctx, result, left, right); + *result = __qmljs_ushr(*left, *right, ctx); } void __qmljs_llvm_gt(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_gt(ctx, result, left, right); + *result = __qmljs_gt(*left, *right, ctx); } void __qmljs_llvm_lt(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_lt(ctx, result, left, right); + *result = __qmljs_lt(*left, *right, ctx); } void __qmljs_llvm_ge(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_ge(ctx, result, left, right); + *result = __qmljs_ge(*left, *right, ctx); } void __qmljs_llvm_le(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_le(ctx, result, left, right); + *result = __qmljs_le(*left, *right, ctx); } void __qmljs_llvm_eq(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_eq(ctx, result, left, right); + *result = __qmljs_eq(*left, *right, ctx); } void __qmljs_llvm_ne(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_ne(ctx, result, left, right); + *result = __qmljs_ne(*left, *right, ctx); } void __qmljs_llvm_se(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_se(ctx, result, left, right); + *result = __qmljs_se(*left, *right, ctx); } void __qmljs_llvm_sne(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_sne(ctx, result, left, right); + *result = __qmljs_sne(*left, *right, ctx); } void __qmljs_llvm_instanceof(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_instanceof(ctx, result, left, right); + *result = __qmljs_instanceof(*left, *right, ctx); } void __qmljs_llvm_in(Context *ctx, Value *result, Value *left, Value *right) { - __qmljs_in(ctx, result, left, right); + *result = __qmljs_in(*left, *right, ctx); } void __qmljs_llvm_uplus(Context *ctx, Value *result, const Value *value) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index d9f2c11cdd..f9931defcb 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -101,7 +101,7 @@ union Instr MOTH_INSTR_HEADER int lhsTempIndex; int rhsTempIndex; - void (*alu)(VM::Context *, VM::Value *, const VM::Value *, const VM::Value *); + VM::Value (*alu)(const VM::Value , const VM::Value, VM::Context *); }; instr_common common; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index bfa9a0624b..d37bcdaf65 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -130,7 +130,7 @@ void InstructionSelection::visitMove(IR::Move *s) qWarning("UNKNOWN MOVE"); } -typedef void (*ALUFunction)(VM::Context*, VM::Value*, const VM::Value*, const VM::Value *); +typedef VM::Value (*ALUFunction)(const VM::Value, const VM::Value, VM::Context*); ALUFunction aluOpFunction(IR::AluOp op) { switch (op) { diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index bc8c9b5f97..4bc2304967 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -146,7 +146,7 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_END_INSTR(CJump) MOTH_BEGIN_INSTR(Binop) - instr.alu(context, &tempRegister, &TEMP(instr.lhsTempIndex), &TEMP(instr.rhsTempIndex)); + tempRegister = instr.alu(TEMP(instr.lhsTempIndex), TEMP(instr.rhsTempIndex), context); MOTH_END_INSTR(Binop) MOTH_BEGIN_INSTR(Ret) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 8b2c7c9f5a..94c2bc299c 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -489,43 +489,41 @@ Value __qmljs_delete_value(Context *ctx, Value value) return __qmljs_throw_type_error(ctx); // ### throw syntax error } -void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Value *right) +Value __qmljs_add_helper(const Value left, const Value right, Context *ctx) { - Value pleft = __qmljs_to_primitive(ctx, *left, PREFERREDTYPE_HINT); - Value pright = __qmljs_to_primitive(ctx, *right, PREFERREDTYPE_HINT); + Value pleft = __qmljs_to_primitive(ctx, left, PREFERREDTYPE_HINT); + Value pright = __qmljs_to_primitive(ctx, right, PREFERREDTYPE_HINT); if (pleft.isString() || pright.isString()) { if (!pleft.isString()) pleft = __qmljs_to_string(ctx, pleft); if (!pright.isString()) pright = __qmljs_to_string(ctx, pright); String *string = __qmljs_string_concat(ctx, pleft.stringValue(), pright.stringValue()); - *result = Value::fromString(string); - } else { - double x = __qmljs_to_number(pleft, ctx); - double y = __qmljs_to_number(pright, ctx); - *result = Value::fromDouble(x + y); + return Value::fromString(string); } + double x = __qmljs_to_number(pleft, ctx); + double y = __qmljs_to_number(pright, ctx); + return Value::fromDouble(x + y); } -void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Value *right) +Value __qmljs_instanceof(const Value left, const Value right, Context *ctx) { - if (FunctionObject *function = right->asFunctionObject()) { - bool r = function->hasInstance(ctx, *left); - *result = Value::fromBoolean(r); - return; + if (FunctionObject *function = right.asFunctionObject()) { + bool r = function->hasInstance(ctx, left); + return Value::fromBoolean(r); } - *result = __qmljs_throw_type_error(ctx); + return __qmljs_throw_type_error(ctx); } -void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *right) +Value __qmljs_in(const Value left, const Value right, Context *ctx) { - if (right->isObject()) { - Value s = __qmljs_to_string(ctx, *left); - bool r = right->objectValue()->hasProperty(ctx, s.stringValue()); - *result = Value::fromBoolean(r); + if (right.isObject()) { + Value s = __qmljs_to_string(ctx, left); + bool r = right.objectValue()->hasProperty(ctx, s.stringValue()); + return Value::fromBoolean(r); } else { - *result = __qmljs_throw_type_error(ctx); + return __qmljs_throw_type_error(ctx); } } @@ -547,7 +545,7 @@ void __qmljs_inplace_bit_xor_name(Context *ctx, String *name, Value *value) void __qmljs_inplace_add_name(Context *ctx, String *name, Value *value) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) - __qmljs_add(ctx, prop, prop, value); + *prop = __qmljs_add(*prop, *value, ctx); else ctx->throwReferenceError(Value::fromString(name)); } @@ -599,7 +597,7 @@ void __qmljs_inplace_bit_or_element(Context *ctx, Value *base, Value *index, Val if (index->isNumber()) { const quint32 idx = index->toUInt32(ctx); Value v = a->value.at(idx); - __qmljs_bit_or(ctx, &v, &v, value); + v = __qmljs_bit_or(v, *value, ctx); a->value.assign(idx, v); return; } @@ -614,7 +612,7 @@ void __qmljs_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, Va if (index->isNumber()) { const quint32 idx = index->toUInt32(ctx); Value v = a->value.at(idx); - __qmljs_bit_xor(ctx, &v, &v, value); + v = __qmljs_bit_xor(v, *value, ctx); a->value.assign(idx, v); return; } @@ -629,7 +627,7 @@ void __qmljs_inplace_add_element(Context *ctx, Value *base, Value *index, Value if (index->isNumber()) { const quint32 idx = index->toUInt32(ctx); Value v = a->value.at(idx); - __qmljs_add(ctx, &v, &v, value); + v = __qmljs_add(v, *value, ctx); a->value.assign(idx, v); return; } @@ -644,7 +642,7 @@ void __qmljs_inplace_sub_element(Context *ctx, Value *base, Value *index, Value if (index->isNumber()) { const quint32 idx = index->toUInt32(ctx); Value v = a->value.at(idx); - __qmljs_sub(ctx, &v, &v, value); + v = __qmljs_sub(v, *value, ctx); a->value.assign(idx, v); return; } @@ -700,14 +698,14 @@ void __qmljs_inplace_bit_xor_member(Context *ctx, Value *base, String *name, Val void __qmljs_inplace_add_member(Context *ctx, Value *base, String *name, Value *value) { Value prop = base->objectValue()->getProperty(ctx, name); - __qmljs_add(ctx, &prop, &prop, value); + prop = __qmljs_add(prop, *value, ctx); base->objectValue()->setProperty(ctx, name, prop); } void __qmljs_inplace_sub_member(Context *ctx, Value *base, String *name, Value *value) { Value prop = base->objectValue()->getProperty(ctx, name); - __qmljs_sub(ctx, &prop, &prop, value); + prop = __qmljs_sub(prop, *value, ctx); base->objectValue()->setProperty(ctx, name, prop); } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index b69e8b98fd..2feadbe164 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -185,29 +185,29 @@ void __qmljs_throw(Context *context, Value value); Value __qmljs_rethrow(Context *context); // binary operators -void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_bit_or(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_bit_xor(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_bit_and(Context *ctx, Value *result, const Value *left,const Value *right); -void __qmljs_add(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_sub(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_mul(Context *ctx, Value *result, const Value *left,const Value *right); -void __qmljs_div(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_mod(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_shl(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_shr(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_le(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_eq(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *right); - -void __qmljs_add_helper(Context *ctx, Value *result, const Value *left, const Value *right); +Value __qmljs_instanceof(const Value left, const Value right, Context *ctx); +Value __qmljs_in(const Value left, const Value right, Context *ctx); +Value __qmljs_bit_or(const Value left, const Value right, Context *ctx); +Value __qmljs_bit_xor(const Value left, const Value right, Context *ctx); +Value __qmljs_bit_and(const Value left, const Value right, Context *ctx); +Value __qmljs_add(const Value left, const Value right, Context *ctx); +Value __qmljs_sub(const Value left, const Value right, Context *ctx); +Value __qmljs_mul(const Value left, const Value right, Context *ctx); +Value __qmljs_div(const Value left, const Value right, Context *ctx); +Value __qmljs_mod(const Value left, const Value right, Context *ctx); +Value __qmljs_shl(const Value left, const Value right, Context *ctx); +Value __qmljs_shr(const Value left, const Value right, Context *ctx); +Value __qmljs_ushr(const Value left, const Value right, Context *ctx); +Value __qmljs_gt(const Value left, const Value right, Context *ctx); +Value __qmljs_lt(const Value left, const Value right, Context *ctx); +Value __qmljs_ge(const Value left, const Value right, Context *ctx); +Value __qmljs_le(const Value left, const Value right, Context *ctx); +Value __qmljs_eq(const Value left, const Value right, Context *ctx); +Value __qmljs_ne(const Value left, const Value right, Context *ctx); +Value __qmljs_se(const Value left, const Value right, Context *ctx); +Value __qmljs_sne(const Value left, const Value right, Context *ctx); + +Value __qmljs_add_helper(const Value left, const Value right, Context *ctx); void __qmljs_inplace_bit_and(Context *ctx, Value *result, Value *value); void __qmljs_inplace_bit_or(Context *ctx, Value *result, Value *value); @@ -257,16 +257,16 @@ void __qmljs_inplace_shl_member(Context *ctx, Value *base, String *name, Value * void __qmljs_inplace_shr_member(Context *ctx, Value *base, String *name, Value *value); void __qmljs_inplace_ushr_member(Context *ctx, Value *base, String *name, Value *value); -Bool __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_le(Context *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_eq(Context *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_ne(Context *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_se(Context *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_sne(Context *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_instanceof(Context *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_gt(Context *ctx, const Value left, const Value right); +Bool __qmljs_cmp_lt(Context *ctx, const Value left, const Value right); +Bool __qmljs_cmp_ge(Context *ctx, const Value left, const Value right); +Bool __qmljs_cmp_le(Context *ctx, const Value left, const Value right); +Bool __qmljs_cmp_eq(Context *ctx, const Value left, const Value right); +Bool __qmljs_cmp_ne(Context *ctx, const Value left, const Value right); +Bool __qmljs_cmp_se(Context *ctx, const Value left, const Value right); +Bool __qmljs_cmp_sne(Context *ctx, const Value left, const Value right); +Bool __qmljs_cmp_instanceof(Context *ctx, const Value left, const Value right); +Bool __qmljs_cmp_in(Context *ctx, const Value left, const Value right); } // extern "C" @@ -944,80 +944,80 @@ inline Value __qmljs_not(const Value value, Context *ctx) } // binary operators -inline void __qmljs_bit_or(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_bit_or(const Value left, const Value right, Context *ctx) { - int lval = __qmljs_to_int32(*left, ctx); - int rval = __qmljs_to_int32(*right, ctx); - *result = Value::fromDouble(lval | rval); + int lval = __qmljs_to_int32(left, ctx); + int rval = __qmljs_to_int32(right, ctx); + return Value::fromDouble(lval | rval); } -inline void __qmljs_bit_xor(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_bit_xor(const Value left, const Value right, Context *ctx) { - int lval = __qmljs_to_int32(*left, ctx); - int rval = __qmljs_to_int32(*right, ctx); - *result = Value::fromDouble(lval ^ rval); + int lval = __qmljs_to_int32(left, ctx); + int rval = __qmljs_to_int32(right, ctx); + return Value::fromDouble(lval ^ rval); } -inline void __qmljs_bit_and(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_bit_and(const Value left, const Value right, Context *ctx) { - int lval = __qmljs_to_int32(*left, ctx); - int rval = __qmljs_to_int32(*right, ctx); - *result = Value::fromDouble(lval & rval); + int lval = __qmljs_to_int32(left, ctx); + int rval = __qmljs_to_int32(right, ctx); + return Value::fromDouble(lval & rval); } inline void __qmljs_inplace_bit_and(Context *ctx, Value *result, Value *value) { - __qmljs_bit_xor(ctx, result, result, value); + *result = __qmljs_bit_xor(*result, *value, ctx); } inline void __qmljs_inplace_bit_or(Context *ctx, Value *result, Value *value) { - __qmljs_bit_or(ctx, result, result, value); + *result = __qmljs_bit_or(*result, *value, ctx); } inline void __qmljs_inplace_bit_xor(Context *ctx, Value *result, Value *value) { - __qmljs_bit_xor(ctx, result, result, value); + *result = __qmljs_bit_xor(*result, *value, ctx); } inline void __qmljs_inplace_add(Context *ctx, Value *result, Value *value) { - __qmljs_add(ctx, result, result, value); + *result = __qmljs_add(*result, *value, ctx); } inline void __qmljs_inplace_sub(Context *ctx, Value *result, Value *value) { - __qmljs_sub(ctx, result, result, value); + *result = __qmljs_sub(*result, *value, ctx); } inline void __qmljs_inplace_mul(Context *ctx, Value *result, Value *value) { - __qmljs_mul(ctx, result, result, value); + *result = __qmljs_mul(*result, *value, ctx); } inline void __qmljs_inplace_div(Context *ctx, Value *result, Value *value) { - __qmljs_div(ctx, result, result, value); + *result = __qmljs_div(*result, *value, ctx); } inline void __qmljs_inplace_mod(Context *ctx, Value *result, Value *value) { - __qmljs_mod(ctx, result, result, value); + *result = __qmljs_mod(*result, *value, ctx); } inline void __qmljs_inplace_shl(Context *ctx, Value *result, Value *value) { - __qmljs_shl(ctx, result, result, value); + *result = __qmljs_shl(*result, *value, ctx); } inline void __qmljs_inplace_shr(Context *ctx, Value *result, Value *value) { - __qmljs_shr(ctx, result, result, value); + *result = __qmljs_shr(*result, *value, ctx); } inline void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value) { - __qmljs_ushr(ctx, result, result, value); + *result = __qmljs_ushr(*result, *value, ctx); } static inline Value add_int32(int a, int b) @@ -1035,217 +1035,212 @@ static inline Value add_int32(int a, int b) return Value::fromDouble((double)a * b); } -inline void __qmljs_add(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_add(const Value left, const Value right, Context *ctx) { - if (left->isInteger() && right->isInteger()) - *result = add_int32(left->integerValue(), right->integerValue()); + if (left.isInteger() && right.isInteger()) + return add_int32(left.integerValue(), right.integerValue()); - if (left->isNumber() && right->isNumber()) - *result = Value::fromDouble(left->asDouble() + right->asDouble()); + if (left.isNumber() && right.isNumber()) + return Value::fromDouble(left.asDouble() + right.asDouble()); else - __qmljs_add_helper(ctx, result, left, right); + return __qmljs_add_helper(left, right, ctx); } -inline void __qmljs_sub(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_sub(const Value left, const Value right, Context *ctx) { - double lval = __qmljs_to_number(*left, ctx); - double rval = __qmljs_to_number(*right, ctx); - *result = Value::fromDouble(lval - rval); + double lval = __qmljs_to_number(left, ctx); + double rval = __qmljs_to_number(right, ctx); + return Value::fromDouble(lval - rval); } -inline void __qmljs_mul(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_mul(const Value left, const Value right, Context *ctx) { - double lval = __qmljs_to_number(*left, ctx); - double rval = __qmljs_to_number(*right, ctx); - *result = Value::fromDouble(lval * rval); + double lval = __qmljs_to_number(left, ctx); + double rval = __qmljs_to_number(right, ctx); + return Value::fromDouble(lval * rval); } -inline void __qmljs_div(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_div(const Value left, const Value right, Context *ctx) { - double lval = __qmljs_to_number(*left, ctx); - double rval = __qmljs_to_number(*right, ctx); - *result = Value::fromDouble(lval / rval); + double lval = __qmljs_to_number(left, ctx); + double rval = __qmljs_to_number(right, ctx); + return Value::fromDouble(lval / rval); } -inline void __qmljs_mod(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_mod(const Value left, const Value right, Context *ctx) { - double lval = __qmljs_to_number(*left, ctx); - double rval = __qmljs_to_number(*right, ctx); - *result = Value::fromDouble(fmod(lval, rval)); + double lval = __qmljs_to_number(left, ctx); + double rval = __qmljs_to_number(right, ctx); + return Value::fromDouble(fmod(lval, rval)); } -inline void __qmljs_shl(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_shl(const Value left, const Value right, Context *ctx) { - int lval = __qmljs_to_int32(*left, ctx); - unsigned rval = __qmljs_to_uint32(*right, ctx); - *result = Value::fromDouble(lval << rval); + int lval = __qmljs_to_int32(left, ctx); + unsigned rval = __qmljs_to_uint32(right, ctx); + return Value::fromDouble(lval << rval); } -inline void __qmljs_shr(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_shr(const Value left, const Value right, Context *ctx) { - int lval = __qmljs_to_int32(*left, ctx); - unsigned rval = __qmljs_to_uint32(*right, ctx); - *result = Value::fromDouble(lval >> rval); + int lval = __qmljs_to_int32(left, ctx); + unsigned rval = __qmljs_to_uint32(right, ctx); + return Value::fromDouble(lval >> rval); } -inline void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_ushr(const Value left, const Value right, Context *ctx) { - unsigned lval = __qmljs_to_uint32(*left, ctx); - unsigned rval = __qmljs_to_uint32(*right, ctx); - *result = Value::fromDouble(lval >> rval); + unsigned lval = __qmljs_to_uint32(left, ctx); + unsigned rval = __qmljs_to_uint32(right, ctx); + return Value::fromDouble(lval >> rval); } -inline void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_gt(const Value left, const Value right, Context *ctx) { - if (left->isInteger() && right->isInteger()) - *result = Value::fromBoolean(left->integerValue() > right->integerValue()); - if (left->isNumber() && right->isNumber()) { - *result = Value::fromBoolean(left->asDouble() > right->asDouble()); + if (left.isInteger() && right.isInteger()) + return Value::fromBoolean(left.integerValue() > right.integerValue()); + if (left.isNumber() && right.isNumber()) { + return Value::fromBoolean(left.asDouble() > right.asDouble()); } else { - *result = __qmljs_compare(ctx, *left, *right, false); + Value result = __qmljs_compare(ctx, left, right, false); - if (result->isUndefined()) - *result = Value::fromBoolean(false); + if (result.isUndefined()) + result = Value::fromBoolean(false); + return result; } } -inline void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_lt(const Value left, const Value right, Context *ctx) { - if (left->isInteger() && right->isInteger()) - *result = Value::fromBoolean(left->integerValue() < right->integerValue()); - if (left->isNumber() && right->isNumber()) { - *result = Value::fromBoolean(left->asDouble() < right->asDouble()); + if (left.isInteger() && right.isInteger()) + return Value::fromBoolean(left.integerValue() < right.integerValue()); + if (left.isNumber() && right.isNumber()) { + return Value::fromBoolean(left.asDouble() < right.asDouble()); } else { - *result = __qmljs_compare(ctx, *left, *right, true); + Value result = __qmljs_compare(ctx, left, right, true); - if (result->isUndefined()) - *result = Value::fromBoolean(false); + if (result.isUndefined()) + result = Value::fromBoolean(false); + return result; } } -inline void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_ge(const Value left, const Value right, Context *ctx) { - if (left->isInteger() && right->isInteger()) - *result = Value::fromBoolean(left->integerValue() >= right->integerValue()); - if (left->isNumber() && right->isNumber()) { - *result = Value::fromBoolean(left->asDouble() >= right->asDouble()); + if (left.isInteger() && right.isInteger()) + return Value::fromBoolean(left.integerValue() >= right.integerValue()); + if (left.isNumber() && right.isNumber()) { + return Value::fromBoolean(left.asDouble() >= right.asDouble()); } else { - *result = __qmljs_compare(ctx, *right, *left, false); + Value result = __qmljs_compare(ctx, right, left, false); - bool r = ! (result->isUndefined() || (result->isBoolean() && result->booleanValue())); - *result = Value::fromBoolean(r); + bool r = ! (result.isUndefined() || (result.isBoolean() && result.booleanValue())); + return Value::fromBoolean(r); } } -inline void __qmljs_le(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_le(const Value left, const Value right, Context *ctx) { - if (left->isInteger() && right->isInteger()) - *result = Value::fromBoolean(left->integerValue() <= right->integerValue()); - if (left->isNumber() && right->isNumber()) { - *result = Value::fromBoolean(left->asDouble() <= right->asDouble()); + if (left.isInteger() && right.isInteger()) + return Value::fromBoolean(left.integerValue() <= right.integerValue()); + if (left.isNumber() && right.isNumber()) { + return Value::fromBoolean(left.asDouble() <= right.asDouble()); } else { - *result = __qmljs_compare(ctx, *right, *left, true); + Value result = __qmljs_compare(ctx, right, left, true); - bool r = ! (result->isUndefined() || (result->isBoolean() && result->booleanValue())); - *result = Value::fromBoolean(r); + bool r = ! (result.isUndefined() || (result.isBoolean() && result.booleanValue())); + return Value::fromBoolean(r); } } -inline void __qmljs_eq(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_eq(const Value left, const Value right, Context *ctx) { - if (left->val == right->val) - *result = Value::fromBoolean(true); - if (left->isNumber() && right->isNumber()) { - *result = Value::fromBoolean(left->asDouble() == right->asDouble()); - } else if (left->isString() && right->isString()) { - *result = Value::fromBoolean(__qmljs_string_equal(ctx, left->stringValue(), right->stringValue())); + if (left.val == right.val) + return Value::fromBoolean(true); + if (left.isNumber() && right.isNumber()) { + return Value::fromBoolean(left.asDouble() == right.asDouble()); + } else if (left.isString() && right.isString()) { + return Value::fromBoolean(__qmljs_string_equal(ctx, left.stringValue(), right.stringValue())); } else { - bool r = __qmljs_equal(ctx, *left, *right); - *result = Value::fromBoolean(r); + bool r = __qmljs_equal(ctx, left, right); + return Value::fromBoolean(r); } } -inline void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_ne(const Value left, const Value right, Context *ctx) { - __qmljs_eq(ctx, result, left, right); - result->int_32 = !(bool)result->int_32; + Value result = __qmljs_eq(left, right, ctx); + result.int_32 = !(bool)result.int_32; + return result; } -inline void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_se(const Value left, const Value right, Context *ctx) { - bool r = __qmljs_strict_equal(ctx, *left, *right); - *result = Value::fromBoolean(r); + bool r = __qmljs_strict_equal(ctx, left, right); + return Value::fromBoolean(r); } -inline void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *right) +inline Value __qmljs_sne(const Value left, const Value right, Context *ctx) { - bool r = ! __qmljs_strict_equal(ctx, *left, *right); - *result = Value::fromBoolean(r); + bool r = ! __qmljs_strict_equal(ctx, left, right); + return Value::fromBoolean(r); } -inline Bool __qmljs_cmp_gt(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_gt(Context *ctx, const Value left, const Value right) { - Value v; - __qmljs_gt(ctx, &v, left, right); + Value v = __qmljs_gt(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_lt(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_lt(Context *ctx, const Value left, const Value right) { - Value v; - __qmljs_lt(ctx, &v, left, right); + Value v = __qmljs_lt(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_ge(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_ge(Context *ctx, const Value left, const Value right) { - Value v; - __qmljs_ge(ctx, &v, left, right); + Value v = __qmljs_ge(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_le(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_le(Context *ctx, const Value left, const Value right) { - Value v; - __qmljs_le(ctx, &v, left, right); + Value v = __qmljs_le(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_eq(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_eq(Context *ctx, const Value left, const Value right) { - Value v; - __qmljs_eq(ctx, &v, left, right); + Value v = __qmljs_eq(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_ne(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_ne(Context *ctx, const Value left, const Value right) { - Value v; - __qmljs_ne(ctx, &v, left, right); + Value v = __qmljs_ne(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_se(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_se(Context *ctx, const Value left, const Value right) { - return __qmljs_strict_equal(ctx, *left, *right); + return __qmljs_strict_equal(ctx, left, right); } -inline Bool __qmljs_cmp_sne(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_sne(Context *ctx, const Value left, const Value right) { - return ! __qmljs_strict_equal(ctx, *left, *right); + return ! __qmljs_strict_equal(ctx, left, right); } -inline Bool __qmljs_cmp_instanceof(Context *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_instanceof(Context *ctx, const Value left, const Value right) { - Value v; - __qmljs_instanceof(ctx, &v, left, right); + Value v = __qmljs_instanceof(left, right, ctx); return v.booleanValue(); } -inline uint __qmljs_cmp_in(Context *ctx, const Value *left, const Value *right) +inline uint __qmljs_cmp_in(Context *ctx, const Value left, const Value right) { - Value v; - __qmljs_in(ctx, &v, left, right); + Value v = __qmljs_in(left, right, ctx); return v.booleanValue(); } diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index f266886d62..79e460dbdc 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -308,10 +308,11 @@ void InstructionSelection::visitLeave(IR::Leave *) assert(!"TODO"); } +#define setOp(op, opName, operation) \ + do { op = operation; opName = isel_stringIfy(operation); } while (0) + void InstructionSelection::visitMove(IR::Move *s) { - #define setOp(op, opName, operation) \ - do { op = operation; opName = isel_stringIfy(operation); } while (0) if (s->op == IR::OpInvalid) { if (IR::Name *n = s->target->asName()) { @@ -410,7 +411,7 @@ void InstructionSelection::visitMove(IR::Move *s) IR::Temp *l = b->left->asTemp(); IR::Temp *r = b->right->asTemp(); if (l && r) { - void (*op)(Context *, Value *, const Value *, const Value *) = 0; + Value (*op)(const Value, const Value, Context *) = 0; const char* opName = 0; switch ((IR::AluOp) b->op) { @@ -452,7 +453,7 @@ void InstructionSelection::visitMove(IR::Move *s) } if (op) { - generateFunctionCallImp(opName, op, ContextRegister, t, l, r); + generateFunctionCallImp2(t, opName, op, l, r, ContextRegister); } return; } @@ -491,7 +492,7 @@ void InstructionSelection::visitMove(IR::Move *s) // inplace assignment, e.g. x += 1, ++x, ... if (IR::Temp *t = s->target->asTemp()) { if (IR::Temp *t2 = s->source->asTemp()) { - void (*op)(Context *ctx, Value *result, const Value *left,const Value *right) = 0; + Value (*op)(const Value left, const Value right, Context *ctx) = 0; const char *opName = 0; switch (s->op) { case IR::OpBitAnd: setOp(op, opName, __qmljs_bit_and); break; @@ -510,7 +511,7 @@ void InstructionSelection::visitMove(IR::Move *s) break; } if (op) - generateFunctionCallImp(opName, op, ContextRegister, t, t, t2); + generateFunctionCallImp2(t, opName, op, t, t2, ContextRegister); return; } } else if (IR::Name *n = s->target->asName()) { @@ -604,7 +605,6 @@ void InstructionSelection::visitMove(IR::Move *s) qout << endl; assert(!"TODO"); - #undef setOp } void InstructionSelection::visitJump(IR::Jump *s) @@ -646,20 +646,23 @@ void InstructionSelection::visitCJump(IR::CJump *s) IR::Temp *l = b->left->asTemp(); IR::Temp *r = b->right->asTemp(); if (l && r) { + Bool (*op)(Context *ctx, const Value, const Value) = 0; + const char *opName = 0; switch (b->op) { default: Q_UNREACHABLE(); assert(!"todo"); break; - case IR::OpGt: generateFunctionCall(__qmljs_cmp_gt, ContextRegister, l, r); break; - case IR::OpLt: generateFunctionCall(__qmljs_cmp_lt, ContextRegister, l, r); break; - case IR::OpGe: generateFunctionCall(__qmljs_cmp_ge, ContextRegister, l, r); break; - case IR::OpLe: generateFunctionCall(__qmljs_cmp_le, ContextRegister, l, r); break; - case IR::OpEqual: generateFunctionCall(__qmljs_cmp_eq, ContextRegister, l, r); break; - case IR::OpNotEqual: generateFunctionCall(__qmljs_cmp_ne, ContextRegister, l, r); break; - case IR::OpStrictEqual: generateFunctionCall(__qmljs_cmp_se, ContextRegister, l, r); break; - case IR::OpStrictNotEqual: generateFunctionCall(__qmljs_cmp_sne, ContextRegister, l, r); break; - case IR::OpInstanceof: generateFunctionCall(__qmljs_cmp_instanceof, ContextRegister, l, r); break; - case IR::OpIn: generateFunctionCall(__qmljs_cmp_in, ContextRegister, l, r); break; + case IR::OpGt: setOp(op, opName, __qmljs_cmp_gt); break; + case IR::OpLt: setOp(op, opName, __qmljs_cmp_lt); break; + case IR::OpGe: setOp(op, opName, __qmljs_cmp_ge); break; + case IR::OpLe: setOp(op, opName, __qmljs_cmp_le); break; + case IR::OpEqual: setOp(op, opName, __qmljs_cmp_eq); break; + case IR::OpNotEqual: setOp(op, opName, __qmljs_cmp_ne); break; + case IR::OpStrictEqual: setOp(op, opName, __qmljs_cmp_se); break; + case IR::OpStrictNotEqual: setOp(op, opName, __qmljs_cmp_sne); break; + case IR::OpInstanceof: setOp(op, opName, __qmljs_cmp_instanceof); break; + case IR::OpIn: setOp(op, opName, __qmljs_cmp_in); break; } // switch + generateFunctionCall2(ReturnValueRegister, op, ContextRegister, l, r); move(ReturnValueRegister, Gpr0); Jump target = branch32(NotEqual, Gpr0, TrustedImm32(0)); -- cgit v1.2.3 From d268d6f8216e150186f167bd94da698f12b76a9b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 Oct 2012 22:26:52 +0200 Subject: Store integer constants as integer Values Generate integer Value's during instruction selection. Value is already prepared for it, so this simply speeds up integer heavy JS execution. Speeds up crypto.js by around 60% Change-Id: I3781767fd5b5c799365f3c75245409a9760ae7fb Reviewed-by: Simon Hausmann --- qmljs_math.h | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ qmljs_runtime.h | 46 +++++++++++++-------------- qv4isel_masm.cpp | 12 +++++-- v4.pro | 1 + 4 files changed, 128 insertions(+), 26 deletions(-) create mode 100644 qmljs_math.h diff --git a/qmljs_math.h b/qmljs_math.h new file mode 100644 index 0000000000..8e4ff2c03b --- /dev/null +++ b/qmljs_math.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_MATH_H +#define QMLJS_MATH_H + +#include +#include + +#if 1 //CPU(X86_64) + +static inline Value add_int32(int a, int b) +{ + quint8 overflow = 0; + + asm ("addl %2, %1\n" + "seto %0" + : "=r" (overflow), "=r" (a) + : "r" (b), "1" (a) + : + ); + if (!overflow) + return Value::fromInt32(a); + return Value::fromDouble((double)a + (double)b); +} + +static inline Value sub_int32(int a, int b) +{ + quint8 overflow = 0; + + asm ("subl %2, %1\n" + "seto %0" + : "=r" (overflow), "=r" (a) + : "r" (b), "1" (a) + : + ); + if (!overflow) + return Value::fromInt32(a); + return Value::fromDouble((double)a - (double)b); +} + +static inline Value mul_int32(int a, int b) +{ + quint8 overflow = 0; + + asm ("imul %2, %1\n" + "seto %0" + : "=r" (overflow), "=r" (a) + : "r" (b), "1" (a) + : + ); + if (!overflow) + return Value::fromInt32(a); + return Value::fromDouble((double)a * (double)b); +} +#endif + +#endif diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 2feadbe164..751b15e942 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -385,7 +385,7 @@ template <> struct ValueBase<8> : public ValueData return (ValueType)(tag & Type_Mask); } - bool booleanValue() const { + Bool booleanValue() const { return int_32; } double doubleValue() const { @@ -497,7 +497,7 @@ inline Value ValueBase<4>::fromBoolean(Bool b) { Value v; v.tag = Boolean_Type; - v.int_32 = b; + v.int_32 = (bool)b; return v; } @@ -551,7 +551,7 @@ inline Value ValueBase<8>::fromBoolean(Bool b) { Value v; v.tag = Boolean_Type; - v.int_32 = b; + v.int_32 = (bool)b; return v; } @@ -602,6 +602,13 @@ template <> struct ValueOffsetHelper enum { DataOffset = offsetof(ValueData, uint_32) }; }; +template <> struct ValueOffsetHelper +{ + enum { DataOffset = offsetof(ValueData, uint_32) }; +}; + +#include + struct Context { ExecutionEngine *engine; Context *parent; @@ -685,7 +692,7 @@ inline Bool __qmljs_to_boolean(const Value value, Context *ctx) return false; case Value::Boolean_Type: case Value::Integer_Type: - return value.int_32; + return (bool)value.int_32; case Value::String_Type: return __qmljs_string_length(ctx, value.stringValue()) > 0; case Value::Object_Type: @@ -829,7 +836,7 @@ inline Value __qmljs_to_string(Context *ctx, const Value value) case Value::Integer_Type: return __qmljs_string_from_number(ctx, value.int_32); break; - default: // number + default: // double return __qmljs_string_from_number(ctx, value.doubleValue()); break; @@ -1020,21 +1027,6 @@ inline void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value) *result = __qmljs_ushr(*result, *value, ctx); } -static inline Value add_int32(int a, int b) -{ - quint8 overflow; - - asm ("addl %1, %2\n" - "seto %0" - : "=q" (overflow) - : "r" (b), "r" (a) - : - ); - if (!overflow) - return Value::fromInt32(a); - return Value::fromDouble((double)a * b); -} - inline Value __qmljs_add(const Value left, const Value right, Context *ctx) { if (left.isInteger() && right.isInteger()) @@ -1048,6 +1040,9 @@ inline Value __qmljs_add(const Value left, const Value right, Context *ctx) inline Value __qmljs_sub(const Value left, const Value right, Context *ctx) { + if (left.isInteger() && right.isInteger()) + return sub_int32(left.integerValue(), right.integerValue()); + double lval = __qmljs_to_number(left, ctx); double rval = __qmljs_to_number(right, ctx); return Value::fromDouble(lval - rval); @@ -1055,6 +1050,9 @@ inline Value __qmljs_sub(const Value left, const Value right, Context *ctx) inline Value __qmljs_mul(const Value left, const Value right, Context *ctx) { + if (left.isInteger() && right.isInteger()) + return mul_int32(left.integerValue(), right.integerValue()); + double lval = __qmljs_to_number(left, ctx); double rval = __qmljs_to_number(right, ctx); return Value::fromDouble(lval * rval); @@ -1074,25 +1072,27 @@ inline Value __qmljs_mod(const Value left, const Value right, Context *ctx) return Value::fromDouble(fmod(lval, rval)); } +// ### unsigned shl missing? + inline Value __qmljs_shl(const Value left, const Value right, Context *ctx) { int lval = __qmljs_to_int32(left, ctx); unsigned rval = __qmljs_to_uint32(right, ctx); - return Value::fromDouble(lval << rval); + return Value::fromInt32(lval << rval); } inline Value __qmljs_shr(const Value left, const Value right, Context *ctx) { int lval = __qmljs_to_int32(left, ctx); unsigned rval = __qmljs_to_uint32(right, ctx); - return Value::fromDouble(lval >> rval); + return Value::fromInt32(lval >> rval); } inline Value __qmljs_ushr(const Value left, const Value right, Context *ctx) { unsigned lval = __qmljs_to_uint32(left, ctx); unsigned rval = __qmljs_to_uint32(right, ctx); - return Value::fromDouble(lval >> rval); + return Value::fromInt32(lval >> rval); } inline Value __qmljs_gt(const Value left, const Value right, Context *ctx) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 79e460dbdc..c187613b33 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -347,9 +347,15 @@ void InstructionSelection::visitMove(IR::Move *s) case IR::BoolType: storeValue(TrustedImm32(c->value != 0), dest); break; - case IR::NumberType: - // ### Taking address of pointer inside IR. - copyValue(dest, &c->value); + case IR::NumberType: { + int ival = (int)c->value; + if (ival == c->value) { + storeValue(TrustedImm32(ival), dest); + } else { + // ### Taking address of pointer inside IR. + copyValue(dest, &c->value); + } + } break; default: Q_UNIMPLEMENTED(); diff --git a/v4.pro b/v4.pro index d13cc429e8..b51850e858 100644 --- a/v4.pro +++ b/v4.pro @@ -26,6 +26,7 @@ HEADERS += \ qv4ir_p.h \ qmljs_runtime.h \ qmljs_objects.h \ + qmljs_math.h \ qv4syntaxchecker_p.h \ qv4ecmaobjects_p.h \ qv4array_p.h \ -- cgit v1.2.3 From 217a5fd5d25eb3ca89bfe7cfed4db1cb17f7fce3 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 00:10:09 +0200 Subject: Fix typo Change-Id: I0620cf0521a22cd8ea497eac31fe29896cd1cb98 Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 751b15e942..e4a2419233 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -974,7 +974,7 @@ inline Value __qmljs_bit_and(const Value left, const Value right, Context *ctx) inline void __qmljs_inplace_bit_and(Context *ctx, Value *result, Value *value) { - *result = __qmljs_bit_xor(*result, *value, ctx); + *result = __qmljs_bit_and(*result, *value, ctx); } inline void __qmljs_inplace_bit_or(Context *ctx, Value *result, Value *value) -- cgit v1.2.3 From 951990255e4a6909a5b2f652ac1c651848d2877c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 00:20:00 +0200 Subject: Store results as integers in a few more places Speeds up crypto.js by another 20%. Change-Id: Ic16c824bf337392d64dc0bb3275f8b6a6c27f792 Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index e4a2419233..3b931641ac 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -928,12 +928,16 @@ inline Value __qmljs_typeof(Context *ctx, const Value value) inline Value __qmljs_uplus(const Value value, Context *ctx) { + if (value.isInteger()) + return value; double n = __qmljs_to_number(value, ctx); return Value::fromDouble(n); } inline Value __qmljs_uminus(const Value value, Context *ctx) { + if (value.isInteger()) + return Value::fromInt32(-value.integerValue()); double n = __qmljs_to_number(value, ctx); return Value::fromDouble(-n); } @@ -941,7 +945,7 @@ inline Value __qmljs_uminus(const Value value, Context *ctx) inline Value __qmljs_compl(const Value value, Context *ctx) { int n = __qmljs_to_int32(value, ctx); - return Value::fromDouble(~n); + return Value::fromInt32(~n); } inline Value __qmljs_not(const Value value, Context *ctx) @@ -955,6 +959,7 @@ inline Value __qmljs_bit_or(const Value left, const Value right, Context *ctx) { int lval = __qmljs_to_int32(left, ctx); int rval = __qmljs_to_int32(right, ctx); + // ### changing this to fromInt32() breaks crypto.js return Value::fromDouble(lval | rval); } @@ -962,14 +967,14 @@ inline Value __qmljs_bit_xor(const Value left, const Value right, Context *ctx) { int lval = __qmljs_to_int32(left, ctx); int rval = __qmljs_to_int32(right, ctx); - return Value::fromDouble(lval ^ rval); + return Value::fromInt32(lval ^ rval); } inline Value __qmljs_bit_and(const Value left, const Value right, Context *ctx) { int lval = __qmljs_to_int32(left, ctx); int rval = __qmljs_to_int32(right, ctx); - return Value::fromDouble(lval & rval); + return Value::fromInt32(lval & rval); } inline void __qmljs_inplace_bit_and(Context *ctx, Value *result, Value *value) -- cgit v1.2.3 From f5b98f63862d4f36774d6baea5d3789350844e04 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 09:05:56 +0200 Subject: Generate correct code when calling values __qmljs_call_value was still using a pointer to a Value. In addition, qcv4isel_masm.cpp was using a wrong order of the arguments. Change-Id: I0414aa732ae8074420e4f11525f5b04712cc1bab Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 2 +- moth/qv4vme_moth.cpp | 2 +- qmljs_runtime.cpp | 10 +++++----- qmljs_runtime.h | 2 +- qv4array.cpp | 2 +- qv4ecmaobjects.cpp | 18 +++++++++--------- qv4isel_masm.cpp | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 96d5e36a08..f8770bbfa6 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -229,7 +229,7 @@ void __qmljs_llvm_call_activation_property(Context *context, Value *result, Stri void __qmljs_llvm_call_value(Context *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) { Value that = thisObject ? *thisObject : Value::undefinedValue(); - *result = __qmljs_call_value(context, that, func, args, argc); + *result = __qmljs_call_value(context, that, *func, args, argc); } void __qmljs_llvm_construct_activation_property(Context *context, Value *result, String *name, Value *args, int argc) diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 4bc2304967..f31fb0a858 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -133,7 +133,7 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_BEGIN_INSTR(Call) VM::Value *args = stack.data() + instr.args; - tempRegister = __qmljs_call_value(context, VM::Value::undefinedValue(), &tempRegister, args, instr.argc); + tempRegister = __qmljs_call_value(context, VM::Value::undefinedValue(), tempRegister, args, instr.argc); MOTH_END_INSTR(Call) MOTH_BEGIN_INSTR(Jump) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 94c2bc299c..73391159f2 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -809,14 +809,14 @@ Value __qmljs_object_default_value(Context *ctx, const Value object, int typeHin Value conv = oo->getProperty(ctx, meth1); if (!conv.isUndefined() && conv.isFunctionObject()) { - Value r = __qmljs_call_value(ctx, object, &conv, 0, 0); + Value r = __qmljs_call_value(ctx, object, conv, 0, 0); if (r.isPrimitive()) return r; } conv = oo->getProperty(ctx, meth2); if (!conv.isUndefined() && conv.isFunctionObject()) { - Value r = __qmljs_call_value(ctx, object, &conv, 0, 0); + Value r = __qmljs_call_value(ctx, object, conv, 0, 0); if (r.isPrimitive()) return r; } @@ -1074,7 +1074,7 @@ Value __qmljs_call_activation_property(Context *context, String *name, Value *ar context->throwReferenceError(Value::fromString(name)); return Value::undefinedValue(); } - Value result = __qmljs_call_value(context, Value::undefinedValue(), func, args, argc); + Value result = __qmljs_call_value(context, Value::undefinedValue(), *func, args, argc); return result; } @@ -1111,10 +1111,10 @@ Value __qmljs_call_property(Context *context, const Value base, String *name, Va return result; } -Value __qmljs_call_value(Context *context, const Value thisObject, const Value *func, Value *args, int argc) +Value __qmljs_call_value(Context *context, const Value thisObject, const Value func, Value *args, int argc) { Value result; - if (FunctionObject *f = func->asFunctionObject()) { + if (FunctionObject *f = func.asFunctionObject()) { Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; const Value *that = thisObject.isUndefined() ? 0 : &thisObject; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 3b931641ac..82becb1467 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -93,7 +93,7 @@ extern "C" { // context Value __qmljs_call_activation_property(Context *, String *name, Value *args, int argc); Value __qmljs_call_property(Context *context, const Value base, String *name, Value *args, int argc); -Value __qmljs_call_value(Context *context, const Value thisObject, const Value *func, Value *args, int argc); +Value __qmljs_call_value(Context *context, const Value thisObject, const Value func, Value *args, int argc); Value __qmljs_construct_activation_property(Context *, String *name, Value *args, int argc); Value __qmljs_construct_property(Context *context, const Value base, String *name, Value *args, int argc); diff --git a/qv4array.cpp b/qv4array.cpp index 7923b1e3a9..7f05d018fa 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -53,7 +53,7 @@ bool ArrayElementLessThan::operator()(const Value &v1, const Value &v2) const return true; if (!m_comparefn.isUndefined()) { Value args[] = { v1, v2 }; - Value result = __qmljs_call_value(m_context, Value::undefinedValue(), &m_comparefn, args, 2); + Value result = __qmljs_call_value(m_context, Value::undefinedValue(), m_comparefn, args, 2); return result.toNumber(m_context) <= 0; } return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 426ab0fedd..54985facf7 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1548,7 +1548,7 @@ void ArrayPrototype::method_every(Context *ctx) args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, &callback, args, 3); + Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); ok = __qmljs_to_boolean(r, ctx); } ctx->result = Value::fromBoolean(ok); @@ -1577,7 +1577,7 @@ void ArrayPrototype::method_some(Context *ctx) args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, &callback, args, 3); + Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); ok = __qmljs_to_boolean(r, ctx); } ctx->result = Value::fromBoolean(ok); @@ -1606,7 +1606,7 @@ void ArrayPrototype::method_forEach(Context *ctx) args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, &callback, args, 3); + Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); } } } else { @@ -1633,7 +1633,7 @@ void ArrayPrototype::method_map(Context *ctx) args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, &callback, args, 3); + Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); a->value.assign(k, r); } ctx->result = Value::fromObject(a); @@ -1661,7 +1661,7 @@ void ArrayPrototype::method_filter(Context *ctx) args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, &callback, args, 3); + Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); if (__qmljs_to_boolean(r, ctx)) { const uint index = a->value.size(); a->value.resize(index + 1); @@ -1697,7 +1697,7 @@ void ArrayPrototype::method_reduce(Context *ctx) args[1] = v; args[2] = Value::fromDouble(k); args[3] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, Value::undefinedValue(), &callback, args, 4); + Value r = __qmljs_call_value(ctx, Value::undefinedValue(), callback, args, 4); acc = r; } ctx->result = acc; @@ -1728,7 +1728,7 @@ void ArrayPrototype::method_reduceRight(Context *ctx) args[1] = v; args[2] = Value::fromDouble(k); args[3] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, Value::undefinedValue(), &callback, args, 4); + Value r = __qmljs_call_value(ctx, Value::undefinedValue(), callback, args, 4); acc = r; } ctx->result = acc; @@ -1798,7 +1798,7 @@ void FunctionPrototype::method_apply(Context *ctx) return; } - ctx->result = __qmljs_call_value(ctx, thisObject, &ctx->thisObject, args.data(), args.size()); + ctx->result = __qmljs_call_value(ctx, thisObject, ctx->thisObject, args.data(), args.size()); } else { ctx->throwTypeError(); } @@ -1812,7 +1812,7 @@ void FunctionPrototype::method_call(Context *ctx) QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); if (ctx->argumentCount) qCopy(ctx->arguments + 1, ctx->arguments + ctx->argumentCount, args.begin()); - ctx->result = __qmljs_call_value(ctx, thisArg, &ctx->thisObject, args.data(), args.size()); + ctx->result = __qmljs_call_value(ctx, thisArg, ctx->thisObject, args.data(), args.size()); } else { ctx->throwTypeError(); } diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index c187613b33..c2f09afe41 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -228,7 +228,7 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) int argc = prepareVariableArguments(call->args); IR::Temp* thisObject = 0; - generateFunctionCall2(result, __qmljs_call_value, ContextRegister, baseTemp, thisObject, baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall2(result, __qmljs_call_value, ContextRegister, thisObject, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } -- cgit v1.2.3 From e6cb8faa9160a5da4db8c443e10e4c3e34671336 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 17 Oct 2012 13:24:46 +0200 Subject: Change usages of math.h into cmath. Fixes MacOS build. At least on MacOS, but possibly elsewhere too, including cmath before or after math.h will undefine e.g. the isinf macro (and others). The math.h include headers will make sure that math.h cannot get included twice, so this will end with a situation where isinf is undefined, but std::isinf is there. Change-Id: Ie59aeadf2adde511ea8db5eb2fafd3272a7e9d51 Reviewed-by: Simon Hausmann --- masm/assembler/MacroAssembler.h | 2 +- masm/config.h | 4 +++- qmljs_math.h | 2 +- qmljs_runtime.cpp | 12 ++++++------ qmljs_runtime.h | 12 ++++++------ qv4codegen.cpp | 2 +- qv4ecmaobjects.cpp | 2 +- qv4ir.cpp | 2 +- 8 files changed, 20 insertions(+), 18 deletions(-) diff --git a/masm/assembler/MacroAssembler.h b/masm/assembler/MacroAssembler.h index 43c9b470f7..b17e19bb9d 100644 --- a/masm/assembler/MacroAssembler.h +++ b/masm/assembler/MacroAssembler.h @@ -534,7 +534,7 @@ public: bool shouldBlindDouble(double value) { // Don't trust NaN or +/-Infinity - if (!isfinite(value)) + if (!std::isfinite(value)) return true; // Try to force normalisation, and check that there's no change diff --git a/masm/config.h b/masm/config.h index 2f4d9f4925..1ced4e454c 100644 --- a/masm/config.h +++ b/masm/config.h @@ -45,8 +45,10 @@ #include #ifdef __cplusplus #include +#include +#else +#include #endif #include -#include #endif // MASM_CONFIG_H diff --git a/qmljs_math.h b/qmljs_math.h index 8e4ff2c03b..89d6e28ee3 100644 --- a/qmljs_math.h +++ b/qmljs_math.h @@ -42,7 +42,7 @@ #define QMLJS_MATH_H #include -#include +#include #if 1 //CPU(X86_64) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 73391159f2..fd8d61a6ad 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -109,26 +109,26 @@ Value Value::fromString(Context *ctx, const QString &s) int Value::toInt32(double number) { - if (! number || isnan(number) || isinf(number)) + if (! number || std::isnan(number) || std::isinf(number)) return +0; return (int) trunc(number); // ### } unsigned int Value::toUInt32(double number) { - if (! number || isnan(number) || isinf(number)) + if (! number || std::isnan(number) || std::isinf(number)) return +0; return (uint) trunc(number); // ### } int Value::toInteger(double number) { - if (isnan(number)) + if (std::isnan(number)) return +0; - else if (! number || isinf(number)) + else if (! number || std::isinf(number)) return number; const double v = floor(fabs(number)); - return signbit(number) ? -v : v; + return std::signbit(number) ? -v : v; } int Value::toUInt16(Context *ctx) @@ -1007,7 +1007,7 @@ Value __qmljs_compare(Context *ctx, const Value x, const Value y, bool leftFirst } else { double nx = __qmljs_to_number(px, ctx); double ny = __qmljs_to_number(py, ctx); - if (isnan(nx) || isnan(ny)) { + if (std::isnan(nx) || std::isnan(ny)) { return Value::undefinedValue(); } else { return Value::fromBoolean(nx < ny); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 82becb1467..a8ea72ca9f 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -47,7 +47,7 @@ # include #endif -#include +#include #include namespace QQmlJS { @@ -733,10 +733,10 @@ inline double __qmljs_to_integer(const Value value, Context *ctx) const double number = __qmljs_to_number(value, ctx); if (qIsNaN(number)) return +0; - else if (! number || isinf(number)) + else if (! number || std::isinf(number)) return number; const double v = floor(fabs(number)); - return signbit(number) ? -v : v; + return std::signbit(number) ? -v : v; } inline int __qmljs_to_int32(const Value value, Context *ctx) @@ -749,7 +749,7 @@ inline int __qmljs_to_int32(const Value value, Context *ctx) return static_cast(number); } - if (! number || qIsNaN(number) || isinf(number)) + if (! number || qIsNaN(number) || std::isinf(number)) return 0; double D32 = 4294967296.0; @@ -774,7 +774,7 @@ inline unsigned __qmljs_to_uint32(const Value value, Context *ctx) return (unsigned) value.int_32; double number = __qmljs_to_number(value, ctx); - if (! number || qIsNaN(number) || isinf(number)) + if (! number || qIsNaN(number) || std::isinf(number)) return +0; double sign = (number < 0) ? -1.0 : 1.0; @@ -792,7 +792,7 @@ inline unsigned __qmljs_to_uint32(const Value value, Context *ctx) inline unsigned short __qmljs_to_uint16(const Value value, Context *ctx) { double number = __qmljs_to_number(value, ctx); - if (! number || qIsNaN(number) || isinf(number)) + if (! number || qIsNaN(number) || std::isinf(number)) return +0; double sign = (number < 0) ? -1.0 : 1.0; diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 892638fe88..69529cd695 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include #include diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 54985facf7..f303f8f123 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/qv4ir.cpp b/qv4ir.cpp index 4805f62ee9..8248cbb647 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -44,7 +44,7 @@ #include #include -#include +#include #include QT_BEGIN_NAMESPACE -- cgit v1.2.3 From c3d6d31543b2070025a10c4f9457d70ebfcfb502 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 17 Oct 2012 13:34:27 +0200 Subject: Prospective mac build fix Don't try to link the generated header file Change-Id: Iebe425ddd7cec5be731b3e8f4f5ca1a73990d0db Reviewed-by: Erik Verbruggen --- masm/masm.pri | 1 + 1 file changed, 1 insertion(+) diff --git a/masm/masm.pri b/masm/masm.pri index a721933d40..119b70f53b 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -39,6 +39,7 @@ SOURCES += $$PWD/disassembler/udis86/udis86_syn-intel.c ITAB = $$PWD/disassembler/udis86/optable.xml udis86.output = udis86_itab.h udis86.input = ITAB +udis86.CONFIG += no_link udis86.commands = python $$PWD/disassembler/udis86/itab.py ${QMAKE_FILE_IN} QMAKE_EXTRA_COMPILERS += udis86 -- cgit v1.2.3 From 159a394901dda04742cb028e9b1a27f87759ac83 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 17 Oct 2012 13:43:58 +0200 Subject: Update wtf and masm from upstream (http://svn.webkit.org/repository/webkit/trunk@131582) This brings in the distinction between loadPtr and load64 in the assembler. Change-Id: Ifd8a8fe727f4fdb56b1d1d8bdd7065604904c472 Reviewed-by: Lars Knoll --- masm/assembler/AbstractMacroAssembler.h | 48 +++- masm/assembler/LinkBuffer.h | 3 + masm/assembler/MIPSAssembler.h | 160 ++++++------ masm/assembler/MacroAssembler.h | 431 +++++++++++++++++++++++++++++- masm/assembler/MacroAssemblerARM.h | 26 ++ masm/assembler/MacroAssemblerARMv7.h | 56 ++++ masm/assembler/MacroAssemblerCodeRef.h | 8 - masm/assembler/MacroAssemblerMIPS.h | 433 +++++++++++++++++++++++++++---- masm/assembler/MacroAssemblerX86.h | 28 ++ masm/assembler/MacroAssemblerX86Common.h | 8 + masm/assembler/MacroAssemblerX86_64.h | 211 ++++++++------- masm/assembler/X86Assembler.h | 14 + masm/masm.pri | 3 +- masm/wtf/Assertions.h | 2 - masm/wtf/Compiler.h | 4 +- masm/wtf/Noncopyable.h | 6 +- masm/wtf/Platform.h | 87 +++---- masm/wtf/RefCounted.h | 52 ++-- masm/wtf/TypeTraits.h | 214 ++------------- masm/wtf/Vector.h | 37 +-- 20 files changed, 1284 insertions(+), 547 deletions(-) diff --git a/masm/assembler/AbstractMacroAssembler.h b/masm/assembler/AbstractMacroAssembler.h index e6a9df9944..c75adb7e96 100644 --- a/masm/assembler/AbstractMacroAssembler.h +++ b/masm/assembler/AbstractMacroAssembler.h @@ -261,6 +261,50 @@ public: }; + // TrustedImm64: + // + // A 64bit immediate operand to an instruction - this is wrapped in a + // class requiring explicit construction in order to prevent RegisterIDs + // (which are implemented as an enum) from accidentally being passed as + // immediate values. + struct TrustedImm64 { + TrustedImm64() { } + + explicit TrustedImm64(int64_t value) + : m_value(value) + { + } + +#if CPU(X86_64) + explicit TrustedImm64(TrustedImmPtr ptr) + : m_value(ptr.asIntptr()) + { + } +#endif + + int64_t m_value; + }; + + struct Imm64 : +#if ENABLE(JIT_CONSTANT_BLINDING) + private TrustedImm64 +#else + public TrustedImm64 +#endif + { + explicit Imm64(int64_t value) + : TrustedImm64(value) + { + } +#if CPU(X86_64) + explicit Imm64(TrustedImmPtr ptr) + : TrustedImm64(ptr) + { + } +#endif + const TrustedImm64& asTrustedImm64() const { return *this; } + }; + // Section 2: MacroAssembler code buffer handles // // The following types are used to reference items in the code buffer @@ -564,7 +608,7 @@ public: m_jumps.append(jump); } - void append(JumpList& other) + void append(const JumpList& other) { m_jumps.append(other.m_jumps.begin(), other.m_jumps.size()); } @@ -579,7 +623,7 @@ public: m_jumps.clear(); } - const JumpVector& jumps() { return m_jumps; } + const JumpVector& jumps() const { return m_jumps; } private: JumpVector m_jumps; diff --git a/masm/assembler/LinkBuffer.h b/masm/assembler/LinkBuffer.h index 484d3a73fb..770144d64a 100644 --- a/masm/assembler/LinkBuffer.h +++ b/masm/assembler/LinkBuffer.h @@ -287,6 +287,9 @@ private: #define FINALIZE_CODE(linkBufferReference, dataLogArgumentsForHeading) \ FINALIZE_CODE_IF(Options::showDisassembly(), linkBufferReference, dataLogArgumentsForHeading) +#define FINALIZE_DFG_CODE(linkBufferReference, dataLogArgumentsForHeading) \ + FINALIZE_CODE_IF(Options::showDFGDisassembly(), linkBufferReference, dataLogArgumentsForHeading) + } // namespace JSC #endif // ENABLE(ASSEMBLER) diff --git a/masm/assembler/MIPSAssembler.h b/masm/assembler/MIPSAssembler.h index 65307d9508..30f172fb8b 100644 --- a/masm/assembler/MIPSAssembler.h +++ b/masm/assembler/MIPSAssembler.h @@ -227,20 +227,17 @@ public: void addiu(RegisterID rt, RegisterID rs, int imm) { - emitInst(0x24000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) - | (imm & 0xffff)); + emitInst(0x24000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); } void addu(RegisterID rd, RegisterID rs, RegisterID rt) { - emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS) - | (rt << OP_SH_RT)); + emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); } void subu(RegisterID rd, RegisterID rs, RegisterID rt) { - emitInst(0x00000023 | (rd << OP_SH_RD) | (rs << OP_SH_RS) - | (rt << OP_SH_RT)); + emitInst(0x00000023 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); } void mult(RegisterID rs, RegisterID rt) @@ -266,8 +263,7 @@ public: void mul(RegisterID rd, RegisterID rs, RegisterID rt) { #if WTF_MIPS_ISA_AT_LEAST(32) - emitInst(0x70000002 | (rd << OP_SH_RD) | (rs << OP_SH_RS) - | (rt << OP_SH_RT)); + emitInst(0x70000002 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); #else mult(rs, rt); mflo(rd); @@ -276,139 +272,139 @@ public: void andInsn(RegisterID rd, RegisterID rs, RegisterID rt) { - emitInst(0x00000024 | (rd << OP_SH_RD) | (rs << OP_SH_RS) - | (rt << OP_SH_RT)); + emitInst(0x00000024 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); } void andi(RegisterID rt, RegisterID rs, int imm) { - emitInst(0x30000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) - | (imm & 0xffff)); + emitInst(0x30000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); } void nor(RegisterID rd, RegisterID rs, RegisterID rt) { - emitInst(0x00000027 | (rd << OP_SH_RD) | (rs << OP_SH_RS) - | (rt << OP_SH_RT)); + emitInst(0x00000027 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); } void orInsn(RegisterID rd, RegisterID rs, RegisterID rt) { - emitInst(0x00000025 | (rd << OP_SH_RD) | (rs << OP_SH_RS) - | (rt << OP_SH_RT)); + emitInst(0x00000025 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); } void ori(RegisterID rt, RegisterID rs, int imm) { - emitInst(0x34000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) - | (imm & 0xffff)); + emitInst(0x34000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); } void xorInsn(RegisterID rd, RegisterID rs, RegisterID rt) { - emitInst(0x00000026 | (rd << OP_SH_RD) | (rs << OP_SH_RS) - | (rt << OP_SH_RT)); + emitInst(0x00000026 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); } void xori(RegisterID rt, RegisterID rs, int imm) { - emitInst(0x38000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) - | (imm & 0xffff)); + emitInst(0x38000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); } void slt(RegisterID rd, RegisterID rs, RegisterID rt) { - emitInst(0x0000002a | (rd << OP_SH_RD) | (rs << OP_SH_RS) - | (rt << OP_SH_RT)); + emitInst(0x0000002a | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); } void sltu(RegisterID rd, RegisterID rs, RegisterID rt) { - emitInst(0x0000002b | (rd << OP_SH_RD) | (rs << OP_SH_RS) - | (rt << OP_SH_RT)); + emitInst(0x0000002b | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); } void sltiu(RegisterID rt, RegisterID rs, int imm) { - emitInst(0x2c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) - | (imm & 0xffff)); + emitInst(0x2c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); } void sll(RegisterID rd, RegisterID rt, int shamt) { - emitInst(0x00000000 | (rd << OP_SH_RD) | (rt << OP_SH_RT) - | ((shamt & 0x1f) << OP_SH_SHAMT)); + emitInst(0x00000000 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); } void sllv(RegisterID rd, RegisterID rt, int rs) { - emitInst(0x00000004 | (rd << OP_SH_RD) | (rt << OP_SH_RT) - | (rs << OP_SH_RS)); + emitInst(0x00000004 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); } void sra(RegisterID rd, RegisterID rt, int shamt) { - emitInst(0x00000003 | (rd << OP_SH_RD) | (rt << OP_SH_RT) - | ((shamt & 0x1f) << OP_SH_SHAMT)); + emitInst(0x00000003 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); } void srav(RegisterID rd, RegisterID rt, RegisterID rs) { - emitInst(0x00000007 | (rd << OP_SH_RD) | (rt << OP_SH_RT) - | (rs << OP_SH_RS)); + emitInst(0x00000007 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); } void srl(RegisterID rd, RegisterID rt, int shamt) { - emitInst(0x00000002 | (rd << OP_SH_RD) | (rt << OP_SH_RT) - | ((shamt & 0x1f) << OP_SH_SHAMT)); + emitInst(0x00000002 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); } void srlv(RegisterID rd, RegisterID rt, RegisterID rs) { - emitInst(0x00000006 | (rd << OP_SH_RD) | (rt << OP_SH_RT) - | (rs << OP_SH_RS)); + emitInst(0x00000006 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); + } + + void lb(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x80000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); } void lbu(RegisterID rt, RegisterID rs, int offset) { - emitInst(0x90000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) - | (offset & 0xffff)); + emitInst(0x90000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); loadDelayNop(); } void lw(RegisterID rt, RegisterID rs, int offset) { - emitInst(0x8c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) - | (offset & 0xffff)); + emitInst(0x8c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); loadDelayNop(); } void lwl(RegisterID rt, RegisterID rs, int offset) { - emitInst(0x88000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) - | (offset & 0xffff)); + emitInst(0x88000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); loadDelayNop(); } void lwr(RegisterID rt, RegisterID rs, int offset) { - emitInst(0x98000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) - | (offset & 0xffff)); + emitInst(0x98000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void lh(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x84000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); loadDelayNop(); } void lhu(RegisterID rt, RegisterID rs, int offset) { - emitInst(0x94000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) - | (offset & 0xffff)); + emitInst(0x94000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); loadDelayNop(); } + void sb(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0xa0000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + } + + void sh(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0xa4000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + } + void sw(RegisterID rt, RegisterID rs, int offset) { - emitInst(0xac000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) - | (offset & 0xffff)); + emitInst(0xac000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); } void jr(RegisterID rs) @@ -469,51 +465,43 @@ public: void addd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) { - emitInst(0x46200000 | (fd << OP_SH_FD) | (fs << OP_SH_FS) - | (ft << OP_SH_FT)); + emitInst(0x46200000 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); } void subd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) { - emitInst(0x46200001 | (fd << OP_SH_FD) | (fs << OP_SH_FS) - | (ft << OP_SH_FT)); + emitInst(0x46200001 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); } void muld(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) { - emitInst(0x46200002 | (fd << OP_SH_FD) | (fs << OP_SH_FS) - | (ft << OP_SH_FT)); + emitInst(0x46200002 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); } void divd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) { - emitInst(0x46200003 | (fd << OP_SH_FD) | (fs << OP_SH_FS) - | (ft << OP_SH_FT)); + emitInst(0x46200003 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); } void lwc1(FPRegisterID ft, RegisterID rs, int offset) { - emitInst(0xc4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) - | (offset & 0xffff)); + emitInst(0xc4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); copDelayNop(); } void ldc1(FPRegisterID ft, RegisterID rs, int offset) { - emitInst(0xd4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) - | (offset & 0xffff)); + emitInst(0xd4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); } void swc1(FPRegisterID ft, RegisterID rs, int offset) { - emitInst(0xe4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) - | (offset & 0xffff)); + emitInst(0xe4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); } void sdc1(FPRegisterID ft, RegisterID rs, int offset) { - emitInst(0xf4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) - | (offset & 0xffff)); + emitInst(0xf4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); } void mtc1(RegisterID rt, FPRegisterID fs) @@ -549,11 +537,21 @@ public: emitInst(0x46800021 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); } + void cvtds(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46000021 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + void cvtwd(FPRegisterID fd, FPRegisterID fs) { emitInst(0x46200024 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); } + void cvtsd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200020 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + void ceqd(FPRegisterID fs, FPRegisterID ft) { emitInst(0x46200032 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); @@ -663,6 +661,19 @@ public: unsigned debugOffset() { return m_buffer.debugOffset(); } + // Assembly helpers for moving data between fp and registers. + void vmov(RegisterID rd1, RegisterID rd2, FPRegisterID rn) + { + mfc1(rd1, rn); + mfc1(rd2, FPRegisterID(rn + 1)); + } + + void vmov(FPRegisterID rd, RegisterID rn1, RegisterID rn2) + { + mtc1(rn1, rd); + mtc1(rn2, FPRegisterID(rd + 1)); + } + static unsigned getCallReturnOffset(AssemblerLabel call) { // The return address is after a call and a delay slot instruction @@ -672,7 +683,7 @@ public: // Linking & patching: // // 'link' and 'patch' methods are for use on unprotected code - such as the code - // within the AssemblerBuffer, and code being patched by the patch buffer. Once + // within the AssemblerBuffer, and code being patched by the patch buffer. Once // code has been finalized it is (platform support permitting) within a non- // writable region of memory; to modify the code in an execute-only execuable // pool the 'repatch' and 'relink' methods should be used. @@ -846,7 +857,7 @@ private: MIPSWord* insn = reinterpret_cast(reinterpret_cast(newBase) + pos); insn = insn + 2; // Need to make sure we have 5 valid instructions after pos - if ((unsigned int)pos >= m_buffer.codeSize() - 5 * sizeof(MIPSWord)) + if ((unsigned)pos >= m_buffer.codeSize() - 5 * sizeof(MIPSWord)) continue; if ((*insn & 0xfc000000) == 0x08000000) { // j @@ -882,11 +893,10 @@ private: static int linkWithOffset(MIPSWord* insn, void* to) { ASSERT((*insn & 0xfc000000) == 0x10000000 // beq - || (*insn & 0xfc000000) == 0x14000000 // bne - || (*insn & 0xffff0000) == 0x45010000 // bc1t - || (*insn & 0xffff0000) == 0x45000000); // bc1f - intptr_t diff = (reinterpret_cast(to) - - reinterpret_cast(insn) - 4) >> 2; + || (*insn & 0xfc000000) == 0x14000000 // bne + || (*insn & 0xffff0000) == 0x45010000 // bc1t + || (*insn & 0xffff0000) == 0x45000000); // bc1f + intptr_t diff = (reinterpret_cast(to) - reinterpret_cast(insn) - 4) >> 2; if (diff < -32768 || diff > 32767 || *(insn + 2) != 0x10000003) { /* diff --git a/masm/assembler/MacroAssembler.h b/masm/assembler/MacroAssembler.h index b17e19bb9d..134908387d 100644 --- a/masm/assembler/MacroAssembler.h +++ b/masm/assembler/MacroAssembler.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,6 +26,8 @@ #ifndef MacroAssembler_h #define MacroAssembler_h +#include + #if ENABLE(ASSEMBLER) #if CPU(ARM_THUMB2) @@ -68,10 +70,6 @@ public: using MacroAssemblerBase::pop; using MacroAssemblerBase::jump; using MacroAssemblerBase::branch32; -#if CPU(X86_64) - using MacroAssemblerBase::branchPtr; - using MacroAssemblerBase::branchTestPtr; -#endif using MacroAssemblerBase::move; #if ENABLE(JIT_CONSTANT_BLINDING) @@ -89,6 +87,8 @@ public: using MacroAssemblerBase::xor32; #endif + static const double twoToThe32; // This is super useful for some double code. + // Utilities used by the DFG JIT. #if ENABLE(DFG_JIT) using MacroAssemblerBase::invert; @@ -183,6 +183,23 @@ public: storePtr(imm, addressForPoke(index)); } +#if CPU(X86_64) + void peek64(RegisterID dest, int index = 0) + { + load64(Address(stackPointerRegister, (index * sizeof(void*))), dest); + } + + void poke(TrustedImm64 value, int index = 0) + { + store64(value, addressForPoke(index)); + } + + void poke64(RegisterID src, int index = 0) + { + store64(src, addressForPoke(index)); + } +#endif + // Backwards banches, these are currently all implemented using existing forwards branch mechanisms. void branchPtr(RelationalCondition cond, RegisterID op1, TrustedImmPtr imm, Label target) @@ -230,6 +247,11 @@ public: } #if !CPU(ARM_THUMB2) + PatchableJump patchableBranchPtr(RelationalCondition cond, Address left, TrustedImmPtr right = TrustedImmPtr(0)) + { + return PatchableJump(branchPtr(cond, left, right)); + } + PatchableJump patchableBranchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) { return PatchableJump(branchPtrWithPatch(cond, left, dataLabel, initialRightValue)); @@ -239,6 +261,16 @@ public: { return PatchableJump(jump()); } + + PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + return PatchableJump(branchTest32(cond, reg, mask)); + } + + PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) + { + return PatchableJump(branch32(cond, reg, imm)); + } #endif void jump(Label target) @@ -486,6 +518,11 @@ public: return branch32(cond, left, TrustedImm32(right)); } + Jump branchSubPtr(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchSub32(cond, src, dest); + } + Jump branchTestPtr(ResultCondition cond, RegisterID reg, RegisterID mask) { return branchTest32(cond, reg, mask); @@ -521,15 +558,295 @@ public: return MacroAssemblerBase::branchTest8(cond, Address(address.base, address.offset), mask); } #else + void addPtr(RegisterID src, RegisterID dest) + { + add64(src, dest); + } + + void addPtr(Address src, RegisterID dest) + { + add64(src, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID srcDest) + { + add64(imm, srcDest); + } + + void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + add64(imm, src, dest); + } + + void addPtr(TrustedImm32 imm, Address address) + { + add64(imm, address); + } + + void addPtr(AbsoluteAddress src, RegisterID dest) + { + add64(src, dest); + } + + void addPtr(TrustedImmPtr imm, RegisterID dest) + { + add64(TrustedImm64(imm), dest); + } + + void addPtr(TrustedImm32 imm, AbsoluteAddress address) + { + add64(imm, address); + } + + void andPtr(RegisterID src, RegisterID dest) + { + and64(src, dest); + } + + void andPtr(TrustedImm32 imm, RegisterID srcDest) + { + and64(imm, srcDest); + } + + void negPtr(RegisterID dest) + { + neg64(dest); + } + + void orPtr(RegisterID src, RegisterID dest) + { + or64(src, dest); + } + + void orPtr(TrustedImm32 imm, RegisterID dest) + { + or64(imm, dest); + } + + void orPtr(TrustedImmPtr imm, RegisterID dest) + { + or64(TrustedImm64(imm), dest); + } + + void orPtr(RegisterID op1, RegisterID op2, RegisterID dest) + { + or64(op1, op2, dest); + } + + void orPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + or64(imm, src, dest); + } + + void rotateRightPtr(TrustedImm32 imm, RegisterID srcDst) + { + rotateRight64(imm, srcDst); + } + + void subPtr(RegisterID src, RegisterID dest) + { + sub64(src, dest); + } + + void subPtr(TrustedImm32 imm, RegisterID dest) + { + sub64(imm, dest); + } + + void subPtr(TrustedImmPtr imm, RegisterID dest) + { + sub64(TrustedImm64(imm), dest); + } + + void xorPtr(RegisterID src, RegisterID dest) + { + xor64(src, dest); + } + + void xorPtr(RegisterID src, Address dest) + { + xor64(src, dest); + } + + void xorPtr(TrustedImm32 imm, RegisterID srcDest) + { + xor64(imm, srcDest); + } + + void loadPtr(ImplicitAddress address, RegisterID dest) + { + load64(address, dest); + } + + void loadPtr(BaseIndex address, RegisterID dest) + { + load64(address, dest); + } + + void loadPtr(const void* address, RegisterID dest) + { + load64(address, dest); + } + + DataLabel32 loadPtrWithAddressOffsetPatch(Address address, RegisterID dest) + { + return load64WithAddressOffsetPatch(address, dest); + } + + DataLabelCompact loadPtrWithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + return load64WithCompactAddressOffsetPatch(address, dest); + } + + void storePtr(RegisterID src, ImplicitAddress address) + { + store64(src, address); + } + + void storePtr(RegisterID src, BaseIndex address) + { + store64(src, address); + } + + void storePtr(RegisterID src, void* address) + { + store64(src, address); + } + + void storePtr(TrustedImmPtr imm, ImplicitAddress address) + { + store64(TrustedImm64(imm), address); + } + + void storePtr(TrustedImmPtr imm, BaseIndex address) + { + store64(TrustedImm64(imm), address); + } + + DataLabel32 storePtrWithAddressOffsetPatch(RegisterID src, Address address) + { + return store64WithAddressOffsetPatch(src, address); + } + + void movePtrToDouble(RegisterID src, FPRegisterID dest) + { + move64ToDouble(src, dest); + } + + void moveDoubleToPtr(FPRegisterID src, RegisterID dest) + { + moveDoubleTo64(src, dest); + } + + void comparePtr(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + compare64(cond, left, right, dest); + } + + void comparePtr(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + compare64(cond, left, right, dest); + } + + void testPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) + { + test64(cond, reg, mask, dest); + } + + void testPtr(ResultCondition cond, RegisterID reg, RegisterID mask, RegisterID dest) + { + test64(cond, reg, mask, dest); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, RegisterID right) + { + return branch64(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, TrustedImmPtr right) + { + return branch64(cond, left, TrustedImm64(right)); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, Address right) + { + return branch64(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, Address left, RegisterID right) + { + return branch64(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + return branch64(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, Address left, TrustedImmPtr right) + { + return branch64(cond, left, TrustedImm64(right)); + } + + Jump branchTestPtr(ResultCondition cond, RegisterID reg, RegisterID mask) + { + return branchTest64(cond, reg, mask); + } + Jump branchTestPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest64(cond, reg, mask); + } + + Jump branchTestPtr(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest64(cond, address, mask); + } + + Jump branchTestPtr(ResultCondition cond, Address address, RegisterID reg) + { + return branchTest64(cond, address, reg); + } + + Jump branchTestPtr(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest64(cond, address, mask); + } + + Jump branchTestPtr(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest64(cond, address, mask); + } + + Jump branchAddPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchAdd64(cond, imm, dest); + } + + Jump branchAddPtr(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchAdd64(cond, src, dest); + } + + Jump branchSubPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchSub64(cond, imm, dest); + } + + Jump branchSubPtr(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchSub64(cond, src, dest); + } + + Jump branchSubPtr(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) + { + return branchSub64(cond, src1, src2, dest); + } + #if ENABLE(JIT_CONSTANT_BLINDING) - using MacroAssemblerBase::addPtr; - using MacroAssemblerBase::andPtr; - using MacroAssemblerBase::branchSubPtr; + using MacroAssemblerBase::and64; using MacroAssemblerBase::convertInt32ToDouble; - using MacroAssemblerBase::storePtr; - using MacroAssemblerBase::subPtr; - using MacroAssemblerBase::xorPtr; + using MacroAssemblerBase::store64; bool shouldBlindDouble(double value) { @@ -539,7 +856,7 @@ public: // Try to force normalisation, and check that there's no change // in the bit pattern - if (bitwise_cast(value * 1.0) != bitwise_cast(value)) + if (bitwise_cast(value * 1.0) != bitwise_cast(value)) return true; value = abs(value); @@ -578,7 +895,6 @@ public: default: { if (value <= 0xff) return false; -#if CPU(X86_64) && 0 JSValue jsValue = JSValue::decode(reinterpret_cast(value)); if (jsValue.isInt32()) return shouldBlind(Imm32(jsValue.asInt32())); @@ -587,7 +903,6 @@ public: if (!shouldBlindDouble(bitwise_cast(value))) return false; -#endif } } return shouldBlindForSpecificArch(value); @@ -617,6 +932,59 @@ public: rotateRightPtr(constant.rotation, dest); } + bool shouldBlind(Imm64 imm) + { +#if !defined(NDEBUG) + UNUSED_PARAM(imm); + // Debug always blind all constants, if only so we know + // if we've broken blinding during patch development. + return true; +#endif + + // First off we'll special case common, "safe" values to avoid hurting + // performance too much + uint64_t value = imm.asTrustedImm64().m_value; + switch (value) { + case 0xffff: + case 0xffffff: + case 0xffffffffL: + case 0xffffffffffL: + case 0xffffffffffffL: + case 0xffffffffffffffL: + case 0xffffffffffffffffL: + return false; + default: { + if (value <= 0xff) + return false; + } + } + return shouldBlindForSpecificArch(value); + } + + struct RotatedImm64 { + RotatedImm64(uint64_t v1, uint8_t v2) + : value(v1) + , rotation(v2) + { + } + TrustedImm64 value; + TrustedImm32 rotation; + }; + + RotatedImm64 rotationBlindConstant(Imm64 imm) + { + uint8_t rotation = random() % (sizeof(int64_t) * 8); + uint64_t value = imm.asTrustedImm64().m_value; + value = (value << rotation) | (value >> (sizeof(int64_t) * 8 - rotation)); + return RotatedImm64(value, rotation); + } + + void loadRotationBlindedConstant(RotatedImm64 constant, RegisterID dest) + { + move(constant.value, dest); + rotateRight64(constant.rotation, dest); + } + void convertInt32ToDouble(Imm32 imm, FPRegisterID dest) { if (shouldBlind(imm)) { @@ -635,6 +1003,24 @@ public: move(imm.asTrustedImmPtr(), dest); } + void move(Imm64 imm, RegisterID dest) + { + if (shouldBlind(imm)) + loadRotationBlindedConstant(rotationBlindConstant(imm), dest); + else + move(imm.asTrustedImm64(), dest); + } + + void and64(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = andBlindedConstant(imm); + and64(key.value1, dest); + and64(key.value2, dest); + } else + and64(imm.asTrustedImm32(), dest); + } + Jump branchPtr(RelationalCondition cond, RegisterID left, ImmPtr right) { if (shouldBlind(right)) { @@ -655,6 +1041,16 @@ public: storePtr(imm.asTrustedImmPtr(), dest); } + void store64(Imm64 imm, Address dest) + { + if (shouldBlind(imm)) { + RegisterID scratchRegister = scratchRegisterForBlinding(); + loadRotationBlindedConstant(rotationBlindConstant(imm), scratchRegister); + store64(scratchRegister, dest); + } else + store64(imm.asTrustedImm64(), dest); + } + #endif #endif // !CPU(X86_64) @@ -848,6 +1244,13 @@ public: storePtr(value, addressForPoke(index)); } +#if CPU(X86_64) + void poke(Imm64 value, int index = 0) + { + store64(value, addressForPoke(index)); + } +#endif + void store32(Imm32 imm, Address dest) { if (shouldBlind(imm)) { diff --git a/masm/assembler/MacroAssemblerARM.h b/masm/assembler/MacroAssemblerARM.h index e3b0be9daa..39d94adeaf 100644 --- a/masm/assembler/MacroAssemblerARM.h +++ b/masm/assembler/MacroAssemblerARM.h @@ -212,6 +212,14 @@ public: m_assembler.orrs(dest, dest, src); } + void or32(RegisterID src, AbsoluteAddress dest) + { + move(TrustedImmPtr(dest.m_ptr), ARMRegisters::S0); + load32(Address(ARMRegisters::S0), ARMRegisters::S1); + or32(src, ARMRegisters::S1); + store32(ARMRegisters::S1, ARMRegisters::S0); + } + void or32(TrustedImm32 imm, RegisterID dest) { m_assembler.orrs(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); @@ -437,6 +445,13 @@ public: m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint8, src, address.base, address.index, static_cast(address.scale), address.offset); } + void store8(TrustedImm32 imm, void* address) + { + move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); + m_assembler.moveImm(imm.m_value, ARMRegisters::S1); + m_assembler.dtrUp(ARMAssembler::StoreUint8, ARMRegisters::S1, ARMRegisters::S0, 0); + } + void store16(RegisterID src, BaseIndex address) { m_assembler.baseIndexTransfer16(ARMAssembler::StoreUint16, src, address.base, address.index, static_cast(address.scale), address.offset); @@ -657,6 +672,17 @@ public: load32(Address(ARMRegisters::S0, 0), ARMRegisters::pc); } + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + m_assembler.vmov(dest1, dest2, src); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + UNUSED_PARAM(scratch); + m_assembler.vmov(dest, src1, src2); + } + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) { ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); diff --git a/masm/assembler/MacroAssemblerARMv7.h b/masm/assembler/MacroAssemblerARMv7.h index d2da886c27..1301038e57 100644 --- a/masm/assembler/MacroAssemblerARMv7.h +++ b/masm/assembler/MacroAssemblerARMv7.h @@ -313,6 +313,14 @@ public: { m_assembler.orr(dest, dest, src); } + + void or32(RegisterID src, AbsoluteAddress dest) + { + move(TrustedImmPtr(dest.m_ptr), addressTempRegister); + load32(addressTempRegister, dataTempRegister); + or32(src, dataTempRegister); + store32(dataTempRegister, addressTempRegister); + } void or32(TrustedImm32 imm, RegisterID dest) { @@ -729,11 +737,35 @@ public: store8(src, setupArmAddress(address)); } + void store8(RegisterID src, void* address) + { + move(TrustedImmPtr(address), addressTempRegister); + store8(src, ArmAddress(addressTempRegister, 0)); + } + + void store8(TrustedImm32 imm, void* address) + { + move(imm, dataTempRegister); + store8(dataTempRegister, address); + } + void store16(RegisterID src, BaseIndex address) { store16(src, setupArmAddress(address)); } + // Possibly clobbers src, but not on this architecture. + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + m_assembler.vmov(dest1, dest2, src); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + UNUSED_PARAM(scratch); + m_assembler.vmov(dest, src1, src2); + } + #if ENABLE(JIT_CONSTANT_BLINDING) static bool shouldBlindForSpecificArch(uint32_t value) { @@ -1652,6 +1684,30 @@ public: dataLabel = moveWithPatch(initialRightValue, dataTempRegister); return branch32(cond, addressTempRegister, dataTempRegister); } + + PatchableJump patchableBranchPtr(RelationalCondition cond, Address left, TrustedImmPtr right = TrustedImmPtr(0)) + { + m_makeJumpPatchable = true; + Jump result = branch32(cond, left, TrustedImm32(right)); + m_makeJumpPatchable = false; + return PatchableJump(result); + } + + PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + m_makeJumpPatchable = true; + Jump result = branchTest32(cond, reg, mask); + m_makeJumpPatchable = false; + return PatchableJump(result); + } + + PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) + { + m_makeJumpPatchable = true; + Jump result = branch32(cond, reg, imm); + m_makeJumpPatchable = false; + return PatchableJump(result); + } PatchableJump patchableBranchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) { diff --git a/masm/assembler/MacroAssemblerCodeRef.h b/masm/assembler/MacroAssemblerCodeRef.h index 2978f10f85..c2af24060a 100644 --- a/masm/assembler/MacroAssemblerCodeRef.h +++ b/masm/assembler/MacroAssemblerCodeRef.h @@ -134,14 +134,6 @@ public: ASSERT_VALID_CODE_POINTER(m_value); } - template - FunctionPtr(returnType(*value)(argType1, argType2, argType3, argType4, argType5, argType6)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - // MSVC doesn't seem to treat functions with different calling conventions as // different types; these methods already defined for fastcall, below. #if CALLING_CONVENTION_IS_STDCALL && !OS(WINDOWS) diff --git a/masm/assembler/MacroAssemblerMIPS.h b/masm/assembler/MacroAssemblerMIPS.h index 8b3ce9f034..fc6f9f40d5 100644 --- a/masm/assembler/MacroAssemblerMIPS.h +++ b/masm/assembler/MacroAssemblerMIPS.h @@ -29,8 +29,8 @@ #if ENABLE(ASSEMBLER) && CPU(MIPS) -#include "MIPSAssembler.h" #include "AbstractMacroAssembler.h" +#include "MIPSAssembler.h" namespace JSC { @@ -155,12 +155,10 @@ public: m_assembler.lw(dataTempRegister, address.base, address.offset); if (imm.m_value >= -32768 && imm.m_value <= 32767 && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, - imm.m_value); + m_assembler.addiu(dataTempRegister, dataTempRegister, imm.m_value); else { move(imm, immTempRegister); - m_assembler.addu(dataTempRegister, dataTempRegister, - immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); } m_assembler.sw(dataTempRegister, address.base, address.offset); } else { @@ -177,12 +175,10 @@ public: m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); if (imm.m_value >= -32768 && imm.m_value <= 32767 && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, - imm.m_value); + m_assembler.addiu(dataTempRegister, dataTempRegister, imm.m_value); else { move(imm, immTempRegister); - m_assembler.addu(dataTempRegister, dataTempRegister, - immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); } m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); } @@ -257,8 +253,7 @@ public: { if (!imm.m_value && !m_fixedWidth) move(MIPSRegisters::zero, dest); - else if (imm.m_value > 0 && imm.m_value < 65535 - && !m_fixedWidth) + else if (imm.m_value > 0 && imm.m_value < 65535 && !m_fixedWidth) m_assembler.andi(dest, dest, imm.m_value); else { /* @@ -335,6 +330,13 @@ public: m_assembler.orInsn(dest, dest, dataTempRegister); } + void or32(RegisterID src, AbsoluteAddress dest) + { + load32(dest.m_ptr, dataTempRegister); + m_assembler.orInsn(dataTempRegister, dataTempRegister, src); + store32(dataTempRegister, dest.m_ptr); + } + void rshift32(RegisterID shiftAmount, RegisterID dest) { m_assembler.srav(dest, dest, shiftAmount); @@ -412,14 +414,11 @@ public: sw dataTemp, offset(base) */ m_assembler.lw(dataTempRegister, address.base, address.offset); - if (imm.m_value >= -32767 && imm.m_value <= 32768 - && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, - -imm.m_value); + if (imm.m_value >= -32767 && imm.m_value <= 32768 && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); else { move(imm, immTempRegister); - m_assembler.subu(dataTempRegister, dataTempRegister, - immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); } m_assembler.sw(dataTempRegister, address.base, address.offset); } else { @@ -437,12 +436,10 @@ public: if (imm.m_value >= -32767 && imm.m_value <= 32768 && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, - -imm.m_value); + m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); else { move(imm, immTempRegister); - m_assembler.subu(dataTempRegister, dataTempRegister, - immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); } m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); } @@ -466,11 +463,9 @@ public: move(TrustedImmPtr(address.m_ptr), addrTempRegister); m_assembler.lw(dataTempRegister, addrTempRegister, 0); - if (imm.m_value >= -32767 && imm.m_value <= 32768 - && !m_fixedWidth) { - m_assembler.addiu(dataTempRegister, dataTempRegister, - -imm.m_value); - } else { + if (imm.m_value >= -32767 && imm.m_value <= 32768 && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); + else { move(imm, immTempRegister); m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); } @@ -524,7 +519,7 @@ public: // Memory access operations: // // Loads are of the form load(address, destination) and stores of the form - // store(source, address). The source for a store may be an TrustedImm32. Address + // store(source, address). The source for a store may be an TrustedImm32. Address // operand objects to loads and store will be implicitly constructed if a // register is passed. @@ -569,12 +564,39 @@ public: m_assembler.sll(addrTempRegister, address.index, address.scale); m_assembler.addu(addrTempRegister, addrTempRegister, address.base); m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, - immTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); m_assembler.lbu(dest, addrTempRegister, address.offset); } } + void load8Signed(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lb dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lb(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lb dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lb(dest, addrTempRegister, address.offset); + } + } + void load32(ImplicitAddress address, RegisterID dest) { if (address.offset >= -32768 && address.offset <= 32767 @@ -615,8 +637,7 @@ public: m_assembler.sll(addrTempRegister, address.index, address.scale); m_assembler.addu(addrTempRegister, addrTempRegister, address.base); m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, - immTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); m_assembler.lw(dest, addrTempRegister, address.offset); } } @@ -668,8 +689,7 @@ public: m_assembler.addu(addrTempRegister, addrTempRegister, address.base); m_assembler.lui(immTempRegister, address.offset >> 16); m_assembler.ori(immTempRegister, immTempRegister, address.offset); - m_assembler.addu(addrTempRegister, addrTempRegister, - immTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); #if CPU(BIG_ENDIAN) m_assembler.lwl(dest, addrTempRegister, 0); m_assembler.lwr(dest, addrTempRegister, 3); @@ -756,12 +776,39 @@ public: m_assembler.sll(addrTempRegister, address.index, address.scale); m_assembler.addu(addrTempRegister, addrTempRegister, address.base); m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, - immTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); m_assembler.lhu(dest, addrTempRegister, address.offset); } } + void load16Signed(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lh dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lh(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lh dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lh(dest, addrTempRegister, address.offset); + } + } + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) { m_fixedWidth = true; @@ -779,6 +826,79 @@ public: return dataLabel; } + void store8(RegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sb src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sb(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sb src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.sb(src, addrTempRegister, address.offset); + } + } + + void store8(TrustedImm32 imm, void* address) + { + /* + li immTemp, imm + li addrTemp, address + sb src, 0(addrTemp) + */ + if (!imm.m_value && !m_fixedWidth) { + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sb(MIPSRegisters::zero, addrTempRegister, 0); + } else { + move(imm, immTempRegister); + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sb(immTempRegister, addrTempRegister, 0); + } + } + + void store16(RegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sh src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sh(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sh src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.sh(src, addrTempRegister, address.offset); + } + } + void store32(RegisterID src, ImplicitAddress address) { if (address.offset >= -32768 && address.offset <= 32767 @@ -819,8 +939,7 @@ public: m_assembler.sll(addrTempRegister, address.index, address.scale); m_assembler.addu(addrTempRegister, addrTempRegister, address.base); m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, - immTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); m_assembler.sw(src, addrTempRegister, address.offset); } } @@ -830,8 +949,7 @@ public: if (address.offset >= -32768 && address.offset <= 32767 && !m_fixedWidth) { if (!imm.m_value) - m_assembler.sw(MIPSRegisters::zero, address.base, - address.offset); + m_assembler.sw(MIPSRegisters::zero, address.base, address.offset); else { move(imm, immTempRegister); m_assembler.sw(immTempRegister, address.base, address.offset); @@ -845,12 +963,10 @@ public: m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); m_assembler.addu(addrTempRegister, addrTempRegister, address.base); if (!imm.m_value && !m_fixedWidth) - m_assembler.sw(MIPSRegisters::zero, addrTempRegister, - address.offset); + m_assembler.sw(MIPSRegisters::zero, addrTempRegister, address.offset); else { move(imm, immTempRegister); - m_assembler.sw(immTempRegister, addrTempRegister, - address.offset); + m_assembler.sw(immTempRegister, addrTempRegister, address.offset); } } } @@ -915,9 +1031,9 @@ public: // Stack manipulation operations: // // The ABI is assumed to provide a stack abstraction to memory, - // containing machine word sized units of data. Push and pop + // containing machine word sized units of data. Push and pop // operations add and remove a single register sized unit of data - // to or from the stack. Peek and poke operations read or write + // to or from the stack. Peek and poke operations read or write // values on the stack, without moving the current stack position. void pop(RegisterID dest) @@ -1201,10 +1317,21 @@ public: m_fixedWidth = false; } + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + m_assembler.vmov(dest1, dest2, src); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + UNUSED_PARAM(scratch); + m_assembler.vmov(dest, src1, src2); + } + // Arithmetic control flow operations: // // This set of conditional branch operations branch based - // on the result of an arithmetic operation. The operation + // on the result of an arithmetic operation. The operation // is performed as normal, storing the result. // // * jz operations branch if the result is zero. @@ -1413,7 +1540,7 @@ public: Call nearCall() { - /* We need two words for relaxation. */ + /* We need two words for relaxation. */ m_assembler.nop(); m_assembler.nop(); m_assembler.jal(); @@ -1501,8 +1628,7 @@ public: m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); } else { move(mask, immTempRegister); - m_assembler.andInsn(cmpTempRegister, dataTempRegister, - immTempRegister); + m_assembler.andInsn(cmpTempRegister, dataTempRegister, immTempRegister); if (cond == Zero) m_assembler.sltiu(dest, cmpTempRegister, 1); else @@ -1521,8 +1647,7 @@ public: m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); } else { move(mask, immTempRegister); - m_assembler.andInsn(cmpTempRegister, dataTempRegister, - immTempRegister); + m_assembler.andInsn(cmpTempRegister, dataTempRegister, immTempRegister); if (cond == Zero) m_assembler.sltiu(dest, cmpTempRegister, 1); else @@ -1598,6 +1723,34 @@ public: return tailRecursiveCall(); } + void loadFloat(BaseIndex address, FPRegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lwc1 dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lwc1 dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + } + } + void loadDouble(ImplicitAddress address, FPRegisterID dest) { #if WTF_MIPS_ISA(1) @@ -1628,6 +1781,65 @@ public: #endif } + void loadDouble(BaseIndex address, FPRegisterID dest) + { +#if WTF_MIPS_ISA(1) + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lwc1 dest, address.offset(addrTemp) + lwc1 dest+1, (address.offset+4)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, address.offset + 4); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lwc1 dest, (address.offset & 0xffff)(at) + lwc1 dest+4, (address.offset & 0xffff + 4)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, address.offset + 4); + } +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + ldc1 dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.ldc1(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + ldc1 dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.ldc1(dest, addrTempRegister, address.offset); + } +#endif + } + void loadDouble(const void* address, FPRegisterID dest) { #if WTF_MIPS_ISA(1) @@ -1649,6 +1861,33 @@ public: #endif } + void storeFloat(FPRegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + swc1 src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.swc1(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + swc1 src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.swc1(src, addrTempRegister, address.offset); + } + } void storeDouble(FPRegisterID src, ImplicitAddress address) { @@ -1680,17 +1919,99 @@ public: #endif } + void storeDouble(FPRegisterID src, BaseIndex address) + { +#if WTF_MIPS_ISA(1) + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + swc1 src, address.offset(addrTemp) + swc1 src+1, (address.offset + 4)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.swc1(src, addrTempRegister, address.offset); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, address.offset + 4); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + swc1 src, (address.offset & 0xffff)(at) + swc1 src+1, (address.offset & 0xffff + 4)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.swc1(src, addrTempRegister, address.offset); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, address.offset + 4); + } +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sdc1 src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sdc1(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sdc1 src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.sdc1(src, addrTempRegister, address.offset); + } +#endif + } + + void storeDouble(FPRegisterID src, const void* address) + { +#if WTF_MIPS_ISA(1) + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.swc1(src, addrTempRegister, 0); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, 4); +#else + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sdc1(src, addrTempRegister, 0); +#endif + } + void addDouble(FPRegisterID src, FPRegisterID dest) { m_assembler.addd(dest, dest, src); } + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.addd(dest, op1, op2); + } + void addDouble(Address src, FPRegisterID dest) { loadDouble(src, fpTempRegister); m_assembler.addd(dest, dest, fpTempRegister); } + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + loadDouble(address.m_ptr, fpTempRegister); + m_assembler.addd(dest, dest, fpTempRegister); + } + void subDouble(FPRegisterID src, FPRegisterID dest) { m_assembler.subd(dest, dest, src); @@ -1738,6 +2059,16 @@ public: m_assembler.cvtdw(dest, fpTempRegister); } + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.cvtds(dst, src); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.cvtsd(dst, src); + } + void insertRelaxationWords() { /* We need four words for relaxation. */ diff --git a/masm/assembler/MacroAssemblerX86.h b/masm/assembler/MacroAssemblerX86.h index d1a4ff3c4f..8fd31466d4 100644 --- a/masm/assembler/MacroAssemblerX86.h +++ b/masm/assembler/MacroAssemblerX86.h @@ -44,6 +44,7 @@ public: using MacroAssemblerX86Common::or32; using MacroAssemblerX86Common::load32; using MacroAssemblerX86Common::store32; + using MacroAssemblerX86Common::store8; using MacroAssemblerX86Common::branch32; using MacroAssemblerX86Common::call; using MacroAssemblerX86Common::jump; @@ -84,6 +85,11 @@ public: m_assembler.orl_im(imm.m_value, address.m_ptr); } + void or32(RegisterID reg, AbsoluteAddress address) + { + m_assembler.orl_rm(reg, address.m_ptr); + } + void sub32(TrustedImm32 imm, AbsoluteAddress address) { m_assembler.subl_im(imm.m_value, address.m_ptr); @@ -127,6 +133,28 @@ public: m_assembler.movl_rm(src, address); } + void store8(TrustedImm32 imm, void* address) + { + ASSERT(-128 <= imm.m_value && imm.m_value < 128); + m_assembler.movb_i8m(imm.m_value, address); + } + + // Possibly clobbers src. + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + movePackedToInt32(src, dest1); + rshiftPacked(TrustedImm32(32), src); + movePackedToInt32(src, dest2); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + moveInt32ToPacked(src1, dest); + moveInt32ToPacked(src2, scratch); + lshiftPacked(TrustedImm32(32), scratch); + orPacked(scratch, dest); + } + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) { m_assembler.addl_im(imm.m_value, dest.m_ptr); diff --git a/masm/assembler/MacroAssemblerX86Common.h b/masm/assembler/MacroAssemblerX86Common.h index 905c094267..66db26acb0 100644 --- a/masm/assembler/MacroAssemblerX86Common.h +++ b/masm/assembler/MacroAssemblerX86Common.h @@ -97,8 +97,11 @@ public: #if ENABLE(JIT_CONSTANT_BLINDING) static bool shouldBlindForSpecificArch(uint32_t value) { return value >= 0x00ffffff; } #if CPU(X86_64) + static bool shouldBlindForSpecificArch(uint64_t value) { return value >= 0x00ffffff; } +#if OS(DARWIN) // On 64-bit systems other than DARWIN uint64_t and uintptr_t are the same type so overload is prohibited. static bool shouldBlindForSpecificArch(uintptr_t value) { return value >= 0x00ffffff; } #endif +#endif #endif // Integer arithmetic operations: @@ -993,6 +996,11 @@ public: m_assembler.movq_i64r(imm.asIntptr(), dest); } + void move(TrustedImm64 imm, RegisterID dest) + { + m_assembler.movq_i64r(imm.m_value, dest); + } + void swap(RegisterID reg1, RegisterID reg2) { if (reg1 != reg2) diff --git a/masm/assembler/MacroAssemblerX86_64.h b/masm/assembler/MacroAssemblerX86_64.h index ac90516f41..ceacf6aa82 100644 --- a/masm/assembler/MacroAssemblerX86_64.h +++ b/masm/assembler/MacroAssemblerX86_64.h @@ -45,6 +45,7 @@ public: using MacroAssemblerX86Common::sub32; using MacroAssemblerX86Common::load32; using MacroAssemblerX86Common::store32; + using MacroAssemblerX86Common::store8; using MacroAssemblerX86Common::call; using MacroAssemblerX86Common::jump; using MacroAssemblerX86Common::addDouble; @@ -75,6 +76,12 @@ public: or32(imm, Address(scratchRegister)); } + void or32(RegisterID reg, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + or32(reg, Address(scratchRegister)); + } + void sub32(TrustedImm32 imm, AbsoluteAddress address) { move(TrustedImmPtr(address.m_ptr), scratchRegister); @@ -108,6 +115,12 @@ public: move(TrustedImmPtr(address), scratchRegister); store32(imm, scratchRegister); } + + void store8(TrustedImm32 imm, void* address) + { + move(TrustedImmPtr(address), scratchRegister); + store8(imm, Address(scratchRegister)); + } Call call() { @@ -141,231 +154,225 @@ public: return Call::fromTailJump(newJump); } + Jump branchAdd32(ResultCondition cond, TrustedImm32 src, AbsoluteAddress dest) + { + move(TrustedImmPtr(dest.m_ptr), scratchRegister); + add32(src, Address(scratchRegister)); + return Jump(m_assembler.jCC(x86Condition(cond))); + } - void addPtr(RegisterID src, RegisterID dest) + void add64(RegisterID src, RegisterID dest) { m_assembler.addq_rr(src, dest); } - void addPtr(Address src, RegisterID dest) + void add64(Address src, RegisterID dest) { m_assembler.addq_mr(src.offset, src.base, dest); } - void addPtr(AbsoluteAddress src, RegisterID dest) + void add64(AbsoluteAddress src, RegisterID dest) { move(TrustedImmPtr(src.m_ptr), scratchRegister); - addPtr(Address(scratchRegister), dest); + add64(Address(scratchRegister), dest); } - void addPtr(TrustedImm32 imm, RegisterID srcDest) + void add64(TrustedImm32 imm, RegisterID srcDest) { m_assembler.addq_ir(imm.m_value, srcDest); } - void addPtr(TrustedImmPtr imm, RegisterID dest) + void add64(TrustedImm64 imm, RegisterID dest) { move(imm, scratchRegister); - m_assembler.addq_rr(scratchRegister, dest); + add64(scratchRegister, dest); } - void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + void add64(TrustedImm32 imm, RegisterID src, RegisterID dest) { m_assembler.leaq_mr(imm.m_value, src, dest); } - void addPtr(TrustedImm32 imm, Address address) + void add64(TrustedImm32 imm, Address address) { m_assembler.addq_im(imm.m_value, address.offset, address.base); } - void addPtr(TrustedImm32 imm, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - addPtr(imm, Address(scratchRegister)); - } - void add64(TrustedImm32 imm, AbsoluteAddress address) { - addPtr(imm, address); + move(TrustedImmPtr(address.m_ptr), scratchRegister); + add64(imm, Address(scratchRegister)); } - void andPtr(RegisterID src, RegisterID dest) + void and64(RegisterID src, RegisterID dest) { m_assembler.andq_rr(src, dest); } - void andPtr(TrustedImm32 imm, RegisterID srcDest) + void and64(TrustedImm32 imm, RegisterID srcDest) { m_assembler.andq_ir(imm.m_value, srcDest); } - void negPtr(RegisterID dest) + void neg64(RegisterID dest) { m_assembler.negq_r(dest); } - void orPtr(RegisterID src, RegisterID dest) + void or64(RegisterID src, RegisterID dest) { m_assembler.orq_rr(src, dest); } - void orPtr(TrustedImmPtr imm, RegisterID dest) + void or64(TrustedImm64 imm, RegisterID dest) { move(imm, scratchRegister); - m_assembler.orq_rr(scratchRegister, dest); + or64(scratchRegister, dest); } - void orPtr(TrustedImm32 imm, RegisterID dest) + void or64(TrustedImm32 imm, RegisterID dest) { m_assembler.orq_ir(imm.m_value, dest); } - void orPtr(RegisterID op1, RegisterID op2, RegisterID dest) + void or64(RegisterID op1, RegisterID op2, RegisterID dest) { if (op1 == op2) move(op1, dest); else if (op1 == dest) - orPtr(op2, dest); + or64(op2, dest); else { move(op2, dest); - orPtr(op1, dest); + or64(op1, dest); } } - void orPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + void or64(TrustedImm32 imm, RegisterID src, RegisterID dest) { move(src, dest); - orPtr(imm, dest); + or64(imm, dest); } - void rotateRightPtr(TrustedImm32 imm, RegisterID srcDst) + void rotateRight64(TrustedImm32 imm, RegisterID srcDst) { m_assembler.rorq_i8r(imm.m_value, srcDst); } - void subPtr(RegisterID src, RegisterID dest) + void sub64(RegisterID src, RegisterID dest) { m_assembler.subq_rr(src, dest); } - void subPtr(TrustedImm32 imm, RegisterID dest) + void sub64(TrustedImm32 imm, RegisterID dest) { m_assembler.subq_ir(imm.m_value, dest); } - void subPtr(TrustedImmPtr imm, RegisterID dest) + void sub64(TrustedImm64 imm, RegisterID dest) { move(imm, scratchRegister); - m_assembler.subq_rr(scratchRegister, dest); + sub64(scratchRegister, dest); } - void xorPtr(RegisterID src, RegisterID dest) + void xor64(RegisterID src, RegisterID dest) { m_assembler.xorq_rr(src, dest); } - void xorPtr(RegisterID src, Address dest) + void xor64(RegisterID src, Address dest) { m_assembler.xorq_rm(src, dest.offset, dest.base); } - void xorPtr(TrustedImm32 imm, RegisterID srcDest) + void xor64(TrustedImm32 imm, RegisterID srcDest) { m_assembler.xorq_ir(imm.m_value, srcDest); } - void loadPtr(ImplicitAddress address, RegisterID dest) - { - m_assembler.movq_mr(address.offset, address.base, dest); - } - - ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + void load64(ImplicitAddress address, RegisterID dest) { - ConvertibleLoadLabel result = ConvertibleLoadLabel(this); m_assembler.movq_mr(address.offset, address.base, dest); - return result; } - void loadPtr(BaseIndex address, RegisterID dest) + void load64(BaseIndex address, RegisterID dest) { m_assembler.movq_mr(address.offset, address.base, address.index, address.scale, dest); } - void loadPtr(const void* address, RegisterID dest) + void load64(const void* address, RegisterID dest) { if (dest == X86Registers::eax) m_assembler.movq_mEAX(address); else { move(TrustedImmPtr(address), dest); - loadPtr(dest, dest); + load64(dest, dest); } } - DataLabel32 loadPtrWithAddressOffsetPatch(Address address, RegisterID dest) + DataLabel32 load64WithAddressOffsetPatch(Address address, RegisterID dest) { padBeforePatch(); m_assembler.movq_mr_disp32(address.offset, address.base, dest); return DataLabel32(this); } - DataLabelCompact loadPtrWithCompactAddressOffsetPatch(Address address, RegisterID dest) + DataLabelCompact load64WithCompactAddressOffsetPatch(Address address, RegisterID dest) { padBeforePatch(); m_assembler.movq_mr_disp8(address.offset, address.base, dest); return DataLabelCompact(this); } - void storePtr(RegisterID src, ImplicitAddress address) + void store64(RegisterID src, ImplicitAddress address) { m_assembler.movq_rm(src, address.offset, address.base); } - void storePtr(RegisterID src, BaseIndex address) + void store64(RegisterID src, BaseIndex address) { m_assembler.movq_rm(src, address.offset, address.base, address.index, address.scale); } - void storePtr(RegisterID src, void* address) + void store64(RegisterID src, void* address) { if (src == X86Registers::eax) m_assembler.movq_EAXm(address); else { move(TrustedImmPtr(address), scratchRegister); - storePtr(src, scratchRegister); + store64(src, scratchRegister); } } - void storePtr(TrustedImmPtr imm, ImplicitAddress address) + void store64(TrustedImm64 imm, ImplicitAddress address) { move(imm, scratchRegister); - storePtr(scratchRegister, address); + store64(scratchRegister, address); } - void storePtr(TrustedImmPtr imm, BaseIndex address) + void store64(TrustedImm64 imm, BaseIndex address) { move(imm, scratchRegister); m_assembler.movq_rm(scratchRegister, address.offset, address.base, address.index, address.scale); } - DataLabel32 storePtrWithAddressOffsetPatch(RegisterID src, Address address) + DataLabel32 store64WithAddressOffsetPatch(RegisterID src, Address address) { padBeforePatch(); m_assembler.movq_rm_disp32(src, address.offset, address.base); return DataLabel32(this); } - void movePtrToDouble(RegisterID src, FPRegisterID dest) + void move64ToDouble(RegisterID src, FPRegisterID dest) { m_assembler.movq_rr(src, dest); } - void moveDoubleToPtr(FPRegisterID src, RegisterID dest) + void moveDoubleTo64(FPRegisterID src, RegisterID dest) { m_assembler.movq_rr(src, dest); } - void comparePtr(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + void compare64(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) { if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) m_assembler.testq_rr(left, left); @@ -375,67 +382,60 @@ public: m_assembler.movzbl_rr(dest, dest); } - void comparePtr(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + void compare64(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) { m_assembler.cmpq_rr(right, left); m_assembler.setCC_r(x86Condition(cond), dest); m_assembler.movzbl_rr(dest, dest); } - Jump branchAdd32(ResultCondition cond, TrustedImm32 src, AbsoluteAddress dest) - { - move(TrustedImmPtr(dest.m_ptr), scratchRegister); - add32(src, Address(scratchRegister)); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, RegisterID right) + Jump branch64(RelationalCondition cond, RegisterID left, RegisterID right) { m_assembler.cmpq_rr(right, left); return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branchPtr(RelationalCondition cond, RegisterID left, TrustedImmPtr right) + Jump branch64(RelationalCondition cond, RegisterID left, TrustedImm64 right) { if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) { m_assembler.testq_rr(left, left); return Jump(m_assembler.jCC(x86Condition(cond))); } move(right, scratchRegister); - return branchPtr(cond, left, scratchRegister); + return branch64(cond, left, scratchRegister); } - Jump branchPtr(RelationalCondition cond, RegisterID left, Address right) + Jump branch64(RelationalCondition cond, RegisterID left, Address right) { m_assembler.cmpq_mr(right.offset, right.base, left); return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + Jump branch64(RelationalCondition cond, AbsoluteAddress left, RegisterID right) { move(TrustedImmPtr(left.m_ptr), scratchRegister); - return branchPtr(cond, Address(scratchRegister), right); + return branch64(cond, Address(scratchRegister), right); } - Jump branchPtr(RelationalCondition cond, Address left, RegisterID right) + Jump branch64(RelationalCondition cond, Address left, RegisterID right) { m_assembler.cmpq_rm(right, left.offset, left.base); return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branchPtr(RelationalCondition cond, Address left, TrustedImmPtr right) + Jump branch64(RelationalCondition cond, Address left, TrustedImm64 right) { move(right, scratchRegister); - return branchPtr(cond, left, scratchRegister); + return branch64(cond, left, scratchRegister); } - Jump branchTestPtr(ResultCondition cond, RegisterID reg, RegisterID mask) + Jump branchTest64(ResultCondition cond, RegisterID reg, RegisterID mask) { m_assembler.testq_rr(reg, mask); return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branchTestPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + Jump branchTest64(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) { // if we are only interested in the low seven bits, this can be tested with a testb if (mask.m_value == -1) @@ -447,7 +447,7 @@ public: return Jump(m_assembler.jCC(x86Condition(cond))); } - void testPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) + void test64(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) { if (mask.m_value == -1) m_assembler.testq_rr(reg, reg); @@ -458,19 +458,19 @@ public: set32(x86Condition(cond), dest); } - void testPtr(ResultCondition cond, RegisterID reg, RegisterID mask, RegisterID dest) + void test64(ResultCondition cond, RegisterID reg, RegisterID mask, RegisterID dest) { m_assembler.testq_rr(reg, mask); set32(x86Condition(cond), dest); } - Jump branchTestPtr(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + Jump branchTest64(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) { - loadPtr(address.m_ptr, scratchRegister); - return branchTestPtr(cond, scratchRegister, mask); + load64(address.m_ptr, scratchRegister); + return branchTest64(cond, scratchRegister, mask); } - Jump branchTestPtr(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + Jump branchTest64(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) { if (mask.m_value == -1) m_assembler.cmpq_im(0, address.offset, address.base); @@ -479,13 +479,13 @@ public: return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branchTestPtr(ResultCondition cond, Address address, RegisterID reg) + Jump branchTest64(ResultCondition cond, Address address, RegisterID reg) { m_assembler.testq_rm(reg, address.offset, address.base); return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branchTestPtr(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + Jump branchTest64(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) { if (mask.m_value == -1) m_assembler.cmpq_im(0, address.offset, address.base, address.index, address.scale); @@ -495,28 +495,41 @@ public: } - Jump branchAddPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + Jump branchAdd64(ResultCondition cond, TrustedImm32 imm, RegisterID dest) { - addPtr(imm, dest); + add64(imm, dest); return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branchAddPtr(ResultCondition cond, RegisterID src, RegisterID dest) + Jump branchAdd64(ResultCondition cond, RegisterID src, RegisterID dest) { - addPtr(src, dest); + add64(src, dest); return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branchSubPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + Jump branchSub64(ResultCondition cond, TrustedImm32 imm, RegisterID dest) { - subPtr(imm, dest); + sub64(imm, dest); return Jump(m_assembler.jCC(x86Condition(cond))); } - Jump branchSubPtr(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) + Jump branchSub64(ResultCondition cond, RegisterID src, RegisterID dest) + { + sub64(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub64(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) { move(src1, dest); - return branchSubPtr(cond, src2, dest); + return branchSub64(cond, src2, dest); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result = ConvertibleLoadLabel(this); + m_assembler.movq_mr(address.offset, address.base, dest); + return result; } DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) @@ -529,22 +542,22 @@ public: Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) { dataLabel = moveWithPatch(initialRightValue, scratchRegister); - return branchPtr(cond, left, scratchRegister); + return branch64(cond, left, scratchRegister); } Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) { dataLabel = moveWithPatch(initialRightValue, scratchRegister); - return branchPtr(cond, left, scratchRegister); + return branch64(cond, left, scratchRegister); } DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) { DataLabelPtr label = moveWithPatch(initialValue, scratchRegister); - storePtr(scratchRegister, address); + store64(scratchRegister, address); return label; } - + using MacroAssemblerX86Common::branchTest8; Jump branchTest8(ResultCondition cond, ExtendedAddress address, TrustedImm32 mask = TrustedImm32(-1)) { diff --git a/masm/assembler/X86Assembler.h b/masm/assembler/X86Assembler.h index adaee4bc07..ecb178e88b 100644 --- a/masm/assembler/X86Assembler.h +++ b/masm/assembler/X86Assembler.h @@ -541,6 +541,11 @@ public: m_formatter.immediate32(imm); } } + + void orl_rm(RegisterID src, const void* addr) + { + m_formatter.oneByteOp(OP_OR_EvGv, src, addr); + } #endif void subl_rr(RegisterID src, RegisterID dst) @@ -1150,6 +1155,15 @@ public: m_formatter.immediate32(imm); } +#if !CPU(X86_64) + void movb_i8m(int imm, const void* addr) + { + ASSERT(-128 <= imm && imm < 128); + m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, addr); + m_formatter.immediate8(imm); + } +#endif + void movb_i8m(int imm, int offset, RegisterID base) { ASSERT(-128 <= imm && imm < 128); diff --git a/masm/masm.pri b/masm/masm.pri index 119b70f53b..117707fc70 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -13,7 +13,8 @@ DEFINES += WTF_EXPORT_PRIVATE="" DEFINES += ENABLE_LLINT=0 DEFINES += ENABLE_DFG_JIT=0 -DEFINES += ENABLE_JIT=0 +DEFINES += ENABLE_JIT=1 +DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 DEFINES += ENABLE_ASSEMBLER=1 DEFINES += USE_SYSTEM_MALLOC=1 diff --git a/masm/wtf/Assertions.h b/masm/wtf/Assertions.h index 7e079ab187..f347a21747 100644 --- a/masm/wtf/Assertions.h +++ b/masm/wtf/Assertions.h @@ -42,8 +42,6 @@ http://msdn2.microsoft.com/en-us/library/ms177415(VS.80).aspx */ -#include - #include #if !COMPILER(MSVC) diff --git a/masm/wtf/Compiler.h b/masm/wtf/Compiler.h index f40e15e601..a9ef419c18 100644 --- a/masm/wtf/Compiler.h +++ b/masm/wtf/Compiler.h @@ -57,10 +57,10 @@ #define WTF_COMPILER_SUPPORTS_CXX_DELETED_FUNCTIONS __has_extension(cxx_deleted_functions) #define WTF_COMPILER_SUPPORTS_CXX_NULLPTR __has_feature(cxx_nullptr) +#define WTF_COMPILER_SUPPORTS_CXX_EXPLICIT_CONVERSIONS __has_feature(cxx_explicit_conversions) #define WTF_COMPILER_SUPPORTS_BLOCKS __has_feature(blocks) #define WTF_COMPILER_SUPPORTS_C_STATIC_ASSERT __has_extension(c_static_assert) #define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL __has_extension(cxx_override_control) - #define WTF_COMPILER_SUPPORTS_HAS_TRIVIAL_DESTRUCTOR __has_extension(has_trivial_destructor) #endif @@ -122,7 +122,7 @@ /* Specific compiler features */ #if COMPILER(GCC) && !COMPILER(CLANG) -#if GCC_VERSION_AT_LEAST(4, 7, 0) && __cplusplus >= 201103L +#if GCC_VERSION_AT_LEAST(4, 7, 0) && defined(__cplusplus) && __cplusplus >= 201103L #define WTF_COMPILER_SUPPORTS_CXX_RVALUE_REFERENCES 1 #define WTF_COMPILER_SUPPORTS_CXX_DELETED_FUNCTIONS 1 #define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 diff --git a/masm/wtf/Noncopyable.h b/masm/wtf/Noncopyable.h index a88cefc34e..f6bdfbb40b 100644 --- a/masm/wtf/Noncopyable.h +++ b/masm/wtf/Noncopyable.h @@ -25,13 +25,9 @@ #if COMPILER_SUPPORTS(CXX_DELETED_FUNCTIONS) #define WTF_MAKE_NONCOPYABLE(ClassName) \ - CLANG_PRAGMA("clang diagnostic push") \ - CLANG_PRAGMA("clang diagnostic ignored \"-Wunknown-pragmas\"") \ - CLANG_PRAGMA("clang diagnostic ignored \"-Wc++0x-extensions\"") \ private: \ ClassName(const ClassName&) = delete; \ - ClassName& operator=(const ClassName&) = delete; \ - CLANG_PRAGMA("clang diagnostic pop") + ClassName& operator=(const ClassName&) = delete; #else #define WTF_MAKE_NONCOPYABLE(ClassName) \ private: \ diff --git a/masm/wtf/Platform.h b/masm/wtf/Platform.h index 0be4bb07dc..3c263c51d9 100644 --- a/masm/wtf/Platform.h +++ b/masm/wtf/Platform.h @@ -540,9 +540,6 @@ #define WTF_USE_SCROLLBAR_PAINTER 1 #define HAVE_XPC 1 #endif -#if !defined(ENABLE_JAVA_BRIDGE) -#define ENABLE_JAVA_BRIDGE 1 -#endif #if !defined(ENABLE_DASHBOARD_SUPPORT) #define ENABLE_DASHBOARD_SUPPORT 1 #endif @@ -591,7 +588,6 @@ #define ENABLE_GEOLOCATION 1 #define ENABLE_ICONDATABASE 0 #define ENABLE_INSPECTOR 1 -#define ENABLE_JAVA_BRIDGE 0 #define ENABLE_NETSCAPE_PLUGIN_API 0 #define ENABLE_ORIENTATION_EVENTS 1 #define ENABLE_REPAINT_THROTTLING 1 @@ -604,11 +600,9 @@ #define WTF_USE_PTHREADS 1 #if PLATFORM(IOS_SIMULATOR) - #define ENABLE_CLASSIC_INTERPRETER 1 #define ENABLE_JIT 0 #define ENABLE_YARR_JIT 0 #else - #define ENABLE_CLASSIC_INTERPRETER 0 #define ENABLE_JIT 1 #define ENABLE_LLINT 1 #define ENABLE_YARR_JIT 1 @@ -641,7 +635,9 @@ #if PLATFORM(WX) #if !CPU(PPC) +#if !defined(ENABLE_ASSEMBLER) #define ENABLE_ASSEMBLER 1 +#endif #define ENABLE_JIT 1 #endif #define ENABLE_GLOBAL_FASTMALLOC_NEW 0 @@ -698,7 +694,6 @@ #define HAVE_LANGINFO_H 1 #define HAVE_MMAP 1 #define HAVE_MERGESORT 1 -#define HAVE_SBRK 1 #define HAVE_STRINGS_H 1 #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_TIME_H 1 @@ -739,7 +734,6 @@ #define HAVE_MMAP 1 #define HAVE_MADV_FREE_REUSE 1 #define HAVE_MADV_FREE 1 -#define HAVE_SBRK 1 #define HAVE_STRINGS_H 1 #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_TIME_H 1 @@ -749,7 +743,6 @@ #define HAVE_ERRNO_H 1 #define HAVE_NMAP 1 -#define HAVE_SBRK 1 #define HAVE_STRINGS_H 1 #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_TIME_H 1 @@ -761,7 +754,6 @@ #define HAVE_ERRNO_H 1 #define HAVE_LANGINFO_H 1 #define HAVE_MMAP 1 -#define HAVE_SBRK 1 #define HAVE_STRINGS_H 1 #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_TIME_H 1 @@ -880,6 +872,7 @@ /* JIT is not implemented for Windows 64-bit */ #if !defined(ENABLE_JIT) && OS(WINDOWS) && CPU(X86_64) #define ENABLE_JIT 0 +#define ENABLE_YARR_JIT 0 #endif #if !defined(ENABLE_JIT) && CPU(SH4) && PLATFORM(QT) @@ -906,17 +899,25 @@ #define ENABLE_DISASSEMBLER 1 #endif +/* On the GTK+ port we take an extra precaution for LLINT support: + * We disable it on x86 builds if the build target doesn't support SSE2 + * instructions (LLINT requires SSE2 on this platform). */ +#if !defined(ENABLE_LLINT) && PLATFORM(GTK) && CPU(X86) && COMPILER(GCC) \ + && !defined(__SSE2__) +#define ENABLE_LLINT 0 +#endif + /* On some of the platforms where we have a JIT, we want to also have the low-level interpreter. */ #if !defined(ENABLE_LLINT) \ && ENABLE(JIT) \ && (OS(DARWIN) || OS(LINUX)) \ - && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(GTK)) \ + && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(GTK) || (PLATFORM(QT) && OS(LINUX))) \ && (CPU(X86) || CPU(X86_64) || CPU(ARM_THUMB2)) #define ENABLE_LLINT 1 #endif -#if !defined(ENABLE_DFG_JIT) && ENABLE(JIT) +#if !defined(ENABLE_DFG_JIT) && ENABLE(JIT) && !COMPILER(MSVC) /* Enable the DFG JIT on X86 and X86_64. Only tested on Mac and GNU/Linux. */ #if (CPU(X86) || CPU(X86_64)) && (PLATFORM(MAC) || OS(LINUX)) #define ENABLE_DFG_JIT 1 @@ -931,6 +932,22 @@ #endif #endif +/* If the jit is not available, enable the LLInt C Loop: */ +#if !ENABLE(JIT) +#undef ENABLE_LLINT /* Undef so that we can redefine it. */ +#undef ENABLE_LLINT_C_LOOP /* Undef so that we can redefine it. */ +#undef ENABLE_DFG_JIT /* Undef so that we can redefine it. */ +#define ENABLE_LLINT 1 +#define ENABLE_LLINT_C_LOOP 1 +#define ENABLE_DFG_JIT 0 +#endif + +/* Do a sanity check to make sure that we at least have one execution engine in + use: */ +#if !(ENABLE(JIT) || ENABLE(LLINT)) +#error You have to have at least one execution model enabled to build JSC +#endif + /* Profiling of types and values used by JIT code. DFG_JIT depends on it, but you can enable it manually with DFG turned off if you want to use it as a standalone profiler. In that case, you probably want to also enable VERBOSE_VALUE_PROFILE @@ -953,29 +970,6 @@ #define ENABLE_WRITE_BARRIER_PROFILING 0 #endif -/* Ensure that either the JIT or the interpreter has been enabled. */ -#if !defined(ENABLE_CLASSIC_INTERPRETER) && !ENABLE(JIT) && !ENABLE(LLINT) -#define ENABLE_CLASSIC_INTERPRETER 1 -#endif - -/* If the jit and classic interpreter is not available, enable the LLInt C Loop: */ -#if !ENABLE(JIT) && !ENABLE(CLASSIC_INTERPRETER) - #define ENABLE_LLINT 1 - #define ENABLE_LLINT_C_LOOP 1 - #define ENABLE_DFG_JIT 0 -#endif - -/* Do a sanity check to make sure that we at least have one execution engine in - use: */ -#if !(ENABLE(JIT) || ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT)) -#error You have to have at least one execution model enabled to build JSC -#endif -/* Do a sanity check to make sure that we don't have both the classic interpreter - and the llint C loop in use at the same time: */ -#if ENABLE(CLASSIC_INTERPRETER) && ENABLE(LLINT_C_LOOP) -#error You cannot build both the classic interpreter and the llint C loop together -#endif - /* Configure the JIT */ #if CPU(X86) && COMPILER(MSVC) #define JSC_HOST_CALL __fastcall @@ -989,12 +983,9 @@ #if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(4, 0, 0, 0) && defined(__GNUC__)) #define HAVE_COMPUTED_GOTO 1 #endif -#if HAVE(COMPUTED_GOTO) && ENABLE(CLASSIC_INTERPRETER) -#define ENABLE_COMPUTED_GOTO_CLASSIC_INTERPRETER 1 -#endif /* Determine if we need to enable Computed Goto Opcodes or not: */ -#if (HAVE(COMPUTED_GOTO) && ENABLE(LLINT)) || ENABLE(COMPUTED_GOTO_CLASSIC_INTERPRETER) +#if HAVE(COMPUTED_GOTO) && ENABLE(LLINT) #define ENABLE_COMPUTED_GOTO_OPCODES 1 #endif @@ -1009,9 +1000,16 @@ #define ENABLE_YARR_JIT_DEBUG 0 #endif +/* If either the JIT or the RegExp JIT is enabled, then the Assembler must be + enabled as well: */ #if ENABLE(JIT) || ENABLE(YARR_JIT) +#if defined(ENABLE_ASSEMBLER) && !ENABLE_ASSEMBLER +#error "Cannot enable the JIT or RegExp JIT without enabling the Assembler" +#else +#undef ENABLE_ASSEMBLER #define ENABLE_ASSEMBLER 1 #endif +#endif /* Pick which allocator to use; we only need an executable allocator if the assembler is compiled in. On x86-64 we use a single fixed mmap, on other platforms we mmap on demand. */ @@ -1027,6 +1025,11 @@ #define ENABLE_PAN_SCROLLING 1 #endif +/*Add other platforms as they update their platfrom specific code to handle TextRun's with 8 bit data. */ +#if PLATFORM(MAC) +#define ENABLE_8BIT_TEXTRUN 1 +#endif + /* Use the QXmlStreamReader implementation for XMLDocumentParser */ /* Use the QXmlQuery implementation for XSLTProcessor */ #if PLATFORM(QT) @@ -1111,7 +1114,7 @@ since most ports try to support sub-project independence, adding new headers to WTF causes many ports to break, and so this way we can address the build breakages one port at a time. */ -#if !defined(WTF_USE_EXPORT_MACROS) && (PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(WX) || PLATFORM(BLACKBERRY)) +#if !defined(WTF_USE_EXPORT_MACROS) && (PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(WX)) #define WTF_USE_EXPORT_MACROS 1 #endif @@ -1181,10 +1184,6 @@ #define WTF_USE_ZLIB 1 #endif -#if PLATFORM(GTK) -#define WTF_DEPRECATED_STRING_OPERATORS -#endif - #if PLATFORM(QT) #include #if defined(QT_OPENGL_ES_2) && !defined(WTF_USE_OPENGL_ES_2) diff --git a/masm/wtf/RefCounted.h b/masm/wtf/RefCounted.h index cea1434e1b..0504b9ed28 100644 --- a/masm/wtf/RefCounted.h +++ b/masm/wtf/RefCounted.h @@ -30,6 +30,12 @@ namespace WTF { +#ifdef NDEBUG +#define CHECK_REF_COUNTED_LIFECYCLE 0 +#else +#define CHECK_REF_COUNTED_LIFECYCLE 1 +#endif + // This base class holds the non-template methods and attributes. // The RefCounted class inherits from it reducing the template bloat // generated by the compiler (technique called template hoisting). @@ -37,7 +43,7 @@ class RefCountedBase { public: void ref() { -#ifndef NDEBUG +#if CHECK_REF_COUNTED_LIFECYCLE // Start thread verification as soon as the ref count gets to 2. This // heuristic reflects the fact that items are often created on one thread // and then given to another thread to be used. @@ -46,26 +52,30 @@ public: // We should be able to add a "detachFromThread" method to make this explicit. if (m_refCount == 1) m_verifier.setShared(true); -#endif // If this assert fires, it either indicates a thread safety issue or // that the verification needs to change. See ThreadRestrictionVerifier for // the different modes. ASSERT(m_verifier.isSafeToUse()); ASSERT(!m_deletionHasBegun); ASSERT(!m_adoptionIsRequired); +#endif ++m_refCount; } bool hasOneRef() const { +#if CHECK_REF_COUNTED_LIFECYCLE ASSERT(m_verifier.isSafeToUse()); ASSERT(!m_deletionHasBegun); +#endif return m_refCount == 1; } int refCount() const { +#if CHECK_REF_COUNTED_LIFECYCLE ASSERT(m_verifier.isSafeToUse()); +#endif return m_refCount; } @@ -87,14 +97,14 @@ public: // safe version of reference counting. void turnOffVerifier() { -#ifndef NDEBUG +#if CHECK_REF_COUNTED_LIFECYCLE m_verifier.turnOffVerification(); #endif } void relaxAdoptionRequirement() { -#ifndef NDEBUG +#if CHECK_REF_COUNTED_LIFECYCLE ASSERT(!m_deletionHasBegun); ASSERT(m_adoptionIsRequired); m_adoptionIsRequired = false; @@ -110,7 +120,7 @@ public: protected: RefCountedBase() : m_refCount(1) -#ifndef NDEBUG +#if CHECK_REF_COUNTED_LIFECYCLE , m_deletionHasBegun(false) , m_adoptionIsRequired(true) #endif @@ -119,27 +129,31 @@ protected: ~RefCountedBase() { +#if CHECK_REF_COUNTED_LIFECYCLE ASSERT(m_deletionHasBegun); ASSERT(!m_adoptionIsRequired); +#endif } // Returns whether the pointer should be freed or not. bool derefBase() { +#if CHECK_REF_COUNTED_LIFECYCLE ASSERT(m_verifier.isSafeToUse()); ASSERT(!m_deletionHasBegun); ASSERT(!m_adoptionIsRequired); +#endif ASSERT(m_refCount > 0); if (m_refCount == 1) { -#ifndef NDEBUG +#if CHECK_REF_COUNTED_LIFECYCLE m_deletionHasBegun = true; #endif return true; } --m_refCount; -#ifndef NDEBUG +#if CHECK_REF_COUNTED_LIFECYCLE // Stop thread verification when the ref goes to 1 because it // is safe to be passed to another thread at this point. if (m_refCount == 1) @@ -148,7 +162,7 @@ protected: return false; } -#ifndef NDEBUG +#if CHECK_REF_COUNTED_LIFECYCLE bool deletionHasBegun() const { return m_deletionHasBegun; @@ -157,20 +171,19 @@ protected: private: -#ifndef NDEBUG +#if CHECK_REF_COUNTED_LIFECYCLE friend void adopted(RefCountedBase*); #endif int m_refCount; -#ifndef NDEBUG +#if CHECK_REF_COUNTED_LIFECYCLE bool m_deletionHasBegun; bool m_adoptionIsRequired; ThreadRestrictionVerifier m_verifier; #endif }; -#ifndef NDEBUG - +#if CHECK_REF_COUNTED_LIFECYCLE inline void adopted(RefCountedBase* object) { if (!object) @@ -178,7 +191,6 @@ inline void adopted(RefCountedBase* object) ASSERT(!object->m_deletionHasBegun); object->m_adoptionIsRequired = false; } - #endif template class RefCounted : public RefCountedBase { @@ -213,24 +225,24 @@ protected: } }; -#ifdef NDEBUG -inline void RefCountedBase::setMutexForVerifier(Mutex&) { } -#else +#if CHECK_REF_COUNTED_LIFECYCLE inline void RefCountedBase::setMutexForVerifier(Mutex& mutex) { m_verifier.setMutexMode(mutex); } +#else +inline void RefCountedBase::setMutexForVerifier(Mutex&) { } #endif #if HAVE(DISPATCH_H) -#ifdef NDEBUG -inline void RefCountedBase::setDispatchQueueForVerifier(dispatch_queue_t) { } -#else +#if CHECK_REF_COUNTED_LIFECYCLE inline void RefCountedBase::setDispatchQueueForVerifier(dispatch_queue_t queue) { m_verifier.setDispatchQueueMode(queue); } -#endif // NDEBUG +#else +inline void RefCountedBase::setDispatchQueueForVerifier(dispatch_queue_t) { } +#endif #endif // HAVE(DISPATCH_H) } // namespace WTF diff --git a/masm/wtf/TypeTraits.h b/masm/wtf/TypeTraits.h index 097b2fd790..b9e46bc555 100644 --- a/masm/wtf/TypeTraits.h +++ b/masm/wtf/TypeTraits.h @@ -228,208 +228,34 @@ namespace WTF { >::Type Type; }; -#if (defined(__GLIBCXX__) && (__GLIBCXX__ >= 20070724) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || (defined(_MSC_VER) && (_MSC_VER >= 1600)) - +#if COMPILER(CLANG) || GCC_VERSION_AT_LEAST(4, 6, 0) || (defined(_MSC_VER) && (_MSC_VER >= 1400) && (_MSC_VER < 1600) && !defined(__INTEL_COMPILER)) + // VC8 (VS2005) and later has __has_trivial_constructor and __has_trivial_destructor, + // but the implementation returns false for built-in types. We add the extra IsPod condition to + // work around this. + template struct HasTrivialConstructor { + static const bool value = __has_trivial_constructor(T) || IsPod >::value; + }; + template struct HasTrivialDestructor { + static const bool value = __has_trivial_destructor(T) || IsPod >::value; + }; +#elif (defined(__GLIBCXX__) && (__GLIBCXX__ >= 20070724) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || (defined(_MSC_VER) && (_MSC_VER >= 1600)) // GCC's libstdc++ 20070724 and later supports C++ TR1 type_traits in the std namespace. // VC10 (VS2010) and later support C++ TR1 type_traits in the std::tr1 namespace. template struct HasTrivialConstructor : public std::tr1::has_trivial_constructor { }; template struct HasTrivialDestructor : public std::tr1::has_trivial_destructor { }; - #else - - // This compiler doesn't provide type traits, so we provide basic HasTrivialConstructor - // and HasTrivialDestructor definitions. The definitions here include most built-in - // scalar types but do not include POD structs and classes. For the intended purposes of - // type_traits this results correct but potentially less efficient code. - template - struct IntegralConstant { - static const T value = v; - typedef T value_type; - typedef IntegralConstant type; + // For compilers that don't support detection of trivial constructors and destructors in classes, + // we use a template that returns true for any POD type that IsPod can detect (see IsPod caveats above), + // but false for all other types (which includes all classes). This will give false negatives, which can hurt + // performance, but avoids false positives, which would result in incorrect behavior. + template struct HasTrivialConstructor { + static const bool value = IsPod >::value; + }; + template struct HasTrivialDestructor { + static const bool value = IsPod >::value; }; - - typedef IntegralConstant true_type; - typedef IntegralConstant false_type; - -#if COMPILER(CLANG) || (defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(__INTEL_COMPILER)) - // VC8 (VS2005) and later have built-in compiler support for HasTrivialConstructor / HasTrivialDestructor, - // but for some unexplained reason it doesn't work on built-in types. - template struct HasTrivialConstructor : public IntegralConstant{ }; - template struct HasTrivialDestructor : public IntegralConstant{ }; -#else - template struct HasTrivialConstructor : public false_type{ }; - template struct HasTrivialDestructor : public false_type{ }; #endif - template struct HasTrivialConstructor : public true_type{ }; - template struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - - #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - template <> struct HasTrivialConstructor : public true_type{ }; - #endif - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - - #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - template <> struct HasTrivialDestructor : public true_type{ }; - #endif - -#endif // __GLIBCXX__, etc. - } // namespace WTF #endif // TypeTraits_h diff --git a/masm/wtf/Vector.h b/masm/wtf/Vector.h index 8a5eaf6213..6cd43115b1 100644 --- a/masm/wtf/Vector.h +++ b/masm/wtf/Vector.h @@ -32,10 +32,6 @@ #include #include -#if PLATFORM(QT) -#include -#endif - namespace WTF { using std::min; @@ -619,6 +615,7 @@ namespace WTF { template void append(const U&); template void uncheckedAppend(const U& val); template void append(const Vector&); + template void appendVector(const Vector&); template bool tryAppend(const U*, size_t); template void insert(size_t position, const U*, size_t); @@ -695,32 +692,6 @@ namespace WTF { Buffer m_buffer; }; -#if PLATFORM(QT) - template - QDataStream& operator<<(QDataStream& stream, const Vector& data) - { - stream << qint64(data.size()); - foreach (const T& i, data) - stream << i; - return stream; - } - - template - QDataStream& operator>>(QDataStream& stream, Vector& data) - { - data.clear(); - qint64 count; - T item; - stream >> count; - data.reserveCapacity(count); - for (qint64 i = 0; i < count; ++i) { - stream >> item; - data.append(item); - } - return stream; - } -#endif - template Vector::Vector(const Vector& other) : m_size(other.size()) @@ -1103,6 +1074,12 @@ namespace WTF { append(val.begin(), val.size()); } + template template + inline void Vector::appendVector(const Vector& val) + { + append(val.begin(), val.size()); + } + template template void Vector::insert(size_t position, const U* data, size_t dataSize) { -- cgit v1.2.3 From 617240f92a52fd0672c348adcf9f565b25986d48 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 13:38:50 +0200 Subject: More changes to the new calling convention Change-Id: Icc22f2ed342ced9eac0d62307151acca8031d2c2 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 4 +- qmljs_runtime.cpp | 132 ++++++++++++++++++++++++++++++++--------------------- qmljs_runtime.h | 89 +++++++++++++++++++----------------- qv4ecmaobjects.cpp | 8 ++-- qv4isel_masm.cpp | 4 +- 5 files changed, 136 insertions(+), 101 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index f8770bbfa6..d828df3760 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -284,12 +284,12 @@ void __qmljs_llvm_set_property(Context *ctx, Value *object, String *name, Value void __qmljs_llvm_typeof(Context *ctx, Value *result, const Value *value) { - *result = __qmljs_typeof(ctx, *value); + *result = __qmljs_typeof(*value, ctx); } void __qmljs_llvm_throw(Context *context, Value *value) { - __qmljs_throw(context, *value); + __qmljs_throw(*value, context); } void __qmljs_llvm_rethrow(Context *context, Value *result) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index fd8d61a6ad..65ca1ae04c 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -163,14 +163,14 @@ double Value::toNumber(Context *ctx) const String *Value::toString(Context *ctx) const { - Value v = __qmljs_to_string(ctx, *this); + Value v = __qmljs_to_string(*this, ctx); assert(v.is(Value::String_Type)); return v.stringValue(); } Value Value::toObject(Context *ctx) const { - return __qmljs_to_object(ctx, *this); + return __qmljs_to_object(*this, ctx); } bool Value::isFunctionObject() const @@ -491,13 +491,13 @@ Value __qmljs_delete_value(Context *ctx, Value value) Value __qmljs_add_helper(const Value left, const Value right, Context *ctx) { - Value pleft = __qmljs_to_primitive(ctx, left, PREFERREDTYPE_HINT); - Value pright = __qmljs_to_primitive(ctx, right, PREFERREDTYPE_HINT); + Value pleft = __qmljs_to_primitive(left, ctx, PREFERREDTYPE_HINT); + Value pright = __qmljs_to_primitive(right, ctx, PREFERREDTYPE_HINT); if (pleft.isString() || pright.isString()) { if (!pleft.isString()) - pleft = __qmljs_to_string(ctx, pleft); + pleft = __qmljs_to_string(pleft, ctx); if (!pright.isString()) - pright = __qmljs_to_string(ctx, pright); + pright = __qmljs_to_string(pright, ctx); String *string = __qmljs_string_concat(ctx, pleft.stringValue(), pright.stringValue()); return Value::fromString(string); } @@ -519,7 +519,7 @@ Value __qmljs_instanceof(const Value left, const Value right, Context *ctx) Value __qmljs_in(const Value left, const Value right, Context *ctx) { if (right.isObject()) { - Value s = __qmljs_to_string(ctx, left); + Value s = __qmljs_to_string(left, ctx); bool r = right.objectValue()->hasProperty(ctx, s.stringValue()); return Value::fromBoolean(r); } else { @@ -527,62 +527,92 @@ Value __qmljs_in(const Value left, const Value right, Context *ctx) } } -void __qmljs_inplace_bit_and_name(Context *ctx, String *name, Value *value) +void __qmljs_inplace_bit_and_name(const Value value, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + if (Value *prop = ctx->lookupPropertyDescriptor(name)) + *prop = __qmljs_bit_and(*prop, value, ctx); + else + ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_bit_or_name(Context *ctx, String *name, Value *value) +void __qmljs_inplace_bit_or_name(const Value value, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + if (Value *prop = ctx->lookupPropertyDescriptor(name)) + *prop = __qmljs_bit_or(*prop, value, ctx); + else + ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_bit_xor_name(Context *ctx, String *name, Value *value) +void __qmljs_inplace_bit_xor_name(const Value value, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + if (Value *prop = ctx->lookupPropertyDescriptor(name)) + *prop = __qmljs_bit_xor(*prop, value, ctx); + else + ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_add_name(Context *ctx, String *name, Value *value) +void __qmljs_inplace_add_name(const Value value, String *name, Context *ctx) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) - *prop = __qmljs_add(*prop, *value, ctx); + *prop = __qmljs_add(*prop, value, ctx); else ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_sub_name(Context *ctx, String *name, Value *value) +void __qmljs_inplace_sub_name(const Value value, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + if (Value *prop = ctx->lookupPropertyDescriptor(name)) + *prop = __qmljs_sub(*prop, value, ctx); + else + ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_mul_name(Context *ctx, String *name, Value *value) +void __qmljs_inplace_mul_name(const Value value, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + if (Value *prop = ctx->lookupPropertyDescriptor(name)) + *prop = __qmljs_mul(*prop, value, ctx); + else + ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_div_name(Context *ctx, String *name, Value *value) +void __qmljs_inplace_div_name(const Value value, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + if (Value *prop = ctx->lookupPropertyDescriptor(name)) + *prop = __qmljs_div(*prop, value, ctx); + else + ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_mod_name(Context *ctx, String *name, Value *value) +void __qmljs_inplace_mod_name(const Value value, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + if (Value *prop = ctx->lookupPropertyDescriptor(name)) + *prop = __qmljs_mod(*prop, value, ctx); + else + ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_shl_name(Context *ctx, String *name, Value *value) +void __qmljs_inplace_shl_name(const Value value, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + if (Value *prop = ctx->lookupPropertyDescriptor(name)) + *prop = __qmljs_shl(*prop, value, ctx); + else + ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_shr_name(Context *ctx, String *name, Value *value) +void __qmljs_inplace_shr_name(const Value value, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + if (Value *prop = ctx->lookupPropertyDescriptor(name)) + *prop = __qmljs_shr(*prop, value, ctx); + else + ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_ushr_name(Context *ctx, String *name, Value *value) +void __qmljs_inplace_ushr_name(const Value value, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + if (Value *prop = ctx->lookupPropertyDescriptor(name)) + *prop = __qmljs_ushr(*prop, value, ctx); + else + ctx->throwReferenceError(Value::fromString(name)); } void __qmljs_inplace_bit_and_element(Context *ctx, Value *base, Value *index, Value *value) @@ -898,7 +928,7 @@ Value __qmljs_get_element(Context *ctx, Value object, Value index) String *name = index.toString(ctx); if (! object.isObject()) - object = __qmljs_to_object(ctx, object); + object = __qmljs_to_object(object, ctx); return object.property(ctx, name); } @@ -912,7 +942,7 @@ void __qmljs_set_element(Context *ctx, Value object, Value index, Value value) String *name = index.toString(ctx); if (! object.isObject()) - object = __qmljs_to_object(ctx, object); + object = __qmljs_to_object(object, ctx); object.objectValue()->setProperty(ctx, name, value, /*flags*/ 0); } @@ -963,7 +993,7 @@ Value __qmljs_get_property(Context *ctx, Value object, String *name) } else if (object.isString() && name->isEqualTo(ctx->engine->id_length)) { return Value::fromDouble(object.stringValue()->toQString().length()); } else { - object = __qmljs_to_object(ctx, object); + object = __qmljs_to_object(object, ctx); if (object.isObject()) { return __qmljs_get_property(ctx, object, name); @@ -989,16 +1019,16 @@ Value __qmljs_get_thisObject(Context *ctx) return ctx->engine->globalObject; } -Value __qmljs_compare(Context *ctx, const Value x, const Value y, bool leftFirst) +Value __qmljs_compare(const Value x, const Value y, Context *ctx, bool leftFirst) { Value px, py; if (leftFirst) { - px = __qmljs_to_primitive(ctx, x, NUMBER_HINT); - py = __qmljs_to_primitive(ctx, y, NUMBER_HINT); + px = __qmljs_to_primitive(x, ctx, NUMBER_HINT); + py = __qmljs_to_primitive(y, ctx, NUMBER_HINT); } else { - px = __qmljs_to_primitive(ctx, x, NUMBER_HINT); - py = __qmljs_to_primitive(ctx, y, NUMBER_HINT); + px = __qmljs_to_primitive(x, ctx, NUMBER_HINT); + py = __qmljs_to_primitive(y, ctx, NUMBER_HINT); } if (px.isString() && py.isString()) { @@ -1015,7 +1045,7 @@ Value __qmljs_compare(Context *ctx, const Value x, const Value y, bool leftFirst } } -uint __qmljs_equal(Context *ctx, const Value x, const Value y) +uint __qmljs_equal(const Value x, const Value y, Context *ctx) { if (x.type() == y.type()) { switch (x.type()) { @@ -1045,22 +1075,22 @@ uint __qmljs_equal(Context *ctx, const Value x, const Value y) return true; } else if (x.isNumber() && y.isString()) { Value ny = Value::fromDouble(__qmljs_to_number(y, ctx)); - return __qmljs_equal(ctx, x, ny); + return __qmljs_equal(x, ny, ctx); } else if (x.isString() && y.isNumber()) { Value nx = Value::fromDouble(__qmljs_to_number(x, ctx)); - return __qmljs_equal(ctx, nx, y); + return __qmljs_equal(nx, y, ctx); } else if (x.isBoolean()) { Value nx = Value::fromDouble((double) x.booleanValue()); - return __qmljs_equal(ctx, nx, y); + return __qmljs_equal(nx, y, ctx); } else if (y.isBoolean()) { Value ny = Value::fromDouble((double) y.booleanValue()); - return __qmljs_equal(ctx, x, ny); + return __qmljs_equal(x, ny, ctx); } else if ((x.isNumber() || x.isString()) && y.isObject()) { - Value py = __qmljs_to_primitive(ctx, y, PREFERREDTYPE_HINT); - return __qmljs_equal(ctx, x, py); + Value py = __qmljs_to_primitive(y, ctx, PREFERREDTYPE_HINT); + return __qmljs_equal(x, py, ctx); } else if (x.isObject() && (y.isNumber() || y.isString())) { - Value px = __qmljs_to_primitive(ctx, x, PREFERREDTYPE_HINT); - return __qmljs_equal(ctx, px, y); + Value px = __qmljs_to_primitive(x, ctx, PREFERREDTYPE_HINT); + return __qmljs_equal(px, y, ctx); } } @@ -1085,7 +1115,7 @@ Value __qmljs_call_property(Context *context, const Value base, String *name, Va if (!base.isUndefined()) { baseObject = base; if (!baseObject.isObject()) - baseObject = __qmljs_to_object(context, baseObject); + baseObject = __qmljs_to_object(baseObject, context); assert(baseObject.isObject()); thisObject = baseObject; } else { @@ -1164,7 +1194,7 @@ Value __qmljs_construct_property(Context *context, const Value base, String *nam { Value thisObject = base; if (!thisObject.isObject()) - thisObject = __qmljs_to_object(context, base); + thisObject = __qmljs_to_object(base, context); assert(thisObject.isObject()); Value func = thisObject.property(context, name); @@ -1185,7 +1215,7 @@ Value __qmljs_construct_property(Context *context, const Value base, String *nam return Value::undefinedValue(); } -void __qmljs_throw(Context *context, Value value) +void __qmljs_throw(Value value, Context *context) { context->hasUncaughtException = true; context->result = value; @@ -1199,13 +1229,13 @@ Value __qmljs_rethrow(Context *context) Value __qmljs_builtin_typeof(Context *context, Value *args, int argc) { Q_UNUSED(argc); - return __qmljs_typeof(context, args[0]); + return __qmljs_typeof(args[0], context); } Value __qmljs_builtin_throw(Context *context, Value *args, int argc) { Q_UNUSED(argc); - __qmljs_throw(context, args[0]); + __qmljs_throw(args[0], context); // ### change to void return value return Value::undefinedValue(); } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index a8ea72ca9f..84a5da8bf8 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -151,22 +151,22 @@ void __qmljs_set_element(Context *ctx, Value object, Value index, Value value); Value __qmljs_get_thisObject(Context *ctx); // type conversion and testing -Value __qmljs_to_primitive(Context *ctx, const Value value, int typeHint); +Value __qmljs_to_primitive(const Value value, Context *ctx, int typeHint); Bool __qmljs_to_boolean(const Value value, Context *ctx); double __qmljs_to_number(const Value value, Context *ctx); double __qmljs_to_integer(const Value value, Context *ctx); int __qmljs_to_int32(const Value value, Context *ctx); unsigned __qmljs_to_uint32(const Value value, Context *ctx); unsigned short __qmljs_to_uint16(const Value value, Context *ctx); -Value __qmljs_to_string(Context *ctx, const Value value); -Value __qmljs_to_object(Context *ctx, const Value value); +Value __qmljs_to_string(const Value value, Context *ctx); +Value __qmljs_to_object(const Value value, Context *ctx); //uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value); -Bool __qmljs_is_callable(Context *ctx, const Value value); -Value __qmljs_default_value(Context *ctx, const Value value, int typeHint); +Bool __qmljs_is_callable(const Value value, Context *ctx); +Value __qmljs_default_value(const Value value, Context *ctx, int typeHint); -Value __qmljs_compare(Context *ctx, const Value left, const Value right, bool leftFlag); -Bool __qmljs_equal(Context *ctx, const Value x, const Value y); -Bool __qmljs_strict_equal(Context *ctx, const Value x, const Value y); +Value __qmljs_compare(const Value left, const Value right, Context *ctx, bool leftFlag); +Bool __qmljs_equal(const Value x, const Value y, Context *ctx); +Bool __qmljs_strict_equal(const Value x, const Value y, Context *ctx); // unary operators Value __qmljs_uplus(const Value value, Context *ctx); @@ -180,8 +180,8 @@ Value __qmljs_delete_member(Context *ctx, Value base, String *name); Value __qmljs_delete_property(Context *ctx, String *name); Value __qmljs_delete_value(Context *ctx, Value value); -Value __qmljs_typeof(Context *ctx, const Value value); -void __qmljs_throw(Context *context, Value value); +Value __qmljs_typeof(const Value value, Context *ctx); +void __qmljs_throw(Value value, Context *context); Value __qmljs_rethrow(Context *context); // binary operators @@ -209,6 +209,8 @@ Value __qmljs_sne(const Value left, const Value right, Context *ctx); Value __qmljs_add_helper(const Value left, const Value right, Context *ctx); +/* + unused and probably don't make sense with the new calling convention void __qmljs_inplace_bit_and(Context *ctx, Value *result, Value *value); void __qmljs_inplace_bit_or(Context *ctx, Value *result, Value *value); void __qmljs_inplace_bit_xor(Context *ctx, Value *result, Value *value); @@ -220,18 +222,19 @@ void __qmljs_inplace_mod(Context *ctx, Value *result, Value *value); void __qmljs_inplace_shl(Context *ctx, Value *result, Value *value); void __qmljs_inplace_shr(Context *ctx, Value *result, Value *value); void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value); +*/ -void __qmljs_inplace_bit_and_name(Context *ctx, String *name, Value *value); -void __qmljs_inplace_bit_or_name(Context *ctx, String *name, Value *value); -void __qmljs_inplace_bit_xor_name(Context *ctx, String *name, Value *value); -void __qmljs_inplace_add_name(Context *ctx, String *name, Value *value); -void __qmljs_inplace_sub_name(Context *ctx, String *name, Value *value); -void __qmljs_inplace_mul_name(Context *ctx, String *name, Value *value); -void __qmljs_inplace_div_name(Context *ctx, String *name, Value *value); -void __qmljs_inplace_mod_name(Context *ctx, String *name, Value *value); -void __qmljs_inplace_shl_name(Context *ctx, String *name, Value *value); -void __qmljs_inplace_shr_name(Context *ctx, String *name, Value *value); -void __qmljs_inplace_ushr_name(Context *ctx, String *name, Value *value); +void __qmljs_inplace_bit_and_name(const Value value, String *name, Context *ctx); +void __qmljs_inplace_bit_or_name(const Value value, String *name, Context *ctx); +void __qmljs_inplace_bit_xor_name(const Value value, String *name, Context *ctx); +void __qmljs_inplace_add_name(const Value value, String *name, Context *ctx); +void __qmljs_inplace_sub_name(const Value value, String *name, Context *ctx); +void __qmljs_inplace_mul_name(const Value value, String *name, Context *ctx); +void __qmljs_inplace_div_name(const Value value, String *name, Context *ctx); +void __qmljs_inplace_mod_name(const Value value, String *name, Context *ctx); +void __qmljs_inplace_shl_name(const Value value, String *name, Context *ctx); +void __qmljs_inplace_shr_name(const Value value, String *name, Context *ctx); +void __qmljs_inplace_ushr_name(const Value value, String *name, Context *ctx); void __qmljs_inplace_bit_and_element(Context *ctx, Value *base, Value *index, Value *value); void __qmljs_inplace_bit_or_element(Context *ctx, Value *base, Value *index, Value *value); @@ -677,11 +680,11 @@ inline Value __qmljs_init_object(Object *object) } // type conversion and testing -inline Value __qmljs_to_primitive(Context *ctx, const Value value, int typeHint) +inline Value __qmljs_to_primitive(const Value value, Context *ctx, int typeHint) { if (!value.isObject()) return value; - return __qmljs_default_value(ctx, value, typeHint); + return __qmljs_default_value(value, ctx, typeHint); } inline Bool __qmljs_to_boolean(const Value value, Context *ctx) @@ -718,7 +721,7 @@ inline double __qmljs_to_number(const Value value, Context *ctx) case Value::String_Type: return __qmljs_string_to_number(ctx, value.stringValue()); case Value::Object_Type: { - Value prim = __qmljs_to_primitive(ctx, value, NUMBER_HINT); + Value prim = __qmljs_to_primitive(value, ctx, NUMBER_HINT); return __qmljs_to_number(prim, ctx); } default: // double @@ -807,7 +810,7 @@ inline unsigned short __qmljs_to_uint16(const Value value, Context *ctx) return (unsigned short)number; } -inline Value __qmljs_to_string(Context *ctx, const Value value) +inline Value __qmljs_to_string(const Value value, Context *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -826,9 +829,9 @@ inline Value __qmljs_to_string(Context *ctx, const Value value) return value; break; case Value::Object_Type: { - Value prim = __qmljs_to_primitive(ctx, value, STRING_HINT); + Value prim = __qmljs_to_primitive(value, ctx, STRING_HINT); if (prim.isPrimitive()) - return __qmljs_to_string(ctx, prim); + return __qmljs_to_string(prim, ctx); else return __qmljs_throw_type_error(ctx); break; @@ -843,7 +846,7 @@ inline Value __qmljs_to_string(Context *ctx, const Value value) } // switch } -inline Value __qmljs_to_object(Context *ctx, const Value value) +inline Value __qmljs_to_object(const Value value, Context *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -882,7 +885,7 @@ inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Va } */ -inline Bool __qmljs_is_callable(Context *ctx, const Value value) +inline Bool __qmljs_is_callable(const Value value, Context *ctx) { if (value.isObject()) return __qmljs_is_function(value); @@ -890,7 +893,7 @@ inline Bool __qmljs_is_callable(Context *ctx, const Value value) return false; } -inline Value __qmljs_default_value(Context *ctx, const Value value, int typeHint) +inline Value __qmljs_default_value(const Value value, Context *ctx, int typeHint) { if (value.isObject()) return __qmljs_object_default_value(ctx, value, typeHint); @@ -899,7 +902,7 @@ inline Value __qmljs_default_value(Context *ctx, const Value value, int typeHint // unary operators -inline Value __qmljs_typeof(Context *ctx, const Value value) +inline Value __qmljs_typeof(const Value value, Context *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -915,7 +918,7 @@ inline Value __qmljs_typeof(Context *ctx, const Value value) return __qmljs_string_literal_string(ctx); break; case Value::Object_Type: - if (__qmljs_is_callable(ctx, value)) + if (__qmljs_is_callable(value, ctx)) return __qmljs_string_literal_function(ctx); else return __qmljs_string_literal_object(ctx); // ### implementation-defined @@ -977,6 +980,7 @@ inline Value __qmljs_bit_and(const Value left, const Value right, Context *ctx) return Value::fromInt32(lval & rval); } +/* inline void __qmljs_inplace_bit_and(Context *ctx, Value *result, Value *value) { *result = __qmljs_bit_and(*result, *value, ctx); @@ -1031,6 +1035,7 @@ inline void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value) { *result = __qmljs_ushr(*result, *value, ctx); } +*/ inline Value __qmljs_add(const Value left, const Value right, Context *ctx) { @@ -1107,7 +1112,7 @@ inline Value __qmljs_gt(const Value left, const Value right, Context *ctx) if (left.isNumber() && right.isNumber()) { return Value::fromBoolean(left.asDouble() > right.asDouble()); } else { - Value result = __qmljs_compare(ctx, left, right, false); + Value result = __qmljs_compare(left, right, ctx, false); if (result.isUndefined()) result = Value::fromBoolean(false); @@ -1122,7 +1127,7 @@ inline Value __qmljs_lt(const Value left, const Value right, Context *ctx) if (left.isNumber() && right.isNumber()) { return Value::fromBoolean(left.asDouble() < right.asDouble()); } else { - Value result = __qmljs_compare(ctx, left, right, true); + Value result = __qmljs_compare(left, right, ctx, true); if (result.isUndefined()) result = Value::fromBoolean(false); @@ -1137,7 +1142,7 @@ inline Value __qmljs_ge(const Value left, const Value right, Context *ctx) if (left.isNumber() && right.isNumber()) { return Value::fromBoolean(left.asDouble() >= right.asDouble()); } else { - Value result = __qmljs_compare(ctx, right, left, false); + Value result = __qmljs_compare(right, left, ctx, false); bool r = ! (result.isUndefined() || (result.isBoolean() && result.booleanValue())); return Value::fromBoolean(r); @@ -1151,7 +1156,7 @@ inline Value __qmljs_le(const Value left, const Value right, Context *ctx) if (left.isNumber() && right.isNumber()) { return Value::fromBoolean(left.asDouble() <= right.asDouble()); } else { - Value result = __qmljs_compare(ctx, right, left, true); + Value result = __qmljs_compare(right, left, ctx, true); bool r = ! (result.isUndefined() || (result.isBoolean() && result.booleanValue())); return Value::fromBoolean(r); @@ -1167,7 +1172,7 @@ inline Value __qmljs_eq(const Value left, const Value right, Context *ctx) } else if (left.isString() && right.isString()) { return Value::fromBoolean(__qmljs_string_equal(ctx, left.stringValue(), right.stringValue())); } else { - bool r = __qmljs_equal(ctx, left, right); + bool r = __qmljs_equal(left, right, ctx); return Value::fromBoolean(r); } } @@ -1181,13 +1186,13 @@ inline Value __qmljs_ne(const Value left, const Value right, Context *ctx) inline Value __qmljs_se(const Value left, const Value right, Context *ctx) { - bool r = __qmljs_strict_equal(ctx, left, right); + bool r = __qmljs_strict_equal(left, right, ctx); return Value::fromBoolean(r); } inline Value __qmljs_sne(const Value left, const Value right, Context *ctx) { - bool r = ! __qmljs_strict_equal(ctx, left, right); + bool r = ! __qmljs_strict_equal(left, right, ctx); return Value::fromBoolean(r); } @@ -1229,12 +1234,12 @@ inline Bool __qmljs_cmp_ne(Context *ctx, const Value left, const Value right) inline Bool __qmljs_cmp_se(Context *ctx, const Value left, const Value right) { - return __qmljs_strict_equal(ctx, left, right); + return __qmljs_strict_equal(left, right, ctx); } inline Bool __qmljs_cmp_sne(Context *ctx, const Value left, const Value right) { - return ! __qmljs_strict_equal(ctx, left, right); + return ! __qmljs_strict_equal(left, right, ctx); } inline Bool __qmljs_cmp_instanceof(Context *ctx, const Value left, const Value right) @@ -1249,7 +1254,7 @@ inline uint __qmljs_cmp_in(Context *ctx, const Value left, const Value right) return v.booleanValue(); } -inline Bool __qmljs_strict_equal(Context *ctx, const Value x, const Value y) +inline Bool __qmljs_strict_equal(const Value x, const Value y, Context *ctx) { if (x.rawValue() == y.rawValue()) return true; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index f303f8f123..c2f349b596 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -714,7 +714,7 @@ void StringCtor::call(Context *ctx) if (arg.is(Value::Undefined_Type)) ctx->result = Value::fromString(ctx->engine->newString(QString())); else - ctx->result = __qmljs_to_string(ctx, arg); + ctx->result = __qmljs_to_string(arg, ctx); } void StringPrototype::init(Context *ctx, const Value &ctor) @@ -808,7 +808,7 @@ void StringPrototype::method_concat(Context *ctx) QString value = getThisString(ctx); for (unsigned i = 0; i < ctx->argumentCount; ++i) { - Value v = __qmljs_to_string(ctx, ctx->arguments[i]); + Value v = __qmljs_to_string(ctx->arguments[i], ctx); assert(v.is(Value::String_Type)); value += v.stringValue()->toQString(); } @@ -841,7 +841,7 @@ void StringPrototype::method_lastIndexOf(Context *ctx) QString searchString; if (ctx->argumentCount) { - Value v = __qmljs_to_string(ctx, ctx->arguments[0]); + Value v = __qmljs_to_string(ctx->arguments[0], ctx); searchString = v.stringValue()->toQString(); } @@ -1848,7 +1848,7 @@ void DateCtor::construct(Context *ctx) if (DateObject *d = arg.asDateObject()) arg = d->value; else - arg = __qmljs_to_primitive(ctx, arg, PREFERREDTYPE_HINT); + arg = __qmljs_to_primitive(arg, ctx, PREFERREDTYPE_HINT); if (arg.isString()) t = ParseString(arg.toString(ctx)->toQString()); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index c2f09afe41..bed5317146 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -522,7 +522,7 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Name *n = s->target->asName()) { if (IR::Temp *t = s->source->asTemp()) { - void (*op)(Context *ctx, String *name, Value *value) = 0; + void (*op)(const Value value, String *name, Context *ctx) = 0; const char *opName = 0; switch (s->op) { case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break; @@ -541,7 +541,7 @@ void InstructionSelection::visitMove(IR::Move *s) break; } if (op) { - generateFunctionCallImp(opName, op, ContextRegister, identifier(*n->id), t); + generateFunctionCallImp2(Void, opName, op, t, identifier(*n->id), ContextRegister); checkExceptions(); } return; -- cgit v1.2.3 From 649c318d399b66593c3c6afced83d3265c7c3a39 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 13:38:56 +0200 Subject: Fix order of arguments Change-Id: I8ecf66667f1dfebc33715ba396cb4bf7d27535d0 Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 40 ++++++++++++++++++++-------------------- qv4isel_masm.cpp | 4 ++-- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 84a5da8bf8..f1767a7c59 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -260,16 +260,16 @@ void __qmljs_inplace_shl_member(Context *ctx, Value *base, String *name, Value * void __qmljs_inplace_shr_member(Context *ctx, Value *base, String *name, Value *value); void __qmljs_inplace_ushr_member(Context *ctx, Value *base, String *name, Value *value); -Bool __qmljs_cmp_gt(Context *ctx, const Value left, const Value right); -Bool __qmljs_cmp_lt(Context *ctx, const Value left, const Value right); -Bool __qmljs_cmp_ge(Context *ctx, const Value left, const Value right); -Bool __qmljs_cmp_le(Context *ctx, const Value left, const Value right); -Bool __qmljs_cmp_eq(Context *ctx, const Value left, const Value right); -Bool __qmljs_cmp_ne(Context *ctx, const Value left, const Value right); -Bool __qmljs_cmp_se(Context *ctx, const Value left, const Value right); -Bool __qmljs_cmp_sne(Context *ctx, const Value left, const Value right); -Bool __qmljs_cmp_instanceof(Context *ctx, const Value left, const Value right); -Bool __qmljs_cmp_in(Context *ctx, const Value left, const Value right); +Bool __qmljs_cmp_gt(const Value left, const Value right, Context *ctx); +Bool __qmljs_cmp_lt(const Value left, const Value right, Context *ctx); +Bool __qmljs_cmp_ge(const Value left, const Value right, Context *ctx); +Bool __qmljs_cmp_le(const Value left, const Value right, Context *ctx); +Bool __qmljs_cmp_eq(const Value left, const Value right, Context *ctx); +Bool __qmljs_cmp_ne(const Value left, const Value right, Context *ctx); +Bool __qmljs_cmp_se(const Value left, const Value right, Context *ctx); +Bool __qmljs_cmp_sne(const Value left, const Value right, Context *ctx); +Bool __qmljs_cmp_instanceof(const Value left, const Value right, Context *ctx); +Bool __qmljs_cmp_in(const Value left, const Value right, Context *ctx); } // extern "C" @@ -1196,59 +1196,59 @@ inline Value __qmljs_sne(const Value left, const Value right, Context *ctx) return Value::fromBoolean(r); } -inline Bool __qmljs_cmp_gt(Context *ctx, const Value left, const Value right) +inline Bool __qmljs_cmp_gt(const Value left, const Value right, Context *ctx) { Value v = __qmljs_gt(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_lt(Context *ctx, const Value left, const Value right) +inline Bool __qmljs_cmp_lt(const Value left, const Value right, Context *ctx) { Value v = __qmljs_lt(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_ge(Context *ctx, const Value left, const Value right) +inline Bool __qmljs_cmp_ge(const Value left, const Value right, Context *ctx) { Value v = __qmljs_ge(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_le(Context *ctx, const Value left, const Value right) +inline Bool __qmljs_cmp_le(const Value left, const Value right, Context *ctx) { Value v = __qmljs_le(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_eq(Context *ctx, const Value left, const Value right) +inline Bool __qmljs_cmp_eq(const Value left, const Value right, Context *ctx) { Value v = __qmljs_eq(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_ne(Context *ctx, const Value left, const Value right) +inline Bool __qmljs_cmp_ne(const Value left, const Value right, Context *ctx) { Value v = __qmljs_ne(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_se(Context *ctx, const Value left, const Value right) +inline Bool __qmljs_cmp_se(const Value left, const Value right, Context *ctx) { return __qmljs_strict_equal(left, right, ctx); } -inline Bool __qmljs_cmp_sne(Context *ctx, const Value left, const Value right) +inline Bool __qmljs_cmp_sne(const Value left, const Value right, Context *ctx) { return ! __qmljs_strict_equal(left, right, ctx); } -inline Bool __qmljs_cmp_instanceof(Context *ctx, const Value left, const Value right) +inline Bool __qmljs_cmp_instanceof(const Value left, const Value right, Context *ctx) { Value v = __qmljs_instanceof(left, right, ctx); return v.booleanValue(); } -inline uint __qmljs_cmp_in(Context *ctx, const Value left, const Value right) +inline uint __qmljs_cmp_in(const Value left, const Value right, Context *ctx) { Value v = __qmljs_in(left, right, ctx); return v.booleanValue(); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index bed5317146..5cf6f5b21c 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -652,7 +652,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) IR::Temp *l = b->left->asTemp(); IR::Temp *r = b->right->asTemp(); if (l && r) { - Bool (*op)(Context *ctx, const Value, const Value) = 0; + Bool (*op)(const Value, const Value, Context *ctx) = 0; const char *opName = 0; switch (b->op) { default: Q_UNREACHABLE(); assert(!"todo"); break; @@ -668,7 +668,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) case IR::OpIn: setOp(op, opName, __qmljs_cmp_in); break; } // switch - generateFunctionCall2(ReturnValueRegister, op, ContextRegister, l, r); + generateFunctionCall2(ReturnValueRegister, op, l, r, ContextRegister); move(ReturnValueRegister, Gpr0); Jump target = branch32(NotEqual, Gpr0, TrustedImm32(0)); -- cgit v1.2.3 From 52a30a1e6f39e607747463303ec3bf7724736172 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 13:38:59 +0200 Subject: Fixed more calling conventions Implemented a few stubs for qmljs_inplace_op_member Change-Id: I542a535862339285db7ec0b547754453545a3dc0 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 135 +++++++++++++++++++++++++++++++++--------------------- qmljs_runtime.h | 46 +++++++++---------- qv4isel_masm.cpp | 8 ++-- 3 files changed, 109 insertions(+), 80 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 65ca1ae04c..0c4082f899 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -615,19 +615,19 @@ void __qmljs_inplace_ushr_name(const Value value, String *name, Context *ctx) ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_bit_and_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, Context *ctx) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_bit_or_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, Context *ctx) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); if (ArrayObject *a = obj->asArrayObject()) { - if (index->isNumber()) { - const quint32 idx = index->toUInt32(ctx); + if (index.isNumber()) { + const quint32 idx = index.toUInt32(ctx); Value v = a->value.at(idx); - v = __qmljs_bit_or(v, *value, ctx); + v = __qmljs_bit_or(v, value, ctx); a->value.assign(idx, v); return; } @@ -635,14 +635,14 @@ void __qmljs_inplace_bit_or_element(Context *ctx, Value *base, Value *index, Val ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, Context *ctx) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); if (ArrayObject *a = obj->asArrayObject()) { - if (index->isNumber()) { - const quint32 idx = index->toUInt32(ctx); + if (index.isNumber()) { + const quint32 idx = index.toUInt32(ctx); Value v = a->value.at(idx); - v = __qmljs_bit_xor(v, *value, ctx); + v = __qmljs_bit_xor(v, value, ctx); a->value.assign(idx, v); return; } @@ -650,14 +650,14 @@ void __qmljs_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, Va ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_add_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_inplace_add_element(Value base, Value index, Value value, Context *ctx) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); if (ArrayObject *a = obj->asArrayObject()) { - if (index->isNumber()) { - const quint32 idx = index->toUInt32(ctx); + if (index.isNumber()) { + const quint32 idx = index.toUInt32(ctx); Value v = a->value.at(idx); - v = __qmljs_add(v, *value, ctx); + v = __qmljs_add(v, value, ctx); a->value.assign(idx, v); return; } @@ -665,14 +665,14 @@ void __qmljs_inplace_add_element(Context *ctx, Value *base, Value *index, Value ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_sub_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_inplace_sub_element(Value base, Value index, Value value, Context *ctx) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); if (ArrayObject *a = obj->asArrayObject()) { - if (index->isNumber()) { - const quint32 idx = index->toUInt32(ctx); + if (index.isNumber()) { + const quint32 idx = index.toUInt32(ctx); Value v = a->value.at(idx); - v = __qmljs_sub(v, *value, ctx); + v = __qmljs_sub(v, value, ctx); a->value.assign(idx, v); return; } @@ -680,93 +680,122 @@ void __qmljs_inplace_sub_element(Context *ctx, Value *base, Value *index, Value ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_mul_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_inplace_mul_element(Value base, Value index, Value value, Context *ctx) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_div_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_inplace_div_element(Value base, Value index, Value value, Context *ctx) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_mod_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_inplace_mod_element(Value base, Value index, Value value, Context *ctx) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_shl_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_inplace_shl_element(Value base, Value index, Value value, Context *ctx) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_shr_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_inplace_shr_element(Value base, Value index, Value value, Context *ctx) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_ushr_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_inplace_ushr_element(Value base, Value index, Value value, Context *ctx) { ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -void __qmljs_inplace_bit_and_member(Context *ctx, Value *base, String *name, Value *value) +void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + Object *o = base.objectValue(); + Value prop = o->getProperty(ctx, name); + prop = __qmljs_bit_and(prop, value, ctx); + o->setProperty(ctx, name, prop); } -void __qmljs_inplace_bit_or_member(Context *ctx, Value *base, String *name, Value *value) +void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + Object *o = base.objectValue(); + Value prop = o->getProperty(ctx, name); + prop = __qmljs_bit_or(prop, value, ctx); + o->setProperty(ctx, name, prop); } -void __qmljs_inplace_bit_xor_member(Context *ctx, Value *base, String *name, Value *value) +void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + Object *o = base.objectValue(); + Value prop = o->getProperty(ctx, name); + prop = __qmljs_bit_xor(prop, value, ctx); + o->setProperty(ctx, name, prop); } -void __qmljs_inplace_add_member(Context *ctx, Value *base, String *name, Value *value) +void __qmljs_inplace_add_member(Value value, Value base, String *name, Context *ctx) { - Value prop = base->objectValue()->getProperty(ctx, name); - prop = __qmljs_add(prop, *value, ctx); - base->objectValue()->setProperty(ctx, name, prop); + Object *o = base.objectValue(); + Value prop = o->getProperty(ctx, name); + prop = __qmljs_add(prop, value, ctx); + o->setProperty(ctx, name, prop); } -void __qmljs_inplace_sub_member(Context *ctx, Value *base, String *name, Value *value) +void __qmljs_inplace_sub_member(Value value, Value base, String *name, Context *ctx) { - Value prop = base->objectValue()->getProperty(ctx, name); - prop = __qmljs_sub(prop, *value, ctx); - base->objectValue()->setProperty(ctx, name, prop); + Object *o = base.objectValue(); + Value prop = o->getProperty(ctx, name); + prop = __qmljs_sub(prop, value, ctx); + o->setProperty(ctx, name, prop); } -void __qmljs_inplace_mul_member(Context *ctx, Value *base, String *name, Value *value) +void __qmljs_inplace_mul_member(Value value, Value base, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + Object *o = base.objectValue(); + Value prop = o->getProperty(ctx, name); + prop = __qmljs_mul(prop, value, ctx); + o->setProperty(ctx, name, prop); } -void __qmljs_inplace_div_member(Context *ctx, Value *base, String *name, Value *value) +void __qmljs_inplace_div_member(Value value, Value base, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + Object *o = base.objectValue(); + Value prop = o->getProperty(ctx, name); + prop = __qmljs_div(prop, value, ctx); + o->setProperty(ctx, name, prop); } -void __qmljs_inplace_mod_member(Context *ctx, Value *base, String *name, Value *value) +void __qmljs_inplace_mod_member(Value value, Value base, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + Object *o = base.objectValue(); + Value prop = o->getProperty(ctx, name); + prop = __qmljs_mod(prop, value, ctx); + o->setProperty(ctx, name, prop); } -void __qmljs_inplace_shl_member(Context *ctx, Value *base, String *name, Value *value) +void __qmljs_inplace_shl_member(Value value, Value base, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + Object *o = base.objectValue(); + Value prop = o->getProperty(ctx, name); + prop = __qmljs_shl(prop, value, ctx); + o->setProperty(ctx, name, prop); } -void __qmljs_inplace_shr_member(Context *ctx, Value *base, String *name, Value *value) +void __qmljs_inplace_shr_member(Value value, Value base, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + Object *o = base.objectValue(); + Value prop = o->getProperty(ctx, name); + prop = __qmljs_shr(prop, value, ctx); + o->setProperty(ctx, name, prop); } -void __qmljs_inplace_ushr_member(Context *ctx, Value *base, String *name, Value *value) +void __qmljs_inplace_ushr_member(Value value, Value base, String *name, Context *ctx) { - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + Object *o = base.objectValue(); + Value prop = o->getProperty(ctx, name); + prop = __qmljs_ushr(prop, value, ctx); + o->setProperty(ctx, name, prop); } String *__qmljs_string_from_utf8(Context *ctx, const char *s) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index f1767a7c59..10f52e6a77 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -236,29 +236,29 @@ void __qmljs_inplace_shl_name(const Value value, String *name, Context *ctx); void __qmljs_inplace_shr_name(const Value value, String *name, Context *ctx); void __qmljs_inplace_ushr_name(const Value value, String *name, Context *ctx); -void __qmljs_inplace_bit_and_element(Context *ctx, Value *base, Value *index, Value *value); -void __qmljs_inplace_bit_or_element(Context *ctx, Value *base, Value *index, Value *value); -void __qmljs_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, Value *value); -void __qmljs_inplace_add_element(Context *ctx, Value *base, Value *index, Value *value); -void __qmljs_inplace_sub_element(Context *ctx, Value *base, Value *index, Value *value); -void __qmljs_inplace_mul_element(Context *ctx, Value *base, Value *index, Value *value); -void __qmljs_inplace_div_element(Context *ctx, Value *base, Value *index, Value *value); -void __qmljs_inplace_mod_element(Context *ctx, Value *base, Value *index, Value *value); -void __qmljs_inplace_shl_element(Context *ctx, Value *base, Value *index, Value *value); -void __qmljs_inplace_shr_element(Context *ctx, Value *base, Value *index, Value *value); -void __qmljs_inplace_ushr_element(Context *ctx, Value *base, Value *index, Value *value); - -void __qmljs_inplace_bit_and_member(Context *ctx, Value *base, String *name, Value *value); -void __qmljs_inplace_bit_or_member(Context *ctx, Value *base, String *name, Value *value); -void __qmljs_inplace_bit_xor_member(Context *ctx, Value *base, String *name, Value *value); -void __qmljs_inplace_add_member(Context *ctx, Value *base, String *name, Value *value); -void __qmljs_inplace_sub_member(Context *ctx, Value *base, String *name, Value *value); -void __qmljs_inplace_mul_member(Context *ctx, Value *base, String *name, Value *value); -void __qmljs_inplace_div_member(Context *ctx, Value *base, String *name, Value *value); -void __qmljs_inplace_mod_member(Context *ctx, Value *base, String *name, Value *value); -void __qmljs_inplace_shl_member(Context *ctx, Value *base, String *name, Value *value); -void __qmljs_inplace_shr_member(Context *ctx, Value *base, String *name, Value *value); -void __qmljs_inplace_ushr_member(Context *ctx, Value *base, String *name, Value *value); +void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, Context *ctx); +void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, Context *ctx); +void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, Context *ctx); +void __qmljs_inplace_add_element(Value base, Value index, Value value, Context *ctx); +void __qmljs_inplace_sub_element(Value base, Value index, Value value, Context *ctx); +void __qmljs_inplace_mul_element(Value base, Value index, Value value, Context *ctx); +void __qmljs_inplace_div_element(Value base, Value index, Value value, Context *ctx); +void __qmljs_inplace_mod_element(Value base, Value index, Value value, Context *ctx); +void __qmljs_inplace_shl_element(Value base, Value index, Value value, Context *ctx); +void __qmljs_inplace_shr_element(Value base, Value index, Value value, Context *ctx); +void __qmljs_inplace_ushr_element(Value base, Value index, Value value, Context *ctx); + +void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, Context *ctx); +void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, Context *ctx); +void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, Context *ctx); +void __qmljs_inplace_add_member(Value value, Value base, String *name, Context *ctx); +void __qmljs_inplace_sub_member(Value value, Value base, String *name, Context *ctx); +void __qmljs_inplace_mul_member(Value value, Value base, String *name, Context *ctx); +void __qmljs_inplace_div_member(Value value, Value base, String *name, Context *ctx); +void __qmljs_inplace_mod_member(Value value, Value base, String *name, Context *ctx); +void __qmljs_inplace_shl_member(Value value, Value base, String *name, Context *ctx); +void __qmljs_inplace_shr_member(Value value, Value base, String *name, Context *ctx); +void __qmljs_inplace_ushr_member(Value value, Value base, String *name, Context *ctx); Bool __qmljs_cmp_gt(const Value left, const Value right, Context *ctx); Bool __qmljs_cmp_lt(const Value left, const Value right, Context *ctx); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 5cf6f5b21c..bd82f1ea0b 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -548,7 +548,7 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Subscript *ss = s->target->asSubscript()) { if (IR::Temp *t = s->source->asTemp()) { - void (*op)(Context *ctx, Value *base, Value *index, Value *value) = 0; + void (*op)(Value base, Value index, Value value, Context *ctx) = 0; const char *opName = 0; switch (s->op) { case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; @@ -570,14 +570,14 @@ void InstructionSelection::visitMove(IR::Move *s) if (op) { IR::Temp* base = ss->base->asTemp(); IR::Temp* index = ss->index->asTemp(); - generateFunctionCallImp(opName, op, ContextRegister, base, index, t); + generateFunctionCallImp2(Void, opName, op, base, index, t, ContextRegister); checkExceptions(); } return; } } else if (IR::Member *m = s->target->asMember()) { if (IR::Temp *t = s->source->asTemp()) { - void (*op)(Context *ctx, Value *base, String *name, Value *value) = 0; + void (*op)(Value value, Value base, String *name, Context *ctx) = 0; const char *opName = 0; switch (s->op) { case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; @@ -599,7 +599,7 @@ void InstructionSelection::visitMove(IR::Move *s) if (op) { IR::Temp* base = m->base->asTemp(); String* member = identifier(*m->name); - generateFunctionCallImp(opName, op, ContextRegister, base, member, t); + generateFunctionCallImp2(Void, opName, op, t, base, member, ContextRegister); checkExceptions(); } return; -- cgit v1.2.3 From c85ac2d9cfd630747ebf042e706b9b9fbce529dd Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 13:43:33 +0200 Subject: Pass Value, not 'const Value' Passing const values doesn't make a whole lot of sense, esp. when the passed object is POD. Change-Id: I7372407862828cf7fa8461583069743ecf583170 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 50 +++++------ qmljs_runtime.h | 242 +++++++++++++++++++++++++++--------------------------- 2 files changed, 146 insertions(+), 146 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 0c4082f899..d5843f79fe 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -298,7 +298,7 @@ Value *Context::lookupPropertyDescriptor(String *name) return 0; } -void Context::throwError(const Value &value) +void Context::throwError(Value value) { result = value; hasUncaughtException = true; @@ -322,7 +322,7 @@ void Context::throwUnimplemented(const QString &message) throwError(Value::fromObject(engine->newErrorObject(v))); } -void Context::throwReferenceError(const Value &value) +void Context::throwReferenceError(Value value) { String *s = value.toString(this); QString msg = s->toQString() + QStringLiteral(" is not defined"); @@ -373,7 +373,7 @@ void Context::leaveCallContext(FunctionObject *f) } } -void Context::initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc) +void Context::initConstructorContext(ExecutionEngine *e, Value *object, FunctionObject *f, Value *args, unsigned argc) { initCallContext(e, object, f, args, argc); calledAsConstructor = true; @@ -489,7 +489,7 @@ Value __qmljs_delete_value(Context *ctx, Value value) return __qmljs_throw_type_error(ctx); // ### throw syntax error } -Value __qmljs_add_helper(const Value left, const Value right, Context *ctx) +Value __qmljs_add_helper(Value left, Value right, Context *ctx) { Value pleft = __qmljs_to_primitive(left, ctx, PREFERREDTYPE_HINT); Value pright = __qmljs_to_primitive(right, ctx, PREFERREDTYPE_HINT); @@ -506,7 +506,7 @@ Value __qmljs_add_helper(const Value left, const Value right, Context *ctx) return Value::fromDouble(x + y); } -Value __qmljs_instanceof(const Value left, const Value right, Context *ctx) +Value __qmljs_instanceof(Value left, Value right, Context *ctx) { if (FunctionObject *function = right.asFunctionObject()) { bool r = function->hasInstance(ctx, left); @@ -516,7 +516,7 @@ Value __qmljs_instanceof(const Value left, const Value right, Context *ctx) return __qmljs_throw_type_error(ctx); } -Value __qmljs_in(const Value left, const Value right, Context *ctx) +Value __qmljs_in(Value left, Value right, Context *ctx) { if (right.isObject()) { Value s = __qmljs_to_string(left, ctx); @@ -527,7 +527,7 @@ Value __qmljs_in(const Value left, const Value right, Context *ctx) } } -void __qmljs_inplace_bit_and_name(const Value value, String *name, Context *ctx) +void __qmljs_inplace_bit_and_name(Value value, String *name, Context *ctx) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) *prop = __qmljs_bit_and(*prop, value, ctx); @@ -535,7 +535,7 @@ void __qmljs_inplace_bit_and_name(const Value value, String *name, Context *ctx) ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_bit_or_name(const Value value, String *name, Context *ctx) +void __qmljs_inplace_bit_or_name(Value value, String *name, Context *ctx) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) *prop = __qmljs_bit_or(*prop, value, ctx); @@ -543,7 +543,7 @@ void __qmljs_inplace_bit_or_name(const Value value, String *name, Context *ctx) ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_bit_xor_name(const Value value, String *name, Context *ctx) +void __qmljs_inplace_bit_xor_name(Value value, String *name, Context *ctx) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) *prop = __qmljs_bit_xor(*prop, value, ctx); @@ -551,7 +551,7 @@ void __qmljs_inplace_bit_xor_name(const Value value, String *name, Context *ctx) ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_add_name(const Value value, String *name, Context *ctx) +void __qmljs_inplace_add_name(Value value, String *name, Context *ctx) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) *prop = __qmljs_add(*prop, value, ctx); @@ -559,7 +559,7 @@ void __qmljs_inplace_add_name(const Value value, String *name, Context *ctx) ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_sub_name(const Value value, String *name, Context *ctx) +void __qmljs_inplace_sub_name(Value value, String *name, Context *ctx) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) *prop = __qmljs_sub(*prop, value, ctx); @@ -567,7 +567,7 @@ void __qmljs_inplace_sub_name(const Value value, String *name, Context *ctx) ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_mul_name(const Value value, String *name, Context *ctx) +void __qmljs_inplace_mul_name(Value value, String *name, Context *ctx) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) *prop = __qmljs_mul(*prop, value, ctx); @@ -575,7 +575,7 @@ void __qmljs_inplace_mul_name(const Value value, String *name, Context *ctx) ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_div_name(const Value value, String *name, Context *ctx) +void __qmljs_inplace_div_name(Value value, String *name, Context *ctx) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) *prop = __qmljs_div(*prop, value, ctx); @@ -583,7 +583,7 @@ void __qmljs_inplace_div_name(const Value value, String *name, Context *ctx) ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_mod_name(const Value value, String *name, Context *ctx) +void __qmljs_inplace_mod_name(Value value, String *name, Context *ctx) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) *prop = __qmljs_mod(*prop, value, ctx); @@ -591,7 +591,7 @@ void __qmljs_inplace_mod_name(const Value value, String *name, Context *ctx) ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_shl_name(const Value value, String *name, Context *ctx) +void __qmljs_inplace_shl_name(Value value, String *name, Context *ctx) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) *prop = __qmljs_shl(*prop, value, ctx); @@ -599,7 +599,7 @@ void __qmljs_inplace_shl_name(const Value value, String *name, Context *ctx) ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_shr_name(const Value value, String *name, Context *ctx) +void __qmljs_inplace_shr_name(Value value, String *name, Context *ctx) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) *prop = __qmljs_shr(*prop, value, ctx); @@ -607,7 +607,7 @@ void __qmljs_inplace_shr_name(const Value value, String *name, Context *ctx) ctx->throwReferenceError(Value::fromString(name)); } -void __qmljs_inplace_ushr_name(const Value value, String *name, Context *ctx) +void __qmljs_inplace_ushr_name(Value value, String *name, Context *ctx) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) *prop = __qmljs_ushr(*prop, value, ctx); @@ -843,12 +843,12 @@ String *__qmljs_string_concat(Context *ctx, String *first, String *second) return ctx->engine->newString(first->toQString() + second->toQString()); } -Bool __qmljs_is_function(const Value value) +Bool __qmljs_is_function(Value value) { return value.objectValue()->asFunctionObject() != 0; } -Value __qmljs_object_default_value(Context *ctx, const Value object, int typeHint) +Value __qmljs_object_default_value(Context *ctx, Value object, int typeHint) { if (typeHint == PREFERREDTYPE_HINT) { if (object.isDateObject()) @@ -1048,7 +1048,7 @@ Value __qmljs_get_thisObject(Context *ctx) return ctx->engine->globalObject; } -Value __qmljs_compare(const Value x, const Value y, Context *ctx, bool leftFirst) +Value __qmljs_compare(Value x, Value y, Context *ctx, bool leftFirst) { Value px, py; @@ -1074,7 +1074,7 @@ Value __qmljs_compare(const Value x, const Value y, Context *ctx, bool leftFirst } } -uint __qmljs_equal(const Value x, const Value y, Context *ctx) +uint __qmljs_equal(Value x, Value y, Context *ctx) { if (x.type() == y.type()) { switch (x.type()) { @@ -1137,7 +1137,7 @@ Value __qmljs_call_activation_property(Context *context, String *name, Value *ar return result; } -Value __qmljs_call_property(Context *context, const Value base, String *name, Value *args, int argc) +Value __qmljs_call_property(Context *context, Value base, String *name, Value *args, int argc) { Value baseObject; Value thisObject; @@ -1170,7 +1170,7 @@ Value __qmljs_call_property(Context *context, const Value base, String *name, Va return result; } -Value __qmljs_call_value(Context *context, const Value thisObject, const Value func, Value *args, int argc) +Value __qmljs_call_value(Context *context, Value thisObject, Value func, Value *args, int argc) { Value result; if (FunctionObject *f = func.asFunctionObject()) { @@ -1201,7 +1201,7 @@ Value __qmljs_construct_activation_property(Context *context, String *name, Valu return __qmljs_construct_value(context, *func, args, argc); } -Value __qmljs_construct_value(Context *context, const Value func, Value *args, int argc) +Value __qmljs_construct_value(Context *context, Value func, Value *args, int argc) { if (FunctionObject *f = func.asFunctionObject()) { Context k; @@ -1219,7 +1219,7 @@ Value __qmljs_construct_value(Context *context, const Value func, Value *args, i return Value::undefinedValue(); } -Value __qmljs_construct_property(Context *context, const Value base, String *name, Value *args, int argc) +Value __qmljs_construct_property(Context *context, Value base, String *name, Value *args, int argc) { Value thisObject = base; if (!thisObject.isObject()) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 10f52e6a77..cbb78e35ed 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -92,12 +92,12 @@ extern "C" { // context Value __qmljs_call_activation_property(Context *, String *name, Value *args, int argc); -Value __qmljs_call_property(Context *context, const Value base, String *name, Value *args, int argc); -Value __qmljs_call_value(Context *context, const Value thisObject, const Value func, Value *args, int argc); +Value __qmljs_call_property(Context *context, Value base, String *name, Value *args, int argc); +Value __qmljs_call_value(Context *context, Value thisObject, Value func, Value *args, int argc); Value __qmljs_construct_activation_property(Context *, String *name, Value *args, int argc); -Value __qmljs_construct_property(Context *context, const Value base, String *name, Value *args, int argc); -Value __qmljs_construct_value(Context *context, const Value func, Value *args, int argc); +Value __qmljs_construct_property(Context *context, Value base, String *name, Value *args, int argc); +Value __qmljs_construct_value(Context *context, Value func, Value *args, int argc); Value __qmljs_builtin_typeof(Context *context, Value *args, int argc); Value __qmljs_builtin_throw(Context *context, Value *args, int argc); @@ -109,7 +109,7 @@ Value __qmljs_init_object(Object *object); Value __qmljs_init_closure(IR::Function *clos, Context *ctx); Value __qmljs_init_native_function(void (*code)(Context *), Context *ctx); -Bool __qmljs_is_function(const Value value); +Bool __qmljs_is_function(Value value); // string literals Value __qmljs_string_literal_undefined(Context *ctx); @@ -133,7 +133,7 @@ String *__qmljs_string_concat(Context *ctx, String *first, String *second); String *__qmljs_identifier_from_utf8(Context *ctx, const char *s); // objects -Value __qmljs_object_default_value(Context *ctx, const Value object, int typeHint); +Value __qmljs_object_default_value(Context *ctx, Value object, int typeHint); Value __qmljs_throw_type_error(Context *ctx); Value __qmljs_new_object(Context *ctx); Value __qmljs_new_boolean_object(Context *ctx, bool boolean); @@ -151,28 +151,28 @@ void __qmljs_set_element(Context *ctx, Value object, Value index, Value value); Value __qmljs_get_thisObject(Context *ctx); // type conversion and testing -Value __qmljs_to_primitive(const Value value, Context *ctx, int typeHint); -Bool __qmljs_to_boolean(const Value value, Context *ctx); -double __qmljs_to_number(const Value value, Context *ctx); -double __qmljs_to_integer(const Value value, Context *ctx); -int __qmljs_to_int32(const Value value, Context *ctx); -unsigned __qmljs_to_uint32(const Value value, Context *ctx); -unsigned short __qmljs_to_uint16(const Value value, Context *ctx); -Value __qmljs_to_string(const Value value, Context *ctx); -Value __qmljs_to_object(const Value value, Context *ctx); -//uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value); -Bool __qmljs_is_callable(const Value value, Context *ctx); -Value __qmljs_default_value(const Value value, Context *ctx, int typeHint); - -Value __qmljs_compare(const Value left, const Value right, Context *ctx, bool leftFlag); -Bool __qmljs_equal(const Value x, const Value y, Context *ctx); -Bool __qmljs_strict_equal(const Value x, const Value y, Context *ctx); +Value __qmljs_to_primitive(Value value, Context *ctx, int typeHint); +Bool __qmljs_to_boolean(Value value, Context *ctx); +double __qmljs_to_number(Value value, Context *ctx); +double __qmljs_to_integer(Value value, Context *ctx); +int __qmljs_to_int32(Value value, Context *ctx); +unsigned __qmljs_to_uint32(Value value, Context *ctx); +unsigned short __qmljs_to_uint16(Value value, Context *ctx); +Value __qmljs_to_string(Value value, Context *ctx); +Value __qmljs_to_object(Value value, Context *ctx); +//uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *value); +Bool __qmljs_is_callable(Value value, Context *ctx); +Value __qmljs_default_value(Value value, Context *ctx, int typeHint); + +Value __qmljs_compare(Value left, Value right, Context *ctx, bool leftFlag); +Bool __qmljs_equal(Value x, Value y, Context *ctx); +Bool __qmljs_strict_equal(Value x, Value y, Context *ctx); // unary operators -Value __qmljs_uplus(const Value value, Context *ctx); -Value __qmljs_uminus(const Value value, Context *ctx); -Value __qmljs_compl(const Value value, Context *ctx); -Value __qmljs_not(const Value value, Context *ctx); +Value __qmljs_uplus(Value value, Context *ctx); +Value __qmljs_uminus(Value value, Context *ctx); +Value __qmljs_compl(Value value, Context *ctx); +Value __qmljs_not(Value value, Context *ctx); /* ### these 4 methods are apparently unused right now */ Value __qmljs_delete_subscript(Context *ctx, Value base, Value index); @@ -180,34 +180,34 @@ Value __qmljs_delete_member(Context *ctx, Value base, String *name); Value __qmljs_delete_property(Context *ctx, String *name); Value __qmljs_delete_value(Context *ctx, Value value); -Value __qmljs_typeof(const Value value, Context *ctx); +Value __qmljs_typeof(Value value, Context *ctx); void __qmljs_throw(Value value, Context *context); Value __qmljs_rethrow(Context *context); // binary operators -Value __qmljs_instanceof(const Value left, const Value right, Context *ctx); -Value __qmljs_in(const Value left, const Value right, Context *ctx); -Value __qmljs_bit_or(const Value left, const Value right, Context *ctx); -Value __qmljs_bit_xor(const Value left, const Value right, Context *ctx); -Value __qmljs_bit_and(const Value left, const Value right, Context *ctx); -Value __qmljs_add(const Value left, const Value right, Context *ctx); -Value __qmljs_sub(const Value left, const Value right, Context *ctx); -Value __qmljs_mul(const Value left, const Value right, Context *ctx); -Value __qmljs_div(const Value left, const Value right, Context *ctx); -Value __qmljs_mod(const Value left, const Value right, Context *ctx); -Value __qmljs_shl(const Value left, const Value right, Context *ctx); -Value __qmljs_shr(const Value left, const Value right, Context *ctx); -Value __qmljs_ushr(const Value left, const Value right, Context *ctx); -Value __qmljs_gt(const Value left, const Value right, Context *ctx); -Value __qmljs_lt(const Value left, const Value right, Context *ctx); -Value __qmljs_ge(const Value left, const Value right, Context *ctx); -Value __qmljs_le(const Value left, const Value right, Context *ctx); -Value __qmljs_eq(const Value left, const Value right, Context *ctx); -Value __qmljs_ne(const Value left, const Value right, Context *ctx); -Value __qmljs_se(const Value left, const Value right, Context *ctx); -Value __qmljs_sne(const Value left, const Value right, Context *ctx); - -Value __qmljs_add_helper(const Value left, const Value right, Context *ctx); +Value __qmljs_instanceof(Value left, Value right, Context *ctx); +Value __qmljs_in(Value left, Value right, Context *ctx); +Value __qmljs_bit_or(Value left, Value right, Context *ctx); +Value __qmljs_bit_xor(Value left, Value right, Context *ctx); +Value __qmljs_bit_and(Value left, Value right, Context *ctx); +Value __qmljs_add(Value left, Value right, Context *ctx); +Value __qmljs_sub(Value left, Value right, Context *ctx); +Value __qmljs_mul(Value left, Value right, Context *ctx); +Value __qmljs_div(Value left, Value right, Context *ctx); +Value __qmljs_mod(Value left, Value right, Context *ctx); +Value __qmljs_shl(Value left, Value right, Context *ctx); +Value __qmljs_shr(Value left, Value right, Context *ctx); +Value __qmljs_ushr(Value left, Value right, Context *ctx); +Value __qmljs_gt(Value left, Value right, Context *ctx); +Value __qmljs_lt(Value left, Value right, Context *ctx); +Value __qmljs_ge(Value left, Value right, Context *ctx); +Value __qmljs_le(Value left, Value right, Context *ctx); +Value __qmljs_eq(Value left, Value right, Context *ctx); +Value __qmljs_ne(Value left, Value right, Context *ctx); +Value __qmljs_se(Value left, Value right, Context *ctx); +Value __qmljs_sne(Value left, Value right, Context *ctx); + +Value __qmljs_add_helper(Value left, Value right, Context *ctx); /* unused and probably don't make sense with the new calling convention @@ -224,17 +224,17 @@ void __qmljs_inplace_shr(Context *ctx, Value *result, Value *value); void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value); */ -void __qmljs_inplace_bit_and_name(const Value value, String *name, Context *ctx); -void __qmljs_inplace_bit_or_name(const Value value, String *name, Context *ctx); -void __qmljs_inplace_bit_xor_name(const Value value, String *name, Context *ctx); -void __qmljs_inplace_add_name(const Value value, String *name, Context *ctx); -void __qmljs_inplace_sub_name(const Value value, String *name, Context *ctx); -void __qmljs_inplace_mul_name(const Value value, String *name, Context *ctx); -void __qmljs_inplace_div_name(const Value value, String *name, Context *ctx); -void __qmljs_inplace_mod_name(const Value value, String *name, Context *ctx); -void __qmljs_inplace_shl_name(const Value value, String *name, Context *ctx); -void __qmljs_inplace_shr_name(const Value value, String *name, Context *ctx); -void __qmljs_inplace_ushr_name(const Value value, String *name, Context *ctx); +void __qmljs_inplace_bit_and_name(Value value, String *name, Context *ctx); +void __qmljs_inplace_bit_or_name(Value value, String *name, Context *ctx); +void __qmljs_inplace_bit_xor_name(Value value, String *name, Context *ctx); +void __qmljs_inplace_add_name(Value value, String *name, Context *ctx); +void __qmljs_inplace_sub_name(Value value, String *name, Context *ctx); +void __qmljs_inplace_mul_name(Value value, String *name, Context *ctx); +void __qmljs_inplace_div_name(Value value, String *name, Context *ctx); +void __qmljs_inplace_mod_name(Value value, String *name, Context *ctx); +void __qmljs_inplace_shl_name(Value value, String *name, Context *ctx); +void __qmljs_inplace_shr_name(Value value, String *name, Context *ctx); +void __qmljs_inplace_ushr_name(Value value, String *name, Context *ctx); void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, Context *ctx); void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, Context *ctx); @@ -260,16 +260,16 @@ void __qmljs_inplace_shl_member(Value value, Value base, String *name, Context * void __qmljs_inplace_shr_member(Value value, Value base, String *name, Context *ctx); void __qmljs_inplace_ushr_member(Value value, Value base, String *name, Context *ctx); -Bool __qmljs_cmp_gt(const Value left, const Value right, Context *ctx); -Bool __qmljs_cmp_lt(const Value left, const Value right, Context *ctx); -Bool __qmljs_cmp_ge(const Value left, const Value right, Context *ctx); -Bool __qmljs_cmp_le(const Value left, const Value right, Context *ctx); -Bool __qmljs_cmp_eq(const Value left, const Value right, Context *ctx); -Bool __qmljs_cmp_ne(const Value left, const Value right, Context *ctx); -Bool __qmljs_cmp_se(const Value left, const Value right, Context *ctx); -Bool __qmljs_cmp_sne(const Value left, const Value right, Context *ctx); -Bool __qmljs_cmp_instanceof(const Value left, const Value right, Context *ctx); -Bool __qmljs_cmp_in(const Value left, const Value right, Context *ctx); +Bool __qmljs_cmp_gt(Value left, Value right, Context *ctx); +Bool __qmljs_cmp_lt(Value left, Value right, Context *ctx); +Bool __qmljs_cmp_ge(Value left, Value right, Context *ctx); +Bool __qmljs_cmp_le(Value left, Value right, Context *ctx); +Bool __qmljs_cmp_eq(Value left, Value right, Context *ctx); +Bool __qmljs_cmp_ne(Value left, Value right, Context *ctx); +Bool __qmljs_cmp_se(Value left, Value right, Context *ctx); +Bool __qmljs_cmp_sne(Value left, Value right, Context *ctx); +Bool __qmljs_cmp_instanceof(Value left, Value right, Context *ctx); +Bool __qmljs_cmp_in(Value left, Value right, Context *ctx); } // extern "C" @@ -647,9 +647,9 @@ struct Context { void init(ExecutionEngine *eng); - void throwError(const Value &value); + void throwError(Value value); void throwTypeError(); - void throwReferenceError(const Value &value); + void throwReferenceError(Value value); #ifndef QMLJS_LLVM_RUNTIME void throwError(const QString &message); @@ -659,7 +659,7 @@ struct Context { void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc); void leaveCallContext(FunctionObject *f); - void initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc); + void initConstructorContext(ExecutionEngine *e, Value *object, FunctionObject *f, Value *args, unsigned argc); void leaveConstructorContext(FunctionObject *f); }; @@ -680,14 +680,14 @@ inline Value __qmljs_init_object(Object *object) } // type conversion and testing -inline Value __qmljs_to_primitive(const Value value, Context *ctx, int typeHint) +inline Value __qmljs_to_primitive(Value value, Context *ctx, int typeHint) { if (!value.isObject()) return value; return __qmljs_default_value(value, ctx, typeHint); } -inline Bool __qmljs_to_boolean(const Value value, Context *ctx) +inline Bool __qmljs_to_boolean(Value value, Context *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -707,7 +707,7 @@ inline Bool __qmljs_to_boolean(const Value value, Context *ctx) } } -inline double __qmljs_to_number(const Value value, Context *ctx) +inline double __qmljs_to_number(Value value, Context *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -729,7 +729,7 @@ inline double __qmljs_to_number(const Value value, Context *ctx) } } -inline double __qmljs_to_integer(const Value value, Context *ctx) +inline double __qmljs_to_integer(Value value, Context *ctx) { if (value.isInteger()) return value.int_32; @@ -742,7 +742,7 @@ inline double __qmljs_to_integer(const Value value, Context *ctx) return std::signbit(number) ? -v : v; } -inline int __qmljs_to_int32(const Value value, Context *ctx) +inline int __qmljs_to_int32(Value value, Context *ctx) { if (value.isInteger()) return value.int_32; @@ -771,7 +771,7 @@ inline int __qmljs_to_int32(const Value value, Context *ctx) return int(number); } -inline unsigned __qmljs_to_uint32(const Value value, Context *ctx) +inline unsigned __qmljs_to_uint32(Value value, Context *ctx) { if (value.isInteger()) return (unsigned) value.int_32; @@ -792,7 +792,7 @@ inline unsigned __qmljs_to_uint32(const Value value, Context *ctx) return unsigned(number); } -inline unsigned short __qmljs_to_uint16(const Value value, Context *ctx) +inline unsigned short __qmljs_to_uint16(Value value, Context *ctx) { double number = __qmljs_to_number(value, ctx); if (! number || qIsNaN(number) || std::isinf(number)) @@ -810,7 +810,7 @@ inline unsigned short __qmljs_to_uint16(const Value value, Context *ctx) return (unsigned short)number; } -inline Value __qmljs_to_string(const Value value, Context *ctx) +inline Value __qmljs_to_string(Value value, Context *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -846,7 +846,7 @@ inline Value __qmljs_to_string(const Value value, Context *ctx) } // switch } -inline Value __qmljs_to_object(const Value value, Context *ctx) +inline Value __qmljs_to_object(Value value, Context *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -872,7 +872,7 @@ inline Value __qmljs_to_object(const Value value, Context *ctx) } /* -inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value) +inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *value) { switch (value->type()) { case Value::Undefined_Type: @@ -885,7 +885,7 @@ inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Va } */ -inline Bool __qmljs_is_callable(const Value value, Context *ctx) +inline Bool __qmljs_is_callable(Value value, Context *ctx) { if (value.isObject()) return __qmljs_is_function(value); @@ -893,7 +893,7 @@ inline Bool __qmljs_is_callable(const Value value, Context *ctx) return false; } -inline Value __qmljs_default_value(const Value value, Context *ctx, int typeHint) +inline Value __qmljs_default_value(Value value, Context *ctx, int typeHint) { if (value.isObject()) return __qmljs_object_default_value(ctx, value, typeHint); @@ -902,7 +902,7 @@ inline Value __qmljs_default_value(const Value value, Context *ctx, int typeHint // unary operators -inline Value __qmljs_typeof(const Value value, Context *ctx) +inline Value __qmljs_typeof(Value value, Context *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -929,7 +929,7 @@ inline Value __qmljs_typeof(const Value value, Context *ctx) } } -inline Value __qmljs_uplus(const Value value, Context *ctx) +inline Value __qmljs_uplus(Value value, Context *ctx) { if (value.isInteger()) return value; @@ -937,7 +937,7 @@ inline Value __qmljs_uplus(const Value value, Context *ctx) return Value::fromDouble(n); } -inline Value __qmljs_uminus(const Value value, Context *ctx) +inline Value __qmljs_uminus(Value value, Context *ctx) { if (value.isInteger()) return Value::fromInt32(-value.integerValue()); @@ -945,20 +945,20 @@ inline Value __qmljs_uminus(const Value value, Context *ctx) return Value::fromDouble(-n); } -inline Value __qmljs_compl(const Value value, Context *ctx) +inline Value __qmljs_compl(Value value, Context *ctx) { int n = __qmljs_to_int32(value, ctx); return Value::fromInt32(~n); } -inline Value __qmljs_not(const Value value, Context *ctx) +inline Value __qmljs_not(Value value, Context *ctx) { bool b = __qmljs_to_boolean(value, ctx); return Value::fromBoolean(!b); } // binary operators -inline Value __qmljs_bit_or(const Value left, const Value right, Context *ctx) +inline Value __qmljs_bit_or(Value left, Value right, Context *ctx) { int lval = __qmljs_to_int32(left, ctx); int rval = __qmljs_to_int32(right, ctx); @@ -966,14 +966,14 @@ inline Value __qmljs_bit_or(const Value left, const Value right, Context *ctx) return Value::fromDouble(lval | rval); } -inline Value __qmljs_bit_xor(const Value left, const Value right, Context *ctx) +inline Value __qmljs_bit_xor(Value left, Value right, Context *ctx) { int lval = __qmljs_to_int32(left, ctx); int rval = __qmljs_to_int32(right, ctx); return Value::fromInt32(lval ^ rval); } -inline Value __qmljs_bit_and(const Value left, const Value right, Context *ctx) +inline Value __qmljs_bit_and(Value left, Value right, Context *ctx) { int lval = __qmljs_to_int32(left, ctx); int rval = __qmljs_to_int32(right, ctx); @@ -1037,7 +1037,7 @@ inline void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value) } */ -inline Value __qmljs_add(const Value left, const Value right, Context *ctx) +inline Value __qmljs_add(Value left, Value right, Context *ctx) { if (left.isInteger() && right.isInteger()) return add_int32(left.integerValue(), right.integerValue()); @@ -1048,7 +1048,7 @@ inline Value __qmljs_add(const Value left, const Value right, Context *ctx) return __qmljs_add_helper(left, right, ctx); } -inline Value __qmljs_sub(const Value left, const Value right, Context *ctx) +inline Value __qmljs_sub(Value left, Value right, Context *ctx) { if (left.isInteger() && right.isInteger()) return sub_int32(left.integerValue(), right.integerValue()); @@ -1058,7 +1058,7 @@ inline Value __qmljs_sub(const Value left, const Value right, Context *ctx) return Value::fromDouble(lval - rval); } -inline Value __qmljs_mul(const Value left, const Value right, Context *ctx) +inline Value __qmljs_mul(Value left, Value right, Context *ctx) { if (left.isInteger() && right.isInteger()) return mul_int32(left.integerValue(), right.integerValue()); @@ -1068,14 +1068,14 @@ inline Value __qmljs_mul(const Value left, const Value right, Context *ctx) return Value::fromDouble(lval * rval); } -inline Value __qmljs_div(const Value left, const Value right, Context *ctx) +inline Value __qmljs_div(Value left, Value right, Context *ctx) { double lval = __qmljs_to_number(left, ctx); double rval = __qmljs_to_number(right, ctx); return Value::fromDouble(lval / rval); } -inline Value __qmljs_mod(const Value left, const Value right, Context *ctx) +inline Value __qmljs_mod(Value left, Value right, Context *ctx) { double lval = __qmljs_to_number(left, ctx); double rval = __qmljs_to_number(right, ctx); @@ -1084,28 +1084,28 @@ inline Value __qmljs_mod(const Value left, const Value right, Context *ctx) // ### unsigned shl missing? -inline Value __qmljs_shl(const Value left, const Value right, Context *ctx) +inline Value __qmljs_shl(Value left, Value right, Context *ctx) { int lval = __qmljs_to_int32(left, ctx); unsigned rval = __qmljs_to_uint32(right, ctx); return Value::fromInt32(lval << rval); } -inline Value __qmljs_shr(const Value left, const Value right, Context *ctx) +inline Value __qmljs_shr(Value left, Value right, Context *ctx) { int lval = __qmljs_to_int32(left, ctx); unsigned rval = __qmljs_to_uint32(right, ctx); return Value::fromInt32(lval >> rval); } -inline Value __qmljs_ushr(const Value left, const Value right, Context *ctx) +inline Value __qmljs_ushr(Value left, Value right, Context *ctx) { unsigned lval = __qmljs_to_uint32(left, ctx); unsigned rval = __qmljs_to_uint32(right, ctx); return Value::fromInt32(lval >> rval); } -inline Value __qmljs_gt(const Value left, const Value right, Context *ctx) +inline Value __qmljs_gt(Value left, Value right, Context *ctx) { if (left.isInteger() && right.isInteger()) return Value::fromBoolean(left.integerValue() > right.integerValue()); @@ -1120,7 +1120,7 @@ inline Value __qmljs_gt(const Value left, const Value right, Context *ctx) } } -inline Value __qmljs_lt(const Value left, const Value right, Context *ctx) +inline Value __qmljs_lt(Value left, Value right, Context *ctx) { if (left.isInteger() && right.isInteger()) return Value::fromBoolean(left.integerValue() < right.integerValue()); @@ -1135,7 +1135,7 @@ inline Value __qmljs_lt(const Value left, const Value right, Context *ctx) } } -inline Value __qmljs_ge(const Value left, const Value right, Context *ctx) +inline Value __qmljs_ge(Value left, Value right, Context *ctx) { if (left.isInteger() && right.isInteger()) return Value::fromBoolean(left.integerValue() >= right.integerValue()); @@ -1149,7 +1149,7 @@ inline Value __qmljs_ge(const Value left, const Value right, Context *ctx) } } -inline Value __qmljs_le(const Value left, const Value right, Context *ctx) +inline Value __qmljs_le(Value left, Value right, Context *ctx) { if (left.isInteger() && right.isInteger()) return Value::fromBoolean(left.integerValue() <= right.integerValue()); @@ -1163,7 +1163,7 @@ inline Value __qmljs_le(const Value left, const Value right, Context *ctx) } } -inline Value __qmljs_eq(const Value left, const Value right, Context *ctx) +inline Value __qmljs_eq(Value left, Value right, Context *ctx) { if (left.val == right.val) return Value::fromBoolean(true); @@ -1177,84 +1177,84 @@ inline Value __qmljs_eq(const Value left, const Value right, Context *ctx) } } -inline Value __qmljs_ne(const Value left, const Value right, Context *ctx) +inline Value __qmljs_ne(Value left, Value right, Context *ctx) { Value result = __qmljs_eq(left, right, ctx); result.int_32 = !(bool)result.int_32; return result; } -inline Value __qmljs_se(const Value left, const Value right, Context *ctx) +inline Value __qmljs_se(Value left, Value right, Context *ctx) { bool r = __qmljs_strict_equal(left, right, ctx); return Value::fromBoolean(r); } -inline Value __qmljs_sne(const Value left, const Value right, Context *ctx) +inline Value __qmljs_sne(Value left, Value right, Context *ctx) { bool r = ! __qmljs_strict_equal(left, right, ctx); return Value::fromBoolean(r); } -inline Bool __qmljs_cmp_gt(const Value left, const Value right, Context *ctx) +inline Bool __qmljs_cmp_gt(Value left, Value right, Context *ctx) { Value v = __qmljs_gt(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_lt(const Value left, const Value right, Context *ctx) +inline Bool __qmljs_cmp_lt(Value left, Value right, Context *ctx) { Value v = __qmljs_lt(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_ge(const Value left, const Value right, Context *ctx) +inline Bool __qmljs_cmp_ge(Value left, Value right, Context *ctx) { Value v = __qmljs_ge(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_le(const Value left, const Value right, Context *ctx) +inline Bool __qmljs_cmp_le(Value left, Value right, Context *ctx) { Value v = __qmljs_le(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_eq(const Value left, const Value right, Context *ctx) +inline Bool __qmljs_cmp_eq(Value left, Value right, Context *ctx) { Value v = __qmljs_eq(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_ne(const Value left, const Value right, Context *ctx) +inline Bool __qmljs_cmp_ne(Value left, Value right, Context *ctx) { Value v = __qmljs_ne(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_cmp_se(const Value left, const Value right, Context *ctx) +inline Bool __qmljs_cmp_se(Value left, Value right, Context *ctx) { return __qmljs_strict_equal(left, right, ctx); } -inline Bool __qmljs_cmp_sne(const Value left, const Value right, Context *ctx) +inline Bool __qmljs_cmp_sne(Value left, Value right, Context *ctx) { return ! __qmljs_strict_equal(left, right, ctx); } -inline Bool __qmljs_cmp_instanceof(const Value left, const Value right, Context *ctx) +inline Bool __qmljs_cmp_instanceof(Value left, Value right, Context *ctx) { Value v = __qmljs_instanceof(left, right, ctx); return v.booleanValue(); } -inline uint __qmljs_cmp_in(const Value left, const Value right, Context *ctx) +inline uint __qmljs_cmp_in(Value left, Value right, Context *ctx) { Value v = __qmljs_in(left, right, ctx); return v.booleanValue(); } -inline Bool __qmljs_strict_equal(const Value x, const Value y, Context *ctx) +inline Bool __qmljs_strict_equal(Value x, Value y, Context *ctx) { if (x.rawValue() == y.rawValue()) return true; -- cgit v1.2.3 From 56e632c2c707272b092abb475c9c4f85ebe04fce Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 14:19:05 +0200 Subject: Implement some stubbed out methods Change-Id: I7533c33850235fb914e35e4fbd92def654158db7 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index d5843f79fe..08bedca0b8 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -617,6 +617,16 @@ void __qmljs_inplace_ushr_name(Value value, String *name, Context *ctx) void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, Context *ctx) { + Object *obj = base.toObject(ctx).objectValue(); + if (ArrayObject *a = obj->asArrayObject()) { + if (index.isNumber()) { + const quint32 idx = index.toUInt32(ctx); + Value v = a->value.at(idx); + v = __qmljs_bit_and(v, value, ctx); + a->value.assign(idx, v); + return; + } + } ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } @@ -682,31 +692,91 @@ void __qmljs_inplace_sub_element(Value base, Value index, Value value, Context * void __qmljs_inplace_mul_element(Value base, Value index, Value value, Context *ctx) { + Object *obj = base.toObject(ctx).objectValue(); + if (ArrayObject *a = obj->asArrayObject()) { + if (index.isNumber()) { + const quint32 idx = index.toUInt32(ctx); + Value v = a->value.at(idx); + v = __qmljs_mul(v, value, ctx); + a->value.assign(idx, v); + return; + } + } ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } void __qmljs_inplace_div_element(Value base, Value index, Value value, Context *ctx) { + Object *obj = base.toObject(ctx).objectValue(); + if (ArrayObject *a = obj->asArrayObject()) { + if (index.isNumber()) { + const quint32 idx = index.toUInt32(ctx); + Value v = a->value.at(idx); + v = __qmljs_div(v, value, ctx); + a->value.assign(idx, v); + return; + } + } ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } void __qmljs_inplace_mod_element(Value base, Value index, Value value, Context *ctx) { + Object *obj = base.toObject(ctx).objectValue(); + if (ArrayObject *a = obj->asArrayObject()) { + if (index.isNumber()) { + const quint32 idx = index.toUInt32(ctx); + Value v = a->value.at(idx); + v = __qmljs_mod(v, value, ctx); + a->value.assign(idx, v); + return; + } + } ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } void __qmljs_inplace_shl_element(Value base, Value index, Value value, Context *ctx) { + Object *obj = base.toObject(ctx).objectValue(); + if (ArrayObject *a = obj->asArrayObject()) { + if (index.isNumber()) { + const quint32 idx = index.toUInt32(ctx); + Value v = a->value.at(idx); + v = __qmljs_shl(v, value, ctx); + a->value.assign(idx, v); + return; + } + } ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } void __qmljs_inplace_shr_element(Value base, Value index, Value value, Context *ctx) { + Object *obj = base.toObject(ctx).objectValue(); + if (ArrayObject *a = obj->asArrayObject()) { + if (index.isNumber()) { + const quint32 idx = index.toUInt32(ctx); + Value v = a->value.at(idx); + v = __qmljs_shr(v, value, ctx); + a->value.assign(idx, v); + return; + } + } ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } void __qmljs_inplace_ushr_element(Value base, Value index, Value value, Context *ctx) { + Object *obj = base.toObject(ctx).objectValue(); + if (ArrayObject *a = obj->asArrayObject()) { + if (index.isNumber()) { + const quint32 idx = index.toUInt32(ctx); + Value v = a->value.at(idx); + v = __qmljs_ushr(v, value, ctx); + a->value.assign(idx, v); + return; + } + } ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); } -- cgit v1.2.3 From 12395b3201372e083da75768de716c6f507f1d67 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 14:55:52 +0200 Subject: Directly store constants as immediate Value's This removes a link from the JIT code to the IR, and should improve performance as we need slightly less copies. On 64 bit, Values are stores in 2 instructions instead of 4 now. Change-Id: I4a90165fcdfc2d77ac3f6e7e9accdef8600fadd6 Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 21 --------------------- qv4isel_masm.cpp | 18 ++++++++++++------ qv4isel_masm_p.h | 21 ++++++++++++++++----- 3 files changed, 28 insertions(+), 32 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index cbb78e35ed..18ed442b88 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -589,27 +589,6 @@ inline Value ValueBase<8>::fromObject(Object *o) return v; } -template struct ValueOffsetHelper; -template <> struct ValueOffsetHelper -{ - enum { DataOffset = offsetof(ValueData, int_32) }; -}; - -template <> struct ValueOffsetHelper -{ - enum { DataOffset = offsetof(ValueData, uint_32) }; -}; - -template <> struct ValueOffsetHelper -{ - enum { DataOffset = offsetof(ValueData, uint_32) }; -}; - -template <> struct ValueOffsetHelper -{ - enum { DataOffset = offsetof(ValueData, uint_32) }; -}; - #include struct Context { diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index bd82f1ea0b..89b62a98be 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -337,23 +337,23 @@ void InstructionSelection::visitMove(IR::Move *s) return; } else if (IR::Const *c = s->source->asConst()) { Address dest = loadTempAddress(Gpr0, t); + Value v; switch (c->type) { case IR::NullType: - storeValue(TrustedImm32(0), dest); + v = Value::nullValue(); break; case IR::UndefinedType: - storeValue(TrustedImm32(0), dest); + v = Value::undefinedValue(); break; case IR::BoolType: - storeValue(TrustedImm32(c->value != 0), dest); + v = Value::fromBoolean(c->value != 0); break; case IR::NumberType: { int ival = (int)c->value; if (ival == c->value) { - storeValue(TrustedImm32(ival), dest); + v = Value::fromInt32(ival); } else { - // ### Taking address of pointer inside IR. - copyValue(dest, &c->value); + v = Value::fromDouble(c->value); } } break; @@ -361,6 +361,7 @@ void InstructionSelection::visitMove(IR::Move *s) Q_UNIMPLEMENTED(); assert(!"TODO"); } + storeValue(v, dest); return; } else if (IR::Temp *t2 = s->source->asTemp()) { copyValue(t, t2); @@ -732,6 +733,11 @@ void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* na template void InstructionSelection::copyValue(Result result, Source source) { +#if CPU(X86_64) + loadArgument1(source, Gpr0); + storeArgument(Gpr0, result); +#else loadDouble(source, FPGpr0); storeDouble(FPGpr0, result); +#endif } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index ceb64a372a..c92430d4d4 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -324,6 +324,11 @@ private: } } + void storeArgument(RegisterID src, const Pointer &dest) + { + storePtr(src, dest); + } + void storeArgument(RegisterID src, RegisterID dest) { move(src, dest); @@ -678,16 +683,22 @@ private: const char* functionName; }; - template - void storeValue(TrustedImm32 value, Address destination) + void storeValue(VM::Value value, Address destination) { +#if CPU(X86_64) + storePtr(TrustedImmPtr((void *)value.val), destination); +#elif CPU(X86) destination.offset += offsetof(VM::ValueData, tag); - store32(TrustedImm32(type), destination); + store32(value.tag, destination); destination.offset -= offsetof(VM::ValueData, tag); - destination.offset += VM::ValueOffsetHelper::DataOffset; - store32(value, destination); + destination.offset += offsetof(VM::ValueData, int_32); + store32(value.int_32, destination); +#else +#error "Missing implementation" +#endif } + VM::ExecutionEngine *_engine; IR::Module *_module; IR::Function *_function; -- cgit v1.2.3 From e057895958448a742417104052635407b3a54c30 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 17 Oct 2012 14:34:46 +0200 Subject: Fix massive parallel builds Some of the udis86 files depend on a generate .c file, which if qmake knows about will be generated _before_ building the file that includes it. Change-Id: I8cf2b63d2c111fc5a64002f40f3e30210095cb29 Reviewed-by: Lars Knoll --- masm/masm.pri | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/masm/masm.pri b/masm/masm.pri index 117707fc70..10540d87c5 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -44,6 +44,10 @@ udis86.CONFIG += no_link udis86.commands = python $$PWD/disassembler/udis86/itab.py ${QMAKE_FILE_IN} QMAKE_EXTRA_COMPILERS += udis86 +udis86_tab_cfile.target = udis86_itab.c +udis86_tab_cfile.depends = udis86_itab.h +QMAKE_EXTRA_TARGETS += udis86_tab_cfile + # Taken from WebKit/Tools/qmake/mkspecs/features/unix/default_post.prf linux-g++* { greaterThan(QT_GCC_MAJOR_VERSION, 3):greaterThan(QT_GCC_MINOR_VERSION, 5) { -- cgit v1.2.3 From 49ca70f3809a1f41ff81edb8941b508a3dcbcb97 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 17 Oct 2012 15:02:54 +0200 Subject: Rename generateFunctionCall2 and loadArgument1. The old functions that implement the old calling convention are unused now. Change-Id: I8a6ba76c0ba2442f48a0dc331196ef399a902da0 Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 50 ++++++++++---------- qv4isel_masm_p.h | 142 ++++++++----------------------------------------------- 2 files changed, 46 insertions(+), 146 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 89b62a98be..9e70ab7fad 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -213,7 +213,7 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu break; case IR::Name::builtin_rethrow: { int argc = prepareVariableArguments(call->args); - generateFunctionCall2(result, __qmljs_builtin_rethrow, ContextRegister, baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall(result, __qmljs_builtin_rethrow, ContextRegister, baseAddressForCallArguments(), TrustedImm32(argc)); return; // we need to return to avoid checking the exceptions } } @@ -228,7 +228,7 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) int argc = prepareVariableArguments(call->args); IR::Temp* thisObject = 0; - generateFunctionCall2(result, __qmljs_call_value, ContextRegister, thisObject, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall(result, __qmljs_call_value, ContextRegister, thisObject, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } @@ -239,7 +239,7 @@ void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) assert(member->base->asTemp() != 0); int argc = prepareVariableArguments(call->args); - generateFunctionCall2(result, __qmljs_call_property, ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall(result, __qmljs_call_property, ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } @@ -258,7 +258,7 @@ void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) assert(member->base->asTemp() != 0); int argc = prepareVariableArguments(call->args); - generateFunctionCall2(result, __qmljs_construct_property, ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall(result, __qmljs_construct_property, ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } @@ -268,7 +268,7 @@ void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) assert(baseTemp != 0); int argc = prepareVariableArguments(call->args); - generateFunctionCall2(result, __qmljs_construct_value, ContextRegister, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall(result, __qmljs_construct_value, ContextRegister, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } @@ -319,7 +319,7 @@ void InstructionSelection::visitMove(IR::Move *s) String *propertyName = identifier(*n->id); if (IR::Temp *t = s->source->asTemp()) { - generateFunctionCall2(Void, __qmljs_set_activation_property, ContextRegister, propertyName, t); + generateFunctionCall(Void, __qmljs_set_activation_property, ContextRegister, propertyName, t); checkExceptions(); return; } else { @@ -328,10 +328,10 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Temp *t = s->target->asTemp()) { if (IR::Name *n = s->source->asName()) { if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. - generateFunctionCall2(t, __qmljs_get_thisObject, ContextRegister); + generateFunctionCall(t, __qmljs_get_thisObject, ContextRegister); } else { String *propertyName = identifier(*n->id); - generateFunctionCall2(t, __qmljs_get_activation_property, ContextRegister, propertyName); + generateFunctionCall(t, __qmljs_get_activation_property, ContextRegister, propertyName); checkExceptions(); } return; @@ -368,10 +368,10 @@ void InstructionSelection::visitMove(IR::Move *s) return; } else if (IR::String *str = s->source->asString()) { // ### inline - generateFunctionCall2(t, __qmljs_init_string, _engine->newString(*str->value)); + generateFunctionCall(t, __qmljs_init_string, _engine->newString(*str->value)); return; } else if (IR::Closure *clos = s->source->asClosure()) { - generateFunctionCall2(t, __qmljs_init_closure, TrustedImmPtr(clos->value), ContextRegister); + generateFunctionCall(t, __qmljs_init_closure, TrustedImmPtr(clos->value), ContextRegister); return; } else if (IR::New *ctor = s->source->asNew()) { if (ctor->base->asName()) { @@ -387,14 +387,14 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Member *m = s->source->asMember()) { //__qmljs_get_property(ctx, result, object, name); if (IR::Temp *base = m->base->asTemp()) { - generateFunctionCall2(t, __qmljs_get_property, ContextRegister, base, identifier(*m->name)); + generateFunctionCall(t, __qmljs_get_property, ContextRegister, base, identifier(*m->name)); checkExceptions(); return; } assert(!"wip"); return; } else if (IR::Subscript *ss = s->source->asSubscript()) { - generateFunctionCall2(t, __qmljs_get_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp()); + generateFunctionCall(t, __qmljs_get_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp()); checkExceptions(); return; } else if (IR::Unop *u = s->source->asUnop()) { @@ -411,7 +411,7 @@ void InstructionSelection::visitMove(IR::Move *s) } // switch if (op) - generateFunctionCallImp2(t, opName, op, e, ContextRegister); + generateFunctionCallImp(t, opName, op, e, ContextRegister); return; } } else if (IR::Binop *b = s->source->asBinop()) { @@ -460,7 +460,7 @@ void InstructionSelection::visitMove(IR::Move *s) } if (op) { - generateFunctionCallImp2(t, opName, op, l, r, ContextRegister); + generateFunctionCallImp(t, opName, op, l, r, ContextRegister); } return; } @@ -479,7 +479,7 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Member *m = s->target->asMember()) { if (IR::Temp *base = m->base->asTemp()) { if (IR::Temp *t = s->source->asTemp()) { - generateFunctionCall2(Void, __qmljs_set_property, ContextRegister, base, identifier(*m->name), t); + generateFunctionCall(Void, __qmljs_set_property, ContextRegister, base, identifier(*m->name), t); checkExceptions(); return; } else { @@ -488,7 +488,7 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Subscript *ss = s->target->asSubscript()) { if (IR::Temp *t2 = s->source->asTemp()) { - generateFunctionCall2(Void, __qmljs_set_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t2); + generateFunctionCall(Void, __qmljs_set_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t2); checkExceptions(); return; } else { @@ -518,7 +518,7 @@ void InstructionSelection::visitMove(IR::Move *s) break; } if (op) - generateFunctionCallImp2(t, opName, op, t, t2, ContextRegister); + generateFunctionCallImp(t, opName, op, t, t2, ContextRegister); return; } } else if (IR::Name *n = s->target->asName()) { @@ -542,7 +542,7 @@ void InstructionSelection::visitMove(IR::Move *s) break; } if (op) { - generateFunctionCallImp2(Void, opName, op, t, identifier(*n->id), ContextRegister); + generateFunctionCallImp(Void, opName, op, t, identifier(*n->id), ContextRegister); checkExceptions(); } return; @@ -571,7 +571,7 @@ void InstructionSelection::visitMove(IR::Move *s) if (op) { IR::Temp* base = ss->base->asTemp(); IR::Temp* index = ss->index->asTemp(); - generateFunctionCallImp2(Void, opName, op, base, index, t, ContextRegister); + generateFunctionCallImp(Void, opName, op, base, index, t, ContextRegister); checkExceptions(); } return; @@ -600,7 +600,7 @@ void InstructionSelection::visitMove(IR::Move *s) if (op) { IR::Temp* base = m->base->asTemp(); String* member = identifier(*m->name); - generateFunctionCallImp2(Void, opName, op, t, base, member, ContextRegister); + generateFunctionCallImp(Void, opName, op, t, base, member, ContextRegister); checkExceptions(); } return; @@ -640,7 +640,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) booleanConversion.link(this); { - generateFunctionCall2(Gpr0, __qmljs_to_boolean, t, ContextRegister); + generateFunctionCall(Gpr0, __qmljs_to_boolean, t, ContextRegister); } testBoolean.link(this); @@ -669,7 +669,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) case IR::OpIn: setOp(op, opName, __qmljs_cmp_in); break; } // switch - generateFunctionCall2(ReturnValueRegister, op, l, r, ContextRegister); + generateFunctionCall(ReturnValueRegister, op, l, r, ContextRegister); move(ReturnValueRegister, Gpr0); Jump target = branch32(NotEqual, Gpr0, TrustedImm32(0)); @@ -719,14 +719,14 @@ void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* na assert(baseName != 0); int argc = prepareVariableArguments(args); - generateFunctionCallImp2(result, name, method, ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCallImp(result, name, method, ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args) { int argc = prepareVariableArguments(args); - generateFunctionCallImp2(result, name, method, ContextRegister, baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCallImp(result, name, method, ContextRegister, baseAddressForCallArguments(), TrustedImm32(argc)); checkExceptions(); } @@ -734,7 +734,7 @@ template void InstructionSelection::copyValue(Result result, Source source) { #if CPU(X86_64) - loadArgument1(source, Gpr0); + loadArgument(source, Gpr0); storeArgument(Gpr0, result); #else loadDouble(source, FPGpr0); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index c92430d4d4..d06e48b28b 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -247,53 +247,17 @@ private: move(source, dest); } - void loadArgument(const Pointer& ptr, RegisterID dest) - { - addPtr(TrustedImm32(ptr.offset), ptr.base, dest); - } - - void loadArgument(IR::Temp* temp, RegisterID dest) - { - if (temp) { - Pointer addr = loadTempAddress(dest, temp); - loadArgument(addr, dest); - } else { - xorPtr(dest, dest); - } - } - void loadArgument(TrustedImmPtr ptr, RegisterID dest) { move(TrustedImmPtr(ptr), dest); } - void loadArgument(VM::String* string, RegisterID dest) - { - loadArgument(TrustedImmPtr(string), dest); - } - - void loadArgument(TrustedImm32 imm32, RegisterID dest) - { - xorPtr(dest, dest); - move(imm32, dest); - } - - void loadArgument1(RegisterID source, RegisterID dest) - { - move(source, dest); - } - - void loadArgument1(TrustedImmPtr ptr, RegisterID dest) - { - move(TrustedImmPtr(ptr), dest); - } - - void loadArgument1(const Pointer& ptr, RegisterID dest) + void loadArgument(const Pointer& ptr, RegisterID dest) { addPtr(TrustedImm32(ptr.offset), ptr.base, dest); } - void loadArgument1(IR::Temp* temp, RegisterID dest) + void loadArgument(IR::Temp* temp, RegisterID dest) { if (!temp) { VM::Value undefined = VM::Value::undefinedValue(); @@ -304,12 +268,12 @@ private: } } - void loadArgument1(VM::String* string, RegisterID dest) + void loadArgument(VM::String* string, RegisterID dest) { - loadArgument1(TrustedImmPtr(string), dest); + loadArgument(TrustedImmPtr(string), dest); } - void loadArgument1(TrustedImm32 imm32, RegisterID dest) + void loadArgument(TrustedImm32 imm32, RegisterID dest) { xorPtr(dest, dest); move(imm32, dest); @@ -385,10 +349,8 @@ private: #define isel_stringIfyx(s) #s #define isel_stringIfy(s) isel_stringIfyx(s) - #define generateFunctionCall(function, ...) \ - generateFunctionCallImp(isel_stringIfy(function), function, __VA_ARGS__) - #define generateFunctionCall2(t, function, ...) \ - generateFunctionCallImp2(t, isel_stringIfy(function), function, __VA_ARGS__) + #define generateFunctionCall(t, function, ...) \ + generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) #if CPU(X86) template @@ -462,123 +424,61 @@ private: callFunctionEpilogue(); } #elif CPU(X86_64) - - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) - { - callFunctionPrologue(); - loadArgument(arg1, RegisterArgument1); - loadArgument(arg2, RegisterArgument2); - loadArgument(arg3, RegisterArgument3); - loadArgument(arg4, RegisterArgument4); - loadArgument(arg5, RegisterArgument5); - loadArgument(arg6, RegisterArgument6); - callAbsolute(functionName, function); - callFunctionEpilogue(); - } - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + template + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { callFunctionPrologue(); loadArgument(arg1, RegisterArgument1); loadArgument(arg2, RegisterArgument2); loadArgument(arg3, RegisterArgument3); loadArgument(arg4, RegisterArgument4); - loadArgument(arg5, RegisterArgument5); callAbsolute(functionName, function); + storeArgument(ReturnValueRegister, r); callFunctionEpilogue(); } - - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + template + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { callFunctionPrologue(); loadArgument(arg1, RegisterArgument1); loadArgument(arg2, RegisterArgument2); loadArgument(arg3, RegisterArgument3); loadArgument(arg4, RegisterArgument4); + loadArgument(arg5, RegisterArgument5); callAbsolute(functionName, function); + storeArgument(ReturnValueRegister, r); callFunctionEpilogue(); } - - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + template + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) { callFunctionPrologue(); loadArgument(arg1, RegisterArgument1); loadArgument(arg2, RegisterArgument2); loadArgument(arg3, RegisterArgument3); callAbsolute(functionName, function); - callFunctionEpilogue(); - } - - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) - { - callFunctionPrologue(); - loadArgument(arg1, RegisterArgument1); - loadArgument(arg2, RegisterArgument2); - callAbsolute(functionName, function); - callFunctionEpilogue(); - } - - template - void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) - { - callFunctionPrologue(); - loadArgument1(arg1, RegisterArgument1); - loadArgument1(arg2, RegisterArgument2); - loadArgument1(arg3, RegisterArgument3); - loadArgument1(arg4, RegisterArgument4); - callAbsolute(functionName, function); - storeArgument(ReturnValueRegister, r); - callFunctionEpilogue(); - } - - template - void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) - { - callFunctionPrologue(); - loadArgument1(arg1, RegisterArgument1); - loadArgument1(arg2, RegisterArgument2); - loadArgument1(arg3, RegisterArgument3); - loadArgument1(arg4, RegisterArgument4); - loadArgument1(arg5, RegisterArgument5); - callAbsolute(functionName, function); - storeArgument(ReturnValueRegister, r); - callFunctionEpilogue(); - } - - template - void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) - { - callFunctionPrologue(); - loadArgument1(arg1, RegisterArgument1); - loadArgument1(arg2, RegisterArgument2); - loadArgument1(arg3, RegisterArgument3); - callAbsolute(functionName, function); storeArgument(ReturnValueRegister, r); callFunctionEpilogue(); } template - void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) { callFunctionPrologue(); - loadArgument1(arg1, RegisterArgument1); - loadArgument1(arg2, RegisterArgument2); + loadArgument(arg1, RegisterArgument1); + loadArgument(arg2, RegisterArgument2); callAbsolute(functionName, function); storeArgument(ReturnValueRegister, r); callFunctionEpilogue(); } template - void generateFunctionCallImp2(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1) + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1) { callFunctionPrologue(); - loadArgument1(arg1, RegisterArgument1); + loadArgument(arg1, RegisterArgument1); callAbsolute(functionName, function); storeArgument(ReturnValueRegister, r); callFunctionEpilogue(); -- cgit v1.2.3 From c2e3c3105314916f9090ca7b769b62bca43d1e0f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 16:37:07 +0200 Subject: Make some methods inline Change-Id: Ic36761b9ee95c7b51b0eb2c23daa01084d316067 Reviewed-by: Robin Burchell Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 42 ---------------------------------------- qmljs_runtime.h | 57 ++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 08bedca0b8..a869c62639 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -131,48 +131,6 @@ int Value::toInteger(double number) return std::signbit(number) ? -v : v; } -int Value::toUInt16(Context *ctx) -{ - return __qmljs_to_uint16(*this, ctx); -} - -int Value::toInt32(Context *ctx) -{ - return __qmljs_to_int32(*this, ctx); -} - -unsigned int Value::toUInt32(Context *ctx) -{ - return __qmljs_to_uint32(*this, ctx); -} - -Bool Value::toBoolean(Context *ctx) const -{ - return __qmljs_to_boolean(*this, ctx); -} - -double Value::toInteger(Context *ctx) const -{ - return __qmljs_to_integer(*this, ctx); -} - -double Value::toNumber(Context *ctx) const -{ - return __qmljs_to_number(*this, ctx); -} - -String *Value::toString(Context *ctx) const -{ - Value v = __qmljs_to_string(*this, ctx); - assert(v.is(Value::String_Type)); - return v.stringValue(); -} - -Value Value::toObject(Context *ctx) const -{ - return __qmljs_to_object(*this, ctx); -} - bool Value::isFunctionObject() const { return isObject() ? objectValue()->asFunctionObject() != 0 : false; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 18ed442b88..11097ab202 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -438,14 +438,14 @@ struct Value : public ValueBase static int toInt32(double value); static unsigned int toUInt32(double value); - int toUInt16(Context *ctx); - int toInt32(Context *ctx); - unsigned int toUInt32(Context *ctx); - Bool toBoolean(Context *ctx) const; - double toInteger(Context *ctx) const; + inline int toUInt16(Context *ctx); + inline int toInt32(Context *ctx); + inline unsigned int toUInt32(Context *ctx); + inline Bool toBoolean(Context *ctx) const; + inline double toInteger(Context *ctx) const; double toNumber(Context *ctx) const; - String *toString(Context *ctx) const; - Value toObject(Context *ctx) const; + inline String *toString(Context *ctx) const; + inline Value toObject(Context *ctx) const; inline bool isUndefined() const { return is(Value::Undefined_Type); } inline bool isNull() const { return is(Value::Null_Type); } @@ -480,6 +480,49 @@ struct Value : public ValueBase Value *getPropertyDescriptor(Context *ctx, String *name) const; }; +inline int Value::toUInt16(Context *ctx) +{ + return __qmljs_to_uint16(*this, ctx); +} + +inline int Value::toInt32(Context *ctx) +{ + return __qmljs_to_int32(*this, ctx); +} + +inline unsigned int Value::toUInt32(Context *ctx) +{ + return __qmljs_to_uint32(*this, ctx); +} + +inline Bool Value::toBoolean(Context *ctx) const +{ + return __qmljs_to_boolean(*this, ctx); +} + +inline double Value::toInteger(Context *ctx) const +{ + return __qmljs_to_integer(*this, ctx); +} + +inline double Value::toNumber(Context *ctx) const +{ + return __qmljs_to_number(*this, ctx); +} + +inline String *Value::toString(Context *ctx) const +{ + Value v = __qmljs_to_string(*this, ctx); + assert(v.is(Value::String_Type)); + return v.stringValue(); +} + +inline Value Value::toObject(Context *ctx) const +{ + return __qmljs_to_object(*this, ctx); +} + + inline Value ValueBase<4>::undefinedValue() { Value v; -- cgit v1.2.3 From 57e72c3e9f17a19da04c99fd9e0bfdcd946c581e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 16:42:47 +0200 Subject: No need to clear a register twice. Change-Id: I51235825566c6e7781af4c168b4902a90350b5cc Reviewed-by: Simon Hausmann --- qv4isel_masm_p.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index d06e48b28b..28b737c42e 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -276,7 +276,8 @@ private: void loadArgument(TrustedImm32 imm32, RegisterID dest) { xorPtr(dest, dest); - move(imm32, dest); + if (imm32.m_value) + move(imm32, dest); } void storeArgument(RegisterID src, IR::Temp *temp) -- cgit v1.2.3 From 2fe85372df20f937dffa3fe61aab3e84ba1bac1c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 17:31:53 +0200 Subject: Simplify some builtin methods Change-Id: I2c524b3634c65e7cc0ee72a21937048c4e3ae10a Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 14 +++++--------- qmljs_runtime.h | 6 +++--- qv4isel_masm.cpp | 27 +++++++++++++++++---------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index a869c62639..599eefc13a 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -1283,21 +1283,17 @@ Value __qmljs_rethrow(Context *context) return context->result; } -Value __qmljs_builtin_typeof(Context *context, Value *args, int argc) +Value __qmljs_builtin_typeof(Value val, Context *context) { - Q_UNUSED(argc); - return __qmljs_typeof(args[0], context); + return __qmljs_typeof(val, context); } -Value __qmljs_builtin_throw(Context *context, Value *args, int argc) +void __qmljs_builtin_throw(Value val, Context *context) { - Q_UNUSED(argc); - __qmljs_throw(args[0], context); - // ### change to void return value - return Value::undefinedValue(); + __qmljs_throw(val, context); } -Value __qmljs_builtin_rethrow(Context *context, Value *, int) +Value __qmljs_builtin_rethrow(Context *context) { return context->result; } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 11097ab202..5ac9150949 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -99,9 +99,9 @@ Value __qmljs_construct_activation_property(Context *, String *name, Value *args Value __qmljs_construct_property(Context *context, Value base, String *name, Value *args, int argc); Value __qmljs_construct_value(Context *context, Value func, Value *args, int argc); -Value __qmljs_builtin_typeof(Context *context, Value *args, int argc); -Value __qmljs_builtin_throw(Context *context, Value *args, int argc); -Value __qmljs_builtin_rethrow(Context *context, Value *args, int argc); +Value __qmljs_builtin_typeof(Value val, Context *context); +void __qmljs_builtin_throw(Value val, Context *context); +Value __qmljs_builtin_rethrow(Context *context); // constructors Value __qmljs_init_string(String *string); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 9e70ab7fad..092a524237 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -202,20 +202,27 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu case IR::Name::builtin_invalid: Q_UNREACHABLE(); break; - case IR::Name::builtin_typeof: - callRuntimeMethod(result, __qmljs_builtin_typeof, call->args); + case IR::Name::builtin_typeof: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + generateFunctionCall(result, __qmljs_builtin_typeof, arg, ContextRegister); + checkExceptions(); + } break; case IR::Name::builtin_delete: Q_UNREACHABLE(); break; - case IR::Name::builtin_throw: - callRuntimeMethod(result, __qmljs_builtin_throw, call->args); - break; - case IR::Name::builtin_rethrow: { - int argc = prepareVariableArguments(call->args); - generateFunctionCall(result, __qmljs_builtin_rethrow, ContextRegister, baseAddressForCallArguments(), TrustedImm32(argc)); - return; // we need to return to avoid checking the exceptions + case IR::Name::builtin_throw: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + generateFunctionCall(Void, __qmljs_builtin_throw, arg, ContextRegister); + checkExceptions(); } + break; + case IR::Name::builtin_rethrow: + // don't use callRuntimeMethod, as we need to return to avoid checking the exceptions + generateFunctionCall(result, __qmljs_builtin_rethrow, ContextRegister); + return; } } } @@ -275,7 +282,7 @@ void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) void InstructionSelection::checkExceptions() { Address addr(ContextRegister, offsetof(Context, hasUncaughtException)); - Jump jmp = branch8(Equal, addr, TrustedImm32(1)); + Jump jmp = branch8(NotEqual, addr, TrustedImm32(0)); _patches[_function->handlersBlock].append(jmp); } -- cgit v1.2.3 From 317252833e835b729c7c8762abf89211c594130b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 17 Oct 2012 18:28:45 +0200 Subject: Simplify function call generate (part 1) This gets rid of the ia32 and arm variants of generateFunctionCall (they'll have to be re-done anyway) and replaces the duplicated bodies of the generateFunctionCallImp overloads with calls to one single instance that has the maximum number of supported arguments. The recently introduce VoidType takes care of non-existant arguments. Change-Id: I7c41f58a72840837709e65620d7f384e4b18f2a8 Reviewed-by: Lars Knoll --- qv4isel_masm_p.h | 184 +++++-------------------------------------------------- 1 file changed, 14 insertions(+), 170 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 28b737c42e..11ec5e6eca 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -280,6 +280,11 @@ private: move(imm32, dest); } + void loadArgument(VoidType, RegisterID) + { + // Nothing to do. + } + void storeArgument(RegisterID src, IR::Temp *temp) { if (temp) { @@ -353,91 +358,6 @@ private: #define generateFunctionCall(t, function, ...) \ generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) -#if CPU(X86) - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) - { - callFunctionPrologue(); - // Reverse order - push(arg6); - push(arg5); - push(arg4); - push(arg3); - push(arg2); - push(arg1); - callAbsolute(functionName, function); - add32(TrustedImm32(6 * sizeof(void*)), StackPointerRegister); - callFunctionEpilogue(); - } - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) - { - callFunctionPrologue(); - // Reverse order - push(arg5); - push(arg4); - push(arg3); - push(arg2); - push(arg1); - callAbsolute(functionName, function); - add32(TrustedImm32(5 * sizeof(void*)), StackPointerRegister); - callFunctionEpilogue(); - } - - - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) - { - callFunctionPrologue(); - // Reverse order - push(arg4); - push(arg3); - push(arg2); - push(arg1); - callAbsolute(functionName, function); - add32(TrustedImm32(4 * sizeof(void*)), StackPointerRegister); - callFunctionEpilogue(); - } - - - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) - { - callFunctionPrologue(); - // Reverse order - push(arg3); - push(arg2); - push(arg1); - callAbsolute(functionName, function); - add32(TrustedImm32(3 * sizeof(void*)), StackPointerRegister); - callFunctionEpilogue(); - } - - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) - { - callFunctionPrologue(); - // Reverse order - push(arg2); - push(arg1); - callAbsolute(functionName, function); - add32(TrustedImm32(2 * sizeof(void*)), StackPointerRegister); - callFunctionEpilogue(); - } -#elif CPU(X86_64) - template - void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) - { - callFunctionPrologue(); - loadArgument(arg1, RegisterArgument1); - loadArgument(arg2, RegisterArgument2); - loadArgument(arg3, RegisterArgument3); - loadArgument(arg4, RegisterArgument4); - callAbsolute(functionName, function); - storeArgument(ReturnValueRegister, r); - callFunctionEpilogue(); - } - template void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { @@ -452,106 +372,30 @@ private: callFunctionEpilogue(); } + template + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType()); + } + template void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) { - callFunctionPrologue(); - loadArgument(arg1, RegisterArgument1); - loadArgument(arg2, RegisterArgument2); - loadArgument(arg3, RegisterArgument3); - callAbsolute(functionName, function); - storeArgument(ReturnValueRegister, r); - callFunctionEpilogue(); + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType()); } template void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) { - callFunctionPrologue(); - loadArgument(arg1, RegisterArgument1); - loadArgument(arg2, RegisterArgument2); - callAbsolute(functionName, function); - storeArgument(ReturnValueRegister, r); - callFunctionEpilogue(); + generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType()); } template void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1) { - callFunctionPrologue(); - loadArgument(arg1, RegisterArgument1); - callAbsolute(functionName, function); - storeArgument(ReturnValueRegister, r); - callFunctionEpilogue(); + generateFunctionCallImp(r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType()); } -#elif CPU(ARM) - - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) - { - callFunctionPrologue(); - loadArgument(arg1, RegisterArgument1); - loadArgument(arg2, RegisterArgument2); - loadArgument(arg3, RegisterArgument3); - loadArgument(arg4, RegisterArgument4); - push(arg5); - push(arg6); - callAbsolute(functionName, function); - add32(TrustedImm32(2 * sizeof(void*)), StackPointerRegister); - callFunctionEpilogue(); - } - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) - { - callFunctionPrologue(); - loadArgument(arg1, RegisterArgument1); - loadArgument(arg2, RegisterArgument2); - loadArgument(arg3, RegisterArgument3); - loadArgument(arg4, RegisterArgument4); - push(arg5); - callAbsolute(functionName, function); - add32(TrustedImm32(1 * sizeof(void*)), StackPointerRegister); - callFunctionEpilogue(); - } - - - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) - { - callFunctionPrologue(); - loadArgument(arg1, RegisterArgument1); - loadArgument(arg2, RegisterArgument2); - loadArgument(arg3, RegisterArgument3); - loadArgument(arg4, RegisterArgument4); - callAbsolute(functionName, function); - callFunctionEpilogue(); - } - - - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) - { - callFunctionPrologue(); - loadArgument(arg1, RegisterArgument1); - loadArgument(arg2, RegisterArgument2); - loadArgument(arg3, RegisterArgument3); - callAbsolute(functionName, function); - callFunctionEpilogue(); - } - - template - void generateFunctionCallImp(const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) - { - callFunctionPrologue(); - loadArgument(arg1, RegisterArgument1); - loadArgument(arg2, RegisterArgument2); - callAbsolute(functionName, function); - callFunctionEpilogue(); - } - -#endif - int prepareVariableArguments(IR::ExprList* args); typedef VM::Value (*ActivationMethod)(VM::Context *, VM::String *name, VM::Value *args, int argc); -- cgit v1.2.3 From 97fa8c5de84c9027ac663b00cb125aaf1bd6484d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 17 Oct 2012 19:28:44 +0200 Subject: Simplify function call generation (part 2) Hide the mapping from function argument to register in a small section of the architecture specific code at the top of the instruction selector and use a generic ArgumentLoader helper class to first try loading function parameters in registers and then fall back to the stack. This is only tested on amd64 right now where we don't end up using the stack right now, so it will need testing/tweaking for ia32/arm. Change-Id: If073cd92a674e90dc28f2c9bdf2841bb07c020f1 Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 2 +- qv4isel_masm_p.h | 128 ++++++++++++++++++++++++++++++++----------------------- 2 files changed, 75 insertions(+), 55 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 092a524237..49abe8a074 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -103,7 +103,7 @@ void InstructionSelection::operator()(IR::Function *function) #if CPU(X86) loadPtr(addressForArgument(0), ContextRegister); #elif CPU(X86_64) || CPU(ARM) - move(RegisterArgument1, ContextRegister); + move(registerForArgument(0), ContextRegister); #else assert(!"TODO"); #endif diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 11ec5e6eca..a488bc85e1 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -74,6 +74,14 @@ protected: static const RegisterID CalleeSavedFirstRegister = Gpr3; static const RegisterID CalleeSavedLastRegister = Gpr3; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + + static const int RegisterArgumentCount = 0; + static RegisterID registerForArgument(int) + { + assert(false); + // Not reached. + return JSC::X86Registers::eax; + } #elif CPU(X86_64) static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; static const RegisterID StackPointerRegister = JSC::X86Registers::esp; @@ -85,12 +93,20 @@ protected: static const RegisterID Gpr3 = JSC::X86Registers::esi; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const RegisterID RegisterArgument1 = JSC::X86Registers::edi; - static const RegisterID RegisterArgument2 = JSC::X86Registers::esi; - static const RegisterID RegisterArgument3 = JSC::X86Registers::edx; - static const RegisterID RegisterArgument4 = JSC::X86Registers::ecx; - static const RegisterID RegisterArgument5 = JSC::X86Registers::r8; - static const RegisterID RegisterArgument6 = JSC::X86Registers::r9; + static const int RegisterArgumentCount = 6; + static RegisterID registerForArgument(int index) + { + static RegisterID regs[RegisterArgumentCount] = { + JSC::X86Registers::edi, + JSC::X86Registers::esi, + JSC::X86Registers::edx, + JSC::X86Registers::ecx, + JSC::X86Registers::r8, + JSC::X86Registers::r9 + }; + assert(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; #elif CPU(ARM) static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4; static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; @@ -108,6 +124,13 @@ protected: static const RegisterID RegisterArgument2 = JSC::ARMRegisters::r1; static const RegisterID RegisterArgument3 = JSC::ARMRegisters::r2; static const RegisterID RegisterArgument4 = JSC::ARMRegisters::r3; + + static const int RegisterArgumentCount = 4; + static RegisterID registerForArgument(int index) + { + assert(index >= 0 && index < RegisterArgumentCount); + return static_cast(JSC::ARMRegisters::r0 + index); + }; #else #error Argh. #endif @@ -153,49 +176,16 @@ protected: #endif } - Address stackAddressForArgument(int index) const + Address addressForArgument(int index) const { + if (index < RegisterArgumentCount) + return Address(registerForArgument(index), 0); + // StackFrameRegister points to its old value on the stack, and above // it we have the return address, hence the need to step over two // values before reaching the first argument. - return Address(StackFrameRegister, (index + 2) * sizeof(void*)); - } -#if CPU(X86) - Address addressForArgument(int index) const - { - return stackAddressForArgument(index); - } -#elif CPU(X86_64) - Address addressForArgument(int index) const - { - static RegisterID args[6] = { - RegisterArgument1, - RegisterArgument2, - RegisterArgument3, - RegisterArgument4, - RegisterArgument5, - RegisterArgument6 - }; - if (index < 6) - return Address(args[index], 0); - else - return stackAddressForArgument(index - 6); - } -#elif CPU(ARM) - Address addressForArgument(int index) const - { - static RegisterID args[4] = { - RegisterArgument1, - RegisterArgument2, - RegisterArgument3, - RegisterArgument4, - }; - if (index < 4) - return Address(args[index], 0); - else - return stackAddressForArgument(index - 4); + return Address(StackFrameRegister, (index - RegisterArgumentCount + 2) * sizeof(void*)); } -#endif // Some run-time functions take (Value* args, int argc). This function is for populating // the args. @@ -280,11 +270,6 @@ private: move(imm32, dest); } - void loadArgument(VoidType, RegisterID) - { - // Nothing to do. - } - void storeArgument(RegisterID src, IR::Temp *temp) { if (temp) { @@ -358,16 +343,51 @@ private: #define generateFunctionCall(t, function, ...) \ generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) + struct ArgumentLoader + { + ArgumentLoader(InstructionSelection* instructionSelection, int totalNumberOfArguments) + : isel(instructionSelection) + , stackArgumentCount(0) + , currentRegisterIndex(qMin(totalNumberOfArguments - 1, RegisterArgumentCount - 1)) + { + } + + template + void load(T argument) + { + if (currentRegisterIndex >= 0) { + isel->loadArgument(argument, registerForArgument(currentRegisterIndex)); + --currentRegisterIndex; + } else { + isel->push(argument); + ++stackArgumentCount; + } + } + + void load(VoidType) + { + if (currentRegisterIndex >= 0) + --currentRegisterIndex; + } + + InstructionSelection *isel; + int stackArgumentCount; + int currentRegisterIndex; + }; + template void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { callFunctionPrologue(); - loadArgument(arg1, RegisterArgument1); - loadArgument(arg2, RegisterArgument2); - loadArgument(arg3, RegisterArgument3); - loadArgument(arg4, RegisterArgument4); - loadArgument(arg5, RegisterArgument5); + ArgumentLoader l(this, 5); + l.load(arg5); + l.load(arg4); + l.load(arg3); + l.load(arg2); + l.load(arg1); callAbsolute(functionName, function); + if (l.stackArgumentCount) + add32(TrustedImm32(l.stackArgumentCount * sizeof(void*)), StackPointerRegister); storeArgument(ReturnValueRegister, r); callFunctionEpilogue(); } -- cgit v1.2.3 From fcb45631910f34d24ab14f386afa2a89b98c7395 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 22:40:28 +0200 Subject: Add missing break statement Change-Id: I9f5d4cb6a7db0cd63c419cdf41c1a7497453559e Reviewed-by: Simon Hausmann --- qv4isel_masm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 49abe8a074..3ad473d293 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -458,7 +458,7 @@ void InstructionSelection::visitMove(IR::Move *s) case IR::OpStrictEqual: setOp(op, opName, __qmljs_se); break; case IR::OpStrictNotEqual: setOp(op, opName, __qmljs_sne); break; case IR::OpInstanceof: setOp(op, opName, __qmljs_instanceof); break; - case IR::OpIn: setOp(op, opName, __qmljs_in); + case IR::OpIn: setOp(op, opName, __qmljs_in); break; case IR::OpAnd: case IR::OpOr: -- cgit v1.2.3 From 96fc5e908cec0ab469e44a6798934b8cc9587972 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 22:40:49 +0200 Subject: Clean up to(U)Int and toInteger conversions Change-Id: Ifdd4f22bd50ba2a4c0dd710ba346a36c84b2c305 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 45 +++++++++++++++++++++++++++++++++++++++------ qmljs_runtime.h | 53 +++++++---------------------------------------------- 2 files changed, 46 insertions(+), 52 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 599eefc13a..f86f8895f7 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -109,19 +109,52 @@ Value Value::fromString(Context *ctx, const QString &s) int Value::toInt32(double number) { - if (! number || std::isnan(number) || std::isinf(number)) - return +0; - return (int) trunc(number); // ### + const double D32 = 4294967296.0; + const double D31 = D32 / 2.0; + + if ((number >= -D31 && number < D31)) + return static_cast(number); + + + if (!std::isfinite(number)) + return 0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D32); + + if (number < -D31) + number += D32; + else if (number >= D31) + number -= D32; + + return int(number); } unsigned int Value::toUInt32(double number) { - if (! number || std::isnan(number) || std::isinf(number)) + const double D32 = 4294967296.0; + if ((number >= 0 && number < D32)) + return static_cast(number); + + if (!std::isfinite(number)) return +0; - return (uint) trunc(number); // ### + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D32); + + if (number < 0) + number += D32; + + return unsigned(number); } -int Value::toInteger(double number) +double Value::toInteger(double number) { if (std::isnan(number)) return +0; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 5ac9150949..643165ce3c 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -434,7 +434,7 @@ struct Value : public ValueBase using ValueBase::fromString; #endif - static int toInteger(double fromNumber); + static double toInteger(double fromNumber); static int toInt32(double value); static unsigned int toUInt32(double value); @@ -755,42 +755,16 @@ inline double __qmljs_to_integer(Value value, Context *ctx) { if (value.isInteger()) return value.int_32; - const double number = __qmljs_to_number(value, ctx); - if (qIsNaN(number)) - return +0; - else if (! number || std::isinf(number)) - return number; - const double v = floor(fabs(number)); - return std::signbit(number) ? -v : v; + + return Value::toInteger(__qmljs_to_number(value, ctx)); } inline int __qmljs_to_int32(Value value, Context *ctx) { if (value.isInteger()) return value.int_32; - double number = __qmljs_to_number(value, ctx); - - if ((number >= -2147483648.0 && number < 2147483648.0)) { - return static_cast(number); - } - - if (! number || qIsNaN(number) || std::isinf(number)) - return 0; - - double D32 = 4294967296.0; - double sign = (number < 0) ? -1.0 : 1.0; - double abs_n = fabs(number); - - number = ::fmod(sign * ::floor(abs_n), D32); - const double D31 = D32 / 2.0; - - if (sign == -1 && number < -D31) - number += D32; - else if (sign != -1 && number >= D31) - number -= D32; - - return int(number); + return Value::toInt32(__qmljs_to_number(value, ctx)); } inline unsigned __qmljs_to_uint32(Value value, Context *ctx) @@ -798,20 +772,7 @@ inline unsigned __qmljs_to_uint32(Value value, Context *ctx) if (value.isInteger()) return (unsigned) value.int_32; - double number = __qmljs_to_number(value, ctx); - if (! number || qIsNaN(number) || std::isinf(number)) - return +0; - - double sign = (number < 0) ? -1.0 : 1.0; - double abs_n = ::fabs(number); - - const double D32 = 4294967296.0; - number = ::fmod(sign * ::floor(abs_n), D32); - - if (number < 0) - number += D32; - - return unsigned(number); + return Value::toUInt32(__qmljs_to_number(value, ctx)); } inline unsigned short __qmljs_to_uint16(Value value, Context *ctx) @@ -1061,10 +1022,10 @@ inline void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value) inline Value __qmljs_add(Value left, Value right, Context *ctx) { - if (left.isInteger() && right.isInteger()) + if (left.isInteger() & right.isInteger()) return add_int32(left.integerValue(), right.integerValue()); - if (left.isNumber() && right.isNumber()) + if (left.isNumber() & right.isNumber()) return Value::fromDouble(left.asDouble() + right.asDouble()); else return __qmljs_add_helper(left, right, ctx); -- cgit v1.2.3 From 535ed96f884fc4c18f0dcaebbe3e184a49bef09b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 22:52:46 +0200 Subject: Clean up toUInt16 and fix a small typo in toUInt32 Change-Id: I49e57cb8eed83237d8b2477297e429cd38d07c19 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 2 +- qmljs_runtime.h | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index f86f8895f7..3c9ca10d3b 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -137,7 +137,7 @@ unsigned int Value::toUInt32(double number) { const double D32 = 4294967296.0; if ((number >= 0 && number < D32)) - return static_cast(number); + return static_cast(number); if (!std::isfinite(number)) return +0; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 643165ce3c..c821010924 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -777,15 +777,23 @@ inline unsigned __qmljs_to_uint32(Value value, Context *ctx) inline unsigned short __qmljs_to_uint16(Value value, Context *ctx) { + if (value.isInteger()) + return (ushort)(uint)value.integerValue(); + double number = __qmljs_to_number(value, ctx); - if (! number || qIsNaN(number) || std::isinf(number)) + + double D16 = 65536.0; + if ((number >= 0 && number < D16)) + return static_cast(number); + + if (!std::isfinite(number)) return +0; - double sign = (number < 0) ? -1.0 : 1.0; - double abs_n = ::fabs(number); + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; - double D16 = 65536.0; - number = ::fmod(sign * ::floor(abs_n), D16); + number = ::fmod(d , D16); if (number < 0) number += D16; -- cgit v1.2.3 From e3f78315a3b65788b784425db8620a08631ce358 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 17 Oct 2012 22:38:55 +0200 Subject: Fix build on ia32 Use the "q" constraint with the argument to seto to ensure we get a lower-8-bit register Change-Id: I483a8b37df9b726d78c050db97b58c5b62fde8e4 Reviewed-by: Lars Knoll --- qmljs_math.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qmljs_math.h b/qmljs_math.h index 89d6e28ee3..a586ed16d9 100644 --- a/qmljs_math.h +++ b/qmljs_math.h @@ -52,7 +52,7 @@ static inline Value add_int32(int a, int b) asm ("addl %2, %1\n" "seto %0" - : "=r" (overflow), "=r" (a) + : "=q" (overflow), "=r" (a) : "r" (b), "1" (a) : ); @@ -67,7 +67,7 @@ static inline Value sub_int32(int a, int b) asm ("subl %2, %1\n" "seto %0" - : "=r" (overflow), "=r" (a) + : "=q" (overflow), "=r" (a) : "r" (b), "1" (a) : ); @@ -82,7 +82,7 @@ static inline Value mul_int32(int a, int b) asm ("imul %2, %1\n" "seto %0" - : "=r" (overflow), "=r" (a) + : "=q" (overflow), "=r" (a) : "r" (b), "1" (a) : ); -- cgit v1.2.3 From af41947939680812c259dddabf5068262595875c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 17 Oct 2012 22:43:07 +0200 Subject: Port to ia32 (stack based value passing) In the architecture section, introduce a VALUE_FITS_IN_REGISTER macro as well as a RegisterSize constant. When storing the return value into a IR::Temp, either copy straight from the return value register if the architecture supports values in registers, otherwise do a load/store copy. Added push() overload that allows pushing constant values. push() with an IR::Temp* now pushes the actual value onto the stack, because our run-time functions expect values to be passed by... value :) Extended the function call logic to reserve space on the stack for the function return value if it doesn't fit into a register. In that case at least the ia32 ABI specifies that there is a hidden first argument which is the address of the space where the callee can place the return value itself. Change-Id: Ice9435dfc1482f4045a4ec4ad2c9ab58d4dfc7aa Reviewed-by: Lars Knoll --- qv4isel_masm_p.h | 111 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 96 insertions(+), 15 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index a488bc85e1..b8936595e1 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -63,6 +63,9 @@ public: protected: #if CPU(X86) + +#undef VALUE_FITS_IN_REGISTER + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; static const RegisterID StackPointerRegister = JSC::X86Registers::esp; static const RegisterID ContextRegister = JSC::X86Registers::esi; @@ -75,6 +78,8 @@ protected: static const RegisterID CalleeSavedLastRegister = Gpr3; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + static const int RegisterSize = 4; + static const int RegisterArgumentCount = 0; static RegisterID registerForArgument(int) { @@ -83,6 +88,9 @@ protected: return JSC::X86Registers::eax; } #elif CPU(X86_64) + +#define VALUE_FITS_IN_REGISTER + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; static const RegisterID StackPointerRegister = JSC::X86Registers::esp; static const RegisterID ContextRegister = JSC::X86Registers::r14; @@ -93,6 +101,8 @@ protected: static const RegisterID Gpr3 = JSC::X86Registers::esi; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + static const int RegisterSize = 8; + static const int RegisterArgumentCount = 6; static RegisterID registerForArgument(int index) { @@ -108,6 +118,9 @@ protected: return regs[index]; }; #elif CPU(ARM) + +#undef VALUE_FITS_IN_REGISTER + static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4; static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; static const RegisterID ContextRegister = JSC::ARMRegisters::r5; @@ -120,6 +133,8 @@ protected: static const RegisterID CalleeSavedLastRegister = JSC::ARMRegisters::r11; static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; + static const int RegisterSize = 4; + static const RegisterID RegisterArgument1 = JSC::ARMRegisters::r0; static const RegisterID RegisterArgument2 = JSC::ARMRegisters::r1; static const RegisterID RegisterArgument3 = JSC::ARMRegisters::r2; @@ -134,6 +149,7 @@ protected: #else #error Argh. #endif + struct VoidType {}; static const VoidType Void; @@ -275,14 +291,23 @@ private: if (temp) { // ### Should use some ScratchRegister here Pointer addr = loadTempAddress(Gpr3, temp); +#ifdef VALUE_FITS_IN_REGISTER storePtr(src, addr); +#else + // If the value doesn't fit into a register, then the + // register contains the address to where the argument + // (return value) is stored. Copy it from there. + copyValue(addr, Pointer(src, 0)); +#endif } } +#if VALUE_FITS_IN_REGISTER void storeArgument(RegisterID src, const Pointer &dest) { storePtr(src, dest); } +#endif void storeArgument(RegisterID src, RegisterID dest) { @@ -301,14 +326,30 @@ private: push(Gpr0); } + void push(VM::Value value) + { +#if VALUE_FITS_IN_REGISTER + move(TrustedImm64((const void *)undefined.val), Gpr0); + push(Gpr0); +#else + move(TrustedImm32(value.int_32), Gpr0); + push(Gpr0); + move(TrustedImm32(value.tag), Gpr0); + push(Gpr0); +#endif + } + void push(IR::Temp* temp) { if (temp) { - Pointer addr = loadTempAddress(Gpr0, temp); + Address addr = loadTempAddress(Gpr0, temp); + addr.offset += 4; + push(addr); + addr.offset -= 4; push(addr); } else { - xorPtr(Gpr0, Gpr0); - push(Gpr0); + VM::Value undefined = VM::Value::undefinedValue(); + push(undefined); } } @@ -343,11 +384,26 @@ private: #define generateFunctionCall(t, function, ...) \ generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) + static inline int sizeOfArgument(VoidType) + { return 0; } + static inline int sizeOfArgument(RegisterID) + { return RegisterSize; } + static inline int sizeOfArgument(IR::Temp*) + { return 8; } // Size of value + static inline int sizeOfArgument(const Pointer&) + { return sizeof(void*); } + static inline int sizeOfArgument(VM::String* string) + { return sizeof(string); } + static inline int sizeOfArgument(TrustedImmPtr) + { return sizeof(void*); } + static inline int sizeOfArgument(TrustedImm32) + { return 4; } + struct ArgumentLoader { ArgumentLoader(InstructionSelection* instructionSelection, int totalNumberOfArguments) : isel(instructionSelection) - , stackArgumentCount(0) + , stackSpaceForArguments(0) , currentRegisterIndex(qMin(totalNumberOfArguments - 1, RegisterArgumentCount - 1)) { } @@ -360,7 +416,7 @@ private: --currentRegisterIndex; } else { isel->push(argument); - ++stackArgumentCount; + stackSpaceForArguments += sizeOfArgument(argument); } } @@ -371,7 +427,7 @@ private: } InstructionSelection *isel; - int stackArgumentCount; + int stackSpaceForArguments; int currentRegisterIndex; }; @@ -379,16 +435,44 @@ private: void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { callFunctionPrologue(); - ArgumentLoader l(this, 5); + + int totalNumberOfArgs = 5; + + // If necessary reserve space for the return value on the stack and + // pass the pointer to it as the first hidden parameter. + bool returnValueOnStack = false; + int sizeOfReturnValueOnStack = sizeOfArgument(r); + if (sizeOfReturnValueOnStack > RegisterSize) { + sub32(TrustedImm32(sizeOfReturnValueOnStack), StackPointerRegister); + ++totalNumberOfArgs; + returnValueOnStack = true; + } + + ArgumentLoader l(this, totalNumberOfArgs); l.load(arg5); l.load(arg4); l.load(arg3); l.load(arg2); l.load(arg1); + + if (returnValueOnStack) { + // Load address of return value + l.load(Pointer(StackPointerRegister, l.stackSpaceForArguments)); + } + callAbsolute(functionName, function); - if (l.stackArgumentCount) - add32(TrustedImm32(l.stackArgumentCount * sizeof(void*)), StackPointerRegister); + + int stackSizeToCorrect = l.stackSpaceForArguments; + if (returnValueOnStack) { + stackSizeToCorrect -= sizeof(void*); // Callee removed the hidden argument (address of return value) + stackSizeToCorrect += sizeOfReturnValueOnStack; + } + storeArgument(ReturnValueRegister, r); + + if (stackSizeToCorrect) + add32(TrustedImm32(stackSizeToCorrect), StackPointerRegister); + callFunctionEpilogue(); } @@ -453,17 +537,14 @@ private: #if CPU(X86_64) storePtr(TrustedImmPtr((void *)value.val), destination); #elif CPU(X86) - destination.offset += offsetof(VM::ValueData, tag); - store32(value.tag, destination); - destination.offset -= offsetof(VM::ValueData, tag); - destination.offset += offsetof(VM::ValueData, int_32); - store32(value.int_32, destination); + store32(TrustedImm32(value.int_32), destination); + destination.offset += 4; + store32(TrustedImm32(value.tag), destination); #else #error "Missing implementation" #endif } - VM::ExecutionEngine *_engine; IR::Module *_module; IR::Function *_function; -- cgit v1.2.3 From ccd7b9f2165486572b22f3680bdb64855b13c7ef Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 17 Oct 2012 22:55:19 +0200 Subject: Fix warning about unused opName parameter Pass it up to generateFunctionCall :) Change-Id: Ic05854e2a7b8bea09eb652e37df43587ffe62824 Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 3ad473d293..2a03dd350a 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -676,7 +676,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) case IR::OpIn: setOp(op, opName, __qmljs_cmp_in); break; } // switch - generateFunctionCall(ReturnValueRegister, op, l, r, ContextRegister); + generateFunctionCallImp(ReturnValueRegister, opName, op, l, r, ContextRegister); move(ReturnValueRegister, Gpr0); Jump target = branch32(NotEqual, Gpr0, TrustedImm32(0)); -- cgit v1.2.3 From a01b71ca7554dbbc89f934591f58b85c9e97fd68 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 23:03:04 +0200 Subject: Compile fixes on amd64 Change-Id: I767d2d8b8e6dec738fe6ed63a1ced781629b81ce Reviewed-by: Simon Hausmann --- qv4isel_masm_p.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index b8936595e1..ee22f39eba 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -302,7 +302,7 @@ private: } } -#if VALUE_FITS_IN_REGISTER +#ifdef VALUE_FITS_IN_REGISTER void storeArgument(RegisterID src, const Pointer &dest) { storePtr(src, dest); @@ -328,8 +328,8 @@ private: void push(VM::Value value) { -#if VALUE_FITS_IN_REGISTER - move(TrustedImm64((const void *)undefined.val), Gpr0); +#ifdef VALUE_FITS_IN_REGISTER + move(TrustedImm64(value.val), Gpr0); push(Gpr0); #else move(TrustedImm32(value.int_32), Gpr0); -- cgit v1.2.3 From 4c0bc0a75add51c98c53a9dc84cd5135ae785674 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 17 Oct 2012 23:07:41 +0200 Subject: Fix the shift operators The JS spec demands that the right hand argument is being masked with 0x1f before applying the nit shift. See e.g. http://bclary.com/2004/11/07/#a-11.7 Change-Id: I6a7dee4708b2fe93df984f13752f7f00b5f2f367 Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index c821010924..2c2207117d 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -1078,21 +1078,21 @@ inline Value __qmljs_mod(Value left, Value right, Context *ctx) inline Value __qmljs_shl(Value left, Value right, Context *ctx) { int lval = __qmljs_to_int32(left, ctx); - unsigned rval = __qmljs_to_uint32(right, ctx); + unsigned rval = __qmljs_to_uint32(right, ctx) & 0x1f; return Value::fromInt32(lval << rval); } inline Value __qmljs_shr(Value left, Value right, Context *ctx) { int lval = __qmljs_to_int32(left, ctx); - unsigned rval = __qmljs_to_uint32(right, ctx); + unsigned rval = __qmljs_to_uint32(right, ctx) & 0x1f; return Value::fromInt32(lval >> rval); } inline Value __qmljs_ushr(Value left, Value right, Context *ctx) { unsigned lval = __qmljs_to_uint32(left, ctx); - unsigned rval = __qmljs_to_uint32(right, ctx); + unsigned rval = __qmljs_to_uint32(right, ctx) & 0x1f; return Value::fromInt32(lval >> rval); } -- cgit v1.2.3 From e400895c0ec5354617751afdbfcf65ad242b3723 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 17 Oct 2012 14:34:44 +0200 Subject: Change usage of qIsNaN to std::isnan. By using std::isnan the compiler/std-lib has a chance to optimize the call away or maybe even generate an intrinsic. Furthermore, when the runtime is compiled to bitcode for inclusion in generated code, we now do not need to link against QtCore to get just one single function. Change-Id: I79995afc1ba215f1ffd9a43df1808b833dfa8481 Reviewed-by: Lars Knoll --- qmljs_math.h | 4 +++- qmljs_runtime.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/qmljs_math.h b/qmljs_math.h index a586ed16d9..588efb1138 100644 --- a/qmljs_math.h +++ b/qmljs_math.h @@ -41,7 +41,9 @@ #ifndef QMLJS_MATH_H #define QMLJS_MATH_H -#include +#ifndef QMLJS_LLVM_RUNTIME +# include +#endif // QMLJS_LLVM_RUNTIME #include #if 1 //CPU(X86_64) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 2c2207117d..32c9f2e323 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -723,7 +723,7 @@ inline Bool __qmljs_to_boolean(Value value, Context *ctx) case Value::Object_Type: return true; default: // double - if (! value.doubleValue() || qIsNaN(value.doubleValue())) + if (! value.doubleValue() || std::isnan(value.doubleValue())) return false; return true; } -- cgit v1.2.3 From af12a21b1b65d7678eb36c64dc4a2bc3ea331ef1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 18 Oct 2012 08:49:22 +0200 Subject: Inline string creation in the generated JIT code Change-Id: Ifaef87046c083b36222f6204291ee3a61ffcd20b Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 6 ------ qv4isel_masm.cpp | 5 +++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 32c9f2e323..4f4d7a1785 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -104,7 +104,6 @@ void __qmljs_builtin_throw(Value val, Context *context); Value __qmljs_builtin_rethrow(Context *context); // constructors -Value __qmljs_init_string(String *string); Value __qmljs_init_object(Object *object); Value __qmljs_init_closure(IR::Function *clos, Context *ctx); Value __qmljs_init_native_function(void (*code)(Context *), Context *ctx); @@ -691,11 +690,6 @@ extern "C" { // constructors -inline Value __qmljs_init_string(String *value) -{ - return Value::fromString(value); -} - inline Value __qmljs_init_object(Object *object) { return Value::fromObject(object); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 2a03dd350a..cf0e06c5d0 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -374,8 +374,9 @@ void InstructionSelection::visitMove(IR::Move *s) copyValue(t, t2); return; } else if (IR::String *str = s->source->asString()) { - // ### inline - generateFunctionCall(t, __qmljs_init_string, _engine->newString(*str->value)); + Address dest = loadTempAddress(Gpr0, t); + Value v = Value::fromString(_engine->newString(*str->value)); + storeValue(v, dest); return; } else if (IR::Closure *clos = s->source->asClosure()) { generateFunctionCall(t, __qmljs_init_closure, TrustedImmPtr(clos->value), ContextRegister); -- cgit v1.2.3 From e5473687ec707a3f760b8593be2a8d418ba9d1f3 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 18 Oct 2012 08:52:40 +0200 Subject: Cleanup Remove __qmljs_init_object and replace it with Value::fromObject Change-Id: Ibc8bd9e7ecd56c6713dc08add72e5cd35ffea78e Reviewed-by: Simon Hausmann --- main.cpp | 2 +- qmljs_objects.cpp | 8 ++++---- qmljs_runtime.cpp | 14 +++++++------- qmljs_runtime.h | 8 -------- qv4ecmaobjects.cpp | 14 +++++++------- 5 files changed, 19 insertions(+), 27 deletions(-) diff --git a/main.cpp b/main.cpp index 5110e48153..becd301b1b 100644 --- a/main.cpp +++ b/main.cpp @@ -287,7 +287,7 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS ctx->hasUncaughtException = false; if (! ctx->activation.isObject()) - ctx->activation = __qmljs_init_object(new QQmlJS::VM::Object()); + ctx->activation = VM::Value::fromObject(new QQmlJS::VM::Object()); foreach (const QString *local, globalCode->locals) { ctx->activation.objectValue()->setProperty(ctx, *local, QQmlJS::VM::Value::undefinedValue()); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 34e223aff5..285f1643fe 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -190,7 +190,7 @@ void FunctionObject::call(Context *ctx) void FunctionObject::construct(Context *ctx) { - ctx->thisObject = __qmljs_init_object(ctx->engine->newObject()); + ctx->thisObject = Value::fromObject(ctx->engine->newObject()); call(ctx); } @@ -235,7 +235,7 @@ void ScriptFunction::construct(VM::Context *ctx) Value proto = getProperty(ctx, ctx->engine->id_prototype); if (proto.isObject()) obj->prototype = proto.objectValue(); - ctx->thisObject = __qmljs_init_object(obj); + ctx->thisObject = Value::fromObject(obj); function->code(ctx, function->codeData); } @@ -347,8 +347,8 @@ ExecutionEngine::ExecutionEngine() // set up the global object // VM::Object *glo = newObject(/*rootContext*/); - globalObject = __qmljs_init_object(glo); - rootContext->activation = __qmljs_init_object(glo); + globalObject = Value::fromObject(glo); + rootContext->activation = Value::fromObject(glo); glo->setProperty(rootContext, identifier(QStringLiteral("Object")), objectCtor); glo->setProperty(rootContext, identifier(QStringLiteral("String")), stringCtor); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 3c9ca10d3b..6cbc8f60a7 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -327,7 +327,7 @@ void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionO result = Value::undefinedValue(); if (f->needsActivation) - activation = __qmljs_init_object(engine->newActivationObject(this)); + activation = Value::fromObject(engine->newActivationObject(this)); else activation = Value::nullValue(); @@ -387,12 +387,12 @@ extern "C" { Value __qmljs_init_closure(IR::Function *clos, Context *ctx) { - return __qmljs_init_object(ctx->engine->newScriptFunction(ctx, clos)); + return Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); } Value __qmljs_init_native_function(void (*code)(Context *), Context *ctx) { - return __qmljs_init_object(ctx->engine->newNativeFunction(ctx, code)); + return Value::fromObject(ctx->engine->newNativeFunction(ctx, code)); } Value __qmljs_string_literal_undefined(Context *ctx) @@ -952,25 +952,25 @@ Value __qmljs_throw_type_error(Context *ctx) Value __qmljs_new_object(Context *ctx) { - return __qmljs_init_object(ctx->engine->newObject()); + return Value::fromObject(ctx->engine->newObject()); } Value __qmljs_new_boolean_object(Context *ctx, bool boolean) { Value value = Value::fromBoolean(boolean); - return __qmljs_init_object(ctx->engine->newBooleanObject(value)); + return Value::fromObject(ctx->engine->newBooleanObject(value)); } Value __qmljs_new_number_object(Context *ctx, double number) { Value value = Value::fromDouble(number); - return __qmljs_init_object(ctx->engine->newNumberObject(value)); + return Value::fromObject(ctx->engine->newNumberObject(value)); } Value __qmljs_new_string_object(Context *ctx, String *string) { Value value = Value::fromString(string); - return __qmljs_init_object(ctx->engine->newStringObject(value)); + return Value::fromObject(ctx->engine->newStringObject(value)); } void __qmljs_set_property(Context *ctx, Value object, String *name, Value value) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 4f4d7a1785..d49a217c37 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -104,7 +104,6 @@ void __qmljs_builtin_throw(Value val, Context *context); Value __qmljs_builtin_rethrow(Context *context); // constructors -Value __qmljs_init_object(Object *object); Value __qmljs_init_closure(IR::Function *clos, Context *ctx); Value __qmljs_init_native_function(void (*code)(Context *), Context *ctx); @@ -688,13 +687,6 @@ struct Context { extern "C" { -// constructors - -inline Value __qmljs_init_object(Object *object) -{ - return Value::fromObject(object); -} - // type conversion and testing inline Value __qmljs_to_primitive(Value value, Context *ctx, int typeHint) { diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index c2f349b596..f081f0689a 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -521,12 +521,12 @@ ObjectCtor::ObjectCtor(Context *scope) void ObjectCtor::construct(Context *ctx) { - ctx->thisObject = __qmljs_init_object(ctx->engine->newObject()); + ctx->thisObject = Value::fromObject(ctx->engine->newObject()); } void ObjectCtor::call(Context *ctx) { - ctx->result = __qmljs_init_object(ctx->engine->newObject()); + ctx->result = Value::fromObject(ctx->engine->newObject()); } Value ObjectCtor::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) @@ -705,7 +705,7 @@ void StringCtor::construct(Context *ctx) value = Value::fromString(ctx->argument(0).toString(ctx)); else value = Value::fromString(ctx, QString()); - ctx->thisObject = __qmljs_init_object(ctx->engine->newStringObject(value)); + ctx->thisObject = Value::fromObject(ctx->engine->newStringObject(value)); } void StringCtor::call(Context *ctx) @@ -1015,7 +1015,7 @@ NumberCtor::NumberCtor(Context *scope) void NumberCtor::construct(Context *ctx) { const double n = ctx->argument(0).toNumber(ctx); - ctx->thisObject = __qmljs_init_object(ctx->engine->newNumberObject(Value::fromDouble(n))); + ctx->thisObject = Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(n))); } void NumberCtor::call(Context *ctx) @@ -1194,7 +1194,7 @@ BooleanCtor::BooleanCtor(Context *scope) void BooleanCtor::construct(Context *ctx) { const double n = ctx->argument(0).toBoolean(ctx); - ctx->thisObject = __qmljs_init_object(ctx->engine->newBooleanObject(Value::fromBoolean(n))); + ctx->thisObject = Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n))); } void BooleanCtor::call(Context *ctx) @@ -2428,12 +2428,12 @@ RegExpCtor::RegExpCtor(Context *scope) void RegExpCtor::construct(Context *ctx) { - ctx->thisObject = __qmljs_init_object(ctx->engine->newStringObject(Value::undefinedValue())); + ctx->thisObject = Value::fromObject(ctx->engine->newStringObject(Value::undefinedValue())); } void RegExpCtor::call(Context *ctx) { - ctx->result = __qmljs_init_object(ctx->engine->newRegExpObject(Value::undefinedValue())); + ctx->result = Value::fromObject(ctx->engine->newRegExpObject(Value::undefinedValue())); } void RegExpPrototype::init(Context *ctx, const Value &ctor) -- cgit v1.2.3 From 45de4917a1d977aefb34a0b52bd44858992d89eb Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 18 Oct 2012 09:52:38 +0200 Subject: Clean up the relational operators Change-Id: I83cf1dd9204b95b0238ed2476cf2a3800b25a017 Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 139 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 72 insertions(+), 67 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index d49a217c37..079d0f58ed 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -1084,81 +1084,32 @@ inline Value __qmljs_ushr(Value left, Value right, Context *ctx) inline Value __qmljs_gt(Value left, Value right, Context *ctx) { - if (left.isInteger() && right.isInteger()) - return Value::fromBoolean(left.integerValue() > right.integerValue()); - if (left.isNumber() && right.isNumber()) { - return Value::fromBoolean(left.asDouble() > right.asDouble()); - } else { - Value result = __qmljs_compare(left, right, ctx, false); - - if (result.isUndefined()) - result = Value::fromBoolean(false); - return result; - } + return Value::fromBoolean(__qmljs_cmp_gt(left, right, ctx)); } inline Value __qmljs_lt(Value left, Value right, Context *ctx) { - if (left.isInteger() && right.isInteger()) - return Value::fromBoolean(left.integerValue() < right.integerValue()); - if (left.isNumber() && right.isNumber()) { - return Value::fromBoolean(left.asDouble() < right.asDouble()); - } else { - Value result = __qmljs_compare(left, right, ctx, true); - - if (result.isUndefined()) - result = Value::fromBoolean(false); - return result; - } + return Value::fromBoolean(__qmljs_cmp_lt(left, right, ctx)); } inline Value __qmljs_ge(Value left, Value right, Context *ctx) { - if (left.isInteger() && right.isInteger()) - return Value::fromBoolean(left.integerValue() >= right.integerValue()); - if (left.isNumber() && right.isNumber()) { - return Value::fromBoolean(left.asDouble() >= right.asDouble()); - } else { - Value result = __qmljs_compare(right, left, ctx, false); - - bool r = ! (result.isUndefined() || (result.isBoolean() && result.booleanValue())); - return Value::fromBoolean(r); - } + return Value::fromBoolean(__qmljs_cmp_ge(left, right, ctx)); } inline Value __qmljs_le(Value left, Value right, Context *ctx) { - if (left.isInteger() && right.isInteger()) - return Value::fromBoolean(left.integerValue() <= right.integerValue()); - if (left.isNumber() && right.isNumber()) { - return Value::fromBoolean(left.asDouble() <= right.asDouble()); - } else { - Value result = __qmljs_compare(right, left, ctx, true); - - bool r = ! (result.isUndefined() || (result.isBoolean() && result.booleanValue())); - return Value::fromBoolean(r); - } + return Value::fromBoolean(__qmljs_cmp_le(left, right, ctx)); } inline Value __qmljs_eq(Value left, Value right, Context *ctx) { - if (left.val == right.val) - return Value::fromBoolean(true); - if (left.isNumber() && right.isNumber()) { - return Value::fromBoolean(left.asDouble() == right.asDouble()); - } else if (left.isString() && right.isString()) { - return Value::fromBoolean(__qmljs_string_equal(ctx, left.stringValue(), right.stringValue())); - } else { - bool r = __qmljs_equal(left, right, ctx); - return Value::fromBoolean(r); - } + return Value::fromBoolean(__qmljs_cmp_eq(left, right, ctx)); } inline Value __qmljs_ne(Value left, Value right, Context *ctx) { - Value result = __qmljs_eq(left, right, ctx); - result.int_32 = !(bool)result.int_32; - return result; + return Value::fromBoolean(!__qmljs_cmp_eq(left, right, ctx)); } inline Value __qmljs_se(Value left, Value right, Context *ctx) @@ -1175,38 +1126,92 @@ inline Value __qmljs_sne(Value left, Value right, Context *ctx) inline Bool __qmljs_cmp_gt(Value left, Value right, Context *ctx) { - Value v = __qmljs_gt(left, right, ctx); - return v.booleanValue(); + left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); + right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); + + if (left.isInteger() && right.isInteger()) + return left.integerValue() > right.integerValue(); + if (left.isNumber() && right.isNumber()) { + return left.asDouble() > right.asDouble(); + } else if (left.isString() && right.isString()) { + return __qmljs_string_compare(ctx, right.stringValue(), left.stringValue()); + } else { + double l = __qmljs_to_number(left, ctx); + double r = __qmljs_to_number(right, ctx); + return l > r; + } } inline Bool __qmljs_cmp_lt(Value left, Value right, Context *ctx) { - Value v = __qmljs_lt(left, right, ctx); - return v.booleanValue(); + left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); + right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); + + if (left.isInteger() && right.isInteger()) + return left.integerValue() < right.integerValue(); + if (left.isNumber() && right.isNumber()) { + return left.asDouble() < right.asDouble(); + } else if (left.isString() && right.isString()) { + return __qmljs_string_compare(ctx, left.stringValue(), right.stringValue()); + } else { + double l = __qmljs_to_number(left, ctx); + double r = __qmljs_to_number(right, ctx); + return l < r; + } } inline Bool __qmljs_cmp_ge(Value left, Value right, Context *ctx) { - Value v = __qmljs_ge(left, right, ctx); - return v.booleanValue(); + left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); + right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); + + if (left.isInteger() && right.isInteger()) + return left.integerValue() >= right.integerValue(); + if (left.isNumber() && right.isNumber()) { + return left.asDouble() >= right.asDouble(); + } else if (left.isString() && right.isString()) { + return !__qmljs_string_compare(ctx, left.stringValue(), right.stringValue()); + } else { + double l = __qmljs_to_number(left, ctx); + double r = __qmljs_to_number(right, ctx); + return l >= r; + } } inline Bool __qmljs_cmp_le(Value left, Value right, Context *ctx) { - Value v = __qmljs_le(left, right, ctx); - return v.booleanValue(); + left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); + right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); + + if (left.isInteger() && right.isInteger()) + return left.integerValue() <= right.integerValue(); + if (left.isNumber() && right.isNumber()) { + return left.asDouble() <= right.asDouble(); + } else if (left.isString() && right.isString()) { + return !__qmljs_string_compare(ctx, right.stringValue(), left.stringValue()); + } else { + double l = __qmljs_to_number(left, ctx); + double r = __qmljs_to_number(right, ctx); + return l <= r; + } } inline Bool __qmljs_cmp_eq(Value left, Value right, Context *ctx) { - Value v = __qmljs_eq(left, right, ctx); - return v.booleanValue(); + // need to test for doubles first as NaN != NaN + if (left.isDouble() && right.isDouble()) + return left.doubleValue() == right.doubleValue(); + if (left.val == right.val) + return true; + if (left.isString() && right.isString()) + return __qmljs_string_equal(ctx, left.stringValue(), right.stringValue()); + + return __qmljs_equal(left, right, ctx); } inline Bool __qmljs_cmp_ne(Value left, Value right, Context *ctx) { - Value v = __qmljs_ne(left, right, ctx); - return v.booleanValue(); + return !__qmljs_cmp_eq(left, right, ctx); } inline Bool __qmljs_cmp_se(Value left, Value right, Context *ctx) -- cgit v1.2.3 From 44dc2d5b309b244efff98b1bb08050c754b4313e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 18 Oct 2012 10:43:49 +0200 Subject: Fix unused parameter/variable warnings. Change-Id: I1e1fad753b99a52a1e65dcf38f64c394fa506735 Reviewed-by: Simon Hausmann --- masm/stubs/WTFStubs.cpp | 2 +- moth/qv4isel_moth.cpp | 12 +++++++++++- qmljs_runtime.h | 2 +- qv4codegen.cpp | 2 +- qv4ecmaobjects.cpp | 4 ++-- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/masm/stubs/WTFStubs.cpp b/masm/stubs/WTFStubs.cpp index d421e9094f..5c45e6cc2a 100644 --- a/masm/stubs/WTFStubs.cpp +++ b/masm/stubs/WTFStubs.cpp @@ -101,7 +101,7 @@ void dataLogString(const char* str) extern "C" { -void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion) +void WTFReportAssertionFailure(const char* /*file*/, int /*line*/, const char* /*function*/, const char* /*assertion*/) { } diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index d37bcdaf65..2a65f77a61 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -4,7 +4,7 @@ using namespace QQmlJS; using namespace QQmlJS::Moth; -InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, +InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module * /*module*/, uchar *code) : _engine(engine), _code(code), _ccode(code) { @@ -64,6 +64,7 @@ void InstructionSelection::visitExp(IR::Exp *s) Q_UNIMPLEMENTED(); } } else if (IR::Member *m = c->base->asMember()) { + Q_UNUSED(m); Q_UNIMPLEMENTED(); } else if (IR::Temp *t = c->base->asTemp()) { Instruction::LoadTemp load; @@ -202,10 +203,12 @@ ALUFunction aluOpFunction(IR::AluOp op) void InstructionSelection::simpleMove(IR::Move *s) { if (IR::Name *n = s->target->asName()) { + Q_UNUSED(n); qWarning("NAME"); } else if (IR::Temp *t = s->target->asTemp()) { if (IR::Name *n = s->source->asName()) { + Q_UNUSED(n); qWarning(" NAME"); } else if (IR::Const *c = s->source->asConst()) { switch (c->type) { @@ -241,12 +244,16 @@ void InstructionSelection::simpleMove(IR::Move *s) load.value = clos->value; addInstruction(load); } else if (IR::New *ctor = s->source->asNew()) { + Q_UNUSED(ctor); qWarning(" NEW"); } else if (IR::Member *m = s->source->asMember()) { + Q_UNUSED(m); qWarning(" MEMBER"); } else if (IR::Subscript *ss = s->source->asSubscript()) { + Q_UNUSED(ss); qWarning(" SUBSCRIPT"); } else if (IR::Unop *u = s->source->asUnop()) { + Q_UNUSED(u); qWarning(" UNOP"); } else if (IR::Binop *b = s->source->asBinop()) { Instruction::Binop binop; @@ -255,6 +262,7 @@ void InstructionSelection::simpleMove(IR::Move *s) binop.rhsTempIndex = b->right->index; addInstruction(binop); } else if (IR::Call *c = s->source->asCall()) { + Q_UNUSED(c); qWarning(" CALL"); } @@ -263,8 +271,10 @@ void InstructionSelection::simpleMove(IR::Move *s) addInstruction(st); } else if (IR::Member *m = s->target->asMember()) { + Q_UNUSED(m); qWarning("MEMBER"); } else if (IR::Subscript *ss = s->target->asSubscript()) { + Q_UNUSED(ss); qWarning("SUBSCRIPT"); } else { Q_UNREACHABLE(); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 079d0f58ed..fc9d49ae77 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -862,7 +862,7 @@ inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *v } */ -inline Bool __qmljs_is_callable(Value value, Context *ctx) +inline Bool __qmljs_is_callable(Value value, Context * /*ctx*/) { if (value.isObject()) return __qmljs_is_function(value); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 69529cd695..252c67293f 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1282,7 +1282,7 @@ bool Codegen::visit(VoidExpression *ast) return false; } -bool Codegen::visit(FunctionDeclaration *ast) +bool Codegen::visit(FunctionDeclaration * /*ast*/) { _expr.accept(nx); return false; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index f081f0689a..1ff21acef5 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1606,7 +1606,7 @@ void ArrayPrototype::method_forEach(Context *ctx) args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); + /*Value r =*/ __qmljs_call_value(ctx, thisArg, callback, args, 3); } } } else { @@ -1777,7 +1777,7 @@ void FunctionPrototype::method_toString(Context *ctx) void FunctionPrototype::method_apply(Context *ctx) { - if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { + if (/*FunctionObject *fun =*/ ctx->thisObject.asFunctionObject()) { Value thisObject = ctx->argument(0).toObject(ctx); if (thisObject.isNull() || thisObject.isUndefined()) -- cgit v1.2.3 From afee906baf566cdc73fbbef5ef08d745356f6637 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 18 Oct 2012 10:44:09 +0200 Subject: Fix insecure use of format string. Change-Id: I6cd282d5780e418bea7a07d4639c035a98b3ed65 Reviewed-by: Simon Hausmann --- qv4isel_masm.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index cf0e06c5d0..9741f0169e 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -147,9 +147,7 @@ void InstructionSelection::operator()(IR::Function *function) #endif QByteArray name = _function->name->toUtf8(); - if (name.startsWith('%')) - name.prepend('%'); - _function->codeRef = linkBuffer.finalizeCodeWithDisassembly(name.data()); + _function->codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); WTF::setDataFile(stderr); #if OS(LINUX) -- cgit v1.2.3 From 0e92d6eac25bed9dedde582f23be51cc27e2e158 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 18 Oct 2012 10:46:31 +0200 Subject: Fix LLVM code generation to call the right methods. Change-Id: Ia63342d2808ca2e3cdd5b19e94054786cc34c495 Reviewed-by: Simon Hausmann --- qv4isel_llvm.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 7971133948..c2cc4c1d2a 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -409,7 +409,7 @@ void LLVMInstructionSelection::visitName(IR::Name *e) _llvmFunction->arg_begin(), result); } else { llvm::Value *name = getIdentifier(*e->id); - CreateCall3(_llvmModule->getFunction("__qmljs_get_activation_property"), + CreateCall3(_llvmModule->getFunction("__qmljs_llvm_get_activation_property"), _llvmFunction->arg_begin(), result, name); } _llvmValue = CreateLoad(result); @@ -427,7 +427,7 @@ void LLVMInstructionSelection::visitClosure(IR::Closure *e) { llvm::Value *tmp = newLLVMTemp(_valueTy); llvm::Value *clos = getLLVMFunction(e->value); - CreateCall3(_llvmModule->getFunction("__qmljs_init_native_function"), + CreateCall3(_llvmModule->getFunction("__qmljs_llvm_init_native_function"), _llvmFunction->arg_begin(), tmp, clos); _llvmValue = CreateLoad(tmp); } @@ -719,7 +719,7 @@ void LLVMInstructionSelection::genCallName(IR::Call *e, llvm::Value *result) int argc = 0; llvm::Value *args = genArguments(e->args, argc); - CreateCall5(_llvmModule->getFunction("__qmljs_call_activation_property"), + CreateCall5(_llvmModule->getFunction("__qmljs_llvm_call_activation_property"), _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); _llvmValue = CreateLoad(result); -- cgit v1.2.3 From e0d4df2b3fca006267c61c5c95c54db004795c5b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 18 Oct 2012 10:57:00 +0200 Subject: Silence signed/unsigned comparison warning. Change-Id: Id23e48b370a73baabcc6875b51067f60dadec0c4 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 6cbc8f60a7..9e9d3640ff 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -449,7 +449,7 @@ Value __qmljs_delete_subscript(Context *ctx, Value base, Value index) else if (index.isDouble()) n = index.doubleValue(); if (n >= 0) { - if (n < a->value.size()) { + if (n < (int) a->value.size()) { a->value.assign(n, Value::undefinedValue()); return Value::fromBoolean(true); } -- cgit v1.2.3 From 4dfe770add60b91ccd799d53a6af73be1072876d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 18 Oct 2012 10:35:27 +0200 Subject: Clean up #ifdefs in storeValue Use VALUE_FITS_IN_REGISTER instead of hard-coding CPU architectures Change-Id: I6d5caa6b2cd8e379517d83fdebef46217f79a8d5 Reviewed-by: Lars Knoll --- qv4isel_masm_p.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index ee22f39eba..bb91fa044b 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -534,14 +534,12 @@ private: void storeValue(VM::Value value, Address destination) { -#if CPU(X86_64) +#ifdef VALUE_FITS_IN_REGISTER storePtr(TrustedImmPtr((void *)value.val), destination); -#elif CPU(X86) +#else store32(TrustedImm32(value.int_32), destination); destination.offset += 4; store32(TrustedImm32(value.tag), destination); -#else -#error "Missing implementation" #endif } -- cgit v1.2.3 From 3fd7a3a8e9bb3c42c1ecb954d99ee3b4218c011e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 18 Oct 2012 11:07:10 +0200 Subject: Moved LLVM specific code out of main.cpp. Change-Id: I16c79667625d5034acb91cec13c22ed58b74984f Reviewed-by: Simon Hausmann --- main.cpp | 65 ++++-------------------------- qv4_llvm_p.h | 43 ++++++++++++++++++++ qv4isel_llvm.cpp | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++---- qv4isel_llvm_p.h | 14 ++++++- v4.pro | 3 +- 5 files changed, 175 insertions(+), 68 deletions(-) create mode 100644 qv4_llvm_p.h diff --git a/main.cpp b/main.cpp index becd301b1b..261aeff4f7 100644 --- a/main.cpp +++ b/main.cpp @@ -40,22 +40,7 @@ ****************************************************************************/ #ifndef QMLJS_NO_LLVM -// These includes have to come first, because WTF/Platform.h defines some macros -// with very unfriendly names that collide with class fields in LLVM. -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include - -# include "qv4isel_llvm_p.h" +# include "qv4_llvm_p.h" #endif #include "qmljs_objects.h" @@ -138,50 +123,16 @@ void compile(const QString &fileName, const QString &source) << ": error: " << qPrintable(m.message) << std::endl; } - if (parsed) { - using namespace AST; - Program *program = AST::cast(parser.rootNode()); + if (!parsed) + return; - Codegen cg; - /*IR::Function *globalCode =*/ cg(program, &module); + using namespace AST; + Program *program = AST::cast(parser.rootNode()); - LLVMInstructionSelection llvmIsel(llvm::getGlobalContext()); - if (llvm::Module *llvmModule = llvmIsel.getLLVMModule(&module)) { - llvm::PassManager PM; + Codegen cg; + /*IR::Function *globalCode =*/ cg(program, &module); - const std::string triple = llvm::sys::getDefaultTargetTriple(); - - LLVMInitializeX86TargetInfo(); - LLVMInitializeX86Target(); - LLVMInitializeX86AsmPrinter(); - LLVMInitializeX86AsmParser(); - LLVMInitializeX86Disassembler(); - LLVMInitializeX86TargetMC(); - - std::string err; - const llvm::Target *target = llvm::TargetRegistry::lookupTarget(triple, err); - if (! err.empty()) { - std::cerr << err << ", triple: " << triple << std::endl; - assert(!"cannot create target for the host triple"); - } - - - std::string cpu; - std::string features; - llvm::TargetOptions options; - llvm::TargetMachine *targetMachine = target->createTargetMachine(triple, cpu, features, options, llvm::Reloc::PIC_); - assert(targetMachine); - - llvm::formatted_raw_ostream out(llvm::outs()); - PM.add(llvm::createScalarReplAggregatesPass()); - PM.add(llvm::createInstructionCombiningPass()); - PM.add(llvm::createGlobalOptimizerPass()); - PM.add(llvm::createFunctionInliningPass(25)); - targetMachine->addPassesToEmitFile(PM, out, llvm::TargetMachine::CGFT_AssemblyFile); - PM.run(*llvmModule); - delete llvmModule; - } - } + compileWithLLVM(&module, fileName); } int compileFiles(const QStringList &files) diff --git a/qv4_llvm_p.h b/qv4_llvm_p.h new file mode 100644 index 0000000000..729da2dada --- /dev/null +++ b/qv4_llvm_p.h @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QV4_LLVM_P_H +#define QV4_LLVM_P_H + +#include "qv4ir_p.h" + +#include + +namespace QQmlJS { + +void compileWithLLVM(IR::Module *module, const QString &fileName); + +} // QQmlJS + +#endif // QV4_LLVM_P_H diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index c2cc4c1d2a..f6e79c963a 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -39,16 +39,117 @@ ** ****************************************************************************/ -#include "qv4isel_llvm_p.h" -#include "qv4ir_p.h" - -#include +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-parameter" +#endif // __clang__ -#include -#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif // __clang__ + +#include +#include +#include +#include #include +#include + +// These includes have to come last, because WTF/Platform.h defines some macros +// with very unfriendly names that collide with class fields in LLVM. +#include "qv4isel_llvm_p.h" +#include "qv4ir_p.h" + +namespace QQmlJS { + +void compileWithLLVM(IR::Module *module, const QString &fileName) +{ + Q_ASSERT(module); + + const QString moduleName = QFileInfo(fileName).fileName(); + + LLVMInstructionSelection llvmIsel(llvm::getGlobalContext()); + llvm::Module *llvmModule = llvmIsel.getLLVMModule(module, moduleName); + if (!llvmModule) + return; + + // TODO: if output type is .ll, print the module to file + + llvm::PassManager PM; + + const std::string triple = llvm::sys::getDefaultTargetTriple(); + + LLVMInitializeX86TargetInfo(); + LLVMInitializeX86Target(); + LLVMInitializeX86AsmPrinter(); + LLVMInitializeX86AsmParser(); + LLVMInitializeX86Disassembler(); + LLVMInitializeX86TargetMC(); + + std::string err; + const llvm::Target *target = llvm::TargetRegistry::lookupTarget(triple, err); + if (! err.empty()) { + std::cerr << err << ", triple: " << triple << std::endl; + assert(!"cannot create target for the host triple"); + } + + std::string cpu; + std::string features; + llvm::TargetOptions options; + llvm::TargetMachine *targetMachine = target->createTargetMachine(triple, cpu, features, options, llvm::Reloc::PIC_); + assert(targetMachine); + + llvm::TargetMachine::CodeGenFileType ft; + QString ofName; + + ft = llvm::TargetMachine::CGFT_ObjectFile; + ofName = fileName + QLatin1String(".o"); + + // TODO: +// ft = llvm::TargetMachine::CGFT_AssemblyFile; +// ofName = fileName + QLatin1String(".s"); + + llvm::raw_fd_ostream dest(ofName.toUtf8().constData(), err, llvm::raw_fd_ostream::F_Binary); + llvm::formatted_raw_ostream destf(dest); + if (!err.empty()) { + std::cerr << err << std::endl; + delete llvmModule; + } + + PM.add(llvm::createScalarReplAggregatesPass()); + PM.add(llvm::createInstructionCombiningPass()); + PM.add(llvm::createGlobalOptimizerPass()); + PM.add(llvm::createFunctionInliningPass(25)); + if (targetMachine->addPassesToEmitFile(PM, destf, ft)) { + std::cerr << err << " (probably no DataLayout in TargetMachine)" << std::endl; + } else { + PM.run(*llvmModule); + + destf.flush(); + dest.flush(); + } + + delete llvmModule; +} + +} // QQmlJS using namespace QQmlJS; @@ -72,9 +173,10 @@ LLVMInstructionSelection::LLVMInstructionSelection(llvm::LLVMContext &context) { } -llvm::Module *LLVMInstructionSelection::getLLVMModule(IR::Module *module) +llvm::Module *LLVMInstructionSelection::getLLVMModule(IR::Module *module, const QString &moduleName) { - llvm::Module *llvmModule = new llvm::Module("a.out", getContext()); + llvm::StringRef moduleId(moduleName.toUtf8().constData()); + llvm::Module *llvmModule = new llvm::Module(moduleId, getContext()); qSwap(_llvmModule, llvmModule); _numberTy = getDoubleTy(); diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 6c661ca16d..a2446a5aa0 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -41,10 +41,20 @@ #ifndef QV4ISEL_LLVM_P_H #define QV4ISEL_LLVM_P_H -#include "qv4ir_p.h" +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-parameter" +#endif // __clang__ + #include #include +#ifdef __clang__ +# pragma clang diagnostic pop +#endif // __clang__ + +#include "qv4ir_p.h" + namespace QQmlJS { class LLVMInstructionSelection: @@ -55,7 +65,7 @@ class LLVMInstructionSelection: public: LLVMInstructionSelection(llvm::LLVMContext &context); - llvm::Module *getLLVMModule(IR::Module *module); + llvm::Module *getLLVMModule(IR::Module *module, const QString &moduleName); llvm::Function *getLLVMFunction(IR::Function *function); llvm::Function *compileLLVMFunction(IR::Function *function); llvm::BasicBlock *getLLVMBasicBlock(IR::BasicBlock *block); diff --git a/v4.pro b/v4.pro index b51850e858..2740936c55 100644 --- a/v4.pro +++ b/v4.pro @@ -38,7 +38,8 @@ SOURCES += \ qv4isel_llvm.cpp HEADERS += \ - qv4isel_llvm_p.h + qv4isel_llvm_p.h \ + qv4_llvm_p.h INCLUDEPATH += \ $$system($$LLVM_CONFIG --includedir) -- cgit v1.2.3 From 19af71d4bf7801b29ffe415f6c7e3ae738995533 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 18 Oct 2012 12:04:22 +0200 Subject: Remove unused method Change-Id: I780afc37dd651922ca66770a69e9ae2050d198c4 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 9e9d3640ff..2522bd57d4 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -1109,32 +1109,6 @@ Value __qmljs_get_thisObject(Context *ctx) return ctx->engine->globalObject; } -Value __qmljs_compare(Value x, Value y, Context *ctx, bool leftFirst) -{ - Value px, py; - - if (leftFirst) { - px = __qmljs_to_primitive(x, ctx, NUMBER_HINT); - py = __qmljs_to_primitive(y, ctx, NUMBER_HINT); - } else { - px = __qmljs_to_primitive(x, ctx, NUMBER_HINT); - py = __qmljs_to_primitive(y, ctx, NUMBER_HINT); - } - - if (px.isString() && py.isString()) { - bool r = __qmljs_string_compare(ctx, px.stringValue(), py.stringValue()); - return Value::fromBoolean(r); - } else { - double nx = __qmljs_to_number(px, ctx); - double ny = __qmljs_to_number(py, ctx); - if (std::isnan(nx) || std::isnan(ny)) { - return Value::undefinedValue(); - } else { - return Value::fromBoolean(nx < ny); - } - } -} - uint __qmljs_equal(Value x, Value y, Context *ctx) { if (x.type() == y.type()) { -- cgit v1.2.3 From 86a31a3dff5fc6848f9a9a7b5798fb87e86b1fe1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 18 Oct 2012 12:04:51 +0200 Subject: Fix integer arithmetics The inline assembly could create incorrect results for overflows, esp when multiplying. Also fix the return type of Value::integerValue() Change-Id: I4c8f195b37bcbb8fd4f0f3d0cd04a8cf73f193cd Reviewed-by: Simon Hausmann --- qmljs_math.h | 27 +++++++++++++++------------ qmljs_runtime.h | 11 ++++++----- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/qmljs_math.h b/qmljs_math.h index 588efb1138..7823eeccc7 100644 --- a/qmljs_math.h +++ b/qmljs_math.h @@ -51,45 +51,48 @@ static inline Value add_int32(int a, int b) { quint8 overflow = 0; + int aa = a; asm ("addl %2, %1\n" "seto %0" - : "=q" (overflow), "=r" (a) - : "r" (b), "1" (a) + : "=q" (overflow), "=r" (aa) + : "r" (b), "1" (aa) : ); if (!overflow) - return Value::fromInt32(a); + return Value::fromInt32(aa); return Value::fromDouble((double)a + (double)b); } static inline Value sub_int32(int a, int b) { quint8 overflow = 0; + int aa = a; asm ("subl %2, %1\n" "seto %0" - : "=q" (overflow), "=r" (a) - : "r" (b), "1" (a) + : "=q" (overflow), "=r" (aa) + : "r" (b), "1" (aa) : ); if (!overflow) - return Value::fromInt32(a); + return Value::fromInt32(aa); return Value::fromDouble((double)a - (double)b); } static inline Value mul_int32(int a, int b) { quint8 overflow = 0; + int aa = a; - asm ("imul %2, %1\n" - "seto %0" - : "=q" (overflow), "=r" (a) - : "r" (b), "1" (a) - : + asm ("imul %2\n" + "setc %0" + : "=q" (overflow), "=a" (aa) + : "r" (b), "1" (aa) + : "edx" ); if (!overflow) - return Value::fromInt32(a); + return Value::fromInt32(aa); return Value::fromDouble((double)a * (double)b); } #endif diff --git a/qmljs_runtime.h b/qmljs_runtime.h index fc9d49ae77..6907eb4708 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -162,7 +162,6 @@ Value __qmljs_to_object(Value value, Context *ctx); Bool __qmljs_is_callable(Value value, Context *ctx); Value __qmljs_default_value(Value value, Context *ctx, int typeHint); -Value __qmljs_compare(Value left, Value right, Context *ctx, bool leftFlag); Bool __qmljs_equal(Value x, Value y, Context *ctx); Bool __qmljs_strict_equal(Value x, Value y, Context *ctx); @@ -334,7 +333,7 @@ template <> struct ValueBase<4> : public ValueData return int_32; return dbl; } - double integerValue() const { + int integerValue() const { return int_32; } @@ -400,7 +399,7 @@ template <> struct ValueBase<8> : public ValueData return int_32; return dbl; } - double integerValue() const { + int integerValue() const { return int_32; } @@ -939,8 +938,7 @@ inline Value __qmljs_bit_or(Value left, Value right, Context *ctx) { int lval = __qmljs_to_int32(left, ctx); int rval = __qmljs_to_int32(right, ctx); - // ### changing this to fromInt32() breaks crypto.js - return Value::fromDouble(lval | rval); + return Value::fromInt32(lval | rval); } inline Value __qmljs_bit_xor(Value left, Value right, Context *ctx) @@ -1054,6 +1052,9 @@ inline Value __qmljs_div(Value left, Value right, Context *ctx) inline Value __qmljs_mod(Value left, Value right, Context *ctx) { + if (left.isInteger() && right.isInteger()) + return Value::fromInt32(left.integerValue() % right.integerValue()); + double lval = __qmljs_to_number(left, ctx); double rval = __qmljs_to_number(right, ctx); return Value::fromDouble(fmod(lval, rval)); -- cgit v1.2.3 From 35fb066f83d6c792a0491e927bbc4d90eca2be36 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 18 Oct 2012 12:15:48 +0200 Subject: Remove unused, commented out methods They don't make sense with the calling convention we now have. Change-Id: I92f773c96588dd276227ba9bbe7414dbd7752db7 Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 72 --------------------------------------------------------- 1 file changed, 72 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 6907eb4708..2e7b7d5bc9 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -206,21 +206,6 @@ Value __qmljs_sne(Value left, Value right, Context *ctx); Value __qmljs_add_helper(Value left, Value right, Context *ctx); -/* - unused and probably don't make sense with the new calling convention -void __qmljs_inplace_bit_and(Context *ctx, Value *result, Value *value); -void __qmljs_inplace_bit_or(Context *ctx, Value *result, Value *value); -void __qmljs_inplace_bit_xor(Context *ctx, Value *result, Value *value); -void __qmljs_inplace_add(Context *ctx, Value *result, Value *value); -void __qmljs_inplace_sub(Context *ctx, Value *result, Value *value); -void __qmljs_inplace_mul(Context *ctx, Value *result, Value *value); -void __qmljs_inplace_div(Context *ctx, Value *result, Value *value); -void __qmljs_inplace_mod(Context *ctx, Value *result, Value *value); -void __qmljs_inplace_shl(Context *ctx, Value *result, Value *value); -void __qmljs_inplace_shr(Context *ctx, Value *result, Value *value); -void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value); -*/ - void __qmljs_inplace_bit_and_name(Value value, String *name, Context *ctx); void __qmljs_inplace_bit_or_name(Value value, String *name, Context *ctx); void __qmljs_inplace_bit_xor_name(Value value, String *name, Context *ctx); @@ -955,63 +940,6 @@ inline Value __qmljs_bit_and(Value left, Value right, Context *ctx) return Value::fromInt32(lval & rval); } -/* -inline void __qmljs_inplace_bit_and(Context *ctx, Value *result, Value *value) -{ - *result = __qmljs_bit_and(*result, *value, ctx); -} - -inline void __qmljs_inplace_bit_or(Context *ctx, Value *result, Value *value) -{ - *result = __qmljs_bit_or(*result, *value, ctx); -} - -inline void __qmljs_inplace_bit_xor(Context *ctx, Value *result, Value *value) -{ - *result = __qmljs_bit_xor(*result, *value, ctx); -} - -inline void __qmljs_inplace_add(Context *ctx, Value *result, Value *value) -{ - *result = __qmljs_add(*result, *value, ctx); -} - -inline void __qmljs_inplace_sub(Context *ctx, Value *result, Value *value) -{ - *result = __qmljs_sub(*result, *value, ctx); -} - -inline void __qmljs_inplace_mul(Context *ctx, Value *result, Value *value) -{ - *result = __qmljs_mul(*result, *value, ctx); -} - -inline void __qmljs_inplace_div(Context *ctx, Value *result, Value *value) -{ - *result = __qmljs_div(*result, *value, ctx); -} - -inline void __qmljs_inplace_mod(Context *ctx, Value *result, Value *value) -{ - *result = __qmljs_mod(*result, *value, ctx); -} - -inline void __qmljs_inplace_shl(Context *ctx, Value *result, Value *value) -{ - *result = __qmljs_shl(*result, *value, ctx); -} - -inline void __qmljs_inplace_shr(Context *ctx, Value *result, Value *value) -{ - *result = __qmljs_shr(*result, *value, ctx); -} - -inline void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value) -{ - *result = __qmljs_ushr(*result, *value, ctx); -} -*/ - inline Value __qmljs_add(Value left, Value right, Context *ctx) { if (left.isInteger() & right.isInteger()) -- cgit v1.2.3 From 82ee7d361ae21001741d5e4eb42a25789b30b948 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 18 Oct 2012 14:39:59 +0200 Subject: Optimise function calls a little. Change-Id: I906a4f27bf47ae0ae088ae40a747b28ba827e10a Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 114 +++++++++++++++++++++++++----------------------- qmljs_runtime.h | 1 + qv4ecmaobjects.cpp | 125 ++++++++++++++++++++++++++++------------------------- 3 files changed, 128 insertions(+), 112 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 2522bd57d4..026cef0a26 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -924,21 +924,25 @@ Value __qmljs_object_default_value(Context *ctx, Value object, int typeHint) if (typeHint == NUMBER_HINT) qSwap(meth1, meth2); - Object *oo = object.asObject(); - assert(oo != 0); + assert(object.isObject()); + Object *oo = object.objectValue(); Value conv = oo->getProperty(ctx, meth1); - if (!conv.isUndefined() && conv.isFunctionObject()) { - Value r = __qmljs_call_value(ctx, object, conv, 0, 0); - if (r.isPrimitive()) - return r; + if (!conv.isUndefined()) { + if (FunctionObject *f = conv.asFunctionObject()) { + Value r = __qmljs_call_function(ctx, object, f, 0, 0); + if (r.isPrimitive()) + return r; + } } conv = oo->getProperty(ctx, meth2); - if (!conv.isUndefined() && conv.isFunctionObject()) { - Value r = __qmljs_call_value(ctx, object, conv, 0, 0); - if (r.isPrimitive()) - return r; + if (!conv.isUndefined()) { + if (FunctionObject *f = conv.asFunctionObject()) { + Value r = __qmljs_call_function(ctx, object, f, 0, 0); + if (r.isPrimitive()) + return r; + } } return Value::undefinedValue(); @@ -1006,42 +1010,42 @@ void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR: Value __qmljs_get_element(Context *ctx, Value object, Value index) { - if (object.isString() && index.isNumber()) { - const QString s = object.stringValue()->toQString().mid(index.toUInt32(ctx), 1); - if (s.isNull()) - return Value::undefinedValue(); - else - return Value::fromString(ctx, s); - } else if (object.isArrayObject() && index.isNumber()) { - return object.asArrayObject()->value.at(index.toUInt32(ctx)); - } else { - String *name = index.toString(ctx); - - if (! object.isObject()) - object = __qmljs_to_object(object, ctx); + if (index.isNumber()) { + if (object.isString()) { + const QString s = object.stringValue()->toQString().mid(index.toUInt32(ctx), 1); + if (s.isNull()) + return Value::undefinedValue(); + else + return Value::fromString(ctx, s); + } - return object.property(ctx, name); + if (ArrayObject *a = object.asArrayObject()) + return a->value.at(index.toUInt32(ctx)); } + + String *name = index.toString(ctx); + + if (! object.isObject()) + object = __qmljs_to_object(object, ctx); + + return object.property(ctx, name); } void __qmljs_set_element(Context *ctx, Value object, Value index, Value value) { - if (object.isArrayObject() && index.isNumber()) { - object.asArrayObject()->value.assign(index.toUInt32(ctx), value); - } else { - String *name = index.toString(ctx); + if (index.isNumber()) { + if (ArrayObject *a = object.asArrayObject()) { + a->value.assign(index.toUInt32(ctx), value); + return; + } + } - if (! object.isObject()) - object = __qmljs_to_object(object, ctx); + String *name = index.toString(ctx); - object.objectValue()->setProperty(ctx, name, value, /*flags*/ 0); - } -} + if (! object.isObject()) + object = __qmljs_to_object(object, ctx); -void __qmljs_set_element_number(Context *ctx, Value *object, Value *index, double number) -{ - Value v = Value::fromDouble(number); - __qmljs_set_element(ctx, *object, *index, v); + object.objectValue()->setProperty(ctx, name, value, /*flags*/ 0); } void __qmljs_set_activation_property(Context *ctx, String *name, Value value) @@ -1079,14 +1083,14 @@ void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Fun Value __qmljs_get_property(Context *ctx, Value object, String *name) { if (object.isObject()) { - return object.property(ctx, name); + return object.objectValue()->getProperty(ctx, name); } else if (object.isString() && name->isEqualTo(ctx->engine->id_length)) { - return Value::fromDouble(object.stringValue()->toQString().length()); + return Value::fromInt32(object.stringValue()->toQString().length()); } else { object = __qmljs_to_object(object, ctx); if (object.isObject()) { - return __qmljs_get_property(ctx, object, name); + return object.objectValue()->getProperty(ctx, name); } else { ctx->throwTypeError(); // ### not necessary. return Value::undefinedValue(); @@ -1205,25 +1209,29 @@ Value __qmljs_call_property(Context *context, Value base, String *name, Value *a return result; } +Value __qmljs_call_function(Context *context, Value thisObject, FunctionObject *f, Value *args, int argc) +{ + Context k; + Context *ctx = f->needsActivation ? context->engine->newContext() : &k; + const Value *that = thisObject.isUndefined() ? 0 : &thisObject; + ctx->initCallContext(context->engine, that, f, args, argc); + f->call(ctx); + if (ctx->hasUncaughtException) { + context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception + context->result = ctx->result; + } + ctx->leaveCallContext(f); + return ctx->result; +} + Value __qmljs_call_value(Context *context, Value thisObject, Value func, Value *args, int argc) { - Value result; if (FunctionObject *f = func.asFunctionObject()) { - Context k; - Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - const Value *that = thisObject.isUndefined() ? 0 : &thisObject; - ctx->initCallContext(context->engine, that, f, args, argc); - f->call(ctx); - if (ctx->hasUncaughtException) { - context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception - context->result = ctx->result; - } - ctx->leaveCallContext(f); - result = ctx->result; + return __qmljs_call_function(context, thisObject, f, args, argc); } else { context->throwTypeError(); + return Value::undefinedValue(); } - return result; } Value __qmljs_construct_activation_property(Context *context, String *name, Value *args, int argc) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 2e7b7d5bc9..994cdd531f 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -94,6 +94,7 @@ extern "C" { Value __qmljs_call_activation_property(Context *, String *name, Value *args, int argc); Value __qmljs_call_property(Context *context, Value base, String *name, Value *args, int argc); Value __qmljs_call_value(Context *context, Value thisObject, Value func, Value *args, int argc); +Value __qmljs_call_function(Context *context, Value thisObject, FunctionObject *f, Value *args, int argc); Value __qmljs_construct_activation_property(Context *, String *name, Value *args, int argc); Value __qmljs_construct_property(Context *context, Value base, String *name, Value *args, int argc); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 1ff21acef5..37d61c12b2 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1536,7 +1536,7 @@ void ArrayPrototype::method_every(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); - if (callback.isFunctionObject()) { + if (FunctionObject *f = callback.asFunctionObject()) { Value thisArg = ctx->argument(1); bool ok = true; for (uint k = 0; ok && k < instance->value.size(); ++k) { @@ -1548,7 +1548,7 @@ void ArrayPrototype::method_every(Context *ctx) args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); + Value r = __qmljs_call_function(ctx, thisArg, f, args, 3); ok = __qmljs_to_boolean(r, ctx); } ctx->result = Value::fromBoolean(ok); @@ -1565,7 +1565,7 @@ void ArrayPrototype::method_some(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); - if (callback.isFunctionObject()) { + if (FunctionObject *f = callback.asFunctionObject()) { Value thisArg = ctx->argument(1); bool ok = false; for (uint k = 0; !ok && k < instance->value.size(); ++k) { @@ -1577,7 +1577,7 @@ void ArrayPrototype::method_some(Context *ctx) args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); + Value r = __qmljs_call_function(ctx, thisArg, f, args, 3); ok = __qmljs_to_boolean(r, ctx); } ctx->result = Value::fromBoolean(ok); @@ -1594,9 +1594,7 @@ void ArrayPrototype::method_forEach(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); - if (! callback.isFunctionObject()) - ctx->throwTypeError(); - else { + if (FunctionObject *f = callback.asFunctionObject()) { Value thisArg = ctx->argument(1); for (quint32 k = 0; k < instance->value.size(); ++k) { Value v = instance->value.at(k); @@ -1606,8 +1604,10 @@ void ArrayPrototype::method_forEach(Context *ctx) args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - /*Value r =*/ __qmljs_call_value(ctx, thisArg, callback, args, 3); + /*Value r =*/ __qmljs_call_function(ctx, thisArg, f, args, 3); } + } else { + ctx->throwTypeError(); } } else { ctx->throwUnimplemented(QStringLiteral("Array.prototype.forEach")); @@ -1619,9 +1619,7 @@ void ArrayPrototype::method_map(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); - if (! callback.isFunctionObject()) - ctx->throwTypeError(); - else { + if (FunctionObject *f = callback.asFunctionObject()) { Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); a->value.resize(instance->value.size()); @@ -1633,10 +1631,12 @@ void ArrayPrototype::method_map(Context *ctx) args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); + Value r = __qmljs_call_function(ctx, thisArg, f, args, 3); a->value.assign(k, r); } ctx->result = Value::fromObject(a); + } else { + ctx->throwTypeError(); } } else { ctx->throwUnimplemented(QStringLiteral("Array.prototype.map")); @@ -1648,9 +1648,7 @@ void ArrayPrototype::method_filter(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); - if (! callback.isFunctionObject()) - ctx->throwTypeError(); - else { + if (FunctionObject *f = callback.asFunctionObject()) { Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); for (quint32 k = 0; k < instance->value.size(); ++k) { @@ -1661,7 +1659,7 @@ void ArrayPrototype::method_filter(Context *ctx) args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); + Value r = __qmljs_call_function(ctx, thisArg, f, args, 3); if (__qmljs_to_boolean(r, ctx)) { const uint index = a->value.size(); a->value.resize(index + 1); @@ -1669,6 +1667,8 @@ void ArrayPrototype::method_filter(Context *ctx) } } ctx->result = Value::fromObject(a); + } else { + ctx->throwTypeError(); } } else { ctx->throwUnimplemented(QStringLiteral("Array.prototype.filter")); @@ -1680,27 +1680,31 @@ void ArrayPrototype::method_reduce(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); - Value initialValue = ctx->argument(1); - Value acc = initialValue; - for (quint32 k = 0; k < instance->value.size(); ++k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - - if (acc.isUndefined()) { - acc = v; - continue; - } + if (FunctionObject *f = callback.asFunctionObject()) { + Value initialValue = ctx->argument(1); + Value acc = initialValue; + for (quint32 k = 0; k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + + if (acc.isUndefined()) { + acc = v; + continue; + } - Value args[4]; - args[0] = acc; - args[1] = v; - args[2] = Value::fromDouble(k); - args[3] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, Value::undefinedValue(), callback, args, 4); - acc = r; + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k); + args[3] = ctx->thisObject; + Value r = __qmljs_call_function(ctx, Value::undefinedValue(), f, args, 4); + acc = r; + } + ctx->result = acc; + } else { + ctx->throwTypeError(); } - ctx->result = acc; } else { ctx->throwUnimplemented(QStringLiteral("Array.prototype.reduce")); } @@ -1711,27 +1715,31 @@ void ArrayPrototype::method_reduceRight(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); - Value initialValue = ctx->argument(1); - Value acc = initialValue; - for (int k = instance->value.size() - 1; k != -1; --k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - - if (acc.isUndefined()) { - acc = v; - continue; - } + if (FunctionObject *f = callback.asFunctionObject()) { + Value initialValue = ctx->argument(1); + Value acc = initialValue; + for (int k = instance->value.size() - 1; k != -1; --k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; - Value args[4]; - args[0] = acc; - args[1] = v; - args[2] = Value::fromDouble(k); - args[3] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, Value::undefinedValue(), callback, args, 4); - acc = r; + if (acc.isUndefined()) { + acc = v; + continue; + } + + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k); + args[3] = ctx->thisObject; + Value r = __qmljs_call_function(ctx, Value::undefinedValue(), f, args, 4); + acc = r; + } + ctx->result = acc; + } else { + ctx->throwTypeError(); } - ctx->result = acc; } else { ctx->throwUnimplemented(QStringLiteral("Array.prototype.reduceRight")); } @@ -1777,7 +1785,7 @@ void FunctionPrototype::method_toString(Context *ctx) void FunctionPrototype::method_apply(Context *ctx) { - if (/*FunctionObject *fun =*/ ctx->thisObject.asFunctionObject()) { + if (FunctionObject *f = ctx->thisObject.asFunctionObject()) { Value thisObject = ctx->argument(0).toObject(ctx); if (thisObject.isNull() || thisObject.isUndefined()) @@ -1798,7 +1806,7 @@ void FunctionPrototype::method_apply(Context *ctx) return; } - ctx->result = __qmljs_call_value(ctx, thisObject, ctx->thisObject, args.data(), args.size()); + ctx->result = __qmljs_call_function(ctx, thisObject, f, args.data(), args.size()); } else { ctx->throwTypeError(); } @@ -1806,13 +1814,12 @@ void FunctionPrototype::method_apply(Context *ctx) void FunctionPrototype::method_call(Context *ctx) { - if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { - Q_UNUSED(fun); + if (FunctionObject *f = ctx->thisObject.asFunctionObject()) { Value thisArg = ctx->argument(0); QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); if (ctx->argumentCount) qCopy(ctx->arguments + 1, ctx->arguments + ctx->argumentCount, args.begin()); - ctx->result = __qmljs_call_value(ctx, thisArg, ctx->thisObject, args.data(), args.size()); + ctx->result = __qmljs_call_function(ctx, thisArg, f, args.data(), args.size()); } else { ctx->throwTypeError(); } -- cgit v1.2.3 From 285a4954e8ad6ac48e3e471e7d7b8f08a263de9c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 18 Oct 2012 14:52:22 +0200 Subject: Micro optimisations Change-Id: Ifae82d259f55e8e3791ef05eb7ea5f607fbfd747 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 026cef0a26..6d0f5cd7e9 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -280,7 +280,7 @@ void Context::init(ExecutionEngine *eng) Value *Context::lookupPropertyDescriptor(String *name) { for (Context *ctx = this; ctx; ctx = ctx->parent) { - if (ctx->activation.is(Value::Object_Type)) { + if (ctx->activation.isObject()) { if (Value *prop = ctx->activation.objectValue()->getPropertyDescriptor(this, name)) { return prop; } @@ -1028,7 +1028,7 @@ Value __qmljs_get_element(Context *ctx, Value object, Value index) if (! object.isObject()) object = __qmljs_to_object(object, ctx); - return object.property(ctx, name); + return object.objectValue()->getProperty(ctx, name); } void __qmljs_set_element(Context *ctx, Value object, Value index, Value value) @@ -1268,8 +1268,7 @@ Value __qmljs_construct_property(Context *context, Value base, String *name, Val if (!thisObject.isObject()) thisObject = __qmljs_to_object(base, context); - assert(thisObject.isObject()); - Value func = thisObject.property(context, name); + Value func = thisObject.objectValue()->getProperty(context, name); if (FunctionObject *f = func.asFunctionObject()) { Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; -- cgit v1.2.3 From 03f9fb6b618bab42a672e76133c43f2d6c3a60df Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 18 Oct 2012 21:48:57 +0200 Subject: Remove the is(Type) methods in Value Simplify and optimise the Value::isXxx() methods at the same time. Change-Id: I3e8fa98cf7b91079a4450c048c6d0795d1684724 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 2 +- qmljs_runtime.h | 42 +++++++++++++++++++----------------------- qv4ecmaobjects.cpp | 4 ++-- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 6d0f5cd7e9..5e6bcc772b 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -372,7 +372,7 @@ void Context::initConstructorContext(ExecutionEngine *e, Value *object, Function void Context::leaveConstructorContext(FunctionObject *f) { - assert(thisObject.is(Value::Object_Type)); + assert(thisObject.isObject()); result = thisObject; Value proto = f->getProperty(this, engine->id_prototype); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 994cdd531f..34aff35fa5 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -296,15 +296,19 @@ template <> struct ValueBase<4> : public ValueData Double_Type = 0 }; - inline bool is(ValueType type) const { - if (type == Double_Type) - return (tag & NaN_Mask) != NaN_Mask; - return tag == type; - } inline ValueType type() const { return (ValueType)tag; } + inline bool isUndefined() const { return tag == Undefined_Type; } + inline bool isNull() const { return tag == Null_Type; } + inline bool isBoolean() const { return tag == Boolean_Type; } + inline bool isInteger() const { return tag == Integer_Type; } + inline bool isDouble() const { return tag == Double_Type; } + inline bool isNumber() const { return tag == Integer_Type || tag == Double_Type; } + inline bool isString() const { return tag == String_Type; } + inline bool isObject() const { return tag == Object_Type; } + bool booleanValue() const { return int_32; } @@ -359,18 +363,19 @@ template <> struct ValueBase<8> : public ValueData Double_Type = 0 }; - inline bool is(ValueType type) const { - if (type == Double_Type) - return (tag & NaN_Mask) != NaN_Mask; - return (tag & Type_Mask) == type; - } - inline bool isNot(ValueType type) { - return !is(type); - } inline ValueType type() const { return (ValueType)(tag & Type_Mask); } + inline bool isUndefined() const { return tag == Undefined_Type; } + inline bool isNull() const { return tag == Null_Type; } + inline bool isBoolean() const { return tag == Boolean_Type; } + inline bool isInteger() const { return tag == Integer_Type; } + inline bool isDouble() const { return (tag & NaN_Mask) != NaN_Mask; } + inline bool isNumber() const { return tag == Integer_Type || (tag & NaN_Mask) != NaN_Mask; } + inline bool isString() const { return (tag & Type_Mask) == String_Type; } + inline bool isObject() const { return (tag & Type_Mask) == Object_Type; } + Bool booleanValue() const { return int_32; } @@ -430,15 +435,6 @@ struct Value : public ValueBase inline String *toString(Context *ctx) const; inline Value toObject(Context *ctx) const; - inline bool isUndefined() const { return is(Value::Undefined_Type); } - inline bool isNull() const { return is(Value::Null_Type); } - inline bool isString() const { return is(Value::String_Type); } - inline bool isBoolean() const { return type() == Value::Boolean_Type; } - inline bool isNumber() const { return is(Value::Integer_Type) || is(Value::Double_Type); } - inline bool isDouble() const { return is(Value::Double_Type); } - inline bool isInteger() const { return type() == Value::Integer_Type; } - inline bool isObject() const { return type() == Value::Object_Type; } - inline bool isPrimitive() const { return type() != Value::Object_Type; } bool isFunctionObject() const; bool isBooleanObject() const; @@ -496,7 +492,7 @@ inline double Value::toNumber(Context *ctx) const inline String *Value::toString(Context *ctx) const { Value v = __qmljs_to_string(*this, ctx); - assert(v.is(Value::String_Type)); + assert(v.isString()); return v.stringValue(); } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 37d61c12b2..2af450c923 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -711,7 +711,7 @@ void StringCtor::construct(Context *ctx) void StringCtor::call(Context *ctx) { const Value arg = ctx->argument(0); - if (arg.is(Value::Undefined_Type)) + if (arg.isUndefined()) ctx->result = Value::fromString(ctx->engine->newString(QString())); else ctx->result = __qmljs_to_string(arg, ctx); @@ -809,7 +809,7 @@ void StringPrototype::method_concat(Context *ctx) for (unsigned i = 0; i < ctx->argumentCount; ++i) { Value v = __qmljs_to_string(ctx->arguments[i], ctx); - assert(v.is(Value::String_Type)); + assert(v.isString()); value += v.stringValue()->toQString(); } -- cgit v1.2.3 From fde57ef5b89a710e204f4153e436a05cd804cb55 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 18 Oct 2012 23:48:31 +0200 Subject: Use some tricks to speed up binary operators Add some flags to the Value's tag that allow us to simplify type conversion from null and bool to integer. Add a method to allow checking in one go whether two values are "integer compatible" Change-Id: I0a05bdf881836ad3f8286c0365dd192bf7f6e5d6 Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 190 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 119 insertions(+), 71 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 34aff35fa5..699f89d23f 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -155,7 +155,6 @@ Bool __qmljs_to_boolean(Value value, Context *ctx); double __qmljs_to_number(Value value, Context *ctx); double __qmljs_to_integer(Value value, Context *ctx); int __qmljs_to_int32(Value value, Context *ctx); -unsigned __qmljs_to_uint32(Value value, Context *ctx); unsigned short __qmljs_to_uint16(Value value, Context *ctx); Value __qmljs_to_string(Value value, Context *ctx); Value __qmljs_to_object(Value value, Context *ctx); @@ -349,32 +348,47 @@ template <> struct ValueBase<4> : public ValueData template <> struct ValueBase<8> : public ValueData { enum Masks { - NaN_Mask = 0x7ff80000, - Type_Mask = 0x7fff0000, + NotDouble_Mask = 0xfff80000, + Type_Mask = 0xffff0000, + Immediate_Mask = NotDouble_Mask | 0x00040000, Tag_Shift = 32 }; enum ValueType { - Undefined_Type = NaN_Mask | 0x70000, - Null_Type = NaN_Mask | 0x00000, - Boolean_Type = NaN_Mask | 0x10000, - Integer_Type = NaN_Mask | 0x20000, - Object_Type = NaN_Mask | 0x30000, - String_Type = NaN_Mask | 0x40000, - Double_Type = 0 + Undefined_Type = Immediate_Mask | 0x00000, + Null_Type = Immediate_Mask | 0x10000, + Boolean_Type = Immediate_Mask | 0x20000, + Integer_Type = Immediate_Mask | 0x30000, + Object_Type = NotDouble_Mask | 0x00000, + String_Type = NotDouble_Mask | 0x10000 + }; + + enum ImmediateFlags { + ConvertibleToInt = NotDouble_Mask | 0x1 + }; + + enum ValueTypeInternal { + _Undefined_Type = Undefined_Type, + _Null_Type = Null_Type | ConvertibleToInt, + _Boolean_Type = Boolean_Type | ConvertibleToInt, + _Integer_Type = Integer_Type | ConvertibleToInt, + _Object_Type = Object_Type, + _String_Type = String_Type + }; inline ValueType type() const { return (ValueType)(tag & Type_Mask); } - inline bool isUndefined() const { return tag == Undefined_Type; } - inline bool isNull() const { return tag == Null_Type; } - inline bool isBoolean() const { return tag == Boolean_Type; } - inline bool isInteger() const { return tag == Integer_Type; } - inline bool isDouble() const { return (tag & NaN_Mask) != NaN_Mask; } - inline bool isNumber() const { return tag == Integer_Type || (tag & NaN_Mask) != NaN_Mask; } + inline bool isUndefined() const { return tag == _Undefined_Type; } + inline bool isNull() const { return tag == _Null_Type; } + inline bool isBoolean() const { return tag == _Boolean_Type; } + inline bool isInteger() const { return tag == _Integer_Type; } + inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } + inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } inline bool isString() const { return (tag & Type_Mask) == String_Type; } inline bool isObject() const { return (tag & Type_Mask) == Object_Type; } + inline bool isConvertibleToInt() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } Bool booleanValue() const { return int_32; @@ -386,7 +400,7 @@ template <> struct ValueBase<8> : public ValueData dbl = d; } double asDouble() const { - if (tag == Integer_Type) + if (tag == _Integer_Type) return int_32; return dbl; } @@ -435,7 +449,19 @@ struct Value : public ValueBase inline String *toString(Context *ctx) const; inline Value toObject(Context *ctx) const; - inline bool isPrimitive() const { return type() != Value::Object_Type; } + inline bool isPrimitive() const { return !isObject(); } + static inline bool integerCompatible(Value a, Value b) { + return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; + } + inline bool tryIntegerConversion() { + bool b = isConvertibleToInt(); + if (b) + tag = _Integer_Type; + return b; + } + + + bool isFunctionObject() const; bool isBooleanObject() const; bool isNumberObject() const; @@ -466,12 +492,18 @@ inline int Value::toUInt16(Context *ctx) inline int Value::toInt32(Context *ctx) { - return __qmljs_to_int32(*this, ctx); + if (isConvertibleToInt()) + return int_32; + + return Value::toInt32(__qmljs_to_number(*this, ctx)); } inline unsigned int Value::toUInt32(Context *ctx) { - return __qmljs_to_uint32(*this, ctx); + if (isConvertibleToInt()) + return (unsigned) int_32; + + return toUInt32(__qmljs_to_number(*this, ctx)); } inline Bool Value::toBoolean(Context *ctx) const @@ -561,21 +593,21 @@ inline Value ValueBase<4>::fromObject(Object *o) inline Value ValueBase<8>::undefinedValue() { Value v; - v.val = quint64(Undefined_Type) << Tag_Shift; + v.val = quint64(_Undefined_Type) << Tag_Shift; return v; } inline Value ValueBase<8>::nullValue() { Value v; - v.val = quint64(Null_Type) << Tag_Shift; + v.val = quint64(_Null_Type) << Tag_Shift; return v; } inline Value ValueBase<8>::fromBoolean(Bool b) { Value v; - v.tag = Boolean_Type; + v.tag = _Boolean_Type; v.int_32 = (bool)b; return v; } @@ -590,7 +622,7 @@ inline Value ValueBase<8>::fromDouble(double d) inline Value ValueBase<8>::fromInt32(int i) { Value v; - v.tag = Integer_Type; + v.tag = _Integer_Type; v.int_32 = i; return v; } @@ -599,7 +631,7 @@ inline Value ValueBase<8>::fromString(String *s) { Value v; v.val = (quint64)s; - v.val |= quint64(String_Type) << Tag_Shift; + v.val |= quint64(_String_Type) << Tag_Shift; return v; } @@ -607,7 +639,7 @@ inline Value ValueBase<8>::fromObject(Object *o) { Value v; v.val = (quint64)o; - v.val |= quint64(Object_Type) << Tag_Shift; + v.val |= quint64(_Object_Type) << Tag_Shift; return v; } @@ -720,7 +752,7 @@ inline double __qmljs_to_number(Value value, Context *ctx) inline double __qmljs_to_integer(Value value, Context *ctx) { - if (value.isInteger()) + if (value.isConvertibleToInt()) return value.int_32; return Value::toInteger(__qmljs_to_number(value, ctx)); @@ -728,23 +760,15 @@ inline double __qmljs_to_integer(Value value, Context *ctx) inline int __qmljs_to_int32(Value value, Context *ctx) { - if (value.isInteger()) + if (value.isConvertibleToInt()) return value.int_32; return Value::toInt32(__qmljs_to_number(value, ctx)); } -inline unsigned __qmljs_to_uint32(Value value, Context *ctx) -{ - if (value.isInteger()) - return (unsigned) value.int_32; - - return Value::toUInt32(__qmljs_to_number(value, ctx)); -} - inline unsigned short __qmljs_to_uint16(Value value, Context *ctx) { - if (value.isInteger()) + if (value.isConvertibleToInt()) return (ushort)(uint)value.integerValue(); double number = __qmljs_to_number(value, ctx); @@ -889,8 +913,9 @@ inline Value __qmljs_typeof(Value value, Context *ctx) inline Value __qmljs_uplus(Value value, Context *ctx) { - if (value.isInteger()) + if (value.tryIntegerConversion()) return value; + double n = __qmljs_to_number(value, ctx); return Value::fromDouble(n); } @@ -905,7 +930,12 @@ inline Value __qmljs_uminus(Value value, Context *ctx) inline Value __qmljs_compl(Value value, Context *ctx) { - int n = __qmljs_to_int32(value, ctx); + int n; + if (value.isConvertibleToInt()) + n = ~value.int_32; + else + n = Value::toInteger(__qmljs_to_number(value, ctx)); + return Value::fromInt32(~n); } @@ -918,39 +948,48 @@ inline Value __qmljs_not(Value value, Context *ctx) // binary operators inline Value __qmljs_bit_or(Value left, Value right, Context *ctx) { - int lval = __qmljs_to_int32(left, ctx); - int rval = __qmljs_to_int32(right, ctx); + if (Value::integerCompatible(left, right)) + return Value::fromInt32(left.integerValue() | right.integerValue()); + + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + int rval = Value::toInt32(__qmljs_to_number(right, ctx)); return Value::fromInt32(lval | rval); } inline Value __qmljs_bit_xor(Value left, Value right, Context *ctx) { - int lval = __qmljs_to_int32(left, ctx); - int rval = __qmljs_to_int32(right, ctx); + if (Value::integerCompatible(left, right)) + return Value::fromInt32(left.integerValue() ^ right.integerValue()); + + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + int rval = Value::toInt32(__qmljs_to_number(right, ctx)); return Value::fromInt32(lval ^ rval); } inline Value __qmljs_bit_and(Value left, Value right, Context *ctx) { - int lval = __qmljs_to_int32(left, ctx); - int rval = __qmljs_to_int32(right, ctx); + if (Value::integerCompatible(left, right)) + return Value::fromInt32(left.integerValue() & right.integerValue()); + + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + int rval = Value::toInt32(__qmljs_to_number(right, ctx)); return Value::fromInt32(lval & rval); } inline Value __qmljs_add(Value left, Value right, Context *ctx) { - if (left.isInteger() & right.isInteger()) + if (Value::integerCompatible(left, right)) return add_int32(left.integerValue(), right.integerValue()); - if (left.isNumber() & right.isNumber()) - return Value::fromDouble(left.asDouble() + right.asDouble()); - else - return __qmljs_add_helper(left, right, ctx); + if (left.isDouble() & right.isDouble()) + return Value::fromDouble(left.doubleValue() + right.doubleValue()); + + return __qmljs_add_helper(left, right, ctx); } inline Value __qmljs_sub(Value left, Value right, Context *ctx) { - if (left.isInteger() && right.isInteger()) + if (Value::integerCompatible(left, right)) return sub_int32(left.integerValue(), right.integerValue()); double lval = __qmljs_to_number(left, ctx); @@ -960,7 +999,7 @@ inline Value __qmljs_sub(Value left, Value right, Context *ctx) inline Value __qmljs_mul(Value left, Value right, Context *ctx) { - if (left.isInteger() && right.isInteger()) + if (Value::integerCompatible(left, right)) return mul_int32(left.integerValue(), right.integerValue()); double lval = __qmljs_to_number(left, ctx); @@ -977,7 +1016,7 @@ inline Value __qmljs_div(Value left, Value right, Context *ctx) inline Value __qmljs_mod(Value left, Value right, Context *ctx) { - if (left.isInteger() && right.isInteger()) + if (Value::integerCompatible(left, right)) return Value::fromInt32(left.integerValue() % right.integerValue()); double lval = __qmljs_to_number(left, ctx); @@ -989,22 +1028,31 @@ inline Value __qmljs_mod(Value left, Value right, Context *ctx) inline Value __qmljs_shl(Value left, Value right, Context *ctx) { - int lval = __qmljs_to_int32(left, ctx); - unsigned rval = __qmljs_to_uint32(right, ctx) & 0x1f; + if (Value::integerCompatible(left, right)) + return Value::fromInt32(left.integerValue() << ((uint(right.integerValue()) & 0x1f))); + + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; return Value::fromInt32(lval << rval); } inline Value __qmljs_shr(Value left, Value right, Context *ctx) { - int lval = __qmljs_to_int32(left, ctx); - unsigned rval = __qmljs_to_uint32(right, ctx) & 0x1f; + if (Value::integerCompatible(left, right)) + return Value::fromInt32(left.integerValue() >> ((uint(right.integerValue()) & 0x1f))); + + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; return Value::fromInt32(lval >> rval); } inline Value __qmljs_ushr(Value left, Value right, Context *ctx) { - unsigned lval = __qmljs_to_uint32(left, ctx); - unsigned rval = __qmljs_to_uint32(right, ctx) & 0x1f; + if (Value::integerCompatible(left, right)) + return Value::fromInt32(uint(left.integerValue()) >> ((uint(right.integerValue()) & 0x1f))); + + unsigned lval = Value::toUInt32(__qmljs_to_number(left, ctx)); + unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; return Value::fromInt32(lval >> rval); } @@ -1055,10 +1103,10 @@ inline Bool __qmljs_cmp_gt(Value left, Value right, Context *ctx) left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); - if (left.isInteger() && right.isInteger()) + if (Value::integerCompatible(left, right)) return left.integerValue() > right.integerValue(); - if (left.isNumber() && right.isNumber()) { - return left.asDouble() > right.asDouble(); + if (left.isDouble() && right.isDouble()) { + return left.doubleValue() > right.doubleValue(); } else if (left.isString() && right.isString()) { return __qmljs_string_compare(ctx, right.stringValue(), left.stringValue()); } else { @@ -1073,10 +1121,10 @@ inline Bool __qmljs_cmp_lt(Value left, Value right, Context *ctx) left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); - if (left.isInteger() && right.isInteger()) + if (Value::integerCompatible(left, right)) return left.integerValue() < right.integerValue(); - if (left.isNumber() && right.isNumber()) { - return left.asDouble() < right.asDouble(); + if (left.isDouble() && right.isDouble()) { + return left.doubleValue() < right.doubleValue(); } else if (left.isString() && right.isString()) { return __qmljs_string_compare(ctx, left.stringValue(), right.stringValue()); } else { @@ -1091,10 +1139,10 @@ inline Bool __qmljs_cmp_ge(Value left, Value right, Context *ctx) left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); - if (left.isInteger() && right.isInteger()) + if (Value::integerCompatible(left, right)) return left.integerValue() >= right.integerValue(); - if (left.isNumber() && right.isNumber()) { - return left.asDouble() >= right.asDouble(); + if (left.isDouble() && right.isDouble()) { + return left.doubleValue() >= right.doubleValue(); } else if (left.isString() && right.isString()) { return !__qmljs_string_compare(ctx, left.stringValue(), right.stringValue()); } else { @@ -1109,10 +1157,10 @@ inline Bool __qmljs_cmp_le(Value left, Value right, Context *ctx) left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); - if (left.isInteger() && right.isInteger()) + if (Value::integerCompatible(left, right)) return left.integerValue() <= right.integerValue(); - if (left.isNumber() && right.isNumber()) { - return left.asDouble() <= right.asDouble(); + if (left.isDouble() && right.isDouble()) { + return left.doubleValue() <= right.doubleValue(); } else if (left.isString() && right.isString()) { return !__qmljs_string_compare(ctx, right.stringValue(), left.stringValue()); } else { -- cgit v1.2.3 From de8f2e20447d1b3c6b2a16d9407a4e92caf2b27b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 19 Oct 2012 00:02:21 +0200 Subject: Make Value a real POD Remove the inheritance from ValueData and ValueBase. Use a few ifdef's for 64 bit instead of a lot of template magic and inheritance to get the correct implementations for 32 and 64 bit. Saves some code, and makes Value a real POD type. Apparently this also helps gcc generate better code, crypto.js runs another few percent faster now. Change-Id: I9dac488b27e629e6ef8c096f2bee86a5d678fd49 Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 196 +++++++++++++++---------------------------------------- qv4isel_masm.cpp | 4 +- 2 files changed, 54 insertions(+), 146 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 699f89d23f..213186af14 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -47,6 +47,8 @@ # include #endif +#include + #include #include @@ -256,7 +258,9 @@ Bool __qmljs_cmp_in(Value left, Value right, Context *ctx); } // extern "C" -struct ValueData { + +struct Value +{ union { quint64 val; double dbl; @@ -267,86 +271,18 @@ struct ValueData { union { uint uint_32; int int_32; +#if CPU(X86_64) +#else + Object *o; + String *s; +#endif }; #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN uint tag; #endif }; }; -}; -struct Value; -template struct ValueBase; -template <> struct ValueBase<4> : public ValueData -{ - // we have all 4 bytes on 32 bit to specify the type - enum Masks { - NaN_Mask = 0xfff80000, - Type_Mask = 0xffffffff - }; - - enum ValueType { - Undefined_Type = NaN_Mask | 0x7ffff, // all 1's - Null_Type = NaN_Mask | 0x0, - Boolean_Type = NaN_Mask | 0x1, - Integer_Type = NaN_Mask | 0x2, - Object_Type = NaN_Mask | 0x2d59b, // give it randomness to avoid accidental collisions (for gc) - String_Type = NaN_Mask | 0x2d5ba, - Double_Type = 0 - }; - - inline ValueType type() const { - return (ValueType)tag; - } - - inline bool isUndefined() const { return tag == Undefined_Type; } - inline bool isNull() const { return tag == Null_Type; } - inline bool isBoolean() const { return tag == Boolean_Type; } - inline bool isInteger() const { return tag == Integer_Type; } - inline bool isDouble() const { return tag == Double_Type; } - inline bool isNumber() const { return tag == Integer_Type || tag == Double_Type; } - inline bool isString() const { return tag == String_Type; } - inline bool isObject() const { return tag == Object_Type; } - - bool booleanValue() const { - return int_32; - } - double doubleValue() const { - return dbl; - } - void setDouble(double d) { - dbl = d; - } - double asDouble() const { - if (tag == Integer_Type) - return int_32; - return dbl; - } - int integerValue() const { - return int_32; - } - - String *stringValue() const { - return (String *)(quintptr) uint_32; - } - Object *objectValue() const { - return (Object *)(quintptr) uint_32; - } - quint64 rawValue() const { - return val; - } - - static inline Value undefinedValue(); - static inline Value nullValue(); - static inline Value fromBoolean(Bool b); - static inline Value fromDouble(double d); - static inline Value fromInt32(int i); - static inline Value fromString(String *s); - static inline Value fromObject(Object *o); -}; - -template <> struct ValueBase<8> : public ValueData -{ enum Masks { NotDouble_Mask = 0xfff80000, Type_Mask = 0xffff0000, @@ -386,8 +322,13 @@ template <> struct ValueBase<8> : public ValueData inline bool isInteger() const { return tag == _Integer_Type; } inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } +#if CPU(X86_64) inline bool isString() const { return (tag & Type_Mask) == String_Type; } inline bool isObject() const { return (tag & Type_Mask) == Object_Type; } +#else + inline bool isString() const { return tag == String_Type; } + inline bool isObject() const { return tag == Object_Type; } +#endif inline bool isConvertibleToInt() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } Bool booleanValue() const { @@ -408,12 +349,22 @@ template <> struct ValueBase<8> : public ValueData return int_32; } +#if CPU(X86_64) String *stringValue() const { return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift)); } Object *objectValue() const { return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift)); } +#else + String *stringValue() const { + return s; + } + Object *objectValue() const { + return o; + } +#endif + quint64 rawValue() const { return val; } @@ -425,15 +376,9 @@ template <> struct ValueBase<8> : public ValueData static Value fromInt32(int i); static Value fromString(String *s); static Value fromObject(Object *o); -}; - - -struct Value : public ValueBase -{ #ifndef QMLJS_LLVM_RUNTIME static Value fromString(Context *ctx, const QString &fromString); - using ValueBase::fromString; #endif static double toInteger(double fromNumber); @@ -533,78 +478,31 @@ inline Value Value::toObject(Context *ctx) const return __qmljs_to_object(*this, ctx); } - -inline Value ValueBase<4>::undefinedValue() -{ - Value v; - v.tag = Undefined_Type; - v.uint_32 = 0; - return v; -} - -inline Value ValueBase<4>::nullValue() -{ - Value v; - v.tag = Null_Type; - v.uint_32 = 0; - return v; -} - -inline Value ValueBase<4>::fromBoolean(Bool b) -{ - Value v; - v.tag = Boolean_Type; - v.int_32 = (bool)b; - return v; -} - -inline Value ValueBase<4>::fromDouble(double d) -{ - Value v; - v.dbl = d; - return v; -} - -inline Value ValueBase<4>::fromInt32(int i) -{ - Value v; - v.tag = Integer_Type; - v.int_32 = i; - return v; -} - -inline Value ValueBase<4>::fromString(String *s) -{ - Value v; - v.tag = String_Type; - v.uint_32 = (quint32)(quintptr) s; - return v; -} - -inline Value ValueBase<4>::fromObject(Object *o) -{ - Value v; - v.tag = Object_Type; - v.uint_32 = (quint32)(quintptr) o; - return v; -} - - -inline Value ValueBase<8>::undefinedValue() +inline Value Value::undefinedValue() { Value v; +#if CPU(X86_64) v.val = quint64(_Undefined_Type) << Tag_Shift; +#else + v.tag = _Undefined_Type; + v.int_32 = (bool)b; +#endif return v; } -inline Value ValueBase<8>::nullValue() +inline Value Value::nullValue() { Value v; +#if CPU(X86_64) v.val = quint64(_Null_Type) << Tag_Shift; +#else + v.tag = _Null_Type; + v.int_32 = (bool)b; +#endif return v; } -inline Value ValueBase<8>::fromBoolean(Bool b) +inline Value Value::fromBoolean(Bool b) { Value v; v.tag = _Boolean_Type; @@ -612,14 +510,14 @@ inline Value ValueBase<8>::fromBoolean(Bool b) return v; } -inline Value ValueBase<8>::fromDouble(double d) +inline Value Value::fromDouble(double d) { Value v; v.dbl = d; return v; } -inline Value ValueBase<8>::fromInt32(int i) +inline Value Value::fromInt32(int i) { Value v; v.tag = _Integer_Type; @@ -627,19 +525,29 @@ inline Value ValueBase<8>::fromInt32(int i) return v; } -inline Value ValueBase<8>::fromString(String *s) +inline Value Value::fromString(String *s) { Value v; +#if CPU(X86_64) v.val = (quint64)s; v.val |= quint64(_String_Type) << Tag_Shift; +#else + v.tag = _String_Type; + v.s = s; +#endif return v; } -inline Value ValueBase<8>::fromObject(Object *o) +inline Value Value::fromObject(Object *o) { Value v; +#if CPU(X86_64) v.val = (quint64)o; v.val |= quint64(_Object_Type) << Tag_Shift; +#else + v.tag = _Object_Type; + v.o = o; +#endif return v; } diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 9741f0169e..249817ea6e 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -636,11 +636,11 @@ void InstructionSelection::visitCJump(IR::CJump *s) if (IR::Temp *t = s->cond->asTemp()) { Address temp = loadTempAddress(Gpr0, t); Address tag = temp; - tag.offset += offsetof(VM::ValueData, tag); + tag.offset += offsetof(VM::Value, tag); Jump booleanConversion = branch32(NotEqual, tag, TrustedImm32(VM::Value::Boolean_Type)); Address data = temp; - data.offset += offsetof(VM::ValueData, int_32); + data.offset += offsetof(VM::Value, int_32); load32(data, Gpr0); Jump testBoolean = jump(); -- cgit v1.2.3 From 110204b184d7ab4ab0a669d57f34f7219e4a7e86 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 19 Oct 2012 10:23:09 +0200 Subject: Fix the dataLog methods qDebug doesn't have an overload taking a va_list. Change-Id: Ic8d58d0a37c0bac11f1146eed9fdc105ddf1dfc8 Reviewed-by: Simon Hausmann --- masm/stubs/WTFStubs.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/masm/stubs/WTFStubs.cpp b/masm/stubs/WTFStubs.cpp index 5c45e6cc2a..860bc5b26e 100644 --- a/masm/stubs/WTFStubs.cpp +++ b/masm/stubs/WTFStubs.cpp @@ -81,15 +81,19 @@ FILE* dataFile() void dataLogV(const char* format, va_list args) { - qDebug(format, args); + char buffer[1024]; + vsnprintf(buffer, sizeof(buffer), format, args); + qDebug("%s", buffer); } void dataLog(const char* format, ...) { + char buffer[1024]; va_list args; va_start(args, format); - qDebug(format, args); + vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); + qDebug("%s", buffer); } void dataLogString(const char* str) -- cgit v1.2.3 From acfb62cc2e6a85a5546dec3f586cf0cdf3deb7ca Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 19 Oct 2012 15:03:28 +0200 Subject: Added LLVM code generation for in-place operators. E.g. +=, <<=, etc. Change-Id: Iffd5eac413e3c3714fedbab58415d9dc4ba42fa6 Reviewed-by: Lars Knoll --- llvm_runtime.cpp | 55 ++++++++++++++++++ main.cpp | 5 +- qv4isel_llvm.cpp | 171 +++++++++++++++++++++++++++++++++++++++++++++++-------- v4.pro | 2 +- 4 files changed, 206 insertions(+), 27 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index d828df3760..8771e6da67 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -216,6 +216,61 @@ void __qmljs_llvm_not(Context *ctx, Value *result, const Value *value) *result = __qmljs_not(*value, ctx); } +void __qmljs_llvm_inplace_bit_and_name(Context *ctx, String *dest, Value *src) +{ + __qmljs_inplace_bit_and_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_bit_or_name(Context *ctx, String *dest, Value *src) +{ + __qmljs_inplace_bit_or_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_bit_xor_name(Context *ctx, String *dest, Value *src) +{ + __qmljs_inplace_bit_xor_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_add_name(Context *ctx, String *dest, Value *src) +{ + __qmljs_inplace_add_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_sub_name(Context *ctx, String *dest, Value *src) +{ + __qmljs_inplace_sub_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_mul_name(Context *ctx, String *dest, Value *src) +{ + __qmljs_inplace_mul_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_div_name(Context *ctx, String *dest, Value *src) +{ + __qmljs_inplace_div_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_mod_name(Context *ctx, String *dest, Value *src) +{ + __qmljs_inplace_mod_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_shl_name(Context *ctx, String *dest, Value *src) +{ + __qmljs_inplace_shl_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_shr_name(Context *ctx, String *dest, Value *src) +{ + __qmljs_inplace_shr_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_ushr_name(Context *ctx, String *dest, Value *src) +{ + __qmljs_inplace_ushr_name(*src, dest, ctx); +} + String *__qmljs_llvm_identifier_from_utf8(Context *ctx, const char *str) { return __qmljs_identifier_from_utf8(ctx, str); // ### make it unique diff --git a/main.cpp b/main.cpp index 261aeff4f7..d20b350344 100644 --- a/main.cpp +++ b/main.cpp @@ -155,7 +155,10 @@ int evaluateCompiledCode(const QStringList &files) QFileInfo libInfo(libName); QLibrary lib(libInfo.absoluteFilePath()); lib.load(); - QFunctionPointer ptr = lib.resolve("_25_entry"); + QFunctionPointer ptr = lib.resolve("%entry"); +// qDebug("_%%entry resolved to address %p", ptr); + if (!ptr) + return -1; void (*code)(VM::Context *) = (void (*)(VM::Context *)) ptr; VM::ExecutionEngine vm; diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index f6e79c963a..40434ae07c 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -56,7 +56,6 @@ #include #include #include -#include #include #include #include @@ -413,31 +412,153 @@ void LLVMInstructionSelection::genMoveMember(IR::Move *s) void LLVMInstructionSelection::visitMove(IR::Move *s) { - if (s->op != IR::OpInvalid) { - s->dump(qerr, IR::Stmt::HIR); - qerr << endl; - Q_UNIMPLEMENTED(); - return; - } - - if (s->target->asSubscript()) { - genMoveSubscript(s); - } else if (s->target->asMember()) { - genMoveMember(s); - } else if (IR::Name *n = s->target->asName()) { - llvm::Value *name = getIdentifier(*n->id); - llvm::Value *source = getLLVMTempReference(s->source); - CreateCall3(_llvmModule->getFunction("__qmljs_llvm_set_activation_property"), - _llvmFunction->arg_begin(), name, source); - } else if (IR::Temp *t = s->target->asTemp()) { - llvm::Value *target = getLLVMTemp(t); - llvm::Value *source = getLLVMValue(s->source); - CreateStore(source, target); + if (s->op == IR::OpInvalid) { + if (s->target->asSubscript()) { + genMoveSubscript(s); + return; + } else if (s->target->asMember()) { + genMoveMember(s); + return; + } else if (IR::Name *n = s->target->asName()) { + llvm::Value *name = getIdentifier(*n->id); + llvm::Value *source = getLLVMTempReference(s->source); + CreateCall3(_llvmModule->getFunction("__qmljs_llvm_set_activation_property"), + _llvmFunction->arg_begin(), name, source); + return; + } else if (IR::Temp *t = s->target->asTemp()) { + llvm::Value *target = getLLVMTemp(t); + llvm::Value *source = getLLVMValue(s->source); + CreateStore(source, target); + return; + } } else { - s->dump(qerr, IR::Stmt::HIR); - qerr << endl; - Q_UNIMPLEMENTED(); + // inplace assignment, e.g. x += 1, ++x, ... + if (IR::Temp *t = s->target->asTemp()) { + if (IR::Temp *t2 = s->source->asTemp()) { + const char *opName = 0; + switch (s->op) { +// case IR::OpBitAnd: setOp(op, opName, __qmljs_bit_and); break; +// case IR::OpBitOr: setOp(op, opName, __qmljs_bit_or); break; +// case IR::OpBitXor: setOp(op, opName, __qmljs_bit_xor); break; + case IR::OpAdd: opName = "__qmljs_llvm_add"; break; +// case IR::OpSub: setOp(op, opName, __qmljs_sub); break; +// case IR::OpMul: setOp(op, opName, __qmljs_mul); break; +// case IR::OpDiv: setOp(op, opName, __qmljs_div); break; +// case IR::OpMod: setOp(op, opName, __qmljs_mod); break; +// case IR::OpLShift: setOp(op, opName, __qmljs_shl); break; +// case IR::OpRShift: setOp(op, opName, __qmljs_shr); break; +// case IR::OpURShift: setOp(op, opName, __qmljs_ushr); break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *target = getLLVMTemp(t); + llvm::Value *s1 = getLLVMTemp(t); + llvm::Value *s2 = getLLVMTemp(t2); + CreateCall4(_llvmModule->getFunction(opName), + _llvmFunction->arg_begin(), target, s1, s2); + return; + } + } + } else if (IR::Name *n = s->target->asName()) { + qDebug()<<__FILE__<<__LINE__; + if (IR::Temp *t = s->source->asTemp()) { + const char *opName = 0; + switch (s->op) { + case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_name"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_name"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_name"; break; + case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_name"; break; + case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_name"; break; + case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_name"; break; + case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_name"; break; + case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_name"; break; + case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_name"; break; + case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_name"; break; + case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_name"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *dst = getIdentifier(*n->id); + llvm::Value *src = getLLVMTemp(t); + CreateCall3(_llvmModule->getFunction(opName), + _llvmFunction->arg_begin(), dst, src); + return; + } + } + } else if (IR::Subscript *ss = s->target->asSubscript()) { + qDebug()<<__FILE__<<__LINE__; +// if (IR::Temp *t = s->source->asTemp()) { +// void (*op)(Value base, Value index, Value value, Context *ctx) = 0; +// const char *opName = 0; +// switch (s->op) { +// case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; +// case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_element); break; +// case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_element); break; +// case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_element); break; +// case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_element); break; +// case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_element); break; +// case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_element); break; +// case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_element); break; +// case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_element); break; +// case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_element); break; +// case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_element); break; +// default: +// Q_UNREACHABLE(); +// break; +// } + +// if (op) { +// IR::Temp* base = ss->base->asTemp(); +// IR::Temp* index = ss->index->asTemp(); +// generateFunctionCallImp(Void, opName, op, base, index, t, ContextRegister); +// checkExceptions(); +// } +// return; +// } + } else if (IR::Member *m = s->target->asMember()) { + qDebug()<<__FILE__<<__LINE__; +// if (IR::Temp *t = s->source->asTemp()) { +// void (*op)(Value value, Value base, String *name, Context *ctx) = 0; +// const char *opName = 0; +// switch (s->op) { +// case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; +// case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_member); break; +// case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_member); break; +// case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_member); break; +// case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_member); break; +// case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_member); break; +// case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_member); break; +// case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_member); break; +// case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_member); break; +// case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_member); break; +// case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_member); break; +// default: +// Q_UNREACHABLE(); +// break; +// } + +// if (op) { +// IR::Temp* base = m->base->asTemp(); +// String* member = identifier(*m->name); +// generateFunctionCallImp(Void, opName, op, t, base, member, ContextRegister); +// checkExceptions(); +// } +// return; +// } + } } + + // For anything else: + s->dump(qerr, IR::Stmt::HIR); + qerr << endl; + Q_UNIMPLEMENTED(); + return; } void LLVMInstructionSelection::visitJump(IR::Jump *s) @@ -843,7 +964,7 @@ void LLVMInstructionSelection::genConstructName(IR::New *e, llvm::Value *result) int argc = 0; llvm::Value *args = genArguments(e->args, argc); - CreateCall5(_llvmModule->getFunction("__qmljs_construct_activation_property"), + CreateCall5(_llvmModule->getFunction("__qmljs_llvm_construct_activation_property"), _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); _llvmValue = CreateLoad(result); diff --git a/v4.pro b/v4.pro index 2740936c55..38f821b542 100644 --- a/v4.pro +++ b/v4.pro @@ -56,7 +56,7 @@ LIBS += \ QMAKE_EXTRA_TARGETS += gen_llvm_runtime gen_llvm_runtime.target = llvm_runtime -gen_llvm_runtime.commands = clang -O2 -emit-llvm -c -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o llvm_runtime.bc +gen_llvm_runtime.commands = clang -O2 -emit-llvm -c -I. -Imasm -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o llvm_runtime.bc } else { -- cgit v1.2.3 From 04b12a90b8ac5c44097bbf60a730f36b58578086 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 19 Oct 2012 23:10:42 +0200 Subject: Implement regexp support Change-Id: I86c9bbe69c9ba4ae9d300b62e7d16b372f3478ea Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 28 +++++++++++++- qmljs_objects.h | 10 +++-- qmljs_runtime.cpp | 10 +++++ qmljs_runtime.h | 3 ++ qv4codegen.cpp | 5 ++- qv4ecmaobjects.cpp | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++--- qv4ecmaobjects_p.h | 2 +- qv4ir.cpp | 22 +++++++++++ qv4ir_p.h | 27 +++++++++++++ qv4isel_masm.cpp | 5 +++ tests/regexp.1.js | 4 ++ 11 files changed, 213 insertions(+), 13 deletions(-) create mode 100644 tests/regexp.1.js diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 285f1643fe..e4c0d3d8e1 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -229,6 +229,23 @@ void ScriptFunction::call(VM::Context *ctx) function->code(ctx, function->codeData); } +Value RegExpObject::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) +{ + QString n = name->toQString(); + if (n == QLatin1String("source")) + return Value::fromString(ctx, value.pattern()); + else if (n == QLatin1String("global")) + return Value::fromBoolean(global); + else if (n == QLatin1String("ignoreCase")) + return Value::fromBoolean(value.patternOptions() & QRegularExpression::CaseInsensitiveOption); + else if (n == QLatin1String("multiline")) + return Value::fromBoolean(value.patternOptions() & QRegularExpression::MultilineOption); + else if (n == QLatin1String("lastIndex")) + return lastIndex; + return Object::getProperty(ctx, name, attributes); +} + + void ScriptFunction::construct(VM::Context *ctx) { Object *obj = ctx->engine->newObject(); @@ -487,9 +504,16 @@ FunctionObject *ExecutionEngine::newDateCtor(Context *ctx) return new DateCtor(ctx); } -Object *ExecutionEngine::newRegExpObject(const Value &value) +Object *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) { - Object *object = new RegExpObject(value); + bool global = (flags & IR::RegExp::RegExp_Global); + QRegularExpression::PatternOptions options = 0; + if (flags & IR::RegExp::RegExp_IgnoreCase) + options |= QRegularExpression::CaseInsensitiveOption; + if (flags & IR::RegExp::RegExp_Multiline) + options |= QRegularExpression::MultilineOption; + + Object *object = new RegExpObject(QRegularExpression(pattern, options), global); object->prototype = regExpPrototype; return object; } diff --git a/qmljs_objects.h b/qmljs_objects.h index 39a03bacba..a4c9082410 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -46,6 +46,7 @@ #include #include +#include #include #include @@ -393,10 +394,13 @@ struct ScriptFunction: FunctionObject { }; struct RegExpObject: Object { - Value value; - RegExpObject(const Value &value): value(value) {} + QRegularExpression value; + Value lastIndex; + bool global; + RegExpObject(const QRegularExpression &value, bool global): value(value), lastIndex(Value::fromInt32(0)), global(global) {} virtual QString className() { return QStringLiteral("RegExp"); } virtual RegExpObject *asRegExpObject() { return this; } + virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes); }; struct ErrorObject: Object { @@ -487,7 +491,7 @@ struct ExecutionEngine Object *newDateObject(const Value &value); FunctionObject *newDateCtor(Context *ctx); - Object *newRegExpObject(const Value &value); + Object *newRegExpObject(const QString &pattern, int flags); FunctionObject *newRegExpCtor(Context *ctx); Object *newErrorObject(const Value &value); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 5e6bcc772b..8a1ffc2f36 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -189,6 +189,11 @@ bool Value::isDateObject() const return isObject() ? objectValue()->asDateObject() != 0 : false; } +bool Value::isRegExpObject() const +{ + return isObject() ? objectValue()->asRegExpObject() != 0 : false; +} + bool Value::isArrayObject() const { return isObject() ? objectValue()->asArrayObject() != 0 : false; @@ -234,6 +239,11 @@ DateObject *Value::asDateObject() const return isObject() ? objectValue()->asDateObject() : 0; } +RegExpObject *Value::asRegExpObject() const +{ + return isObject() ? objectValue()->asRegExpObject() : 0; +} + ArrayObject *Value::asArrayObject() const { return isObject() ? objectValue()->asArrayObject() : 0; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 213186af14..7f33fde0c7 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -83,6 +83,7 @@ struct BooleanObject; struct NumberObject; struct StringObject; struct DateObject; +struct RegExpObject; struct ArrayObject; struct ErrorObject; struct ActivationObject; @@ -412,6 +413,7 @@ struct Value bool isNumberObject() const; bool isStringObject() const; bool isDateObject() const; + bool isRegExpObject() const; bool isArrayObject() const; bool isErrorObject() const; bool isArgumentsObject() const; @@ -422,6 +424,7 @@ struct Value NumberObject *asNumberObject() const; StringObject *asStringObject() const; DateObject *asDateObject() const; + RegExpObject *asRegExpObject() const; ArrayObject *asArrayObject() const; ErrorObject *asErrorObject() const; ActivationObject *asArgumentsObject() const; diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 252c67293f..5dd0041b2e 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -90,6 +90,7 @@ struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor virtual void visitConst(IR::Const *) {} virtual void visitString(IR::String *) {} + virtual void visitRegExp(IR::RegExp *) {} virtual void visitName(IR::Name *) {} virtual void visitClosure(IR::Closure *) {} virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); } @@ -1212,9 +1213,9 @@ bool Codegen::visit(PreIncrementExpression *ast) return false; } -bool Codegen::visit(RegExpLiteral *) +bool Codegen::visit(RegExpLiteral *ast) { - assert(!"not implemented"); + _expr.code = _block->REGEXP(_function->newString(ast->pattern.toString()), ast->flags); return false; } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 2af450c923..38d2119e71 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -2435,12 +2436,66 @@ RegExpCtor::RegExpCtor(Context *scope) void RegExpCtor::construct(Context *ctx) { - ctx->thisObject = Value::fromObject(ctx->engine->newStringObject(Value::undefinedValue())); +// if (ctx->argumentCount > 2) { +// ctx->throwTypeError(); +// return; +// } + + Value r = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); + Value f = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); + if (RegExpObject *re = r.asRegExpObject()) { + if (!f.isUndefined()) { + ctx->throwTypeError(); + return; + } + ctx->result = Value::fromObject(new RegExpObject(re->value, false)); + return; + } + + if (r.isUndefined()) + r = Value::fromString(ctx, QString()); + else if (!r.isString()) + r = __qmljs_to_string(r, ctx); + + bool global = false; + QRegularExpression::PatternOptions options = QRegularExpression::NoPatternOption; + if (!f.isUndefined()) { + f = __qmljs_to_string(f, ctx); + QString str = f.stringValue()->toQString(); + for (int i = 0; i < str.length(); ++i) { + if (str.at(i) == QChar('g') && !global) { + global = true; + } else if (str.at(i) == QChar('i') && !(options & QRegularExpression::CaseInsensitiveOption)) { + options |= QRegularExpression::CaseInsensitiveOption; + } else if (str.at(i) == QChar('m') && !(options & QRegularExpression::MultilineOption)) { + options |= QRegularExpression::MultilineOption; + } else { + ctx->throwTypeError(); + return; + } + } + } + + QRegularExpression re(r.stringValue()->toQString(), options); + if (!re.isValid()) { + ctx->throwTypeError(); + return; + } + ctx->thisObject = Value::fromObject(new RegExpObject(re, global)); } void RegExpCtor::call(Context *ctx) { - ctx->result = Value::fromObject(ctx->engine->newRegExpObject(Value::undefinedValue())); + if (ctx->argumentCount > 0 && ctx->argument(0).isRegExpObject()) { + if (ctx->argumentCount == 1 || ctx->argument(1).isUndefined()) { + ctx->result = ctx->argument(0); + return; + } + } + Value that = ctx->thisObject; + construct(ctx); + ctx->result = ctx->thisObject; + ctx->thisObject = that; } void RegExpPrototype::init(Context *ctx, const Value &ctor) @@ -2454,17 +2509,62 @@ void RegExpPrototype::init(Context *ctx, const Value &ctor) void RegExpPrototype::method_exec(Context *ctx) { - ctx->throwUnimplemented(QStringLiteral("RegExp.prototype.exec")); + if (RegExpObject *r = ctx->thisObject.asRegExpObject()) { + Value arg = ctx->argument(0); + arg = __qmljs_to_string(arg, ctx); + QString s = arg.stringValue()->toQString(); + + int offset = r->global ? r->lastIndex.toInt32(ctx) : 0; + if (offset < 0 || offset > s.length()) { + ctx->result = Value::nullValue(); + return; + } + + QRegularExpressionMatch match = r->value.match(s, offset); + if (!match.hasMatch()) { + ctx->result = Value::nullValue(); + return; + } + + // fill in result data + ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); + int captured = match.lastCapturedIndex(); + for (int i = 0; i <= captured; ++i) + array->value.push(Value::fromString(ctx, match.captured(i))); + + array->setProperty(ctx, QLatin1String("index"), Value::fromInt32(match.capturedStart(0))); + array->setProperty(ctx, QLatin1String("input"), arg); + + if (r->global) + r->lastIndex = Value::fromInt32(match.capturedEnd(0)); + + ctx->result = Value::fromObject(array); + } else { + ctx->throwTypeError(); + } } void RegExpPrototype::method_test(Context *ctx) { - ctx->throwUnimplemented(QStringLiteral("RegExp.prototype.test")); + method_exec(ctx); + ctx->result = Value::fromBoolean(!ctx->result.isNull()); } void RegExpPrototype::method_toString(Context *ctx) { - ctx->throwUnimplemented(QStringLiteral("RegExp.prototype.toString")); + if (RegExpObject *r = ctx->thisObject.asRegExpObject()) { + QString result = QChar('/') + r->value.pattern(); + result += QChar('/'); + QRegularExpression::PatternOptions o = r->value.patternOptions(); + // ### 'g' option missing + if (o & QRegularExpression::CaseInsensitiveOption) + result += QChar('i'); + if (o & QRegularExpression::MultilineOption) + result += QChar('m'); + ctx->result = Value::fromString(ctx, result); + } else { + ctx->throwTypeError(); + } } // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index c65ab81a84..b918973bf4 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -286,7 +286,7 @@ struct RegExpCtor: FunctionObject struct RegExpPrototype: RegExpObject { - RegExpPrototype(): RegExpObject(Value::fromDouble(qSNaN())) {} + RegExpPrototype(): RegExpObject(QRegularExpression(), false) {} void init(Context *ctx, const Value &ctor); static void method_exec(Context *ctx); diff --git a/qv4ir.cpp b/qv4ir.cpp index 8248cbb647..f3db7e975d 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -182,6 +182,21 @@ QString String::escape(const QString &s) return r; } +void RegExp::dump(QTextStream &out) +{ + char f[3]; + int i = 0; + if (flags & RegExp_Global) + f[i++] = 'g'; + if (flags & RegExp_IgnoreCase) + f[i++] = 'i'; + if (flags & RegExp_Multiline) + f[i++] = 'm'; + f[i] = 0; + + out << '/' << *value << '/' << f; +} + void Name::init(const QString *id, quint32 line, quint32 column) { this->id = id; @@ -401,6 +416,13 @@ Expr *BasicBlock::STRING(const QString *value) return e; } +Expr *BasicBlock::REGEXP(const QString *value, int flags) +{ + RegExp *e = function->New(); + e->init(value, flags); + return e; +} + Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) { Name *e = function->New(); diff --git a/qv4ir_p.h b/qv4ir_p.h index a457aa8fc0..efae2e42a8 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -87,6 +87,7 @@ struct Expr; // expressions struct Const; struct String; +struct RegExp; struct Name; struct Temp; struct Closure; @@ -158,6 +159,7 @@ struct ExprVisitor { virtual ~ExprVisitor() {} virtual void visitConst(Const *) = 0; virtual void visitString(String *) = 0; + virtual void visitRegExp(RegExp *) = 0; virtual void visitName(Name *) = 0; virtual void visitTemp(Temp *) = 0; virtual void visitClosure(Closure *) = 0; @@ -186,6 +188,7 @@ struct Expr { virtual bool isLValue() { return false; } virtual Const *asConst() { return 0; } virtual String *asString() { return 0; } + virtual RegExp *asRegExp() { return 0; } virtual Name *asName() { return 0; } virtual Temp *asTemp() { return 0; } virtual Closure *asClosure() { return 0; } @@ -240,6 +243,29 @@ struct String: Expr { static QString escape(const QString &s); }; +struct RegExp: Expr { + // needs to be compatible with the flags in the lexer + enum Flags { + RegExp_Global = 0x01, + RegExp_IgnoreCase = 0x02, + RegExp_Multiline = 0x04 + }; + + const QString *value; + int flags; + + void init(const QString *value, int flags) + { + this->value = value; + this->flags = flags; + } + + virtual void accept(ExprVisitor *v) { v->visitRegExp(this); } + virtual RegExp *asRegExp() { return this; } + + virtual void dump(QTextStream &out); +}; + struct Name: Expr { enum Builtin { builtin_invalid, @@ -644,6 +670,7 @@ struct BasicBlock { Expr *CONST(Type type, double value); Expr *STRING(const QString *value); + Expr *REGEXP(const QString *value, int flags); Name *NAME(const QString &id, quint32 line, quint32 column); Name *NAME(Name::Builtin builtin, quint32 line, quint32 column); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 249817ea6e..9d01a8a9d2 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -376,6 +376,11 @@ void InstructionSelection::visitMove(IR::Move *s) Value v = Value::fromString(_engine->newString(*str->value)); storeValue(v, dest); return; + } else if (IR::RegExp *re = s->source->asRegExp()) { + Address dest = loadTempAddress(Gpr0, t); + Value v = Value::fromObject(_engine->newRegExpObject(*re->value, re->flags)); + storeValue(v, dest); + return; } else if (IR::Closure *clos = s->source->asClosure()) { generateFunctionCall(t, __qmljs_init_closure, TrustedImmPtr(clos->value), ContextRegister); return; diff --git a/tests/regexp.1.js b/tests/regexp.1.js new file mode 100644 index 0000000000..c508bd53bb --- /dev/null +++ b/tests/regexp.1.js @@ -0,0 +1,4 @@ +var re = new RegExp("abc") +print(re) +var match = re.exec("xxxabc") +print(match.length, match.index, match[0]) -- cgit v1.2.3 From 06f90a63f99a3de0831b654cad2b61549f23647b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 20 Oct 2012 14:13:23 +0200 Subject: Fix build on Ubuntu 12.10 Include unistd.h for sysconf as per man-page. Change-Id: Iafd7687e6f12beda5ace1442c4d436c051d1beb0 Reviewed-by: Lars Knoll --- masm/stubs/ExecutableAllocator.h | 1 + 1 file changed, 1 insertion(+) diff --git a/masm/stubs/ExecutableAllocator.h b/masm/stubs/ExecutableAllocator.h index 37700c27ba..7b3004aa90 100644 --- a/masm/stubs/ExecutableAllocator.h +++ b/masm/stubs/ExecutableAllocator.h @@ -45,6 +45,7 @@ #include #include +#include namespace JSC { -- cgit v1.2.3 From 4b15ce81039ef495c0af4074d96c2885f3c80615 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 20 Oct 2012 14:13:42 +0200 Subject: Fix build on ia32 Fix undefinedValue() and nullValue() initializers. Change-Id: I7117331137c7d76b39099b57a5dc17b15136fd26 Reviewed-by: Lars Knoll --- qmljs_runtime.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 7f33fde0c7..012c556ce4 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -488,7 +488,7 @@ inline Value Value::undefinedValue() v.val = quint64(_Undefined_Type) << Tag_Shift; #else v.tag = _Undefined_Type; - v.int_32 = (bool)b; + v.int_32 = 0; #endif return v; } @@ -500,7 +500,7 @@ inline Value Value::nullValue() v.val = quint64(_Null_Type) << Tag_Shift; #else v.tag = _Null_Type; - v.int_32 = (bool)b; + v.int_32 = 0; #endif return v; } -- cgit v1.2.3 From acc3fdb393431448a9aa76c2cb75e95f54198146 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 20 Oct 2012 13:48:43 +0200 Subject: Cleanup: Reduce use of Gpr0 We don't need to explicitly operate on Gpr0 for the return value when we can use the ReturnValueRegister alias instead. No change in code generation because on Amd64 and Ia32 Gpr0 == ReturnValueRegister. Change-Id: Ia21613a51878093a9fb425879645ea3b8a4dbf72 Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 9d01a8a9d2..3996ef7901 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -646,16 +646,16 @@ void InstructionSelection::visitCJump(IR::CJump *s) Address data = temp; data.offset += offsetof(VM::Value, int_32); - load32(data, Gpr0); + load32(data, ReturnValueRegister); Jump testBoolean = jump(); booleanConversion.link(this); { - generateFunctionCall(Gpr0, __qmljs_to_boolean, t, ContextRegister); + generateFunctionCall(ReturnValueRegister, __qmljs_to_boolean, t, ContextRegister); } testBoolean.link(this); - Jump target = branch32(NotEqual, Gpr0, TrustedImm32(0)); + Jump target = branch32(NotEqual, ReturnValueRegister, TrustedImm32(0)); _patches[s->iftrue].append(target); jumpToBlock(s->iffalse); @@ -681,9 +681,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) } // switch generateFunctionCallImp(ReturnValueRegister, opName, op, l, r, ContextRegister); - move(ReturnValueRegister, Gpr0); - Jump target = branch32(NotEqual, Gpr0, TrustedImm32(0)); + Jump target = branch32(NotEqual, ReturnValueRegister, TrustedImm32(0)); _patches[s->iftrue].append(target); jumpToBlock(s->iffalse); -- cgit v1.2.3 From 7b9643428c26dc32fbc7c2a081d26e5593155995 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 20 Oct 2012 14:17:03 +0200 Subject: Cleanup: Eliminate Gpr0-3 register aliases Instead introduce a ScratchRegister which is explicitly different from the ReturnValueRegister and is therefore safe to use. Change-Id: I4675b6a4d242f3e02a4e848c7b65660ffa97cfe6 Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 12 ++++++------ qv4isel_masm_p.h | 48 +++++++++++++++++++----------------------------- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 3996ef7901..cd294cac28 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -341,7 +341,7 @@ void InstructionSelection::visitMove(IR::Move *s) } return; } else if (IR::Const *c = s->source->asConst()) { - Address dest = loadTempAddress(Gpr0, t); + Address dest = loadTempAddress(ScratchRegister, t); Value v; switch (c->type) { case IR::NullType: @@ -372,12 +372,12 @@ void InstructionSelection::visitMove(IR::Move *s) copyValue(t, t2); return; } else if (IR::String *str = s->source->asString()) { - Address dest = loadTempAddress(Gpr0, t); + Address dest = loadTempAddress(ScratchRegister, t); Value v = Value::fromString(_engine->newString(*str->value)); storeValue(v, dest); return; } else if (IR::RegExp *re = s->source->asRegExp()) { - Address dest = loadTempAddress(Gpr0, t); + Address dest = loadTempAddress(ScratchRegister, t); Value v = Value::fromObject(_engine->newRegExpObject(*re->value, re->flags)); storeValue(v, dest); return; @@ -639,7 +639,7 @@ void InstructionSelection::jumpToBlock(IR::BasicBlock *target) void InstructionSelection::visitCJump(IR::CJump *s) { if (IR::Temp *t = s->cond->asTemp()) { - Address temp = loadTempAddress(Gpr0, t); + Address temp = loadTempAddress(ScratchRegister, t); Address tag = temp; tag.offset += offsetof(VM::Value, tag); Jump booleanConversion = branch32(NotEqual, tag, TrustedImm32(VM::Value::Boolean_Type)); @@ -744,8 +744,8 @@ template void InstructionSelection::copyValue(Result result, Source source) { #if CPU(X86_64) - loadArgument(source, Gpr0); - storeArgument(Gpr0, result); + loadArgument(source, ScratchRegister); + storeArgument(ScratchRegister, result); #else loadDouble(source, FPGpr0); storeDouble(FPGpr0, result); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index bb91fa044b..e2242e1035 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -70,12 +70,9 @@ protected: static const RegisterID StackPointerRegister = JSC::X86Registers::esp; static const RegisterID ContextRegister = JSC::X86Registers::esi; static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID Gpr0 = JSC::X86Registers::eax; - static const RegisterID Gpr1 = JSC::X86Registers::ecx; - static const RegisterID Gpr2 = JSC::X86Registers::edx; - static const RegisterID Gpr3 = JSC::X86Registers::esi; - static const RegisterID CalleeSavedFirstRegister = Gpr3; - static const RegisterID CalleeSavedLastRegister = Gpr3; + static const RegisterID ScratchRegister = JSC::X86Registers::ecx; + static const RegisterID CalleeSavedFirstRegister = ScratchRegister; + static const RegisterID CalleeSavedLastRegister = ScratchRegister; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; static const int RegisterSize = 4; @@ -95,10 +92,7 @@ protected: static const RegisterID StackPointerRegister = JSC::X86Registers::esp; static const RegisterID ContextRegister = JSC::X86Registers::r14; static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID Gpr0 = JSC::X86Registers::eax; - static const RegisterID Gpr1 = JSC::X86Registers::ecx; - static const RegisterID Gpr2 = JSC::X86Registers::edx; - static const RegisterID Gpr3 = JSC::X86Registers::esi; + static const RegisterID ScratchRegister = JSC::MacroAssemblerX86Common::scratchRegister; // r11 static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; static const int RegisterSize = 8; @@ -125,10 +119,7 @@ protected: static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; static const RegisterID ContextRegister = JSC::ARMRegisters::r5; static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; - static const RegisterID Gpr0 = JSC::ARMRegisters::r6; - static const RegisterID Gpr1 = JSC::ARMRegisters::r7; - static const RegisterID Gpr2 = JSC::ARMRegisters::r8; - static const RegisterID Gpr3 = JSC::ARMRegisters::r10; + static const RegisterID ScratchRegister = JSC::ARMRegisters::r6; static const RegisterID CalleeSavedFirstRegister = JSC::ARMRegisters::r4; static const RegisterID CalleeSavedLastRegister = JSC::ARMRegisters::r11; static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; @@ -289,8 +280,7 @@ private: void storeArgument(RegisterID src, IR::Temp *temp) { if (temp) { - // ### Should use some ScratchRegister here - Pointer addr = loadTempAddress(Gpr3, temp); + Pointer addr = loadTempAddress(ScratchRegister, temp); #ifdef VALUE_FITS_IN_REGISTER storePtr(src, addr); #else @@ -322,27 +312,27 @@ private: void push(const Pointer& ptr) { - addPtr(TrustedImm32(ptr.offset), ptr.base, Gpr0); - push(Gpr0); + addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister); + push(ScratchRegister); } void push(VM::Value value) { #ifdef VALUE_FITS_IN_REGISTER - move(TrustedImm64(value.val), Gpr0); - push(Gpr0); + move(TrustedImm64(value.val), ScratchRegister); + push(ScratchRegister); #else - move(TrustedImm32(value.int_32), Gpr0); - push(Gpr0); - move(TrustedImm32(value.tag), Gpr0); - push(Gpr0); + move(TrustedImm32(value.int_32), ScratchRegister); + push(ScratchRegister); + move(TrustedImm32(value.tag), ScratchRegister); + push(ScratchRegister); #endif } void push(IR::Temp* temp) { if (temp) { - Address addr = loadTempAddress(Gpr0, temp); + Address addr = loadTempAddress(ScratchRegister, temp); addr.offset += 4; push(addr); addr.offset -= 4; @@ -355,8 +345,8 @@ private: void push(TrustedImmPtr ptr) { - move(TrustedImmPtr(ptr), Gpr0); - push(Gpr0); + move(TrustedImmPtr(ptr), ScratchRegister); + push(ScratchRegister); } void push(VM::String* name) @@ -512,14 +502,14 @@ private: using JSC::MacroAssembler::loadDouble; void loadDouble(IR::Temp* temp, FPRegisterID dest) { - Pointer ptr = loadTempAddress(Gpr0, temp); + Pointer ptr = loadTempAddress(ScratchRegister, temp); loadDouble(ptr, dest); } using JSC::MacroAssembler::storeDouble; void storeDouble(FPRegisterID source, IR::Temp* temp) { - Pointer ptr = loadTempAddress(Gpr0, temp); + Pointer ptr = loadTempAddress(ScratchRegister, temp); storeDouble(source, ptr); } -- cgit v1.2.3 From 6d05c917cea8e564014c52199a81ea84aedc1130 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 22 Oct 2012 10:40:20 +0200 Subject: Fix: implement missing virtual method. Change-Id: Iec6a70155ff2343dafa2116fa54740e891539c61 Reviewed-by: Simon Hausmann --- qv4isel_llvm.cpp | 8 ++++++++ qv4isel_llvm_p.h | 1 + 2 files changed, 9 insertions(+) diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 40434ae07c..950f855bd4 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -623,6 +623,14 @@ void LLVMInstructionSelection::visitString(IR::String *e) _llvmValue = CreateLoad(tmp); } +void LLVMInstructionSelection::visitRegExp(IR::RegExp *e) +{ + e->dump(qerr); + qerr << endl; + Q_UNIMPLEMENTED(); + _llvmValue = llvm::Constant::getNullValue(_valueTy); +} + void LLVMInstructionSelection::visitName(IR::Name *e) { llvm::Value *result = newLLVMTemp(_valueTy); diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index a2446a5aa0..5c914433bf 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -98,6 +98,7 @@ public: virtual void visitConst(IR::Const *); virtual void visitString(IR::String *); + virtual void visitRegExp(IR::RegExp *); virtual void visitName(IR::Name *); virtual void visitTemp(IR::Temp *); virtual void visitClosure(IR::Closure *); -- cgit v1.2.3 From af06d1a1a5db83a290a4518272f9aa05423ccdc7 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 22 Oct 2012 10:33:04 +0200 Subject: More LLVM code generation. This should complete the expressions. Change-Id: Ic920ceb85cab38093b565acd1c14aeddf20d0bb3 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 110 +++++++++++++++++++++++++++++++++++++++++++ qv4isel_llvm.cpp | 139 +++++++++++++++++++++++++++---------------------------- 2 files changed, 179 insertions(+), 70 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 8771e6da67..fbc534f1ed 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -271,6 +271,116 @@ void __qmljs_llvm_inplace_ushr_name(Context *ctx, String *dest, Value *src) __qmljs_inplace_ushr_name(*src, dest, ctx); } +void __qmljs_llvm_inplace_bit_and_element(Context *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_bit_and_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_bit_or_element(Context *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_bit_or_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_bit_xor_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_add_element(Context *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_add_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_sub_element(Context *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_sub_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_mul_element(Context *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_mul_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_div_element(Context *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_div_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_mod_element(Context *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_mod_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_shl_element(Context *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_shl_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_shr_element(Context *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_shr_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_ushr_element(Context *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_ushr_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_bit_and_member(Context *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_bit_and_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_bit_or_member(Context *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_bit_or_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_bit_xor_member(Context *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_bit_xor_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_add_member(Context *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_add_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_sub_member(Context *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_sub_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_mul_member(Context *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_mul_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_div_member(Context *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_div_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_mod_member(Context *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_mod_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_shl_member(Context *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_shl_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_shr_member(Context *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_shr_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_ushr_member(Context *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_ushr_member(*value, *base, member, ctx); +} + String *__qmljs_llvm_identifier_from_utf8(Context *ctx, const char *str) { return __qmljs_identifier_from_utf8(ctx, str); // ### make it unique diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 950f855bd4..eae3bc46a3 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -432,22 +432,21 @@ void LLVMInstructionSelection::visitMove(IR::Move *s) return; } } else { - // inplace assignment, e.g. x += 1, ++x, ... if (IR::Temp *t = s->target->asTemp()) { if (IR::Temp *t2 = s->source->asTemp()) { const char *opName = 0; switch (s->op) { -// case IR::OpBitAnd: setOp(op, opName, __qmljs_bit_and); break; -// case IR::OpBitOr: setOp(op, opName, __qmljs_bit_or); break; -// case IR::OpBitXor: setOp(op, opName, __qmljs_bit_xor); break; + case IR::OpBitAnd: opName = "__qmljs_llvm_bit_and"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_bit_or"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_bit_xor"; break; case IR::OpAdd: opName = "__qmljs_llvm_add"; break; -// case IR::OpSub: setOp(op, opName, __qmljs_sub); break; -// case IR::OpMul: setOp(op, opName, __qmljs_mul); break; -// case IR::OpDiv: setOp(op, opName, __qmljs_div); break; -// case IR::OpMod: setOp(op, opName, __qmljs_mod); break; -// case IR::OpLShift: setOp(op, opName, __qmljs_shl); break; -// case IR::OpRShift: setOp(op, opName, __qmljs_shr); break; -// case IR::OpURShift: setOp(op, opName, __qmljs_ushr); break; + case IR::OpSub: opName = "__qmljs_llvm_sub"; break; + case IR::OpMul: opName = "__qmljs_llvm_mul"; break; + case IR::OpDiv: opName = "__qmljs_llvm_div"; break; + case IR::OpMod: opName = "__qmljs_llvm_mod"; break; + case IR::OpLShift: opName = "__qmljs_llvm_shl"; break; + case IR::OpRShift: opName = "__qmljs_llvm_shr"; break; + case IR::OpURShift: opName = "__qmljs_llvm_ushr"; break; default: Q_UNREACHABLE(); break; @@ -463,7 +462,7 @@ void LLVMInstructionSelection::visitMove(IR::Move *s) } } } else if (IR::Name *n = s->target->asName()) { - qDebug()<<__FILE__<<__LINE__; + // inplace assignment, e.g. x += 1, ++x, ... if (IR::Temp *t = s->source->asTemp()) { const char *opName = 0; switch (s->op) { @@ -492,65 +491,65 @@ void LLVMInstructionSelection::visitMove(IR::Move *s) } } } else if (IR::Subscript *ss = s->target->asSubscript()) { - qDebug()<<__FILE__<<__LINE__; -// if (IR::Temp *t = s->source->asTemp()) { -// void (*op)(Value base, Value index, Value value, Context *ctx) = 0; -// const char *opName = 0; -// switch (s->op) { -// case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; -// case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_element); break; -// case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_element); break; -// case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_element); break; -// case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_element); break; -// case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_element); break; -// case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_element); break; -// case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_element); break; -// case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_element); break; -// case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_element); break; -// case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_element); break; -// default: -// Q_UNREACHABLE(); -// break; -// } - -// if (op) { -// IR::Temp* base = ss->base->asTemp(); -// IR::Temp* index = ss->index->asTemp(); -// generateFunctionCallImp(Void, opName, op, base, index, t, ContextRegister); -// checkExceptions(); -// } -// return; -// } + if (IR::Temp *t = s->source->asTemp()) { + const char *opName = 0; + switch (s->op) { + case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_element"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_element"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_element"; break; + case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_element"; break; + case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_element"; break; + case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_element"; break; + case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_element"; break; + case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_element"; break; + case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_element"; break; + case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_element"; break; + case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_element"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *base = getLLVMTemp(ss->base->asTemp()); + llvm::Value *index = getLLVMTemp(ss->index->asTemp()); + llvm::Value *value = getLLVMTemp(t); + CreateCall4(_llvmModule->getFunction(opName), + _llvmFunction->arg_begin(), base, index, value); + // TODO: checkExceptions(); + } + return; + } } else if (IR::Member *m = s->target->asMember()) { - qDebug()<<__FILE__<<__LINE__; -// if (IR::Temp *t = s->source->asTemp()) { -// void (*op)(Value value, Value base, String *name, Context *ctx) = 0; -// const char *opName = 0; -// switch (s->op) { -// case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; -// case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_member); break; -// case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_member); break; -// case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_member); break; -// case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_member); break; -// case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_member); break; -// case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_member); break; -// case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_member); break; -// case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_member); break; -// case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_member); break; -// case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_member); break; -// default: -// Q_UNREACHABLE(); -// break; -// } - -// if (op) { -// IR::Temp* base = m->base->asTemp(); -// String* member = identifier(*m->name); -// generateFunctionCallImp(Void, opName, op, t, base, member, ContextRegister); -// checkExceptions(); -// } -// return; -// } + if (IR::Temp *t = s->source->asTemp()) { + const char *opName = 0; + switch (s->op) { + case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_member"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_member"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_member"; break; + case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_member"; break; + case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_member"; break; + case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_member"; break; + case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_member"; break; + case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_member"; break; + case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_member"; break; + case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_member"; break; + case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_member"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *base = getLLVMTemp(m->base->asTemp()); + llvm::Value *member = getIdentifier(*m->name); + llvm::Value *value = getLLVMTemp(t); + CreateCall4(_llvmModule->getFunction(opName), + _llvmFunction->arg_begin(), value, base, member); + // TODO: checkExceptions(); + } + return; + } } } -- cgit v1.2.3 From c469b31a518cffef3aec4ca100f21935496215d1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 21 Oct 2012 18:22:26 +0200 Subject: Fix ScratchRegister usage on x86_64 r11 is already being used quite a bit internally in the macro assembler, leading to collisions and crashes. Rather use r10 for most scratch operations. Change-Id: I27bbbf825ca0e90af4ec85b8da12c25ae8ca00df Reviewed-by: Simon Hausmann --- qv4isel_masm_p.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index e2242e1035..7d0b81afe7 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -92,7 +92,8 @@ protected: static const RegisterID StackPointerRegister = JSC::X86Registers::esp; static const RegisterID ContextRegister = JSC::X86Registers::r14; static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID ScratchRegister = JSC::MacroAssemblerX86Common::scratchRegister; // r11 + static const RegisterID ScratchRegister = JSC::X86Registers::r10; + static const RegisterID ScratchRegister2 = JSC::X86Registers::r11; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; static const int RegisterSize = 8; @@ -280,7 +281,7 @@ private: void storeArgument(RegisterID src, IR::Temp *temp) { if (temp) { - Pointer addr = loadTempAddress(ScratchRegister, temp); + Pointer addr = loadTempAddress(ScratchRegister2, temp); #ifdef VALUE_FITS_IN_REGISTER storePtr(src, addr); #else -- cgit v1.2.3 From 89b2015c02cb42f688ee114c5b3eb24c9da8aa69 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 22 Oct 2012 11:37:03 +0200 Subject: [masm] Cleanup: Use load64/store64 masm interface The API allows us to distinguish between loading/storing a 64-bit value or a pointer and we should make use of that to make the code more readable and to be able to run on x32 in the future. Change-Id: Id2e1d8af566486540a1f6d086fb8db91f0fed6f8 Reviewed-by: Lars Knoll --- qv4isel_masm_p.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 7d0b81afe7..b0e415873b 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -255,16 +255,18 @@ private: addPtr(TrustedImm32(ptr.offset), ptr.base, dest); } +#ifdef VALUE_FITS_IN_REGISTER void loadArgument(IR::Temp* temp, RegisterID dest) { if (!temp) { VM::Value undefined = VM::Value::undefinedValue(); - move(TrustedImmPtr((const void *)undefined.val), dest); + move(TrustedImm64(undefined.val), dest); } else { Pointer addr = loadTempAddress(dest, temp); - loadPtr(addr, dest); + load64(addr, dest); } } +#endif void loadArgument(VM::String* string, RegisterID dest) { @@ -283,7 +285,7 @@ private: if (temp) { Pointer addr = loadTempAddress(ScratchRegister2, temp); #ifdef VALUE_FITS_IN_REGISTER - storePtr(src, addr); + store64(src, addr); #else // If the value doesn't fit into a register, then the // register contains the address to where the argument @@ -296,7 +298,7 @@ private: #ifdef VALUE_FITS_IN_REGISTER void storeArgument(RegisterID src, const Pointer &dest) { - storePtr(src, dest); + store64(src, dest); } #endif @@ -526,7 +528,7 @@ private: void storeValue(VM::Value value, Address destination) { #ifdef VALUE_FITS_IN_REGISTER - storePtr(TrustedImmPtr((void *)value.val), destination); + store64(TrustedImm64(value.val), destination); #else store32(TrustedImm32(value.int_32), destination); destination.offset += 4; -- cgit v1.2.3 From 43f970df35a5fa71e0616fc8f9f56a9c4c737291 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 22 Oct 2012 15:03:43 +0200 Subject: [masm] Cleanup: Eliminate ScratchRegister2 on AMD64 Functions like loadArgument and storeValue that work right above the macro assembler may use ScratchRegister because they only call functions that do not "allocate" that register themselves. copyValue on the other hand is a higher-level function and may not use ScratchRegister because the functions it calls (i.e. loadArgument) may clobber it. In the 64-bit case of quickly copying one VM value to another location, we might as well use the ReturnValueRegister as intermediate register and therefore re-allow the use of ScratchRegister in the lower-level loadArgument/storeArgument functions. Change-Id: I9f0c0d326e5af87101972f0cceb86cffe4ebdd0d Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 8 +++++--- qv4isel_masm_p.h | 3 +-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index cd294cac28..e5c78ab1db 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -743,9 +743,11 @@ void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* na template void InstructionSelection::copyValue(Result result, Source source) { -#if CPU(X86_64) - loadArgument(source, ScratchRegister); - storeArgument(ScratchRegister, result); +#ifdef VALUE_FITS_IN_REGISTER + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgument(source, ReturnValueRegister); + storeArgument(ReturnValueRegister, result); #else loadDouble(source, FPGpr0); storeDouble(FPGpr0, result); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index b0e415873b..8565ad265b 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -93,7 +93,6 @@ protected: static const RegisterID ContextRegister = JSC::X86Registers::r14; static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; static const RegisterID ScratchRegister = JSC::X86Registers::r10; - static const RegisterID ScratchRegister2 = JSC::X86Registers::r11; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; static const int RegisterSize = 8; @@ -283,7 +282,7 @@ private: void storeArgument(RegisterID src, IR::Temp *temp) { if (temp) { - Pointer addr = loadTempAddress(ScratchRegister2, temp); + Pointer addr = loadTempAddress(ScratchRegister, temp); #ifdef VALUE_FITS_IN_REGISTER store64(src, addr); #else -- cgit v1.2.3 From 2c62e69b8d1b49dd440f17c7de2f93be970699cb Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 23 Oct 2012 16:06:52 +0200 Subject: Added property activation to the interpreter. Change-Id: Icbc900f8d617c8736a5663d19f5691f336bedb30 Reviewed-by: Simon Hausmann --- moth/qv4instr_moth_p.h | 6 ++++++ moth/qv4isel_moth.cpp | 15 +++++++++++++-- moth/qv4vme_moth.cpp | 18 +++++++++++++++++- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index f9931defcb..1637963f90 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -22,6 +22,7 @@ F(Jump, jump) \ F(CJump, jump) \ F(Binop, binop) \ + F(ActivateProperty, activateProperty) #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) # define MOTH_THREADED_INTERPRETER @@ -103,6 +104,10 @@ union Instr int rhsTempIndex; VM::Value (*alu)(const VM::Value , const VM::Value, VM::Context *); }; + struct instr_activateProperty { + MOTH_INSTR_HEADER + VM::String *propName; + }; instr_common common; instr_ret ret; @@ -117,6 +122,7 @@ union Instr instr_call call; instr_jump jump; instr_binop binop; + instr_activateProperty activateProperty; static int size(Type type); }; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 2a65f77a61..6a3e70da2a 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -132,7 +132,7 @@ void InstructionSelection::visitMove(IR::Move *s) } typedef VM::Value (*ALUFunction)(const VM::Value, const VM::Value, VM::Context*); -ALUFunction aluOpFunction(IR::AluOp op) +static inline ALUFunction aluOpFunction(IR::AluOp op) { switch (op) { case IR::OpInvalid: @@ -204,7 +204,18 @@ void InstructionSelection::simpleMove(IR::Move *s) { if (IR::Name *n = s->target->asName()) { Q_UNUSED(n); - qWarning("NAME"); + // set activation property + if (IR::Temp *t = s->source->asTemp()) { + Instruction::LoadTemp load; + load.tempIndex = t->index; + addInstruction(load); + + Instruction::ActivateProperty activate; + activate.propName = _engine->newString(*n->id); + addInstruction(activate); + } else { + Q_UNREACHABLE(); + } } else if (IR::Temp *t = s->target->asTemp()) { if (IR::Name *n = s->source->asName()) { diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index f31fb0a858..a9d7c096d2 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -4,10 +4,19 @@ using namespace QQmlJS; using namespace QQmlJS::Moth; +#ifdef DO_TRACE_INSTR +# define TRACE_INSTR(I) fprintf(stderr, "%s\n", #I); +# define TRACE(n, str, ...) { fprintf(stderr, "-- %s : ", #n); fprintf(stderr, str, __VA_ARGS__); fprintf(stderr, "\n"); } +#else +# define TRACE_INSTR(I) +# define TRACE(n, str, ...) +#endif // DO_TRACE_INSTR + #define MOTH_BEGIN_INSTR_COMMON(I) { \ const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ code += InstrMeta<(int)Instr::I>::Size; \ - Q_UNUSED(instr); + Q_UNUSED(instr); \ + TRACE_INSTR(I) #ifdef MOTH_THREADED_INTERPRETER @@ -40,6 +49,8 @@ using namespace QQmlJS::Moth; static inline VM::Value *tempValue(QQmlJS::VM::Context *context, QVector &stack, int index) { + TRACE(tempValue, "index = %d / arg = %d / local = %d, stack size = %d", index, (-index-1), index - stack.count(), stack.size()); + if (index < 0) { const int arg = -index - 1; return context->arguments + arg; @@ -154,6 +165,11 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code return; MOTH_END_INSTR(Ret) + MOTH_BEGIN_INSTR(ActivateProperty) + TRACE(inline, "property name = %s", instr.propName->toQString().toUtf8().constData()); + __qmljs_set_activation_property(context, instr.propName, tempRegister); + MOTH_END_INSTR(ActivateProperty) + #ifdef MOTH_THREADED_INTERPRETER // nothing to do #else -- cgit v1.2.3 From 5f06c71bc1bf146977b6a189491f73ddf8792777 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 22 Oct 2012 00:18:31 +0200 Subject: Proper exception handling Implement exceptions using setjmp/longjmp. The advantage is that this removes all exception handling overhead from regular code, the only code that still has a (very small) overhead is the try{} catch() {} statement. Change-Id: I43d6a60dfc9dfd4b7a20d2e99ab0a9315b4d8a2f Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 5 ---- main.cpp | 18 +++++++----- qmljs_objects.h | 8 +++++ qmljs_runtime.cpp | 86 +++++++++++++++++++++++++++-------------------------- qmljs_runtime.h | 14 +++++---- qv4codegen.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++++++++------- qv4codegen_p.h | 1 - qv4ir.cpp | 24 ++++++++++++++- qv4ir_p.h | 4 ++- qv4isel_llvm.cpp | 7 ----- qv4isel_masm.cpp | 43 ++++++++++----------------- qv4isel_masm_p.h | 6 +++- 12 files changed, 195 insertions(+), 109 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index fbc534f1ed..158cb66b20 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -457,11 +457,6 @@ void __qmljs_llvm_throw(Context *context, Value *value) __qmljs_throw(*value, context); } -void __qmljs_llvm_rethrow(Context *context, Value *result) -{ - *result = __qmljs_rethrow(context); -} - void __qmljs_llvm_get_this_object(Context *ctx, Value *result) { *result = __qmljs_get_thisObject(ctx); diff --git a/main.cpp b/main.cpp index d20b350344..f5f51c3016 100644 --- a/main.cpp +++ b/main.cpp @@ -44,6 +44,7 @@ #endif #include "qmljs_objects.h" +#include "qmljs_runtime.h" #include "qv4codegen_p.h" #include "qv4isel_masm_p.h" #include "qv4isel_moth_p.h" @@ -239,7 +240,6 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS return; } - ctx->hasUncaughtException = false; if (! ctx->activation.isObject()) ctx->activation = VM::Value::fromObject(new QQmlJS::VM::Object()); @@ -247,6 +247,15 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS ctx->activation.objectValue()->setProperty(ctx, *local, QQmlJS::VM::Value::undefinedValue()); } + void * buf = __qmljs_create_exception_handler(ctx); + if (setjmp(*(jmp_buf *)buf)) { + if (VM::ErrorObject *e = ctx->result.asErrorObject()) + std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; + else + std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; + return; + } + if (useMoth) { Moth::VME vme; vme(ctx, code); @@ -254,12 +263,7 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS globalCode->code(ctx, globalCode->codeData); } - if (ctx->hasUncaughtException) { - if (VM::ErrorObject *e = ctx->result.asErrorObject()) - std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; - else - std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; - } else if (! ctx->result.isUndefined()) { + if (! ctx->result.isUndefined()) { if (! qgetenv("SHOW_EXIT_VALUE").isNull()) std::cout << "exit value: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; } diff --git a/qmljs_objects.h b/qmljs_objects.h index a4c9082410..a98b640675 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -49,6 +49,7 @@ #include #include #include +#include namespace QQmlJS { @@ -459,6 +460,13 @@ struct ExecutionEngine String *id_arguments; String *id___proto__; + struct ExceptionHandler { + Context *context; + jmp_buf stackFrame; + }; + + QVector unwindStack; + ExecutionEngine(); Context *newContext(); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 8a1ffc2f36..f0c73263cf 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -284,7 +284,6 @@ void Context::init(ExecutionEngine *eng) vars = 0; varCount = 0; calledAsConstructor = false; - hasUncaughtException = false; } Value *Context::lookupPropertyDescriptor(String *name) @@ -302,7 +301,7 @@ Value *Context::lookupPropertyDescriptor(String *name) void Context::throwError(Value value) { result = value; - hasUncaughtException = true; + __qmljs_builtin_throw(value, this); } void Context::throwError(const QString &message) @@ -330,10 +329,11 @@ void Context::throwReferenceError(Value value) throwError(Value::fromObject(engine->newErrorObject(Value::fromString(this, msg)))); } -void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc) +void Context::initCallContext(Context *parent, const Value *object, FunctionObject *f, Value *args, unsigned argc) { - engine = e; - parent = f->scope; + engine = parent->engine; + this->parent = f->scope; + assert(this->parent == f->scope); result = Value::undefinedValue(); if (f->needsActivation) @@ -360,23 +360,22 @@ void Context::initCallContext(ExecutionEngine *e, const Value *object, FunctionO vars = f->varList; varCount = f->varCount; locals = varCount ? new Value[varCount] : 0; - hasUncaughtException = false; calledAsConstructor = false; if (varCount) std::fill(locals, locals + varCount, Value::undefinedValue()); } -void Context::leaveCallContext(FunctionObject *f) +void Context::leaveCallContext() { - if (! f->needsActivation) { + if (!activation.isNull()) { delete[] locals; locals = 0; } } -void Context::initConstructorContext(ExecutionEngine *e, Value *object, FunctionObject *f, Value *args, unsigned argc) +void Context::initConstructorContext(Context *parent, Value *object, FunctionObject *f, Value *args, unsigned argc) { - initCallContext(e, object, f, args, argc); + initCallContext(parent, object, f, args, argc); calledAsConstructor = true; } @@ -390,7 +389,7 @@ void Context::leaveConstructorContext(FunctionObject *f) if (! thisObject.isObject()) thisObject.objectValue()->prototype = engine->objectPrototype; - leaveCallContext(f); + leaveCallContext(); } extern "C" { @@ -1205,13 +1204,9 @@ Value __qmljs_call_property(Context *context, Value base, String *name, Value *a if (FunctionObject *f = func.asFunctionObject()) { Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->initCallContext(context->engine, &thisObject, f, args, argc); + ctx->initCallContext(context, &thisObject, f, args, argc); f->call(ctx); - if (ctx->hasUncaughtException) { - context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception - context->result = ctx->result; - } - ctx->leaveCallContext(f); + ctx->leaveCallContext(); result = ctx->result; } else { context->throwTypeError(); @@ -1224,13 +1219,9 @@ Value __qmljs_call_function(Context *context, Value thisObject, FunctionObject * Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; const Value *that = thisObject.isUndefined() ? 0 : &thisObject; - ctx->initCallContext(context->engine, that, f, args, argc); + ctx->initCallContext(context, that, f, args, argc); f->call(ctx); - if (ctx->hasUncaughtException) { - context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception - context->result = ctx->result; - } - ctx->leaveCallContext(f); + ctx->leaveCallContext(); return ctx->result; } @@ -1259,12 +1250,8 @@ Value __qmljs_construct_value(Context *context, Value func, Value *args, int arg if (FunctionObject *f = func.asFunctionObject()) { Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->initConstructorContext(context->engine, 0, f, args, argc); + ctx->initConstructorContext(context, 0, f, args, argc); f->construct(ctx); - if (ctx->hasUncaughtException) { - context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception - context->result = ctx->result; - } ctx->leaveConstructorContext(f); return ctx->result; } @@ -1282,13 +1269,9 @@ Value __qmljs_construct_property(Context *context, Value base, String *name, Val if (FunctionObject *f = func.asFunctionObject()) { Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->initConstructorContext(context->engine, 0, f, args, argc); + ctx->initConstructorContext(context, 0, f, args, argc); ctx->calledAsConstructor = true; f->construct(ctx); - if (ctx->hasUncaughtException) { - context->hasUncaughtException = ctx->hasUncaughtException; // propagate the exception - context->result = ctx->result; - } ctx->leaveConstructorContext(f); return ctx->result; } @@ -1298,11 +1281,37 @@ Value __qmljs_construct_property(Context *context, Value base, String *name, Val void __qmljs_throw(Value value, Context *context) { - context->hasUncaughtException = true; - context->result = value; + assert(!context->engine->unwindStack.isEmpty()); + + ExecutionEngine::ExceptionHandler handler = context->engine->unwindStack.last(); + + // clean up call contexts + while (context != handler.context) { + context->leaveCallContext(); + context = context->parent; + } + + handler.context->result = value; + + longjmp(handler.stackFrame, 1); +} + +void *__qmljs_create_exception_handler(Context *context) +{ + context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler()); + ExecutionEngine::ExceptionHandler *handler = &context->engine->unwindStack.last(); + handler->context = context; + return handler->stackFrame; +} + +void __qmljs_delete_exception_handler(Context *context) +{ + assert(!context->engine->unwindStack.isEmpty()); + + context->engine->unwindStack.pop_back(); } -Value __qmljs_rethrow(Context *context) +Value __qmljs_get_exception(Context *context) { return context->result; } @@ -1317,11 +1326,6 @@ void __qmljs_builtin_throw(Value val, Context *context) __qmljs_throw(val, context); } -Value __qmljs_builtin_rethrow(Context *context) -{ - return context->result; -} - } // extern "C" diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 012c556ce4..e424b08bd2 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -105,7 +105,7 @@ Value __qmljs_construct_value(Context *context, Value func, Value *args, int arg Value __qmljs_builtin_typeof(Value val, Context *context); void __qmljs_builtin_throw(Value val, Context *context); -Value __qmljs_builtin_rethrow(Context *context); + // constructors Value __qmljs_init_closure(IR::Function *clos, Context *ctx); @@ -182,7 +182,10 @@ Value __qmljs_delete_value(Context *ctx, Value value); Value __qmljs_typeof(Value value, Context *ctx); void __qmljs_throw(Value value, Context *context); -Value __qmljs_rethrow(Context *context); +// actually returns a jmp_buf * +void *__qmljs_create_exception_handler(Context *context); +void __qmljs_delete_exception_handler(Context *context); +Value __qmljs_get_exception(Context *context); // binary operators Value __qmljs_instanceof(Value left, Value right, Context *ctx); @@ -570,7 +573,6 @@ struct Context { String **vars; unsigned int varCount; int calledAsConstructor; - int hasUncaughtException; Value *lookupPropertyDescriptor(String *name); @@ -600,10 +602,10 @@ struct Context { void throwUnimplemented(const QString &message); #endif - void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc); - void leaveCallContext(FunctionObject *f); + void initCallContext(Context *parent, const Value *object, FunctionObject *f, Value *args, unsigned argc); + void leaveCallContext(); - void initConstructorContext(ExecutionEngine *e, Value *object, FunctionObject *f, Value *args, unsigned argc); + void initConstructorContext(Context *parent, Value *object, FunctionObject *f, Value *args, unsigned argc); void leaveConstructorContext(FunctionObject *f); }; diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 5dd0041b2e..a0377397f5 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -294,7 +294,6 @@ Codegen::Codegen() , _block(0) , _exitBlock(0) , _throwBlock(0) - , _handlersBlock(0) , _returnAddress(0) , _mode(GlobalCode) , _env(0) @@ -1069,8 +1068,9 @@ bool Codegen::visit(FunctionExpression *ast) bool Codegen::visit(IdentifierExpression *ast) { + int index = _env->findMember(ast->name.toString()); + if (! _function->hasDirectEval && _env->parent) { - int index = _env->findMember(ast->name.toString()); if (index != -1) { _expr.code = _block->TEMP(index); return false; @@ -1082,6 +1082,12 @@ bool Codegen::visit(IdentifierExpression *ast) } } + if (index >= _env->vars.size()) { + // named local variable, e.g. in a catch statement + _expr.code = _block->TEMP(index); + return false; + } + _expr.code = _block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn); @@ -1484,7 +1490,6 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, qSwap(_block, entryBlock); qSwap(_exitBlock, exitBlock); qSwap(_throwBlock, throwBlock); - qSwap(_handlersBlock, handlersBlock); qSwap(_returnAddress, returnAddress); for (FormalParameterList *it = formals; it; it = it->next) { @@ -1512,17 +1517,13 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, _block->JUMP(_exitBlock); - _throwBlock->JUMP(_function->handlersBlock); - - _handlersBlock->MOVE(_handlersBlock->TEMP(_returnAddress), - _handlersBlock->CALL(_handlersBlock->NAME(IR::Name::builtin_rethrow, 0, 0), 0)); - _handlersBlock->JUMP(_exitBlock); + // ### should not be required + _throwBlock->JUMP(_exitBlock); qSwap(_function, function); qSwap(_block, entryBlock); qSwap(_exitBlock, exitBlock); qSwap(_throwBlock, throwBlock); - qSwap(_handlersBlock, handlersBlock); qSwap(_returnAddress, returnAddress); leaveEnvironment(); @@ -1858,9 +1859,74 @@ bool Codegen::visit(ThrowStatement *ast) return false; } -bool Codegen::visit(TryStatement *) +bool Codegen::visit(TryStatement *ast) { - assert(!"not implemented"); + IR::BasicBlock *tryBody = _function->newBasicBlock(); + IR::BasicBlock *catchBody = ast->catchExpression ? _function->newBasicBlock() : 0; + IR::BasicBlock *finallyBody = ast->finallyExpression ? _function->newBasicBlock() : 0; + IR::BasicBlock *after = _function->newBasicBlock(); + assert(catchBody || finallyBody); + + int inCatch = 0; + if (catchBody && finallyBody) { + inCatch = _block->newTemp(); + move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, false)); + } + + int hasException = _block->newTemp(); + move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_create_exception_handler, 0, 0), 0)); + + _block->CJUMP(_block->TEMP(hasException), catchBody ? catchBody : finallyBody, tryBody); + + _block = tryBody; + statement(ast->statement); + _block->JUMP(finallyBody ? finallyBody : after); + + // regular flow does not go into the catch statement + if (catchBody) { + _block = catchBody; + + if (finallyBody) { + if (inCatch != 0) { + // an exception got thrown within catch. Go to finally + // and then rethrow + IR::BasicBlock *b = _function->newBasicBlock(); + _block->CJUMP(_block->TEMP(inCatch), finallyBody, b); + _block = b; + } + // if we have finally we need to clear the exception here, so we don't rethrow + move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, true)); + move(_block->TEMP(hasException), _block->CONST(IR::BoolType, false)); + } else { + // otherwise remove the exception handler, so that throw inside catch will + // give the correct result + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); + } + + const int exception = _block->newTemp(); + move(_block->TEMP(exception), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0)); + + // the variable ued in the catch statement is local and hides any global + // variable with the same name. + int hiddenIndex = _env->findMember(ast->catchExpression->name.toString()); + _env->members.insert(ast->catchExpression->name.toString(), exception); + + statement(ast->catchExpression->statement); + + // reset the variable name to the one from the outer scope + _env->members.insert(ast->catchExpression->name.toString(), hiddenIndex); + _block->JUMP(finallyBody ? finallyBody : after); + } + + if (finallyBody) { + _block = finallyBody; + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); + statement(ast->finallyExpression->statement); + _block->CJUMP(_block->TEMP(hasException), _throwBlock, after); + } + + _block = after; + return false; } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 8e9f505c87..95fedb5bc6 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -312,7 +312,6 @@ private: IR::BasicBlock *_block; IR::BasicBlock *_exitBlock; IR::BasicBlock *_throwBlock; - IR::BasicBlock *_handlersBlock; unsigned _returnAddress; Mode _mode; Environment *_env; diff --git a/qv4ir.cpp b/qv4ir.cpp index f3db7e975d..40c85c8efd 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -213,12 +213,34 @@ void Name::init(Builtin builtin, quint32 line, quint32 column) this->column = column; } +static const char *builtin_to_string(Name::Builtin b) +{ + switch (b) { + case Name::builtin_invalid: + return "builtin_invalid"; + case Name::builtin_typeof: + return "builtin_typeof"; + case Name::builtin_delete: + return "builtin_delete"; + case Name::builtin_throw: + return "builtin_throw"; + case Name::builtin_create_exception_handler: + return "builtin_create_exception_handler"; + case Name::builtin_delete_exception_handler: + return "builtin_delete_exception_handler"; + case Name::builtin_get_exception: + return "builtin_get_exception"; + } + return "builtin_(###FIXME)"; +}; + + void Name::dump(QTextStream &out) { if (id) out << *id; else - out << "__qmljs_builtin_%" << (int) builtin; + out << builtin_to_string(builtin); } void Temp::dump(QTextStream &out) diff --git a/qv4ir_p.h b/qv4ir_p.h index efae2e42a8..7853cc2f29 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -272,7 +272,9 @@ struct Name: Expr { builtin_typeof, builtin_delete, builtin_throw, - builtin_rethrow + builtin_create_exception_handler, + builtin_delete_exception_handler, + builtin_get_exception }; const QString *id; diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index eae3bc46a3..040111f5ac 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -935,13 +935,6 @@ void LLVMInstructionSelection::genCallName(IR::Call *e, llvm::Value *result) _llvmValue = llvm::UndefValue::get(_valueTy); return; - case IR::Name::builtin_rethrow: - CreateCall2(_llvmModule->getFunction("__qmljs_llvm_rethrow"), - _llvmFunction->arg_begin(), result); - _llvmValue = CreateLoad(result); - return; - } - Q_UNREACHABLE(); } else { llvm::Value *name = getIdentifier(*base->id); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index e5c78ab1db..5254b6d389 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -139,6 +139,11 @@ void InstructionSelection::operator()(IR::Function *function) functions[ctl.externalFunction.value()] = ctl.functionName; } + foreach (CatchBlockToLink cbl, _catchHandlers) { + Label target = _addrs.value(cbl.catchBlock); + linkBuffer.patch(cbl.ptr, linkBuffer.locationOf(target)); + } + #if OS(LINUX) char* disasmOutput = 0; size_t disasmLength = 0; @@ -204,7 +209,6 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu IR::Temp *arg = call->args->expr->asTemp(); assert(arg != 0); generateFunctionCall(result, __qmljs_builtin_typeof, arg, ContextRegister); - checkExceptions(); } break; case IR::Name::builtin_delete: @@ -214,13 +218,18 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu IR::Temp *arg = call->args->expr->asTemp(); assert(arg != 0); generateFunctionCall(Void, __qmljs_builtin_throw, arg, ContextRegister); - checkExceptions(); } break; - case IR::Name::builtin_rethrow: - // don't use callRuntimeMethod, as we need to return to avoid checking the exceptions - generateFunctionCall(result, __qmljs_builtin_rethrow, ContextRegister); - return; + case IR::Name::builtin_create_exception_handler: + generateFunctionCall(ReturnValueRegister, __qmljs_create_exception_handler, ContextRegister); + generateFunctionCall(result, setjmp, ReturnValueRegister); + break; + case IR::Name::builtin_delete_exception_handler: + generateFunctionCall(Void, __qmljs_delete_exception_handler, ContextRegister); + break; + case IR::Name::builtin_get_exception: + generateFunctionCall(result, __qmljs_get_exception, ContextRegister); + break; } } } @@ -234,7 +243,6 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) int argc = prepareVariableArguments(call->args); IR::Temp* thisObject = 0; generateFunctionCall(result, __qmljs_call_value, ContextRegister, thisObject, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc)); - checkExceptions(); } void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) @@ -245,7 +253,6 @@ void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) int argc = prepareVariableArguments(call->args); generateFunctionCall(result, __qmljs_call_property, ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); - checkExceptions(); } void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) @@ -264,7 +271,6 @@ void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) int argc = prepareVariableArguments(call->args); generateFunctionCall(result, __qmljs_construct_property, ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); - checkExceptions(); } void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) @@ -274,14 +280,6 @@ void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) int argc = prepareVariableArguments(call->args); generateFunctionCall(result, __qmljs_construct_value, ContextRegister, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc)); - checkExceptions(); -} - -void InstructionSelection::checkExceptions() -{ - Address addr(ContextRegister, offsetof(Context, hasUncaughtException)); - Jump jmp = branch8(NotEqual, addr, TrustedImm32(0)); - _patches[_function->handlersBlock].append(jmp); } void InstructionSelection::visitExp(IR::Exp *s) @@ -325,7 +323,6 @@ void InstructionSelection::visitMove(IR::Move *s) if (IR::Temp *t = s->source->asTemp()) { generateFunctionCall(Void, __qmljs_set_activation_property, ContextRegister, propertyName, t); - checkExceptions(); return; } else { Q_UNREACHABLE(); @@ -337,7 +334,6 @@ void InstructionSelection::visitMove(IR::Move *s) } else { String *propertyName = identifier(*n->id); generateFunctionCall(t, __qmljs_get_activation_property, ContextRegister, propertyName); - checkExceptions(); } return; } else if (IR::Const *c = s->source->asConst()) { @@ -399,14 +395,12 @@ void InstructionSelection::visitMove(IR::Move *s) //__qmljs_get_property(ctx, result, object, name); if (IR::Temp *base = m->base->asTemp()) { generateFunctionCall(t, __qmljs_get_property, ContextRegister, base, identifier(*m->name)); - checkExceptions(); return; } assert(!"wip"); return; } else if (IR::Subscript *ss = s->source->asSubscript()) { generateFunctionCall(t, __qmljs_get_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp()); - checkExceptions(); return; } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { @@ -491,7 +485,6 @@ void InstructionSelection::visitMove(IR::Move *s) if (IR::Temp *base = m->base->asTemp()) { if (IR::Temp *t = s->source->asTemp()) { generateFunctionCall(Void, __qmljs_set_property, ContextRegister, base, identifier(*m->name), t); - checkExceptions(); return; } else { Q_UNREACHABLE(); @@ -500,7 +493,6 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Subscript *ss = s->target->asSubscript()) { if (IR::Temp *t2 = s->source->asTemp()) { generateFunctionCall(Void, __qmljs_set_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t2); - checkExceptions(); return; } else { Q_UNIMPLEMENTED(); @@ -554,7 +546,6 @@ void InstructionSelection::visitMove(IR::Move *s) } if (op) { generateFunctionCallImp(Void, opName, op, t, identifier(*n->id), ContextRegister); - checkExceptions(); } return; } @@ -583,7 +574,6 @@ void InstructionSelection::visitMove(IR::Move *s) IR::Temp* base = ss->base->asTemp(); IR::Temp* index = ss->index->asTemp(); generateFunctionCallImp(Void, opName, op, base, index, t, ContextRegister); - checkExceptions(); } return; } @@ -612,7 +602,6 @@ void InstructionSelection::visitMove(IR::Move *s) IR::Temp* base = m->base->asTemp(); String* member = identifier(*m->name); generateFunctionCallImp(Void, opName, op, t, base, member, ContextRegister); - checkExceptions(); } return; } @@ -730,14 +719,12 @@ void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* na int argc = prepareVariableArguments(args); generateFunctionCallImp(result, name, method, ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), TrustedImm32(argc)); - checkExceptions(); } void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args) { int argc = prepareVariableArguments(args); generateFunctionCallImp(result, name, method, ContextRegister, baseAddressForCallArguments(), TrustedImm32(argc)); - checkExceptions(); } template diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 8565ad265b..49f712ffca 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -216,7 +216,6 @@ protected: void constructProperty(IR::New *ctor, IR::Temp *result); void callValue(IR::Call *call, IR::Temp *result); void constructValue(IR::New *call, IR::Temp *result); - void checkExceptions(); virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); @@ -523,6 +522,10 @@ private: FunctionPtr externalFunction; const char* functionName; }; + struct CatchBlockToLink { + DataLabelPtr ptr; + IR::BasicBlock *catchBlock; + }; void storeValue(VM::Value value, Address destination) { @@ -544,6 +547,7 @@ private: uchar *_codePtr; QHash > _patches; QHash _addrs; + QList _catchHandlers; QList _callsToLink; }; -- cgit v1.2.3 From 39cb093255c23bcc68b765f0abc4ac461fce0c9c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 23 Oct 2012 14:37:26 +0200 Subject: Fix stack alignment to align on 16 byte boundaries. Aligning on 16 byte boundaries is a requirement for MacOS, and it gives the optimal performance when using MMX on stack allocated data. Change-Id: I1c9a27e796f1c240002a05196490f15df68c3f73 Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 3 --- qv4isel_masm_p.h | 28 ++++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 5254b6d389..519ff5ede0 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -99,7 +99,6 @@ void InstructionSelection::operator()(IR::Function *function) locals = (locals + 1) & ~1; enterStandardStackFrame(locals); - push(ContextRegister); #if CPU(X86) loadPtr(addressForArgument(0), ContextRegister); #elif CPU(X86_64) || CPU(ARM) @@ -116,8 +115,6 @@ void InstructionSelection::operator()(IR::Function *function) } } - pop(ContextRegister); - leaveStandardStackFrame(locals); ret(); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 49f712ffca..32f71ae7db 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -164,19 +164,38 @@ protected: #endif push(StackFrameRegister); move(StackPointerRegister, StackFrameRegister); - subPtr(TrustedImm32(locals*sizeof(QQmlJS::VM::Value)), StackPointerRegister); + + // space for the locals and the ContextRegister + int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*); + +#if CPU(X86) || CPU(X86_64) + frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX +#endif + subPtr(TrustedImm32(frameSize), StackPointerRegister); + #if CPU(X86) || CPU(ARM) for (int saveReg = CalleeSavedFirstRegister; saveReg <= CalleeSavedLastRegister; ++saveReg) push(static_cast(saveReg)); #endif + // save the ContextRegister + storePtr(ContextRegister, StackPointerRegister); } void leaveStandardStackFrame(int locals) { + // restore the ContextRegister + loadPtr(StackPointerRegister, ContextRegister); + #if CPU(X86) || CPU(ARM) for (int saveReg = CalleeSavedLastRegister; saveReg >= CalleeSavedFirstRegister; --saveReg) pop(static_cast(saveReg)); #endif - addPtr(TrustedImm32(locals*sizeof(QQmlJS::VM::Value)), StackPointerRegister); + // space for the locals and the ContextRegister + int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*); +#if CPU(X86) || CPU(X86_64) + frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX +#endif + addPtr(TrustedImm32(frameSize), StackPointerRegister); + pop(StackFrameRegister); #if CPU(ARM) pop(JSC::ARMRegisters::lr); @@ -459,6 +478,11 @@ private: stackSizeToCorrect += sizeOfReturnValueOnStack; } +#if CPU(X86) || CPU(X86_64) + while (stackSizeToCorrect % 16) + ++stackSizeToCorrect; +#endif + storeArgument(ReturnValueRegister, r); if (stackSizeToCorrect) -- cgit v1.2.3 From c9c2b0c5411a9b3cfd11013021e4017eea5425c4 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 24 Oct 2012 10:09:30 +0200 Subject: Fix LLVM backend compilation. Change-Id: I326cf2531a600fd09b888e9955052b29624ccdbe Reviewed-by: Lars Knoll --- main.cpp | 9 ++++++--- qv4isel_llvm.cpp | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/main.cpp b/main.cpp index f5f51c3016..bdd5b37dd9 100644 --- a/main.cpp +++ b/main.cpp @@ -169,15 +169,18 @@ int evaluateCompiledCode(const QStringList &files) globalObject->setProperty(ctx, vm.identifier(QStringLiteral("print")), QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); - code(ctx); - - if (ctx->hasUncaughtException) { + void * buf = __qmljs_create_exception_handler(ctx); + if (setjmp(*(jmp_buf *)buf)) { if (VM::ErrorObject *e = ctx->result.asErrorObject()) std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; else std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; + return -2; } + + code(ctx); } + return 0; } diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 040111f5ac..9e35e6ef56 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -935,7 +935,9 @@ void LLVMInstructionSelection::genCallName(IR::Call *e, llvm::Value *result) _llvmValue = llvm::UndefValue::get(_valueTy); return; - Q_UNREACHABLE(); + default: + Q_UNREACHABLE(); + } } else { llvm::Value *name = getIdentifier(*base->id); -- cgit v1.2.3 From 4cb9b8d77ef64241782fe362f40cb29891303bfd Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 24 Oct 2012 13:45:43 +0200 Subject: Added loading of "this" and renamed "ActivateProperty" to "StoreName". Change-Id: I033abe53368a815a9e3d036021429732f5526ea2 Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 15 ++++++++++----- moth/qv4isel_moth.cpp | 20 ++++++++++++++------ moth/qv4vme_moth.cpp | 17 +++++++++++------ 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 1637963f90..bddf3074b3 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -17,12 +17,13 @@ F(LoadTemp, loadTemp) \ F(MoveTemp, moveTemp) \ F(LoadName, loadName) \ + F(StoreName, storeName) \ F(Push, push) \ F(Call, call) \ F(Jump, jump) \ F(CJump, jump) \ F(Binop, binop) \ - F(ActivateProperty, activateProperty) + F(LoadThis, loadThis) #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) # define MOTH_THREADED_INTERPRETER @@ -83,7 +84,11 @@ union Instr }; struct instr_loadName { MOTH_INSTR_HEADER - VM::String *value; + VM::String *name; + }; + struct instr_storeName { + MOTH_INSTR_HEADER + VM::String *name; }; struct instr_push { MOTH_INSTR_HEADER @@ -104,9 +109,8 @@ union Instr int rhsTempIndex; VM::Value (*alu)(const VM::Value , const VM::Value, VM::Context *); }; - struct instr_activateProperty { + struct instr_loadThis { MOTH_INSTR_HEADER - VM::String *propName; }; instr_common common; @@ -118,11 +122,12 @@ union Instr instr_loadString loadString; instr_loadClosure loadClosure; instr_loadName loadName; + instr_storeName storeName; instr_push push; instr_call call; instr_jump jump; instr_binop binop; - instr_activateProperty activateProperty; + instr_loadThis loadThis; static int size(Type type); }; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 6a3e70da2a..0ae5bcd673 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -58,7 +58,7 @@ void InstructionSelection::visitExp(IR::Exp *s) if (IR::Name *n = c->base->asName()) { if (n->builtin == IR::Name::builtin_invalid) { Instruction::LoadName load; - load.value = _engine->newString(*n->id); + load.name = _engine->newString(*n->id); addInstruction(load); } else { Q_UNIMPLEMENTED(); @@ -206,21 +206,29 @@ void InstructionSelection::simpleMove(IR::Move *s) Q_UNUSED(n); // set activation property if (IR::Temp *t = s->source->asTemp()) { + // TODO: fold the next 2 instructions. Instruction::LoadTemp load; load.tempIndex = t->index; addInstruction(load); - Instruction::ActivateProperty activate; - activate.propName = _engine->newString(*n->id); - addInstruction(activate); + Instruction::StoreName store; + store.name = _engine->newString(*n->id); + addInstruction(store); } else { Q_UNREACHABLE(); } } else if (IR::Temp *t = s->target->asTemp()) { - + // Check what kind of load it is, and generate the instruction for that. + // The store to the temp (the target) is done afterwards. if (IR::Name *n = s->source->asName()) { Q_UNUSED(n); - qWarning(" NAME"); + if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. + addInstruction(Instruction::LoadThis()); + } else { + Instruction::LoadName load; + load.name = _engine->newString(*n->id); + addInstruction(load); + } } else if (IR::Const *c = s->source->asConst()) { switch (c->type) { case IR::UndefinedType: diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index a9d7c096d2..8c01768d47 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -5,7 +5,7 @@ using namespace QQmlJS; using namespace QQmlJS::Moth; #ifdef DO_TRACE_INSTR -# define TRACE_INSTR(I) fprintf(stderr, "%s\n", #I); +# define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); # define TRACE(n, str, ...) { fprintf(stderr, "-- %s : ", #n); fprintf(stderr, str, __VA_ARGS__); fprintf(stderr, "\n"); } #else # define TRACE_INSTR(I) @@ -135,9 +135,15 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) - tempRegister = __qmljs_get_activation_property(context, instr.value); + TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); + tempRegister = __qmljs_get_activation_property(context, instr.name); MOTH_END_INSTR(LoadName) + MOTH_BEGIN_INSTR(StoreName) + TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); + __qmljs_set_activation_property(context, instr.name, tempRegister); + MOTH_END_INSTR(StoreName) + MOTH_BEGIN_INSTR(Push) stack.resize(instr.value); MOTH_END_INSTR(Push) @@ -165,10 +171,9 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code return; MOTH_END_INSTR(Ret) - MOTH_BEGIN_INSTR(ActivateProperty) - TRACE(inline, "property name = %s", instr.propName->toQString().toUtf8().constData()); - __qmljs_set_activation_property(context, instr.propName, tempRegister); - MOTH_END_INSTR(ActivateProperty) + MOTH_BEGIN_INSTR(LoadThis) + tempRegister = __qmljs_get_thisObject(context); + MOTH_END_INSTR(LoadThis) #ifdef MOTH_THREADED_INTERPRETER // nothing to do -- cgit v1.2.3 From a412eab7a8406c7f319868a21862520b5c2bc7c4 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 24 Oct 2012 16:35:15 +0200 Subject: Removed duplicate code for the various function call methods. The main change is renaming __qmljs_call_function to callFunction, and move duplicate error checking in there. Now __qmljs_call_value does not do its own call anymore, but calls __qmljs_call_function. All calls to __qmljs_call_function are replaced by calls to __qmljs_call_value. Change-Id: I7fffc2e811c43e84e7344d70e2ed521331ed710d Reviewed-by: Lars Knoll --- qmljs_runtime.cpp | 87 +++++++--------- qmljs_runtime.h | 1 - qv4ecmaobjects.cpp | 289 +++++++++++++++++++++++------------------------------ 3 files changed, 163 insertions(+), 214 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index f0c73263cf..a826c0ac64 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -55,6 +55,22 @@ namespace QQmlJS { namespace VM { +static inline Value callFunction(Context *context, Value thisObject, FunctionObject *func, Value *args, int argc) +{ + if (func) { + Context k; + Context *ctx = func->needsActivation ? context->engine->newContext() : &k; + const Value *that = thisObject.isUndefined() ? 0 : &thisObject; + ctx->initCallContext(context, that, func, args, argc); + func->call(ctx); + ctx->leaveCallContext(); + return ctx->result; + } else { + context->throwTypeError(); + return Value::undefinedValue(); + } +} + QString numberToString(double num, int radix = 10) { if (qIsNaN(num)) { @@ -937,21 +953,17 @@ Value __qmljs_object_default_value(Context *ctx, Value object, int typeHint) Object *oo = object.objectValue(); Value conv = oo->getProperty(ctx, meth1); - if (!conv.isUndefined()) { - if (FunctionObject *f = conv.asFunctionObject()) { - Value r = __qmljs_call_function(ctx, object, f, 0, 0); - if (r.isPrimitive()) - return r; - } + if (FunctionObject *f = conv.asFunctionObject()) { + Value r = callFunction(ctx, object, f, 0, 0); + if (r.isPrimitive()) + return r; } conv = oo->getProperty(ctx, meth2); - if (!conv.isUndefined()) { - if (FunctionObject *f = conv.asFunctionObject()) { - Value r = __qmljs_call_function(ctx, object, f, 0, 0); - if (r.isPrimitive()) - return r; - } + if (FunctionObject *f = conv.asFunctionObject()) { + Value r = callFunction(ctx, object, f, 0, 0); + if (r.isPrimitive()) + return r; } return Value::undefinedValue(); @@ -1101,7 +1113,7 @@ Value __qmljs_get_property(Context *ctx, Value object, String *name) if (object.isObject()) { return object.objectValue()->getProperty(ctx, name); } else { - ctx->throwTypeError(); // ### not necessary. + ctx->throwTypeError(); return Value::undefinedValue(); } } @@ -1176,63 +1188,38 @@ uint __qmljs_equal(Value x, Value y, Context *ctx) Value __qmljs_call_activation_property(Context *context, String *name, Value *args, int argc) { - Value *func = context->lookupPropertyDescriptor(name); - if (! func) { + Value func = __qmljs_get_activation_property(context, name); + if (FunctionObject *f = func.asFunctionObject()) { + return callFunction(context, Value::undefinedValue(), f, args, argc); + } else { context->throwReferenceError(Value::fromString(name)); return Value::undefinedValue(); } - Value result = __qmljs_call_value(context, Value::undefinedValue(), *func, args, argc); - return result; } Value __qmljs_call_property(Context *context, Value base, String *name, Value *args, int argc) { Value baseObject; Value thisObject; - if (!base.isUndefined()) { + + if (base.isUndefined()) { + baseObject = context->activation; + thisObject = Value::nullValue(); + } else { baseObject = base; if (!baseObject.isObject()) baseObject = __qmljs_to_object(baseObject, context); assert(baseObject.isObject()); thisObject = baseObject; - } else { - baseObject = context->activation; - thisObject = Value::nullValue(); - } - Value func = baseObject.property(context, name); - Value result; - if (FunctionObject *f = func.asFunctionObject()) { - Context k; - Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->initCallContext(context, &thisObject, f, args, argc); - f->call(ctx); - ctx->leaveCallContext(); - result = ctx->result; - } else { - context->throwTypeError(); } - return result; -} -Value __qmljs_call_function(Context *context, Value thisObject, FunctionObject *f, Value *args, int argc) -{ - Context k; - Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - const Value *that = thisObject.isUndefined() ? 0 : &thisObject; - ctx->initCallContext(context, that, f, args, argc); - f->call(ctx); - ctx->leaveCallContext(); - return ctx->result; + Value func = baseObject.property(context, name); + return callFunction(context, thisObject, func.asFunctionObject(), args, argc); } Value __qmljs_call_value(Context *context, Value thisObject, Value func, Value *args, int argc) { - if (FunctionObject *f = func.asFunctionObject()) { - return __qmljs_call_function(context, thisObject, f, args, argc); - } else { - context->throwTypeError(); - return Value::undefinedValue(); - } + return callFunction(context, thisObject, func.asFunctionObject(), args, argc); } Value __qmljs_construct_activation_property(Context *context, String *name, Value *args, int argc) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index e424b08bd2..339bb48dc5 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -97,7 +97,6 @@ extern "C" { Value __qmljs_call_activation_property(Context *, String *name, Value *args, int argc); Value __qmljs_call_property(Context *context, Value base, String *name, Value *args, int argc); Value __qmljs_call_value(Context *context, Value thisObject, Value func, Value *args, int argc); -Value __qmljs_call_function(Context *context, Value thisObject, FunctionObject *f, Value *args, int argc); Value __qmljs_construct_activation_property(Context *, String *name, Value *args, int argc); Value __qmljs_construct_property(Context *context, Value base, String *name, Value *args, int argc); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 38d2119e71..db97a433b0 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1537,25 +1537,21 @@ void ArrayPrototype::method_every(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); - if (FunctionObject *f = callback.asFunctionObject()) { - Value thisArg = ctx->argument(1); - bool ok = true; - for (uint k = 0; ok && k < instance->value.size(); ++k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value r = __qmljs_call_function(ctx, thisArg, f, args, 3); - ok = __qmljs_to_boolean(r, ctx); - } - ctx->result = Value::fromBoolean(ok); - } else { - ctx->throwTypeError(); + Value thisArg = ctx->argument(1); + bool ok = true; + for (uint k = 0; ok && k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); + ok = __qmljs_to_boolean(r, ctx); } + ctx->result = Value::fromBoolean(ok); } else { ctx->throwUnimplemented(QStringLiteral("Array.prototype.every")); } @@ -1566,25 +1562,21 @@ void ArrayPrototype::method_some(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); - if (FunctionObject *f = callback.asFunctionObject()) { - Value thisArg = ctx->argument(1); - bool ok = false; - for (uint k = 0; !ok && k < instance->value.size(); ++k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value r = __qmljs_call_function(ctx, thisArg, f, args, 3); - ok = __qmljs_to_boolean(r, ctx); - } - ctx->result = Value::fromBoolean(ok); - } else { - ctx->throwTypeError(); + Value thisArg = ctx->argument(1); + bool ok = false; + for (uint k = 0; !ok && k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); + ok = __qmljs_to_boolean(r, ctx); } + ctx->result = Value::fromBoolean(ok); } else { ctx->throwUnimplemented(QStringLiteral("Array.prototype.some")); } @@ -1595,20 +1587,16 @@ void ArrayPrototype::method_forEach(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); - if (FunctionObject *f = callback.asFunctionObject()) { - Value thisArg = ctx->argument(1); - for (quint32 k = 0; k < instance->value.size(); ++k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - /*Value r =*/ __qmljs_call_function(ctx, thisArg, f, args, 3); - } - } else { - ctx->throwTypeError(); + Value thisArg = ctx->argument(1); + for (quint32 k = 0; k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + /*Value r =*/ __qmljs_call_value(ctx, thisArg, callback, args, 3); } } else { ctx->throwUnimplemented(QStringLiteral("Array.prototype.forEach")); @@ -1620,25 +1608,21 @@ void ArrayPrototype::method_map(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); - if (FunctionObject *f = callback.asFunctionObject()) { - Value thisArg = ctx->argument(1); - ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); - a->value.resize(instance->value.size()); - for (quint32 k = 0; k < instance->value.size(); ++k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value r = __qmljs_call_function(ctx, thisArg, f, args, 3); - a->value.assign(k, r); - } - ctx->result = Value::fromObject(a); - } else { - ctx->throwTypeError(); + Value thisArg = ctx->argument(1); + ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); + a->value.resize(instance->value.size()); + for (quint32 k = 0; k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); + a->value.assign(k, r); } + ctx->result = Value::fromObject(a); } else { ctx->throwUnimplemented(QStringLiteral("Array.prototype.map")); } @@ -1649,28 +1633,24 @@ void ArrayPrototype::method_filter(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); - if (FunctionObject *f = callback.asFunctionObject()) { - Value thisArg = ctx->argument(1); - ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); - for (quint32 k = 0; k < instance->value.size(); ++k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value r = __qmljs_call_function(ctx, thisArg, f, args, 3); - if (__qmljs_to_boolean(r, ctx)) { - const uint index = a->value.size(); - a->value.resize(index + 1); - a->value.assign(index, v); - } + Value thisArg = ctx->argument(1); + ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); + for (quint32 k = 0; k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); + if (__qmljs_to_boolean(r, ctx)) { + const uint index = a->value.size(); + a->value.resize(index + 1); + a->value.assign(index, v); } - ctx->result = Value::fromObject(a); - } else { - ctx->throwTypeError(); } + ctx->result = Value::fromObject(a); } else { ctx->throwUnimplemented(QStringLiteral("Array.prototype.filter")); } @@ -1681,31 +1661,27 @@ void ArrayPrototype::method_reduce(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); - if (FunctionObject *f = callback.asFunctionObject()) { - Value initialValue = ctx->argument(1); - Value acc = initialValue; - for (quint32 k = 0; k < instance->value.size(); ++k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - - if (acc.isUndefined()) { - acc = v; - continue; - } - - Value args[4]; - args[0] = acc; - args[1] = v; - args[2] = Value::fromDouble(k); - args[3] = ctx->thisObject; - Value r = __qmljs_call_function(ctx, Value::undefinedValue(), f, args, 4); - acc = r; + Value initialValue = ctx->argument(1); + Value acc = initialValue; + for (quint32 k = 0; k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + + if (acc.isUndefined()) { + acc = v; + continue; } - ctx->result = acc; - } else { - ctx->throwTypeError(); + + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k); + args[3] = ctx->thisObject; + Value r = __qmljs_call_value(ctx, Value::undefinedValue(), callback, args, 4); + acc = r; } + ctx->result = acc; } else { ctx->throwUnimplemented(QStringLiteral("Array.prototype.reduce")); } @@ -1716,31 +1692,27 @@ void ArrayPrototype::method_reduceRight(Context *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { Value callback = ctx->argument(0); - if (FunctionObject *f = callback.asFunctionObject()) { - Value initialValue = ctx->argument(1); - Value acc = initialValue; - for (int k = instance->value.size() - 1; k != -1; --k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - - if (acc.isUndefined()) { - acc = v; - continue; - } - - Value args[4]; - args[0] = acc; - args[1] = v; - args[2] = Value::fromDouble(k); - args[3] = ctx->thisObject; - Value r = __qmljs_call_function(ctx, Value::undefinedValue(), f, args, 4); - acc = r; + Value initialValue = ctx->argument(1); + Value acc = initialValue; + for (int k = instance->value.size() - 1; k != -1; --k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + + if (acc.isUndefined()) { + acc = v; + continue; } - ctx->result = acc; - } else { - ctx->throwTypeError(); + + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k); + args[3] = ctx->thisObject; + Value r = __qmljs_call_value(ctx, Value::undefinedValue(), callback, args, 4); + acc = r; } + ctx->result = acc; } else { ctx->throwUnimplemented(QStringLiteral("Array.prototype.reduceRight")); } @@ -1786,44 +1758,35 @@ void FunctionPrototype::method_toString(Context *ctx) void FunctionPrototype::method_apply(Context *ctx) { - if (FunctionObject *f = ctx->thisObject.asFunctionObject()) { - - Value thisObject = ctx->argument(0).toObject(ctx); - if (thisObject.isNull() || thisObject.isUndefined()) - thisObject = ctx->engine->globalObject; + Value thisObject = ctx->argument(0).toObject(ctx); + if (thisObject.isNull() || thisObject.isUndefined()) + thisObject = ctx->engine->globalObject; - Value arg = ctx->argument(1); - QVector args; + Value arg = ctx->argument(1); + QVector args; - if (ArrayObject *arr = arg.asArrayObject()) { - const Array &actuals = arr->value; + if (ArrayObject *arr = arg.asArrayObject()) { + const Array &actuals = arr->value; - for (quint32 i = 0; i < actuals.count(); ++i) { - Value a = actuals.at(i); - args.append(a); - } - } else if (!(arg.isUndefined() || arg.isNull())) { - ctx->throwError(QLatin1String("Function.prototype.apply: second argument is not an array")); - return; + for (quint32 i = 0; i < actuals.count(); ++i) { + Value a = actuals.at(i); + args.append(a); } - - ctx->result = __qmljs_call_function(ctx, thisObject, f, args.data(), args.size()); - } else { - ctx->throwTypeError(); + } else if (!(arg.isUndefined() || arg.isNull())) { + ctx->throwError(QLatin1String("Function.prototype.apply: second argument is not an array")); + return; } + + ctx->result = __qmljs_call_value(ctx, thisObject, ctx->thisObject, args.data(), args.size()); } void FunctionPrototype::method_call(Context *ctx) { - if (FunctionObject *f = ctx->thisObject.asFunctionObject()) { - Value thisArg = ctx->argument(0); - QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); - if (ctx->argumentCount) - qCopy(ctx->arguments + 1, ctx->arguments + ctx->argumentCount, args.begin()); - ctx->result = __qmljs_call_function(ctx, thisArg, f, args.data(), args.size()); - } else { - ctx->throwTypeError(); - } + Value thisArg = ctx->argument(0); + QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); + if (ctx->argumentCount) + qCopy(ctx->arguments + 1, ctx->arguments + ctx->argumentCount, args.begin()); + ctx->result = __qmljs_call_value(ctx, thisArg, ctx->thisObject, args.data(), args.size()); } void FunctionPrototype::method_bind(Context *ctx) -- cgit v1.2.3 From 17b1b66104c465635c41175c65e5daf90cbbee36 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 24 Oct 2012 17:04:29 +0200 Subject: Implemented calls in the interpreter. Change-Id: Iafc9b04a1aa39272ab8aac24fb410a23df02a756 Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 14 +++++-- moth/qv4isel_moth.cpp | 108 +++++++++++++++++++++++++++++++++---------------- moth/qv4isel_moth_p.h | 5 ++- moth/qv4vme_moth.cpp | 9 ++++- qmljs_runtime.cpp | 1 + 5 files changed, 97 insertions(+), 40 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index bddf3074b3..08481339fa 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -19,7 +19,8 @@ F(LoadName, loadName) \ F(StoreName, storeName) \ F(Push, push) \ - F(Call, call) \ + F(CallValue, callValue) \ + F(CallProperty, callProperty) \ F(Jump, jump) \ F(CJump, jump) \ F(Binop, binop) \ @@ -94,11 +95,17 @@ union Instr MOTH_INSTR_HEADER quint32 value; }; - struct instr_call { + struct instr_callValue { MOTH_INSTR_HEADER quint32 argc; quint32 args; }; + struct instr_callProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + }; struct instr_jump { MOTH_INSTR_HEADER ptrdiff_t offset; @@ -124,7 +131,8 @@ union Instr instr_loadName loadName; instr_storeName storeName; instr_push push; - instr_call call; + instr_callValue callValue; + instr_callProperty callProperty; instr_jump jump; instr_binop binop; instr_loadThis loadThis; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 0ae5bcd673..a32d5b76fd 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -52,63 +52,97 @@ void InstructionSelection::operator()(IR::Function *function) qSwap(_function, function); } -void InstructionSelection::visitExp(IR::Exp *s) +void InstructionSelection::callActivationProperty(IR::Call *c) { - if (IR::Call *c = s->expr->asCall()) { - if (IR::Name *n = c->base->asName()) { - if (n->builtin == IR::Name::builtin_invalid) { - Instruction::LoadName load; - load.name = _engine->newString(*n->id); - addInstruction(load); - } else { - Q_UNIMPLEMENTED(); - } - } else if (IR::Member *m = c->base->asMember()) { - Q_UNUSED(m); - Q_UNIMPLEMENTED(); - } else if (IR::Temp *t = c->base->asTemp()) { - Instruction::LoadTemp load; - load.tempIndex = t->index; - addInstruction(load); - } else { - Q_UNREACHABLE(); - } + IR::Name *n = c->base->asName(); + Q_ASSERT(n); + + if (n->builtin == IR::Name::builtin_invalid) { + Instruction::LoadName load; + load.name = _engine->newString(*n->id); + addInstruction(load); - call(c->args); + Instruction::CallValue call; + prepareCallArgs(c->args, call.argc, call.args); + addInstruction(call); } else { - Q_UNREACHABLE(); + Q_UNIMPLEMENTED(); } } -void InstructionSelection::call(IR::ExprList *e) +void InstructionSelection::callValue(IR::Call *c) +{ + IR::Temp *t = c->base->asTemp(); + Q_ASSERT(t); + + Instruction::LoadTemp load; + load.tempIndex = t->index; + addInstruction(load); + + Instruction::CallValue call; + prepareCallArgs(c->args, call.argc, call.args); + addInstruction(call); +} + +void InstructionSelection::callProperty(IR::Call *c) { - Instruction::Call call; - call.argc = 0; - call.args = 0; + IR::Member *m = c->base->asMember(); + Q_ASSERT(m); + + // load the base + Instruction::LoadTemp load; + load.tempIndex = m->base->asTemp()->index; + addInstruction(load); + + // call the property on the loaded base + Instruction::CallProperty call; + call.name = _engine->newString(*m->name); + prepareCallArgs(c->args, call.argc, call.args); + addInstruction(call); +} +void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 &args) +{ int locals = _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments; if (e && e->next == 0 && e->expr->asTemp()->index >= 0 && e->expr->asTemp()->index < locals) { // We pass single arguments as references to the stack - call.argc = 1; - call.args = e->expr->asTemp()->index; + argc = 1; + args = e->expr->asTemp()->index; } else if (e) { // We need to move all the temps into the function arg array int argLocation = _function->tempCount - _function->locals.size(); assert(argLocation >= 0); - call.args = argLocation; + args = argLocation; while (e) { Instruction::MoveTemp move; move.fromTempIndex = e->expr->asTemp()->index; move.toTempIndex = argLocation; addInstruction(move); ++argLocation; - ++call.argc; + ++argc; e = e->next; } } +} - addInstruction(call); +void InstructionSelection::visitExp(IR::Exp *s) +{ + if (IR::Call *c = s->expr->asCall()) { + if (c->base->asName()) { + callActivationProperty(c); + } else if (c->base->asTemp()) { + callValue(c); + } else if (c->base->asMember()) { + callProperty(c); + } else { + Q_UNREACHABLE(); + } + + // TODO: check if we should store the return value ? + } else { + Q_UNREACHABLE(); + } } void InstructionSelection::visitEnter(IR::Enter *) @@ -281,14 +315,20 @@ void InstructionSelection::simpleMove(IR::Move *s) binop.rhsTempIndex = b->right->index; addInstruction(binop); } else if (IR::Call *c = s->source->asCall()) { - Q_UNUSED(c); - qWarning(" CALL"); + if (c->base->asName()) { + callActivationProperty(c); + } else if (c->base->asMember()) { + callProperty(c); + } else if (c->base->asTemp()) { + callValue(c); + } else { + Q_UNREACHABLE(); + } } Instruction::StoreTemp st; st.tempIndex = t->index; addInstruction(st); - } else if (IR::Member *m = s->target->asMember()) { Q_UNUSED(m); qWarning("MEMBER"); diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 009d0b3a36..76e32409a7 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -35,7 +35,10 @@ private: }; void simpleMove(IR::Move *); - void call(IR::ExprList *); + void callActivationProperty(IR::Call *c); + void callValue(IR::Call *c); + void callProperty(IR::Call *c); + void prepareCallArgs(IR::ExprList *, quint32 &, quint32 &); template inline ptrdiff_t addInstruction(const InstrData &data); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 8c01768d47..b3f61c296c 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -148,10 +148,15 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code stack.resize(instr.value); MOTH_END_INSTR(Push) - MOTH_BEGIN_INSTR(Call) + MOTH_BEGIN_INSTR(CallValue) VM::Value *args = stack.data() + instr.args; tempRegister = __qmljs_call_value(context, VM::Value::undefinedValue(), tempRegister, args, instr.argc); - MOTH_END_INSTR(Call) + MOTH_END_INSTR(CallValue) + + MOTH_BEGIN_INSTR(CallProperty) + VM::Value *args = stack.data() + instr.args; + tempRegister = __qmljs_call_property(context, tempRegister, instr.name, args, instr.argc); + MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(Jump) code = ((uchar *)&instr.offset) + instr.offset; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index a826c0ac64..1572e82091 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -1186,6 +1186,7 @@ uint __qmljs_equal(Value x, Value y, Context *ctx) return false; } +// TODO: remove this function. Backends should just generate a __qmljs_get_activation_property followed by a __qmljs_call_value Value __qmljs_call_activation_property(Context *context, String *name, Value *args, int argc) { Value func = __qmljs_get_activation_property(context, name); -- cgit v1.2.3 From 966429f5527ff139ac9a630bf9ca3971e6576266 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 24 Oct 2012 17:05:44 +0200 Subject: Readability change: builtin_invalid implies baseName->id being set. And vice-versa, so drop the wrapping if statement. Change-Id: I57b7aef23feeee6b3f94edd5ca85bfc460bb2307 Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 60 ++++++++++++++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 519ff5ede0..3662b880fc 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -195,39 +195,35 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu IR::Name *baseName = call->base->asName(); assert(baseName != 0); - if (baseName->id) + switch (baseName->builtin) { + case IR::Name::builtin_invalid: callRuntimeMethod(result, __qmljs_call_activation_property, call->base, call->args); - else { - switch (baseName->builtin) { - case IR::Name::builtin_invalid: - Q_UNREACHABLE(); - break; - case IR::Name::builtin_typeof: { - IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - generateFunctionCall(result, __qmljs_builtin_typeof, arg, ContextRegister); - } - break; - case IR::Name::builtin_delete: - Q_UNREACHABLE(); - break; - case IR::Name::builtin_throw: { - IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - generateFunctionCall(Void, __qmljs_builtin_throw, arg, ContextRegister); - } - break; - case IR::Name::builtin_create_exception_handler: - generateFunctionCall(ReturnValueRegister, __qmljs_create_exception_handler, ContextRegister); - generateFunctionCall(result, setjmp, ReturnValueRegister); - break; - case IR::Name::builtin_delete_exception_handler: - generateFunctionCall(Void, __qmljs_delete_exception_handler, ContextRegister); - break; - case IR::Name::builtin_get_exception: - generateFunctionCall(result, __qmljs_get_exception, ContextRegister); - break; - } + break; + case IR::Name::builtin_typeof: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + generateFunctionCall(result, __qmljs_builtin_typeof, arg, ContextRegister); + } + break; + case IR::Name::builtin_delete: + Q_UNREACHABLE(); + break; + case IR::Name::builtin_throw: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + generateFunctionCall(Void, __qmljs_builtin_throw, arg, ContextRegister); + } + break; + case IR::Name::builtin_create_exception_handler: + generateFunctionCall(ReturnValueRegister, __qmljs_create_exception_handler, ContextRegister); + generateFunctionCall(result, setjmp, ReturnValueRegister); + break; + case IR::Name::builtin_delete_exception_handler: + generateFunctionCall(Void, __qmljs_delete_exception_handler, ContextRegister); + break; + case IR::Name::builtin_get_exception: + generateFunctionCall(result, __qmljs_get_exception, ContextRegister); + break; } } -- cgit v1.2.3 From 4554cdfdcbad336b49394bd534460a278cf1c9b4 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 25 Oct 2012 09:15:11 +0200 Subject: Remove all is*Object methods. They differ from the isObject/isUndefined/etc. methods because they do an extra virtual function call. And with isObject and friends being cheap, is*Object methods could have made the same (but faulty) impression. Change-Id: I32f26a96e73251bd14f8198b0a1ffb5d59e53f31 Reviewed-by: Lars Knoll --- qmljs_runtime.cpp | 47 +---------------------------------------------- qmljs_runtime.h | 12 ------------ qv4ecmaobjects.cpp | 2 +- 3 files changed, 2 insertions(+), 59 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 1572e82091..faae161008 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -180,51 +180,6 @@ double Value::toInteger(double number) return std::signbit(number) ? -v : v; } -bool Value::isFunctionObject() const -{ - return isObject() ? objectValue()->asFunctionObject() != 0 : false; -} - -bool Value::isBooleanObject() const -{ - return isObject() ? objectValue()->asBooleanObject() != 0 : false; -} - -bool Value::isNumberObject() const -{ - return isObject() ? objectValue()->asNumberObject() != 0 : false; -} - -bool Value::isStringObject() const -{ - return isObject() ? objectValue()->asStringObject() != 0 : false; -} - -bool Value::isDateObject() const -{ - return isObject() ? objectValue()->asDateObject() != 0 : false; -} - -bool Value::isRegExpObject() const -{ - return isObject() ? objectValue()->asRegExpObject() != 0 : false; -} - -bool Value::isArrayObject() const -{ - return isObject() ? objectValue()->asArrayObject() != 0 : false; -} - -bool Value::isErrorObject() const -{ - return isObject() ? objectValue()->asErrorObject() != 0 : false; -} - -bool Value::isArgumentsObject() const -{ - return isObject() ? objectValue()->asActivationObject() != 0 : false; -} - Object *Value::asObject() const { return isObject() ? objectValue() : 0; @@ -937,7 +892,7 @@ Bool __qmljs_is_function(Value value) Value __qmljs_object_default_value(Context *ctx, Value object, int typeHint) { if (typeHint == PREFERREDTYPE_HINT) { - if (object.isDateObject()) + if (object.asDateObject()) typeHint = STRING_HINT; else typeHint = NUMBER_HINT; diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 339bb48dc5..bacde9509d 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -408,18 +408,6 @@ struct Value return b; } - - - bool isFunctionObject() const; - bool isBooleanObject() const; - bool isNumberObject() const; - bool isStringObject() const; - bool isDateObject() const; - bool isRegExpObject() const; - bool isArrayObject() const; - bool isErrorObject() const; - bool isArgumentsObject() const; - Object *asObject() const; FunctionObject *asFunctionObject() const; BooleanObject *asBooleanObject() const; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index db97a433b0..ef211ea470 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2449,7 +2449,7 @@ void RegExpCtor::construct(Context *ctx) void RegExpCtor::call(Context *ctx) { - if (ctx->argumentCount > 0 && ctx->argument(0).isRegExpObject()) { + if (ctx->argumentCount > 0 && ctx->argument(0).asRegExpObject()) { if (ctx->argumentCount == 1 || ctx->argument(1).isUndefined()) { ctx->result = ctx->argument(0); return; -- cgit v1.2.3 From 4149d244163729b37e143c7e58ed788ce32b20c1 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 25 Oct 2012 09:24:11 +0200 Subject: Moved definition of Value before the extern block to suppress warnings. Change-Id: Ic400255d0a7f6830dcb0604688d88d5020628b15 Reviewed-by: Lars Knoll --- qmljs_runtime.h | 325 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 161 insertions(+), 164 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index bacde9509d..4427ede3a5 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -74,7 +74,6 @@ enum PropertyAttributes { ConfigurableAttribute = 8 }; -struct Value; struct Object; struct String; struct Context; @@ -91,6 +90,167 @@ struct ExecutionEngine; typedef uint Bool; +struct Value +{ + union { + quint64 val; + double dbl; + struct { +#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN + uint tag; +#endif + union { + uint uint_32; + int int_32; +#if CPU(X86_64) +#else + Object *o; + String *s; +#endif + }; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + uint tag; +#endif + }; + }; + + enum Masks { + NotDouble_Mask = 0xfff80000, + Type_Mask = 0xffff0000, + Immediate_Mask = NotDouble_Mask | 0x00040000, + Tag_Shift = 32 + }; + enum ValueType { + Undefined_Type = Immediate_Mask | 0x00000, + Null_Type = Immediate_Mask | 0x10000, + Boolean_Type = Immediate_Mask | 0x20000, + Integer_Type = Immediate_Mask | 0x30000, + Object_Type = NotDouble_Mask | 0x00000, + String_Type = NotDouble_Mask | 0x10000 + }; + + enum ImmediateFlags { + ConvertibleToInt = NotDouble_Mask | 0x1 + }; + + enum ValueTypeInternal { + _Undefined_Type = Undefined_Type, + _Null_Type = Null_Type | ConvertibleToInt, + _Boolean_Type = Boolean_Type | ConvertibleToInt, + _Integer_Type = Integer_Type | ConvertibleToInt, + _Object_Type = Object_Type, + _String_Type = String_Type + + }; + + inline ValueType type() const { + return (ValueType)(tag & Type_Mask); + } + + inline bool isUndefined() const { return tag == _Undefined_Type; } + inline bool isNull() const { return tag == _Null_Type; } + inline bool isBoolean() const { return tag == _Boolean_Type; } + inline bool isInteger() const { return tag == _Integer_Type; } + inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } + inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } +#if CPU(X86_64) + inline bool isString() const { return (tag & Type_Mask) == String_Type; } + inline bool isObject() const { return (tag & Type_Mask) == Object_Type; } +#else + inline bool isString() const { return tag == String_Type; } + inline bool isObject() const { return tag == Object_Type; } +#endif + inline bool isConvertibleToInt() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } + + Bool booleanValue() const { + return int_32; + } + double doubleValue() const { + return dbl; + } + void setDouble(double d) { + dbl = d; + } + double asDouble() const { + if (tag == _Integer_Type) + return int_32; + return dbl; + } + int integerValue() const { + return int_32; + } + +#if CPU(X86_64) + String *stringValue() const { + return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } + Object *objectValue() const { + return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } +#else + String *stringValue() const { + return s; + } + Object *objectValue() const { + return o; + } +#endif + + quint64 rawValue() const { + return val; + } + + static Value undefinedValue(); + static Value nullValue(); + static Value fromBoolean(Bool b); + static Value fromDouble(double d); + static Value fromInt32(int i); + static Value fromString(String *s); + static Value fromObject(Object *o); + +#ifndef QMLJS_LLVM_RUNTIME + static Value fromString(Context *ctx, const QString &fromString); +#endif + + static double toInteger(double fromNumber); + static int toInt32(double value); + static unsigned int toUInt32(double value); + + inline int toUInt16(Context *ctx); + inline int toInt32(Context *ctx); + inline unsigned int toUInt32(Context *ctx); + inline Bool toBoolean(Context *ctx) const; + inline double toInteger(Context *ctx) const; + double toNumber(Context *ctx) const; + inline String *toString(Context *ctx) const; + inline Value toObject(Context *ctx) const; + + inline bool isPrimitive() const { return !isObject(); } + static inline bool integerCompatible(Value a, Value b) { + return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; + } + inline bool tryIntegerConversion() { + bool b = isConvertibleToInt(); + if (b) + tag = _Integer_Type; + return b; + } + + Object *asObject() const; + FunctionObject *asFunctionObject() const; + BooleanObject *asBooleanObject() const; + NumberObject *asNumberObject() const; + StringObject *asStringObject() const; + DateObject *asDateObject() const; + RegExpObject *asRegExpObject() const; + ArrayObject *asArrayObject() const; + ErrorObject *asErrorObject() const; + ActivationObject *asArgumentsObject() const; + + Value property(Context *ctx, String *name) const; + Value *getPropertyDescriptor(Context *ctx, String *name) const; +}; + extern "C" { // context @@ -258,171 +418,8 @@ Bool __qmljs_cmp_sne(Value left, Value right, Context *ctx); Bool __qmljs_cmp_instanceof(Value left, Value right, Context *ctx); Bool __qmljs_cmp_in(Value left, Value right, Context *ctx); - } // extern "C" - -struct Value -{ - union { - quint64 val; - double dbl; - struct { -#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN - uint tag; -#endif - union { - uint uint_32; - int int_32; -#if CPU(X86_64) -#else - Object *o; - String *s; -#endif - }; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - uint tag; -#endif - }; - }; - - enum Masks { - NotDouble_Mask = 0xfff80000, - Type_Mask = 0xffff0000, - Immediate_Mask = NotDouble_Mask | 0x00040000, - Tag_Shift = 32 - }; - enum ValueType { - Undefined_Type = Immediate_Mask | 0x00000, - Null_Type = Immediate_Mask | 0x10000, - Boolean_Type = Immediate_Mask | 0x20000, - Integer_Type = Immediate_Mask | 0x30000, - Object_Type = NotDouble_Mask | 0x00000, - String_Type = NotDouble_Mask | 0x10000 - }; - - enum ImmediateFlags { - ConvertibleToInt = NotDouble_Mask | 0x1 - }; - - enum ValueTypeInternal { - _Undefined_Type = Undefined_Type, - _Null_Type = Null_Type | ConvertibleToInt, - _Boolean_Type = Boolean_Type | ConvertibleToInt, - _Integer_Type = Integer_Type | ConvertibleToInt, - _Object_Type = Object_Type, - _String_Type = String_Type - - }; - - inline ValueType type() const { - return (ValueType)(tag & Type_Mask); - } - - inline bool isUndefined() const { return tag == _Undefined_Type; } - inline bool isNull() const { return tag == _Null_Type; } - inline bool isBoolean() const { return tag == _Boolean_Type; } - inline bool isInteger() const { return tag == _Integer_Type; } - inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } - inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } -#if CPU(X86_64) - inline bool isString() const { return (tag & Type_Mask) == String_Type; } - inline bool isObject() const { return (tag & Type_Mask) == Object_Type; } -#else - inline bool isString() const { return tag == String_Type; } - inline bool isObject() const { return tag == Object_Type; } -#endif - inline bool isConvertibleToInt() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } - - Bool booleanValue() const { - return int_32; - } - double doubleValue() const { - return dbl; - } - void setDouble(double d) { - dbl = d; - } - double asDouble() const { - if (tag == _Integer_Type) - return int_32; - return dbl; - } - int integerValue() const { - return int_32; - } - -#if CPU(X86_64) - String *stringValue() const { - return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift)); - } - Object *objectValue() const { - return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift)); - } -#else - String *stringValue() const { - return s; - } - Object *objectValue() const { - return o; - } -#endif - - quint64 rawValue() const { - return val; - } - - static Value undefinedValue(); - static Value nullValue(); - static Value fromBoolean(Bool b); - static Value fromDouble(double d); - static Value fromInt32(int i); - static Value fromString(String *s); - static Value fromObject(Object *o); - -#ifndef QMLJS_LLVM_RUNTIME - static Value fromString(Context *ctx, const QString &fromString); -#endif - - static double toInteger(double fromNumber); - static int toInt32(double value); - static unsigned int toUInt32(double value); - - inline int toUInt16(Context *ctx); - inline int toInt32(Context *ctx); - inline unsigned int toUInt32(Context *ctx); - inline Bool toBoolean(Context *ctx) const; - inline double toInteger(Context *ctx) const; - double toNumber(Context *ctx) const; - inline String *toString(Context *ctx) const; - inline Value toObject(Context *ctx) const; - - inline bool isPrimitive() const { return !isObject(); } - static inline bool integerCompatible(Value a, Value b) { - return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; - } - inline bool tryIntegerConversion() { - bool b = isConvertibleToInt(); - if (b) - tag = _Integer_Type; - return b; - } - - Object *asObject() const; - FunctionObject *asFunctionObject() const; - BooleanObject *asBooleanObject() const; - NumberObject *asNumberObject() const; - StringObject *asStringObject() const; - DateObject *asDateObject() const; - RegExpObject *asRegExpObject() const; - ArrayObject *asArrayObject() const; - ErrorObject *asErrorObject() const; - ActivationObject *asArgumentsObject() const; - - Value property(Context *ctx, String *name) const; - Value *getPropertyDescriptor(Context *ctx, String *name) const; -}; - inline int Value::toUInt16(Context *ctx) { return __qmljs_to_uint16(*this, ctx); -- cgit v1.2.3 From 6ce5bc6abac40b9286cd68a948d5e278a37e1487 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 25 Oct 2012 14:27:14 +0200 Subject: More correct type tagging for constant created from binops In many cases the result of the binop is actually a boolean, not a number. Correctly type the constant as boolean in this case. Change-Id: I3c9b1f5b9fbd15bf97e4bafc9ceee6947343fd32 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index a0377397f5..0f51f70b16 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -403,30 +403,28 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) if (IR::Const *c1 = left->asConst()) { if (IR::Const *c2 = right->asConst()) { if (c1->type == IR::NumberType && c2->type == IR::NumberType) { - const IR::Type ty = IR::NumberType; - switch (op) { - case IR::OpAdd: return _block->CONST(ty, c1->value + c2->value); - case IR::OpAnd: return _block->CONST(ty, c1->value ? c2->value : 0); - case IR::OpBitAnd: return _block->CONST(ty, int(c1->value) & int(c2->value)); - case IR::OpBitOr: return _block->CONST(ty, int(c1->value) | int(c2->value)); - case IR::OpBitXor: return _block->CONST(ty, int(c1->value) ^ int(c2->value)); - case IR::OpDiv: return _block->CONST(ty, c1->value / c2->value); - case IR::OpEqual: return _block->CONST(ty, c1->value == c2->value); - case IR::OpGe: return _block->CONST(ty, c1->value >= c2->value); - case IR::OpGt: return _block->CONST(ty, c1->value > c2->value); - case IR::OpLe: return _block->CONST(ty, c1->value <= c2->value); - case IR::OpLShift: return _block->CONST(ty, int(c1->value) << int(c2->value)); - case IR::OpLt: return _block->CONST(ty, c1->value < c2->value); - case IR::OpMod: return _block->CONST(ty, ::fmod(c1->value, c2->value)); - case IR::OpMul: return _block->CONST(ty, c1->value * c2->value); - case IR::OpNotEqual: return _block->CONST(ty, c1->value != c2->value); - case IR::OpOr: return _block->CONST(ty, c1->value ? c1->value : c2->value); - case IR::OpRShift: return _block->CONST(ty, int(c1->value) >> int(c2->value)); - case IR::OpStrictEqual: return _block->CONST(ty, c1->value == c2->value); - case IR::OpStrictNotEqual: return _block->CONST(ty, c1->value != c2->value); - case IR::OpSub: return _block->CONST(ty, c1->value - c2->value); - case IR::OpURShift: return _block->CONST(ty, unsigned(c1->value) >> int(c2->value)); + case IR::OpAdd: return _block->CONST(IR::NumberType, c1->value + c2->value); + case IR::OpAnd: return _block->CONST(IR::BoolType, c1->value ? c2->value : 0); + case IR::OpBitAnd: return _block->CONST(IR::NumberType, int(c1->value) & int(c2->value)); + case IR::OpBitOr: return _block->CONST(IR::NumberType, int(c1->value) | int(c2->value)); + case IR::OpBitXor: return _block->CONST(IR::NumberType, int(c1->value) ^ int(c2->value)); + case IR::OpDiv: return _block->CONST(IR::NumberType, c1->value / c2->value); + case IR::OpEqual: return _block->CONST(IR::BoolType, c1->value == c2->value); + case IR::OpNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value); + case IR::OpStrictEqual: return _block->CONST(IR::BoolType, c1->value == c2->value); + case IR::OpStrictNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value); + case IR::OpGe: return _block->CONST(IR::BoolType, c1->value >= c2->value); + case IR::OpGt: return _block->CONST(IR::BoolType, c1->value > c2->value); + case IR::OpLe: return _block->CONST(IR::BoolType, c1->value <= c2->value); + case IR::OpLt: return _block->CONST(IR::BoolType, c1->value < c2->value); + case IR::OpLShift: return _block->CONST(IR::NumberType, int(c1->value) << int(c2->value)); + case IR::OpMod: return _block->CONST(IR::NumberType, ::fmod(c1->value, c2->value)); + case IR::OpMul: return _block->CONST(IR::NumberType, c1->value * c2->value); + case IR::OpOr: return _block->CONST(IR::NumberType, c1->value ? c1->value : c2->value); + case IR::OpRShift: return _block->CONST(IR::NumberType, int(c1->value) >> int(c2->value)); + case IR::OpSub: return _block->CONST(IR::NumberType, c1->value - c2->value); + case IR::OpURShift: return _block->CONST(IR::NumberType, unsigned(c1->value) >> int(c2->value)); case IR::OpInstanceof: case IR::OpIn: -- cgit v1.2.3 From 02c10bef52b29ab5384627c8cf84db95e5182c4d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 25 Oct 2012 14:14:10 +0200 Subject: Constant folding for unary operations Do constant folding for unary operators. Change-Id: I88f8b73c947d719ef089a9e53f7949fc6d66134b Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 0f51f70b16..9f2ddd57fd 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -389,6 +390,22 @@ IR::Expr *Codegen::argument(IR::Expr *expr) IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr) { + if (IR::Const *c = expr->asConst()) { + if (c->type == IR::NumberType) { + switch (op) { + case IR::OpNot: + return _block->CONST(IR::BoolType, !c->value); + case IR::OpUMinus: + return _block->CONST(IR::NumberType, -c->value); + case IR::OpUPlus: + return expr; + case IR::OpCompl: + return _block->CONST(IR::NumberType, ~VM::Value::toInt32(c->value)); + default: + break; + } + } + } if (! expr->asTemp()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), expr); -- cgit v1.2.3 From 577cbfa2eed1abe677216f3d57b41a8496ceb18b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 28 Oct 2012 16:24:06 +0100 Subject: Added basic support for 'for (var x in y)' statement Change-Id: I8f3c8add78bebf92e0073348d1ecbdf3f328af6d Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 26 +++++++++++++++++++++++ qmljs_objects.h | 13 ++++++++++++ qmljs_runtime.cpp | 21 +++++++++++++++++++ qmljs_runtime.h | 4 ++++ qv4codegen.cpp | 63 ++++++++++++++++++++++++++++++++++++++++--------------- qv4codegen_p.h | 2 ++ qv4ir.cpp | 4 ++++ qv4ir_p.h | 4 +++- qv4isel_masm.cpp | 11 ++++++++++ 9 files changed, 130 insertions(+), 18 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index e4c0d3d8e1..eccee64367 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -150,6 +150,27 @@ void Object::defineOwnProperty(Context *ctx, const Value &getter, const Value &s ctx->throwUnimplemented(QStringLiteral("defineOwnProperty")); } +String *ForEachIteratorObject::nextPropertyName() +{ + Property *p = 0; + while (1) { + if (!current) + return 0; + + // ### index array data as well + ++tableIndex; + if (!current->members || tableIndex > current->members->_propertyCount) { + current = current->prototype; + tableIndex = -1; + continue; + } + p = current->members->_properties[tableIndex]; + // ### check that it's not a repeated attribute + if (p /*&& !(p->attributes & DontEnumAttribute)*/) + return p->name; + } +} + Value ArrayObject::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) { if (name->isEqualTo(ctx->engine->id_length)) @@ -541,3 +562,8 @@ Object *ExecutionEngine::newActivationObject(Context *ctx) { return new ActivationObject(ctx); } + +Object *ExecutionEngine::newForEachIteratorObject(Object *o) +{ + return new ForEachIteratorObject(o); +} diff --git a/qmljs_objects.h b/qmljs_objects.h index a98b640675..61f43b1584 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -264,6 +264,7 @@ private: } private: + friend struct ForEachIteratorObject; Property **_properties; Property **_buckets; Property *_freeList; @@ -314,6 +315,16 @@ struct Object { void setProperty(Context *ctx, const QString &name, void (*code)(Context *), int count = 0); }; +struct ForEachIteratorObject: Object { + Object *object; + Object *current; // inside the prototype chain + int tableIndex; + ForEachIteratorObject(Object *o) : object(o), current(o), tableIndex(-1) {} + virtual QString className() { return QStringLiteral("__ForEachIteratorObject"); } + + String *nextPropertyName(); +}; + struct BooleanObject: Object { Value value; BooleanObject(const Value &value): value(value) {} @@ -505,6 +516,8 @@ struct ExecutionEngine Object *newErrorObject(const Value &value); Object *newMathObject(Context *ctx); Object *newActivationObject(Context *ctx); + + Object *newForEachIteratorObject(Object *o); }; } // namespace VM diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index faae161008..2abaf11511 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -1024,6 +1024,27 @@ void __qmljs_set_element(Context *ctx, Value object, Value index, Value value) object.objectValue()->setProperty(ctx, name, value, /*flags*/ 0); } +Value __qmljs_foreach_iterator_object(Value in, Context *ctx) +{ + in = __qmljs_to_object(in, ctx); + Object *it = ctx->engine->newForEachIteratorObject(in.objectValue()); + return Value::fromObject(it); +} + +Value __qmljs_foreach_next_property_name(Value foreach_iterator) +{ + assert(foreach_iterator.isObject()); + + ForEachIteratorObject *it = static_cast(foreach_iterator.objectValue()); + assert(it->className() == QLatin1String("__ForEachIteratorObject")); + + String *s = it->nextPropertyName(); + if (!s) + return Value::nullValue(); + return Value::fromString(s); +} + + void __qmljs_set_activation_property(Context *ctx, String *name, Value value) { if (Value *prop = ctx->lookupPropertyDescriptor(name)) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 4427ede3a5..508396d8cf 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -308,6 +308,10 @@ Value __qmljs_get_activation_property(Context *ctx, String *name); Value __qmljs_get_element(Context *ctx, Value object, Value index); void __qmljs_set_element(Context *ctx, Value object, Value index, Value value); +// For each +Value __qmljs_foreach_iterator_object(Value in, Context *ctx); +Value __qmljs_foreach_next_property_name(Value foreach_iterator); + // context Value __qmljs_get_thisObject(Context *ctx); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 9f2ddd57fd..4e9c9cf421 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1081,32 +1081,32 @@ bool Codegen::visit(FunctionExpression *ast) return false; } -bool Codegen::visit(IdentifierExpression *ast) +IR::Expr *Codegen::identifier(const QString &name, int line, int col) { - int index = _env->findMember(ast->name.toString()); + int index = _env->findMember(name); if (! _function->hasDirectEval && _env->parent) { if (index != -1) { - _expr.code = _block->TEMP(index); - return false; + return _block->TEMP(index); } - index = indexOfArgument(ast->name); + index = indexOfArgument(&name); if (index != -1) { - _expr.code = _block->TEMP(-(index + 1)); - return false; + return _block->TEMP(-(index + 1)); } } if (index >= _env->vars.size()) { // named local variable, e.g. in a catch statement - _expr.code = _block->TEMP(index); - return false; + return _block->TEMP(index); } - _expr.code = _block->NAME(ast->name.toString(), - ast->identifierToken.startLine, - ast->identifierToken.startColumn); + return _block->NAME(name, line, col); +} + +bool Codegen::visit(IdentifierExpression *ast) +{ + _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn); return false; } @@ -1532,9 +1532,6 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, _block->JUMP(_exitBlock); - // ### should not be required - _throwBlock->JUMP(_exitBlock); - qSwap(_function, function); qSwap(_block, entryBlock); qSwap(_exitBlock, exitBlock); @@ -1746,9 +1743,41 @@ bool Codegen::visit(LabelledStatement *ast) return false; } -bool Codegen::visit(LocalForEachStatement *) +bool Codegen::visit(LocalForEachStatement *ast) { - assert(!"not implemented"); + IR::BasicBlock *foreachin = _function->newBasicBlock(); + IR::BasicBlock *foreachbody = _function->newBasicBlock(); + IR::BasicBlock *foreachend = _function->newBasicBlock(); + + enterLoop(ast, foreachend, foreachin); + + variableDeclaration(ast->declaration); + + int iterator = _block->newTemp(); + move(_block->TEMP(iterator), *expression(ast->expression)); + IR::ExprList *args = _function->New(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + + _block->JUMP(foreachin); + + _block = foreachbody; + int temp = _block->newTemp(); + move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); + statement(ast->statement); + _block->JUMP(foreachin); + + _block = foreachin; + + args = _function->New(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args)); + int null = _block->newTemp(); + move(_block->TEMP(null), _block->CONST(IR::NullType, 0)); + cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); + _block = foreachend; + + leaveLoop(); return false; } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 95fedb5bc6..1f5b87feba 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -203,6 +203,8 @@ protected: void variableDeclaration(AST::VariableDeclaration *ast); void variableDeclarationList(AST::VariableDeclarationList *ast); + IR::Expr *identifier(const QString &name, int line = 0, int col = 0); + // nodes virtual bool visit(AST::ArgumentList *ast); virtual bool visit(AST::CaseBlock *ast); diff --git a/qv4ir.cpp b/qv4ir.cpp index 40c85c8efd..21b0fe7193 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -230,6 +230,10 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_delete_exception_handler"; case Name::builtin_get_exception: return "builtin_get_exception"; + case IR::Name::builtin_foreach_iterator_object: + return "builtin_foreach_iterator_object"; + case IR::Name::builtin_foreach_next_property_name: + return "builtin_foreach_next_property_name"; } return "builtin_(###FIXME)"; }; diff --git a/qv4ir_p.h b/qv4ir_p.h index 7853cc2f29..9e004a518a 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -274,7 +274,9 @@ struct Name: Expr { builtin_throw, builtin_create_exception_handler, builtin_delete_exception_handler, - builtin_get_exception + builtin_get_exception, + builtin_foreach_iterator_object, + builtin_foreach_next_property_name }; const QString *id; diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 3662b880fc..54a0b4c77f 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -223,6 +223,17 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu break; case IR::Name::builtin_get_exception: generateFunctionCall(result, __qmljs_get_exception, ContextRegister); + case IR::Name::builtin_foreach_iterator_object: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + generateFunctionCall(result, __qmljs_foreach_iterator_object, arg, ContextRegister); + } + break; + case IR::Name::builtin_foreach_next_property_name: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + generateFunctionCall(result, __qmljs_foreach_next_property_name, arg); + } break; } } -- cgit v1.2.3 From 6ace5d1a2b3b1dd9ef1e1223db8298f6c20a9e58 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 28 Oct 2012 20:33:54 +0100 Subject: Implement the other variant of the 'for in' statement Change-Id: I90823a97331fd15a8289bab93d7e9c1f34aaa10d Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 4e9c9cf421..225b43cb3a 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1663,9 +1663,39 @@ bool Codegen::visit(ExpressionStatement *ast) return false; } -bool Codegen::visit(ForEachStatement *) +bool Codegen::visit(ForEachStatement *ast) { - assert(!"not implemented"); + IR::BasicBlock *foreachin = _function->newBasicBlock(); + IR::BasicBlock *foreachbody = _function->newBasicBlock(); + IR::BasicBlock *foreachend = _function->newBasicBlock(); + + enterLoop(ast, foreachend, foreachin); + + int iterator = _block->newTemp(); + move(_block->TEMP(iterator), *expression(ast->expression)); + IR::ExprList *args = _function->New(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + + _block->JUMP(foreachin); + + _block = foreachbody; + int temp = _block->newTemp(); + move(*expression(ast->initialiser), _block->TEMP(temp)); + statement(ast->statement); + _block->JUMP(foreachin); + + _block = foreachin; + + args = _function->New(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args)); + int null = _block->newTemp(); + move(_block->TEMP(null), _block->CONST(IR::NullType, 0)); + cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); + _block = foreachend; + + leaveLoop(); return false; } -- cgit v1.2.3 From aa96410f3fa04a76f05312a26b2becd9f924c040 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 28 Oct 2012 21:56:15 +0100 Subject: Rework properties This brings the basic structure or accessing properties more in line with the EcmaScript 5.1 specification. There's however still quite some work to be done to make things fully compliant. Change-Id: If55afd7ae6e4f7aa5ce06afe49b1453b537ac98b Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 149 +++++++++++++++++++++++++++++++----------------- qmljs_objects.h | 164 ++++++++++++++++++++++++++++++++++------------------- qmljs_runtime.cpp | 10 +--- qmljs_runtime.h | 10 +--- qv4ecmaobjects.cpp | 10 ++-- qv4ecmaobjects_p.h | 2 +- 6 files changed, 213 insertions(+), 132 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index eccee64367..f7ba720ec2 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -69,55 +69,86 @@ void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context setProperty(ctx, name, Value::fromObject(ctx->engine->newNativeFunction(ctx, code))); } -Value Object::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) +Value Object::getProperty(Context *ctx, String *name) { if (name->isEqualTo(ctx->engine->id___proto__)) return Value::fromObject(prototype); - else if (Value *v = getPropertyDescriptor(ctx, name, attributes)) - return *v; + + PropertyDescriptor tmp; + if (PropertyDescriptor *p = getPropertyDescriptor(ctx, name, &tmp)) { + if (p->isData()) + return p->value; + if (!p->get) + return Value::undefinedValue(); + FunctionObject *f = p->get->asFunctionObject(); + if (f) { + f->call(ctx); + return ctx->result; + } + } return Value::undefinedValue(); } -Value *Object::getOwnProperty(Context *, String *name, PropertyAttributes *attributes) +// Section 8.12.1 +PropertyDescriptor *Object::getOwnProperty(Context *, String *name) { - if (members) { - if (Property *prop = members->find(name)) { - if (attributes) - *attributes = prop->attributes; - return &prop->value; - } - } + if (members) + return members->find(name); return 0; } -Value *Object::getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes) +PropertyDescriptor *Object::getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill) { - if (Value *prop = getOwnProperty(ctx, name, attributes)) - return prop; - else if (prototype) - return prototype->getPropertyDescriptor(ctx, name, attributes); + if (PropertyDescriptor *p = getOwnProperty(ctx, name)) + return p; + + if (prototype) + return prototype->getPropertyDescriptor(ctx, name, to_fill); return 0; } -void Object::setProperty(Context *, String *name, const Value &value, bool flag) +// Section 8.12.5 +void Object::setProperty(Context *ctx, String *name, const Value &value, bool throwException) { - Q_UNUSED(flag); + if (!canSetProperty(ctx, name)) { + if (throwException) + __qmljs_throw_type_error(ctx); + return; + } if (! members) - members = new Table(); + members = new PropertyTable(); - members->insert(name, value); + PropertyDescriptor *pd = getOwnProperty(ctx, name); + if (pd) { + if (pd->isData()) { + pd->value = value; + return; + } + } + PropertyDescriptor *p = members->insert(name); + *p = PropertyDescriptor::fromValue(value); } +// Section 8.12.4 bool Object::canSetProperty(Context *ctx, String *name) { - PropertyAttributes attrs = PropertyAttributes(); - if (getOwnProperty(ctx, name, &attrs)) { - return attrs & WritableAttribute; - } else if (! prototype) { + if (PropertyDescriptor *p = getOwnProperty(ctx, name)) { + if (p->isAccessor()) + return p->get != 0; + return p->isWritable(); + } + + if (! prototype) return extensible; - } else if (prototype->getPropertyDescriptor(ctx, name, &attrs)) { - return attrs & WritableAttribute; + + PropertyDescriptor tmp; + if (PropertyDescriptor *p = prototype->getPropertyDescriptor(ctx, name, &tmp)) { + if (p->isAccessor()) + return p->get != 0; + if (!extensible) + return false; + return p->isWritable(); } else { return extensible; } @@ -142,17 +173,26 @@ bool Object::deleteProperty(Context *, String *name, bool flag) return false; } -void Object::defineOwnProperty(Context *ctx, const Value &getter, const Value &setter, bool flag) +bool Object::defineOwnProperty(Context *ctx, String *name, const Value &getter, const Value &setter, bool flag) { - Q_UNUSED(getter); - Q_UNUSED(setter); - Q_UNUSED(flag); - ctx->throwUnimplemented(QStringLiteral("defineOwnProperty")); + if (!members) + members = new PropertyTable(); + + PropertyDescriptor *p = getOwnProperty(ctx, name); + if (!p) { + if (!extensible) + goto reject; + } + + reject: + if (flag) + __qmljs_throw_type_error(ctx); + return false; } String *ForEachIteratorObject::nextPropertyName() { - Property *p = 0; + PropertyTableEntry *p = 0; while (1) { if (!current) return 0; @@ -171,11 +211,11 @@ String *ForEachIteratorObject::nextPropertyName() } } -Value ArrayObject::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) +Value ArrayObject::getProperty(Context *ctx, String *name) { if (name->isEqualTo(ctx->engine->id_length)) return Value::fromDouble(value.size()); - return Object::getProperty(ctx, name, attributes); + return Object::getProperty(ctx, name); } bool FunctionObject::hasInstance(Context *ctx, const Value &value) @@ -250,7 +290,7 @@ void ScriptFunction::call(VM::Context *ctx) function->code(ctx, function->codeData); } -Value RegExpObject::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) +Value RegExpObject::getProperty(Context *ctx, String *name) { QString n = name->toQString(); if (n == QLatin1String("source")) @@ -263,7 +303,7 @@ Value RegExpObject::getProperty(Context *ctx, String *name, PropertyAttributes * return Value::fromBoolean(value.patternOptions() & QRegularExpression::MultilineOption); else if (n == QLatin1String("lastIndex")) return lastIndex; - return Object::getProperty(ctx, name, attributes); + return Object::getProperty(ctx, name); } @@ -277,23 +317,23 @@ void ScriptFunction::construct(VM::Context *ctx) function->code(ctx, function->codeData); } -Value *ActivationObject::getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes) +PropertyDescriptor *ActivationObject::getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill) { if (context) { for (unsigned int i = 0; i < context->varCount; ++i) { String *var = context->vars[i]; if (__qmljs_string_equal(context, var, name)) { - if (attributes) - *attributes = PropertyAttributes(*attributes | WritableAttribute); - return &context->locals[i]; + *to_fill = PropertyDescriptor::fromValue(context->locals[i]); + to_fill->writable = PropertyDescriptor::Set; + return to_fill; } } for (unsigned int i = 0; i < context->formalCount; ++i) { String *formal = context->formals[i]; if (__qmljs_string_equal(context, formal, name)) { - if (attributes) - *attributes = PropertyAttributes(*attributes | WritableAttribute); - return &context->arguments[i]; + *to_fill = PropertyDescriptor::fromValue(context->arguments[i]); + to_fill->writable = PropertyDescriptor::Set; + return to_fill; } } if (name->isEqualTo(ctx->engine->id_arguments)) { @@ -302,29 +342,32 @@ Value *ActivationObject::getPropertyDescriptor(Context *ctx, String *name, Prope arguments.objectValue()->prototype = ctx->engine->objectPrototype; } - return &arguments; + *to_fill = PropertyDescriptor::fromValue(arguments); + return to_fill; } } - if (Value *prop = Object::getPropertyDescriptor(ctx, name, attributes)) - return prop; - return 0; + + return Object::getPropertyDescriptor(ctx, name, to_fill); } -Value ArgumentsObject::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) +Value ArgumentsObject::getProperty(Context *ctx, String *name) { if (name->isEqualTo(ctx->engine->id_length)) return Value::fromDouble(context->argumentCount); - return Object::getProperty(ctx, name, attributes); + return Object::getProperty(ctx, name); } -Value *ArgumentsObject::getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes) +PropertyDescriptor *ArgumentsObject::getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill) { if (context) { const quint32 i = Value::fromString(name).toUInt32(ctx); - if (i < context->argumentCount) - return &context->arguments[i]; + if (i < context->argumentCount) { + *to_fill = PropertyDescriptor::fromValue(context->arguments[i]); + return to_fill; + } } - return Object::getPropertyDescriptor(ctx, name, attributes); + + return Object::getPropertyDescriptor(ctx, name, to_fill); } ExecutionEngine::ExecutionEngine() diff --git a/qmljs_objects.h b/qmljs_objects.h index 61f43b1584..425cbd53ff 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -111,39 +111,80 @@ private: mutable unsigned _hashValue; }; -struct Property { - String *name; - Value value; - PropertyAttributes attributes; - Property *next; - int index; +struct PropertyDescriptor { + enum Type { + Generic, + Data, + Accessor + }; + enum State { + Undefined, + Unset, + Set + }; + union { + Value value; + struct { + Object *get; + Object *set; + }; + }; + uint type : 8; + uint writable : 8; + uint enumberable : 8; + uint configurable : 8; + + static inline PropertyDescriptor fromValue(Value v) { + PropertyDescriptor pd; + pd.value = v; + pd.type = Data; + pd.writable = Set; + pd.enumberable = Set; + pd.configurable = Set; + return pd; + } + static inline PropertyDescriptor fromAccessor(Object *getter, Object *setter) { + PropertyDescriptor pd; + pd.get = getter; + pd.set = setter; + pd.type = Accessor; + pd.writable = Undefined; + pd.enumberable = Set; + pd.configurable = Set; + return pd; + } - inline Property(String *name, const Value &value, PropertyAttributes flags = NoAttributes) - { init(name, value, flags); } + inline bool isData() const { return type == Data; } + inline bool isAccessor() const { return type == Accessor; } + inline bool isGeneric() const { return type == Generic; } - inline void init(String *name, const Value &value, PropertyAttributes flags = NoAttributes) - { - this->name = name; - this->value = value; - this->attributes = flags; - this->next = 0; - this->index = -1; - } + inline bool isWritable() const { return writable == Set; } + inline bool isEnumerable() const { return enumberable == Set; } + inline bool isConfigurable() const { return configurable == Set; } +}; - inline bool isWritable() const { return attributes & WritableAttribute; } - inline bool isEnumerable() const { return attributes & EnumerableAttribute; } - inline bool isConfigurable() const { return attributes & ConfigurableAttribute; } +struct PropertyTableEntry { + PropertyDescriptor descriptor; + String *name; + PropertyTableEntry *next; + int index; + + inline PropertyTableEntry(String *name) + : name(name), + next(0), + index(-1) + { } inline bool hasName(String *n) const { return name->isEqualTo(n); } inline unsigned hashValue() const { return name->hashValue(); } }; -class Table +class PropertyTable { - Q_DISABLE_COPY(Table) + Q_DISABLE_COPY(PropertyTable) public: - Table() + PropertyTable() : _properties(0) , _buckets(0) , _freeList(0) @@ -151,7 +192,7 @@ public: , _bucketCount(0) , _allocated(0) {} - ~Table() + ~PropertyTable() { qDeleteAll(_properties, _properties + _propertyCount + 1); delete[] _properties; @@ -160,20 +201,20 @@ public: inline bool isEmpty() const { return _propertyCount == -1; } - typedef Property **iterator; + typedef PropertyTableEntry **iterator; inline iterator begin() const { return _properties; } inline iterator end() const { return _properties + (_propertyCount + 1); } bool remove(String *name) { - if (Property *prop = find(name)) { + if (PropertyTableEntry *prop = findEntry(name)) { // ### TODO check if the property can be removed - Property *bucket = _buckets[prop->hashValue() % _bucketCount]; + PropertyTableEntry *bucket = _buckets[prop->hashValue() % _bucketCount]; if (bucket == prop) { bucket = bucket->next; } else { - for (Property *it = bucket; it; it = it->next) { + for (PropertyTableEntry *it = bucket; it; it = it->next) { if (it->next == prop) { it->next = it->next->next; break; @@ -189,10 +230,10 @@ public: return true; } - Property *find(String *name) const + PropertyTableEntry *findEntry(String *name) const { if (_properties) { - for (Property *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { + for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { if (prop && (prop->name == name || prop->hasName(name))) return prop; } @@ -201,32 +242,41 @@ public: return 0; } - Property *insert(String *name, const Value &value) + PropertyDescriptor *find(String *name) const { - if (Property *prop = find(name)) { - prop->value = value; - return prop; + if (_properties) { + for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { + if (prop && (prop->name == name || prop->hasName(name))) + return &prop->descriptor; + } } + return 0; + } + + PropertyDescriptor *insert(String *name) + { + if (PropertyTableEntry *prop = findEntry(name)) + return &prop->descriptor; + if (++_propertyCount == _allocated) { if (! _allocated) _allocated = 4; else _allocated *= 2; - Property **properties = new Property*[_allocated]; + PropertyTableEntry **properties = new PropertyTableEntry*[_allocated]; std::copy(_properties, _properties + _propertyCount, properties); delete[] _properties; _properties = properties; } - Property *prop; + PropertyTableEntry *prop; if (_freeList) { prop = _freeList; _freeList = _freeList->next; - prop->init(name, value); } else { - prop = new Property(name, value); + prop = new PropertyTableEntry(name); } prop->index = _propertyCount; @@ -235,12 +285,12 @@ public: if (! _buckets || 3 * _propertyCount >= 2 * _bucketCount) { rehash(); } else { - Property *&bucket = _buckets[prop->hashValue() % _bucketCount]; + PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; prop->next = bucket; bucket = prop; } - return prop; + return &prop->descriptor; } private: @@ -252,12 +302,12 @@ private: _bucketCount = 11; delete[] _buckets; - _buckets = new Property *[_bucketCount]; - std::fill(_buckets, _buckets + _bucketCount, (Property *) 0); + _buckets = new PropertyTableEntry *[_bucketCount]; + std::fill(_buckets, _buckets + _bucketCount, (PropertyTableEntry *) 0); for (int i = 0; i <= _propertyCount; ++i) { - Property *prop = _properties[i]; - Property *&bucket = _buckets[prop->hashValue() % _bucketCount]; + PropertyTableEntry *prop = _properties[i]; + PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; prop->next = bucket; bucket = prop; } @@ -265,9 +315,9 @@ private: private: friend struct ForEachIteratorObject; - Property **_properties; - Property **_buckets; - Property *_freeList; + PropertyTableEntry **_properties; + PropertyTableEntry **_buckets; + PropertyTableEntry *_freeList; int _propertyCount; int _bucketCount; int _allocated; @@ -276,7 +326,7 @@ private: struct Object { Object *prototype; String *klass; - Table *members; + PropertyTable *members; bool extensible; Object() @@ -299,14 +349,14 @@ struct Object { virtual ActivationObject *asActivationObject() { return 0; } virtual ArgumentsObject *asArgumentsObject() { return 0; } - virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes = 0); - virtual Value *getOwnProperty(Context *ctx, String *name, PropertyAttributes *attributes = 0); - virtual Value *getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes = 0); - virtual void setProperty(Context *ctx, String *name, const Value &value, bool flag = false); + virtual Value getProperty(Context *ctx, String *name); + virtual PropertyDescriptor *getOwnProperty(Context *ctx, String *name); + virtual PropertyDescriptor *getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill); + virtual void setProperty(Context *ctx, String *name, const Value &value, bool throwException = false); virtual bool canSetProperty(Context *ctx, String *name); virtual bool hasProperty(Context *ctx, String *name) const; virtual bool deleteProperty(Context *ctx, String *name, bool flag); - virtual void defineOwnProperty(Context *ctx, const Value &getter, const Value &setter, bool flag = false); + virtual bool defineOwnProperty(Context *ctx, String *name, const Value &getter, const Value &setter, bool flag = false); // // helpers @@ -359,7 +409,7 @@ struct ArrayObject: Object { ArrayObject(const Array &value): value(value) {} virtual QString className() { return QStringLiteral("Array"); } virtual ArrayObject *asArrayObject() { return this; } - virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes); + virtual Value getProperty(Context *ctx, String *name); }; struct FunctionObject: Object { @@ -412,7 +462,7 @@ struct RegExpObject: Object { RegExpObject(const QRegularExpression &value, bool global): value(value), lastIndex(Value::fromInt32(0)), global(global) {} virtual QString className() { return QStringLiteral("RegExp"); } virtual RegExpObject *asRegExpObject() { return this; } - virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes); + virtual Value getProperty(Context *ctx, String *name); }; struct ErrorObject: Object { @@ -428,7 +478,7 @@ struct ActivationObject: Object { ActivationObject(Context *context): context(context), arguments(Value::undefinedValue()) {} virtual QString className() { return QStringLiteral("Activation"); } virtual ActivationObject *asActivationObject() { return this; } - virtual Value *getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes); + virtual PropertyDescriptor *getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill); }; struct ArgumentsObject: Object { @@ -436,8 +486,8 @@ struct ArgumentsObject: Object { ArgumentsObject(Context *context): context(context) {} virtual QString className() { return QStringLiteral("Arguments"); } virtual ArgumentsObject *asArgumentsObject() { return this; } - virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes); - virtual Value *getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes); + virtual Value getProperty(Context *ctx, String *name); + virtual PropertyDescriptor *getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill); }; struct ExecutionEngine diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 2abaf11511..904a7b0bb1 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -235,11 +235,6 @@ Value Value::property(Context *ctx, String *name) const return isObject() ? objectValue()->getProperty(ctx, name) : undefinedValue(); } -Value *Value::getPropertyDescriptor(Context *ctx, String *name) const -{ - return isObject() ? objectValue()->getPropertyDescriptor(ctx, name) : 0; -} - void Context::init(ExecutionEngine *eng) { engine = eng; @@ -261,8 +256,9 @@ Value *Context::lookupPropertyDescriptor(String *name) { for (Context *ctx = this; ctx; ctx = ctx->parent) { if (ctx->activation.isObject()) { - if (Value *prop = ctx->activation.objectValue()->getPropertyDescriptor(this, name)) { - return prop; + PropertyDescriptor tmp; + if (PropertyDescriptor *pd = ctx->activation.objectValue()->getPropertyDescriptor(this, name, &tmp)) { + return &pd->value; } } } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 508396d8cf..c5216732bc 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -66,16 +66,9 @@ enum TypeHint { STRING_HINT }; -enum PropertyAttributes { - NoAttributes = 0, - ValueAttribute = 1, - WritableAttribute = 2, - EnumerableAttribute = 4, - ConfigurableAttribute = 8 -}; - struct Object; struct String; +struct PropertyDescriptor; struct Context; struct FunctionObject; struct BooleanObject; @@ -248,7 +241,6 @@ struct Value ActivationObject *asArgumentsObject() const; Value property(Context *ctx, String *name) const; - Value *getPropertyDescriptor(Context *ctx, String *name) const; }; extern "C" { diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index ef211ea470..1e75ef9153 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -530,11 +530,11 @@ void ObjectCtor::call(Context *ctx) ctx->result = Value::fromObject(ctx->engine->newObject()); } -Value ObjectCtor::getProperty(Context *ctx, String *name, PropertyAttributes *attributes) +Value ObjectCtor::getProperty(Context *ctx, String *name) { if (name == ctx->engine->id_length) return Value::fromDouble(1); - return Object::getProperty(ctx, name, attributes); + return Object::getProperty(ctx, name); } void ObjectPrototype::init(Context *ctx, const Value &ctor) @@ -586,9 +586,9 @@ void ObjectPrototype::method_getOwnPropertyNames(Context *ctx) else { ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); Array &a = array->value; - if (Table *members = O.objectValue()->members) { - for (Property **it = members->begin(), **end = members->end(); it != end; ++it) { - if (Property *prop = *it) { + if (PropertyTable *members = O.objectValue()->members) { + for (PropertyTableEntry **it = members->begin(), **end = members->end(); it != end; ++it) { + if (PropertyTableEntry *prop = *it) { a.push(Value::fromString(prop->name)); } } diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index b918973bf4..b0484047e9 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -53,7 +53,7 @@ struct ObjectCtor: FunctionObject virtual void construct(Context *ctx); virtual void call(Context *ctx); - virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes); + virtual Value getProperty(Context *ctx, String *name); }; struct ObjectPrototype: Object -- cgit v1.2.3 From 950e4f70a31b5ab220a1d36cb492f8a22921ee84 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 29 Oct 2012 12:26:40 +0100 Subject: Add a Value::sameValue() method This implements Section 9.11 of the spec. Also simplify __qmljs_strict_equal(). Change-Id: I99b33308c33f4d4732b4a72bd5327336b2b22257 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 6 +++--- qmljs_runtime.cpp | 6 +++--- qmljs_runtime.h | 37 ++++++++++++++++++++++++------------- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index f7ba720ec2..bc8aa9f103 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -116,7 +116,7 @@ void Object::setProperty(Context *ctx, String *name, const Value &value, bool th return; } - if (! members) + if (!members) members = new PropertyTable(); PropertyDescriptor *pd = getOwnProperty(ctx, name); @@ -322,7 +322,7 @@ PropertyDescriptor *ActivationObject::getPropertyDescriptor(Context *ctx, String if (context) { for (unsigned int i = 0; i < context->varCount; ++i) { String *var = context->vars[i]; - if (__qmljs_string_equal(context, var, name)) { + if (__qmljs_string_equal(var, name)) { *to_fill = PropertyDescriptor::fromValue(context->locals[i]); to_fill->writable = PropertyDescriptor::Set; return to_fill; @@ -330,7 +330,7 @@ PropertyDescriptor *ActivationObject::getPropertyDescriptor(Context *ctx, String } for (unsigned int i = 0; i < context->formalCount; ++i) { String *formal = context->formals[i]; - if (__qmljs_string_equal(context, formal, name)) { + if (__qmljs_string_equal(formal, name)) { *to_fill = PropertyDescriptor::fromValue(context->arguments[i]); to_fill->writable = PropertyDescriptor::Set; return to_fill; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 904a7b0bb1..8acaec375d 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -870,9 +870,9 @@ Bool __qmljs_string_compare(Context *, String *left, String *right) return left->toQString() < right->toQString(); } -Bool __qmljs_string_equal(Context *, String *left, String *right) +Bool __qmljs_string_equal(String *left, String *right) { - return left == right || left->isEqualTo(right); + return left->isEqualTo(right); } String *__qmljs_string_concat(Context *ctx, String *first, String *second) @@ -1120,7 +1120,7 @@ uint __qmljs_equal(Value x, Value y, Context *ctx) case Value::Integer_Type: return x.integerValue() == y.integerValue(); case Value::String_Type: - return __qmljs_string_equal(ctx, x.stringValue(), y.stringValue()); + return __qmljs_string_equal(x.stringValue(), y.stringValue()); case Value::Object_Type: return x.objectValue() == y.objectValue(); default: // double diff --git a/qmljs_runtime.h b/qmljs_runtime.h index c5216732bc..fc60299065 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -241,6 +241,9 @@ struct Value ActivationObject *asArgumentsObject() const; Value property(Context *ctx, String *name) const; + + // Section 9.12 + bool sameValue(Value other); }; extern "C" { @@ -281,7 +284,7 @@ int __qmljs_string_length(Context *ctx, String *string); double __qmljs_string_to_number(Context *ctx, String *string); Value __qmljs_string_from_number(Context *ctx, double number); Bool __qmljs_string_compare(Context *ctx, String *left, String *right); -Bool __qmljs_string_equal(Context *ctx, String *left, String *right); +Bool __qmljs_string_equal(String *left, String *right); String *__qmljs_string_concat(Context *ctx, String *first, String *second); String *__qmljs_identifier_from_utf8(Context *ctx, const char *s); @@ -321,7 +324,7 @@ Bool __qmljs_is_callable(Value value, Context *ctx); Value __qmljs_default_value(Value value, Context *ctx, int typeHint); Bool __qmljs_equal(Value x, Value y, Context *ctx); -Bool __qmljs_strict_equal(Value x, Value y, Context *ctx); +Bool __qmljs_strict_equal(Value x, Value y); // unary operators Value __qmljs_uplus(Value value, Context *ctx); @@ -537,6 +540,14 @@ inline Value Value::fromObject(Object *o) return v; } +inline bool Value::sameValue(Value other) { + if (val == other.val) + return true; + if (isString() && other.isString()) + return __qmljs_string_equal(stringValue(), other.stringValue()); + return false; +} + #include struct Context { @@ -979,15 +990,15 @@ inline Value __qmljs_ne(Value left, Value right, Context *ctx) return Value::fromBoolean(!__qmljs_cmp_eq(left, right, ctx)); } -inline Value __qmljs_se(Value left, Value right, Context *ctx) +inline Value __qmljs_se(Value left, Value right, Context *) { - bool r = __qmljs_strict_equal(left, right, ctx); + bool r = __qmljs_strict_equal(left, right); return Value::fromBoolean(r); } -inline Value __qmljs_sne(Value left, Value right, Context *ctx) +inline Value __qmljs_sne(Value left, Value right, Context *) { - bool r = ! __qmljs_strict_equal(left, right, ctx); + bool r = ! __qmljs_strict_equal(left, right); return Value::fromBoolean(r); } @@ -1071,7 +1082,7 @@ inline Bool __qmljs_cmp_eq(Value left, Value right, Context *ctx) if (left.val == right.val) return true; if (left.isString() && right.isString()) - return __qmljs_string_equal(ctx, left.stringValue(), right.stringValue()); + return __qmljs_string_equal(left.stringValue(), right.stringValue()); return __qmljs_equal(left, right, ctx); } @@ -1081,14 +1092,14 @@ inline Bool __qmljs_cmp_ne(Value left, Value right, Context *ctx) return !__qmljs_cmp_eq(left, right, ctx); } -inline Bool __qmljs_cmp_se(Value left, Value right, Context *ctx) +inline Bool __qmljs_cmp_se(Value left, Value right, Context *) { - return __qmljs_strict_equal(left, right, ctx); + return __qmljs_strict_equal(left, right); } -inline Bool __qmljs_cmp_sne(Value left, Value right, Context *ctx) +inline Bool __qmljs_cmp_sne(Value left, Value right, Context *) { - return ! __qmljs_strict_equal(left, right, ctx); + return ! __qmljs_strict_equal(left, right); } inline Bool __qmljs_cmp_instanceof(Value left, Value right, Context *ctx) @@ -1103,12 +1114,12 @@ inline uint __qmljs_cmp_in(Value left, Value right, Context *ctx) return v.booleanValue(); } -inline Bool __qmljs_strict_equal(Value x, Value y, Context *ctx) +inline Bool __qmljs_strict_equal(Value x, Value y) { if (x.rawValue() == y.rawValue()) return true; if (x.isString() && y.isString()) - return __qmljs_string_equal(ctx, x.stringValue(), y.stringValue()); + return __qmljs_string_equal(x.stringValue(), y.stringValue()); return false; } -- cgit v1.2.3 From ddba8f6bb06f0f2312a8c070f8cf4a9e079c9ca7 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 29 Oct 2012 15:37:02 +0100 Subject: Conformant implementation of the object internal methods See section 8.12 of the standard. This implements 8.12.1 - 8.12.7 and 8.12.9 Also gave these methods standard conformant names. They are marked as [[foo]] in the standard, which translates to __foo__ in our code. Change-Id: I1990d6c6dd24e929c23d5c51d36f1e2e0a0a3b63 Reviewed-by: Simon Hausmann --- main.cpp | 6 +- qmljs_objects.cpp | 274 +++++++++++++++++++++++++++------------- qmljs_objects.h | 112 ++++++++++++----- qmljs_runtime.cpp | 82 ++++++------ qv4ecmaobjects.cpp | 364 ++++++++++++++++++++++++++--------------------------- qv4ecmaobjects_p.h | 2 +- 6 files changed, 496 insertions(+), 344 deletions(-) diff --git a/main.cpp b/main.cpp index bdd5b37dd9..91247b8c7b 100644 --- a/main.cpp +++ b/main.cpp @@ -247,7 +247,7 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS ctx->activation = VM::Value::fromObject(new QQmlJS::VM::Object()); foreach (const QString *local, globalCode->locals) { - ctx->activation.objectValue()->setProperty(ctx, *local, QQmlJS::VM::Value::undefinedValue()); + ctx->activation.objectValue()->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); } void * buf = __qmljs_create_exception_handler(ctx); @@ -297,10 +297,10 @@ int main(int argc, char *argv[]) QQmlJS::VM::Context *ctx = vm.rootContext; QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); - globalObject->setProperty(ctx, vm.identifier(QStringLiteral("print")), + globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); - globalObject->setProperty(ctx, vm.identifier(QStringLiteral("eval")), + globalObject->__put__(ctx, vm.identifier(QStringLiteral("eval")), QQmlJS::VM::Value::fromObject(new builtins::Eval(ctx))); foreach (const QString &fn, args) { diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index bc8aa9f103..ecf97c416d 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -58,82 +58,61 @@ Object::~Object() delete members; } -void Object::setProperty(Context *ctx, const QString &name, const Value &value) +void Object::__put__(Context *ctx, const QString &name, const Value &value) { - setProperty(ctx, ctx->engine->identifier(name), value); + __put__(ctx, ctx->engine->identifier(name), value); } -void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context *), int count) +void Object::__put__(Context *ctx, const QString &name, void (*code)(Context *), int count) { Q_UNUSED(count); - setProperty(ctx, name, Value::fromObject(ctx->engine->newNativeFunction(ctx, code))); -} - -Value Object::getProperty(Context *ctx, String *name) -{ - if (name->isEqualTo(ctx->engine->id___proto__)) - return Value::fromObject(prototype); - - PropertyDescriptor tmp; - if (PropertyDescriptor *p = getPropertyDescriptor(ctx, name, &tmp)) { - if (p->isData()) - return p->value; - if (!p->get) - return Value::undefinedValue(); - FunctionObject *f = p->get->asFunctionObject(); - if (f) { - f->call(ctx); - return ctx->result; - } - } - return Value::undefinedValue(); + __put__(ctx, name, Value::fromObject(ctx->engine->newNativeFunction(ctx, code))); } // Section 8.12.1 -PropertyDescriptor *Object::getOwnProperty(Context *, String *name) +PropertyDescriptor *Object::__getOwnProperty__(Context *, String *name) { if (members) return members->find(name); return 0; } -PropertyDescriptor *Object::getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill) +// Section 8.12.2 +PropertyDescriptor *Object::__getPropertyDescriptor__(Context *ctx, String *name, PropertyDescriptor *to_fill) { - if (PropertyDescriptor *p = getOwnProperty(ctx, name)) + if (PropertyDescriptor *p = __getOwnProperty__(ctx, name)) return p; if (prototype) - return prototype->getPropertyDescriptor(ctx, name, to_fill); + return prototype->__getPropertyDescriptor__(ctx, name, to_fill); return 0; } -// Section 8.12.5 -void Object::setProperty(Context *ctx, String *name, const Value &value, bool throwException) +// Section 8.12.3 +Value Object::__get__(Context *ctx, String *name) { - if (!canSetProperty(ctx, name)) { - if (throwException) - __qmljs_throw_type_error(ctx); - return; - } - - if (!members) - members = new PropertyTable(); + if (name->isEqualTo(ctx->engine->id___proto__)) + return Value::fromObject(prototype); - PropertyDescriptor *pd = getOwnProperty(ctx, name); - if (pd) { - if (pd->isData()) { - pd->value = value; - return; + PropertyDescriptor tmp; + if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name, &tmp)) { + if (p->isData()) + return p->value; + if (!p->get) + return Value::undefinedValue(); + FunctionObject *f = p->get->asFunctionObject(); + if (f) { + f->call(ctx); + return ctx->result; } } - PropertyDescriptor *p = members->insert(name); - *p = PropertyDescriptor::fromValue(value); + return Value::undefinedValue(); } // Section 8.12.4 -bool Object::canSetProperty(Context *ctx, String *name) +bool Object::__canPut__(Context *ctx, String *name) { - if (PropertyDescriptor *p = getOwnProperty(ctx, name)) { + if (PropertyDescriptor *p = __getOwnProperty__(ctx, name)) { if (p->isAccessor()) return p->get != 0; return p->isWritable(); @@ -143,7 +122,7 @@ bool Object::canSetProperty(Context *ctx, String *name) return extensible; PropertyDescriptor tmp; - if (PropertyDescriptor *p = prototype->getPropertyDescriptor(ctx, name, &tmp)) { + if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, name, &tmp)) { if (p->isAccessor()) return p->get != 0; if (!extensible) @@ -155,37 +134,164 @@ bool Object::canSetProperty(Context *ctx, String *name) return true; } -bool Object::hasProperty(Context *ctx, String *name) const +// Section 8.12.5 +void Object::__put__(Context *ctx, String *name, const Value &value, bool throwException) { - if (members) - return members->find(name) != 0; + // clause 1 + if (!__canPut__(ctx, name)) + goto reject; - return prototype ? prototype->hasProperty(ctx, name) : false; + if (!members) + members = new PropertyTable(); + + { + // Clause 2 + PropertyDescriptor *pd = __getOwnProperty__(ctx, name); + // Clause 3 + if (pd && pd->isData()) { + // spec says to call [[DefineOwnProperty]] with { [[Value]]: value } + + // ### to simplify and speed up we should expand the relevant parts here (clauses 6,7,9,10,12,13) + PropertyDescriptor desc = PropertyDescriptor::fromValue(value); + desc.configurable = PropertyDescriptor::Undefined; + desc.enumberable = PropertyDescriptor::Undefined; + desc.writable = PropertyDescriptor::Undefined; + __defineOwnProperty__(ctx, name, &desc, throwException); + return; + } + + // clause 4 + PropertyDescriptor tmp; + if (prototype) + pd = prototype->__getPropertyDescriptor__(ctx, name, &tmp); + + // Clause 5 + if (pd && pd->isAccessor()) { + assert(pd->set != 0); + FunctionObject *func = pd->set->asFunctionObject(); + assert(func); + + // ### unify with callFunction method + Context k; + Context *c = func->needsActivation ? ctx->engine->newContext() : &k; + Value that = Value::fromObject(this); + Value args[1]; + args[0] = value; + c->initCallContext(ctx, &that, func, args, 1); + func->call(c); + c->leaveCallContext(); + return; + } + + PropertyDescriptor *p = members->insert(name); + *p = PropertyDescriptor::fromValue(value); + } + + reject: + if (throwException) + __qmljs_throw_type_error(ctx); } -bool Object::deleteProperty(Context *, String *name, bool flag) +// Section 8.12.6 +bool Object::__hasProperty__(Context *ctx, String *name) const { - Q_UNUSED(flag); - if (members) - return members->remove(name); + return members->find(name) != 0; - return false; + return prototype ? prototype->__hasProperty__(ctx, name) : false; } -bool Object::defineOwnProperty(Context *ctx, String *name, const Value &getter, const Value &setter, bool flag) +// Section 8.12.7 +bool Object::__delete__(Context *ctx, String *name, bool throwException) +{ + if (members) { + if (PropertyTableEntry *entry = members->findEntry(name)) { + if (entry->descriptor.isConfigurable()) { + members->remove(entry); + return true; + } + if (throwException) + __qmljs_throw_type_error(ctx); + return false; + } + } + return true; +} + +// Section 8.12.9 +bool Object::__defineOwnProperty__(Context *ctx, String *name, PropertyDescriptor *desc, bool throwException) { if (!members) members = new PropertyTable(); - PropertyDescriptor *p = getOwnProperty(ctx, name); - if (!p) { + // Clause 1 + PropertyDescriptor *current = __getOwnProperty__(ctx, name); + if (!current) { + // clause 3 if (!extensible) goto reject; + // clause 4 + *current = *desc; + current->fullyPopulated(); + return true; + } + + // clause 5 + if (desc->isEmpty()) + return true; + + // clause 6 + if (desc->isSubset(current)) + return true; + + // clause 7 + if (!current->isConfigurable()) { + if (desc->isConfigurable()) + goto reject; + if (desc->enumberable != PropertyDescriptor::Unset && desc->enumberable != current->enumberable) + goto reject; } + // clause 8 + if (desc->isGeneric()) + goto accept; + + // clause 9 + if (current->isData() != desc->isData()) { + // 9a + if (!current->isConfigurable()) + goto reject; + if (current->isData()) { + // 9b + current->type = PropertyDescriptor::Accessor; + current->writable = PropertyDescriptor::Undefined; + current->get = 0; + current->set = 0; + } else { + // 9c + current->type = PropertyDescriptor::Data; + current->writable = PropertyDescriptor::Unset; + current->value = Value::undefinedValue(); + } + } else if (current->isData() && desc->isData()) { // clause 10 + if (!current->isConfigurable() && !current->isWritable()) { + if (desc->isWritable() || !current->value.sameValue(desc->value)) + goto reject; + } + } else { // clause 10 + assert(current->isAccessor() && desc->isAccessor()); + if (!current->isConfigurable()) { + if (current->get != desc->get || current->set != desc->set) + goto reject; + } + } + + accept: + + *current += *desc; + return true; reject: - if (flag) + if (throwException) __qmljs_throw_type_error(ctx); return false; } @@ -211,11 +317,11 @@ String *ForEachIteratorObject::nextPropertyName() } } -Value ArrayObject::getProperty(Context *ctx, String *name) +Value ArrayObject::__get__(Context *ctx, String *name) { if (name->isEqualTo(ctx->engine->id_length)) return Value::fromDouble(value.size()); - return Object::getProperty(ctx, name); + return Object::__get__(ctx, name); } bool FunctionObject::hasInstance(Context *ctx, const Value &value) @@ -225,7 +331,7 @@ bool FunctionObject::hasInstance(Context *ctx, const Value &value) return false; } - Value o = getProperty(ctx, ctx->engine->id_prototype); + Value o = __get__(ctx, ctx->engine->id_prototype); if (! o.isObject()) { ctx->throwTypeError(); return false; @@ -290,7 +396,7 @@ void ScriptFunction::call(VM::Context *ctx) function->code(ctx, function->codeData); } -Value RegExpObject::getProperty(Context *ctx, String *name) +Value RegExpObject::__get__(Context *ctx, String *name) { QString n = name->toQString(); if (n == QLatin1String("source")) @@ -303,21 +409,21 @@ Value RegExpObject::getProperty(Context *ctx, String *name) return Value::fromBoolean(value.patternOptions() & QRegularExpression::MultilineOption); else if (n == QLatin1String("lastIndex")) return lastIndex; - return Object::getProperty(ctx, name); + return Object::__get__(ctx, name); } void ScriptFunction::construct(VM::Context *ctx) { Object *obj = ctx->engine->newObject(); - Value proto = getProperty(ctx, ctx->engine->id_prototype); + Value proto = __get__(ctx, ctx->engine->id_prototype); if (proto.isObject()) obj->prototype = proto.objectValue(); ctx->thisObject = Value::fromObject(obj); function->code(ctx, function->codeData); } -PropertyDescriptor *ActivationObject::getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill) +PropertyDescriptor *ActivationObject::__getPropertyDescriptor__(Context *ctx, String *name, PropertyDescriptor *to_fill) { if (context) { for (unsigned int i = 0; i < context->varCount; ++i) { @@ -347,17 +453,17 @@ PropertyDescriptor *ActivationObject::getPropertyDescriptor(Context *ctx, String } } - return Object::getPropertyDescriptor(ctx, name, to_fill); + return Object::__getPropertyDescriptor__(ctx, name, to_fill); } -Value ArgumentsObject::getProperty(Context *ctx, String *name) +Value ArgumentsObject::__get__(Context *ctx, String *name) { if (name->isEqualTo(ctx->engine->id_length)) return Value::fromDouble(context->argumentCount); - return Object::getProperty(ctx, name); + return Object::__get__(ctx, name); } -PropertyDescriptor *ArgumentsObject::getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill) +PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(Context *ctx, String *name, PropertyDescriptor *to_fill) { if (context) { const quint32 i = Value::fromString(name).toUInt32(ctx); @@ -367,7 +473,7 @@ PropertyDescriptor *ArgumentsObject::getPropertyDescriptor(Context *ctx, String } } - return Object::getPropertyDescriptor(ctx, name, to_fill); + return Object::__getPropertyDescriptor__(ctx, name, to_fill); } ExecutionEngine::ExecutionEngine() @@ -431,15 +537,15 @@ ExecutionEngine::ExecutionEngine() globalObject = Value::fromObject(glo); rootContext->activation = Value::fromObject(glo); - glo->setProperty(rootContext, identifier(QStringLiteral("Object")), objectCtor); - glo->setProperty(rootContext, identifier(QStringLiteral("String")), stringCtor); - glo->setProperty(rootContext, identifier(QStringLiteral("Number")), numberCtor); - glo->setProperty(rootContext, identifier(QStringLiteral("Boolean")), booleanCtor); - glo->setProperty(rootContext, identifier(QStringLiteral("Array")), arrayCtor); - glo->setProperty(rootContext, identifier(QStringLiteral("Function")), functionCtor); - glo->setProperty(rootContext, identifier(QStringLiteral("Date")), dateCtor); - glo->setProperty(rootContext, identifier(QStringLiteral("RegExp")), regExpCtor); - glo->setProperty(rootContext, identifier(QStringLiteral("Math")), Value::fromObject(newMathObject(rootContext))); + glo->__put__(rootContext, identifier(QStringLiteral("Object")), objectCtor); + glo->__put__(rootContext, identifier(QStringLiteral("String")), stringCtor); + glo->__put__(rootContext, identifier(QStringLiteral("Number")), numberCtor); + glo->__put__(rootContext, identifier(QStringLiteral("Boolean")), booleanCtor); + glo->__put__(rootContext, identifier(QStringLiteral("Array")), arrayCtor); + glo->__put__(rootContext, identifier(QStringLiteral("Function")), functionCtor); + glo->__put__(rootContext, identifier(QStringLiteral("Date")), dateCtor); + glo->__put__(rootContext, identifier(QStringLiteral("RegExp")), regExpCtor); + glo->__put__(rootContext, identifier(QStringLiteral("Math")), Value::fromObject(newMathObject(rootContext))); } Context *ExecutionEngine::newContext() @@ -466,8 +572,8 @@ FunctionObject *ExecutionEngine::newScriptFunction(Context *scope, IR::Function { ScriptFunction *f = new ScriptFunction(scope, function); Object *proto = scope->engine->newObject(); - proto->setProperty(scope, scope->engine->id_constructor, Value::fromObject(f)); - f->setProperty(scope, scope->engine->id_prototype, Value::fromObject(proto)); + proto->__put__(scope, scope->engine->id_constructor, Value::fromObject(f)); + f->__put__(scope, scope->engine->id_prototype, Value::fromObject(proto)); f->prototype = scope->engine->functionPrototype; return f; } diff --git a/qmljs_objects.h b/qmljs_objects.h index 425cbd53ff..07605916fb 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -154,6 +154,24 @@ struct PropertyDescriptor { return pd; } + // Section 8.10 + inline void fullyPopulated() { + if (type == Generic) { + type = Data; + value = Value::undefinedValue(); + } + if (type == Data) { + if (writable == Undefined) + writable = Unset; + } else { + writable = Undefined; + } + if (enumberable == Undefined) + enumberable = Unset; + if (configurable == Undefined) + configurable = Unset; + } + inline bool isData() const { return type == Data; } inline bool isAccessor() const { return type == Accessor; } inline bool isGeneric() const { return type == Generic; } @@ -161,6 +179,40 @@ struct PropertyDescriptor { inline bool isWritable() const { return writable == Set; } inline bool isEnumerable() const { return enumberable == Set; } inline bool isConfigurable() const { return configurable == Set; } + + inline bool isEmpty() { + return type == Generic && writable == Undefined && enumberable == Undefined && configurable == Undefined; + } + inline bool isSubset(PropertyDescriptor *other) { + if (type != other->type) + return false; + if (enumberable != Undefined && enumberable != other->enumberable) + return false; + if (configurable != Undefined && configurable != other->configurable) + return false; + if (writable != Undefined && writable != other->writable) + return false; + if (type == Data && !value.sameValue(other->value)) + return false; + if (type == Accessor && (get != other->get || set != other->set)) + return false; + return true; + } + inline void operator+=(const PropertyDescriptor &other) { + type = other.type; + if (other.enumberable != Undefined) + enumberable = other.enumberable; + if (other.configurable != Undefined) + configurable = other.configurable; + if (other.writable != Undefined) + writable = other.writable; + if (type == Accessor) { + get = other.get; + set = other.set; + } else { + value = other.value; + } + } }; struct PropertyTableEntry { @@ -205,29 +257,23 @@ public: inline iterator begin() const { return _properties; } inline iterator end() const { return _properties + (_propertyCount + 1); } - bool remove(String *name) + void remove(PropertyTableEntry *prop) { - if (PropertyTableEntry *prop = findEntry(name)) { - // ### TODO check if the property can be removed - - PropertyTableEntry *bucket = _buckets[prop->hashValue() % _bucketCount]; - if (bucket == prop) { - bucket = bucket->next; - } else { - for (PropertyTableEntry *it = bucket; it; it = it->next) { - if (it->next == prop) { - it->next = it->next->next; - break; - } + PropertyTableEntry *bucket = _buckets[prop->hashValue() % _bucketCount]; + if (bucket == prop) { + bucket = bucket->next; + } else { + for (PropertyTableEntry *it = bucket; it; it = it->next) { + if (it->next == prop) { + it->next = it->next->next; + break; } } - - _properties[prop->index] = 0; - prop->next = _freeList; - _freeList = prop; } - return true; + _properties[prop->index] = 0; + prop->next = _freeList; + _freeList = prop; } PropertyTableEntry *findEntry(String *name) const @@ -349,20 +395,20 @@ struct Object { virtual ActivationObject *asActivationObject() { return 0; } virtual ArgumentsObject *asArgumentsObject() { return 0; } - virtual Value getProperty(Context *ctx, String *name); - virtual PropertyDescriptor *getOwnProperty(Context *ctx, String *name); - virtual PropertyDescriptor *getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill); - virtual void setProperty(Context *ctx, String *name, const Value &value, bool throwException = false); - virtual bool canSetProperty(Context *ctx, String *name); - virtual bool hasProperty(Context *ctx, String *name) const; - virtual bool deleteProperty(Context *ctx, String *name, bool flag); - virtual bool defineOwnProperty(Context *ctx, String *name, const Value &getter, const Value &setter, bool flag = false); + virtual Value __get__(Context *ctx, String *name); + virtual PropertyDescriptor *__getOwnProperty__(Context *ctx, String *name); + virtual PropertyDescriptor *__getPropertyDescriptor__(Context *ctx, String *name, PropertyDescriptor *to_fill); + virtual void __put__(Context *ctx, String *name, const Value &value, bool throwException = false); + virtual bool __canPut__(Context *ctx, String *name); + virtual bool __hasProperty__(Context *ctx, String *name) const; + virtual bool __delete__(Context *ctx, String *name, bool throwException); + virtual bool __defineOwnProperty__(Context *ctx, String *name, PropertyDescriptor *desc, bool throwException = false); // // helpers // - void setProperty(Context *ctx, const QString &name, const Value &value); - void setProperty(Context *ctx, const QString &name, void (*code)(Context *), int count = 0); + void __put__(Context *ctx, const QString &name, const Value &value); + void __put__(Context *ctx, const QString &name, void (*code)(Context *), int count = 0); }; struct ForEachIteratorObject: Object { @@ -409,7 +455,7 @@ struct ArrayObject: Object { ArrayObject(const Array &value): value(value) {} virtual QString className() { return QStringLiteral("Array"); } virtual ArrayObject *asArrayObject() { return this; } - virtual Value getProperty(Context *ctx, String *name); + virtual Value __get__(Context *ctx, String *name); }; struct FunctionObject: Object { @@ -462,7 +508,7 @@ struct RegExpObject: Object { RegExpObject(const QRegularExpression &value, bool global): value(value), lastIndex(Value::fromInt32(0)), global(global) {} virtual QString className() { return QStringLiteral("RegExp"); } virtual RegExpObject *asRegExpObject() { return this; } - virtual Value getProperty(Context *ctx, String *name); + virtual Value __get__(Context *ctx, String *name); }; struct ErrorObject: Object { @@ -478,7 +524,7 @@ struct ActivationObject: Object { ActivationObject(Context *context): context(context), arguments(Value::undefinedValue()) {} virtual QString className() { return QStringLiteral("Activation"); } virtual ActivationObject *asActivationObject() { return this; } - virtual PropertyDescriptor *getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill); + virtual PropertyDescriptor *__getPropertyDescriptor__(Context *ctx, String *name, PropertyDescriptor *to_fill); }; struct ArgumentsObject: Object { @@ -486,8 +532,8 @@ struct ArgumentsObject: Object { ArgumentsObject(Context *context): context(context) {} virtual QString className() { return QStringLiteral("Arguments"); } virtual ArgumentsObject *asArgumentsObject() { return this; } - virtual Value getProperty(Context *ctx, String *name); - virtual PropertyDescriptor *getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill); + virtual Value __get__(Context *ctx, String *name); + virtual PropertyDescriptor *__getPropertyDescriptor__(Context *ctx, String *name, PropertyDescriptor *to_fill); }; struct ExecutionEngine diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 8acaec375d..5291d45e08 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -232,7 +232,7 @@ ActivationObject *Value::asArgumentsObject() const Value Value::property(Context *ctx, String *name) const { - return isObject() ? objectValue()->getProperty(ctx, name) : undefinedValue(); + return isObject() ? objectValue()->__get__(ctx, name) : undefinedValue(); } void Context::init(ExecutionEngine *eng) @@ -257,7 +257,7 @@ Value *Context::lookupPropertyDescriptor(String *name) for (Context *ctx = this; ctx; ctx = ctx->parent) { if (ctx->activation.isObject()) { PropertyDescriptor tmp; - if (PropertyDescriptor *pd = ctx->activation.objectValue()->getPropertyDescriptor(this, name, &tmp)) { + if (PropertyDescriptor *pd = ctx->activation.objectValue()->__getPropertyDescriptor__(this, name, &tmp)) { return &pd->value; } } @@ -351,7 +351,7 @@ void Context::leaveConstructorContext(FunctionObject *f) assert(thisObject.isObject()); result = thisObject; - Value proto = f->getProperty(this, engine->id_prototype); + Value proto = f->__get__(this, engine->id_prototype); thisObject.objectValue()->prototype = proto.objectValue(); if (! thisObject.isObject()) thisObject.objectValue()->prototype = engine->objectPrototype; @@ -439,7 +439,7 @@ Value __qmljs_delete_subscript(Context *ctx, Value base, Value index) Value __qmljs_delete_member(Context *ctx, Value base, String *name) { Value obj = base.toObject(ctx); - return Value::fromBoolean(obj.objectValue()->deleteProperty(ctx, name, true)); + return Value::fromBoolean(obj.objectValue()->__delete__(ctx, name, true)); } Value __qmljs_delete_property(Context *ctx, String *name) @@ -447,7 +447,7 @@ Value __qmljs_delete_property(Context *ctx, String *name) Value obj = ctx->activation; if (! obj.isObject()) obj = ctx->engine->globalObject; - return Value::fromBoolean(obj.objectValue()->deleteProperty(ctx, name, true)); + return Value::fromBoolean(obj.objectValue()->__delete__(ctx, name, true)); } Value __qmljs_delete_value(Context *ctx, Value value) @@ -487,7 +487,7 @@ Value __qmljs_in(Value left, Value right, Context *ctx) { if (right.isObject()) { Value s = __qmljs_to_string(left, ctx); - bool r = right.objectValue()->hasProperty(ctx, s.stringValue()); + bool r = right.objectValue()->__hasProperty__(ctx, s.stringValue()); return Value::fromBoolean(r); } else { return __qmljs_throw_type_error(ctx); @@ -750,89 +750,89 @@ void __qmljs_inplace_ushr_element(Value base, Value index, Value value, Context void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.objectValue(); - Value prop = o->getProperty(ctx, name); + Value prop = o->__get__(ctx, name); prop = __qmljs_bit_and(prop, value, ctx); - o->setProperty(ctx, name, prop); + o->__put__(ctx, name, prop); } void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.objectValue(); - Value prop = o->getProperty(ctx, name); + Value prop = o->__get__(ctx, name); prop = __qmljs_bit_or(prop, value, ctx); - o->setProperty(ctx, name, prop); + o->__put__(ctx, name, prop); } void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.objectValue(); - Value prop = o->getProperty(ctx, name); + Value prop = o->__get__(ctx, name); prop = __qmljs_bit_xor(prop, value, ctx); - o->setProperty(ctx, name, prop); + o->__put__(ctx, name, prop); } void __qmljs_inplace_add_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.objectValue(); - Value prop = o->getProperty(ctx, name); + Value prop = o->__get__(ctx, name); prop = __qmljs_add(prop, value, ctx); - o->setProperty(ctx, name, prop); + o->__put__(ctx, name, prop); } void __qmljs_inplace_sub_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.objectValue(); - Value prop = o->getProperty(ctx, name); + Value prop = o->__get__(ctx, name); prop = __qmljs_sub(prop, value, ctx); - o->setProperty(ctx, name, prop); + o->__put__(ctx, name, prop); } void __qmljs_inplace_mul_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.objectValue(); - Value prop = o->getProperty(ctx, name); + Value prop = o->__get__(ctx, name); prop = __qmljs_mul(prop, value, ctx); - o->setProperty(ctx, name, prop); + o->__put__(ctx, name, prop); } void __qmljs_inplace_div_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.objectValue(); - Value prop = o->getProperty(ctx, name); + Value prop = o->__get__(ctx, name); prop = __qmljs_div(prop, value, ctx); - o->setProperty(ctx, name, prop); + o->__put__(ctx, name, prop); } void __qmljs_inplace_mod_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.objectValue(); - Value prop = o->getProperty(ctx, name); + Value prop = o->__get__(ctx, name); prop = __qmljs_mod(prop, value, ctx); - o->setProperty(ctx, name, prop); + o->__put__(ctx, name, prop); } void __qmljs_inplace_shl_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.objectValue(); - Value prop = o->getProperty(ctx, name); + Value prop = o->__get__(ctx, name); prop = __qmljs_shl(prop, value, ctx); - o->setProperty(ctx, name, prop); + o->__put__(ctx, name, prop); } void __qmljs_inplace_shr_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.objectValue(); - Value prop = o->getProperty(ctx, name); + Value prop = o->__get__(ctx, name); prop = __qmljs_shr(prop, value, ctx); - o->setProperty(ctx, name, prop); + o->__put__(ctx, name, prop); } void __qmljs_inplace_ushr_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.objectValue(); - Value prop = o->getProperty(ctx, name); + Value prop = o->__get__(ctx, name); prop = __qmljs_ushr(prop, value, ctx); - o->setProperty(ctx, name, prop); + o->__put__(ctx, name, prop); } String *__qmljs_string_from_utf8(Context *ctx, const char *s) @@ -903,14 +903,14 @@ Value __qmljs_object_default_value(Context *ctx, Value object, int typeHint) assert(object.isObject()); Object *oo = object.objectValue(); - Value conv = oo->getProperty(ctx, meth1); + Value conv = oo->__get__(ctx, meth1); if (FunctionObject *f = conv.asFunctionObject()) { Value r = callFunction(ctx, object, f, 0, 0); if (r.isPrimitive()) return r; } - conv = oo->getProperty(ctx, meth2); + conv = oo->__get__(ctx, meth2); if (FunctionObject *f = conv.asFunctionObject()) { Value r = callFunction(ctx, object, f, 0, 0); if (r.isPrimitive()) @@ -951,33 +951,33 @@ Value __qmljs_new_string_object(Context *ctx, String *string) void __qmljs_set_property(Context *ctx, Value object, String *name, Value value) { - object.objectValue()->setProperty(ctx, name, value, /*flags*/ 0); + object.objectValue()->__put__(ctx, name, value, /*flags*/ 0); } void __qmljs_set_property_boolean(Context *ctx, Value *object, String *name, bool b) { Value value = Value::fromBoolean(b); - object->objectValue()->setProperty(ctx, name, value, /*flag*/ 0); + object->objectValue()->__put__(ctx, name, value, /*flag*/ 0); } void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double number) { Q_UNUSED(ctx); Value value = Value::fromDouble(number); - object->objectValue()->setProperty(ctx, name, value, /*flag*/ 0); + object->objectValue()->__put__(ctx, name, value, /*flag*/ 0); } void __qmljs_set_property_string(Context *ctx, Value *object, String *name, String *s) { Q_UNUSED(ctx); Value value = Value::fromString(s); - object->objectValue()->setProperty(ctx, name, value, /*flag*/ 0); + object->objectValue()->__put__(ctx, name, value, /*flag*/ 0); } void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR::Function *function) { Value value = __qmljs_init_closure(function, ctx); - object->objectValue()->setProperty(ctx, name, value, /*flag*/ 0); + object->objectValue()->__put__(ctx, name, value, /*flag*/ 0); } Value __qmljs_get_element(Context *ctx, Value object, Value index) @@ -1000,7 +1000,7 @@ Value __qmljs_get_element(Context *ctx, Value object, Value index) if (! object.isObject()) object = __qmljs_to_object(object, ctx); - return object.objectValue()->getProperty(ctx, name); + return object.objectValue()->__get__(ctx, name); } void __qmljs_set_element(Context *ctx, Value object, Value index, Value value) @@ -1017,7 +1017,7 @@ void __qmljs_set_element(Context *ctx, Value object, Value index, Value value) if (! object.isObject()) object = __qmljs_to_object(object, ctx); - object.objectValue()->setProperty(ctx, name, value, /*flags*/ 0); + object.objectValue()->__put__(ctx, name, value, /*flags*/ 0); } Value __qmljs_foreach_iterator_object(Value in, Context *ctx) @@ -1046,7 +1046,7 @@ void __qmljs_set_activation_property(Context *ctx, String *name, Value value) if (Value *prop = ctx->lookupPropertyDescriptor(name)) *prop = value; else - ctx->engine->globalObject.objectValue()->setProperty(ctx, name, value); + ctx->engine->globalObject.objectValue()->__put__(ctx, name, value); } void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool b) @@ -1076,14 +1076,14 @@ void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Fun Value __qmljs_get_property(Context *ctx, Value object, String *name) { if (object.isObject()) { - return object.objectValue()->getProperty(ctx, name); + return object.objectValue()->__get__(ctx, name); } else if (object.isString() && name->isEqualTo(ctx->engine->id_length)) { return Value::fromInt32(object.stringValue()->toQString().length()); } else { object = __qmljs_to_object(object, ctx); if (object.isObject()) { - return object.objectValue()->getProperty(ctx, name); + return object.objectValue()->__get__(ctx, name); } else { ctx->throwTypeError(); return Value::undefinedValue(); @@ -1225,7 +1225,7 @@ Value __qmljs_construct_property(Context *context, Value base, String *name, Val if (!thisObject.isObject()) thisObject = __qmljs_to_object(base, context); - Value func = thisObject.objectValue()->getProperty(context, name); + Value func = thisObject.objectValue()->__get__(context, name); if (FunctionObject *f = func.asFunctionObject()) { Context k; Context *ctx = f->needsActivation ? context->engine->newContext() : &k; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 1e75ef9153..3427d56ed7 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -530,37 +530,37 @@ void ObjectCtor::call(Context *ctx) ctx->result = Value::fromObject(ctx->engine->newObject()); } -Value ObjectCtor::getProperty(Context *ctx, String *name) +Value ObjectCtor::__get__(Context *ctx, String *name) { if (name == ctx->engine->id_length) return Value::fromDouble(1); - return Object::getProperty(ctx, name); + return Object::__get__(ctx, name); } void ObjectPrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->setProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 0); - ctor.objectValue()->setProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 0); - ctor.objectValue()->setProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 0); - ctor.objectValue()->setProperty(ctx, QStringLiteral("create"), method_create, 0); - ctor.objectValue()->setProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 0); - ctor.objectValue()->setProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 0); - ctor.objectValue()->setProperty(ctx, QStringLiteral("seal"), method_seal, 0); - ctor.objectValue()->setProperty(ctx, QStringLiteral("freeze"), method_freeze, 0); - ctor.objectValue()->setProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 0); - ctor.objectValue()->setProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 0); - ctor.objectValue()->setProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 0); - ctor.objectValue()->setProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 0); - ctor.objectValue()->setProperty(ctx, QStringLiteral("keys"), method_keys, 0); - - setProperty(ctx, QStringLiteral("constructor"), ctor); - setProperty(ctx, QStringLiteral("toString"), method_toString, 0); - setProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); - setProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); - setProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 0); - setProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 0); - setProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 0); + ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->__put__(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 0); + ctor.objectValue()->__put__(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 0); + ctor.objectValue()->__put__(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 0); + ctor.objectValue()->__put__(ctx, QStringLiteral("create"), method_create, 0); + ctor.objectValue()->__put__(ctx, QStringLiteral("defineProperty"), method_defineProperty, 0); + ctor.objectValue()->__put__(ctx, QStringLiteral("defineProperties"), method_defineProperties, 0); + ctor.objectValue()->__put__(ctx, QStringLiteral("seal"), method_seal, 0); + ctor.objectValue()->__put__(ctx, QStringLiteral("freeze"), method_freeze, 0); + ctor.objectValue()->__put__(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 0); + ctor.objectValue()->__put__(ctx, QStringLiteral("isSealed"), method_isSealed, 0); + ctor.objectValue()->__put__(ctx, QStringLiteral("isFrozen"), method_isFrozen, 0); + ctor.objectValue()->__put__(ctx, QStringLiteral("isExtensible"), method_isExtensible, 0); + ctor.objectValue()->__put__(ctx, QStringLiteral("keys"), method_keys, 0); + + __put__(ctx, QStringLiteral("constructor"), ctor); + __put__(ctx, QStringLiteral("toString"), method_toString, 0); + __put__(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + __put__(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + __put__(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 0); + __put__(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 0); + __put__(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 0); } void ObjectPrototype::method_getPrototypeOf(Context *ctx) @@ -670,7 +670,7 @@ void ObjectPrototype::method_hasOwnProperty(Context *ctx) { String *P = ctx->argument(0).toString(ctx); Value O = ctx->thisObject.toObject(ctx); - bool r = O.objectValue()->getOwnProperty(ctx, P) != 0; + bool r = O.objectValue()->__getOwnProperty__(ctx, P) != 0; ctx->result = Value::fromBoolean(r); } @@ -720,29 +720,29 @@ void StringCtor::call(Context *ctx) void StringPrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->setProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode); - - setProperty(ctx, QStringLiteral("constructor"), ctor); - setProperty(ctx, QStringLiteral("toString"), method_toString); - setProperty(ctx, QStringLiteral("valueOf"), method_valueOf); - setProperty(ctx, QStringLiteral("charAt"), method_charAt); - setProperty(ctx, QStringLiteral("charCodeAt"), method_charCodeAt); - setProperty(ctx, QStringLiteral("concat"), method_concat); - setProperty(ctx, QStringLiteral("indexOf"), method_indexOf); - setProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf); - setProperty(ctx, QStringLiteral("localeCompare"), method_localeCompare); - setProperty(ctx, QStringLiteral("match"), method_match); - setProperty(ctx, QStringLiteral("replace"), method_replace); - setProperty(ctx, QStringLiteral("search"), method_search); - setProperty(ctx, QStringLiteral("slice"), method_slice); - setProperty(ctx, QStringLiteral("split"), method_split); - setProperty(ctx, QStringLiteral("substr"), method_substr); - setProperty(ctx, QStringLiteral("substring"), method_substring); - setProperty(ctx, QStringLiteral("toLowerCase"), method_toLowerCase); - setProperty(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); - setProperty(ctx, QStringLiteral("toUpperCase"), method_toUpperCase); - setProperty(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); + ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->__put__(ctx, QStringLiteral("fromCharCode"), method_fromCharCode); + + __put__(ctx, QStringLiteral("constructor"), ctor); + __put__(ctx, QStringLiteral("toString"), method_toString); + __put__(ctx, QStringLiteral("valueOf"), method_valueOf); + __put__(ctx, QStringLiteral("charAt"), method_charAt); + __put__(ctx, QStringLiteral("charCodeAt"), method_charCodeAt); + __put__(ctx, QStringLiteral("concat"), method_concat); + __put__(ctx, QStringLiteral("indexOf"), method_indexOf); + __put__(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf); + __put__(ctx, QStringLiteral("localeCompare"), method_localeCompare); + __put__(ctx, QStringLiteral("match"), method_match); + __put__(ctx, QStringLiteral("replace"), method_replace); + __put__(ctx, QStringLiteral("search"), method_search); + __put__(ctx, QStringLiteral("slice"), method_slice); + __put__(ctx, QStringLiteral("split"), method_split); + __put__(ctx, QStringLiteral("substr"), method_substr); + __put__(ctx, QStringLiteral("substring"), method_substring); + __put__(ctx, QStringLiteral("toLowerCase"), method_toLowerCase); + __put__(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); + __put__(ctx, QStringLiteral("toUpperCase"), method_toUpperCase); + __put__(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); } QString StringPrototype::getThisString(Context *ctx) @@ -1027,27 +1027,27 @@ void NumberCtor::call(Context *ctx) void NumberPrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->setProperty(ctx, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); - ctor.objectValue()->setProperty(ctx, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); - ctor.objectValue()->setProperty(ctx, QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf())); - ctor.objectValue()->setProperty(ctx, QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308)); + ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->__put__(ctx, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); + ctor.objectValue()->__put__(ctx, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); + ctor.objectValue()->__put__(ctx, QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf())); + ctor.objectValue()->__put__(ctx, QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308)); #ifdef __INTEL_COMPILER # pragma warning( push ) # pragma warning(disable: 239) #endif - ctor.objectValue()->setProperty(ctx, QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324)); + ctor.objectValue()->__put__(ctx, QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324)); #ifdef __INTEL_COMPILER # pragma warning( pop ) #endif - setProperty(ctx, QStringLiteral("constructor"), ctor); - setProperty(ctx, QStringLiteral("toString"), method_toString); - setProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString); - setProperty(ctx, QStringLiteral("valueOf"), method_valueOf); - setProperty(ctx, QStringLiteral("toFixed"), method_toFixed); - setProperty(ctx, QStringLiteral("toExponential"), method_toExponential); - setProperty(ctx, QStringLiteral("toPrecision"), method_toPrecision); + __put__(ctx, QStringLiteral("constructor"), ctor); + __put__(ctx, QStringLiteral("toString"), method_toString); + __put__(ctx, QStringLiteral("toLocalString"), method_toLocaleString); + __put__(ctx, QStringLiteral("valueOf"), method_valueOf); + __put__(ctx, QStringLiteral("toFixed"), method_toFixed); + __put__(ctx, QStringLiteral("toExponential"), method_toExponential); + __put__(ctx, QStringLiteral("toPrecision"), method_toPrecision); } void NumberPrototype::method_toString(Context *ctx) @@ -1206,10 +1206,10 @@ void BooleanCtor::call(Context *ctx) void BooleanPrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - setProperty(ctx, QStringLiteral("constructor"), ctor); - setProperty(ctx, QStringLiteral("toString"), method_toString); - setProperty(ctx, QStringLiteral("valueOf"), method_valueOf); + ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + __put__(ctx, QStringLiteral("constructor"), ctor); + __put__(ctx, QStringLiteral("toString"), method_toString); + __put__(ctx, QStringLiteral("valueOf"), method_valueOf); } void BooleanPrototype::method_toString(Context *ctx) @@ -1268,29 +1268,29 @@ void ArrayCtor::call(Context *ctx) void ArrayPrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - setProperty(ctx, QStringLiteral("constructor"), ctor); - setProperty(ctx, QStringLiteral("toString"), method_toString, 0); - setProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString, 0); - setProperty(ctx, QStringLiteral("concat"), method_concat, 1); - setProperty(ctx, QStringLiteral("join"), method_join, 1); - setProperty(ctx, QStringLiteral("pop"), method_pop, 0); - setProperty(ctx, QStringLiteral("push"), method_push, 1); - setProperty(ctx, QStringLiteral("reverse"), method_reverse, 0); - setProperty(ctx, QStringLiteral("shift"), method_shift, 0); - setProperty(ctx, QStringLiteral("slice"), method_slice, 2); - setProperty(ctx, QStringLiteral("sort"), method_sort, 1); - setProperty(ctx, QStringLiteral("splice"), method_splice, 2); - setProperty(ctx, QStringLiteral("unshift"), method_unshift, 1); - setProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 0); - setProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 0); - setProperty(ctx, QStringLiteral("every"), method_every, 0); - setProperty(ctx, QStringLiteral("some"), method_some, 0); - setProperty(ctx, QStringLiteral("forEach"), method_forEach, 0); - setProperty(ctx, QStringLiteral("map"), method_map, 0); - setProperty(ctx, QStringLiteral("filter"), method_filter, 0); - setProperty(ctx, QStringLiteral("reduce"), method_reduce, 0); - setProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 0); + ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + __put__(ctx, QStringLiteral("constructor"), ctor); + __put__(ctx, QStringLiteral("toString"), method_toString, 0); + __put__(ctx, QStringLiteral("toLocalString"), method_toLocaleString, 0); + __put__(ctx, QStringLiteral("concat"), method_concat, 1); + __put__(ctx, QStringLiteral("join"), method_join, 1); + __put__(ctx, QStringLiteral("pop"), method_pop, 0); + __put__(ctx, QStringLiteral("push"), method_push, 1); + __put__(ctx, QStringLiteral("reverse"), method_reverse, 0); + __put__(ctx, QStringLiteral("shift"), method_shift, 0); + __put__(ctx, QStringLiteral("slice"), method_slice, 2); + __put__(ctx, QStringLiteral("sort"), method_sort, 1); + __put__(ctx, QStringLiteral("splice"), method_splice, 2); + __put__(ctx, QStringLiteral("unshift"), method_unshift, 1); + __put__(ctx, QStringLiteral("indexOf"), method_indexOf, 0); + __put__(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 0); + __put__(ctx, QStringLiteral("every"), method_every, 0); + __put__(ctx, QStringLiteral("some"), method_some, 0); + __put__(ctx, QStringLiteral("forEach"), method_forEach, 0); + __put__(ctx, QStringLiteral("map"), method_map, 0); + __put__(ctx, QStringLiteral("filter"), method_filter, 0); + __put__(ctx, QStringLiteral("reduce"), method_reduce, 0); + __put__(ctx, QStringLiteral("reduceRight"), method_reduceRight, 0); } void ArrayPrototype::method_toString(Context *ctx) @@ -1396,12 +1396,12 @@ void ArrayPrototype::method_pop(Context *ctx) Value r1 = self.property(ctx, ctx->engine->id_length); quint32 r2 = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; if (! r2) { - self.objectValue()->setProperty(ctx, ctx->engine->id_length, Value::fromDouble(0)); + self.objectValue()->__put__(ctx, ctx->engine->id_length, Value::fromDouble(0)); } else { String *r6 = Value::fromDouble(r2 - 1).toString(ctx); Value r7 = self.property(ctx, r6); - self.objectValue()->deleteProperty(ctx, r6, 0); - self.objectValue()->setProperty(ctx, ctx->engine->id_length, Value::fromDouble(2 - 1)); + self.objectValue()->__delete__(ctx, r6, 0); + self.objectValue()->__put__(ctx, ctx->engine->id_length, Value::fromDouble(2 - 1)); ctx->result = r7; } } @@ -1423,10 +1423,10 @@ void ArrayPrototype::method_push(Context *ctx) for (unsigned int index = 0; index < ctx->argumentCount; ++index, ++n) { Value r3 = ctx->argument(index); String *name = Value::fromDouble(n).toString(ctx); - self.objectValue()->setProperty(ctx, name, r3); + self.objectValue()->__put__(ctx, name, r3); } Value r = Value::fromDouble(n); - self.objectValue()->setProperty(ctx, ctx->engine->id_length, r); + self.objectValue()->__put__(ctx, ctx->engine->id_length, r); ctx->result = r; } } @@ -1738,12 +1738,12 @@ void FunctionCtor::call(Context *ctx) void FunctionPrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - setProperty(ctx, QStringLiteral("constructor"), ctor); - setProperty(ctx, QStringLiteral("toString"), method_toString, 0); - setProperty(ctx, QStringLiteral("apply"), method_apply, 0); - setProperty(ctx, QStringLiteral("call"), method_call, 0); - setProperty(ctx, QStringLiteral("bind"), method_bind, 0); + ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + __put__(ctx, QStringLiteral("constructor"), ctor); + __put__(ctx, QStringLiteral("toString"), method_toString, 0); + __put__(ctx, QStringLiteral("apply"), method_apply, 0); + __put__(ctx, QStringLiteral("call"), method_call, 0); + __put__(ctx, QStringLiteral("bind"), method_bind, 0); } void FunctionPrototype::method_toString(Context *ctx) @@ -1853,57 +1853,57 @@ void DateCtor::call(Context *ctx) void DatePrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); LocalTZA = getLocalTZA(); - ctor.objectValue()->setProperty(ctx, QStringLiteral("parse"), method_parse, 1); - ctor.objectValue()->setProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); - - setProperty(ctx, QStringLiteral("constructor"), ctor); - setProperty(ctx, QStringLiteral("toString"), method_toString, 0); - setProperty(ctx, QStringLiteral("toDateString"), method_toDateString, 0); - setProperty(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0); - setProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); - setProperty(ctx, QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); - setProperty(ctx, QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); - setProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); - setProperty(ctx, QStringLiteral("getTime"), method_getTime, 0); - setProperty(ctx, QStringLiteral("getYear"), method_getYear, 0); - setProperty(ctx, QStringLiteral("getFullYear"), method_getFullYear, 0); - setProperty(ctx, QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0); - setProperty(ctx, QStringLiteral("getMonth"), method_getMonth, 0); - setProperty(ctx, QStringLiteral("getUTCMonth"), method_getUTCMonth, 0); - setProperty(ctx, QStringLiteral("getDate"), method_getDate, 0); - setProperty(ctx, QStringLiteral("getUTCDate"), method_getUTCDate, 0); - setProperty(ctx, QStringLiteral("getDay"), method_getDay, 0); - setProperty(ctx, QStringLiteral("getUTCDay"), method_getUTCDay, 0); - setProperty(ctx, QStringLiteral("getHours"), method_getHours, 0); - setProperty(ctx, QStringLiteral("getUTCHours"), method_getUTCHours, 0); - setProperty(ctx, QStringLiteral("getMinutes"), method_getMinutes, 0); - setProperty(ctx, QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0); - setProperty(ctx, QStringLiteral("getSeconds"), method_getSeconds, 0); - setProperty(ctx, QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0); - setProperty(ctx, QStringLiteral("getMilliseconds"), method_getMilliseconds, 0); - setProperty(ctx, QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0); - setProperty(ctx, QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0); - setProperty(ctx, QStringLiteral("setTime"), method_setTime, 1); - setProperty(ctx, QStringLiteral("setMilliseconds"), method_setMilliseconds, 1); - setProperty(ctx, QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1); - setProperty(ctx, QStringLiteral("setSeconds"), method_setSeconds, 2); - setProperty(ctx, QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2); - setProperty(ctx, QStringLiteral("setMinutes"), method_setMinutes, 3); - setProperty(ctx, QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3); - setProperty(ctx, QStringLiteral("setHours"), method_setHours, 4); - setProperty(ctx, QStringLiteral("setUTCHours"), method_setUTCHours, 4); - setProperty(ctx, QStringLiteral("setDate"), method_setDate, 1); - setProperty(ctx, QStringLiteral("setUTCDate"), method_setUTCDate, 1); - setProperty(ctx, QStringLiteral("setMonth"), method_setMonth, 2); - setProperty(ctx, QStringLiteral("setUTCMonth"), method_setUTCMonth, 2); - setProperty(ctx, QStringLiteral("setYear"), method_setYear, 1); - setProperty(ctx, QStringLiteral("setFullYear"), method_setFullYear, 3); - setProperty(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); - setProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); - setProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); + ctor.objectValue()->__put__(ctx, QStringLiteral("parse"), method_parse, 1); + ctor.objectValue()->__put__(ctx, QStringLiteral("UTC"), method_UTC, 7); + + __put__(ctx, QStringLiteral("constructor"), ctor); + __put__(ctx, QStringLiteral("toString"), method_toString, 0); + __put__(ctx, QStringLiteral("toDateString"), method_toDateString, 0); + __put__(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0); + __put__(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + __put__(ctx, QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); + __put__(ctx, QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); + __put__(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + __put__(ctx, QStringLiteral("getTime"), method_getTime, 0); + __put__(ctx, QStringLiteral("getYear"), method_getYear, 0); + __put__(ctx, QStringLiteral("getFullYear"), method_getFullYear, 0); + __put__(ctx, QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0); + __put__(ctx, QStringLiteral("getMonth"), method_getMonth, 0); + __put__(ctx, QStringLiteral("getUTCMonth"), method_getUTCMonth, 0); + __put__(ctx, QStringLiteral("getDate"), method_getDate, 0); + __put__(ctx, QStringLiteral("getUTCDate"), method_getUTCDate, 0); + __put__(ctx, QStringLiteral("getDay"), method_getDay, 0); + __put__(ctx, QStringLiteral("getUTCDay"), method_getUTCDay, 0); + __put__(ctx, QStringLiteral("getHours"), method_getHours, 0); + __put__(ctx, QStringLiteral("getUTCHours"), method_getUTCHours, 0); + __put__(ctx, QStringLiteral("getMinutes"), method_getMinutes, 0); + __put__(ctx, QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0); + __put__(ctx, QStringLiteral("getSeconds"), method_getSeconds, 0); + __put__(ctx, QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0); + __put__(ctx, QStringLiteral("getMilliseconds"), method_getMilliseconds, 0); + __put__(ctx, QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0); + __put__(ctx, QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0); + __put__(ctx, QStringLiteral("setTime"), method_setTime, 1); + __put__(ctx, QStringLiteral("setMilliseconds"), method_setMilliseconds, 1); + __put__(ctx, QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1); + __put__(ctx, QStringLiteral("setSeconds"), method_setSeconds, 2); + __put__(ctx, QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2); + __put__(ctx, QStringLiteral("setMinutes"), method_setMinutes, 3); + __put__(ctx, QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3); + __put__(ctx, QStringLiteral("setHours"), method_setHours, 4); + __put__(ctx, QStringLiteral("setUTCHours"), method_setUTCHours, 4); + __put__(ctx, QStringLiteral("setDate"), method_setDate, 1); + __put__(ctx, QStringLiteral("setUTCDate"), method_setUTCDate, 1); + __put__(ctx, QStringLiteral("setMonth"), method_setMonth, 2); + __put__(ctx, QStringLiteral("setUTCMonth"), method_setUTCMonth, 2); + __put__(ctx, QStringLiteral("setYear"), method_setYear, 1); + __put__(ctx, QStringLiteral("setFullYear"), method_setFullYear, 3); + __put__(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); + __put__(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); + __put__(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); } double DatePrototype::getThisDate(Context *ctx) @@ -2463,11 +2463,11 @@ void RegExpCtor::call(Context *ctx) void RegExpPrototype::init(Context *ctx, const Value &ctor) { - ctor.objectValue()->setProperty(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - setProperty(ctx, QStringLiteral("constructor"), ctor); - setProperty(ctx, QStringLiteral("exec"), method_exec, 0); - setProperty(ctx, QStringLiteral("test"), method_test, 0); - setProperty(ctx, QStringLiteral("toString"), method_toString, 0); + ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + __put__(ctx, QStringLiteral("constructor"), ctor); + __put__(ctx, QStringLiteral("exec"), method_exec, 0); + __put__(ctx, QStringLiteral("test"), method_test, 0); + __put__(ctx, QStringLiteral("toString"), method_toString, 0); } void RegExpPrototype::method_exec(Context *ctx) @@ -2495,8 +2495,8 @@ void RegExpPrototype::method_exec(Context *ctx) for (int i = 0; i <= captured; ++i) array->value.push(Value::fromString(ctx, match.captured(i))); - array->setProperty(ctx, QLatin1String("index"), Value::fromInt32(match.capturedStart(0))); - array->setProperty(ctx, QLatin1String("input"), arg); + array->__put__(ctx, QLatin1String("index"), Value::fromInt32(match.capturedStart(0))); + array->__put__(ctx, QLatin1String("input"), arg); if (r->global) r->lastIndex = Value::fromInt32(match.capturedEnd(0)); @@ -2535,33 +2535,33 @@ void RegExpPrototype::method_toString(Context *ctx) // MathObject::MathObject(Context *ctx) { - setProperty(ctx, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); - setProperty(ctx, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); - setProperty(ctx, QStringLiteral("LN10"), Value::fromDouble(::log(10.0))); - setProperty(ctx, QStringLiteral("LOG2E"), Value::fromDouble(1.0/::log(2.0))); - setProperty(ctx, QStringLiteral("LOG10E"), Value::fromDouble(1.0/::log(10.0))); - setProperty(ctx, QStringLiteral("PI"), Value::fromDouble(qt_PI)); - setProperty(ctx, QStringLiteral("SQRT1_2"), Value::fromDouble(::sqrt(0.5))); - setProperty(ctx, QStringLiteral("SQRT2"), Value::fromDouble(::sqrt(2.0))); - - setProperty(ctx, QStringLiteral("abs"), method_abs, 1); - setProperty(ctx, QStringLiteral("acos"), method_acos, 1); - setProperty(ctx, QStringLiteral("asin"), method_asin, 0); - setProperty(ctx, QStringLiteral("atan"), method_atan, 1); - setProperty(ctx, QStringLiteral("atan2"), method_atan2, 2); - setProperty(ctx, QStringLiteral("ceil"), method_ceil, 1); - setProperty(ctx, QStringLiteral("cos"), method_cos, 1); - setProperty(ctx, QStringLiteral("exp"), method_exp, 1); - setProperty(ctx, QStringLiteral("floor"), method_floor, 1); - setProperty(ctx, QStringLiteral("log"), method_log, 1); - setProperty(ctx, QStringLiteral("max"), method_max, 2); - setProperty(ctx, QStringLiteral("min"), method_min, 2); - setProperty(ctx, QStringLiteral("pow"), method_pow, 2); - setProperty(ctx, QStringLiteral("random"), method_random, 0); - setProperty(ctx, QStringLiteral("round"), method_round, 1); - setProperty(ctx, QStringLiteral("sin"), method_sin, 1); - setProperty(ctx, QStringLiteral("sqrt"), method_sqrt, 1); - setProperty(ctx, QStringLiteral("tan"), method_tan, 1); + __put__(ctx, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); + __put__(ctx, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); + __put__(ctx, QStringLiteral("LN10"), Value::fromDouble(::log(10.0))); + __put__(ctx, QStringLiteral("LOG2E"), Value::fromDouble(1.0/::log(2.0))); + __put__(ctx, QStringLiteral("LOG10E"), Value::fromDouble(1.0/::log(10.0))); + __put__(ctx, QStringLiteral("PI"), Value::fromDouble(qt_PI)); + __put__(ctx, QStringLiteral("SQRT1_2"), Value::fromDouble(::sqrt(0.5))); + __put__(ctx, QStringLiteral("SQRT2"), Value::fromDouble(::sqrt(2.0))); + + __put__(ctx, QStringLiteral("abs"), method_abs, 1); + __put__(ctx, QStringLiteral("acos"), method_acos, 1); + __put__(ctx, QStringLiteral("asin"), method_asin, 0); + __put__(ctx, QStringLiteral("atan"), method_atan, 1); + __put__(ctx, QStringLiteral("atan2"), method_atan2, 2); + __put__(ctx, QStringLiteral("ceil"), method_ceil, 1); + __put__(ctx, QStringLiteral("cos"), method_cos, 1); + __put__(ctx, QStringLiteral("exp"), method_exp, 1); + __put__(ctx, QStringLiteral("floor"), method_floor, 1); + __put__(ctx, QStringLiteral("log"), method_log, 1); + __put__(ctx, QStringLiteral("max"), method_max, 2); + __put__(ctx, QStringLiteral("min"), method_min, 2); + __put__(ctx, QStringLiteral("pow"), method_pow, 2); + __put__(ctx, QStringLiteral("random"), method_random, 0); + __put__(ctx, QStringLiteral("round"), method_round, 1); + __put__(ctx, QStringLiteral("sin"), method_sin, 1); + __put__(ctx, QStringLiteral("sqrt"), method_sqrt, 1); + __put__(ctx, QStringLiteral("tan"), method_tan, 1); } /* copies the sign from y to x and returns the result */ diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index b0484047e9..6b20e27eb1 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -53,7 +53,7 @@ struct ObjectCtor: FunctionObject virtual void construct(Context *ctx); virtual void call(Context *ctx); - virtual Value getProperty(Context *ctx, String *name); + virtual Value __get__(Context *ctx, String *name); }; struct ObjectPrototype: Object -- cgit v1.2.3 From 477ea45b0b7d7b9dcc60d4cba4a4036359a969b6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 29 Oct 2012 15:50:07 +0100 Subject: Set property attributes more correctly Also now check for enumerable in for/in statements. Change-Id: I03a9968fc3d7f8f5e4eaf26591040acd9cc8ced1 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 14 ++++++++++---- qmljs_objects.h | 10 +++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index ecf97c416d..5d71aa4d6d 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -153,9 +153,6 @@ void Object::__put__(Context *ctx, String *name, const Value &value, bool throwE // ### to simplify and speed up we should expand the relevant parts here (clauses 6,7,9,10,12,13) PropertyDescriptor desc = PropertyDescriptor::fromValue(value); - desc.configurable = PropertyDescriptor::Undefined; - desc.enumberable = PropertyDescriptor::Undefined; - desc.writable = PropertyDescriptor::Undefined; __defineOwnProperty__(ctx, name, &desc, throwException); return; } @@ -185,6 +182,9 @@ void Object::__put__(Context *ctx, String *name, const Value &value, bool throwE PropertyDescriptor *p = members->insert(name); *p = PropertyDescriptor::fromValue(value); + p->configurable = PropertyDescriptor::Set; + p->enumberable = PropertyDescriptor::Set; + p->writable = PropertyDescriptor::Set; } reject: @@ -312,7 +312,7 @@ String *ForEachIteratorObject::nextPropertyName() } p = current->members->_properties[tableIndex]; // ### check that it's not a repeated attribute - if (p /*&& !(p->attributes & DontEnumAttribute)*/) + if (p && p->descriptor.isEnumerable()) return p->name; } } @@ -431,6 +431,7 @@ PropertyDescriptor *ActivationObject::__getPropertyDescriptor__(Context *ctx, St if (__qmljs_string_equal(var, name)) { *to_fill = PropertyDescriptor::fromValue(context->locals[i]); to_fill->writable = PropertyDescriptor::Set; + to_fill->enumberable = PropertyDescriptor::Set; return to_fill; } } @@ -439,6 +440,7 @@ PropertyDescriptor *ActivationObject::__getPropertyDescriptor__(Context *ctx, St if (__qmljs_string_equal(formal, name)) { *to_fill = PropertyDescriptor::fromValue(context->arguments[i]); to_fill->writable = PropertyDescriptor::Set; + to_fill->enumberable = PropertyDescriptor::Set; return to_fill; } } @@ -449,6 +451,8 @@ PropertyDescriptor *ActivationObject::__getPropertyDescriptor__(Context *ctx, St } *to_fill = PropertyDescriptor::fromValue(arguments); + to_fill->writable = PropertyDescriptor::Unset; + to_fill->enumberable = PropertyDescriptor::Unset; return to_fill; } } @@ -469,6 +473,8 @@ PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(Context *ctx, Str const quint32 i = Value::fromString(name).toUInt32(ctx); if (i < context->argumentCount) { *to_fill = PropertyDescriptor::fromValue(context->arguments[i]); + to_fill->writable = PropertyDescriptor::Unset; + to_fill->enumberable = PropertyDescriptor::Unset; return to_fill; } } diff --git a/qmljs_objects.h b/qmljs_objects.h index 07605916fb..f7c35076ea 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -138,9 +138,9 @@ struct PropertyDescriptor { PropertyDescriptor pd; pd.value = v; pd.type = Data; - pd.writable = Set; - pd.enumberable = Set; - pd.configurable = Set; + pd.writable = Undefined; + pd.enumberable = Undefined; + pd.configurable = Undefined; return pd; } static inline PropertyDescriptor fromAccessor(Object *getter, Object *setter) { @@ -149,8 +149,8 @@ struct PropertyDescriptor { pd.set = setter; pd.type = Accessor; pd.writable = Undefined; - pd.enumberable = Set; - pd.configurable = Set; + pd.enumberable = Undefined; + pd.configurable = Undefined; return pd; } -- cgit v1.2.3 From 29f8677ac27d628937cc3b6548c302a9d628630c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 30 Oct 2012 07:29:14 +0100 Subject: Implement initial support for the delete operator And don't dump the assemply unless SHOW_CODE is set. Change-Id: I17ad36f002404b57c65f910048e5c82d42307976 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 1 + qmljs_runtime.h | 1 - qv4codegen.cpp | 7 +++++-- qv4isel_masm.cpp | 41 ++++++++++++++++++++++++++++------------- 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 5291d45e08..051430beab 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -1286,6 +1286,7 @@ void __qmljs_builtin_throw(Value val, Context *context) __qmljs_throw(val, context); } + } // extern "C" diff --git a/qmljs_runtime.h b/qmljs_runtime.h index fc60299065..6b318849d8 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -260,7 +260,6 @@ Value __qmljs_construct_value(Context *context, Value func, Value *args, int arg Value __qmljs_builtin_typeof(Value val, Context *context); void __qmljs_builtin_throw(Value val, Context *context); - // constructors Value __qmljs_init_closure(IR::Function *clos, Context *ctx); Value __qmljs_init_native_function(void (*code)(Context *), Context *ctx); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 225b43cb3a..976ab05fa5 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1051,9 +1051,12 @@ bool Codegen::visit(ConditionalExpression *ast) return false; } -bool Codegen::visit(DeleteExpression *) +bool Codegen::visit(DeleteExpression *ast) { - assert(!"not implemented"); + Result expr = expression(ast->expression); + IR::ExprList *args = _function->New(); + args->init(*expr); + _expr.code = call(_block->NAME(IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args); return false; } diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 54a0b4c77f..e253ec75a8 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -141,24 +141,29 @@ void InstructionSelection::operator()(IR::Function *function) linkBuffer.patch(cbl.ptr, linkBuffer.locationOf(target)); } + static bool showCode = !qgetenv("SHOW_CODE").isNull(); + if (showCode) { #if OS(LINUX) - char* disasmOutput = 0; - size_t disasmLength = 0; - FILE* disasmStream = open_memstream(&disasmOutput, &disasmLength); - WTF::setDataFile(disasmStream); + char* disasmOutput = 0; + size_t disasmLength = 0; + FILE* disasmStream = open_memstream(&disasmOutput, &disasmLength); + WTF::setDataFile(disasmStream); #endif - QByteArray name = _function->name->toUtf8(); - _function->codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); + QByteArray name = _function->name->toUtf8(); + _function->codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); - WTF::setDataFile(stderr); + WTF::setDataFile(stderr); #if OS(LINUX) - fclose(disasmStream); + fclose(disasmStream); #if CPU(X86) || CPU(X86_64) - printDisassembledOutputWithCalls(disasmOutput, functions); + printDisassembledOutputWithCalls(disasmOutput, functions); #endif - free(disasmOutput); + free(disasmOutput); #endif + } else { + _function->codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); + } _function->code = (void (*)(VM::Context *, const uchar *)) _function->codeRef.code().executableAddress(); @@ -205,13 +210,23 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu generateFunctionCall(result, __qmljs_builtin_typeof, arg, ContextRegister); } break; - case IR::Name::builtin_delete: - Q_UNREACHABLE(); + case IR::Name::builtin_delete: { + if (IR::Member *m = call->args->expr->asMember()) { + generateFunctionCall(result, __qmljs_delete_member, ContextRegister, m->base->asTemp(), identifier(*m->name)); + return; + } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { + generateFunctionCall(result, __qmljs_delete_subscript, ContextRegister, ss->base->asTemp(), ss->index->asTemp()); + return; + } else { + assert(!"builtin_delete: unimplemented"); + Q_UNIMPLEMENTED(); + } break; + } case IR::Name::builtin_throw: { IR::Temp *arg = call->args->expr->asTemp(); assert(arg != 0); - generateFunctionCall(Void, __qmljs_builtin_throw, arg, ContextRegister); + generateFunctionCall(result, __qmljs_builtin_throw, arg, ContextRegister); } break; case IR::Name::builtin_create_exception_handler: -- cgit v1.2.3 From f1d508199fddf652324eb3182c5209397f353f40 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 30 Oct 2012 07:39:45 +0100 Subject: Add missing break statement Change-Id: I61e91701bbaa173c8189af9f703ac2b509b99cd0 Reviewed-by: Simon Hausmann --- qv4isel_masm.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index e253ec75a8..2deab59719 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -238,6 +238,7 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu break; case IR::Name::builtin_get_exception: generateFunctionCall(result, __qmljs_get_exception, ContextRegister); + break; case IR::Name::builtin_foreach_iterator_object: { IR::Temp *arg = call->args->expr->asTemp(); assert(arg != 0); -- cgit v1.2.3 From 9fff7d88693ddbb8364abdb9055f8cc5a202111a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 30 Oct 2012 07:45:58 +0100 Subject: Add undefined, NaN and Infinity to the global object Change-Id: Ic5047c231ab54ccc04043bf95418c0f6511b10bf Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 5d71aa4d6d..33dd11edb7 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -552,6 +552,9 @@ ExecutionEngine::ExecutionEngine() glo->__put__(rootContext, identifier(QStringLiteral("Date")), dateCtor); glo->__put__(rootContext, identifier(QStringLiteral("RegExp")), regExpCtor); glo->__put__(rootContext, identifier(QStringLiteral("Math")), Value::fromObject(newMathObject(rootContext))); + glo->__put__(rootContext, identifier(QStringLiteral("undefined")), Value::undefinedValue()); + glo->__put__(rootContext, identifier(QStringLiteral("NaN")), Value::fromDouble(nan(""))); + glo->__put__(rootContext, identifier(QStringLiteral("Infinity")), Value::fromDouble(INFINITY)); } Context *ExecutionEngine::newContext() -- cgit v1.2.3 From 88e869fe567b185e2bc7a851d78776cda3b60c03 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 1 Nov 2012 14:49:09 +0100 Subject: Implement Function(...) and new Function(...) Change-Id: I3ff229740820883a99dee6e32370fc528bf9169c Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 27 ++++++++++++++++++++++++-- qv4codegen_p.h | 1 + qv4ecmaobjects.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 976ab05fa5..39fc3e4901 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -259,8 +259,10 @@ protected: virtual bool visit(FunctionExpression *ast) { - _env->hasNestedFunctions = true; - _env->enter(ast->name.toString()); + if (_env) { + _env->hasNestedFunctions = true; + _env->enter(ast->name.toString()); + } enterEnvironment(ast); return true; } @@ -323,6 +325,27 @@ IR::Function *Codegen::operator()(Program *node, IR::Module *module, Mode mode) return globalCode; } +IR::Function *Codegen::operator()(AST::FunctionExpression *ast, IR::Module *module) +{ + _module = module; + _env = 0; + + ScanFunctions scan(this); + scan(ast); + + IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + + foreach (IR::Function *function, _module->functions) { + linearize(function); + } + + qDeleteAll(_envMap); + _envMap.clear(); + + return function; +} + + void Codegen::enterEnvironment(Node *node) { _env = _envMap.value(node); diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 1f5b87feba..bd8f3f829a 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -62,6 +62,7 @@ public: }; IR::Function *operator()(AST::Program *ast, IR::Module *module, Mode mode = GlobalCode); + IR::Function *operator()(AST::FunctionExpression *ast, IR::Module *module); protected: enum Format { ex, cx, nx }; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 3427d56ed7..f7f8455f49 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -53,6 +53,14 @@ #include #include +#include +#include +#include +#include +#include +#include +#include + #ifndef Q_WS_WIN # include # ifndef Q_OS_VXWORKS @@ -1726,14 +1734,58 @@ FunctionCtor::FunctionCtor(Context *scope) { } +// 15.3.2 void FunctionCtor::construct(Context *ctx) { - ctx->throwUnimplemented(QStringLiteral("Function.prototype.constructor")); + QString args; + QString body; + if (ctx->argumentCount > 0) + body = ctx->arguments[ctx->argumentCount - 1].toString(ctx)->toQString(); + + for (uint i = 0; i < ctx->argumentCount - 1; ++i) { + if (i) + args += QLatin1String(", "); + args += ctx->arguments[i].toString(ctx)->toQString(); + } + + QString function = QLatin1String("function(") + args + QLatin1String("){") + body + QLatin1String("}"); + + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(function, 1, false); + Parser parser(engine); + + const bool parsed = parser.parseExpression(); + + if (!parsed) + // ### Syntax error + __qmljs_throw_type_error(ctx); + + using namespace AST; + FunctionExpression *fe = AST::cast(parser.rootNode()); + if (!fe) + // ### Syntax error + __qmljs_throw_type_error(ctx); + + IR::Module module; + + Codegen cg; + IR::Function *irf = cg(fe, &module); + + uchar *code = 0; + MASM::InstructionSelection isel(ctx->engine, &module, code); + isel(irf); + + ctx->thisObject = Value::fromObject(new ScriptFunction(ctx->engine->rootContext, irf)); } +// 15.3.1: This is equivalent to new Function(...) void FunctionCtor::call(Context *ctx) { - ctx->throwUnimplemented(QStringLiteral("Function")); + Value v = ctx->thisObject; + construct(ctx); + ctx->result = ctx->thisObject; + ctx->thisObject = v; } void FunctionPrototype::init(Context *ctx, const Value &ctor) -- cgit v1.2.3 From 6f931118d6940f3dc315613d870498145fcc0d26 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 1 Nov 2012 15:37:47 +0100 Subject: Initial implementation of the Error prototype and constructor Change-Id: Iffd1a01b75bc923c0cd8c0b786558be20a52ab2c Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 6 ++++++ qmljs_objects.h | 3 +++ qv4ecmaobjects.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4ecmaobjects_p.h | 17 +++++++++++++++ 4 files changed, 88 insertions(+) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 33dd11edb7..725c5aceb9 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -501,6 +501,7 @@ ExecutionEngine::ExecutionEngine() datePrototype = new DatePrototype(); functionPrototype = new FunctionPrototype(rootContext); regExpPrototype = new RegExpPrototype(); + errorPrototype = new ErrorPrototype(); stringPrototype->prototype = objectPrototype; numberPrototype->prototype = objectPrototype; @@ -509,6 +510,7 @@ ExecutionEngine::ExecutionEngine() datePrototype->prototype = objectPrototype; functionPrototype->prototype = objectPrototype; regExpPrototype->prototype = objectPrototype; + errorPrototype->prototype = objectPrototype; objectCtor = Value::fromObject(new ObjectCtor(rootContext)); stringCtor = Value::fromObject(new StringCtor(rootContext)); @@ -518,6 +520,7 @@ ExecutionEngine::ExecutionEngine() functionCtor = Value::fromObject(new FunctionCtor(rootContext)); dateCtor = Value::fromObject(new DateCtor(rootContext)); regExpCtor = Value::fromObject(new RegExpCtor(rootContext)); + errorCtor = Value::fromObject(new ErrorCtor(rootContext)); stringCtor.objectValue()->prototype = functionPrototype; numberCtor.objectValue()->prototype = functionPrototype; @@ -526,6 +529,7 @@ ExecutionEngine::ExecutionEngine() functionCtor.objectValue()->prototype = functionPrototype; dateCtor.objectValue()->prototype = functionPrototype; regExpCtor.objectValue()->prototype = functionPrototype; + errorCtor.objectValue()->prototype = functionPrototype; objectPrototype->init(rootContext, objectCtor); stringPrototype->init(rootContext, stringCtor); @@ -535,6 +539,7 @@ ExecutionEngine::ExecutionEngine() datePrototype->init(rootContext, dateCtor); functionPrototype->init(rootContext, functionCtor); regExpPrototype->init(rootContext, regExpCtor); + errorPrototype->init(rootContext, errorCtor); // // set up the global object @@ -551,6 +556,7 @@ ExecutionEngine::ExecutionEngine() glo->__put__(rootContext, identifier(QStringLiteral("Function")), functionCtor); glo->__put__(rootContext, identifier(QStringLiteral("Date")), dateCtor); glo->__put__(rootContext, identifier(QStringLiteral("RegExp")), regExpCtor); + glo->__put__(rootContext, identifier(QStringLiteral("Error")), errorCtor); glo->__put__(rootContext, identifier(QStringLiteral("Math")), Value::fromObject(newMathObject(rootContext))); glo->__put__(rootContext, identifier(QStringLiteral("undefined")), Value::undefinedValue()); glo->__put__(rootContext, identifier(QStringLiteral("NaN")), Value::fromDouble(nan(""))); diff --git a/qmljs_objects.h b/qmljs_objects.h index f7c35076ea..1b8a4b9fe3 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -82,6 +82,7 @@ struct ArrayPrototype; struct FunctionPrototype; struct DatePrototype; struct RegExpPrototype; +struct ErrorPrototype; struct String { String(const QString &text) @@ -549,6 +550,7 @@ struct ExecutionEngine Value functionCtor; Value dateCtor; Value regExpCtor; + Value errorCtor; ObjectPrototype *objectPrototype; StringPrototype *stringPrototype; @@ -558,6 +560,7 @@ struct ExecutionEngine FunctionPrototype *functionPrototype; DatePrototype *datePrototype; RegExpPrototype *regExpPrototype; + ErrorPrototype *errorPrototype; QHash identifiers; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index f7f8455f49..5d2a6edf38 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2582,6 +2582,67 @@ void RegExpPrototype::method_toString(Context *ctx) } } +// +// ErrorCtr +// +ErrorCtor::ErrorCtor(Context *scope) + : FunctionObject(scope) +{ +} + +void ErrorCtor::construct(Context *ctx) +{ + ctx->thisObject = Value::fromObject(new ErrorObject(ctx->argument(0))); +} + +void ErrorCtor::call(Context *ctx) +{ + Value that = ctx->thisObject; + construct(ctx); + ctx->result = ctx->thisObject; + ctx->thisObject = that; +} + +void ErrorPrototype::init(Context *ctx, const Value &ctor) +{ + ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + __put__(ctx, QStringLiteral("constructor"), ctor); + __put__(ctx, QStringLiteral("toString"), method_toString, 0); +} + +void ErrorPrototype::method_toString(Context *ctx) +{ + Object *o = ctx->thisObject.asObject(); + if (!o) + __qmljs_throw_type_error(ctx); + + String n(QString::fromLatin1("name")); + Value name = o->__get__(ctx, &n); + QString qname; + if (name.isUndefined()) + qname = QString::fromLatin1("Error"); + else + qname = __qmljs_to_string(name, ctx).stringValue()->toQString(); + + String m(QString::fromLatin1("message")); + Value message = o->__get__(ctx, &m); + QString qmessage; + if (!message.isUndefined()) + qmessage = __qmljs_to_string(message, ctx).stringValue()->toQString(); + + QString str; + if (qname.isEmpty()) { + str = qmessage; + } else if (qmessage.isEmpty()) { + str = qname; + } else { + str = qname + QLatin1String(": ") + qmessage; + } + + ctx->result = Value::fromString(ctx, str); +} + + // // Math object // @@ -2833,3 +2894,4 @@ void MathObject::method_tan(Context *ctx) else ctx->result = Value::fromDouble(::tan(v)); } + diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 6b20e27eb1..3f78feaa64 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -294,6 +294,23 @@ struct RegExpPrototype: RegExpObject static void method_toString(Context *ctx); }; +struct ErrorCtor: FunctionObject +{ + ErrorCtor(Context *scope); + + virtual void construct(Context *ctx); + virtual void call(Context *ctx); +}; + +struct ErrorPrototype: ErrorObject +{ + // ### shouldn't be undefined + ErrorPrototype(): ErrorObject(Value::undefinedValue()) {} + void init(Context *ctx, const Value &ctor); + + static void method_toString(Context *ctx); +}; + struct MathObject: Object { MathObject(Context *ctx); -- cgit v1.2.3 From c3bcb5a2f6df2c604e857dda321bf17e5f8d4f60 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 1 Nov 2012 16:46:54 +0100 Subject: Fix === operator Correctly handle the case where one of the two arguments is a double and the other an integer. Change-Id: I589ac46acc30180b025c7e377c0523cf0889f294 Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 6b318849d8..1220118af4 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -1115,6 +1115,8 @@ inline uint __qmljs_cmp_in(Value left, Value right, Context *ctx) inline Bool __qmljs_strict_equal(Value x, Value y) { + if (x.isDouble() || y.isDouble()) + return x.asDouble() == y.asDouble(); if (x.rawValue() == y.rawValue()) return true; if (x.isString() && y.isString()) -- cgit v1.2.3 From b5baf031d5b51b7bba45e9bc17c22194cf895ef2 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 25 Oct 2012 10:08:22 +0200 Subject: Added calls to built-ins to moth. Change-Id: I277b1136ed3b073a9fc85726dc714ef68109fbfa Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 14 +++++++++++++ moth/qv4isel_moth.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++----- moth/qv4vme_moth.cpp | 23 +++++++++++++++++++++ 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 08481339fa..47b1545702 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -21,6 +21,7 @@ F(Push, push) \ F(CallValue, callValue) \ F(CallProperty, callProperty) \ + F(CallBuiltin, callBuiltin) \ F(Jump, jump) \ F(CJump, jump) \ F(Binop, binop) \ @@ -106,6 +107,18 @@ union Instr quint32 argc; quint32 args; }; + struct instr_callBuiltin { + MOTH_INSTR_HEADER + enum { + builtin_typeof, + builtin_throw, + builtin_create_exception_handler, + builtin_delete_exception_handler, + builtin_get_exception + } builtin; + quint32 argc; + quint32 args; + }; struct instr_jump { MOTH_INSTR_HEADER ptrdiff_t offset; @@ -133,6 +146,7 @@ union Instr instr_push push; instr_callValue callValue; instr_callProperty callProperty; + instr_callBuiltin callBuiltin; instr_jump jump; instr_binop binop; instr_loadThis loadThis; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index a32d5b76fd..227e005603 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -54,18 +54,63 @@ void InstructionSelection::operator()(IR::Function *function) void InstructionSelection::callActivationProperty(IR::Call *c) { - IR::Name *n = c->base->asName(); - Q_ASSERT(n); + IR::Name *baseNamen = c->base->asName(); + Q_ASSERT(baseNamen); - if (n->builtin == IR::Name::builtin_invalid) { + switch (baseNamen->builtin) { + case IR::Name::builtin_invalid: { Instruction::LoadName load; - load.name = _engine->newString(*n->id); + load.name = _engine->newString(*baseNamen->id); addInstruction(load); Instruction::CallValue call; prepareCallArgs(c->args, call.argc, call.args); addInstruction(call); - } else { + } break; + + case IR::Name::builtin_typeof: { + IR::Temp *arg = c->args->expr->asTemp(); + assert(arg != 0); + + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_typeof; + prepareCallArgs(c->args, call.argc, call.args); + addInstruction(call); + } break; + + case IR::Name::builtin_throw: { + IR::Temp *arg = c->args->expr->asTemp(); + assert(arg != 0); + + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_throw; + prepareCallArgs(c->args, call.argc, call.args); + addInstruction(call); + } break; + + case IR::Name::builtin_create_exception_handler: { + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_create_exception_handler; + addInstruction(call); + } break; + + case IR::Name::builtin_delete_exception_handler: { + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_delete_exception_handler; + addInstruction(call); + } break; + + case IR::Name::builtin_get_exception: { + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_get_exception; + addInstruction(call); + } break; + + case IR::Name::builtin_delete: + Q_UNIMPLEMENTED(); + break; + + default: Q_UNIMPLEMENTED(); } } diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index b3f61c296c..98b40ff47f 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -158,6 +158,29 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code tempRegister = __qmljs_call_property(context, tempRegister, instr.name, args, instr.argc); MOTH_END_INSTR(CallProperty) + MOTH_BEGIN_INSTR(CallBuiltin) + VM::Value *args = stack.data() + instr.args; + void *buf; + switch (instr.builtin) { + case Instr::instr_callBuiltin::builtin_typeof: + tempRegister = __qmljs_builtin_typeof(args[0], context); + break; + case Instr::instr_callBuiltin::builtin_throw: + __qmljs_builtin_typeof(args[0], context); + break; + case Instr::instr_callBuiltin::builtin_create_exception_handler: + buf = __qmljs_create_exception_handler(context); + tempRegister = VM::Value::fromInt32(setjmp(* static_cast(buf))); + break; + case Instr::instr_callBuiltin::builtin_delete_exception_handler: + __qmljs_delete_exception_handler(context); + break; + case Instr::instr_callBuiltin::builtin_get_exception: + tempRegister = __qmljs_get_exception(context); + break; + } + MOTH_END_INSTR(CallBuiltin) + MOTH_BEGIN_INSTR(Jump) code = ((uchar *)&instr.offset) + instr.offset; MOTH_END_INSTR(Jump) -- cgit v1.2.3 From 4df46b2543b6843619adf7d713a7c1dacb4ba23d Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 6 Nov 2012 12:24:32 +0100 Subject: Fix compilation of LLVM backend. Change-Id: I654f30c9811a74634cef9d27e07d016f73daa141 Reviewed-by: Lars Knoll --- main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index 91247b8c7b..545806bb29 100644 --- a/main.cpp +++ b/main.cpp @@ -166,8 +166,8 @@ int evaluateCompiledCode(const QStringList &files) VM::Context *ctx = vm.rootContext; QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); - globalObject->setProperty(ctx, vm.identifier(QStringLiteral("print")), - QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); + globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), + QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); void * buf = __qmljs_create_exception_handler(ctx); if (setjmp(*(jmp_buf *)buf)) { -- cgit v1.2.3 From c82b8448207e470a918ce7c398d059bc159cafbf Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 6 Nov 2012 12:31:02 +0100 Subject: Add tracing for runtime methods. Change-Id: I540297e83e3e297d0724c3e08e5780eee0d2fac8 Reviewed-by: Lars Knoll --- moth/moth.pri | 1 + moth/qv4vme_moth.cpp | 10 ++++--- qmljs_runtime.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) diff --git a/moth/moth.pri b/moth/moth.pri index d91d13fca8..4381c9a53b 100644 --- a/moth/moth.pri +++ b/moth/moth.pri @@ -10,3 +10,4 @@ SOURCES += \ $$PWD/qv4instr_moth.cpp \ $$PWD/qv4vme_moth.cpp \ +#DEFINES += DO_TRACE_INSTR diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 98b40ff47f..abe7909456 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -1,17 +1,17 @@ #include "qv4vme_moth_p.h" #include "qv4instr_moth_p.h" -using namespace QQmlJS; -using namespace QQmlJS::Moth; - #ifdef DO_TRACE_INSTR # define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); -# define TRACE(n, str, ...) { fprintf(stderr, "-- %s : ", #n); fprintf(stderr, str, __VA_ARGS__); fprintf(stderr, "\n"); } +# define TRACE(n, str, ...) { fprintf(stderr, " %s : ", #n); fprintf(stderr, str, __VA_ARGS__); fprintf(stderr, "\n"); } #else # define TRACE_INSTR(I) # define TRACE(n, str, ...) #endif // DO_TRACE_INSTR +using namespace QQmlJS; +using namespace QQmlJS::Moth; + #define MOTH_BEGIN_INSTR_COMMON(I) { \ const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ code += InstrMeta<(int)Instr::I>::Size; \ @@ -123,6 +123,7 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_END_INSTR(LoadFalse) MOTH_BEGIN_INSTR(LoadNumber) + TRACE(inline, "number = %f", instr.value); tempRegister = VM::Value::fromDouble(instr.value); MOTH_END_INSTR(LoadNumber) @@ -145,6 +146,7 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_END_INSTR(StoreName) MOTH_BEGIN_INSTR(Push) + TRACE(inline, "stack size: %u", instr.value); stack.resize(instr.value); MOTH_END_INSTR(Push) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 1220118af4..2d1f1cdcdf 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -52,6 +52,14 @@ #include #include +#ifdef DO_TRACE_INSTR +# define TRACE1(x) fprintf(stderr, " %s\n", __FUNCTION__); +# define TRACE2(x, y) fprintf(stderr, " %s\n", __FUNCTION__); +#else +# define TRACE1(x) +# define TRACE2(x, y) +#endif // TRACE1 + namespace QQmlJS { namespace IR { @@ -816,6 +824,8 @@ inline Value __qmljs_typeof(Value value, Context *ctx) inline Value __qmljs_uplus(Value value, Context *ctx) { + TRACE1(value); + if (value.tryIntegerConversion()) return value; @@ -825,6 +835,8 @@ inline Value __qmljs_uplus(Value value, Context *ctx) inline Value __qmljs_uminus(Value value, Context *ctx) { + TRACE1(value); + if (value.isInteger()) return Value::fromInt32(-value.integerValue()); double n = __qmljs_to_number(value, ctx); @@ -833,6 +845,8 @@ inline Value __qmljs_uminus(Value value, Context *ctx) inline Value __qmljs_compl(Value value, Context *ctx) { + TRACE1(value); + int n; if (value.isConvertibleToInt()) n = ~value.int_32; @@ -844,6 +858,8 @@ inline Value __qmljs_compl(Value value, Context *ctx) inline Value __qmljs_not(Value value, Context *ctx) { + TRACE1(value); + bool b = __qmljs_to_boolean(value, ctx); return Value::fromBoolean(!b); } @@ -851,6 +867,8 @@ inline Value __qmljs_not(Value value, Context *ctx) // binary operators inline Value __qmljs_bit_or(Value left, Value right, Context *ctx) { + TRACE2(left, right); + if (Value::integerCompatible(left, right)) return Value::fromInt32(left.integerValue() | right.integerValue()); @@ -861,6 +879,8 @@ inline Value __qmljs_bit_or(Value left, Value right, Context *ctx) inline Value __qmljs_bit_xor(Value left, Value right, Context *ctx) { + TRACE2(left, right); + if (Value::integerCompatible(left, right)) return Value::fromInt32(left.integerValue() ^ right.integerValue()); @@ -871,6 +891,8 @@ inline Value __qmljs_bit_xor(Value left, Value right, Context *ctx) inline Value __qmljs_bit_and(Value left, Value right, Context *ctx) { + TRACE2(left, right); + if (Value::integerCompatible(left, right)) return Value::fromInt32(left.integerValue() & right.integerValue()); @@ -881,6 +903,8 @@ inline Value __qmljs_bit_and(Value left, Value right, Context *ctx) inline Value __qmljs_add(Value left, Value right, Context *ctx) { + TRACE2(left, right); + if (Value::integerCompatible(left, right)) return add_int32(left.integerValue(), right.integerValue()); @@ -892,6 +916,8 @@ inline Value __qmljs_add(Value left, Value right, Context *ctx) inline Value __qmljs_sub(Value left, Value right, Context *ctx) { + TRACE2(left, right); + if (Value::integerCompatible(left, right)) return sub_int32(left.integerValue(), right.integerValue()); @@ -902,6 +928,8 @@ inline Value __qmljs_sub(Value left, Value right, Context *ctx) inline Value __qmljs_mul(Value left, Value right, Context *ctx) { + TRACE2(left, right); + if (Value::integerCompatible(left, right)) return mul_int32(left.integerValue(), right.integerValue()); @@ -912,6 +940,8 @@ inline Value __qmljs_mul(Value left, Value right, Context *ctx) inline Value __qmljs_div(Value left, Value right, Context *ctx) { + TRACE2(left, right); + double lval = __qmljs_to_number(left, ctx); double rval = __qmljs_to_number(right, ctx); return Value::fromDouble(lval / rval); @@ -919,6 +949,8 @@ inline Value __qmljs_div(Value left, Value right, Context *ctx) inline Value __qmljs_mod(Value left, Value right, Context *ctx) { + TRACE2(left, right); + if (Value::integerCompatible(left, right)) return Value::fromInt32(left.integerValue() % right.integerValue()); @@ -931,6 +963,8 @@ inline Value __qmljs_mod(Value left, Value right, Context *ctx) inline Value __qmljs_shl(Value left, Value right, Context *ctx) { + TRACE2(left, right); + if (Value::integerCompatible(left, right)) return Value::fromInt32(left.integerValue() << ((uint(right.integerValue()) & 0x1f))); @@ -941,6 +975,8 @@ inline Value __qmljs_shl(Value left, Value right, Context *ctx) inline Value __qmljs_shr(Value left, Value right, Context *ctx) { + TRACE2(left, right); + if (Value::integerCompatible(left, right)) return Value::fromInt32(left.integerValue() >> ((uint(right.integerValue()) & 0x1f))); @@ -951,6 +987,8 @@ inline Value __qmljs_shr(Value left, Value right, Context *ctx) inline Value __qmljs_ushr(Value left, Value right, Context *ctx) { + TRACE2(left, right); + if (Value::integerCompatible(left, right)) return Value::fromInt32(uint(left.integerValue()) >> ((uint(right.integerValue()) & 0x1f))); @@ -961,48 +999,66 @@ inline Value __qmljs_ushr(Value left, Value right, Context *ctx) inline Value __qmljs_gt(Value left, Value right, Context *ctx) { + TRACE2(left, right); + return Value::fromBoolean(__qmljs_cmp_gt(left, right, ctx)); } inline Value __qmljs_lt(Value left, Value right, Context *ctx) { + TRACE2(left, right); + return Value::fromBoolean(__qmljs_cmp_lt(left, right, ctx)); } inline Value __qmljs_ge(Value left, Value right, Context *ctx) { + TRACE2(left, right); + return Value::fromBoolean(__qmljs_cmp_ge(left, right, ctx)); } inline Value __qmljs_le(Value left, Value right, Context *ctx) { + TRACE2(left, right); + return Value::fromBoolean(__qmljs_cmp_le(left, right, ctx)); } inline Value __qmljs_eq(Value left, Value right, Context *ctx) { + TRACE2(left, right); + return Value::fromBoolean(__qmljs_cmp_eq(left, right, ctx)); } inline Value __qmljs_ne(Value left, Value right, Context *ctx) { + TRACE2(left, right); + return Value::fromBoolean(!__qmljs_cmp_eq(left, right, ctx)); } inline Value __qmljs_se(Value left, Value right, Context *) { + TRACE2(left, right); + bool r = __qmljs_strict_equal(left, right); return Value::fromBoolean(r); } inline Value __qmljs_sne(Value left, Value right, Context *) { + TRACE2(left, right); + bool r = ! __qmljs_strict_equal(left, right); return Value::fromBoolean(r); } inline Bool __qmljs_cmp_gt(Value left, Value right, Context *ctx) { + TRACE2(left, right); + left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); @@ -1021,6 +1077,8 @@ inline Bool __qmljs_cmp_gt(Value left, Value right, Context *ctx) inline Bool __qmljs_cmp_lt(Value left, Value right, Context *ctx) { + TRACE2(left, right); + left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); @@ -1039,6 +1097,8 @@ inline Bool __qmljs_cmp_lt(Value left, Value right, Context *ctx) inline Bool __qmljs_cmp_ge(Value left, Value right, Context *ctx) { + TRACE2(left, right); + left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); @@ -1057,6 +1117,8 @@ inline Bool __qmljs_cmp_ge(Value left, Value right, Context *ctx) inline Bool __qmljs_cmp_le(Value left, Value right, Context *ctx) { + TRACE2(left, right); + left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); @@ -1075,6 +1137,8 @@ inline Bool __qmljs_cmp_le(Value left, Value right, Context *ctx) inline Bool __qmljs_cmp_eq(Value left, Value right, Context *ctx) { + TRACE2(left, right); + // need to test for doubles first as NaN != NaN if (left.isDouble() && right.isDouble()) return left.doubleValue() == right.doubleValue(); @@ -1088,33 +1152,45 @@ inline Bool __qmljs_cmp_eq(Value left, Value right, Context *ctx) inline Bool __qmljs_cmp_ne(Value left, Value right, Context *ctx) { + TRACE2(left, right); + return !__qmljs_cmp_eq(left, right, ctx); } inline Bool __qmljs_cmp_se(Value left, Value right, Context *) { + TRACE2(left, right); + return __qmljs_strict_equal(left, right); } inline Bool __qmljs_cmp_sne(Value left, Value right, Context *) { + TRACE2(left, right); + return ! __qmljs_strict_equal(left, right); } inline Bool __qmljs_cmp_instanceof(Value left, Value right, Context *ctx) { + TRACE2(left, right); + Value v = __qmljs_instanceof(left, right, ctx); return v.booleanValue(); } inline uint __qmljs_cmp_in(Value left, Value right, Context *ctx) { + TRACE2(left, right); + Value v = __qmljs_in(left, right, ctx); return v.booleanValue(); } inline Bool __qmljs_strict_equal(Value x, Value y) { + TRACE2(x, y); + if (x.isDouble() || y.isDouble()) return x.asDouble() == y.asDouble(); if (x.rawValue() == y.rawValue()) -- cgit v1.2.3 From 70ba6c398c95597d7366fe068cf2d21dd70e3184 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 6 Nov 2012 12:31:53 +0100 Subject: Fix temp/local addressing in MOTH. Change-Id: Iae9d7a7679b2e315c671de6890ccdaafa28f8a03 Reviewed-by: Lars Knoll --- moth/qv4vme_moth.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index abe7909456..3b88ecefe7 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -54,10 +54,11 @@ static inline VM::Value *tempValue(QQmlJS::VM::Context *context, QVectorarguments + arg; - } else if (index < stack.count()) { - return stack.data() + index; + } else if (index < (int) context->varCount) { + return context->locals + index; } else { - return context->locals + index - stack.count(); + int off = index - context->varCount; + return stack.data() + off; } } -- cgit v1.2.3 From 6b73c9dc2b2fbb14351bf6bfce0ca6df07c1c574 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 6 Nov 2012 12:54:20 +0100 Subject: Added more instruction selection to moth to get crypto.js through. Change-Id: Ic9582ea3241d97ef619304ece9f18c7eaae90407 Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 80 +++++++++++++++++++++- moth/qv4isel_moth.cpp | 178 ++++++++++++++++++++++++++++++++++++++++++------- moth/qv4isel_moth_p.h | 1 + moth/qv4vme_moth.cpp | 54 +++++++++++++++ 4 files changed, 288 insertions(+), 25 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 47b1545702..bb2ae03659 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -18,14 +18,24 @@ F(MoveTemp, moveTemp) \ F(LoadName, loadName) \ F(StoreName, storeName) \ + F(LoadElement, loadElement) \ + F(StoreElement, storeElement) \ + F(LoadProperty, loadProperty) \ + F(StoreProperty, storeProperty) \ F(Push, push) \ F(CallValue, callValue) \ F(CallProperty, callProperty) \ F(CallBuiltin, callBuiltin) \ + F(CreateValue, createValue) \ + F(CreateProperty, createProperty) \ + F(CreateActivationProperty, createActivationProperty) \ F(Jump, jump) \ F(CJump, jump) \ + F(Unop, unop) \ F(Binop, binop) \ - F(LoadThis, loadThis) + F(LoadThis, loadThis) \ + F(InplaceElementOp, inplaceElementOp) \ + F(InplaceMemberOp, inplaceMemberOp) #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) # define MOTH_THREADED_INTERPRETER @@ -92,6 +102,26 @@ union Instr MOTH_INSTR_HEADER VM::String *name; }; + struct instr_loadProperty { + MOTH_INSTR_HEADER + int baseTemp; + VM::String *name; + }; + struct instr_storeProperty { + MOTH_INSTR_HEADER + int baseTemp; + VM::String *name; + }; + struct instr_loadElement { + MOTH_INSTR_HEADER + int base; + int index; + }; + struct instr_storeElement { + MOTH_INSTR_HEADER + int base; + int index; + }; struct instr_push { MOTH_INSTR_HEADER quint32 value; @@ -119,10 +149,34 @@ union Instr quint32 argc; quint32 args; }; + struct instr_createValue { + MOTH_INSTR_HEADER + int func; + quint32 argc; + quint32 args; + }; + struct instr_createProperty { + MOTH_INSTR_HEADER + int base; + VM::String *name; + quint32 argc; + quint32 args; + }; + struct instr_createActivationProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + }; struct instr_jump { MOTH_INSTR_HEADER ptrdiff_t offset; }; + struct instr_unop { + MOTH_INSTR_HEADER + int e; + VM::Value (*alu)(const VM::Value value, VM::Context *ctx); + }; struct instr_binop { MOTH_INSTR_HEADER int lhsTempIndex; @@ -132,6 +186,20 @@ union Instr struct instr_loadThis { MOTH_INSTR_HEADER }; + struct instr_inplaceElementOp { + MOTH_INSTR_HEADER + void (*alu)(VM::Value, VM::Value, VM::Value, VM::Context *); + int targetBase; + int targetIndex; + int source; + }; + struct instr_inplaceMemberOp { + MOTH_INSTR_HEADER + void (*alu)(VM::Value, VM::Value, VM::String *, VM::Context *); + int targetBase; + VM::String *targetMember; + int source; + }; instr_common common; instr_ret ret; @@ -143,13 +211,23 @@ union Instr instr_loadClosure loadClosure; instr_loadName loadName; instr_storeName storeName; + instr_loadElement loadElement; + instr_storeElement storeElement; + instr_loadProperty loadProperty; + instr_storeProperty storeProperty; instr_push push; instr_callValue callValue; instr_callProperty callProperty; instr_callBuiltin callBuiltin; + instr_createValue createValue; + instr_createProperty createProperty; + instr_createActivationProperty createActivationProperty; instr_jump jump; + instr_unop unop; instr_binop binop; instr_loadThis loadThis; + instr_inplaceElementOp inplaceElementOp; + instr_inplaceMemberOp inplaceMemberOp; static int size(Type type); }; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 227e005603..1eec3d0ae1 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -4,6 +4,10 @@ using namespace QQmlJS; using namespace QQmlJS::Moth; +namespace { +QTextStream qout(stderr, QIODevice::WriteOnly); +} + InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module * /*module*/, uchar *code) : _engine(engine), _code(code), _ccode(code) @@ -146,6 +150,32 @@ void InstructionSelection::callProperty(IR::Call *c) addInstruction(call); } +void InstructionSelection::construct(IR::New *ctor) +{ + if (IR::Name *baseName = ctor->base->asName()) { + Instruction::CreateActivationProperty create; + create.name = _engine->newString(*baseName->id); + prepareCallArgs(ctor->args, create.argc, create.args); + addInstruction(create); + } else if (IR::Member *member = ctor->base->asMember()) { + IR::Temp *base = member->base->asTemp(); + assert(base != 0); + + Instruction::CreateProperty create; + create.base = base->index; + create.name = _engine->newString(*member->name); + prepareCallArgs(ctor->args, create.argc, create.args); + addInstruction(create); + } else if (IR::Temp *baseTemp = ctor->base->asTemp()) { + Instruction::CreateValue create; + create.func = baseTemp->index; + prepareCallArgs(ctor->args, create.argc, create.args); + addInstruction(create); + } else { + qWarning(" NEW"); + } +} + void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 &args) { int locals = _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments; @@ -168,6 +198,9 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint ++argc; e = e->next; } + } else { + argc = 0; + args = 0; } } @@ -202,14 +235,6 @@ void InstructionSelection::visitLeave(IR::Leave *) Q_UNREACHABLE(); } -void InstructionSelection::visitMove(IR::Move *s) -{ - if (s->op == IR::OpInvalid) - simpleMove(s); - else - qWarning("UNKNOWN MOVE"); -} - typedef VM::Value (*ALUFunction)(const VM::Value, const VM::Value, VM::Context*); static inline ALUFunction aluOpFunction(IR::AluOp op) { @@ -278,8 +303,7 @@ static inline ALUFunction aluOpFunction(IR::AluOp op) } }; -// A move that doesn't involve an inplace operation -void InstructionSelection::simpleMove(IR::Move *s) +void InstructionSelection::visitMove(IR::Move *s) { if (IR::Name *n = s->target->asName()) { Q_UNUSED(n); @@ -293,6 +317,7 @@ void InstructionSelection::simpleMove(IR::Move *s) Instruction::StoreName store; store.name = _engine->newString(*n->id); addInstruction(store); + return; } else { Q_UNREACHABLE(); } @@ -342,17 +367,44 @@ void InstructionSelection::simpleMove(IR::Move *s) load.value = clos->value; addInstruction(load); } else if (IR::New *ctor = s->source->asNew()) { - Q_UNUSED(ctor); - qWarning(" NEW"); + construct(ctor); } else if (IR::Member *m = s->source->asMember()) { - Q_UNUSED(m); - qWarning(" MEMBER"); + if (IR::Temp *base = m->base->asTemp()) { + Instruction::LoadProperty load; + load.baseTemp = base->index; + load.name = _engine->newString(*m->name); + addInstruction(load); + } else { + qWarning(" MEMBER"); + } } else if (IR::Subscript *ss = s->source->asSubscript()) { - Q_UNUSED(ss); - qWarning(" SUBSCRIPT"); + Instruction::LoadElement load; + load.base = ss->base->asTemp()->index; + load.index = ss->index->asTemp()->index; + addInstruction(load); } else if (IR::Unop *u = s->source->asUnop()) { - Q_UNUSED(u); - qWarning(" UNOP"); + if (IR::Temp *e = u->expr->asTemp()) { + VM::Value (*op)(const VM::Value value, VM::Context *ctx) = 0; + switch (u->op) { + case IR::OpIfTrue: assert(!"unreachable"); break; + case IR::OpNot: op = VM::__qmljs_not; break; + case IR::OpUMinus: op = VM::__qmljs_uminus; break; + case IR::OpUPlus: op = VM::__qmljs_uplus; break; + case IR::OpCompl: op = VM::__qmljs_compl; break; + default: assert(!"unreachable"); break; + } // switch + + if (op) { + Instruction::Unop unop; + unop.alu = op; + unop.e = e->index; + addInstruction(unop); + } + } else { + qWarning(" UNOP"); + s->dump(qout, IR::Stmt::MIR); + qout << endl; + } } else if (IR::Binop *b = s->source->asBinop()) { Instruction::Binop binop; binop.alu = aluOpFunction(b->op); @@ -374,15 +426,93 @@ void InstructionSelection::simpleMove(IR::Move *s) Instruction::StoreTemp st; st.tempIndex = t->index; addInstruction(st); - } else if (IR::Member *m = s->target->asMember()) { - Q_UNUSED(m); - qWarning("MEMBER"); + return; } else if (IR::Subscript *ss = s->target->asSubscript()) { - Q_UNUSED(ss); + if (IR::Temp *t = s->source->asTemp()) { + void (*op)(VM::Value base, VM::Value index, VM::Value value, VM::Context *ctx) = 0; + switch (s->op) { + case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_element; break; + case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_element; break; + case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_element; break; + case IR::OpAdd: op = VM::__qmljs_inplace_add_element; break; + case IR::OpSub: op = VM::__qmljs_inplace_sub_element; break; + case IR::OpMul: op = VM::__qmljs_inplace_mul_element; break; + case IR::OpDiv: op = VM::__qmljs_inplace_div_element; break; + case IR::OpMod: op = VM::__qmljs_inplace_mod_element; break; + case IR::OpLShift: op = VM::__qmljs_inplace_shl_element; break; + case IR::OpRShift: op = VM::__qmljs_inplace_shr_element; break; + case IR::OpURShift: op = VM::__qmljs_inplace_ushr_element; break; + default: break; + } + + if (op) { + Instruction::InplaceElementOp ieo; + ieo.alu = op; + ieo.targetBase = ss->base->asTemp()->index; + ieo.targetIndex = ss->index->asTemp()->index; + ieo.source = t->index; + addInstruction(ieo); + return; + } else if (s->op == IR::OpInvalid) { + if (IR::Temp *t2 = s->source->asTemp()) { + Instruction::LoadTemp load; + load.tempIndex = t2->index; + addInstruction(load); + + Instruction::StoreElement store; + store.base = ss->base->asTemp()->index; + store.index = ss->index->asTemp()->index; + addInstruction(store); + return; + } + } + } qWarning("SUBSCRIPT"); - } else { - Q_UNREACHABLE(); + } else if (IR::Member *m = s->target->asMember()) { + if (IR::Temp *t = s->source->asTemp()) { + void (*op)(VM::Value value, VM::Value base, VM::String *name, VM::Context *ctx) = 0; + switch (s->op) { + case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_member; break; + case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_member; break; + case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_member; break; + case IR::OpAdd: op = VM::__qmljs_inplace_add_member; break; + case IR::OpSub: op = VM::__qmljs_inplace_sub_member; break; + case IR::OpMul: op = VM::__qmljs_inplace_mul_member; break; + case IR::OpDiv: op = VM::__qmljs_inplace_div_member; break; + case IR::OpMod: op = VM::__qmljs_inplace_mod_member; break; + case IR::OpLShift: op = VM::__qmljs_inplace_shl_member; break; + case IR::OpRShift: op = VM::__qmljs_inplace_shr_member; break; + case IR::OpURShift: op = VM::__qmljs_inplace_ushr_member; break; + default: break; + } + + if (op) { + Instruction::InplaceMemberOp imo; + imo.alu = op; + imo.targetBase = m->base->asTemp()->index; + imo.targetMember = _engine->newString(*m->name); + imo.source = t->index; + addInstruction(imo); + return; + } else if (s->op == IR::OpInvalid) { + Instruction::LoadTemp load; + load.tempIndex = t->index; + addInstruction(load); + + Instruction::StoreProperty store; + store.baseTemp = m->base->asTemp()->index; + store.name = _engine->newString(*m->name); + addInstruction(store); + return; + } + } + qWarning("MEMBER"); } + + Q_UNIMPLEMENTED(); + s->dump(qout, IR::Stmt::MIR); + qout << endl; + Q_UNREACHABLE(); } void InstructionSelection::visitJump(IR::Jump *s) diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 76e32409a7..6c92564bc7 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -38,6 +38,7 @@ private: void callActivationProperty(IR::Call *c); void callValue(IR::Call *c); void callProperty(IR::Call *c); + void construct(IR::New *ctor); void prepareCallArgs(IR::ExprList *, quint32 &, quint32 &); template diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 3b88ecefe7..2ea3337e37 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -146,6 +146,26 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code __qmljs_set_activation_property(context, instr.name, tempRegister); MOTH_END_INSTR(StoreName) + MOTH_BEGIN_INSTR(LoadElement) + tempRegister = __qmljs_get_element(context, TEMP(instr.base), TEMP(instr.index)); + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(StoreElement) + __qmljs_set_element(context, TEMP(instr.base), TEMP(instr.index), tempRegister); + MOTH_END_INSTR(StoreElement) + + MOTH_BEGIN_INSTR(LoadProperty) + TRACE(inline, "base temp = %d, property name = %s", instr.baseTemp, instr.name->toQString().toUtf8().constData()); + VM::Value base = TEMP(instr.baseTemp); + tempRegister = __qmljs_get_property(context, base, instr.name); + MOTH_END_INSTR(LoadProperty) + + MOTH_BEGIN_INSTR(StoreProperty) + TRACE(inline, "base temp = %d, property name = %s", instr.baseTemp, instr.name->toQString().toUtf8().constData()); + VM::Value base = TEMP(instr.baseTemp); + __qmljs_set_property(context, base, instr.name, tempRegister); + MOTH_END_INSTR(StoreProperty) + MOTH_BEGIN_INSTR(Push) TRACE(inline, "stack size: %u", instr.value); stack.resize(instr.value); @@ -184,6 +204,22 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code } MOTH_END_INSTR(CallBuiltin) + MOTH_BEGIN_INSTR(CreateValue) + VM::Value *args = stack.data() + instr.args; + tempRegister = __qmljs_construct_value(context, TEMP(instr.func), args, instr.argc); + MOTH_END_INSTR(CreateValue) + + MOTH_BEGIN_INSTR(CreateProperty) + VM::Value *args = stack.data() + instr.args; + tempRegister = __qmljs_construct_property(context, TEMP(instr.base), instr.name, args, instr.argc); + MOTH_END_INSTR(CreateProperty) + + MOTH_BEGIN_INSTR(CreateActivationProperty) + TRACE(inline, "property name = %s, argc = %d", instr.name->toQString().toUtf8().constData(), instr.argc); + VM::Value *args = stack.data() + instr.args; + tempRegister = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); + MOTH_END_INSTR(CreateActivationProperty) + MOTH_BEGIN_INSTR(Jump) code = ((uchar *)&instr.offset) + instr.offset; MOTH_END_INSTR(Jump) @@ -193,6 +229,10 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code code = ((uchar *)&instr.offset) + instr.offset; MOTH_END_INSTR(CJump) + MOTH_BEGIN_INSTR(Unop) + tempRegister = instr.alu(TEMP(instr.e), context); + MOTH_END_INSTR(Unop) + MOTH_BEGIN_INSTR(Binop) tempRegister = instr.alu(TEMP(instr.lhsTempIndex), TEMP(instr.rhsTempIndex), context); MOTH_END_INSTR(Binop) @@ -206,6 +246,20 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code tempRegister = __qmljs_get_thisObject(context); MOTH_END_INSTR(LoadThis) + MOTH_BEGIN_INSTR(InplaceElementOp) + instr.alu(TEMP(instr.targetBase), + TEMP(instr.targetIndex), + TEMP(instr.source), + context); + MOTH_END_INSTR(InplaceElementOp) + + MOTH_BEGIN_INSTR(InplaceMemberOp) + instr.alu(TEMP(instr.source), + TEMP(instr.targetBase), + instr.targetMember, + context); + MOTH_END_INSTR(InplaceMemberOp) + #ifdef MOTH_THREADED_INTERPRETER // nothing to do #else -- cgit v1.2.3 From 8e65e790fbaa85572566f5fb490346520c3314fd Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 7 Nov 2012 09:54:30 +0100 Subject: Fix codegen for void expressions. Change-Id: If89306aa91995e84106437a802e1d6d1940f17e7 Reviewed-by: Lars Knoll --- qv4codegen.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 39fc3e4901..8f8db30349 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1326,7 +1326,8 @@ bool Codegen::visit(UnaryPlusExpression *ast) bool Codegen::visit(VoidExpression *ast) { - statement(ast->expression); // ### CHECK + statement(ast->expression); + _expr.code = _block->CONST(IR::UndefinedType, 0); return false; } -- cgit v1.2.3 From 3edff21eb896bd0b31b5ebc048fb39e42d9787f2 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 7 Nov 2012 10:11:21 +0100 Subject: Fix build with shadow builds Otherwise, we get: gmake: *** No rule to make target `/home/thiago/src/qt/qt5/v4vm/udis86_itab.c', needed by `udis86_itab_holder.o'. Stop. Change-Id: I1d84ec00b6422fb8601e271d427ef619d462fd28 Reviewed-by: Lars Knoll --- masm/masm.pri | 1 + 1 file changed, 1 insertion(+) diff --git a/masm/masm.pri b/masm/masm.pri index 10540d87c5..a252291e30 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -27,6 +27,7 @@ INCLUDEPATH += $$PWD DEFINES += WTF_USE_UDIS86=1 INCLUDEPATH += $$PWD/disassembler INCLUDEPATH += $$PWD/disassembler/udis86 +INCLUDEPATH += $$_OUT_PWD SOURCES += $$PWD/disassembler/UDis86Disassembler.cpp SOURCES += $$PWD/disassembler/udis86/udis86.c SOURCES += $$PWD/disassembler/udis86/udis86_decode.c -- cgit v1.2.3 From 8ca7b975ce8932af949397c89c82b06612a70c61 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 7 Nov 2012 11:19:09 +0100 Subject: Fix use of uninitialised values. Also fix a typo in a variable name. Change-Id: I5e21544b69bed777c6733e2f517d52aca93a900b Reviewed-by: Simon Hausmann --- moth/qv4isel_moth.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 1eec3d0ae1..452577f683 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -58,13 +58,13 @@ void InstructionSelection::operator()(IR::Function *function) void InstructionSelection::callActivationProperty(IR::Call *c) { - IR::Name *baseNamen = c->base->asName(); - Q_ASSERT(baseNamen); + IR::Name *baseName = c->base->asName(); + Q_ASSERT(baseName); - switch (baseNamen->builtin) { + switch (baseName->builtin) { case IR::Name::builtin_invalid: { Instruction::LoadName load; - load.name = _engine->newString(*baseNamen->id); + load.name = _engine->newString(*baseName->id); addInstruction(load); Instruction::CallValue call; @@ -178,6 +178,9 @@ void InstructionSelection::construct(IR::New *ctor) void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 &args) { + argc = 0; + args = 0; + int locals = _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments; if (e && e->next == 0 && e->expr->asTemp()->index >= 0 && e->expr->asTemp()->index < locals) { @@ -198,9 +201,6 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint ++argc; e = e->next; } - } else { - argc = 0; - args = 0; } } -- cgit v1.2.3 From d2fb24b80aa85091b7efb15bf6e197011c023242 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 2 Nov 2012 22:07:58 +0100 Subject: Fix free memory reads This doesn't quite fix all issues, as activation records and arguments objects will still not work correctly in all cases (esp. the inplace operators). Change-Id: I0ebf2a08e6c61e4049d4b6f449987757bfc3f81c Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 80 ++++++++++++++++++++++++++++++++----------------------- qmljs_runtime.h | 2 +- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 051430beab..abb4a9d2a6 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -252,14 +252,12 @@ void Context::init(ExecutionEngine *eng) calledAsConstructor = false; } -Value *Context::lookupPropertyDescriptor(String *name) +PropertyDescriptor *Context::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp) { for (Context *ctx = this; ctx; ctx = ctx->parent) { - if (ctx->activation.isObject()) { - PropertyDescriptor tmp; - if (PropertyDescriptor *pd = ctx->activation.objectValue()->__getPropertyDescriptor__(this, name, &tmp)) { - return &pd->value; - } + if (Object *act = ctx->activation.asObject()) { + if (PropertyDescriptor *pd = act->__getPropertyDescriptor__(this, name, tmp)) + return pd; } } return 0; @@ -496,88 +494,99 @@ Value __qmljs_in(Value left, Value right, Context *ctx) void __qmljs_inplace_bit_and_name(Value value, String *name, Context *ctx) { - if (Value *prop = ctx->lookupPropertyDescriptor(name)) - *prop = __qmljs_bit_and(*prop, value, ctx); + PropertyDescriptor tmp; + if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) + prop->value = __qmljs_bit_and(prop->value, value, ctx); else ctx->throwReferenceError(Value::fromString(name)); } void __qmljs_inplace_bit_or_name(Value value, String *name, Context *ctx) { - if (Value *prop = ctx->lookupPropertyDescriptor(name)) - *prop = __qmljs_bit_or(*prop, value, ctx); + PropertyDescriptor tmp; + if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) + prop->value = __qmljs_bit_or(prop->value, value, ctx); else ctx->throwReferenceError(Value::fromString(name)); } void __qmljs_inplace_bit_xor_name(Value value, String *name, Context *ctx) { - if (Value *prop = ctx->lookupPropertyDescriptor(name)) - *prop = __qmljs_bit_xor(*prop, value, ctx); + PropertyDescriptor tmp; + if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) + prop->value = __qmljs_bit_xor(prop->value, value, ctx); else ctx->throwReferenceError(Value::fromString(name)); } void __qmljs_inplace_add_name(Value value, String *name, Context *ctx) { - if (Value *prop = ctx->lookupPropertyDescriptor(name)) - *prop = __qmljs_add(*prop, value, ctx); + PropertyDescriptor tmp; + if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) + prop->value = __qmljs_add(prop->value, value, ctx); else ctx->throwReferenceError(Value::fromString(name)); } void __qmljs_inplace_sub_name(Value value, String *name, Context *ctx) { - if (Value *prop = ctx->lookupPropertyDescriptor(name)) - *prop = __qmljs_sub(*prop, value, ctx); + PropertyDescriptor tmp; + if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) + prop->value = __qmljs_sub(prop->value, value, ctx); else ctx->throwReferenceError(Value::fromString(name)); } void __qmljs_inplace_mul_name(Value value, String *name, Context *ctx) { - if (Value *prop = ctx->lookupPropertyDescriptor(name)) - *prop = __qmljs_mul(*prop, value, ctx); + PropertyDescriptor tmp; + if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) + prop->value = __qmljs_mul(prop->value, value, ctx); else ctx->throwReferenceError(Value::fromString(name)); } void __qmljs_inplace_div_name(Value value, String *name, Context *ctx) { - if (Value *prop = ctx->lookupPropertyDescriptor(name)) - *prop = __qmljs_div(*prop, value, ctx); + PropertyDescriptor tmp; + if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) + prop->value = __qmljs_div(prop->value, value, ctx); else ctx->throwReferenceError(Value::fromString(name)); } void __qmljs_inplace_mod_name(Value value, String *name, Context *ctx) { - if (Value *prop = ctx->lookupPropertyDescriptor(name)) - *prop = __qmljs_mod(*prop, value, ctx); + PropertyDescriptor tmp; + if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) + prop->value = __qmljs_mod(prop->value, value, ctx); else ctx->throwReferenceError(Value::fromString(name)); } void __qmljs_inplace_shl_name(Value value, String *name, Context *ctx) { - if (Value *prop = ctx->lookupPropertyDescriptor(name)) - *prop = __qmljs_shl(*prop, value, ctx); + PropertyDescriptor tmp; + if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) + prop->value = __qmljs_shl(prop->value, value, ctx); else ctx->throwReferenceError(Value::fromString(name)); } void __qmljs_inplace_shr_name(Value value, String *name, Context *ctx) { - if (Value *prop = ctx->lookupPropertyDescriptor(name)) - *prop = __qmljs_shr(*prop, value, ctx); + PropertyDescriptor tmp; + if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) + prop->value = __qmljs_shr(prop->value, value, ctx); else ctx->throwReferenceError(Value::fromString(name)); } void __qmljs_inplace_ushr_name(Value value, String *name, Context *ctx) { - if (Value *prop = ctx->lookupPropertyDescriptor(name)) - *prop = __qmljs_ushr(*prop, value, ctx); + PropertyDescriptor tmp; + if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) + prop->value = __qmljs_ushr(prop->value, value, ctx); else ctx->throwReferenceError(Value::fromString(name)); } @@ -1043,8 +1052,9 @@ Value __qmljs_foreach_next_property_name(Value foreach_iterator) void __qmljs_set_activation_property(Context *ctx, String *name, Value value) { - if (Value *prop = ctx->lookupPropertyDescriptor(name)) - *prop = value; + PropertyDescriptor tmp; + if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) + prop->value = value; else ctx->engine->globalObject.objectValue()->__put__(ctx, name, value); } @@ -1093,8 +1103,9 @@ Value __qmljs_get_property(Context *ctx, Value object, String *name) Value __qmljs_get_activation_property(Context *ctx, String *name) { - if (Value *prop = ctx->lookupPropertyDescriptor(name)) - return *prop; + PropertyDescriptor tmp; + if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) + return prop->value; ctx->throwReferenceError(Value::fromString(name)); return Value::undefinedValue(); } @@ -1197,12 +1208,13 @@ Value __qmljs_call_value(Context *context, Value thisObject, Value func, Value * Value __qmljs_construct_activation_property(Context *context, String *name, Value *args, int argc) { - Value *func = context->lookupPropertyDescriptor(name); + PropertyDescriptor tmp; + PropertyDescriptor *func = context->lookupPropertyDescriptor(name, &tmp); if (! func) { context->throwReferenceError(Value::fromString(name)); return Value::undefinedValue(); } - return __qmljs_construct_value(context, *func, args, argc); + return __qmljs_construct_value(context, func->value, args, argc); } Value __qmljs_construct_value(Context *context, Value func, Value *args, int argc) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 2d1f1cdcdf..702d8dc204 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -572,7 +572,7 @@ struct Context { unsigned int varCount; int calledAsConstructor; - Value *lookupPropertyDescriptor(String *name); + PropertyDescriptor *lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp); inline Value argument(unsigned int index = 0) { -- cgit v1.2.3 From fe308861dcc0022f0a3b5b64d4437bfd783eec8c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 7 Nov 2012 16:31:10 +0100 Subject: Fixed/added name handling in expressions for moth. Change-Id: I6dc2faed00a465b74735d23e4c0bda9498dc7bd9 Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 10 ++++++- moth/qv4isel_moth.cpp | 79 ++++++++++++++++++++++++++++++++------------------ moth/qv4vme_moth.cpp | 6 ++++ 3 files changed, 65 insertions(+), 30 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index bb2ae03659..8073aba01f 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -35,7 +35,8 @@ F(Binop, binop) \ F(LoadThis, loadThis) \ F(InplaceElementOp, inplaceElementOp) \ - F(InplaceMemberOp, inplaceMemberOp) + F(InplaceMemberOp, inplaceMemberOp) \ + F(InplaceNameOp, inplaceNameOp) #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) # define MOTH_THREADED_INTERPRETER @@ -200,6 +201,12 @@ union Instr VM::String *targetMember; int source; }; + struct instr_inplaceNameOp { + MOTH_INSTR_HEADER + void (*alu)(VM::Value, VM::String *, VM::Context *); + VM::String *targetName; + int source; + }; instr_common common; instr_ret ret; @@ -228,6 +235,7 @@ union Instr instr_loadThis loadThis; instr_inplaceElementOp inplaceElementOp; instr_inplaceMemberOp inplaceMemberOp; + instr_inplaceNameOp inplaceNameOp; static int size(Type type); }; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 452577f683..3ab6e051f4 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -305,23 +305,7 @@ static inline ALUFunction aluOpFunction(IR::AluOp op) void InstructionSelection::visitMove(IR::Move *s) { - if (IR::Name *n = s->target->asName()) { - Q_UNUSED(n); - // set activation property - if (IR::Temp *t = s->source->asTemp()) { - // TODO: fold the next 2 instructions. - Instruction::LoadTemp load; - load.tempIndex = t->index; - addInstruction(load); - - Instruction::StoreName store; - store.name = _engine->newString(*n->id); - addInstruction(store); - return; - } else { - Q_UNREACHABLE(); - } - } else if (IR::Temp *t = s->target->asTemp()) { + if (IR::Temp *t = s->target->asTemp()) { // Check what kind of load it is, and generate the instruction for that. // The store to the temp (the target) is done afterwards. if (IR::Name *n = s->source->asName()) { @@ -399,9 +383,11 @@ void InstructionSelection::visitMove(IR::Move *s) unop.alu = op; unop.e = e->index; addInstruction(unop); + } else { + qWarning(" UNOP1"); } } else { - qWarning(" UNOP"); + qWarning(" UNOP2"); s->dump(qout, IR::Stmt::MIR); qout << endl; } @@ -427,6 +413,43 @@ void InstructionSelection::visitMove(IR::Move *s) st.tempIndex = t->index; addInstruction(st); return; + } else if (IR::Name *n = s->target->asName()) { + if (IR::Temp *t = s->source->asTemp()) { + void (*op)(VM::Value value, VM::String *name, VM::Context *ctx) = 0; + switch (s->op) { + case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_name; break; + case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_name; break; + case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_name; break; + case IR::OpAdd: op = VM::__qmljs_inplace_add_name; break; + case IR::OpSub: op = VM::__qmljs_inplace_sub_name; break; + case IR::OpMul: op = VM::__qmljs_inplace_mul_name; break; + case IR::OpDiv: op = VM::__qmljs_inplace_div_name; break; + case IR::OpMod: op = VM::__qmljs_inplace_mod_name; break; + case IR::OpLShift: op = VM::__qmljs_inplace_shl_name; break; + case IR::OpRShift: op = VM::__qmljs_inplace_shr_name; break; + case IR::OpURShift: op = VM::__qmljs_inplace_ushr_name; break; + default: break; + } + + if (op) { + Instruction::InplaceNameOp ieo; + ieo.alu = op; + ieo.targetName = _engine->newString(*n->id); + ieo.source = t->index; + addInstruction(ieo); + return; + } else if (s->op == IR::OpInvalid) { + Instruction::LoadTemp load; + load.tempIndex = t->index; + addInstruction(load); + + Instruction::StoreName store; + store.name = _engine->newString(*n->id); + addInstruction(store); + return; + } + } + qWarning("NAME"); } else if (IR::Subscript *ss = s->target->asSubscript()) { if (IR::Temp *t = s->source->asTemp()) { void (*op)(VM::Value base, VM::Value index, VM::Value value, VM::Context *ctx) = 0; @@ -454,17 +477,15 @@ void InstructionSelection::visitMove(IR::Move *s) addInstruction(ieo); return; } else if (s->op == IR::OpInvalid) { - if (IR::Temp *t2 = s->source->asTemp()) { - Instruction::LoadTemp load; - load.tempIndex = t2->index; - addInstruction(load); - - Instruction::StoreElement store; - store.base = ss->base->asTemp()->index; - store.index = ss->index->asTemp()->index; - addInstruction(store); - return; - } + Instruction::LoadTemp load; + load.tempIndex = t->index; + addInstruction(load); + + Instruction::StoreElement store; + store.base = ss->base->asTemp()->index; + store.index = ss->index->asTemp()->index; + addInstruction(store); + return; } } qWarning("SUBSCRIPT"); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 2ea3337e37..82a61ef865 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -260,6 +260,12 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code context); MOTH_END_INSTR(InplaceMemberOp) + MOTH_BEGIN_INSTR(InplaceNameOp) + instr.alu(TEMP(instr.source), + instr.targetName, + context); + MOTH_END_INSTR(InplaceNameOp) + #ifdef MOTH_THREADED_INTERPRETER // nothing to do #else -- cgit v1.2.3 From a55e56327952b0d6cba11e2573ec122ef98ccad8 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 8 Nov 2012 12:45:40 +0100 Subject: Changed commandline and return codes for test harness. The different backends can now be selected by command-line options, and when none is specified, masm is used. The difference is that the environment variable USE_MOTH is not used anymore. The return codes are 0 or -1, so the test harness can easily check for errors. The ECMA test262 harness uses the $ERROR function to report errors. When the environment variable IN_TEST_HARNESS is set, this function will get added to the global object. Change-Id: I291c72332ea50892afb99f9a0a15004ffc81c200 Reviewed-by: Simon Hausmann --- main.cpp | 155 ++++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 113 insertions(+), 42 deletions(-) diff --git a/main.cpp b/main.cpp index 545806bb29..5bb61a6090 100644 --- a/main.cpp +++ b/main.cpp @@ -70,7 +70,8 @@ static inline bool protect(const void *addr, size_t size) return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; } -static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QString &source, +static int evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, + const QString &source, bool useInterpreter, QQmlJS::Codegen::Mode mode = QQmlJS::Codegen::GlobalCode); namespace builtins { @@ -95,13 +96,36 @@ struct Print: FunctionObject struct Eval: FunctionObject { - Eval(Context *scope): FunctionObject(scope) {} + Eval(Context *scope, bool useInterpreter): FunctionObject(scope), useInterpreter(useInterpreter) {} virtual void call(Context *ctx) { const QString code = ctx->argument(0).toString(ctx)->toQString(); - evaluate(ctx, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode); + evaluate(ctx, QStringLiteral("eval code"), code, useInterpreter, QQmlJS::Codegen::EvalCode); } + +private: + bool useInterpreter; +}; + +struct TestHarnessError: FunctionObject +{ + TestHarnessError(Context *scope, bool &errorInTestHarness): FunctionObject(scope), errorOccurred(errorInTestHarness) {} + + virtual void call(Context *ctx) + { + errorOccurred = true; + + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { + String *s = ctx->argument(i).toString(ctx); + if (i) + std::cerr << ' '; + std::cerr << qPrintable(s->toQString()); + } + std::cerr << std::endl; + } + + bool &errorOccurred; }; } // builtins @@ -145,7 +169,7 @@ int compileFiles(const QStringList &files) compile(fileName, source); } } - return 0; + return EXIT_SUCCESS; } int evaluateCompiledCode(const QStringList &files) @@ -159,7 +183,7 @@ int evaluateCompiledCode(const QStringList &files) QFunctionPointer ptr = lib.resolve("%entry"); // qDebug("_%%entry resolved to address %p", ptr); if (!ptr) - return -1; + return EXIT_FAILURE; void (*code)(VM::Context *) = (void (*)(VM::Context *)) ptr; VM::ExecutionEngine vm; @@ -175,19 +199,20 @@ int evaluateCompiledCode(const QStringList &files) std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; else std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; - return -2; + return EXIT_FAILURE; } code(ctx); } - return 0; + return EXIT_SUCCESS; } #endif -static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QString &source, - QQmlJS::Codegen::Mode mode) +static int evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, + const QString &source, bool useInterpreter, + QQmlJS::Codegen::Mode mode) { using namespace QQmlJS; @@ -202,8 +227,6 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS assert(code); assert(! (size_t(code) & 15)); - static bool useMoth = !qgetenv("USE_MOTH").isNull(); - { QQmlJS::Engine ee, *engine = ⅇ Lexer lexer(engine); @@ -224,7 +247,7 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS Codegen cg; globalCode = cg(program, &module, mode); - if (useMoth) { + if (useInterpreter) { Moth::InstructionSelection isel(vm, &module, code); foreach (IR::Function *function, module.functions) isel(function); @@ -240,7 +263,7 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS } if (! globalCode) - return; + return EXIT_FAILURE; } if (! ctx->activation.isObject()) @@ -256,10 +279,10 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; else std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; - return; + return EXIT_FAILURE; } - if (useMoth) { + if (useInterpreter) { Moth::VME vme; vme(ctx, code); } else { @@ -267,9 +290,11 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS } if (! ctx->result.isUndefined()) { - if (! qgetenv("SHOW_EXIT_VALUE").isNull()) + if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) std::cout << "exit value: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; } + + return EXIT_SUCCESS; } int main(int argc, char *argv[]) @@ -278,38 +303,84 @@ int main(int argc, char *argv[]) QStringList args = app.arguments(); args.removeFirst(); + enum { + use_masm, + use_moth, + use_llvm_compiler, + use_llvm_runtime + } mode = use_masm; + + if (!args.isEmpty()) { + if (args.first() == QLatin1String("--jit")) { + mode = use_masm; + args.removeFirst(); + } + + if (args.first() == QLatin1String("--interpret")) { + mode = use_moth; + args.removeFirst(); + } + #ifndef QMLJS_NO_LLVM - if (args.isEmpty()) { - std::cerr << "Usage: v4 [--compile|--aot] file..." << std::endl; - return 0; + if (args.first() == QLatin1String("--compile")) { + mode = use_llvm_compiler; + args.removeFirst(); + } + + if (args.first() == QLatin1String("--aot")) { + mode = use_llvm_runtime; + args.removeFirst(); + } +#endif // QMLJS_NO_LLVM + if (args.first() == QLatin1String("--help")) { + std::cerr << "Usage: v4 [|--jit|--interpret|--compile|--aot] file..." << std::endl; + return EXIT_SUCCESS; + } } - if (args.first() == QLatin1String("--compile")) { - args.removeFirst(); + switch (mode) { +#ifdef QMLJS_NO_LLVM + case use_llvm_compiler: + case use_llvm_runtime: + std::cerr << "LLVM backend was not built, compiler is unavailable." << std::endl; + return EXIT_FAILURE; +#else // QMLJS_NO_LLVM + case use_llvm_compiler: return compileFiles(args); - } else if (args.first() == QLatin1String("--aot")) { - args.removeFirst(); + case use_llvm_runtime: return evaluateCompiledCode(args); - } -#endif +#endif // QMLJS_NO_LLVM + case use_masm: + case use_moth: { + bool useInterpreter = mode == use_moth; + QQmlJS::VM::ExecutionEngine vm; + QQmlJS::VM::Context *ctx = vm.rootContext; - QQmlJS::VM::ExecutionEngine vm; - QQmlJS::VM::Context *ctx = vm.rootContext; - - QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); - globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), - QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); - - globalObject->__put__(ctx, vm.identifier(QStringLiteral("eval")), - QQmlJS::VM::Value::fromObject(new builtins::Eval(ctx))); - - foreach (const QString &fn, args) { - QFile file(fn); - if (file.open(QFile::ReadOnly)) { - const QString code = QString::fromUtf8(file.readAll()); - file.close(); - - evaluate(vm.rootContext, fn, code); + QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); + globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), + QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); + + globalObject->__put__(ctx, vm.identifier(QStringLiteral("eval")), + QQmlJS::VM::Value::fromObject(new builtins::Eval(ctx, useInterpreter))); + + bool errorInTestHarness = false; + if (!qgetenv("IN_TEST_HARNESS").isEmpty()) + globalObject->__put__(ctx, vm.identifier(QStringLiteral("$ERROR")), + QQmlJS::VM::Value::fromObject(new builtins::TestHarnessError(ctx, errorInTestHarness))); + + foreach (const QString &fn, args) { + QFile file(fn); + if (file.open(QFile::ReadOnly)) { + const QString code = QString::fromUtf8(file.readAll()); + file.close(); + + int exitCode = evaluate(vm.rootContext, fn, code, useInterpreter); + if (exitCode != EXIT_SUCCESS) + return exitCode; + if (errorInTestHarness) + return EXIT_FAILURE; + } } + } return EXIT_SUCCESS; } } -- cgit v1.2.3 From f20dac2647af7ac7865cfe2a49e84a6ce063d962 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 8 Nov 2012 14:54:37 +0100 Subject: Fixed compilation of LLVM glue-code. Change-Id: I3adc68012f7492281fe8849f58764473dcb3671e Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 8 +++----- v4.pro | 9 ++++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 702d8dc204..ee200e320f 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -41,11 +41,9 @@ #ifndef QMLJS_RUNTIME_H #define QMLJS_RUNTIME_H -#ifndef QMLJS_LLVM_RUNTIME -# include -# include -# include -#endif +#include +#include +#include #include diff --git a/v4.pro b/v4.pro index 38f821b542..701e503639 100644 --- a/v4.pro +++ b/v4.pro @@ -44,10 +44,7 @@ HEADERS += \ INCLUDEPATH += \ $$system($$LLVM_CONFIG --includedir) -DEFINES += \ - __STDC_CONSTANT_MACROS \ - __STDC_FORMAT_MACROS \ - __STDC_LIMIT_MACROS +QMAKE_CXXFLAGS += $$system($$LLVM_CONFIG --cppflags) LIBS += \ $$system($$LLVM_CONFIG --ldflags) \ @@ -55,8 +52,10 @@ LIBS += \ QMAKE_EXTRA_TARGETS += gen_llvm_runtime +GEN_LLVM_RUNTIME_FLAGS = $$system($$LLVM_CONFIG --cppflags) + gen_llvm_runtime.target = llvm_runtime -gen_llvm_runtime.commands = clang -O2 -emit-llvm -c -I. -Imasm -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o llvm_runtime.bc +gen_llvm_runtime.commands = clang -O2 -emit-llvm -c $(INCPATH) $$GEN_LLVM_RUNTIME_FLAGS -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o llvm_runtime.bc } else { -- cgit v1.2.3 From 1b424de677998c38e5cf6df5ee82a149926422eb Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 7 Nov 2012 15:34:34 +0100 Subject: Change ConvertibleToInt to not interfere with valid addresses. Change-Id: I1415907f94d064e64bf5495b7c136355ffd866fe Reviewed-by: Lars Knoll --- qmljs_runtime.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index ee200e320f..a629b57b12 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -129,7 +129,7 @@ struct Value }; enum ImmediateFlags { - ConvertibleToInt = NotDouble_Mask | 0x1 + ConvertibleToInt = Immediate_Mask | (0x1 << 15) }; enum ValueTypeInternal { -- cgit v1.2.3 From 288e5048b716d9a66617b39637e02abb2c0f0d4e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 9 Nov 2012 23:33:07 +0100 Subject: Implement member operations for non integers indices Implement the more general foo[expr] op= bar Change-Id: Idb2143cfe4b48fc9ce177d699970818e877dc735 Reviewed-by: Erik Verbruggen --- qmljs_runtime.cpp | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index abb4a9d2a6..3b26daf9b8 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -603,7 +603,9 @@ void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, Conte return; } } - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + String *s = index.toString(ctx); + assert(s); + __qmljs_inplace_bit_and_member(value, base, s, ctx); } void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, Context *ctx) @@ -618,7 +620,9 @@ void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, Contex return; } } - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + String *s = index.toString(ctx); + assert(s); + __qmljs_inplace_bit_or_member(value, base, s, ctx); } void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, Context *ctx) @@ -633,7 +637,9 @@ void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, Conte return; } } - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + String *s = index.toString(ctx); + assert(s); + __qmljs_inplace_bit_xor_member(value, base, s, ctx); } void __qmljs_inplace_add_element(Value base, Value index, Value value, Context *ctx) @@ -648,7 +654,9 @@ void __qmljs_inplace_add_element(Value base, Value index, Value value, Context * return; } } - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + String *s = index.toString(ctx); + assert(s); + __qmljs_inplace_add_member(value, base, s, ctx); } void __qmljs_inplace_sub_element(Value base, Value index, Value value, Context *ctx) @@ -663,7 +671,9 @@ void __qmljs_inplace_sub_element(Value base, Value index, Value value, Context * return; } } - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + String *s = index.toString(ctx); + assert(s); + __qmljs_inplace_sub_member(value, base, s, ctx); } void __qmljs_inplace_mul_element(Value base, Value index, Value value, Context *ctx) @@ -678,7 +688,9 @@ void __qmljs_inplace_mul_element(Value base, Value index, Value value, Context * return; } } - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + String *s = index.toString(ctx); + assert(s); + __qmljs_inplace_mul_member(value, base, s, ctx); } void __qmljs_inplace_div_element(Value base, Value index, Value value, Context *ctx) @@ -693,7 +705,9 @@ void __qmljs_inplace_div_element(Value base, Value index, Value value, Context * return; } } - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + String *s = index.toString(ctx); + assert(s); + __qmljs_inplace_div_member(value, base, s, ctx); } void __qmljs_inplace_mod_element(Value base, Value index, Value value, Context *ctx) @@ -708,7 +722,9 @@ void __qmljs_inplace_mod_element(Value base, Value index, Value value, Context * return; } } - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + String *s = index.toString(ctx); + assert(s); + __qmljs_inplace_mod_member(value, base, s, ctx); } void __qmljs_inplace_shl_element(Value base, Value index, Value value, Context *ctx) @@ -723,7 +739,9 @@ void __qmljs_inplace_shl_element(Value base, Value index, Value value, Context * return; } } - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + String *s = index.toString(ctx); + assert(s); + __qmljs_inplace_shl_member(value, base, s, ctx); } void __qmljs_inplace_shr_element(Value base, Value index, Value value, Context *ctx) @@ -738,7 +756,9 @@ void __qmljs_inplace_shr_element(Value base, Value index, Value value, Context * return; } } - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + String *s = index.toString(ctx); + assert(s); + __qmljs_inplace_shr_member(value, base, s, ctx); } void __qmljs_inplace_ushr_element(Value base, Value index, Value value, Context *ctx) @@ -753,7 +773,9 @@ void __qmljs_inplace_ushr_element(Value base, Value index, Value value, Context return; } } - ctx->throwUnimplemented(QLatin1String(Q_FUNC_INFO)); + String *s = index.toString(ctx); + assert(s); + __qmljs_inplace_ushr_member(value, base, s, ctx); } void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, Context *ctx) -- cgit v1.2.3 From d5fe0946bd1aa7eb74b74b37303d3953dd5f12d5 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 10 Nov 2012 12:45:00 +0100 Subject: Cleanup call and construct semantics Implement proper call() and construct() methods in FunctionObject, and remove some duplicated initialization code. Change-Id: I5c86f9f2cf6e4b65d23d000149db2b79c35538c4 Reviewed-by: Erik Verbruggen --- qmljs_objects.cpp | 33 +++++++++++++++++++++++---------- qmljs_objects.h | 11 ++++++++--- qmljs_runtime.cpp | 31 +++++++------------------------ 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 725c5aceb9..447051f6d1 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -102,7 +102,7 @@ Value Object::__get__(Context *ctx, String *name) return Value::undefinedValue(); FunctionObject *f = p->get->asFunctionObject(); if (f) { - f->call(ctx); + f->call(ctx, Value::fromObject(this), 0, 0); return ctx->result; } } @@ -165,18 +165,10 @@ void Object::__put__(Context *ctx, String *name, const Value &value, bool throwE // Clause 5 if (pd && pd->isAccessor()) { assert(pd->set != 0); - FunctionObject *func = pd->set->asFunctionObject(); - assert(func); - // ### unify with callFunction method - Context k; - Context *c = func->needsActivation ? ctx->engine->newContext() : &k; - Value that = Value::fromObject(this); Value args[1]; args[0] = value; - c->initCallContext(ctx, &that, func, args, 1); - func->call(c); - c->leaveCallContext(); + pd->set->call(ctx, Value::fromObject(this), args, 1); return; } @@ -350,6 +342,27 @@ bool FunctionObject::hasInstance(Context *ctx, const Value &value) return false; } +Value FunctionObject::construct(Context *context, Value *args, int argc) +{ + Context k; + Context *ctx = needsActivation ? context->engine->newContext() : &k; + ctx->initConstructorContext(context, 0, this, args, argc); + construct(ctx); + ctx->leaveConstructorContext(this); + return ctx->result; +} + +Value FunctionObject::call(Context *context, Value thisObject, Value *args, int argc) +{ + Context k; + Context *ctx = needsActivation ? context->engine->newContext() : &k; + const Value *that = thisObject.isUndefined() ? 0 : &thisObject; + ctx->initCallContext(context, that, this, args, argc); + call(ctx); + ctx->leaveCallContext(); + return ctx->result; +} + void FunctionObject::call(Context *ctx) { Q_UNUSED(ctx); diff --git a/qmljs_objects.h b/qmljs_objects.h index 1b8a4b9fe3..8cac59daaa 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -126,8 +126,8 @@ struct PropertyDescriptor { union { Value value; struct { - Object *get; - Object *set; + FunctionObject *get; + FunctionObject *set; }; }; uint type : 8; @@ -144,7 +144,7 @@ struct PropertyDescriptor { pd.configurable = Undefined; return pd; } - static inline PropertyDescriptor fromAccessor(Object *getter, Object *setter) { + static inline PropertyDescriptor fromAccessor(FunctionObject *getter, FunctionObject *setter) { PropertyDescriptor pd; pd.get = getter; pd.set = setter; @@ -480,6 +480,11 @@ struct FunctionObject: Object { virtual QString className() { return QStringLiteral("Function"); } virtual FunctionObject *asFunctionObject() { return this; } virtual bool hasInstance(Context *ctx, const Value &value); + + Value construct(Context *context, Value *args, int argc); + Value call(Context *context, Value thisObject, Value *args, int argc); + +protected: virtual void call(Context *ctx); virtual void construct(Context *ctx); }; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 3b26daf9b8..16c940216d 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -58,13 +58,7 @@ namespace VM { static inline Value callFunction(Context *context, Value thisObject, FunctionObject *func, Value *args, int argc) { if (func) { - Context k; - Context *ctx = func->needsActivation ? context->engine->newContext() : &k; - const Value *that = thisObject.isUndefined() ? 0 : &thisObject; - ctx->initCallContext(context, that, func, args, argc); - func->call(ctx); - ctx->leaveCallContext(); - return ctx->result; + return func->call(context, thisObject, args, argc); } else { context->throwTypeError(); return Value::undefinedValue(); @@ -1241,14 +1235,9 @@ Value __qmljs_construct_activation_property(Context *context, String *name, Valu Value __qmljs_construct_value(Context *context, Value func, Value *args, int argc) { - if (FunctionObject *f = func.asFunctionObject()) { - Context k; - Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->initConstructorContext(context, 0, f, args, argc); - f->construct(ctx); - ctx->leaveConstructorContext(f); - return ctx->result; - } + if (FunctionObject *f = func.asFunctionObject()) + return f->construct(context, args, argc); + context->throwTypeError(); return Value::undefinedValue(); } @@ -1260,15 +1249,9 @@ Value __qmljs_construct_property(Context *context, Value base, String *name, Val thisObject = __qmljs_to_object(base, context); Value func = thisObject.objectValue()->__get__(context, name); - if (FunctionObject *f = func.asFunctionObject()) { - Context k; - Context *ctx = f->needsActivation ? context->engine->newContext() : &k; - ctx->initConstructorContext(context, 0, f, args, argc); - ctx->calledAsConstructor = true; - f->construct(ctx); - ctx->leaveConstructorContext(f); - return ctx->result; - } + if (FunctionObject *f = func.asFunctionObject()) + return f->construct(context, args, argc); + context->throwTypeError(); return Value::undefinedValue(); } -- cgit v1.2.3 From 0c1424362fad42cd4ed027adad811ecdaea6d676 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 10 Nov 2012 23:35:06 +0100 Subject: Improve handling of binops on LHS expressions Add a generic method to handle binops such as += to Object and use it. We can probably remove most of the __qmljs_inplace_xxx methods later on and call one generic method instead. Change-Id: If7cbd2e3012ad381e29b9eec2059e17f02a0a38a Reviewed-by: Erik Verbruggen --- qmljs_objects.cpp | 36 ++++++++++----- qmljs_objects.h | 3 ++ qmljs_runtime.cpp | 132 +++++++++++++++++++++++------------------------------- qmljs_runtime.h | 2 + 4 files changed, 85 insertions(+), 88 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 447051f6d1..353f7f917b 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -69,6 +69,28 @@ void Object::__put__(Context *ctx, const QString &name, void (*code)(Context *), __put__(ctx, name, Value::fromObject(ctx->engine->newNativeFunction(ctx, code))); } +Value Object::getValue(Context *ctx, PropertyDescriptor *p) const +{ + if (p->isData()) + return p->value; + if (!p->get) + return Value::undefinedValue(); + + p->get->call(ctx, Value::fromObject(const_cast(this)), 0, 0); + return ctx->result; +} + +bool Object::inplaceBinOp(Value rhs, Context *ctx, String *name, BinOp op) +{ + PropertyDescriptor to_fill; + PropertyDescriptor *pd = __getPropertyDescriptor__(ctx, name, &to_fill); + if (!pd) + return false; + Value result = op(getValue(ctx, pd), rhs, ctx); + __put__(ctx, name, result); + return true; +} + // Section 8.12.1 PropertyDescriptor *Object::__getOwnProperty__(Context *, String *name) { @@ -95,17 +117,9 @@ Value Object::__get__(Context *ctx, String *name) return Value::fromObject(prototype); PropertyDescriptor tmp; - if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name, &tmp)) { - if (p->isData()) - return p->value; - if (!p->get) - return Value::undefinedValue(); - FunctionObject *f = p->get->asFunctionObject(); - if (f) { - f->call(ctx, Value::fromObject(this), 0, 0); - return ctx->result; - } - } + if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name, &tmp)) + return getValue(ctx, p); + return Value::undefinedValue(); } diff --git a/qmljs_objects.h b/qmljs_objects.h index 8cac59daaa..ebb2291586 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -410,6 +410,9 @@ struct Object { // void __put__(Context *ctx, const QString &name, const Value &value); void __put__(Context *ctx, const QString &name, void (*code)(Context *), int count = 0); + + Value getValue(Context *ctx, PropertyDescriptor *p) const; + bool inplaceBinOp(Value rhs, Context *ctx, String *name, BinOp op); }; struct ForEachIteratorObject: Object { diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 16c940216d..5d6ea221d0 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -597,9 +597,9 @@ void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, Conte return; } } - String *s = index.toString(ctx); - assert(s); - __qmljs_inplace_bit_and_member(value, base, s, ctx); + String *name = index.toString(ctx); + assert(name); + obj->inplaceBinOp(value, ctx, name, __qmljs_bit_and); } void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, Context *ctx) @@ -614,9 +614,9 @@ void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, Contex return; } } - String *s = index.toString(ctx); - assert(s); - __qmljs_inplace_bit_or_member(value, base, s, ctx); + String *name = index.toString(ctx); + assert(name); + obj->inplaceBinOp(value, ctx, name, __qmljs_bit_or); } void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, Context *ctx) @@ -631,9 +631,9 @@ void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, Conte return; } } - String *s = index.toString(ctx); - assert(s); - __qmljs_inplace_bit_xor_member(value, base, s, ctx); + String *name = index.toString(ctx); + assert(name); + obj->inplaceBinOp(value, ctx, name, __qmljs_bit_xor); } void __qmljs_inplace_add_element(Value base, Value index, Value value, Context *ctx) @@ -648,9 +648,9 @@ void __qmljs_inplace_add_element(Value base, Value index, Value value, Context * return; } } - String *s = index.toString(ctx); - assert(s); - __qmljs_inplace_add_member(value, base, s, ctx); + String *name = index.toString(ctx); + assert(name); + obj->inplaceBinOp(value, ctx, name, __qmljs_add); } void __qmljs_inplace_sub_element(Value base, Value index, Value value, Context *ctx) @@ -665,9 +665,9 @@ void __qmljs_inplace_sub_element(Value base, Value index, Value value, Context * return; } } - String *s = index.toString(ctx); - assert(s); - __qmljs_inplace_sub_member(value, base, s, ctx); + String *name = index.toString(ctx); + assert(name); + obj->inplaceBinOp(value, ctx, name, __qmljs_sub); } void __qmljs_inplace_mul_element(Value base, Value index, Value value, Context *ctx) @@ -682,9 +682,9 @@ void __qmljs_inplace_mul_element(Value base, Value index, Value value, Context * return; } } - String *s = index.toString(ctx); - assert(s); - __qmljs_inplace_mul_member(value, base, s, ctx); + String *name = index.toString(ctx); + assert(name); + obj->inplaceBinOp(value, ctx, name, __qmljs_mul); } void __qmljs_inplace_div_element(Value base, Value index, Value value, Context *ctx) @@ -699,9 +699,9 @@ void __qmljs_inplace_div_element(Value base, Value index, Value value, Context * return; } } - String *s = index.toString(ctx); - assert(s); - __qmljs_inplace_div_member(value, base, s, ctx); + String *name = index.toString(ctx); + assert(name); + obj->inplaceBinOp(value, ctx, name, __qmljs_div); } void __qmljs_inplace_mod_element(Value base, Value index, Value value, Context *ctx) @@ -716,9 +716,9 @@ void __qmljs_inplace_mod_element(Value base, Value index, Value value, Context * return; } } - String *s = index.toString(ctx); - assert(s); - __qmljs_inplace_mod_member(value, base, s, ctx); + String *name = index.toString(ctx); + assert(name); + obj->inplaceBinOp(value, ctx, name, __qmljs_mod); } void __qmljs_inplace_shl_element(Value base, Value index, Value value, Context *ctx) @@ -733,9 +733,9 @@ void __qmljs_inplace_shl_element(Value base, Value index, Value value, Context * return; } } - String *s = index.toString(ctx); - assert(s); - __qmljs_inplace_shl_member(value, base, s, ctx); + String *name = index.toString(ctx); + assert(name); + obj->inplaceBinOp(value, ctx, name, __qmljs_shl); } void __qmljs_inplace_shr_element(Value base, Value index, Value value, Context *ctx) @@ -750,9 +750,9 @@ void __qmljs_inplace_shr_element(Value base, Value index, Value value, Context * return; } } - String *s = index.toString(ctx); - assert(s); - __qmljs_inplace_shr_member(value, base, s, ctx); + String *name = index.toString(ctx); + assert(name); + obj->inplaceBinOp(value, ctx, name, __qmljs_shr); } void __qmljs_inplace_ushr_element(Value base, Value index, Value value, Context *ctx) @@ -767,97 +767,75 @@ void __qmljs_inplace_ushr_element(Value base, Value index, Value value, Context return; } } - String *s = index.toString(ctx); - assert(s); - __qmljs_inplace_ushr_member(value, base, s, ctx); + String *name = index.toString(ctx); + assert(name); + obj->inplaceBinOp(value, ctx, name, __qmljs_ushr); } void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, Context *ctx) { - Object *o = base.objectValue(); - Value prop = o->__get__(ctx, name); - prop = __qmljs_bit_and(prop, value, ctx); - o->__put__(ctx, name, prop); + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, ctx, name, __qmljs_bit_and); } void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, Context *ctx) { - Object *o = base.objectValue(); - Value prop = o->__get__(ctx, name); - prop = __qmljs_bit_or(prop, value, ctx); - o->__put__(ctx, name, prop); + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, ctx, name, __qmljs_bit_or); } void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, Context *ctx) { - Object *o = base.objectValue(); - Value prop = o->__get__(ctx, name); - prop = __qmljs_bit_xor(prop, value, ctx); - o->__put__(ctx, name, prop); + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, ctx, name, __qmljs_bit_xor); } void __qmljs_inplace_add_member(Value value, Value base, String *name, Context *ctx) { - Object *o = base.objectValue(); - Value prop = o->__get__(ctx, name); - prop = __qmljs_add(prop, value, ctx); - o->__put__(ctx, name, prop); + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, ctx, name, __qmljs_add); } void __qmljs_inplace_sub_member(Value value, Value base, String *name, Context *ctx) { - Object *o = base.objectValue(); - Value prop = o->__get__(ctx, name); - prop = __qmljs_sub(prop, value, ctx); - o->__put__(ctx, name, prop); + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, ctx, name, __qmljs_sub); } void __qmljs_inplace_mul_member(Value value, Value base, String *name, Context *ctx) { - Object *o = base.objectValue(); - Value prop = o->__get__(ctx, name); - prop = __qmljs_mul(prop, value, ctx); - o->__put__(ctx, name, prop); + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, ctx, name, __qmljs_mul); } void __qmljs_inplace_div_member(Value value, Value base, String *name, Context *ctx) { - Object *o = base.objectValue(); - Value prop = o->__get__(ctx, name); - prop = __qmljs_div(prop, value, ctx); - o->__put__(ctx, name, prop); + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, ctx, name, __qmljs_div); } void __qmljs_inplace_mod_member(Value value, Value base, String *name, Context *ctx) { - Object *o = base.objectValue(); - Value prop = o->__get__(ctx, name); - prop = __qmljs_mod(prop, value, ctx); - o->__put__(ctx, name, prop); + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, ctx, name, __qmljs_mod); } void __qmljs_inplace_shl_member(Value value, Value base, String *name, Context *ctx) { - Object *o = base.objectValue(); - Value prop = o->__get__(ctx, name); - prop = __qmljs_shl(prop, value, ctx); - o->__put__(ctx, name, prop); + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, ctx, name, __qmljs_shl); } void __qmljs_inplace_shr_member(Value value, Value base, String *name, Context *ctx) { - Object *o = base.objectValue(); - Value prop = o->__get__(ctx, name); - prop = __qmljs_shr(prop, value, ctx); - o->__put__(ctx, name, prop); + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, ctx, name, __qmljs_shr); } void __qmljs_inplace_ushr_member(Value value, Value base, String *name, Context *ctx) { - Object *o = base.objectValue(); - Value prop = o->__get__(ctx, name); - prop = __qmljs_ushr(prop, value, ctx); - o->__put__(ctx, name, prop); + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, ctx, name, __qmljs_ushr); } String *__qmljs_string_from_utf8(Context *ctx, const char *s) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index a629b57b12..3916ec37a0 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -351,6 +351,8 @@ void __qmljs_delete_exception_handler(Context *context); Value __qmljs_get_exception(Context *context); // binary operators +typedef Value (*BinOp)(Value left, Value right, Context *ctx); + Value __qmljs_instanceof(Value left, Value right, Context *ctx); Value __qmljs_in(Value left, Value right, Context *ctx); Value __qmljs_bit_or(Value left, Value right, Context *ctx); -- cgit v1.2.3 From 29bc5f59a12710a8369ab0651418bf6f5e25f063 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 11 Nov 2012 00:08:44 +0100 Subject: Correct implementation of inplace binops Fix the last parts of the inplace bitops to do the correct thing. Change-Id: I748dde919082193c034cdc20e92cc568d8efa225 Reviewed-by: Erik Verbruggen --- qmljs_runtime.cpp | 77 ++++++++++++++++--------------------------------------- qmljs_runtime.h | 1 + 2 files changed, 23 insertions(+), 55 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 5d6ea221d0..e98b7ebbe8 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -257,6 +257,17 @@ PropertyDescriptor *Context::lookupPropertyDescriptor(String *name, PropertyDesc return 0; } +void Context::inplaceBitOp(Value value, String *name, BinOp op) +{ + for (Context *ctx = this; ctx; ctx = ctx->parent) { + if (Object *act = ctx->activation.asObject()) { + if (act->inplaceBinOp(value, this, name, op)) + return; + } + } + throwReferenceError(Value::fromString(name)); +} + void Context::throwError(Value value) { result = value; @@ -488,101 +499,57 @@ Value __qmljs_in(Value left, Value right, Context *ctx) void __qmljs_inplace_bit_and_name(Value value, String *name, Context *ctx) { - PropertyDescriptor tmp; - if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) - prop->value = __qmljs_bit_and(prop->value, value, ctx); - else - ctx->throwReferenceError(Value::fromString(name)); + ctx->inplaceBitOp(value, name, __qmljs_bit_and); } void __qmljs_inplace_bit_or_name(Value value, String *name, Context *ctx) { - PropertyDescriptor tmp; - if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) - prop->value = __qmljs_bit_or(prop->value, value, ctx); - else - ctx->throwReferenceError(Value::fromString(name)); + ctx->inplaceBitOp(value, name, __qmljs_bit_or); } void __qmljs_inplace_bit_xor_name(Value value, String *name, Context *ctx) { - PropertyDescriptor tmp; - if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) - prop->value = __qmljs_bit_xor(prop->value, value, ctx); - else - ctx->throwReferenceError(Value::fromString(name)); + ctx->inplaceBitOp(value, name, __qmljs_bit_xor); } void __qmljs_inplace_add_name(Value value, String *name, Context *ctx) { - PropertyDescriptor tmp; - if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) - prop->value = __qmljs_add(prop->value, value, ctx); - else - ctx->throwReferenceError(Value::fromString(name)); + ctx->inplaceBitOp(value, name, __qmljs_add); } void __qmljs_inplace_sub_name(Value value, String *name, Context *ctx) { - PropertyDescriptor tmp; - if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) - prop->value = __qmljs_sub(prop->value, value, ctx); - else - ctx->throwReferenceError(Value::fromString(name)); + ctx->inplaceBitOp(value, name, __qmljs_sub); } void __qmljs_inplace_mul_name(Value value, String *name, Context *ctx) { - PropertyDescriptor tmp; - if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) - prop->value = __qmljs_mul(prop->value, value, ctx); - else - ctx->throwReferenceError(Value::fromString(name)); + ctx->inplaceBitOp(value, name, __qmljs_mul); } void __qmljs_inplace_div_name(Value value, String *name, Context *ctx) { - PropertyDescriptor tmp; - if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) - prop->value = __qmljs_div(prop->value, value, ctx); - else - ctx->throwReferenceError(Value::fromString(name)); + ctx->inplaceBitOp(value, name, __qmljs_div); } void __qmljs_inplace_mod_name(Value value, String *name, Context *ctx) { - PropertyDescriptor tmp; - if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) - prop->value = __qmljs_mod(prop->value, value, ctx); - else - ctx->throwReferenceError(Value::fromString(name)); + ctx->inplaceBitOp(value, name, __qmljs_mod); } void __qmljs_inplace_shl_name(Value value, String *name, Context *ctx) { - PropertyDescriptor tmp; - if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) - prop->value = __qmljs_shl(prop->value, value, ctx); - else - ctx->throwReferenceError(Value::fromString(name)); + ctx->inplaceBitOp(value, name, __qmljs_shl); } void __qmljs_inplace_shr_name(Value value, String *name, Context *ctx) { - PropertyDescriptor tmp; - if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) - prop->value = __qmljs_shr(prop->value, value, ctx); - else - ctx->throwReferenceError(Value::fromString(name)); + ctx->inplaceBitOp(value, name, __qmljs_shr); } void __qmljs_inplace_ushr_name(Value value, String *name, Context *ctx) { - PropertyDescriptor tmp; - if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) - prop->value = __qmljs_ushr(prop->value, value, ctx); - else - ctx->throwReferenceError(Value::fromString(name)); + ctx->inplaceBitOp(value, name, __qmljs_ushr); } void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, Context *ctx) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 3916ec37a0..5cea79f13e 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -573,6 +573,7 @@ struct Context { int calledAsConstructor; PropertyDescriptor *lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp); + void inplaceBitOp(Value value, String *name, BinOp op); inline Value argument(unsigned int index = 0) { -- cgit v1.2.3 From b651c293b105bd4316d94d6321688406efbc0f20 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 11 Nov 2012 21:23:59 +0100 Subject: These methods are actually used now. Change-Id: Ic0d43902fec5d3b752678eadfbccb9f71e21d895 Reviewed-by: Erik Verbruggen --- qmljs_runtime.h | 1 - 1 file changed, 1 deletion(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 5cea79f13e..85ec5ea4c1 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -337,7 +337,6 @@ Value __qmljs_uminus(Value value, Context *ctx); Value __qmljs_compl(Value value, Context *ctx); Value __qmljs_not(Value value, Context *ctx); -/* ### these 4 methods are apparently unused right now */ Value __qmljs_delete_subscript(Context *ctx, Value base, Value index); Value __qmljs_delete_member(Context *ctx, Value base, String *name); Value __qmljs_delete_property(Context *ctx, String *name); -- cgit v1.2.3 From c5b258c21dea10e69e6977920c14c96368384933 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 11 Nov 2012 21:56:38 +0100 Subject: Clean up inplace binary operations for subscripts. Remove a lot of duplicated code, and make it a lot easier to implement JS compliant semantics. Change-Id: Ic5ecb58d34a5df8fe05e86c5f906d70c50c51b54 Reviewed-by: Erik Verbruggen --- qmljs_objects.cpp | 19 ++++++++ qmljs_objects.h | 3 ++ qmljs_runtime.cpp | 143 +++++------------------------------------------------- 3 files changed, 33 insertions(+), 132 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 353f7f917b..db5e23776d 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -91,6 +91,13 @@ bool Object::inplaceBinOp(Value rhs, Context *ctx, String *name, BinOp op) return true; } +bool Object::inplaceBinOp(Value rhs, Value index, BinOp op, Context *ctx) +{ + String *name = index.toString(ctx); + assert(name); + return inplaceBinOp(rhs, ctx, name, op); +} + // Section 8.12.1 PropertyDescriptor *Object::__getOwnProperty__(Context *, String *name) { @@ -330,6 +337,18 @@ Value ArrayObject::__get__(Context *ctx, String *name) return Object::__get__(ctx, name); } +bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, Context *ctx) +{ + if (index.isNumber()) { + const quint32 idx = index.toUInt32(ctx); + Value v = value.at(idx); + v = op(v, rhs, ctx); + value.assign(idx, v); + return true; + } + return Object::inplaceBinOp(rhs, index, op, ctx); +} + bool FunctionObject::hasInstance(Context *ctx, const Value &value) { if (! value.isObject()) { diff --git a/qmljs_objects.h b/qmljs_objects.h index ebb2291586..6fae34d79f 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -413,6 +413,7 @@ struct Object { Value getValue(Context *ctx, PropertyDescriptor *p) const; bool inplaceBinOp(Value rhs, Context *ctx, String *name, BinOp op); + virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, Context *ctx); }; struct ForEachIteratorObject: Object { @@ -460,6 +461,8 @@ struct ArrayObject: Object { virtual QString className() { return QStringLiteral("Array"); } virtual ArrayObject *asArrayObject() { return this; } virtual Value __get__(Context *ctx, String *name); + + virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, Context *ctx); }; struct FunctionObject: Object { diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index e98b7ebbe8..4821b456ae 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -555,188 +555,67 @@ void __qmljs_inplace_ushr_name(Value value, String *name, Context *ctx) void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, Context *ctx) { Object *obj = base.toObject(ctx).objectValue(); - if (ArrayObject *a = obj->asArrayObject()) { - if (index.isNumber()) { - const quint32 idx = index.toUInt32(ctx); - Value v = a->value.at(idx); - v = __qmljs_bit_and(v, value, ctx); - a->value.assign(idx, v); - return; - } - } - String *name = index.toString(ctx); - assert(name); - obj->inplaceBinOp(value, ctx, name, __qmljs_bit_and); + obj->inplaceBinOp(value, index, __qmljs_bit_and, ctx); } void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, Context *ctx) { Object *obj = base.toObject(ctx).objectValue(); - if (ArrayObject *a = obj->asArrayObject()) { - if (index.isNumber()) { - const quint32 idx = index.toUInt32(ctx); - Value v = a->value.at(idx); - v = __qmljs_bit_or(v, value, ctx); - a->value.assign(idx, v); - return; - } - } - String *name = index.toString(ctx); - assert(name); - obj->inplaceBinOp(value, ctx, name, __qmljs_bit_or); + obj->inplaceBinOp(value, index, __qmljs_bit_or, ctx); } void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, Context *ctx) { Object *obj = base.toObject(ctx).objectValue(); - if (ArrayObject *a = obj->asArrayObject()) { - if (index.isNumber()) { - const quint32 idx = index.toUInt32(ctx); - Value v = a->value.at(idx); - v = __qmljs_bit_xor(v, value, ctx); - a->value.assign(idx, v); - return; - } - } - String *name = index.toString(ctx); - assert(name); - obj->inplaceBinOp(value, ctx, name, __qmljs_bit_xor); + obj->inplaceBinOp(value, index, __qmljs_bit_xor, ctx); } void __qmljs_inplace_add_element(Value base, Value index, Value value, Context *ctx) { Object *obj = base.toObject(ctx).objectValue(); - if (ArrayObject *a = obj->asArrayObject()) { - if (index.isNumber()) { - const quint32 idx = index.toUInt32(ctx); - Value v = a->value.at(idx); - v = __qmljs_add(v, value, ctx); - a->value.assign(idx, v); - return; - } - } - String *name = index.toString(ctx); - assert(name); - obj->inplaceBinOp(value, ctx, name, __qmljs_add); + obj->inplaceBinOp(value, index, __qmljs_add, ctx); } void __qmljs_inplace_sub_element(Value base, Value index, Value value, Context *ctx) { Object *obj = base.toObject(ctx).objectValue(); - if (ArrayObject *a = obj->asArrayObject()) { - if (index.isNumber()) { - const quint32 idx = index.toUInt32(ctx); - Value v = a->value.at(idx); - v = __qmljs_sub(v, value, ctx); - a->value.assign(idx, v); - return; - } - } - String *name = index.toString(ctx); - assert(name); - obj->inplaceBinOp(value, ctx, name, __qmljs_sub); + obj->inplaceBinOp(value, index, __qmljs_sub, ctx); } void __qmljs_inplace_mul_element(Value base, Value index, Value value, Context *ctx) { Object *obj = base.toObject(ctx).objectValue(); - if (ArrayObject *a = obj->asArrayObject()) { - if (index.isNumber()) { - const quint32 idx = index.toUInt32(ctx); - Value v = a->value.at(idx); - v = __qmljs_mul(v, value, ctx); - a->value.assign(idx, v); - return; - } - } - String *name = index.toString(ctx); - assert(name); - obj->inplaceBinOp(value, ctx, name, __qmljs_mul); + obj->inplaceBinOp(value, index, __qmljs_mul, ctx); } void __qmljs_inplace_div_element(Value base, Value index, Value value, Context *ctx) { Object *obj = base.toObject(ctx).objectValue(); - if (ArrayObject *a = obj->asArrayObject()) { - if (index.isNumber()) { - const quint32 idx = index.toUInt32(ctx); - Value v = a->value.at(idx); - v = __qmljs_div(v, value, ctx); - a->value.assign(idx, v); - return; - } - } - String *name = index.toString(ctx); - assert(name); - obj->inplaceBinOp(value, ctx, name, __qmljs_div); + obj->inplaceBinOp(value, index, __qmljs_div, ctx); } void __qmljs_inplace_mod_element(Value base, Value index, Value value, Context *ctx) { Object *obj = base.toObject(ctx).objectValue(); - if (ArrayObject *a = obj->asArrayObject()) { - if (index.isNumber()) { - const quint32 idx = index.toUInt32(ctx); - Value v = a->value.at(idx); - v = __qmljs_mod(v, value, ctx); - a->value.assign(idx, v); - return; - } - } - String *name = index.toString(ctx); - assert(name); - obj->inplaceBinOp(value, ctx, name, __qmljs_mod); + obj->inplaceBinOp(value, index, __qmljs_mod, ctx); } void __qmljs_inplace_shl_element(Value base, Value index, Value value, Context *ctx) { Object *obj = base.toObject(ctx).objectValue(); - if (ArrayObject *a = obj->asArrayObject()) { - if (index.isNumber()) { - const quint32 idx = index.toUInt32(ctx); - Value v = a->value.at(idx); - v = __qmljs_shl(v, value, ctx); - a->value.assign(idx, v); - return; - } - } - String *name = index.toString(ctx); - assert(name); - obj->inplaceBinOp(value, ctx, name, __qmljs_shl); + obj->inplaceBinOp(value, index, __qmljs_shl, ctx); } void __qmljs_inplace_shr_element(Value base, Value index, Value value, Context *ctx) { Object *obj = base.toObject(ctx).objectValue(); - if (ArrayObject *a = obj->asArrayObject()) { - if (index.isNumber()) { - const quint32 idx = index.toUInt32(ctx); - Value v = a->value.at(idx); - v = __qmljs_shr(v, value, ctx); - a->value.assign(idx, v); - return; - } - } - String *name = index.toString(ctx); - assert(name); - obj->inplaceBinOp(value, ctx, name, __qmljs_shr); + obj->inplaceBinOp(value, index, __qmljs_shr, ctx); } void __qmljs_inplace_ushr_element(Value base, Value index, Value value, Context *ctx) { Object *obj = base.toObject(ctx).objectValue(); - if (ArrayObject *a = obj->asArrayObject()) { - if (index.isNumber()) { - const quint32 idx = index.toUInt32(ctx); - Value v = a->value.at(idx); - v = __qmljs_ushr(v, value, ctx); - a->value.assign(idx, v); - return; - } - } - String *name = index.toString(ctx); - assert(name); - obj->inplaceBinOp(value, ctx, name, __qmljs_ushr); + obj->inplaceBinOp(value, index, __qmljs_ushr, ctx); } void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, Context *ctx) -- cgit v1.2.3 From bc42b47a117df146bf57a0bd6ec43acd515de4d6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 11 Nov 2012 22:08:39 +0100 Subject: Better argument ordering. Change-Id: I1d47ce7bd2cf27bc43a0e9afbfb2914296c9d704 Reviewed-by: Erik Verbruggen --- qmljs_objects.cpp | 4 ++-- qmljs_objects.h | 2 +- qmljs_runtime.cpp | 24 ++++++++++++------------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index db5e23776d..167dc812f2 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -80,7 +80,7 @@ Value Object::getValue(Context *ctx, PropertyDescriptor *p) const return ctx->result; } -bool Object::inplaceBinOp(Value rhs, Context *ctx, String *name, BinOp op) +bool Object::inplaceBinOp(Value rhs, String *name, BinOp op, Context *ctx) { PropertyDescriptor to_fill; PropertyDescriptor *pd = __getPropertyDescriptor__(ctx, name, &to_fill); @@ -95,7 +95,7 @@ bool Object::inplaceBinOp(Value rhs, Value index, BinOp op, Context *ctx) { String *name = index.toString(ctx); assert(name); - return inplaceBinOp(rhs, ctx, name, op); + return inplaceBinOp(rhs, name, op, ctx); } // Section 8.12.1 diff --git a/qmljs_objects.h b/qmljs_objects.h index 6fae34d79f..139ea10f77 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -412,7 +412,7 @@ struct Object { void __put__(Context *ctx, const QString &name, void (*code)(Context *), int count = 0); Value getValue(Context *ctx, PropertyDescriptor *p) const; - bool inplaceBinOp(Value rhs, Context *ctx, String *name, BinOp op); + bool inplaceBinOp(Value rhs, String *name, BinOp op, Context *ctx); virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, Context *ctx); }; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 4821b456ae..d70a8517bb 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -261,7 +261,7 @@ void Context::inplaceBitOp(Value value, String *name, BinOp op) { for (Context *ctx = this; ctx; ctx = ctx->parent) { if (Object *act = ctx->activation.asObject()) { - if (act->inplaceBinOp(value, this, name, op)) + if (act->inplaceBinOp(value, name, op, this)) return; } } @@ -621,67 +621,67 @@ void __qmljs_inplace_ushr_element(Value base, Value index, Value value, Context void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, ctx, name, __qmljs_bit_and); + o->inplaceBinOp(value, name, __qmljs_bit_and, ctx); } void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, ctx, name, __qmljs_bit_or); + o->inplaceBinOp(value, name, __qmljs_bit_or, ctx); } void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, ctx, name, __qmljs_bit_xor); + o->inplaceBinOp(value, name, __qmljs_bit_xor, ctx); } void __qmljs_inplace_add_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, ctx, name, __qmljs_add); + o->inplaceBinOp(value, name, __qmljs_add, ctx); } void __qmljs_inplace_sub_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, ctx, name, __qmljs_sub); + o->inplaceBinOp(value, name, __qmljs_sub, ctx); } void __qmljs_inplace_mul_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, ctx, name, __qmljs_mul); + o->inplaceBinOp(value, name, __qmljs_mul, ctx); } void __qmljs_inplace_div_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, ctx, name, __qmljs_div); + o->inplaceBinOp(value, name, __qmljs_div, ctx); } void __qmljs_inplace_mod_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, ctx, name, __qmljs_mod); + o->inplaceBinOp(value, name, __qmljs_mod, ctx); } void __qmljs_inplace_shl_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, ctx, name, __qmljs_shl); + o->inplaceBinOp(value, name, __qmljs_shl, ctx); } void __qmljs_inplace_shr_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, ctx, name, __qmljs_shr); + o->inplaceBinOp(value, name, __qmljs_shr, ctx); } void __qmljs_inplace_ushr_member(Value value, Value base, String *name, Context *ctx) { Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, ctx, name, __qmljs_ushr); + o->inplaceBinOp(value, name, __qmljs_ushr, ctx); } String *__qmljs_string_from_utf8(Context *ctx, const char *s) -- cgit v1.2.3 From fa3b6bd3fc7f2cc7060286b7c8ad14450533a5a6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 25 Oct 2012 16:31:12 +0200 Subject: Allow Const's as operands to Binop This allows us to use expressions such as %x = %y + const in the IR. This still requires an implementation for moth. Change-Id: I134e96ddad08bcbe4f3ea5fa27c5338a96acac80 Reviewed-by: Erik Verbruggen --- moth/qv4isel_moth.cpp | 8 +++--- qmljs_runtime.h | 15 ++++++---- qv4codegen.cpp | 10 ++++--- qv4ir.cpp | 2 +- qv4ir_p.h | 8 +++--- qv4isel_masm.cpp | 14 ++++----- qv4isel_masm_p.h | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 110 insertions(+), 27 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 3ab6e051f4..23cef69bc0 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -394,8 +394,8 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Binop *b = s->source->asBinop()) { Instruction::Binop binop; binop.alu = aluOpFunction(b->op); - binop.lhsTempIndex = b->left->index; - binop.rhsTempIndex = b->right->index; +// binop.lhsTempIndex = b->left->index; +// binop.rhsTempIndex = b->right->index; addInstruction(binop); } else if (IR::Call *c = s->source->asCall()) { if (c->base->asName()) { @@ -554,8 +554,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) } else if (IR::Binop *b = s->cond->asBinop()) { Instruction::Binop binop; binop.alu = aluOpFunction(b->op); - binop.lhsTempIndex = b->left->index; - binop.rhsTempIndex = b->right->index; +// binop.lhsTempIndex = b->left->index; +// binop.rhsTempIndex = b->right->index; addInstruction(binop); } else { Q_UNREACHABLE(); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 85ec5ea4c1..751fd128c2 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -228,6 +228,9 @@ struct Value static inline bool integerCompatible(Value a, Value b) { return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; } + static inline bool bothDouble(Value a, Value b) { + return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask; + } inline bool tryIntegerConversion() { bool b = isConvertibleToInt(); if (b) @@ -908,7 +911,7 @@ inline Value __qmljs_add(Value left, Value right, Context *ctx) if (Value::integerCompatible(left, right)) return add_int32(left.integerValue(), right.integerValue()); - if (left.isDouble() & right.isDouble()) + if (Value::bothDouble(left, right)) return Value::fromDouble(left.doubleValue() + right.doubleValue()); return __qmljs_add_helper(left, right, ctx); @@ -1064,7 +1067,7 @@ inline Bool __qmljs_cmp_gt(Value left, Value right, Context *ctx) if (Value::integerCompatible(left, right)) return left.integerValue() > right.integerValue(); - if (left.isDouble() && right.isDouble()) { + if (Value::bothDouble(left, right)) { return left.doubleValue() > right.doubleValue(); } else if (left.isString() && right.isString()) { return __qmljs_string_compare(ctx, right.stringValue(), left.stringValue()); @@ -1084,7 +1087,7 @@ inline Bool __qmljs_cmp_lt(Value left, Value right, Context *ctx) if (Value::integerCompatible(left, right)) return left.integerValue() < right.integerValue(); - if (left.isDouble() && right.isDouble()) { + if (Value::bothDouble(left, right)) { return left.doubleValue() < right.doubleValue(); } else if (left.isString() && right.isString()) { return __qmljs_string_compare(ctx, left.stringValue(), right.stringValue()); @@ -1104,7 +1107,7 @@ inline Bool __qmljs_cmp_ge(Value left, Value right, Context *ctx) if (Value::integerCompatible(left, right)) return left.integerValue() >= right.integerValue(); - if (left.isDouble() && right.isDouble()) { + if (Value::bothDouble(left, right)) { return left.doubleValue() >= right.doubleValue(); } else if (left.isString() && right.isString()) { return !__qmljs_string_compare(ctx, left.stringValue(), right.stringValue()); @@ -1124,7 +1127,7 @@ inline Bool __qmljs_cmp_le(Value left, Value right, Context *ctx) if (Value::integerCompatible(left, right)) return left.integerValue() <= right.integerValue(); - if (left.isDouble() && right.isDouble()) { + if (Value::bothDouble(left, right)) { return left.doubleValue() <= right.doubleValue(); } else if (left.isString() && right.isString()) { return !__qmljs_string_compare(ctx, right.stringValue(), left.stringValue()); @@ -1140,7 +1143,7 @@ inline Bool __qmljs_cmp_eq(Value left, Value right, Context *ctx) TRACE2(left, right); // need to test for doubles first as NaN != NaN - if (left.isDouble() && right.isDouble()) + if (Value::bothDouble(left, right)) return left.doubleValue() == right.doubleValue(); if (left.val == right.val) return true; diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 8f8db30349..0276cd3686 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -489,20 +489,22 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) } } - if (!left->asTemp()) { + if (!left->asTemp() && !left->asConst()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), left); left = _block->TEMP(t); } - if (!right->asTemp()) { + if (!right->asTemp() && !right->asConst()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), right); right = _block->TEMP(t); } - assert(left->asTemp() && right->asTemp()); - return _block->BINOP(op, left->asTemp(), right->asTemp()); + assert(left->asTemp() || left->asConst()); + assert(right->asTemp() || right->asConst()); + + return _block->BINOP(op, left, right); } IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args) diff --git a/qv4ir.cpp b/qv4ir.cpp index 21b0fe7193..811c8024a2 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -477,7 +477,7 @@ Expr *BasicBlock::UNOP(AluOp op, Temp *expr) return e; } -Expr *BasicBlock::BINOP(AluOp op, Temp *left, Temp *right) +Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) { Binop *e = function->New(); e->init(op, left, right); diff --git a/qv4ir_p.h b/qv4ir_p.h index 9e004a518a..f736b02ef6 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -341,10 +341,10 @@ struct Unop: Expr { struct Binop: Expr { AluOp op; - Temp *left; - Temp *right; + Expr *left; // Temp or Const + Expr *right; // Temp or Const - void init(AluOp op, Temp *left, Temp *right) + void init(AluOp op, Expr *left, Expr *right) { this->op = op; this->left = left; @@ -682,7 +682,7 @@ struct BasicBlock { Closure *CLOSURE(Function *function); Expr *UNOP(AluOp op, Temp *expr); - Expr *BINOP(AluOp op, Temp *left, Temp *right); + Expr *BINOP(AluOp op, Expr *left, Expr *right); Expr *CALL(Expr *base, ExprList *args = 0); Expr *NEW(Expr *base, ExprList *args = 0); Expr *SUBSCRIPT(Temp *base, Temp *index); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 2deab59719..250d3241a3 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -440,9 +440,8 @@ void InstructionSelection::visitMove(IR::Move *s) return; } } else if (IR::Binop *b = s->source->asBinop()) { - IR::Temp *l = b->left->asTemp(); - IR::Temp *r = b->right->asTemp(); - if (l && r) { + if ((b->left->asTemp() || b->left->asConst()) && + (b->right->asTemp() || b->right->asConst())) { Value (*op)(const Value, const Value, Context *) = 0; const char* opName = 0; @@ -485,7 +484,7 @@ void InstructionSelection::visitMove(IR::Move *s) } if (op) { - generateFunctionCallImp(t, opName, op, l, r, ContextRegister); + generateFunctionCallImp(t, opName, op, b->left, b->right, ContextRegister); } return; } @@ -670,9 +669,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) jumpToBlock(s->iffalse); return; } else if (IR::Binop *b = s->cond->asBinop()) { - IR::Temp *l = b->left->asTemp(); - IR::Temp *r = b->right->asTemp(); - if (l && r) { + if ((b->left->asTemp() || b->left->asConst()) && + (b->right->asTemp() || b->right->asConst())) { Bool (*op)(const Value, const Value, Context *ctx) = 0; const char *opName = 0; switch (b->op) { @@ -689,7 +687,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) case IR::OpIn: setOp(op, opName, __qmljs_cmp_in); break; } // switch - generateFunctionCallImp(ReturnValueRegister, opName, op, l, r, ContextRegister); + generateFunctionCallImp(ReturnValueRegister, opName, op, b->left, b->right, ContextRegister); Jump target = branch32(NotEqual, ReturnValueRegister, TrustedImm32(0)); _patches[s->iftrue].append(target); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 32f71ae7db..a4ab604e90 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -283,6 +283,45 @@ private: load64(addr, dest); } } + + void loadArgument(IR::Const* c, RegisterID dest) + { + VM::Value v; + switch (c->type) { + case IR::NullType: + v = VM::Value::nullValue(); + break; + case IR::UndefinedType: + v = VM::Value::undefinedValue(); + break; + case IR::BoolType: + v = VM::Value::fromBoolean(c->value != 0); + break; + case IR::NumberType: { + int ival = (int)c->value; + if (ival == c->value) { + v = VM::Value::fromInt32(ival); + } else { + v = VM::Value::fromDouble(c->value); + } + } + } + move(TrustedImm64(v.val), dest); + } + + void loadArgument(IR::Expr* expr, RegisterID dest) + { + if (!expr) { + VM::Value undefined = VM::Value::undefinedValue(); + move(TrustedImm64(undefined.val), dest); + } else if (expr->asTemp()){ + loadArgument(expr->asTemp(), dest); + } else if (expr->asConst()) { + loadArgument(expr->asConst(), dest); + } else { + assert(!"unimplemented expression type in loadArgument"); + } + } #endif void loadArgument(VM::String* string, RegisterID dest) @@ -363,6 +402,45 @@ private: } } + void push(IR::Const* c) + { + VM::Value v; + switch (c->type) { + case IR::NullType: + v = VM::Value::nullValue(); + break; + case IR::UndefinedType: + v = VM::Value::undefinedValue(); + break; + case IR::BoolType: + v = VM::Value::fromBoolean(c->value != 0); + break; + case IR::NumberType: { + int ival = (int)c->value; + if (ival == c->value) { + v = VM::Value::fromInt32(ival); + } else { + v = VM::Value::fromDouble(c->value); + } + } + } + push(v); + } + + void push(IR::Expr* e) + { + if (!e) { + VM::Value undefined = VM::Value::undefinedValue(); + push(undefined); + } else if (IR::Const *c = e->asConst()) + push(c); + else if (IR::Temp *t = e->asTemp()) { + push(t); + } else { + assert(!"Trying to push an expression that is not a Temp or Const"); + } + } + void push(TrustedImmPtr ptr) { move(TrustedImmPtr(ptr), ScratchRegister); @@ -400,6 +478,8 @@ private: { return RegisterSize; } static inline int sizeOfArgument(IR::Temp*) { return 8; } // Size of value + static inline int sizeOfArgument(IR::Expr*) + { return 8; } // Size of value static inline int sizeOfArgument(const Pointer&) { return sizeof(void*); } static inline int sizeOfArgument(VM::String* string) -- cgit v1.2.3 From d5a17daf7bed4b4507676d0058486d7bcb5972bc Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 11 Nov 2012 22:29:25 +0100 Subject: Smaller 64 bit optimisation Change-Id: I78311310c6f986bf187e3923d1fb71e09f4569fb Reviewed-by: Erik Verbruggen --- qmljs_runtime.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 751fd128c2..12690357ca 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -225,12 +225,23 @@ struct Value inline Value toObject(Context *ctx) const; inline bool isPrimitive() const { return !isObject(); } +#if CPU(X86_64) + static inline bool integerCompatible(Value a, Value b) { + const quint64 mask = quint64(ConvertibleToInt) << 32; + return ((a.val & b.val) & mask) == mask; + } + static inline bool bothDouble(Value a, Value b) { + const quint64 mask = quint64(NotDouble_Mask) << 32; + return ((a.val | b.val) & mask) != mask; + } +#else static inline bool integerCompatible(Value a, Value b) { return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; } static inline bool bothDouble(Value a, Value b) { return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask; } +#endif inline bool tryIntegerConversion() { bool b = isConvertibleToInt(); if (b) -- cgit v1.2.3 From 012527ba91ab7688d20c27755ae1112f5113a65f Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 12 Nov 2012 09:39:06 +0100 Subject: Allow Const's as operands to Binop in moth. Change-Id: Ic0954c8170bc720a02fa88f3ab50112bff4767a7 Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 11 +++++++-- moth/qv4isel_moth.cpp | 24 +++++++++++++++---- moth/qv4vme_moth.cpp | 4 +++- qv4isel_masm_p.h | 22 ++---------------- qv4isel_util_p.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ v4.pro | 3 ++- 6 files changed, 98 insertions(+), 28 deletions(-) create mode 100644 qv4isel_util_p.h diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 8073aba01f..3227b6c3cc 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -59,6 +59,11 @@ namespace Moth { union Instr { + union ValueOrTemp { + VM::Value value; + int tempIndex; + }; + enum Type { FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) }; @@ -180,8 +185,10 @@ union Instr }; struct instr_binop { MOTH_INSTR_HEADER - int lhsTempIndex; - int rhsTempIndex; + ValueOrTemp lhs; + ValueOrTemp rhs; + unsigned lhsIsTemp:1; + unsigned rhsIsTemp:1; VM::Value (*alu)(const VM::Value , const VM::Value, VM::Context *); }; struct instr_loadThis { diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 23cef69bc0..7af4a31926 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -1,3 +1,4 @@ +#include "qv4isel_util_p.h" #include "qv4isel_moth_p.h" #include "qv4vme_moth_p.h" @@ -5,9 +6,24 @@ using namespace QQmlJS; using namespace QQmlJS::Moth; namespace { + QTextStream qout(stderr, QIODevice::WriteOnly); + +static unsigned toValueOrTemp(IR::Expr *e, Instr::ValueOrTemp &vot) +{ + if (IR::Const *c = e->asConst()) { + vot.value = convertToValue(c); + return 0; + } else if (IR::Temp *t = e->asTemp()) { + vot.tempIndex = t->index; + return 1; + } else { + Q_UNREACHABLE(); + } } +} // anonymous namespace + InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module * /*module*/, uchar *code) : _engine(engine), _code(code), _ccode(code) @@ -394,8 +410,8 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Binop *b = s->source->asBinop()) { Instruction::Binop binop; binop.alu = aluOpFunction(b->op); -// binop.lhsTempIndex = b->left->index; -// binop.rhsTempIndex = b->right->index; + binop.lhsIsTemp = toValueOrTemp(b->left, binop.lhs); + binop.rhsIsTemp = toValueOrTemp(b->right, binop.rhs); addInstruction(binop); } else if (IR::Call *c = s->source->asCall()) { if (c->base->asName()) { @@ -554,8 +570,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) } else if (IR::Binop *b = s->cond->asBinop()) { Instruction::Binop binop; binop.alu = aluOpFunction(b->op); -// binop.lhsTempIndex = b->left->index; -// binop.rhsTempIndex = b->right->index; + binop.lhsIsTemp = toValueOrTemp(b->left, binop.lhs); + binop.rhsIsTemp = toValueOrTemp(b->right, binop.rhs); addInstruction(binop); } else { Q_UNREACHABLE(); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 82a61ef865..8a9638d347 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -234,7 +234,9 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_END_INSTR(Unop) MOTH_BEGIN_INSTR(Binop) - tempRegister = instr.alu(TEMP(instr.lhsTempIndex), TEMP(instr.rhsTempIndex), context); + VM::Value lhs = instr.lhsIsTemp ? TEMP(instr.lhs.tempIndex) : instr.lhs.value; + VM::Value rhs = instr.rhsIsTemp ? TEMP(instr.rhs.tempIndex) : instr.rhs.value; + tempRegister = instr.alu(lhs, rhs, context); MOTH_END_INSTR(Binop) MOTH_BEGIN_INSTR(Ret) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index a4ab604e90..e38ce608f5 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -42,6 +42,7 @@ #define QV4ISEL_MASM_P_H #include "qv4ir_p.h" +#include "qv4isel_util_p.h" #include "qmljs_objects.h" #include "qmljs_runtime.h" @@ -286,26 +287,7 @@ private: void loadArgument(IR::Const* c, RegisterID dest) { - VM::Value v; - switch (c->type) { - case IR::NullType: - v = VM::Value::nullValue(); - break; - case IR::UndefinedType: - v = VM::Value::undefinedValue(); - break; - case IR::BoolType: - v = VM::Value::fromBoolean(c->value != 0); - break; - case IR::NumberType: { - int ival = (int)c->value; - if (ival == c->value) { - v = VM::Value::fromInt32(ival); - } else { - v = VM::Value::fromDouble(c->value); - } - } - } + VM::Value v = convertToValue(c); move(TrustedImm64(v.val), dest); } diff --git a/qv4isel_util_p.h b/qv4isel_util_p.h new file mode 100644 index 0000000000..ff6cc210ec --- /dev/null +++ b/qv4isel_util_p.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QV4ISEL_UTIL_P_H +#define QV4ISEL_UTIL_P_H + +#include "qmljs_runtime.h" +#include "qv4ir_p.h" + +namespace QQmlJS { + +inline VM::Value convertToValue(IR::Const *c) +{ + switch (c->type) { + case IR::NullType: + return VM::Value::nullValue(); + case IR::UndefinedType: + return VM::Value::undefinedValue(); + case IR::BoolType: + return VM::Value::fromBoolean(c->value != 0); + case IR::NumberType: { + int ival = (int)c->value; + if (ival == c->value) { + return VM::Value::fromInt32(ival); + } else { + return VM::Value::fromDouble(c->value); + } + } + default: + Q_UNREACHABLE(); + } +} + +} // namespace QQmlJS + +#endif // QV4ISEL_UTIL_P_H diff --git a/v4.pro b/v4.pro index 701e503639..bc8ada4ccf 100644 --- a/v4.pro +++ b/v4.pro @@ -30,7 +30,8 @@ HEADERS += \ qv4syntaxchecker_p.h \ qv4ecmaobjects_p.h \ qv4array_p.h \ - qv4isel_masm_p.h + qv4isel_masm_p.h \ + qv4isel_util_p.h llvm { -- cgit v1.2.3 From cdfbf401e173c39cbef9011a241b736e9f120d36 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 12 Nov 2012 12:22:22 +0100 Subject: Changed the LLVM backend to also support the LLVM JIT. This is useful for quick LLVM codegen testing. Change-Id: I45778371375b903e154222c47b15411d08085ae5 Reviewed-by: Lars Knoll --- main.cpp | 80 ++++++++++++------- qmljs_math.h | 2 +- qmljs_runtime.h | 6 ++ qv4_llvm_p.h | 2 +- qv4isel_llvm.cpp | 240 +++++++++++++++++++++++++++++++++++-------------------- qv4isel_llvm_p.h | 8 +- v4.pro | 2 +- 7 files changed, 221 insertions(+), 119 deletions(-) diff --git a/main.cpp b/main.cpp index 5bb61a6090..623ee20721 100644 --- a/main.cpp +++ b/main.cpp @@ -131,7 +131,35 @@ struct TestHarnessError: FunctionObject } // builtins #ifndef QMLJS_NO_LLVM -void compile(const QString &fileName, const QString &source) +int executeLLVMCode(void *codePtr) +{ + using namespace QQmlJS; + + if (!codePtr) + return EXIT_FAILURE; + void (*code)(VM::Context *) = (void (*)(VM::Context *)) codePtr; + + VM::ExecutionEngine vm; + VM::Context *ctx = vm.rootContext; + + QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); + globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), + QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); + + void * buf = __qmljs_create_exception_handler(ctx); + if (setjmp(*(jmp_buf *)buf)) { + if (VM::ErrorObject *e = ctx->result.asErrorObject()) + std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; + else + std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; + return EXIT_FAILURE; + } + + code(ctx); + return EXIT_SUCCESS; +} + +int compile(const QString &fileName, const QString &source, bool runInJit) { using namespace QQmlJS; @@ -149,7 +177,7 @@ void compile(const QString &fileName, const QString &source) } if (!parsed) - return; + return EXIT_FAILURE; using namespace AST; Program *program = AST::cast(parser.rootNode()); @@ -157,16 +185,19 @@ void compile(const QString &fileName, const QString &source) Codegen cg; /*IR::Function *globalCode =*/ cg(program, &module); - compileWithLLVM(&module, fileName); + int (*exec)(void *) = runInJit ? executeLLVMCode : 0; + return compileWithLLVM(&module, fileName, exec); } -int compileFiles(const QStringList &files) +int compileFiles(const QStringList &files, bool runInJit) { foreach (const QString &fileName, files) { QFile file(fileName); if (file.open(QFile::ReadOnly)) { QString source = QString::fromUtf8(file.readAll()); - compile(fileName, source); + int result = compile(fileName, source, runInJit); + if (result != EXIT_SUCCESS) + return result; } } return EXIT_SUCCESS; @@ -182,27 +213,9 @@ int evaluateCompiledCode(const QStringList &files) lib.load(); QFunctionPointer ptr = lib.resolve("%entry"); // qDebug("_%%entry resolved to address %p", ptr); - if (!ptr) - return EXIT_FAILURE; - void (*code)(VM::Context *) = (void (*)(VM::Context *)) ptr; - - VM::ExecutionEngine vm; - VM::Context *ctx = vm.rootContext; - - QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); - globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), - QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); - - void * buf = __qmljs_create_exception_handler(ctx); - if (setjmp(*(jmp_buf *)buf)) { - if (VM::ErrorObject *e = ctx->result.asErrorObject()) - std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; - else - std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; - return EXIT_FAILURE; - } - - code(ctx); + int result = executeLLVMCode((void *) ptr); + if (result != EXIT_SUCCESS) + return result; } return EXIT_SUCCESS; @@ -307,7 +320,8 @@ int main(int argc, char *argv[]) use_masm, use_moth, use_llvm_compiler, - use_llvm_runtime + use_llvm_runtime, + use_llvm_jit } mode = use_masm; if (!args.isEmpty()) { @@ -331,9 +345,14 @@ int main(int argc, char *argv[]) mode = use_llvm_runtime; args.removeFirst(); } + + if (args.first() == QLatin1String("--llvm-jit")) { + mode = use_llvm_jit; + args.removeFirst(); + } #endif // QMLJS_NO_LLVM if (args.first() == QLatin1String("--help")) { - std::cerr << "Usage: v4 [|--jit|--interpret|--compile|--aot] file..." << std::endl; + std::cerr << "Usage: v4 [|--jit|--interpret|--compile|--aot|--llvm-jit] file..." << std::endl; return EXIT_SUCCESS; } } @@ -342,11 +361,14 @@ int main(int argc, char *argv[]) #ifdef QMLJS_NO_LLVM case use_llvm_compiler: case use_llvm_runtime: + case use_llvm_jit: std::cerr << "LLVM backend was not built, compiler is unavailable." << std::endl; return EXIT_FAILURE; #else // QMLJS_NO_LLVM + case use_llvm_jit: + return compileFiles(args, true); case use_llvm_compiler: - return compileFiles(args); + return compileFiles(args, false); case use_llvm_runtime: return evaluateCompiledCode(args); #endif // QMLJS_NO_LLVM diff --git a/qmljs_math.h b/qmljs_math.h index 7823eeccc7..873e8932ff 100644 --- a/qmljs_math.h +++ b/qmljs_math.h @@ -46,7 +46,7 @@ #endif // QMLJS_LLVM_RUNTIME #include -#if 1 //CPU(X86_64) +#if !defined(QMLJS_LLVM_RUNTIME) && 1 //CPU(X86_64) static inline Value add_int32(int a, int b) { diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 12690357ca..ff1f4a3f17 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -919,8 +919,10 @@ inline Value __qmljs_add(Value left, Value right, Context *ctx) { TRACE2(left, right); +#ifndef QMLJS_LLVM_RUNTIME if (Value::integerCompatible(left, right)) return add_int32(left.integerValue(), right.integerValue()); +#endif // QMLJS_LLVM_RUNTIME if (Value::bothDouble(left, right)) return Value::fromDouble(left.doubleValue() + right.doubleValue()); @@ -932,8 +934,10 @@ inline Value __qmljs_sub(Value left, Value right, Context *ctx) { TRACE2(left, right); +#ifndef QMLJS_LLVM_RUNTIME if (Value::integerCompatible(left, right)) return sub_int32(left.integerValue(), right.integerValue()); +#endif // QMLJS_LLVM_RUNTIME double lval = __qmljs_to_number(left, ctx); double rval = __qmljs_to_number(right, ctx); @@ -944,8 +948,10 @@ inline Value __qmljs_mul(Value left, Value right, Context *ctx) { TRACE2(left, right); +#ifndef QMLJS_LLVM_RUNTIME if (Value::integerCompatible(left, right)) return mul_int32(left.integerValue(), right.integerValue()); +#endif // QMLJS_LLVM_RUNTIME double lval = __qmljs_to_number(left, ctx); double rval = __qmljs_to_number(right, ctx); diff --git a/qv4_llvm_p.h b/qv4_llvm_p.h index 729da2dada..e15479ab68 100644 --- a/qv4_llvm_p.h +++ b/qv4_llvm_p.h @@ -36,7 +36,7 @@ namespace QQmlJS { -void compileWithLLVM(IR::Module *module, const QString &fileName); +int compileWithLLVM(IR::Module *module, const QString &fileName, int (*exec)(void *)); } // QQmlJS diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 9e35e6ef56..c674377d6b 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -45,9 +45,12 @@ #endif // __clang__ #include +#include #include #include -#include +#include +#include +#include #include #include #include @@ -74,27 +77,16 @@ // These includes have to come last, because WTF/Platform.h defines some macros // with very unfriendly names that collide with class fields in LLVM. #include "qv4isel_llvm_p.h" +#include "qv4_llvm_p.h" #include "qv4ir_p.h" namespace QQmlJS { -void compileWithLLVM(IR::Module *module, const QString &fileName) +int compileWithLLVM(IR::Module *module, const QString &fileName, int (*exec)(void *)) { Q_ASSERT(module); - const QString moduleName = QFileInfo(fileName).fileName(); - - LLVMInstructionSelection llvmIsel(llvm::getGlobalContext()); - llvm::Module *llvmModule = llvmIsel.getLLVMModule(module, moduleName); - if (!llvmModule) - return; - - // TODO: if output type is .ll, print the module to file - - llvm::PassManager PM; - - const std::string triple = llvm::sys::getDefaultTargetTriple(); - + // TODO: should this be done here? LLVMInitializeX86TargetInfo(); LLVMInitializeX86Target(); LLVMInitializeX86AsmPrinter(); @@ -102,50 +94,105 @@ void compileWithLLVM(IR::Module *module, const QString &fileName) LLVMInitializeX86Disassembler(); LLVMInitializeX86TargetMC(); - std::string err; - const llvm::Target *target = llvm::TargetRegistry::lookupTarget(triple, err); - if (! err.empty()) { - std::cerr << err << ", triple: " << triple << std::endl; - assert(!"cannot create target for the host triple"); - } + //---- - std::string cpu; - std::string features; - llvm::TargetOptions options; - llvm::TargetMachine *targetMachine = target->createTargetMachine(triple, cpu, features, options, llvm::Reloc::PIC_); - assert(targetMachine); + llvm::InitializeNativeTarget(); + LLVMInstructionSelection llvmIsel(llvm::getGlobalContext()); - llvm::TargetMachine::CodeGenFileType ft; - QString ofName; + const QString moduleName = QFileInfo(fileName).fileName(); + llvm::StringRef moduleId(moduleName.toUtf8().constData()); + llvm::Module *llvmModule = new llvm::Module(moduleId, llvmIsel.getContext()); + + if (exec) { + // The execution engine takes ownership of the model. No need to delete it anymore. + std::string errStr; + llvm::ExecutionEngine *execEngine = llvm::EngineBuilder(llvmModule) + .setUseMCJIT(true) +// .setJITMemoryManager(llvm::JITMemoryManager::CreateDefaultMemManager()) + .setErrorStr(&errStr).create(); + if (!execEngine) { + std::cerr << "Could not create LLVM JIT: " << errStr << std::endl; + return EXIT_FAILURE; + } - ft = llvm::TargetMachine::CGFT_ObjectFile; - ofName = fileName + QLatin1String(".o"); + llvm::FunctionPassManager fpm(llvmModule); + // Set up the optimizer pipeline. Start with registering info about how the + // target lays out data structures. + fpm.add(new llvm::DataLayout(*execEngine->getDataLayout())); + // Provide basic AliasAnalysis support for GVN. + fpm.add(llvm::createBasicAliasAnalysisPass()); + // Do simple "peephole" optimizations and bit-twiddling optzns. + fpm.add(llvm::createInstructionCombiningPass()); + // Reassociate expressions. + fpm.add(llvm::createReassociatePass()); + // Eliminate Common SubExpressions. + fpm.add(llvm::createGVNPass()); + // Simplify the control flow graph (deleting unreachable blocks, etc). + fpm.add(llvm::createCFGSimplificationPass()); + + fpm.doInitialization(); + + llvmIsel.buildLLVMModule(module, llvmModule, &fpm); + + llvm::Function *entryPoint = llvmModule->getFunction("%entry"); + Q_ASSERT(entryPoint); + void *funcPtr = execEngine->getPointerToFunction(entryPoint); + return exec(funcPtr); + } else { + // TODO: add a FunctionPassManager + llvmIsel.buildLLVMModule(module, llvmModule, 0); - // TODO: -// ft = llvm::TargetMachine::CGFT_AssemblyFile; -// ofName = fileName + QLatin1String(".s"); + // TODO: if output type is .ll, print the module to file - llvm::raw_fd_ostream dest(ofName.toUtf8().constData(), err, llvm::raw_fd_ostream::F_Binary); - llvm::formatted_raw_ostream destf(dest); - if (!err.empty()) { - std::cerr << err << std::endl; - delete llvmModule; - } + const std::string triple = llvm::sys::getDefaultTargetTriple(); - PM.add(llvm::createScalarReplAggregatesPass()); - PM.add(llvm::createInstructionCombiningPass()); - PM.add(llvm::createGlobalOptimizerPass()); - PM.add(llvm::createFunctionInliningPass(25)); - if (targetMachine->addPassesToEmitFile(PM, destf, ft)) { - std::cerr << err << " (probably no DataLayout in TargetMachine)" << std::endl; - } else { - PM.run(*llvmModule); + std::string err; + const llvm::Target *target = llvm::TargetRegistry::lookupTarget(triple, err); + if (! err.empty()) { + std::cerr << err << ", triple: " << triple << std::endl; + assert(!"cannot create target for the host triple"); + } - destf.flush(); - dest.flush(); - } + std::string cpu; + std::string features; + llvm::TargetOptions options; + llvm::TargetMachine *targetMachine = target->createTargetMachine(triple, cpu, features, options, llvm::Reloc::PIC_); + assert(targetMachine); + + llvm::TargetMachine::CodeGenFileType ft; + QString ofName; + + ft = llvm::TargetMachine::CGFT_ObjectFile; + ofName = fileName + QLatin1String(".o"); + + // TODO: + // ft = llvm::TargetMachine::CGFT_AssemblyFile; + // ofName = fileName + QLatin1String(".s"); - delete llvmModule; + llvm::raw_fd_ostream dest(ofName.toUtf8().constData(), err, llvm::raw_fd_ostream::F_Binary); + llvm::formatted_raw_ostream destf(dest); + if (!err.empty()) { + std::cerr << err << std::endl; + delete llvmModule; + } + + llvm::PassManager PM; + PM.add(llvm::createScalarReplAggregatesPass()); + PM.add(llvm::createInstructionCombiningPass()); + PM.add(llvm::createGlobalOptimizerPass()); + PM.add(llvm::createFunctionInliningPass(25)); + if (targetMachine->addPassesToEmitFile(PM, destf, ft)) { + std::cerr << err << " (probably no DataLayout in TargetMachine)" << std::endl; + } else { + PM.run(*llvmModule); + + destf.flush(); + dest.flush(); + } + + delete llvmModule; + return EXIT_SUCCESS; + } } } // QQmlJS @@ -169,14 +216,14 @@ LLVMInstructionSelection::LLVMInstructionSelection(llvm::LLVMContext &context) , _allocaInsertPoint(0) , _function(0) , _block(0) + , _fpm(0) { } -llvm::Module *LLVMInstructionSelection::getLLVMModule(IR::Module *module, const QString &moduleName) +void LLVMInstructionSelection::buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm) { - llvm::StringRef moduleId(moduleName.toUtf8().constData()); - llvm::Module *llvmModule = new llvm::Module(moduleId, getContext()); qSwap(_llvmModule, llvmModule); + qSwap(_fpm, fpm); _numberTy = getDoubleTy(); @@ -215,7 +262,6 @@ llvm::Module *LLVMInstructionSelection::getLLVMModule(IR::Module *module, const foreach (IR::Function *function, module->functions) (void) compileLLVMFunction(function); qSwap(_llvmModule, llvmModule); - return llvmModule; } llvm::Function *LLVMInstructionSelection::getLLVMFunction(IR::Function *function) @@ -286,6 +332,13 @@ llvm::Function *LLVMInstructionSelection::compileLLVMFunction(IR::Function *func qSwap(_tempMap, tempMap); qSwap(_function, function); qSwap(_llvmFunction, llvmFunction); + + // Validate the generated code, checking for consistency. + llvm::verifyFunction(*llvmFunction); + // Optimize the function. + if (_fpm) + _fpm->run(*llvmFunction); + return llvmFunction; } @@ -582,33 +635,9 @@ void LLVMInstructionSelection::visitRet(IR::Ret *s) CreateRetVoid(); } - void LLVMInstructionSelection::visitConst(IR::Const *e) { - llvm::Value *tmp = newLLVMTemp(_valueTy); - - switch (e->type) { - case IR::UndefinedType: - CreateCall(_llvmModule->getFunction("__qmljs_llvm_init_undefined"), tmp); - break; - - case IR::NullType: - CreateCall(_llvmModule->getFunction("__qmljs_llvm_init_null"), tmp); - break; - - case IR::BoolType: - CreateCall2(_llvmModule->getFunction("__qmljs_llvm_init_boolean"), tmp, - getInt1(e->value ? 1 : 0)); - break; - - case IR::NumberType: - CreateCall2(_llvmModule->getFunction("__qmljs_llvm_init_number"), tmp, - llvm::ConstantFP::get(_numberTy, e->value)); - break; - - default: - Q_UNREACHABLE(); - } + llvm::Value *tmp = createValue(e); _llvmValue = CreateLoad(tmp); } @@ -700,13 +729,11 @@ void LLVMInstructionSelection::genUnop(llvm::Value *result, IR::Unop *e) void LLVMInstructionSelection::genBinop(llvm::Value *result, IR::Binop *e) { - IR::Temp *t1 = e->left->asTemp(); - IR::Temp *t2 = e->right->asTemp(); - assert(t1 != 0); - assert(t2 != 0); + assert(e->left->asTemp() || e->left->asConst()); + assert(e->right->asTemp() || e->right->asConst()); - llvm::Value *left = getLLVMTemp(t1); - llvm::Value *right = getLLVMTemp(t2); + llvm::Value *left = toValuePtr(e->left); + llvm::Value *right = toValuePtr(e->right); llvm::Value *op = 0; switch (e->op) { case IR::OpInvalid: @@ -1041,3 +1068,44 @@ void LLVMInstructionSelection::visitMember(IR::Member *e) _llvmFunction->arg_begin(), result, base, name); _llvmValue = CreateLoad(result); } + +llvm::Value *LLVMInstructionSelection::createValue(IR::Const *e) +{ + llvm::Value *tmp = newLLVMTemp(_valueTy); + + switch (e->type) { + case IR::UndefinedType: + CreateCall(_llvmModule->getFunction("__qmljs_llvm_init_undefined"), tmp); + break; + + case IR::NullType: + CreateCall(_llvmModule->getFunction("__qmljs_llvm_init_null"), tmp); + break; + + case IR::BoolType: + CreateCall2(_llvmModule->getFunction("__qmljs_llvm_init_boolean"), tmp, + getInt1(e->value ? 1 : 0)); + break; + + case IR::NumberType: + CreateCall2(_llvmModule->getFunction("__qmljs_llvm_init_number"), tmp, + llvm::ConstantFP::get(_numberTy, e->value)); + break; + + default: + Q_UNREACHABLE(); + } + + return tmp; +} + +llvm::Value *LLVMInstructionSelection::toValuePtr(IR::Expr *e) +{ + if (IR::Temp *t = e->asTemp()) { + return getLLVMTemp(t); + } else if (IR::Const *c = e->asConst()) { + return createValue(c); + } else { + Q_UNREACHABLE(); + } +} diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 5c914433bf..2142ea42c2 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -47,6 +47,7 @@ #endif // __clang__ #include +#include #include #ifdef __clang__ @@ -65,7 +66,7 @@ class LLVMInstructionSelection: public: LLVMInstructionSelection(llvm::LLVMContext &context); - llvm::Module *getLLVMModule(IR::Module *module, const QString &moduleName); + void buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm); llvm::Function *getLLVMFunction(IR::Function *function); llvm::Function *compileLLVMFunction(IR::Function *function); llvm::BasicBlock *getLLVMBasicBlock(IR::BasicBlock *block); @@ -109,6 +110,10 @@ public: virtual void visitSubscript(IR::Subscript *); virtual void visitMember(IR::Member *); +private: + llvm::Value *createValue(IR::Const *e); + llvm::Value *toValuePtr(IR::Expr *e); + private: llvm::Module *_llvmModule; llvm::Function *_llvmFunction; @@ -125,6 +130,7 @@ private: QHash _blockMap; QVector _tempMap; QHash _stringMap; + llvm::FunctionPassManager *_fpm; }; } // end of namespace QQmlJS diff --git a/v4.pro b/v4.pro index bc8ada4ccf..c2790f044f 100644 --- a/v4.pro +++ b/v4.pro @@ -49,7 +49,7 @@ QMAKE_CXXFLAGS += $$system($$LLVM_CONFIG --cppflags) LIBS += \ $$system($$LLVM_CONFIG --ldflags) \ - $$system($$LLVM_CONFIG --libs core jit bitreader linker ipo target x86 arm) + $$system($$LLVM_CONFIG --libs core jit bitreader linker ipo target x86 arm native) QMAKE_EXTRA_TARGETS += gen_llvm_runtime -- cgit v1.2.3 From 12f4a6c8b1aed7071fd9f8132c09df29ab437e76 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 12 Nov 2012 14:31:05 +0100 Subject: Added more builtins to moth. - delete member - delete subscript - delete name - delete value - foreach-iterator-object - foreach-next-property-name Change-Id: I639a3c3a293ea88019e2f69e1f51ce2264a68bcc Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 30 +++++++++++++++++++++++++++++- moth/qv4isel_moth.cpp | 39 ++++++++++++++++++++++++++++++++++++--- moth/qv4vme_moth.cpp | 22 ++++++++++++++++++++++ 3 files changed, 87 insertions(+), 4 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 3227b6c3cc..6fad95e083 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -26,6 +26,10 @@ F(CallValue, callValue) \ F(CallProperty, callProperty) \ F(CallBuiltin, callBuiltin) \ + F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ + F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ + F(CallBuiltinDeleteName, callBuiltinDeleteName) \ + F(CallBuiltinDeleteValue, callBuiltinDeleteValue) \ F(CreateValue, createValue) \ F(CreateProperty, createProperty) \ F(CreateActivationProperty, createActivationProperty) \ @@ -150,11 +154,31 @@ union Instr builtin_throw, builtin_create_exception_handler, builtin_delete_exception_handler, - builtin_get_exception + builtin_get_exception, + builtin_foreach_iterator_object, + builtin_foreach_next_property_name } builtin; quint32 argc; quint32 args; }; + struct instr_callBuiltinDeleteMember { + MOTH_INSTR_HEADER + int base; + VM::String *member; + }; + struct instr_callBuiltinDeleteSubscript { + MOTH_INSTR_HEADER + int base; + int index; + }; + struct instr_callBuiltinDeleteName { + MOTH_INSTR_HEADER + VM::String *name; + }; + struct instr_callBuiltinDeleteValue { + MOTH_INSTR_HEADER + int tempIndex; + }; struct instr_createValue { MOTH_INSTR_HEADER int func; @@ -233,6 +257,10 @@ union Instr instr_callValue callValue; instr_callProperty callProperty; instr_callBuiltin callBuiltin; + instr_callBuiltinDeleteMember callBuiltinDeleteMember; + instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; + instr_callBuiltinDeleteName callBuiltinDeleteName; + instr_callBuiltinDeleteValue callBuiltinDeleteValue; instr_createValue createValue; instr_createProperty createProperty; instr_createActivationProperty createActivationProperty; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 7af4a31926..94116580b0 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -126,12 +126,45 @@ void InstructionSelection::callActivationProperty(IR::Call *c) addInstruction(call); } break; - case IR::Name::builtin_delete: - Q_UNIMPLEMENTED(); - break; + case IR::Name::builtin_foreach_iterator_object: { + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_foreach_iterator_object; + prepareCallArgs(c->args, call.argc, call.args); + addInstruction(call); + } break; + + case IR::Name::builtin_foreach_next_property_name: { + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_foreach_next_property_name; + prepareCallArgs(c->args, call.argc, call.args); + addInstruction(call); + } break; + + case IR::Name::builtin_delete: { + if (IR::Member *m = c->args->expr->asMember()) { + Instruction::CallBuiltinDeleteMember call; + call.base = m->base->asTemp()->index; + call.member = _engine->newString(*m->name); + addInstruction(call); + } else if (IR::Subscript *ss = c->args->expr->asSubscript()) { + Instruction::CallBuiltinDeleteSubscript call; + call.base = m->base->asTemp()->index; + call.index = ss->index->asTemp()->index; + addInstruction(call); + } else if (IR::Name *n = c->args->expr->asName()) { + Instruction::CallBuiltinDeleteName call; + call.name = _engine->newString(*n->id); + addInstruction(call); + } else { + Instruction::CallBuiltinDeleteValue call; + call.tempIndex = c->args->expr->asTemp()->index; + addInstruction(call); + } + } break; default: Q_UNIMPLEMENTED(); + c->dump(qout); qout << endl; } } diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 8a9638d347..9bd3bf8042 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -201,9 +201,31 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code case Instr::instr_callBuiltin::builtin_get_exception: tempRegister = __qmljs_get_exception(context); break; + case Instr::instr_callBuiltin::builtin_foreach_iterator_object: + tempRegister = __qmljs_foreach_iterator_object(args[0], context); + break; + case Instr::instr_callBuiltin::builtin_foreach_next_property_name: + tempRegister = __qmljs_foreach_next_property_name(args[0]); + break; } MOTH_END_INSTR(CallBuiltin) + MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) + tempRegister = __qmljs_delete_member(context, TEMP(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinDeleteMember) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) + tempRegister = __qmljs_delete_subscript(context, TEMP(instr.base), TEMP(instr.index)); + MOTH_END_INSTR(CallBuiltinDeleteSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteName) + tempRegister = __qmljs_delete_property(context, instr.name); + MOTH_END_INSTR(CallBuiltinDeleteName) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteValue) + tempRegister = __qmljs_delete_value(context, TEMP(instr.tempIndex)); + MOTH_END_INSTR(CallBuiltinDeleteValue) + MOTH_BEGIN_INSTR(CreateValue) VM::Value *args = stack.data() + instr.args; tempRegister = __qmljs_construct_value(context, TEMP(instr.func), args, instr.argc); -- cgit v1.2.3 From 6cf913c485cb9b90f013ad8569c843f53deba3a3 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 12 Nov 2012 16:19:04 +0100 Subject: Fix property deletion. Also tweak the bucket size calculation to be prime for just a bit longer than. Change-Id: I9fc6779956693301eb01a6558d4c20d8e39d8bad Reviewed-by: Lars Knoll --- qmljs_objects.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qmljs_objects.h b/qmljs_objects.h index 139ea10f77..857b12bcd9 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -260,11 +260,11 @@ public: void remove(PropertyTableEntry *prop) { - PropertyTableEntry *bucket = _buckets[prop->hashValue() % _bucketCount]; - if (bucket == prop) { - bucket = bucket->next; + PropertyTableEntry **bucket = _buckets + (prop->hashValue() % _bucketCount); + if (*bucket == prop) { + *bucket = prop->next; } else { - for (PropertyTableEntry *it = bucket; it; it = it->next) { + for (PropertyTableEntry *it = *bucket; it; it = it->next) { if (it->next == prop) { it->next = it->next->next; break; @@ -344,7 +344,7 @@ private: void rehash() { if (_bucketCount) - _bucketCount *= 2; // ### next prime + _bucketCount = _bucketCount * 2 + 1; // ### next prime else _bucketCount = 11; -- cgit v1.2.3 From c870a64bad28bc52e4bf7239a78e166804fd2124 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Fri, 9 Nov 2012 15:16:58 +0100 Subject: Add a script to test the interpreter on *.js files. By default this will run ./v4 on all *.js files in test262 and generate test reports based on the exit code and the output of the interpreter. Change-Id: I5a90b9f4c5bf376c5c8df12b5e777d0626d9d82e Reviewed-by: Erik Verbruggen --- test_interpreter.py | 460 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 460 insertions(+) create mode 100755 test_interpreter.py diff --git a/test_interpreter.py b/test_interpreter.py new file mode 100755 index 0000000000..f69d96eb7b --- /dev/null +++ b/test_interpreter.py @@ -0,0 +1,460 @@ +#!/usr/bin/env python + +# +# See CommandLineProcessor.print_usage() below. +# + +# TODO: Consider checking "@non_strict_only". + +import datetime +import fnmatch +import getopt +import os +import platform +import resource +import subprocess +import threading +import sys + +### Settings & Command Line Processing ######################################## + +class Settings: + interpreter = 'v4' + interpreter_timeout = 60 # in s + modes = ['jit', 'interpret'] + printers = ['txt', 'html'] + verbose = False + input_dir = 'test262' + # List of patterns with unix shell-style wildcards, see also + # http://docs.python.org/2/library/fnmatch.html + # Example: + # blacklist_patterns = [ + # "test262/console/harness/cth*" + # ] + blacklist_patterns = [] + +class CommandLineProcessor: + """ Process command line arguments and overwrite values in Settings.""" + def __init__(self, args): + self.args = args + self.valid_modes = ['aot', 'compile', 'jit', 'interpret', 'llvm-jit'] + self.valid_printers = ['txt', 'html'] + + def print_usage(self): + sys.stdout.write("""\ +Usage: %s [-v] [-i interpreter] [-t timeout] [-d input_dir] [-p printers] [-m mode_list] + +Run an interpreter with different modes (mode_list) on all *.js files in +input_dir and generate test reports based on the exit code and the output of +the interpreter. + +Test reports are generated in TXT and HTML format. Paths to generated reports +are printed upon a finished run. + +Options: + -i, --interpreter Use the specified interpreter (default: '%s'). + -t, --timeout Timeout in seconds for the interpreter to process a single + file (default: '%s'). + -d, --input-dir *.js files will be searched in input_dir. (default: '%s'). + -m, --modes Run the interpreter with specified modes. mode_list is a + comma separated list (valid: '%s', + default: '%s'). + -p, --printers Print results by specified printers. printers is a comma + separated list (valid: '%s', default: '%s'). + -v, --verbose Print some more information, e.g. files are being + processed (default: not enabled). + +""" % ( + sys.argv[0], + Settings.interpreter, + Settings.interpreter_timeout, + Settings.input_dir, + ",".join(self.valid_modes), + ",".join(Settings.modes), + ",".join(self.valid_printers), + ",".join(Settings.printers), + )) + + def run(self): + try: + options, arguments = getopt.getopt(self.args, "hvm:i:d:p:t:", + ["help", "verbose", "modes=", "interpreter=", "input-dir=", "printers=", "timeout="]) + except getopt.error, msg: + sys.stdout.write("For help use -h, --help.") + sys.exit(2) + for option, argument in options: + if option in ("-h", "--help"): + self.print_usage() + sys.exit(0) + elif option in ("-m", "--modes"): + modes = filter(None, argument.split(',')) + if not modes: + sys.stdout.write("No modes provided.") + sys.stdout.write("For help use -h, --help.") + sys.exit(2) + for mode in modes: + if not mode in self.valid_modes: + sys.stdout.write("Mode '%s' is invalid." %(mode)) + sys.stdout.write("For help use -h, --help.") + sys.exit(2) + Settings.modes = modes + elif option in ("-p", "--printers"): + printers = filter(None, argument.split(',')) + if not printers: + sys.stdout.write("No printers provided.") + sys.stdout.write("For help use -h, --help.") + sys.exit(2) + for printer in printers: + if not printer in self.valid_printers: + sys.stdout.write("Printer '%s' is invalid." %(printer)) + sys.stdout.write("For help use -h, --help.") + sys.exit(2) + Settings.printers = printers + elif option in ("-i", "--interpreter"): + Settings.interpreter = argument + elif option in ("-d", "--input-dir"): + Settings.input_dir = argument + elif option in ("-t", "--interpreter-timeout"): + Settings.interpreter_timeout = int(argument) + elif option in ("-v", "--verbose"): + Settings.verbose = True + else: + sys.stdout.write("Unknown option '%s'." %(option)) + sys.stdout.write("For help use -h, --help.") + sys.exit(2) + +### Utils ##################################################################### + +def check_candidates(candidates): + """ Return files passing the black list Settings.blacklist_patterns. """ + input_files = [] + for candidate in candidates: + is_blacklisted = False + for p in Settings.blacklist_patterns: + if fnmatch.fnmatch(candidate, p): + if Settings.verbose: + sys.stdout.write("Will ignore '%s' since it matches black list filter '%s'.\n" %(candidate, p)) + is_blacklisted = True + break + if not is_blacklisted: + input_files.append(candidate) + return input_files + +def generic_footer(time_started, time_finished, total_pass, total_passn, total_fail, total_gout, total_time): + totals_processed = total_pass + total_passn + total_fail + total_gout + return """ +Key: + PASS: Exit code is zero and _no_ output generated. + PASSN: Exit code is non zero and *.js contains "@negative". + GOUT: Exit code is zero and output generated. + FAIL: Exit code is non zero. + TIME: Interpreter does not return within timeout of %d seconds. + +Totals: %d processed - %d passed, %d passn'ed, %d failed, %d gout'ed, %d timeout'ed. + +Started: %s +Finished: %s""" %( + Settings.interpreter_timeout, + totals_processed, total_pass, total_passn, total_fail, total_gout, total_time, + time_started.strftime("%A, %d. %B %Y %I:%M%p"), + time_finished.strftime("%A, %d. %B %Y %I:%M%p") + ) + +class Command(object): + def __init__(self, cmd): + self.cmd = cmd + self.process = None + + self.finished_within_timeout = False + self.exit_code = None + self.stdout = None + self.stderr = None + + def run(self, timeout): + def target(): + my_env = os.environ.copy() + my_env['IN_TEST_HARNESS'] = '1' + self.process = subprocess.Popen(self.cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, preexec_fn=self.__set_limits, env=my_env) + (self.stdout, self.stderr) = self.process.communicate() + + thread = threading.Thread(target=target) + thread.start() + thread.join(timeout) + if thread.is_alive(): + self.process.terminate() + thread.join() + else: + self.finished_within_timeout = True + self.exit_code = self.process.returncode + + def __set_limits(self): + if platform.system() in ('Linux', 'Darwin'): + one_gigabyte = 1024*1024*1024 + resource.setrlimit(resource.RLIMIT_AS, (one_gigabyte, one_gigabyte)) + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + +class InterpreterRunnerInfo: + def __init__(self, info): + self.info = info + +class InterpreterResult: + def __init__(self, file_path, finished_within_timeout, code, output_stdout, output_stderr): + self.file_path = file_path + self.finished_within_timeout = finished_within_timeout + self.code = code + self.output_stdout= output_stdout + self.output_stderr = output_stderr + +class InterpreterRunner: + def __init__(self, input_files, label, extra_args, printers): + self.input_files = input_files + self.label = label + self.extra_args = extra_args + self.printers = printers + self.runner_info = InterpreterRunnerInfo(Settings.interpreter + ' ' + " ".join(self.extra_args)) + + def __printers_begin(self): + for printer in self.printers: + printer.reset() + printer.set_output_file("testresults_%s.%s" %(os.path.basename(Settings.interpreter), self.label)) + printer.set_runner_info(self.runner_info) + printer.print_header(datetime.datetime.now()) + + def __printers_end(self): + for printer in self.printers: + printer.print_footer(datetime.datetime.now()) + + def __printers_do(self, result): + for printer in self.printers: + printer.print_print(result) + + def run(self): + sys.stdout.write("Running interpreter '%s %s' for each test file.\n" + %(Settings.interpreter, " ".join(self.extra_args))) + self.__printers_begin() + for input_file in self.input_files: + if not os.path.exists(input_file): + sys.stderr.write("Warning: File '%s' does not exist anymore for interpreter '%s'.\n" + %(input_file, self.runner_info.info)) + break + if Settings.verbose: + sys.stdout.write(" %s\n" %(input_file)) + command = ['./' + Settings.interpreter] + self.extra_args + [input_file] + + process = Command(command) + process.run(timeout=Settings.interpreter_timeout) + if not process.finished_within_timeout: + sys.stderr.write("Warning: Interpreter '%s' did not finish within %d seconds processing file '%s'.\n" + %(self.runner_info.info, Settings.interpreter_timeout, input_file)) + result = InterpreterResult( + input_file, + process.finished_within_timeout, + process.exit_code, + process.stdout, + process.stderr + ) + self.__printers_do(result) + self.__printers_end() + +### Printers ################################################################## + +class AbstractInterpreterPrinter: + def __init__(self, file_suffix): + self.file_suffix = file_suffix + self.reset() + + def reset(self): + self.count_pass = 0 + self.count_passn = 0 + self.count_fail = 0 + self.count_gout = 0 + self.count_time = 0 + + def set_output_file(self, file_path): + self.output_file = open(file_path + self.file_suffix, 'w') + + def set_runner_info(self, runner_info): + self.runner_info = runner_info + + def print_header(self, time_started): + """Must be called by derived method to ensure correct totals.""" + self.reset() + self.time_started = time_started + + def print_print(self, result): + """ Returns result marker and update totals. + Must be called by derived method to ensure correct totals. """ + if result.finished_within_timeout: + if result.code != 0: + if '@negative' in open(result.file_path, 'r').read(1024): + result_marker = "PASSN" + self.count_passn = self.count_passn + 1 + else: + result_marker = "FAIL" + self.count_fail = self.count_fail + 1 + else: + if result.output_stdout or result.output_stderr: + result_marker = "GOUT" + self.count_gout = self.count_gout + 1 + else: + result_marker = "PASS" + self.count_pass = self.count_pass + 1 + else: + result_marker = "TIME" + self.count_time = self.count_time + 1 + return result_marker + + def print_footer(self, time_finished): + raise NotImplementedError, "Implement me" + + def generic_footer(self): + return generic_footer(self.time_started, self.time_finished, self.count_pass, + self.count_passn, self.count_fail, self.count_gout, self.count_time) + +class InterpreterTextPrinter(AbstractInterpreterPrinter): + def __init__(self): + AbstractInterpreterPrinter.__init__(self, '.txt') + + def print_header(self, time_started): + AbstractInterpreterPrinter.print_header(self, time_started) + self.output_file.write("********* Start testing of '%s' *********\n" %(Settings.interpreter)) + self.output_file.write("Config: %s\n" %(self.runner_info.info)) + self.output_file.flush() + + def print_print(self, result): + result_marker = AbstractInterpreterPrinter.print_print(self, result) + self.output_file.write("%-5s %-26s %s\n" %(result_marker, os.path.basename(result.file_path), + result.file_path)) + self.output_file.flush() + + def print_footer(self, time_finished): + self.time_finished = time_finished + self.output_file.write("********* Finished testing of '%s' *********\n" %(Settings.interpreter)) + self.output_file.write(AbstractInterpreterPrinter.generic_footer(self)) + self.output_file.flush() + sys.stdout.write("Generated '%s'.\n" %(self.output_file.name)) + +class InterpreterHtmlPrinter(AbstractInterpreterPrinter): + def __init__(self): + AbstractInterpreterPrinter.__init__(self, '.html') + + def print_header(self, time_started): + AbstractInterpreterPrinter.print_header(self, time_started) + + preamble = ("""\ + + + +Test results for '%s' + + + +

Test results for '%s'

+ + + + + + + + + +""" %( + self.runner_info.info, + self.runner_info.info + )) + self.output_file.write(preamble) + self.output_file.flush() + + def print_print(self, result): + result_marker = AbstractInterpreterPrinter.print_print(self, result) + has_output = result.output_stdout or result.output_stderr + output = result.output_stdout + " " + result.output_stderr + "" + if not has_output: + output = "" + row = ("""\ + + + + + + """ %( + result_marker.lower(), + result_marker, + os.path.basename(result.file_path), + output, + result.file_path + )) + + self.output_file.write(row) + self.output_file.flush() + + def print_footer(self, time_finished): + self.time_finished = time_finished + footer = ("""\ +
ResultTest FileInterpreter Output (stdout stderr)Full Test File Path
%s%s%s%s
+
+%s
+
+ + +""" % (AbstractInterpreterPrinter.generic_footer(self))) + self.output_file.write(footer) + sys.stdout.write("Generated '%s'.\n" %(self.output_file.name)) + +### Main ###################################################################### + +def main(): + # Handle command line options + cmdline_processor = CommandLineProcessor(sys.argv[1:]) + cmdline_processor.run() + + # Sanity checks + if not os.path.exists(Settings.interpreter): + sys.stderr.write("Error: Interpreter '%s' not found.\n" %(Settings.interpreter)) + sys.exit(3) + if not os.path.exists(Settings.input_dir): + sys.stderr.write("Error: Directory with test data '%s' not found.\n" %(Settings.input_dir)) + sys.exit(3) + + # Collect files + candidates = [] + for root, dirnames, filenames in os.walk(Settings.input_dir): + for filename in fnmatch.filter(filenames, '*.js'): + candidates.append(os.path.join(root, filename)) + candidates.sort() + input_files = check_candidates(candidates) + + if not input_files: + sys.stderr.write("Error: No relevant test files found in '%s'.\n" %(Settings.input_dir)) + sys.exit(3) + if Settings.verbose: + sys.stdout.write("Will process %s files for each interpreter mode.\n" %(len(input_files))) + + # Run interpreter + printers = [] + for printer in Settings.printers: + if printer == "txt": + printers.append( InterpreterTextPrinter() ) + elif printer == "html": + printers.append( InterpreterHtmlPrinter() ) + + for mode in Settings.modes: + runner = InterpreterRunner(input_files, mode, ["--" + mode], printers) + runner.run() + +if __name__ == "__main__": + main() -- cgit v1.2.3 From 2a8d0b894ba59c6e8966b39bf872ce081579f50d Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 13 Nov 2012 09:54:24 +0100 Subject: Added different output types to LLVM backend to ease debugging. While in that area of code, also throw in the pass-managers for some extra optimisations during compilation. Change-Id: I1239ab9d21fc50b2e65c2f9d77a03ae593b607bc Reviewed-by: Simon Hausmann --- main.cpp | 30 +++++++++++++----- qv4_llvm_p.h | 11 ++++++- qv4isel_llvm.cpp | 93 ++++++++++++++++++++++++++++++++++++++------------------ 3 files changed, 96 insertions(+), 38 deletions(-) diff --git a/main.cpp b/main.cpp index 623ee20721..530757ed4e 100644 --- a/main.cpp +++ b/main.cpp @@ -159,7 +159,7 @@ int executeLLVMCode(void *codePtr) return EXIT_SUCCESS; } -int compile(const QString &fileName, const QString &source, bool runInJit) +int compile(const QString &fileName, const QString &source, QQmlJS::LLVMOutputType outputType) { using namespace QQmlJS; @@ -185,17 +185,17 @@ int compile(const QString &fileName, const QString &source, bool runInJit) Codegen cg; /*IR::Function *globalCode =*/ cg(program, &module); - int (*exec)(void *) = runInJit ? executeLLVMCode : 0; - return compileWithLLVM(&module, fileName, exec); + int (*exec)(void *) = outputType == LLVMOutputJit ? executeLLVMCode : 0; + return compileWithLLVM(&module, fileName, outputType, exec); } -int compileFiles(const QStringList &files, bool runInJit) +int compileFiles(const QStringList &files, QQmlJS::LLVMOutputType outputType) { foreach (const QString &fileName, files) { QFile file(fileName); if (file.open(QFile::ReadOnly)) { QString source = QString::fromUtf8(file.readAll()); - int result = compile(fileName, source, runInJit); + int result = compile(fileName, source, outputType); if (result != EXIT_SUCCESS) return result; } @@ -324,6 +324,10 @@ int main(int argc, char *argv[]) use_llvm_jit } mode = use_masm; +#ifndef QMLJS_NO_LLVM + QQmlJS::LLVMOutputType fileType = QQmlJS::LLVMOutputObject; +#endif // QMLJS_NO_LLVM + if (!args.isEmpty()) { if (args.first() == QLatin1String("--jit")) { mode = use_masm; @@ -339,6 +343,18 @@ int main(int argc, char *argv[]) if (args.first() == QLatin1String("--compile")) { mode = use_llvm_compiler; args.removeFirst(); + + if (!args.isEmpty() && args.first() == QLatin1String("-t")) { + args.removeFirst(); + // Note: keep this list in sync with the enum! + static QStringList fileTypes = QStringList() << QLatin1String("ll") << QLatin1String("bc") << QLatin1String("asm") << QLatin1String("obj"); + if (args.isEmpty() || !fileTypes.contains(args.first())) { + std::cerr << "file types: ll, bc, asm, obj" << std::endl; + return EXIT_FAILURE; + } + fileType = (QQmlJS::LLVMOutputType) fileTypes.indexOf(args.first()); + args.removeFirst(); + } } if (args.first() == QLatin1String("--aot")) { @@ -366,9 +382,9 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; #else // QMLJS_NO_LLVM case use_llvm_jit: - return compileFiles(args, true); + return compileFiles(args, QQmlJS::LLVMOutputJit); case use_llvm_compiler: - return compileFiles(args, false); + return compileFiles(args, fileType); case use_llvm_runtime: return evaluateCompiledCode(args); #endif // QMLJS_NO_LLVM diff --git a/qv4_llvm_p.h b/qv4_llvm_p.h index e15479ab68..c0f18e555c 100644 --- a/qv4_llvm_p.h +++ b/qv4_llvm_p.h @@ -36,7 +36,16 @@ namespace QQmlJS { -int compileWithLLVM(IR::Module *module, const QString &fileName, int (*exec)(void *)); +// Note: keep this enum in sync with the command-line option! +enum LLVMOutputType { + LLVMOutputJit = -1, + LLVMOutputIR = 0, // .ll + LLVMOutputBitcode = 1, // .bc + LLVMOutputAssembler = 2, // .s + LLVMOutputObject = 3 // .o +}; + +int compileWithLLVM(IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*exec)(void *)); } // QQmlJS diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index c674377d6b..9b6907159f 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -82,9 +82,10 @@ namespace QQmlJS { -int compileWithLLVM(IR::Module *module, const QString &fileName, int (*exec)(void *)) +int compileWithLLVM(IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*exec)(void *)) { Q_ASSERT(module); + Q_ASSERT(exec || outputType != LLVMOutputJit); // TODO: should this be done here? LLVMInitializeX86TargetInfo(); @@ -103,44 +104,61 @@ int compileWithLLVM(IR::Module *module, const QString &fileName, int (*exec)(voi llvm::StringRef moduleId(moduleName.toUtf8().constData()); llvm::Module *llvmModule = new llvm::Module(moduleId, llvmIsel.getContext()); - if (exec) { + if (outputType == LLVMOutputJit) { // The execution engine takes ownership of the model. No need to delete it anymore. std::string errStr; llvm::ExecutionEngine *execEngine = llvm::EngineBuilder(llvmModule) - .setUseMCJIT(true) -// .setJITMemoryManager(llvm::JITMemoryManager::CreateDefaultMemManager()) +// .setUseMCJIT(true) .setErrorStr(&errStr).create(); if (!execEngine) { std::cerr << "Could not create LLVM JIT: " << errStr << std::endl; return EXIT_FAILURE; } - llvm::FunctionPassManager fpm(llvmModule); + llvm::FunctionPassManager functionPassManager(llvmModule); // Set up the optimizer pipeline. Start with registering info about how the // target lays out data structures. - fpm.add(new llvm::DataLayout(*execEngine->getDataLayout())); + functionPassManager.add(new llvm::DataLayout(*execEngine->getDataLayout())); + // Promote allocas to registers. + functionPassManager.add(llvm::createPromoteMemoryToRegisterPass()); // Provide basic AliasAnalysis support for GVN. - fpm.add(llvm::createBasicAliasAnalysisPass()); + functionPassManager.add(llvm::createBasicAliasAnalysisPass()); // Do simple "peephole" optimizations and bit-twiddling optzns. - fpm.add(llvm::createInstructionCombiningPass()); + functionPassManager.add(llvm::createInstructionCombiningPass()); // Reassociate expressions. - fpm.add(llvm::createReassociatePass()); + functionPassManager.add(llvm::createReassociatePass()); // Eliminate Common SubExpressions. - fpm.add(llvm::createGVNPass()); + functionPassManager.add(llvm::createGVNPass()); // Simplify the control flow graph (deleting unreachable blocks, etc). - fpm.add(llvm::createCFGSimplificationPass()); + functionPassManager.add(llvm::createCFGSimplificationPass()); - fpm.doInitialization(); + functionPassManager.doInitialization(); - llvmIsel.buildLLVMModule(module, llvmModule, &fpm); + llvmIsel.buildLLVMModule(module, llvmModule, &functionPassManager); llvm::Function *entryPoint = llvmModule->getFunction("%entry"); Q_ASSERT(entryPoint); void *funcPtr = execEngine->getPointerToFunction(entryPoint); return exec(funcPtr); } else { - // TODO: add a FunctionPassManager - llvmIsel.buildLLVMModule(module, llvmModule, 0); + llvm::FunctionPassManager functionPassManager(llvmModule); + // Set up the optimizer pipeline. + // Promote allocas to registers. + functionPassManager.add(llvm::createPromoteMemoryToRegisterPass()); + // Provide basic AliasAnalysis support for GVN. + functionPassManager.add(llvm::createBasicAliasAnalysisPass()); + // Do simple "peephole" optimizations and bit-twiddling optzns. + functionPassManager.add(llvm::createInstructionCombiningPass()); + // Reassociate expressions. + functionPassManager.add(llvm::createReassociatePass()); + // Eliminate Common SubExpressions. + functionPassManager.add(llvm::createGVNPass()); + // Simplify the control flow graph (deleting unreachable blocks, etc). + functionPassManager.add(llvm::createCFGSimplificationPass()); + + functionPassManager.doInitialization(); + + llvmIsel.buildLLVMModule(module, llvmModule, &functionPassManager); // TODO: if output type is .ll, print the module to file @@ -162,12 +180,16 @@ int compileWithLLVM(IR::Module *module, const QString &fileName, int (*exec)(voi llvm::TargetMachine::CodeGenFileType ft; QString ofName; - ft = llvm::TargetMachine::CGFT_ObjectFile; - ofName = fileName + QLatin1String(".o"); - - // TODO: - // ft = llvm::TargetMachine::CGFT_AssemblyFile; - // ofName = fileName + QLatin1String(".s"); + if (outputType == LLVMOutputObject) { + ft = llvm::TargetMachine::CGFT_ObjectFile; + ofName = fileName + QLatin1String(".o"); + } else if (outputType == LLVMOutputAssembler) { + ft = llvm::TargetMachine::CGFT_AssemblyFile; + ofName = fileName + QLatin1String(".s"); + } else { + // ft is not used. + ofName = fileName + QLatin1String(".ll"); + } llvm::raw_fd_ostream dest(ofName.toUtf8().constData(), err, llvm::raw_fd_ostream::F_Binary); llvm::formatted_raw_ostream destf(dest); @@ -176,15 +198,25 @@ int compileWithLLVM(IR::Module *module, const QString &fileName, int (*exec)(voi delete llvmModule; } - llvm::PassManager PM; - PM.add(llvm::createScalarReplAggregatesPass()); - PM.add(llvm::createInstructionCombiningPass()); - PM.add(llvm::createGlobalOptimizerPass()); - PM.add(llvm::createFunctionInliningPass(25)); - if (targetMachine->addPassesToEmitFile(PM, destf, ft)) { - std::cerr << err << " (probably no DataLayout in TargetMachine)" << std::endl; - } else { - PM.run(*llvmModule); + llvm::PassManager globalPassManager; + globalPassManager.add(llvm::createScalarReplAggregatesPass()); + globalPassManager.add(llvm::createInstructionCombiningPass()); + globalPassManager.add(llvm::createGlobalOptimizerPass()); + globalPassManager.add(llvm::createFunctionInliningPass(25)); +// globalPassManager.add(llvm::createFunctionInliningPass(125)); + + if (outputType == LLVMOutputObject || outputType == LLVMOutputAssembler) { + if (targetMachine->addPassesToEmitFile(globalPassManager, destf, ft)) { + std::cerr << err << " (probably no DataLayout in TargetMachine)" << std::endl; + } else { + globalPassManager.run(*llvmModule); + + destf.flush(); + dest.flush(); + } + } else { // .ll + globalPassManager.run(*llvmModule); + llvmModule->print(destf, 0); destf.flush(); dest.flush(); @@ -261,6 +293,7 @@ void LLVMInstructionSelection::buildLLVMModule(IR::Module *module, llvm::Module foreach (IR::Function *function, module->functions) (void) compileLLVMFunction(function); + qSwap(_fpm, fpm); qSwap(_llvmModule, llvmModule); } -- cgit v1.2.3 From 72c07ef0469838f5776824065faf9efda0261f9e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 13 Nov 2012 13:44:16 +0100 Subject: Give an error message when a JS file does not exist. Instead of silently failing, which is a bit ambiguous in case of test262. Change-Id: I8b8dc066df63f93273ccc6c27547edfcd1a68cb7 Reviewed-by: Simon Hausmann --- main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/main.cpp b/main.cpp index 530757ed4e..8b1b0ac53b 100644 --- a/main.cpp +++ b/main.cpp @@ -198,6 +198,9 @@ int compileFiles(const QStringList &files, QQmlJS::LLVMOutputType outputType) int result = compile(fileName, source, outputType); if (result != EXIT_SUCCESS) return result; + } else { + std::cerr << "Error: cannot open file " << fileName.toUtf8().constData() << std::endl; + return EXIT_FAILURE; } } return EXIT_SUCCESS; @@ -417,6 +420,9 @@ int main(int argc, char *argv[]) return exitCode; if (errorInTestHarness) return EXIT_FAILURE; + } else { + std::cerr << "Error: cannot open file " << fn.toUtf8().constData() << std::endl; + return EXIT_FAILURE; } } } return EXIT_SUCCESS; -- cgit v1.2.3 From 2049a3e9740970f3c2a5633fb9211f5d5865831f Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 13 Nov 2012 12:25:23 +0100 Subject: Remove the tempRegister from moth by loading/storing values directly. Change-Id: I51982d5852db2e10234620d63c235484c5b8a573 Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 64 ++++++++++++++++------ moth/qv4isel_moth.cpp | 140 +++++++++++++++++++++++++++++-------------------- moth/qv4isel_moth_p.h | 9 ++-- moth/qv4vme_moth.cpp | 94 ++++++++++++++++++--------------- 4 files changed, 189 insertions(+), 118 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 6fad95e083..e37f302f4c 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -6,15 +6,13 @@ #define FOR_EACH_MOTH_INSTR(F) \ F(Ret, ret) \ - F(LoadUndefined, common) \ - F(LoadNull, common) \ - F(LoadFalse, common) \ - F(LoadTrue, common) \ + F(LoadUndefined, loadUndefined) \ + F(LoadNull, loadNull) \ + F(LoadFalse, loadFalse) \ + F(LoadTrue, loadTrue) \ F(LoadNumber, loadNumber) \ F(LoadString, loadString) \ F(LoadClosure, loadClosure) \ - F(StoreTemp, storeTemp) \ - F(LoadTemp, loadTemp) \ F(MoveTemp, moveTemp) \ F(LoadName, loadName) \ F(StoreName, storeName) \ @@ -74,18 +72,27 @@ union Instr struct instr_common { MOTH_INSTR_HEADER + int tempIndex; }; struct instr_ret { MOTH_INSTR_HEADER int tempIndex; }; - struct instr_storeTemp { + struct instr_loadUndefined { MOTH_INSTR_HEADER - int tempIndex; + int targetTempIndex; }; - struct instr_loadTemp { + struct instr_loadNull { MOTH_INSTR_HEADER - int tempIndex; + int targetTempIndex; + }; + struct instr_loadFalse { + MOTH_INSTR_HEADER + int targetTempIndex; + }; + struct instr_loadTrue { + MOTH_INSTR_HEADER + int targetTempIndex; }; struct instr_moveTemp { MOTH_INSTR_HEADER @@ -95,30 +102,37 @@ union Instr struct instr_loadNumber { MOTH_INSTR_HEADER double value; + int targetTempIndex; }; struct instr_loadString { MOTH_INSTR_HEADER VM::String *value; - }; + int targetTempIndex; + }; struct instr_loadClosure { MOTH_INSTR_HEADER IR::Function *value; + int targetTempIndex; }; struct instr_loadName { MOTH_INSTR_HEADER VM::String *name; + int targetTempIndex; }; struct instr_storeName { MOTH_INSTR_HEADER VM::String *name; + int sourceTemp; }; struct instr_loadProperty { MOTH_INSTR_HEADER int baseTemp; + int targetTempIndex; VM::String *name; }; struct instr_storeProperty { MOTH_INSTR_HEADER + int sourceTemp; int baseTemp; VM::String *name; }; @@ -126,9 +140,11 @@ union Instr MOTH_INSTR_HEADER int base; int index; + int targetTempIndex; }; struct instr_storeElement { MOTH_INSTR_HEADER + int sourceTemp; int base; int index; }; @@ -140,12 +156,16 @@ union Instr MOTH_INSTR_HEADER quint32 argc; quint32 args; + int destIndex; + int targetTempIndex; }; struct instr_callProperty { MOTH_INSTR_HEADER VM::String *name; + int baseTemp; quint32 argc; quint32 args; + int targetTempIndex; }; struct instr_callBuiltin { MOTH_INSTR_HEADER @@ -160,30 +180,36 @@ union Instr } builtin; quint32 argc; quint32 args; + int targetTempIndex; }; struct instr_callBuiltinDeleteMember { MOTH_INSTR_HEADER int base; VM::String *member; + int targetTempIndex; }; struct instr_callBuiltinDeleteSubscript { MOTH_INSTR_HEADER int base; int index; + int targetTempIndex; }; struct instr_callBuiltinDeleteName { MOTH_INSTR_HEADER VM::String *name; + int targetTempIndex; }; struct instr_callBuiltinDeleteValue { MOTH_INSTR_HEADER int tempIndex; + int targetTempIndex; }; struct instr_createValue { MOTH_INSTR_HEADER int func; quint32 argc; quint32 args; + int targetTempIndex; }; struct instr_createProperty { MOTH_INSTR_HEADER @@ -191,32 +217,38 @@ union Instr VM::String *name; quint32 argc; quint32 args; + int targetTempIndex; }; struct instr_createActivationProperty { MOTH_INSTR_HEADER VM::String *name; quint32 argc; quint32 args; + int targetTempIndex; }; struct instr_jump { MOTH_INSTR_HEADER ptrdiff_t offset; + int tempIndex; }; struct instr_unop { MOTH_INSTR_HEADER - int e; VM::Value (*alu)(const VM::Value value, VM::Context *ctx); + int e; + int targetTempIndex; }; struct instr_binop { MOTH_INSTR_HEADER + VM::Value (*alu)(const VM::Value , const VM::Value, VM::Context *); + int targetTempIndex; ValueOrTemp lhs; ValueOrTemp rhs; unsigned lhsIsTemp:1; unsigned rhsIsTemp:1; - VM::Value (*alu)(const VM::Value , const VM::Value, VM::Context *); }; struct instr_loadThis { MOTH_INSTR_HEADER + int targetTempIndex; }; struct instr_inplaceElementOp { MOTH_INSTR_HEADER @@ -241,8 +273,10 @@ union Instr instr_common common; instr_ret ret; - instr_storeTemp storeTemp; - instr_loadTemp loadTemp; + instr_loadUndefined loadUndefined; + instr_loadNull loadNull; + instr_loadFalse loadFalse; + instr_loadTrue loadTrue; instr_moveTemp moveTemp; instr_loadNumber loadNumber; instr_loadString loadString; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 94116580b0..9550bb6f8e 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -41,7 +41,7 @@ void InstructionSelection::operator()(IR::Function *function) _function->code = VME::exec; _function->codeData = _ccode; - int locals = _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments; + int locals = _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments + 1; assert(locals >= 0); Instruction::Push push; @@ -72,19 +72,24 @@ void InstructionSelection::operator()(IR::Function *function) qSwap(_function, function); } -void InstructionSelection::callActivationProperty(IR::Call *c) +void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempIndex) { IR::Name *baseName = c->base->asName(); Q_ASSERT(baseName); switch (baseName->builtin) { case IR::Name::builtin_invalid: { + const int scratchIndex = scratchTempIndex(); + Instruction::LoadName load; load.name = _engine->newString(*baseName->id); + load.targetTempIndex = scratchIndex; addInstruction(load); Instruction::CallValue call; prepareCallArgs(c->args, call.argc, call.args); + call.destIndex = scratchIndex; + call.targetTempIndex = targetTempIndex; addInstruction(call); } break; @@ -95,6 +100,7 @@ void InstructionSelection::callActivationProperty(IR::Call *c) Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_typeof; prepareCallArgs(c->args, call.argc, call.args); + call.targetTempIndex = targetTempIndex; addInstruction(call); } break; @@ -105,24 +111,28 @@ void InstructionSelection::callActivationProperty(IR::Call *c) Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_throw; prepareCallArgs(c->args, call.argc, call.args); + call.targetTempIndex = targetTempIndex; addInstruction(call); } break; case IR::Name::builtin_create_exception_handler: { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_create_exception_handler; + call.targetTempIndex = targetTempIndex; addInstruction(call); } break; case IR::Name::builtin_delete_exception_handler: { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_delete_exception_handler; + call.targetTempIndex = targetTempIndex; addInstruction(call); } break; case IR::Name::builtin_get_exception: { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_get_exception; + call.targetTempIndex = targetTempIndex; addInstruction(call); } break; @@ -130,6 +140,7 @@ void InstructionSelection::callActivationProperty(IR::Call *c) Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_foreach_iterator_object; prepareCallArgs(c->args, call.argc, call.args); + call.targetTempIndex = targetTempIndex; addInstruction(call); } break; @@ -137,6 +148,7 @@ void InstructionSelection::callActivationProperty(IR::Call *c) Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_foreach_next_property_name; prepareCallArgs(c->args, call.argc, call.args); + call.targetTempIndex = targetTempIndex; addInstruction(call); } break; @@ -145,19 +157,23 @@ void InstructionSelection::callActivationProperty(IR::Call *c) Instruction::CallBuiltinDeleteMember call; call.base = m->base->asTemp()->index; call.member = _engine->newString(*m->name); + call.targetTempIndex = targetTempIndex; addInstruction(call); } else if (IR::Subscript *ss = c->args->expr->asSubscript()) { Instruction::CallBuiltinDeleteSubscript call; call.base = m->base->asTemp()->index; call.index = ss->index->asTemp()->index; + call.targetTempIndex = targetTempIndex; addInstruction(call); } else if (IR::Name *n = c->args->expr->asName()) { Instruction::CallBuiltinDeleteName call; call.name = _engine->newString(*n->id); + call.targetTempIndex = targetTempIndex; addInstruction(call); } else { Instruction::CallBuiltinDeleteValue call; call.tempIndex = c->args->expr->asTemp()->index; + call.targetTempIndex = targetTempIndex; addInstruction(call); } } break; @@ -168,43 +184,39 @@ void InstructionSelection::callActivationProperty(IR::Call *c) } } -void InstructionSelection::callValue(IR::Call *c) +void InstructionSelection::callValue(IR::Call *c, int targetTempIndex) { IR::Temp *t = c->base->asTemp(); Q_ASSERT(t); - Instruction::LoadTemp load; - load.tempIndex = t->index; - addInstruction(load); - Instruction::CallValue call; prepareCallArgs(c->args, call.argc, call.args); + call.destIndex = t->index; + call.targetTempIndex = targetTempIndex; addInstruction(call); } -void InstructionSelection::callProperty(IR::Call *c) +void InstructionSelection::callProperty(IR::Call *c, int targetTempIndex) { IR::Member *m = c->base->asMember(); Q_ASSERT(m); - // load the base - Instruction::LoadTemp load; - load.tempIndex = m->base->asTemp()->index; - addInstruction(load); - // call the property on the loaded base Instruction::CallProperty call; + call.baseTemp = m->base->asTemp()->index; call.name = _engine->newString(*m->name); prepareCallArgs(c->args, call.argc, call.args); + call.targetTempIndex = targetTempIndex; addInstruction(call); } -void InstructionSelection::construct(IR::New *ctor) +void InstructionSelection::construct(IR::New *ctor, int targetTempIndex) { if (IR::Name *baseName = ctor->base->asName()) { Instruction::CreateActivationProperty create; create.name = _engine->newString(*baseName->id); prepareCallArgs(ctor->args, create.argc, create.args); + create.targetTempIndex = targetTempIndex; addInstruction(create); } else if (IR::Member *member = ctor->base->asMember()) { IR::Temp *base = member->base->asTemp(); @@ -214,11 +226,13 @@ void InstructionSelection::construct(IR::New *ctor) create.base = base->index; create.name = _engine->newString(*member->name); prepareCallArgs(ctor->args, create.argc, create.args); + create.targetTempIndex = targetTempIndex; addInstruction(create); } else if (IR::Temp *baseTemp = ctor->base->asTemp()) { Instruction::CreateValue create; create.func = baseTemp->index; prepareCallArgs(ctor->args, create.argc, create.args); + create.targetTempIndex = targetTempIndex; addInstruction(create); } else { qWarning(" NEW"); @@ -230,13 +244,15 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint argc = 0; args = 0; + /* int locals = _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments; + The condition for this case is wrong: the locals check is incorrect: if (e && e->next == 0 && e->expr->asTemp()->index >= 0 && e->expr->asTemp()->index < locals) { // We pass single arguments as references to the stack argc = 1; args = e->expr->asTemp()->index; - } else if (e) { + } else */if (e) { // We need to move all the temps into the function arg array int argLocation = _function->tempCount - _function->locals.size(); assert(argLocation >= 0); @@ -256,17 +272,17 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint void InstructionSelection::visitExp(IR::Exp *s) { if (IR::Call *c = s->expr->asCall()) { + // These are calls where the result is ignored. + const int targetTempIndex = scratchTempIndex(); if (c->base->asName()) { - callActivationProperty(c); + callActivationProperty(c, targetTempIndex); } else if (c->base->asTemp()) { - callValue(c); + callValue(c, targetTempIndex); } else if (c->base->asMember()) { - callProperty(c); + callProperty(c, targetTempIndex); } else { Q_UNREACHABLE(); } - - // TODO: check if we should store the return value ? } else { Q_UNREACHABLE(); } @@ -355,57 +371,77 @@ static inline ALUFunction aluOpFunction(IR::AluOp op) void InstructionSelection::visitMove(IR::Move *s) { if (IR::Temp *t = s->target->asTemp()) { + const int targetTempIndex = t->index; // Check what kind of load it is, and generate the instruction for that. // The store to the temp (the target) is done afterwards. if (IR::Name *n = s->source->asName()) { Q_UNUSED(n); if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. - addInstruction(Instruction::LoadThis()); + Instruction::LoadThis load; + load.targetTempIndex = targetTempIndex; + addInstruction(load); } else { Instruction::LoadName load; load.name = _engine->newString(*n->id); + load.targetTempIndex = targetTempIndex; addInstruction(load); } } else if (IR::Const *c = s->source->asConst()) { switch (c->type) { - case IR::UndefinedType: - addInstruction(Instruction::LoadUndefined()); - break; - case IR::NullType: - addInstruction(Instruction::LoadNull()); - break; + case IR::UndefinedType: { + Instruction::LoadUndefined load; + load.targetTempIndex = targetTempIndex; + addInstruction(load); + } break; + case IR::NullType: { + Instruction::LoadNull load; + load.targetTempIndex = targetTempIndex; + addInstruction(load); + } break; case IR::BoolType: - if (c->value) addInstruction(Instruction::LoadTrue()); - else addInstruction(Instruction::LoadFalse()); + if (c->value) { + Instruction::LoadTrue load; + load.targetTempIndex = targetTempIndex; + addInstruction(load); + } else { + Instruction::LoadFalse load; + load.targetTempIndex = targetTempIndex; + addInstruction(load); + } break; case IR::NumberType: { Instruction::LoadNumber load; load.value = c->value; + load.targetTempIndex = targetTempIndex; addInstruction(load); - } break; + } break; default: Q_UNREACHABLE(); break; } } else if (IR::Temp *t2 = s->source->asTemp()) { - Instruction::LoadTemp load; - load.tempIndex = t2->index; - addInstruction(load); + Instruction::MoveTemp move; + move.fromTempIndex = t2->index; + move.toTempIndex = targetTempIndex; + addInstruction(move); } else if (IR::String *str = s->source->asString()) { Instruction::LoadString load; load.value = _engine->newString(*str->value); + load.targetTempIndex = targetTempIndex; addInstruction(load); } else if (IR::Closure *clos = s->source->asClosure()) { Instruction::LoadClosure load; load.value = clos->value; + load.targetTempIndex = targetTempIndex; addInstruction(load); } else if (IR::New *ctor = s->source->asNew()) { - construct(ctor); + construct(ctor, targetTempIndex); } else if (IR::Member *m = s->source->asMember()) { if (IR::Temp *base = m->base->asTemp()) { Instruction::LoadProperty load; load.baseTemp = base->index; load.name = _engine->newString(*m->name); + load.targetTempIndex = targetTempIndex; addInstruction(load); } else { qWarning(" MEMBER"); @@ -414,6 +450,7 @@ void InstructionSelection::visitMove(IR::Move *s) Instruction::LoadElement load; load.base = ss->base->asTemp()->index; load.index = ss->index->asTemp()->index; + load.targetTempIndex = targetTempIndex; addInstruction(load); } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { @@ -431,6 +468,7 @@ void InstructionSelection::visitMove(IR::Move *s) Instruction::Unop unop; unop.alu = op; unop.e = e->index; + unop.targetTempIndex = targetTempIndex; addInstruction(unop); } else { qWarning(" UNOP1"); @@ -445,22 +483,19 @@ void InstructionSelection::visitMove(IR::Move *s) binop.alu = aluOpFunction(b->op); binop.lhsIsTemp = toValueOrTemp(b->left, binop.lhs); binop.rhsIsTemp = toValueOrTemp(b->right, binop.rhs); + binop.targetTempIndex = targetTempIndex; addInstruction(binop); } else if (IR::Call *c = s->source->asCall()) { if (c->base->asName()) { - callActivationProperty(c); + callActivationProperty(c, targetTempIndex); } else if (c->base->asMember()) { - callProperty(c); + callProperty(c, targetTempIndex); } else if (c->base->asTemp()) { - callValue(c); + callValue(c, targetTempIndex); } else { Q_UNREACHABLE(); } } - - Instruction::StoreTemp st; - st.tempIndex = t->index; - addInstruction(st); return; } else if (IR::Name *n = s->target->asName()) { if (IR::Temp *t = s->source->asTemp()) { @@ -488,11 +523,8 @@ void InstructionSelection::visitMove(IR::Move *s) addInstruction(ieo); return; } else if (s->op == IR::OpInvalid) { - Instruction::LoadTemp load; - load.tempIndex = t->index; - addInstruction(load); - Instruction::StoreName store; + store.sourceTemp = t->index; store.name = _engine->newString(*n->id); addInstruction(store); return; @@ -526,11 +558,8 @@ void InstructionSelection::visitMove(IR::Move *s) addInstruction(ieo); return; } else if (s->op == IR::OpInvalid) { - Instruction::LoadTemp load; - load.tempIndex = t->index; - addInstruction(load); - Instruction::StoreElement store; + store.sourceTemp = t->index; store.base = ss->base->asTemp()->index; store.index = ss->index->asTemp()->index; addInstruction(store); @@ -565,11 +594,8 @@ void InstructionSelection::visitMove(IR::Move *s) addInstruction(imo); return; } else if (s->op == IR::OpInvalid) { - Instruction::LoadTemp load; - load.tempIndex = t->index; - addInstruction(load); - Instruction::StoreProperty store; + store.sourceTemp = t->index; store.baseTemp = m->base->asTemp()->index; store.name = _engine->newString(*m->name); addInstruction(store); @@ -596,15 +622,16 @@ void InstructionSelection::visitJump(IR::Jump *s) void InstructionSelection::visitCJump(IR::CJump *s) { + int tempIndex; if (IR::Temp *t = s->cond->asTemp()) { - Instruction::LoadTemp load; - load.tempIndex = t->index; - addInstruction(load); + tempIndex = t->index; } else if (IR::Binop *b = s->cond->asBinop()) { + tempIndex = scratchTempIndex(); Instruction::Binop binop; binop.alu = aluOpFunction(b->op); binop.lhsIsTemp = toValueOrTemp(b->left, binop.lhs); binop.rhsIsTemp = toValueOrTemp(b->right, binop.rhs); + binop.targetTempIndex = tempIndex; addInstruction(binop); } else { Q_UNREACHABLE(); @@ -612,6 +639,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) Instruction::CJump jump; jump.offset = 0; + jump.tempIndex = tempIndex; ptrdiff_t tl = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); _patches[s->iftrue].append(tl); diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 6c92564bc7..bb024c58f3 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -35,11 +35,12 @@ private: }; void simpleMove(IR::Move *); - void callActivationProperty(IR::Call *c); - void callValue(IR::Call *c); - void callProperty(IR::Call *c); - void construct(IR::New *ctor); + void callActivationProperty(IR::Call *c, int targetTempIndex); + void callValue(IR::Call *c, int targetTempIndex); + void callProperty(IR::Call *c, int targetTempIndex); + void construct(IR::New *ctor, int targetTempIndex); void prepareCallArgs(IR::ExprList *, quint32 &, quint32 &); + int scratchTempIndex() { return _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments; } template inline ptrdiff_t addInstruction(const InstrData &data); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 9bd3bf8042..66df75f79e 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -49,7 +49,22 @@ using namespace QQmlJS::Moth; static inline VM::Value *tempValue(QQmlJS::VM::Context *context, QVector &stack, int index) { - TRACE(tempValue, "index = %d / arg = %d / local = %d, stack size = %d", index, (-index-1), index - stack.count(), stack.size()); +#ifdef DO_TRACE_INSTR + const char *kind; + int pos; + if (index < 0) { + kind = "arg"; + pos = -index - 1; + } else if (index < (int) context->varCount) { + kind = "local"; + pos = index; + } else { + kind = "temp"; + pos = index - context->varCount; + } + fprintf(stderr, " tempValue: index = %d : %s = %d, stack size = %d\n", + index, kind, pos, stack.size()); +#endif // DO_TRACE_INSTR if (index < 0) { const int arg = -index - 1; @@ -84,7 +99,6 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code #endif QVector stack; - VM::Value tempRegister; #ifdef MOTH_THREADED_INTERPRETER const Instr *genericInstr = reinterpret_cast(code); @@ -95,75 +109,68 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code switch (genericInstr->common.instructionType) { #endif - MOTH_BEGIN_INSTR(StoreTemp) - TEMP(instr.tempIndex) = tempRegister; - MOTH_END_INSTR(StoreTemp) - - MOTH_BEGIN_INSTR(LoadTemp) - tempRegister = TEMP(instr.tempIndex); - MOTH_END_INSTR(LoadTemp) - MOTH_BEGIN_INSTR(MoveTemp) - TEMP(instr.toTempIndex) = TEMP(instr.fromTempIndex); + VM::Value tmp = TEMP(instr.fromTempIndex); + TEMP(instr.toTempIndex) = tmp; MOTH_END_INSTR(MoveTemp) MOTH_BEGIN_INSTR(LoadUndefined) - tempRegister = VM::Value::undefinedValue(); + TEMP(instr.targetTempIndex) = VM::Value::undefinedValue(); MOTH_END_INSTR(LoadUndefined) MOTH_BEGIN_INSTR(LoadNull) - tempRegister = VM::Value::nullValue(); + TEMP(instr.targetTempIndex) = VM::Value::nullValue(); MOTH_END_INSTR(LoadNull) MOTH_BEGIN_INSTR(LoadTrue) - tempRegister = VM::Value::fromBoolean(true); + TEMP(instr.targetTempIndex) = VM::Value::fromBoolean(true); MOTH_END_INSTR(LoadTrue) MOTH_BEGIN_INSTR(LoadFalse) - tempRegister = VM::Value::fromBoolean(false); + TEMP(instr.targetTempIndex) = VM::Value::fromBoolean(false); MOTH_END_INSTR(LoadFalse) MOTH_BEGIN_INSTR(LoadNumber) TRACE(inline, "number = %f", instr.value); - tempRegister = VM::Value::fromDouble(instr.value); + TEMP(instr.targetTempIndex) = VM::Value::fromDouble(instr.value); MOTH_END_INSTR(LoadNumber) MOTH_BEGIN_INSTR(LoadString) - tempRegister = VM::Value::fromString(instr.value); + TEMP(instr.targetTempIndex) = VM::Value::fromString(instr.value); MOTH_END_INSTR(LoadString) MOTH_BEGIN_INSTR(LoadClosure) - tempRegister = __qmljs_init_closure(instr.value, context); + TEMP(instr.targetTempIndex) = __qmljs_init_closure(instr.value, context); MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); - tempRegister = __qmljs_get_activation_property(context, instr.name); + TEMP(instr.targetTempIndex) = __qmljs_get_activation_property(context, instr.name); MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(StoreName) TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); - __qmljs_set_activation_property(context, instr.name, tempRegister); + __qmljs_set_activation_property(context, instr.name, TEMP(instr.sourceTemp)); MOTH_END_INSTR(StoreName) MOTH_BEGIN_INSTR(LoadElement) - tempRegister = __qmljs_get_element(context, TEMP(instr.base), TEMP(instr.index)); + TEMP(instr.targetTempIndex) = __qmljs_get_element(context, TEMP(instr.base), TEMP(instr.index)); MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) - __qmljs_set_element(context, TEMP(instr.base), TEMP(instr.index), tempRegister); + __qmljs_set_element(context, TEMP(instr.base), TEMP(instr.index), TEMP(instr.sourceTemp)); MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) TRACE(inline, "base temp = %d, property name = %s", instr.baseTemp, instr.name->toQString().toUtf8().constData()); VM::Value base = TEMP(instr.baseTemp); - tempRegister = __qmljs_get_property(context, base, instr.name); + TEMP(instr.targetTempIndex) = __qmljs_get_property(context, base, instr.name); MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(StoreProperty) TRACE(inline, "base temp = %d, property name = %s", instr.baseTemp, instr.name->toQString().toUtf8().constData()); VM::Value base = TEMP(instr.baseTemp); - __qmljs_set_property(context, base, instr.name, tempRegister); + __qmljs_set_property(context, base, instr.name, TEMP(instr.sourceTemp)); MOTH_END_INSTR(StoreProperty) MOTH_BEGIN_INSTR(Push) @@ -172,13 +179,14 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_END_INSTR(Push) MOTH_BEGIN_INSTR(CallValue) + TRACE(Call, "argStart = %d, argc = %d, result temp index = %d", instr.args, instr.argc, instr.targetTempIndex); VM::Value *args = stack.data() + instr.args; - tempRegister = __qmljs_call_value(context, VM::Value::undefinedValue(), tempRegister, args, instr.argc); + TEMP(instr.targetTempIndex) = __qmljs_call_value(context, VM::Value::undefinedValue(), TEMP(instr.destIndex), args, instr.argc); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) VM::Value *args = stack.data() + instr.args; - tempRegister = __qmljs_call_property(context, tempRegister, instr.name, args, instr.argc); + TEMP(instr.targetTempIndex) = __qmljs_call_property(context, TEMP(instr.baseTemp), instr.name, args, instr.argc); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallBuiltin) @@ -186,60 +194,60 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code void *buf; switch (instr.builtin) { case Instr::instr_callBuiltin::builtin_typeof: - tempRegister = __qmljs_builtin_typeof(args[0], context); + TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof(args[0], context); break; case Instr::instr_callBuiltin::builtin_throw: - __qmljs_builtin_typeof(args[0], context); + TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof(args[0], context); break; case Instr::instr_callBuiltin::builtin_create_exception_handler: buf = __qmljs_create_exception_handler(context); - tempRegister = VM::Value::fromInt32(setjmp(* static_cast(buf))); + TEMP(instr.targetTempIndex) = VM::Value::fromInt32(setjmp(* static_cast(buf))); break; case Instr::instr_callBuiltin::builtin_delete_exception_handler: __qmljs_delete_exception_handler(context); break; case Instr::instr_callBuiltin::builtin_get_exception: - tempRegister = __qmljs_get_exception(context); + TEMP(instr.targetTempIndex) = __qmljs_get_exception(context); break; case Instr::instr_callBuiltin::builtin_foreach_iterator_object: - tempRegister = __qmljs_foreach_iterator_object(args[0], context); + TEMP(instr.targetTempIndex) = __qmljs_foreach_iterator_object(args[0], context); break; case Instr::instr_callBuiltin::builtin_foreach_next_property_name: - tempRegister = __qmljs_foreach_next_property_name(args[0]); + TEMP(instr.targetTempIndex) = __qmljs_foreach_next_property_name(args[0]); break; } MOTH_END_INSTR(CallBuiltin) MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) - tempRegister = __qmljs_delete_member(context, TEMP(instr.base), instr.member); + TEMP(instr.targetTempIndex) = __qmljs_delete_member(context, TEMP(instr.base), instr.member); MOTH_END_INSTR(CallBuiltinDeleteMember) MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) - tempRegister = __qmljs_delete_subscript(context, TEMP(instr.base), TEMP(instr.index)); + TEMP(instr.targetTempIndex) = __qmljs_delete_subscript(context, TEMP(instr.base), TEMP(instr.index)); MOTH_END_INSTR(CallBuiltinDeleteSubscript) MOTH_BEGIN_INSTR(CallBuiltinDeleteName) - tempRegister = __qmljs_delete_property(context, instr.name); + TEMP(instr.targetTempIndex) = __qmljs_delete_property(context, instr.name); MOTH_END_INSTR(CallBuiltinDeleteName) MOTH_BEGIN_INSTR(CallBuiltinDeleteValue) - tempRegister = __qmljs_delete_value(context, TEMP(instr.tempIndex)); + TEMP(instr.targetTempIndex) = __qmljs_delete_value(context, TEMP(instr.tempIndex)); MOTH_END_INSTR(CallBuiltinDeleteValue) MOTH_BEGIN_INSTR(CreateValue) VM::Value *args = stack.data() + instr.args; - tempRegister = __qmljs_construct_value(context, TEMP(instr.func), args, instr.argc); + TEMP(instr.targetTempIndex) = __qmljs_construct_value(context, TEMP(instr.func), args, instr.argc); MOTH_END_INSTR(CreateValue) MOTH_BEGIN_INSTR(CreateProperty) VM::Value *args = stack.data() + instr.args; - tempRegister = __qmljs_construct_property(context, TEMP(instr.base), instr.name, args, instr.argc); + TEMP(instr.targetTempIndex) = __qmljs_construct_property(context, TEMP(instr.base), instr.name, args, instr.argc); MOTH_END_INSTR(CreateProperty) MOTH_BEGIN_INSTR(CreateActivationProperty) TRACE(inline, "property name = %s, argc = %d", instr.name->toQString().toUtf8().constData(), instr.argc); VM::Value *args = stack.data() + instr.args; - tempRegister = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); + TEMP(instr.targetTempIndex) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); MOTH_END_INSTR(CreateActivationProperty) MOTH_BEGIN_INSTR(Jump) @@ -247,18 +255,18 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_END_INSTR(Jump) MOTH_BEGIN_INSTR(CJump) - if (__qmljs_to_boolean(tempRegister, context)) + if (__qmljs_to_boolean(TEMP(instr.tempIndex), context)) code = ((uchar *)&instr.offset) + instr.offset; MOTH_END_INSTR(CJump) MOTH_BEGIN_INSTR(Unop) - tempRegister = instr.alu(TEMP(instr.e), context); + TEMP(instr.targetTempIndex) = instr.alu(TEMP(instr.e), context); MOTH_END_INSTR(Unop) MOTH_BEGIN_INSTR(Binop) VM::Value lhs = instr.lhsIsTemp ? TEMP(instr.lhs.tempIndex) : instr.lhs.value; VM::Value rhs = instr.rhsIsTemp ? TEMP(instr.rhs.tempIndex) : instr.rhs.value; - tempRegister = instr.alu(lhs, rhs, context); + TEMP(instr.targetTempIndex) = instr.alu(lhs, rhs, context); MOTH_END_INSTR(Binop) MOTH_BEGIN_INSTR(Ret) @@ -267,7 +275,7 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_END_INSTR(Ret) MOTH_BEGIN_INSTR(LoadThis) - tempRegister = __qmljs_get_thisObject(context); + TEMP(instr.targetTempIndex) = __qmljs_get_thisObject(context); MOTH_END_INSTR(LoadThis) MOTH_BEGIN_INSTR(InplaceElementOp) -- cgit v1.2.3 From 614b6c0a8d1c7621b423228a4bf4d1761503b148 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 14 Nov 2012 10:12:42 +0100 Subject: Untangle the Jump/CJump interpreter instructions. Shrinks the instruction size of a Jump a bit. Change-Id: I1a6b043d13e8493d8b0d23011141d46c10414fa8 Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index e37f302f4c..09aec200d3 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -32,7 +32,7 @@ F(CreateProperty, createProperty) \ F(CreateActivationProperty, createActivationProperty) \ F(Jump, jump) \ - F(CJump, jump) \ + F(CJump, cjump) \ F(Unop, unop) \ F(Binop, binop) \ F(LoadThis, loadThis) \ @@ -229,6 +229,10 @@ union Instr struct instr_jump { MOTH_INSTR_HEADER ptrdiff_t offset; + }; + struct instr_cjump { + MOTH_INSTR_HEADER + ptrdiff_t offset; int tempIndex; }; struct instr_unop { @@ -299,6 +303,7 @@ union Instr instr_createProperty createProperty; instr_createActivationProperty createActivationProperty; instr_jump jump; + instr_cjump cjump; instr_unop unop; instr_binop binop; instr_loadThis loadThis; -- cgit v1.2.3 From 8f69d75c785ff86face31db880abdb8414d1cbfe Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 14 Nov 2012 10:15:19 +0100 Subject: Fix interpreter exception handling. The stack frame of the interpreting function is restored, but all the datastructures live on the heap. So, save them out on handler creation, and restore them afterwards. Change-Id: I84b84007cc9944b56926bf0387c2798f7841cd2a Reviewed-by: Lars Knoll --- moth/qv4vme_moth.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++---- moth/qv4vme_moth_p.h | 4 ++++ qmljs_objects.h | 13 ++++++++++++- qmljs_runtime.cpp | 13 +++++++------ 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 66df75f79e..e9c43d4701 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -197,13 +197,30 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof(args[0], context); break; case Instr::instr_callBuiltin::builtin_throw: - TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof(args[0], context); + TRACE(builtin_throw, "Throwing now...%s", ""); + __qmljs_builtin_throw(args[0], context); break; - case Instr::instr_callBuiltin::builtin_create_exception_handler: + case Instr::instr_callBuiltin::builtin_create_exception_handler: { + TRACE(builtin_create_exception_handler, "%s", ""); buf = __qmljs_create_exception_handler(context); - TEMP(instr.targetTempIndex) = VM::Value::fromInt32(setjmp(* static_cast(buf))); - break; + // The targetTempIndex is the only value we need from the instr to + // continue execution when an exception is caught. + int targetTempIndex = instr.targetTempIndex; + int didThrow = setjmp(* static_cast(buf)); + // Two ways to come here: after a create, or after a throw. + if (didThrow) + // At this point, the interpreter state can be anything but + // valid, so first restore the state. This includes all relevant + // locals. + restoreState(context, stack, targetTempIndex, code); + else + // Save the state and any variables we need when catching an + // exception, so we can restore the state at that point. + saveState(context, stack, targetTempIndex, code); + TEMP(targetTempIndex) = VM::Value::fromInt32(didThrow); + } break; case Instr::instr_callBuiltin::builtin_delete_exception_handler: + TRACE(builtin_delete_exception_handler, "%s", ""); __qmljs_delete_exception_handler(context); break; case Instr::instr_callBuiltin::builtin_get_exception: @@ -328,3 +345,24 @@ void VME::exec(VM::Context *ctxt, const uchar *code) vme(ctxt, code); } +void VME::restoreState(VM::Context *context, QVector &stack, int &targetTempIndex, const uchar *&code) +{ + typedef VM::ExecutionEngine::ExceptionHandler EH; + EH::InterpreterState *state = context->engine->unwindStack.last()->interpreterState; + assert(state); + stack.resize(state->stack.size()); + ::memcpy(stack.data(), state->stack.data(), sizeof(VM::Value) * state->stack.size()); + targetTempIndex = state->targetTempIndex; + code = state->code; +} + +void VME::saveState(VM::Context *context, const QVector &stack, int targetTempIndex, const uchar *code) +{ + typedef VM::ExecutionEngine::ExceptionHandler EH; + EH::InterpreterState *state = new EH::InterpreterState; + context->engine->unwindStack.last()->interpreterState = state; + state->stack.resize(stack.size()); + ::memcpy(state->stack.data(), stack.data(), sizeof(VM::Value) * stack.size()); + state->targetTempIndex = targetTempIndex; + state->code = code; +} diff --git a/moth/qv4vme_moth_p.h b/moth/qv4vme_moth_p.h index db1507d84d..b1bc45bab5 100644 --- a/moth/qv4vme_moth_p.h +++ b/moth/qv4vme_moth_p.h @@ -21,6 +21,10 @@ public: #ifdef MOTH_THREADED_INTERPRETER static void **instructionJumpTable(); #endif + +private: + static void restoreState(VM::Context *context, QVector &stack, int &targetTempIndex, const uchar *&code); + static void saveState(VM::Context *context, const QVector &stack, int targetTempIndex, const uchar *code); }; } // namespace Moth diff --git a/qmljs_objects.h b/qmljs_objects.h index 857b12bcd9..f40eab0802 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -582,13 +582,24 @@ struct ExecutionEngine String *id___proto__; struct ExceptionHandler { + struct InterpreterState { + QVector stack; + int targetTempIndex; + const uchar *code; + }; + Context *context; jmp_buf stackFrame; + InterpreterState *interpreterState; + + ExceptionHandler(): interpreterState(0) {} + ~ExceptionHandler() { delete interpreterState; } }; - QVector unwindStack; + QVector unwindStack; ExecutionEngine(); + ~ExecutionEngine() { qDeleteAll(unwindStack); } Context *newContext(); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index d70a8517bb..668be71532 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -1084,23 +1084,23 @@ void __qmljs_throw(Value value, Context *context) { assert(!context->engine->unwindStack.isEmpty()); - ExecutionEngine::ExceptionHandler handler = context->engine->unwindStack.last(); + ExecutionEngine::ExceptionHandler *handler = context->engine->unwindStack.last(); // clean up call contexts - while (context != handler.context) { + while (context != handler->context) { context->leaveCallContext(); context = context->parent; } - handler.context->result = value; + handler->context->result = value; - longjmp(handler.stackFrame, 1); + longjmp(handler->stackFrame, 1); } void *__qmljs_create_exception_handler(Context *context) { - context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler()); - ExecutionEngine::ExceptionHandler *handler = &context->engine->unwindStack.last(); + ExecutionEngine::ExceptionHandler *handler = new ExecutionEngine::ExceptionHandler; + context->engine->unwindStack.append(handler); handler->context = context; return handler->stackFrame; } @@ -1109,6 +1109,7 @@ void __qmljs_delete_exception_handler(Context *context) { assert(!context->engine->unwindStack.isEmpty()); + delete context->engine->unwindStack.last(); context->engine->unwindStack.pop_back(); } -- cgit v1.2.3 From 03e7d5e3ef407852c37ffb7a3008da26fce77e6a Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 14 Nov 2012 11:39:38 +0100 Subject: Fix codegen for try-statements in loops. The tricky part is that a break/continue in a try or catch block must still execute the finally block. Change-Id: I5215c1b5688d87f3c9d1dd168eed7f3555bae22a Reviewed-by: Lars Knoll --- qv4codegen.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 0276cd3686..eed854ce5f 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1970,6 +1970,23 @@ bool Codegen::visit(TryStatement *ast) IR::BasicBlock *after = _function->newBasicBlock(); assert(catchBody || finallyBody); + Loop *loop = 0; + if (_loop) { + // So there is a loop around the try statement, and now we have to setup + // a new loop-break-block and a new loop-continue-block so that if either + // a break or a continue is executed, they will go to these blocks + // which need to pop the exception handler. If there is a finally block, + // its code also gets generated into all the new break/continue blocks. + // And of course, as there can be a labeled break/continue, we have to + // do it for all parent loops as well. + for (Loop *it = _loop, **it2 = &loop; it; it = it->parent) { + *it2 = new Loop(it->node, _function->newBasicBlock(), _function->newBasicBlock(), 0); + (*it2)->labelledStatement = it->labelledStatement; + it2 = &(*it2)->parent; + } + std::swap(_loop, loop); + } + int inCatch = 0; if (catchBody && finallyBody) { inCatch = _block->newTemp(); @@ -2004,12 +2021,16 @@ bool Codegen::visit(TryStatement *ast) // otherwise remove the exception handler, so that throw inside catch will // give the correct result _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); + + // the exception handler is deleted, so we can restore the + // original break/continue blocks. + std::swap(_loop, loop); } const int exception = _block->newTemp(); move(_block->TEMP(exception), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0)); - // the variable ued in the catch statement is local and hides any global + // the variable used in the catch statement is local and hides any global // variable with the same name. int hiddenIndex = _env->findMember(ast->catchExpression->name.toString()); _env->members.insert(ast->catchExpression->name.toString(), exception); @@ -2024,10 +2045,39 @@ bool Codegen::visit(TryStatement *ast) if (finallyBody) { _block = finallyBody; _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); + + // the exception handler is deleted, so we can restore the + // original break/continue blocks. + std::swap(_loop, loop); + statement(ast->finallyExpression->statement); _block->CJUMP(_block->TEMP(hasException), _throwBlock, after); } + if (loop) { + // now do the codegen for each break/continue block + for (Loop *it = loop, *it2 = _loop; it && it2; it = it->parent, it2 = it2->parent) { + _block = it->breakBlock; + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); + if (finallyBody) + statement(ast->finallyExpression->statement); + _block->JUMP(it2->breakBlock); + + _block = it->continueBlock; + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); + if (finallyBody) + statement(ast->finallyExpression->statement); + _block->JUMP(it2->continueBlock); + } + + // we don't need the Loop struct itself anymore. + while (loop) { + Loop *next = loop->parent; + delete loop; + loop = next; + } + } + _block = after; return false; -- cgit v1.2.3 From a09d75dd1fd4d598e641ff29ee73c312a0aaba73 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 14 Nov 2012 10:10:04 +0100 Subject: Added more built-ins to the llvm backend and runtime. Change-Id: I7c63395bc80ce8d37d04f1102b02220a54050d06 Reviewed-by: Lars Knoll --- llvm_runtime.cpp | 27 +++++++++++++++++++++++++++ qv4isel_llvm.cpp | 45 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 158cb66b20..90c7a17c09 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -41,6 +41,7 @@ #include "qmljs_runtime.h" #include +#include using namespace QQmlJS::VM; @@ -457,6 +458,32 @@ void __qmljs_llvm_throw(Context *context, Value *value) __qmljs_throw(*value, context); } +void __qmljs_llvm_create_exception_handler(Context *context, Value *result) +{ + void *buf = __qmljs_create_exception_handler(context); + *result = Value::fromInt32(setjmp(* static_cast(buf))); +} + +void __qmljs_llvm_delete_exception_handler(Context *context) +{ + __qmljs_delete_exception_handler(context); +} + +void __qmljs_llvm_get_exception(Context *context, Value *result) +{ + *result = __qmljs_get_exception(context); +} + +void __qmljs_llvm_foreach_iterator_object(Context *context, Value *result, Value *in) +{ + *result = __qmljs_foreach_iterator_object(*in, context); +} + +void __qmljs_llvm_foreach_next_property_name(Value *result, Value *it) +{ + *result = __qmljs_foreach_next_property_name(*it); +} + void __qmljs_llvm_get_this_object(Context *ctx, Value *result) { *result = __qmljs_get_thisObject(ctx); diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 9b6907159f..d7643ef167 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -393,6 +393,7 @@ llvm::Value *LLVMInstructionSelection::getLLVMValue(IR::Expr *expr) qSwap(_llvmValue, llvmValue); } if (! llvmValue) { + expr->dump(qerr);qerr<getFunction("__qmljs_llvm_typeof"), _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); _llvmValue = CreateLoad(result); return; + case IR::Name::builtin_throw: + CreateCall2(_llvmModule->getFunction("__qmljs_llvm_throw"), + _llvmFunction->arg_begin(), getLLVMTempReference(e->args->expr)); + _llvmValue = llvm::UndefValue::get(_valueTy); + return; + + case IR::Name::builtin_create_exception_handler: + CreateCall2(_llvmModule->getFunction("__qmljs_llvm_create_exception_handler"), + _llvmFunction->arg_begin(), result); + _llvmValue = CreateLoad(result); + return; + + case IR::Name::builtin_delete_exception_handler: + CreateCall(_llvmModule->getFunction("__qmljs_llvm_delete_exception_handler"), + _llvmFunction->arg_begin()); + return; + + case IR::Name::builtin_get_exception: + CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_exception"), + _llvmFunction->arg_begin(), result); + _llvmValue = CreateLoad(result); + return; + + case IR::Name::builtin_foreach_iterator_object: + CreateCall3(_llvmModule->getFunction("__qmljs_llvm_foreach_iterator_object"), + _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + + case IR::Name::builtin_foreach_next_property_name: + CreateCall2(_llvmModule->getFunction("__qmljs_llvm_foreach_next_property_name"), + result, getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + case IR::Name::builtin_delete: { if (IR::Subscript *subscript = e->args->expr->asSubscript()) { CreateCall4(_llvmModule->getFunction("__qmljs_llvm_delete_subscript"), @@ -987,13 +1022,7 @@ void LLVMInstructionSelection::genCallName(IR::Call *e, llvm::Value *result) _llvmValue = CreateLoad(result); return; } - } break; - - case IR::Name::builtin_throw: - CreateCall2(_llvmModule->getFunction("__qmljs_llvm_throw"), - _llvmFunction->arg_begin(), getLLVMTempReference(e->args->expr)); - _llvmValue = llvm::UndefValue::get(_valueTy); - return; + } break; default: Q_UNREACHABLE(); -- cgit v1.2.3 From b233a73fea9bc34c97ef0fcd6d8088288394f643 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 14 Nov 2012 09:07:01 +0100 Subject: Optimise move instructions involving constants Change-Id: Ic12a54ab1c5789cc5684d38961c58f6b34f9597a Reviewed-by: Erik Verbruggen --- qv4codegen.cpp | 2 +- qv4isel_masm.cpp | 28 ++++++++++++++-------------- qv4isel_masm_p.h | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index eed854ce5f..7cbaa8a260 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -520,7 +520,7 @@ void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) { assert(target->isLValue()); - if (! source->asTemp() && (op != IR::OpInvalid || ! target->asTemp())) { + if (!source->asTemp() && !source->asConst() && (op != IR::OpInvalid || ! target->asTemp())) { unsigned t = _block->newTemp(); _block->MOVE(_block->TEMP(t), source); source = _block->TEMP(t); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 250d3241a3..6afcaafae8 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -341,8 +341,8 @@ void InstructionSelection::visitMove(IR::Move *s) if (IR::Name *n = s->target->asName()) { String *propertyName = identifier(*n->id); - if (IR::Temp *t = s->source->asTemp()) { - generateFunctionCall(Void, __qmljs_set_activation_property, ContextRegister, propertyName, t); + if (s->source->asTemp() || s->source->asConst()) { + generateFunctionCall(Void, __qmljs_set_activation_property, ContextRegister, propertyName, s->source); return; } else { Q_UNREACHABLE(); @@ -502,16 +502,16 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Member *m = s->target->asMember()) { if (IR::Temp *base = m->base->asTemp()) { - if (IR::Temp *t = s->source->asTemp()) { - generateFunctionCall(Void, __qmljs_set_property, ContextRegister, base, identifier(*m->name), t); + if (s->source->asTemp() || s->source->asConst()) { + generateFunctionCall(Void, __qmljs_set_property, ContextRegister, base, identifier(*m->name), s->source); return; } else { Q_UNREACHABLE(); } } } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (IR::Temp *t2 = s->source->asTemp()) { - generateFunctionCall(Void, __qmljs_set_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t2); + if (s->source->asTemp() || s->source->asConst()) { + generateFunctionCall(Void, __qmljs_set_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), s->source); return; } else { Q_UNIMPLEMENTED(); @@ -520,7 +520,7 @@ void InstructionSelection::visitMove(IR::Move *s) } else { // inplace assignment, e.g. x += 1, ++x, ... if (IR::Temp *t = s->target->asTemp()) { - if (IR::Temp *t2 = s->source->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { Value (*op)(const Value left, const Value right, Context *ctx) = 0; const char *opName = 0; switch (s->op) { @@ -540,11 +540,11 @@ void InstructionSelection::visitMove(IR::Move *s) break; } if (op) - generateFunctionCallImp(t, opName, op, t, t2, ContextRegister); + generateFunctionCallImp(t, opName, op, t, s->source, ContextRegister); return; } } else if (IR::Name *n = s->target->asName()) { - if (IR::Temp *t = s->source->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { void (*op)(const Value value, String *name, Context *ctx) = 0; const char *opName = 0; switch (s->op) { @@ -564,12 +564,12 @@ void InstructionSelection::visitMove(IR::Move *s) break; } if (op) { - generateFunctionCallImp(Void, opName, op, t, identifier(*n->id), ContextRegister); + generateFunctionCallImp(Void, opName, op, s->source, identifier(*n->id), ContextRegister); } return; } } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (IR::Temp *t = s->source->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { void (*op)(Value base, Value index, Value value, Context *ctx) = 0; const char *opName = 0; switch (s->op) { @@ -592,12 +592,12 @@ void InstructionSelection::visitMove(IR::Move *s) if (op) { IR::Temp* base = ss->base->asTemp(); IR::Temp* index = ss->index->asTemp(); - generateFunctionCallImp(Void, opName, op, base, index, t, ContextRegister); + generateFunctionCallImp(Void, opName, op, base, index, s->source, ContextRegister); } return; } } else if (IR::Member *m = s->target->asMember()) { - if (IR::Temp *t = s->source->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { void (*op)(Value value, Value base, String *name, Context *ctx) = 0; const char *opName = 0; switch (s->op) { @@ -620,7 +620,7 @@ void InstructionSelection::visitMove(IR::Move *s) if (op) { IR::Temp* base = m->base->asTemp(); String* member = identifier(*m->name); - generateFunctionCallImp(Void, opName, op, t, base, member, ContextRegister); + generateFunctionCallImp(Void, opName, op, s->source, base, member, ContextRegister); } return; } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index e38ce608f5..bf37b0720b 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -240,7 +240,7 @@ protected: virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); virtual void visitLeave(IR::Leave *); - virtual void visitMove(IR::Move *); + virtual void visitMove(IR::Move *s); virtual void visitJump(IR::Jump *); virtual void visitCJump(IR::CJump *); virtual void visitRet(IR::Ret *); -- cgit v1.2.3 From 4ebd6f29822f33da30e055daebb287d23404167e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 14 Nov 2012 12:18:50 +0100 Subject: Fix optimisation for passing a single argument to a call. Change-Id: I57a881ccd9f86a36d2d2ea5451046652ac0aca21 Reviewed-by: Lars Knoll --- moth/qv4isel_moth.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 9550bb6f8e..d66673d0be 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -244,15 +244,22 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint argc = 0; args = 0; - /* - int locals = _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments; + bool singleArgIsTemp = false; + if (e && e->next == 0) { + // ok, only 1 argument in the cal... + const int idx = e->expr->asTemp()->index; + if (idx >= 0) { + // not an argument to this function... + // so if it's not a local, we're in: + singleArgIsTemp = idx >= _function->locals.size(); + } + } - The condition for this case is wrong: the locals check is incorrect: - if (e && e->next == 0 && e->expr->asTemp()->index >= 0 && e->expr->asTemp()->index < locals) { - // We pass single arguments as references to the stack + if (singleArgIsTemp) { + // We pass single arguments as references to the stack, but only if it's not a local or an argument. argc = 1; args = e->expr->asTemp()->index; - } else */if (e) { + } else if (e) { // We need to move all the temps into the function arg array int argLocation = _function->tempCount - _function->locals.size(); assert(argLocation >= 0); -- cgit v1.2.3 From 399eff6704fe5645cea4fa0c3baa9f255282e9d6 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 14 Nov 2012 12:51:54 +0100 Subject: Changed the interpreter and compiler backends to move consts. See https://codereview.qt-project.org/39510 for details. Change-Id: I308364cd7d66ad2fd12e6ab7e185882fe8d1795e Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 18 ++++++++++++------ moth/qv4isel_moth.cpp | 18 +++++++++--------- moth/qv4vme_moth.cpp | 18 ++++++++++++------ qv4isel_llvm.cpp | 24 ++++++++++++------------ 4 files changed, 45 insertions(+), 33 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 09aec200d3..d6500352cc 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -122,7 +122,8 @@ union Instr struct instr_storeName { MOTH_INSTR_HEADER VM::String *name; - int sourceTemp; + ValueOrTemp source; + unsigned sourceIsTemp:1; }; struct instr_loadProperty { MOTH_INSTR_HEADER @@ -132,9 +133,10 @@ union Instr }; struct instr_storeProperty { MOTH_INSTR_HEADER - int sourceTemp; int baseTemp; VM::String *name; + ValueOrTemp source; + unsigned sourceIsTemp:1; }; struct instr_loadElement { MOTH_INSTR_HEADER @@ -144,9 +146,10 @@ union Instr }; struct instr_storeElement { MOTH_INSTR_HEADER - int sourceTemp; int base; int index; + ValueOrTemp source; + unsigned sourceIsTemp:1; }; struct instr_push { MOTH_INSTR_HEADER @@ -259,20 +262,23 @@ union Instr void (*alu)(VM::Value, VM::Value, VM::Value, VM::Context *); int targetBase; int targetIndex; - int source; + ValueOrTemp source; + unsigned sourceIsTemp:1; }; struct instr_inplaceMemberOp { MOTH_INSTR_HEADER void (*alu)(VM::Value, VM::Value, VM::String *, VM::Context *); int targetBase; VM::String *targetMember; - int source; + ValueOrTemp source; + unsigned sourceIsTemp:1; }; struct instr_inplaceNameOp { MOTH_INSTR_HEADER void (*alu)(VM::Value, VM::String *, VM::Context *); VM::String *targetName; - int source; + ValueOrTemp source; + unsigned sourceIsTemp:1; }; instr_common common; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index d66673d0be..849cd293d3 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -505,7 +505,7 @@ void InstructionSelection::visitMove(IR::Move *s) } return; } else if (IR::Name *n = s->target->asName()) { - if (IR::Temp *t = s->source->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { void (*op)(VM::Value value, VM::String *name, VM::Context *ctx) = 0; switch (s->op) { case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_name; break; @@ -526,12 +526,12 @@ void InstructionSelection::visitMove(IR::Move *s) Instruction::InplaceNameOp ieo; ieo.alu = op; ieo.targetName = _engine->newString(*n->id); - ieo.source = t->index; + ieo.sourceIsTemp = toValueOrTemp(s->source, ieo.source); addInstruction(ieo); return; } else if (s->op == IR::OpInvalid) { Instruction::StoreName store; - store.sourceTemp = t->index; + store.sourceIsTemp = toValueOrTemp(s->source, store.source); store.name = _engine->newString(*n->id); addInstruction(store); return; @@ -539,7 +539,7 @@ void InstructionSelection::visitMove(IR::Move *s) } qWarning("NAME"); } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (IR::Temp *t = s->source->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { void (*op)(VM::Value base, VM::Value index, VM::Value value, VM::Context *ctx) = 0; switch (s->op) { case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_element; break; @@ -561,21 +561,21 @@ void InstructionSelection::visitMove(IR::Move *s) ieo.alu = op; ieo.targetBase = ss->base->asTemp()->index; ieo.targetIndex = ss->index->asTemp()->index; - ieo.source = t->index; + ieo.sourceIsTemp = toValueOrTemp(s->source, ieo.source); addInstruction(ieo); return; } else if (s->op == IR::OpInvalid) { Instruction::StoreElement store; - store.sourceTemp = t->index; store.base = ss->base->asTemp()->index; store.index = ss->index->asTemp()->index; + store.sourceIsTemp = toValueOrTemp(s->source, store.source); addInstruction(store); return; } } qWarning("SUBSCRIPT"); } else if (IR::Member *m = s->target->asMember()) { - if (IR::Temp *t = s->source->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { void (*op)(VM::Value value, VM::Value base, VM::String *name, VM::Context *ctx) = 0; switch (s->op) { case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_member; break; @@ -597,14 +597,14 @@ void InstructionSelection::visitMove(IR::Move *s) imo.alu = op; imo.targetBase = m->base->asTemp()->index; imo.targetMember = _engine->newString(*m->name); - imo.source = t->index; + imo.sourceIsTemp = toValueOrTemp(s->source, imo.source); addInstruction(imo); return; } else if (s->op == IR::OpInvalid) { Instruction::StoreProperty store; - store.sourceTemp = t->index; store.baseTemp = m->base->asTemp()->index; store.name = _engine->newString(*m->name); + store.sourceIsTemp = toValueOrTemp(s->source, store.source); addInstruction(store); return; } diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index e9c43d4701..6fae010985 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -150,7 +150,8 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_BEGIN_INSTR(StoreName) TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); - __qmljs_set_activation_property(context, instr.name, TEMP(instr.sourceTemp)); + VM::Value source = instr.sourceIsTemp ? TEMP(instr.source.tempIndex) : instr.source.value; + __qmljs_set_activation_property(context, instr.name, source); MOTH_END_INSTR(StoreName) MOTH_BEGIN_INSTR(LoadElement) @@ -158,7 +159,8 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) - __qmljs_set_element(context, TEMP(instr.base), TEMP(instr.index), TEMP(instr.sourceTemp)); + VM::Value source = instr.sourceIsTemp ? TEMP(instr.source.tempIndex) : instr.source.value; + __qmljs_set_element(context, TEMP(instr.base), TEMP(instr.index), source); MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) @@ -170,7 +172,8 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_BEGIN_INSTR(StoreProperty) TRACE(inline, "base temp = %d, property name = %s", instr.baseTemp, instr.name->toQString().toUtf8().constData()); VM::Value base = TEMP(instr.baseTemp); - __qmljs_set_property(context, base, instr.name, TEMP(instr.sourceTemp)); + VM::Value source = instr.sourceIsTemp ? TEMP(instr.source.tempIndex) : instr.source.value; + __qmljs_set_property(context, base, instr.name, source); MOTH_END_INSTR(StoreProperty) MOTH_BEGIN_INSTR(Push) @@ -296,21 +299,24 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code MOTH_END_INSTR(LoadThis) MOTH_BEGIN_INSTR(InplaceElementOp) + VM::Value source = instr.sourceIsTemp ? TEMP(instr.source.tempIndex) : instr.source.value; instr.alu(TEMP(instr.targetBase), TEMP(instr.targetIndex), - TEMP(instr.source), + source, context); MOTH_END_INSTR(InplaceElementOp) MOTH_BEGIN_INSTR(InplaceMemberOp) - instr.alu(TEMP(instr.source), + VM::Value source = instr.sourceIsTemp ? TEMP(instr.source.tempIndex) : instr.source.value; + instr.alu(source, TEMP(instr.targetBase), instr.targetMember, context); MOTH_END_INSTR(InplaceMemberOp) MOTH_BEGIN_INSTR(InplaceNameOp) - instr.alu(TEMP(instr.source), + VM::Value source = instr.sourceIsTemp ? TEMP(instr.source.tempIndex) : instr.source.value; + instr.alu(source, instr.targetName, context); MOTH_END_INSTR(InplaceNameOp) diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index d7643ef167..80c55d201a 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -482,7 +482,7 @@ void LLVMInstructionSelection::genMoveSubscript(IR::Move *s) IR::Subscript *subscript = s->target->asSubscript(); llvm::Value *base = getLLVMTempReference(subscript->base); llvm::Value *index = getLLVMTempReference(subscript->index); - llvm::Value *source = getLLVMTempReference(s->source); + llvm::Value *source = toValuePtr(s->source); CreateCall4(_llvmModule->getFunction("__qmljs_llvm_set_element"), _llvmFunction->arg_begin(), base, index, source); } @@ -492,7 +492,7 @@ void LLVMInstructionSelection::genMoveMember(IR::Move *s) IR::Member *m = s->target->asMember(); llvm::Value *base = getLLVMTempReference(m->base); llvm::Value *name = getIdentifier(*m->name); - llvm::Value *source = getLLVMTempReference(s->source); + llvm::Value *source = toValuePtr(s->source); CreateCall4(_llvmModule->getFunction("__qmljs_llvm_set_property"), _llvmFunction->arg_begin(), base, name, source); } @@ -508,7 +508,7 @@ void LLVMInstructionSelection::visitMove(IR::Move *s) return; } else if (IR::Name *n = s->target->asName()) { llvm::Value *name = getIdentifier(*n->id); - llvm::Value *source = getLLVMTempReference(s->source); + llvm::Value *source = toValuePtr(s->source); CreateCall3(_llvmModule->getFunction("__qmljs_llvm_set_activation_property"), _llvmFunction->arg_begin(), name, source); return; @@ -520,7 +520,7 @@ void LLVMInstructionSelection::visitMove(IR::Move *s) } } else { if (IR::Temp *t = s->target->asTemp()) { - if (IR::Temp *t2 = s->source->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { const char *opName = 0; switch (s->op) { case IR::OpBitAnd: opName = "__qmljs_llvm_bit_and"; break; @@ -541,8 +541,8 @@ void LLVMInstructionSelection::visitMove(IR::Move *s) if (opName) { llvm::Value *target = getLLVMTemp(t); - llvm::Value *s1 = getLLVMTemp(t); - llvm::Value *s2 = getLLVMTemp(t2); + llvm::Value *s1 = toValuePtr(s->target); + llvm::Value *s2 = toValuePtr(s->source); CreateCall4(_llvmModule->getFunction(opName), _llvmFunction->arg_begin(), target, s1, s2); return; @@ -550,7 +550,7 @@ void LLVMInstructionSelection::visitMove(IR::Move *s) } } else if (IR::Name *n = s->target->asName()) { // inplace assignment, e.g. x += 1, ++x, ... - if (IR::Temp *t = s->source->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { const char *opName = 0; switch (s->op) { case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_name"; break; @@ -571,14 +571,14 @@ void LLVMInstructionSelection::visitMove(IR::Move *s) if (opName) { llvm::Value *dst = getIdentifier(*n->id); - llvm::Value *src = getLLVMTemp(t); + llvm::Value *src = toValuePtr(s->source); CreateCall3(_llvmModule->getFunction(opName), _llvmFunction->arg_begin(), dst, src); return; } } } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (IR::Temp *t = s->source->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { const char *opName = 0; switch (s->op) { case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_element"; break; @@ -600,7 +600,7 @@ void LLVMInstructionSelection::visitMove(IR::Move *s) if (opName) { llvm::Value *base = getLLVMTemp(ss->base->asTemp()); llvm::Value *index = getLLVMTemp(ss->index->asTemp()); - llvm::Value *value = getLLVMTemp(t); + llvm::Value *value = toValuePtr(s->source); CreateCall4(_llvmModule->getFunction(opName), _llvmFunction->arg_begin(), base, index, value); // TODO: checkExceptions(); @@ -608,7 +608,7 @@ void LLVMInstructionSelection::visitMove(IR::Move *s) return; } } else if (IR::Member *m = s->target->asMember()) { - if (IR::Temp *t = s->source->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { const char *opName = 0; switch (s->op) { case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_member"; break; @@ -630,7 +630,7 @@ void LLVMInstructionSelection::visitMove(IR::Move *s) if (opName) { llvm::Value *base = getLLVMTemp(m->base->asTemp()); llvm::Value *member = getIdentifier(*m->name); - llvm::Value *value = getLLVMTemp(t); + llvm::Value *value = toValuePtr(s->source); CreateCall4(_llvmModule->getFunction(opName), _llvmFunction->arg_begin(), value, base, member); // TODO: checkExceptions(); -- cgit v1.2.3 From 68b099b8f8caf0b3df1ca77dd7f2b884f1fea1ed Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 14 Nov 2012 14:12:16 +0100 Subject: Add missing message property to the ErrorObject. Change-Id: Ice16360f98d1d66162440d972c6bfbc416884474 Reviewed-by: Lars Knoll --- qmljs_objects.cpp | 7 +++++++ qmljs_objects.h | 1 + 2 files changed, 8 insertions(+) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 167dc812f2..ab9ad588fd 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -458,6 +458,13 @@ Value RegExpObject::__get__(Context *ctx, String *name) return Object::__get__(ctx, name); } +Value ErrorObject::__get__(Context *ctx, String *name) +{ + QString n = name->toQString(); + if (n == QLatin1String("message")) + return value; + return Object::__get__(ctx, name); +} void ScriptFunction::construct(VM::Context *ctx) { diff --git a/qmljs_objects.h b/qmljs_objects.h index f40eab0802..844538526e 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -528,6 +528,7 @@ struct ErrorObject: Object { ErrorObject(const Value &message): value(message) {} virtual QString className() { return QStringLiteral("Error"); } virtual ErrorObject *asErrorObject() { return this; } + virtual Value __get__(Context *ctx, String *name); }; struct ActivationObject: Object { -- cgit v1.2.3 From 559115fc6320ebc1d376da111d92c3f0d262e9d5 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 15 Nov 2012 13:12:55 +0100 Subject: Rethrow the right exception after finally if catch is missing. Save the (unhandled) exception, because there might be another try block with a catch in the finally block. Restore it at the end of the finally block. The codegen for the finally, which is also needed for the break/continue trampoline blocks, has moved into a helper method. Change-Id: Iff28506b92a3749c6a5585fb6d94f3120696e89a Reviewed-by: Lars Knoll --- qv4codegen.cpp | 52 ++++++++++++++++++++++++++++++++++++---------------- qv4codegen_p.h | 2 ++ 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 7cbaa8a260..9c5ce2c340 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -2043,31 +2043,18 @@ bool Codegen::visit(TryStatement *ast) } if (finallyBody) { - _block = finallyBody; - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); - // the exception handler is deleted, so we can restore the // original break/continue blocks. std::swap(_loop, loop); - statement(ast->finallyExpression->statement); - _block->CJUMP(_block->TEMP(hasException), _throwBlock, after); + generateFinallyBlock(finallyBody, !catchBody, ast->finallyExpression->statement, hasException, after); } if (loop) { // now do the codegen for each break/continue block for (Loop *it = loop, *it2 = _loop; it && it2; it = it->parent, it2 = it2->parent) { - _block = it->breakBlock; - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); - if (finallyBody) - statement(ast->finallyExpression->statement); - _block->JUMP(it2->breakBlock); - - _block = it->continueBlock; - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); - if (finallyBody) - statement(ast->finallyExpression->statement); - _block->JUMP(it2->continueBlock); + generateFinallyBlock(it->breakBlock, !catchBody, ast->finallyExpression->statement, hasException, it2->breakBlock); + generateFinallyBlock(it->continueBlock, !catchBody, ast->finallyExpression->statement, hasException, it2->continueBlock); } // we don't need the Loop struct itself anymore. @@ -2083,6 +2070,39 @@ bool Codegen::visit(TryStatement *ast) return false; } +void Codegen::generateFinallyBlock(IR::BasicBlock *finallyBlock, bool exceptionNeedsSaving, Block *ast, int hasException, IR::BasicBlock *after) +{ + _block = finallyBlock; + + int exception; + if (exceptionNeedsSaving) { + // save the exception so we can rethrow it later. + IR::BasicBlock *saveExceptionBlock = _function->newBasicBlock(); + IR::BasicBlock *realFinallyBlock = _function->newBasicBlock(); + _block->CJUMP(_block->TEMP(hasException), saveExceptionBlock, realFinallyBlock); + + _block = saveExceptionBlock; + exception = saveExceptionBlock->newTemp(); + move(_block->TEMP(exception), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0)); + _block->JUMP(realFinallyBlock); + + _block = realFinallyBlock; + } + + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); + if (ast) + statement(ast); + + if (exceptionNeedsSaving) { + IR::BasicBlock *restoreExceptionBlock = _function->newBasicBlock(); + _block->CJUMP(_block->TEMP(hasException), restoreExceptionBlock, after); + restoreExceptionBlock->MOVE(restoreExceptionBlock->TEMP(_returnAddress), restoreExceptionBlock->TEMP(exception)); + restoreExceptionBlock->JUMP(_throwBlock); + } else { + _block->CJUMP(_block->TEMP(hasException), _throwBlock, after); + } +} + bool Codegen::visit(VariableStatement *ast) { variableDeclarationList(ast->declarations); diff --git a/qv4codegen_p.h b/qv4codegen_p.h index bd8f3f829a..fa84beabe4 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -188,6 +188,8 @@ protected: AST::SourceElements *body, Mode mode = FunctionCode); int indexOfArgument(const QStringRef &string) const; + void generateFinallyBlock(IR::BasicBlock *finallyBlock, bool exceptionNeedsSaving, AST::Block *ast, int hasException, IR::BasicBlock *after); + void statement(AST::Statement *ast); void statement(AST::ExpressionNode *ast); void condition(AST::ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse); -- cgit v1.2.3 From 1cbec5b889fb29732337c4db3f3f1fef1241f65b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 15 Nov 2012 12:11:38 +0100 Subject: Remove unused variable and method Change-Id: I357f3062f58e76c1749c9818426be5c0eedf3327 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 6 ------ qmljs_runtime.cpp | 3 --- qmljs_runtime.h | 1 - 3 files changed, 10 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 90c7a17c09..55051fb779 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -509,10 +509,4 @@ void __qmljs_llvm_delete_value(Context *ctx, Value *result, Value *value) *result = __qmljs_delete_value(ctx, *value); } -void __qmljs_llvm_init_this_object(Context *ctx) -{ - if (ctx->calledAsConstructor) - ctx->thisObject = __qmljs_new_object(ctx); -} - } // extern "C" diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 668be71532..47e5dffa29 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -243,7 +243,6 @@ void Context::init(ExecutionEngine *eng) formalCount = 0; vars = 0; varCount = 0; - calledAsConstructor = false; } PropertyDescriptor *Context::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp) @@ -330,7 +329,6 @@ void Context::initCallContext(Context *parent, const Value *object, FunctionObje vars = f->varList; varCount = f->varCount; locals = varCount ? new Value[varCount] : 0; - calledAsConstructor = false; if (varCount) std::fill(locals, locals + varCount, Value::undefinedValue()); } @@ -346,7 +344,6 @@ void Context::leaveCallContext() void Context::initConstructorContext(Context *parent, Value *object, FunctionObject *f, Value *args, unsigned argc) { initCallContext(parent, object, f, args, argc); - calledAsConstructor = true; } void Context::leaveConstructorContext(FunctionObject *f) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index ff1f4a3f17..5c6d3c4939 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -583,7 +583,6 @@ struct Context { unsigned int formalCount; String **vars; unsigned int varCount; - int calledAsConstructor; PropertyDescriptor *lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp); void inplaceBitOp(Value value, String *name, BinOp op); -- cgit v1.2.3 From 4810e095b7940de875eb6d9d8a907e632ede561d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 15 Nov 2012 12:23:39 +0100 Subject: Store the activation object as a pointer, not as a Value. The activation object inside the context is always an object, or null. This avoids some needless conversions to and from Value's. Change-Id: Ibbd88c83fa073a4ed3cf03742129357dd9567cec Reviewed-by: Simon Hausmann --- main.cpp | 6 +++--- qmljs_objects.cpp | 2 +- qmljs_runtime.cpp | 38 +++++++++++++++++++------------------- qmljs_runtime.h | 2 +- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/main.cpp b/main.cpp index 8b1b0ac53b..2b69a4ffb0 100644 --- a/main.cpp +++ b/main.cpp @@ -282,11 +282,11 @@ static int evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, return EXIT_FAILURE; } - if (! ctx->activation.isObject()) - ctx->activation = VM::Value::fromObject(new QQmlJS::VM::Object()); + if (!ctx->activation) + ctx->activation = new QQmlJS::VM::Object(); foreach (const QString *local, globalCode->locals) { - ctx->activation.objectValue()->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); + ctx->activation->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); } void * buf = __qmljs_create_exception_handler(ctx); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index ab9ad588fd..e1a4c947c2 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -599,7 +599,7 @@ ExecutionEngine::ExecutionEngine() // VM::Object *glo = newObject(/*rootContext*/); globalObject = Value::fromObject(glo); - rootContext->activation = Value::fromObject(glo); + rootContext->activation = glo; glo->__put__(rootContext, identifier(QStringLiteral("Object")), objectCtor); glo->__put__(rootContext, identifier(QStringLiteral("String")), stringCtor); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 47e5dffa29..411057bc51 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -236,7 +236,7 @@ void Context::init(ExecutionEngine *eng) arguments = 0; argumentCount = 0; locals = 0; - activation = Value::nullValue(); + activation = 0; thisObject = Value::nullValue(); result = Value::undefinedValue(); formals = 0; @@ -248,8 +248,8 @@ void Context::init(ExecutionEngine *eng) PropertyDescriptor *Context::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp) { for (Context *ctx = this; ctx; ctx = ctx->parent) { - if (Object *act = ctx->activation.asObject()) { - if (PropertyDescriptor *pd = act->__getPropertyDescriptor__(this, name, tmp)) + if (ctx->activation) { + if (PropertyDescriptor *pd = ctx->activation->__getPropertyDescriptor__(this, name, tmp)) return pd; } } @@ -259,8 +259,8 @@ PropertyDescriptor *Context::lookupPropertyDescriptor(String *name, PropertyDesc void Context::inplaceBitOp(Value value, String *name, BinOp op) { for (Context *ctx = this; ctx; ctx = ctx->parent) { - if (Object *act = ctx->activation.asObject()) { - if (act->inplaceBinOp(value, name, op, this)) + if (ctx->activation) { + if (ctx->activation->inplaceBinOp(value, name, op, this)) return; } } @@ -306,9 +306,9 @@ void Context::initCallContext(Context *parent, const Value *object, FunctionObje result = Value::undefinedValue(); if (f->needsActivation) - activation = Value::fromObject(engine->newActivationObject(this)); + activation = engine->newActivationObject(this); else - activation = Value::nullValue(); + activation = 0; if (object) thisObject = *object; @@ -335,7 +335,7 @@ void Context::initCallContext(Context *parent, const Value *object, FunctionObje void Context::leaveCallContext() { - if (!activation.isNull()) { + if (activation) { delete[] locals; locals = 0; } @@ -444,10 +444,10 @@ Value __qmljs_delete_member(Context *ctx, Value base, String *name) Value __qmljs_delete_property(Context *ctx, String *name) { - Value obj = ctx->activation; - if (! obj.isObject()) - obj = ctx->engine->globalObject; - return Value::fromBoolean(obj.objectValue()->__delete__(ctx, name, true)); + Object *obj = ctx->activation; + if (!obj) + obj = ctx->engine->globalObject.objectValue(); + return Value::fromBoolean(obj->__delete__(ctx, name, true)); } Value __qmljs_delete_value(Context *ctx, Value value) @@ -1020,21 +1020,21 @@ Value __qmljs_call_activation_property(Context *context, String *name, Value *ar Value __qmljs_call_property(Context *context, Value base, String *name, Value *args, int argc) { - Value baseObject; + Object *baseObject; Value thisObject; if (base.isUndefined()) { baseObject = context->activation; thisObject = Value::nullValue(); } else { - baseObject = base; - if (!baseObject.isObject()) - baseObject = __qmljs_to_object(baseObject, context); - assert(baseObject.isObject()); - thisObject = baseObject; + if (!base.isObject()) + base = __qmljs_to_object(base, context); + assert(base.isObject()); + baseObject = base.objectValue(); + thisObject = base; } - Value func = baseObject.property(context, name); + Value func = baseObject ? baseObject->__get__(context, name) : Value::undefinedValue(); return callFunction(context, thisObject, func.asFunctionObject(), args, argc); } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 5c6d3c4939..51eec1a403 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -573,7 +573,7 @@ inline bool Value::sameValue(Value other) { struct Context { ExecutionEngine *engine; Context *parent; - Value activation; + Object *activation; Value thisObject; Value *arguments; unsigned int argumentCount; -- cgit v1.2.3 From 92b5cedeef42f57c7e526e9bb5592692960bce18 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 15 Nov 2012 12:58:49 +0100 Subject: Smaller cleanups in the Context class Change-Id: I02bc1cad6231988fdf453d0b4fdde55dd1ae9148 Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 51eec1a403..f550b3603c 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -588,36 +588,25 @@ struct Context { void inplaceBitOp(Value value, String *name, BinOp op); inline Value argument(unsigned int index = 0) - { - Value arg; - getArgument(&arg, index); - return arg; - } - - inline void getArgument(Value *result, unsigned int index) { if (index < argumentCount) - *result = arguments[index]; - else - *result = Value::undefinedValue(); + return arguments[index]; + return Value::undefinedValue(); } void init(ExecutionEngine *eng); - void throwError(Value value); - void throwTypeError(); - void throwReferenceError(Value value); - -#ifndef QMLJS_LLVM_RUNTIME - void throwError(const QString &message); - void throwUnimplemented(const QString &message); -#endif - void initCallContext(Context *parent, const Value *object, FunctionObject *f, Value *args, unsigned argc); void leaveCallContext(); void initConstructorContext(Context *parent, Value *object, FunctionObject *f, Value *args, unsigned argc); void leaveConstructorContext(FunctionObject *f); + + void throwError(Value value); + void throwError(const QString &message); + void throwTypeError(); + void throwReferenceError(Value value); + void throwUnimplemented(const QString &message); }; -- cgit v1.2.3 From bd63199bba91769487d24f3a8635f20105eeff06 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 15 Nov 2012 13:31:57 +0100 Subject: Cleanup Context initialization Make the initialization for call() compliant with the standard, and add a strict mode boolean. Change-Id: I8617af8dbfde47d8b2a8a0a7ce0ab491031ba4ba Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 14 ++++++++++---- qmljs_objects.h | 2 +- qmljs_runtime.cpp | 11 ++++------- qmljs_runtime.h | 4 ++-- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index e1a4c947c2..62ecc42f41 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -379,18 +379,24 @@ Value FunctionObject::construct(Context *context, Value *args, int argc) { Context k; Context *ctx = needsActivation ? context->engine->newContext() : &k; - ctx->initConstructorContext(context, 0, this, args, argc); + ctx->initConstructorContext(context, Value::nullValue(), this, args, argc); construct(ctx); ctx->leaveConstructorContext(this); return ctx->result; } -Value FunctionObject::call(Context *context, Value thisObject, Value *args, int argc) +Value FunctionObject::call(Context *context, Value thisObject, Value *args, int argc, bool strictMode) { Context k; Context *ctx = needsActivation ? context->engine->newContext() : &k; - const Value *that = thisObject.isUndefined() ? 0 : &thisObject; - ctx->initCallContext(context, that, this, args, argc); + + if (!strictMode && !thisObject.isObject()) { + if (thisObject.isUndefined() || thisObject.isNull()) + thisObject = context->engine->globalObject; + else + thisObject = __qmljs_to_object(thisObject, context); + } + ctx->initCallContext(context, thisObject, this, args, argc); call(ctx); ctx->leaveCallContext(); return ctx->result; diff --git a/qmljs_objects.h b/qmljs_objects.h index 844538526e..0ccf7a6f4b 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -488,7 +488,7 @@ struct FunctionObject: Object { virtual bool hasInstance(Context *ctx, const Value &value); Value construct(Context *context, Value *args, int argc); - Value call(Context *context, Value thisObject, Value *args, int argc); + Value call(Context *context, Value thisObject, Value *args, int argc, bool strictMode = false); protected: virtual void call(Context *ctx); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 411057bc51..f10bb9a318 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -298,7 +298,7 @@ void Context::throwReferenceError(Value value) throwError(Value::fromObject(engine->newErrorObject(Value::fromString(this, msg)))); } -void Context::initCallContext(Context *parent, const Value *object, FunctionObject *f, Value *args, unsigned argc) +void Context::initCallContext(Context *parent, const Value that, FunctionObject *f, Value *args, unsigned argc) { engine = parent->engine; this->parent = f->scope; @@ -310,10 +310,7 @@ void Context::initCallContext(Context *parent, const Value *object, FunctionObje else activation = 0; - if (object) - thisObject = *object; - else - thisObject = Value::nullValue(); + thisObject = that; formals = f->formalParameterList; formalCount = f->formalParameterCount; @@ -341,9 +338,9 @@ void Context::leaveCallContext() } } -void Context::initConstructorContext(Context *parent, Value *object, FunctionObject *f, Value *args, unsigned argc) +void Context::initConstructorContext(Context *parent, Value that, FunctionObject *f, Value *args, unsigned argc) { - initCallContext(parent, object, f, args, argc); + initCallContext(parent, that, f, args, argc); } void Context::leaveConstructorContext(FunctionObject *f) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index f550b3603c..09ee5224ae 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -596,10 +596,10 @@ struct Context { void init(ExecutionEngine *eng); - void initCallContext(Context *parent, const Value *object, FunctionObject *f, Value *args, unsigned argc); + void initCallContext(Context *parent, const Value that, FunctionObject *f, Value *args, unsigned argc); void leaveCallContext(); - void initConstructorContext(Context *parent, Value *object, FunctionObject *f, Value *args, unsigned argc); + void initConstructorContext(Context *parent, Value that, FunctionObject *f, Value *args, unsigned argc); void leaveConstructorContext(FunctionObject *f); void throwError(Value value); -- cgit v1.2.3 From 72b478b8a2a27047df7fab09142c01a9b7a7ca6e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 14 Nov 2012 16:44:08 +0100 Subject: Added all missing error objects. Change-Id: I806184c5593af44d79b21afb1e3235ec9afa1e2a Reviewed-by: Lars Knoll --- qmljs_objects.cpp | 43 +++++++++++++++++++++++++++- qmljs_objects.h | 51 +++++++++++++++++++++++++++++++++ qv4ecmaobjects.cpp | 38 ++++++++++++++++++++++--- qv4ecmaobjects_p.h | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 209 insertions(+), 6 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 62ecc42f41..574e1bd897 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -472,6 +472,11 @@ Value ErrorObject::__get__(Context *ctx, String *name) return Object::__get__(ctx, name); } +void ErrorObject::setNameProperty(Context *ctx) +{ + __put__(ctx, QLatin1String("name"), Value::fromString(ctx, className())); +} + void ScriptFunction::construct(VM::Context *ctx) { Object *obj = ctx->engine->newObject(); @@ -561,6 +566,12 @@ ExecutionEngine::ExecutionEngine() functionPrototype = new FunctionPrototype(rootContext); regExpPrototype = new RegExpPrototype(); errorPrototype = new ErrorPrototype(); + evalErrorPrototype = new EvalErrorPrototype(rootContext); + rangeErrorPrototype = new RangeErrorPrototype(rootContext); + referenceErrorPrototype = new ReferenceErrorPrototype(rootContext); + syntaxErrorPrototype = new SyntaxErrorPrototype(rootContext); + typeErrorPrototype = new TypeErrorPrototype(rootContext); + uRIErrorPrototype = new URIErrorPrototype(rootContext); stringPrototype->prototype = objectPrototype; numberPrototype->prototype = objectPrototype; @@ -570,6 +581,12 @@ ExecutionEngine::ExecutionEngine() functionPrototype->prototype = objectPrototype; regExpPrototype->prototype = objectPrototype; errorPrototype->prototype = objectPrototype; + evalErrorPrototype->prototype = errorPrototype; + rangeErrorPrototype->prototype = errorPrototype; + referenceErrorPrototype->prototype = errorPrototype; + syntaxErrorPrototype->prototype = errorPrototype; + typeErrorPrototype->prototype = errorPrototype; + uRIErrorPrototype->prototype = errorPrototype; objectCtor = Value::fromObject(new ObjectCtor(rootContext)); stringCtor = Value::fromObject(new StringCtor(rootContext)); @@ -580,6 +597,12 @@ ExecutionEngine::ExecutionEngine() dateCtor = Value::fromObject(new DateCtor(rootContext)); regExpCtor = Value::fromObject(new RegExpCtor(rootContext)); errorCtor = Value::fromObject(new ErrorCtor(rootContext)); + evalErrorCtor = Value::fromObject(new EvalErrorCtor(rootContext)); + rangeErrorCtor = Value::fromObject(new RangeErrorCtor(rootContext)); + referenceErrorCtor = Value::fromObject(new ReferenceErrorCtor(rootContext)); + syntaxErrorCtor = Value::fromObject(new SyntaxErrorCtor(rootContext)); + typeErrorCtor = Value::fromObject(new TypeErrorCtor(rootContext)); + uRIErrorCtor = Value::fromObject(new URIErrorCtor(rootContext)); stringCtor.objectValue()->prototype = functionPrototype; numberCtor.objectValue()->prototype = functionPrototype; @@ -589,6 +612,12 @@ ExecutionEngine::ExecutionEngine() dateCtor.objectValue()->prototype = functionPrototype; regExpCtor.objectValue()->prototype = functionPrototype; errorCtor.objectValue()->prototype = functionPrototype; + evalErrorCtor.objectValue()->prototype = errorPrototype; + rangeErrorCtor.objectValue()->prototype = errorPrototype; + referenceErrorCtor.objectValue()->prototype = errorPrototype; + syntaxErrorCtor.objectValue()->prototype = errorPrototype; + typeErrorCtor.objectValue()->prototype = errorPrototype; + uRIErrorCtor.objectValue()->prototype = errorPrototype; objectPrototype->init(rootContext, objectCtor); stringPrototype->init(rootContext, stringCtor); @@ -599,6 +628,12 @@ ExecutionEngine::ExecutionEngine() functionPrototype->init(rootContext, functionCtor); regExpPrototype->init(rootContext, regExpCtor); errorPrototype->init(rootContext, errorCtor); + evalErrorPrototype->init(rootContext, evalErrorCtor); + rangeErrorPrototype->init(rootContext, rangeErrorCtor); + referenceErrorPrototype->init(rootContext, referenceErrorCtor); + syntaxErrorPrototype->init(rootContext, syntaxErrorCtor); + typeErrorPrototype->init(rootContext, typeErrorCtor); + uRIErrorPrototype->init(rootContext, uRIErrorCtor); // // set up the global object @@ -616,6 +651,12 @@ ExecutionEngine::ExecutionEngine() glo->__put__(rootContext, identifier(QStringLiteral("Date")), dateCtor); glo->__put__(rootContext, identifier(QStringLiteral("RegExp")), regExpCtor); glo->__put__(rootContext, identifier(QStringLiteral("Error")), errorCtor); + glo->__put__(rootContext, identifier(QStringLiteral("EvalError")), evalErrorCtor); + glo->__put__(rootContext, identifier(QStringLiteral("RangeError")), rangeErrorCtor); + glo->__put__(rootContext, identifier(QStringLiteral("ReferenceError")), referenceErrorCtor); + glo->__put__(rootContext, identifier(QStringLiteral("SyntaxError")), syntaxErrorCtor); + glo->__put__(rootContext, identifier(QStringLiteral("TypeError")), typeErrorCtor); + glo->__put__(rootContext, identifier(QStringLiteral("URIError")), uRIErrorCtor); glo->__put__(rootContext, identifier(QStringLiteral("Math")), Value::fromObject(newMathObject(rootContext))); glo->__put__(rootContext, identifier(QStringLiteral("undefined")), Value::undefinedValue()); glo->__put__(rootContext, identifier(QStringLiteral("NaN")), Value::fromDouble(nan(""))); @@ -770,7 +811,7 @@ FunctionObject *ExecutionEngine::newRegExpCtor(Context *ctx) Object *ExecutionEngine::newErrorObject(const Value &value) { ErrorObject *object = new ErrorObject(value); - object->prototype = objectPrototype; + object->prototype = errorPrototype; return object; } diff --git a/qmljs_objects.h b/qmljs_objects.h index 0ccf7a6f4b..49fed3e6e1 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -83,6 +83,12 @@ struct FunctionPrototype; struct DatePrototype; struct RegExpPrototype; struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; struct String { String(const QString &text) @@ -529,6 +535,39 @@ struct ErrorObject: Object { virtual QString className() { return QStringLiteral("Error"); } virtual ErrorObject *asErrorObject() { return this; } virtual Value __get__(Context *ctx, String *name); + +protected: + void setNameProperty(Context *ctx); +}; + +struct EvalErrorObject: ErrorObject { + EvalErrorObject(Context *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + virtual QString className() { return QStringLiteral("EvalError"); } +}; + +struct RangeErrorObject: ErrorObject { + RangeErrorObject(Context *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + virtual QString className() { return QStringLiteral("RangeError"); } +}; + +struct ReferenceErrorObject: ErrorObject { + ReferenceErrorObject(Context *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + virtual QString className() { return QStringLiteral("ReferenceError"); } +}; + +struct SyntaxErrorObject: ErrorObject { + SyntaxErrorObject(Context *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + virtual QString className() { return QStringLiteral("SyntaxError"); } +}; + +struct TypeErrorObject: ErrorObject { + TypeErrorObject(Context *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + virtual QString className() { return QStringLiteral("TypeError"); } +}; + +struct URIErrorObject: ErrorObject { + URIErrorObject(Context *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + virtual QString className() { return QStringLiteral("URIError"); } }; struct ActivationObject: Object { @@ -563,6 +602,12 @@ struct ExecutionEngine Value dateCtor; Value regExpCtor; Value errorCtor; + Value evalErrorCtor; + Value rangeErrorCtor; + Value referenceErrorCtor; + Value syntaxErrorCtor; + Value typeErrorCtor; + Value uRIErrorCtor; ObjectPrototype *objectPrototype; StringPrototype *stringPrototype; @@ -573,6 +618,12 @@ struct ExecutionEngine DatePrototype *datePrototype; RegExpPrototype *regExpPrototype; ErrorPrototype *errorPrototype; + EvalErrorPrototype *evalErrorPrototype; + RangeErrorPrototype *rangeErrorPrototype; + ReferenceErrorPrototype *referenceErrorPrototype; + SyntaxErrorPrototype *syntaxErrorPrototype; + TypeErrorPrototype *typeErrorPrototype; + URIErrorPrototype *uRIErrorPrototype; QHash identifiers; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 5d2a6edf38..d56e338c9a 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2603,11 +2603,41 @@ void ErrorCtor::call(Context *ctx) ctx->thisObject = that; } -void ErrorPrototype::init(Context *ctx, const Value &ctor) +void EvalErrorCtor::construct(Context *ctx) { - ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - __put__(ctx, QStringLiteral("constructor"), ctor); - __put__(ctx, QStringLiteral("toString"), method_toString, 0); + ctx->thisObject = Value::fromObject(new EvalErrorObject(ctx)); +} + +void RangeErrorCtor::construct(Context *ctx) +{ + ctx->thisObject = Value::fromObject(new RangeErrorObject(ctx)); +} + +void ReferenceErrorCtor::construct(Context *ctx) +{ + ctx->thisObject = Value::fromObject(new ReferenceErrorObject(ctx)); +} + +void SyntaxErrorCtor::construct(Context *ctx) +{ + ctx->thisObject = Value::fromObject(new SyntaxErrorObject(ctx)); +} + +void TypeErrorCtor::construct(Context *ctx) +{ + ctx->thisObject = Value::fromObject(new TypeErrorObject(ctx)); +} + +void URIErrorCtor::construct(Context *ctx) +{ + ctx->thisObject = Value::fromObject(new URIErrorObject(ctx)); +} + +void ErrorPrototype::init(Context *ctx, const Value &ctor, Object *obj) +{ + ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(obj)); + obj->__put__(ctx, QStringLiteral("constructor"), ctor); + obj->__put__(ctx, QStringLiteral("toString"), method_toString, 0); } void ErrorPrototype::method_toString(Context *ctx) diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 3f78feaa64..031a953562 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -302,15 +302,96 @@ struct ErrorCtor: FunctionObject virtual void call(Context *ctx); }; +struct EvalErrorCtor: ErrorCtor +{ + EvalErrorCtor(Context *scope): ErrorCtor(scope) {} + + virtual void construct(Context *ctx); +}; + +struct RangeErrorCtor: ErrorCtor +{ + RangeErrorCtor(Context *scope): ErrorCtor(scope) {} + + virtual void construct(Context *ctx); +}; + +struct ReferenceErrorCtor: ErrorCtor +{ + ReferenceErrorCtor(Context *scope): ErrorCtor(scope) {} + + virtual void construct(Context *ctx); +}; + +struct SyntaxErrorCtor: ErrorCtor +{ + SyntaxErrorCtor(Context *scope): ErrorCtor(scope) {} + + virtual void construct(Context *ctx); +}; + +struct TypeErrorCtor: ErrorCtor +{ + TypeErrorCtor(Context *scope): ErrorCtor(scope) {} + + virtual void construct(Context *ctx); +}; + +struct URIErrorCtor: ErrorCtor +{ + URIErrorCtor(Context *scope): ErrorCtor(scope) {} + + virtual void construct(Context *ctx); +}; + + struct ErrorPrototype: ErrorObject { // ### shouldn't be undefined ErrorPrototype(): ErrorObject(Value::undefinedValue()) {} - void init(Context *ctx, const Value &ctor); + void init(Context *ctx, const Value &ctor) { init(ctx, ctor, this); } + static void init(Context *ctx, const Value &ctor, Object *obj); static void method_toString(Context *ctx); }; +struct EvalErrorPrototype: EvalErrorObject +{ + EvalErrorPrototype(Context *ctx): EvalErrorObject(ctx) {} + void init(Context *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct RangeErrorPrototype: RangeErrorObject +{ + RangeErrorPrototype(Context *ctx): RangeErrorObject(ctx) {} + void init(Context *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct ReferenceErrorPrototype: ReferenceErrorObject +{ + ReferenceErrorPrototype(Context *ctx): ReferenceErrorObject(ctx) {} + void init(Context *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct SyntaxErrorPrototype: SyntaxErrorObject +{ + SyntaxErrorPrototype(Context *ctx): SyntaxErrorObject(ctx) {} + void init(Context *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct TypeErrorPrototype: TypeErrorObject +{ + TypeErrorPrototype(Context *ctx): TypeErrorObject(ctx) {} + void init(Context *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct URIErrorPrototype: URIErrorObject +{ + URIErrorPrototype(Context *ctx): URIErrorObject(ctx) {} + void init(Context *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + + struct MathObject: Object { MathObject(Context *ctx); -- cgit v1.2.3 From 2b789d4fa081f466842a03afdc221931bbb66e9c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 15 Nov 2012 13:33:36 +0100 Subject: Tune break/continue exception trampoline block generation. If the break/continue trampoline blocks get reached, there cannot be a pending exception, so don't bother with saving it. Change-Id: Id079d48d961f21dd236dbc33fad1712edbd2df97 Reviewed-by: Lars Knoll --- qv4codegen.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 9c5ce2c340..e14df9531b 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -2053,8 +2053,10 @@ bool Codegen::visit(TryStatement *ast) if (loop) { // now do the codegen for each break/continue block for (Loop *it = loop, *it2 = _loop; it && it2; it = it->parent, it2 = it2->parent) { - generateFinallyBlock(it->breakBlock, !catchBody, ast->finallyExpression->statement, hasException, it2->breakBlock); - generateFinallyBlock(it->continueBlock, !catchBody, ast->finallyExpression->statement, hasException, it2->continueBlock); + // if the break/continue trampoline blocks get reached, there cannot + // be a pending exception, so don't bother with saving it. + generateFinallyBlock(it->breakBlock, false, ast->finallyExpression->statement, hasException, it2->breakBlock); + generateFinallyBlock(it->continueBlock, false, ast->finallyExpression->statement, hasException, it2->continueBlock); } // we don't need the Loop struct itself anymore. @@ -2089,6 +2091,9 @@ void Codegen::generateFinallyBlock(IR::BasicBlock *finallyBlock, bool exceptionN _block = realFinallyBlock; } + // TODO: this will not re-throw when there is a pending exception, and + // the finally block contains a break/continue. Should another set of + // break/continue trampoline blocks get inserted? _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); if (ast) statement(ast); @@ -2133,7 +2138,7 @@ bool Codegen::visit(WhileStatement *ast) bool Codegen::visit(WithStatement *) { - assert(!"not implemented"); + assert(!"with not implemented"); return false; } -- cgit v1.2.3 From 424532fd92174ef7aa5a3a94f983265d03d49a96 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 14 Nov 2012 15:16:17 +0100 Subject: Fix for calling Error constructor as a function (15.11.1). Change-Id: I989c2a546b7e4d9a4fd2f7ff8b682c747d38332f Reviewed-by: Lars Knoll --- qmljs_runtime.cpp | 7 ++++++- qmljs_runtime.h | 1 + qv4ecmaobjects.cpp | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index f10bb9a318..cb68331765 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -344,6 +344,12 @@ void Context::initConstructorContext(Context *parent, Value that, FunctionObject } void Context::leaveConstructorContext(FunctionObject *f) +{ + wireUpPrototype(f); + leaveCallContext(); +} + +void Context::wireUpPrototype(FunctionObject *f) { assert(thisObject.isObject()); result = thisObject; @@ -353,7 +359,6 @@ void Context::leaveConstructorContext(FunctionObject *f) if (! thisObject.isObject()) thisObject.objectValue()->prototype = engine->objectPrototype; - leaveCallContext(); } extern "C" { diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 09ee5224ae..072367cba2 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -601,6 +601,7 @@ struct Context { void initConstructorContext(Context *parent, Value that, FunctionObject *f, Value *args, unsigned argc); void leaveConstructorContext(FunctionObject *f); + void wireUpPrototype(FunctionObject *f); void throwError(Value value); void throwError(const QString &message); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index d56e338c9a..c4d75fe2c9 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2599,7 +2599,8 @@ void ErrorCtor::call(Context *ctx) { Value that = ctx->thisObject; construct(ctx); - ctx->result = ctx->thisObject; + ctx->wireUpPrototype(this); + ctx->thisObject = that; } -- cgit v1.2.3 From ef25fdacc89c0cbcba887bff44c4de2f848243ca Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 15 Nov 2012 16:29:47 +0100 Subject: Do not save/restore interpreter stack for exceptions. Only the instruction pointer and targetTempIndex are needed. Change-Id: I21279d68e74dac42d875e3ced9004e9d3c42c29d Reviewed-by: Lars Knoll --- moth/qv4vme_moth.cpp | 28 ++++++++++------------------ moth/qv4vme_moth_p.h | 4 ++-- qmljs_objects.h | 15 +++------------ qmljs_runtime.cpp | 17 ++++++++--------- 4 files changed, 23 insertions(+), 41 deletions(-) diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 6fae010985..07860c24a0 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -215,11 +215,11 @@ void VME::operator()(QQmlJS::VM::Context *context, const uchar *code // At this point, the interpreter state can be anything but // valid, so first restore the state. This includes all relevant // locals. - restoreState(context, stack, targetTempIndex, code); + restoreState(context, targetTempIndex, code); else // Save the state and any variables we need when catching an // exception, so we can restore the state at that point. - saveState(context, stack, targetTempIndex, code); + saveState(context, targetTempIndex, code); TEMP(targetTempIndex) = VM::Value::fromInt32(didThrow); } break; case Instr::instr_callBuiltin::builtin_delete_exception_handler: @@ -351,24 +351,16 @@ void VME::exec(VM::Context *ctxt, const uchar *code) vme(ctxt, code); } -void VME::restoreState(VM::Context *context, QVector &stack, int &targetTempIndex, const uchar *&code) +void VME::restoreState(VM::Context *context, int &targetTempIndex, const uchar *&code) { - typedef VM::ExecutionEngine::ExceptionHandler EH; - EH::InterpreterState *state = context->engine->unwindStack.last()->interpreterState; - assert(state); - stack.resize(state->stack.size()); - ::memcpy(stack.data(), state->stack.data(), sizeof(VM::Value) * state->stack.size()); - targetTempIndex = state->targetTempIndex; - code = state->code; + VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); + targetTempIndex = handler.targetTempIndex; + code = handler.code; } -void VME::saveState(VM::Context *context, const QVector &stack, int targetTempIndex, const uchar *code) +void VME::saveState(VM::Context *context, int targetTempIndex, const uchar *code) { - typedef VM::ExecutionEngine::ExceptionHandler EH; - EH::InterpreterState *state = new EH::InterpreterState; - context->engine->unwindStack.last()->interpreterState = state; - state->stack.resize(stack.size()); - ::memcpy(state->stack.data(), stack.data(), sizeof(VM::Value) * stack.size()); - state->targetTempIndex = targetTempIndex; - state->code = code; + VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); + handler.targetTempIndex = targetTempIndex; + handler.code = code; } diff --git a/moth/qv4vme_moth_p.h b/moth/qv4vme_moth_p.h index b1bc45bab5..e3a2347c34 100644 --- a/moth/qv4vme_moth_p.h +++ b/moth/qv4vme_moth_p.h @@ -23,8 +23,8 @@ public: #endif private: - static void restoreState(VM::Context *context, QVector &stack, int &targetTempIndex, const uchar *&code); - static void saveState(VM::Context *context, const QVector &stack, int targetTempIndex, const uchar *code); + static void restoreState(VM::Context *context, int &targetTempIndex, const uchar *&code); + static void saveState(VM::Context *context, int targetTempIndex, const uchar *code); }; } // namespace Moth diff --git a/qmljs_objects.h b/qmljs_objects.h index 49fed3e6e1..490eae2eef 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -634,24 +634,15 @@ struct ExecutionEngine String *id___proto__; struct ExceptionHandler { - struct InterpreterState { - QVector stack; - int targetTempIndex; - const uchar *code; - }; - Context *context; + const uchar *code; // Interpreter state + int targetTempIndex; // Interpreter state jmp_buf stackFrame; - InterpreterState *interpreterState; - - ExceptionHandler(): interpreterState(0) {} - ~ExceptionHandler() { delete interpreterState; } }; - QVector unwindStack; + QVector unwindStack; ExecutionEngine(); - ~ExecutionEngine() { qDeleteAll(unwindStack); } Context *newContext(); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index cb68331765..9428118a0d 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -1083,32 +1083,31 @@ void __qmljs_throw(Value value, Context *context) { assert(!context->engine->unwindStack.isEmpty()); - ExecutionEngine::ExceptionHandler *handler = context->engine->unwindStack.last(); + ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); // clean up call contexts - while (context != handler->context) { + while (context != handler.context) { context->leaveCallContext(); context = context->parent; } - handler->context->result = value; + handler.context->result = value; - longjmp(handler->stackFrame, 1); + longjmp(handler.stackFrame, 1); } void *__qmljs_create_exception_handler(Context *context) { - ExecutionEngine::ExceptionHandler *handler = new ExecutionEngine::ExceptionHandler; - context->engine->unwindStack.append(handler); - handler->context = context; - return handler->stackFrame; + context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler()); + ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); + handler.context = context; + return handler.stackFrame; } void __qmljs_delete_exception_handler(Context *context) { assert(!context->engine->unwindStack.isEmpty()); - delete context->engine->unwindStack.last(); context->engine->unwindStack.pop_back(); } -- cgit v1.2.3 From cedaab81e3d38d6777f81d99b7cab2b607065d44 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 15 Nov 2012 16:31:03 +0100 Subject: Fix possible null-pointer deref. Change-Id: I8c4e2c34b2a3803d1346a1f716a0e40bde6bfdff Reviewed-by: Lars Knoll --- qv4codegen.cpp | 19 ++++++++++--------- qv4codegen_p.h | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index e14df9531b..ac5b660e34 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -2008,7 +2008,7 @@ bool Codegen::visit(TryStatement *ast) if (finallyBody) { if (inCatch != 0) { - // an exception got thrown within catch. Go to finally + // check if an exception got thrown within catch. Go to finally // and then rethrow IR::BasicBlock *b = _function->newBasicBlock(); _block->CJUMP(_block->TEMP(inCatch), finallyBody, b); @@ -2047,7 +2047,7 @@ bool Codegen::visit(TryStatement *ast) // original break/continue blocks. std::swap(_loop, loop); - generateFinallyBlock(finallyBody, !catchBody, ast->finallyExpression->statement, hasException, after); + generateFinallyBlock(finallyBody, !catchBody, ast->finallyExpression, hasException, after); } if (loop) { @@ -2055,8 +2055,8 @@ bool Codegen::visit(TryStatement *ast) for (Loop *it = loop, *it2 = _loop; it && it2; it = it->parent, it2 = it2->parent) { // if the break/continue trampoline blocks get reached, there cannot // be a pending exception, so don't bother with saving it. - generateFinallyBlock(it->breakBlock, false, ast->finallyExpression->statement, hasException, it2->breakBlock); - generateFinallyBlock(it->continueBlock, false, ast->finallyExpression->statement, hasException, it2->continueBlock); + generateFinallyBlock(it->breakBlock, false, ast->finallyExpression, hasException, it2->breakBlock); + generateFinallyBlock(it->continueBlock, false, ast->finallyExpression, hasException, it2->continueBlock); } // we don't need the Loop struct itself anymore. @@ -2072,7 +2072,7 @@ bool Codegen::visit(TryStatement *ast) return false; } -void Codegen::generateFinallyBlock(IR::BasicBlock *finallyBlock, bool exceptionNeedsSaving, Block *ast, int hasException, IR::BasicBlock *after) +void Codegen::generateFinallyBlock(IR::BasicBlock *finallyBlock, bool exceptionNeedsSaving, Finally *ast, int hasException, IR::BasicBlock *after) { _block = finallyBlock; @@ -2095,14 +2095,15 @@ void Codegen::generateFinallyBlock(IR::BasicBlock *finallyBlock, bool exceptionN // the finally block contains a break/continue. Should another set of // break/continue trampoline blocks get inserted? _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); - if (ast) - statement(ast); + if (ast && ast->statement) + statement(ast->statement); if (exceptionNeedsSaving) { IR::BasicBlock *restoreExceptionBlock = _function->newBasicBlock(); _block->CJUMP(_block->TEMP(hasException), restoreExceptionBlock, after); - restoreExceptionBlock->MOVE(restoreExceptionBlock->TEMP(_returnAddress), restoreExceptionBlock->TEMP(exception)); - restoreExceptionBlock->JUMP(_throwBlock); + _block = restoreExceptionBlock; + _block->MOVE(_block->TEMP(_returnAddress), _block->TEMP(exception)); + _block->JUMP(_throwBlock); } else { _block->CJUMP(_block->TEMP(hasException), _throwBlock, after); } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index fa84beabe4..c830345331 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -188,7 +188,7 @@ protected: AST::SourceElements *body, Mode mode = FunctionCode); int indexOfArgument(const QStringRef &string) const; - void generateFinallyBlock(IR::BasicBlock *finallyBlock, bool exceptionNeedsSaving, AST::Block *ast, int hasException, IR::BasicBlock *after); + void generateFinallyBlock(IR::BasicBlock *finallyBlock, bool exceptionNeedsSaving, AST::Finally *ast, int hasException, IR::BasicBlock *after); void statement(AST::Statement *ast); void statement(AST::ExpressionNode *ast); -- cgit v1.2.3 From 84f722c4963346cebe471eb44e762cf085ff8177 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 15 Nov 2012 16:56:52 +0100 Subject: Fix __qmljs_string_to_number. Stop using QString::toDouble, because it doesn't handle Infinity, -Infinity, 10e10000, etc. Change-Id: Ic51bc1d7fe4d2d233b551b87d48833f3ba76a6dc Reviewed-by: Lars Knoll --- qmljs_runtime.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 9428118a0d..d115877e9c 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -39,7 +39,6 @@ ** ****************************************************************************/ - #include "qmljs_runtime.h" #include "qmljs_objects.h" #include "qv4ir_p.h" @@ -51,6 +50,8 @@ #include #include #include +#include +#include namespace QQmlJS { namespace VM { @@ -703,8 +704,10 @@ double __qmljs_string_to_number(Context *, String *string) const QString s = string->toQString(); if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) return s.toLong(0, 16); - bool ok = false; - return string->toQString().toDouble(&ok); // ### TODO + locale_t c_locale = newlocale(LC_ALL_MASK, NULL, NULL); + double d = ::strtod_l(s.toUtf8().constData(), 0, c_locale); + freelocale(c_locale); + return d; } Value __qmljs_string_from_number(Context *ctx, double number) -- cgit v1.2.3 From d6553a206bf3c263159764ab222cb7f43ed1da10 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 16 Nov 2012 22:21:34 +0100 Subject: Create a proper function object for eval Still doesn't work correctly, as we can't modify the global context there. Change-Id: Ifd0ab217c3cf2d0c1b86f09907b440ea31c29ac8 Reviewed-by: Simon Hausmann --- main.cpp | 118 +------------------------------------------- qmljs_objects.cpp | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ qmljs_objects.h | 14 +++++- 3 files changed, 158 insertions(+), 118 deletions(-) diff --git a/main.cpp b/main.cpp index 2b69a4ffb0..12f0bb2a87 100644 --- a/main.cpp +++ b/main.cpp @@ -61,19 +61,6 @@ #include #include -static inline bool protect(const void *addr, size_t size) -{ - size_t pageSize = sysconf(_SC_PAGESIZE); - size_t iaddr = reinterpret_cast(addr); - size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); - int mode = PROT_READ | PROT_WRITE | PROT_EXEC; - return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; -} - -static int evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, - const QString &source, bool useInterpreter, - QQmlJS::Codegen::Mode mode = QQmlJS::Codegen::GlobalCode); - namespace builtins { using namespace QQmlJS::VM; @@ -94,20 +81,6 @@ struct Print: FunctionObject } }; -struct Eval: FunctionObject -{ - Eval(Context *scope, bool useInterpreter): FunctionObject(scope), useInterpreter(useInterpreter) {} - - virtual void call(Context *ctx) - { - const QString code = ctx->argument(0).toString(ctx)->toQString(); - evaluate(ctx, QStringLiteral("eval code"), code, useInterpreter, QQmlJS::Codegen::EvalCode); - } - -private: - bool useInterpreter; -}; - struct TestHarnessError: FunctionObject { TestHarnessError(Context *scope, bool &errorInTestHarness): FunctionObject(scope), errorOccurred(errorInTestHarness) {} @@ -226,92 +199,6 @@ int evaluateCompiledCode(const QStringList &files) #endif -static int evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, - const QString &source, bool useInterpreter, - QQmlJS::Codegen::Mode mode) -{ - using namespace QQmlJS; - - VM::ExecutionEngine *vm = ctx->engine; - IR::Module module; - IR::Function *globalCode = 0; - - const size_t codeSize = 400 * getpagesize(); - uchar *code = 0; - if (posix_memalign((void**)&code, 16, codeSize)) - assert(!"memalign failed"); - assert(code); - assert(! (size_t(code) & 15)); - - { - QQmlJS::Engine ee, *engine = ⅇ - Lexer lexer(engine); - lexer.setCode(source, 1, false); - Parser parser(engine); - - const bool parsed = parser.parseProgram(); - - foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { - std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn - << ": error: " << qPrintable(m.message) << std::endl; - } - - if (parsed) { - using namespace AST; - Program *program = AST::cast(parser.rootNode()); - - Codegen cg; - globalCode = cg(program, &module, mode); - - if (useInterpreter) { - Moth::InstructionSelection isel(vm, &module, code); - foreach (IR::Function *function, module.functions) - isel(function); - } else { - foreach (IR::Function *function, module.functions) { - MASM::InstructionSelection isel(vm, &module, code); - isel(function); - } - - if (! protect(code, codeSize)) - Q_UNREACHABLE(); - } - } - - if (! globalCode) - return EXIT_FAILURE; - } - - if (!ctx->activation) - ctx->activation = new QQmlJS::VM::Object(); - - foreach (const QString *local, globalCode->locals) { - ctx->activation->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); - } - - void * buf = __qmljs_create_exception_handler(ctx); - if (setjmp(*(jmp_buf *)buf)) { - if (VM::ErrorObject *e = ctx->result.asErrorObject()) - std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; - else - std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; - return EXIT_FAILURE; - } - - if (useInterpreter) { - Moth::VME vme; - vme(ctx, code); - } else { - globalCode->code(ctx, globalCode->codeData); - } - - if (! ctx->result.isUndefined()) { - if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) - std::cout << "exit value: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; - } - - return EXIT_SUCCESS; -} int main(int argc, char *argv[]) { @@ -401,9 +288,6 @@ int main(int argc, char *argv[]) globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); - globalObject->__put__(ctx, vm.identifier(QStringLiteral("eval")), - QQmlJS::VM::Value::fromObject(new builtins::Eval(ctx, useInterpreter))); - bool errorInTestHarness = false; if (!qgetenv("IN_TEST_HARNESS").isEmpty()) globalObject->__put__(ctx, vm.identifier(QStringLiteral("$ERROR")), @@ -415,7 +299,7 @@ int main(int argc, char *argv[]) const QString code = QString::fromUtf8(file.readAll()); file.close(); - int exitCode = evaluate(vm.rootContext, fn, code, useInterpreter); + int exitCode = QQmlJS::VM::EvalFunction::evaluate(vm.rootContext, fn, code, useInterpreter, QQmlJS::Codegen::GlobalCode); if (exitCode != EXIT_SUCCESS) return exitCode; if (errorInTestHarness) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 574e1bd897..622ef621b1 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -43,10 +43,20 @@ #include "qmljs_objects.h" #include "qv4ir_p.h" #include "qv4ecmaobjects_p.h" + +#include +#include +#include +#include +#include +#include +#include + #include #include #include #include +#include using namespace QQmlJS::VM; @@ -448,6 +458,137 @@ void ScriptFunction::call(VM::Context *ctx) function->code(ctx, function->codeData); } + +Value EvalFunction::call(Context *context, Value thisObject, Value *args, int argc, bool strictMode) +{ + Value s = context->argument(0); + if (!s.isString()) { + context->result = s; + return s; + } + const QString code = context->argument(0).stringValue()->toQString(); + + // ### how to determine this correctly + bool directCall = true; + + Context k, *ctx; + if (!directCall) { + // ### + } else if (strictMode) { + ctx = &k; + ctx->initCallContext(context, context->thisObject, this, args, argc); + } else { + ctx = context; + } + // ##### inline and do this in the correct scope + evaluate(ctx, QStringLiteral("eval code"), code, /*useInterpreter*/ false, QQmlJS::Codegen::EvalCode); + + if (strictMode) + ctx->leaveCallContext(); +} + +static inline bool protect(const void *addr, size_t size) +{ + size_t pageSize = sysconf(_SC_PAGESIZE); + size_t iaddr = reinterpret_cast(addr); + size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); + int mode = PROT_READ | PROT_WRITE | PROT_EXEC; + return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; +} + + +int EvalFunction::evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, + const QString &source, bool useInterpreter, + QQmlJS::Codegen::Mode mode) +{ + using namespace QQmlJS; + + VM::ExecutionEngine *vm = ctx->engine; + IR::Module module; + IR::Function *globalCode = 0; + + const size_t codeSize = 400 * getpagesize(); + uchar *code = 0; + if (posix_memalign((void**)&code, 16, codeSize)) + assert(!"memalign failed"); + assert(code); + assert(! (size_t(code) & 15)); + + { + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(source, 1, false); + Parser parser(engine); + + const bool parsed = parser.parseProgram(); + + foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { + std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn + << ": error: " << qPrintable(m.message) << std::endl; + } + + if (parsed) { + using namespace AST; + Program *program = AST::cast(parser.rootNode()); + + Codegen cg; + globalCode = cg(program, &module, mode); + +// if (useInterpreter) { +// Moth::InstructionSelection isel(vm, &module, code); +// foreach (IR::Function *function, module.functions) +// isel(function); +// } else + { + foreach (IR::Function *function, module.functions) { + MASM::InstructionSelection isel(vm, &module, code); + isel(function); + } + + if (! protect(code, codeSize)) + Q_UNREACHABLE(); + } + } + + if (! globalCode) + return EXIT_FAILURE; + } + + if (!ctx->activation) + ctx->activation = new QQmlJS::VM::Object(); + + foreach (const QString *local, globalCode->locals) { + ctx->activation->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); + } + + if (mode == Codegen::GlobalCode) { + void * buf = __qmljs_create_exception_handler(ctx); + if (setjmp(*(jmp_buf *)buf)) { + if (VM::ErrorObject *e = ctx->result.asErrorObject()) + std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; + else + std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; + return EXIT_FAILURE; + } + } + +// if (useInterpreter) { +// Moth::VME vme; +// vme(ctx, code); +// } else + { + globalCode->code(ctx, globalCode->codeData); + } + + if (! ctx->result.isUndefined()) { + if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) + std::cout << "exit value: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; + } + + return EXIT_SUCCESS; +} + + Value RegExpObject::__get__(Context *ctx, String *name) { QString n = name->toQString(); @@ -661,6 +802,9 @@ ExecutionEngine::ExecutionEngine() glo->__put__(rootContext, identifier(QStringLiteral("undefined")), Value::undefinedValue()); glo->__put__(rootContext, identifier(QStringLiteral("NaN")), Value::fromDouble(nan(""))); glo->__put__(rootContext, identifier(QStringLiteral("Infinity")), Value::fromDouble(INFINITY)); + glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new EvalFunction(rootContext))); + + } Context *ExecutionEngine::newContext() diff --git a/qmljs_objects.h b/qmljs_objects.h index 490eae2eef..3e06440b68 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -43,6 +43,7 @@ #include "qmljs_runtime.h" #include "qv4array_p.h" +#include "qv4codegen_p.h" #include #include @@ -494,7 +495,7 @@ struct FunctionObject: Object { virtual bool hasInstance(Context *ctx, const Value &value); Value construct(Context *context, Value *args, int argc); - Value call(Context *context, Value thisObject, Value *args, int argc, bool strictMode = false); + virtual Value call(Context *context, Value thisObject, Value *args, int argc, bool strictMode = false); protected: virtual void call(Context *ctx); @@ -519,6 +520,17 @@ struct ScriptFunction: FunctionObject { virtual void construct(Context *ctx); }; +struct EvalFunction : FunctionObject +{ + EvalFunction(Context *scope): FunctionObject(scope) {} + + static int evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, + const QString &source, bool useInterpreter, + QQmlJS::Codegen::Mode mode); + + virtual Value call(Context *context, Value thisObject, Value *args, int argc, bool strictMode = false); +}; + struct RegExpObject: Object { QRegularExpression value; Value lastIndex; -- cgit v1.2.3 From d7416a80faa4e8b32824975b712f6756eda7b18f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 16 Nov 2012 23:07:10 +0100 Subject: Rename Context to ExecutionContext This is so it'll map to the name used in the ECMAScript spec once the other refactorings are in. Change-Id: I8dcc7ad43b457ce50e7123c57bc4c770bcda8d11 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 174 ++++++++++---------- main.cpp | 10 +- moth/qv4instr_moth_p.h | 10 +- moth/qv4isel_moth.cpp | 10 +- moth/qv4vme_moth.cpp | 10 +- moth/qv4vme_moth_p.h | 8 +- qmljs_objects.cpp | 102 ++++++------ qmljs_objects.h | 130 +++++++-------- qmljs_runtime.cpp | 222 ++++++++++++------------- qmljs_runtime.h | 412 +++++++++++++++++++++++----------------------- qv4array_p.h | 8 +- qv4ecmaobjects.cpp | 376 +++++++++++++++++++++--------------------- qv4ecmaobjects_p.h | 436 ++++++++++++++++++++++++------------------------- qv4ir_p.h | 4 +- qv4isel_masm.cpp | 22 +-- qv4isel_masm_p.h | 4 +- 16 files changed, 969 insertions(+), 969 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 55051fb779..aa5d70ccbb 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -47,12 +47,12 @@ using namespace QQmlJS::VM; extern "C" { -void __qmljs_llvm_return(Context *ctx, Value *result) +void __qmljs_llvm_return(ExecutionContext *ctx, Value *result) { ctx->result = *result; } -Value *__qmljs_llvm_get_argument(Context *ctx, int index) +Value *__qmljs_llvm_get_argument(ExecutionContext *ctx, int index) { return &ctx->arguments[index]; } @@ -77,404 +77,404 @@ void __qmljs_llvm_init_number(Value *result, double value) *result = Value::fromDouble(value); } -void __qmljs_llvm_init_string(Context *ctx, Value *result, const char *str) +void __qmljs_llvm_init_string(ExecutionContext *ctx, Value *result, const char *str) { *result = Value::fromString(__qmljs_string_from_utf8(ctx, str)); } -void __qmljs_llvm_init_native_function(Context *ctx, Value *result, void (*code)(Context *)) +void __qmljs_llvm_init_native_function(ExecutionContext *ctx, Value *result, void (*code)(ExecutionContext *)) { *result = __qmljs_init_native_function(code, ctx); } -bool __qmljs_llvm_to_boolean(Context *ctx, const Value *value) +bool __qmljs_llvm_to_boolean(ExecutionContext *ctx, const Value *value) { return __qmljs_to_boolean(*value, ctx); } -void __qmljs_llvm_bit_and(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_bit_and(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_bit_and(*left, *right, ctx); } -void __qmljs_llvm_bit_or(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_bit_or(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_bit_or(*left, *right, ctx); } -void __qmljs_llvm_bit_xor(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_bit_xor(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_bit_xor(*left, *right, ctx); } -void __qmljs_llvm_add(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_add(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_add(*left, *right, ctx); } -void __qmljs_llvm_sub(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_sub(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_sub(*left, *right, ctx); } -void __qmljs_llvm_mul(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_mul(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_mul(*left, *right, ctx); } -void __qmljs_llvm_div(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_div(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_div(*left, *right, ctx); } -void __qmljs_llvm_mod(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_mod(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_mod(*left, *right, ctx); } -void __qmljs_llvm_shl(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_shl(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_shl(*left, *right, ctx); } -void __qmljs_llvm_shr(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_shr(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_shr(*left, *right, ctx); } -void __qmljs_llvm_ushr(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_ushr(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_ushr(*left, *right, ctx); } -void __qmljs_llvm_gt(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_gt(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_gt(*left, *right, ctx); } -void __qmljs_llvm_lt(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_lt(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_lt(*left, *right, ctx); } -void __qmljs_llvm_ge(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_ge(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_ge(*left, *right, ctx); } -void __qmljs_llvm_le(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_le(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_le(*left, *right, ctx); } -void __qmljs_llvm_eq(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_eq(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_eq(*left, *right, ctx); } -void __qmljs_llvm_ne(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_ne(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_ne(*left, *right, ctx); } -void __qmljs_llvm_se(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_se(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_se(*left, *right, ctx); } -void __qmljs_llvm_sne(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_sne(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_sne(*left, *right, ctx); } -void __qmljs_llvm_instanceof(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_instanceof(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_instanceof(*left, *right, ctx); } -void __qmljs_llvm_in(Context *ctx, Value *result, Value *left, Value *right) +void __qmljs_llvm_in(ExecutionContext *ctx, Value *result, Value *left, Value *right) { *result = __qmljs_in(*left, *right, ctx); } -void __qmljs_llvm_uplus(Context *ctx, Value *result, const Value *value) +void __qmljs_llvm_uplus(ExecutionContext *ctx, Value *result, const Value *value) { *result = __qmljs_uplus(*value, ctx); } -void __qmljs_llvm_uminus(Context *ctx, Value *result, const Value *value) +void __qmljs_llvm_uminus(ExecutionContext *ctx, Value *result, const Value *value) { *result = __qmljs_uminus(*value, ctx); } -void __qmljs_llvm_compl(Context *ctx, Value *result, const Value *value) +void __qmljs_llvm_compl(ExecutionContext *ctx, Value *result, const Value *value) { *result = __qmljs_compl(*value, ctx); } -void __qmljs_llvm_not(Context *ctx, Value *result, const Value *value) +void __qmljs_llvm_not(ExecutionContext *ctx, Value *result, const Value *value) { *result = __qmljs_not(*value, ctx); } -void __qmljs_llvm_inplace_bit_and_name(Context *ctx, String *dest, Value *src) +void __qmljs_llvm_inplace_bit_and_name(ExecutionContext *ctx, String *dest, Value *src) { __qmljs_inplace_bit_and_name(*src, dest, ctx); } -void __qmljs_llvm_inplace_bit_or_name(Context *ctx, String *dest, Value *src) +void __qmljs_llvm_inplace_bit_or_name(ExecutionContext *ctx, String *dest, Value *src) { __qmljs_inplace_bit_or_name(*src, dest, ctx); } -void __qmljs_llvm_inplace_bit_xor_name(Context *ctx, String *dest, Value *src) +void __qmljs_llvm_inplace_bit_xor_name(ExecutionContext *ctx, String *dest, Value *src) { __qmljs_inplace_bit_xor_name(*src, dest, ctx); } -void __qmljs_llvm_inplace_add_name(Context *ctx, String *dest, Value *src) +void __qmljs_llvm_inplace_add_name(ExecutionContext *ctx, String *dest, Value *src) { __qmljs_inplace_add_name(*src, dest, ctx); } -void __qmljs_llvm_inplace_sub_name(Context *ctx, String *dest, Value *src) +void __qmljs_llvm_inplace_sub_name(ExecutionContext *ctx, String *dest, Value *src) { __qmljs_inplace_sub_name(*src, dest, ctx); } -void __qmljs_llvm_inplace_mul_name(Context *ctx, String *dest, Value *src) +void __qmljs_llvm_inplace_mul_name(ExecutionContext *ctx, String *dest, Value *src) { __qmljs_inplace_mul_name(*src, dest, ctx); } -void __qmljs_llvm_inplace_div_name(Context *ctx, String *dest, Value *src) +void __qmljs_llvm_inplace_div_name(ExecutionContext *ctx, String *dest, Value *src) { __qmljs_inplace_div_name(*src, dest, ctx); } -void __qmljs_llvm_inplace_mod_name(Context *ctx, String *dest, Value *src) +void __qmljs_llvm_inplace_mod_name(ExecutionContext *ctx, String *dest, Value *src) { __qmljs_inplace_mod_name(*src, dest, ctx); } -void __qmljs_llvm_inplace_shl_name(Context *ctx, String *dest, Value *src) +void __qmljs_llvm_inplace_shl_name(ExecutionContext *ctx, String *dest, Value *src) { __qmljs_inplace_shl_name(*src, dest, ctx); } -void __qmljs_llvm_inplace_shr_name(Context *ctx, String *dest, Value *src) +void __qmljs_llvm_inplace_shr_name(ExecutionContext *ctx, String *dest, Value *src) { __qmljs_inplace_shr_name(*src, dest, ctx); } -void __qmljs_llvm_inplace_ushr_name(Context *ctx, String *dest, Value *src) +void __qmljs_llvm_inplace_ushr_name(ExecutionContext *ctx, String *dest, Value *src) { __qmljs_inplace_ushr_name(*src, dest, ctx); } -void __qmljs_llvm_inplace_bit_and_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_llvm_inplace_bit_and_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { __qmljs_inplace_bit_and_element(*base, *index, *value, ctx); } -void __qmljs_llvm_inplace_bit_or_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_llvm_inplace_bit_or_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { __qmljs_inplace_bit_or_element(*base, *index, *value, ctx); } -void __qmljs_llvm_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_llvm_inplace_bit_xor_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { __qmljs_inplace_bit_xor_element(*base, *index, *value, ctx); } -void __qmljs_llvm_inplace_add_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_llvm_inplace_add_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { __qmljs_inplace_add_element(*base, *index, *value, ctx); } -void __qmljs_llvm_inplace_sub_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_llvm_inplace_sub_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { __qmljs_inplace_sub_element(*base, *index, *value, ctx); } -void __qmljs_llvm_inplace_mul_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_llvm_inplace_mul_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { __qmljs_inplace_mul_element(*base, *index, *value, ctx); } -void __qmljs_llvm_inplace_div_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_llvm_inplace_div_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { __qmljs_inplace_div_element(*base, *index, *value, ctx); } -void __qmljs_llvm_inplace_mod_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_llvm_inplace_mod_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { __qmljs_inplace_mod_element(*base, *index, *value, ctx); } -void __qmljs_llvm_inplace_shl_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_llvm_inplace_shl_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { __qmljs_inplace_shl_element(*base, *index, *value, ctx); } -void __qmljs_llvm_inplace_shr_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_llvm_inplace_shr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { __qmljs_inplace_shr_element(*base, *index, *value, ctx); } -void __qmljs_llvm_inplace_ushr_element(Context *ctx, Value *base, Value *index, Value *value) +void __qmljs_llvm_inplace_ushr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { __qmljs_inplace_ushr_element(*base, *index, *value, ctx); } -void __qmljs_llvm_inplace_bit_and_member(Context *ctx, Value *value, Value *base, String *member) +void __qmljs_llvm_inplace_bit_and_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { __qmljs_inplace_bit_and_member(*value, *base, member, ctx); } -void __qmljs_llvm_inplace_bit_or_member(Context *ctx, Value *value, Value *base, String *member) +void __qmljs_llvm_inplace_bit_or_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { __qmljs_inplace_bit_or_member(*value, *base, member, ctx); } -void __qmljs_llvm_inplace_bit_xor_member(Context *ctx, Value *value, Value *base, String *member) +void __qmljs_llvm_inplace_bit_xor_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { __qmljs_inplace_bit_xor_member(*value, *base, member, ctx); } -void __qmljs_llvm_inplace_add_member(Context *ctx, Value *value, Value *base, String *member) +void __qmljs_llvm_inplace_add_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { __qmljs_inplace_add_member(*value, *base, member, ctx); } -void __qmljs_llvm_inplace_sub_member(Context *ctx, Value *value, Value *base, String *member) +void __qmljs_llvm_inplace_sub_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { __qmljs_inplace_sub_member(*value, *base, member, ctx); } -void __qmljs_llvm_inplace_mul_member(Context *ctx, Value *value, Value *base, String *member) +void __qmljs_llvm_inplace_mul_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { __qmljs_inplace_mul_member(*value, *base, member, ctx); } -void __qmljs_llvm_inplace_div_member(Context *ctx, Value *value, Value *base, String *member) +void __qmljs_llvm_inplace_div_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { __qmljs_inplace_div_member(*value, *base, member, ctx); } -void __qmljs_llvm_inplace_mod_member(Context *ctx, Value *value, Value *base, String *member) +void __qmljs_llvm_inplace_mod_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { __qmljs_inplace_mod_member(*value, *base, member, ctx); } -void __qmljs_llvm_inplace_shl_member(Context *ctx, Value *value, Value *base, String *member) +void __qmljs_llvm_inplace_shl_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { __qmljs_inplace_shl_member(*value, *base, member, ctx); } -void __qmljs_llvm_inplace_shr_member(Context *ctx, Value *value, Value *base, String *member) +void __qmljs_llvm_inplace_shr_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { __qmljs_inplace_shr_member(*value, *base, member, ctx); } -void __qmljs_llvm_inplace_ushr_member(Context *ctx, Value *value, Value *base, String *member) +void __qmljs_llvm_inplace_ushr_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { __qmljs_inplace_ushr_member(*value, *base, member, ctx); } -String *__qmljs_llvm_identifier_from_utf8(Context *ctx, const char *str) +String *__qmljs_llvm_identifier_from_utf8(ExecutionContext *ctx, const char *str) { return __qmljs_identifier_from_utf8(ctx, str); // ### make it unique } -void __qmljs_llvm_call_activation_property(Context *context, Value *result, String *name, Value *args, int argc) +void __qmljs_llvm_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) { *result = __qmljs_call_activation_property(context, name, args, argc); } -void __qmljs_llvm_call_value(Context *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) +void __qmljs_llvm_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) { Value that = thisObject ? *thisObject : Value::undefinedValue(); *result = __qmljs_call_value(context, that, *func, args, argc); } -void __qmljs_llvm_construct_activation_property(Context *context, Value *result, String *name, Value *args, int argc) +void __qmljs_llvm_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) { *result = __qmljs_construct_activation_property(context, name, args, argc); } -void __qmljs_llvm_construct_value(Context *context, Value *result, const Value *func, Value *args, int argc) +void __qmljs_llvm_construct_value(ExecutionContext *context, Value *result, const Value *func, Value *args, int argc) { *result = __qmljs_construct_value(context, *func, args, argc); } -void __qmljs_llvm_get_activation_property(Context *ctx, Value *result, String *name) +void __qmljs_llvm_get_activation_property(ExecutionContext *ctx, Value *result, String *name) { *result = __qmljs_get_activation_property(ctx, name); } -void __qmljs_llvm_set_activation_property(Context *ctx, String *name, Value *value) +void __qmljs_llvm_set_activation_property(ExecutionContext *ctx, String *name, Value *value) { __qmljs_set_activation_property(ctx, name, *value); } -void __qmljs_llvm_get_property(Context *ctx, Value *result, Value *object, String *name) +void __qmljs_llvm_get_property(ExecutionContext *ctx, Value *result, Value *object, String *name) { *result = __qmljs_get_property(ctx, *object, name); } -void __qmljs_llvm_call_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) +void __qmljs_llvm_call_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) { *result = __qmljs_call_property(context, *base, name, args, argc); } -void __qmljs_llvm_construct_property(Context *context, Value *result, const Value *base, String *name, Value *args, int argc) +void __qmljs_llvm_construct_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) { *result = __qmljs_construct_property(context, *base, name, args, argc); } -void __qmljs_llvm_get_element(Context *ctx, Value *result, Value *object, Value *index) +void __qmljs_llvm_get_element(ExecutionContext *ctx, Value *result, Value *object, Value *index) { *result = __qmljs_get_element(ctx, *object, *index); } -void __qmljs_llvm_set_element(Context *ctx, Value *object, Value *index, Value *value) +void __qmljs_llvm_set_element(ExecutionContext *ctx, Value *object, Value *index, Value *value) { __qmljs_set_element(ctx, *object, *index, *value); } -void __qmljs_llvm_set_property(Context *ctx, Value *object, String *name, Value *value) +void __qmljs_llvm_set_property(ExecutionContext *ctx, Value *object, String *name, Value *value) { __qmljs_set_property(ctx, *object, name, *value); } -void __qmljs_llvm_typeof(Context *ctx, Value *result, const Value *value) +void __qmljs_llvm_typeof(ExecutionContext *ctx, Value *result, const Value *value) { *result = __qmljs_typeof(*value, ctx); } -void __qmljs_llvm_throw(Context *context, Value *value) +void __qmljs_llvm_throw(ExecutionContext *context, Value *value) { __qmljs_throw(*value, context); } -void __qmljs_llvm_create_exception_handler(Context *context, Value *result) +void __qmljs_llvm_create_exception_handler(ExecutionContext *context, Value *result) { void *buf = __qmljs_create_exception_handler(context); *result = Value::fromInt32(setjmp(* static_cast(buf))); } -void __qmljs_llvm_delete_exception_handler(Context *context) +void __qmljs_llvm_delete_exception_handler(ExecutionContext *context) { __qmljs_delete_exception_handler(context); } -void __qmljs_llvm_get_exception(Context *context, Value *result) +void __qmljs_llvm_get_exception(ExecutionContext *context, Value *result) { *result = __qmljs_get_exception(context); } -void __qmljs_llvm_foreach_iterator_object(Context *context, Value *result, Value *in) +void __qmljs_llvm_foreach_iterator_object(ExecutionContext *context, Value *result, Value *in) { *result = __qmljs_foreach_iterator_object(*in, context); } @@ -484,27 +484,27 @@ void __qmljs_llvm_foreach_next_property_name(Value *result, Value *it) *result = __qmljs_foreach_next_property_name(*it); } -void __qmljs_llvm_get_this_object(Context *ctx, Value *result) +void __qmljs_llvm_get_this_object(ExecutionContext *ctx, Value *result) { *result = __qmljs_get_thisObject(ctx); } -void __qmljs_llvm_delete_subscript(Context *ctx, Value *result, Value *base, Value *index) +void __qmljs_llvm_delete_subscript(ExecutionContext *ctx, Value *result, Value *base, Value *index) { *result = __qmljs_delete_subscript(ctx, *base, *index); } -void __qmljs_llvm_delete_member(Context *ctx, Value *result, Value *base, String *name) +void __qmljs_llvm_delete_member(ExecutionContext *ctx, Value *result, Value *base, String *name) { *result = __qmljs_delete_member(ctx, *base, name); } -void __qmljs_llvm_delete_property(Context *ctx, Value *result, String *name) +void __qmljs_llvm_delete_property(ExecutionContext *ctx, Value *result, String *name) { *result = __qmljs_delete_property(ctx, name); } -void __qmljs_llvm_delete_value(Context *ctx, Value *result, Value *value) +void __qmljs_llvm_delete_value(ExecutionContext *ctx, Value *result, Value *value) { *result = __qmljs_delete_value(ctx, *value); } diff --git a/main.cpp b/main.cpp index 12f0bb2a87..1e8acfdaaa 100644 --- a/main.cpp +++ b/main.cpp @@ -67,9 +67,9 @@ using namespace QQmlJS::VM; struct Print: FunctionObject { - Print(Context *scope): FunctionObject(scope) {} + Print(ExecutionContext *scope): FunctionObject(scope) {} - virtual void call(Context *ctx) + virtual void call(ExecutionContext *ctx) { for (unsigned int i = 0; i < ctx->argumentCount; ++i) { String *s = ctx->argument(i).toString(ctx); @@ -83,9 +83,9 @@ struct Print: FunctionObject struct TestHarnessError: FunctionObject { - TestHarnessError(Context *scope, bool &errorInTestHarness): FunctionObject(scope), errorOccurred(errorInTestHarness) {} + TestHarnessError(ExecutionContext *scope, bool &errorInTestHarness): FunctionObject(scope), errorOccurred(errorInTestHarness) {} - virtual void call(Context *ctx) + virtual void call(ExecutionContext *ctx) { errorOccurred = true; @@ -282,7 +282,7 @@ int main(int argc, char *argv[]) case use_moth: { bool useInterpreter = mode == use_moth; QQmlJS::VM::ExecutionEngine vm; - QQmlJS::VM::Context *ctx = vm.rootContext; + QQmlJS::VM::ExecutionContext *ctx = vm.rootContext; QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index d6500352cc..6fba81496e 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -240,13 +240,13 @@ union Instr }; struct instr_unop { MOTH_INSTR_HEADER - VM::Value (*alu)(const VM::Value value, VM::Context *ctx); + VM::Value (*alu)(const VM::Value value, VM::ExecutionContext *ctx); int e; int targetTempIndex; }; struct instr_binop { MOTH_INSTR_HEADER - VM::Value (*alu)(const VM::Value , const VM::Value, VM::Context *); + VM::Value (*alu)(const VM::Value , const VM::Value, VM::ExecutionContext *); int targetTempIndex; ValueOrTemp lhs; ValueOrTemp rhs; @@ -259,7 +259,7 @@ union Instr }; struct instr_inplaceElementOp { MOTH_INSTR_HEADER - void (*alu)(VM::Value, VM::Value, VM::Value, VM::Context *); + void (*alu)(VM::Value, VM::Value, VM::Value, VM::ExecutionContext *); int targetBase; int targetIndex; ValueOrTemp source; @@ -267,7 +267,7 @@ union Instr }; struct instr_inplaceMemberOp { MOTH_INSTR_HEADER - void (*alu)(VM::Value, VM::Value, VM::String *, VM::Context *); + void (*alu)(VM::Value, VM::Value, VM::String *, VM::ExecutionContext *); int targetBase; VM::String *targetMember; ValueOrTemp source; @@ -275,7 +275,7 @@ union Instr }; struct instr_inplaceNameOp { MOTH_INSTR_HEADER - void (*alu)(VM::Value, VM::String *, VM::Context *); + void (*alu)(VM::Value, VM::String *, VM::ExecutionContext *); VM::String *targetName; ValueOrTemp source; unsigned sourceIsTemp:1; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 849cd293d3..318b227df1 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -307,7 +307,7 @@ void InstructionSelection::visitLeave(IR::Leave *) Q_UNREACHABLE(); } -typedef VM::Value (*ALUFunction)(const VM::Value, const VM::Value, VM::Context*); +typedef VM::Value (*ALUFunction)(const VM::Value, const VM::Value, VM::ExecutionContext*); static inline ALUFunction aluOpFunction(IR::AluOp op) { switch (op) { @@ -461,7 +461,7 @@ void InstructionSelection::visitMove(IR::Move *s) addInstruction(load); } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { - VM::Value (*op)(const VM::Value value, VM::Context *ctx) = 0; + VM::Value (*op)(const VM::Value value, VM::ExecutionContext *ctx) = 0; switch (u->op) { case IR::OpIfTrue: assert(!"unreachable"); break; case IR::OpNot: op = VM::__qmljs_not; break; @@ -506,7 +506,7 @@ void InstructionSelection::visitMove(IR::Move *s) return; } else if (IR::Name *n = s->target->asName()) { if (s->source->asTemp() || s->source->asConst()) { - void (*op)(VM::Value value, VM::String *name, VM::Context *ctx) = 0; + void (*op)(VM::Value value, VM::String *name, VM::ExecutionContext *ctx) = 0; switch (s->op) { case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_name; break; case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_name; break; @@ -540,7 +540,7 @@ void InstructionSelection::visitMove(IR::Move *s) qWarning("NAME"); } else if (IR::Subscript *ss = s->target->asSubscript()) { if (s->source->asTemp() || s->source->asConst()) { - void (*op)(VM::Value base, VM::Value index, VM::Value value, VM::Context *ctx) = 0; + void (*op)(VM::Value base, VM::Value index, VM::Value value, VM::ExecutionContext *ctx) = 0; switch (s->op) { case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_element; break; case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_element; break; @@ -576,7 +576,7 @@ void InstructionSelection::visitMove(IR::Move *s) qWarning("SUBSCRIPT"); } else if (IR::Member *m = s->target->asMember()) { if (s->source->asTemp() || s->source->asConst()) { - void (*op)(VM::Value value, VM::Value base, VM::String *name, VM::Context *ctx) = 0; + void (*op)(VM::Value value, VM::Value base, VM::String *name, VM::ExecutionContext *ctx) = 0; switch (s->op) { case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_member; break; case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_member; break; diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 07860c24a0..c058002aa2 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -47,7 +47,7 @@ using namespace QQmlJS::Moth; #endif -static inline VM::Value *tempValue(QQmlJS::VM::Context *context, QVector &stack, int index) +static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVector &stack, int index) { #ifdef DO_TRACE_INSTR const char *kind; @@ -79,7 +79,7 @@ static inline VM::Value *tempValue(QQmlJS::VM::Context *context, QVectorengine->unwindStack.last(); targetTempIndex = handler.targetTempIndex; code = handler.code; } -void VME::saveState(VM::Context *context, int targetTempIndex, const uchar *code) +void VME::saveState(VM::ExecutionContext *context, int targetTempIndex, const uchar *code) { VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); handler.targetTempIndex = targetTempIndex; diff --git a/moth/qv4vme_moth_p.h b/moth/qv4vme_moth_p.h index e3a2347c34..428cd4e31d 100644 --- a/moth/qv4vme_moth_p.h +++ b/moth/qv4vme_moth_p.h @@ -10,9 +10,9 @@ namespace Moth { class VME { public: - static void exec(VM::Context *, const uchar *); + static void exec(VM::ExecutionContext *, const uchar *); - void operator()(QQmlJS::VM::Context *, const uchar *code + void operator()(QQmlJS::VM::ExecutionContext *, const uchar *code #ifdef MOTH_THREADED_INTERPRETER , void ***storeJumpTable = 0 #endif @@ -23,8 +23,8 @@ public: #endif private: - static void restoreState(VM::Context *context, int &targetTempIndex, const uchar *&code); - static void saveState(VM::Context *context, int targetTempIndex, const uchar *code); + static void restoreState(VM::ExecutionContext *context, int &targetTempIndex, const uchar *&code); + static void saveState(VM::ExecutionContext *context, int targetTempIndex, const uchar *code); }; } // namespace Moth diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 622ef621b1..d352774cdf 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -68,18 +68,18 @@ Object::~Object() delete members; } -void Object::__put__(Context *ctx, const QString &name, const Value &value) +void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &value) { __put__(ctx, ctx->engine->identifier(name), value); } -void Object::__put__(Context *ctx, const QString &name, void (*code)(Context *), int count) +void Object::__put__(ExecutionContext *ctx, const QString &name, void (*code)(ExecutionContext *), int count) { Q_UNUSED(count); __put__(ctx, name, Value::fromObject(ctx->engine->newNativeFunction(ctx, code))); } -Value Object::getValue(Context *ctx, PropertyDescriptor *p) const +Value Object::getValue(ExecutionContext *ctx, PropertyDescriptor *p) const { if (p->isData()) return p->value; @@ -90,7 +90,7 @@ Value Object::getValue(Context *ctx, PropertyDescriptor *p) const return ctx->result; } -bool Object::inplaceBinOp(Value rhs, String *name, BinOp op, Context *ctx) +bool Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx) { PropertyDescriptor to_fill; PropertyDescriptor *pd = __getPropertyDescriptor__(ctx, name, &to_fill); @@ -101,7 +101,7 @@ bool Object::inplaceBinOp(Value rhs, String *name, BinOp op, Context *ctx) return true; } -bool Object::inplaceBinOp(Value rhs, Value index, BinOp op, Context *ctx) +bool Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) { String *name = index.toString(ctx); assert(name); @@ -109,7 +109,7 @@ bool Object::inplaceBinOp(Value rhs, Value index, BinOp op, Context *ctx) } // Section 8.12.1 -PropertyDescriptor *Object::__getOwnProperty__(Context *, String *name) +PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *, String *name) { if (members) return members->find(name); @@ -117,7 +117,7 @@ PropertyDescriptor *Object::__getOwnProperty__(Context *, String *name) } // Section 8.12.2 -PropertyDescriptor *Object::__getPropertyDescriptor__(Context *ctx, String *name, PropertyDescriptor *to_fill) +PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill) { if (PropertyDescriptor *p = __getOwnProperty__(ctx, name)) return p; @@ -128,7 +128,7 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(Context *ctx, String *name } // Section 8.12.3 -Value Object::__get__(Context *ctx, String *name) +Value Object::__get__(ExecutionContext *ctx, String *name) { if (name->isEqualTo(ctx->engine->id___proto__)) return Value::fromObject(prototype); @@ -141,7 +141,7 @@ Value Object::__get__(Context *ctx, String *name) } // Section 8.12.4 -bool Object::__canPut__(Context *ctx, String *name) +bool Object::__canPut__(ExecutionContext *ctx, String *name) { if (PropertyDescriptor *p = __getOwnProperty__(ctx, name)) { if (p->isAccessor()) @@ -166,7 +166,7 @@ bool Object::__canPut__(Context *ctx, String *name) } // Section 8.12.5 -void Object::__put__(Context *ctx, String *name, const Value &value, bool throwException) +void Object::__put__(ExecutionContext *ctx, String *name, const Value &value, bool throwException) { // clause 1 if (!__canPut__(ctx, name)) @@ -216,7 +216,7 @@ void Object::__put__(Context *ctx, String *name, const Value &value, bool throwE } // Section 8.12.6 -bool Object::__hasProperty__(Context *ctx, String *name) const +bool Object::__hasProperty__(ExecutionContext *ctx, String *name) const { if (members) return members->find(name) != 0; @@ -225,7 +225,7 @@ bool Object::__hasProperty__(Context *ctx, String *name) const } // Section 8.12.7 -bool Object::__delete__(Context *ctx, String *name, bool throwException) +bool Object::__delete__(ExecutionContext *ctx, String *name, bool throwException) { if (members) { if (PropertyTableEntry *entry = members->findEntry(name)) { @@ -242,7 +242,7 @@ bool Object::__delete__(Context *ctx, String *name, bool throwException) } // Section 8.12.9 -bool Object::__defineOwnProperty__(Context *ctx, String *name, PropertyDescriptor *desc, bool throwException) +bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc, bool throwException) { if (!members) members = new PropertyTable(); @@ -340,14 +340,14 @@ String *ForEachIteratorObject::nextPropertyName() } } -Value ArrayObject::__get__(Context *ctx, String *name) +Value ArrayObject::__get__(ExecutionContext *ctx, String *name) { if (name->isEqualTo(ctx->engine->id_length)) return Value::fromDouble(value.size()); return Object::__get__(ctx, name); } -bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, Context *ctx) +bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) { if (index.isNumber()) { const quint32 idx = index.toUInt32(ctx); @@ -359,7 +359,7 @@ bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, Context *ctx) return Object::inplaceBinOp(rhs, index, op, ctx); } -bool FunctionObject::hasInstance(Context *ctx, const Value &value) +bool FunctionObject::hasInstance(ExecutionContext *ctx, const Value &value) { if (! value.isObject()) { ctx->throwTypeError(); @@ -385,20 +385,20 @@ bool FunctionObject::hasInstance(Context *ctx, const Value &value) return false; } -Value FunctionObject::construct(Context *context, Value *args, int argc) +Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc) { - Context k; - Context *ctx = needsActivation ? context->engine->newContext() : &k; + ExecutionContext k; + ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; ctx->initConstructorContext(context, Value::nullValue(), this, args, argc); construct(ctx); ctx->leaveConstructorContext(this); return ctx->result; } -Value FunctionObject::call(Context *context, Value thisObject, Value *args, int argc, bool strictMode) +Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode) { - Context k; - Context *ctx = needsActivation ? context->engine->newContext() : &k; + ExecutionContext k; + ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; if (!strictMode && !thisObject.isObject()) { if (thisObject.isUndefined() || thisObject.isNull()) @@ -412,18 +412,18 @@ Value FunctionObject::call(Context *context, Value thisObject, Value *args, int return ctx->result; } -void FunctionObject::call(Context *ctx) +void FunctionObject::call(ExecutionContext *ctx) { Q_UNUSED(ctx); } -void FunctionObject::construct(Context *ctx) +void FunctionObject::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(ctx->engine->newObject()); call(ctx); } -ScriptFunction::ScriptFunction(Context *scope, IR::Function *function) +ScriptFunction::ScriptFunction(ExecutionContext *scope, IR::Function *function) : FunctionObject(scope) , function(function) { @@ -453,13 +453,13 @@ ScriptFunction::~ScriptFunction() delete[] varList; } -void ScriptFunction::call(VM::Context *ctx) +void ScriptFunction::call(VM::ExecutionContext *ctx) { function->code(ctx, function->codeData); } -Value EvalFunction::call(Context *context, Value thisObject, Value *args, int argc, bool strictMode) +Value EvalFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode) { Value s = context->argument(0); if (!s.isString()) { @@ -471,7 +471,7 @@ Value EvalFunction::call(Context *context, Value thisObject, Value *args, int ar // ### how to determine this correctly bool directCall = true; - Context k, *ctx; + ExecutionContext k, *ctx; if (!directCall) { // ### } else if (strictMode) { @@ -497,7 +497,7 @@ static inline bool protect(const void *addr, size_t size) } -int EvalFunction::evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, +int EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, const QString &source, bool useInterpreter, QQmlJS::Codegen::Mode mode) { @@ -589,7 +589,7 @@ int EvalFunction::evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, } -Value RegExpObject::__get__(Context *ctx, String *name) +Value RegExpObject::__get__(ExecutionContext *ctx, String *name) { QString n = name->toQString(); if (n == QLatin1String("source")) @@ -605,7 +605,7 @@ Value RegExpObject::__get__(Context *ctx, String *name) return Object::__get__(ctx, name); } -Value ErrorObject::__get__(Context *ctx, String *name) +Value ErrorObject::__get__(ExecutionContext *ctx, String *name) { QString n = name->toQString(); if (n == QLatin1String("message")) @@ -613,12 +613,12 @@ Value ErrorObject::__get__(Context *ctx, String *name) return Object::__get__(ctx, name); } -void ErrorObject::setNameProperty(Context *ctx) +void ErrorObject::setNameProperty(ExecutionContext *ctx) { __put__(ctx, QLatin1String("name"), Value::fromString(ctx, className())); } -void ScriptFunction::construct(VM::Context *ctx) +void ScriptFunction::construct(VM::ExecutionContext *ctx) { Object *obj = ctx->engine->newObject(); Value proto = __get__(ctx, ctx->engine->id_prototype); @@ -628,7 +628,7 @@ void ScriptFunction::construct(VM::Context *ctx) function->code(ctx, function->codeData); } -PropertyDescriptor *ActivationObject::__getPropertyDescriptor__(Context *ctx, String *name, PropertyDescriptor *to_fill) +PropertyDescriptor *ActivationObject::__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill) { if (context) { for (unsigned int i = 0; i < context->varCount; ++i) { @@ -665,14 +665,14 @@ PropertyDescriptor *ActivationObject::__getPropertyDescriptor__(Context *ctx, St return Object::__getPropertyDescriptor__(ctx, name, to_fill); } -Value ArgumentsObject::__get__(Context *ctx, String *name) +Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name) { if (name->isEqualTo(ctx->engine->id_length)) return Value::fromDouble(context->argumentCount); return Object::__get__(ctx, name); } -PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(Context *ctx, String *name, PropertyDescriptor *to_fill) +PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill) { if (context) { const quint32 i = Value::fromString(name).toUInt32(ctx); @@ -807,9 +807,9 @@ ExecutionEngine::ExecutionEngine() } -Context *ExecutionEngine::newContext() +ExecutionContext *ExecutionEngine::newContext() { - return new Context(); + return new ExecutionContext(); } String *ExecutionEngine::identifier(const QString &s) @@ -820,14 +820,14 @@ String *ExecutionEngine::identifier(const QString &s) return id; } -FunctionObject *ExecutionEngine::newNativeFunction(Context *scope, void (*code)(Context *)) +FunctionObject *ExecutionEngine::newNativeFunction(ExecutionContext *scope, void (*code)(ExecutionContext *)) { NativeFunction *f = new NativeFunction(scope, code); f->prototype = scope->engine->functionPrototype; return f; } -FunctionObject *ExecutionEngine::newScriptFunction(Context *scope, IR::Function *function) +FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, IR::Function *function) { ScriptFunction *f = new ScriptFunction(scope, function); Object *proto = scope->engine->newObject(); @@ -844,7 +844,7 @@ Object *ExecutionEngine::newObject() return object; } -FunctionObject *ExecutionEngine::newObjectCtor(Context *ctx) +FunctionObject *ExecutionEngine::newObjectCtor(ExecutionContext *ctx) { return new ObjectCtor(ctx); } @@ -861,7 +861,7 @@ Object *ExecutionEngine::newStringObject(const Value &value) return object; } -FunctionObject *ExecutionEngine::newStringCtor(Context *ctx) +FunctionObject *ExecutionEngine::newStringCtor(ExecutionContext *ctx) { return new StringCtor(ctx); } @@ -873,7 +873,7 @@ Object *ExecutionEngine::newNumberObject(const Value &value) return object; } -FunctionObject *ExecutionEngine::newNumberCtor(Context *ctx) +FunctionObject *ExecutionEngine::newNumberCtor(ExecutionContext *ctx) { return new NumberCtor(ctx); } @@ -885,19 +885,19 @@ Object *ExecutionEngine::newBooleanObject(const Value &value) return object; } -FunctionObject *ExecutionEngine::newBooleanCtor(Context *ctx) +FunctionObject *ExecutionEngine::newBooleanCtor(ExecutionContext *ctx) { return new BooleanCtor(ctx); } -Object *ExecutionEngine::newFunctionObject(Context *ctx) +Object *ExecutionEngine::newFunctionObject(ExecutionContext *ctx) { Object *object = new FunctionObject(ctx); object->prototype = functionPrototype; return object; } -FunctionObject *ExecutionEngine::newFunctionCtor(Context *ctx) +FunctionObject *ExecutionEngine::newFunctionCtor(ExecutionContext *ctx) { return new FunctionCtor(ctx); } @@ -916,7 +916,7 @@ Object *ExecutionEngine::newArrayObject(const Array &value) return object; } -FunctionObject *ExecutionEngine::newArrayCtor(Context *ctx) +FunctionObject *ExecutionEngine::newArrayCtor(ExecutionContext *ctx) { return new ArrayCtor(ctx); } @@ -928,7 +928,7 @@ Object *ExecutionEngine::newDateObject(const Value &value) return object; } -FunctionObject *ExecutionEngine::newDateCtor(Context *ctx) +FunctionObject *ExecutionEngine::newDateCtor(ExecutionContext *ctx) { return new DateCtor(ctx); } @@ -947,7 +947,7 @@ Object *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) return object; } -FunctionObject *ExecutionEngine::newRegExpCtor(Context *ctx) +FunctionObject *ExecutionEngine::newRegExpCtor(ExecutionContext *ctx) { return new RegExpCtor(ctx); } @@ -959,14 +959,14 @@ Object *ExecutionEngine::newErrorObject(const Value &value) return object; } -Object *ExecutionEngine::newMathObject(Context *ctx) +Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) { MathObject *object = new MathObject(ctx); object->prototype = objectPrototype; return object; } -Object *ExecutionEngine::newActivationObject(Context *ctx) +Object *ExecutionEngine::newActivationObject(ExecutionContext *ctx) { return new ActivationObject(ctx); } diff --git a/qmljs_objects.h b/qmljs_objects.h index 3e06440b68..963b7638c6 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -72,7 +72,7 @@ struct RegExpObject; struct ErrorObject; struct ActivationObject; struct ArgumentsObject; -struct Context; +struct ExecutionContext; struct ExecutionEngine; struct ObjectPrototype; @@ -403,24 +403,24 @@ struct Object { virtual ActivationObject *asActivationObject() { return 0; } virtual ArgumentsObject *asArgumentsObject() { return 0; } - virtual Value __get__(Context *ctx, String *name); - virtual PropertyDescriptor *__getOwnProperty__(Context *ctx, String *name); - virtual PropertyDescriptor *__getPropertyDescriptor__(Context *ctx, String *name, PropertyDescriptor *to_fill); - virtual void __put__(Context *ctx, String *name, const Value &value, bool throwException = false); - virtual bool __canPut__(Context *ctx, String *name); - virtual bool __hasProperty__(Context *ctx, String *name) const; - virtual bool __delete__(Context *ctx, String *name, bool throwException); - virtual bool __defineOwnProperty__(Context *ctx, String *name, PropertyDescriptor *desc, bool throwException = false); + virtual Value __get__(ExecutionContext *ctx, String *name); + virtual PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); + virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); + virtual void __put__(ExecutionContext *ctx, String *name, const Value &value, bool throwException = false); + virtual bool __canPut__(ExecutionContext *ctx, String *name); + virtual bool __hasProperty__(ExecutionContext *ctx, String *name) const; + virtual bool __delete__(ExecutionContext *ctx, String *name, bool throwException); + virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc, bool throwException = false); // // helpers // - void __put__(Context *ctx, const QString &name, const Value &value); - void __put__(Context *ctx, const QString &name, void (*code)(Context *), int count = 0); + void __put__(ExecutionContext *ctx, const QString &name, const Value &value); + void __put__(ExecutionContext *ctx, const QString &name, void (*code)(ExecutionContext *), int count = 0); - Value getValue(Context *ctx, PropertyDescriptor *p) const; - bool inplaceBinOp(Value rhs, String *name, BinOp op, Context *ctx); - virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, Context *ctx); + Value getValue(ExecutionContext *ctx, PropertyDescriptor *p) const; + bool inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); + virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); }; struct ForEachIteratorObject: Object { @@ -467,13 +467,13 @@ struct ArrayObject: Object { ArrayObject(const Array &value): value(value) {} virtual QString className() { return QStringLiteral("Array"); } virtual ArrayObject *asArrayObject() { return this; } - virtual Value __get__(Context *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name); - virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, Context *ctx); + virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); }; struct FunctionObject: Object { - Context *scope; + ExecutionContext *scope; String *name; String **formalParameterList; unsigned int formalParameterCount; @@ -481,7 +481,7 @@ struct FunctionObject: Object { unsigned int varCount; bool needsActivation; - FunctionObject(Context *scope) + FunctionObject(ExecutionContext *scope) : scope(scope) , name(0) , formalParameterList(0) @@ -492,43 +492,43 @@ struct FunctionObject: Object { virtual QString className() { return QStringLiteral("Function"); } virtual FunctionObject *asFunctionObject() { return this; } - virtual bool hasInstance(Context *ctx, const Value &value); + virtual bool hasInstance(ExecutionContext *ctx, const Value &value); - Value construct(Context *context, Value *args, int argc); - virtual Value call(Context *context, Value thisObject, Value *args, int argc, bool strictMode = false); + Value construct(ExecutionContext *context, Value *args, int argc); + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode = false); protected: - virtual void call(Context *ctx); - virtual void construct(Context *ctx); + virtual void call(ExecutionContext *ctx); + virtual void construct(ExecutionContext *ctx); }; struct NativeFunction: FunctionObject { - void (*code)(Context *); + void (*code)(ExecutionContext *); - NativeFunction(Context *scope, void (*code)(Context *)): FunctionObject(scope), code(code) {} - virtual void call(Context *ctx) { code(ctx); } - virtual void construct(Context *ctx) { code(ctx); } + NativeFunction(ExecutionContext *scope, void (*code)(ExecutionContext *)): FunctionObject(scope), code(code) {} + virtual void call(ExecutionContext *ctx) { code(ctx); } + virtual void construct(ExecutionContext *ctx) { code(ctx); } }; struct ScriptFunction: FunctionObject { IR::Function *function; - ScriptFunction(Context *scope, IR::Function *function); + ScriptFunction(ExecutionContext *scope, IR::Function *function); virtual ~ScriptFunction(); - virtual void call(Context *ctx); - virtual void construct(Context *ctx); + virtual void call(ExecutionContext *ctx); + virtual void construct(ExecutionContext *ctx); }; struct EvalFunction : FunctionObject { - EvalFunction(Context *scope): FunctionObject(scope) {} + EvalFunction(ExecutionContext *scope): FunctionObject(scope) {} - static int evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, + static int evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, const QString &source, bool useInterpreter, QQmlJS::Codegen::Mode mode); - virtual Value call(Context *context, Value thisObject, Value *args, int argc, bool strictMode = false); + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode = false); }; struct RegExpObject: Object { @@ -538,7 +538,7 @@ struct RegExpObject: Object { RegExpObject(const QRegularExpression &value, bool global): value(value), lastIndex(Value::fromInt32(0)), global(global) {} virtual QString className() { return QStringLiteral("RegExp"); } virtual RegExpObject *asRegExpObject() { return this; } - virtual Value __get__(Context *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name); }; struct ErrorObject: Object { @@ -546,63 +546,63 @@ struct ErrorObject: Object { ErrorObject(const Value &message): value(message) {} virtual QString className() { return QStringLiteral("Error"); } virtual ErrorObject *asErrorObject() { return this; } - virtual Value __get__(Context *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name); protected: - void setNameProperty(Context *ctx); + void setNameProperty(ExecutionContext *ctx); }; struct EvalErrorObject: ErrorObject { - EvalErrorObject(Context *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + EvalErrorObject(ExecutionContext *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("EvalError"); } }; struct RangeErrorObject: ErrorObject { - RangeErrorObject(Context *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + RangeErrorObject(ExecutionContext *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("RangeError"); } }; struct ReferenceErrorObject: ErrorObject { - ReferenceErrorObject(Context *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + ReferenceErrorObject(ExecutionContext *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("ReferenceError"); } }; struct SyntaxErrorObject: ErrorObject { - SyntaxErrorObject(Context *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + SyntaxErrorObject(ExecutionContext *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("SyntaxError"); } }; struct TypeErrorObject: ErrorObject { - TypeErrorObject(Context *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + TypeErrorObject(ExecutionContext *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("TypeError"); } }; struct URIErrorObject: ErrorObject { - URIErrorObject(Context *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + URIErrorObject(ExecutionContext *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("URIError"); } }; struct ActivationObject: Object { - Context *context; + ExecutionContext *context; Value arguments; - ActivationObject(Context *context): context(context), arguments(Value::undefinedValue()) {} + ActivationObject(ExecutionContext *context): context(context), arguments(Value::undefinedValue()) {} virtual QString className() { return QStringLiteral("Activation"); } virtual ActivationObject *asActivationObject() { return this; } - virtual PropertyDescriptor *__getPropertyDescriptor__(Context *ctx, String *name, PropertyDescriptor *to_fill); + virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); }; struct ArgumentsObject: Object { - Context *context; - ArgumentsObject(Context *context): context(context) {} + ExecutionContext *context; + ArgumentsObject(ExecutionContext *context): context(context) {} virtual QString className() { return QStringLiteral("Arguments"); } virtual ArgumentsObject *asArgumentsObject() { return this; } - virtual Value __get__(Context *ctx, String *name); - virtual PropertyDescriptor *__getPropertyDescriptor__(Context *ctx, String *name, PropertyDescriptor *to_fill); + virtual Value __get__(ExecutionContext *ctx, String *name); + virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); }; struct ExecutionEngine { - Context *rootContext; + ExecutionContext *rootContext; Value globalObject; Value objectCtor; @@ -646,7 +646,7 @@ struct ExecutionEngine String *id___proto__; struct ExceptionHandler { - Context *context; + ExecutionContext *context; const uchar *code; // Interpreter state int targetTempIndex; // Interpreter state jmp_buf stackFrame; @@ -656,42 +656,42 @@ struct ExecutionEngine ExecutionEngine(); - Context *newContext(); + ExecutionContext *newContext(); String *identifier(const QString &s); - FunctionObject *newNativeFunction(Context *scope, void (*code)(Context *)); - FunctionObject *newScriptFunction(Context *scope, IR::Function *function); + FunctionObject *newNativeFunction(ExecutionContext *scope, void (*code)(ExecutionContext *)); + FunctionObject *newScriptFunction(ExecutionContext *scope, IR::Function *function); Object *newObject(); - FunctionObject *newObjectCtor(Context *ctx); + FunctionObject *newObjectCtor(ExecutionContext *ctx); String *newString(const QString &s); Object *newStringObject(const Value &value); - FunctionObject *newStringCtor(Context *ctx); + FunctionObject *newStringCtor(ExecutionContext *ctx); Object *newNumberObject(const Value &value); - FunctionObject *newNumberCtor(Context *ctx); + FunctionObject *newNumberCtor(ExecutionContext *ctx); Object *newBooleanObject(const Value &value); - FunctionObject *newBooleanCtor(Context *ctx); + FunctionObject *newBooleanCtor(ExecutionContext *ctx); - Object *newFunctionObject(Context *ctx); - FunctionObject *newFunctionCtor(Context *ctx); + Object *newFunctionObject(ExecutionContext *ctx); + FunctionObject *newFunctionCtor(ExecutionContext *ctx); Object *newArrayObject(); Object *newArrayObject(const Array &value); - FunctionObject *newArrayCtor(Context *ctx); + FunctionObject *newArrayCtor(ExecutionContext *ctx); Object *newDateObject(const Value &value); - FunctionObject *newDateCtor(Context *ctx); + FunctionObject *newDateCtor(ExecutionContext *ctx); Object *newRegExpObject(const QString &pattern, int flags); - FunctionObject *newRegExpCtor(Context *ctx); + FunctionObject *newRegExpCtor(ExecutionContext *ctx); Object *newErrorObject(const Value &value); - Object *newMathObject(Context *ctx); - Object *newActivationObject(Context *ctx); + Object *newMathObject(ExecutionContext *ctx); + Object *newActivationObject(ExecutionContext *ctx); Object *newForEachIteratorObject(Object *o); }; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index d115877e9c..ec97864192 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -56,7 +56,7 @@ namespace QQmlJS { namespace VM { -static inline Value callFunction(Context *context, Value thisObject, FunctionObject *func, Value *args, int argc) +static inline Value callFunction(ExecutionContext *context, Value thisObject, FunctionObject *func, Value *args, int argc) { if (func) { return func->call(context, thisObject, args, argc); @@ -113,7 +113,7 @@ QString numberToString(double num, int radix = 10) } -Value Value::fromString(Context *ctx, const QString &s) +Value Value::fromString(ExecutionContext *ctx, const QString &s) { return fromString(ctx->engine->newString(s)); } @@ -225,12 +225,12 @@ ActivationObject *Value::asArgumentsObject() const return isObject() ? objectValue()->asActivationObject() : 0; } -Value Value::property(Context *ctx, String *name) const +Value Value::property(ExecutionContext *ctx, String *name) const { return isObject() ? objectValue()->__get__(ctx, name) : undefinedValue(); } -void Context::init(ExecutionEngine *eng) +void ExecutionContext::init(ExecutionEngine *eng) { engine = eng; parent = 0; @@ -246,9 +246,9 @@ void Context::init(ExecutionEngine *eng) varCount = 0; } -PropertyDescriptor *Context::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp) +PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp) { - for (Context *ctx = this; ctx; ctx = ctx->parent) { + for (ExecutionContext *ctx = this; ctx; ctx = ctx->parent) { if (ctx->activation) { if (PropertyDescriptor *pd = ctx->activation->__getPropertyDescriptor__(this, name, tmp)) return pd; @@ -257,9 +257,9 @@ PropertyDescriptor *Context::lookupPropertyDescriptor(String *name, PropertyDesc return 0; } -void Context::inplaceBitOp(Value value, String *name, BinOp op) +void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) { - for (Context *ctx = this; ctx; ctx = ctx->parent) { + for (ExecutionContext *ctx = this; ctx; ctx = ctx->parent) { if (ctx->activation) { if (ctx->activation->inplaceBinOp(value, name, op, this)) return; @@ -268,38 +268,38 @@ void Context::inplaceBitOp(Value value, String *name, BinOp op) throwReferenceError(Value::fromString(name)); } -void Context::throwError(Value value) +void ExecutionContext::throwError(Value value) { result = value; __qmljs_builtin_throw(value, this); } -void Context::throwError(const QString &message) +void ExecutionContext::throwError(const QString &message) { Value v = Value::fromString(this, message); throwError(Value::fromObject(engine->newErrorObject(v))); } -void Context::throwTypeError() +void ExecutionContext::throwTypeError() { Value v = Value::fromString(this, QStringLiteral("Type error")); throwError(Value::fromObject(engine->newErrorObject(v))); } -void Context::throwUnimplemented(const QString &message) +void ExecutionContext::throwUnimplemented(const QString &message) { Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message); throwError(Value::fromObject(engine->newErrorObject(v))); } -void Context::throwReferenceError(Value value) +void ExecutionContext::throwReferenceError(Value value) { String *s = value.toString(this); QString msg = s->toQString() + QStringLiteral(" is not defined"); throwError(Value::fromObject(engine->newErrorObject(Value::fromString(this, msg)))); } -void Context::initCallContext(Context *parent, const Value that, FunctionObject *f, Value *args, unsigned argc) +void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc) { engine = parent->engine; this->parent = f->scope; @@ -331,7 +331,7 @@ void Context::initCallContext(Context *parent, const Value that, FunctionObject std::fill(locals, locals + varCount, Value::undefinedValue()); } -void Context::leaveCallContext() +void ExecutionContext::leaveCallContext() { if (activation) { delete[] locals; @@ -339,18 +339,18 @@ void Context::leaveCallContext() } } -void Context::initConstructorContext(Context *parent, Value that, FunctionObject *f, Value *args, unsigned argc) +void ExecutionContext::initConstructorContext(ExecutionContext *parent, Value that, FunctionObject *f, Value *args, unsigned argc) { initCallContext(parent, that, f, args, argc); } -void Context::leaveConstructorContext(FunctionObject *f) +void ExecutionContext::leaveConstructorContext(FunctionObject *f) { wireUpPrototype(f); leaveCallContext(); } -void Context::wireUpPrototype(FunctionObject *f) +void ExecutionContext::wireUpPrototype(FunctionObject *f) { assert(thisObject.isObject()); result = thisObject; @@ -364,62 +364,62 @@ void Context::wireUpPrototype(FunctionObject *f) extern "C" { -Value __qmljs_init_closure(IR::Function *clos, Context *ctx) +Value __qmljs_init_closure(IR::Function *clos, ExecutionContext *ctx) { return Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); } -Value __qmljs_init_native_function(void (*code)(Context *), Context *ctx) +Value __qmljs_init_native_function(void (*code)(ExecutionContext *), ExecutionContext *ctx) { return Value::fromObject(ctx->engine->newNativeFunction(ctx, code)); } -Value __qmljs_string_literal_undefined(Context *ctx) +Value __qmljs_string_literal_undefined(ExecutionContext *ctx) { return Value::fromString(ctx->engine->identifier(QStringLiteral("undefined"))); } -Value __qmljs_string_literal_null(Context *ctx) +Value __qmljs_string_literal_null(ExecutionContext *ctx) { return Value::fromString(ctx->engine->identifier(QStringLiteral("null"))); } -Value __qmljs_string_literal_true(Context *ctx) +Value __qmljs_string_literal_true(ExecutionContext *ctx) { return Value::fromString(ctx->engine->identifier(QStringLiteral("true"))); } -Value __qmljs_string_literal_false(Context *ctx) +Value __qmljs_string_literal_false(ExecutionContext *ctx) { return Value::fromString(ctx->engine->identifier(QStringLiteral("false"))); } -Value __qmljs_string_literal_object(Context *ctx) +Value __qmljs_string_literal_object(ExecutionContext *ctx) { return Value::fromString(ctx->engine->identifier(QStringLiteral("object"))); } -Value __qmljs_string_literal_boolean(Context *ctx) +Value __qmljs_string_literal_boolean(ExecutionContext *ctx) { return Value::fromString(ctx->engine->identifier(QStringLiteral("boolean"))); } -Value __qmljs_string_literal_number(Context *ctx) +Value __qmljs_string_literal_number(ExecutionContext *ctx) { return Value::fromString(ctx->engine->identifier(QStringLiteral("number"))); } -Value __qmljs_string_literal_string(Context *ctx) +Value __qmljs_string_literal_string(ExecutionContext *ctx) { return Value::fromString(ctx->engine->identifier(QStringLiteral("string"))); } -Value __qmljs_string_literal_function(Context *ctx) +Value __qmljs_string_literal_function(ExecutionContext *ctx) { return Value::fromString(ctx->engine->identifier(QStringLiteral("function"))); } -Value __qmljs_delete_subscript(Context *ctx, Value base, Value index) +Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index) { if (ArrayObject *a = base.asArrayObject()) { int n = -1; @@ -439,13 +439,13 @@ Value __qmljs_delete_subscript(Context *ctx, Value base, Value index) return __qmljs_delete_member(ctx, base, name); } -Value __qmljs_delete_member(Context *ctx, Value base, String *name) +Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name) { Value obj = base.toObject(ctx); return Value::fromBoolean(obj.objectValue()->__delete__(ctx, name, true)); } -Value __qmljs_delete_property(Context *ctx, String *name) +Value __qmljs_delete_property(ExecutionContext *ctx, String *name) { Object *obj = ctx->activation; if (!obj) @@ -453,13 +453,13 @@ Value __qmljs_delete_property(Context *ctx, String *name) return Value::fromBoolean(obj->__delete__(ctx, name, true)); } -Value __qmljs_delete_value(Context *ctx, Value value) +Value __qmljs_delete_value(ExecutionContext *ctx, Value value) { Q_UNUSED(value); return __qmljs_throw_type_error(ctx); // ### throw syntax error } -Value __qmljs_add_helper(Value left, Value right, Context *ctx) +Value __qmljs_add_helper(Value left, Value right, ExecutionContext *ctx) { Value pleft = __qmljs_to_primitive(left, ctx, PREFERREDTYPE_HINT); Value pright = __qmljs_to_primitive(right, ctx, PREFERREDTYPE_HINT); @@ -476,7 +476,7 @@ Value __qmljs_add_helper(Value left, Value right, Context *ctx) return Value::fromDouble(x + y); } -Value __qmljs_instanceof(Value left, Value right, Context *ctx) +Value __qmljs_instanceof(Value left, Value right, ExecutionContext *ctx) { if (FunctionObject *function = right.asFunctionObject()) { bool r = function->hasInstance(ctx, left); @@ -486,7 +486,7 @@ Value __qmljs_instanceof(Value left, Value right, Context *ctx) return __qmljs_throw_type_error(ctx); } -Value __qmljs_in(Value left, Value right, Context *ctx) +Value __qmljs_in(Value left, Value right, ExecutionContext *ctx) { if (right.isObject()) { Value s = __qmljs_to_string(left, ctx); @@ -497,209 +497,209 @@ Value __qmljs_in(Value left, Value right, Context *ctx) } } -void __qmljs_inplace_bit_and_name(Value value, String *name, Context *ctx) +void __qmljs_inplace_bit_and_name(Value value, String *name, ExecutionContext *ctx) { ctx->inplaceBitOp(value, name, __qmljs_bit_and); } -void __qmljs_inplace_bit_or_name(Value value, String *name, Context *ctx) +void __qmljs_inplace_bit_or_name(Value value, String *name, ExecutionContext *ctx) { ctx->inplaceBitOp(value, name, __qmljs_bit_or); } -void __qmljs_inplace_bit_xor_name(Value value, String *name, Context *ctx) +void __qmljs_inplace_bit_xor_name(Value value, String *name, ExecutionContext *ctx) { ctx->inplaceBitOp(value, name, __qmljs_bit_xor); } -void __qmljs_inplace_add_name(Value value, String *name, Context *ctx) +void __qmljs_inplace_add_name(Value value, String *name, ExecutionContext *ctx) { ctx->inplaceBitOp(value, name, __qmljs_add); } -void __qmljs_inplace_sub_name(Value value, String *name, Context *ctx) +void __qmljs_inplace_sub_name(Value value, String *name, ExecutionContext *ctx) { ctx->inplaceBitOp(value, name, __qmljs_sub); } -void __qmljs_inplace_mul_name(Value value, String *name, Context *ctx) +void __qmljs_inplace_mul_name(Value value, String *name, ExecutionContext *ctx) { ctx->inplaceBitOp(value, name, __qmljs_mul); } -void __qmljs_inplace_div_name(Value value, String *name, Context *ctx) +void __qmljs_inplace_div_name(Value value, String *name, ExecutionContext *ctx) { ctx->inplaceBitOp(value, name, __qmljs_div); } -void __qmljs_inplace_mod_name(Value value, String *name, Context *ctx) +void __qmljs_inplace_mod_name(Value value, String *name, ExecutionContext *ctx) { ctx->inplaceBitOp(value, name, __qmljs_mod); } -void __qmljs_inplace_shl_name(Value value, String *name, Context *ctx) +void __qmljs_inplace_shl_name(Value value, String *name, ExecutionContext *ctx) { ctx->inplaceBitOp(value, name, __qmljs_shl); } -void __qmljs_inplace_shr_name(Value value, String *name, Context *ctx) +void __qmljs_inplace_shr_name(Value value, String *name, ExecutionContext *ctx) { ctx->inplaceBitOp(value, name, __qmljs_shr); } -void __qmljs_inplace_ushr_name(Value value, String *name, Context *ctx) +void __qmljs_inplace_ushr_name(Value value, String *name, ExecutionContext *ctx) { ctx->inplaceBitOp(value, name, __qmljs_ushr); } -void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, Context *ctx) +void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, ExecutionContext *ctx) { Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(value, index, __qmljs_bit_and, ctx); } -void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, Context *ctx) +void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, ExecutionContext *ctx) { Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(value, index, __qmljs_bit_or, ctx); } -void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, Context *ctx) +void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, ExecutionContext *ctx) { Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(value, index, __qmljs_bit_xor, ctx); } -void __qmljs_inplace_add_element(Value base, Value index, Value value, Context *ctx) +void __qmljs_inplace_add_element(Value base, Value index, Value value, ExecutionContext *ctx) { Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(value, index, __qmljs_add, ctx); } -void __qmljs_inplace_sub_element(Value base, Value index, Value value, Context *ctx) +void __qmljs_inplace_sub_element(Value base, Value index, Value value, ExecutionContext *ctx) { Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(value, index, __qmljs_sub, ctx); } -void __qmljs_inplace_mul_element(Value base, Value index, Value value, Context *ctx) +void __qmljs_inplace_mul_element(Value base, Value index, Value value, ExecutionContext *ctx) { Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(value, index, __qmljs_mul, ctx); } -void __qmljs_inplace_div_element(Value base, Value index, Value value, Context *ctx) +void __qmljs_inplace_div_element(Value base, Value index, Value value, ExecutionContext *ctx) { Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(value, index, __qmljs_div, ctx); } -void __qmljs_inplace_mod_element(Value base, Value index, Value value, Context *ctx) +void __qmljs_inplace_mod_element(Value base, Value index, Value value, ExecutionContext *ctx) { Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(value, index, __qmljs_mod, ctx); } -void __qmljs_inplace_shl_element(Value base, Value index, Value value, Context *ctx) +void __qmljs_inplace_shl_element(Value base, Value index, Value value, ExecutionContext *ctx) { Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(value, index, __qmljs_shl, ctx); } -void __qmljs_inplace_shr_element(Value base, Value index, Value value, Context *ctx) +void __qmljs_inplace_shr_element(Value base, Value index, Value value, ExecutionContext *ctx) { Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(value, index, __qmljs_shr, ctx); } -void __qmljs_inplace_ushr_element(Value base, Value index, Value value, Context *ctx) +void __qmljs_inplace_ushr_element(Value base, Value index, Value value, ExecutionContext *ctx) { Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(value, index, __qmljs_ushr, ctx); } -void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, Context *ctx) +void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, ExecutionContext *ctx) { Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(value, name, __qmljs_bit_and, ctx); } -void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, Context *ctx) +void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, ExecutionContext *ctx) { Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(value, name, __qmljs_bit_or, ctx); } -void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, Context *ctx) +void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, ExecutionContext *ctx) { Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(value, name, __qmljs_bit_xor, ctx); } -void __qmljs_inplace_add_member(Value value, Value base, String *name, Context *ctx) +void __qmljs_inplace_add_member(Value value, Value base, String *name, ExecutionContext *ctx) { Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(value, name, __qmljs_add, ctx); } -void __qmljs_inplace_sub_member(Value value, Value base, String *name, Context *ctx) +void __qmljs_inplace_sub_member(Value value, Value base, String *name, ExecutionContext *ctx) { Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(value, name, __qmljs_sub, ctx); } -void __qmljs_inplace_mul_member(Value value, Value base, String *name, Context *ctx) +void __qmljs_inplace_mul_member(Value value, Value base, String *name, ExecutionContext *ctx) { Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(value, name, __qmljs_mul, ctx); } -void __qmljs_inplace_div_member(Value value, Value base, String *name, Context *ctx) +void __qmljs_inplace_div_member(Value value, Value base, String *name, ExecutionContext *ctx) { Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(value, name, __qmljs_div, ctx); } -void __qmljs_inplace_mod_member(Value value, Value base, String *name, Context *ctx) +void __qmljs_inplace_mod_member(Value value, Value base, String *name, ExecutionContext *ctx) { Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(value, name, __qmljs_mod, ctx); } -void __qmljs_inplace_shl_member(Value value, Value base, String *name, Context *ctx) +void __qmljs_inplace_shl_member(Value value, Value base, String *name, ExecutionContext *ctx) { Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(value, name, __qmljs_shl, ctx); } -void __qmljs_inplace_shr_member(Value value, Value base, String *name, Context *ctx) +void __qmljs_inplace_shr_member(Value value, Value base, String *name, ExecutionContext *ctx) { Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(value, name, __qmljs_shr, ctx); } -void __qmljs_inplace_ushr_member(Value value, Value base, String *name, Context *ctx) +void __qmljs_inplace_ushr_member(Value value, Value base, String *name, ExecutionContext *ctx) { Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(value, name, __qmljs_ushr, ctx); } -String *__qmljs_string_from_utf8(Context *ctx, const char *s) +String *__qmljs_string_from_utf8(ExecutionContext *ctx, const char *s) { return ctx->engine->newString(QString::fromUtf8(s)); } -String *__qmljs_identifier_from_utf8(Context *ctx, const char *s) +String *__qmljs_identifier_from_utf8(ExecutionContext *ctx, const char *s) { return ctx->engine->identifier(QString::fromUtf8(s)); } -int __qmljs_string_length(Context *, String *string) +int __qmljs_string_length(ExecutionContext *, String *string) { return string->toQString().length(); } -double __qmljs_string_to_number(Context *, String *string) +double __qmljs_string_to_number(ExecutionContext *, String *string) { const QString s = string->toQString(); if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) @@ -710,13 +710,13 @@ double __qmljs_string_to_number(Context *, String *string) return d; } -Value __qmljs_string_from_number(Context *ctx, double number) +Value __qmljs_string_from_number(ExecutionContext *ctx, double number) { String *string = ctx->engine->newString(numberToString(number, 10)); return Value::fromString(string); } -Bool __qmljs_string_compare(Context *, String *left, String *right) +Bool __qmljs_string_compare(ExecutionContext *, String *left, String *right) { return left->toQString() < right->toQString(); } @@ -726,7 +726,7 @@ Bool __qmljs_string_equal(String *left, String *right) return left->isEqualTo(right); } -String *__qmljs_string_concat(Context *ctx, String *first, String *second) +String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second) { return ctx->engine->newString(first->toQString() + second->toQString()); } @@ -736,7 +736,7 @@ Bool __qmljs_is_function(Value value) return value.objectValue()->asFunctionObject() != 0; } -Value __qmljs_object_default_value(Context *ctx, Value object, int typeHint) +Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int typeHint) { if (typeHint == PREFERREDTYPE_HINT) { if (object.asDateObject()) @@ -771,67 +771,67 @@ Value __qmljs_object_default_value(Context *ctx, Value object, int typeHint) return Value::undefinedValue(); } -Value __qmljs_throw_type_error(Context *ctx) +Value __qmljs_throw_type_error(ExecutionContext *ctx) { ctx->throwTypeError(); return ctx->result; } -Value __qmljs_new_object(Context *ctx) +Value __qmljs_new_object(ExecutionContext *ctx) { return Value::fromObject(ctx->engine->newObject()); } -Value __qmljs_new_boolean_object(Context *ctx, bool boolean) +Value __qmljs_new_boolean_object(ExecutionContext *ctx, bool boolean) { Value value = Value::fromBoolean(boolean); return Value::fromObject(ctx->engine->newBooleanObject(value)); } -Value __qmljs_new_number_object(Context *ctx, double number) +Value __qmljs_new_number_object(ExecutionContext *ctx, double number) { Value value = Value::fromDouble(number); return Value::fromObject(ctx->engine->newNumberObject(value)); } -Value __qmljs_new_string_object(Context *ctx, String *string) +Value __qmljs_new_string_object(ExecutionContext *ctx, String *string) { Value value = Value::fromString(string); return Value::fromObject(ctx->engine->newStringObject(value)); } -void __qmljs_set_property(Context *ctx, Value object, String *name, Value value) +void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Value value) { object.objectValue()->__put__(ctx, name, value, /*flags*/ 0); } -void __qmljs_set_property_boolean(Context *ctx, Value *object, String *name, bool b) +void __qmljs_set_property_boolean(ExecutionContext *ctx, Value *object, String *name, bool b) { Value value = Value::fromBoolean(b); object->objectValue()->__put__(ctx, name, value, /*flag*/ 0); } -void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double number) +void __qmljs_set_property_number(ExecutionContext *ctx, Value *object, String *name, double number) { Q_UNUSED(ctx); Value value = Value::fromDouble(number); object->objectValue()->__put__(ctx, name, value, /*flag*/ 0); } -void __qmljs_set_property_string(Context *ctx, Value *object, String *name, String *s) +void __qmljs_set_property_string(ExecutionContext *ctx, Value *object, String *name, String *s) { Q_UNUSED(ctx); Value value = Value::fromString(s); object->objectValue()->__put__(ctx, name, value, /*flag*/ 0); } -void __qmljs_set_property_closure(Context *ctx, Value *object, String *name, IR::Function *function) +void __qmljs_set_property_closure(ExecutionContext *ctx, Value *object, String *name, IR::Function *function) { Value value = __qmljs_init_closure(function, ctx); object->objectValue()->__put__(ctx, name, value, /*flag*/ 0); } -Value __qmljs_get_element(Context *ctx, Value object, Value index) +Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) { if (index.isNumber()) { if (object.isString()) { @@ -854,7 +854,7 @@ Value __qmljs_get_element(Context *ctx, Value object, Value index) return object.objectValue()->__get__(ctx, name); } -void __qmljs_set_element(Context *ctx, Value object, Value index, Value value) +void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value value) { if (index.isNumber()) { if (ArrayObject *a = object.asArrayObject()) { @@ -871,7 +871,7 @@ void __qmljs_set_element(Context *ctx, Value object, Value index, Value value) object.objectValue()->__put__(ctx, name, value, /*flags*/ 0); } -Value __qmljs_foreach_iterator_object(Value in, Context *ctx) +Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx) { in = __qmljs_to_object(in, ctx); Object *it = ctx->engine->newForEachIteratorObject(in.objectValue()); @@ -892,7 +892,7 @@ Value __qmljs_foreach_next_property_name(Value foreach_iterator) } -void __qmljs_set_activation_property(Context *ctx, String *name, Value value) +void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, Value value) { PropertyDescriptor tmp; if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) @@ -901,31 +901,31 @@ void __qmljs_set_activation_property(Context *ctx, String *name, Value value) ctx->engine->globalObject.objectValue()->__put__(ctx, name, value); } -void __qmljs_set_activation_property_boolean(Context *ctx, String *name, bool b) +void __qmljs_set_activation_property_boolean(ExecutionContext *ctx, String *name, bool b) { Value value = Value::fromBoolean(b); __qmljs_set_activation_property(ctx, name, value); } -void __qmljs_set_activation_property_number(Context *ctx, String *name, double number) +void __qmljs_set_activation_property_number(ExecutionContext *ctx, String *name, double number) { Value value = Value::fromDouble(number); __qmljs_set_activation_property(ctx, name, value); } -void __qmljs_set_activation_property_string(Context *ctx, String *name, String *string) +void __qmljs_set_activation_property_string(ExecutionContext *ctx, String *name, String *string) { Value value = Value::fromString(string); __qmljs_set_activation_property(ctx, name, value); } -void __qmljs_set_activation_property_closure(Context *ctx, String *name, IR::Function *clos) +void __qmljs_set_activation_property_closure(ExecutionContext *ctx, String *name, IR::Function *clos) { Value value = __qmljs_init_closure(clos, ctx); __qmljs_set_activation_property(ctx, name, value); } -Value __qmljs_get_property(Context *ctx, Value object, String *name) +Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name) { if (object.isObject()) { return object.objectValue()->__get__(ctx, name); @@ -943,7 +943,7 @@ Value __qmljs_get_property(Context *ctx, Value object, String *name) } } -Value __qmljs_get_activation_property(Context *ctx, String *name) +Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name) { PropertyDescriptor tmp; if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) @@ -952,14 +952,14 @@ Value __qmljs_get_activation_property(Context *ctx, String *name) return Value::undefinedValue(); } -Value __qmljs_get_thisObject(Context *ctx) +Value __qmljs_get_thisObject(ExecutionContext *ctx) { if (ctx->thisObject.isObject()) return ctx->thisObject; return ctx->engine->globalObject; } -uint __qmljs_equal(Value x, Value y, Context *ctx) +uint __qmljs_equal(Value x, Value y, ExecutionContext *ctx) { if (x.type() == y.type()) { switch (x.type()) { @@ -1012,7 +1012,7 @@ uint __qmljs_equal(Value x, Value y, Context *ctx) } // TODO: remove this function. Backends should just generate a __qmljs_get_activation_property followed by a __qmljs_call_value -Value __qmljs_call_activation_property(Context *context, String *name, Value *args, int argc) +Value __qmljs_call_activation_property(ExecutionContext *context, String *name, Value *args, int argc) { Value func = __qmljs_get_activation_property(context, name); if (FunctionObject *f = func.asFunctionObject()) { @@ -1023,7 +1023,7 @@ Value __qmljs_call_activation_property(Context *context, String *name, Value *ar } } -Value __qmljs_call_property(Context *context, Value base, String *name, Value *args, int argc) +Value __qmljs_call_property(ExecutionContext *context, Value base, String *name, Value *args, int argc) { Object *baseObject; Value thisObject; @@ -1043,12 +1043,12 @@ Value __qmljs_call_property(Context *context, Value base, String *name, Value *a return callFunction(context, thisObject, func.asFunctionObject(), args, argc); } -Value __qmljs_call_value(Context *context, Value thisObject, Value func, Value *args, int argc) +Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc) { return callFunction(context, thisObject, func.asFunctionObject(), args, argc); } -Value __qmljs_construct_activation_property(Context *context, String *name, Value *args, int argc) +Value __qmljs_construct_activation_property(ExecutionContext *context, String *name, Value *args, int argc) { PropertyDescriptor tmp; PropertyDescriptor *func = context->lookupPropertyDescriptor(name, &tmp); @@ -1059,7 +1059,7 @@ Value __qmljs_construct_activation_property(Context *context, String *name, Valu return __qmljs_construct_value(context, func->value, args, argc); } -Value __qmljs_construct_value(Context *context, Value func, Value *args, int argc) +Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc) { if (FunctionObject *f = func.asFunctionObject()) return f->construct(context, args, argc); @@ -1068,7 +1068,7 @@ Value __qmljs_construct_value(Context *context, Value func, Value *args, int arg return Value::undefinedValue(); } -Value __qmljs_construct_property(Context *context, Value base, String *name, Value *args, int argc) +Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc) { Value thisObject = base; if (!thisObject.isObject()) @@ -1082,7 +1082,7 @@ Value __qmljs_construct_property(Context *context, Value base, String *name, Val return Value::undefinedValue(); } -void __qmljs_throw(Value value, Context *context) +void __qmljs_throw(Value value, ExecutionContext *context) { assert(!context->engine->unwindStack.isEmpty()); @@ -1099,7 +1099,7 @@ void __qmljs_throw(Value value, Context *context) longjmp(handler.stackFrame, 1); } -void *__qmljs_create_exception_handler(Context *context) +void *__qmljs_create_exception_handler(ExecutionContext *context) { context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler()); ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); @@ -1107,24 +1107,24 @@ void *__qmljs_create_exception_handler(Context *context) return handler.stackFrame; } -void __qmljs_delete_exception_handler(Context *context) +void __qmljs_delete_exception_handler(ExecutionContext *context) { assert(!context->engine->unwindStack.isEmpty()); context->engine->unwindStack.pop_back(); } -Value __qmljs_get_exception(Context *context) +Value __qmljs_get_exception(ExecutionContext *context) { return context->result; } -Value __qmljs_builtin_typeof(Value val, Context *context) +Value __qmljs_builtin_typeof(Value val, ExecutionContext *context) { return __qmljs_typeof(val, context); } -void __qmljs_builtin_throw(Value val, Context *context) +void __qmljs_builtin_throw(Value val, ExecutionContext *context) { __qmljs_throw(val, context); } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 072367cba2..98c628cf9c 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -75,7 +75,7 @@ enum TypeHint { struct Object; struct String; struct PropertyDescriptor; -struct Context; +struct ExecutionContext; struct FunctionObject; struct BooleanObject; struct NumberObject; @@ -208,21 +208,21 @@ struct Value static Value fromObject(Object *o); #ifndef QMLJS_LLVM_RUNTIME - static Value fromString(Context *ctx, const QString &fromString); + static Value fromString(ExecutionContext *ctx, const QString &fromString); #endif static double toInteger(double fromNumber); static int toInt32(double value); static unsigned int toUInt32(double value); - inline int toUInt16(Context *ctx); - inline int toInt32(Context *ctx); - inline unsigned int toUInt32(Context *ctx); - inline Bool toBoolean(Context *ctx) const; - inline double toInteger(Context *ctx) const; - double toNumber(Context *ctx) const; - inline String *toString(Context *ctx) const; - inline Value toObject(Context *ctx) const; + inline int toUInt16(ExecutionContext *ctx); + inline int toInt32(ExecutionContext *ctx); + inline unsigned int toUInt32(ExecutionContext *ctx); + inline Bool toBoolean(ExecutionContext *ctx) const; + inline double toInteger(ExecutionContext *ctx) const; + double toNumber(ExecutionContext *ctx) const; + inline String *toString(ExecutionContext *ctx) const; + inline Value toObject(ExecutionContext *ctx) const; inline bool isPrimitive() const { return !isObject(); } #if CPU(X86_64) @@ -260,7 +260,7 @@ struct Value ErrorObject *asErrorObject() const; ActivationObject *asArgumentsObject() const; - Value property(Context *ctx, String *name) const; + Value property(ExecutionContext *ctx, String *name) const; // Section 9.12 bool sameValue(Value other); @@ -269,182 +269,182 @@ struct Value extern "C" { // context -Value __qmljs_call_activation_property(Context *, String *name, Value *args, int argc); -Value __qmljs_call_property(Context *context, Value base, String *name, Value *args, int argc); -Value __qmljs_call_value(Context *context, Value thisObject, Value func, Value *args, int argc); +Value __qmljs_call_activation_property(ExecutionContext *, String *name, Value *args, int argc); +Value __qmljs_call_property(ExecutionContext *context, Value base, String *name, Value *args, int argc); +Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc); -Value __qmljs_construct_activation_property(Context *, String *name, Value *args, int argc); -Value __qmljs_construct_property(Context *context, Value base, String *name, Value *args, int argc); -Value __qmljs_construct_value(Context *context, Value func, Value *args, int argc); +Value __qmljs_construct_activation_property(ExecutionContext *, String *name, Value *args, int argc); +Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc); +Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc); -Value __qmljs_builtin_typeof(Value val, Context *context); -void __qmljs_builtin_throw(Value val, Context *context); +Value __qmljs_builtin_typeof(Value val, ExecutionContext *context); +void __qmljs_builtin_throw(Value val, ExecutionContext *context); // constructors -Value __qmljs_init_closure(IR::Function *clos, Context *ctx); -Value __qmljs_init_native_function(void (*code)(Context *), Context *ctx); +Value __qmljs_init_closure(IR::Function *clos, ExecutionContext *ctx); +Value __qmljs_init_native_function(void (*code)(ExecutionContext *), ExecutionContext *ctx); Bool __qmljs_is_function(Value value); // string literals -Value __qmljs_string_literal_undefined(Context *ctx); -Value __qmljs_string_literal_null(Context *ctx); -Value __qmljs_string_literal_true(Context *ctx); -Value __qmljs_string_literal_false(Context *ctx); -Value __qmljs_string_literal_object(Context *ctx); -Value __qmljs_string_literal_boolean(Context *ctx); -Value __qmljs_string_literal_number(Context *ctx); -Value __qmljs_string_literal_string(Context *ctx); -Value __qmljs_string_literal_function(Context *ctx); +Value __qmljs_string_literal_undefined(ExecutionContext *ctx); +Value __qmljs_string_literal_null(ExecutionContext *ctx); +Value __qmljs_string_literal_true(ExecutionContext *ctx); +Value __qmljs_string_literal_false(ExecutionContext *ctx); +Value __qmljs_string_literal_object(ExecutionContext *ctx); +Value __qmljs_string_literal_boolean(ExecutionContext *ctx); +Value __qmljs_string_literal_number(ExecutionContext *ctx); +Value __qmljs_string_literal_string(ExecutionContext *ctx); +Value __qmljs_string_literal_function(ExecutionContext *ctx); // strings -String *__qmljs_string_from_utf8(Context *ctx, const char *s); -int __qmljs_string_length(Context *ctx, String *string); -double __qmljs_string_to_number(Context *ctx, String *string); -Value __qmljs_string_from_number(Context *ctx, double number); -Bool __qmljs_string_compare(Context *ctx, String *left, String *right); +String *__qmljs_string_from_utf8(ExecutionContext *ctx, const char *s); +int __qmljs_string_length(ExecutionContext *ctx, String *string); +double __qmljs_string_to_number(ExecutionContext *ctx, String *string); +Value __qmljs_string_from_number(ExecutionContext *ctx, double number); +Bool __qmljs_string_compare(ExecutionContext *ctx, String *left, String *right); Bool __qmljs_string_equal(String *left, String *right); -String *__qmljs_string_concat(Context *ctx, String *first, String *second); -String *__qmljs_identifier_from_utf8(Context *ctx, const char *s); +String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second); +String *__qmljs_identifier_from_utf8(ExecutionContext *ctx, const char *s); // objects -Value __qmljs_object_default_value(Context *ctx, Value object, int typeHint); -Value __qmljs_throw_type_error(Context *ctx); -Value __qmljs_new_object(Context *ctx); -Value __qmljs_new_boolean_object(Context *ctx, bool boolean); -Value __qmljs_new_number_object(Context *ctx, double n); -Value __qmljs_new_string_object(Context *ctx, String *string); -void __qmljs_set_activation_property(Context *ctx, String *name, Value value); -void __qmljs_set_property(Context *ctx, Value object, String *name, Value value); -Value __qmljs_get_property(Context *ctx, Value object, String *name); -Value __qmljs_get_activation_property(Context *ctx, String *name); - -Value __qmljs_get_element(Context *ctx, Value object, Value index); -void __qmljs_set_element(Context *ctx, Value object, Value index, Value value); +Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int typeHint); +Value __qmljs_throw_type_error(ExecutionContext *ctx); +Value __qmljs_new_object(ExecutionContext *ctx); +Value __qmljs_new_boolean_object(ExecutionContext *ctx, bool boolean); +Value __qmljs_new_number_object(ExecutionContext *ctx, double n); +Value __qmljs_new_string_object(ExecutionContext *ctx, String *string); +void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, Value value); +void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Value value); +Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name); +Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name); + +Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index); +void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value value); // For each -Value __qmljs_foreach_iterator_object(Value in, Context *ctx); +Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx); Value __qmljs_foreach_next_property_name(Value foreach_iterator); // context -Value __qmljs_get_thisObject(Context *ctx); +Value __qmljs_get_thisObject(ExecutionContext *ctx); // type conversion and testing -Value __qmljs_to_primitive(Value value, Context *ctx, int typeHint); -Bool __qmljs_to_boolean(Value value, Context *ctx); -double __qmljs_to_number(Value value, Context *ctx); -double __qmljs_to_integer(Value value, Context *ctx); -int __qmljs_to_int32(Value value, Context *ctx); -unsigned short __qmljs_to_uint16(Value value, Context *ctx); -Value __qmljs_to_string(Value value, Context *ctx); -Value __qmljs_to_object(Value value, Context *ctx); +Value __qmljs_to_primitive(Value value, ExecutionContext *ctx, int typeHint); +Bool __qmljs_to_boolean(Value value, ExecutionContext *ctx); +double __qmljs_to_number(Value value, ExecutionContext *ctx); +double __qmljs_to_integer(Value value, ExecutionContext *ctx); +int __qmljs_to_int32(Value value, ExecutionContext *ctx); +unsigned short __qmljs_to_uint16(Value value, ExecutionContext *ctx); +Value __qmljs_to_string(Value value, ExecutionContext *ctx); +Value __qmljs_to_object(Value value, ExecutionContext *ctx); //uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *value); -Bool __qmljs_is_callable(Value value, Context *ctx); -Value __qmljs_default_value(Value value, Context *ctx, int typeHint); +Bool __qmljs_is_callable(Value value, ExecutionContext *ctx); +Value __qmljs_default_value(Value value, ExecutionContext *ctx, int typeHint); -Bool __qmljs_equal(Value x, Value y, Context *ctx); +Bool __qmljs_equal(Value x, Value y, ExecutionContext *ctx); Bool __qmljs_strict_equal(Value x, Value y); // unary operators -Value __qmljs_uplus(Value value, Context *ctx); -Value __qmljs_uminus(Value value, Context *ctx); -Value __qmljs_compl(Value value, Context *ctx); -Value __qmljs_not(Value value, Context *ctx); - -Value __qmljs_delete_subscript(Context *ctx, Value base, Value index); -Value __qmljs_delete_member(Context *ctx, Value base, String *name); -Value __qmljs_delete_property(Context *ctx, String *name); -Value __qmljs_delete_value(Context *ctx, Value value); - -Value __qmljs_typeof(Value value, Context *ctx); -void __qmljs_throw(Value value, Context *context); +Value __qmljs_uplus(Value value, ExecutionContext *ctx); +Value __qmljs_uminus(Value value, ExecutionContext *ctx); +Value __qmljs_compl(Value value, ExecutionContext *ctx); +Value __qmljs_not(Value value, ExecutionContext *ctx); + +Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index); +Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name); +Value __qmljs_delete_property(ExecutionContext *ctx, String *name); +Value __qmljs_delete_value(ExecutionContext *ctx, Value value); + +Value __qmljs_typeof(Value value, ExecutionContext *ctx); +void __qmljs_throw(Value value, ExecutionContext *context); // actually returns a jmp_buf * -void *__qmljs_create_exception_handler(Context *context); -void __qmljs_delete_exception_handler(Context *context); -Value __qmljs_get_exception(Context *context); +void *__qmljs_create_exception_handler(ExecutionContext *context); +void __qmljs_delete_exception_handler(ExecutionContext *context); +Value __qmljs_get_exception(ExecutionContext *context); // binary operators -typedef Value (*BinOp)(Value left, Value right, Context *ctx); - -Value __qmljs_instanceof(Value left, Value right, Context *ctx); -Value __qmljs_in(Value left, Value right, Context *ctx); -Value __qmljs_bit_or(Value left, Value right, Context *ctx); -Value __qmljs_bit_xor(Value left, Value right, Context *ctx); -Value __qmljs_bit_and(Value left, Value right, Context *ctx); -Value __qmljs_add(Value left, Value right, Context *ctx); -Value __qmljs_sub(Value left, Value right, Context *ctx); -Value __qmljs_mul(Value left, Value right, Context *ctx); -Value __qmljs_div(Value left, Value right, Context *ctx); -Value __qmljs_mod(Value left, Value right, Context *ctx); -Value __qmljs_shl(Value left, Value right, Context *ctx); -Value __qmljs_shr(Value left, Value right, Context *ctx); -Value __qmljs_ushr(Value left, Value right, Context *ctx); -Value __qmljs_gt(Value left, Value right, Context *ctx); -Value __qmljs_lt(Value left, Value right, Context *ctx); -Value __qmljs_ge(Value left, Value right, Context *ctx); -Value __qmljs_le(Value left, Value right, Context *ctx); -Value __qmljs_eq(Value left, Value right, Context *ctx); -Value __qmljs_ne(Value left, Value right, Context *ctx); -Value __qmljs_se(Value left, Value right, Context *ctx); -Value __qmljs_sne(Value left, Value right, Context *ctx); - -Value __qmljs_add_helper(Value left, Value right, Context *ctx); - -void __qmljs_inplace_bit_and_name(Value value, String *name, Context *ctx); -void __qmljs_inplace_bit_or_name(Value value, String *name, Context *ctx); -void __qmljs_inplace_bit_xor_name(Value value, String *name, Context *ctx); -void __qmljs_inplace_add_name(Value value, String *name, Context *ctx); -void __qmljs_inplace_sub_name(Value value, String *name, Context *ctx); -void __qmljs_inplace_mul_name(Value value, String *name, Context *ctx); -void __qmljs_inplace_div_name(Value value, String *name, Context *ctx); -void __qmljs_inplace_mod_name(Value value, String *name, Context *ctx); -void __qmljs_inplace_shl_name(Value value, String *name, Context *ctx); -void __qmljs_inplace_shr_name(Value value, String *name, Context *ctx); -void __qmljs_inplace_ushr_name(Value value, String *name, Context *ctx); - -void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, Context *ctx); -void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, Context *ctx); -void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, Context *ctx); -void __qmljs_inplace_add_element(Value base, Value index, Value value, Context *ctx); -void __qmljs_inplace_sub_element(Value base, Value index, Value value, Context *ctx); -void __qmljs_inplace_mul_element(Value base, Value index, Value value, Context *ctx); -void __qmljs_inplace_div_element(Value base, Value index, Value value, Context *ctx); -void __qmljs_inplace_mod_element(Value base, Value index, Value value, Context *ctx); -void __qmljs_inplace_shl_element(Value base, Value index, Value value, Context *ctx); -void __qmljs_inplace_shr_element(Value base, Value index, Value value, Context *ctx); -void __qmljs_inplace_ushr_element(Value base, Value index, Value value, Context *ctx); - -void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, Context *ctx); -void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, Context *ctx); -void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, Context *ctx); -void __qmljs_inplace_add_member(Value value, Value base, String *name, Context *ctx); -void __qmljs_inplace_sub_member(Value value, Value base, String *name, Context *ctx); -void __qmljs_inplace_mul_member(Value value, Value base, String *name, Context *ctx); -void __qmljs_inplace_div_member(Value value, Value base, String *name, Context *ctx); -void __qmljs_inplace_mod_member(Value value, Value base, String *name, Context *ctx); -void __qmljs_inplace_shl_member(Value value, Value base, String *name, Context *ctx); -void __qmljs_inplace_shr_member(Value value, Value base, String *name, Context *ctx); -void __qmljs_inplace_ushr_member(Value value, Value base, String *name, Context *ctx); - -Bool __qmljs_cmp_gt(Value left, Value right, Context *ctx); -Bool __qmljs_cmp_lt(Value left, Value right, Context *ctx); -Bool __qmljs_cmp_ge(Value left, Value right, Context *ctx); -Bool __qmljs_cmp_le(Value left, Value right, Context *ctx); -Bool __qmljs_cmp_eq(Value left, Value right, Context *ctx); -Bool __qmljs_cmp_ne(Value left, Value right, Context *ctx); -Bool __qmljs_cmp_se(Value left, Value right, Context *ctx); -Bool __qmljs_cmp_sne(Value left, Value right, Context *ctx); -Bool __qmljs_cmp_instanceof(Value left, Value right, Context *ctx); -Bool __qmljs_cmp_in(Value left, Value right, Context *ctx); +typedef Value (*BinOp)(Value left, Value right, ExecutionContext *ctx); + +Value __qmljs_instanceof(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_in(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_bit_or(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_bit_xor(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_bit_and(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_add(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_sub(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_mul(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_div(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_mod(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_shl(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_shr(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_ushr(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_gt(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_lt(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_ge(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_le(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_eq(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_ne(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_se(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_sne(Value left, Value right, ExecutionContext *ctx); + +Value __qmljs_add_helper(Value left, Value right, ExecutionContext *ctx); + +void __qmljs_inplace_bit_and_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_bit_or_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_bit_xor_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_add_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_sub_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_mul_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_div_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_mod_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_shl_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_shr_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_ushr_name(Value value, String *name, ExecutionContext *ctx); + +void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_add_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_sub_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_mul_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_div_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_mod_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_shl_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_shr_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_ushr_element(Value base, Value index, Value value, ExecutionContext *ctx); + +void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_add_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_sub_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_mul_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_div_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_mod_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_shl_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_shr_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_ushr_member(Value value, Value base, String *name, ExecutionContext *ctx); + +Bool __qmljs_cmp_gt(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_lt(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_ge(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_le(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_eq(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_ne(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_se(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_sne(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_instanceof(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_in(Value left, Value right, ExecutionContext *ctx); } // extern "C" -inline int Value::toUInt16(Context *ctx) +inline int Value::toUInt16(ExecutionContext *ctx) { return __qmljs_to_uint16(*this, ctx); } -inline int Value::toInt32(Context *ctx) +inline int Value::toInt32(ExecutionContext *ctx) { if (isConvertibleToInt()) return int_32; @@ -452,7 +452,7 @@ inline int Value::toInt32(Context *ctx) return Value::toInt32(__qmljs_to_number(*this, ctx)); } -inline unsigned int Value::toUInt32(Context *ctx) +inline unsigned int Value::toUInt32(ExecutionContext *ctx) { if (isConvertibleToInt()) return (unsigned) int_32; @@ -460,29 +460,29 @@ inline unsigned int Value::toUInt32(Context *ctx) return toUInt32(__qmljs_to_number(*this, ctx)); } -inline Bool Value::toBoolean(Context *ctx) const +inline Bool Value::toBoolean(ExecutionContext *ctx) const { return __qmljs_to_boolean(*this, ctx); } -inline double Value::toInteger(Context *ctx) const +inline double Value::toInteger(ExecutionContext *ctx) const { return __qmljs_to_integer(*this, ctx); } -inline double Value::toNumber(Context *ctx) const +inline double Value::toNumber(ExecutionContext *ctx) const { return __qmljs_to_number(*this, ctx); } -inline String *Value::toString(Context *ctx) const +inline String *Value::toString(ExecutionContext *ctx) const { Value v = __qmljs_to_string(*this, ctx); assert(v.isString()); return v.stringValue(); } -inline Value Value::toObject(Context *ctx) const +inline Value Value::toObject(ExecutionContext *ctx) const { return __qmljs_to_object(*this, ctx); } @@ -570,9 +570,9 @@ inline bool Value::sameValue(Value other) { #include -struct Context { +struct ExecutionContext { ExecutionEngine *engine; - Context *parent; + ExecutionContext *parent; Object *activation; Value thisObject; Value *arguments; @@ -596,10 +596,10 @@ struct Context { void init(ExecutionEngine *eng); - void initCallContext(Context *parent, const Value that, FunctionObject *f, Value *args, unsigned argc); + void initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc); void leaveCallContext(); - void initConstructorContext(Context *parent, Value that, FunctionObject *f, Value *args, unsigned argc); + void initConstructorContext(ExecutionContext *parent, Value that, FunctionObject *f, Value *args, unsigned argc); void leaveConstructorContext(FunctionObject *f); void wireUpPrototype(FunctionObject *f); @@ -615,14 +615,14 @@ struct Context { extern "C" { // type conversion and testing -inline Value __qmljs_to_primitive(Value value, Context *ctx, int typeHint) +inline Value __qmljs_to_primitive(Value value, ExecutionContext *ctx, int typeHint) { if (!value.isObject()) return value; return __qmljs_default_value(value, ctx, typeHint); } -inline Bool __qmljs_to_boolean(Value value, Context *ctx) +inline Bool __qmljs_to_boolean(Value value, ExecutionContext *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -642,7 +642,7 @@ inline Bool __qmljs_to_boolean(Value value, Context *ctx) } } -inline double __qmljs_to_number(Value value, Context *ctx) +inline double __qmljs_to_number(Value value, ExecutionContext *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -664,7 +664,7 @@ inline double __qmljs_to_number(Value value, Context *ctx) } } -inline double __qmljs_to_integer(Value value, Context *ctx) +inline double __qmljs_to_integer(Value value, ExecutionContext *ctx) { if (value.isConvertibleToInt()) return value.int_32; @@ -672,7 +672,7 @@ inline double __qmljs_to_integer(Value value, Context *ctx) return Value::toInteger(__qmljs_to_number(value, ctx)); } -inline int __qmljs_to_int32(Value value, Context *ctx) +inline int __qmljs_to_int32(Value value, ExecutionContext *ctx) { if (value.isConvertibleToInt()) return value.int_32; @@ -680,7 +680,7 @@ inline int __qmljs_to_int32(Value value, Context *ctx) return Value::toInt32(__qmljs_to_number(value, ctx)); } -inline unsigned short __qmljs_to_uint16(Value value, Context *ctx) +inline unsigned short __qmljs_to_uint16(Value value, ExecutionContext *ctx) { if (value.isConvertibleToInt()) return (ushort)(uint)value.integerValue(); @@ -706,7 +706,7 @@ inline unsigned short __qmljs_to_uint16(Value value, Context *ctx) return (unsigned short)number; } -inline Value __qmljs_to_string(Value value, Context *ctx) +inline Value __qmljs_to_string(Value value, ExecutionContext *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -742,7 +742,7 @@ inline Value __qmljs_to_string(Value value, Context *ctx) } // switch } -inline Value __qmljs_to_object(Value value, Context *ctx) +inline Value __qmljs_to_object(Value value, ExecutionContext *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -781,7 +781,7 @@ inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *v } */ -inline Bool __qmljs_is_callable(Value value, Context * /*ctx*/) +inline Bool __qmljs_is_callable(Value value, ExecutionContext * /*ctx*/) { if (value.isObject()) return __qmljs_is_function(value); @@ -789,7 +789,7 @@ inline Bool __qmljs_is_callable(Value value, Context * /*ctx*/) return false; } -inline Value __qmljs_default_value(Value value, Context *ctx, int typeHint) +inline Value __qmljs_default_value(Value value, ExecutionContext *ctx, int typeHint) { if (value.isObject()) return __qmljs_object_default_value(ctx, value, typeHint); @@ -798,7 +798,7 @@ inline Value __qmljs_default_value(Value value, Context *ctx, int typeHint) // unary operators -inline Value __qmljs_typeof(Value value, Context *ctx) +inline Value __qmljs_typeof(Value value, ExecutionContext *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -825,7 +825,7 @@ inline Value __qmljs_typeof(Value value, Context *ctx) } } -inline Value __qmljs_uplus(Value value, Context *ctx) +inline Value __qmljs_uplus(Value value, ExecutionContext *ctx) { TRACE1(value); @@ -836,7 +836,7 @@ inline Value __qmljs_uplus(Value value, Context *ctx) return Value::fromDouble(n); } -inline Value __qmljs_uminus(Value value, Context *ctx) +inline Value __qmljs_uminus(Value value, ExecutionContext *ctx) { TRACE1(value); @@ -846,7 +846,7 @@ inline Value __qmljs_uminus(Value value, Context *ctx) return Value::fromDouble(-n); } -inline Value __qmljs_compl(Value value, Context *ctx) +inline Value __qmljs_compl(Value value, ExecutionContext *ctx) { TRACE1(value); @@ -859,7 +859,7 @@ inline Value __qmljs_compl(Value value, Context *ctx) return Value::fromInt32(~n); } -inline Value __qmljs_not(Value value, Context *ctx) +inline Value __qmljs_not(Value value, ExecutionContext *ctx) { TRACE1(value); @@ -868,7 +868,7 @@ inline Value __qmljs_not(Value value, Context *ctx) } // binary operators -inline Value __qmljs_bit_or(Value left, Value right, Context *ctx) +inline Value __qmljs_bit_or(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -880,7 +880,7 @@ inline Value __qmljs_bit_or(Value left, Value right, Context *ctx) return Value::fromInt32(lval | rval); } -inline Value __qmljs_bit_xor(Value left, Value right, Context *ctx) +inline Value __qmljs_bit_xor(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -892,7 +892,7 @@ inline Value __qmljs_bit_xor(Value left, Value right, Context *ctx) return Value::fromInt32(lval ^ rval); } -inline Value __qmljs_bit_and(Value left, Value right, Context *ctx) +inline Value __qmljs_bit_and(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -904,7 +904,7 @@ inline Value __qmljs_bit_and(Value left, Value right, Context *ctx) return Value::fromInt32(lval & rval); } -inline Value __qmljs_add(Value left, Value right, Context *ctx) +inline Value __qmljs_add(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -919,7 +919,7 @@ inline Value __qmljs_add(Value left, Value right, Context *ctx) return __qmljs_add_helper(left, right, ctx); } -inline Value __qmljs_sub(Value left, Value right, Context *ctx) +inline Value __qmljs_sub(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -933,7 +933,7 @@ inline Value __qmljs_sub(Value left, Value right, Context *ctx) return Value::fromDouble(lval - rval); } -inline Value __qmljs_mul(Value left, Value right, Context *ctx) +inline Value __qmljs_mul(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -947,7 +947,7 @@ inline Value __qmljs_mul(Value left, Value right, Context *ctx) return Value::fromDouble(lval * rval); } -inline Value __qmljs_div(Value left, Value right, Context *ctx) +inline Value __qmljs_div(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -956,7 +956,7 @@ inline Value __qmljs_div(Value left, Value right, Context *ctx) return Value::fromDouble(lval / rval); } -inline Value __qmljs_mod(Value left, Value right, Context *ctx) +inline Value __qmljs_mod(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -970,7 +970,7 @@ inline Value __qmljs_mod(Value left, Value right, Context *ctx) // ### unsigned shl missing? -inline Value __qmljs_shl(Value left, Value right, Context *ctx) +inline Value __qmljs_shl(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -982,7 +982,7 @@ inline Value __qmljs_shl(Value left, Value right, Context *ctx) return Value::fromInt32(lval << rval); } -inline Value __qmljs_shr(Value left, Value right, Context *ctx) +inline Value __qmljs_shr(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -994,7 +994,7 @@ inline Value __qmljs_shr(Value left, Value right, Context *ctx) return Value::fromInt32(lval >> rval); } -inline Value __qmljs_ushr(Value left, Value right, Context *ctx) +inline Value __qmljs_ushr(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -1006,49 +1006,49 @@ inline Value __qmljs_ushr(Value left, Value right, Context *ctx) return Value::fromInt32(lval >> rval); } -inline Value __qmljs_gt(Value left, Value right, Context *ctx) +inline Value __qmljs_gt(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); return Value::fromBoolean(__qmljs_cmp_gt(left, right, ctx)); } -inline Value __qmljs_lt(Value left, Value right, Context *ctx) +inline Value __qmljs_lt(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); return Value::fromBoolean(__qmljs_cmp_lt(left, right, ctx)); } -inline Value __qmljs_ge(Value left, Value right, Context *ctx) +inline Value __qmljs_ge(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); return Value::fromBoolean(__qmljs_cmp_ge(left, right, ctx)); } -inline Value __qmljs_le(Value left, Value right, Context *ctx) +inline Value __qmljs_le(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); return Value::fromBoolean(__qmljs_cmp_le(left, right, ctx)); } -inline Value __qmljs_eq(Value left, Value right, Context *ctx) +inline Value __qmljs_eq(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); return Value::fromBoolean(__qmljs_cmp_eq(left, right, ctx)); } -inline Value __qmljs_ne(Value left, Value right, Context *ctx) +inline Value __qmljs_ne(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); return Value::fromBoolean(!__qmljs_cmp_eq(left, right, ctx)); } -inline Value __qmljs_se(Value left, Value right, Context *) +inline Value __qmljs_se(Value left, Value right, ExecutionContext *) { TRACE2(left, right); @@ -1056,7 +1056,7 @@ inline Value __qmljs_se(Value left, Value right, Context *) return Value::fromBoolean(r); } -inline Value __qmljs_sne(Value left, Value right, Context *) +inline Value __qmljs_sne(Value left, Value right, ExecutionContext *) { TRACE2(left, right); @@ -1064,7 +1064,7 @@ inline Value __qmljs_sne(Value left, Value right, Context *) return Value::fromBoolean(r); } -inline Bool __qmljs_cmp_gt(Value left, Value right, Context *ctx) +inline Bool __qmljs_cmp_gt(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -1084,7 +1084,7 @@ inline Bool __qmljs_cmp_gt(Value left, Value right, Context *ctx) } } -inline Bool __qmljs_cmp_lt(Value left, Value right, Context *ctx) +inline Bool __qmljs_cmp_lt(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -1104,7 +1104,7 @@ inline Bool __qmljs_cmp_lt(Value left, Value right, Context *ctx) } } -inline Bool __qmljs_cmp_ge(Value left, Value right, Context *ctx) +inline Bool __qmljs_cmp_ge(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -1124,7 +1124,7 @@ inline Bool __qmljs_cmp_ge(Value left, Value right, Context *ctx) } } -inline Bool __qmljs_cmp_le(Value left, Value right, Context *ctx) +inline Bool __qmljs_cmp_le(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -1144,7 +1144,7 @@ inline Bool __qmljs_cmp_le(Value left, Value right, Context *ctx) } } -inline Bool __qmljs_cmp_eq(Value left, Value right, Context *ctx) +inline Bool __qmljs_cmp_eq(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -1159,28 +1159,28 @@ inline Bool __qmljs_cmp_eq(Value left, Value right, Context *ctx) return __qmljs_equal(left, right, ctx); } -inline Bool __qmljs_cmp_ne(Value left, Value right, Context *ctx) +inline Bool __qmljs_cmp_ne(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); return !__qmljs_cmp_eq(left, right, ctx); } -inline Bool __qmljs_cmp_se(Value left, Value right, Context *) +inline Bool __qmljs_cmp_se(Value left, Value right, ExecutionContext *) { TRACE2(left, right); return __qmljs_strict_equal(left, right); } -inline Bool __qmljs_cmp_sne(Value left, Value right, Context *) +inline Bool __qmljs_cmp_sne(Value left, Value right, ExecutionContext *) { TRACE2(left, right); return ! __qmljs_strict_equal(left, right); } -inline Bool __qmljs_cmp_instanceof(Value left, Value right, Context *ctx) +inline Bool __qmljs_cmp_instanceof(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -1188,7 +1188,7 @@ inline Bool __qmljs_cmp_instanceof(Value left, Value right, Context *ctx) return v.booleanValue(); } -inline uint __qmljs_cmp_in(Value left, Value right, Context *ctx) +inline uint __qmljs_cmp_in(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); diff --git a/qv4array_p.h b/qv4array_p.h index 2bce001c53..20022affe7 100644 --- a/qv4array_p.h +++ b/qv4array_p.h @@ -68,7 +68,7 @@ public: inline void concat(const Array &other); inline Value pop(); inline Value takeFirst(); - inline void sort(Context *context, const Value &comparefn); + inline void sort(ExecutionContext *context, const Value &comparefn); inline void splice(double start, double deleteCount, const QVector &items, Array &other); @@ -81,13 +81,13 @@ private: class ArrayElementLessThan { public: - inline ArrayElementLessThan(Context *context, const Value &comparefn) + inline ArrayElementLessThan(ExecutionContext *context, const Value &comparefn) : m_context(context), m_comparefn(comparefn) {} bool operator()(const Value &v1, const Value &v2) const; private: - Context *m_context; + ExecutionContext *m_context; Value m_comparefn; }; @@ -183,7 +183,7 @@ inline Value Array::takeFirst() return v; } -inline void Array::sort(Context *context, const Value &comparefn) +inline void Array::sort(ExecutionContext *context, const Value &comparefn) { ArrayElementLessThan lessThan(context, comparefn); std::sort(to_vector->begin(), to_vector->end(), lessThan); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index c4d75fe2c9..652fc4525a 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -523,29 +523,29 @@ static double getLocalTZA() // // Object // -ObjectCtor::ObjectCtor(Context *scope) +ObjectCtor::ObjectCtor(ExecutionContext *scope) : FunctionObject(scope) { } -void ObjectCtor::construct(Context *ctx) +void ObjectCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(ctx->engine->newObject()); } -void ObjectCtor::call(Context *ctx) +void ObjectCtor::call(ExecutionContext *ctx) { ctx->result = Value::fromObject(ctx->engine->newObject()); } -Value ObjectCtor::__get__(Context *ctx, String *name) +Value ObjectCtor::__get__(ExecutionContext *ctx, String *name) { if (name == ctx->engine->id_length) return Value::fromDouble(1); return Object::__get__(ctx, name); } -void ObjectPrototype::init(Context *ctx, const Value &ctor) +void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); ctor.objectValue()->__put__(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 0); @@ -571,7 +571,7 @@ void ObjectPrototype::init(Context *ctx, const Value &ctor) __put__(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 0); } -void ObjectPrototype::method_getPrototypeOf(Context *ctx) +void ObjectPrototype::method_getPrototypeOf(ExecutionContext *ctx) { Value o = ctx->argument(0); if (! o.isObject()) { @@ -581,12 +581,12 @@ void ObjectPrototype::method_getPrototypeOf(Context *ctx) } } -void ObjectPrototype::method_getOwnPropertyDescriptor(Context *ctx) +void ObjectPrototype::method_getOwnPropertyDescriptor(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.getOwnPropertyDescriptors")); } -void ObjectPrototype::method_getOwnPropertyNames(Context *ctx) +void ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) { Value O = ctx->argument(0); if (! O.isObject()) @@ -605,57 +605,57 @@ void ObjectPrototype::method_getOwnPropertyNames(Context *ctx) } } -void ObjectPrototype::method_create(Context *ctx) +void ObjectPrototype::method_create(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.create")); } -void ObjectPrototype::method_defineProperty(Context *ctx) +void ObjectPrototype::method_defineProperty(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.defineProperty")); } -void ObjectPrototype::method_defineProperties(Context *ctx) +void ObjectPrototype::method_defineProperties(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.defineProperties")); } -void ObjectPrototype::method_seal(Context *ctx) +void ObjectPrototype::method_seal(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.seal")); } -void ObjectPrototype::method_freeze(Context *ctx) +void ObjectPrototype::method_freeze(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.freeze")); } -void ObjectPrototype::method_preventExtensions(Context *ctx) +void ObjectPrototype::method_preventExtensions(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.preventExtensions")); } -void ObjectPrototype::method_isSealed(Context *ctx) +void ObjectPrototype::method_isSealed(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.isSealed")); } -void ObjectPrototype::method_isFrozen(Context *ctx) +void ObjectPrototype::method_isFrozen(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.isFrozen")); } -void ObjectPrototype::method_isExtensible(Context *ctx) +void ObjectPrototype::method_isExtensible(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.isExtensible")); } -void ObjectPrototype::method_keys(Context *ctx) +void ObjectPrototype::method_keys(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.keys")); } -void ObjectPrototype::method_toString(Context *ctx) +void ObjectPrototype::method_toString(ExecutionContext *ctx) { if (! ctx->thisObject.isObject()) ctx->throwTypeError(); @@ -663,18 +663,18 @@ void ObjectPrototype::method_toString(Context *ctx) ctx->result = Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(ctx->thisObject.objectValue()->className())); } -void ObjectPrototype::method_toLocaleString(Context *ctx) +void ObjectPrototype::method_toLocaleString(ExecutionContext *ctx) { method_toString(ctx); } -void ObjectPrototype::method_valueOf(Context *ctx) +void ObjectPrototype::method_valueOf(ExecutionContext *ctx) { Value o = ctx->thisObject.toObject(ctx); ctx->result = o; } -void ObjectPrototype::method_hasOwnProperty(Context *ctx) +void ObjectPrototype::method_hasOwnProperty(ExecutionContext *ctx) { String *P = ctx->argument(0).toString(ctx); Value O = ctx->thisObject.toObject(ctx); @@ -682,7 +682,7 @@ void ObjectPrototype::method_hasOwnProperty(Context *ctx) ctx->result = Value::fromBoolean(r); } -void ObjectPrototype::method_isPrototypeOf(Context *ctx) +void ObjectPrototype::method_isPrototypeOf(ExecutionContext *ctx) { Value V = ctx->argument(0); if (! V.isObject()) @@ -694,7 +694,7 @@ void ObjectPrototype::method_isPrototypeOf(Context *ctx) } } -void ObjectPrototype::method_propertyIsEnumerable(Context *ctx) +void ObjectPrototype::method_propertyIsEnumerable(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.prototype.propertyIsEnumerable")); } @@ -702,12 +702,12 @@ void ObjectPrototype::method_propertyIsEnumerable(Context *ctx) // // String // -StringCtor::StringCtor(Context *scope) +StringCtor::StringCtor(ExecutionContext *scope) : FunctionObject(scope) { } -void StringCtor::construct(Context *ctx) +void StringCtor::construct(ExecutionContext *ctx) { Value value; if (ctx->argumentCount) @@ -717,7 +717,7 @@ void StringCtor::construct(Context *ctx) ctx->thisObject = Value::fromObject(ctx->engine->newStringObject(value)); } -void StringCtor::call(Context *ctx) +void StringCtor::call(ExecutionContext *ctx) { const Value arg = ctx->argument(0); if (arg.isUndefined()) @@ -726,7 +726,7 @@ void StringCtor::call(Context *ctx) ctx->result = __qmljs_to_string(arg, ctx); } -void StringPrototype::init(Context *ctx, const Value &ctor) +void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); ctor.objectValue()->__put__(ctx, QStringLiteral("fromCharCode"), method_fromCharCode); @@ -753,7 +753,7 @@ void StringPrototype::init(Context *ctx, const Value &ctor) __put__(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); } -QString StringPrototype::getThisString(Context *ctx) +QString StringPrototype::getThisString(ExecutionContext *ctx) { if (StringObject *thisObject = ctx->thisObject.asStringObject()) { return thisObject->value.stringValue()->toQString(); @@ -763,7 +763,7 @@ QString StringPrototype::getThisString(Context *ctx) } } -void StringPrototype::method_toString(Context *ctx) +void StringPrototype::method_toString(ExecutionContext *ctx) { if (StringObject *o = ctx->thisObject.asStringObject()) { ctx->result = o->value; @@ -772,7 +772,7 @@ void StringPrototype::method_toString(Context *ctx) } } -void StringPrototype::method_valueOf(Context *ctx) +void StringPrototype::method_valueOf(ExecutionContext *ctx) { if (StringObject *o = ctx->thisObject.asStringObject()) { ctx->result = o->value; @@ -781,7 +781,7 @@ void StringPrototype::method_valueOf(Context *ctx) } } -void StringPrototype::method_charAt(Context *ctx) +void StringPrototype::method_charAt(ExecutionContext *ctx) { const QString str = getThisString(ctx); @@ -796,7 +796,7 @@ void StringPrototype::method_charAt(Context *ctx) ctx->result = Value::fromString(ctx, result); } -void StringPrototype::method_charCodeAt(Context *ctx) +void StringPrototype::method_charCodeAt(ExecutionContext *ctx) { const QString str = getThisString(ctx); @@ -812,7 +812,7 @@ void StringPrototype::method_charCodeAt(Context *ctx) ctx->result = Value::fromDouble(result); } -void StringPrototype::method_concat(Context *ctx) +void StringPrototype::method_concat(ExecutionContext *ctx) { QString value = getThisString(ctx); @@ -825,7 +825,7 @@ void StringPrototype::method_concat(Context *ctx) ctx->result = Value::fromString(ctx, value); } -void StringPrototype::method_indexOf(Context *ctx) +void StringPrototype::method_indexOf(ExecutionContext *ctx) { QString value = getThisString(ctx); @@ -844,7 +844,7 @@ void StringPrototype::method_indexOf(Context *ctx) ctx->result = Value::fromDouble(index); } -void StringPrototype::method_lastIndexOf(Context *ctx) +void StringPrototype::method_lastIndexOf(ExecutionContext *ctx) { const QString value = getThisString(ctx); @@ -868,32 +868,32 @@ void StringPrototype::method_lastIndexOf(Context *ctx) ctx->result = Value::fromDouble(index); } -void StringPrototype::method_localeCompare(Context *ctx) +void StringPrototype::method_localeCompare(ExecutionContext *ctx) { const QString value = getThisString(ctx); const QString that = ctx->argument(0).toString(ctx)->toQString(); ctx->result = Value::fromDouble(QString::localeAwareCompare(value, that)); } -void StringPrototype::method_match(Context *ctx) +void StringPrototype::method_match(ExecutionContext *ctx) { // requires Regexp ctx->throwUnimplemented(QStringLiteral("String.prototype.match")); } -void StringPrototype::method_replace(Context *ctx) +void StringPrototype::method_replace(ExecutionContext *ctx) { // requires Regexp ctx->throwUnimplemented(QStringLiteral("String.prototype.replace")); } -void StringPrototype::method_search(Context *ctx) +void StringPrototype::method_search(ExecutionContext *ctx) { // requires Regexp ctx->throwUnimplemented(QStringLiteral("String.prototype.search")); } -void StringPrototype::method_slice(Context *ctx) +void StringPrototype::method_slice(ExecutionContext *ctx) { const QString text = getThisString(ctx); const int length = text.length(); @@ -916,12 +916,12 @@ void StringPrototype::method_slice(Context *ctx) ctx->result = Value::fromString(ctx, text.mid(start, count)); } -void StringPrototype::method_split(Context *ctx) +void StringPrototype::method_split(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("String.prototype.splt")); } -void StringPrototype::method_substr(Context *ctx) +void StringPrototype::method_substr(ExecutionContext *ctx) { const QString value = getThisString(ctx); @@ -944,7 +944,7 @@ void StringPrototype::method_substr(Context *ctx) ctx->result = Value::fromString(ctx, value.mid(x, y)); } -void StringPrototype::method_substring(Context *ctx) +void StringPrototype::method_substring(ExecutionContext *ctx) { QString value = getThisString(ctx); int length = value.length(); @@ -981,29 +981,29 @@ void StringPrototype::method_substring(Context *ctx) ctx->result = Value::fromString(ctx, value.mid(x, y)); } -void StringPrototype::method_toLowerCase(Context *ctx) +void StringPrototype::method_toLowerCase(ExecutionContext *ctx) { QString value = getThisString(ctx); ctx->result = Value::fromString(ctx, value.toLower()); } -void StringPrototype::method_toLocaleLowerCase(Context *ctx) +void StringPrototype::method_toLocaleLowerCase(ExecutionContext *ctx) { method_toLowerCase(ctx); } -void StringPrototype::method_toUpperCase(Context *ctx) +void StringPrototype::method_toUpperCase(ExecutionContext *ctx) { QString value = getThisString(ctx); ctx->result = Value::fromString(ctx, value.toUpper()); } -void StringPrototype::method_toLocaleUpperCase(Context *ctx) +void StringPrototype::method_toLocaleUpperCase(ExecutionContext *ctx) { method_toUpperCase(ctx); } -void StringPrototype::method_fromCharCode(Context *ctx) +void StringPrototype::method_fromCharCode(ExecutionContext *ctx) { QString str; for (unsigned i = 0; i < ctx->argumentCount; ++i) { @@ -1016,24 +1016,24 @@ void StringPrototype::method_fromCharCode(Context *ctx) // // Number object // -NumberCtor::NumberCtor(Context *scope) +NumberCtor::NumberCtor(ExecutionContext *scope) : FunctionObject(scope) { } -void NumberCtor::construct(Context *ctx) +void NumberCtor::construct(ExecutionContext *ctx) { const double n = ctx->argument(0).toNumber(ctx); ctx->thisObject = Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(n))); } -void NumberCtor::call(Context *ctx) +void NumberCtor::call(ExecutionContext *ctx) { double value = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; ctx->result = Value::fromDouble(value); } -void NumberPrototype::init(Context *ctx, const Value &ctor) +void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); ctor.objectValue()->__put__(ctx, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); @@ -1058,7 +1058,7 @@ void NumberPrototype::init(Context *ctx, const Value &ctor) __put__(ctx, QStringLiteral("toPrecision"), method_toPrecision); } -void NumberPrototype::method_toString(Context *ctx) +void NumberPrototype::method_toString(ExecutionContext *ctx) { if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { Value arg = ctx->argument(0); @@ -1119,7 +1119,7 @@ void NumberPrototype::method_toString(Context *ctx) } } -void NumberPrototype::method_toLocaleString(Context *ctx) +void NumberPrototype::method_toLocaleString(ExecutionContext *ctx) { if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { String *str = thisObject->value.toString(ctx); @@ -1129,7 +1129,7 @@ void NumberPrototype::method_toLocaleString(Context *ctx) } } -void NumberPrototype::method_valueOf(Context *ctx) +void NumberPrototype::method_valueOf(ExecutionContext *ctx) { if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { ctx->result = thisObject->value; @@ -1138,7 +1138,7 @@ void NumberPrototype::method_valueOf(Context *ctx) } } -void NumberPrototype::method_toFixed(Context *ctx) +void NumberPrototype::method_toFixed(ExecutionContext *ctx) { if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { double fdigits = 0; @@ -1163,7 +1163,7 @@ void NumberPrototype::method_toFixed(Context *ctx) } } -void NumberPrototype::method_toExponential(Context *ctx) +void NumberPrototype::method_toExponential(ExecutionContext *ctx) { if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { double fdigits = 0; @@ -1178,7 +1178,7 @@ void NumberPrototype::method_toExponential(Context *ctx) } } -void NumberPrototype::method_toPrecision(Context *ctx) +void NumberPrototype::method_toPrecision(ExecutionContext *ctx) { if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { double fdigits = 0; @@ -1195,24 +1195,24 @@ void NumberPrototype::method_toPrecision(Context *ctx) // // Boolean object // -BooleanCtor::BooleanCtor(Context *scope) +BooleanCtor::BooleanCtor(ExecutionContext *scope) : FunctionObject(scope) { } -void BooleanCtor::construct(Context *ctx) +void BooleanCtor::construct(ExecutionContext *ctx) { const double n = ctx->argument(0).toBoolean(ctx); ctx->thisObject = Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n))); } -void BooleanCtor::call(Context *ctx) +void BooleanCtor::call(ExecutionContext *ctx) { bool value = ctx->argumentCount ? ctx->argument(0).toBoolean(ctx) : 0; ctx->result = Value::fromBoolean(value); } -void BooleanPrototype::init(Context *ctx, const Value &ctor) +void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); __put__(ctx, QStringLiteral("constructor"), ctor); @@ -1220,7 +1220,7 @@ void BooleanPrototype::init(Context *ctx, const Value &ctor) __put__(ctx, QStringLiteral("valueOf"), method_valueOf); } -void BooleanPrototype::method_toString(Context *ctx) +void BooleanPrototype::method_toString(ExecutionContext *ctx) { if (BooleanObject *thisObject = ctx->thisObject.asBooleanObject()) { ctx->result = Value::fromString(ctx, QLatin1String(thisObject->value.booleanValue() ? "true" : "false")); @@ -1229,7 +1229,7 @@ void BooleanPrototype::method_toString(Context *ctx) } } -void BooleanPrototype::method_valueOf(Context *ctx) +void BooleanPrototype::method_valueOf(ExecutionContext *ctx) { if (BooleanObject *thisObject = ctx->thisObject.asBooleanObject()) { ctx->result = thisObject->value; @@ -1241,18 +1241,18 @@ void BooleanPrototype::method_valueOf(Context *ctx) // // Array object // -ArrayCtor::ArrayCtor(Context *scope) +ArrayCtor::ArrayCtor(ExecutionContext *scope) : FunctionObject(scope) { } -void ArrayCtor::construct(Context *ctx) +void ArrayCtor::construct(ExecutionContext *ctx) { call(ctx); ctx->thisObject = ctx->result; } -void ArrayCtor::call(Context *ctx) +void ArrayCtor::call(ExecutionContext *ctx) { Array value; if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { @@ -1274,7 +1274,7 @@ void ArrayCtor::call(Context *ctx) ctx->result = Value::fromObject(ctx->engine->newArrayObject(value)); } -void ArrayPrototype::init(Context *ctx, const Value &ctor) +void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); __put__(ctx, QStringLiteral("constructor"), ctor); @@ -1301,17 +1301,17 @@ void ArrayPrototype::init(Context *ctx, const Value &ctor) __put__(ctx, QStringLiteral("reduceRight"), method_reduceRight, 0); } -void ArrayPrototype::method_toString(Context *ctx) +void ArrayPrototype::method_toString(ExecutionContext *ctx) { method_join(ctx); } -void ArrayPrototype::method_toLocaleString(Context *ctx) +void ArrayPrototype::method_toLocaleString(ExecutionContext *ctx) { method_toString(ctx); } -void ArrayPrototype::method_concat(Context *ctx) +void ArrayPrototype::method_concat(ExecutionContext *ctx) { Array result; @@ -1336,7 +1336,7 @@ void ArrayPrototype::method_concat(Context *ctx) ctx->result = Value::fromObject(ctx->engine->newArrayObject(result)); } -void ArrayPrototype::method_join(Context *ctx) +void ArrayPrototype::method_join(ExecutionContext *ctx) { Value arg = ctx->argument(0); @@ -1394,7 +1394,7 @@ void ArrayPrototype::method_join(Context *ctx) ctx->result = Value::fromString(ctx, R); } -void ArrayPrototype::method_pop(Context *ctx) +void ArrayPrototype::method_pop(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { @@ -1415,7 +1415,7 @@ void ArrayPrototype::method_pop(Context *ctx) } } -void ArrayPrototype::method_push(Context *ctx) +void ArrayPrototype::method_push(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { @@ -1439,7 +1439,7 @@ void ArrayPrototype::method_push(Context *ctx) } } -void ArrayPrototype::method_reverse(Context *ctx) +void ArrayPrototype::method_reverse(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { @@ -1455,7 +1455,7 @@ void ArrayPrototype::method_reverse(Context *ctx) } } -void ArrayPrototype::method_shift(Context *ctx) +void ArrayPrototype::method_shift(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { @@ -1465,7 +1465,7 @@ void ArrayPrototype::method_shift(Context *ctx) } } -void ArrayPrototype::method_slice(Context *ctx) +void ArrayPrototype::method_slice(ExecutionContext *ctx) { // ### TODO implement the fast non-generic version of slice. @@ -1491,7 +1491,7 @@ void ArrayPrototype::method_slice(Context *ctx) ctx->result = Value::fromObject(ctx->engine->newArrayObject(result)); } -void ArrayPrototype::method_sort(Context *ctx) +void ArrayPrototype::method_sort(ExecutionContext *ctx) { Value self = ctx->thisObject; Value comparefn = ctx->argument(0); @@ -1503,7 +1503,7 @@ void ArrayPrototype::method_sort(Context *ctx) } } -void ArrayPrototype::method_splice(Context *ctx) +void ArrayPrototype::method_splice(ExecutionContext *ctx) { if (ctx->argumentCount < 2) return; @@ -1525,22 +1525,22 @@ void ArrayPrototype::method_splice(Context *ctx) } } -void ArrayPrototype::method_unshift(Context *ctx) +void ArrayPrototype::method_unshift(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); } -void ArrayPrototype::method_indexOf(Context *ctx) +void ArrayPrototype::method_indexOf(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); } -void ArrayPrototype::method_lastIndexOf(Context *ctx) +void ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); } -void ArrayPrototype::method_every(Context *ctx) +void ArrayPrototype::method_every(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { @@ -1565,7 +1565,7 @@ void ArrayPrototype::method_every(Context *ctx) } } -void ArrayPrototype::method_some(Context *ctx) +void ArrayPrototype::method_some(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { @@ -1590,7 +1590,7 @@ void ArrayPrototype::method_some(Context *ctx) } } -void ArrayPrototype::method_forEach(Context *ctx) +void ArrayPrototype::method_forEach(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { @@ -1611,7 +1611,7 @@ void ArrayPrototype::method_forEach(Context *ctx) } } -void ArrayPrototype::method_map(Context *ctx) +void ArrayPrototype::method_map(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { @@ -1636,7 +1636,7 @@ void ArrayPrototype::method_map(Context *ctx) } } -void ArrayPrototype::method_filter(Context *ctx) +void ArrayPrototype::method_filter(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { @@ -1664,7 +1664,7 @@ void ArrayPrototype::method_filter(Context *ctx) } } -void ArrayPrototype::method_reduce(Context *ctx) +void ArrayPrototype::method_reduce(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { @@ -1695,7 +1695,7 @@ void ArrayPrototype::method_reduce(Context *ctx) } } -void ArrayPrototype::method_reduceRight(Context *ctx) +void ArrayPrototype::method_reduceRight(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { @@ -1729,13 +1729,13 @@ void ArrayPrototype::method_reduceRight(Context *ctx) // // Function object // -FunctionCtor::FunctionCtor(Context *scope) +FunctionCtor::FunctionCtor(ExecutionContext *scope) : FunctionObject(scope) { } // 15.3.2 -void FunctionCtor::construct(Context *ctx) +void FunctionCtor::construct(ExecutionContext *ctx) { QString args; QString body; @@ -1780,7 +1780,7 @@ void FunctionCtor::construct(Context *ctx) } // 15.3.1: This is equivalent to new Function(...) -void FunctionCtor::call(Context *ctx) +void FunctionCtor::call(ExecutionContext *ctx) { Value v = ctx->thisObject; construct(ctx); @@ -1788,7 +1788,7 @@ void FunctionCtor::call(Context *ctx) ctx->thisObject = v; } -void FunctionPrototype::init(Context *ctx, const Value &ctor) +void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); __put__(ctx, QStringLiteral("constructor"), ctor); @@ -1798,7 +1798,7 @@ void FunctionPrototype::init(Context *ctx, const Value &ctor) __put__(ctx, QStringLiteral("bind"), method_bind, 0); } -void FunctionPrototype::method_toString(Context *ctx) +void FunctionPrototype::method_toString(ExecutionContext *ctx) { if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { Q_UNUSED(fun); @@ -1808,7 +1808,7 @@ void FunctionPrototype::method_toString(Context *ctx) } } -void FunctionPrototype::method_apply(Context *ctx) +void FunctionPrototype::method_apply(ExecutionContext *ctx) { Value thisObject = ctx->argument(0).toObject(ctx); if (thisObject.isNull() || thisObject.isUndefined()) @@ -1832,7 +1832,7 @@ void FunctionPrototype::method_apply(Context *ctx) ctx->result = __qmljs_call_value(ctx, thisObject, ctx->thisObject, args.data(), args.size()); } -void FunctionPrototype::method_call(Context *ctx) +void FunctionPrototype::method_call(ExecutionContext *ctx) { Value thisArg = ctx->argument(0); QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); @@ -1841,7 +1841,7 @@ void FunctionPrototype::method_call(Context *ctx) ctx->result = __qmljs_call_value(ctx, thisArg, ctx->thisObject, args.data(), args.size()); } -void FunctionPrototype::method_bind(Context *ctx) +void FunctionPrototype::method_bind(ExecutionContext *ctx) { if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { Q_UNUSED(fun); @@ -1854,12 +1854,12 @@ void FunctionPrototype::method_bind(Context *ctx) // // Date object // -DateCtor::DateCtor(Context *scope) +DateCtor::DateCtor(ExecutionContext *scope) : FunctionObject(scope) { } -void DateCtor::construct(Context *ctx) +void DateCtor::construct(ExecutionContext *ctx) { double t = 0; @@ -1897,13 +1897,13 @@ void DateCtor::construct(Context *ctx) ctx->thisObject = Value::fromObject(d); } -void DateCtor::call(Context *ctx) +void DateCtor::call(ExecutionContext *ctx) { double t = currentTime(); ctx->result = Value::fromString(ctx, ToString(t)); } -void DatePrototype::init(Context *ctx, const Value &ctor) +void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); LocalTZA = getLocalTZA(); @@ -1958,7 +1958,7 @@ void DatePrototype::init(Context *ctx, const Value &ctor) __put__(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); } -double DatePrototype::getThisDate(Context *ctx) +double DatePrototype::getThisDate(ExecutionContext *ctx) { if (DateObject *thisObject = ctx->thisObject.asDateObject()) return thisObject->value.asDouble(); @@ -1968,27 +1968,27 @@ double DatePrototype::getThisDate(Context *ctx) } } -void DatePrototype::method_MakeTime(Context *ctx) +void DatePrototype::method_MakeTime(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Data.MakeTime")); } -void DatePrototype::method_MakeDate(Context *ctx) +void DatePrototype::method_MakeDate(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Data.MakeDate")); } -void DatePrototype::method_TimeClip(Context *ctx) +void DatePrototype::method_TimeClip(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Data.TimeClip")); } -void DatePrototype::method_parse(Context *ctx) +void DatePrototype::method_parse(ExecutionContext *ctx) { ctx->result = Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString())); } -void DatePrototype::method_UTC(Context *ctx) +void DatePrototype::method_UTC(ExecutionContext *ctx) { const int numArgs = ctx->argumentCount; if (numArgs >= 2) { @@ -2007,55 +2007,55 @@ void DatePrototype::method_UTC(Context *ctx) } } -void DatePrototype::method_toString(Context *ctx) +void DatePrototype::method_toString(ExecutionContext *ctx) { double t = getThisDate(ctx); ctx->result = Value::fromString(ctx, ToString(t)); } -void DatePrototype::method_toDateString(Context *ctx) +void DatePrototype::method_toDateString(ExecutionContext *ctx) { double t = getThisDate(ctx); ctx->result = Value::fromString(ctx, ToDateString(t)); } -void DatePrototype::method_toTimeString(Context *ctx) +void DatePrototype::method_toTimeString(ExecutionContext *ctx) { double t = getThisDate(ctx); ctx->result = Value::fromString(ctx, ToTimeString(t)); } -void DatePrototype::method_toLocaleString(Context *ctx) +void DatePrototype::method_toLocaleString(ExecutionContext *ctx) { double t = getThisDate(ctx); ctx->result = Value::fromString(ctx, ToLocaleString(t)); } -void DatePrototype::method_toLocaleDateString(Context *ctx) +void DatePrototype::method_toLocaleDateString(ExecutionContext *ctx) { double t = getThisDate(ctx); ctx->result = Value::fromString(ctx, ToLocaleDateString(t)); } -void DatePrototype::method_toLocaleTimeString(Context *ctx) +void DatePrototype::method_toLocaleTimeString(ExecutionContext *ctx) { double t = getThisDate(ctx); ctx->result = Value::fromString(ctx, ToLocaleTimeString(t)); } -void DatePrototype::method_valueOf(Context *ctx) +void DatePrototype::method_valueOf(ExecutionContext *ctx) { double t = getThisDate(ctx); ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getTime(Context *ctx) +void DatePrototype::method_getTime(ExecutionContext *ctx) { double t = getThisDate(ctx); ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getYear(Context *ctx) +void DatePrototype::method_getYear(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2063,7 +2063,7 @@ void DatePrototype::method_getYear(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getFullYear(Context *ctx) +void DatePrototype::method_getFullYear(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2071,7 +2071,7 @@ void DatePrototype::method_getFullYear(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getUTCFullYear(Context *ctx) +void DatePrototype::method_getUTCFullYear(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2079,7 +2079,7 @@ void DatePrototype::method_getUTCFullYear(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getMonth(Context *ctx) +void DatePrototype::method_getMonth(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2087,7 +2087,7 @@ void DatePrototype::method_getMonth(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getUTCMonth(Context *ctx) +void DatePrototype::method_getUTCMonth(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2095,7 +2095,7 @@ void DatePrototype::method_getUTCMonth(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getDate(Context *ctx) +void DatePrototype::method_getDate(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2103,7 +2103,7 @@ void DatePrototype::method_getDate(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getUTCDate(Context *ctx) +void DatePrototype::method_getUTCDate(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2111,7 +2111,7 @@ void DatePrototype::method_getUTCDate(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getDay(Context *ctx) +void DatePrototype::method_getDay(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2119,7 +2119,7 @@ void DatePrototype::method_getDay(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getUTCDay(Context *ctx) +void DatePrototype::method_getUTCDay(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2127,7 +2127,7 @@ void DatePrototype::method_getUTCDay(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getHours(Context *ctx) +void DatePrototype::method_getHours(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2135,7 +2135,7 @@ void DatePrototype::method_getHours(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getUTCHours(Context *ctx) +void DatePrototype::method_getUTCHours(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2143,7 +2143,7 @@ void DatePrototype::method_getUTCHours(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getMinutes(Context *ctx) +void DatePrototype::method_getMinutes(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2151,7 +2151,7 @@ void DatePrototype::method_getMinutes(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getUTCMinutes(Context *ctx) +void DatePrototype::method_getUTCMinutes(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2159,7 +2159,7 @@ void DatePrototype::method_getUTCMinutes(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getSeconds(Context *ctx) +void DatePrototype::method_getSeconds(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2167,7 +2167,7 @@ void DatePrototype::method_getSeconds(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getUTCSeconds(Context *ctx) +void DatePrototype::method_getUTCSeconds(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2175,7 +2175,7 @@ void DatePrototype::method_getUTCSeconds(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getMilliseconds(Context *ctx) +void DatePrototype::method_getMilliseconds(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2183,7 +2183,7 @@ void DatePrototype::method_getMilliseconds(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getUTCMilliseconds(Context *ctx) +void DatePrototype::method_getUTCMilliseconds(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2191,7 +2191,7 @@ void DatePrototype::method_getUTCMilliseconds(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_getTimezoneOffset(Context *ctx) +void DatePrototype::method_getTimezoneOffset(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) @@ -2199,7 +2199,7 @@ void DatePrototype::method_getTimezoneOffset(Context *ctx) ctx->result = Value::fromDouble(t); } -void DatePrototype::method_setTime(Context *ctx) +void DatePrototype::method_setTime(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { self->value.setDouble(TimeClip(ctx->argument(0).toNumber(ctx))); @@ -2209,7 +2209,7 @@ void DatePrototype::method_setTime(Context *ctx) } } -void DatePrototype::method_setMilliseconds(Context *ctx) +void DatePrototype::method_setMilliseconds(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.asDouble()); @@ -2221,7 +2221,7 @@ void DatePrototype::method_setMilliseconds(Context *ctx) } } -void DatePrototype::method_setUTCMilliseconds(Context *ctx) +void DatePrototype::method_setUTCMilliseconds(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.asDouble(); @@ -2233,7 +2233,7 @@ void DatePrototype::method_setUTCMilliseconds(Context *ctx) } } -void DatePrototype::method_setSeconds(Context *ctx) +void DatePrototype::method_setSeconds(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.asDouble()); @@ -2247,7 +2247,7 @@ void DatePrototype::method_setSeconds(Context *ctx) } } -void DatePrototype::method_setUTCSeconds(Context *ctx) +void DatePrototype::method_setUTCSeconds(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.asDouble(); @@ -2261,7 +2261,7 @@ void DatePrototype::method_setUTCSeconds(Context *ctx) } } -void DatePrototype::method_setMinutes(Context *ctx) +void DatePrototype::method_setMinutes(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.asDouble()); @@ -2276,7 +2276,7 @@ void DatePrototype::method_setMinutes(Context *ctx) } } -void DatePrototype::method_setUTCMinutes(Context *ctx) +void DatePrototype::method_setUTCMinutes(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.asDouble(); @@ -2291,7 +2291,7 @@ void DatePrototype::method_setUTCMinutes(Context *ctx) } } -void DatePrototype::method_setHours(Context *ctx) +void DatePrototype::method_setHours(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.asDouble()); @@ -2307,7 +2307,7 @@ void DatePrototype::method_setHours(Context *ctx) } } -void DatePrototype::method_setUTCHours(Context *ctx) +void DatePrototype::method_setUTCHours(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.asDouble(); @@ -2323,7 +2323,7 @@ void DatePrototype::method_setUTCHours(Context *ctx) } } -void DatePrototype::method_setDate(Context *ctx) +void DatePrototype::method_setDate(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.asDouble()); @@ -2336,7 +2336,7 @@ void DatePrototype::method_setDate(Context *ctx) } } -void DatePrototype::method_setUTCDate(Context *ctx) +void DatePrototype::method_setUTCDate(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.asDouble(); @@ -2349,7 +2349,7 @@ void DatePrototype::method_setUTCDate(Context *ctx) } } -void DatePrototype::method_setMonth(Context *ctx) +void DatePrototype::method_setMonth(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.asDouble()); @@ -2363,7 +2363,7 @@ void DatePrototype::method_setMonth(Context *ctx) } } -void DatePrototype::method_setUTCMonth(Context *ctx) +void DatePrototype::method_setUTCMonth(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.asDouble(); @@ -2377,7 +2377,7 @@ void DatePrototype::method_setUTCMonth(Context *ctx) } } -void DatePrototype::method_setYear(Context *ctx) +void DatePrototype::method_setYear(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.asDouble(); @@ -2403,7 +2403,7 @@ void DatePrototype::method_setYear(Context *ctx) } } -void DatePrototype::method_setUTCFullYear(Context *ctx) +void DatePrototype::method_setUTCFullYear(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.asDouble(); @@ -2418,7 +2418,7 @@ void DatePrototype::method_setUTCFullYear(Context *ctx) } } -void DatePrototype::method_setFullYear(Context *ctx) +void DatePrototype::method_setFullYear(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.asDouble()); @@ -2433,7 +2433,7 @@ void DatePrototype::method_setFullYear(Context *ctx) } } -void DatePrototype::method_toUTCString(Context *ctx) +void DatePrototype::method_toUTCString(ExecutionContext *ctx) { if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.asDouble(); @@ -2444,12 +2444,12 @@ void DatePrototype::method_toUTCString(Context *ctx) // // RegExp object // -RegExpCtor::RegExpCtor(Context *scope) +RegExpCtor::RegExpCtor(ExecutionContext *scope) : FunctionObject(scope) { } -void RegExpCtor::construct(Context *ctx) +void RegExpCtor::construct(ExecutionContext *ctx) { // if (ctx->argumentCount > 2) { // ctx->throwTypeError(); @@ -2499,7 +2499,7 @@ void RegExpCtor::construct(Context *ctx) ctx->thisObject = Value::fromObject(new RegExpObject(re, global)); } -void RegExpCtor::call(Context *ctx) +void RegExpCtor::call(ExecutionContext *ctx) { if (ctx->argumentCount > 0 && ctx->argument(0).asRegExpObject()) { if (ctx->argumentCount == 1 || ctx->argument(1).isUndefined()) { @@ -2513,7 +2513,7 @@ void RegExpCtor::call(Context *ctx) ctx->thisObject = that; } -void RegExpPrototype::init(Context *ctx, const Value &ctor) +void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); __put__(ctx, QStringLiteral("constructor"), ctor); @@ -2522,7 +2522,7 @@ void RegExpPrototype::init(Context *ctx, const Value &ctor) __put__(ctx, QStringLiteral("toString"), method_toString, 0); } -void RegExpPrototype::method_exec(Context *ctx) +void RegExpPrototype::method_exec(ExecutionContext *ctx) { if (RegExpObject *r = ctx->thisObject.asRegExpObject()) { Value arg = ctx->argument(0); @@ -2559,13 +2559,13 @@ void RegExpPrototype::method_exec(Context *ctx) } } -void RegExpPrototype::method_test(Context *ctx) +void RegExpPrototype::method_test(ExecutionContext *ctx) { method_exec(ctx); ctx->result = Value::fromBoolean(!ctx->result.isNull()); } -void RegExpPrototype::method_toString(Context *ctx) +void RegExpPrototype::method_toString(ExecutionContext *ctx) { if (RegExpObject *r = ctx->thisObject.asRegExpObject()) { QString result = QChar('/') + r->value.pattern(); @@ -2585,17 +2585,17 @@ void RegExpPrototype::method_toString(Context *ctx) // // ErrorCtr // -ErrorCtor::ErrorCtor(Context *scope) +ErrorCtor::ErrorCtor(ExecutionContext *scope) : FunctionObject(scope) { } -void ErrorCtor::construct(Context *ctx) +void ErrorCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(new ErrorObject(ctx->argument(0))); } -void ErrorCtor::call(Context *ctx) +void ErrorCtor::call(ExecutionContext *ctx) { Value that = ctx->thisObject; construct(ctx); @@ -2604,44 +2604,44 @@ void ErrorCtor::call(Context *ctx) ctx->thisObject = that; } -void EvalErrorCtor::construct(Context *ctx) +void EvalErrorCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(new EvalErrorObject(ctx)); } -void RangeErrorCtor::construct(Context *ctx) +void RangeErrorCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(new RangeErrorObject(ctx)); } -void ReferenceErrorCtor::construct(Context *ctx) +void ReferenceErrorCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(new ReferenceErrorObject(ctx)); } -void SyntaxErrorCtor::construct(Context *ctx) +void SyntaxErrorCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(new SyntaxErrorObject(ctx)); } -void TypeErrorCtor::construct(Context *ctx) +void TypeErrorCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(new TypeErrorObject(ctx)); } -void URIErrorCtor::construct(Context *ctx) +void URIErrorCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(new URIErrorObject(ctx)); } -void ErrorPrototype::init(Context *ctx, const Value &ctor, Object *obj) +void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) { ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(obj)); obj->__put__(ctx, QStringLiteral("constructor"), ctor); obj->__put__(ctx, QStringLiteral("toString"), method_toString, 0); } -void ErrorPrototype::method_toString(Context *ctx) +void ErrorPrototype::method_toString(ExecutionContext *ctx) { Object *o = ctx->thisObject.asObject(); if (!o) @@ -2677,7 +2677,7 @@ void ErrorPrototype::method_toString(Context *ctx) // // Math object // -MathObject::MathObject(Context *ctx) +MathObject::MathObject(ExecutionContext *ctx) { __put__(ctx, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); __put__(ctx, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); @@ -2720,7 +2720,7 @@ static double copySign(double x, double y) return x; } -void MathObject::method_abs(Context *ctx) +void MathObject::method_abs(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v == 0) // 0 | -0 @@ -2729,7 +2729,7 @@ void MathObject::method_abs(Context *ctx) ctx->result = Value::fromDouble(v < 0 ? -v : v); } -void MathObject::method_acos(Context *ctx) +void MathObject::method_acos(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v > 1) @@ -2738,7 +2738,7 @@ void MathObject::method_acos(Context *ctx) ctx->result = Value::fromDouble(::acos(v)); } -void MathObject::method_asin(Context *ctx) +void MathObject::method_asin(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v > 1) @@ -2747,7 +2747,7 @@ void MathObject::method_asin(Context *ctx) ctx->result = Value::fromDouble(::asin(v)); } -void MathObject::method_atan(Context *ctx) +void MathObject::method_atan(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v == 0.0) @@ -2756,7 +2756,7 @@ void MathObject::method_atan(Context *ctx) ctx->result = Value::fromDouble(::atan(v)); } -void MathObject::method_atan2(Context *ctx) +void MathObject::method_atan2(ExecutionContext *ctx) { double v1 = ctx->argument(0).toNumber(ctx); double v2 = ctx->argument(1).toNumber(ctx); @@ -2776,7 +2776,7 @@ void MathObject::method_atan2(Context *ctx) ctx->result = Value::fromDouble(::atan2(v1, v2)); } -void MathObject::method_ceil(Context *ctx) +void MathObject::method_ceil(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v < 0.0 && v > -1.0) @@ -2785,13 +2785,13 @@ void MathObject::method_ceil(Context *ctx) ctx->result = Value::fromDouble(::ceil(v)); } -void MathObject::method_cos(Context *ctx) +void MathObject::method_cos(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); ctx->result = Value::fromDouble(::cos(v)); } -void MathObject::method_exp(Context *ctx) +void MathObject::method_exp(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (qIsInf(v)) { @@ -2804,13 +2804,13 @@ void MathObject::method_exp(Context *ctx) } } -void MathObject::method_floor(Context *ctx) +void MathObject::method_floor(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); ctx->result = Value::fromDouble(::floor(v)); } -void MathObject::method_log(Context *ctx) +void MathObject::method_log(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v < 0) @@ -2819,7 +2819,7 @@ void MathObject::method_log(Context *ctx) ctx->result = Value::fromDouble(::log(v)); } -void MathObject::method_max(Context *ctx) +void MathObject::method_max(ExecutionContext *ctx) { double mx = -qInf(); for (unsigned i = 0; i < ctx->argumentCount; ++i) { @@ -2830,7 +2830,7 @@ void MathObject::method_max(Context *ctx) ctx->result = Value::fromDouble(mx); } -void MathObject::method_min(Context *ctx) +void MathObject::method_min(ExecutionContext *ctx) { double mx = qInf(); for (unsigned i = 0; i < ctx->argumentCount; ++i) { @@ -2843,7 +2843,7 @@ void MathObject::method_min(Context *ctx) ctx->result = Value::fromDouble(mx); } -void MathObject::method_pow(Context *ctx) +void MathObject::method_pow(ExecutionContext *ctx) { double x = ctx->argument(0).toNumber(ctx); double y = ctx->argument(1).toNumber(ctx); @@ -2893,31 +2893,31 @@ void MathObject::method_pow(Context *ctx) } } -void MathObject::method_random(Context *ctx) +void MathObject::method_random(ExecutionContext *ctx) { ctx->result = Value::fromDouble(qrand() / (double) RAND_MAX); } -void MathObject::method_round(Context *ctx) +void MathObject::method_round(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); v = copySign(::floor(v + 0.5), v); ctx->result = Value::fromDouble(v); } -void MathObject::method_sin(Context *ctx) +void MathObject::method_sin(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); ctx->result = Value::fromDouble(::sin(v)); } -void MathObject::method_sqrt(Context *ctx) +void MathObject::method_sqrt(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); ctx->result = Value::fromDouble(::sqrt(v)); } -void MathObject::method_tan(Context *ctx) +void MathObject::method_tan(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v == 0.0) diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 031a953562..6150cefc1a 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -49,299 +49,299 @@ namespace VM { struct ObjectCtor: FunctionObject { - ObjectCtor(Context *scope); + ObjectCtor(ExecutionContext *scope); - virtual void construct(Context *ctx); - virtual void call(Context *ctx); - virtual Value __get__(Context *ctx, String *name); + virtual void construct(ExecutionContext *ctx); + virtual void call(ExecutionContext *ctx); + virtual Value __get__(ExecutionContext *ctx, String *name); }; struct ObjectPrototype: Object { - void init(Context *ctx, const Value &ctor); - - static void method_getPrototypeOf(Context *ctx); - static void method_getOwnPropertyDescriptor(Context *ctx); - static void method_getOwnPropertyNames(Context *ctx); - static void method_create(Context *ctx); - static void method_defineProperty(Context *ctx); - static void method_defineProperties(Context *ctx); - static void method_seal(Context *ctx); - static void method_freeze(Context *ctx); - static void method_preventExtensions(Context *ctx); - static void method_isSealed(Context *ctx); - static void method_isFrozen(Context *ctx); - static void method_isExtensible(Context *ctx); - static void method_keys(Context *ctx); - - static void method_toString(Context *ctx); - static void method_toLocaleString(Context *ctx); - static void method_valueOf(Context *ctx); - static void method_hasOwnProperty(Context *ctx); - static void method_isPrototypeOf(Context *ctx); - static void method_propertyIsEnumerable(Context *ctx); + void init(ExecutionContext *ctx, const Value &ctor); + + static void method_getPrototypeOf(ExecutionContext *ctx); + static void method_getOwnPropertyDescriptor(ExecutionContext *ctx); + static void method_getOwnPropertyNames(ExecutionContext *ctx); + static void method_create(ExecutionContext *ctx); + static void method_defineProperty(ExecutionContext *ctx); + static void method_defineProperties(ExecutionContext *ctx); + static void method_seal(ExecutionContext *ctx); + static void method_freeze(ExecutionContext *ctx); + static void method_preventExtensions(ExecutionContext *ctx); + static void method_isSealed(ExecutionContext *ctx); + static void method_isFrozen(ExecutionContext *ctx); + static void method_isExtensible(ExecutionContext *ctx); + static void method_keys(ExecutionContext *ctx); + + static void method_toString(ExecutionContext *ctx); + static void method_toLocaleString(ExecutionContext *ctx); + static void method_valueOf(ExecutionContext *ctx); + static void method_hasOwnProperty(ExecutionContext *ctx); + static void method_isPrototypeOf(ExecutionContext *ctx); + static void method_propertyIsEnumerable(ExecutionContext *ctx); }; struct StringCtor: FunctionObject { - StringCtor(Context *scope); + StringCtor(ExecutionContext *scope); - virtual void construct(Context *ctx); - virtual void call(Context *ctx); + virtual void construct(ExecutionContext *ctx); + virtual void call(ExecutionContext *ctx); }; struct StringPrototype: StringObject { - StringPrototype(Context *ctx): StringObject(Value::fromString(ctx, QString())) {} - void init(Context *ctx, const Value &ctor); - - static QString getThisString(Context *ctx); - - static void method_toString(Context *ctx); - static void method_valueOf(Context *ctx); - static void method_charAt(Context *ctx); - static void method_charCodeAt(Context *ctx); - static void method_concat(Context *ctx); - static void method_indexOf(Context *ctx); - static void method_lastIndexOf(Context *ctx); - static void method_localeCompare(Context *ctx); - static void method_match(Context *ctx); - static void method_replace(Context *ctx); - static void method_search(Context *ctx); - static void method_slice(Context *ctx); - static void method_split(Context *ctx); - static void method_substr(Context *ctx); - static void method_substring(Context *ctx); - static void method_toLowerCase(Context *ctx); - static void method_toLocaleLowerCase(Context *ctx); - static void method_toUpperCase(Context *ctx); - static void method_toLocaleUpperCase(Context *ctx); - static void method_fromCharCode(Context *ctx); + StringPrototype(ExecutionContext *ctx): StringObject(Value::fromString(ctx, QString())) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static QString getThisString(ExecutionContext *ctx); + + static void method_toString(ExecutionContext *ctx); + static void method_valueOf(ExecutionContext *ctx); + static void method_charAt(ExecutionContext *ctx); + static void method_charCodeAt(ExecutionContext *ctx); + static void method_concat(ExecutionContext *ctx); + static void method_indexOf(ExecutionContext *ctx); + static void method_lastIndexOf(ExecutionContext *ctx); + static void method_localeCompare(ExecutionContext *ctx); + static void method_match(ExecutionContext *ctx); + static void method_replace(ExecutionContext *ctx); + static void method_search(ExecutionContext *ctx); + static void method_slice(ExecutionContext *ctx); + static void method_split(ExecutionContext *ctx); + static void method_substr(ExecutionContext *ctx); + static void method_substring(ExecutionContext *ctx); + static void method_toLowerCase(ExecutionContext *ctx); + static void method_toLocaleLowerCase(ExecutionContext *ctx); + static void method_toUpperCase(ExecutionContext *ctx); + static void method_toLocaleUpperCase(ExecutionContext *ctx); + static void method_fromCharCode(ExecutionContext *ctx); }; struct NumberCtor: FunctionObject { - NumberCtor(Context *scope); + NumberCtor(ExecutionContext *scope); - virtual void construct(Context *ctx); - virtual void call(Context *ctx); + virtual void construct(ExecutionContext *ctx); + virtual void call(ExecutionContext *ctx); }; struct NumberPrototype: NumberObject { NumberPrototype(): NumberObject(Value::fromDouble(0)) {} - void init(Context *ctx, const Value &ctor); - - static void method_toString(Context *ctx); - static void method_toLocaleString(Context *ctx); - static void method_valueOf(Context *ctx); - static void method_toFixed(Context *ctx); - static void method_toExponential(Context *ctx); - static void method_toPrecision(Context *ctx); + void init(ExecutionContext *ctx, const Value &ctor); + + static void method_toString(ExecutionContext *ctx); + static void method_toLocaleString(ExecutionContext *ctx); + static void method_valueOf(ExecutionContext *ctx); + static void method_toFixed(ExecutionContext *ctx); + static void method_toExponential(ExecutionContext *ctx); + static void method_toPrecision(ExecutionContext *ctx); }; struct BooleanCtor: FunctionObject { - BooleanCtor(Context *scope); + BooleanCtor(ExecutionContext *scope); - virtual void construct(Context *ctx); - virtual void call(Context *ctx); + virtual void construct(ExecutionContext *ctx); + virtual void call(ExecutionContext *ctx); }; struct BooleanPrototype: BooleanObject { BooleanPrototype(): BooleanObject(Value::fromBoolean(false)) {} - void init(Context *ctx, const Value &ctor); + void init(ExecutionContext *ctx, const Value &ctor); - static void method_toString(Context *ctx); - static void method_valueOf(Context *ctx); + static void method_toString(ExecutionContext *ctx); + static void method_valueOf(ExecutionContext *ctx); }; struct ArrayCtor: FunctionObject { - ArrayCtor(Context *scope); + ArrayCtor(ExecutionContext *scope); - virtual void construct(Context *ctx); - virtual void call(Context *ctx); + virtual void construct(ExecutionContext *ctx); + virtual void call(ExecutionContext *ctx); }; struct ArrayPrototype: ArrayObject { - void init(Context *ctx, const Value &ctor); - - static void method_toString(Context *ctx); - static void method_toLocaleString(Context *ctx); - static void method_concat(Context *ctx); - static void method_join(Context *ctx); - static void method_pop(Context *ctx); - static void method_push(Context *ctx); - static void method_reverse(Context *ctx); - static void method_shift(Context *ctx); - static void method_slice(Context *ctx); - static void method_sort(Context *ctx); - static void method_splice(Context *ctx); - static void method_unshift(Context *ctx); - static void method_indexOf(Context *ctx); - static void method_lastIndexOf(Context *ctx); - static void method_every(Context *ctx); - static void method_some(Context *ctx); - static void method_forEach(Context *ctx); - static void method_map(Context *ctx); - static void method_filter(Context *ctx); - static void method_reduce(Context *ctx); - static void method_reduceRight(Context *ctx); + void init(ExecutionContext *ctx, const Value &ctor); + + static void method_toString(ExecutionContext *ctx); + static void method_toLocaleString(ExecutionContext *ctx); + static void method_concat(ExecutionContext *ctx); + static void method_join(ExecutionContext *ctx); + static void method_pop(ExecutionContext *ctx); + static void method_push(ExecutionContext *ctx); + static void method_reverse(ExecutionContext *ctx); + static void method_shift(ExecutionContext *ctx); + static void method_slice(ExecutionContext *ctx); + static void method_sort(ExecutionContext *ctx); + static void method_splice(ExecutionContext *ctx); + static void method_unshift(ExecutionContext *ctx); + static void method_indexOf(ExecutionContext *ctx); + static void method_lastIndexOf(ExecutionContext *ctx); + static void method_every(ExecutionContext *ctx); + static void method_some(ExecutionContext *ctx); + static void method_forEach(ExecutionContext *ctx); + static void method_map(ExecutionContext *ctx); + static void method_filter(ExecutionContext *ctx); + static void method_reduce(ExecutionContext *ctx); + static void method_reduceRight(ExecutionContext *ctx); }; struct FunctionCtor: FunctionObject { - FunctionCtor(Context *scope); + FunctionCtor(ExecutionContext *scope); - virtual void construct(Context *ctx); - virtual void call(Context *ctx); + virtual void construct(ExecutionContext *ctx); + virtual void call(ExecutionContext *ctx); }; struct FunctionPrototype: FunctionObject { - FunctionPrototype(Context *ctx): FunctionObject(ctx) {} - void init(Context *ctx, const Value &ctor); + FunctionPrototype(ExecutionContext *ctx): FunctionObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor); - static void method_toString(Context *ctx); - static void method_apply(Context *ctx); - static void method_call(Context *ctx); - static void method_bind(Context *ctx); + static void method_toString(ExecutionContext *ctx); + static void method_apply(ExecutionContext *ctx); + static void method_call(ExecutionContext *ctx); + static void method_bind(ExecutionContext *ctx); }; struct DateCtor: FunctionObject { - DateCtor(Context *scope); + DateCtor(ExecutionContext *scope); - virtual void construct(Context *ctx); - virtual void call(Context *ctx); + virtual void construct(ExecutionContext *ctx); + virtual void call(ExecutionContext *ctx); }; struct DatePrototype: DateObject { DatePrototype(): DateObject(Value::fromDouble(qSNaN())) {} - void init(Context *ctx, const Value &ctor); - - static double getThisDate(Context *ctx); - - static void method_MakeTime(Context *ctx); - static void method_MakeDate(Context *ctx); - static void method_TimeClip(Context *ctx); - static void method_parse(Context *ctx); - static void method_UTC(Context *ctx); - static void method_toString(Context *ctx); - static void method_toDateString(Context *ctx); - static void method_toTimeString(Context *ctx); - static void method_toLocaleString(Context *ctx); - static void method_toLocaleDateString(Context *ctx); - static void method_toLocaleTimeString(Context *ctx); - static void method_valueOf(Context *ctx); - static void method_getTime(Context *ctx); - static void method_getYear(Context *ctx); - static void method_getFullYear(Context *ctx); - static void method_getUTCFullYear(Context *ctx); - static void method_getMonth(Context *ctx); - static void method_getUTCMonth(Context *ctx); - static void method_getDate(Context *ctx); - static void method_getUTCDate(Context *ctx); - static void method_getDay(Context *ctx); - static void method_getUTCDay(Context *ctx); - static void method_getHours(Context *ctx); - static void method_getUTCHours(Context *ctx); - static void method_getMinutes(Context *ctx); - static void method_getUTCMinutes(Context *ctx); - static void method_getSeconds(Context *ctx); - static void method_getUTCSeconds(Context *ctx); - static void method_getMilliseconds(Context *ctx); - static void method_getUTCMilliseconds(Context *ctx); - static void method_getTimezoneOffset(Context *ctx); - static void method_setTime(Context *ctx); - static void method_setMilliseconds(Context *ctx); - static void method_setUTCMilliseconds(Context *ctx); - static void method_setSeconds(Context *ctx); - static void method_setUTCSeconds(Context *ctx); - static void method_setMinutes(Context *ctx); - static void method_setUTCMinutes(Context *ctx); - static void method_setHours(Context *ctx); - static void method_setUTCHours(Context *ctx); - static void method_setDate(Context *ctx); - static void method_setUTCDate(Context *ctx); - static void method_setMonth(Context *ctx); - static void method_setUTCMonth(Context *ctx); - static void method_setYear(Context *ctx); - static void method_setFullYear(Context *ctx); - static void method_setUTCFullYear(Context *ctx); - static void method_toUTCString(Context *ctx); + void init(ExecutionContext *ctx, const Value &ctor); + + static double getThisDate(ExecutionContext *ctx); + + static void method_MakeTime(ExecutionContext *ctx); + static void method_MakeDate(ExecutionContext *ctx); + static void method_TimeClip(ExecutionContext *ctx); + static void method_parse(ExecutionContext *ctx); + static void method_UTC(ExecutionContext *ctx); + static void method_toString(ExecutionContext *ctx); + static void method_toDateString(ExecutionContext *ctx); + static void method_toTimeString(ExecutionContext *ctx); + static void method_toLocaleString(ExecutionContext *ctx); + static void method_toLocaleDateString(ExecutionContext *ctx); + static void method_toLocaleTimeString(ExecutionContext *ctx); + static void method_valueOf(ExecutionContext *ctx); + static void method_getTime(ExecutionContext *ctx); + static void method_getYear(ExecutionContext *ctx); + static void method_getFullYear(ExecutionContext *ctx); + static void method_getUTCFullYear(ExecutionContext *ctx); + static void method_getMonth(ExecutionContext *ctx); + static void method_getUTCMonth(ExecutionContext *ctx); + static void method_getDate(ExecutionContext *ctx); + static void method_getUTCDate(ExecutionContext *ctx); + static void method_getDay(ExecutionContext *ctx); + static void method_getUTCDay(ExecutionContext *ctx); + static void method_getHours(ExecutionContext *ctx); + static void method_getUTCHours(ExecutionContext *ctx); + static void method_getMinutes(ExecutionContext *ctx); + static void method_getUTCMinutes(ExecutionContext *ctx); + static void method_getSeconds(ExecutionContext *ctx); + static void method_getUTCSeconds(ExecutionContext *ctx); + static void method_getMilliseconds(ExecutionContext *ctx); + static void method_getUTCMilliseconds(ExecutionContext *ctx); + static void method_getTimezoneOffset(ExecutionContext *ctx); + static void method_setTime(ExecutionContext *ctx); + static void method_setMilliseconds(ExecutionContext *ctx); + static void method_setUTCMilliseconds(ExecutionContext *ctx); + static void method_setSeconds(ExecutionContext *ctx); + static void method_setUTCSeconds(ExecutionContext *ctx); + static void method_setMinutes(ExecutionContext *ctx); + static void method_setUTCMinutes(ExecutionContext *ctx); + static void method_setHours(ExecutionContext *ctx); + static void method_setUTCHours(ExecutionContext *ctx); + static void method_setDate(ExecutionContext *ctx); + static void method_setUTCDate(ExecutionContext *ctx); + static void method_setMonth(ExecutionContext *ctx); + static void method_setUTCMonth(ExecutionContext *ctx); + static void method_setYear(ExecutionContext *ctx); + static void method_setFullYear(ExecutionContext *ctx); + static void method_setUTCFullYear(ExecutionContext *ctx); + static void method_toUTCString(ExecutionContext *ctx); }; struct RegExpCtor: FunctionObject { - RegExpCtor(Context *scope); + RegExpCtor(ExecutionContext *scope); - virtual void construct(Context *ctx); - virtual void call(Context *ctx); + virtual void construct(ExecutionContext *ctx); + virtual void call(ExecutionContext *ctx); }; struct RegExpPrototype: RegExpObject { RegExpPrototype(): RegExpObject(QRegularExpression(), false) {} - void init(Context *ctx, const Value &ctor); + void init(ExecutionContext *ctx, const Value &ctor); - static void method_exec(Context *ctx); - static void method_test(Context *ctx); - static void method_toString(Context *ctx); + static void method_exec(ExecutionContext *ctx); + static void method_test(ExecutionContext *ctx); + static void method_toString(ExecutionContext *ctx); }; struct ErrorCtor: FunctionObject { - ErrorCtor(Context *scope); + ErrorCtor(ExecutionContext *scope); - virtual void construct(Context *ctx); - virtual void call(Context *ctx); + virtual void construct(ExecutionContext *ctx); + virtual void call(ExecutionContext *ctx); }; struct EvalErrorCtor: ErrorCtor { - EvalErrorCtor(Context *scope): ErrorCtor(scope) {} + EvalErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual void construct(Context *ctx); + virtual void construct(ExecutionContext *ctx); }; struct RangeErrorCtor: ErrorCtor { - RangeErrorCtor(Context *scope): ErrorCtor(scope) {} + RangeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual void construct(Context *ctx); + virtual void construct(ExecutionContext *ctx); }; struct ReferenceErrorCtor: ErrorCtor { - ReferenceErrorCtor(Context *scope): ErrorCtor(scope) {} + ReferenceErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual void construct(Context *ctx); + virtual void construct(ExecutionContext *ctx); }; struct SyntaxErrorCtor: ErrorCtor { - SyntaxErrorCtor(Context *scope): ErrorCtor(scope) {} + SyntaxErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual void construct(Context *ctx); + virtual void construct(ExecutionContext *ctx); }; struct TypeErrorCtor: ErrorCtor { - TypeErrorCtor(Context *scope): ErrorCtor(scope) {} + TypeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual void construct(Context *ctx); + virtual void construct(ExecutionContext *ctx); }; struct URIErrorCtor: ErrorCtor { - URIErrorCtor(Context *scope): ErrorCtor(scope) {} + URIErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual void construct(Context *ctx); + virtual void construct(ExecutionContext *ctx); }; @@ -349,71 +349,71 @@ struct ErrorPrototype: ErrorObject { // ### shouldn't be undefined ErrorPrototype(): ErrorObject(Value::undefinedValue()) {} - void init(Context *ctx, const Value &ctor) { init(ctx, ctor, this); } + void init(ExecutionContext *ctx, const Value &ctor) { init(ctx, ctor, this); } - static void init(Context *ctx, const Value &ctor, Object *obj); - static void method_toString(Context *ctx); + static void init(ExecutionContext *ctx, const Value &ctor, Object *obj); + static void method_toString(ExecutionContext *ctx); }; struct EvalErrorPrototype: EvalErrorObject { - EvalErrorPrototype(Context *ctx): EvalErrorObject(ctx) {} - void init(Context *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } + EvalErrorPrototype(ExecutionContext *ctx): EvalErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; struct RangeErrorPrototype: RangeErrorObject { - RangeErrorPrototype(Context *ctx): RangeErrorObject(ctx) {} - void init(Context *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } + RangeErrorPrototype(ExecutionContext *ctx): RangeErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; struct ReferenceErrorPrototype: ReferenceErrorObject { - ReferenceErrorPrototype(Context *ctx): ReferenceErrorObject(ctx) {} - void init(Context *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } + ReferenceErrorPrototype(ExecutionContext *ctx): ReferenceErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; struct SyntaxErrorPrototype: SyntaxErrorObject { - SyntaxErrorPrototype(Context *ctx): SyntaxErrorObject(ctx) {} - void init(Context *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } + SyntaxErrorPrototype(ExecutionContext *ctx): SyntaxErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; struct TypeErrorPrototype: TypeErrorObject { - TypeErrorPrototype(Context *ctx): TypeErrorObject(ctx) {} - void init(Context *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } + TypeErrorPrototype(ExecutionContext *ctx): TypeErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; struct URIErrorPrototype: URIErrorObject { - URIErrorPrototype(Context *ctx): URIErrorObject(ctx) {} - void init(Context *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } + URIErrorPrototype(ExecutionContext *ctx): URIErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; struct MathObject: Object { - MathObject(Context *ctx); - - static void method_abs(Context *ctx); - static void method_acos(Context *ctx); - static void method_asin(Context *ctx); - static void method_atan(Context *ctx); - static void method_atan2(Context *ctx); - static void method_ceil(Context *ctx); - static void method_cos(Context *ctx); - static void method_exp(Context *ctx); - static void method_floor(Context *ctx); - static void method_log(Context *ctx); - static void method_max(Context *ctx); - static void method_min(Context *ctx); - static void method_pow(Context *ctx); - static void method_random(Context *ctx); - static void method_round(Context *ctx); - static void method_sin(Context *ctx); - static void method_sqrt(Context *ctx); - static void method_tan(Context *ctx); + MathObject(ExecutionContext *ctx); + + static void method_abs(ExecutionContext *ctx); + static void method_acos(ExecutionContext *ctx); + static void method_asin(ExecutionContext *ctx); + static void method_atan(ExecutionContext *ctx); + static void method_atan2(ExecutionContext *ctx); + static void method_ceil(ExecutionContext *ctx); + static void method_cos(ExecutionContext *ctx); + static void method_exp(ExecutionContext *ctx); + static void method_floor(ExecutionContext *ctx); + static void method_log(ExecutionContext *ctx); + static void method_max(ExecutionContext *ctx); + static void method_min(ExecutionContext *ctx); + static void method_pow(ExecutionContext *ctx); + static void method_random(ExecutionContext *ctx); + static void method_round(ExecutionContext *ctx); + static void method_sin(ExecutionContext *ctx); + static void method_sqrt(ExecutionContext *ctx); + static void method_tan(ExecutionContext *ctx); }; } // end of namespace VM diff --git a/qv4ir_p.h b/qv4ir_p.h index f736b02ef6..38f690b282 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -71,7 +71,7 @@ class QQmlType; namespace QQmlJS { namespace VM { -struct Context; +struct ExecutionContext; struct Value; } @@ -596,7 +596,7 @@ struct Function { QList locals; IR::BasicBlock *handlersBlock; - void (*code)(VM::Context *, const uchar *); + void (*code)(VM::ExecutionContext *, const uchar *); const uchar *codeData; JSC::MacroAssemblerCodeRef codeRef; diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 6afcaafae8..90b3cb968d 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -165,7 +165,7 @@ void InstructionSelection::operator()(IR::Function *function) _function->codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); } - _function->code = (void (*)(VM::Context *, const uchar *)) _function->codeRef.code().executableAddress(); + _function->code = (void (*)(VM::ExecutionContext *, const uchar *)) _function->codeRef.code().executableAddress(); qSwap(_function, function); } @@ -180,10 +180,10 @@ InstructionSelection::Pointer InstructionSelection::loadTempAddress(RegisterID r int32_t offset = 0; if (t->index < 0) { const int arg = -t->index - 1; - loadPtr(Address(ContextRegister, offsetof(Context, arguments)), reg); + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, arguments)), reg); offset = arg * sizeof(Value); } else if (t->index < _function->locals.size()) { - loadPtr(Address(ContextRegister, offsetof(Context, locals)), reg); + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, locals)), reg); offset = t->index * sizeof(Value); } else { const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size(); @@ -424,7 +424,7 @@ void InstructionSelection::visitMove(IR::Move *s) return; } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { - Value (*op)(const Value value, Context *ctx) = 0; + Value (*op)(const Value value, ExecutionContext *ctx) = 0; const char *opName = 0; switch (u->op) { case IR::OpIfTrue: assert(!"unreachable"); break; @@ -442,7 +442,7 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Binop *b = s->source->asBinop()) { if ((b->left->asTemp() || b->left->asConst()) && (b->right->asTemp() || b->right->asConst())) { - Value (*op)(const Value, const Value, Context *) = 0; + Value (*op)(const Value, const Value, ExecutionContext *) = 0; const char* opName = 0; switch ((IR::AluOp) b->op) { @@ -521,7 +521,7 @@ void InstructionSelection::visitMove(IR::Move *s) // inplace assignment, e.g. x += 1, ++x, ... if (IR::Temp *t = s->target->asTemp()) { if (s->source->asTemp() || s->source->asConst()) { - Value (*op)(const Value left, const Value right, Context *ctx) = 0; + Value (*op)(const Value left, const Value right, ExecutionContext *ctx) = 0; const char *opName = 0; switch (s->op) { case IR::OpBitAnd: setOp(op, opName, __qmljs_bit_and); break; @@ -545,7 +545,7 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Name *n = s->target->asName()) { if (s->source->asTemp() || s->source->asConst()) { - void (*op)(const Value value, String *name, Context *ctx) = 0; + void (*op)(const Value value, String *name, ExecutionContext *ctx) = 0; const char *opName = 0; switch (s->op) { case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break; @@ -570,7 +570,7 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Subscript *ss = s->target->asSubscript()) { if (s->source->asTemp() || s->source->asConst()) { - void (*op)(Value base, Value index, Value value, Context *ctx) = 0; + void (*op)(Value base, Value index, Value value, ExecutionContext *ctx) = 0; const char *opName = 0; switch (s->op) { case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; @@ -598,7 +598,7 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Member *m = s->target->asMember()) { if (s->source->asTemp() || s->source->asConst()) { - void (*op)(Value value, Value base, String *name, Context *ctx) = 0; + void (*op)(Value value, Value base, String *name, ExecutionContext *ctx) = 0; const char *opName = 0; switch (s->op) { case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; @@ -671,7 +671,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) } else if (IR::Binop *b = s->cond->asBinop()) { if ((b->left->asTemp() || b->left->asConst()) && (b->right->asTemp() || b->right->asConst())) { - Bool (*op)(const Value, const Value, Context *ctx) = 0; + Bool (*op)(const Value, const Value, ExecutionContext *ctx) = 0; const char *opName = 0; switch (b->op) { default: Q_UNREACHABLE(); assert(!"todo"); break; @@ -706,7 +706,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { if (IR::Temp *t = s->expr->asTemp()) { - copyValue(Pointer(ContextRegister, offsetof(Context, result)), t); + copyValue(Pointer(ContextRegister, offsetof(ExecutionContext, result)), t); return; } Q_UNIMPLEMENTED(); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index bf37b0720b..82bc471be5 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -579,8 +579,8 @@ private: int prepareVariableArguments(IR::ExprList* args); - typedef VM::Value (*ActivationMethod)(VM::Context *, VM::String *name, VM::Value *args, int argc); - typedef VM::Value (*BuiltinMethod)(VM::Context *, VM::Value *args, int argc); + typedef VM::Value (*ActivationMethod)(VM::ExecutionContext *, VM::String *name, VM::Value *args, int argc); + typedef VM::Value (*BuiltinMethod)(VM::ExecutionContext *, VM::Value *args, int argc); void callRuntimeMethodImp(IR::Temp *result, const char* name, ActivationMethod method, IR::Expr *base, IR::ExprList *args); void callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args); #define callRuntimeMethod(result, function, ...) \ -- cgit v1.2.3 From 65724ce3e7ae798c6efad3a039072356063f3e4c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 17 Nov 2012 21:54:26 +0100 Subject: Move the engine and context classes into their own files Change-Id: Ie20138990908a921ca3d7475618275ed82d9cb5c Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 1 + qmljs_engine.cpp | 339 ++++++++++++++++++++++++++++++++++++++++++++++++++ qmljs_engine.h | 182 +++++++++++++++++++++++++++ qmljs_environment.cpp | 181 +++++++++++++++++++++++++++ qmljs_environment.h | 98 +++++++++++++++ qmljs_objects.cpp | 289 ------------------------------------------ qmljs_objects.h | 99 +-------------- qmljs_runtime.cpp | 132 -------------------- qmljs_runtime.h | 41 ------ v4.pro | 4 + 10 files changed, 807 insertions(+), 559 deletions(-) create mode 100644 qmljs_engine.cpp create mode 100644 qmljs_engine.h create mode 100644 qmljs_environment.cpp create mode 100644 qmljs_environment.h diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index aa5d70ccbb..b911ad4880 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qmljs_runtime.h" +#include "qmljs_environment.h" #include #include diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp new file mode 100644 index 0000000000..9229dbd9a2 --- /dev/null +++ b/qmljs_engine.cpp @@ -0,0 +1,339 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include + +namespace QQmlJS { +namespace VM { + +ExecutionEngine::ExecutionEngine() +{ + rootContext = newContext(); + rootContext->init(this); + + id_length = identifier(QStringLiteral("length")); + id_prototype = identifier(QStringLiteral("prototype")); + id_constructor = identifier(QStringLiteral("constructor")); + id_arguments = identifier(QStringLiteral("arguments")); + id___proto__ = identifier(QStringLiteral("__proto__")); + + objectPrototype = new ObjectPrototype(); + stringPrototype = new StringPrototype(rootContext); + numberPrototype = new NumberPrototype(); + booleanPrototype = new BooleanPrototype(); + arrayPrototype = new ArrayPrototype(); + datePrototype = new DatePrototype(); + functionPrototype = new FunctionPrototype(rootContext); + regExpPrototype = new RegExpPrototype(); + errorPrototype = new ErrorPrototype(); + evalErrorPrototype = new EvalErrorPrototype(rootContext); + rangeErrorPrototype = new RangeErrorPrototype(rootContext); + referenceErrorPrototype = new ReferenceErrorPrototype(rootContext); + syntaxErrorPrototype = new SyntaxErrorPrototype(rootContext); + typeErrorPrototype = new TypeErrorPrototype(rootContext); + uRIErrorPrototype = new URIErrorPrototype(rootContext); + + stringPrototype->prototype = objectPrototype; + numberPrototype->prototype = objectPrototype; + booleanPrototype->prototype = objectPrototype; + arrayPrototype->prototype = objectPrototype; + datePrototype->prototype = objectPrototype; + functionPrototype->prototype = objectPrototype; + regExpPrototype->prototype = objectPrototype; + errorPrototype->prototype = objectPrototype; + evalErrorPrototype->prototype = errorPrototype; + rangeErrorPrototype->prototype = errorPrototype; + referenceErrorPrototype->prototype = errorPrototype; + syntaxErrorPrototype->prototype = errorPrototype; + typeErrorPrototype->prototype = errorPrototype; + uRIErrorPrototype->prototype = errorPrototype; + + objectCtor = Value::fromObject(new ObjectCtor(rootContext)); + stringCtor = Value::fromObject(new StringCtor(rootContext)); + numberCtor = Value::fromObject(new NumberCtor(rootContext)); + booleanCtor = Value::fromObject(new BooleanCtor(rootContext)); + arrayCtor = Value::fromObject(new ArrayCtor(rootContext)); + functionCtor = Value::fromObject(new FunctionCtor(rootContext)); + dateCtor = Value::fromObject(new DateCtor(rootContext)); + regExpCtor = Value::fromObject(new RegExpCtor(rootContext)); + errorCtor = Value::fromObject(new ErrorCtor(rootContext)); + evalErrorCtor = Value::fromObject(new EvalErrorCtor(rootContext)); + rangeErrorCtor = Value::fromObject(new RangeErrorCtor(rootContext)); + referenceErrorCtor = Value::fromObject(new ReferenceErrorCtor(rootContext)); + syntaxErrorCtor = Value::fromObject(new SyntaxErrorCtor(rootContext)); + typeErrorCtor = Value::fromObject(new TypeErrorCtor(rootContext)); + uRIErrorCtor = Value::fromObject(new URIErrorCtor(rootContext)); + + stringCtor.objectValue()->prototype = functionPrototype; + numberCtor.objectValue()->prototype = functionPrototype; + booleanCtor.objectValue()->prototype = functionPrototype; + arrayCtor.objectValue()->prototype = functionPrototype; + functionCtor.objectValue()->prototype = functionPrototype; + dateCtor.objectValue()->prototype = functionPrototype; + regExpCtor.objectValue()->prototype = functionPrototype; + errorCtor.objectValue()->prototype = functionPrototype; + evalErrorCtor.objectValue()->prototype = errorPrototype; + rangeErrorCtor.objectValue()->prototype = errorPrototype; + referenceErrorCtor.objectValue()->prototype = errorPrototype; + syntaxErrorCtor.objectValue()->prototype = errorPrototype; + typeErrorCtor.objectValue()->prototype = errorPrototype; + uRIErrorCtor.objectValue()->prototype = errorPrototype; + + objectPrototype->init(rootContext, objectCtor); + stringPrototype->init(rootContext, stringCtor); + numberPrototype->init(rootContext, numberCtor); + booleanPrototype->init(rootContext, booleanCtor); + arrayPrototype->init(rootContext, arrayCtor); + datePrototype->init(rootContext, dateCtor); + functionPrototype->init(rootContext, functionCtor); + regExpPrototype->init(rootContext, regExpCtor); + errorPrototype->init(rootContext, errorCtor); + evalErrorPrototype->init(rootContext, evalErrorCtor); + rangeErrorPrototype->init(rootContext, rangeErrorCtor); + referenceErrorPrototype->init(rootContext, referenceErrorCtor); + syntaxErrorPrototype->init(rootContext, syntaxErrorCtor); + typeErrorPrototype->init(rootContext, typeErrorCtor); + uRIErrorPrototype->init(rootContext, uRIErrorCtor); + + // + // set up the global object + // + VM::Object *glo = newObject(/*rootContext*/); + globalObject = Value::fromObject(glo); + rootContext->activation = glo; + + glo->__put__(rootContext, identifier(QStringLiteral("Object")), objectCtor); + glo->__put__(rootContext, identifier(QStringLiteral("String")), stringCtor); + glo->__put__(rootContext, identifier(QStringLiteral("Number")), numberCtor); + glo->__put__(rootContext, identifier(QStringLiteral("Boolean")), booleanCtor); + glo->__put__(rootContext, identifier(QStringLiteral("Array")), arrayCtor); + glo->__put__(rootContext, identifier(QStringLiteral("Function")), functionCtor); + glo->__put__(rootContext, identifier(QStringLiteral("Date")), dateCtor); + glo->__put__(rootContext, identifier(QStringLiteral("RegExp")), regExpCtor); + glo->__put__(rootContext, identifier(QStringLiteral("Error")), errorCtor); + glo->__put__(rootContext, identifier(QStringLiteral("EvalError")), evalErrorCtor); + glo->__put__(rootContext, identifier(QStringLiteral("RangeError")), rangeErrorCtor); + glo->__put__(rootContext, identifier(QStringLiteral("ReferenceError")), referenceErrorCtor); + glo->__put__(rootContext, identifier(QStringLiteral("SyntaxError")), syntaxErrorCtor); + glo->__put__(rootContext, identifier(QStringLiteral("TypeError")), typeErrorCtor); + glo->__put__(rootContext, identifier(QStringLiteral("URIError")), uRIErrorCtor); + glo->__put__(rootContext, identifier(QStringLiteral("Math")), Value::fromObject(newMathObject(rootContext))); + glo->__put__(rootContext, identifier(QStringLiteral("undefined")), Value::undefinedValue()); + glo->__put__(rootContext, identifier(QStringLiteral("NaN")), Value::fromDouble(nan(""))); + glo->__put__(rootContext, identifier(QStringLiteral("Infinity")), Value::fromDouble(INFINITY)); + glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new EvalFunction(rootContext))); + + +} + +ExecutionContext *ExecutionEngine::newContext() +{ + return new ExecutionContext(); +} + +String *ExecutionEngine::identifier(const QString &s) +{ + String *&id = identifiers[s]; + if (! id) + id = newString(s); + return id; +} + +FunctionObject *ExecutionEngine::newNativeFunction(ExecutionContext *scope, void (*code)(ExecutionContext *)) +{ + NativeFunction *f = new NativeFunction(scope, code); + f->prototype = scope->engine->functionPrototype; + return f; +} + +FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, IR::Function *function) +{ + ScriptFunction *f = new ScriptFunction(scope, function); + Object *proto = scope->engine->newObject(); + proto->__put__(scope, scope->engine->id_constructor, Value::fromObject(f)); + f->__put__(scope, scope->engine->id_prototype, Value::fromObject(proto)); + f->prototype = scope->engine->functionPrototype; + return f; +} + +Object *ExecutionEngine::newObject() +{ + Object *object = new Object(); + object->prototype = objectPrototype; + return object; +} + +FunctionObject *ExecutionEngine::newObjectCtor(ExecutionContext *ctx) +{ + return new ObjectCtor(ctx); +} + +String *ExecutionEngine::newString(const QString &s) +{ + return new String(s); +} + +Object *ExecutionEngine::newStringObject(const Value &value) +{ + StringObject *object = new StringObject(value); + object->prototype = stringPrototype; + return object; +} + +FunctionObject *ExecutionEngine::newStringCtor(ExecutionContext *ctx) +{ + return new StringCtor(ctx); +} + +Object *ExecutionEngine::newNumberObject(const Value &value) +{ + NumberObject *object = new NumberObject(value); + object->prototype = numberPrototype; + return object; +} + +FunctionObject *ExecutionEngine::newNumberCtor(ExecutionContext *ctx) +{ + return new NumberCtor(ctx); +} + +Object *ExecutionEngine::newBooleanObject(const Value &value) +{ + Object *object = new BooleanObject(value); + object->prototype = booleanPrototype; + return object; +} + +FunctionObject *ExecutionEngine::newBooleanCtor(ExecutionContext *ctx) +{ + return new BooleanCtor(ctx); +} + +Object *ExecutionEngine::newFunctionObject(ExecutionContext *ctx) +{ + Object *object = new FunctionObject(ctx); + object->prototype = functionPrototype; + return object; +} + +FunctionObject *ExecutionEngine::newFunctionCtor(ExecutionContext *ctx) +{ + return new FunctionCtor(ctx); +} + +Object *ExecutionEngine::newArrayObject() +{ + ArrayObject *object = new ArrayObject(); + object->prototype = arrayPrototype; + return object; +} + +Object *ExecutionEngine::newArrayObject(const Array &value) +{ + ArrayObject *object = new ArrayObject(value); + object->prototype = arrayPrototype; + return object; +} + +FunctionObject *ExecutionEngine::newArrayCtor(ExecutionContext *ctx) +{ + return new ArrayCtor(ctx); +} + +Object *ExecutionEngine::newDateObject(const Value &value) +{ + Object *object = new DateObject(value); + object->prototype = datePrototype; + return object; +} + +FunctionObject *ExecutionEngine::newDateCtor(ExecutionContext *ctx) +{ + return new DateCtor(ctx); +} + +Object *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) +{ + bool global = (flags & IR::RegExp::RegExp_Global); + QRegularExpression::PatternOptions options = 0; + if (flags & IR::RegExp::RegExp_IgnoreCase) + options |= QRegularExpression::CaseInsensitiveOption; + if (flags & IR::RegExp::RegExp_Multiline) + options |= QRegularExpression::MultilineOption; + + Object *object = new RegExpObject(QRegularExpression(pattern, options), global); + object->prototype = regExpPrototype; + return object; +} + +FunctionObject *ExecutionEngine::newRegExpCtor(ExecutionContext *ctx) +{ + return new RegExpCtor(ctx); +} + +Object *ExecutionEngine::newErrorObject(const Value &value) +{ + ErrorObject *object = new ErrorObject(value); + object->prototype = errorPrototype; + return object; +} + +Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) +{ + MathObject *object = new MathObject(ctx); + object->prototype = objectPrototype; + return object; +} + +Object *ExecutionEngine::newActivationObject(ExecutionContext *ctx) +{ + return new ActivationObject(ctx); +} + +Object *ExecutionEngine::newForEachIteratorObject(Object *o) +{ + return new ForEachIteratorObject(o); +} + + +} // namespace VM +} // namespace QQmlJS diff --git a/qmljs_engine.h b/qmljs_engine.h new file mode 100644 index 0000000000..ed4b0d4a16 --- /dev/null +++ b/qmljs_engine.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_ENGINE_H +#define QMLJS_ENGINE_H + +#include +#include + +namespace QQmlJS { +namespace VM { + +struct Value; +struct Array; +struct Object; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct RegExpObject; +struct ErrorObject; +struct ActivationObject; +struct ArgumentsObject; +struct ExecutionContext; +struct ExecutionEngine; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct RegExpPrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; + +struct ExecutionEngine +{ + ExecutionContext *rootContext; + Value globalObject; + + Value objectCtor; + Value stringCtor; + Value numberCtor; + Value booleanCtor; + Value arrayCtor; + Value functionCtor; + Value dateCtor; + Value regExpCtor; + Value errorCtor; + Value evalErrorCtor; + Value rangeErrorCtor; + Value referenceErrorCtor; + Value syntaxErrorCtor; + Value typeErrorCtor; + Value uRIErrorCtor; + + ObjectPrototype *objectPrototype; + StringPrototype *stringPrototype; + NumberPrototype *numberPrototype; + BooleanPrototype *booleanPrototype; + ArrayPrototype *arrayPrototype; + FunctionPrototype *functionPrototype; + DatePrototype *datePrototype; + RegExpPrototype *regExpPrototype; + ErrorPrototype *errorPrototype; + EvalErrorPrototype *evalErrorPrototype; + RangeErrorPrototype *rangeErrorPrototype; + ReferenceErrorPrototype *referenceErrorPrototype; + SyntaxErrorPrototype *syntaxErrorPrototype; + TypeErrorPrototype *typeErrorPrototype; + URIErrorPrototype *uRIErrorPrototype; + + QHash identifiers; + + String *id_length; + String *id_prototype; + String *id_constructor; + String *id_arguments; + String *id___proto__; + + struct ExceptionHandler { + ExecutionContext *context; + const uchar *code; // Interpreter state + int targetTempIndex; // Interpreter state + jmp_buf stackFrame; + }; + + QVector unwindStack; + + ExecutionEngine(); + + ExecutionContext *newContext(); + + String *identifier(const QString &s); + + FunctionObject *newNativeFunction(ExecutionContext *scope, void (*code)(ExecutionContext *)); + FunctionObject *newScriptFunction(ExecutionContext *scope, IR::Function *function); + + Object *newObject(); + FunctionObject *newObjectCtor(ExecutionContext *ctx); + + String *newString(const QString &s); + Object *newStringObject(const Value &value); + FunctionObject *newStringCtor(ExecutionContext *ctx); + + Object *newNumberObject(const Value &value); + FunctionObject *newNumberCtor(ExecutionContext *ctx); + + Object *newBooleanObject(const Value &value); + FunctionObject *newBooleanCtor(ExecutionContext *ctx); + + Object *newFunctionObject(ExecutionContext *ctx); + FunctionObject *newFunctionCtor(ExecutionContext *ctx); + + Object *newArrayObject(); + Object *newArrayObject(const Array &value); + FunctionObject *newArrayCtor(ExecutionContext *ctx); + + Object *newDateObject(const Value &value); + FunctionObject *newDateCtor(ExecutionContext *ctx); + + Object *newRegExpObject(const QString &pattern, int flags); + FunctionObject *newRegExpCtor(ExecutionContext *ctx); + + Object *newErrorObject(const Value &value); + Object *newMathObject(ExecutionContext *ctx); + Object *newActivationObject(ExecutionContext *ctx); + + Object *newForEachIteratorObject(Object *o); +}; + + +} // namespace VM +} // namespace QQmlJS + +#endif diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp new file mode 100644 index 0000000000..550d619fe1 --- /dev/null +++ b/qmljs_environment.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include + +namespace QQmlJS { +namespace VM { + +void ExecutionContext::init(ExecutionEngine *eng) +{ + engine = eng; + parent = 0; + arguments = 0; + argumentCount = 0; + locals = 0; + activation = 0; + thisObject = Value::nullValue(); + result = Value::undefinedValue(); + formals = 0; + formalCount = 0; + vars = 0; + varCount = 0; +} + +PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp) +{ + for (ExecutionContext *ctx = this; ctx; ctx = ctx->parent) { + if (ctx->activation) { + if (PropertyDescriptor *pd = ctx->activation->__getPropertyDescriptor__(this, name, tmp)) + return pd; + } + } + return 0; +} + +void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) +{ + for (ExecutionContext *ctx = this; ctx; ctx = ctx->parent) { + if (ctx->activation) { + if (ctx->activation->inplaceBinOp(value, name, op, this)) + return; + } + } + throwReferenceError(Value::fromString(name)); +} + +void ExecutionContext::throwError(Value value) +{ + result = value; + __qmljs_builtin_throw(value, this); +} + +void ExecutionContext::throwError(const QString &message) +{ + Value v = Value::fromString(this, message); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void ExecutionContext::throwTypeError() +{ + Value v = Value::fromString(this, QStringLiteral("Type error")); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void ExecutionContext::throwUnimplemented(const QString &message) +{ + Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void ExecutionContext::throwReferenceError(Value value) +{ + String *s = value.toString(this); + QString msg = s->toQString() + QStringLiteral(" is not defined"); + throwError(Value::fromObject(engine->newErrorObject(Value::fromString(this, msg)))); +} + +void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc) +{ + engine = parent->engine; + this->parent = f->scope; + assert(this->parent == f->scope); + result = Value::undefinedValue(); + + if (f->needsActivation) + activation = engine->newActivationObject(this); + else + activation = 0; + + thisObject = that; + + formals = f->formalParameterList; + formalCount = f->formalParameterCount; + arguments = args; + argumentCount = argc; + if (f->needsActivation || argc < formalCount){ + arguments = new Value[qMax(argc, formalCount)]; + if (argc) + std::copy(args, args + argc, arguments); + if (argc < formalCount) + std::fill(arguments + argc, arguments + formalCount, Value::undefinedValue()); + } + vars = f->varList; + varCount = f->varCount; + locals = varCount ? new Value[varCount] : 0; + if (varCount) + std::fill(locals, locals + varCount, Value::undefinedValue()); +} + +void ExecutionContext::leaveCallContext() +{ + if (activation) { + delete[] locals; + locals = 0; + } +} + +void ExecutionContext::initConstructorContext(ExecutionContext *parent, Value that, FunctionObject *f, Value *args, unsigned argc) +{ + initCallContext(parent, that, f, args, argc); +} + +void ExecutionContext::leaveConstructorContext(FunctionObject *f) +{ + wireUpPrototype(f); + leaveCallContext(); +} + +void ExecutionContext::wireUpPrototype(FunctionObject *f) +{ + assert(thisObject.isObject()); + result = thisObject; + + Value proto = f->__get__(this, engine->id_prototype); + thisObject.objectValue()->prototype = proto.objectValue(); + if (! thisObject.isObject()) + thisObject.objectValue()->prototype = engine->objectPrototype; + +} + +} // namespace VM +} // namespace QQmlJS diff --git a/qmljs_environment.h b/qmljs_environment.h new file mode 100644 index 0000000000..74568cdf25 --- /dev/null +++ b/qmljs_environment.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_ENVIRONMENT_H +#define QMLJS_ENVIRONMENT_H + +#include + +namespace QQmlJS { +namespace VM { + +struct Value; +struct Object; +struct ExecutionEngine; +struct ExecutionContext; + +struct ExecutionContext { + ExecutionEngine *engine; + ExecutionContext *parent; + Object *activation; + Value thisObject; + Value *arguments; + unsigned int argumentCount; + Value *locals; + Value result; + String **formals; + unsigned int formalCount; + String **vars; + unsigned int varCount; + + PropertyDescriptor *lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp); + void inplaceBitOp(Value value, String *name, BinOp op); + + inline Value argument(unsigned int index = 0) + { + if (index < argumentCount) + return arguments[index]; + return Value::undefinedValue(); + } + + void init(ExecutionEngine *eng); + + void initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc); + void leaveCallContext(); + + void initConstructorContext(ExecutionContext *parent, Value that, FunctionObject *f, Value *args, unsigned argc); + void leaveConstructorContext(FunctionObject *f); + void wireUpPrototype(FunctionObject *f); + + void throwError(Value value); + void throwError(const QString &message); + void throwTypeError(); + void throwReferenceError(Value value); + void throwUnimplemented(const QString &message); +}; + + +} // namespace VM +} // namespace QQmlJS + +#endif diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index d352774cdf..7552a83a03 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -686,292 +686,3 @@ PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(ExecutionContext return Object::__getPropertyDescriptor__(ctx, name, to_fill); } - -ExecutionEngine::ExecutionEngine() -{ - rootContext = newContext(); - rootContext->init(this); - - id_length = identifier(QStringLiteral("length")); - id_prototype = identifier(QStringLiteral("prototype")); - id_constructor = identifier(QStringLiteral("constructor")); - id_arguments = identifier(QStringLiteral("arguments")); - id___proto__ = identifier(QStringLiteral("__proto__")); - - objectPrototype = new ObjectPrototype(); - stringPrototype = new StringPrototype(rootContext); - numberPrototype = new NumberPrototype(); - booleanPrototype = new BooleanPrototype(); - arrayPrototype = new ArrayPrototype(); - datePrototype = new DatePrototype(); - functionPrototype = new FunctionPrototype(rootContext); - regExpPrototype = new RegExpPrototype(); - errorPrototype = new ErrorPrototype(); - evalErrorPrototype = new EvalErrorPrototype(rootContext); - rangeErrorPrototype = new RangeErrorPrototype(rootContext); - referenceErrorPrototype = new ReferenceErrorPrototype(rootContext); - syntaxErrorPrototype = new SyntaxErrorPrototype(rootContext); - typeErrorPrototype = new TypeErrorPrototype(rootContext); - uRIErrorPrototype = new URIErrorPrototype(rootContext); - - stringPrototype->prototype = objectPrototype; - numberPrototype->prototype = objectPrototype; - booleanPrototype->prototype = objectPrototype; - arrayPrototype->prototype = objectPrototype; - datePrototype->prototype = objectPrototype; - functionPrototype->prototype = objectPrototype; - regExpPrototype->prototype = objectPrototype; - errorPrototype->prototype = objectPrototype; - evalErrorPrototype->prototype = errorPrototype; - rangeErrorPrototype->prototype = errorPrototype; - referenceErrorPrototype->prototype = errorPrototype; - syntaxErrorPrototype->prototype = errorPrototype; - typeErrorPrototype->prototype = errorPrototype; - uRIErrorPrototype->prototype = errorPrototype; - - objectCtor = Value::fromObject(new ObjectCtor(rootContext)); - stringCtor = Value::fromObject(new StringCtor(rootContext)); - numberCtor = Value::fromObject(new NumberCtor(rootContext)); - booleanCtor = Value::fromObject(new BooleanCtor(rootContext)); - arrayCtor = Value::fromObject(new ArrayCtor(rootContext)); - functionCtor = Value::fromObject(new FunctionCtor(rootContext)); - dateCtor = Value::fromObject(new DateCtor(rootContext)); - regExpCtor = Value::fromObject(new RegExpCtor(rootContext)); - errorCtor = Value::fromObject(new ErrorCtor(rootContext)); - evalErrorCtor = Value::fromObject(new EvalErrorCtor(rootContext)); - rangeErrorCtor = Value::fromObject(new RangeErrorCtor(rootContext)); - referenceErrorCtor = Value::fromObject(new ReferenceErrorCtor(rootContext)); - syntaxErrorCtor = Value::fromObject(new SyntaxErrorCtor(rootContext)); - typeErrorCtor = Value::fromObject(new TypeErrorCtor(rootContext)); - uRIErrorCtor = Value::fromObject(new URIErrorCtor(rootContext)); - - stringCtor.objectValue()->prototype = functionPrototype; - numberCtor.objectValue()->prototype = functionPrototype; - booleanCtor.objectValue()->prototype = functionPrototype; - arrayCtor.objectValue()->prototype = functionPrototype; - functionCtor.objectValue()->prototype = functionPrototype; - dateCtor.objectValue()->prototype = functionPrototype; - regExpCtor.objectValue()->prototype = functionPrototype; - errorCtor.objectValue()->prototype = functionPrototype; - evalErrorCtor.objectValue()->prototype = errorPrototype; - rangeErrorCtor.objectValue()->prototype = errorPrototype; - referenceErrorCtor.objectValue()->prototype = errorPrototype; - syntaxErrorCtor.objectValue()->prototype = errorPrototype; - typeErrorCtor.objectValue()->prototype = errorPrototype; - uRIErrorCtor.objectValue()->prototype = errorPrototype; - - objectPrototype->init(rootContext, objectCtor); - stringPrototype->init(rootContext, stringCtor); - numberPrototype->init(rootContext, numberCtor); - booleanPrototype->init(rootContext, booleanCtor); - arrayPrototype->init(rootContext, arrayCtor); - datePrototype->init(rootContext, dateCtor); - functionPrototype->init(rootContext, functionCtor); - regExpPrototype->init(rootContext, regExpCtor); - errorPrototype->init(rootContext, errorCtor); - evalErrorPrototype->init(rootContext, evalErrorCtor); - rangeErrorPrototype->init(rootContext, rangeErrorCtor); - referenceErrorPrototype->init(rootContext, referenceErrorCtor); - syntaxErrorPrototype->init(rootContext, syntaxErrorCtor); - typeErrorPrototype->init(rootContext, typeErrorCtor); - uRIErrorPrototype->init(rootContext, uRIErrorCtor); - - // - // set up the global object - // - VM::Object *glo = newObject(/*rootContext*/); - globalObject = Value::fromObject(glo); - rootContext->activation = glo; - - glo->__put__(rootContext, identifier(QStringLiteral("Object")), objectCtor); - glo->__put__(rootContext, identifier(QStringLiteral("String")), stringCtor); - glo->__put__(rootContext, identifier(QStringLiteral("Number")), numberCtor); - glo->__put__(rootContext, identifier(QStringLiteral("Boolean")), booleanCtor); - glo->__put__(rootContext, identifier(QStringLiteral("Array")), arrayCtor); - glo->__put__(rootContext, identifier(QStringLiteral("Function")), functionCtor); - glo->__put__(rootContext, identifier(QStringLiteral("Date")), dateCtor); - glo->__put__(rootContext, identifier(QStringLiteral("RegExp")), regExpCtor); - glo->__put__(rootContext, identifier(QStringLiteral("Error")), errorCtor); - glo->__put__(rootContext, identifier(QStringLiteral("EvalError")), evalErrorCtor); - glo->__put__(rootContext, identifier(QStringLiteral("RangeError")), rangeErrorCtor); - glo->__put__(rootContext, identifier(QStringLiteral("ReferenceError")), referenceErrorCtor); - glo->__put__(rootContext, identifier(QStringLiteral("SyntaxError")), syntaxErrorCtor); - glo->__put__(rootContext, identifier(QStringLiteral("TypeError")), typeErrorCtor); - glo->__put__(rootContext, identifier(QStringLiteral("URIError")), uRIErrorCtor); - glo->__put__(rootContext, identifier(QStringLiteral("Math")), Value::fromObject(newMathObject(rootContext))); - glo->__put__(rootContext, identifier(QStringLiteral("undefined")), Value::undefinedValue()); - glo->__put__(rootContext, identifier(QStringLiteral("NaN")), Value::fromDouble(nan(""))); - glo->__put__(rootContext, identifier(QStringLiteral("Infinity")), Value::fromDouble(INFINITY)); - glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new EvalFunction(rootContext))); - - -} - -ExecutionContext *ExecutionEngine::newContext() -{ - return new ExecutionContext(); -} - -String *ExecutionEngine::identifier(const QString &s) -{ - String *&id = identifiers[s]; - if (! id) - id = newString(s); - return id; -} - -FunctionObject *ExecutionEngine::newNativeFunction(ExecutionContext *scope, void (*code)(ExecutionContext *)) -{ - NativeFunction *f = new NativeFunction(scope, code); - f->prototype = scope->engine->functionPrototype; - return f; -} - -FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, IR::Function *function) -{ - ScriptFunction *f = new ScriptFunction(scope, function); - Object *proto = scope->engine->newObject(); - proto->__put__(scope, scope->engine->id_constructor, Value::fromObject(f)); - f->__put__(scope, scope->engine->id_prototype, Value::fromObject(proto)); - f->prototype = scope->engine->functionPrototype; - return f; -} - -Object *ExecutionEngine::newObject() -{ - Object *object = new Object(); - object->prototype = objectPrototype; - return object; -} - -FunctionObject *ExecutionEngine::newObjectCtor(ExecutionContext *ctx) -{ - return new ObjectCtor(ctx); -} - -String *ExecutionEngine::newString(const QString &s) -{ - return new String(s); -} - -Object *ExecutionEngine::newStringObject(const Value &value) -{ - StringObject *object = new StringObject(value); - object->prototype = stringPrototype; - return object; -} - -FunctionObject *ExecutionEngine::newStringCtor(ExecutionContext *ctx) -{ - return new StringCtor(ctx); -} - -Object *ExecutionEngine::newNumberObject(const Value &value) -{ - NumberObject *object = new NumberObject(value); - object->prototype = numberPrototype; - return object; -} - -FunctionObject *ExecutionEngine::newNumberCtor(ExecutionContext *ctx) -{ - return new NumberCtor(ctx); -} - -Object *ExecutionEngine::newBooleanObject(const Value &value) -{ - Object *object = new BooleanObject(value); - object->prototype = booleanPrototype; - return object; -} - -FunctionObject *ExecutionEngine::newBooleanCtor(ExecutionContext *ctx) -{ - return new BooleanCtor(ctx); -} - -Object *ExecutionEngine::newFunctionObject(ExecutionContext *ctx) -{ - Object *object = new FunctionObject(ctx); - object->prototype = functionPrototype; - return object; -} - -FunctionObject *ExecutionEngine::newFunctionCtor(ExecutionContext *ctx) -{ - return new FunctionCtor(ctx); -} - -Object *ExecutionEngine::newArrayObject() -{ - ArrayObject *object = new ArrayObject(); - object->prototype = arrayPrototype; - return object; -} - -Object *ExecutionEngine::newArrayObject(const Array &value) -{ - ArrayObject *object = new ArrayObject(value); - object->prototype = arrayPrototype; - return object; -} - -FunctionObject *ExecutionEngine::newArrayCtor(ExecutionContext *ctx) -{ - return new ArrayCtor(ctx); -} - -Object *ExecutionEngine::newDateObject(const Value &value) -{ - Object *object = new DateObject(value); - object->prototype = datePrototype; - return object; -} - -FunctionObject *ExecutionEngine::newDateCtor(ExecutionContext *ctx) -{ - return new DateCtor(ctx); -} - -Object *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) -{ - bool global = (flags & IR::RegExp::RegExp_Global); - QRegularExpression::PatternOptions options = 0; - if (flags & IR::RegExp::RegExp_IgnoreCase) - options |= QRegularExpression::CaseInsensitiveOption; - if (flags & IR::RegExp::RegExp_Multiline) - options |= QRegularExpression::MultilineOption; - - Object *object = new RegExpObject(QRegularExpression(pattern, options), global); - object->prototype = regExpPrototype; - return object; -} - -FunctionObject *ExecutionEngine::newRegExpCtor(ExecutionContext *ctx) -{ - return new RegExpCtor(ctx); -} - -Object *ExecutionEngine::newErrorObject(const Value &value) -{ - ErrorObject *object = new ErrorObject(value); - object->prototype = errorPrototype; - return object; -} - -Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) -{ - MathObject *object = new MathObject(ctx); - object->prototype = objectPrototype; - return object; -} - -Object *ExecutionEngine::newActivationObject(ExecutionContext *ctx) -{ - return new ActivationObject(ctx); -} - -Object *ExecutionEngine::newForEachIteratorObject(Object *o) -{ - return new ForEachIteratorObject(o); -} diff --git a/qmljs_objects.h b/qmljs_objects.h index 963b7638c6..ce26089e8f 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -42,6 +42,8 @@ #define QMLJS_OBJECTS_H #include "qmljs_runtime.h" +#include "qmljs_engine.h" +#include "qmljs_environment.h" #include "qv4array_p.h" #include "qv4codegen_p.h" @@ -50,7 +52,6 @@ #include #include #include -#include namespace QQmlJS { @@ -600,102 +601,6 @@ struct ArgumentsObject: Object { virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); }; -struct ExecutionEngine -{ - ExecutionContext *rootContext; - Value globalObject; - - Value objectCtor; - Value stringCtor; - Value numberCtor; - Value booleanCtor; - Value arrayCtor; - Value functionCtor; - Value dateCtor; - Value regExpCtor; - Value errorCtor; - Value evalErrorCtor; - Value rangeErrorCtor; - Value referenceErrorCtor; - Value syntaxErrorCtor; - Value typeErrorCtor; - Value uRIErrorCtor; - - ObjectPrototype *objectPrototype; - StringPrototype *stringPrototype; - NumberPrototype *numberPrototype; - BooleanPrototype *booleanPrototype; - ArrayPrototype *arrayPrototype; - FunctionPrototype *functionPrototype; - DatePrototype *datePrototype; - RegExpPrototype *regExpPrototype; - ErrorPrototype *errorPrototype; - EvalErrorPrototype *evalErrorPrototype; - RangeErrorPrototype *rangeErrorPrototype; - ReferenceErrorPrototype *referenceErrorPrototype; - SyntaxErrorPrototype *syntaxErrorPrototype; - TypeErrorPrototype *typeErrorPrototype; - URIErrorPrototype *uRIErrorPrototype; - - QHash identifiers; - - String *id_length; - String *id_prototype; - String *id_constructor; - String *id_arguments; - String *id___proto__; - - struct ExceptionHandler { - ExecutionContext *context; - const uchar *code; // Interpreter state - int targetTempIndex; // Interpreter state - jmp_buf stackFrame; - }; - - QVector unwindStack; - - ExecutionEngine(); - - ExecutionContext *newContext(); - - String *identifier(const QString &s); - - FunctionObject *newNativeFunction(ExecutionContext *scope, void (*code)(ExecutionContext *)); - FunctionObject *newScriptFunction(ExecutionContext *scope, IR::Function *function); - - Object *newObject(); - FunctionObject *newObjectCtor(ExecutionContext *ctx); - - String *newString(const QString &s); - Object *newStringObject(const Value &value); - FunctionObject *newStringCtor(ExecutionContext *ctx); - - Object *newNumberObject(const Value &value); - FunctionObject *newNumberCtor(ExecutionContext *ctx); - - Object *newBooleanObject(const Value &value); - FunctionObject *newBooleanCtor(ExecutionContext *ctx); - - Object *newFunctionObject(ExecutionContext *ctx); - FunctionObject *newFunctionCtor(ExecutionContext *ctx); - - Object *newArrayObject(); - Object *newArrayObject(const Array &value); - FunctionObject *newArrayCtor(ExecutionContext *ctx); - - Object *newDateObject(const Value &value); - FunctionObject *newDateCtor(ExecutionContext *ctx); - - Object *newRegExpObject(const QString &pattern, int flags); - FunctionObject *newRegExpCtor(ExecutionContext *ctx); - - Object *newErrorObject(const Value &value); - Object *newMathObject(ExecutionContext *ctx); - Object *newActivationObject(ExecutionContext *ctx); - - Object *newForEachIteratorObject(Object *o); -}; - } // namespace VM } // namespace QQmlJS diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index ec97864192..d660aaf87b 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -230,138 +230,6 @@ Value Value::property(ExecutionContext *ctx, String *name) const return isObject() ? objectValue()->__get__(ctx, name) : undefinedValue(); } -void ExecutionContext::init(ExecutionEngine *eng) -{ - engine = eng; - parent = 0; - arguments = 0; - argumentCount = 0; - locals = 0; - activation = 0; - thisObject = Value::nullValue(); - result = Value::undefinedValue(); - formals = 0; - formalCount = 0; - vars = 0; - varCount = 0; -} - -PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp) -{ - for (ExecutionContext *ctx = this; ctx; ctx = ctx->parent) { - if (ctx->activation) { - if (PropertyDescriptor *pd = ctx->activation->__getPropertyDescriptor__(this, name, tmp)) - return pd; - } - } - return 0; -} - -void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) -{ - for (ExecutionContext *ctx = this; ctx; ctx = ctx->parent) { - if (ctx->activation) { - if (ctx->activation->inplaceBinOp(value, name, op, this)) - return; - } - } - throwReferenceError(Value::fromString(name)); -} - -void ExecutionContext::throwError(Value value) -{ - result = value; - __qmljs_builtin_throw(value, this); -} - -void ExecutionContext::throwError(const QString &message) -{ - Value v = Value::fromString(this, message); - throwError(Value::fromObject(engine->newErrorObject(v))); -} - -void ExecutionContext::throwTypeError() -{ - Value v = Value::fromString(this, QStringLiteral("Type error")); - throwError(Value::fromObject(engine->newErrorObject(v))); -} - -void ExecutionContext::throwUnimplemented(const QString &message) -{ - Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message); - throwError(Value::fromObject(engine->newErrorObject(v))); -} - -void ExecutionContext::throwReferenceError(Value value) -{ - String *s = value.toString(this); - QString msg = s->toQString() + QStringLiteral(" is not defined"); - throwError(Value::fromObject(engine->newErrorObject(Value::fromString(this, msg)))); -} - -void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc) -{ - engine = parent->engine; - this->parent = f->scope; - assert(this->parent == f->scope); - result = Value::undefinedValue(); - - if (f->needsActivation) - activation = engine->newActivationObject(this); - else - activation = 0; - - thisObject = that; - - formals = f->formalParameterList; - formalCount = f->formalParameterCount; - arguments = args; - argumentCount = argc; - if (f->needsActivation || argc < formalCount){ - arguments = new Value[qMax(argc, formalCount)]; - if (argc) - std::copy(args, args + argc, arguments); - if (argc < formalCount) - std::fill(arguments + argc, arguments + formalCount, Value::undefinedValue()); - } - vars = f->varList; - varCount = f->varCount; - locals = varCount ? new Value[varCount] : 0; - if (varCount) - std::fill(locals, locals + varCount, Value::undefinedValue()); -} - -void ExecutionContext::leaveCallContext() -{ - if (activation) { - delete[] locals; - locals = 0; - } -} - -void ExecutionContext::initConstructorContext(ExecutionContext *parent, Value that, FunctionObject *f, Value *args, unsigned argc) -{ - initCallContext(parent, that, f, args, argc); -} - -void ExecutionContext::leaveConstructorContext(FunctionObject *f) -{ - wireUpPrototype(f); - leaveCallContext(); -} - -void ExecutionContext::wireUpPrototype(FunctionObject *f) -{ - assert(thisObject.isObject()); - result = thisObject; - - Value proto = f->__get__(this, engine->id_prototype); - thisObject.objectValue()->prototype = proto.objectValue(); - if (! thisObject.isObject()) - thisObject.objectValue()->prototype = engine->objectPrototype; - -} - extern "C" { Value __qmljs_init_closure(IR::Function *clos, ExecutionContext *ctx) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 98c628cf9c..56cc95334d 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -570,47 +570,6 @@ inline bool Value::sameValue(Value other) { #include -struct ExecutionContext { - ExecutionEngine *engine; - ExecutionContext *parent; - Object *activation; - Value thisObject; - Value *arguments; - unsigned int argumentCount; - Value *locals; - Value result; - String **formals; - unsigned int formalCount; - String **vars; - unsigned int varCount; - - PropertyDescriptor *lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp); - void inplaceBitOp(Value value, String *name, BinOp op); - - inline Value argument(unsigned int index = 0) - { - if (index < argumentCount) - return arguments[index]; - return Value::undefinedValue(); - } - - void init(ExecutionEngine *eng); - - void initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc); - void leaveCallContext(); - - void initConstructorContext(ExecutionContext *parent, Value that, FunctionObject *f, Value *args, unsigned argc); - void leaveConstructorContext(FunctionObject *f); - void wireUpPrototype(FunctionObject *f); - - void throwError(Value value); - void throwError(const QString &message); - void throwTypeError(); - void throwReferenceError(Value value); - void throwUnimplemented(const QString &message); -}; - - extern "C" { diff --git a/v4.pro b/v4.pro index c2790f044f..159e4d8967 100644 --- a/v4.pro +++ b/v4.pro @@ -13,6 +13,8 @@ LIBS += -rdynamic SOURCES += main.cpp \ qv4codegen.cpp \ qv4ir.cpp \ + qmljs_engine.cpp \ + qmljs_environment.cpp \ qmljs_runtime.cpp \ qmljs_objects.cpp \ qv4syntaxchecker.cpp \ @@ -24,6 +26,8 @@ SOURCES += main.cpp \ HEADERS += \ qv4codegen_p.h \ qv4ir_p.h \ + qmljs_engine.h \ + qmljs_environment.h \ qmljs_runtime.h \ qmljs_objects.h \ qmljs_math.h \ -- cgit v1.2.3 From 1162d4dde662908e3affe0bdcd45afb26676a52d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 17 Nov 2012 22:10:52 +0100 Subject: Move Value into it's own header/cpp file Change-Id: I2872d824a2016a653e64f0332f6416f416eef0e7 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 118 -------------------- qmljs_runtime.h | 311 +-------------------------------------------------- qmljs_value.cpp | 226 +++++++++++++++++++++++++++++++++++++ qmljs_value.h | 325 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ v4.pro | 2 + 5 files changed, 555 insertions(+), 427 deletions(-) create mode 100644 qmljs_value.cpp create mode 100644 qmljs_value.h diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index d660aaf87b..44226fb593 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -112,124 +112,6 @@ QString numberToString(double num, int radix = 10) return str; } - -Value Value::fromString(ExecutionContext *ctx, const QString &s) -{ - return fromString(ctx->engine->newString(s)); -} - -int Value::toInt32(double number) -{ - const double D32 = 4294967296.0; - const double D31 = D32 / 2.0; - - if ((number >= -D31 && number < D31)) - return static_cast(number); - - - if (!std::isfinite(number)) - return 0; - - double d = ::floor(::fabs(number)); - if (std::signbit(number)) - d = -d; - - number = ::fmod(d , D32); - - if (number < -D31) - number += D32; - else if (number >= D31) - number -= D32; - - return int(number); -} - -unsigned int Value::toUInt32(double number) -{ - const double D32 = 4294967296.0; - if ((number >= 0 && number < D32)) - return static_cast(number); - - if (!std::isfinite(number)) - return +0; - - double d = ::floor(::fabs(number)); - if (std::signbit(number)) - d = -d; - - number = ::fmod(d , D32); - - if (number < 0) - number += D32; - - return unsigned(number); -} - -double Value::toInteger(double number) -{ - if (std::isnan(number)) - return +0; - else if (! number || std::isinf(number)) - return number; - const double v = floor(fabs(number)); - return std::signbit(number) ? -v : v; -} - -Object *Value::asObject() const -{ - return isObject() ? objectValue() : 0; -} - -FunctionObject *Value::asFunctionObject() const -{ - return isObject() ? objectValue()->asFunctionObject() : 0; -} - -BooleanObject *Value::asBooleanObject() const -{ - return isObject() ? objectValue()->asBooleanObject() : 0; -} - -NumberObject *Value::asNumberObject() const -{ - return isObject() ? objectValue()->asNumberObject() : 0; -} - -StringObject *Value::asStringObject() const -{ - return isObject() ? objectValue()->asStringObject() : 0; -} - -DateObject *Value::asDateObject() const -{ - return isObject() ? objectValue()->asDateObject() : 0; -} - -RegExpObject *Value::asRegExpObject() const -{ - return isObject() ? objectValue()->asRegExpObject() : 0; -} - -ArrayObject *Value::asArrayObject() const -{ - return isObject() ? objectValue()->asArrayObject() : 0; -} - -ErrorObject *Value::asErrorObject() const -{ - return isObject() ? objectValue()->asErrorObject() : 0; -} - -ActivationObject *Value::asArgumentsObject() const -{ - return isObject() ? objectValue()->asActivationObject() : 0; -} - -Value Value::property(ExecutionContext *ctx, String *name) const -{ - return isObject() ? objectValue()->__get__(ctx, name) : undefinedValue(); -} - extern "C" { Value __qmljs_init_closure(IR::Function *clos, ExecutionContext *ctx) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 56cc95334d..549c8a4b91 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -41,12 +41,12 @@ #ifndef QMLJS_RUNTIME_H #define QMLJS_RUNTIME_H +#include + #include #include #include -#include - #include #include @@ -87,185 +87,6 @@ struct ErrorObject; struct ActivationObject; struct ExecutionEngine; -typedef uint Bool; - -struct Value -{ - union { - quint64 val; - double dbl; - struct { -#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN - uint tag; -#endif - union { - uint uint_32; - int int_32; -#if CPU(X86_64) -#else - Object *o; - String *s; -#endif - }; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - uint tag; -#endif - }; - }; - - enum Masks { - NotDouble_Mask = 0xfff80000, - Type_Mask = 0xffff0000, - Immediate_Mask = NotDouble_Mask | 0x00040000, - Tag_Shift = 32 - }; - enum ValueType { - Undefined_Type = Immediate_Mask | 0x00000, - Null_Type = Immediate_Mask | 0x10000, - Boolean_Type = Immediate_Mask | 0x20000, - Integer_Type = Immediate_Mask | 0x30000, - Object_Type = NotDouble_Mask | 0x00000, - String_Type = NotDouble_Mask | 0x10000 - }; - - enum ImmediateFlags { - ConvertibleToInt = Immediate_Mask | (0x1 << 15) - }; - - enum ValueTypeInternal { - _Undefined_Type = Undefined_Type, - _Null_Type = Null_Type | ConvertibleToInt, - _Boolean_Type = Boolean_Type | ConvertibleToInt, - _Integer_Type = Integer_Type | ConvertibleToInt, - _Object_Type = Object_Type, - _String_Type = String_Type - - }; - - inline ValueType type() const { - return (ValueType)(tag & Type_Mask); - } - - inline bool isUndefined() const { return tag == _Undefined_Type; } - inline bool isNull() const { return tag == _Null_Type; } - inline bool isBoolean() const { return tag == _Boolean_Type; } - inline bool isInteger() const { return tag == _Integer_Type; } - inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } - inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } -#if CPU(X86_64) - inline bool isString() const { return (tag & Type_Mask) == String_Type; } - inline bool isObject() const { return (tag & Type_Mask) == Object_Type; } -#else - inline bool isString() const { return tag == String_Type; } - inline bool isObject() const { return tag == Object_Type; } -#endif - inline bool isConvertibleToInt() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } - - Bool booleanValue() const { - return int_32; - } - double doubleValue() const { - return dbl; - } - void setDouble(double d) { - dbl = d; - } - double asDouble() const { - if (tag == _Integer_Type) - return int_32; - return dbl; - } - int integerValue() const { - return int_32; - } - -#if CPU(X86_64) - String *stringValue() const { - return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift)); - } - Object *objectValue() const { - return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift)); - } -#else - String *stringValue() const { - return s; - } - Object *objectValue() const { - return o; - } -#endif - - quint64 rawValue() const { - return val; - } - - static Value undefinedValue(); - static Value nullValue(); - static Value fromBoolean(Bool b); - static Value fromDouble(double d); - static Value fromInt32(int i); - static Value fromString(String *s); - static Value fromObject(Object *o); - -#ifndef QMLJS_LLVM_RUNTIME - static Value fromString(ExecutionContext *ctx, const QString &fromString); -#endif - - static double toInteger(double fromNumber); - static int toInt32(double value); - static unsigned int toUInt32(double value); - - inline int toUInt16(ExecutionContext *ctx); - inline int toInt32(ExecutionContext *ctx); - inline unsigned int toUInt32(ExecutionContext *ctx); - inline Bool toBoolean(ExecutionContext *ctx) const; - inline double toInteger(ExecutionContext *ctx) const; - double toNumber(ExecutionContext *ctx) const; - inline String *toString(ExecutionContext *ctx) const; - inline Value toObject(ExecutionContext *ctx) const; - - inline bool isPrimitive() const { return !isObject(); } -#if CPU(X86_64) - static inline bool integerCompatible(Value a, Value b) { - const quint64 mask = quint64(ConvertibleToInt) << 32; - return ((a.val & b.val) & mask) == mask; - } - static inline bool bothDouble(Value a, Value b) { - const quint64 mask = quint64(NotDouble_Mask) << 32; - return ((a.val | b.val) & mask) != mask; - } -#else - static inline bool integerCompatible(Value a, Value b) { - return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; - } - static inline bool bothDouble(Value a, Value b) { - return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask; - } -#endif - inline bool tryIntegerConversion() { - bool b = isConvertibleToInt(); - if (b) - tag = _Integer_Type; - return b; - } - - Object *asObject() const; - FunctionObject *asFunctionObject() const; - BooleanObject *asBooleanObject() const; - NumberObject *asNumberObject() const; - StringObject *asStringObject() const; - DateObject *asDateObject() const; - RegExpObject *asRegExpObject() const; - ArrayObject *asArrayObject() const; - ErrorObject *asErrorObject() const; - ActivationObject *asArgumentsObject() const; - - Value property(ExecutionContext *ctx, String *name) const; - - // Section 9.12 - bool sameValue(Value other); -}; - extern "C" { // context @@ -439,134 +260,6 @@ Bool __qmljs_cmp_in(Value left, Value right, ExecutionContext *ctx); } // extern "C" -inline int Value::toUInt16(ExecutionContext *ctx) -{ - return __qmljs_to_uint16(*this, ctx); -} - -inline int Value::toInt32(ExecutionContext *ctx) -{ - if (isConvertibleToInt()) - return int_32; - - return Value::toInt32(__qmljs_to_number(*this, ctx)); -} - -inline unsigned int Value::toUInt32(ExecutionContext *ctx) -{ - if (isConvertibleToInt()) - return (unsigned) int_32; - - return toUInt32(__qmljs_to_number(*this, ctx)); -} - -inline Bool Value::toBoolean(ExecutionContext *ctx) const -{ - return __qmljs_to_boolean(*this, ctx); -} - -inline double Value::toInteger(ExecutionContext *ctx) const -{ - return __qmljs_to_integer(*this, ctx); -} - -inline double Value::toNumber(ExecutionContext *ctx) const -{ - return __qmljs_to_number(*this, ctx); -} - -inline String *Value::toString(ExecutionContext *ctx) const -{ - Value v = __qmljs_to_string(*this, ctx); - assert(v.isString()); - return v.stringValue(); -} - -inline Value Value::toObject(ExecutionContext *ctx) const -{ - return __qmljs_to_object(*this, ctx); -} - -inline Value Value::undefinedValue() -{ - Value v; -#if CPU(X86_64) - v.val = quint64(_Undefined_Type) << Tag_Shift; -#else - v.tag = _Undefined_Type; - v.int_32 = 0; -#endif - return v; -} - -inline Value Value::nullValue() -{ - Value v; -#if CPU(X86_64) - v.val = quint64(_Null_Type) << Tag_Shift; -#else - v.tag = _Null_Type; - v.int_32 = 0; -#endif - return v; -} - -inline Value Value::fromBoolean(Bool b) -{ - Value v; - v.tag = _Boolean_Type; - v.int_32 = (bool)b; - return v; -} - -inline Value Value::fromDouble(double d) -{ - Value v; - v.dbl = d; - return v; -} - -inline Value Value::fromInt32(int i) -{ - Value v; - v.tag = _Integer_Type; - v.int_32 = i; - return v; -} - -inline Value Value::fromString(String *s) -{ - Value v; -#if CPU(X86_64) - v.val = (quint64)s; - v.val |= quint64(_String_Type) << Tag_Shift; -#else - v.tag = _String_Type; - v.s = s; -#endif - return v; -} - -inline Value Value::fromObject(Object *o) -{ - Value v; -#if CPU(X86_64) - v.val = (quint64)o; - v.val |= quint64(_Object_Type) << Tag_Shift; -#else - v.tag = _Object_Type; - v.o = o; -#endif - return v; -} - -inline bool Value::sameValue(Value other) { - if (val == other.val) - return true; - if (isString() && other.isString()) - return __qmljs_string_equal(stringValue(), other.stringValue()); - return false; -} #include diff --git a/qmljs_value.cpp b/qmljs_value.cpp new file mode 100644 index 0000000000..cbf481399b --- /dev/null +++ b/qmljs_value.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include + +namespace QQmlJS { +namespace VM { + + +int Value::toUInt16(ExecutionContext *ctx) +{ + return __qmljs_to_uint16(*this, ctx); +} + +int Value::toInt32(ExecutionContext *ctx) +{ + if (isConvertibleToInt()) + return int_32; + + return Value::toInt32(__qmljs_to_number(*this, ctx)); +} + +unsigned int Value::toUInt32(ExecutionContext *ctx) +{ + if (isConvertibleToInt()) + return (unsigned) int_32; + + return toUInt32(__qmljs_to_number(*this, ctx)); +} + +Bool Value::toBoolean(ExecutionContext *ctx) const +{ + return __qmljs_to_boolean(*this, ctx); +} + +double Value::toInteger(ExecutionContext *ctx) const +{ + return __qmljs_to_integer(*this, ctx); +} + +double Value::toNumber(ExecutionContext *ctx) const +{ + return __qmljs_to_number(*this, ctx); +} + +String *Value::toString(ExecutionContext *ctx) const +{ + Value v = __qmljs_to_string(*this, ctx); + assert(v.isString()); + return v.stringValue(); +} + +Value Value::toObject(ExecutionContext *ctx) const +{ + return __qmljs_to_object(*this, ctx); +} + + +bool Value::sameValue(Value other) { + if (val == other.val) + return true; + if (isString() && other.isString()) + return __qmljs_string_equal(stringValue(), other.stringValue()); + return false; +} + +Value Value::fromString(ExecutionContext *ctx, const QString &s) +{ + return fromString(ctx->engine->newString(s)); +} + +int Value::toInt32(double number) +{ + const double D32 = 4294967296.0; + const double D31 = D32 / 2.0; + + if ((number >= -D31 && number < D31)) + return static_cast(number); + + + if (!std::isfinite(number)) + return 0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D32); + + if (number < -D31) + number += D32; + else if (number >= D31) + number -= D32; + + return int(number); +} + +unsigned int Value::toUInt32(double number) +{ + const double D32 = 4294967296.0; + if ((number >= 0 && number < D32)) + return static_cast(number); + + if (!std::isfinite(number)) + return +0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D32); + + if (number < 0) + number += D32; + + return unsigned(number); +} + +double Value::toInteger(double number) +{ + if (std::isnan(number)) + return +0; + else if (! number || std::isinf(number)) + return number; + const double v = floor(fabs(number)); + return std::signbit(number) ? -v : v; +} + +Object *Value::asObject() const +{ + return isObject() ? objectValue() : 0; +} + +FunctionObject *Value::asFunctionObject() const +{ + return isObject() ? objectValue()->asFunctionObject() : 0; +} + +BooleanObject *Value::asBooleanObject() const +{ + return isObject() ? objectValue()->asBooleanObject() : 0; +} + +NumberObject *Value::asNumberObject() const +{ + return isObject() ? objectValue()->asNumberObject() : 0; +} + +StringObject *Value::asStringObject() const +{ + return isObject() ? objectValue()->asStringObject() : 0; +} + +DateObject *Value::asDateObject() const +{ + return isObject() ? objectValue()->asDateObject() : 0; +} + +RegExpObject *Value::asRegExpObject() const +{ + return isObject() ? objectValue()->asRegExpObject() : 0; +} + +ArrayObject *Value::asArrayObject() const +{ + return isObject() ? objectValue()->asArrayObject() : 0; +} + +ErrorObject *Value::asErrorObject() const +{ + return isObject() ? objectValue()->asErrorObject() : 0; +} + +ActivationObject *Value::asArgumentsObject() const +{ + return isObject() ? objectValue()->asActivationObject() : 0; +} + +Value Value::property(ExecutionContext *ctx, String *name) const +{ + return isObject() ? objectValue()->__get__(ctx, name) : undefinedValue(); +} + + + +} // namespace VM +} // namespace QQmlJS diff --git a/qmljs_value.h b/qmljs_value.h new file mode 100644 index 0000000000..b504af0889 --- /dev/null +++ b/qmljs_value.h @@ -0,0 +1,325 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_VALUE_H +#define QMLJS_VALUE_H + +#include + +#include +#include +#include + +namespace QQmlJS { +namespace VM { + +struct Array; +struct String; +struct Object; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct RegExpObject; +struct ErrorObject; +struct ActivationObject; +struct ArgumentsObject; +struct ExecutionContext; +struct ExecutionEngine; + +typedef uint Bool; + + +struct Value +{ + union { + quint64 val; + double dbl; + struct { +#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN + uint tag; +#endif + union { + uint uint_32; + int int_32; +#if CPU(X86_64) +#else + Object *o; + String *s; +#endif + }; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + uint tag; +#endif + }; + }; + + enum Masks { + NotDouble_Mask = 0xfff80000, + Type_Mask = 0xffff0000, + Immediate_Mask = NotDouble_Mask | 0x00040000, + Tag_Shift = 32 + }; + enum ValueType { + Undefined_Type = Immediate_Mask | 0x00000, + Null_Type = Immediate_Mask | 0x10000, + Boolean_Type = Immediate_Mask | 0x20000, + Integer_Type = Immediate_Mask | 0x30000, + Object_Type = NotDouble_Mask | 0x00000, + String_Type = NotDouble_Mask | 0x10000 + }; + + enum ImmediateFlags { + ConvertibleToInt = Immediate_Mask | (0x1 << 15) + }; + + enum ValueTypeInternal { + _Undefined_Type = Undefined_Type, + _Null_Type = Null_Type | ConvertibleToInt, + _Boolean_Type = Boolean_Type | ConvertibleToInt, + _Integer_Type = Integer_Type | ConvertibleToInt, + _Object_Type = Object_Type, + _String_Type = String_Type + + }; + + inline ValueType type() const { + return (ValueType)(tag & Type_Mask); + } + + inline bool isUndefined() const { return tag == _Undefined_Type; } + inline bool isNull() const { return tag == _Null_Type; } + inline bool isBoolean() const { return tag == _Boolean_Type; } + inline bool isInteger() const { return tag == _Integer_Type; } + inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } + inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } +#if CPU(X86_64) + inline bool isString() const { return (tag & Type_Mask) == String_Type; } + inline bool isObject() const { return (tag & Type_Mask) == Object_Type; } +#else + inline bool isString() const { return tag == String_Type; } + inline bool isObject() const { return tag == Object_Type; } +#endif + inline bool isConvertibleToInt() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } + + Bool booleanValue() const { + return int_32; + } + double doubleValue() const { + return dbl; + } + void setDouble(double d) { + dbl = d; + } + double asDouble() const { + if (tag == _Integer_Type) + return int_32; + return dbl; + } + int integerValue() const { + return int_32; + } + +#if CPU(X86_64) + String *stringValue() const { + return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } + Object *objectValue() const { + return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } +#else + String *stringValue() const { + return s; + } + Object *objectValue() const { + return o; + } +#endif + + quint64 rawValue() const { + return val; + } + + static Value undefinedValue(); + static Value nullValue(); + static Value fromBoolean(Bool b); + static Value fromDouble(double d); + static Value fromInt32(int i); + static Value fromString(String *s); + static Value fromObject(Object *o); + +#ifndef QMLJS_LLVM_RUNTIME + static Value fromString(ExecutionContext *ctx, const QString &fromString); +#endif + + static double toInteger(double fromNumber); + static int toInt32(double value); + static unsigned int toUInt32(double value); + + int toUInt16(ExecutionContext *ctx); + int toInt32(ExecutionContext *ctx); + unsigned int toUInt32(ExecutionContext *ctx); + Bool toBoolean(ExecutionContext *ctx) const; + double toInteger(ExecutionContext *ctx) const; + double toNumber(ExecutionContext *ctx) const; + String *toString(ExecutionContext *ctx) const; + Value toObject(ExecutionContext *ctx) const; + + inline bool isPrimitive() const { return !isObject(); } +#if CPU(X86_64) + static inline bool integerCompatible(Value a, Value b) { + const quint64 mask = quint64(ConvertibleToInt) << 32; + return ((a.val & b.val) & mask) == mask; + } + static inline bool bothDouble(Value a, Value b) { + const quint64 mask = quint64(NotDouble_Mask) << 32; + return ((a.val | b.val) & mask) != mask; + } +#else + static inline bool integerCompatible(Value a, Value b) { + return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; + } + static inline bool bothDouble(Value a, Value b) { + return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask; + } +#endif + inline bool tryIntegerConversion() { + bool b = isConvertibleToInt(); + if (b) + tag = _Integer_Type; + return b; + } + + Object *asObject() const; + FunctionObject *asFunctionObject() const; + BooleanObject *asBooleanObject() const; + NumberObject *asNumberObject() const; + StringObject *asStringObject() const; + DateObject *asDateObject() const; + RegExpObject *asRegExpObject() const; + ArrayObject *asArrayObject() const; + ErrorObject *asErrorObject() const; + ActivationObject *asArgumentsObject() const; + + Value property(ExecutionContext *ctx, String *name) const; + + // Section 9.12 + bool sameValue(Value other); +}; + +inline Value Value::undefinedValue() +{ + Value v; +#if CPU(X86_64) + v.val = quint64(_Undefined_Type) << Tag_Shift; +#else + v.tag = _Undefined_Type; + v.int_32 = 0; +#endif + return v; +} + +inline Value Value::nullValue() +{ + Value v; +#if CPU(X86_64) + v.val = quint64(_Null_Type) << Tag_Shift; +#else + v.tag = _Null_Type; + v.int_32 = 0; +#endif + return v; +} + +inline Value Value::fromBoolean(Bool b) +{ + Value v; + v.tag = _Boolean_Type; + v.int_32 = (bool)b; + return v; +} + +inline Value Value::fromDouble(double d) +{ + Value v; + v.dbl = d; + return v; +} + +inline Value Value::fromInt32(int i) +{ + Value v; + v.tag = _Integer_Type; + v.int_32 = i; + return v; +} + +inline Value Value::fromString(String *s) +{ + Value v; +#if CPU(X86_64) + v.val = (quint64)s; + v.val |= quint64(_String_Type) << Tag_Shift; +#else + v.tag = _String_Type; + v.s = s; +#endif + return v; +} + +inline Value Value::fromObject(Object *o) +{ + Value v; +#if CPU(X86_64) + v.val = (quint64)o; + v.val |= quint64(_Object_Type) << Tag_Shift; +#else + v.tag = _Object_Type; + v.o = o; +#endif + return v; +} + +} // namespace VM +} // namespace QQmlJS + +#endif diff --git a/v4.pro b/v4.pro index 159e4d8967..9358bf489e 100644 --- a/v4.pro +++ b/v4.pro @@ -17,6 +17,7 @@ SOURCES += main.cpp \ qmljs_environment.cpp \ qmljs_runtime.cpp \ qmljs_objects.cpp \ + qmljs_value.cpp \ qv4syntaxchecker.cpp \ qv4ecmaobjects.cpp \ qv4array.cpp \ @@ -31,6 +32,7 @@ HEADERS += \ qmljs_runtime.h \ qmljs_objects.h \ qmljs_math.h \ + qmljs_value.h \ qv4syntaxchecker_p.h \ qv4ecmaobjects_p.h \ qv4array_p.h \ -- cgit v1.2.3 From 4f1dfd1e260c238a0f8b1e3c7338431fa2972b90 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 17 Nov 2012 22:14:06 +0100 Subject: Properly namespace the math header Change-Id: I0f4ac76376028081b13b1cdeaadc4c9b09c1556d Reviewed-by: Simon Hausmann --- qmljs_math.h | 6 ++++++ qmljs_runtime.h | 9 +-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/qmljs_math.h b/qmljs_math.h index 873e8932ff..492f7c8692 100644 --- a/qmljs_math.h +++ b/qmljs_math.h @@ -48,6 +48,9 @@ #if !defined(QMLJS_LLVM_RUNTIME) && 1 //CPU(X86_64) +namespace QQmlJS { +namespace VM { + static inline Value add_int32(int a, int b) { quint8 overflow = 0; @@ -97,4 +100,7 @@ static inline Value mul_int32(int a, int b) } #endif +} // namespace VM +} // namespace QQmlJS + #endif diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 549c8a4b91..bc404e9bc0 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -42,6 +42,7 @@ #define QMLJS_RUNTIME_H #include +#include #include #include @@ -258,14 +259,6 @@ Bool __qmljs_cmp_sne(Value left, Value right, ExecutionContext *ctx); Bool __qmljs_cmp_instanceof(Value left, Value right, ExecutionContext *ctx); Bool __qmljs_cmp_in(Value left, Value right, ExecutionContext *ctx); -} // extern "C" - - -#include - - -extern "C" { - // type conversion and testing inline Value __qmljs_to_primitive(Value value, ExecutionContext *ctx, int typeHint) { -- cgit v1.2.3 From a2676884804fb523ee586f1ebc6a11353ec79fcb Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 18 Nov 2012 23:33:06 +0100 Subject: Better handling of contexts and environments Make the ExecutionContext standard compliant. Move most of it's members into a new DeclarativeEnvironment data structure that contains locals and arguments. Change-Id: I094f559168810dbd3717d677fe28750076015976 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 2 +- main.cpp | 4 +- moth/qv4vme_moth.cpp | 8 +-- qmljs_engine.cpp | 4 +- qmljs_engine.h | 5 +- qmljs_environment.cpp | 165 +++++++++++++++++++++++++++++++++++++++----------- qmljs_environment.h | 53 ++++++++++++---- qmljs_objects.cpp | 12 ++-- qmljs_objects.h | 27 ++++++--- qmljs_runtime.cpp | 4 +- qv4ecmaobjects.cpp | 135 +++++++++++++++++++++-------------------- qv4isel_masm.cpp | 6 +- 12 files changed, 279 insertions(+), 146 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index b911ad4880..553f2fd1ac 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -55,7 +55,7 @@ void __qmljs_llvm_return(ExecutionContext *ctx, Value *result) Value *__qmljs_llvm_get_argument(ExecutionContext *ctx, int index) { - return &ctx->arguments[index]; + return &ctx->variableEnvironment->arguments[index]; } void __qmljs_llvm_init_undefined(Value *result) diff --git a/main.cpp b/main.cpp index 1e8acfdaaa..d147d04a91 100644 --- a/main.cpp +++ b/main.cpp @@ -71,7 +71,7 @@ struct Print: FunctionObject virtual void call(ExecutionContext *ctx) { - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { + for (unsigned int i = 0; i < ctx->argumentCount(); ++i) { String *s = ctx->argument(i).toString(ctx); if (i) std::cout << ' '; @@ -89,7 +89,7 @@ struct TestHarnessError: FunctionObject { errorOccurred = true; - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { + for (unsigned int i = 0; i < ctx->argumentCount(); ++i) { String *s = ctx->argument(i).toString(ctx); if (i) std::cerr << ' '; diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index c058002aa2..30a5d3c3a6 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -68,11 +68,11 @@ static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVecto if (index < 0) { const int arg = -index - 1; - return context->arguments + arg; - } else if (index < (int) context->varCount) { - return context->locals + index; + return context->variableEnvironment->arguments + arg; + } else if (index < (int) context->variableEnvironment->varCount) { + return context->variableEnvironment->locals + index; } else { - int off = index - context->varCount; + int off = index - context->variableEnvironment->varCount; return stack.data() + off; } } diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 9229dbd9a2..8097639e54 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -139,7 +139,7 @@ ExecutionEngine::ExecutionEngine() // VM::Object *glo = newObject(/*rootContext*/); globalObject = Value::fromObject(glo); - rootContext->activation = glo; + rootContext->variableEnvironment->activation = glo; glo->__put__(rootContext, identifier(QStringLiteral("Object")), objectCtor); glo->__put__(rootContext, identifier(QStringLiteral("String")), stringCtor); @@ -324,7 +324,7 @@ Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) return object; } -Object *ExecutionEngine::newActivationObject(ExecutionContext *ctx) +Object *ExecutionEngine::newActivationObject(DeclarativeEnvironment *ctx) { return new ActivationObject(ctx); } diff --git a/qmljs_engine.h b/qmljs_engine.h index ed4b0d4a16..f111979f0d 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -42,6 +42,7 @@ #define QMLJS_ENGINE_H #include +#include #include namespace QQmlJS { @@ -81,7 +82,9 @@ struct URIErrorPrototype; struct ExecutionEngine { + ExecutionContext *current; ExecutionContext *rootContext; + Value globalObject; Value objectCtor; @@ -170,7 +173,7 @@ struct ExecutionEngine Object *newErrorObject(const Value &value); Object *newMathObject(ExecutionContext *ctx); - Object *newActivationObject(ExecutionContext *ctx); + Object *newActivationObject(DeclarativeEnvironment *ctx); Object *newForEachIteratorObject(Object *o); }; diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 550d619fe1..338006474e 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -45,25 +45,134 @@ namespace QQmlJS { namespace VM { -void ExecutionContext::init(ExecutionEngine *eng) +DeclarativeEnvironment::DeclarativeEnvironment(ExecutionEngine *e) { - engine = eng; - parent = 0; + engine = e; + outer = 0; arguments = 0; argumentCount = 0; locals = 0; activation = 0; - thisObject = Value::nullValue(); - result = Value::undefinedValue(); formals = 0; formalCount = 0; vars = 0; varCount = 0; } +DeclarativeEnvironment::DeclarativeEnvironment(FunctionObject *f, Value *args, uint argc) +{ + outer = f->scope; + engine = outer->engine; + + if (f->needsActivation) + activation = engine->newActivationObject(this); + else + activation = 0; + + formals = f->formalParameterList; + formalCount = f->formalParameterCount; + arguments = args; + argumentCount = argc; + if (f->needsActivation || argc < formalCount){ + arguments = new Value[qMax(argc, formalCount)]; + if (argc) + std::copy(args, args + argc, arguments); + if (argc < formalCount) + std::fill(arguments + argc, arguments + formalCount, Value::undefinedValue()); + } + vars = f->varList; + varCount = f->varCount; + locals = varCount ? new Value[varCount] : 0; + if (varCount) + std::fill(locals, locals + varCount, Value::undefinedValue()); +} + +bool DeclarativeEnvironment::hasBinding(String *name) const +{ + for (unsigned int i = 0; i < varCount; ++i) { + if (__qmljs_string_equal(vars[i], name)) + return true; + } + for (unsigned int i = 0; i < formalCount; ++i) { + if (__qmljs_string_equal(formals[i], name)) + return true; + } + return deletableLocals.contains(name->toQString()); +} + +void DeclarativeEnvironment::createMutableBinding(String *name, bool deletable) +{ + // all non deletable vars should get created at compile time + assert(deletable); + assert(!hasBinding(name)); + + deletableLocals.insert(name->toQString(), Value::undefinedValue()); +} + +void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool strict) +{ + // ### throw if strict is true, and it would change an immutable binding + for (unsigned int i = 0; i < varCount; ++i) { + if (__qmljs_string_equal(vars[i], name)) { + locals[i] = value; + return; + } + } + for (unsigned int i = 0; i < formalCount; ++i) { + if (__qmljs_string_equal(formals[i], name)) { + arguments[i] = value; + return; + } + } + QHash::iterator it = deletableLocals.find(name->toQString()); + if (it != deletableLocals.end()) { + *it = value; + return; + } + assert(false); +} + +Value DeclarativeEnvironment::getBindingValue(String *name, bool strict) const +{ + for (unsigned int i = 0; i < varCount; ++i) { + if (__qmljs_string_equal(vars[i], name)) + return locals[i]; + } + for (unsigned int i = 0; i < formalCount; ++i) { + if (__qmljs_string_equal(formals[i], name)) + return arguments[i]; + } + QHash::const_iterator it = deletableLocals.find(name->toQString()); + if (it != deletableLocals.end()) + return *it; + + assert(false); +} + +bool DeclarativeEnvironment::deleteBinding(String *name) +{ + QHash::iterator it = deletableLocals.find(name->toQString()); + if (it != deletableLocals.end()) { + deletableLocals.erase(it); + return true; + } + return !hasBinding(name); +} + + +void ExecutionContext::init(ExecutionEngine *eng) +{ + engine = eng; + parent = 0; + variableEnvironment = new DeclarativeEnvironment(eng); + lexicalEnvironment = variableEnvironment; + thisObject = Value::nullValue(); + result = Value::undefinedValue(); +} + PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp) { - for (ExecutionContext *ctx = this; ctx; ctx = ctx->parent) { + for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer) { if (ctx->activation) { if (PropertyDescriptor *pd = ctx->activation->__getPropertyDescriptor__(this, name, tmp)) return pd; @@ -74,7 +183,7 @@ PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, Pro void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) { - for (ExecutionContext *ctx = this; ctx; ctx = ctx->parent) { + for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer) { if (ctx->activation) { if (ctx->activation->inplaceBinOp(value, name, op, this)) return; @@ -117,40 +226,21 @@ void ExecutionContext::throwReferenceError(Value value) void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc) { engine = parent->engine; - this->parent = f->scope; - assert(this->parent == f->scope); - result = Value::undefinedValue(); + this->parent = parent; - if (f->needsActivation) - activation = engine->newActivationObject(this); - else - activation = 0; + variableEnvironment = new DeclarativeEnvironment(f, args, argc); + lexicalEnvironment = variableEnvironment; thisObject = that; - - formals = f->formalParameterList; - formalCount = f->formalParameterCount; - arguments = args; - argumentCount = argc; - if (f->needsActivation || argc < formalCount){ - arguments = new Value[qMax(argc, formalCount)]; - if (argc) - std::copy(args, args + argc, arguments); - if (argc < formalCount) - std::fill(arguments + argc, arguments + formalCount, Value::undefinedValue()); - } - vars = f->varList; - varCount = f->varCount; - locals = varCount ? new Value[varCount] : 0; - if (varCount) - std::fill(locals, locals + varCount, Value::undefinedValue()); + result = Value::undefinedValue(); } void ExecutionContext::leaveCallContext() { - if (activation) { - delete[] locals; - locals = 0; + // ## Should rather be handled by a the activation object having a ref to the environment + if (variableEnvironment->activation) { + delete[] variableEnvironment->locals; + variableEnvironment->locals = 0; } } @@ -171,11 +261,12 @@ void ExecutionContext::wireUpPrototype(FunctionObject *f) result = thisObject; Value proto = f->__get__(this, engine->id_prototype); - thisObject.objectValue()->prototype = proto.objectValue(); - if (! thisObject.isObject()) + if (proto.isObject()) + thisObject.objectValue()->prototype = proto.objectValue(); + else thisObject.objectValue()->prototype = engine->objectPrototype; - } + } // namespace VM } // namespace QQmlJS diff --git a/qmljs_environment.h b/qmljs_environment.h index 74568cdf25..bbb5cc4839 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -50,30 +50,47 @@ struct Value; struct Object; struct ExecutionEngine; struct ExecutionContext; +struct DeclarativeEnvironment; -struct ExecutionContext { +// This merges LexicalEnvironment and EnvironmentRecord from +// Sec. 10.2 into one class +struct DeclarativeEnvironment +{ ExecutionEngine *engine; - ExecutionContext *parent; + DeclarativeEnvironment *outer; + Object *activation; - Value thisObject; Value *arguments; unsigned int argumentCount; Value *locals; - Value result; String **formals; unsigned int formalCount; String **vars; unsigned int varCount; - PropertyDescriptor *lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp); - void inplaceBitOp(Value value, String *name, BinOp op); + // these get used for createMutableBinding(..., true). + // the only place this is being used is eval(...) + QHash deletableLocals; - inline Value argument(unsigned int index = 0) - { - if (index < argumentCount) - return arguments[index]; - return Value::undefinedValue(); - } + DeclarativeEnvironment(ExecutionEngine *e); + DeclarativeEnvironment(FunctionObject *f, Value *args, uint argc); + + bool hasBinding(String *name) const; + void createMutableBinding(String *name, bool deletable); + void setMutableBinding(String *name, Value value, bool strict); + Value getBindingValue(String *name, bool strict) const; + bool deleteBinding(String *name); +}; + +struct ExecutionContext +{ + ExecutionEngine *engine; + ExecutionContext *parent; + // ### Should be a general environment + DeclarativeEnvironment *lexicalEnvironment; + DeclarativeEnvironment *variableEnvironment; + Value thisObject; + Value result; void init(ExecutionEngine *eng); @@ -89,9 +106,21 @@ struct ExecutionContext { void throwTypeError(); void throwReferenceError(Value value); void throwUnimplemented(const QString &message); + + PropertyDescriptor *lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp); + void inplaceBitOp(Value value, String *name, BinOp op); + + inline uint argumentCount() const { return variableEnvironment->argumentCount; } + inline Value argument(unsigned int index = 0) + { + if (index < variableEnvironment->argumentCount) + return variableEnvironment->arguments[index]; + return Value::undefinedValue(); + } }; + } // namespace VM } // namespace QQmlJS diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 7552a83a03..4f831b01bf 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -554,11 +554,11 @@ int EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fil return EXIT_FAILURE; } - if (!ctx->activation) - ctx->activation = new QQmlJS::VM::Object(); + if (!ctx->variableEnvironment->activation) + ctx->variableEnvironment->activation = new QQmlJS::VM::Object(); foreach (const QString *local, globalCode->locals) { - ctx->activation->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); + ctx->variableEnvironment->activation->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); } if (mode == Codegen::GlobalCode) { @@ -668,7 +668,7 @@ PropertyDescriptor *ActivationObject::__getPropertyDescriptor__(ExecutionContext Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name) { if (name->isEqualTo(ctx->engine->id_length)) - return Value::fromDouble(context->argumentCount); + return Value::fromInt32(context->argumentCount()); return Object::__get__(ctx, name); } @@ -676,8 +676,8 @@ PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(ExecutionContext { if (context) { const quint32 i = Value::fromString(name).toUInt32(ctx); - if (i < context->argumentCount) { - *to_fill = PropertyDescriptor::fromValue(context->arguments[i]); + if (i < context->argumentCount()) { + *to_fill = PropertyDescriptor::fromValue(context->argument(i)); to_fill->writable = PropertyDescriptor::Unset; to_fill->enumberable = PropertyDescriptor::Unset; return to_fill; diff --git a/qmljs_objects.h b/qmljs_objects.h index ce26089e8f..6d0e0fa35d 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -474,7 +474,7 @@ struct ArrayObject: Object { }; struct FunctionObject: Object { - ExecutionContext *scope; + DeclarativeEnvironment *scope; String *name; String **formalParameterList; unsigned int formalParameterCount; @@ -483,7 +483,7 @@ struct FunctionObject: Object { bool needsActivation; FunctionObject(ExecutionContext *scope) - : scope(scope) + : scope(scope->variableEnvironment) , name(0) , formalParameterList(0) , formalParameterCount(0) @@ -554,39 +554,46 @@ protected: }; struct EvalErrorObject: ErrorObject { - EvalErrorObject(ExecutionContext *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + EvalErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("EvalError"); } }; struct RangeErrorObject: ErrorObject { - RangeErrorObject(ExecutionContext *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + RangeErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("RangeError"); } }; struct ReferenceErrorObject: ErrorObject { - ReferenceErrorObject(ExecutionContext *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + ReferenceErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("ReferenceError"); } }; struct SyntaxErrorObject: ErrorObject { - SyntaxErrorObject(ExecutionContext *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + SyntaxErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("SyntaxError"); } }; struct TypeErrorObject: ErrorObject { - TypeErrorObject(ExecutionContext *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + TypeErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("TypeError"); } }; struct URIErrorObject: ErrorObject { - URIErrorObject(ExecutionContext *ctx): ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + URIErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("URIError"); } }; struct ActivationObject: Object { - ExecutionContext *context; + DeclarativeEnvironment *context; Value arguments; - ActivationObject(ExecutionContext *context): context(context), arguments(Value::undefinedValue()) {} + ActivationObject(DeclarativeEnvironment *context) + : context(context), arguments(Value::undefinedValue()) {} virtual QString className() { return QStringLiteral("Activation"); } virtual ActivationObject *asActivationObject() { return this; } virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 44226fb593..407348fe6a 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -197,7 +197,7 @@ Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name) Value __qmljs_delete_property(ExecutionContext *ctx, String *name) { - Object *obj = ctx->activation; + Object *obj = ctx->lexicalEnvironment->activation; if (!obj) obj = ctx->engine->globalObject.objectValue(); return Value::fromBoolean(obj->__delete__(ctx, name, true)); @@ -779,7 +779,7 @@ Value __qmljs_call_property(ExecutionContext *context, Value base, String *name, Value thisObject; if (base.isUndefined()) { - baseObject = context->activation; + baseObject = context->lexicalEnvironment->activation; thisObject = Value::nullValue(); } else { if (!base.isObject()) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 652fc4525a..7d0b25b5db 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -710,7 +710,7 @@ StringCtor::StringCtor(ExecutionContext *scope) void StringCtor::construct(ExecutionContext *ctx) { Value value; - if (ctx->argumentCount) + if (ctx->argumentCount()) value = Value::fromString(ctx->argument(0).toString(ctx)); else value = Value::fromString(ctx, QString()); @@ -786,7 +786,7 @@ void StringPrototype::method_charAt(ExecutionContext *ctx) const QString str = getThisString(ctx); int pos = 0; - if (ctx->argumentCount > 0) + if (ctx->argumentCount() > 0) pos = (int) ctx->argument(0).toInteger(ctx); QString result; @@ -801,7 +801,7 @@ void StringPrototype::method_charCodeAt(ExecutionContext *ctx) const QString str = getThisString(ctx); int pos = 0; - if (ctx->argumentCount > 0) + if (ctx->argumentCount() > 0) pos = (int) ctx->argument(0).toInteger(ctx); double result = qSNaN(); @@ -816,8 +816,8 @@ void StringPrototype::method_concat(ExecutionContext *ctx) { QString value = getThisString(ctx); - for (unsigned i = 0; i < ctx->argumentCount; ++i) { - Value v = __qmljs_to_string(ctx->arguments[i], ctx); + for (unsigned i = 0; i < ctx->argumentCount(); ++i) { + Value v = __qmljs_to_string(ctx->argument(i), ctx); assert(v.isString()); value += v.stringValue()->toQString(); } @@ -830,11 +830,11 @@ void StringPrototype::method_indexOf(ExecutionContext *ctx) QString value = getThisString(ctx); QString searchString; - if (ctx->argumentCount) + if (ctx->argumentCount()) searchString = ctx->argument(0).toString(ctx)->toQString(); int pos = 0; - if (ctx->argumentCount > 1) + if (ctx->argumentCount() > 1) pos = (int) ctx->argument(1).toInteger(ctx); int index = -1; @@ -849,8 +849,8 @@ void StringPrototype::method_lastIndexOf(ExecutionContext *ctx) const QString value = getThisString(ctx); QString searchString; - if (ctx->argumentCount) { - Value v = __qmljs_to_string(ctx->arguments[0], ctx); + if (ctx->argumentCount()) { + Value v = __qmljs_to_string(ctx->argument(0), ctx); searchString = v.stringValue()->toQString(); } @@ -926,11 +926,11 @@ void StringPrototype::method_substr(ExecutionContext *ctx) const QString value = getThisString(ctx); double start = 0; - if (ctx->argumentCount > 0) + if (ctx->argumentCount() > 0) start = ctx->argument(0).toInteger(ctx); double length = +qInf(); - if (ctx->argumentCount > 1) + if (ctx->argumentCount() > 1) length = ctx->argument(1).toInteger(ctx); double count = value.length(); @@ -952,10 +952,10 @@ void StringPrototype::method_substring(ExecutionContext *ctx) double start = 0; double end = length; - if (ctx->argumentCount > 0) + if (ctx->argumentCount() > 0) start = ctx->argument(0).toInteger(ctx); - if (ctx->argumentCount > 1) + if (ctx->argumentCount() > 1) end = ctx->argument(1).toInteger(ctx); if (qIsNaN(start) || start < 0) @@ -1006,7 +1006,7 @@ void StringPrototype::method_toLocaleUpperCase(ExecutionContext *ctx) void StringPrototype::method_fromCharCode(ExecutionContext *ctx) { QString str; - for (unsigned i = 0; i < ctx->argumentCount; ++i) { + for (unsigned i = 0; i < ctx->argumentCount(); ++i) { QChar c(ctx->argument(i).toUInt16(ctx)); str += c; } @@ -1029,7 +1029,7 @@ void NumberCtor::construct(ExecutionContext *ctx) void NumberCtor::call(ExecutionContext *ctx) { - double value = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; + double value = ctx->argumentCount() ? ctx->argument(0).toNumber(ctx) : 0; ctx->result = Value::fromDouble(value); } @@ -1143,7 +1143,7 @@ void NumberPrototype::method_toFixed(ExecutionContext *ctx) if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { double fdigits = 0; - if (ctx->argumentCount > 0) + if (ctx->argumentCount() > 0) fdigits = ctx->argument(0).toInteger(ctx); if (qIsNaN(fdigits)) @@ -1168,7 +1168,7 @@ void NumberPrototype::method_toExponential(ExecutionContext *ctx) if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { double fdigits = 0; - if (ctx->argumentCount > 0) + if (ctx->argumentCount() > 0) fdigits = ctx->argument(0).toInteger(ctx); QString z = QString::number(thisObject->value.asDouble(), 'e', int (fdigits)); @@ -1183,7 +1183,7 @@ void NumberPrototype::method_toPrecision(ExecutionContext *ctx) if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { double fdigits = 0; - if (ctx->argumentCount > 0) + if (ctx->argumentCount() > 0) fdigits = ctx->argument(0).toInteger(ctx); ctx->result = Value::fromString(ctx, QString::number(thisObject->value.asDouble(), 'g', int (fdigits))); @@ -1208,7 +1208,7 @@ void BooleanCtor::construct(ExecutionContext *ctx) void BooleanCtor::call(ExecutionContext *ctx) { - bool value = ctx->argumentCount ? ctx->argument(0).toBoolean(ctx) : 0; + bool value = ctx->argumentCount() ? ctx->argument(0).toBoolean(ctx) : 0; ctx->result = Value::fromBoolean(value); } @@ -1255,7 +1255,7 @@ void ArrayCtor::construct(ExecutionContext *ctx) void ArrayCtor::call(ExecutionContext *ctx) { Array value; - if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { + if (ctx->argumentCount() == 1 && ctx->argument(0).isNumber()) { double size = ctx->argument(0).asDouble(); quint32 isize = Value::toUInt32(size); @@ -1266,7 +1266,7 @@ void ArrayCtor::call(ExecutionContext *ctx) value.resize(isize); } else { - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { + for (unsigned int i = 0; i < ctx->argumentCount(); ++i) { value.assign(i, ctx->argument(i)); } } @@ -1322,7 +1322,7 @@ void ArrayPrototype::method_concat(ExecutionContext *ctx) result.assign(0, Value::fromString(ctx, v)); } - for (uint i = 0; i < ctx->argumentCount; ++i) { + for (uint i = 0; i < ctx->argumentCount(); ++i) { quint32 k = result.size(); Value arg = ctx->argument(i); @@ -1420,7 +1420,7 @@ void ArrayPrototype::method_push(ExecutionContext *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { uint pos = instance->value.size(); - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { + for (unsigned int i = 0; i < ctx->argumentCount(); ++i) { Value val = ctx->argument(i); instance->value.assign(pos++, val); } @@ -1428,7 +1428,7 @@ void ArrayPrototype::method_push(ExecutionContext *ctx) } else { Value r1 = self.property(ctx, ctx->engine->id_length); quint32 n = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; - for (unsigned int index = 0; index < ctx->argumentCount; ++index, ++n) { + for (unsigned int index = 0; index < ctx->argumentCount(); ++index, ++n) { Value r3 = ctx->argument(index); String *name = Value::fromDouble(n).toString(ctx); self.objectValue()->__put__(ctx, name, r3); @@ -1505,7 +1505,7 @@ void ArrayPrototype::method_sort(ExecutionContext *ctx) void ArrayPrototype::method_splice(ExecutionContext *ctx) { - if (ctx->argumentCount < 2) + if (ctx->argumentCount() < 2) return; double start = ctx->argument(0).toInteger(ctx); @@ -1514,7 +1514,7 @@ void ArrayPrototype::method_splice(ExecutionContext *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { QVector items; - for (unsigned int i = 2; i < ctx->argumentCount; ++i) + for (unsigned int i = 2; i < ctx->argumentCount(); ++i) items << ctx->argument(i); ArrayObject *otherInstance = a.asArrayObject(); assert(otherInstance); @@ -1739,13 +1739,13 @@ void FunctionCtor::construct(ExecutionContext *ctx) { QString args; QString body; - if (ctx->argumentCount > 0) - body = ctx->arguments[ctx->argumentCount - 1].toString(ctx)->toQString(); + if (ctx->argumentCount() > 0) + body = ctx->argument(ctx->argumentCount() - 1).toString(ctx)->toQString(); - for (uint i = 0; i < ctx->argumentCount - 1; ++i) { + for (uint i = 0; i < ctx->argumentCount() - 1; ++i) { if (i) args += QLatin1String(", "); - args += ctx->arguments[i].toString(ctx)->toQString(); + args += ctx->argument(i).toString(ctx)->toQString(); } QString function = QLatin1String("function(") + args + QLatin1String("){") + body + QLatin1String("}"); @@ -1835,9 +1835,10 @@ void FunctionPrototype::method_apply(ExecutionContext *ctx) void FunctionPrototype::method_call(ExecutionContext *ctx) { Value thisArg = ctx->argument(0); - QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); - if (ctx->argumentCount) - qCopy(ctx->arguments + 1, ctx->arguments + ctx->argumentCount, args.begin()); + QVector args(ctx->argumentCount() ? ctx->argumentCount() - 1 : 0); + if (ctx->argumentCount()) + qCopy(ctx->variableEnvironment->arguments + 1, + ctx->variableEnvironment->arguments + ctx->argumentCount(), args.begin()); ctx->result = __qmljs_call_value(ctx, thisArg, ctx->thisObject, args.data(), args.size()); } @@ -1863,10 +1864,10 @@ void DateCtor::construct(ExecutionContext *ctx) { double t = 0; - if (ctx->argumentCount == 0) + if (ctx->argumentCount() == 0) t = currentTime(); - else if (ctx->argumentCount == 1) { + else if (ctx->argumentCount() == 1) { Value arg = ctx->argument(0); if (DateObject *d = arg.asDateObject()) arg = d->value; @@ -1879,14 +1880,14 @@ void DateCtor::construct(ExecutionContext *ctx) t = TimeClip(arg.toNumber(ctx)); } - else { // ctx->argumentCount() > 1 + else { // ctx->argumentCount()() > 1 double year = ctx->argument(0).toNumber(ctx); double month = ctx->argument(1).toNumber(ctx); - double day = ctx->argumentCount >= 3 ? ctx->argument(2).toNumber(ctx) : 1; - double hours = ctx->argumentCount >= 4 ? ctx->argument(3).toNumber(ctx) : 0; - double mins = ctx->argumentCount >= 5 ? ctx->argument(4).toNumber(ctx) : 0; - double secs = ctx->argumentCount >= 6 ? ctx->argument(5).toNumber(ctx) : 0; - double ms = ctx->argumentCount >= 7 ? ctx->argument(6).toNumber(ctx) : 0; + double day = ctx->argumentCount() >= 3 ? ctx->argument(2).toNumber(ctx) : 1; + double hours = ctx->argumentCount() >= 4 ? ctx->argument(3).toNumber(ctx) : 0; + double mins = ctx->argumentCount() >= 5 ? ctx->argument(4).toNumber(ctx) : 0; + double secs = ctx->argumentCount() >= 6 ? ctx->argument(5).toNumber(ctx) : 0; + double ms = ctx->argumentCount() >= 7 ? ctx->argument(6).toNumber(ctx) : 0; if (year >= 0 && year <= 99) year += 1900; t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); @@ -1990,7 +1991,7 @@ void DatePrototype::method_parse(ExecutionContext *ctx) void DatePrototype::method_UTC(ExecutionContext *ctx) { - const int numArgs = ctx->argumentCount; + const int numArgs = ctx->argumentCount(); if (numArgs >= 2) { double year = ctx->argument(0).toNumber(ctx); double month = ctx->argument(1).toNumber(ctx); @@ -2238,7 +2239,7 @@ void DatePrototype::method_setSeconds(ExecutionContext *ctx) if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.asDouble()); double sec = ctx->argument(0).toNumber(ctx); - double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount() < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); self->value.setDouble(t); ctx->result = self->value; @@ -2252,7 +2253,7 @@ void DatePrototype::method_setUTCSeconds(ExecutionContext *ctx) if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.asDouble(); double sec = ctx->argument(0).toNumber(ctx); - double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount() < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); self->value.setDouble(t); ctx->result = self->value; @@ -2266,8 +2267,8 @@ void DatePrototype::method_setMinutes(ExecutionContext *ctx) if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.asDouble()); double min = ctx->argument(0).toNumber(ctx); - double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); - double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); + double sec = (ctx->argumentCount() < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount() < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); self->value.setDouble(t); ctx->result = self->value; @@ -2281,8 +2282,8 @@ void DatePrototype::method_setUTCMinutes(ExecutionContext *ctx) if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.asDouble(); double min = ctx->argument(0).toNumber(ctx); - double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); - double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); + double sec = (ctx->argumentCount() < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount() < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); self->value.setDouble(t); ctx->result = self->value; @@ -2296,9 +2297,9 @@ void DatePrototype::method_setHours(ExecutionContext *ctx) if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.asDouble()); double hour = ctx->argument(0).toNumber(ctx); - double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); - double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); - double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); + double min = (ctx->argumentCount() < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); + double sec = (ctx->argumentCount() < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); + double ms = (ctx->argumentCount() < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); self->value.setDouble(t); ctx->result = self->value; @@ -2312,9 +2313,9 @@ void DatePrototype::method_setUTCHours(ExecutionContext *ctx) if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.asDouble(); double hour = ctx->argument(0).toNumber(ctx); - double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); - double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); - double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); + double min = (ctx->argumentCount() < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); + double sec = (ctx->argumentCount() < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); + double ms = (ctx->argumentCount() < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); self->value.setDouble(t); ctx->result = self->value; @@ -2354,7 +2355,7 @@ void DatePrototype::method_setMonth(ExecutionContext *ctx) if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.asDouble()); double month = ctx->argument(0).toNumber(ctx); - double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount() < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); self->value.setDouble(t); ctx->result = self->value; @@ -2368,7 +2369,7 @@ void DatePrototype::method_setUTCMonth(ExecutionContext *ctx) if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.asDouble(); double month = ctx->argument(0).toNumber(ctx); - double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount() < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); self->value.setDouble(t); ctx->result = self->value; @@ -2408,8 +2409,8 @@ void DatePrototype::method_setUTCFullYear(ExecutionContext *ctx) if (DateObject *self = ctx->thisObject.asDateObject()) { double t = self->value.asDouble(); double year = ctx->argument(0).toNumber(ctx); - double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); - double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); + double month = (ctx->argumentCount() < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount() < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); self->value.setDouble(t); ctx->result = self->value; @@ -2423,8 +2424,8 @@ void DatePrototype::method_setFullYear(ExecutionContext *ctx) if (DateObject *self = ctx->thisObject.asDateObject()) { double t = LocalTime(self->value.asDouble()); double year = ctx->argument(0).toNumber(ctx); - double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); - double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); + double month = (ctx->argumentCount() < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount() < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); self->value.setDouble(t); ctx->result = self->value; @@ -2451,13 +2452,13 @@ RegExpCtor::RegExpCtor(ExecutionContext *scope) void RegExpCtor::construct(ExecutionContext *ctx) { -// if (ctx->argumentCount > 2) { +// if (ctx->argumentCount() > 2) { // ctx->throwTypeError(); // return; // } - Value r = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); - Value f = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); + Value r = ctx->argumentCount() > 0 ? ctx->argument(0) : Value::undefinedValue(); + Value f = ctx->argumentCount() > 1 ? ctx->argument(1) : Value::undefinedValue(); if (RegExpObject *re = r.asRegExpObject()) { if (!f.isUndefined()) { ctx->throwTypeError(); @@ -2501,8 +2502,8 @@ void RegExpCtor::construct(ExecutionContext *ctx) void RegExpCtor::call(ExecutionContext *ctx) { - if (ctx->argumentCount > 0 && ctx->argument(0).asRegExpObject()) { - if (ctx->argumentCount == 1 || ctx->argument(1).isUndefined()) { + if (ctx->argumentCount() > 0 && ctx->argument(0).asRegExpObject()) { + if (ctx->argumentCount() == 1 || ctx->argument(1).isUndefined()) { ctx->result = ctx->argument(0); return; } @@ -2822,7 +2823,7 @@ void MathObject::method_log(ExecutionContext *ctx) void MathObject::method_max(ExecutionContext *ctx) { double mx = -qInf(); - for (unsigned i = 0; i < ctx->argumentCount; ++i) { + for (unsigned i = 0; i < ctx->argumentCount(); ++i) { double x = ctx->argument(i).toNumber(ctx); if (x > mx || qIsNaN(x)) mx = x; @@ -2833,7 +2834,7 @@ void MathObject::method_max(ExecutionContext *ctx) void MathObject::method_min(ExecutionContext *ctx) { double mx = qInf(); - for (unsigned i = 0; i < ctx->argumentCount; ++i) { + for (unsigned i = 0; i < ctx->argumentCount(); ++i) { double x = ctx->argument(i).toNumber(ctx); if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) || (x < mx) || qIsNaN(x)) { diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 90b3cb968d..8a8e663e93 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -180,10 +180,12 @@ InstructionSelection::Pointer InstructionSelection::loadTempAddress(RegisterID r int32_t offset = 0; if (t->index < 0) { const int arg = -t->index - 1; - loadPtr(Address(ContextRegister, offsetof(ExecutionContext, arguments)), reg); + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, variableEnvironment)), reg); + loadPtr(Address(reg, offsetof(DeclarativeEnvironment, arguments)), reg); offset = arg * sizeof(Value); } else if (t->index < _function->locals.size()) { - loadPtr(Address(ContextRegister, offsetof(ExecutionContext, locals)), reg); + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, variableEnvironment)), reg); + loadPtr(Address(reg, offsetof(DeclarativeEnvironment, locals)), reg); offset = t->index * sizeof(Value); } else { const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size(); -- cgit v1.2.3 From b072fd9317fa20d9206e9faaca7b0b45ae28a519 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 19 Nov 2012 11:54:54 +0100 Subject: Added isNaN and isFinite to the global context. Change-Id: Ia85d27a6ac82fd5dbf6b0f706747afa6418626b1 Reviewed-by: Lars Knoll --- qmljs_engine.cpp | 5 ++++- qmljs_objects.cpp | 16 ++++++++++++++++ qmljs_objects.h | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 8097639e54..4415231934 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -162,7 +162,10 @@ ExecutionEngine::ExecutionEngine() glo->__put__(rootContext, identifier(QStringLiteral("Infinity")), Value::fromDouble(INFINITY)); glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new EvalFunction(rootContext))); - + // TODO: parseInt [15.1.2.2] + // TODO: parseFloat [15.1.2.3] + glo->__put__(rootContext, identifier(QStringLiteral("isNaN")), Value::fromObject(new IsNaNFunction(rootContext))); // isNaN [15.1.2.4] + glo->__put__(rootContext, identifier(QStringLiteral("isFinite")), Value::fromObject(new IsFiniteFunction(rootContext))); // isFinite [15.1.2.5] } ExecutionContext *ExecutionEngine::newContext() diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 4f831b01bf..290a4f793c 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -487,6 +487,22 @@ Value EvalFunction::call(ExecutionContext *context, Value thisObject, Value *arg ctx->leaveCallContext(); } +/// isNaN [15.1.2.4] +Value IsNaNFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, Value *args, int /*argc*/, bool /*strictMode*/) +{ + // TODO: see if we can generate code for this directly + const Value &v = args[0]; + return Value::fromBoolean(v.isDouble() ? std::isnan(v.doubleValue()) : false); +} + +/// isFinite [15.1.2.5] +Value IsFiniteFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, Value *args, int /*argc*/, bool /*strictMode*/) +{ + // TODO: see if we can generate code for this directly + const Value &v = args[0]; + return Value::fromBoolean(v.isDouble() ? std::isfinite(v.doubleValue()) : true); +} + static inline bool protect(const void *addr, size_t size) { size_t pageSize = sysconf(_SC_PAGESIZE); diff --git a/qmljs_objects.h b/qmljs_objects.h index 6d0e0fa35d..a2b956f567 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -532,6 +532,20 @@ struct EvalFunction : FunctionObject virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode = false); }; +struct IsNaNFunction: FunctionObject +{ + IsNaNFunction(ExecutionContext *scope): FunctionObject(scope) {} + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode = false); +}; + +struct IsFiniteFunction: FunctionObject +{ + IsFiniteFunction(ExecutionContext *scope): FunctionObject(scope) {} + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode = false); +}; + struct RegExpObject: Object { QRegularExpression value; Value lastIndex; -- cgit v1.2.3 From e1bbbb6cf9e6e4790eca41416c93cf271b0b7c02 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 19 Nov 2012 13:15:25 +0100 Subject: Fix isel for eval and a whole bunch of other warnings. A factory is now passed along to do the codegen for eval(). Change-Id: If15b1f28c9c0a8f8b6d18b56d6e7bc5d942927e5 Reviewed-by: Lars Knoll --- main.cpp | 18 ++++++++++----- moth/qv4isel_moth_p.h | 18 +++++++++++++-- qmljs_engine.cpp | 4 ++-- qmljs_engine.h | 5 ++-- qmljs_environment.cpp | 4 ++-- qmljs_math.h | 4 ++-- qmljs_objects.cpp | 46 +++++++++++++++--------------------- qmljs_objects.h | 8 +++++-- qmljs_value.h | 2 +- qv4isel_llvm_p.h | 1 + qv4isel_masm.cpp | 14 +++++++++++ qv4isel_masm_p.h | 15 +++++++++++- qv4isel_p.h | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ v4.pro | 4 +++- 14 files changed, 158 insertions(+), 49 deletions(-) create mode 100644 qv4isel_p.h diff --git a/main.cpp b/main.cpp index d147d04a91..2442cd1bc7 100644 --- a/main.cpp +++ b/main.cpp @@ -51,6 +51,7 @@ #include "qv4vme_moth_p.h" #include "qv4syntaxchecker_p.h" #include "qv4ecmaobjects_p.h" +#include "qv4isel_p.h" #include #include @@ -110,10 +111,11 @@ int executeLLVMCode(void *codePtr) if (!codePtr) return EXIT_FAILURE; - void (*code)(VM::Context *) = (void (*)(VM::Context *)) codePtr; + void (*code)(VM::ExecutionContext *) = (void (*)(VM::ExecutionContext *)) codePtr; - VM::ExecutionEngine vm; - VM::Context *ctx = vm.rootContext; + QScopedPointer iSelFactory(new QQmlJS::Moth::ISelFactory); + VM::ExecutionEngine vm(iSelFactory.data()); + VM::ExecutionContext *ctx = vm.rootContext; QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), @@ -280,8 +282,12 @@ int main(int argc, char *argv[]) #endif // QMLJS_NO_LLVM case use_masm: case use_moth: { - bool useInterpreter = mode == use_moth; - QQmlJS::VM::ExecutionEngine vm; + QScopedPointer iSelFactory; + if (mode == use_moth) + iSelFactory.reset(new QQmlJS::Moth::ISelFactory); + else + iSelFactory.reset(new QQmlJS::MASM::ISelFactory); + QQmlJS::VM::ExecutionEngine vm(iSelFactory.data()); QQmlJS::VM::ExecutionContext *ctx = vm.rootContext; QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); @@ -299,7 +305,7 @@ int main(int argc, char *argv[]) const QString code = QString::fromUtf8(file.readAll()); file.close(); - int exitCode = QQmlJS::VM::EvalFunction::evaluate(vm.rootContext, fn, code, useInterpreter, QQmlJS::Codegen::GlobalCode); + int exitCode = QQmlJS::VM::EvalFunction::evaluate(vm.rootContext, fn, code, iSelFactory.data(), QQmlJS::Codegen::GlobalCode); if (exitCode != EXIT_SUCCESS) return exitCode; if (errorInTestHarness) diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index bb024c58f3..1105eb1c73 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -1,6 +1,7 @@ #ifndef QV4ISEL_MOTH_P_H #define QV4ISEL_MOTH_P_H +#include "qv4isel_p.h" #include "qv4ir_p.h" #include "qmljs_objects.h" #include "qv4instr_moth_p.h" @@ -8,13 +9,18 @@ namespace QQmlJS { namespace Moth { -class InstructionSelection : public IR::StmtVisitor +class InstructionSelection : public IR::StmtVisitor, public EvalInstructionSelection { public: InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *code); ~InstructionSelection(); - void operator()(IR::Function *function); + virtual void run(IR::Function *function) + { this->operator()(function); } + virtual void operator()(IR::Function *function); + + virtual bool finishModule(size_t) + { return true; } protected: virtual void visitExp(IR::Exp *); @@ -57,6 +63,14 @@ private: uchar *_ccode; }; +class ISelFactory: public EValISelFactory +{ +public: + virtual ~ISelFactory() {} + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module, uchar *code) + { return new InstructionSelection(engine, module, code); } +}; + template ptrdiff_t InstructionSelection::addInstruction(const InstrData &data) { diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 4415231934..40cf7f3294 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -45,7 +45,7 @@ namespace QQmlJS { namespace VM { -ExecutionEngine::ExecutionEngine() +ExecutionEngine::ExecutionEngine(EValISelFactory *factory) { rootContext = newContext(); rootContext->init(this); @@ -160,7 +160,7 @@ ExecutionEngine::ExecutionEngine() glo->__put__(rootContext, identifier(QStringLiteral("undefined")), Value::undefinedValue()); glo->__put__(rootContext, identifier(QStringLiteral("NaN")), Value::fromDouble(nan(""))); glo->__put__(rootContext, identifier(QStringLiteral("Infinity")), Value::fromDouble(INFINITY)); - glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new EvalFunction(rootContext))); + glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new EvalFunction(rootContext, factory))); // TODO: parseInt [15.1.2.2] // TODO: parseFloat [15.1.2.3] diff --git a/qmljs_engine.h b/qmljs_engine.h index f111979f0d..4ecc386d00 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -41,6 +41,7 @@ #ifndef QMLJS_ENGINE_H #define QMLJS_ENGINE_H +#include #include #include #include @@ -49,7 +50,7 @@ namespace QQmlJS { namespace VM { struct Value; -struct Array; +class Array; struct Object; struct BooleanObject; struct NumberObject; @@ -136,7 +137,7 @@ struct ExecutionEngine QVector unwindStack; - ExecutionEngine(); + ExecutionEngine(EValISelFactory *factory); ExecutionContext *newContext(); diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 338006474e..4a4d8e1864 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -109,7 +109,7 @@ void DeclarativeEnvironment::createMutableBinding(String *name, bool deletable) deletableLocals.insert(name->toQString(), Value::undefinedValue()); } -void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool strict) +void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool /*strict*/) { // ### throw if strict is true, and it would change an immutable binding for (unsigned int i = 0; i < varCount; ++i) { @@ -132,7 +132,7 @@ void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool s assert(false); } -Value DeclarativeEnvironment::getBindingValue(String *name, bool strict) const +Value DeclarativeEnvironment::getBindingValue(String *name, bool /*strict*/) const { for (unsigned int i = 0; i < varCount; ++i) { if (__qmljs_string_equal(vars[i], name)) diff --git a/qmljs_math.h b/qmljs_math.h index 492f7c8692..1fb9261835 100644 --- a/qmljs_math.h +++ b/qmljs_math.h @@ -98,9 +98,9 @@ static inline Value mul_int32(int a, int b) return Value::fromInt32(aa); return Value::fromDouble((double)a * (double)b); } -#endif } // namespace VM } // namespace QQmlJS -#endif +#endif // !defined(QMLJS_LLVM_RUNTIME) && 1 //CPU(X86_64) +#endif // QMLJS_MATH_H diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 290a4f793c..c34af50561 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -42,6 +42,7 @@ #include "qmljs_objects.h" #include "qv4ir_p.h" +#include "qv4isel_p.h" #include "qv4ecmaobjects_p.h" #include @@ -50,7 +51,6 @@ #include #include #include -#include #include #include @@ -459,7 +459,7 @@ void ScriptFunction::call(VM::ExecutionContext *ctx) } -Value EvalFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode) +Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc, bool strictMode) { Value s = context->argument(0); if (!s.isString()) { @@ -481,10 +481,15 @@ Value EvalFunction::call(ExecutionContext *context, Value thisObject, Value *arg ctx = context; } // ##### inline and do this in the correct scope - evaluate(ctx, QStringLiteral("eval code"), code, /*useInterpreter*/ false, QQmlJS::Codegen::EvalCode); + int result = evaluate(ctx, QStringLiteral("eval code"), code, _factory, QQmlJS::Codegen::EvalCode); if (strictMode) ctx->leaveCallContext(); + + if (result == EXIT_SUCCESS) + return ctx->result; + else + return Value::undefinedValue(); } /// isNaN [15.1.2.4] @@ -503,18 +508,8 @@ Value IsFiniteFunction::call(ExecutionContext * /*context*/, Value /*thisObject* return Value::fromBoolean(v.isDouble() ? std::isfinite(v.doubleValue()) : true); } -static inline bool protect(const void *addr, size_t size) -{ - size_t pageSize = sysconf(_SC_PAGESIZE); - size_t iaddr = reinterpret_cast(addr); - size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); - int mode = PROT_READ | PROT_WRITE | PROT_EXEC; - return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; -} - - int EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, - const QString &source, bool useInterpreter, + const QString &source, EValISelFactory *factory, QQmlJS::Codegen::Mode mode) { using namespace QQmlJS; @@ -550,20 +545,15 @@ int EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fil Codegen cg; globalCode = cg(program, &module, mode); -// if (useInterpreter) { -// Moth::InstructionSelection isel(vm, &module, code); -// foreach (IR::Function *function, module.functions) -// isel(function); -// } else - { - foreach (IR::Function *function, module.functions) { - MASM::InstructionSelection isel(vm, &module, code); - isel(function); - } - - if (! protect(code, codeSize)) - Q_UNREACHABLE(); - } + EvalInstructionSelection *isel = factory->create(vm, &module, code); + + foreach (IR::Function *function, module.functions) + isel->run(function); + + if (! isel->finishModule(codeSize)) + Q_UNREACHABLE(); + + delete isel; } if (! globalCode) diff --git a/qmljs_objects.h b/qmljs_objects.h index a2b956f567..083c124e85 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -46,6 +46,7 @@ #include "qmljs_environment.h" #include "qv4array_p.h" #include "qv4codegen_p.h" +#include "qv4isel_p.h" #include #include @@ -523,13 +524,16 @@ struct ScriptFunction: FunctionObject { struct EvalFunction : FunctionObject { - EvalFunction(ExecutionContext *scope): FunctionObject(scope) {} + EvalFunction(ExecutionContext *scope, EValISelFactory *factory): FunctionObject(scope), _factory(factory) {} static int evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, - const QString &source, bool useInterpreter, + const QString &source, EValISelFactory *factory, QQmlJS::Codegen::Mode mode); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode = false); + +private: + EValISelFactory *_factory; }; struct IsNaNFunction: FunctionObject diff --git a/qmljs_value.h b/qmljs_value.h index b504af0889..e15b970257 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -50,7 +50,7 @@ namespace QQmlJS { namespace VM { -struct Array; +class Array; struct String; struct Object; struct BooleanObject; diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 2142ea42c2..3f51a21b3f 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -54,6 +54,7 @@ # pragma clang diagnostic pop #endif // __clang__ +#include "qv4isel_p.h" #include "qv4ir_p.h" namespace QQmlJS { diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 8a8e663e93..6290dd5990 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -170,6 +170,20 @@ void InstructionSelection::operator()(IR::Function *function) qSwap(_function, function); } +static inline bool protect(const void *addr, size_t size) +{ + size_t pageSize = sysconf(_SC_PAGESIZE); + size_t iaddr = reinterpret_cast(addr); + size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); + int mode = PROT_READ | PROT_WRITE | PROT_EXEC; + return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; +} + +bool InstructionSelection::finishModule(size_t size) +{ + return protect(_code, size); +} + String *InstructionSelection::identifier(const QString &s) { return _engine->identifier(s); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 82bc471be5..aaaf2e0f9a 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -42,6 +42,7 @@ #define QV4ISEL_MASM_P_H #include "qv4ir_p.h" +#include "qv4isel_p.h" #include "qv4isel_util_p.h" #include "qmljs_objects.h" #include "qmljs_runtime.h" @@ -54,14 +55,18 @@ namespace QQmlJS { namespace MASM { -class InstructionSelection: protected IR::StmtVisitor, public JSC::MacroAssembler +class InstructionSelection: protected IR::StmtVisitor, public JSC::MacroAssembler, public EvalInstructionSelection { public: InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *code); ~InstructionSelection(); + virtual void run(IR::Function *function) + { this->operator()(function); } void operator()(IR::Function *function); + virtual bool finishModule(size_t size); + protected: #if CPU(X86) @@ -637,6 +642,14 @@ private: QList _callsToLink; }; +class ISelFactory: public EValISelFactory +{ +public: + virtual ~ISelFactory() {} + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module, uchar *code) + { return new InstructionSelection(engine, module, code); } +}; + } // end of namespace MASM } // end of namespace QQmlJS diff --git a/qv4isel_p.h b/qv4isel_p.h new file mode 100644 index 0000000000..effd44fcf3 --- /dev/null +++ b/qv4isel_p.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QV4ISEL_P_H +#define QV4ISEL_P_H + +#include + +namespace QQmlJS { + +namespace IR { +struct Function; +struct Module; +} // namespace IR; + +namespace VM { +struct ExecutionEngine; +} // namespace VM + +class EvalInstructionSelection +{ +public: + virtual ~EvalInstructionSelection() = 0; + + virtual void run(IR::Function *function) = 0; + virtual bool finishModule(size_t codeSize) = 0; +}; + +class EValISelFactory +{ +public: + virtual ~EValISelFactory() = 0; + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module, uchar *code) = 0; +}; + +} // namespace QQmlJS + +#endif // QV4ISEL_P_H diff --git a/v4.pro b/v4.pro index 9358bf489e..1a6e2a41ab 100644 --- a/v4.pro +++ b/v4.pro @@ -22,7 +22,8 @@ SOURCES += main.cpp \ qv4ecmaobjects.cpp \ qv4array.cpp \ qv4isel_masm.cpp \ - llvm_runtime.cpp + llvm_runtime.cpp \ + qv4isel_p.cpp HEADERS += \ qv4codegen_p.h \ @@ -37,6 +38,7 @@ HEADERS += \ qv4ecmaobjects_p.h \ qv4array_p.h \ qv4isel_masm_p.h \ + qv4isel_p.h \ qv4isel_util_p.h llvm { -- cgit v1.2.3 From 95e8de83dccc12b58e065ac7f0c54de25f580bf2 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 19 Nov 2012 14:59:53 +0100 Subject: Added missing file. Change-Id: I2b55ed78e8b30bffc67c4a1eee0bf4a438f0f29a Reviewed-by: Lars Knoll --- qv4isel_p.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 qv4isel_p.cpp diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp new file mode 100644 index 0000000000..294a22f6c8 --- /dev/null +++ b/qv4isel_p.cpp @@ -0,0 +1,9 @@ +#include "qv4isel_p.h" + +using namespace QQmlJS; + +EvalInstructionSelection::~EvalInstructionSelection() +{} + +EValISelFactory::~EValISelFactory() +{} -- cgit v1.2.3 From 79753b8fe5f3df79a9a4208056ac05281b5f97d0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 19 Nov 2012 00:00:50 +0100 Subject: return results directly instead of using the context The result variable in the context is not really required, as we can return results directly in the return value register. Change-Id: I12554c228500aa24625ef82e31fd7f72989a71bb Reviewed-by: Erik Verbruggen --- llvm_runtime.cpp | 6 +- main.cpp | 24 +- moth/qv4vme_moth.cpp | 11 +- moth/qv4vme_moth_p.h | 8 +- qmljs_engine.cpp | 2 +- qmljs_engine.h | 2 +- qmljs_environment.cpp | 14 +- qmljs_environment.h | 2 +- qmljs_objects.cpp | 69 +- qmljs_objects.h | 20 +- qmljs_runtime.cpp | 8 +- qmljs_runtime.h | 2 +- qv4ecmaobjects.cpp | 1700 +++++++++++++++++++++++++------------------------ qv4ecmaobjects_p.h | 334 +++++----- qv4ir_p.h | 2 +- qv4isel_masm.cpp | 4 +- 16 files changed, 1106 insertions(+), 1102 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 553f2fd1ac..e63ab62d7e 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -48,9 +48,9 @@ using namespace QQmlJS::VM; extern "C" { -void __qmljs_llvm_return(ExecutionContext *ctx, Value *result) +Value __qmljs_llvm_return(ExecutionContext */*ctx*/, Value *result) { - ctx->result = *result; + return *result; } Value *__qmljs_llvm_get_argument(ExecutionContext *ctx, int index) @@ -83,7 +83,7 @@ void __qmljs_llvm_init_string(ExecutionContext *ctx, Value *result, const char * *result = Value::fromString(__qmljs_string_from_utf8(ctx, str)); } -void __qmljs_llvm_init_native_function(ExecutionContext *ctx, Value *result, void (*code)(ExecutionContext *)) +void __qmljs_llvm_init_native_function(ExecutionContext *ctx, Value *result, Value (*code)(ExecutionContext *)) { *result = __qmljs_init_native_function(code, ctx); } diff --git a/main.cpp b/main.cpp index 2442cd1bc7..7467568d0b 100644 --- a/main.cpp +++ b/main.cpp @@ -70,7 +70,7 @@ struct Print: FunctionObject { Print(ExecutionContext *scope): FunctionObject(scope) {} - virtual void call(ExecutionContext *ctx) + virtual Value call(ExecutionContext *ctx) { for (unsigned int i = 0; i < ctx->argumentCount(); ++i) { String *s = ctx->argument(i).toString(ctx); @@ -79,6 +79,7 @@ struct Print: FunctionObject std::cout << qPrintable(s->toQString()); } std::cout << std::endl; + return Value::undefinedValue(); } }; @@ -86,7 +87,7 @@ struct TestHarnessError: FunctionObject { TestHarnessError(ExecutionContext *scope, bool &errorInTestHarness): FunctionObject(scope), errorOccurred(errorInTestHarness) {} - virtual void call(ExecutionContext *ctx) + virtual Value call(ExecutionContext *ctx) { errorOccurred = true; @@ -97,6 +98,7 @@ struct TestHarnessError: FunctionObject std::cerr << qPrintable(s->toQString()); } std::cerr << std::endl; + return Value::undefinedValue(); } bool &errorOccurred; @@ -305,9 +307,21 @@ int main(int argc, char *argv[]) const QString code = QString::fromUtf8(file.readAll()); file.close(); - int exitCode = QQmlJS::VM::EvalFunction::evaluate(vm.rootContext, fn, code, iSelFactory.data(), QQmlJS::Codegen::GlobalCode); - if (exitCode != EXIT_SUCCESS) - return exitCode; + void * buf = __qmljs_create_exception_handler(ctx); + if (setjmp(*(jmp_buf *)buf)) { + if (QQmlJS::VM::ErrorObject *e = ctx->res.asErrorObject()) + std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; + else + std::cerr << "Uncaught exception: " << qPrintable(ctx->res.toString(ctx)->toQString()) << std::endl; + return EXIT_FAILURE; + } + + QQmlJS::VM::Value result = QQmlJS::VM::EvalFunction::evaluate(vm.rootContext, fn, code, iSelFactory.data(), QQmlJS::Codegen::GlobalCode); + if (!result.isUndefined()) { + if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) + std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; + } + if (errorInTestHarness) return EXIT_FAILURE; } else { diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 30a5d3c3a6..a4033ab6d5 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -1,5 +1,6 @@ #include "qv4vme_moth_p.h" #include "qv4instr_moth_p.h" +#include "qmljs_value.h" #ifdef DO_TRACE_INSTR # define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); @@ -79,7 +80,7 @@ static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVecto #define TEMP(index) *tempValue(context, stack, index) -void VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code +VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code #ifdef MOTH_THREADED_INTERPRETER , void ***storeJumpTable #endif @@ -94,7 +95,7 @@ void VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code }; #undef MOTH_INSTR_ADDR *storeJumpTable = jumpTable; - return; + return VM::Value::undefinedValue(); } #endif @@ -290,8 +291,7 @@ void VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code MOTH_END_INSTR(Binop) MOTH_BEGIN_INSTR(Ret) - context->result = TEMP(instr.tempIndex); - return; + return TEMP(instr.tempIndex); MOTH_END_INSTR(Ret) MOTH_BEGIN_INSTR(LoadThis) @@ -345,10 +345,11 @@ void **VME::instructionJumpTable() } #endif -void VME::exec(VM::ExecutionContext *ctxt, const uchar *code) +VM::Value VME::exec(VM::ExecutionContext *ctxt, const uchar *code) { VME vme; vme(ctxt, code); + return VM::Value::undefinedValue(); } void VME::restoreState(VM::ExecutionContext *context, int &targetTempIndex, const uchar *&code) diff --git a/moth/qv4vme_moth_p.h b/moth/qv4vme_moth_p.h index 428cd4e31d..c3ccccd59a 100644 --- a/moth/qv4vme_moth_p.h +++ b/moth/qv4vme_moth_p.h @@ -5,14 +5,18 @@ #include "qv4instr_moth_p.h" namespace QQmlJS { +namespace VM { + struct Value; +} + namespace Moth { class VME { public: - static void exec(VM::ExecutionContext *, const uchar *); + static VM::Value exec(VM::ExecutionContext *, const uchar *); - void operator()(QQmlJS::VM::ExecutionContext *, const uchar *code + VM::Value operator()(QQmlJS::VM::ExecutionContext *, const uchar *code #ifdef MOTH_THREADED_INTERPRETER , void ***storeJumpTable = 0 #endif diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 40cf7f3294..31ed260bee 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -181,7 +181,7 @@ String *ExecutionEngine::identifier(const QString &s) return id; } -FunctionObject *ExecutionEngine::newNativeFunction(ExecutionContext *scope, void (*code)(ExecutionContext *)) +FunctionObject *ExecutionEngine::newNativeFunction(ExecutionContext *scope, Value (*code)(ExecutionContext *)) { NativeFunction *f = new NativeFunction(scope, code); f->prototype = scope->engine->functionPrototype; diff --git a/qmljs_engine.h b/qmljs_engine.h index 4ecc386d00..d22cd85c50 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -143,7 +143,7 @@ struct ExecutionEngine String *identifier(const QString &s); - FunctionObject *newNativeFunction(ExecutionContext *scope, void (*code)(ExecutionContext *)); + FunctionObject *newNativeFunction(ExecutionContext *scope, Value (*code)(ExecutionContext *)); FunctionObject *newScriptFunction(ExecutionContext *scope, IR::Function *function); Object *newObject(); diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 4a4d8e1864..87f8543722 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -109,8 +109,10 @@ void DeclarativeEnvironment::createMutableBinding(String *name, bool deletable) deletableLocals.insert(name->toQString(), Value::undefinedValue()); } -void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool /*strict*/) +void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool strict) { + Q_UNUSED(strict); + // ### throw if strict is true, and it would change an immutable binding for (unsigned int i = 0; i < varCount; ++i) { if (__qmljs_string_equal(vars[i], name)) { @@ -132,8 +134,10 @@ void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool / assert(false); } -Value DeclarativeEnvironment::getBindingValue(String *name, bool /*strict*/) const +Value DeclarativeEnvironment::getBindingValue(String *name, bool strict) const { + Q_UNUSED(strict); + for (unsigned int i = 0; i < varCount; ++i) { if (__qmljs_string_equal(vars[i], name)) return locals[i]; @@ -167,7 +171,7 @@ void ExecutionContext::init(ExecutionEngine *eng) variableEnvironment = new DeclarativeEnvironment(eng); lexicalEnvironment = variableEnvironment; thisObject = Value::nullValue(); - result = Value::undefinedValue(); + res = Value::undefinedValue(); } PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp) @@ -194,7 +198,6 @@ void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) void ExecutionContext::throwError(Value value) { - result = value; __qmljs_builtin_throw(value, this); } @@ -232,7 +235,7 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha lexicalEnvironment = variableEnvironment; thisObject = that; - result = Value::undefinedValue(); + res = Value::undefinedValue(); } void ExecutionContext::leaveCallContext() @@ -258,7 +261,6 @@ void ExecutionContext::leaveConstructorContext(FunctionObject *f) void ExecutionContext::wireUpPrototype(FunctionObject *f) { assert(thisObject.isObject()); - result = thisObject; Value proto = f->__get__(this, engine->id_prototype); if (proto.isObject()) diff --git a/qmljs_environment.h b/qmljs_environment.h index bbb5cc4839..8fe14b2844 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -90,7 +90,7 @@ struct ExecutionContext DeclarativeEnvironment *lexicalEnvironment; DeclarativeEnvironment *variableEnvironment; Value thisObject; - Value result; + Value res; void init(ExecutionEngine *eng); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index c34af50561..5ad7642395 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -73,7 +73,7 @@ void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &va __put__(ctx, ctx->engine->identifier(name), value); } -void Object::__put__(ExecutionContext *ctx, const QString &name, void (*code)(ExecutionContext *), int count) +void Object::__put__(ExecutionContext *ctx, const QString &name, Value (*code)(ExecutionContext *), int count) { Q_UNUSED(count); __put__(ctx, name, Value::fromObject(ctx->engine->newNativeFunction(ctx, code))); @@ -86,8 +86,7 @@ Value Object::getValue(ExecutionContext *ctx, PropertyDescriptor *p) const if (!p->get) return Value::undefinedValue(); - p->get->call(ctx, Value::fromObject(const_cast(this)), 0, 0); - return ctx->result; + return p->get->call(ctx, Value::fromObject(const_cast(this)), 0, 0); } bool Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx) @@ -390,9 +389,9 @@ Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc ExecutionContext k; ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; ctx->initConstructorContext(context, Value::nullValue(), this, args, argc); - construct(ctx); + Value result = construct(ctx); ctx->leaveConstructorContext(this); - return ctx->result; + return result; } Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode) @@ -407,20 +406,22 @@ Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *a thisObject = __qmljs_to_object(thisObject, context); } ctx->initCallContext(context, thisObject, this, args, argc); - call(ctx); + Value result = call(ctx); ctx->leaveCallContext(); - return ctx->result; + return result; } -void FunctionObject::call(ExecutionContext *ctx) +Value FunctionObject::call(ExecutionContext *ctx) { Q_UNUSED(ctx); + return Value::undefinedValue(); } -void FunctionObject::construct(ExecutionContext *ctx) +Value FunctionObject::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(ctx->engine->newObject()); call(ctx); + return ctx->thisObject; } ScriptFunction::ScriptFunction(ExecutionContext *scope, IR::Function *function) @@ -453,19 +454,18 @@ ScriptFunction::~ScriptFunction() delete[] varList; } -void ScriptFunction::call(VM::ExecutionContext *ctx) +Value ScriptFunction::call(VM::ExecutionContext *ctx) { - function->code(ctx, function->codeData); + return function->code(ctx, function->codeData); } Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc, bool strictMode) { Value s = context->argument(0); - if (!s.isString()) { - context->result = s; + if (!s.isString()) return s; - } + const QString code = context->argument(0).stringValue()->toQString(); // ### how to determine this correctly @@ -481,15 +481,12 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value ctx = context; } // ##### inline and do this in the correct scope - int result = evaluate(ctx, QStringLiteral("eval code"), code, _factory, QQmlJS::Codegen::EvalCode); + Value result = evaluate(ctx, QStringLiteral("eval code"), code, _factory, QQmlJS::Codegen::EvalCode); if (strictMode) ctx->leaveCallContext(); - if (result == EXIT_SUCCESS) - return ctx->result; - else - return Value::undefinedValue(); + return result; } /// isNaN [15.1.2.4] @@ -508,7 +505,7 @@ Value IsFiniteFunction::call(ExecutionContext * /*context*/, Value /*thisObject* return Value::fromBoolean(v.isDouble() ? std::isfinite(v.doubleValue()) : true); } -int EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, +Value EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, const QString &source, EValISelFactory *factory, QQmlJS::Codegen::Mode mode) { @@ -557,7 +554,8 @@ int EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fil } if (! globalCode) - return EXIT_FAILURE; + // ### should be a syntax error + __qmljs_throw_type_error(ctx); } if (!ctx->variableEnvironment->activation) @@ -567,31 +565,7 @@ int EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fil ctx->variableEnvironment->activation->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); } - if (mode == Codegen::GlobalCode) { - void * buf = __qmljs_create_exception_handler(ctx); - if (setjmp(*(jmp_buf *)buf)) { - if (VM::ErrorObject *e = ctx->result.asErrorObject()) - std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; - else - std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; - return EXIT_FAILURE; - } - } - -// if (useInterpreter) { -// Moth::VME vme; -// vme(ctx, code); -// } else - { - globalCode->code(ctx, globalCode->codeData); - } - - if (! ctx->result.isUndefined()) { - if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) - std::cout << "exit value: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; - } - - return EXIT_SUCCESS; + return globalCode->code(ctx, globalCode->codeData); } @@ -624,7 +598,7 @@ void ErrorObject::setNameProperty(ExecutionContext *ctx) __put__(ctx, QLatin1String("name"), Value::fromString(ctx, className())); } -void ScriptFunction::construct(VM::ExecutionContext *ctx) +Value ScriptFunction::construct(VM::ExecutionContext *ctx) { Object *obj = ctx->engine->newObject(); Value proto = __get__(ctx, ctx->engine->id_prototype); @@ -632,6 +606,7 @@ void ScriptFunction::construct(VM::ExecutionContext *ctx) obj->prototype = proto.objectValue(); ctx->thisObject = Value::fromObject(obj); function->code(ctx, function->codeData); + return ctx->thisObject; } PropertyDescriptor *ActivationObject::__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill) diff --git a/qmljs_objects.h b/qmljs_objects.h index 083c124e85..6048e99771 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -418,7 +418,7 @@ struct Object { // helpers // void __put__(ExecutionContext *ctx, const QString &name, const Value &value); - void __put__(ExecutionContext *ctx, const QString &name, void (*code)(ExecutionContext *), int count = 0); + void __put__(ExecutionContext *ctx, const QString &name, Value (*code)(ExecutionContext *), int count = 0); Value getValue(ExecutionContext *ctx, PropertyDescriptor *p) const; bool inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); @@ -500,16 +500,16 @@ struct FunctionObject: Object { virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode = false); protected: - virtual void call(ExecutionContext *ctx); - virtual void construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); }; struct NativeFunction: FunctionObject { - void (*code)(ExecutionContext *); + Value (*code)(ExecutionContext *); - NativeFunction(ExecutionContext *scope, void (*code)(ExecutionContext *)): FunctionObject(scope), code(code) {} - virtual void call(ExecutionContext *ctx) { code(ctx); } - virtual void construct(ExecutionContext *ctx) { code(ctx); } + NativeFunction(ExecutionContext *scope, Value (*code)(ExecutionContext *)): FunctionObject(scope), code(code) {} + virtual Value call(ExecutionContext *ctx) { return code(ctx); } + virtual Value construct(ExecutionContext *ctx) { ctx->thisObject = code(ctx); return ctx->thisObject; } }; struct ScriptFunction: FunctionObject { @@ -518,15 +518,15 @@ struct ScriptFunction: FunctionObject { ScriptFunction(ExecutionContext *scope, IR::Function *function); virtual ~ScriptFunction(); - virtual void call(ExecutionContext *ctx); - virtual void construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); }; struct EvalFunction : FunctionObject { EvalFunction(ExecutionContext *scope, EValISelFactory *factory): FunctionObject(scope), _factory(factory) {} - static int evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, + static Value evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, const QString &source, EValISelFactory *factory, QQmlJS::Codegen::Mode mode); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 407348fe6a..a81a6a9fb7 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -119,7 +119,7 @@ Value __qmljs_init_closure(IR::Function *clos, ExecutionContext *ctx) return Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); } -Value __qmljs_init_native_function(void (*code)(ExecutionContext *), ExecutionContext *ctx) +Value __qmljs_init_native_function(Value (*code)(ExecutionContext *), ExecutionContext *ctx) { return Value::fromObject(ctx->engine->newNativeFunction(ctx, code)); } @@ -524,7 +524,7 @@ Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int type Value __qmljs_throw_type_error(ExecutionContext *ctx) { ctx->throwTypeError(); - return ctx->result; + return Value::undefinedValue(); } Value __qmljs_new_object(ExecutionContext *ctx) @@ -844,7 +844,7 @@ void __qmljs_throw(Value value, ExecutionContext *context) context = context->parent; } - handler.context->result = value; + handler.context->res = value; longjmp(handler.stackFrame, 1); } @@ -866,7 +866,7 @@ void __qmljs_delete_exception_handler(ExecutionContext *context) Value __qmljs_get_exception(ExecutionContext *context) { - return context->result; + return context->res; } Value __qmljs_builtin_typeof(Value val, ExecutionContext *context) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index bc404e9bc0..4a1e3480b9 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -104,7 +104,7 @@ void __qmljs_builtin_throw(Value val, ExecutionContext *context); // constructors Value __qmljs_init_closure(IR::Function *clos, ExecutionContext *ctx); -Value __qmljs_init_native_function(void (*code)(ExecutionContext *), ExecutionContext *ctx); +Value __qmljs_init_native_function(Value (*code)(ExecutionContext *), ExecutionContext *ctx); Bool __qmljs_is_function(Value value); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 7d0b25b5db..16ba5e118e 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -528,14 +528,15 @@ ObjectCtor::ObjectCtor(ExecutionContext *scope) { } -void ObjectCtor::construct(ExecutionContext *ctx) +Value ObjectCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(ctx->engine->newObject()); + return ctx->thisObject; } -void ObjectCtor::call(ExecutionContext *ctx) +Value ObjectCtor::call(ExecutionContext *ctx) { - ctx->result = Value::fromObject(ctx->engine->newObject()); + return Value::fromObject(ctx->engine->newObject()); } Value ObjectCtor::__get__(ExecutionContext *ctx, String *name) @@ -571,132 +572,139 @@ void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) __put__(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 0); } -void ObjectPrototype::method_getPrototypeOf(ExecutionContext *ctx) +Value ObjectPrototype::method_getPrototypeOf(ExecutionContext *ctx) { Value o = ctx->argument(0); - if (! o.isObject()) { + if (! o.isObject()) ctx->throwTypeError(); - } else { - ctx->result = Value::fromObject(o.objectValue()->prototype); - } + + return Value::fromObject(o.objectValue()->prototype); } -void ObjectPrototype::method_getOwnPropertyDescriptor(ExecutionContext *ctx) +Value ObjectPrototype::method_getOwnPropertyDescriptor(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.getOwnPropertyDescriptors")); + return Value::undefinedValue(); } -void ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) +Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) { Value O = ctx->argument(0); if (! O.isObject()) ctx->throwTypeError(); - else { - ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); - Array &a = array->value; - if (PropertyTable *members = O.objectValue()->members) { - for (PropertyTableEntry **it = members->begin(), **end = members->end(); it != end; ++it) { - if (PropertyTableEntry *prop = *it) { - a.push(Value::fromString(prop->name)); - } + + ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); + Array &a = array->value; + if (PropertyTable *members = O.objectValue()->members) { + for (PropertyTableEntry **it = members->begin(), **end = members->end(); it != end; ++it) { + if (PropertyTableEntry *prop = *it) { + a.push(Value::fromString(prop->name)); } } - ctx->result = Value::fromObject(array); } + return Value::fromObject(array); } -void ObjectPrototype::method_create(ExecutionContext *ctx) +Value ObjectPrototype::method_create(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.create")); + return Value::undefinedValue(); } -void ObjectPrototype::method_defineProperty(ExecutionContext *ctx) +Value ObjectPrototype::method_defineProperty(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.defineProperty")); + return Value::undefinedValue(); } -void ObjectPrototype::method_defineProperties(ExecutionContext *ctx) +Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.defineProperties")); + return Value::undefinedValue(); } -void ObjectPrototype::method_seal(ExecutionContext *ctx) +Value ObjectPrototype::method_seal(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.seal")); + return Value::undefinedValue(); } -void ObjectPrototype::method_freeze(ExecutionContext *ctx) +Value ObjectPrototype::method_freeze(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.freeze")); + return Value::undefinedValue(); } -void ObjectPrototype::method_preventExtensions(ExecutionContext *ctx) +Value ObjectPrototype::method_preventExtensions(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.preventExtensions")); + return Value::undefinedValue(); } -void ObjectPrototype::method_isSealed(ExecutionContext *ctx) +Value ObjectPrototype::method_isSealed(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.isSealed")); + return Value::undefinedValue(); } -void ObjectPrototype::method_isFrozen(ExecutionContext *ctx) +Value ObjectPrototype::method_isFrozen(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.isFrozen")); + return Value::undefinedValue(); } -void ObjectPrototype::method_isExtensible(ExecutionContext *ctx) +Value ObjectPrototype::method_isExtensible(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.isExtensible")); + return Value::undefinedValue(); } -void ObjectPrototype::method_keys(ExecutionContext *ctx) +Value ObjectPrototype::method_keys(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.keys")); + return Value::undefinedValue(); } -void ObjectPrototype::method_toString(ExecutionContext *ctx) +Value ObjectPrototype::method_toString(ExecutionContext *ctx) { if (! ctx->thisObject.isObject()) ctx->throwTypeError(); - else - ctx->result = Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(ctx->thisObject.objectValue()->className())); + return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(ctx->thisObject.objectValue()->className())); } -void ObjectPrototype::method_toLocaleString(ExecutionContext *ctx) +Value ObjectPrototype::method_toLocaleString(ExecutionContext *ctx) { - method_toString(ctx); + return method_toString(ctx); } -void ObjectPrototype::method_valueOf(ExecutionContext *ctx) +Value ObjectPrototype::method_valueOf(ExecutionContext *ctx) { - Value o = ctx->thisObject.toObject(ctx); - ctx->result = o; + return ctx->thisObject.toObject(ctx); } -void ObjectPrototype::method_hasOwnProperty(ExecutionContext *ctx) +Value ObjectPrototype::method_hasOwnProperty(ExecutionContext *ctx) { String *P = ctx->argument(0).toString(ctx); Value O = ctx->thisObject.toObject(ctx); bool r = O.objectValue()->__getOwnProperty__(ctx, P) != 0; - ctx->result = Value::fromBoolean(r); + return Value::fromBoolean(r); } -void ObjectPrototype::method_isPrototypeOf(ExecutionContext *ctx) +Value ObjectPrototype::method_isPrototypeOf(ExecutionContext *ctx) { Value V = ctx->argument(0); if (! V.isObject()) - ctx->result = Value::fromBoolean(false); - else { - Value O = ctx->thisObject.toObject(ctx); - Object *proto = V.objectValue()->prototype; - ctx->result = Value::fromBoolean(proto && O.objectValue() == proto); - } + return Value::fromBoolean(false); + + Value O = ctx->thisObject.toObject(ctx); + Object *proto = V.objectValue()->prototype; + return Value::fromBoolean(proto && O.objectValue() == proto); } -void ObjectPrototype::method_propertyIsEnumerable(ExecutionContext *ctx) +Value ObjectPrototype::method_propertyIsEnumerable(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Object.prototype.propertyIsEnumerable")); + return Value::undefinedValue(); } // @@ -707,7 +715,7 @@ StringCtor::StringCtor(ExecutionContext *scope) { } -void StringCtor::construct(ExecutionContext *ctx) +Value StringCtor::construct(ExecutionContext *ctx) { Value value; if (ctx->argumentCount()) @@ -715,15 +723,16 @@ void StringCtor::construct(ExecutionContext *ctx) else value = Value::fromString(ctx, QString()); ctx->thisObject = Value::fromObject(ctx->engine->newStringObject(value)); + return ctx->thisObject; } -void StringCtor::call(ExecutionContext *ctx) +Value StringCtor::call(ExecutionContext *ctx) { const Value arg = ctx->argument(0); if (arg.isUndefined()) - ctx->result = Value::fromString(ctx->engine->newString(QString())); + return Value::fromString(ctx->engine->newString(QString())); else - ctx->result = __qmljs_to_string(arg, ctx); + return __qmljs_to_string(arg, ctx); } void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) @@ -763,25 +772,23 @@ QString StringPrototype::getThisString(ExecutionContext *ctx) } } -void StringPrototype::method_toString(ExecutionContext *ctx) +Value StringPrototype::method_toString(ExecutionContext *ctx) { - if (StringObject *o = ctx->thisObject.asStringObject()) { - ctx->result = o->value; - } else { + StringObject *o = ctx->thisObject.asStringObject(); + if (!o) ctx->throwTypeError(); - } + return o->value; } -void StringPrototype::method_valueOf(ExecutionContext *ctx) +Value StringPrototype::method_valueOf(ExecutionContext *ctx) { - if (StringObject *o = ctx->thisObject.asStringObject()) { - ctx->result = o->value; - } else { + StringObject *o = ctx->thisObject.asStringObject(); + if (!o) ctx->throwTypeError(); - } + return o->value; } -void StringPrototype::method_charAt(ExecutionContext *ctx) +Value StringPrototype::method_charAt(ExecutionContext *ctx) { const QString str = getThisString(ctx); @@ -793,10 +800,10 @@ void StringPrototype::method_charAt(ExecutionContext *ctx) if (pos >= 0 && pos < str.length()) result += str.at(pos); - ctx->result = Value::fromString(ctx, result); + return Value::fromString(ctx, result); } -void StringPrototype::method_charCodeAt(ExecutionContext *ctx) +Value StringPrototype::method_charCodeAt(ExecutionContext *ctx) { const QString str = getThisString(ctx); @@ -809,10 +816,10 @@ void StringPrototype::method_charCodeAt(ExecutionContext *ctx) if (pos >= 0 && pos < str.length()) result = str.at(pos).unicode(); - ctx->result = Value::fromDouble(result); + return Value::fromDouble(result); } -void StringPrototype::method_concat(ExecutionContext *ctx) +Value StringPrototype::method_concat(ExecutionContext *ctx) { QString value = getThisString(ctx); @@ -822,10 +829,10 @@ void StringPrototype::method_concat(ExecutionContext *ctx) value += v.stringValue()->toQString(); } - ctx->result = Value::fromString(ctx, value); + return Value::fromString(ctx, value); } -void StringPrototype::method_indexOf(ExecutionContext *ctx) +Value StringPrototype::method_indexOf(ExecutionContext *ctx) { QString value = getThisString(ctx); @@ -841,10 +848,10 @@ void StringPrototype::method_indexOf(ExecutionContext *ctx) if (! value.isEmpty()) index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); - ctx->result = Value::fromDouble(index); + return Value::fromDouble(index); } -void StringPrototype::method_lastIndexOf(ExecutionContext *ctx) +Value StringPrototype::method_lastIndexOf(ExecutionContext *ctx) { const QString value = getThisString(ctx); @@ -865,35 +872,38 @@ void StringPrototype::method_lastIndexOf(ExecutionContext *ctx) if (!searchString.isEmpty() && pos == value.length()) --pos; int index = value.lastIndexOf(searchString, pos); - ctx->result = Value::fromDouble(index); + return Value::fromDouble(index); } -void StringPrototype::method_localeCompare(ExecutionContext *ctx) +Value StringPrototype::method_localeCompare(ExecutionContext *ctx) { const QString value = getThisString(ctx); const QString that = ctx->argument(0).toString(ctx)->toQString(); - ctx->result = Value::fromDouble(QString::localeAwareCompare(value, that)); + return Value::fromDouble(QString::localeAwareCompare(value, that)); } -void StringPrototype::method_match(ExecutionContext *ctx) +Value StringPrototype::method_match(ExecutionContext *ctx) { // requires Regexp ctx->throwUnimplemented(QStringLiteral("String.prototype.match")); + return Value::undefinedValue(); } -void StringPrototype::method_replace(ExecutionContext *ctx) +Value StringPrototype::method_replace(ExecutionContext *ctx) { // requires Regexp ctx->throwUnimplemented(QStringLiteral("String.prototype.replace")); + return Value::undefinedValue(); } -void StringPrototype::method_search(ExecutionContext *ctx) +Value StringPrototype::method_search(ExecutionContext *ctx) { // requires Regexp ctx->throwUnimplemented(QStringLiteral("String.prototype.search")); + return Value::undefinedValue(); } -void StringPrototype::method_slice(ExecutionContext *ctx) +Value StringPrototype::method_slice(ExecutionContext *ctx) { const QString text = getThisString(ctx); const int length = text.length(); @@ -913,15 +923,16 @@ void StringPrototype::method_slice(ExecutionContext *ctx) end = qMin(end, length); int count = qMax(0, end - start); - ctx->result = Value::fromString(ctx, text.mid(start, count)); + return Value::fromString(ctx, text.mid(start, count)); } -void StringPrototype::method_split(ExecutionContext *ctx) +Value StringPrototype::method_split(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("String.prototype.splt")); + return Value::undefinedValue(); } -void StringPrototype::method_substr(ExecutionContext *ctx) +Value StringPrototype::method_substr(ExecutionContext *ctx) { const QString value = getThisString(ctx); @@ -941,10 +952,10 @@ void StringPrototype::method_substr(ExecutionContext *ctx) qint32 x = Value::toInt32(start); qint32 y = Value::toInt32(length); - ctx->result = Value::fromString(ctx, value.mid(x, y)); + return Value::fromString(ctx, value.mid(x, y)); } -void StringPrototype::method_substring(ExecutionContext *ctx) +Value StringPrototype::method_substring(ExecutionContext *ctx) { QString value = getThisString(ctx); int length = value.length(); @@ -978,39 +989,39 @@ void StringPrototype::method_substring(ExecutionContext *ctx) qint32 x = Value::toInt32(start); qint32 y = Value::toInt32(end - start); - ctx->result = Value::fromString(ctx, value.mid(x, y)); + return Value::fromString(ctx, value.mid(x, y)); } -void StringPrototype::method_toLowerCase(ExecutionContext *ctx) +Value StringPrototype::method_toLowerCase(ExecutionContext *ctx) { QString value = getThisString(ctx); - ctx->result = Value::fromString(ctx, value.toLower()); + return Value::fromString(ctx, value.toLower()); } -void StringPrototype::method_toLocaleLowerCase(ExecutionContext *ctx) +Value StringPrototype::method_toLocaleLowerCase(ExecutionContext *ctx) { - method_toLowerCase(ctx); + return method_toLowerCase(ctx); } -void StringPrototype::method_toUpperCase(ExecutionContext *ctx) +Value StringPrototype::method_toUpperCase(ExecutionContext *ctx) { QString value = getThisString(ctx); - ctx->result = Value::fromString(ctx, value.toUpper()); + return Value::fromString(ctx, value.toUpper()); } -void StringPrototype::method_toLocaleUpperCase(ExecutionContext *ctx) +Value StringPrototype::method_toLocaleUpperCase(ExecutionContext *ctx) { - method_toUpperCase(ctx); + return method_toUpperCase(ctx); } -void StringPrototype::method_fromCharCode(ExecutionContext *ctx) +Value StringPrototype::method_fromCharCode(ExecutionContext *ctx) { QString str; for (unsigned i = 0; i < ctx->argumentCount(); ++i) { QChar c(ctx->argument(i).toUInt16(ctx)); str += c; } - ctx->result = Value::fromString(ctx, str); + return Value::fromString(ctx, str); } // @@ -1021,16 +1032,17 @@ NumberCtor::NumberCtor(ExecutionContext *scope) { } -void NumberCtor::construct(ExecutionContext *ctx) +Value NumberCtor::construct(ExecutionContext *ctx) { const double n = ctx->argument(0).toNumber(ctx); ctx->thisObject = Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(n))); + return ctx->thisObject; } -void NumberCtor::call(ExecutionContext *ctx) +Value NumberCtor::call(ExecutionContext *ctx) { double value = ctx->argumentCount() ? ctx->argument(0).toNumber(ctx) : 0; - ctx->result = Value::fromDouble(value); + return Value::fromDouble(value); } void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) @@ -1058,138 +1070,135 @@ void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) __put__(ctx, QStringLiteral("toPrecision"), method_toPrecision); } -void NumberPrototype::method_toString(ExecutionContext *ctx) +Value NumberPrototype::method_toString(ExecutionContext *ctx) { - if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { - Value arg = ctx->argument(0); - if (!arg.isUndefined()) { - int radix = arg.toInt32(ctx); - if (radix < 2 || radix > 36) { - ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") - .arg(radix)); - return; - } + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); - double num = thisObject->value.asDouble(); - if (qIsNaN(num)) { - ctx->result = Value::fromString(ctx, QStringLiteral("NaN")); - return; - } else if (qIsInf(num)) { - ctx->result = Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); - return; - } + Value arg = ctx->argument(0); + if (!arg.isUndefined()) { + int radix = arg.toInt32(ctx); + if (radix < 2 || radix > 36) { + ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") + .arg(radix)); + return Value::undefinedValue(); + } - if (radix != 10) { - QString str; - bool negative = false; - if (num < 0) { - negative = true; - num = -num; - } - double frac = num - ::floor(num); - num = Value::toInteger(num); + double num = thisObject->value.asDouble(); + if (qIsNaN(num)) { + return Value::fromString(ctx, QStringLiteral("NaN")); + } else if (qIsInf(num)) { + return Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); + } + + if (radix != 10) { + QString str; + bool negative = false; + if (num < 0) { + negative = true; + num = -num; + } + double frac = num - ::floor(num); + num = Value::toInteger(num); + do { + char c = (char)::fmod(num, radix); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.prepend(QLatin1Char(c)); + num = ::floor(num / radix); + } while (num != 0); + if (frac != 0) { + str.append(QLatin1Char('.')); do { - char c = (char)::fmod(num, radix); + frac = frac * radix; + char c = (char)::floor(frac); c = (c < 10) ? (c + '0') : (c - 10 + 'a'); - str.prepend(QLatin1Char(c)); - num = ::floor(num / radix); - } while (num != 0); - if (frac != 0) { - str.append(QLatin1Char('.')); - do { - frac = frac * radix; - char c = (char)::floor(frac); - c = (c < 10) ? (c + '0') : (c - 10 + 'a'); - str.append(QLatin1Char(c)); - frac = frac - ::floor(frac); - } while (frac != 0); - } - if (negative) - str.prepend(QLatin1Char('-')); - ctx->result = Value::fromString(ctx, str); - return; + str.append(QLatin1Char(c)); + frac = frac - ::floor(frac); + } while (frac != 0); } + if (negative) + str.prepend(QLatin1Char('-')); + return Value::fromString(ctx, str); } - - Value internalValue = thisObject->value; - String *str = internalValue.toString(ctx); - ctx->result = Value::fromString(str); - } else { - ctx->throwTypeError(); } + + Value internalValue = thisObject->value; + String *str = internalValue.toString(ctx); + return Value::fromString(str); } -void NumberPrototype::method_toLocaleString(ExecutionContext *ctx) +Value NumberPrototype::method_toLocaleString(ExecutionContext *ctx) { - if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { - String *str = thisObject->value.toString(ctx); - ctx->result = Value::fromString(str); - } else { + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) ctx->throwTypeError(); - } + + String *str = thisObject->value.toString(ctx); + return Value::fromString(str); } -void NumberPrototype::method_valueOf(ExecutionContext *ctx) +Value NumberPrototype::method_valueOf(ExecutionContext *ctx) { - if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { - ctx->result = thisObject->value; - } else { + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) ctx->throwTypeError(); - } + + return thisObject->value; } -void NumberPrototype::method_toFixed(ExecutionContext *ctx) +Value NumberPrototype::method_toFixed(ExecutionContext *ctx) { - if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { - double fdigits = 0; + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); - if (ctx->argumentCount() > 0) - fdigits = ctx->argument(0).toInteger(ctx); + double fdigits = 0; - if (qIsNaN(fdigits)) - fdigits = 0; + if (ctx->argumentCount() > 0) + fdigits = ctx->argument(0).toInteger(ctx); - double v = thisObject->value.asDouble(); - QString str; - if (qIsNaN(v)) - str = QString::fromLatin1("NaN"); - else if (qIsInf(v)) - str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); - else - str = QString::number(v, 'f', int (fdigits)); - ctx->result = Value::fromString(ctx, str); - } else { - ctx->throwTypeError(); - } + if (qIsNaN(fdigits)) + fdigits = 0; + + double v = thisObject->value.asDouble(); + QString str; + if (qIsNaN(v)) + str = QString::fromLatin1("NaN"); + else if (qIsInf(v)) + str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); + else + str = QString::number(v, 'f', int (fdigits)); + return Value::fromString(ctx, str); } -void NumberPrototype::method_toExponential(ExecutionContext *ctx) +Value NumberPrototype::method_toExponential(ExecutionContext *ctx) { - if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { - double fdigits = 0; + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); - if (ctx->argumentCount() > 0) - fdigits = ctx->argument(0).toInteger(ctx); + double fdigits = 0; - QString z = QString::number(thisObject->value.asDouble(), 'e', int (fdigits)); - ctx->result = Value::fromString(ctx, z); - } else { - ctx->throwTypeError(); - } + if (ctx->argumentCount() > 0) + fdigits = ctx->argument(0).toInteger(ctx); + + QString z = QString::number(thisObject->value.asDouble(), 'e', int (fdigits)); + return Value::fromString(ctx, z); } -void NumberPrototype::method_toPrecision(ExecutionContext *ctx) +Value NumberPrototype::method_toPrecision(ExecutionContext *ctx) { - if (NumberObject *thisObject = ctx->thisObject.asNumberObject()) { - double fdigits = 0; + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); - if (ctx->argumentCount() > 0) - fdigits = ctx->argument(0).toInteger(ctx); + double fdigits = 0; - ctx->result = Value::fromString(ctx, QString::number(thisObject->value.asDouble(), 'g', int (fdigits))); - } else { - ctx->throwTypeError(); - } + if (ctx->argumentCount() > 0) + fdigits = ctx->argument(0).toInteger(ctx); + + return Value::fromString(ctx, QString::number(thisObject->value.asDouble(), 'g', int (fdigits))); } // @@ -1200,16 +1209,17 @@ BooleanCtor::BooleanCtor(ExecutionContext *scope) { } -void BooleanCtor::construct(ExecutionContext *ctx) +Value BooleanCtor::construct(ExecutionContext *ctx) { const double n = ctx->argument(0).toBoolean(ctx); ctx->thisObject = Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n))); + return ctx->thisObject; } -void BooleanCtor::call(ExecutionContext *ctx) +Value BooleanCtor::call(ExecutionContext *ctx) { bool value = ctx->argumentCount() ? ctx->argument(0).toBoolean(ctx) : 0; - ctx->result = Value::fromBoolean(value); + return Value::fromBoolean(value); } void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) @@ -1220,22 +1230,22 @@ void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) __put__(ctx, QStringLiteral("valueOf"), method_valueOf); } -void BooleanPrototype::method_toString(ExecutionContext *ctx) +Value BooleanPrototype::method_toString(ExecutionContext *ctx) { - if (BooleanObject *thisObject = ctx->thisObject.asBooleanObject()) { - ctx->result = Value::fromString(ctx, QLatin1String(thisObject->value.booleanValue() ? "true" : "false")); - } else { + BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); + if (!thisObject) ctx->throwTypeError(); - } + + return Value::fromString(ctx, QLatin1String(thisObject->value.booleanValue() ? "true" : "false")); } -void BooleanPrototype::method_valueOf(ExecutionContext *ctx) +Value BooleanPrototype::method_valueOf(ExecutionContext *ctx) { - if (BooleanObject *thisObject = ctx->thisObject.asBooleanObject()) { - ctx->result = thisObject->value; - } else { + BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); + if (!thisObject) ctx->throwTypeError(); - } + + return thisObject->value; } // @@ -1246,13 +1256,14 @@ ArrayCtor::ArrayCtor(ExecutionContext *scope) { } -void ArrayCtor::construct(ExecutionContext *ctx) +Value ArrayCtor::construct(ExecutionContext *ctx) { - call(ctx); - ctx->thisObject = ctx->result; + Value result = call(ctx); + ctx->thisObject = result; + return result; } -void ArrayCtor::call(ExecutionContext *ctx) +Value ArrayCtor::call(ExecutionContext *ctx) { Array value; if (ctx->argumentCount() == 1 && ctx->argument(0).isNumber()) { @@ -1261,7 +1272,7 @@ void ArrayCtor::call(ExecutionContext *ctx) if (size != double(isize)) { ctx->throwError(QStringLiteral("Invalid array length")); - return; + return Value::undefinedValue(); } value.resize(isize); @@ -1271,7 +1282,7 @@ void ArrayCtor::call(ExecutionContext *ctx) } } - ctx->result = Value::fromObject(ctx->engine->newArrayObject(value)); + return Value::fromObject(ctx->engine->newArrayObject(value)); } void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) @@ -1301,17 +1312,17 @@ void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) __put__(ctx, QStringLiteral("reduceRight"), method_reduceRight, 0); } -void ArrayPrototype::method_toString(ExecutionContext *ctx) +Value ArrayPrototype::method_toString(ExecutionContext *ctx) { - method_join(ctx); + return method_join(ctx); } -void ArrayPrototype::method_toLocaleString(ExecutionContext *ctx) +Value ArrayPrototype::method_toLocaleString(ExecutionContext *ctx) { - method_toString(ctx); + return method_toString(ctx); } -void ArrayPrototype::method_concat(ExecutionContext *ctx) +Value ArrayPrototype::method_concat(ExecutionContext *ctx) { Array result; @@ -1333,10 +1344,10 @@ void ArrayPrototype::method_concat(ExecutionContext *ctx) result.assign(k, arg); } - ctx->result = Value::fromObject(ctx->engine->newArrayObject(result)); + return Value::fromObject(ctx->engine->newArrayObject(result)); } -void ArrayPrototype::method_join(ExecutionContext *ctx) +Value ArrayPrototype::method_join(ExecutionContext *ctx) { Value arg = ctx->argument(0); @@ -1352,10 +1363,8 @@ void ArrayPrototype::method_join(ExecutionContext *ctx) static QSet visitedArrayElements; - if (! r2 || visitedArrayElements.contains(self.objectValue())) { - ctx->result = Value::fromString(ctx, QString()); - return; - } + if (! r2 || visitedArrayElements.contains(self.objectValue())) + return Value::fromString(ctx, QString()); // avoid infinite recursion visitedArrayElements.insert(self.objectValue()); @@ -1391,31 +1400,30 @@ void ArrayPrototype::method_join(ExecutionContext *ctx) } visitedArrayElements.remove(self.objectValue()); - ctx->result = Value::fromString(ctx, R); + return Value::fromString(ctx, R); } -void ArrayPrototype::method_pop(ExecutionContext *ctx) +Value ArrayPrototype::method_pop(ExecutionContext *ctx) { Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - Value elt = instance->value.pop(); - ctx->result = elt; - } else { - Value r1 = self.property(ctx, ctx->engine->id_length); - quint32 r2 = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; - if (! r2) { - self.objectValue()->__put__(ctx, ctx->engine->id_length, Value::fromDouble(0)); - } else { - String *r6 = Value::fromDouble(r2 - 1).toString(ctx); - Value r7 = self.property(ctx, r6); - self.objectValue()->__delete__(ctx, r6, 0); - self.objectValue()->__put__(ctx, ctx->engine->id_length, Value::fromDouble(2 - 1)); - ctx->result = r7; - } + if (ArrayObject *instance = self.asArrayObject()) + return instance->value.pop(); + + Value r1 = self.property(ctx, ctx->engine->id_length); + quint32 r2 = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; + if (r2) { + String *r6 = Value::fromDouble(r2 - 1).toString(ctx); + Value r7 = self.property(ctx, r6); + self.objectValue()->__delete__(ctx, r6, 0); + self.objectValue()->__put__(ctx, ctx->engine->id_length, Value::fromDouble(2 - 1)); + return r7; } + + self.objectValue()->__put__(ctx, ctx->engine->id_length, Value::fromDouble(0)); + return Value::undefinedValue(); } -void ArrayPrototype::method_push(ExecutionContext *ctx) +Value ArrayPrototype::method_push(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { @@ -1424,48 +1432,47 @@ void ArrayPrototype::method_push(ExecutionContext *ctx) Value val = ctx->argument(i); instance->value.assign(pos++, val); } - ctx->result = Value::fromDouble(pos); - } else { - Value r1 = self.property(ctx, ctx->engine->id_length); - quint32 n = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; - for (unsigned int index = 0; index < ctx->argumentCount(); ++index, ++n) { - Value r3 = ctx->argument(index); - String *name = Value::fromDouble(n).toString(ctx); - self.objectValue()->__put__(ctx, name, r3); - } - Value r = Value::fromDouble(n); - self.objectValue()->__put__(ctx, ctx->engine->id_length, r); - ctx->result = r; + return Value::fromDouble(pos); } + + Value r1 = self.property(ctx, ctx->engine->id_length); + quint32 n = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; + for (unsigned int index = 0; index < ctx->argumentCount(); ++index, ++n) { + Value r3 = ctx->argument(index); + String *name = Value::fromDouble(n).toString(ctx); + self.objectValue()->__put__(ctx, name, r3); + } + Value r = Value::fromDouble(n); + self.objectValue()->__put__(ctx, ctx->engine->id_length, r); + return r; } -void ArrayPrototype::method_reverse(ExecutionContext *ctx) +Value ArrayPrototype::method_reverse(ExecutionContext *ctx) { - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - int lo = 0, hi = instance->value.count() - 1; - - for (; lo < hi; ++lo, --hi) { - Value tmp = instance->value.at(lo); - instance->value.assign(lo, instance->value.at(hi)); - instance->value.assign(hi, tmp); - } - } else { + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) ctx->throwUnimplemented(QStringLiteral("Array.prototype.reverse")); + + int lo = 0, hi = instance->value.count() - 1; + + for (; lo < hi; ++lo, --hi) { + Value tmp = instance->value.at(lo); + instance->value.assign(lo, instance->value.at(hi)); + instance->value.assign(hi, tmp); } + return Value::undefinedValue(); } -void ArrayPrototype::method_shift(ExecutionContext *ctx) +Value ArrayPrototype::method_shift(ExecutionContext *ctx) { - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - ctx->result = instance->value.takeFirst(); - } else { - ctx->throwUnimplemented(QStringLiteral("Array.prototype.reverse")); - } + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) + ctx->throwUnimplemented(QStringLiteral("Array.prototype.shift")); + + return instance->value.takeFirst(); } -void ArrayPrototype::method_slice(ExecutionContext *ctx) +Value ArrayPrototype::method_slice(ExecutionContext *ctx) { // ### TODO implement the fast non-generic version of slice. @@ -1488,242 +1495,238 @@ void ArrayPrototype::method_slice(ExecutionContext *ctx) if (! v.isUndefined()) result.assign(n++, v); } - ctx->result = Value::fromObject(ctx->engine->newArrayObject(result)); + return Value::fromObject(ctx->engine->newArrayObject(result)); } -void ArrayPrototype::method_sort(ExecutionContext *ctx) +Value ArrayPrototype::method_sort(ExecutionContext *ctx) { - Value self = ctx->thisObject; - Value comparefn = ctx->argument(0); - if (ArrayObject *instance = self.asArrayObject()) { - instance->value.sort(ctx, comparefn); - ctx->result = ctx->thisObject; - } else { + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) ctx->throwUnimplemented(QStringLiteral("Array.prototype.sort")); - } + + Value comparefn = ctx->argument(0); + instance->value.sort(ctx, comparefn); + return ctx->thisObject; } -void ArrayPrototype::method_splice(ExecutionContext *ctx) +Value ArrayPrototype::method_splice(ExecutionContext *ctx) { if (ctx->argumentCount() < 2) - return; + // ### check + return Value::undefinedValue(); + + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) + ctx->throwUnimplemented(QStringLiteral("Array.prototype.splice")); double start = ctx->argument(0).toInteger(ctx); double deleteCount = ctx->argument(1).toInteger(ctx); Value a = Value::fromObject(ctx->engine->newArrayObject()); - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - QVector items; - for (unsigned int i = 2; i < ctx->argumentCount(); ++i) - items << ctx->argument(i); - ArrayObject *otherInstance = a.asArrayObject(); - assert(otherInstance); - instance->value.splice(start, deleteCount, items, otherInstance->value); - ctx->result = a; - } else { - ctx->throwUnimplemented(QStringLiteral("Array.prototype.splice")); - } + QVector items; + for (unsigned int i = 2; i < ctx->argumentCount(); ++i) + items << ctx->argument(i); + ArrayObject *otherInstance = a.asArrayObject(); + assert(otherInstance); + instance->value.splice(start, deleteCount, items, otherInstance->value); + return a; } -void ArrayPrototype::method_unshift(ExecutionContext *ctx) +Value ArrayPrototype::method_unshift(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); + return Value::undefinedValue(); } -void ArrayPrototype::method_indexOf(ExecutionContext *ctx) +Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); + return Value::undefinedValue(); } -void ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) +Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); + return Value::undefinedValue(); } -void ArrayPrototype::method_every(ExecutionContext *ctx) +Value ArrayPrototype::method_every(ExecutionContext *ctx) { - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - Value callback = ctx->argument(0); - Value thisArg = ctx->argument(1); - bool ok = true; - for (uint k = 0; ok && k < instance->value.size(); ++k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); - ok = __qmljs_to_boolean(r, ctx); - } - ctx->result = Value::fromBoolean(ok); - } else { + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) ctx->throwUnimplemented(QStringLiteral("Array.prototype.every")); + + Value callback = ctx->argument(0); + Value thisArg = ctx->argument(1); + bool ok = true; + for (uint k = 0; ok && k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); + ok = __qmljs_to_boolean(r, ctx); } + return Value::fromBoolean(ok); } -void ArrayPrototype::method_some(ExecutionContext *ctx) +Value ArrayPrototype::method_some(ExecutionContext *ctx) { - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - Value callback = ctx->argument(0); - Value thisArg = ctx->argument(1); - bool ok = false; - for (uint k = 0; !ok && k < instance->value.size(); ++k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); - ok = __qmljs_to_boolean(r, ctx); - } - ctx->result = Value::fromBoolean(ok); - } else { + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) ctx->throwUnimplemented(QStringLiteral("Array.prototype.some")); + + Value callback = ctx->argument(0); + Value thisArg = ctx->argument(1); + bool ok = false; + for (uint k = 0; !ok && k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); + ok = __qmljs_to_boolean(r, ctx); } + return Value::fromBoolean(ok); } -void ArrayPrototype::method_forEach(ExecutionContext *ctx) +Value ArrayPrototype::method_forEach(ExecutionContext *ctx) { - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - Value callback = ctx->argument(0); - Value thisArg = ctx->argument(1); - for (quint32 k = 0; k < instance->value.size(); ++k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - /*Value r =*/ __qmljs_call_value(ctx, thisArg, callback, args, 3); - } - } else { + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) ctx->throwUnimplemented(QStringLiteral("Array.prototype.forEach")); + + Value callback = ctx->argument(0); + Value thisArg = ctx->argument(1); + for (quint32 k = 0; k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + /*Value r =*/ __qmljs_call_value(ctx, thisArg, callback, args, 3); } + return Value::undefinedValue(); } -void ArrayPrototype::method_map(ExecutionContext *ctx) +Value ArrayPrototype::method_map(ExecutionContext *ctx) { - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - Value callback = ctx->argument(0); - Value thisArg = ctx->argument(1); - ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); - a->value.resize(instance->value.size()); - for (quint32 k = 0; k < instance->value.size(); ++k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); - a->value.assign(k, r); - } - ctx->result = Value::fromObject(a); - } else { + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) ctx->throwUnimplemented(QStringLiteral("Array.prototype.map")); + + Value callback = ctx->argument(0); + Value thisArg = ctx->argument(1); + ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); + a->value.resize(instance->value.size()); + for (quint32 k = 0; k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); + a->value.assign(k, r); } + return Value::fromObject(a); } -void ArrayPrototype::method_filter(ExecutionContext *ctx) +Value ArrayPrototype::method_filter(ExecutionContext *ctx) { - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - Value callback = ctx->argument(0); - Value thisArg = ctx->argument(1); - ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); - for (quint32 k = 0; k < instance->value.size(); ++k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); - if (__qmljs_to_boolean(r, ctx)) { - const uint index = a->value.size(); - a->value.resize(index + 1); - a->value.assign(index, v); - } - } - ctx->result = Value::fromObject(a); - } else { + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) ctx->throwUnimplemented(QStringLiteral("Array.prototype.filter")); + + Value callback = ctx->argument(0); + Value thisArg = ctx->argument(1); + ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); + for (quint32 k = 0; k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); + if (__qmljs_to_boolean(r, ctx)) { + const uint index = a->value.size(); + a->value.resize(index + 1); + a->value.assign(index, v); + } } + return Value::fromObject(a); } -void ArrayPrototype::method_reduce(ExecutionContext *ctx) +Value ArrayPrototype::method_reduce(ExecutionContext *ctx) { - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - Value callback = ctx->argument(0); - Value initialValue = ctx->argument(1); - Value acc = initialValue; - for (quint32 k = 0; k < instance->value.size(); ++k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - - if (acc.isUndefined()) { - acc = v; - continue; - } + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) + ctx->throwUnimplemented(QStringLiteral("Array.prototype.reduce")); - Value args[4]; - args[0] = acc; - args[1] = v; - args[2] = Value::fromDouble(k); - args[3] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, Value::undefinedValue(), callback, args, 4); - acc = r; + Value callback = ctx->argument(0); + Value initialValue = ctx->argument(1); + Value acc = initialValue; + for (quint32 k = 0; k < instance->value.size(); ++k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + + if (acc.isUndefined()) { + acc = v; + continue; } - ctx->result = acc; - } else { - ctx->throwUnimplemented(QStringLiteral("Array.prototype.reduce")); + + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k); + args[3] = ctx->thisObject; + Value r = __qmljs_call_value(ctx, Value::undefinedValue(), callback, args, 4); + acc = r; } + return acc; } -void ArrayPrototype::method_reduceRight(ExecutionContext *ctx) +Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) { - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - Value callback = ctx->argument(0); - Value initialValue = ctx->argument(1); - Value acc = initialValue; - for (int k = instance->value.size() - 1; k != -1; --k) { - Value v = instance->value.at(k); - if (v.isUndefined()) - continue; - - if (acc.isUndefined()) { - acc = v; - continue; - } + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) + ctx->throwUnimplemented(QStringLiteral("Array.prototype.reduceRight")); - Value args[4]; - args[0] = acc; - args[1] = v; - args[2] = Value::fromDouble(k); - args[3] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, Value::undefinedValue(), callback, args, 4); - acc = r; + Value callback = ctx->argument(0); + Value initialValue = ctx->argument(1); + Value acc = initialValue; + for (int k = instance->value.size() - 1; k != -1; --k) { + Value v = instance->value.at(k); + if (v.isUndefined()) + continue; + + if (acc.isUndefined()) { + acc = v; + continue; } - ctx->result = acc; - } else { - ctx->throwUnimplemented(QStringLiteral("Array.prototype.reduceRight")); + + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k); + args[3] = ctx->thisObject; + Value r = __qmljs_call_value(ctx, Value::undefinedValue(), callback, args, 4); + acc = r; } + return acc; } // @@ -1735,7 +1738,7 @@ FunctionCtor::FunctionCtor(ExecutionContext *scope) } // 15.3.2 -void FunctionCtor::construct(ExecutionContext *ctx) +Value FunctionCtor::construct(ExecutionContext *ctx) { QString args; QString body; @@ -1777,15 +1780,16 @@ void FunctionCtor::construct(ExecutionContext *ctx) isel(irf); ctx->thisObject = Value::fromObject(new ScriptFunction(ctx->engine->rootContext, irf)); + return ctx->thisObject; } // 15.3.1: This is equivalent to new Function(...) -void FunctionCtor::call(ExecutionContext *ctx) +Value FunctionCtor::call(ExecutionContext *ctx) { Value v = ctx->thisObject; - construct(ctx); - ctx->result = ctx->thisObject; + Value result = construct(ctx); ctx->thisObject = v; + return result; } void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) @@ -1798,17 +1802,16 @@ void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) __put__(ctx, QStringLiteral("bind"), method_bind, 0); } -void FunctionPrototype::method_toString(ExecutionContext *ctx) +Value FunctionPrototype::method_toString(ExecutionContext *ctx) { - if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { - Q_UNUSED(fun); - ctx->result = Value::fromString(ctx, QStringLiteral("function() { [code] }")); - } else { + FunctionObject *fun = ctx->thisObject.asFunctionObject(); + if (!fun) ctx->throwTypeError(); - } + + return Value::fromString(ctx, QStringLiteral("function() { [code] }")); } -void FunctionPrototype::method_apply(ExecutionContext *ctx) +Value FunctionPrototype::method_apply(ExecutionContext *ctx) { Value thisObject = ctx->argument(0).toObject(ctx); if (thisObject.isNull() || thisObject.isUndefined()) @@ -1826,30 +1829,30 @@ void FunctionPrototype::method_apply(ExecutionContext *ctx) } } else if (!(arg.isUndefined() || arg.isNull())) { ctx->throwError(QLatin1String("Function.prototype.apply: second argument is not an array")); - return; + return Value::undefinedValue(); } - ctx->result = __qmljs_call_value(ctx, thisObject, ctx->thisObject, args.data(), args.size()); + return __qmljs_call_value(ctx, thisObject, ctx->thisObject, args.data(), args.size()); } -void FunctionPrototype::method_call(ExecutionContext *ctx) +Value FunctionPrototype::method_call(ExecutionContext *ctx) { Value thisArg = ctx->argument(0); QVector args(ctx->argumentCount() ? ctx->argumentCount() - 1 : 0); if (ctx->argumentCount()) qCopy(ctx->variableEnvironment->arguments + 1, ctx->variableEnvironment->arguments + ctx->argumentCount(), args.begin()); - ctx->result = __qmljs_call_value(ctx, thisArg, ctx->thisObject, args.data(), args.size()); + return __qmljs_call_value(ctx, thisArg, ctx->thisObject, args.data(), args.size()); } -void FunctionPrototype::method_bind(ExecutionContext *ctx) +Value FunctionPrototype::method_bind(ExecutionContext *ctx) { - if (FunctionObject *fun = ctx->thisObject.asFunctionObject()) { - Q_UNUSED(fun); - ctx->throwUnimplemented(QStringLiteral("Function.prototype.bind")); - } else { + FunctionObject *fun = ctx->thisObject.asFunctionObject(); + if (!fun) ctx->throwTypeError(); - } + + ctx->throwUnimplemented(QStringLiteral("Function.prototype.bind")); + return Value::undefinedValue(); } // @@ -1860,7 +1863,7 @@ DateCtor::DateCtor(ExecutionContext *scope) { } -void DateCtor::construct(ExecutionContext *ctx) +Value DateCtor::construct(ExecutionContext *ctx) { double t = 0; @@ -1896,12 +1899,13 @@ void DateCtor::construct(ExecutionContext *ctx) Object *d = ctx->engine->newDateObject(Value::fromDouble(t)); ctx->thisObject = Value::fromObject(d); + return ctx->thisObject; } -void DateCtor::call(ExecutionContext *ctx) +Value DateCtor::call(ExecutionContext *ctx) { double t = currentTime(); - ctx->result = Value::fromString(ctx, ToString(t)); + return Value::fromString(ctx, ToString(t)); } void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) @@ -1969,27 +1973,30 @@ double DatePrototype::getThisDate(ExecutionContext *ctx) } } -void DatePrototype::method_MakeTime(ExecutionContext *ctx) +Value DatePrototype::method_MakeTime(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Data.MakeTime")); + return Value::undefinedValue(); } -void DatePrototype::method_MakeDate(ExecutionContext *ctx) +Value DatePrototype::method_MakeDate(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Data.MakeDate")); + return Value::undefinedValue(); } -void DatePrototype::method_TimeClip(ExecutionContext *ctx) +Value DatePrototype::method_TimeClip(ExecutionContext *ctx) { ctx->throwUnimplemented(QStringLiteral("Data.TimeClip")); + return Value::undefinedValue(); } -void DatePrototype::method_parse(ExecutionContext *ctx) +Value DatePrototype::method_parse(ExecutionContext *ctx) { - ctx->result = Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString())); + return Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString())); } -void DatePrototype::method_UTC(ExecutionContext *ctx) +Value DatePrototype::method_UTC(ExecutionContext *ctx) { const int numArgs = ctx->argumentCount(); if (numArgs >= 2) { @@ -2004,442 +2011,445 @@ void DatePrototype::method_UTC(ExecutionContext *ctx) year += 1900; double t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); - ctx->result = Value::fromDouble(TimeClip(t)); + return Value::fromDouble(TimeClip(t)); } + return Value::undefinedValue(); } -void DatePrototype::method_toString(ExecutionContext *ctx) +Value DatePrototype::method_toString(ExecutionContext *ctx) { double t = getThisDate(ctx); - ctx->result = Value::fromString(ctx, ToString(t)); + return Value::fromString(ctx, ToString(t)); } -void DatePrototype::method_toDateString(ExecutionContext *ctx) +Value DatePrototype::method_toDateString(ExecutionContext *ctx) { double t = getThisDate(ctx); - ctx->result = Value::fromString(ctx, ToDateString(t)); + return Value::fromString(ctx, ToDateString(t)); } -void DatePrototype::method_toTimeString(ExecutionContext *ctx) +Value DatePrototype::method_toTimeString(ExecutionContext *ctx) { double t = getThisDate(ctx); - ctx->result = Value::fromString(ctx, ToTimeString(t)); + return Value::fromString(ctx, ToTimeString(t)); } -void DatePrototype::method_toLocaleString(ExecutionContext *ctx) +Value DatePrototype::method_toLocaleString(ExecutionContext *ctx) { double t = getThisDate(ctx); - ctx->result = Value::fromString(ctx, ToLocaleString(t)); + return Value::fromString(ctx, ToLocaleString(t)); } -void DatePrototype::method_toLocaleDateString(ExecutionContext *ctx) +Value DatePrototype::method_toLocaleDateString(ExecutionContext *ctx) { double t = getThisDate(ctx); - ctx->result = Value::fromString(ctx, ToLocaleDateString(t)); + return Value::fromString(ctx, ToLocaleDateString(t)); } -void DatePrototype::method_toLocaleTimeString(ExecutionContext *ctx) +Value DatePrototype::method_toLocaleTimeString(ExecutionContext *ctx) { double t = getThisDate(ctx); - ctx->result = Value::fromString(ctx, ToLocaleTimeString(t)); + return Value::fromString(ctx, ToLocaleTimeString(t)); } -void DatePrototype::method_valueOf(ExecutionContext *ctx) +Value DatePrototype::method_valueOf(ExecutionContext *ctx) { double t = getThisDate(ctx); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getTime(ExecutionContext *ctx) +Value DatePrototype::method_getTime(ExecutionContext *ctx) { double t = getThisDate(ctx); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getYear(ExecutionContext *ctx) +Value DatePrototype::method_getYear(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = YearFromTime(LocalTime(t)) - 1900; - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getFullYear(ExecutionContext *ctx) +Value DatePrototype::method_getFullYear(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = YearFromTime(LocalTime(t)); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getUTCFullYear(ExecutionContext *ctx) +Value DatePrototype::method_getUTCFullYear(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = YearFromTime(t); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getMonth(ExecutionContext *ctx) +Value DatePrototype::method_getMonth(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = MonthFromTime(LocalTime(t)); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getUTCMonth(ExecutionContext *ctx) +Value DatePrototype::method_getUTCMonth(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = MonthFromTime(t); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getDate(ExecutionContext *ctx) +Value DatePrototype::method_getDate(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = DateFromTime(LocalTime(t)); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getUTCDate(ExecutionContext *ctx) +Value DatePrototype::method_getUTCDate(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = DateFromTime(t); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getDay(ExecutionContext *ctx) +Value DatePrototype::method_getDay(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = WeekDay(LocalTime(t)); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getUTCDay(ExecutionContext *ctx) +Value DatePrototype::method_getUTCDay(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = WeekDay(t); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getHours(ExecutionContext *ctx) +Value DatePrototype::method_getHours(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = HourFromTime(LocalTime(t)); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getUTCHours(ExecutionContext *ctx) +Value DatePrototype::method_getUTCHours(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = HourFromTime(t); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getMinutes(ExecutionContext *ctx) +Value DatePrototype::method_getMinutes(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = MinFromTime(LocalTime(t)); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getUTCMinutes(ExecutionContext *ctx) +Value DatePrototype::method_getUTCMinutes(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = MinFromTime(t); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getSeconds(ExecutionContext *ctx) +Value DatePrototype::method_getSeconds(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = SecFromTime(LocalTime(t)); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getUTCSeconds(ExecutionContext *ctx) +Value DatePrototype::method_getUTCSeconds(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = SecFromTime(t); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getMilliseconds(ExecutionContext *ctx) +Value DatePrototype::method_getMilliseconds(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = msFromTime(LocalTime(t)); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getUTCMilliseconds(ExecutionContext *ctx) +Value DatePrototype::method_getUTCMilliseconds(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = msFromTime(t); - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_getTimezoneOffset(ExecutionContext *ctx) +Value DatePrototype::method_getTimezoneOffset(ExecutionContext *ctx) { double t = getThisDate(ctx); if (! qIsNaN(t)) t = (t - LocalTime(t)) / msPerMinute; - ctx->result = Value::fromDouble(t); + return Value::fromDouble(t); } -void DatePrototype::method_setTime(ExecutionContext *ctx) +Value DatePrototype::method_setTime(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - self->value.setDouble(TimeClip(ctx->argument(0).toNumber(ctx))); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + self->value.setDouble(TimeClip(ctx->argument(0).toNumber(ctx))); + return self->value; } -void DatePrototype::method_setMilliseconds(ExecutionContext *ctx) +Value DatePrototype::method_setMilliseconds(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.asDouble()); - double ms = ctx->argument(0).toNumber(ctx); - self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + double t = LocalTime(self->value.asDouble()); + double ms = ctx->argument(0).toNumber(ctx); + self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + return self->value; } -void DatePrototype::method_setUTCMilliseconds(ExecutionContext *ctx) +Value DatePrototype::method_setUTCMilliseconds(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.asDouble(); - double ms = ctx->argument(0).toNumber(ctx); - self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + double t = self->value.asDouble(); + double ms = ctx->argument(0).toNumber(ctx); + self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + return self->value; } -void DatePrototype::method_setSeconds(ExecutionContext *ctx) +Value DatePrototype::method_setSeconds(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.asDouble()); - double sec = ctx->argument(0).toNumber(ctx); - double ms = (ctx->argumentCount() < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); - self->value.setDouble(t); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + double t = LocalTime(self->value.asDouble()); + double sec = ctx->argument(0).toNumber(ctx); + double ms = (ctx->argumentCount() < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.setDouble(t); + return self->value; } -void DatePrototype::method_setUTCSeconds(ExecutionContext *ctx) +Value DatePrototype::method_setUTCSeconds(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.asDouble(); - double sec = ctx->argument(0).toNumber(ctx); - double ms = (ctx->argumentCount() < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); - self->value.setDouble(t); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + double t = self->value.asDouble(); + double sec = ctx->argument(0).toNumber(ctx); + double ms = (ctx->argumentCount() < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.setDouble(t); + return self->value; } -void DatePrototype::method_setMinutes(ExecutionContext *ctx) +Value DatePrototype::method_setMinutes(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.asDouble()); - double min = ctx->argument(0).toNumber(ctx); - double sec = (ctx->argumentCount() < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); - double ms = (ctx->argumentCount() < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); - self->value.setDouble(t); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + double t = LocalTime(self->value.asDouble()); + double min = ctx->argument(0).toNumber(ctx); + double sec = (ctx->argumentCount() < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount() < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.setDouble(t); + return self->value; } -void DatePrototype::method_setUTCMinutes(ExecutionContext *ctx) +Value DatePrototype::method_setUTCMinutes(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.asDouble(); - double min = ctx->argument(0).toNumber(ctx); - double sec = (ctx->argumentCount() < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); - double ms = (ctx->argumentCount() < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); - self->value.setDouble(t); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + double t = self->value.asDouble(); + double min = ctx->argument(0).toNumber(ctx); + double sec = (ctx->argumentCount() < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount() < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.setDouble(t); + return self->value; } -void DatePrototype::method_setHours(ExecutionContext *ctx) +Value DatePrototype::method_setHours(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.asDouble()); - double hour = ctx->argument(0).toNumber(ctx); - double min = (ctx->argumentCount() < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); - double sec = (ctx->argumentCount() < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); - double ms = (ctx->argumentCount() < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); - self->value.setDouble(t); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + double t = LocalTime(self->value.asDouble()); + double hour = ctx->argument(0).toNumber(ctx); + double min = (ctx->argumentCount() < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); + double sec = (ctx->argumentCount() < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); + double ms = (ctx->argumentCount() < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.setDouble(t); + return self->value; } -void DatePrototype::method_setUTCHours(ExecutionContext *ctx) +Value DatePrototype::method_setUTCHours(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.asDouble(); - double hour = ctx->argument(0).toNumber(ctx); - double min = (ctx->argumentCount() < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); - double sec = (ctx->argumentCount() < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); - double ms = (ctx->argumentCount() < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); - self->value.setDouble(t); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + double t = self->value.asDouble(); + double hour = ctx->argument(0).toNumber(ctx); + double min = (ctx->argumentCount() < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); + double sec = (ctx->argumentCount() < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); + double ms = (ctx->argumentCount() < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.setDouble(t); + return self->value; } -void DatePrototype::method_setDate(ExecutionContext *ctx) +Value DatePrototype::method_setDate(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.asDouble()); - double date = ctx->argument(0).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); - self->value.setDouble(t); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + double t = LocalTime(self->value.asDouble()); + double date = ctx->argument(0).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; } -void DatePrototype::method_setUTCDate(ExecutionContext *ctx) +Value DatePrototype::method_setUTCDate(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.asDouble(); - double date = ctx->argument(0).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); - self->value.setDouble(t); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + double t = self->value.asDouble(); + double date = ctx->argument(0).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; } -void DatePrototype::method_setMonth(ExecutionContext *ctx) +Value DatePrototype::method_setMonth(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.asDouble()); - double month = ctx->argument(0).toNumber(ctx); - double date = (ctx->argumentCount() < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + double t = LocalTime(self->value.asDouble()); + double month = ctx->argument(0).toNumber(ctx); + double date = (ctx->argumentCount() < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; } -void DatePrototype::method_setUTCMonth(ExecutionContext *ctx) +Value DatePrototype::method_setUTCMonth(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.asDouble(); - double month = ctx->argument(0).toNumber(ctx); - double date = (ctx->argumentCount() < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + double t = self->value.asDouble(); + double month = ctx->argument(0).toNumber(ctx); + double date = (ctx->argumentCount() < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; } -void DatePrototype::method_setYear(ExecutionContext *ctx) +Value DatePrototype::method_setYear(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.asDouble(); - if (qIsNaN(t)) - t = 0; - else - t = LocalTime(t); - double year = ctx->argument(0).toNumber(ctx); - double r; - if (qIsNaN(year)) { - r = qSNaN(); - } else { - if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99)) - year += 1900; - r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); - r = UTC(MakeDate(r, TimeWithinDay(t))); - r = TimeClip(r); - } - self->value.setDouble(r); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); + + double t = self->value.asDouble(); + if (qIsNaN(t)) + t = 0; + else + t = LocalTime(t); + double year = ctx->argument(0).toNumber(ctx); + double r; + if (qIsNaN(year)) { + r = qSNaN(); + } else { + if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99)) + year += 1900; + r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); + r = UTC(MakeDate(r, TimeWithinDay(t))); + r = TimeClip(r); } + self->value.setDouble(r); + return self->value; } -void DatePrototype::method_setUTCFullYear(ExecutionContext *ctx) +Value DatePrototype::method_setUTCFullYear(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.asDouble(); - double year = ctx->argument(0).toNumber(ctx); - double month = (ctx->argumentCount() < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); - double date = (ctx->argumentCount() < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + double t = self->value.asDouble(); + double year = ctx->argument(0).toNumber(ctx); + double month = (ctx->argumentCount() < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount() < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; } -void DatePrototype::method_setFullYear(ExecutionContext *ctx) +Value DatePrototype::method_setFullYear(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = LocalTime(self->value.asDouble()); - double year = ctx->argument(0).toNumber(ctx); - double month = (ctx->argumentCount() < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); - double date = (ctx->argumentCount() < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - ctx->result = self->value; - } else { + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) ctx->throwTypeError(); - } + + double t = LocalTime(self->value.asDouble()); + double year = ctx->argument(0).toNumber(ctx); + double month = (ctx->argumentCount() < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount() < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; } -void DatePrototype::method_toUTCString(ExecutionContext *ctx) +Value DatePrototype::method_toUTCString(ExecutionContext *ctx) { - if (DateObject *self = ctx->thisObject.asDateObject()) { - double t = self->value.asDouble(); - ctx->result = Value::fromString(ctx, ToUTCString(t)); - } + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + return Value::fromString(ctx, ToUTCString(t)); } // @@ -2450,7 +2460,7 @@ RegExpCtor::RegExpCtor(ExecutionContext *scope) { } -void RegExpCtor::construct(ExecutionContext *ctx) +Value RegExpCtor::construct(ExecutionContext *ctx) { // if (ctx->argumentCount() > 2) { // ctx->throwTypeError(); @@ -2460,12 +2470,10 @@ void RegExpCtor::construct(ExecutionContext *ctx) Value r = ctx->argumentCount() > 0 ? ctx->argument(0) : Value::undefinedValue(); Value f = ctx->argumentCount() > 1 ? ctx->argument(1) : Value::undefinedValue(); if (RegExpObject *re = r.asRegExpObject()) { - if (!f.isUndefined()) { + if (!f.isUndefined()) ctx->throwTypeError(); - return; - } - ctx->result = Value::fromObject(new RegExpObject(re->value, false)); - return; + + return Value::fromObject(new RegExpObject(re->value, false)); } if (r.isUndefined()) @@ -2487,31 +2495,29 @@ void RegExpCtor::construct(ExecutionContext *ctx) options |= QRegularExpression::MultilineOption; } else { ctx->throwTypeError(); - return; } } } QRegularExpression re(r.stringValue()->toQString(), options); - if (!re.isValid()) { + if (!re.isValid()) ctx->throwTypeError(); - return; - } + ctx->thisObject = Value::fromObject(new RegExpObject(re, global)); + return ctx->thisObject; } -void RegExpCtor::call(ExecutionContext *ctx) +Value RegExpCtor::call(ExecutionContext *ctx) { if (ctx->argumentCount() > 0 && ctx->argument(0).asRegExpObject()) { - if (ctx->argumentCount() == 1 || ctx->argument(1).isUndefined()) { - ctx->result = ctx->argument(0); - return; - } + if (ctx->argumentCount() == 1 || ctx->argument(1).isUndefined()) + return ctx->argument(0); } + Value that = ctx->thisObject; - construct(ctx); - ctx->result = ctx->thisObject; + Value result = construct(ctx); ctx->thisObject = that; + return result; } void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) @@ -2523,64 +2529,60 @@ void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) __put__(ctx, QStringLiteral("toString"), method_toString, 0); } -void RegExpPrototype::method_exec(ExecutionContext *ctx) +Value RegExpPrototype::method_exec(ExecutionContext *ctx) { - if (RegExpObject *r = ctx->thisObject.asRegExpObject()) { - Value arg = ctx->argument(0); - arg = __qmljs_to_string(arg, ctx); - QString s = arg.stringValue()->toQString(); + RegExpObject *r = ctx->thisObject.asRegExpObject(); + if (!r) + ctx->throwTypeError(); - int offset = r->global ? r->lastIndex.toInt32(ctx) : 0; - if (offset < 0 || offset > s.length()) { - ctx->result = Value::nullValue(); - return; - } + Value arg = ctx->argument(0); + arg = __qmljs_to_string(arg, ctx); + QString s = arg.stringValue()->toQString(); - QRegularExpressionMatch match = r->value.match(s, offset); - if (!match.hasMatch()) { - ctx->result = Value::nullValue(); - return; - } + int offset = r->global ? r->lastIndex.toInt32(ctx) : 0; + if (offset < 0 || offset > s.length()) + return Value::nullValue(); - // fill in result data - ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); - int captured = match.lastCapturedIndex(); - for (int i = 0; i <= captured; ++i) - array->value.push(Value::fromString(ctx, match.captured(i))); + QRegularExpressionMatch match = r->value.match(s, offset); + if (!match.hasMatch()) + return Value::nullValue(); - array->__put__(ctx, QLatin1String("index"), Value::fromInt32(match.capturedStart(0))); - array->__put__(ctx, QLatin1String("input"), arg); + // fill in result data + ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); + int captured = match.lastCapturedIndex(); + for (int i = 0; i <= captured; ++i) + array->value.push(Value::fromString(ctx, match.captured(i))); - if (r->global) - r->lastIndex = Value::fromInt32(match.capturedEnd(0)); + array->__put__(ctx, QLatin1String("index"), Value::fromInt32(match.capturedStart(0))); + array->__put__(ctx, QLatin1String("input"), arg); - ctx->result = Value::fromObject(array); - } else { - ctx->throwTypeError(); - } + if (r->global) + r->lastIndex = Value::fromInt32(match.capturedEnd(0)); + + return Value::fromObject(array); } -void RegExpPrototype::method_test(ExecutionContext *ctx) +Value RegExpPrototype::method_test(ExecutionContext *ctx) { - method_exec(ctx); - ctx->result = Value::fromBoolean(!ctx->result.isNull()); + Value r = method_exec(ctx); + return Value::fromBoolean(!r.isNull()); } -void RegExpPrototype::method_toString(ExecutionContext *ctx) +Value RegExpPrototype::method_toString(ExecutionContext *ctx) { - if (RegExpObject *r = ctx->thisObject.asRegExpObject()) { - QString result = QChar('/') + r->value.pattern(); - result += QChar('/'); - QRegularExpression::PatternOptions o = r->value.patternOptions(); - // ### 'g' option missing - if (o & QRegularExpression::CaseInsensitiveOption) - result += QChar('i'); - if (o & QRegularExpression::MultilineOption) - result += QChar('m'); - ctx->result = Value::fromString(ctx, result); - } else { + RegExpObject *r = ctx->thisObject.asRegExpObject(); + if (!r) ctx->throwTypeError(); - } + + QString result = QChar('/') + r->value.pattern(); + result += QChar('/'); + QRegularExpression::PatternOptions o = r->value.patternOptions(); + // ### 'g' option missing + if (o & QRegularExpression::CaseInsensitiveOption) + result += QChar('i'); + if (o & QRegularExpression::MultilineOption) + result += QChar('m'); + return Value::fromString(ctx, result); } // @@ -2591,48 +2593,57 @@ ErrorCtor::ErrorCtor(ExecutionContext *scope) { } -void ErrorCtor::construct(ExecutionContext *ctx) +Value ErrorCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(new ErrorObject(ctx->argument(0))); + return ctx->thisObject; } -void ErrorCtor::call(ExecutionContext *ctx) +Value ErrorCtor::call(ExecutionContext *ctx) { Value that = ctx->thisObject; construct(ctx); ctx->wireUpPrototype(this); + Value res = ctx->thisObject; ctx->thisObject = that; + return res; } -void EvalErrorCtor::construct(ExecutionContext *ctx) +Value EvalErrorCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(new EvalErrorObject(ctx)); + return ctx->thisObject; } -void RangeErrorCtor::construct(ExecutionContext *ctx) +Value RangeErrorCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(new RangeErrorObject(ctx)); + return ctx->thisObject; } -void ReferenceErrorCtor::construct(ExecutionContext *ctx) +Value ReferenceErrorCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(new ReferenceErrorObject(ctx)); + return ctx->thisObject; } -void SyntaxErrorCtor::construct(ExecutionContext *ctx) +Value SyntaxErrorCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(new SyntaxErrorObject(ctx)); + return ctx->thisObject; } -void TypeErrorCtor::construct(ExecutionContext *ctx) +Value TypeErrorCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(new TypeErrorObject(ctx)); + return ctx->thisObject; } -void URIErrorCtor::construct(ExecutionContext *ctx) +Value URIErrorCtor::construct(ExecutionContext *ctx) { ctx->thisObject = Value::fromObject(new URIErrorObject(ctx)); + return ctx->thisObject; } void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) @@ -2642,7 +2653,7 @@ void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) obj->__put__(ctx, QStringLiteral("toString"), method_toString, 0); } -void ErrorPrototype::method_toString(ExecutionContext *ctx) +Value ErrorPrototype::method_toString(ExecutionContext *ctx) { Object *o = ctx->thisObject.asObject(); if (!o) @@ -2671,7 +2682,7 @@ void ErrorPrototype::method_toString(ExecutionContext *ctx) str = qname + QLatin1String(": ") + qmessage; } - ctx->result = Value::fromString(ctx, str); + return Value::fromString(ctx, str); } @@ -2721,106 +2732,103 @@ static double copySign(double x, double y) return x; } -void MathObject::method_abs(ExecutionContext *ctx) +Value MathObject::method_abs(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v == 0) // 0 | -0 - ctx->result = Value::fromDouble(0); - else - ctx->result = Value::fromDouble(v < 0 ? -v : v); + return Value::fromDouble(0); + + return Value::fromDouble(v < 0 ? -v : v); } -void MathObject::method_acos(ExecutionContext *ctx) +Value MathObject::method_acos(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v > 1) - ctx->result = Value::fromDouble(qSNaN()); - else - ctx->result = Value::fromDouble(::acos(v)); + return Value::fromDouble(qSNaN()); + + return Value::fromDouble(::acos(v)); } -void MathObject::method_asin(ExecutionContext *ctx) +Value MathObject::method_asin(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v > 1) - ctx->result = Value::fromDouble(qSNaN()); + return Value::fromDouble(qSNaN()); else - ctx->result = Value::fromDouble(::asin(v)); + return Value::fromDouble(::asin(v)); } -void MathObject::method_atan(ExecutionContext *ctx) +Value MathObject::method_atan(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v == 0.0) - ctx->result = Value::fromDouble(v); + return Value::fromDouble(v); else - ctx->result = Value::fromDouble(::atan(v)); + return Value::fromDouble(::atan(v)); } -void MathObject::method_atan2(ExecutionContext *ctx) +Value MathObject::method_atan2(ExecutionContext *ctx) { double v1 = ctx->argument(0).toNumber(ctx); double v2 = ctx->argument(1).toNumber(ctx); if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) { - ctx->result = Value::fromDouble(copySign(0, -1.0)); - return; + return Value::fromDouble(copySign(0, -1.0)); } if ((v1 == 0.0) && (v2 == 0.0)) { if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) { - ctx->result = Value::fromDouble(qt_PI); - return; + return Value::fromDouble(qt_PI); } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) { - ctx->result = Value::fromDouble(-qt_PI); - return; + return Value::fromDouble(-qt_PI); } } - ctx->result = Value::fromDouble(::atan2(v1, v2)); + return Value::fromDouble(::atan2(v1, v2)); } -void MathObject::method_ceil(ExecutionContext *ctx) +Value MathObject::method_ceil(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v < 0.0 && v > -1.0) - ctx->result = Value::fromDouble(copySign(0, -1.0)); + return Value::fromDouble(copySign(0, -1.0)); else - ctx->result = Value::fromDouble(::ceil(v)); + return Value::fromDouble(::ceil(v)); } -void MathObject::method_cos(ExecutionContext *ctx) +Value MathObject::method_cos(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); - ctx->result = Value::fromDouble(::cos(v)); + return Value::fromDouble(::cos(v)); } -void MathObject::method_exp(ExecutionContext *ctx) +Value MathObject::method_exp(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (qIsInf(v)) { if (copySign(1.0, v) == -1.0) - ctx->result = Value::fromDouble(0); + return Value::fromDouble(0); else - ctx->result = Value::fromDouble(qInf()); + return Value::fromDouble(qInf()); } else { - ctx->result = Value::fromDouble(::exp(v)); + return Value::fromDouble(::exp(v)); } } -void MathObject::method_floor(ExecutionContext *ctx) +Value MathObject::method_floor(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); - ctx->result = Value::fromDouble(::floor(v)); + return Value::fromDouble(::floor(v)); } -void MathObject::method_log(ExecutionContext *ctx) +Value MathObject::method_log(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v < 0) - ctx->result = Value::fromDouble(qSNaN()); + return Value::fromDouble(qSNaN()); else - ctx->result = Value::fromDouble(::log(v)); + return Value::fromDouble(::log(v)); } -void MathObject::method_max(ExecutionContext *ctx) +Value MathObject::method_max(ExecutionContext *ctx) { double mx = -qInf(); for (unsigned i = 0; i < ctx->argumentCount(); ++i) { @@ -2828,10 +2836,10 @@ void MathObject::method_max(ExecutionContext *ctx) if (x > mx || qIsNaN(x)) mx = x; } - ctx->result = Value::fromDouble(mx); + return Value::fromDouble(mx); } -void MathObject::method_min(ExecutionContext *ctx) +Value MathObject::method_min(ExecutionContext *ctx) { double mx = qInf(); for (unsigned i = 0; i < ctx->argumentCount(); ++i) { @@ -2841,36 +2849,34 @@ void MathObject::method_min(ExecutionContext *ctx) mx = x; } } - ctx->result = Value::fromDouble(mx); + return Value::fromDouble(mx); } -void MathObject::method_pow(ExecutionContext *ctx) +Value MathObject::method_pow(ExecutionContext *ctx) { double x = ctx->argument(0).toNumber(ctx); double y = ctx->argument(1).toNumber(ctx); - if (qIsNaN(y)) { - ctx->result = Value::fromDouble(qSNaN()); - return; - } + if (qIsNaN(y)) + return Value::fromDouble(qSNaN()); if (y == 0) { - ctx->result = Value::fromDouble(1); + return Value::fromDouble(1); } else if (((x == 1) || (x == -1)) && qIsInf(y)) { - ctx->result = Value::fromDouble(qSNaN()); + return Value::fromDouble(qSNaN()); } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { - ctx->result = Value::fromDouble(qInf()); + return Value::fromDouble(qInf()); } else if ((x == 0) && copySign(1.0, x) == -1.0) { if (y < 0) { if (::fmod(-y, 2.0) == 1.0) - ctx->result = Value::fromDouble(-qInf()); + return Value::fromDouble(-qInf()); else - ctx->result = Value::fromDouble(qInf()); + return Value::fromDouble(qInf()); } else if (y > 0) { if (::fmod(y, 2.0) == 1.0) - ctx->result = Value::fromDouble(copySign(0, -1.0)); + return Value::fromDouble(copySign(0, -1.0)); else - ctx->result = Value::fromDouble(0); + return Value::fromDouble(0); } } @@ -2878,52 +2884,54 @@ void MathObject::method_pow(ExecutionContext *ctx) else if (qIsInf(x) && copySign(1.0, x) == -1.0) { if (y > 0) { if (::fmod(y, 2.0) == 1.0) - ctx->result = Value::number(ctx, -qInf()); + return Value::number(ctx, -qInf()); else - ctx->result = Value::number(ctx, qInf()); + return Value::number(ctx, qInf()); } else if (y < 0) { if (::fmod(-y, 2.0) == 1.0) - ctx->result = Value::number(ctx, copySign(0, -1.0)); + return Value::number(ctx, copySign(0, -1.0)); else - ctx->result = Value::number(ctx, 0); + return Value::number(ctx, 0); } } #endif else { - ctx->result = Value::fromDouble(::pow(x, y)); + return Value::fromDouble(::pow(x, y)); } + // ### + return Value::fromDouble(qSNaN()); } -void MathObject::method_random(ExecutionContext *ctx) +Value MathObject::method_random(ExecutionContext */*ctx*/) { - ctx->result = Value::fromDouble(qrand() / (double) RAND_MAX); + return Value::fromDouble(qrand() / (double) RAND_MAX); } -void MathObject::method_round(ExecutionContext *ctx) +Value MathObject::method_round(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); v = copySign(::floor(v + 0.5), v); - ctx->result = Value::fromDouble(v); + return Value::fromDouble(v); } -void MathObject::method_sin(ExecutionContext *ctx) +Value MathObject::method_sin(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); - ctx->result = Value::fromDouble(::sin(v)); + return Value::fromDouble(::sin(v)); } -void MathObject::method_sqrt(ExecutionContext *ctx) +Value MathObject::method_sqrt(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); - ctx->result = Value::fromDouble(::sqrt(v)); + return Value::fromDouble(::sqrt(v)); } -void MathObject::method_tan(ExecutionContext *ctx) +Value MathObject::method_tan(ExecutionContext *ctx) { double v = ctx->argument(0).toNumber(ctx); if (v == 0.0) - ctx->result = Value::fromDouble(v); + return Value::fromDouble(v); else - ctx->result = Value::fromDouble(::tan(v)); + return Value::fromDouble(::tan(v)); } diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 6150cefc1a..d94c979413 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -51,8 +51,8 @@ struct ObjectCtor: FunctionObject { ObjectCtor(ExecutionContext *scope); - virtual void construct(ExecutionContext *ctx); - virtual void call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); virtual Value __get__(ExecutionContext *ctx, String *name); }; @@ -60,34 +60,34 @@ struct ObjectPrototype: Object { void init(ExecutionContext *ctx, const Value &ctor); - static void method_getPrototypeOf(ExecutionContext *ctx); - static void method_getOwnPropertyDescriptor(ExecutionContext *ctx); - static void method_getOwnPropertyNames(ExecutionContext *ctx); - static void method_create(ExecutionContext *ctx); - static void method_defineProperty(ExecutionContext *ctx); - static void method_defineProperties(ExecutionContext *ctx); - static void method_seal(ExecutionContext *ctx); - static void method_freeze(ExecutionContext *ctx); - static void method_preventExtensions(ExecutionContext *ctx); - static void method_isSealed(ExecutionContext *ctx); - static void method_isFrozen(ExecutionContext *ctx); - static void method_isExtensible(ExecutionContext *ctx); - static void method_keys(ExecutionContext *ctx); - - static void method_toString(ExecutionContext *ctx); - static void method_toLocaleString(ExecutionContext *ctx); - static void method_valueOf(ExecutionContext *ctx); - static void method_hasOwnProperty(ExecutionContext *ctx); - static void method_isPrototypeOf(ExecutionContext *ctx); - static void method_propertyIsEnumerable(ExecutionContext *ctx); + static Value method_getPrototypeOf(ExecutionContext *ctx); + static Value method_getOwnPropertyDescriptor(ExecutionContext *ctx); + static Value method_getOwnPropertyNames(ExecutionContext *ctx); + static Value method_create(ExecutionContext *ctx); + static Value method_defineProperty(ExecutionContext *ctx); + static Value method_defineProperties(ExecutionContext *ctx); + static Value method_seal(ExecutionContext *ctx); + static Value method_freeze(ExecutionContext *ctx); + static Value method_preventExtensions(ExecutionContext *ctx); + static Value method_isSealed(ExecutionContext *ctx); + static Value method_isFrozen(ExecutionContext *ctx); + static Value method_isExtensible(ExecutionContext *ctx); + static Value method_keys(ExecutionContext *ctx); + + static Value method_toString(ExecutionContext *ctx); + static Value method_toLocaleString(ExecutionContext *ctx); + static Value method_valueOf(ExecutionContext *ctx); + static Value method_hasOwnProperty(ExecutionContext *ctx); + static Value method_isPrototypeOf(ExecutionContext *ctx); + static Value method_propertyIsEnumerable(ExecutionContext *ctx); }; struct StringCtor: FunctionObject { StringCtor(ExecutionContext *scope); - virtual void construct(ExecutionContext *ctx); - virtual void call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); }; struct StringPrototype: StringObject @@ -97,34 +97,34 @@ struct StringPrototype: StringObject static QString getThisString(ExecutionContext *ctx); - static void method_toString(ExecutionContext *ctx); - static void method_valueOf(ExecutionContext *ctx); - static void method_charAt(ExecutionContext *ctx); - static void method_charCodeAt(ExecutionContext *ctx); - static void method_concat(ExecutionContext *ctx); - static void method_indexOf(ExecutionContext *ctx); - static void method_lastIndexOf(ExecutionContext *ctx); - static void method_localeCompare(ExecutionContext *ctx); - static void method_match(ExecutionContext *ctx); - static void method_replace(ExecutionContext *ctx); - static void method_search(ExecutionContext *ctx); - static void method_slice(ExecutionContext *ctx); - static void method_split(ExecutionContext *ctx); - static void method_substr(ExecutionContext *ctx); - static void method_substring(ExecutionContext *ctx); - static void method_toLowerCase(ExecutionContext *ctx); - static void method_toLocaleLowerCase(ExecutionContext *ctx); - static void method_toUpperCase(ExecutionContext *ctx); - static void method_toLocaleUpperCase(ExecutionContext *ctx); - static void method_fromCharCode(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *ctx); + static Value method_valueOf(ExecutionContext *ctx); + static Value method_charAt(ExecutionContext *ctx); + static Value method_charCodeAt(ExecutionContext *ctx); + static Value method_concat(ExecutionContext *ctx); + static Value method_indexOf(ExecutionContext *ctx); + static Value method_lastIndexOf(ExecutionContext *ctx); + static Value method_localeCompare(ExecutionContext *ctx); + static Value method_match(ExecutionContext *ctx); + static Value method_replace(ExecutionContext *ctx); + static Value method_search(ExecutionContext *ctx); + static Value method_slice(ExecutionContext *ctx); + static Value method_split(ExecutionContext *ctx); + static Value method_substr(ExecutionContext *ctx); + static Value method_substring(ExecutionContext *ctx); + static Value method_toLowerCase(ExecutionContext *ctx); + static Value method_toLocaleLowerCase(ExecutionContext *ctx); + static Value method_toUpperCase(ExecutionContext *ctx); + static Value method_toLocaleUpperCase(ExecutionContext *ctx); + static Value method_fromCharCode(ExecutionContext *ctx); }; struct NumberCtor: FunctionObject { NumberCtor(ExecutionContext *scope); - virtual void construct(ExecutionContext *ctx); - virtual void call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); }; struct NumberPrototype: NumberObject @@ -132,20 +132,20 @@ struct NumberPrototype: NumberObject NumberPrototype(): NumberObject(Value::fromDouble(0)) {} void init(ExecutionContext *ctx, const Value &ctor); - static void method_toString(ExecutionContext *ctx); - static void method_toLocaleString(ExecutionContext *ctx); - static void method_valueOf(ExecutionContext *ctx); - static void method_toFixed(ExecutionContext *ctx); - static void method_toExponential(ExecutionContext *ctx); - static void method_toPrecision(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *ctx); + static Value method_toLocaleString(ExecutionContext *ctx); + static Value method_valueOf(ExecutionContext *ctx); + static Value method_toFixed(ExecutionContext *ctx); + static Value method_toExponential(ExecutionContext *ctx); + static Value method_toPrecision(ExecutionContext *ctx); }; struct BooleanCtor: FunctionObject { BooleanCtor(ExecutionContext *scope); - virtual void construct(ExecutionContext *ctx); - virtual void call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); }; struct BooleanPrototype: BooleanObject @@ -153,51 +153,51 @@ struct BooleanPrototype: BooleanObject BooleanPrototype(): BooleanObject(Value::fromBoolean(false)) {} void init(ExecutionContext *ctx, const Value &ctor); - static void method_toString(ExecutionContext *ctx); - static void method_valueOf(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *ctx); + static Value method_valueOf(ExecutionContext *ctx); }; struct ArrayCtor: FunctionObject { ArrayCtor(ExecutionContext *scope); - virtual void construct(ExecutionContext *ctx); - virtual void call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); }; struct ArrayPrototype: ArrayObject { void init(ExecutionContext *ctx, const Value &ctor); - static void method_toString(ExecutionContext *ctx); - static void method_toLocaleString(ExecutionContext *ctx); - static void method_concat(ExecutionContext *ctx); - static void method_join(ExecutionContext *ctx); - static void method_pop(ExecutionContext *ctx); - static void method_push(ExecutionContext *ctx); - static void method_reverse(ExecutionContext *ctx); - static void method_shift(ExecutionContext *ctx); - static void method_slice(ExecutionContext *ctx); - static void method_sort(ExecutionContext *ctx); - static void method_splice(ExecutionContext *ctx); - static void method_unshift(ExecutionContext *ctx); - static void method_indexOf(ExecutionContext *ctx); - static void method_lastIndexOf(ExecutionContext *ctx); - static void method_every(ExecutionContext *ctx); - static void method_some(ExecutionContext *ctx); - static void method_forEach(ExecutionContext *ctx); - static void method_map(ExecutionContext *ctx); - static void method_filter(ExecutionContext *ctx); - static void method_reduce(ExecutionContext *ctx); - static void method_reduceRight(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *ctx); + static Value method_toLocaleString(ExecutionContext *ctx); + static Value method_concat(ExecutionContext *ctx); + static Value method_join(ExecutionContext *ctx); + static Value method_pop(ExecutionContext *ctx); + static Value method_push(ExecutionContext *ctx); + static Value method_reverse(ExecutionContext *ctx); + static Value method_shift(ExecutionContext *ctx); + static Value method_slice(ExecutionContext *ctx); + static Value method_sort(ExecutionContext *ctx); + static Value method_splice(ExecutionContext *ctx); + static Value method_unshift(ExecutionContext *ctx); + static Value method_indexOf(ExecutionContext *ctx); + static Value method_lastIndexOf(ExecutionContext *ctx); + static Value method_every(ExecutionContext *ctx); + static Value method_some(ExecutionContext *ctx); + static Value method_forEach(ExecutionContext *ctx); + static Value method_map(ExecutionContext *ctx); + static Value method_filter(ExecutionContext *ctx); + static Value method_reduce(ExecutionContext *ctx); + static Value method_reduceRight(ExecutionContext *ctx); }; struct FunctionCtor: FunctionObject { FunctionCtor(ExecutionContext *scope); - virtual void construct(ExecutionContext *ctx); - virtual void call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); }; struct FunctionPrototype: FunctionObject @@ -205,18 +205,18 @@ struct FunctionPrototype: FunctionObject FunctionPrototype(ExecutionContext *ctx): FunctionObject(ctx) {} void init(ExecutionContext *ctx, const Value &ctor); - static void method_toString(ExecutionContext *ctx); - static void method_apply(ExecutionContext *ctx); - static void method_call(ExecutionContext *ctx); - static void method_bind(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *ctx); + static Value method_apply(ExecutionContext *ctx); + static Value method_call(ExecutionContext *ctx); + static Value method_bind(ExecutionContext *ctx); }; struct DateCtor: FunctionObject { DateCtor(ExecutionContext *scope); - virtual void construct(ExecutionContext *ctx); - virtual void call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); }; struct DatePrototype: DateObject @@ -226,62 +226,62 @@ struct DatePrototype: DateObject static double getThisDate(ExecutionContext *ctx); - static void method_MakeTime(ExecutionContext *ctx); - static void method_MakeDate(ExecutionContext *ctx); - static void method_TimeClip(ExecutionContext *ctx); - static void method_parse(ExecutionContext *ctx); - static void method_UTC(ExecutionContext *ctx); - static void method_toString(ExecutionContext *ctx); - static void method_toDateString(ExecutionContext *ctx); - static void method_toTimeString(ExecutionContext *ctx); - static void method_toLocaleString(ExecutionContext *ctx); - static void method_toLocaleDateString(ExecutionContext *ctx); - static void method_toLocaleTimeString(ExecutionContext *ctx); - static void method_valueOf(ExecutionContext *ctx); - static void method_getTime(ExecutionContext *ctx); - static void method_getYear(ExecutionContext *ctx); - static void method_getFullYear(ExecutionContext *ctx); - static void method_getUTCFullYear(ExecutionContext *ctx); - static void method_getMonth(ExecutionContext *ctx); - static void method_getUTCMonth(ExecutionContext *ctx); - static void method_getDate(ExecutionContext *ctx); - static void method_getUTCDate(ExecutionContext *ctx); - static void method_getDay(ExecutionContext *ctx); - static void method_getUTCDay(ExecutionContext *ctx); - static void method_getHours(ExecutionContext *ctx); - static void method_getUTCHours(ExecutionContext *ctx); - static void method_getMinutes(ExecutionContext *ctx); - static void method_getUTCMinutes(ExecutionContext *ctx); - static void method_getSeconds(ExecutionContext *ctx); - static void method_getUTCSeconds(ExecutionContext *ctx); - static void method_getMilliseconds(ExecutionContext *ctx); - static void method_getUTCMilliseconds(ExecutionContext *ctx); - static void method_getTimezoneOffset(ExecutionContext *ctx); - static void method_setTime(ExecutionContext *ctx); - static void method_setMilliseconds(ExecutionContext *ctx); - static void method_setUTCMilliseconds(ExecutionContext *ctx); - static void method_setSeconds(ExecutionContext *ctx); - static void method_setUTCSeconds(ExecutionContext *ctx); - static void method_setMinutes(ExecutionContext *ctx); - static void method_setUTCMinutes(ExecutionContext *ctx); - static void method_setHours(ExecutionContext *ctx); - static void method_setUTCHours(ExecutionContext *ctx); - static void method_setDate(ExecutionContext *ctx); - static void method_setUTCDate(ExecutionContext *ctx); - static void method_setMonth(ExecutionContext *ctx); - static void method_setUTCMonth(ExecutionContext *ctx); - static void method_setYear(ExecutionContext *ctx); - static void method_setFullYear(ExecutionContext *ctx); - static void method_setUTCFullYear(ExecutionContext *ctx); - static void method_toUTCString(ExecutionContext *ctx); + static Value method_MakeTime(ExecutionContext *ctx); + static Value method_MakeDate(ExecutionContext *ctx); + static Value method_TimeClip(ExecutionContext *ctx); + static Value method_parse(ExecutionContext *ctx); + static Value method_UTC(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *ctx); + static Value method_toDateString(ExecutionContext *ctx); + static Value method_toTimeString(ExecutionContext *ctx); + static Value method_toLocaleString(ExecutionContext *ctx); + static Value method_toLocaleDateString(ExecutionContext *ctx); + static Value method_toLocaleTimeString(ExecutionContext *ctx); + static Value method_valueOf(ExecutionContext *ctx); + static Value method_getTime(ExecutionContext *ctx); + static Value method_getYear(ExecutionContext *ctx); + static Value method_getFullYear(ExecutionContext *ctx); + static Value method_getUTCFullYear(ExecutionContext *ctx); + static Value method_getMonth(ExecutionContext *ctx); + static Value method_getUTCMonth(ExecutionContext *ctx); + static Value method_getDate(ExecutionContext *ctx); + static Value method_getUTCDate(ExecutionContext *ctx); + static Value method_getDay(ExecutionContext *ctx); + static Value method_getUTCDay(ExecutionContext *ctx); + static Value method_getHours(ExecutionContext *ctx); + static Value method_getUTCHours(ExecutionContext *ctx); + static Value method_getMinutes(ExecutionContext *ctx); + static Value method_getUTCMinutes(ExecutionContext *ctx); + static Value method_getSeconds(ExecutionContext *ctx); + static Value method_getUTCSeconds(ExecutionContext *ctx); + static Value method_getMilliseconds(ExecutionContext *ctx); + static Value method_getUTCMilliseconds(ExecutionContext *ctx); + static Value method_getTimezoneOffset(ExecutionContext *ctx); + static Value method_setTime(ExecutionContext *ctx); + static Value method_setMilliseconds(ExecutionContext *ctx); + static Value method_setUTCMilliseconds(ExecutionContext *ctx); + static Value method_setSeconds(ExecutionContext *ctx); + static Value method_setUTCSeconds(ExecutionContext *ctx); + static Value method_setMinutes(ExecutionContext *ctx); + static Value method_setUTCMinutes(ExecutionContext *ctx); + static Value method_setHours(ExecutionContext *ctx); + static Value method_setUTCHours(ExecutionContext *ctx); + static Value method_setDate(ExecutionContext *ctx); + static Value method_setUTCDate(ExecutionContext *ctx); + static Value method_setMonth(ExecutionContext *ctx); + static Value method_setUTCMonth(ExecutionContext *ctx); + static Value method_setYear(ExecutionContext *ctx); + static Value method_setFullYear(ExecutionContext *ctx); + static Value method_setUTCFullYear(ExecutionContext *ctx); + static Value method_toUTCString(ExecutionContext *ctx); }; struct RegExpCtor: FunctionObject { RegExpCtor(ExecutionContext *scope); - virtual void construct(ExecutionContext *ctx); - virtual void call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); }; struct RegExpPrototype: RegExpObject @@ -289,59 +289,59 @@ struct RegExpPrototype: RegExpObject RegExpPrototype(): RegExpObject(QRegularExpression(), false) {} void init(ExecutionContext *ctx, const Value &ctor); - static void method_exec(ExecutionContext *ctx); - static void method_test(ExecutionContext *ctx); - static void method_toString(ExecutionContext *ctx); + static Value method_exec(ExecutionContext *ctx); + static Value method_test(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *ctx); }; struct ErrorCtor: FunctionObject { ErrorCtor(ExecutionContext *scope); - virtual void construct(ExecutionContext *ctx); - virtual void call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); }; struct EvalErrorCtor: ErrorCtor { EvalErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual void construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); }; struct RangeErrorCtor: ErrorCtor { RangeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual void construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); }; struct ReferenceErrorCtor: ErrorCtor { ReferenceErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual void construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); }; struct SyntaxErrorCtor: ErrorCtor { SyntaxErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual void construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); }; struct TypeErrorCtor: ErrorCtor { TypeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual void construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); }; struct URIErrorCtor: ErrorCtor { URIErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual void construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); }; @@ -352,7 +352,7 @@ struct ErrorPrototype: ErrorObject void init(ExecutionContext *ctx, const Value &ctor) { init(ctx, ctor, this); } static void init(ExecutionContext *ctx, const Value &ctor, Object *obj); - static void method_toString(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *ctx); }; struct EvalErrorPrototype: EvalErrorObject @@ -396,24 +396,24 @@ struct MathObject: Object { MathObject(ExecutionContext *ctx); - static void method_abs(ExecutionContext *ctx); - static void method_acos(ExecutionContext *ctx); - static void method_asin(ExecutionContext *ctx); - static void method_atan(ExecutionContext *ctx); - static void method_atan2(ExecutionContext *ctx); - static void method_ceil(ExecutionContext *ctx); - static void method_cos(ExecutionContext *ctx); - static void method_exp(ExecutionContext *ctx); - static void method_floor(ExecutionContext *ctx); - static void method_log(ExecutionContext *ctx); - static void method_max(ExecutionContext *ctx); - static void method_min(ExecutionContext *ctx); - static void method_pow(ExecutionContext *ctx); - static void method_random(ExecutionContext *ctx); - static void method_round(ExecutionContext *ctx); - static void method_sin(ExecutionContext *ctx); - static void method_sqrt(ExecutionContext *ctx); - static void method_tan(ExecutionContext *ctx); + static Value method_abs(ExecutionContext *ctx); + static Value method_acos(ExecutionContext *ctx); + static Value method_asin(ExecutionContext *ctx); + static Value method_atan(ExecutionContext *ctx); + static Value method_atan2(ExecutionContext *ctx); + static Value method_ceil(ExecutionContext *ctx); + static Value method_cos(ExecutionContext *ctx); + static Value method_exp(ExecutionContext *ctx); + static Value method_floor(ExecutionContext *ctx); + static Value method_log(ExecutionContext *ctx); + static Value method_max(ExecutionContext *ctx); + static Value method_min(ExecutionContext *ctx); + static Value method_pow(ExecutionContext *ctx); + static Value method_random(ExecutionContext *ctx); + static Value method_round(ExecutionContext *ctx); + static Value method_sin(ExecutionContext *ctx); + static Value method_sqrt(ExecutionContext *ctx); + static Value method_tan(ExecutionContext *ctx); }; } // end of namespace VM diff --git a/qv4ir_p.h b/qv4ir_p.h index 38f690b282..c73bd64426 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -596,7 +596,7 @@ struct Function { QList locals; IR::BasicBlock *handlersBlock; - void (*code)(VM::ExecutionContext *, const uchar *); + VM::Value (*code)(VM::ExecutionContext *, const uchar *); const uchar *codeData; JSC::MacroAssemblerCodeRef codeRef; diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 6290dd5990..6f95671c54 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -165,7 +165,7 @@ void InstructionSelection::operator()(IR::Function *function) _function->codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); } - _function->code = (void (*)(VM::ExecutionContext *, const uchar *)) _function->codeRef.code().executableAddress(); + _function->code = (Value (*)(VM::ExecutionContext *, const uchar *)) _function->codeRef.code().executableAddress(); qSwap(_function, function); } @@ -722,7 +722,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { if (IR::Temp *t = s->expr->asTemp()) { - copyValue(Pointer(ContextRegister, offsetof(ExecutionContext, result)), t); + copyValue(ReturnValueRegister, t); return; } Q_UNIMPLEMENTED(); -- cgit v1.2.3 From cd287e3c5db436cb354fe1682d44c00a1e1ac109 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 19 Nov 2012 16:14:37 +0100 Subject: Make masm work again Masm can't currently deal correctly with a single instance of the isel object. Otherwise we'll get completely wrong pointers to the different methods set up. Change-Id: Ic3ee06f2f83e5491a6b9ee422ae39611b89d1460 Reviewed-by: Erik Verbruggen --- qmljs_objects.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 5ad7642395..c920c124d0 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -542,15 +542,14 @@ Value EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &f Codegen cg; globalCode = cg(program, &module, mode); - EvalInstructionSelection *isel = factory->create(vm, &module, code); - foreach (IR::Function *function, module.functions) + foreach (IR::Function *function, module.functions) { + EvalInstructionSelection *isel = factory->create(vm, &module, code); isel->run(function); - - if (! isel->finishModule(codeSize)) - Q_UNREACHABLE(); - - delete isel; + if (! isel->finishModule(codeSize)) + Q_UNREACHABLE(); + delete isel; + } } if (! globalCode) -- cgit v1.2.3 From be507044a9f0eefb247fb9e385808d5d39b5dc38 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 19 Nov 2012 21:32:15 +0100 Subject: Remove the unused handlersBlock in the IR::Function This block was used for the old exception handling mechanism that is not in use anymore. Change-Id: If5adf89e43a22ab167378827c0e97f9907d8a366 Reviewed-by: Erik Verbruggen --- qv4codegen.cpp | 6 ------ qv4ir_p.h | 2 -- 2 files changed, 8 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index ac5b660e34..7a367ca9a7 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1346,7 +1346,6 @@ void Codegen::linearize(IR::Function *function) assert(exitBlock->terminator()->asRet()); QSet V; - V.insert(function->handlersBlock); V.insert(exitBlock); QVector trace; @@ -1388,9 +1387,6 @@ void Codegen::linearize(IR::Function *function) I::trace(function->basicBlocks.first(), &V, &trace); - function->handlersBlock->index = trace.size(); - trace.append(function->handlersBlock); - exitBlock->index = trace.size(); trace.append(exitBlock); @@ -1511,11 +1507,9 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, IR::BasicBlock *entryBlock = function->newBasicBlock(); IR::BasicBlock *exitBlock = function->newBasicBlock(IR::Function::DontInsertBlock); IR::BasicBlock *throwBlock = function->newBasicBlock(); - IR::BasicBlock *handlersBlock = function->newBasicBlock(); function->hasDirectEval = _env->hasDirectEval; function->hasNestedFunctions = _env->hasNestedFunctions; function->maxNumberOfArguments = _env->maxNumberOfArguments; - function->handlersBlock = handlersBlock; for (int i = 0; i < _env->vars.size(); ++i) { unsigned t = entryBlock->newTemp(); diff --git a/qv4ir_p.h b/qv4ir_p.h index c73bd64426..502275d4b9 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -594,7 +594,6 @@ struct Function { QSet strings; QList formals; QList locals; - IR::BasicBlock *handlersBlock; VM::Value (*code)(VM::ExecutionContext *, const uchar *); const uchar *codeData; @@ -610,7 +609,6 @@ struct Function { , pool(&module->pool) , tempCount(0) , maxNumberOfArguments(0) - , handlersBlock(0) , code(0) , codeData(0) , hasDirectEval(false) -- cgit v1.2.3 From 590b9491836fc5357393c5b89c2a8f82f5ec24b6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 19 Nov 2012 22:24:12 +0100 Subject: Move the exception variable into the engine It's easier to store this in the engine then in the execution context. Change-Id: I01ff447602a7d785165e774bdf6e1735c073be2d Reviewed-by: Erik Verbruggen --- main.cpp | 4 ++-- qmljs_engine.h | 1 + qmljs_environment.cpp | 3 +-- qmljs_environment.h | 1 - qmljs_runtime.cpp | 5 +++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/main.cpp b/main.cpp index 7467568d0b..c9e31701a7 100644 --- a/main.cpp +++ b/main.cpp @@ -309,10 +309,10 @@ int main(int argc, char *argv[]) void * buf = __qmljs_create_exception_handler(ctx); if (setjmp(*(jmp_buf *)buf)) { - if (QQmlJS::VM::ErrorObject *e = ctx->res.asErrorObject()) + if (QQmlJS::VM::ErrorObject *e = ctx->engine->exception.asErrorObject()) std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; else - std::cerr << "Uncaught exception: " << qPrintable(ctx->res.toString(ctx)->toQString()) << std::endl; + std::cerr << "Uncaught exception: " << qPrintable(ctx->engine->exception.toString(ctx)->toQString()) << std::endl; return EXIT_FAILURE; } diff --git a/qmljs_engine.h b/qmljs_engine.h index d22cd85c50..2404ebd7f8 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -136,6 +136,7 @@ struct ExecutionEngine }; QVector unwindStack; + Value exception; ExecutionEngine(EValISelFactory *factory); diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 87f8543722..121670c999 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -171,7 +171,7 @@ void ExecutionContext::init(ExecutionEngine *eng) variableEnvironment = new DeclarativeEnvironment(eng); lexicalEnvironment = variableEnvironment; thisObject = Value::nullValue(); - res = Value::undefinedValue(); + eng->exception = Value::undefinedValue(); } PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp) @@ -235,7 +235,6 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha lexicalEnvironment = variableEnvironment; thisObject = that; - res = Value::undefinedValue(); } void ExecutionContext::leaveCallContext() diff --git a/qmljs_environment.h b/qmljs_environment.h index 8fe14b2844..b16c0f9325 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -90,7 +90,6 @@ struct ExecutionContext DeclarativeEnvironment *lexicalEnvironment; DeclarativeEnvironment *variableEnvironment; Value thisObject; - Value res; void init(ExecutionEngine *eng); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index a81a6a9fb7..20ea18467c 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -844,13 +844,14 @@ void __qmljs_throw(Value value, ExecutionContext *context) context = context->parent; } - handler.context->res = value; + context->engine->exception = value; longjmp(handler.stackFrame, 1); } void *__qmljs_create_exception_handler(ExecutionContext *context) { + context->engine->exception = Value::undefinedValue(); context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler()); ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); handler.context = context; @@ -866,7 +867,7 @@ void __qmljs_delete_exception_handler(ExecutionContext *context) Value __qmljs_get_exception(ExecutionContext *context) { - return context->res; + return context->engine->exception; } Value __qmljs_builtin_typeof(Value val, ExecutionContext *context) -- cgit v1.2.3 From e625c28b3957c680baea7127c079f9497fa11f0f Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 20 Nov 2012 10:08:14 +0100 Subject: Fix debug code and add some asserts. Change-Id: I947a72ff43190d15fd4a2857ed97b75cc510db66 Reviewed-by: Lars Knoll --- moth/qv4vme_moth.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index a4033ab6d5..080d3953bc 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -56,12 +56,12 @@ static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVecto if (index < 0) { kind = "arg"; pos = -index - 1; - } else if (index < (int) context->varCount) { + } else if (index < (int) context->variableEnvironment->varCount) { kind = "local"; pos = index; } else { kind = "temp"; - pos = index - context->varCount; + pos = index - context->variableEnvironment->varCount; } fprintf(stderr, " tempValue: index = %d : %s = %d, stack size = %d\n", index, kind, pos, stack.size()); @@ -69,11 +69,23 @@ static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVecto if (index < 0) { const int arg = -index - 1; + + Q_ASSERT(arg >= 0); + Q_ASSERT((unsigned) arg < context->variableEnvironment->argumentCount); + Q_ASSERT(context->variableEnvironment->arguments); + return context->variableEnvironment->arguments + arg; } else if (index < (int) context->variableEnvironment->varCount) { + Q_ASSERT(index >= 0); + Q_ASSERT(context->variableEnvironment->locals); + return context->variableEnvironment->locals + index; } else { int off = index - context->variableEnvironment->varCount; + + Q_ASSERT(off >= 0); + Q_ASSERT(off < stack.size()); + return stack.data() + off; } } -- cgit v1.2.3 From 3780a47c4654d228933eeecef036d1b7963e256a Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 20 Nov 2012 09:47:18 +0100 Subject: Make LLVM backend compile again. Moved common code into a separate function. Change-Id: Iaa96c27214659a23b3df70b80560fb8f42792b38 Reviewed-by: Lars Knoll --- main.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/main.cpp b/main.cpp index c9e31701a7..4753279d2b 100644 --- a/main.cpp +++ b/main.cpp @@ -106,6 +106,14 @@ struct TestHarnessError: FunctionObject } // builtins +static void showException(QQmlJS::VM::ExecutionContext *ctx) +{ + if (QQmlJS::VM::ErrorObject *e = ctx->engine->exception.asErrorObject()) + std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; + else + std::cerr << "Uncaught exception: " << qPrintable(ctx->engine->exception.toString(ctx)->toQString()) << std::endl; +} + #ifndef QMLJS_NO_LLVM int executeLLVMCode(void *codePtr) { @@ -125,10 +133,7 @@ int executeLLVMCode(void *codePtr) void * buf = __qmljs_create_exception_handler(ctx); if (setjmp(*(jmp_buf *)buf)) { - if (VM::ErrorObject *e = ctx->result.asErrorObject()) - std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; - else - std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl; + showException(ctx); return EXIT_FAILURE; } @@ -309,10 +314,7 @@ int main(int argc, char *argv[]) void * buf = __qmljs_create_exception_handler(ctx); if (setjmp(*(jmp_buf *)buf)) { - if (QQmlJS::VM::ErrorObject *e = ctx->engine->exception.asErrorObject()) - std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; - else - std::cerr << "Uncaught exception: " << qPrintable(ctx->engine->exception.toString(ctx)->toQString()) << std::endl; + showException(ctx); return EXIT_FAILURE; } -- cgit v1.2.3 From 72cda7ed8c06103d0a66a6441ddf1ab52d3010f1 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 20 Nov 2012 09:48:11 +0100 Subject: Actually return the return value from the interpreter. Change-Id: I38cb4bc431f1bab796f08fc217747e06070a2c78 Reviewed-by: Lars Knoll --- moth/qv4vme_moth.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 080d3953bc..48a02f6032 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -360,8 +360,7 @@ void **VME::instructionJumpTable() VM::Value VME::exec(VM::ExecutionContext *ctxt, const uchar *code) { VME vme; - vme(ctxt, code); - return VM::Value::undefinedValue(); + return vme(ctxt, code); } void VME::restoreState(VM::ExecutionContext *context, int &targetTempIndex, const uchar *&code) -- cgit v1.2.3 From 43cd73b7a03cfb0d1de1f2ab9f579b4fd5842722 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 20 Nov 2012 09:35:32 +0100 Subject: Compress temp usage in the interpreter. Uses a variation on linear scan register allocation as the algorithm. As it depends on liveness analysis, keep that data around after codegen is finished (in IR::Stmt::Data). Added clean-up code for it in the IR::Function destructor. Change-Id: If3636648efbafcc1df469a24aaa885e21e6a2f16 Reviewed-by: Lars Knoll --- moth/qv4isel_moth.cpp | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++ qv4codegen.cpp | 21 +++++----- qv4ir.cpp | 6 +++ 3 files changed, 125 insertions(+), 9 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 318b227df1..3dc747caa7 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -22,6 +22,111 @@ static unsigned toValueOrTemp(IR::Expr *e, Instr::ValueOrTemp &vot) } } +class CompressTemps: public IR::StmtVisitor, IR::ExprVisitor +{ +public: + void run(IR::Function *function) + { + _active.reserve(function->tempCount); + _localCount = function->locals.size(); + int maxUsed = _localCount; + + foreach (IR::BasicBlock *block, function->basicBlocks) { + foreach (IR::Stmt *s, block->statements) { + _currentStatement = s; + if (s->d) + s->accept(this); + } + maxUsed = std::max(maxUsed, _nextFree + _localCount); + } + + function->tempCount = maxUsed; + } + +private: + virtual void visitConst(IR::Const *) {} + virtual void visitString(IR::String *) {} + virtual void visitRegExp(IR::RegExp *) {} + virtual void visitName(IR::Name *) {} + virtual void visitClosure(IR::Closure *) {} + virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); } + virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(IR::Member *e) { e->base->accept(this); } + virtual void visitExp(IR::Exp *s) { s->expr->accept(this); } + virtual void visitEnter(IR::Enter *) {} + virtual void visitLeave(IR::Leave *) {} + virtual void visitJump(IR::Jump *) {} + virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); } + virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } + + virtual void visitTemp(IR::Temp *e) { + if (e->index < 0) + return; + if (e->index < _localCount) // don't optimise locals yet. + return; + + e->index = remap(e->index - _localCount) + _localCount; + } + + virtual void visitCall(IR::Call *e) { + e->base->accept(this); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(IR::New *e) { + e->base->accept(this); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitMove(IR::Move *s) { + s->target->accept(this); + s->source->accept(this); + } + + int remap(int tempIndex) { + for (ActiveTemps::const_iterator i = _active.begin(), ei = _active.end(); i < ei; ++i) { + if (i->first == tempIndex) { + return i->second; + } + } + + int firstFree = expireOld(); + if (_nextFree <= firstFree) + _nextFree = firstFree + 1; + _active.prepend(qMakePair(tempIndex, firstFree)); + return firstFree; + } + + int expireOld() { + const QBitArray &liveIn = _currentStatement->d->liveIn; + QBitArray inUse(_nextFree); + int i = 0; + while (i < _active.size()) { + const QPair &p = _active[i]; + if (liveIn[p.first + _localCount]) { + inUse[p.second] = true; + ++i; + } else { + _active.remove(i); + } + } + for (int i = 0, ei = inUse.size(); i < ei; ++i) + if (!inUse[i]) + return i; + return _nextFree; + } + +private: + typedef QVector > ActiveTemps; + ActiveTemps _active; + IR::Stmt *_currentStatement; + int _localCount; + int _nextFree; +}; + } // anonymous namespace InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module * /*module*/, @@ -38,6 +143,8 @@ void InstructionSelection::operator()(IR::Function *function) { qSwap(_function, function); + CompressTemps().run(_function); + _function->code = VME::exec; _function->codeData = _ccode; diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 7a367ca9a7..30def8f5d4 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1432,7 +1432,17 @@ void Codegen::linearize(IR::Function *function) if (IR::BasicBlock *bb = leader.value(s)) { qout << endl; - qout << 'L' << bb->index << ':' << endl; + QByteArray str; + str.append('L'); + str.append(QByteArray::number(bb->index)); + str.append(':'); + for (int i = 66 - str.length(); i; --i) + str.append(' '); + qout << str; + qout << "// predecessor blocks:"; + foreach (IR::BasicBlock *in, bb->in) + qout << " L" << in->index; + qout << endl; } IR::Stmt *n = (i + 1) < code.size() ? code.at(i + 1) : 0; if (n && s->asJump() && s->asJump()->target == leader.value(n)) { @@ -1467,7 +1477,7 @@ void Codegen::linearize(IR::Function *function) // } if (! s->d->liveOut.isEmpty()) { - qout << " // lives:"; + qout << " // lives out:"; for (int i = 0; i < s->d->liveOut.size(); ++i) { if (s->d->liveOut.testBit(i)) qout << " %" << i; @@ -1487,13 +1497,6 @@ void Codegen::linearize(IR::Function *function) qout << "}" << endl << endl; } - -#ifndef QV4_NO_LIVENESS - foreach (IR::BasicBlock *block, function->basicBlocks) { - foreach (IR::Stmt *s, block->statements) - s->destroyData(); - } -#endif } IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, diff --git a/qv4ir.cpp b/qv4ir.cpp index 811c8024a2..3a84f64346 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -387,6 +387,12 @@ Function *Module::newFunction(const QString &name) Function::~Function() { + // destroy the Stmt::Data blocks manually, because memory pool cleanup won't + // call the Stmt destructors. + foreach (IR::BasicBlock *b, basicBlocks) + foreach (IR::Stmt *s, b->statements) + s->destroyData(); + qDeleteAll(basicBlocks); } -- cgit v1.2.3 From 06454ec1e949fe6aec961d74677be1262d88c482 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 20 Nov 2012 12:45:36 +0100 Subject: Cleanup instruction selection interface for MASM and MOTH. This fixes a regression in MOTH. Change-Id: Icd9e2ebf49ab6190bf932a94da03c4171c8d9c61 Reviewed-by: Lars Knoll --- moth/qv4isel_moth.cpp | 10 ++++++---- moth/qv4isel_moth_p.h | 9 +++------ qmljs_objects.cpp | 12 +----------- qv4ecmaobjects.cpp | 4 ++-- qv4ir.cpp | 1 + qv4isel_masm.cpp | 20 +------------------- qv4isel_masm_p.h | 12 +++--------- qv4isel_p.h | 3 +-- 8 files changed, 18 insertions(+), 53 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 3dc747caa7..503a82505d 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -129,10 +129,12 @@ private: } // anonymous namespace -InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module * /*module*/, - uchar *code) -: _engine(engine), _code(code), _ccode(code) +InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine) + : _engine(engine) { + // FIXME: make the size dynamic. This requires changing the patching. + _code = new uchar[getpagesize() * 4000]; + _ccode = _code; } InstructionSelection::~InstructionSelection() @@ -146,7 +148,7 @@ void InstructionSelection::operator()(IR::Function *function) CompressTemps().run(_function); _function->code = VME::exec; - _function->codeData = _ccode; + _function->codeData = _code; int locals = _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments + 1; assert(locals >= 0); diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 1105eb1c73..b181f79831 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -12,16 +12,13 @@ namespace Moth { class InstructionSelection : public IR::StmtVisitor, public EvalInstructionSelection { public: - InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *code); + InstructionSelection(VM::ExecutionEngine *engine); ~InstructionSelection(); virtual void run(IR::Function *function) { this->operator()(function); } virtual void operator()(IR::Function *function); - virtual bool finishModule(size_t) - { return true; } - protected: virtual void visitExp(IR::Exp *); virtual void visitEnter(IR::Enter *); @@ -67,8 +64,8 @@ class ISelFactory: public EValISelFactory { public: virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module, uchar *code) - { return new InstructionSelection(engine, module, code); } + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine) + { return new InstructionSelection(engine); } }; template diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index c920c124d0..cbe5cba8a3 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -515,13 +515,6 @@ Value EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &f IR::Module module; IR::Function *globalCode = 0; - const size_t codeSize = 400 * getpagesize(); - uchar *code = 0; - if (posix_memalign((void**)&code, 16, codeSize)) - assert(!"memalign failed"); - assert(code); - assert(! (size_t(code) & 15)); - { QQmlJS::Engine ee, *engine = ⅇ Lexer lexer(engine); @@ -542,12 +535,9 @@ Value EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &f Codegen cg; globalCode = cg(program, &module, mode); - foreach (IR::Function *function, module.functions) { - EvalInstructionSelection *isel = factory->create(vm, &module, code); + EvalInstructionSelection *isel = factory->create(vm); isel->run(function); - if (! isel->finishModule(codeSize)) - Q_UNREACHABLE(); delete isel; } } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 16ba5e118e..2cabb0ea45 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1775,8 +1775,8 @@ Value FunctionCtor::construct(ExecutionContext *ctx) Codegen cg; IR::Function *irf = cg(fe, &module); - uchar *code = 0; - MASM::InstructionSelection isel(ctx->engine, &module, code); + // FIXME: this mixes MASM and MOTH + MASM::InstructionSelection isel(ctx->engine); isel(irf); ctx->thisObject = Value::fromObject(new ScriptFunction(ctx->engine->rootContext, irf)); diff --git a/qv4ir.cpp b/qv4ir.cpp index 3a84f64346..6d23129baf 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -394,6 +394,7 @@ Function::~Function() s->destroyData(); qDeleteAll(basicBlocks); + delete[] codeData; } const QString *Function::newString(const QString &text) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 6f95671c54..2b5fe97d54 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -76,14 +76,10 @@ static void printDisassembledOutputWithCalls(const char* output, const QHash(addr); - size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); - int mode = PROT_READ | PROT_WRITE | PROT_EXEC; - return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; -} - -bool InstructionSelection::finishModule(size_t size) -{ - return protect(_code, size); -} - String *InstructionSelection::identifier(const QString &s) { return _engine->identifier(s); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index aaaf2e0f9a..468d00ff73 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -58,15 +58,13 @@ namespace MASM { class InstructionSelection: protected IR::StmtVisitor, public JSC::MacroAssembler, public EvalInstructionSelection { public: - InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *code); + InstructionSelection(VM::ExecutionEngine *engine); ~InstructionSelection(); virtual void run(IR::Function *function) { this->operator()(function); } void operator()(IR::Function *function); - virtual bool finishModule(size_t size); - protected: #if CPU(X86) @@ -630,12 +628,8 @@ private: } VM::ExecutionEngine *_engine; - IR::Module *_module; IR::Function *_function; IR::BasicBlock *_block; - uchar *_buffer; - uchar *_code; - uchar *_codePtr; QHash > _patches; QHash _addrs; QList _catchHandlers; @@ -646,8 +640,8 @@ class ISelFactory: public EValISelFactory { public: virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module, uchar *code) - { return new InstructionSelection(engine, module, code); } + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine) + { return new InstructionSelection(engine); } }; } // end of namespace MASM diff --git a/qv4isel_p.h b/qv4isel_p.h index effd44fcf3..d50ebae2a2 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -49,14 +49,13 @@ public: virtual ~EvalInstructionSelection() = 0; virtual void run(IR::Function *function) = 0; - virtual bool finishModule(size_t codeSize) = 0; }; class EValISelFactory { public: virtual ~EValISelFactory() = 0; - virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module, uchar *code) = 0; + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine) = 0; }; } // namespace QQmlJS -- cgit v1.2.3 From 1dab0e59049dbe2b9dee048c32a8f38f557dc29d Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 20 Nov 2012 13:24:24 +0100 Subject: Use an isel factory instead of MASM in FunctionCTor. Change-Id: I070f056411f16d837ff5eac191fac11cd0bd3c47 Reviewed-by: Lars Knoll --- qmljs_engine.cpp | 7 +------ qmljs_engine.h | 1 - qv4ecmaobjects.cpp | 9 +++++---- qv4ecmaobjects_p.h | 5 ++++- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 31ed260bee..6bb89378ff 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -92,7 +92,7 @@ ExecutionEngine::ExecutionEngine(EValISelFactory *factory) numberCtor = Value::fromObject(new NumberCtor(rootContext)); booleanCtor = Value::fromObject(new BooleanCtor(rootContext)); arrayCtor = Value::fromObject(new ArrayCtor(rootContext)); - functionCtor = Value::fromObject(new FunctionCtor(rootContext)); + functionCtor = Value::fromObject(new FunctionCtor(rootContext, factory)); dateCtor = Value::fromObject(new DateCtor(rootContext)); regExpCtor = Value::fromObject(new RegExpCtor(rootContext)); errorCtor = Value::fromObject(new ErrorCtor(rootContext)); @@ -258,11 +258,6 @@ Object *ExecutionEngine::newFunctionObject(ExecutionContext *ctx) return object; } -FunctionObject *ExecutionEngine::newFunctionCtor(ExecutionContext *ctx) -{ - return new FunctionCtor(ctx); -} - Object *ExecutionEngine::newArrayObject() { ArrayObject *object = new ArrayObject(); diff --git a/qmljs_engine.h b/qmljs_engine.h index 2404ebd7f8..862ad57b1f 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -161,7 +161,6 @@ struct ExecutionEngine FunctionObject *newBooleanCtor(ExecutionContext *ctx); Object *newFunctionObject(ExecutionContext *ctx); - FunctionObject *newFunctionCtor(ExecutionContext *ctx); Object *newArrayObject(); Object *newArrayObject(const Array &value); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 2cabb0ea45..514b04d65d 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1732,8 +1732,9 @@ Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) // // Function object // -FunctionCtor::FunctionCtor(ExecutionContext *scope) +FunctionCtor::FunctionCtor(ExecutionContext *scope, EValISelFactory *factory) : FunctionObject(scope) + , _factory(factory) { } @@ -1775,9 +1776,9 @@ Value FunctionCtor::construct(ExecutionContext *ctx) Codegen cg; IR::Function *irf = cg(fe, &module); - // FIXME: this mixes MASM and MOTH - MASM::InstructionSelection isel(ctx->engine); - isel(irf); + EvalInstructionSelection *isel = _factory->create(ctx->engine); + isel->run(irf); + delete isel; ctx->thisObject = Value::fromObject(new ScriptFunction(ctx->engine->rootContext, irf)); return ctx->thisObject; diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index d94c979413..354d9fce25 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -194,10 +194,13 @@ struct ArrayPrototype: ArrayObject struct FunctionCtor: FunctionObject { - FunctionCtor(ExecutionContext *scope); + FunctionCtor(ExecutionContext *scope, EValISelFactory *factory); virtual Value construct(ExecutionContext *ctx); virtual Value call(ExecutionContext *ctx); + +private: + EValISelFactory *_factory; }; struct FunctionPrototype: FunctionObject -- cgit v1.2.3 From 71446af5495a429f3c465c8bd2bcea4871408d57 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 20 Nov 2012 10:50:42 +0100 Subject: Fix function calls on linux/ia32 Remove supposed stack size alignment correction after function calls This code was supposed to "align" the stack pointer after a function call, but we can't really add more to the stack pointer than what we previously subtracted. It should've practically been a noop on amd64 since we don't actually use the stack for function calls there, which is why this manifested only on ia32. Change-Id: I5644e9e179bd1b7c45daca7d88687bd5d83a4104 Reviewed-by: Erik Verbruggen --- qv4isel_masm_p.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 468d00ff73..6e0316e87a 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -543,11 +543,6 @@ private: stackSizeToCorrect += sizeOfReturnValueOnStack; } -#if CPU(X86) || CPU(X86_64) - while (stackSizeToCorrect % 16) - ++stackSizeToCorrect; -#endif - storeArgument(ReturnValueRegister, r); if (stackSizeToCorrect) -- cgit v1.2.3 From 7704d8b3b6024881da15fea44f51db3b87286709 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 20 Nov 2012 13:41:49 +0100 Subject: Keep the EvalISelFactory in the ExecutionEngine. Also corrected the class name cApiTaliSatiOn. Change-Id: I131566e904c8ee575686a469f16d098dd512d865 Reviewed-by: Lars Knoll --- main.cpp | 6 +++--- moth/qv4isel_moth_p.h | 2 +- qmljs_engine.cpp | 7 ++++--- qmljs_engine.h | 3 ++- qmljs_objects.cpp | 7 +++---- qmljs_objects.h | 11 ++++------- qv4ecmaobjects.cpp | 5 ++--- qv4ecmaobjects_p.h | 5 +---- qv4isel_masm_p.h | 2 +- qv4isel_p.cpp | 2 +- qv4isel_p.h | 4 ++-- 11 files changed, 24 insertions(+), 30 deletions(-) diff --git a/main.cpp b/main.cpp index 4753279d2b..f703cbcf00 100644 --- a/main.cpp +++ b/main.cpp @@ -123,7 +123,7 @@ int executeLLVMCode(void *codePtr) return EXIT_FAILURE; void (*code)(VM::ExecutionContext *) = (void (*)(VM::ExecutionContext *)) codePtr; - QScopedPointer iSelFactory(new QQmlJS::Moth::ISelFactory); + QScopedPointer iSelFactory(new QQmlJS::Moth::ISelFactory); VM::ExecutionEngine vm(iSelFactory.data()); VM::ExecutionContext *ctx = vm.rootContext; @@ -289,7 +289,7 @@ int main(int argc, char *argv[]) #endif // QMLJS_NO_LLVM case use_masm: case use_moth: { - QScopedPointer iSelFactory; + QScopedPointer iSelFactory; if (mode == use_moth) iSelFactory.reset(new QQmlJS::Moth::ISelFactory); else @@ -318,7 +318,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - QQmlJS::VM::Value result = QQmlJS::VM::EvalFunction::evaluate(vm.rootContext, fn, code, iSelFactory.data(), QQmlJS::Codegen::GlobalCode); + QQmlJS::VM::Value result = QQmlJS::VM::EvalFunction::evaluate(vm.rootContext, fn, code, QQmlJS::Codegen::GlobalCode); if (!result.isUndefined()) { if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index b181f79831..043958b4b3 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -60,7 +60,7 @@ private: uchar *_ccode; }; -class ISelFactory: public EValISelFactory +class ISelFactory: public EvalISelFactory { public: virtual ~ISelFactory() {} diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 6bb89378ff..acf221304e 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -45,7 +45,8 @@ namespace QQmlJS { namespace VM { -ExecutionEngine::ExecutionEngine(EValISelFactory *factory) +ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) + : iselFactory(factory) { rootContext = newContext(); rootContext->init(this); @@ -92,7 +93,7 @@ ExecutionEngine::ExecutionEngine(EValISelFactory *factory) numberCtor = Value::fromObject(new NumberCtor(rootContext)); booleanCtor = Value::fromObject(new BooleanCtor(rootContext)); arrayCtor = Value::fromObject(new ArrayCtor(rootContext)); - functionCtor = Value::fromObject(new FunctionCtor(rootContext, factory)); + functionCtor = Value::fromObject(new FunctionCtor(rootContext)); dateCtor = Value::fromObject(new DateCtor(rootContext)); regExpCtor = Value::fromObject(new RegExpCtor(rootContext)); errorCtor = Value::fromObject(new ErrorCtor(rootContext)); @@ -160,7 +161,7 @@ ExecutionEngine::ExecutionEngine(EValISelFactory *factory) glo->__put__(rootContext, identifier(QStringLiteral("undefined")), Value::undefinedValue()); glo->__put__(rootContext, identifier(QStringLiteral("NaN")), Value::fromDouble(nan(""))); glo->__put__(rootContext, identifier(QStringLiteral("Infinity")), Value::fromDouble(INFINITY)); - glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new EvalFunction(rootContext, factory))); + glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new EvalFunction(rootContext))); // TODO: parseInt [15.1.2.2] // TODO: parseFloat [15.1.2.3] diff --git a/qmljs_engine.h b/qmljs_engine.h index 862ad57b1f..5460412576 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -83,6 +83,7 @@ struct URIErrorPrototype; struct ExecutionEngine { + EvalISelFactory *iselFactory; ExecutionContext *current; ExecutionContext *rootContext; @@ -138,7 +139,7 @@ struct ExecutionEngine QVector unwindStack; Value exception; - ExecutionEngine(EValISelFactory *factory); + ExecutionEngine(EvalISelFactory *iselFactory); ExecutionContext *newContext(); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index cbe5cba8a3..d19040304c 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -481,7 +481,7 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value ctx = context; } // ##### inline and do this in the correct scope - Value result = evaluate(ctx, QStringLiteral("eval code"), code, _factory, QQmlJS::Codegen::EvalCode); + Value result = evaluate(ctx, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode); if (strictMode) ctx->leaveCallContext(); @@ -506,8 +506,7 @@ Value IsFiniteFunction::call(ExecutionContext * /*context*/, Value /*thisObject* } Value EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, - const QString &source, EValISelFactory *factory, - QQmlJS::Codegen::Mode mode) + const QString &source, QQmlJS::Codegen::Mode mode) { using namespace QQmlJS; @@ -536,7 +535,7 @@ Value EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &f globalCode = cg(program, &module, mode); foreach (IR::Function *function, module.functions) { - EvalInstructionSelection *isel = factory->create(vm); + EvalInstructionSelection *isel = ctx->engine->iselFactory->create(vm); isel->run(function); delete isel; } diff --git a/qmljs_objects.h b/qmljs_objects.h index 6048e99771..31d493a790 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -524,16 +524,13 @@ struct ScriptFunction: FunctionObject { struct EvalFunction : FunctionObject { - EvalFunction(ExecutionContext *scope, EValISelFactory *factory): FunctionObject(scope), _factory(factory) {} + EvalFunction(ExecutionContext *scope): FunctionObject(scope) {} - static Value evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, - const QString &source, EValISelFactory *factory, - QQmlJS::Codegen::Mode mode); + static Value evaluate(QQmlJS::VM::ExecutionContext *ctx, + const QString &fileName, const QString &source, + QQmlJS::Codegen::Mode mode); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode = false); - -private: - EValISelFactory *_factory; }; struct IsNaNFunction: FunctionObject diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 514b04d65d..01c0d824fe 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1732,9 +1732,8 @@ Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) // // Function object // -FunctionCtor::FunctionCtor(ExecutionContext *scope, EValISelFactory *factory) +FunctionCtor::FunctionCtor(ExecutionContext *scope) : FunctionObject(scope) - , _factory(factory) { } @@ -1776,7 +1775,7 @@ Value FunctionCtor::construct(ExecutionContext *ctx) Codegen cg; IR::Function *irf = cg(fe, &module); - EvalInstructionSelection *isel = _factory->create(ctx->engine); + EvalInstructionSelection *isel = ctx->engine->iselFactory->create(ctx->engine); isel->run(irf); delete isel; diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 354d9fce25..d94c979413 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -194,13 +194,10 @@ struct ArrayPrototype: ArrayObject struct FunctionCtor: FunctionObject { - FunctionCtor(ExecutionContext *scope, EValISelFactory *factory); + FunctionCtor(ExecutionContext *scope); virtual Value construct(ExecutionContext *ctx); virtual Value call(ExecutionContext *ctx); - -private: - EValISelFactory *_factory; }; struct FunctionPrototype: FunctionObject diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 6e0316e87a..53201c0d00 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -631,7 +631,7 @@ private: QList _callsToLink; }; -class ISelFactory: public EValISelFactory +class ISelFactory: public EvalISelFactory { public: virtual ~ISelFactory() {} diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 294a22f6c8..175cea547e 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -5,5 +5,5 @@ using namespace QQmlJS; EvalInstructionSelection::~EvalInstructionSelection() {} -EValISelFactory::~EValISelFactory() +EvalISelFactory::~EvalISelFactory() {} diff --git a/qv4isel_p.h b/qv4isel_p.h index d50ebae2a2..e8c6d387b7 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -51,10 +51,10 @@ public: virtual void run(IR::Function *function) = 0; }; -class EValISelFactory +class EvalISelFactory { public: - virtual ~EValISelFactory() = 0; + virtual ~EvalISelFactory() = 0; virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine) = 0; }; -- cgit v1.2.3 From 8e76024d0afbe01e649ca963e46c785ca365cc92 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 22 Nov 2012 12:18:56 +0100 Subject: Fix for local count in the global context. Change-Id: If0f07c995a51df45603c5581c721da15c1050158 Reviewed-by: Lars Knoll --- moth/qv4isel_moth.cpp | 21 +++++++++++++++++---- moth/qv4vme_moth.cpp | 20 +++++++++++--------- qv4codegen.cpp | 7 +++++-- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 503a82505d..2276756bfc 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -22,14 +22,16 @@ static unsigned toValueOrTemp(IR::Expr *e, Instr::ValueOrTemp &vot) } } +#undef DEBUG_TEMP_COMPRESSION class CompressTemps: public IR::StmtVisitor, IR::ExprVisitor { public: void run(IR::Function *function) { + _nextFree = 0; _active.reserve(function->tempCount); _localCount = function->locals.size(); - int maxUsed = _localCount; + int maxUsed = 0; foreach (IR::BasicBlock *block, function->basicBlocks) { foreach (IR::Stmt *s, block->statements) { @@ -37,10 +39,12 @@ public: if (s->d) s->accept(this); } - maxUsed = std::max(maxUsed, _nextFree + _localCount); + maxUsed = std::max(maxUsed, _nextFree); } - - function->tempCount = maxUsed; +#ifdef DEBUG_TEMP_COMPRESSION + qDebug() << "function" << (*function->name) << "uses" << maxUsed << "temps."; +#endif // DEBUG_TEMP_COMPRESSION + function->tempCount = maxUsed + _localCount; } private: @@ -89,6 +93,9 @@ private: int remap(int tempIndex) { for (ActiveTemps::const_iterator i = _active.begin(), ei = _active.end(); i < ei; ++i) { if (i->first == tempIndex) { +#ifdef DEBUG_TEMP_COMPRESSION + qDebug() << "lookup" << (tempIndex + _localCount) << "->" << (i->second + _localCount); +#endif // DEBUG_TEMP_COMPRESSION return i->second; } } @@ -97,6 +104,9 @@ private: if (_nextFree <= firstFree) _nextFree = firstFree + 1; _active.prepend(qMakePair(tempIndex, firstFree)); +#ifdef DEBUG_TEMP_COMPRESSION + qDebug() << " add" << (tempIndex + _localCount) << "->" << (firstFree+ _localCount); +#endif // DEBUG_TEMP_COMPRESSION return firstFree; } @@ -110,6 +120,9 @@ private: inUse[p.second] = true; ++i; } else { +#ifdef DEBUG_TEMP_COMPRESSION + qDebug() << "remove" << (p.first + _localCount); +#endif // DEBUG_TEMP_COMPRESSION _active.remove(i); } } diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 48a02f6032..7e8ba0f711 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -50,18 +50,20 @@ using namespace QQmlJS::Moth; static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVector &stack, int index) { + VM::DeclarativeEnvironment *varEnv = context->variableEnvironment; + #ifdef DO_TRACE_INSTR const char *kind; int pos; if (index < 0) { kind = "arg"; pos = -index - 1; - } else if (index < (int) context->variableEnvironment->varCount) { + } else if (index < (int) varEnv->varCount) { kind = "local"; pos = index; } else { kind = "temp"; - pos = index - context->variableEnvironment->varCount; + pos = index - varEnv->varCount; } fprintf(stderr, " tempValue: index = %d : %s = %d, stack size = %d\n", index, kind, pos, stack.size()); @@ -71,17 +73,17 @@ static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVecto const int arg = -index - 1; Q_ASSERT(arg >= 0); - Q_ASSERT((unsigned) arg < context->variableEnvironment->argumentCount); - Q_ASSERT(context->variableEnvironment->arguments); + Q_ASSERT((unsigned) arg < varEnv->argumentCount); + Q_ASSERT(varEnv->arguments); - return context->variableEnvironment->arguments + arg; - } else if (index < (int) context->variableEnvironment->varCount) { + return varEnv->arguments + arg; + } else if (index < (int) varEnv->varCount) { Q_ASSERT(index >= 0); - Q_ASSERT(context->variableEnvironment->locals); + Q_ASSERT(varEnv->locals); - return context->variableEnvironment->locals + index; + return varEnv->locals + index; } else { - int off = index - context->variableEnvironment->varCount; + int off = index - varEnv->varCount; Q_ASSERT(off >= 0); Q_ASSERT(off < stack.size()); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 30def8f5d4..f6ba992ee4 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1537,8 +1537,11 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, _function->RECEIVE(it->name.toString()); } - foreach (const QString &local, _env->vars) { - _function->LOCAL(local); + // variables in global code are properties of the global context object, not locals as with other functions. + if (mode != GlobalCode) { + foreach (const QString &local, _env->vars) { + _function->LOCAL(local); + } } foreach (AST::FunctionDeclaration *f, _env->functions) { -- cgit v1.2.3 From d2609682609b6f9523f30eced4b0afdf2135979f Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 22 Nov 2012 16:28:13 +0100 Subject: Fixed temp compression and added a whole lot of debug info. Unlike the variables (locals), the return value did not have a fixed temp. So by pinning it, it won't move around. Change-Id: Ib35024e2baa60b64fcb05ca4aa7f49acad89e7cb Reviewed-by: Lars Knoll --- moth/qv4isel_moth.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++--------- moth/qv4vme_moth.cpp | 4 +++- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 2276756bfc..7f4344ba3c 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -31,13 +31,25 @@ public: _nextFree = 0; _active.reserve(function->tempCount); _localCount = function->locals.size(); - int maxUsed = 0; + pinReturnTemp(function); + int maxUsed = _nextFree; foreach (IR::BasicBlock *block, function->basicBlocks) { - foreach (IR::Stmt *s, block->statements) { - _currentStatement = s; - if (s->d) - s->accept(this); +#ifdef DEBUG_TEMP_COMPRESSION + qDebug("L%d:", block->index); +#endif // DEBUG_TEMP_COMPRESSION + + for (int i = 0, ei = block->statements.size(); i < ei; ++i ) { + _currentStatement = block->statements[i]; + if (i == 0) + expireOld(); + +#ifdef DEBUG_TEMP_COMPRESSION + _currentStatement->dump(qout);qout<d) + _currentStatement->accept(this); } maxUsed = std::max(maxUsed, _nextFree); } @@ -48,6 +60,18 @@ public: } private: + void pinReturnTemp(IR::Function *function) { + const IR::BasicBlock *returnBlock = function->basicBlocks.last(); + assert(returnBlock); + IR::Ret *ret = returnBlock->terminator()->asRet(); + assert(ret); + IR::Temp *t = ret->expr->asTemp(); + assert(t); + assert(t->index >= 0); + _pinnedReturnValue = _nextFree; + add(t->index, _pinnedReturnValue); + } + virtual void visitConst(IR::Const *) {} virtual void visitString(IR::String *) {} virtual void visitRegExp(IR::RegExp *) {} @@ -94,34 +118,47 @@ private: for (ActiveTemps::const_iterator i = _active.begin(), ei = _active.end(); i < ei; ++i) { if (i->first == tempIndex) { #ifdef DEBUG_TEMP_COMPRESSION - qDebug() << "lookup" << (tempIndex + _localCount) << "->" << (i->second + _localCount); + qDebug() << " lookup" << (tempIndex + _localCount) << "->" << (i->second + _localCount); #endif // DEBUG_TEMP_COMPRESSION return i->second; } } int firstFree = expireOld(); + add(tempIndex, firstFree); + return firstFree; + } + + void add(int tempIndex, int firstFree) { if (_nextFree <= firstFree) _nextFree = firstFree + 1; _active.prepend(qMakePair(tempIndex, firstFree)); #ifdef DEBUG_TEMP_COMPRESSION - qDebug() << " add" << (tempIndex + _localCount) << "->" << (firstFree+ _localCount); + qDebug() << " add" << (tempIndex + _localCount) << "->" << (firstFree+ _localCount); #endif // DEBUG_TEMP_COMPRESSION - return firstFree; } int expireOld() { + Q_ASSERT(_currentStatement->d); + const QBitArray &liveIn = _currentStatement->d->liveIn; QBitArray inUse(_nextFree); int i = 0; while (i < _active.size()) { const QPair &p = _active[i]; + + if (p.second == _pinnedReturnValue) { + inUse.setBit(p.second); + ++i; + continue; + } + if (liveIn[p.first + _localCount]) { inUse[p.second] = true; ++i; } else { #ifdef DEBUG_TEMP_COMPRESSION - qDebug() << "remove" << (p.first + _localCount); + qDebug() << " remove" << (p.first + _localCount) << "->" << (p.second + _localCount); #endif // DEBUG_TEMP_COMPRESSION _active.remove(i); } @@ -138,6 +175,7 @@ private: IR::Stmt *_currentStatement; int _localCount; int _nextFree; + int _pinnedReturnValue; }; } // anonymous namespace diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 7e8ba0f711..d8871f40c5 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -305,7 +305,9 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(Binop) MOTH_BEGIN_INSTR(Ret) - return TEMP(instr.tempIndex); + VM::Value result = TEMP(instr.tempIndex); + TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); + return result; MOTH_END_INSTR(Ret) MOTH_BEGIN_INSTR(LoadThis) -- cgit v1.2.3 From 92420c29563176e3e1185a89c185267b8c455c44 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 23 Nov 2012 23:56:25 +0100 Subject: Correctly bind varibles in the global context Variables need to be bound to the execution context before entering the method. For the global scope this implies that declared variables are available, but may be undefined. The lines: print(x) var x = 1; has to print 'undefined' and not throw a type error. Change-Id: I9d7062823da83cd4c4e74588693bb0ce8dc85c6b Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index f6ba992ee4..a8e3c0b24c 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1514,9 +1514,16 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, function->hasNestedFunctions = _env->hasNestedFunctions; function->maxNumberOfArguments = _env->maxNumberOfArguments; + // variables in global code are properties of the global context object, not locals as with other functions. for (int i = 0; i < _env->vars.size(); ++i) { - unsigned t = entryBlock->newTemp(); - assert(t == unsigned(i)); + const QString &local = _env->vars.at(i); + if (!_env->parent) { + entryBlock->MOVE(entryBlock->NAME(local, 0, 0), entryBlock->CONST(IR::UndefinedType, 0)); + } else { + function->LOCAL(local); + unsigned t = entryBlock->newTemp(); + assert(t == unsigned(i)); + } } unsigned returnAddress = entryBlock->newTemp(); @@ -1537,13 +1544,6 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, _function->RECEIVE(it->name.toString()); } - // variables in global code are properties of the global context object, not locals as with other functions. - if (mode != GlobalCode) { - foreach (const QString &local, _env->vars) { - _function->LOCAL(local); - } - } - foreach (AST::FunctionDeclaration *f, _env->functions) { IR::Function *function = defineFunction(f->name.toString(), f, f->formals, f->body ? f->body->elements : 0); -- cgit v1.2.3 From 7ec56d373d0c843ce19cfe03cc3ef0b0cb48a23b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 24 Nov 2012 08:37:20 +0100 Subject: Fix compilation on ia32 Added a stub loadArgument(Expr, Register) that isn't actually used at run-time Change-Id: Ie75df4dbdde84f9c51f164292ff2fc9b01aa1da3 Reviewed-by: Lars Knoll --- qv4isel_masm_p.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 53201c0d00..8fee0a49ab 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -307,6 +307,11 @@ private: assert(!"unimplemented expression type in loadArgument"); } } +#else + void loadArgument(IR::Expr*, RegisterID) + { + assert(!"unimplemented: expression in loadArgument"); + } #endif void loadArgument(VM::String* string, RegisterID dest) -- cgit v1.2.3 From 889ac44c014e8634dc8a3b3dff987481062b6cc5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 24 Nov 2012 09:59:51 +0100 Subject: Fix return from generated code when return value is passed on the stack On ia32 the return VM value doesn't fit into a register. Instead the caller provides a pointer to where the return value can be stored as a first invisible argument. This is where we then copy the return value to. It is the callee's responsibility to remove that one invisible argument from the stack. Usually this is done using a "ret " instruction, which however masm doesn't support, so we emulate it. That might not be any slower, since it's kind of replacing the CISC ret instruction with RISC :) Change-Id: I4cb842e5a0a95a3d52867e66216dd711d627cf25 Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 2b5fe97d54..3bd4049547 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -95,10 +95,17 @@ void InstructionSelection::operator()(IR::Function *function) locals = (locals + 1) & ~1; enterStandardStackFrame(locals); + int contextPointer = 0; +#ifndef VALUE_FITS_IN_REGISTER + // When the return VM value doesn't fit into a register, then + // the caller provides a pointer for storage as first argument. + // That shifts the index the context pointer argument by one. + contextPointer++; +#endif #if CPU(X86) - loadPtr(addressForArgument(0), ContextRegister); + loadPtr(addressForArgument(contextPointer), ContextRegister); #elif CPU(X86_64) || CPU(ARM) - move(registerForArgument(0), ContextRegister); + move(registerForArgument(contextPointer), ContextRegister); #else assert(!"TODO"); #endif @@ -112,6 +119,14 @@ void InstructionSelection::operator()(IR::Function *function) } leaveStandardStackFrame(locals); +#ifndef VALUE_FITS_IN_REGISTER + // Emulate ret(n) instruction + // Pop off return address into scratch register ... + pop(ScratchRegister); + // ... and overwrite the invisible argument with + // the return address. + poke(ScratchRegister); +#endif ret(); QHashIterator > it(_patches); @@ -704,7 +719,12 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { if (IR::Temp *t = s->expr->asTemp()) { +#ifdef VALUE_FITS_IN_REGISTER copyValue(ReturnValueRegister, t); +#else + loadPtr(addressForArgument(0), ReturnValueRegister); + copyValue(Address(ReturnValueRegister, 0), t); +#endif return; } Q_UNIMPLEMENTED(); -- cgit v1.2.3 From 2bd3fa3fc27379a78e8cca0e66eda800729ed8c9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 24 Nov 2012 22:04:33 +0100 Subject: Remove some unused methods Change-Id: Ic23a93822ff28701890e97c2d5844fa9a23c4b4a Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 20ea18467c..4053e42663 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -651,30 +651,6 @@ void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, Value ctx->engine->globalObject.objectValue()->__put__(ctx, name, value); } -void __qmljs_set_activation_property_boolean(ExecutionContext *ctx, String *name, bool b) -{ - Value value = Value::fromBoolean(b); - __qmljs_set_activation_property(ctx, name, value); -} - -void __qmljs_set_activation_property_number(ExecutionContext *ctx, String *name, double number) -{ - Value value = Value::fromDouble(number); - __qmljs_set_activation_property(ctx, name, value); -} - -void __qmljs_set_activation_property_string(ExecutionContext *ctx, String *name, String *string) -{ - Value value = Value::fromString(string); - __qmljs_set_activation_property(ctx, name, value); -} - -void __qmljs_set_activation_property_closure(ExecutionContext *ctx, String *name, IR::Function *clos) -{ - Value value = __qmljs_init_closure(clos, ctx); - __qmljs_set_activation_property(ctx, name, value); -} - Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name) { if (object.isObject()) { -- cgit v1.2.3 From 87b31798170233b92f13ba4d39e0278fdf61e10e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 24 Nov 2012 22:07:02 +0100 Subject: Support for the with statement Add the with object (list) to the environment, and check properties there if it's available. Generate IR statements implementing with() support. Add two new builtin methods to enter and leave a with scope. Implement support for the builtin's in masm. Make sure exception handling works across with scopes. Change-Id: I8257a16cfccc91a1acedfd740ade711b016b33fd Reviewed-by: Simon Hausmann --- qmljs_engine.h | 1 + qmljs_environment.cpp | 68 +++++++++++++++++++++++++++++++++++++++------------ qmljs_environment.h | 14 +++++++++-- qmljs_runtime.cpp | 18 ++++++++++++++ qmljs_runtime.h | 2 ++ qv4codegen.cpp | 25 ++++++++++++++++--- qv4ir.cpp | 4 +++ qv4ir_p.h | 6 ++++- qv4isel_masm.cpp | 9 +++++++ tests/with.js | 43 ++++++++++++++++++++++++++++++++ 10 files changed, 167 insertions(+), 23 deletions(-) create mode 100644 tests/with.js diff --git a/qmljs_engine.h b/qmljs_engine.h index 5460412576..8d5662783a 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -131,6 +131,7 @@ struct ExecutionEngine struct ExceptionHandler { ExecutionContext *context; + DeclarativeEnvironment::With *with; const uchar *code; // Interpreter state int targetTempIndex; // Interpreter state jmp_buf stackFrame; diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 121670c999..5504763b31 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -52,11 +52,12 @@ DeclarativeEnvironment::DeclarativeEnvironment(ExecutionEngine *e) arguments = 0; argumentCount = 0; locals = 0; - activation = 0; formals = 0; formalCount = 0; vars = 0; varCount = 0; + activation = 0; + withObject = 0; } DeclarativeEnvironment::DeclarativeEnvironment(FunctionObject *f, Value *args, uint argc) @@ -64,11 +65,6 @@ DeclarativeEnvironment::DeclarativeEnvironment(FunctionObject *f, Value *args, u outer = f->scope; engine = outer->engine; - if (f->needsActivation) - activation = engine->newActivationObject(this); - else - activation = 0; - formals = f->formalParameterList; formalCount = f->formalParameterCount; arguments = args; @@ -85,6 +81,13 @@ DeclarativeEnvironment::DeclarativeEnvironment(FunctionObject *f, Value *args, u locals = varCount ? new Value[varCount] : 0; if (varCount) std::fill(locals, locals + varCount, Value::undefinedValue()); + + if (f->needsActivation) + activation = engine->newActivationObject(this); + else + activation = 0; + + withObject = 0; } bool DeclarativeEnvironment::hasBinding(String *name) const @@ -97,7 +100,9 @@ bool DeclarativeEnvironment::hasBinding(String *name) const if (__qmljs_string_equal(formals[i], name)) return true; } - return deletableLocals.contains(name->toQString()); + if (!deletableLocals) + return false; + return deletableLocals->contains(name->toQString()); } void DeclarativeEnvironment::createMutableBinding(String *name, bool deletable) @@ -106,7 +111,9 @@ void DeclarativeEnvironment::createMutableBinding(String *name, bool deletable) assert(deletable); assert(!hasBinding(name)); - deletableLocals.insert(name->toQString(), Value::undefinedValue()); + if (!deletableLocals) + deletableLocals = new QHash(); + deletableLocals->insert(name->toQString(), Value::undefinedValue()); } void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool strict) @@ -126,8 +133,9 @@ void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool s return; } } - QHash::iterator it = deletableLocals.find(name->toQString()); - if (it != deletableLocals.end()) { + assert(deletableLocals); + QHash::iterator it = deletableLocals->find(name->toQString()); + if (it != deletableLocals->end()) { *it = value; return; } @@ -146,8 +154,9 @@ Value DeclarativeEnvironment::getBindingValue(String *name, bool strict) const if (__qmljs_string_equal(formals[i], name)) return arguments[i]; } - QHash::const_iterator it = deletableLocals.find(name->toQString()); - if (it != deletableLocals.end()) + assert(deletableLocals); + QHash::const_iterator it = deletableLocals->find(name->toQString()); + if (it != deletableLocals->end()) return *it; assert(false); @@ -155,14 +164,33 @@ Value DeclarativeEnvironment::getBindingValue(String *name, bool strict) const bool DeclarativeEnvironment::deleteBinding(String *name) { - QHash::iterator it = deletableLocals.find(name->toQString()); - if (it != deletableLocals.end()) { - deletableLocals.erase(it); - return true; + if (deletableLocals) { + QHash::iterator it = deletableLocals->find(name->toQString()); + if (it != deletableLocals->end()) { + deletableLocals->erase(it); + return true; + } } return !hasBinding(name); } +void DeclarativeEnvironment::pushWithObject(Object *with) +{ + With *w = new With; + w->next = withObject; + w->object = with; + withObject = w; +} + +void DeclarativeEnvironment::popWithObject() +{ + assert(withObject); + + With *w = withObject; + withObject = w->next; + delete w; +} + void ExecutionContext::init(ExecutionEngine *eng) { @@ -177,6 +205,14 @@ void ExecutionContext::init(ExecutionEngine *eng) PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp) { for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer) { + if (ctx->withObject) { + DeclarativeEnvironment::With *w = ctx->withObject; + while (w) { + if (PropertyDescriptor *pd = w->object->__getPropertyDescriptor__(this, name, tmp)) + return pd; + w = w->next; + } + } if (ctx->activation) { if (PropertyDescriptor *pd = ctx->activation->__getPropertyDescriptor__(this, name, tmp)) return pd; diff --git a/qmljs_environment.h b/qmljs_environment.h index b16c0f9325..7a3a012b01 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -59,7 +59,6 @@ struct DeclarativeEnvironment ExecutionEngine *engine; DeclarativeEnvironment *outer; - Object *activation; Value *arguments; unsigned int argumentCount; Value *locals; @@ -68,18 +67,29 @@ struct DeclarativeEnvironment String **vars; unsigned int varCount; + Object *activation; + struct With { + Object *object; + With *next; + } *withObject; + // these get used for createMutableBinding(..., true). // the only place this is being used is eval(...) - QHash deletableLocals; + QHash *deletableLocals; DeclarativeEnvironment(ExecutionEngine *e); DeclarativeEnvironment(FunctionObject *f, Value *args, uint argc); + ~DeclarativeEnvironment() { delete deletableLocals; } bool hasBinding(String *name) const; void createMutableBinding(String *name, bool deletable); void setMutableBinding(String *name, Value value, bool strict); Value getBindingValue(String *name, bool strict) const; bool deleteBinding(String *name); + + // ### needs a bit of work in exception handlers + void pushWithObject(Object *with); + void popWithObject(); }; struct ExecutionContext diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 4053e42663..eabceeff7f 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -819,6 +819,12 @@ void __qmljs_throw(Value value, ExecutionContext *context) context->leaveCallContext(); context = context->parent; } + DeclarativeEnvironment *env = context->lexicalEnvironment; + while (env->withObject != handler.with) { + DeclarativeEnvironment::With *w = env->withObject; + env->withObject = w->next; + delete w; + } context->engine->exception = value; @@ -831,6 +837,7 @@ void *__qmljs_create_exception_handler(ExecutionContext *context) context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler()); ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); handler.context = context; + handler.with = context->lexicalEnvironment->withObject; return handler.stackFrame; } @@ -856,6 +863,17 @@ void __qmljs_builtin_throw(Value val, ExecutionContext *context) __qmljs_throw(val, context); } +void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx) +{ + Object *obj = __qmljs_to_object(o, ctx).asObject(); + ctx->lexicalEnvironment->pushWithObject(obj); +} + +void __qmljs_builtin_pop_with(ExecutionContext *ctx) +{ + ctx->lexicalEnvironment->popWithObject(); +} + } // extern "C" diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 4a1e3480b9..ff8395af13 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -101,6 +101,8 @@ Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args Value __qmljs_builtin_typeof(Value val, ExecutionContext *context); void __qmljs_builtin_throw(Value val, ExecutionContext *context); +void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx); +void __qmljs_builtin_pop_with(ExecutionContext *ctx); // constructors Value __qmljs_init_closure(IR::Function *clos, ExecutionContext *ctx); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index a8e3c0b24c..33bb064996 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -665,7 +665,7 @@ void Codegen::variableDeclaration(VariableDeclaration *ast) if (! initializer) initializer = _block->CONST(IR::UndefinedType, 0); - if (! _env->parent) { + if (! _env->parent || _function->insideWith) { // it's global code. move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), initializer); } else { @@ -1113,7 +1113,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col) { int index = _env->findMember(name); - if (! _function->hasDirectEval && _env->parent) { + if (! _function->hasDirectEval && !_function->insideWith && _env->parent) { if (index != -1) { return _block->TEMP(index); } @@ -2137,9 +2137,26 @@ bool Codegen::visit(WhileStatement *ast) return false; } -bool Codegen::visit(WithStatement *) +bool Codegen::visit(WithStatement *ast) { - assert(!"with not implemented"); + IR::BasicBlock *withBlock = _function->newBasicBlock(); + + _block->JUMP(withBlock); + _block = withBlock; + int withObject = _block->newTemp(); + _block->MOVE(_block->TEMP(withObject), *expression(ast->expression)); + IR::ExprList *args = _function->New(); + args->init(_block->TEMP(withObject)); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with, 0, 0), args)); + ++_function->insideWith; + statement(ast->statement); + --_function->insideWith; + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_with, 0, 0), 0)); + + IR::BasicBlock *next = _function->newBasicBlock(); + _block->JUMP(next); + _block = next; + return false; } diff --git a/qv4ir.cpp b/qv4ir.cpp index 6d23129baf..57f6de958f 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -234,6 +234,10 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_foreach_iterator_object"; case IR::Name::builtin_foreach_next_property_name: return "builtin_foreach_next_property_name"; + case IR::Name::builtin_push_with: + return "builtin_push_with"; + case IR::Name::builtin_pop_with: + return "builtin_pop_with"; } return "builtin_(###FIXME)"; }; diff --git a/qv4ir_p.h b/qv4ir_p.h index 502275d4b9..b0be0f9bb2 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -276,7 +276,9 @@ struct Name: Expr { builtin_delete_exception_handler, builtin_get_exception, builtin_foreach_iterator_object, - builtin_foreach_next_property_name + builtin_foreach_next_property_name, + builtin_push_with, + builtin_pop_with }; const QString *id; @@ -601,6 +603,7 @@ struct Function { bool hasDirectEval: 1; bool hasNestedFunctions: 1; + int insideWith; template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } @@ -613,6 +616,7 @@ struct Function { , codeData(0) , hasDirectEval(false) , hasNestedFunctions(false) + , insideWith(0) { this->name = newString(name); } ~Function(); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 3bd4049547..270209a37f 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -264,6 +264,15 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu generateFunctionCall(result, __qmljs_foreach_next_property_name, arg); } break; + case IR::Name::builtin_push_with: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + generateFunctionCall(Void, __qmljs_builtin_push_with, arg, ContextRegister); + } + break; + case IR::Name::builtin_pop_with: + generateFunctionCall(Void, __qmljs_builtin_pop_with, ContextRegister); + break; } } diff --git a/tests/with.js b/tests/with.js new file mode 100644 index 0000000000..91ab5a0cdc --- /dev/null +++ b/tests/with.js @@ -0,0 +1,43 @@ +var o = { "x": 1 } +var x = 0; +with(o) { + with( { "x": 2 } ) { + print(x) + } + print(x) +} +print(x) + + +function foo() { + var x = 0; + with(o) { + with( { "x": 2 } ) { + print(x) + } + print(x) + } + print(x) +} + +print("\n") +foo(); + + +function bar() { + var x = 0; + try { + with(o) { + with( { "x": 2 } ) { + print(x) + throw 0; + } + print(x) + } + } + catch(e) {} + print(x) +} + +print("\n") +bar(); -- cgit v1.2.3 From 15326415d127e17016b60820b85c85016c3682e6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 25 Nov 2012 00:26:28 +0100 Subject: Fix some issues with the delete operator Properly implement delete operator for identifiers and local variables. Change-Id: I8ac55edc80c31a94d11444c9f5c78caf4b131c95 Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 9 ++------- moth/qv4vme_moth.cpp | 4 ++-- qmljs_environment.cpp | 20 ++++++++++++++++++++ qmljs_environment.h | 1 + qmljs_objects.h | 2 +- qmljs_runtime.cpp | 13 ++----------- qmljs_runtime.h | 3 +-- qv4isel_masm.cpp | 12 +++++++++--- 8 files changed, 38 insertions(+), 26 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index e63ab62d7e..2ecbee6e59 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -500,14 +500,9 @@ void __qmljs_llvm_delete_member(ExecutionContext *ctx, Value *result, Value *bas *result = __qmljs_delete_member(ctx, *base, name); } -void __qmljs_llvm_delete_property(ExecutionContext *ctx, Value *result, String *name) +void __qmljs_llvm_delete_name(ExecutionContext *ctx, Value *result, String *name) { - *result = __qmljs_delete_property(ctx, name); -} - -void __qmljs_llvm_delete_value(ExecutionContext *ctx, Value *result, Value *value) -{ - *result = __qmljs_delete_value(ctx, *value); + *result = __qmljs_delete_name(ctx, name); } } // extern "C" diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index d8871f40c5..46bd64299c 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -262,11 +262,11 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinDeleteSubscript) MOTH_BEGIN_INSTR(CallBuiltinDeleteName) - TEMP(instr.targetTempIndex) = __qmljs_delete_property(context, instr.name); + TEMP(instr.targetTempIndex) = __qmljs_delete_name(context, instr.name); MOTH_END_INSTR(CallBuiltinDeleteName) MOTH_BEGIN_INSTR(CallBuiltinDeleteValue) - TEMP(instr.targetTempIndex) = __qmljs_delete_value(context, TEMP(instr.tempIndex)); + TEMP(instr.targetTempIndex) = VM::Value::fromBoolean(false); MOTH_END_INSTR(CallBuiltinDeleteValue) MOTH_BEGIN_INSTR(CreateValue) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 5504763b31..8093c120f2 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -221,6 +221,26 @@ PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, Pro return 0; } +bool ExecutionContext::deleteProperty(String *name) +{ + for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer) { + if (ctx->withObject) { + DeclarativeEnvironment::With *w = ctx->withObject; + while (w) { + if (w->object->__hasProperty__(this, name)) + w->object->__delete__(this, name); + w = w->next; + } + } + if (ctx->activation) { + if (ctx->activation->__hasProperty__(this, name)) + ctx->activation->__delete__(this, name); + } + } + // ### throw syntax error in strict mode + return true; +} + void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) { for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer) { diff --git a/qmljs_environment.h b/qmljs_environment.h index 7a3a012b01..47376649d0 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -118,6 +118,7 @@ struct ExecutionContext PropertyDescriptor *lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp); void inplaceBitOp(Value value, String *name, BinOp op); + bool deleteProperty(String *name); inline uint argumentCount() const { return variableEnvironment->argumentCount; } inline Value argument(unsigned int index = 0) diff --git a/qmljs_objects.h b/qmljs_objects.h index 31d493a790..b54e6062d1 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -411,7 +411,7 @@ struct Object { virtual void __put__(ExecutionContext *ctx, String *name, const Value &value, bool throwException = false); virtual bool __canPut__(ExecutionContext *ctx, String *name); virtual bool __hasProperty__(ExecutionContext *ctx, String *name) const; - virtual bool __delete__(ExecutionContext *ctx, String *name, bool throwException); + virtual bool __delete__(ExecutionContext *ctx, String *name, bool throwException = false); virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc, bool throwException = false); // diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index eabceeff7f..86d1bbea09 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -195,18 +195,9 @@ Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name) return Value::fromBoolean(obj.objectValue()->__delete__(ctx, name, true)); } -Value __qmljs_delete_property(ExecutionContext *ctx, String *name) +Value __qmljs_delete_name(ExecutionContext *ctx, String *name) { - Object *obj = ctx->lexicalEnvironment->activation; - if (!obj) - obj = ctx->engine->globalObject.objectValue(); - return Value::fromBoolean(obj->__delete__(ctx, name, true)); -} - -Value __qmljs_delete_value(ExecutionContext *ctx, Value value) -{ - Q_UNUSED(value); - return __qmljs_throw_type_error(ctx); // ### throw syntax error + return Value::fromBoolean(ctx->deleteProperty(name)); } Value __qmljs_add_helper(Value left, Value right, ExecutionContext *ctx) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index ff8395af13..1831f68d60 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -177,8 +177,7 @@ Value __qmljs_not(Value value, ExecutionContext *ctx); Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index); Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name); -Value __qmljs_delete_property(ExecutionContext *ctx, String *name); -Value __qmljs_delete_value(ExecutionContext *ctx, Value value); +Value __qmljs_delete_name(ExecutionContext *ctx, String *name); Value __qmljs_typeof(Value value, ExecutionContext *ctx); void __qmljs_throw(Value value, ExecutionContext *context); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 270209a37f..f39c3b75b1 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -230,9 +230,15 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { generateFunctionCall(result, __qmljs_delete_subscript, ContextRegister, ss->base->asTemp(), ss->index->asTemp()); return; - } else { - assert(!"builtin_delete: unimplemented"); - Q_UNIMPLEMENTED(); + } else if (IR::Name *n = call->args->expr->asName()) { + generateFunctionCall(result, __qmljs_delete_name, ContextRegister, n); + return; + } else if (call->args->expr->asTemp()){ + // ### should throw in strict mode + Address dest = loadTempAddress(ScratchRegister, result); + Value v = Value::fromBoolean(false); + storeValue(v, dest); + return; } break; } -- cgit v1.2.3 From 42aa43d28908ed44485d47d450e23f08debfadf7 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 25 Nov 2012 20:53:57 +0100 Subject: Fix the worst problems with eval() Support is still not fully compliant. Especially variables declared within eval() might end up in the wrong (global) scope in some cases. Change-Id: I44d60f57b2af0b54a2759bd887fb2b3751116cd2 Reviewed-by: Erik Verbruggen --- qmljs_objects.cpp | 55 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index d19040304c..2400e5ab24 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -462,25 +462,33 @@ Value ScriptFunction::call(VM::ExecutionContext *ctx) Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc, bool strictMode) { - Value s = context->argument(0); - if (!s.isString()) - return s; + if (argc < 1) + return Value::undefinedValue(); - const QString code = context->argument(0).stringValue()->toQString(); + if (!args[0].isString()) + return args[0]; - // ### how to determine this correctly + // ### how to determine this correctly? bool directCall = true; ExecutionContext k, *ctx; if (!directCall) { + qDebug() << "!direct"; // ### } else if (strictMode) { ctx = &k; ctx->initCallContext(context, context->thisObject, this, args, argc); } else { - ctx = context; + ctx = &k; + // a clone of the surrounding context + ctx->engine = context->engine; + ctx->parent = context; + ctx->thisObject = context->thisObject; + ctx->lexicalEnvironment = context->lexicalEnvironment; + ctx->variableEnvironment = context->variableEnvironment; } - // ##### inline and do this in the correct scope + const QString code = args[0].stringValue()->toQString(); + Value result = evaluate(ctx, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode); if (strictMode) @@ -489,22 +497,6 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value return result; } -/// isNaN [15.1.2.4] -Value IsNaNFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, Value *args, int /*argc*/, bool /*strictMode*/) -{ - // TODO: see if we can generate code for this directly - const Value &v = args[0]; - return Value::fromBoolean(v.isDouble() ? std::isnan(v.doubleValue()) : false); -} - -/// isFinite [15.1.2.5] -Value IsFiniteFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, Value *args, int /*argc*/, bool /*strictMode*/) -{ - // TODO: see if we can generate code for this directly - const Value &v = args[0]; - return Value::fromBoolean(v.isDouble() ? std::isfinite(v.doubleValue()) : true); -} - Value EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, const QString &source, QQmlJS::Codegen::Mode mode) { @@ -557,6 +549,23 @@ Value EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &f } +/// isNaN [15.1.2.4] +Value IsNaNFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, Value *args, int /*argc*/, bool /*strictMode*/) +{ + // TODO: see if we can generate code for this directly + const Value &v = args[0]; + return Value::fromBoolean(v.isDouble() ? std::isnan(v.doubleValue()) : false); +} + +/// isFinite [15.1.2.5] +Value IsFiniteFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, Value *args, int /*argc*/, bool /*strictMode*/) +{ + // TODO: see if we can generate code for this directly + const Value &v = args[0]; + return Value::fromBoolean(v.isDouble() ? std::isfinite(v.doubleValue()) : true); +} + + Value RegExpObject::__get__(ExecutionContext *ctx, String *name) { QString n = name->toQString(); -- cgit v1.2.3 From 215bd586e6f9c3e75111271b332b2182a163db3d Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 26 Nov 2012 14:38:14 +0100 Subject: Fix: do not rune codegen when there is no program. Happens in valid cases like an empty JS file (or one only containing comments). Change-Id: I553f57503b46da29276b06532a14e2a74395d84c Reviewed-by: Lars Knoll --- main.cpp | 1 + qmljs_objects.cpp | 17 ++++++++++++----- qv4codegen.cpp | 2 ++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/main.cpp b/main.cpp index f703cbcf00..a228c9baa2 100644 --- a/main.cpp +++ b/main.cpp @@ -165,6 +165,7 @@ int compile(const QString &fileName, const QString &source, QQmlJS::LLVMOutputTy Program *program = AST::cast(parser.rootNode()); Codegen cg; + // FIXME: if the program is empty, we should we generate an empty %entry, or give an error? /*IR::Function *globalCode =*/ cg(program, &module); int (*exec)(void *) = outputType == LLVMOutputJit ? executeLLVMCode : 0; diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 2400e5ab24..88a7c953be 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -522,14 +522,21 @@ Value EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &f if (parsed) { using namespace AST; Program *program = AST::cast(parser.rootNode()); + if (!program) { + // if parsing was successful, and we have no program, then + // we're done...: + return QQmlJS::VM::Value::undefinedValue(); + } Codegen cg; globalCode = cg(program, &module, mode); - - foreach (IR::Function *function, module.functions) { - EvalInstructionSelection *isel = ctx->engine->iselFactory->create(vm); - isel->run(function); - delete isel; + if (globalCode) { + // only generate other functions if global code generation succeeded. + foreach (IR::Function *function, module.functions) { + EvalInstructionSelection *isel = ctx->engine->iselFactory->create(vm); + isel->run(function); + delete isel; + } } } diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 33bb064996..cd4cbebb0a 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -307,6 +307,8 @@ Codegen::Codegen() IR::Function *Codegen::operator()(Program *node, IR::Module *module, Mode mode) { + assert(node); + _module = module; _env = 0; -- cgit v1.2.3 From 102aff16a896e2e237a00843731e84ef3d824b25 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 26 Nov 2012 22:49:35 +0100 Subject: Fix constant value parameter passing on ia32 Fix the order of tag and value when pushing constants onto the stack, given that the stack grows in reverse order. This fixes crypto.js and all sorts of other tests. Change-Id: I1f6aa546e5453f890db582283fd4da609292f22b Reviewed-by: Lars Knoll --- qv4isel_masm_p.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 8fee0a49ab..15ba5b0ad0 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -371,10 +371,10 @@ private: move(TrustedImm64(value.val), ScratchRegister); push(ScratchRegister); #else - move(TrustedImm32(value.int_32), ScratchRegister); - push(ScratchRegister); move(TrustedImm32(value.tag), ScratchRegister); push(ScratchRegister); + move(TrustedImm32(value.int_32), ScratchRegister); + push(ScratchRegister); #endif } -- cgit v1.2.3 From b32c10a8d36aef52c38ad8b51cef33c100c8df9e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 26 Nov 2012 23:26:39 +0100 Subject: Correctly instantiate variables in the local scope This fixes cases where eval() would create variables in the wrong scope. Change-Id: Ie93ec2d1fb125e588c1b6ffa2ca8ca4b6e3112c9 Reviewed-by: Erik Verbruggen --- qmljs_environment.cpp | 50 +++++++++++++++++++------------------------------- qmljs_environment.h | 8 ++------ qmljs_objects.cpp | 8 +++++--- qmljs_runtime.cpp | 4 ++++ qmljs_runtime.h | 1 + qv4codegen.cpp | 25 ++++++++++++++++++++----- qv4ir.cpp | 2 ++ qv4ir_p.h | 3 ++- qv4isel_masm.cpp | 12 ++++++++++++ 9 files changed, 67 insertions(+), 46 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 8093c120f2..0a8fadaf84 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -100,20 +100,22 @@ bool DeclarativeEnvironment::hasBinding(String *name) const if (__qmljs_string_equal(formals[i], name)) return true; } - if (!deletableLocals) - return false; - return deletableLocals->contains(name->toQString()); + return false; } -void DeclarativeEnvironment::createMutableBinding(String *name, bool deletable) +void DeclarativeEnvironment::createMutableBinding(ExecutionContext *ctx, String *name, bool deletable) { - // all non deletable vars should get created at compile time - assert(deletable); - assert(!hasBinding(name)); - - if (!deletableLocals) - deletableLocals = new QHash(); - deletableLocals->insert(name->toQString(), Value::undefinedValue()); + if (activation) { + if (activation->__hasProperty__(ctx, name)) + return; + PropertyDescriptor desc; + desc.value = Value::undefinedValue(); + desc.type = PropertyDescriptor::Data; + desc.configurable = deletable ? PropertyDescriptor::Set : PropertyDescriptor::Unset; + desc.writable = PropertyDescriptor::Set; + desc.enumberable = PropertyDescriptor::Set; + activation->__defineOwnProperty__(ctx, name, &desc, true); + } } void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool strict) @@ -133,12 +135,6 @@ void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool s return; } } - assert(deletableLocals); - QHash::iterator it = deletableLocals->find(name->toQString()); - if (it != deletableLocals->end()) { - *it = value; - return; - } assert(false); } @@ -154,24 +150,16 @@ Value DeclarativeEnvironment::getBindingValue(String *name, bool strict) const if (__qmljs_string_equal(formals[i], name)) return arguments[i]; } - assert(deletableLocals); - QHash::const_iterator it = deletableLocals->find(name->toQString()); - if (it != deletableLocals->end()) - return *it; - assert(false); } -bool DeclarativeEnvironment::deleteBinding(String *name) +bool DeclarativeEnvironment::deleteBinding(ExecutionContext *ctx, String *name) { - if (deletableLocals) { - QHash::iterator it = deletableLocals->find(name->toQString()); - if (it != deletableLocals->end()) { - deletableLocals->erase(it); - return true; - } - } - return !hasBinding(name); + if (activation) + activation->__delete__(ctx, name, false); + + // ### throw in strict mode? + return false; } void DeclarativeEnvironment::pushWithObject(Object *with) diff --git a/qmljs_environment.h b/qmljs_environment.h index 47376649d0..e631ec79a6 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -73,19 +73,15 @@ struct DeclarativeEnvironment With *next; } *withObject; - // these get used for createMutableBinding(..., true). - // the only place this is being used is eval(...) - QHash *deletableLocals; DeclarativeEnvironment(ExecutionEngine *e); DeclarativeEnvironment(FunctionObject *f, Value *args, uint argc); - ~DeclarativeEnvironment() { delete deletableLocals; } bool hasBinding(String *name) const; - void createMutableBinding(String *name, bool deletable); + void createMutableBinding(ExecutionContext *ctx, String *name, bool deletable); void setMutableBinding(String *name, Value value, bool strict); Value getBindingValue(String *name, bool strict) const; - bool deleteBinding(String *name); + bool deleteBinding(ExecutionContext *ctx, String *name); // ### needs a bit of work in exception handlers void pushWithObject(Object *with); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 88a7c953be..a7e2f8068b 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -253,8 +253,9 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property if (!extensible) goto reject; // clause 4 - *current = *desc; - current->fullyPopulated(); + PropertyDescriptor *pd = members->insert(name); + *pd = *desc; + pd->fullyPopulated(); return true; } @@ -270,7 +271,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property if (!current->isConfigurable()) { if (desc->isConfigurable()) goto reject; - if (desc->enumberable != PropertyDescriptor::Unset && desc->enumberable != current->enumberable) + if (desc->enumberable != PropertyDescriptor::Undefined && desc->enumberable != current->enumberable) goto reject; } @@ -313,6 +314,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property *current += *desc; return true; reject: + qDebug() << "___put__ rejected" << name->toQString(); if (throwException) __qmljs_throw_type_error(ctx); return false; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 86d1bbea09..1491b01446 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -865,6 +865,10 @@ void __qmljs_builtin_pop_with(ExecutionContext *ctx) ctx->lexicalEnvironment->popWithObject(); } +void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) +{ + ctx->lexicalEnvironment->createMutableBinding(ctx, name, deletable); +} } // extern "C" diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 1831f68d60..a02713d941 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -103,6 +103,7 @@ Value __qmljs_builtin_typeof(Value val, ExecutionContext *context); void __qmljs_builtin_throw(Value val, ExecutionContext *context); void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx); void __qmljs_builtin_pop_with(ExecutionContext *ctx); +void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name); // constructors Value __qmljs_init_closure(IR::Function *clos, ExecutionContext *ctx); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index cd4cbebb0a..e605e2493a 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1517,15 +1517,30 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, function->maxNumberOfArguments = _env->maxNumberOfArguments; // variables in global code are properties of the global context object, not locals as with other functions. - for (int i = 0; i < _env->vars.size(); ++i) { - const QString &local = _env->vars.at(i); - if (!_env->parent) { - entryBlock->MOVE(entryBlock->NAME(local, 0, 0), entryBlock->CONST(IR::UndefinedType, 0)); - } else { + if (_mode == FunctionCode) { + for (int i = 0; i < _env->vars.size(); ++i) { + const QString &local = _env->vars.at(i); function->LOCAL(local); unsigned t = entryBlock->newTemp(); assert(t == unsigned(i)); } + } else { + IR::ExprList *args = 0; + for (int i = 0; i < _env->vars.size(); ++i) { + const QString &local = _env->vars.at(i); + IR::ExprList *next = function->New(); + next->expr = entryBlock->NAME(local, 0, 0); + next->next = args; + args = next; + } + if (args) { + IR::ExprList *next = function->New(); + next->expr = entryBlock->CONST(IR::BoolType, mode == EvalCode); + next->next = args; + args = next; + + entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(IR::Name::builtin_declare_vars, 0, 0), args)); + } } unsigned returnAddress = entryBlock->newTemp(); diff --git a/qv4ir.cpp b/qv4ir.cpp index 57f6de958f..7646b371d0 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -238,6 +238,8 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_push_with"; case IR::Name::builtin_pop_with: return "builtin_pop_with"; + case IR::Name::builtin_declare_vars: + return "builtin_declare_vars"; } return "builtin_(###FIXME)"; }; diff --git a/qv4ir_p.h b/qv4ir_p.h index b0be0f9bb2..740ca9b3f9 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -278,7 +278,8 @@ struct Name: Expr { builtin_foreach_iterator_object, builtin_foreach_next_property_name, builtin_push_with, - builtin_pop_with + builtin_pop_with, + builtin_declare_vars }; const QString *id; diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index f39c3b75b1..a0262c8079 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -279,6 +279,18 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu case IR::Name::builtin_pop_with: generateFunctionCall(Void, __qmljs_builtin_pop_with, ContextRegister); break; + case IR::Name::builtin_declare_vars: { + if (!call->args) + return; + IR::Const *deletable = call->args->expr->asConst(); + assert(deletable->type == IR::BoolType); + for (IR::ExprList *it = call->args->next; it; it = it->next) { + IR::Name *arg = it->expr->asName(); + assert(arg != 0); + generateFunctionCall(Void, __qmljs_builtin_declare_var, ContextRegister, + TrustedImm32(deletable->value != 0), identifier(*arg->id)); + } + } } } -- cgit v1.2.3 From 3125086ab8e9fe139a9870c1d23b248f04dcb4a3 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 27 Nov 2012 12:17:19 +0100 Subject: Add more built-ins to the interpreter. Change-Id: I6a1656a8a2042b0a02d6e3bb8e59c9db52f6fd5d Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 11 +++++++- moth/qv4isel_moth.cpp | 71 ++++++++++++++++++++++++++++++++++---------------- moth/qv4vme_moth.cpp | 17 ++++++++++++ 3 files changed, 75 insertions(+), 24 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 6fba81496e..c3be3a2a08 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -28,6 +28,7 @@ F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ F(CallBuiltinDeleteName, callBuiltinDeleteName) \ F(CallBuiltinDeleteValue, callBuiltinDeleteValue) \ + F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ F(CreateValue, createValue) \ F(CreateProperty, createProperty) \ F(CreateActivationProperty, createActivationProperty) \ @@ -179,7 +180,9 @@ union Instr builtin_delete_exception_handler, builtin_get_exception, builtin_foreach_iterator_object, - builtin_foreach_next_property_name + builtin_foreach_next_property_name, + builtin_push_with, + builtin_pop_with } builtin; quint32 argc; quint32 args; @@ -207,6 +210,11 @@ union Instr int tempIndex; int targetTempIndex; }; + struct instr_callBuiltinDeclareVar { + MOTH_INSTR_HEADER + bool isDeletable; + VM::String *varName; + }; struct instr_createValue { MOTH_INSTR_HEADER int func; @@ -305,6 +313,7 @@ union Instr instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; instr_callBuiltinDeleteName callBuiltinDeleteName; instr_callBuiltinDeleteValue callBuiltinDeleteValue; + instr_callBuiltinDeclareVar callBuiltinDeclareVar; instr_createValue createValue; instr_createProperty createProperty; instr_createActivationProperty createActivationProperty; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 7f4344ba3c..df2f873298 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -264,6 +264,32 @@ void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempInd addInstruction(call); } break; + case IR::Name::builtin_delete: { + if (IR::Member *m = c->args->expr->asMember()) { + Instruction::CallBuiltinDeleteMember call; + call.base = m->base->asTemp()->index; + call.member = _engine->newString(*m->name); + call.targetTempIndex = targetTempIndex; + addInstruction(call); + } else if (IR::Subscript *ss = c->args->expr->asSubscript()) { + Instruction::CallBuiltinDeleteSubscript call; + call.base = m->base->asTemp()->index; + call.index = ss->index->asTemp()->index; + call.targetTempIndex = targetTempIndex; + addInstruction(call); + } else if (IR::Name *n = c->args->expr->asName()) { + Instruction::CallBuiltinDeleteName call; + call.name = _engine->newString(*n->id); + call.targetTempIndex = targetTempIndex; + addInstruction(call); + } else { + Instruction::CallBuiltinDeleteValue call; + call.tempIndex = c->args->expr->asTemp()->index; + call.targetTempIndex = targetTempIndex; + addInstruction(call); + } + } break; + case IR::Name::builtin_throw: { IR::Temp *arg = c->args->expr->asTemp(); assert(arg != 0); @@ -312,29 +338,28 @@ void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempInd addInstruction(call); } break; - case IR::Name::builtin_delete: { - if (IR::Member *m = c->args->expr->asMember()) { - Instruction::CallBuiltinDeleteMember call; - call.base = m->base->asTemp()->index; - call.member = _engine->newString(*m->name); - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } else if (IR::Subscript *ss = c->args->expr->asSubscript()) { - Instruction::CallBuiltinDeleteSubscript call; - call.base = m->base->asTemp()->index; - call.index = ss->index->asTemp()->index; - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } else if (IR::Name *n = c->args->expr->asName()) { - Instruction::CallBuiltinDeleteName call; - call.name = _engine->newString(*n->id); - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } else { - Instruction::CallBuiltinDeleteValue call; - call.tempIndex = c->args->expr->asTemp()->index; - call.targetTempIndex = targetTempIndex; - addInstruction(call); + case IR::Name::builtin_push_with: { + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_push_with; + prepareCallArgs(c->args, call.argc, call.args); + assert(call.argc == 1); + addInstruction(call); + } break; + + case IR::Name::builtin_pop_with: { + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_pop_with; + addInstruction(call); + } break; + + case IR::Name::builtin_declare_vars: if (c->args) { + IR::Const *deletable = c->args->expr->asConst(); + assert(deletable->type == IR::BoolType); + const bool isDeletable = deletable->value != 0; + for (IR::ExprList *it = c->args->next; it; it = it->next) { + Instruction::CallBuiltinDeclareVar call; + call.isDeletable = isDeletable; + call.varName = _engine->newString(*it->expr->asName()->id); } } break; diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 46bd64299c..5a0ba30b25 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -212,10 +212,12 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co void *buf; switch (instr.builtin) { case Instr::instr_callBuiltin::builtin_typeof: + Q_ASSERT(instr.argc == 1); TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof(args[0], context); break; case Instr::instr_callBuiltin::builtin_throw: TRACE(builtin_throw, "Throwing now...%s", ""); + Q_ASSERT(instr.argc == 1); __qmljs_builtin_throw(args[0], context); break; case Instr::instr_callBuiltin::builtin_create_exception_handler: { @@ -245,11 +247,22 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co TEMP(instr.targetTempIndex) = __qmljs_get_exception(context); break; case Instr::instr_callBuiltin::builtin_foreach_iterator_object: + Q_ASSERT(instr.argc == 1); TEMP(instr.targetTempIndex) = __qmljs_foreach_iterator_object(args[0], context); break; case Instr::instr_callBuiltin::builtin_foreach_next_property_name: + Q_ASSERT(instr.argc == 1); TEMP(instr.targetTempIndex) = __qmljs_foreach_next_property_name(args[0]); break; + case Instr::instr_callBuiltin::builtin_push_with: + Q_ASSERT(instr.argc == 1); + __qmljs_builtin_push_with(args[0], context); + break; + case Instr::instr_callBuiltin::builtin_pop_with: + __qmljs_builtin_pop_with(context); + break; + default: + Q_UNREACHABLE(); } MOTH_END_INSTR(CallBuiltin) @@ -269,6 +282,10 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co TEMP(instr.targetTempIndex) = VM::Value::fromBoolean(false); MOTH_END_INSTR(CallBuiltinDeleteValue) + MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) + __qmljs_builtin_declare_var(context, instr.isDeletable, instr.varName); + MOTH_END_INSTR(CallBuiltinDeleteValue) + MOTH_BEGIN_INSTR(CreateValue) VM::Value *args = stack.data() + instr.args; TEMP(instr.targetTempIndex) = __qmljs_construct_value(context, TEMP(instr.func), args, instr.argc); -- cgit v1.2.3 From 1f2dc8856e04047648163445871b891fa898a3c1 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 27 Nov 2012 16:46:04 +0100 Subject: Check for strict mode, and store it. Change-Id: I88e48a982eb6d4041aed085de0420d98cf96d406 Reviewed-by: Lars Knoll --- qv4codegen.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4codegen_p.h | 5 ++++- qv4ir_p.h | 7 +++++-- 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index e605e2493a..0f3cedb55c 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -215,9 +215,48 @@ protected: _env = _envStack.isEmpty() ? 0 : _envStack.top(); } + void checkDirectivePrologue(SourceElements *ast) + { + for (SourceElements *it = ast; it; it = it->next) { + if (StatementSourceElement *stmt = cast(it->element)) { + if (ExpressionStatement *expr = cast(stmt->statement)) { + if (StringLiteral *strLit = cast(expr->expression)) { + if (strLit->value == QLatin1String("use strict")) { + _env->isStrict = true; + } else { + // TODO: give a warning. + } + continue; + } + } + } + + break; + } + } + + void checkName(const QStringRef &name, const SourceLocation &loc) + { + if (_env->isStrict) { + if (name == QLatin1String("implements") + || name == QLatin1String("interface") + || name == QLatin1String("let") + || name == QLatin1String("package") + || name == QLatin1String("private") + || name == QLatin1String("protected") + || name == QLatin1String("public") + || name == QLatin1String("static") + || name == QLatin1String("yield")) { + // TODO: error! + qDebug("TODO: give a proper error message @%u:%u", loc.startLine, loc.startColumn); + } + } + } + virtual bool visit(Program *ast) { enterEnvironment(ast); + checkDirectivePrologue(ast->elements); return true; } @@ -253,10 +292,17 @@ protected: virtual bool visit(VariableDeclaration *ast) { + checkName(ast->name, ast->identifierToken); _env->enter(ast->name.toString()); return true; } + virtual bool visit(IdentifierExpression *ast) + { + checkName(ast->name, ast->identifierToken); + return true; + } + virtual bool visit(FunctionExpression *ast) { if (_env) { @@ -264,6 +310,8 @@ protected: _env->enter(ast->name.toString()); } enterEnvironment(ast); + if (ast->body) + checkDirectivePrologue(ast->body->elements); return true; } @@ -278,6 +326,8 @@ protected: _env->hasNestedFunctions = true; _env->enter(ast->name.toString()); enterEnvironment(ast); + if (ast->body) + checkDirectivePrologue(ast->body->elements); return true; } @@ -286,6 +336,17 @@ protected: leaveEnvironment(); } + virtual bool visit(WithStatement *ast) + { + if (_env->isStrict) { + // TODO: give a proper error message + qDebug("TODO: give proper error message @%u:%u", ast->withToken.startLine, ast->withToken.startColumn); + return false; + } + + return true; + } + Codegen *_cg; Environment *_env; QStack _envStack; @@ -1515,6 +1576,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, function->hasDirectEval = _env->hasDirectEval; function->hasNestedFunctions = _env->hasNestedFunctions; function->maxNumberOfArguments = _env->maxNumberOfArguments; + function->isStrict = _env->isStrict; // variables in global code are properties of the global context object, not locals as with other functions. if (_mode == FunctionCode) { diff --git a/qv4codegen_p.h b/qv4codegen_p.h index c830345331..4622d25452 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -108,12 +108,15 @@ protected: int maxNumberOfArguments; bool hasDirectEval; bool hasNestedFunctions; + bool isStrict; Environment(Environment *parent) : parent(parent) , maxNumberOfArguments(0) , hasDirectEval(false) - , hasNestedFunctions(false) {} + , hasNestedFunctions(false) + , isStrict(false) + {} int findMember(const QString &name) const { diff --git a/qv4ir_p.h b/qv4ir_p.h index 740ca9b3f9..ee91807971 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -602,9 +602,11 @@ struct Function { const uchar *codeData; JSC::MacroAssemblerCodeRef codeRef; + int insideWith; + bool hasDirectEval: 1; bool hasNestedFunctions: 1; - int insideWith; + bool isStrict: 1; template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } @@ -615,9 +617,10 @@ struct Function { , maxNumberOfArguments(0) , code(0) , codeData(0) + , insideWith(0) , hasDirectEval(false) , hasNestedFunctions(false) - , insideWith(0) + , isStrict(false) { this->name = newString(name); } ~Function(); -- cgit v1.2.3 From b320af12ebd25de020537a08c39fbd6c7f6b9aad Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 27 Nov 2012 22:42:20 +0100 Subject: Implement the first parts of strict mode. Tested with eval() which now obeys strict mode semantics. Change-Id: Ib3c7f31047e43c1ef0fa74261f23ec4f2ea4244f Reviewed-by: Erik Verbruggen --- main.cpp | 6 +++++- qmljs_environment.cpp | 25 ++++++++++++++----------- qmljs_environment.h | 1 + qmljs_objects.cpp | 33 ++++++++++++++++++++------------- qmljs_objects.h | 18 ++++++++++-------- qv4codegen_p.h | 5 ++++- 6 files changed, 54 insertions(+), 34 deletions(-) diff --git a/main.cpp b/main.cpp index a228c9baa2..bd1bcea89e 100644 --- a/main.cpp +++ b/main.cpp @@ -319,7 +319,11 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - QQmlJS::VM::Value result = QQmlJS::VM::EvalFunction::evaluate(vm.rootContext, fn, code, QQmlJS::Codegen::GlobalCode); + QQmlJS::IR::Function *f = QQmlJS::VM::EvalFunction::parseSource(vm.rootContext, fn, code, QQmlJS::Codegen::GlobalCode); + if (!f) + continue; + ctx->lexicalEnvironment->strictMode = f->isStrict; + QQmlJS::VM::Value result = f->code(ctx, f->codeData); if (!result.isUndefined()) { if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 0a8fadaf84..140b5ac933 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -58,12 +58,14 @@ DeclarativeEnvironment::DeclarativeEnvironment(ExecutionEngine *e) varCount = 0; activation = 0; withObject = 0; + strictMode = false; } DeclarativeEnvironment::DeclarativeEnvironment(FunctionObject *f, Value *args, uint argc) { outer = f->scope; engine = outer->engine; + strictMode = f->strictMode; formals = f->formalParameterList; formalCount = f->formalParameterCount; @@ -105,17 +107,18 @@ bool DeclarativeEnvironment::hasBinding(String *name) const void DeclarativeEnvironment::createMutableBinding(ExecutionContext *ctx, String *name, bool deletable) { - if (activation) { - if (activation->__hasProperty__(ctx, name)) - return; - PropertyDescriptor desc; - desc.value = Value::undefinedValue(); - desc.type = PropertyDescriptor::Data; - desc.configurable = deletable ? PropertyDescriptor::Set : PropertyDescriptor::Unset; - desc.writable = PropertyDescriptor::Set; - desc.enumberable = PropertyDescriptor::Set; - activation->__defineOwnProperty__(ctx, name, &desc, true); - } + if (!activation) + activation = engine->newActivationObject(this); + + if (activation->__hasProperty__(ctx, name)) + return; + PropertyDescriptor desc; + desc.value = Value::undefinedValue(); + desc.type = PropertyDescriptor::Data; + desc.configurable = deletable ? PropertyDescriptor::Set : PropertyDescriptor::Unset; + desc.writable = PropertyDescriptor::Set; + desc.enumberable = PropertyDescriptor::Set; + activation->__defineOwnProperty__(ctx, name, &desc, true); } void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool strict) diff --git a/qmljs_environment.h b/qmljs_environment.h index e631ec79a6..f8339dec7e 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -66,6 +66,7 @@ struct DeclarativeEnvironment unsigned int formalCount; String **vars; unsigned int varCount; + bool strictMode; Object *activation; struct With { diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index a7e2f8068b..a43b576c05 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -396,7 +396,7 @@ Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc return result; } -Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode) +Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *args, int argc) { ExecutionContext k; ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; @@ -433,6 +433,7 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, IR::Function *function) if (function->name) name = scope->engine->identifier(*function->name); needsActivation = function->needsActivation(); + strictMode = function->isStrict; formalParameterCount = function->formals.size(); if (formalParameterCount) { formalParameterList = new String*[formalParameterCount]; @@ -462,7 +463,7 @@ Value ScriptFunction::call(VM::ExecutionContext *ctx) } -Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc, bool strictMode) +Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) { if (argc < 1) return Value::undefinedValue(); @@ -473,11 +474,18 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value // ### how to determine this correctly? bool directCall = true; + const QString code = args[0].stringValue()->toQString(); + QQmlJS::IR::Function *f = parseSource(context, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode); + if (!f) + return Value::undefinedValue(); + + bool strict = f->isStrict || context->lexicalEnvironment->strictMode; + ExecutionContext k, *ctx; if (!directCall) { qDebug() << "!direct"; // ### - } else if (strictMode) { + } else if (strict) { ctx = &k; ctx->initCallContext(context, context->thisObject, this, args, argc); } else { @@ -489,18 +497,18 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value ctx->lexicalEnvironment = context->lexicalEnvironment; ctx->variableEnvironment = context->variableEnvironment; } - const QString code = args[0].stringValue()->toQString(); - Value result = evaluate(ctx, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode); + Value result = f->code(ctx, f->codeData); - if (strictMode) + if (strict) ctx->leaveCallContext(); return result; } -Value EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, - const QString &source, QQmlJS::Codegen::Mode mode) +QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, + const QString &fileName, const QString &source, + QQmlJS::Codegen::Mode mode) { using namespace QQmlJS; @@ -527,7 +535,7 @@ Value EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &f if (!program) { // if parsing was successful, and we have no program, then // we're done...: - return QQmlJS::VM::Value::undefinedValue(); + return 0; } Codegen cg; @@ -553,13 +561,12 @@ Value EvalFunction::evaluate(QQmlJS::VM::ExecutionContext *ctx, const QString &f foreach (const QString *local, globalCode->locals) { ctx->variableEnvironment->activation->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); } - - return globalCode->code(ctx, globalCode->codeData); + return globalCode; } /// isNaN [15.1.2.4] -Value IsNaNFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, Value *args, int /*argc*/, bool /*strictMode*/) +Value IsNaNFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, Value *args, int /*argc*/) { // TODO: see if we can generate code for this directly const Value &v = args[0]; @@ -567,7 +574,7 @@ Value IsNaNFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, } /// isFinite [15.1.2.5] -Value IsFiniteFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, Value *args, int /*argc*/, bool /*strictMode*/) +Value IsFiniteFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, Value *args, int /*argc*/) { // TODO: see if we can generate code for this directly const Value &v = args[0]; diff --git a/qmljs_objects.h b/qmljs_objects.h index b54e6062d1..55907e10e0 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -482,6 +482,7 @@ struct FunctionObject: Object { String **varList; unsigned int varCount; bool needsActivation; + bool strictMode; FunctionObject(ExecutionContext *scope) : scope(scope->variableEnvironment) @@ -490,14 +491,15 @@ struct FunctionObject: Object { , formalParameterCount(0) , varList(0) , varCount(0) - , needsActivation(false) {} + , needsActivation(false) + , strictMode(false) {} virtual QString className() { return QStringLiteral("Function"); } virtual FunctionObject *asFunctionObject() { return this; } virtual bool hasInstance(ExecutionContext *ctx, const Value &value); Value construct(ExecutionContext *context, Value *args, int argc); - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode = false); + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); protected: virtual Value call(ExecutionContext *ctx); @@ -526,25 +528,25 @@ struct EvalFunction : FunctionObject { EvalFunction(ExecutionContext *scope): FunctionObject(scope) {} - static Value evaluate(QQmlJS::VM::ExecutionContext *ctx, - const QString &fileName, const QString &source, - QQmlJS::Codegen::Mode mode); + static QQmlJS::IR::Function *parseSource(QQmlJS::VM::ExecutionContext *ctx, + const QString &fileName, const QString &source, + QQmlJS::Codegen::Mode mode); - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode = false); + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); }; struct IsNaNFunction: FunctionObject { IsNaNFunction(ExecutionContext *scope): FunctionObject(scope) {} - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode = false); + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); }; struct IsFiniteFunction: FunctionObject { IsFiniteFunction(ExecutionContext *scope): FunctionObject(scope) {} - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool strictMode = false); + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); }; struct RegExpObject: Object { diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 4622d25452..4952c690b8 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -116,7 +116,10 @@ protected: , hasDirectEval(false) , hasNestedFunctions(false) , isStrict(false) - {} + { + if (parent && parent->isStrict) + isStrict = true; + } int findMember(const QString &name) const { -- cgit v1.2.3 From 1dbad2e68953f9e2d30bce44b3d02dd65c273c9a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 27 Nov 2012 22:59:51 +0100 Subject: Remove unused methods Change-Id: I4b0553b1fb6522131a05a66f8cba44bce5192e55 Reviewed-by: Erik Verbruggen --- qmljs_runtime.cpp | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 1491b01446..7182ad07c1 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -546,32 +546,6 @@ void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Val object.objectValue()->__put__(ctx, name, value, /*flags*/ 0); } -void __qmljs_set_property_boolean(ExecutionContext *ctx, Value *object, String *name, bool b) -{ - Value value = Value::fromBoolean(b); - object->objectValue()->__put__(ctx, name, value, /*flag*/ 0); -} - -void __qmljs_set_property_number(ExecutionContext *ctx, Value *object, String *name, double number) -{ - Q_UNUSED(ctx); - Value value = Value::fromDouble(number); - object->objectValue()->__put__(ctx, name, value, /*flag*/ 0); -} - -void __qmljs_set_property_string(ExecutionContext *ctx, Value *object, String *name, String *s) -{ - Q_UNUSED(ctx); - Value value = Value::fromString(s); - object->objectValue()->__put__(ctx, name, value, /*flag*/ 0); -} - -void __qmljs_set_property_closure(ExecutionContext *ctx, Value *object, String *name, IR::Function *function) -{ - Value value = __qmljs_init_closure(function, ctx); - object->objectValue()->__put__(ctx, name, value, /*flag*/ 0); -} - Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) { if (index.isNumber()) { -- cgit v1.2.3 From 94a344c01a62fe32ffbc0f04cefe8daabcfab7a1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 27 Nov 2012 23:23:04 +0100 Subject: Obey strict mode for property getters and setters Change-Id: I6f51cd72c2607989c55373dfee53130381f5ef75 Reviewed-by: Erik Verbruggen --- qmljs_environment.cpp | 7 ++++--- qmljs_objects.cpp | 14 +++++++------- qmljs_objects.h | 6 +++--- qmljs_runtime.cpp | 6 +++--- qv4ecmaobjects.cpp | 2 +- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 140b5ac933..299ffffc4a 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -118,7 +118,7 @@ void DeclarativeEnvironment::createMutableBinding(ExecutionContext *ctx, String desc.configurable = deletable ? PropertyDescriptor::Set : PropertyDescriptor::Unset; desc.writable = PropertyDescriptor::Set; desc.enumberable = PropertyDescriptor::Set; - activation->__defineOwnProperty__(ctx, name, &desc, true); + activation->__defineOwnProperty__(ctx, name, &desc); } void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool strict) @@ -159,9 +159,10 @@ Value DeclarativeEnvironment::getBindingValue(String *name, bool strict) const bool DeclarativeEnvironment::deleteBinding(ExecutionContext *ctx, String *name) { if (activation) - activation->__delete__(ctx, name, false); + activation->__delete__(ctx, name); - // ### throw in strict mode? + if (ctx->lexicalEnvironment->strictMode) + __qmljs_throw_type_error(ctx); return false; } diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index a43b576c05..35bea07f8c 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -165,7 +165,7 @@ bool Object::__canPut__(ExecutionContext *ctx, String *name) } // Section 8.12.5 -void Object::__put__(ExecutionContext *ctx, String *name, const Value &value, bool throwException) +void Object::__put__(ExecutionContext *ctx, String *name, const Value &value) { // clause 1 if (!__canPut__(ctx, name)) @@ -183,7 +183,7 @@ void Object::__put__(ExecutionContext *ctx, String *name, const Value &value, bo // ### to simplify and speed up we should expand the relevant parts here (clauses 6,7,9,10,12,13) PropertyDescriptor desc = PropertyDescriptor::fromValue(value); - __defineOwnProperty__(ctx, name, &desc, throwException); + __defineOwnProperty__(ctx, name, &desc); return; } @@ -210,7 +210,7 @@ void Object::__put__(ExecutionContext *ctx, String *name, const Value &value, bo } reject: - if (throwException) + if (ctx->lexicalEnvironment->strictMode) __qmljs_throw_type_error(ctx); } @@ -224,7 +224,7 @@ bool Object::__hasProperty__(ExecutionContext *ctx, String *name) const } // Section 8.12.7 -bool Object::__delete__(ExecutionContext *ctx, String *name, bool throwException) +bool Object::__delete__(ExecutionContext *ctx, String *name) { if (members) { if (PropertyTableEntry *entry = members->findEntry(name)) { @@ -232,7 +232,7 @@ bool Object::__delete__(ExecutionContext *ctx, String *name, bool throwException members->remove(entry); return true; } - if (throwException) + if (ctx->lexicalEnvironment->strictMode) __qmljs_throw_type_error(ctx); return false; } @@ -241,7 +241,7 @@ bool Object::__delete__(ExecutionContext *ctx, String *name, bool throwException } // Section 8.12.9 -bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc, bool throwException) +bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc) { if (!members) members = new PropertyTable(); @@ -315,7 +315,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property return true; reject: qDebug() << "___put__ rejected" << name->toQString(); - if (throwException) + if (ctx->lexicalEnvironment->strictMode) __qmljs_throw_type_error(ctx); return false; } diff --git a/qmljs_objects.h b/qmljs_objects.h index 55907e10e0..45e301c56e 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -408,11 +408,11 @@ struct Object { virtual Value __get__(ExecutionContext *ctx, String *name); virtual PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); - virtual void __put__(ExecutionContext *ctx, String *name, const Value &value, bool throwException = false); + virtual void __put__(ExecutionContext *ctx, String *name, const Value &value); virtual bool __canPut__(ExecutionContext *ctx, String *name); virtual bool __hasProperty__(ExecutionContext *ctx, String *name) const; - virtual bool __delete__(ExecutionContext *ctx, String *name, bool throwException = false); - virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc, bool throwException = false); + virtual bool __delete__(ExecutionContext *ctx, String *name); + virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc); // // helpers diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 7182ad07c1..a6fd7862bf 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -192,7 +192,7 @@ Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index) Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name) { Value obj = base.toObject(ctx); - return Value::fromBoolean(obj.objectValue()->__delete__(ctx, name, true)); + return Value::fromBoolean(obj.objectValue()->__delete__(ctx, name)); } Value __qmljs_delete_name(ExecutionContext *ctx, String *name) @@ -543,7 +543,7 @@ Value __qmljs_new_string_object(ExecutionContext *ctx, String *string) void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Value value) { - object.objectValue()->__put__(ctx, name, value, /*flags*/ 0); + object.objectValue()->__put__(ctx, name, value); } Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) @@ -583,7 +583,7 @@ void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value if (! object.isObject()) object = __qmljs_to_object(object, ctx); - object.objectValue()->__put__(ctx, name, value, /*flags*/ 0); + object.objectValue()->__put__(ctx, name, value); } Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 01c0d824fe..67ef7aa8a5 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1414,7 +1414,7 @@ Value ArrayPrototype::method_pop(ExecutionContext *ctx) if (r2) { String *r6 = Value::fromDouble(r2 - 1).toString(ctx); Value r7 = self.property(ctx, r6); - self.objectValue()->__delete__(ctx, r6, 0); + self.objectValue()->__delete__(ctx, r6); self.objectValue()->__put__(ctx, ctx->engine->id_length, Value::fromDouble(2 - 1)); return r7; } -- cgit v1.2.3 From 61460c785907049567120e07dcadd714fff93cdb Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 28 Nov 2012 00:12:33 +0100 Subject: Fix a bug in Object::__put__() The method was always throwing in strict mode, due to a missing return statement. Change-Id: I85e44f8067d1f2aea76d03e42abf31a0d5a2d180 Reviewed-by: Erik Verbruggen --- qmljs_objects.cpp | 3 ++- qmljs_objects.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 35bea07f8c..a7d5d20b86 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -165,7 +165,7 @@ bool Object::__canPut__(ExecutionContext *ctx, String *name) } // Section 8.12.5 -void Object::__put__(ExecutionContext *ctx, String *name, const Value &value) +void Object::__put__(ExecutionContext *ctx, String *name, Value value) { // clause 1 if (!__canPut__(ctx, name)) @@ -207,6 +207,7 @@ void Object::__put__(ExecutionContext *ctx, String *name, const Value &value) p->configurable = PropertyDescriptor::Set; p->enumberable = PropertyDescriptor::Set; p->writable = PropertyDescriptor::Set; + return; } reject: diff --git a/qmljs_objects.h b/qmljs_objects.h index 45e301c56e..25790f457e 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -408,7 +408,7 @@ struct Object { virtual Value __get__(ExecutionContext *ctx, String *name); virtual PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); - virtual void __put__(ExecutionContext *ctx, String *name, const Value &value); + virtual void __put__(ExecutionContext *ctx, String *name, Value value); virtual bool __canPut__(ExecutionContext *ctx, String *name); virtual bool __hasProperty__(ExecutionContext *ctx, String *name) const; virtual bool __delete__(ExecutionContext *ctx, String *name); -- cgit v1.2.3 From 4bd20c2ac36c59eb72acc71bc78ddbc058199666 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 28 Nov 2012 11:00:23 +0100 Subject: Throw a SyntaxError instead of printing an error message. Change-Id: I94ef8a4f2bea80bc3689b104e381a9dc134439fa Reviewed-by: Lars Knoll --- main.cpp | 28 +++++++++++++++++++++++++--- qmljs_engine.cpp | 7 +++++++ qmljs_engine.h | 1 + qmljs_environment.cpp | 16 ++++++++++++++++ qmljs_environment.h | 17 +++++++++++++++++ qmljs_objects.cpp | 31 ++++++++++++++++++++++++++++--- qmljs_objects.h | 11 +++++++++-- qv4ecmaobjects.cpp | 2 +- qv4ecmaobjects_p.h | 2 +- 9 files changed, 105 insertions(+), 10 deletions(-) diff --git a/main.cpp b/main.cpp index bd1bcea89e..efe2b75b9b 100644 --- a/main.cpp +++ b/main.cpp @@ -108,10 +108,32 @@ struct TestHarnessError: FunctionObject static void showException(QQmlJS::VM::ExecutionContext *ctx) { - if (QQmlJS::VM::ErrorObject *e = ctx->engine->exception.asErrorObject()) - std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; - else + QQmlJS::VM::ErrorObject *e = ctx->engine->exception.asErrorObject(); + if (!e) { std::cerr << "Uncaught exception: " << qPrintable(ctx->engine->exception.toString(ctx)->toQString()) << std::endl; + return; + } + + if (QQmlJS::VM::SyntaxErrorObject *err = e->asSyntaxError()) { + QQmlJS::VM::DiagnosticMessage *msg = err->message(); + if (!msg) { + std::cerr << "Uncaught exception: Syntax error" << std::endl; + return; + } + + for (; msg; msg = msg->next) { + if (msg->fileName) + std::cerr << qPrintable(msg->fileName->toQString()); + std::cerr << ':' << msg->startLine << ':' << msg->startColumn << ": "; + if (msg->type == QQmlJS::VM::DiagnosticMessage::Error) + std::cerr << "error"; + else + std::cerr << "warning"; + std::cerr << ": " << qPrintable(msg->message->toQString()) << std::endl; + } + } else { + std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; + } } #ifndef QMLJS_NO_LLVM diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index acf221304e..6ac7250aef 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -316,6 +316,13 @@ Object *ExecutionEngine::newErrorObject(const Value &value) return object; } +Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) +{ + SyntaxErrorObject *object = new SyntaxErrorObject(ctx, message); + object->prototype = syntaxErrorPrototype; + return object; +} + Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) { MathObject *object = new MathObject(ctx); diff --git a/qmljs_engine.h b/qmljs_engine.h index 8d5662783a..9ed8421d27 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -175,6 +175,7 @@ struct ExecutionEngine FunctionObject *newRegExpCtor(ExecutionContext *ctx); Object *newErrorObject(const Value &value); + Object *newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message); Object *newMathObject(ExecutionContext *ctx); Object *newActivationObject(DeclarativeEnvironment *ctx); diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 299ffffc4a..f5b6624dc3 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -45,6 +45,17 @@ namespace QQmlJS { namespace VM { +DiagnosticMessage::DiagnosticMessage() + : fileName(0) + , offset(0) + , length(0) + , startLine(0) + , startColumn(0) + , type(0) + , message(0) + , next(0) +{} + DeclarativeEnvironment::DeclarativeEnvironment(ExecutionEngine *e) { engine = e; @@ -255,6 +266,11 @@ void ExecutionContext::throwError(const QString &message) throwError(Value::fromObject(engine->newErrorObject(v))); } +void ExecutionContext::throwSyntaxError(DiagnosticMessage *message) +{ + throwError(Value::fromObject(engine->newSyntaxErrorObject(this, message))); +} + void ExecutionContext::throwTypeError() { Value v = Value::fromString(this, QStringLiteral("Type error")); diff --git a/qmljs_environment.h b/qmljs_environment.h index f8339dec7e..74c362aa73 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -52,6 +52,22 @@ struct ExecutionEngine; struct ExecutionContext; struct DeclarativeEnvironment; +struct DiagnosticMessage +{ + enum { Error, Warning }; + + String *fileName; + quint32 offset; + quint32 length; + quint32 startLine; + unsigned startColumn: 31; + unsigned type: 1; + String *message; + DiagnosticMessage *next; + + DiagnosticMessage(); +}; + // This merges LexicalEnvironment and EnvironmentRecord from // Sec. 10.2 into one class struct DeclarativeEnvironment @@ -109,6 +125,7 @@ struct ExecutionContext void throwError(Value value); void throwError(const QString &message); + void throwSyntaxError(DiagnosticMessage *message); void throwTypeError(); void throwReferenceError(Value value); void throwUnimplemented(const QString &message); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index a7d5d20b86..8039f0f1bf 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -525,10 +525,25 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct const bool parsed = parser.parseProgram(); - foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { - std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn - << ": error: " << qPrintable(m.message) << std::endl; + VM::DiagnosticMessage *error = 0, **errIt = &error; + foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { + if (m.isError()) { + *errIt = new VM::DiagnosticMessage; // FIXME: should we ask the engine to create this object? + (*errIt)->fileName = ctx->engine->newString(fileName); + (*errIt)->offset = m.loc.offset; + (*errIt)->length = m.loc.length; + (*errIt)->startLine = m.loc.startLine; + (*errIt)->startColumn = m.loc.startColumn; + (*errIt)->type = VM::DiagnosticMessage::Error; + (*errIt)->message = ctx->engine->newString(m.message); + errIt = &(*errIt)->next; + } else { + std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn + << ": warning: " << qPrintable(m.message) << std::endl; + } } + if (error) + ctx->throwSyntaxError(error); if (parsed) { using namespace AST; @@ -612,6 +627,16 @@ void ErrorObject::setNameProperty(ExecutionContext *ctx) __put__(ctx, QLatin1String("name"), Value::fromString(ctx, className())); } +SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) + : ErrorObject(ctx->argument(0)) + , msg(message) +{ + if (message) + value = Value::fromString(message->message); + setNameProperty(ctx); +} + + Value ScriptFunction::construct(VM::ExecutionContext *ctx) { Object *obj = ctx->engine->newObject(); diff --git a/qmljs_objects.h b/qmljs_objects.h index 25790f457e..0b6f68992b 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -566,6 +566,8 @@ struct ErrorObject: Object { virtual ErrorObject *asErrorObject() { return this; } virtual Value __get__(ExecutionContext *ctx, String *name); + virtual struct SyntaxErrorObject *asSyntaxError() { return 0; } + protected: void setNameProperty(ExecutionContext *ctx); }; @@ -589,9 +591,14 @@ struct ReferenceErrorObject: ErrorObject { }; struct SyntaxErrorObject: ErrorObject { - SyntaxErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg); virtual QString className() { return QStringLiteral("SyntaxError"); } + + virtual SyntaxErrorObject *asSyntaxError() { return this; } + DiagnosticMessage *message() { return msg; } + +private: + DiagnosticMessage *msg; }; struct TypeErrorObject: ErrorObject { diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 67ef7aa8a5..b191144077 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2630,7 +2630,7 @@ Value ReferenceErrorCtor::construct(ExecutionContext *ctx) Value SyntaxErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new SyntaxErrorObject(ctx)); + ctx->thisObject = Value::fromObject(new SyntaxErrorObject(ctx, 0)); return ctx->thisObject; } diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index d94c979413..7afbe43ac5 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -375,7 +375,7 @@ struct ReferenceErrorPrototype: ReferenceErrorObject struct SyntaxErrorPrototype: SyntaxErrorObject { - SyntaxErrorPrototype(ExecutionContext *ctx): SyntaxErrorObject(ctx) {} + SyntaxErrorPrototype(ExecutionContext *ctx): SyntaxErrorObject(ctx, 0) {} void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; -- cgit v1.2.3 From bc88b706e2b894b4b5ae0977e3477a85b6762bc1 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 28 Nov 2012 13:35:53 +0100 Subject: Stop leaking the DeclarativeEnvironment and ExecutionContext. Also fixes non-POD warnings with Clang, as the constructor of the DeclarativeEnvironment is now gone. Change-Id: I15e02aabb195243415b088a8b030f0064a7e84a3 Reviewed-by: Lars Knoll --- qmljs_environment.cpp | 14 +++++++++----- qmljs_environment.h | 4 ++-- qmljs_objects.cpp | 2 ++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index f5b6624dc3..eee359aaa8 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -56,7 +56,7 @@ DiagnosticMessage::DiagnosticMessage() , next(0) {} -DeclarativeEnvironment::DeclarativeEnvironment(ExecutionEngine *e) +void DeclarativeEnvironment::init(ExecutionEngine *e) { engine = e; outer = 0; @@ -72,7 +72,7 @@ DeclarativeEnvironment::DeclarativeEnvironment(ExecutionEngine *e) strictMode = false; } -DeclarativeEnvironment::DeclarativeEnvironment(FunctionObject *f, Value *args, uint argc) +void DeclarativeEnvironment::init(FunctionObject *f, Value *args, uint argc) { outer = f->scope; engine = outer->engine; @@ -194,12 +194,12 @@ void DeclarativeEnvironment::popWithObject() delete w; } - void ExecutionContext::init(ExecutionEngine *eng) { engine = eng; parent = 0; - variableEnvironment = new DeclarativeEnvironment(eng); + variableEnvironment = new DeclarativeEnvironment; + variableEnvironment->init(eng); lexicalEnvironment = variableEnvironment; thisObject = Value::nullValue(); eng->exception = Value::undefinedValue(); @@ -295,7 +295,8 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha engine = parent->engine; this->parent = parent; - variableEnvironment = new DeclarativeEnvironment(f, args, argc); + variableEnvironment = new DeclarativeEnvironment; + variableEnvironment->init(f, args, argc); lexicalEnvironment = variableEnvironment; thisObject = that; @@ -308,6 +309,9 @@ void ExecutionContext::leaveCallContext() delete[] variableEnvironment->locals; variableEnvironment->locals = 0; } + + delete variableEnvironment; + variableEnvironment = 0; } void ExecutionContext::initConstructorContext(ExecutionContext *parent, Value that, FunctionObject *f, Value *args, unsigned argc) diff --git a/qmljs_environment.h b/qmljs_environment.h index 74c362aa73..ed2052a25b 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -91,8 +91,8 @@ struct DeclarativeEnvironment } *withObject; - DeclarativeEnvironment(ExecutionEngine *e); - DeclarativeEnvironment(FunctionObject *f, Value *args, uint argc); + void init(ExecutionEngine *e); + void init(FunctionObject *f, Value *args, uint argc); bool hasBinding(String *name) const; void createMutableBinding(ExecutionContext *ctx, String *name, bool deletable); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 8039f0f1bf..8b65b1d375 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -394,6 +394,8 @@ Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc ctx->initConstructorContext(context, Value::nullValue(), this, args, argc); Value result = construct(ctx); ctx->leaveConstructorContext(this); + if (ctx != &k) + delete ctx; return result; } -- cgit v1.2.3 From 721f2156a6c1ba77dd2c12bf06b7a3dcb0ea0b2c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 28 Nov 2012 13:39:14 +0100 Subject: Introduce a simple string pool to speed up lookups. This change uniques string pointers, so the String::isEqualTo will more often succeed in the pointer-equality case. Change-Id: I1d4f1a70147c48bc75359642a56a0446b5fbf199 Reviewed-by: Lars Knoll --- qmljs_engine.cpp | 19 ++++++++++++++++++- qmljs_engine.h | 2 ++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 6ac7250aef..7d12df5ace 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -45,9 +45,26 @@ namespace QQmlJS { namespace VM { +struct StringPool +{ + QHash strings; + + String *newString(const QString &s) + { + QHash::const_iterator it = strings.find(s); + if (it != strings.end()) + return it.value(); + String *str = new String(s); + strings.insert(s, str); + return str; + } +}; + ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) : iselFactory(factory) { + stringPool = new StringPool; + rootContext = newContext(); rootContext->init(this); @@ -213,7 +230,7 @@ FunctionObject *ExecutionEngine::newObjectCtor(ExecutionContext *ctx) String *ExecutionEngine::newString(const QString &s) { - return new String(s); + return stringPool->newString(s); } Object *ExecutionEngine::newStringObject(const Value &value) diff --git a/qmljs_engine.h b/qmljs_engine.h index 9ed8421d27..54e303cd54 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -140,6 +140,8 @@ struct ExecutionEngine QVector unwindStack; Value exception; + struct StringPool *stringPool; + ExecutionEngine(EvalISelFactory *iselFactory); ExecutionContext *newContext(); -- cgit v1.2.3 From 803f57d53864582d694acb2a82d03393749d3b0c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 28 Nov 2012 13:44:26 +0100 Subject: Tune the bucket count a bit to be a prime upto 68000 entries. The ECMA test suite has some tests that throw in vars with all possible unicode names. So, this should make it safer for longer. Change-Id: I4a65ab7d09a357d7665509d38e401098ab6e4607 Reviewed-by: Lars Knoll --- qmljs_objects.h | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/qmljs_objects.h b/qmljs_objects.h index 0b6f68992b..84dc513843 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -252,7 +252,9 @@ public: , _freeList(0) , _propertyCount(-1) , _bucketCount(0) - , _allocated(0) {} + , _primeIdx(-1) + , _allocated(0) + {} ~PropertyTable() { @@ -352,10 +354,7 @@ public: private: void rehash() { - if (_bucketCount) - _bucketCount = _bucketCount * 2 + 1; // ### next prime - else - _bucketCount = 11; + _bucketCount = nextPrime(); delete[] _buckets; _buckets = new PropertyTableEntry *[_bucketCount]; @@ -369,6 +368,19 @@ private: } } + inline int nextPrime() + { + // IMPORTANT: do not add more primes without checking if _primeIdx needs more bits! + static int primes[] = { + 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853, 25717, 51437, 102877 + }; + + if (_primeIdx < (int) (sizeof(primes)/sizeof(int))) + return primes[++_primeIdx]; + else + return _bucketCount * 2 + 1; // Yes, we're degrading here. But who needs more than about 68000 properties? + } + private: friend struct ForEachIteratorObject; PropertyTableEntry **_properties; @@ -376,7 +388,8 @@ private: PropertyTableEntry *_freeList; int _propertyCount; int _bucketCount; - int _allocated; + int _primeIdx: 4; + int _allocated: 28; }; struct Object { -- cgit v1.2.3 From 97a281d4b3534f74eb9c767767e622742f9faf91 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 28 Nov 2012 13:47:20 +0100 Subject: Replace qIsNaN usage with std::isnan, which is often faster. Change-Id: Ie4d16fcdb575ca168d5b58978c5a510acf0cd1ca std::isnan is often a compiler intrinsic. Reviewed-by: Lars Knoll --- qmljs_runtime.cpp | 2 +- qv4ecmaobjects.cpp | 66 +++++++++++++++++++++++++++--------------------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index a6fd7862bf..c5d796f5f9 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -68,7 +68,7 @@ static inline Value callFunction(ExecutionContext *context, Value thisObject, Fu QString numberToString(double num, int radix = 10) { - if (qIsNaN(num)) { + if (std::isnan(num)) { return QStringLiteral("NaN"); } else if (qIsInf(num)) { return QLatin1String(num < 0 ? "-Infinity" : "Infinity"); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index b191144077..8af8f457e7 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -436,7 +436,7 @@ static inline double ParseString(const QString &s) */ static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) { - if (qIsNaN(t)) + if (std::isnan(t)) return QDateTime(); if (spec == Qt::LocalTime) t = LocalTime(t); @@ -452,7 +452,7 @@ static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) static inline QString ToString(double t) { - if (qIsNaN(t)) + if (std::isnan(t)) return QStringLiteral("Invalid Date"); QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT"); double tzoffset = LocalTZA + DaylightSavingTA(t); @@ -472,7 +472,7 @@ static inline QString ToString(double t) static inline QString ToUTCString(double t) { - if (qIsNaN(t)) + if (std::isnan(t)) return QStringLiteral("Invalid Date"); return ToDateTime(t, Qt::UTC).toString() + QStringLiteral(" GMT"); } @@ -863,7 +863,7 @@ Value StringPrototype::method_lastIndexOf(ExecutionContext *ctx) Value posArg = ctx->argument(1); double position = __qmljs_to_number(posArg, ctx); - if (qIsNaN(position)) + if (std::isnan(position)) position = +qInf(); else position = trunc(position); @@ -969,10 +969,10 @@ Value StringPrototype::method_substring(ExecutionContext *ctx) if (ctx->argumentCount() > 1) end = ctx->argument(1).toInteger(ctx); - if (qIsNaN(start) || start < 0) + if (std::isnan(start) || start < 0) start = 0; - if (qIsNaN(end) || end < 0) + if (std::isnan(end) || end < 0) end = 0; if (start > length) @@ -1086,7 +1086,7 @@ Value NumberPrototype::method_toString(ExecutionContext *ctx) } double num = thisObject->value.asDouble(); - if (qIsNaN(num)) { + if (std::isnan(num)) { return Value::fromString(ctx, QStringLiteral("NaN")); } else if (qIsInf(num)) { return Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); @@ -1158,12 +1158,12 @@ Value NumberPrototype::method_toFixed(ExecutionContext *ctx) if (ctx->argumentCount() > 0) fdigits = ctx->argument(0).toInteger(ctx); - if (qIsNaN(fdigits)) + if (std::isnan(fdigits)) fdigits = 0; double v = thisObject->value.asDouble(); QString str; - if (qIsNaN(v)) + if (std::isnan(v)) str = QString::fromLatin1("NaN"); else if (qIsInf(v)) str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); @@ -2067,7 +2067,7 @@ Value DatePrototype::method_getTime(ExecutionContext *ctx) Value DatePrototype::method_getYear(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = YearFromTime(LocalTime(t)) - 1900; return Value::fromDouble(t); } @@ -2075,7 +2075,7 @@ Value DatePrototype::method_getYear(ExecutionContext *ctx) Value DatePrototype::method_getFullYear(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = YearFromTime(LocalTime(t)); return Value::fromDouble(t); } @@ -2083,7 +2083,7 @@ Value DatePrototype::method_getFullYear(ExecutionContext *ctx) Value DatePrototype::method_getUTCFullYear(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = YearFromTime(t); return Value::fromDouble(t); } @@ -2091,7 +2091,7 @@ Value DatePrototype::method_getUTCFullYear(ExecutionContext *ctx) Value DatePrototype::method_getMonth(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = MonthFromTime(LocalTime(t)); return Value::fromDouble(t); } @@ -2099,7 +2099,7 @@ Value DatePrototype::method_getMonth(ExecutionContext *ctx) Value DatePrototype::method_getUTCMonth(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = MonthFromTime(t); return Value::fromDouble(t); } @@ -2107,7 +2107,7 @@ Value DatePrototype::method_getUTCMonth(ExecutionContext *ctx) Value DatePrototype::method_getDate(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = DateFromTime(LocalTime(t)); return Value::fromDouble(t); } @@ -2115,7 +2115,7 @@ Value DatePrototype::method_getDate(ExecutionContext *ctx) Value DatePrototype::method_getUTCDate(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = DateFromTime(t); return Value::fromDouble(t); } @@ -2123,7 +2123,7 @@ Value DatePrototype::method_getUTCDate(ExecutionContext *ctx) Value DatePrototype::method_getDay(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = WeekDay(LocalTime(t)); return Value::fromDouble(t); } @@ -2131,7 +2131,7 @@ Value DatePrototype::method_getDay(ExecutionContext *ctx) Value DatePrototype::method_getUTCDay(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = WeekDay(t); return Value::fromDouble(t); } @@ -2139,7 +2139,7 @@ Value DatePrototype::method_getUTCDay(ExecutionContext *ctx) Value DatePrototype::method_getHours(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = HourFromTime(LocalTime(t)); return Value::fromDouble(t); } @@ -2147,7 +2147,7 @@ Value DatePrototype::method_getHours(ExecutionContext *ctx) Value DatePrototype::method_getUTCHours(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = HourFromTime(t); return Value::fromDouble(t); } @@ -2155,7 +2155,7 @@ Value DatePrototype::method_getUTCHours(ExecutionContext *ctx) Value DatePrototype::method_getMinutes(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = MinFromTime(LocalTime(t)); return Value::fromDouble(t); } @@ -2163,7 +2163,7 @@ Value DatePrototype::method_getMinutes(ExecutionContext *ctx) Value DatePrototype::method_getUTCMinutes(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = MinFromTime(t); return Value::fromDouble(t); } @@ -2171,7 +2171,7 @@ Value DatePrototype::method_getUTCMinutes(ExecutionContext *ctx) Value DatePrototype::method_getSeconds(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = SecFromTime(LocalTime(t)); return Value::fromDouble(t); } @@ -2179,7 +2179,7 @@ Value DatePrototype::method_getSeconds(ExecutionContext *ctx) Value DatePrototype::method_getUTCSeconds(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = SecFromTime(t); return Value::fromDouble(t); } @@ -2187,7 +2187,7 @@ Value DatePrototype::method_getUTCSeconds(ExecutionContext *ctx) Value DatePrototype::method_getMilliseconds(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = msFromTime(LocalTime(t)); return Value::fromDouble(t); } @@ -2195,7 +2195,7 @@ Value DatePrototype::method_getMilliseconds(ExecutionContext *ctx) Value DatePrototype::method_getUTCMilliseconds(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = msFromTime(t); return Value::fromDouble(t); } @@ -2203,7 +2203,7 @@ Value DatePrototype::method_getUTCMilliseconds(ExecutionContext *ctx) Value DatePrototype::method_getTimezoneOffset(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! qIsNaN(t)) + if (! std::isnan(t)) t = (t - LocalTime(t)) / msPerMinute; return Value::fromDouble(t); } @@ -2393,13 +2393,13 @@ Value DatePrototype::method_setYear(ExecutionContext *ctx) ctx->throwTypeError(); double t = self->value.asDouble(); - if (qIsNaN(t)) + if (std::isnan(t)) t = 0; else t = LocalTime(t); double year = ctx->argument(0).toNumber(ctx); double r; - if (qIsNaN(year)) { + if (std::isnan(year)) { r = qSNaN(); } else { if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99)) @@ -2833,7 +2833,7 @@ Value MathObject::method_max(ExecutionContext *ctx) double mx = -qInf(); for (unsigned i = 0; i < ctx->argumentCount(); ++i) { double x = ctx->argument(i).toNumber(ctx); - if (x > mx || qIsNaN(x)) + if (x > mx || std::isnan(x)) mx = x; } return Value::fromDouble(mx); @@ -2845,7 +2845,7 @@ Value MathObject::method_min(ExecutionContext *ctx) for (unsigned i = 0; i < ctx->argumentCount(); ++i) { double x = ctx->argument(i).toNumber(ctx); if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) - || (x < mx) || qIsNaN(x)) { + || (x < mx) || std::isnan(x)) { mx = x; } } @@ -2857,12 +2857,12 @@ Value MathObject::method_pow(ExecutionContext *ctx) double x = ctx->argument(0).toNumber(ctx); double y = ctx->argument(1).toNumber(ctx); - if (qIsNaN(y)) + if (std::isnan(y)) return Value::fromDouble(qSNaN()); if (y == 0) { return Value::fromDouble(1); - } else if (((x == 1) || (x == -1)) && qIsInf(y)) { + } else if (((x == 1) || (x == -1)) && std::isinf(y)) { return Value::fromDouble(qSNaN()); } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { return Value::fromDouble(qInf()); -- cgit v1.2.3 From b73d5145973787ab2794c569f8e3be29239be31e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 28 Nov 2012 14:16:21 +0100 Subject: Tune the message property (value) of SytaxError a bit. Change-Id: I231eb1eeb8f01461ea61b3989743bbd01256e251 Reviewed-by: Lars Knoll --- main.cpp | 9 +-------- qmljs_environment.cpp | 15 +++++++++++++++ qmljs_environment.h | 1 + qmljs_objects.cpp | 2 +- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/main.cpp b/main.cpp index efe2b75b9b..47e4747922 100644 --- a/main.cpp +++ b/main.cpp @@ -122,14 +122,7 @@ static void showException(QQmlJS::VM::ExecutionContext *ctx) } for (; msg; msg = msg->next) { - if (msg->fileName) - std::cerr << qPrintable(msg->fileName->toQString()); - std::cerr << ':' << msg->startLine << ':' << msg->startColumn << ": "; - if (msg->type == QQmlJS::VM::DiagnosticMessage::Error) - std::cerr << "error"; - else - std::cerr << "warning"; - std::cerr << ": " << qPrintable(msg->message->toQString()) << std::endl; + std::cerr << qPrintable(msg->buildFullMessage(ctx)->toQString()) << std::endl; } } else { std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index eee359aaa8..1b073ef119 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -56,6 +56,21 @@ DiagnosticMessage::DiagnosticMessage() , next(0) {} +String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const +{ + QString msg; + if (fileName) + msg = fileName->toQString() + QLatin1Char(':'); + msg += QString::number(startLine) + QLatin1Char(':') + QString::number(startColumn) + QLatin1String(": "); + if (type == QQmlJS::VM::DiagnosticMessage::Error) + msg += QLatin1String("error"); + else + msg += QLatin1String("warning"); + msg += ": " + message->toQString(); + + return ctx->engine->newString(msg); +} + void DeclarativeEnvironment::init(ExecutionEngine *e) { engine = e; diff --git a/qmljs_environment.h b/qmljs_environment.h index ed2052a25b..2364556d9e 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -66,6 +66,7 @@ struct DiagnosticMessage DiagnosticMessage *next; DiagnosticMessage(); + String *buildFullMessage(ExecutionContext *ctx) const; }; // This merges LexicalEnvironment and EnvironmentRecord from diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 8b65b1d375..b0b1d71fbd 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -634,7 +634,7 @@ SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *m , msg(message) { if (message) - value = Value::fromString(message->message); + value = Value::fromString(message->buildFullMessage(ctx)); setNameProperty(ctx); } -- cgit v1.2.3 From 80ece65fbd7a8ea4c3481215cb406fc301d10d50 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 28 Nov 2012 14:59:42 +0100 Subject: Fix copy-paste coding error. "m" is null in the else part. Change-Id: I5ef62c984f169b9a50ec1d90bd02c5593fde3745 Reviewed-by: Lars Knoll --- moth/qv4isel_moth.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index df2f873298..382b34ebfb 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -273,7 +273,7 @@ void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempInd addInstruction(call); } else if (IR::Subscript *ss = c->args->expr->asSubscript()) { Instruction::CallBuiltinDeleteSubscript call; - call.base = m->base->asTemp()->index; + call.base = ss->base->asTemp()->index; call.index = ss->index->asTemp()->index; call.targetTempIndex = targetTempIndex; addInstruction(call); -- cgit v1.2.3 From 4035e103cb51f670fb7172f3c19e133ac670b5f1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 28 Nov 2012 22:45:43 +0100 Subject: Add defineGetter and defineSetter methods to Object These methods are de-facto standard (every engine implements them), and also allow testing of accessor properties. Change-Id: I1fcaa7467f7be56ea758bf511e843385f74b9641 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 9 +++++---- qmljs_objects.h | 6 ++++-- qv4ecmaobjects.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ qv4ecmaobjects_p.h | 3 +++ tests/accessors.js | 8 ++++++++ 5 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 tests/accessors.js diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index b0b1d71fbd..d55006024b 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -144,7 +144,7 @@ bool Object::__canPut__(ExecutionContext *ctx, String *name) { if (PropertyDescriptor *p = __getOwnProperty__(ctx, name)) { if (p->isAccessor()) - return p->get != 0; + return p->set != 0; return p->isWritable(); } @@ -154,7 +154,7 @@ bool Object::__canPut__(ExecutionContext *ctx, String *name) PropertyDescriptor tmp; if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, name, &tmp)) { if (p->isAccessor()) - return p->get != 0; + return p->set != 0; if (!extensible) return false; return p->isWritable(); @@ -189,7 +189,7 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) // clause 4 PropertyDescriptor tmp; - if (prototype) + if (!pd && prototype) pd = prototype->__getPropertyDescriptor__(ctx, name, &tmp); // Clause 5 @@ -305,7 +305,8 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property } else { // clause 10 assert(current->isAccessor() && desc->isAccessor()); if (!current->isConfigurable()) { - if (current->get != desc->get || current->set != desc->set) + if ((desc->get && current->get != desc->get) || + (desc->set && current->set != desc->set)) goto reject; } } diff --git a/qmljs_objects.h b/qmljs_objects.h index 84dc513843..103a9de8fc 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -217,8 +217,10 @@ struct PropertyDescriptor { if (other.writable != Undefined) writable = other.writable; if (type == Accessor) { - get = other.get; - set = other.set; + if (other.get) + get = other.get; + if (other.set) + set = other.set; } else { value = other.value; } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 8af8f457e7..8c374e1c30 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -570,6 +570,8 @@ void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) __put__(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 0); __put__(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 0); __put__(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 0); + __put__(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 0); + __put__(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 0); } Value ObjectPrototype::method_getPrototypeOf(ExecutionContext *ctx) @@ -707,6 +709,46 @@ Value ObjectPrototype::method_propertyIsEnumerable(ExecutionContext *ctx) return Value::undefinedValue(); } +Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx) +{ + if (ctx->argumentCount() < 2) + __qmljs_throw_type_error(ctx); + String *prop = ctx->argument(0).toString(ctx); + + FunctionObject *f = ctx->argument(1).asFunctionObject(); + if (!f) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->thisObject.toObject(ctx).objectValue(); + + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(f, 0); + pd.writable = PropertyDescriptor::Set; + pd.configurable = PropertyDescriptor::Set; + pd.enumberable = PropertyDescriptor::Set; + o->__defineOwnProperty__(ctx, prop, &pd); + return Value::undefinedValue(); +} + +Value ObjectPrototype::method_defineSetter(ExecutionContext *ctx) +{ + if (ctx->argumentCount() < 2) + __qmljs_throw_type_error(ctx); + String *prop = ctx->argument(0).toString(ctx); + + FunctionObject *f = ctx->argument(1).asFunctionObject(); + if (!f) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->thisObject.toObject(ctx).objectValue(); + + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(0, f); + pd.writable = PropertyDescriptor::Set; + pd.configurable = PropertyDescriptor::Set; + pd.enumberable = PropertyDescriptor::Set; + o->__defineOwnProperty__(ctx, prop, &pd); + return Value::undefinedValue(); +} + // // String // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 7afbe43ac5..02d7d8cf19 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -80,6 +80,9 @@ struct ObjectPrototype: Object static Value method_hasOwnProperty(ExecutionContext *ctx); static Value method_isPrototypeOf(ExecutionContext *ctx); static Value method_propertyIsEnumerable(ExecutionContext *ctx); + + static Value method_defineGetter(ExecutionContext *ctx); + static Value method_defineSetter(ExecutionContext *ctx); }; struct StringCtor: FunctionObject diff --git a/tests/accessors.js b/tests/accessors.js new file mode 100644 index 0000000000..3874e4d329 --- /dev/null +++ b/tests/accessors.js @@ -0,0 +1,8 @@ +"use strict"; +var foo = { y: 1 } +foo.__defineSetter__("x", function(x) { this.y = x;} ) +foo.__defineGetter__("x", function() { return this.y;} ) +print(foo.y); +foo.x = "ok"; +print(foo.x); +print(foo.y); -- cgit v1.2.3 From e75251e72bd435b508aef212cf5e399fdab59d28 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 28 Nov 2012 23:10:03 +0100 Subject: Implement Object.seal/freeze/preventExtensions And the corresponding getters. Change-Id: I5038ec3f87f932d65c67cafd36ec00b9970a5f51 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 78 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 9 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 8c374e1c30..c51c55caa9 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -627,38 +627,98 @@ Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) Value ObjectPrototype::method_seal(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.seal")); + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + if (o->members) { + PropertyTable::iterator it = o->members->begin(); + while (it != o->members->end()) { + (*it)->descriptor.configurable = PropertyDescriptor::Unset; + ++it; + } + } return Value::undefinedValue(); } Value ObjectPrototype::method_freeze(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.freeze")); + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + if (o->members) { + PropertyTable::iterator it = o->members->begin(); + while (it != o->members->end()) { + if ((*it)->descriptor.isData()) + (*it)->descriptor.writable = PropertyDescriptor::Unset; + (*it)->descriptor.configurable = PropertyDescriptor::Unset; + ++it; + } + } return Value::undefinedValue(); } Value ObjectPrototype::method_preventExtensions(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.preventExtensions")); + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; return Value::undefinedValue(); } Value ObjectPrototype::method_isSealed(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.isSealed")); - return Value::undefinedValue(); + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + if (o->extensible) + return Value::fromBoolean(false); + if (o->members) { + PropertyTable::iterator it = o->members->begin(); + while (it != o->members->end()) { + if ((*it)->descriptor.configurable != PropertyDescriptor::Unset) + return Value::fromBoolean(false); + ++it; + } + } + return Value::fromBoolean(true); } Value ObjectPrototype::method_isFrozen(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.isFrozen")); - return Value::undefinedValue(); + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + if (o->extensible) + return Value::fromBoolean(false); + if (o->members) { + PropertyTable::iterator it = o->members->begin(); + while (it != o->members->end()) { + if ((*it)->descriptor.isData() && + ((*it)->descriptor.writable != PropertyDescriptor::Unset)) + return Value::fromBoolean(false); + if ((*it)->descriptor.configurable != PropertyDescriptor::Unset) + return Value::fromBoolean(false); + ++it; + } + } + return Value::fromBoolean(true); } Value ObjectPrototype::method_isExtensible(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.isExtensible")); - return Value::undefinedValue(); + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + return Value::fromBoolean(o->extensible); } Value ObjectPrototype::method_keys(ExecutionContext *ctx) -- cgit v1.2.3 From feafa104981951aa1853055bdb8b1cf2ce1a508b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 28 Nov 2012 23:26:26 +0100 Subject: Implement Object.keys and obj.proto.propertyIsEnumerable Change-Id: I30df135ad95f24246e43553b2711ad1008319d56 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 4 ++-- qmljs_engine.h | 4 ++-- qv4ecmaobjects.cpp | 26 ++++++++++++++++++++++---- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 7d12df5ace..ffecdf8f15 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -276,14 +276,14 @@ Object *ExecutionEngine::newFunctionObject(ExecutionContext *ctx) return object; } -Object *ExecutionEngine::newArrayObject() +ArrayObject *ExecutionEngine::newArrayObject() { ArrayObject *object = new ArrayObject(); object->prototype = arrayPrototype; return object; } -Object *ExecutionEngine::newArrayObject(const Array &value) +ArrayObject *ExecutionEngine::newArrayObject(const Array &value) { ArrayObject *object = new ArrayObject(value); object->prototype = arrayPrototype; diff --git a/qmljs_engine.h b/qmljs_engine.h index 54e303cd54..4ecc1daac8 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -166,8 +166,8 @@ struct ExecutionEngine Object *newFunctionObject(ExecutionContext *ctx); - Object *newArrayObject(); - Object *newArrayObject(const Array &value); + ArrayObject *newArrayObject(); + ArrayObject *newArrayObject(const Array &value); FunctionObject *newArrayCtor(ExecutionContext *ctx); Object *newDateObject(const Value &value); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index c51c55caa9..9fd95b94a0 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -723,8 +723,23 @@ Value ObjectPrototype::method_isExtensible(ExecutionContext *ctx) Value ObjectPrototype::method_keys(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.keys")); - return Value::undefinedValue(); + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + + ArrayObject *a = ctx->engine->newArrayObject(); + + if (o->members) { + PropertyTable::iterator it = o->members->begin(); + while (it != o->members->end()) { + if ((*it)->descriptor.isEnumerable()) + a->value.push(Value::fromString((*it)->name)); + ++it; + } + } + + return Value::fromObject(a); } Value ObjectPrototype::method_toString(ExecutionContext *ctx) @@ -765,8 +780,11 @@ Value ObjectPrototype::method_isPrototypeOf(ExecutionContext *ctx) Value ObjectPrototype::method_propertyIsEnumerable(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.prototype.propertyIsEnumerable")); - return Value::undefinedValue(); + String *p = ctx->argument(0).toString(ctx); + + Object *o = ctx->thisObject.toObject(ctx).objectValue(); + PropertyDescriptor *pd = o->__getOwnProperty__(ctx, p); + return Value::fromBoolean(pd && pd->enumberable); } Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx) -- cgit v1.2.3 From 6d72378043ac836442dc4bd0f0e407062d161d9b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 29 Nov 2012 14:35:18 +0100 Subject: Fix outgoing argument start position. Change-Id: Ie1b86746dc578ce479f5c072e2e1190826a7739c Reviewed-by: Lars Knoll --- moth/qv4isel_moth.cpp | 4 ++-- moth/qv4isel_moth_p.h | 5 ++++- moth/qv4vme_moth.cpp | 39 ++++++++++++++++++++++++++++++--------- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 382b34ebfb..d16678936e 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -201,7 +201,7 @@ void InstructionSelection::operator()(IR::Function *function) _function->code = VME::exec; _function->codeData = _code; - int locals = _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments + 1; + int locals = frameSize(); assert(locals >= 0); Instruction::Push push; @@ -446,7 +446,7 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint args = e->expr->asTemp()->index; } else if (e) { // We need to move all the temps into the function arg array - int argLocation = _function->tempCount - _function->locals.size(); + int argLocation = outgoingArgumentTempStart(); assert(argLocation >= 0); args = argLocation; while (e) { diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 043958b4b3..6fe5259dfb 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -43,7 +43,10 @@ private: void callProperty(IR::Call *c, int targetTempIndex); void construct(IR::New *ctor, int targetTempIndex); void prepareCallArgs(IR::ExprList *, quint32 &, quint32 &); - int scratchTempIndex() { return _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments; } + + int outgoingArgumentTempStart() const { return _function->tempCount; } + int scratchTempIndex() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; } + int frameSize() const { return scratchTempIndex() + 1 - _function->locals.size(); } template inline ptrdiff_t addInstruction(const InstrData &data); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 5a0ba30b25..87d50a36d0 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -155,7 +155,11 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(LoadString) MOTH_BEGIN_INSTR(LoadClosure) - TEMP(instr.targetTempIndex) = __qmljs_init_closure(instr.value, context); + VM::Value c = __qmljs_init_closure(instr.value, context); + TEMP(instr.targetTempIndex) = c; +#ifdef DO_TRACE_INSTR + qDebug() << "loaded:" << c.toString(context)->toQString(); +#endif MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) @@ -197,18 +201,32 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(Push) MOTH_BEGIN_INSTR(CallValue) - TRACE(Call, "argStart = %d, argc = %d, result temp index = %d", instr.args, instr.argc, instr.targetTempIndex); - VM::Value *args = stack.data() + instr.args; +#ifdef DO_TRACE_INSTR + if (Debugging::Debugger *debugger = context->engine->debugger) { + if (VM::FunctionObject *o = (TEMP(instr.destIndex)).asFunctionObject()) { + if (Debugging::FunctionDebugInfo *info = debugger->debugInfo(o)) { + QString n = debugger->name(o); + std::cerr << "*** Call to \"" << (n.isNull() ? "" : qPrintable(n)) << "\" defined @" << info->startLine << ":" << info->startColumn << std::endl; + } + } + } +#endif // DO_TRACE_INSTR + quint32 argStart = instr.args - context->variableEnvironment->varCount; + TRACE(Call, "value index = %d, argStart = %d, argc = %d, result temp index = %d", instr.destIndex, argStart, instr.argc, instr.targetTempIndex); + VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_call_value(context, VM::Value::undefinedValue(), TEMP(instr.destIndex), args, instr.argc); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) - VM::Value *args = stack.data() + instr.args; - TEMP(instr.targetTempIndex) = __qmljs_call_property(context, TEMP(instr.baseTemp), instr.name, args, instr.argc); + quint32 argStart = instr.args - context->variableEnvironment->varCount; + VM::Value *args = stack.data() + argStart; + VM::Value base = TEMP(instr.baseTemp); + TEMP(instr.targetTempIndex) = __qmljs_call_property(context, base, instr.name, args, instr.argc); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallBuiltin) - VM::Value *args = stack.data() + instr.args; + quint32 argStart = instr.args - context->variableEnvironment->varCount; + VM::Value *args = stack.data() + argStart; void *buf; switch (instr.builtin) { case Instr::instr_callBuiltin::builtin_typeof: @@ -287,18 +305,21 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinDeleteValue) MOTH_BEGIN_INSTR(CreateValue) - VM::Value *args = stack.data() + instr.args; + quint32 argStart = instr.args - context->variableEnvironment->varCount; + VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_value(context, TEMP(instr.func), args, instr.argc); MOTH_END_INSTR(CreateValue) MOTH_BEGIN_INSTR(CreateProperty) - VM::Value *args = stack.data() + instr.args; + quint32 argStart = instr.args - context->variableEnvironment->varCount; + VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_property(context, TEMP(instr.base), instr.name, args, instr.argc); MOTH_END_INSTR(CreateProperty) MOTH_BEGIN_INSTR(CreateActivationProperty) TRACE(inline, "property name = %s, argc = %d", instr.name->toQString().toUtf8().constData(), instr.argc); - VM::Value *args = stack.data() + instr.args; + quint32 argStart = instr.args - context->variableEnvironment->varCount; + VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); MOTH_END_INSTR(CreateActivationProperty) -- cgit v1.2.3 From 968fc9c6c9f3532e9456b3bf9049e6aa2282de7a Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 29 Nov 2012 14:36:16 +0100 Subject: Fix missing code generation for inplace operations on locals. Change-Id: I8fe7d87eabf2566f251319e8dae005aacc27eb0d Reviewed-by: Lars Knoll --- moth/qv4isel_moth.cpp | 83 +++++++++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index d16678936e..94a06d393b 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -578,44 +578,57 @@ void InstructionSelection::visitMove(IR::Move *s) load.targetTempIndex = targetTempIndex; addInstruction(load); } - } else if (IR::Const *c = s->source->asConst()) { - switch (c->type) { - case IR::UndefinedType: { - Instruction::LoadUndefined load; - load.targetTempIndex = targetTempIndex; - addInstruction(load); - } break; - case IR::NullType: { - Instruction::LoadNull load; - load.targetTempIndex = targetTempIndex; - addInstruction(load); - } break; - case IR::BoolType: - if (c->value) { - Instruction::LoadTrue load; - load.targetTempIndex = targetTempIndex; - addInstruction(load); + } else if (s->source->asTemp() || s->source->asConst()) { + if (s->op == IR::OpInvalid) { + if (IR::Temp *t2 = s->source->asTemp()) { + Instruction::MoveTemp move; + move.fromTempIndex = t2->index; + move.toTempIndex = targetTempIndex; + addInstruction(move); } else { - Instruction::LoadFalse load; - load.targetTempIndex = targetTempIndex; - addInstruction(load); + IR::Const *c = s->source->asConst(); + assert(c); + switch (c->type) { + case IR::UndefinedType: { + Instruction::LoadUndefined load; + load.targetTempIndex = targetTempIndex; + addInstruction(load); + } break; + case IR::NullType: { + Instruction::LoadNull load; + load.targetTempIndex = targetTempIndex; + addInstruction(load); + } break; + case IR::BoolType: + if (c->value) { + Instruction::LoadTrue load; + load.targetTempIndex = targetTempIndex; + addInstruction(load); + } else { + Instruction::LoadFalse load; + load.targetTempIndex = targetTempIndex; + addInstruction(load); + } + break; + case IR::NumberType: { + Instruction::LoadNumber load; + load.value = c->value; + load.targetTempIndex = targetTempIndex; + addInstruction(load); + } break; + default: + Q_UNREACHABLE(); + break; + } } - break; - case IR::NumberType: { - Instruction::LoadNumber load; - load.value = c->value; - load.targetTempIndex = targetTempIndex; - addInstruction(load); - } break; - default: - Q_UNREACHABLE(); - break; + } else { + Instruction::Binop binop; + binop.alu = aluOpFunction(s->op); + binop.lhsIsTemp = toValueOrTemp(t, binop.lhs); + binop.rhsIsTemp = toValueOrTemp(s->source, binop.rhs); + binop.targetTempIndex = targetTempIndex; + addInstruction(binop); } - } else if (IR::Temp *t2 = s->source->asTemp()) { - Instruction::MoveTemp move; - move.fromTempIndex = t2->index; - move.toTempIndex = targetTempIndex; - addInstruction(move); } else if (IR::String *str = s->source->asString()) { Instruction::LoadString load; load.value = _engine->newString(*str->value); -- cgit v1.2.3 From 36356a4b273e19ca599bf2a0cbfb02fda69e6c0a Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 29 Nov 2012 14:39:19 +0100 Subject: Set the name of a function in more (most?) cases. Change-Id: I1c2b9d61b6d97e3c2a8cb976fb6be8b68d51ae28 Reviewed-by: Lars Knoll --- llvm_runtime.cpp | 4 ++-- main.cpp | 8 ++++++-- qmljs_engine.cpp | 4 ++-- qmljs_engine.h | 2 +- qmljs_objects.cpp | 29 ++++++++++++++++++++++++++++- qmljs_objects.h | 8 ++++---- qmljs_runtime.cpp | 4 ++-- qmljs_runtime.h | 2 +- 8 files changed, 46 insertions(+), 15 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 2ecbee6e59..9205dd575c 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -83,9 +83,9 @@ void __qmljs_llvm_init_string(ExecutionContext *ctx, Value *result, const char * *result = Value::fromString(__qmljs_string_from_utf8(ctx, str)); } -void __qmljs_llvm_init_native_function(ExecutionContext *ctx, Value *result, Value (*code)(ExecutionContext *)) +void __qmljs_llvm_init_native_function(ExecutionContext *ctx, Value *result, String *name, Value (*code)(ExecutionContext *)) { - *result = __qmljs_init_native_function(code, ctx); + *result = __qmljs_init_native_function(name, code, ctx); } bool __qmljs_llvm_to_boolean(ExecutionContext *ctx, const Value *value) diff --git a/main.cpp b/main.cpp index 47e4747922..dccac7edce 100644 --- a/main.cpp +++ b/main.cpp @@ -68,7 +68,9 @@ using namespace QQmlJS::VM; struct Print: FunctionObject { - Print(ExecutionContext *scope): FunctionObject(scope) {} + Print(ExecutionContext *scope): FunctionObject(scope) { + name = scope->engine->newString("print"); + } virtual Value call(ExecutionContext *ctx) { @@ -85,7 +87,9 @@ struct Print: FunctionObject struct TestHarnessError: FunctionObject { - TestHarnessError(ExecutionContext *scope, bool &errorInTestHarness): FunctionObject(scope), errorOccurred(errorInTestHarness) {} + TestHarnessError(ExecutionContext *scope, bool &errorInTestHarness): FunctionObject(scope), errorOccurred(errorInTestHarness) { + name = scope->engine->newString("$ERROR"); + } virtual Value call(ExecutionContext *ctx) { diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index ffecdf8f15..a1b3256fc7 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -199,9 +199,9 @@ String *ExecutionEngine::identifier(const QString &s) return id; } -FunctionObject *ExecutionEngine::newNativeFunction(ExecutionContext *scope, Value (*code)(ExecutionContext *)) +FunctionObject *ExecutionEngine::newNativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) { - NativeFunction *f = new NativeFunction(scope, code); + NativeFunction *f = new NativeFunction(scope, name, code); f->prototype = scope->engine->functionPrototype; return f; } diff --git a/qmljs_engine.h b/qmljs_engine.h index 4ecc1daac8..dcae173fef 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -148,7 +148,7 @@ struct ExecutionEngine String *identifier(const QString &s); - FunctionObject *newNativeFunction(ExecutionContext *scope, Value (*code)(ExecutionContext *)); + FunctionObject *newNativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); FunctionObject *newScriptFunction(ExecutionContext *scope, IR::Function *function); Object *newObject(); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index d55006024b..28932e371c 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -76,7 +76,8 @@ void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &va void Object::__put__(ExecutionContext *ctx, const QString &name, Value (*code)(ExecutionContext *), int count) { Q_UNUSED(count); - __put__(ctx, name, Value::fromObject(ctx->engine->newNativeFunction(ctx, code))); + String *nameStr = ctx->engine->newString(name); + __put__(ctx, name, Value::fromObject(ctx->engine->newNativeFunction(ctx, nameStr, code))); } Value Object::getValue(ExecutionContext *ctx, PropertyDescriptor *p) const @@ -510,6 +511,12 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value return result; } +EvalFunction::EvalFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("eval")); +} + QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, const QString &source, QQmlJS::Codegen::Mode mode) @@ -585,6 +592,12 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct /// isNaN [15.1.2.4] +IsNaNFunction::IsNaNFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("isNaN")); +} + Value IsNaNFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, Value *args, int /*argc*/) { // TODO: see if we can generate code for this directly @@ -593,6 +606,12 @@ Value IsNaNFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, } /// isFinite [15.1.2.5] +IsFiniteFunction::IsFiniteFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("isFinite")); +} + Value IsFiniteFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, Value *args, int /*argc*/) { // TODO: see if we can generate code for this directly @@ -709,3 +728,11 @@ PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(ExecutionContext return Object::__getPropertyDescriptor__(ctx, name, to_fill); } + + +NativeFunction::NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) + : FunctionObject(scope) + , code(code) +{ + this->name = name; +} diff --git a/qmljs_objects.h b/qmljs_objects.h index 103a9de8fc..957a562d97 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -524,7 +524,7 @@ protected: struct NativeFunction: FunctionObject { Value (*code)(ExecutionContext *); - NativeFunction(ExecutionContext *scope, Value (*code)(ExecutionContext *)): FunctionObject(scope), code(code) {} + NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); virtual Value call(ExecutionContext *ctx) { return code(ctx); } virtual Value construct(ExecutionContext *ctx) { ctx->thisObject = code(ctx); return ctx->thisObject; } }; @@ -541,7 +541,7 @@ struct ScriptFunction: FunctionObject { struct EvalFunction : FunctionObject { - EvalFunction(ExecutionContext *scope): FunctionObject(scope) {} + EvalFunction(ExecutionContext *scope); static QQmlJS::IR::Function *parseSource(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, const QString &source, @@ -552,14 +552,14 @@ struct EvalFunction : FunctionObject struct IsNaNFunction: FunctionObject { - IsNaNFunction(ExecutionContext *scope): FunctionObject(scope) {} + IsNaNFunction(ExecutionContext *scope); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); }; struct IsFiniteFunction: FunctionObject { - IsFiniteFunction(ExecutionContext *scope): FunctionObject(scope) {} + IsFiniteFunction(ExecutionContext *scope); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); }; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index c5d796f5f9..1aa3c94f2c 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -119,9 +119,9 @@ Value __qmljs_init_closure(IR::Function *clos, ExecutionContext *ctx) return Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); } -Value __qmljs_init_native_function(Value (*code)(ExecutionContext *), ExecutionContext *ctx) +Value __qmljs_init_native_function(String *name, Value (*code)(ExecutionContext *), ExecutionContext *ctx) { - return Value::fromObject(ctx->engine->newNativeFunction(ctx, code)); + return Value::fromObject(ctx->engine->newNativeFunction(ctx, name, code)); } Value __qmljs_string_literal_undefined(ExecutionContext *ctx) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index a02713d941..2df8dd105c 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -107,7 +107,7 @@ void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String * // constructors Value __qmljs_init_closure(IR::Function *clos, ExecutionContext *ctx); -Value __qmljs_init_native_function(Value (*code)(ExecutionContext *), ExecutionContext *ctx); +Value __qmljs_init_native_function(String *name, Value (*code)(ExecutionContext *), ExecutionContext *ctx); Bool __qmljs_is_function(Value value); -- cgit v1.2.3 From a14e7549c4a3faece3474f059b2d04005cfc7cbf Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 29 Nov 2012 14:41:26 +0100 Subject: Add some debugging infrastructure to the interpreter. This currently mainly intended to be useful in a C++ debugger. The infrastructure makes it a lot easier to access (parent) contexts, find function names, etc. Change-Id: I0493d3a3bd4bf5c3a03379c1a2b545ed76862cd5 Reviewed-by: Lars Knoll --- debugging.cpp | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++ debugging.h | 137 +++++++++++++++++++++++++++++++++++++ main.cpp | 26 ++++++- moth/qv4isel_moth.cpp | 4 ++ moth/qv4vme_moth.cpp | 20 ++++++ qmljs_engine.cpp | 1 + qmljs_engine.h | 8 ++- qmljs_environment.cpp | 8 +++ qmljs_objects.cpp | 3 +- qmljs_objects.h | 4 ++ qmljs_runtime.cpp | 4 ++ qv4codegen.cpp | 18 ++++- qv4codegen_p.h | 7 +- qv4ecmaobjects.cpp | 2 +- qv4isel_llvm.cpp | 1 + v4.pro | 6 +- 16 files changed, 421 insertions(+), 11 deletions(-) create mode 100644 debugging.cpp create mode 100644 debugging.h diff --git a/debugging.cpp b/debugging.cpp new file mode 100644 index 0000000000..741d5daee4 --- /dev/null +++ b/debugging.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "debugging.h" +#include "qmljs_objects.h" + +#include + +using namespace QQmlJS; +using namespace QQmlJS::Debugging; + +FunctionState::FunctionState(VM::ExecutionContext *context) + : _context(context) +{ + if (debugger()) + debugger()->enterFunction(this); +} + +FunctionState::~FunctionState() +{ + if (debugger()) + debugger()->leaveFunction(this); +} + +VM::Value *FunctionState::argument(unsigned idx) +{ + if (idx < _context->variableEnvironment->argumentCount) + return _context->variableEnvironment->arguments + idx; + else + return 0; +} + +VM::Value *FunctionState::local(unsigned idx) +{ + if (idx < _context->variableEnvironment->varCount) + return _context->variableEnvironment->locals + idx; + else + return 0; +} + +Debugger::Debugger(VM::ExecutionEngine *engine) + : _engine(engine) +{ +} + +Debugger::~Debugger() +{ + qDeleteAll(_functionInfo.values()); +} + +void Debugger::addFunction(IR::Function *function) +{ + _functionInfo.insert(function, new FunctionDebugInfo(function)); +} + +void Debugger::addaddBasicBlockOffset(IR::Function *function, IR::BasicBlock *block, ptrdiff_t blockOffset) +{ + _functionInfo[function]->addBasicBlockOffset(block, blockOffset); +} + +void Debugger::setSourceLocation(IR::Function *function, unsigned line, unsigned column) +{ + _functionInfo[function]->setSourceLocation(line, column); +} + +FunctionDebugInfo *Debugger::debugInfo(VM::FunctionObject *function) const +{ + if (!function) + return 0; + + if (VM::ScriptFunction *sf = function->asScriptFunction()) + return _functionInfo[sf->function]; + else + return 0; +} + +QString Debugger::name(VM::FunctionObject *function) const +{ + if (FunctionDebugInfo *i = debugInfo(function)) + if (i->function) + if (const QString *n = i->function->name) + return *n; + + return QString(); +} + +void Debugger::aboutToCall(VM::FunctionObject *function, VM::ExecutionContext *context) +{ + _callStack.append(CallInfo(context, function)); +} + +void Debugger::justLeft(VM::ExecutionContext *context) +{ + int idx = callIndex(context); + if (idx < 0) + qDebug() << "Oops, leaving a function that was not registered...?"; + else + _callStack.resize(idx); +} + +void Debugger::enterFunction(FunctionState *state) +{ + _callStack[callIndex(state->context())].state = state; + +#ifdef DO_TRACE_INSTR + QString n = name(_callStack[callIndex(state->context())].function); + std::cerr << "*** Entering \"" << qPrintable(n) << "\" with" << state->context()->variableEnvironment->argumentCount << "args" << std::endl; + for (unsigned i = 0; i < state->context()->variableEnvironment->argumentCount; ++i) + std::cerr << " " << i << ": " << currentArg(i) << std::endl; +#endif // DO_TRACE_INSTR +} + +void Debugger::leaveFunction(FunctionState *state) +{ + _callStack[callIndex(state->context())].state = 0; +} + +void Debugger::aboutToThrow(VM::Value *value) +{ + qDebug() << "*** We are about to throw...:" << value->toString(currentState()->context())->toQString(); +} + +FunctionState *Debugger::currentState() const +{ + if (_callStack.isEmpty()) + return 0; + else + return _callStack.last().state; +} + +const char *Debugger::currentArg(unsigned idx) const +{ + FunctionState *state = currentState(); + return qPrintable(state->argument(idx)->toString(state->context())->toQString()); +} + +const char *Debugger::currentLocal(unsigned idx) const +{ + FunctionState *state = currentState(); + return qPrintable(state->local(idx)->toString(state->context())->toQString()); +} + +const char *Debugger::currentTemp(unsigned idx) const +{ + FunctionState *state = currentState(); + return qPrintable(state->temp(idx)->toString(state->context())->toQString()); +} + +int Debugger::callIndex(VM::ExecutionContext *context) +{ + for (int idx = _callStack.size() - 1; idx >= 0; --idx) { + if (_callStack[idx].context == context) + return idx; + } + + return -1; +} diff --git a/debugging.h b/debugging.h new file mode 100644 index 0000000000..7a0a27dfdb --- /dev/null +++ b/debugging.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef DEBUGGING_H +#define DEBUGGING_H + +#include "qmljs_engine.h" +#include "qmljs_environment.h" + +#include + +namespace QQmlJS { + +namespace IR { +struct BasicBlock; +struct Function; +} // namespace IR + +namespace Debugging { + +class Debugger; + +struct FunctionDebugInfo { // TODO: use opaque d-pointers here + IR::Function *function; + QHash blockOffsets; + unsigned startLine, startColumn; + + FunctionDebugInfo(IR::Function *function): function(function), startLine(0), startColumn(0) {} + + void addBasicBlockOffset(IR::BasicBlock *block, ptrdiff_t offset) { + blockOffsets.insert(offset, block); + } + + void setSourceLocation(unsigned line, unsigned column) + { startLine = line; startColumn = column; } +}; + +class FunctionState +{ +public: + FunctionState(VM::ExecutionContext *context); + virtual ~FunctionState(); + + virtual VM::Value *argument(unsigned idx); + virtual VM::Value *local(unsigned idx); + virtual VM::Value *temp(unsigned idx) = 0; + + VM::ExecutionContext *context() const + { return _context; } + + Debugger *debugger() const + { return _context->engine->debugger; } + +private: + VM::ExecutionContext *_context; +}; + +struct CallInfo +{ + VM::ExecutionContext *context; + VM::FunctionObject *function; + FunctionState *state; + + CallInfo(VM::ExecutionContext *context = 0, VM::FunctionObject *function = 0, FunctionState *state = 0) + : context(context) + , function(function) + , state(state) + {} +}; + +class Debugger +{ +public: + Debugger(VM::ExecutionEngine *_engine); + ~Debugger(); + +public: // compile-time interface + void addFunction(IR::Function *function); + void addaddBasicBlockOffset(IR::Function *function, IR::BasicBlock *block, ptrdiff_t blockOffset); + void setSourceLocation(IR::Function *function, unsigned line, unsigned column); + +public: // run-time querying interface + FunctionDebugInfo *debugInfo(VM::FunctionObject *function) const; + QString name(VM::FunctionObject *function) const; + +public: // execution hooks + void aboutToCall(VM::FunctionObject *function, VM::ExecutionContext *context); + void justLeft(VM::ExecutionContext *context); + void enterFunction(FunctionState *state); + void leaveFunction(FunctionState *state); + void aboutToThrow(VM::Value *value); + +public: // debugging hooks + FunctionState *currentState() const; + const char *currentArg(unsigned idx) const; + const char *currentLocal(unsigned idx) const; + const char *currentTemp(unsigned idx) const; + +private: + int callIndex(VM::ExecutionContext *context); + +private: // TODO: use opaque d-pointers here + VM::ExecutionEngine *_engine; + QHash _functionInfo; + QVector _callStack; +}; + +} // namespace Debugging +} // namespace QQmlJS + +#endif // DEBUGGING_H diff --git a/main.cpp b/main.cpp index dccac7edce..8418dc16fd 100644 --- a/main.cpp +++ b/main.cpp @@ -43,6 +43,7 @@ # include "qv4_llvm_p.h" #endif +#include "debugging.h" #include "qmljs_objects.h" #include "qmljs_runtime.h" #include "qv4codegen_p.h" @@ -183,7 +184,7 @@ int compile(const QString &fileName, const QString &source, QQmlJS::LLVMOutputTy using namespace AST; Program *program = AST::cast(parser.rootNode()); - Codegen cg; + Codegen cg(0); // FIXME: if the program is empty, we should we generate an empty %entry, or give an error? /*IR::Function *globalCode =*/ cg(program, &module); @@ -246,6 +247,14 @@ int main(int argc, char *argv[]) #ifndef QMLJS_NO_LLVM QQmlJS::LLVMOutputType fileType = QQmlJS::LLVMOutputObject; #endif // QMLJS_NO_LLVM + bool enableDebugging = false; + + if (!args.isEmpty()) { + if (args.first() == QLatin1String("-d") || args.first() == QLatin1String("--debug")) { + enableDebugging = true; + args.removeFirst(); + } + } if (!args.isEmpty()) { if (args.first() == QLatin1String("--jit")) { @@ -287,7 +296,7 @@ int main(int argc, char *argv[]) } #endif // QMLJS_NO_LLVM if (args.first() == QLatin1String("--help")) { - std::cerr << "Usage: v4 [|--jit|--interpret|--compile|--aot|--llvm-jit] file..." << std::endl; + std::cerr << "Usage: v4 [|--debug|-d] [|--jit|--interpret|--compile|--aot|--llvm-jit] file..." << std::endl; return EXIT_SUCCESS; } } @@ -314,7 +323,14 @@ int main(int argc, char *argv[]) iSelFactory.reset(new QQmlJS::Moth::ISelFactory); else iSelFactory.reset(new QQmlJS::MASM::ISelFactory); + QQmlJS::VM::ExecutionEngine vm(iSelFactory.data()); + + QScopedPointer debugger; + if (enableDebugging) + debugger.reset(new QQmlJS::Debugging::Debugger(&vm)); + vm.debugger = debugger.data(); + QQmlJS::VM::ExecutionContext *ctx = vm.rootContext; QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); @@ -338,11 +354,15 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - QQmlJS::IR::Function *f = QQmlJS::VM::EvalFunction::parseSource(vm.rootContext, fn, code, QQmlJS::Codegen::GlobalCode); + QQmlJS::IR::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode); if (!f) continue; ctx->lexicalEnvironment->strictMode = f->isStrict; + if (debugger) + debugger->aboutToCall(0, ctx); QQmlJS::VM::Value result = f->code(ctx, f->codeData); + if (debugger) + debugger->justLeft(ctx); if (!result.isUndefined()) { if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 94a06d393b..482d0633b0 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -1,6 +1,7 @@ #include "qv4isel_util_p.h" #include "qv4isel_moth_p.h" #include "qv4vme_moth_p.h" +#include "debugging.h" using namespace QQmlJS; using namespace QQmlJS::Moth; @@ -209,7 +210,10 @@ void InstructionSelection::operator()(IR::Function *function) addInstruction(push); foreach (_block, _function->basicBlocks) { + ptrdiff_t blockOffset = _ccode - _code; _addrs.insert(_block, _ccode - _code); + if (_engine->debugger) + _engine->debugger->addaddBasicBlockOffset(_function, _block, blockOffset); foreach (IR::Stmt *s, _block->statements) s->accept(this); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 87d50a36d0..92e7c7c206 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -1,6 +1,9 @@ #include "qv4vme_moth_p.h" #include "qv4instr_moth_p.h" #include "qmljs_value.h" +#include "debugging.h" + +#include #ifdef DO_TRACE_INSTR # define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); @@ -92,6 +95,22 @@ static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVecto } } +class FunctionState: public Debugging::FunctionState +{ +public: + FunctionState(QQmlJS::VM::ExecutionContext *context, QVector *stack, const uchar **code) + : Debugging::FunctionState(context) + , stack(stack) + , code(code) + {} + + virtual VM::Value *temp(unsigned idx) { return stack->data() + idx; } + +private: + QVector *stack; + const uchar **code; +}; + #define TEMP(index) *tempValue(context, stack, index) VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code @@ -114,6 +133,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co #endif QVector stack; + FunctionState state(context, &stack, &code); #ifdef MOTH_THREADED_INTERPRETER const Instr *genericInstr = reinterpret_cast(code); diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index a1b3256fc7..1609559659 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -62,6 +62,7 @@ struct StringPool ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) : iselFactory(factory) + , debugger(0) { stringPool = new StringPool; diff --git a/qmljs_engine.h b/qmljs_engine.h index dcae173fef..9b91f01c04 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -47,6 +47,11 @@ #include namespace QQmlJS { + +namespace Debugging { +class Debugger; +} // namespace Debugging + namespace VM { struct Value; @@ -87,6 +92,8 @@ struct ExecutionEngine ExecutionContext *current; ExecutionContext *rootContext; + Debugging::Debugger *debugger; + Value globalObject; Value objectCtor; @@ -184,7 +191,6 @@ struct ExecutionEngine Object *newForEachIteratorObject(Object *o); }; - } // namespace VM } // namespace QQmlJS diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 1b073ef119..e9b55a89f7 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -38,6 +38,8 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ + +#include "debugging.h" #include #include #include @@ -315,6 +317,9 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha lexicalEnvironment = variableEnvironment; thisObject = that; + + if (engine->debugger) + engine->debugger->aboutToCall(f, this); } void ExecutionContext::leaveCallContext() @@ -327,6 +332,9 @@ void ExecutionContext::leaveCallContext() delete variableEnvironment; variableEnvironment = 0; + + if (engine->debugger) + engine->debugger->justLeft(this); } void ExecutionContext::initConstructorContext(ExecutionContext *parent, Value that, FunctionObject *f, Value *args, unsigned argc) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 28932e371c..302e5749ca 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -39,7 +39,6 @@ ** ****************************************************************************/ - #include "qmljs_objects.h" #include "qv4ir_p.h" #include "qv4isel_p.h" @@ -564,7 +563,7 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct return 0; } - Codegen cg; + Codegen cg(ctx->engine->debugger); globalCode = cg(program, &module, mode); if (globalCode) { // only generate other functions if global code generation succeeded. diff --git a/qmljs_objects.h b/qmljs_objects.h index 957a562d97..4e0af583d1 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -516,6 +516,8 @@ struct FunctionObject: Object { Value construct(ExecutionContext *context, Value *args, int argc); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); + virtual struct ScriptFunction *asScriptFunction() { return 0; } + protected: virtual Value call(ExecutionContext *ctx); virtual Value construct(ExecutionContext *ctx); @@ -537,6 +539,8 @@ struct ScriptFunction: FunctionObject { virtual Value call(ExecutionContext *ctx); virtual Value construct(ExecutionContext *ctx); + + virtual ScriptFunction *asScriptFunction() { return this; } }; struct EvalFunction : FunctionObject diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 1aa3c94f2c..6779f86d11 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -39,6 +39,7 @@ ** ****************************************************************************/ +#include "debugging.h" #include "qmljs_runtime.h" #include "qmljs_objects.h" #include "qv4ir_p.h" @@ -777,6 +778,9 @@ void __qmljs_throw(Value value, ExecutionContext *context) { assert(!context->engine->unwindStack.isEmpty()); + if (context->engine->debugger) + context->engine->debugger->aboutToThrow(&value); + ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); // clean up call contexts diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 0f3cedb55c..1980ed95a4 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qv4codegen_p.h" +#include "debugging.h" #include #include @@ -352,7 +353,7 @@ protected: QStack _envStack; }; -Codegen::Codegen() +Codegen::Codegen(Debugging::Debugger *debugger) : _module(0) , _function(0) , _block(0) @@ -363,6 +364,7 @@ Codegen::Codegen() , _env(0) , _loop(0) , _labelledStatement(0) + , _debugger(debugger) { } @@ -377,6 +379,12 @@ IR::Function *Codegen::operator()(Program *node, IR::Module *module, Mode mode) scan(node); IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, node->elements, mode); + if (_debugger) { + if (node->elements->element) { + SourceLocation loc = node->elements->element->firstSourceLocation(); + _debugger->setSourceLocation(globalCode, loc.startLine, loc.startColumn); + } + } foreach (IR::Function *function, _module->functions) { linearize(function); @@ -397,6 +405,8 @@ IR::Function *Codegen::operator()(AST::FunctionExpression *ast, IR::Module *modu scan(ast); IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + if (_debugger) + _debugger->setSourceLocation(function, ast->functionToken.startLine, ast->functionToken.startColumn); foreach (IR::Function *function, _module->functions) { linearize(function); @@ -1168,6 +1178,8 @@ bool Codegen::visit(FieldMemberExpression *ast) bool Codegen::visit(FunctionExpression *ast) { IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + if (_debugger) + _debugger->setSourceLocation(function, ast->functionToken.startLine, ast->functionToken.startColumn); _expr.code = _block->CLOSURE(function); return false; } @@ -1570,6 +1582,8 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, enterEnvironment(ast); IR::Function *function = _module->newFunction(name); + if (_debugger) + _debugger->addFunction(function); IR::BasicBlock *entryBlock = function->newBasicBlock(); IR::BasicBlock *exitBlock = function->newBasicBlock(IR::Function::DontInsertBlock); IR::BasicBlock *throwBlock = function->newBasicBlock(); @@ -1626,6 +1640,8 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, foreach (AST::FunctionDeclaration *f, _env->functions) { IR::Function *function = defineFunction(f->name.toString(), f, f->formals, f->body ? f->body->elements : 0); + if (_debugger) + _debugger->setSourceLocation(function, f->functionToken.startLine, f->functionToken.startColumn); if (! _env->parent) move(_block->NAME(f->name.toString(), f->identifierToken.startLine, f->identifierToken.startColumn), _block->CLOSURE(function)); diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 4952c690b8..27c910fd8b 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -50,10 +50,14 @@ namespace AST { class UiParameterList; } +namespace Debugging { +class Debugger; +} // namespace Debugging + class Codegen: protected AST::Visitor { public: - Codegen(); + Codegen(Debugging::Debugger *debugger); enum Mode { GlobalCode, @@ -330,6 +334,7 @@ private: AST::LabelledStatement *_labelledStatement; QHash _envMap; QHash _functionMap; + Debugging::Debugger *_debugger; class ScanFunctions; }; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 9fd95b94a0..7adb16ed15 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1892,7 +1892,7 @@ Value FunctionCtor::construct(ExecutionContext *ctx) IR::Module module; - Codegen cg; + Codegen cg(ctx->engine->debugger); IR::Function *irf = cg(fe, &module); EvalInstructionSelection *isel = ctx->engine->iselFactory->create(ctx->engine); diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 80c55d201a..5ea5df9d58 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -720,6 +720,7 @@ void LLVMInstructionSelection::visitClosure(IR::Closure *e) { llvm::Value *tmp = newLLVMTemp(_valueTy); llvm::Value *clos = getLLVMFunction(e->value); + assert("!broken: pass function name!"); CreateCall3(_llvmModule->getFunction("__qmljs_llvm_init_native_function"), _llvmFunction->arg_begin(), tmp, clos); _llvmValue = CreateLoad(tmp); diff --git a/v4.pro b/v4.pro index 1a6e2a41ab..24ab7c0d0e 100644 --- a/v4.pro +++ b/v4.pro @@ -23,7 +23,8 @@ SOURCES += main.cpp \ qv4array.cpp \ qv4isel_masm.cpp \ llvm_runtime.cpp \ - qv4isel_p.cpp + qv4isel_p.cpp \ + debugging.cpp HEADERS += \ qv4codegen_p.h \ @@ -39,7 +40,8 @@ HEADERS += \ qv4array_p.h \ qv4isel_masm_p.h \ qv4isel_p.h \ - qv4isel_util_p.h + qv4isel_util_p.h \ + debugging.h llvm { -- cgit v1.2.3 From f00cf7cfae716f5e5af2d0952c3a822744977811 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 29 Nov 2012 22:25:21 +0100 Subject: Give primeIds the amount of bits it requires Also mark the prime number array as const Change-Id: Ieae3e5d13decb0edf0d086780b323a12351a55d8 Reviewed-by: Erik Verbruggen --- qmljs_objects.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qmljs_objects.h b/qmljs_objects.h index 4e0af583d1..a35d59f447 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -373,7 +373,7 @@ private: inline int nextPrime() { // IMPORTANT: do not add more primes without checking if _primeIdx needs more bits! - static int primes[] = { + static const int primes[] = { 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853, 25717, 51437, 102877 }; @@ -390,8 +390,8 @@ private: PropertyTableEntry *_freeList; int _propertyCount; int _bucketCount; - int _primeIdx: 4; - int _allocated: 28; + int _primeIdx: 5; + int _allocated: 27; }; struct Object { -- cgit v1.2.3 From f26d5c36cff33cc8a968c87a69e9dddbb4278855 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 29 Nov 2012 22:37:25 +0100 Subject: Get rid of variableEnvironment Found out that the variableEnv is only required for two use cases: To expose the exception in the catch statement, and to create temporary environments for the with() statment. Both can be better handled differently and don't require the overhead of two environments. Change-Id: I149e1fd7bdfc3267544b141b6f94e46e42cf641b Reviewed-by: Erik Verbruggen --- debugging.cpp | 8 ++++---- llvm_runtime.cpp | 2 +- moth/qv4vme_moth.cpp | 14 +++++++------- qmljs_engine.cpp | 2 +- qmljs_environment.cpp | 20 +++++++++----------- qmljs_environment.h | 7 +++---- qmljs_objects.cpp | 7 +++---- qmljs_objects.h | 2 +- qv4ecmaobjects.cpp | 4 ++-- qv4isel_masm.cpp | 4 ++-- 10 files changed, 33 insertions(+), 37 deletions(-) diff --git a/debugging.cpp b/debugging.cpp index 741d5daee4..e55660e16c 100644 --- a/debugging.cpp +++ b/debugging.cpp @@ -50,16 +50,16 @@ FunctionState::~FunctionState() VM::Value *FunctionState::argument(unsigned idx) { - if (idx < _context->variableEnvironment->argumentCount) - return _context->variableEnvironment->arguments + idx; + if (idx < _context->lexicalEnvironment->argumentCount) + return _context->lexicalEnvironment->arguments + idx; else return 0; } VM::Value *FunctionState::local(unsigned idx) { - if (idx < _context->variableEnvironment->varCount) - return _context->variableEnvironment->locals + idx; + if (idx < _context->lexicalEnvironment->varCount) + return _context->lexicalEnvironment->locals + idx; else return 0; } diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 9205dd575c..c5d1fd0144 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -55,7 +55,7 @@ Value __qmljs_llvm_return(ExecutionContext */*ctx*/, Value *result) Value *__qmljs_llvm_get_argument(ExecutionContext *ctx, int index) { - return &ctx->variableEnvironment->arguments[index]; + return &ctx->lexicalEnvironment->arguments[index]; } void __qmljs_llvm_init_undefined(Value *result) diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 92e7c7c206..e71733d2a1 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -53,7 +53,7 @@ using namespace QQmlJS::Moth; static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVector &stack, int index) { - VM::DeclarativeEnvironment *varEnv = context->variableEnvironment; + VM::DeclarativeEnvironment *varEnv = context->lexicalEnvironment; #ifdef DO_TRACE_INSTR const char *kind; @@ -231,21 +231,21 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co } } #endif // DO_TRACE_INSTR - quint32 argStart = instr.args - context->variableEnvironment->varCount; + quint32 argStart = instr.args - context->lexicalEnvironment->varCount; TRACE(Call, "value index = %d, argStart = %d, argc = %d, result temp index = %d", instr.destIndex, argStart, instr.argc, instr.targetTempIndex); VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_call_value(context, VM::Value::undefinedValue(), TEMP(instr.destIndex), args, instr.argc); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) - quint32 argStart = instr.args - context->variableEnvironment->varCount; + quint32 argStart = instr.args - context->lexicalEnvironment->varCount; VM::Value *args = stack.data() + argStart; VM::Value base = TEMP(instr.baseTemp); TEMP(instr.targetTempIndex) = __qmljs_call_property(context, base, instr.name, args, instr.argc); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallBuiltin) - quint32 argStart = instr.args - context->variableEnvironment->varCount; + quint32 argStart = instr.args - context->lexicalEnvironment->varCount; VM::Value *args = stack.data() + argStart; void *buf; switch (instr.builtin) { @@ -325,20 +325,20 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinDeleteValue) MOTH_BEGIN_INSTR(CreateValue) - quint32 argStart = instr.args - context->variableEnvironment->varCount; + quint32 argStart = instr.args - context->lexicalEnvironment->varCount; VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_value(context, TEMP(instr.func), args, instr.argc); MOTH_END_INSTR(CreateValue) MOTH_BEGIN_INSTR(CreateProperty) - quint32 argStart = instr.args - context->variableEnvironment->varCount; + quint32 argStart = instr.args - context->lexicalEnvironment->varCount; VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_property(context, TEMP(instr.base), instr.name, args, instr.argc); MOTH_END_INSTR(CreateProperty) MOTH_BEGIN_INSTR(CreateActivationProperty) TRACE(inline, "property name = %s, argc = %d", instr.name->toQString().toUtf8().constData(), instr.argc); - quint32 argStart = instr.args - context->variableEnvironment->varCount; + quint32 argStart = instr.args - context->lexicalEnvironment->varCount; VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); MOTH_END_INSTR(CreateActivationProperty) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 1609559659..4ccbe4ea84 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -158,7 +158,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) // VM::Object *glo = newObject(/*rootContext*/); globalObject = Value::fromObject(glo); - rootContext->variableEnvironment->activation = glo; + rootContext->lexicalEnvironment->activation = glo; glo->__put__(rootContext, identifier(QStringLiteral("Object")), objectCtor); glo->__put__(rootContext, identifier(QStringLiteral("String")), stringCtor); diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index e9b55a89f7..a47698cfa6 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -215,9 +215,8 @@ void ExecutionContext::init(ExecutionEngine *eng) { engine = eng; parent = 0; - variableEnvironment = new DeclarativeEnvironment; - variableEnvironment->init(eng); - lexicalEnvironment = variableEnvironment; + lexicalEnvironment = new DeclarativeEnvironment; + lexicalEnvironment->init(eng); thisObject = Value::nullValue(); eng->exception = Value::undefinedValue(); } @@ -312,9 +311,8 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha engine = parent->engine; this->parent = parent; - variableEnvironment = new DeclarativeEnvironment; - variableEnvironment->init(f, args, argc); - lexicalEnvironment = variableEnvironment; + lexicalEnvironment = new DeclarativeEnvironment; + lexicalEnvironment->init(f, args, argc); thisObject = that; @@ -325,13 +323,13 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha void ExecutionContext::leaveCallContext() { // ## Should rather be handled by a the activation object having a ref to the environment - if (variableEnvironment->activation) { - delete[] variableEnvironment->locals; - variableEnvironment->locals = 0; + if (lexicalEnvironment->activation) { + delete[] lexicalEnvironment->locals; + lexicalEnvironment->locals = 0; } - delete variableEnvironment; - variableEnvironment = 0; + delete lexicalEnvironment; + lexicalEnvironment = 0; if (engine->debugger) engine->debugger->justLeft(this); diff --git a/qmljs_environment.h b/qmljs_environment.h index 2364556d9e..235d1bf283 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -112,7 +112,6 @@ struct ExecutionContext ExecutionContext *parent; // ### Should be a general environment DeclarativeEnvironment *lexicalEnvironment; - DeclarativeEnvironment *variableEnvironment; Value thisObject; void init(ExecutionEngine *eng); @@ -135,11 +134,11 @@ struct ExecutionContext void inplaceBitOp(Value value, String *name, BinOp op); bool deleteProperty(String *name); - inline uint argumentCount() const { return variableEnvironment->argumentCount; } + inline uint argumentCount() const { return lexicalEnvironment->argumentCount; } inline Value argument(unsigned int index = 0) { - if (index < variableEnvironment->argumentCount) - return variableEnvironment->arguments[index]; + if (index < lexicalEnvironment->argumentCount) + return lexicalEnvironment->arguments[index]; return Value::undefinedValue(); } }; diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 302e5749ca..9fa63faa8d 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -499,7 +499,6 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value ctx->parent = context; ctx->thisObject = context->thisObject; ctx->lexicalEnvironment = context->lexicalEnvironment; - ctx->variableEnvironment = context->variableEnvironment; } Value result = f->code(ctx, f->codeData); @@ -580,11 +579,11 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct __qmljs_throw_type_error(ctx); } - if (!ctx->variableEnvironment->activation) - ctx->variableEnvironment->activation = new QQmlJS::VM::Object(); + if (!ctx->lexicalEnvironment->activation) + ctx->lexicalEnvironment->activation = new QQmlJS::VM::Object(); foreach (const QString *local, globalCode->locals) { - ctx->variableEnvironment->activation->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); + ctx->lexicalEnvironment->activation->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); } return globalCode; } diff --git a/qmljs_objects.h b/qmljs_objects.h index a35d59f447..f136304009 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -500,7 +500,7 @@ struct FunctionObject: Object { bool strictMode; FunctionObject(ExecutionContext *scope) - : scope(scope->variableEnvironment) + : scope(scope->lexicalEnvironment) , name(0) , formalParameterList(0) , formalParameterCount(0) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 7adb16ed15..dfaf5f8b69 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1960,8 +1960,8 @@ Value FunctionPrototype::method_call(ExecutionContext *ctx) Value thisArg = ctx->argument(0); QVector args(ctx->argumentCount() ? ctx->argumentCount() - 1 : 0); if (ctx->argumentCount()) - qCopy(ctx->variableEnvironment->arguments + 1, - ctx->variableEnvironment->arguments + ctx->argumentCount(), args.begin()); + qCopy(ctx->lexicalEnvironment->arguments + 1, + ctx->lexicalEnvironment->arguments + ctx->argumentCount(), args.begin()); return __qmljs_call_value(ctx, thisArg, ctx->thisObject, args.data(), args.size()); } diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index a0262c8079..5f95442009 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -191,11 +191,11 @@ InstructionSelection::Pointer InstructionSelection::loadTempAddress(RegisterID r int32_t offset = 0; if (t->index < 0) { const int arg = -t->index - 1; - loadPtr(Address(ContextRegister, offsetof(ExecutionContext, variableEnvironment)), reg); + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, lexicalEnvironment)), reg); loadPtr(Address(reg, offsetof(DeclarativeEnvironment, arguments)), reg); offset = arg * sizeof(Value); } else if (t->index < _function->locals.size()) { - loadPtr(Address(ContextRegister, offsetof(ExecutionContext, variableEnvironment)), reg); + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, lexicalEnvironment)), reg); loadPtr(Address(reg, offsetof(DeclarativeEnvironment, locals)), reg); offset = t->index * sizeof(Value); } else { -- cgit v1.2.3 From d63043df6fa8e8624b4ab9c7a70f320df8156a4a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 30 Nov 2012 09:48:46 +0100 Subject: Clean up the DeclarativeEnvironment Add a pointer to the FunctionObject in there, and remove the duplication of the formal and local variable names. Change-Id: Id8017b3e167228292b5d351e02b8927f0bfb41eb Reviewed-by: Erik Verbruggen --- debugging.cpp | 5 ++-- moth/qv4vme_moth.cpp | 16 +++++------ qmljs_environment.cpp | 79 ++++++++++++++++++++++++++++++++------------------- qmljs_environment.h | 10 ++++--- qmljs_objects.cpp | 12 +++++--- qmljs_objects.h | 2 +- 6 files changed, 74 insertions(+), 50 deletions(-) diff --git a/debugging.cpp b/debugging.cpp index e55660e16c..a54c1a0678 100644 --- a/debugging.cpp +++ b/debugging.cpp @@ -58,10 +58,9 @@ VM::Value *FunctionState::argument(unsigned idx) VM::Value *FunctionState::local(unsigned idx) { - if (idx < _context->lexicalEnvironment->varCount) + if (idx < _context->lexicalEnvironment->variableCount()) return _context->lexicalEnvironment->locals + idx; - else - return 0; + return 0; } Debugger::Debugger(VM::ExecutionEngine *engine) diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index e71733d2a1..ff42684fac 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -80,13 +80,13 @@ static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVecto Q_ASSERT(varEnv->arguments); return varEnv->arguments + arg; - } else if (index < (int) varEnv->varCount) { + } else if (index < (int) varEnv->variableCount()) { Q_ASSERT(index >= 0); Q_ASSERT(varEnv->locals); return varEnv->locals + index; } else { - int off = index - varEnv->varCount; + int off = index - varEnv->variableCount(); Q_ASSERT(off >= 0); Q_ASSERT(off < stack.size()); @@ -231,21 +231,21 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co } } #endif // DO_TRACE_INSTR - quint32 argStart = instr.args - context->lexicalEnvironment->varCount; + quint32 argStart = instr.args - context->lexicalEnvironment->variableCount(); TRACE(Call, "value index = %d, argStart = %d, argc = %d, result temp index = %d", instr.destIndex, argStart, instr.argc, instr.targetTempIndex); VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_call_value(context, VM::Value::undefinedValue(), TEMP(instr.destIndex), args, instr.argc); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) - quint32 argStart = instr.args - context->lexicalEnvironment->varCount; + quint32 argStart = instr.args - context->lexicalEnvironment->variableCount(); VM::Value *args = stack.data() + argStart; VM::Value base = TEMP(instr.baseTemp); TEMP(instr.targetTempIndex) = __qmljs_call_property(context, base, instr.name, args, instr.argc); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallBuiltin) - quint32 argStart = instr.args - context->lexicalEnvironment->varCount; + quint32 argStart = instr.args - context->lexicalEnvironment->variableCount(); VM::Value *args = stack.data() + argStart; void *buf; switch (instr.builtin) { @@ -325,20 +325,20 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinDeleteValue) MOTH_BEGIN_INSTR(CreateValue) - quint32 argStart = instr.args - context->lexicalEnvironment->varCount; + quint32 argStart = instr.args - context->lexicalEnvironment->variableCount(); VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_value(context, TEMP(instr.func), args, instr.argc); MOTH_END_INSTR(CreateValue) MOTH_BEGIN_INSTR(CreateProperty) - quint32 argStart = instr.args - context->lexicalEnvironment->varCount; + quint32 argStart = instr.args - context->lexicalEnvironment->variableCount(); VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_property(context, TEMP(instr.base), instr.name, args, instr.argc); MOTH_END_INSTR(CreateProperty) MOTH_BEGIN_INSTR(CreateActivationProperty) TRACE(inline, "property name = %s, argc = %d", instr.name->toQString().toUtf8().constData(), instr.argc); - quint32 argStart = instr.args - context->lexicalEnvironment->varCount; + quint32 argStart = instr.args - context->lexicalEnvironment->variableCount(); VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); MOTH_END_INSTR(CreateActivationProperty) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index a47698cfa6..326ed931b3 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -77,13 +77,10 @@ void DeclarativeEnvironment::init(ExecutionEngine *e) { engine = e; outer = 0; + function = 0; arguments = 0; argumentCount = 0; locals = 0; - formals = 0; - formalCount = 0; - vars = 0; - varCount = 0; activation = 0; withObject = 0; strictMode = false; @@ -91,28 +88,25 @@ void DeclarativeEnvironment::init(ExecutionEngine *e) void DeclarativeEnvironment::init(FunctionObject *f, Value *args, uint argc) { - outer = f->scope; + function = f; + outer = function->scope; engine = outer->engine; strictMode = f->strictMode; - formals = f->formalParameterList; - formalCount = f->formalParameterCount; arguments = args; argumentCount = argc; - if (f->needsActivation || argc < formalCount){ - arguments = new Value[qMax(argc, formalCount)]; + if (function->needsActivation || argc < function->formalParameterCount){ + arguments = new Value[qMax(argc, function->formalParameterCount)]; if (argc) std::copy(args, args + argc, arguments); - if (argc < formalCount) - std::fill(arguments + argc, arguments + formalCount, Value::undefinedValue()); + if (argc < function->formalParameterCount) + std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); } - vars = f->varList; - varCount = f->varCount; - locals = varCount ? new Value[varCount] : 0; - if (varCount) - std::fill(locals, locals + varCount, Value::undefinedValue()); + locals = function->varCount ? new Value[function->varCount] : 0; + if (function->varCount) + std::fill(locals, locals + function->varCount, Value::undefinedValue()); - if (f->needsActivation) + if (function->needsActivation) activation = engine->newActivationObject(this); else activation = 0; @@ -122,12 +116,15 @@ void DeclarativeEnvironment::init(FunctionObject *f, Value *args, uint argc) bool DeclarativeEnvironment::hasBinding(String *name) const { - for (unsigned int i = 0; i < varCount; ++i) { - if (__qmljs_string_equal(vars[i], name)) + if (!function) + return false; + + for (unsigned int i = 0; i < function->varCount; ++i) { + if (__qmljs_string_equal(function->varList[i], name)) return true; } - for (unsigned int i = 0; i < formalCount; ++i) { - if (__qmljs_string_equal(formals[i], name)) + for (unsigned int i = 0; i < function->formalParameterCount; ++i) { + if (__qmljs_string_equal(function->formalParameterList[i], name)) return true; } return false; @@ -152,16 +149,17 @@ void DeclarativeEnvironment::createMutableBinding(ExecutionContext *ctx, String void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool strict) { Q_UNUSED(strict); + assert(function); // ### throw if strict is true, and it would change an immutable binding - for (unsigned int i = 0; i < varCount; ++i) { - if (__qmljs_string_equal(vars[i], name)) { + for (unsigned int i = 0; i < variableCount(); ++i) { + if (__qmljs_string_equal(variables()[i], name)) { locals[i] = value; return; } } - for (unsigned int i = 0; i < formalCount; ++i) { - if (__qmljs_string_equal(formals[i], name)) { + for (unsigned int i = 0; i < formalCount(); ++i) { + if (__qmljs_string_equal(formals()[i], name)) { arguments[i] = value; return; } @@ -172,13 +170,14 @@ void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool s Value DeclarativeEnvironment::getBindingValue(String *name, bool strict) const { Q_UNUSED(strict); + assert(function); - for (unsigned int i = 0; i < varCount; ++i) { - if (__qmljs_string_equal(vars[i], name)) + for (unsigned int i = 0; i < variableCount(); ++i) { + if (__qmljs_string_equal(variables()[i], name)) return locals[i]; } - for (unsigned int i = 0; i < formalCount; ++i) { - if (__qmljs_string_equal(formals[i], name)) + for (unsigned int i = 0; i < formalCount(); ++i) { + if (__qmljs_string_equal(formals()[i], name)) return arguments[i]; } assert(false); @@ -211,6 +210,27 @@ void DeclarativeEnvironment::popWithObject() delete w; } +String **DeclarativeEnvironment::formals() const +{ + return function ? function->formalParameterList : 0; +} + +unsigned int DeclarativeEnvironment::formalCount() const +{ + return function ? function->formalParameterCount : 0; +} + +String **DeclarativeEnvironment::variables() const +{ + return function ? function->varList : 0; +} + +unsigned int DeclarativeEnvironment::variableCount() const +{ + return function ? function->varCount : 0; +} + + void ExecutionContext::init(ExecutionEngine *eng) { engine = eng; @@ -357,6 +377,5 @@ void ExecutionContext::wireUpPrototype(FunctionObject *f) thisObject.objectValue()->prototype = engine->objectPrototype; } - } // namespace VM } // namespace QQmlJS diff --git a/qmljs_environment.h b/qmljs_environment.h index 235d1bf283..6caada9ba3 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -76,13 +76,15 @@ struct DeclarativeEnvironment ExecutionEngine *engine; DeclarativeEnvironment *outer; + FunctionObject *function; + Value *arguments; unsigned int argumentCount; Value *locals; - String **formals; - unsigned int formalCount; - String **vars; - unsigned int varCount; + String **formals() const; + unsigned int formalCount() const; + String **variables() const; + unsigned int variableCount() const; bool strictMode; Object *activation; diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 9fa63faa8d..09e937dbab 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -434,6 +434,10 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, IR::Function *function) : FunctionObject(scope) , function(function) { + // global function + if (!scope) + return; + if (function->name) name = scope->engine->identifier(*function->name); needsActivation = function->needsActivation(); @@ -671,8 +675,8 @@ Value ScriptFunction::construct(VM::ExecutionContext *ctx) PropertyDescriptor *ActivationObject::__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill) { if (context) { - for (unsigned int i = 0; i < context->varCount; ++i) { - String *var = context->vars[i]; + for (unsigned int i = 0; i < context->variableCount(); ++i) { + String *var = context->variables()[i]; if (__qmljs_string_equal(var, name)) { *to_fill = PropertyDescriptor::fromValue(context->locals[i]); to_fill->writable = PropertyDescriptor::Set; @@ -680,8 +684,8 @@ PropertyDescriptor *ActivationObject::__getPropertyDescriptor__(ExecutionContext return to_fill; } } - for (unsigned int i = 0; i < context->formalCount; ++i) { - String *formal = context->formals[i]; + for (unsigned int i = 0; i < context->formalCount(); ++i) { + String *formal = context->formals()[i]; if (__qmljs_string_equal(formal, name)) { *to_fill = PropertyDescriptor::fromValue(context->arguments[i]); to_fill->writable = PropertyDescriptor::Set; diff --git a/qmljs_objects.h b/qmljs_objects.h index f136304009..a74f6198ec 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -500,7 +500,7 @@ struct FunctionObject: Object { bool strictMode; FunctionObject(ExecutionContext *scope) - : scope(scope->lexicalEnvironment) + : scope(scope ? scope->lexicalEnvironment : 0) , name(0) , formalParameterList(0) , formalParameterCount(0) -- cgit v1.2.3 From d2eb5c6510bb05f6cd7e7bd06e9957bdf85a515f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 1 Dec 2012 23:59:53 +0100 Subject: Simplify push(IR::Const*) Use the convertToValue() function instead of manually trying to convert the IR::Const to a VM::Value. Change-Id: Ib669def3c2ef3dfba6cabb0b0c0b3c1d014a13ca Reviewed-by: Lars Knoll --- qv4isel_masm_p.h | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 15ba5b0ad0..1b3cee1d37 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -394,26 +394,7 @@ private: void push(IR::Const* c) { - VM::Value v; - switch (c->type) { - case IR::NullType: - v = VM::Value::nullValue(); - break; - case IR::UndefinedType: - v = VM::Value::undefinedValue(); - break; - case IR::BoolType: - v = VM::Value::fromBoolean(c->value != 0); - break; - case IR::NumberType: { - int ival = (int)c->value; - if (ival == c->value) { - v = VM::Value::fromInt32(ival); - } else { - v = VM::Value::fromDouble(c->value); - } - } - } + VM::Value v = convertToValue(c); push(v); } -- cgit v1.2.3 From 4c2f11c67dadb684a602ecad397763804b62adf9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 2 Dec 2012 01:04:34 +0100 Subject: [masm] First implementation of inline addition / subtraction Change-Id: I4e54ae0feded8d99737245c870e0dfbb9b80247e Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- qv4isel_masm_p.h | 10 ++++++ 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 5f95442009..37ad3fe9bc 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -484,6 +484,8 @@ void InstructionSelection::visitMove(IR::Move *s) (b->right->asTemp() || b->right->asConst())) { Value (*op)(const Value, const Value, ExecutionContext *) = 0; const char* opName = 0; + MemBinOpWithOverFlow inlineMemBinOp = 0; + ImmBinOpWithOverFlow inlineImmBinOp = 0; switch ((IR::AluOp) b->op) { case IR::OpInvalid: @@ -498,8 +500,18 @@ void InstructionSelection::visitMove(IR::Move *s) case IR::OpBitAnd: setOp(op, opName, __qmljs_bit_and); break; case IR::OpBitOr: setOp(op, opName, __qmljs_bit_or); break; case IR::OpBitXor: setOp(op, opName, __qmljs_bit_xor); break; - case IR::OpAdd: setOp(op, opName, __qmljs_add); break; - case IR::OpSub: setOp(op, opName, __qmljs_sub); break; + case IR::OpAdd: { + setOp(op, opName, __qmljs_add); + inlineMemBinOp = &JSC::MacroAssembler::branchAdd32; + inlineImmBinOp = &JSC::MacroAssembler::branchAdd32; + break; + } + case IR::OpSub: { + setOp(op, opName, __qmljs_sub); + inlineMemBinOp = &JSC::MacroAssembler::branchSub32; + inlineImmBinOp = &JSC::MacroAssembler::branchSub32; + break; + } case IR::OpMul: setOp(op, opName, __qmljs_mul); break; case IR::OpDiv: setOp(op, opName, __qmljs_div); break; case IR::OpMod: setOp(op, opName, __qmljs_mod); break; @@ -524,7 +536,11 @@ void InstructionSelection::visitMove(IR::Move *s) } if (op) { - generateFunctionCallImp(t, opName, op, b->left, b->right, ContextRegister); + if (inlineMemBinOp && inlineImmBinOp + && generateArithmeticIntegerInlineBinOp(t, b->left, b->right, inlineMemBinOp, inlineImmBinOp, op, opName)) + return; + else + generateFunctionCallImp(t, opName, op, b->left, b->right, ContextRegister); } return; } @@ -803,3 +819,75 @@ void InstructionSelection::copyValue(Result result, Source source) storeDouble(FPGpr0, result); #endif } + +bool InstructionSelection::generateArithmeticIntegerInlineBinOp(IR::Temp* target, IR::Expr* left, IR::Expr* right, + MemBinOpWithOverFlow memOp, ImmBinOpWithOverFlow immOp, FallbackOp fallbackOp, const char* fallbackOpName) +{ + VM::Value leftConst; + if (left->asConst()) { + leftConst = convertToValue(left->asConst()); + if (!leftConst.tryIntegerConversion()) + return false; + } + VM::Value rightConst; + if (right->asConst()) { + rightConst = convertToValue(right->asConst()); + if (!rightConst.tryIntegerConversion()) + return false; + } + + Jump leftTypeCheck; + if (left->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, left->asTemp()); + typeAddress.offset += offsetof(VM::Value, tag); + leftTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); + } + + Jump rightTypeCheck; + if (right->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, right->asTemp()); + typeAddress.offset += offsetof(VM::Value, tag); + rightTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); + } + + if (left->asTemp()) { + Address leftValue = loadTempAddress(ScratchRegister, left->asTemp()); + leftValue.offset += offsetof(VM::Value, int_32); + load32(leftValue, IntegerOpRegister); + } else { // left->asConst() + move(TrustedImm32(leftConst.integerValue()), IntegerOpRegister); + } + + Jump overflowCheck; + + if (right->asTemp()) { + Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); + rightValue.offset += offsetof(VM::Value, int_32); + + overflowCheck = (this->*memOp)(Overflow, rightValue, IntegerOpRegister); + } else { // right->asConst() + VM::Value value = convertToValue(right->asConst()); + overflowCheck = (this->*immOp)(Overflow, TrustedImm32(value.integerValue()), IntegerOpRegister); + } + + Address resultAddr = loadTempAddress(ScratchRegister, target); + Address resultValueAddr = resultAddr; + resultValueAddr.offset += offsetof(VM::Value, int_32); + store32(IntegerOpRegister, resultValueAddr); + + Address resultTypeAddr = resultAddr; + resultTypeAddr.offset += offsetof(VM::Value, tag); + store32(TrustedImm32(VM::Value::_Integer_Type), resultTypeAddr); + + Jump finishBinOp = jump(); + + if (leftTypeCheck.isSet()) + leftTypeCheck.link(this); + if (rightTypeCheck.isSet()) + rightTypeCheck.link(this); + overflowCheck.link(this); + generateFunctionCallImp(target, fallbackOpName, fallbackOp, left, right, ContextRegister); + + finishBinOp.link(this); + return true; +} diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 1b3cee1d37..55e75d3c03 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -77,6 +77,7 @@ protected: static const RegisterID ScratchRegister = JSC::X86Registers::ecx; static const RegisterID CalleeSavedFirstRegister = ScratchRegister; static const RegisterID CalleeSavedLastRegister = ScratchRegister; + static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; static const int RegisterSize = 4; @@ -97,6 +98,7 @@ protected: static const RegisterID ContextRegister = JSC::X86Registers::r14; static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; static const RegisterID ScratchRegister = JSC::X86Registers::r10; + static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; static const int RegisterSize = 8; @@ -126,6 +128,7 @@ protected: static const RegisterID ScratchRegister = JSC::ARMRegisters::r6; static const RegisterID CalleeSavedFirstRegister = JSC::ARMRegisters::r4; static const RegisterID CalleeSavedLastRegister = JSC::ARMRegisters::r11; + static const RegisterID IntegerOpRegister = JSC::X86Registers::r0; static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; static const int RegisterSize = 4; @@ -608,6 +611,13 @@ private: #endif } + typedef VM::Value (*FallbackOp)(const VM::Value, const VM::Value, VM::ExecutionContext*); + + typedef Jump (JSC::MacroAssembler::*MemBinOpWithOverFlow)(ResultCondition, Address, RegisterID); + typedef Jump (JSC::MacroAssembler::*ImmBinOpWithOverFlow)(ResultCondition, TrustedImm32, RegisterID); + bool generateArithmeticIntegerInlineBinOp(IR::Temp* target, IR::Expr* left, IR::Expr* right, + MemBinOpWithOverFlow memOp, ImmBinOpWithOverFlow immOp, FallbackOp fallback, const char* fallbackOpName); + VM::ExecutionEngine *_engine; IR::Function *_function; IR::BasicBlock *_block; -- cgit v1.2.3 From b589811f735ffde5dd1272bd9dd69599de421000 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 30 Nov 2012 23:15:55 +0100 Subject: Remove the outer member in declarativeEnvironment The function already has it as the scope parameter, so don't duplicate the data. Change-Id: Iadd0418cafa9ad273db11c06c44086ac64b1e5bf Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 15 +++++++++------ qmljs_environment.h | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 326ed931b3..76f1c8c4f2 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -76,7 +76,6 @@ String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const void DeclarativeEnvironment::init(ExecutionEngine *e) { engine = e; - outer = 0; function = 0; arguments = 0; argumentCount = 0; @@ -89,8 +88,7 @@ void DeclarativeEnvironment::init(ExecutionEngine *e) void DeclarativeEnvironment::init(FunctionObject *f, Value *args, uint argc) { function = f; - outer = function->scope; - engine = outer->engine; + engine = f->scope->engine; strictMode = f->strictMode; arguments = args; @@ -210,6 +208,11 @@ void DeclarativeEnvironment::popWithObject() delete w; } +DeclarativeEnvironment *DeclarativeEnvironment::outer() const +{ + return function ? function->scope : 0; +} + String **DeclarativeEnvironment::formals() const { return function ? function->formalParameterList : 0; @@ -243,7 +246,7 @@ void ExecutionContext::init(ExecutionEngine *eng) PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp) { - for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer) { + for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer()) { if (ctx->withObject) { DeclarativeEnvironment::With *w = ctx->withObject; while (w) { @@ -262,7 +265,7 @@ PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, Pro bool ExecutionContext::deleteProperty(String *name) { - for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer) { + for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer()) { if (ctx->withObject) { DeclarativeEnvironment::With *w = ctx->withObject; while (w) { @@ -282,7 +285,7 @@ bool ExecutionContext::deleteProperty(String *name) void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) { - for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer) { + for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer()) { if (ctx->activation) { if (ctx->activation->inplaceBinOp(value, name, op, this)) return; diff --git a/qmljs_environment.h b/qmljs_environment.h index 6caada9ba3..d780c56ae6 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -74,8 +74,6 @@ struct DiagnosticMessage struct DeclarativeEnvironment { ExecutionEngine *engine; - DeclarativeEnvironment *outer; - FunctionObject *function; Value *arguments; @@ -87,6 +85,8 @@ struct DeclarativeEnvironment unsigned int variableCount() const; bool strictMode; + DeclarativeEnvironment *outer() const; + Object *activation; struct With { Object *object; -- cgit v1.2.3 From 2104e7fdcb9c16cabc58e93f0cd1d11e36bdca34 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 1 Dec 2012 14:05:07 +0100 Subject: Remove the DeclarativeEnvironment class again The class is a specification detail that we can implement in a more performant way. ExecutionContext now contains everything needed again. Change-Id: Ideb5f04eeeecaf2b8543676c626e3943e4d6d7a1 Reviewed-by: Simon Hausmann --- debugging.cpp | 8 +-- llvm_runtime.cpp | 2 +- main.cpp | 6 +-- moth/qv4vme_moth.cpp | 28 +++++------ qmljs_engine.cpp | 4 +- qmljs_engine.h | 4 +- qmljs_environment.cpp | 73 ++++++++++++--------------- qmljs_environment.h | 29 ++++------- qmljs_objects.cpp | 26 +++++----- qmljs_objects.h | 8 +-- qmljs_runtime.cpp | 18 +++---- qv4ecmaobjects.cpp | 134 +++++++++++++++++++++++++------------------------- qv4isel_masm.cpp | 6 +-- 13 files changed, 159 insertions(+), 187 deletions(-) diff --git a/debugging.cpp b/debugging.cpp index a54c1a0678..207eab71e8 100644 --- a/debugging.cpp +++ b/debugging.cpp @@ -50,16 +50,16 @@ FunctionState::~FunctionState() VM::Value *FunctionState::argument(unsigned idx) { - if (idx < _context->lexicalEnvironment->argumentCount) - return _context->lexicalEnvironment->arguments + idx; + if (idx < _context->argumentCount) + return _context->arguments + idx; else return 0; } VM::Value *FunctionState::local(unsigned idx) { - if (idx < _context->lexicalEnvironment->variableCount()) - return _context->lexicalEnvironment->locals + idx; + if (idx < _context->variableCount()) + return _context->locals + idx; return 0; } diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index c5d1fd0144..76abd793d1 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -55,7 +55,7 @@ Value __qmljs_llvm_return(ExecutionContext */*ctx*/, Value *result) Value *__qmljs_llvm_get_argument(ExecutionContext *ctx, int index) { - return &ctx->lexicalEnvironment->arguments[index]; + return &ctx->arguments[index]; } void __qmljs_llvm_init_undefined(Value *result) diff --git a/main.cpp b/main.cpp index 8418dc16fd..9090cd2a9f 100644 --- a/main.cpp +++ b/main.cpp @@ -75,7 +75,7 @@ struct Print: FunctionObject virtual Value call(ExecutionContext *ctx) { - for (unsigned int i = 0; i < ctx->argumentCount(); ++i) { + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { String *s = ctx->argument(i).toString(ctx); if (i) std::cout << ' '; @@ -96,7 +96,7 @@ struct TestHarnessError: FunctionObject { errorOccurred = true; - for (unsigned int i = 0; i < ctx->argumentCount(); ++i) { + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { String *s = ctx->argument(i).toString(ctx); if (i) std::cerr << ' '; @@ -357,7 +357,7 @@ int main(int argc, char *argv[]) QQmlJS::IR::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode); if (!f) continue; - ctx->lexicalEnvironment->strictMode = f->isStrict; + ctx->strictMode = f->isStrict; if (debugger) debugger->aboutToCall(0, ctx); QQmlJS::VM::Value result = f->code(ctx, f->codeData); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index ff42684fac..138d81a635 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -53,8 +53,6 @@ using namespace QQmlJS::Moth; static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVector &stack, int index) { - VM::DeclarativeEnvironment *varEnv = context->lexicalEnvironment; - #ifdef DO_TRACE_INSTR const char *kind; int pos; @@ -76,17 +74,17 @@ static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVecto const int arg = -index - 1; Q_ASSERT(arg >= 0); - Q_ASSERT((unsigned) arg < varEnv->argumentCount); - Q_ASSERT(varEnv->arguments); + Q_ASSERT((unsigned) arg < context->argumentCount); + Q_ASSERT(context->arguments); - return varEnv->arguments + arg; - } else if (index < (int) varEnv->variableCount()) { + return context->arguments + arg; + } else if (index < (int) context->variableCount()) { Q_ASSERT(index >= 0); - Q_ASSERT(varEnv->locals); + Q_ASSERT(context->locals); - return varEnv->locals + index; + return context->locals + index; } else { - int off = index - varEnv->variableCount(); + int off = index - context->variableCount(); Q_ASSERT(off >= 0); Q_ASSERT(off < stack.size()); @@ -231,21 +229,21 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co } } #endif // DO_TRACE_INSTR - quint32 argStart = instr.args - context->lexicalEnvironment->variableCount(); + quint32 argStart = instr.args - context->variableCount(); TRACE(Call, "value index = %d, argStart = %d, argc = %d, result temp index = %d", instr.destIndex, argStart, instr.argc, instr.targetTempIndex); VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_call_value(context, VM::Value::undefinedValue(), TEMP(instr.destIndex), args, instr.argc); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) - quint32 argStart = instr.args - context->lexicalEnvironment->variableCount(); + quint32 argStart = instr.args - context->variableCount(); VM::Value *args = stack.data() + argStart; VM::Value base = TEMP(instr.baseTemp); TEMP(instr.targetTempIndex) = __qmljs_call_property(context, base, instr.name, args, instr.argc); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallBuiltin) - quint32 argStart = instr.args - context->lexicalEnvironment->variableCount(); + quint32 argStart = instr.args - context->variableCount(); VM::Value *args = stack.data() + argStart; void *buf; switch (instr.builtin) { @@ -325,20 +323,20 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinDeleteValue) MOTH_BEGIN_INSTR(CreateValue) - quint32 argStart = instr.args - context->lexicalEnvironment->variableCount(); + quint32 argStart = instr.args - context->variableCount(); VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_value(context, TEMP(instr.func), args, instr.argc); MOTH_END_INSTR(CreateValue) MOTH_BEGIN_INSTR(CreateProperty) - quint32 argStart = instr.args - context->lexicalEnvironment->variableCount(); + quint32 argStart = instr.args - context->variableCount(); VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_property(context, TEMP(instr.base), instr.name, args, instr.argc); MOTH_END_INSTR(CreateProperty) MOTH_BEGIN_INSTR(CreateActivationProperty) TRACE(inline, "property name = %s, argc = %d", instr.name->toQString().toUtf8().constData(), instr.argc); - quint32 argStart = instr.args - context->lexicalEnvironment->variableCount(); + quint32 argStart = instr.args - context->variableCount(); VM::Value *args = stack.data() + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); MOTH_END_INSTR(CreateActivationProperty) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 4ccbe4ea84..6c5edd16e2 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -158,7 +158,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) // VM::Object *glo = newObject(/*rootContext*/); globalObject = Value::fromObject(glo); - rootContext->lexicalEnvironment->activation = glo; + rootContext->activation = glo; glo->__put__(rootContext, identifier(QStringLiteral("Object")), objectCtor); glo->__put__(rootContext, identifier(QStringLiteral("String")), stringCtor); @@ -348,7 +348,7 @@ Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) return object; } -Object *ExecutionEngine::newActivationObject(DeclarativeEnvironment *ctx) +Object *ExecutionEngine::newActivationObject(ExecutionContext *ctx) { return new ActivationObject(ctx); } diff --git a/qmljs_engine.h b/qmljs_engine.h index 9b91f01c04..a6f1808676 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -138,7 +138,7 @@ struct ExecutionEngine struct ExceptionHandler { ExecutionContext *context; - DeclarativeEnvironment::With *with; + ExecutionContext::With *with; const uchar *code; // Interpreter state int targetTempIndex; // Interpreter state jmp_buf stackFrame; @@ -186,7 +186,7 @@ struct ExecutionEngine Object *newErrorObject(const Value &value); Object *newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message); Object *newMathObject(ExecutionContext *ctx); - Object *newActivationObject(DeclarativeEnvironment *ctx); + Object *newActivationObject(ExecutionContext *ctx); Object *newForEachIteratorObject(Object *o); }; diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 76f1c8c4f2..ebbe7d3d87 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -73,19 +73,7 @@ String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const return ctx->engine->newString(msg); } -void DeclarativeEnvironment::init(ExecutionEngine *e) -{ - engine = e; - function = 0; - arguments = 0; - argumentCount = 0; - locals = 0; - activation = 0; - withObject = 0; - strictMode = false; -} - -void DeclarativeEnvironment::init(FunctionObject *f, Value *args, uint argc) +void ExecutionContext::init(FunctionObject *f, Value *args, uint argc) { function = f; engine = f->scope->engine; @@ -112,7 +100,7 @@ void DeclarativeEnvironment::init(FunctionObject *f, Value *args, uint argc) withObject = 0; } -bool DeclarativeEnvironment::hasBinding(String *name) const +bool ExecutionContext::hasBinding(String *name) const { if (!function) return false; @@ -128,7 +116,7 @@ bool DeclarativeEnvironment::hasBinding(String *name) const return false; } -void DeclarativeEnvironment::createMutableBinding(ExecutionContext *ctx, String *name, bool deletable) +void ExecutionContext::createMutableBinding(ExecutionContext *ctx, String *name, bool deletable) { if (!activation) activation = engine->newActivationObject(this); @@ -144,7 +132,7 @@ void DeclarativeEnvironment::createMutableBinding(ExecutionContext *ctx, String activation->__defineOwnProperty__(ctx, name, &desc); } -void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool strict) +void ExecutionContext::setMutableBinding(String *name, Value value, bool strict) { Q_UNUSED(strict); assert(function); @@ -165,7 +153,7 @@ void DeclarativeEnvironment::setMutableBinding(String *name, Value value, bool s assert(false); } -Value DeclarativeEnvironment::getBindingValue(String *name, bool strict) const +Value ExecutionContext::getBindingValue(String *name, bool strict) const { Q_UNUSED(strict); assert(function); @@ -181,17 +169,17 @@ Value DeclarativeEnvironment::getBindingValue(String *name, bool strict) const assert(false); } -bool DeclarativeEnvironment::deleteBinding(ExecutionContext *ctx, String *name) +bool ExecutionContext::deleteBinding(ExecutionContext *ctx, String *name) { if (activation) activation->__delete__(ctx, name); - if (ctx->lexicalEnvironment->strictMode) + if (ctx->strictMode) __qmljs_throw_type_error(ctx); return false; } -void DeclarativeEnvironment::pushWithObject(Object *with) +void ExecutionContext::pushWithObject(Object *with) { With *w = new With; w->next = withObject; @@ -199,7 +187,7 @@ void DeclarativeEnvironment::pushWithObject(Object *with) withObject = w; } -void DeclarativeEnvironment::popWithObject() +void ExecutionContext::popWithObject() { assert(withObject); @@ -208,27 +196,27 @@ void DeclarativeEnvironment::popWithObject() delete w; } -DeclarativeEnvironment *DeclarativeEnvironment::outer() const +ExecutionContext *ExecutionContext::outer() const { return function ? function->scope : 0; } -String **DeclarativeEnvironment::formals() const +String **ExecutionContext::formals() const { return function ? function->formalParameterList : 0; } -unsigned int DeclarativeEnvironment::formalCount() const +unsigned int ExecutionContext::formalCount() const { return function ? function->formalParameterCount : 0; } -String **DeclarativeEnvironment::variables() const +String **ExecutionContext::variables() const { return function ? function->varList : 0; } -unsigned int DeclarativeEnvironment::variableCount() const +unsigned int ExecutionContext::variableCount() const { return function ? function->varCount : 0; } @@ -238,17 +226,24 @@ void ExecutionContext::init(ExecutionEngine *eng) { engine = eng; parent = 0; - lexicalEnvironment = new DeclarativeEnvironment; - lexicalEnvironment->init(eng); thisObject = Value::nullValue(); + + function = 0; + arguments = 0; + argumentCount = 0; + locals = 0; + strictMode = false; + activation = 0; + withObject = 0; + eng->exception = Value::undefinedValue(); } PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp) { - for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer()) { + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { if (ctx->withObject) { - DeclarativeEnvironment::With *w = ctx->withObject; + With *w = ctx->withObject; while (w) { if (PropertyDescriptor *pd = w->object->__getPropertyDescriptor__(this, name, tmp)) return pd; @@ -265,9 +260,9 @@ PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, Pro bool ExecutionContext::deleteProperty(String *name) { - for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer()) { + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { if (ctx->withObject) { - DeclarativeEnvironment::With *w = ctx->withObject; + ExecutionContext::With *w = ctx->withObject; while (w) { if (w->object->__hasProperty__(this, name)) w->object->__delete__(this, name); @@ -285,7 +280,7 @@ bool ExecutionContext::deleteProperty(String *name) void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) { - for (DeclarativeEnvironment *ctx = lexicalEnvironment; ctx; ctx = ctx->outer()) { + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { if (ctx->activation) { if (ctx->activation->inplaceBinOp(value, name, op, this)) return; @@ -334,8 +329,7 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha engine = parent->engine; this->parent = parent; - lexicalEnvironment = new DeclarativeEnvironment; - lexicalEnvironment->init(f, args, argc); + init(f, args, argc); thisObject = that; @@ -346,14 +340,11 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha void ExecutionContext::leaveCallContext() { // ## Should rather be handled by a the activation object having a ref to the environment - if (lexicalEnvironment->activation) { - delete[] lexicalEnvironment->locals; - lexicalEnvironment->locals = 0; + if (activation) { + delete[] locals; + locals = 0; } - delete lexicalEnvironment; - lexicalEnvironment = 0; - if (engine->debugger) engine->debugger->justLeft(this); } diff --git a/qmljs_environment.h b/qmljs_environment.h index d780c56ae6..a34774ed96 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -69,23 +69,25 @@ struct DiagnosticMessage String *buildFullMessage(ExecutionContext *ctx) const; }; -// This merges LexicalEnvironment and EnvironmentRecord from -// Sec. 10.2 into one class -struct DeclarativeEnvironment +struct ExecutionContext { ExecutionEngine *engine; + ExecutionContext *parent; + Value thisObject; + FunctionObject *function; Value *arguments; unsigned int argumentCount; Value *locals; + String **formals() const; unsigned int formalCount() const; String **variables() const; unsigned int variableCount() const; - bool strictMode; + ExecutionContext *outer() const; - DeclarativeEnvironment *outer() const; + bool strictMode; Object *activation; struct With { @@ -103,20 +105,8 @@ struct DeclarativeEnvironment Value getBindingValue(String *name, bool strict) const; bool deleteBinding(ExecutionContext *ctx, String *name); - // ### needs a bit of work in exception handlers void pushWithObject(Object *with); void popWithObject(); -}; - -struct ExecutionContext -{ - ExecutionEngine *engine; - ExecutionContext *parent; - // ### Should be a general environment - DeclarativeEnvironment *lexicalEnvironment; - Value thisObject; - - void init(ExecutionEngine *eng); void initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc); void leaveCallContext(); @@ -136,11 +126,10 @@ struct ExecutionContext void inplaceBitOp(Value value, String *name, BinOp op); bool deleteProperty(String *name); - inline uint argumentCount() const { return lexicalEnvironment->argumentCount; } inline Value argument(unsigned int index = 0) { - if (index < lexicalEnvironment->argumentCount) - return lexicalEnvironment->arguments[index]; + if (index < argumentCount) + return arguments[index]; return Value::undefinedValue(); } }; diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 09e937dbab..75fab4b5fc 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -211,7 +211,7 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) } reject: - if (ctx->lexicalEnvironment->strictMode) + if (ctx->strictMode) __qmljs_throw_type_error(ctx); } @@ -233,7 +233,7 @@ bool Object::__delete__(ExecutionContext *ctx, String *name) members->remove(entry); return true; } - if (ctx->lexicalEnvironment->strictMode) + if (ctx->strictMode) __qmljs_throw_type_error(ctx); return false; } @@ -317,7 +317,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property return true; reject: qDebug() << "___put__ rejected" << name->toQString(); - if (ctx->lexicalEnvironment->strictMode) + if (ctx->strictMode) __qmljs_throw_type_error(ctx); return false; } @@ -487,7 +487,7 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value if (!f) return Value::undefinedValue(); - bool strict = f->isStrict || context->lexicalEnvironment->strictMode; + bool strict = f->isStrict || context->strictMode; ExecutionContext k, *ctx; if (!directCall) { @@ -497,12 +497,8 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value ctx = &k; ctx->initCallContext(context, context->thisObject, this, args, argc); } else { - ctx = &k; - // a clone of the surrounding context - ctx->engine = context->engine; - ctx->parent = context; - ctx->thisObject = context->thisObject; - ctx->lexicalEnvironment = context->lexicalEnvironment; + // use the surrounding context + ctx = context; } Value result = f->code(ctx, f->codeData); @@ -583,11 +579,11 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct __qmljs_throw_type_error(ctx); } - if (!ctx->lexicalEnvironment->activation) - ctx->lexicalEnvironment->activation = new QQmlJS::VM::Object(); + if (!ctx->activation) + ctx->activation = new QQmlJS::VM::Object(); foreach (const QString *local, globalCode->locals) { - ctx->lexicalEnvironment->activation->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); + ctx->activation->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); } return globalCode; } @@ -712,7 +708,7 @@ PropertyDescriptor *ActivationObject::__getPropertyDescriptor__(ExecutionContext Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name) { if (name->isEqualTo(ctx->engine->id_length)) - return Value::fromInt32(context->argumentCount()); + return Value::fromInt32(context->argumentCount); return Object::__get__(ctx, name); } @@ -720,7 +716,7 @@ PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(ExecutionContext { if (context) { const quint32 i = Value::fromString(name).toUInt32(ctx); - if (i < context->argumentCount()) { + if (i < context->argumentCount) { *to_fill = PropertyDescriptor::fromValue(context->argument(i)); to_fill->writable = PropertyDescriptor::Unset; to_fill->enumberable = PropertyDescriptor::Unset; diff --git a/qmljs_objects.h b/qmljs_objects.h index a74f6198ec..927b9f32d1 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -490,7 +490,7 @@ struct ArrayObject: Object { }; struct FunctionObject: Object { - DeclarativeEnvironment *scope; + ExecutionContext *scope; String *name; String **formalParameterList; unsigned int formalParameterCount; @@ -500,7 +500,7 @@ struct FunctionObject: Object { bool strictMode; FunctionObject(ExecutionContext *scope) - : scope(scope ? scope->lexicalEnvironment : 0) + : scope(scope) , name(0) , formalParameterList(0) , formalParameterCount(0) @@ -633,9 +633,9 @@ struct URIErrorObject: ErrorObject { }; struct ActivationObject: Object { - DeclarativeEnvironment *context; + ExecutionContext *context; Value arguments; - ActivationObject(DeclarativeEnvironment *context) + ActivationObject(ExecutionContext *context) : context(context), arguments(Value::undefinedValue()) {} virtual QString className() { return QStringLiteral("Activation"); } virtual ActivationObject *asActivationObject() { return this; } diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 6779f86d11..5aacc79ca3 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -721,7 +721,7 @@ Value __qmljs_call_property(ExecutionContext *context, Value base, String *name, Value thisObject; if (base.isUndefined()) { - baseObject = context->lexicalEnvironment->activation; + baseObject = context->activation; thisObject = Value::nullValue(); } else { if (!base.isObject()) @@ -788,10 +788,10 @@ void __qmljs_throw(Value value, ExecutionContext *context) context->leaveCallContext(); context = context->parent; } - DeclarativeEnvironment *env = context->lexicalEnvironment; - while (env->withObject != handler.with) { - DeclarativeEnvironment::With *w = env->withObject; - env->withObject = w->next; + + while (context->withObject != handler.with) { + ExecutionContext::With *w = context->withObject; + context->withObject = w->next; delete w; } @@ -806,7 +806,7 @@ void *__qmljs_create_exception_handler(ExecutionContext *context) context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler()); ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); handler.context = context; - handler.with = context->lexicalEnvironment->withObject; + handler.with = context->withObject; return handler.stackFrame; } @@ -835,17 +835,17 @@ void __qmljs_builtin_throw(Value val, ExecutionContext *context) void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx) { Object *obj = __qmljs_to_object(o, ctx).asObject(); - ctx->lexicalEnvironment->pushWithObject(obj); + ctx->pushWithObject(obj); } void __qmljs_builtin_pop_with(ExecutionContext *ctx) { - ctx->lexicalEnvironment->popWithObject(); + ctx->popWithObject(); } void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) { - ctx->lexicalEnvironment->createMutableBinding(ctx, name, deletable); + ctx->createMutableBinding(ctx, name, deletable); } } // extern "C" diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index dfaf5f8b69..642eaa8bae 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -789,7 +789,7 @@ Value ObjectPrototype::method_propertyIsEnumerable(ExecutionContext *ctx) Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx) { - if (ctx->argumentCount() < 2) + if (ctx->argumentCount < 2) __qmljs_throw_type_error(ctx); String *prop = ctx->argument(0).toString(ctx); @@ -809,7 +809,7 @@ Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx) Value ObjectPrototype::method_defineSetter(ExecutionContext *ctx) { - if (ctx->argumentCount() < 2) + if (ctx->argumentCount < 2) __qmljs_throw_type_error(ctx); String *prop = ctx->argument(0).toString(ctx); @@ -838,7 +838,7 @@ StringCtor::StringCtor(ExecutionContext *scope) Value StringCtor::construct(ExecutionContext *ctx) { Value value; - if (ctx->argumentCount()) + if (ctx->argumentCount) value = Value::fromString(ctx->argument(0).toString(ctx)); else value = Value::fromString(ctx, QString()); @@ -913,7 +913,7 @@ Value StringPrototype::method_charAt(ExecutionContext *ctx) const QString str = getThisString(ctx); int pos = 0; - if (ctx->argumentCount() > 0) + if (ctx->argumentCount > 0) pos = (int) ctx->argument(0).toInteger(ctx); QString result; @@ -928,7 +928,7 @@ Value StringPrototype::method_charCodeAt(ExecutionContext *ctx) const QString str = getThisString(ctx); int pos = 0; - if (ctx->argumentCount() > 0) + if (ctx->argumentCount > 0) pos = (int) ctx->argument(0).toInteger(ctx); double result = qSNaN(); @@ -943,7 +943,7 @@ Value StringPrototype::method_concat(ExecutionContext *ctx) { QString value = getThisString(ctx); - for (unsigned i = 0; i < ctx->argumentCount(); ++i) { + for (unsigned i = 0; i < ctx->argumentCount; ++i) { Value v = __qmljs_to_string(ctx->argument(i), ctx); assert(v.isString()); value += v.stringValue()->toQString(); @@ -957,11 +957,11 @@ Value StringPrototype::method_indexOf(ExecutionContext *ctx) QString value = getThisString(ctx); QString searchString; - if (ctx->argumentCount()) + if (ctx->argumentCount) searchString = ctx->argument(0).toString(ctx)->toQString(); int pos = 0; - if (ctx->argumentCount() > 1) + if (ctx->argumentCount > 1) pos = (int) ctx->argument(1).toInteger(ctx); int index = -1; @@ -976,7 +976,7 @@ Value StringPrototype::method_lastIndexOf(ExecutionContext *ctx) const QString value = getThisString(ctx); QString searchString; - if (ctx->argumentCount()) { + if (ctx->argumentCount) { Value v = __qmljs_to_string(ctx->argument(0), ctx); searchString = v.stringValue()->toQString(); } @@ -1057,11 +1057,11 @@ Value StringPrototype::method_substr(ExecutionContext *ctx) const QString value = getThisString(ctx); double start = 0; - if (ctx->argumentCount() > 0) + if (ctx->argumentCount > 0) start = ctx->argument(0).toInteger(ctx); double length = +qInf(); - if (ctx->argumentCount() > 1) + if (ctx->argumentCount > 1) length = ctx->argument(1).toInteger(ctx); double count = value.length(); @@ -1083,10 +1083,10 @@ Value StringPrototype::method_substring(ExecutionContext *ctx) double start = 0; double end = length; - if (ctx->argumentCount() > 0) + if (ctx->argumentCount > 0) start = ctx->argument(0).toInteger(ctx); - if (ctx->argumentCount() > 1) + if (ctx->argumentCount > 1) end = ctx->argument(1).toInteger(ctx); if (std::isnan(start) || start < 0) @@ -1137,7 +1137,7 @@ Value StringPrototype::method_toLocaleUpperCase(ExecutionContext *ctx) Value StringPrototype::method_fromCharCode(ExecutionContext *ctx) { QString str; - for (unsigned i = 0; i < ctx->argumentCount(); ++i) { + for (unsigned i = 0; i < ctx->argumentCount; ++i) { QChar c(ctx->argument(i).toUInt16(ctx)); str += c; } @@ -1161,7 +1161,7 @@ Value NumberCtor::construct(ExecutionContext *ctx) Value NumberCtor::call(ExecutionContext *ctx) { - double value = ctx->argumentCount() ? ctx->argument(0).toNumber(ctx) : 0; + double value = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; return Value::fromDouble(value); } @@ -1275,7 +1275,7 @@ Value NumberPrototype::method_toFixed(ExecutionContext *ctx) double fdigits = 0; - if (ctx->argumentCount() > 0) + if (ctx->argumentCount > 0) fdigits = ctx->argument(0).toInteger(ctx); if (std::isnan(fdigits)) @@ -1300,7 +1300,7 @@ Value NumberPrototype::method_toExponential(ExecutionContext *ctx) double fdigits = 0; - if (ctx->argumentCount() > 0) + if (ctx->argumentCount > 0) fdigits = ctx->argument(0).toInteger(ctx); QString z = QString::number(thisObject->value.asDouble(), 'e', int (fdigits)); @@ -1315,7 +1315,7 @@ Value NumberPrototype::method_toPrecision(ExecutionContext *ctx) double fdigits = 0; - if (ctx->argumentCount() > 0) + if (ctx->argumentCount > 0) fdigits = ctx->argument(0).toInteger(ctx); return Value::fromString(ctx, QString::number(thisObject->value.asDouble(), 'g', int (fdigits))); @@ -1338,7 +1338,7 @@ Value BooleanCtor::construct(ExecutionContext *ctx) Value BooleanCtor::call(ExecutionContext *ctx) { - bool value = ctx->argumentCount() ? ctx->argument(0).toBoolean(ctx) : 0; + bool value = ctx->argumentCount ? ctx->argument(0).toBoolean(ctx) : 0; return Value::fromBoolean(value); } @@ -1386,7 +1386,7 @@ Value ArrayCtor::construct(ExecutionContext *ctx) Value ArrayCtor::call(ExecutionContext *ctx) { Array value; - if (ctx->argumentCount() == 1 && ctx->argument(0).isNumber()) { + if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { double size = ctx->argument(0).asDouble(); quint32 isize = Value::toUInt32(size); @@ -1397,7 +1397,7 @@ Value ArrayCtor::call(ExecutionContext *ctx) value.resize(isize); } else { - for (unsigned int i = 0; i < ctx->argumentCount(); ++i) { + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { value.assign(i, ctx->argument(i)); } } @@ -1453,7 +1453,7 @@ Value ArrayPrototype::method_concat(ExecutionContext *ctx) result.assign(0, Value::fromString(ctx, v)); } - for (uint i = 0; i < ctx->argumentCount(); ++i) { + for (uint i = 0; i < ctx->argumentCount; ++i) { quint32 k = result.size(); Value arg = ctx->argument(i); @@ -1548,7 +1548,7 @@ Value ArrayPrototype::method_push(ExecutionContext *ctx) Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { uint pos = instance->value.size(); - for (unsigned int i = 0; i < ctx->argumentCount(); ++i) { + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { Value val = ctx->argument(i); instance->value.assign(pos++, val); } @@ -1557,7 +1557,7 @@ Value ArrayPrototype::method_push(ExecutionContext *ctx) Value r1 = self.property(ctx, ctx->engine->id_length); quint32 n = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; - for (unsigned int index = 0; index < ctx->argumentCount(); ++index, ++n) { + for (unsigned int index = 0; index < ctx->argumentCount; ++index, ++n) { Value r3 = ctx->argument(index); String *name = Value::fromDouble(n).toString(ctx); self.objectValue()->__put__(ctx, name, r3); @@ -1631,7 +1631,7 @@ Value ArrayPrototype::method_sort(ExecutionContext *ctx) Value ArrayPrototype::method_splice(ExecutionContext *ctx) { - if (ctx->argumentCount() < 2) + if (ctx->argumentCount < 2) // ### check return Value::undefinedValue(); @@ -1643,7 +1643,7 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) double deleteCount = ctx->argument(1).toInteger(ctx); Value a = Value::fromObject(ctx->engine->newArrayObject()); QVector items; - for (unsigned int i = 2; i < ctx->argumentCount(); ++i) + for (unsigned int i = 2; i < ctx->argumentCount; ++i) items << ctx->argument(i); ArrayObject *otherInstance = a.asArrayObject(); assert(otherInstance); @@ -1862,10 +1862,10 @@ Value FunctionCtor::construct(ExecutionContext *ctx) { QString args; QString body; - if (ctx->argumentCount() > 0) - body = ctx->argument(ctx->argumentCount() - 1).toString(ctx)->toQString(); + if (ctx->argumentCount > 0) + body = ctx->argument(ctx->argumentCount - 1).toString(ctx)->toQString(); - for (uint i = 0; i < ctx->argumentCount() - 1; ++i) { + for (uint i = 0; i < ctx->argumentCount - 1; ++i) { if (i) args += QLatin1String(", "); args += ctx->argument(i).toString(ctx)->toQString(); @@ -1958,10 +1958,10 @@ Value FunctionPrototype::method_apply(ExecutionContext *ctx) Value FunctionPrototype::method_call(ExecutionContext *ctx) { Value thisArg = ctx->argument(0); - QVector args(ctx->argumentCount() ? ctx->argumentCount() - 1 : 0); - if (ctx->argumentCount()) - qCopy(ctx->lexicalEnvironment->arguments + 1, - ctx->lexicalEnvironment->arguments + ctx->argumentCount(), args.begin()); + QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); + if (ctx->argumentCount) + qCopy(ctx->arguments + 1, + ctx->arguments + ctx->argumentCount, args.begin()); return __qmljs_call_value(ctx, thisArg, ctx->thisObject, args.data(), args.size()); } @@ -1987,10 +1987,10 @@ Value DateCtor::construct(ExecutionContext *ctx) { double t = 0; - if (ctx->argumentCount() == 0) + if (ctx->argumentCount == 0) t = currentTime(); - else if (ctx->argumentCount() == 1) { + else if (ctx->argumentCount == 1) { Value arg = ctx->argument(0); if (DateObject *d = arg.asDateObject()) arg = d->value; @@ -2003,14 +2003,14 @@ Value DateCtor::construct(ExecutionContext *ctx) t = TimeClip(arg.toNumber(ctx)); } - else { // ctx->argumentCount()() > 1 + else { // ctx->argumentCount > 1 double year = ctx->argument(0).toNumber(ctx); double month = ctx->argument(1).toNumber(ctx); - double day = ctx->argumentCount() >= 3 ? ctx->argument(2).toNumber(ctx) : 1; - double hours = ctx->argumentCount() >= 4 ? ctx->argument(3).toNumber(ctx) : 0; - double mins = ctx->argumentCount() >= 5 ? ctx->argument(4).toNumber(ctx) : 0; - double secs = ctx->argumentCount() >= 6 ? ctx->argument(5).toNumber(ctx) : 0; - double ms = ctx->argumentCount() >= 7 ? ctx->argument(6).toNumber(ctx) : 0; + double day = ctx->argumentCount >= 3 ? ctx->argument(2).toNumber(ctx) : 1; + double hours = ctx->argumentCount >= 4 ? ctx->argument(3).toNumber(ctx) : 0; + double mins = ctx->argumentCount >= 5 ? ctx->argument(4).toNumber(ctx) : 0; + double secs = ctx->argumentCount >= 6 ? ctx->argument(5).toNumber(ctx) : 0; + double ms = ctx->argumentCount >= 7 ? ctx->argument(6).toNumber(ctx) : 0; if (year >= 0 && year <= 99) year += 1900; t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); @@ -2118,7 +2118,7 @@ Value DatePrototype::method_parse(ExecutionContext *ctx) Value DatePrototype::method_UTC(ExecutionContext *ctx) { - const int numArgs = ctx->argumentCount(); + const int numArgs = ctx->argumentCount; if (numArgs >= 2) { double year = ctx->argument(0).toNumber(ctx); double month = ctx->argument(1).toNumber(ctx); @@ -2370,7 +2370,7 @@ Value DatePrototype::method_setSeconds(ExecutionContext *ctx) double t = LocalTime(self->value.asDouble()); double sec = ctx->argument(0).toNumber(ctx); - double ms = (ctx->argumentCount() < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); self->value.setDouble(t); return self->value; @@ -2384,7 +2384,7 @@ Value DatePrototype::method_setUTCSeconds(ExecutionContext *ctx) double t = self->value.asDouble(); double sec = ctx->argument(0).toNumber(ctx); - double ms = (ctx->argumentCount() < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); self->value.setDouble(t); return self->value; @@ -2398,8 +2398,8 @@ Value DatePrototype::method_setMinutes(ExecutionContext *ctx) double t = LocalTime(self->value.asDouble()); double min = ctx->argument(0).toNumber(ctx); - double sec = (ctx->argumentCount() < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); - double ms = (ctx->argumentCount() < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); self->value.setDouble(t); return self->value; @@ -2413,8 +2413,8 @@ Value DatePrototype::method_setUTCMinutes(ExecutionContext *ctx) double t = self->value.asDouble(); double min = ctx->argument(0).toNumber(ctx); - double sec = (ctx->argumentCount() < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); - double ms = (ctx->argumentCount() < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); self->value.setDouble(t); return self->value; @@ -2428,9 +2428,9 @@ Value DatePrototype::method_setHours(ExecutionContext *ctx) double t = LocalTime(self->value.asDouble()); double hour = ctx->argument(0).toNumber(ctx); - double min = (ctx->argumentCount() < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); - double sec = (ctx->argumentCount() < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); - double ms = (ctx->argumentCount() < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); self->value.setDouble(t); return self->value; @@ -2444,9 +2444,9 @@ Value DatePrototype::method_setUTCHours(ExecutionContext *ctx) double t = self->value.asDouble(); double hour = ctx->argument(0).toNumber(ctx); - double min = (ctx->argumentCount() < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); - double sec = (ctx->argumentCount() < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); - double ms = (ctx->argumentCount() < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); self->value.setDouble(t); return self->value; @@ -2486,7 +2486,7 @@ Value DatePrototype::method_setMonth(ExecutionContext *ctx) double t = LocalTime(self->value.asDouble()); double month = ctx->argument(0).toNumber(ctx); - double date = (ctx->argumentCount() < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); self->value.setDouble(t); return self->value; @@ -2500,7 +2500,7 @@ Value DatePrototype::method_setUTCMonth(ExecutionContext *ctx) double t = self->value.asDouble(); double month = ctx->argument(0).toNumber(ctx); - double date = (ctx->argumentCount() < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); self->value.setDouble(t); return self->value; @@ -2540,8 +2540,8 @@ Value DatePrototype::method_setUTCFullYear(ExecutionContext *ctx) double t = self->value.asDouble(); double year = ctx->argument(0).toNumber(ctx); - double month = (ctx->argumentCount() < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); - double date = (ctx->argumentCount() < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); self->value.setDouble(t); return self->value; @@ -2555,8 +2555,8 @@ Value DatePrototype::method_setFullYear(ExecutionContext *ctx) double t = LocalTime(self->value.asDouble()); double year = ctx->argument(0).toNumber(ctx); - double month = (ctx->argumentCount() < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); - double date = (ctx->argumentCount() < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); self->value.setDouble(t); return self->value; @@ -2582,13 +2582,13 @@ RegExpCtor::RegExpCtor(ExecutionContext *scope) Value RegExpCtor::construct(ExecutionContext *ctx) { -// if (ctx->argumentCount() > 2) { +// if (ctx->argumentCount > 2) { // ctx->throwTypeError(); // return; // } - Value r = ctx->argumentCount() > 0 ? ctx->argument(0) : Value::undefinedValue(); - Value f = ctx->argumentCount() > 1 ? ctx->argument(1) : Value::undefinedValue(); + Value r = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); + Value f = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); if (RegExpObject *re = r.asRegExpObject()) { if (!f.isUndefined()) ctx->throwTypeError(); @@ -2629,8 +2629,8 @@ Value RegExpCtor::construct(ExecutionContext *ctx) Value RegExpCtor::call(ExecutionContext *ctx) { - if (ctx->argumentCount() > 0 && ctx->argument(0).asRegExpObject()) { - if (ctx->argumentCount() == 1 || ctx->argument(1).isUndefined()) + if (ctx->argumentCount > 0 && ctx->argument(0).asRegExpObject()) { + if (ctx->argumentCount == 1 || ctx->argument(1).isUndefined()) return ctx->argument(0); } @@ -2951,7 +2951,7 @@ Value MathObject::method_log(ExecutionContext *ctx) Value MathObject::method_max(ExecutionContext *ctx) { double mx = -qInf(); - for (unsigned i = 0; i < ctx->argumentCount(); ++i) { + for (unsigned i = 0; i < ctx->argumentCount; ++i) { double x = ctx->argument(i).toNumber(ctx); if (x > mx || std::isnan(x)) mx = x; @@ -2962,7 +2962,7 @@ Value MathObject::method_max(ExecutionContext *ctx) Value MathObject::method_min(ExecutionContext *ctx) { double mx = qInf(); - for (unsigned i = 0; i < ctx->argumentCount(); ++i) { + for (unsigned i = 0; i < ctx->argumentCount; ++i) { double x = ctx->argument(i).toNumber(ctx); if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) || (x < mx) || std::isnan(x)) { diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 37ad3fe9bc..236d6aa703 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -191,12 +191,10 @@ InstructionSelection::Pointer InstructionSelection::loadTempAddress(RegisterID r int32_t offset = 0; if (t->index < 0) { const int arg = -t->index - 1; - loadPtr(Address(ContextRegister, offsetof(ExecutionContext, lexicalEnvironment)), reg); - loadPtr(Address(reg, offsetof(DeclarativeEnvironment, arguments)), reg); + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, arguments)), reg); offset = arg * sizeof(Value); } else if (t->index < _function->locals.size()) { - loadPtr(Address(ContextRegister, offsetof(ExecutionContext, lexicalEnvironment)), reg); - loadPtr(Address(reg, offsetof(DeclarativeEnvironment, locals)), reg); + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, locals)), reg); offset = t->index * sizeof(Value); } else { const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size(); -- cgit v1.2.3 From 4e643637dd7997b36b58edad079c2d4c71dc7222 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 1 Dec 2012 14:25:54 +0100 Subject: Further clean up the ExecutionContext remove the distinction between initCallContext and initConstructorContext. Since the context now has a pointer to the current function, we can also simplify the wireupPrototype method. Change-Id: I06cbaced2438b01b3033182e136e1504c087c8c0 Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 68 ++++++++++++++++++++------------------------------- qmljs_environment.h | 5 +--- qmljs_objects.cpp | 5 ++-- qv4ecmaobjects.cpp | 2 +- 4 files changed, 31 insertions(+), 49 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index ebbe7d3d87..ce1bf9da82 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -73,33 +73,6 @@ String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const return ctx->engine->newString(msg); } -void ExecutionContext::init(FunctionObject *f, Value *args, uint argc) -{ - function = f; - engine = f->scope->engine; - strictMode = f->strictMode; - - arguments = args; - argumentCount = argc; - if (function->needsActivation || argc < function->formalParameterCount){ - arguments = new Value[qMax(argc, function->formalParameterCount)]; - if (argc) - std::copy(args, args + argc, arguments); - if (argc < function->formalParameterCount) - std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); - } - locals = function->varCount ? new Value[function->varCount] : 0; - if (function->varCount) - std::fill(locals, locals + function->varCount, Value::undefinedValue()); - - if (function->needsActivation) - activation = engine->newActivationObject(this); - else - activation = 0; - - withObject = 0; -} - bool ExecutionContext::hasBinding(String *name) const { if (!function) @@ -328,10 +301,31 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha { engine = parent->engine; this->parent = parent; + thisObject = that; + + function = f; + strictMode = f->strictMode; - init(f, args, argc); + arguments = args; + argumentCount = argc; + if (function->needsActivation || argc < function->formalParameterCount){ + arguments = new Value[qMax(argc, function->formalParameterCount)]; + if (argc) + std::copy(args, args + argc, arguments); + if (argc < function->formalParameterCount) + std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); + } + locals = function->varCount ? new Value[function->varCount] : 0; + if (function->varCount) + std::fill(locals, locals + function->varCount, Value::undefinedValue()); + + if (function->needsActivation) + activation = engine->newActivationObject(this); + else + activation = 0; + + withObject = 0; - thisObject = that; if (engine->debugger) engine->debugger->aboutToCall(f, this); @@ -344,27 +338,17 @@ void ExecutionContext::leaveCallContext() delete[] locals; locals = 0; } + parent = 0; if (engine->debugger) engine->debugger->justLeft(this); } -void ExecutionContext::initConstructorContext(ExecutionContext *parent, Value that, FunctionObject *f, Value *args, unsigned argc) -{ - initCallContext(parent, that, f, args, argc); -} - -void ExecutionContext::leaveConstructorContext(FunctionObject *f) -{ - wireUpPrototype(f); - leaveCallContext(); -} - -void ExecutionContext::wireUpPrototype(FunctionObject *f) +void ExecutionContext::wireUpPrototype() { assert(thisObject.isObject()); - Value proto = f->__get__(this, engine->id_prototype); + Value proto = function->__get__(this, engine->id_prototype); if (proto.isObject()) thisObject.objectValue()->prototype = proto.objectValue(); else diff --git a/qmljs_environment.h b/qmljs_environment.h index a34774ed96..3457771296 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -97,7 +97,6 @@ struct ExecutionContext void init(ExecutionEngine *e); - void init(FunctionObject *f, Value *args, uint argc); bool hasBinding(String *name) const; void createMutableBinding(ExecutionContext *ctx, String *name, bool deletable); @@ -111,9 +110,7 @@ struct ExecutionContext void initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc); void leaveCallContext(); - void initConstructorContext(ExecutionContext *parent, Value that, FunctionObject *f, Value *args, unsigned argc); - void leaveConstructorContext(FunctionObject *f); - void wireUpPrototype(FunctionObject *f); + void wireUpPrototype(); void throwError(Value value); void throwError(const QString &message); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 75fab4b5fc..5ed251e54a 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -392,9 +392,10 @@ Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc { ExecutionContext k; ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; - ctx->initConstructorContext(context, Value::nullValue(), this, args, argc); + ctx->initCallContext(context, Value::nullValue(), this, args, argc); Value result = construct(ctx); - ctx->leaveConstructorContext(this); + ctx->wireUpPrototype(); + ctx->leaveCallContext(); if (ctx != &k) delete ctx; return result; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 642eaa8bae..8edd43cb14 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2723,7 +2723,7 @@ Value ErrorCtor::call(ExecutionContext *ctx) { Value that = ctx->thisObject; construct(ctx); - ctx->wireUpPrototype(this); + ctx->wireUpPrototype(); Value res = ctx->thisObject; ctx->thisObject = that; -- cgit v1.2.3 From 2a7408ae5a83334adab3d004ccbe83b18c49bf0d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 1 Dec 2012 19:39:57 +0100 Subject: Throw when trying to set an undefined variable in strict mode Change-Id: Ia4cbe302b96e53147aa7857dcded811e73136329 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 5aacc79ca3..774676a63f 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -611,10 +611,14 @@ Value __qmljs_foreach_next_property_name(Value foreach_iterator) void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, Value value) { PropertyDescriptor tmp; - if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) + PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp); + if (prop) { prop->value = value; - else - ctx->engine->globalObject.objectValue()->__put__(ctx, name, value); + return; + } + if (ctx->strictMode) + ctx->throwReferenceError(Value::fromString(name)); + ctx->engine->globalObject.objectValue()->__put__(ctx, name, value); } Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name) @@ -638,10 +642,10 @@ Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name) Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name) { PropertyDescriptor tmp; - if (PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp)) - return prop->value; - ctx->throwReferenceError(Value::fromString(name)); - return Value::undefinedValue(); + PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp); + if (!prop) + ctx->throwReferenceError(Value::fromString(name)); + return prop->value; } Value __qmljs_get_thisObject(ExecutionContext *ctx) @@ -744,10 +748,8 @@ Value __qmljs_construct_activation_property(ExecutionContext *context, String *n { PropertyDescriptor tmp; PropertyDescriptor *func = context->lookupPropertyDescriptor(name, &tmp); - if (! func) { + if (! func) context->throwReferenceError(Value::fromString(name)); - return Value::undefinedValue(); - } return __qmljs_construct_value(context, func->value, args, argc); } @@ -785,8 +787,9 @@ void __qmljs_throw(Value value, ExecutionContext *context) // clean up call contexts while (context != handler.context) { + ExecutionContext *parent = context->parent; context->leaveCallContext(); - context = context->parent; + context = parent; } while (context->withObject != handler.with) { -- cgit v1.2.3 From 751ceb0f8ff517e83c96d39a5d0b74132adb78aa Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 1 Dec 2012 19:57:26 +0100 Subject: Fix qmljs_call_property Change-Id: Ic32fc8815704ed201a3b50eae05ff2705372210d Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 20 ++++++-------------- qmljs_runtime.h | 2 +- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 774676a63f..dcfb62d90d 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -719,23 +719,15 @@ Value __qmljs_call_activation_property(ExecutionContext *context, String *name, } } -Value __qmljs_call_property(ExecutionContext *context, Value base, String *name, Value *args, int argc) +Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String *name, Value *args, int argc) { - Object *baseObject; - Value thisObject; + if (!thisObject.isObject()) + thisObject = __qmljs_to_object(thisObject, context); - if (base.isUndefined()) { - baseObject = context->activation; - thisObject = Value::nullValue(); - } else { - if (!base.isObject()) - base = __qmljs_to_object(base, context); - assert(base.isObject()); - baseObject = base.objectValue(); - thisObject = base; - } + assert(thisObject.isObject()); + Object *baseObject = base.objectValue(); - Value func = baseObject ? baseObject->__get__(context, name) : Value::undefinedValue(); + Value func = baseObject->__get__(context, name); return callFunction(context, thisObject, func.asFunctionObject(), args, argc); } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 2df8dd105c..d6a7839dce 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -92,7 +92,7 @@ extern "C" { // context Value __qmljs_call_activation_property(ExecutionContext *, String *name, Value *args, int argc); -Value __qmljs_call_property(ExecutionContext *context, Value base, String *name, Value *args, int argc); +Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String *name, Value *args, int argc); Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc); Value __qmljs_construct_activation_property(ExecutionContext *, String *name, Value *args, int argc); -- cgit v1.2.3 From 5d2f12ee03d942e5bd37df2b976b90525f51a1e4 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 1 Dec 2012 20:08:26 +0100 Subject: Remove the callFunction() method Give Object a virtual call() method, that simply throws a type error. FunctionObject reimplements this to do the right thing. Change-Id: I5a11a4de0302ad86b9ad3a822501224e11692b70 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 6 ++++++ qmljs_objects.h | 2 ++ qmljs_runtime.cpp | 40 ++++++++++++++++++---------------------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 5ed251e54a..e0ebc67b75 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -322,6 +322,12 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property return false; } +Value Object::call(ExecutionContext *context, Value , Value *, int) +{ + context->throwTypeError(); + return Value::undefinedValue(); +} + String *ForEachIteratorObject::nextPropertyName() { PropertyTableEntry *p = 0; diff --git a/qmljs_objects.h b/qmljs_objects.h index 927b9f32d1..cd9c2430cc 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -429,6 +429,8 @@ struct Object { virtual bool __delete__(ExecutionContext *ctx, String *name); virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc); + virtual Value call(ExecutionContext *context, Value, Value *, int); + // // helpers // diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index dcfb62d90d..20002aac06 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -57,16 +57,6 @@ namespace QQmlJS { namespace VM { -static inline Value callFunction(ExecutionContext *context, Value thisObject, FunctionObject *func, Value *args, int argc) -{ - if (func) { - return func->call(context, thisObject, args, argc); - } else { - context->throwTypeError(); - return Value::undefinedValue(); - } -} - QString numberToString(double num, int radix = 10) { if (std::isnan(num)) { @@ -497,15 +487,15 @@ Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int type Object *oo = object.objectValue(); Value conv = oo->__get__(ctx, meth1); - if (FunctionObject *f = conv.asFunctionObject()) { - Value r = callFunction(ctx, object, f, 0, 0); + if (Object *o = conv.asObject()) { + Value r = o->call(ctx, object, 0, 0); if (r.isPrimitive()) return r; } conv = oo->__get__(ctx, meth2); - if (FunctionObject *f = conv.asFunctionObject()) { - Value r = callFunction(ctx, object, f, 0, 0); + if (Object *o = conv.asObject()) { + Value r = o->call(ctx, object, 0, 0); if (r.isPrimitive()) return r; } @@ -711,12 +701,11 @@ uint __qmljs_equal(Value x, Value y, ExecutionContext *ctx) Value __qmljs_call_activation_property(ExecutionContext *context, String *name, Value *args, int argc) { Value func = __qmljs_get_activation_property(context, name); - if (FunctionObject *f = func.asFunctionObject()) { - return callFunction(context, Value::undefinedValue(), f, args, argc); - } else { + Object *o = func.asObject(); + if (!o) context->throwReferenceError(Value::fromString(name)); - return Value::undefinedValue(); - } + + return o->call(context, Value::undefinedValue(), args, argc); } Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String *name, Value *args, int argc) @@ -725,15 +714,22 @@ Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String thisObject = __qmljs_to_object(thisObject, context); assert(thisObject.isObject()); - Object *baseObject = base.objectValue(); + Object *baseObject = thisObject.objectValue(); Value func = baseObject->__get__(context, name); - return callFunction(context, thisObject, func.asFunctionObject(), args, argc); + Object *o = func.asObject(); + if (!o) + context->throwTypeError(); + + return o->call(context, thisObject, args, argc); } Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc) { - return callFunction(context, thisObject, func.asFunctionObject(), args, argc); + Object *o = func.asObject(); + if (!o) + context->throwTypeError(); + return o->call(context, thisObject, args, argc); } Value __qmljs_construct_activation_property(ExecutionContext *context, String *name, Value *args, int argc) -- cgit v1.2.3 From 11c4b283e45c08dee5b29391617486ef9f891fda Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 2 Dec 2012 19:04:57 +0100 Subject: [masm] Clean up binop code generation Instead of a gigantic switch and that duplicated across regular binop and in-place binop, let's use one table where we can store all sorts of meta-information about various aspects of the op implementations. Then we can centralize the code for generating the inline operation as well as the call to the fallback in one helper function. Change-Id: I13b6dae7fd2a1490ae315689fa5f813eee83dd7b Reviewed-by: Lars Knoll --- qv4ir_p.h | 4 +- qv4isel_masm.cpp | 262 +++++++++++++++++++++++++++---------------------------- qv4isel_masm_p.h | 7 +- 3 files changed, 131 insertions(+), 142 deletions(-) diff --git a/qv4ir_p.h b/qv4ir_p.h index ee91807971..92276d0b6d 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -143,7 +143,9 @@ enum AluOp { OpIn, OpAnd, - OpOr + OpOr, + + LastAluOp = OpOr }; AluOp binaryOperator(int op); const char *opname(IR::AluOp op); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 236d6aa703..bb9e224302 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -62,6 +62,61 @@ namespace { QTextStream qout(stderr, QIODevice::WriteOnly); } +typedef JSC::MacroAssembler::Jump (JSC::MacroAssembler::*MemBinOpWithOverFlow)(JSC::MacroAssembler::ResultCondition, JSC::MacroAssembler::Address, JSC::MacroAssembler::RegisterID); +typedef JSC::MacroAssembler::Jump (JSC::MacroAssembler::*ImmBinOpWithOverFlow)(JSC::MacroAssembler::ResultCondition, JSC::MacroAssembler::TrustedImm32, JSC::MacroAssembler::RegisterID); + +#define OP(op) \ + { isel_stringIfy(op), op, 0, 0 } + +#define INLINE_OP(op, memOp, immOp) \ + { isel_stringIfy(op), op, memOp, immOp } + +#define NULL_OP \ + { 0, 0, 0, 0 } + +static const struct BinaryOperationInfo { + const char *name; + Value (*fallbackImplementation)(const Value, const Value, ExecutionContext *); + MemBinOpWithOverFlow inlineMemOp; + ImmBinOpWithOverFlow inlineImmOp; +} binaryOperations[QQmlJS::IR::LastAluOp + 1] = { + NULL_OP, // OpInvalid + NULL_OP, // OpIfTrue + NULL_OP, // OpNot + NULL_OP, // OpUMinus + NULL_OP, // OpUPlus + NULL_OP, // OpCompl + + OP(__qmljs_bit_and), // OpBitAnd + OP(__qmljs_bit_or), // OpBitOr + OP(__qmljs_bit_xor), // OpBitXor + + INLINE_OP(__qmljs_add, &JSC::MacroAssembler::branchAdd32, &JSC::MacroAssembler::branchAdd32), // OpAdd + INLINE_OP(__qmljs_sub, &JSC::MacroAssembler::branchSub32, &JSC::MacroAssembler::branchSub32), // OpSub + OP(__qmljs_mul), // OpMul + OP(__qmljs_div), // OpDiv + OP(__qmljs_mod), // OpMod + + OP(__qmljs_shl), // OpLShift + OP(__qmljs_shr), // OpRShift + OP(__qmljs_ushr), // OpURShift + + OP(__qmljs_gt), // OpGt + OP(__qmljs_lt), // OpLt + OP(__qmljs_ge), // OpGe + OP(__qmljs_le), // OpLe + OP(__qmljs_eq), // OpEqual + OP(__qmljs_ne), // OpNotEqual + OP(__qmljs_se), // OpStrictEqual + OP(__qmljs_sne), // OpStrictNotEqual + + OP(__qmljs_instanceof), // OpInstanceof + OP(__qmljs_in), // OpIn + + NULL_OP, // OpAnd + NULL_OP // OpOr +}; + #if OS(LINUX) static void printDisassembledOutputWithCalls(const char* output, const QHash& functions) { @@ -480,66 +535,7 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Binop *b = s->source->asBinop()) { if ((b->left->asTemp() || b->left->asConst()) && (b->right->asTemp() || b->right->asConst())) { - Value (*op)(const Value, const Value, ExecutionContext *) = 0; - const char* opName = 0; - MemBinOpWithOverFlow inlineMemBinOp = 0; - ImmBinOpWithOverFlow inlineImmBinOp = 0; - - switch ((IR::AluOp) b->op) { - case IR::OpInvalid: - case IR::OpIfTrue: - case IR::OpNot: - case IR::OpUMinus: - case IR::OpUPlus: - case IR::OpCompl: - assert(!"unreachable"); - break; - - case IR::OpBitAnd: setOp(op, opName, __qmljs_bit_and); break; - case IR::OpBitOr: setOp(op, opName, __qmljs_bit_or); break; - case IR::OpBitXor: setOp(op, opName, __qmljs_bit_xor); break; - case IR::OpAdd: { - setOp(op, opName, __qmljs_add); - inlineMemBinOp = &JSC::MacroAssembler::branchAdd32; - inlineImmBinOp = &JSC::MacroAssembler::branchAdd32; - break; - } - case IR::OpSub: { - setOp(op, opName, __qmljs_sub); - inlineMemBinOp = &JSC::MacroAssembler::branchSub32; - inlineImmBinOp = &JSC::MacroAssembler::branchSub32; - break; - } - case IR::OpMul: setOp(op, opName, __qmljs_mul); break; - case IR::OpDiv: setOp(op, opName, __qmljs_div); break; - case IR::OpMod: setOp(op, opName, __qmljs_mod); break; - case IR::OpLShift: setOp(op, opName, __qmljs_shl); break; - case IR::OpRShift: setOp(op, opName, __qmljs_shr); break; - case IR::OpURShift: setOp(op, opName, __qmljs_ushr); break; - case IR::OpGt: setOp(op, opName, __qmljs_gt); break; - case IR::OpLt: setOp(op, opName, __qmljs_lt); break; - case IR::OpGe: setOp(op, opName, __qmljs_ge); break; - case IR::OpLe: setOp(op, opName, __qmljs_le); break; - case IR::OpEqual: setOp(op, opName, __qmljs_eq); break; - case IR::OpNotEqual: setOp(op, opName, __qmljs_ne); break; - case IR::OpStrictEqual: setOp(op, opName, __qmljs_se); break; - case IR::OpStrictNotEqual: setOp(op, opName, __qmljs_sne); break; - case IR::OpInstanceof: setOp(op, opName, __qmljs_instanceof); break; - case IR::OpIn: setOp(op, opName, __qmljs_in); break; - - case IR::OpAnd: - case IR::OpOr: - assert(!"unreachable"); - break; - } - - if (op) { - if (inlineMemBinOp && inlineImmBinOp - && generateArithmeticIntegerInlineBinOp(t, b->left, b->right, inlineMemBinOp, inlineImmBinOp, op, opName)) - return; - else - generateFunctionCallImp(t, opName, op, b->left, b->right, ContextRegister); - } + generateBinOp((IR::AluOp)b->op, t, b->left, b->right); return; } } else if (IR::Call *c = s->source->asCall()) { @@ -575,26 +571,7 @@ void InstructionSelection::visitMove(IR::Move *s) // inplace assignment, e.g. x += 1, ++x, ... if (IR::Temp *t = s->target->asTemp()) { if (s->source->asTemp() || s->source->asConst()) { - Value (*op)(const Value left, const Value right, ExecutionContext *ctx) = 0; - const char *opName = 0; - switch (s->op) { - case IR::OpBitAnd: setOp(op, opName, __qmljs_bit_and); break; - case IR::OpBitOr: setOp(op, opName, __qmljs_bit_or); break; - case IR::OpBitXor: setOp(op, opName, __qmljs_bit_xor); break; - case IR::OpAdd: setOp(op, opName, __qmljs_add); break; - case IR::OpSub: setOp(op, opName, __qmljs_sub); break; - case IR::OpMul: setOp(op, opName, __qmljs_mul); break; - case IR::OpDiv: setOp(op, opName, __qmljs_div); break; - case IR::OpMod: setOp(op, opName, __qmljs_mod); break; - case IR::OpLShift: setOp(op, opName, __qmljs_shl); break; - case IR::OpRShift: setOp(op, opName, __qmljs_shr); break; - case IR::OpURShift: setOp(op, opName, __qmljs_ushr); break; - default: - Q_UNREACHABLE(); - break; - } - if (op) - generateFunctionCallImp(t, opName, op, t, s->source, ContextRegister); + generateBinOp((IR::AluOp)s->op, t, t, s->source); return; } } else if (IR::Name *n = s->target->asName()) { @@ -818,74 +795,89 @@ void InstructionSelection::copyValue(Result result, Source source) #endif } -bool InstructionSelection::generateArithmeticIntegerInlineBinOp(IR::Temp* target, IR::Expr* left, IR::Expr* right, - MemBinOpWithOverFlow memOp, ImmBinOpWithOverFlow immOp, FallbackOp fallbackOp, const char* fallbackOpName) +void InstructionSelection::generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Expr* left, IR::Expr* right) { - VM::Value leftConst; - if (left->asConst()) { - leftConst = convertToValue(left->asConst()); - if (!leftConst.tryIntegerConversion()) - return false; + const BinaryOperationInfo& info = binaryOperations[operation]; + if (!info.fallbackImplementation) { + assert(!"unreachable"); + return; } + + VM::Value leftConst; VM::Value rightConst; - if (right->asConst()) { - rightConst = convertToValue(right->asConst()); - if (!rightConst.tryIntegerConversion()) - return false; - } - Jump leftTypeCheck; - if (left->asTemp()) { - Address typeAddress = loadTempAddress(ScratchRegister, left->asTemp()); - typeAddress.offset += offsetof(VM::Value, tag); - leftTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); - } + bool canDoInline = info.inlineMemOp && info.inlineImmOp; - Jump rightTypeCheck; - if (right->asTemp()) { - Address typeAddress = loadTempAddress(ScratchRegister, right->asTemp()); - typeAddress.offset += offsetof(VM::Value, tag); - rightTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); + if (canDoInline) { + if (left->asConst()) { + leftConst = convertToValue(left->asConst()); + canDoInline = canDoInline && leftConst.tryIntegerConversion(); + } + if (right->asConst()) { + rightConst = convertToValue(right->asConst()); + canDoInline = canDoInline && rightConst.tryIntegerConversion(); + } } - if (left->asTemp()) { - Address leftValue = loadTempAddress(ScratchRegister, left->asTemp()); - leftValue.offset += offsetof(VM::Value, int_32); - load32(leftValue, IntegerOpRegister); - } else { // left->asConst() - move(TrustedImm32(leftConst.integerValue()), IntegerOpRegister); - } + Jump binOpFinished; - Jump overflowCheck; + if (canDoInline) { - if (right->asTemp()) { - Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); - rightValue.offset += offsetof(VM::Value, int_32); + Jump leftTypeCheck; + if (left->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, left->asTemp()); + typeAddress.offset += offsetof(VM::Value, tag); + leftTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); + } - overflowCheck = (this->*memOp)(Overflow, rightValue, IntegerOpRegister); - } else { // right->asConst() - VM::Value value = convertToValue(right->asConst()); - overflowCheck = (this->*immOp)(Overflow, TrustedImm32(value.integerValue()), IntegerOpRegister); - } + Jump rightTypeCheck; + if (right->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, right->asTemp()); + typeAddress.offset += offsetof(VM::Value, tag); + rightTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); + } - Address resultAddr = loadTempAddress(ScratchRegister, target); - Address resultValueAddr = resultAddr; - resultValueAddr.offset += offsetof(VM::Value, int_32); - store32(IntegerOpRegister, resultValueAddr); + if (left->asTemp()) { + Address leftValue = loadTempAddress(ScratchRegister, left->asTemp()); + leftValue.offset += offsetof(VM::Value, int_32); + load32(leftValue, IntegerOpRegister); + } else { // left->asConst() + move(TrustedImm32(leftConst.integerValue()), IntegerOpRegister); + } + + Jump overflowCheck; + + if (right->asTemp()) { + Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); + rightValue.offset += offsetof(VM::Value, int_32); + + overflowCheck = (this->*info.inlineMemOp)(Overflow, rightValue, IntegerOpRegister); + } else { // right->asConst() + VM::Value value = convertToValue(right->asConst()); + overflowCheck = (this->*info.inlineImmOp)(Overflow, TrustedImm32(value.integerValue()), IntegerOpRegister); + } + + Address resultAddr = loadTempAddress(ScratchRegister, target); + Address resultValueAddr = resultAddr; + resultValueAddr.offset += offsetof(VM::Value, int_32); + store32(IntegerOpRegister, resultValueAddr); - Address resultTypeAddr = resultAddr; - resultTypeAddr.offset += offsetof(VM::Value, tag); - store32(TrustedImm32(VM::Value::_Integer_Type), resultTypeAddr); + Address resultTypeAddr = resultAddr; + resultTypeAddr.offset += offsetof(VM::Value, tag); + store32(TrustedImm32(VM::Value::_Integer_Type), resultTypeAddr); - Jump finishBinOp = jump(); + binOpFinished = jump(); + + if (leftTypeCheck.isSet()) + leftTypeCheck.link(this); + if (rightTypeCheck.isSet()) + rightTypeCheck.link(this); + overflowCheck.link(this); + } - if (leftTypeCheck.isSet()) - leftTypeCheck.link(this); - if (rightTypeCheck.isSet()) - rightTypeCheck.link(this); - overflowCheck.link(this); - generateFunctionCallImp(target, fallbackOpName, fallbackOp, left, right, ContextRegister); + // Fallback + generateFunctionCallImp(target, info.name, info.fallbackImplementation, left, right, ContextRegister); - finishBinOp.link(this); - return true; + if (binOpFinished.isSet()) + binOpFinished.link(this); } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 55e75d3c03..4d15fd3d1e 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -611,12 +611,7 @@ private: #endif } - typedef VM::Value (*FallbackOp)(const VM::Value, const VM::Value, VM::ExecutionContext*); - - typedef Jump (JSC::MacroAssembler::*MemBinOpWithOverFlow)(ResultCondition, Address, RegisterID); - typedef Jump (JSC::MacroAssembler::*ImmBinOpWithOverFlow)(ResultCondition, TrustedImm32, RegisterID); - bool generateArithmeticIntegerInlineBinOp(IR::Temp* target, IR::Expr* left, IR::Expr* right, - MemBinOpWithOverFlow memOp, ImmBinOpWithOverFlow immOp, FallbackOp fallback, const char* fallbackOpName); + void generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Expr* left, IR::Expr* right); VM::ExecutionEngine *_engine; IR::Function *_function; -- cgit v1.2.3 From d8530fedc2cc75c1abc8452dac18b8e81d61d6d5 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 2 Dec 2012 10:29:59 -0800 Subject: Fix code generation for try statements The old code was not correctly handling statements as try { return; } finally {...} and others. In addition it was hard to read an maintain. We now keep a stack of try statements inside the code generator. Loops know about their surrounding try statement. Whenever a break, continue or return statement is encountered we now generate code for the finally statements and exception handlers we need to cleanup. Change-Id: I53bcc0587f1e923be00fea9b562453ef1e96b2de Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 104 ++++++++++++++++++++++----------------------------------- qv4codegen_p.h | 12 +++++++ 2 files changed, 51 insertions(+), 65 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 1980ed95a4..c9978ed2a8 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -364,6 +364,7 @@ Codegen::Codegen(Debugging::Debugger *debugger) , _env(0) , _loop(0) , _labelledStatement(0) + , _tryCleanup(0) , _debugger(debugger) { } @@ -435,6 +436,7 @@ void Codegen::enterLoop(Statement *node, IR::BasicBlock *breakBlock, IR::BasicBl { _loop = new Loop(node, breakBlock, continueBlock, _loop); _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement + _loop->tryCleanup = _tryCleanup; _labelledStatement = 0; } @@ -1718,6 +1720,7 @@ bool Codegen::visit(Block *ast) bool Codegen::visit(BreakStatement *ast) { + unwindException(_loop->tryCleanup); if (ast->label.isEmpty()) _block->JUMP(_loop->breakBlock); else { @@ -1735,9 +1738,11 @@ bool Codegen::visit(BreakStatement *ast) bool Codegen::visit(ContinueStatement *ast) { - if (ast->label.isEmpty()) + unwindException(_loop->tryCleanup); + + if (ast->label.isEmpty()) { _block->JUMP(_loop->continueBlock); - else { + } else { for (Loop *loop = _loop; loop; loop = loop->parent) { if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { if (! loop->continueBlock) @@ -1966,6 +1971,7 @@ bool Codegen::visit(LocalForStatement *ast) bool Codegen::visit(ReturnStatement *ast) { + unwindException(0); if (ast->expression) { Result expr = expression(ast->expression); move(_block->TEMP(_returnAddress), *expr); @@ -2061,29 +2067,17 @@ bool Codegen::visit(TryStatement *ast) { IR::BasicBlock *tryBody = _function->newBasicBlock(); IR::BasicBlock *catchBody = ast->catchExpression ? _function->newBasicBlock() : 0; - IR::BasicBlock *finallyBody = ast->finallyExpression ? _function->newBasicBlock() : 0; + // We always need a finally body to clean up the exception handler + IR::BasicBlock *finallyBody = _function->newBasicBlock(); + + TryCleanup tcf(_tryCleanup, ast->finallyExpression); + _tryCleanup = &tcf; + IR::BasicBlock *after = _function->newBasicBlock(); assert(catchBody || finallyBody); - Loop *loop = 0; - if (_loop) { - // So there is a loop around the try statement, and now we have to setup - // a new loop-break-block and a new loop-continue-block so that if either - // a break or a continue is executed, they will go to these blocks - // which need to pop the exception handler. If there is a finally block, - // its code also gets generated into all the new break/continue blocks. - // And of course, as there can be a labeled break/continue, we have to - // do it for all parent loops as well. - for (Loop *it = _loop, **it2 = &loop; it; it = it->parent) { - *it2 = new Loop(it->node, _function->newBasicBlock(), _function->newBasicBlock(), 0); - (*it2)->labelledStatement = it->labelledStatement; - it2 = &(*it2)->parent; - } - std::swap(_loop, loop); - } - int inCatch = 0; - if (catchBody && finallyBody) { + if (catchBody) { inCatch = _block->newTemp(); move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, false)); } @@ -2101,26 +2095,16 @@ bool Codegen::visit(TryStatement *ast) if (catchBody) { _block = catchBody; - if (finallyBody) { - if (inCatch != 0) { - // check if an exception got thrown within catch. Go to finally - // and then rethrow - IR::BasicBlock *b = _function->newBasicBlock(); - _block->CJUMP(_block->TEMP(inCatch), finallyBody, b); - _block = b; - } - // if we have finally we need to clear the exception here, so we don't rethrow - move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, true)); - move(_block->TEMP(hasException), _block->CONST(IR::BoolType, false)); - } else { - // otherwise remove the exception handler, so that throw inside catch will - // give the correct result - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); - - // the exception handler is deleted, so we can restore the - // original break/continue blocks. - std::swap(_loop, loop); + if (inCatch != 0) { + // check if an exception got thrown within catch. Go to finally + // and then rethrow + IR::BasicBlock *b = _function->newBasicBlock(); + _block->CJUMP(_block->TEMP(inCatch), finallyBody, b); + _block = b; } + // if we have finally we need to clear the exception here, so we don't rethrow + move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, true)); + move(_block->TEMP(hasException), _block->CONST(IR::BoolType, false)); const int exception = _block->newTemp(); move(_block->TEMP(exception), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0)); @@ -2137,30 +2121,10 @@ bool Codegen::visit(TryStatement *ast) _block->JUMP(finallyBody ? finallyBody : after); } - if (finallyBody) { - // the exception handler is deleted, so we can restore the - // original break/continue blocks. - std::swap(_loop, loop); + _tryCleanup = tcf.parent; + if (finallyBody) generateFinallyBlock(finallyBody, !catchBody, ast->finallyExpression, hasException, after); - } - - if (loop) { - // now do the codegen for each break/continue block - for (Loop *it = loop, *it2 = _loop; it && it2; it = it->parent, it2 = it2->parent) { - // if the break/continue trampoline blocks get reached, there cannot - // be a pending exception, so don't bother with saving it. - generateFinallyBlock(it->breakBlock, false, ast->finallyExpression, hasException, it2->breakBlock); - generateFinallyBlock(it->continueBlock, false, ast->finallyExpression, hasException, it2->continueBlock); - } - - // we don't need the Loop struct itself anymore. - while (loop) { - Loop *next = loop->parent; - delete loop; - loop = next; - } - } _block = after; @@ -2186,9 +2150,6 @@ void Codegen::generateFinallyBlock(IR::BasicBlock *finallyBlock, bool exceptionN _block = realFinallyBlock; } - // TODO: this will not re-throw when there is a pending exception, and - // the finally block contains a break/continue. Should another set of - // break/continue trampoline blocks get inserted? _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); if (ast && ast->statement) statement(ast->statement); @@ -2204,6 +2165,19 @@ void Codegen::generateFinallyBlock(IR::BasicBlock *finallyBlock, bool exceptionN } } +void Codegen::unwindException(Codegen::TryCleanup *outest) +{ + TryCleanup *tryCleanup = _tryCleanup; + qSwap(_tryCleanup, tryCleanup); + while (_tryCleanup != outest) { + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); + TryCleanup *tc = _tryCleanup; + _tryCleanup = tc->parent; + if (tc->finally && tc->finally->statement) + statement(tc->finally->statement); + } +} + bool Codegen::visit(VariableStatement *ast) { variableDeclarationList(ast->declarations); diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 27c910fd8b..a75c872b58 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -167,12 +167,21 @@ protected: struct UiMember { }; + struct TryCleanup { + TryCleanup *parent; + AST::Finally *finally; + + TryCleanup(TryCleanup *parent, AST::Finally *finally) + : parent(parent), finally(finally) {} + }; + struct Loop { AST::LabelledStatement *labelledStatement; AST::Statement *node; IR::BasicBlock *breakBlock; IR::BasicBlock *continueBlock; Loop *parent; + TryCleanup *tryCleanup; Loop(AST::Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock, Loop *parent) : labelledStatement(0), node(node), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {} @@ -184,6 +193,7 @@ protected: void enterLoop(AST::Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock); void leaveLoop(); + IR::Expr *member(IR::Expr *base, const QString *name); IR::Expr *subscript(IR::Expr *base, IR::Expr *index); IR::Expr *argument(IR::Expr *expr); @@ -199,6 +209,7 @@ protected: int indexOfArgument(const QStringRef &string) const; void generateFinallyBlock(IR::BasicBlock *finallyBlock, bool exceptionNeedsSaving, AST::Finally *ast, int hasException, IR::BasicBlock *after); + void unwindException(TryCleanup *outest); void statement(AST::Statement *ast); void statement(AST::ExpressionNode *ast); @@ -332,6 +343,7 @@ private: Environment *_env; Loop *_loop; AST::LabelledStatement *_labelledStatement; + TryCleanup *_tryCleanup; QHash _envMap; QHash _functionMap; Debugging::Debugger *_debugger; -- cgit v1.2.3 From 73d95c5e914b3d9f31c1f673ba3edb029981dc17 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 2 Dec 2012 20:10:09 +0100 Subject: Fix clobber list for inline add/sub/mul instructions These inline asm blocks do also change flags, therefore we need to add the condition code register to the list of clobbered pseudo registers. Change-Id: Iea7b2557813bbb2fa86d7506e6416092fdc14702 Reviewed-by: Lars Knoll --- qmljs_math.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qmljs_math.h b/qmljs_math.h index 1fb9261835..b9dbff891b 100644 --- a/qmljs_math.h +++ b/qmljs_math.h @@ -60,7 +60,7 @@ static inline Value add_int32(int a, int b) "seto %0" : "=q" (overflow), "=r" (aa) : "r" (b), "1" (aa) - : + : "cc" ); if (!overflow) return Value::fromInt32(aa); @@ -76,7 +76,7 @@ static inline Value sub_int32(int a, int b) "seto %0" : "=q" (overflow), "=r" (aa) : "r" (b), "1" (aa) - : + : "cc" ); if (!overflow) return Value::fromInt32(aa); @@ -92,7 +92,7 @@ static inline Value mul_int32(int a, int b) "setc %0" : "=q" (overflow), "=a" (aa) : "r" (b), "1" (aa) - : "edx" + : "edx", "cc" ); if (!overflow) return Value::fromInt32(aa); -- cgit v1.2.3 From ecb72cd271992ed4b0b300c6b4ccbde9c3bcc729 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 2 Dec 2012 10:58:35 -0800 Subject: Pass the ExecutionContext into the code generator Use the contexts strict mode flag to correctly parse eval code inside strict mode sections. Add code to allow the code generator to throw syntax errors. Change-Id: I4e4258b0d0b88952f4d609ec51bbe8db9a1c66a9 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 2 +- qv4codegen.cpp | 28 ++++++++++++++++++++-------- qv4codegen_p.h | 9 ++++++++- qv4ecmaobjects.cpp | 2 +- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index e0ebc67b75..e2cb5cf5b4 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -569,7 +569,7 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct return 0; } - Codegen cg(ctx->engine->debugger); + Codegen cg(ctx); globalCode = cg(program, &module, mode); if (globalCode) { // only generate other functions if global code generation succeeded. diff --git a/qv4codegen.cpp b/qv4codegen.cpp index c9978ed2a8..893bbb175c 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -206,6 +207,7 @@ protected: inline void enterEnvironment(Node *node) { Environment *e = _cg->newEnvironment(node, _env); + e->isStrict = _cg->_context->strictMode; _envStack.append(e); _env = e; } @@ -248,8 +250,7 @@ protected: || name == QLatin1String("public") || name == QLatin1String("static") || name == QLatin1String("yield")) { - // TODO: error! - qDebug("TODO: give a proper error message @%u:%u", loc.startLine, loc.startColumn); + _cg->throwSyntaxError(loc, "Unexpected strict mode reserved word"); } } } @@ -353,7 +354,7 @@ protected: QStack _envStack; }; -Codegen::Codegen(Debugging::Debugger *debugger) +Codegen::Codegen(VM::ExecutionContext *context) : _module(0) , _function(0) , _block(0) @@ -365,7 +366,8 @@ Codegen::Codegen(Debugging::Debugger *debugger) , _loop(0) , _labelledStatement(0) , _tryCleanup(0) - , _debugger(debugger) + , _context(context) + , _debugger(context->engine->debugger) { } @@ -1021,7 +1023,7 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::Assign: if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) { - assert(!"syntax error"); + throwSyntaxError(ast->lastSourceLocation(), "syntax error"); } if (_expr.accept(nx)) { @@ -1046,7 +1048,7 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::InplaceURightShift: case QSOperator::InplaceXor: { if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) { - assert(!"syntax error"); + throwSyntaxError(ast->lastSourceLocation(), "syntax error"); } if (_expr.accept(nx)) { @@ -1730,7 +1732,7 @@ bool Codegen::visit(BreakStatement *ast) return false; } } - assert(!"throw syntax error"); + throwSyntaxError(ast->lastSourceLocation(), QString("Undefined label '") + ast->label.toString() + QString("'")); } return false; @@ -1752,7 +1754,7 @@ bool Codegen::visit(ContinueStatement *ast) return false; } } - assert(!"throw syntax error"); + throwSyntaxError(ast->lastSourceLocation(), QString("Undefined label '") + ast->label.toString() + QString("'")); } return false; } @@ -2264,3 +2266,13 @@ bool Codegen::visit(UiSourceElement *) assert(!"not implemented"); return false; } + +void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) +{ + VM::DiagnosticMessage *msg = new VM::DiagnosticMessage; + msg->offset = loc.begin(); + msg->startLine = loc.startLine; + msg->startColumn = loc.startColumn; + msg->message = new VM::String(detail); + _context->throwSyntaxError(msg); +} diff --git a/qv4codegen_p.h b/qv4codegen_p.h index a75c872b58..f1dca06350 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -50,6 +50,10 @@ namespace AST { class UiParameterList; } +namespace VM { +struct ExecutionContext; +} + namespace Debugging { class Debugger; } // namespace Debugging @@ -57,7 +61,7 @@ class Debugger; class Codegen: protected AST::Visitor { public: - Codegen(Debugging::Debugger *debugger); + Codegen(VM::ExecutionContext *ctx); enum Mode { GlobalCode, @@ -329,6 +333,8 @@ protected: virtual bool visit(AST::UiScriptBinding *ast); virtual bool visit(AST::UiSourceElement *ast); + void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); + private: Result _expr; QString _property; @@ -346,6 +352,7 @@ private: TryCleanup *_tryCleanup; QHash _envMap; QHash _functionMap; + VM::ExecutionContext *_context; Debugging::Debugger *_debugger; class ScanFunctions; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 8edd43cb14..99375d2e82 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1892,7 +1892,7 @@ Value FunctionCtor::construct(ExecutionContext *ctx) IR::Module module; - Codegen cg(ctx->engine->debugger); + Codegen cg(ctx); IR::Function *irf = cg(fe, &module); EvalInstructionSelection *isel = ctx->engine->iselFactory->create(ctx->engine); -- cgit v1.2.3 From a2b6483dbebd0faa768e049d69d9aa621b8e0fad Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 4 Dec 2012 10:55:01 +0100 Subject: Merged Value load instructions. Change-Id: I45601d9618f8f569ff5705693fbea383d73c031d Reviewed-by: Simon Hausmann --- moth/qv4instr_moth_p.h | 39 ++++----------------------------------- moth/qv4isel_moth.cpp | 40 ++++++---------------------------------- moth/qv4vme_moth.cpp | 25 ++----------------------- 3 files changed, 12 insertions(+), 92 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index c3be3a2a08..67d11370ac 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -6,12 +6,7 @@ #define FOR_EACH_MOTH_INSTR(F) \ F(Ret, ret) \ - F(LoadUndefined, loadUndefined) \ - F(LoadNull, loadNull) \ - F(LoadFalse, loadFalse) \ - F(LoadTrue, loadTrue) \ - F(LoadNumber, loadNumber) \ - F(LoadString, loadString) \ + F(LoadValue, loadValue) \ F(LoadClosure, loadClosure) \ F(MoveTemp, moveTemp) \ F(LoadName, loadName) \ @@ -79,37 +74,16 @@ union Instr MOTH_INSTR_HEADER int tempIndex; }; - struct instr_loadUndefined { - MOTH_INSTR_HEADER - int targetTempIndex; - }; - struct instr_loadNull { - MOTH_INSTR_HEADER - int targetTempIndex; - }; - struct instr_loadFalse { - MOTH_INSTR_HEADER - int targetTempIndex; - }; - struct instr_loadTrue { + struct instr_loadValue { MOTH_INSTR_HEADER int targetTempIndex; + VM::Value value; }; struct instr_moveTemp { MOTH_INSTR_HEADER int fromTempIndex; int toTempIndex; }; - struct instr_loadNumber { - MOTH_INSTR_HEADER - double value; - int targetTempIndex; - }; - struct instr_loadString { - MOTH_INSTR_HEADER - VM::String *value; - int targetTempIndex; - }; struct instr_loadClosure { MOTH_INSTR_HEADER IR::Function *value; @@ -291,13 +265,8 @@ union Instr instr_common common; instr_ret ret; - instr_loadUndefined loadUndefined; - instr_loadNull loadNull; - instr_loadFalse loadFalse; - instr_loadTrue loadTrue; + instr_loadValue loadValue; instr_moveTemp moveTemp; - instr_loadNumber loadNumber; - instr_loadString loadString; instr_loadClosure loadClosure; instr_loadName loadName; instr_storeName storeName; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 482d0633b0..10a5c1106d 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -592,38 +592,10 @@ void InstructionSelection::visitMove(IR::Move *s) } else { IR::Const *c = s->source->asConst(); assert(c); - switch (c->type) { - case IR::UndefinedType: { - Instruction::LoadUndefined load; - load.targetTempIndex = targetTempIndex; - addInstruction(load); - } break; - case IR::NullType: { - Instruction::LoadNull load; - load.targetTempIndex = targetTempIndex; - addInstruction(load); - } break; - case IR::BoolType: - if (c->value) { - Instruction::LoadTrue load; - load.targetTempIndex = targetTempIndex; - addInstruction(load); - } else { - Instruction::LoadFalse load; - load.targetTempIndex = targetTempIndex; - addInstruction(load); - } - break; - case IR::NumberType: { - Instruction::LoadNumber load; - load.value = c->value; - load.targetTempIndex = targetTempIndex; - addInstruction(load); - } break; - default: - Q_UNREACHABLE(); - break; - } + Instruction::LoadValue load; + load.targetTempIndex = targetTempIndex; + load.value = convertToValue(c); + addInstruction(load); } } else { Instruction::Binop binop; @@ -634,8 +606,8 @@ void InstructionSelection::visitMove(IR::Move *s) addInstruction(binop); } } else if (IR::String *str = s->source->asString()) { - Instruction::LoadString load; - load.value = _engine->newString(*str->value); + Instruction::LoadValue load; + load.value = VM::Value::fromString(_engine->newString(*str->value)); load.targetTempIndex = targetTempIndex; addInstruction(load); } else if (IR::Closure *clos = s->source->asClosure()) { diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 138d81a635..c83e35f1a2 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -147,31 +147,10 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co TEMP(instr.toTempIndex) = tmp; MOTH_END_INSTR(MoveTemp) - MOTH_BEGIN_INSTR(LoadUndefined) - TEMP(instr.targetTempIndex) = VM::Value::undefinedValue(); + MOTH_BEGIN_INSTR(LoadValue) + TEMP(instr.targetTempIndex) = instr.value; MOTH_END_INSTR(LoadUndefined) - MOTH_BEGIN_INSTR(LoadNull) - TEMP(instr.targetTempIndex) = VM::Value::nullValue(); - MOTH_END_INSTR(LoadNull) - - MOTH_BEGIN_INSTR(LoadTrue) - TEMP(instr.targetTempIndex) = VM::Value::fromBoolean(true); - MOTH_END_INSTR(LoadTrue) - - MOTH_BEGIN_INSTR(LoadFalse) - TEMP(instr.targetTempIndex) = VM::Value::fromBoolean(false); - MOTH_END_INSTR(LoadFalse) - - MOTH_BEGIN_INSTR(LoadNumber) - TRACE(inline, "number = %f", instr.value); - TEMP(instr.targetTempIndex) = VM::Value::fromDouble(instr.value); - MOTH_END_INSTR(LoadNumber) - - MOTH_BEGIN_INSTR(LoadString) - TEMP(instr.targetTempIndex) = VM::Value::fromString(instr.value); - MOTH_END_INSTR(LoadString) - MOTH_BEGIN_INSTR(LoadClosure) VM::Value c = __qmljs_init_closure(instr.value, context); TEMP(instr.targetTempIndex) = c; -- cgit v1.2.3 From 070e0d07d821342bfbe7a409a51c4c53185c62c7 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 4 Dec 2012 11:30:26 +0100 Subject: Fix another memory leak. Now we should only leak objects that are VM::Values, and directly related objects like the PropertyTable. The directly related ones will disappear through destructor calls when the VM::Values are GC-ed. Change-Id: Id221ac62a22671d86bbc6ac9beea49d2bd5b330d Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 7 +++++++ qmljs_engine.h | 1 + 2 files changed, 8 insertions(+) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 6c5edd16e2..4af900549d 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -187,6 +187,13 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) glo->__put__(rootContext, identifier(QStringLiteral("isFinite")), Value::fromObject(new IsFiniteFunction(rootContext))); // isFinite [15.1.2.5] } +ExecutionEngine::~ExecutionEngine() +{ + delete globalObject.asObject(); + delete rootContext; + delete stringPool; // the String pointers should get GC-ed. +} + ExecutionContext *ExecutionEngine::newContext() { return new ExecutionContext(); diff --git a/qmljs_engine.h b/qmljs_engine.h index a6f1808676..d47c2d46b6 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -150,6 +150,7 @@ struct ExecutionEngine struct StringPool *stringPool; ExecutionEngine(EvalISelFactory *iselFactory); + ~ExecutionEngine(); ExecutionContext *newContext(); -- cgit v1.2.3 From 955f5f03afd9915b7f43ff07ce4b624a86c58a1a Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 4 Dec 2012 12:00:23 +0100 Subject: Allow only the ExecutionEngine's StringPool to create Strings. Strings are the only non-Object Values living on the heap. So by tracking creation, we can help the future GC a lot. Change-Id: I5d5044f9ff10da42aeb75dd4a556d6ab3d839b1a Reviewed-by: Lars Knoll --- main.cpp | 2 +- qmljs_environment.cpp | 16 ++++++++++------ qmljs_environment.h | 5 +++-- qmljs_objects.cpp | 8 ++++---- qmljs_objects.h | 9 ++++++--- qv4codegen.cpp | 9 ++++++--- qv4codegen_p.h | 5 +++-- qv4ecmaobjects.cpp | 8 +++----- 8 files changed, 36 insertions(+), 26 deletions(-) diff --git a/main.cpp b/main.cpp index 9090cd2a9f..a516953008 100644 --- a/main.cpp +++ b/main.cpp @@ -186,7 +186,7 @@ int compile(const QString &fileName, const QString &source, QQmlJS::LLVMOutputTy Codegen cg(0); // FIXME: if the program is empty, we should we generate an empty %entry, or give an error? - /*IR::Function *globalCode =*/ cg(program, &module); + /*IR::Function *globalCode =*/ cg(fileName, program, &module); int (*exec)(void *) = outputType == LLVMOutputJit ? executeLLVMCode : 0; return compileWithLLVM(&module, fileName, outputType, exec); diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index ce1bf9da82..94a877586a 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -39,6 +39,7 @@ ** ****************************************************************************/ +#include #include "debugging.h" #include #include @@ -48,27 +49,30 @@ namespace QQmlJS { namespace VM { DiagnosticMessage::DiagnosticMessage() - : fileName(0) - , offset(0) + : offset(0) , length(0) , startLine(0) , startColumn(0) , type(0) - , message(0) , next(0) {} +DiagnosticMessage::~DiagnosticMessage() +{ + delete next; +} + String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const { QString msg; - if (fileName) - msg = fileName->toQString() + QLatin1Char(':'); + if (!fileName.isEmpty()) + msg = fileName + QLatin1Char(':'); msg += QString::number(startLine) + QLatin1Char(':') + QString::number(startColumn) + QLatin1String(": "); if (type == QQmlJS::VM::DiagnosticMessage::Error) msg += QLatin1String("error"); else msg += QLatin1String("warning"); - msg += ": " + message->toQString(); + msg += ": " + message; return ctx->engine->newString(msg); } diff --git a/qmljs_environment.h b/qmljs_environment.h index 3457771296..eb10b6630d 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -56,16 +56,17 @@ struct DiagnosticMessage { enum { Error, Warning }; - String *fileName; + QString fileName; quint32 offset; quint32 length; quint32 startLine; unsigned startColumn: 31; unsigned type: 1; - String *message; + QString message; DiagnosticMessage *next; DiagnosticMessage(); + ~DiagnosticMessage(); String *buildFullMessage(ExecutionContext *ctx) const; }; diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index e2cb5cf5b4..f12999bc22 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -543,14 +543,14 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct VM::DiagnosticMessage *error = 0, **errIt = &error; foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { if (m.isError()) { - *errIt = new VM::DiagnosticMessage; // FIXME: should we ask the engine to create this object? - (*errIt)->fileName = ctx->engine->newString(fileName); + *errIt = new VM::DiagnosticMessage; + (*errIt)->fileName = fileName; (*errIt)->offset = m.loc.offset; (*errIt)->length = m.loc.length; (*errIt)->startLine = m.loc.startLine; (*errIt)->startColumn = m.loc.startColumn; (*errIt)->type = VM::DiagnosticMessage::Error; - (*errIt)->message = ctx->engine->newString(m.message); + (*errIt)->message = m.message; errIt = &(*errIt)->next; } else { std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn @@ -570,7 +570,7 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct } Codegen cg(ctx); - globalCode = cg(program, &module, mode); + globalCode = cg(fileName, program, &module, mode); if (globalCode) { // only generate other functions if global code generation succeeded. foreach (IR::Function *function, module.functions) { diff --git a/qmljs_objects.h b/qmljs_objects.h index cd9c2430cc..2590b012b2 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -94,9 +94,6 @@ struct TypeErrorPrototype; struct URIErrorPrototype; struct String { - String(const QString &text) - : _text(text), _hashValue(0) {} - inline bool isEqualTo(const String *other) const { if (this == other) return true; @@ -116,6 +113,11 @@ struct String { return _hashValue; } +private: + friend struct StringPool; + String(const QString &text) + : _text(text), _hashValue(0) {} + private: QString _text; mutable unsigned _hashValue; @@ -613,6 +615,7 @@ struct ReferenceErrorObject: ErrorObject { struct SyntaxErrorObject: ErrorObject { SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg); + ~SyntaxErrorObject() { delete msg; } virtual QString className() { return QStringLiteral("SyntaxError"); } virtual SyntaxErrorObject *asSyntaxError() { return this; } diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 893bbb175c..34c785d634 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -371,10 +371,11 @@ Codegen::Codegen(VM::ExecutionContext *context) { } -IR::Function *Codegen::operator()(Program *node, IR::Module *module, Mode mode) +IR::Function *Codegen::operator()(const QString &fileName, Program *node, IR::Module *module, Mode mode) { assert(node); + _fileName = fileName; _module = module; _env = 0; @@ -399,8 +400,9 @@ IR::Function *Codegen::operator()(Program *node, IR::Module *module, Mode mode) return globalCode; } -IR::Function *Codegen::operator()(AST::FunctionExpression *ast, IR::Module *module) +IR::Function *Codegen::operator()(const QString &fileName, AST::FunctionExpression *ast, IR::Module *module) { + _fileName = fileName; _module = module; _env = 0; @@ -2270,9 +2272,10 @@ bool Codegen::visit(UiSourceElement *) void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) { VM::DiagnosticMessage *msg = new VM::DiagnosticMessage; + msg->fileName = _fileName; msg->offset = loc.begin(); msg->startLine = loc.startLine; msg->startColumn = loc.startColumn; - msg->message = new VM::String(detail); + msg->message = detail; _context->throwSyntaxError(msg); } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index f1dca06350..474e5580e8 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -69,8 +69,8 @@ public: FunctionCode }; - IR::Function *operator()(AST::Program *ast, IR::Module *module, Mode mode = GlobalCode); - IR::Function *operator()(AST::FunctionExpression *ast, IR::Module *module); + IR::Function *operator()(const QString &fileName, AST::Program *ast, IR::Module *module, Mode mode = GlobalCode); + IR::Function *operator()(const QString &fileName, AST::FunctionExpression *ast, IR::Module *module); protected: enum Format { ex, cx, nx }; @@ -336,6 +336,7 @@ protected: void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); private: + QString _fileName; Result _expr; QString _property; UiMember _uiMember; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 99375d2e82..607c1f1624 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1893,7 +1893,7 @@ Value FunctionCtor::construct(ExecutionContext *ctx) IR::Module module; Codegen cg(ctx); - IR::Function *irf = cg(fe, &module); + IR::Function *irf = cg(QString(), fe, &module); EvalInstructionSelection *isel = ctx->engine->iselFactory->create(ctx->engine); isel->run(irf); @@ -2779,16 +2779,14 @@ Value ErrorPrototype::method_toString(ExecutionContext *ctx) if (!o) __qmljs_throw_type_error(ctx); - String n(QString::fromLatin1("name")); - Value name = o->__get__(ctx, &n); + Value name = o->__get__(ctx, ctx->engine->newString(QString::fromLatin1("name"))); QString qname; if (name.isUndefined()) qname = QString::fromLatin1("Error"); else qname = __qmljs_to_string(name, ctx).stringValue()->toQString(); - String m(QString::fromLatin1("message")); - Value message = o->__get__(ctx, &m); + Value message = o->__get__(ctx, ctx->engine->newString(QString::fromLatin1("message"))); QString qmessage; if (!message.isUndefined()) qmessage = __qmljs_to_string(message, ctx).stringValue()->toQString(); -- cgit v1.2.3 From 944af86fca2eac90cd6528d2dfbff5316fa911a9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 4 Dec 2012 10:50:25 -0800 Subject: Throw proper type and reference errors Change-Id: I898017f3e63ada72fc2e50abfa1880f9fd7ffe37 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 14 ++++++++++++++ qmljs_engine.h | 3 +++ qmljs_environment.cpp | 5 ++--- qmljs_objects.h | 4 ++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 4af900549d..027cc33a9d 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -348,6 +348,20 @@ Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticM return object; } +Object *ExecutionEngine::newReferenceErrorObject(ExecutionContext *ctx, const QString &message) +{ + ReferenceErrorObject *object = new ReferenceErrorObject(ctx, message); + object->prototype = referenceErrorPrototype; + return object; +} + +Object *ExecutionEngine::newTypeErrorObject(ExecutionContext *ctx, const QString &message) +{ + TypeErrorObject *object = new TypeErrorObject(ctx, message); + object->prototype = typeErrorPrototype; + return object; +} + Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) { MathObject *object = new MathObject(ctx); diff --git a/qmljs_engine.h b/qmljs_engine.h index d47c2d46b6..2b053fd0f2 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -186,6 +186,9 @@ struct ExecutionEngine Object *newErrorObject(const Value &value); Object *newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message); + Object *newReferenceErrorObject(ExecutionContext *ctx, const QString &message); + Object *newTypeErrorObject(ExecutionContext *ctx, const QString &message); + Object *newMathObject(ExecutionContext *ctx); Object *newActivationObject(ExecutionContext *ctx); diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 94a877586a..12a2091dc8 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -284,8 +284,7 @@ void ExecutionContext::throwSyntaxError(DiagnosticMessage *message) void ExecutionContext::throwTypeError() { - Value v = Value::fromString(this, QStringLiteral("Type error")); - throwError(Value::fromObject(engine->newErrorObject(v))); + throwError(Value::fromObject(engine->newTypeErrorObject(this, QStringLiteral("Type error")))); } void ExecutionContext::throwUnimplemented(const QString &message) @@ -298,7 +297,7 @@ void ExecutionContext::throwReferenceError(Value value) { String *s = value.toString(this); QString msg = s->toQString() + QStringLiteral(" is not defined"); - throwError(Value::fromObject(engine->newErrorObject(Value::fromString(this, msg)))); + throwError(Value::fromObject(engine->newReferenceErrorObject(this, msg))); } void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc) diff --git a/qmljs_objects.h b/qmljs_objects.h index 2590b012b2..4a98ee16d5 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -610,6 +610,8 @@ struct RangeErrorObject: ErrorObject { struct ReferenceErrorObject: ErrorObject { ReferenceErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + ReferenceErrorObject(ExecutionContext *ctx, const QString &msg) + : ErrorObject(Value::fromString(ctx,msg)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("ReferenceError"); } }; @@ -628,6 +630,8 @@ private: struct TypeErrorObject: ErrorObject { TypeErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + TypeErrorObject(ExecutionContext *ctx, const QString &msg) + : ErrorObject(Value::fromString(ctx,msg)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("TypeError"); } }; -- cgit v1.2.3 From f4e8ad5e609921e677e579e01020930c64f652ba Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 4 Dec 2012 11:02:26 -0800 Subject: Correctly set the strict mode flag when calling eval() Change-Id: I677eea907a3b79373c0784c1496ed97f0f5ee6f6 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index f12999bc22..12cc44783f 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -508,8 +508,14 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value ctx = context; } + // set the correct strict mode flag on the context + bool cstrict = ctx->strictMode; + ctx->strictMode = strict; + Value result = f->code(ctx, f->codeData); + ctx->strictMode = cstrict; + if (strict) ctx->leaveCallContext(); -- cgit v1.2.3 From f75c05b7d2495ad4b87e57472ebff745048721d9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 2 Dec 2012 19:34:50 +0100 Subject: [masm] Cleanup: avoid redundant convertToValue() call in binop generation We already do that conversion to a constant VM::Value earlier. Change-Id: I142435f2be7c841f322ccfc34473b23cf85e6b5c Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index bb9e224302..a0787a09a2 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -803,8 +803,8 @@ void InstructionSelection::generateBinOp(IR::AluOp operation, IR::Temp* target, return; } - VM::Value leftConst; - VM::Value rightConst; + Value leftConst = Value::undefinedValue(); + Value rightConst = Value::undefinedValue(); bool canDoInline = info.inlineMemOp && info.inlineImmOp; @@ -853,8 +853,7 @@ void InstructionSelection::generateBinOp(IR::AluOp operation, IR::Temp* target, overflowCheck = (this->*info.inlineMemOp)(Overflow, rightValue, IntegerOpRegister); } else { // right->asConst() - VM::Value value = convertToValue(right->asConst()); - overflowCheck = (this->*info.inlineImmOp)(Overflow, TrustedImm32(value.integerValue()), IntegerOpRegister); + overflowCheck = (this->*info.inlineImmOp)(Overflow, TrustedImm32(rightConst.integerValue()), IntegerOpRegister); } Address resultAddr = loadTempAddress(ScratchRegister, target); -- cgit v1.2.3 From cde1dd670a49bc37809e8100bef1b7c711779a31 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 4 Dec 2012 20:00:25 +0100 Subject: Use two operand version of imul This is a slightly nicer variant to use and is easier on the register usage Change-Id: I6c9299f99251594b5a1adaed1e7dae9a5419c370 Reviewed-by: Lars Knoll --- qmljs_math.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qmljs_math.h b/qmljs_math.h index b9dbff891b..4645c5a324 100644 --- a/qmljs_math.h +++ b/qmljs_math.h @@ -88,11 +88,11 @@ static inline Value mul_int32(int a, int b) quint8 overflow = 0; int aa = a; - asm ("imul %2\n" + asm ("imul %2, %1\n" "setc %0" - : "=q" (overflow), "=a" (aa) + : "=q" (overflow), "=r" (aa) : "r" (b), "1" (aa) - : "edx", "cc" + : "cc" ); if (!overflow) return Value::fromInt32(aa); -- cgit v1.2.3 From 8a6402d494131242f80e1464f3f628cd770bea45 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 4 Dec 2012 20:06:26 +0100 Subject: [masm] Implement inline multiplication Unfortunately this requires an extra entry in the info for the binary op, because we can't do mul(imm, reg) only mul(imm, reg, reg). Change-Id: I75beb3cb08ff24421483e824afaa9703befa488a Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index a0787a09a2..f6ea055dfb 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -62,23 +62,25 @@ namespace { QTextStream qout(stderr, QIODevice::WriteOnly); } -typedef JSC::MacroAssembler::Jump (JSC::MacroAssembler::*MemBinOpWithOverFlow)(JSC::MacroAssembler::ResultCondition, JSC::MacroAssembler::Address, JSC::MacroAssembler::RegisterID); -typedef JSC::MacroAssembler::Jump (JSC::MacroAssembler::*ImmBinOpWithOverFlow)(JSC::MacroAssembler::ResultCondition, JSC::MacroAssembler::TrustedImm32, JSC::MacroAssembler::RegisterID); +typedef JSC::MacroAssembler::Jump (JSC::MacroAssembler::*MemRegBinOp)(JSC::MacroAssembler::ResultCondition, JSC::MacroAssembler::Address, JSC::MacroAssembler::RegisterID); +typedef JSC::MacroAssembler::Jump (JSC::MacroAssembler::*ImmRegBinOp)(JSC::MacroAssembler::ResultCondition, JSC::MacroAssembler::TrustedImm32, JSC::MacroAssembler::RegisterID); +typedef JSC::MacroAssembler::Jump (JSC::MacroAssembler::*ImmRegRegBinOp)(JSC::MacroAssembler::ResultCondition, JSC::MacroAssembler::TrustedImm32, JSC::MacroAssembler::RegisterID, JSC::MacroAssembler::RegisterID); #define OP(op) \ - { isel_stringIfy(op), op, 0, 0 } + { isel_stringIfy(op), op, 0, 0, 0 } #define INLINE_OP(op, memOp, immOp) \ - { isel_stringIfy(op), op, memOp, immOp } + { isel_stringIfy(op), op, memOp, immOp, 0 } #define NULL_OP \ - { 0, 0, 0, 0 } + { 0, 0, 0, 0, 0 } static const struct BinaryOperationInfo { const char *name; Value (*fallbackImplementation)(const Value, const Value, ExecutionContext *); - MemBinOpWithOverFlow inlineMemOp; - ImmBinOpWithOverFlow inlineImmOp; + MemRegBinOp inlineMemRegOp; + ImmRegBinOp inlineImmRegOp; + ImmRegRegBinOp inlineImmRegRegOp; } binaryOperations[QQmlJS::IR::LastAluOp + 1] = { NULL_OP, // OpInvalid NULL_OP, // OpIfTrue @@ -93,7 +95,10 @@ static const struct BinaryOperationInfo { INLINE_OP(__qmljs_add, &JSC::MacroAssembler::branchAdd32, &JSC::MacroAssembler::branchAdd32), // OpAdd INLINE_OP(__qmljs_sub, &JSC::MacroAssembler::branchSub32, &JSC::MacroAssembler::branchSub32), // OpSub - OP(__qmljs_mul), // OpMul + // There's no branchMul32(Immediate, Reg) variant, only branchMul32(Immediate, Reg, Reg), so we need + // to use the last entry in the struct. + { "__qmljs_mul", __qmljs_mul, &JSC::MacroAssembler::branchMul32, 0, &JSC::MacroAssembler::branchMul32 }, + OP(__qmljs_div), // OpDiv OP(__qmljs_mod), // OpMod @@ -806,7 +811,7 @@ void InstructionSelection::generateBinOp(IR::AluOp operation, IR::Temp* target, Value leftConst = Value::undefinedValue(); Value rightConst = Value::undefinedValue(); - bool canDoInline = info.inlineMemOp && info.inlineImmOp; + bool canDoInline = info.inlineMemRegOp && (info.inlineImmRegOp || info.inlineImmRegRegOp); if (canDoInline) { if (left->asConst()) { @@ -851,9 +856,12 @@ void InstructionSelection::generateBinOp(IR::AluOp operation, IR::Temp* target, Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); rightValue.offset += offsetof(VM::Value, int_32); - overflowCheck = (this->*info.inlineMemOp)(Overflow, rightValue, IntegerOpRegister); + overflowCheck = (this->*info.inlineMemRegOp)(Overflow, rightValue, IntegerOpRegister); } else { // right->asConst() - overflowCheck = (this->*info.inlineImmOp)(Overflow, TrustedImm32(rightConst.integerValue()), IntegerOpRegister); + if (info.inlineImmRegOp) + overflowCheck = (this->*info.inlineImmRegOp)(Overflow, TrustedImm32(rightConst.integerValue()), IntegerOpRegister); + else + overflowCheck = (this->*info.inlineImmRegRegOp)(Overflow, TrustedImm32(rightConst.integerValue()), IntegerOpRegister, IntegerOpRegister); } Address resultAddr = loadTempAddress(ScratchRegister, target); -- cgit v1.2.3 From b40b5102aba7967a95ba39d6f59bb20458d50762 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 4 Dec 2012 20:37:05 +0100 Subject: [masm] Simplify inline op registration code Don't store pointers to the JSC::MacroAssembler functions directly in our binops table but provide wrapper functions. Those can do operation specific things, like in case of mul32 map from (imm, reg) to (imm, reg, reg) or take care of overflow handling (needed for add/mul, but not shl/shr). Change-Id: I63297c5be22c2b978b5dedabdc3baa5be0e586af Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 62 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index f6ea055dfb..1d26f44ffb 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -62,25 +62,53 @@ namespace { QTextStream qout(stderr, QIODevice::WriteOnly); } -typedef JSC::MacroAssembler::Jump (JSC::MacroAssembler::*MemRegBinOp)(JSC::MacroAssembler::ResultCondition, JSC::MacroAssembler::Address, JSC::MacroAssembler::RegisterID); -typedef JSC::MacroAssembler::Jump (JSC::MacroAssembler::*ImmRegBinOp)(JSC::MacroAssembler::ResultCondition, JSC::MacroAssembler::TrustedImm32, JSC::MacroAssembler::RegisterID); -typedef JSC::MacroAssembler::Jump (JSC::MacroAssembler::*ImmRegRegBinOp)(JSC::MacroAssembler::ResultCondition, JSC::MacroAssembler::TrustedImm32, JSC::MacroAssembler::RegisterID, JSC::MacroAssembler::RegisterID); +typedef JSC::MacroAssembler::Jump (*MemRegBinOp)(JSC::MacroAssembler*, JSC::MacroAssembler::Address, JSC::MacroAssembler::RegisterID); +typedef JSC::MacroAssembler::Jump (*ImmRegBinOp)(JSC::MacroAssembler*, JSC::MacroAssembler::TrustedImm32, JSC::MacroAssembler::RegisterID); + +static JSC::MacroAssembler::Jump masm_add32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) +{ + return assembler->branchAdd32(JSC::MacroAssembler::Overflow, addr, reg); +} + +static JSC::MacroAssembler::Jump masm_add32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) +{ + return assembler->branchAdd32(JSC::MacroAssembler::Overflow, imm, reg); +} + +static JSC::MacroAssembler::Jump masm_sub32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) +{ + return assembler->branchSub32(JSC::MacroAssembler::Overflow, addr, reg); +} + +static JSC::MacroAssembler::Jump masm_sub32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) +{ + return assembler->branchSub32(JSC::MacroAssembler::Overflow, imm, reg); +} + +static JSC::MacroAssembler::Jump masm_mul32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) +{ + return assembler->branchMul32(JSC::MacroAssembler::Overflow, addr, reg); +} + +static JSC::MacroAssembler::Jump masm_mul32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) +{ + return assembler->branchMul32(JSC::MacroAssembler::Overflow, imm, reg, reg); +} #define OP(op) \ - { isel_stringIfy(op), op, 0, 0, 0 } + { isel_stringIfy(op), op, 0, 0 } #define INLINE_OP(op, memOp, immOp) \ - { isel_stringIfy(op), op, memOp, immOp, 0 } + { isel_stringIfy(op), op, memOp, immOp } #define NULL_OP \ - { 0, 0, 0, 0, 0 } + { 0, 0, 0, 0 } static const struct BinaryOperationInfo { const char *name; Value (*fallbackImplementation)(const Value, const Value, ExecutionContext *); MemRegBinOp inlineMemRegOp; ImmRegBinOp inlineImmRegOp; - ImmRegRegBinOp inlineImmRegRegOp; } binaryOperations[QQmlJS::IR::LastAluOp + 1] = { NULL_OP, // OpInvalid NULL_OP, // OpIfTrue @@ -93,11 +121,9 @@ static const struct BinaryOperationInfo { OP(__qmljs_bit_or), // OpBitOr OP(__qmljs_bit_xor), // OpBitXor - INLINE_OP(__qmljs_add, &JSC::MacroAssembler::branchAdd32, &JSC::MacroAssembler::branchAdd32), // OpAdd - INLINE_OP(__qmljs_sub, &JSC::MacroAssembler::branchSub32, &JSC::MacroAssembler::branchSub32), // OpSub - // There's no branchMul32(Immediate, Reg) variant, only branchMul32(Immediate, Reg, Reg), so we need - // to use the last entry in the struct. - { "__qmljs_mul", __qmljs_mul, &JSC::MacroAssembler::branchMul32, 0, &JSC::MacroAssembler::branchMul32 }, + INLINE_OP(__qmljs_add, &masm_add32, &masm_add32), // OpAdd + INLINE_OP(__qmljs_sub, &masm_sub32, &masm_sub32), // OpSub + INLINE_OP(__qmljs_mul, &masm_mul32, &masm_mul32), // OpMul OP(__qmljs_div), // OpDiv OP(__qmljs_mod), // OpMod @@ -811,7 +837,7 @@ void InstructionSelection::generateBinOp(IR::AluOp operation, IR::Temp* target, Value leftConst = Value::undefinedValue(); Value rightConst = Value::undefinedValue(); - bool canDoInline = info.inlineMemRegOp && (info.inlineImmRegOp || info.inlineImmRegRegOp); + bool canDoInline = info.inlineMemRegOp && info.inlineImmRegOp; if (canDoInline) { if (left->asConst()) { @@ -856,12 +882,9 @@ void InstructionSelection::generateBinOp(IR::AluOp operation, IR::Temp* target, Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); rightValue.offset += offsetof(VM::Value, int_32); - overflowCheck = (this->*info.inlineMemRegOp)(Overflow, rightValue, IntegerOpRegister); + overflowCheck = info.inlineMemRegOp(this, rightValue, IntegerOpRegister); } else { // right->asConst() - if (info.inlineImmRegOp) - overflowCheck = (this->*info.inlineImmRegOp)(Overflow, TrustedImm32(rightConst.integerValue()), IntegerOpRegister); - else - overflowCheck = (this->*info.inlineImmRegRegOp)(Overflow, TrustedImm32(rightConst.integerValue()), IntegerOpRegister, IntegerOpRegister); + overflowCheck = info.inlineImmRegOp(this, TrustedImm32(rightConst.integerValue()), IntegerOpRegister); } Address resultAddr = loadTempAddress(ScratchRegister, target); @@ -879,7 +902,8 @@ void InstructionSelection::generateBinOp(IR::AluOp operation, IR::Temp* target, leftTypeCheck.link(this); if (rightTypeCheck.isSet()) rightTypeCheck.link(this); - overflowCheck.link(this); + if (overflowCheck.isSet()) + overflowCheck.link(this); } // Fallback -- cgit v1.2.3 From 008e910249fa144408dc0d934e6c274269898b91 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 4 Dec 2012 20:54:54 +0100 Subject: [masm] Implement shl and shr inline Change-Id: Ibc5475030a68d9270e283aa8ac981661c590a29f Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 34 ++++++++++++++++++++++++++++++++-- qv4isel_masm_p.h | 2 +- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 1d26f44ffb..2cf0a747a8 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -95,6 +95,36 @@ static JSC::MacroAssembler::Jump masm_mul32(JSC::MacroAssembler* assembler, JSC: return assembler->branchMul32(JSC::MacroAssembler::Overflow, imm, reg, reg); } +static JSC::MacroAssembler::Jump masm_shl32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) +{ + assembler->load32(addr, InstructionSelection::ScratchRegister); + assembler->and32(JSC::MacroAssembler::TrustedImm32(0x1f), InstructionSelection::ScratchRegister); + assembler->lshift32(InstructionSelection::ScratchRegister, reg); + return JSC::MacroAssembler::Jump(); +} + +static JSC::MacroAssembler::Jump masm_shl32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) +{ + imm.m_value &= 0x1f; + assembler->lshift32(imm, reg); + return JSC::MacroAssembler::Jump(); +} + +static JSC::MacroAssembler::Jump masm_shr32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) +{ + assembler->load32(addr, InstructionSelection::ScratchRegister); + assembler->and32(JSC::MacroAssembler::TrustedImm32(0x1f), InstructionSelection::ScratchRegister); + assembler->rshift32(InstructionSelection::ScratchRegister, reg); + return JSC::MacroAssembler::Jump(); +} + +static JSC::MacroAssembler::Jump masm_shr32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) +{ + imm.m_value &= 0x1f; + assembler->rshift32(imm, reg); + return JSC::MacroAssembler::Jump(); +} + #define OP(op) \ { isel_stringIfy(op), op, 0, 0 } @@ -128,8 +158,8 @@ static const struct BinaryOperationInfo { OP(__qmljs_div), // OpDiv OP(__qmljs_mod), // OpMod - OP(__qmljs_shl), // OpLShift - OP(__qmljs_shr), // OpRShift + INLINE_OP(__qmljs_shl, &masm_shl32, &masm_shl32), // OpLShift + INLINE_OP(__qmljs_shr, &masm_shr32, &masm_shr32), // OpRShift OP(__qmljs_ushr), // OpURShift OP(__qmljs_gt), // OpGt diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 4d15fd3d1e..16cd42796a 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -65,7 +65,6 @@ public: { this->operator()(function); } void operator()(IR::Function *function); -protected: #if CPU(X86) #undef VALUE_FITS_IN_REGISTER @@ -148,6 +147,7 @@ protected: #error Argh. #endif +protected: struct VoidType {}; static const VoidType Void; -- cgit v1.2.3 From 51c0e3da8fd64a1b4b8a42c5dac61ecff4a8f9ae Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 4 Dec 2012 20:59:13 +0100 Subject: [masm] Implement and/or and xor inline Change-Id: I1e2703eaf1f8d4f5397690380ab6a76859ee1720 Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 2cf0a747a8..4b1248c247 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -125,6 +125,43 @@ static JSC::MacroAssembler::Jump masm_shr32(JSC::MacroAssembler* assembler, JSC: return JSC::MacroAssembler::Jump(); } +static JSC::MacroAssembler::Jump masm_and32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) +{ + assembler->and32(addr, reg); + return JSC::MacroAssembler::Jump(); +} + +static JSC::MacroAssembler::Jump masm_and32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) +{ + assembler->and32(imm, reg); + return JSC::MacroAssembler::Jump(); +} + +static JSC::MacroAssembler::Jump masm_or32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) +{ + assembler->or32(addr, reg); + return JSC::MacroAssembler::Jump(); +} + +static JSC::MacroAssembler::Jump masm_or32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) +{ + assembler->or32(imm, reg); + return JSC::MacroAssembler::Jump(); +} + +static JSC::MacroAssembler::Jump masm_xor32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) +{ + assembler->xor32(addr, reg); + return JSC::MacroAssembler::Jump(); +} + +static JSC::MacroAssembler::Jump masm_xor32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) +{ + assembler->xor32(imm, reg); + return JSC::MacroAssembler::Jump(); +} + + #define OP(op) \ { isel_stringIfy(op), op, 0, 0 } @@ -147,9 +184,9 @@ static const struct BinaryOperationInfo { NULL_OP, // OpUPlus NULL_OP, // OpCompl - OP(__qmljs_bit_and), // OpBitAnd - OP(__qmljs_bit_or), // OpBitOr - OP(__qmljs_bit_xor), // OpBitXor + INLINE_OP(__qmljs_bit_and, &masm_and32, &masm_and32), // OpBitAnd + INLINE_OP(__qmljs_bit_or, &masm_or32, &masm_or32), // OpBitOr + INLINE_OP(__qmljs_bit_xor, &masm_xor32, &masm_xor32), // OpBitXor INLINE_OP(__qmljs_add, &masm_add32, &masm_add32), // OpAdd INLINE_OP(__qmljs_sub, &masm_sub32, &masm_sub32), // OpSub -- cgit v1.2.3 From de232f2f148bf492ee9e9be5d8a343446caad85c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 4 Dec 2012 21:21:08 +0100 Subject: [masm] Clean up inline arithmetic operations implementation Less namespacing, less clutter. Change-Id: I08935413fc8dc021f2c207c2a8237067fd8f4c43 Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 127 +++++-------------------------------------------------- qv4isel_masm_p.h | 110 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 120 insertions(+), 117 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 4b1248c247..34fc273f07 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -62,106 +62,6 @@ namespace { QTextStream qout(stderr, QIODevice::WriteOnly); } -typedef JSC::MacroAssembler::Jump (*MemRegBinOp)(JSC::MacroAssembler*, JSC::MacroAssembler::Address, JSC::MacroAssembler::RegisterID); -typedef JSC::MacroAssembler::Jump (*ImmRegBinOp)(JSC::MacroAssembler*, JSC::MacroAssembler::TrustedImm32, JSC::MacroAssembler::RegisterID); - -static JSC::MacroAssembler::Jump masm_add32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) -{ - return assembler->branchAdd32(JSC::MacroAssembler::Overflow, addr, reg); -} - -static JSC::MacroAssembler::Jump masm_add32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) -{ - return assembler->branchAdd32(JSC::MacroAssembler::Overflow, imm, reg); -} - -static JSC::MacroAssembler::Jump masm_sub32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) -{ - return assembler->branchSub32(JSC::MacroAssembler::Overflow, addr, reg); -} - -static JSC::MacroAssembler::Jump masm_sub32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) -{ - return assembler->branchSub32(JSC::MacroAssembler::Overflow, imm, reg); -} - -static JSC::MacroAssembler::Jump masm_mul32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) -{ - return assembler->branchMul32(JSC::MacroAssembler::Overflow, addr, reg); -} - -static JSC::MacroAssembler::Jump masm_mul32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) -{ - return assembler->branchMul32(JSC::MacroAssembler::Overflow, imm, reg, reg); -} - -static JSC::MacroAssembler::Jump masm_shl32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) -{ - assembler->load32(addr, InstructionSelection::ScratchRegister); - assembler->and32(JSC::MacroAssembler::TrustedImm32(0x1f), InstructionSelection::ScratchRegister); - assembler->lshift32(InstructionSelection::ScratchRegister, reg); - return JSC::MacroAssembler::Jump(); -} - -static JSC::MacroAssembler::Jump masm_shl32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) -{ - imm.m_value &= 0x1f; - assembler->lshift32(imm, reg); - return JSC::MacroAssembler::Jump(); -} - -static JSC::MacroAssembler::Jump masm_shr32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) -{ - assembler->load32(addr, InstructionSelection::ScratchRegister); - assembler->and32(JSC::MacroAssembler::TrustedImm32(0x1f), InstructionSelection::ScratchRegister); - assembler->rshift32(InstructionSelection::ScratchRegister, reg); - return JSC::MacroAssembler::Jump(); -} - -static JSC::MacroAssembler::Jump masm_shr32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) -{ - imm.m_value &= 0x1f; - assembler->rshift32(imm, reg); - return JSC::MacroAssembler::Jump(); -} - -static JSC::MacroAssembler::Jump masm_and32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) -{ - assembler->and32(addr, reg); - return JSC::MacroAssembler::Jump(); -} - -static JSC::MacroAssembler::Jump masm_and32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) -{ - assembler->and32(imm, reg); - return JSC::MacroAssembler::Jump(); -} - -static JSC::MacroAssembler::Jump masm_or32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) -{ - assembler->or32(addr, reg); - return JSC::MacroAssembler::Jump(); -} - -static JSC::MacroAssembler::Jump masm_or32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) -{ - assembler->or32(imm, reg); - return JSC::MacroAssembler::Jump(); -} - -static JSC::MacroAssembler::Jump masm_xor32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::Address addr, JSC::MacroAssembler::RegisterID reg) -{ - assembler->xor32(addr, reg); - return JSC::MacroAssembler::Jump(); -} - -static JSC::MacroAssembler::Jump masm_xor32(JSC::MacroAssembler* assembler, JSC::MacroAssembler::TrustedImm32 imm, JSC::MacroAssembler::RegisterID reg) -{ - assembler->xor32(imm, reg); - return JSC::MacroAssembler::Jump(); -} - - #define OP(op) \ { isel_stringIfy(op), op, 0, 0 } @@ -171,12 +71,7 @@ static JSC::MacroAssembler::Jump masm_xor32(JSC::MacroAssembler* assembler, JSC: #define NULL_OP \ { 0, 0, 0, 0 } -static const struct BinaryOperationInfo { - const char *name; - Value (*fallbackImplementation)(const Value, const Value, ExecutionContext *); - MemRegBinOp inlineMemRegOp; - ImmRegBinOp inlineImmRegOp; -} binaryOperations[QQmlJS::IR::LastAluOp + 1] = { +const InstructionSelection::BinaryOperationInfo InstructionSelection::binaryOperations[QQmlJS::IR::LastAluOp + 1] = { NULL_OP, // OpInvalid NULL_OP, // OpIfTrue NULL_OP, // OpNot @@ -184,19 +79,19 @@ static const struct BinaryOperationInfo { NULL_OP, // OpUPlus NULL_OP, // OpCompl - INLINE_OP(__qmljs_bit_and, &masm_and32, &masm_and32), // OpBitAnd - INLINE_OP(__qmljs_bit_or, &masm_or32, &masm_or32), // OpBitOr - INLINE_OP(__qmljs_bit_xor, &masm_xor32, &masm_xor32), // OpBitXor + INLINE_OP(__qmljs_bit_and, &InstructionSelection::inline_and32, &InstructionSelection::inline_and32), // OpBitAnd + INLINE_OP(__qmljs_bit_or, &InstructionSelection::inline_or32, &InstructionSelection::inline_or32), // OpBitOr + INLINE_OP(__qmljs_bit_xor, &InstructionSelection::inline_xor32, &InstructionSelection::inline_xor32), // OpBitXor - INLINE_OP(__qmljs_add, &masm_add32, &masm_add32), // OpAdd - INLINE_OP(__qmljs_sub, &masm_sub32, &masm_sub32), // OpSub - INLINE_OP(__qmljs_mul, &masm_mul32, &masm_mul32), // OpMul + INLINE_OP(__qmljs_add, &InstructionSelection::inline_add32, &InstructionSelection::inline_add32), // OpAdd + INLINE_OP(__qmljs_sub, &InstructionSelection::inline_sub32, &InstructionSelection::inline_sub32), // OpSub + INLINE_OP(__qmljs_mul, &InstructionSelection::inline_mul32, &InstructionSelection::inline_mul32), // OpMul OP(__qmljs_div), // OpDiv OP(__qmljs_mod), // OpMod - INLINE_OP(__qmljs_shl, &masm_shl32, &masm_shl32), // OpLShift - INLINE_OP(__qmljs_shr, &masm_shr32, &masm_shr32), // OpRShift + INLINE_OP(__qmljs_shl, &InstructionSelection::inline_shl32, &InstructionSelection::inline_shl32), // OpLShift + INLINE_OP(__qmljs_shr, &InstructionSelection::inline_shr32, &InstructionSelection::inline_shr32), // OpRShift OP(__qmljs_ushr), // OpURShift OP(__qmljs_gt), // OpGt @@ -949,9 +844,9 @@ void InstructionSelection::generateBinOp(IR::AluOp operation, IR::Temp* target, Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); rightValue.offset += offsetof(VM::Value, int_32); - overflowCheck = info.inlineMemRegOp(this, rightValue, IntegerOpRegister); + overflowCheck = (this->*info.inlineMemRegOp)(rightValue, IntegerOpRegister); } else { // right->asConst() - overflowCheck = info.inlineImmRegOp(this, TrustedImm32(rightConst.integerValue()), IntegerOpRegister); + overflowCheck = (this->*info.inlineImmRegOp)(TrustedImm32(rightConst.integerValue()), IntegerOpRegister); } Address resultAddr = loadTempAddress(ScratchRegister, target); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 16cd42796a..ccd1ab5b28 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -65,6 +65,7 @@ public: { this->operator()(function); } void operator()(IR::Function *function); +protected: #if CPU(X86) #undef VALUE_FITS_IN_REGISTER @@ -147,7 +148,6 @@ public: #error Argh. #endif -protected: struct VoidType {}; static const VoidType Void; @@ -611,8 +611,116 @@ private: #endif } + typedef Jump (InstructionSelection::*MemRegBinOp)(Address, RegisterID); + typedef Jump (InstructionSelection::*ImmRegBinOp)(TrustedImm32, RegisterID); + + struct BinaryOperationInfo { + const char *name; + VM::Value (*fallbackImplementation)(const VM::Value, const VM::Value, VM::ExecutionContext *); + MemRegBinOp inlineMemRegOp; + ImmRegBinOp inlineImmRegOp; + }; + + static const BinaryOperationInfo binaryOperations[QQmlJS::IR::LastAluOp + 1]; + void generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Expr* left, IR::Expr* right); + Jump inline_add32(Address addr, RegisterID reg) + { + return branchAdd32(Overflow, addr, reg); + } + + Jump inline_add32(TrustedImm32 imm, RegisterID reg) + { + return branchAdd32(Overflow, imm, reg); + } + + Jump inline_sub32(Address addr, RegisterID reg) + { + return branchSub32(Overflow, addr, reg); + } + + Jump inline_sub32(TrustedImm32 imm, RegisterID reg) + { + return branchSub32(Overflow, imm, reg); + } + + Jump inline_mul32(Address addr, RegisterID reg) + { + return branchMul32(Overflow, addr, reg); + } + + Jump inline_mul32(TrustedImm32 imm, RegisterID reg) + { + return branchMul32(Overflow, imm, reg, reg); + } + + Jump inline_shl32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + lshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_shl32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + lshift32(imm, reg); + return Jump(); + } + + Jump inline_shr32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + rshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_shr32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + rshift32(imm, reg); + return Jump(); + } + + Jump inline_and32(Address addr, RegisterID reg) + { + and32(addr, reg); + return Jump(); + } + + Jump inline_and32(TrustedImm32 imm, RegisterID reg) + { + and32(imm, reg); + return Jump(); + } + + Jump inline_or32(Address addr, RegisterID reg) + { + or32(addr, reg); + return Jump(); + } + + Jump inline_or32(TrustedImm32 imm, RegisterID reg) + { + or32(imm, reg); + return Jump(); + } + + Jump inline_xor32(Address addr, RegisterID reg) + { + xor32(addr, reg); + return Jump(); + } + + Jump inline_xor32(TrustedImm32 imm, RegisterID reg) + { + xor32(imm, reg); + return Jump(); + } + VM::ExecutionEngine *_engine; IR::Function *_function; IR::BasicBlock *_block; -- cgit v1.2.3 From 5c62e677001e559ccee0e46e64b1d6b1e5cc6731 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 4 Dec 2012 22:32:32 +0100 Subject: [masm] Remove unused variable Change-Id: I6034dad659ea4029256828bee06e8621902fd52c Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 5 ----- qv4isel_masm_p.h | 5 ----- 2 files changed, 10 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 34fc273f07..1029d75783 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -195,11 +195,6 @@ void InstructionSelection::operator()(IR::Function *function) functions[ctl.externalFunction.value()] = ctl.functionName; } - foreach (CatchBlockToLink cbl, _catchHandlers) { - Label target = _addrs.value(cbl.catchBlock); - linkBuffer.patch(cbl.ptr, linkBuffer.locationOf(target)); - } - static bool showCode = !qgetenv("SHOW_CODE").isNull(); if (showCode) { #if OS(LINUX) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index ccd1ab5b28..48801d0e94 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -595,10 +595,6 @@ private: FunctionPtr externalFunction; const char* functionName; }; - struct CatchBlockToLink { - DataLabelPtr ptr; - IR::BasicBlock *catchBlock; - }; void storeValue(VM::Value value, Address destination) { @@ -726,7 +722,6 @@ private: IR::BasicBlock *_block; QHash > _patches; QHash _addrs; - QList _catchHandlers; QList _callsToLink; }; -- cgit v1.2.3 From b725f76711e8350b3d52921c851e693145b7251c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 4 Dec 2012 14:26:29 -0800 Subject: Make crypto.js work in both v4 and nodejs Change-Id: I7cb200d1626404898ed94b71972499864b2d99d9 Reviewed-by: Simon Hausmann --- tests/crypto.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/crypto.js b/tests/crypto.js index a7d9d9f587..cd48bdb019 100644 --- a/tests/crypto.js +++ b/tests/crypto.js @@ -1705,4 +1705,8 @@ function decrypt() { var d1 = +new Date encrypt() decrypt() + +if (typeof(print) === "undefined") + print = console.log; + print("done in", new Date - d1) -- cgit v1.2.3 From 46953ed4d0a03d07c7d1c0e4aeeb5b0258909097 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 4 Dec 2012 13:46:48 -0800 Subject: Get rid of the ActivationObject Also implement __qmljs_xxx_activation_property in a more correct way. Change-Id: I60c330bccca21fad99930987ed78153114a80c7d Reviewed-by: Simon Hausmann --- main.cpp | 1 + qmljs_engine.cpp | 21 ++++++++--- qmljs_engine.h | 3 +- qmljs_environment.cpp | 100 ++++++++++++++++++++++++++++++++++++++++---------- qmljs_environment.h | 8 ++-- qmljs_objects.cpp | 44 +--------------------- qmljs_objects.h | 14 +------ qmljs_runtime.cpp | 27 +++----------- qmljs_runtime.h | 1 - qmljs_value.cpp | 5 --- qmljs_value.h | 2 - 11 files changed, 112 insertions(+), 114 deletions(-) diff --git a/main.cpp b/main.cpp index a516953008..6f6d5126f1 100644 --- a/main.cpp +++ b/main.cpp @@ -357,6 +357,7 @@ int main(int argc, char *argv[]) QQmlJS::IR::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode); if (!f) continue; + ctx->strictMode = f->isStrict; if (debugger) debugger->aboutToCall(0, ctx); diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 027cc33a9d..661781001d 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -176,9 +176,20 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) glo->__put__(rootContext, identifier(QStringLiteral("TypeError")), typeErrorCtor); glo->__put__(rootContext, identifier(QStringLiteral("URIError")), uRIErrorCtor); glo->__put__(rootContext, identifier(QStringLiteral("Math")), Value::fromObject(newMathObject(rootContext))); - glo->__put__(rootContext, identifier(QStringLiteral("undefined")), Value::undefinedValue()); - glo->__put__(rootContext, identifier(QStringLiteral("NaN")), Value::fromDouble(nan(""))); - glo->__put__(rootContext, identifier(QStringLiteral("Infinity")), Value::fromDouble(INFINITY)); + + PropertyDescriptor pd; + pd.type = PropertyDescriptor::Data; + pd.writable = PropertyDescriptor::Unset; + pd.enumberable = PropertyDescriptor::Unset; + pd.configurable = PropertyDescriptor::Unset; + + pd.value = Value::undefinedValue(); + glo->__defineOwnProperty__(rootContext, identifier(QStringLiteral("undefined")), &pd); + pd.value = Value::fromDouble(nan("")); + glo->__defineOwnProperty__(rootContext, identifier(QStringLiteral("NaN")), &pd); + pd.value = Value::fromDouble(INFINITY); + glo->__defineOwnProperty__(rootContext, identifier(QStringLiteral("Infinity")), &pd); + glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new EvalFunction(rootContext))); // TODO: parseInt [15.1.2.2] @@ -369,9 +380,9 @@ Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) return object; } -Object *ExecutionEngine::newActivationObject(ExecutionContext *ctx) +Object *ExecutionEngine::newActivationObject() { - return new ActivationObject(ctx); + return new Object(); } Object *ExecutionEngine::newForEachIteratorObject(Object *o) diff --git a/qmljs_engine.h b/qmljs_engine.h index 2b053fd0f2..2837adb882 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -65,7 +65,6 @@ struct DateObject; struct FunctionObject; struct RegExpObject; struct ErrorObject; -struct ActivationObject; struct ArgumentsObject; struct ExecutionContext; struct ExecutionEngine; @@ -190,7 +189,7 @@ struct ExecutionEngine Object *newTypeErrorObject(ExecutionContext *ctx, const QString &message); Object *newMathObject(ExecutionContext *ctx); - Object *newActivationObject(ExecutionContext *ctx); + Object *newActivationObject(); Object *newForEachIteratorObject(Object *o); }; diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 12a2091dc8..c542422128 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -90,15 +90,17 @@ bool ExecutionContext::hasBinding(String *name) const if (__qmljs_string_equal(function->formalParameterList[i], name)) return true; } + if (activation) + return activation->__hasProperty__(this, name); return false; } -void ExecutionContext::createMutableBinding(ExecutionContext *ctx, String *name, bool deletable) +void ExecutionContext::createMutableBinding(String *name, bool deletable) { if (!activation) - activation = engine->newActivationObject(this); + activation = engine->newActivationObject(); - if (activation->__hasProperty__(ctx, name)) + if (activation->__hasProperty__(this, name)) return; PropertyDescriptor desc; desc.value = Value::undefinedValue(); @@ -106,31 +108,33 @@ void ExecutionContext::createMutableBinding(ExecutionContext *ctx, String *name, desc.configurable = deletable ? PropertyDescriptor::Set : PropertyDescriptor::Unset; desc.writable = PropertyDescriptor::Set; desc.enumberable = PropertyDescriptor::Set; - activation->__defineOwnProperty__(ctx, name, &desc); + activation->__defineOwnProperty__(this, name, &desc); } -void ExecutionContext::setMutableBinding(String *name, Value value, bool strict) +bool ExecutionContext::setMutableBinding(ExecutionContext *scope, String *name, Value value) { - Q_UNUSED(strict); - assert(function); - - // ### throw if strict is true, and it would change an immutable binding + // ### throw if scope->strict is true, and it would change an immutable binding for (unsigned int i = 0; i < variableCount(); ++i) { if (__qmljs_string_equal(variables()[i], name)) { locals[i] = value; - return; + return true; } } for (unsigned int i = 0; i < formalCount(); ++i) { if (__qmljs_string_equal(formals()[i], name)) { arguments[i] = value; - return; + return true; } } - assert(false); + if (activation && activation->__hasProperty__(scope, name)) { + activation->__put__(scope, name, value); + return true; + } + + return false; } -Value ExecutionContext::getBindingValue(String *name, bool strict) const +Value ExecutionContext::getBindingValue(ExecutionContext *scope, String *name, bool strict) const { Q_UNUSED(strict); assert(function); @@ -143,16 +147,18 @@ Value ExecutionContext::getBindingValue(String *name, bool strict) const if (__qmljs_string_equal(formals()[i], name)) return arguments[i]; } + if (activation && activation->__hasProperty__(this, name)) + return activation->__get__(scope, name); assert(false); } -bool ExecutionContext::deleteBinding(ExecutionContext *ctx, String *name) +bool ExecutionContext::deleteBinding(ExecutionContext *scope, String *name) { if (activation) - activation->__delete__(ctx, name); + activation->__delete__(scope, name); - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); + if (scope->strictMode) + __qmljs_throw_type_error(scope); return false; } @@ -251,10 +257,66 @@ bool ExecutionContext::deleteProperty(String *name) ctx->activation->__delete__(this, name); } } - // ### throw syntax error in strict mode + if (strictMode) + throwSyntaxError(0); return true; } +void ExecutionContext::setProperty(String *name, Value value) +{ + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { + if (ctx->withObject) { + With *w = ctx->withObject; + while (w) { + if (w->object->__hasProperty__(ctx, name)) { + w->object->__put__(ctx, name, value); + return; + } + w = w->next; + } + } + if (ctx->setMutableBinding(this, name, value)) + return; + } + if (strictMode) + throwReferenceError(Value::fromString(name)); + engine->globalObject.objectValue()->__put__(this, name, value); +} + +Value ExecutionContext::getProperty(String *name) +{ + PropertyDescriptor tmp; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { + if (ctx->withObject) { + With *w = ctx->withObject; + while (w) { + if (PropertyDescriptor *pd = w->object->__getPropertyDescriptor__(this, name, &tmp)) + return pd->value; + w = w->next; + } + } + + for (unsigned int i = 0; i < ctx->variableCount(); ++i) + if (__qmljs_string_equal(ctx->variables()[i], name)) + return ctx->locals[i]; + for (unsigned int i = 0; i < ctx->formalCount(); ++i) + if (__qmljs_string_equal(ctx->formals()[i], name)) + return ctx->arguments[i]; + if (ctx->activation && ctx->activation->__hasProperty__(ctx, name)) + return ctx->activation->__get__(ctx, name); + if (name->isEqualTo(ctx->engine->id_arguments)) { + Value arguments = Value::fromObject(new ArgumentsObject(this)); + createMutableBinding(ctx->engine->id_arguments, false); + setMutableBinding(this, ctx->engine->id_arguments, arguments); + return arguments; + } + } + throwReferenceError(Value::fromString(name)); + return Value::undefinedValue(); +} + + + void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) { for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { @@ -323,7 +385,7 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha std::fill(locals, locals + function->varCount, Value::undefinedValue()); if (function->needsActivation) - activation = engine->newActivationObject(this); + activation = engine->newActivationObject(); else activation = 0; diff --git a/qmljs_environment.h b/qmljs_environment.h index eb10b6630d..dca22dc75c 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -100,9 +100,9 @@ struct ExecutionContext void init(ExecutionEngine *e); bool hasBinding(String *name) const; - void createMutableBinding(ExecutionContext *ctx, String *name, bool deletable); - void setMutableBinding(String *name, Value value, bool strict); - Value getBindingValue(String *name, bool strict) const; + void createMutableBinding(String *name, bool deletable); + bool setMutableBinding(ExecutionContext *scope, String *name, Value value); + Value getBindingValue(ExecutionContext *scope, String *name, bool strict) const; bool deleteBinding(ExecutionContext *ctx, String *name); void pushWithObject(Object *with); @@ -121,6 +121,8 @@ struct ExecutionContext void throwUnimplemented(const QString &message); PropertyDescriptor *lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp); + void setProperty(String *name, Value value); + Value getProperty(String *name); void inplaceBitOp(Value value, String *name, BinOp op); bool deleteProperty(String *name); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 12cc44783f..7fdb86aa72 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -216,7 +216,7 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) } // Section 8.12.6 -bool Object::__hasProperty__(ExecutionContext *ctx, String *name) const +bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const { if (members) return members->find(name) != 0; @@ -592,12 +592,6 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct __qmljs_throw_type_error(ctx); } - if (!ctx->activation) - ctx->activation = new QQmlJS::VM::Object(); - - foreach (const QString *local, globalCode->locals) { - ctx->activation->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); - } return globalCode; } @@ -681,42 +675,6 @@ Value ScriptFunction::construct(VM::ExecutionContext *ctx) return ctx->thisObject; } -PropertyDescriptor *ActivationObject::__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill) -{ - if (context) { - for (unsigned int i = 0; i < context->variableCount(); ++i) { - String *var = context->variables()[i]; - if (__qmljs_string_equal(var, name)) { - *to_fill = PropertyDescriptor::fromValue(context->locals[i]); - to_fill->writable = PropertyDescriptor::Set; - to_fill->enumberable = PropertyDescriptor::Set; - return to_fill; - } - } - for (unsigned int i = 0; i < context->formalCount(); ++i) { - String *formal = context->formals()[i]; - if (__qmljs_string_equal(formal, name)) { - *to_fill = PropertyDescriptor::fromValue(context->arguments[i]); - to_fill->writable = PropertyDescriptor::Set; - to_fill->enumberable = PropertyDescriptor::Set; - return to_fill; - } - } - if (name->isEqualTo(ctx->engine->id_arguments)) { - if (arguments.isUndefined()) { - arguments = Value::fromObject(new ArgumentsObject(ctx)); - arguments.objectValue()->prototype = ctx->engine->objectPrototype; - } - - *to_fill = PropertyDescriptor::fromValue(arguments); - to_fill->writable = PropertyDescriptor::Unset; - to_fill->enumberable = PropertyDescriptor::Unset; - return to_fill; - } - } - - return Object::__getPropertyDescriptor__(ctx, name, to_fill); -} Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name) { diff --git a/qmljs_objects.h b/qmljs_objects.h index 4a98ee16d5..fc8deed7f9 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -72,7 +72,6 @@ struct DateObject; struct FunctionObject; struct RegExpObject; struct ErrorObject; -struct ActivationObject; struct ArgumentsObject; struct ExecutionContext; struct ExecutionEngine; @@ -419,7 +418,6 @@ struct Object { virtual FunctionObject *asFunctionObject() { return 0; } virtual RegExpObject *asRegExpObject() { return 0; } virtual ErrorObject *asErrorObject() { return 0; } - virtual ActivationObject *asActivationObject() { return 0; } virtual ArgumentsObject *asArgumentsObject() { return 0; } virtual Value __get__(ExecutionContext *ctx, String *name); @@ -427,7 +425,7 @@ struct Object { virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); virtual void __put__(ExecutionContext *ctx, String *name, Value value); virtual bool __canPut__(ExecutionContext *ctx, String *name); - virtual bool __hasProperty__(ExecutionContext *ctx, String *name) const; + virtual bool __hasProperty__(const ExecutionContext *ctx, String *name) const; virtual bool __delete__(ExecutionContext *ctx, String *name); virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc); @@ -641,16 +639,6 @@ struct URIErrorObject: ErrorObject { virtual QString className() { return QStringLiteral("URIError"); } }; -struct ActivationObject: Object { - ExecutionContext *context; - Value arguments; - ActivationObject(ExecutionContext *context) - : context(context), arguments(Value::undefinedValue()) {} - virtual QString className() { return QStringLiteral("Activation"); } - virtual ActivationObject *asActivationObject() { return this; } - virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); -}; - struct ArgumentsObject: Object { ExecutionContext *context; ArgumentsObject(ExecutionContext *context): context(context) {} diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 20002aac06..d7bbf0eec1 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -600,15 +600,7 @@ Value __qmljs_foreach_next_property_name(Value foreach_iterator) void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, Value value) { - PropertyDescriptor tmp; - PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp); - if (prop) { - prop->value = value; - return; - } - if (ctx->strictMode) - ctx->throwReferenceError(Value::fromString(name)); - ctx->engine->globalObject.objectValue()->__put__(ctx, name, value); + ctx->setProperty(name, value); } Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name) @@ -631,11 +623,7 @@ Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name) Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name) { - PropertyDescriptor tmp; - PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp); - if (!prop) - ctx->throwReferenceError(Value::fromString(name)); - return prop->value; + return ctx->getProperty(name); } Value __qmljs_get_thisObject(ExecutionContext *ctx) @@ -700,7 +688,7 @@ uint __qmljs_equal(Value x, Value y, ExecutionContext *ctx) // TODO: remove this function. Backends should just generate a __qmljs_get_activation_property followed by a __qmljs_call_value Value __qmljs_call_activation_property(ExecutionContext *context, String *name, Value *args, int argc) { - Value func = __qmljs_get_activation_property(context, name); + Value func = context->getProperty(name); Object *o = func.asObject(); if (!o) context->throwReferenceError(Value::fromString(name)); @@ -734,11 +722,8 @@ Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func Value __qmljs_construct_activation_property(ExecutionContext *context, String *name, Value *args, int argc) { - PropertyDescriptor tmp; - PropertyDescriptor *func = context->lookupPropertyDescriptor(name, &tmp); - if (! func) - context->throwReferenceError(Value::fromString(name)); - return __qmljs_construct_value(context, func->value, args, argc); + Value func = context->getProperty(name); + return __qmljs_construct_value(context, func, args, argc); } Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc) @@ -836,7 +821,7 @@ void __qmljs_builtin_pop_with(ExecutionContext *ctx) void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) { - ctx->createMutableBinding(ctx, name, deletable); + ctx->createMutableBinding(name, deletable); } } // extern "C" diff --git a/qmljs_runtime.h b/qmljs_runtime.h index d6a7839dce..adcdae6601 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -85,7 +85,6 @@ struct DateObject; struct RegExpObject; struct ArrayObject; struct ErrorObject; -struct ActivationObject; struct ExecutionEngine; extern "C" { diff --git a/qmljs_value.cpp b/qmljs_value.cpp index cbf481399b..38e1f5d911 100644 --- a/qmljs_value.cpp +++ b/qmljs_value.cpp @@ -210,11 +210,6 @@ ErrorObject *Value::asErrorObject() const return isObject() ? objectValue()->asErrorObject() : 0; } -ActivationObject *Value::asArgumentsObject() const -{ - return isObject() ? objectValue()->asActivationObject() : 0; -} - Value Value::property(ExecutionContext *ctx, String *name) const { return isObject() ? objectValue()->__get__(ctx, name) : undefinedValue(); diff --git a/qmljs_value.h b/qmljs_value.h index e15b970257..3d29deb326 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -61,7 +61,6 @@ struct DateObject; struct FunctionObject; struct RegExpObject; struct ErrorObject; -struct ActivationObject; struct ArgumentsObject; struct ExecutionContext; struct ExecutionEngine; @@ -238,7 +237,6 @@ struct Value RegExpObject *asRegExpObject() const; ArrayObject *asArrayObject() const; ErrorObject *asErrorObject() const; - ActivationObject *asArgumentsObject() const; Value property(ExecutionContext *ctx, String *name) const; -- cgit v1.2.3 From 4216bf0fe07ccb570eed67c4f7af810335138386 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 4 Dec 2012 13:50:49 -0800 Subject: Remove unused method Change-Id: Ic8dd47453951bd129e0dcceeb43ecfa299d4c189 Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 19 ------------------- qmljs_environment.h | 1 - 2 files changed, 20 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index c542422128..ca3c43bd4b 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -222,25 +222,6 @@ void ExecutionContext::init(ExecutionEngine *eng) eng->exception = Value::undefinedValue(); } -PropertyDescriptor *ExecutionContext::lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp) -{ - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { - if (ctx->withObject) { - With *w = ctx->withObject; - while (w) { - if (PropertyDescriptor *pd = w->object->__getPropertyDescriptor__(this, name, tmp)) - return pd; - w = w->next; - } - } - if (ctx->activation) { - if (PropertyDescriptor *pd = ctx->activation->__getPropertyDescriptor__(this, name, tmp)) - return pd; - } - } - return 0; -} - bool ExecutionContext::deleteProperty(String *name) { for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { diff --git a/qmljs_environment.h b/qmljs_environment.h index dca22dc75c..4063f384d0 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -120,7 +120,6 @@ struct ExecutionContext void throwReferenceError(Value value); void throwUnimplemented(const QString &message); - PropertyDescriptor *lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp); void setProperty(String *name, Value value); Value getProperty(String *name); void inplaceBitOp(Value value, String *name, BinOp op); -- cgit v1.2.3 From 0a509b1b6d9bd0726a25c1236a5bab1d7e3c432f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 4 Dec 2012 14:12:51 -0800 Subject: Use __get__, so that accessor properties work correctly Change-Id: I99f06c09017b50bffeb34a8158f0dbf902c75945 Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index ca3c43bd4b..0127b04852 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -271,8 +271,8 @@ Value ExecutionContext::getProperty(String *name) if (ctx->withObject) { With *w = ctx->withObject; while (w) { - if (PropertyDescriptor *pd = w->object->__getPropertyDescriptor__(this, name, &tmp)) - return pd->value; + if (w->object->__hasProperty__(ctx, name)) + return w->object->__get__(ctx, name); w = w->next; } } -- cgit v1.2.3 From a6ffc32325c99c158cea2d19c3503f29fc91f75e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 4 Dec 2012 23:11:28 +0100 Subject: [masm] Inline ushr Change-Id: Ia3855625e72ae7ed50b9890edbad11e2aa338930 Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 2 +- qv4isel_masm_p.h | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 1029d75783..2fa506d022 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -92,7 +92,7 @@ const InstructionSelection::BinaryOperationInfo InstructionSelection::binaryOper INLINE_OP(__qmljs_shl, &InstructionSelection::inline_shl32, &InstructionSelection::inline_shl32), // OpLShift INLINE_OP(__qmljs_shr, &InstructionSelection::inline_shr32, &InstructionSelection::inline_shr32), // OpRShift - OP(__qmljs_ushr), // OpURShift + INLINE_OP(__qmljs_ushr, &InstructionSelection::inline_ushr32, &InstructionSelection::inline_ushr32), // OpURShift OP(__qmljs_gt), // OpGt OP(__qmljs_lt), // OpLt diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 48801d0e94..aa39a15ca5 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -681,6 +681,21 @@ private: return Jump(); } + Jump inline_ushr32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + urshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_ushr32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + urshift32(imm, reg); + return Jump(); + } + Jump inline_and32(Address addr, RegisterID reg) { and32(addr, reg); -- cgit v1.2.3 From 3b3f3bebcd24073455de9f4abf2f0c7712a1c1ee Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 4 Dec 2012 10:31:31 +0100 Subject: Add utility function to print stack traces from lldb/gdb. Change-Id: I81315a1cd6900dbecfc9a39d9dc4256461163921 Reviewed-by: Simon Hausmann --- debugging.cpp | 35 ++++++++++++++++++++++++++++++++--- debugging.h | 1 + moth/qv4vme_moth.cpp | 7 ++----- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/debugging.cpp b/debugging.cpp index 207eab71e8..62e38d0e17 100644 --- a/debugging.cpp +++ b/debugging.cpp @@ -32,6 +32,8 @@ #include +#define LOW_LEVEL_DEBUGGING_HELPERS + using namespace QQmlJS; using namespace QQmlJS::Debugging; @@ -63,13 +65,32 @@ VM::Value *FunctionState::local(unsigned idx) return 0; } +#ifdef LOW_LEVEL_DEBUGGING_HELPERS +Debugger *globalInstance = 0; + +void printStackTrace() +{ + if (globalInstance) + globalInstance->printStackTrace(); + else + std::cerr << "No debugger." << std::endl; +} +#endif // DO_TRACE_INSTR + Debugger::Debugger(VM::ExecutionEngine *engine) : _engine(engine) { +#ifdef LOW_LEVEL_DEBUGGING_HELPERS + globalInstance = this; +#endif // DO_TRACE_INSTR } Debugger::~Debugger() { +#ifdef LOW_LEVEL_DEBUGGING_HELPERS + globalInstance = 0; +#endif // DO_TRACE_INSTR + qDeleteAll(_functionInfo.values()); } @@ -129,9 +150,9 @@ void Debugger::enterFunction(FunctionState *state) #ifdef DO_TRACE_INSTR QString n = name(_callStack[callIndex(state->context())].function); - std::cerr << "*** Entering \"" << qPrintable(n) << "\" with" << state->context()->variableEnvironment->argumentCount << "args" << std::endl; - for (unsigned i = 0; i < state->context()->variableEnvironment->argumentCount; ++i) - std::cerr << " " << i << ": " << currentArg(i) << std::endl; + std::cerr << "*** Entering \"" << qPrintable(n) << "\" with " << state->context()->variableEnvironment->argumentCount << " args" << std::endl; +// for (unsigned i = 0; i < state->context()->variableEnvironment->argumentCount; ++i) +// std::cerr << " " << i << ": " << currentArg(i) << std::endl; #endif // DO_TRACE_INSTR } @@ -171,6 +192,14 @@ const char *Debugger::currentTemp(unsigned idx) const return qPrintable(state->temp(idx)->toString(state->context())->toQString()); } +void Debugger::printStackTrace() const +{ + for (int i = _callStack.size() - 1; i >=0; --i) { + QString n = name(_callStack[i].function); + std::cerr << "\tframe #" << i << ": " << qPrintable(n) << std::endl; + } +} + int Debugger::callIndex(VM::ExecutionContext *context) { for (int idx = _callStack.size() - 1; idx >= 0; --idx) { diff --git a/debugging.h b/debugging.h index 7a0a27dfdb..7c4c273e43 100644 --- a/debugging.h +++ b/debugging.h @@ -121,6 +121,7 @@ public: // debugging hooks const char *currentArg(unsigned idx) const; const char *currentLocal(unsigned idx) const; const char *currentTemp(unsigned idx) const; + void printStackTrace() const; private: int callIndex(VM::ExecutionContext *context); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index c83e35f1a2..d38d7d7cb7 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -7,7 +7,7 @@ #ifdef DO_TRACE_INSTR # define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); -# define TRACE(n, str, ...) { fprintf(stderr, " %s : ", #n); fprintf(stderr, str, __VA_ARGS__); fprintf(stderr, "\n"); } +# define TRACE(n, str, ...) { char buf[4096]; snprintf(buf, 4096, str, __VA_ARGS__); fprintf(stderr, " %s : %s\n", #n, buf); } #else # define TRACE_INSTR(I) # define TRACE(n, str, ...) @@ -154,9 +154,6 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(LoadClosure) VM::Value c = __qmljs_init_closure(instr.value, context); TEMP(instr.targetTempIndex) = c; -#ifdef DO_TRACE_INSTR - qDebug() << "loaded:" << c.toString(context)->toQString(); -#endif MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) @@ -341,7 +338,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(Ret) VM::Value result = TEMP(instr.tempIndex); - TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); +// TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); return result; MOTH_END_INSTR(Ret) -- cgit v1.2.3 From 5f22fbd7fc4ca6a7f4629cbd34e0fc2e3c1b1cee Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 4 Dec 2012 13:40:18 +0100 Subject: Add a MemoryManager, which does GC for the interpreter. Todo: - stack walking for MASM - fix all TODOs/FIXMEs and hidden treasures (bugs). Change-Id: I36f8cdc3a545df7287ce1df17b3570a9c017865e Reviewed-by: Lars Knoll --- main.cpp | 23 ++-- moth/moth.pri | 2 + moth/qv4mm_moth.cpp | 81 +++++++++++ moth/qv4mm_moth.h | 59 ++++++++ moth/qv4vme_moth.cpp | 43 +++--- qmljs_engine.cpp | 136 +++++++++--------- qmljs_engine.h | 4 +- qmljs_environment.cpp | 6 +- qmljs_objects.cpp | 65 ++++++++- qmljs_objects.h | 36 ++++- qv4array_p.h | 13 +- qv4codegen.cpp | 5 +- qv4ecmaobjects.cpp | 23 ++-- qv4ir.cpp | 16 ++- qv4ir_p.h | 3 + qv4mm.cpp | 375 ++++++++++++++++++++++++++++++++++++++++++++++++++ qv4mm.h | 184 +++++++++++++++++++++++++ v4.pro | 6 +- 18 files changed, 972 insertions(+), 108 deletions(-) create mode 100644 moth/qv4mm_moth.cpp create mode 100644 moth/qv4mm_moth.h create mode 100644 qv4mm.cpp create mode 100644 qv4mm.h diff --git a/main.cpp b/main.cpp index 6f6d5126f1..decb799ce6 100644 --- a/main.cpp +++ b/main.cpp @@ -53,6 +53,7 @@ #include "qv4syntaxchecker_p.h" #include "qv4ecmaobjects_p.h" #include "qv4isel_p.h" +#include "qv4mm_moth.h" #include #include @@ -144,12 +145,12 @@ int executeLLVMCode(void *codePtr) void (*code)(VM::ExecutionContext *) = (void (*)(VM::ExecutionContext *)) codePtr; QScopedPointer iSelFactory(new QQmlJS::Moth::ISelFactory); - VM::ExecutionEngine vm(iSelFactory.data()); + VM::ExecutionEngine vm(0, iSelFactory.data()); VM::ExecutionContext *ctx = vm.rootContext; QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), - QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); + QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::Print(ctx))); void * buf = __qmljs_create_exception_handler(ctx); if (setjmp(*(jmp_buf *)buf)) { @@ -318,13 +319,17 @@ int main(int argc, char *argv[]) #endif // QMLJS_NO_LLVM case use_masm: case use_moth: { + QScopedPointer mm; QScopedPointer iSelFactory; - if (mode == use_moth) + if (mode == use_moth) { + mm.reset(new QQmlJS::Moth::MemoryManager); iSelFactory.reset(new QQmlJS::Moth::ISelFactory); - else + } else { + mm.reset(new QQmlJS::VM::MemoryManagerWithoutGC); iSelFactory.reset(new QQmlJS::MASM::ISelFactory); + } - QQmlJS::VM::ExecutionEngine vm(iSelFactory.data()); + QQmlJS::VM::ExecutionEngine vm(mm.data(), iSelFactory.data()); QScopedPointer debugger; if (enableDebugging) @@ -335,12 +340,12 @@ int main(int argc, char *argv[]) QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), - QQmlJS::VM::Value::fromObject(new builtins::Print(ctx))); + QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::Print(ctx))); bool errorInTestHarness = false; if (!qgetenv("IN_TEST_HARNESS").isEmpty()) globalObject->__put__(ctx, vm.identifier(QStringLiteral("$ERROR")), - QQmlJS::VM::Value::fromObject(new builtins::TestHarnessError(ctx, errorInTestHarness))); + QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::TestHarnessError(ctx, errorInTestHarness))); foreach (const QString &fn, args) { QFile file(fn); @@ -354,7 +359,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - QQmlJS::IR::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode); + QScopedPointer f(QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode)); if (!f) continue; @@ -376,6 +381,8 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } } + + mm->dumpStats(); } return EXIT_SUCCESS; } } diff --git a/moth/moth.pri b/moth/moth.pri index 4381c9a53b..160c1aac00 100644 --- a/moth/moth.pri +++ b/moth/moth.pri @@ -4,10 +4,12 @@ HEADERS += \ $$PWD/qv4isel_moth_p.h \ $$PWD/qv4instr_moth_p.h \ $$PWD/qv4vme_moth_p.h \ + $$PWD/qv4mm_moth.h SOURCES += \ $$PWD/qv4isel_moth.cpp \ $$PWD/qv4instr_moth.cpp \ $$PWD/qv4vme_moth.cpp \ + $$PWD/qv4mm_moth.cpp #DEFINES += DO_TRACE_INSTR diff --git a/moth/qv4mm_moth.cpp b/moth/qv4mm_moth.cpp new file mode 100644 index 0000000000..44e45fe027 --- /dev/null +++ b/moth/qv4mm_moth.cpp @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qmljs_engine.h" +#include "qv4mm_moth.h" + +#include + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +MemoryManager::MemoryManager() +{ + stackFrames.reserve(64); +} + +MemoryManager::~MemoryManager() +{ +} + +VM::Value *MemoryManager::allocStackFrame(std::size_t frameSize) +{ + std::size_t size = frameSize * sizeof(VM::Value); + MMObject *m = alloc(align(size)); + stackFrames.append(m); + return reinterpret_cast(&m->data); +} + +void MemoryManager::deallocStackFrame(VM::Value *stackFrame) +{ + MMObject *o = toObject(stackFrame); + for (int i = stackFrames.size() - 1; i >= 0; --i) { + if (stackFrames[i] == o) { + stackFrames.remove(i); + dealloc(o); + return; + } + } + + Q_UNREACHABLE(); +} + +void MemoryManager::collectRootsOnStack(QVector &roots) const +{ + for (int i = 0, ei = stackFrames.size(); i < ei; ++i) { + MMObject *m = stackFrames[i]; + VM::Value *frame = reinterpret_cast(&m->data); + std::size_t frameSize = (m->info.size - align(sizeof(MMInfo))) / sizeof(VM::Value); + for (std::size_t j = 0; j < frameSize; ++j) { + if (VM::Object *o = frame[j].asObject()) { + roots.append(o); + } + } + } +} diff --git a/moth/qv4mm_moth.h b/moth/qv4mm_moth.h new file mode 100644 index 0000000000..a2b1ebd556 --- /dev/null +++ b/moth/qv4mm_moth.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QV4GC_MOTH_H +#define QV4GC_MOTH_H + +#include "qv4mm.h" + +#include + +namespace QQmlJS { +namespace Moth { + +class MemoryManager: public QQmlJS::VM::MemoryManager +{ +public: + MemoryManager(); + ~MemoryManager(); + + VM::Value *allocStackFrame(std::size_t frameSize); + void deallocStackFrame(VM::Value *stackFrame); + +protected: + virtual void collectRootsOnStack(QVector &roots) const; + +private: + QVector stackFrames; +}; + +} // namespace Moth +} // namespace QQmlJS + +#endif // QV4GC_MOTH_H diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index d38d7d7cb7..86b396f422 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -2,6 +2,7 @@ #include "qv4instr_moth_p.h" #include "qmljs_value.h" #include "debugging.h" +#include "qv4mm_moth.h" #include @@ -51,7 +52,7 @@ using namespace QQmlJS::Moth; #endif -static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVector &stack, int index) +static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, VM::Value* stack, int index) { #ifdef DO_TRACE_INSTR const char *kind; @@ -87,25 +88,32 @@ static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, QVecto int off = index - context->variableCount(); Q_ASSERT(off >= 0); - Q_ASSERT(off < stack.size()); - return stack.data() + off; + return stack + off; } } class FunctionState: public Debugging::FunctionState { public: - FunctionState(QQmlJS::VM::ExecutionContext *context, QVector *stack, const uchar **code) + FunctionState(QQmlJS::VM::ExecutionContext *context, const uchar **code) : Debugging::FunctionState(context) - , stack(stack) + , stack(0) + , stackSize(0) , code(code) {} - virtual VM::Value *temp(unsigned idx) { return stack->data() + idx; } + ~FunctionState() + { if (stack) static_cast(context()->engine->memoryManager)->deallocStackFrame(stack); } + + virtual VM::Value *temp(unsigned idx) { return stack + idx; } + + void setStack(VM::Value *stack, unsigned stackSize) + { this->stack = stack; this->stackSize = stackSize; } private: - QVector *stack; + VM::Value *stack; + unsigned stackSize; const uchar **code; }; @@ -130,8 +138,9 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co } #endif - QVector stack; - FunctionState state(context, &stack, &code); + VM::Value *stack = 0; + unsigned stackSize = 0; + FunctionState state(context, &code); #ifdef MOTH_THREADED_INTERPRETER const Instr *genericInstr = reinterpret_cast(code); @@ -191,7 +200,9 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(Push) TRACE(inline, "stack size: %u", instr.value); - stack.resize(instr.value); + stackSize = instr.value; + stack = static_cast(context->engine->memoryManager)->allocStackFrame(stackSize); + state.setStack(stack, stackSize); MOTH_END_INSTR(Push) MOTH_BEGIN_INSTR(CallValue) @@ -207,20 +218,20 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co #endif // DO_TRACE_INSTR quint32 argStart = instr.args - context->variableCount(); TRACE(Call, "value index = %d, argStart = %d, argc = %d, result temp index = %d", instr.destIndex, argStart, instr.argc, instr.targetTempIndex); - VM::Value *args = stack.data() + argStart; + VM::Value *args = stack + argStart; TEMP(instr.targetTempIndex) = __qmljs_call_value(context, VM::Value::undefinedValue(), TEMP(instr.destIndex), args, instr.argc); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) quint32 argStart = instr.args - context->variableCount(); - VM::Value *args = stack.data() + argStart; + VM::Value *args = stack + argStart; VM::Value base = TEMP(instr.baseTemp); TEMP(instr.targetTempIndex) = __qmljs_call_property(context, base, instr.name, args, instr.argc); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallBuiltin) quint32 argStart = instr.args - context->variableCount(); - VM::Value *args = stack.data() + argStart; + VM::Value *args = stack + argStart; void *buf; switch (instr.builtin) { case Instr::instr_callBuiltin::builtin_typeof: @@ -300,20 +311,20 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(CreateValue) quint32 argStart = instr.args - context->variableCount(); - VM::Value *args = stack.data() + argStart; + VM::Value *args = stack + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_value(context, TEMP(instr.func), args, instr.argc); MOTH_END_INSTR(CreateValue) MOTH_BEGIN_INSTR(CreateProperty) quint32 argStart = instr.args - context->variableCount(); - VM::Value *args = stack.data() + argStart; + VM::Value *args = stack + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_property(context, TEMP(instr.base), instr.name, args, instr.argc); MOTH_END_INSTR(CreateProperty) MOTH_BEGIN_INSTR(CreateActivationProperty) TRACE(inline, "property name = %s, argc = %d", instr.name->toQString().toUtf8().constData(), instr.argc); quint32 argStart = instr.args - context->variableCount(); - VM::Value *args = stack.data() + argStart; + VM::Value *args = stack + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); MOTH_END_INSTR(CreateActivationProperty) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 661781001d..ea6b32a079 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -41,6 +41,7 @@ #include #include #include +#include "qv4mm.h" namespace QQmlJS { namespace VM { @@ -49,6 +50,9 @@ struct StringPool { QHash strings; + ~StringPool() + { qDeleteAll(strings.values()); } + String *newString(const QString &s) { QHash::const_iterator it = strings.find(s); @@ -60,11 +64,18 @@ struct StringPool } }; -ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) - : iselFactory(factory) +ExecutionEngine::ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory *factory) + : memoryManager(memoryManager) + , iselFactory(factory) , debugger(0) + , globalObject(Value::nullValue()) + , exception(Value::nullValue()) { + MemoryManager::GCBlocker gcBlocker(memoryManager); + stringPool = new StringPool; + memoryManager->setStringPool(stringPool); + memoryManager->setExecutionEngine(this); rootContext = newContext(); rootContext->init(this); @@ -75,21 +86,21 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) id_arguments = identifier(QStringLiteral("arguments")); id___proto__ = identifier(QStringLiteral("__proto__")); - objectPrototype = new ObjectPrototype(); - stringPrototype = new StringPrototype(rootContext); - numberPrototype = new NumberPrototype(); - booleanPrototype = new BooleanPrototype(); - arrayPrototype = new ArrayPrototype(); - datePrototype = new DatePrototype(); - functionPrototype = new FunctionPrototype(rootContext); - regExpPrototype = new RegExpPrototype(); - errorPrototype = new ErrorPrototype(); - evalErrorPrototype = new EvalErrorPrototype(rootContext); - rangeErrorPrototype = new RangeErrorPrototype(rootContext); - referenceErrorPrototype = new ReferenceErrorPrototype(rootContext); - syntaxErrorPrototype = new SyntaxErrorPrototype(rootContext); - typeErrorPrototype = new TypeErrorPrototype(rootContext); - uRIErrorPrototype = new URIErrorPrototype(rootContext); + objectPrototype = new (memoryManager) ObjectPrototype(); + stringPrototype = new (memoryManager) StringPrototype(rootContext); + numberPrototype = new (memoryManager) NumberPrototype(); + booleanPrototype = new (memoryManager) BooleanPrototype(); + arrayPrototype = new (memoryManager) ArrayPrototype(); + datePrototype = new (memoryManager) DatePrototype(); + functionPrototype = new (memoryManager) FunctionPrototype(rootContext); + regExpPrototype = new (memoryManager) RegExpPrototype(); + errorPrototype = new (memoryManager) ErrorPrototype(); + evalErrorPrototype = new (memoryManager) EvalErrorPrototype(rootContext); + rangeErrorPrototype = new (memoryManager) RangeErrorPrototype(rootContext); + referenceErrorPrototype = new (memoryManager) ReferenceErrorPrototype(rootContext); + syntaxErrorPrototype = new (memoryManager) SyntaxErrorPrototype(rootContext); + typeErrorPrototype = new (memoryManager) TypeErrorPrototype(rootContext); + uRIErrorPrototype = new (memoryManager) URIErrorPrototype(rootContext); stringPrototype->prototype = objectPrototype; numberPrototype->prototype = objectPrototype; @@ -106,21 +117,21 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) typeErrorPrototype->prototype = errorPrototype; uRIErrorPrototype->prototype = errorPrototype; - objectCtor = Value::fromObject(new ObjectCtor(rootContext)); - stringCtor = Value::fromObject(new StringCtor(rootContext)); - numberCtor = Value::fromObject(new NumberCtor(rootContext)); - booleanCtor = Value::fromObject(new BooleanCtor(rootContext)); - arrayCtor = Value::fromObject(new ArrayCtor(rootContext)); - functionCtor = Value::fromObject(new FunctionCtor(rootContext)); - dateCtor = Value::fromObject(new DateCtor(rootContext)); - regExpCtor = Value::fromObject(new RegExpCtor(rootContext)); - errorCtor = Value::fromObject(new ErrorCtor(rootContext)); - evalErrorCtor = Value::fromObject(new EvalErrorCtor(rootContext)); - rangeErrorCtor = Value::fromObject(new RangeErrorCtor(rootContext)); - referenceErrorCtor = Value::fromObject(new ReferenceErrorCtor(rootContext)); - syntaxErrorCtor = Value::fromObject(new SyntaxErrorCtor(rootContext)); - typeErrorCtor = Value::fromObject(new TypeErrorCtor(rootContext)); - uRIErrorCtor = Value::fromObject(new URIErrorCtor(rootContext)); + objectCtor = Value::fromObject(new (memoryManager) ObjectCtor(rootContext)); + stringCtor = Value::fromObject(new (memoryManager) StringCtor(rootContext)); + numberCtor = Value::fromObject(new (memoryManager) NumberCtor(rootContext)); + booleanCtor = Value::fromObject(new (memoryManager) BooleanCtor(rootContext)); + arrayCtor = Value::fromObject(new (memoryManager) ArrayCtor(rootContext)); + functionCtor = Value::fromObject(new (memoryManager) FunctionCtor(rootContext)); + dateCtor = Value::fromObject(new (memoryManager) DateCtor(rootContext)); + regExpCtor = Value::fromObject(new (memoryManager) RegExpCtor(rootContext)); + errorCtor = Value::fromObject(new (memoryManager) ErrorCtor(rootContext)); + evalErrorCtor = Value::fromObject(new (memoryManager) EvalErrorCtor(rootContext)); + rangeErrorCtor = Value::fromObject(new (memoryManager) RangeErrorCtor(rootContext)); + referenceErrorCtor = Value::fromObject(new (memoryManager) ReferenceErrorCtor(rootContext)); + syntaxErrorCtor = Value::fromObject(new (memoryManager) SyntaxErrorCtor(rootContext)); + typeErrorCtor = Value::fromObject(new (memoryManager) TypeErrorCtor(rootContext)); + uRIErrorCtor = Value::fromObject(new (memoryManager) URIErrorCtor(rootContext)); stringCtor.objectValue()->prototype = functionPrototype; numberCtor.objectValue()->prototype = functionPrototype; @@ -190,19 +201,19 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) pd.value = Value::fromDouble(INFINITY); glo->__defineOwnProperty__(rootContext, identifier(QStringLiteral("Infinity")), &pd); - glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new EvalFunction(rootContext))); + glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new (memoryManager) EvalFunction(rootContext))); // TODO: parseInt [15.1.2.2] // TODO: parseFloat [15.1.2.3] - glo->__put__(rootContext, identifier(QStringLiteral("isNaN")), Value::fromObject(new IsNaNFunction(rootContext))); // isNaN [15.1.2.4] - glo->__put__(rootContext, identifier(QStringLiteral("isFinite")), Value::fromObject(new IsFiniteFunction(rootContext))); // isFinite [15.1.2.5] + glo->__put__(rootContext, identifier(QStringLiteral("isNaN")), Value::fromObject(new (memoryManager) IsNaNFunction(rootContext))); // isNaN [15.1.2.4] + glo->__put__(rootContext, identifier(QStringLiteral("isFinite")), Value::fromObject(new (memoryManager) IsFiniteFunction(rootContext))); // isFinite [15.1.2.5] } ExecutionEngine::~ExecutionEngine() { delete globalObject.asObject(); delete rootContext; - delete stringPool; // the String pointers should get GC-ed. + delete stringPool; } ExecutionContext *ExecutionEngine::newContext() @@ -220,14 +231,16 @@ String *ExecutionEngine::identifier(const QString &s) FunctionObject *ExecutionEngine::newNativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) { - NativeFunction *f = new NativeFunction(scope, name, code); + NativeFunction *f = new (memoryManager) NativeFunction(scope, name, code); f->prototype = scope->engine->functionPrototype; return f; } FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, IR::Function *function) { - ScriptFunction *f = new ScriptFunction(scope, function); + MemoryManager::GCBlocker gcBlocker(memoryManager); + + ScriptFunction *f = new (memoryManager) ScriptFunction(scope, function); Object *proto = scope->engine->newObject(); proto->__put__(scope, scope->engine->id_constructor, Value::fromObject(f)); f->__put__(scope, scope->engine->id_prototype, Value::fromObject(proto)); @@ -237,14 +250,14 @@ FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, IR:: Object *ExecutionEngine::newObject() { - Object *object = new Object(); + Object *object = new (memoryManager) Object(); object->prototype = objectPrototype; return object; } FunctionObject *ExecutionEngine::newObjectCtor(ExecutionContext *ctx) { - return new ObjectCtor(ctx); + return new (memoryManager) ObjectCtor(ctx); } String *ExecutionEngine::newString(const QString &s) @@ -254,76 +267,76 @@ String *ExecutionEngine::newString(const QString &s) Object *ExecutionEngine::newStringObject(const Value &value) { - StringObject *object = new StringObject(value); + StringObject *object = new (memoryManager) StringObject(value); object->prototype = stringPrototype; return object; } FunctionObject *ExecutionEngine::newStringCtor(ExecutionContext *ctx) { - return new StringCtor(ctx); + return new (memoryManager) StringCtor(ctx); } Object *ExecutionEngine::newNumberObject(const Value &value) { - NumberObject *object = new NumberObject(value); + NumberObject *object = new (memoryManager) NumberObject(value); object->prototype = numberPrototype; return object; } FunctionObject *ExecutionEngine::newNumberCtor(ExecutionContext *ctx) { - return new NumberCtor(ctx); + return new (memoryManager) NumberCtor(ctx); } Object *ExecutionEngine::newBooleanObject(const Value &value) { - Object *object = new BooleanObject(value); + Object *object = new (memoryManager) BooleanObject(value); object->prototype = booleanPrototype; return object; } FunctionObject *ExecutionEngine::newBooleanCtor(ExecutionContext *ctx) { - return new BooleanCtor(ctx); + return new (memoryManager) BooleanCtor(ctx); } Object *ExecutionEngine::newFunctionObject(ExecutionContext *ctx) { - Object *object = new FunctionObject(ctx); + Object *object = new (memoryManager) FunctionObject(ctx); object->prototype = functionPrototype; return object; } ArrayObject *ExecutionEngine::newArrayObject() { - ArrayObject *object = new ArrayObject(); + ArrayObject *object = new (memoryManager) ArrayObject(); object->prototype = arrayPrototype; return object; } ArrayObject *ExecutionEngine::newArrayObject(const Array &value) { - ArrayObject *object = new ArrayObject(value); + ArrayObject *object = new (memoryManager) ArrayObject(value); object->prototype = arrayPrototype; return object; } FunctionObject *ExecutionEngine::newArrayCtor(ExecutionContext *ctx) { - return new ArrayCtor(ctx); + return new (memoryManager) ArrayCtor(ctx); } Object *ExecutionEngine::newDateObject(const Value &value) { - Object *object = new DateObject(value); + Object *object = new (memoryManager) DateObject(value); object->prototype = datePrototype; return object; } FunctionObject *ExecutionEngine::newDateCtor(ExecutionContext *ctx) { - return new DateCtor(ctx); + return new (memoryManager) DateCtor(ctx); } Object *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) @@ -335,61 +348,60 @@ Object *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) if (flags & IR::RegExp::RegExp_Multiline) options |= QRegularExpression::MultilineOption; - Object *object = new RegExpObject(QRegularExpression(pattern, options), global); + Object *object = new (memoryManager) RegExpObject(QRegularExpression(pattern, options), global); object->prototype = regExpPrototype; return object; } FunctionObject *ExecutionEngine::newRegExpCtor(ExecutionContext *ctx) { - return new RegExpCtor(ctx); + return new (memoryManager) RegExpCtor(ctx); } Object *ExecutionEngine::newErrorObject(const Value &value) { - ErrorObject *object = new ErrorObject(value); + ErrorObject *object = new (memoryManager) ErrorObject(value); object->prototype = errorPrototype; return object; } Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) { - SyntaxErrorObject *object = new SyntaxErrorObject(ctx, message); + SyntaxErrorObject *object = new (memoryManager) SyntaxErrorObject(ctx, message); object->prototype = syntaxErrorPrototype; return object; } Object *ExecutionEngine::newReferenceErrorObject(ExecutionContext *ctx, const QString &message) { - ReferenceErrorObject *object = new ReferenceErrorObject(ctx, message); + ReferenceErrorObject *object = new (memoryManager) ReferenceErrorObject(ctx, message); object->prototype = referenceErrorPrototype; return object; } Object *ExecutionEngine::newTypeErrorObject(ExecutionContext *ctx, const QString &message) { - TypeErrorObject *object = new TypeErrorObject(ctx, message); + TypeErrorObject *object = new (memoryManager) TypeErrorObject(ctx, message); object->prototype = typeErrorPrototype; return object; } Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) { - MathObject *object = new MathObject(ctx); + MathObject *object = new (memoryManager) MathObject(ctx); object->prototype = objectPrototype; return object; } Object *ExecutionEngine::newActivationObject() { - return new Object(); + return new (memoryManager) Object(); } Object *ExecutionEngine::newForEachIteratorObject(Object *o) { - return new ForEachIteratorObject(o); + return new (memoryManager) ForEachIteratorObject(o); } - } // namespace VM } // namespace QQmlJS diff --git a/qmljs_engine.h b/qmljs_engine.h index 2837adb882..520acab675 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -68,6 +68,7 @@ struct ErrorObject; struct ArgumentsObject; struct ExecutionContext; struct ExecutionEngine; +class MemoryManager; struct ObjectPrototype; struct StringPrototype; @@ -87,6 +88,7 @@ struct URIErrorPrototype; struct ExecutionEngine { + MemoryManager *memoryManager; EvalISelFactory *iselFactory; ExecutionContext *current; ExecutionContext *rootContext; @@ -148,7 +150,7 @@ struct ExecutionEngine struct StringPool *stringPool; - ExecutionEngine(EvalISelFactory *iselFactory); + ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory *iselFactory); ~ExecutionEngine(); ExecutionContext *newContext(); diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 0127b04852..0d6b2006ea 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -44,6 +44,7 @@ #include #include #include +#include "qv4mm.h" namespace QQmlJS { namespace VM { @@ -266,7 +267,6 @@ void ExecutionContext::setProperty(String *name, Value value) Value ExecutionContext::getProperty(String *name) { - PropertyDescriptor tmp; for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { if (ctx->withObject) { With *w = ctx->withObject; @@ -286,7 +286,7 @@ Value ExecutionContext::getProperty(String *name) if (ctx->activation && ctx->activation->__hasProperty__(ctx, name)) return ctx->activation->__get__(ctx, name); if (name->isEqualTo(ctx->engine->id_arguments)) { - Value arguments = Value::fromObject(new ArgumentsObject(this)); + Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this)); createMutableBinding(ctx->engine->id_arguments, false); setMutableBinding(this, ctx->engine->id_arguments, arguments); return arguments; @@ -345,6 +345,8 @@ void ExecutionContext::throwReferenceError(Value value) void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc) { + MemoryManager::GCBlocker blockGC(parent->engine->memoryManager); + engine = parent->engine; this->parent = parent; thisObject = that; diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 7fdb86aa72..d74a501a7e 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -43,6 +43,7 @@ #include "qv4ir_p.h" #include "qv4isel_p.h" #include "qv4ecmaobjects_p.h" +#include "qv4mm.h" #include #include @@ -59,6 +60,28 @@ using namespace QQmlJS::VM; + +Managed::~Managed() +{ +} + +void *Managed::operator new(size_t size, MemoryManager *mm) +{ + assert(mm); + + return mm->allocManaged(size); +} + +void Managed::operator delete(void *ptr) +{ + if (!ptr) + return; + + Managed *m = reinterpret_cast(ptr); + assert(m->mm); + m->mm->deallocManaged(m); +} + // // Object // @@ -107,6 +130,20 @@ bool Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ct return inplaceBinOp(rhs, name, op, ctx); } +void Object::getCollectables(QVector &objects) +{ + if (prototype) + objects.append(prototype); + + if (members) { + for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) { + if ((*it)->descriptor.isData()) + if (Object *o = (*it)->descriptor.value.asObject()) + objects.append(o); + } + } +} + // Section 8.12.1 PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *, String *name) { @@ -349,6 +386,15 @@ String *ForEachIteratorObject::nextPropertyName() } } +void ForEachIteratorObject::getCollectables(QVector &objects) +{ + Object::getCollectables(objects); + if (object) + objects.append(object); + if (current) + objects.append(current); +} + Value ArrayObject::__get__(ExecutionContext *ctx, String *name) { if (name->isEqualTo(ctx->engine->id_length)) @@ -368,6 +414,12 @@ bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContex return Object::inplaceBinOp(rhs, index, op, ctx); } +void ArrayObject::getCollectables(QVector &objects) +{ + Object::getCollectables(objects); + value.getCollectables(objects); +} + bool FunctionObject::hasInstance(ExecutionContext *ctx, const Value &value) { if (! value.isObject()) { @@ -490,7 +542,7 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value bool directCall = true; const QString code = args[0].stringValue()->toQString(); - QQmlJS::IR::Function *f = parseSource(context, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode); + QScopedPointer f(parseSource(context, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode)); if (!f) return Value::undefinedValue(); @@ -534,6 +586,8 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct { using namespace QQmlJS; + MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); + VM::ExecutionEngine *vm = ctx->engine; IR::Module module; IR::Function *globalCode = 0; @@ -654,6 +708,13 @@ void ErrorObject::setNameProperty(ExecutionContext *ctx) __put__(ctx, QLatin1String("name"), Value::fromString(ctx, className())); } +void ErrorObject::getCollectables(QVector &objects) +{ + Object::getCollectables(objects); + if (Object *o = value.asObject()) + objects.append(o); +} + SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) : ErrorObject(ctx->argument(0)) , msg(message) @@ -675,7 +736,6 @@ Value ScriptFunction::construct(VM::ExecutionContext *ctx) return ctx->thisObject; } - Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name) { if (name->isEqualTo(ctx->engine->id_length)) @@ -698,7 +758,6 @@ PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(ExecutionContext return Object::__getPropertyDescriptor__(ctx, name, to_fill); } - NativeFunction::NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) : FunctionObject(scope) , code(code) diff --git a/qmljs_objects.h b/qmljs_objects.h index fc8deed7f9..9daa84132b 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -75,6 +75,7 @@ struct ErrorObject; struct ArgumentsObject; struct ExecutionContext; struct ExecutionEngine; +class MemoryManager; struct ObjectPrototype; struct StringPrototype; @@ -92,6 +93,29 @@ struct SyntaxErrorPrototype; struct TypeErrorPrototype; struct URIErrorPrototype; +struct Managed +{ +private: + Managed(const Managed &other); + void operator = (const Managed &other); + +protected: + Managed() {} + +public: + virtual ~Managed(); + + void *operator new(size_t size, MemoryManager *mm); + void operator delete(void *ptr); + +protected: + virtual void getCollectables(QVector &objects) = 0; + +private: + friend class MemoryManager; + MemoryManager *mm; +}; + struct String { inline bool isEqualTo(const String *other) const { if (this == other) @@ -395,7 +419,7 @@ private: int _allocated: 27; }; -struct Object { +struct Object: Managed { Object *prototype; String *klass; PropertyTable *members; @@ -440,6 +464,9 @@ struct Object { Value getValue(ExecutionContext *ctx, PropertyDescriptor *p) const; bool inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); + +protected: + virtual void getCollectables(QVector &objects); }; struct ForEachIteratorObject: Object { @@ -450,6 +477,9 @@ struct ForEachIteratorObject: Object { virtual QString className() { return QStringLiteral("__ForEachIteratorObject"); } String *nextPropertyName(); + +protected: + virtual void getCollectables(QVector &objects); }; struct BooleanObject: Object { @@ -489,6 +519,9 @@ struct ArrayObject: Object { virtual Value __get__(ExecutionContext *ctx, String *name); virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); + +protected: + virtual void getCollectables(QVector &objects); }; struct FunctionObject: Object { @@ -591,6 +624,7 @@ struct ErrorObject: Object { protected: void setNameProperty(ExecutionContext *ctx); + virtual void getCollectables(QVector &objects); }; struct EvalErrorObject: ErrorObject { diff --git a/qv4array_p.h b/qv4array_p.h index 20022affe7..22c29f91b5 100644 --- a/qv4array_p.h +++ b/qv4array_p.h @@ -74,8 +74,11 @@ public: Array &other); inline void push(const Value &value); + void getCollectables(QVector &objects); + private: - std::deque *to_vector; + typedef std::deque ToVectorType; + ToVectorType *to_vector; }; class ArrayElementLessThan @@ -194,6 +197,14 @@ inline void Array::push(const Value &value) to_vector->push_back(value); } +inline void Array::getCollectables(QVector &objects) +{ + for (ToVectorType::const_iterator it = to_vector->begin(), eit = to_vector->end(); it != eit; ++it) { + if (Object *o = it->asObject()) + objects.append(o); + } +} + inline void Array::splice(double start, double deleteCount, const QVector &items, Array &other) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 34c785d634..8b3361b3bb 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -151,7 +151,7 @@ void liveness(IR::Function *function) computeUseDef(s); } - dfs(function->basicBlocks.first(), &V, &blocks); + dfs(function->basicBlocks.at(0), &V, &blocks); bool changed; do { @@ -1471,6 +1471,9 @@ void Codegen::linearize(IR::Function *function) exitBlock->index = trace.size(); trace.append(exitBlock); + foreach (IR::BasicBlock *b, function->basicBlocks) + if (!trace.contains(b)) + delete b; function->basicBlocks = trace; #ifndef QV4_NO_LIVENESS diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 607c1f1624..6eca545f48 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -42,6 +42,7 @@ #include "qv4ecmaobjects_p.h" #include "qv4array_p.h" +#include "qv4mm.h" #include #include #include @@ -1860,6 +1861,8 @@ FunctionCtor::FunctionCtor(ExecutionContext *scope) // 15.3.2 Value FunctionCtor::construct(ExecutionContext *ctx) { + MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); + QString args; QString body; if (ctx->argumentCount > 0) @@ -1899,7 +1902,7 @@ Value FunctionCtor::construct(ExecutionContext *ctx) isel->run(irf); delete isel; - ctx->thisObject = Value::fromObject(new ScriptFunction(ctx->engine->rootContext, irf)); + ctx->thisObject = Value::fromObject(ctx->engine->newScriptFunction(ctx->engine->rootContext, irf)); return ctx->thisObject; } @@ -2593,7 +2596,7 @@ Value RegExpCtor::construct(ExecutionContext *ctx) if (!f.isUndefined()) ctx->throwTypeError(); - return Value::fromObject(new RegExpObject(re->value, false)); + return Value::fromObject(new (ctx->engine->memoryManager) RegExpObject(re->value, false)); } if (r.isUndefined()) @@ -2623,7 +2626,7 @@ Value RegExpCtor::construct(ExecutionContext *ctx) if (!re.isValid()) ctx->throwTypeError(); - ctx->thisObject = Value::fromObject(new RegExpObject(re, global)); + ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) RegExpObject(re, global)); return ctx->thisObject; } @@ -2715,7 +2718,7 @@ ErrorCtor::ErrorCtor(ExecutionContext *scope) Value ErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new ErrorObject(ctx->argument(0))); + ctx->thisObject = Value::fromObject(ctx->engine->newErrorObject(ctx->argument(0))); return ctx->thisObject; } @@ -2732,37 +2735,37 @@ Value ErrorCtor::call(ExecutionContext *ctx) Value EvalErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new EvalErrorObject(ctx)); + ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx)); return ctx->thisObject; } Value RangeErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new RangeErrorObject(ctx)); + ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx)); return ctx->thisObject; } Value ReferenceErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new ReferenceErrorObject(ctx)); + ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx)); return ctx->thisObject; } Value SyntaxErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new SyntaxErrorObject(ctx, 0)); + ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) SyntaxErrorObject(ctx, 0)); return ctx->thisObject; } Value TypeErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new TypeErrorObject(ctx)); + ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx)); return ctx->thisObject; } Value URIErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new URIErrorObject(ctx)); + ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx)); return ctx->thisObject; } diff --git a/qv4ir.cpp b/qv4ir.cpp index 7646b371d0..2a9414bb5d 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -391,7 +391,19 @@ Function *Module::newFunction(const QString &name) return f; } +Module::~Module() +{ + foreach (Function *f, functions) + f->releaseModuleManagedData(); +} + Function::~Function() +{ + delete[] codeData; +} + + +void Function::releaseModuleManagedData() { // destroy the Stmt::Data blocks manually, because memory pool cleanup won't // call the Stmt destructors. @@ -400,9 +412,11 @@ Function::~Function() s->destroyData(); qDeleteAll(basicBlocks); - delete[] codeData; + pool = 0; + module = 0; } + const QString *Function::newString(const QString &text) { return &*strings.insert(text); diff --git a/qv4ir_p.h b/qv4ir_p.h index 92276d0b6d..14c73e5b1a 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -587,6 +587,8 @@ struct Module { QVector functions; Function *newFunction(const QString &name); + + ~Module(); }; struct Function { @@ -626,6 +628,7 @@ struct Function { { this->name = newString(name); } ~Function(); + void releaseModuleManagedData(); enum BasicBlockInsertMode { InsertBlock, diff --git a/qv4mm.cpp b/qv4mm.cpp new file mode 100644 index 0000000000..4be7219d7c --- /dev/null +++ b/qv4mm.cpp @@ -0,0 +1,375 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qmljs_engine.h" +#include "qmljs_objects.h" +#include "qv4ecmaobjects_p.h" +#include "qv4mm.h" + +#include +#include +#include + +#include + +using namespace QQmlJS::VM; + +static const std::size_t CHUNK_SIZE = 65536; + +struct MemoryManager::Data +{ + bool enableGC; + bool gcBlocked; + bool scribble; + bool aggressiveGC; + ExecutionEngine *engine; + StringPool *stringPool; + + // FIXME: this freeList will get out of hand if there is one allocation+deallocation of, say, 16M. + // TODO: turn the freeList into a fixed length array which can hold the most common sizes (e.g. up to 64K), then use a tree for anything afterwards. + // TODO: this requires that the interaction with the freeList is factored out first into separate methods. + QVector freeList; + QLinkedList > heapChunks; + + // statistics: +#ifdef DETAILED_MM_STATS + QVector allocSizeCounters; +#endif // DETAILED_MM_STATS + + Data(bool enableGC) + : enableGC(enableGC) + , gcBlocked(false) + , engine(0) + , stringPool(0) + , freeList(0) + { + scribble = qgetenv("MM_NO_SCRIBBLE").isEmpty(); + aggressiveGC = !qgetenv("MM_AGGRESSIVE_GC").isEmpty(); + } + + ~Data() + { + for (QLinkedList >::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) + delete[] i->first; + } +}; + +MemoryManager::MemoryManager() + : m_d(new Data(true)) +{ +} + +MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) +{ + if (m_d->aggressiveGC) + runGC(); +#ifdef DETAILED_MM_STATS + willAllocate(size); +#endif // DETAILED_MM_STATS + + size += sizeof(MMInfo); + assert(size >= 16); + assert(size % 16 == 0); + + for (std::size_t s = size / 16, es = m_d->freeList.size(); s < es; ++s) { + if (MMObject *m = m_d->freeList.at(s)) { + m_d->freeList[s] = m->info.next; + + if (s != size / 16) { + MMObject *tail = reinterpret_cast(reinterpret_cast(m) + size); + assert(m->info.size == s * 16); + tail->info.inUse = 0; + tail->info.markBit = 0; + tail->info.size = m->info.size - size; + MMObject *&f = m_d->freeList[tail->info.size / 16]; + tail->info.next = f; + f = tail; + m->info.size = size; + } + + m->info.inUse = 1; + m->info.markBit = 0; + scribble(m, 0xaa); +// qDebug("alloc(%lu) -> %p", size, m); + return m; + } + } + + if (!m_d->aggressiveGC) + if (runGC() >= size) + return alloc(size - sizeof(MMInfo)); + + std::size_t allocSize = std::max(size, CHUNK_SIZE); + char *ptr = new char[allocSize]; + m_d->heapChunks.append(qMakePair(ptr, allocSize)); +// qDebug("Allocated new chunk of %lu bytes @ %p", allocSize, ptr); + + if (allocSize > size) { + MMObject *m = reinterpret_cast(ptr + size); + m->info.size = allocSize - size; + std::size_t off = m->info.size / 16; + if (((std::size_t) m_d->freeList.size()) <= off) + m_d->freeList.resize(off + 1); + MMObject *&f = m_d->freeList[off]; + m->info.next = f; + f = m; + } + + MMObject *m = reinterpret_cast(ptr); + m->info.inUse = 1; + m->info.markBit = 0; + m->info.size = size; + scribble(m, 0xaa); +// qDebug("alloc(%lu) -> %p", size, ptr); + return m; +} + +void MemoryManager::dealloc(MMObject *ptr) +{ + if (!ptr) + return; + + assert(ptr->info.size >= 16); + assert(ptr->info.size % 16 == 0); + +// qDebug("dealloc %p (%lu)", ptr, ptr->info.size); + + std::size_t off = ptr->info.size / 16; + if (((std::size_t) m_d->freeList.size()) <= off) + m_d->freeList.resize(off + 1); + MMObject *&f = m_d->freeList[off]; + ptr->info.next = f; + ptr->info.inUse = 0; + ptr->info.markBit = 0; + ptr->info.needsManagedDestructorCall = 0; + f = ptr; + scribble(ptr, 0x55); +} + +void MemoryManager::scribble(MemoryManager::MMObject *obj, int c) const +{ + if (m_d->scribble) + ::memset(&obj->data, c, obj->info.size - sizeof(MMInfo)); +} + +std::size_t MemoryManager::mark(const QVector &objects) +{ + std::size_t marks = 0; + + QVector kids; + kids.reserve(32); + + foreach (Object *o, objects) { + if (!o) + continue; + + MMObject *obj = toObject(o); + assert(obj->info.inUse); + if (obj->info.markBit == 0) { + obj->info.markBit = 1; + ++marks; + static_cast(o)->getCollectables(kids); + marks += mark(kids); + kids.resize(0); + } + } + + return marks; +} + +std::size_t MemoryManager::sweep(std::size_t &largestFreedBlock) +{ + std::size_t freedCount = 0; + + for (QLinkedList >::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) + freedCount += sweep(i->first, i->second, largestFreedBlock); + + return freedCount; +} + +std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, std::size_t &largestFreedBlock) +{ +// qDebug("chunkStart @ %p", chunkStart); + std::size_t freedCount = 0; + + for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize; chunk < chunkEnd; ) { + MMObject *m = reinterpret_cast(chunk); +// qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s", +// chunk, m->info.size, (m->info.inUse ? "yes" : "no"), (m->info.markBit ? "true" : "false")); + + assert((intptr_t) chunk % 16 == 0); + assert(m->info.size >= 16); + assert(m->info.size % 16 == 0); + + chunk = chunk + m->info.size; + if (m->info.inUse) { + if (m->info.markBit) { + m->info.markBit = 0; + } else { +// qDebug("-- collecting it."); + if (m->info.needsManagedDestructorCall) + reinterpret_cast(&m->data)->~Managed(); + dealloc(m); + largestFreedBlock = std::max(largestFreedBlock, m->info.size); + ++freedCount; + } + } + } + + return freedCount; +} + +bool MemoryManager::isGCBlocked() const +{ + return m_d->gcBlocked; +} + +void MemoryManager::setGCBlocked(bool blockGC) +{ + m_d->gcBlocked = blockGC; +} + +std::size_t MemoryManager::runGC() +{ + if (!m_d->enableGC || m_d->gcBlocked) { +// qDebug() << "Not running GC."; + return 0; + } + +// QTime t; t.start(); + + QVector roots; + collectRoots(roots); +// std::cerr << "GC: found " << roots.size() +// << " roots in " << t.elapsed() +// << "ms" << std::endl; + +// t.restart(); + /*std::size_t marks =*/ mark(roots); +// std::cerr << "GC: marked " << marks +// << " objects in " << t.elapsed() +// << "ms" << std::endl; + +// t.restart(); + std::size_t freedCount = 0, largestFreedBlock = 0; + freedCount = sweep(largestFreedBlock); +// std::cerr << "GC: sweep freed " << freedCount +// << " objects in " << t.elapsed() +// << "ms" << std::endl; + + return largestFreedBlock; +} + +void MemoryManager::setEnableGC(bool enableGC) +{ + m_d->enableGC = enableGC; +} + +MemoryManager::~MemoryManager() +{ + std::size_t dummy = 0; + sweep(dummy); +} + +static inline void add(QVector &values, const Value &v) +{ + if (Object *o = v.asObject()) + values.append(o); +} + +void MemoryManager::setExecutionEngine(ExecutionEngine *engine) +{ + m_d->engine = engine; +} + +void MemoryManager::setStringPool(StringPool *stringPool) +{ + m_d->stringPool = stringPool; +} + +void MemoryManager::dumpStats() const +{ + std::cerr << "=================" << std::endl; + std::cerr << "Allocation stats:" << std::endl; +#ifdef DETAILED_MM_STATS + std::cerr << "Requests for each chunk size:" << std::endl; + for (int i = 0; i < m_d->allocSizeCounters.size(); ++i) { + if (unsigned count = m_d->allocSizeCounters[i]) { + std::cerr << "\t" << (i << 4) << " bytes chunks: " << count << std::endl; + } + } +#endif // DETAILED_MM_STATS +} + +ExecutionEngine *MemoryManager::engine() const +{ + return m_d->engine; +} + +#ifdef DETAILED_MM_STATS +void MemoryManager::willAllocate(std::size_t size) +{ + unsigned alignedSize = (size + 15) >> 4; + QVector &counters = m_d->allocSizeCounters; + if ((unsigned) counters.size() < alignedSize + 1) + counters.resize(alignedSize + 1); + counters[alignedSize]++; +} +#endif // DETAILED_MM_STATS + +void MemoryManager::collectRoots(QVector &roots) const +{ + add(roots, m_d->engine->globalObject); + add(roots, m_d->engine->exception); + + for (ExecutionContext *ctxt = engine()->current; ctxt; ctxt = ctxt->parent) { + add(roots, ctxt->thisObject); + if (ctxt->function) + roots.append(ctxt->function); + for (unsigned arg = 0, lastArg = ctxt->formalCount(); arg < lastArg; ++arg) + add(roots, ctxt->arguments[arg]); + for (unsigned local = 0, lastLocal = ctxt->variableCount(); local < lastLocal; ++local) + add(roots, ctxt->locals[local]); + if (ctxt->activation) + roots.append(ctxt->activation); + for (ExecutionContext::With *it = ctxt->withObject; it; it = it->next) + if (it->object) + roots.append(it->object); + } + + collectRootsOnStack(roots); +} + +MemoryManagerWithoutGC::~MemoryManagerWithoutGC() +{} + +void MemoryManagerWithoutGC::collectRootsOnStack(QVector &roots) const +{ + Q_UNUSED(roots); +} diff --git a/qv4mm.h b/qv4mm.h new file mode 100644 index 0000000000..ece3765e3b --- /dev/null +++ b/qv4mm.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QV4GC_H +#define QV4GC_H + +#include "qmljs_objects.h" + +#include + +#define DETAILED_MM_STATS + +namespace QQmlJS { +namespace VM { + +class MemoryManager +{ + MemoryManager(const MemoryManager &); + MemoryManager &operator=(const MemoryManager&); + + struct Data; + +public: + class GCBlocker + { + public: + GCBlocker(MemoryManager *mm) + : mm(mm) + , wasBlocked(mm->isGCBlocked()) + { + mm->setGCBlocked(true); + } + + ~GCBlocker() + { + mm->setGCBlocked(wasBlocked); + } + + private: + MemoryManager *mm; + bool wasBlocked; + }; + +public: + MemoryManager(); + virtual ~MemoryManager() = 0; + + // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries). + // Note: all occurances of "16" in alloc/dealloc are also due to the alignment. + static inline std::size_t align(std::size_t size) + { return (size + 15) & ~0xf; } + + inline Managed *allocManaged(std::size_t size) + { + size = align(size); + MMObject *o = alloc(size); + o->info.needsManagedDestructorCall = 1; + Managed *ptr = reinterpret_cast(&o->data); + ptr->mm = this; + return ptr; + } + + inline void deallocManaged(Managed *m) + { + if (!m) + return; + + assert(m->mm == this); + dealloc(toObject(m)); + } + + bool isGCBlocked() const; + void setGCBlocked(bool blockGC); + std::size_t runGC(); + + void setEnableGC(bool enableGC); + void setExecutionEngine(ExecutionEngine *engine); + void setStringPool(StringPool *stringPool); + + void dumpStats() const; + +protected: +#if 1 // 64bit and x86: + struct MMObject; + struct MMInfo { + std::size_t inUse : 1; + std::size_t markBit : 1; + std::size_t needsManagedDestructorCall : 1; + std::size_t size : 61; + MMObject *next; + }; + struct MMObject { + MMInfo info; + std::size_t data; + }; +#endif +#if 0 // for 32bits: + // untested! + struct MMInfo { + std::size_t inUse : 1; + std::size_t markBit : 1; + std::size_t size : 30; + }; + struct MMObject { + MMInfo info; + union { + struct MMObject *next; + char data[1]; + } + }; +#endif + +protected: + static inline MMObject *toObject(void *ptr) { return reinterpret_cast(reinterpret_cast(ptr) - sizeof(MMInfo)); } + + /// expects size to be aligned + // TODO: try to inline + MMObject *alloc(std::size_t size); + + // TODO: try to inline + void dealloc(MMObject *ptr); + + void scribble(MMObject *obj, int c) const; + + virtual void collectRootsOnStack(QVector &roots) const = 0; + + ExecutionEngine *engine() const; + +#ifdef DETAILED_MM_STATS + void willAllocate(std::size_t size); +#endif // DETAILED_MM_STATS + +private: + void collectRoots(QVector &roots) const; + static std::size_t mark(const QVector &objects); + std::size_t sweep(std::size_t &largestFreedBlock); + std::size_t sweep(char *chunkStart, std::size_t chunkSize, std::size_t &largestFreedBlock); + +private: + QScopedPointer m_d; +}; + +class MemoryManagerWithoutGC: public MemoryManager +{ +public: + MemoryManagerWithoutGC() + { setEnableGC(false); } + + virtual ~MemoryManagerWithoutGC(); + +protected: + virtual void collectRootsOnStack(QVector &roots) const; +}; + +} // namespace VM +} // namespace QQmlJS + +#endif // QV4GC_H diff --git a/v4.pro b/v4.pro index 24ab7c0d0e..ce1409b303 100644 --- a/v4.pro +++ b/v4.pro @@ -24,7 +24,8 @@ SOURCES += main.cpp \ qv4isel_masm.cpp \ llvm_runtime.cpp \ qv4isel_p.cpp \ - debugging.cpp + debugging.cpp \ + qv4mm.cpp HEADERS += \ qv4codegen_p.h \ @@ -41,7 +42,8 @@ HEADERS += \ qv4isel_masm_p.h \ qv4isel_p.h \ qv4isel_util_p.h \ - debugging.h + debugging.h \ + qv4mm.h llvm { -- cgit v1.2.3 From 357633a4a3e40aa1f4ffacc0506da9b790b2bd76 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 6 Dec 2012 13:52:16 +0100 Subject: Fixed memory leak. Change-Id: Id4ae9d9192e2f2bd97e4a789a1613b669feaccb0 Reviewed-by: Lars Knoll --- qv4ecmaobjects.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 6eca545f48..5a29c64fc2 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -506,13 +506,13 @@ static inline QString ToLocaleTimeString(double t) static double getLocalTZA() { #ifndef Q_WS_WIN - struct tm* t; + struct tm t; time_t curr; time(&curr); - t = localtime(&curr); - time_t locl = mktime(t); - t = gmtime(&curr); - time_t globl = mktime(t); + localtime_r(&curr, &t); + time_t locl = mktime(&t); + gmtime_r(&curr, &t); + time_t globl = mktime(&t); return double(locl - globl) * 1000.0; #else TIME_ZONE_INFORMATION tzInfo; -- cgit v1.2.3 From 852cc7f443f2321b0c53077f3fc0cee577903199 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 8 Dec 2012 05:18:22 +0100 Subject: Fix gc on ia32 * size += sizeof(MMInfo) risk unaligning size, to run it through align() again * Don't rely on new returning an aligned pointer, use memalign * Enable #ifdefs for 32-bit MMInfo/MMObject Change-Id: If22abb9e0d77ece385793ea5e92540f177d3a07c Reviewed-by: Lars Knoll --- qv4mm.cpp | 5 +++-- qv4mm.h | 15 ++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/qv4mm.cpp b/qv4mm.cpp index 4be7219d7c..8fe3815110 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -37,6 +37,7 @@ #include #include +#include using namespace QQmlJS::VM; @@ -93,7 +94,7 @@ MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) willAllocate(size); #endif // DETAILED_MM_STATS - size += sizeof(MMInfo); + size += align(sizeof(MMInfo)); assert(size >= 16); assert(size % 16 == 0); @@ -126,7 +127,7 @@ MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) return alloc(size - sizeof(MMInfo)); std::size_t allocSize = std::max(size, CHUNK_SIZE); - char *ptr = new char[allocSize]; + char *ptr = (char*)memalign(16, allocSize); m_d->heapChunks.append(qMakePair(ptr, allocSize)); // qDebug("Allocated new chunk of %lu bytes @ %p", allocSize, ptr); diff --git a/qv4mm.h b/qv4mm.h index ece3765e3b..85395b937b 100644 --- a/qv4mm.h +++ b/qv4mm.h @@ -106,8 +106,8 @@ public: void dumpStats() const; protected: -#if 1 // 64bit and x86: struct MMObject; +#if CPU(X86_64) // 64bit and x86: struct MMInfo { std::size_t inUse : 1; std::size_t markBit : 1; @@ -119,20 +119,17 @@ protected: MMInfo info; std::size_t data; }; -#endif -#if 0 // for 32bits: - // untested! +#elif CPU(X86) // for 32bits: struct MMInfo { std::size_t inUse : 1; std::size_t markBit : 1; - std::size_t size : 30; + std::size_t needsManagedDestructorCall : 1; + std::size_t size : 29; + struct MMObject *next; }; struct MMObject { MMInfo info; - union { - struct MMObject *next; - char data[1]; - } + std::size_t data; }; #endif -- cgit v1.2.3 From 806b5fb4c14b9da712d57525c35b6333665cf26d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 7 Dec 2012 20:31:19 -0800 Subject: Fix typeof to work with undefined references Change-Id: I90cd2b34a25476dfee1ec01315275b6c179d11dc Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 2 +- qmljs_environment.cpp | 30 ++++++++++++++++++++++++++++++ qmljs_environment.h | 1 + qmljs_runtime.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++-- qmljs_runtime.h | 35 +++++------------------------------ qv4codegen.cpp | 2 +- qv4isel_masm.cpp | 20 ++++++++++++++++---- 7 files changed, 96 insertions(+), 38 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 76abd793d1..6a0c163c13 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -451,7 +451,7 @@ void __qmljs_llvm_set_property(ExecutionContext *ctx, Value *object, String *nam void __qmljs_llvm_typeof(ExecutionContext *ctx, Value *result, const Value *value) { - *result = __qmljs_typeof(*value, ctx); + *result = __qmljs_builtin_typeof(*value, ctx); } void __qmljs_llvm_throw(ExecutionContext *context, Value *value) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 0d6b2006ea..a64d9dccb6 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -296,6 +296,36 @@ Value ExecutionContext::getProperty(String *name) return Value::undefinedValue(); } +Value ExecutionContext::getPropertyNoThrow(String *name) +{ + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { + if (ctx->withObject) { + With *w = ctx->withObject; + while (w) { + if (w->object->__hasProperty__(ctx, name)) + return w->object->__get__(ctx, name); + w = w->next; + } + } + + for (unsigned int i = 0; i < ctx->variableCount(); ++i) + if (__qmljs_string_equal(ctx->variables()[i], name)) + return ctx->locals[i]; + for (unsigned int i = 0; i < ctx->formalCount(); ++i) + if (__qmljs_string_equal(ctx->formals()[i], name)) + return ctx->arguments[i]; + if (ctx->activation && ctx->activation->__hasProperty__(ctx, name)) + return ctx->activation->__get__(ctx, name); + if (name->isEqualTo(ctx->engine->id_arguments)) { + Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this)); + createMutableBinding(ctx->engine->id_arguments, false); + setMutableBinding(this, ctx->engine->id_arguments, arguments); + return arguments; + } + } + return Value::undefinedValue(); +} + void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) diff --git a/qmljs_environment.h b/qmljs_environment.h index 4063f384d0..5ecc56e9ab 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -122,6 +122,7 @@ struct ExecutionContext void setProperty(String *name, Value value); Value getProperty(String *name); + Value getPropertyNoThrow(String *name); void inplaceBitOp(Value value, String *name, BinOp op); bool deleteProperty(String *name); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index d7bbf0eec1..aaa5be9ef6 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -798,9 +798,49 @@ Value __qmljs_get_exception(ExecutionContext *context) return context->engine->exception; } -Value __qmljs_builtin_typeof(Value val, ExecutionContext *context) +Value __qmljs_builtin_typeof(Value value, ExecutionContext *ctx) +{ + switch (value.type()) { + case Value::Undefined_Type: + return __qmljs_string_literal_undefined(ctx); + break; + case Value::Null_Type: + return __qmljs_string_literal_object(ctx); + break; + case Value::Boolean_Type: + return __qmljs_string_literal_boolean(ctx); + break; + case Value::String_Type: + return __qmljs_string_literal_string(ctx); + break; + case Value::Object_Type: + if (__qmljs_is_callable(value, ctx)) + return __qmljs_string_literal_function(ctx); + else + return __qmljs_string_literal_object(ctx); // ### implementation-defined + break; + default: + return __qmljs_string_literal_number(ctx); + break; + } +} + +Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context) +{ + return __qmljs_builtin_typeof(context->getPropertyNoThrow(name), context); +} + +Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext *context) +{ + Value obj = base.toObject(context); + return __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context); +} + +Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context) { - return __qmljs_typeof(val, context); + String *name = index.toString(context); + Value obj = base.toObject(context); + return __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context); } void __qmljs_builtin_throw(Value val, ExecutionContext *context) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index adcdae6601..6675920cb3 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -98,7 +98,11 @@ Value __qmljs_construct_activation_property(ExecutionContext *, String *name, Va Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc); Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc); -Value __qmljs_builtin_typeof(Value val, ExecutionContext *context); +Value __qmljs_builtin_typeof(Value val, ExecutionContext *ctx); +Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context); +Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext *context); +Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context); + void __qmljs_builtin_throw(Value val, ExecutionContext *context); void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx); void __qmljs_builtin_pop_with(ExecutionContext *ctx); @@ -179,7 +183,6 @@ Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index); Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name); Value __qmljs_delete_name(ExecutionContext *ctx, String *name); -Value __qmljs_typeof(Value value, ExecutionContext *ctx); void __qmljs_throw(Value value, ExecutionContext *context); // actually returns a jmp_buf * void *__qmljs_create_exception_handler(ExecutionContext *context); @@ -443,34 +446,6 @@ inline Value __qmljs_default_value(Value value, ExecutionContext *ctx, int typeH } -// unary operators -inline Value __qmljs_typeof(Value value, ExecutionContext *ctx) -{ - switch (value.type()) { - case Value::Undefined_Type: - return __qmljs_string_literal_undefined(ctx); - break; - case Value::Null_Type: - return __qmljs_string_literal_object(ctx); - break; - case Value::Boolean_Type: - return __qmljs_string_literal_boolean(ctx); - break; - case Value::String_Type: - return __qmljs_string_literal_string(ctx); - break; - case Value::Object_Type: - if (__qmljs_is_callable(value, ctx)) - return __qmljs_string_literal_function(ctx); - else - return __qmljs_string_literal_object(ctx); // ### implementation-defined - break; - default: - return __qmljs_string_literal_number(ctx); - break; - } -} - inline Value __qmljs_uplus(Value value, ExecutionContext *ctx) { TRACE1(value); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 8b3361b3bb..8159ede389 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1384,7 +1384,7 @@ bool Codegen::visit(TypeOfExpression *ast) { Result expr = expression(ast->expression); IR::ExprList *args = _function->New(); - args->init(argument(*expr)); + args->init(*expr); _expr.code = call(_block->NAME(IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args); return false; } diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 2fa506d022..a754bee666 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -259,9 +259,21 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu callRuntimeMethod(result, __qmljs_call_activation_property, call->base, call->args); break; case IR::Name::builtin_typeof: { - IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - generateFunctionCall(result, __qmljs_builtin_typeof, arg, ContextRegister); + if (IR::Member *m = call->args->expr->asMember()) { + generateFunctionCall(result, __qmljs_builtin_typeof_member, m->base->asTemp(), identifier(*m->name), ContextRegister); + return; + } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { + generateFunctionCall(result, __qmljs_builtin_typeof_element, ss->base->asTemp(), ss->index->asTemp(), ContextRegister); + return; + } else if (IR::Name *n = call->args->expr->asName()) { + generateFunctionCall(result, __qmljs_builtin_typeof_name, identifier(*n->id), ContextRegister); + return; + } else if (IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + generateFunctionCall(result, __qmljs_builtin_typeof, arg, ContextRegister); + } else { + assert(false); + } } break; case IR::Name::builtin_delete: { @@ -272,7 +284,7 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu generateFunctionCall(result, __qmljs_delete_subscript, ContextRegister, ss->base->asTemp(), ss->index->asTemp()); return; } else if (IR::Name *n = call->args->expr->asName()) { - generateFunctionCall(result, __qmljs_delete_name, ContextRegister, n); + generateFunctionCall(result, __qmljs_delete_name, ContextRegister, identifier(*n->id)); return; } else if (call->args->expr->asTemp()){ // ### should throw in strict mode -- cgit v1.2.3 From 932d4925fa89e02f7f4e7047f971889009ec6890 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 8 Dec 2012 07:32:04 +0100 Subject: Fix shadow builds Make it clear that the generated itab.c file ends up in the OUT_PWD, so that for dependency tracking we can assume to find it there instead of in the source dir. Change-Id: I8c6435e5ba203819cde485cc1291037af58da000 Reviewed-by: Lars Knoll --- masm/masm.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/masm/masm.pri b/masm/masm.pri index a252291e30..90f30b964e 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -45,7 +45,7 @@ udis86.CONFIG += no_link udis86.commands = python $$PWD/disassembler/udis86/itab.py ${QMAKE_FILE_IN} QMAKE_EXTRA_COMPILERS += udis86 -udis86_tab_cfile.target = udis86_itab.c +udis86_tab_cfile.target = $$OUT_PWD/udis86_itab.c udis86_tab_cfile.depends = udis86_itab.h QMAKE_EXTRA_TARGETS += udis86_tab_cfile -- cgit v1.2.3 From 6287991abb09441e6990b1e0b05f393ce2e3d08c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 8 Dec 2012 05:18:22 +0100 Subject: Fix new/free mismatch Earlier patch replaced new with malloc, so when free'ing we also have to use free() instead of delete. Change-Id: I353494d88d4de91eb115b7c3dc41fcc556ff6aeb Reviewed-by: Lars Knoll --- qv4mm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4mm.cpp b/qv4mm.cpp index 8fe3815110..60c61808c7 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -77,7 +77,7 @@ struct MemoryManager::Data ~Data() { for (QLinkedList >::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) - delete[] i->first; + free(i->first); } }; -- cgit v1.2.3 From 759d70e8a4ef5d35385b3dbc6aed471f3480ea0e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 7 Dec 2012 22:44:51 -0800 Subject: Keep references while moving other expressions into a temp delete and typeof need to get correct references as arguments. But we still need to evaluate other expressions correctly. The best way to do that is to store them in a temp. Change-Id: I7bcd152742bf752df47fd63a837952c57ea70bf5 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 15 +++++++++++++-- qv4codegen_p.h | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 8159ede389..95555238da 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -490,6 +490,17 @@ IR::Expr *Codegen::argument(IR::Expr *expr) return expr; } +// keeps references alive, converts other expressions to temps +IR::Expr *Codegen::reference(IR::Expr *expr) +{ + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember() && !expr->asSubscript()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + return expr; +} + IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr) { if (IR::Const *c = expr->asConst()) { @@ -1159,7 +1170,7 @@ bool Codegen::visit(DeleteExpression *ast) { Result expr = expression(ast->expression); IR::ExprList *args = _function->New(); - args->init(*expr); + args->init(reference(*expr)); _expr.code = call(_block->NAME(IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args); return false; } @@ -1384,7 +1395,7 @@ bool Codegen::visit(TypeOfExpression *ast) { Result expr = expression(ast->expression); IR::ExprList *args = _function->New(); - args->init(*expr); + args->init(reference(*expr)); _expr.code = call(_block->NAME(IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args); return false; } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 474e5580e8..ba3b477e1b 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -201,6 +201,7 @@ protected: IR::Expr *member(IR::Expr *base, const QString *name); IR::Expr *subscript(IR::Expr *base, IR::Expr *index); IR::Expr *argument(IR::Expr *expr); + IR::Expr *reference(IR::Expr *expr); IR::Expr *unop(IR::AluOp op, IR::Expr *expr); IR::Expr *binop(IR::AluOp op, IR::Expr *left, IR::Expr *right); IR::Expr *call(IR::Expr *base, IR::ExprList *args); -- cgit v1.2.3 From 13ea451c4fad5925377f51fca1a8bdb35c4bc7f8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 7 Dec 2012 23:57:35 -0800 Subject: Implement Object.create/defineProperty/defineProperties Change-Id: I3a71597d012b5fb7d7a2f482f4a16431c71c1c22 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 6 ++++ qmljs_engine.h | 6 ++++ qmljs_runtime.cpp | 5 +-- qmljs_runtime.h | 2 +- qv4ecmaobjects.cpp | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++--- qv4ecmaobjects_p.h | 2 ++ 6 files changed, 107 insertions(+), 7 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index ea6b32a079..b0a964ff90 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -85,6 +85,12 @@ ExecutionEngine::ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory * id_constructor = identifier(QStringLiteral("constructor")); id_arguments = identifier(QStringLiteral("arguments")); id___proto__ = identifier(QStringLiteral("__proto__")); + id_enumerable = identifier(QStringLiteral("enumerable")); + id_configurable = identifier(QStringLiteral("configurable")); + id_writable = identifier(QStringLiteral("writable")); + id_value = identifier(QStringLiteral("value")); + id_get = identifier(QStringLiteral("get")); + id_set = identifier(QStringLiteral("set")); objectPrototype = new (memoryManager) ObjectPrototype(); stringPrototype = new (memoryManager) StringPrototype(rootContext); diff --git a/qmljs_engine.h b/qmljs_engine.h index 520acab675..85a3909599 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -136,6 +136,12 @@ struct ExecutionEngine String *id_constructor; String *id_arguments; String *id___proto__; + String *id_enumerable; + String *id_configurable; + String *id_writable; + String *id_value; + String *id_get; + String *id_set; struct ExceptionHandler { ExecutionContext *context; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index aaa5be9ef6..53f313f61e 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -696,8 +696,9 @@ Value __qmljs_call_activation_property(ExecutionContext *context, String *name, return o->call(context, Value::undefinedValue(), args, argc); } -Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String *name, Value *args, int argc) +Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, Value *args, int argc) { + Value thisObject = that; if (!thisObject.isObject()) thisObject = __qmljs_to_object(thisObject, context); @@ -709,7 +710,7 @@ Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String if (!o) context->throwTypeError(); - return o->call(context, thisObject, args, argc); + return o->call(context, that, args, argc); } Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 6675920cb3..e8049d9192 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -91,7 +91,7 @@ extern "C" { // context Value __qmljs_call_activation_property(ExecutionContext *, String *name, Value *args, int argc); -Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String *name, Value *args, int argc); +Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, Value *args, int argc); Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc); Value __qmljs_construct_activation_property(ExecutionContext *, String *name, Value *args, int argc); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 5a29c64fc2..88acebf1d9 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -610,18 +610,46 @@ Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) Value ObjectPrototype::method_create(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.create")); - return Value::undefinedValue(); + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + Object *newObject = ctx->engine->newObject(); + newObject->prototype = O.objectValue(); + + Value objValue = Value::fromObject(newObject); + ctx->arguments[0] = objValue; + method_defineProperties(ctx); + + return objValue; } Value ObjectPrototype::method_defineProperty(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.defineProperty")); - return Value::undefinedValue(); + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + String *name = ctx->argument(1).toString(ctx); + + Value attributes = ctx->argument(2); + PropertyDescriptor pd; + toPropertyDescriptor(ctx, attributes, &pd); + + bool strict = ctx->strictMode; + ctx->strictMode = true; + O.objectValue()->__defineOwnProperty__(ctx, name, &pd); + ctx->strictMode = strict; + + return O; } Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) { + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + ctx->throwUnimplemented(QStringLiteral("Object.defineProperties")); return Value::undefinedValue(); } @@ -828,6 +856,63 @@ Value ObjectPrototype::method_defineSetter(ExecutionContext *ctx) return Value::undefinedValue(); } +void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc) +{ + if (!v.isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = v.objectValue(); + + desc->type = PropertyDescriptor::Generic; + + desc->enumberable = PropertyDescriptor::Undefined; + if (o->__hasProperty__(ctx, ctx->engine->id_enumerable)) + desc->enumberable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_enumerable), ctx) ? PropertyDescriptor::Set : PropertyDescriptor::Unset; + + desc->configurable = PropertyDescriptor::Undefined; + if (o->__hasProperty__(ctx, ctx->engine->id_configurable)) + desc->enumberable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_configurable), ctx) ? PropertyDescriptor::Set : PropertyDescriptor::Unset; + + desc->writable = PropertyDescriptor::Undefined; + if (o->__hasProperty__(ctx, ctx->engine->id_writable)) + desc->enumberable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_writable), ctx) ? PropertyDescriptor::Set : PropertyDescriptor::Unset; + + if (o->__hasProperty__(ctx, ctx->engine->id_value)) { + desc->value = o->__get__(ctx, ctx->engine->id_value); + desc->type = PropertyDescriptor::Data; + } + + if (o->__hasProperty__(ctx, ctx->engine->id_get)) { + Value get = o->__get__(ctx, ctx->engine->id_get); + FunctionObject *f = get.asFunctionObject(); + if (f) { + if (desc->isWritable() || desc->isData()) + __qmljs_throw_type_error(ctx); + desc->get = f; + } else if (!get.isUndefined()) { + __qmljs_throw_type_error(ctx); + } else { + desc->get = 0; + } + desc->type = PropertyDescriptor::Accessor; + } + + if (o->__hasProperty__(ctx, ctx->engine->id_set)) { + Value get = o->__get__(ctx, ctx->engine->id_set); + FunctionObject *f = get.asFunctionObject(); + if (f) { + if (desc->isWritable() || desc->isData()) + __qmljs_throw_type_error(ctx); + desc->set = f; + } else if (!get.isUndefined()) { + __qmljs_throw_type_error(ctx); + } else { + desc->set = 0; + } + desc->type = PropertyDescriptor::Accessor; + } +} + // // String // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 02d7d8cf19..580363310d 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -83,6 +83,8 @@ struct ObjectPrototype: Object static Value method_defineGetter(ExecutionContext *ctx); static Value method_defineSetter(ExecutionContext *ctx); + + static void toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc); }; struct StringCtor: FunctionObject -- cgit v1.2.3 From 5b870e94e855b0f3acdecf08b5467de5f683cf1e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 8 Dec 2012 07:01:23 -0800 Subject: Create a temp when a new expression is called with e.g. a closure Don't crash on e.g. var x = new (function() { return this; }) Change-Id: I120410b40ecda7fdc41e1dcc2c17251397143bc1 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 95555238da..eefd3a38cb 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1239,7 +1239,13 @@ bool Codegen::visit(NestedExpression *ast) bool Codegen::visit(NewExpression *ast) { Result base = expression(ast->expression); - _expr.code = _block->NEW(*base, 0); + IR::Expr *expr = *base; + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + _expr.code = _block->NEW(expr, 0); return false; } -- cgit v1.2.3 From 7d6a6358f80d7dfe3c8428209f5cd6c6990f73a2 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 4 Dec 2012 10:54:32 +0100 Subject: Fix linker warning. When compiling LLVM with GCC or Clang, the visibility for inline methods is set to hidden. When linking code compiled without this flag to LLVM, the system linker will produce warnings like: ld: warning: direct access in llvm::fdbgs() to global weak symbol llvm::formatted_raw_ostream::~formatted_raw_ostream() means the weak symbol cannot be overridden at runtime. This was likely caused by different translation units being compiled with different visibility settings. Change-Id: Ia86ff44660d448c8b0673e229137855e56c895f5 Reviewed-by: Simon Hausmann --- v4.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v4.pro b/v4.pro index ce1409b303..3aecdabe27 100644 --- a/v4.pro +++ b/v4.pro @@ -57,7 +57,7 @@ HEADERS += \ INCLUDEPATH += \ $$system($$LLVM_CONFIG --includedir) -QMAKE_CXXFLAGS += $$system($$LLVM_CONFIG --cppflags) +QMAKE_CXXFLAGS += $$system($$LLVM_CONFIG --cppflags) -fvisibility-inlines-hidden LIBS += \ $$system($$LLVM_CONFIG --ldflags) \ -- cgit v1.2.3 From 49108716ea5b751cacc2492da81d5556c7b00ecc Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 10 Dec 2012 09:56:30 +0100 Subject: Fix compilation on posix-compliant systems. memalign is deprecated and linux-only, and malloc.h does not exist on non-glibc systems. Change-Id: I44942378b7514c2a3fb6cb5f60256bbcc8ffe370 Reviewed-by: Simon Hausmann --- qv4mm.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qv4mm.cpp b/qv4mm.cpp index 60c61808c7..fe72551b6d 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -37,7 +37,7 @@ #include #include -#include +#include using namespace QQmlJS::VM; @@ -127,7 +127,8 @@ MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) return alloc(size - sizeof(MMInfo)); std::size_t allocSize = std::max(size, CHUNK_SIZE); - char *ptr = (char*)memalign(16, allocSize); + char *ptr = 0; + posix_memalign(reinterpret_cast(&ptr), 16, allocSize); m_d->heapChunks.append(qMakePair(ptr, allocSize)); // qDebug("Allocated new chunk of %lu bytes @ %p", allocSize, ptr); -- cgit v1.2.3 From da3c085626d965acd5ff52c304497759be86740a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 8 Dec 2012 18:22:25 +0100 Subject: Add initial version of stack walker Not used yet Change-Id: Id096f7efd5582d2a20c3b921180be0ebf48996a5 Reviewed-by: Lars Knoll --- masm/masm.pri | 3 + masm/wtf/StackBounds.cpp | 261 +++++++++++++++++++++++++++++++++++++++++++++++ masm/wtf/StackBounds.h | 129 +++++++++++++++++++++++ qv4mm.cpp | 18 ++++ qv4mm.h | 13 +++ 5 files changed, 424 insertions(+) create mode 100644 masm/wtf/StackBounds.cpp create mode 100644 masm/wtf/StackBounds.h diff --git a/masm/masm.pri b/masm/masm.pri index 90f30b964e..642926d620 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -6,6 +6,9 @@ SOURCES += $$PWD/assembler/MacroAssemblerARM.cpp SOURCES += $$PWD/assembler/MacroAssemblerSH4.cpp SOURCES += $$PWD/assembler/LinkBuffer.cpp +SOURCES += $$PWD/wtf/StackBounds.cpp +HEADERS += $$PWD/wtf/StackBounds.h + SOURCES += $$PWD/stubs/WTFStubs.cpp HEADERS += $$PWD/stubs/WTFStubs.h diff --git a/masm/wtf/StackBounds.cpp b/masm/wtf/StackBounds.cpp new file mode 100644 index 0000000000..a272ce3de9 --- /dev/null +++ b/masm/wtf/StackBounds.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "StackBounds.h" + +#if OS(DARWIN) + +#include +#include +#include + +#elif OS(WINDOWS) + +#include + +#elif OS(SOLARIS) + +#include + +#elif OS(QNX) + +#include +#include +#include +#include +#include +#include + +#elif OS(UNIX) + +#include +#if HAVE(PTHREAD_NP_H) +#include +#endif + +#endif + +namespace WTF { + +// Bug 26276 - Need a mechanism to determine stack extent +// +// These platforms should now be working correctly: +// DARWIN, QNX, UNIX +// These platforms are not: +// WINDOWS, SOLARIS, OPENBSD, WINCE +// +// FIXME: remove this! - this code unsafely guesses at stack sizes! +#if OS(WINDOWS) || OS(SOLARIS) || OS(OPENBSD) +// Based on the current limit used by the JSC parser, guess the stack size. +static const ptrdiff_t estimatedStackSize = 128 * sizeof(void*) * 1024; +// This method assumes the stack is growing downwards. +static void* estimateStackBound(void* origin) +{ + return static_cast(origin) - estimatedStackSize; +} +#endif + +#if OS(DARWIN) + +void StackBounds::initialize() +{ + pthread_t thread = pthread_self(); + m_origin = pthread_get_stackaddr_np(thread); + m_bound = static_cast(m_origin) - pthread_get_stacksize_np(thread); +} + +#elif OS(QNX) + +void StackBounds::initialize() +{ + void* stackBase = 0; + size_t stackSize = 0; + + struct _debug_thread_info threadInfo; + memset(&threadInfo, 0, sizeof(threadInfo)); + threadInfo.tid = pthread_self(); + int fd = open("/proc/self", O_RDONLY); + if (fd == -1) { + LOG_ERROR("Unable to open /proc/self (errno: %d)", errno); + CRASH(); + } + devctl(fd, DCMD_PROC_TIDSTATUS, &threadInfo, sizeof(threadInfo), 0); + close(fd); + stackBase = reinterpret_cast(threadInfo.stkbase); + stackSize = threadInfo.stksize; + ASSERT(stackBase); + + m_bound = static_cast(stackBase) + 0x1000; // 4kb guard page + m_origin = static_cast(stackBase) + stackSize; +} + +#elif OS(SOLARIS) + +void StackBounds::initialize() +{ + stack_t s; + thr_stksegment(&s); + m_origin = s.ss_sp; + m_bound = estimateStackBound(m_origin); +} + +#elif OS(OPENBSD) + +void StackBounds::initialize() +{ + pthread_t thread = pthread_self(); + stack_t stack; + pthread_stackseg_np(thread, &stack); + m_origin = stack.ss_sp; + m_bound = estimateStackBound(m_origin); +} + +#elif OS(UNIX) + +void StackBounds::initialize() +{ + void* stackBase = 0; + size_t stackSize = 0; + + pthread_t thread = pthread_self(); + pthread_attr_t sattr; + pthread_attr_init(&sattr); +#if HAVE(PTHREAD_NP_H) || OS(NETBSD) + // e.g. on FreeBSD 5.4, neundorf@kde.org + pthread_attr_get_np(thread, &sattr); +#else + // FIXME: this function is non-portable; other POSIX systems may have different np alternatives + pthread_getattr_np(thread, &sattr); +#endif + int rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize); + (void)rc; // FIXME: Deal with error code somehow? Seems fatal. + ASSERT(stackBase); + pthread_attr_destroy(&sattr); + m_bound = stackBase; + m_origin = static_cast(stackBase) + stackSize; +} + +#elif OS(WINCE) + +static bool detectGrowingDownward(void* previousFrame) +{ + // Find the address of this stack frame by taking the address of a local variable. + int thisFrame; + return previousFrame > &thisFrame; +} + +static inline bool isPageWritable(void* page) +{ + MEMORY_BASIC_INFORMATION memoryInformation; + DWORD result = VirtualQuery(page, &memoryInformation, sizeof(memoryInformation)); + + // return false on error, including ptr outside memory + if (result != sizeof(memoryInformation)) + return false; + + DWORD protect = memoryInformation.Protect & ~(PAGE_GUARD | PAGE_NOCACHE); + return protect == PAGE_READWRITE + || protect == PAGE_WRITECOPY + || protect == PAGE_EXECUTE_READWRITE + || protect == PAGE_EXECUTE_WRITECOPY; +} + +static inline void* getLowerStackBound(char* currentPage, DWORD pageSize) +{ + while (currentPage > 0) { + // check for underflow + if (currentPage >= reinterpret_cast(pageSize)) + currentPage -= pageSize; + else + currentPage = 0; + + if (!isPageWritable(currentPage)) + return currentPage + pageSize; + } + + return 0; +} + +static inline void* getUpperStackBound(char* currentPage, DWORD pageSize) +{ + do { + // guaranteed to complete because isPageWritable returns false at end of memory + currentPage += pageSize; + } while (isPageWritable(currentPage)); + + return currentPage - pageSize; +} + +void StackBounds::initialize() +{ + // find the address of this stack frame by taking the address of a local variable + void* thisFrame = &thisFrame; + bool isGrowingDownward = detectGrowingDownward(thisFrame); + + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + DWORD pageSize = systemInfo.dwPageSize; + + // scan all of memory starting from this frame, and return the last writeable page found + char* currentPage = reinterpret_cast(reinterpret_cast(thisFrame) & ~(pageSize - 1)); + void* lowerStackBound = getLowerStackBound(currentPage, pageSize); + void* upperStackBound = getUpperStackBound(currentPage, pageSize); + + m_origin = isGrowingDownward ? upperStackBound : lowerStackBound; + m_bound = isGrowingDownward ? lowerStackBound : upperStackBound; +} + +#elif OS(WINDOWS) + +void StackBounds::initialize() +{ +#if CPU(X86) && COMPILER(MSVC) + // offset 0x18 from the FS segment register gives a pointer to + // the thread information block for the current thread + NT_TIB* pTib; + __asm { + MOV EAX, FS:[18h] + MOV pTib, EAX + } + m_origin = static_cast(pTib->StackBase); +#elif CPU(X86) && COMPILER(GCC) + // offset 0x18 from the FS segment register gives a pointer to + // the thread information block for the current thread + NT_TIB* pTib; + asm ( "movl %%fs:0x18, %0\n" + : "=r" (pTib) + ); + m_origin = static_cast(pTib->StackBase); +#elif CPU(X86_64) + PNT_TIB64 pTib = reinterpret_cast(NtCurrentTeb()); + m_origin = reinterpret_cast(pTib->StackBase); +#else +#error Need a way to get the stack bounds on this platform (Windows) +#endif + // Looks like we should be able to get pTib->StackLimit + m_bound = estimateStackBound(m_origin); +} + +#else +#error Need a way to get the stack bounds on this platform +#endif + +} // namespace WTF diff --git a/masm/wtf/StackBounds.h b/masm/wtf/StackBounds.h new file mode 100644 index 0000000000..185afec226 --- /dev/null +++ b/masm/wtf/StackBounds.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2010 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef StackBounds_h +#define StackBounds_h + +namespace WTF { + +class StackBounds { + // isSafeToRecurse() / recursionLimit() tests (by default) + // that we are at least this far from the end of the stack. + // + // This 64k number was picked because a sampling of stack usage differences + // between consecutive entries into one of the Interpreter::execute...() + // functions was seen to be as high as 27k. Hence, 64k is chosen as a + // conservative availability value that is not too large but comfortably + // exceeds 27k with some buffer for error. + const static size_t s_defaultAvailabilityDelta = 64 * 1024; + +public: + StackBounds() + : m_origin(0) + , m_bound(0) + { + } + + static StackBounds currentThreadStackBounds() + { + StackBounds bounds; + bounds.initialize(); + bounds.checkConsistency(); + return bounds; + } + + void* origin() const + { + ASSERT(m_origin); + return m_origin; + } + + void* current() const + { + checkConsistency(); + void* currentPosition = ¤tPosition; + return currentPosition; + } + + size_t size() const + { + return isGrowingDownward() + ? static_cast(m_origin) - static_cast(m_bound) + : static_cast(m_bound) - static_cast(m_origin); + } + + void* recursionLimit(size_t minAvailableDelta = s_defaultAvailabilityDelta) const + { + checkConsistency(); + return isGrowingDownward() + ? static_cast(m_bound) + minAvailableDelta + : static_cast(m_bound) - minAvailableDelta; + } + + bool isSafeToRecurse(size_t minAvailableDelta = s_defaultAvailabilityDelta) const + { + checkConsistency(); + return isGrowingDownward() + ? current() >= recursionLimit(minAvailableDelta) + : current() <= recursionLimit(minAvailableDelta); + } + +private: + void initialize(); + + + bool isGrowingDownward() const + { + ASSERT(m_origin && m_bound); +#if OS(WINCE) + return m_origin > m_bound; +#else + return true; +#endif + } + + void checkConsistency() const + { +#if !ASSERT_DISABLED + void* currentPosition = ¤tPosition; + ASSERT(m_origin != m_bound); + ASSERT(isGrowingDownward() + ? (currentPosition < m_origin && currentPosition > m_bound) + : (currentPosition > m_origin && currentPosition < m_bound)); +#endif + } + + void* m_origin; + void* m_bound; + + friend class StackStats; +}; + +} // namespace WTF + +using WTF::StackBounds; + +#endif diff --git a/qv4mm.cpp b/qv4mm.cpp index fe72551b6d..05fce092ad 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -31,6 +31,7 @@ #include "qmljs_objects.h" #include "qv4ecmaobjects_p.h" #include "qv4mm.h" +#include "StackBounds.h" #include #include @@ -375,3 +376,20 @@ void MemoryManagerWithoutGC::collectRootsOnStack(QVector &roots) c { Q_UNUSED(roots); } + +MemoryManagerWithNativeStack::~MemoryManagerWithNativeStack() +{ +} + +void MemoryManagerWithNativeStack::collectRootsOnStack(QVector &roots) const +{ + StackBounds bounds = StackBounds::currentThreadStackBounds(); + Value* top = reinterpret_cast(bounds.origin()); + Value* current = reinterpret_cast(bounds.current()); + qDebug("Collecting on stack. top %p current %p\n", top, current); + for (; current < top; ++current) { + if (current->asObject()) + qDebug("found object %p on stack", (void*)current); + add(roots, *current); + } +} diff --git a/qv4mm.h b/qv4mm.h index 85395b937b..5bf007799c 100644 --- a/qv4mm.h +++ b/qv4mm.h @@ -175,6 +175,19 @@ protected: virtual void collectRootsOnStack(QVector &roots) const; }; +class MemoryManagerWithNativeStack: public MemoryManager +{ +public: + MemoryManagerWithNativeStack() + { setEnableGC(true); } + + virtual ~MemoryManagerWithNativeStack(); + +protected: + virtual void collectRootsOnStack(QVector &roots) const; +}; + + } // namespace VM } // namespace QQmlJS -- cgit v1.2.3 From f7408295611f61a7c607430e4ffa7e6a37f4c6c3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 8 Dec 2012 18:20:54 +0100 Subject: Add gc() function to triggering manual call to gc Similar to jsc Change-Id: I7c547ef10cb1620523465fe659cd3075dd27a456 Reviewed-by: Lars Knoll --- main.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/main.cpp b/main.cpp index decb799ce6..919cca76d3 100644 --- a/main.cpp +++ b/main.cpp @@ -110,6 +110,20 @@ struct TestHarnessError: FunctionObject bool &errorOccurred; }; +struct GC: public FunctionObject +{ + GC(ExecutionContext* scope) + : FunctionObject(scope) + { + name = scope->engine->newString("gc"); + } + virtual Value call(ExecutionContext *ctx) + { + ctx->engine->memoryManager->runGC(); + return Value::undefinedValue(); + } +}; + } // builtins static void showException(QQmlJS::VM::ExecutionContext *ctx) @@ -341,6 +355,8 @@ int main(int argc, char *argv[]) QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::Print(ctx))); + globalObject->__put__(ctx, vm.identifier(QStringLiteral("gc")), + QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::GC(ctx))); bool errorInTestHarness = false; if (!qgetenv("IN_TEST_HARNESS").isEmpty()) -- cgit v1.2.3 From df458859f443c6c559c39c667952bcb85f277e9a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 9 Dec 2012 05:16:04 +0100 Subject: Return the this object if we ask for it This makes expressions such as typeof(this) work correctly. Change-Id: I44270f877fdee648e69ae44089ffc8fb57243401 Reviewed-by: Erik Verbruggen --- qmljs_engine.cpp | 1 + qmljs_engine.h | 1 + qmljs_environment.cpp | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index b0a964ff90..a694d3ecf0 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -84,6 +84,7 @@ ExecutionEngine::ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory * id_prototype = identifier(QStringLiteral("prototype")); id_constructor = identifier(QStringLiteral("constructor")); id_arguments = identifier(QStringLiteral("arguments")); + id_this = identifier(QStringLiteral("this")); id___proto__ = identifier(QStringLiteral("__proto__")); id_enumerable = identifier(QStringLiteral("enumerable")); id_configurable = identifier(QStringLiteral("configurable")); diff --git a/qmljs_engine.h b/qmljs_engine.h index 85a3909599..1b4e593200 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -135,6 +135,7 @@ struct ExecutionEngine String *id_prototype; String *id_constructor; String *id_arguments; + String *id_this; String *id___proto__; String *id_enumerable; String *id_configurable; diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index a64d9dccb6..4f2f7981cd 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -267,6 +267,9 @@ void ExecutionContext::setProperty(String *name, Value value) Value ExecutionContext::getProperty(String *name) { + if (name == engine->id_this) + return thisObject; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { if (ctx->withObject) { With *w = ctx->withObject; @@ -298,6 +301,9 @@ Value ExecutionContext::getProperty(String *name) Value ExecutionContext::getPropertyNoThrow(String *name) { + if (name == engine->id_this) + return thisObject; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { if (ctx->withObject) { With *w = ctx->withObject; -- cgit v1.2.3 From 9c104bb97098f690e4c8af6e17b7217891a8c690 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 10 Dec 2012 19:30:08 +0100 Subject: Properly set up the 'this' pointer Make sure the this pointer is setup correctly for function calls. Also make sure we set the strict mode flag correctly in all functions. Change-Id: Idaacc92bf6469145b7addfac2bbddea588e85c2d Reviewed-by: Erik Verbruggen --- qmljs_engine.cpp | 1 + qmljs_environment.cpp | 11 +++++++++-- qmljs_objects.cpp | 8 +------- qmljs_runtime.cpp | 4 +--- qv4codegen.cpp | 3 ++- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index a694d3ecf0..4da9564939 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -177,6 +177,7 @@ ExecutionEngine::ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory * VM::Object *glo = newObject(/*rootContext*/); globalObject = Value::fromObject(glo); rootContext->activation = glo; + rootContext->thisObject = Value::fromObject(glo); glo->__put__(rootContext, identifier(QStringLiteral("Object")), objectCtor); glo->__put__(rootContext, identifier(QStringLiteral("String")), stringCtor); diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 4f2f7981cd..9f900f9e94 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -210,7 +210,7 @@ void ExecutionContext::init(ExecutionEngine *eng) { engine = eng; parent = 0; - thisObject = Value::nullValue(); + thisObject = eng->globalObject; function = 0; arguments = 0; @@ -385,11 +385,18 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha engine = parent->engine; this->parent = parent; - thisObject = that; function = f; strictMode = f->strictMode; + thisObject = that; + if (!strictMode && !thisObject.isObject()) { + if (thisObject.isUndefined() || thisObject.isNull()) + thisObject = engine->globalObject; + else + thisObject = thisObject.toObject(this); + } + arguments = args; argumentCount = argc; if (function->needsActivation || argc < function->formalParameterCount){ diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index d74a501a7e..6e0af63109 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -450,7 +450,7 @@ Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc { ExecutionContext k; ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; - ctx->initCallContext(context, Value::nullValue(), this, args, argc); + ctx->initCallContext(context, Value::undefinedValue(), this, args, argc); Value result = construct(ctx); ctx->wireUpPrototype(); ctx->leaveCallContext(); @@ -464,12 +464,6 @@ Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *a ExecutionContext k; ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; - if (!strictMode && !thisObject.isObject()) { - if (thisObject.isUndefined() || thisObject.isNull()) - thisObject = context->engine->globalObject; - else - thisObject = __qmljs_to_object(thisObject, context); - } ctx->initCallContext(context, thisObject, this, args, argc); Value result = call(ctx); ctx->leaveCallContext(); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 53f313f61e..62e7be4f1e 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -628,9 +628,7 @@ Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name) Value __qmljs_get_thisObject(ExecutionContext *ctx) { - if (ctx->thisObject.isObject()) - return ctx->thisObject; - return ctx->engine->globalObject; + return ctx->thisObject; } uint __qmljs_equal(Value x, Value y, ExecutionContext *ctx) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index eefd3a38cb..d9ac31d91e 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -207,7 +207,8 @@ protected: inline void enterEnvironment(Node *node) { Environment *e = _cg->newEnvironment(node, _env); - e->isStrict = _cg->_context->strictMode; + if (!e->isStrict) + e->isStrict = _cg->_context->strictMode; _envStack.append(e); _env = e; } -- cgit v1.2.3 From b268bd9dbaf4e33a73a175cfddb517c51c0a2ed6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 11 Dec 2012 00:41:35 +0100 Subject: Use QString::toDouble to convert to numbers QString::toDouble() is always using the C locale in Qt 5, strtod_l seems to have some stability issues for me, and creating a locale on the stack doesn't sound very performant. Change-Id: I35705a125b0c5913a5390ed1429c4e7490300f92 Reviewed-by: Erik Verbruggen --- qmljs_runtime.cpp | 16 ++++++++++++---- v4.pro | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 62e7be4f1e..1e664d2b09 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -44,6 +44,7 @@ #include "qmljs_objects.h" #include "qv4ir_p.h" #include "qv4ecmaobjects_p.h" +#include "private/qlocale_tools_p.h" #include #include @@ -52,7 +53,6 @@ #include #include #include -#include namespace QQmlJS { namespace VM { @@ -436,9 +436,17 @@ double __qmljs_string_to_number(ExecutionContext *, String *string) const QString s = string->toQString(); if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) return s.toLong(0, 16); - locale_t c_locale = newlocale(LC_ALL_MASK, NULL, NULL); - double d = ::strtod_l(s.toUtf8().constData(), 0, c_locale); - freelocale(c_locale); + bool ok; + QByteArray ba = s.toLatin1(); + const char *begin = ba.constData(); + const char *end = 0; + double d = qstrtod(begin, &end, &ok); + if (end - begin != ba.size() - 1) { + if (ba == "Infinity") + d = INFINITY; + else + d = nan(""); + } return d; } diff --git a/v4.pro b/v4.pro index 3aecdabe27..1f1604b10f 100644 --- a/v4.pro +++ b/v4.pro @@ -1,4 +1,4 @@ -QT = core qmldevtools-private +QT = core-private qmldevtools-private CONFIG -= app_bundle CONFIG += console -- cgit v1.2.3 From 430618712b4d12067fa4e76fb2038edbce693df2 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 11 Dec 2012 19:34:48 +0100 Subject: Fix infinite look when constructing a Function with no arguments Change-Id: Ic64d8f68678df3a6d71b0681ce32ad8f2a8802fe Reviewed-by: Erik Verbruggen --- qv4ecmaobjects.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 88acebf1d9..b7d2e88412 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1950,13 +1950,14 @@ Value FunctionCtor::construct(ExecutionContext *ctx) QString args; QString body; - if (ctx->argumentCount > 0) + if (ctx->argumentCount > 0) { body = ctx->argument(ctx->argumentCount - 1).toString(ctx)->toQString(); - for (uint i = 0; i < ctx->argumentCount - 1; ++i) { - if (i) - args += QLatin1String(", "); - args += ctx->argument(i).toString(ctx)->toQString(); + for (uint i = 0; i < ctx->argumentCount - 1; ++i) { + if (i) + args += QLatin1String(", "); + args += ctx->argument(i).toString(ctx)->toQString(); + } } QString function = QLatin1String("function(") + args + QLatin1String("){") + body + QLatin1String("}"); -- cgit v1.2.3 From 7dba11b7031c4be9338d98e38c80f0573acc47fd Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 11 Dec 2012 20:27:17 +0100 Subject: Implement Object.defineProperties and fix Object.create Change-Id: I5a55b8d9b3c8e34018defcbe8ee97bde43a714c6 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index b7d2e88412..64c56bbcbf 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -618,8 +618,10 @@ Value ObjectPrototype::method_create(ExecutionContext *ctx) newObject->prototype = O.objectValue(); Value objValue = Value::fromObject(newObject); - ctx->arguments[0] = objValue; - method_defineProperties(ctx); + if (ctx->argumentCount > 1) { + ctx->arguments[0] = objValue; + method_defineProperties(ctx); + } return objValue; } @@ -650,8 +652,22 @@ Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) if (!O.isObject()) ctx->throwTypeError(); - ctx->throwUnimplemented(QStringLiteral("Object.defineProperties")); - return Value::undefinedValue(); + Object *o = ctx->argument(1).toObject(ctx).objectValue(); + + if (o->members) { + PropertyTable::iterator it = o->members->begin(); + while (it != o->members->end()) { + if ((*it)->descriptor.isEnumerable()) { + String *name = (*it)->name; + PropertyDescriptor pd; + toPropertyDescriptor(ctx, o->__get__(ctx, name), &pd); + O.objectValue()->__defineOwnProperty__(ctx, name, &pd); + } + ++it; + } + } + + return O; } Value ObjectPrototype::method_seal(ExecutionContext *ctx) -- cgit v1.2.3 From d72158d6c584e0a3df92f6131cd26ed930c6fc4a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 11 Dec 2012 20:45:19 +0100 Subject: Implement Object.getOwnPropertyDescriptor Change-Id: I800d3ebd93e41c7b0618e13ce8141d230b1bd58e Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++-- qv4ecmaobjects_p.h | 1 + 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 64c56bbcbf..79322571dd 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -586,8 +586,13 @@ Value ObjectPrototype::method_getPrototypeOf(ExecutionContext *ctx) Value ObjectPrototype::method_getOwnPropertyDescriptor(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Object.getOwnPropertyDescriptors")); - return Value::undefinedValue(); + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + String *name = ctx->argument(1).toString(ctx); + PropertyDescriptor *desc = O.objectValue()->__getOwnProperty__(ctx, name); + return fromPropertyDescriptor(ctx, desc); } Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) @@ -929,6 +934,41 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Prope } } + +Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc) +{ + if (!desc) + return Value::undefinedValue(); + + ExecutionEngine *engine = ctx->engine; +// Let obj be the result of creating a new object as if by the expression new Object() where Object is the standard built-in constructor with that name. + Object *o = engine->newObject(); + + PropertyDescriptor pd; + pd.type = PropertyDescriptor::Data; + pd.writable = PropertyDescriptor::Set; + pd.enumberable = PropertyDescriptor::Set; + pd.configurable = PropertyDescriptor::Set; + + if (desc->isData()) { + pd.value = desc->value; + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("value")), &pd); + pd.value = Value::fromBoolean(desc->writable == PropertyDescriptor::Set ? true : false); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("writable")), &pd); + } else { + pd.value = desc->get ? Value::fromObject(desc->get) : Value::undefinedValue(); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("get")), &pd); + pd.value = desc->set ? Value::fromObject(desc->set) : Value::undefinedValue(); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("set")), &pd); + } + pd.value = Value::fromBoolean(desc->enumberable == PropertyDescriptor::Set ? true : false); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("enumerable")), &pd); + pd.value = Value::fromBoolean(desc->configurable == PropertyDescriptor::Set ? true : false); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("configurable")), &pd); + + return Value::fromObject(o); +} + // // String // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 580363310d..e6ab1d7f6d 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -85,6 +85,7 @@ struct ObjectPrototype: Object static Value method_defineSetter(ExecutionContext *ctx); static void toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc); + static Value fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc); }; struct StringCtor: FunctionObject -- cgit v1.2.3 From 381ce0e902254f39b411f3b21548943720e3db0c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 11 Dec 2012 23:58:40 +0100 Subject: Make the Math constants constant Mark the Math.pi etc. constants as readonly. Change-Id: I9224400ae48c7f21fc3b0478898c7c78aa7f45df Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 6 ++++++ qmljs_objects.h | 1 + qv4ecmaobjects.cpp | 30 ++++++++++++++++++++++-------- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 6e0af63109..c3aafae56b 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -359,6 +359,12 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property return false; } +bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, PropertyDescriptor *desc) +{ + return __defineOwnProperty__(ctx, ctx->engine->identifier(name), desc); +} + + Value Object::call(ExecutionContext *context, Value , Value *, int) { context->throwTypeError(); diff --git a/qmljs_objects.h b/qmljs_objects.h index 9daa84132b..51127f6268 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -452,6 +452,7 @@ struct Object: Managed { virtual bool __hasProperty__(const ExecutionContext *ctx, String *name) const; virtual bool __delete__(ExecutionContext *ctx, String *name); virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, PropertyDescriptor *desc); virtual Value call(ExecutionContext *context, Value, Value *, int); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 79322571dd..c9b9593adc 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2954,14 +2954,28 @@ Value ErrorPrototype::method_toString(ExecutionContext *ctx) // MathObject::MathObject(ExecutionContext *ctx) { - __put__(ctx, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); - __put__(ctx, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); - __put__(ctx, QStringLiteral("LN10"), Value::fromDouble(::log(10.0))); - __put__(ctx, QStringLiteral("LOG2E"), Value::fromDouble(1.0/::log(2.0))); - __put__(ctx, QStringLiteral("LOG10E"), Value::fromDouble(1.0/::log(10.0))); - __put__(ctx, QStringLiteral("PI"), Value::fromDouble(qt_PI)); - __put__(ctx, QStringLiteral("SQRT1_2"), Value::fromDouble(::sqrt(0.5))); - __put__(ctx, QStringLiteral("SQRT2"), Value::fromDouble(::sqrt(2.0))); + PropertyDescriptor desc; + desc.type = PropertyDescriptor::Data; + desc.writable = PropertyDescriptor::Unset; + desc.enumberable = PropertyDescriptor::Unset; + desc.configurable = PropertyDescriptor::Unset; + + desc.value = Value::fromDouble(::exp(1.0)); + __defineOwnProperty__(ctx, QStringLiteral("E"), &desc); + desc.value = Value::fromDouble(::log(2.0)); + __defineOwnProperty__(ctx, QStringLiteral("LN2"), &desc); + desc.value = Value::fromDouble(::log(10.0)); + __defineOwnProperty__(ctx, QStringLiteral("LN10"), &desc); + desc.value = Value::fromDouble(1.0/::log(2.0)); + __defineOwnProperty__(ctx, QStringLiteral("LOG2E"), &desc); + desc.value = Value::fromDouble(1.0/::log(10.0)); + __defineOwnProperty__(ctx, QStringLiteral("LOG10E"), &desc); + desc.value = Value::fromDouble(qt_PI); + __defineOwnProperty__(ctx, QStringLiteral("PI"), &desc); + desc.value = Value::fromDouble(::sqrt(0.5)); + __defineOwnProperty__(ctx, QStringLiteral("SQRT1_2"), &desc); + desc.value = Value::fromDouble(::sqrt(2.0)); + __defineOwnProperty__(ctx, QStringLiteral("SQRT2"), &desc); __put__(ctx, QStringLiteral("abs"), method_abs, 1); __put__(ctx, QStringLiteral("acos"), method_acos, 1); -- cgit v1.2.3 From bcdddfda8ca81752b249540b0abaefb46eb5f766 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 11 Dec 2012 11:17:55 +0100 Subject: Make MASM InstructionSelection reentrant The isel is currently not reentrant because the MacroAssembler's instruction output buffer is inaccessible and can only be reset by destroying the instance and re-creating it. This patch moves assembler specific code into an Assembler subclass and changes isel to instantiate and use it instead of subclassing JSC::MacroAssembler. Change-Id: Ic633214c67f475195202459698077e47a75ece2f Reviewed-by: Erik Verbruggen --- qv4isel_masm.cpp | 577 +++++++++++++++++++++++++++++++------------------------ qv4isel_masm_p.h | 297 +++++++++++++--------------- 2 files changed, 463 insertions(+), 411 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index a754bee666..bcd7585c64 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -62,6 +62,109 @@ namespace { QTextStream qout(stderr, QIODevice::WriteOnly); } +Assembler::Assembler(IR::Function* function) + : _function(function) +{ +} + +void Assembler::registerBlock(IR::BasicBlock* block) +{ + _addrs[block] = label(); +} + +void Assembler::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) +{ + if (current->index + 1 != target->index) + _patches[target].append(jump()); +} + +void Assembler::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) +{ + _patches[targetBlock].append(targetJump); +} + +Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, IR::Temp *t) +{ + int32_t offset = 0; + if (t->index < 0) { + const int arg = -t->index - 1; + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, arguments)), reg); + offset = arg * sizeof(Value); + } else if (t->index < _function->locals.size()) { + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, locals)), reg); + offset = t->index * sizeof(Value); + } else { + const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size(); + // StackFrameRegister points to its old value on the stack, so even for the first temp we need to + // subtract at least sizeof(Value). + offset = - sizeof(Value) * (arg + 1); + reg = StackFrameRegister; + } + return Pointer(reg, offset); +} + +template +void Assembler::copyValue(Result result, Source source) +{ +#ifdef VALUE_FITS_IN_REGISTER + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgument(source, ReturnValueRegister); + storeArgument(ReturnValueRegister, result); +#else + loadDouble(source, FPGpr0); + storeDouble(FPGpr0, result); +#endif +} + +void Assembler::enterStandardStackFrame(int locals) +{ +#if CPU(ARM) + push(JSC::ARMRegisters::lr); +#endif + push(StackFrameRegister); + move(StackPointerRegister, StackFrameRegister); + + // space for the locals and the ContextRegister + int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*); + +#if CPU(X86) || CPU(X86_64) + frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX +#endif + subPtr(TrustedImm32(frameSize), StackPointerRegister); + +#if CPU(X86) || CPU(ARM) + for (int saveReg = CalleeSavedFirstRegister; saveReg <= CalleeSavedLastRegister; ++saveReg) + push(static_cast(saveReg)); +#endif + // save the ContextRegister + storePtr(ContextRegister, StackPointerRegister); +} + +void Assembler::leaveStandardStackFrame(int locals) +{ + // restore the ContextRegister + loadPtr(StackPointerRegister, ContextRegister); + +#if CPU(X86) || CPU(ARM) + for (int saveReg = CalleeSavedLastRegister; saveReg >= CalleeSavedFirstRegister; --saveReg) + pop(static_cast(saveReg)); +#endif + // space for the locals and the ContextRegister + int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*); +#if CPU(X86) || CPU(X86_64) + frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX +#endif + addPtr(TrustedImm32(frameSize), StackPointerRegister); + + pop(StackFrameRegister); +#if CPU(ARM) + pop(JSC::ARMRegisters::lr); +#endif +} + + + #define OP(op) \ { isel_stringIfy(op), op, 0, 0 } @@ -71,7 +174,7 @@ QTextStream qout(stderr, QIODevice::WriteOnly); #define NULL_OP \ { 0, 0, 0, 0 } -const InstructionSelection::BinaryOperationInfo InstructionSelection::binaryOperations[QQmlJS::IR::LastAluOp + 1] = { +const Assembler::BinaryOperationInfo Assembler::binaryOperations[QQmlJS::IR::LastAluOp + 1] = { NULL_OP, // OpInvalid NULL_OP, // OpIfTrue NULL_OP, // OpNot @@ -79,20 +182,20 @@ const InstructionSelection::BinaryOperationInfo InstructionSelection::binaryOper NULL_OP, // OpUPlus NULL_OP, // OpCompl - INLINE_OP(__qmljs_bit_and, &InstructionSelection::inline_and32, &InstructionSelection::inline_and32), // OpBitAnd - INLINE_OP(__qmljs_bit_or, &InstructionSelection::inline_or32, &InstructionSelection::inline_or32), // OpBitOr - INLINE_OP(__qmljs_bit_xor, &InstructionSelection::inline_xor32, &InstructionSelection::inline_xor32), // OpBitXor + INLINE_OP(__qmljs_bit_and, &Assembler::inline_and32, &Assembler::inline_and32), // OpBitAnd + INLINE_OP(__qmljs_bit_or, &Assembler::inline_or32, &Assembler::inline_or32), // OpBitOr + INLINE_OP(__qmljs_bit_xor, &Assembler::inline_xor32, &Assembler::inline_xor32), // OpBitXor - INLINE_OP(__qmljs_add, &InstructionSelection::inline_add32, &InstructionSelection::inline_add32), // OpAdd - INLINE_OP(__qmljs_sub, &InstructionSelection::inline_sub32, &InstructionSelection::inline_sub32), // OpSub - INLINE_OP(__qmljs_mul, &InstructionSelection::inline_mul32, &InstructionSelection::inline_mul32), // OpMul + INLINE_OP(__qmljs_add, &Assembler::inline_add32, &Assembler::inline_add32), // OpAdd + INLINE_OP(__qmljs_sub, &Assembler::inline_sub32, &Assembler::inline_sub32), // OpSub + INLINE_OP(__qmljs_mul, &Assembler::inline_mul32, &Assembler::inline_mul32), // OpMul OP(__qmljs_div), // OpDiv OP(__qmljs_mod), // OpMod - INLINE_OP(__qmljs_shl, &InstructionSelection::inline_shl32, &InstructionSelection::inline_shl32), // OpLShift - INLINE_OP(__qmljs_shr, &InstructionSelection::inline_shr32, &InstructionSelection::inline_shr32), // OpRShift - INLINE_OP(__qmljs_ushr, &InstructionSelection::inline_ushr32, &InstructionSelection::inline_ushr32), // OpURShift + INLINE_OP(__qmljs_shl, &Assembler::inline_shl32, &Assembler::inline_shl32), // OpLShift + INLINE_OP(__qmljs_shr, &Assembler::inline_shr32, &Assembler::inline_shr32), // OpRShift + INLINE_OP(__qmljs_ushr, &Assembler::inline_ushr32, &Assembler::inline_ushr32), // OpURShift OP(__qmljs_gt), // OpGt OP(__qmljs_lt), // OpLt @@ -110,6 +213,92 @@ const InstructionSelection::BinaryOperationInfo InstructionSelection::binaryOper NULL_OP // OpOr }; +void Assembler::generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Expr* left, IR::Expr* right) +{ + const BinaryOperationInfo& info = binaryOperations[operation]; + if (!info.fallbackImplementation) { + assert(!"unreachable"); + return; + } + + Value leftConst = Value::undefinedValue(); + Value rightConst = Value::undefinedValue(); + + bool canDoInline = info.inlineMemRegOp && info.inlineImmRegOp; + + if (canDoInline) { + if (left->asConst()) { + leftConst = convertToValue(left->asConst()); + canDoInline = canDoInline && leftConst.tryIntegerConversion(); + } + if (right->asConst()) { + rightConst = convertToValue(right->asConst()); + canDoInline = canDoInline && rightConst.tryIntegerConversion(); + } + } + + Jump binOpFinished; + + if (canDoInline) { + + Jump leftTypeCheck; + if (left->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, left->asTemp()); + typeAddress.offset += offsetof(VM::Value, tag); + leftTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); + } + + Jump rightTypeCheck; + if (right->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, right->asTemp()); + typeAddress.offset += offsetof(VM::Value, tag); + rightTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); + } + + if (left->asTemp()) { + Address leftValue = loadTempAddress(ScratchRegister, left->asTemp()); + leftValue.offset += offsetof(VM::Value, int_32); + load32(leftValue, IntegerOpRegister); + } else { // left->asConst() + move(TrustedImm32(leftConst.integerValue()), IntegerOpRegister); + } + + Jump overflowCheck; + + if (right->asTemp()) { + Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); + rightValue.offset += offsetof(VM::Value, int_32); + + overflowCheck = (this->*info.inlineMemRegOp)(rightValue, IntegerOpRegister); + } else { // right->asConst() + overflowCheck = (this->*info.inlineImmRegOp)(TrustedImm32(rightConst.integerValue()), IntegerOpRegister); + } + + Address resultAddr = loadTempAddress(ScratchRegister, target); + Address resultValueAddr = resultAddr; + resultValueAddr.offset += offsetof(VM::Value, int_32); + store32(IntegerOpRegister, resultValueAddr); + + Address resultTypeAddr = resultAddr; + resultTypeAddr.offset += offsetof(VM::Value, tag); + store32(TrustedImm32(VM::Value::_Integer_Type), resultTypeAddr); + + binOpFinished = jump(); + + if (leftTypeCheck.isSet()) + leftTypeCheck.link(this); + if (rightTypeCheck.isSet()) + rightTypeCheck.link(this); + if (overflowCheck.isSet()) + overflowCheck.link(this); + } + + // Fallback + generateFunctionCallImp(target, info.name, info.fallbackImplementation, left, right, ContextRegister); + + if (binOpFinished.isSet()) + binOpFinished.link(this); +} #if OS(LINUX) static void printDisassembledOutputWithCalls(const char* output, const QHash& functions) { @@ -124,59 +313,8 @@ static void printDisassembledOutputWithCalls(const char* output, const QHashtempCount - _function->locals.size() + _function->maxNumberOfArguments); - locals = (locals + 1) & ~1; - enterStandardStackFrame(locals); - - int contextPointer = 0; -#ifndef VALUE_FITS_IN_REGISTER - // When the return VM value doesn't fit into a register, then - // the caller provides a pointer for storage as first argument. - // That shifts the index the context pointer argument by one. - contextPointer++; -#endif -#if CPU(X86) - loadPtr(addressForArgument(contextPointer), ContextRegister); -#elif CPU(X86_64) || CPU(ARM) - move(registerForArgument(contextPointer), ContextRegister); -#else - assert(!"TODO"); -#endif - - foreach (IR::BasicBlock *block, _function->basicBlocks) { - _block = block; - _addrs[block] = label(); - foreach (IR::Stmt *s, block->statements) { - s->accept(this); - } - } - - leaveStandardStackFrame(locals); -#ifndef VALUE_FITS_IN_REGISTER - // Emulate ret(n) instruction - // Pop off return address into scratch register ... - pop(ScratchRegister); - // ... and overwrite the invisible argument with - // the return address. - poke(ScratchRegister); -#endif - ret(); - QHashIterator > it(_patches); while (it.hasNext()) { it.next(); @@ -220,33 +358,75 @@ void InstructionSelection::operator()(IR::Function *function) } _function->code = (Value (*)(VM::ExecutionContext *, const uchar *)) _function->codeRef.code().executableAddress(); +} - qSwap(_function, function); +InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine) + : _engine(engine) + , _block(0) + , _function(0) + , _asm(0) +{ } -String *InstructionSelection::identifier(const QString &s) +InstructionSelection::~InstructionSelection() { - return _engine->identifier(s); + delete _asm; } -InstructionSelection::Pointer InstructionSelection::loadTempAddress(RegisterID reg, IR::Temp *t) +void InstructionSelection::operator()(IR::Function *function) { - int32_t offset = 0; - if (t->index < 0) { - const int arg = -t->index - 1; - loadPtr(Address(ContextRegister, offsetof(ExecutionContext, arguments)), reg); - offset = arg * sizeof(Value); - } else if (t->index < _function->locals.size()) { - loadPtr(Address(ContextRegister, offsetof(ExecutionContext, locals)), reg); - offset = t->index * sizeof(Value); - } else { - const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size(); - // StackFrameRegister points to its old value on the stack, so even for the first temp we need to - // subtract at least sizeof(Value). - offset = - sizeof(Value) * (arg + 1); - reg = StackFrameRegister; + qSwap(_function, function); + Assembler* oldAssembler = _asm; + _asm = new Assembler(_function); + + int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments); + locals = (locals + 1) & ~1; + _asm->enterStandardStackFrame(locals); + + int contextPointer = 0; +#ifndef VALUE_FITS_IN_REGISTER + // When the return VM value doesn't fit into a register, then + // the caller provides a pointer for storage as first argument. + // That shifts the index the context pointer argument by one. + contextPointer++; +#endif +#if CPU(X86) + _asm->loadPtr(addressForArgument(contextPointer), Assembler::ContextRegister); +#elif CPU(X86_64) || CPU(ARM) + _asm->move(_asm->registerForArgument(contextPointer), Assembler::ContextRegister); +#else + assert(!"TODO"); +#endif + + foreach (IR::BasicBlock *block, _function->basicBlocks) { + _block = block; + _asm->registerBlock(_block); + foreach (IR::Stmt *s, block->statements) { + s->accept(this); + } } - return Pointer(reg, offset); + + _asm->leaveStandardStackFrame(locals); +#ifndef VALUE_FITS_IN_REGISTER + // Emulate ret(n) instruction + // Pop off return address into scratch register ... + _asm->pop(Assembler::ScratchRegister); + // ... and overwrite the invisible argument with + // the return address. + _asm->poke(Assembler::ScratchRegister); +#endif + _asm->ret(); + + _asm->link(); + + qSwap(_function, function); + delete _asm; + _asm = oldAssembler; +} + +String *InstructionSelection::identifier(const QString &s) +{ + return _engine->identifier(s); } void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) @@ -260,17 +440,17 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu break; case IR::Name::builtin_typeof: { if (IR::Member *m = call->args->expr->asMember()) { - generateFunctionCall(result, __qmljs_builtin_typeof_member, m->base->asTemp(), identifier(*m->name), ContextRegister); + generateFunctionCall(result, __qmljs_builtin_typeof_member, m->base->asTemp(), identifier(*m->name), Assembler::ContextRegister); return; } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { - generateFunctionCall(result, __qmljs_builtin_typeof_element, ss->base->asTemp(), ss->index->asTemp(), ContextRegister); + generateFunctionCall(result, __qmljs_builtin_typeof_element, ss->base->asTemp(), ss->index->asTemp(), Assembler::ContextRegister); return; } else if (IR::Name *n = call->args->expr->asName()) { - generateFunctionCall(result, __qmljs_builtin_typeof_name, identifier(*n->id), ContextRegister); + generateFunctionCall(result, __qmljs_builtin_typeof_name, identifier(*n->id), Assembler::ContextRegister); return; } else if (IR::Temp *arg = call->args->expr->asTemp()){ assert(arg != 0); - generateFunctionCall(result, __qmljs_builtin_typeof, arg, ContextRegister); + generateFunctionCall(result, __qmljs_builtin_typeof, arg, Assembler::ContextRegister); } else { assert(false); } @@ -278,19 +458,19 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu break; case IR::Name::builtin_delete: { if (IR::Member *m = call->args->expr->asMember()) { - generateFunctionCall(result, __qmljs_delete_member, ContextRegister, m->base->asTemp(), identifier(*m->name)); + generateFunctionCall(result, __qmljs_delete_member, Assembler::ContextRegister, m->base->asTemp(), identifier(*m->name)); return; } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { - generateFunctionCall(result, __qmljs_delete_subscript, ContextRegister, ss->base->asTemp(), ss->index->asTemp()); + generateFunctionCall(result, __qmljs_delete_subscript, Assembler::ContextRegister, ss->base->asTemp(), ss->index->asTemp()); return; } else if (IR::Name *n = call->args->expr->asName()) { - generateFunctionCall(result, __qmljs_delete_name, ContextRegister, identifier(*n->id)); + generateFunctionCall(result, __qmljs_delete_name, Assembler::ContextRegister, identifier(*n->id)); return; } else if (call->args->expr->asTemp()){ // ### should throw in strict mode - Address dest = loadTempAddress(ScratchRegister, result); + Address dest = _asm->loadTempAddress(Assembler::ScratchRegister, result); Value v = Value::fromBoolean(false); - storeValue(v, dest); + _asm->storeValue(v, dest); return; } break; @@ -298,23 +478,23 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu case IR::Name::builtin_throw: { IR::Temp *arg = call->args->expr->asTemp(); assert(arg != 0); - generateFunctionCall(result, __qmljs_builtin_throw, arg, ContextRegister); + generateFunctionCall(result, __qmljs_builtin_throw, arg, Assembler::ContextRegister); } break; case IR::Name::builtin_create_exception_handler: - generateFunctionCall(ReturnValueRegister, __qmljs_create_exception_handler, ContextRegister); - generateFunctionCall(result, setjmp, ReturnValueRegister); + generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_create_exception_handler, Assembler::ContextRegister); + generateFunctionCall(result, setjmp, Assembler::ReturnValueRegister); break; case IR::Name::builtin_delete_exception_handler: - generateFunctionCall(Void, __qmljs_delete_exception_handler, ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_delete_exception_handler, Assembler::ContextRegister); break; case IR::Name::builtin_get_exception: - generateFunctionCall(result, __qmljs_get_exception, ContextRegister); + generateFunctionCall(result, __qmljs_get_exception, Assembler::ContextRegister); break; case IR::Name::builtin_foreach_iterator_object: { IR::Temp *arg = call->args->expr->asTemp(); assert(arg != 0); - generateFunctionCall(result, __qmljs_foreach_iterator_object, arg, ContextRegister); + generateFunctionCall(result, __qmljs_foreach_iterator_object, arg, Assembler::ContextRegister); } break; case IR::Name::builtin_foreach_next_property_name: { @@ -326,11 +506,11 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu case IR::Name::builtin_push_with: { IR::Temp *arg = call->args->expr->asTemp(); assert(arg != 0); - generateFunctionCall(Void, __qmljs_builtin_push_with, arg, ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_push_with, arg, Assembler::ContextRegister); } break; case IR::Name::builtin_pop_with: - generateFunctionCall(Void, __qmljs_builtin_pop_with, ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_pop_with, Assembler::ContextRegister); break; case IR::Name::builtin_declare_vars: { if (!call->args) @@ -340,8 +520,8 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu for (IR::ExprList *it = call->args->next; it; it = it->next) { IR::Name *arg = it->expr->asName(); assert(arg != 0); - generateFunctionCall(Void, __qmljs_builtin_declare_var, ContextRegister, - TrustedImm32(deletable->value != 0), identifier(*arg->id)); + generateFunctionCall(Assembler::Void, __qmljs_builtin_declare_var, Assembler::ContextRegister, + Assembler::TrustedImm32(deletable->value != 0), identifier(*arg->id)); } } } @@ -355,7 +535,7 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) int argc = prepareVariableArguments(call->args); IR::Temp* thisObject = 0; - generateFunctionCall(result, __qmljs_call_value, ContextRegister, thisObject, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall(result, __qmljs_call_value, Assembler::ContextRegister, thisObject, baseTemp, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) @@ -365,7 +545,7 @@ void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) assert(member->base->asTemp() != 0); int argc = prepareVariableArguments(call->args); - generateFunctionCall(result, __qmljs_call_property, ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall(result, __qmljs_call_property, Assembler::ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) @@ -383,7 +563,7 @@ void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) assert(member->base->asTemp() != 0); int argc = prepareVariableArguments(call->args); - generateFunctionCall(result, __qmljs_construct_property, ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall(result, __qmljs_construct_property, Assembler::ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) @@ -392,7 +572,7 @@ void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) assert(baseTemp != 0); int argc = prepareVariableArguments(call->args); - generateFunctionCall(result, __qmljs_construct_value, ContextRegister, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc)); + generateFunctionCall(result, __qmljs_construct_value, Assembler::ContextRegister, baseTemp, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } void InstructionSelection::visitExp(IR::Exp *s) @@ -435,7 +615,7 @@ void InstructionSelection::visitMove(IR::Move *s) String *propertyName = identifier(*n->id); if (s->source->asTemp() || s->source->asConst()) { - generateFunctionCall(Void, __qmljs_set_activation_property, ContextRegister, propertyName, s->source); + generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, Assembler::ContextRegister, propertyName, s->source); return; } else { Q_UNREACHABLE(); @@ -443,14 +623,14 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Temp *t = s->target->asTemp()) { if (IR::Name *n = s->source->asName()) { if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. - generateFunctionCall(t, __qmljs_get_thisObject, ContextRegister); + generateFunctionCall(t, __qmljs_get_thisObject, Assembler::ContextRegister); } else { String *propertyName = identifier(*n->id); - generateFunctionCall(t, __qmljs_get_activation_property, ContextRegister, propertyName); + generateFunctionCall(t, __qmljs_get_activation_property, Assembler::ContextRegister, propertyName); } return; } else if (IR::Const *c = s->source->asConst()) { - Address dest = loadTempAddress(ScratchRegister, t); + Address dest = _asm->loadTempAddress(Assembler::ScratchRegister, t); Value v; switch (c->type) { case IR::NullType: @@ -475,23 +655,23 @@ void InstructionSelection::visitMove(IR::Move *s) Q_UNIMPLEMENTED(); assert(!"TODO"); } - storeValue(v, dest); + _asm->storeValue(v, dest); return; } else if (IR::Temp *t2 = s->source->asTemp()) { - copyValue(t, t2); + _asm->copyValue(t, t2); return; } else if (IR::String *str = s->source->asString()) { - Address dest = loadTempAddress(ScratchRegister, t); + Address dest = _asm->loadTempAddress(Assembler::ScratchRegister, t); Value v = Value::fromString(_engine->newString(*str->value)); - storeValue(v, dest); + _asm->storeValue(v, dest); return; } else if (IR::RegExp *re = s->source->asRegExp()) { - Address dest = loadTempAddress(ScratchRegister, t); + Address dest = _asm->loadTempAddress(Assembler::ScratchRegister, t); Value v = Value::fromObject(_engine->newRegExpObject(*re->value, re->flags)); - storeValue(v, dest); + _asm->storeValue(v, dest); return; } else if (IR::Closure *clos = s->source->asClosure()) { - generateFunctionCall(t, __qmljs_init_closure, TrustedImmPtr(clos->value), ContextRegister); + generateFunctionCall(t, __qmljs_init_closure, Assembler::TrustedImmPtr(clos->value), Assembler::ContextRegister); return; } else if (IR::New *ctor = s->source->asNew()) { if (ctor->base->asName()) { @@ -507,13 +687,13 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Member *m = s->source->asMember()) { //__qmljs_get_property(ctx, result, object, name); if (IR::Temp *base = m->base->asTemp()) { - generateFunctionCall(t, __qmljs_get_property, ContextRegister, base, identifier(*m->name)); + generateFunctionCall(t, __qmljs_get_property, Assembler::ContextRegister, base, identifier(*m->name)); return; } assert(!"wip"); return; } else if (IR::Subscript *ss = s->source->asSubscript()) { - generateFunctionCall(t, __qmljs_get_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp()); + generateFunctionCall(t, __qmljs_get_element, Assembler::ContextRegister, ss->base->asTemp(), ss->index->asTemp()); return; } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { @@ -529,13 +709,13 @@ void InstructionSelection::visitMove(IR::Move *s) } // switch if (op) - generateFunctionCallImp(t, opName, op, e, ContextRegister); + _asm->generateFunctionCallImp(t, opName, op, e, Assembler::ContextRegister); return; } } else if (IR::Binop *b = s->source->asBinop()) { if ((b->left->asTemp() || b->left->asConst()) && (b->right->asTemp() || b->right->asConst())) { - generateBinOp((IR::AluOp)b->op, t, b->left, b->right); + _asm->generateBinOp((IR::AluOp)b->op, t, b->left, b->right); return; } } else if (IR::Call *c = s->source->asCall()) { @@ -553,7 +733,7 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (IR::Member *m = s->target->asMember()) { if (IR::Temp *base = m->base->asTemp()) { if (s->source->asTemp() || s->source->asConst()) { - generateFunctionCall(Void, __qmljs_set_property, ContextRegister, base, identifier(*m->name), s->source); + generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, base, identifier(*m->name), s->source); return; } else { Q_UNREACHABLE(); @@ -561,7 +741,7 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Subscript *ss = s->target->asSubscript()) { if (s->source->asTemp() || s->source->asConst()) { - generateFunctionCall(Void, __qmljs_set_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), s->source); + generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, ss->base->asTemp(), ss->index->asTemp(), s->source); return; } else { Q_UNIMPLEMENTED(); @@ -571,7 +751,7 @@ void InstructionSelection::visitMove(IR::Move *s) // inplace assignment, e.g. x += 1, ++x, ... if (IR::Temp *t = s->target->asTemp()) { if (s->source->asTemp() || s->source->asConst()) { - generateBinOp((IR::AluOp)s->op, t, t, s->source); + _asm->generateBinOp((IR::AluOp)s->op, t, t, s->source); return; } } else if (IR::Name *n = s->target->asName()) { @@ -595,7 +775,7 @@ void InstructionSelection::visitMove(IR::Move *s) break; } if (op) { - generateFunctionCallImp(Void, opName, op, s->source, identifier(*n->id), ContextRegister); + _asm->generateFunctionCallImp(Assembler::Void, opName, op, s->source, identifier(*n->id), Assembler::ContextRegister); } return; } @@ -623,7 +803,7 @@ void InstructionSelection::visitMove(IR::Move *s) if (op) { IR::Temp* base = ss->base->asTemp(); IR::Temp* index = ss->index->asTemp(); - generateFunctionCallImp(Void, opName, op, base, index, s->source, ContextRegister); + _asm->generateFunctionCallImp(Assembler::Void, opName, op, base, index, s->source, Assembler::ContextRegister); } return; } @@ -651,7 +831,7 @@ void InstructionSelection::visitMove(IR::Move *s) if (op) { IR::Temp* base = m->base->asTemp(); String* member = identifier(*m->name); - generateFunctionCallImp(Void, opName, op, s->source, base, member, ContextRegister); + _asm->generateFunctionCallImp(Assembler::Void, opName, op, s->source, base, member, Assembler::ContextRegister); } return; } @@ -666,38 +846,32 @@ void InstructionSelection::visitMove(IR::Move *s) void InstructionSelection::visitJump(IR::Jump *s) { - jumpToBlock(s->target); -} - -void InstructionSelection::jumpToBlock(IR::BasicBlock *target) -{ - if (_block->index + 1 != target->index) - _patches[target].append(jump()); + _asm->jumpToBlock(_block, s->target); } void InstructionSelection::visitCJump(IR::CJump *s) { if (IR::Temp *t = s->cond->asTemp()) { - Address temp = loadTempAddress(ScratchRegister, t); + Address temp = _asm->loadTempAddress(Assembler::ScratchRegister, t); Address tag = temp; tag.offset += offsetof(VM::Value, tag); - Jump booleanConversion = branch32(NotEqual, tag, TrustedImm32(VM::Value::Boolean_Type)); + Assembler::Jump booleanConversion = _asm->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(VM::Value::Boolean_Type)); Address data = temp; data.offset += offsetof(VM::Value, int_32); - load32(data, ReturnValueRegister); - Jump testBoolean = jump(); + _asm->load32(data, Assembler::ReturnValueRegister); + Assembler::Jump testBoolean = _asm->jump(); - booleanConversion.link(this); + booleanConversion.link(_asm); { - generateFunctionCall(ReturnValueRegister, __qmljs_to_boolean, t, ContextRegister); + generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, t, Assembler::ContextRegister); } - testBoolean.link(this); - Jump target = branch32(NotEqual, ReturnValueRegister, TrustedImm32(0)); - _patches[s->iftrue].append(target); + testBoolean.link(_asm); + Assembler::Jump target = _asm->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); + _asm->addPatch(s->iftrue, target); - jumpToBlock(s->iffalse); + _asm->jumpToBlock(_block, s->iffalse); return; } else if (IR::Binop *b = s->cond->asBinop()) { if ((b->left->asTemp() || b->left->asConst()) && @@ -718,12 +892,12 @@ void InstructionSelection::visitCJump(IR::CJump *s) case IR::OpIn: setOp(op, opName, __qmljs_cmp_in); break; } // switch - generateFunctionCallImp(ReturnValueRegister, opName, op, b->left, b->right, ContextRegister); + _asm->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, b->left, b->right, Assembler::ContextRegister); - Jump target = branch32(NotEqual, ReturnValueRegister, TrustedImm32(0)); - _patches[s->iftrue].append(target); + Assembler::Jump target = _asm->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); + _asm->addPatch(s->iftrue, target); - jumpToBlock(s->iffalse); + _asm->jumpToBlock(_block, s->iffalse); return; } else { assert(!"wip"); @@ -738,10 +912,10 @@ void InstructionSelection::visitRet(IR::Ret *s) { if (IR::Temp *t = s->expr->asTemp()) { #ifdef VALUE_FITS_IN_REGISTER - copyValue(ReturnValueRegister, t); + _asm->copyValue(Assembler::ReturnValueRegister, t); #else - loadPtr(addressForArgument(0), ReturnValueRegister); - copyValue(Address(ReturnValueRegister, 0), t); + _asm->loadPtr(addressForArgument(0), Assembler::ReturnValueRegister); + _asm->copyValue(Address(Assembler::ReturnValueRegister, 0), t); #endif return; } @@ -760,7 +934,7 @@ int InstructionSelection::prepareVariableArguments(IR::ExprList* args) for (IR::ExprList *it = args; it; it = it->next, ++i) { IR::Temp *arg = it->expr->asTemp(); assert(arg != 0); - copyValue(argumentAddressForCall(i), arg); + _asm->copyValue(argumentAddressForCall(i), arg); } return argc; @@ -772,112 +946,13 @@ void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* na assert(baseName != 0); int argc = prepareVariableArguments(args); - generateFunctionCallImp(result, name, method, ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), TrustedImm32(argc)); + _asm->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args) { int argc = prepareVariableArguments(args); - generateFunctionCallImp(result, name, method, ContextRegister, baseAddressForCallArguments(), TrustedImm32(argc)); -} - -template -void InstructionSelection::copyValue(Result result, Source source) -{ -#ifdef VALUE_FITS_IN_REGISTER - // Use ReturnValueRegister as "scratch" register because loadArgument - // and storeArgument are functions that may need a scratch register themselves. - loadArgument(source, ReturnValueRegister); - storeArgument(ReturnValueRegister, result); -#else - loadDouble(source, FPGpr0); - storeDouble(FPGpr0, result); -#endif + _asm->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } -void InstructionSelection::generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Expr* left, IR::Expr* right) -{ - const BinaryOperationInfo& info = binaryOperations[operation]; - if (!info.fallbackImplementation) { - assert(!"unreachable"); - return; - } - - Value leftConst = Value::undefinedValue(); - Value rightConst = Value::undefinedValue(); - - bool canDoInline = info.inlineMemRegOp && info.inlineImmRegOp; - - if (canDoInline) { - if (left->asConst()) { - leftConst = convertToValue(left->asConst()); - canDoInline = canDoInline && leftConst.tryIntegerConversion(); - } - if (right->asConst()) { - rightConst = convertToValue(right->asConst()); - canDoInline = canDoInline && rightConst.tryIntegerConversion(); - } - } - - Jump binOpFinished; - - if (canDoInline) { - - Jump leftTypeCheck; - if (left->asTemp()) { - Address typeAddress = loadTempAddress(ScratchRegister, left->asTemp()); - typeAddress.offset += offsetof(VM::Value, tag); - leftTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); - } - - Jump rightTypeCheck; - if (right->asTemp()) { - Address typeAddress = loadTempAddress(ScratchRegister, right->asTemp()); - typeAddress.offset += offsetof(VM::Value, tag); - rightTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); - } - - if (left->asTemp()) { - Address leftValue = loadTempAddress(ScratchRegister, left->asTemp()); - leftValue.offset += offsetof(VM::Value, int_32); - load32(leftValue, IntegerOpRegister); - } else { // left->asConst() - move(TrustedImm32(leftConst.integerValue()), IntegerOpRegister); - } - - Jump overflowCheck; - if (right->asTemp()) { - Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); - rightValue.offset += offsetof(VM::Value, int_32); - - overflowCheck = (this->*info.inlineMemRegOp)(rightValue, IntegerOpRegister); - } else { // right->asConst() - overflowCheck = (this->*info.inlineImmRegOp)(TrustedImm32(rightConst.integerValue()), IntegerOpRegister); - } - - Address resultAddr = loadTempAddress(ScratchRegister, target); - Address resultValueAddr = resultAddr; - resultValueAddr.offset += offsetof(VM::Value, int_32); - store32(IntegerOpRegister, resultValueAddr); - - Address resultTypeAddr = resultAddr; - resultTypeAddr.offset += offsetof(VM::Value, tag); - store32(TrustedImm32(VM::Value::_Integer_Type), resultTypeAddr); - - binOpFinished = jump(); - - if (leftTypeCheck.isSet()) - leftTypeCheck.link(this); - if (rightTypeCheck.isSet()) - rightTypeCheck.link(this); - if (overflowCheck.isSet()) - overflowCheck.link(this); - } - - // Fallback - generateFunctionCallImp(target, info.name, info.fallbackImplementation, left, right, ContextRegister); - - if (binOpFinished.isSet()) - binOpFinished.link(this); -} diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index aa39a15ca5..7a478ae499 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -55,17 +55,10 @@ namespace QQmlJS { namespace MASM { -class InstructionSelection: protected IR::StmtVisitor, public JSC::MacroAssembler, public EvalInstructionSelection +class Assembler : public JSC::MacroAssembler { public: - InstructionSelection(VM::ExecutionEngine *engine); - ~InstructionSelection(); - - virtual void run(IR::Function *function) - { this->operator()(function); } - void operator()(IR::Function *function); - -protected: + Assembler(IR::Function* function); #if CPU(X86) #undef VALUE_FITS_IN_REGISTER @@ -148,9 +141,6 @@ protected: #error Argh. #endif - struct VoidType {}; - static const VoidType Void; - // Explicit type to allow distinguishing between // pushing an address itself or the value it points // to onto the stack when calling functions. @@ -164,98 +154,18 @@ protected: {} }; - void enterStandardStackFrame(int locals) - { -#if CPU(ARM) - push(JSC::ARMRegisters::lr); -#endif - push(StackFrameRegister); - move(StackPointerRegister, StackFrameRegister); - - // space for the locals and the ContextRegister - int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*); - -#if CPU(X86) || CPU(X86_64) - frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX -#endif - subPtr(TrustedImm32(frameSize), StackPointerRegister); - -#if CPU(X86) || CPU(ARM) - for (int saveReg = CalleeSavedFirstRegister; saveReg <= CalleeSavedLastRegister; ++saveReg) - push(static_cast(saveReg)); -#endif - // save the ContextRegister - storePtr(ContextRegister, StackPointerRegister); - } - void leaveStandardStackFrame(int locals) - { - // restore the ContextRegister - loadPtr(StackPointerRegister, ContextRegister); - -#if CPU(X86) || CPU(ARM) - for (int saveReg = CalleeSavedLastRegister; saveReg >= CalleeSavedFirstRegister; --saveReg) - pop(static_cast(saveReg)); -#endif - // space for the locals and the ContextRegister - int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*); -#if CPU(X86) || CPU(X86_64) - frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX -#endif - addPtr(TrustedImm32(frameSize), StackPointerRegister); - - pop(StackFrameRegister); -#if CPU(ARM) - pop(JSC::ARMRegisters::lr); -#endif - } - - Address addressForArgument(int index) const - { - if (index < RegisterArgumentCount) - return Address(registerForArgument(index), 0); - - // StackFrameRegister points to its old value on the stack, and above - // it we have the return address, hence the need to step over two - // values before reaching the first argument. - return Address(StackFrameRegister, (index - RegisterArgumentCount + 2) * sizeof(void*)); - } - - // Some run-time functions take (Value* args, int argc). This function is for populating - // the args. - Pointer argumentAddressForCall(int argument) - { - const int index = _function->maxNumberOfArguments - argument; - return Pointer(StackFrameRegister, sizeof(VM::Value) * (-index) - - sizeof(void*) // size of ebp - ); - } - Pointer baseAddressForCallArguments() - { - return argumentAddressForCall(0); - } - - VM::String *identifier(const QString &s); - Pointer loadTempAddress(RegisterID reg, IR::Temp *t); - void callActivationProperty(IR::Call *call, IR::Temp *result); - void callProperty(IR::Call *call, IR::Temp *result); - void constructActivationProperty(IR::New *call, IR::Temp *result); - void constructProperty(IR::New *ctor, IR::Temp *result); - void callValue(IR::Call *call, IR::Temp *result); - void constructValue(IR::New *call, IR::Temp *result); - - virtual void visitExp(IR::Exp *); - virtual void visitEnter(IR::Enter *); - virtual void visitLeave(IR::Leave *); - virtual void visitMove(IR::Move *s); - virtual void visitJump(IR::Jump *); - virtual void visitCJump(IR::CJump *); - virtual void visitRet(IR::Ret *); + struct VoidType {}; + static const VoidType Void; -private: - void jumpToBlock(IR::BasicBlock *target); typedef JSC::FunctionPtr FunctionPtr; + struct CallToLink { + Call call; + FunctionPtr externalFunction; + const char* functionName; + }; + void callAbsolute(const char* functionName, FunctionPtr function) { CallToLink ctl; ctl.call = call(); @@ -264,6 +174,12 @@ private: _callsToLink.append(ctl); } + void registerBlock(IR::BasicBlock*); + void jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target); + void addPatch(IR::BasicBlock* targetBlock, Jump targetJump); + + Pointer loadTempAddress(RegisterID reg, IR::Temp *t); + void loadArgument(RegisterID source, RegisterID dest) { move(source, dest); @@ -426,6 +342,37 @@ private: push(TrustedImmPtr(name)); } + using JSC::MacroAssembler::loadDouble; + void loadDouble(IR::Temp* temp, FPRegisterID dest) + { + Pointer ptr = loadTempAddress(ScratchRegister, temp); + loadDouble(ptr, dest); + } + + using JSC::MacroAssembler::storeDouble; + void storeDouble(FPRegisterID source, IR::Temp* temp) + { + Pointer ptr = loadTempAddress(ScratchRegister, temp); + storeDouble(source, ptr); + } + + template + void copyValue(Result result, Source source); + + void storeValue(VM::Value value, Address destination) + { +#ifdef VALUE_FITS_IN_REGISTER + store64(TrustedImm64(value.val), destination); +#else + store32(TrustedImm32(value.int_32), destination); + destination.offset += 4; + store32(TrustedImm32(value.tag), destination); +#endif + } + + void enterStandardStackFrame(int locals); + void leaveStandardStackFrame(int locals); + void callFunctionPrologue() { #if CPU(X86) @@ -440,12 +387,6 @@ private: #endif } - #define isel_stringIfyx(s) #s - #define isel_stringIfy(s) isel_stringIfyx(s) - - #define generateFunctionCall(t, function, ...) \ - generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) - static inline int sizeOfArgument(VoidType) { return 0; } static inline int sizeOfArgument(RegisterID) @@ -465,8 +406,8 @@ private: struct ArgumentLoader { - ArgumentLoader(InstructionSelection* instructionSelection, int totalNumberOfArguments) - : isel(instructionSelection) + ArgumentLoader(Assembler* _assembler, int totalNumberOfArguments) + : assembler(_assembler) , stackSpaceForArguments(0) , currentRegisterIndex(qMin(totalNumberOfArguments - 1, RegisterArgumentCount - 1)) { @@ -476,10 +417,10 @@ private: void load(T argument) { if (currentRegisterIndex >= 0) { - isel->loadArgument(argument, registerForArgument(currentRegisterIndex)); + assembler->loadArgument(argument, registerForArgument(currentRegisterIndex)); --currentRegisterIndex; } else { - isel->push(argument); + assembler->push(argument); stackSpaceForArguments += sizeOfArgument(argument); } } @@ -490,7 +431,7 @@ private: --currentRegisterIndex; } - InstructionSelection *isel; + Assembler *assembler; int stackSpaceForArguments; int currentRegisterIndex; }; @@ -564,51 +505,8 @@ private: generateFunctionCallImp(r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType()); } - int prepareVariableArguments(IR::ExprList* args); - - typedef VM::Value (*ActivationMethod)(VM::ExecutionContext *, VM::String *name, VM::Value *args, int argc); - typedef VM::Value (*BuiltinMethod)(VM::ExecutionContext *, VM::Value *args, int argc); - void callRuntimeMethodImp(IR::Temp *result, const char* name, ActivationMethod method, IR::Expr *base, IR::ExprList *args); - void callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args); -#define callRuntimeMethod(result, function, ...) \ - callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__) - - using JSC::MacroAssembler::loadDouble; - void loadDouble(IR::Temp* temp, FPRegisterID dest) - { - Pointer ptr = loadTempAddress(ScratchRegister, temp); - loadDouble(ptr, dest); - } - - using JSC::MacroAssembler::storeDouble; - void storeDouble(FPRegisterID source, IR::Temp* temp) - { - Pointer ptr = loadTempAddress(ScratchRegister, temp); - storeDouble(source, ptr); - } - - template - void copyValue(Result result, Source source); - - struct CallToLink { - Call call; - FunctionPtr externalFunction; - const char* functionName; - }; - - void storeValue(VM::Value value, Address destination) - { -#ifdef VALUE_FITS_IN_REGISTER - store64(TrustedImm64(value.val), destination); -#else - store32(TrustedImm32(value.int_32), destination); - destination.offset += 4; - store32(TrustedImm32(value.tag), destination); -#endif - } - - typedef Jump (InstructionSelection::*MemRegBinOp)(Address, RegisterID); - typedef Jump (InstructionSelection::*ImmRegBinOp)(TrustedImm32, RegisterID); + typedef Jump (Assembler::*MemRegBinOp)(Address, RegisterID); + typedef Jump (Assembler::*ImmRegBinOp)(TrustedImm32, RegisterID); struct BinaryOperationInfo { const char *name; @@ -732,14 +630,93 @@ private: return Jump(); } - VM::ExecutionEngine *_engine; - IR::Function *_function; - IR::BasicBlock *_block; - QHash > _patches; + void link(); + +private: + IR::Function* _function; QHash _addrs; + QHash > _patches; QList _callsToLink; }; +class InstructionSelection: protected IR::StmtVisitor, public EvalInstructionSelection +{ +public: + InstructionSelection(VM::ExecutionEngine *engine); + ~InstructionSelection(); + + virtual void run(IR::Function *function) + { this->operator()(function); } + void operator()(IR::Function *function); + +protected: + typedef Assembler::Address Address; + typedef Assembler::Pointer Pointer; + + Address addressForArgument(int index) const + { + if (index < Assembler::RegisterArgumentCount) + return Address(_asm->registerForArgument(index), 0); + + // StackFrameRegister points to its old value on the stack, and above + // it we have the return address, hence the need to step over two + // values before reaching the first argument. + return Address(Assembler::StackFrameRegister, (index - Assembler::RegisterArgumentCount + 2) * sizeof(void*)); + } + + // Some run-time functions take (Value* args, int argc). This function is for populating + // the args. + Pointer argumentAddressForCall(int argument) + { + const int index = _function->maxNumberOfArguments - argument; + return Pointer(Assembler::StackFrameRegister, sizeof(VM::Value) * (-index) + - sizeof(void*) // size of ebp + ); + } + Pointer baseAddressForCallArguments() + { + return argumentAddressForCall(0); + } + + VM::String *identifier(const QString &s); + void callActivationProperty(IR::Call *call, IR::Temp *result); + void callProperty(IR::Call *call, IR::Temp *result); + void constructActivationProperty(IR::New *call, IR::Temp *result); + void constructProperty(IR::New *ctor, IR::Temp *result); + void callValue(IR::Call *call, IR::Temp *result); + void constructValue(IR::New *call, IR::Temp *result); + + virtual void visitExp(IR::Exp *); + virtual void visitEnter(IR::Enter *); + virtual void visitLeave(IR::Leave *); + virtual void visitMove(IR::Move *s); + virtual void visitJump(IR::Jump *); + virtual void visitCJump(IR::CJump *); + virtual void visitRet(IR::Ret *); + +private: + #define isel_stringIfyx(s) #s + #define isel_stringIfy(s) isel_stringIfyx(s) + + #define generateFunctionCall(t, function, ...) \ + _asm->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) + + int prepareVariableArguments(IR::ExprList* args); + + typedef VM::Value (*ActivationMethod)(VM::ExecutionContext *, VM::String *name, VM::Value *args, int argc); + typedef VM::Value (*BuiltinMethod)(VM::ExecutionContext *, VM::Value *args, int argc); + void callRuntimeMethodImp(IR::Temp *result, const char* name, ActivationMethod method, IR::Expr *base, IR::ExprList *args); + void callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args); +#define callRuntimeMethod(result, function, ...) \ + callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__) + + + VM::ExecutionEngine *_engine; + IR::BasicBlock *_block; + IR::Function* _function; + Assembler* _asm; +}; + class ISelFactory: public EvalISelFactory { public: -- cgit v1.2.3 From 5e39d56c0974faa28c5060a12064e1ad7f611ea0 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 11 Dec 2012 10:03:40 +0100 Subject: Remove IR::Function from the runtime. This fixes potential leaks of IR::Functions, lowers the memory usage of the functions that the VM needs (because the IR fields are not present in the VM::Function), and makes both managed by the module respectively the ExecutionEngine. Change-Id: I6748ad98b062f994eae9dd14f1919aec5aa7c0b0 Reviewed-by: Lars Knoll --- debugging.cpp | 15 ++++++++---- debugging.h | 12 ++++------ main.cpp | 2 +- moth/qv4instr_moth_p.h | 2 +- moth/qv4isel_moth.cpp | 65 +++++++++++++++++++++++++++++--------------------- moth/qv4isel_moth_p.h | 11 ++++----- qmljs_engine.cpp | 12 +++++++++- qmljs_engine.h | 6 ++++- qmljs_objects.cpp | 37 ++++++++++++++++++---------- qmljs_objects.h | 44 +++++++++++++++++++++++++++------- qmljs_runtime.cpp | 3 ++- qmljs_runtime.h | 8 ++----- qv4ecmaobjects.cpp | 7 +++--- qv4ir.cpp | 11 +++------ qv4ir_p.h | 9 ------- qv4isel_masm.cpp | 29 ++++++++++++---------- qv4isel_masm_p.h | 14 ++++------- qv4isel_p.cpp | 37 ++++++++++++++++++++++++++++ qv4isel_p.h | 16 +++++++++++-- 19 files changed, 217 insertions(+), 123 deletions(-) diff --git a/debugging.cpp b/debugging.cpp index 62e38d0e17..4376d0a736 100644 --- a/debugging.cpp +++ b/debugging.cpp @@ -99,14 +99,14 @@ void Debugger::addFunction(IR::Function *function) _functionInfo.insert(function, new FunctionDebugInfo(function)); } -void Debugger::addaddBasicBlockOffset(IR::Function *function, IR::BasicBlock *block, ptrdiff_t blockOffset) +void Debugger::setSourceLocation(IR::Function *function, unsigned line, unsigned column) { - _functionInfo[function]->addBasicBlockOffset(block, blockOffset); + _functionInfo[function]->setSourceLocation(line, column); } -void Debugger::setSourceLocation(IR::Function *function, unsigned line, unsigned column) +void Debugger::mapFunction(VM::Function *vmf, IR::Function *irf) { - _functionInfo[function]->setSourceLocation(line, column); + _vmToIr.insert(vmf, irf); } FunctionDebugInfo *Debugger::debugInfo(VM::FunctionObject *function) const @@ -115,7 +115,7 @@ FunctionDebugInfo *Debugger::debugInfo(VM::FunctionObject *function) const return 0; if (VM::ScriptFunction *sf = function->asScriptFunction()) - return _functionInfo[sf->function]; + return _functionInfo[irFunction(sf->function)]; else return 0; } @@ -209,3 +209,8 @@ int Debugger::callIndex(VM::ExecutionContext *context) return -1; } + +IR::Function *Debugger::irFunction(VM::Function *vmf) const +{ + return _vmToIr[vmf]; +} diff --git a/debugging.h b/debugging.h index 7c4c273e43..e2833148f2 100644 --- a/debugging.h +++ b/debugging.h @@ -48,14 +48,8 @@ class Debugger; struct FunctionDebugInfo { // TODO: use opaque d-pointers here IR::Function *function; - QHash blockOffsets; unsigned startLine, startColumn; - - FunctionDebugInfo(IR::Function *function): function(function), startLine(0), startColumn(0) {} - - void addBasicBlockOffset(IR::BasicBlock *block, ptrdiff_t offset) { - blockOffsets.insert(offset, block); - } + FunctionDebugInfo(IR::Function *function): function(function), startLine(0), startColumn(0) {} void setSourceLocation(unsigned line, unsigned column) { startLine = line; startColumn = column; } @@ -102,8 +96,8 @@ public: public: // compile-time interface void addFunction(IR::Function *function); - void addaddBasicBlockOffset(IR::Function *function, IR::BasicBlock *block, ptrdiff_t blockOffset); void setSourceLocation(IR::Function *function, unsigned line, unsigned column); + void mapFunction(VM::Function *vmf, IR::Function *irf); public: // run-time querying interface FunctionDebugInfo *debugInfo(VM::FunctionObject *function) const; @@ -125,10 +119,12 @@ public: // debugging hooks private: int callIndex(VM::ExecutionContext *context); + IR::Function *irFunction(VM::Function *vmf) const; private: // TODO: use opaque d-pointers here VM::ExecutionEngine *_engine; QHash _functionInfo; + QHash _vmToIr; QVector _callStack; }; diff --git a/main.cpp b/main.cpp index 919cca76d3..7a79a7cd77 100644 --- a/main.cpp +++ b/main.cpp @@ -375,7 +375,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - QScopedPointer f(QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode)); + QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode); if (!f) continue; diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 67d11370ac..65a6a0303e 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -86,7 +86,7 @@ union Instr }; struct instr_loadClosure { MOTH_INSTR_HEADER - IR::Function *value; + VM::Function *value; int targetTempIndex; }; struct instr_loadName { diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 10a5c1106d..328fdce38c 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -181,27 +181,28 @@ private: } // anonymous namespace -InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine) - : _engine(engine) +InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module) + : EvalInstructionSelection(engine, module) + , _function(0) + , _block(0) + , _code(0) + , _ccode(0) { - // FIXME: make the size dynamic. This requires changing the patching. - _code = new uchar[getpagesize() * 4000]; - _ccode = _code; } InstructionSelection::~InstructionSelection() { } -void InstructionSelection::operator()(IR::Function *function) +VM::Function *InstructionSelection::run(IR::Function *function) { qSwap(_function, function); + // FIXME: make the size dynamic. This requires changing the patching. + _code = new uchar[getpagesize() * 4000]; + _ccode = _code; CompressTemps().run(_function); - _function->code = VME::exec; - _function->codeData = _code; - int locals = frameSize(); assert(locals >= 0); @@ -210,10 +211,7 @@ void InstructionSelection::operator()(IR::Function *function) addInstruction(push); foreach (_block, _function->basicBlocks) { - ptrdiff_t blockOffset = _ccode - _code; _addrs.insert(_block, _ccode - _code); - if (_engine->debugger) - _engine->debugger->addaddBasicBlockOffset(_function, _block, blockOffset); foreach (IR::Stmt *s, _block->statements) s->accept(this); @@ -234,6 +232,17 @@ void InstructionSelection::operator()(IR::Function *function) } qSwap(_function, function); + _patches.clear(); + _addrs.clear(); + + VM::Function *vmFunc = vmFunction(function); + vmFunc->code = VME::exec; + vmFunc->codeData = _code; + + _block = 0; + _code = 0; + _ccode = 0; + return vmFunc; } void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempIndex) @@ -246,7 +255,7 @@ void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempInd const int scratchIndex = scratchTempIndex(); Instruction::LoadName load; - load.name = _engine->newString(*baseName->id); + load.name = engine()->newString(*baseName->id); load.targetTempIndex = scratchIndex; addInstruction(load); @@ -272,7 +281,7 @@ void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempInd if (IR::Member *m = c->args->expr->asMember()) { Instruction::CallBuiltinDeleteMember call; call.base = m->base->asTemp()->index; - call.member = _engine->newString(*m->name); + call.member = engine()->newString(*m->name); call.targetTempIndex = targetTempIndex; addInstruction(call); } else if (IR::Subscript *ss = c->args->expr->asSubscript()) { @@ -283,7 +292,7 @@ void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempInd addInstruction(call); } else if (IR::Name *n = c->args->expr->asName()) { Instruction::CallBuiltinDeleteName call; - call.name = _engine->newString(*n->id); + call.name = engine()->newString(*n->id); call.targetTempIndex = targetTempIndex; addInstruction(call); } else { @@ -363,7 +372,7 @@ void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempInd for (IR::ExprList *it = c->args->next; it; it = it->next) { Instruction::CallBuiltinDeclareVar call; call.isDeletable = isDeletable; - call.varName = _engine->newString(*it->expr->asName()->id); + call.varName = engine()->newString(*it->expr->asName()->id); } } break; @@ -393,7 +402,7 @@ void InstructionSelection::callProperty(IR::Call *c, int targetTempIndex) // call the property on the loaded base Instruction::CallProperty call; call.baseTemp = m->base->asTemp()->index; - call.name = _engine->newString(*m->name); + call.name = engine()->newString(*m->name); prepareCallArgs(c->args, call.argc, call.args); call.targetTempIndex = targetTempIndex; addInstruction(call); @@ -403,7 +412,7 @@ void InstructionSelection::construct(IR::New *ctor, int targetTempIndex) { if (IR::Name *baseName = ctor->base->asName()) { Instruction::CreateActivationProperty create; - create.name = _engine->newString(*baseName->id); + create.name = engine()->newString(*baseName->id); prepareCallArgs(ctor->args, create.argc, create.args); create.targetTempIndex = targetTempIndex; addInstruction(create); @@ -413,7 +422,7 @@ void InstructionSelection::construct(IR::New *ctor, int targetTempIndex) Instruction::CreateProperty create; create.base = base->index; - create.name = _engine->newString(*member->name); + create.name = engine()->newString(*member->name); prepareCallArgs(ctor->args, create.argc, create.args); create.targetTempIndex = targetTempIndex; addInstruction(create); @@ -578,7 +587,7 @@ void InstructionSelection::visitMove(IR::Move *s) addInstruction(load); } else { Instruction::LoadName load; - load.name = _engine->newString(*n->id); + load.name = engine()->newString(*n->id); load.targetTempIndex = targetTempIndex; addInstruction(load); } @@ -607,12 +616,14 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::String *str = s->source->asString()) { Instruction::LoadValue load; - load.value = VM::Value::fromString(_engine->newString(*str->value)); + load.value = VM::Value::fromString(engine()->newString(*str->value)); load.targetTempIndex = targetTempIndex; addInstruction(load); } else if (IR::Closure *clos = s->source->asClosure()) { + VM::Function *vmFunc = vmFunction(clos->value); + assert(vmFunc); Instruction::LoadClosure load; - load.value = clos->value; + load.value = vmFunc; load.targetTempIndex = targetTempIndex; addInstruction(load); } else if (IR::New *ctor = s->source->asNew()) { @@ -621,7 +632,7 @@ void InstructionSelection::visitMove(IR::Move *s) if (IR::Temp *base = m->base->asTemp()) { Instruction::LoadProperty load; load.baseTemp = base->index; - load.name = _engine->newString(*m->name); + load.name = engine()->newString(*m->name); load.targetTempIndex = targetTempIndex; addInstruction(load); } else { @@ -699,14 +710,14 @@ void InstructionSelection::visitMove(IR::Move *s) if (op) { Instruction::InplaceNameOp ieo; ieo.alu = op; - ieo.targetName = _engine->newString(*n->id); + ieo.targetName = engine()->newString(*n->id); ieo.sourceIsTemp = toValueOrTemp(s->source, ieo.source); addInstruction(ieo); return; } else if (s->op == IR::OpInvalid) { Instruction::StoreName store; store.sourceIsTemp = toValueOrTemp(s->source, store.source); - store.name = _engine->newString(*n->id); + store.name = engine()->newString(*n->id); addInstruction(store); return; } @@ -770,14 +781,14 @@ void InstructionSelection::visitMove(IR::Move *s) Instruction::InplaceMemberOp imo; imo.alu = op; imo.targetBase = m->base->asTemp()->index; - imo.targetMember = _engine->newString(*m->name); + imo.targetMember = engine()->newString(*m->name); imo.sourceIsTemp = toValueOrTemp(s->source, imo.source); addInstruction(imo); return; } else if (s->op == IR::OpInvalid) { Instruction::StoreProperty store; store.baseTemp = m->base->asTemp()->index; - store.name = _engine->newString(*m->name); + store.name = engine()->newString(*m->name); store.sourceIsTemp = toValueOrTemp(s->source, store.source); addInstruction(store); return; diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 6fe5259dfb..e7c85174c3 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -12,12 +12,10 @@ namespace Moth { class InstructionSelection : public IR::StmtVisitor, public EvalInstructionSelection { public: - InstructionSelection(VM::ExecutionEngine *engine); + InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); ~InstructionSelection(); - virtual void run(IR::Function *function) - { this->operator()(function); } - virtual void operator()(IR::Function *function); + virtual VM::Function *run(IR::Function *function); protected: virtual void visitExp(IR::Exp *); @@ -52,7 +50,6 @@ private: inline ptrdiff_t addInstruction(const InstrData &data); ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); - VM::ExecutionEngine *_engine; IR::Function *_function; IR::BasicBlock *_block; @@ -67,8 +64,8 @@ class ISelFactory: public EvalISelFactory { public: virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine) - { return new InstructionSelection(engine); } + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module) + { return new InstructionSelection(engine, module); } }; template diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 4da9564939..a29088f68e 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -222,6 +222,7 @@ ExecutionEngine::~ExecutionEngine() delete globalObject.asObject(); delete rootContext; delete stringPool; + qDeleteAll(functions); } ExecutionContext *ExecutionEngine::newContext() @@ -237,6 +238,13 @@ String *ExecutionEngine::identifier(const QString &s) return id; } +Function *ExecutionEngine::newFunction(const QString &name) +{ + VM::Function *f = new VM::Function(name); + functions.append(f); + return f; +} + FunctionObject *ExecutionEngine::newNativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) { NativeFunction *f = new (memoryManager) NativeFunction(scope, name, code); @@ -244,8 +252,10 @@ FunctionObject *ExecutionEngine::newNativeFunction(ExecutionContext *scope, Stri return f; } -FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, IR::Function *function) +FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, VM::Function *function) { + assert(function); + MemoryManager::GCBlocker gcBlocker(memoryManager); ScriptFunction *f = new (memoryManager) ScriptFunction(scope, function); diff --git a/qmljs_engine.h b/qmljs_engine.h index 1b4e593200..eef62a3249 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -56,6 +56,7 @@ namespace VM { struct Value; class Array; +struct Function; struct Object; struct BooleanObject; struct NumberObject; @@ -156,6 +157,7 @@ struct ExecutionEngine Value exception; struct StringPool *stringPool; + QVector functions; ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory *iselFactory); ~ExecutionEngine(); @@ -164,8 +166,10 @@ struct ExecutionEngine String *identifier(const QString &s); + VM::Function *newFunction(const QString &name); + FunctionObject *newNativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); - FunctionObject *newScriptFunction(ExecutionContext *scope, IR::Function *function); + FunctionObject *newScriptFunction(ExecutionContext *scope, VM::Function *function); Object *newObject(); FunctionObject *newObjectCtor(ExecutionContext *ctx); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index c3aafae56b..431d508160 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -426,6 +426,11 @@ void ArrayObject::getCollectables(QVector &objects) value.getCollectables(objects); } +Function::~Function() +{ + delete[] codeData; +} + bool FunctionObject::hasInstance(ExecutionContext *ctx, const Value &value) { if (! value.isObject()) { @@ -489,23 +494,26 @@ Value FunctionObject::construct(ExecutionContext *ctx) return ctx->thisObject; } -ScriptFunction::ScriptFunction(ExecutionContext *scope, IR::Function *function) +ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) : FunctionObject(scope) , function(function) { + assert(function); + assert(function->code); + // global function if (!scope) return; - if (function->name) - name = scope->engine->identifier(*function->name); + if (!function->name.isEmpty()) + name = scope->engine->identifier(function->name); needsActivation = function->needsActivation(); strictMode = function->isStrict; formalParameterCount = function->formals.size(); if (formalParameterCount) { formalParameterList = new String*[formalParameterCount]; for (unsigned int i = 0; i < formalParameterCount; ++i) { - formalParameterList[i] = scope->engine->identifier(*function->formals.at(i)); + formalParameterList[i] = scope->engine->identifier(function->formals.at(i)); } } @@ -513,7 +521,7 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, IR::Function *function) if (varCount) { varList = new String*[varCount]; for (unsigned int i = 0; i < varCount; ++i) { - varList[i] = scope->engine->identifier(*function->locals.at(i)); + varList[i] = scope->engine->identifier(function->locals.at(i)); } } } @@ -526,6 +534,7 @@ ScriptFunction::~ScriptFunction() Value ScriptFunction::call(VM::ExecutionContext *ctx) { + assert(function->code); return function->code(ctx, function->codeData); } @@ -542,7 +551,7 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value bool directCall = true; const QString code = args[0].stringValue()->toQString(); - QScopedPointer f(parseSource(context, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode)); + QQmlJS::VM::Function *f = parseSource(context, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode); if (!f) return Value::undefinedValue(); @@ -580,7 +589,7 @@ EvalFunction::EvalFunction(ExecutionContext *scope) name = scope->engine->newString(QLatin1String("eval")); } -QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, +QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, const QString &source, QQmlJS::Codegen::Mode mode) { @@ -590,7 +599,7 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct VM::ExecutionEngine *vm = ctx->engine; IR::Module module; - IR::Function *globalCode = 0; + VM::Function *globalCode = 0; { QQmlJS::Engine ee, *engine = ⅇ @@ -630,13 +639,17 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct } Codegen cg(ctx); - globalCode = cg(fileName, program, &module, mode); + IR::Function *globalIRCode = cg(fileName, program, &module, mode); + QScopedPointer isel(ctx->engine->iselFactory->create(vm, &module)); + if (globalIRCode) { + globalCode = isel->run(globalIRCode); + } if (globalCode) { // only generate other functions if global code generation succeeded. foreach (IR::Function *function, module.functions) { - EvalInstructionSelection *isel = ctx->engine->iselFactory->create(vm); - isel->run(function); - delete isel; + if (function == globalIRCode) + continue; + globalCode->nestedFunctions.append(isel->run(function)); } } } diff --git a/qmljs_objects.h b/qmljs_objects.h index 51127f6268..1b0b67fab9 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -56,13 +56,10 @@ namespace QQmlJS { -namespace IR { -struct Function; -} - namespace VM { struct Value; +struct Function; struct Object; struct BooleanObject; struct NumberObject; @@ -525,6 +522,34 @@ protected: virtual void getCollectables(QVector &objects); }; +struct Function { + QString name; + + VM::Value (*code)(VM::ExecutionContext *, const uchar *); + const uchar *codeData; + JSC::MacroAssemblerCodeRef codeRef; + + QList formals; + QList locals; + QVector nestedFunctions; + + bool hasDirectEval: 1; + bool isStrict: 1; + + Function(const QString &name) + : name(name) + , code(0) + , codeData(0) + , hasDirectEval(false) + , isStrict(false) + {} + ~Function(); + + inline bool hasNestedFunctions() const { return !nestedFunctions.isEmpty(); } + + inline bool needsActivation() const { return hasNestedFunctions() || hasDirectEval; } +}; + struct FunctionObject: Object { ExecutionContext *scope; String *name; @@ -568,9 +593,9 @@ struct NativeFunction: FunctionObject { }; struct ScriptFunction: FunctionObject { - IR::Function *function; + VM::Function *function; - ScriptFunction(ExecutionContext *scope, IR::Function *function); + ScriptFunction(ExecutionContext *scope, VM::Function *function); virtual ~ScriptFunction(); virtual Value call(ExecutionContext *ctx); @@ -583,9 +608,10 @@ struct EvalFunction : FunctionObject { EvalFunction(ExecutionContext *scope); - static QQmlJS::IR::Function *parseSource(QQmlJS::VM::ExecutionContext *ctx, - const QString &fileName, const QString &source, - QQmlJS::Codegen::Mode mode); + static QQmlJS::VM::Function *parseSource(QQmlJS::VM::ExecutionContext *ctx, + const QString &fileName, + const QString &source, + QQmlJS::Codegen::Mode mode); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); }; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 1e664d2b09..a00ac6c3c6 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -105,8 +105,9 @@ QString numberToString(double num, int radix = 10) extern "C" { -Value __qmljs_init_closure(IR::Function *clos, ExecutionContext *ctx) +Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx) { + assert(clos); return Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); } diff --git a/qmljs_runtime.h b/qmljs_runtime.h index e8049d9192..1021d4e89d 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -60,11 +60,6 @@ #endif // TRACE1 namespace QQmlJS { - -namespace IR { -struct Function; -} - namespace VM { enum TypeHint { @@ -73,6 +68,7 @@ enum TypeHint { STRING_HINT }; +struct Function; struct Object; struct String; struct PropertyDescriptor; @@ -109,7 +105,7 @@ void __qmljs_builtin_pop_with(ExecutionContext *ctx); void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name); // constructors -Value __qmljs_init_closure(IR::Function *clos, ExecutionContext *ctx); +Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx); Value __qmljs_init_native_function(String *name, Value (*code)(ExecutionContext *), ExecutionContext *ctx); Bool __qmljs_is_function(Value value); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index c9b9593adc..fb3f6d7df7 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2040,11 +2040,10 @@ Value FunctionCtor::construct(ExecutionContext *ctx) Codegen cg(ctx); IR::Function *irf = cg(QString(), fe, &module); - EvalInstructionSelection *isel = ctx->engine->iselFactory->create(ctx->engine); - isel->run(irf); - delete isel; + QScopedPointer isel(ctx->engine->iselFactory->create(ctx->engine, &module)); + VM::Function *vmf = isel->run(irf); - ctx->thisObject = Value::fromObject(ctx->engine->newScriptFunction(ctx->engine->rootContext, irf)); + ctx->thisObject = Value::fromObject(ctx->engine->newScriptFunction(ctx->engine->rootContext, vmf)); return ctx->thisObject; } diff --git a/qv4ir.cpp b/qv4ir.cpp index 2a9414bb5d..3cceeb8680 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -393,17 +393,12 @@ Function *Module::newFunction(const QString &name) Module::~Module() { - foreach (Function *f, functions) - f->releaseModuleManagedData(); + foreach (Function *f, functions) { + delete f; + } } Function::~Function() -{ - delete[] codeData; -} - - -void Function::releaseModuleManagedData() { // destroy the Stmt::Data blocks manually, because memory pool cleanup won't // call the Stmt destructors. diff --git a/qv4ir_p.h b/qv4ir_p.h index 14c73e5b1a..c6d05d21c4 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -602,10 +602,6 @@ struct Function { QList formals; QList locals; - VM::Value (*code)(VM::ExecutionContext *, const uchar *); - const uchar *codeData; - JSC::MacroAssemblerCodeRef codeRef; - int insideWith; bool hasDirectEval: 1; @@ -619,8 +615,6 @@ struct Function { , pool(&module->pool) , tempCount(0) , maxNumberOfArguments(0) - , code(0) - , codeData(0) , insideWith(0) , hasDirectEval(false) , hasNestedFunctions(false) @@ -628,7 +622,6 @@ struct Function { { this->name = newString(name); } ~Function(); - void releaseModuleManagedData(); enum BasicBlockInsertMode { InsertBlock, @@ -644,8 +637,6 @@ struct Function { inline BasicBlock *insertBasicBlock(BasicBlock *block) { basicBlocks.append(block); return block; } void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); - - inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval; } }; struct BasicBlock { diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index bcd7585c64..d107a6be0c 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -313,7 +313,7 @@ static void printDisassembledOutputWithCalls(const char* output, const QHash > it(_patches); while (it.hasNext()) { @@ -343,7 +343,7 @@ void Assembler::link() #endif QByteArray name = _function->name->toUtf8(); - _function->codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); + vmFunc->codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); WTF::setDataFile(stderr); #if OS(LINUX) @@ -354,14 +354,14 @@ void Assembler::link() free(disasmOutput); #endif } else { - _function->codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); + vmFunc->codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); } - _function->code = (Value (*)(VM::ExecutionContext *, const uchar *)) _function->codeRef.code().executableAddress(); + vmFunc->code = (Value (*)(VM::ExecutionContext *, const uchar *)) vmFunc->codeRef.code().executableAddress(); } -InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine) - : _engine(engine) +InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module) + : EvalInstructionSelection(engine, module) , _block(0) , _function(0) , _asm(0) @@ -373,7 +373,7 @@ InstructionSelection::~InstructionSelection() delete _asm; } -void InstructionSelection::operator()(IR::Function *function) +VM::Function *InstructionSelection::run(IR::Function *function) { qSwap(_function, function); Assembler* oldAssembler = _asm; @@ -417,16 +417,19 @@ void InstructionSelection::operator()(IR::Function *function) #endif _asm->ret(); - _asm->link(); + VM::Function *vmFunc = vmFunction(_function); + _asm->link(vmFunc); qSwap(_function, function); delete _asm; _asm = oldAssembler; + + return vmFunc; } String *InstructionSelection::identifier(const QString &s) { - return _engine->identifier(s); + return engine()->identifier(s); } void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) @@ -662,16 +665,18 @@ void InstructionSelection::visitMove(IR::Move *s) return; } else if (IR::String *str = s->source->asString()) { Address dest = _asm->loadTempAddress(Assembler::ScratchRegister, t); - Value v = Value::fromString(_engine->newString(*str->value)); + Value v = Value::fromString(engine()->newString(*str->value)); _asm->storeValue(v, dest); return; } else if (IR::RegExp *re = s->source->asRegExp()) { Address dest = _asm->loadTempAddress(Assembler::ScratchRegister, t); - Value v = Value::fromObject(_engine->newRegExpObject(*re->value, re->flags)); + Value v = Value::fromObject(engine()->newRegExpObject(*re->value, re->flags)); _asm->storeValue(v, dest); return; } else if (IR::Closure *clos = s->source->asClosure()) { - generateFunctionCall(t, __qmljs_init_closure, Assembler::TrustedImmPtr(clos->value), Assembler::ContextRegister); + VM::Function *vmFunc = vmFunction(clos->value); + assert(vmFunc); + generateFunctionCall(t, __qmljs_init_closure, Assembler::TrustedImmPtr(vmFunc), Assembler::ContextRegister); return; } else if (IR::New *ctor = s->source->asNew()) { if (ctor->base->asName()) { diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 7a478ae499..243b066271 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -630,7 +630,7 @@ public: return Jump(); } - void link(); + void link(VM::Function *vmFunc); private: IR::Function* _function; @@ -642,12 +642,10 @@ private: class InstructionSelection: protected IR::StmtVisitor, public EvalInstructionSelection { public: - InstructionSelection(VM::ExecutionEngine *engine); + InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); ~InstructionSelection(); - virtual void run(IR::Function *function) - { this->operator()(function); } - void operator()(IR::Function *function); + virtual VM::Function *run(IR::Function *function); protected: typedef Assembler::Address Address; @@ -710,8 +708,6 @@ private: #define callRuntimeMethod(result, function, ...) \ callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__) - - VM::ExecutionEngine *_engine; IR::BasicBlock *_block; IR::Function* _function; Assembler* _asm; @@ -721,8 +717,8 @@ class ISelFactory: public EvalISelFactory { public: virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine) - { return new InstructionSelection(engine); } + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module) + { return new InstructionSelection(engine, module); } }; } // end of namespace MASM diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 175cea547e..5a668fa246 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -1,9 +1,46 @@ +#include "debugging.h" +#include "qmljs_engine.h" +#include "qv4ir_p.h" #include "qv4isel_p.h" +#include "qv4isel_util_p.h" + +#include + +#include using namespace QQmlJS; +EvalInstructionSelection::EvalInstructionSelection(VM::ExecutionEngine *engine, IR::Module *module) + : _engine(engine) +{ + assert(engine); + assert(module); + + foreach (IR::Function *f, module->functions) + _irToVM.insert(f, createFunctionMapping(engine, f)); +} + EvalInstructionSelection::~EvalInstructionSelection() {} EvalISelFactory::~EvalISelFactory() {} + +VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngine *engine, IR::Function *irFunction) +{ + VM::Function *vmFunction = engine->newFunction(irFunction->name ? *irFunction->name : QString()); + vmFunction->hasDirectEval = irFunction->hasDirectEval; + vmFunction->isStrict = irFunction->isStrict; + + foreach (const QString *formal, irFunction->formals) + if (formal) + vmFunction->formals.append(*formal); + foreach (const QString *local, irFunction->locals) + if (local) + vmFunction->locals.append(*local); + + if (engine->debugger) + engine->debugger->mapFunction(vmFunction, irFunction); + + return vmFunction; +} diff --git a/qv4isel_p.h b/qv4isel_p.h index e8c6d387b7..109ff795a5 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -31,6 +31,7 @@ #define QV4ISEL_P_H #include +#include namespace QQmlJS { @@ -41,21 +42,32 @@ struct Module; namespace VM { struct ExecutionEngine; +struct Function; } // namespace VM class EvalInstructionSelection { public: + EvalInstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); virtual ~EvalInstructionSelection() = 0; - virtual void run(IR::Function *function) = 0; + virtual VM::Function *run(IR::Function *function) = 0; + +protected: + VM::Function *createFunctionMapping(VM::ExecutionEngine *engine, IR::Function *irFunction); + VM::Function *vmFunction(IR::Function *f) const { return _irToVM[f]; } + VM::ExecutionEngine *engine() const { return _engine; } + +private: + VM::ExecutionEngine *_engine; + QHash _irToVM; }; class EvalISelFactory { public: virtual ~EvalISelFactory() = 0; - virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine) = 0; + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module) = 0; }; } // namespace QQmlJS -- cgit v1.2.3 From 834d0b72dcb4f785699d2b9b476bc9a78f5574fb Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 08:22:06 +0100 Subject: Don't allow this as LHS operand Change-Id: I18a16721312b07485d0671bf072da27dc97490a8 Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 9f900f9e94..caf891566d 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -260,7 +260,7 @@ void ExecutionContext::setProperty(String *name, Value value) if (ctx->setMutableBinding(this, name, value)) return; } - if (strictMode) + if (strictMode || name == engine->id_this) throwReferenceError(Value::fromString(name)); engine->globalObject.objectValue()->__put__(this, name, value); } -- cgit v1.2.3 From 463ed070beb7cc5ad15692b6ea2cc403695b2a49 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 08:28:08 +0100 Subject: Properly set the prototype for regexp objects Clean up the code, so that regexp's get instantiated by the ExecutionEngine Change-Id: Iacc8d9fee0427342156747d6e8814d7660bdbb1a Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 10 ++++++++-- qmljs_engine.h | 3 ++- qv4ecmaobjects.cpp | 12 +++++------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index a29088f68e..6e837e21ba 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -357,7 +357,7 @@ FunctionObject *ExecutionEngine::newDateCtor(ExecutionContext *ctx) return new (memoryManager) DateCtor(ctx); } -Object *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) +RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) { bool global = (flags & IR::RegExp::RegExp_Global); QRegularExpression::PatternOptions options = 0; @@ -366,7 +366,13 @@ Object *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) if (flags & IR::RegExp::RegExp_Multiline) options |= QRegularExpression::MultilineOption; - Object *object = new (memoryManager) RegExpObject(QRegularExpression(pattern, options), global); + QRegularExpression re(pattern, options); + return newRegExpObject(re, global); +} + +RegExpObject *ExecutionEngine::newRegExpObject(const QRegularExpression &re, bool global) +{ + RegExpObject *object = new (memoryManager) RegExpObject(re, global); object->prototype = regExpPrototype; return object; } diff --git a/qmljs_engine.h b/qmljs_engine.h index eef62a3249..bcaff099e9 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -193,7 +193,8 @@ struct ExecutionEngine Object *newDateObject(const Value &value); FunctionObject *newDateCtor(ExecutionContext *ctx); - Object *newRegExpObject(const QString &pattern, int flags); + RegExpObject *newRegExpObject(const QString &pattern, int flags); + RegExpObject *newRegExpObject(const QRegularExpression &re, bool global); FunctionObject *newRegExpCtor(ExecutionContext *ctx); Object *newErrorObject(const Value &value); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index fb3f6d7df7..82d20f72c7 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2726,18 +2726,15 @@ RegExpCtor::RegExpCtor(ExecutionContext *scope) Value RegExpCtor::construct(ExecutionContext *ctx) { -// if (ctx->argumentCount > 2) { -// ctx->throwTypeError(); -// return; -// } - Value r = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); Value f = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); if (RegExpObject *re = r.asRegExpObject()) { if (!f.isUndefined()) ctx->throwTypeError(); - return Value::fromObject(new (ctx->engine->memoryManager) RegExpObject(re->value, false)); + RegExpObject *o = ctx->engine->newRegExpObject(re->value, re->global); + ctx->thisObject = Value::fromObject(o); + return ctx->thisObject; } if (r.isUndefined()) @@ -2767,7 +2764,8 @@ Value RegExpCtor::construct(ExecutionContext *ctx) if (!re.isValid()) ctx->throwTypeError(); - ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) RegExpObject(re, global)); + RegExpObject *o = ctx->engine->newRegExpObject(re, global); + ctx->thisObject = Value::fromObject(o); return ctx->thisObject; } -- cgit v1.2.3 From 446bea86f5753bccd1772347aa1e753ddf2b44f8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 09:00:19 +0100 Subject: Fix hasProperty internal method Change-Id: Ifd0b19d6ff011bb85475e6654681a022c8f58e2d Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 431d508160..d7d0365db3 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -255,8 +255,8 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) // Section 8.12.6 bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const { - if (members) - return members->find(name) != 0; + if (members && members->find(name) != 0) + return true; return prototype ? prototype->__hasProperty__(ctx, name) : false; } -- cgit v1.2.3 From 249f697824275598c85a0ca8f618e6e558a75ade Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 09:00:30 +0100 Subject: Small cleanup Change-Id: Ia25515e5f5e9af561c66589a2aa0c524167719ff Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index a00ac6c3c6..4c200b251f 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -222,8 +222,8 @@ Value __qmljs_instanceof(Value left, Value right, ExecutionContext *ctx) Value __qmljs_in(Value left, Value right, ExecutionContext *ctx) { if (right.isObject()) { - Value s = __qmljs_to_string(left, ctx); - bool r = right.objectValue()->__hasProperty__(ctx, s.stringValue()); + String *s = left.toString(ctx); + bool r = right.objectValue()->__hasProperty__(ctx, s); return Value::fromBoolean(r); } else { return __qmljs_throw_type_error(ctx); -- cgit v1.2.3 From 3c397f6b2afabe7323141dd68cfb38d66761fc3f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 18:21:36 +0100 Subject: Remove references to deleted basic blocks When linearizing, we can sometimes remove some basic blocks that are never being jumped to. In this case we also need to clean up the back references to these blocks from other blocks. This fixes a valgrind error with SHOW_CODE=1 Change-Id: I07d74cef24d6cf2c8bcc1e748e314a3a5b5ed60a Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index d9ac31d91e..a16d8c4d9e 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1490,8 +1490,14 @@ void Codegen::linearize(IR::Function *function) trace.append(exitBlock); foreach (IR::BasicBlock *b, function->basicBlocks) - if (!trace.contains(b)) + if (!trace.contains(b)) { + foreach (IR::BasicBlock *out, b->out) { + int idx = out->in.indexOf(b); + assert(idx >= 0); + out->in.remove(idx); + } delete b; + } function->basicBlocks = trace; #ifndef QV4_NO_LIVENESS -- cgit v1.2.3 From d597083c012274d9a591e0805e2d892393cc9e89 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 18:35:42 +0100 Subject: Use a new and empty cleanup list for try statements when entering a function The cleanup list is local to a function definition. Otherwise inner functions would create cleanup code for try statements in outer functions leading to crashes. Change-Id: I5d35893b0ea6b0692cda44d5b34b0bb3dfc93fdd Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index a16d8c4d9e..a6c1f7f4ce 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1613,6 +1613,8 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, { qSwap(_mode, mode); // enter function code. + TryCleanup *tryCleanup = 0; + enterEnvironment(ast); IR::Function *function = _module->newFunction(name); if (_debugger) @@ -1665,6 +1667,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, qSwap(_exitBlock, exitBlock); qSwap(_throwBlock, throwBlock); qSwap(_returnAddress, returnAddress); + qSwap(_tryCleanup, tryCleanup); for (FormalParameterList *it = formals; it; it = it->next) { _function->RECEIVE(it->name.toString()); @@ -1694,6 +1697,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, qSwap(_exitBlock, exitBlock); qSwap(_throwBlock, throwBlock); qSwap(_returnAddress, returnAddress); + qSwap(_tryCleanup, tryCleanup); leaveEnvironment(); -- cgit v1.2.3 From 363a7b57d87b72ffd906dfb437774a3cdb9c3bda Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 19:06:19 +0100 Subject: Throw a type error if defaultValue doesn't lead to a primitive type As specified in 8.12.8, we need to throw a type error if calling [[defaultValue]] on an object doesn't convert to a primitive type. Change-Id: I6b5db91a36a21c6037998efb15646d00d49b5d4c Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 4c200b251f..55258fd06d 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -509,6 +509,7 @@ Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int type return r; } + ctx->throwTypeError(); return Value::undefinedValue(); } -- cgit v1.2.3 From cdd2892007db685207ad524d0e8d85cf1fc5beb8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 19:18:40 +0100 Subject: Fix a bug in __qmljs_string_to_number The check whether we successfully converted the whole string was broken, leading to lots of NaN's when converting. Change-Id: Iea0c37e5900e4fe1a1d0adca9a91e76aeb544336 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 55258fd06d..25a3c11ec0 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -442,7 +442,7 @@ double __qmljs_string_to_number(ExecutionContext *, String *string) const char *begin = ba.constData(); const char *end = 0; double d = qstrtod(begin, &end, &ok); - if (end - begin != ba.size() - 1) { + if (end - begin != ba.size()) { if (ba == "Infinity") d = INFINITY; else -- cgit v1.2.3 From 2d8bd32e5d0072213db9f2a3cf3e33589b7a9c05 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 19:41:10 +0100 Subject: Fix some smaller bugs in toPropertyDescriptor This should now be fully compliant with 8.10.5 Change-Id: I4afacb95a9ec0eb9366181da9dbeb74a5c34c55b Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 82d20f72c7..3b042cc7dd 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -892,23 +892,13 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Prope desc->configurable = PropertyDescriptor::Undefined; if (o->__hasProperty__(ctx, ctx->engine->id_configurable)) - desc->enumberable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_configurable), ctx) ? PropertyDescriptor::Set : PropertyDescriptor::Unset; - - desc->writable = PropertyDescriptor::Undefined; - if (o->__hasProperty__(ctx, ctx->engine->id_writable)) - desc->enumberable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_writable), ctx) ? PropertyDescriptor::Set : PropertyDescriptor::Unset; - - if (o->__hasProperty__(ctx, ctx->engine->id_value)) { - desc->value = o->__get__(ctx, ctx->engine->id_value); - desc->type = PropertyDescriptor::Data; - } + desc->configurable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_configurable), ctx) ? PropertyDescriptor::Set : PropertyDescriptor::Unset; + desc->get = 0; if (o->__hasProperty__(ctx, ctx->engine->id_get)) { Value get = o->__get__(ctx, ctx->engine->id_get); FunctionObject *f = get.asFunctionObject(); if (f) { - if (desc->isWritable() || desc->isData()) - __qmljs_throw_type_error(ctx); desc->get = f; } else if (!get.isUndefined()) { __qmljs_throw_type_error(ctx); @@ -918,12 +908,11 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Prope desc->type = PropertyDescriptor::Accessor; } + desc->set = 0; if (o->__hasProperty__(ctx, ctx->engine->id_set)) { Value get = o->__get__(ctx, ctx->engine->id_set); FunctionObject *f = get.asFunctionObject(); if (f) { - if (desc->isWritable() || desc->isData()) - __qmljs_throw_type_error(ctx); desc->set = f; } else if (!get.isUndefined()) { __qmljs_throw_type_error(ctx); @@ -932,6 +921,24 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Prope } desc->type = PropertyDescriptor::Accessor; } + + desc->writable = PropertyDescriptor::Undefined; + if (o->__hasProperty__(ctx, ctx->engine->id_writable)) { + if (desc->isAccessor()) + __qmljs_throw_type_error(ctx); + desc->writable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_writable), ctx) ? PropertyDescriptor::Set : PropertyDescriptor::Unset; + // writable forces it to be a data descriptor + desc->type = PropertyDescriptor::Data; + desc->value = Value::undefinedValue(); + } + + if (o->__hasProperty__(ctx, ctx->engine->id_value)) { + if (desc->isAccessor()) + __qmljs_throw_type_error(ctx); + desc->value = o->__get__(ctx, ctx->engine->id_value); + desc->type = PropertyDescriptor::Data; + } + } -- cgit v1.2.3 From fc9e8852c3d2b323b119c9151f13c41bc932608f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 8 Dec 2012 21:12:11 +0100 Subject: Avoid uninitialized ExecutionContext::current pointer Change-Id: I63fb1fbb39786fc349ccc121a25951b15ec31d57 Reviewed-by: Lars Knoll --- qmljs_environment.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index caf891566d..2a49a67588 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -385,6 +385,7 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha engine = parent->engine; this->parent = parent; + engine->current = this; function = f; strictMode = f->strictMode; @@ -429,7 +430,9 @@ void ExecutionContext::leaveCallContext() delete[] locals; locals = 0; } + engine->current = parent; parent = 0; + function = 0; if (engine->debugger) engine->debugger->justLeft(this); -- cgit v1.2.3 From 47b01bb7e5ac8812470d5840534aeaa2eb342164 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 20:34:19 +0100 Subject: Fix indentation Change-Id: I8ccb47cae3214ab2ac8ed51657856f51fc6c4e95 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index a6c1f7f4ce..35e1014d52 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -510,7 +510,7 @@ IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr) case IR::OpNot: return _block->CONST(IR::BoolType, !c->value); case IR::OpUMinus: - return _block->CONST(IR::NumberType, -c->value); + return _block->CONST(IR::NumberType, -c->value); case IR::OpUPlus: return expr; case IR::OpCompl: -- cgit v1.2.3 From 5cf108e7a530229f4c9fa2751ec0aac42924fea6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 20:34:31 +0100 Subject: Correctly handle negative 0 -0 and +0 are two distinct numbers. Since integers only have one 0 value, we need to convert the number to double when negating a 0 Change-Id: I915c4bd7168eece947fa91c6b65137a873d4f75a Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 3 ++- qv4isel_masm.cpp | 25 +------------------------ qv4isel_util_p.h | 3 ++- 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 1021d4e89d..2de6f19f55 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -457,7 +457,8 @@ inline Value __qmljs_uminus(Value value, ExecutionContext *ctx) { TRACE1(value); - if (value.isInteger()) + // +0 != -0, so we need to convert to double when negating 0 + if (value.isInteger() && value.integerValue()) return Value::fromInt32(-value.integerValue()); double n = __qmljs_to_number(value, ctx); return Value::fromDouble(-n); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index d107a6be0c..b3d7b781e3 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -634,30 +634,7 @@ void InstructionSelection::visitMove(IR::Move *s) return; } else if (IR::Const *c = s->source->asConst()) { Address dest = _asm->loadTempAddress(Assembler::ScratchRegister, t); - Value v; - switch (c->type) { - case IR::NullType: - v = Value::nullValue(); - break; - case IR::UndefinedType: - v = Value::undefinedValue(); - break; - case IR::BoolType: - v = Value::fromBoolean(c->value != 0); - break; - case IR::NumberType: { - int ival = (int)c->value; - if (ival == c->value) { - v = Value::fromInt32(ival); - } else { - v = Value::fromDouble(c->value); - } - } - break; - default: - Q_UNIMPLEMENTED(); - assert(!"TODO"); - } + Value v = convertToValue(c); _asm->storeValue(v, dest); return; } else if (IR::Temp *t2 = s->source->asTemp()) { diff --git a/qv4isel_util_p.h b/qv4isel_util_p.h index ff6cc210ec..693af03e88 100644 --- a/qv4isel_util_p.h +++ b/qv4isel_util_p.h @@ -46,7 +46,8 @@ inline VM::Value convertToValue(IR::Const *c) return VM::Value::fromBoolean(c->value != 0); case IR::NumberType: { int ival = (int)c->value; - if (ival == c->value) { + // +0 != -0, so we need to convert to double when negating 0 + if (ival == c->value && c->value != -0) { return VM::Value::fromInt32(ival); } else { return VM::Value::fromDouble(c->value); -- cgit v1.2.3 From 018af32fc2d902ff47482d0501195f7e0739cdcf Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 11 Dec 2012 14:43:50 +0100 Subject: Cleanup: Added a storeValue() overload that operates on an IR::Temp* Change-Id: Iab23eaa41f4ef4d3f99dccd6d2075fa4ba8e918e Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 19 ++++++++++--------- qv4isel_masm_p.h | 2 ++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index b3d7b781e3..09147e4bcd 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -117,6 +117,12 @@ void Assembler::copyValue(Result result, Source source) #endif } +void Assembler::storeValue(VM::Value value, IR::Temp* destination) +{ + Address addr = loadTempAddress(ScratchRegister, destination); + storeValue(value, addr); +} + void Assembler::enterStandardStackFrame(int locals) { #if CPU(ARM) @@ -471,9 +477,7 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu return; } else if (call->args->expr->asTemp()){ // ### should throw in strict mode - Address dest = _asm->loadTempAddress(Assembler::ScratchRegister, result); - Value v = Value::fromBoolean(false); - _asm->storeValue(v, dest); + _asm->storeValue(Value::fromBoolean(false), result); return; } break; @@ -633,22 +637,19 @@ void InstructionSelection::visitMove(IR::Move *s) } return; } else if (IR::Const *c = s->source->asConst()) { - Address dest = _asm->loadTempAddress(Assembler::ScratchRegister, t); Value v = convertToValue(c); - _asm->storeValue(v, dest); + _asm->storeValue(t, dest); return; } else if (IR::Temp *t2 = s->source->asTemp()) { _asm->copyValue(t, t2); return; } else if (IR::String *str = s->source->asString()) { - Address dest = _asm->loadTempAddress(Assembler::ScratchRegister, t); Value v = Value::fromString(engine()->newString(*str->value)); - _asm->storeValue(v, dest); + _asm->storeValue(v, t); return; } else if (IR::RegExp *re = s->source->asRegExp()) { - Address dest = _asm->loadTempAddress(Assembler::ScratchRegister, t); Value v = Value::fromObject(engine()->newRegExpObject(*re->value, re->flags)); - _asm->storeValue(v, dest); + _asm->storeValue(v, t); return; } else if (IR::Closure *clos = s->source->asClosure()) { VM::Function *vmFunc = vmFunction(clos->value); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 243b066271..ca32511ae4 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -370,6 +370,8 @@ public: #endif } + void storeValue(VM::Value value, IR::Temp* temp); + void enterStandardStackFrame(int locals); void leaveStandardStackFrame(int locals); -- cgit v1.2.3 From d94c57574ac70e65c229fd3a84ee65ba8b335f64 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 12 Dec 2012 13:23:02 +0100 Subject: Fix masm compilation. Change-Id: I74f08976c046f7e8a6c16c786e6d32720ad88485 Reviewed-by: Simon Hausmann --- qv4isel_masm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 09147e4bcd..d667fa5cb3 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -638,7 +638,7 @@ void InstructionSelection::visitMove(IR::Move *s) return; } else if (IR::Const *c = s->source->asConst()) { Value v = convertToValue(c); - _asm->storeValue(t, dest); + _asm->storeValue(v, t); return; } else if (IR::Temp *t2 = s->source->asTemp()) { _asm->copyValue(t, t2); -- cgit v1.2.3 From 2c8b749ee7cb98979fdc24fb36af7f0de13b4e0f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 22:57:06 +0100 Subject: Fix assertion Change-Id: I2c79d7c8c5f8624a5cfd5cb1c5af1488ccd5aada Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 35e1014d52..48027863f3 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1493,8 +1493,8 @@ void Codegen::linearize(IR::Function *function) if (!trace.contains(b)) { foreach (IR::BasicBlock *out, b->out) { int idx = out->in.indexOf(b); - assert(idx >= 0); - out->in.remove(idx); + if (idx >= 0) + out->in.remove(idx); } delete b; } -- cgit v1.2.3 From 8b9a17e97c67eac5163b25eab4ededdfdf6e9354 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 20:48:11 +0100 Subject: Value properties of the Number constructor are readonly See 15.7.3.2 - 15.7.3.6 Change-Id: I39cace57456ecce9532ba6547b3dab5735fd874d Reviewed-by: Erik Verbruggen --- qv4ecmaobjects.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 3b042cc7dd..4aa9c44027 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1317,15 +1317,28 @@ Value NumberCtor::call(ExecutionContext *ctx) void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->__put__(ctx, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); - ctor.objectValue()->__put__(ctx, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); - ctor.objectValue()->__put__(ctx, QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf())); - ctor.objectValue()->__put__(ctx, QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308)); + + PropertyDescriptor desc; + desc.type = PropertyDescriptor::Data; + desc.writable = PropertyDescriptor::Unset; + desc.enumberable = PropertyDescriptor::Unset; + desc.configurable = PropertyDescriptor::Unset; + + desc.value = Value::fromDouble(qSNaN()); + ctor.objectValue()->__defineOwnProperty__(ctx, QStringLiteral("NaN"), &desc); + desc.value = Value::fromDouble(-qInf()); + ctor.objectValue()->__defineOwnProperty__(ctx, QStringLiteral("NEGATIVE_INFINITY"), &desc); + desc.value = Value::fromDouble(qInf()); + ctor.objectValue()->__defineOwnProperty__(ctx, QStringLiteral("POSITIVE_INFINITY"), &desc); + desc.value = Value::fromDouble(1.7976931348623158e+308); + ctor.objectValue()->__defineOwnProperty__(ctx, QStringLiteral("MAX_VALUE"), &desc); + #ifdef __INTEL_COMPILER # pragma warning( push ) # pragma warning(disable: 239) #endif - ctor.objectValue()->__put__(ctx, QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324)); + desc.value = Value::fromDouble(5e-324); + ctor.objectValue()->__defineOwnProperty__(ctx, QStringLiteral("MIN_VALUE"), &desc); #ifdef __INTEL_COMPILER # pragma warning( pop ) #endif -- cgit v1.2.3 From 42ebe165489b5429d1e1dc9f850bbf7d37dd127f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 22:46:57 +0100 Subject: Fix increment and decrement operators These operators have semantics that are different from (foo + 1), as they always convert the LHS to a number first. Change-Id: I3fb4a1a328e3dfcb334875435c3cec90d01b67dd Reviewed-by: Erik Verbruggen --- moth/qv4isel_moth.cpp | 2 ++ qmljs_runtime.cpp | 22 ++++++++++++++++++++++ qmljs_runtime.h | 2 ++ qv4codegen.cpp | 48 +++++++++++++++++++++++++++++++++++++++++------- qv4ir.cpp | 2 ++ qv4ir_p.h | 2 ++ qv4isel_masm.cpp | 4 ++++ 7 files changed, 75 insertions(+), 7 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 328fdce38c..5b00ccf552 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -653,6 +653,8 @@ void InstructionSelection::visitMove(IR::Move *s) case IR::OpUMinus: op = VM::__qmljs_uminus; break; case IR::OpUPlus: op = VM::__qmljs_uplus; break; case IR::OpCompl: op = VM::__qmljs_compl; break; + case IR::OpIncrement: op = VM::__qmljs_increment; break; + case IR::OpDecrement: op = VM::__qmljs_decrement; break; default: assert(!"unreachable"); break; } // switch diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 25a3c11ec0..5ef2c6c9f0 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -873,6 +873,28 @@ void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String * ctx->createMutableBinding(name, deletable); } +Value __qmljs_increment(Value value, ExecutionContext *ctx) +{ + TRACE1(value); + + if (value.isInteger()) + return Value::fromInt32(value.integerValue() + 1); + + double d = __qmljs_to_number(value, ctx); + return Value::fromDouble(d + 1); +} + +Value __qmljs_decrement(Value value, ExecutionContext *ctx) +{ + TRACE1(value); + + if (value.isInteger()) + return Value::fromInt32(value.integerValue() - 1); + + double d = __qmljs_to_number(value, ctx); + return Value::fromDouble(d - 1); +} + } // extern "C" diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 2de6f19f55..263c151145 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -174,6 +174,8 @@ Value __qmljs_uplus(Value value, ExecutionContext *ctx); Value __qmljs_uminus(Value value, ExecutionContext *ctx); Value __qmljs_compl(Value value, ExecutionContext *ctx); Value __qmljs_not(Value value, ExecutionContext *ctx); +Value __qmljs_increment(Value value, ExecutionContext *ctx); +Value __qmljs_decrement(Value value, ExecutionContext *ctx); Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index); Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 48027863f3..fef6b4e733 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -515,6 +515,10 @@ IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr) return expr; case IR::OpCompl: return _block->CONST(IR::NumberType, ~VM::Value::toInt32(c->value)); + case IR::OpIncrement: + return _block->CONST(IR::NumberType, c->value + 1); + case IR::OpDecrement: + return _block->CONST(IR::NumberType, c->value - 1); default: break; } @@ -567,6 +571,8 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) case IR::OpUMinus: case IR::OpUPlus: case IR::OpCompl: + case IR::OpIncrement: + case IR::OpDecrement: case IR::OpInvalid: break; } @@ -1310,13 +1316,21 @@ bool Codegen::visit(ObjectLiteral *ast) bool Codegen::visit(PostDecrementExpression *ast) { + // ### + // Throw a SyntaxError exception if the following conditions are all true: + // Type(lhs) is Reference is true + // IsStrictReference(lhs) is true + // Type(GetBase(lhs)) is Environment Record + // GetReferencedName(lhs) is either "eval" or "arguments" + + Result expr = expression(ast->base); if (_expr.accept(nx)) { - move(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub); + move(*expr, unop(IR::OpDecrement, *expr)); } else { const unsigned t = _block->newTemp(); move(_block->TEMP(t), *expr); - move(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub); + move(*expr, unop(IR::OpDecrement, _block->TEMP(t))); _expr.code = _block->TEMP(t); } return false; @@ -1324,13 +1338,20 @@ bool Codegen::visit(PostDecrementExpression *ast) bool Codegen::visit(PostIncrementExpression *ast) { + // ### + // Throw a SyntaxError exception if the following conditions are all true: + // Type(lhs) is Reference is true + // IsStrictReference(lhs) is true + // Type(GetBase(lhs)) is Environment Record + // GetReferencedName(lhs) is either "eval" or "arguments" + Result expr = expression(ast->base); if (_expr.accept(nx)) { - move(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd); + move(*expr, unop(IR::OpIncrement, *expr)); } else { const unsigned t = _block->newTemp(); move(_block->TEMP(t), *expr); - move(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd); + move(*expr, unop(IR::OpIncrement, _block->TEMP(t))); _expr.code = _block->TEMP(t); } return false; @@ -1338,8 +1359,15 @@ bool Codegen::visit(PostIncrementExpression *ast) bool Codegen::visit(PreDecrementExpression *ast) { + // ### + // Throw a SyntaxError exception if the following conditions are all true: + // Type(lhs) is Reference is true + // IsStrictReference(lhs) is true + // Type(GetBase(lhs)) is Environment Record + // GetReferencedName(lhs) is either "eval" or "arguments" + Result expr = expression(ast->expression); - move(*expr, _block->CONST(IR::NumberType, 1), IR::OpSub); + move(*expr, unop(IR::OpDecrement, *expr)); if (_expr.accept(nx)) { // nothing to do } else { @@ -1350,9 +1378,15 @@ bool Codegen::visit(PreDecrementExpression *ast) bool Codegen::visit(PreIncrementExpression *ast) { - Result expr = expression(ast->expression); - move(*expr, _block->CONST(IR::NumberType, 1), IR::OpAdd); + // ### + // Throw a SyntaxError exception if the following conditions are all true: + // Type(lhs) is Reference is true + // IsStrictReference(lhs) is true + // Type(GetBase(lhs)) is Environment Record + // GetReferencedName(lhs) is either "eval" or "arguments" + Result expr = expression(ast->expression); + move(*expr, unop(IR::OpIncrement, *expr)); if (_expr.accept(nx)) { // nothing to do } else { diff --git a/qv4ir.cpp b/qv4ir.cpp index 3cceeb8680..33b8b7721d 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -73,6 +73,8 @@ const char *opname(AluOp op) case OpUMinus: return "-"; case OpUPlus: return "+"; case OpCompl: return "~"; + case OpIncrement: return "++"; + case OpDecrement: return "--"; case OpBitAnd: return "&"; case OpBitOr: return "|"; diff --git a/qv4ir_p.h b/qv4ir_p.h index c6d05d21c4..b605e60b26 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -115,6 +115,8 @@ enum AluOp { OpUMinus, OpUPlus, OpCompl, + OpIncrement, + OpDecrement, OpBitAnd, OpBitOr, diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index d667fa5cb3..cabd1f7c5c 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -187,6 +187,8 @@ const Assembler::BinaryOperationInfo Assembler::binaryOperations[QQmlJS::IR::Las NULL_OP, // OpUMinus NULL_OP, // OpUPlus NULL_OP, // OpCompl + NULL_OP, // OpIncrement + NULL_OP, // OpDecrement INLINE_OP(__qmljs_bit_and, &Assembler::inline_and32, &Assembler::inline_and32), // OpBitAnd INLINE_OP(__qmljs_bit_or, &Assembler::inline_or32, &Assembler::inline_or32), // OpBitOr @@ -688,6 +690,8 @@ void InstructionSelection::visitMove(IR::Move *s) case IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break; case IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break; case IR::OpCompl: setOp(op, opName, __qmljs_compl); break; + case IR::OpIncrement: setOp(op, opName, __qmljs_increment); break; + case IR::OpDecrement: setOp(op, opName, __qmljs_decrement); break; default: assert(!"unreachable"); break; } // switch -- cgit v1.2.3 From 949303cd7da0edcab235bc19d5fad7d4af7eaed3 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 23:43:53 +0100 Subject: Fix isNaN and isFinite We need to convert objects to numbers before doing the check. Change-Id: Ie25128b6145845a3eb3e0098f5c5fc09f2be6830 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 18 ++++++++++++------ qmljs_value.h | 7 +++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index d7d0365db3..12269f2030 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -670,11 +670,14 @@ IsNaNFunction::IsNaNFunction(ExecutionContext *scope) name = scope->engine->newString(QLatin1String("isNaN")); } -Value IsNaNFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, Value *args, int /*argc*/) +Value IsNaNFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int /*argc*/) { - // TODO: see if we can generate code for this directly const Value &v = args[0]; - return Value::fromBoolean(v.isDouble() ? std::isnan(v.doubleValue()) : false); + if (v.integerCompatible()) + return Value::fromBoolean(false); + + double d = v.toNumber(context); + return Value::fromBoolean(std::isnan(d)); } /// isFinite [15.1.2.5] @@ -684,11 +687,14 @@ IsFiniteFunction::IsFiniteFunction(ExecutionContext *scope) name = scope->engine->newString(QLatin1String("isFinite")); } -Value IsFiniteFunction::call(ExecutionContext * /*context*/, Value /*thisObject*/, Value *args, int /*argc*/) +Value IsFiniteFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int /*argc*/) { - // TODO: see if we can generate code for this directly const Value &v = args[0]; - return Value::fromBoolean(v.isDouble() ? std::isfinite(v.doubleValue()) : true); + if (v.integerCompatible()) + return Value::fromBoolean(true); + + double d = v.toNumber(context); + return Value::fromBoolean(std::isfinite(d)); } diff --git a/qmljs_value.h b/qmljs_value.h index 3d29deb326..7f095caf87 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -205,6 +205,10 @@ struct Value inline bool isPrimitive() const { return !isObject(); } #if CPU(X86_64) + inline bool integerCompatible() const { + const quint64 mask = quint64(ConvertibleToInt) << 32; + return (val & mask) == mask; + } static inline bool integerCompatible(Value a, Value b) { const quint64 mask = quint64(ConvertibleToInt) << 32; return ((a.val & b.val) & mask) == mask; @@ -214,6 +218,9 @@ struct Value return ((a.val | b.val) & mask) != mask; } #else + inline bool integerCompatible() const { + return (tag & ConvertibleToInt) == ConvertibleToInt; + } static inline bool integerCompatible(Value a, Value b) { return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; } -- cgit v1.2.3 From c28f8c7b409fdd7214bb721c4192374786ec682f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 13 Dec 2012 00:53:04 +0100 Subject: Fix access rights for builtin properties According to the spec all builtin properties have writable: true, enumerable: false and configurable:true by default. This is what is now being used. Some constants have all attributes set to false, and there is an extra method for setting these readonly properties. Change-Id: If5ba875bcc9f1644aa8a07a2d9b37716bf228e12 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 57 ++++---- qmljs_objects.cpp | 46 +++++-- qmljs_objects.h | 8 +- qv4ecmaobjects.cpp | 379 +++++++++++++++++++++++++---------------------------- 4 files changed, 246 insertions(+), 244 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 6e837e21ba..45db9fcb2b 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -179,42 +179,33 @@ ExecutionEngine::ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory * rootContext->activation = glo; rootContext->thisObject = Value::fromObject(glo); - glo->__put__(rootContext, identifier(QStringLiteral("Object")), objectCtor); - glo->__put__(rootContext, identifier(QStringLiteral("String")), stringCtor); - glo->__put__(rootContext, identifier(QStringLiteral("Number")), numberCtor); - glo->__put__(rootContext, identifier(QStringLiteral("Boolean")), booleanCtor); - glo->__put__(rootContext, identifier(QStringLiteral("Array")), arrayCtor); - glo->__put__(rootContext, identifier(QStringLiteral("Function")), functionCtor); - glo->__put__(rootContext, identifier(QStringLiteral("Date")), dateCtor); - glo->__put__(rootContext, identifier(QStringLiteral("RegExp")), regExpCtor); - glo->__put__(rootContext, identifier(QStringLiteral("Error")), errorCtor); - glo->__put__(rootContext, identifier(QStringLiteral("EvalError")), evalErrorCtor); - glo->__put__(rootContext, identifier(QStringLiteral("RangeError")), rangeErrorCtor); - glo->__put__(rootContext, identifier(QStringLiteral("ReferenceError")), referenceErrorCtor); - glo->__put__(rootContext, identifier(QStringLiteral("SyntaxError")), syntaxErrorCtor); - glo->__put__(rootContext, identifier(QStringLiteral("TypeError")), typeErrorCtor); - glo->__put__(rootContext, identifier(QStringLiteral("URIError")), uRIErrorCtor); - glo->__put__(rootContext, identifier(QStringLiteral("Math")), Value::fromObject(newMathObject(rootContext))); - - PropertyDescriptor pd; - pd.type = PropertyDescriptor::Data; - pd.writable = PropertyDescriptor::Unset; - pd.enumberable = PropertyDescriptor::Unset; - pd.configurable = PropertyDescriptor::Unset; - - pd.value = Value::undefinedValue(); - glo->__defineOwnProperty__(rootContext, identifier(QStringLiteral("undefined")), &pd); - pd.value = Value::fromDouble(nan("")); - glo->__defineOwnProperty__(rootContext, identifier(QStringLiteral("NaN")), &pd); - pd.value = Value::fromDouble(INFINITY); - glo->__defineOwnProperty__(rootContext, identifier(QStringLiteral("Infinity")), &pd); - - glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new (memoryManager) EvalFunction(rootContext))); + glo->defineDefaultProperty(rootContext, QStringLiteral("Object"), objectCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("String"), stringCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("Number"), numberCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("Boolean"), booleanCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("Array"), arrayCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("Function"), functionCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("Date"), dateCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("RegExp"), regExpCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("Error"), errorCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("EvalError"), evalErrorCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("RangeError"), rangeErrorCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("ReferenceError"), referenceErrorCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("SyntaxError"), syntaxErrorCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("TypeError"), typeErrorCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("URIError"), uRIErrorCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("Math"), Value::fromObject(newMathObject(rootContext))); + + glo->defineReadonlyProperty(this, QStringLiteral("undefined"), Value::undefinedValue()); + glo->defineReadonlyProperty(this, QStringLiteral("NaN"), Value::fromDouble(nan(""))); + glo->defineReadonlyProperty(this, QStringLiteral("Infinity"), Value::fromDouble(INFINITY)); + + glo->defineDefaultProperty(rootContext, QStringLiteral("eval"), Value::fromObject(new (memoryManager) EvalFunction(rootContext))); // TODO: parseInt [15.1.2.2] // TODO: parseFloat [15.1.2.3] - glo->__put__(rootContext, identifier(QStringLiteral("isNaN")), Value::fromObject(new (memoryManager) IsNaNFunction(rootContext))); // isNaN [15.1.2.4] - glo->__put__(rootContext, identifier(QStringLiteral("isFinite")), Value::fromObject(new (memoryManager) IsFiniteFunction(rootContext))); // isFinite [15.1.2.5] + glo->defineDefaultProperty(rootContext, QStringLiteral("isNaN"), Value::fromObject(new (memoryManager) IsNaNFunction(rootContext))); + glo->defineDefaultProperty(rootContext, QStringLiteral("isFinite"), Value::fromObject(new (memoryManager) IsFiniteFunction(rootContext))); } ExecutionEngine::~ExecutionEngine() diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 12269f2030..8ab6e50178 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -95,13 +95,6 @@ void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &va __put__(ctx, ctx->engine->identifier(name), value); } -void Object::__put__(ExecutionContext *ctx, const QString &name, Value (*code)(ExecutionContext *), int count) -{ - Q_UNUSED(count); - String *nameStr = ctx->engine->newString(name); - __put__(ctx, name, Value::fromObject(ctx->engine->newNativeFunction(ctx, nameStr, code))); -} - Value Object::getValue(ExecutionContext *ctx, PropertyDescriptor *p) const { if (p->isData()) @@ -130,6 +123,43 @@ bool Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ct return inplaceBinOp(rhs, name, op, ctx); } +void Object::defineDefaultProperty(String *name, Value value) +{ + if (!members) + members = new PropertyTable(); + PropertyDescriptor *pd = members->insert(name); + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Set; + pd->enumberable = PropertyDescriptor::Unset; + pd->configurable = PropertyDescriptor::Set; + pd->value = value; +} + +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value value) +{ + defineDefaultProperty(context->engine->identifier(name), value); +} + +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int count) +{ + Q_UNUSED(count); + // ### FIX count + String *s = context->engine->identifier(name); + defineDefaultProperty(s, Value::fromObject(context->engine->newNativeFunction(context, s, code))); +} + +void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value) +{ + if (!members) + members = new PropertyTable(); + PropertyDescriptor *pd = members->insert(engine->identifier(name)); + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Unset; + pd->enumberable = PropertyDescriptor::Unset; + pd->configurable = PropertyDescriptor::Unset; + pd->value = value; +} + void Object::getCollectables(QVector &objects) { if (prototype) @@ -724,7 +754,7 @@ Value ErrorObject::__get__(ExecutionContext *ctx, String *name) void ErrorObject::setNameProperty(ExecutionContext *ctx) { - __put__(ctx, QLatin1String("name"), Value::fromString(ctx, className())); + defineDefaultProperty(ctx, QLatin1String("name"), Value::fromString(ctx, className())); } void ErrorObject::getCollectables(QVector &objects) diff --git a/qmljs_objects.h b/qmljs_objects.h index 1b0b67fab9..14218e7be1 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -457,12 +457,18 @@ struct Object: Managed { // helpers // void __put__(ExecutionContext *ctx, const QString &name, const Value &value); - void __put__(ExecutionContext *ctx, const QString &name, Value (*code)(ExecutionContext *), int count = 0); Value getValue(ExecutionContext *ctx, PropertyDescriptor *p) const; bool inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); + /* The spec default: Writable: true, Enumerable: false, Configurable: true */ + void defineDefaultProperty(String *name, Value value); + void defineDefaultProperty(ExecutionContext *context, const QString &name, Value value); + void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int count = 0); + /* Fixed: Writable: false, Enumerable: false, Configurable: false */ + void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value); + protected: virtual void getCollectables(QVector &objects); }; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 4aa9c44027..daee93ac2a 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -549,30 +549,30 @@ Value ObjectCtor::__get__(ExecutionContext *ctx, String *name) void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->__put__(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 0); - ctor.objectValue()->__put__(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 0); - ctor.objectValue()->__put__(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 0); - ctor.objectValue()->__put__(ctx, QStringLiteral("create"), method_create, 0); - ctor.objectValue()->__put__(ctx, QStringLiteral("defineProperty"), method_defineProperty, 0); - ctor.objectValue()->__put__(ctx, QStringLiteral("defineProperties"), method_defineProperties, 0); - ctor.objectValue()->__put__(ctx, QStringLiteral("seal"), method_seal, 0); - ctor.objectValue()->__put__(ctx, QStringLiteral("freeze"), method_freeze, 0); - ctor.objectValue()->__put__(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 0); - ctor.objectValue()->__put__(ctx, QStringLiteral("isSealed"), method_isSealed, 0); - ctor.objectValue()->__put__(ctx, QStringLiteral("isFrozen"), method_isFrozen, 0); - ctor.objectValue()->__put__(ctx, QStringLiteral("isExtensible"), method_isExtensible, 0); - ctor.objectValue()->__put__(ctx, QStringLiteral("keys"), method_keys, 0); - - __put__(ctx, QStringLiteral("constructor"), ctor); - __put__(ctx, QStringLiteral("toString"), method_toString, 0); - __put__(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); - __put__(ctx, QStringLiteral("valueOf"), method_valueOf, 0); - __put__(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 0); - __put__(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 0); - __put__(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 0); - __put__(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 0); - __put__(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 0); + ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 0); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 0); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 0); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("create"), method_create, 0); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 0); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 0); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("seal"), method_seal, 0); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("freeze"), method_freeze, 0); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 0); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 0); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 0); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 0); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("keys"), method_keys, 0); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + defineDefaultProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 0); + defineDefaultProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 0); + defineDefaultProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 0); + defineDefaultProperty(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 0); + defineDefaultProperty(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 0); } Value ObjectPrototype::method_getPrototypeOf(ExecutionContext *ctx) @@ -1006,29 +1006,29 @@ Value StringCtor::call(ExecutionContext *ctx) void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->__put__(ctx, QStringLiteral("fromCharCode"), method_fromCharCode); - - __put__(ctx, QStringLiteral("constructor"), ctor); - __put__(ctx, QStringLiteral("toString"), method_toString); - __put__(ctx, QStringLiteral("valueOf"), method_valueOf); - __put__(ctx, QStringLiteral("charAt"), method_charAt); - __put__(ctx, QStringLiteral("charCodeAt"), method_charCodeAt); - __put__(ctx, QStringLiteral("concat"), method_concat); - __put__(ctx, QStringLiteral("indexOf"), method_indexOf); - __put__(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf); - __put__(ctx, QStringLiteral("localeCompare"), method_localeCompare); - __put__(ctx, QStringLiteral("match"), method_match); - __put__(ctx, QStringLiteral("replace"), method_replace); - __put__(ctx, QStringLiteral("search"), method_search); - __put__(ctx, QStringLiteral("slice"), method_slice); - __put__(ctx, QStringLiteral("split"), method_split); - __put__(ctx, QStringLiteral("substr"), method_substr); - __put__(ctx, QStringLiteral("substring"), method_substring); - __put__(ctx, QStringLiteral("toLowerCase"), method_toLowerCase); - __put__(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); - __put__(ctx, QStringLiteral("toUpperCase"), method_toUpperCase); - __put__(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); + ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); + defineDefaultProperty(ctx, QStringLiteral("charAt"), method_charAt); + defineDefaultProperty(ctx, QStringLiteral("charCodeAt"), method_charCodeAt); + defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat); + defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf); + defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf); + defineDefaultProperty(ctx, QStringLiteral("localeCompare"), method_localeCompare); + defineDefaultProperty(ctx, QStringLiteral("match"), method_match); + defineDefaultProperty(ctx, QStringLiteral("replace"), method_replace); + defineDefaultProperty(ctx, QStringLiteral("search"), method_search); + defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice); + defineDefaultProperty(ctx, QStringLiteral("split"), method_split); + defineDefaultProperty(ctx, QStringLiteral("substr"), method_substr); + defineDefaultProperty(ctx, QStringLiteral("substring"), method_substring); + defineDefaultProperty(ctx, QStringLiteral("toLowerCase"), method_toLowerCase); + defineDefaultProperty(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); + defineDefaultProperty(ctx, QStringLiteral("toUpperCase"), method_toUpperCase); + defineDefaultProperty(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); } QString StringPrototype::getThisString(ExecutionContext *ctx) @@ -1316,40 +1316,29 @@ Value NumberCtor::call(ExecutionContext *ctx) void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); - PropertyDescriptor desc; - desc.type = PropertyDescriptor::Data; - desc.writable = PropertyDescriptor::Unset; - desc.enumberable = PropertyDescriptor::Unset; - desc.configurable = PropertyDescriptor::Unset; - - desc.value = Value::fromDouble(qSNaN()); - ctor.objectValue()->__defineOwnProperty__(ctx, QStringLiteral("NaN"), &desc); - desc.value = Value::fromDouble(-qInf()); - ctor.objectValue()->__defineOwnProperty__(ctx, QStringLiteral("NEGATIVE_INFINITY"), &desc); - desc.value = Value::fromDouble(qInf()); - ctor.objectValue()->__defineOwnProperty__(ctx, QStringLiteral("POSITIVE_INFINITY"), &desc); - desc.value = Value::fromDouble(1.7976931348623158e+308); - ctor.objectValue()->__defineOwnProperty__(ctx, QStringLiteral("MAX_VALUE"), &desc); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308)); #ifdef __INTEL_COMPILER # pragma warning( push ) # pragma warning(disable: 239) #endif - desc.value = Value::fromDouble(5e-324); - ctor.objectValue()->__defineOwnProperty__(ctx, QStringLiteral("MIN_VALUE"), &desc); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324)); #ifdef __INTEL_COMPILER # pragma warning( pop ) #endif - __put__(ctx, QStringLiteral("constructor"), ctor); - __put__(ctx, QStringLiteral("toString"), method_toString); - __put__(ctx, QStringLiteral("toLocalString"), method_toLocaleString); - __put__(ctx, QStringLiteral("valueOf"), method_valueOf); - __put__(ctx, QStringLiteral("toFixed"), method_toFixed); - __put__(ctx, QStringLiteral("toExponential"), method_toExponential); - __put__(ctx, QStringLiteral("toPrecision"), method_toPrecision); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); + defineDefaultProperty(ctx, QStringLiteral("toFixed"), method_toFixed); + defineDefaultProperty(ctx, QStringLiteral("toExponential"), method_toExponential); + defineDefaultProperty(ctx, QStringLiteral("toPrecision"), method_toPrecision); } Value NumberPrototype::method_toString(ExecutionContext *ctx) @@ -1506,10 +1495,10 @@ Value BooleanCtor::call(ExecutionContext *ctx) void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - __put__(ctx, QStringLiteral("constructor"), ctor); - __put__(ctx, QStringLiteral("toString"), method_toString); - __put__(ctx, QStringLiteral("valueOf"), method_valueOf); + ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); } Value BooleanPrototype::method_toString(ExecutionContext *ctx) @@ -1569,29 +1558,29 @@ Value ArrayCtor::call(ExecutionContext *ctx) void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - __put__(ctx, QStringLiteral("constructor"), ctor); - __put__(ctx, QStringLiteral("toString"), method_toString, 0); - __put__(ctx, QStringLiteral("toLocalString"), method_toLocaleString, 0); - __put__(ctx, QStringLiteral("concat"), method_concat, 1); - __put__(ctx, QStringLiteral("join"), method_join, 1); - __put__(ctx, QStringLiteral("pop"), method_pop, 0); - __put__(ctx, QStringLiteral("push"), method_push, 1); - __put__(ctx, QStringLiteral("reverse"), method_reverse, 0); - __put__(ctx, QStringLiteral("shift"), method_shift, 0); - __put__(ctx, QStringLiteral("slice"), method_slice, 2); - __put__(ctx, QStringLiteral("sort"), method_sort, 1); - __put__(ctx, QStringLiteral("splice"), method_splice, 2); - __put__(ctx, QStringLiteral("unshift"), method_unshift, 1); - __put__(ctx, QStringLiteral("indexOf"), method_indexOf, 0); - __put__(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 0); - __put__(ctx, QStringLiteral("every"), method_every, 0); - __put__(ctx, QStringLiteral("some"), method_some, 0); - __put__(ctx, QStringLiteral("forEach"), method_forEach, 0); - __put__(ctx, QStringLiteral("map"), method_map, 0); - __put__(ctx, QStringLiteral("filter"), method_filter, 0); - __put__(ctx, QStringLiteral("reduce"), method_reduce, 0); - __put__(ctx, QStringLiteral("reduceRight"), method_reduceRight, 0); + ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(ctx, QStringLiteral("join"), method_join, 1); + defineDefaultProperty(ctx, QStringLiteral("pop"), method_pop, 0); + defineDefaultProperty(ctx, QStringLiteral("push"), method_push, 1); + defineDefaultProperty(ctx, QStringLiteral("reverse"), method_reverse, 0); + defineDefaultProperty(ctx, QStringLiteral("shift"), method_shift, 0); + defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); + defineDefaultProperty(ctx, QStringLiteral("sort"), method_sort, 1); + defineDefaultProperty(ctx, QStringLiteral("splice"), method_splice, 2); + defineDefaultProperty(ctx, QStringLiteral("unshift"), method_unshift, 1); + defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 0); + defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 0); + defineDefaultProperty(ctx, QStringLiteral("every"), method_every, 0); + defineDefaultProperty(ctx, QStringLiteral("some"), method_some, 0); + defineDefaultProperty(ctx, QStringLiteral("forEach"), method_forEach, 0); + defineDefaultProperty(ctx, QStringLiteral("map"), method_map, 0); + defineDefaultProperty(ctx, QStringLiteral("filter"), method_filter, 0); + defineDefaultProperty(ctx, QStringLiteral("reduce"), method_reduce, 0); + defineDefaultProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 0); } Value ArrayPrototype::method_toString(ExecutionContext *ctx) @@ -2078,12 +2067,12 @@ Value FunctionCtor::call(ExecutionContext *ctx) void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - __put__(ctx, QStringLiteral("constructor"), ctor); - __put__(ctx, QStringLiteral("toString"), method_toString, 0); - __put__(ctx, QStringLiteral("apply"), method_apply, 0); - __put__(ctx, QStringLiteral("call"), method_call, 0); - __put__(ctx, QStringLiteral("bind"), method_bind, 0); + ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 0); + defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 0); + defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 0); } Value FunctionPrototype::method_toString(ExecutionContext *ctx) @@ -2194,57 +2183,57 @@ Value DateCtor::call(ExecutionContext *ctx) void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); LocalTZA = getLocalTZA(); - ctor.objectValue()->__put__(ctx, QStringLiteral("parse"), method_parse, 1); - ctor.objectValue()->__put__(ctx, QStringLiteral("UTC"), method_UTC, 7); - - __put__(ctx, QStringLiteral("constructor"), ctor); - __put__(ctx, QStringLiteral("toString"), method_toString, 0); - __put__(ctx, QStringLiteral("toDateString"), method_toDateString, 0); - __put__(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0); - __put__(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); - __put__(ctx, QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); - __put__(ctx, QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); - __put__(ctx, QStringLiteral("valueOf"), method_valueOf, 0); - __put__(ctx, QStringLiteral("getTime"), method_getTime, 0); - __put__(ctx, QStringLiteral("getYear"), method_getYear, 0); - __put__(ctx, QStringLiteral("getFullYear"), method_getFullYear, 0); - __put__(ctx, QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0); - __put__(ctx, QStringLiteral("getMonth"), method_getMonth, 0); - __put__(ctx, QStringLiteral("getUTCMonth"), method_getUTCMonth, 0); - __put__(ctx, QStringLiteral("getDate"), method_getDate, 0); - __put__(ctx, QStringLiteral("getUTCDate"), method_getUTCDate, 0); - __put__(ctx, QStringLiteral("getDay"), method_getDay, 0); - __put__(ctx, QStringLiteral("getUTCDay"), method_getUTCDay, 0); - __put__(ctx, QStringLiteral("getHours"), method_getHours, 0); - __put__(ctx, QStringLiteral("getUTCHours"), method_getUTCHours, 0); - __put__(ctx, QStringLiteral("getMinutes"), method_getMinutes, 0); - __put__(ctx, QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0); - __put__(ctx, QStringLiteral("getSeconds"), method_getSeconds, 0); - __put__(ctx, QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0); - __put__(ctx, QStringLiteral("getMilliseconds"), method_getMilliseconds, 0); - __put__(ctx, QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0); - __put__(ctx, QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0); - __put__(ctx, QStringLiteral("setTime"), method_setTime, 1); - __put__(ctx, QStringLiteral("setMilliseconds"), method_setMilliseconds, 1); - __put__(ctx, QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1); - __put__(ctx, QStringLiteral("setSeconds"), method_setSeconds, 2); - __put__(ctx, QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2); - __put__(ctx, QStringLiteral("setMinutes"), method_setMinutes, 3); - __put__(ctx, QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3); - __put__(ctx, QStringLiteral("setHours"), method_setHours, 4); - __put__(ctx, QStringLiteral("setUTCHours"), method_setUTCHours, 4); - __put__(ctx, QStringLiteral("setDate"), method_setDate, 1); - __put__(ctx, QStringLiteral("setUTCDate"), method_setUTCDate, 1); - __put__(ctx, QStringLiteral("setMonth"), method_setMonth, 2); - __put__(ctx, QStringLiteral("setUTCMonth"), method_setUTCMonth, 2); - __put__(ctx, QStringLiteral("setYear"), method_setYear, 1); - __put__(ctx, QStringLiteral("setFullYear"), method_setFullYear, 3); - __put__(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); - __put__(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); - __put__(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("parse"), method_parse, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toDateString"), method_toDateString, 0); + defineDefaultProperty(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + defineDefaultProperty(ctx, QStringLiteral("getTime"), method_getTime, 0); + defineDefaultProperty(ctx, QStringLiteral("getYear"), method_getYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getFullYear"), method_getFullYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getMonth"), method_getMonth, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMonth"), method_getUTCMonth, 0); + defineDefaultProperty(ctx, QStringLiteral("getDate"), method_getDate, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCDate"), method_getUTCDate, 0); + defineDefaultProperty(ctx, QStringLiteral("getDay"), method_getDay, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCDay"), method_getUTCDay, 0); + defineDefaultProperty(ctx, QStringLiteral("getHours"), method_getHours, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCHours"), method_getUTCHours, 0); + defineDefaultProperty(ctx, QStringLiteral("getMinutes"), method_getMinutes, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0); + defineDefaultProperty(ctx, QStringLiteral("getSeconds"), method_getSeconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getMilliseconds"), method_getMilliseconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0); + defineDefaultProperty(ctx, QStringLiteral("setTime"), method_setTime, 1); + defineDefaultProperty(ctx, QStringLiteral("setMilliseconds"), method_setMilliseconds, 1); + defineDefaultProperty(ctx, QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1); + defineDefaultProperty(ctx, QStringLiteral("setSeconds"), method_setSeconds, 2); + defineDefaultProperty(ctx, QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2); + defineDefaultProperty(ctx, QStringLiteral("setMinutes"), method_setMinutes, 3); + defineDefaultProperty(ctx, QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3); + defineDefaultProperty(ctx, QStringLiteral("setHours"), method_setHours, 4); + defineDefaultProperty(ctx, QStringLiteral("setUTCHours"), method_setUTCHours, 4); + defineDefaultProperty(ctx, QStringLiteral("setDate"), method_setDate, 1); + defineDefaultProperty(ctx, QStringLiteral("setUTCDate"), method_setUTCDate, 1); + defineDefaultProperty(ctx, QStringLiteral("setMonth"), method_setMonth, 2); + defineDefaultProperty(ctx, QStringLiteral("setUTCMonth"), method_setUTCMonth, 2); + defineDefaultProperty(ctx, QStringLiteral("setYear"), method_setYear, 1); + defineDefaultProperty(ctx, QStringLiteral("setFullYear"), method_setFullYear, 3); + defineDefaultProperty(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); + defineDefaultProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); + defineDefaultProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); } double DatePrototype::getThisDate(ExecutionContext *ctx) @@ -2804,11 +2793,11 @@ Value RegExpCtor::call(ExecutionContext *ctx) void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(this)); - __put__(ctx, QStringLiteral("constructor"), ctor); - __put__(ctx, QStringLiteral("exec"), method_exec, 0); - __put__(ctx, QStringLiteral("test"), method_test, 0); - __put__(ctx, QStringLiteral("toString"), method_toString, 0); + ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 0); + defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 0); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); } Value RegExpPrototype::method_exec(ExecutionContext *ctx) @@ -2930,9 +2919,9 @@ Value URIErrorCtor::construct(ExecutionContext *ctx) void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) { - ctor.objectValue()->__put__(ctx, ctx->engine->id_prototype, Value::fromObject(obj)); - obj->__put__(ctx, QStringLiteral("constructor"), ctor); - obj->__put__(ctx, QStringLiteral("toString"), method_toString, 0); + ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(obj)); + obj->defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + obj->defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); } Value ErrorPrototype::method_toString(ExecutionContext *ctx) @@ -2971,47 +2960,33 @@ Value ErrorPrototype::method_toString(ExecutionContext *ctx) // MathObject::MathObject(ExecutionContext *ctx) { - PropertyDescriptor desc; - desc.type = PropertyDescriptor::Data; - desc.writable = PropertyDescriptor::Unset; - desc.enumberable = PropertyDescriptor::Unset; - desc.configurable = PropertyDescriptor::Unset; - - desc.value = Value::fromDouble(::exp(1.0)); - __defineOwnProperty__(ctx, QStringLiteral("E"), &desc); - desc.value = Value::fromDouble(::log(2.0)); - __defineOwnProperty__(ctx, QStringLiteral("LN2"), &desc); - desc.value = Value::fromDouble(::log(10.0)); - __defineOwnProperty__(ctx, QStringLiteral("LN10"), &desc); - desc.value = Value::fromDouble(1.0/::log(2.0)); - __defineOwnProperty__(ctx, QStringLiteral("LOG2E"), &desc); - desc.value = Value::fromDouble(1.0/::log(10.0)); - __defineOwnProperty__(ctx, QStringLiteral("LOG10E"), &desc); - desc.value = Value::fromDouble(qt_PI); - __defineOwnProperty__(ctx, QStringLiteral("PI"), &desc); - desc.value = Value::fromDouble(::sqrt(0.5)); - __defineOwnProperty__(ctx, QStringLiteral("SQRT1_2"), &desc); - desc.value = Value::fromDouble(::sqrt(2.0)); - __defineOwnProperty__(ctx, QStringLiteral("SQRT2"), &desc); - - __put__(ctx, QStringLiteral("abs"), method_abs, 1); - __put__(ctx, QStringLiteral("acos"), method_acos, 1); - __put__(ctx, QStringLiteral("asin"), method_asin, 0); - __put__(ctx, QStringLiteral("atan"), method_atan, 1); - __put__(ctx, QStringLiteral("atan2"), method_atan2, 2); - __put__(ctx, QStringLiteral("ceil"), method_ceil, 1); - __put__(ctx, QStringLiteral("cos"), method_cos, 1); - __put__(ctx, QStringLiteral("exp"), method_exp, 1); - __put__(ctx, QStringLiteral("floor"), method_floor, 1); - __put__(ctx, QStringLiteral("log"), method_log, 1); - __put__(ctx, QStringLiteral("max"), method_max, 2); - __put__(ctx, QStringLiteral("min"), method_min, 2); - __put__(ctx, QStringLiteral("pow"), method_pow, 2); - __put__(ctx, QStringLiteral("random"), method_random, 0); - __put__(ctx, QStringLiteral("round"), method_round, 1); - __put__(ctx, QStringLiteral("sin"), method_sin, 1); - __put__(ctx, QStringLiteral("sqrt"), method_sqrt, 1); - __put__(ctx, QStringLiteral("tan"), method_tan, 1); + defineReadonlyProperty(ctx->engine, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LN10"), Value::fromDouble(::log(10.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LOG2E"), Value::fromDouble(1.0/::log(2.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LOG10E"), Value::fromDouble(1.0/::log(10.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("PI"), Value::fromDouble(qt_PI)); + defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT1_2"), Value::fromDouble(::sqrt(0.5))); + defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT2"), Value::fromDouble(::sqrt(2.0))); + + defineDefaultProperty(ctx, QStringLiteral("abs"), method_abs, 1); + defineDefaultProperty(ctx, QStringLiteral("acos"), method_acos, 1); + defineDefaultProperty(ctx, QStringLiteral("asin"), method_asin, 0); + defineDefaultProperty(ctx, QStringLiteral("atan"), method_atan, 1); + defineDefaultProperty(ctx, QStringLiteral("atan2"), method_atan2, 2); + defineDefaultProperty(ctx, QStringLiteral("ceil"), method_ceil, 1); + defineDefaultProperty(ctx, QStringLiteral("cos"), method_cos, 1); + defineDefaultProperty(ctx, QStringLiteral("exp"), method_exp, 1); + defineDefaultProperty(ctx, QStringLiteral("floor"), method_floor, 1); + defineDefaultProperty(ctx, QStringLiteral("log"), method_log, 1); + defineDefaultProperty(ctx, QStringLiteral("max"), method_max, 2); + defineDefaultProperty(ctx, QStringLiteral("min"), method_min, 2); + defineDefaultProperty(ctx, QStringLiteral("pow"), method_pow, 2); + defineDefaultProperty(ctx, QStringLiteral("random"), method_random, 0); + defineDefaultProperty(ctx, QStringLiteral("round"), method_round, 1); + defineDefaultProperty(ctx, QStringLiteral("sin"), method_sin, 1); + defineDefaultProperty(ctx, QStringLiteral("sqrt"), method_sqrt, 1); + defineDefaultProperty(ctx, QStringLiteral("tan"), method_tan, 1); } /* copies the sign from y to x and returns the result */ -- cgit v1.2.3 From c267edf74b5e8fba75e2191dc70aeb11f11edce0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 13 Dec 2012 01:15:44 +0100 Subject: Fix Object.prototype.isPrototypeOf() Implement the method according to spec (15.2.4.6) Change-Id: I84b943366dcb1048966d4ae2f60bcbf01c99e7ea Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index daee93ac2a..bb3cd5f0e9 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -823,9 +823,14 @@ Value ObjectPrototype::method_isPrototypeOf(ExecutionContext *ctx) if (! V.isObject()) return Value::fromBoolean(false); - Value O = ctx->thisObject.toObject(ctx); + Object *O = ctx->thisObject.toObject(ctx).objectValue(); Object *proto = V.objectValue()->prototype; - return Value::fromBoolean(proto && O.objectValue() == proto); + while (proto) { + if (O == proto) + return Value::fromBoolean(true); + proto = proto->prototype; + } + return Value::fromBoolean(false); } Value ObjectPrototype::method_propertyIsEnumerable(ExecutionContext *ctx) -- cgit v1.2.3 From c4aa210e58e59d8cce233bfc5e9eb12ebda66a79 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 13 Dec 2012 01:28:46 +0100 Subject: Fix a possible crash when setting a property on a primitive type Change-Id: Icb8d97536b87779cdc3e543260755509232e10cb Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 5ef2c6c9f0..1768cbf1d1 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -544,6 +544,8 @@ Value __qmljs_new_string_object(ExecutionContext *ctx, String *string) void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Value value) { + if (! object.isObject()) + object = __qmljs_to_object(object, ctx); object.objectValue()->__put__(ctx, name, value); } -- cgit v1.2.3 From 542055c95151c071df60ea88a6fd0142218b3915 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 13 Dec 2012 01:29:10 +0100 Subject: Fix return values for Object.protoype.seal/freeze/preventExtensions Change-Id: Icc9844cfac5d8afae09ca10eeed024e65385801f Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index bb3cd5f0e9..eb3dbd4c7b 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -689,7 +689,7 @@ Value ObjectPrototype::method_seal(ExecutionContext *ctx) ++it; } } - return Value::undefinedValue(); + return ctx->argument(0); } Value ObjectPrototype::method_freeze(ExecutionContext *ctx) @@ -708,7 +708,7 @@ Value ObjectPrototype::method_freeze(ExecutionContext *ctx) ++it; } } - return Value::undefinedValue(); + return ctx->argument(0); } Value ObjectPrototype::method_preventExtensions(ExecutionContext *ctx) @@ -718,7 +718,7 @@ Value ObjectPrototype::method_preventExtensions(ExecutionContext *ctx) Object *o = ctx->argument(0).objectValue(); o->extensible = false; - return Value::undefinedValue(); + return ctx->argument(0); } Value ObjectPrototype::method_isSealed(ExecutionContext *ctx) -- cgit v1.2.3 From 45dc9011984ca10e873d841be39a2fcf2c8bce24 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 13 Dec 2012 01:39:36 +0100 Subject: Fix delete expression. Change-Id: Ifc0918272c615889aee4ae63435071a72aad7119 Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 2a49a67588..e785d89a3d 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -230,13 +230,13 @@ bool ExecutionContext::deleteProperty(String *name) ExecutionContext::With *w = ctx->withObject; while (w) { if (w->object->__hasProperty__(this, name)) - w->object->__delete__(this, name); + return w->object->__delete__(this, name); w = w->next; } } if (ctx->activation) { if (ctx->activation->__hasProperty__(this, name)) - ctx->activation->__delete__(this, name); + return ctx->activation->__delete__(this, name); } } if (strictMode) -- cgit v1.2.3 From 2e722c0bc08e91040c5b358a3f17204f3ec8f106 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 13 Dec 2012 01:44:20 +0100 Subject: Fix the Number constructor. new Number() is supposed to return a number object initialized with 0. Change-Id: I1c88abee81da0d84485e2d606bf72a8c9283ed61 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index eb3dbd4c7b..e89ceab205 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1308,8 +1308,10 @@ NumberCtor::NumberCtor(ExecutionContext *scope) Value NumberCtor::construct(ExecutionContext *ctx) { - const double n = ctx->argument(0).toNumber(ctx); - ctx->thisObject = Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(n))); + double d = 0; + if (!ctx->argument(0).isUndefined()) + d = ctx->argument(0).toNumber(ctx); + ctx->thisObject = Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(d))); return ctx->thisObject; } -- cgit v1.2.3 From 151201ad24f5bd64f2f15e65e0facd366d838769 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 13 Dec 2012 01:53:13 +0100 Subject: Fix string to number conversion Change-Id: Ib261bc61a5fd6ed99292a5021b173330b2fd96e7 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 1768cbf1d1..21aa1c2acc 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -434,7 +434,8 @@ int __qmljs_string_length(ExecutionContext *, String *string) double __qmljs_string_to_number(ExecutionContext *, String *string) { - const QString s = string->toQString(); + QString s = string->toQString(); + s = s.trimmed(); if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) return s.toLong(0, 16); bool ok; @@ -443,8 +444,10 @@ double __qmljs_string_to_number(ExecutionContext *, String *string) const char *end = 0; double d = qstrtod(begin, &end, &ok); if (end - begin != ba.size()) { - if (ba == "Infinity") + if (ba == "Infinity" || ba == "+Infinity") d = INFINITY; + else if (ba == "-Infinity") + d = -INFINITY; else d = nan(""); } -- cgit v1.2.3 From 5968e9bb4eaca7ee08dac91eb3d621dfdc9ba1f8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 21:33:17 +0100 Subject: Fix the bit shift and complement operators Take care of some corner cases and make them pass the test suite. Change-Id: Ic83508859800c62681ee873968b475ef81fffb82 Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 22 +++++++++++++--------- qv4codegen.cpp | 6 +++--- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 263c151145..aa2bc4e7cb 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -472,9 +472,9 @@ inline Value __qmljs_compl(Value value, ExecutionContext *ctx) int n; if (value.isConvertibleToInt()) - n = ~value.int_32; + n = value.int_32; else - n = Value::toInteger(__qmljs_to_number(value, ctx)); + n = Value::toInt32(__qmljs_to_number(value, ctx)); return Value::fromInt32(~n); } @@ -588,8 +588,6 @@ inline Value __qmljs_mod(Value left, Value right, ExecutionContext *ctx) return Value::fromDouble(fmod(lval, rval)); } -// ### unsigned shl missing? - inline Value __qmljs_shl(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); @@ -618,12 +616,18 @@ inline Value __qmljs_ushr(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); - if (Value::integerCompatible(left, right)) - return Value::fromInt32(uint(left.integerValue()) >> ((uint(right.integerValue()) & 0x1f))); + uint result; + if (Value::integerCompatible(left, right)) { + result = uint(left.integerValue()) >> (uint(right.integerValue()) & 0x1f); + } else { + unsigned lval = Value::toUInt32(__qmljs_to_number(left, ctx)); + unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; + result = lval >> rval; + } - unsigned lval = Value::toUInt32(__qmljs_to_number(left, ctx)); - unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; - return Value::fromInt32(lval >> rval); + if (result > INT_MAX) + return Value::fromDouble(result); + return Value::fromInt32(result); } inline Value __qmljs_gt(Value left, Value right, ExecutionContext *ctx) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index fef6b4e733..7409a54bc6 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -553,13 +553,13 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) case IR::OpGt: return _block->CONST(IR::BoolType, c1->value > c2->value); case IR::OpLe: return _block->CONST(IR::BoolType, c1->value <= c2->value); case IR::OpLt: return _block->CONST(IR::BoolType, c1->value < c2->value); - case IR::OpLShift: return _block->CONST(IR::NumberType, int(c1->value) << int(c2->value)); + case IR::OpLShift: return _block->CONST(IR::NumberType, VM::Value::toInt32(c1->value) << (VM::Value::toUInt32(c2->value) & 0x1f)); case IR::OpMod: return _block->CONST(IR::NumberType, ::fmod(c1->value, c2->value)); case IR::OpMul: return _block->CONST(IR::NumberType, c1->value * c2->value); case IR::OpOr: return _block->CONST(IR::NumberType, c1->value ? c1->value : c2->value); - case IR::OpRShift: return _block->CONST(IR::NumberType, int(c1->value) >> int(c2->value)); + case IR::OpRShift: return _block->CONST(IR::NumberType, VM::Value::toInt32(c1->value) << (VM::Value::toUInt32(c2->value) & 0x1f)); case IR::OpSub: return _block->CONST(IR::NumberType, c1->value - c2->value); - case IR::OpURShift: return _block->CONST(IR::NumberType, unsigned(c1->value) >> int(c2->value)); + case IR::OpURShift: return _block->CONST(IR::NumberType,VM::Value::toUInt32(c1->value) << (VM::Value::toUInt32(c2->value) & 0x1f)); case IR::OpInstanceof: case IR::OpIn: -- cgit v1.2.3 From b2d094e56d00774dd883f434624d571316a7e5c0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 21:39:26 +0100 Subject: Fix StringCtr::call Check for the number of arguments, not whether the first arg is undefined. Change-Id: I23829404dfd4547f829b1bc7a4407017d79f7162 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index e89ceab205..bd360ee978 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1002,11 +1002,12 @@ Value StringCtor::construct(ExecutionContext *ctx) Value StringCtor::call(ExecutionContext *ctx) { - const Value arg = ctx->argument(0); - if (arg.isUndefined()) - return Value::fromString(ctx->engine->newString(QString())); + Value value; + if (ctx->argumentCount) + value = Value::fromString(ctx->argument(0).toString(ctx)); else - return __qmljs_to_string(arg, ctx); + value = Value::fromString(ctx, QString()); + return value; } void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) -- cgit v1.2.3 From 22b5ff5b93822da3d08861400415200068cd0756 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 22:05:42 +0100 Subject: Fix NumberCtor::construct Change-Id: I4714a447d2a4fc71e73c7492fc8ed939efc0192f Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index bd360ee978..fc428e6c83 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1309,17 +1309,15 @@ NumberCtor::NumberCtor(ExecutionContext *scope) Value NumberCtor::construct(ExecutionContext *ctx) { - double d = 0; - if (!ctx->argument(0).isUndefined()) - d = ctx->argument(0).toNumber(ctx); + double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; ctx->thisObject = Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(d))); return ctx->thisObject; } Value NumberCtor::call(ExecutionContext *ctx) { - double value = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; - return Value::fromDouble(value); + double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; + return Value::fromDouble(d); } void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) -- cgit v1.2.3 From d6fcbdbf5182dcd549ef685cfeb2e3e90d3e81c7 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 12 Dec 2012 22:10:50 +0100 Subject: Fix ObjectCtor::call Change-Id: Ifa74ba46ed5213661d009fcd748d66f2b5670814 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index fc428e6c83..1a95c14603 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -537,7 +537,9 @@ Value ObjectCtor::construct(ExecutionContext *ctx) Value ObjectCtor::call(ExecutionContext *ctx) { - return Value::fromObject(ctx->engine->newObject()); + if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull()) + return Value::fromObject(ctx->engine->newObject()); + return __qmljs_to_object(ctx->argument(0), ctx); } Value ObjectCtor::__get__(ExecutionContext *ctx, String *name) -- cgit v1.2.3 From 9edd6967753c16512c8c589f3dedcae02687070a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 13 Dec 2012 12:09:04 +0100 Subject: Correctly set up nested functions Make sure we have the correct set of nested functions in both IR::Function and VM::Function. This is required so that closures can work correctly. Change-Id: I42493d5ee503090653b71650c8d19e06c4bcfdda Reviewed-by: Simon Hausmann --- moth/qv4isel_moth.cpp | 32 ++++++++++++++++++++++---------- moth/qv4isel_moth_p.h | 2 +- qmljs_objects.cpp | 13 ++----------- qv4codegen.cpp | 4 ++-- qv4ecmaobjects.cpp | 2 +- qv4ir.cpp | 8 +++++++- qv4ir_p.h | 7 ++++--- qv4isel_masm.cpp | 7 ++----- qv4isel_masm_p.h | 2 +- qv4isel_p.cpp | 18 ++++++++++++++++-- qv4isel_p.h | 4 ++-- 11 files changed, 60 insertions(+), 39 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 5b00ccf552..14fd1cbbd3 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -194,12 +194,24 @@ InstructionSelection::~InstructionSelection() { } -VM::Function *InstructionSelection::run(IR::Function *function) +void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) { qSwap(_function, function); + + IR::BasicBlock *block; + + QHash > patches; + QHash addrs; + // FIXME: make the size dynamic. This requires changing the patching. - _code = new uchar[getpagesize() * 4000]; - _ccode = _code; + uchar *code = new uchar[getpagesize() * 4000]; + uchar *ccode = code; + + qSwap(block, _block); + qSwap(patches, _patches); + qSwap(addrs, _addrs); + qSwap(code, _code); + qSwap(ccode, _ccode); CompressTemps().run(_function); @@ -235,14 +247,14 @@ VM::Function *InstructionSelection::run(IR::Function *function) _patches.clear(); _addrs.clear(); - VM::Function *vmFunc = vmFunction(function); - vmFunc->code = VME::exec; - vmFunc->codeData = _code; + vmFunction->code = VME::exec; + vmFunction->codeData = _code; - _block = 0; - _code = 0; - _ccode = 0; - return vmFunc; + qSwap(block, _block); + qSwap(patches, _patches); + qSwap(addrs, _addrs); + qSwap(code, _code); + qSwap(ccode, _ccode); } void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempIndex) diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index e7c85174c3..dece41323f 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -15,7 +15,7 @@ public: InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); ~InstructionSelection(); - virtual VM::Function *run(IR::Function *function); + virtual void run(VM::Function *vmFunction, IR::Function *function); protected: virtual void visitExp(IR::Exp *); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 8ab6e50178..b282332c2e 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -671,17 +671,8 @@ QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct Codegen cg(ctx); IR::Function *globalIRCode = cg(fileName, program, &module, mode); QScopedPointer isel(ctx->engine->iselFactory->create(vm, &module)); - if (globalIRCode) { - globalCode = isel->run(globalIRCode); - } - if (globalCode) { - // only generate other functions if global code generation succeeded. - foreach (IR::Function *function, module.functions) { - if (function == globalIRCode) - continue; - globalCode->nestedFunctions.append(isel->run(function)); - } - } + if (globalIRCode) + globalCode = isel->vmFunction(globalIRCode); } if (! globalCode) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 7409a54bc6..1cbdc843a6 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1650,14 +1650,14 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, TryCleanup *tryCleanup = 0; enterEnvironment(ast); - IR::Function *function = _module->newFunction(name); + IR::Function *function = _module->newFunction(name, _function); + if (_debugger) _debugger->addFunction(function); IR::BasicBlock *entryBlock = function->newBasicBlock(); IR::BasicBlock *exitBlock = function->newBasicBlock(IR::Function::DontInsertBlock); IR::BasicBlock *throwBlock = function->newBasicBlock(); function->hasDirectEval = _env->hasDirectEval; - function->hasNestedFunctions = _env->hasNestedFunctions; function->maxNumberOfArguments = _env->maxNumberOfArguments; function->isStrict = _env->isStrict; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 1a95c14603..6bf661cdce 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2058,7 +2058,7 @@ Value FunctionCtor::construct(ExecutionContext *ctx) IR::Function *irf = cg(QString(), fe, &module); QScopedPointer isel(ctx->engine->iselFactory->create(ctx->engine, &module)); - VM::Function *vmf = isel->run(irf); + VM::Function *vmf = isel->vmFunction(irf); ctx->thisObject = Value::fromObject(ctx->engine->newScriptFunction(ctx->engine->rootContext, vmf)); return ctx->thisObject; diff --git a/qv4ir.cpp b/qv4ir.cpp index 33b8b7721d..8d4c4f8b53 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -386,10 +386,16 @@ void Ret::dump(QTextStream &out, Mode) out << ';'; } -Function *Module::newFunction(const QString &name) +Function *Module::newFunction(const QString &name, Function *outer) { Function *f = new Function(this, name); functions.append(f); + if (!outer) { + assert(!rootFunction); + rootFunction = f; + } else { + outer->nestedFunctions.append(f); + } return f; } diff --git a/qv4ir_p.h b/qv4ir_p.h index b605e60b26..5f474f215c 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -587,9 +587,11 @@ struct Ret: Stmt { struct Module { MemoryPool pool; QVector functions; + Function *rootFunction; - Function *newFunction(const QString &name); + Function *newFunction(const QString &name, Function *outer); + Module() : rootFunction(0) {} ~Module(); }; @@ -603,11 +605,11 @@ struct Function { QSet strings; QList formals; QList locals; + QVector nestedFunctions; int insideWith; bool hasDirectEval: 1; - bool hasNestedFunctions: 1; bool isStrict: 1; template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } @@ -619,7 +621,6 @@ struct Function { , maxNumberOfArguments(0) , insideWith(0) , hasDirectEval(false) - , hasNestedFunctions(false) , isStrict(false) { this->name = newString(name); } diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index cabd1f7c5c..298d0b51f1 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -381,7 +381,7 @@ InstructionSelection::~InstructionSelection() delete _asm; } -VM::Function *InstructionSelection::run(IR::Function *function) +void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) { qSwap(_function, function); Assembler* oldAssembler = _asm; @@ -425,14 +425,11 @@ VM::Function *InstructionSelection::run(IR::Function *function) #endif _asm->ret(); - VM::Function *vmFunc = vmFunction(_function); - _asm->link(vmFunc); + _asm->link(vmFunction); qSwap(_function, function); delete _asm; _asm = oldAssembler; - - return vmFunc; } String *InstructionSelection::identifier(const QString &s) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index ca32511ae4..481392c571 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -647,7 +647,7 @@ public: InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); ~InstructionSelection(); - virtual VM::Function *run(IR::Function *function); + virtual void run(VM::Function *vmFunction, IR::Function *function); protected: typedef Assembler::Address Address; diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 5a668fa246..b49d4d3adc 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -16,8 +16,10 @@ EvalInstructionSelection::EvalInstructionSelection(VM::ExecutionEngine *engine, assert(engine); assert(module); - foreach (IR::Function *f, module->functions) - _irToVM.insert(f, createFunctionMapping(engine, f)); + createFunctionMapping(engine, module->rootFunction); + foreach (IR::Function *f, module->functions) { + assert(_irToVM.contains(f)); + } } EvalInstructionSelection::~EvalInstructionSelection() @@ -29,6 +31,8 @@ EvalISelFactory::~EvalISelFactory() VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngine *engine, IR::Function *irFunction) { VM::Function *vmFunction = engine->newFunction(irFunction->name ? *irFunction->name : QString()); + _irToVM.insert(irFunction, vmFunction); + vmFunction->hasDirectEval = irFunction->hasDirectEval; vmFunction->isStrict = irFunction->isStrict; @@ -39,8 +43,18 @@ VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngin if (local) vmFunction->locals.append(*local); + foreach (IR::Function *function, irFunction->nestedFunctions) + vmFunction->nestedFunctions.append(createFunctionMapping(engine, function)); + if (engine->debugger) engine->debugger->mapFunction(vmFunction, irFunction); return vmFunction; } + +VM::Function *EvalInstructionSelection::vmFunction(IR::Function *f) { + VM::Function *function = _irToVM[f]; + if (!function->code) + run(function, f); + return function; +} diff --git a/qv4isel_p.h b/qv4isel_p.h index 109ff795a5..221a564f32 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -51,12 +51,12 @@ public: EvalInstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); virtual ~EvalInstructionSelection() = 0; - virtual VM::Function *run(IR::Function *function) = 0; + VM::Function *vmFunction(IR::Function *f); protected: VM::Function *createFunctionMapping(VM::ExecutionEngine *engine, IR::Function *irFunction); - VM::Function *vmFunction(IR::Function *f) const { return _irToVM[f]; } VM::ExecutionEngine *engine() const { return _engine; } + virtual void run(VM::Function *vmFunction, IR::Function *function) = 0; private: VM::ExecutionEngine *_engine; -- cgit v1.2.3 From 1db8ff269d7965ec514d7e2f35940b4d758a6987 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 13 Dec 2012 12:11:47 +0100 Subject: Don't reset the function pointer in leaveCallContext() We need the function pointer to be able to map variable names correctly. Fixes the remaining problems with nested functions and closures. Change-Id: I976be2df57b93edb3b762afc33790c4483430b05 Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index e785d89a3d..c4177ad26e 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -432,7 +432,6 @@ void ExecutionContext::leaveCallContext() } engine->current = parent; parent = 0; - function = 0; if (engine->debugger) engine->debugger->justLeft(this); -- cgit v1.2.3 From 0485444fbbc16371c41847b2a380f32679cd734a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Thu, 13 Dec 2012 10:14:22 +0100 Subject: Remove unused property (Object::klass) Change-Id: I623ceeceb810719f44ed832f0cc37a1d74430db3 Reviewed-by: Simon Hausmann --- qmljs_objects.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/qmljs_objects.h b/qmljs_objects.h index 14218e7be1..384c6934b7 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -418,13 +418,11 @@ private: struct Object: Managed { Object *prototype; - String *klass; PropertyTable *members; bool extensible; Object() : prototype(0) - , klass(0) , members(0) , extensible(true) {} -- cgit v1.2.3 From a809f30dd4aa10893a46b208a56ba3577a89aaec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Thu, 13 Dec 2012 10:19:14 +0100 Subject: Use a smart pointer for Object::members. Change-Id: I070c00281a5b92de82568d4d4e0bb35700233a21 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 9 ++++----- qmljs_objects.h | 4 ++-- qv4ecmaobjects.cpp | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index b282332c2e..5116282f1c 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -87,7 +87,6 @@ void Managed::operator delete(void *ptr) // Object::~Object() { - delete members; } void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &value) @@ -126,7 +125,7 @@ bool Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ct void Object::defineDefaultProperty(String *name, Value value) { if (!members) - members = new PropertyTable(); + members.reset(new PropertyTable()); PropertyDescriptor *pd = members->insert(name); pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Set; @@ -151,7 +150,7 @@ void Object::defineDefaultProperty(ExecutionContext *context, const QString &nam void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value) { if (!members) - members = new PropertyTable(); + members.reset(new PropertyTable()); PropertyDescriptor *pd = members->insert(engine->identifier(name)); pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Unset; @@ -239,7 +238,7 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) goto reject; if (!members) - members = new PropertyTable(); + members.reset(new PropertyTable()); { // Clause 2 @@ -312,7 +311,7 @@ bool Object::__delete__(ExecutionContext *ctx, String *name) bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc) { if (!members) - members = new PropertyTable(); + members.reset(new PropertyTable()); // Clause 1 PropertyDescriptor *current = __getOwnProperty__(ctx, name); diff --git a/qmljs_objects.h b/qmljs_objects.h index 384c6934b7..99e29feaf7 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -418,12 +419,11 @@ private: struct Object: Managed { Object *prototype; - PropertyTable *members; + QScopedPointer members; bool extensible; Object() : prototype(0) - , members(0) , extensible(true) {} virtual ~Object(); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 6bf661cdce..3090a3ee2c 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -605,7 +605,7 @@ Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); Array &a = array->value; - if (PropertyTable *members = O.objectValue()->members) { + if (PropertyTable *members = O.objectValue()->members.data()) { for (PropertyTableEntry **it = members->begin(), **end = members->end(); it != end; ++it) { if (PropertyTableEntry *prop = *it) { a.push(Value::fromString(prop->name)); -- cgit v1.2.3 From 5d97f07d5c7848a453a29814d357bf91e8f2ce19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Thu, 13 Dec 2012 10:46:31 +0100 Subject: Use a smart pointer for ExecutionEngine::stringPool Change-Id: I5434fbd146e9d8dcc0000327c5a5b953bc744b83 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 5 ++--- qmljs_engine.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 45db9fcb2b..b65b358afa 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -73,8 +73,8 @@ ExecutionEngine::ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory * { MemoryManager::GCBlocker gcBlocker(memoryManager); - stringPool = new StringPool; - memoryManager->setStringPool(stringPool); + stringPool.reset(new StringPool); + memoryManager->setStringPool(stringPool.data()); memoryManager->setExecutionEngine(this); rootContext = newContext(); @@ -212,7 +212,6 @@ ExecutionEngine::~ExecutionEngine() { delete globalObject.asObject(); delete rootContext; - delete stringPool; qDeleteAll(functions); } diff --git a/qmljs_engine.h b/qmljs_engine.h index bcaff099e9..16d8fc50c6 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -156,7 +156,7 @@ struct ExecutionEngine QVector unwindStack; Value exception; - struct StringPool *stringPool; + QScopedPointer stringPool; QVector functions; ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory *iselFactory); -- cgit v1.2.3 From 73824a7b6fbaa91c02a3b0e88b1c86f3267c5f49 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 13 Dec 2012 12:58:21 +0100 Subject: Prevent accidental allocation of GC managed objects on the regular heap Make the regular new operator private. As it turns out no other changes were required, all the existing objects are already allocated on the GC heap, thanks to the new* factor functions in ExecutionEngine. Change-Id: I9b69221b5bbc4491ca909ec6bb5afe0f3c010b54 Reviewed-by: Lars Knoll --- qmljs_objects.h | 1 + 1 file changed, 1 insertion(+) diff --git a/qmljs_objects.h b/qmljs_objects.h index 99e29feaf7..965b333230 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -110,6 +110,7 @@ protected: virtual void getCollectables(QVector &objects) = 0; private: + void *operator new(size_t); friend class MemoryManager; MemoryManager *mm; }; -- cgit v1.2.3 From 0e8302d22966ed1e8b364a21fd7b2f3b06358f9c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 13 Dec 2012 13:12:10 +0100 Subject: Rename Unset/Set to the more readable Enabled/Disabled for the property tristate Change-Id: I67f5a509be64b20a5fa0205779f2a67dc1ba6536 Reviewed-by: Lars Knoll --- qmljs_environment.cpp | 6 +++--- qmljs_objects.cpp | 24 ++++++++++++------------ qmljs_objects.h | 16 ++++++++-------- qv4ecmaobjects.cpp | 42 +++++++++++++++++++++--------------------- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index c4177ad26e..e717851069 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -106,9 +106,9 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) PropertyDescriptor desc; desc.value = Value::undefinedValue(); desc.type = PropertyDescriptor::Data; - desc.configurable = deletable ? PropertyDescriptor::Set : PropertyDescriptor::Unset; - desc.writable = PropertyDescriptor::Set; - desc.enumberable = PropertyDescriptor::Set; + desc.configurable = deletable ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; + desc.writable = PropertyDescriptor::Enabled; + desc.enumberable = PropertyDescriptor::Enabled; activation->__defineOwnProperty__(this, name, &desc); } diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 5116282f1c..616fe82df8 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -128,9 +128,9 @@ void Object::defineDefaultProperty(String *name, Value value) members.reset(new PropertyTable()); PropertyDescriptor *pd = members->insert(name); pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Set; - pd->enumberable = PropertyDescriptor::Unset; - pd->configurable = PropertyDescriptor::Set; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Enabled; pd->value = value; } @@ -153,9 +153,9 @@ void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name members.reset(new PropertyTable()); PropertyDescriptor *pd = members->insert(engine->identifier(name)); pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Unset; - pd->enumberable = PropertyDescriptor::Unset; - pd->configurable = PropertyDescriptor::Unset; + pd->writable = PropertyDescriptor::Disabled; + pd->enumberable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Disabled; pd->value = value; } @@ -270,9 +270,9 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) PropertyDescriptor *p = members->insert(name); *p = PropertyDescriptor::fromValue(value); - p->configurable = PropertyDescriptor::Set; - p->enumberable = PropertyDescriptor::Set; - p->writable = PropertyDescriptor::Set; + p->configurable = PropertyDescriptor::Enabled; + p->enumberable = PropertyDescriptor::Enabled; + p->writable = PropertyDescriptor::Enabled; return; } @@ -360,7 +360,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property } else { // 9c current->type = PropertyDescriptor::Data; - current->writable = PropertyDescriptor::Unset; + current->writable = PropertyDescriptor::Disabled; current->value = Value::undefinedValue(); } } else if (current->isData() && desc->isData()) { // clause 10 @@ -788,8 +788,8 @@ PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(ExecutionContext const quint32 i = Value::fromString(name).toUInt32(ctx); if (i < context->argumentCount) { *to_fill = PropertyDescriptor::fromValue(context->argument(i)); - to_fill->writable = PropertyDescriptor::Unset; - to_fill->enumberable = PropertyDescriptor::Unset; + to_fill->writable = PropertyDescriptor::Disabled; + to_fill->enumberable = PropertyDescriptor::Disabled; return to_fill; } } diff --git a/qmljs_objects.h b/qmljs_objects.h index 965b333230..419ada796d 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -153,8 +153,8 @@ struct PropertyDescriptor { }; enum State { Undefined, - Unset, - Set + Disabled, + Enabled }; union { Value value; @@ -196,23 +196,23 @@ struct PropertyDescriptor { } if (type == Data) { if (writable == Undefined) - writable = Unset; + writable = Disabled; } else { writable = Undefined; } if (enumberable == Undefined) - enumberable = Unset; + enumberable = Disabled; if (configurable == Undefined) - configurable = Unset; + configurable = Disabled; } inline bool isData() const { return type == Data; } inline bool isAccessor() const { return type == Accessor; } inline bool isGeneric() const { return type == Generic; } - inline bool isWritable() const { return writable == Set; } - inline bool isEnumerable() const { return enumberable == Set; } - inline bool isConfigurable() const { return configurable == Set; } + inline bool isWritable() const { return writable == Enabled; } + inline bool isEnumerable() const { return enumberable == Enabled; } + inline bool isConfigurable() const { return configurable == Enabled; } inline bool isEmpty() { return type == Generic && writable == Undefined && enumberable == Undefined && configurable == Undefined; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 3090a3ee2c..bd8969d886 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -687,7 +687,7 @@ Value ObjectPrototype::method_seal(ExecutionContext *ctx) if (o->members) { PropertyTable::iterator it = o->members->begin(); while (it != o->members->end()) { - (*it)->descriptor.configurable = PropertyDescriptor::Unset; + (*it)->descriptor.configurable = PropertyDescriptor::Disabled; ++it; } } @@ -705,8 +705,8 @@ Value ObjectPrototype::method_freeze(ExecutionContext *ctx) PropertyTable::iterator it = o->members->begin(); while (it != o->members->end()) { if ((*it)->descriptor.isData()) - (*it)->descriptor.writable = PropertyDescriptor::Unset; - (*it)->descriptor.configurable = PropertyDescriptor::Unset; + (*it)->descriptor.writable = PropertyDescriptor::Disabled; + (*it)->descriptor.configurable = PropertyDescriptor::Disabled; ++it; } } @@ -734,7 +734,7 @@ Value ObjectPrototype::method_isSealed(ExecutionContext *ctx) if (o->members) { PropertyTable::iterator it = o->members->begin(); while (it != o->members->end()) { - if ((*it)->descriptor.configurable != PropertyDescriptor::Unset) + if ((*it)->descriptor.configurable != PropertyDescriptor::Disabled) return Value::fromBoolean(false); ++it; } @@ -754,9 +754,9 @@ Value ObjectPrototype::method_isFrozen(ExecutionContext *ctx) PropertyTable::iterator it = o->members->begin(); while (it != o->members->end()) { if ((*it)->descriptor.isData() && - ((*it)->descriptor.writable != PropertyDescriptor::Unset)) + ((*it)->descriptor.writable != PropertyDescriptor::Disabled)) return Value::fromBoolean(false); - if ((*it)->descriptor.configurable != PropertyDescriptor::Unset) + if ((*it)->descriptor.configurable != PropertyDescriptor::Disabled) return Value::fromBoolean(false); ++it; } @@ -857,9 +857,9 @@ Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx) Object *o = ctx->thisObject.toObject(ctx).objectValue(); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(f, 0); - pd.writable = PropertyDescriptor::Set; - pd.configurable = PropertyDescriptor::Set; - pd.enumberable = PropertyDescriptor::Set; + pd.writable = PropertyDescriptor::Enabled; + pd.configurable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; o->__defineOwnProperty__(ctx, prop, &pd); return Value::undefinedValue(); } @@ -877,9 +877,9 @@ Value ObjectPrototype::method_defineSetter(ExecutionContext *ctx) Object *o = ctx->thisObject.toObject(ctx).objectValue(); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(0, f); - pd.writable = PropertyDescriptor::Set; - pd.configurable = PropertyDescriptor::Set; - pd.enumberable = PropertyDescriptor::Set; + pd.writable = PropertyDescriptor::Enabled; + pd.configurable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; o->__defineOwnProperty__(ctx, prop, &pd); return Value::undefinedValue(); } @@ -895,11 +895,11 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Prope desc->enumberable = PropertyDescriptor::Undefined; if (o->__hasProperty__(ctx, ctx->engine->id_enumerable)) - desc->enumberable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_enumerable), ctx) ? PropertyDescriptor::Set : PropertyDescriptor::Unset; + desc->enumberable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_enumerable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; desc->configurable = PropertyDescriptor::Undefined; if (o->__hasProperty__(ctx, ctx->engine->id_configurable)) - desc->configurable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_configurable), ctx) ? PropertyDescriptor::Set : PropertyDescriptor::Unset; + desc->configurable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_configurable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; desc->get = 0; if (o->__hasProperty__(ctx, ctx->engine->id_get)) { @@ -933,7 +933,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Prope if (o->__hasProperty__(ctx, ctx->engine->id_writable)) { if (desc->isAccessor()) __qmljs_throw_type_error(ctx); - desc->writable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_writable), ctx) ? PropertyDescriptor::Set : PropertyDescriptor::Unset; + desc->writable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_writable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; // writable forces it to be a data descriptor desc->type = PropertyDescriptor::Data; desc->value = Value::undefinedValue(); @@ -960,14 +960,14 @@ Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Prope PropertyDescriptor pd; pd.type = PropertyDescriptor::Data; - pd.writable = PropertyDescriptor::Set; - pd.enumberable = PropertyDescriptor::Set; - pd.configurable = PropertyDescriptor::Set; + pd.writable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + pd.configurable = PropertyDescriptor::Enabled; if (desc->isData()) { pd.value = desc->value; o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("value")), &pd); - pd.value = Value::fromBoolean(desc->writable == PropertyDescriptor::Set ? true : false); + pd.value = Value::fromBoolean(desc->writable == PropertyDescriptor::Enabled ? true : false); o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("writable")), &pd); } else { pd.value = desc->get ? Value::fromObject(desc->get) : Value::undefinedValue(); @@ -975,9 +975,9 @@ Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Prope pd.value = desc->set ? Value::fromObject(desc->set) : Value::undefinedValue(); o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("set")), &pd); } - pd.value = Value::fromBoolean(desc->enumberable == PropertyDescriptor::Set ? true : false); + pd.value = Value::fromBoolean(desc->enumberable == PropertyDescriptor::Enabled ? true : false); o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("enumerable")), &pd); - pd.value = Value::fromBoolean(desc->configurable == PropertyDescriptor::Set ? true : false); + pd.value = Value::fromBoolean(desc->configurable == PropertyDescriptor::Enabled ? true : false); o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("configurable")), &pd); return Value::fromObject(o); -- cgit v1.2.3 From c498ddfbb6f6207ef2943929720c00a4f9c108f8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 13 Dec 2012 13:46:49 +0100 Subject: Avoid creating the activation object in most cases it's now only being used for the global context, and in case a non strict eval defines an additional variable in any other context. Change-Id: Ib6531bfce8d19634af79cc813d38c41f5348f961 Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index e717851069..ef552521ad 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -234,10 +234,8 @@ bool ExecutionContext::deleteProperty(String *name) w = w->next; } } - if (ctx->activation) { - if (ctx->activation->__hasProperty__(this, name)) - return ctx->activation->__delete__(this, name); - } + if (ctx->activation && ctx->activation->__hasProperty__(this, name)) + return ctx->activation->__delete__(this, name); } if (strictMode) throwSyntaxError(0); @@ -336,13 +334,9 @@ Value ExecutionContext::getPropertyNoThrow(String *name) void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) { - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { - if (ctx->activation) { - if (ctx->activation->inplaceBinOp(value, name, op, this)) - return; - } - } - throwReferenceError(Value::fromString(name)); + Value rhs = getProperty(name); + value = op(value, rhs, this); + setProperty(name, value); } void ExecutionContext::throwError(Value value) @@ -411,10 +405,7 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha if (function->varCount) std::fill(locals, locals + function->varCount, Value::undefinedValue()); - if (function->needsActivation) - activation = engine->newActivationObject(); - else - activation = 0; + activation = 0; withObject = 0; @@ -425,8 +416,7 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha void ExecutionContext::leaveCallContext() { - // ## Should rather be handled by a the activation object having a ref to the environment - if (activation) { + if (!function->needsActivation) { delete[] locals; locals = 0; } -- cgit v1.2.3 From 48b825007fe45d84cea1ae6f526103ccedef87c3 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 13 Dec 2012 14:48:20 +0100 Subject: inline some code in toInt32 and toUInt32 Speeds up crypto.js by ~5% Change-Id: I707bd6e7dc0f13b70889955e1b90f0c436db1848 Reviewed-by: Simon Hausmann --- qmljs_value.cpp | 16 ---------------- qmljs_value.h | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/qmljs_value.cpp b/qmljs_value.cpp index 38e1f5d911..32c0ca6b1f 100644 --- a/qmljs_value.cpp +++ b/qmljs_value.cpp @@ -51,22 +51,6 @@ int Value::toUInt16(ExecutionContext *ctx) return __qmljs_to_uint16(*this, ctx); } -int Value::toInt32(ExecutionContext *ctx) -{ - if (isConvertibleToInt()) - return int_32; - - return Value::toInt32(__qmljs_to_number(*this, ctx)); -} - -unsigned int Value::toUInt32(ExecutionContext *ctx) -{ - if (isConvertibleToInt()) - return (unsigned) int_32; - - return toUInt32(__qmljs_to_number(*this, ctx)); -} - Bool Value::toBoolean(ExecutionContext *ctx) const { return __qmljs_to_boolean(*this, ctx); diff --git a/qmljs_value.h b/qmljs_value.h index 7f095caf87..9446c79dc3 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -64,6 +64,11 @@ struct ErrorObject; struct ArgumentsObject; struct ExecutionContext; struct ExecutionEngine; +struct Value; + +extern "C" { +double __qmljs_to_number(Value value, ExecutionContext *ctx); +} typedef uint Bool; @@ -197,6 +202,7 @@ struct Value int toUInt16(ExecutionContext *ctx); int toInt32(ExecutionContext *ctx); unsigned int toUInt32(ExecutionContext *ctx); + Bool toBoolean(ExecutionContext *ctx) const; double toInteger(ExecutionContext *ctx) const; double toNumber(ExecutionContext *ctx) const; @@ -324,6 +330,40 @@ inline Value Value::fromObject(Object *o) return v; } +inline int Value::toInt32(ExecutionContext *ctx) +{ + if (isConvertibleToInt()) + return int_32; + double d; + if (isDouble()) + d = dbl; + else + d = __qmljs_to_number(*this, ctx); + + const double D32 = 4294967296.0; + const double D31 = D32 / 2.0; + + if ((d >= -D31 && d < D31)) + return static_cast(d); + + return Value::toInt32(__qmljs_to_number(*this, ctx)); +} + +inline unsigned int Value::toUInt32(ExecutionContext *ctx) { + if (isConvertibleToInt()) + return (unsigned) int_32; + double d; + if (isDouble()) + d = dbl; + else + d = __qmljs_to_number(*this, ctx); + + const double D32 = 4294967296.0; + if (dbl >= 0 && dbl < D32) + return static_cast(dbl); + return toUInt32(d); +} + } // namespace VM } // namespace QQmlJS -- cgit v1.2.3 From 2761c4ceacab2e4f7da2e2a293a895595c02a928 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 13 Dec 2012 15:52:07 +0100 Subject: Fix invalid reads in valgrind during unused basic block collections When clearing cross-references to unused basic blocks blocks, don't delete the block right afterwards because another block might also still reference it. Instead keep track of the ones to be deleted and delete them afterwards in one shot. Also replaces the existance check for the blocks from a linear vector search to a hash set lookup which we already have around. Change-Id: I3bd72359259065ba26bf2116bf849575e4601f32 Reviewed-by: Lars Knoll --- qv4codegen.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 1cbdc843a6..17b96fe738 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1520,18 +1520,21 @@ void Codegen::linearize(IR::Function *function) I::trace(function->basicBlocks.first(), &V, &trace); + V.insert(exitBlock); exitBlock->index = trace.size(); trace.append(exitBlock); + QVarLengthArray blocksToDelete; foreach (IR::BasicBlock *b, function->basicBlocks) - if (!trace.contains(b)) { - foreach (IR::BasicBlock *out, b->out) { - int idx = out->in.indexOf(b); - if (idx >= 0) - out->in.remove(idx); + if (!V.contains(b)) { + foreach (IR::BasicBlock *out, b->out) { + int idx = out->in.indexOf(b); + if (idx >= 0) + out->in.remove(idx); + } + blocksToDelete.append(b); } - delete b; - } + qDeleteAll(blocksToDelete); function->basicBlocks = trace; #ifndef QV4_NO_LIVENESS -- cgit v1.2.3 From 21d90ca005700fe01bddda28b94db32421d59808 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 13 Dec 2012 23:46:51 +0100 Subject: Fix quadratic behavior in the memory manager The old freeList implementation was causing quadratic behavior in alloc(), as the free item ended up in the highest chunk. The new implementation uses a fixed size array for small objects (up to 256 bytes), a QMap for large chunks, and a defaultFree object pointing to the heap that has never been used before. Gives around 25% performance boost on crypto.js, and bsaically makes the memory manager invisible in kcachegrind. Change-Id: I559fb527bcd9e21d4ac265f4d78b8376bfda2522 Reviewed-by: Simon Hausmann --- qv4mm.cpp | 139 +++++++++++++++++++++++++++++++++++++++----------------------- qv4mm.h | 2 + 2 files changed, 89 insertions(+), 52 deletions(-) diff --git a/qv4mm.cpp b/qv4mm.cpp index 05fce092ad..abfd120d33 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -53,10 +54,9 @@ struct MemoryManager::Data ExecutionEngine *engine; StringPool *stringPool; - // FIXME: this freeList will get out of hand if there is one allocation+deallocation of, say, 16M. - // TODO: turn the freeList into a fixed length array which can hold the most common sizes (e.g. up to 64K), then use a tree for anything afterwards. - // TODO: this requires that the interaction with the freeList is factored out first into separate methods. - QVector freeList; + MMObject *fallbackObject; + MMObject *smallItems[16]; // 16 - 256 bytes + QMap largeItems; QLinkedList > heapChunks; // statistics: @@ -69,8 +69,9 @@ struct MemoryManager::Data , gcBlocked(false) , engine(0) , stringPool(0) - , freeList(0) + , fallbackObject(0) { + memset(smallItems, 0, sizeof(smallItems)); scribble = qgetenv("MM_NO_SCRIBBLE").isEmpty(); aggressiveGC = !qgetenv("MM_AGGRESSIVE_GC").isEmpty(); } @@ -96,60 +97,76 @@ MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) #endif // DETAILED_MM_STATS size += align(sizeof(MMInfo)); + assert(size >= 16); assert(size % 16 == 0); - for (std::size_t s = size / 16, es = m_d->freeList.size(); s < es; ++s) { - if (MMObject *m = m_d->freeList.at(s)) { - m_d->freeList[s] = m->info.next; - - if (s != size / 16) { - MMObject *tail = reinterpret_cast(reinterpret_cast(m) + size); - assert(m->info.size == s * 16); - tail->info.inUse = 0; - tail->info.markBit = 0; - tail->info.size = m->info.size - size; - MMObject *&f = m_d->freeList[tail->info.size / 16]; - tail->info.next = f; - f = tail; - m->info.size = size; - } + size_t pos = size >> 4; + // fits into a small bucket + if (pos < sizeof(m_d->smallItems)/sizeof(MMObject *)) { + MMObject *m = m_d->smallItems[pos]; + if (m) { + m_d->smallItems[pos] = m->info.next; m->info.inUse = 1; m->info.markBit = 0; - scribble(m, 0xaa); -// qDebug("alloc(%lu) -> %p", size, m); return m; } } - if (!m_d->aggressiveGC) - if (runGC() >= size) - return alloc(size - sizeof(MMInfo)); - - std::size_t allocSize = std::max(size, CHUNK_SIZE); - char *ptr = 0; - posix_memalign(reinterpret_cast(&ptr), 16, allocSize); - m_d->heapChunks.append(qMakePair(ptr, allocSize)); -// qDebug("Allocated new chunk of %lu bytes @ %p", allocSize, ptr); - - if (allocSize > size) { - MMObject *m = reinterpret_cast(ptr + size); - m->info.size = allocSize - size; - std::size_t off = m->info.size / 16; - if (((std::size_t) m_d->freeList.size()) <= off) - m_d->freeList.resize(off + 1); - MMObject *&f = m_d->freeList[off]; - m->info.next = f; - f = m; + + // ### use new heap space if available + if (m_d->fallbackObject && m_d->fallbackObject->info.size >= size) { + MMObject *m = m_d->fallbackObject; + m_d->fallbackObject = splitItem(m, size); + m->info.inUse = 1; + m->info.markBit = 0; + return m; + } + + // use or split up a large bucket + QMap::iterator it = m_d->largeItems.lowerBound(pos); + if (it == m_d->largeItems.end()) { + // try to free up space, otherwise allocate + if (!m_d->aggressiveGC || runGC() < size) { + std::size_t allocSize = std::max(size, CHUNK_SIZE); + char *ptr = 0; + posix_memalign(reinterpret_cast(&ptr), 16, allocSize); + m_d->heapChunks.append(qMakePair(ptr, allocSize)); + m_d->fallbackObject = reinterpret_cast(ptr); + m_d->fallbackObject->info.inUse = 0; + m_d->fallbackObject->info.next = 0; + m_d->fallbackObject->info.markBit = 0; + m_d->fallbackObject->info.size = allocSize; + } + return alloc(size - sizeof(MMInfo)); + } + + MMObject *m = it.value(); + assert(m); + + if (it.key() == pos) { + // a match, return it + if (!m->info.next) + m_d->largeItems.erase(it); + else + *it = m->info.next; + m->info.inUse = 1; + m->info.markBit = 0; + return m; } - MMObject *m = reinterpret_cast(ptr); + // split up + if (!m->info.next) + m_d->largeItems.erase(it); + else + *it = m->info.next; + MMObject *tail = splitItem(m, size); + MMObject *&f = m_d->largeItems[tail->info.size]; + tail->info.next = f; + f = tail; m->info.inUse = 1; m->info.markBit = 0; - m->info.size = size; - scribble(m, 0xaa); -// qDebug("alloc(%lu) -> %p", size, ptr); return m; } @@ -163,16 +180,33 @@ void MemoryManager::dealloc(MMObject *ptr) // qDebug("dealloc %p (%lu)", ptr, ptr->info.size); - std::size_t off = ptr->info.size / 16; - if (((std::size_t) m_d->freeList.size()) <= off) - m_d->freeList.resize(off + 1); - MMObject *&f = m_d->freeList[off]; - ptr->info.next = f; + std::size_t pos = ptr->info.size >> 4; + MMObject **f; + + // fits into a small bucket + if (pos < sizeof(m_d->smallItems)/sizeof(MMObject *)) { + f = &m_d->smallItems[pos]; + } else { + f = &m_d->largeItems[pos]; + } + + ptr->info.next = *f; ptr->info.inUse = 0; ptr->info.markBit = 0; ptr->info.needsManagedDestructorCall = 0; - f = ptr; - scribble(ptr, 0x55); + *f = ptr; +} + +MemoryManager::MMObject *MemoryManager::splitItem(MemoryManager::MMObject *m, int newSize) +{ + if (newSize - m->info.size <= sizeof(MMObject)) + return 0; + MMObject *tail = reinterpret_cast(reinterpret_cast(m) + newSize); + tail->info.inUse = 0; + tail->info.markBit = 0; + tail->info.size = m->info.size - newSize; + m->info.size = newSize; + return tail; } void MemoryManager::scribble(MemoryManager::MMObject *obj, int c) const @@ -344,6 +378,7 @@ void MemoryManager::willAllocate(std::size_t size) counters.resize(alignedSize + 1); counters[alignedSize]++; } + #endif // DETAILED_MM_STATS void MemoryManager::collectRoots(QVector &roots) const diff --git a/qv4mm.h b/qv4mm.h index 5bf007799c..530eadb5db 100644 --- a/qv4mm.h +++ b/qv4mm.h @@ -153,6 +153,8 @@ protected: void willAllocate(std::size_t size); #endif // DETAILED_MM_STATS + MMObject *splitItem(MMObject *m, int newSize); + private: void collectRoots(QVector &roots) const; static std::size_t mark(const QVector &objects); -- cgit v1.2.3 From ad1e9fb11927d470184b0f5bb2d702e563ff68d1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 14 Dec 2012 09:57:02 +0100 Subject: Simplify/speed up retrieving of properties The hasProperty()/get() sequence used so far is as in the spec, but requires us to lookup the name twice. Instead add a bool hasProperty() to Object::__get__() and use that. Speeds up fact.2.js by ~20% Change-Id: Ic8c84718f1a702c3da9487010c0d6dd0fee44609 Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 36 +++++++++++++++++++-------- qmljs_objects.cpp | 67 +++++++++++++++++++++++++++++++++++---------------- qmljs_objects.h | 10 ++++---- qv4ecmaobjects.cpp | 9 ++++--- qv4ecmaobjects_p.h | 2 +- 5 files changed, 84 insertions(+), 40 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index ef552521ad..a7cf2ce539 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -148,8 +148,12 @@ Value ExecutionContext::getBindingValue(ExecutionContext *scope, String *name, b if (__qmljs_string_equal(formals()[i], name)) return arguments[i]; } - if (activation && activation->__hasProperty__(this, name)) - return activation->__get__(scope, name); + if (activation) { + bool hasProperty = false; + Value v = activation->__get__(scope, name, &hasProperty); + if (hasProperty) + return v; + } assert(false); } @@ -272,8 +276,10 @@ Value ExecutionContext::getProperty(String *name) if (ctx->withObject) { With *w = ctx->withObject; while (w) { - if (w->object->__hasProperty__(ctx, name)) - return w->object->__get__(ctx, name); + bool hasProperty = false; + Value v = w->object->__get__(ctx, name, &hasProperty); + if (hasProperty) + return v; w = w->next; } } @@ -284,8 +290,12 @@ Value ExecutionContext::getProperty(String *name) for (unsigned int i = 0; i < ctx->formalCount(); ++i) if (__qmljs_string_equal(ctx->formals()[i], name)) return ctx->arguments[i]; - if (ctx->activation && ctx->activation->__hasProperty__(ctx, name)) - return ctx->activation->__get__(ctx, name); + if (ctx->activation) { + bool hasProperty = false; + Value v = ctx->activation->__get__(ctx, name, &hasProperty); + if (hasProperty) + return v; + } if (name->isEqualTo(ctx->engine->id_arguments)) { Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this)); createMutableBinding(ctx->engine->id_arguments, false); @@ -306,8 +316,10 @@ Value ExecutionContext::getPropertyNoThrow(String *name) if (ctx->withObject) { With *w = ctx->withObject; while (w) { - if (w->object->__hasProperty__(ctx, name)) - return w->object->__get__(ctx, name); + bool hasProperty = false; + Value v = w->object->__get__(ctx, name, &hasProperty); + if (hasProperty) + return v; w = w->next; } } @@ -318,8 +330,12 @@ Value ExecutionContext::getPropertyNoThrow(String *name) for (unsigned int i = 0; i < ctx->formalCount(); ++i) if (__qmljs_string_equal(ctx->formals()[i], name)) return ctx->arguments[i]; - if (ctx->activation && ctx->activation->__hasProperty__(ctx, name)) - return ctx->activation->__get__(ctx, name); + if (ctx->activation) { + bool hasProperty = false; + Value v = ctx->activation->__get__(ctx, name, &hasProperty); + if (hasProperty) + return v; + } if (name->isEqualTo(ctx->engine->id_arguments)) { Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this)); createMutableBinding(ctx->engine->id_arguments, false); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 616fe82df8..eb0f150799 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -184,8 +184,9 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *, String *name) // Section 8.12.2 PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill) { - if (PropertyDescriptor *p = __getOwnProperty__(ctx, name)) - return p; + if (members) + if (PropertyDescriptor *p = members->find(name)) + return p; if (prototype) return prototype->__getPropertyDescriptor__(ctx, name, to_fill); @@ -193,15 +194,23 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, Str } // Section 8.12.3 -Value Object::__get__(ExecutionContext *ctx, String *name) +Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { - if (name->isEqualTo(ctx->engine->id___proto__)) + if (name->isEqualTo(ctx->engine->id___proto__)) { + if (hasProperty) + *hasProperty = true; return Value::fromObject(prototype); + } PropertyDescriptor tmp; - if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name, &tmp)) + if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name, &tmp)) { + if (hasProperty) + *hasProperty = true; return getValue(ctx, p); + } + if (hasProperty) + *hasProperty = false; return Value::undefinedValue(); } @@ -430,11 +439,14 @@ void ForEachIteratorObject::getCollectables(QVector &objects) objects.append(current); } -Value ArrayObject::__get__(ExecutionContext *ctx, String *name) +Value ArrayObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { - if (name->isEqualTo(ctx->engine->id_length)) + if (name->isEqualTo(ctx->engine->id_length)) { + if (hasProperty) + *hasProperty = true; return Value::fromDouble(value.size()); - return Object::__get__(ctx, name); + } + return Object::__get__(ctx, name, hasProperty); } bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) @@ -718,28 +730,38 @@ Value IsFiniteFunction::call(ExecutionContext *context, Value /*thisObject*/, Va } -Value RegExpObject::__get__(ExecutionContext *ctx, String *name) +Value RegExpObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { QString n = name->toQString(); + Value v = Value::undefinedValue(); if (n == QLatin1String("source")) - return Value::fromString(ctx, value.pattern()); + v = Value::fromString(ctx, value.pattern()); else if (n == QLatin1String("global")) - return Value::fromBoolean(global); + v = Value::fromBoolean(global); else if (n == QLatin1String("ignoreCase")) - return Value::fromBoolean(value.patternOptions() & QRegularExpression::CaseInsensitiveOption); + v = Value::fromBoolean(value.patternOptions() & QRegularExpression::CaseInsensitiveOption); else if (n == QLatin1String("multiline")) - return Value::fromBoolean(value.patternOptions() & QRegularExpression::MultilineOption); + v = Value::fromBoolean(value.patternOptions() & QRegularExpression::MultilineOption); else if (n == QLatin1String("lastIndex")) - return lastIndex; - return Object::__get__(ctx, name); + v = lastIndex; + if (v.type() != Value::Undefined_Type) { + if (hasProperty) + *hasProperty = true; + return v; + } + + return Object::__get__(ctx, name, hasProperty); } -Value ErrorObject::__get__(ExecutionContext *ctx, String *name) +Value ErrorObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { QString n = name->toQString(); - if (n == QLatin1String("message")) + if (n == QLatin1String("message")) { + if (hasProperty) + *hasProperty = true; return value; - return Object::__get__(ctx, name); + } + return Object::__get__(ctx, name, hasProperty); } void ErrorObject::setNameProperty(ExecutionContext *ctx) @@ -775,11 +797,14 @@ Value ScriptFunction::construct(VM::ExecutionContext *ctx) return ctx->thisObject; } -Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name) +Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { - if (name->isEqualTo(ctx->engine->id_length)) + if (name->isEqualTo(ctx->engine->id_length)) { + if (hasProperty) + *hasProperty = true; return Value::fromInt32(context->argumentCount); - return Object::__get__(ctx, name); + } + return Object::__get__(ctx, name, hasProperty); } PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill) diff --git a/qmljs_objects.h b/qmljs_objects.h index 419ada796d..9e3edebd71 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -440,7 +440,7 @@ struct Object: Managed { virtual ErrorObject *asErrorObject() { return 0; } virtual ArgumentsObject *asArgumentsObject() { return 0; } - virtual Value __get__(ExecutionContext *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); virtual PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); virtual void __put__(ExecutionContext *ctx, String *name, Value value); @@ -519,7 +519,7 @@ struct ArrayObject: Object { ArrayObject(const Array &value): value(value) {} virtual QString className() { return QStringLiteral("Array"); } virtual ArrayObject *asArrayObject() { return this; } - virtual Value __get__(ExecutionContext *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); @@ -642,7 +642,7 @@ struct RegExpObject: Object { RegExpObject(const QRegularExpression &value, bool global): value(value), lastIndex(Value::fromInt32(0)), global(global) {} virtual QString className() { return QStringLiteral("RegExp"); } virtual RegExpObject *asRegExpObject() { return this; } - virtual Value __get__(ExecutionContext *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); }; struct ErrorObject: Object { @@ -650,7 +650,7 @@ struct ErrorObject: Object { ErrorObject(const Value &message): value(message) {} virtual QString className() { return QStringLiteral("Error"); } virtual ErrorObject *asErrorObject() { return this; } - virtual Value __get__(ExecutionContext *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); virtual struct SyntaxErrorObject *asSyntaxError() { return 0; } @@ -710,7 +710,7 @@ struct ArgumentsObject: Object { ArgumentsObject(ExecutionContext *context): context(context) {} virtual QString className() { return QStringLiteral("Arguments"); } virtual ArgumentsObject *asArgumentsObject() { return this; } - virtual Value __get__(ExecutionContext *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); }; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index bd8969d886..c9e0ea6098 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -542,11 +542,14 @@ Value ObjectCtor::call(ExecutionContext *ctx) return __qmljs_to_object(ctx->argument(0), ctx); } -Value ObjectCtor::__get__(ExecutionContext *ctx, String *name) +Value ObjectCtor::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { - if (name == ctx->engine->id_length) + if (name == ctx->engine->id_length) { + if (hasProperty) + *hasProperty = true; return Value::fromDouble(1); - return Object::__get__(ctx, name); + } + return Object::__get__(ctx, name, hasProperty); } void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index e6ab1d7f6d..be4b9387e5 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -53,7 +53,7 @@ struct ObjectCtor: FunctionObject virtual Value construct(ExecutionContext *ctx); virtual Value call(ExecutionContext *ctx); - virtual Value __get__(ExecutionContext *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); }; struct ObjectPrototype: Object -- cgit v1.2.3 From b33c0ab3fa936f236e9873ee91cf5f570b28db36 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 14 Dec 2012 09:55:10 +0100 Subject: Fix execution with MM_AGGRESSIVE_GC=1 Make sure that the current context pointer is initialized to the root context, so that early GC runs before the first function call find an initialized pointer in MemoryManager::collectRoots. Change-Id: I224695b253e27674913310b76d12d42bff5c1b82 Reviewed-by: Lars Knoll --- qmljs_engine.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index b65b358afa..7d5a8a4a11 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -79,6 +79,7 @@ ExecutionEngine::ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory * rootContext = newContext(); rootContext->init(this); + current = rootContext; id_length = identifier(QStringLiteral("length")); id_prototype = identifier(QStringLiteral("prototype")); -- cgit v1.2.3 From 445b4e240210b08679e12ef155163ab2782af6a6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 14 Dec 2012 09:46:25 +0100 Subject: Fix failing assertions in GC on ia32 We currently round up allocation sizes to 16-bytes on 32 and 64-bit. When recursively calling alloc() after the allocation of a new heap chunk, make sure to adjust the requested size parameter again to its original value, to ensure 16 byte alignment. Change-Id: Ie8cd29d60639bf43023a310b7be6f772305fa826 Reviewed-by: Lars Knoll --- qv4mm.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qv4mm.cpp b/qv4mm.cpp index abfd120d33..322ad97f13 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -96,7 +96,8 @@ MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) willAllocate(size); #endif // DETAILED_MM_STATS - size += align(sizeof(MMInfo)); + const std::size_t alignedSizeOfMMInfo = align(sizeof(MMInfo)); + size += alignedSizeOfMMInfo; assert(size >= 16); assert(size % 16 == 0); @@ -139,7 +140,7 @@ MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) m_d->fallbackObject->info.markBit = 0; m_d->fallbackObject->info.size = allocSize; } - return alloc(size - sizeof(MMInfo)); + return alloc(size - alignedSizeOfMMInfo); } MMObject *m = it.value(); -- cgit v1.2.3 From 3a9321de688e2d81fd16e7d4710325810840a6ea Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 14 Dec 2012 10:25:27 +0100 Subject: Cleanup getPropertyDescriptor and arguments object The only place where getProepertyDescriptor was still being used outside of Object itself was the arguments object. Fixed that by reimplementing get, hasProperty, put and canPut in the arguments object. Change-Id: Ie44c7acf857321c65bc0f58915de0353231459c0 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 61 +++++++++++++++++++++++++++++++++++++------------------ qmljs_objects.h | 7 ++++--- 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index eb0f150799..871985ac63 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -107,10 +107,11 @@ Value Object::getValue(ExecutionContext *ctx, PropertyDescriptor *p) const bool Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx) { PropertyDescriptor to_fill; - PropertyDescriptor *pd = __getPropertyDescriptor__(ctx, name, &to_fill); - if (!pd) + bool hasProperty = false; + Value v = __get__(ctx, name, &hasProperty); + if (!hasProperty) return false; - Value result = op(getValue(ctx, pd), rhs, ctx); + Value result = op(v, rhs, ctx); __put__(ctx, name, result); return true; } @@ -182,14 +183,14 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *, String *name) } // Section 8.12.2 -PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill) +PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name) { if (members) if (PropertyDescriptor *p = members->find(name)) return p; if (prototype) - return prototype->__getPropertyDescriptor__(ctx, name, to_fill); + return prototype->__getPropertyDescriptor__(ctx, name); return 0; } @@ -202,8 +203,7 @@ Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) return Value::fromObject(prototype); } - PropertyDescriptor tmp; - if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name, &tmp)) { + if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name)) { if (hasProperty) *hasProperty = true; return getValue(ctx, p); @@ -226,8 +226,7 @@ bool Object::__canPut__(ExecutionContext *ctx, String *name) if (! prototype) return extensible; - PropertyDescriptor tmp; - if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, name, &tmp)) { + if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, name)) { if (p->isAccessor()) return p->set != 0; if (!extensible) @@ -263,9 +262,8 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) } // clause 4 - PropertyDescriptor tmp; if (!pd && prototype) - pd = prototype->__getPropertyDescriptor__(ctx, name, &tmp); + pd = prototype->__getPropertyDescriptor__(ctx, name); // Clause 5 if (pd && pd->isAccessor()) { @@ -804,22 +802,45 @@ Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name, bool *hasPro *hasProperty = true; return Value::fromInt32(context->argumentCount); } + if (context) { + bool ok = false; + int idx = name->toQString().toInt(&ok); + if (ok && idx >= 0 && idx < context->argumentCount) + return context->argument(idx); + } + return Object::__get__(ctx, name, hasProperty); } -PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill) + +ArgumentsObject::ArgumentsObject(ExecutionContext *context) + : context(context) +{ + defineDefaultProperty(context->engine->id_length, Value::fromInt32(context->argumentCount)); +} + +void ArgumentsObject::__put__(ExecutionContext *ctx, String *name, Value value) { if (context) { - const quint32 i = Value::fromString(name).toUInt32(ctx); - if (i < context->argumentCount) { - *to_fill = PropertyDescriptor::fromValue(context->argument(i)); - to_fill->writable = PropertyDescriptor::Disabled; - to_fill->enumberable = PropertyDescriptor::Disabled; - return to_fill; - } + bool ok = false; + int idx = name->toQString().toInt(&ok); + if (ok && idx >= 0 && idx < context->argumentCount) + context->arguments[idx] = value; + } + + return Object::__put__(ctx, name, value); +} + +bool ArgumentsObject::__canPut__(ExecutionContext *ctx, String *name) +{ + if (context) { + bool ok = false; + int idx = name->toQString().toInt(&ok); + if (ok && idx >= 0 && idx < context->argumentCount) + return true; } - return Object::__getPropertyDescriptor__(ctx, name, to_fill); + return Object::__canPut__(ctx, name); } NativeFunction::NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) diff --git a/qmljs_objects.h b/qmljs_objects.h index 9e3edebd71..ab309718a0 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -442,7 +442,7 @@ struct Object: Managed { virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); virtual PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); - virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); + PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name); virtual void __put__(ExecutionContext *ctx, String *name, Value value); virtual bool __canPut__(ExecutionContext *ctx, String *name); virtual bool __hasProperty__(const ExecutionContext *ctx, String *name) const; @@ -707,11 +707,12 @@ struct URIErrorObject: ErrorObject { struct ArgumentsObject: Object { ExecutionContext *context; - ArgumentsObject(ExecutionContext *context): context(context) {} + ArgumentsObject(ExecutionContext *context); virtual QString className() { return QStringLiteral("Arguments"); } virtual ArgumentsObject *asArgumentsObject() { return this; } virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); - virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); + virtual void __put__(ExecutionContext *ctx, String *name, Value value); + virtual bool __canPut__(ExecutionContext *ctx, String *name); }; } // namespace VM -- cgit v1.2.3 From e1c5e2f03b9c2bee49dfc64376de1b880ca530c0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 14 Dec 2012 11:32:52 +0100 Subject: Fixes for the arguments object We now pass most of the test cases for it. Change-Id: Idc43a9baa75c3c1e8fe760d78cf5e6092f051c6e Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 21 ++++++++------------ qmljs_objects.cpp | 55 +++++++++++---------------------------------------- qmljs_objects.h | 8 ++++---- qv4codegen.cpp | 19 ++++++++++++++++++ qv4codegen_p.h | 8 ++++++++ qv4ir_p.h | 2 ++ qv4isel_p.cpp | 1 + 7 files changed, 54 insertions(+), 60 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index a7cf2ce539..e2bae58c56 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -296,12 +296,6 @@ Value ExecutionContext::getProperty(String *name) if (hasProperty) return v; } - if (name->isEqualTo(ctx->engine->id_arguments)) { - Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this)); - createMutableBinding(ctx->engine->id_arguments, false); - setMutableBinding(this, ctx->engine->id_arguments, arguments); - return arguments; - } } throwReferenceError(Value::fromString(name)); return Value::undefinedValue(); @@ -336,12 +330,6 @@ Value ExecutionContext::getPropertyNoThrow(String *name) if (hasProperty) return v; } - if (name->isEqualTo(ctx->engine->id_arguments)) { - Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this)); - createMutableBinding(ctx->engine->id_arguments, false); - setMutableBinding(this, ctx->engine->id_arguments, arguments); - return arguments; - } } return Value::undefinedValue(); } @@ -417,14 +405,21 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha if (argc < function->formalParameterCount) std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); } + locals = function->varCount ? new Value[function->varCount] : 0; if (function->varCount) std::fill(locals, locals + function->varCount, Value::undefinedValue()); activation = 0; - withObject = 0; + if (function->usesArgumentsObject) { + ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this); + args->prototype = engine->objectPrototype; + Value arguments = Value::fromObject(args); + createMutableBinding(engine->id_arguments, false); + setMutableBinding(this, engine->id_arguments, arguments); + } if (engine->debugger) engine->debugger->aboutToCall(f, this); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 871985ac63..ceaa2d72ff 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -106,7 +106,6 @@ Value Object::getValue(ExecutionContext *ctx, PropertyDescriptor *p) const bool Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx) { - PropertyDescriptor to_fill; bool hasProperty = false; Value v = __get__(ctx, name, &hasProperty); if (!hasProperty) @@ -547,6 +546,7 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) if (!function->name.isEmpty()) name = scope->engine->identifier(function->name); needsActivation = function->needsActivation(); + usesArgumentsObject = function->usesArgumentsObject; strictMode = function->isStrict; formalParameterCount = function->formals.size(); if (formalParameterCount) { @@ -795,52 +795,21 @@ Value ScriptFunction::construct(VM::ExecutionContext *ctx) return ctx->thisObject; } -Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) -{ - if (name->isEqualTo(ctx->engine->id_length)) { - if (hasProperty) - *hasProperty = true; - return Value::fromInt32(context->argumentCount); - } - if (context) { - bool ok = false; - int idx = name->toQString().toInt(&ok); - if (ok && idx >= 0 && idx < context->argumentCount) - return context->argument(idx); - } - - return Object::__get__(ctx, name, hasProperty); -} - - ArgumentsObject::ArgumentsObject(ExecutionContext *context) - : context(context) { defineDefaultProperty(context->engine->id_length, Value::fromInt32(context->argumentCount)); -} - -void ArgumentsObject::__put__(ExecutionContext *ctx, String *name, Value value) -{ - if (context) { - bool ok = false; - int idx = name->toQString().toInt(&ok); - if (ok && idx >= 0 && idx < context->argumentCount) - context->arguments[idx] = value; - } - - return Object::__put__(ctx, name, value); -} - -bool ArgumentsObject::__canPut__(ExecutionContext *ctx, String *name) -{ - if (context) { - bool ok = false; - int idx = name->toQString().toInt(&ok); - if (ok && idx >= 0 && idx < context->argumentCount) - return true; + for (uint i = 0; i < context->argumentCount; ++i) + __put__(context, QString::number(i), context->arguments[i]); + if (context->strictMode) { + FunctionObject *thrower = context->engine->newNativeFunction(context, 0, __qmljs_throw_type_error); + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); + pd.configurable = PropertyDescriptor::Disabled; + pd.enumberable = PropertyDescriptor::Disabled; + __defineOwnProperty__(context, QStringLiteral("callee"), &pd); + __defineOwnProperty__(context, QStringLiteral("caller"), &pd); + } else { + defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); } - - return Object::__canPut__(ctx, name); } NativeFunction::NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) diff --git a/qmljs_objects.h b/qmljs_objects.h index ab309718a0..21dc82a2cc 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -539,6 +539,7 @@ struct Function { QVector nestedFunctions; bool hasDirectEval: 1; + bool usesArgumentsObject : 1; bool isStrict: 1; Function(const QString &name) @@ -546,6 +547,7 @@ struct Function { , code(0) , codeData(0) , hasDirectEval(false) + , usesArgumentsObject(false) , isStrict(false) {} ~Function(); @@ -563,6 +565,7 @@ struct FunctionObject: Object { String **varList; unsigned int varCount; bool needsActivation; + bool usesArgumentsObject; bool strictMode; FunctionObject(ExecutionContext *scope) @@ -573,6 +576,7 @@ struct FunctionObject: Object { , varList(0) , varCount(0) , needsActivation(false) + , usesArgumentsObject(false) , strictMode(false) {} virtual QString className() { return QStringLiteral("Function"); } @@ -706,13 +710,9 @@ struct URIErrorObject: ErrorObject { }; struct ArgumentsObject: Object { - ExecutionContext *context; ArgumentsObject(ExecutionContext *context); virtual QString className() { return QStringLiteral("Arguments"); } virtual ArgumentsObject *asArgumentsObject() { return this; } - virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); - virtual void __put__(ExecutionContext *ctx, String *name, Value value); - virtual bool __canPut__(ExecutionContext *ctx, String *name); }; } // namespace VM diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 17b96fe738..6cf1d62beb 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -255,6 +255,14 @@ protected: } } } + void checkForArguments(AST::FormalParameterList *parameters) + { + while (parameters) { + if (parameters->name == QStringLiteral("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + parameters = parameters->next; + } + } virtual bool visit(Program *ast) { @@ -273,6 +281,8 @@ protected: if (! _env->hasDirectEval) { if (IdentifierExpression *id = cast(ast->base)) { if (id->name == QStringLiteral("eval")) { + if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown) + _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; _env->hasDirectEval = true; } } @@ -296,6 +306,8 @@ protected: virtual bool visit(VariableDeclaration *ast) { checkName(ast->name, ast->identifierToken); + if (ast->name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; _env->enter(ast->name.toString()); return true; } @@ -303,6 +315,8 @@ protected: virtual bool visit(IdentifierExpression *ast) { checkName(ast->name, ast->identifierToken); + if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; return true; } @@ -313,6 +327,7 @@ protected: _env->enter(ast->name.toString()); } enterEnvironment(ast); + checkForArguments(ast->formals); if (ast->body) checkDirectivePrologue(ast->body->elements); return true; @@ -328,7 +343,10 @@ protected: _env->functions.append(ast); _env->hasNestedFunctions = true; _env->enter(ast->name.toString()); + if (ast->name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; enterEnvironment(ast); + checkForArguments(ast->formals); if (ast->body) checkDirectivePrologue(ast->body->elements); return true; @@ -1661,6 +1679,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, IR::BasicBlock *exitBlock = function->newBasicBlock(IR::Function::DontInsertBlock); IR::BasicBlock *throwBlock = function->newBasicBlock(); function->hasDirectEval = _env->hasDirectEval; + function->usesArgumentsObject = (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed); function->maxNumberOfArguments = _env->maxNumberOfArguments; function->isStrict = _env->isStrict; diff --git a/qv4codegen_p.h b/qv4codegen_p.h index ba3b477e1b..3776db6304 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -117,6 +117,13 @@ protected: bool hasDirectEval; bool hasNestedFunctions; bool isStrict; + enum UsesArgumentsObject { + ArgumentsObjectUnknown, + ArgumentsObjectNotUsed, + ArgumentsObjectUsed + }; + + UsesArgumentsObject usesArgumentsObject; Environment(Environment *parent) : parent(parent) @@ -124,6 +131,7 @@ protected: , hasDirectEval(false) , hasNestedFunctions(false) , isStrict(false) + , usesArgumentsObject(ArgumentsObjectUnknown) { if (parent && parent->isStrict) isStrict = true; diff --git a/qv4ir_p.h b/qv4ir_p.h index 5f474f215c..8c570d1947 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -610,6 +610,7 @@ struct Function { int insideWith; bool hasDirectEval: 1; + bool usesArgumentsObject : 1; bool isStrict: 1; template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } @@ -621,6 +622,7 @@ struct Function { , maxNumberOfArguments(0) , insideWith(0) , hasDirectEval(false) + , usesArgumentsObject(false) , isStrict(false) { this->name = newString(name); } diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index b49d4d3adc..6d5d2297b8 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -34,6 +34,7 @@ VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngin _irToVM.insert(irFunction, vmFunction); vmFunction->hasDirectEval = irFunction->hasDirectEval; + vmFunction->usesArgumentsObject = irFunction->usesArgumentsObject; vmFunction->isStrict = irFunction->isStrict; foreach (const QString *formal, irFunction->formals) -- cgit v1.2.3 From e2b907a90e6aa87f70c83d5e24290f87980442ea Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 14 Dec 2012 13:27:26 +0100 Subject: Fixes for arguments object in non strict mode In non strict mode, the arguments object actually reflects the argument as it changes over the lifetime of a function, unless you explicitly delete some of it's properties. The code to implement this is pretty ugly, but still better then the specification :) Change-Id: Ie42ed25c797513615fbc4bdee14145d953f323f3 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- qmljs_objects.h | 10 ++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index ceaa2d72ff..892286d9a5 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -796,11 +796,13 @@ Value ScriptFunction::construct(VM::ExecutionContext *ctx) } ArgumentsObject::ArgumentsObject(ExecutionContext *context) + : context(context) + , currentIndex(-1) { defineDefaultProperty(context->engine->id_length, Value::fromInt32(context->argumentCount)); - for (uint i = 0; i < context->argumentCount; ++i) - __put__(context, QString::number(i), context->arguments[i]); if (context->strictMode) { + for (uint i = 0; i < context->argumentCount; ++i) + Object::__put__(context, QString::number(i), context->arguments[i]); FunctionObject *thrower = context->engine->newNativeFunction(context, 0, __qmljs_throw_type_error); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); pd.configurable = PropertyDescriptor::Disabled; @@ -808,10 +810,66 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context) __defineOwnProperty__(context, QStringLiteral("callee"), &pd); __defineOwnProperty__(context, QStringLiteral("caller"), &pd); } else { + FunctionObject *get = context->engine->newNativeFunction(context, 0, method_getArg); + FunctionObject *set = context->engine->newNativeFunction(context, 0, method_setArg); + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(get, set); + pd.configurable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + for (int i = 0; i < context->argumentCount; ++i) + __defineOwnProperty__(context, QString::number(i), &pd); defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); } } +Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) +{ + bool ok = false; + currentIndex = name->toQString().toInt(&ok); + if (!ok) + currentIndex = -1; + Value result = Object::__get__(ctx, name, hasProperty); + currentIndex = -1; + return result; +} + +void ArgumentsObject::__put__(ExecutionContext *ctx, String *name, Value value) +{ + bool ok = false; + currentIndex = name->toQString().toInt(&ok); + if (!ok) + currentIndex = -1; + Object::__put__(ctx, name, value); + currentIndex = -1; +} + +Value ArgumentsObject::method_getArg(ExecutionContext *ctx) +{ + Object *that = ctx->thisObject.asObject(); + if (!that) + __qmljs_throw_type_error(ctx); + ArgumentsObject *args = that->asArgumentsObject(); + if (!args) + __qmljs_throw_type_error(ctx); + + assert(ctx != args->context); + assert(args->currentIndex >= 0 && args->currentIndex < args->context->argumentCount); + return args->context->argument(args->currentIndex); +} + +Value ArgumentsObject::method_setArg(ExecutionContext *ctx) +{ + Object *that = ctx->thisObject.asObject(); + if (!that) + __qmljs_throw_type_error(ctx); + ArgumentsObject *args = that->asArgumentsObject(); + if (!args) + __qmljs_throw_type_error(ctx); + + assert(ctx != args->context); + assert(args->currentIndex >= 0 && args->currentIndex < args->context->argumentCount); + args->context->arguments[args->currentIndex] = ctx->arguments[0]; +} + NativeFunction::NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) : FunctionObject(scope) , code(code) diff --git a/qmljs_objects.h b/qmljs_objects.h index 21dc82a2cc..18b5271b3d 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -554,7 +554,7 @@ struct Function { inline bool hasNestedFunctions() const { return !nestedFunctions.isEmpty(); } - inline bool needsActivation() const { return hasNestedFunctions() || hasDirectEval; } + inline bool needsActivation() const { return hasNestedFunctions() || hasDirectEval || usesArgumentsObject; } }; struct FunctionObject: Object { @@ -710,9 +710,17 @@ struct URIErrorObject: ErrorObject { }; struct ArgumentsObject: Object { + ExecutionContext *context; + int currentIndex; ArgumentsObject(ExecutionContext *context); virtual QString className() { return QStringLiteral("Arguments"); } virtual ArgumentsObject *asArgumentsObject() { return this; } + + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); + virtual void __put__(ExecutionContext *ctx, String *name, Value value); + + static Value method_getArg(ExecutionContext *ctx); + static Value method_setArg(ExecutionContext *ctx); }; } // namespace VM -- cgit v1.2.3 From b41cf690e0ed48132a0c2c02f58fe0b2697bf77b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 14 Dec 2012 13:53:22 +0100 Subject: Small cleanups for ArgumentsObject Change-Id: Ib9a2f3ab23dd76f52bb4c41edf539f4fa0cd8929 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 892286d9a5..3cbd0411f2 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -815,7 +815,7 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context) PropertyDescriptor pd = PropertyDescriptor::fromAccessor(get, set); pd.configurable = PropertyDescriptor::Enabled; pd.enumberable = PropertyDescriptor::Enabled; - for (int i = 0; i < context->argumentCount; ++i) + for (uint i = 0; i < context->argumentCount; ++i) __defineOwnProperty__(context, QString::number(i), &pd); defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); } @@ -823,10 +823,12 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context) Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { - bool ok = false; - currentIndex = name->toQString().toInt(&ok); - if (!ok) - currentIndex = -1; + if (!ctx->strictMode) { + bool ok = false; + currentIndex = name->toQString().toInt(&ok); + if (!ok) + currentIndex = -1; + } Value result = Object::__get__(ctx, name, hasProperty); currentIndex = -1; return result; @@ -834,10 +836,12 @@ Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name, bool *hasPro void ArgumentsObject::__put__(ExecutionContext *ctx, String *name, Value value) { - bool ok = false; - currentIndex = name->toQString().toInt(&ok); - if (!ok) - currentIndex = -1; + if (!ctx->strictMode) { + bool ok = false; + currentIndex = name->toQString().toInt(&ok); + if (!ok) + currentIndex = -1; + } Object::__put__(ctx, name, value); currentIndex = -1; } @@ -852,7 +856,7 @@ Value ArgumentsObject::method_getArg(ExecutionContext *ctx) __qmljs_throw_type_error(ctx); assert(ctx != args->context); - assert(args->currentIndex >= 0 && args->currentIndex < args->context->argumentCount); + assert(args->currentIndex >= 0 && args->currentIndex < (int)args->context->argumentCount); return args->context->argument(args->currentIndex); } @@ -866,8 +870,9 @@ Value ArgumentsObject::method_setArg(ExecutionContext *ctx) __qmljs_throw_type_error(ctx); assert(ctx != args->context); - assert(args->currentIndex >= 0 && args->currentIndex < args->context->argumentCount); + assert(args->currentIndex >= 0 && args->currentIndex < (int)args->context->argumentCount); args->context->arguments[args->currentIndex] = ctx->arguments[0]; + return Value::undefinedValue(); } NativeFunction::NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) -- cgit v1.2.3 From 9de4ccef6b14e908f9d3d967f45dca89858772c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Thu, 13 Dec 2012 16:12:53 +0100 Subject: Remove ExecutionEngine::identifiers. The member was a duplicate of stringPool, there is no point in caching a cache. Change-Id: If34c80ee120dfaff1dd94326625b02d8014806dc Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 5 +---- qmljs_engine.h | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 7d5a8a4a11..1940a4fa90 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -223,10 +223,7 @@ ExecutionContext *ExecutionEngine::newContext() String *ExecutionEngine::identifier(const QString &s) { - String *&id = identifiers[s]; - if (! id) - id = newString(s); - return id; + return stringPool->newString(s); } Function *ExecutionEngine::newFunction(const QString &name) diff --git a/qmljs_engine.h b/qmljs_engine.h index 16d8fc50c6..94093cbbe8 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -130,8 +130,6 @@ struct ExecutionEngine TypeErrorPrototype *typeErrorPrototype; URIErrorPrototype *uRIErrorPrototype; - QHash identifiers; - String *id_length; String *id_prototype; String *id_constructor; -- cgit v1.2.3 From c09b940a64df421a05b3471af4218a4c320cf6e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Thu, 13 Dec 2012 15:11:21 +0100 Subject: Micro optimization of StringPool In destructor we do not need to create a copy off all pointers. By hiding StringPool::strings in class private section we reduce risk of it being accidentally copied. Change-Id: I1b9df6bf9e49bd6926e84b8eac6b3d904277e50a Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 8 +++++--- qmljs_engine.h | 2 +- qmljs_objects.h | 2 +- qv4mm.cpp | 1 + 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 1940a4fa90..78fd64bf7b 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -46,12 +46,14 @@ namespace QQmlJS { namespace VM { -struct StringPool +class StringPool { QHash strings; - +public: ~StringPool() - { qDeleteAll(strings.values()); } + { + qDeleteAll(strings); + } String *newString(const QString &s) { diff --git a/qmljs_engine.h b/qmljs_engine.h index 94093cbbe8..dd856f62b7 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -154,7 +154,7 @@ struct ExecutionEngine QVector unwindStack; Value exception; - QScopedPointer stringPool; + QScopedPointer stringPool; QVector functions; ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory *iselFactory); diff --git a/qmljs_objects.h b/qmljs_objects.h index 18b5271b3d..eacbc0030a 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -136,7 +136,7 @@ struct String { } private: - friend struct StringPool; + friend class StringPool; String(const QString &text) : _text(text), _hashValue(0) {} diff --git a/qv4mm.cpp b/qv4mm.cpp index 322ad97f13..a5215cd6be 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -45,6 +45,7 @@ using namespace QQmlJS::VM; static const std::size_t CHUNK_SIZE = 65536; +class StringPool; struct MemoryManager::Data { bool enableGC; -- cgit v1.2.3 From b6582ef2b917275064b511ee734d331fc35c3d4c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 14 Dec 2012 14:23:21 +0100 Subject: Fix a bug in the Object contructor new Object(x) should convert x to an object and return it if x is not null or undefined Change-Id: Icb5547a23df83018757901bfecb5f024610e7c68 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index c9e0ea6098..4774c7888a 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -531,7 +531,10 @@ ObjectCtor::ObjectCtor(ExecutionContext *scope) Value ObjectCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(ctx->engine->newObject()); + if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull()) + ctx->thisObject = Value::fromObject(ctx->engine->newObject()); + else + ctx->thisObject = __qmljs_to_object(ctx->argument(0), ctx); return ctx->thisObject; } -- cgit v1.2.3 From 6c8466f0fd3cca892c03bc48ad12fe69e2d11137 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 14 Dec 2012 14:23:55 +0100 Subject: Fix a bug in the construct method of Function objects Function objects when called as a contructor should return the return value of the called function if the return value is an object (see 13.2.2) Change-Id: I9d9e52859935d62b7f949fff46cb00d257bad90f Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 3cbd0411f2..a7ab96f218 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -527,8 +527,14 @@ Value FunctionObject::call(ExecutionContext *ctx) Value FunctionObject::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(ctx->engine->newObject()); - call(ctx); + Object *obj = ctx->engine->newObject(); + Value proto = __get__(ctx, ctx->engine->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + ctx->thisObject = Value::fromObject(obj); + Value result = call(ctx); + if (result.isObject()) + return result; return ctx->thisObject; } @@ -791,7 +797,9 @@ Value ScriptFunction::construct(VM::ExecutionContext *ctx) if (proto.isObject()) obj->prototype = proto.objectValue(); ctx->thisObject = Value::fromObject(obj); - function->code(ctx, function->codeData); + Value result = function->code(ctx, function->codeData); + if (result.isObject()) + return result; return ctx->thisObject; } -- cgit v1.2.3 From 6cf74ab25d021f11341483141125389dd6b83f4e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 14 Dec 2012 16:40:48 +0100 Subject: Fix delete operator on local variable and arguments throw in strict mode, and ignore delete operations on arguments otherwise. Change-Id: Icd55f1c99dc5e5c35b3fea1ce5fdf46d5295dbaf Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 6cf1d62beb..cc605d9878 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1194,6 +1194,16 @@ bool Codegen::visit(ConditionalExpression *ast) bool Codegen::visit(DeleteExpression *ast) { Result expr = expression(ast->expression); + if ((*expr)->asTemp() && (*expr)->asTemp()->index < 0) { + // expr points to a function argument + if (_function->isStrict) + throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); + // can't delete an argument, just evaluate expr for side effects + _expr.accept(nx); + return false; + } + if (_function->isStrict && (*expr)->asName()) + throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); IR::ExprList *args = _function->New(); args->init(reference(*expr)); _expr.code = call(_block->NAME(IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args); -- cgit v1.2.3 From fb070c2d8c845bf062ab30ac19dd6cf1a7f37ec5 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 16 Dec 2012 23:00:01 +0100 Subject: Fix return value corruption in masm codegeneration In some cases, the first argument for runtime calls and the return value where being placed in the same location on the stack leading to corrupted return values. This mainly happens when no local variable are defined, but other functions are being called. Change-Id: I93f1e518ce2998f62fb9f38c538dd718f41e522d Reviewed-by: Erik Verbruggen --- qv4isel_masm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 298d0b51f1..0b2b5b2f1f 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -94,7 +94,7 @@ Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, IR::Temp *t) loadPtr(Address(ContextRegister, offsetof(ExecutionContext, locals)), reg); offset = t->index * sizeof(Value); } else { - const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size(); + const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size() + 1; // StackFrameRegister points to its old value on the stack, so even for the first temp we need to // subtract at least sizeof(Value). offset = - sizeof(Value) * (arg + 1); @@ -387,7 +387,7 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) Assembler* oldAssembler = _asm; _asm = new Assembler(_function); - int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments); + int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) + 1; locals = (locals + 1) & ~1; _asm->enterStandardStackFrame(locals); -- cgit v1.2.3 From c6251acc4b6595fd84ce6ad1aea4c9d39c07d952 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 16 Dec 2012 23:08:07 +0100 Subject: Cleanup constructor handling Simplify the code and unify the generic part of object construction in FunctionObject::construct. Change-Id: Ie430458bedaa211efba37c8283e26a9b84e6764a Reviewed-by: Erik Verbruggen --- qmljs_objects.cpp | 39 +++++++++-------------------- qmljs_objects.h | 2 -- qv4ecmaobjects.cpp | 73 ++++++++++++++---------------------------------------- qv4ecmaobjects_p.h | 1 - 4 files changed, 31 insertions(+), 84 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index a7ab96f218..a7703d8790 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -497,15 +497,21 @@ bool FunctionObject::hasInstance(ExecutionContext *ctx, const Value &value) Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc) { + Object *obj = context->engine->newObject(); + Value proto = __get__(context, context->engine->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + ExecutionContext k; ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; - ctx->initCallContext(context, Value::undefinedValue(), this, args, argc); + + ctx->initCallContext(context, Value::fromObject(obj), this, args, argc); Value result = construct(ctx); - ctx->wireUpPrototype(); ctx->leaveCallContext(); - if (ctx != &k) - delete ctx; - return result; + + if (result.isObject()) + return result; + return Value::fromObject(obj); } Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *args, int argc) @@ -527,15 +533,7 @@ Value FunctionObject::call(ExecutionContext *ctx) Value FunctionObject::construct(ExecutionContext *ctx) { - Object *obj = ctx->engine->newObject(); - Value proto = __get__(ctx, ctx->engine->id_prototype); - if (proto.isObject()) - obj->prototype = proto.objectValue(); - ctx->thisObject = Value::fromObject(obj); - Value result = call(ctx); - if (result.isObject()) - return result; - return ctx->thisObject; + return call(ctx); } ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) @@ -790,19 +788,6 @@ SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *m } -Value ScriptFunction::construct(VM::ExecutionContext *ctx) -{ - Object *obj = ctx->engine->newObject(); - Value proto = __get__(ctx, ctx->engine->id_prototype); - if (proto.isObject()) - obj->prototype = proto.objectValue(); - ctx->thisObject = Value::fromObject(obj); - Value result = function->code(ctx, function->codeData); - if (result.isObject()) - return result; - return ctx->thisObject; -} - ArgumentsObject::ArgumentsObject(ExecutionContext *context) : context(context) , currentIndex(-1) diff --git a/qmljs_objects.h b/qmljs_objects.h index eacbc0030a..eae6eebcc6 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -598,7 +598,6 @@ struct NativeFunction: FunctionObject { NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); virtual Value call(ExecutionContext *ctx) { return code(ctx); } - virtual Value construct(ExecutionContext *ctx) { ctx->thisObject = code(ctx); return ctx->thisObject; } }; struct ScriptFunction: FunctionObject { @@ -608,7 +607,6 @@ struct ScriptFunction: FunctionObject { virtual ~ScriptFunction(); virtual Value call(ExecutionContext *ctx); - virtual Value construct(ExecutionContext *ctx); virtual ScriptFunction *asScriptFunction() { return this; } }; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 4774c7888a..12ef269303 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -532,10 +532,8 @@ ObjectCtor::ObjectCtor(ExecutionContext *scope) Value ObjectCtor::construct(ExecutionContext *ctx) { if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull()) - ctx->thisObject = Value::fromObject(ctx->engine->newObject()); - else - ctx->thisObject = __qmljs_to_object(ctx->argument(0), ctx); - return ctx->thisObject; + return ctx->thisObject; + return __qmljs_to_object(ctx->argument(0), ctx); } Value ObjectCtor::call(ExecutionContext *ctx) @@ -1004,8 +1002,7 @@ Value StringCtor::construct(ExecutionContext *ctx) value = Value::fromString(ctx->argument(0).toString(ctx)); else value = Value::fromString(ctx, QString()); - ctx->thisObject = Value::fromObject(ctx->engine->newStringObject(value)); - return ctx->thisObject; + return Value::fromObject(ctx->engine->newStringObject(value)); } Value StringCtor::call(ExecutionContext *ctx) @@ -1318,8 +1315,7 @@ NumberCtor::NumberCtor(ExecutionContext *scope) Value NumberCtor::construct(ExecutionContext *ctx) { double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; - ctx->thisObject = Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(d))); - return ctx->thisObject; + return Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(d))); } Value NumberCtor::call(ExecutionContext *ctx) @@ -1497,8 +1493,7 @@ BooleanCtor::BooleanCtor(ExecutionContext *scope) Value BooleanCtor::construct(ExecutionContext *ctx) { const double n = ctx->argument(0).toBoolean(ctx); - ctx->thisObject = Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n))); - return ctx->thisObject; + return Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n))); } Value BooleanCtor::call(ExecutionContext *ctx) @@ -1541,13 +1536,6 @@ ArrayCtor::ArrayCtor(ExecutionContext *scope) { } -Value ArrayCtor::construct(ExecutionContext *ctx) -{ - Value result = call(ctx); - ctx->thisObject = result; - return result; -} - Value ArrayCtor::call(ExecutionContext *ctx) { Array value; @@ -2066,17 +2054,13 @@ Value FunctionCtor::construct(ExecutionContext *ctx) QScopedPointer isel(ctx->engine->iselFactory->create(ctx->engine, &module)); VM::Function *vmf = isel->vmFunction(irf); - ctx->thisObject = Value::fromObject(ctx->engine->newScriptFunction(ctx->engine->rootContext, vmf)); - return ctx->thisObject; + return Value::fromObject(ctx->engine->newScriptFunction(ctx->engine->rootContext, vmf)); } // 15.3.1: This is equivalent to new Function(...) Value FunctionCtor::call(ExecutionContext *ctx) { - Value v = ctx->thisObject; - Value result = construct(ctx); - ctx->thisObject = v; - return result; + return construct(ctx); } void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) @@ -2185,8 +2169,7 @@ Value DateCtor::construct(ExecutionContext *ctx) } Object *d = ctx->engine->newDateObject(Value::fromDouble(t)); - ctx->thisObject = Value::fromObject(d); - return ctx->thisObject; + return Value::fromObject(d); } Value DateCtor::call(ExecutionContext *ctx) @@ -2756,8 +2739,7 @@ Value RegExpCtor::construct(ExecutionContext *ctx) ctx->throwTypeError(); RegExpObject *o = ctx->engine->newRegExpObject(re->value, re->global); - ctx->thisObject = Value::fromObject(o); - return ctx->thisObject; + return Value::fromObject(o); } if (r.isUndefined()) @@ -2788,8 +2770,7 @@ Value RegExpCtor::construct(ExecutionContext *ctx) ctx->throwTypeError(); RegExpObject *o = ctx->engine->newRegExpObject(re, global); - ctx->thisObject = Value::fromObject(o); - return ctx->thisObject; + return Value::fromObject(o); } Value RegExpCtor::call(ExecutionContext *ctx) @@ -2799,10 +2780,7 @@ Value RegExpCtor::call(ExecutionContext *ctx) return ctx->argument(0); } - Value that = ctx->thisObject; - Value result = construct(ctx); - ctx->thisObject = that; - return result; + return construct(ctx); } void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) @@ -2880,55 +2858,42 @@ ErrorCtor::ErrorCtor(ExecutionContext *scope) Value ErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(ctx->engine->newErrorObject(ctx->argument(0))); - return ctx->thisObject; + return Value::fromObject(ctx->engine->newErrorObject(ctx->argument(0))); } Value ErrorCtor::call(ExecutionContext *ctx) { - Value that = ctx->thisObject; - construct(ctx); - ctx->wireUpPrototype(); - Value res = ctx->thisObject; - - ctx->thisObject = that; - return res; + return construct(ctx); } Value EvalErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx)); - return ctx->thisObject; + return Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx)); } Value RangeErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx)); - return ctx->thisObject; + return Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx)); } Value ReferenceErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx)); - return ctx->thisObject; + return Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx)); } Value SyntaxErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) SyntaxErrorObject(ctx, 0)); - return ctx->thisObject; + return Value::fromObject(new (ctx->engine->memoryManager) SyntaxErrorObject(ctx, 0)); } Value TypeErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx)); - return ctx->thisObject; + return Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx)); } Value URIErrorCtor::construct(ExecutionContext *ctx) { - ctx->thisObject = Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx)); - return ctx->thisObject; + return Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx)); } void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index be4b9387e5..8b16b952d1 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -167,7 +167,6 @@ struct ArrayCtor: FunctionObject { ArrayCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx); virtual Value call(ExecutionContext *ctx); }; -- cgit v1.2.3 From 75f656946f52f64eeec86d7937db5415a99d6799 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 17 Dec 2012 09:52:04 +0100 Subject: Smaller cleanups and code simplifications Change-Id: I1634ce8b105ee0d22b67fafa45962fe7c22b9f3a Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index a7703d8790..16a872f6e4 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -184,12 +184,14 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *, String *name) // Section 8.12.2 PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name) { - if (members) - if (PropertyDescriptor *p = members->find(name)) - return p; - - if (prototype) - return prototype->__getPropertyDescriptor__(ctx, name); + Object *o = this; + while (o) { + if (o->members) { + if (PropertyDescriptor *p = o->members->find(name)) + return p; + } + o = o->prototype; + } return 0; } @@ -231,10 +233,9 @@ bool Object::__canPut__(ExecutionContext *ctx, String *name) if (!extensible) return false; return p->isWritable(); - } else { - return extensible; } - return true; + + return extensible; } // Section 8.12.5 -- cgit v1.2.3 From ceba70fec95aadd4697ea33c01c7e77d2942abe0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 17 Dec 2012 10:03:37 +0100 Subject: Allow the compiler to inline use String::isEqualTo instead of qmljs_string_equal to allow for inlining. Change-Id: I55d41ab34f1e04cb0f752d8018e3ce9b11a90d1d Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 20 ++++++++++---------- qmljs_runtime.cpp | 2 +- qmljs_value.cpp | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index e2bae58c56..435257b8fa 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -84,11 +84,11 @@ bool ExecutionContext::hasBinding(String *name) const return false; for (unsigned int i = 0; i < function->varCount; ++i) { - if (__qmljs_string_equal(function->varList[i], name)) + if (function->varList[i]->isEqualTo(name)) return true; } for (unsigned int i = 0; i < function->formalParameterCount; ++i) { - if (__qmljs_string_equal(function->formalParameterList[i], name)) + if (function->formalParameterList[i]->isEqualTo(name)) return true; } if (activation) @@ -116,13 +116,13 @@ bool ExecutionContext::setMutableBinding(ExecutionContext *scope, String *name, { // ### throw if scope->strict is true, and it would change an immutable binding for (unsigned int i = 0; i < variableCount(); ++i) { - if (__qmljs_string_equal(variables()[i], name)) { + if (variables()[i]->isEqualTo(name)) { locals[i] = value; return true; } } for (unsigned int i = 0; i < formalCount(); ++i) { - if (__qmljs_string_equal(formals()[i], name)) { + if (formals()[i]->isEqualTo(name)) { arguments[i] = value; return true; } @@ -141,11 +141,11 @@ Value ExecutionContext::getBindingValue(ExecutionContext *scope, String *name, b assert(function); for (unsigned int i = 0; i < variableCount(); ++i) { - if (__qmljs_string_equal(variables()[i], name)) + if (variables()[i]->isEqualTo(name)) return locals[i]; } for (unsigned int i = 0; i < formalCount(); ++i) { - if (__qmljs_string_equal(formals()[i], name)) + if (formals()[i]->isEqualTo(name)) return arguments[i]; } if (activation) { @@ -285,10 +285,10 @@ Value ExecutionContext::getProperty(String *name) } for (unsigned int i = 0; i < ctx->variableCount(); ++i) - if (__qmljs_string_equal(ctx->variables()[i], name)) + if (ctx->variables()[i]->isEqualTo(name)) return ctx->locals[i]; for (unsigned int i = 0; i < ctx->formalCount(); ++i) - if (__qmljs_string_equal(ctx->formals()[i], name)) + if (ctx->formals()[i]->isEqualTo(name)) return ctx->arguments[i]; if (ctx->activation) { bool hasProperty = false; @@ -319,10 +319,10 @@ Value ExecutionContext::getPropertyNoThrow(String *name) } for (unsigned int i = 0; i < ctx->variableCount(); ++i) - if (__qmljs_string_equal(ctx->variables()[i], name)) + if (ctx->variables()[i]->isEqualTo(name)) return ctx->locals[i]; for (unsigned int i = 0; i < ctx->formalCount(); ++i) - if (__qmljs_string_equal(ctx->formals()[i], name)) + if (ctx->formals()[i]->isEqualTo(name)) return ctx->arguments[i]; if (ctx->activation) { bool hasProperty = false; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 21aa1c2acc..17292d4858 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -660,7 +660,7 @@ uint __qmljs_equal(Value x, Value y, ExecutionContext *ctx) case Value::Integer_Type: return x.integerValue() == y.integerValue(); case Value::String_Type: - return __qmljs_string_equal(x.stringValue(), y.stringValue()); + return x.stringValue()->isEqualTo(y.stringValue()); case Value::Object_Type: return x.objectValue() == y.objectValue(); default: // double diff --git a/qmljs_value.cpp b/qmljs_value.cpp index 32c0ca6b1f..9ea4be1456 100644 --- a/qmljs_value.cpp +++ b/qmljs_value.cpp @@ -83,7 +83,7 @@ bool Value::sameValue(Value other) { if (val == other.val) return true; if (isString() && other.isString()) - return __qmljs_string_equal(stringValue(), other.stringValue()); + return stringValue()->isEqualTo(other.stringValue()); return false; } -- cgit v1.2.3 From 344a5b804cc9570ebe51f62ff492d3a896d97856 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 17 Dec 2012 10:29:48 +0100 Subject: Avoid lookup of locals and formals when possible If we do not have a Function or the function doesn't need activation, we directly generate code to get and set formals and locals, so there's no need to look these up from the execution context. Change-Id: I888fc65fd2527f9102fab7cae2822600f87f9edc Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 435257b8fa..229d3504ef 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -273,8 +273,7 @@ Value ExecutionContext::getProperty(String *name) return thisObject; for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { - if (ctx->withObject) { - With *w = ctx->withObject; + if (With *w = ctx->withObject) { while (w) { bool hasProperty = false; Value v = w->object->__get__(ctx, name, &hasProperty); @@ -284,12 +283,16 @@ Value ExecutionContext::getProperty(String *name) } } - for (unsigned int i = 0; i < ctx->variableCount(); ++i) - if (ctx->variables()[i]->isEqualTo(name)) - return ctx->locals[i]; - for (unsigned int i = 0; i < ctx->formalCount(); ++i) - if (ctx->formals()[i]->isEqualTo(name)) - return ctx->arguments[i]; + if (FunctionObject *f = ctx->function) { + if (f->needsActivation || ctx->withObject) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return ctx->locals[i]; + for (unsigned int i = 0; i < f->formalParameterCount; ++i) + if (f->formalParameterList[i]->isEqualTo(name)) + return ctx->arguments[i]; + } + } if (ctx->activation) { bool hasProperty = false; Value v = ctx->activation->__get__(ctx, name, &hasProperty); @@ -307,8 +310,7 @@ Value ExecutionContext::getPropertyNoThrow(String *name) return thisObject; for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { - if (ctx->withObject) { - With *w = ctx->withObject; + if (With *w = ctx->withObject) { while (w) { bool hasProperty = false; Value v = w->object->__get__(ctx, name, &hasProperty); @@ -318,12 +320,16 @@ Value ExecutionContext::getPropertyNoThrow(String *name) } } - for (unsigned int i = 0; i < ctx->variableCount(); ++i) - if (ctx->variables()[i]->isEqualTo(name)) - return ctx->locals[i]; - for (unsigned int i = 0; i < ctx->formalCount(); ++i) - if (ctx->formals()[i]->isEqualTo(name)) - return ctx->arguments[i]; + if (FunctionObject *f = ctx->function) { + if (f->needsActivation || ctx->withObject) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return ctx->locals[i]; + for (unsigned int i = 0; i < f->formalParameterCount; ++i) + if (f->formalParameterList[i]->isEqualTo(name)) + return ctx->arguments[i]; + } + } if (ctx->activation) { bool hasProperty = false; Value v = ctx->activation->__get__(ctx, name, &hasProperty); -- cgit v1.2.3 From 2d4daa564719d61191aa4907494e8a728fbe335b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 17 Dec 2012 13:34:39 +0100 Subject: Resolve argument names from right to left Duplicated names for arguments are allowed in JS, later ones shadow previous ones. So we need to iterate from back to front to resolve the names correctly. Change-Id: If427ce9d11ac561457c24e41f79c11263fa0a8dc Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 42 ++++++++++++++++++++++-------------------- qv4codegen.cpp | 2 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 229d3504ef..d8f4320ca6 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -115,18 +115,19 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) bool ExecutionContext::setMutableBinding(ExecutionContext *scope, String *name, Value value) { // ### throw if scope->strict is true, and it would change an immutable binding - for (unsigned int i = 0; i < variableCount(); ++i) { - if (variables()[i]->isEqualTo(name)) { - locals[i] = value; - return true; - } - } - for (unsigned int i = 0; i < formalCount(); ++i) { - if (formals()[i]->isEqualTo(name)) { - arguments[i] = value; - return true; - } + if (function) { + for (unsigned int i = 0; i < function->varCount; ++i) + if (function->varList[i]->isEqualTo(name)) { + locals[i] = value; + return true; + } + for (int i = (int)function->formalParameterCount - 1; i >= 0; --i) + if (function->formalParameterList[i]->isEqualTo(name)) { + arguments[i] = value; + return true; + } } + if (activation && activation->__hasProperty__(scope, name)) { activation->__put__(scope, name, value); return true; @@ -140,14 +141,15 @@ Value ExecutionContext::getBindingValue(ExecutionContext *scope, String *name, b Q_UNUSED(strict); assert(function); - for (unsigned int i = 0; i < variableCount(); ++i) { - if (variables()[i]->isEqualTo(name)) - return locals[i]; - } - for (unsigned int i = 0; i < formalCount(); ++i) { - if (formals()[i]->isEqualTo(name)) - return arguments[i]; + if (function) { + for (unsigned int i = 0; i < function->varCount; ++i) + if (function->varList[i]->isEqualTo(name)) + return locals[i]; + for (int i = (int)function->formalParameterCount - 1; i >= 0; --i) + if (function->formalParameterList[i]->isEqualTo(name)) + return arguments[i]; } + if (activation) { bool hasProperty = false; Value v = activation->__get__(scope, name, &hasProperty); @@ -288,7 +290,7 @@ Value ExecutionContext::getProperty(String *name) for (unsigned int i = 0; i < f->varCount; ++i) if (f->varList[i]->isEqualTo(name)) return ctx->locals[i]; - for (unsigned int i = 0; i < f->formalParameterCount; ++i) + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) if (f->formalParameterList[i]->isEqualTo(name)) return ctx->arguments[i]; } @@ -325,7 +327,7 @@ Value ExecutionContext::getPropertyNoThrow(String *name) for (unsigned int i = 0; i < f->varCount; ++i) if (f->varList[i]->isEqualTo(name)) return ctx->locals[i]; - for (unsigned int i = 0; i < f->formalParameterCount; ++i) + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) if (f->formalParameterList[i]->isEqualTo(name)) return ctx->arguments[i]; } diff --git a/qv4codegen.cpp b/qv4codegen.cpp index cc605d9878..063243ad15 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1774,7 +1774,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, int Codegen::indexOfArgument(const QStringRef &string) const { - for (int i = 0; i < _function->formals.size(); ++i) { + for (int i = _function->formals.size() - 1; i >= 0; --i) { if (*_function->formals.at(i) == string) return i; } -- cgit v1.2.3 From 4f22fcb2494e1e770edae4d08f394f93ed519740 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 17 Dec 2012 15:18:38 +0100 Subject: Fix a bug in catch{}finally{} and simplify code There was a bug in the implementation of unwindException(), that caused failures when called twice from one catch statement. Also refactor and simplify the TryStatement code further by introducing a rethrow builtin. Change-Id: I77bf37f1707042f402488ef2dfaf4e59bf8dc82a Reviewed-by: Simon Hausmann --- moth/qv4instr_moth_p.h | 1 + moth/qv4isel_moth.cpp | 7 +++++++ qmljs_runtime.cpp | 6 ++++++ qmljs_runtime.h | 1 + qv4codegen.cpp | 56 +++++++++++++++----------------------------------- qv4codegen_p.h | 1 - qv4ir.cpp | 2 ++ qv4ir_p.h | 1 + qv4isel_masm.cpp | 5 ++++- 9 files changed, 38 insertions(+), 42 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 65a6a0303e..1699598032 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -150,6 +150,7 @@ union Instr enum { builtin_typeof, builtin_throw, + builtin_rethrow, builtin_create_exception_handler, builtin_delete_exception_handler, builtin_get_exception, diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 14fd1cbbd3..ebd3099ff8 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -326,6 +326,13 @@ void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempInd addInstruction(call); } break; + case IR::Name::builtin_rethrow: { + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_rethrow; + call.targetTempIndex = targetTempIndex; + addInstruction(call); + } break; + case IR::Name::builtin_create_exception_handler: { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_create_exception_handler; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 17292d4858..3d6e0536ca 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -862,6 +862,12 @@ void __qmljs_builtin_throw(Value val, ExecutionContext *context) __qmljs_throw(val, context); } +void __qmljs_builtin_rethrow(ExecutionContext *context) +{ + __qmljs_throw(context->engine->exception, context); +} + + void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx) { Object *obj = __qmljs_to_object(o, ctx).asObject(); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index aa2bc4e7cb..c072e4ac58 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -100,6 +100,7 @@ Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext * Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context); void __qmljs_builtin_throw(Value val, ExecutionContext *context); +void __qmljs_builtin_rethrow(ExecutionContext *context); void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx); void __qmljs_builtin_pop_with(ExecutionContext *ctx); void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 063243ad15..a15ba22ca4 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -2174,9 +2174,6 @@ bool Codegen::visit(TryStatement *ast) TryCleanup tcf(_tryCleanup, ast->finallyExpression); _tryCleanup = &tcf; - IR::BasicBlock *after = _function->newBasicBlock(); - assert(catchBody || finallyBody); - int inCatch = 0; if (catchBody) { inCatch = _block->newTemp(); @@ -2190,7 +2187,7 @@ bool Codegen::visit(TryStatement *ast) _block = tryBody; statement(ast->statement); - _block->JUMP(finallyBody ? finallyBody : after); + _block->JUMP(finallyBody); // regular flow does not go into the catch statement if (catchBody) { @@ -2219,51 +2216,29 @@ bool Codegen::visit(TryStatement *ast) // reset the variable name to the one from the outer scope _env->members.insert(ast->catchExpression->name.toString(), hiddenIndex); - _block->JUMP(finallyBody ? finallyBody : after); + _block->JUMP(finallyBody); } _tryCleanup = tcf.parent; - if (finallyBody) - generateFinallyBlock(finallyBody, !catchBody, ast->finallyExpression, hasException, after); - - _block = after; - - return false; -} - -void Codegen::generateFinallyBlock(IR::BasicBlock *finallyBlock, bool exceptionNeedsSaving, Finally *ast, int hasException, IR::BasicBlock *after) -{ - _block = finallyBlock; - - int exception; - if (exceptionNeedsSaving) { - // save the exception so we can rethrow it later. - IR::BasicBlock *saveExceptionBlock = _function->newBasicBlock(); - IR::BasicBlock *realFinallyBlock = _function->newBasicBlock(); - _block->CJUMP(_block->TEMP(hasException), saveExceptionBlock, realFinallyBlock); - - _block = saveExceptionBlock; - exception = saveExceptionBlock->newTemp(); - move(_block->TEMP(exception), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0)); - _block->JUMP(realFinallyBlock); - - _block = realFinallyBlock; - } + IR::BasicBlock *after = _function->newBasicBlock(); + _block = finallyBody; _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); - if (ast && ast->statement) - statement(ast->statement); - - if (exceptionNeedsSaving) { - IR::BasicBlock *restoreExceptionBlock = _function->newBasicBlock(); - _block->CJUMP(_block->TEMP(hasException), restoreExceptionBlock, after); - _block = restoreExceptionBlock; - _block->MOVE(_block->TEMP(_returnAddress), _block->TEMP(exception)); - _block->JUMP(_throwBlock); + if (ast->finallyExpression && ast->finallyExpression->statement) + statement(ast->finallyExpression->statement); + + if (!catchBody) { + IR::BasicBlock *rethrowBlock = _function->newBasicBlock(); + _block->CJUMP(_block->TEMP(hasException), rethrowBlock, after); + _block = rethrowBlock; + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_rethrow, 0, 0), 0)); } else { _block->CJUMP(_block->TEMP(hasException), _throwBlock, after); } + _block = after; + + return false; } void Codegen::unwindException(Codegen::TryCleanup *outest) @@ -2277,6 +2252,7 @@ void Codegen::unwindException(Codegen::TryCleanup *outest) if (tc->finally && tc->finally->statement) statement(tc->finally->statement); } + qSwap(_tryCleanup, tryCleanup); } bool Codegen::visit(VariableStatement *ast) diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 3776db6304..2cb32353fa 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -221,7 +221,6 @@ protected: AST::SourceElements *body, Mode mode = FunctionCode); int indexOfArgument(const QStringRef &string) const; - void generateFinallyBlock(IR::BasicBlock *finallyBlock, bool exceptionNeedsSaving, AST::Finally *ast, int hasException, IR::BasicBlock *after); void unwindException(TryCleanup *outest); void statement(AST::Statement *ast); diff --git a/qv4ir.cpp b/qv4ir.cpp index 8d4c4f8b53..a61ce0ba50 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -226,6 +226,8 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_delete"; case Name::builtin_throw: return "builtin_throw"; + case Name::builtin_rethrow: + return "builtin_rethrow"; case Name::builtin_create_exception_handler: return "builtin_create_exception_handler"; case Name::builtin_delete_exception_handler: diff --git a/qv4ir_p.h b/qv4ir_p.h index 8c570d1947..802a11de48 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -276,6 +276,7 @@ struct Name: Expr { builtin_typeof, builtin_delete, builtin_throw, + builtin_rethrow, builtin_create_exception_handler, builtin_delete_exception_handler, builtin_get_exception, diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 0b2b5b2f1f..694044fac7 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -484,9 +484,12 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu case IR::Name::builtin_throw: { IR::Temp *arg = call->args->expr->asTemp(); assert(arg != 0); - generateFunctionCall(result, __qmljs_builtin_throw, arg, Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, arg, Assembler::ContextRegister); } break; + case IR::Name::builtin_rethrow: + generateFunctionCall(Assembler::Void, __qmljs_builtin_rethrow, Assembler::ContextRegister); + break; case IR::Name::builtin_create_exception_handler: generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_create_exception_handler, Assembler::ContextRegister); generateFunctionCall(result, setjmp, Assembler::ReturnValueRegister); -- cgit v1.2.3 From b43e1edf9bf0e129a588885f0bc52a73ebe2a5ce Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 17 Dec 2012 22:43:22 +0100 Subject: Fix native stack traversal * For the traversal range, don't take the top of the stack but one value pointer below, as the top is actually the end of the stack and we can't read beyond it. For the bottom go one pointer beyond a locally declared (and thus aligned) value. This ensure sane and aligned boundaries for the traversal. * For quick elimination of pointer values on the stack that do not actually point into one of our managed objects, implement Lars' idea: Take the heap chunk beginning and end pointers and do a lower bound search. An even index indicates that the pointer is before the start of a chunk, thus out of range. An odd index indicates that it is before a chunk end and therefore in range. * For obscure reasons we also seem to sometimes hit "dangling" pointers into otherwise already dealloc'ed objects (as debug output in dealloc() indicates), so protect ourselves against that. Change-Id: Ic3337932777871bec370a3441581801273d53bd4 Reviewed-by: Lars Knoll --- qv4mm.cpp | 40 +++++++++++++++++++++++++++++++++------- qv4mm.h | 2 +- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/qv4mm.cpp b/qv4mm.cpp index a5215cd6be..aa3844bf03 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -40,6 +40,7 @@ #include #include +#include using namespace QQmlJS::VM; @@ -420,13 +421,38 @@ MemoryManagerWithNativeStack::~MemoryManagerWithNativeStack() void MemoryManagerWithNativeStack::collectRootsOnStack(QVector &roots) const { + if (!m_d->heapChunks.count()) + return; + Value valueOnStack = Value::undefinedValue(); StackBounds bounds = StackBounds::currentThreadStackBounds(); - Value* top = reinterpret_cast(bounds.origin()); - Value* current = reinterpret_cast(bounds.current()); - qDebug("Collecting on stack. top %p current %p\n", top, current); - for (; current < top; ++current) { - if (current->asObject()) - qDebug("found object %p on stack", (void*)current); - add(roots, *current); + Value* top = reinterpret_cast(bounds.origin()) - 1; + Value* current = (&valueOnStack) + 1; + + char** heapChunkBoundaries = (char**)alloca(m_d->heapChunks.count() * 2 * sizeof(char*)); + char** heapChunkBoundariesEnd = heapChunkBoundaries + 2 * m_d->heapChunks.count(); + int i = 0; + for (QLinkedList >::Iterator it = m_d->heapChunks.begin(), end = + m_d->heapChunks.end(); it != end; ++it) { + heapChunkBoundaries[i++] = it->first; + heapChunkBoundaries[i++] = it->first + it->second; + } + qSort(heapChunkBoundaries, heapChunkBoundariesEnd); + + int blah = 0; + for (; current < top; ++current, ++blah) { + Object* possibleObject = current->asObject(); + if (!possibleObject) + continue; + char* genericPtr = reinterpret_cast(possibleObject); + if (genericPtr < *heapChunkBoundaries || genericPtr >= *(heapChunkBoundariesEnd - 1)) + continue; + int index = qLowerBound(heapChunkBoundaries, heapChunkBoundariesEnd, genericPtr) - heapChunkBoundaries; + // An odd index means the pointer is _before_ the end of a heap chunk and therefore valid. + if (index & 1) { + // It appears to happen that the stack can still contain a pointer to an already + // dealloc'ed. Skip those. + if (toObject(possibleObject)->info.inUse) + roots.append(possibleObject); + } } } diff --git a/qv4mm.h b/qv4mm.h index 530eadb5db..a4ea893c15 100644 --- a/qv4mm.h +++ b/qv4mm.h @@ -161,7 +161,7 @@ private: std::size_t sweep(std::size_t &largestFreedBlock); std::size_t sweep(char *chunkStart, std::size_t chunkSize, std::size_t &largestFreedBlock); -private: +protected: QScopedPointer m_d; }; -- cgit v1.2.3 From 2522d2d3ff8a8dbd2e45bad1d8dc7cb1f45dd10b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 17 Dec 2012 22:48:24 +0100 Subject: For the JIT enable the memory manager that traverses the native stack for references to managed objects Change-Id: Ie4a26ddc75abd1382af29b966915437ad485a041 Reviewed-by: Lars Knoll --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 7a79a7cd77..28f06022e1 100644 --- a/main.cpp +++ b/main.cpp @@ -339,7 +339,7 @@ int main(int argc, char *argv[]) mm.reset(new QQmlJS::Moth::MemoryManager); iSelFactory.reset(new QQmlJS::Moth::ISelFactory); } else { - mm.reset(new QQmlJS::VM::MemoryManagerWithoutGC); + mm.reset(new QQmlJS::VM::MemoryManagerWithNativeStack); iSelFactory.reset(new QQmlJS::MASM::ISelFactory); } -- cgit v1.2.3 From 97625f03a660f0caf7960e9bad2c2fc792abc296 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 17 Dec 2012 21:56:19 +0100 Subject: ensure correct initialization order for local variables section 10.5 requires that function definitions get initialized at the beginning of the method. variable declarations do not override the function definitions. assignments to variables happen when they appear in the source code. Also remove a duplicated intializations of variables to undefined. This is already being done by initCallContext or builtin_declare_vars, so no need to do it in the generated code again. Change-Id: I63805b97017f8676d57e0662073689e852b6ac23 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 68 +++++++++++++++++++++++++++++++--------------------------- qv4codegen_p.h | 43 +++++++++++++++++++++++++++++-------- 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index a15ba22ca4..0e5eedb5bf 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -308,7 +308,7 @@ protected: checkName(ast->name, ast->identifierToken); if (ast->name == QLatin1String("arguments")) _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - _env->enter(ast->name.toString()); + _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration); return true; } @@ -324,7 +324,7 @@ protected: { if (_env) { _env->hasNestedFunctions = true; - _env->enter(ast->name.toString()); + _env->enter(ast->name.toString(), Environment::FunctionDefinition); } enterEnvironment(ast); checkForArguments(ast->formals); @@ -340,9 +340,8 @@ protected: virtual bool visit(FunctionDeclaration *ast) { - _env->functions.append(ast); _env->hasNestedFunctions = true; - _env->enter(ast->name.toString()); + _env->enter(ast->name.toString(), Environment::FunctionDefinition, ast); if (ast->name == QLatin1String("arguments")) _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; enterEnvironment(ast); @@ -771,14 +770,11 @@ void Codegen::sourceElements(SourceElements *ast) void Codegen::variableDeclaration(VariableDeclaration *ast) { IR::Expr *initializer = 0; - if (ast->expression) { - Result expr = expression(ast->expression); - assert(expr.code); - initializer = *expr; - } - - if (! initializer) - initializer = _block->CONST(IR::UndefinedType, 0); + if (!ast->expression) + return; + Result expr = expression(ast->expression); + assert(expr.code); + initializer = *expr; if (! _env->parent || _function->insideWith) { // it's global code. @@ -1250,7 +1246,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col) } } - if (index >= _env->vars.size()) { + if (index >= _env->members.size()) { // named local variable, e.g. in a catch statement return _block->TEMP(index); } @@ -1695,16 +1691,16 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, // variables in global code are properties of the global context object, not locals as with other functions. if (_mode == FunctionCode) { - for (int i = 0; i < _env->vars.size(); ++i) { - const QString &local = _env->vars.at(i); + for (Environment::MemberMap::iterator it = _env->members.begin(); it != _env->members.end(); ++it) { + const QString &local = it.key(); function->LOCAL(local); unsigned t = entryBlock->newTemp(); - assert(t == unsigned(i)); + (*it).index = t; } } else { IR::ExprList *args = 0; - for (int i = 0; i < _env->vars.size(); ++i) { - const QString &local = _env->vars.at(i); + for (Environment::MemberMap::const_iterator it = _env->members.constBegin(); it != _env->members.constEnd(); ++it) { + const QString &local = it.key(); IR::ExprList *next = function->New(); next->expr = entryBlock->NAME(local, 0, 0); next->next = args; @@ -1739,17 +1735,20 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, _function->RECEIVE(it->name.toString()); } - foreach (AST::FunctionDeclaration *f, _env->functions) { - IR::Function *function = defineFunction(f->name.toString(), f, f->formals, - f->body ? f->body->elements : 0); - if (_debugger) - _debugger->setSourceLocation(function, f->functionToken.startLine, f->functionToken.startColumn); - if (! _env->parent) - move(_block->NAME(f->name.toString(), f->identifierToken.startLine, f->identifierToken.startColumn), - _block->CLOSURE(function)); - else - move(_block->TEMP(_env->findMember(f->name.toString())), - _block->CLOSURE(function)); + foreach (const Environment::Member &member, _env->members) { + if (member.function) { + IR::Function *function = defineFunction(member.function->name.toString(), member.function, member.function->formals, + member.function->body ? member.function->body->elements : 0); + if (_debugger) + _debugger->setSourceLocation(function, member.function->functionToken.startLine, member.function->functionToken.startColumn); + if (! _env->parent) { + move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), + _block->CLOSURE(function)); + } else { + assert(member.index >= 0); + move(_block->TEMP(member.index), _block->CLOSURE(function)); + } + } } sourceElements(body); @@ -2209,13 +2208,18 @@ bool Codegen::visit(TryStatement *ast) // the variable used in the catch statement is local and hides any global // variable with the same name. - int hiddenIndex = _env->findMember(ast->catchExpression->name.toString()); - _env->members.insert(ast->catchExpression->name.toString(), exception); + const Environment::Member undefinedMember = { Environment::UndefinedMember, -1 , 0 }; + const Environment::Member catchMember = { Environment::VariableDefinition, exception, 0 }; + Environment::Member m = _env->members.value(ast->catchExpression->name.toString(), undefinedMember); + _env->members.insert(ast->catchExpression->name.toString(), catchMember); statement(ast->catchExpression->statement); // reset the variable name to the one from the outer scope - _env->members.insert(ast->catchExpression->name.toString(), hiddenIndex); + if (m.type == Environment::UndefinedMember) + _env->members.remove(ast->catchExpression->name.toString()); + else + _env->members.insert(ast->catchExpression->name.toString(), m); _block->JUMP(finallyBody); } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 2cb32353fa..132bfcc184 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -43,6 +43,7 @@ #include "qv4ir_p.h" #include +#include namespace QQmlJS { @@ -110,9 +111,21 @@ protected: struct Environment { Environment *parent; - QHash members; - QVector vars; - QVector functions; + + enum MemberType { + UndefinedMember, + VariableDefinition, + VariableDeclaration, + FunctionDefinition + }; + struct Member { + MemberType type; + int index; + AST::FunctionDeclaration *function; + }; + typedef QMap MemberMap; + + MemberMap members; int maxNumberOfArguments; bool hasDirectEval; bool hasNestedFunctions; @@ -139,7 +152,11 @@ protected: int findMember(const QString &name) const { - return members.value(name, -1); + MemberMap::const_iterator it = members.find(name); + if (it == members.end()) + return -1; + assert((*it).index != -1 || !parent); + return (*it).index; } bool lookupMember(const QString &name, Environment **scope, int *index, int *distance) @@ -157,13 +174,21 @@ protected: return false; } - void enter(const QString &name) + void enter(const QString &name, MemberType type, AST::FunctionDeclaration *function = 0) { if (! name.isEmpty()) { - int idx = members.value(name, -1); - if (idx == -1) { - members.insert(name, vars.count()); - vars.append(name); + MemberMap::iterator it = members.find(name); + if (it == members.end()) { + Member m; + m.index = -1; + m.type = type; + m.function = function; + members.insert(name, m); + } else { + if ((*it).type < type) { + (*it).type = type; + (*it).function = function; + } } } } -- cgit v1.2.3 From dfd7da9c33ded0447135f1e4160d9d530a371131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Wed, 12 Dec 2012 16:29:14 +0100 Subject: Allow to run tests in parallel. The test_interpreter script waste a lot of time in the kernel process, that should be fixed. For know magic '-j' option allows to speedup test execution a bit. Change-Id: Ib1f0cbecc2053e434eb8df4e2e1edb22bc3e7846 Reviewed-by: Erik Verbruggen --- test_interpreter.py | 61 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/test_interpreter.py b/test_interpreter.py index f69d96eb7b..494ed7a3dd 100755 --- a/test_interpreter.py +++ b/test_interpreter.py @@ -15,6 +15,7 @@ import resource import subprocess import threading import sys +import multiprocessing ### Settings & Command Line Processing ######################################## @@ -32,6 +33,7 @@ class Settings: # "test262/console/harness/cth*" # ] blacklist_patterns = [] + job_count = 1 class CommandLineProcessor: """ Process command line arguments and overwrite values in Settings.""" @@ -61,6 +63,7 @@ Options: default: '%s'). -p, --printers Print results by specified printers. printers is a comma separated list (valid: '%s', default: '%s'). + -j, --jobs Allow N jobs at once (default: %s). -v, --verbose Print some more information, e.g. files are being processed (default: not enabled). @@ -73,12 +76,13 @@ Options: ",".join(Settings.modes), ",".join(self.valid_printers), ",".join(Settings.printers), + Settings.job_count )) def run(self): try: - options, arguments = getopt.getopt(self.args, "hvm:i:d:p:t:", - ["help", "verbose", "modes=", "interpreter=", "input-dir=", "printers=", "timeout="]) + options, arguments = getopt.getopt(self.args, "hvm:i:d:p:t:j:", + ["help", "verbose", "modes=", "interpreter=", "input-dir=", "printers=", "timeout=", "jobs="]) except getopt.error, msg: sys.stdout.write("For help use -h, --help.") sys.exit(2) @@ -118,6 +122,8 @@ Options: Settings.interpreter_timeout = int(argument) elif option in ("-v", "--verbose"): Settings.verbose = True + elif option in ("-j", "--jobs"): + Settings.job_count = int(argument) else: sys.stdout.write("Unknown option '%s'." %(option)) sys.stdout.write("For help use -h, --help.") @@ -206,6 +212,30 @@ class InterpreterResult: self.output_stdout= output_stdout self.output_stderr = output_stderr +def executeCommand(data): + interpreterRunner, input_file = data + if not os.path.exists(input_file): + sys.stderr.write("Warning: File '%s' does not exist anymore for interpreter '%s'.\n" + %(input_file, interpreterRunner.runner_info.info)) + return + if Settings.verbose: + sys.stdout.write(" %s\n" %(input_file)) + command = ['./' + Settings.interpreter] + interpreterRunner.extra_args + [input_file] + + process = Command(command) + process.run(timeout=Settings.interpreter_timeout) + if not process.finished_within_timeout: + sys.stderr.write("Warning: Interpreter '%s' did not finish within %d seconds processing file '%s'.\n" + %(interpreterRunner.runner_info.info, Settings.interpreter_timeout, input_file)) + result = InterpreterResult( + input_file, + process.finished_within_timeout, + process.exit_code, + process.stdout, + process.stderr + ) + return result + class InterpreterRunner: def __init__(self, input_files, label, extra_args, printers): self.input_files = input_files @@ -232,29 +262,12 @@ class InterpreterRunner: def run(self): sys.stdout.write("Running interpreter '%s %s' for each test file.\n" %(Settings.interpreter, " ".join(self.extra_args))) + self.__printers_begin() - for input_file in self.input_files: - if not os.path.exists(input_file): - sys.stderr.write("Warning: File '%s' does not exist anymore for interpreter '%s'.\n" - %(input_file, self.runner_info.info)) - break - if Settings.verbose: - sys.stdout.write(" %s\n" %(input_file)) - command = ['./' + Settings.interpreter] + self.extra_args + [input_file] - - process = Command(command) - process.run(timeout=Settings.interpreter_timeout) - if not process.finished_within_timeout: - sys.stderr.write("Warning: Interpreter '%s' did not finish within %d seconds processing file '%s'.\n" - %(self.runner_info.info, Settings.interpreter_timeout, input_file)) - result = InterpreterResult( - input_file, - process.finished_within_timeout, - process.exit_code, - process.stdout, - process.stderr - ) - self.__printers_do(result) + processPool = multiprocessing.Pool(processes = Settings.job_count) + results = processPool.map(executeCommand, [(self, input_file) for input_file in self.input_files]) + for result in results: + self.__printers_do(result) # TODO we may put it to the executeCommand, if it is safe self.__printers_end() ### Printers ################################################################## -- cgit v1.2.3 From 451f26ab81a22bb45cf07bb5bb50061a12eefb1c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 12 Dec 2012 13:50:13 +0100 Subject: Fix compilation after changes in the QML parser. Change-Id: I5c6903446a252139fde4ce180f6c01a5eff85406 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 14 +++++++++++++- qv4codegen_p.h | 4 +++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 0e5eedb5bf..4c9648f251 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -866,7 +866,19 @@ bool Codegen::visit(Program *) return false; } -bool Codegen::visit(PropertyNameAndValueList *) +bool Codegen::visit(PropertyAssignmentList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyNameAndValue *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyGetterSetter *) { assert(!"unreachable"); return false; diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 132bfcc184..b9e0a71414 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -279,7 +279,9 @@ protected: virtual bool visit(AST::FormalParameterList *ast); virtual bool visit(AST::FunctionBody *ast); virtual bool visit(AST::Program *ast); - virtual bool visit(AST::PropertyNameAndValueList *ast); + virtual bool visit(AST::PropertyNameAndValue *ast); + virtual bool visit(AST::PropertyAssignmentList *ast); + virtual bool visit(AST::PropertyGetterSetter *ast); virtual bool visit(AST::SourceElements *ast); virtual bool visit(AST::StatementList *ast); virtual bool visit(AST::UiArrayMemberList *ast); -- cgit v1.2.3 From 0f1203b8b12d19386e16945171b727a271cf3e6d Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 12 Dec 2012 13:54:31 +0100 Subject: Added check for formal parameter names in strict mode. Also fixed up other error messages. Change-Id: I26bfab761ab6a8ced3755a8e3dfbc42d428194e3 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 88 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 31 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 4c9648f251..cf4d94f885 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -42,6 +42,7 @@ #include "qv4codegen_p.h" #include "debugging.h" +#include #include #include #include @@ -251,7 +252,7 @@ protected: || name == QLatin1String("public") || name == QLatin1String("static") || name == QLatin1String("yield")) { - _cg->throwSyntaxError(loc, "Unexpected strict mode reserved word"); + _cg->throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Unexpected strict mode reserved word")); } } } @@ -322,14 +323,7 @@ protected: virtual bool visit(FunctionExpression *ast) { - if (_env) { - _env->hasNestedFunctions = true; - _env->enter(ast->name.toString(), Environment::FunctionDefinition); - } - enterEnvironment(ast); - checkForArguments(ast->formals); - if (ast->body) - checkDirectivePrologue(ast->body->elements); + enterFunction(ast, ast->name.toString(), ast->formals, ast->body); return true; } @@ -338,16 +332,20 @@ protected: leaveEnvironment(); } + virtual bool visit(PropertyGetterSetter *ast) + { + enterFunction(ast, QString(), ast->formals, ast->functionBody); + return true; + } + + virtual void endVisit(PropertyGetterSetter *) + { + leaveEnvironment(); + } + virtual bool visit(FunctionDeclaration *ast) { - _env->hasNestedFunctions = true; - _env->enter(ast->name.toString(), Environment::FunctionDefinition, ast); - if (ast->name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - enterEnvironment(ast); - checkForArguments(ast->formals); - if (ast->body) - checkDirectivePrologue(ast->body->elements); + enterFunction(ast, ast->name.toString(), ast->formals, ast->body, ast); return true; } @@ -359,14 +357,38 @@ protected: virtual bool visit(WithStatement *ast) { if (_env->isStrict) { - // TODO: give a proper error message - qDebug("TODO: give proper error message @%u:%u", ast->withToken.startLine, ast->withToken.startColumn); + _cg->throwSyntaxError(ast->withToken, QCoreApplication::translate("qv4codegen", "'with' statement is not allowed in strict mode")); return false; } return true; } +private: + void enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionDeclaration *decl = 0) + { + bool wasStrict = false; + if (_env) { + _env->hasNestedFunctions = true; + _env->enter(name, Environment::FunctionDefinition, decl); + if (name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + wasStrict = _env->isStrict; + } + + enterEnvironment(ast); + checkForArguments(formals); + + if (body) + checkDirectivePrologue(body->elements); + + if (wasStrict || _env->isStrict) + for (FormalParameterList *it = formals; it; it = it->next) + if (it->name == QLatin1String("eval") || it->name == QLatin1String("arguments")) + _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "'%1' cannot be used as parameter name in strict mode").arg(it->name.toString())); + } + + Codegen *_cg; Environment *_env; QStack _envStack; @@ -1068,9 +1090,8 @@ bool Codegen::visit(BinaryExpression *ast) break; case QSOperator::Assign: - if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) { - throwSyntaxError(ast->lastSourceLocation(), "syntax error"); - } + if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) + throwSyntaxError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of assignment operator is not an lvalue")); if (_expr.accept(nx)) { move(*left, *right); @@ -1093,9 +1114,8 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::InplaceRightShift: case QSOperator::InplaceURightShift: case QSOperator::InplaceXor: { - if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) { - throwSyntaxError(ast->lastSourceLocation(), "syntax error"); - } + if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) + throwSyntaxError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of inplace operator is not an lvalue")); if (_expr.accept(nx)) { move(*left, *right, baseOp(ast->op)); @@ -1341,10 +1361,16 @@ bool Codegen::visit(ObjectLiteral *ast) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Object"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); - for (PropertyNameAndValueList *it = ast->properties; it; it = it->next) { - QString name = propertyName(it->name); - Result value = expression(it->value); - move(member(_block->TEMP(t), _function->newString(name)), *value); + for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { + if (PropertyNameAndValue *nv = AST::cast(it->assignment)) { + QString name = propertyName(nv->name); + Result value = expression(nv->value); + move(member(_block->TEMP(t), _function->newString(name)), *value); + } else if (PropertyGetterSetter *gs = AST::cast(it->assignment)) { + assert(!"todo!"); + } else { + Q_UNREACHABLE(); + } } _expr.code = _block->TEMP(t); return false; @@ -1842,7 +1868,7 @@ bool Codegen::visit(BreakStatement *ast) return false; } } - throwSyntaxError(ast->lastSourceLocation(), QString("Undefined label '") + ast->label.toString() + QString("'")); + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); } return false; @@ -1864,7 +1890,7 @@ bool Codegen::visit(ContinueStatement *ast) return false; } } - throwSyntaxError(ast->lastSourceLocation(), QString("Undefined label '") + ast->label.toString() + QString("'")); + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); } return false; } -- cgit v1.2.3 From 1014d1fb1cfa2d44d6d3fdb51dfb93b736e7fc26 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 18 Dec 2012 09:06:03 +0100 Subject: More compilation fixes. Change-Id: I5940e0b1e72c06420ae95ff3adfd78572888c886 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 2 ++ qv4isel_llvm.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 16a872f6e4..3766081bd7 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -184,6 +184,8 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *, String *name) // Section 8.12.2 PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name) { + Q_UNUSED(ctx); + Object *o = this; while (o) { if (o->members) { diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 5ea5df9d58..eea515cdea 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -780,6 +780,11 @@ void LLVMInstructionSelection::genBinop(llvm::Value *result, IR::Binop *e) Q_UNREACHABLE(); break; + case IR::OpIncrement: + case IR::OpDecrement: + assert(!"TODO!"); + break; + case IR::OpBitAnd: op = _llvmModule->getFunction("__qmljs_llvm_bit_and"); break; case IR::OpBitOr: op = _llvmModule->getFunction("__qmljs_llvm_bit_or"); break; case IR::OpBitXor: op = _llvmModule->getFunction("__qmljs_llvm_bit_xor"); break; -- cgit v1.2.3 From fde4002b2c6636c9a03bcfbc8fac24e4d4222d00 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 18 Dec 2012 08:08:31 +0100 Subject: Remove unused variable While the name may suggest that the blah variable is a very performance critical piece of the stack traversal algorithm, it turns out to be of little relevance :) Change-Id: Ia12eaf5f169a6eae64f005364da7452e1ef86daf Reviewed-by: Lars Knoll --- qv4mm.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qv4mm.cpp b/qv4mm.cpp index aa3844bf03..4cdb211a92 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -438,8 +438,7 @@ void MemoryManagerWithNativeStack::collectRootsOnStack(QVector &ro } qSort(heapChunkBoundaries, heapChunkBoundariesEnd); - int blah = 0; - for (; current < top; ++current, ++blah) { + for (; current < top; ++current) { Object* possibleObject = current->asObject(); if (!possibleObject) continue; -- cgit v1.2.3 From daab2673cf1d3ee0948fd123290bc4c76978bf27 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 18 Dec 2012 10:59:26 +0100 Subject: Fix moth isel for typeof. Change-Id: If5b5a91a69d6b6bf0fd3eaf4c21a42c575839be2 Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 31 ++++++++++++++++++++++++++++++- moth/qv4isel_moth.cpp | 34 ++++++++++++++++++++++++++-------- moth/qv4vme_moth.cpp | 20 ++++++++++++++++---- 3 files changed, 72 insertions(+), 13 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 1699598032..d1b2de6ca3 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -23,6 +23,10 @@ F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ F(CallBuiltinDeleteName, callBuiltinDeleteName) \ F(CallBuiltinDeleteValue, callBuiltinDeleteValue) \ + F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \ + F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \ + F(CallBuiltinTypeofName, callBuiltinTypeofName) \ + F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \ F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ F(CreateValue, createValue) \ F(CreateProperty, createProperty) \ @@ -148,7 +152,6 @@ union Instr struct instr_callBuiltin { MOTH_INSTR_HEADER enum { - builtin_typeof, builtin_throw, builtin_rethrow, builtin_create_exception_handler, @@ -185,6 +188,28 @@ union Instr int tempIndex; int targetTempIndex; }; + struct instr_callBuiltinTypeofMember { + MOTH_INSTR_HEADER + int base; + VM::String *member; + int targetTempIndex; + }; + struct instr_callBuiltinTypeofSubscript { + MOTH_INSTR_HEADER + int base; + int index; + int targetTempIndex; + }; + struct instr_callBuiltinTypeofName { + MOTH_INSTR_HEADER + VM::String *name; + int targetTempIndex; + }; + struct instr_callBuiltinTypeofValue { + MOTH_INSTR_HEADER + int tempIndex; + int targetTempIndex; + }; struct instr_callBuiltinDeclareVar { MOTH_INSTR_HEADER bool isDeletable; @@ -283,6 +308,10 @@ union Instr instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; instr_callBuiltinDeleteName callBuiltinDeleteName; instr_callBuiltinDeleteValue callBuiltinDeleteValue; + instr_callBuiltinTypeofMember callBuiltinTypeofMember; + instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript; + instr_callBuiltinTypeofName callBuiltinTypeofName; + instr_callBuiltinTypeofValue callBuiltinTypeofValue; instr_callBuiltinDeclareVar callBuiltinDeclareVar; instr_createValue createValue; instr_createProperty createProperty; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index ebd3099ff8..a3d03d4d12 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -279,14 +279,32 @@ void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempInd } break; case IR::Name::builtin_typeof: { - IR::Temp *arg = c->args->expr->asTemp(); - assert(arg != 0); - - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_typeof; - prepareCallArgs(c->args, call.argc, call.args); - call.targetTempIndex = targetTempIndex; - addInstruction(call); + if (IR::Member *m = c->args->expr->asMember()) { + Instruction::CallBuiltinTypeofMember call; + call.base = m->base->asTemp()->index; + call.member = engine()->identifier(*m->name); + call.targetTempIndex = targetTempIndex; + addInstruction(call); + } else if (IR::Subscript *ss = c->args->expr->asSubscript()) { + Instruction::CallBuiltinTypeofSubscript call; + call.base = ss->base->asTemp()->index; + call.index = ss->index->asTemp()->index; + call.targetTempIndex = targetTempIndex; + addInstruction(call); + } else if (IR::Name *n = c->args->expr->asName()) { + Instruction::CallBuiltinTypeofName call; + call.name = engine()->identifier(*n->id); + call.targetTempIndex = targetTempIndex; + addInstruction(call); + } else if (IR::Temp *arg = c->args->expr->asTemp()){ + assert(arg != 0); + Instruction::CallBuiltinTypeofValue call; + call.tempIndex = arg->index; + call.targetTempIndex = targetTempIndex; + addInstruction(call); + } else { + assert(false); + } } break; case IR::Name::builtin_delete: { diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 86b396f422..b8404bc550 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -234,10 +234,6 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co VM::Value *args = stack + argStart; void *buf; switch (instr.builtin) { - case Instr::instr_callBuiltin::builtin_typeof: - Q_ASSERT(instr.argc == 1); - TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof(args[0], context); - break; case Instr::instr_callBuiltin::builtin_throw: TRACE(builtin_throw, "Throwing now...%s", ""); Q_ASSERT(instr.argc == 1); @@ -305,6 +301,22 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co TEMP(instr.targetTempIndex) = VM::Value::fromBoolean(false); MOTH_END_INSTR(CallBuiltinDeleteValue) + MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) + TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof_member(TEMP(instr.base), instr.member, context); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) + TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof_element(TEMP(instr.base), TEMP(instr.index), context); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofName) + TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof_name(instr.name, context); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) + TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof(TEMP(instr.tempIndex), context); + MOTH_END_INSTR(CallBuiltinTypeofValue) + MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) __qmljs_builtin_declare_var(context, instr.isDeletable, instr.varName); MOTH_END_INSTR(CallBuiltinDeleteValue) -- cgit v1.2.3 From 256b8e60ef1690abdfcbf9a75ec302c4ae3d9e36 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 18 Dec 2012 10:59:54 +0100 Subject: Fix instruction tracing. Change-Id: Ie2ff005e2914bc372e4c6d08dd28d34efdde8da3 Reviewed-by: Lars Knoll --- debugging.cpp | 2 +- moth/qv4vme_moth.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/debugging.cpp b/debugging.cpp index 4376d0a736..64fd940f16 100644 --- a/debugging.cpp +++ b/debugging.cpp @@ -150,7 +150,7 @@ void Debugger::enterFunction(FunctionState *state) #ifdef DO_TRACE_INSTR QString n = name(_callStack[callIndex(state->context())].function); - std::cerr << "*** Entering \"" << qPrintable(n) << "\" with " << state->context()->variableEnvironment->argumentCount << " args" << std::endl; + std::cerr << "*** Entering \"" << qPrintable(n) << "\" with " << state->context()->argumentCount << " args" << std::endl; // for (unsigned i = 0; i < state->context()->variableEnvironment->argumentCount; ++i) // std::cerr << " " << i << ": " << currentArg(i) << std::endl; #endif // DO_TRACE_INSTR diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index b8404bc550..2a1302f423 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -60,15 +60,15 @@ static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, VM::Va if (index < 0) { kind = "arg"; pos = -index - 1; - } else if (index < (int) varEnv->varCount) { + } else if (index < (int) context->variableCount()) { kind = "local"; pos = index; } else { kind = "temp"; - pos = index - varEnv->varCount; + pos = index - context->variableCount(); } - fprintf(stderr, " tempValue: index = %d : %s = %d, stack size = %d\n", - index, kind, pos, stack.size()); + fprintf(stderr, " tempValue: index = %d : %s = %d\n", + index, kind, pos); #endif // DO_TRACE_INSTR if (index < 0) { -- cgit v1.2.3 From 328ee44e8a3645623e8400d278563b286d178b2b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 18 Dec 2012 11:01:32 +0100 Subject: Fix liveness analyses for hidden exception handling TEMPs. By passing the inCatch/hasException temps to builtin_delete_exception_handler, the TEMPs are marked as alive all through the exception handling block. Change-Id: Ib0f17059e04c03ca98d264759bb2a7e4786ed9be Reviewed-by: Lars Knoll --- qv4codegen.cpp | 20 +++++++++++++++----- qv4codegen_p.h | 6 ++++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index cf4d94f885..65e7a337ab 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -2208,9 +2208,6 @@ bool Codegen::visit(TryStatement *ast) // We always need a finally body to clean up the exception handler IR::BasicBlock *finallyBody = _function->newBasicBlock(); - TryCleanup tcf(_tryCleanup, ast->finallyExpression); - _tryCleanup = &tcf; - int inCatch = 0; if (catchBody) { inCatch = _block->newTemp(); @@ -2220,6 +2217,19 @@ bool Codegen::visit(TryStatement *ast) int hasException = _block->newTemp(); move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_create_exception_handler, 0, 0), 0)); + // Pass the hidden "inCatch" and "hasException" TEMPs to the + // builtin_delete_exception_handler, in order to have those TEMPs alive for + // the duration of the exception handling block. + IR::ExprList *deleteExceptionArgs = _function->New(); + deleteExceptionArgs->init(_block->TEMP(hasException)); + if (inCatch) { + deleteExceptionArgs->next = _function->New(); + deleteExceptionArgs->next->init(_block->TEMP(inCatch)); + } + + TryCleanup tcf(_tryCleanup, ast->finallyExpression, deleteExceptionArgs); + _tryCleanup = &tcf; + _block->CJUMP(_block->TEMP(hasException), catchBody ? catchBody : finallyBody, tryBody); _block = tryBody; @@ -2266,7 +2276,7 @@ bool Codegen::visit(TryStatement *ast) IR::BasicBlock *after = _function->newBasicBlock(); _block = finallyBody; - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), deleteExceptionArgs)); if (ast->finallyExpression && ast->finallyExpression->statement) statement(ast->finallyExpression->statement); @@ -2288,7 +2298,7 @@ void Codegen::unwindException(Codegen::TryCleanup *outest) TryCleanup *tryCleanup = _tryCleanup; qSwap(_tryCleanup, tryCleanup); while (_tryCleanup != outest) { - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), 0)); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), _tryCleanup->deleteExceptionArgs)); TryCleanup *tc = _tryCleanup; _tryCleanup = tc->parent; if (tc->finally && tc->finally->statement) diff --git a/qv4codegen_p.h b/qv4codegen_p.h index b9e0a71414..4ee9c5ac08 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -207,9 +207,11 @@ protected: struct TryCleanup { TryCleanup *parent; AST::Finally *finally; + IR::ExprList *deleteExceptionArgs; - TryCleanup(TryCleanup *parent, AST::Finally *finally) - : parent(parent), finally(finally) {} + TryCleanup(TryCleanup *parent, AST::Finally *finally, IR::ExprList *deleteExceptionArgs) + : parent(parent), finally(finally), deleteExceptionArgs(deleteExceptionArgs) + {} }; struct Loop { -- cgit v1.2.3 From b1511245979406ba4f552635ba8e65b925d65eec Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 18 Dec 2012 12:09:06 +0100 Subject: Fix temp compression, and actually add var decl instructions. Change-Id: Ic73a8e4284fd7644e37251498a659e107e49f0d8 Reviewed-by: Simon Hausmann --- moth/qv4isel_moth.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index a3d03d4d12..ed70b42a8f 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -29,6 +29,7 @@ class CompressTemps: public IR::StmtVisitor, IR::ExprVisitor public: void run(IR::Function *function) { + _seenTemps.clear(); _nextFree = 0; _active.reserve(function->tempCount); _localCount = function->locals.size(); @@ -90,6 +91,10 @@ private: virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } virtual void visitTemp(IR::Temp *e) { + if (_seenTemps.contains(e)) + return; + _seenTemps.insert(e); + if (e->index < 0) return; if (e->index < _localCount) // don't optimise locals yet. @@ -173,6 +178,7 @@ private: private: typedef QVector > ActiveTemps; ActiveTemps _active; + QSet _seenTemps; IR::Stmt *_currentStatement; int _localCount; int _nextFree; @@ -410,6 +416,7 @@ void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempInd Instruction::CallBuiltinDeclareVar call; call.isDeletable = isDeletable; call.varName = engine()->newString(*it->expr->asName()->id); + addInstruction(call); } } break; -- cgit v1.2.3 From b9c3d61eeb5f0dc8e538d94a41c2edae06acca38 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 18 Dec 2012 13:53:18 +0100 Subject: Set the argumentCount in the Context to the length of arguments. Change-Id: I2bc0c6130248dfca6764222bcc95a4d2e6f82233 Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index d8f4320ca6..2edf2a892f 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -412,6 +412,7 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha std::copy(args, args + argc, arguments); if (argc < function->formalParameterCount) std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); + argumentCount = function->formalParameterCount; } locals = function->varCount ? new Value[function->varCount] : 0; -- cgit v1.2.3 From 50eef1d4a1b2fde99c5b8fc104ccb50ae0f03584 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 18 Dec 2012 13:53:32 +0100 Subject: Debugging fixes. Change-Id: I53b7301c28314210f96acc358744ff7e2a65546d Reviewed-by: Lars Knoll --- debugging.cpp | 4 +--- debugging.h | 10 ++++++++-- moth/qv4isel_moth.cpp | 4 ++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/debugging.cpp b/debugging.cpp index 64fd940f16..8033f36592 100644 --- a/debugging.cpp +++ b/debugging.cpp @@ -123,9 +123,7 @@ FunctionDebugInfo *Debugger::debugInfo(VM::FunctionObject *function) const QString Debugger::name(VM::FunctionObject *function) const { if (FunctionDebugInfo *i = debugInfo(function)) - if (i->function) - if (const QString *n = i->function->name) - return *n; + return i->name; return QString(); } diff --git a/debugging.h b/debugging.h index e2833148f2..e6626c53ce 100644 --- a/debugging.h +++ b/debugging.h @@ -47,9 +47,15 @@ namespace Debugging { class Debugger; struct FunctionDebugInfo { // TODO: use opaque d-pointers here - IR::Function *function; + QString name; unsigned startLine, startColumn; - FunctionDebugInfo(IR::Function *function): function(function), startLine(0), startColumn(0) {} + + FunctionDebugInfo(IR::Function *function): + startLine(0), startColumn(0) + { + if (function->name) + name = *function->name; + } void setSourceLocation(unsigned line, unsigned column) { startLine = line; startColumn = column; } diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index ed70b42a8f..0277fc60f1 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -29,6 +29,10 @@ class CompressTemps: public IR::StmtVisitor, IR::ExprVisitor public: void run(IR::Function *function) { +#ifdef DEBUG_TEMP_COMPRESSION + qDebug() << "starting on function" << (*function->name) << "with" << function->tempCount << "temps."; +#endif // DEBUG_TEMP_COMPRESSION + _seenTemps.clear(); _nextFree = 0; _active.reserve(function->tempCount); -- cgit v1.2.3 From eb5471f4fbdc2bbd1b811f935fa34664671d8c67 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 18 Dec 2012 10:21:16 +0100 Subject: Updated wtf and masm from upstream (r137997) This also brings in the page allocation code from WTF Change-Id: If6f9fdb4fb2d10530f06e3e9524c95ff5876d084 Reviewed-by: Lars Knoll --- masm/assembler/ARMAssembler.h | 7 + masm/assembler/ARMv7Assembler.h | 112 +++++------- masm/assembler/AbstractMacroAssembler.h | 20 ++- masm/assembler/LinkBuffer.cpp | 23 ++- masm/assembler/LinkBuffer.h | 14 +- masm/assembler/MIPSAssembler.h | 25 +++ masm/assembler/MacroAssembler.h | 67 ++++--- masm/assembler/MacroAssemblerARM.h | 71 +++++++- masm/assembler/MacroAssemblerARMv7.h | 24 +++ masm/assembler/MacroAssemblerMIPS.h | 56 +++++- masm/assembler/MacroAssemblerSH4.h | 271 ++++++++++++++++++++++++++-- masm/assembler/MacroAssemblerX86.h | 34 ++++ masm/assembler/MacroAssemblerX86Common.h | 4 + masm/assembler/MacroAssemblerX86_64.h | 27 +++ masm/assembler/RepatchBuffer.h | 28 +++ masm/assembler/SH4Assembler.h | 65 +++++-- masm/assembler/X86Assembler.h | 78 +++++++- masm/disassembler/Disassembler.cpp | 43 +++++ masm/disassembler/Disassembler.h | 11 +- masm/disassembler/UDis86Disassembler.cpp | 4 +- masm/disassembler/udis86/differences.txt | 2 + masm/disassembler/udis86/itab.py | 16 +- masm/masm.pri | 23 +++ masm/stubs/WTFStubs.cpp | 19 +- masm/wtf/Assertions.h | 2 + masm/wtf/Atomics.h | 10 +- masm/wtf/CheckedArithmetic.h | 4 +- masm/wtf/DataLog.h | 90 +++++++++- masm/wtf/FilePrintStream.cpp | 64 +++++++ masm/wtf/FilePrintStream.h | 62 +++++++ masm/wtf/OSAllocator.h | 115 ++++++++++++ masm/wtf/OSAllocatorPosix.cpp | 184 +++++++++++++++++++ masm/wtf/OSAllocatorWin.cpp | 80 +++++++++ masm/wtf/OwnPtrCommon.h | 8 + masm/wtf/PageAllocation.h | 120 +++++++++++++ masm/wtf/PageAllocationAligned.cpp | 87 +++++++++ masm/wtf/PageAllocationAligned.h | 70 ++++++++ masm/wtf/PageBlock.cpp | 78 ++++++++ masm/wtf/PageBlock.h | 88 +++++++++ masm/wtf/PageReservation.h | 149 +++++++++++++++ masm/wtf/PassOwnPtr.h | 172 ++++++++++++++++++ masm/wtf/Platform.h | 72 +++++--- masm/wtf/PrintStream.cpp | 112 ++++++++++++ masm/wtf/PrintStream.h | 300 +++++++++++++++++++++++++++++++ masm/wtf/RawPointer.h | 58 ++++++ masm/wtf/StdLibExtras.h | 144 ++++++--------- masm/wtf/ThreadingPrimitives.h | 25 --- masm/wtf/VMTags.h | 75 ++++++++ masm/wtf/Vector.h | 42 +---- 49 files changed, 2876 insertions(+), 379 deletions(-) create mode 100644 masm/disassembler/Disassembler.cpp create mode 100644 masm/wtf/FilePrintStream.cpp create mode 100644 masm/wtf/FilePrintStream.h create mode 100644 masm/wtf/OSAllocator.h create mode 100644 masm/wtf/OSAllocatorPosix.cpp create mode 100644 masm/wtf/OSAllocatorWin.cpp create mode 100644 masm/wtf/PageAllocation.h create mode 100644 masm/wtf/PageAllocationAligned.cpp create mode 100644 masm/wtf/PageAllocationAligned.h create mode 100644 masm/wtf/PageBlock.cpp create mode 100644 masm/wtf/PageBlock.h create mode 100644 masm/wtf/PageReservation.h create mode 100644 masm/wtf/PassOwnPtr.h create mode 100644 masm/wtf/PrintStream.cpp create mode 100644 masm/wtf/PrintStream.h create mode 100644 masm/wtf/RawPointer.h create mode 100644 masm/wtf/VMTags.h diff --git a/masm/assembler/ARMAssembler.h b/masm/assembler/ARMAssembler.h index 38d0c5e6d2..ebab46d98a 100644 --- a/masm/assembler/ARMAssembler.h +++ b/masm/assembler/ARMAssembler.h @@ -402,6 +402,13 @@ namespace JSC { emitInstruction(toARMWord(cc) | MOV | SetConditionalCodes, rd, ARMRegisters::r0, op2); } + static void revertJump(void* instructionStart, RegisterID rd, ARMWord imm) + { + ARMWord* insn = reinterpret_cast(instructionStart); + ARMWord* address = getLdrImmAddress(insn); + *address = imm; + } + void bic(int rd, int rn, ARMWord op2, Condition cc = AL) { emitInstruction(toARMWord(cc) | BIC, rd, rn, op2); diff --git a/masm/assembler/ARMv7Assembler.h b/masm/assembler/ARMv7Assembler.h index e9b9fcc50e..b93ec6e63f 100644 --- a/masm/assembler/ARMv7Assembler.h +++ b/masm/assembler/ARMv7Assembler.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2010, 2012 Apple Inc. All rights reserved. * Copyright (C) 2010 University of Szeged * * Redistribution and use in source and binary forms, with or without @@ -507,7 +507,7 @@ public: private: // ARMv7, Appx-A.6.3 - bool BadReg(RegisterID reg) + static bool BadReg(RegisterID reg) { return (reg == ARMRegisters::sp) || (reg == ARMRegisters::pc); } @@ -1261,6 +1261,18 @@ public: m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOV_imm_T3, imm.m_value.imm4, rd, imm); } + + static void revertJumpTo_movT3(void* instructionStart, RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isValid()); + ASSERT(!imm.isEncodedImm()); + ASSERT(!BadReg(rd)); + + uint16_t* address = static_cast(instructionStart); + address[0] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, imm); + address[1] = twoWordOp5i6Imm4Reg4EncodedImmSecond(rd, imm); + cacheFlush(address, sizeof(uint16_t) * 2); + } ALWAYS_INLINE void mov(RegisterID rd, ARMThumbImmediate imm) { @@ -1928,7 +1940,6 @@ public: return LinkConditionalBX; const int paddingSize = JUMP_ENUM_SIZE(jumpType); - bool mayTriggerErrata = false; if (jumpType == JumpCondition) { // 2-byte conditional T1 @@ -1937,17 +1948,13 @@ public: return LinkJumpT1; // 4-byte conditional T3 const uint16_t* jumpT3Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT3))); - if (canBeJumpT3(jumpT3Location, to, mayTriggerErrata)) { - if (!mayTriggerErrata) - return LinkJumpT3; - } + if (canBeJumpT3(jumpT3Location, to)) + return LinkJumpT3; // 4-byte conditional T4 with IT const uint16_t* conditionalJumpT4Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkConditionalJumpT4))); - if (canBeJumpT4(conditionalJumpT4Location, to, mayTriggerErrata)) { - if (!mayTriggerErrata) - return LinkConditionalJumpT4; - } + if (canBeJumpT4(conditionalJumpT4Location, to)) + return LinkConditionalJumpT4; } else { // 2-byte unconditional T2 const uint16_t* jumpT2Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT2))); @@ -1955,10 +1962,8 @@ public: return LinkJumpT2; // 4-byte unconditional T4 const uint16_t* jumpT4Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT4))); - if (canBeJumpT4(jumpT4Location, to, mayTriggerErrata)) { - if (!mayTriggerErrata) - return LinkJumpT4; - } + if (canBeJumpT4(jumpT4Location, to)) + return LinkJumpT4; // use long jump sequence return LinkBX; } @@ -2057,12 +2062,12 @@ public: ASSERT(from.isSet()); ASSERT(reinterpret_cast(to) & 1); - setPointer(reinterpret_cast(reinterpret_cast(code) + from.m_offset) - 1, to); + setPointer(reinterpret_cast(reinterpret_cast(code) + from.m_offset) - 1, to, false); } static void linkPointer(void* code, AssemblerLabel where, void* value) { - setPointer(reinterpret_cast(code) + where.m_offset, value); + setPointer(reinterpret_cast(code) + where.m_offset, value, false); } static void relinkJump(void* from, void* to) @@ -2080,7 +2085,7 @@ public: ASSERT(!(reinterpret_cast(from) & 1)); ASSERT(reinterpret_cast(to) & 1); - setPointer(reinterpret_cast(from) - 1, to); + setPointer(reinterpret_cast(from) - 1, to, true); } static void* readCallTarget(void* from) @@ -2092,7 +2097,7 @@ public: { ASSERT(!(reinterpret_cast(where) & 1)); - setInt32(where, value); + setInt32(where, value, true); } static void repatchCompact(void* where, int32_t offset) @@ -2119,7 +2124,7 @@ public: { ASSERT(!(reinterpret_cast(where) & 1)); - setPointer(where, value); + setPointer(where, value, true); } static void* readPointer(void* where) @@ -2133,22 +2138,13 @@ public: ASSERT(!(bitwise_cast(to) & 1)); uint16_t* ptr = reinterpret_cast(instructionStart) + 2; - // Ensure that we're not in one of those errata-triggering thingies. If we are, then - // prepend a nop. - bool spansTwo4K = ((reinterpret_cast(ptr) & 0xfff) == 0x002); - - if (spansTwo4K) { - ptr[-2] = OP_NOP_T1; - ptr++; - } - linkJumpT4(ptr, to); cacheFlush(ptr - 2, sizeof(uint16_t) * 2); } static ptrdiff_t maxJumpReplacementSize() { - return 6; + return 4; } static void replaceWithLoad(void* instructionStart) @@ -2164,11 +2160,11 @@ public: ptr[0] |= OP_LDR_imm_T3; ptr[1] |= (ptr[1] & 0x0F00) << 4; ptr[1] &= 0xF0FF; + cacheFlush(ptr, sizeof(uint16_t) * 2); break; default: ASSERT_NOT_REACHED(); } - cacheFlush(ptr, sizeof(uint16_t) * 2); } static void replaceWithAddressComputation(void* instructionStart) @@ -2182,13 +2178,13 @@ public: ptr[0] |= OP_ADD_imm_T3; ptr[1] |= (ptr[1] & 0xF000) >> 4; ptr[1] &= 0x0FFF; + cacheFlush(ptr, sizeof(uint16_t) * 2); break; case OP_ADD_imm_T3: break; default: ASSERT_NOT_REACHED(); } - cacheFlush(ptr, sizeof(uint16_t) * 2); } unsigned debugOffset() { return m_formatter.debugOffset(); } @@ -2291,7 +2287,7 @@ private: return VFPOperand(op); } - static void setInt32(void* code, uint32_t value) + static void setInt32(void* code, uint32_t value, bool flush) { uint16_t* location = reinterpret_cast(code); ASSERT(isMOV_imm_T3(location - 4) && isMOVT(location - 2)); @@ -2303,7 +2299,8 @@ private: location[-2] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); location[-1] = twoWordOp5i6Imm4Reg4EncodedImmSecond((location[-1] >> 8) & 0xf, hi16); - cacheFlush(location - 4, 4 * sizeof(uint16_t)); + if (flush) + cacheFlush(location - 4, 4 * sizeof(uint16_t)); } static int32_t readInt32(void* code) @@ -2334,9 +2331,9 @@ private: cacheFlush(location, sizeof(uint16_t)); } - static void setPointer(void* code, void* value) + static void setPointer(void* code, void* value, bool flush) { - setInt32(code, reinterpret_cast(value)); + setInt32(code, reinterpret_cast(value), flush); } static bool isB(void* address) @@ -2401,46 +2398,22 @@ private: return ((relative << 20) >> 20) == relative; } - static bool canBeJumpT3(const uint16_t* instruction, const void* target, bool& mayTriggerErrata) + static bool canBeJumpT3(const uint16_t* instruction, const void* target) { ASSERT(!(reinterpret_cast(instruction) & 1)); ASSERT(!(reinterpret_cast(target) & 1)); intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - // From Cortex-A8 errata: - // If the 32-bit Thumb-2 branch instruction spans two 4KiB regions and - // the target of the branch falls within the first region it is - // possible for the processor to incorrectly determine the branch - // instruction, and it is also possible in some cases for the processor - // to enter a deadlock state. - // The instruction is spanning two pages if it ends at an address ending 0x002 - bool spansTwo4K = ((reinterpret_cast(instruction) & 0xfff) == 0x002); - mayTriggerErrata = spansTwo4K; - // The target is in the first page if the jump branch back by [3..0x1002] bytes - bool targetInFirstPage = (relative >= -0x1002) && (relative < -2); - bool wouldTriggerA8Errata = spansTwo4K && targetInFirstPage; - return ((relative << 11) >> 11) == relative && !wouldTriggerA8Errata; + return ((relative << 11) >> 11) == relative; } - static bool canBeJumpT4(const uint16_t* instruction, const void* target, bool& mayTriggerErrata) + static bool canBeJumpT4(const uint16_t* instruction, const void* target) { ASSERT(!(reinterpret_cast(instruction) & 1)); ASSERT(!(reinterpret_cast(target) & 1)); intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - // From Cortex-A8 errata: - // If the 32-bit Thumb-2 branch instruction spans two 4KiB regions and - // the target of the branch falls within the first region it is - // possible for the processor to incorrectly determine the branch - // instruction, and it is also possible in some cases for the processor - // to enter a deadlock state. - // The instruction is spanning two pages if it ends at an address ending 0x002 - bool spansTwo4K = ((reinterpret_cast(instruction) & 0xfff) == 0x002); - mayTriggerErrata = spansTwo4K; - // The target is in the first page if the jump branch back by [3..0x1002] bytes - bool targetInFirstPage = (relative >= -0x1002) && (relative < -2); - bool wouldTriggerA8Errata = spansTwo4K && targetInFirstPage; - return ((relative << 7) >> 7) == relative && !wouldTriggerA8Errata; + return ((relative << 7) >> 7) == relative; } void linkJumpT1(Condition cond, uint16_t* instruction, void* target) @@ -2484,9 +2457,7 @@ private: // FIMXE: this should be up in the MacroAssembler layer. :-( ASSERT(!(reinterpret_cast(instruction) & 1)); ASSERT(!(reinterpret_cast(target) & 1)); - bool scratch; - UNUSED_PARAM(scratch); - ASSERT(canBeJumpT3(instruction, target, scratch)); + ASSERT(canBeJumpT3(instruction, target)); intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); @@ -2501,9 +2472,7 @@ private: // FIMXE: this should be up in the MacroAssembler layer. :-( ASSERT(!(reinterpret_cast(instruction) & 1)); ASSERT(!(reinterpret_cast(target) & 1)); - bool scratch; - UNUSED_PARAM(scratch); - ASSERT(canBeJumpT4(instruction, target, scratch)); + ASSERT(canBeJumpT4(instruction, target)); intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); // ARM encoding for the top two bits below the sign bit is 'peculiar'. @@ -2561,8 +2530,7 @@ private: ASSERT((isMOV_imm_T3(instruction - 5) && isMOVT(instruction - 3) && isBX(instruction - 1)) || (isNOP_T1(instruction - 5) && isNOP_T2(instruction - 4) && isB(instruction - 2))); - bool scratch; - if (canBeJumpT4(instruction, target, scratch)) { + if (canBeJumpT4(instruction, target)) { // There may be a better way to fix this, but right now put the NOPs first, since in the // case of an conditional branch this will be coming after an ITTT predicating *three* // instructions! Looking backwards to modify the ITTT to an IT is not easy, due to diff --git a/masm/assembler/AbstractMacroAssembler.h b/masm/assembler/AbstractMacroAssembler.h index c75adb7e96..ee78ef84eb 100644 --- a/masm/assembler/AbstractMacroAssembler.h +++ b/masm/assembler/AbstractMacroAssembler.h @@ -51,7 +51,7 @@ class LinkBuffer; class RepatchBuffer; class Watchpoint; namespace DFG { -class CorrectableJumpPoint; +struct OSRExit; } template @@ -320,7 +320,7 @@ public: class Label { template friend class AbstractMacroAssembler; - friend class DFG::CorrectableJumpPoint; + friend struct DFG::OSRExit; friend class Jump; friend class JumpReplacementWatchpoint; friend class MacroAssemblerCodeRef; @@ -501,7 +501,7 @@ public: template friend class AbstractMacroAssembler; friend class Call; - friend class DFG::CorrectableJumpPoint; + friend struct DFG::OSRExit; friend class LinkBuffer; public: Jump() @@ -528,6 +528,13 @@ public: { } #endif + + Label label() const + { + Label result; + result.m_label = m_label; + return result; + } void link(AbstractMacroAssembler* masm) const { @@ -586,6 +593,13 @@ public: public: typedef Vector JumpVector; + + JumpList() { } + + JumpList(Jump jump) + { + append(jump); + } void link(AbstractMacroAssembler* masm) { diff --git a/masm/assembler/LinkBuffer.cpp b/masm/assembler/LinkBuffer.cpp index 0176e4307a..c269157ba5 100644 --- a/masm/assembler/LinkBuffer.cpp +++ b/masm/assembler/LinkBuffer.cpp @@ -45,16 +45,15 @@ LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithDisassembly(const char* format, CodeRef result = finalizeCodeWithoutDisassembly(); - dataLog("Generated JIT code for "); + dataLogF("Generated JIT code for "); va_list argList; va_start(argList, format); - WTF::dataLogV(format, argList); + WTF::dataLogFV(format, argList); va_end(argList); - dataLog(":\n"); + dataLogF(":\n"); - dataLog(" Code at [%p, %p):\n", result.code().executableAddress(), static_cast(result.code().executableAddress()) + result.size()); - if (!tryToDisassemble(result.code(), m_size, " ", WTF::dataFile())) - dataLog(" \n"); + dataLogF(" Code at [%p, %p):\n", result.code().executableAddress(), static_cast(result.code().executableAddress()) + result.size()); + disassemble(result.code(), m_size, " ", WTF::dataFile()); return result; } @@ -169,11 +168,11 @@ void LinkBuffer::dumpLinkStatistics(void* code, size_t initializeSize, size_t fi linkCount++; totalInitialSize += initialSize; totalFinalSize += finalSize; - dataLog("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", + dataLogF("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", code, static_cast(initialSize), static_cast(finalSize), static_cast(initialSize - finalSize), 100.0 * (initialSize - finalSize) / initialSize); - dataLog("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", + dataLogF("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", linkCount, totalInitialSize, totalFinalSize, totalInitialSize - totalFinalSize, 100.0 * (totalInitialSize - totalFinalSize) / totalInitialSize); } @@ -192,7 +191,7 @@ void LinkBuffer::dumpCode(void* code, size_t size) size_t tsize = size / sizeof(short); char nameBuf[128]; snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); - dataLog("\t.syntax unified\n" + dataLogF("\t.syntax unified\n" "\t.section\t__TEXT,__text,regular,pure_instructions\n" "\t.globl\t%s\n" "\t.align 2\n" @@ -202,7 +201,7 @@ void LinkBuffer::dumpCode(void* code, size_t size) "%s:\n", nameBuf, nameBuf, code, nameBuf); for (unsigned i = 0; i < tsize; i++) - dataLog("\t.short\t0x%x\n", tcode[i]); + dataLogF("\t.short\t0x%x\n", tcode[i]); #elif CPU(ARM_TRADITIONAL) // gcc -c jit.s // objdump -D jit.o @@ -211,7 +210,7 @@ void LinkBuffer::dumpCode(void* code, size_t size) size_t tsize = size / sizeof(unsigned int); char nameBuf[128]; snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); - dataLog("\t.globl\t%s\n" + dataLogF("\t.globl\t%s\n" "\t.align 4\n" "\t.code 32\n" "\t.text\n" @@ -219,7 +218,7 @@ void LinkBuffer::dumpCode(void* code, size_t size) "%s:\n", nameBuf, code, nameBuf); for (unsigned i = 0; i < tsize; i++) - dataLog("\t.long\t0x%x\n", tcode[i]); + dataLogF("\t.long\t0x%x\n", tcode[i]); #endif } #endif diff --git a/masm/assembler/LinkBuffer.h b/masm/assembler/LinkBuffer.h index 770144d64a..e1882433c1 100644 --- a/masm/assembler/LinkBuffer.h +++ b/masm/assembler/LinkBuffer.h @@ -263,9 +263,9 @@ private: #endif }; -#define FINALIZE_CODE_IF(condition, linkBufferReference, dataLogArgumentsForHeading) \ +#define FINALIZE_CODE_IF(condition, linkBufferReference, dataLogFArgumentsForHeading) \ (UNLIKELY((condition)) \ - ? ((linkBufferReference).finalizeCodeWithDisassembly dataLogArgumentsForHeading) \ + ? ((linkBufferReference).finalizeCodeWithDisassembly dataLogFArgumentsForHeading) \ : (linkBufferReference).finalizeCodeWithoutDisassembly()) // Use this to finalize code, like so: @@ -281,14 +281,14 @@ private: // // ... and so on. // -// Note that the dataLogArgumentsForHeading are only evaluated when showDisassembly +// Note that the dataLogFArgumentsForHeading are only evaluated when showDisassembly // is true, so you can hide expensive disassembly-only computations inside there. -#define FINALIZE_CODE(linkBufferReference, dataLogArgumentsForHeading) \ - FINALIZE_CODE_IF(Options::showDisassembly(), linkBufferReference, dataLogArgumentsForHeading) +#define FINALIZE_CODE(linkBufferReference, dataLogFArgumentsForHeading) \ + FINALIZE_CODE_IF(Options::showDisassembly(), linkBufferReference, dataLogFArgumentsForHeading) -#define FINALIZE_DFG_CODE(linkBufferReference, dataLogArgumentsForHeading) \ - FINALIZE_CODE_IF(Options::showDFGDisassembly(), linkBufferReference, dataLogArgumentsForHeading) +#define FINALIZE_DFG_CODE(linkBufferReference, dataLogFArgumentsForHeading) \ + FINALIZE_CODE_IF((Options::showDisassembly() || Options::showDFGDisassembly()), linkBufferReference, dataLogFArgumentsForHeading) } // namespace JSC diff --git a/masm/assembler/MIPSAssembler.h b/masm/assembler/MIPSAssembler.h index 30f172fb8b..026f87e52a 100644 --- a/masm/assembler/MIPSAssembler.h +++ b/masm/assembler/MIPSAssembler.h @@ -825,6 +825,31 @@ public: #endif } + static void revertJumpToMove(void* instructionStart, RegisterID rt, int imm) + { + MIPSWord* insn = static_cast(instructionStart) + 1; + ASSERT((*insn & 0xfc000000) == 0x34000000); + *insn = (*insn & 0xfc1f0000) | (imm & 0xffff); + cacheFlush(insn, sizeof(MIPSWord)); + } + + static void replaceWithJump(void* instructionStart, void* to) + { + MIPSWord* instruction = reinterpret_cast(instructionStart); + intptr_t jumpTo = reinterpret_cast(to); + + // lui + instruction[0] = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((jumpTo >> 16) & 0xffff); + // ori + instruction[1] = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (jumpTo & 0xffff); + // jr + instruction[2] = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); + // nop + instruction[3] = 0x0; + + cacheFlush(instruction, sizeof(MIPSWord) * 4); + } + static void replaceWithLoad(void* instructionStart) { MIPSWord* insn = reinterpret_cast(instructionStart); diff --git a/masm/assembler/MacroAssembler.h b/masm/assembler/MacroAssembler.h index 134908387d..3d57340f93 100644 --- a/masm/assembler/MacroAssembler.h +++ b/masm/assembler/MacroAssembler.h @@ -266,12 +266,14 @@ public: { return PatchableJump(branchTest32(cond, reg, mask)); } - +#endif // !CPU(ARM_THUMB2) + +#if !CPU(ARM) PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) { return PatchableJump(branch32(cond, reg, imm)); } -#endif +#endif // !(CPU(ARM) void jump(Label target) { @@ -306,7 +308,12 @@ public: ASSERT(condition == Equal || condition == NotEqual); return condition; } - + + static const unsigned BlindingModulus = 64; + bool shouldConsiderBlinding() + { + return !(random() & (BlindingModulus - 1)); + } // Ptr methods // On 32-bit platforms (i.e. x86), these methods directly map onto their 32-bit equivalents. @@ -728,16 +735,6 @@ public: return store64WithAddressOffsetPatch(src, address); } - void movePtrToDouble(RegisterID src, FPRegisterID dest) - { - move64ToDouble(src, dest); - } - - void moveDoubleToPtr(FPRegisterID src, RegisterID dest) - { - moveDoubleTo64(src, dest); - } - void comparePtr(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) { compare64(cond, left, right, dest); @@ -847,26 +844,25 @@ public: using MacroAssemblerBase::and64; using MacroAssemblerBase::convertInt32ToDouble; using MacroAssemblerBase::store64; - bool shouldBlindDouble(double value) { // Don't trust NaN or +/-Infinity - if (!std::isfinite(value)) - return true; + if (!isfinite(value)) + return shouldConsiderBlinding(); // Try to force normalisation, and check that there's no change // in the bit pattern if (bitwise_cast(value * 1.0) != bitwise_cast(value)) - return true; + return shouldConsiderBlinding(); value = abs(value); // Only allow a limited set of fractional components double scaledValue = value * 8; if (scaledValue / 8 != value) - return true; + return shouldConsiderBlinding(); double frac = scaledValue - floor(scaledValue); if (frac != 0.0) - return true; + return shouldConsiderBlinding(); return value > 0xff; } @@ -895,16 +891,14 @@ public: default: { if (value <= 0xff) return false; - JSValue jsValue = JSValue::decode(reinterpret_cast(value)); - if (jsValue.isInt32()) - return shouldBlind(Imm32(jsValue.asInt32())); - if (jsValue.isDouble() && !shouldBlindDouble(jsValue.asDouble())) - return false; - - if (!shouldBlindDouble(bitwise_cast(value))) + if (~value <= 0xff) return false; } } + + if (!shouldConsiderBlinding()) + return false; + return shouldBlindForSpecificArch(value); } @@ -956,8 +950,23 @@ public: default: { if (value <= 0xff) return false; + if (~value <= 0xff) + return false; + + JSValue jsValue = JSValue::decode(value); + if (jsValue.isInt32()) + return shouldBlind(Imm32(jsValue.asInt32())); + if (jsValue.isDouble() && !shouldBlindDouble(jsValue.asDouble())) + return false; + + if (!shouldBlindDouble(bitwise_cast(value))) + return false; } } + + if (!shouldConsiderBlinding()) + return false; + return shouldBlindForSpecificArch(value); } @@ -1076,7 +1085,13 @@ public: default: if (value <= 0xff) return false; + if (~value <= 0xff) + return false; } + + if (!shouldConsiderBlinding()) + return false; + return shouldBlindForSpecificArch(value); #endif } diff --git a/masm/assembler/MacroAssemblerARM.h b/masm/assembler/MacroAssemblerARM.h index 39d94adeaf..527126b438 100644 --- a/masm/assembler/MacroAssemblerARM.h +++ b/masm/assembler/MacroAssemblerARM.h @@ -570,11 +570,7 @@ public: Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right, int useConstantPool = 0) { - ARMWord tmp = (static_cast(right.m_value) == 0x80000000) ? ARMAssembler::InvalidImmediate : m_assembler.getOp2(-right.m_value); - if (tmp != ARMAssembler::InvalidImmediate) - m_assembler.cmn(left, tmp); - else - m_assembler.cmp(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); + internalCompare32(left, right); return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); } @@ -807,6 +803,14 @@ public: return Jump(m_assembler.jmp(ARMCondition(cond))); } + PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) + { + internalCompare32(reg, imm); + Jump jump(m_assembler.loadBranchTarget(ARMRegisters::S1, ARMCondition(cond), true)); + m_assembler.bx(ARMRegisters::S1, ARMCondition(cond)); + return PatchableJump(jump); + } + void breakpoint() { m_assembler.bkpt(0); @@ -889,6 +893,31 @@ public: m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); } + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + ARMWord tmp; + + move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S0, ARMRegisters::S1, 0); + + if ((tmp = ARMAssembler::getOp2(imm.m_value)) != ARMAssembler::InvalidImmediate) + m_assembler.adds(ARMRegisters::S0, ARMRegisters::S0, tmp); + else if ((tmp = ARMAssembler::getOp2(-imm.m_value)) != ARMAssembler::InvalidImmediate) + m_assembler.subs(ARMRegisters::S0, ARMRegisters::S0, tmp); + else { + m_assembler.adds(ARMRegisters::S0, ARMRegisters::S0, m_assembler.getImm(imm.m_value, ARMRegisters::S1)); + move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); + } + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S0, ARMRegisters::S1, 0); + + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S0, ARMRegisters::S1, sizeof(ARMWord)); + if (imm.m_value >= 0) + m_assembler.adc(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + else + m_assembler.sbc(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S0, ARMRegisters::S1, sizeof(ARMWord)); + } + void sub32(TrustedImm32 imm, AbsoluteAddress address) { m_assembler.ldrUniqueImmediate(ARMRegisters::S1, reinterpret_cast(address.m_ptr)); @@ -1266,6 +1295,29 @@ public: return 0; } + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + return label.labelAtOffset(0); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID reg, void* initialValue) + { + ARMAssembler::revertJump(instructionStart.dataLocation(), reg, reinterpret_cast(initialValue) & 0xffff); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + UNREACHABLE_FOR_PLATFORM(); + } + protected: ARMAssembler::Condition ARMCondition(RelationalCondition cond) { @@ -1297,6 +1349,15 @@ private: friend class LinkBuffer; friend class RepatchBuffer; + void internalCompare32(RegisterID left, TrustedImm32 right) + { + ARMWord tmp = (static_cast(right.m_value) == 0x80000000) ? ARMAssembler::InvalidImmediate : m_assembler.getOp2(-right.m_value); + if (tmp != ARMAssembler::InvalidImmediate) + m_assembler.cmn(left, tmp); + else + m_assembler.cmp(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); + } + static void linkCall(void* code, Call call, FunctionPtr function) { ARMAssembler::linkCall(code, call.m_label, function.value()); diff --git a/masm/assembler/MacroAssemblerARMv7.h b/masm/assembler/MacroAssemblerARMv7.h index 1301038e57..8d7a3a69aa 100644 --- a/masm/assembler/MacroAssemblerARMv7.h +++ b/masm/assembler/MacroAssemblerARMv7.h @@ -1758,6 +1758,30 @@ public: { return FunctionPtr(reinterpret_cast(ARMv7Assembler::readCallTarget(call.dataLocation()))); } + + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + const unsigned twoWordOpSize = 4; + return label.labelAtOffset(-twoWordOpSize * 2); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) + { + ARMv7Assembler::revertJumpTo_movT3(instructionStart.dataLocation(), dataTempRegister, ARMThumbImmediate::makeUInt16(reinterpret_cast(initialValue) & 0xffff)); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel, Address, void*) + { + UNREACHABLE_FOR_PLATFORM(); + } protected: ALWAYS_INLINE Jump jump() diff --git a/masm/assembler/MacroAssemblerMIPS.h b/masm/assembler/MacroAssemblerMIPS.h index fc6f9f40d5..3ab2553001 100644 --- a/masm/assembler/MacroAssemblerMIPS.h +++ b/masm/assembler/MacroAssemblerMIPS.h @@ -228,22 +228,40 @@ public: /* li addrTemp, address li immTemp, imm - lw dataTemp, 0(addrTemp) - addu dataTemp, dataTemp, immTemp + lw cmpTemp, 0(addrTemp) + addu dataTemp, cmpTemp, immTemp sw dataTemp, 0(addrTemp) */ move(TrustedImmPtr(address.m_ptr), addrTempRegister); - m_assembler.lw(dataTempRegister, addrTempRegister, 0); - if (imm.m_value >= -32768 && imm.m_value <= 32767 - && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, imm.m_value); + m_assembler.lw(cmpTempRegister, addrTempRegister, 0); + if (imm.m_value >= -32768 && imm.m_value <= 32767 && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, cmpTempRegister, imm.m_value); else { move(imm, immTempRegister); - m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); + m_assembler.addu(dataTempRegister, cmpTempRegister, immTempRegister); } m_assembler.sw(dataTempRegister, addrTempRegister, 0); } + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + /* + add32(imm, address) + sltu immTemp, dataTemp, cmpTemp # set carry-in bit + lw dataTemp, 4(addrTemp) + addiu dataTemp, imm.m_value >> 31 ? -1 : 0 + addu dataTemp, dataTemp, immTemp + sw dataTemp, 4(addrTemp) + */ + add32(imm, address); + m_assembler.sltu(immTempRegister, dataTempRegister, cmpTempRegister); + m_assembler.lw(dataTempRegister, addrTempRegister, 4); + if (imm.m_value >> 31) + m_assembler.addiu(dataTempRegister, dataTempRegister, -1); + m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); + m_assembler.sw(dataTempRegister, addrTempRegister, 4); + } + void and32(RegisterID src, RegisterID dest) { m_assembler.andInsn(dest, dest, src); @@ -2242,6 +2260,30 @@ public: return 0; } + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + return label.labelAtOffset(0); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) + { + MIPSAssembler::revertJumpToMove(instructionStart.dataLocation(), immTempRegister, reinterpret_cast(initialValue) & 0xffff); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + UNREACHABLE_FOR_PLATFORM(); + } + + private: // If m_fixedWidth is true, we will generate a fixed number of instructions. // Otherwise, we can emit any number of instructions. diff --git a/masm/assembler/MacroAssemblerSH4.h b/masm/assembler/MacroAssemblerSH4.h index ca410afa8c..ef210f80cb 100644 --- a/masm/assembler/MacroAssemblerSH4.h +++ b/masm/assembler/MacroAssemblerSH4.h @@ -402,6 +402,34 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) releaseScratch(scratchReg); } + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + RegisterID scr1 = claimScratch(); + RegisterID scr2 = claimScratch(); + + // Add 32-bit LSB first. + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scr1); + m_assembler.movlMemReg(scr1, scr1); // scr1 = 32-bit LSB of int64 @ address + m_assembler.loadConstant(imm.m_value, scr2); + m_assembler.clrt(); + m_assembler.addclRegReg(scr1, scr2); + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scr1); + m_assembler.movlRegMem(scr2, scr1); // Update address with 32-bit LSB result. + + // Then add 32-bit MSB. + m_assembler.addlImm8r(4, scr1); + m_assembler.movlMemReg(scr1, scr1); // scr1 = 32-bit MSB of int64 @ address + m_assembler.movt(scr2); + if (imm.m_value < 0) + m_assembler.addlImm8r(-1, scr2); // Sign extend imm value if needed. + m_assembler.addvlRegReg(scr2, scr1); + m_assembler.loadConstant(reinterpret_cast(address.m_ptr) + 4, scr2); + m_assembler.movlRegMem(scr1, scr2); // Update (address + 4) with 32-bit MSB result. + + releaseScratch(scr2); + releaseScratch(scr1); + } + void sub32(TrustedImm32 imm, RegisterID dest) { if (m_assembler.isImmediate(-imm.m_value)) { @@ -602,6 +630,16 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) releaseScratch(scr); } + void load8Signed(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + load8Signed(scr, address.offset, dest); + releaseScratch(scr); + } + void load32(BaseIndex address, RegisterID dest) { RegisterID scr = claimScratch(); @@ -649,6 +687,32 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) releaseScratch(scr); } + void load8Signed(RegisterID base, int offset, RegisterID dest) + { + if (!offset) { + m_assembler.movbMemReg(base, dest); + return; + } + + if ((offset > 0) && (offset < 64) && (dest == SH4Registers::r0)) { + m_assembler.movbMemReg(offset, base, dest); + return; + } + + if (base != dest) { + m_assembler.loadConstant((offset), dest); + m_assembler.addlRegReg(base, dest); + m_assembler.movbMemReg(dest, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((offset), scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movbMemReg(scr, dest); + releaseScratch(scr); + } + void load8(RegisterID base, int offset, RegisterID dest) { if (!offset) { @@ -749,6 +813,11 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) extuw(dest, dest); } + void load16Signed(RegisterID src, RegisterID dest) + { + m_assembler.movwMemReg(src, dest); + } + void load16(RegisterID r0, RegisterID src, RegisterID dest) { ASSERT(r0 == SH4Registers::r0); @@ -756,6 +825,12 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) extuw(dest, dest); } + void load16Signed(RegisterID r0, RegisterID src, RegisterID dest) + { + ASSERT(r0 == SH4Registers::r0); + m_assembler.movwR0mr(src, dest); + } + void load16(BaseIndex address, RegisterID dest) { RegisterID scr = claimScratch(); @@ -775,6 +850,51 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) releaseScratch(scr); } + void load16Signed(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + + if (address.offset) + add32(TrustedImm32(address.offset), scr); + if (address.base == SH4Registers::r0) + load16Signed(address.base, scr, dest); + else { + add32(address.base, scr); + load16Signed(scr, dest); + } + + releaseScratch(scr); + } + + void store8(RegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + + m_assembler.movbRegMem(src, scr); + + releaseScratch(scr); + } + + void store16(RegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + + m_assembler.movwRegMem(src, scr); + + releaseScratch(scr); + } + void store32(RegisterID src, ImplicitAddress address) { RegisterID scr = claimScratch(); @@ -900,13 +1020,59 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) return result; } - // Floating-point operations + // Floating-point operations static bool supportsFloatingPoint() { return true; } static bool supportsFloatingPointTruncate() { return true; } static bool supportsFloatingPointSqrt() { return true; } static bool supportsFloatingPointAbs() { return false; } + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + m_assembler.fldsfpul((FPRegisterID)(src + 1)); + m_assembler.stsfpulReg(dest1); + m_assembler.fldsfpul(src); + m_assembler.stsfpulReg(dest2); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + UNUSED_PARAM(scratch); + m_assembler.ldsrmfpul(src1); + m_assembler.fstsfpul((FPRegisterID)(dest + 1)); + m_assembler.ldsrmfpul(src2); + m_assembler.fstsfpul(dest); + } + + void loadFloat(BaseIndex address, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.fmovsReadrm(scr, dest); + releaseScratch(scr); + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); + m_assembler.fmovsReadrm(scr, dest); + releaseScratch(scr); + } + void loadDouble(ImplicitAddress address, FPRegisterID dest) { RegisterID scr = claimScratch(); @@ -935,6 +1101,21 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) releaseScratch(scr); } + void storeFloat(FPRegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.fmovsWriterm(src, scr); + + releaseScratch(scr); + } + void storeDouble(FPRegisterID src, ImplicitAddress address) { RegisterID scr = claimScratch(); @@ -946,11 +1127,44 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) releaseScratch(scr); } + void storeDouble(FPRegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.fmovsWriterm((FPRegisterID)(src + 1), scr); + m_assembler.addlImm8r(4, scr); + m_assembler.fmovsWriterm(src, scr); + + releaseScratch(scr); + } + + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + if (op1 == dest) + m_assembler.daddRegReg(op2, dest); + else { + m_assembler.dmovRegReg(op1, dest); + m_assembler.daddRegReg(op2, dest); + } + } + void addDouble(FPRegisterID src, FPRegisterID dest) { m_assembler.daddRegReg(src, dest); } + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + loadDouble(address.m_ptr, fscratch); + addDouble(fscratch, dest); + } + void addDouble(Address address, FPRegisterID dest) { loadDouble(address, fscratch); @@ -984,6 +1198,18 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) m_assembler.ddivRegReg(src, dest); } + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.fldsfpul(src); + m_assembler.dcnvsd(dst); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.dcnvds(src); + m_assembler.fstsfpul(dst); + } + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) { m_assembler.ldsrmfpul(src); @@ -1595,7 +1821,7 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) m_assembler.testlRegReg(reg, mask); - if (cond == NotEqual) + if (cond == NonZero) // NotEqual return branchFalse(); return branchTrue(); } @@ -1609,7 +1835,7 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) else testlImm(mask.m_value, reg); - if (cond == NotEqual) + if (cond == NonZero) // NotEqual return branchFalse(); return branchTrue(); } @@ -1623,7 +1849,7 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) else testImm(mask.m_value, address.offset, address.base); - if (cond == NotEqual) + if (cond == NonZero) // NotEqual return branchFalse(); return branchTrue(); } @@ -1644,7 +1870,7 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) releaseScratch(scr); - if (cond == NotEqual) + if (cond == NonZero) // NotEqual return branchFalse(); return branchTrue(); } @@ -1697,7 +1923,7 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) m_assembler.addlRegReg(src, dest); compare32(0, dest, Equal); - if (cond == NotEqual) + if (cond == NonZero) // NotEqual return branchFalse(); return branchTrue(); } @@ -1732,7 +1958,7 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) compare32(0, dest, Equal); - if (cond == NotEqual) + if (cond == NonZero) // NotEqual return branchFalse(); return branchTrue(); } @@ -1766,7 +1992,7 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) compare32(0, dest, static_cast(cond)); - if (cond == NotEqual) + if (cond == NonZero) // NotEqual return branchFalse(); return branchTrue(); } @@ -1801,7 +2027,7 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) sub32(src, dest); compare32(0, dest, static_cast(cond)); - if (cond == NotEqual) + if (cond == NonZero) // NotEqual return branchFalse(); return branchTrue(); } @@ -1842,7 +2068,7 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) or32(src, dest); compare32(0, dest, static_cast(cond)); - if (cond == NotEqual) + if (cond == NonZero) // NotEqual return branchFalse(); return branchTrue(); } @@ -1893,7 +2119,7 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) void urshift32(RegisterID src, TrustedImm32 shiftamount, RegisterID dest) { if (src != dest) - move(src, dest); + move(src, dest); urshift32(shiftamount, dest); } @@ -2018,6 +2244,29 @@ void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) return 0; } + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + return label.labelAtOffset(0); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) + { + SH4Assembler::revertJump(instructionStart.dataLocation(), reinterpret_cast(initialValue) & 0xffff); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + UNREACHABLE_FOR_PLATFORM(); + } + protected: SH4Assembler::Condition SH4Condition(RelationalCondition cond) { diff --git a/masm/assembler/MacroAssemblerX86.h b/masm/assembler/MacroAssemblerX86.h index 8fd31466d4..27a030edfd 100644 --- a/masm/assembler/MacroAssemblerX86.h +++ b/masm/assembler/MacroAssemblerX86.h @@ -253,6 +253,40 @@ public: return FunctionPtr(reinterpret_cast(reinterpret_cast(call.dataLocation()) + offset)); } + static bool canJumpReplacePatchableBranchPtrWithPatch() { return true; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + const int opcodeBytes = 1; + const int modRMBytes = 1; + const int immediateBytes = 4; + const int totalBytes = opcodeBytes + modRMBytes + immediateBytes; + ASSERT(totalBytes >= maxJumpReplacementSize()); + return label.labelAtOffset(-totalBytes); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) + { + const int opcodeBytes = 1; + const int modRMBytes = 1; + const int offsetBytes = 0; + const int immediateBytes = 4; + const int totalBytes = opcodeBytes + modRMBytes + offsetBytes + immediateBytes; + ASSERT(totalBytes >= maxJumpReplacementSize()); + return label.labelAtOffset(-totalBytes); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID reg, void* initialValue) + { + X86Assembler::revertJumpTo_cmpl_ir_force32(instructionStart.executableAddress(), reinterpret_cast(initialValue), reg); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address address, void* initialValue) + { + ASSERT(!address.offset); + X86Assembler::revertJumpTo_cmpl_im_force32(instructionStart.executableAddress(), reinterpret_cast(initialValue), 0, address.base); + } + private: friend class LinkBuffer; friend class RepatchBuffer; diff --git a/masm/assembler/MacroAssemblerX86Common.h b/masm/assembler/MacroAssemblerX86Common.h index 66db26acb0..53cb80c210 100644 --- a/masm/assembler/MacroAssemblerX86Common.h +++ b/masm/assembler/MacroAssemblerX86Common.h @@ -826,11 +826,15 @@ public: m_assembler.ucomisd_rr(right, left); if (cond == DoubleEqual) { + if (left == right) + return Jump(m_assembler.jnp()); Jump isUnordered(m_assembler.jp()); Jump result = Jump(m_assembler.je()); isUnordered.link(this); return result; } else if (cond == DoubleNotEqualOrUnordered) { + if (left == right) + return Jump(m_assembler.jp()); Jump isUnordered(m_assembler.jp()); Jump isEqual(m_assembler.je()); isUnordered.link(this); diff --git a/masm/assembler/MacroAssemblerX86_64.h b/masm/assembler/MacroAssemblerX86_64.h index ceacf6aa82..c711e6f8da 100644 --- a/masm/assembler/MacroAssemblerX86_64.h +++ b/masm/assembler/MacroAssemblerX86_64.h @@ -585,6 +585,33 @@ public: static RegisterID scratchRegisterForBlinding() { return scratchRegister; } + static bool canJumpReplacePatchableBranchPtrWithPatch() { return true; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + const int rexBytes = 1; + const int opcodeBytes = 1; + const int immediateBytes = 8; + const int totalBytes = rexBytes + opcodeBytes + immediateBytes; + ASSERT(totalBytes >= maxJumpReplacementSize()); + return label.labelAtOffset(-totalBytes); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) + { + return startOfBranchPtrWithPatchOnRegister(label); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + X86Assembler::revertJumpTo_movq_i64r(instructionStart.executableAddress(), reinterpret_cast(initialValue), scratchRegister); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) + { + X86Assembler::revertJumpTo_movq_i64r(instructionStart.executableAddress(), reinterpret_cast(initialValue), scratchRegister); + } + private: friend class LinkBuffer; friend class RepatchBuffer; diff --git a/masm/assembler/RepatchBuffer.h b/masm/assembler/RepatchBuffer.h index 531dda934b..dbb56f9ad5 100644 --- a/masm/assembler/RepatchBuffer.h +++ b/masm/assembler/RepatchBuffer.h @@ -141,6 +141,34 @@ public: replaceWithAddressComputation(label); } + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + return MacroAssembler::startOfBranchPtrWithPatchOnRegister(label); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) + { + return MacroAssembler::startOfPatchableBranchPtrWithPatchOnAddress(label); + } + + void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + MacroAssembler::replaceWithJump(instructionStart, destination); + } + + // This is a *bit* of a silly API, since we currently always also repatch the + // immediate after calling this. But I'm fine with that, since this just feels + // less yucky. + void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, MacroAssembler::RegisterID reg, void* value) + { + MacroAssembler::revertJumpReplacementToBranchPtrWithPatch(instructionStart, reg, value); + } + + void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, MacroAssembler::Address address, void* value) + { + MacroAssembler::revertJumpReplacementToPatchableBranchPtrWithPatch(instructionStart, address, value); + } + private: void* m_start; size_t m_size; diff --git a/masm/assembler/SH4Assembler.h b/masm/assembler/SH4Assembler.h index 373f469dcc..39f5585be1 100644 --- a/masm/assembler/SH4Assembler.h +++ b/masm/assembler/SH4Assembler.h @@ -166,6 +166,7 @@ enum { FMOVS_WRITE_RN_DEC_OPCODE = 0xf00b, FMOVS_WRITE_R0RN_OPCODE = 0xf007, FCNVDS_DRM_FPUL_OPCODE = 0xf0bd, + FCNVSD_FPUL_DRN_OPCODE = 0xf0ad, LDS_RM_FPUL_OPCODE = 0x405a, FLDS_FRM_FPUL_OPCODE = 0xf01d, STS_FPUL_RN_OPCODE = 0x005a, @@ -326,8 +327,9 @@ public: padForAlign32 = 0x00090009, }; - enum JumpType { JumpFar, - JumpNear + enum JumpType { + JumpFar, + JumpNear }; SH4Assembler() @@ -491,14 +493,14 @@ public: void sublRegReg(RegisterID src, RegisterID dst) { - uint16_t opc = getOpcodeGroup1(SUB_OPCODE, dst, src); - oneShortOp(opc); + uint16_t opc = getOpcodeGroup1(SUB_OPCODE, dst, src); + oneShortOp(opc); } void subvlRegReg(RegisterID src, RegisterID dst) { - uint16_t opc = getOpcodeGroup1(SUBV_OPCODE, dst, src); - oneShortOp(opc); + uint16_t opc = getOpcodeGroup1(SUBV_OPCODE, dst, src); + oneShortOp(opc); } void xorlRegReg(RegisterID src, RegisterID dst) @@ -803,7 +805,7 @@ public: oneShortOp(opc); } - void floatfpulfrn(RegisterID src) + void floatfpulfrn(FPRegisterID src) { uint16_t opc = getOpcodeGroup2(FLOAT_OPCODE, src); oneShortOp(opc, true, false); @@ -857,13 +859,13 @@ public: oneShortOp(opc, true, false); } - void fldsfpul(RegisterID src) + void fldsfpul(FPRegisterID src) { uint16_t opc = getOpcodeGroup2(FLDS_FRM_FPUL_OPCODE, src); oneShortOp(opc); } - void fstsfpul(RegisterID src) + void fstsfpul(FPRegisterID src) { uint16_t opc = getOpcodeGroup2(FSTS_FPUL_FRN_OPCODE, src); oneShortOp(opc); @@ -889,6 +891,12 @@ public: oneShortOp(opc); } + void dcnvsd(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup7(FCNVSD_FPUL_DRN_OPCODE, dst >> 1); + oneShortOp(opc); + } + void dcmppeq(FPRegisterID src, FPRegisterID dst) { uint16_t opc = getOpcodeGroup8(FCMPEQ_OPCODE, dst >> 1, src >> 1); @@ -1082,6 +1090,12 @@ public: oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset)); } + void movbRegMem(RegisterID src, RegisterID base) + { + uint16_t opc = getOpcodeGroup1(MOVB_WRITE_RN_OPCODE, base, src); + oneShortOp(opc); + } + void movbMemReg(int offset, RegisterID base, RegisterID dst) { ASSERT(dst == SH4Registers::r0); @@ -1253,7 +1267,7 @@ public: int sizeOfConstantPool() { - return m_buffer.sizeOfConstantPool(); + return m_buffer.sizeOfConstantPool(); } AssemblerLabel align(int alignment) @@ -1305,7 +1319,7 @@ public: *instructionPtr = instruction; printBlockInstr(instructionPtr - 2, from.m_offset, 3); return; - } + } /* MOV #imm, reg => LDR reg braf @reg braf @reg @@ -1448,6 +1462,20 @@ public: // Linking & patching + static void revertJump(void* instructionStart, SH4Word imm) + { + SH4Word *insn = reinterpret_cast(instructionStart); + SH4Word disp; + + ASSERT((insn[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); + + disp = insn[0] & 0x00ff; + insn += 2 + (disp << 1); // PC += 4 + (disp*4) + insn = (SH4Word *) ((unsigned) insn & (~3)); + insn[0] = imm; + cacheFlush(insn, sizeof(SH4Word)); + } + void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type = JumpFar) { ASSERT(to.isSet()); @@ -1592,7 +1620,7 @@ public: size_t codeSize() const { return m_buffer.codeSize(); } #ifdef SH4_ASSEMBLER_TRACING - static void printInstr(uint16_t opc, unsigned int size, bool isdoubleInst = true) + static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true) { if (!getenv("JavaScriptCoreDumpJIT")) return; @@ -1741,6 +1769,9 @@ public: case FCNVDS_DRM_FPUL_OPCODE: format = " FCNVDS FR%d, FPUL\n"; break; + case FCNVSD_FPUL_DRN_OPCODE: + format = " FCNVSD FPUL, FR%d\n"; + break; } if (format) { if (isdoubleInst) @@ -2070,19 +2101,19 @@ public: static void vprintfStdoutInstr(const char* format, va_list args) { if (getenv("JavaScriptCoreDumpJIT")) - WTF::dataLogV(format, args); + WTF::dataLogFV(format, args); } - static void printBlockInstr(uint16_t* first, unsigned int offset, int nbInstr) + static void printBlockInstr(uint16_t* first, unsigned offset, int nbInstr) { printfStdoutInstr(">> repatch instructions after link\n"); for (int i = 0; i <= nbInstr; i++) - printInstr(*(first + i), offset + i); + printInstr(*(first + i), offset + i); printfStdoutInstr(">> end repatch\n"); } #else - static void printInstr(uint16_t opc, unsigned int size, bool isdoubleInst = true) {}; - static void printBlockInstr(uint16_t* first, unsigned int offset, int nbInstr) {}; + static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true) { }; + static void printBlockInstr(uint16_t* first, unsigned offset, int nbInstr) { }; #endif static void replaceWithLoad(void* instructionStart) diff --git a/masm/assembler/X86Assembler.h b/masm/assembler/X86Assembler.h index ecb178e88b..25ff6f0a50 100644 --- a/masm/assembler/X86Assembler.h +++ b/masm/assembler/X86Assembler.h @@ -1475,6 +1475,12 @@ public: return m_formatter.immediateRel32(); } + AssemblerLabel jnp() + { + m_formatter.twoByteOp(jccRel32(ConditionNP)); + return m_formatter.immediateRel32(); + } + AssemblerLabel jp() { m_formatter.twoByteOp(jccRel32(ConditionP)); @@ -1877,6 +1883,61 @@ public: return 5; } +#if CPU(X86_64) + static void revertJumpTo_movq_i64r(void* instructionStart, int64_t imm, RegisterID dst) + { + const int rexBytes = 1; + const int opcodeBytes = 1; + ASSERT(rexBytes + opcodeBytes <= maxJumpReplacementSize()); + uint8_t* ptr = reinterpret_cast(instructionStart); + ptr[0] = PRE_REX | (1 << 3) | (dst >> 3); + ptr[1] = OP_MOV_EAXIv | (dst & 7); + + union { + uint64_t asWord; + uint8_t asBytes[8]; + } u; + u.asWord = imm; + for (unsigned i = rexBytes + opcodeBytes; i < static_cast(maxJumpReplacementSize()); ++i) + ptr[i] = u.asBytes[i - rexBytes - opcodeBytes]; + } +#endif + + static void revertJumpTo_cmpl_ir_force32(void* instructionStart, int32_t imm, RegisterID dst) + { + const int opcodeBytes = 1; + const int modRMBytes = 1; + ASSERT(opcodeBytes + modRMBytes <= maxJumpReplacementSize()); + uint8_t* ptr = reinterpret_cast(instructionStart); + ptr[0] = OP_GROUP1_EvIz; + ptr[1] = (X86InstructionFormatter::ModRmRegister << 6) | (GROUP1_OP_CMP << 3) | dst; + union { + uint32_t asWord; + uint8_t asBytes[4]; + } u; + u.asWord = imm; + for (unsigned i = opcodeBytes + modRMBytes; i < static_cast(maxJumpReplacementSize()); ++i) + ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes]; + } + + static void revertJumpTo_cmpl_im_force32(void* instructionStart, int32_t imm, int offset, RegisterID dst) + { + ASSERT_UNUSED(offset, !offset); + const int opcodeBytes = 1; + const int modRMBytes = 1; + ASSERT(opcodeBytes + modRMBytes <= maxJumpReplacementSize()); + uint8_t* ptr = reinterpret_cast(instructionStart); + ptr[0] = OP_GROUP1_EvIz; + ptr[1] = (X86InstructionFormatter::ModRmMemoryNoDisp << 6) | (GROUP1_OP_CMP << 3) | dst; + union { + uint32_t asWord; + uint8_t asBytes[4]; + } u; + u.asWord = imm; + for (unsigned i = opcodeBytes + modRMBytes; i < static_cast(maxJumpReplacementSize()); ++i) + ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes]; + } + static void replaceWithLoad(void* instructionStart) { uint8_t* ptr = reinterpret_cast(instructionStart); @@ -1976,6 +2037,13 @@ private: public: + enum ModRmMode { + ModRmMemoryNoDisp, + ModRmMemoryDisp8, + ModRmMemoryDisp32, + ModRmRegister, + }; + // Legacy prefix bytes: // // These are emmitted prior to the instruction. @@ -2314,6 +2382,9 @@ private: // Format a REX prefix byte. inline void emitRex(bool w, int r, int x, int b) { + ASSERT(r >= 0); + ASSERT(x >= 0); + ASSERT(b >= 0); m_buffer.putByteUnchecked(PRE_REX | ((int)w << 3) | ((r>>3)<<2) | ((x>>3)<<1) | (b>>3)); } @@ -2343,13 +2414,6 @@ private: inline void emitRexIfNeeded(int, int, int) {} #endif - enum ModRmMode { - ModRmMemoryNoDisp, - ModRmMemoryDisp8, - ModRmMemoryDisp32, - ModRmRegister, - }; - void putModRm(ModRmMode mode, int reg, RegisterID rm) { m_buffer.putByteUnchecked((mode << 6) | ((reg & 7) << 3) | (rm & 7)); diff --git a/masm/disassembler/Disassembler.cpp b/masm/disassembler/Disassembler.cpp new file mode 100644 index 0000000000..3fed2cdab8 --- /dev/null +++ b/masm/disassembler/Disassembler.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Disassembler.h" + +#include "MacroAssemblerCodeRef.h" +#include + +namespace JSC { + +void disassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const char* prefix, PrintStream& out) +{ + if (tryToDisassemble(codePtr, size, prefix, out)) + return; + + out.printf("%sdisassembly not available for range %p...%p\n", prefix, codePtr.executableAddress(), static_cast(codePtr.executableAddress()) + size); +} + +} // namespace JSC + diff --git a/masm/disassembler/Disassembler.h b/masm/disassembler/Disassembler.h index 7d7400ac81..a087a657b3 100644 --- a/masm/disassembler/Disassembler.h +++ b/masm/disassembler/Disassembler.h @@ -26,23 +26,26 @@ #ifndef Disassembler_h #define Disassembler_h -#include #include -#include +#include namespace JSC { class MacroAssemblerCodePtr; #if ENABLE(DISASSEMBLER) -bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, FILE* out); +bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, PrintStream&); #else -inline bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char*, FILE*) +inline bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char*, PrintStream&) { return false; } #endif +// Prints either the disassembly, or a line of text indicating that disassembly failed and +// the range of machine code addresses. +void disassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, PrintStream& out); + } // namespace JSC #endif // Disassembler_h diff --git a/masm/disassembler/UDis86Disassembler.cpp b/masm/disassembler/UDis86Disassembler.cpp index b6baed4a27..63c235b920 100644 --- a/masm/disassembler/UDis86Disassembler.cpp +++ b/masm/disassembler/UDis86Disassembler.cpp @@ -33,7 +33,7 @@ namespace JSC { -bool tryToDisassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const char* prefix, FILE* out) +bool tryToDisassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const char* prefix, PrintStream& out) { ud_t disassembler; ud_init(&disassembler); @@ -50,7 +50,7 @@ bool tryToDisassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const c while (ud_disassemble(&disassembler)) { char pcString[20]; snprintf(pcString, sizeof(pcString), "0x%lx", static_cast(currentPC)); - fprintf(out, "%s%16s: %s\n", prefix, pcString, ud_insn_asm(&disassembler)); + out.printf("%s%16s: %s\n", prefix, pcString, ud_insn_asm(&disassembler)); currentPC = disassembler.pc; } diff --git a/masm/disassembler/udis86/differences.txt b/masm/disassembler/udis86/differences.txt index 3ef51efcfc..dc225b6ffe 100644 --- a/masm/disassembler/udis86/differences.txt +++ b/masm/disassembler/udis86/differences.txt @@ -20,3 +20,5 @@ here: - Made the code in udis86_syn.h use vsnprintf() instead of vsprintf(). - Fixed udis86_syn-att.c's jump destination printing to work correctly in 64-bit mode. + +- Add --outputDir option to itab.py. diff --git a/masm/disassembler/udis86/itab.py b/masm/disassembler/udis86/itab.py index 27fa9b3f37..07e20a6e10 100644 --- a/masm/disassembler/udis86/itab.py +++ b/masm/disassembler/udis86/itab.py @@ -23,6 +23,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from optparse import OptionParser +import os import sys sys.path.append( '../scripts' ); @@ -194,10 +196,11 @@ class UdItabGenerator( ud_opcode.UdOpcodeTables ): MnemonicAliases = ( "invalid", "3dnow", "none", "db", "pause" ) - def __init__( self ): + def __init__( self, outputDir ): # first itab entry (0) is Invalid self.Itab.append( self.InvalidEntry ) self.MnemonicsTable.extend( self.MnemonicAliases ) + self.outputDir = outputDir def toGroupId( self, id ): return 0x8000 | id @@ -289,7 +292,7 @@ class UdItabGenerator( ud_opcode.UdOpcodeTables ): def genItabH( self ): - self.ItabH = open( "udis86_itab.h", "w" ) + self.ItabH = open( os.path.join(self.outputDir, "udis86_itab.h"), "w" ) # Generate Table Type Enumeration self.ItabH.write( "#ifndef UD_ITAB_H\n" ) @@ -328,7 +331,7 @@ class UdItabGenerator( ud_opcode.UdOpcodeTables ): def genItabC( self ): - self.ItabC = open( "udis86_itab.c", "w" ) + self.ItabC = open( os.path.join(self.outputDir, "udis86_itab.c"), "w" ) self.ItabC.write( "/* itab.c -- generated by itab.py, do no edit" ) self.ItabC.write( " */\n" ); self.ItabC.write( "#include \"udis86_decode.h\"\n\n" ); @@ -344,9 +347,12 @@ class UdItabGenerator( ud_opcode.UdOpcodeTables ): self.genItabH() def main(): - generator = UdItabGenerator() + parser = OptionParser() + parser.add_option("--outputDir", dest="outputDir", default="") + options, args = parser.parse_args() + generator = UdItabGenerator(os.path.normpath(options.outputDir)) optableXmlParser = ud_optable.UdOptableXmlParser() - optableXmlParser.parse( sys.argv[ 1 ], generator.addInsnDef ) + optableXmlParser.parse( args[ 0 ], generator.addInsnDef ) generator.genItab() diff --git a/masm/masm.pri b/masm/masm.pri index 642926d620..c648170937 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -9,6 +9,28 @@ SOURCES += $$PWD/assembler/LinkBuffer.cpp SOURCES += $$PWD/wtf/StackBounds.cpp HEADERS += $$PWD/wtf/StackBounds.h +SOURCES += $$PWD/wtf/PrintStream.cpp +HEADERS += $$PWD/wtf/PrintStream.h + +SOURCES += $$PWD/wtf/FilePrintStream.cpp +HEADERS += $$PWD/wtf/FilePrintStream.h + +HEADERS += $$PWD/wtf/RawPointer.h +HEADERS += $$PWD/wtf/PassOwnPtr.h + +win32: SOURCES += $$PWD/wtf/OSAllocatorWin.cpp +else: SOURCES += $$PWD/wtf/OSAllocatorPosix.cpp +HEADERS += $$PWD/wtf/OSAllocator.h + +SOURCES += $$PWD/wtf/PageAllocationAligned.cpp +HEADERS += $$PWD/wtf/PageAllocationAligned.h +HEADERS += $$PWD/wtf/PageAllocation.h + +SOURCES += $$PWD/wtf/PageBlock.cpp +HEADERS += $$PWD/wtf/PageBlock.h + +HEADERS += $$PWD/wtf/PageReservation.h + SOURCES += $$PWD/stubs/WTFStubs.cpp HEADERS += $$PWD/stubs/WTFStubs.h @@ -31,6 +53,7 @@ DEFINES += WTF_USE_UDIS86=1 INCLUDEPATH += $$PWD/disassembler INCLUDEPATH += $$PWD/disassembler/udis86 INCLUDEPATH += $$_OUT_PWD +SOURCES += $$PWD/disassembler/Disassembler.cpp SOURCES += $$PWD/disassembler/UDis86Disassembler.cpp SOURCES += $$PWD/disassembler/udis86/udis86.c SOURCES += $$PWD/disassembler/udis86/udis86_decode.c diff --git a/masm/stubs/WTFStubs.cpp b/masm/stubs/WTFStubs.cpp index 860bc5b26e..530804fe3e 100644 --- a/masm/stubs/WTFStubs.cpp +++ b/masm/stubs/WTFStubs.cpp @@ -42,8 +42,8 @@ #include #include #include - #include +#include namespace WTF { @@ -67,26 +67,29 @@ uint32_t cryptographicallyRandomNumber() return 0; } -static FILE* s_dataFile = stderr; +static FilePrintStream* s_dataFile; void setDataFile(FILE* f) { - s_dataFile = f; + delete s_dataFile; + s_dataFile = new FilePrintStream(f, FilePrintStream::Borrow); } -FILE* dataFile() +FilePrintStream& dataFile() { - return s_dataFile; + if (!s_dataFile) + s_dataFile = new FilePrintStream(stderr, FilePrintStream::Borrow); + return *s_dataFile; } -void dataLogV(const char* format, va_list args) +void dataLogFV(const char* format, va_list args) { char buffer[1024]; vsnprintf(buffer, sizeof(buffer), format, args); qDebug("%s", buffer); } -void dataLog(const char* format, ...) +void dataLogF(const char* format, ...) { char buffer[1024]; va_list args; @@ -96,7 +99,7 @@ void dataLog(const char* format, ...) qDebug("%s", buffer); } -void dataLogString(const char* str) +void dataLogFString(const char* str) { qDebug("%s", str); } diff --git a/masm/wtf/Assertions.h b/masm/wtf/Assertions.h index f347a21747..7e079ab187 100644 --- a/masm/wtf/Assertions.h +++ b/masm/wtf/Assertions.h @@ -42,6 +42,8 @@ http://msdn2.microsoft.com/en-us/library/ms177415(VS.80).aspx */ +#include + #include #if !COMPILER(MSVC) diff --git a/masm/wtf/Atomics.h b/masm/wtf/Atomics.h index 9f89df1409..750e1092ad 100644 --- a/masm/wtf/Atomics.h +++ b/masm/wtf/Atomics.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2010, 2012 Apple Inc. All rights reserved. * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) * * Redistribution and use in source and binary forms, with or without @@ -98,6 +98,9 @@ inline int atomicDecrement(int volatile* addend) { return InterlockedDecrement(r inline int atomicIncrement(int volatile* addend) { return OSAtomicIncrement32Barrier(const_cast(addend)); } inline int atomicDecrement(int volatile* addend) { return OSAtomicDecrement32Barrier(const_cast(addend)); } +inline int64_t atomicIncrement(int64_t volatile* addend) { return OSAtomicIncrement64Barrier(const_cast(addend)); } +inline int64_t atomicDecrement(int64_t volatile* addend) { return OSAtomicDecrement64Barrier(const_cast(addend)); } + #elif OS(QNX) #define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 @@ -142,8 +145,8 @@ inline bool weakCompareAndSwap(unsigned* location, unsigned expected, unsigned n #endif { #if ENABLE(COMPARE_AND_SWAP) - bool result; #if CPU(X86) || CPU(X86_64) + unsigned char result; asm volatile( "lock; cmpxchgl %3, %2\n\t" "sete %1" @@ -153,6 +156,7 @@ inline bool weakCompareAndSwap(unsigned* location, unsigned expected, unsigned n ); #elif CPU(ARM_THUMB2) unsigned tmp; + unsigned result; asm volatile( "movw %1, #1\n\t" "ldrex %2, %0\n\t" @@ -173,7 +177,7 @@ inline bool weakCompareAndSwap(unsigned* location, unsigned expected, unsigned n UNUSED_PARAM(expected); UNUSED_PARAM(newValue); CRASH(); - return 0; + return false; #endif } diff --git a/masm/wtf/CheckedArithmetic.h b/masm/wtf/CheckedArithmetic.h index ef8a03695e..f5d3b75500 100644 --- a/masm/wtf/CheckedArithmetic.h +++ b/masm/wtf/CheckedArithmetic.h @@ -154,7 +154,7 @@ template struct BoundsChecker::value> struct BoundsCheckElider; +template ::value || (sizeof(Target) > sizeof(Source)) > struct BoundsCheckElider; template struct BoundsCheckElider { static bool inBounds(Source) { return true; } }; @@ -270,7 +270,7 @@ template struct ArithmeticOper if (lhs && (std::numeric_limits::max() / lhs) < rhs) return false; } else { - if (lhs == std::numeric_limits::min() || rhs == std::numeric_limits::min()) + if (static_cast(lhs) == std::numeric_limits::min() || static_cast(rhs) == std::numeric_limits::min()) return false; if ((std::numeric_limits::max() / -lhs) < -rhs) return false; diff --git a/masm/wtf/DataLog.h b/masm/wtf/DataLog.h index 11e1d3e55d..0bd8efe727 100644 --- a/masm/wtf/DataLog.h +++ b/masm/wtf/DataLog.h @@ -28,21 +28,101 @@ #include #include +#include #include #include namespace WTF { -WTF_EXPORT_PRIVATE FILE* dataFile(); +WTF_EXPORT_PRIVATE FilePrintStream& dataFile(); -WTF_EXPORT_PRIVATE void dataLogV(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(1, 0); -WTF_EXPORT_PRIVATE void dataLog(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); -WTF_EXPORT_PRIVATE void dataLogString(const char*); +WTF_EXPORT_PRIVATE void dataLogFV(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(1, 0); +WTF_EXPORT_PRIVATE void dataLogF(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); +WTF_EXPORT_PRIVATE void dataLogFString(const char*); + +template +void dataLog(const T& value) +{ + dataFile().print(value); +} + +template +void dataLog(const T1& value1, const T2& value2) +{ + dataFile().print(value1, value2); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3) +{ + dataFile().print(value1, value2, value3); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4) +{ + dataFile().print(value1, value2, value3, value4); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5) +{ + dataFile().print(value1, value2, value3, value4, value5); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6) +{ + dataFile().print(value1, value2, value3, value4, value5, value6); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11, value12); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12, const T13& value13) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11, value12, value13); +} } // namespace WTF using WTF::dataLog; -using WTF::dataLogString; +using WTF::dataLogF; +using WTF::dataLogFString; #endif // DataLog_h diff --git a/masm/wtf/FilePrintStream.cpp b/masm/wtf/FilePrintStream.cpp new file mode 100644 index 0000000000..b5ab25e0bf --- /dev/null +++ b/masm/wtf/FilePrintStream.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FilePrintStream.h" + +namespace WTF { + +FilePrintStream::FilePrintStream(FILE* file, AdoptionMode adoptionMode) + : m_file(file) + , m_adoptionMode(adoptionMode) +{ +} + +FilePrintStream::~FilePrintStream() +{ + if (m_adoptionMode == Borrow) + return; + fclose(m_file); +} + +PassOwnPtr FilePrintStream::open(const char* filename, const char* mode) +{ + FILE* file = fopen(filename, mode); + if (!file) + return PassOwnPtr(); + + return adoptPtr(new FilePrintStream(file)); +} + +void FilePrintStream::vprintf(const char* format, va_list argList) +{ + vfprintf(m_file, format, argList); +} + +void FilePrintStream::flush() +{ + fflush(m_file); +} + +} // namespace WTF + diff --git a/masm/wtf/FilePrintStream.h b/masm/wtf/FilePrintStream.h new file mode 100644 index 0000000000..bdeab4c479 --- /dev/null +++ b/masm/wtf/FilePrintStream.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FilePrintStream_h +#define FilePrintStream_h + +#include +#include +#include + +namespace WTF { + +class FilePrintStream : public PrintStream { +public: + enum AdoptionMode { + Adopt, + Borrow + }; + + FilePrintStream(FILE*, AdoptionMode = Adopt); + virtual ~FilePrintStream(); + + static PassOwnPtr open(const char* filename, const char* mode); + + FILE* file() { return m_file; } + + void vprintf(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(2, 0); + void flush(); + +private: + FILE* m_file; + AdoptionMode m_adoptionMode; +}; + +} // namespace WTF + +using WTF::FilePrintStream; + +#endif // FilePrintStream_h + diff --git a/masm/wtf/OSAllocator.h b/masm/wtf/OSAllocator.h new file mode 100644 index 0000000000..a12a467497 --- /dev/null +++ b/masm/wtf/OSAllocator.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OSAllocator_h +#define OSAllocator_h + +#include +#include +#include + +namespace WTF { + +class OSAllocator { +public: + enum Usage { + UnknownUsage = -1, + FastMallocPages = VM_TAG_FOR_TCMALLOC_MEMORY, + JSGCHeapPages = VM_TAG_FOR_COLLECTOR_MEMORY, + JSVMStackPages = VM_TAG_FOR_REGISTERFILE_MEMORY, + JSJITCodePages = VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY, + }; + + // These methods are symmetric; reserveUncommitted allocates VM in an uncommitted state, + // releaseDecommitted should be called on a region of VM allocated by a single reservation, + // the memory must all currently be in a decommitted state. + static void* reserveUncommitted(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool includesGuardPages = false); + WTF_EXPORT_PRIVATE static void releaseDecommitted(void*, size_t); + + // These methods are symmetric; they commit or decommit a region of VM (uncommitted VM should + // never be accessed, since the OS may not have attached physical memory for these regions). + // Clients should only call commit on uncommitted regions and decommit on committed regions. + static void commit(void*, size_t, bool writable, bool executable); + static void decommit(void*, size_t); + + // These methods are symmetric; reserveAndCommit allocates VM in an committed state, + // decommitAndRelease should be called on a region of VM allocated by a single reservation, + // the memory must all currently be in a committed state. + WTF_EXPORT_PRIVATE static void* reserveAndCommit(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool includesGuardPages = false); + static void decommitAndRelease(void* base, size_t size); + + // These methods are akin to reserveAndCommit/decommitAndRelease, above - however rather than + // committing/decommitting the entire region additional parameters allow a subregion to be + // specified. + static void* reserveAndCommit(size_t reserveSize, size_t commitSize, Usage = UnknownUsage, bool writable = true, bool executable = false); + static void decommitAndRelease(void* releaseBase, size_t releaseSize, void* decommitBase, size_t decommitSize); + + // Reallocate an existing, committed allocation. + // The prior allocation must be fully comitted, and the new size will also be fully committed. + // This interface is provided since it may be possible to optimize this operation on some platforms. + template + static T* reallocateCommitted(T*, size_t oldSize, size_t newSize, Usage = UnknownUsage, bool writable = true, bool executable = false); +}; + +inline void* OSAllocator::reserveAndCommit(size_t reserveSize, size_t commitSize, Usage usage, bool writable, bool executable) +{ + void* base = reserveUncommitted(reserveSize, usage, writable, executable); + commit(base, commitSize, writable, executable); + return base; +} + +inline void OSAllocator::decommitAndRelease(void* releaseBase, size_t releaseSize, void* decommitBase, size_t decommitSize) +{ + ASSERT(decommitBase >= releaseBase && (static_cast(decommitBase) + decommitSize) <= (static_cast(releaseBase) + releaseSize)); +#if OS(WINCE) + // On most platforms we can actually skip this final decommit; releasing the VM will + // implicitly decommit any physical memory in the region. This is not true on WINCE. + decommit(decommitBase, decommitSize); +#else + UNUSED_PARAM(decommitBase); + UNUSED_PARAM(decommitSize); +#endif + releaseDecommitted(releaseBase, releaseSize); +} + +inline void OSAllocator::decommitAndRelease(void* base, size_t size) +{ + decommitAndRelease(base, size, base, size); +} + +template +inline T* OSAllocator::reallocateCommitted(T* oldBase, size_t oldSize, size_t newSize, Usage usage, bool writable, bool executable) +{ + void* newBase = reserveAndCommit(newSize, usage, writable, executable); + memcpy(newBase, oldBase, std::min(oldSize, newSize)); + decommitAndRelease(oldBase, oldSize); + return static_cast(newBase); +} + +} // namespace WTF + +using WTF::OSAllocator; + +#endif // OSAllocator_h diff --git a/masm/wtf/OSAllocatorPosix.cpp b/masm/wtf/OSAllocatorPosix.cpp new file mode 100644 index 0000000000..b5b903b8a3 --- /dev/null +++ b/masm/wtf/OSAllocatorPosix.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "OSAllocator.h" + +#include "PageAllocation.h" +#include +#include +#include +#include + +namespace WTF { + +void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) +{ +#if OS(QNX) + // Reserve memory with PROT_NONE and MAP_LAZY so it isn't committed now. + void* result = mmap(0, bytes, PROT_NONE, MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); + if (result == MAP_FAILED) + CRASH(); +#elif OS(LINUX) + void* result = mmap(0, bytes, PROT_NONE, MAP_NORESERVE | MAP_PRIVATE | MAP_ANON, -1, 0); + if (result == MAP_FAILED) + CRASH(); + madvise(result, bytes, MADV_DONTNEED); +#else + void* result = reserveAndCommit(bytes, usage, writable, executable, includesGuardPages); +#if HAVE(MADV_FREE_REUSE) + // To support the "reserve then commit" model, we have to initially decommit. + while (madvise(result, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { } +#endif + +#endif // OS(QNX) + + return result; +} + +void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) +{ + // All POSIX reservations start out logically committed. + int protection = PROT_READ; + if (writable) + protection |= PROT_WRITE; + if (executable) + protection |= PROT_EXEC; + + int flags = MAP_PRIVATE | MAP_ANON; +#if PLATFORM(IOS) + if (executable) + flags |= MAP_JIT; +#endif + +#if OS(DARWIN) + int fd = usage; +#else + UNUSED_PARAM(usage); + int fd = -1; +#endif + + void* result = 0; +#if (OS(DARWIN) && CPU(X86_64)) + if (executable) { + ASSERT(includesGuardPages); + // Cook up an address to allocate at, using the following recipe: + // 17 bits of zero, stay in userspace kids. + // 26 bits of randomness for ASLR. + // 21 bits of zero, at least stay aligned within one level of the pagetables. + // + // But! - as a temporary workaround for some plugin problems (rdar://problem/6812854), + // for now instead of 2^26 bits of ASLR lets stick with 25 bits of randomization plus + // 2^24, which should put up somewhere in the middle of userspace (in the address range + // 0x200000000000 .. 0x5fffffffffff). + intptr_t randomLocation = 0; + randomLocation = arc4random() & ((1 << 25) - 1); + randomLocation += (1 << 24); + randomLocation <<= 21; + result = reinterpret_cast(randomLocation); + } +#endif + + result = mmap(result, bytes, protection, flags, fd, 0); + if (result == MAP_FAILED) { +#if ENABLE(LLINT) + if (executable) + result = 0; + else +#endif + CRASH(); + } + if (result && includesGuardPages) { + // We use mmap to remap the guardpages rather than using mprotect as + // mprotect results in multiple references to the code region. This + // breaks the madvise based mechanism we use to return physical memory + // to the OS. + mmap(result, pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0); + mmap(static_cast(result) + bytes - pageSize(), pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0); + } + return result; +} + +void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable) +{ +#if OS(QNX) + int protection = PROT_READ; + if (writable) + protection |= PROT_WRITE; + if (executable) + protection |= PROT_EXEC; + if (MAP_FAILED == mmap(address, bytes, protection, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0)) + CRASH(); +#elif OS(LINUX) + int protection = PROT_READ; + if (writable) + protection |= PROT_WRITE; + if (executable) + protection |= PROT_EXEC; + if (mprotect(address, bytes, protection)) + CRASH(); + madvise(address, bytes, MADV_WILLNEED); +#elif HAVE(MADV_FREE_REUSE) + UNUSED_PARAM(writable); + UNUSED_PARAM(executable); + while (madvise(address, bytes, MADV_FREE_REUSE) == -1 && errno == EAGAIN) { } +#else + // Non-MADV_FREE_REUSE reservations automatically commit on demand. + UNUSED_PARAM(address); + UNUSED_PARAM(bytes); + UNUSED_PARAM(writable); + UNUSED_PARAM(executable); +#endif +} + +void OSAllocator::decommit(void* address, size_t bytes) +{ +#if OS(QNX) + // Use PROT_NONE and MAP_LAZY to decommit the pages. + mmap(address, bytes, PROT_NONE, MAP_FIXED | MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); +#elif OS(LINUX) + madvise(address, bytes, MADV_DONTNEED); + if (mprotect(address, bytes, PROT_NONE)) + CRASH(); +#elif HAVE(MADV_FREE_REUSE) + while (madvise(address, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { } +#elif HAVE(MADV_FREE) + while (madvise(address, bytes, MADV_FREE) == -1 && errno == EAGAIN) { } +#elif HAVE(MADV_DONTNEED) + while (madvise(address, bytes, MADV_DONTNEED) == -1 && errno == EAGAIN) { } +#else + UNUSED_PARAM(address); + UNUSED_PARAM(bytes); +#endif +} + +void OSAllocator::releaseDecommitted(void* address, size_t bytes) +{ + int result = munmap(address, bytes); + if (result == -1) + CRASH(); +} + +} // namespace WTF diff --git a/masm/wtf/OSAllocatorWin.cpp b/masm/wtf/OSAllocatorWin.cpp new file mode 100644 index 0000000000..7f5d9b8904 --- /dev/null +++ b/masm/wtf/OSAllocatorWin.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "OSAllocator.h" + +#include "windows.h" +#include + +namespace WTF { + +static inline DWORD protection(bool writable, bool executable) +{ + return executable ? + (writable ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ) : + (writable ? PAGE_READWRITE : PAGE_READONLY); +} + +void* OSAllocator::reserveUncommitted(size_t bytes, Usage, bool writable, bool executable, bool) +{ + void* result = VirtualAlloc(0, bytes, MEM_RESERVE, protection(writable, executable)); + if (!result) + CRASH(); + return result; +} + +void* OSAllocator::reserveAndCommit(size_t bytes, Usage, bool writable, bool executable, bool) +{ + void* result = VirtualAlloc(0, bytes, MEM_RESERVE | MEM_COMMIT, protection(writable, executable)); + if (!result) + CRASH(); + return result; +} + +void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable) +{ + void* result = VirtualAlloc(address, bytes, MEM_COMMIT, protection(writable, executable)); + if (!result) + CRASH(); +} + +void OSAllocator::decommit(void* address, size_t bytes) +{ + bool result = VirtualFree(address, bytes, MEM_DECOMMIT); + if (!result) + CRASH(); +} + +void OSAllocator::releaseDecommitted(void* address, size_t bytes) +{ + // According to http://msdn.microsoft.com/en-us/library/aa366892(VS.85).aspx, + // dwSize must be 0 if dwFreeType is MEM_RELEASE. + bool result = VirtualFree(address, 0, MEM_RELEASE); + if (!result) + CRASH(); +} + +} // namespace WTF diff --git a/masm/wtf/OwnPtrCommon.h b/masm/wtf/OwnPtrCommon.h index 43a3cb7de9..c564001844 100644 --- a/masm/wtf/OwnPtrCommon.h +++ b/masm/wtf/OwnPtrCommon.h @@ -40,11 +40,15 @@ typedef struct HRGN__* HRGN; #if PLATFORM(EFL) typedef struct _Ecore_Evas Ecore_Evas; +typedef struct _Ecore_IMF_Context Ecore_IMF_Context; typedef struct _Ecore_Pipe Ecore_Pipe; typedef struct _Ecore_Timer Ecore_Timer; typedef struct _Eina_Hash Eina_Hash; typedef struct _Eina_Module Eina_Module; typedef struct _Evas_Object Evas_Object; +#if USE(ACCELERATED_COMPOSITING) +typedef struct _Evas_GL Evas_GL; +#endif #endif namespace WTF { @@ -68,11 +72,15 @@ namespace WTF { #if PLATFORM(EFL) WTF_EXPORT_PRIVATE void deleteOwnedPtr(Ecore_Evas*); + WTF_EXPORT_PRIVATE void deleteOwnedPtr(Ecore_IMF_Context*); WTF_EXPORT_PRIVATE void deleteOwnedPtr(Ecore_Pipe*); WTF_EXPORT_PRIVATE void deleteOwnedPtr(Ecore_Timer*); WTF_EXPORT_PRIVATE void deleteOwnedPtr(Eina_Hash*); WTF_EXPORT_PRIVATE void deleteOwnedPtr(Eina_Module*); WTF_EXPORT_PRIVATE void deleteOwnedPtr(Evas_Object*); +#if USE(ACCELERATED_COMPOSITING) + WTF_EXPORT_PRIVATE void deleteOwnedPtr(Evas_GL*); +#endif #endif } // namespace WTF diff --git a/masm/wtf/PageAllocation.h b/masm/wtf/PageAllocation.h new file mode 100644 index 0000000000..18d31880c0 --- /dev/null +++ b/masm/wtf/PageAllocation.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PageAllocation_h +#define PageAllocation_h + +#include +#include +#include +#include +#include +#include + +#if OS(DARWIN) +#include +#include +#endif + +#if OS(WINDOWS) +#include +#include +#endif + +#if HAVE(ERRNO_H) +#include +#endif + +#if HAVE(MMAP) +#include +#include +#endif + +namespace WTF { + +/* + PageAllocation + + The PageAllocation class provides a cross-platform memory allocation interface + with similar capabilities to posix mmap/munmap. Memory is allocated by calling + PageAllocation::allocate, and deallocated by calling deallocate on the + PageAllocation object. The PageAllocation holds the allocation's base pointer + and size. + + The allocate method is passed the size required (which must be a multiple of + the system page size, which can be accessed using PageAllocation::pageSize). + Callers may also optinally provide a flag indicating the usage (for use by + system memory usage tracking tools, where implemented), and boolean values + specifying the required protection (defaulting to writable, non-executable). +*/ + +class PageAllocation : private PageBlock { +public: + PageAllocation() + { + } + + using PageBlock::size; + using PageBlock::base; + +#ifndef __clang__ + using PageBlock::operator bool; +#else + // FIXME: This is a workaround for , wherein Clang incorrectly emits an access + // control warning when a client tries to use operator bool exposed above via "using PageBlock::operator bool". + operator bool() const { return PageBlock::operator bool(); } +#endif + + static PageAllocation allocate(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) + { + ASSERT(isPageAligned(size)); + return PageAllocation(OSAllocator::reserveAndCommit(size, usage, writable, executable), size); + } + + void deallocate() + { + // Clear base & size before calling release; if this is *inside* allocation + // then we won't be able to clear then after deallocating the memory. + PageAllocation tmp; + std::swap(tmp, *this); + + ASSERT(tmp); + ASSERT(!*this); + + OSAllocator::decommitAndRelease(tmp.base(), tmp.size()); + } + +private: + PageAllocation(void* base, size_t size) + : PageBlock(base, size, false) + { + } +}; + +} // namespace WTF + +using WTF::PageAllocation; + +#endif // PageAllocation_h diff --git a/masm/wtf/PageAllocationAligned.cpp b/masm/wtf/PageAllocationAligned.cpp new file mode 100644 index 0000000000..6f54710d0b --- /dev/null +++ b/masm/wtf/PageAllocationAligned.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PageAllocationAligned.h" + +namespace WTF { + +PageAllocationAligned PageAllocationAligned::allocate(size_t size, size_t alignment, OSAllocator::Usage usage, bool writable, bool executable) +{ + ASSERT(isPageAligned(size)); + ASSERT(isPageAligned(alignment)); + ASSERT(isPowerOfTwo(alignment)); + ASSERT(size >= alignment); + size_t alignmentMask = alignment - 1; + +#if OS(DARWIN) + int flags = VM_FLAGS_ANYWHERE; + if (usage != OSAllocator::UnknownUsage) + flags |= usage; + int protection = PROT_READ; + if (writable) + protection |= PROT_WRITE; + if (executable) + protection |= PROT_EXEC; + + vm_address_t address = 0; + vm_map(current_task(), &address, size, alignmentMask, flags, MEMORY_OBJECT_NULL, 0, FALSE, protection, PROT_READ | PROT_WRITE | PROT_EXEC, VM_INHERIT_DEFAULT); + return PageAllocationAligned(reinterpret_cast(address), size); +#else + size_t alignmentDelta = alignment - pageSize(); + + // Resererve with suffcient additional VM to correctly align. + size_t reservationSize = size + alignmentDelta; + void* reservationBase = OSAllocator::reserveUncommitted(reservationSize, usage, writable, executable); + + // Select an aligned region within the reservation and commit. + void* alignedBase = reinterpret_cast(reservationBase) & alignmentMask + ? reinterpret_cast((reinterpret_cast(reservationBase) & ~alignmentMask) + alignment) + : reservationBase; + OSAllocator::commit(alignedBase, size, writable, executable); + + return PageAllocationAligned(alignedBase, size, reservationBase, reservationSize); +#endif +} + +void PageAllocationAligned::deallocate() +{ + // Clear base & size before calling release; if this is *inside* allocation + // then we won't be able to clear then after deallocating the memory. + PageAllocationAligned tmp; + std::swap(tmp, *this); + + ASSERT(tmp); + ASSERT(!*this); + +#if OS(DARWIN) + vm_deallocate(current_task(), reinterpret_cast(tmp.base()), tmp.size()); +#else + ASSERT(tmp.m_reservation.contains(tmp.base(), tmp.size())); + OSAllocator::decommitAndRelease(tmp.m_reservation.base(), tmp.m_reservation.size(), tmp.base(), tmp.size()); +#endif +} + +} // namespace WTF diff --git a/masm/wtf/PageAllocationAligned.h b/masm/wtf/PageAllocationAligned.h new file mode 100644 index 0000000000..c018dabd8e --- /dev/null +++ b/masm/wtf/PageAllocationAligned.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PageAllocationAligned_h +#define PageAllocationAligned_h + +#include +#include + +namespace WTF { + +class PageAllocationAligned : private PageBlock { +public: + PageAllocationAligned() + { + } + + using PageBlock::operator bool; + using PageBlock::size; + using PageBlock::base; + + static PageAllocationAligned allocate(size_t size, size_t alignment, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false); + + void deallocate(); + +private: +#if OS(DARWIN) + PageAllocationAligned(void* base, size_t size) + : PageBlock(base, size, false) + { + } +#else + PageAllocationAligned(void* base, size_t size, void* reservationBase, size_t reservationSize) + : PageBlock(base, size, false) + , m_reservation(reservationBase, reservationSize, false) + { + } + + PageBlock m_reservation; +#endif +}; + + +} // namespace WTF + +using WTF::PageAllocationAligned; + +#endif // PageAllocationAligned_h diff --git a/masm/wtf/PageBlock.cpp b/masm/wtf/PageBlock.cpp new file mode 100644 index 0000000000..8bbd7eb600 --- /dev/null +++ b/masm/wtf/PageBlock.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PageBlock.h" + +#if OS(UNIX) +#include +#endif + +#if OS(WINDOWS) +#include +#include +#endif + +namespace WTF { + +static size_t s_pageSize; +static size_t s_pageMask; + +#if OS(UNIX) + +inline size_t systemPageSize() +{ + return getpagesize(); +} + +#elif OS(WINDOWS) + +inline size_t systemPageSize() +{ + static size_t size = 0; + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + size = system_info.dwPageSize; + return size; +} + +#endif + +size_t pageSize() +{ + if (!s_pageSize) + s_pageSize = systemPageSize(); + ASSERT(isPowerOfTwo(s_pageSize)); + return s_pageSize; +} + +size_t pageMask() +{ + if (!s_pageMask) + s_pageMask = ~(pageSize() - 1); + return s_pageMask; +} + +} // namespace WTF diff --git a/masm/wtf/PageBlock.h b/masm/wtf/PageBlock.h new file mode 100644 index 0000000000..56e5570178 --- /dev/null +++ b/masm/wtf/PageBlock.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PageBlock_h +#define PageBlock_h + +namespace WTF { + +WTF_EXPORT_PRIVATE size_t pageSize(); +WTF_EXPORT_PRIVATE size_t pageMask(); +inline bool isPageAligned(void* address) { return !(reinterpret_cast(address) & (pageSize() - 1)); } +inline bool isPageAligned(size_t size) { return !(size & (pageSize() - 1)); } +inline bool isPowerOfTwo(size_t size) { return !(size & (size - 1)); } + +class PageBlock { +public: + PageBlock(); + PageBlock(const PageBlock&); + PageBlock(void*, size_t, bool hasGuardPages); + + void* base() const { return m_base; } + size_t size() const { return m_size; } + + operator bool() const { return !!m_realBase; } + + bool contains(void* containedBase, size_t containedSize) + { + return containedBase >= m_base + && (static_cast(containedBase) + containedSize) <= (static_cast(m_base) + m_size); + } + +private: + void* m_realBase; + void* m_base; + size_t m_size; +}; + +inline PageBlock::PageBlock() + : m_realBase(0) + , m_base(0) + , m_size(0) +{ +} + +inline PageBlock::PageBlock(const PageBlock& other) + : m_realBase(other.m_realBase) + , m_base(other.m_base) + , m_size(other.m_size) +{ +} + +inline PageBlock::PageBlock(void* base, size_t size, bool hasGuardPages) + : m_realBase(base) + , m_base(static_cast(base) + ((base && hasGuardPages) ? pageSize() : 0)) + , m_size(size) +{ +} + +} // namespace WTF + +using WTF::pageSize; +using WTF::isPageAligned; +using WTF::isPageAligned; +using WTF::isPowerOfTwo; + +#endif // PageBlock_h diff --git a/masm/wtf/PageReservation.h b/masm/wtf/PageReservation.h new file mode 100644 index 0000000000..77783ebcc4 --- /dev/null +++ b/masm/wtf/PageReservation.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PageReservation_h +#define PageReservation_h + +#include + +namespace WTF { + +/* + PageReservation + + Like PageAllocation, the PageReservation class provides a cross-platform memory + allocation interface, but with a set of capabilities more similar to that of + VirtualAlloc than posix mmap. PageReservation can be used to allocate virtual + memory without committing physical memory pages using PageReservation::reserve. + Following a call to reserve all memory in the region is in a decommited state, + in which the memory should not be used (accessing the memory may cause a fault). + + Before using memory it must be committed by calling commit, which is passed start + and size values (both of which require system page size granularity). One the + committed memory is no longer needed 'decommit' may be called to return the + memory to its devommitted state. Commit should only be called on memory that is + currently decommitted, and decommit should only be called on memory regions that + are currently committed. All memory should be decommited before the reservation + is deallocated. Values in memory may not be retained accross a pair of calls if + the region of memory is decommitted and then committed again. + + Memory protection should not be changed on decommitted memory, and if protection + is changed on memory while it is committed it should be returned to the orignal + protection before decommit is called. +*/ + +class PageReservation : private PageBlock { +public: + PageReservation() + : m_committed(0) + , m_writable(false) + , m_executable(false) + { + } + + using PageBlock::base; + using PageBlock::size; + +#ifndef __clang__ + using PageBlock::operator bool; +#else + // FIXME: This is a workaround for , wherein Clang incorrectly emits an access + // control warning when a client tries to use operator bool exposed above via "using PageBlock::operator bool". + operator bool() const { return PageBlock::operator bool(); } +#endif + + void commit(void* start, size_t size) + { + ASSERT(*this); + ASSERT(isPageAligned(start)); + ASSERT(isPageAligned(size)); + ASSERT(contains(start, size)); + + m_committed += size; + OSAllocator::commit(start, size, m_writable, m_executable); + } + + void decommit(void* start, size_t size) + { + ASSERT(*this); + ASSERT(isPageAligned(start)); + ASSERT(isPageAligned(size)); + ASSERT(contains(start, size)); + + m_committed -= size; + OSAllocator::decommit(start, size); + } + + size_t committed() + { + return m_committed; + } + + static PageReservation reserve(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) + { + ASSERT(isPageAligned(size)); + return PageReservation(OSAllocator::reserveUncommitted(size, usage, writable, executable), size, writable, executable, false); + } + + static PageReservation reserveWithGuardPages(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) + { + ASSERT(isPageAligned(size)); + return PageReservation(OSAllocator::reserveUncommitted(size + pageSize() * 2, usage, writable, executable, true), size, writable, executable, true); + } + + void deallocate() + { + ASSERT(!m_committed); + + // Clear base & size before calling release; if this is *inside* allocation + // then we won't be able to clear then after deallocating the memory. + PageReservation tmp; + std::swap(tmp, *this); + + ASSERT(tmp); + ASSERT(!*this); + + OSAllocator::releaseDecommitted(tmp.base(), tmp.size()); + } + +private: + PageReservation(void* base, size_t size, bool writable, bool executable, bool hasGuardPages) + : PageBlock(base, size, hasGuardPages) + , m_committed(0) + , m_writable(writable) + , m_executable(executable) + { + } + + size_t m_committed; + bool m_writable; + bool m_executable; +}; + +} + +using WTF::PageReservation; + +#endif // PageReservation_h diff --git a/masm/wtf/PassOwnPtr.h b/masm/wtf/PassOwnPtr.h new file mode 100644 index 0000000000..5ebf83d65a --- /dev/null +++ b/masm/wtf/PassOwnPtr.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_PassOwnPtr_h +#define WTF_PassOwnPtr_h + +#include +#include +#include +#include + +namespace WTF { + + // Unlike most of our smart pointers, PassOwnPtr can take either the pointer type or the pointed-to type. + + template class OwnPtr; + template class PassOwnPtr; + template PassOwnPtr adoptPtr(T*); + + template class PassOwnPtr { + public: + typedef typename RemovePointer::Type ValueType; + typedef ValueType* PtrType; + + PassOwnPtr() : m_ptr(0) { } + PassOwnPtr(std::nullptr_t) : m_ptr(0) { } + + // It somewhat breaks the type system to allow transfer of ownership out of + // a const PassOwnPtr. However, it makes it much easier to work with PassOwnPtr + // temporaries, and we don't have a need to use real const PassOwnPtrs anyway. + PassOwnPtr(const PassOwnPtr& o) : m_ptr(o.leakPtr()) { } + template PassOwnPtr(const PassOwnPtr& o) : m_ptr(o.leakPtr()) { } + + ~PassOwnPtr() { deleteOwnedPtr(m_ptr); } + + PtrType get() const { return m_ptr; } + + PtrType leakPtr() const WARN_UNUSED_RETURN; + + ValueType& operator*() const { ASSERT(m_ptr); return *m_ptr; } + PtrType operator->() const { ASSERT(m_ptr); return m_ptr; } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef PtrType PassOwnPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_ptr ? &PassOwnPtr::m_ptr : 0; } + + PassOwnPtr& operator=(const PassOwnPtr&) { COMPILE_ASSERT(!sizeof(T*), PassOwnPtr_should_never_be_assigned_to); return *this; } + + template friend PassOwnPtr adoptPtr(U*); + + private: + explicit PassOwnPtr(PtrType ptr) : m_ptr(ptr) { } + + // We should never have two OwnPtrs for the same underlying object (otherwise we'll get + // double-destruction), so these equality operators should never be needed. + template bool operator==(const PassOwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + template bool operator!=(const PassOwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + template bool operator==(const OwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + template bool operator!=(const OwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } + + mutable PtrType m_ptr; + }; + + template inline typename PassOwnPtr::PtrType PassOwnPtr::leakPtr() const + { + PtrType ptr = m_ptr; + m_ptr = 0; + return ptr; + } + + template inline bool operator==(const PassOwnPtr& a, const PassOwnPtr& b) + { + return a.get() == b.get(); + } + + template inline bool operator==(const PassOwnPtr& a, const OwnPtr& b) + { + return a.get() == b.get(); + } + + template inline bool operator==(const OwnPtr& a, const PassOwnPtr& b) + { + return a.get() == b.get(); + } + + template inline bool operator==(const PassOwnPtr& a, U* b) + { + return a.get() == b; + } + + template inline bool operator==(T* a, const PassOwnPtr& b) + { + return a == b.get(); + } + + template inline bool operator!=(const PassOwnPtr& a, const PassOwnPtr& b) + { + return a.get() != b.get(); + } + + template inline bool operator!=(const PassOwnPtr& a, const OwnPtr& b) + { + return a.get() != b.get(); + } + + template inline bool operator!=(const OwnPtr& a, const PassOwnPtr& b) + { + return a.get() != b.get(); + } + + template inline bool operator!=(const PassOwnPtr& a, U* b) + { + return a.get() != b; + } + + template inline bool operator!=(T* a, const PassOwnPtr& b) + { + return a != b.get(); + } + + template inline PassOwnPtr adoptPtr(T* ptr) + { + return PassOwnPtr(ptr); + } + + template inline PassOwnPtr static_pointer_cast(const PassOwnPtr& p) + { + return adoptPtr(static_cast(p.leakPtr())); + } + + template inline PassOwnPtr const_pointer_cast(const PassOwnPtr& p) + { + return adoptPtr(const_cast(p.leakPtr())); + } + + template inline T* getPtr(const PassOwnPtr& p) + { + return p.get(); + } + +} // namespace WTF + +using WTF::PassOwnPtr; +using WTF::adoptPtr; +using WTF::const_pointer_cast; +using WTF::static_pointer_cast; + +#endif // WTF_PassOwnPtr_h diff --git a/masm/wtf/Platform.h b/masm/wtf/Platform.h index 3c263c51d9..f2fd3b0ad5 100644 --- a/masm/wtf/Platform.h +++ b/masm/wtf/Platform.h @@ -302,6 +302,14 @@ #define HAVE_ARM_NEON_INTRINSICS 1 #endif +#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) +#define WTF_CPU_ARM_VFP 1 +#endif + +#if defined(__ARM_ARCH_7S__) +#define WTF_CPU_APPLE_ARMV7S 1 +#endif + #endif /* ARM */ #if CPU(ARM) || CPU(MIPS) || CPU(SH4) || CPU(SPARC) @@ -424,6 +432,7 @@ /* PLATFORM(CHROMIUM) */ /* PLATFORM(QT) */ /* PLATFORM(WX) */ +/* PLATFORM(EFL) */ /* PLATFORM(GTK) */ /* PLATFORM(BLACKBERRY) */ /* PLATFORM(MAC) */ @@ -434,6 +443,8 @@ #define WTF_PLATFORM_QT 1 #elif defined(BUILDING_WX__) #define WTF_PLATFORM_WX 1 +#elif defined(BUILDING_EFL__) +#define WTF_PLATFORM_EFL 1 #elif defined(BUILDING_GTK__) #define WTF_PLATFORM_GTK 1 #elif defined(BUILDING_BLACKBERRY__) @@ -545,7 +556,6 @@ #endif #define WTF_USE_CF 1 #define WTF_USE_PTHREADS 1 -#define HAVE_PTHREAD_RWLOCK 1 #define HAVE_READLINE 1 #define HAVE_RUNLOOP_TIMER 1 #define ENABLE_FULLSCREEN_API 1 @@ -565,11 +575,21 @@ #if PLATFORM(CHROMIUM) && OS(DARWIN) #define WTF_USE_CF 1 #define WTF_USE_PTHREADS 1 -#define HAVE_PTHREAD_RWLOCK 1 - #define WTF_USE_WK_SCROLLBAR_PAINTER 1 #endif +#if PLATFORM(CHROMIUM) +#if OS(DARWIN) +/* We can't override the global operator new and delete on OS(DARWIN) because + * some object are allocated by WebKit and deallocated by the embedder. */ +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#else /* !OS(DARWIN) */ +/* On non-OS(DARWIN), the "system malloc" is actually TCMalloc anyway, so there's + * no need to use WebKit's copy of TCMalloc. */ +#define USE_SYSTEM_MALLOC 1 +#endif /* OS(DARWIN) */ +#endif /* PLATFORM(CHROMIUM) */ + #if PLATFORM(IOS) #define DONT_FINALIZE_ON_MAIN_THREAD 1 #endif @@ -592,11 +612,10 @@ #define ENABLE_ORIENTATION_EVENTS 1 #define ENABLE_REPAINT_THROTTLING 1 #define ENABLE_WEB_ARCHIVE 1 -#define HAVE_NETWORK_CFDATA_ARRAY_CALLBACK 1 -#define HAVE_PTHREAD_RWLOCK 1 #define HAVE_READLINE 1 #define WTF_USE_CF 1 #define WTF_USE_CFNETWORK 1 +#define WTF_USE_NETWORK_CFDATA_ARRAY_CALLBACK 1 #define WTF_USE_PTHREADS 1 #if PLATFORM(IOS_SIMULATOR) @@ -622,7 +641,6 @@ #if USE(CFNETWORK) || PLATFORM(MAC) || PLATFORM(IOS) #define WTF_USE_CFURLCACHE 1 -#define WTF_USE_CFURLSTORAGESESSIONS 1 #endif #if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(QT) @@ -650,11 +668,10 @@ #if OS(UNIX) && (PLATFORM(GTK) || PLATFORM(QT)) #define WTF_USE_PTHREADS 1 -#define HAVE_PTHREAD_RWLOCK 1 #endif #if !defined(HAVE_ACCESSIBILITY) -#if PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && !OS(ANDROID)) +#if PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && !OS(ANDROID)) || PLATFORM(EFL) #define HAVE_ACCESSIBILITY 1 #endif #endif /* !defined(HAVE_ACCESSIBILITY) */ @@ -818,14 +835,6 @@ #endif #endif -#if !defined(ENABLE_GESTURE_ANIMATION) -#if PLATFORM(QT) || !ENABLE(SMOOTH_SCROLLING) -#define ENABLE_GESTURE_ANIMATION 0 -#else -#define ENABLE_GESTURE_ANIMATION 1 -#endif -#endif - #if !defined(ENABLE_SATURATED_LAYOUT_ARITHMETIC) #define ENABLE_SATURATED_LAYOUT_ARITHMETIC 0 #endif @@ -834,6 +843,10 @@ #error "ENABLE_SATURATED_LAYOUT_ARITHMETIC requires ENABLE_SUBPIXEL_LAYOUT" #endif +#if ENABLE(INPUT_TYPE_DATE) || ENABLE(INPUT_TYPE_DATETIME) || ENABLE(INPUT_TYPE_DATETIMELOCAL) || ENABLE(INPUT_TYPE_MONTH) || ENABLE(INPUT_TYPE_TIME) || ENABLE(INPUT_TYPE_WEEK) +#define ENABLE_DATE_AND_TIME_INPUT_TYPES 1 +#endif + #define ENABLE_DEBUG_WITH_BREAKPOINT 0 #define ENABLE_SAMPLING_COUNTERS 0 #define ENABLE_SAMPLING_FLAGS 0 @@ -884,14 +897,15 @@ && (CPU(X86) || CPU(X86_64) || CPU(ARM) || CPU(MIPS)) \ && (OS(DARWIN) || !COMPILER(GCC) || GCC_VERSION_AT_LEAST(4, 1, 0)) \ && !OS(WINCE) \ - && !OS(QNX) + && !(OS(QNX) && !PLATFORM(QT)) /* We use JIT in QNX Qt */ #define ENABLE_JIT 1 #endif /* If possible, try to enable a disassembler. This is optional. We proceed in two steps: first we try to find some disassembler that we can use, and then we decide if the high-level disassembler API can be enabled. */ -#if !defined(WTF_USE_UDIS86) && ENABLE(JIT) && PLATFORM(MAC) && (CPU(X86) || CPU(X86_64)) +#if !defined(WTF_USE_UDIS86) && ENABLE(JIT) && (PLATFORM(MAC) || (PLATFORM(QT) && OS(LINUX))) \ + && (CPU(X86) || CPU(X86_64)) #define WTF_USE_UDIS86 1 #endif @@ -912,7 +926,7 @@ #if !defined(ENABLE_LLINT) \ && ENABLE(JIT) \ && (OS(DARWIN) || OS(LINUX)) \ - && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(GTK) || (PLATFORM(QT) && OS(LINUX))) \ + && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(QT)) \ && (CPU(X86) || CPU(X86_64) || CPU(ARM_THUMB2)) #define ENABLE_LLINT 1 #endif @@ -922,8 +936,8 @@ #if (CPU(X86) || CPU(X86_64)) && (PLATFORM(MAC) || OS(LINUX)) #define ENABLE_DFG_JIT 1 #endif -/* Enable the DFG JIT on ARMv7. Only tested on iOS. */ -#if CPU(ARM_THUMB2) && (PLATFORM(IOS) || PLATFORM(BLACKBERRY)) +/* Enable the DFG JIT on ARMv7. Only tested on iOS and Qt Linux. */ +#if CPU(ARM_THUMB2) && (PLATFORM(IOS) || PLATFORM(BLACKBERRY) || PLATFORM(QT)) #define ENABLE_DFG_JIT 1 #endif /* Enable the DFG JIT on ARM. */ @@ -993,7 +1007,7 @@ #define ENABLE_REGEXP_TRACING 0 /* Yet Another Regex Runtime - turned on by default for JIT enabled ports. */ -#if !defined(ENABLE_YARR_JIT) && (ENABLE(JIT) || ENABLE(LLINT_C_LOOP)) && !PLATFORM(CHROMIUM) +#if !defined(ENABLE_YARR_JIT) && (ENABLE(JIT) || ENABLE(LLINT_C_LOOP)) && !PLATFORM(CHROMIUM) && !(OS(QNX) && PLATFORM(QT)) #define ENABLE_YARR_JIT 1 /* Setting this flag compares JIT results with interpreter results. */ @@ -1048,17 +1062,17 @@ #define ENABLE_CSS_IMAGE_SET 1 #endif +#if ENABLE(WEBGL) && !defined(WTF_USE_3D_GRAPHICS) +#define WTF_USE_3D_GRAPHICS 1 +#endif /* Qt always uses Texture Mapper */ #if PLATFORM(QT) #define WTF_USE_TEXTURE_MAPPER 1 -#if USE(3D_GRAPHICS) -#define WTF_USE_TEXTURE_MAPPER_GL 1 -#endif #endif -#if ENABLE(WEBGL) && !defined(WTF_USE_3D_GRAPHICS) -#define WTF_USE_3D_GRAPHICS 1 +#if USE(TEXTURE_MAPPER) && USE(3D_GRAPHICS) && !defined(WTF_USE_TEXTURE_MAPPER_GL) +#define WTF_USE_TEXTURE_MAPPER_GL 1 #endif /* Compositing on the UI-process in WebKit2 */ @@ -1151,6 +1165,10 @@ #define WTF_USE_COREMEDIA 1 #endif +#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 +#define HAVE_AVFOUNDATION_TEXT_TRACK_SUPPORT 1 +#endif + #if PLATFORM(MAC) || PLATFORM(GTK) || PLATFORM(EFL) || (PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(WIN_CAIRO)) || PLATFORM(BLACKBERRY) #define WTF_USE_REQUEST_ANIMATION_FRAME_TIMER 1 #endif diff --git a/masm/wtf/PrintStream.cpp b/masm/wtf/PrintStream.cpp new file mode 100644 index 0000000000..7dd4060971 --- /dev/null +++ b/masm/wtf/PrintStream.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PrintStream.h" + +#include + +namespace WTF { + +PrintStream::PrintStream() { } +PrintStream::~PrintStream() { } // Force the vtable to be in this module + +void PrintStream::printf(const char* format, ...) +{ + va_list argList; + va_start(argList, format); + vprintf(format, argList); + va_end(argList); +} + +void PrintStream::flush() +{ +} + +void printInternal(PrintStream& out, const char* string) +{ + out.printf("%s", string); +} + +void printInternal(PrintStream& out, bool value) +{ + if (value) + out.print("true"); + else + out.print("false"); +} + +void printInternal(PrintStream& out, int value) +{ + out.printf("%d", value); +} + +void printInternal(PrintStream& out, unsigned value) +{ + out.printf("%u", value); +} + +void printInternal(PrintStream& out, long value) +{ + out.printf("%ld", value); +} + +void printInternal(PrintStream& out, unsigned long value) +{ + out.printf("%lu", value); +} + +void printInternal(PrintStream& out, long long value) +{ + out.printf("%lld", value); +} + +void printInternal(PrintStream& out, unsigned long long value) +{ + out.printf("%llu", value); +} + +void printInternal(PrintStream& out, float value) +{ + out.print(static_cast(value)); +} + +void printInternal(PrintStream& out, double value) +{ + out.printf("%lf", value); +} + +void printInternal(PrintStream& out, RawPointer value) +{ + out.printf("%p", value.value()); +} + +void dumpCharacter(PrintStream& out, char value) +{ + out.printf("%c", value); +} + +} // namespace WTF + diff --git a/masm/wtf/PrintStream.h b/masm/wtf/PrintStream.h new file mode 100644 index 0000000000..4134dcf182 --- /dev/null +++ b/masm/wtf/PrintStream.h @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PrintStream_h +#define PrintStream_h + +#include +#include +#include +#include +#include +#include + +namespace WTF { + +class CString; +class String; + +class PrintStream { + WTF_MAKE_FAST_ALLOCATED; WTF_MAKE_NONCOPYABLE(PrintStream); +public: + PrintStream(); + virtual ~PrintStream(); + + void printf(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); + virtual void vprintf(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(2, 0) = 0; + + // Typically a no-op for many subclasses of PrintStream, this is a hint that + // the implementation should flush its buffers if it had not done so already. + virtual void flush(); + + template + void print(const T& value) + { + printInternal(*this, value); + } + + template + void print(const T1& value1, const T2& value2) + { + print(value1); + print(value2); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3) + { + print(value1); + print(value2); + print(value3); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4) + { + print(value1); + print(value2); + print(value3); + print(value4); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + print(value11); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + print(value11); + print(value12); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12, const T13& value13) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + print(value11); + print(value12); + print(value13); + } +}; + +void printInternal(PrintStream&, const char*); +void printInternal(PrintStream&, const CString&); +void printInternal(PrintStream&, const String&); +inline void printInternal(PrintStream& out, char* value) { printInternal(out, static_cast(value)); } +inline void printInternal(PrintStream& out, CString& value) { printInternal(out, static_cast(value)); } +inline void printInternal(PrintStream& out, String& value) { printInternal(out, static_cast(value)); } +void printInternal(PrintStream&, bool); +void printInternal(PrintStream&, int); +void printInternal(PrintStream&, unsigned); +void printInternal(PrintStream&, long); +void printInternal(PrintStream&, unsigned long); +void printInternal(PrintStream&, long long); +void printInternal(PrintStream&, unsigned long long); +void printInternal(PrintStream&, float); +void printInternal(PrintStream&, double); +void printInternal(PrintStream&, RawPointer); + +template +void printInternal(PrintStream& out, const T& value) +{ + value.dump(out); +} + +#define MAKE_PRINT_ADAPTOR(Name, Type, function) \ + class Name { \ + public: \ + Name(const Type& value) \ + : m_value(value) \ + { \ + } \ + void dump(PrintStream& out) const \ + { \ + function(out, m_value); \ + } \ + private: \ + Type m_value; \ + } + +#define MAKE_PRINT_METHOD_ADAPTOR(Name, Type, method) \ + class Name { \ + public: \ + Name(const Type& value) \ + : m_value(value) \ + { \ + } \ + void dump(PrintStream& out) const \ + { \ + m_value.method(out); \ + } \ + private: \ + Type m_value; \ + } + +// Use an adaptor-based dumper for characters to avoid situations where +// you've "compressed" an integer to a character and it ends up printing +// as ASCII when you wanted it to print as a number. +void dumpCharacter(PrintStream&, char); +MAKE_PRINT_ADAPTOR(CharacterDump, char, dumpCharacter); + +template +class PointerDump { +public: + PointerDump(const T* ptr) + : m_ptr(ptr) + { + } + + void dump(PrintStream& out) const + { + if (m_ptr) + printInternal(out, *m_ptr); + else + out.print("(null)"); + } +private: + const T* m_ptr; +}; + +template +PointerDump pointerDump(const T* ptr) { return PointerDump(ptr); } + +} // namespace WTF + +using WTF::CharacterDump; +using WTF::PointerDump; +using WTF::PrintStream; +using WTF::pointerDump; + +#endif // PrintStream_h + diff --git a/masm/wtf/RawPointer.h b/masm/wtf/RawPointer.h new file mode 100644 index 0000000000..6dc7292fb4 --- /dev/null +++ b/masm/wtf/RawPointer.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RawPointer_h +#define RawPointer_h + +namespace WTF { + +class RawPointer { +public: + RawPointer() + : m_value(0) + { + } + + explicit RawPointer(void* value) + : m_value(value) + { + } + + explicit RawPointer(const void* value) + : m_value(value) + { + } + + const void* value() const { return m_value; } + +private: + const void* m_value; +}; + +} // namespace WTF + +using WTF::RawPointer; + +#endif // RawPointer_h diff --git a/masm/wtf/StdLibExtras.h b/masm/wtf/StdLibExtras.h index e66df2c310..f5e9f78df1 100644 --- a/masm/wtf/StdLibExtras.h +++ b/masm/wtf/StdLibExtras.h @@ -178,125 +178,85 @@ template inline size_t roundUpToMultipleOf(size_t x) enum BinarySearchMode { KeyMustBePresentInArray, - KeyMustNotBePresentInArray + KeyMightNotBePresentInArray, + ReturnAdjacentElementIfKeyIsNotPresent }; -// Binary search algorithm, calls extractKey on pre-sorted elements in array, -// compares result with key (KeyTypes should be comparable with '--', '<', '>'). -template -inline ArrayElementType* binarySearch(ArrayElementType* array, size_t size, KeyType key, BinarySearchMode mode = KeyMustBePresentInArray) +template +inline ArrayElementType* binarySearchImpl(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) { - // The array must contain at least one element (pre-condition, array does contain key). - // If the array contains only one element, no need to do the comparison. + size_t offset = 0; while (size > 1) { - // Pick an element to check, half way through the array, and read the value. int pos = (size - 1) >> 1; - KeyType val = extractKey(&array[pos]); - - // If the key matches, success! + KeyType val = extractKey(&array[offset + pos]); + if (val == key) - return &array[pos]; + return &array[offset + pos]; // The item we are looking for is smaller than the item being check; reduce the value of 'size', // chopping off the right hand half of the array. - else if (key < val) + if (key < val) size = pos; // Discard all values in the left hand half of the array, up to and including the item at pos. else { size -= (pos + 1); - array += (pos + 1); + offset += (pos + 1); } - // In case of BinarySearchMode = KeyMustBePresentInArray 'size' should never reach zero. - if (mode == KeyMustBePresentInArray) - ASSERT(size); + ASSERT(mode != KeyMustBePresentInArray || size); } + + if (mode == KeyMightNotBePresentInArray && !size) + return 0; + + ArrayElementType* result = &array[offset]; + + if (mode == KeyMightNotBePresentInArray && key != extractKey(result)) + return 0; - // In case of BinarySearchMode = KeyMustBePresentInArray if we reach this point - // we've chopped down to one element, no need to check it matches if (mode == KeyMustBePresentInArray) { ASSERT(size == 1); - ASSERT(key == extractKey(&array[0])); + ASSERT(key == extractKey(result)); } - return &array[0]; + return result; } -// Modified binary search algorithm that uses a functor. Note that this is strictly -// more powerful than the above, but results in somewhat less template specialization. -// Hence, depending on inlining heuristics, it might be slower. -template -inline ArrayElementType* binarySearchWithFunctor(ArrayElementType* array, size_t size, KeyType key, BinarySearchMode mode = KeyMustBePresentInArray, const ExtractKey& extractKey = ExtractKey()) +// If the element is not found, crash if asserts are enabled, and behave like approximateBinarySearch in release builds. +template +inline ArrayElementType* binarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) { - // The array must contain at least one element (pre-condition, array does contain key). - // If the array contains only one element, no need to do the comparison. - while (size > 1) { - // Pick an element to check, half way through the array, and read the value. - int pos = (size - 1) >> 1; - KeyType val = extractKey(&array[pos]); - - // If the key matches, success! - if (val == key) - return &array[pos]; - // The item we are looking for is smaller than the item being check; reduce the value of 'size', - // chopping off the right hand half of the array. - else if (key < val) - size = pos; - // Discard all values in the left hand half of the array, up to and including the item at pos. - else { - size -= (pos + 1); - array += (pos + 1); - } - - // In case of BinarySearchMode = KeyMustBePresentInArray 'size' should never reach zero. - if (mode == KeyMustBePresentInArray) - ASSERT(size); - } - - // In case of BinarySearchMode = KeyMustBePresentInArray if we reach this point - // we've chopped down to one element, no need to check it matches - if (mode == KeyMustBePresentInArray) { - ASSERT(size == 1); - ASSERT(key == extractKey(&array[0])); - } + return binarySearchImpl(array, size, key, extractKey); +} - return &array[0]; +// Return zero if the element is not found. +template +inline ArrayElementType* tryBinarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(array, size, key, extractKey); } -// Modified binarySearch() algorithm designed for array-like classes that support -// operator[] but not operator+=. One example of a class that qualifies is -// SegmentedVector. -template -inline ArrayElementType* genericBinarySearch(ArrayType& array, size_t size, KeyType key) +// Return the element that is either to the left, or the right, of where the element would have been found. +template +inline ArrayElementType* approximateBinarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) { - // The array must contain at least one element (pre-condition, array does conatin key). - // If the array only contains one element, no need to do the comparison. - size_t offset = 0; - while (size > 1) { - // Pick an element to check, half way through the array, and read the value. - int pos = (size - 1) >> 1; - KeyType val = extractKey(&array[offset + pos]); - - // If the key matches, success! - if (val == key) - return &array[offset + pos]; - // The item we are looking for is smaller than the item being check; reduce the value of 'size', - // chopping off the right hand half of the array. - if (key < val) - size = pos; - // Discard all values in the left hand half of the array, up to and including the item at pos. - else { - size -= (pos + 1); - offset += (pos + 1); - } + return binarySearchImpl(array, size, key, extractKey); +} - // 'size' should never reach zero. - ASSERT(size); - } - - // If we reach this point we've chopped down to one element, no need to check it matches - ASSERT(size == 1); - ASSERT(key == extractKey(&array[offset])); - return &array[offset]; +// Variants of the above that use const. +template +inline ArrayElementType* binarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(const_cast(array), size, key, extractKey); +} +template +inline ArrayElementType* tryBinarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(const_cast(array), size, key, extractKey); +} +template +inline ArrayElementType* approximateBinarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(const_cast(array), size, key, extractKey); } } // namespace WTF @@ -314,6 +274,8 @@ using WTF::MB; using WTF::isPointerAligned; using WTF::is8ByteAligned; using WTF::binarySearch; +using WTF::tryBinarySearch; +using WTF::approximateBinarySearch; using WTF::bitwise_cast; using WTF::safeCast; diff --git a/masm/wtf/ThreadingPrimitives.h b/masm/wtf/ThreadingPrimitives.h index ab7dc36ff7..abfc36def6 100644 --- a/masm/wtf/ThreadingPrimitives.h +++ b/masm/wtf/ThreadingPrimitives.h @@ -50,18 +50,12 @@ namespace WTF { #if USE(PTHREADS) typedef pthread_mutex_t PlatformMutex; -#if HAVE(PTHREAD_RWLOCK) -typedef pthread_rwlock_t PlatformReadWriteLock; -#else -typedef void* PlatformReadWriteLock; -#endif typedef pthread_cond_t PlatformCondition; #elif OS(WINDOWS) struct PlatformMutex { CRITICAL_SECTION m_internalMutex; size_t m_recursionCount; }; -typedef void* PlatformReadWriteLock; // FIXME: Implement. struct PlatformCondition { size_t m_waitersGone; size_t m_waitersBlocked; @@ -75,7 +69,6 @@ struct PlatformCondition { }; #else typedef void* PlatformMutex; -typedef void* PlatformReadWriteLock; typedef void* PlatformCondition; #endif @@ -114,24 +107,6 @@ private: bool m_locked; }; -class ReadWriteLock { - WTF_MAKE_NONCOPYABLE(ReadWriteLock); -public: - ReadWriteLock(); - ~ReadWriteLock(); - - void readLock(); - bool tryReadLock(); - - void writeLock(); - bool tryWriteLock(); - - void unlock(); - -private: - PlatformReadWriteLock m_readWriteLock; -}; - class ThreadCondition { WTF_MAKE_NONCOPYABLE(ThreadCondition); public: diff --git a/masm/wtf/VMTags.h b/masm/wtf/VMTags.h new file mode 100644 index 0000000000..117bc3721e --- /dev/null +++ b/masm/wtf/VMTags.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VMTags_h +#define VMTags_h + +// On Mac OS X, the VM subsystem allows tagging memory requested from mmap and vm_map +// in order to aid tools that inspect system memory use. +#if OS(DARWIN) + +#include + +#if defined(VM_MEMORY_TCMALLOC) +#define VM_TAG_FOR_TCMALLOC_MEMORY VM_MAKE_TAG(VM_MEMORY_TCMALLOC) +#else +#define VM_TAG_FOR_TCMALLOC_MEMORY VM_MAKE_TAG(53) +#endif // defined(VM_MEMORY_TCMALLOC) + +#if defined(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) +#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) +#else +#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY VM_MAKE_TAG(64) +#endif // defined(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) + +#if defined(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) +#define VM_TAG_FOR_REGISTERFILE_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) +#else +#define VM_TAG_FOR_REGISTERFILE_MEMORY VM_MAKE_TAG(65) +#endif // defined(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) + +#if defined(VM_MEMORY_JAVASCRIPT_CORE) +#define VM_TAG_FOR_COLLECTOR_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_CORE) +#else +#define VM_TAG_FOR_COLLECTOR_MEMORY VM_MAKE_TAG(63) +#endif // defined(VM_MEMORY_JAVASCRIPT_CORE) + +#if defined(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) +#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY VM_MAKE_TAG(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) +#else +#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY VM_MAKE_TAG(69) +#endif // defined(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) + +#else // OS(DARWIN) + +#define VM_TAG_FOR_TCMALLOC_MEMORY -1 +#define VM_TAG_FOR_COLLECTOR_MEMORY -1 +#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY -1 +#define VM_TAG_FOR_REGISTERFILE_MEMORY -1 +#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY -1 + +#endif // OS(DARWIN) + +#endif // VMTags_h diff --git a/masm/wtf/Vector.h b/masm/wtf/Vector.h index 6cd43115b1..d2dedefea8 100644 --- a/masm/wtf/Vector.h +++ b/masm/wtf/Vector.h @@ -34,9 +34,6 @@ namespace WTF { - using std::min; - using std::max; - template struct VectorDestructor; @@ -278,14 +275,7 @@ namespace WTF { bool shouldReallocateBuffer(size_t newCapacity) const { -#if PLATFORM(BLACKBERRY) - // Tested on BlackBerry. return VectorTraits::canMoveWithMemcpy && m_capacity && newCapacity; -#else - // FIXME: Return true on the platforms where realloc() gives better performance. - UNUSED_PARAM(newCapacity); - return false; -#endif } void reallocateBuffer(size_t newCapacity) @@ -312,7 +302,6 @@ namespace WTF { T* buffer() { return m_buffer; } const T* buffer() const { return m_buffer; } - T** bufferSlot() { return &m_buffer; } size_t capacity() const { return m_capacity; } T* releaseBuffer() @@ -385,7 +374,6 @@ namespace WTF { using Base::deallocateBuffer; using Base::buffer; - using Base::bufferSlot; using Base::capacity; using Base::releaseBuffer; @@ -486,7 +474,6 @@ namespace WTF { } using Base::buffer; - using Base::bufferSlot; using Base::capacity; T* releaseBuffer() @@ -514,8 +501,6 @@ namespace WTF { typedef VectorBuffer Buffer; typedef VectorTypeOperations TypeOperations; - class VectorReverseProxy; - public: typedef T ValueType; @@ -588,9 +573,6 @@ namespace WTF { const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } - VectorReverseProxy& reversed() { return static_cast(*this); } - const VectorReverseProxy& reversed() const { return static_cast(*this); } - T& first() { return at(0); } const T& first() const { return at(0); } T& last() { return at(size() - 1); } @@ -668,26 +650,6 @@ namespace WTF { template U* expandCapacity(size_t newMinCapacity, U*); template void appendSlowCase(const U&); - class VectorReverseProxy : private Vector { - public: - typedef typename Vector::reverse_iterator iterator; - typedef typename Vector::const_reverse_iterator const_iterator; - - iterator begin() { return Vector::rbegin(); } - iterator end() { return Vector::rend(); } - const_iterator begin() const { return Vector::rbegin(); } - const_iterator end() const { return Vector::rend(); } - - private: - friend class Vector; - - // These are intentionally not implemented. - VectorReverseProxy(); - VectorReverseProxy(const VectorReverseProxy&); - VectorReverseProxy& operator=(const VectorReverseProxy&); - ~VectorReverseProxy(); - }; - size_t m_size; Buffer m_buffer; }; @@ -848,7 +810,7 @@ namespace WTF { template void Vector::expandCapacity(size_t newMinCapacity) { - reserveCapacity(max(newMinCapacity, max(static_cast(16), capacity() + capacity() / 4 + 1))); + reserveCapacity(std::max(newMinCapacity, std::max(static_cast(16), capacity() + capacity() / 4 + 1))); } template @@ -866,7 +828,7 @@ namespace WTF { template bool Vector::tryExpandCapacity(size_t newMinCapacity) { - return tryReserveCapacity(max(newMinCapacity, max(static_cast(16), capacity() + capacity() / 4 + 1))); + return tryReserveCapacity(std::max(newMinCapacity, std::max(static_cast(16), capacity() + capacity() / 4 + 1))); } template -- cgit v1.2.3 From f2e52c5bf26cfcedd0901040dfe1a6c86c5aa146 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 18 Dec 2012 15:03:26 +0100 Subject: Changed GC heap chunks to be allocated from separate page allocations Use page allocations instead of the regular libc heap for the chunks of the memory manager. This will allow for easier return of the memory to the operation system in the future. Change-Id: Ie370e54042251b17335e94b497933f06ab62ecc3 Reviewed-by: Lars Knoll --- qv4mm.cpp | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/qv4mm.cpp b/qv4mm.cpp index 4cdb211a92..25dfaa4cd2 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -32,6 +32,8 @@ #include "qv4ecmaobjects_p.h" #include "qv4mm.h" #include "StackBounds.h" +#include "PageAllocation.h" +#include "StdLibExtras.h" #include #include @@ -43,6 +45,7 @@ #include using namespace QQmlJS::VM; +using namespace WTF; static const std::size_t CHUNK_SIZE = 65536; @@ -59,7 +62,7 @@ struct MemoryManager::Data MMObject *fallbackObject; MMObject *smallItems[16]; // 16 - 256 bytes QMap largeItems; - QLinkedList > heapChunks; + QLinkedList heapChunks; // statistics: #ifdef DETAILED_MM_STATS @@ -80,8 +83,8 @@ struct MemoryManager::Data ~Data() { - for (QLinkedList >::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) - free(i->first); + for (QLinkedList::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) + i->deallocate(); } }; @@ -133,14 +136,14 @@ MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) // try to free up space, otherwise allocate if (!m_d->aggressiveGC || runGC() < size) { std::size_t allocSize = std::max(size, CHUNK_SIZE); - char *ptr = 0; - posix_memalign(reinterpret_cast(&ptr), 16, allocSize); - m_d->heapChunks.append(qMakePair(ptr, allocSize)); - m_d->fallbackObject = reinterpret_cast(ptr); + allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); + PageAllocation allocation = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); + m_d->heapChunks.append(allocation); + m_d->fallbackObject = reinterpret_cast(allocation.base()); m_d->fallbackObject->info.inUse = 0; m_d->fallbackObject->info.next = 0; m_d->fallbackObject->info.markBit = 0; - m_d->fallbackObject->info.size = allocSize; + m_d->fallbackObject->info.size = allocation.size(); } return alloc(size - alignedSizeOfMMInfo); } @@ -247,8 +250,8 @@ std::size_t MemoryManager::sweep(std::size_t &largestFreedBlock) { std::size_t freedCount = 0; - for (QLinkedList >::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) - freedCount += sweep(i->first, i->second, largestFreedBlock); + for (QLinkedList::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) + freedCount += sweep(reinterpret_cast(i->base()), i->size(), largestFreedBlock); return freedCount; } @@ -431,10 +434,10 @@ void MemoryManagerWithNativeStack::collectRootsOnStack(QVector &ro char** heapChunkBoundaries = (char**)alloca(m_d->heapChunks.count() * 2 * sizeof(char*)); char** heapChunkBoundariesEnd = heapChunkBoundaries + 2 * m_d->heapChunks.count(); int i = 0; - for (QLinkedList >::Iterator it = m_d->heapChunks.begin(), end = + for (QLinkedList::Iterator it = m_d->heapChunks.begin(), end = m_d->heapChunks.end(); it != end; ++it) { - heapChunkBoundaries[i++] = it->first; - heapChunkBoundaries[i++] = it->first + it->second; + heapChunkBoundaries[i++] = reinterpret_cast(it->base()); + heapChunkBoundaries[i++] = reinterpret_cast(it->base()) + it->size(); } qSort(heapChunkBoundaries, heapChunkBoundariesEnd); -- cgit v1.2.3 From 278eba11a464d2a2b2b842018508d2e02aac9df1 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 19 Dec 2012 11:21:06 +0100 Subject: Change the interpreter's stack frame to be allocated with alloca. Change-Id: Ia02ad1af3bb0f429a6078029bf7aaee5a17f3413 Reviewed-by: Simon Hausmann --- main.cpp | 6 ++-- moth/moth.pri | 6 ++-- moth/qv4mm_moth.cpp | 81 ---------------------------------------------------- moth/qv4mm_moth.h | 59 -------------------------------------- moth/qv4vme_moth.cpp | 8 ++---- 5 files changed, 7 insertions(+), 153 deletions(-) delete mode 100644 moth/qv4mm_moth.cpp delete mode 100644 moth/qv4mm_moth.h diff --git a/main.cpp b/main.cpp index 28f06022e1..39f90f7d6e 100644 --- a/main.cpp +++ b/main.cpp @@ -53,7 +53,7 @@ #include "qv4syntaxchecker_p.h" #include "qv4ecmaobjects_p.h" #include "qv4isel_p.h" -#include "qv4mm_moth.h" +#include "qv4mm.h" #include #include @@ -333,13 +333,11 @@ int main(int argc, char *argv[]) #endif // QMLJS_NO_LLVM case use_masm: case use_moth: { - QScopedPointer mm; + QScopedPointer mm(new QQmlJS::VM::MemoryManagerWithNativeStack); QScopedPointer iSelFactory; if (mode == use_moth) { - mm.reset(new QQmlJS::Moth::MemoryManager); iSelFactory.reset(new QQmlJS::Moth::ISelFactory); } else { - mm.reset(new QQmlJS::VM::MemoryManagerWithNativeStack); iSelFactory.reset(new QQmlJS::MASM::ISelFactory); } diff --git a/moth/moth.pri b/moth/moth.pri index 160c1aac00..73bd893286 100644 --- a/moth/moth.pri +++ b/moth/moth.pri @@ -3,13 +3,11 @@ INCLUDEPATH += $$PWD HEADERS += \ $$PWD/qv4isel_moth_p.h \ $$PWD/qv4instr_moth_p.h \ - $$PWD/qv4vme_moth_p.h \ - $$PWD/qv4mm_moth.h + $$PWD/qv4vme_moth_p.h SOURCES += \ $$PWD/qv4isel_moth.cpp \ $$PWD/qv4instr_moth.cpp \ - $$PWD/qv4vme_moth.cpp \ - $$PWD/qv4mm_moth.cpp + $$PWD/qv4vme_moth.cpp #DEFINES += DO_TRACE_INSTR diff --git a/moth/qv4mm_moth.cpp b/moth/qv4mm_moth.cpp deleted file mode 100644 index 44e45fe027..0000000000 --- a/moth/qv4mm_moth.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#include "qmljs_engine.h" -#include "qv4mm_moth.h" - -#include - -using namespace QQmlJS; -using namespace QQmlJS::Moth; - -MemoryManager::MemoryManager() -{ - stackFrames.reserve(64); -} - -MemoryManager::~MemoryManager() -{ -} - -VM::Value *MemoryManager::allocStackFrame(std::size_t frameSize) -{ - std::size_t size = frameSize * sizeof(VM::Value); - MMObject *m = alloc(align(size)); - stackFrames.append(m); - return reinterpret_cast(&m->data); -} - -void MemoryManager::deallocStackFrame(VM::Value *stackFrame) -{ - MMObject *o = toObject(stackFrame); - for (int i = stackFrames.size() - 1; i >= 0; --i) { - if (stackFrames[i] == o) { - stackFrames.remove(i); - dealloc(o); - return; - } - } - - Q_UNREACHABLE(); -} - -void MemoryManager::collectRootsOnStack(QVector &roots) const -{ - for (int i = 0, ei = stackFrames.size(); i < ei; ++i) { - MMObject *m = stackFrames[i]; - VM::Value *frame = reinterpret_cast(&m->data); - std::size_t frameSize = (m->info.size - align(sizeof(MMInfo))) / sizeof(VM::Value); - for (std::size_t j = 0; j < frameSize; ++j) { - if (VM::Object *o = frame[j].asObject()) { - roots.append(o); - } - } - } -} diff --git a/moth/qv4mm_moth.h b/moth/qv4mm_moth.h deleted file mode 100644 index a2b1ebd556..0000000000 --- a/moth/qv4mm_moth.h +++ /dev/null @@ -1,59 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#ifndef QV4GC_MOTH_H -#define QV4GC_MOTH_H - -#include "qv4mm.h" - -#include - -namespace QQmlJS { -namespace Moth { - -class MemoryManager: public QQmlJS::VM::MemoryManager -{ -public: - MemoryManager(); - ~MemoryManager(); - - VM::Value *allocStackFrame(std::size_t frameSize); - void deallocStackFrame(VM::Value *stackFrame); - -protected: - virtual void collectRootsOnStack(QVector &roots) const; - -private: - QVector stackFrames; -}; - -} // namespace Moth -} // namespace QQmlJS - -#endif // QV4GC_MOTH_H diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 2a1302f423..0156e4a8da 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -2,10 +2,11 @@ #include "qv4instr_moth_p.h" #include "qmljs_value.h" #include "debugging.h" -#include "qv4mm_moth.h" #include +#include + #ifdef DO_TRACE_INSTR # define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); # define TRACE(n, str, ...) { char buf[4096]; snprintf(buf, 4096, str, __VA_ARGS__); fprintf(stderr, " %s : %s\n", #n, buf); } @@ -103,9 +104,6 @@ public: , code(code) {} - ~FunctionState() - { if (stack) static_cast(context()->engine->memoryManager)->deallocStackFrame(stack); } - virtual VM::Value *temp(unsigned idx) { return stack + idx; } void setStack(VM::Value *stack, unsigned stackSize) @@ -201,7 +199,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(Push) TRACE(inline, "stack size: %u", instr.value); stackSize = instr.value; - stack = static_cast(context->engine->memoryManager)->allocStackFrame(stackSize); + stack = static_cast(alloca(stackSize * sizeof(VM::Value))); state.setStack(stack, stackSize); MOTH_END_INSTR(Push) -- cgit v1.2.3 From 274583f2dbf53ec296e50f0e68a7907d90482cee Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 19 Dec 2012 15:48:42 +0100 Subject: Another fix to the temp compression. Pin all temps that escape BBs, not only the return value. Change-Id: Idf21d117bfd12224cbff4cef35766c454189a5fa Reviewed-by: Lars Knoll --- moth/qv4isel_moth.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 0277fc60f1..8f1dfa3143 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -37,7 +37,21 @@ public: _nextFree = 0; _active.reserve(function->tempCount); _localCount = function->locals.size(); - pinReturnTemp(function); + + QVector pinned; + foreach (IR::BasicBlock *block, function->basicBlocks) { + if (IR::Stmt *last = block->terminator()) { + const QBitArray &liveOut = last->d->liveOut; + for (int i = 0, ei = liveOut.size(); i < ei; ++i) { + if (liveOut.at(i) && !pinned.contains(i)) { + pinned.append(i); + add(i - _localCount, _nextFree); + } + } + } + } + _pinnedCount = _nextFree; + int maxUsed = _nextFree; foreach (IR::BasicBlock *block, function->basicBlocks) { @@ -66,18 +80,6 @@ public: } private: - void pinReturnTemp(IR::Function *function) { - const IR::BasicBlock *returnBlock = function->basicBlocks.last(); - assert(returnBlock); - IR::Ret *ret = returnBlock->terminator()->asRet(); - assert(ret); - IR::Temp *t = ret->expr->asTemp(); - assert(t); - assert(t->index >= 0); - _pinnedReturnValue = _nextFree; - add(t->index, _pinnedReturnValue); - } - virtual void visitConst(IR::Const *) {} virtual void visitString(IR::String *) {} virtual void visitRegExp(IR::RegExp *) {} @@ -157,7 +159,7 @@ private: while (i < _active.size()) { const QPair &p = _active[i]; - if (p.second == _pinnedReturnValue) { + if (p.second < _pinnedCount) { inUse.setBit(p.second); ++i; continue; @@ -186,7 +188,7 @@ private: IR::Stmt *_currentStatement; int _localCount; int _nextFree; - int _pinnedReturnValue; + int _pinnedCount; }; } // anonymous namespace -- cgit v1.2.3 From eb8bb49a1331e0c1739c2ac0ff0c443df3b56e81 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 2 Jan 2013 12:28:10 +0100 Subject: Move the Managed class into it's own file Preparations for some further cleanups. Change-Id: Id9fa3a8541748ee70085bc84985ac508f989e1d3 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 22 --------------- qmljs_objects.h | 25 +---------------- qv4managed.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++++ qv4managed.h | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ v4.pro | 6 +++-- 5 files changed, 152 insertions(+), 48 deletions(-) create mode 100644 qv4managed.cpp create mode 100644 qv4managed.h diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 3766081bd7..1840e059ca 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -60,28 +60,6 @@ using namespace QQmlJS::VM; - -Managed::~Managed() -{ -} - -void *Managed::operator new(size_t size, MemoryManager *mm) -{ - assert(mm); - - return mm->allocManaged(size); -} - -void Managed::operator delete(void *ptr) -{ - if (!ptr) - return; - - Managed *m = reinterpret_cast(ptr); - assert(m->mm); - m->mm->deallocManaged(m); -} - // // Object // diff --git a/qmljs_objects.h b/qmljs_objects.h index eae6eebcc6..0e62b6361a 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -47,6 +47,7 @@ #include "qv4array_p.h" #include "qv4codegen_p.h" #include "qv4isel_p.h" +#include "qv4managed.h" #include #include @@ -91,30 +92,6 @@ struct SyntaxErrorPrototype; struct TypeErrorPrototype; struct URIErrorPrototype; -struct Managed -{ -private: - Managed(const Managed &other); - void operator = (const Managed &other); - -protected: - Managed() {} - -public: - virtual ~Managed(); - - void *operator new(size_t size, MemoryManager *mm); - void operator delete(void *ptr); - -protected: - virtual void getCollectables(QVector &objects) = 0; - -private: - void *operator new(size_t); - friend class MemoryManager; - MemoryManager *mm; -}; - struct String { inline bool isEqualTo(const String *other) const { if (this == other) diff --git a/qv4managed.cpp b/qv4managed.cpp new file mode 100644 index 0000000000..a78f809fad --- /dev/null +++ b/qv4managed.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4managed.h" +#include "qv4mm.h" + +using namespace QQmlJS::VM; + +Managed::~Managed() +{ +} + +void *Managed::operator new(size_t size, MemoryManager *mm) +{ + assert(mm); + + return mm->allocManaged(size); +} + +void Managed::operator delete(void *ptr) +{ + if (!ptr) + return; + + Managed *m = reinterpret_cast(ptr); + assert(m->mm); + m->mm->deallocManaged(m); +} diff --git a/qv4managed.h b/qv4managed.h new file mode 100644 index 0000000000..068f662662 --- /dev/null +++ b/qv4managed.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_MANAGED_H +#define QMLJS_MANAGED_H + +#include +#include + +namespace QQmlJS { + +namespace VM { + +class MemoryManager; +struct Object; + +struct Managed +{ +private: + Managed(const Managed &other); + void operator = (const Managed &other); + +protected: + Managed() {} + +public: + virtual ~Managed(); + + void *operator new(size_t size, MemoryManager *mm); + void operator delete(void *ptr); + +protected: + virtual void getCollectables(QVector &objects) = 0; + +private: + void *operator new(size_t); + friend class MemoryManager; + MemoryManager *mm; +}; + +} +} + +#endif diff --git a/v4.pro b/v4.pro index 1f1604b10f..7bdb8959b4 100644 --- a/v4.pro +++ b/v4.pro @@ -25,7 +25,8 @@ SOURCES += main.cpp \ llvm_runtime.cpp \ qv4isel_p.cpp \ debugging.cpp \ - qv4mm.cpp + qv4mm.cpp \ + qv4managed.cpp HEADERS += \ qv4codegen_p.h \ @@ -43,7 +44,8 @@ HEADERS += \ qv4isel_p.h \ qv4isel_util_p.h \ debugging.h \ - qv4mm.h + qv4mm.h \ + qv4managed.h llvm { -- cgit v1.2.3 From 283e5efe5cc41767b9e0ad4ecae19d4b43f02582 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 2 Jan 2013 15:25:25 +0100 Subject: Remove unused memory manager classes Unify the base class with the GC memory manager and remove the memory manager that didn't manage ;-) Change-Id: I8579bc80b66688e98203448afc645a231c97fede Reviewed-by: Simon Hausmann --- main.cpp | 2 +- qv4mm.cpp | 15 ++------------- qv4mm.h | 28 ++-------------------------- 3 files changed, 5 insertions(+), 40 deletions(-) diff --git a/main.cpp b/main.cpp index 39f90f7d6e..8f1f147829 100644 --- a/main.cpp +++ b/main.cpp @@ -333,7 +333,7 @@ int main(int argc, char *argv[]) #endif // QMLJS_NO_LLVM case use_masm: case use_moth: { - QScopedPointer mm(new QQmlJS::VM::MemoryManagerWithNativeStack); + QScopedPointer mm(new QQmlJS::VM::MemoryManager); QScopedPointer iSelFactory; if (mode == use_moth) { iSelFactory.reset(new QQmlJS::Moth::ISelFactory); diff --git a/qv4mm.cpp b/qv4mm.cpp index 25dfaa4cd2..8a6c5eac0a 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -91,6 +91,7 @@ struct MemoryManager::Data MemoryManager::MemoryManager() : m_d(new Data(true)) { + setEnableGC(true); } MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) @@ -410,19 +411,7 @@ void MemoryManager::collectRoots(QVector &roots) const collectRootsOnStack(roots); } -MemoryManagerWithoutGC::~MemoryManagerWithoutGC() -{} - -void MemoryManagerWithoutGC::collectRootsOnStack(QVector &roots) const -{ - Q_UNUSED(roots); -} - -MemoryManagerWithNativeStack::~MemoryManagerWithNativeStack() -{ -} - -void MemoryManagerWithNativeStack::collectRootsOnStack(QVector &roots) const +void MemoryManager::collectRootsOnStack(QVector &roots) const { if (!m_d->heapChunks.count()) return; diff --git a/qv4mm.h b/qv4mm.h index a4ea893c15..6f163efe5e 100644 --- a/qv4mm.h +++ b/qv4mm.h @@ -69,7 +69,7 @@ public: public: MemoryManager(); - virtual ~MemoryManager() = 0; + ~MemoryManager(); // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries). // Note: all occurances of "16" in alloc/dealloc are also due to the alignment. @@ -145,7 +145,7 @@ protected: void scribble(MMObject *obj, int c) const; - virtual void collectRootsOnStack(QVector &roots) const = 0; + void collectRootsOnStack(QVector &roots) const; ExecutionEngine *engine() const; @@ -165,30 +165,6 @@ protected: QScopedPointer m_d; }; -class MemoryManagerWithoutGC: public MemoryManager -{ -public: - MemoryManagerWithoutGC() - { setEnableGC(false); } - - virtual ~MemoryManagerWithoutGC(); - -protected: - virtual void collectRootsOnStack(QVector &roots) const; -}; - -class MemoryManagerWithNativeStack: public MemoryManager -{ -public: - MemoryManagerWithNativeStack() - { setEnableGC(true); } - - virtual ~MemoryManagerWithNativeStack(); - -protected: - virtual void collectRootsOnStack(QVector &roots) const; -}; - } // namespace VM } // namespace QQmlJS -- cgit v1.2.3 From b46c04669f32a360eb839ca3ec17e769a7494825 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 2 Jan 2013 16:43:47 +0100 Subject: Simplify and fix memory management The old code could lead to crashes because we didn't correctly check the alignment of objects in the managed heaps. A bogus pointer on the stack (which can easily happen), can then point into the middle of an object in the heap and cause memory corruption. Change-Id: I741401d278a7926a549810707ca46435bdaf7cc9 Reviewed-by: Simon Hausmann --- qv4mm.cpp | 196 +++++++++++++++++++++++++++----------------------------------- qv4mm.h | 17 ++---- 2 files changed, 91 insertions(+), 122 deletions(-) diff --git a/qv4mm.cpp b/qv4mm.cpp index 8a6c5eac0a..bffdad3bfa 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -37,7 +37,7 @@ #include #include -#include +#include #include #include @@ -59,10 +59,14 @@ struct MemoryManager::Data ExecutionEngine *engine; StringPool *stringPool; - MMObject *fallbackObject; - MMObject *smallItems[16]; // 16 - 256 bytes - QMap largeItems; - QLinkedList heapChunks; + enum { MaxItemSize = 128 }; + MMObject *smallItems[MaxItemSize/16]; + struct Chunk { + PageAllocation memory; + int chunkSize; + }; + + QVector heapChunks; // statistics: #ifdef DETAILED_MM_STATS @@ -74,7 +78,6 @@ struct MemoryManager::Data , gcBlocked(false) , engine(0) , stringPool(0) - , fallbackObject(0) { memset(smallItems, 0, sizeof(smallItems)); scribble = qgetenv("MM_NO_SCRIBBLE").isEmpty(); @@ -83,11 +86,17 @@ struct MemoryManager::Data ~Data() { - for (QLinkedList::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) - i->deallocate(); + for (QVector::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) + i->memory.deallocate(); } }; +static bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::Chunk &b) +{ + return a.memory.base() < b.memory.base(); +} + + MemoryManager::MemoryManager() : m_d(new Data(true)) { @@ -111,67 +120,47 @@ MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) size_t pos = size >> 4; // fits into a small bucket - if (pos < sizeof(m_d->smallItems)/sizeof(MMObject *)) { - MMObject *m = m_d->smallItems[pos]; - if (m) { - m_d->smallItems[pos] = m->info.next; - m->info.inUse = 1; - m->info.markBit = 0; - return m; - } - } + assert(size < MemoryManager::Data::MaxItemSize); + MMObject *m = m_d->smallItems[pos]; + if (m) + goto found; - // ### use new heap space if available - if (m_d->fallbackObject && m_d->fallbackObject->info.size >= size) { - MMObject *m = m_d->fallbackObject; - m_d->fallbackObject = splitItem(m, size); - m->info.inUse = 1; - m->info.markBit = 0; - return m; - } + // try to free up space, otherwise allocate +// if (!m_d->aggressiveGC) +// runGC(); - // use or split up a large bucket - QMap::iterator it = m_d->largeItems.lowerBound(pos); - if (it == m_d->largeItems.end()) { - // try to free up space, otherwise allocate - if (!m_d->aggressiveGC || runGC() < size) { - std::size_t allocSize = std::max(size, CHUNK_SIZE); - allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); - PageAllocation allocation = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); - m_d->heapChunks.append(allocation); - m_d->fallbackObject = reinterpret_cast(allocation.base()); - m_d->fallbackObject->info.inUse = 0; - m_d->fallbackObject->info.next = 0; - m_d->fallbackObject->info.markBit = 0; - m_d->fallbackObject->info.size = allocation.size(); - } - return alloc(size - alignedSizeOfMMInfo); - } + m = m_d->smallItems[pos]; + if (m) + goto found; - MMObject *m = it.value(); - assert(m); - - if (it.key() == pos) { - // a match, return it - if (!m->info.next) - m_d->largeItems.erase(it); - else - *it = m->info.next; - m->info.inUse = 1; - m->info.markBit = 0; - return m; + // no free item available, allocate a new chunk + { + std::size_t allocSize = std::max(size, CHUNK_SIZE); + allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); + Data::Chunk allocation; + allocation.memory = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); + allocation.chunkSize = size; + m_d->heapChunks.append(allocation); + qSort(m_d->heapChunks); + char *chunk = (char *)allocation.memory.base(); + char *end = chunk + allocation.memory.size() - size; + MMObject **last = &m_d->smallItems[pos]; + while (chunk <= end) { + MMObject *o = reinterpret_cast(chunk); + o->info.inUse = 0; + o->info.next = 0; + o->info.markBit = 0; + o->info.size = size; + *last = o; + last = &o->info.next; + chunk += size; + } + m = m_d->smallItems[pos]; } - // split up - if (!m->info.next) - m_d->largeItems.erase(it); - else - *it = m->info.next; - MMObject *tail = splitItem(m, size); - MMObject *&f = m_d->largeItems[tail->info.size]; - tail->info.next = f; - f = tail; + found: + m_d->smallItems[pos] = m->info.next; m->info.inUse = 1; m->info.markBit = 0; return m; @@ -188,34 +177,15 @@ void MemoryManager::dealloc(MMObject *ptr) // qDebug("dealloc %p (%lu)", ptr, ptr->info.size); std::size_t pos = ptr->info.size >> 4; - MMObject **f; - - // fits into a small bucket - if (pos < sizeof(m_d->smallItems)/sizeof(MMObject *)) { - f = &m_d->smallItems[pos]; - } else { - f = &m_d->largeItems[pos]; - } + MMObject **f = &m_d->smallItems[pos]; ptr->info.next = *f; ptr->info.inUse = 0; ptr->info.markBit = 0; - ptr->info.needsManagedDestructorCall = 0; +// scribble(ptr, 0x99); *f = ptr; } -MemoryManager::MMObject *MemoryManager::splitItem(MemoryManager::MMObject *m, int newSize) -{ - if (newSize - m->info.size <= sizeof(MMObject)) - return 0; - MMObject *tail = reinterpret_cast(reinterpret_cast(m) + newSize); - tail->info.inUse = 0; - tail->info.markBit = 0; - tail->info.size = m->info.size - newSize; - m->info.size = newSize; - return tail; -} - void MemoryManager::scribble(MemoryManager::MMObject *obj, int c) const { if (m_d->scribble) @@ -247,40 +217,39 @@ std::size_t MemoryManager::mark(const QVector &objects) return marks; } -std::size_t MemoryManager::sweep(std::size_t &largestFreedBlock) +std::size_t MemoryManager::sweep() { std::size_t freedCount = 0; - for (QLinkedList::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) - freedCount += sweep(reinterpret_cast(i->base()), i->size(), largestFreedBlock); + for (QVector::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) + freedCount += sweep(reinterpret_cast(i->memory.base()), i->memory.size(), i->chunkSize); return freedCount; } -std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, std::size_t &largestFreedBlock) +std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t size) { -// qDebug("chunkStart @ %p", chunkStart); +// qDebug("chunkStart @ %p, size=%x", chunkStart, size); std::size_t freedCount = 0; - for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize; chunk < chunkEnd; ) { + for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize - size; chunk <= chunkEnd; ) { MMObject *m = reinterpret_cast(chunk); // qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s", // chunk, m->info.size, (m->info.inUse ? "yes" : "no"), (m->info.markBit ? "true" : "false")); assert((intptr_t) chunk % 16 == 0); assert(m->info.size >= 16); + assert(m->info.size == size); assert(m->info.size % 16 == 0); - chunk = chunk + m->info.size; + chunk = chunk + size; if (m->info.inUse) { if (m->info.markBit) { m->info.markBit = 0; } else { -// qDebug("-- collecting it."); - if (m->info.needsManagedDestructorCall) - reinterpret_cast(&m->data)->~Managed(); +// qDebug() << "-- collecting it." << m << reinterpret_cast(&m->data); + reinterpret_cast(&m->data)->~Managed(); dealloc(m); - largestFreedBlock = std::max(largestFreedBlock, m->info.size); ++freedCount; } } @@ -299,15 +268,16 @@ void MemoryManager::setGCBlocked(bool blockGC) m_d->gcBlocked = blockGC; } -std::size_t MemoryManager::runGC() +void MemoryManager::runGC() { if (!m_d->enableGC || m_d->gcBlocked) { // qDebug() << "Not running GC."; - return 0; + return; } // QTime t; t.start(); +// qDebug() << ">>>>>>>>runGC"; QVector roots; collectRoots(roots); // std::cerr << "GC: found " << roots.size() @@ -321,13 +291,10 @@ std::size_t MemoryManager::runGC() // << "ms" << std::endl; // t.restart(); - std::size_t freedCount = 0, largestFreedBlock = 0; - freedCount = sweep(largestFreedBlock); + /*std::size_t freedCount =*/ sweep(); // std::cerr << "GC: sweep freed " << freedCount // << " objects in " << t.elapsed() // << "ms" << std::endl; - - return largestFreedBlock; } void MemoryManager::setEnableGC(bool enableGC) @@ -337,8 +304,7 @@ void MemoryManager::setEnableGC(bool enableGC) MemoryManager::~MemoryManager() { - std::size_t dummy = 0; - sweep(dummy); + sweep(); } static inline void add(QVector &values, const Value &v) @@ -423,27 +389,35 @@ void MemoryManager::collectRootsOnStack(QVector &roots) const char** heapChunkBoundaries = (char**)alloca(m_d->heapChunks.count() * 2 * sizeof(char*)); char** heapChunkBoundariesEnd = heapChunkBoundaries + 2 * m_d->heapChunks.count(); int i = 0; - for (QLinkedList::Iterator it = m_d->heapChunks.begin(), end = + for (QVector::Iterator it = m_d->heapChunks.begin(), end = m_d->heapChunks.end(); it != end; ++it) { - heapChunkBoundaries[i++] = reinterpret_cast(it->base()); - heapChunkBoundaries[i++] = reinterpret_cast(it->base()) + it->size(); + heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()); + heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()) + it->memory.size(); } - qSort(heapChunkBoundaries, heapChunkBoundariesEnd); for (; current < top; ++current) { Object* possibleObject = current->asObject(); if (!possibleObject) continue; + char* genericPtr = reinterpret_cast(possibleObject); if (genericPtr < *heapChunkBoundaries || genericPtr >= *(heapChunkBoundariesEnd - 1)) continue; int index = qLowerBound(heapChunkBoundaries, heapChunkBoundariesEnd, genericPtr) - heapChunkBoundaries; // An odd index means the pointer is _before_ the end of a heap chunk and therefore valid. if (index & 1) { - // It appears to happen that the stack can still contain a pointer to an already - // dealloc'ed. Skip those. - if (toObject(possibleObject)->info.inUse) - roots.append(possibleObject); + int size = m_d->heapChunks.at(index >> 1).chunkSize; + MMObject *m = toObject(possibleObject); + + if (((quintptr)m - (quintptr)heapChunkBoundaries[index-1]) % size) + // wrongly aligned value, skip it + continue; + + if (m->info.inUse) + // Skip pointers to already freed objects, they are bogus as well + continue; + + roots.append(possibleObject); } } } diff --git a/qv4mm.h b/qv4mm.h index 6f163efe5e..5995984971 100644 --- a/qv4mm.h +++ b/qv4mm.h @@ -44,9 +44,9 @@ class MemoryManager MemoryManager(const MemoryManager &); MemoryManager &operator=(const MemoryManager&); +public: struct Data; -public: class GCBlocker { public: @@ -80,7 +80,6 @@ public: { size = align(size); MMObject *o = alloc(size); - o->info.needsManagedDestructorCall = 1; Managed *ptr = reinterpret_cast(&o->data); ptr->mm = this; return ptr; @@ -97,7 +96,7 @@ public: bool isGCBlocked() const; void setGCBlocked(bool blockGC); - std::size_t runGC(); + void runGC(); void setEnableGC(bool enableGC); void setExecutionEngine(ExecutionEngine *engine); @@ -111,8 +110,7 @@ protected: struct MMInfo { std::size_t inUse : 1; std::size_t markBit : 1; - std::size_t needsManagedDestructorCall : 1; - std::size_t size : 61; + std::size_t size : 62; MMObject *next; }; struct MMObject { @@ -123,8 +121,7 @@ protected: struct MMInfo { std::size_t inUse : 1; std::size_t markBit : 1; - std::size_t needsManagedDestructorCall : 1; - std::size_t size : 29; + std::size_t size : 30; struct MMObject *next; }; struct MMObject { @@ -153,13 +150,11 @@ protected: void willAllocate(std::size_t size); #endif // DETAILED_MM_STATS - MMObject *splitItem(MMObject *m, int newSize); - private: void collectRoots(QVector &roots) const; static std::size_t mark(const QVector &objects); - std::size_t sweep(std::size_t &largestFreedBlock); - std::size_t sweep(char *chunkStart, std::size_t chunkSize, std::size_t &largestFreedBlock); + std::size_t sweep(); + std::size_t sweep(char *chunkStart, std::size_t chunkSize, size_t size); protected: QScopedPointer m_d; -- cgit v1.2.3 From 18d493f9a3f873532eaa197665db60638c415cff Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 2 Jan 2013 22:09:31 +0100 Subject: Move the mark and inUse bits into the Managed class This simplifies the memory manager further, and removes some book-keeping overhead. Change-Id: I969c092cee822f7f0ab89e09d76b5c68c6bd50d2 Reviewed-by: Simon Hausmann --- qv4managed.cpp | 7 +++-- qv4managed.h | 25 ++++++++++++---- qv4mm.cpp | 93 +++++++++++++++++++++++----------------------------------- qv4mm.h | 52 ++++---------------------------- 4 files changed, 67 insertions(+), 110 deletions(-) diff --git a/qv4managed.cpp b/qv4managed.cpp index a78f809fad..a48b9896eb 100644 --- a/qv4managed.cpp +++ b/qv4managed.cpp @@ -46,6 +46,8 @@ using namespace QQmlJS::VM; Managed::~Managed() { + nextFree = 0; + inUse = 0; } void *Managed::operator new(size_t size, MemoryManager *mm) @@ -60,7 +62,6 @@ void Managed::operator delete(void *ptr) if (!ptr) return; - Managed *m = reinterpret_cast(ptr); - assert(m->mm); - m->mm->deallocManaged(m); + Managed *m = static_cast(ptr); + m->~Managed(); } diff --git a/qv4managed.h b/qv4managed.h index 068f662662..584b9d76dd 100644 --- a/qv4managed.h +++ b/qv4managed.h @@ -43,6 +43,8 @@ #include #include +#include +#include namespace QQmlJS { @@ -54,15 +56,15 @@ struct Object; struct Managed { private: + void *operator new(size_t); Managed(const Managed &other); void operator = (const Managed &other); protected: - Managed() {} - -public: + Managed() : markBit(0), inUse(1), unused(0) { } virtual ~Managed(); +public: void *operator new(size_t size, MemoryManager *mm); void operator delete(void *ptr); @@ -70,9 +72,22 @@ protected: virtual void getCollectables(QVector &objects) = 0; private: - void *operator new(size_t); friend class MemoryManager; - MemoryManager *mm; + + union { + Managed *nextFree; + struct { + quintptr markBit : 1; + quintptr inUse : 1; +#if CPU(X86_64) + quintptr unused : 62; +#elif CPU(X86) + quintptr unused : 30; +#else +#error "implement me" +#endif + }; + }; }; } diff --git a/qv4mm.cpp b/qv4mm.cpp index bffdad3bfa..c7344501a5 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -60,7 +60,7 @@ struct MemoryManager::Data StringPool *stringPool; enum { MaxItemSize = 128 }; - MMObject *smallItems[MaxItemSize/16]; + Managed *smallItems[MaxItemSize/16]; struct Chunk { PageAllocation memory; int chunkSize; @@ -103,7 +103,7 @@ MemoryManager::MemoryManager() setEnableGC(true); } -MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) +Managed *MemoryManager::alloc(std::size_t size) { if (m_d->aggressiveGC) runGC(); @@ -111,9 +111,6 @@ MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) willAllocate(size); #endif // DETAILED_MM_STATS - const std::size_t alignedSizeOfMMInfo = align(sizeof(MMInfo)); - size += alignedSizeOfMMInfo; - assert(size >= 16); assert(size % 16 == 0); @@ -122,13 +119,13 @@ MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) // fits into a small bucket assert(size < MemoryManager::Data::MaxItemSize); - MMObject *m = m_d->smallItems[pos]; + Managed *m = m_d->smallItems[pos]; if (m) goto found; // try to free up space, otherwise allocate -// if (!m_d->aggressiveGC) -// runGC(); + if (!m_d->aggressiveGC) + runGC(); m = m_d->smallItems[pos]; if (m) @@ -145,51 +142,28 @@ MemoryManager::MMObject *MemoryManager::alloc(std::size_t size) qSort(m_d->heapChunks); char *chunk = (char *)allocation.memory.base(); char *end = chunk + allocation.memory.size() - size; - MMObject **last = &m_d->smallItems[pos]; + Managed **last = &m_d->smallItems[pos]; while (chunk <= end) { - MMObject *o = reinterpret_cast(chunk); - o->info.inUse = 0; - o->info.next = 0; - o->info.markBit = 0; - o->info.size = size; + Managed *o = reinterpret_cast(chunk); + o->inUse = 0; + o->markBit = 0; *last = o; - last = &o->info.next; + last = &o->nextFree; chunk += size; } + *last = 0; m = m_d->smallItems[pos]; } found: - m_d->smallItems[pos] = m->info.next; - m->info.inUse = 1; - m->info.markBit = 0; + m_d->smallItems[pos] = m->nextFree; return m; } -void MemoryManager::dealloc(MMObject *ptr) -{ - if (!ptr) - return; - - assert(ptr->info.size >= 16); - assert(ptr->info.size % 16 == 0); - -// qDebug("dealloc %p (%lu)", ptr, ptr->info.size); - - std::size_t pos = ptr->info.size >> 4; - MMObject **f = &m_d->smallItems[pos]; - - ptr->info.next = *f; - ptr->info.inUse = 0; - ptr->info.markBit = 0; -// scribble(ptr, 0x99); - *f = ptr; -} - -void MemoryManager::scribble(MemoryManager::MMObject *obj, int c) const +void MemoryManager::scribble(Managed *obj, int c, int size) const { if (m_d->scribble) - ::memset(&obj->data, c, obj->info.size - sizeof(MMInfo)); + ::memset(obj + 1, c, size - sizeof(Managed)); } std::size_t MemoryManager::mark(const QVector &objects) @@ -203,10 +177,10 @@ std::size_t MemoryManager::mark(const QVector &objects) if (!o) continue; - MMObject *obj = toObject(o); - assert(obj->info.inUse); - if (obj->info.markBit == 0) { - obj->info.markBit = 1; + Managed *obj = o; + assert(obj->inUse); + if (obj->markBit == 0) { + obj->markBit = 1; ++marks; static_cast(o)->getCollectables(kids); marks += mark(kids); @@ -232,26 +206,32 @@ std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t // qDebug("chunkStart @ %p, size=%x", chunkStart, size); std::size_t freedCount = 0; + Managed **f = &m_d->smallItems[size >> 4]; + for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize - size; chunk <= chunkEnd; ) { - MMObject *m = reinterpret_cast(chunk); + Managed *m = reinterpret_cast(chunk); // qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s", -// chunk, m->info.size, (m->info.inUse ? "yes" : "no"), (m->info.markBit ? "true" : "false")); +// chunk, m->size, (m->inUse ? "yes" : "no"), (m->markBit ? "true" : "false")); assert((intptr_t) chunk % 16 == 0); - assert(m->info.size >= 16); - assert(m->info.size == size); - assert(m->info.size % 16 == 0); chunk = chunk + size; - if (m->info.inUse) { - if (m->info.markBit) { - m->info.markBit = 0; + if (m->inUse) { + if (m->markBit) { + m->markBit = 0; } else { // qDebug() << "-- collecting it." << m << reinterpret_cast(&m->data); - reinterpret_cast(&m->data)->~Managed(); - dealloc(m); + m->~Managed(); + + m->nextFree = *f; + f = &m->nextFree; + //scribble(m, 0x99, size); ++freedCount; } + } else if (!m->nextFree) { + m->nextFree = *f; + f = &m->nextFree; + ++freedCount; } } @@ -381,6 +361,7 @@ void MemoryManager::collectRootsOnStack(QVector &roots) const { if (!m_d->heapChunks.count()) return; + Value valueOnStack = Value::undefinedValue(); StackBounds bounds = StackBounds::currentThreadStackBounds(); Value* top = reinterpret_cast(bounds.origin()) - 1; @@ -407,13 +388,13 @@ void MemoryManager::collectRootsOnStack(QVector &roots) const // An odd index means the pointer is _before_ the end of a heap chunk and therefore valid. if (index & 1) { int size = m_d->heapChunks.at(index >> 1).chunkSize; - MMObject *m = toObject(possibleObject); + Managed *m = possibleObject; if (((quintptr)m - (quintptr)heapChunkBoundaries[index-1]) % size) // wrongly aligned value, skip it continue; - if (m->info.inUse) + if (!m->inUse) // Skip pointers to already freed objects, they are bogus as well continue; diff --git a/qv4mm.h b/qv4mm.h index 5995984971..cefb97b01d 100644 --- a/qv4mm.h +++ b/qv4mm.h @@ -39,6 +39,8 @@ namespace QQmlJS { namespace VM { +class Managed; + class MemoryManager { MemoryManager(const MemoryManager &); @@ -79,19 +81,8 @@ public: inline Managed *allocManaged(std::size_t size) { size = align(size); - MMObject *o = alloc(size); - Managed *ptr = reinterpret_cast(&o->data); - ptr->mm = this; - return ptr; - } - - inline void deallocManaged(Managed *m) - { - if (!m) - return; - - assert(m->mm == this); - dealloc(toObject(m)); + Managed *o = alloc(size); + return o; } bool isGCBlocked() const; @@ -105,42 +96,11 @@ public: void dumpStats() const; protected: - struct MMObject; -#if CPU(X86_64) // 64bit and x86: - struct MMInfo { - std::size_t inUse : 1; - std::size_t markBit : 1; - std::size_t size : 62; - MMObject *next; - }; - struct MMObject { - MMInfo info; - std::size_t data; - }; -#elif CPU(X86) // for 32bits: - struct MMInfo { - std::size_t inUse : 1; - std::size_t markBit : 1; - std::size_t size : 30; - struct MMObject *next; - }; - struct MMObject { - MMInfo info; - std::size_t data; - }; -#endif - -protected: - static inline MMObject *toObject(void *ptr) { return reinterpret_cast(reinterpret_cast(ptr) - sizeof(MMInfo)); } - /// expects size to be aligned // TODO: try to inline - MMObject *alloc(std::size_t size); - - // TODO: try to inline - void dealloc(MMObject *ptr); + Managed *alloc(std::size_t size); - void scribble(MMObject *obj, int c) const; + void scribble(Managed *obj, int c, int size) const; void collectRootsOnStack(QVector &roots) const; -- cgit v1.2.3 From 5e496cc7bd5c236fce41ff903894992e218effa1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 2 Jan 2013 22:35:18 +0100 Subject: Save some memory Move some boolean flags from Object and FunctionObject into the Managed class and reduce the size of these Objects. Change-Id: Iee9ab09407ec44b447f9597a9b1d55e9092e7ad5 Reviewed-by: Simon Hausmann --- qmljs_objects.h | 17 ++++++----------- qv4managed.h | 10 +++++++--- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/qmljs_objects.h b/qmljs_objects.h index 0e62b6361a..61fee48eef 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -398,11 +398,9 @@ private: struct Object: Managed { Object *prototype; QScopedPointer members; - bool extensible; Object() - : prototype(0) - , extensible(true) {} + : prototype(0) {} virtual ~Object(); @@ -538,23 +536,20 @@ struct FunctionObject: Object { ExecutionContext *scope; String *name; String **formalParameterList; - unsigned int formalParameterCount; String **varList; + unsigned int formalParameterCount; unsigned int varCount; - bool needsActivation; - bool usesArgumentsObject; - bool strictMode; FunctionObject(ExecutionContext *scope) : scope(scope) , name(0) , formalParameterList(0) - , formalParameterCount(0) , varList(0) + , formalParameterCount(0) , varCount(0) - , needsActivation(false) - , usesArgumentsObject(false) - , strictMode(false) {} + { needsActivation = false; + usesArgumentsObject = false; + strictMode = false; } virtual QString className() { return QStringLiteral("Function"); } virtual FunctionObject *asFunctionObject() { return this; } diff --git a/qv4managed.h b/qv4managed.h index 584b9d76dd..22138b9310 100644 --- a/qv4managed.h +++ b/qv4managed.h @@ -61,7 +61,7 @@ private: void operator = (const Managed &other); protected: - Managed() : markBit(0), inUse(1), unused(0) { } + Managed() : markBit(0), inUse(1), extensible(true), unused(0) { } virtual ~Managed(); public: @@ -79,10 +79,14 @@ private: struct { quintptr markBit : 1; quintptr inUse : 1; + quintptr extensible : 1; // used by Object + quintptr needsActivation : 1; // used by FunctionObject + quintptr usesArgumentsObject : 1; // used by FunctionObject + quintptr strictMode : 1; // used by FunctionObject #if CPU(X86_64) - quintptr unused : 62; + quintptr unused : 58; #elif CPU(X86) - quintptr unused : 30; + quintptr unused : 26; #else #error "implement me" #endif -- cgit v1.2.3 From 215c0458e6b6f19677b6ba0b3a88ecc3b2e4b58b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 3 Jan 2013 13:50:10 +0100 Subject: Get rid of wtf/StackBounds All we need is the starting address of the stack, not the entire class. Change-Id: I447482f6900afa0a66efce2dcc32239828b64f8e Reviewed-by: Lars Knoll --- masm/masm.pri | 5 +- masm/wtf/StackBounds.cpp | 261 ----------------------------------------------- masm/wtf/StackBounds.h | 129 ----------------------- qv4mm.cpp | 17 ++- 4 files changed, 16 insertions(+), 396 deletions(-) delete mode 100644 masm/wtf/StackBounds.cpp delete mode 100644 masm/wtf/StackBounds.h diff --git a/masm/masm.pri b/masm/masm.pri index c648170937..3a8968d112 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -6,9 +6,6 @@ SOURCES += $$PWD/assembler/MacroAssemblerARM.cpp SOURCES += $$PWD/assembler/MacroAssemblerSH4.cpp SOURCES += $$PWD/assembler/LinkBuffer.cpp -SOURCES += $$PWD/wtf/StackBounds.cpp -HEADERS += $$PWD/wtf/StackBounds.h - SOURCES += $$PWD/wtf/PrintStream.cpp HEADERS += $$PWD/wtf/PrintStream.h @@ -43,6 +40,8 @@ DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 DEFINES += ENABLE_ASSEMBLER=1 DEFINES += USE_SYSTEM_MALLOC=1 +DEFINES += BUILDING_QT__ + INCLUDEPATH += $$PWD/jit INCLUDEPATH += $$PWD/assembler INCLUDEPATH += $$PWD/wtf diff --git a/masm/wtf/StackBounds.cpp b/masm/wtf/StackBounds.cpp deleted file mode 100644 index a272ce3de9..0000000000 --- a/masm/wtf/StackBounds.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. - * Copyright (C) 2007 Eric Seidel - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "config.h" -#include "StackBounds.h" - -#if OS(DARWIN) - -#include -#include -#include - -#elif OS(WINDOWS) - -#include - -#elif OS(SOLARIS) - -#include - -#elif OS(QNX) - -#include -#include -#include -#include -#include -#include - -#elif OS(UNIX) - -#include -#if HAVE(PTHREAD_NP_H) -#include -#endif - -#endif - -namespace WTF { - -// Bug 26276 - Need a mechanism to determine stack extent -// -// These platforms should now be working correctly: -// DARWIN, QNX, UNIX -// These platforms are not: -// WINDOWS, SOLARIS, OPENBSD, WINCE -// -// FIXME: remove this! - this code unsafely guesses at stack sizes! -#if OS(WINDOWS) || OS(SOLARIS) || OS(OPENBSD) -// Based on the current limit used by the JSC parser, guess the stack size. -static const ptrdiff_t estimatedStackSize = 128 * sizeof(void*) * 1024; -// This method assumes the stack is growing downwards. -static void* estimateStackBound(void* origin) -{ - return static_cast(origin) - estimatedStackSize; -} -#endif - -#if OS(DARWIN) - -void StackBounds::initialize() -{ - pthread_t thread = pthread_self(); - m_origin = pthread_get_stackaddr_np(thread); - m_bound = static_cast(m_origin) - pthread_get_stacksize_np(thread); -} - -#elif OS(QNX) - -void StackBounds::initialize() -{ - void* stackBase = 0; - size_t stackSize = 0; - - struct _debug_thread_info threadInfo; - memset(&threadInfo, 0, sizeof(threadInfo)); - threadInfo.tid = pthread_self(); - int fd = open("/proc/self", O_RDONLY); - if (fd == -1) { - LOG_ERROR("Unable to open /proc/self (errno: %d)", errno); - CRASH(); - } - devctl(fd, DCMD_PROC_TIDSTATUS, &threadInfo, sizeof(threadInfo), 0); - close(fd); - stackBase = reinterpret_cast(threadInfo.stkbase); - stackSize = threadInfo.stksize; - ASSERT(stackBase); - - m_bound = static_cast(stackBase) + 0x1000; // 4kb guard page - m_origin = static_cast(stackBase) + stackSize; -} - -#elif OS(SOLARIS) - -void StackBounds::initialize() -{ - stack_t s; - thr_stksegment(&s); - m_origin = s.ss_sp; - m_bound = estimateStackBound(m_origin); -} - -#elif OS(OPENBSD) - -void StackBounds::initialize() -{ - pthread_t thread = pthread_self(); - stack_t stack; - pthread_stackseg_np(thread, &stack); - m_origin = stack.ss_sp; - m_bound = estimateStackBound(m_origin); -} - -#elif OS(UNIX) - -void StackBounds::initialize() -{ - void* stackBase = 0; - size_t stackSize = 0; - - pthread_t thread = pthread_self(); - pthread_attr_t sattr; - pthread_attr_init(&sattr); -#if HAVE(PTHREAD_NP_H) || OS(NETBSD) - // e.g. on FreeBSD 5.4, neundorf@kde.org - pthread_attr_get_np(thread, &sattr); -#else - // FIXME: this function is non-portable; other POSIX systems may have different np alternatives - pthread_getattr_np(thread, &sattr); -#endif - int rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize); - (void)rc; // FIXME: Deal with error code somehow? Seems fatal. - ASSERT(stackBase); - pthread_attr_destroy(&sattr); - m_bound = stackBase; - m_origin = static_cast(stackBase) + stackSize; -} - -#elif OS(WINCE) - -static bool detectGrowingDownward(void* previousFrame) -{ - // Find the address of this stack frame by taking the address of a local variable. - int thisFrame; - return previousFrame > &thisFrame; -} - -static inline bool isPageWritable(void* page) -{ - MEMORY_BASIC_INFORMATION memoryInformation; - DWORD result = VirtualQuery(page, &memoryInformation, sizeof(memoryInformation)); - - // return false on error, including ptr outside memory - if (result != sizeof(memoryInformation)) - return false; - - DWORD protect = memoryInformation.Protect & ~(PAGE_GUARD | PAGE_NOCACHE); - return protect == PAGE_READWRITE - || protect == PAGE_WRITECOPY - || protect == PAGE_EXECUTE_READWRITE - || protect == PAGE_EXECUTE_WRITECOPY; -} - -static inline void* getLowerStackBound(char* currentPage, DWORD pageSize) -{ - while (currentPage > 0) { - // check for underflow - if (currentPage >= reinterpret_cast(pageSize)) - currentPage -= pageSize; - else - currentPage = 0; - - if (!isPageWritable(currentPage)) - return currentPage + pageSize; - } - - return 0; -} - -static inline void* getUpperStackBound(char* currentPage, DWORD pageSize) -{ - do { - // guaranteed to complete because isPageWritable returns false at end of memory - currentPage += pageSize; - } while (isPageWritable(currentPage)); - - return currentPage - pageSize; -} - -void StackBounds::initialize() -{ - // find the address of this stack frame by taking the address of a local variable - void* thisFrame = &thisFrame; - bool isGrowingDownward = detectGrowingDownward(thisFrame); - - SYSTEM_INFO systemInfo; - GetSystemInfo(&systemInfo); - DWORD pageSize = systemInfo.dwPageSize; - - // scan all of memory starting from this frame, and return the last writeable page found - char* currentPage = reinterpret_cast(reinterpret_cast(thisFrame) & ~(pageSize - 1)); - void* lowerStackBound = getLowerStackBound(currentPage, pageSize); - void* upperStackBound = getUpperStackBound(currentPage, pageSize); - - m_origin = isGrowingDownward ? upperStackBound : lowerStackBound; - m_bound = isGrowingDownward ? lowerStackBound : upperStackBound; -} - -#elif OS(WINDOWS) - -void StackBounds::initialize() -{ -#if CPU(X86) && COMPILER(MSVC) - // offset 0x18 from the FS segment register gives a pointer to - // the thread information block for the current thread - NT_TIB* pTib; - __asm { - MOV EAX, FS:[18h] - MOV pTib, EAX - } - m_origin = static_cast(pTib->StackBase); -#elif CPU(X86) && COMPILER(GCC) - // offset 0x18 from the FS segment register gives a pointer to - // the thread information block for the current thread - NT_TIB* pTib; - asm ( "movl %%fs:0x18, %0\n" - : "=r" (pTib) - ); - m_origin = static_cast(pTib->StackBase); -#elif CPU(X86_64) - PNT_TIB64 pTib = reinterpret_cast(NtCurrentTeb()); - m_origin = reinterpret_cast(pTib->StackBase); -#else -#error Need a way to get the stack bounds on this platform (Windows) -#endif - // Looks like we should be able to get pTib->StackLimit - m_bound = estimateStackBound(m_origin); -} - -#else -#error Need a way to get the stack bounds on this platform -#endif - -} // namespace WTF diff --git a/masm/wtf/StackBounds.h b/masm/wtf/StackBounds.h deleted file mode 100644 index 185afec226..0000000000 --- a/masm/wtf/StackBounds.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef StackBounds_h -#define StackBounds_h - -namespace WTF { - -class StackBounds { - // isSafeToRecurse() / recursionLimit() tests (by default) - // that we are at least this far from the end of the stack. - // - // This 64k number was picked because a sampling of stack usage differences - // between consecutive entries into one of the Interpreter::execute...() - // functions was seen to be as high as 27k. Hence, 64k is chosen as a - // conservative availability value that is not too large but comfortably - // exceeds 27k with some buffer for error. - const static size_t s_defaultAvailabilityDelta = 64 * 1024; - -public: - StackBounds() - : m_origin(0) - , m_bound(0) - { - } - - static StackBounds currentThreadStackBounds() - { - StackBounds bounds; - bounds.initialize(); - bounds.checkConsistency(); - return bounds; - } - - void* origin() const - { - ASSERT(m_origin); - return m_origin; - } - - void* current() const - { - checkConsistency(); - void* currentPosition = ¤tPosition; - return currentPosition; - } - - size_t size() const - { - return isGrowingDownward() - ? static_cast(m_origin) - static_cast(m_bound) - : static_cast(m_bound) - static_cast(m_origin); - } - - void* recursionLimit(size_t minAvailableDelta = s_defaultAvailabilityDelta) const - { - checkConsistency(); - return isGrowingDownward() - ? static_cast(m_bound) + minAvailableDelta - : static_cast(m_bound) - minAvailableDelta; - } - - bool isSafeToRecurse(size_t minAvailableDelta = s_defaultAvailabilityDelta) const - { - checkConsistency(); - return isGrowingDownward() - ? current() >= recursionLimit(minAvailableDelta) - : current() <= recursionLimit(minAvailableDelta); - } - -private: - void initialize(); - - - bool isGrowingDownward() const - { - ASSERT(m_origin && m_bound); -#if OS(WINCE) - return m_origin > m_bound; -#else - return true; -#endif - } - - void checkConsistency() const - { -#if !ASSERT_DISABLED - void* currentPosition = ¤tPosition; - ASSERT(m_origin != m_bound); - ASSERT(isGrowingDownward() - ? (currentPosition < m_origin && currentPosition > m_bound) - : (currentPosition > m_origin && currentPosition < m_bound)); -#endif - } - - void* m_origin; - void* m_bound; - - friend class StackStats; -}; - -} // namespace WTF - -using WTF::StackBounds; - -#endif diff --git a/qv4mm.cpp b/qv4mm.cpp index c7344501a5..ea6031317f 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -31,7 +31,6 @@ #include "qmljs_objects.h" #include "qv4ecmaobjects_p.h" #include "qv4mm.h" -#include "StackBounds.h" #include "PageAllocation.h" #include "StdLibExtras.h" @@ -363,8 +362,20 @@ void MemoryManager::collectRootsOnStack(QVector &roots) const return; Value valueOnStack = Value::undefinedValue(); - StackBounds bounds = StackBounds::currentThreadStackBounds(); - Value* top = reinterpret_cast(bounds.origin()) - 1; + + void* stackTop = 0; +#if USE(PTHREADS) +#if OS(DARWIN) + stackTop = pthread_get_stackaddr_np(pthread_self()); +#else + pthread_attr_t attr; + pthread_getattr_np(pthread_self(), &attr); + size_t stackSize = 0; + pthread_attr_getstack(&attr, &stackTop, &stackSize); +#endif +#endif + + Value* top = reinterpret_cast(stackTop) - 1; Value* current = (&valueOnStack) + 1; char** heapChunkBoundaries = (char**)alloca(m_d->heapChunks.count() * 2 * sizeof(char*)); -- cgit v1.2.3 From 03573f480a086ede7c8150a800790be22fa43d4d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 3 Jan 2013 12:26:19 +0100 Subject: Clean up memory manager ownership Since we have now only one memory manager, we might as well let the ExecutionEngine create and own it. Change-Id: I908adadf64da59e0b8f4c09d4d9502785399cb99 Reviewed-by: Lars Knoll --- main.cpp | 5 ++--- qmljs_engine.cpp | 5 +++-- qmljs_engine.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/main.cpp b/main.cpp index 8f1f147829..2404ee96c0 100644 --- a/main.cpp +++ b/main.cpp @@ -333,7 +333,6 @@ int main(int argc, char *argv[]) #endif // QMLJS_NO_LLVM case use_masm: case use_moth: { - QScopedPointer mm(new QQmlJS::VM::MemoryManager); QScopedPointer iSelFactory; if (mode == use_moth) { iSelFactory.reset(new QQmlJS::Moth::ISelFactory); @@ -341,7 +340,7 @@ int main(int argc, char *argv[]) iSelFactory.reset(new QQmlJS::MASM::ISelFactory); } - QQmlJS::VM::ExecutionEngine vm(mm.data(), iSelFactory.data()); + QQmlJS::VM::ExecutionEngine vm(iSelFactory.data()); QScopedPointer debugger; if (enableDebugging) @@ -396,7 +395,7 @@ int main(int argc, char *argv[]) } } - mm->dumpStats(); + vm.memoryManager->dumpStats(); } return EXIT_SUCCESS; } } diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 78fd64bf7b..e322cfa261 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -66,8 +66,8 @@ public: } }; -ExecutionEngine::ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory *factory) - : memoryManager(memoryManager) +ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) + : memoryManager(new QQmlJS::VM::MemoryManager) , iselFactory(factory) , debugger(0) , globalObject(Value::nullValue()) @@ -216,6 +216,7 @@ ExecutionEngine::~ExecutionEngine() delete globalObject.asObject(); delete rootContext; qDeleteAll(functions); + delete memoryManager; } ExecutionContext *ExecutionEngine::newContext() diff --git a/qmljs_engine.h b/qmljs_engine.h index dd856f62b7..e281620b21 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -157,7 +157,7 @@ struct ExecutionEngine QScopedPointer stringPool; QVector functions; - ExecutionEngine(MemoryManager *memoryManager, EvalISelFactory *iselFactory); + ExecutionEngine(EvalISelFactory *iselFactory); ~ExecutionEngine(); ExecutionContext *newContext(); -- cgit v1.2.3 From 9951c682ff93a940db75f6a46b843a7a05660e29 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 3 Jan 2013 14:00:59 +0100 Subject: Fix compilation with clang on Mac OS X Add missing friend declarations for required access to private fields and moved operator< into namespace so that the compiler can find it when calling qSort(). Change-Id: I6c94b6fc79c5039903e62ce08b0a6b273133e104 Reviewed-by: Lars Knoll --- qv4managed.h | 8 ++++++++ qv4mm.cpp | 5 ++++- qv4mm.h | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/qv4managed.h b/qv4managed.h index 22138b9310..6ccc68c777 100644 --- a/qv4managed.h +++ b/qv4managed.h @@ -52,6 +52,9 @@ namespace VM { class MemoryManager; struct Object; +struct ObjectPrototype; +struct ExecutionContext; +struct ScriptFunction; struct Managed { @@ -73,6 +76,11 @@ protected: private: friend class MemoryManager; + friend struct Object; + friend struct ObjectPrototype; + friend struct FunctionObject; + friend struct ExecutionContext; + friend struct ScriptFunction; union { Managed *nextFree; diff --git a/qv4mm.cpp b/qv4mm.cpp index ea6031317f..fe652b4028 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -90,11 +90,14 @@ struct MemoryManager::Data } }; -static bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::Chunk &b) +namespace QQmlJS { namespace VM { + +bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::Chunk &b) { return a.memory.base() < b.memory.base(); } +} } // namespace QQmlJS::VM MemoryManager::MemoryManager() : m_d(new Data(true)) diff --git a/qv4mm.h b/qv4mm.h index cefb97b01d..10b64c5a82 100644 --- a/qv4mm.h +++ b/qv4mm.h @@ -39,7 +39,7 @@ namespace QQmlJS { namespace VM { -class Managed; +struct Managed; class MemoryManager { -- cgit v1.2.3 From cf2c03a5a42e11e9f05ee02f0065ab008dec321e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 3 Jan 2013 16:16:03 +0100 Subject: Fix remaining number-to-string failures in chapter 9 ...by using the double-conversion code from http://code.google.com/p/double-conversion/ Change-Id: I4cfc17b65c811b7c20a856d1d38961bec78d85a2 Reviewed-by: Lars Knoll --- 3rdparty/double-conversion/README | 6 + 3rdparty/double-conversion/bignum-dtoa.cc | 640 ++++++++++++++++ 3rdparty/double-conversion/bignum-dtoa.h | 84 +++ 3rdparty/double-conversion/bignum.cc | 764 +++++++++++++++++++ 3rdparty/double-conversion/bignum.h | 145 ++++ 3rdparty/double-conversion/cached-powers.cc | 175 +++++ 3rdparty/double-conversion/cached-powers.h | 64 ++ 3rdparty/double-conversion/diy-fp.cc | 57 ++ 3rdparty/double-conversion/diy-fp.h | 118 +++ 3rdparty/double-conversion/double-conversion.cc | 889 +++++++++++++++++++++++ 3rdparty/double-conversion/double-conversion.h | 536 ++++++++++++++ 3rdparty/double-conversion/double-conversion.pri | 3 + 3rdparty/double-conversion/fast-dtoa.cc | 664 +++++++++++++++++ 3rdparty/double-conversion/fast-dtoa.h | 88 +++ 3rdparty/double-conversion/fixed-dtoa.cc | 402 ++++++++++ 3rdparty/double-conversion/fixed-dtoa.h | 56 ++ 3rdparty/double-conversion/ieee.h | 398 ++++++++++ 3rdparty/double-conversion/strtod.cc | 554 ++++++++++++++ 3rdparty/double-conversion/strtod.h | 45 ++ 3rdparty/double-conversion/utils.h | 313 ++++++++ qmljs_runtime.cpp | 10 +- v4.pro | 1 + 22 files changed, 6010 insertions(+), 2 deletions(-) create mode 100644 3rdparty/double-conversion/README create mode 100644 3rdparty/double-conversion/bignum-dtoa.cc create mode 100644 3rdparty/double-conversion/bignum-dtoa.h create mode 100644 3rdparty/double-conversion/bignum.cc create mode 100644 3rdparty/double-conversion/bignum.h create mode 100644 3rdparty/double-conversion/cached-powers.cc create mode 100644 3rdparty/double-conversion/cached-powers.h create mode 100644 3rdparty/double-conversion/diy-fp.cc create mode 100644 3rdparty/double-conversion/diy-fp.h create mode 100644 3rdparty/double-conversion/double-conversion.cc create mode 100644 3rdparty/double-conversion/double-conversion.h create mode 100644 3rdparty/double-conversion/double-conversion.pri create mode 100644 3rdparty/double-conversion/fast-dtoa.cc create mode 100644 3rdparty/double-conversion/fast-dtoa.h create mode 100644 3rdparty/double-conversion/fixed-dtoa.cc create mode 100644 3rdparty/double-conversion/fixed-dtoa.h create mode 100644 3rdparty/double-conversion/ieee.h create mode 100644 3rdparty/double-conversion/strtod.cc create mode 100644 3rdparty/double-conversion/strtod.h create mode 100644 3rdparty/double-conversion/utils.h diff --git a/3rdparty/double-conversion/README b/3rdparty/double-conversion/README new file mode 100644 index 0000000000..40ed4a7efd --- /dev/null +++ b/3rdparty/double-conversion/README @@ -0,0 +1,6 @@ +This is a copy of the library for binary-decimal and decimal-binary conversion routines for IEEE doubles, taken +from + + http://code.google.com/p/double-conversion/ + +commit e5b34421b763f7bf7e4f9081403db417d5a55a36 diff --git a/3rdparty/double-conversion/bignum-dtoa.cc b/3rdparty/double-conversion/bignum-dtoa.cc new file mode 100644 index 0000000000..b6c2e85d17 --- /dev/null +++ b/3rdparty/double-conversion/bignum-dtoa.cc @@ -0,0 +1,640 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "bignum-dtoa.h" + +#include "bignum.h" +#include "ieee.h" + +namespace double_conversion { + +static int NormalizedExponent(uint64_t significand, int exponent) { + ASSERT(significand != 0); + while ((significand & Double::kHiddenBit) == 0) { + significand = significand << 1; + exponent = exponent - 1; + } + return exponent; +} + + +// Forward declarations: +// Returns an estimation of k such that 10^(k-1) <= v < 10^k. +static int EstimatePower(int exponent); +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. +static void InitialScaledStartValues(uint64_t significand, + int exponent, + bool lower_boundary_is_closer, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus); +// Multiplies numerator/denominator so that its values lies in the range 1-10. +// Returns decimal_point s.t. +// v = numerator'/denominator' * 10^(decimal_point-1) +// where numerator' and denominator' are the values of numerator and +// denominator after the call to this function. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus); +// Generates digits from the left to the right and stops when the generated +// digits yield the shortest decimal representation of v. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector buffer, int* length); +// Generates 'requested_digits' after the decimal point. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector(buffer), int* length); +// Generates 'count' digits of numerator/denominator. +// Once 'count' digits have been produced rounds the result depending on the +// remainder (remainders of exactly .5 round upwards). Might update the +// decimal_point when rounding up (for example for 0.9999). +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector(buffer), int* length); + + +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector buffer, int* length, int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + uint64_t significand; + int exponent; + bool lower_boundary_is_closer; + if (mode == BIGNUM_DTOA_SHORTEST_SINGLE) { + float f = static_cast(v); + ASSERT(f == v); + significand = Single(f).Significand(); + exponent = Single(f).Exponent(); + lower_boundary_is_closer = Single(f).LowerBoundaryIsCloser(); + } else { + significand = Double(v).Significand(); + exponent = Double(v).Exponent(); + lower_boundary_is_closer = Double(v).LowerBoundaryIsCloser(); + } + bool need_boundary_deltas = + (mode == BIGNUM_DTOA_SHORTEST || mode == BIGNUM_DTOA_SHORTEST_SINGLE); + + bool is_even = (significand & 1) == 0; + int normalized_exponent = NormalizedExponent(significand, exponent); + // estimated_power might be too low by 1. + int estimated_power = EstimatePower(normalized_exponent); + + // Shortcut for Fixed. + // The requested digits correspond to the digits after the point. If the + // number is much too small, then there is no need in trying to get any + // digits. + if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) { + buffer[0] = '\0'; + *length = 0; + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + return; + } + + Bignum numerator; + Bignum denominator; + Bignum delta_minus; + Bignum delta_plus; + // Make sure the bignum can grow large enough. The smallest double equals + // 4e-324. In this case the denominator needs fewer than 324*4 binary digits. + // The maximum double is 1.7976931348623157e308 which needs fewer than + // 308*4 binary digits. + ASSERT(Bignum::kMaxSignificantBits >= 324*4); + InitialScaledStartValues(significand, exponent, lower_boundary_is_closer, + estimated_power, need_boundary_deltas, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^estimated_power. + FixupMultiply10(estimated_power, is_even, decimal_point, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^(decimal_point-1), and + // 1 <= (numerator + delta_plus) / denominator < 10 + switch (mode) { + case BIGNUM_DTOA_SHORTEST: + case BIGNUM_DTOA_SHORTEST_SINGLE: + GenerateShortestDigits(&numerator, &denominator, + &delta_minus, &delta_plus, + is_even, buffer, length); + break; + case BIGNUM_DTOA_FIXED: + BignumToFixed(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + case BIGNUM_DTOA_PRECISION: + GenerateCountedDigits(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + default: + UNREACHABLE(); + } + buffer[*length] = '\0'; +} + + +// The procedure starts generating digits from the left to the right and stops +// when the generated digits yield the shortest decimal representation of v. A +// decimal representation of v is a number lying closer to v than to any other +// double, so it converts to v when read. +// +// This is true if d, the decimal representation, is between m- and m+, the +// upper and lower boundaries. d must be strictly between them if !is_even. +// m- := (numerator - delta_minus) / denominator +// m+ := (numerator + delta_plus) / denominator +// +// Precondition: 0 <= (numerator+delta_plus) / denominator < 10. +// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit +// will be produced. This should be the standard precondition. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector buffer, int* length) { + // Small optimization: if delta_minus and delta_plus are the same just reuse + // one of the two bignums. + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_plus = delta_minus; + } + *length = 0; + while (true) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[(*length)++] = digit + '0'; + + // Can we stop already? + // If the remainder of the division is less than the distance to the lower + // boundary we can stop. In this case we simply round down (discarding the + // remainder). + // Similarly we test if we can round up (using the upper boundary). + bool in_delta_room_minus; + bool in_delta_room_plus; + if (is_even) { + in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus); + } else { + in_delta_room_minus = Bignum::Less(*numerator, *delta_minus); + } + if (is_even) { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (!in_delta_room_minus && !in_delta_room_plus) { + // Prepare for next iteration. + numerator->Times10(); + delta_minus->Times10(); + // We optimized delta_plus to be equal to delta_minus (if they share the + // same value). So don't multiply delta_plus if they point to the same + // object. + if (delta_minus != delta_plus) { + delta_plus->Times10(); + } + } else if (in_delta_room_minus && in_delta_room_plus) { + // Let's see if 2*numerator < denominator. + // If yes, then the next digit would be < 5 and we can round down. + int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator); + if (compare < 0) { + // Remaining digits are less than .5. -> Round down (== do nothing). + } else if (compare > 0) { + // Remaining digits are more than .5 of denominator. -> Round up. + // Note that the last digit could not be a '9' as otherwise the whole + // loop would have stopped earlier. + // We still have an assert here in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } else { + // Halfway case. + // TODO(floitsch): need a way to solve half-way cases. + // For now let's round towards even (since this is what Gay seems to + // do). + + if ((buffer[(*length) - 1] - '0') % 2 == 0) { + // Round down => Do nothing. + } else { + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } + } + return; + } else if (in_delta_room_minus) { + // Round down (== do nothing). + return; + } else { // in_delta_room_plus + // Round up. + // Note again that the last digit could not be '9' since this would have + // stopped the loop earlier. + // We still have an ASSERT here, in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) -1] != '9'); + buffer[(*length) - 1]++; + return; + } + } +} + + +// Let v = numerator / denominator < 10. +// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point) +// from left to right. Once 'count' digits have been produced we decide wether +// to round up or down. Remainders of exactly .5 round upwards. Numbers such +// as 9.999999 propagate a carry all the way, and change the +// exponent (decimal_point), when rounding upwards. +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector(buffer), int* length) { + ASSERT(count >= 0); + for (int i = 0; i < count - 1; ++i) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[i] = digit + '0'; + // Prepare for next iteration. + numerator->Times10(); + } + // Generate the last digit. + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + digit++; + } + buffer[count - 1] = digit + '0'; + // Correct bad digits (in case we had a sequence of '9's). Propagate the + // carry until we hat a non-'9' or til we reach the first digit. + for (int i = count - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + if (buffer[0] == '0' + 10) { + // Propagate a carry past the top place. + buffer[0] = '1'; + (*decimal_point)++; + } + *length = count; +} + + +// Generates 'requested_digits' after the decimal point. It might omit +// trailing '0's. If the input number is too small then no digits at all are +// generated (ex.: 2 fixed digits for 0.00001). +// +// Input verifies: 1 <= (numerator + delta) / denominator < 10. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector(buffer), int* length) { + // Note that we have to look at more than just the requested_digits, since + // a number could be rounded up. Example: v=0.5 with requested_digits=0. + // Even though the power of v equals 0 we can't just stop here. + if (-(*decimal_point) > requested_digits) { + // The number is definitively too small. + // Ex: 0.001 with requested_digits == 1. + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + *length = 0; + return; + } else if (-(*decimal_point) == requested_digits) { + // We only need to verify if the number rounds down or up. + // Ex: 0.04 and 0.06 with requested_digits == 1. + ASSERT(*decimal_point == -requested_digits); + // Initially the fraction lies in range (1, 10]. Multiply the denominator + // by 10 so that we can compare more easily. + denominator->Times10(); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + // If the fraction is >= 0.5 then we have to include the rounded + // digit. + buffer[0] = '1'; + *length = 1; + (*decimal_point)++; + } else { + // Note that we caught most of similar cases earlier. + *length = 0; + } + return; + } else { + // The requested digits correspond to the digits after the point. + // The variable 'needed_digits' includes the digits before the point. + int needed_digits = (*decimal_point) + requested_digits; + GenerateCountedDigits(needed_digits, decimal_point, + numerator, denominator, + buffer, length); + } +} + + +// Returns an estimation of k such that 10^(k-1) <= v < 10^k where +// v = f * 2^exponent and 2^52 <= f < 2^53. +// v is hence a normalized double with the given exponent. The output is an +// approximation for the exponent of the decimal approimation .digits * 10^k. +// +// The result might undershoot by 1 in which case 10^k <= v < 10^k+1. +// Note: this property holds for v's upper boundary m+ too. +// 10^k <= m+ < 10^k+1. +// (see explanation below). +// +// Examples: +// EstimatePower(0) => 16 +// EstimatePower(-52) => 0 +// +// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0. +static int EstimatePower(int exponent) { + // This function estimates log10 of v where v = f*2^e (with e == exponent). + // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)). + // Note that f is bounded by its container size. Let p = 53 (the double's + // significand size). Then 2^(p-1) <= f < 2^p. + // + // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close + // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)). + // The computed number undershoots by less than 0.631 (when we compute log3 + // and not log10). + // + // Optimization: since we only need an approximated result this computation + // can be performed on 64 bit integers. On x86/x64 architecture the speedup is + // not really measurable, though. + // + // Since we want to avoid overshooting we decrement by 1e10 so that + // floating-point imprecisions don't affect us. + // + // Explanation for v's boundary m+: the computation takes advantage of + // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement + // (even for denormals where the delta can be much more important). + + const double k1Log10 = 0.30102999566398114; // 1/lg(10) + + // For doubles len(f) == 53 (don't forget the hidden bit). + const int kSignificandSize = Double::kSignificandSize; + double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10); + return static_cast(estimate); +} + + +// See comments for InitialScaledStartValues. +static void InitialScaledStartValuesPositiveExponent( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // A positive exponent implies a positive power. + ASSERT(estimated_power >= 0); + // Since the estimated_power is positive we simply multiply the denominator + // by 10^estimated_power. + + // numerator = v. + numerator->AssignUInt64(significand); + numerator->ShiftLeft(exponent); + // denominator = 10^estimated_power. + denominator->AssignPowerUInt16(10, estimated_power); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + delta_plus->AssignUInt16(1); + delta_plus->ShiftLeft(exponent); + // Same for delta_minus. The adjustments if f == 2^p-1 are done later. + delta_minus->AssignUInt16(1); + delta_minus->ShiftLeft(exponent); + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentPositivePower( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // v = f * 2^e with e < 0, and with estimated_power >= 0. + // This means that e is close to 0 (have a look at how estimated_power is + // computed). + + // numerator = significand + // since v = significand * 2^exponent this is equivalent to + // numerator = v * / 2^-exponent + numerator->AssignUInt64(significand); + // denominator = 10^estimated_power * 2^-exponent (with exponent < 0) + denominator->AssignPowerUInt16(10, estimated_power); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + // Given that the denominator already includes v's exponent the distance + // to the boundaries is simply 1. + delta_plus->AssignUInt16(1); + // Same for delta_minus. The adjustments if f == 2^p-1 are done later. + delta_minus->AssignUInt16(1); + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentNegativePower( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // Instead of multiplying the denominator with 10^estimated_power we + // multiply all values (numerator and deltas) by 10^-estimated_power. + + // Use numerator as temporary container for power_ten. + Bignum* power_ten = numerator; + power_ten->AssignPowerUInt16(10, -estimated_power); + + if (need_boundary_deltas) { + // Since power_ten == numerator we must make a copy of 10^estimated_power + // before we complete the computation of the numerator. + // delta_plus = delta_minus = 10^estimated_power + delta_plus->AssignBignum(*power_ten); + delta_minus->AssignBignum(*power_ten); + } + + // numerator = significand * 2 * 10^-estimated_power + // since v = significand * 2^exponent this is equivalent to + // numerator = v * 10^-estimated_power * 2 * 2^-exponent. + // Remember: numerator has been abused as power_ten. So no need to assign it + // to itself. + ASSERT(numerator == power_ten); + numerator->MultiplyByUInt64(significand); + + // denominator = 2 * 2^-exponent with exponent < 0. + denominator->AssignUInt16(1); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + numerator->ShiftLeft(1); + denominator->ShiftLeft(1); + // With this shift the boundaries have their correct value, since + // delta_plus = 10^-estimated_power, and + // delta_minus = 10^-estimated_power. + // These assignments have been done earlier. + // The adjustments if f == 2^p-1 (lower boundary is closer) are done later. + } +} + + +// Let v = significand * 2^exponent. +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. The functions GenerateShortestDigits and +// GenerateCountedDigits will then convert this ratio to its decimal +// representation d, with the required accuracy. +// Then d * 10^estimated_power is the representation of v. +// (Note: the fraction and the estimated_power might get adjusted before +// generating the decimal representation.) +// +// The initial start values consist of: +// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power. +// - a scaled (common) denominator. +// optionally (used by GenerateShortestDigits to decide if it has the shortest +// decimal converting back to v): +// - v - m-: the distance to the lower boundary. +// - m+ - v: the distance to the upper boundary. +// +// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator. +// +// Let ep == estimated_power, then the returned values will satisfy: +// v / 10^ep = numerator / denominator. +// v's boundarys m- and m+: +// m- / 10^ep == v / 10^ep - delta_minus / denominator +// m+ / 10^ep == v / 10^ep + delta_plus / denominator +// Or in other words: +// m- == v - delta_minus * 10^ep / denominator; +// m+ == v + delta_plus * 10^ep / denominator; +// +// Since 10^(k-1) <= v < 10^k (with k == estimated_power) +// or 10^k <= v < 10^(k+1) +// we then have 0.1 <= numerator/denominator < 1 +// or 1 <= numerator/denominator < 10 +// +// It is then easy to kickstart the digit-generation routine. +// +// The boundary-deltas are only filled if the mode equals BIGNUM_DTOA_SHORTEST +// or BIGNUM_DTOA_SHORTEST_SINGLE. + +static void InitialScaledStartValues(uint64_t significand, + int exponent, + bool lower_boundary_is_closer, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) { + if (exponent >= 0) { + InitialScaledStartValuesPositiveExponent( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else if (estimated_power >= 0) { + InitialScaledStartValuesNegativeExponentPositivePower( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else { + InitialScaledStartValuesNegativeExponentNegativePower( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } + + if (need_boundary_deltas && lower_boundary_is_closer) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the common denominator and adapt all but the delta_minus. + denominator->ShiftLeft(1); // *2 + numerator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } +} + + +// This routine multiplies numerator/denominator so that its values lies in the +// range 1-10. That is after a call to this function we have: +// 1 <= (numerator + delta_plus) /denominator < 10. +// Let numerator the input before modification and numerator' the argument +// after modification, then the output-parameter decimal_point is such that +// numerator / denominator * 10^estimated_power == +// numerator' / denominator' * 10^(decimal_point - 1) +// In some cases estimated_power was too low, and this is already the case. We +// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k == +// estimated_power) but do not touch the numerator or denominator. +// Otherwise the routine multiplies the numerator and the deltas by 10. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + bool in_range; + if (is_even) { + // For IEEE doubles half-way cases (in decimal system numbers ending with 5) + // are rounded to the closest floating-point number with even significand. + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (in_range) { + // Since numerator + delta_plus >= denominator we already have + // 1 <= numerator/denominator < 10. Simply update the estimated_power. + *decimal_point = estimated_power + 1; + } else { + *decimal_point = estimated_power; + numerator->Times10(); + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_minus->Times10(); + delta_plus->AssignBignum(*delta_minus); + } else { + delta_minus->Times10(); + delta_plus->Times10(); + } + } +} + +} // namespace double_conversion diff --git a/3rdparty/double-conversion/bignum-dtoa.h b/3rdparty/double-conversion/bignum-dtoa.h new file mode 100644 index 0000000000..34b961992d --- /dev/null +++ b/3rdparty/double-conversion/bignum-dtoa.h @@ -0,0 +1,84 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_BIGNUM_DTOA_H_ +#define DOUBLE_CONVERSION_BIGNUM_DTOA_H_ + +#include "utils.h" + +namespace double_conversion { + +enum BignumDtoaMode { + // Return the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate but + // correct) 0.3. + BIGNUM_DTOA_SHORTEST, + // Same as BIGNUM_DTOA_SHORTEST but for single-precision floats. + BIGNUM_DTOA_SHORTEST_SINGLE, + // Return a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + BIGNUM_DTOA_FIXED, + // Return a fixed number of digits, no matter what the exponent is. + BIGNUM_DTOA_PRECISION +}; + +// Converts the given double 'v' to ascii. +// The result should be interpreted as buffer * 10^(point-length). +// The buffer will be null-terminated. +// +// The input v must be > 0 and different from NaN, and Infinity. +// +// The output depends on the given mode: +// - SHORTEST: produce the least amount of digits for which the internal +// identity requirement is still satisfied. If the digits are printed +// (together with the correct exponent) then reading this number will give +// 'v' again. The buffer will choose the representation that is closest to +// 'v'. If there are two at the same distance, than the number is round up. +// In this mode the 'requested_digits' parameter is ignored. +// - FIXED: produces digits necessary to print a given number with +// 'requested_digits' digits after the decimal point. The produced digits +// might be too short in which case the caller has to fill the gaps with '0's. +// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. +// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns +// buffer="2", point=0. +// Note: the length of the returned buffer has no meaning wrt the significance +// of its digits. That is, just because it contains '0's does not mean that +// any other digit would not satisfy the internal identity requirement. +// - PRECISION: produces 'requested_digits' where the first digit is not '0'. +// Even though the length of produced digits usually equals +// 'requested_digits', the function is allowed to return fewer digits, in +// which case the caller has to fill the missing digits with '0's. +// Halfway cases are again rounded up. +// 'BignumDtoa' expects the given buffer to be big enough to hold all digits +// and a terminating null-character. +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector buffer, int* length, int* point); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_BIGNUM_DTOA_H_ diff --git a/3rdparty/double-conversion/bignum.cc b/3rdparty/double-conversion/bignum.cc new file mode 100644 index 0000000000..747491a089 --- /dev/null +++ b/3rdparty/double-conversion/bignum.cc @@ -0,0 +1,764 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "bignum.h" +#include "utils.h" + +namespace double_conversion { + +Bignum::Bignum() + : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) { + for (int i = 0; i < kBigitCapacity; ++i) { + bigits_[i] = 0; + } +} + + +template +static int BitSize(S value) { + return 8 * sizeof(value); +} + +// Guaranteed to lie in one Bigit. +void Bignum::AssignUInt16(uint16_t value) { + ASSERT(kBigitSize >= BitSize(value)); + Zero(); + if (value == 0) return; + + EnsureCapacity(1); + bigits_[0] = value; + used_digits_ = 1; +} + + +void Bignum::AssignUInt64(uint64_t value) { + const int kUInt64Size = 64; + + Zero(); + if (value == 0) return; + + int needed_bigits = kUInt64Size / kBigitSize + 1; + EnsureCapacity(needed_bigits); + for (int i = 0; i < needed_bigits; ++i) { + bigits_[i] = value & kBigitMask; + value = value >> kBigitSize; + } + used_digits_ = needed_bigits; + Clamp(); +} + + +void Bignum::AssignBignum(const Bignum& other) { + exponent_ = other.exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + bigits_[i] = other.bigits_[i]; + } + // Clear the excess digits (if there were any). + for (int i = other.used_digits_; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = other.used_digits_; +} + + +static uint64_t ReadUInt64(Vector buffer, + int from, + int digits_to_read) { + uint64_t result = 0; + for (int i = from; i < from + digits_to_read; ++i) { + int digit = buffer[i] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = result * 10 + digit; + } + return result; +} + + +void Bignum::AssignDecimalString(Vector value) { + // 2^64 = 18446744073709551616 > 10^19 + const int kMaxUint64DecimalDigits = 19; + Zero(); + int length = value.length(); + int pos = 0; + // Let's just say that each digit needs 4 bits. + while (length >= kMaxUint64DecimalDigits) { + uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits); + pos += kMaxUint64DecimalDigits; + length -= kMaxUint64DecimalDigits; + MultiplyByPowerOfTen(kMaxUint64DecimalDigits); + AddUInt64(digits); + } + uint64_t digits = ReadUInt64(value, pos, length); + MultiplyByPowerOfTen(length); + AddUInt64(digits); + Clamp(); +} + + +static int HexCharValue(char c) { + if ('0' <= c && c <= '9') return c - '0'; + if ('a' <= c && c <= 'f') return 10 + c - 'a'; + if ('A' <= c && c <= 'F') return 10 + c - 'A'; + UNREACHABLE(); + return 0; // To make compiler happy. +} + + +void Bignum::AssignHexString(Vector value) { + Zero(); + int length = value.length(); + + int needed_bigits = length * 4 / kBigitSize + 1; + EnsureCapacity(needed_bigits); + int string_index = length - 1; + for (int i = 0; i < needed_bigits - 1; ++i) { + // These bigits are guaranteed to be "full". + Chunk current_bigit = 0; + for (int j = 0; j < kBigitSize / 4; j++) { + current_bigit += HexCharValue(value[string_index--]) << (j * 4); + } + bigits_[i] = current_bigit; + } + used_digits_ = needed_bigits - 1; + + Chunk most_significant_bigit = 0; // Could be = 0; + for (int j = 0; j <= string_index; ++j) { + most_significant_bigit <<= 4; + most_significant_bigit += HexCharValue(value[j]); + } + if (most_significant_bigit != 0) { + bigits_[used_digits_] = most_significant_bigit; + used_digits_++; + } + Clamp(); +} + + +void Bignum::AddUInt64(uint64_t operand) { + if (operand == 0) return; + Bignum other; + other.AssignUInt64(operand); + AddBignum(other); +} + + +void Bignum::AddBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + + // If this has a greater exponent than other append zero-bigits to this. + // After this call exponent_ <= other.exponent_. + Align(other); + + // There are two possibilities: + // aaaaaaaaaaa 0000 (where the 0s represent a's exponent) + // bbbbb 00000000 + // ---------------- + // ccccccccccc 0000 + // or + // aaaaaaaaaa 0000 + // bbbbbbbbb 0000000 + // ----------------- + // cccccccccccc 0000 + // In both cases we might need a carry bigit. + + EnsureCapacity(1 + Max(BigitLength(), other.BigitLength()) - exponent_); + Chunk carry = 0; + int bigit_pos = other.exponent_ - exponent_; + ASSERT(bigit_pos >= 0); + for (int i = 0; i < other.used_digits_; ++i) { + Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + + while (carry != 0) { + Chunk sum = bigits_[bigit_pos] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + used_digits_ = Max(bigit_pos, used_digits_); + ASSERT(IsClamped()); +} + + +void Bignum::SubtractBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + // We require this to be bigger than other. + ASSERT(LessEqual(other, *this)); + + Align(other); + + int offset = other.exponent_ - exponent_; + Chunk borrow = 0; + int i; + for (i = 0; i < other.used_digits_; ++i) { + ASSERT((borrow == 0) || (borrow == 1)); + Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + } + while (borrow != 0) { + Chunk difference = bigits_[i + offset] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + ++i; + } + Clamp(); +} + + +void Bignum::ShiftLeft(int shift_amount) { + if (used_digits_ == 0) return; + exponent_ += shift_amount / kBigitSize; + int local_shift = shift_amount % kBigitSize; + EnsureCapacity(used_digits_ + 1); + BigitsShiftLeft(local_shift); +} + + +void Bignum::MultiplyByUInt32(uint32_t factor) { + if (factor == 1) return; + if (factor == 0) { + Zero(); + return; + } + if (used_digits_ == 0) return; + + // The product of a bigit with the factor is of size kBigitSize + 32. + // Assert that this number + 1 (for the carry) fits into double chunk. + ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1); + DoubleChunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + DoubleChunk product = static_cast(factor) * bigits_[i] + carry; + bigits_[i] = static_cast(product & kBigitMask); + carry = (product >> kBigitSize); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } +} + + +void Bignum::MultiplyByUInt64(uint64_t factor) { + if (factor == 1) return; + if (factor == 0) { + Zero(); + return; + } + ASSERT(kBigitSize < 32); + uint64_t carry = 0; + uint64_t low = factor & 0xFFFFFFFF; + uint64_t high = factor >> 32; + for (int i = 0; i < used_digits_; ++i) { + uint64_t product_low = low * bigits_[i]; + uint64_t product_high = high * bigits_[i]; + uint64_t tmp = (carry & kBigitMask) + product_low; + bigits_[i] = tmp & kBigitMask; + carry = (carry >> kBigitSize) + (tmp >> kBigitSize) + + (product_high << (32 - kBigitSize)); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } +} + + +void Bignum::MultiplyByPowerOfTen(int exponent) { + const uint64_t kFive27 = UINT64_2PART_C(0x6765c793, fa10079d); + const uint16_t kFive1 = 5; + const uint16_t kFive2 = kFive1 * 5; + const uint16_t kFive3 = kFive2 * 5; + const uint16_t kFive4 = kFive3 * 5; + const uint16_t kFive5 = kFive4 * 5; + const uint16_t kFive6 = kFive5 * 5; + const uint32_t kFive7 = kFive6 * 5; + const uint32_t kFive8 = kFive7 * 5; + const uint32_t kFive9 = kFive8 * 5; + const uint32_t kFive10 = kFive9 * 5; + const uint32_t kFive11 = kFive10 * 5; + const uint32_t kFive12 = kFive11 * 5; + const uint32_t kFive13 = kFive12 * 5; + const uint32_t kFive1_to_12[] = + { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6, + kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 }; + + ASSERT(exponent >= 0); + if (exponent == 0) return; + if (used_digits_ == 0) return; + + // We shift by exponent at the end just before returning. + int remaining_exponent = exponent; + while (remaining_exponent >= 27) { + MultiplyByUInt64(kFive27); + remaining_exponent -= 27; + } + while (remaining_exponent >= 13) { + MultiplyByUInt32(kFive13); + remaining_exponent -= 13; + } + if (remaining_exponent > 0) { + MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]); + } + ShiftLeft(exponent); +} + + +void Bignum::Square() { + ASSERT(IsClamped()); + int product_length = 2 * used_digits_; + EnsureCapacity(product_length); + + // Comba multiplication: compute each column separately. + // Example: r = a2a1a0 * b2b1b0. + // r = 1 * a0b0 + + // 10 * (a1b0 + a0b1) + + // 100 * (a2b0 + a1b1 + a0b2) + + // 1000 * (a2b1 + a1b2) + + // 10000 * a2b2 + // + // In the worst case we have to accumulate nb-digits products of digit*digit. + // + // Assert that the additional number of bits in a DoubleChunk are enough to + // sum up used_digits of Bigit*Bigit. + if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) { + UNIMPLEMENTED(); + } + DoubleChunk accumulator = 0; + // First shift the digits so we don't overwrite them. + int copy_offset = used_digits_; + for (int i = 0; i < used_digits_; ++i) { + bigits_[copy_offset + i] = bigits_[i]; + } + // We have two loops to avoid some 'if's in the loop. + for (int i = 0; i < used_digits_; ++i) { + // Process temporary digit i with power i. + // The sum of the two indices must be equal to i. + int bigit_index1 = i; + int bigit_index2 = 0; + // Sum all of the sub-products. + while (bigit_index1 >= 0) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + bigits_[i] = static_cast(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + for (int i = used_digits_; i < product_length; ++i) { + int bigit_index1 = used_digits_ - 1; + int bigit_index2 = i - bigit_index1; + // Invariant: sum of both indices is again equal to i. + // Inner loop runs 0 times on last iteration, emptying accumulator. + while (bigit_index2 < used_digits_) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + // The overwritten bigits_[i] will never be read in further loop iterations, + // because bigit_index1 and bigit_index2 are always greater + // than i - used_digits_. + bigits_[i] = static_cast(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + // Since the result was guaranteed to lie inside the number the + // accumulator must be 0 now. + ASSERT(accumulator == 0); + + // Don't forget to update the used_digits and the exponent. + used_digits_ = product_length; + exponent_ *= 2; + Clamp(); +} + + +void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { + ASSERT(base != 0); + ASSERT(power_exponent >= 0); + if (power_exponent == 0) { + AssignUInt16(1); + return; + } + Zero(); + int shifts = 0; + // We expect base to be in range 2-32, and most often to be 10. + // It does not make much sense to implement different algorithms for counting + // the bits. + while ((base & 1) == 0) { + base >>= 1; + shifts++; + } + int bit_size = 0; + int tmp_base = base; + while (tmp_base != 0) { + tmp_base >>= 1; + bit_size++; + } + int final_size = bit_size * power_exponent; + // 1 extra bigit for the shifting, and one for rounded final_size. + EnsureCapacity(final_size / kBigitSize + 2); + + // Left to Right exponentiation. + int mask = 1; + while (power_exponent >= mask) mask <<= 1; + + // The mask is now pointing to the bit above the most significant 1-bit of + // power_exponent. + // Get rid of first 1-bit; + mask >>= 2; + uint64_t this_value = base; + + bool delayed_multipliciation = false; + const uint64_t max_32bits = 0xFFFFFFFF; + while (mask != 0 && this_value <= max_32bits) { + this_value = this_value * this_value; + // Verify that there is enough space in this_value to perform the + // multiplication. The first bit_size bits must be 0. + if ((power_exponent & mask) != 0) { + uint64_t base_bits_mask = + ~((static_cast(1) << (64 - bit_size)) - 1); + bool high_bits_zero = (this_value & base_bits_mask) == 0; + if (high_bits_zero) { + this_value *= base; + } else { + delayed_multipliciation = true; + } + } + mask >>= 1; + } + AssignUInt64(this_value); + if (delayed_multipliciation) { + MultiplyByUInt32(base); + } + + // Now do the same thing as a bignum. + while (mask != 0) { + Square(); + if ((power_exponent & mask) != 0) { + MultiplyByUInt32(base); + } + mask >>= 1; + } + + // And finally add the saved shifts. + ShiftLeft(shifts * power_exponent); +} + + +// Precondition: this/other < 16bit. +uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + ASSERT(other.used_digits_ > 0); + + // Easy case: if we have less digits than the divisor than the result is 0. + // Note: this handles the case where this == 0, too. + if (BigitLength() < other.BigitLength()) { + return 0; + } + + Align(other); + + uint16_t result = 0; + + // Start by removing multiples of 'other' until both numbers have the same + // number of digits. + while (BigitLength() > other.BigitLength()) { + // This naive approach is extremely inefficient if the this divided other + // might be big. This function is implemented for doubleToString where + // the result should be small (less than 10). + ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16)); + // Remove the multiples of the first digit. + // Example this = 23 and other equals 9. -> Remove 2 multiples. + result += bigits_[used_digits_ - 1]; + SubtractTimes(other, bigits_[used_digits_ - 1]); + } + + ASSERT(BigitLength() == other.BigitLength()); + + // Both bignums are at the same length now. + // Since other has more than 0 digits we know that the access to + // bigits_[used_digits_ - 1] is safe. + Chunk this_bigit = bigits_[used_digits_ - 1]; + Chunk other_bigit = other.bigits_[other.used_digits_ - 1]; + + if (other.used_digits_ == 1) { + // Shortcut for easy (and common) case. + int quotient = this_bigit / other_bigit; + bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient; + result += quotient; + Clamp(); + return result; + } + + int division_estimate = this_bigit / (other_bigit + 1); + result += division_estimate; + SubtractTimes(other, division_estimate); + + if (other_bigit * (division_estimate + 1) > this_bigit) { + // No need to even try to subtract. Even if other's remaining digits were 0 + // another subtraction would be too much. + return result; + } + + while (LessEqual(other, *this)) { + SubtractBignum(other); + result++; + } + return result; +} + + +template +static int SizeInHexChars(S number) { + ASSERT(number > 0); + int result = 0; + while (number != 0) { + number >>= 4; + result++; + } + return result; +} + + +static char HexCharOfValue(int value) { + ASSERT(0 <= value && value <= 16); + if (value < 10) return value + '0'; + return value - 10 + 'A'; +} + + +bool Bignum::ToHexString(char* buffer, int buffer_size) const { + ASSERT(IsClamped()); + // Each bigit must be printable as separate hex-character. + ASSERT(kBigitSize % 4 == 0); + const int kHexCharsPerBigit = kBigitSize / 4; + + if (used_digits_ == 0) { + if (buffer_size < 2) return false; + buffer[0] = '0'; + buffer[1] = '\0'; + return true; + } + // We add 1 for the terminating '\0' character. + int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit + + SizeInHexChars(bigits_[used_digits_ - 1]) + 1; + if (needed_chars > buffer_size) return false; + int string_index = needed_chars - 1; + buffer[string_index--] = '\0'; + for (int i = 0; i < exponent_; ++i) { + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = '0'; + } + } + for (int i = 0; i < used_digits_ - 1; ++i) { + Chunk current_bigit = bigits_[i]; + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = HexCharOfValue(current_bigit & 0xF); + current_bigit >>= 4; + } + } + // And finally the last bigit. + Chunk most_significant_bigit = bigits_[used_digits_ - 1]; + while (most_significant_bigit != 0) { + buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF); + most_significant_bigit >>= 4; + } + return true; +} + + +Bignum::Chunk Bignum::BigitAt(int index) const { + if (index >= BigitLength()) return 0; + if (index < exponent_) return 0; + return bigits_[index - exponent_]; +} + + +int Bignum::Compare(const Bignum& a, const Bignum& b) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + int bigit_length_a = a.BigitLength(); + int bigit_length_b = b.BigitLength(); + if (bigit_length_a < bigit_length_b) return -1; + if (bigit_length_a > bigit_length_b) return +1; + for (int i = bigit_length_a - 1; i >= Min(a.exponent_, b.exponent_); --i) { + Chunk bigit_a = a.BigitAt(i); + Chunk bigit_b = b.BigitAt(i); + if (bigit_a < bigit_b) return -1; + if (bigit_a > bigit_b) return +1; + // Otherwise they are equal up to this digit. Try the next digit. + } + return 0; +} + + +int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + ASSERT(c.IsClamped()); + if (a.BigitLength() < b.BigitLength()) { + return PlusCompare(b, a, c); + } + if (a.BigitLength() + 1 < c.BigitLength()) return -1; + if (a.BigitLength() > c.BigitLength()) return +1; + // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than + // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one + // of 'a'. + if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) { + return -1; + } + + Chunk borrow = 0; + // Starting at min_exponent all digits are == 0. So no need to compare them. + int min_exponent = Min(Min(a.exponent_, b.exponent_), c.exponent_); + for (int i = c.BigitLength() - 1; i >= min_exponent; --i) { + Chunk chunk_a = a.BigitAt(i); + Chunk chunk_b = b.BigitAt(i); + Chunk chunk_c = c.BigitAt(i); + Chunk sum = chunk_a + chunk_b; + if (sum > chunk_c + borrow) { + return +1; + } else { + borrow = chunk_c + borrow - sum; + if (borrow > 1) return -1; + borrow <<= kBigitSize; + } + } + if (borrow == 0) return 0; + return -1; +} + + +void Bignum::Clamp() { + while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) { + used_digits_--; + } + if (used_digits_ == 0) { + // Zero. + exponent_ = 0; + } +} + + +bool Bignum::IsClamped() const { + return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0; +} + + +void Bignum::Zero() { + for (int i = 0; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = 0; + exponent_ = 0; +} + + +void Bignum::Align(const Bignum& other) { + if (exponent_ > other.exponent_) { + // If "X" represents a "hidden" digit (by the exponent) then we are in the + // following case (a == this, b == other): + // a: aaaaaaXXXX or a: aaaaaXXX + // b: bbbbbbX b: bbbbbbbbXX + // We replace some of the hidden digits (X) of a with 0 digits. + // a: aaaaaa000X or a: aaaaa0XX + int zero_digits = exponent_ - other.exponent_; + EnsureCapacity(used_digits_ + zero_digits); + for (int i = used_digits_ - 1; i >= 0; --i) { + bigits_[i + zero_digits] = bigits_[i]; + } + for (int i = 0; i < zero_digits; ++i) { + bigits_[i] = 0; + } + used_digits_ += zero_digits; + exponent_ -= zero_digits; + ASSERT(used_digits_ >= 0); + ASSERT(exponent_ >= 0); + } +} + + +void Bignum::BigitsShiftLeft(int shift_amount) { + ASSERT(shift_amount < kBigitSize); + ASSERT(shift_amount >= 0); + Chunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount); + bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask; + carry = new_carry; + } + if (carry != 0) { + bigits_[used_digits_] = carry; + used_digits_++; + } +} + + +void Bignum::SubtractTimes(const Bignum& other, int factor) { + ASSERT(exponent_ <= other.exponent_); + if (factor < 3) { + for (int i = 0; i < factor; ++i) { + SubtractBignum(other); + } + return; + } + Chunk borrow = 0; + int exponent_diff = other.exponent_ - exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + DoubleChunk product = static_cast(factor) * other.bigits_[i]; + DoubleChunk remove = borrow + product; + Chunk difference = bigits_[i + exponent_diff] - (remove & kBigitMask); + bigits_[i + exponent_diff] = difference & kBigitMask; + borrow = static_cast((difference >> (kChunkSize - 1)) + + (remove >> kBigitSize)); + } + for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) { + if (borrow == 0) return; + Chunk difference = bigits_[i] - borrow; + bigits_[i] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + ++i; + } + Clamp(); +} + + +} // namespace double_conversion diff --git a/3rdparty/double-conversion/bignum.h b/3rdparty/double-conversion/bignum.h new file mode 100644 index 0000000000..5ec3544f57 --- /dev/null +++ b/3rdparty/double-conversion/bignum.h @@ -0,0 +1,145 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_BIGNUM_H_ +#define DOUBLE_CONVERSION_BIGNUM_H_ + +#include "utils.h" + +namespace double_conversion { + +class Bignum { + public: + // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately. + // This bignum can encode much bigger numbers, since it contains an + // exponent. + static const int kMaxSignificantBits = 3584; + + Bignum(); + void AssignUInt16(uint16_t value); + void AssignUInt64(uint64_t value); + void AssignBignum(const Bignum& other); + + void AssignDecimalString(Vector value); + void AssignHexString(Vector value); + + void AssignPowerUInt16(uint16_t base, int exponent); + + void AddUInt16(uint16_t operand); + void AddUInt64(uint64_t operand); + void AddBignum(const Bignum& other); + // Precondition: this >= other. + void SubtractBignum(const Bignum& other); + + void Square(); + void ShiftLeft(int shift_amount); + void MultiplyByUInt32(uint32_t factor); + void MultiplyByUInt64(uint64_t factor); + void MultiplyByPowerOfTen(int exponent); + void Times10() { return MultiplyByUInt32(10); } + // Pseudocode: + // int result = this / other; + // this = this % other; + // In the worst case this function is in O(this/other). + uint16_t DivideModuloIntBignum(const Bignum& other); + + bool ToHexString(char* buffer, int buffer_size) const; + + // Returns + // -1 if a < b, + // 0 if a == b, and + // +1 if a > b. + static int Compare(const Bignum& a, const Bignum& b); + static bool Equal(const Bignum& a, const Bignum& b) { + return Compare(a, b) == 0; + } + static bool LessEqual(const Bignum& a, const Bignum& b) { + return Compare(a, b) <= 0; + } + static bool Less(const Bignum& a, const Bignum& b) { + return Compare(a, b) < 0; + } + // Returns Compare(a + b, c); + static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c); + // Returns a + b == c + static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) == 0; + } + // Returns a + b <= c + static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) <= 0; + } + // Returns a + b < c + static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) < 0; + } + private: + typedef uint32_t Chunk; + typedef uint64_t DoubleChunk; + + static const int kChunkSize = sizeof(Chunk) * 8; + static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8; + // With bigit size of 28 we loose some bits, but a double still fits easily + // into two chunks, and more importantly we can use the Comba multiplication. + static const int kBigitSize = 28; + static const Chunk kBigitMask = (1 << kBigitSize) - 1; + // Every instance allocates kBigitLength chunks on the stack. Bignums cannot + // grow. There are no checks if the stack-allocated space is sufficient. + static const int kBigitCapacity = kMaxSignificantBits / kBigitSize; + + void EnsureCapacity(int size) { + if (size > kBigitCapacity) { + UNREACHABLE(); + } + } + void Align(const Bignum& other); + void Clamp(); + bool IsClamped() const; + void Zero(); + // Requires this to have enough capacity (no tests done). + // Updates used_digits_ if necessary. + // shift_amount must be < kBigitSize. + void BigitsShiftLeft(int shift_amount); + // BigitLength includes the "hidden" digits encoded in the exponent. + int BigitLength() const { return used_digits_ + exponent_; } + Chunk BigitAt(int index) const; + void SubtractTimes(const Bignum& other, int factor); + + Chunk bigits_buffer_[kBigitCapacity]; + // A vector backed by bigits_buffer_. This way accesses to the array are + // checked for out-of-bounds errors. + Vector bigits_; + int used_digits_; + // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize). + int exponent_; + + DISALLOW_COPY_AND_ASSIGN(Bignum); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_BIGNUM_H_ diff --git a/3rdparty/double-conversion/cached-powers.cc b/3rdparty/double-conversion/cached-powers.cc new file mode 100644 index 0000000000..c676429194 --- /dev/null +++ b/3rdparty/double-conversion/cached-powers.cc @@ -0,0 +1,175 @@ +// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include + +#include "utils.h" + +#include "cached-powers.h" + +namespace double_conversion { + +struct CachedPower { + uint64_t significand; + int16_t binary_exponent; + int16_t decimal_exponent; +}; + +static const CachedPower kCachedPowers[] = { + {UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348}, + {UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340}, + {UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332}, + {UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324}, + {UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316}, + {UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308}, + {UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300}, + {UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292}, + {UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284}, + {UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276}, + {UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268}, + {UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260}, + {UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252}, + {UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244}, + {UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236}, + {UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228}, + {UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220}, + {UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212}, + {UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204}, + {UINT64_2PART_C(0xef340a98, 172aace5), -715, -196}, + {UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188}, + {UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180}, + {UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172}, + {UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164}, + {UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156}, + {UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148}, + {UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140}, + {UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132}, + {UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124}, + {UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116}, + {UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108}, + {UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100}, + {UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92}, + {UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84}, + {UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76}, + {UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68}, + {UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60}, + {UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52}, + {UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44}, + {UINT64_2PART_C(0xaa242499, 697392d3), -183, -36}, + {UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28}, + {UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20}, + {UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12}, + {UINT64_2PART_C(0xd1b71758, e219652c), -77, -4}, + {UINT64_2PART_C(0x9c400000, 00000000), -50, 4}, + {UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12}, + {UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20}, + {UINT64_2PART_C(0x813f3978, f8940984), 30, 28}, + {UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36}, + {UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44}, + {UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52}, + {UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60}, + {UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68}, + {UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76}, + {UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84}, + {UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92}, + {UINT64_2PART_C(0x924d692c, a61be758), 269, 100}, + {UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108}, + {UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116}, + {UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124}, + {UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132}, + {UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140}, + {UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148}, + {UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156}, + {UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164}, + {UINT64_2PART_C(0xa59bc234, db398c25), 508, 172}, + {UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180}, + {UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188}, + {UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196}, + {UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204}, + {UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212}, + {UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220}, + {UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228}, + {UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236}, + {UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244}, + {UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252}, + {UINT64_2PART_C(0xd01fef10, a657842c), 800, 260}, + {UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268}, + {UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276}, + {UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284}, + {UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292}, + {UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300}, + {UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308}, + {UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316}, + {UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324}, + {UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332}, + {UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340}, +}; + +static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers); +static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent. +static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) +// Difference between the decimal exponents in the table above. +const int PowersOfTenCache::kDecimalExponentDistance = 8; +const int PowersOfTenCache::kMinDecimalExponent = -348; +const int PowersOfTenCache::kMaxDecimalExponent = 340; + +void PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent) { + int kQ = DiyFp::kSignificandSize; + double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10); + int foo = kCachedPowersOffset; + int index = + (foo + static_cast(k) - 1) / kDecimalExponentDistance + 1; + ASSERT(0 <= index && index < kCachedPowersLength); + CachedPower cached_power = kCachedPowers[index]; + ASSERT(min_exponent <= cached_power.binary_exponent); + ASSERT(cached_power.binary_exponent <= max_exponent); + *decimal_exponent = cached_power.decimal_exponent; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); +} + + +void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent) { + ASSERT(kMinDecimalExponent <= requested_exponent); + ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance); + int index = + (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance; + CachedPower cached_power = kCachedPowers[index]; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); + *found_exponent = cached_power.decimal_exponent; + ASSERT(*found_exponent <= requested_exponent); + ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance); +} + +} // namespace double_conversion diff --git a/3rdparty/double-conversion/cached-powers.h b/3rdparty/double-conversion/cached-powers.h new file mode 100644 index 0000000000..61a50614cf --- /dev/null +++ b/3rdparty/double-conversion/cached-powers.h @@ -0,0 +1,64 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_CACHED_POWERS_H_ +#define DOUBLE_CONVERSION_CACHED_POWERS_H_ + +#include "diy-fp.h" + +namespace double_conversion { + +class PowersOfTenCache { + public: + + // Not all powers of ten are cached. The decimal exponent of two neighboring + // cached numbers will differ by kDecimalExponentDistance. + static const int kDecimalExponentDistance; + + static const int kMinDecimalExponent; + static const int kMaxDecimalExponent; + + // Returns a cached power-of-ten with a binary exponent in the range + // [min_exponent; max_exponent] (boundaries included). + static void GetCachedPowerForBinaryExponentRange(int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent); + + // Returns a cached power of ten x ~= 10^k such that + // k <= decimal_exponent < k + kCachedPowersDecimalDistance. + // The given decimal_exponent must satisfy + // kMinDecimalExponent <= requested_exponent, and + // requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance. + static void GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_CACHED_POWERS_H_ diff --git a/3rdparty/double-conversion/diy-fp.cc b/3rdparty/double-conversion/diy-fp.cc new file mode 100644 index 0000000000..ddd1891b16 --- /dev/null +++ b/3rdparty/double-conversion/diy-fp.cc @@ -0,0 +1,57 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +#include "diy-fp.h" +#include "utils.h" + +namespace double_conversion { + +void DiyFp::Multiply(const DiyFp& other) { + // Simply "emulates" a 128 bit multiplication. + // However: the resulting number only contains 64 bits. The least + // significant 64 bits are only used for rounding the most significant 64 + // bits. + const uint64_t kM32 = 0xFFFFFFFFU; + uint64_t a = f_ >> 32; + uint64_t b = f_ & kM32; + uint64_t c = other.f_ >> 32; + uint64_t d = other.f_ & kM32; + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32); + // By adding 1U << 31 to tmp we round the final result. + // Halfway cases will be round up. + tmp += 1U << 31; + uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + e_ += other.e_ + 64; + f_ = result_f; +} + +} // namespace double_conversion diff --git a/3rdparty/double-conversion/diy-fp.h b/3rdparty/double-conversion/diy-fp.h new file mode 100644 index 0000000000..9dcf8fbdba --- /dev/null +++ b/3rdparty/double-conversion/diy-fp.h @@ -0,0 +1,118 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_DIY_FP_H_ +#define DOUBLE_CONVERSION_DIY_FP_H_ + +#include "utils.h" + +namespace double_conversion { + +// This "Do It Yourself Floating Point" class implements a floating-point number +// with a uint64 significand and an int exponent. Normalized DiyFp numbers will +// have the most significant bit of the significand set. +// Multiplication and Subtraction do not normalize their results. +// DiyFp are not designed to contain special doubles (NaN and Infinity). +class DiyFp { + public: + static const int kSignificandSize = 64; + + DiyFp() : f_(0), e_(0) {} + DiyFp(uint64_t f, int e) : f_(f), e_(e) {} + + // this = this - other. + // The exponents of both numbers must be the same and the significand of this + // must be bigger than the significand of other. + // The result will not be normalized. + void Subtract(const DiyFp& other) { + ASSERT(e_ == other.e_); + ASSERT(f_ >= other.f_); + f_ -= other.f_; + } + + // Returns a - b. + // The exponents of both numbers must be the same and this must be bigger + // than other. The result will not be normalized. + static DiyFp Minus(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Subtract(b); + return result; + } + + + // this = this * other. + void Multiply(const DiyFp& other); + + // returns a * b; + static DiyFp Times(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Multiply(b); + return result; + } + + void Normalize() { + ASSERT(f_ != 0); + uint64_t f = f_; + int e = e_; + + // This method is mainly called for normalizing boundaries. In general + // boundaries need to be shifted by 10 bits. We thus optimize for this case. + const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000); + while ((f & k10MSBits) == 0) { + f <<= 10; + e -= 10; + } + while ((f & kUint64MSB) == 0) { + f <<= 1; + e--; + } + f_ = f; + e_ = e; + } + + static DiyFp Normalize(const DiyFp& a) { + DiyFp result = a; + result.Normalize(); + return result; + } + + uint64_t f() const { return f_; } + int e() const { return e_; } + + void set_f(uint64_t new_value) { f_ = new_value; } + void set_e(int new_value) { e_ = new_value; } + + private: + static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000); + + uint64_t f_; + int e_; +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_DIY_FP_H_ diff --git a/3rdparty/double-conversion/double-conversion.cc b/3rdparty/double-conversion/double-conversion.cc new file mode 100644 index 0000000000..a79fe92d22 --- /dev/null +++ b/3rdparty/double-conversion/double-conversion.cc @@ -0,0 +1,889 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include + +#include "double-conversion.h" + +#include "bignum-dtoa.h" +#include "fast-dtoa.h" +#include "fixed-dtoa.h" +#include "ieee.h" +#include "strtod.h" +#include "utils.h" + +namespace double_conversion { + +const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() { + int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN; + static DoubleToStringConverter converter(flags, + "Infinity", + "NaN", + 'e', + -6, 21, + 6, 0); + return converter; +} + + +bool DoubleToStringConverter::HandleSpecialValues( + double value, + StringBuilder* result_builder) const { + Double double_inspect(value); + if (double_inspect.IsInfinite()) { + if (infinity_symbol_ == NULL) return false; + if (value < 0) { + result_builder->AddCharacter('-'); + } + result_builder->AddString(infinity_symbol_); + return true; + } + if (double_inspect.IsNan()) { + if (nan_symbol_ == NULL) return false; + result_builder->AddString(nan_symbol_); + return true; + } + return false; +} + + +void DoubleToStringConverter::CreateExponentialRepresentation( + const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const { + ASSERT(length != 0); + result_builder->AddCharacter(decimal_digits[0]); + if (length != 1) { + result_builder->AddCharacter('.'); + result_builder->AddSubstring(&decimal_digits[1], length-1); + } + result_builder->AddCharacter(exponent_character_); + if (exponent < 0) { + result_builder->AddCharacter('-'); + exponent = -exponent; + } else { + if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) { + result_builder->AddCharacter('+'); + } + } + if (exponent == 0) { + result_builder->AddCharacter('0'); + return; + } + ASSERT(exponent < 1e4); + const int kMaxExponentLength = 5; + char buffer[kMaxExponentLength + 1]; + buffer[kMaxExponentLength] = '\0'; + int first_char_pos = kMaxExponentLength; + while (exponent > 0) { + buffer[--first_char_pos] = '0' + (exponent % 10); + exponent /= 10; + } + result_builder->AddSubstring(&buffer[first_char_pos], + kMaxExponentLength - first_char_pos); +} + + +void DoubleToStringConverter::CreateDecimalRepresentation( + const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const { + // Create a representation that is padded with zeros if needed. + if (decimal_point <= 0) { + // "0.00000decimal_rep". + result_builder->AddCharacter('0'); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', -decimal_point); + ASSERT(length <= digits_after_point - (-decimal_point)); + result_builder->AddSubstring(decimal_digits, length); + int remaining_digits = digits_after_point - (-decimal_point) - length; + result_builder->AddPadding('0', remaining_digits); + } + } else if (decimal_point >= length) { + // "decimal_rep0000.00000" or "decimal_rep.0000" + result_builder->AddSubstring(decimal_digits, length); + result_builder->AddPadding('0', decimal_point - length); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', digits_after_point); + } + } else { + // "decima.l_rep000" + ASSERT(digits_after_point > 0); + result_builder->AddSubstring(decimal_digits, decimal_point); + result_builder->AddCharacter('.'); + ASSERT(length - decimal_point <= digits_after_point); + result_builder->AddSubstring(&decimal_digits[decimal_point], + length - decimal_point); + int remaining_digits = digits_after_point - (length - decimal_point); + result_builder->AddPadding('0', remaining_digits); + } + if (digits_after_point == 0) { + if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) { + result_builder->AddCharacter('.'); + } + if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) { + result_builder->AddCharacter('0'); + } + } +} + + +bool DoubleToStringConverter::ToShortestIeeeNumber( + double value, + StringBuilder* result_builder, + DoubleToStringConverter::DtoaMode mode) const { + ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE); + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + int decimal_point; + bool sign; + const int kDecimalRepCapacity = kBase10MaximalLength + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = (flags_ & UNIQUE_ZERO) != 0; + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + if ((decimal_in_shortest_low_ <= exponent) && + (exponent < decimal_in_shortest_high_)) { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, + decimal_point, + Max(0, decimal_rep_length - decimal_point), + result_builder); + } else { + CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, + result_builder); + } + return true; +} + + +bool DoubleToStringConverter::ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const { + ASSERT(kMaxFixedDigitsBeforePoint == 60); + const double kFirstNonFixed = 1e60; + + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits > kMaxFixedDigitsAfterPoint) return false; + if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false; + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add space for the '\0' byte. + const int kDecimalRepCapacity = + kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + DoubleToAscii(value, FIXED, requested_digits, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + requested_digits, result_builder); + return true; +} + + +bool DoubleToStringConverter::ToExponential( + double value, + int requested_digits, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits < -1) return false; + if (requested_digits > kMaxExponentialDigits) return false; + + int decimal_point; + bool sign; + // Add space for digit before the decimal point and the '\0' character. + const int kDecimalRepCapacity = kMaxExponentialDigits + 2; + ASSERT(kDecimalRepCapacity > kBase10MaximalLength); + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + if (requested_digits == -1) { + DoubleToAscii(value, SHORTEST, 0, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + } else { + DoubleToAscii(value, PRECISION, requested_digits + 1, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + ASSERT(decimal_rep_length <= requested_digits + 1); + + for (int i = decimal_rep_length; i < requested_digits + 1; ++i) { + decimal_rep[i] = '0'; + } + decimal_rep_length = requested_digits + 1; + } + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + CreateExponentialRepresentation(decimal_rep, + decimal_rep_length, + exponent, + result_builder); + return true; +} + + +bool DoubleToStringConverter::ToPrecision(double value, + int precision, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { + return false; + } + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add one for the terminating null character. + const int kDecimalRepCapacity = kMaxPrecisionDigits + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, PRECISION, precision, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + ASSERT(decimal_rep_length <= precision); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + // The exponent if we print the number as x.xxeyyy. That is with the + // decimal point after the first digit. + int exponent = decimal_point - 1; + + int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0; + if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) || + (decimal_point - precision + extra_zero > + max_trailing_padding_zeroes_in_precision_mode_)) { + // Fill buffer to contain 'precision' digits. + // Usually the buffer is already at the correct length, but 'DoubleToAscii' + // is allowed to return less characters. + for (int i = decimal_rep_length; i < precision; ++i) { + decimal_rep[i] = '0'; + } + + CreateExponentialRepresentation(decimal_rep, + precision, + exponent, + result_builder); + } else { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + Max(0, precision - decimal_point), + result_builder); + } + return true; +} + + +static BignumDtoaMode DtoaToBignumDtoaMode( + DoubleToStringConverter::DtoaMode dtoa_mode) { + switch (dtoa_mode) { + case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST; + case DoubleToStringConverter::SHORTEST_SINGLE: + return BIGNUM_DTOA_SHORTEST_SINGLE; + case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED; + case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION; + default: + UNREACHABLE(); + return BIGNUM_DTOA_SHORTEST; // To silence compiler. + } +} + + +void DoubleToStringConverter::DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point) { + Vector vector(buffer, buffer_length); + ASSERT(!Double(v).IsSpecial()); + ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0); + + if (Double(v).Sign() < 0) { + *sign = true; + v = -v; + } else { + *sign = false; + } + + if (mode == PRECISION && requested_digits == 0) { + vector[0] = '\0'; + *length = 0; + return; + } + + if (v == 0) { + vector[0] = '0'; + vector[1] = '\0'; + *length = 1; + *point = 1; + return; + } + + bool fast_worked; + switch (mode) { + case SHORTEST: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point); + break; + case SHORTEST_SINGLE: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0, + vector, length, point); + break; + case FIXED: + fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point); + break; + case PRECISION: + fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, + vector, length, point); + break; + default: + UNREACHABLE(); + fast_worked = false; + } + if (fast_worked) return; + + // If the fast dtoa didn't succeed use the slower bignum version. + BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); + BignumDtoa(v, bignum_mode, requested_digits, vector, length, point); + vector[*length] = '\0'; +} + + +// Consumes the given substring from the iterator. +// Returns false, if the substring does not match. +static bool ConsumeSubString(const char** current, + const char* end, + const char* substring) { + ASSERT(**current == *substring); + for (substring++; *substring != '\0'; substring++) { + ++*current; + if (*current == end || **current != *substring) return false; + } + ++*current; + return true; +} + + +// Maximum number of significant digits in decimal representation. +// The longest possible double in decimal representation is +// (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074 +// (768 digits). If we parse a number whose first digits are equal to a +// mean of 2 adjacent doubles (that could have up to 769 digits) the result +// must be rounded to the bigger one unless the tail consists of zeros, so +// we don't need to preserve all the digits. +const int kMaxSignificantDigits = 772; + + +// Returns true if a nonspace found and false if the end has reached. +static inline bool AdvanceToNonspace(const char** current, const char* end) { + while (*current != end) { + if (**current != ' ') return true; + ++*current; + } + return false; +} + + +static bool isDigit(int x, int radix) { + return (x >= '0' && x <= '9' && x < '0' + radix) + || (radix > 10 && x >= 'a' && x < 'a' + radix - 10) + || (radix > 10 && x >= 'A' && x < 'A' + radix - 10); +} + + +static double SignedZero(bool sign) { + return sign ? -0.0 : 0.0; +} + + +// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end. +template +static double RadixStringToIeee(const char* current, + const char* end, + bool sign, + bool allow_trailing_junk, + double junk_string_value, + bool read_as_double, + const char** trailing_pointer) { + ASSERT(current != end); + + const int kDoubleSize = Double::kSignificandSize; + const int kSingleSize = Single::kSignificandSize; + const int kSignificandSize = read_as_double? kDoubleSize: kSingleSize; + + // Skip leading 0s. + while (*current == '0') { + ++current; + if (current == end) { + *trailing_pointer = end; + return SignedZero(sign); + } + } + + int64_t number = 0; + int exponent = 0; + const int radix = (1 << radix_log_2); + + do { + int digit; + if (*current >= '0' && *current <= '9' && *current < '0' + radix) { + digit = static_cast(*current) - '0'; + } else if (radix > 10 && *current >= 'a' && *current < 'a' + radix - 10) { + digit = static_cast(*current) - 'a' + 10; + } else if (radix > 10 && *current >= 'A' && *current < 'A' + radix - 10) { + digit = static_cast(*current) - 'A' + 10; + } else { + if (allow_trailing_junk || !AdvanceToNonspace(¤t, end)) { + break; + } else { + return junk_string_value; + } + } + + number = number * radix + digit; + int overflow = static_cast(number >> kSignificandSize); + if (overflow != 0) { + // Overflow occurred. Need to determine which direction to round the + // result. + int overflow_bits_count = 1; + while (overflow > 1) { + overflow_bits_count++; + overflow >>= 1; + } + + int dropped_bits_mask = ((1 << overflow_bits_count) - 1); + int dropped_bits = static_cast(number) & dropped_bits_mask; + number >>= overflow_bits_count; + exponent = overflow_bits_count; + + bool zero_tail = true; + while (true) { + ++current; + if (current == end || !isDigit(*current, radix)) break; + zero_tail = zero_tail && *current == '0'; + exponent += radix_log_2; + } + + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value; + } + + int middle_value = (1 << (overflow_bits_count - 1)); + if (dropped_bits > middle_value) { + number++; // Rounding up. + } else if (dropped_bits == middle_value) { + // Rounding to even to consistency with decimals: half-way case rounds + // up if significant part is odd and down otherwise. + if ((number & 1) != 0 || !zero_tail) { + number++; // Rounding up. + } + } + + // Rounding up may cause overflow. + if ((number & ((int64_t)1 << kSignificandSize)) != 0) { + exponent++; + number >>= 1; + } + break; + } + ++current; + } while (current != end); + + ASSERT(number < ((int64_t)1 << kSignificandSize)); + ASSERT(static_cast(static_cast(number)) == number); + + *trailing_pointer = current; + + if (exponent == 0) { + if (sign) { + if (number == 0) return -0.0; + number = -number; + } + return static_cast(number); + } + + ASSERT(number != 0); + return Double(DiyFp(number, exponent)).value(); +} + + +double StringToDoubleConverter::StringToIeee( + const char* input, + int length, + int* processed_characters_count, + bool read_as_double) { + const char* current = input; + const char* end = input + length; + + *processed_characters_count = 0; + + const bool allow_trailing_junk = (flags_ & ALLOW_TRAILING_JUNK) != 0; + const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0; + const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0; + const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0; + + // To make sure that iterator dereferencing is valid the following + // convention is used: + // 1. Each '++current' statement is followed by check for equality to 'end'. + // 2. If AdvanceToNonspace returned false then current == end. + // 3. If 'current' becomes equal to 'end' the function returns or goes to + // 'parsing_done'. + // 4. 'current' is not dereferenced after the 'parsing_done' label. + // 5. Code before 'parsing_done' may rely on 'current != end'. + if (current == end) return empty_string_value_; + + if (allow_leading_spaces || allow_trailing_spaces) { + if (!AdvanceToNonspace(¤t, end)) { + *processed_characters_count = current - input; + return empty_string_value_; + } + if (!allow_leading_spaces && (input != current)) { + // No leading spaces allowed, but AdvanceToNonspace moved forward. + return junk_string_value_; + } + } + + // The longest form of simplified number is: "-.1eXXX\0". + const int kBufferSize = kMaxSignificantDigits + 10; + char buffer[kBufferSize]; // NOLINT: size is known at compile time. + int buffer_pos = 0; + + // Exponent will be adjusted if insignificant digits of the integer part + // or insignificant leading zeros of the fractional part are dropped. + int exponent = 0; + int significant_digits = 0; + int insignificant_digits = 0; + bool nonzero_digit_dropped = false; + + bool sign = false; + + if (*current == '+' || *current == '-') { + sign = (*current == '-'); + ++current; + const char* next_non_space = current; + // Skip following spaces (if allowed). + if (!AdvanceToNonspace(&next_non_space, end)) return junk_string_value_; + if (!allow_spaces_after_sign && (current != next_non_space)) { + return junk_string_value_; + } + current = next_non_space; + } + + if (infinity_symbol_ != NULL) { + if (*current == infinity_symbol_[0]) { + if (!ConsumeSubString(¤t, end, infinity_symbol_)) { + return junk_string_value_; + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + + ASSERT(buffer_pos == 0); + *processed_characters_count = current - input; + return sign ? -Double::Infinity() : Double::Infinity(); + } + } + + if (nan_symbol_ != NULL) { + if (*current == nan_symbol_[0]) { + if (!ConsumeSubString(¤t, end, nan_symbol_)) { + return junk_string_value_; + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + + ASSERT(buffer_pos == 0); + *processed_characters_count = current - input; + return sign ? -Double::NaN() : Double::NaN(); + } + } + + bool leading_zero = false; + if (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = current - input; + return SignedZero(sign); + } + + leading_zero = true; + + // It could be hexadecimal value. + if ((flags_ & ALLOW_HEX) && (*current == 'x' || *current == 'X')) { + ++current; + if (current == end || !isDigit(*current, 16)) { + return junk_string_value_; // "0x". + } + + const char* tail_pointer = NULL; + double result = RadixStringToIeee<4>(current, + end, + sign, + allow_trailing_junk, + junk_string_value_, + read_as_double, + &tail_pointer); + if (tail_pointer != NULL) { + if (allow_trailing_spaces) AdvanceToNonspace(&tail_pointer, end); + *processed_characters_count = tail_pointer - input; + } + return result; + } + + // Ignore leading zeros in the integer part. + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = current - input; + return SignedZero(sign); + } + } + } + + bool octal = leading_zero && (flags_ & ALLOW_OCTALS) != 0; + + // Copy significant digits of the integer part (if any) to the buffer. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast(*current); + significant_digits++; + // Will later check if it's an octal in the buffer. + } else { + insignificant_digits++; // Move the digit into the exponential part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + octal = octal && *current < '8'; + ++current; + if (current == end) goto parsing_done; + } + + if (significant_digits == 0) { + octal = false; + } + + if (*current == '.') { + if (octal && !allow_trailing_junk) return junk_string_value_; + if (octal) goto parsing_done; + + ++current; + if (current == end) { + if (significant_digits == 0 && !leading_zero) { + return junk_string_value_; + } else { + goto parsing_done; + } + } + + if (significant_digits == 0) { + // octal = false; + // Integer part consists of 0 or is absent. Significant digits start after + // leading zeros (if any). + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = current - input; + return SignedZero(sign); + } + exponent--; // Move this 0 into the exponent. + } + } + + // There is a fractional part. + // We don't emit a '.', but adjust the exponent instead. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast(*current); + significant_digits++; + exponent--; + } else { + // Ignore insignificant digits in the fractional part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + ++current; + if (current == end) goto parsing_done; + } + } + + if (!leading_zero && exponent == 0 && significant_digits == 0) { + // If leading_zeros is true then the string contains zeros. + // If exponent < 0 then string was [+-]\.0*... + // If significant_digits != 0 the string is not equal to 0. + // Otherwise there are no digits in the string. + return junk_string_value_; + } + + // Parse exponential part. + if (*current == 'e' || *current == 'E') { + if (octal && !allow_trailing_junk) return junk_string_value_; + if (octal) goto parsing_done; + ++current; + if (current == end) { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + char sign = '+'; + if (*current == '+' || *current == '-') { + sign = static_cast(*current); + ++current; + if (current == end) { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + } + + if (current == end || *current < '0' || *current > '9') { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + + const int max_exponent = INT_MAX / 2; + ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); + int num = 0; + do { + // Check overflow. + int digit = *current - '0'; + if (num >= max_exponent / 10 + && !(num == max_exponent / 10 && digit <= max_exponent % 10)) { + num = max_exponent; + } else { + num = num * 10 + digit; + } + ++current; + } while (current != end && *current >= '0' && *current <= '9'); + + exponent += (sign == '-' ? -num : num); + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + if (allow_trailing_spaces) { + AdvanceToNonspace(¤t, end); + } + + parsing_done: + exponent += insignificant_digits; + + if (octal) { + double result; + const char* tail_pointer = NULL; + result = RadixStringToIeee<3>(buffer, + buffer + buffer_pos, + sign, + allow_trailing_junk, + junk_string_value_, + read_as_double, + &tail_pointer); + ASSERT(tail_pointer != NULL); + *processed_characters_count = current - input; + return result; + } + + if (nonzero_digit_dropped) { + buffer[buffer_pos++] = '1'; + exponent--; + } + + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos] = '\0'; + + double converted; + if (read_as_double) { + converted = Strtod(Vector(buffer, buffer_pos), exponent); + } else { + converted = Strtof(Vector(buffer, buffer_pos), exponent); + } + *processed_characters_count = current - input; + return sign? -converted: converted; +} + +} // namespace double_conversion diff --git a/3rdparty/double-conversion/double-conversion.h b/3rdparty/double-conversion/double-conversion.h new file mode 100644 index 0000000000..f98edae75a --- /dev/null +++ b/3rdparty/double-conversion/double-conversion.h @@ -0,0 +1,536 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ +#define DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ + +#include "utils.h" + +namespace double_conversion { + +class DoubleToStringConverter { + public: + // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint + // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the + // function returns false. + static const int kMaxFixedDigitsBeforePoint = 60; + static const int kMaxFixedDigitsAfterPoint = 60; + + // When calling ToExponential with a requested_digits + // parameter > kMaxExponentialDigits then the function returns false. + static const int kMaxExponentialDigits = 120; + + // When calling ToPrecision with a requested_digits + // parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits + // then the function returns false. + static const int kMinPrecisionDigits = 1; + static const int kMaxPrecisionDigits = 120; + + enum Flags { + NO_FLAGS = 0, + EMIT_POSITIVE_EXPONENT_SIGN = 1, + EMIT_TRAILING_DECIMAL_POINT = 2, + EMIT_TRAILING_ZERO_AFTER_POINT = 4, + UNIQUE_ZERO = 8 + }; + + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent + // form, emits a '+' for positive exponents. Example: 1.2e+2. + // - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is + // converted into decimal format then a trailing decimal point is appended. + // Example: 2345.0 is converted to "2345.". + // - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point + // emits a trailing '0'-character. This flag requires the + // EXMIT_TRAILING_DECIMAL_POINT flag. + // Example: 2345.0 is converted to "2345.0". + // - UNIQUE_ZERO: "-0.0" is converted to "0.0". + // + // Infinity symbol and nan_symbol provide the string representation for these + // special values. If the string is NULL and the special value is encountered + // then the conversion functions return false. + // + // The exponent_character is used in exponential representations. It is + // usually 'e' or 'E'. + // + // When converting to the shortest representation the converter will + // represent input numbers in decimal format if they are in the interval + // [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[ + // (lower boundary included, greater boundary excluded). + // Example: with decimal_in_shortest_low = -6 and + // decimal_in_shortest_high = 21: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // When converting to precision mode the converter may add + // max_leading_padding_zeroes before returning the number in exponential + // format. + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarily the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + DoubleToStringConverter(int flags, + const char* infinity_symbol, + const char* nan_symbol, + char exponent_character, + int decimal_in_shortest_low, + int decimal_in_shortest_high, + int max_leading_padding_zeroes_in_precision_mode, + int max_trailing_padding_zeroes_in_precision_mode) + : flags_(flags), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol), + exponent_character_(exponent_character), + decimal_in_shortest_low_(decimal_in_shortest_low), + decimal_in_shortest_high_(decimal_in_shortest_high), + max_leading_padding_zeroes_in_precision_mode_( + max_leading_padding_zeroes_in_precision_mode), + max_trailing_padding_zeroes_in_precision_mode_( + max_trailing_padding_zeroes_in_precision_mode) { + // When 'trailing zero after the point' is set, then 'trailing point' + // must be set too. + ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) || + !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0)); + } + + // Returns a converter following the EcmaScript specification. + static const DoubleToStringConverter& EcmaScriptConverter(); + + // Computes the shortest string of digits that correctly represent the input + // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high + // (see constructor) it then either returns a decimal representation, or an + // exponential representation. + // Example with decimal_in_shortest_low = -6, + // decimal_in_shortest_high = 21, + // EMIT_POSITIVE_EXPONENT_SIGN activated, and + // EMIT_TRAILING_DECIMAL_POINT deactived: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // Note: the conversion may round the output if the returned string + // is accurate enough to uniquely identify the input-number. + // For example the most precise representation of the double 9e59 equals + // "899999999999999918767229449717619953810131273674690656206848", but + // the converter will return the shorter (but still correct) "9e59". + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except when the input value is special and no infinity_symbol or + // nan_symbol has been given to the constructor. + bool ToShortest(double value, StringBuilder* result_builder) const { + return ToShortestIeeeNumber(value, result_builder, SHORTEST); + } + + // Same as ToShortest, but for single-precision floats. + bool ToShortestSingle(float value, StringBuilder* result_builder) const { + return ToShortestIeeeNumber(value, result_builder, SHORTEST_SINGLE); + } + + + // Computes a decimal representation with a fixed number of digits after the + // decimal point. The last emitted digit is rounded. + // + // Examples: + // ToFixed(3.12, 1) -> "3.1" + // ToFixed(3.1415, 3) -> "3.142" + // ToFixed(1234.56789, 4) -> "1234.5679" + // ToFixed(1.23, 5) -> "1.23000" + // ToFixed(0.1, 4) -> "0.1000" + // ToFixed(1e30, 2) -> "1000000000000000019884624838656.00" + // ToFixed(0.1, 30) -> "0.100000000000000005551115123126" + // ToFixed(0.1, 17) -> "0.10000000000000001" + // + // If requested_digits equals 0, then the tail of the result depends on + // the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples, for requested_digits == 0, + // let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be + // - false and false: then 123.45 -> 123 + // 0.678 -> 1 + // - true and false: then 123.45 -> 123. + // 0.678 -> 1. + // - true and true: then 123.45 -> 123.0 + // 0.678 -> 1.0 + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'value' > 10^kMaxFixedDigitsBeforePoint, or + // - 'requested_digits' > kMaxFixedDigitsAfterPoint. + // The last two conditions imply that the result will never contain more than + // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters + // (one additional character for the sign, and one for the decimal point). + bool ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const; + + // Computes a representation in exponential format with requested_digits + // after the decimal point. The last emitted digit is rounded. + // If requested_digits equals -1, then the shortest exponential representation + // is computed. + // + // Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and + // exponent_character set to 'e'. + // ToExponential(3.12, 1) -> "3.1e0" + // ToExponential(5.0, 3) -> "5.000e0" + // ToExponential(0.001, 2) -> "1.00e-3" + // ToExponential(3.1415, -1) -> "3.1415e0" + // ToExponential(3.1415, 4) -> "3.1415e0" + // ToExponential(3.1415, 3) -> "3.142e0" + // ToExponential(123456789000000, 3) -> "1.235e14" + // ToExponential(1000000000000000019884624838656.0, -1) -> "1e30" + // ToExponential(1000000000000000019884624838656.0, 32) -> + // "1.00000000000000001988462483865600e30" + // ToExponential(1234, 0) -> "1e3" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'requested_digits' > kMaxExponentialDigits. + // The last condition implies that the result will never contain more than + // kMaxExponentialDigits + 8 characters (the sign, the digit before the + // decimal point, the decimal point, the exponent character, the + // exponent's sign, and at most 3 exponent digits). + bool ToExponential(double value, + int requested_digits, + StringBuilder* result_builder) const; + + // Computes 'precision' leading digits of the given 'value' and returns them + // either in exponential or decimal format, depending on + // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the + // constructor). + // The last computed digit is rounded. + // + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarily the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no + // EMIT_TRAILING_ZERO_AFTER_POINT: + // ToPrecision(123450.0, 6) -> "123450" + // ToPrecision(123450.0, 5) -> "123450" + // ToPrecision(123450.0, 4) -> "123500" + // ToPrecision(123450.0, 3) -> "123000" + // ToPrecision(123450.0, 2) -> "1.2e5" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - precision < kMinPericisionDigits + // - precision > kMaxPrecisionDigits + // The last condition implies that the result will never contain more than + // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the + // exponent character, the exponent's sign, and at most 3 exponent digits). + bool ToPrecision(double value, + int precision, + StringBuilder* result_builder) const; + + enum DtoaMode { + // Produce the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate + // but correct) 0.3. + SHORTEST, + // Same as SHORTEST, but for single-precision floats. + SHORTEST_SINGLE, + // Produce a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + FIXED, + // Fixed number of digits (independent of the decimal point). + PRECISION + }; + + // The maximal number of digits that are needed to emit a double in base 10. + // A higher precision can be achieved by using more digits, but the shortest + // accurate representation of any double will never use more digits than + // kBase10MaximalLength. + // Note that DoubleToAscii null-terminates its input. So the given buffer + // should be at least kBase10MaximalLength + 1 characters long. + static const int kBase10MaximalLength = 17; + + // Converts the given double 'v' to ascii. 'v' must not be NaN, +Infinity, or + // -Infinity. In SHORTEST_SINGLE-mode this restriction also applies to 'v' + // after it has been casted to a single-precision float. That is, in this + // mode static_cast(v) must not be NaN, +Infinity or -Infinity. + // + // The result should be interpreted as buffer * 10^(point-length). + // + // The output depends on the given mode: + // - SHORTEST: produce the least amount of digits for which the internal + // identity requirement is still satisfied. If the digits are printed + // (together with the correct exponent) then reading this number will give + // 'v' again. The buffer will choose the representation that is closest to + // 'v'. If there are two at the same distance, than the one farther away + // from 0 is chosen (halfway cases - ending with 5 - are rounded up). + // In this mode the 'requested_digits' parameter is ignored. + // - SHORTEST_SINGLE: same as SHORTEST but with single-precision. + // - FIXED: produces digits necessary to print a given number with + // 'requested_digits' digits after the decimal point. The produced digits + // might be too short in which case the caller has to fill the remainder + // with '0's. + // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. + // Halfway cases are rounded towards +/-Infinity (away from 0). The call + // toFixed(0.15, 2) thus returns buffer="2", point=0. + // The returned buffer may contain digits that would be truncated from the + // shortest representation of the input. + // - PRECISION: produces 'requested_digits' where the first digit is not '0'. + // Even though the length of produced digits usually equals + // 'requested_digits', the function is allowed to return fewer digits, in + // which case the caller has to fill the missing digits with '0's. + // Halfway cases are again rounded away from 0. + // DoubleToAscii expects the given buffer to be big enough to hold all + // digits and a terminating null-character. In SHORTEST-mode it expects a + // buffer of at least kBase10MaximalLength + 1. In all other modes the + // requested_digits parameter and the padding-zeroes limit the size of the + // output. Don't forget the decimal point, the exponent character and the + // terminating null-character when computing the maximal output size. + // The given length is only used in debug mode to ensure the buffer is big + // enough. + static void DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point); + + private: + // Implementation for ToShortest and ToShortestSingle. + bool ToShortestIeeeNumber(double value, + StringBuilder* result_builder, + DtoaMode mode) const; + + // If the value is a special value (NaN or Infinity) constructs the + // corresponding string using the configured infinity/nan-symbol. + // If either of them is NULL or the value is not special then the + // function returns false. + bool HandleSpecialValues(double value, StringBuilder* result_builder) const; + // Constructs an exponential representation (i.e. 1.234e56). + // The given exponent assumes a decimal point after the first decimal digit. + void CreateExponentialRepresentation(const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const; + // Creates a decimal representation (i.e 1234.5678). + void CreateDecimalRepresentation(const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const; + + const int flags_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + const char exponent_character_; + const int decimal_in_shortest_low_; + const int decimal_in_shortest_high_; + const int max_leading_padding_zeroes_in_precision_mode_; + const int max_trailing_padding_zeroes_in_precision_mode_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter); +}; + + +class StringToDoubleConverter { + public: + // Enumeration for allowing octals and ignoring junk when converting + // strings to numbers. + enum Flags { + NO_FLAGS = 0, + ALLOW_HEX = 1, + ALLOW_OCTALS = 2, + ALLOW_TRAILING_JUNK = 4, + ALLOW_LEADING_SPACES = 8, + ALLOW_TRAILING_SPACES = 16, + ALLOW_SPACES_AFTER_SIGN = 32 + }; + + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - ALLOW_HEX: recognizes the prefix "0x". Hex numbers may only be integers. + // Ex: StringToDouble("0x1234") -> 4660.0 + // In StringToDouble("0x1234.56") the characters ".56" are trailing + // junk. The result of the call is hence dependent on + // the ALLOW_TRAILING_JUNK flag and/or the junk value. + // With this flag "0x" is a junk-string. Even with ALLOW_TRAILING_JUNK, + // the string will not be parsed as "0" followed by junk. + // + // - ALLOW_OCTALS: recognizes the prefix "0" for octals: + // If a sequence of octal digits starts with '0', then the number is + // read as octal integer. Octal numbers may only be integers. + // Ex: StringToDouble("01234") -> 668.0 + // StringToDouble("012349") -> 12349.0 // Not a sequence of octal + // // digits. + // In StringToDouble("01234.56") the characters ".56" are trailing + // junk. The result of the call is hence dependent on + // the ALLOW_TRAILING_JUNK flag and/or the junk value. + // In StringToDouble("01234e56") the characters "e56" are trailing + // junk, too. + // - ALLOW_TRAILING_JUNK: ignore trailing characters that are not part of + // a double literal. + // - ALLOW_LEADING_SPACES: skip over leading spaces. + // - ALLOW_TRAILING_SPACES: ignore trailing spaces. + // - ALLOW_SPACES_AFTER_SIGN: ignore spaces after the sign. + // Ex: StringToDouble("- 123.2") -> -123.2. + // StringToDouble("+ 123.2") -> 123.2 + // + // empty_string_value is returned when an empty string is given as input. + // If ALLOW_LEADING_SPACES or ALLOW_TRAILING_SPACES are set, then a string + // containing only spaces is converted to the 'empty_string_value', too. + // + // junk_string_value is returned when + // a) ALLOW_TRAILING_JUNK is not set, and a junk character (a character not + // part of a double-literal) is found. + // b) ALLOW_TRAILING_JUNK is set, but the string does not start with a + // double literal. + // + // infinity_symbol and nan_symbol are strings that are used to detect + // inputs that represent infinity and NaN. They can be null, in which case + // they are ignored. + // The conversion routine first reads any possible signs. Then it compares the + // following character of the input-string with the first character of + // the infinity, and nan-symbol. If either matches, the function assumes, that + // a match has been found, and expects the following input characters to match + // the remaining characters of the special-value symbol. + // This means that the following restrictions apply to special-value symbols: + // - they must not start with signs ('+', or '-'), + // - they must not have the same first character. + // - they must not start with digits. + // + // Examples: + // flags = ALLOW_HEX | ALLOW_TRAILING_JUNK, + // empty_string_value = 0.0, + // junk_string_value = NaN, + // infinity_symbol = "infinity", + // nan_symbol = "nan": + // StringToDouble("0x1234") -> 4660.0. + // StringToDouble("0x1234K") -> 4660.0. + // StringToDouble("") -> 0.0 // empty_string_value. + // StringToDouble(" ") -> NaN // junk_string_value. + // StringToDouble(" 1") -> NaN // junk_string_value. + // StringToDouble("0x") -> NaN // junk_string_value. + // StringToDouble("-123.45") -> -123.45. + // StringToDouble("--123.45") -> NaN // junk_string_value. + // StringToDouble("123e45") -> 123e45. + // StringToDouble("123E45") -> 123e45. + // StringToDouble("123e+45") -> 123e45. + // StringToDouble("123E-45") -> 123e-45. + // StringToDouble("123e") -> 123.0 // trailing junk ignored. + // StringToDouble("123e-") -> 123.0 // trailing junk ignored. + // StringToDouble("+NaN") -> NaN // NaN string literal. + // StringToDouble("-infinity") -> -inf. // infinity literal. + // StringToDouble("Infinity") -> NaN // junk_string_value. + // + // flags = ALLOW_OCTAL | ALLOW_LEADING_SPACES, + // empty_string_value = 0.0, + // junk_string_value = NaN, + // infinity_symbol = NULL, + // nan_symbol = NULL: + // StringToDouble("0x1234") -> NaN // junk_string_value. + // StringToDouble("01234") -> 668.0. + // StringToDouble("") -> 0.0 // empty_string_value. + // StringToDouble(" ") -> 0.0 // empty_string_value. + // StringToDouble(" 1") -> 1.0 + // StringToDouble("0x") -> NaN // junk_string_value. + // StringToDouble("0123e45") -> NaN // junk_string_value. + // StringToDouble("01239E45") -> 1239e45. + // StringToDouble("-infinity") -> NaN // junk_string_value. + // StringToDouble("NaN") -> NaN // junk_string_value. + StringToDoubleConverter(int flags, + double empty_string_value, + double junk_string_value, + const char* infinity_symbol, + const char* nan_symbol) + : flags_(flags), + empty_string_value_(empty_string_value), + junk_string_value_(junk_string_value), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol) { + } + + // Performs the conversion. + // The output parameter 'processed_characters_count' is set to the number + // of characters that have been processed to read the number. + // Spaces than are processed with ALLOW_{LEADING|TRAILING}_SPACES are included + // in the 'processed_characters_count'. Trailing junk is never included. + double StringToDouble(const char* buffer, + int length, + int* processed_characters_count) { + return StringToIeee(buffer, length, processed_characters_count, true); + } + + // Same as StringToDouble but reads a float. + // Note that this is not equivalent to static_cast(StringToDouble(...)) + // due to potential double-rounding. + float StringToFloat(const char* buffer, + int length, + int* processed_characters_count) { + return static_cast(StringToIeee(buffer, length, + processed_characters_count, false)); + } + + private: + const int flags_; + const double empty_string_value_; + const double junk_string_value_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + + double StringToIeee(const char* buffer, + int length, + int* processed_characters_count, + bool read_as_double); + + DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ diff --git a/3rdparty/double-conversion/double-conversion.pri b/3rdparty/double-conversion/double-conversion.pri new file mode 100644 index 0000000000..9a9a2b89d8 --- /dev/null +++ b/3rdparty/double-conversion/double-conversion.pri @@ -0,0 +1,3 @@ +VPATH += $$PWD +SOURCES += $$PWD/*.cc +HEADERS += $$PWD/*.h diff --git a/3rdparty/double-conversion/fast-dtoa.cc b/3rdparty/double-conversion/fast-dtoa.cc new file mode 100644 index 0000000000..1a0f823509 --- /dev/null +++ b/3rdparty/double-conversion/fast-dtoa.cc @@ -0,0 +1,664 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "fast-dtoa.h" + +#include "cached-powers.h" +#include "diy-fp.h" +#include "ieee.h" + +namespace double_conversion { + +// The minimal and maximal target exponent define the range of w's binary +// exponent, where 'w' is the result of multiplying the input by a cached power +// of ten. +// +// A different range might be chosen on a different platform, to optimize digit +// generation, but a smaller range requires more powers of ten to be cached. +static const int kMinimalTargetExponent = -60; +static const int kMaximalTargetExponent = -32; + + +// Adjusts the last digit of the generated number, and screens out generated +// solutions that may be inaccurate. A solution may be inaccurate if it is +// outside the safe interval, or if we cannot prove that it is closer to the +// input than a neighboring representation of the same length. +// +// Input: * buffer containing the digits of too_high / 10^kappa +// * the buffer's length +// * distance_too_high_w == (too_high - w).f() * unit +// * unsafe_interval == (too_high - too_low).f() * unit +// * rest = (too_high - buffer * 10^kappa).f() * unit +// * ten_kappa = 10^kappa * unit +// * unit = the common multiplier +// Output: returns true if the buffer is guaranteed to contain the closest +// representable number to the input. +// Modifies the generated digits in the buffer to approach (round towards) w. +static bool RoundWeed(Vector buffer, + int length, + uint64_t distance_too_high_w, + uint64_t unsafe_interval, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit) { + uint64_t small_distance = distance_too_high_w - unit; + uint64_t big_distance = distance_too_high_w + unit; + // Let w_low = too_high - big_distance, and + // w_high = too_high - small_distance. + // Note: w_low < w < w_high + // + // The real w (* unit) must lie somewhere inside the interval + // ]w_low; w_high[ (often written as "(w_low; w_high)") + + // Basically the buffer currently contains a number in the unsafe interval + // ]too_low; too_high[ with too_low < w < too_high + // + // too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // ^v 1 unit ^ ^ ^ ^ + // boundary_high --------------------- . . . . + // ^v 1 unit . . . . + // - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . . + // . . ^ . . + // . big_distance . . . + // . . . . rest + // small_distance . . . . + // v . . . . + // w_high - - - - - - - - - - - - - - - - - - . . . . + // ^v 1 unit . . . . + // w ---------------------------------------- . . . . + // ^v 1 unit v . . . + // w_low - - - - - - - - - - - - - - - - - - - - - . . . + // . . v + // buffer --------------------------------------------------+-------+-------- + // . . + // safe_interval . + // v . + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + // ^v 1 unit . + // boundary_low ------------------------- unsafe_interval + // ^v 1 unit v + // too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // + // + // Note that the value of buffer could lie anywhere inside the range too_low + // to too_high. + // + // boundary_low, boundary_high and w are approximations of the real boundaries + // and v (the input number). They are guaranteed to be precise up to one unit. + // In fact the error is guaranteed to be strictly less than one unit. + // + // Anything that lies outside the unsafe interval is guaranteed not to round + // to v when read again. + // Anything that lies inside the safe interval is guaranteed to round to v + // when read again. + // If the number inside the buffer lies inside the unsafe interval but not + // inside the safe interval then we simply do not know and bail out (returning + // false). + // + // Similarly we have to take into account the imprecision of 'w' when finding + // the closest representation of 'w'. If we have two potential + // representations, and one is closer to both w_low and w_high, then we know + // it is closer to the actual value v. + // + // By generating the digits of too_high we got the largest (closest to + // too_high) buffer that is still in the unsafe interval. In the case where + // w_high < buffer < too_high we try to decrement the buffer. + // This way the buffer approaches (rounds towards) w. + // There are 3 conditions that stop the decrementation process: + // 1) the buffer is already below w_high + // 2) decrementing the buffer would make it leave the unsafe interval + // 3) decrementing the buffer would yield a number below w_high and farther + // away than the current number. In other words: + // (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high + // Instead of using the buffer directly we use its distance to too_high. + // Conceptually rest ~= too_high - buffer + // We need to do the following tests in this order to avoid over- and + // underflows. + ASSERT(rest <= unsafe_interval); + while (rest < small_distance && // Negated condition 1 + unsafe_interval - rest >= ten_kappa && // Negated condition 2 + (rest + ten_kappa < small_distance || // buffer{-1} > w_high + small_distance - rest >= rest + ten_kappa - small_distance)) { + buffer[length - 1]--; + rest += ten_kappa; + } + + // We have approached w+ as much as possible. We now test if approaching w- + // would require changing the buffer. If yes, then we have two possible + // representations close to w, but we cannot decide which one is closer. + if (rest < big_distance && + unsafe_interval - rest >= ten_kappa && + (rest + ten_kappa < big_distance || + big_distance - rest > rest + ten_kappa - big_distance)) { + return false; + } + + // Weeding test. + // The safe interval is [too_low + 2 ulp; too_high - 2 ulp] + // Since too_low = too_high - unsafe_interval this is equivalent to + // [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp] + // Conceptually we have: rest ~= too_high - buffer + return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit); +} + + +// Rounds the buffer upwards if the result is closer to v by possibly adding +// 1 to the buffer. If the precision of the calculation is not sufficient to +// round correctly, return false. +// The rounding might shift the whole buffer in which case the kappa is +// adjusted. For example "99", kappa = 3 might become "10", kappa = 4. +// +// If 2*rest > ten_kappa then the buffer needs to be round up. +// rest can have an error of +/- 1 unit. This function accounts for the +// imprecision and returns false, if the rounding direction cannot be +// unambiguously determined. +// +// Precondition: rest < ten_kappa. +static bool RoundWeedCounted(Vector buffer, + int length, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit, + int* kappa) { + ASSERT(rest < ten_kappa); + // The following tests are done in a specific order to avoid overflows. They + // will work correctly with any uint64 values of rest < ten_kappa and unit. + // + // If the unit is too big, then we don't know which way to round. For example + // a unit of 50 means that the real number lies within rest +/- 50. If + // 10^kappa == 40 then there is no way to tell which way to round. + if (unit >= ten_kappa) return false; + // Even if unit is just half the size of 10^kappa we are already completely + // lost. (And after the previous test we know that the expression will not + // over/underflow.) + if (ten_kappa - unit <= unit) return false; + // If 2 * (rest + unit) <= 10^kappa we can safely round down. + if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) { + return true; + } + // If 2 * (rest - unit) >= 10^kappa, then we can safely round up. + if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) { + // Increment the last digit recursively until we find a non '9' digit. + buffer[length - 1]++; + for (int i = length - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + // If the first digit is now '0'+ 10 we had a buffer with all '9's. With the + // exception of the first digit all digits are now '0'. Simply switch the + // first digit to '1' and adjust the kappa. Example: "99" becomes "10" and + // the power (the kappa) is increased. + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*kappa) += 1; + } + return true; + } + return false; +} + +// Returns the biggest power of ten that is less than or equal to the given +// number. We furthermore receive the maximum number of bits 'number' has. +// +// Returns power == 10^(exponent_plus_one-1) such that +// power <= number < power * 10. +// If number_bits == 0 then 0^(0-1) is returned. +// The number of bits must be <= 32. +// Precondition: number < (1 << (number_bits + 1)). + +// Inspired by the method for finding an integer log base 10 from here: +// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 +static unsigned int const kSmallPowersOfTen[] = + {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, + 1000000000}; + +static void BiggestPowerTen(uint32_t number, + int number_bits, + uint32_t* power, + int* exponent_plus_one) { + ASSERT(number < (1u << (number_bits + 1))); + // 1233/4096 is approximately 1/lg(10). + int exponent_plus_one_guess = ((number_bits + 1) * 1233 >> 12); + // We increment to skip over the first entry in the kPowersOf10 table. + // Note: kPowersOf10[i] == 10^(i-1). + exponent_plus_one_guess++; + // We don't have any guarantees that 2^number_bits <= number. + // TODO(floitsch): can we change the 'while' into an 'if'? We definitely see + // number < (2^number_bits - 1), but I haven't encountered + // number < (2^number_bits - 2) yet. + while (number < kSmallPowersOfTen[exponent_plus_one_guess]) { + exponent_plus_one_guess--; + } + *power = kSmallPowersOfTen[exponent_plus_one_guess]; + *exponent_plus_one = exponent_plus_one_guess; +} + +// Generates the digits of input number w. +// w is a floating-point number (DiyFp), consisting of a significand and an +// exponent. Its exponent is bounded by kMinimalTargetExponent and +// kMaximalTargetExponent. +// Hence -60 <= w.e() <= -32. +// +// Returns false if it fails, in which case the generated digits in the buffer +// should not be used. +// Preconditions: +// * low, w and high are correct up to 1 ulp (unit in the last place). That +// is, their error must be less than a unit of their last digits. +// * low.e() == w.e() == high.e() +// * low < w < high, and taking into account their error: low~ <= high~ +// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent +// Postconditions: returns false if procedure fails. +// otherwise: +// * buffer is not null-terminated, but len contains the number of digits. +// * buffer contains the shortest possible decimal digit-sequence +// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the +// correct values of low and high (without their error). +// * if more than one decimal representation gives the minimal number of +// decimal digits then the one closest to W (where W is the correct value +// of w) is chosen. +// Remark: this procedure takes into account the imprecision of its input +// numbers. If the precision is not enough to guarantee all the postconditions +// then false is returned. This usually happens rarely (~0.5%). +// +// Say, for the sake of example, that +// w.e() == -48, and w.f() == 0x1234567890abcdef +// w's value can be computed by w.f() * 2^w.e() +// We can obtain w's integral digits by simply shifting w.f() by -w.e(). +// -> w's integral part is 0x1234 +// w's fractional part is therefore 0x567890abcdef. +// Printing w's integral part is easy (simply print 0x1234 in decimal). +// In order to print its fraction we repeatedly multiply the fraction by 10 and +// get each digit. Example the first digit after the point would be computed by +// (0x567890abcdef * 10) >> 48. -> 3 +// The whole thing becomes slightly more complicated because we want to stop +// once we have enough digits. That is, once the digits inside the buffer +// represent 'w' we can stop. Everything inside the interval low - high +// represents w. However we have to pay attention to low, high and w's +// imprecision. +static bool DigitGen(DiyFp low, + DiyFp w, + DiyFp high, + Vector buffer, + int* length, + int* kappa) { + ASSERT(low.e() == w.e() && w.e() == high.e()); + ASSERT(low.f() + 1 <= high.f() - 1); + ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + // low, w and high are imprecise, but by less than one ulp (unit in the last + // place). + // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that + // the new numbers are outside of the interval we want the final + // representation to lie in. + // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield + // numbers that are certain to lie in the interval. We will use this fact + // later on. + // We will now start by generating the digits within the uncertain + // interval. Later we will weed out representations that lie outside the safe + // interval and thus _might_ lie outside the correct interval. + uint64_t unit = 1; + DiyFp too_low = DiyFp(low.f() - unit, low.e()); + DiyFp too_high = DiyFp(high.f() + unit, high.e()); + // too_low and too_high are guaranteed to lie outside the interval we want the + // generated number in. + DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low); + // We now cut the input number into two parts: the integral digits and the + // fractionals. We will not write any decimal separator though, but adapt + // kappa instead. + // Reminder: we are currently computing the digits (stored inside the buffer) + // such that: too_low < buffer * 10^kappa < too_high + // We use too_high for the digit_generation and stop as soon as possible. + // If we stop early we effectively round down. + DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast(too_high.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = too_high.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent_plus_one; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + &divisor, &divisor_exponent_plus_one); + *kappa = divisor_exponent_plus_one; + *length = 0; + // Loop invariant: buffer = too_high / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divisor exponent + 1. And the divisor is the biggest power of ten + // that is smaller than integrals. + while (*kappa > 0) { + int digit = integrals / divisor; + buffer[*length] = '0' + digit; + (*length)++; + integrals %= divisor; + (*kappa)--; + // Note that kappa now equals the exponent of the divisor and that the + // invariant thus holds again. + uint64_t rest = + (static_cast(integrals) << -one.e()) + fractionals; + // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e()) + // Reminder: unsafe_interval.e() == one.e() + if (rest < unsafe_interval.f()) { + // Rounding down (by not emitting the remaining digits) yields a number + // that lies within the unsafe interval. + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(), + unsafe_interval.f(), rest, + static_cast(divisor) << -one.e(), unit); + } + divisor /= 10; + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (like the interval or 'unit'), too. + // Note that the multiplication by 10 does not overflow, because w.e >= -60 + // and thus one.e >= -60. + ASSERT(one.e() >= -60); + ASSERT(fractionals < one.f()); + ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + while (true) { + fractionals *= 10; + unit *= 10; + unsafe_interval.set_f(unsafe_interval.f() * 10); + // Integer division by one. + int digit = static_cast(fractionals >> -one.e()); + buffer[*length] = '0' + digit; + (*length)++; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + if (fractionals < unsafe_interval.f()) { + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit, + unsafe_interval.f(), fractionals, one.f(), unit); + } + } +} + + + +// Generates (at most) requested_digits digits of input number w. +// w is a floating-point number (DiyFp), consisting of a significand and an +// exponent. Its exponent is bounded by kMinimalTargetExponent and +// kMaximalTargetExponent. +// Hence -60 <= w.e() <= -32. +// +// Returns false if it fails, in which case the generated digits in the buffer +// should not be used. +// Preconditions: +// * w is correct up to 1 ulp (unit in the last place). That +// is, its error must be strictly less than a unit of its last digit. +// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent +// +// Postconditions: returns false if procedure fails. +// otherwise: +// * buffer is not null-terminated, but length contains the number of +// digits. +// * the representation in buffer is the most precise representation of +// requested_digits digits. +// * buffer contains at most requested_digits digits of w. If there are less +// than requested_digits digits then some trailing '0's have been removed. +// * kappa is such that +// w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2. +// +// Remark: This procedure takes into account the imprecision of its input +// numbers. If the precision is not enough to guarantee all the postconditions +// then false is returned. This usually happens rarely, but the failure-rate +// increases with higher requested_digits. +static bool DigitGenCounted(DiyFp w, + int requested_digits, + Vector buffer, + int* length, + int* kappa) { + ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + ASSERT(kMinimalTargetExponent >= -60); + ASSERT(kMaximalTargetExponent <= -32); + // w is assumed to have an error less than 1 unit. Whenever w is scaled we + // also scale its error. + uint64_t w_error = 1; + // We cut the input number into two parts: the integral digits and the + // fractional digits. We don't emit any decimal separator, but adapt kappa + // instead. Example: instead of writing "1.2" we put "12" into the buffer and + // increase kappa by 1. + DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast(w.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = w.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent_plus_one; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + &divisor, &divisor_exponent_plus_one); + *kappa = divisor_exponent_plus_one; + *length = 0; + + // Loop invariant: buffer = w / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divisor exponent + 1. And the divisor is the biggest power of ten + // that is smaller than 'integrals'. + while (*kappa > 0) { + int digit = integrals / divisor; + buffer[*length] = '0' + digit; + (*length)++; + requested_digits--; + integrals %= divisor; + (*kappa)--; + // Note that kappa now equals the exponent of the divisor and that the + // invariant thus holds again. + if (requested_digits == 0) break; + divisor /= 10; + } + + if (requested_digits == 0) { + uint64_t rest = + (static_cast(integrals) << -one.e()) + fractionals; + return RoundWeedCounted(buffer, *length, rest, + static_cast(divisor) << -one.e(), w_error, + kappa); + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (the 'unit'), too. + // Note that the multiplication by 10 does not overflow, because w.e >= -60 + // and thus one.e >= -60. + ASSERT(one.e() >= -60); + ASSERT(fractionals < one.f()); + ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + while (requested_digits > 0 && fractionals > w_error) { + fractionals *= 10; + w_error *= 10; + // Integer division by one. + int digit = static_cast(fractionals >> -one.e()); + buffer[*length] = '0' + digit; + (*length)++; + requested_digits--; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + } + if (requested_digits != 0) return false; + return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error, + kappa); +} + + +// Provides a decimal representation of v. +// Returns true if it succeeds, otherwise the result cannot be trusted. +// There will be *length digits inside the buffer (not null-terminated). +// If the function returns true then +// v == (double) (buffer * 10^decimal_exponent). +// The digits in the buffer are the shortest representation possible: no +// 0.09999999999999999 instead of 0.1. The shorter representation will even be +// chosen even if the longer one would be closer to v. +// The last digit will be closest to the actual v. That is, even if several +// digits might correctly yield 'v' when read again, the closest will be +// computed. +static bool Grisu3(double v, + FastDtoaMode mode, + Vector buffer, + int* length, + int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + // boundary_minus and boundary_plus are the boundaries between v and its + // closest floating-point neighbors. Any number strictly between + // boundary_minus and boundary_plus will round to v when convert to a double. + // Grisu3 will never output representations that lie exactly on a boundary. + DiyFp boundary_minus, boundary_plus; + if (mode == FAST_DTOA_SHORTEST) { + Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus); + } else { + ASSERT(mode == FAST_DTOA_SHORTEST_SINGLE); + float single_v = static_cast(v); + Single(single_v).NormalizedBoundaries(&boundary_minus, &boundary_plus); + } + ASSERT(boundary_plus.e() == w.e()); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, + ten_mk_maximal_binary_exponent, + &ten_mk, &mk); + ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize)); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + ASSERT(scaled_w.e() == + boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize); + // In theory it would be possible to avoid some recomputations by computing + // the difference between w and boundary_minus/plus (a power of 2) and to + // compute scaled_boundary_minus/plus by subtracting/adding from + // scaled_w. However the code becomes much less readable and the speed + // enhancements are not terriffic. + DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk); + DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk); + + // DigitGen will generate the digits of scaled_w. Therefore we have + // v == (double) (scaled_w * 10^-mk). + // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an + // integer than it will be updated. For instance if scaled_w == 1.23 then + // the buffer will be filled with "123" und the decimal_exponent will be + // decreased by 2. + int kappa; + bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + + +// The "counted" version of grisu3 (see above) only generates requested_digits +// number of digits. This version does not generate the shortest representation, +// and with enough requested digits 0.1 will at some point print as 0.9999999... +// Grisu3 is too imprecise for real halfway cases (1.5 will not work) and +// therefore the rounding strategy for halfway cases is irrelevant. +static bool Grisu3Counted(double v, + int requested_digits, + Vector buffer, + int* length, + int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, + ten_mk_maximal_binary_exponent, + &ten_mk, &mk); + ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize)); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + + // We now have (double) (scaled_w * 10^-mk). + // DigitGen will generate the first requested_digits digits of scaled_w and + // return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It + // will not always be exactly the same since DigitGenCounted only produces a + // limited number of digits.) + int kappa; + bool result = DigitGenCounted(scaled_w, requested_digits, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + + +bool FastDtoa(double v, + FastDtoaMode mode, + int requested_digits, + Vector buffer, + int* length, + int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + + bool result = false; + int decimal_exponent = 0; + switch (mode) { + case FAST_DTOA_SHORTEST: + case FAST_DTOA_SHORTEST_SINGLE: + result = Grisu3(v, mode, buffer, length, &decimal_exponent); + break; + case FAST_DTOA_PRECISION: + result = Grisu3Counted(v, requested_digits, + buffer, length, &decimal_exponent); + break; + default: + UNREACHABLE(); + } + if (result) { + *decimal_point = *length + decimal_exponent; + buffer[*length] = '\0'; + } + return result; +} + +} // namespace double_conversion diff --git a/3rdparty/double-conversion/fast-dtoa.h b/3rdparty/double-conversion/fast-dtoa.h new file mode 100644 index 0000000000..5f1e8eee5e --- /dev/null +++ b/3rdparty/double-conversion/fast-dtoa.h @@ -0,0 +1,88 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_FAST_DTOA_H_ +#define DOUBLE_CONVERSION_FAST_DTOA_H_ + +#include "utils.h" + +namespace double_conversion { + +enum FastDtoaMode { + // Computes the shortest representation of the given input. The returned + // result will be the most accurate number of this length. Longer + // representations might be more accurate. + FAST_DTOA_SHORTEST, + // Same as FAST_DTOA_SHORTEST but for single-precision floats. + FAST_DTOA_SHORTEST_SINGLE, + // Computes a representation where the precision (number of digits) is + // given as input. The precision is independent of the decimal point. + FAST_DTOA_PRECISION +}; + +// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not +// include the terminating '\0' character. +static const int kFastDtoaMaximalLength = 17; +// Same for single-precision numbers. +static const int kFastDtoaMaximalSingleLength = 9; + +// Provides a decimal representation of v. +// The result should be interpreted as buffer * 10^(point - length). +// +// Precondition: +// * v must be a strictly positive finite double. +// +// Returns true if it succeeds, otherwise the result can not be trusted. +// There will be *length digits inside the buffer followed by a null terminator. +// If the function returns true and mode equals +// - FAST_DTOA_SHORTEST, then +// the parameter requested_digits is ignored. +// The result satisfies +// v == (double) (buffer * 10^(point - length)). +// The digits in the buffer are the shortest representation possible. E.g. +// if 0.099999999999 and 0.1 represent the same double then "1" is returned +// with point = 0. +// The last digit will be closest to the actual v. That is, even if several +// digits might correctly yield 'v' when read again, the buffer will contain +// the one closest to v. +// - FAST_DTOA_PRECISION, then +// the buffer contains requested_digits digits. +// the difference v - (buffer * 10^(point-length)) is closest to zero for +// all possible representations of requested_digits digits. +// If there are two values that are equally close, then FastDtoa returns +// false. +// For both modes the buffer must be large enough to hold the result. +bool FastDtoa(double d, + FastDtoaMode mode, + int requested_digits, + Vector buffer, + int* length, + int* decimal_point); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_FAST_DTOA_H_ diff --git a/3rdparty/double-conversion/fixed-dtoa.cc b/3rdparty/double-conversion/fixed-dtoa.cc new file mode 100644 index 0000000000..d56b1449b2 --- /dev/null +++ b/3rdparty/double-conversion/fixed-dtoa.cc @@ -0,0 +1,402 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "fixed-dtoa.h" +#include "ieee.h" + +namespace double_conversion { + +// Represents a 128bit type. This class should be replaced by a native type on +// platforms that support 128bit integers. +class UInt128 { + public: + UInt128() : high_bits_(0), low_bits_(0) { } + UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) { } + + void Multiply(uint32_t multiplicand) { + uint64_t accumulator; + + accumulator = (low_bits_ & kMask32) * multiplicand; + uint32_t part = static_cast(accumulator & kMask32); + accumulator >>= 32; + accumulator = accumulator + (low_bits_ >> 32) * multiplicand; + low_bits_ = (accumulator << 32) + part; + accumulator >>= 32; + accumulator = accumulator + (high_bits_ & kMask32) * multiplicand; + part = static_cast(accumulator & kMask32); + accumulator >>= 32; + accumulator = accumulator + (high_bits_ >> 32) * multiplicand; + high_bits_ = (accumulator << 32) + part; + ASSERT((accumulator >> 32) == 0); + } + + void Shift(int shift_amount) { + ASSERT(-64 <= shift_amount && shift_amount <= 64); + if (shift_amount == 0) { + return; + } else if (shift_amount == -64) { + high_bits_ = low_bits_; + low_bits_ = 0; + } else if (shift_amount == 64) { + low_bits_ = high_bits_; + high_bits_ = 0; + } else if (shift_amount <= 0) { + high_bits_ <<= -shift_amount; + high_bits_ += low_bits_ >> (64 + shift_amount); + low_bits_ <<= -shift_amount; + } else { + low_bits_ >>= shift_amount; + low_bits_ += high_bits_ << (64 - shift_amount); + high_bits_ >>= shift_amount; + } + } + + // Modifies *this to *this MOD (2^power). + // Returns *this DIV (2^power). + int DivModPowerOf2(int power) { + if (power >= 64) { + int result = static_cast(high_bits_ >> (power - 64)); + high_bits_ -= static_cast(result) << (power - 64); + return result; + } else { + uint64_t part_low = low_bits_ >> power; + uint64_t part_high = high_bits_ << (64 - power); + int result = static_cast(part_low + part_high); + high_bits_ = 0; + low_bits_ -= part_low << power; + return result; + } + } + + bool IsZero() const { + return high_bits_ == 0 && low_bits_ == 0; + } + + int BitAt(int position) { + if (position >= 64) { + return static_cast(high_bits_ >> (position - 64)) & 1; + } else { + return static_cast(low_bits_ >> position) & 1; + } + } + + private: + static const uint64_t kMask32 = 0xFFFFFFFF; + // Value == (high_bits_ << 64) + low_bits_ + uint64_t high_bits_; + uint64_t low_bits_; +}; + + +static const int kDoubleSignificandSize = 53; // Includes the hidden bit. + + +static void FillDigits32FixedLength(uint32_t number, int requested_length, + Vector buffer, int* length) { + for (int i = requested_length - 1; i >= 0; --i) { + buffer[(*length) + i] = '0' + number % 10; + number /= 10; + } + *length += requested_length; +} + + +static void FillDigits32(uint32_t number, Vector buffer, int* length) { + int number_length = 0; + // We fill the digits in reverse order and exchange them afterwards. + while (number != 0) { + int digit = number % 10; + number /= 10; + buffer[(*length) + number_length] = '0' + digit; + number_length++; + } + // Exchange the digits. + int i = *length; + int j = *length + number_length - 1; + while (i < j) { + char tmp = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = tmp; + i++; + j--; + } + *length += number_length; +} + + +static void FillDigits64FixedLength(uint64_t number, int requested_length, + Vector buffer, int* length) { + const uint32_t kTen7 = 10000000; + // For efficiency cut the number into 3 uint32_t parts, and print those. + uint32_t part2 = static_cast(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast(number % kTen7); + uint32_t part0 = static_cast(number / kTen7); + + FillDigits32FixedLength(part0, 3, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); +} + + +static void FillDigits64(uint64_t number, Vector buffer, int* length) { + const uint32_t kTen7 = 10000000; + // For efficiency cut the number into 3 uint32_t parts, and print those. + uint32_t part2 = static_cast(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast(number % kTen7); + uint32_t part0 = static_cast(number / kTen7); + + if (part0 != 0) { + FillDigits32(part0, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } else if (part1 != 0) { + FillDigits32(part1, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } else { + FillDigits32(part2, buffer, length); + } +} + + +static void RoundUp(Vector buffer, int* length, int* decimal_point) { + // An empty buffer represents 0. + if (*length == 0) { + buffer[0] = '1'; + *decimal_point = 1; + *length = 1; + return; + } + // Round the last digit until we either have a digit that was not '9' or until + // we reached the first digit. + buffer[(*length) - 1]++; + for (int i = (*length) - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) { + return; + } + buffer[i] = '0'; + buffer[i - 1]++; + } + // If the first digit is now '0' + 10, we would need to set it to '0' and add + // a '1' in front. However we reach the first digit only if all following + // digits had been '9' before rounding up. Now all trailing digits are '0' and + // we simply switch the first digit to '1' and update the decimal-point + // (indicating that the point is now one digit to the right). + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*decimal_point)++; + } +} + + +// The given fractionals number represents a fixed-point number with binary +// point at bit (-exponent). +// Preconditions: +// -128 <= exponent <= 0. +// 0 <= fractionals * 2^exponent < 1 +// The buffer holds the result. +// The function will round its result. During the rounding-process digits not +// generated by this function might be updated, and the decimal-point variable +// might be updated. If this function generates the digits 99 and the buffer +// already contained "199" (thus yielding a buffer of "19999") then a +// rounding-up will change the contents of the buffer to "20000". +static void FillFractionals(uint64_t fractionals, int exponent, + int fractional_count, Vector buffer, + int* length, int* decimal_point) { + ASSERT(-128 <= exponent && exponent <= 0); + // 'fractionals' is a fixed-point number, with binary point at bit + // (-exponent). Inside the function the non-converted remainder of fractionals + // is a fixed-point number, with binary point at bit 'point'. + if (-exponent <= 64) { + // One 64 bit number is sufficient. + ASSERT(fractionals >> 56 == 0); + int point = -exponent; + for (int i = 0; i < fractional_count; ++i) { + if (fractionals == 0) break; + // Instead of multiplying by 10 we multiply by 5 and adjust the point + // location. This way the fractionals variable will not overflow. + // Invariant at the beginning of the loop: fractionals < 2^point. + // Initially we have: point <= 64 and fractionals < 2^56 + // After each iteration the point is decremented by one. + // Note that 5^3 = 125 < 128 = 2^7. + // Therefore three iterations of this loop will not overflow fractionals + // (even without the subtraction at the end of the loop body). At this + // time point will satisfy point <= 61 and therefore fractionals < 2^point + // and any further multiplication of fractionals by 5 will not overflow. + fractionals *= 5; + point--; + int digit = static_cast(fractionals >> point); + buffer[*length] = '0' + digit; + (*length)++; + fractionals -= static_cast(digit) << point; + } + // If the first bit after the point is set we have to round up. + if (((fractionals >> (point - 1)) & 1) == 1) { + RoundUp(buffer, length, decimal_point); + } + } else { // We need 128 bits. + ASSERT(64 < -exponent && -exponent <= 128); + UInt128 fractionals128 = UInt128(fractionals, 0); + fractionals128.Shift(-exponent - 64); + int point = 128; + for (int i = 0; i < fractional_count; ++i) { + if (fractionals128.IsZero()) break; + // As before: instead of multiplying by 10 we multiply by 5 and adjust the + // point location. + // This multiplication will not overflow for the same reasons as before. + fractionals128.Multiply(5); + point--; + int digit = fractionals128.DivModPowerOf2(point); + buffer[*length] = '0' + digit; + (*length)++; + } + if (fractionals128.BitAt(point - 1) == 1) { + RoundUp(buffer, length, decimal_point); + } + } +} + + +// Removes leading and trailing zeros. +// If leading zeros are removed then the decimal point position is adjusted. +static void TrimZeros(Vector buffer, int* length, int* decimal_point) { + while (*length > 0 && buffer[(*length) - 1] == '0') { + (*length)--; + } + int first_non_zero = 0; + while (first_non_zero < *length && buffer[first_non_zero] == '0') { + first_non_zero++; + } + if (first_non_zero != 0) { + for (int i = first_non_zero; i < *length; ++i) { + buffer[i - first_non_zero] = buffer[i]; + } + *length -= first_non_zero; + *decimal_point -= first_non_zero; + } +} + + +bool FastFixedDtoa(double v, + int fractional_count, + Vector buffer, + int* length, + int* decimal_point) { + const uint32_t kMaxUInt32 = 0xFFFFFFFF; + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // v = significand * 2^exponent (with significand a 53bit integer). + // If the exponent is larger than 20 (i.e. we may have a 73bit number) then we + // don't know how to compute the representation. 2^73 ~= 9.5*10^21. + // If necessary this limit could probably be increased, but we don't need + // more. + if (exponent > 20) return false; + if (fractional_count > 20) return false; + *length = 0; + // At most kDoubleSignificandSize bits of the significand are non-zero. + // Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero + // bits: 0..11*..0xxx..53*..xx + if (exponent + kDoubleSignificandSize > 64) { + // The exponent must be > 11. + // + // We know that v = significand * 2^exponent. + // And the exponent > 11. + // We simplify the task by dividing v by 10^17. + // The quotient delivers the first digits, and the remainder fits into a 64 + // bit number. + // Dividing by 10^17 is equivalent to dividing by 5^17*2^17. + const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17 + uint64_t divisor = kFive17; + int divisor_power = 17; + uint64_t dividend = significand; + uint32_t quotient; + uint64_t remainder; + // Let v = f * 2^e with f == significand and e == exponent. + // Then need q (quotient) and r (remainder) as follows: + // v = q * 10^17 + r + // f * 2^e = q * 10^17 + r + // f * 2^e = q * 5^17 * 2^17 + r + // If e > 17 then + // f * 2^(e-17) = q * 5^17 + r/2^17 + // else + // f = q * 5^17 * 2^(17-e) + r/2^e + if (exponent > divisor_power) { + // We only allow exponents of up to 20 and therefore (17 - e) <= 3 + dividend <<= exponent - divisor_power; + quotient = static_cast(dividend / divisor); + remainder = (dividend % divisor) << divisor_power; + } else { + divisor <<= divisor_power - exponent; + quotient = static_cast(dividend / divisor); + remainder = (dividend % divisor) << exponent; + } + FillDigits32(quotient, buffer, length); + FillDigits64FixedLength(remainder, divisor_power, buffer, length); + *decimal_point = *length; + } else if (exponent >= 0) { + // 0 <= exponent <= 11 + significand <<= exponent; + FillDigits64(significand, buffer, length); + *decimal_point = *length; + } else if (exponent > -kDoubleSignificandSize) { + // We have to cut the number. + uint64_t integrals = significand >> -exponent; + uint64_t fractionals = significand - (integrals << -exponent); + if (integrals > kMaxUInt32) { + FillDigits64(integrals, buffer, length); + } else { + FillDigits32(static_cast(integrals), buffer, length); + } + *decimal_point = *length; + FillFractionals(fractionals, exponent, fractional_count, + buffer, length, decimal_point); + } else if (exponent < -128) { + // This configuration (with at most 20 digits) means that all digits must be + // 0. + ASSERT(fractional_count <= 20); + buffer[0] = '\0'; + *length = 0; + *decimal_point = -fractional_count; + } else { + *decimal_point = 0; + FillFractionals(significand, exponent, fractional_count, + buffer, length, decimal_point); + } + TrimZeros(buffer, length, decimal_point); + buffer[*length] = '\0'; + if ((*length) == 0) { + // The string is empty and the decimal_point thus has no importance. Mimick + // Gay's dtoa and and set it to -fractional_count. + *decimal_point = -fractional_count; + } + return true; +} + +} // namespace double_conversion diff --git a/3rdparty/double-conversion/fixed-dtoa.h b/3rdparty/double-conversion/fixed-dtoa.h new file mode 100644 index 0000000000..3bdd08e21f --- /dev/null +++ b/3rdparty/double-conversion/fixed-dtoa.h @@ -0,0 +1,56 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_FIXED_DTOA_H_ +#define DOUBLE_CONVERSION_FIXED_DTOA_H_ + +#include "utils.h" + +namespace double_conversion { + +// Produces digits necessary to print a given number with +// 'fractional_count' digits after the decimal point. +// The buffer must be big enough to hold the result plus one terminating null +// character. +// +// The produced digits might be too short in which case the caller has to fill +// the gaps with '0's. +// Example: FastFixedDtoa(0.001, 5, ...) is allowed to return buffer = "1", and +// decimal_point = -2. +// Halfway cases are rounded towards +/-Infinity (away from 0). The call +// FastFixedDtoa(0.15, 2, ...) thus returns buffer = "2", decimal_point = 0. +// The returned buffer may contain digits that would be truncated from the +// shortest representation of the input. +// +// This method only works for some parameters. If it can't handle the input it +// returns false. The output is null-terminated when the function succeeds. +bool FastFixedDtoa(double v, int fractional_count, + Vector buffer, int* length, int* decimal_point); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_FIXED_DTOA_H_ diff --git a/3rdparty/double-conversion/ieee.h b/3rdparty/double-conversion/ieee.h new file mode 100644 index 0000000000..839dc47d45 --- /dev/null +++ b/3rdparty/double-conversion/ieee.h @@ -0,0 +1,398 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_DOUBLE_H_ +#define DOUBLE_CONVERSION_DOUBLE_H_ + +#include "diy-fp.h" + +namespace double_conversion { + +// We assume that doubles and uint64_t have the same endianness. +static uint64_t double_to_uint64(double d) { return BitCast(d); } +static double uint64_to_double(uint64_t d64) { return BitCast(d64); } +static uint32_t float_to_uint32(float f) { return BitCast(f); } +static float uint32_to_float(uint32_t d32) { return BitCast(d32); } + +// Helper functions for doubles. +class Double { + public: + static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000); + static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000); + static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit. + static const int kSignificandSize = 53; + + Double() : d64_(0) {} + explicit Double(double d) : d64_(double_to_uint64(d)) {} + explicit Double(uint64_t d64) : d64_(d64) {} + explicit Double(DiyFp diy_fp) + : d64_(DiyFpToUint64(diy_fp)) {} + + // The value encoded by this Double must be greater or equal to +0.0. + // It must not be special (infinity, or NaN). + DiyFp AsDiyFp() const { + ASSERT(Sign() > 0); + ASSERT(!IsSpecial()); + return DiyFp(Significand(), Exponent()); + } + + // The value encoded by this Double must be strictly greater than 0. + DiyFp AsNormalizedDiyFp() const { + ASSERT(value() > 0.0); + uint64_t f = Significand(); + int e = Exponent(); + + // The current double could be a denormal. + while ((f & kHiddenBit) == 0) { + f <<= 1; + e--; + } + // Do the final shifts in one go. + f <<= DiyFp::kSignificandSize - kSignificandSize; + e -= DiyFp::kSignificandSize - kSignificandSize; + return DiyFp(f, e); + } + + // Returns the double's bit as uint64. + uint64_t AsUint64() const { + return d64_; + } + + // Returns the next greater double. Returns +infinity on input +infinity. + double NextDouble() const { + if (d64_ == kInfinity) return Double(kInfinity).value(); + if (Sign() < 0 && Significand() == 0) { + // -0.0 + return 0.0; + } + if (Sign() < 0) { + return Double(d64_ - 1).value(); + } else { + return Double(d64_ + 1).value(); + } + } + + double PreviousDouble() const { + if (d64_ == (kInfinity | kSignMask)) return -Double::Infinity(); + if (Sign() < 0) { + return Double(d64_ + 1).value(); + } else { + if (Significand() == 0) return -0.0; + return Double(d64_ - 1).value(); + } + } + + int Exponent() const { + if (IsDenormal()) return kDenormalExponent; + + uint64_t d64 = AsUint64(); + int biased_e = + static_cast((d64 & kExponentMask) >> kPhysicalSignificandSize); + return biased_e - kExponentBias; + } + + uint64_t Significand() const { + uint64_t d64 = AsUint64(); + uint64_t significand = d64 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + // Returns true if the double is a denormal. + bool IsDenormal() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == kExponentMask; + } + + bool IsNan() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) != 0); + } + + bool IsInfinite() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) == 0); + } + + int Sign() const { + uint64_t d64 = AsUint64(); + return (d64 & kSignMask) == 0? 1: -1; + } + + // Precondition: the value encoded by this Double must be greater or equal + // than +0.0. + DiyFp UpperBoundary() const { + ASSERT(Sign() > 0); + return DiyFp(Significand() * 2 + 1, Exponent() - 1); + } + + // Computes the two boundaries of this. + // The bigger boundary (m_plus) is normalized. The lower boundary has the same + // exponent as m_plus. + // Precondition: the value encoded by this Double must be greater than 0. + void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + ASSERT(value() > 0.0); + DiyFp v = this->AsDiyFp(); + DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); + DiyFp m_minus; + if (LowerBoundaryIsCloser()) { + m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); + } else { + m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); + } + m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); + m_minus.set_e(m_plus.e()); + *out_m_plus = m_plus; + *out_m_minus = m_minus; + } + + bool LowerBoundaryIsCloser() const { + // The boundary is closer if the significand is of the form f == 2^p-1 then + // the lower boundary is closer. + // Think of v = 1000e10 and v- = 9999e9. + // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but + // at a distance of 1e8. + // The only exception is for the smallest normal: the largest denormal is + // at the same distance as its successor. + // Note: denormals have the same exponent as the smallest normals. + bool physical_significand_is_zero = ((AsUint64() & kSignificandMask) == 0); + return physical_significand_is_zero && (Exponent() != kDenormalExponent); + } + + double value() const { return uint64_to_double(d64_); } + + // Returns the significand size for a given order of magnitude. + // If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude. + // This function returns the number of significant binary digits v will have + // once it's encoded into a double. In almost all cases this is equal to + // kSignificandSize. The only exceptions are denormals. They start with + // leading zeroes and their effective significand-size is hence smaller. + static int SignificandSizeForOrderOfMagnitude(int order) { + if (order >= (kDenormalExponent + kSignificandSize)) { + return kSignificandSize; + } + if (order <= kDenormalExponent) return 0; + return order - kDenormalExponent; + } + + static double Infinity() { + return Double(kInfinity).value(); + } + + static double NaN() { + return Double(kNaN).value(); + } + + private: + static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; + static const int kDenormalExponent = -kExponentBias + 1; + static const int kMaxExponent = 0x7FF - kExponentBias; + static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000); + + const uint64_t d64_; + + static uint64_t DiyFpToUint64(DiyFp diy_fp) { + uint64_t significand = diy_fp.f(); + int exponent = diy_fp.e(); + while (significand > kHiddenBit + kSignificandMask) { + significand >>= 1; + exponent++; + } + if (exponent >= kMaxExponent) { + return kInfinity; + } + if (exponent < kDenormalExponent) { + return 0; + } + while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) { + significand <<= 1; + exponent--; + } + uint64_t biased_exponent; + if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) { + biased_exponent = 0; + } else { + biased_exponent = static_cast(exponent + kExponentBias); + } + return (significand & kSignificandMask) | + (biased_exponent << kPhysicalSignificandSize); + } +}; + +class Single { + public: + static const uint32_t kSignMask = 0x80000000; + static const uint32_t kExponentMask = 0x7F800000; + static const uint32_t kSignificandMask = 0x007FFFFF; + static const uint32_t kHiddenBit = 0x00800000; + static const int kPhysicalSignificandSize = 23; // Excludes the hidden bit. + static const int kSignificandSize = 24; + + Single() : d32_(0) {} + explicit Single(float f) : d32_(float_to_uint32(f)) {} + explicit Single(uint32_t d32) : d32_(d32) {} + + // The value encoded by this Single must be greater or equal to +0.0. + // It must not be special (infinity, or NaN). + DiyFp AsDiyFp() const { + ASSERT(Sign() > 0); + ASSERT(!IsSpecial()); + return DiyFp(Significand(), Exponent()); + } + + // Returns the single's bit as uint64. + uint32_t AsUint32() const { + return d32_; + } + + int Exponent() const { + if (IsDenormal()) return kDenormalExponent; + + uint32_t d32 = AsUint32(); + int biased_e = + static_cast((d32 & kExponentMask) >> kPhysicalSignificandSize); + return biased_e - kExponentBias; + } + + uint32_t Significand() const { + uint32_t d32 = AsUint32(); + uint32_t significand = d32 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + // Returns true if the single is a denormal. + bool IsDenormal() const { + uint32_t d32 = AsUint32(); + return (d32 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint32_t d32 = AsUint32(); + return (d32 & kExponentMask) == kExponentMask; + } + + bool IsNan() const { + uint32_t d32 = AsUint32(); + return ((d32 & kExponentMask) == kExponentMask) && + ((d32 & kSignificandMask) != 0); + } + + bool IsInfinite() const { + uint32_t d32 = AsUint32(); + return ((d32 & kExponentMask) == kExponentMask) && + ((d32 & kSignificandMask) == 0); + } + + int Sign() const { + uint32_t d32 = AsUint32(); + return (d32 & kSignMask) == 0? 1: -1; + } + + // Computes the two boundaries of this. + // The bigger boundary (m_plus) is normalized. The lower boundary has the same + // exponent as m_plus. + // Precondition: the value encoded by this Single must be greater than 0. + void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + ASSERT(value() > 0.0); + DiyFp v = this->AsDiyFp(); + DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); + DiyFp m_minus; + if (LowerBoundaryIsCloser()) { + m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); + } else { + m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); + } + m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); + m_minus.set_e(m_plus.e()); + *out_m_plus = m_plus; + *out_m_minus = m_minus; + } + + // Precondition: the value encoded by this Single must be greater or equal + // than +0.0. + DiyFp UpperBoundary() const { + ASSERT(Sign() > 0); + return DiyFp(Significand() * 2 + 1, Exponent() - 1); + } + + bool LowerBoundaryIsCloser() const { + // The boundary is closer if the significand is of the form f == 2^p-1 then + // the lower boundary is closer. + // Think of v = 1000e10 and v- = 9999e9. + // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but + // at a distance of 1e8. + // The only exception is for the smallest normal: the largest denormal is + // at the same distance as its successor. + // Note: denormals have the same exponent as the smallest normals. + bool physical_significand_is_zero = ((AsUint32() & kSignificandMask) == 0); + return physical_significand_is_zero && (Exponent() != kDenormalExponent); + } + + float value() const { return uint32_to_float(d32_); } + + static float Infinity() { + return Single(kInfinity).value(); + } + + static float NaN() { + return Single(kNaN).value(); + } + + private: + static const int kExponentBias = 0x7F + kPhysicalSignificandSize; + static const int kDenormalExponent = -kExponentBias + 1; + static const int kMaxExponent = 0xFF - kExponentBias; + static const uint32_t kInfinity = 0x7F800000; + static const uint32_t kNaN = 0x7FC00000; + + const uint32_t d32_; +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_DOUBLE_H_ diff --git a/3rdparty/double-conversion/strtod.cc b/3rdparty/double-conversion/strtod.cc new file mode 100644 index 0000000000..9758989f71 --- /dev/null +++ b/3rdparty/double-conversion/strtod.cc @@ -0,0 +1,554 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include + +#include "strtod.h" +#include "bignum.h" +#include "cached-powers.h" +#include "ieee.h" + +namespace double_conversion { + +// 2^53 = 9007199254740992. +// Any integer with at most 15 decimal digits will hence fit into a double +// (which has a 53bit significand) without loss of precision. +static const int kMaxExactDoubleIntegerDecimalDigits = 15; +// 2^64 = 18446744073709551616 > 10^19 +static const int kMaxUint64DecimalDigits = 19; + +// Max double: 1.7976931348623157 x 10^308 +// Min non-zero double: 4.9406564584124654 x 10^-324 +// Any x >= 10^309 is interpreted as +infinity. +// Any x <= 10^-324 is interpreted as 0. +// Note that 2.5e-324 (despite being smaller than the min double) will be read +// as non-zero (equal to the min non-zero double). +static const int kMaxDecimalPower = 309; +static const int kMinDecimalPower = -324; + +// 2^64 = 18446744073709551616 +static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); + + +static const double exact_powers_of_ten[] = { + 1.0, // 10^0 + 10.0, + 100.0, + 1000.0, + 10000.0, + 100000.0, + 1000000.0, + 10000000.0, + 100000000.0, + 1000000000.0, + 10000000000.0, // 10^10 + 100000000000.0, + 1000000000000.0, + 10000000000000.0, + 100000000000000.0, + 1000000000000000.0, + 10000000000000000.0, + 100000000000000000.0, + 1000000000000000000.0, + 10000000000000000000.0, + 100000000000000000000.0, // 10^20 + 1000000000000000000000.0, + // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22 + 10000000000000000000000.0 +}; +static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten); + +// Maximum number of significant digits in the decimal representation. +// In fact the value is 772 (see conversions.cc), but to give us some margin +// we round up to 780. +static const int kMaxSignificantDecimalDigits = 780; + +static Vector TrimLeadingZeros(Vector buffer) { + for (int i = 0; i < buffer.length(); i++) { + if (buffer[i] != '0') { + return buffer.SubVector(i, buffer.length()); + } + } + return Vector(buffer.start(), 0); +} + + +static Vector TrimTrailingZeros(Vector buffer) { + for (int i = buffer.length() - 1; i >= 0; --i) { + if (buffer[i] != '0') { + return buffer.SubVector(0, i + 1); + } + } + return Vector(buffer.start(), 0); +} + + +static void CutToMaxSignificantDigits(Vector buffer, + int exponent, + char* significant_buffer, + int* significant_exponent) { + for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) { + significant_buffer[i] = buffer[i]; + } + // The input buffer has been trimmed. Therefore the last digit must be + // different from '0'. + ASSERT(buffer[buffer.length() - 1] != '0'); + // Set the last digit to be non-zero. This is sufficient to guarantee + // correct rounding. + significant_buffer[kMaxSignificantDecimalDigits - 1] = '1'; + *significant_exponent = + exponent + (buffer.length() - kMaxSignificantDecimalDigits); +} + + +// Trims the buffer and cuts it to at most kMaxSignificantDecimalDigits. +// If possible the input-buffer is reused, but if the buffer needs to be +// modified (due to cutting), then the input needs to be copied into the +// buffer_copy_space. +static void TrimAndCut(Vector buffer, int exponent, + char* buffer_copy_space, int space_size, + Vector* trimmed, int* updated_exponent) { + Vector left_trimmed = TrimLeadingZeros(buffer); + Vector right_trimmed = TrimTrailingZeros(left_trimmed); + exponent += left_trimmed.length() - right_trimmed.length(); + if (right_trimmed.length() > kMaxSignificantDecimalDigits) { + ASSERT(space_size >= kMaxSignificantDecimalDigits); + CutToMaxSignificantDigits(right_trimmed, exponent, + buffer_copy_space, updated_exponent); + *trimmed = Vector(buffer_copy_space, + kMaxSignificantDecimalDigits); + } else { + *trimmed = right_trimmed; + *updated_exponent = exponent; + } +} + + +// Reads digits from the buffer and converts them to a uint64. +// Reads in as many digits as fit into a uint64. +// When the string starts with "1844674407370955161" no further digit is read. +// Since 2^64 = 18446744073709551616 it would still be possible read another +// digit if it was less or equal than 6, but this would complicate the code. +static uint64_t ReadUint64(Vector buffer, + int* number_of_read_digits) { + uint64_t result = 0; + int i = 0; + while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) { + int digit = buffer[i++] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = 10 * result + digit; + } + *number_of_read_digits = i; + return result; +} + + +// Reads a DiyFp from the buffer. +// The returned DiyFp is not necessarily normalized. +// If remaining_decimals is zero then the returned DiyFp is accurate. +// Otherwise it has been rounded and has error of at most 1/2 ulp. +static void ReadDiyFp(Vector buffer, + DiyFp* result, + int* remaining_decimals) { + int read_digits; + uint64_t significand = ReadUint64(buffer, &read_digits); + if (buffer.length() == read_digits) { + *result = DiyFp(significand, 0); + *remaining_decimals = 0; + } else { + // Round the significand. + if (buffer[read_digits] >= '5') { + significand++; + } + // Compute the binary exponent. + int exponent = 0; + *result = DiyFp(significand, exponent); + *remaining_decimals = buffer.length() - read_digits; + } +} + + +static bool DoubleStrtod(Vector trimmed, + int exponent, + double* result) { +#if !defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) + // On x86 the floating-point stack can be 64 or 80 bits wide. If it is + // 80 bits wide (as is the case on Linux) then double-rounding occurs and the + // result is not accurate. + // We know that Windows32 uses 64 bits and is therefore accurate. + // Note that the ARM simulator is compiled for 32bits. It therefore exhibits + // the same problem. + return false; +#endif + if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) { + int read_digits; + // The trimmed input fits into a double. + // If the 10^exponent (resp. 10^-exponent) fits into a double too then we + // can compute the result-double simply by multiplying (resp. dividing) the + // two numbers. + // This is possible because IEEE guarantees that floating-point operations + // return the best possible approximation. + if (exponent < 0 && -exponent < kExactPowersOfTenSize) { + // 10^-exponent fits into a double. + *result = static_cast(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result /= exact_powers_of_ten[-exponent]; + return true; + } + if (0 <= exponent && exponent < kExactPowersOfTenSize) { + // 10^exponent fits into a double. + *result = static_cast(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[exponent]; + return true; + } + int remaining_digits = + kMaxExactDoubleIntegerDecimalDigits - trimmed.length(); + if ((0 <= exponent) && + (exponent - remaining_digits < kExactPowersOfTenSize)) { + // The trimmed string was short and we can multiply it with + // 10^remaining_digits. As a result the remaining exponent now fits + // into a double too. + *result = static_cast(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[remaining_digits]; + *result *= exact_powers_of_ten[exponent - remaining_digits]; + return true; + } + } + return false; +} + + +// Returns 10^exponent as an exact DiyFp. +// The given exponent must be in the range [1; kDecimalExponentDistance[. +static DiyFp AdjustmentPowerOfTen(int exponent) { + ASSERT(0 < exponent); + ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance); + // Simply hardcode the remaining powers for the given decimal exponent + // distance. + ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8); + switch (exponent) { + case 1: return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60); + case 2: return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57); + case 3: return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54); + case 4: return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50); + case 5: return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47); + case 6: return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44); + case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40); + default: + UNREACHABLE(); + return DiyFp(0, 0); + } +} + + +// If the function returns true then the result is the correct double. +// Otherwise it is either the correct double or the double that is just below +// the correct double. +static bool DiyFpStrtod(Vector buffer, + int exponent, + double* result) { + DiyFp input; + int remaining_decimals; + ReadDiyFp(buffer, &input, &remaining_decimals); + // Since we may have dropped some digits the input is not accurate. + // If remaining_decimals is different than 0 than the error is at most + // .5 ulp (unit in the last place). + // We don't want to deal with fractions and therefore keep a common + // denominator. + const int kDenominatorLog = 3; + const int kDenominator = 1 << kDenominatorLog; + // Move the remaining decimals into the exponent. + exponent += remaining_decimals; + int error = (remaining_decimals == 0 ? 0 : kDenominator / 2); + + int old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent); + if (exponent < PowersOfTenCache::kMinDecimalExponent) { + *result = 0.0; + return true; + } + DiyFp cached_power; + int cached_decimal_exponent; + PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent, + &cached_power, + &cached_decimal_exponent); + + if (cached_decimal_exponent != exponent) { + int adjustment_exponent = exponent - cached_decimal_exponent; + DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent); + input.Multiply(adjustment_power); + if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) { + // The product of input with the adjustment power fits into a 64 bit + // integer. + ASSERT(DiyFp::kSignificandSize == 64); + } else { + // The adjustment power is exact. There is hence only an error of 0.5. + error += kDenominator / 2; + } + } + + input.Multiply(cached_power); + // The error introduced by a multiplication of a*b equals + // error_a + error_b + error_a*error_b/2^64 + 0.5 + // Substituting a with 'input' and b with 'cached_power' we have + // error_b = 0.5 (all cached powers have an error of less than 0.5 ulp), + // error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64 + int error_b = kDenominator / 2; + int error_ab = (error == 0 ? 0 : 1); // We round up to 1. + int fixed_error = kDenominator / 2; + error += error_b + error_ab + fixed_error; + + old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + // See if the double's significand changes if we add/subtract the error. + int order_of_magnitude = DiyFp::kSignificandSize + input.e(); + int effective_significand_size = + Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude); + int precision_digits_count = + DiyFp::kSignificandSize - effective_significand_size; + if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) { + // This can only happen for very small denormals. In this case the + // half-way multiplied by the denominator exceeds the range of an uint64. + // Simply shift everything to the right. + int shift_amount = (precision_digits_count + kDenominatorLog) - + DiyFp::kSignificandSize + 1; + input.set_f(input.f() >> shift_amount); + input.set_e(input.e() + shift_amount); + // We add 1 for the lost precision of error, and kDenominator for + // the lost precision of input.f(). + error = (error >> shift_amount) + 1 + kDenominator; + precision_digits_count -= shift_amount; + } + // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too. + ASSERT(DiyFp::kSignificandSize == 64); + ASSERT(precision_digits_count < 64); + uint64_t one64 = 1; + uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1; + uint64_t precision_bits = input.f() & precision_bits_mask; + uint64_t half_way = one64 << (precision_digits_count - 1); + precision_bits *= kDenominator; + half_way *= kDenominator; + DiyFp rounded_input(input.f() >> precision_digits_count, + input.e() + precision_digits_count); + if (precision_bits >= half_way + error) { + rounded_input.set_f(rounded_input.f() + 1); + } + // If the last_bits are too close to the half-way case than we are too + // inaccurate and round down. In this case we return false so that we can + // fall back to a more precise algorithm. + + *result = Double(rounded_input).value(); + if (half_way - error < precision_bits && precision_bits < half_way + error) { + // Too imprecise. The caller will have to fall back to a slower version. + // However the returned number is guaranteed to be either the correct + // double, or the next-lower double. + return false; + } else { + return true; + } +} + + +// Returns +// - -1 if buffer*10^exponent < diy_fp. +// - 0 if buffer*10^exponent == diy_fp. +// - +1 if buffer*10^exponent > diy_fp. +// Preconditions: +// buffer.length() + exponent <= kMaxDecimalPower + 1 +// buffer.length() + exponent > kMinDecimalPower +// buffer.length() <= kMaxDecimalSignificantDigits +static int CompareBufferWithDiyFp(Vector buffer, + int exponent, + DiyFp diy_fp) { + ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); + ASSERT(buffer.length() + exponent > kMinDecimalPower); + ASSERT(buffer.length() <= kMaxSignificantDecimalDigits); + // Make sure that the Bignum will be able to hold all our numbers. + // Our Bignum implementation has a separate field for exponents. Shifts will + // consume at most one bigit (< 64 bits). + // ln(10) == 3.3219... + ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits); + Bignum buffer_bignum; + Bignum diy_fp_bignum; + buffer_bignum.AssignDecimalString(buffer); + diy_fp_bignum.AssignUInt64(diy_fp.f()); + if (exponent >= 0) { + buffer_bignum.MultiplyByPowerOfTen(exponent); + } else { + diy_fp_bignum.MultiplyByPowerOfTen(-exponent); + } + if (diy_fp.e() > 0) { + diy_fp_bignum.ShiftLeft(diy_fp.e()); + } else { + buffer_bignum.ShiftLeft(-diy_fp.e()); + } + return Bignum::Compare(buffer_bignum, diy_fp_bignum); +} + + +// Returns true if the guess is the correct double. +// Returns false, when guess is either correct or the next-lower double. +static bool ComputeGuess(Vector trimmed, int exponent, + double* guess) { + if (trimmed.length() == 0) { + *guess = 0.0; + return true; + } + if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) { + *guess = Double::Infinity(); + return true; + } + if (exponent + trimmed.length() <= kMinDecimalPower) { + *guess = 0.0; + return true; + } + + if (DoubleStrtod(trimmed, exponent, guess) || + DiyFpStrtod(trimmed, exponent, guess)) { + return true; + } + if (*guess == Double::Infinity()) { + return true; + } + return false; +} + +double Strtod(Vector buffer, int exponent) { + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector trimmed; + int updated_exponent; + TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, + &trimmed, &updated_exponent); + exponent = updated_exponent; + + double guess; + bool is_correct = ComputeGuess(trimmed, exponent, &guess); + if (is_correct) return guess; + + DiyFp upper_boundary = Double(guess).UpperBoundary(); + int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); + if (comparison < 0) { + return guess; + } else if (comparison > 0) { + return Double(guess).NextDouble(); + } else if ((Double(guess).Significand() & 1) == 0) { + // Round towards even. + return guess; + } else { + return Double(guess).NextDouble(); + } +} + +float Strtof(Vector buffer, int exponent) { + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector trimmed; + int updated_exponent; + TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, + &trimmed, &updated_exponent); + exponent = updated_exponent; + + double double_guess; + bool is_correct = ComputeGuess(trimmed, exponent, &double_guess); + + float float_guess = static_cast(double_guess); + if (float_guess == double_guess) { + // This shortcut triggers for integer values. + return float_guess; + } + + // We must catch double-rounding. Say the double has been rounded up, and is + // now a boundary of a float, and rounds up again. This is why we have to + // look at previous too. + // Example (in decimal numbers): + // input: 12349 + // high-precision (4 digits): 1235 + // low-precision (3 digits): + // when read from input: 123 + // when rounded from high precision: 124. + // To do this we simply look at the neigbors of the correct result and see + // if they would round to the same float. If the guess is not correct we have + // to look at four values (since two different doubles could be the correct + // double). + + double double_next = Double(double_guess).NextDouble(); + double double_previous = Double(double_guess).PreviousDouble(); + + float f1 = static_cast(double_previous); + float f2 = float_guess; + float f3 = static_cast(double_next); + float f4; + if (is_correct) { + f4 = f3; + } else { + double double_next2 = Double(double_next).NextDouble(); + f4 = static_cast(double_next2); + } + ASSERT(f1 <= f2 && f2 <= f3 && f3 <= f4); + + // If the guess doesn't lie near a single-precision boundary we can simply + // return its float-value. + if (f1 == f4) { + return float_guess; + } + + ASSERT((f1 != f2 && f2 == f3 && f3 == f4) || + (f1 == f2 && f2 != f3 && f3 == f4) || + (f1 == f2 && f2 == f3 && f3 != f4)); + + // guess and next are the two possible canditates (in the same way that + // double_guess was the lower candidate for a double-precision guess). + float guess = f1; + float next = f4; + DiyFp upper_boundary; + if (guess == 0.0f) { + float min_float = 1e-45f; + upper_boundary = Double(static_cast(min_float) / 2).AsDiyFp(); + } else { + upper_boundary = Single(guess).UpperBoundary(); + } + int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); + if (comparison < 0) { + return guess; + } else if (comparison > 0) { + return next; + } else if ((Single(guess).Significand() & 1) == 0) { + // Round towards even. + return guess; + } else { + return next; + } +} + +} // namespace double_conversion diff --git a/3rdparty/double-conversion/strtod.h b/3rdparty/double-conversion/strtod.h new file mode 100644 index 0000000000..ed0293b8f5 --- /dev/null +++ b/3rdparty/double-conversion/strtod.h @@ -0,0 +1,45 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_STRTOD_H_ +#define DOUBLE_CONVERSION_STRTOD_H_ + +#include "utils.h" + +namespace double_conversion { + +// The buffer must only contain digits in the range [0-9]. It must not +// contain a dot or a sign. It must not start with '0', and must not be empty. +double Strtod(Vector buffer, int exponent); + +// The buffer must only contain digits in the range [0-9]. It must not +// contain a dot or a sign. It must not start with '0', and must not be empty. +float Strtof(Vector buffer, int exponent); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_STRTOD_H_ diff --git a/3rdparty/double-conversion/utils.h b/3rdparty/double-conversion/utils.h new file mode 100644 index 0000000000..767094b8b7 --- /dev/null +++ b/3rdparty/double-conversion/utils.h @@ -0,0 +1,313 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_UTILS_H_ +#define DOUBLE_CONVERSION_UTILS_H_ + +#include +#include + +#include +#ifndef ASSERT +#define ASSERT(condition) (assert(condition)) +#endif +#ifndef UNIMPLEMENTED +#define UNIMPLEMENTED() (abort()) +#endif +#ifndef UNREACHABLE +#define UNREACHABLE() (abort()) +#endif + +// Double operations detection based on target architecture. +// Linux uses a 80bit wide floating point stack on x86. This induces double +// rounding, which in turn leads to wrong results. +// An easy way to test if the floating-point operations are correct is to +// evaluate: 89255.0/1e22. If the floating-point stack is 64 bits wide then +// the result is equal to 89255e-22. +// The best way to test this, is to create a division-function and to compare +// the output of the division with the expected result. (Inlining must be +// disabled.) +// On Linux,x86 89255e-22 != Div_double(89255.0/1e22) +#if defined(_M_X64) || defined(__x86_64__) || \ + defined(__ARMEL__) || defined(__avr32__) || \ + defined(__hppa__) || defined(__ia64__) || \ + defined(__mips__) || defined(__powerpc__) || \ + defined(__sparc__) || defined(__sparc) || defined(__s390__) || \ + defined(__SH4__) || defined(__alpha__) || \ + defined(_MIPS_ARCH_MIPS32R2) +#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 +#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) +#if defined(_WIN32) +// Windows uses a 64bit wide floating point stack. +#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 +#else +#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS +#endif // _WIN32 +#else +#error Target architecture was not detected as supported by Double-Conversion. +#endif + + +#if defined(_WIN32) && !defined(__MINGW32__) + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; // NOLINT +typedef unsigned short uint16_t; // NOLINT +typedef int int32_t; +typedef unsigned int uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +// intptr_t and friends are defined in crtdefs.h through stdio.h. + +#else + +#include + +#endif + +// The following macro works on both 32 and 64-bit platforms. +// Usage: instead of writing 0x1234567890123456 +// write UINT64_2PART_C(0x12345678,90123456); +#define UINT64_2PART_C(a, b) (((static_cast(a) << 32) + 0x##b##u)) + + +// The expression ARRAY_SIZE(a) is a compile-time constant of type +// size_t which represents the number of elements of the given +// array. You should only use ARRAY_SIZE on statically allocated +// arrays. +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast(!(sizeof(a) % sizeof(*(a))))) +#endif + +// A macro to disallow the evil copy constructor and operator= functions +// This should be used in the private: declarations for a class +#ifndef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#ifndef DISALLOW_IMPLICIT_CONSTRUCTORS +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + DISALLOW_COPY_AND_ASSIGN(TypeName) +#endif + +namespace double_conversion { + +static const int kCharSize = sizeof(char); + +// Returns the maximum of the two parameters. +template +static T Max(T a, T b) { + return a < b ? b : a; +} + + +// Returns the minimum of the two parameters. +template +static T Min(T a, T b) { + return a < b ? a : b; +} + + +inline int StrLength(const char* string) { + size_t length = strlen(string); + ASSERT(length == static_cast(static_cast(length))); + return static_cast(length); +} + +// This is a simplified version of V8's Vector class. +template +class Vector { + public: + Vector() : start_(NULL), length_(0) {} + Vector(T* data, int length) : start_(data), length_(length) { + ASSERT(length == 0 || (length > 0 && data != NULL)); + } + + // Returns a vector using the same backing storage as this one, + // spanning from and including 'from', to but not including 'to'. + Vector SubVector(int from, int to) { + ASSERT(to <= length_); + ASSERT(from < to); + ASSERT(0 <= from); + return Vector(start() + from, to - from); + } + + // Returns the length of the vector. + int length() const { return length_; } + + // Returns whether or not the vector is empty. + bool is_empty() const { return length_ == 0; } + + // Returns the pointer to the start of the data in the vector. + T* start() const { return start_; } + + // Access individual vector elements - checks bounds in debug mode. + T& operator[](int index) const { + ASSERT(0 <= index && index < length_); + return start_[index]; + } + + T& first() { return start_[0]; } + + T& last() { return start_[length_ - 1]; } + + private: + T* start_; + int length_; +}; + + +// Helper class for building result strings in a character buffer. The +// purpose of the class is to use safe operations that checks the +// buffer bounds on all operations in debug mode. +class StringBuilder { + public: + StringBuilder(char* buffer, int size) + : buffer_(buffer, size), position_(0) { } + + ~StringBuilder() { if (!is_finalized()) Finalize(); } + + int size() const { return buffer_.length(); } + + // Get the current position in the builder. + int position() const { + ASSERT(!is_finalized()); + return position_; + } + + // Reset the position. + void Reset() { position_ = 0; } + + // Add a single character to the builder. It is not allowed to add + // 0-characters; use the Finalize() method to terminate the string + // instead. + void AddCharacter(char c) { + ASSERT(c != '\0'); + ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_++] = c; + } + + // Add an entire string to the builder. Uses strlen() internally to + // compute the length of the input string. + void AddString(const char* s) { + AddSubstring(s, StrLength(s)); + } + + // Add the first 'n' characters of the given string 's' to the + // builder. The input string must have enough characters. + void AddSubstring(const char* s, int n) { + ASSERT(!is_finalized() && position_ + n < buffer_.length()); + ASSERT(static_cast(n) <= strlen(s)); + memmove(&buffer_[position_], s, n * kCharSize); + position_ += n; + } + + + // Add character padding to the builder. If count is non-positive, + // nothing is added to the builder. + void AddPadding(char c, int count) { + for (int i = 0; i < count; i++) { + AddCharacter(c); + } + } + + // Finalize the string by 0-terminating it and returning the buffer. + char* Finalize() { + ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_] = '\0'; + // Make sure nobody managed to add a 0-character to the + // buffer while building the string. + ASSERT(strlen(buffer_.start()) == static_cast(position_)); + position_ = -1; + ASSERT(is_finalized()); + return buffer_.start(); + } + + private: + Vector buffer_; + int position_; + + bool is_finalized() const { return position_ < 0; } + + DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder); +}; + +// The type-based aliasing rule allows the compiler to assume that pointers of +// different types (for some definition of different) never alias each other. +// Thus the following code does not work: +// +// float f = foo(); +// int fbits = *(int*)(&f); +// +// The compiler 'knows' that the int pointer can't refer to f since the types +// don't match, so the compiler may cache f in a register, leaving random data +// in fbits. Using C++ style casts makes no difference, however a pointer to +// char data is assumed to alias any other pointer. This is the 'memcpy +// exception'. +// +// Bit_cast uses the memcpy exception to move the bits from a variable of one +// type of a variable of another type. Of course the end result is likely to +// be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005) +// will completely optimize BitCast away. +// +// There is an additional use for BitCast. +// Recent gccs will warn when they see casts that may result in breakage due to +// the type-based aliasing rule. If you have checked that there is no breakage +// you can use BitCast to cast one pointer type to another. This confuses gcc +// enough that it can no longer see that you have cast one pointer type to +// another thus avoiding the warning. +template +inline Dest BitCast(const Source& source) { + // Compile time assertion: sizeof(Dest) == sizeof(Source) + // A compile error here means your Dest and Source have different sizes. + typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1]; + + Dest dest; + memmove(&dest, &source, sizeof(dest)); + return dest; +} + +template +inline Dest BitCast(Source* source) { + return BitCast(reinterpret_cast(source)); +} + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_UTILS_H_ diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 3d6e0536ca..293e1dbe34 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -54,6 +54,8 @@ #include #include +#include "3rdparty/double-conversion/double-conversion.h" + namespace QQmlJS { namespace VM { @@ -65,8 +67,12 @@ QString numberToString(double num, int radix = 10) return QLatin1String(num < 0 ? "-Infinity" : "Infinity"); } - if (radix == 10) - return QString::number(num, 'g', 16); + if (radix == 10) { + char str[100]; + double_conversion::StringBuilder builder(str, sizeof(str)); + double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToShortest(num, &builder); + return QString::fromLatin1(builder.Finalize()); + } QString str; bool negative = false; diff --git a/v4.pro b/v4.pro index 7bdb8959b4..a2c90a6ad6 100644 --- a/v4.pro +++ b/v4.pro @@ -81,3 +81,4 @@ DEFINES += QMLJS_NO_LLVM include(moth/moth.pri) include(masm/masm.pri) +include(3rdparty/double-conversion/double-conversion.pri) -- cgit v1.2.3 From e593643820f954f53a4c9796e5255f559f2ee106 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 3 Jan 2013 14:45:00 +0100 Subject: Get rid of unused FastMalloc definitions We don't ship the implementation and we only need a really simple stub to compile the code we _really_ want to keep. Change-Id: I36bd0f836b963ed38feb10b970326a4dcd5cbc12 Reviewed-by: Lars Knoll --- masm/masm.pri | 1 - masm/stubs/wtf/FastAllocBase.h | 48 +++++ masm/stubs/wtf/FastMalloc.h | 46 +++++ masm/wtf/FastAllocBase.h | 427 ----------------------------------------- masm/wtf/FastMalloc.h | 281 --------------------------- 5 files changed, 94 insertions(+), 709 deletions(-) create mode 100644 masm/stubs/wtf/FastAllocBase.h create mode 100644 masm/stubs/wtf/FastMalloc.h delete mode 100644 masm/wtf/FastAllocBase.h delete mode 100644 masm/wtf/FastMalloc.h diff --git a/masm/masm.pri b/masm/masm.pri index 3a8968d112..edbb044f0e 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -38,7 +38,6 @@ DEFINES += ENABLE_DFG_JIT=0 DEFINES += ENABLE_JIT=1 DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 DEFINES += ENABLE_ASSEMBLER=1 -DEFINES += USE_SYSTEM_MALLOC=1 DEFINES += BUILDING_QT__ diff --git a/masm/stubs/wtf/FastAllocBase.h b/masm/stubs/wtf/FastAllocBase.h new file mode 100644 index 0000000000..a062a885af --- /dev/null +++ b/masm/stubs/wtf/FastAllocBase.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef FASTALLOCBASE_H +#define FASTALLOCBASE_H + +/* Dummy empty header file, only needed for #include source compatibility */ + +#define WTF_MAKE_FAST_ALLOCATED + +#endif // FASTALLOCBASE_H diff --git a/masm/stubs/wtf/FastMalloc.h b/masm/stubs/wtf/FastMalloc.h new file mode 100644 index 0000000000..1248c79dec --- /dev/null +++ b/masm/stubs/wtf/FastMalloc.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef FASTMALLOC_H +#define FASTMALLOC_H + +/* Dummy empty header file, only needed for #include source compatibility */ + +#endif // FASTMALLOC_H diff --git a/masm/wtf/FastAllocBase.h b/masm/wtf/FastAllocBase.h deleted file mode 100644 index a0804ad3d7..0000000000 --- a/masm/wtf/FastAllocBase.h +++ /dev/null @@ -1,427 +0,0 @@ -/* - * Copyright (C) 2008, 2009 Paul Pedriana . All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef FastAllocBase_h -#define FastAllocBase_h - -// Provides customizable overrides of fastMalloc/fastFree and operator new/delete -// -// Provided functionality: -// Macro: WTF_MAKE_FAST_ALLOCATED -// namespace WTF { -// -// T* fastNew(); -// T* fastNew(arg); -// T* fastNew(arg, arg); -// T* fastNewArray(count); -// void fastDelete(T* p); -// void fastDeleteArray(T* p); -// void fastNonNullDelete(T* p); -// void fastNonNullDeleteArray(T* p); -// } -// -// FastDelete assumes that the underlying -// -// Example usage: -// class Widget { -// WTF_MAKE_FAST_ALLOCATED -// ... -// }; -// -// struct Data { -// WTF_MAKE_FAST_ALLOCATED -// public: -// ... -// }; -// -// char* charPtr = fastNew(); -// fastDelete(charPtr); -// -// char* charArrayPtr = fastNewArray(37); -// fastDeleteArray(charArrayPtr); -// -// void** voidPtrPtr = fastNew(); -// fastDelete(voidPtrPtr); -// -// void** voidPtrArrayPtr = fastNewArray(37); -// fastDeleteArray(voidPtrArrayPtr); -// -// POD* podPtr = fastNew(); -// fastDelete(podPtr); -// -// POD* podArrayPtr = fastNewArray(37); -// fastDeleteArray(podArrayPtr); -// -// Object* objectPtr = fastNew(); -// fastDelete(objectPtr); -// -// Object* objectArrayPtr = fastNewArray(37); -// fastDeleteArray(objectArrayPtr); -// - -#include -#include -#include -#include -#include -#include -#include -#include - -#define WTF_MAKE_FAST_ALLOCATED \ -public: \ - void* operator new(size_t, void* p) { return p; } \ - void* operator new[](size_t, void* p) { return p; } \ - \ - void* operator new(size_t size) \ - { \ - void* p = ::WTF::fastMalloc(size); \ - ::WTF::fastMallocMatchValidateMalloc(p, ::WTF::Internal::AllocTypeClassNew); \ - return p; \ - } \ - \ - void operator delete(void* p) \ - { \ - ::WTF::fastMallocMatchValidateFree(p, ::WTF::Internal::AllocTypeClassNew); \ - ::WTF::fastFree(p); \ - } \ - \ - void* operator new[](size_t size) \ - { \ - void* p = ::WTF::fastMalloc(size); \ - ::WTF::fastMallocMatchValidateMalloc(p, ::WTF::Internal::AllocTypeClassNewArray); \ - return p; \ - } \ - \ - void operator delete[](void* p) \ - { \ - ::WTF::fastMallocMatchValidateFree(p, ::WTF::Internal::AllocTypeClassNewArray); \ - ::WTF::fastFree(p); \ - } \ - void* operator new(size_t, NotNullTag, void* location) \ - { \ - ASSERT(location); \ - return location; \ - } \ -private: \ -typedef int __thisIsHereToForceASemicolonAfterThisMacro - -namespace WTF { - - // fastNew / fastDelete - - template - inline T* fastNew() - { - void* p = fastMalloc(sizeof(T)); - - if (!p) - return 0; - - fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); - return ::new (p) T; - } - - template - inline T* fastNew(Arg1 arg1) - { - void* p = fastMalloc(sizeof(T)); - - if (!p) - return 0; - - fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); - return ::new (p) T(arg1); - } - - template - inline T* fastNew(Arg1 arg1, Arg2 arg2) - { - void* p = fastMalloc(sizeof(T)); - - if (!p) - return 0; - - fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); - return ::new (p) T(arg1, arg2); - } - - template - inline T* fastNew(Arg1 arg1, Arg2 arg2, Arg3 arg3) - { - void* p = fastMalloc(sizeof(T)); - - if (!p) - return 0; - - fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); - return ::new (p) T(arg1, arg2, arg3); - } - - template - inline T* fastNew(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) - { - void* p = fastMalloc(sizeof(T)); - - if (!p) - return 0; - - fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); - return ::new (p) T(arg1, arg2, arg3, arg4); - } - - template - inline T* fastNew(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) - { - void* p = fastMalloc(sizeof(T)); - - if (!p) - return 0; - - fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNew); - return ::new (p) T(arg1, arg2, arg3, arg4, arg5); - } - - namespace Internal { - - // We define a union of pointer to an integer and pointer to T. - // When non-POD arrays are allocated we add a few leading bytes to tell what - // the size of the array is. We return to the user the pointer to T. - // The way to think of it is as if we allocate a struct like so: - // struct Array { - // AllocAlignmentInteger m_size; - // T m_T[array count]; - // }; - - template - union ArraySize { - AllocAlignmentInteger* size; - T* t; - }; - - // This is a support template for fastNewArray. - // This handles the case wherein T has a trivial ctor and a trivial dtor. - template - struct NewArrayImpl { - static T* fastNewArray(size_t count) - { - T* p = static_cast(fastMalloc(sizeof(T) * count)); - fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNewArray); - return p; - } - }; - - // This is a support template for fastNewArray. - // This handles the case wherein T has a non-trivial ctor and a trivial dtor. - template - struct NewArrayImpl { - static T* fastNewArray(size_t count) - { - T* p = static_cast(fastMalloc(sizeof(T) * count)); - - if (!p) - return 0; - - fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNewArray); - - for (T* pObject = p, *pObjectEnd = pObject + count; pObject != pObjectEnd; ++pObject) - ::new (pObject) T; - - return p; - } - }; - - // This is a support template for fastNewArray. - // This handles the case wherein T has a trivial ctor and a non-trivial dtor. - template - struct NewArrayImpl { - static T* fastNewArray(size_t count) - { - void* p = fastMalloc(sizeof(AllocAlignmentInteger) + (sizeof(T) * count)); - ArraySize a = { static_cast(p) }; - - if (!p) - return 0; - - fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNewArray); - *a.size++ = count; - // No need to construct the objects in this case. - - return a.t; - } - }; - - // This is a support template for fastNewArray. - // This handles the case wherein T has a non-trivial ctor and a non-trivial dtor. - template - struct NewArrayImpl { - static T* fastNewArray(size_t count) - { - void* p = fastMalloc(sizeof(AllocAlignmentInteger) + (sizeof(T) * count)); - ArraySize a = { static_cast(p) }; - - if (!p) - return 0; - - fastMallocMatchValidateMalloc(p, Internal::AllocTypeFastNewArray); - *a.size++ = count; - - for (T* pT = a.t, *pTEnd = pT + count; pT != pTEnd; ++pT) - ::new (pT) T; - - return a.t; - } - }; - } // namespace Internal - - template - inline T* fastNewArray(size_t count) - { - return Internal::NewArrayImpl::value, WTF::HasTrivialDestructor::value>::fastNewArray(count); - } - - template - inline void fastDelete(T* p) - { - if (!p) - return; - - fastMallocMatchValidateFree(p, Internal::AllocTypeFastNew); - p->~T(); - fastFree(p); - } - - template - inline void fastDeleteSkippingDestructor(T* p) - { - if (!p) - return; - - fastMallocMatchValidateFree(p, Internal::AllocTypeFastNew); - fastFree(p); - } - - namespace Internal { - // This is a support template for fastDeleteArray. - // This handles the case wherein T has a trivial dtor. - template - struct DeleteArrayImpl { - static void fastDeleteArray(void* p) - { - // No need to destruct the objects in this case. - // We expect that fastFree checks for null. - fastMallocMatchValidateFree(p, Internal::AllocTypeFastNewArray); - fastFree(p); - } - }; - - // This is a support template for fastDeleteArray. - // This handles the case wherein T has a non-trivial dtor. - template - struct DeleteArrayImpl { - static void fastDeleteArray(T* p) - { - if (!p) - return; - - ArraySize a; - a.t = p; - a.size--; // Decrement size pointer - - T* pEnd = p + *a.size; - while (pEnd-- != p) - pEnd->~T(); - - fastMallocMatchValidateFree(a.size, Internal::AllocTypeFastNewArray); - fastFree(a.size); - } - }; - - } // namespace Internal - - template - void fastDeleteArray(T* p) - { - Internal::DeleteArrayImpl::value>::fastDeleteArray(p); - } - - - template - inline void fastNonNullDelete(T* p) - { - fastMallocMatchValidateFree(p, Internal::AllocTypeFastNew); - p->~T(); - fastFree(p); - } - - namespace Internal { - // This is a support template for fastDeleteArray. - // This handles the case wherein T has a trivial dtor. - template - struct NonNullDeleteArrayImpl { - static void fastNonNullDeleteArray(void* p) - { - fastMallocMatchValidateFree(p, Internal::AllocTypeFastNewArray); - // No need to destruct the objects in this case. - fastFree(p); - } - }; - - // This is a support template for fastDeleteArray. - // This handles the case wherein T has a non-trivial dtor. - template - struct NonNullDeleteArrayImpl { - static void fastNonNullDeleteArray(T* p) - { - ArraySize a; - a.t = p; - a.size--; - - T* pEnd = p + *a.size; - while (pEnd-- != p) - pEnd->~T(); - - fastMallocMatchValidateFree(a.size, Internal::AllocTypeFastNewArray); - fastFree(a.size); - } - }; - - } // namespace Internal - - template - void fastNonNullDeleteArray(T* p) - { - Internal::NonNullDeleteArrayImpl::value>::fastNonNullDeleteArray(p); - } - - -} // namespace WTF - -using WTF::fastDeleteSkippingDestructor; - -#endif // FastAllocBase_h diff --git a/masm/wtf/FastMalloc.h b/masm/wtf/FastMalloc.h deleted file mode 100644 index 1300a8ed60..0000000000 --- a/masm/wtf/FastMalloc.h +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef WTF_FastMalloc_h -#define WTF_FastMalloc_h - -#include -#include -#include -#include - -namespace WTF { - - // These functions call CRASH() if an allocation fails. - WTF_EXPORT_PRIVATE void* fastMalloc(size_t); - WTF_EXPORT_PRIVATE void* fastZeroedMalloc(size_t); - WTF_EXPORT_PRIVATE void* fastCalloc(size_t numElements, size_t elementSize); - WTF_EXPORT_PRIVATE void* fastRealloc(void*, size_t); - WTF_EXPORT_PRIVATE char* fastStrDup(const char*); - WTF_EXPORT_PRIVATE size_t fastMallocSize(const void*); - - struct TryMallocReturnValue { - TryMallocReturnValue(void* data) - : m_data(data) - { - } - TryMallocReturnValue(const TryMallocReturnValue& source) - : m_data(source.m_data) - { - source.m_data = 0; - } - ~TryMallocReturnValue() { ASSERT(!m_data); } - template bool getValue(T& data) WARN_UNUSED_RETURN; - template operator PossiblyNull() - { - T value; - getValue(value); - return PossiblyNull(value); - } - private: - mutable void* m_data; - }; - - template bool TryMallocReturnValue::getValue(T& data) - { - union u { void* data; T target; } res; - res.data = m_data; - data = res.target; - bool returnValue = !!m_data; - m_data = 0; - return returnValue; - } - - WTF_EXPORT_PRIVATE TryMallocReturnValue tryFastMalloc(size_t n); - TryMallocReturnValue tryFastZeroedMalloc(size_t n); - WTF_EXPORT_PRIVATE TryMallocReturnValue tryFastCalloc(size_t n_elements, size_t element_size); - WTF_EXPORT_PRIVATE TryMallocReturnValue tryFastRealloc(void* p, size_t n); - - WTF_EXPORT_PRIVATE void fastFree(void*); - -#ifndef NDEBUG - WTF_EXPORT_PRIVATE void fastMallocForbid(); - WTF_EXPORT_PRIVATE void fastMallocAllow(); -#endif - - WTF_EXPORT_PRIVATE void releaseFastMallocFreeMemory(); - - struct FastMallocStatistics { - size_t reservedVMBytes; - size_t committedVMBytes; - size_t freeListBytes; - }; - WTF_EXPORT_PRIVATE FastMallocStatistics fastMallocStatistics(); - - // This defines a type which holds an unsigned integer and is the same - // size as the minimally aligned memory allocation. - typedef unsigned long long AllocAlignmentInteger; - - namespace Internal { - enum AllocType { // Start with an unusual number instead of zero, because zero is common. - AllocTypeMalloc = 0x375d6750, // Encompasses fastMalloc, fastZeroedMalloc, fastCalloc, fastRealloc. - AllocTypeClassNew, // Encompasses class operator new from FastAllocBase. - AllocTypeClassNewArray, // Encompasses class operator new[] from FastAllocBase. - AllocTypeFastNew, // Encompasses fastNew. - AllocTypeFastNewArray, // Encompasses fastNewArray. - AllocTypeNew, // Encompasses global operator new. - AllocTypeNewArray // Encompasses global operator new[]. - }; - - enum { - ValidationPrefix = 0xf00df00d, - ValidationSuffix = 0x0badf00d - }; - - typedef unsigned ValidationTag; - - struct ValidationHeader { - AllocType m_type; - unsigned m_size; - ValidationTag m_prefix; - unsigned m_alignment; - }; - - static const int ValidationBufferSize = sizeof(ValidationHeader) + sizeof(ValidationTag); - } - -#if ENABLE(WTF_MALLOC_VALIDATION) - - // Malloc validation is a scheme whereby a tag is attached to an - // allocation which identifies how it was originally allocated. - // This allows us to verify that the freeing operation matches the - // allocation operation. If memory is allocated with operator new[] - // but freed with free or delete, this system would detect that. - // In the implementation here, the tag is an integer prepended to - // the allocation memory which is assigned one of the AllocType - // enumeration values. An alternative implementation of this - // scheme could store the tag somewhere else or ignore it. - // Users of FastMalloc don't need to know or care how this tagging - // is implemented. - - namespace Internal { - - // Handle a detected alloc/free mismatch. By default this calls CRASH(). - void fastMallocMatchFailed(void* p); - - inline ValidationHeader* fastMallocValidationHeader(void* p) - { - return reinterpret_cast(static_cast(p) - sizeof(ValidationHeader)); - } - - inline ValidationTag* fastMallocValidationSuffix(void* p) - { - ValidationHeader* header = fastMallocValidationHeader(p); - if (header->m_prefix != static_cast(ValidationPrefix)) - fastMallocMatchFailed(p); - - return reinterpret_cast(static_cast(p) + header->m_size); - } - - // Return the AllocType tag associated with the allocated block p. - inline AllocType fastMallocMatchValidationType(void* p) - { - return fastMallocValidationHeader(p)->m_type; - } - - // Set the AllocType tag to be associaged with the allocated block p. - inline void setFastMallocMatchValidationType(void* p, AllocType allocType) - { - fastMallocValidationHeader(p)->m_type = allocType; - } - - } // namespace Internal - - // This is a higher level function which is used by FastMalloc-using code. - inline void fastMallocMatchValidateMalloc(void* p, Internal::AllocType allocType) - { - if (!p) - return; - - Internal::setFastMallocMatchValidationType(p, allocType); - } - - // This is a higher level function which is used by FastMalloc-using code. - inline void fastMallocMatchValidateFree(void* p, Internal::AllocType) - { - if (!p) - return; - - Internal::ValidationHeader* header = Internal::fastMallocValidationHeader(p); - if (header->m_prefix != static_cast(Internal::ValidationPrefix)) - Internal::fastMallocMatchFailed(p); - - if (*Internal::fastMallocValidationSuffix(p) != Internal::ValidationSuffix) - Internal::fastMallocMatchFailed(p); - - Internal::setFastMallocMatchValidationType(p, Internal::AllocTypeMalloc); // Set it to this so that fastFree thinks it's OK. - } - - inline void fastMallocValidate(void* p) - { - if (!p) - return; - - Internal::ValidationHeader* header = Internal::fastMallocValidationHeader(p); - if (header->m_prefix != static_cast(Internal::ValidationPrefix)) - Internal::fastMallocMatchFailed(p); - - if (*Internal::fastMallocValidationSuffix(p) != Internal::ValidationSuffix) - Internal::fastMallocMatchFailed(p); - } - -#else - - inline void fastMallocMatchValidateMalloc(void*, Internal::AllocType) - { - } - - inline void fastMallocMatchValidateFree(void*, Internal::AllocType) - { - } - -#endif - -} // namespace WTF - -using WTF::fastCalloc; -using WTF::fastFree; -using WTF::fastMalloc; -using WTF::fastMallocSize; -using WTF::fastRealloc; -using WTF::fastStrDup; -using WTF::fastZeroedMalloc; -using WTF::tryFastCalloc; -using WTF::tryFastMalloc; -using WTF::tryFastRealloc; -using WTF::tryFastZeroedMalloc; - -#ifndef NDEBUG -using WTF::fastMallocForbid; -using WTF::fastMallocAllow; -#endif - -#if COMPILER(GCC) && OS(DARWIN) -#define WTF_PRIVATE_INLINE __private_extern__ inline __attribute__((always_inline)) -#elif COMPILER(GCC) -#define WTF_PRIVATE_INLINE inline __attribute__((always_inline)) -#elif COMPILER(MSVC) || COMPILER(RVCT) -#define WTF_PRIVATE_INLINE __forceinline -#else -#define WTF_PRIVATE_INLINE inline -#endif - -#if !defined(_CRTDBG_MAP_ALLOC) && !(defined(USE_SYSTEM_MALLOC) && USE_SYSTEM_MALLOC) - -// The nothrow functions here are actually not all that helpful, because fastMalloc will -// call CRASH() rather than returning 0, and returning 0 is what nothrow is all about. -// But since WebKit code never uses exceptions or nothrow at all, this is probably OK. -// Long term we will adopt FastAllocBase.h everywhere, and and replace this with -// debug-only code to make sure we don't use the system malloc via the default operator -// new by accident. - -#if ENABLE(GLOBAL_FASTMALLOC_NEW) - -#if COMPILER(MSVC) -#pragma warning(push) -#pragma warning(disable: 4290) // Disable the C++ exception specification ignored warning. -#endif -WTF_PRIVATE_INLINE void* operator new(size_t size) throw (std::bad_alloc) { return fastMalloc(size); } -WTF_PRIVATE_INLINE void* operator new(size_t size, const std::nothrow_t&) throw() { return fastMalloc(size); } -WTF_PRIVATE_INLINE void operator delete(void* p) throw() { fastFree(p); } -WTF_PRIVATE_INLINE void operator delete(void* p, const std::nothrow_t&) throw() { fastFree(p); } -WTF_PRIVATE_INLINE void* operator new[](size_t size) throw (std::bad_alloc) { return fastMalloc(size); } -WTF_PRIVATE_INLINE void* operator new[](size_t size, const std::nothrow_t&) throw() { return fastMalloc(size); } -WTF_PRIVATE_INLINE void operator delete[](void* p) throw() { fastFree(p); } -WTF_PRIVATE_INLINE void operator delete[](void* p, const std::nothrow_t&) throw() { fastFree(p); } -#if COMPILER(MSVC) -#pragma warning(pop) -#endif - -#endif - -#endif - -#endif /* WTF_FastMalloc_h */ -- cgit v1.2.3 From e47237d334c8c5c13526c44f59cfbca54b769e76 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 3 Jan 2013 14:26:51 +0100 Subject: Replace WTF::Vector with a tiny std::vector wrapper Change-Id: Ieb371c42790b19ee1f12b3622041ac139e1e03c2 Reviewed-by: Lars Knoll --- masm/assembler/AssemblerBuffer.h | 4 +- masm/stubs/wtf/Vector.h | 66 +++ masm/wtf/ValueCheck.h | 53 -- masm/wtf/Vector.h | 1202 -------------------------------------- masm/wtf/VectorTraits.h | 104 ---- 5 files changed, 68 insertions(+), 1361 deletions(-) create mode 100644 masm/stubs/wtf/Vector.h delete mode 100644 masm/wtf/ValueCheck.h delete mode 100644 masm/wtf/Vector.h delete mode 100644 masm/wtf/VectorTraits.h diff --git a/masm/assembler/AssemblerBuffer.h b/masm/assembler/AssemblerBuffer.h index 6bc1b3924d..bc52801ba7 100644 --- a/masm/assembler/AssemblerBuffer.h +++ b/masm/assembler/AssemblerBuffer.h @@ -65,7 +65,7 @@ namespace JSC { public: AssemblerBuffer() : m_storage(inlineCapacity) - , m_buffer(m_storage.begin()) + , m_buffer(&(*m_storage.begin())) , m_capacity(inlineCapacity) , m_index(0) { @@ -164,7 +164,7 @@ namespace JSC { m_capacity += m_capacity / 2 + extraCapacity; m_storage.grow(m_capacity); - m_buffer = m_storage.begin(); + m_buffer = &(*m_storage.begin()); } private: diff --git a/masm/stubs/wtf/Vector.h b/masm/stubs/wtf/Vector.h new file mode 100644 index 0000000000..2682824da8 --- /dev/null +++ b/masm/stubs/wtf/Vector.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef VECTOR_H +#define VECTOR_H + +#include +#include + +namespace WTF { + +template +class Vector : public std::vector { +public: + Vector() {} + Vector(int initialSize) : std::vector(initialSize) {} + + inline void append(const T& value) + { this->push_back(value); } + + inline void grow(size_t size) + { this->resize(size); } +}; + +} + +using WTF::Vector; + +#endif // VECTOR_H diff --git a/masm/wtf/ValueCheck.h b/masm/wtf/ValueCheck.h deleted file mode 100644 index 2a86eb0f23..0000000000 --- a/masm/wtf/ValueCheck.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ValueCheck_h -#define ValueCheck_h - -#include - -namespace WTF { - -template struct ValueCheck { - typedef T TraitType; - static void checkConsistency(const T&) { } -}; - -#if !ASSERT_DISABLED -template struct ValueCheck { - typedef P* TraitType; - static void checkConsistency(const P* p) - { - if (!p) - return; - ASSERT(fastMallocSize(p)); - ValueCheck

::checkConsistency(*p); - } -}; -#endif - -} - -#endif // ValueCheck_h diff --git a/masm/wtf/Vector.h b/masm/wtf/Vector.h deleted file mode 100644 index d2dedefea8..0000000000 --- a/masm/wtf/Vector.h +++ /dev/null @@ -1,1202 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef WTF_Vector_h -#define WTF_Vector_h - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace WTF { - - template - struct VectorDestructor; - - template - struct VectorDestructor - { - static void destruct(T*, T*) {} - }; - - template - struct VectorDestructor - { - static void destruct(T* begin, T* end) - { - for (T* cur = begin; cur != end; ++cur) - cur->~T(); - } - }; - - template - struct VectorInitializer; - - template - struct VectorInitializer - { - static void initialize(T*, T*) {} - }; - - template - struct VectorInitializer - { - static void initialize(T* begin, T* end) - { - for (T* cur = begin; cur != end; ++cur) - new (NotNull, cur) T; - } - }; - - template - struct VectorInitializer - { - static void initialize(T* begin, T* end) - { - memset(begin, 0, reinterpret_cast(end) - reinterpret_cast(begin)); - } - }; - - template - struct VectorMover; - - template - struct VectorMover - { - static void move(const T* src, const T* srcEnd, T* dst) - { - while (src != srcEnd) { - new (NotNull, dst) T(*src); -#if COMPILER(SUNCC) && __SUNPRO_CC <= 0x590 - const_cast(src)->~T(); // Work around obscure SunCC 12 compiler bug. -#else - src->~T(); -#endif - ++dst; - ++src; - } - } - static void moveOverlapping(const T* src, const T* srcEnd, T* dst) - { - if (src > dst) - move(src, srcEnd, dst); - else { - T* dstEnd = dst + (srcEnd - src); - while (src != srcEnd) { - --srcEnd; - --dstEnd; - new (NotNull, dstEnd) T(*srcEnd); - srcEnd->~T(); - } - } - } - }; - - template - struct VectorMover - { - static void move(const T* src, const T* srcEnd, T* dst) - { - memcpy(dst, src, reinterpret_cast(srcEnd) - reinterpret_cast(src)); - } - static void moveOverlapping(const T* src, const T* srcEnd, T* dst) - { - memmove(dst, src, reinterpret_cast(srcEnd) - reinterpret_cast(src)); - } - }; - - template - struct VectorCopier; - - template - struct VectorCopier - { - static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) - { - while (src != srcEnd) { - new (NotNull, dst) T(*src); - ++dst; - ++src; - } - } - }; - - template - struct VectorCopier - { - static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) - { - memcpy(dst, src, reinterpret_cast(srcEnd) - reinterpret_cast(src)); - } - }; - - template - struct VectorFiller; - - template - struct VectorFiller - { - static void uninitializedFill(T* dst, T* dstEnd, const T& val) - { - while (dst != dstEnd) { - new (NotNull, dst) T(val); - ++dst; - } - } - }; - - template - struct VectorFiller - { - static void uninitializedFill(T* dst, T* dstEnd, const T& val) - { - ASSERT(sizeof(T) == sizeof(char)); -#if COMPILER(GCC) && defined(_FORTIFY_SOURCE) - if (!__builtin_constant_p(dstEnd - dst) || (!(dstEnd - dst))) -#endif - memset(dst, val, dstEnd - dst); - } - }; - - template - struct VectorComparer; - - template - struct VectorComparer - { - static bool compare(const T* a, const T* b, size_t size) - { - for (size_t i = 0; i < size; ++i) - if (!(a[i] == b[i])) - return false; - return true; - } - }; - - template - struct VectorComparer - { - static bool compare(const T* a, const T* b, size_t size) - { - return memcmp(a, b, sizeof(T) * size) == 0; - } - }; - - template - struct VectorTypeOperations - { - static void destruct(T* begin, T* end) - { - VectorDestructor::needsDestruction, T>::destruct(begin, end); - } - - static void initialize(T* begin, T* end) - { - VectorInitializer::needsInitialization, VectorTraits::canInitializeWithMemset, T>::initialize(begin, end); - } - - static void move(const T* src, const T* srcEnd, T* dst) - { - VectorMover::canMoveWithMemcpy, T>::move(src, srcEnd, dst); - } - - static void moveOverlapping(const T* src, const T* srcEnd, T* dst) - { - VectorMover::canMoveWithMemcpy, T>::moveOverlapping(src, srcEnd, dst); - } - - static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) - { - VectorCopier::canCopyWithMemcpy, T>::uninitializedCopy(src, srcEnd, dst); - } - - static void uninitializedFill(T* dst, T* dstEnd, const T& val) - { - VectorFiller::canFillWithMemset, T>::uninitializedFill(dst, dstEnd, val); - } - - static bool compare(const T* a, const T* b, size_t size) - { - return VectorComparer::canCompareWithMemcmp, T>::compare(a, b, size); - } - }; - - template - class VectorBufferBase { - WTF_MAKE_NONCOPYABLE(VectorBufferBase); - public: - void allocateBuffer(size_t newCapacity) - { - ASSERT(newCapacity); - m_capacity = newCapacity; - if (newCapacity > std::numeric_limits::max() / sizeof(T)) - CRASH(); - m_buffer = static_cast(fastMalloc(newCapacity * sizeof(T))); - } - - bool tryAllocateBuffer(size_t newCapacity) - { - ASSERT(newCapacity); - if (newCapacity > std::numeric_limits::max() / sizeof(T)) - return false; - - T* newBuffer; - if (tryFastMalloc(newCapacity * sizeof(T)).getValue(newBuffer)) { - m_capacity = newCapacity; - m_buffer = newBuffer; - return true; - } - return false; - } - - bool shouldReallocateBuffer(size_t newCapacity) const - { - return VectorTraits::canMoveWithMemcpy && m_capacity && newCapacity; - } - - void reallocateBuffer(size_t newCapacity) - { - ASSERT(shouldReallocateBuffer(newCapacity)); - m_capacity = newCapacity; - if (newCapacity > std::numeric_limits::max() / sizeof(T)) - CRASH(); - m_buffer = static_cast(fastRealloc(m_buffer, newCapacity * sizeof(T))); - } - - void deallocateBuffer(T* bufferToDeallocate) - { - if (!bufferToDeallocate) - return; - - if (m_buffer == bufferToDeallocate) { - m_buffer = 0; - m_capacity = 0; - } - - fastFree(bufferToDeallocate); - } - - T* buffer() { return m_buffer; } - const T* buffer() const { return m_buffer; } - size_t capacity() const { return m_capacity; } - - T* releaseBuffer() - { - T* buffer = m_buffer; - m_buffer = 0; - m_capacity = 0; - return buffer; - } - - protected: - VectorBufferBase() - : m_buffer(0) - , m_capacity(0) - { - } - - VectorBufferBase(T* buffer, size_t capacity) - : m_buffer(buffer) - , m_capacity(capacity) - { - } - - ~VectorBufferBase() - { - // FIXME: It would be nice to find a way to ASSERT that m_buffer hasn't leaked here. - } - - T* m_buffer; - size_t m_capacity; - }; - - template - class VectorBuffer; - - template - class VectorBuffer : private VectorBufferBase { - private: - typedef VectorBufferBase Base; - public: - VectorBuffer() - { - } - - VectorBuffer(size_t capacity) - { - // Calling malloc(0) might take a lock and may actually do an - // allocation on some systems. - if (capacity) - allocateBuffer(capacity); - } - - ~VectorBuffer() - { - deallocateBuffer(buffer()); - } - - void swap(VectorBuffer& other) - { - std::swap(m_buffer, other.m_buffer); - std::swap(m_capacity, other.m_capacity); - } - - void restoreInlineBufferIfNeeded() { } - - using Base::allocateBuffer; - using Base::tryAllocateBuffer; - using Base::shouldReallocateBuffer; - using Base::reallocateBuffer; - using Base::deallocateBuffer; - - using Base::buffer; - using Base::capacity; - - using Base::releaseBuffer; - private: - using Base::m_buffer; - using Base::m_capacity; - }; - - template - class VectorBuffer : private VectorBufferBase { - WTF_MAKE_NONCOPYABLE(VectorBuffer); - private: - typedef VectorBufferBase Base; - public: - VectorBuffer() - : Base(inlineBuffer(), inlineCapacity) - { - } - - VectorBuffer(size_t capacity) - : Base(inlineBuffer(), inlineCapacity) - { - if (capacity > inlineCapacity) - Base::allocateBuffer(capacity); - } - - ~VectorBuffer() - { - deallocateBuffer(buffer()); - } - - void allocateBuffer(size_t newCapacity) - { - // FIXME: This should ASSERT(!m_buffer) to catch misuse/leaks. - if (newCapacity > inlineCapacity) - Base::allocateBuffer(newCapacity); - else { - m_buffer = inlineBuffer(); - m_capacity = inlineCapacity; - } - } - - bool tryAllocateBuffer(size_t newCapacity) - { - if (newCapacity > inlineCapacity) - return Base::tryAllocateBuffer(newCapacity); - m_buffer = inlineBuffer(); - m_capacity = inlineCapacity; - return true; - } - - void deallocateBuffer(T* bufferToDeallocate) - { - if (bufferToDeallocate == inlineBuffer()) - return; - Base::deallocateBuffer(bufferToDeallocate); - } - - bool shouldReallocateBuffer(size_t newCapacity) const - { - // We cannot reallocate the inline buffer. - return Base::shouldReallocateBuffer(newCapacity) && std::min(m_capacity, newCapacity) > inlineCapacity; - } - - void reallocateBuffer(size_t newCapacity) - { - ASSERT(shouldReallocateBuffer(newCapacity)); - Base::reallocateBuffer(newCapacity); - } - - void swap(VectorBuffer& other) - { - if (buffer() == inlineBuffer() && other.buffer() == other.inlineBuffer()) { - WTF::swap(m_inlineBuffer, other.m_inlineBuffer); - std::swap(m_capacity, other.m_capacity); - } else if (buffer() == inlineBuffer()) { - m_buffer = other.m_buffer; - other.m_buffer = other.inlineBuffer(); - WTF::swap(m_inlineBuffer, other.m_inlineBuffer); - std::swap(m_capacity, other.m_capacity); - } else if (other.buffer() == other.inlineBuffer()) { - other.m_buffer = m_buffer; - m_buffer = inlineBuffer(); - WTF::swap(m_inlineBuffer, other.m_inlineBuffer); - std::swap(m_capacity, other.m_capacity); - } else { - std::swap(m_buffer, other.m_buffer); - std::swap(m_capacity, other.m_capacity); - } - } - - void restoreInlineBufferIfNeeded() - { - if (m_buffer) - return; - m_buffer = inlineBuffer(); - m_capacity = inlineCapacity; - } - - using Base::buffer; - using Base::capacity; - - T* releaseBuffer() - { - if (buffer() == inlineBuffer()) - return 0; - return Base::releaseBuffer(); - } - - private: - using Base::m_buffer; - using Base::m_capacity; - - static const size_t m_inlineBufferSize = inlineCapacity * sizeof(T); - T* inlineBuffer() { return reinterpret_cast_ptr(m_inlineBuffer.buffer); } - const T* inlineBuffer() const { return reinterpret_cast_ptr(m_inlineBuffer.buffer); } - - AlignedBuffer m_inlineBuffer; - }; - - template - class Vector { - WTF_MAKE_FAST_ALLOCATED; - private: - typedef VectorBuffer Buffer; - typedef VectorTypeOperations TypeOperations; - - public: - typedef T ValueType; - - typedef T* iterator; - typedef const T* const_iterator; - typedef std::reverse_iterator reverse_iterator; - typedef std::reverse_iterator const_reverse_iterator; - - Vector() - : m_size(0) - { - } - - explicit Vector(size_t size) - : m_size(size) - , m_buffer(size) - { - if (begin()) - TypeOperations::initialize(begin(), end()); - } - - ~Vector() - { - if (m_size) - shrink(0); - } - - Vector(const Vector&); - template - Vector(const Vector&); - - Vector& operator=(const Vector&); - template - Vector& operator=(const Vector&); - -#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) - Vector(Vector&&); - Vector& operator=(Vector&&); -#endif - - size_t size() const { return m_size; } - size_t capacity() const { return m_buffer.capacity(); } - bool isEmpty() const { return !size(); } - - T& at(size_t i) - { - ASSERT(i < size()); - return m_buffer.buffer()[i]; - } - const T& at(size_t i) const - { - ASSERT(i < size()); - return m_buffer.buffer()[i]; - } - - T& operator[](size_t i) { return at(i); } - const T& operator[](size_t i) const { return at(i); } - - T* data() { return m_buffer.buffer(); } - const T* data() const { return m_buffer.buffer(); } - T** dataSlot() { return m_buffer.bufferSlot(); } - - iterator begin() { return data(); } - iterator end() { return begin() + m_size; } - const_iterator begin() const { return data(); } - const_iterator end() const { return begin() + m_size; } - - reverse_iterator rbegin() { return reverse_iterator(end()); } - reverse_iterator rend() { return reverse_iterator(begin()); } - const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } - const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } - - T& first() { return at(0); } - const T& first() const { return at(0); } - T& last() { return at(size() - 1); } - const T& last() const { return at(size() - 1); } - - template bool contains(const U&) const; - template size_t find(const U&) const; - template size_t reverseFind(const U&) const; - - void shrink(size_t size); - void grow(size_t size); - void resize(size_t size); - void reserveCapacity(size_t newCapacity); - bool tryReserveCapacity(size_t newCapacity); - void reserveInitialCapacity(size_t initialCapacity); - void shrinkCapacity(size_t newCapacity); - void shrinkToFit() { shrinkCapacity(size()); } - - void clear() { shrinkCapacity(0); } - - template void append(const U*, size_t); - template void append(const U&); - template void uncheckedAppend(const U& val); - template void append(const Vector&); - template void appendVector(const Vector&); - template bool tryAppend(const U*, size_t); - - template void insert(size_t position, const U*, size_t); - template void insert(size_t position, const U&); - template void insert(size_t position, const Vector&); - - template void prepend(const U*, size_t); - template void prepend(const U&); - template void prepend(const Vector&); - - void remove(size_t position); - void remove(size_t position, size_t length); - - void removeLast() - { - ASSERT(!isEmpty()); - shrink(size() - 1); - } - - Vector(size_t size, const T& val) - : m_size(size) - , m_buffer(size) - { - if (begin()) - TypeOperations::uninitializedFill(begin(), end(), val); - } - - void fill(const T&, size_t); - void fill(const T& val) { fill(val, size()); } - - template void appendRange(Iterator start, Iterator end); - - T* releaseBuffer(); - - void swap(Vector& other) - { - std::swap(m_size, other.m_size); - m_buffer.swap(other.m_buffer); - } - - void reverse(); - - void checkConsistency(); - - private: - void expandCapacity(size_t newMinCapacity); - const T* expandCapacity(size_t newMinCapacity, const T*); - bool tryExpandCapacity(size_t newMinCapacity); - const T* tryExpandCapacity(size_t newMinCapacity, const T*); - template U* expandCapacity(size_t newMinCapacity, U*); - template void appendSlowCase(const U&); - - size_t m_size; - Buffer m_buffer; - }; - - template - Vector::Vector(const Vector& other) - : m_size(other.size()) - , m_buffer(other.capacity()) - { - if (begin()) - TypeOperations::uninitializedCopy(other.begin(), other.end(), begin()); - } - - template - template - Vector::Vector(const Vector& other) - : m_size(other.size()) - , m_buffer(other.capacity()) - { - if (begin()) - TypeOperations::uninitializedCopy(other.begin(), other.end(), begin()); - } - - template - Vector& Vector::operator=(const Vector& other) - { - if (&other == this) - return *this; - - if (size() > other.size()) - shrink(other.size()); - else if (other.size() > capacity()) { - clear(); - reserveCapacity(other.size()); - if (!begin()) - return *this; - } - -// Works around an assert in VS2010. See https://connect.microsoft.com/VisualStudio/feedback/details/558044/std-copy-should-not-check-dest-when-first-last -#if COMPILER(MSVC) && defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL - if (!begin()) - return *this; -#endif - - std::copy(other.begin(), other.begin() + size(), begin()); - TypeOperations::uninitializedCopy(other.begin() + size(), other.end(), end()); - m_size = other.size(); - - return *this; - } - - inline bool typelessPointersAreEqual(const void* a, const void* b) { return a == b; } - - template - template - Vector& Vector::operator=(const Vector& other) - { - // If the inline capacities match, we should call the more specific - // template. If the inline capacities don't match, the two objects - // shouldn't be allocated the same address. - ASSERT(!typelessPointersAreEqual(&other, this)); - - if (size() > other.size()) - shrink(other.size()); - else if (other.size() > capacity()) { - clear(); - reserveCapacity(other.size()); - if (!begin()) - return *this; - } - -// Works around an assert in VS2010. See https://connect.microsoft.com/VisualStudio/feedback/details/558044/std-copy-should-not-check-dest-when-first-last -#if COMPILER(MSVC) && defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL - if (!begin()) - return *this; -#endif - - std::copy(other.begin(), other.begin() + size(), begin()); - TypeOperations::uninitializedCopy(other.begin() + size(), other.end(), end()); - m_size = other.size(); - - return *this; - } - -#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) - template - Vector::Vector(Vector&& other) - : m_size(0) - { - // It's a little weird to implement a move constructor using swap but this way we - // don't have to add a move constructor to VectorBuffer. - swap(other); - } - - template - Vector& Vector::operator=(Vector&& other) - { - swap(other); - return *this; - } -#endif - - template - template - bool Vector::contains(const U& value) const - { - return find(value) != notFound; - } - - template - template - size_t Vector::find(const U& value) const - { - for (size_t i = 0; i < size(); ++i) { - if (at(i) == value) - return i; - } - return notFound; - } - - template - template - size_t Vector::reverseFind(const U& value) const - { - for (size_t i = 1; i <= size(); ++i) { - const size_t index = size() - i; - if (at(index) == value) - return index; - } - return notFound; - } - - template - void Vector::fill(const T& val, size_t newSize) - { - if (size() > newSize) - shrink(newSize); - else if (newSize > capacity()) { - clear(); - reserveCapacity(newSize); - if (!begin()) - return; - } - - std::fill(begin(), end(), val); - TypeOperations::uninitializedFill(end(), begin() + newSize, val); - m_size = newSize; - } - - template - template - void Vector::appendRange(Iterator start, Iterator end) - { - for (Iterator it = start; it != end; ++it) - append(*it); - } - - template - void Vector::expandCapacity(size_t newMinCapacity) - { - reserveCapacity(std::max(newMinCapacity, std::max(static_cast(16), capacity() + capacity() / 4 + 1))); - } - - template - const T* Vector::expandCapacity(size_t newMinCapacity, const T* ptr) - { - if (ptr < begin() || ptr >= end()) { - expandCapacity(newMinCapacity); - return ptr; - } - size_t index = ptr - begin(); - expandCapacity(newMinCapacity); - return begin() + index; - } - - template - bool Vector::tryExpandCapacity(size_t newMinCapacity) - { - return tryReserveCapacity(std::max(newMinCapacity, std::max(static_cast(16), capacity() + capacity() / 4 + 1))); - } - - template - const T* Vector::tryExpandCapacity(size_t newMinCapacity, const T* ptr) - { - if (ptr < begin() || ptr >= end()) { - if (!tryExpandCapacity(newMinCapacity)) - return 0; - return ptr; - } - size_t index = ptr - begin(); - if (!tryExpandCapacity(newMinCapacity)) - return 0; - return begin() + index; - } - - template template - inline U* Vector::expandCapacity(size_t newMinCapacity, U* ptr) - { - expandCapacity(newMinCapacity); - return ptr; - } - - template - inline void Vector::resize(size_t size) - { - if (size <= m_size) - TypeOperations::destruct(begin() + size, end()); - else { - if (size > capacity()) - expandCapacity(size); - if (begin()) - TypeOperations::initialize(end(), begin() + size); - } - - m_size = size; - } - - template - void Vector::shrink(size_t size) - { - ASSERT(size <= m_size); - TypeOperations::destruct(begin() + size, end()); - m_size = size; - } - - template - void Vector::grow(size_t size) - { - ASSERT(size >= m_size); - if (size > capacity()) - expandCapacity(size); - if (begin()) - TypeOperations::initialize(end(), begin() + size); - m_size = size; - } - - template - void Vector::reserveCapacity(size_t newCapacity) - { - if (newCapacity <= capacity()) - return; - T* oldBuffer = begin(); - T* oldEnd = end(); - m_buffer.allocateBuffer(newCapacity); - if (begin()) - TypeOperations::move(oldBuffer, oldEnd, begin()); - m_buffer.deallocateBuffer(oldBuffer); - } - - template - bool Vector::tryReserveCapacity(size_t newCapacity) - { - if (newCapacity <= capacity()) - return true; - T* oldBuffer = begin(); - T* oldEnd = end(); - if (!m_buffer.tryAllocateBuffer(newCapacity)) - return false; - ASSERT(begin()); - TypeOperations::move(oldBuffer, oldEnd, begin()); - m_buffer.deallocateBuffer(oldBuffer); - return true; - } - - template - inline void Vector::reserveInitialCapacity(size_t initialCapacity) - { - ASSERT(!m_size); - ASSERT(capacity() == inlineCapacity); - if (initialCapacity > inlineCapacity) - m_buffer.allocateBuffer(initialCapacity); - } - - template - void Vector::shrinkCapacity(size_t newCapacity) - { - if (newCapacity >= capacity()) - return; - - if (newCapacity < size()) - shrink(newCapacity); - - T* oldBuffer = begin(); - if (newCapacity > 0) { - if (m_buffer.shouldReallocateBuffer(newCapacity)) { - m_buffer.reallocateBuffer(newCapacity); - return; - } - - T* oldEnd = end(); - m_buffer.allocateBuffer(newCapacity); - if (begin() != oldBuffer) - TypeOperations::move(oldBuffer, oldEnd, begin()); - } - - m_buffer.deallocateBuffer(oldBuffer); - m_buffer.restoreInlineBufferIfNeeded(); - } - - // Templatizing these is better than just letting the conversion happen implicitly, - // because for instance it allows a PassRefPtr to be appended to a RefPtr vector - // without refcount thrash. - - template template - void Vector::append(const U* data, size_t dataSize) - { - size_t newSize = m_size + dataSize; - if (newSize > capacity()) { - data = expandCapacity(newSize, data); - if (!begin()) - return; - } - if (newSize < m_size) - CRASH(); - T* dest = end(); - for (size_t i = 0; i < dataSize; ++i) - new (NotNull, &dest[i]) T(data[i]); - m_size = newSize; - } - - template template - bool Vector::tryAppend(const U* data, size_t dataSize) - { - size_t newSize = m_size + dataSize; - if (newSize > capacity()) { - data = tryExpandCapacity(newSize, data); - if (!data) - return false; - ASSERT(begin()); - } - if (newSize < m_size) - return false; - T* dest = end(); - for (size_t i = 0; i < dataSize; ++i) - new (NotNull, &dest[i]) T(data[i]); - m_size = newSize; - return true; - } - - template template - ALWAYS_INLINE void Vector::append(const U& val) - { - if (size() != capacity()) { - new (NotNull, end()) T(val); - ++m_size; - return; - } - - appendSlowCase(val); - } - - template template - void Vector::appendSlowCase(const U& val) - { - ASSERT(size() == capacity()); - - const U* ptr = &val; - ptr = expandCapacity(size() + 1, ptr); - if (!begin()) - return; - - new (NotNull, end()) T(*ptr); - ++m_size; - } - - // This version of append saves a branch in the case where you know that the - // vector's capacity is large enough for the append to succeed. - - template template - inline void Vector::uncheckedAppend(const U& val) - { - ASSERT(size() < capacity()); - const U* ptr = &val; - new (NotNull, end()) T(*ptr); - ++m_size; - } - - // This method should not be called append, a better name would be appendElements. - // It could also be eliminated entirely, and call sites could just use - // appendRange(val.begin(), val.end()). - template template - inline void Vector::append(const Vector& val) - { - append(val.begin(), val.size()); - } - - template template - inline void Vector::appendVector(const Vector& val) - { - append(val.begin(), val.size()); - } - - template template - void Vector::insert(size_t position, const U* data, size_t dataSize) - { - ASSERT(position <= size()); - size_t newSize = m_size + dataSize; - if (newSize > capacity()) { - data = expandCapacity(newSize, data); - if (!begin()) - return; - } - if (newSize < m_size) - CRASH(); - T* spot = begin() + position; - TypeOperations::moveOverlapping(spot, end(), spot + dataSize); - for (size_t i = 0; i < dataSize; ++i) - new (NotNull, &spot[i]) T(data[i]); - m_size = newSize; - } - - template template - inline void Vector::insert(size_t position, const U& val) - { - ASSERT(position <= size()); - const U* data = &val; - if (size() == capacity()) { - data = expandCapacity(size() + 1, data); - if (!begin()) - return; - } - T* spot = begin() + position; - TypeOperations::moveOverlapping(spot, end(), spot + 1); - new (NotNull, spot) T(*data); - ++m_size; - } - - template template - inline void Vector::insert(size_t position, const Vector& val) - { - insert(position, val.begin(), val.size()); - } - - template template - void Vector::prepend(const U* data, size_t dataSize) - { - insert(0, data, dataSize); - } - - template template - inline void Vector::prepend(const U& val) - { - insert(0, val); - } - - template template - inline void Vector::prepend(const Vector& val) - { - insert(0, val.begin(), val.size()); - } - - template - inline void Vector::remove(size_t position) - { - ASSERT(position < size()); - T* spot = begin() + position; - spot->~T(); - TypeOperations::moveOverlapping(spot + 1, end(), spot); - --m_size; - } - - template - inline void Vector::remove(size_t position, size_t length) - { - ASSERT(position <= size()); - ASSERT(position + length <= size()); - T* beginSpot = begin() + position; - T* endSpot = beginSpot + length; - TypeOperations::destruct(beginSpot, endSpot); - TypeOperations::moveOverlapping(endSpot, end(), beginSpot); - m_size -= length; - } - - template - inline void Vector::reverse() - { - for (size_t i = 0; i < m_size / 2; ++i) - std::swap(at(i), at(m_size - 1 - i)); - } - - template - inline T* Vector::releaseBuffer() - { - T* buffer = m_buffer.releaseBuffer(); - if (inlineCapacity && !buffer && m_size) { - // If the vector had some data, but no buffer to release, - // that means it was using the inline buffer. In that case, - // we create a brand new buffer so the caller always gets one. - size_t bytes = m_size * sizeof(T); - buffer = static_cast(fastMalloc(bytes)); - memcpy(buffer, data(), bytes); - } - m_size = 0; - return buffer; - } - - template - inline void Vector::checkConsistency() - { -#if !ASSERT_DISABLED - for (size_t i = 0; i < size(); ++i) - ValueCheck::checkConsistency(at(i)); -#endif - } - - template - void deleteAllValues(const Vector& collection) - { - typedef typename Vector::const_iterator iterator; - iterator end = collection.end(); - for (iterator it = collection.begin(); it != end; ++it) - delete *it; - } - - template - inline void swap(Vector& a, Vector& b) - { - a.swap(b); - } - - template - bool operator==(const Vector& a, const Vector& b) - { - if (a.size() != b.size()) - return false; - - return VectorTypeOperations::compare(a.data(), b.data(), a.size()); - } - - template - inline bool operator!=(const Vector& a, const Vector& b) - { - return !(a == b); - } - -#if !ASSERT_DISABLED - template struct ValueCheck > { - typedef Vector TraitType; - static void checkConsistency(const Vector& v) - { - v.checkConsistency(); - } - }; -#endif - -} // namespace WTF - -using WTF::Vector; - -#endif // WTF_Vector_h diff --git a/masm/wtf/VectorTraits.h b/masm/wtf/VectorTraits.h deleted file mode 100644 index 678225034a..0000000000 --- a/masm/wtf/VectorTraits.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef WTF_VectorTraits_h -#define WTF_VectorTraits_h - -#include -#include -#include -#include -#include - -using std::pair; - -namespace WTF { - - class AtomicString; - - template - struct VectorTraitsBase; - - template - struct VectorTraitsBase - { - static const bool needsDestruction = true; - static const bool needsInitialization = true; - static const bool canInitializeWithMemset = false; - static const bool canMoveWithMemcpy = false; - static const bool canCopyWithMemcpy = false; - static const bool canFillWithMemset = false; - static const bool canCompareWithMemcmp = false; - }; - - template - struct VectorTraitsBase - { - static const bool needsDestruction = false; - static const bool needsInitialization = false; - static const bool canInitializeWithMemset = false; - static const bool canMoveWithMemcpy = true; - static const bool canCopyWithMemcpy = true; - static const bool canFillWithMemset = sizeof(T) == sizeof(char); - static const bool canCompareWithMemcmp = true; - }; - - template - struct VectorTraits : VectorTraitsBase::value, T> { }; - - struct SimpleClassVectorTraits : VectorTraitsBase - { - static const bool canInitializeWithMemset = true; - static const bool canMoveWithMemcpy = true; - static const bool canCompareWithMemcmp = true; - }; - - // we know OwnPtr and RefPtr are simple enough that initializing to 0 and moving with memcpy - // (and then not destructing the original) will totally work - template - struct VectorTraits > : SimpleClassVectorTraits { }; - - template - struct VectorTraits > : SimpleClassVectorTraits { }; - - template<> - struct VectorTraits : SimpleClassVectorTraits { }; - - template - struct VectorTraits > - { - typedef VectorTraits FirstTraits; - typedef VectorTraits SecondTraits; - - static const bool needsDestruction = FirstTraits::needsDestruction || SecondTraits::needsDestruction; - static const bool needsInitialization = FirstTraits::needsInitialization || SecondTraits::needsInitialization; - static const bool canInitializeWithMemset = FirstTraits::canInitializeWithMemset && SecondTraits::canInitializeWithMemset; - static const bool canMoveWithMemcpy = FirstTraits::canMoveWithMemcpy && SecondTraits::canMoveWithMemcpy; - static const bool canCopyWithMemcpy = FirstTraits::canCopyWithMemcpy && SecondTraits::canCopyWithMemcpy; - static const bool canFillWithMemset = false; - static const bool canCompareWithMemcmp = FirstTraits::canCompareWithMemcmp && SecondTraits::canCompareWithMemcmp; - }; - -} // namespace WTF - -using WTF::VectorTraits; -using WTF::SimpleClassVectorTraits; - -#endif // WTF_VectorTraits_h -- cgit v1.2.3 From 6340e86eb0e78cebd5d95b05b11644d9bb8b5a61 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 3 Jan 2013 17:11:22 +0100 Subject: Implement { get/set ... } Implement support for defining accessor properties through { ... }. Implementation for moth is pending. Change-Id: I558b6811bc5656dc0fae78c49e23155043ce9cb1 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 10 ++++++++++ qmljs_runtime.h | 1 + qv4codegen.cpp | 41 ++++++++++++++++++++++++++++++++++++++++- qv4ir.cpp | 2 ++ qv4ir_p.h | 3 ++- qv4isel_masm.cpp | 21 +++++++++++++++++++++ 6 files changed, 76 insertions(+), 2 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 293e1dbe34..73555c4c92 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -890,6 +890,16 @@ void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String * ctx->createMutableBinding(name, deletable); } +void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx) +{ + Object *o = object.asObject(); + assert(o); + + PropertyDescriptor desc; + desc.fromAccessor(getter.asFunctionObject(), setter.asFunctionObject()); + o->__defineOwnProperty__(ctx, name, &desc); +} + Value __qmljs_increment(Value value, ExecutionContext *ctx) { TRACE1(value); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index c072e4ac58..085125a9c4 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -104,6 +104,7 @@ void __qmljs_builtin_rethrow(ExecutionContext *context); void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx); void __qmljs_builtin_pop_with(ExecutionContext *ctx); void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name); +void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); // constructors Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 65e7a337ab..180d1c80e1 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1357,8 +1357,15 @@ bool Codegen::visit(NumericLiteral *ast) return false; } +struct Accessor { + IR::Function *getter; + IR::Function *setter; +}; + bool Codegen::visit(ObjectLiteral *ast) { + QMap accessorMap; + const unsigned t = _block->newTemp(); move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Object"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { @@ -1367,11 +1374,43 @@ bool Codegen::visit(ObjectLiteral *ast) Result value = expression(nv->value); move(member(_block->TEMP(t), _function->newString(name)), *value); } else if (PropertyGetterSetter *gs = AST::cast(it->assignment)) { - assert(!"todo!"); + QString name = propertyName(gs->name); + IR::Function *function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0); + if (_debugger) + _debugger->setSourceLocation(function, gs->getSetToken.startLine, gs->getSetToken.startColumn); + if (gs->type == PropertyGetterSetter::Getter) + accessorMap[name].getter = function; + else + accessorMap[name].setter = function; } else { Q_UNREACHABLE(); } } + if (!accessorMap.isEmpty()) { + const unsigned getter = _block->newTemp(); + const unsigned setter = _block->newTemp(); + for (QMap::const_iterator it = accessorMap.constBegin(); it != accessorMap.constEnd(); ++it) { + move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(IR::UndefinedType, 0)); + move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(IR::UndefinedType, 0)); + + + // __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); + IR::ExprList *args = _function->New(); + IR::ExprList *current = args; + current->expr = _block->TEMP(t); + current->next = _function->New(); + current = current->next; + current->expr = _block->NAME(it.key(), 0, 0); + current->next = _function->New(); + current = current->next; + current->expr = _block->TEMP(getter); + current->next = _function->New(); + current = current->next; + current->expr = _block->TEMP(setter); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_define_getter_setter, 0, 0), args)); + } + } + _expr.code = _block->TEMP(t); return false; } diff --git a/qv4ir.cpp b/qv4ir.cpp index a61ce0ba50..67854133dd 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -244,6 +244,8 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_pop_with"; case IR::Name::builtin_declare_vars: return "builtin_declare_vars"; + case IR::Name::builtin_define_getter_setter: + return "builtin_define_getter_setter"; } return "builtin_(###FIXME)"; }; diff --git a/qv4ir_p.h b/qv4ir_p.h index 802a11de48..9cd0a4049b 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -284,7 +284,8 @@ struct Name: Expr { builtin_foreach_next_property_name, builtin_push_with, builtin_pop_with, - builtin_declare_vars + builtin_declare_vars, + builtin_define_getter_setter }; const QString *id; diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 694044fac7..de6a67e3f3 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -532,6 +532,27 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu generateFunctionCall(Assembler::Void, __qmljs_builtin_declare_var, Assembler::ContextRegister, Assembler::TrustedImm32(deletable->value != 0), identifier(*arg->id)); } + break; + } + case IR::Name::builtin_define_getter_setter: { + if (!call->args) + return; + IR::ExprList *args = call->args; + IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + IR::Name *name = args->expr->asName(); + args = args->next; + assert(args); + IR::Temp *getter = args->expr->asTemp(); + args = args->next; + assert(args); + IR::Temp *setter = args->expr->asTemp(); + + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter, + object, identifier(*name->id), getter, setter, Assembler::ContextRegister); + break; } } } -- cgit v1.2.3 From 8746fcc8b81a57d42d11504806556910fd4ab94d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 3 Jan 2013 20:45:36 +0100 Subject: Correctly size arrays with trailing elisions Change-Id: Iae09ec815e3307615299bf97741716b3671c9560 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 180d1c80e1..fa23664980 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -997,8 +997,11 @@ bool Codegen::visit(ArrayLiteral *ast) move(subscript(_block->TEMP(t), _block->CONST(IR::NumberType, index)), *expr); ++index; } - for (Elision *elision = ast->elision; elision; elision = elision->next) - ++index; // ### set the size of the array + if (ast->elision) { + for (Elision *elision = ast->elision->next; elision; elision = elision->next) + ++index; + move(subscript(_block->TEMP(t), _block->CONST(IR::NumberType, index)), _block->CONST(IR::UndefinedType, 0)); + } _expr.code = _block->TEMP(t); return false; } -- cgit v1.2.3 From 8260deef953115e098ed63eadaaac65234f0ec28 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 3 Jan 2013 22:17:46 +0100 Subject: Add fast conversion of a string to an array index This will be required later on when Array gets fixed properly. Change-Id: I37eccf94b202c9a003aab30b078ee24c422359a1 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 18 ++++++++++++++++++ qmljs_objects.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 1840e059ca..9070128543 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -60,6 +60,24 @@ using namespace QQmlJS::VM; + +uint String::asArrayIndex() const +{ + uint u = 0; + const QChar *ch = _text.constData(); + const QChar *end = ch + _text.length(); + while (ch < end) { + if (ch->unicode() < '0' && ch->unicode() > '9') + return InvalidArrayIndex; + uint n = u*10 + ch->unicode() - '0'; + if (n < u) + // overflow + return InvalidArrayIndex; + u = n; + } + return u; +} + // // Object // diff --git a/qmljs_objects.h b/qmljs_objects.h index 61fee48eef..9645ea701b 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -111,6 +111,8 @@ struct String { return _hashValue; } + enum { InvalidArrayIndex = 0xffffffff }; + uint asArrayIndex() const; private: friend class StringPool; -- cgit v1.2.3 From 22480fc638afdc0bc860602381e81654e4cbfa3c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 3 Jan 2013 22:40:02 +0100 Subject: Throw syntax errors with duplicated values in Object literals Object literals can only contain duplicated values for data properties in non strict mode. In all other cases throw a syntax error. Change-Id: Icb06ba1c343daffdb6a0532a08f7b9ae027e4e45 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 66 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index fa23664980..9e4c0c90cd 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1360,14 +1360,15 @@ bool Codegen::visit(NumericLiteral *ast) return false; } -struct Accessor { +struct ObjectPropertyValue { + IR::Expr *value; IR::Function *getter; IR::Function *setter; }; bool Codegen::visit(ObjectLiteral *ast) { - QMap accessorMap; + QMap valueMap; const unsigned t = _block->newTemp(); move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Object"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); @@ -1375,42 +1376,57 @@ bool Codegen::visit(ObjectLiteral *ast) if (PropertyNameAndValue *nv = AST::cast(it->assignment)) { QString name = propertyName(nv->name); Result value = expression(nv->value); - move(member(_block->TEMP(t), _function->newString(name)), *value); + ObjectPropertyValue &v = valueMap[name]; + if (v.getter || v.setter || (_function->isStrict && v.value)) + throwSyntaxError(nv->lastSourceLocation(), + QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); + + valueMap[name].value = *value; } else if (PropertyGetterSetter *gs = AST::cast(it->assignment)) { QString name = propertyName(gs->name); IR::Function *function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0); if (_debugger) _debugger->setSourceLocation(function, gs->getSetToken.startLine, gs->getSetToken.startColumn); + ObjectPropertyValue &v = valueMap[name]; + if (v.value || + (gs->type == PropertyGetterSetter::Getter && v.getter) || + (gs->type == PropertyGetterSetter::Setter && v.setter)) + throwSyntaxError(gs->lastSourceLocation(), + QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); if (gs->type == PropertyGetterSetter::Getter) - accessorMap[name].getter = function; + v.getter = function; else - accessorMap[name].setter = function; + v.setter = function; } else { Q_UNREACHABLE(); } } - if (!accessorMap.isEmpty()) { + if (!valueMap.isEmpty()) { const unsigned getter = _block->newTemp(); const unsigned setter = _block->newTemp(); - for (QMap::const_iterator it = accessorMap.constBegin(); it != accessorMap.constEnd(); ++it) { - move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(IR::UndefinedType, 0)); - move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(IR::UndefinedType, 0)); - - - // __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); - IR::ExprList *args = _function->New(); - IR::ExprList *current = args; - current->expr = _block->TEMP(t); - current->next = _function->New(); - current = current->next; - current->expr = _block->NAME(it.key(), 0, 0); - current->next = _function->New(); - current = current->next; - current->expr = _block->TEMP(getter); - current->next = _function->New(); - current = current->next; - current->expr = _block->TEMP(setter); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_define_getter_setter, 0, 0), args)); + for (QMap::const_iterator it = valueMap.constBegin(); it != valueMap.constEnd(); ++it) { + if (it->value) { + move(member(_block->TEMP(t), _function->newString(it.key())), it->value); + } else { + move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(IR::UndefinedType, 0)); + move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(IR::UndefinedType, 0)); + + + // __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); + IR::ExprList *args = _function->New(); + IR::ExprList *current = args; + current->expr = _block->TEMP(t); + current->next = _function->New(); + current = current->next; + current->expr = _block->NAME(it.key(), 0, 0); + current->next = _function->New(); + current = current->next; + current->expr = _block->TEMP(getter); + current->next = _function->New(); + current = current->next; + current->expr = _block->TEMP(setter); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_define_getter_setter, 0, 0), args)); + } } } -- cgit v1.2.3 From 9d1e3e301d88a3dad6115043396fdd8d0ee31ca4 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 3 Jan 2013 22:56:52 +0100 Subject: Set data properties using __defineOwnProperties__ Properties in object literals are defined using defineOwnProperty, so that they get set even if the object prototype contains a non writable property with the same name. This fixes all remaining test cases for 11.1.5 Change-Id: I3928a144d09c51c5bf20a25bcb1c6c3c243975ee Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 25 ++++++++++++++++++++++--- qmljs_runtime.h | 1 + qv4codegen.cpp | 33 ++++++++++++++++++++++----------- qv4ir.cpp | 2 ++ qv4ir_p.h | 1 + qv4isel_masm.cpp | 17 +++++++++++++++++ 6 files changed, 65 insertions(+), 14 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 73555c4c92..80e4a9ddc1 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -890,14 +890,33 @@ void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String * ctx->createMutableBinding(name, deletable); } +void __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) +{ + Object *o = object.asObject(); + assert(o); + + PropertyDescriptor pd; + pd.value = val; + pd.type = PropertyDescriptor::Data; + pd.writable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + pd.configurable = PropertyDescriptor::Enabled; + o->__defineOwnProperty__(ctx, name, &pd); +} + void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx) { Object *o = object.asObject(); assert(o); - PropertyDescriptor desc; - desc.fromAccessor(getter.asFunctionObject(), setter.asFunctionObject()); - o->__defineOwnProperty__(ctx, name, &desc); + PropertyDescriptor pd; + pd.get = getter.asFunctionObject(); + pd.set = setter.asFunctionObject(); + pd.type = PropertyDescriptor::Accessor; + pd.writable = PropertyDescriptor::Undefined; + pd.enumberable = PropertyDescriptor::Enabled; + pd.configurable = PropertyDescriptor::Enabled; + o->__defineOwnProperty__(ctx, name, &pd); } Value __qmljs_increment(Value value, ExecutionContext *ctx) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 085125a9c4..66d9c780cc 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -104,6 +104,7 @@ void __qmljs_builtin_rethrow(ExecutionContext *context); void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx); void __qmljs_builtin_pop_with(ExecutionContext *ctx); void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name); +void __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx); void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); // constructors diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 9e4c0c90cd..6446a81111 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1402,25 +1402,36 @@ bool Codegen::visit(ObjectLiteral *ast) } } if (!valueMap.isEmpty()) { - const unsigned getter = _block->newTemp(); - const unsigned setter = _block->newTemp(); + unsigned value = 0; + unsigned getter = 0; + unsigned setter = 0; for (QMap::const_iterator it = valueMap.constBegin(); it != valueMap.constEnd(); ++it) { + IR::ExprList *args = _function->New(); + IR::ExprList *current = args; + current->expr = _block->TEMP(t); + current->next = _function->New(); + current = current->next; + current->expr = _block->NAME(it.key(), 0, 0); + current->next = _function->New(); + current = current->next; + if (it->value) { - move(member(_block->TEMP(t), _function->newString(it.key())), it->value); + if (!value) + value = _block->newTemp(); + move(_block->TEMP(value), it->value); + // __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) + current->expr = _block->TEMP(value); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_define_property, 0, 0), args)); } else { + if (!getter) { + getter = _block->newTemp(); + setter = _block->newTemp(); + } move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(IR::UndefinedType, 0)); move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(IR::UndefinedType, 0)); // __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); - IR::ExprList *args = _function->New(); - IR::ExprList *current = args; - current->expr = _block->TEMP(t); - current->next = _function->New(); - current = current->next; - current->expr = _block->NAME(it.key(), 0, 0); - current->next = _function->New(); - current = current->next; current->expr = _block->TEMP(getter); current->next = _function->New(); current = current->next; diff --git a/qv4ir.cpp b/qv4ir.cpp index 67854133dd..68976fb39d 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -244,6 +244,8 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_pop_with"; case IR::Name::builtin_declare_vars: return "builtin_declare_vars"; + case IR::Name::builtin_define_property: + return "builtin_define_property"; case IR::Name::builtin_define_getter_setter: return "builtin_define_getter_setter"; } diff --git a/qv4ir_p.h b/qv4ir_p.h index 9cd0a4049b..962aa452dc 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -285,6 +285,7 @@ struct Name: Expr { builtin_push_with, builtin_pop_with, builtin_declare_vars, + builtin_define_property, builtin_define_getter_setter }; diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index de6a67e3f3..3f44f260da 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -554,6 +554,23 @@ void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *resu object, identifier(*name->id), getter, setter, Assembler::ContextRegister); break; } + case IR::Name::builtin_define_property: { + if (!call->args) + return; + IR::ExprList *args = call->args; + IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + IR::Name *name = args->expr->asName(); + args = args->next; + assert(args); + IR::Temp *value = args->expr->asTemp(); + + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property, + object, identifier(*name->id), value, Assembler::ContextRegister); + break; + } } } -- cgit v1.2.3 From dcc4275e667a57b423a6799a7aecf47438878237 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 4 Jan 2013 09:09:39 +0100 Subject: Fix hang in do-while loops with continue statement in the body The construct do { ... continue; } while (condition) Would result in the body basic block ending with a jump to the loop body, causing an infinite loop. Instead we have to place the condition into an extra block and set that one as the target for continue. This is also covered by ch12/12.14/S12.14_A9_T2, which uses such a loop. Change-Id: If06de112b338b74b9a49e3d6b51078463645196d Reviewed-by: Lars Knoll --- qv4codegen.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 6446a81111..fe754a435c 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1973,14 +1973,20 @@ bool Codegen::visit(DebuggerStatement *) bool Codegen::visit(DoWhileStatement *ast) { IR::BasicBlock *loopbody = _function->newBasicBlock(); + IR::BasicBlock *loopcond = _function->newBasicBlock(); IR::BasicBlock *loopend = _function->newBasicBlock(); - enterLoop(ast, loopend, loopbody); + enterLoop(ast, loopend, loopcond); _block->JUMP(loopbody); + _block = loopbody; statement(ast->statement); + _block->JUMP(loopcond); + + _block = loopcond; condition(ast->expression, loopbody, loopend); + _block = loopend; leaveLoop(); -- cgit v1.2.3 From 70f0a25864e74b6778bf1603684d36fff6e20bcc Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 4 Jan 2013 09:59:04 +0100 Subject: Produce a syntax error when a break appears outside of an iteration Fixes expected failure of ch12/12.8/S12.8_A1_T2 and others Change-Id: I261d649f6a29bbd6debfca35e7ccaf1a0a7006b9 Reviewed-by: Lars Knoll --- qv4codegen.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index fe754a435c..190c0dcf63 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1927,6 +1927,8 @@ bool Codegen::visit(Block *ast) bool Codegen::visit(BreakStatement *ast) { + if (!_loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Break outside of loop")); unwindException(_loop->tryCleanup); if (ast->label.isEmpty()) _block->JUMP(_loop->breakBlock); @@ -2104,7 +2106,7 @@ bool Codegen::visit(LabelledStatement *ast) AST::cast(ast->statement) || AST::cast(ast->statement)) { statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. - } else { + } else if (_loop) { IR::BasicBlock *breakBlock = _function->newBasicBlock(); enterLoop(ast->statement, breakBlock, /*continueBlock*/ 0); statement(ast->statement); -- cgit v1.2.3 From 2f831d4e0b10fd0659a8dd2d5a0997cff42a382f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 4 Jan 2013 10:25:01 +0100 Subject: Fix break without identifier in labelled loop Make sure to do leaveLoop() after enterLoop() when processing labelled statements. Fixes hang in ch12/12.8/S12.8_A3 Change-Id: Iee96ea515524e9ea879f7528dc43e552b1343020 Reviewed-by: Lars Knoll --- qv4codegen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 190c0dcf63..5c95cd0158 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -2112,6 +2112,7 @@ bool Codegen::visit(LabelledStatement *ast) statement(ast->statement); _block->JUMP(breakBlock); _block = breakBlock; + leaveLoop(); } return false; -- cgit v1.2.3 From f97c4e15ea24fe8ada5ad417329df647041aa0ea Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 4 Jan 2013 13:46:21 +0100 Subject: Initial import of Test262 test suite This also includes a copy of the test262.py script to run all the tests, which we are going to modify to suit our needs. Change-Id: I15fd7d0278e39c7076d4e45650fbcf786f7a483f Reviewed-by: Lars Knoll --- .gitmodules | 3 + tests/test262 | 1 + tests/test262.py | 458 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 462 insertions(+) create mode 100644 .gitmodules create mode 160000 tests/test262 create mode 100755 tests/test262.py diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..610c233811 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tests/test262"] + path = tests/test262 + url = git://github.com/tronical/test262.git diff --git a/tests/test262 b/tests/test262 new file mode 160000 index 0000000000..0b5af3dcec --- /dev/null +++ b/tests/test262 @@ -0,0 +1 @@ +Subproject commit 0b5af3dcec772bb06b4d685a20b2859cda59d189 diff --git a/tests/test262.py b/tests/test262.py new file mode 100755 index 0000000000..496f4cdfe7 --- /dev/null +++ b/tests/test262.py @@ -0,0 +1,458 @@ +#!/usr/bin/env python +# Copyright 2009 the Sputnik authors. All rights reserved. +# This code is governed by the BSD license found in the LICENSE file. + +# This is derived from sputnik.py, the Sputnik console test runner, +# with elements from packager.py, which is separately +# copyrighted. TODO: Refactor so there is less duplication between +# test262.py and packager.py. + + +import logging +import optparse +import os +from os import path +import platform +import re +import subprocess +import sys +import tempfile +import time +import xml.dom.minidom +import datetime +import shutil +import json +import stat + + +from parseTestRecord import parseTestRecord, stripHeader + +from packagerConfig import * + +class Test262Error(Exception): + def __init__(self, message): + self.message = message + +def ReportError(s): + raise Test262Error(s) + + + +if not os.path.exists(EXCLUDED_FILENAME): + print "Cannot generate (JSON) test262 tests without a file," + \ + " %s, showing which tests have been disabled!" % EXCLUDED_FILENAME + sys.exit(1) +EXCLUDE_LIST = xml.dom.minidom.parse(EXCLUDED_FILENAME) +EXCLUDE_LIST = EXCLUDE_LIST.getElementsByTagName("test") +EXCLUDE_LIST = [x.getAttribute("id") for x in EXCLUDE_LIST] + + +def BuildOptions(): + result = optparse.OptionParser() + result.add_option("--command", default=None, help="The command-line to run") + result.add_option("--tests", default=path.abspath('.'), + help="Path to the tests") + result.add_option("--cat", default=False, action="store_true", + help="Print packaged test code that would be run") + result.add_option("--summary", default=False, action="store_true", + help="Print summary after running tests") + result.add_option("--full-summary", default=False, action="store_true", + help="Print summary and test output after running tests") + result.add_option("--strict_only", default=False, action="store_true", + help="Test only strict mode") + result.add_option("--non_strict_only", default=False, action="store_true", + help="Test only non-strict mode") + # TODO: Once enough tests are made strict compat, change the default + # to "both" + result.add_option("--unmarked_default", default="non_strict", + help="default mode for tests of unspecified strictness") + return result + + +def ValidateOptions(options): + if not options.command: + ReportError("A --command must be specified.") + if not path.exists(options.tests): + ReportError("Couldn't find test path '%s'" % options.tests) + + +placeHolderPattern = re.compile(r"\{\{(\w+)\}\}") + + +def IsWindows(): + p = platform.system() + return (p == 'Windows') or (p == 'Microsoft') + + +class TempFile(object): + + def __init__(self, suffix="", prefix="tmp", text=False): + self.suffix = suffix + self.prefix = prefix + self.text = text + self.fd = None + self.name = None + self.is_closed = False + self.Open() + + def Open(self): + (self.fd, self.name) = tempfile.mkstemp( + suffix = self.suffix, + prefix = self.prefix, + text = self.text) + + def Write(self, str): + os.write(self.fd, str) + + def Read(self): + f = file(self.name) + result = f.read() + f.close() + return result + + def Close(self): + if not self.is_closed: + self.is_closed = True + os.close(self.fd) + + def Dispose(self): + try: + self.Close() + os.unlink(self.name) + except OSError, e: + logging.error("Error disposing temp file: %s", str(e)) + + +class TestResult(object): + + def __init__(self, exit_code, stdout, stderr, case): + self.exit_code = exit_code + self.stdout = stdout + self.stderr = stderr + self.case = case + + def ReportOutcome(self, long_format): + name = self.case.GetName() + mode = self.case.GetMode() + if self.HasUnexpectedOutcome(): + if self.case.IsNegative(): + print "=== %s was expected to fail in %s, but didn't ===" % (name, mode) + else: + if long_format: + print "=== %s failed in %s ===" % (name, mode) + else: + print "%s in %s: " % (name, mode) + out = self.stdout.strip() + if len(out) > 0: + print "--- output ---" + print out + err = self.stderr.strip() + if len(err) > 0: + print "--- errors ---" + print err + if long_format: + print "===" + elif self.case.IsNegative(): + print "%s failed in %s as expected" % (name, mode) + else: + print "%s passed in %s" % (name, mode) + + def HasFailed(self): + return self.exit_code != 0 + + def HasUnexpectedOutcome(self): + if self.case.IsNegative(): + return not self.HasFailed() + else: + return self.HasFailed() + + +class TestCase(object): + + def __init__(self, suite, name, full_path, strict_mode): + self.suite = suite + self.name = name + self.full_path = full_path + self.strict_mode = strict_mode + f = open(self.full_path) + self.contents = f.read() + f.close() + testRecord = parseTestRecord(self.contents, name) + self.test = testRecord["test"] + del testRecord["test"] + del testRecord["header"] + del testRecord["commentary"] + self.testRecord = testRecord; + + + def GetName(self): + return path.join(*self.name) + + def GetMode(self): + if self.strict_mode: + return "strict mode" + else: + return "non-strict mode" + + def GetPath(self): + return self.name + + def IsNegative(self): + return 'negative' in self.testRecord + + def IsOnlyStrict(self): + return 'onlyStrict' in self.testRecord + + def IsNoStrict(self): + return 'noStrict' in self.testRecord + + def GetSource(self): + # "var testDescrip = " + str(self.testRecord) + ';\n\n' + \ + source = self.suite.GetInclude("cth.js") + \ + self.suite.GetInclude("sta.js") + \ + self.suite.GetInclude("ed.js") + \ + self.suite.GetInclude("testBuiltInObject.js") + \ + self.suite.GetInclude("testIntl.js") + \ + self.test + '\n' + + if self.strict_mode: + source = '"use strict";\nvar strict_mode = true;\n' + source + else: + source = "var strict_mode = false; \n" + source + return source + + def InstantiateTemplate(self, template, params): + def GetParameter(match): + key = match.group(1) + return params.get(key, match.group(0)) + return placeHolderPattern.sub(GetParameter, template) + + def Execute(self, command): + if IsWindows(): + args = '"%s"' % command + else: + args = command.split(" ") + stdout = TempFile(prefix="test262-out-") + stderr = TempFile(prefix="test262-err-") + try: + logging.info("exec: %s", str(args)) + process = subprocess.Popen( + args, + shell = IsWindows(), + stdout = stdout.fd, + stderr = stderr.fd + ) + code = process.wait() + out = stdout.Read() + err = stderr.Read() + finally: + stdout.Dispose() + stderr.Dispose() + return (code, out, err) + + def RunTestIn(self, command_template, tmp): + tmp.Write(self.GetSource()) + tmp.Close() + command = self.InstantiateTemplate(command_template, { + 'path': tmp.name + }) + (code, out, err) = self.Execute(command) + return TestResult(code, out, err, self) + + def Run(self, command_template): + tmp = TempFile(suffix=".js", prefix="test262-", text=True) + try: + result = self.RunTestIn(command_template, tmp) + finally: + tmp.Dispose() + return result + + def Print(self): + print self.GetSource() + + +class ProgressIndicator(object): + + def __init__(self, count): + self.count = count + self.succeeded = 0 + self.failed = 0 + self.failed_tests = [] + + def HasRun(self, result): + result.ReportOutcome(True) + if result.HasUnexpectedOutcome(): + self.failed += 1 + self.failed_tests.append(result) + else: + self.succeeded += 1 + + +def MakePlural(n): + if (n == 1): + return (n, "") + else: + return (n, "s") + + +class TestSuite(object): + + def __init__(self, root, strict_only, non_strict_only, unmarked_default): + # TODO: derive from packagerConfig.py + self.test_root = path.join(root, 'test', 'suite') + self.lib_root = path.join(root, 'test', 'harness') + self.strict_only = strict_only + self.non_strict_only = non_strict_only + self.unmarked_default = unmarked_default + self.include_cache = { } + + def Validate(self): + if not path.exists(self.test_root): + ReportError("No test repository found") + if not path.exists(self.lib_root): + ReportError("No test library found") + + def IsHidden(self, path): + return path.startswith('.') or path == 'CVS' + + def IsTestCase(self, path): + return path.endswith('.js') + + def ShouldRun(self, rel_path, tests): + if len(tests) == 0: + return True + for test in tests: + if test in rel_path: + return True + return False + + def GetInclude(self, name): + if not name in self.include_cache: + static = path.join(self.lib_root, name) + if path.exists(static): + f = open(static) + contents = stripHeader(f.read()) + contents = re.sub(r'\r\n', '\n', contents) + self.include_cache[name] = contents + "\n" + f.close() + else: + ReportError("Can't find: " + static) + return self.include_cache[name] + + def EnumerateTests(self, tests): + logging.info("Listing tests in %s", self.test_root) + cases = [] + for root, dirs, files in os.walk(self.test_root): + for f in [x for x in dirs if self.IsHidden(x)]: + dirs.remove(f) + dirs.sort() + for f in sorted(files): + if self.IsTestCase(f): + full_path = path.join(root, f) + if full_path.startswith(self.test_root): + rel_path = full_path[len(self.test_root)+1:] + else: + logging.warning("Unexpected path %s", full_path) + rel_path = full_path + if self.ShouldRun(rel_path, tests): + basename = path.basename(full_path)[:-3] + name = rel_path.split(path.sep)[:-1] + [basename] + if EXCLUDE_LIST.count(basename) >= 1: + print 'Excluded: ' + basename + else: + if not self.non_strict_only: + strict_case = TestCase(self, name, full_path, True) + if not strict_case.IsNoStrict(): + if strict_case.IsOnlyStrict() or \ + self.unmarked_default in ['both', 'strict']: + cases.append(strict_case) + if not self.strict_only: + non_strict_case = TestCase(self, name, full_path, False) + if not non_strict_case.IsOnlyStrict(): + if non_strict_case.IsNoStrict() or \ + self.unmarked_default in ['both', 'non_strict']: + cases.append(non_strict_case) + logging.info("Done listing tests") + return cases + + def PrintSummary(self, progress): + print + print "=== Summary ===" + count = progress.count + succeeded = progress.succeeded + failed = progress.failed + print " - Ran %i test%s" % MakePlural(count) + if progress.failed == 0: + print " - All tests succeeded" + else: + percent = ((100.0 * succeeded) / count,) + print " - Passed %i test%s (%.1f%%)" % (MakePlural(succeeded) + percent) + percent = ((100.0 * failed) / count,) + print " - Failed %i test%s (%.1f%%)" % (MakePlural(failed) + percent) + positive = [c for c in progress.failed_tests if not c.case.IsNegative()] + negative = [c for c in progress.failed_tests if c.case.IsNegative()] + if len(positive) > 0: + print + print "Failed tests" + for result in positive: + print " %s in %s" % (result.case.GetName(), result.case.GetMode()) + if len(negative) > 0: + print + print "Expected to fail but passed ---" + for result in negative: + print " %s in %s" % (result.case.GetName(), result.case.GetMode()) + + def PrintFailureOutput(self, progress): + for result in progress.failed_tests: + print + result.ReportOutcome(False) + + def Run(self, command_template, tests, print_summary, full_summary): + if not "{{path}}" in command_template: + command_template += " {{path}}" + cases = self.EnumerateTests(tests) + if len(cases) == 0: + ReportError("No tests to run") + progress = ProgressIndicator(len(cases)) + for case in cases: + result = case.Run(command_template) + progress.HasRun(result) + if print_summary: + self.PrintSummary(progress) + if full_summary: + self.PrintFailureOutput(progress) + else: + print + print "Use --full-summary to see output from failed tests" + print + + def Print(self, tests): + cases = self.EnumerateTests(tests) + if len(cases) > 0: + cases[0].Print() + + +def Main(): + parser = BuildOptions() + (options, args) = parser.parse_args() + ValidateOptions(options) + test_suite = TestSuite(options.tests, + options.strict_only, + options.non_strict_only, + options.unmarked_default) + test_suite.Validate() + if options.cat: + test_suite.Print(args) + else: + test_suite.Run(options.command, args, + options.summary or options.full_summary, + options.full_summary) + + +if __name__ == '__main__': + try: + Main() + sys.exit(0) + except Test262Error, e: + print "Error: %s" % e.message + sys.exit(1) -- cgit v1.2.3 From 5f1ba2b90ed52f0c7cdfe9fe9e4b3314529629b1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 4 Jan 2013 13:52:06 +0100 Subject: Simplify test262.py execution Make it possible to call test262.py from anywhere and default to v4 as command to execute. Change-Id: If805c7b3733a49f86465e8f7ebaffd581536dfec Reviewed-by: Lars Knoll --- tests/test262.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/test262.py b/tests/test262.py index 496f4cdfe7..20a9ea8767 100755 --- a/tests/test262.py +++ b/tests/test262.py @@ -7,15 +7,17 @@ # copyrighted. TODO: Refactor so there is less duplication between # test262.py and packager.py. +import sys +from os import path +rootDir = path.dirname(path.realpath(__file__)) +sys.path.insert(0, path.abspath(rootDir + "/test262/tools/packaging")) import logging import optparse import os -from os import path import platform import re import subprocess -import sys import tempfile import time import xml.dom.minidom @@ -49,12 +51,12 @@ EXCLUDE_LIST = [x.getAttribute("id") for x in EXCLUDE_LIST] def BuildOptions(): result = optparse.OptionParser() - result.add_option("--command", default=None, help="The command-line to run") - result.add_option("--tests", default=path.abspath('.'), + result.add_option("--command", default=path.abspath(rootDir + "/../v4"), help="The command-line to run") + result.add_option("--tests", default=path.abspath(rootDir + '/test262'), help="Path to the tests") result.add_option("--cat", default=False, action="store_true", help="Print packaged test code that would be run") - result.add_option("--summary", default=False, action="store_true", + result.add_option("--summary", default=True, action="store_true", help="Print summary after running tests") result.add_option("--full-summary", default=False, action="store_true", help="Print summary and test output after running tests") -- cgit v1.2.3 From 77f451cbd25326a927aaf2914640e923c4b5c4d6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 4 Jan 2013 15:51:16 +0100 Subject: Add support to basic test exceptation handling to test262.py Initially this will be used to merely skip tests that we cannot run yet, but it will be extended in the future to also list tests that are currently failing but _should_ be passing, so that we can detect it when we fix these. Change-Id: Ic59f60c7023b261950b6b0ff3a45e00afe7c63e9 Reviewed-by: Lars Knoll --- tests/TestExpectations | 20 ++++++++++++++++++++ tests/test262.py | 13 ++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/TestExpectations diff --git a/tests/TestExpectations b/tests/TestExpectations new file mode 100644 index 0000000000..30eaab6e79 --- /dev/null +++ b/tests/TestExpectations @@ -0,0 +1,20 @@ +# Skipped because our array implementation isn't sparse and therefore this test causes OOM. +S15.4_A1.1_T10 +S15.4.2.2_A2.1_T1 +S15.4.4.10_A1.3_T4 +S15.4.4.10_A2.2_T3 +S15.4.4.10_A2.2_T4 +S15.4.4.10_A3_T1 +S15.4.4.10_A3_T2 +15.4.4.14-5-12 +15.4.4.14-5-16 +15.4.4.14-9-9 +15.4.4.15-5-12 +15.4.4.15-5-16 +15.4.4.15-8-9 +S15.4.5.2_A1_T1 +S15.4.5.2_A3_T4 + +# Skipped because of slow string concatenation, test takes a looong time to execute. +S15.1.3.1_A2.5_T1 +S15.1.3.2_A2.5_T1 diff --git a/tests/test262.py b/tests/test262.py index 20a9ea8767..7c0aa2f74f 100755 --- a/tests/test262.py +++ b/tests/test262.py @@ -39,6 +39,16 @@ def ReportError(s): raise Test262Error(s) +class TestExpectations: + def __init__(self): + f = open(rootDir + "/TestExpectations") + self.testsToSkip = [] + for line in f.read().splitlines(): + line = line.strip() + if len(line) == 0 or line[0] == "#": + continue + self.testsToSkip.append(line) + f.close() if not os.path.exists(EXCLUDED_FILENAME): print "Cannot generate (JSON) test262 tests without a file," + \ @@ -307,6 +317,7 @@ class TestSuite(object): self.non_strict_only = non_strict_only self.unmarked_default = unmarked_default self.include_cache = { } + self.expectations = TestExpectations() def Validate(self): if not path.exists(self.test_root): @@ -359,7 +370,7 @@ class TestSuite(object): if self.ShouldRun(rel_path, tests): basename = path.basename(full_path)[:-3] name = rel_path.split(path.sep)[:-1] + [basename] - if EXCLUDE_LIST.count(basename) >= 1: + if EXCLUDE_LIST.count(basename) >= 1 or self.expectations.testsToSkip.count(basename) >= 1: print 'Excluded: ' + basename else: if not self.non_strict_only: -- cgit v1.2.3 From 1a34d023fd1732a828066a99f1c44fe79bf310f0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 4 Jan 2013 15:53:37 +0100 Subject: Add top-level "make check" target ... that just runs all the tests. Change-Id: Ie14f73ab3c742b64aa52a0748b21ae9c9df3dd84 Reviewed-by: Lars Knoll --- v4.pro | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/v4.pro b/v4.pro index a2c90a6ad6..0c5c39fcd7 100644 --- a/v4.pro +++ b/v4.pro @@ -79,6 +79,11 @@ DEFINES += QMLJS_NO_LLVM } +checktarget.target = check +checktarget.commands = python tests/test262.py +checktarget.depends = all +QMAKE_EXTRA_TARGETS += checktarget + include(moth/moth.pri) include(masm/masm.pri) include(3rdparty/double-conversion/double-conversion.pri) -- cgit v1.2.3 From 0bcf32f13608b5563acfdb060722bbecd1f3740e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 4 Jan 2013 17:44:01 +0100 Subject: Add support for marking failing tests as expected failures If they suddenly pass, then that's going to show up as "failure" Might need to tweak the output a little. Change-Id: I4cec410953bc83d7b85e8c1d1a1ac30d53767e68 Reviewed-by: Lars Knoll --- tests/TestExpectations | 6109 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/test262.py | 20 +- 2 files changed, 6128 insertions(+), 1 deletion(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index 30eaab6e79..cda18160cb 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -18,3 +18,6112 @@ S15.4.5.2_A3_T4 # Skipped because of slow string concatenation, test takes a looong time to execute. S15.1.3.1_A2.5_T1 S15.1.3.2_A2.5_T1 + +# Tests failing that are supposed to pass. +Sbp_A10_T1 failing +Sbp_A10_T2 failing +7.3-10 failing +7.3-3 failing +7.3-4 failing +7.3-7 failing +7.3-8 failing +7.3-9 failing +S7.4_A5 failing +S7.4_A6 failing +7.6-1 failing +7.6-10 failing +7.6-11 failing +7.6-12 failing +7.6-13 failing +7.6-14 failing +7.6-15 failing +7.6-16 failing +7.6-17 failing +7.6-18 failing +7.6-19 failing +7.6-2 failing +7.6-20 failing +7.6-21 failing +7.6-22 failing +7.6-23 failing +7.6-24 failing +7.6-25 failing +7.6-26 failing +7.6-27 failing +7.6-28 failing +7.6-29 failing +7.6-3 failing +7.6-30 failing +7.6-31 failing +7.6-32 failing +7.6-33 failing +7.6-34 failing +7.6-35 failing +7.6-36 failing +7.6-4 failing +7.6-5 failing +7.6-6 failing +7.6-7 failing +7.6-8 failing +7.6-9 failing +S7.6_A2.1_T1 failing +S7.6_A2.1_T2 failing +S7.6_A2.1_T3 failing +S7.6_A2.1_T4 failing +7.6.1-4-1 failing +7.6.1-4-10 failing +7.6.1-4-11 failing +7.6.1-4-12 failing +7.6.1-4-13 failing +7.6.1-4-14 failing +7.6.1-4-15 failing +7.6.1-4-16 failing +7.6.1-4-2 failing +7.6.1-4-3 failing +7.6.1-4-4 failing +7.6.1-4-5 failing +7.6.1-4-6 failing +7.6.1-4-7 failing +7.6.1-4-8 failing +7.6.1-4-9 failing +7.6.1-8-1 failing +7.6.1-8-10 failing +7.6.1-8-11 failing +7.6.1-8-12 failing +7.6.1-8-13 failing +7.6.1-8-14 failing +7.6.1-8-15 failing +7.6.1-8-16 failing +7.6.1-8-2 failing +7.6.1-8-3 failing +7.6.1-8-4 failing +7.6.1-8-5 failing +7.6.1-8-6 failing +7.6.1-8-7 failing +7.6.1-8-8 failing +7.6.1-8-9 failing +7.6.1-17-s failing +7.6.1-18-s failing +7.6.1-19-s failing +7.6.1-20-s failing +7.6.1-21-s failing +7.6.1-22-s failing +7.6.1-23-s failing +7.6.1-24-s failing +7.6.1-25-s failing +7.6.1.2-1-s failing +7.6.1.2-2-s failing +7.6.1.2-3-s failing +7.6.1.2-4-s failing +7.6.1.2-5-s failing +7.6.1.2-6-s failing +7.6.1.2-7-s failing +7.6.1.2-8-s failing +7.6.1.2-9-s failing +7.8.3-1-s failing +7.8.3-2-s failing +7.8.3-3-s failing +7.8.3-4-s failing +7.8.3-5-s failing +7.8.3-6-s failing +7.8.3-7-s failing +7.8.4-1-s failing +7.8.4-10-s failing +7.8.4-11-s failing +7.8.4-12-s failing +7.8.4-13-s failing +7.8.4-14-s failing +7.8.4-15-s failing +7.8.4-16-s failing +7.8.4-17-s failing +7.8.4-18-s failing +7.8.4-19-s failing +7.8.4-2-s failing +7.8.4-20-s failing +7.8.4-21-s failing +7.8.4-22-s failing +7.8.4-23-s failing +7.8.4-24-s failing +7.8.4-25-s failing +7.8.4-26-s failing +7.8.4-27-s failing +7.8.4-28-s failing +7.8.4-29-s failing +7.8.4-3-s failing +7.8.4-30-s failing +7.8.4-31-s failing +7.8.4-32-s failing +7.8.4-33-s failing +7.8.4-4-s failing +7.8.4-5-s failing +7.8.4-6-s failing +7.8.4-7-s failing +7.8.4-8-s failing +7.8.4-9-s failing +7.8.5-1 failing +S7.8.5_A1.1_T2 failing +S7.8.5_A1.3_T2 failing +S7.8.5_A1.3_T4 failing +S7.8.5_A1.3_T5 failing +S7.8.5_A1.3_T6 failing +S7.8.5_A1.4_T2 failing +S7.8.5_A1.5_T2 failing +S7.8.5_A1.5_T4 failing +S7.8.5_A1.5_T5 failing +S7.8.5_A1.5_T6 failing +S7.8.5_A2.1_T2 failing +S7.8.5_A2.3_T2 failing +S7.8.5_A2.3_T4 failing +S7.8.5_A2.3_T5 failing +S7.8.5_A2.3_T6 failing +S7.8.5_A2.4_T2 failing +S7.8.5_A2.5_T2 failing +S7.8.5_A2.5_T4 failing +S7.8.5_A2.5_T5 failing +S7.8.5_A2.5_T6 failing +S7.9_A5.8_T1 failing +8.12.1-1_20 failing +8.12.1-1_21 failing +8.12.1-1_22 failing +8.12.1-1_23 failing +8.12.1-1_24 failing +8.12.1-1_25 failing +8.14.4-8-b_2 failing +8.12.5-3-b_1 failing +8.12.5-3-b_2 failing +8.12.5-5-b_1 failing +S8.12.8_A1 failing +S8.12.8_A2 failing +S8.12.8_A3 failing +S8.12.8_A4 failing +S8.5_A2.1 failing +S8.5_A2.2 failing +S8.6.2_A8 failing +8.7.2-1-s failing +8.7.2-3-s failing +8.7.2-4-s failing +8.7.2-5-s failing +S8.8_A2_T1 failing +S8.8_A2_T2 failing +S8.8_A2_T3 failing +S9.5_A3.1_T4 failing +S9.6_A3.1_T4 failing +S9.7_A3.1_T4 failing +S9.9_A1 failing +S9.9_A2 failing +S10.1.1_A2_T1 failing +S10.1.6_A1_T2 failing +10.1.1-11-s failing +10.1.1-14-s failing +10.1.1-15-s failing +10.1.1-16-s failing +10.1.1-17-s failing +10.1.1-18-s failing +10.1.1-19-s failing +10.1.1-2-s failing +10.1.1-22-s failing +10.1.1-25-s failing +10.1.1-26-s failing +10.1.1-30-s failing +10.1.1-5-s failing +10.1.1-8-s failing +10.2.1.1.3-4-16-s failing +10.2.1.1.3-4-18-s failing +10.2.1.1.3-4-22-s failing +10.2.1.1.3-4-27-s failing +S10.2.3_A1.1_T2 failing +S10.2.3_A1.2_T2 failing +S10.2.3_A1.3_T2 failing +10.4.2-1-1 failing +10.4.2-1-2 failing +10.4.2-1-3 failing +10.4.2-1-4 failing +10.4.2-1-5 failing +10.4.3-1-100-s failing +10.4.3-1-100gs failing +10.4.3-1-101-s failing +10.4.3-1-101gs failing +10.4.3-1-102-s failing +10.4.3-1-102gs failing +10.4.3-1-104 failing +10.4.3-1-106 failing +10.4.3-1-17-s failing +10.4.3-1-54-s failing +10.4.3-1-54gs failing +10.4.3-1-55-s failing +10.4.3-1-55gs failing +10.4.3-1-56-s failing +10.4.3-1-56gs failing +10.4.3-1-57-s failing +10.4.3-1-57gs failing +10.4.3-1-63-s failing +10.4.3-1-66-s failing +10.4.3-1-66gs failing +10.4.3-1-67-s failing +10.4.3-1-67gs failing +10.4.3-1-68-s failing +10.4.3-1-68gs failing +10.4.3-1-76-s failing +10.4.3-1-76gs failing +10.4.3-1-77-s failing +10.4.3-1-77gs failing +10.4.3-1-78-s failing +10.4.3-1-78gs failing +10.4.3-1-79-s failing +10.4.3-1-79gs failing +10.4.3-1-80-s failing +10.4.3-1-80gs failing +10.4.3-1-82-s failing +10.4.3-1-85-s failing +10.4.3-1-85gs failing +10.4.3-1-86-s failing +10.4.3-1-86gs failing +10.4.3-1-87-s failing +10.4.3-1-87gs failing +10.4.3-1-95-s failing +10.4.3-1-95gs failing +10.4.3-1-96-s failing +10.4.3-1-96gs failing +10.4.3-1-97-s failing +10.4.3-1-97gs failing +10.4.3-1-98-s failing +10.4.3-1-98gs failing +10.4.3-1-99-s failing +10.4.3-1-99gs failing +10.5-1-s failing +10.5-7-b-1-s failing +10.5-7-b-4-s failing +10.6-11-b-1 failing +10.6-12-1 failing +10.6-13-1 failing +10.6-13-a-1 failing +10.6-13-b-1-s failing +10.6-13-c-1-s failing +10.6-14-b-4-s failing +10.6-14-c-4-s failing +10.6-6-4 failing +10.6-7-1 failing +S10.6_A1 failing +S10.6_A2 failing +S10.6_A3_T1 failing +S10.6_A3_T2 failing +S10.6_A3_T3 failing +S10.6_A3_T4 failing +S10.6_A4 failing +S10.6_A5_T1 failing +S10.6_A5_T2 failing +S10.6_A5_T3 failing +S10.6_A5_T4 failing +S10.6_A6 failing +S10.6_A7 failing +S11.1.2_A1_T2 failing +11.1.4_4-5-1 failing +11.1.4_5-6-1 failing +11.1.5-0-1 failing +11.1.5-0-2 failing +11.1.5-1-s failing +11.1.5-2-s failing +11.1.5-3-s failing +11.1.5-4-4-a-1-s failing +11.1.5-4-s failing +11.1.5_3-3-1 failing +11.1.5_4-4-b-1 failing +11.1.5_4-4-b-2 failing +11.1.5_4-4-c-1 failing +11.1.5_4-4-c-2 failing +11.1.5_4-4-d-1 failing +11.1.5_4-4-d-2 failing +11.1.5_4-4-d-3 failing +11.1.5_4-4-d-4 failing +11.1.5_4-5-1 failing +11.1.5_6-2-1-s failing +11.1.5_6-2-2-s failing +11.1.5_6-3-1 failing +11.1.5_6-3-2 failing +11.1.5_7-2-1-s failing +11.1.5_7-2-2-s failing +11.1.5_7-3-1 failing +11.1.5_7-3-2 failing +S11.10.1_A2.1_T2 failing +S11.10.1_A2.1_T3 failing +S11.10.1_A2.2_T1 failing +S11.10.1_A2.3_T1 failing +S11.10.1_A2.4_T1 failing +S11.10.1_A2.4_T2 failing +S11.10.1_A2.4_T3 failing +S11.10.2_A2.1_T2 failing +S11.10.2_A2.1_T3 failing +S11.10.2_A2.2_T1 failing +S11.10.2_A2.3_T1 failing +S11.10.2_A2.4_T1 failing +S11.10.2_A2.4_T2 failing +S11.10.2_A2.4_T3 failing +S11.10.3_A2.1_T2 failing +S11.10.3_A2.1_T3 failing +S11.10.3_A2.2_T1 failing +S11.10.3_A2.3_T1 failing +S11.10.3_A2.4_T1 failing +S11.10.3_A2.4_T2 failing +S11.10.3_A2.4_T3 failing +S11.11.1_A2.1_T1 failing +S11.11.1_A2.1_T2 failing +S11.11.1_A2.1_T3 failing +S11.11.1_A2.4_T2 failing +S11.11.1_A2.4_T3 failing +S11.11.1_A3_T2 failing +S11.11.1_A3_T3 failing +S11.11.1_A3_T4 failing +S11.11.2_A2.1_T2 failing +S11.11.2_A2.1_T3 failing +S11.11.2_A2.4_T2 failing +S11.11.2_A2.4_T3 failing +S11.12_A2.1_T2 failing +S11.12_A2.1_T3 failing +S11.12_A2.1_T4 failing +11.13.1-1-1 failing +11.13.1-1-2 failing +11.13.1-1-3 failing +11.13.1-1-4 failing +11.13.1-1-6-s failing +11.13.1-1-s failing +11.13.1-2-s failing +11.13.1-3-s failing +11.13.1-4-14-s failing +11.13.1-4-27-s failing +11.13.1-4-28-s failing +11.13.1-4-29-s failing +11.13.1-4-3-s failing +11.13.1-4-30-s failing +11.13.1-4-31-s failing +11.13.1-4-6-s failing +S11.13.1_A2.1_T2 failing +S11.13.1_A4_T2 failing +11.13.2-1-s failing +11.13.2-10-s failing +11.13.2-11-s failing +11.13.2-2-s failing +11.13.2-23-s failing +11.13.2-24-s failing +11.13.2-25-s failing +11.13.2-26-s failing +11.13.2-27-s failing +11.13.2-28-s failing +11.13.2-29-s failing +11.13.2-3-s failing +11.13.2-30-s failing +11.13.2-31-s failing +11.13.2-32-s failing +11.13.2-33-s failing +11.13.2-34-s failing +11.13.2-35-s failing +11.13.2-36-s failing +11.13.2-37-s failing +11.13.2-38-s failing +11.13.2-39-s failing +11.13.2-4-s failing +11.13.2-40-s failing +11.13.2-41-s failing +11.13.2-42-s failing +11.13.2-43-s failing +11.13.2-44-s failing +11.13.2-45-s failing +11.13.2-46-s failing +11.13.2-47-s failing +11.13.2-48-s failing +11.13.2-49-s failing +11.13.2-5-s failing +11.13.2-50-s failing +11.13.2-51-s failing +11.13.2-52-s failing +11.13.2-53-s failing +11.13.2-54-s failing +11.13.2-55-s failing +11.13.2-6-1-s failing +11.13.2-6-10-s failing +11.13.2-6-11-s failing +11.13.2-6-12-s failing +11.13.2-6-13-s failing +11.13.2-6-14-s failing +11.13.2-6-15-s failing +11.13.2-6-16-s failing +11.13.2-6-17-s failing +11.13.2-6-18-s failing +11.13.2-6-19-s failing +11.13.2-6-2-s failing +11.13.2-6-20-s failing +11.13.2-6-21-s failing +11.13.2-6-22-s failing +11.13.2-6-3-s failing +11.13.2-6-4-s failing +11.13.2-6-5-s failing +11.13.2-6-6-s failing +11.13.2-6-7-s failing +11.13.2-6-8-s failing +11.13.2-6-9-s failing +11.13.2-6-s failing +11.13.2-7-s failing +11.13.2-8-s failing +11.13.2-9-s failing +S11.13.2_A1_T5 failing +S11.13.2_A2.1_T1.3 failing +S11.13.2_A2.1_T1.7 failing +S11.13.2_A2.1_T1.8 failing +S11.13.2_A2.1_T2.1 failing +S11.13.2_A2.1_T2.10 failing +S11.13.2_A2.1_T2.11 failing +S11.13.2_A2.1_T2.2 failing +S11.13.2_A2.1_T2.3 failing +S11.13.2_A2.1_T2.4 failing +S11.13.2_A2.1_T2.5 failing +S11.13.2_A2.1_T2.6 failing +S11.13.2_A2.1_T2.7 failing +S11.13.2_A2.1_T2.8 failing +S11.13.2_A2.1_T2.9 failing +S11.13.2_A2.1_T3.1 failing +S11.13.2_A2.1_T3.10 failing +S11.13.2_A2.1_T3.11 failing +S11.13.2_A2.1_T3.2 failing +S11.13.2_A2.1_T3.3 failing +S11.13.2_A2.1_T3.4 failing +S11.13.2_A2.1_T3.5 failing +S11.13.2_A2.1_T3.6 failing +S11.13.2_A2.1_T3.7 failing +S11.13.2_A2.1_T3.8 failing +S11.13.2_A2.1_T3.9 failing +S11.13.2_A3.1_T3 failing +S11.13.2_A3.1_T5 failing +S11.13.2_A3.1_T7 failing +S11.13.2_A3.1_T8 failing +S11.13.2_A3.2_T3 failing +S11.13.2_A3.2_T5 failing +S11.13.2_A3.2_T7 failing +S11.13.2_A3.2_T8 failing +S11.13.2_A4.2_T1.4 failing +S11.13.2_A4.2_T2.3 failing +S11.13.2_A4.2_T2.7 failing +S11.13.2_A4.2_T2.9 failing +S11.13.2_A4.3_T1.4 failing +S11.13.2_A4.3_T2.3 failing +S11.13.2_A4.3_T2.7 failing +S11.13.2_A4.3_T2.9 failing +S11.13.2_A4.4_T1.4 failing +S11.13.2_A4.4_T2.6 failing +S11.13.2_A4.4_T2.7 failing +S11.13.2_A4.4_T2.8 failing +S11.13.2_A4.4_T2.9 failing +S11.13.2_A4.5_T2.3 failing +S11.13.2_A4.5_T2.7 failing +S11.13.2_A4.5_T2.9 failing +S11.13.2_A4.6_T1.3 failing +S11.13.2_A4.6_T2.2 failing +S11.13.2_A4.6_T2.3 failing +S11.13.2_A4.6_T2.4 failing +S11.13.2_A4.6_T2.6 failing +S11.13.2_A4.6_T2.7 failing +S11.13.2_A4.6_T2.8 failing +S11.13.2_A4.6_T2.9 failing +S11.13.2_A4.7_T1.3 failing +S11.13.2_A4.7_T2.2 failing +S11.13.2_A4.7_T2.3 failing +S11.13.2_A4.7_T2.4 failing +S11.13.2_A4.7_T2.6 failing +S11.13.2_A4.7_T2.7 failing +S11.13.2_A4.7_T2.8 failing +S11.13.2_A4.7_T2.9 failing +S11.13.2_A4.8_T1.3 failing +S11.13.2_A4.8_T2.2 failing +S11.13.2_A4.8_T2.3 failing +S11.13.2_A4.8_T2.4 failing +S11.13.2_A4.8_T2.6 failing +S11.13.2_A4.8_T2.7 failing +S11.13.2_A4.8_T2.8 failing +S11.13.2_A4.8_T2.9 failing +S11.14_A2.1_T2 failing +S11.14_A2.1_T3 failing +S11.2.1_A2 failing +S11.2.1_A3_T1 failing +S11.2.1_A3_T2 failing +S11.2.1_A3_T3 failing +S11.2.1_A3_T4 failing +S11.2.1_A3_T5 failing +S11.2.1_A4_T1 failing +S11.2.1_A4_T2 failing +S11.2.1_A4_T3 failing +S11.2.1_A4_T4 failing +S11.2.1_A4_T5 failing +S11.2.2_A2 failing +S11.2.2_A3_T1 failing +S11.2.2_A3_T2 failing +S11.2.2_A3_T3 failing +S11.2.2_A3_T4 failing +S11.2.2_A3_T5 failing +S11.2.2_A4_T1 failing +S11.2.2_A4_T2 failing +S11.2.2_A4_T3 failing +S11.2.2_A4_T4 failing +S11.2.2_A4_T5 failing +11.2.3-3_1 failing +11.2.3-3_2 failing +11.2.3-3_3 failing +11.2.3-3_4 failing +11.2.3-3_5 failing +11.2.3-3_6 failing +11.2.3-3_7 failing +11.2.3-3_8 failing +S11.2.3_A2 failing +S11.2.3_A3_T1 failing +S11.2.3_A3_T2 failing +S11.2.3_A3_T3 failing +S11.2.3_A3_T4 failing +S11.2.3_A3_T5 failing +S11.2.3_A4_T1 failing +S11.2.3_A4_T2 failing +S11.2.3_A4_T3 failing +S11.2.3_A4_T4 failing +S11.2.3_A4_T5 failing +S11.2.4_A1.1_T2 failing +S11.2.4_A1.2_T1 failing +S11.2.4_A1.2_T2 failing +S11.2.4_A1.4_T2 failing +S11.2.4_A1.4_T4 failing +11.3.1-2-1-s failing +11.3.1-2-2-s failing +S11.3.1_A2.1_T2 failing +S11.3.1_A2.2_T1 failing +S11.3.1_A4_T1 failing +S11.3.1_A4_T2 failing +S11.3.1_A4_T3 failing +S11.3.1_A4_T4 failing +11.3.2-2-1-s failing +11.3.2-2-2-s failing +S11.3.2_A2.1_T2 failing +S11.3.2_A2.2_T1 failing +S11.3.2_A4_T1 failing +S11.3.2_A4_T2 failing +S11.3.2_A4_T3 failing +S11.3.2_A4_T4 failing +11.4.1-0-1 failing +11.4.1-2-1 failing +11.4.1-2-2 failing +11.4.1-2-3 failing +11.4.1-2-4 failing +11.4.1-2-5 failing +11.4.1-2-6 failing +11.4.1-3-2 failing +11.4.1-3-a-1-s failing +11.4.1-4-a-1-s failing +11.4.1-4-a-2-s failing +11.4.1-4.a-10 failing +11.4.1-4.a-12 failing +11.4.1-4.a-13 failing +11.4.1-4.a-3-s failing +11.4.1-4.a-5 failing +11.4.1-4.a-7 failing +11.4.1-4.a-8-s failing +11.4.1-4.a-8 failing +11.4.1-4.a-9-s failing +11.4.1-5-2 failing +11.4.1-5-a-1-s failing +11.4.1-5-a-10-s failing +11.4.1-5-a-11-s failing +11.4.1-5-a-12-s failing +11.4.1-5-a-13-s failing +11.4.1-5-a-14-s failing +11.4.1-5-a-15-s failing +11.4.1-5-a-16-s failing +11.4.1-5-a-17-s failing +11.4.1-5-a-18-s failing +11.4.1-5-a-19-s failing +11.4.1-5-a-2-s failing +11.4.1-5-a-20-s failing +11.4.1-5-a-21-s failing +11.4.1-5-a-22-s failing +11.4.1-5-a-23-s failing +11.4.1-5-a-24-s failing +11.4.1-5-a-25-s failing +11.4.1-5-a-26-s failing +11.4.1-5-a-27-s failing +11.4.1-5-a-28-s failing +11.4.1-5-a-3-s failing +11.4.1-5-a-4-s failing +11.4.1-5-a-5-s failing +11.4.1-5-a-6-s failing +11.4.1-5-a-7-s failing +11.4.1-5-a-8-s failing +11.4.1-5-a-9-s failing +11.4.4-4.a-3-s failing +S11.4.1_A1 failing +S11.4.1_A2.1 failing +S11.4.1_A3.3 failing +11.4.4-2-1-s failing +11.4.4-2-2-s failing +S11.4.4_A2.1_T2 failing +S11.4.4_A2.2_T1 failing +11.4.5-2-1-s failing +11.4.5-2-2-s failing +S11.4.5_A2.1_T2 failing +S11.4.5_A2.2_T1 failing +S11.4.6_A2.1_T2 failing +S11.4.6_A2.2_T1 failing +S11.4.7_A2.1_T2 failing +S11.4.7_A2.2_T1 failing +S11.4.7_A3_T3 failing +S11.4.7_A3_T4 failing +S11.4.7_A3_T5 failing +S11.4.7_A4.1 failing +S11.4.8_A2.1_T2 failing +S11.4.8_A2.2_T1 failing +S11.4.9_A2.1_T2 failing +S11.5.1_A2.1_T2 failing +S11.5.1_A2.1_T3 failing +S11.5.1_A2.2_T1 failing +S11.5.1_A2.3_T1 failing +S11.5.1_A2.4_T1 failing +S11.5.1_A2.4_T2 failing +S11.5.1_A2.4_T3 failing +S11.5.1_A4_T3 failing +S11.5.2_A2.1_T2 failing +S11.5.2_A2.1_T3 failing +S11.5.2_A2.2_T1 failing +S11.5.2_A2.3_T1 failing +S11.5.2_A2.4_T1 failing +S11.5.2_A2.4_T2 failing +S11.5.2_A2.4_T3 failing +S11.5.2_A3_T1.4 failing +S11.5.2_A4_T4 failing +S11.5.2_A4_T7 failing +S11.5.3_A2.1_T2 failing +S11.5.3_A2.1_T3 failing +S11.5.3_A2.2_T1 failing +S11.5.3_A2.3_T1 failing +S11.5.3_A2.4_T1 failing +S11.5.3_A2.4_T2 failing +S11.5.3_A2.4_T3 failing +S11.5.3_A3_T1.4 failing +S11.5.3_A3_T2.3 failing +S11.5.3_A3_T2.7 failing +S11.5.3_A3_T2.9 failing +S11.5.3_A4_T2 failing +S11.5.3_A4_T3 failing +S11.5.3_A4_T4 failing +S11.6.1_A2.1_T2 failing +S11.6.1_A2.1_T3 failing +S11.6.1_A2.2_T1 failing +S11.6.1_A2.3_T1 failing +S11.6.1_A2.4_T1 failing +S11.6.1_A2.4_T2 failing +S11.6.1_A2.4_T3 failing +S11.6.1_A4_T2 failing +S11.6.2_A2.1_T2 failing +S11.6.2_A2.1_T3 failing +S11.6.2_A2.2_T1 failing +S11.6.2_A2.3_T1 failing +S11.6.2_A2.4_T1 failing +S11.6.2_A2.4_T2 failing +S11.6.2_A2.4_T3 failing +S11.6.2_A4_T3 failing +S11.7.1_A2.1_T2 failing +S11.7.1_A2.1_T3 failing +S11.7.1_A2.2_T1 failing +S11.7.1_A2.3_T1 failing +S11.7.1_A2.4_T1 failing +S11.7.1_A2.4_T2 failing +S11.7.1_A2.4_T3 failing +S11.7.2_A2.1_T2 failing +S11.7.2_A2.1_T3 failing +S11.7.2_A2.2_T1 failing +S11.7.2_A2.3_T1 failing +S11.7.2_A2.4_T1 failing +S11.7.2_A2.4_T2 failing +S11.7.2_A2.4_T3 failing +S11.7.2_A3_T1.2 failing +S11.7.2_A4_T3 failing +S11.7.2_A4_T4 failing +S11.7.2_A5.2_T1 failing +S11.7.3_A2.1_T2 failing +S11.7.3_A2.1_T3 failing +S11.7.3_A2.2_T1 failing +S11.7.3_A2.3_T1 failing +S11.7.3_A2.4_T1 failing +S11.7.3_A2.4_T2 failing +S11.7.3_A2.4_T3 failing +S11.7.3_A3_T1.2 failing +S11.7.3_A4_T1 failing +S11.7.3_A4_T2 failing +S11.7.3_A4_T3 failing +S11.7.3_A4_T4 failing +S11.7.3_A5.2_T1 failing +S11.8.1_A2.1_T2 failing +S11.8.1_A2.1_T3 failing +S11.8.1_A2.2_T1 failing +S11.8.1_A2.3_T1 failing +S11.8.1_A2.4_T1 failing +S11.8.1_A2.4_T2 failing +S11.8.1_A2.4_T3 failing +S11.8.2_A2.1_T2 failing +S11.8.2_A2.1_T3 failing +S11.8.2_A2.2_T1 failing +S11.8.2_A2.3_T1 failing +S11.8.2_A2.4_T1 failing +S11.8.2_A2.4_T2 failing +S11.8.2_A2.4_T3 failing +S11.8.3_A2.1_T2 failing +S11.8.3_A2.1_T3 failing +S11.8.3_A2.2_T1 failing +S11.8.3_A2.3_T1 failing +S11.8.3_A2.4_T1 failing +S11.8.3_A2.4_T2 failing +S11.8.3_A2.4_T3 failing +S11.8.4_A2.1_T2 failing +S11.8.4_A2.1_T3 failing +S11.8.4_A2.2_T1 failing +S11.8.4_A2.3_T1 failing +S11.8.4_A2.4_T1 failing +S11.8.4_A2.4_T2 failing +S11.8.4_A2.4_T3 failing +S11.8.6_A2.1_T2 failing +S11.8.6_A2.1_T3 failing +S11.8.6_A2.4_T1 failing +S11.8.6_A2.4_T2 failing +S11.8.6_A2.4_T3 failing +S11.8.6_A3 failing +S11.8.6_A4_T1 failing +S11.8.6_A4_T2 failing +S11.8.6_A4_T3 failing +S11.8.6_A5_T2 failing +S11.8.6_A6_T1 failing +S11.8.6_A6_T2 failing +S11.8.6_A6_T4 failing +S11.8.7_A2.1_T2 failing +S11.8.7_A2.1_T3 failing +S11.8.7_A2.4_T1 failing +S11.8.7_A2.4_T2 failing +S11.8.7_A2.4_T3 failing +S11.8.7_A3 failing +S11.9.1_A2.1_T2 failing +S11.9.1_A2.1_T3 failing +S11.9.1_A2.4_T1 failing +S11.9.1_A2.4_T2 failing +S11.9.1_A2.4_T3 failing +S11.9.1_A7.8 failing +S11.9.1_A7.9 failing +S11.9.2_A2.1_T2 failing +S11.9.2_A2.1_T3 failing +S11.9.2_A2.4_T1 failing +S11.9.2_A2.4_T2 failing +S11.9.2_A2.4_T3 failing +S11.9.2_A7.8 failing +S11.9.2_A7.9 failing +S11.9.4_A2.1_T2 failing +S11.9.4_A2.1_T3 failing +S11.9.4_A2.4_T1 failing +S11.9.4_A2.4_T2 failing +S11.9.4_A2.4_T3 failing +S11.9.4_A8_T5 failing +S11.9.5_A2.1_T2 failing +S11.9.5_A2.1_T3 failing +S11.9.5_A2.4_T1 failing +S11.9.5_A2.4_T2 failing +S11.9.5_A2.4_T3 failing +S11.9.5_A8_T5 failing +12.1-1 failing +12.1-2 failing +12.1-3 failing +12.1-4 failing +12.1-5 failing +12.1-6 failing +12.1-7 failing +S12.1_A2 failing +S12.1_A5 failing +12.10-0-3 failing +12.10-0-7 failing +12.10-2-1 failing +12.10-2-2 failing +12.10-2-3 failing +12.10-7-1 failing +S12.10_A1.10_T1 failing +S12.10_A1.10_T2 failing +S12.10_A1.10_T3 failing +S12.10_A1.10_T4 failing +S12.10_A1.10_T5 failing +S12.10_A1.11_T1 failing +S12.10_A1.11_T2 failing +S12.10_A1.11_T3 failing +S12.10_A1.11_T4 failing +S12.10_A1.11_T5 failing +S12.10_A1.12_T1 failing +S12.10_A1.12_T2 failing +S12.10_A1.12_T3 failing +S12.10_A1.12_T4 failing +S12.10_A1.12_T5 failing +S12.10_A1.1_T1 failing +S12.10_A1.1_T2 failing +S12.10_A1.1_T3 failing +S12.10_A1.2_T1 failing +S12.10_A1.2_T2 failing +S12.10_A1.2_T3 failing +S12.10_A1.2_T4 failing +S12.10_A1.2_T5 failing +S12.10_A1.3_T1 failing +S12.10_A1.3_T2 failing +S12.10_A1.3_T3 failing +S12.10_A1.3_T4 failing +S12.10_A1.3_T5 failing +S12.10_A1.4_T1 failing +S12.10_A1.4_T2 failing +S12.10_A1.4_T3 failing +S12.10_A1.4_T4 failing +S12.10_A1.4_T5 failing +S12.10_A1.5_T1 failing +S12.10_A1.5_T2 failing +S12.10_A1.5_T3 failing +S12.10_A1.5_T4 failing +S12.10_A1.5_T5 failing +S12.10_A1.6_T1 failing +S12.10_A1.6_T2 failing +S12.10_A1.6_T3 failing +S12.10_A1.7_T1 failing +S12.10_A1.7_T2 failing +S12.10_A1.7_T3 failing +S12.10_A1.7_T4 failing +S12.10_A1.7_T5 failing +S12.10_A1.8_T1 failing +S12.10_A1.8_T2 failing +S12.10_A1.8_T3 failing +S12.10_A1.8_T4 failing +S12.10_A1.8_T5 failing +S12.10_A1.9_T1 failing +S12.10_A1.9_T2 failing +S12.10_A1.9_T3 failing +S12.10_A3.10_T2 failing +S12.10_A3.10_T3 failing +S12.10_A3.11_T1 failing +S12.10_A3.11_T2 failing +S12.10_A3.11_T3 failing +S12.10_A3.11_T4 failing +S12.10_A3.11_T5 failing +S12.10_A3.12_T1 failing +S12.10_A3.12_T2 failing +S12.10_A3.12_T3 failing +S12.10_A3.12_T4 failing +S12.10_A3.12_T5 failing +S12.10_A3.1_T2 failing +S12.10_A3.1_T3 failing +S12.10_A3.2_T4 failing +S12.10_A3.2_T5 failing +S12.10_A3.3_T4 failing +S12.10_A3.4_T2 failing +S12.10_A3.4_T3 failing +S12.10_A3.4_T4 failing +S12.10_A3.4_T5 failing +S12.10_A3.5_T2 failing +S12.10_A3.5_T3 failing +S12.10_A3.5_T4 failing +S12.10_A3.5_T5 failing +S12.10_A3.6_T2 failing +S12.10_A3.6_T3 failing +S12.10_A3.7_T4 failing +S12.10_A3.7_T5 failing +S12.10_A3.8_T4 failing +S12.10_A3.8_T5 failing +S12.10_A5_T4 failing +S12.10_A5_T5 failing +S12.10_A5_T6 failing +12.10.1-1-s failing +12.10.1-10-s failing +12.10.1-11-s failing +12.10.1-12-s failing +12.10.1-14-s failing +12.10.1-15-s failing +12.10.1-16-s failing +12.10.1-2-s failing +12.10.1-3-s failing +12.10.1-4-s failing +12.10.1-7-s failing +12.10.1-8-s failing +12.10.1-9-s failing +S12.11_A1_T1 failing +S12.11_A1_T2 failing +S12.11_A1_T3 failing +S12.11_A1_T4 failing +S12.13_A2_T1 failing +S12.13_A2_T2 failing +S12.13_A2_T3 failing +S12.13_A2_T4 failing +S12.13_A2_T5 failing +S12.13_A2_T6 failing +S12.13_A2_T7 failing +S12.13_A3_T1 failing +S12.13_A3_T2 failing +S12.13_A3_T3 failing +S12.13_A3_T4 failing +S12.13_A3_T5 failing +S12.13_A3_T6 failing +12.14-1 failing +12.14-10 failing +12.14-11 failing +12.14-12 failing +12.14-13 failing +12.14-14 failing +12.14-15 failing +12.14-16 failing +12.14-2 failing +12.14-3 failing +12.14-4 failing +12.14-6 failing +12.14-7 failing +12.14-8 failing +12.14-9 failing +S12.14_A1 failing +S12.14_A10_T1 failing +S12.14_A10_T2 failing +S12.14_A10_T3 failing +S12.14_A10_T4 failing +S12.14_A10_T5 failing +S12.14_A11_T1 failing +S12.14_A11_T2 failing +S12.14_A11_T3 failing +S12.14_A11_T4 failing +S12.14_A12_T1 failing +S12.14_A12_T2 failing +S12.14_A12_T3 failing +S12.14_A12_T4 failing +S12.14_A13_T1 failing +S12.14_A13_T2 failing +S12.14_A13_T3 failing +S12.14_A14 failing +S12.14_A15 failing +S12.14_A17 failing +S12.14_A18_T1 failing +S12.14_A18_T2 failing +S12.14_A18_T3 failing +S12.14_A18_T4 failing +S12.14_A18_T5 failing +S12.14_A18_T6 failing +S12.14_A18_T7 failing +S12.14_A19_T1 failing +S12.14_A19_T2 failing +S12.14_A2 failing +S12.14_A3 failing +S12.14_A4 failing +S12.14_A5 failing +S12.14_A6 failing +S12.14_A7_T1 failing +S12.14_A7_T2 failing +S12.14_A7_T3 failing +S12.14_A8 failing +S12.14_A9_T1 failing +S12.14_A9_T2 failing +S12.14_A9_T3 failing +S12.14_A9_T4 failing +S12.14_A9_T5 failing +12.14.1-1-s failing +12.14.1-2-s failing +12.14.1-3-s failing +12.14.1-4-s failing +12.14.1-5-s failing +12.14.1-6-s failing +S12.2_A1 failing +S12.2_A10 failing +S12.2_A12 failing +S12.2_A4 failing +S12.2_A5 failing +S12.2_A6_T1 failing +S12.2_A6_T2 failing +S12.2_A7 failing +12.2.1-1-s failing +12.2.1-12-s failing +12.2.1-13-s failing +12.2.1-14-s failing +12.2.1-15-s failing +12.2.1-18-s failing +12.2.1-19-s failing +12.2.1-2-s failing +12.2.1-21-s failing +12.2.1-22-s failing +12.2.1-23-s failing +12.2.1-24-s failing +12.2.1-25-s failing +12.2.1-26-s failing +12.2.1-27-s failing +12.2.1-28-s failing +12.2.1-29-s failing +12.2.1-3-s failing +12.2.1-30-s failing +12.2.1-31-s failing +12.2.1-32-s failing +12.2.1-33-s failing +12.2.1-34-s failing +12.2.1-35-s failing +12.2.1-36-s failing +12.2.1-37-s failing +12.2.1-4-s failing +12.2.1-7-s failing +12.2.1-8-s failing +S12.5_A3 failing +S12.5_A4 failing +S12.5_A5 failing +S12.5_A7 failing +S12.6.1_A2 failing +S12.6.1_A8 failing +S12.6.1_A9 failing +S12.6.2_A2 failing +S12.6.2_A8 failing +S12.6.2_A9 failing +S12.6.3_A1 failing +S12.6.3_A10.1 failing +S12.6.3_A10 failing +S12.6.3_A11.1_T1 failing +S12.6.3_A11.1_T2 failing +S12.6.3_A11_T1 failing +S12.6.3_A11_T2 failing +S12.6.3_A12.1_T1 failing +S12.6.3_A12.1_T2 failing +S12.6.3_A12_T1 failing +S12.6.3_A12_T2 failing +S12.6.3_A13 failing +S12.6.3_A2.1 failing +S12.6.3_A2.2 failing +S12.6.3_A2 failing +S12.6.3_A3 failing +S12.6.3_A5 failing +S12.6.3_A6 failing +S12.6.3_A9.1 failing +S12.6.3_A9 failing +S12.6.4_A1 failing +S12.6.4_A2 failing +S12.6.4_A3.1 failing +S12.6.4_A3 failing +S12.7_A2 failing +S12.7_A7 failing +S12.7_A9_T1 failing +S12.7_A9_T2 failing +S12.8_A2 failing +S12.8_A4_T1 failing +S12.8_A4_T2 failing +S12.8_A4_T3 failing +S12.8_A7 failing +S12.8_A9_T1 failing +S12.8_A9_T2 failing +S12.9_A2 failing +13.0-1 failing +13.0-10-s failing +13.0-11-s failing +13.0-13-s failing +13.0-14-s failing +13.0-15-s failing +13.0-16-s failing +13.0-2 failing +13.0-3 failing +13.0-4 failing +13.0-7-s failing +13.0-8-s failing +13.0-9-s failing +S13_A11_T3 failing +S13_A15_T4 failing +S13_A17_T1 failing +S13_A17_T2 failing +S13_A2_T2 failing +S13_A2_T3 failing +S13_A3_T1 failing +S13_A4_T4 failing +S13_A6_T2 failing +S13_A7_T2 failing +S13_A8_T1 failing +S13_A8_T2 failing +13.1-1-1 failing +13.1-1-2 failing +13.1-1-s failing +13.1-10-s failing +13.1-11-s failing +13.1-12-s failing +13.1-13-s failing +13.1-14-s failing +13.1-15-s failing +13.1-16-s failing +13.1-17-s failing +13.1-18-s failing +13.1-19-s failing +13.1-2-1 failing +13.1-2-5 failing +13.1-2-s failing +13.1-20-s failing +13.1-21-s failing +13.1-22-s failing +13.1-23-s failing +13.1-24-s failing +13.1-25-s failing +13.1-26-s failing +13.1-27-s failing +13.1-28-s failing +13.1-29-s failing +13.1-3-1 failing +13.1-3-2 failing +13.1-3-7 failing +13.1-3-8 failing +13.1-3-s failing +13.1-30-s failing +13.1-31-s failing +13.1-32-s failing +13.1-33-s failing +13.1-34-s failing +13.1-35-s failing +13.1-36-s failing +13.1-37-s failing +13.1-38-s failing +13.1-39-s failing +13.1-4-s failing +13.1-40-s failing +13.1-41-s failing +13.1-42-s failing +13.1-5-s failing +13.1-6-s failing +13.1-7-s failing +13.1-8-s failing +13.1-9-s failing +13.2-10-s failing +13.2-12-s failing +13.2-13-s failing +13.2-14-s failing +13.2-15-1 failing +13.2-16-s failing +13.2-17-1 failing +13.2-17-s failing +13.2-18-1 failing +13.2-18-s failing +13.2-2-s failing +13.2-20-s failing +13.2-21-s failing +13.2-22-s failing +13.2-24-s failing +13.2-25-s failing +13.2-26-s failing +13.2-28-s failing +13.2-29-s failing +13.2-30-s failing +13.2-31-s failing +13.2-32-s failing +13.2-33-s failing +13.2-34-s failing +13.2-35-s failing +13.2-36-s failing +13.2-4-s failing +13.2-5-s failing +13.2-6-s failing +13.2-8-s failing +13.2-9-s failing +S13.2.1_A4_T3 failing +S13.2.1_A4_T4 failing +S13.2.1_A7_T1 failing +S13.2.1_A7_T2 failing +S13.2.1_A7_T3 failing +S13.2.1_A7_T4 failing +S13.2.1_A8_T1 failing +S13.2.1_A8_T2 failing +S13.2.2_A10 failing +S13.2.2_A11 failing +S13.2.2_A12 failing +S13.2.2_A13 failing +S13.2.2_A14 failing +S13.2.2_A16_T2 failing +S13.2.2_A16_T3 failing +S13.2.2_A17_T2 failing +S13.2.2_A19_T1 failing +S13.2.2_A19_T3 failing +S13.2.2_A19_T4 failing +S13.2.2_A19_T5 failing +S13.2.2_A19_T6 failing +S13.2.2_A19_T8 failing +S13.2.2_A1_T1 failing +S13.2.2_A1_T2 failing +S13.2.2_A2 failing +S13.2.2_A5_T1 failing +S13.2.2_A5_T2 failing +S13.2.2_A8_T3 failing +S13.2.2_A9 failing +S13.2.3_A1 failing +S13.2_A2_T1 failing +S13.2_A2_T2 failing +S13.2_A3 failing +S13.2_A4_T1 failing +S13.2_A4_T2 failing +S14_A2 failing +S14_A5_T1 failing +S14_A5_T2 failing +14.1-5-s failing +15.1.1.3-2 failing +15.1.1.3-3 failing +S15.1.2.1_A2_T1 failing +S15.1.2.1_A3.2_T8 failing +S15.1.2.1_A3.3_T1 failing +S15.1.2.1_A3.3_T2 failing +S15.1.2.1_A3.3_T3 failing +S15.1.2.1_A3.3_T4 failing +S15.1.2.1_A4.1 failing +S15.1.2.1_A4.2 failing +S15.1.2.1_A4.3 failing +S15.1.2.1_A4.4 failing +S15.1.2.1_A4.5 failing +S15.1.2.1_A4.7 failing +15.1.2.2-2-1 failing +S15.1.2.2_A1_T1 failing +S15.1.2.2_A1_T2 failing +S15.1.2.2_A1_T3 failing +S15.1.2.2_A1_T4 failing +S15.1.2.2_A1_T5 failing +S15.1.2.2_A1_T6 failing +S15.1.2.2_A1_T7 failing +S15.1.2.2_A2_T1 failing +S15.1.2.2_A2_T10 failing +S15.1.2.2_A2_T2 failing +S15.1.2.2_A2_T3 failing +S15.1.2.2_A2_T4 failing +S15.1.2.2_A2_T5 failing +S15.1.2.2_A2_T6 failing +S15.1.2.2_A2_T7 failing +S15.1.2.2_A2_T8 failing +S15.1.2.2_A2_T9 failing +S15.1.2.2_A3.1_T1 failing +S15.1.2.2_A3.1_T2 failing +S15.1.2.2_A3.1_T3 failing +S15.1.2.2_A3.1_T4 failing +S15.1.2.2_A3.1_T5 failing +S15.1.2.2_A3.1_T6 failing +S15.1.2.2_A3.1_T7 failing +S15.1.2.2_A3.2_T1 failing +S15.1.2.2_A3.2_T2 failing +S15.1.2.2_A3.2_T3 failing +S15.1.2.2_A4.1_T1 failing +S15.1.2.2_A4.1_T2 failing +S15.1.2.2_A4.2_T1 failing +S15.1.2.2_A4.2_T2 failing +S15.1.2.2_A4.2_T3 failing +S15.1.2.2_A5.1_T1 failing +S15.1.2.2_A5.2_T1 failing +S15.1.2.2_A5.2_T2 failing +S15.1.2.2_A6.1_T1 failing +S15.1.2.2_A6.1_T2 failing +S15.1.2.2_A6.1_T3 failing +S15.1.2.2_A6.1_T4 failing +S15.1.2.2_A6.1_T5 failing +S15.1.2.2_A6.1_T6 failing +S15.1.2.2_A7.1_T1 failing +S15.1.2.2_A7.1_T2 failing +S15.1.2.2_A7.2_T1 failing +S15.1.2.2_A7.2_T2 failing +S15.1.2.2_A7.2_T3 failing +S15.1.2.2_A7.3_T1 failing +S15.1.2.2_A7.3_T2 failing +S15.1.2.2_A7.3_T3 failing +S15.1.2.2_A8 failing +S15.1.2.2_A9.1 failing +S15.1.2.2_A9.2 failing +S15.1.2.2_A9.3 failing +S15.1.2.2_A9.4 failing +S15.1.2.2_A9.6 failing +S15.1.2.2_A9.7 failing +15.1.2.3-2-1 failing +S15.1.2.3_A1_T1 failing +S15.1.2.3_A1_T2 failing +S15.1.2.3_A1_T3 failing +S15.1.2.3_A1_T4 failing +S15.1.2.3_A1_T5 failing +S15.1.2.3_A1_T6 failing +S15.1.2.3_A1_T7 failing +S15.1.2.3_A2_T1 failing +S15.1.2.3_A2_T10 failing +S15.1.2.3_A2_T2 failing +S15.1.2.3_A2_T3 failing +S15.1.2.3_A2_T4 failing +S15.1.2.3_A2_T5 failing +S15.1.2.3_A2_T6 failing +S15.1.2.3_A2_T7 failing +S15.1.2.3_A2_T8 failing +S15.1.2.3_A2_T9 failing +S15.1.2.3_A3_T1 failing +S15.1.2.3_A3_T2 failing +S15.1.2.3_A3_T3 failing +S15.1.2.3_A4_T1 failing +S15.1.2.3_A4_T2 failing +S15.1.2.3_A4_T3 failing +S15.1.2.3_A4_T4 failing +S15.1.2.3_A4_T5 failing +S15.1.2.3_A4_T6 failing +S15.1.2.3_A4_T7 failing +S15.1.2.3_A5_T1 failing +S15.1.2.3_A5_T2 failing +S15.1.2.3_A5_T3 failing +S15.1.2.3_A5_T4 failing +S15.1.2.3_A6 failing +S15.1.2.3_A7.1 failing +S15.1.2.3_A7.2 failing +S15.1.2.3_A7.3 failing +S15.1.2.3_A7.4 failing +S15.1.2.3_A7.6 failing +S15.1.2.3_A7.7 failing +S15.1.2.4_A2.1 failing +S15.1.2.4_A2.2 failing +S15.1.2.4_A2.3 failing +S15.1.2.4_A2.4 failing +S15.1.2.4_A2.5 failing +S15.1.2.4_A2.7 failing +S15.1.2.5_A2.1 failing +S15.1.2.5_A2.2 failing +S15.1.2.5_A2.3 failing +S15.1.2.5_A2.4 failing +S15.1.2.5_A2.5 failing +S15.1.2.5_A2.7 failing +S15.1.3.1_A1.10_T1 failing +S15.1.3.1_A1.11_T1 failing +S15.1.3.1_A1.11_T2 failing +S15.1.3.1_A1.12_T1 failing +S15.1.3.1_A1.12_T2 failing +S15.1.3.1_A1.12_T3 failing +S15.1.3.1_A1.13_T1 failing +S15.1.3.1_A1.13_T2 failing +S15.1.3.1_A1.14_T1 failing +S15.1.3.1_A1.14_T2 failing +S15.1.3.1_A1.14_T3 failing +S15.1.3.1_A1.14_T4 failing +S15.1.3.1_A1.15_T1 failing +S15.1.3.1_A1.15_T2 failing +S15.1.3.1_A1.15_T3 failing +S15.1.3.1_A1.15_T4 failing +S15.1.3.1_A1.15_T5 failing +S15.1.3.1_A1.15_T6 failing +S15.1.3.1_A1.1_T1 failing +S15.1.3.1_A1.2_T1 failing +S15.1.3.1_A1.2_T2 failing +S15.1.3.1_A1.3_T1 failing +S15.1.3.1_A1.3_T2 failing +S15.1.3.1_A1.4_T1 failing +S15.1.3.1_A1.5_T1 failing +S15.1.3.1_A1.6_T1 failing +S15.1.3.1_A1.7_T1 failing +S15.1.3.1_A1.8_T1 failing +S15.1.3.1_A1.8_T2 failing +S15.1.3.1_A1.9_T1 failing +S15.1.3.1_A1.9_T2 failing +S15.1.3.1_A1.9_T3 failing +S15.1.3.1_A2.1_T1 failing +S15.1.3.1_A2.2_T1 failing +S15.1.3.1_A2.3_T1 failing +S15.1.3.1_A2.4_T1 failing +S15.1.3.1_A3_T1 failing +S15.1.3.1_A3_T2 failing +S15.1.3.1_A3_T3 failing +S15.1.3.1_A4_T1 failing +S15.1.3.1_A4_T2 failing +S15.1.3.1_A4_T3 failing +S15.1.3.1_A4_T4 failing +S15.1.3.1_A5.1 failing +S15.1.3.1_A5.2 failing +S15.1.3.1_A5.3 failing +S15.1.3.1_A5.4 failing +S15.1.3.1_A5.6 failing +S15.1.3.1_A5.7 failing +S15.1.3.1_A6_T1 failing +S15.1.3.2_A1.10_T1 failing +S15.1.3.2_A1.11_T1 failing +S15.1.3.2_A1.11_T2 failing +S15.1.3.2_A1.12_T1 failing +S15.1.3.2_A1.12_T2 failing +S15.1.3.2_A1.12_T3 failing +S15.1.3.2_A1.13_T1 failing +S15.1.3.2_A1.13_T2 failing +S15.1.3.2_A1.14_T1 failing +S15.1.3.2_A1.14_T2 failing +S15.1.3.2_A1.14_T3 failing +S15.1.3.2_A1.14_T4 failing +S15.1.3.2_A1.15_T1 failing +S15.1.3.2_A1.15_T2 failing +S15.1.3.2_A1.15_T3 failing +S15.1.3.2_A1.15_T4 failing +S15.1.3.2_A1.15_T5 failing +S15.1.3.2_A1.15_T6 failing +S15.1.3.2_A1.1_T1 failing +S15.1.3.2_A1.2_T1 failing +S15.1.3.2_A1.2_T2 failing +S15.1.3.2_A1.3_T1 failing +S15.1.3.2_A1.3_T2 failing +S15.1.3.2_A1.4_T1 failing +S15.1.3.2_A1.5_T1 failing +S15.1.3.2_A1.6_T1 failing +S15.1.3.2_A1.7_T1 failing +S15.1.3.2_A1.8_T1 failing +S15.1.3.2_A1.8_T2 failing +S15.1.3.2_A1.9_T1 failing +S15.1.3.2_A1.9_T2 failing +S15.1.3.2_A1.9_T3 failing +S15.1.3.2_A2.1_T1 failing +S15.1.3.2_A2.2_T1 failing +S15.1.3.2_A2.3_T1 failing +S15.1.3.2_A2.4_T1 failing +S15.1.3.2_A3_T1 failing +S15.1.3.2_A3_T2 failing +S15.1.3.2_A3_T3 failing +S15.1.3.2_A4_T1 failing +S15.1.3.2_A4_T2 failing +S15.1.3.2_A4_T3 failing +S15.1.3.2_A4_T4 failing +S15.1.3.2_A5.1 failing +S15.1.3.2_A5.2 failing +S15.1.3.2_A5.3 failing +S15.1.3.2_A5.4 failing +S15.1.3.2_A5.6 failing +S15.1.3.2_A5.7 failing +S15.1.3.2_A6_T1 failing +S15.1.3.3_A1.1_T1 failing +S15.1.3.3_A1.1_T2 failing +S15.1.3.3_A1.2_T1 failing +S15.1.3.3_A1.2_T2 failing +S15.1.3.3_A1.3_T1 failing +S15.1.3.3_A2.1_T1 failing +S15.1.3.3_A2.2_T1 failing +S15.1.3.3_A2.3_T1 failing +S15.1.3.3_A2.4_T1 failing +S15.1.3.3_A2.4_T2 failing +S15.1.3.3_A2.5_T1 failing +S15.1.3.3_A3.1_T1 failing +S15.1.3.3_A3.2_T1 failing +S15.1.3.3_A3.2_T2 failing +S15.1.3.3_A3.2_T3 failing +S15.1.3.3_A3.3_T1 failing +S15.1.3.3_A4_T1 failing +S15.1.3.3_A4_T2 failing +S15.1.3.3_A4_T3 failing +S15.1.3.3_A4_T4 failing +S15.1.3.3_A5.1 failing +S15.1.3.3_A5.2 failing +S15.1.3.3_A5.3 failing +S15.1.3.3_A5.4 failing +S15.1.3.3_A5.6 failing +S15.1.3.3_A5.7 failing +S15.1.3.3_A6_T1 failing +S15.1.3.4_A1.1_T1 failing +S15.1.3.4_A1.1_T2 failing +S15.1.3.4_A1.2_T1 failing +S15.1.3.4_A1.2_T2 failing +S15.1.3.4_A1.3_T1 failing +S15.1.3.4_A2.1_T1 failing +S15.1.3.4_A2.2_T1 failing +S15.1.3.4_A2.3_T1 failing +S15.1.3.4_A2.4_T1 failing +S15.1.3.4_A2.4_T2 failing +S15.1.3.4_A2.5_T1 failing +S15.1.3.4_A3.1_T1 failing +S15.1.3.4_A3.2_T1 failing +S15.1.3.4_A3.2_T2 failing +S15.1.3.4_A3.2_T3 failing +S15.1.3.4_A3.3_T1 failing +S15.1.3.4_A4_T1 failing +S15.1.3.4_A4_T2 failing +S15.1.3.4_A4_T3 failing +S15.1.3.4_A4_T4 failing +S15.1.3.4_A5.1 failing +S15.1.3.4_A5.2 failing +S15.1.3.4_A5.3 failing +S15.1.3.4_A5.4 failing +S15.1.3.4_A5.6 failing +S15.1.3.4_A5.7 failing +S15.1.3.4_A6_T1 failing +S15.10.1_A1_T1 failing +S15.10.1_A1_T10 failing +S15.10.1_A1_T11 failing +S15.10.1_A1_T12 failing +S15.10.1_A1_T13 failing +S15.10.1_A1_T14 failing +S15.10.1_A1_T15 failing +S15.10.1_A1_T16 failing +S15.10.1_A1_T2 failing +S15.10.1_A1_T3 failing +S15.10.1_A1_T4 failing +S15.10.1_A1_T5 failing +S15.10.1_A1_T6 failing +S15.10.1_A1_T7 failing +S15.10.1_A1_T8 failing +S15.10.1_A1_T9 failing +15.10.2.15-3-1 failing +15.10.2.15-3-2 failing +15.10.2.15-6-1 failing +15.10.2.2-1 failing +15.10.2.5-3-1 failing +S15.10.2_A1_T1 failing +S15.10.2.10_A2.1_T3 failing +S15.10.2.10_A4.1_T1 failing +S15.10.2.10_A4.1_T2 failing +S15.10.2.10_A4.1_T3 failing +S15.10.2.10_A5.1_T1 failing +S15.10.2.11_A1_T5 failing +S15.10.2.11_A1_T7 failing +S15.10.2.12_A1_T1 failing +S15.10.2.12_A1_T2 failing +S15.10.2.12_A1_T5 failing +S15.10.2.12_A2_T1 failing +S15.10.2.12_A2_T2 failing +S15.10.2.12_A2_T5 failing +S15.10.2.12_A3_T1 failing +S15.10.2.12_A4_T1 failing +S15.10.2.12_A5_T1 failing +S15.10.2.12_A6_T1 failing +S15.10.2.13_A2_T1 failing +S15.10.2.13_A2_T2 failing +S15.10.2.13_A2_T8 failing +S15.10.2.15_A1_T1 failing +S15.10.2.15_A1_T10 failing +S15.10.2.15_A1_T11 failing +S15.10.2.15_A1_T12 failing +S15.10.2.15_A1_T13 failing +S15.10.2.15_A1_T14 failing +S15.10.2.15_A1_T15 failing +S15.10.2.15_A1_T16 failing +S15.10.2.15_A1_T17 failing +S15.10.2.15_A1_T18 failing +S15.10.2.15_A1_T19 failing +S15.10.2.15_A1_T2 failing +S15.10.2.15_A1_T20 failing +S15.10.2.15_A1_T21 failing +S15.10.2.15_A1_T22 failing +S15.10.2.15_A1_T23 failing +S15.10.2.15_A1_T24 failing +S15.10.2.15_A1_T25 failing +S15.10.2.15_A1_T26 failing +S15.10.2.15_A1_T27 failing +S15.10.2.15_A1_T28 failing +S15.10.2.15_A1_T29 failing +S15.10.2.15_A1_T3 failing +S15.10.2.15_A1_T30 failing +S15.10.2.15_A1_T31 failing +S15.10.2.15_A1_T32 failing +S15.10.2.15_A1_T33 failing +S15.10.2.15_A1_T34 failing +S15.10.2.15_A1_T35 failing +S15.10.2.15_A1_T36 failing +S15.10.2.15_A1_T37 failing +S15.10.2.15_A1_T38 failing +S15.10.2.15_A1_T39 failing +S15.10.2.15_A1_T4 failing +S15.10.2.15_A1_T40 failing +S15.10.2.15_A1_T41 failing +S15.10.2.15_A1_T5 failing +S15.10.2.15_A1_T6 failing +S15.10.2.15_A1_T7 failing +S15.10.2.15_A1_T8 failing +S15.10.2.15_A1_T9 failing +S15.10.2.3_A1_T15 failing +S15.10.2.3_A1_T17 failing +S15.10.2.3_A1_T2 failing +S15.10.2.5_A1_T4 failing +S15.10.2.7_A5_T2 failing +S15.10.2.8_A1_T4 failing +S15.10.2.8_A2_T1 failing +S15.10.2.8_A3_T15 failing +S15.10.2.8_A3_T16 failing +S15.10.2.8_A3_T17 failing +S15.10.2.8_A3_T18 failing +S15.10.2.8_A3_T19 failing +S15.10.2.8_A3_T2 failing +S15.10.2.8_A3_T22 failing +S15.10.2.8_A3_T25 failing +S15.10.2.8_A3_T26 failing +S15.10.2.8_A3_T27 failing +S15.10.2.8_A3_T28 failing +S15.10.2.8_A3_T29 failing +S15.10.2.9_A1_T2 failing +S15.10.3.1_A2_T1 failing +S15.10.3.1_A2_T2 failing +S15.10.4.1_A2_T1 failing +S15.10.4.1_A2_T2 failing +S15.10.4.1_A5_T1 failing +S15.10.4.1_A5_T2 failing +S15.10.4.1_A5_T3 failing +S15.10.4.1_A5_T4 failing +S15.10.4.1_A5_T6 failing +S15.10.4.1_A5_T7 failing +S15.10.4.1_A5_T8 failing +S15.10.4.1_A5_T9 failing +S15.10.4.1_A8_T12 failing +S15.10.4.1_A8_T13 failing +S15.10.4.1_A8_T2 failing +S15.10.4.1_A8_T6 failing +S15.10.4.1_A8_T7 failing +S15.10.4.1_A8_T8 failing +S15.10.4.1_A9_T1 failing +S15.10.4.1_A9_T2 failing +S15.10.4.1_A9_T3 failing +15.10.4.1-1 failing +15.10.4.1-2 failing +15.10.4.1-3 failing +15.10.4.1-4 failing +S15.10.5.1_A2 failing +S15.10.5.1_A3 failing +S15.10.5.1_A4 failing +S15.10.5_A1 failing +S15.10.6.2_A10 failing +S15.10.6.2_A11 failing +S15.10.6.2_A1_T2 failing +S15.10.6.2_A1_T6 failing +S15.10.6.2_A1_T7 failing +S15.10.6.2_A1_T8 failing +S15.10.6.2_A2_T1 failing +S15.10.6.2_A2_T10 failing +S15.10.6.2_A2_T2 failing +S15.10.6.2_A2_T3 failing +S15.10.6.2_A2_T4 failing +S15.10.6.2_A2_T5 failing +S15.10.6.2_A2_T6 failing +S15.10.6.2_A2_T7 failing +S15.10.6.2_A2_T8 failing +S15.10.6.2_A2_T9 failing +S15.10.6.2_A4_T1 failing +S15.10.6.2_A4_T10 failing +S15.10.6.2_A4_T11 failing +S15.10.6.2_A4_T12 failing +S15.10.6.2_A4_T2 failing +S15.10.6.2_A4_T3 failing +S15.10.6.2_A4_T4 failing +S15.10.6.2_A4_T5 failing +S15.10.6.2_A4_T6 failing +S15.10.6.2_A4_T7 failing +S15.10.6.2_A4_T8 failing +S15.10.6.2_A4_T9 failing +S15.10.6.2_A5_T1 failing +S15.10.6.2_A5_T2 failing +S15.10.6.2_A5_T3 failing +S15.10.6.2_A7 failing +S15.10.6.2_A8 failing +S15.10.6.2_A9 failing +S15.10.6.3_A10 failing +S15.10.6.3_A11 failing +S15.10.6.3_A1_T7 failing +S15.10.6.3_A1_T8 failing +S15.10.6.3_A2_T1 failing +S15.10.6.3_A2_T10 failing +S15.10.6.3_A2_T2 failing +S15.10.6.3_A2_T3 failing +S15.10.6.3_A2_T4 failing +S15.10.6.3_A2_T5 failing +S15.10.6.3_A2_T6 failing +S15.10.6.3_A2_T7 failing +S15.10.6.3_A2_T8 failing +S15.10.6.3_A2_T9 failing +S15.10.6.3_A7 failing +S15.10.6.3_A8 failing +S15.10.6.3_A9 failing +S15.10.6.4_A10 failing +S15.10.6.4_A11 failing +S15.10.6.4_A7 failing +S15.10.6.4_A8 failing +S15.10.6.4_A9 failing +S15.10.7_A1_T1 failing +S15.10.7_A1_T2 failing +S15.10.7_A2_T1 failing +S15.10.7_A2_T2 failing +15.10.7.1-2 failing +S15.10.7.1_A10 failing +S15.10.7.1_A8 failing +S15.10.7.1_A9 failing +15.10.7.2-2 failing +S15.10.7.2_A10 failing +S15.10.7.2_A8 failing +S15.10.7.2_A9 failing +15.10.7.3-2 failing +S15.10.7.3_A10 failing +S15.10.7.3_A8 failing +S15.10.7.3_A9 failing +15.10.7.4-2 failing +S15.10.7.4_A10 failing +S15.10.7.4_A8 failing +S15.10.7.4_A9 failing +15.10.7.5-2 failing +S15.10.7.5_A8 failing +S15.10.7.5_A9 failing +S15.11.3.1_A1_T1 failing +S15.11.3.1_A2_T1 failing +S15.11.3.1_A3_T1 failing +S15.11.3_A2_T1 failing +S15.11.4.2_A1 failing +S15.11.4.2_A2 failing +S15.11.4.3_A1 failing +S15.11.4.3_A2 failing +S15.11.4.4_A2 failing +S15.11.4_A3 failing +S15.11.4_A4 failing +15.12-0-1 failing +15.12-0-2 failing +15.12-0-3 failing +15.12-0-4 failing +15.12.1.1-0-1 failing +15.12.1.1-0-2 failing +15.12.1.1-0-3 failing +15.12.1.1-0-4 failing +15.12.1.1-0-5 failing +15.12.1.1-0-6 failing +15.12.1.1-0-7 failing +15.12.1.1-0-8 failing +15.12.1.1-0-9 failing +15.12.1.1-g1-1 failing +15.12.1.1-g1-2 failing +15.12.1.1-g1-3 failing +15.12.1.1-g1-4 failing +15.12.1.1-g2-1 failing +15.12.1.1-g2-2 failing +15.12.1.1-g2-3 failing +15.12.1.1-g2-4 failing +15.12.1.1-g2-5 failing +15.12.1.1-g4-1 failing +15.12.1.1-g4-2 failing +15.12.1.1-g4-3 failing +15.12.1.1-g4-4 failing +15.12.1.1-g5-1 failing +15.12.1.1-g5-2 failing +15.12.1.1-g5-3 failing +15.12.1.1-g6-1 failing +15.12.1.1-g6-2 failing +15.12.1.1-g6-3 failing +15.12.1.1-g6-4 failing +15.12.1.1-g6-5 failing +15.12.1.1-g6-6 failing +15.12.1.1-g6-7 failing +15.12.2-0-1 failing +15.12.2-0-2 failing +15.12.2-0-3 failing +S15.12.2_A1 failing +15.12.3-0-1 failing +15.12.3-0-2 failing +15.12.3-0-3 failing +15.12.3-11-1 failing +15.12.3-11-10 failing +15.12.3-11-11 failing +15.12.3-11-12 failing +15.12.3-11-13 failing +15.12.3-11-14 failing +15.12.3-11-15 failing +15.12.3-11-2 failing +15.12.3-11-26 failing +15.12.3-11-3 failing +15.12.3-11-4 failing +15.12.3-11-5 failing +15.12.3-11-6 failing +15.12.3-11-7 failing +15.12.3-11-8 failing +15.12.3-11-9 failing +15.12.3-4-1 failing +15.12.3-5-a-i-1 failing +15.12.3-5-b-i-1 failing +15.12.3-6-a-1 failing +15.12.3-6-a-2 failing +15.12.3-6-b-1 failing +15.12.3-6-b-2 failing +15.12.3-6-b-3 failing +15.12.3-6-b-4 failing +15.12.3-7-a-1 failing +15.12.3-8-a-1 failing +15.12.3-8-a-2 failing +15.12.3-8-a-3 failing +15.12.3-8-a-4 failing +15.12.3-8-a-5 failing +15.12.3_2-2-b-i-1 failing +15.12.3_2-2-b-i-2 failing +15.12.3_2-2-b-i-3 failing +15.12.3_2-3-a-1 failing +15.12.3_2-3-a-2 failing +15.12.3_2-3-a-3 failing +15.12.3_4-1-1 failing +15.12.3_4-1-2 failing +15.12.3_4-1-3 failing +S15.2.3_A1 failing +S15.2.3_A2 failing +S15.2.3_A3 failing +15.2.3.1 failing +S15.2.3.1_A1 failing +S15.2.3.1_A2 failing +S15.2.3.1_A3 failing +15.2.3.10-0-2 failing +15.2.3.10-1-1 failing +15.2.3.10-1-2 failing +15.2.3.10-1-3 failing +15.2.3.10-1-4 failing +15.2.3.10-1 failing +15.2.3.10-3-12 failing +15.2.3.10-3-5 failing +15.2.3.11-0-2 failing +15.2.3.11-1 failing +15.2.3.11-4-27 failing +15.2.3.12-0-2 failing +15.2.3.12-1-1 failing +15.2.3.12-1-2 failing +15.2.3.12-1-3 failing +15.2.3.12-1-4 failing +15.2.3.12-1 failing +15.2.3.12-3-27 failing +15.2.3.13-0-2 failing +15.2.3.13-1-1 failing +15.2.3.13-1-2 failing +15.2.3.13-1-3 failing +15.2.3.13-1-4 failing +15.2.3.13-1 failing +15.2.3.13-2-12 failing +15.2.3.14-0-2 failing +15.2.3.14-1-1 failing +15.2.3.14-1-2 failing +15.2.3.14-1-3 failing +15.2.3.14-1-4 failing +15.2.3.14-1-5 failing +15.2.3.14-2-1 failing +15.2.3.14-3-2 failing +15.2.3.14-3-3 failing +15.2.3.14-3-4 failing +15.2.3.14-4-1 failing +15.2.3.14-5-1 failing +15.2.3.14-5-12 failing +15.2.3.14-5-13 failing +15.2.3.14-5-14 failing +15.2.3.14-5-2 failing +15.2.3.14-5-a-1 failing +15.2.3.14-5-a-2 failing +15.2.3.14-5-a-3 failing +15.2.3.14-5-a-4 failing +15.2.3.2-0-2 failing +15.2.3.2-0-3 failing +15.2.3.2-1-2 failing +15.2.3.2-1-3 failing +15.2.3.2-1-4 failing +15.2.3.2-1 failing +15.2.3.2-2-12 failing +15.2.3.2-2-13 failing +15.2.3.2-2-14 failing +15.2.3.2-2-15 failing +15.2.3.2-2-16 failing +15.2.3.2-2-17 failing +15.2.3.2-2-18 failing +15.2.3.2-2-3 failing +15.2.3.2-2-31 failing +15.2.3.3-0-2 failing +15.2.3.3-1-1 failing +15.2.3.3-1-2 failing +15.2.3.3-1-3 failing +15.2.3.3-1-4 failing +15.2.3.3-1 failing +15.2.3.3-2-46 failing +15.2.3.3-3-13 failing +15.2.3.3-3-14 failing +15.2.3.3-4-10 failing +15.2.3.3-4-11 failing +15.2.3.3-4-12 failing +15.2.3.3-4-13 failing +15.2.3.3-4-161 failing +15.2.3.3-4-162 failing +15.2.3.3-4-164 failing +15.2.3.3-4-176 failing +15.2.3.3-4-177 failing +15.2.3.3-4-182 failing +15.2.3.3-4-185 failing +15.2.3.3-4-186 failing +15.2.3.3-4-187 failing +15.2.3.3-4-189 failing +15.2.3.3-4-190 failing +15.2.3.3-4-191 failing +15.2.3.3-4-192 failing +15.2.3.3-4-193 failing +15.2.3.3-4-194 failing +15.2.3.3-4-195 failing +15.2.3.3-4-201 failing +15.2.3.3-4-210 failing +15.2.3.3-4-211 failing +15.2.3.3-4-212 failing +15.2.3.3-4-213 failing +15.2.3.3-4-214 failing +15.2.3.3-4-215 failing +15.2.3.3-4-216 failing +15.2.3.3-4-217 failing +15.2.3.3-4-218 failing +15.2.3.3-4-219 failing +15.2.3.3-4-220 failing +15.2.3.3-4-221 failing +15.2.3.3-4-222 failing +15.2.3.3-4-224 failing +15.2.3.3-4-226 failing +15.2.3.3-4-228 failing +15.2.3.3-4-230 failing +15.2.3.3-4-232 failing +15.2.3.3-4-234 failing +15.2.3.3-4-236 failing +15.2.3.3-4-238 failing +15.2.3.3-4-240 failing +15.2.3.3-4-242 failing +15.2.3.3-4-244 failing +15.2.3.3-4-246 failing +15.2.3.3-4-5 failing +15.2.3.3-4-51 failing +15.2.3.3-4-6 failing +15.2.3.3-4-82 failing +15.2.3.3-4-9 failing +15.2.3.3-4-90 failing +15.2.3.4-0-2 failing +15.2.3.4-1-2 failing +15.2.3.4-1-3 failing +15.2.3.4-1-4 failing +15.2.3.4-1-5 failing +15.2.3.4-1 failing +15.2.3.4-2-1 failing +15.2.3.4-2-4 failing +15.2.3.4-3-1 failing +15.2.3.4-4-2 failing +15.2.3.4-4-40 failing +15.2.3.4-4-41 failing +15.2.3.4-4-42 failing +15.2.3.4-4-43 failing +15.2.3.4-4-44 failing +15.2.3.4-4-45 failing +15.2.3.4-4-46 failing +15.2.3.4-4-47 failing +15.2.3.4-4-48 failing +15.2.3.4-4-49 failing +15.2.3.4-4-b-1 failing +15.2.3.4-4-b-3 failing +15.2.3.4-4-b-4 failing +15.2.3.4-4-b-5 failing +15.2.3.4-4-b-6 failing +15.2.3.5-0-2 failing +15.2.3.5-1-1 failing +15.2.3.5-1-2 failing +15.2.3.5-1-3 failing +15.2.3.5-1-4 failing +15.2.3.5-1 failing +15.2.3.5-4-10 failing +15.2.3.5-4-117 failing +15.2.3.5-4-120 failing +15.2.3.5-4-124 failing +15.2.3.5-4-13 failing +15.2.3.5-4-145 failing +15.2.3.5-4-170 failing +15.2.3.5-4-173 failing +15.2.3.5-4-177 failing +15.2.3.5-4-196 failing +15.2.3.5-4-199 failing +15.2.3.5-4-2 failing +15.2.3.5-4-203 failing +15.2.3.5-4-224 failing +15.2.3.5-4-251 failing +15.2.3.5-4-252 failing +15.2.3.5-4-256 failing +15.2.3.5-4-258 failing +15.2.3.5-4-259 failing +15.2.3.5-4-26 failing +15.2.3.5-4-260 failing +15.2.3.5-4-261 failing +15.2.3.5-4-262 failing +15.2.3.5-4-27 failing +15.2.3.5-4-284 failing +15.2.3.5-4-287 failing +15.2.3.5-4-291 failing +15.2.3.5-4-293 failing +15.2.3.5-4-294 failing +15.2.3.5-4-295 failing +15.2.3.5-4-296 failing +15.2.3.5-4-297 failing +15.2.3.5-4-3 failing +15.2.3.5-4-300 failing +15.2.3.5-4-301 failing +15.2.3.5-4-302 failing +15.2.3.5-4-303 failing +15.2.3.5-4-304 failing +15.2.3.5-4-305 failing +15.2.3.5-4-306 failing +15.2.3.5-4-309 failing +15.2.3.5-4-315 failing +15.2.3.5-4-33 failing +15.2.3.5-4-36 failing +15.2.3.5-4-40 failing +15.2.3.5-4-41 failing +15.2.3.5-4-42 failing +15.2.3.5-4-43 failing +15.2.3.5-4-44 failing +15.2.3.5-4-45 failing +15.2.3.5-4-64 failing +15.2.3.5-4-67 failing +15.2.3.5-4-71 failing +15.2.3.5-4-92 failing +15.2.3.6-0-2 failing +15.2.3.6-1-1 failing +15.2.3.6-1-2 failing +15.2.3.6-1-3 failing +15.2.3.6-1-4 failing +15.2.3.6-1 failing +15.2.3.6-2-17-1 failing +15.2.3.6-2-47 failing +15.2.3.6-3-1 failing +15.2.3.6-3-10 failing +15.2.3.6-3-11 failing +15.2.3.6-3-119 failing +15.2.3.6-3-12 failing +15.2.3.6-3-13 failing +15.2.3.6-3-139-1 failing +15.2.3.6-3-14 failing +15.2.3.6-3-140-1 failing +15.2.3.6-3-141-1 failing +15.2.3.6-3-142-1 failing +15.2.3.6-3-143-1 failing +15.2.3.6-3-144-1 failing +15.2.3.6-3-144 failing +15.2.3.6-3-145-1 failing +15.2.3.6-3-146-1 failing +15.2.3.6-3-147-1 failing +15.2.3.6-3-147 failing +15.2.3.6-3-148-1 failing +15.2.3.6-3-149-1 failing +15.2.3.6-3-15 failing +15.2.3.6-3-151 failing +15.2.3.6-3-16 failing +15.2.3.6-3-165-1 failing +15.2.3.6-3-166-1 failing +15.2.3.6-3-167-1 failing +15.2.3.6-3-168-1 failing +15.2.3.6-3-169-1 failing +15.2.3.6-3-17 failing +15.2.3.6-3-170-1 failing +15.2.3.6-3-170 failing +15.2.3.6-3-171-1 failing +15.2.3.6-3-172-1 failing +15.2.3.6-3-173-1 failing +15.2.3.6-3-173 failing +15.2.3.6-3-174-1 failing +15.2.3.6-3-175-1 failing +15.2.3.6-3-177 failing +15.2.3.6-3-18 failing +15.2.3.6-3-19 failing +15.2.3.6-3-198 failing +15.2.3.6-3-2 failing +15.2.3.6-3-218-1 failing +15.2.3.6-3-219-1 failing +15.2.3.6-3-220-1 failing +15.2.3.6-3-221-1 failing +15.2.3.6-3-222-1 failing +15.2.3.6-3-223-1 failing +15.2.3.6-3-223 failing +15.2.3.6-3-224-1 failing +15.2.3.6-3-225-1 failing +15.2.3.6-3-226-1 failing +15.2.3.6-3-226 failing +15.2.3.6-3-227-1 failing +15.2.3.6-3-228-1 failing +15.2.3.6-3-230 failing +15.2.3.6-3-248-1 failing +15.2.3.6-3-249-1 failing +15.2.3.6-3-250-1 failing +15.2.3.6-3-251-1 failing +15.2.3.6-3-252-1 failing +15.2.3.6-3-253-1 failing +15.2.3.6-3-253 failing +15.2.3.6-3-254-1 failing +15.2.3.6-3-255-1 failing +15.2.3.6-3-256-1 failing +15.2.3.6-3-256 failing +15.2.3.6-3-257-1 failing +15.2.3.6-3-258-1 failing +15.2.3.6-3-260 failing +15.2.3.6-3-3 failing +15.2.3.6-3-33-1 failing +15.2.3.6-3-34-1 failing +15.2.3.6-3-35-1 failing +15.2.3.6-3-36-1 failing +15.2.3.6-3-37-1 failing +15.2.3.6-3-38-1 failing +15.2.3.6-3-38 failing +15.2.3.6-3-39-1 failing +15.2.3.6-3-4 failing +15.2.3.6-3-40-1 failing +15.2.3.6-3-41-1 failing +15.2.3.6-3-41 failing +15.2.3.6-3-42-1 failing +15.2.3.6-3-43-1 failing +15.2.3.6-3-45 failing +15.2.3.6-3-5 failing +15.2.3.6-3-6 failing +15.2.3.6-3-66 failing +15.2.3.6-3-7 failing +15.2.3.6-3-8 failing +15.2.3.6-3-86-1 failing +15.2.3.6-3-87-1 failing +15.2.3.6-3-88-1 failing +15.2.3.6-3-89-1 failing +15.2.3.6-3-9 failing +15.2.3.6-3-90-1 failing +15.2.3.6-3-91-1 failing +15.2.3.6-3-91 failing +15.2.3.6-3-92-1 failing +15.2.3.6-3-93-1 failing +15.2.3.6-3-94-1 failing +15.2.3.6-3-94 failing +15.2.3.6-3-95-1 failing +15.2.3.6-3-96-1 failing +15.2.3.6-3-98 failing +15.2.3.6-4-1 failing +15.2.3.6-4-10 failing +15.2.3.6-4-100 failing +15.2.3.6-4-101 failing +15.2.3.6-4-102 failing +15.2.3.6-4-103 failing +15.2.3.6-4-104 failing +15.2.3.6-4-105 failing +15.2.3.6-4-106 failing +15.2.3.6-4-107 failing +15.2.3.6-4-108 failing +15.2.3.6-4-109 failing +15.2.3.6-4-11 failing +15.2.3.6-4-110 failing +15.2.3.6-4-111 failing +15.2.3.6-4-112 failing +15.2.3.6-4-113 failing +15.2.3.6-4-114 failing +15.2.3.6-4-115 failing +15.2.3.6-4-116 failing +15.2.3.6-4-117 failing +15.2.3.6-4-118 failing +15.2.3.6-4-119 failing +15.2.3.6-4-12 failing +15.2.3.6-4-120 failing +15.2.3.6-4-121 failing +15.2.3.6-4-122 failing +15.2.3.6-4-123 failing +15.2.3.6-4-124 failing +15.2.3.6-4-125 failing +15.2.3.6-4-126 failing +15.2.3.6-4-127 failing +15.2.3.6-4-128 failing +15.2.3.6-4-129 failing +15.2.3.6-4-13 failing +15.2.3.6-4-130 failing +15.2.3.6-4-131 failing +15.2.3.6-4-132 failing +15.2.3.6-4-133 failing +15.2.3.6-4-134 failing +15.2.3.6-4-135 failing +15.2.3.6-4-136 failing +15.2.3.6-4-137 failing +15.2.3.6-4-138 failing +15.2.3.6-4-139 failing +15.2.3.6-4-140 failing +15.2.3.6-4-141 failing +15.2.3.6-4-142 failing +15.2.3.6-4-143 failing +15.2.3.6-4-144 failing +15.2.3.6-4-145 failing +15.2.3.6-4-146 failing +15.2.3.6-4-147 failing +15.2.3.6-4-148 failing +15.2.3.6-4-149 failing +15.2.3.6-4-150 failing +15.2.3.6-4-151 failing +15.2.3.6-4-152 failing +15.2.3.6-4-153 failing +15.2.3.6-4-154 failing +15.2.3.6-4-155 failing +15.2.3.6-4-156 failing +15.2.3.6-4-157 failing +15.2.3.6-4-159 failing +15.2.3.6-4-16 failing +15.2.3.6-4-161 failing +15.2.3.6-4-162 failing +15.2.3.6-4-163 failing +15.2.3.6-4-164 failing +15.2.3.6-4-165 failing +15.2.3.6-4-166 failing +15.2.3.6-4-167 failing +15.2.3.6-4-168 failing +15.2.3.6-4-169 failing +15.2.3.6-4-17 failing +15.2.3.6-4-170 failing +15.2.3.6-4-171 failing +15.2.3.6-4-172 failing +15.2.3.6-4-173 failing +15.2.3.6-4-174 failing +15.2.3.6-4-175 failing +15.2.3.6-4-176 failing +15.2.3.6-4-177 failing +15.2.3.6-4-179-1 failing +15.2.3.6-4-18 failing +15.2.3.6-4-181 failing +15.2.3.6-4-182 failing +15.2.3.6-4-183 failing +15.2.3.6-4-184 failing +15.2.3.6-4-185 failing +15.2.3.6-4-186 failing +15.2.3.6-4-187 failing +15.2.3.6-4-188 failing +15.2.3.6-4-189 failing +15.2.3.6-4-190 failing +15.2.3.6-4-191 failing +15.2.3.6-4-192 failing +15.2.3.6-4-193 failing +15.2.3.6-4-194 failing +15.2.3.6-4-195 failing +15.2.3.6-4-196 failing +15.2.3.6-4-197 failing +15.2.3.6-4-198 failing +15.2.3.6-4-199 failing +15.2.3.6-4-20 failing +15.2.3.6-4-200 failing +15.2.3.6-4-201 failing +15.2.3.6-4-202 failing +15.2.3.6-4-203 failing +15.2.3.6-4-204 failing +15.2.3.6-4-205 failing +15.2.3.6-4-207 failing +15.2.3.6-4-208 failing +15.2.3.6-4-209 failing +15.2.3.6-4-210 failing +15.2.3.6-4-211 failing +15.2.3.6-4-212 failing +15.2.3.6-4-213 failing +15.2.3.6-4-214 failing +15.2.3.6-4-215 failing +15.2.3.6-4-216 failing +15.2.3.6-4-217 failing +15.2.3.6-4-218 failing +15.2.3.6-4-219 failing +15.2.3.6-4-22 failing +15.2.3.6-4-220 failing +15.2.3.6-4-221 failing +15.2.3.6-4-222 failing +15.2.3.6-4-223 failing +15.2.3.6-4-224 failing +15.2.3.6-4-225 failing +15.2.3.6-4-226 failing +15.2.3.6-4-227 failing +15.2.3.6-4-228 failing +15.2.3.6-4-229 failing +15.2.3.6-4-230 failing +15.2.3.6-4-231 failing +15.2.3.6-4-232 failing +15.2.3.6-4-233 failing +15.2.3.6-4-234 failing +15.2.3.6-4-235 failing +15.2.3.6-4-236 failing +15.2.3.6-4-237 failing +15.2.3.6-4-238 failing +15.2.3.6-4-239 failing +15.2.3.6-4-24 failing +15.2.3.6-4-240 failing +15.2.3.6-4-241 failing +15.2.3.6-4-242-1 failing +15.2.3.6-4-242 failing +15.2.3.6-4-243-1 failing +15.2.3.6-4-243 failing +15.2.3.6-4-244 failing +15.2.3.6-4-245 failing +15.2.3.6-4-246 failing +15.2.3.6-4-247 failing +15.2.3.6-4-248 failing +15.2.3.6-4-249 failing +15.2.3.6-4-25 failing +15.2.3.6-4-250 failing +15.2.3.6-4-251 failing +15.2.3.6-4-252 failing +15.2.3.6-4-253 failing +15.2.3.6-4-255 failing +15.2.3.6-4-256 failing +15.2.3.6-4-257 failing +15.2.3.6-4-258 failing +15.2.3.6-4-259 failing +15.2.3.6-4-26 failing +15.2.3.6-4-260 failing +15.2.3.6-4-261 failing +15.2.3.6-4-262 failing +15.2.3.6-4-263 failing +15.2.3.6-4-264 failing +15.2.3.6-4-265 failing +15.2.3.6-4-266 failing +15.2.3.6-4-267 failing +15.2.3.6-4-268 failing +15.2.3.6-4-269 failing +15.2.3.6-4-270 failing +15.2.3.6-4-271 failing +15.2.3.6-4-272 failing +15.2.3.6-4-273 failing +15.2.3.6-4-274 failing +15.2.3.6-4-275 failing +15.2.3.6-4-276 failing +15.2.3.6-4-277 failing +15.2.3.6-4-278 failing +15.2.3.6-4-279 failing +15.2.3.6-4-28 failing +15.2.3.6-4-280 failing +15.2.3.6-4-281 failing +15.2.3.6-4-282 failing +15.2.3.6-4-283 failing +15.2.3.6-4-284 failing +15.2.3.6-4-285 failing +15.2.3.6-4-286 failing +15.2.3.6-4-287 failing +15.2.3.6-4-288 failing +15.2.3.6-4-289-1 failing +15.2.3.6-4-289 failing +15.2.3.6-4-29 failing +15.2.3.6-4-290-1 failing +15.2.3.6-4-290 failing +15.2.3.6-4-291-1 failing +15.2.3.6-4-291 failing +15.2.3.6-4-292-1 failing +15.2.3.6-4-292 failing +15.2.3.6-4-293-1 failing +15.2.3.6-4-293-2 failing +15.2.3.6-4-293-3 failing +15.2.3.6-4-293 failing +15.2.3.6-4-294-1 failing +15.2.3.6-4-294 failing +15.2.3.6-4-295-1 failing +15.2.3.6-4-295 failing +15.2.3.6-4-296-1 failing +15.2.3.6-4-296 failing +15.2.3.6-4-297-1 failing +15.2.3.6-4-297 failing +15.2.3.6-4-298-1 failing +15.2.3.6-4-298 failing +15.2.3.6-4-299-1 failing +15.2.3.6-4-299 failing +15.2.3.6-4-30 failing +15.2.3.6-4-300-1 failing +15.2.3.6-4-300 failing +15.2.3.6-4-301-1 failing +15.2.3.6-4-301 failing +15.2.3.6-4-302-1 failing +15.2.3.6-4-302 failing +15.2.3.6-4-303 failing +15.2.3.6-4-304 failing +15.2.3.6-4-305 failing +15.2.3.6-4-306 failing +15.2.3.6-4-307 failing +15.2.3.6-4-308 failing +15.2.3.6-4-309 failing +15.2.3.6-4-31 failing +15.2.3.6-4-310 failing +15.2.3.6-4-311 failing +15.2.3.6-4-312 failing +15.2.3.6-4-313-1 failing +15.2.3.6-4-313 failing +15.2.3.6-4-314-1 failing +15.2.3.6-4-314 failing +15.2.3.6-4-315-1 failing +15.2.3.6-4-315 failing +15.2.3.6-4-316-1 failing +15.2.3.6-4-316 failing +15.2.3.6-4-317-1 failing +15.2.3.6-4-317 failing +15.2.3.6-4-318-1 failing +15.2.3.6-4-318 failing +15.2.3.6-4-319-1 failing +15.2.3.6-4-319 failing +15.2.3.6-4-320-1 failing +15.2.3.6-4-320 failing +15.2.3.6-4-321-1 failing +15.2.3.6-4-321 failing +15.2.3.6-4-322-1 failing +15.2.3.6-4-322 failing +15.2.3.6-4-323-1 failing +15.2.3.6-4-323 failing +15.2.3.6-4-324-1 failing +15.2.3.6-4-324 failing +15.2.3.6-4-329 failing +15.2.3.6-4-33 failing +15.2.3.6-4-330 failing +15.2.3.6-4-331 failing +15.2.3.6-4-333-1 failing +15.2.3.6-4-333-2 failing +15.2.3.6-4-333-3 failing +15.2.3.6-4-333-4 failing +15.2.3.6-4-333-6 failing +15.2.3.6-4-333-7 failing +15.2.3.6-4-333-8 failing +15.2.3.6-4-336 failing +15.2.3.6-4-337 failing +15.2.3.6-4-338 failing +15.2.3.6-4-339-1 failing +15.2.3.6-4-339-2 failing +15.2.3.6-4-339-3 failing +15.2.3.6-4-339-4 failing +15.2.3.6-4-339 failing +15.2.3.6-4-34 failing +15.2.3.6-4-343 failing +15.2.3.6-4-344 failing +15.2.3.6-4-345 failing +15.2.3.6-4-35 failing +15.2.3.6-4-350 failing +15.2.3.6-4-351 failing +15.2.3.6-4-352 failing +15.2.3.6-4-353 failing +15.2.3.6-4-354-1 failing +15.2.3.6-4-354-10 failing +15.2.3.6-4-354-11 failing +15.2.3.6-4-354-12 failing +15.2.3.6-4-354-13 failing +15.2.3.6-4-354-2 failing +15.2.3.6-4-354-3 failing +15.2.3.6-4-354-4 failing +15.2.3.6-4-354-6 failing +15.2.3.6-4-354-8 failing +15.2.3.6-4-357 failing +15.2.3.6-4-358 failing +15.2.3.6-4-359 failing +15.2.3.6-4-36 failing +15.2.3.6-4-360-1 failing +15.2.3.6-4-360-3 failing +15.2.3.6-4-360-7 failing +15.2.3.6-4-364 failing +15.2.3.6-4-365 failing +15.2.3.6-4-366 failing +15.2.3.6-4-367 failing +15.2.3.6-4-37 failing +15.2.3.6-4-371 failing +15.2.3.6-4-372 failing +15.2.3.6-4-373 failing +15.2.3.6-4-378 failing +15.2.3.6-4-379 failing +15.2.3.6-4-38 failing +15.2.3.6-4-380 failing +15.2.3.6-4-381 failing +15.2.3.6-4-39 failing +15.2.3.6-4-40 failing +15.2.3.6-4-402 failing +15.2.3.6-4-403 failing +15.2.3.6-4-404 failing +15.2.3.6-4-405 failing +15.2.3.6-4-406 failing +15.2.3.6-4-407 failing +15.2.3.6-4-408 failing +15.2.3.6-4-409 failing +15.2.3.6-4-41 failing +15.2.3.6-4-410 failing +15.2.3.6-4-411 failing +15.2.3.6-4-417 failing +15.2.3.6-4-418 failing +15.2.3.6-4-419 failing +15.2.3.6-4-42 failing +15.2.3.6-4-420 failing +15.2.3.6-4-421 failing +15.2.3.6-4-43 failing +15.2.3.6-4-435 failing +15.2.3.6-4-436 failing +15.2.3.6-4-437 failing +15.2.3.6-4-438 failing +15.2.3.6-4-439 failing +15.2.3.6-4-45 failing +15.2.3.6-4-453 failing +15.2.3.6-4-454 failing +15.2.3.6-4-455 failing +15.2.3.6-4-456 failing +15.2.3.6-4-457 failing +15.2.3.6-4-463 failing +15.2.3.6-4-47 failing +15.2.3.6-4-471 failing +15.2.3.6-4-472 failing +15.2.3.6-4-473 failing +15.2.3.6-4-474 failing +15.2.3.6-4-475 failing +15.2.3.6-4-48 failing +15.2.3.6-4-481 failing +15.2.3.6-4-489 failing +15.2.3.6-4-49 failing +15.2.3.6-4-490 failing +15.2.3.6-4-491 failing +15.2.3.6-4-492 failing +15.2.3.6-4-493 failing +15.2.3.6-4-498 failing +15.2.3.6-4-50 failing +15.2.3.6-4-507 failing +15.2.3.6-4-508 failing +15.2.3.6-4-509 failing +15.2.3.6-4-51 failing +15.2.3.6-4-510 failing +15.2.3.6-4-511 failing +15.2.3.6-4-516 failing +15.2.3.6-4-52 failing +15.2.3.6-4-525 failing +15.2.3.6-4-526 failing +15.2.3.6-4-527 failing +15.2.3.6-4-528 failing +15.2.3.6-4-529 failing +15.2.3.6-4-53 failing +15.2.3.6-4-531-1 failing +15.2.3.6-4-531-10 failing +15.2.3.6-4-531-11 failing +15.2.3.6-4-531-12 failing +15.2.3.6-4-531-13 failing +15.2.3.6-4-531-17 failing +15.2.3.6-4-531-2 failing +15.2.3.6-4-531-3 failing +15.2.3.6-4-531-4 failing +15.2.3.6-4-531-6 failing +15.2.3.6-4-531-8 failing +15.2.3.6-4-534 failing +15.2.3.6-4-535 failing +15.2.3.6-4-538-1 failing +15.2.3.6-4-538-2 failing +15.2.3.6-4-538-3 failing +15.2.3.6-4-538-5 failing +15.2.3.6-4-538-6 failing +15.2.3.6-4-538-7 failing +15.2.3.6-4-538 failing +15.2.3.6-4-540-1 failing +15.2.3.6-4-540-2 failing +15.2.3.6-4-540-3 failing +15.2.3.6-4-540-4 failing +15.2.3.6-4-540-6 failing +15.2.3.6-4-540-7 failing +15.2.3.6-4-540-8 failing +15.2.3.6-4-543 failing +15.2.3.6-4-544 failing +15.2.3.6-4-545 failing +15.2.3.6-4-546 failing +15.2.3.6-4-547-1 failing +15.2.3.6-4-547-2 failing +15.2.3.6-4-547-3 failing +15.2.3.6-4-547-4 failing +15.2.3.6-4-547 failing +15.2.3.6-4-55 failing +15.2.3.6-4-552 failing +15.2.3.6-4-553 failing +15.2.3.6-4-56 failing +15.2.3.6-4-561 failing +15.2.3.6-4-562 failing +15.2.3.6-4-563 failing +15.2.3.6-4-564 failing +15.2.3.6-4-565 failing +15.2.3.6-4-57 failing +15.2.3.6-4-578 failing +15.2.3.6-4-579 failing +15.2.3.6-4-58 failing +15.2.3.6-4-580 failing +15.2.3.6-4-581 failing +15.2.3.6-4-582 failing +15.2.3.6-4-583 failing +15.2.3.6-4-584 failing +15.2.3.6-4-585 failing +15.2.3.6-4-586 failing +15.2.3.6-4-587 failing +15.2.3.6-4-59 failing +15.2.3.6-4-593 failing +15.2.3.6-4-594 failing +15.2.3.6-4-595 failing +15.2.3.6-4-596 failing +15.2.3.6-4-597 failing +15.2.3.6-4-598 failing +15.2.3.6-4-599 failing +15.2.3.6-4-60 failing +15.2.3.6-4-600 failing +15.2.3.6-4-601 failing +15.2.3.6-4-602 failing +15.2.3.6-4-603 failing +15.2.3.6-4-604 failing +15.2.3.6-4-605 failing +15.2.3.6-4-606 failing +15.2.3.6-4-607 failing +15.2.3.6-4-608 failing +15.2.3.6-4-609 failing +15.2.3.6-4-61 failing +15.2.3.6-4-610 failing +15.2.3.6-4-611 failing +15.2.3.6-4-612 failing +15.2.3.6-4-613 failing +15.2.3.6-4-614 failing +15.2.3.6-4-615 failing +15.2.3.6-4-616 failing +15.2.3.6-4-617 failing +15.2.3.6-4-618 failing +15.2.3.6-4-619 failing +15.2.3.6-4-62 failing +15.2.3.6-4-620 failing +15.2.3.6-4-621 failing +15.2.3.6-4-622 failing +15.2.3.6-4-623 failing +15.2.3.6-4-624 failing +15.2.3.6-4-64 failing +15.2.3.6-4-65 failing +15.2.3.6-4-66 failing +15.2.3.6-4-67 failing +15.2.3.6-4-68 failing +15.2.3.6-4-69 failing +15.2.3.6-4-7 failing +15.2.3.6-4-70 failing +15.2.3.6-4-71 failing +15.2.3.6-4-72 failing +15.2.3.6-4-73 failing +15.2.3.6-4-74 failing +15.2.3.6-4-75 failing +15.2.3.6-4-76 failing +15.2.3.6-4-77 failing +15.2.3.6-4-78 failing +15.2.3.6-4-79 failing +15.2.3.6-4-8 failing +15.2.3.6-4-80 failing +15.2.3.6-4-81 failing +15.2.3.6-4-82-1 failing +15.2.3.6-4-82-10 failing +15.2.3.6-4-82-11 failing +15.2.3.6-4-82-12 failing +15.2.3.6-4-82-13 failing +15.2.3.6-4-82-14 failing +15.2.3.6-4-82-15 failing +15.2.3.6-4-82-16 failing +15.2.3.6-4-82-17 failing +15.2.3.6-4-82-18 failing +15.2.3.6-4-82-19 failing +15.2.3.6-4-82-2 failing +15.2.3.6-4-82-20 failing +15.2.3.6-4-82-21 failing +15.2.3.6-4-82-22 failing +15.2.3.6-4-82-23 failing +15.2.3.6-4-82-24 failing +15.2.3.6-4-82-3 failing +15.2.3.6-4-82-4 failing +15.2.3.6-4-82-5 failing +15.2.3.6-4-82-6 failing +15.2.3.6-4-82-7 failing +15.2.3.6-4-82-8 failing +15.2.3.6-4-82-9 failing +15.2.3.6-4-82 failing +15.2.3.6-4-83 failing +15.2.3.6-4-84 failing +15.2.3.6-4-86 failing +15.2.3.6-4-87 failing +15.2.3.6-4-88 failing +15.2.3.6-4-89 failing +15.2.3.6-4-9 failing +15.2.3.6-4-90 failing +15.2.3.6-4-91 failing +15.2.3.6-4-92 failing +15.2.3.6-4-93 failing +15.2.3.6-4-94 failing +15.2.3.6-4-95 failing +15.2.3.6-4-96 failing +15.2.3.6-4-97 failing +15.2.3.6-4-98 failing +15.2.3.6-4-99 failing +15.2.3.7-0-2 failing +15.2.3.7-1-1 failing +15.2.3.7-1-2 failing +15.2.3.7-1-3 failing +15.2.3.7-1-4 failing +15.2.3.7-1 failing +15.2.3.7-2-1 failing +15.2.3.7-2-11 failing +15.2.3.7-2-14 failing +15.2.3.7-2-15 failing +15.2.3.7-2-18 failing +15.2.3.7-2-2 failing +15.2.3.7-5-a-12 failing +15.2.3.7-5-a-15 failing +15.2.3.7-5-a-16 failing +15.2.3.7-5-b-1 failing +15.2.3.7-5-b-105 failing +15.2.3.7-5-b-130 failing +15.2.3.7-5-b-133 failing +15.2.3.7-5-b-137 failing +15.2.3.7-5-b-156 failing +15.2.3.7-5-b-159 failing +15.2.3.7-5-b-163 failing +15.2.3.7-5-b-184 failing +15.2.3.7-5-b-2 failing +15.2.3.7-5-b-209 failing +15.2.3.7-5-b-212 failing +15.2.3.7-5-b-216 failing +15.2.3.7-5-b-218 failing +15.2.3.7-5-b-219 failing +15.2.3.7-5-b-220 failing +15.2.3.7-5-b-221 failing +15.2.3.7-5-b-222 failing +15.2.3.7-5-b-227 failing +15.2.3.7-5-b-24 failing +15.2.3.7-5-b-244 failing +15.2.3.7-5-b-247 failing +15.2.3.7-5-b-248 failing +15.2.3.7-5-b-252 failing +15.2.3.7-5-b-253 failing +15.2.3.7-5-b-254 failing +15.2.3.7-5-b-255 failing +15.2.3.7-5-b-256 failing +15.2.3.7-5-b-257 failing +15.2.3.7-5-b-261 failing +15.2.3.7-5-b-262 failing +15.2.3.7-5-b-263 failing +15.2.3.7-5-b-264 failing +15.2.3.7-5-b-27 failing +15.2.3.7-5-b-3 failing +15.2.3.7-5-b-31 failing +15.2.3.7-5-b-4 failing +15.2.3.7-5-b-5 failing +15.2.3.7-5-b-52 failing +15.2.3.7-5-b-77 failing +15.2.3.7-5-b-80 failing +15.2.3.7-5-b-84 failing +15.2.3.7-6-a-1 failing +15.2.3.7-6-a-10 failing +15.2.3.7-6-a-100 failing +15.2.3.7-6-a-101 failing +15.2.3.7-6-a-102 failing +15.2.3.7-6-a-103 failing +15.2.3.7-6-a-104 failing +15.2.3.7-6-a-105 failing +15.2.3.7-6-a-106 failing +15.2.3.7-6-a-107 failing +15.2.3.7-6-a-108 failing +15.2.3.7-6-a-109 failing +15.2.3.7-6-a-110 failing +15.2.3.7-6-a-112 failing +15.2.3.7-6-a-113 failing +15.2.3.7-6-a-114 failing +15.2.3.7-6-a-115 failing +15.2.3.7-6-a-116 failing +15.2.3.7-6-a-117 failing +15.2.3.7-6-a-118 failing +15.2.3.7-6-a-119 failing +15.2.3.7-6-a-12 failing +15.2.3.7-6-a-120 failing +15.2.3.7-6-a-121 failing +15.2.3.7-6-a-122 failing +15.2.3.7-6-a-123 failing +15.2.3.7-6-a-124 failing +15.2.3.7-6-a-125 failing +15.2.3.7-6-a-126 failing +15.2.3.7-6-a-127 failing +15.2.3.7-6-a-128 failing +15.2.3.7-6-a-129 failing +15.2.3.7-6-a-13 failing +15.2.3.7-6-a-130 failing +15.2.3.7-6-a-131 failing +15.2.3.7-6-a-132 failing +15.2.3.7-6-a-133 failing +15.2.3.7-6-a-134 failing +15.2.3.7-6-a-135 failing +15.2.3.7-6-a-136 failing +15.2.3.7-6-a-137 failing +15.2.3.7-6-a-138 failing +15.2.3.7-6-a-139 failing +15.2.3.7-6-a-14 failing +15.2.3.7-6-a-140 failing +15.2.3.7-6-a-141 failing +15.2.3.7-6-a-142 failing +15.2.3.7-6-a-143 failing +15.2.3.7-6-a-144 failing +15.2.3.7-6-a-145 failing +15.2.3.7-6-a-146 failing +15.2.3.7-6-a-147 failing +15.2.3.7-6-a-148 failing +15.2.3.7-6-a-149 failing +15.2.3.7-6-a-15 failing +15.2.3.7-6-a-150 failing +15.2.3.7-6-a-151 failing +15.2.3.7-6-a-152 failing +15.2.3.7-6-a-153 failing +15.2.3.7-6-a-155 failing +15.2.3.7-6-a-156 failing +15.2.3.7-6-a-157 failing +15.2.3.7-6-a-158 failing +15.2.3.7-6-a-159 failing +15.2.3.7-6-a-16 failing +15.2.3.7-6-a-160 failing +15.2.3.7-6-a-161 failing +15.2.3.7-6-a-162 failing +15.2.3.7-6-a-163 failing +15.2.3.7-6-a-164 failing +15.2.3.7-6-a-165 failing +15.2.3.7-6-a-166 failing +15.2.3.7-6-a-167 failing +15.2.3.7-6-a-168 failing +15.2.3.7-6-a-169 failing +15.2.3.7-6-a-17 failing +15.2.3.7-6-a-170 failing +15.2.3.7-6-a-171 failing +15.2.3.7-6-a-172 failing +15.2.3.7-6-a-173 failing +15.2.3.7-6-a-175 failing +15.2.3.7-6-a-176 failing +15.2.3.7-6-a-177 failing +15.2.3.7-6-a-178 failing +15.2.3.7-6-a-179 failing +15.2.3.7-6-a-18 failing +15.2.3.7-6-a-180 failing +15.2.3.7-6-a-181 failing +15.2.3.7-6-a-182 failing +15.2.3.7-6-a-183 failing +15.2.3.7-6-a-184 failing +15.2.3.7-6-a-185 failing +15.2.3.7-6-a-186 failing +15.2.3.7-6-a-187 failing +15.2.3.7-6-a-188 failing +15.2.3.7-6-a-189 failing +15.2.3.7-6-a-19 failing +15.2.3.7-6-a-190 failing +15.2.3.7-6-a-191 failing +15.2.3.7-6-a-192 failing +15.2.3.7-6-a-193 failing +15.2.3.7-6-a-194 failing +15.2.3.7-6-a-195 failing +15.2.3.7-6-a-197 failing +15.2.3.7-6-a-198 failing +15.2.3.7-6-a-199 failing +15.2.3.7-6-a-2 failing +15.2.3.7-6-a-20 failing +15.2.3.7-6-a-200 failing +15.2.3.7-6-a-204 failing +15.2.3.7-6-a-205 failing +15.2.3.7-6-a-206 failing +15.2.3.7-6-a-207 failing +15.2.3.7-6-a-208 failing +15.2.3.7-6-a-209 failing +15.2.3.7-6-a-21 failing +15.2.3.7-6-a-210 failing +15.2.3.7-6-a-211 failing +15.2.3.7-6-a-212 failing +15.2.3.7-6-a-213 failing +15.2.3.7-6-a-214 failing +15.2.3.7-6-a-215 failing +15.2.3.7-6-a-216 failing +15.2.3.7-6-a-217 failing +15.2.3.7-6-a-218 failing +15.2.3.7-6-a-219 failing +15.2.3.7-6-a-22 failing +15.2.3.7-6-a-220 failing +15.2.3.7-6-a-221 failing +15.2.3.7-6-a-222 failing +15.2.3.7-6-a-223 failing +15.2.3.7-6-a-224 failing +15.2.3.7-6-a-225 failing +15.2.3.7-6-a-226 failing +15.2.3.7-6-a-227 failing +15.2.3.7-6-a-228 failing +15.2.3.7-6-a-229 failing +15.2.3.7-6-a-230 failing +15.2.3.7-6-a-231 failing +15.2.3.7-6-a-232 failing +15.2.3.7-6-a-233 failing +15.2.3.7-6-a-234 failing +15.2.3.7-6-a-235 failing +15.2.3.7-6-a-236 failing +15.2.3.7-6-a-237 failing +15.2.3.7-6-a-238 failing +15.2.3.7-6-a-239 failing +15.2.3.7-6-a-24 failing +15.2.3.7-6-a-240 failing +15.2.3.7-6-a-241 failing +15.2.3.7-6-a-242 failing +15.2.3.7-6-a-243 failing +15.2.3.7-6-a-244 failing +15.2.3.7-6-a-245 failing +15.2.3.7-6-a-246 failing +15.2.3.7-6-a-247 failing +15.2.3.7-6-a-248 failing +15.2.3.7-6-a-249 failing +15.2.3.7-6-a-25 failing +15.2.3.7-6-a-250 failing +15.2.3.7-6-a-251 failing +15.2.3.7-6-a-252 failing +15.2.3.7-6-a-253 failing +15.2.3.7-6-a-254 failing +15.2.3.7-6-a-255 failing +15.2.3.7-6-a-256 failing +15.2.3.7-6-a-257 failing +15.2.3.7-6-a-258 failing +15.2.3.7-6-a-259 failing +15.2.3.7-6-a-260 failing +15.2.3.7-6-a-261 failing +15.2.3.7-6-a-262 failing +15.2.3.7-6-a-263 failing +15.2.3.7-6-a-264 failing +15.2.3.7-6-a-265 failing +15.2.3.7-6-a-266 failing +15.2.3.7-6-a-267 failing +15.2.3.7-6-a-268 failing +15.2.3.7-6-a-269 failing +15.2.3.7-6-a-270 failing +15.2.3.7-6-a-271 failing +15.2.3.7-6-a-272 failing +15.2.3.7-6-a-273 failing +15.2.3.7-6-a-274 failing +15.2.3.7-6-a-275 failing +15.2.3.7-6-a-276 failing +15.2.3.7-6-a-277 failing +15.2.3.7-6-a-278 failing +15.2.3.7-6-a-279 failing +15.2.3.7-6-a-280 failing +15.2.3.7-6-a-281 failing +15.2.3.7-6-a-282 failing +15.2.3.7-6-a-283 failing +15.2.3.7-6-a-284 failing +15.2.3.7-6-a-285 failing +15.2.3.7-6-a-286 failing +15.2.3.7-6-a-287 failing +15.2.3.7-6-a-288 failing +15.2.3.7-6-a-289 failing +15.2.3.7-6-a-290 failing +15.2.3.7-6-a-291 failing +15.2.3.7-6-a-292 failing +15.2.3.7-6-a-293 failing +15.2.3.7-6-a-294 failing +15.2.3.7-6-a-295 failing +15.2.3.7-6-a-296 failing +15.2.3.7-6-a-297 failing +15.2.3.7-6-a-298 failing +15.2.3.7-6-a-299 failing +15.2.3.7-6-a-3 failing +15.2.3.7-6-a-300 failing +15.2.3.7-6-a-301 failing +15.2.3.7-6-a-302 failing +15.2.3.7-6-a-303 failing +15.2.3.7-6-a-304 failing +15.2.3.7-6-a-305 failing +15.2.3.7-6-a-306 failing +15.2.3.7-6-a-307 failing +15.2.3.7-6-a-308 failing +15.2.3.7-6-a-309 failing +15.2.3.7-6-a-31 failing +15.2.3.7-6-a-310 failing +15.2.3.7-6-a-311 failing +15.2.3.7-6-a-312 failing +15.2.3.7-6-a-313 failing +15.2.3.7-6-a-33 failing +15.2.3.7-6-a-35 failing +15.2.3.7-6-a-36 failing +15.2.3.7-6-a-37 failing +15.2.3.7-6-a-38-1 failing +15.2.3.7-6-a-38 failing +15.2.3.7-6-a-39 failing +15.2.3.7-6-a-4 failing +15.2.3.7-6-a-40 failing +15.2.3.7-6-a-41 failing +15.2.3.7-6-a-42 failing +15.2.3.7-6-a-43 failing +15.2.3.7-6-a-45 failing +15.2.3.7-6-a-46 failing +15.2.3.7-6-a-47 failing +15.2.3.7-6-a-48 failing +15.2.3.7-6-a-49 failing +15.2.3.7-6-a-5 failing +15.2.3.7-6-a-50 failing +15.2.3.7-6-a-51 failing +15.2.3.7-6-a-52 failing +15.2.3.7-6-a-53 failing +15.2.3.7-6-a-54 failing +15.2.3.7-6-a-55 failing +15.2.3.7-6-a-56 failing +15.2.3.7-6-a-59 failing +15.2.3.7-6-a-60 failing +15.2.3.7-6-a-61 failing +15.2.3.7-6-a-62 failing +15.2.3.7-6-a-63 failing +15.2.3.7-6-a-64 failing +15.2.3.7-6-a-65 failing +15.2.3.7-6-a-66-1 failing +15.2.3.7-6-a-66 failing +15.2.3.7-6-a-67 failing +15.2.3.7-6-a-68 failing +15.2.3.7-6-a-7 failing +15.2.3.7-6-a-70 failing +15.2.3.7-6-a-71 failing +15.2.3.7-6-a-72 failing +15.2.3.7-6-a-73 failing +15.2.3.7-6-a-74 failing +15.2.3.7-6-a-76 failing +15.2.3.7-6-a-77 failing +15.2.3.7-6-a-78 failing +15.2.3.7-6-a-79 failing +15.2.3.7-6-a-8 failing +15.2.3.7-6-a-80 failing +15.2.3.7-6-a-81 failing +15.2.3.7-6-a-82 failing +15.2.3.7-6-a-83 failing +15.2.3.7-6-a-84-1 failing +15.2.3.7-6-a-84 failing +15.2.3.7-6-a-85 failing +15.2.3.7-6-a-86-1 failing +15.2.3.7-6-a-86 failing +15.2.3.7-6-a-87 failing +15.2.3.7-6-a-88 failing +15.2.3.7-6-a-9 failing +15.2.3.7-6-a-90 failing +15.2.3.7-6-a-91 failing +15.2.3.7-6-a-92 failing +15.2.3.7-6-a-93-1 failing +15.2.3.7-6-a-93-2 failing +15.2.3.7-6-a-93-3 failing +15.2.3.7-6-a-93-4 failing +15.2.3.7-6-a-93 failing +15.2.3.7-6-a-94 failing +15.2.3.7-6-a-95 failing +15.2.3.7-6-a-96 failing +15.2.3.7-6-a-97 failing +15.2.3.7-6-a-98 failing +15.2.3.7-6-a-99 failing +15.2.3.8-0-2 failing +15.2.3.8-1-1 failing +15.2.3.8-1-2 failing +15.2.3.8-1-3 failing +15.2.3.8-1-4 failing +15.2.3.8-1 failing +15.2.3.8-2-b-1 failing +15.2.3.8-2-b-2 failing +15.2.3.8-2-b-3 failing +15.2.3.8-2-b-4 failing +15.2.3.9-0-2 failing +15.2.3.9-1-1 failing +15.2.3.9-1-2 failing +15.2.3.9-1-3 failing +15.2.3.9-1-4 failing +15.2.3.9-1 failing +15.2.3.9-2-a-11 failing +15.2.3.9-2-a-12 failing +15.2.3.9-2-a-14 failing +15.2.3.9-2-b-i-1 failing +15.2.3.9-2-b-i-2 failing +15.2.3.9-2-c-1 failing +15.2.3.9-2-c-3 failing +15.2.3.9-2-c-4 failing +S15.2.4_A1_T2 failing +S15.2.4_A3 failing +S15.2.4_A4 failing +15.2.4.2-1-1 failing +15.2.4.2-1-2 failing +15.2.4.2-2-1 failing +15.2.4.2-2-2 failing +S15.2.4.2_A10 failing +S15.2.4.2_A11 failing +S15.2.4.2_A12 failing +S15.2.4.2_A13 failing +S15.2.4.2_A7 failing +S15.2.4.2_A8 failing +S15.2.4.2_A9 failing +S15.2.4.3_A10 failing +S15.2.4.3_A11 failing +S15.2.4.3_A7 failing +S15.2.4.3_A8 failing +S15.2.4.3_A9 failing +S15.2.4.4_A10 failing +S15.2.4.4_A11 failing +S15.2.4.4_A7 failing +S15.2.4.4_A8 failing +S15.2.4.4_A9 failing +S15.2.4.5_A10 failing +S15.2.4.5_A11 failing +S15.2.4.5_A7 failing +S15.2.4.5_A8 failing +S15.2.4.5_A9 failing +S15.2.4.6_A10 failing +S15.2.4.6_A11 failing +S15.2.4.6_A7 failing +S15.2.4.6_A8 failing +S15.2.4.6_A9 failing +S15.2.4.7_A10 failing +S15.2.4.7_A11 failing +S15.2.4.7_A2_T1 failing +S15.2.4.7_A7 failing +S15.2.4.7_A8 failing +S15.2.4.7_A9 failing +S15.3.1_A1_T1 failing +S15.3_A2_T1 failing +S15.3_A2_T2 failing +S15.3_A3_T3 failing +S15.3.2.1_A1_T1 failing +S15.3.2.1_A1_T10 failing +S15.3.2.1_A1_T11 failing +S15.3.2.1_A1_T12 failing +S15.3.2.1_A1_T13 failing +S15.3.2.1_A1_T2 failing +S15.3.2.1_A1_T3 failing +S15.3.2.1_A1_T4 failing +S15.3.2.1_A1_T5 failing +S15.3.2.1_A1_T6 failing +S15.3.2.1_A1_T7 failing +S15.3.2.1_A1_T8 failing +S15.3.2.1_A1_T9 failing +S15.3.2.1_A2_T1 failing +S15.3.2.1_A2_T2 failing +S15.3.2.1_A2_T3 failing +S15.3.2.1_A2_T4 failing +S15.3.2.1_A2_T5 failing +S15.3.2.1_A2_T6 failing +S15.3.2.1_A3_T1 failing +S15.3.2.1_A3_T10 failing +S15.3.2.1_A3_T11 failing +S15.3.2.1_A3_T12 failing +S15.3.2.1_A3_T13 failing +S15.3.2.1_A3_T14 failing +S15.3.2.1_A3_T15 failing +S15.3.2.1_A3_T2 failing +S15.3.2.1_A3_T3 failing +S15.3.2.1_A3_T4 failing +S15.3.2.1_A3_T5 failing +S15.3.2.1_A3_T6 failing +S15.3.2.1_A3_T7 failing +S15.3.2.1_A3_T8 failing +S15.3.2.1_A3_T9 failing +15.3.2.1-11-1-s failing +15.3.2.1-11-2-s failing +15.3.2.1-11-3-s failing +15.3.2.1-11-4-s failing +15.3.2.1-11-5-s failing +15.3.2.1-11-6-s failing +15.3.2.1-11-7-s failing +S15.3.3_A3 failing +S15.3.3.1_A1 failing +S15.3.3.1_A2 failing +S15.3.3.1_A3 failing +15.3.3.2-1 failing +S15.3.4_A2_T1 failing +S15.3.4_A2_T2 failing +S15.3.4_A2_T3 failing +S15.3.4_A5 failing +S15.3.4.2_A10 failing +S15.3.4.2_A11 failing +S15.3.4.2_A7 failing +S15.3.4.2_A8 failing +S15.3.4.2_A9 failing +15.3.4.3-1-s failing +15.3.4.3-2-s failing +15.3.4.3-3-s failing +S15.3.4.3_A10 failing +S15.3.4.3_A11 failing +S15.3.4.3_A16 failing +S15.3.4.3_A1_T1 failing +S15.3.4.3_A1_T2 failing +S15.3.4.3_A2_T1 failing +S15.3.4.3_A2_T2 failing +S15.3.4.3_A3_T1 failing +S15.3.4.3_A3_T10 failing +S15.3.4.3_A3_T2 failing +S15.3.4.3_A3_T3 failing +S15.3.4.3_A3_T4 failing +S15.3.4.3_A3_T5 failing +S15.3.4.3_A3_T6 failing +S15.3.4.3_A3_T7 failing +S15.3.4.3_A3_T8 failing +S15.3.4.3_A3_T9 failing +S15.3.4.3_A5_T4 failing +S15.3.4.3_A6_T2 failing +S15.3.4.3_A6_T3 failing +S15.3.4.3_A7_T1 failing +S15.3.4.3_A7_T10 failing +S15.3.4.3_A7_T2 failing +S15.3.4.3_A7_T3 failing +S15.3.4.3_A7_T4 failing +S15.3.4.3_A7_T5 failing +S15.3.4.3_A7_T6 failing +S15.3.4.3_A7_T7 failing +S15.3.4.3_A7_T8 failing +S15.3.4.3_A7_T9 failing +S15.3.4.3_A8_T1 failing +S15.3.4.3_A8_T2 failing +S15.3.4.3_A8_T3 failing +S15.3.4.3_A8_T4 failing +S15.3.4.3_A8_T5 failing +S15.3.4.3_A8_T6 failing +S15.3.4.3_A9 failing +15.3.4.4-1-s failing +15.3.4.4-2-s failing +15.3.4.4-3-s failing +S15.3.4.4_A10 failing +S15.3.4.4_A11 failing +S15.3.4.4_A16 failing +S15.3.4.4_A1_T1 failing +S15.3.4.4_A1_T2 failing +S15.3.4.4_A2_T1 failing +S15.3.4.4_A2_T2 failing +S15.3.4.4_A6_T1 failing +S15.3.4.4_A6_T10 failing +S15.3.4.4_A6_T2 failing +S15.3.4.4_A6_T3 failing +S15.3.4.4_A6_T4 failing +S15.3.4.4_A6_T5 failing +S15.3.4.4_A6_T6 failing +S15.3.4.4_A6_T7 failing +S15.3.4.4_A6_T8 failing +S15.3.4.4_A6_T9 failing +S15.3.4.4_A7_T1 failing +S15.3.4.4_A7_T2 failing +S15.3.4.4_A7_T3 failing +S15.3.4.4_A7_T4 failing +S15.3.4.4_A7_T5 failing +S15.3.4.4_A7_T6 failing +S15.3.4.4_A9 failing +15.3.4.5-0-2 failing +15.3.4.5-10-1 failing +15.3.4.5-11-1 failing +15.3.4.5-13.b-1 failing +15.3.4.5-13.b-2 failing +15.3.4.5-13.b-3 failing +15.3.4.5-13.b-4 failing +15.3.4.5-13.b-5 failing +15.3.4.5-13.b-6 failing +15.3.4.5-15-1 failing +15.3.4.5-15-2 failing +15.3.4.5-15-3 failing +15.3.4.5-15-4 failing +15.3.4.5-15-5 failing +15.3.4.5-16-1 failing +15.3.4.5-16-2 failing +15.3.4.5-2-1 failing +15.3.4.5-2-10 failing +15.3.4.5-2-11 failing +15.3.4.5-2-12 failing +15.3.4.5-2-13 failing +15.3.4.5-2-14 failing +15.3.4.5-2-15 failing +15.3.4.5-2-16 failing +15.3.4.5-2-2 failing +15.3.4.5-2-3 failing +15.3.4.5-2-4 failing +15.3.4.5-2-5 failing +15.3.4.5-2-6 failing +15.3.4.5-2-7 failing +15.3.4.5-2-8 failing +15.3.4.5-2-9 failing +15.3.4.5-20-1 failing +15.3.4.5-20-2 failing +15.3.4.5-20-3 failing +15.3.4.5-20-4 failing +15.3.4.5-20-5 failing +15.3.4.5-21-1 failing +15.3.4.5-21-2 failing +15.3.4.5-21-3 failing +15.3.4.5-21-4 failing +15.3.4.5-21-5 failing +15.3.4.5-3-1 failing +15.3.4.5-6-1 failing +15.3.4.5-6-10 failing +15.3.4.5-6-11 failing +15.3.4.5-6-12 failing +15.3.4.5-6-2 failing +15.3.4.5-6-3 failing +15.3.4.5-6-4 failing +15.3.4.5-6-5 failing +15.3.4.5-6-6 failing +15.3.4.5-6-7 failing +15.3.4.5-6-8 failing +15.3.4.5-6-9 failing +15.3.4.5-8-1 failing +15.3.4.5-8-2 failing +15.3.4.5-9-1 failing +15.3.4.5-9-2 failing +S15.3.4.5_A16 failing +S15.3.4.5_A4 failing +S15.3.4.5_A5 failing +15.3.4.5.1-4-1 failing +15.3.4.5.1-4-10 failing +15.3.4.5.1-4-11 failing +15.3.4.5.1-4-12 failing +15.3.4.5.1-4-13 failing +15.3.4.5.1-4-14 failing +15.3.4.5.1-4-15 failing +15.3.4.5.1-4-2 failing +15.3.4.5.1-4-3 failing +15.3.4.5.1-4-4 failing +15.3.4.5.1-4-5 failing +15.3.4.5.1-4-6 failing +15.3.4.5.1-4-7 failing +15.3.4.5.1-4-8 failing +15.3.4.5.1-4-9 failing +15.3.4.5.2-4-1 failing +15.3.4.5.2-4-10 failing +15.3.4.5.2-4-11 failing +15.3.4.5.2-4-12 failing +15.3.4.5.2-4-13 failing +15.3.4.5.2-4-14 failing +15.3.4.5.2-4-2 failing +15.3.4.5.2-4-3 failing +15.3.4.5.2-4-4 failing +15.3.4.5.2-4-5 failing +15.3.4.5.2-4-6 failing +15.3.4.5.2-4-7 failing +15.3.4.5.2-4-8 failing +15.3.4.5.2-4-9 failing +S15.3.5.1_A1_T1 failing +S15.3.5.1_A1_T2 failing +S15.3.5.1_A1_T3 failing +S15.3.5.1_A2_T1 failing +S15.3.5.1_A2_T2 failing +S15.3.5.1_A2_T3 failing +S15.3.5.1_A3_T1 failing +S15.3.5.1_A3_T2 failing +S15.3.5.1_A3_T3 failing +S15.3.5.1_A4_T1 failing +S15.3.5.1_A4_T2 failing +S15.3.5.1_A4_T3 failing +S15.3.5.2_A1_T1 failing +S15.3.5.2_A1_T2 failing +S15.3.5.3_A1_T1 failing +S15.3.5.3_A1_T2 failing +S15.3.5.3_A1_T3 failing +S15.3.5.3_A1_T4 failing +S15.3.5.3_A1_T5 failing +S15.3.5.3_A1_T6 failing +S15.3.5.3_A1_T7 failing +S15.3.5.3_A1_T8 failing +S15.3.5.3_A2_T2 failing +S15.3.5.3_A2_T5 failing +S15.3.5.3_A2_T6 failing +S15.3.5_A2_T2 failing +S15.3.5_A3_T2 failing +15.3.5.4_2-79gs failing +15.3.5.4_2-80gs failing +15.3.5.4_2-81gs failing +15.3.5.4_2-89gs failing +15.3.5.4_2-90gs failing +15.3.5.4_2-91gs failing +15.3.5.4_2-92gs failing +15.3.5.4_2-93gs failing +S15.4_A1.1_T2 failing +S15.4_A1.1_T3 failing +S15.4_A1.1_T4 failing +S15.4_A1.1_T7 failing +S15.4_A1.1_T8 failing +S15.4_A1.1_T9 failing +S15.4.2.2_A2.2_T1 failing +S15.4.2.2_A2.2_T2 failing +S15.4.2.2_A2.2_T3 failing +S15.4.3_A2.2 failing +S15.4.3_A2.3 failing +S15.4.3_A2.4 failing +S15.4.3.1_A2 failing +S15.4.3.1_A3 failing +S15.4.3.1_A4 failing +15.4.3.2-0-1 failing +15.4.3.2-0-2 failing +15.4.3.2-0-3 failing +15.4.3.2-0-4 failing +15.4.3.2-0-5 failing +15.4.3.2-0-6 failing +15.4.3.2-0-7 failing +15.4.3.2-1-1 failing +15.4.3.2-1-10 failing +15.4.3.2-1-11 failing +15.4.3.2-1-12 failing +15.4.3.2-1-13 failing +15.4.3.2-1-15 failing +15.4.3.2-1-2 failing +15.4.3.2-1-3 failing +15.4.3.2-1-4 failing +15.4.3.2-1-5 failing +15.4.3.2-1-6 failing +15.4.3.2-1-7 failing +15.4.3.2-1-8 failing +15.4.3.2-1-9 failing +15.4.3.2-2-1 failing +15.4.3.2-2-2 failing +15.4.3.2-2-3 failing +15.4.4.10-10-c-ii-1 failing +S15.4.4.10_A1.1_T1 failing +S15.4.4.10_A1.1_T5 failing +S15.4.4.10_A1.1_T6 failing +S15.4.4.10_A1.1_T7 failing +S15.4.4.10_A1.2_T1 failing +S15.4.4.10_A1.2_T2 failing +S15.4.4.10_A1.2_T3 failing +S15.4.4.10_A1.2_T4 failing +S15.4.4.10_A1.3_T1 failing +S15.4.4.10_A1.4_T1 failing +S15.4.4.10_A1.4_T2 failing +S15.4.4.10_A1.4_T3 failing +S15.4.4.10_A1.5_T1 failing +S15.4.4.10_A1.5_T2 failing +S15.4.4.10_A2.1_T1 failing +S15.4.4.10_A2.1_T2 failing +S15.4.4.10_A2.1_T4 failing +S15.4.4.10_A2.1_T5 failing +S15.4.4.10_A2.2_T1 failing +S15.4.4.10_A2.2_T5 failing +S15.4.4.10_A3_T3 failing +S15.4.4.10_A4_T1 failing +S15.4.4.10_A5.1 failing +S15.4.4.10_A5.2 failing +S15.4.4.10_A5.3 failing +S15.4.4.10_A5.4 failing +S15.4.4.10_A5.7 failing +S15.4.4.11_A3_T1 failing +S15.4.4.11_A3_T2 failing +S15.4.4.11_A4_T1 failing +S15.4.4.11_A4_T2 failing +S15.4.4.11_A4_T3 failing +S15.4.4.11_A5_T1 failing +S15.4.4.11_A6_T2 failing +S15.4.4.11_A7.1 failing +S15.4.4.11_A7.2 failing +S15.4.4.11_A7.3 failing +S15.4.4.11_A7.4 failing +S15.4.4.11_A7.7 failing +15.4.4.12-9-c-ii-1 failing +S15.4.4.12_A1.1_T4 failing +S15.4.4.12_A1.1_T6 failing +S15.4.4.12_A1.4_T4 failing +S15.4.4.12_A1.4_T6 failing +S15.4.4.12_A2.1_T1 failing +S15.4.4.12_A2.2_T1 failing +S15.4.4.12_A2_T1 failing +S15.4.4.12_A2_T2 failing +S15.4.4.12_A2_T3 failing +S15.4.4.12_A2_T4 failing +S15.4.4.12_A3_T1 failing +S15.4.4.12_A3_T2 failing +S15.4.4.12_A3_T3 failing +S15.4.4.12_A4_T1 failing +S15.4.4.12_A4_T2 failing +S15.4.4.12_A4_T3 failing +S15.4.4.12_A5.1 failing +S15.4.4.12_A5.2 failing +S15.4.4.12_A5.3 failing +S15.4.4.12_A5.4 failing +S15.4.4.12_A5.7 failing +S15.4.4.13_A1_T1 failing +S15.4.4.13_A1_T2 failing +S15.4.4.13_A2_T1 failing +S15.4.4.13_A2_T2 failing +S15.4.4.13_A2_T3 failing +S15.4.4.13_A3_T1 failing +S15.4.4.13_A3_T2 failing +S15.4.4.13_A3_T3 failing +S15.4.4.13_A4_T1 failing +S15.4.4.13_A4_T2 failing +S15.4.4.13_A5.1 failing +S15.4.4.13_A5.2 failing +S15.4.4.13_A5.3 failing +S15.4.4.13_A5.4 failing +S15.4.4.13_A5.7 failing +15.4.4.14-0-2 failing +15.4.4.14-1-1 failing +15.4.4.14-1-10 failing +15.4.4.14-1-11 failing +15.4.4.14-1-12 failing +15.4.4.14-1-13 failing +15.4.4.14-1-14 failing +15.4.4.14-1-15 failing +15.4.4.14-1-17 failing +15.4.4.14-1-2 failing +15.4.4.14-1-3 failing +15.4.4.14-1-4 failing +15.4.4.14-1-5 failing +15.4.4.14-1-6 failing +15.4.4.14-1-7 failing +15.4.4.14-1-8 failing +15.4.4.14-1-9 failing +15.4.4.14-10-1 failing +15.4.4.14-10-2 failing +15.4.4.14-2-1 failing +15.4.4.14-2-10 failing +15.4.4.14-2-11 failing +15.4.4.14-2-12 failing +15.4.4.14-2-13 failing +15.4.4.14-2-14 failing +15.4.4.14-2-15 failing +15.4.4.14-2-17 failing +15.4.4.14-2-18 failing +15.4.4.14-2-19 failing +15.4.4.14-2-2 failing +15.4.4.14-2-3 failing +15.4.4.14-2-4 failing +15.4.4.14-2-5 failing +15.4.4.14-2-6 failing +15.4.4.14-2-7 failing +15.4.4.14-2-8 failing +15.4.4.14-2-9 failing +15.4.4.14-3-1 failing +15.4.4.14-3-10 failing +15.4.4.14-3-11 failing +15.4.4.14-3-12 failing +15.4.4.14-3-13 failing +15.4.4.14-3-14 failing +15.4.4.14-3-15 failing +15.4.4.14-3-16 failing +15.4.4.14-3-17 failing +15.4.4.14-3-18 failing +15.4.4.14-3-19 failing +15.4.4.14-3-2 failing +15.4.4.14-3-20 failing +15.4.4.14-3-21 failing +15.4.4.14-3-22 failing +15.4.4.14-3-23 failing +15.4.4.14-3-24 failing +15.4.4.14-3-25 failing +15.4.4.14-3-28 failing +15.4.4.14-3-29 failing +15.4.4.14-3-3 failing +15.4.4.14-3-4 failing +15.4.4.14-3-5 failing +15.4.4.14-3-6 failing +15.4.4.14-3-7 failing +15.4.4.14-3-8 failing +15.4.4.14-3-9 failing +15.4.4.14-4-1 failing +15.4.4.14-4-10 failing +15.4.4.14-4-11 failing +15.4.4.14-4-2 failing +15.4.4.14-4-3 failing +15.4.4.14-4-4 failing +15.4.4.14-4-5 failing +15.4.4.14-4-6 failing +15.4.4.14-4-7 failing +15.4.4.14-4-8 failing +15.4.4.14-4-9 failing +15.4.4.14-5-1 failing +15.4.4.14-5-10 failing +15.4.4.14-5-11 failing +15.4.4.14-5-13 failing +15.4.4.14-5-14 failing +15.4.4.14-5-15 failing +15.4.4.14-5-17 failing +15.4.4.14-5-18 failing +15.4.4.14-5-19 failing +15.4.4.14-5-2 failing +15.4.4.14-5-20 failing +15.4.4.14-5-21 failing +15.4.4.14-5-22 failing +15.4.4.14-5-23 failing +15.4.4.14-5-24 failing +15.4.4.14-5-25 failing +15.4.4.14-5-26 failing +15.4.4.14-5-27 failing +15.4.4.14-5-28 failing +15.4.4.14-5-29 failing +15.4.4.14-5-3 failing +15.4.4.14-5-30 failing +15.4.4.14-5-31 failing +15.4.4.14-5-32 failing +15.4.4.14-5-33 failing +15.4.4.14-5-4 failing +15.4.4.14-5-5 failing +15.4.4.14-5-6 failing +15.4.4.14-5-7 failing +15.4.4.14-5-8 failing +15.4.4.14-5-9 failing +15.4.4.14-6-1 failing +15.4.4.14-7-1 failing +15.4.4.14-7-2 failing +15.4.4.14-7-3 failing +15.4.4.14-7-4 failing +15.4.4.14-7-5 failing +15.4.4.14-8-1 failing +15.4.4.14-8-2 failing +15.4.4.14-8-3 failing +15.4.4.14-8-4 failing +15.4.4.14-9-1 failing +15.4.4.14-9-10 failing +15.4.4.14-9-11 failing +15.4.4.14-9-2 failing +15.4.4.14-9-3 failing +15.4.4.14-9-4 failing +15.4.4.14-9-5 failing +15.4.4.14-9-6 failing +15.4.4.14-9-7 failing +15.4.4.14-9-8 failing +15.4.4.14-9-a-1 failing +15.4.4.14-9-a-10 failing +15.4.4.14-9-a-11 failing +15.4.4.14-9-a-12 failing +15.4.4.14-9-a-13 failing +15.4.4.14-9-a-14 failing +15.4.4.14-9-a-15 failing +15.4.4.14-9-a-16 failing +15.4.4.14-9-a-17 failing +15.4.4.14-9-a-18 failing +15.4.4.14-9-a-19 failing +15.4.4.14-9-a-2 failing +15.4.4.14-9-a-3 failing +15.4.4.14-9-a-4 failing +15.4.4.14-9-a-5 failing +15.4.4.14-9-a-6 failing +15.4.4.14-9-a-7 failing +15.4.4.14-9-a-8 failing +15.4.4.14-9-a-9 failing +15.4.4.14-9-b-1 failing +15.4.4.14-9-b-i-1 failing +15.4.4.14-9-b-i-10 failing +15.4.4.14-9-b-i-11 failing +15.4.4.14-9-b-i-12 failing +15.4.4.14-9-b-i-13 failing +15.4.4.14-9-b-i-14 failing +15.4.4.14-9-b-i-15 failing +15.4.4.14-9-b-i-16 failing +15.4.4.14-9-b-i-17 failing +15.4.4.14-9-b-i-18 failing +15.4.4.14-9-b-i-19 failing +15.4.4.14-9-b-i-2 failing +15.4.4.14-9-b-i-20 failing +15.4.4.14-9-b-i-21 failing +15.4.4.14-9-b-i-22 failing +15.4.4.14-9-b-i-23 failing +15.4.4.14-9-b-i-25 failing +15.4.4.14-9-b-i-26 failing +15.4.4.14-9-b-i-27 failing +15.4.4.14-9-b-i-28 failing +15.4.4.14-9-b-i-29 failing +15.4.4.14-9-b-i-3 failing +15.4.4.14-9-b-i-30 failing +15.4.4.14-9-b-i-31 failing +15.4.4.14-9-b-i-4 failing +15.4.4.14-9-b-i-5 failing +15.4.4.14-9-b-i-6 failing +15.4.4.14-9-b-i-7 failing +15.4.4.14-9-b-i-8 failing +15.4.4.14-9-b-i-9 failing +15.4.4.14-9-b-ii-1 failing +15.4.4.14-9-b-ii-10 failing +15.4.4.14-9-b-ii-11 failing +15.4.4.14-9-b-ii-2 failing +15.4.4.14-9-b-ii-3 failing +15.4.4.14-9-b-ii-4 failing +15.4.4.14-9-b-ii-5 failing +15.4.4.14-9-b-ii-6 failing +15.4.4.14-9-b-ii-7 failing +15.4.4.14-9-b-ii-8 failing +15.4.4.14-9-b-ii-9 failing +15.4.4.14-9-b-iii-1 failing +15.4.4.14-9-b-iii-2 failing +15.4.4.15-0-2 failing +15.4.4.15-1-1 failing +15.4.4.15-1-10 failing +15.4.4.15-1-11 failing +15.4.4.15-1-12 failing +15.4.4.15-1-13 failing +15.4.4.15-1-14 failing +15.4.4.15-1-15 failing +15.4.4.15-1-17 failing +15.4.4.15-1-2 failing +15.4.4.15-1-3 failing +15.4.4.15-1-4 failing +15.4.4.15-1-5 failing +15.4.4.15-1-6 failing +15.4.4.15-1-7 failing +15.4.4.15-1-8 failing +15.4.4.15-1-9 failing +15.4.4.15-2-1 failing +15.4.4.15-2-10 failing +15.4.4.15-2-11 failing +15.4.4.15-2-12 failing +15.4.4.15-2-13 failing +15.4.4.15-2-14 failing +15.4.4.15-2-15 failing +15.4.4.15-2-17 failing +15.4.4.15-2-18 failing +15.4.4.15-2-19 failing +15.4.4.15-2-2 failing +15.4.4.15-2-3 failing +15.4.4.15-2-4 failing +15.4.4.15-2-5 failing +15.4.4.15-2-6 failing +15.4.4.15-2-7 failing +15.4.4.15-2-8 failing +15.4.4.15-2-9 failing +15.4.4.15-3-1 failing +15.4.4.15-3-10 failing +15.4.4.15-3-11 failing +15.4.4.15-3-12 failing +15.4.4.15-3-13 failing +15.4.4.15-3-14 failing +15.4.4.15-3-15 failing +15.4.4.15-3-16 failing +15.4.4.15-3-17 failing +15.4.4.15-3-18 failing +15.4.4.15-3-19 failing +15.4.4.15-3-2 failing +15.4.4.15-3-20 failing +15.4.4.15-3-21 failing +15.4.4.15-3-22 failing +15.4.4.15-3-23 failing +15.4.4.15-3-24 failing +15.4.4.15-3-25 failing +15.4.4.15-3-28 failing +15.4.4.15-3-29 failing +15.4.4.15-3-3 failing +15.4.4.15-3-4 failing +15.4.4.15-3-5 failing +15.4.4.15-3-6 failing +15.4.4.15-3-7 failing +15.4.4.15-3-8 failing +15.4.4.15-3-9 failing +15.4.4.15-4-1 failing +15.4.4.15-4-10 failing +15.4.4.15-4-11 failing +15.4.4.15-4-2 failing +15.4.4.15-4-3 failing +15.4.4.15-4-4 failing +15.4.4.15-4-5 failing +15.4.4.15-4-6 failing +15.4.4.15-4-7 failing +15.4.4.15-4-8 failing +15.4.4.15-4-9 failing +15.4.4.15-5-1 failing +15.4.4.15-5-10 failing +15.4.4.15-5-11 failing +15.4.4.15-5-13 failing +15.4.4.15-5-14 failing +15.4.4.15-5-15 failing +15.4.4.15-5-17 failing +15.4.4.15-5-18 failing +15.4.4.15-5-19 failing +15.4.4.15-5-2 failing +15.4.4.15-5-20 failing +15.4.4.15-5-21 failing +15.4.4.15-5-22 failing +15.4.4.15-5-23 failing +15.4.4.15-5-24 failing +15.4.4.15-5-25 failing +15.4.4.15-5-26 failing +15.4.4.15-5-27 failing +15.4.4.15-5-28 failing +15.4.4.15-5-29 failing +15.4.4.15-5-3 failing +15.4.4.15-5-30 failing +15.4.4.15-5-31 failing +15.4.4.15-5-32 failing +15.4.4.15-5-33 failing +15.4.4.15-5-4 failing +15.4.4.15-5-5 failing +15.4.4.15-5-6 failing +15.4.4.15-5-7 failing +15.4.4.15-5-8 failing +15.4.4.15-5-9 failing +15.4.4.15-6-1 failing +15.4.4.15-6-2 failing +15.4.4.15-6-3 failing +15.4.4.15-6-4 failing +15.4.4.15-6-5 failing +15.4.4.15-6-6 failing +15.4.4.15-7-1 failing +15.4.4.15-7-2 failing +15.4.4.15-7-3 failing +15.4.4.15-7-4 failing +15.4.4.15-8-1 failing +15.4.4.15-8-10 failing +15.4.4.15-8-11 failing +15.4.4.15-8-2 failing +15.4.4.15-8-3 failing +15.4.4.15-8-4 failing +15.4.4.15-8-5 failing +15.4.4.15-8-6 failing +15.4.4.15-8-7 failing +15.4.4.15-8-8 failing +15.4.4.15-8-a-1 failing +15.4.4.15-8-a-10 failing +15.4.4.15-8-a-11 failing +15.4.4.15-8-a-12 failing +15.4.4.15-8-a-13 failing +15.4.4.15-8-a-14 failing +15.4.4.15-8-a-15 failing +15.4.4.15-8-a-16 failing +15.4.4.15-8-a-17 failing +15.4.4.15-8-a-18 failing +15.4.4.15-8-a-19 failing +15.4.4.15-8-a-2 failing +15.4.4.15-8-a-3 failing +15.4.4.15-8-a-4 failing +15.4.4.15-8-a-5 failing +15.4.4.15-8-a-6 failing +15.4.4.15-8-a-7 failing +15.4.4.15-8-a-8 failing +15.4.4.15-8-a-9 failing +15.4.4.15-8-b-1 failing +15.4.4.15-8-b-i-1 failing +15.4.4.15-8-b-i-10 failing +15.4.4.15-8-b-i-11 failing +15.4.4.15-8-b-i-12 failing +15.4.4.15-8-b-i-13 failing +15.4.4.15-8-b-i-14 failing +15.4.4.15-8-b-i-15 failing +15.4.4.15-8-b-i-16 failing +15.4.4.15-8-b-i-17 failing +15.4.4.15-8-b-i-18 failing +15.4.4.15-8-b-i-19 failing +15.4.4.15-8-b-i-2 failing +15.4.4.15-8-b-i-20 failing +15.4.4.15-8-b-i-21 failing +15.4.4.15-8-b-i-22 failing +15.4.4.15-8-b-i-23 failing +15.4.4.15-8-b-i-25 failing +15.4.4.15-8-b-i-26 failing +15.4.4.15-8-b-i-27 failing +15.4.4.15-8-b-i-28 failing +15.4.4.15-8-b-i-29 failing +15.4.4.15-8-b-i-3 failing +15.4.4.15-8-b-i-30 failing +15.4.4.15-8-b-i-31 failing +15.4.4.15-8-b-i-4 failing +15.4.4.15-8-b-i-5 failing +15.4.4.15-8-b-i-6 failing +15.4.4.15-8-b-i-7 failing +15.4.4.15-8-b-i-8 failing +15.4.4.15-8-b-i-9 failing +15.4.4.15-8-b-ii-1 failing +15.4.4.15-8-b-ii-10 failing +15.4.4.15-8-b-ii-11 failing +15.4.4.15-8-b-ii-2 failing +15.4.4.15-8-b-ii-3 failing +15.4.4.15-8-b-ii-4 failing +15.4.4.15-8-b-ii-5 failing +15.4.4.15-8-b-ii-6 failing +15.4.4.15-8-b-ii-7 failing +15.4.4.15-8-b-ii-8 failing +15.4.4.15-8-b-ii-9 failing +15.4.4.15-8-b-iii-1 failing +15.4.4.15-8-b-iii-2 failing +15.4.4.15-9-1 failing +15.4.4.15-9-2 failing +15.4.4.16-0-2 failing +15.4.4.16-1-1 failing +15.4.4.16-1-10 failing +15.4.4.16-1-11 failing +15.4.4.16-1-12 failing +15.4.4.16-1-13 failing +15.4.4.16-1-14 failing +15.4.4.16-1-15 failing +15.4.4.16-1-2 failing +15.4.4.16-1-3 failing +15.4.4.16-1-4 failing +15.4.4.16-1-5 failing +15.4.4.16-1-6 failing +15.4.4.16-1-7 failing +15.4.4.16-1-8 failing +15.4.4.16-1-9 failing +15.4.4.16-2-1 failing +15.4.4.16-2-10 failing +15.4.4.16-2-11 failing +15.4.4.16-2-12 failing +15.4.4.16-2-13 failing +15.4.4.16-2-14 failing +15.4.4.16-2-15 failing +15.4.4.16-2-17 failing +15.4.4.16-2-18 failing +15.4.4.16-2-19 failing +15.4.4.16-2-2 failing +15.4.4.16-2-3 failing +15.4.4.16-2-4 failing +15.4.4.16-2-5 failing +15.4.4.16-2-6 failing +15.4.4.16-2-7 failing +15.4.4.16-2-8 failing +15.4.4.16-2-9 failing +15.4.4.16-3-1 failing +15.4.4.16-3-10 failing +15.4.4.16-3-11 failing +15.4.4.16-3-12 failing +15.4.4.16-3-13 failing +15.4.4.16-3-14 failing +15.4.4.16-3-15 failing +15.4.4.16-3-16 failing +15.4.4.16-3-17 failing +15.4.4.16-3-18 failing +15.4.4.16-3-19 failing +15.4.4.16-3-2 failing +15.4.4.16-3-20 failing +15.4.4.16-3-21 failing +15.4.4.16-3-22 failing +15.4.4.16-3-23 failing +15.4.4.16-3-24 failing +15.4.4.16-3-25 failing +15.4.4.16-3-28 failing +15.4.4.16-3-29 failing +15.4.4.16-3-3 failing +15.4.4.16-3-4 failing +15.4.4.16-3-5 failing +15.4.4.16-3-6 failing +15.4.4.16-3-7 failing +15.4.4.16-3-8 failing +15.4.4.16-3-9 failing +15.4.4.16-4-1 failing +15.4.4.16-4-10 failing +15.4.4.16-4-11 failing +15.4.4.16-4-15 failing +15.4.4.16-4-3 failing +15.4.4.16-4-4 failing +15.4.4.16-4-5 failing +15.4.4.16-4-6 failing +15.4.4.16-4-7 failing +15.4.4.16-4-8 failing +15.4.4.16-4-9 failing +15.4.4.16-5-17 failing +15.4.4.16-7-4 failing +15.4.4.16-7-5 failing +15.4.4.16-7-6 failing +15.4.4.16-7-8 failing +15.4.4.16-7-9 failing +15.4.4.16-7-b-1 failing +15.4.4.16-7-b-10 failing +15.4.4.16-7-b-11 failing +15.4.4.16-7-b-12 failing +15.4.4.16-7-b-13 failing +15.4.4.16-7-b-14 failing +15.4.4.16-7-b-15 failing +15.4.4.16-7-b-16 failing +15.4.4.16-7-b-2 failing +15.4.4.16-7-b-3 failing +15.4.4.16-7-b-4 failing +15.4.4.16-7-b-5 failing +15.4.4.16-7-b-6 failing +15.4.4.16-7-b-7 failing +15.4.4.16-7-b-8 failing +15.4.4.16-7-b-9 failing +15.4.4.16-7-c-i-1 failing +15.4.4.16-7-c-i-10 failing +15.4.4.16-7-c-i-11 failing +15.4.4.16-7-c-i-12 failing +15.4.4.16-7-c-i-13 failing +15.4.4.16-7-c-i-14 failing +15.4.4.16-7-c-i-15 failing +15.4.4.16-7-c-i-16 failing +15.4.4.16-7-c-i-17 failing +15.4.4.16-7-c-i-18 failing +15.4.4.16-7-c-i-19 failing +15.4.4.16-7-c-i-20 failing +15.4.4.16-7-c-i-21 failing +15.4.4.16-7-c-i-22 failing +15.4.4.16-7-c-i-23 failing +15.4.4.16-7-c-i-25 failing +15.4.4.16-7-c-i-26 failing +15.4.4.16-7-c-i-27 failing +15.4.4.16-7-c-i-28 failing +15.4.4.16-7-c-i-29 failing +15.4.4.16-7-c-i-3 failing +15.4.4.16-7-c-i-30 failing +15.4.4.16-7-c-i-31 failing +15.4.4.16-7-c-i-4 failing +15.4.4.16-7-c-i-5 failing +15.4.4.16-7-c-i-6 failing +15.4.4.16-7-c-i-7 failing +15.4.4.16-7-c-i-8 failing +15.4.4.16-7-c-i-9 failing +15.4.4.16-7-c-ii-11 failing +15.4.4.16-7-c-ii-13 failing +15.4.4.16-7-c-ii-16 failing +15.4.4.16-7-c-ii-17 failing +15.4.4.16-7-c-ii-18 failing +15.4.4.16-7-c-ii-19 failing +15.4.4.16-7-c-ii-20 failing +15.4.4.16-7-c-ii-21 failing +15.4.4.16-7-c-ii-22 failing +15.4.4.16-7-c-ii-23 failing +15.4.4.16-7-c-ii-5 failing +15.4.4.16-7-c-ii-6 failing +15.4.4.16-7-c-ii-7 failing +15.4.4.16-7-c-ii-8 failing +15.4.4.16-7-c-iii-1 failing +15.4.4.16-7-c-iii-2 failing +15.4.4.16-7-c-iii-23 failing +15.4.4.16-7-c-iii-28 failing +15.4.4.16-7-c-iii-3 failing +15.4.4.16-7-c-iii-4 failing +15.4.4.16-8-10 failing +15.4.4.16-8-2 failing +15.4.4.16-8-3 failing +15.4.4.16-8-4 failing +15.4.4.16-8-5 failing +15.4.4.16-8-6 failing +15.4.4.16-8-7 failing +15.4.4.16-8-8 failing +15.4.4.17-0-2 failing +15.4.4.17-1-1 failing +15.4.4.17-1-10 failing +15.4.4.17-1-11 failing +15.4.4.17-1-12 failing +15.4.4.17-1-13 failing +15.4.4.17-1-14 failing +15.4.4.17-1-15 failing +15.4.4.17-1-2 failing +15.4.4.17-1-3 failing +15.4.4.17-1-4 failing +15.4.4.17-1-5 failing +15.4.4.17-1-6 failing +15.4.4.17-1-7 failing +15.4.4.17-1-8 failing +15.4.4.17-1-9 failing +15.4.4.17-2-1 failing +15.4.4.17-2-10 failing +15.4.4.17-2-11 failing +15.4.4.17-2-12 failing +15.4.4.17-2-13 failing +15.4.4.17-2-14 failing +15.4.4.17-2-15 failing +15.4.4.17-2-17 failing +15.4.4.17-2-18 failing +15.4.4.17-2-19 failing +15.4.4.17-2-2 failing +15.4.4.17-2-3 failing +15.4.4.17-2-4 failing +15.4.4.17-2-5 failing +15.4.4.17-2-6 failing +15.4.4.17-2-7 failing +15.4.4.17-2-8 failing +15.4.4.17-2-9 failing +15.4.4.17-3-1 failing +15.4.4.17-3-10 failing +15.4.4.17-3-11 failing +15.4.4.17-3-12 failing +15.4.4.17-3-13 failing +15.4.4.17-3-14 failing +15.4.4.17-3-15 failing +15.4.4.17-3-16 failing +15.4.4.17-3-17 failing +15.4.4.17-3-18 failing +15.4.4.17-3-19 failing +15.4.4.17-3-2 failing +15.4.4.17-3-20 failing +15.4.4.17-3-21 failing +15.4.4.17-3-22 failing +15.4.4.17-3-23 failing +15.4.4.17-3-24 failing +15.4.4.17-3-25 failing +15.4.4.17-3-28 failing +15.4.4.17-3-29 failing +15.4.4.17-3-3 failing +15.4.4.17-3-4 failing +15.4.4.17-3-5 failing +15.4.4.17-3-6 failing +15.4.4.17-3-7 failing +15.4.4.17-3-8 failing +15.4.4.17-3-9 failing +15.4.4.17-4-1 failing +15.4.4.17-4-10 failing +15.4.4.17-4-11 failing +15.4.4.17-4-15 failing +15.4.4.17-4-2 failing +15.4.4.17-4-3 failing +15.4.4.17-4-4 failing +15.4.4.17-4-5 failing +15.4.4.17-4-6 failing +15.4.4.17-4-7 failing +15.4.4.17-4-8 failing +15.4.4.17-4-9 failing +15.4.4.17-5-17 failing +15.4.4.17-7-4 failing +15.4.4.17-7-5 failing +15.4.4.17-7-6 failing +15.4.4.17-7-8 failing +15.4.4.17-7-9 failing +15.4.4.17-7-b-1 failing +15.4.4.17-7-b-10 failing +15.4.4.17-7-b-11 failing +15.4.4.17-7-b-12 failing +15.4.4.17-7-b-13 failing +15.4.4.17-7-b-14 failing +15.4.4.17-7-b-15 failing +15.4.4.17-7-b-16 failing +15.4.4.17-7-b-2 failing +15.4.4.17-7-b-3 failing +15.4.4.17-7-b-4 failing +15.4.4.17-7-b-5 failing +15.4.4.17-7-b-6 failing +15.4.4.17-7-b-7 failing +15.4.4.17-7-b-8 failing +15.4.4.17-7-b-9 failing +15.4.4.17-7-c-i-1 failing +15.4.4.17-7-c-i-10 failing +15.4.4.17-7-c-i-11 failing +15.4.4.17-7-c-i-12 failing +15.4.4.17-7-c-i-13 failing +15.4.4.17-7-c-i-14 failing +15.4.4.17-7-c-i-15 failing +15.4.4.17-7-c-i-16 failing +15.4.4.17-7-c-i-17 failing +15.4.4.17-7-c-i-18 failing +15.4.4.17-7-c-i-19 failing +15.4.4.17-7-c-i-20 failing +15.4.4.17-7-c-i-21 failing +15.4.4.17-7-c-i-22 failing +15.4.4.17-7-c-i-23 failing +15.4.4.17-7-c-i-25 failing +15.4.4.17-7-c-i-26 failing +15.4.4.17-7-c-i-27 failing +15.4.4.17-7-c-i-28 failing +15.4.4.17-7-c-i-29 failing +15.4.4.17-7-c-i-3 failing +15.4.4.17-7-c-i-30 failing +15.4.4.17-7-c-i-31 failing +15.4.4.17-7-c-i-4 failing +15.4.4.17-7-c-i-5 failing +15.4.4.17-7-c-i-6 failing +15.4.4.17-7-c-i-7 failing +15.4.4.17-7-c-i-8 failing +15.4.4.17-7-c-i-9 failing +15.4.4.17-7-c-ii-11 failing +15.4.4.17-7-c-ii-13 failing +15.4.4.17-7-c-ii-16 failing +15.4.4.17-7-c-ii-17 failing +15.4.4.17-7-c-ii-18 failing +15.4.4.17-7-c-ii-19 failing +15.4.4.17-7-c-ii-20 failing +15.4.4.17-7-c-ii-21 failing +15.4.4.17-7-c-ii-22 failing +15.4.4.17-7-c-ii-23 failing +15.4.4.17-7-c-ii-5 failing +15.4.4.17-7-c-ii-6 failing +15.4.4.17-7-c-ii-7 failing +15.4.4.17-7-c-ii-8 failing +15.4.4.17-7-c-iii-1 failing +15.4.4.17-7-c-iii-2 failing +15.4.4.17-7-c-iii-23 failing +15.4.4.17-7-c-iii-28 failing +15.4.4.17-7-c-iii-3 failing +15.4.4.17-7-c-iii-4 failing +15.4.4.17-8-10 failing +15.4.4.17-8-2 failing +15.4.4.17-8-3 failing +15.4.4.17-8-4 failing +15.4.4.17-8-5 failing +15.4.4.17-8-6 failing +15.4.4.17-8-7 failing +15.4.4.17-8-8 failing +15.4.4.18-0-2 failing +15.4.4.18-1-1 failing +15.4.4.18-1-10 failing +15.4.4.18-1-11 failing +15.4.4.18-1-12 failing +15.4.4.18-1-13 failing +15.4.4.18-1-14 failing +15.4.4.18-1-15 failing +15.4.4.18-1-2 failing +15.4.4.18-1-3 failing +15.4.4.18-1-4 failing +15.4.4.18-1-5 failing +15.4.4.18-1-6 failing +15.4.4.18-1-7 failing +15.4.4.18-1-8 failing +15.4.4.18-1-9 failing +15.4.4.18-2-1 failing +15.4.4.18-2-10 failing +15.4.4.18-2-11 failing +15.4.4.18-2-12 failing +15.4.4.18-2-13 failing +15.4.4.18-2-14 failing +15.4.4.18-2-15 failing +15.4.4.18-2-17 failing +15.4.4.18-2-18 failing +15.4.4.18-2-19 failing +15.4.4.18-2-3 failing +15.4.4.18-2-4 failing +15.4.4.18-2-5 failing +15.4.4.18-2-6 failing +15.4.4.18-2-7 failing +15.4.4.18-2-8 failing +15.4.4.18-2-9 failing +15.4.4.18-3-1 failing +15.4.4.18-3-10 failing +15.4.4.18-3-11 failing +15.4.4.18-3-12 failing +15.4.4.18-3-13 failing +15.4.4.18-3-14 failing +15.4.4.18-3-15 failing +15.4.4.18-3-16 failing +15.4.4.18-3-17 failing +15.4.4.18-3-18 failing +15.4.4.18-3-19 failing +15.4.4.18-3-2 failing +15.4.4.18-3-20 failing +15.4.4.18-3-21 failing +15.4.4.18-3-22 failing +15.4.4.18-3-23 failing +15.4.4.18-3-24 failing +15.4.4.18-3-25 failing +15.4.4.18-3-28 failing +15.4.4.18-3-29 failing +15.4.4.18-3-3 failing +15.4.4.18-3-4 failing +15.4.4.18-3-5 failing +15.4.4.18-3-6 failing +15.4.4.18-3-7 failing +15.4.4.18-3-8 failing +15.4.4.18-3-9 failing +15.4.4.18-4-1 failing +15.4.4.18-4-10 failing +15.4.4.18-4-11 failing +15.4.4.18-4-15 failing +15.4.4.18-4-2 failing +15.4.4.18-4-3 failing +15.4.4.18-4-4 failing +15.4.4.18-4-5 failing +15.4.4.18-4-6 failing +15.4.4.18-4-7 failing +15.4.4.18-4-8 failing +15.4.4.18-4-9 failing +15.4.4.18-5-17 failing +15.4.4.18-7-1 failing +15.4.4.18-7-3 failing +15.4.4.18-7-4 failing +15.4.4.18-7-5 failing +15.4.4.18-7-8 failing +15.4.4.18-7-9 failing +15.4.4.18-7-b-1 failing +15.4.4.18-7-b-10 failing +15.4.4.18-7-b-11 failing +15.4.4.18-7-b-12 failing +15.4.4.18-7-b-13 failing +15.4.4.18-7-b-14 failing +15.4.4.18-7-b-15 failing +15.4.4.18-7-b-16 failing +15.4.4.18-7-b-2 failing +15.4.4.18-7-b-3 failing +15.4.4.18-7-b-4 failing +15.4.4.18-7-b-5 failing +15.4.4.18-7-b-6 failing +15.4.4.18-7-b-7 failing +15.4.4.18-7-b-8 failing +15.4.4.18-7-b-9 failing +15.4.4.18-7-c-i-1 failing +15.4.4.18-7-c-i-10 failing +15.4.4.18-7-c-i-11 failing +15.4.4.18-7-c-i-12 failing +15.4.4.18-7-c-i-13 failing +15.4.4.18-7-c-i-14 failing +15.4.4.18-7-c-i-15 failing +15.4.4.18-7-c-i-16 failing +15.4.4.18-7-c-i-17 failing +15.4.4.18-7-c-i-18 failing +15.4.4.18-7-c-i-19 failing +15.4.4.18-7-c-i-20 failing +15.4.4.18-7-c-i-21 failing +15.4.4.18-7-c-i-22 failing +15.4.4.18-7-c-i-23 failing +15.4.4.18-7-c-i-25 failing +15.4.4.18-7-c-i-26 failing +15.4.4.18-7-c-i-27 failing +15.4.4.18-7-c-i-28 failing +15.4.4.18-7-c-i-29 failing +15.4.4.18-7-c-i-3 failing +15.4.4.18-7-c-i-30 failing +15.4.4.18-7-c-i-31 failing +15.4.4.18-7-c-i-4 failing +15.4.4.18-7-c-i-5 failing +15.4.4.18-7-c-i-6 failing +15.4.4.18-7-c-i-7 failing +15.4.4.18-7-c-i-8 failing +15.4.4.18-7-c-i-9 failing +15.4.4.18-7-c-ii-11 failing +15.4.4.18-7-c-ii-13 failing +15.4.4.18-7-c-ii-16 failing +15.4.4.18-7-c-ii-17 failing +15.4.4.18-7-c-ii-18 failing +15.4.4.18-7-c-ii-19 failing +15.4.4.18-7-c-ii-20 failing +15.4.4.18-7-c-ii-21 failing +15.4.4.18-7-c-ii-22 failing +15.4.4.18-7-c-ii-23 failing +15.4.4.18-7-c-ii-5 failing +15.4.4.18-7-c-ii-6 failing +15.4.4.18-7-c-ii-7 failing +15.4.4.18-7-c-ii-8 failing +15.4.4.18-8-10 failing +15.4.4.18-8-2 failing +15.4.4.18-8-3 failing +15.4.4.18-8-4 failing +15.4.4.18-8-5 failing +15.4.4.18-8-6 failing +15.4.4.18-8-7 failing +15.4.4.18-8-8 failing +15.4.4.18-8-9 failing +15.4.4.19-0-2 failing +15.4.4.19-1-1 failing +15.4.4.19-1-10 failing +15.4.4.19-1-11 failing +15.4.4.19-1-12 failing +15.4.4.19-1-13 failing +15.4.4.19-1-14 failing +15.4.4.19-1-15 failing +15.4.4.19-1-2 failing +15.4.4.19-1-3 failing +15.4.4.19-1-4 failing +15.4.4.19-1-5 failing +15.4.4.19-1-6 failing +15.4.4.19-1-7 failing +15.4.4.19-1-8 failing +15.4.4.19-1-9 failing +15.4.4.19-2-1 failing +15.4.4.19-2-10 failing +15.4.4.19-2-11 failing +15.4.4.19-2-12 failing +15.4.4.19-2-13 failing +15.4.4.19-2-14 failing +15.4.4.19-2-15 failing +15.4.4.19-2-17 failing +15.4.4.19-2-18 failing +15.4.4.19-2-19 failing +15.4.4.19-2-3 failing +15.4.4.19-2-4 failing +15.4.4.19-2-5 failing +15.4.4.19-2-6 failing +15.4.4.19-2-7 failing +15.4.4.19-2-8 failing +15.4.4.19-2-9 failing +15.4.4.19-3-1 failing +15.4.4.19-3-10 failing +15.4.4.19-3-11 failing +15.4.4.19-3-12 failing +15.4.4.19-3-13 failing +15.4.4.19-3-14 failing +15.4.4.19-3-15 failing +15.4.4.19-3-16 failing +15.4.4.19-3-17 failing +15.4.4.19-3-18 failing +15.4.4.19-3-19 failing +15.4.4.19-3-2 failing +15.4.4.19-3-20 failing +15.4.4.19-3-21 failing +15.4.4.19-3-22 failing +15.4.4.19-3-23 failing +15.4.4.19-3-24 failing +15.4.4.19-3-25 failing +15.4.4.19-3-28 failing +15.4.4.19-3-29 failing +15.4.4.19-3-3 failing +15.4.4.19-3-4 failing +15.4.4.19-3-5 failing +15.4.4.19-3-6 failing +15.4.4.19-3-7 failing +15.4.4.19-3-8 failing +15.4.4.19-3-9 failing +15.4.4.19-4-1 failing +15.4.4.19-4-10 failing +15.4.4.19-4-11 failing +15.4.4.19-4-15 failing +15.4.4.19-4-2 failing +15.4.4.19-4-3 failing +15.4.4.19-4-4 failing +15.4.4.19-4-5 failing +15.4.4.19-4-6 failing +15.4.4.19-4-7 failing +15.4.4.19-4-8 failing +15.4.4.19-4-9 failing +15.4.4.19-5-1 failing +15.4.4.19-5-17 failing +15.4.4.19-6-1 failing +15.4.4.19-8-1 failing +15.4.4.19-8-4 failing +15.4.4.19-8-5 failing +15.4.4.19-8-6 failing +15.4.4.19-8-8 failing +15.4.4.19-8-b-1 failing +15.4.4.19-8-b-10 failing +15.4.4.19-8-b-11 failing +15.4.4.19-8-b-12 failing +15.4.4.19-8-b-13 failing +15.4.4.19-8-b-15 failing +15.4.4.19-8-b-16 failing +15.4.4.19-8-b-2 failing +15.4.4.19-8-b-3 failing +15.4.4.19-8-b-4 failing +15.4.4.19-8-b-5 failing +15.4.4.19-8-b-6 failing +15.4.4.19-8-b-7 failing +15.4.4.19-8-b-8 failing +15.4.4.19-8-c-i-1 failing +15.4.4.19-8-c-i-10 failing +15.4.4.19-8-c-i-11 failing +15.4.4.19-8-c-i-12 failing +15.4.4.19-8-c-i-13 failing +15.4.4.19-8-c-i-14 failing +15.4.4.19-8-c-i-15 failing +15.4.4.19-8-c-i-16 failing +15.4.4.19-8-c-i-17 failing +15.4.4.19-8-c-i-18 failing +15.4.4.19-8-c-i-19 failing +15.4.4.19-8-c-i-20 failing +15.4.4.19-8-c-i-21 failing +15.4.4.19-8-c-i-22 failing +15.4.4.19-8-c-i-23 failing +15.4.4.19-8-c-i-25 failing +15.4.4.19-8-c-i-26 failing +15.4.4.19-8-c-i-27 failing +15.4.4.19-8-c-i-28 failing +15.4.4.19-8-c-i-29 failing +15.4.4.19-8-c-i-3 failing +15.4.4.19-8-c-i-30 failing +15.4.4.19-8-c-i-31 failing +15.4.4.19-8-c-i-4 failing +15.4.4.19-8-c-i-5 failing +15.4.4.19-8-c-i-6 failing +15.4.4.19-8-c-i-7 failing +15.4.4.19-8-c-i-8 failing +15.4.4.19-8-c-i-9 failing +15.4.4.19-8-c-ii-11 failing +15.4.4.19-8-c-ii-13 failing +15.4.4.19-8-c-ii-16 failing +15.4.4.19-8-c-ii-17 failing +15.4.4.19-8-c-ii-18 failing +15.4.4.19-8-c-ii-19 failing +15.4.4.19-8-c-ii-20 failing +15.4.4.19-8-c-ii-21 failing +15.4.4.19-8-c-ii-22 failing +15.4.4.19-8-c-ii-23 failing +15.4.4.19-8-c-ii-5 failing +15.4.4.19-8-c-ii-6 failing +15.4.4.19-8-c-ii-7 failing +15.4.4.19-8-c-ii-8 failing +15.4.4.19-8-c-iii-1 failing +15.4.4.19-8-c-iii-2 failing +15.4.4.19-8-c-iii-3 failing +15.4.4.19-8-c-iii-4 failing +15.4.4.19-8-c-iii-5 failing +15.4.4.19-9-10 failing +15.4.4.19-9-11 failing +15.4.4.19-9-12 failing +15.4.4.19-9-3 failing +15.4.4.19-9-5 failing +15.4.4.19-9-6 failing +15.4.4.19-9-7 failing +15.4.4.19-9-8 failing +15.4.4.19-9-9 failing +S15.4.4.2_A1_T1 failing +S15.4.4.2_A1_T4 failing +S15.4.4.2_A3_T1 failing +S15.4.4.2_A4.2 failing +S15.4.4.2_A4.3 failing +S15.4.4.2_A4.4 failing +S15.4.4.2_A4.7 failing +15.4.4.20-0-2 failing +15.4.4.20-1-1 failing +15.4.4.20-1-10 failing +15.4.4.20-1-11 failing +15.4.4.20-1-12 failing +15.4.4.20-1-13 failing +15.4.4.20-1-14 failing +15.4.4.20-1-15 failing +15.4.4.20-1-2 failing +15.4.4.20-1-3 failing +15.4.4.20-1-4 failing +15.4.4.20-1-5 failing +15.4.4.20-1-6 failing +15.4.4.20-1-7 failing +15.4.4.20-1-8 failing +15.4.4.20-1-9 failing +15.4.4.20-10-3 failing +15.4.4.20-2-1 failing +15.4.4.20-2-10 failing +15.4.4.20-2-11 failing +15.4.4.20-2-12 failing +15.4.4.20-2-13 failing +15.4.4.20-2-14 failing +15.4.4.20-2-15 failing +15.4.4.20-2-17 failing +15.4.4.20-2-18 failing +15.4.4.20-2-19 failing +15.4.4.20-2-3 failing +15.4.4.20-2-4 failing +15.4.4.20-2-5 failing +15.4.4.20-2-6 failing +15.4.4.20-2-7 failing +15.4.4.20-2-8 failing +15.4.4.20-2-9 failing +15.4.4.20-3-1 failing +15.4.4.20-3-10 failing +15.4.4.20-3-11 failing +15.4.4.20-3-12 failing +15.4.4.20-3-13 failing +15.4.4.20-3-14 failing +15.4.4.20-3-15 failing +15.4.4.20-3-16 failing +15.4.4.20-3-17 failing +15.4.4.20-3-18 failing +15.4.4.20-3-19 failing +15.4.4.20-3-2 failing +15.4.4.20-3-20 failing +15.4.4.20-3-21 failing +15.4.4.20-3-22 failing +15.4.4.20-3-23 failing +15.4.4.20-3-24 failing +15.4.4.20-3-25 failing +15.4.4.20-3-28 failing +15.4.4.20-3-29 failing +15.4.4.20-3-3 failing +15.4.4.20-3-4 failing +15.4.4.20-3-5 failing +15.4.4.20-3-6 failing +15.4.4.20-3-7 failing +15.4.4.20-3-8 failing +15.4.4.20-3-9 failing +15.4.4.20-4-1 failing +15.4.4.20-4-10 failing +15.4.4.20-4-11 failing +15.4.4.20-4-15 failing +15.4.4.20-4-2 failing +15.4.4.20-4-3 failing +15.4.4.20-4-4 failing +15.4.4.20-4-5 failing +15.4.4.20-4-6 failing +15.4.4.20-4-7 failing +15.4.4.20-4-8 failing +15.4.4.20-4-9 failing +15.4.4.20-5-17 failing +15.4.4.20-5-27 failing +15.4.4.20-6-1 failing +15.4.4.20-6-2 failing +15.4.4.20-6-3 failing +15.4.4.20-6-4 failing +15.4.4.20-6-5 failing +15.4.4.20-6-6 failing +15.4.4.20-6-7 failing +15.4.4.20-6-8 failing +15.4.4.20-9-1 failing +15.4.4.20-9-4 failing +15.4.4.20-9-5 failing +15.4.4.20-9-6 failing +15.4.4.20-9-8 failing +15.4.4.20-9-9 failing +15.4.4.20-9-b-1 failing +15.4.4.20-9-b-10 failing +15.4.4.20-9-b-11 failing +15.4.4.20-9-b-12 failing +15.4.4.20-9-b-13 failing +15.4.4.20-9-b-14 failing +15.4.4.20-9-b-15 failing +15.4.4.20-9-b-16 failing +15.4.4.20-9-b-2 failing +15.4.4.20-9-b-3 failing +15.4.4.20-9-b-4 failing +15.4.4.20-9-b-5 failing +15.4.4.20-9-b-6 failing +15.4.4.20-9-b-7 failing +15.4.4.20-9-b-8 failing +15.4.4.20-9-b-9 failing +15.4.4.20-9-c-i-1 failing +15.4.4.20-9-c-i-10 failing +15.4.4.20-9-c-i-11 failing +15.4.4.20-9-c-i-12 failing +15.4.4.20-9-c-i-13 failing +15.4.4.20-9-c-i-14 failing +15.4.4.20-9-c-i-15 failing +15.4.4.20-9-c-i-16 failing +15.4.4.20-9-c-i-17 failing +15.4.4.20-9-c-i-18 failing +15.4.4.20-9-c-i-19 failing +15.4.4.20-9-c-i-20 failing +15.4.4.20-9-c-i-21 failing +15.4.4.20-9-c-i-22 failing +15.4.4.20-9-c-i-23 failing +15.4.4.20-9-c-i-25 failing +15.4.4.20-9-c-i-26 failing +15.4.4.20-9-c-i-27 failing +15.4.4.20-9-c-i-28 failing +15.4.4.20-9-c-i-29 failing +15.4.4.20-9-c-i-3 failing +15.4.4.20-9-c-i-30 failing +15.4.4.20-9-c-i-31 failing +15.4.4.20-9-c-i-4 failing +15.4.4.20-9-c-i-5 failing +15.4.4.20-9-c-i-6 failing +15.4.4.20-9-c-i-7 failing +15.4.4.20-9-c-i-8 failing +15.4.4.20-9-c-i-9 failing +15.4.4.20-9-c-ii-11 failing +15.4.4.20-9-c-ii-13 failing +15.4.4.20-9-c-ii-16 failing +15.4.4.20-9-c-ii-17 failing +15.4.4.20-9-c-ii-18 failing +15.4.4.20-9-c-ii-19 failing +15.4.4.20-9-c-ii-20 failing +15.4.4.20-9-c-ii-21 failing +15.4.4.20-9-c-ii-22 failing +15.4.4.20-9-c-ii-23 failing +15.4.4.20-9-c-ii-6 failing +15.4.4.20-9-c-ii-7 failing +15.4.4.20-9-c-ii-8 failing +15.4.4.20-9-c-iii-1-1 failing +15.4.4.20-9-c-iii-1-2 failing +15.4.4.20-9-c-iii-1-3 failing +15.4.4.20-9-c-iii-1-4 failing +15.4.4.20-9-c-iii-1 failing +15.4.4.20-9-c-iii-2 failing +15.4.4.20-9-c-iii-24 failing +15.4.4.20-9-c-iii-29 failing +15.4.4.20-9-c-iii-3 failing +15.4.4.20-9-c-iii-4 failing +15.4.4.20-9-c-iii-5 failing +15.4.4.21-0-2 failing +15.4.4.21-1-1 failing +15.4.4.21-1-10 failing +15.4.4.21-1-11 failing +15.4.4.21-1-12 failing +15.4.4.21-1-13 failing +15.4.4.21-1-14 failing +15.4.4.21-1-15 failing +15.4.4.21-1-2 failing +15.4.4.21-1-3 failing +15.4.4.21-1-4 failing +15.4.4.21-1-5 failing +15.4.4.21-1-6 failing +15.4.4.21-1-7 failing +15.4.4.21-1-8 failing +15.4.4.21-1-9 failing +15.4.4.21-10-3 failing +15.4.4.21-10-4 failing +15.4.4.21-10-6 failing +15.4.4.21-10-7 failing +15.4.4.21-2-1 failing +15.4.4.21-2-10 failing +15.4.4.21-2-11 failing +15.4.4.21-2-12 failing +15.4.4.21-2-13 failing +15.4.4.21-2-14 failing +15.4.4.21-2-15 failing +15.4.4.21-2-17 failing +15.4.4.21-2-18 failing +15.4.4.21-2-19 failing +15.4.4.21-2-3 failing +15.4.4.21-2-4 failing +15.4.4.21-2-5 failing +15.4.4.21-2-6 failing +15.4.4.21-2-7 failing +15.4.4.21-2-8 failing +15.4.4.21-2-9 failing +15.4.4.21-3-1 failing +15.4.4.21-3-10 failing +15.4.4.21-3-11 failing +15.4.4.21-3-12 failing +15.4.4.21-3-13 failing +15.4.4.21-3-14 failing +15.4.4.21-3-15 failing +15.4.4.21-3-16 failing +15.4.4.21-3-17 failing +15.4.4.21-3-18 failing +15.4.4.21-3-19 failing +15.4.4.21-3-2 failing +15.4.4.21-3-20 failing +15.4.4.21-3-21 failing +15.4.4.21-3-22 failing +15.4.4.21-3-23 failing +15.4.4.21-3-24 failing +15.4.4.21-3-25 failing +15.4.4.21-3-28 failing +15.4.4.21-3-29 failing +15.4.4.21-3-3 failing +15.4.4.21-3-4 failing +15.4.4.21-3-5 failing +15.4.4.21-3-6 failing +15.4.4.21-3-7 failing +15.4.4.21-3-8 failing +15.4.4.21-3-9 failing +15.4.4.21-4-1 failing +15.4.4.21-4-10 failing +15.4.4.21-4-11 failing +15.4.4.21-4-15 failing +15.4.4.21-4-2 failing +15.4.4.21-4-3 failing +15.4.4.21-4-4 failing +15.4.4.21-4-5 failing +15.4.4.21-4-6 failing +15.4.4.21-4-7 failing +15.4.4.21-4-8 failing +15.4.4.21-4-9 failing +15.4.4.21-5-1 failing +15.4.4.21-5-10 failing +15.4.4.21-5-11 failing +15.4.4.21-5-12 failing +15.4.4.21-5-13 failing +15.4.4.21-5-2 failing +15.4.4.21-5-3 failing +15.4.4.21-5-4 failing +15.4.4.21-5-5 failing +15.4.4.21-5-6 failing +15.4.4.21-5-7 failing +15.4.4.21-5-8 failing +15.4.4.21-7-1 failing +15.4.4.21-7-2 failing +15.4.4.21-7-3 failing +15.4.4.21-7-4 failing +15.4.4.21-7-5 failing +15.4.4.21-7-6 failing +15.4.4.21-7-7 failing +15.4.4.21-7-8 failing +15.4.4.21-7-9 failing +15.4.4.21-8-b-1 failing +15.4.4.21-8-b-2 failing +15.4.4.21-8-b-3 failing +15.4.4.21-8-b-ii-1 failing +15.4.4.21-8-b-ii-2 failing +15.4.4.21-8-b-iii-1-1 failing +15.4.4.21-8-b-iii-1-10 failing +15.4.4.21-8-b-iii-1-11 failing +15.4.4.21-8-b-iii-1-12 failing +15.4.4.21-8-b-iii-1-13 failing +15.4.4.21-8-b-iii-1-14 failing +15.4.4.21-8-b-iii-1-15 failing +15.4.4.21-8-b-iii-1-16 failing +15.4.4.21-8-b-iii-1-17 failing +15.4.4.21-8-b-iii-1-18 failing +15.4.4.21-8-b-iii-1-19 failing +15.4.4.21-8-b-iii-1-20 failing +15.4.4.21-8-b-iii-1-21 failing +15.4.4.21-8-b-iii-1-22 failing +15.4.4.21-8-b-iii-1-23 failing +15.4.4.21-8-b-iii-1-25 failing +15.4.4.21-8-b-iii-1-26 failing +15.4.4.21-8-b-iii-1-27 failing +15.4.4.21-8-b-iii-1-28 failing +15.4.4.21-8-b-iii-1-29 failing +15.4.4.21-8-b-iii-1-3 failing +15.4.4.21-8-b-iii-1-30 failing +15.4.4.21-8-b-iii-1-31 failing +15.4.4.21-8-b-iii-1-32 failing +15.4.4.21-8-b-iii-1-33 failing +15.4.4.21-8-b-iii-1-4 failing +15.4.4.21-8-b-iii-1-5 failing +15.4.4.21-8-b-iii-1-6 failing +15.4.4.21-8-b-iii-1-7 failing +15.4.4.21-8-b-iii-1-8 failing +15.4.4.21-8-b-iii-1-9 failing +15.4.4.21-8-c-1 failing +15.4.4.21-8-c-2 failing +15.4.4.21-8-c-3 failing +15.4.4.21-8-c-4 failing +15.4.4.21-8-c-5 failing +15.4.4.21-8-c-6 failing +15.4.4.21-8-c-7 failing +15.4.4.21-8-c-8 failing +15.4.4.21-9-1 failing +15.4.4.21-9-10 failing +15.4.4.21-9-4 failing +15.4.4.21-9-6 failing +15.4.4.21-9-8 failing +15.4.4.21-9-9 failing +15.4.4.21-9-b-10 failing +15.4.4.21-9-b-11 failing +15.4.4.21-9-b-12 failing +15.4.4.21-9-b-13 failing +15.4.4.21-9-b-15 failing +15.4.4.21-9-b-16 failing +15.4.4.21-9-b-17 failing +15.4.4.21-9-b-18 failing +15.4.4.21-9-b-19 failing +15.4.4.21-9-b-2 failing +15.4.4.21-9-b-20 failing +15.4.4.21-9-b-21 failing +15.4.4.21-9-b-23 failing +15.4.4.21-9-b-24 failing +15.4.4.21-9-b-25 failing +15.4.4.21-9-b-26 failing +15.4.4.21-9-b-27 failing +15.4.4.21-9-b-28 failing +15.4.4.21-9-b-29 failing +15.4.4.21-9-b-3 failing +15.4.4.21-9-b-4 failing +15.4.4.21-9-b-5 failing +15.4.4.21-9-b-6 failing +15.4.4.21-9-b-7 failing +15.4.4.21-9-b-8 failing +15.4.4.21-9-b-9 failing +15.4.4.21-9-c-1 failing +15.4.4.21-9-c-i-1 failing +15.4.4.21-9-c-i-10 failing +15.4.4.21-9-c-i-11 failing +15.4.4.21-9-c-i-12 failing +15.4.4.21-9-c-i-13 failing +15.4.4.21-9-c-i-14 failing +15.4.4.21-9-c-i-15 failing +15.4.4.21-9-c-i-16 failing +15.4.4.21-9-c-i-17 failing +15.4.4.21-9-c-i-18 failing +15.4.4.21-9-c-i-19 failing +15.4.4.21-9-c-i-2 failing +15.4.4.21-9-c-i-20 failing +15.4.4.21-9-c-i-21 failing +15.4.4.21-9-c-i-22 failing +15.4.4.21-9-c-i-23 failing +15.4.4.21-9-c-i-25 failing +15.4.4.21-9-c-i-26 failing +15.4.4.21-9-c-i-27 failing +15.4.4.21-9-c-i-28 failing +15.4.4.21-9-c-i-29 failing +15.4.4.21-9-c-i-3 failing +15.4.4.21-9-c-i-30 failing +15.4.4.21-9-c-i-31 failing +15.4.4.21-9-c-i-32 failing +15.4.4.21-9-c-i-33 failing +15.4.4.21-9-c-i-4 failing +15.4.4.21-9-c-i-5 failing +15.4.4.21-9-c-i-6 failing +15.4.4.21-9-c-i-7 failing +15.4.4.21-9-c-i-8 failing +15.4.4.21-9-c-i-9 failing +15.4.4.21-9-c-ii-12 failing +15.4.4.21-9-c-ii-14 failing +15.4.4.21-9-c-ii-16 failing +15.4.4.21-9-c-ii-18 failing +15.4.4.21-9-c-ii-20 failing +15.4.4.21-9-c-ii-21 failing +15.4.4.21-9-c-ii-22 failing +15.4.4.21-9-c-ii-23 failing +15.4.4.21-9-c-ii-24 failing +15.4.4.21-9-c-ii-25 failing +15.4.4.21-9-c-ii-26 failing +15.4.4.21-9-c-ii-27 failing +15.4.4.21-9-c-ii-28 failing +15.4.4.21-9-c-ii-29 failing +15.4.4.21-9-c-ii-30 failing +15.4.4.21-9-c-ii-31 failing +15.4.4.21-9-c-ii-32 failing +15.4.4.21-9-c-ii-33 failing +15.4.4.21-9-c-ii-34 failing +15.4.4.21-9-c-ii-35 failing +15.4.4.21-9-c-ii-37 failing +15.4.4.21-9-c-ii-4 failing +15.4.4.21-9-c-ii-5 failing +15.4.4.21-9-c-ii-7 failing +15.4.4.21-9-c-ii-8 failing +15.4.4.21-9-c-ii-9 failing +15.4.4.22-0-2 failing +15.4.4.22-1-1 failing +15.4.4.22-1-10 failing +15.4.4.22-1-11 failing +15.4.4.22-1-12 failing +15.4.4.22-1-13 failing +15.4.4.22-1-14 failing +15.4.4.22-1-15 failing +15.4.4.22-1-2 failing +15.4.4.22-1-3 failing +15.4.4.22-1-4 failing +15.4.4.22-1-5 failing +15.4.4.22-1-6 failing +15.4.4.22-1-7 failing +15.4.4.22-1-8 failing +15.4.4.22-1-9 failing +15.4.4.22-10-3 failing +15.4.4.22-10-4 failing +15.4.4.22-10-6 failing +15.4.4.22-10-7 failing +15.4.4.22-10-8 failing +15.4.4.22-2-1 failing +15.4.4.22-2-10 failing +15.4.4.22-2-11 failing +15.4.4.22-2-12 failing +15.4.4.22-2-13 failing +15.4.4.22-2-14 failing +15.4.4.22-2-15 failing +15.4.4.22-2-17 failing +15.4.4.22-2-18 failing +15.4.4.22-2-19 failing +15.4.4.22-2-3 failing +15.4.4.22-2-4 failing +15.4.4.22-2-5 failing +15.4.4.22-2-6 failing +15.4.4.22-2-7 failing +15.4.4.22-2-8 failing +15.4.4.22-2-9 failing +15.4.4.22-3-1 failing +15.4.4.22-3-10 failing +15.4.4.22-3-11 failing +15.4.4.22-3-12 failing +15.4.4.22-3-13 failing +15.4.4.22-3-14 failing +15.4.4.22-3-15 failing +15.4.4.22-3-16 failing +15.4.4.22-3-17 failing +15.4.4.22-3-18 failing +15.4.4.22-3-19 failing +15.4.4.22-3-2 failing +15.4.4.22-3-20 failing +15.4.4.22-3-21 failing +15.4.4.22-3-22 failing +15.4.4.22-3-23 failing +15.4.4.22-3-24 failing +15.4.4.22-3-25 failing +15.4.4.22-3-28 failing +15.4.4.22-3-29 failing +15.4.4.22-3-3 failing +15.4.4.22-3-4 failing +15.4.4.22-3-5 failing +15.4.4.22-3-6 failing +15.4.4.22-3-7 failing +15.4.4.22-3-8 failing +15.4.4.22-3-9 failing +15.4.4.22-4-1 failing +15.4.4.22-4-10 failing +15.4.4.22-4-11 failing +15.4.4.22-4-15 failing +15.4.4.22-4-2 failing +15.4.4.22-4-3 failing +15.4.4.22-4-4 failing +15.4.4.22-4-5 failing +15.4.4.22-4-6 failing +15.4.4.22-4-7 failing +15.4.4.22-4-8 failing +15.4.4.22-4-9 failing +15.4.4.22-5-1 failing +15.4.4.22-5-10 failing +15.4.4.22-5-11 failing +15.4.4.22-5-12 failing +15.4.4.22-5-13 failing +15.4.4.22-5-2 failing +15.4.4.22-5-3 failing +15.4.4.22-5-4 failing +15.4.4.22-5-5 failing +15.4.4.22-5-6 failing +15.4.4.22-5-7 failing +15.4.4.22-5-8 failing +15.4.4.22-7-1 failing +15.4.4.22-7-2 failing +15.4.4.22-7-3 failing +15.4.4.22-7-4 failing +15.4.4.22-7-5 failing +15.4.4.22-7-6 failing +15.4.4.22-7-7 failing +15.4.4.22-7-8 failing +15.4.4.22-7-9 failing +15.4.4.22-8-b-1 failing +15.4.4.22-8-b-2 failing +15.4.4.22-8-b-3 failing +15.4.4.22-8-b-ii-1 failing +15.4.4.22-8-b-ii-2 failing +15.4.4.22-8-b-iii-1-1 failing +15.4.4.22-8-b-iii-1-10 failing +15.4.4.22-8-b-iii-1-11 failing +15.4.4.22-8-b-iii-1-12 failing +15.4.4.22-8-b-iii-1-13 failing +15.4.4.22-8-b-iii-1-14 failing +15.4.4.22-8-b-iii-1-15 failing +15.4.4.22-8-b-iii-1-16 failing +15.4.4.22-8-b-iii-1-17 failing +15.4.4.22-8-b-iii-1-18 failing +15.4.4.22-8-b-iii-1-19 failing +15.4.4.22-8-b-iii-1-20 failing +15.4.4.22-8-b-iii-1-21 failing +15.4.4.22-8-b-iii-1-22 failing +15.4.4.22-8-b-iii-1-23 failing +15.4.4.22-8-b-iii-1-25 failing +15.4.4.22-8-b-iii-1-26 failing +15.4.4.22-8-b-iii-1-27 failing +15.4.4.22-8-b-iii-1-28 failing +15.4.4.22-8-b-iii-1-29 failing +15.4.4.22-8-b-iii-1-3 failing +15.4.4.22-8-b-iii-1-30 failing +15.4.4.22-8-b-iii-1-31 failing +15.4.4.22-8-b-iii-1-32 failing +15.4.4.22-8-b-iii-1-33 failing +15.4.4.22-8-b-iii-1-4 failing +15.4.4.22-8-b-iii-1-5 failing +15.4.4.22-8-b-iii-1-6 failing +15.4.4.22-8-b-iii-1-7 failing +15.4.4.22-8-b-iii-1-8 failing +15.4.4.22-8-b-iii-1-9 failing +15.4.4.22-8-c-1 failing +15.4.4.22-8-c-2 failing +15.4.4.22-8-c-3 failing +15.4.4.22-8-c-4 failing +15.4.4.22-8-c-5 failing +15.4.4.22-8-c-6 failing +15.4.4.22-8-c-7 failing +15.4.4.22-8-c-8 failing +15.4.4.22-9-4 failing +15.4.4.22-9-6 failing +15.4.4.22-9-8 failing +15.4.4.22-9-9 failing +15.4.4.22-9-b-10 failing +15.4.4.22-9-b-11 failing +15.4.4.22-9-b-12 failing +15.4.4.22-9-b-13 failing +15.4.4.22-9-b-14 failing +15.4.4.22-9-b-15 failing +15.4.4.22-9-b-16 failing +15.4.4.22-9-b-17 failing +15.4.4.22-9-b-18 failing +15.4.4.22-9-b-19 failing +15.4.4.22-9-b-2 failing +15.4.4.22-9-b-20 failing +15.4.4.22-9-b-21 failing +15.4.4.22-9-b-23 failing +15.4.4.22-9-b-24 failing +15.4.4.22-9-b-25 failing +15.4.4.22-9-b-26 failing +15.4.4.22-9-b-28 failing +15.4.4.22-9-b-29 failing +15.4.4.22-9-b-3 failing +15.4.4.22-9-b-4 failing +15.4.4.22-9-b-5 failing +15.4.4.22-9-b-6 failing +15.4.4.22-9-b-7 failing +15.4.4.22-9-b-8 failing +15.4.4.22-9-b-9 failing +15.4.4.22-9-c-1 failing +15.4.4.22-9-c-i-1 failing +15.4.4.22-9-c-i-10 failing +15.4.4.22-9-c-i-11 failing +15.4.4.22-9-c-i-12 failing +15.4.4.22-9-c-i-13 failing +15.4.4.22-9-c-i-14 failing +15.4.4.22-9-c-i-15 failing +15.4.4.22-9-c-i-16 failing +15.4.4.22-9-c-i-17 failing +15.4.4.22-9-c-i-18 failing +15.4.4.22-9-c-i-19 failing +15.4.4.22-9-c-i-2 failing +15.4.4.22-9-c-i-20 failing +15.4.4.22-9-c-i-21 failing +15.4.4.22-9-c-i-22 failing +15.4.4.22-9-c-i-23 failing +15.4.4.22-9-c-i-25 failing +15.4.4.22-9-c-i-26 failing +15.4.4.22-9-c-i-27 failing +15.4.4.22-9-c-i-28 failing +15.4.4.22-9-c-i-29 failing +15.4.4.22-9-c-i-3 failing +15.4.4.22-9-c-i-30 failing +15.4.4.22-9-c-i-31 failing +15.4.4.22-9-c-i-32 failing +15.4.4.22-9-c-i-33 failing +15.4.4.22-9-c-i-4 failing +15.4.4.22-9-c-i-5 failing +15.4.4.22-9-c-i-6 failing +15.4.4.22-9-c-i-7 failing +15.4.4.22-9-c-i-8 failing +15.4.4.22-9-c-i-9 failing +15.4.4.22-9-c-ii-12 failing +15.4.4.22-9-c-ii-14 failing +15.4.4.22-9-c-ii-16 failing +15.4.4.22-9-c-ii-20 failing +15.4.4.22-9-c-ii-21 failing +15.4.4.22-9-c-ii-22 failing +15.4.4.22-9-c-ii-23 failing +15.4.4.22-9-c-ii-24 failing +15.4.4.22-9-c-ii-25 failing +15.4.4.22-9-c-ii-26 failing +15.4.4.22-9-c-ii-27 failing +15.4.4.22-9-c-ii-28 failing +15.4.4.22-9-c-ii-29 failing +15.4.4.22-9-c-ii-30 failing +15.4.4.22-9-c-ii-31 failing +15.4.4.22-9-c-ii-32 failing +15.4.4.22-9-c-ii-33 failing +15.4.4.22-9-c-ii-34 failing +15.4.4.22-9-c-ii-35 failing +15.4.4.22-9-c-ii-37 failing +15.4.4.22-9-c-ii-4 failing +15.4.4.22-9-c-ii-5 failing +15.4.4.22-9-c-ii-7 failing +15.4.4.22-9-c-ii-8 failing +S15.4.4.3_A1_T1 failing +S15.4.4.3_A3_T1 failing +S15.4.4.3_A4.2 failing +S15.4.4.3_A4.3 failing +S15.4.4.3_A4.4 failing +S15.4.4.3_A4.7 failing +15.4.4.4-5-b-iii-3-b-1 failing +15.4.4.4-5-c-i-1 failing +S15.4.4.4_A1_T4 failing +S15.4.4.4_A2_T1 failing +S15.4.4.4_A2_T2 failing +S15.4.4.4_A3_T1 failing +S15.4.4.4_A4.1 failing +S15.4.4.4_A4.2 failing +S15.4.4.4_A4.3 failing +S15.4.4.4_A4.4 failing +S15.4.4.4_A4.7 failing +S15.4.4.5_A1.1_T1 failing +S15.4.4.5_A2_T4 failing +S15.4.4.5_A3.1_T2 failing +S15.4.4.5_A3.2_T2 failing +S15.4.4.5_A5_T1 failing +S15.4.4.5_A6.1 failing +S15.4.4.5_A6.2 failing +S15.4.4.5_A6.3 failing +S15.4.4.5_A6.4 failing +S15.4.4.5_A6.7 failing +S15.4.4.6_A1.1_T1 failing +S15.4.4.6_A1.2_T1 failing +S15.4.4.6_A2_T4 failing +S15.4.4.6_A3_T2 failing +S15.4.4.6_A3_T3 failing +S15.4.4.6_A4_T1 failing +S15.4.4.6_A4_T2 failing +S15.4.4.6_A5.1 failing +S15.4.4.6_A5.2 failing +S15.4.4.6_A5.3 failing +S15.4.4.6_A5.4 failing +S15.4.4.6_A5.7 failing +S15.4.4.7_A2_T3 failing +S15.4.4.7_A3 failing +S15.4.4.7_A4_T2 failing +S15.4.4.7_A4_T3 failing +S15.4.4.7_A6.1 failing +S15.4.4.7_A6.2 failing +S15.4.4.7_A6.3 failing +S15.4.4.7_A6.4 failing +S15.4.4.7_A6.7 failing +S15.4.4.8_A1_T1 failing +S15.4.4.8_A1_T2 failing +S15.4.4.8_A2_T1 failing +S15.4.4.8_A2_T2 failing +S15.4.4.8_A2_T3 failing +S15.4.4.8_A3_T1 failing +S15.4.4.8_A3_T2 failing +S15.4.4.8_A3_T3 failing +S15.4.4.8_A4_T1 failing +S15.4.4.8_A4_T2 failing +S15.4.4.8_A5.1 failing +S15.4.4.8_A5.2 failing +S15.4.4.8_A5.3 failing +S15.4.4.8_A5.4 failing +S15.4.4.8_A5.7 failing +S15.4.4.9_A1.1_T1 failing +S15.4.4.9_A1.2_T1 failing +S15.4.4.9_A2_T1 failing +S15.4.4.9_A2_T2 failing +S15.4.4.9_A2_T3 failing +S15.4.4.9_A2_T4 failing +S15.4.4.9_A2_T5 failing +S15.4.4.9_A3_T1 failing +S15.4.4.9_A3_T2 failing +S15.4.4.9_A3_T3 failing +S15.4.4.9_A4_T1 failing +S15.4.4.9_A4_T2 failing +S15.4.4.9_A5.1 failing +S15.4.4.9_A5.2 failing +S15.4.4.9_A5.3 failing +S15.4.4.9_A5.4 failing +S15.4.4.9_A5.7 failing +15.4.5.1-3.d-1 failing +15.4.5.1-3.d-2 failing +15.4.5.1-3.d-3 failing +15.4.5.1-5-1 failing +15.4.5.1-5-2 failing +S15.4.5.1_A1.1_T1 failing +S15.4.5.1_A1.1_T2 failing +S15.4.5.1_A1.2_T1 failing +S15.4.5.1_A1.2_T2 failing +S15.4.5.1_A1.2_T3 failing +S15.4.5.1_A1.3_T1 failing +S15.4.5.1_A1.3_T2 failing +S15.4.5.1_A2.1_T1 failing +S15.4.5.2_A1_T2 failing +S15.4.5.2_A3_T1 failing +S15.4.5.2_A3_T2 failing +S15.4.5.2_A3_T3 failing +S15.5.2.1_A1_T12 failing +S15.5.2.1_A1_T13 failing +S15.5.3.1_A2 failing +S15.5.3.1_A3 failing +S15.5.3.1_A4 failing +S15.5.3.2_A1 failing +S15.5.3.2_A4 failing +S15.5.3_A1 failing +S15.5.4.2_A2_T1 failing +S15.5.4.2_A2_T2 failing +S15.5.4.2_A4_T1 failing +S15.5.4.3_A2_T1 failing +S15.5.4.3_A2_T2 failing +S15.5.4.10_A10 failing +S15.5.4.10_A11 failing +S15.5.4.10_A1_T1 failing +S15.5.4.10_A1_T10 failing +S15.5.4.10_A1_T11 failing +S15.5.4.10_A1_T12 failing +S15.5.4.10_A1_T13 failing +S15.5.4.10_A1_T14 failing +S15.5.4.10_A1_T2 failing +S15.5.4.10_A1_T3 failing +S15.5.4.10_A1_T4 failing +S15.5.4.10_A1_T5 failing +S15.5.4.10_A1_T6 failing +S15.5.4.10_A1_T7 failing +S15.5.4.10_A1_T8 failing +S15.5.4.10_A1_T9 failing +S15.5.4.10_A2_T1 failing +S15.5.4.10_A2_T10 failing +S15.5.4.10_A2_T11 failing +S15.5.4.10_A2_T12 failing +S15.5.4.10_A2_T13 failing +S15.5.4.10_A2_T14 failing +S15.5.4.10_A2_T15 failing +S15.5.4.10_A2_T16 failing +S15.5.4.10_A2_T17 failing +S15.5.4.10_A2_T18 failing +S15.5.4.10_A2_T2 failing +S15.5.4.10_A2_T3 failing +S15.5.4.10_A2_T4 failing +S15.5.4.10_A2_T5 failing +S15.5.4.10_A2_T6 failing +S15.5.4.10_A2_T7 failing +S15.5.4.10_A2_T8 failing +S15.5.4.10_A2_T9 failing +S15.5.4.10_A7 failing +S15.5.4.10_A8 failing +S15.5.4.10_A9 failing +15.5.4.11-1 failing +S15.5.4.11_A10 failing +S15.5.4.11_A11 failing +S15.5.4.11_A12 failing +S15.5.4.11_A1_T1 failing +S15.5.4.11_A1_T10 failing +S15.5.4.11_A1_T11 failing +S15.5.4.11_A1_T12 failing +S15.5.4.11_A1_T13 failing +S15.5.4.11_A1_T14 failing +S15.5.4.11_A1_T15 failing +S15.5.4.11_A1_T16 failing +S15.5.4.11_A1_T17 failing +S15.5.4.11_A1_T2 failing +S15.5.4.11_A1_T4 failing +S15.5.4.11_A1_T5 failing +S15.5.4.11_A1_T6 failing +S15.5.4.11_A1_T7 failing +S15.5.4.11_A1_T8 failing +S15.5.4.11_A1_T9 failing +S15.5.4.11_A2_T1 failing +S15.5.4.11_A2_T10 failing +S15.5.4.11_A2_T2 failing +S15.5.4.11_A2_T3 failing +S15.5.4.11_A2_T4 failing +S15.5.4.11_A2_T5 failing +S15.5.4.11_A2_T6 failing +S15.5.4.11_A2_T7 failing +S15.5.4.11_A2_T8 failing +S15.5.4.11_A2_T9 failing +S15.5.4.11_A3_T1 failing +S15.5.4.11_A3_T2 failing +S15.5.4.11_A3_T3 failing +S15.5.4.11_A4_T1 failing +S15.5.4.11_A4_T2 failing +S15.5.4.11_A4_T3 failing +S15.5.4.11_A4_T4 failing +S15.5.4.11_A5_T1 failing +S15.5.4.11_A7 failing +S15.5.4.11_A8 failing +S15.5.4.11_A9 failing +S15.5.4.12_A1.1_T1 failing +S15.5.4.12_A10 failing +S15.5.4.12_A11 failing +S15.5.4.12_A1_T1 failing +S15.5.4.12_A1_T10 failing +S15.5.4.12_A1_T11 failing +S15.5.4.12_A1_T12 failing +S15.5.4.12_A1_T13 failing +S15.5.4.12_A1_T14 failing +S15.5.4.12_A1_T2 failing +S15.5.4.12_A1_T4 failing +S15.5.4.12_A1_T5 failing +S15.5.4.12_A1_T6 failing +S15.5.4.12_A1_T7 failing +S15.5.4.12_A1_T8 failing +S15.5.4.12_A1_T9 failing +S15.5.4.12_A2_T1 failing +S15.5.4.12_A2_T2 failing +S15.5.4.12_A2_T3 failing +S15.5.4.12_A2_T4 failing +S15.5.4.12_A2_T5 failing +S15.5.4.12_A2_T6 failing +S15.5.4.12_A2_T7 failing +S15.5.4.12_A3_T1 failing +S15.5.4.12_A3_T2 failing +S15.5.4.12_A7 failing +S15.5.4.12_A8 failing +S15.5.4.12_A9 failing +S15.5.4.13_A10 failing +S15.5.4.13_A11 failing +S15.5.4.13_A1_T1 failing +S15.5.4.13_A1_T10 failing +S15.5.4.13_A1_T11 failing +S15.5.4.13_A1_T12 failing +S15.5.4.13_A1_T13 failing +S15.5.4.13_A1_T15 failing +S15.5.4.13_A1_T2 failing +S15.5.4.13_A1_T5 failing +S15.5.4.13_A2_T2 failing +S15.5.4.13_A2_T7 failing +S15.5.4.13_A3_T1 failing +S15.5.4.13_A3_T2 failing +S15.5.4.13_A3_T3 failing +S15.5.4.13_A3_T4 failing +S15.5.4.13_A7 failing +S15.5.4.13_A8 failing +S15.5.4.13_A9 failing +S15.5.4.14_A10 failing +S15.5.4.14_A11 failing +S15.5.4.14_A1_T1 failing +S15.5.4.14_A1_T10 failing +S15.5.4.14_A1_T11 failing +S15.5.4.14_A1_T12 failing +S15.5.4.14_A1_T13 failing +S15.5.4.14_A1_T14 failing +S15.5.4.14_A1_T15 failing +S15.5.4.14_A1_T16 failing +S15.5.4.14_A1_T17 failing +S15.5.4.14_A1_T18 failing +S15.5.4.14_A1_T2 failing +S15.5.4.14_A1_T3 failing +S15.5.4.14_A1_T4 failing +S15.5.4.14_A1_T5 failing +S15.5.4.14_A1_T6 failing +S15.5.4.14_A1_T7 failing +S15.5.4.14_A1_T8 failing +S15.5.4.14_A1_T9 failing +S15.5.4.14_A2_T1 failing +S15.5.4.14_A2_T10 failing +S15.5.4.14_A2_T11 failing +S15.5.4.14_A2_T12 failing +S15.5.4.14_A2_T13 failing +S15.5.4.14_A2_T14 failing +S15.5.4.14_A2_T15 failing +S15.5.4.14_A2_T16 failing +S15.5.4.14_A2_T17 failing +S15.5.4.14_A2_T18 failing +S15.5.4.14_A2_T19 failing +S15.5.4.14_A2_T2 failing +S15.5.4.14_A2_T20 failing +S15.5.4.14_A2_T21 failing +S15.5.4.14_A2_T22 failing +S15.5.4.14_A2_T23 failing +S15.5.4.14_A2_T24 failing +S15.5.4.14_A2_T25 failing +S15.5.4.14_A2_T26 failing +S15.5.4.14_A2_T27 failing +S15.5.4.14_A2_T28 failing +S15.5.4.14_A2_T29 failing +S15.5.4.14_A2_T3 failing +S15.5.4.14_A2_T30 failing +S15.5.4.14_A2_T31 failing +S15.5.4.14_A2_T32 failing +S15.5.4.14_A2_T33 failing +S15.5.4.14_A2_T34 failing +S15.5.4.14_A2_T35 failing +S15.5.4.14_A2_T36 failing +S15.5.4.14_A2_T37 failing +S15.5.4.14_A2_T38 failing +S15.5.4.14_A2_T39 failing +S15.5.4.14_A2_T4 failing +S15.5.4.14_A2_T40 failing +S15.5.4.14_A2_T41 failing +S15.5.4.14_A2_T42 failing +S15.5.4.14_A2_T43 failing +S15.5.4.14_A2_T5 failing +S15.5.4.14_A2_T6 failing +S15.5.4.14_A2_T7 failing +S15.5.4.14_A2_T8 failing +S15.5.4.14_A2_T9 failing +S15.5.4.14_A3_T1 failing +S15.5.4.14_A3_T10 failing +S15.5.4.14_A3_T11 failing +S15.5.4.14_A3_T2 failing +S15.5.4.14_A3_T3 failing +S15.5.4.14_A3_T4 failing +S15.5.4.14_A3_T5 failing +S15.5.4.14_A3_T6 failing +S15.5.4.14_A3_T7 failing +S15.5.4.14_A3_T8 failing +S15.5.4.14_A3_T9 failing +S15.5.4.14_A4_T1 failing +S15.5.4.14_A4_T10 failing +S15.5.4.14_A4_T11 failing +S15.5.4.14_A4_T12 failing +S15.5.4.14_A4_T13 failing +S15.5.4.14_A4_T14 failing +S15.5.4.14_A4_T15 failing +S15.5.4.14_A4_T16 failing +S15.5.4.14_A4_T17 failing +S15.5.4.14_A4_T18 failing +S15.5.4.14_A4_T19 failing +S15.5.4.14_A4_T2 failing +S15.5.4.14_A4_T20 failing +S15.5.4.14_A4_T21 failing +S15.5.4.14_A4_T22 failing +S15.5.4.14_A4_T23 failing +S15.5.4.14_A4_T24 failing +S15.5.4.14_A4_T25 failing +S15.5.4.14_A4_T3 failing +S15.5.4.14_A4_T4 failing +S15.5.4.14_A4_T5 failing +S15.5.4.14_A4_T6 failing +S15.5.4.14_A4_T7 failing +S15.5.4.14_A4_T8 failing +S15.5.4.14_A4_T9 failing +S15.5.4.14_A7 failing +S15.5.4.14_A8 failing +S15.5.4.14_A9 failing +S15.5.4.15_A10 failing +S15.5.4.15_A11 failing +S15.5.4.15_A1_T1 failing +S15.5.4.15_A1_T10 failing +S15.5.4.15_A1_T11 failing +S15.5.4.15_A1_T12 failing +S15.5.4.15_A1_T13 failing +S15.5.4.15_A1_T15 failing +S15.5.4.15_A1_T2 failing +S15.5.4.15_A1_T5 failing +S15.5.4.15_A1_T7 failing +S15.5.4.15_A1_T8 failing +S15.5.4.15_A1_T9 failing +S15.5.4.15_A2_T8 failing +S15.5.4.15_A3_T1 failing +S15.5.4.15_A3_T10 failing +S15.5.4.15_A3_T11 failing +S15.5.4.15_A3_T2 failing +S15.5.4.15_A3_T3 failing +S15.5.4.15_A3_T4 failing +S15.5.4.15_A3_T5 failing +S15.5.4.15_A3_T6 failing +S15.5.4.15_A3_T7 failing +S15.5.4.15_A3_T8 failing +S15.5.4.15_A3_T9 failing +S15.5.4.15_A7 failing +S15.5.4.15_A8 failing +S15.5.4.15_A9 failing +S15.5.4.16_A10 failing +S15.5.4.16_A11 failing +S15.5.4.16_A1_T1 failing +S15.5.4.16_A1_T10 failing +S15.5.4.16_A1_T11 failing +S15.5.4.16_A1_T12 failing +S15.5.4.16_A1_T13 failing +S15.5.4.16_A1_T14 failing +S15.5.4.16_A1_T2 failing +S15.5.4.16_A1_T6 failing +S15.5.4.16_A1_T7 failing +S15.5.4.16_A1_T8 failing +S15.5.4.16_A7 failing +S15.5.4.16_A8 failing +S15.5.4.16_A9 failing +S15.5.4.17_A10 failing +S15.5.4.17_A11 failing +S15.5.4.17_A1_T1 failing +S15.5.4.17_A1_T10 failing +S15.5.4.17_A1_T11 failing +S15.5.4.17_A1_T12 failing +S15.5.4.17_A1_T13 failing +S15.5.4.17_A1_T14 failing +S15.5.4.17_A1_T2 failing +S15.5.4.17_A1_T6 failing +S15.5.4.17_A1_T7 failing +S15.5.4.17_A1_T8 failing +S15.5.4.17_A7 failing +S15.5.4.17_A8 failing +S15.5.4.17_A9 failing +S15.5.4.18_A10 failing +S15.5.4.18_A11 failing +S15.5.4.18_A1_T1 failing +S15.5.4.18_A1_T10 failing +S15.5.4.18_A1_T11 failing +S15.5.4.18_A1_T12 failing +S15.5.4.18_A1_T13 failing +S15.5.4.18_A1_T14 failing +S15.5.4.18_A1_T2 failing +S15.5.4.18_A1_T6 failing +S15.5.4.18_A1_T7 failing +S15.5.4.18_A1_T8 failing +S15.5.4.18_A7 failing +S15.5.4.18_A8 failing +S15.5.4.18_A9 failing +S15.5.4.19_A10 failing +S15.5.4.19_A11 failing +S15.5.4.19_A1_T1 failing +S15.5.4.19_A1_T10 failing +S15.5.4.19_A1_T11 failing +S15.5.4.19_A1_T12 failing +S15.5.4.19_A1_T13 failing +S15.5.4.19_A1_T14 failing +S15.5.4.19_A1_T2 failing +S15.5.4.19_A1_T6 failing +S15.5.4.19_A1_T7 failing +S15.5.4.19_A1_T8 failing +S15.5.4.19_A7 failing +S15.5.4.19_A8 failing +S15.5.4.19_A9 failing +15.5.4.20-0-1 failing +15.5.4.20-0-2 failing +15.5.4.20-1-1 failing +15.5.4.20-1-2 failing +15.5.4.20-1-3 failing +15.5.4.20-1-4 failing +15.5.4.20-1-5 failing +15.5.4.20-1-6 failing +15.5.4.20-1-7 failing +15.5.4.20-1-8 failing +15.5.4.20-1-9 failing +15.5.4.20-2-1 failing +15.5.4.20-2-10 failing +15.5.4.20-2-11 failing +15.5.4.20-2-12 failing +15.5.4.20-2-13 failing +15.5.4.20-2-14 failing +15.5.4.20-2-15 failing +15.5.4.20-2-16 failing +15.5.4.20-2-17 failing +15.5.4.20-2-18 failing +15.5.4.20-2-19 failing +15.5.4.20-2-2 failing +15.5.4.20-2-20 failing +15.5.4.20-2-21 failing +15.5.4.20-2-22 failing +15.5.4.20-2-23 failing +15.5.4.20-2-24 failing +15.5.4.20-2-25 failing +15.5.4.20-2-26 failing +15.5.4.20-2-27 failing +15.5.4.20-2-28 failing +15.5.4.20-2-29 failing +15.5.4.20-2-3 failing +15.5.4.20-2-30 failing +15.5.4.20-2-31 failing +15.5.4.20-2-32 failing +15.5.4.20-2-33 failing +15.5.4.20-2-34 failing +15.5.4.20-2-35 failing +15.5.4.20-2-36 failing +15.5.4.20-2-37 failing +15.5.4.20-2-38 failing +15.5.4.20-2-39 failing +15.5.4.20-2-4 failing +15.5.4.20-2-40 failing +15.5.4.20-2-41 failing +15.5.4.20-2-42 failing +15.5.4.20-2-43 failing +15.5.4.20-2-44 failing +15.5.4.20-2-45 failing +15.5.4.20-2-46 failing +15.5.4.20-2-47 failing +15.5.4.20-2-49 failing +15.5.4.20-2-5 failing +15.5.4.20-2-50 failing +15.5.4.20-2-51 failing +15.5.4.20-2-6 failing +15.5.4.20-2-7 failing +15.5.4.20-2-8 failing +15.5.4.20-2-9 failing +15.5.4.20-3-1 failing +15.5.4.20-3-10 failing +15.5.4.20-3-11 failing +15.5.4.20-3-12 failing +15.5.4.20-3-13 failing +15.5.4.20-3-14 failing +15.5.4.20-3-2 failing +15.5.4.20-3-3 failing +15.5.4.20-3-4 failing +15.5.4.20-3-5 failing +15.5.4.20-3-6 failing +15.5.4.20-3-7 failing +15.5.4.20-3-8 failing +15.5.4.20-3-9 failing +15.5.4.20-4-1 failing +15.5.4.20-4-10 failing +15.5.4.20-4-11 failing +15.5.4.20-4-12 failing +15.5.4.20-4-13 failing +15.5.4.20-4-14 failing +15.5.4.20-4-16 failing +15.5.4.20-4-18 failing +15.5.4.20-4-19 failing +15.5.4.20-4-2 failing +15.5.4.20-4-20 failing +15.5.4.20-4-21 failing +15.5.4.20-4-22 failing +15.5.4.20-4-24 failing +15.5.4.20-4-27 failing +15.5.4.20-4-28 failing +15.5.4.20-4-29 failing +15.5.4.20-4-3 failing +15.5.4.20-4-30 failing +15.5.4.20-4-32 failing +15.5.4.20-4-34 failing +15.5.4.20-4-35 failing +15.5.4.20-4-36 failing +15.5.4.20-4-37 failing +15.5.4.20-4-38 failing +15.5.4.20-4-39 failing +15.5.4.20-4-4 failing +15.5.4.20-4-40 failing +15.5.4.20-4-41 failing +15.5.4.20-4-42 failing +15.5.4.20-4-43 failing +15.5.4.20-4-44 failing +15.5.4.20-4-45 failing +15.5.4.20-4-46 failing +15.5.4.20-4-47 failing +15.5.4.20-4-48 failing +15.5.4.20-4-49 failing +15.5.4.20-4-5 failing +15.5.4.20-4-50 failing +15.5.4.20-4-51 failing +15.5.4.20-4-52 failing +15.5.4.20-4-53 failing +15.5.4.20-4-54 failing +15.5.4.20-4-55 failing +15.5.4.20-4-56 failing +15.5.4.20-4-57 failing +15.5.4.20-4-58 failing +15.5.4.20-4-59 failing +15.5.4.20-4-6 failing +15.5.4.20-4-60 failing +15.5.4.20-4-8 failing +S15.5.4.4_A1.1 failing +S15.5.4.4_A10 failing +S15.5.4.4_A11 failing +S15.5.4.4_A1_T1 failing +S15.5.4.4_A1_T10 failing +S15.5.4.4_A1_T2 failing +S15.5.4.4_A2 failing +S15.5.4.4_A5 failing +S15.5.4.4_A7 failing +S15.5.4.4_A8 failing +S15.5.4.4_A9 failing +S15.5.4.5_A1.1 failing +S15.5.4.5_A10 failing +S15.5.4.5_A11 failing +S15.5.4.5_A1_T1 failing +S15.5.4.5_A1_T10 failing +S15.5.4.5_A1_T2 failing +S15.5.4.5_A2 failing +S15.5.4.5_A4 failing +S15.5.4.5_A7 failing +S15.5.4.5_A8 failing +S15.5.4.5_A9 failing +S15.5.4.6_A10 failing +S15.5.4.6_A11 failing +S15.5.4.6_A1_T1 failing +S15.5.4.6_A1_T10 failing +S15.5.4.6_A1_T2 failing +S15.5.4.6_A2 failing +S15.5.4.6_A4_T1 failing +S15.5.4.6_A4_T2 failing +S15.5.4.6_A7 failing +S15.5.4.6_A8 failing +S15.5.4.6_A9 failing +S15.5.4.7_A10 failing +S15.5.4.7_A11 failing +S15.5.4.7_A1_T1 failing +S15.5.4.7_A1_T10 failing +S15.5.4.7_A1_T11 failing +S15.5.4.7_A1_T12 failing +S15.5.4.7_A1_T2 failing +S15.5.4.7_A4_T1 failing +S15.5.4.7_A4_T2 failing +S15.5.4.7_A4_T4 failing +S15.5.4.7_A4_T5 failing +S15.5.4.7_A7 failing +S15.5.4.7_A8 failing +S15.5.4.7_A9 failing +S15.5.4.8_A10 failing +S15.5.4.8_A11 failing +S15.5.4.8_A1_T1 failing +S15.5.4.8_A1_T10 failing +S15.5.4.8_A1_T12 failing +S15.5.4.8_A1_T2 failing +S15.5.4.8_A1_T4 failing +S15.5.4.8_A4_T1 failing +S15.5.4.8_A4_T2 failing +S15.5.4.8_A4_T4 failing +S15.5.4.8_A4_T5 failing +S15.5.4.8_A7 failing +S15.5.4.8_A8 failing +S15.5.4.8_A9 failing +S15.5.4.9_A10 failing +S15.5.4.9_A11 failing +S15.5.4.9_A7 failing +S15.5.4.9_A8 failing +S15.5.4.9_A9 failing +S15.5.5.1_A1 failing +S15.5.5.1_A2 failing +S15.5.5.1_A3 failing +S15.5.5.1_A4 failing +S15.5.5.1_A5 failing +S15.5.5_A1_T1 failing +S15.5.5_A1_T2 failing +S15.5.5_A2_T1 failing +S15.5.5_A2_T2 failing +15.5.5.5.2-3-6 failing +15.5.5.5.2-3-7 failing +15.5.5.5.2-7-4 failing +S15.6.3_A3 failing +S15.6.3.1_A2 failing +S15.6.3.1_A3 failing +S15.6.3.1_A4 failing +S15.6.4.2_A2_T1 failing +S15.6.4.2_A2_T2 failing +S15.6.4.2_A2_T3 failing +S15.6.4.2_A2_T4 failing +S15.6.4.2_A2_T5 failing +S15.6.4.3_A2_T1 failing +S15.6.4.3_A2_T2 failing +S15.6.4.3_A2_T3 failing +S15.6.4.3_A2_T4 failing +S15.6.4.3_A2_T5 failing +S15.7.3_A8 failing +15.7.3.1-1 failing +S15.7.3.1_A1_T1 failing +S15.7.3.1_A1_T2 failing +S15.7.3.1_A1_T3 failing +S15.7.3.2_A4 failing +S15.7.3.3_A4 failing +S15.7.3.4_A4 failing +S15.7.3.5_A4 failing +S15.7.3.6_A4 failing +S15.7.4_A3.3 failing +S15.7.4.2_A3_T01 failing +S15.7.4.2_A3_T02 failing +S15.7.4.2_A3_T03 failing +S15.7.4.2_A3_T04 failing +S15.7.4.2_A4_T01 failing +S15.7.4.2_A4_T02 failing +S15.7.4.2_A4_T03 failing +S15.7.4.2_A4_T04 failing +S15.7.4.2_A4_T05 failing +S15.7.4.4_A2_T01 failing +S15.7.4.4_A2_T02 failing +S15.7.4.4_A2_T03 failing +S15.7.4.4_A2_T04 failing +S15.7.4.4_A2_T05 failing +S15.7.4.5_A1.1_T01 failing +S15.7.4.5_A1.1_T02 failing +S15.7.4.5_A1.3_T01 failing +S15.7.4.5_A1.3_T02 failing +S15.7.4.5_A1.4_T01 failing +S15.7.4.5_A2_T01 failing +S15.8.2.11_A4 failing +S15.8.2.12_A4 failing +S15.8.2.16_A4 failing +S15.8.2.16_A5 failing +S15.8.2.17_A2 failing +S15.8.2.18_A4 failing +S15.8.2.18_A5 failing +S15.8.2.7_A4 failing +S15.8.2.7_A5 failing +15.9.1.15-1 failing +S15.9.2.1_A2 failing +S15.9.3.1_A4_T1 failing +S15.9.3.1_A4_T2 failing +S15.9.3.1_A4_T3 failing +S15.9.3.1_A4_T4 failing +S15.9.3.1_A4_T5 failing +S15.9.3.1_A4_T6 failing +S15.9.3.1_A5_T1 failing +S15.9.3.1_A5_T2 failing +S15.9.3.1_A5_T3 failing +S15.9.3.1_A5_T4 failing +S15.9.3.1_A5_T5 failing +S15.9.3.1_A5_T6 failing +S15.9.4_A5 failing +S15.9.4.1_A1_T1 failing +S15.9.4.1_A1_T2 failing +S15.9.4.1_A1_T3 failing +S15.9.4.2_A1_T3 failing +S15.9.4.2_A2_T1 failing +S15.9.4.2_A3_T1 failing +S15.9.4.2_A3_T2 failing +S15.9.4.3_A1_T3 failing +S15.9.4.3_A2_T1 failing +S15.9.4.3_A3_T1 failing +S15.9.4.3_A3_T2 failing +15.9.4.4-0-1 failing +15.9.4.4-0-2 failing +15.9.4.4-0-3 failing +15.9.4.4-0-4 failing +S15.9.5.1_A1_T3 failing +S15.9.5.1_A2_T1 failing +S15.9.5.1_A3_T1 failing +S15.9.5.1_A3_T2 failing +S15.9.5.10_A1_T3 failing +S15.9.5.10_A2_T1 failing +S15.9.5.10_A3_T1 failing +S15.9.5.10_A3_T2 failing +S15.9.5.11_A1_T3 failing +S15.9.5.11_A2_T1 failing +S15.9.5.11_A3_T1 failing +S15.9.5.11_A3_T2 failing +S15.9.5.12_A1_T3 failing +S15.9.5.12_A2_T1 failing +S15.9.5.12_A3_T1 failing +S15.9.5.12_A3_T2 failing +S15.9.5.13_A1_T3 failing +S15.9.5.13_A2_T1 failing +S15.9.5.13_A3_T1 failing +S15.9.5.13_A3_T2 failing +S15.9.5.14_A1_T3 failing +S15.9.5.14_A2_T1 failing +S15.9.5.14_A3_T1 failing +S15.9.5.14_A3_T2 failing +S15.9.5.15_A1_T3 failing +S15.9.5.15_A2_T1 failing +S15.9.5.15_A3_T1 failing +S15.9.5.15_A3_T2 failing +S15.9.5.16_A1_T3 failing +S15.9.5.16_A2_T1 failing +S15.9.5.16_A3_T1 failing +S15.9.5.16_A3_T2 failing +S15.9.5.17_A1_T3 failing +S15.9.5.17_A2_T1 failing +S15.9.5.17_A3_T1 failing +S15.9.5.17_A3_T2 failing +S15.9.5.18_A1_T3 failing +S15.9.5.18_A2_T1 failing +S15.9.5.18_A3_T1 failing +S15.9.5.18_A3_T2 failing +S15.9.5.19_A1_T3 failing +S15.9.5.19_A2_T1 failing +S15.9.5.19_A3_T1 failing +S15.9.5.19_A3_T2 failing +S15.9.5.2_A1_T3 failing +S15.9.5.2_A2_T1 failing +S15.9.5.2_A3_T1 failing +S15.9.5.2_A3_T2 failing +S15.9.5.20_A1_T3 failing +S15.9.5.20_A2_T1 failing +S15.9.5.20_A3_T1 failing +S15.9.5.20_A3_T2 failing +S15.9.5.21_A1_T3 failing +S15.9.5.21_A2_T1 failing +S15.9.5.21_A3_T1 failing +S15.9.5.21_A3_T2 failing +S15.9.5.22_A1_T3 failing +S15.9.5.22_A2_T1 failing +S15.9.5.22_A3_T1 failing +S15.9.5.22_A3_T2 failing +S15.9.5.23_A1_T3 failing +S15.9.5.23_A2_T1 failing +S15.9.5.23_A3_T1 failing +S15.9.5.23_A3_T2 failing +S15.9.5.24_A1_T3 failing +S15.9.5.24_A2_T1 failing +S15.9.5.24_A3_T1 failing +S15.9.5.24_A3_T2 failing +S15.9.5.25_A1_T3 failing +S15.9.5.25_A2_T1 failing +S15.9.5.25_A3_T1 failing +S15.9.5.25_A3_T2 failing +S15.9.5.26_A1_T3 failing +S15.9.5.26_A2_T1 failing +S15.9.5.26_A3_T1 failing +S15.9.5.26_A3_T2 failing +S15.9.5.27_A1_T3 failing +S15.9.5.27_A2_T1 failing +S15.9.5.27_A3_T1 failing +S15.9.5.27_A3_T2 failing +S15.9.5.28_A1_T3 failing +S15.9.5.28_A2_T1 failing +S15.9.5.28_A3_T1 failing +S15.9.5.28_A3_T2 failing +S15.9.5.29_A1_T3 failing +S15.9.5.29_A2_T1 failing +S15.9.5.29_A3_T1 failing +S15.9.5.29_A3_T2 failing +S15.9.5.3_A1_T3 failing +S15.9.5.3_A2_T1 failing +S15.9.5.3_A3_T1 failing +S15.9.5.3_A3_T2 failing +S15.9.5.30_A1_T3 failing +S15.9.5.30_A2_T1 failing +S15.9.5.30_A3_T1 failing +S15.9.5.30_A3_T2 failing +S15.9.5.31_A1_T3 failing +S15.9.5.31_A2_T1 failing +S15.9.5.31_A3_T1 failing +S15.9.5.31_A3_T2 failing +S15.9.5.32_A1_T3 failing +S15.9.5.32_A2_T1 failing +S15.9.5.32_A3_T1 failing +S15.9.5.32_A3_T2 failing +S15.9.5.33_A1_T3 failing +S15.9.5.33_A2_T1 failing +S15.9.5.33_A3_T1 failing +S15.9.5.33_A3_T2 failing +S15.9.5.34_A1_T3 failing +S15.9.5.34_A2_T1 failing +S15.9.5.34_A3_T1 failing +S15.9.5.34_A3_T2 failing +S15.9.5.35_A1_T3 failing +S15.9.5.35_A2_T1 failing +S15.9.5.35_A3_T1 failing +S15.9.5.35_A3_T2 failing +S15.9.5.36_A1_T3 failing +S15.9.5.36_A2_T1 failing +S15.9.5.36_A3_T1 failing +S15.9.5.36_A3_T2 failing +S15.9.5.37_A1_T3 failing +S15.9.5.37_A2_T1 failing +S15.9.5.37_A3_T1 failing +S15.9.5.37_A3_T2 failing +S15.9.5.38_A1_T3 failing +S15.9.5.38_A2_T1 failing +S15.9.5.38_A3_T1 failing +S15.9.5.38_A3_T2 failing +S15.9.5.39_A1_T3 failing +S15.9.5.39_A2_T1 failing +S15.9.5.39_A3_T1 failing +S15.9.5.39_A3_T2 failing +S15.9.5.4_A1_T3 failing +S15.9.5.4_A2_T1 failing +S15.9.5.4_A3_T1 failing +S15.9.5.4_A3_T2 failing +15.9.5.40_1 failing +S15.9.5.40_A1_T3 failing +S15.9.5.40_A2_T1 failing +S15.9.5.40_A3_T1 failing +S15.9.5.40_A3_T2 failing +S15.9.5.41_A1_T3 failing +S15.9.5.41_A2_T1 failing +S15.9.5.41_A3_T1 failing +S15.9.5.41_A3_T2 failing +S15.9.5.42_A1_T3 failing +S15.9.5.42_A2_T1 failing +S15.9.5.42_A3_T1 failing +S15.9.5.42_A3_T2 failing +15.9.5.43-0-10 failing +15.9.5.43-0-11 failing +15.9.5.43-0-12 failing +15.9.5.43-0-13 failing +15.9.5.43-0-14 failing +15.9.5.43-0-15 failing +15.9.5.43-0-16 failing +15.9.5.43-0-2 failing +15.9.5.43-0-3 failing +15.9.5.43-0-4 failing +15.9.5.43-0-5 failing +15.9.5.43-0-6 failing +15.9.5.43-0-7 failing +15.9.5.43-0-8 failing +15.9.5.43-0-9 failing +15.9.5.44-0-1 failing +15.9.5.44-0-2 failing +S15.9.5.5_A1_T3 failing +S15.9.5.5_A2_T1 failing +S15.9.5.5_A3_T1 failing +S15.9.5.5_A3_T2 failing +S15.9.5.6_A1_T3 failing +S15.9.5.6_A2_T1 failing +S15.9.5.6_A3_T1 failing +S15.9.5.6_A3_T2 failing +S15.9.5.7_A1_T3 failing +S15.9.5.7_A2_T1 failing +S15.9.5.7_A3_T1 failing +S15.9.5.7_A3_T2 failing +S15.9.5.8_A1_T3 failing +S15.9.5.8_A2_T1 failing +S15.9.5.8_A3_T1 failing +S15.9.5.8_A3_T2 failing +S15.9.5.9_A1_T3 failing +S15.9.5.9_A2_T1 failing +S15.9.5.9_A3_T1 failing +S15.9.5.9_A3_T2 failing +6.2.2_a failing +6.2.2_b failing +6.2.2_c failing +6.2.3 failing +6.2.4 failing +6.3.1_a failing +6.3.1_b failing +6.4_a failing +6.4_b failing +6.4_c failing +8.0_L15 failing +9.1_a failing +9.1_b failing +9.2.1_1 failing +9.2.1_2 failing +9.2.1_3 failing +9.2.1_4 failing +9.2.1_8_c_ii failing +9.2.1_8_c_vi failing +9.2.2 failing +9.2.3_5 failing +9.2.5_11_g_ii_2 failing +9.2.5_6 failing +9.2.6_2 failing +9.2.6_4 failing +9.2.6_4_b failing +9.2.6_4_c failing +9.2.8_1_c failing +9.2.8_4 failing +10.1.1_1 failing +10.1.1_10 failing +10.1.1_11 failing +10.1.1_13 failing +10.1.1_19_b failing +10.1.1_19_c failing +10.1.1_20 failing +10.1.1_23 failing +10.1.1_6 failing +10.1.2_a failing +10.1.3 failing +10.1_L15 failing +10.2.1 failing +10.2.2 failing +10.2.2_L15 failing +10.2.3_b failing +10.3.1 failing +10.3.2_1_a_L15 failing +10.3.2_1_c failing +10.3.2_CS_a failing +10.3.2_CS_b_NN failing +10.3.2_CS_c_NN failing +10.3.2_CS_d_NN failing +10.3.2_L15 failing +10.3.3 failing +10.3.3_L15 failing +10.3_L15 failing +10.3_a failing +10.3_b failing +10.4_a failing +11.1.1_1 failing +11.1.1_15 failing +11.1.1_17 failing +11.1.1_19 failing +11.1.1_20_c failing +11.1.1_21 failing +11.1.1_34 failing +11.1.1_6 failing +11.1.1_7 failing +11.1.2 failing +11.1.3 failing +11.1_L15 failing +11.2.1 failing +11.2.2 failing +11.2.2_L15 failing +11.2.3_b failing +11.3.1 failing +11.3.2_1_a_L15 failing +11.3.2_1_a_ii failing +11.3.2_1_c failing +11.3.2_FN_1 failing +11.3.2_FN_2 failing +11.3.2_FN_3_b failing +11.3.2_L15 failing +11.3.2_TRF failing +11.3.2_TRP failing +11.3.3 failing +11.3.3_L15 failing +11.3_L15 failing +11.3_a failing +11.3_b failing +11.4_a failing +12.1.1_1 failing +12.1.1_18 failing +12.1.1_22 failing +12.1.1_23 failing +12.1.1_25 failing +12.1.1_5 failing +12.1.1_6 failing +12.1.1_TDTO failing +12.1.2 failing +12.1.3 failing +12.1_L15 failing +12.2.1 failing +12.2.2 failing +12.2.2_L15 failing +12.2.3_b failing +12.2.3_c failing +12.3.1 failing +12.3.2_1_a_L15 failing +12.3.2_1_c failing +12.3.2_FDT_1 failing +12.3.2_FDT_7_a_iv failing +12.3.2_L15 failing +12.3.2_TLT_2 failing +12.3.3 failing +12.3.3_L15 failing +12.3_L15 failing +12.3_a failing +12.3_b failing +12.4_a failing +13.1.1_1 failing +13.1.1_2 failing +13.1.1_6 failing +13.1.1_7 failing +13.1.1_L15 failing +13.2.1_1 failing +13.2.1_4 failing +13.2.1_5 failing +13.2.1_L15 failing +13.3.0_1 failing +13.3.0_2 failing +13.3.0_6 failing +13.3.0_7 failing +13.3.1_L15 failing +13.3.2_L15 failing +13.3.3_L15 failing diff --git a/tests/test262.py b/tests/test262.py index 7c0aa2f74f..c003120849 100755 --- a/tests/test262.py +++ b/tests/test262.py @@ -43,11 +43,19 @@ class TestExpectations: def __init__(self): f = open(rootDir + "/TestExpectations") self.testsToSkip = [] + self.failingTests = [] for line in f.read().splitlines(): line = line.strip() if len(line) == 0 or line[0] == "#": continue - self.testsToSkip.append(line) + record = line.split() + if len(record) == 1: + self.testsToSkip.append(record) + else: + test = record[0] + expectation = record[1] + if expectation == "failing": + self.failingTests.append(test) f.close() if not os.path.exists(EXCLUDED_FILENAME): @@ -209,6 +217,12 @@ class TestCase(object): def GetPath(self): return self.name + def NegateResult(self): + if self.IsNegative(): + del self.testRecord['negative'] + else: + self.testRecord['negative'] = "Some failure"; + def IsNegative(self): return 'negative' in self.testRecord @@ -375,12 +389,16 @@ class TestSuite(object): else: if not self.non_strict_only: strict_case = TestCase(self, name, full_path, True) + if self.expectations.failingTests.count(basename) >= 1: + strict_case.NegateResult() if not strict_case.IsNoStrict(): if strict_case.IsOnlyStrict() or \ self.unmarked_default in ['both', 'strict']: cases.append(strict_case) if not self.strict_only: non_strict_case = TestCase(self, name, full_path, False) + if self.expectations.failingTests.count(basename) >= 1: + non_strict_case.NegateResult() if not non_strict_case.IsOnlyStrict(): if non_strict_case.IsNoStrict() or \ self.unmarked_default in ['both', 'non_strict']: -- cgit v1.2.3 From e00fdc489b76b840771405f69c76073b398e86cf Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 6 Jan 2013 22:47:47 +0100 Subject: Add initial support for parallel test execution Todo: Incremental test reporting while the tests are running Change-Id: I69048211b19b5b6b15cc32f65c8ae4a93f27861c Reviewed-by: Lars Knoll --- tests/test262.py | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/tests/test262.py b/tests/test262.py index c003120849..c0159f20ed 100755 --- a/tests/test262.py +++ b/tests/test262.py @@ -25,12 +25,27 @@ import datetime import shutil import json import stat +import multiprocessing +import signal from parseTestRecord import parseTestRecord, stripHeader from packagerConfig import * +# ############# Helpers needed for parallel multi-process test execution ############ + +def runTest(case, args): + return case.Run(args) + +def runTestVarArgs(args): + return runTest(*args) + +def initWorkerProcess(): + signal.signal(signal.SIGINT, signal.SIG_IGN) + +# ############# + class Test262Error(Exception): def __init__(self, message): self.message = message @@ -82,6 +97,8 @@ def BuildOptions(): help="Test only strict mode") result.add_option("--non_strict_only", default=False, action="store_true", help="Test only non-strict mode") + result.add_option("--parallel", default=False, action="store_true", + help="Run tests in parallel") # TODO: Once enough tests are made strict compat, change the default # to "both" result.add_option("--unmarked_default", default="non_strict", @@ -438,16 +455,23 @@ class TestSuite(object): print result.ReportOutcome(False) - def Run(self, command_template, tests, print_summary, full_summary): + def Run(self, command_template, tests, print_summary, full_summary, parallel): if not "{{path}}" in command_template: command_template += " {{path}}" cases = self.EnumerateTests(tests) if len(cases) == 0: ReportError("No tests to run") progress = ProgressIndicator(len(cases)) - for case in cases: - result = case.Run(command_template) - progress.HasRun(result) + + if parallel: + pool = multiprocessing.Pool(processes=multiprocessing.cpu_count(), initializer=initWorkerProcess) + results = pool.map_async(func=runTestVarArgs, iterable=[(case, command_template) for case in cases], chunksize=len(cases) / multiprocessing.cpu_count()).get(9999999) + for result in results: + progress.HasRun(result) + else: + for case in cases: + result = case.Run(command_template) + progress.HasRun(result) if print_summary: self.PrintSummary(progress) if full_summary: @@ -477,7 +501,8 @@ def Main(): else: test_suite.Run(options.command, args, options.summary or options.full_summary, - options.full_summary) + options.full_summary, + options.parallel) if __name__ == '__main__': -- cgit v1.2.3 From 7f151c69e50cd8f4001b89fed49c6cb4ea5d4b0d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 7 Jan 2013 08:51:52 +0100 Subject: Support progress reporting during parallel test runs Cut the test results into smaller chunks and allow python to process them in any order, so that we can process incoming results as they come in. Change-Id: I96f8d0173a63da2d460a6fcdcecae56137b4acb7 Reviewed-by: Lars Knoll --- tests/test262.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test262.py b/tests/test262.py index c0159f20ed..45bbdbe658 100755 --- a/tests/test262.py +++ b/tests/test262.py @@ -465,7 +465,7 @@ class TestSuite(object): if parallel: pool = multiprocessing.Pool(processes=multiprocessing.cpu_count(), initializer=initWorkerProcess) - results = pool.map_async(func=runTestVarArgs, iterable=[(case, command_template) for case in cases], chunksize=len(cases) / multiprocessing.cpu_count()).get(9999999) + results = pool.imap_unordered(func=runTestVarArgs, iterable=[(case, command_template) for case in cases], chunksize=multiprocessing.cpu_count() * 8) for result in results: progress.HasRun(result) else: -- cgit v1.2.3 From 9c8d24e8a73664ca9463a5d54095bdd29829f3b6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 7 Jan 2013 12:15:19 +0100 Subject: Speed up "make check" With make check run the tests in parallel. Change-Id: I6c9827a5d2ae8264c6d30ec39b0dbea2227ee3b3 Reviewed-by: Lars Knoll --- v4.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v4.pro b/v4.pro index 0c5c39fcd7..a4acfc456d 100644 --- a/v4.pro +++ b/v4.pro @@ -80,7 +80,7 @@ DEFINES += QMLJS_NO_LLVM } checktarget.target = check -checktarget.commands = python tests/test262.py +checktarget.commands = python tests/test262.py --parallel checktarget.depends = all QMAKE_EXTRA_TARGETS += checktarget -- cgit v1.2.3 From 977f00d739a20c74d2e70b5536b25831ce994828 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 7 Jan 2013 12:14:12 +0100 Subject: Fix parsing of skipped tests While adding support for running tests that are supposed to fail, I broke support for skipping tests altogether. After the split() on the line the result is of course a list, which in the case of a single "skip" entry needs to be dereferenced instead of appending a list to a list as a list item. Change-Id: Ifd27ee4f762ab092a7333b286ca187309cd631ab Reviewed-by: Lars Knoll --- tests/test262.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test262.py b/tests/test262.py index 45bbdbe658..a348dff808 100755 --- a/tests/test262.py +++ b/tests/test262.py @@ -65,7 +65,7 @@ class TestExpectations: continue record = line.split() if len(record) == 1: - self.testsToSkip.append(record) + self.testsToSkip.append(record[0]) else: test = record[0] expectation = record[1] -- cgit v1.2.3 From fb476cb5ddb15fcc0f5d1a6390ddab9e3310d4e8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 7 Jan 2013 12:10:46 +0100 Subject: Update test expectations Add a bunch of tests are that _passing_ but are really supposed to fail, for example tests that contain invalid syntax that's supposed to throw syntax errors instead. Those tests are tagged with @negative in the test case itself. Change-Id: I4756cc07f4a9f026af8047716fd79d2bcf25d1f1 Reviewed-by: Lars Knoll --- tests/TestExpectations | 2791 +++++++++++------------------------------------- 1 file changed, 597 insertions(+), 2194 deletions(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index cda18160cb..b7bec83b4a 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -20,217 +20,12 @@ S15.1.3.1_A2.5_T1 S15.1.3.2_A2.5_T1 # Tests failing that are supposed to pass. -Sbp_A10_T1 failing -Sbp_A10_T2 failing -7.3-10 failing -7.3-3 failing -7.3-4 failing -7.3-7 failing -7.3-8 failing -7.3-9 failing -S7.4_A5 failing -S7.4_A6 failing -7.6-1 failing -7.6-10 failing -7.6-11 failing -7.6-12 failing -7.6-13 failing -7.6-14 failing -7.6-15 failing -7.6-16 failing -7.6-17 failing -7.6-18 failing -7.6-19 failing -7.6-2 failing -7.6-20 failing -7.6-21 failing -7.6-22 failing -7.6-23 failing -7.6-24 failing -7.6-25 failing -7.6-26 failing -7.6-27 failing -7.6-28 failing -7.6-29 failing -7.6-3 failing -7.6-30 failing -7.6-31 failing -7.6-32 failing -7.6-33 failing -7.6-34 failing -7.6-35 failing -7.6-36 failing -7.6-4 failing -7.6-5 failing -7.6-6 failing -7.6-7 failing -7.6-8 failing -7.6-9 failing -S7.6_A2.1_T1 failing -S7.6_A2.1_T2 failing -S7.6_A2.1_T3 failing -S7.6_A2.1_T4 failing -7.6.1-4-1 failing -7.6.1-4-10 failing -7.6.1-4-11 failing -7.6.1-4-12 failing -7.6.1-4-13 failing -7.6.1-4-14 failing -7.6.1-4-15 failing -7.6.1-4-16 failing -7.6.1-4-2 failing -7.6.1-4-3 failing -7.6.1-4-4 failing -7.6.1-4-5 failing -7.6.1-4-6 failing -7.6.1-4-7 failing -7.6.1-4-8 failing -7.6.1-4-9 failing -7.6.1-8-1 failing -7.6.1-8-10 failing -7.6.1-8-11 failing -7.6.1-8-12 failing -7.6.1-8-13 failing -7.6.1-8-14 failing -7.6.1-8-15 failing -7.6.1-8-16 failing -7.6.1-8-2 failing -7.6.1-8-3 failing -7.6.1-8-4 failing -7.6.1-8-5 failing -7.6.1-8-6 failing -7.6.1-8-7 failing -7.6.1-8-8 failing -7.6.1-8-9 failing -7.6.1-17-s failing -7.6.1-18-s failing -7.6.1-19-s failing -7.6.1-20-s failing -7.6.1-21-s failing -7.6.1-22-s failing -7.6.1-23-s failing -7.6.1-24-s failing -7.6.1-25-s failing -7.6.1.2-1-s failing -7.6.1.2-2-s failing -7.6.1.2-3-s failing -7.6.1.2-4-s failing -7.6.1.2-5-s failing -7.6.1.2-6-s failing -7.6.1.2-7-s failing -7.6.1.2-8-s failing -7.6.1.2-9-s failing -7.8.3-1-s failing -7.8.3-2-s failing -7.8.3-3-s failing -7.8.3-4-s failing -7.8.3-5-s failing -7.8.3-6-s failing -7.8.3-7-s failing -7.8.4-1-s failing -7.8.4-10-s failing -7.8.4-11-s failing -7.8.4-12-s failing -7.8.4-13-s failing -7.8.4-14-s failing -7.8.4-15-s failing -7.8.4-16-s failing -7.8.4-17-s failing -7.8.4-18-s failing -7.8.4-19-s failing -7.8.4-2-s failing -7.8.4-20-s failing -7.8.4-21-s failing -7.8.4-22-s failing -7.8.4-23-s failing -7.8.4-24-s failing -7.8.4-25-s failing -7.8.4-26-s failing -7.8.4-27-s failing -7.8.4-28-s failing -7.8.4-29-s failing -7.8.4-3-s failing -7.8.4-30-s failing -7.8.4-31-s failing -7.8.4-32-s failing -7.8.4-33-s failing -7.8.4-4-s failing -7.8.4-5-s failing -7.8.4-6-s failing -7.8.4-7-s failing -7.8.4-8-s failing -7.8.4-9-s failing -7.8.5-1 failing -S7.8.5_A1.1_T2 failing -S7.8.5_A1.3_T2 failing -S7.8.5_A1.3_T4 failing -S7.8.5_A1.3_T5 failing -S7.8.5_A1.3_T6 failing -S7.8.5_A1.4_T2 failing -S7.8.5_A1.5_T2 failing -S7.8.5_A1.5_T4 failing -S7.8.5_A1.5_T5 failing -S7.8.5_A1.5_T6 failing -S7.8.5_A2.1_T2 failing -S7.8.5_A2.3_T2 failing -S7.8.5_A2.3_T4 failing -S7.8.5_A2.3_T5 failing -S7.8.5_A2.3_T6 failing -S7.8.5_A2.4_T2 failing -S7.8.5_A2.5_T2 failing -S7.8.5_A2.5_T4 failing -S7.8.5_A2.5_T5 failing -S7.8.5_A2.5_T6 failing S7.9_A5.8_T1 failing -8.12.1-1_20 failing -8.12.1-1_21 failing -8.12.1-1_22 failing -8.12.1-1_23 failing -8.12.1-1_24 failing -8.12.1-1_25 failing -8.14.4-8-b_2 failing -8.12.5-3-b_1 failing -8.12.5-3-b_2 failing -8.12.5-5-b_1 failing -S8.12.8_A1 failing -S8.12.8_A2 failing -S8.12.8_A3 failing -S8.12.8_A4 failing -S8.5_A2.1 failing -S8.5_A2.2 failing -S8.6.2_A8 failing -8.7.2-1-s failing -8.7.2-3-s failing -8.7.2-4-s failing -8.7.2-5-s failing S8.8_A2_T1 failing S8.8_A2_T2 failing S8.8_A2_T3 failing -S9.5_A3.1_T4 failing -S9.6_A3.1_T4 failing -S9.7_A3.1_T4 failing -S9.9_A1 failing -S9.9_A2 failing S10.1.1_A2_T1 failing S10.1.6_A1_T2 failing -10.1.1-11-s failing -10.1.1-14-s failing -10.1.1-15-s failing -10.1.1-16-s failing -10.1.1-17-s failing -10.1.1-18-s failing -10.1.1-19-s failing -10.1.1-2-s failing -10.1.1-22-s failing -10.1.1-25-s failing -10.1.1-26-s failing -10.1.1-30-s failing -10.1.1-5-s failing -10.1.1-8-s failing -10.2.1.1.3-4-16-s failing -10.2.1.1.3-4-18-s failing -10.2.1.1.3-4-22-s failing -10.2.1.1.3-4-27-s failing S10.2.3_A1.1_T2 failing S10.2.3_A1.2_T2 failing S10.2.3_A1.3_T2 failing @@ -248,14 +43,6 @@ S10.2.3_A1.3_T2 failing 10.4.3-1-104 failing 10.4.3-1-106 failing 10.4.3-1-17-s failing -10.4.3-1-54-s failing -10.4.3-1-54gs failing -10.4.3-1-55-s failing -10.4.3-1-55gs failing -10.4.3-1-56-s failing -10.4.3-1-56gs failing -10.4.3-1-57-s failing -10.4.3-1-57gs failing 10.4.3-1-63-s failing 10.4.3-1-66-s failing 10.4.3-1-66gs failing @@ -294,144 +81,45 @@ S10.2.3_A1.3_T2 failing 10.5-7-b-1-s failing 10.5-7-b-4-s failing 10.6-11-b-1 failing -10.6-12-1 failing -10.6-13-1 failing -10.6-13-a-1 failing -10.6-13-b-1-s failing -10.6-13-c-1-s failing -10.6-14-b-4-s failing -10.6-14-c-4-s failing 10.6-6-4 failing -10.6-7-1 failing -S10.6_A1 failing -S10.6_A2 failing -S10.6_A3_T1 failing -S10.6_A3_T2 failing -S10.6_A3_T3 failing -S10.6_A3_T4 failing -S10.6_A4 failing -S10.6_A5_T1 failing -S10.6_A5_T2 failing -S10.6_A5_T3 failing -S10.6_A5_T4 failing S10.6_A6 failing S10.6_A7 failing -S11.1.2_A1_T2 failing 11.1.4_4-5-1 failing 11.1.4_5-6-1 failing -11.1.5-0-1 failing -11.1.5-0-2 failing -11.1.5-1-s failing -11.1.5-2-s failing -11.1.5-3-s failing -11.1.5-4-4-a-1-s failing -11.1.5-4-s failing -11.1.5_3-3-1 failing -11.1.5_4-4-b-1 failing -11.1.5_4-4-b-2 failing -11.1.5_4-4-c-1 failing -11.1.5_4-4-c-2 failing -11.1.5_4-4-d-1 failing -11.1.5_4-4-d-2 failing -11.1.5_4-4-d-3 failing -11.1.5_4-4-d-4 failing -11.1.5_4-5-1 failing -11.1.5_6-2-1-s failing -11.1.5_6-2-2-s failing -11.1.5_6-3-1 failing -11.1.5_6-3-2 failing -11.1.5_7-2-1-s failing -11.1.5_7-2-2-s failing -11.1.5_7-3-1 failing -11.1.5_7-3-2 failing -S11.10.1_A2.1_T2 failing -S11.10.1_A2.1_T3 failing -S11.10.1_A2.2_T1 failing -S11.10.1_A2.3_T1 failing S11.10.1_A2.4_T1 failing -S11.10.1_A2.4_T2 failing S11.10.1_A2.4_T3 failing -S11.10.2_A2.1_T2 failing -S11.10.2_A2.1_T3 failing -S11.10.2_A2.2_T1 failing -S11.10.2_A2.3_T1 failing S11.10.2_A2.4_T1 failing -S11.10.2_A2.4_T2 failing S11.10.2_A2.4_T3 failing -S11.10.3_A2.1_T2 failing -S11.10.3_A2.1_T3 failing -S11.10.3_A2.2_T1 failing -S11.10.3_A2.3_T1 failing +S11.13.2_A2.1_T1.7 failing +S11.13.2_A2.1_T1.8 failing +S11.13.2_A3.1_T3 failing +S11.13.2_A3.1_T5 failing +S11.13.2_A3.1_T7 failing +S11.13.2_A3.1_T8 failing +S11.13.2_A3.2_T3 failing +S11.13.2_A3.2_T5 failing +S11.13.2_A3.2_T7 failing +S11.13.2_A3.2_T8 failing S11.10.3_A2.4_T1 failing -S11.10.3_A2.4_T2 failing S11.10.3_A2.4_T3 failing S11.11.1_A2.1_T1 failing -S11.11.1_A2.1_T2 failing -S11.11.1_A2.1_T3 failing -S11.11.1_A2.4_T2 failing -S11.11.1_A2.4_T3 failing S11.11.1_A3_T2 failing S11.11.1_A3_T3 failing S11.11.1_A3_T4 failing -S11.11.2_A2.1_T2 failing -S11.11.2_A2.1_T3 failing -S11.11.2_A2.4_T2 failing -S11.11.2_A2.4_T3 failing -S11.12_A2.1_T2 failing -S11.12_A2.1_T3 failing -S11.12_A2.1_T4 failing 11.13.1-1-1 failing 11.13.1-1-2 failing 11.13.1-1-3 failing 11.13.1-1-4 failing -11.13.1-1-6-s failing -11.13.1-1-s failing -11.13.1-2-s failing -11.13.1-3-s failing -11.13.1-4-14-s failing -11.13.1-4-27-s failing 11.13.1-4-28-s failing 11.13.1-4-29-s failing -11.13.1-4-3-s failing 11.13.1-4-30-s failing 11.13.1-4-31-s failing 11.13.1-4-6-s failing -S11.13.1_A2.1_T2 failing -S11.13.1_A4_T2 failing -11.13.2-1-s failing -11.13.2-10-s failing -11.13.2-11-s failing -11.13.2-2-s failing -11.13.2-23-s failing -11.13.2-24-s failing -11.13.2-25-s failing -11.13.2-26-s failing -11.13.2-27-s failing -11.13.2-28-s failing -11.13.2-29-s failing -11.13.2-3-s failing -11.13.2-30-s failing -11.13.2-31-s failing -11.13.2-32-s failing -11.13.2-33-s failing -11.13.2-34-s failing -11.13.2-35-s failing -11.13.2-36-s failing -11.13.2-37-s failing -11.13.2-38-s failing -11.13.2-39-s failing -11.13.2-4-s failing -11.13.2-40-s failing -11.13.2-41-s failing -11.13.2-42-s failing -11.13.2-43-s failing -11.13.2-44-s failing 11.13.2-45-s failing 11.13.2-46-s failing 11.13.2-47-s failing 11.13.2-48-s failing 11.13.2-49-s failing -11.13.2-5-s failing 11.13.2-50-s failing 11.13.2-51-s failing 11.13.2-52-s failing @@ -460,44 +148,8 @@ S11.13.1_A4_T2 failing 11.13.2-6-7-s failing 11.13.2-6-8-s failing 11.13.2-6-9-s failing -11.13.2-6-s failing -11.13.2-7-s failing -11.13.2-8-s failing -11.13.2-9-s failing S11.13.2_A1_T5 failing S11.13.2_A2.1_T1.3 failing -S11.13.2_A2.1_T1.7 failing -S11.13.2_A2.1_T1.8 failing -S11.13.2_A2.1_T2.1 failing -S11.13.2_A2.1_T2.10 failing -S11.13.2_A2.1_T2.11 failing -S11.13.2_A2.1_T2.2 failing -S11.13.2_A2.1_T2.3 failing -S11.13.2_A2.1_T2.4 failing -S11.13.2_A2.1_T2.5 failing -S11.13.2_A2.1_T2.6 failing -S11.13.2_A2.1_T2.7 failing -S11.13.2_A2.1_T2.8 failing -S11.13.2_A2.1_T2.9 failing -S11.13.2_A2.1_T3.1 failing -S11.13.2_A2.1_T3.10 failing -S11.13.2_A2.1_T3.11 failing -S11.13.2_A2.1_T3.2 failing -S11.13.2_A2.1_T3.3 failing -S11.13.2_A2.1_T3.4 failing -S11.13.2_A2.1_T3.5 failing -S11.13.2_A2.1_T3.6 failing -S11.13.2_A2.1_T3.7 failing -S11.13.2_A2.1_T3.8 failing -S11.13.2_A2.1_T3.9 failing -S11.13.2_A3.1_T3 failing -S11.13.2_A3.1_T5 failing -S11.13.2_A3.1_T7 failing -S11.13.2_A3.1_T8 failing -S11.13.2_A3.2_T3 failing -S11.13.2_A3.2_T5 failing -S11.13.2_A3.2_T7 failing -S11.13.2_A3.2_T8 failing S11.13.2_A4.2_T1.4 failing S11.13.2_A4.2_T2.3 failing S11.13.2_A4.2_T2.7 failing @@ -538,57 +190,25 @@ S11.13.2_A4.8_T2.6 failing S11.13.2_A4.8_T2.7 failing S11.13.2_A4.8_T2.8 failing S11.13.2_A4.8_T2.9 failing -S11.14_A2.1_T2 failing -S11.14_A2.1_T3 failing -S11.2.1_A2 failing S11.2.1_A3_T1 failing S11.2.1_A3_T2 failing S11.2.1_A3_T3 failing -S11.2.1_A3_T4 failing -S11.2.1_A3_T5 failing S11.2.1_A4_T1 failing S11.2.1_A4_T2 failing S11.2.1_A4_T3 failing S11.2.1_A4_T4 failing S11.2.1_A4_T5 failing -S11.2.2_A2 failing -S11.2.2_A3_T1 failing -S11.2.2_A3_T2 failing -S11.2.2_A3_T3 failing -S11.2.2_A3_T4 failing -S11.2.2_A3_T5 failing -S11.2.2_A4_T1 failing -S11.2.2_A4_T2 failing -S11.2.2_A4_T3 failing -S11.2.2_A4_T4 failing -S11.2.2_A4_T5 failing -11.2.3-3_1 failing -11.2.3-3_2 failing 11.2.3-3_3 failing -11.2.3-3_4 failing -11.2.3-3_5 failing -11.2.3-3_6 failing -11.2.3-3_7 failing -11.2.3-3_8 failing -S11.2.3_A2 failing S11.2.3_A3_T1 failing S11.2.3_A3_T2 failing S11.2.3_A3_T3 failing S11.2.3_A3_T4 failing S11.2.3_A3_T5 failing -S11.2.3_A4_T1 failing -S11.2.3_A4_T2 failing -S11.2.3_A4_T3 failing -S11.2.3_A4_T4 failing -S11.2.3_A4_T5 failing S11.2.4_A1.1_T2 failing S11.2.4_A1.2_T1 failing S11.2.4_A1.2_T2 failing -S11.2.4_A1.4_T2 failing -S11.2.4_A1.4_T4 failing 11.3.1-2-1-s failing 11.3.1-2-2-s failing -S11.3.1_A2.1_T2 failing S11.3.1_A2.2_T1 failing S11.3.1_A4_T1 failing S11.3.1_A4_T2 failing @@ -596,7 +216,6 @@ S11.3.1_A4_T3 failing S11.3.1_A4_T4 failing 11.3.2-2-1-s failing 11.3.2-2-2-s failing -S11.3.2_A2.1_T2 failing S11.3.2_A2.2_T1 failing S11.3.2_A4_T1 failing S11.3.2_A4_T2 failing @@ -609,143 +228,61 @@ S11.3.2_A4_T4 failing 11.4.1-2-4 failing 11.4.1-2-5 failing 11.4.1-2-6 failing -11.4.1-3-2 failing -11.4.1-3-a-1-s failing -11.4.1-4-a-1-s failing -11.4.1-4-a-2-s failing 11.4.1-4.a-10 failing 11.4.1-4.a-12 failing 11.4.1-4.a-13 failing -11.4.1-4.a-3-s failing 11.4.1-4.a-5 failing 11.4.1-4.a-7 failing -11.4.1-4.a-8-s failing 11.4.1-4.a-8 failing -11.4.1-4.a-9-s failing 11.4.1-5-2 failing -11.4.1-5-a-1-s failing -11.4.1-5-a-10-s failing -11.4.1-5-a-11-s failing -11.4.1-5-a-12-s failing -11.4.1-5-a-13-s failing -11.4.1-5-a-14-s failing -11.4.1-5-a-15-s failing -11.4.1-5-a-16-s failing -11.4.1-5-a-17-s failing -11.4.1-5-a-18-s failing -11.4.1-5-a-19-s failing -11.4.1-5-a-2-s failing -11.4.1-5-a-20-s failing -11.4.1-5-a-21-s failing -11.4.1-5-a-22-s failing -11.4.1-5-a-23-s failing -11.4.1-5-a-24-s failing -11.4.1-5-a-25-s failing -11.4.1-5-a-26-s failing -11.4.1-5-a-27-s failing 11.4.1-5-a-28-s failing -11.4.1-5-a-3-s failing -11.4.1-5-a-4-s failing -11.4.1-5-a-5-s failing -11.4.1-5-a-6-s failing -11.4.1-5-a-7-s failing -11.4.1-5-a-8-s failing -11.4.1-5-a-9-s failing -11.4.4-4.a-3-s failing S11.4.1_A1 failing S11.4.1_A2.1 failing S11.4.1_A3.3 failing 11.4.4-2-1-s failing 11.4.4-2-2-s failing -S11.4.4_A2.1_T2 failing -S11.4.4_A2.2_T1 failing 11.4.5-2-1-s failing 11.4.5-2-2-s failing -S11.4.5_A2.1_T2 failing -S11.4.5_A2.2_T1 failing -S11.4.6_A2.1_T2 failing -S11.4.6_A2.2_T1 failing -S11.4.7_A2.1_T2 failing -S11.4.7_A2.2_T1 failing S11.4.7_A3_T3 failing S11.4.7_A3_T4 failing S11.4.7_A3_T5 failing S11.4.7_A4.1 failing -S11.4.8_A2.1_T2 failing -S11.4.8_A2.2_T1 failing -S11.4.9_A2.1_T2 failing -S11.5.1_A2.1_T2 failing -S11.5.1_A2.1_T3 failing -S11.5.1_A2.2_T1 failing -S11.5.1_A2.3_T1 failing S11.5.1_A2.4_T1 failing -S11.5.1_A2.4_T2 failing S11.5.1_A2.4_T3 failing S11.5.1_A4_T3 failing -S11.5.2_A2.1_T2 failing -S11.5.2_A2.1_T3 failing -S11.5.2_A2.2_T1 failing -S11.5.2_A2.3_T1 failing S11.5.2_A2.4_T1 failing -S11.5.2_A2.4_T2 failing S11.5.2_A2.4_T3 failing S11.5.2_A3_T1.4 failing S11.5.2_A4_T4 failing S11.5.2_A4_T7 failing -S11.5.3_A2.1_T2 failing -S11.5.3_A2.1_T3 failing -S11.5.3_A2.2_T1 failing -S11.5.3_A2.3_T1 failing S11.5.3_A2.4_T1 failing -S11.5.3_A2.4_T2 failing S11.5.3_A2.4_T3 failing S11.5.3_A3_T1.4 failing S11.5.3_A3_T2.3 failing S11.5.3_A3_T2.7 failing S11.5.3_A3_T2.9 failing S11.5.3_A4_T2 failing -S11.5.3_A4_T3 failing -S11.5.3_A4_T4 failing -S11.6.1_A2.1_T2 failing -S11.6.1_A2.1_T3 failing -S11.6.1_A2.2_T1 failing -S11.6.1_A2.3_T1 failing -S11.6.1_A2.4_T1 failing -S11.6.1_A2.4_T2 failing -S11.6.1_A2.4_T3 failing -S11.6.1_A4_T2 failing -S11.6.2_A2.1_T2 failing -S11.6.2_A2.1_T3 failing -S11.6.2_A2.2_T1 failing -S11.6.2_A2.3_T1 failing -S11.6.2_A2.4_T1 failing -S11.6.2_A2.4_T2 failing -S11.6.2_A2.4_T3 failing S11.6.2_A4_T3 failing -S11.7.1_A2.1_T2 failing -S11.7.1_A2.1_T3 failing -S11.7.1_A2.2_T1 failing -S11.7.1_A2.3_T1 failing S11.7.1_A2.4_T1 failing -S11.7.1_A2.4_T2 failing S11.7.1_A2.4_T3 failing -S11.7.2_A2.1_T2 failing -S11.7.2_A2.1_T3 failing -S11.7.2_A2.2_T1 failing -S11.7.2_A2.3_T1 failing S11.7.2_A2.4_T1 failing -S11.7.2_A2.4_T2 failing S11.7.2_A2.4_T3 failing S11.7.2_A3_T1.2 failing S11.7.2_A4_T3 failing S11.7.2_A4_T4 failing S11.7.2_A5.2_T1 failing -S11.7.3_A2.1_T2 failing -S11.7.3_A2.1_T3 failing -S11.7.3_A2.2_T1 failing -S11.7.3_A2.3_T1 failing +S11.5.3_A4_T3 failing +S11.5.3_A4_T4 failing +S11.6.1_A2.4_T1 failing +S11.6.1_A2.4_T3 failing +S11.6.1_A4_T2 failing +S11.6.2_A2.4_T1 failing +S11.6.2_A2.4_T3 failing +S11.8.2_A2.4_T1 failing +S11.8.2_A2.4_T3 failing +S11.8.3_A2.4_T1 failing +S11.8.3_A2.4_T3 failing S11.7.3_A2.4_T1 failing -S11.7.3_A2.4_T2 failing S11.7.3_A2.4_T3 failing S11.7.3_A3_T1.2 failing S11.7.3_A4_T1 failing @@ -753,284 +290,99 @@ S11.7.3_A4_T2 failing S11.7.3_A4_T3 failing S11.7.3_A4_T4 failing S11.7.3_A5.2_T1 failing -S11.8.1_A2.1_T2 failing -S11.8.1_A2.1_T3 failing -S11.8.1_A2.2_T1 failing -S11.8.1_A2.3_T1 failing S11.8.1_A2.4_T1 failing -S11.8.1_A2.4_T2 failing S11.8.1_A2.4_T3 failing -S11.8.2_A2.1_T2 failing -S11.8.2_A2.1_T3 failing -S11.8.2_A2.2_T1 failing -S11.8.2_A2.3_T1 failing -S11.8.2_A2.4_T1 failing -S11.8.2_A2.4_T2 failing -S11.8.2_A2.4_T3 failing -S11.8.3_A2.1_T2 failing -S11.8.3_A2.1_T3 failing -S11.8.3_A2.2_T1 failing -S11.8.3_A2.3_T1 failing -S11.8.3_A2.4_T1 failing -S11.8.3_A2.4_T2 failing -S11.8.3_A2.4_T3 failing -S11.8.4_A2.1_T2 failing -S11.8.4_A2.1_T3 failing -S11.8.4_A2.2_T1 failing -S11.8.4_A2.3_T1 failing S11.8.4_A2.4_T1 failing -S11.8.4_A2.4_T2 failing S11.8.4_A2.4_T3 failing -S11.8.6_A2.1_T2 failing -S11.8.6_A2.1_T3 failing S11.8.6_A2.4_T1 failing -S11.8.6_A2.4_T2 failing S11.8.6_A2.4_T3 failing S11.8.6_A3 failing S11.8.6_A4_T1 failing S11.8.6_A4_T2 failing S11.8.6_A4_T3 failing S11.8.6_A5_T2 failing -S11.8.6_A6_T1 failing -S11.8.6_A6_T2 failing -S11.8.6_A6_T4 failing -S11.8.7_A2.1_T2 failing -S11.8.7_A2.1_T3 failing S11.8.7_A2.4_T1 failing -S11.8.7_A2.4_T2 failing S11.8.7_A2.4_T3 failing -S11.8.7_A3 failing -S11.9.1_A2.1_T2 failing -S11.9.1_A2.1_T3 failing S11.9.1_A2.4_T1 failing -S11.9.1_A2.4_T2 failing S11.9.1_A2.4_T3 failing -S11.9.1_A7.8 failing -S11.9.1_A7.9 failing -S11.9.2_A2.1_T2 failing -S11.9.2_A2.1_T3 failing S11.9.2_A2.4_T1 failing -S11.9.2_A2.4_T2 failing S11.9.2_A2.4_T3 failing -S11.9.2_A7.8 failing -S11.9.2_A7.9 failing -S11.9.4_A2.1_T2 failing -S11.9.4_A2.1_T3 failing S11.9.4_A2.4_T1 failing -S11.9.4_A2.4_T2 failing S11.9.4_A2.4_T3 failing -S11.9.4_A8_T5 failing -S11.9.5_A2.1_T2 failing -S11.9.5_A2.1_T3 failing S11.9.5_A2.4_T1 failing -S11.9.5_A2.4_T2 failing S11.9.5_A2.4_T3 failing -S11.9.5_A8_T5 failing -12.1-1 failing -12.1-2 failing -12.1-3 failing -12.1-4 failing -12.1-5 failing -12.1-6 failing -12.1-7 failing -S12.1_A2 failing -S12.1_A5 failing +S12.14_A12_T2 failing +S12.14_A12_T3 failing +S12.14_A12_T4 failing +S12.14_A13_T1 failing +S12.14_A13_T3 failing +S12.14_A19_T1 failing +S12.14_A19_T2 failing +S12.14_A7_T1 failing +S12.14_A7_T3 failing +12.14.1-1-s failing +12.14.1-2-s failing +12.14.1-3-s failing +S12.10_A1.9_T1 failing +S12.10_A1.9_T2 failing +S12.10_A3.11_T1 failing +S12.10_A3.11_T2 failing +S12.10_A3.11_T3 failing +S12.10_A3.11_T4 failing +S12.10_A3.11_T5 failing +S12.10_A3.12_T1 failing +S12.10_A3.12_T2 failing +S12.10_A3.12_T3 failing +S12.10_A3.12_T4 failing +S12.10_A3.12_T5 failing +S12.10_A3.4_T4 failing +S12.10_A3.4_T5 failing +S12.10_A3.5_T4 failing +S12.10_A3.5_T5 failing +S12.11_A1_T1 failing +S12.11_A1_T2 failing +S12.11_A1_T3 failing +S12.11_A1_T4 failing +S12.13_A3_T3 failing +12.14-13 failing +12.14-14 failing +12.14-15 failing +12.14-16 failing 12.10-0-3 failing -12.10-0-7 failing -12.10-2-1 failing -12.10-2-2 failing -12.10-2-3 failing -12.10-7-1 failing S12.10_A1.10_T1 failing S12.10_A1.10_T2 failing -S12.10_A1.10_T3 failing S12.10_A1.10_T4 failing -S12.10_A1.10_T5 failing S12.10_A1.11_T1 failing S12.10_A1.11_T2 failing -S12.10_A1.11_T3 failing S12.10_A1.11_T4 failing -S12.10_A1.11_T5 failing S12.10_A1.12_T1 failing S12.10_A1.12_T2 failing -S12.10_A1.12_T3 failing S12.10_A1.12_T4 failing -S12.10_A1.12_T5 failing S12.10_A1.1_T1 failing S12.10_A1.1_T2 failing -S12.10_A1.1_T3 failing S12.10_A1.2_T1 failing S12.10_A1.2_T2 failing -S12.10_A1.2_T3 failing S12.10_A1.2_T4 failing -S12.10_A1.2_T5 failing S12.10_A1.3_T1 failing S12.10_A1.3_T2 failing -S12.10_A1.3_T3 failing S12.10_A1.3_T4 failing -S12.10_A1.3_T5 failing S12.10_A1.4_T1 failing S12.10_A1.4_T2 failing -S12.10_A1.4_T3 failing S12.10_A1.4_T4 failing S12.10_A1.4_T5 failing S12.10_A1.5_T1 failing S12.10_A1.5_T2 failing -S12.10_A1.5_T3 failing S12.10_A1.5_T4 failing S12.10_A1.5_T5 failing S12.10_A1.6_T1 failing S12.10_A1.6_T2 failing -S12.10_A1.6_T3 failing S12.10_A1.7_T1 failing S12.10_A1.7_T2 failing -S12.10_A1.7_T3 failing S12.10_A1.7_T4 failing -S12.10_A1.7_T5 failing S12.10_A1.8_T1 failing S12.10_A1.8_T2 failing -S12.10_A1.8_T3 failing S12.10_A1.8_T4 failing -S12.10_A1.8_T5 failing -S12.10_A1.9_T1 failing -S12.10_A1.9_T2 failing -S12.10_A1.9_T3 failing -S12.10_A3.10_T2 failing -S12.10_A3.10_T3 failing -S12.10_A3.11_T1 failing -S12.10_A3.11_T2 failing -S12.10_A3.11_T3 failing -S12.10_A3.11_T4 failing -S12.10_A3.11_T5 failing -S12.10_A3.12_T1 failing -S12.10_A3.12_T2 failing -S12.10_A3.12_T3 failing -S12.10_A3.12_T4 failing -S12.10_A3.12_T5 failing -S12.10_A3.1_T2 failing -S12.10_A3.1_T3 failing -S12.10_A3.2_T4 failing -S12.10_A3.2_T5 failing -S12.10_A3.3_T4 failing -S12.10_A3.4_T2 failing -S12.10_A3.4_T3 failing -S12.10_A3.4_T4 failing -S12.10_A3.4_T5 failing -S12.10_A3.5_T2 failing -S12.10_A3.5_T3 failing -S12.10_A3.5_T4 failing -S12.10_A3.5_T5 failing -S12.10_A3.6_T2 failing -S12.10_A3.6_T3 failing -S12.10_A3.7_T4 failing -S12.10_A3.7_T5 failing -S12.10_A3.8_T4 failing -S12.10_A3.8_T5 failing -S12.10_A5_T4 failing -S12.10_A5_T5 failing -S12.10_A5_T6 failing -12.10.1-1-s failing -12.10.1-10-s failing -12.10.1-11-s failing -12.10.1-12-s failing -12.10.1-14-s failing -12.10.1-15-s failing -12.10.1-16-s failing -12.10.1-2-s failing -12.10.1-3-s failing -12.10.1-4-s failing -12.10.1-7-s failing -12.10.1-8-s failing -12.10.1-9-s failing -S12.11_A1_T1 failing -S12.11_A1_T2 failing -S12.11_A1_T3 failing -S12.11_A1_T4 failing -S12.13_A2_T1 failing -S12.13_A2_T2 failing -S12.13_A2_T3 failing -S12.13_A2_T4 failing -S12.13_A2_T5 failing -S12.13_A2_T6 failing -S12.13_A2_T7 failing -S12.13_A3_T1 failing -S12.13_A3_T2 failing -S12.13_A3_T3 failing -S12.13_A3_T4 failing -S12.13_A3_T5 failing -S12.13_A3_T6 failing -12.14-1 failing -12.14-10 failing -12.14-11 failing -12.14-12 failing -12.14-13 failing -12.14-14 failing -12.14-15 failing -12.14-16 failing -12.14-2 failing -12.14-3 failing -12.14-4 failing -12.14-6 failing -12.14-7 failing -12.14-8 failing -12.14-9 failing -S12.14_A1 failing -S12.14_A10_T1 failing -S12.14_A10_T2 failing -S12.14_A10_T3 failing -S12.14_A10_T4 failing -S12.14_A10_T5 failing -S12.14_A11_T1 failing -S12.14_A11_T2 failing -S12.14_A11_T3 failing -S12.14_A11_T4 failing -S12.14_A12_T1 failing -S12.14_A12_T2 failing -S12.14_A12_T3 failing -S12.14_A12_T4 failing -S12.14_A13_T1 failing -S12.14_A13_T2 failing -S12.14_A13_T3 failing -S12.14_A14 failing -S12.14_A15 failing -S12.14_A17 failing -S12.14_A18_T1 failing -S12.14_A18_T2 failing -S12.14_A18_T3 failing -S12.14_A18_T4 failing -S12.14_A18_T5 failing -S12.14_A18_T6 failing -S12.14_A18_T7 failing -S12.14_A19_T1 failing -S12.14_A19_T2 failing -S12.14_A2 failing -S12.14_A3 failing -S12.14_A4 failing -S12.14_A5 failing -S12.14_A6 failing -S12.14_A7_T1 failing -S12.14_A7_T2 failing -S12.14_A7_T3 failing -S12.14_A8 failing -S12.14_A9_T1 failing -S12.14_A9_T2 failing -S12.14_A9_T3 failing -S12.14_A9_T4 failing -S12.14_A9_T5 failing -12.14.1-1-s failing -12.14.1-2-s failing -12.14.1-3-s failing -12.14.1-4-s failing -12.14.1-5-s failing -12.14.1-6-s failing -S12.2_A1 failing -S12.2_A10 failing -S12.2_A12 failing -S12.2_A4 failing -S12.2_A5 failing -S12.2_A6_T1 failing -S12.2_A6_T2 failing -S12.2_A7 failing +S12.6.1_A8 failing 12.2.1-1-s failing 12.2.1-12-s failing 12.2.1-13-s failing @@ -1060,17 +412,7 @@ S12.2_A7 failing 12.2.1-4-s failing 12.2.1-7-s failing 12.2.1-8-s failing -S12.5_A3 failing -S12.5_A4 failing -S12.5_A5 failing -S12.5_A7 failing -S12.6.1_A2 failing -S12.6.1_A8 failing -S12.6.1_A9 failing -S12.6.2_A2 failing S12.6.2_A8 failing -S12.6.2_A9 failing -S12.6.3_A1 failing S12.6.3_A10.1 failing S12.6.3_A10 failing S12.6.3_A11.1_T1 failing @@ -1081,41 +423,12 @@ S12.6.3_A12.1_T1 failing S12.6.3_A12.1_T2 failing S12.6.3_A12_T1 failing S12.6.3_A12_T2 failing -S12.6.3_A13 failing -S12.6.3_A2.1 failing -S12.6.3_A2.2 failing -S12.6.3_A2 failing -S12.6.3_A3 failing -S12.6.3_A5 failing -S12.6.3_A6 failing -S12.6.3_A9.1 failing -S12.6.3_A9 failing -S12.6.4_A1 failing -S12.6.4_A2 failing -S12.6.4_A3.1 failing -S12.6.4_A3 failing -S12.7_A2 failing -S12.7_A7 failing -S12.7_A9_T1 failing -S12.7_A9_T2 failing -S12.8_A2 failing -S12.8_A4_T1 failing -S12.8_A4_T2 failing -S12.8_A4_T3 failing -S12.8_A7 failing -S12.8_A9_T1 failing -S12.8_A9_T2 failing -S12.9_A2 failing -13.0-1 failing 13.0-10-s failing 13.0-11-s failing 13.0-13-s failing 13.0-14-s failing 13.0-15-s failing 13.0-16-s failing -13.0-2 failing -13.0-3 failing -13.0-4 failing 13.0-7-s failing 13.0-8-s failing 13.0-9-s failing @@ -1128,28 +441,13 @@ S13_A2_T3 failing S13_A3_T1 failing S13_A4_T4 failing S13_A6_T2 failing -S13_A7_T2 failing S13_A8_T1 failing S13_A8_T2 failing -13.1-1-1 failing -13.1-1-2 failing -13.1-1-s failing 13.1-10-s failing 13.1-11-s failing 13.1-12-s failing 13.1-13-s failing 13.1-14-s failing -13.1-15-s failing -13.1-16-s failing -13.1-17-s failing -13.1-18-s failing -13.1-19-s failing -13.1-2-1 failing -13.1-2-5 failing -13.1-2-s failing -13.1-20-s failing -13.1-21-s failing -13.1-22-s failing 13.1-23-s failing 13.1-24-s failing 13.1-25-s failing @@ -1157,11 +455,6 @@ S13_A8_T2 failing 13.1-27-s failing 13.1-28-s failing 13.1-29-s failing -13.1-3-1 failing -13.1-3-2 failing -13.1-3-7 failing -13.1-3-8 failing -13.1-3-s failing 13.1-30-s failing 13.1-31-s failing 13.1-32-s failing @@ -1172,7 +465,6 @@ S13_A8_T2 failing 13.1-37-s failing 13.1-38-s failing 13.1-39-s failing -13.1-4-s failing 13.1-40-s failing 13.1-41-s failing 13.1-42-s failing @@ -1214,33 +506,17 @@ S13_A8_T2 failing 13.2-9-s failing S13.2.1_A4_T3 failing S13.2.1_A4_T4 failing -S13.2.1_A7_T1 failing -S13.2.1_A7_T2 failing -S13.2.1_A7_T3 failing -S13.2.1_A7_T4 failing -S13.2.1_A8_T1 failing -S13.2.1_A8_T2 failing -S13.2.2_A10 failing -S13.2.2_A11 failing -S13.2.2_A12 failing -S13.2.2_A13 failing -S13.2.2_A14 failing S13.2.2_A16_T2 failing S13.2.2_A16_T3 failing -S13.2.2_A17_T2 failing S13.2.2_A19_T1 failing S13.2.2_A19_T3 failing S13.2.2_A19_T4 failing S13.2.2_A19_T5 failing S13.2.2_A19_T6 failing S13.2.2_A19_T8 failing -S13.2.2_A1_T1 failing -S13.2.2_A1_T2 failing -S13.2.2_A2 failing S13.2.2_A5_T1 failing S13.2.2_A5_T2 failing S13.2.2_A8_T3 failing -S13.2.2_A9 failing S13.2.3_A1 failing S13.2_A2_T1 failing S13.2_A2_T2 failing @@ -1251,14 +527,10 @@ S14_A2 failing S14_A5_T1 failing S14_A5_T2 failing 14.1-5-s failing -15.1.1.3-2 failing 15.1.1.3-3 failing -S15.1.2.1_A2_T1 failing S15.1.2.1_A3.2_T8 failing S15.1.2.1_A3.3_T1 failing -S15.1.2.1_A3.3_T2 failing S15.1.2.1_A3.3_T3 failing -S15.1.2.1_A3.3_T4 failing S15.1.2.1_A4.1 failing S15.1.2.1_A4.2 failing S15.1.2.1_A4.3 failing @@ -1286,6 +558,14 @@ S15.1.2.2_A2_T9 failing S15.1.2.2_A3.1_T1 failing S15.1.2.2_A3.1_T2 failing S15.1.2.2_A3.1_T3 failing +S12.6.4_A1 failing +S12.6.4_A2 failing +S12.6.4_A3.1 failing +S12.6.4_A3 failing +S12.7_A7 failing +S12.8_A4_T1 failing +S12.8_A4_T2 failing +S12.8_A4_T3 failing S15.1.2.2_A3.1_T4 failing S15.1.2.2_A3.1_T5 failing S15.1.2.2_A3.1_T6 failing @@ -1349,222 +629,20 @@ S15.1.2.3_A4_T3 failing S15.1.2.3_A4_T4 failing S15.1.2.3_A4_T5 failing S15.1.2.3_A4_T6 failing -S15.1.2.3_A4_T7 failing -S15.1.2.3_A5_T1 failing -S15.1.2.3_A5_T2 failing -S15.1.2.3_A5_T3 failing -S15.1.2.3_A5_T4 failing -S15.1.2.3_A6 failing -S15.1.2.3_A7.1 failing -S15.1.2.3_A7.2 failing -S15.1.2.3_A7.3 failing -S15.1.2.3_A7.4 failing -S15.1.2.3_A7.6 failing -S15.1.2.3_A7.7 failing -S15.1.2.4_A2.1 failing -S15.1.2.4_A2.2 failing -S15.1.2.4_A2.3 failing -S15.1.2.4_A2.4 failing -S15.1.2.4_A2.5 failing -S15.1.2.4_A2.7 failing -S15.1.2.5_A2.1 failing -S15.1.2.5_A2.2 failing -S15.1.2.5_A2.3 failing -S15.1.2.5_A2.4 failing -S15.1.2.5_A2.5 failing -S15.1.2.5_A2.7 failing -S15.1.3.1_A1.10_T1 failing -S15.1.3.1_A1.11_T1 failing -S15.1.3.1_A1.11_T2 failing -S15.1.3.1_A1.12_T1 failing -S15.1.3.1_A1.12_T2 failing -S15.1.3.1_A1.12_T3 failing -S15.1.3.1_A1.13_T1 failing -S15.1.3.1_A1.13_T2 failing -S15.1.3.1_A1.14_T1 failing -S15.1.3.1_A1.14_T2 failing -S15.1.3.1_A1.14_T3 failing -S15.1.3.1_A1.14_T4 failing -S15.1.3.1_A1.15_T1 failing -S15.1.3.1_A1.15_T2 failing -S15.1.3.1_A1.15_T3 failing -S15.1.3.1_A1.15_T4 failing -S15.1.3.1_A1.15_T5 failing -S15.1.3.1_A1.15_T6 failing -S15.1.3.1_A1.1_T1 failing -S15.1.3.1_A1.2_T1 failing -S15.1.3.1_A1.2_T2 failing -S15.1.3.1_A1.3_T1 failing -S15.1.3.1_A1.3_T2 failing -S15.1.3.1_A1.4_T1 failing -S15.1.3.1_A1.5_T1 failing -S15.1.3.1_A1.6_T1 failing -S15.1.3.1_A1.7_T1 failing -S15.1.3.1_A1.8_T1 failing -S15.1.3.1_A1.8_T2 failing -S15.1.3.1_A1.9_T1 failing -S15.1.3.1_A1.9_T2 failing -S15.1.3.1_A1.9_T3 failing -S15.1.3.1_A2.1_T1 failing -S15.1.3.1_A2.2_T1 failing -S15.1.3.1_A2.3_T1 failing -S15.1.3.1_A2.4_T1 failing -S15.1.3.1_A3_T1 failing -S15.1.3.1_A3_T2 failing -S15.1.3.1_A3_T3 failing -S15.1.3.1_A4_T1 failing -S15.1.3.1_A4_T2 failing -S15.1.3.1_A4_T3 failing -S15.1.3.1_A4_T4 failing -S15.1.3.1_A5.1 failing -S15.1.3.1_A5.2 failing -S15.1.3.1_A5.3 failing -S15.1.3.1_A5.4 failing -S15.1.3.1_A5.6 failing -S15.1.3.1_A5.7 failing -S15.1.3.1_A6_T1 failing -S15.1.3.2_A1.10_T1 failing -S15.1.3.2_A1.11_T1 failing -S15.1.3.2_A1.11_T2 failing -S15.1.3.2_A1.12_T1 failing -S15.1.3.2_A1.12_T2 failing -S15.1.3.2_A1.12_T3 failing -S15.1.3.2_A1.13_T1 failing -S15.1.3.2_A1.13_T2 failing -S15.1.3.2_A1.14_T1 failing -S15.1.3.2_A1.14_T2 failing -S15.1.3.2_A1.14_T3 failing -S15.1.3.2_A1.14_T4 failing -S15.1.3.2_A1.15_T1 failing -S15.1.3.2_A1.15_T2 failing -S15.1.3.2_A1.15_T3 failing -S15.1.3.2_A1.15_T4 failing -S15.1.3.2_A1.15_T5 failing -S15.1.3.2_A1.15_T6 failing -S15.1.3.2_A1.1_T1 failing -S15.1.3.2_A1.2_T1 failing -S15.1.3.2_A1.2_T2 failing -S15.1.3.2_A1.3_T1 failing -S15.1.3.2_A1.3_T2 failing -S15.1.3.2_A1.4_T1 failing -S15.1.3.2_A1.5_T1 failing -S15.1.3.2_A1.6_T1 failing -S15.1.3.2_A1.7_T1 failing -S15.1.3.2_A1.8_T1 failing -S15.1.3.2_A1.8_T2 failing -S15.1.3.2_A1.9_T1 failing -S15.1.3.2_A1.9_T2 failing -S15.1.3.2_A1.9_T3 failing -S15.1.3.2_A2.1_T1 failing -S15.1.3.2_A2.2_T1 failing -S15.1.3.2_A2.3_T1 failing -S15.1.3.2_A2.4_T1 failing -S15.1.3.2_A3_T1 failing -S15.1.3.2_A3_T2 failing -S15.1.3.2_A3_T3 failing -S15.1.3.2_A4_T1 failing -S15.1.3.2_A4_T2 failing -S15.1.3.2_A4_T3 failing -S15.1.3.2_A4_T4 failing -S15.1.3.2_A5.1 failing -S15.1.3.2_A5.2 failing -S15.1.3.2_A5.3 failing -S15.1.3.2_A5.4 failing -S15.1.3.2_A5.6 failing -S15.1.3.2_A5.7 failing -S15.1.3.2_A6_T1 failing -S15.1.3.3_A1.1_T1 failing -S15.1.3.3_A1.1_T2 failing -S15.1.3.3_A1.2_T1 failing -S15.1.3.3_A1.2_T2 failing -S15.1.3.3_A1.3_T1 failing -S15.1.3.3_A2.1_T1 failing -S15.1.3.3_A2.2_T1 failing -S15.1.3.3_A2.3_T1 failing -S15.1.3.3_A2.4_T1 failing -S15.1.3.3_A2.4_T2 failing -S15.1.3.3_A2.5_T1 failing -S15.1.3.3_A3.1_T1 failing -S15.1.3.3_A3.2_T1 failing -S15.1.3.3_A3.2_T2 failing -S15.1.3.3_A3.2_T3 failing -S15.1.3.3_A3.3_T1 failing -S15.1.3.3_A4_T1 failing -S15.1.3.3_A4_T2 failing -S15.1.3.3_A4_T3 failing -S15.1.3.3_A4_T4 failing -S15.1.3.3_A5.1 failing -S15.1.3.3_A5.2 failing -S15.1.3.3_A5.3 failing -S15.1.3.3_A5.4 failing -S15.1.3.3_A5.6 failing -S15.1.3.3_A5.7 failing -S15.1.3.3_A6_T1 failing -S15.1.3.4_A1.1_T1 failing -S15.1.3.4_A1.1_T2 failing -S15.1.3.4_A1.2_T1 failing -S15.1.3.4_A1.2_T2 failing -S15.1.3.4_A1.3_T1 failing -S15.1.3.4_A2.1_T1 failing -S15.1.3.4_A2.2_T1 failing -S15.1.3.4_A2.3_T1 failing -S15.1.3.4_A2.4_T1 failing -S15.1.3.4_A2.4_T2 failing -S15.1.3.4_A2.5_T1 failing -S15.1.3.4_A3.1_T1 failing -S15.1.3.4_A3.2_T1 failing -S15.1.3.4_A3.2_T2 failing -S15.1.3.4_A3.2_T3 failing -S15.1.3.4_A3.3_T1 failing -S15.1.3.4_A4_T1 failing -S15.1.3.4_A4_T2 failing -S15.1.3.4_A4_T3 failing -S15.1.3.4_A4_T4 failing -S15.1.3.4_A5.1 failing -S15.1.3.4_A5.2 failing -S15.1.3.4_A5.3 failing -S15.1.3.4_A5.4 failing -S15.1.3.4_A5.6 failing -S15.1.3.4_A5.7 failing -S15.1.3.4_A6_T1 failing -S15.10.1_A1_T1 failing -S15.10.1_A1_T10 failing -S15.10.1_A1_T11 failing -S15.10.1_A1_T12 failing -S15.10.1_A1_T13 failing -S15.10.1_A1_T14 failing -S15.10.1_A1_T15 failing -S15.10.1_A1_T16 failing -S15.10.1_A1_T2 failing -S15.10.1_A1_T3 failing -S15.10.1_A1_T4 failing -S15.10.1_A1_T5 failing -S15.10.1_A1_T6 failing -S15.10.1_A1_T7 failing -S15.10.1_A1_T8 failing -S15.10.1_A1_T9 failing -15.10.2.15-3-1 failing -15.10.2.15-3-2 failing -15.10.2.15-6-1 failing -15.10.2.2-1 failing -15.10.2.5-3-1 failing -S15.10.2_A1_T1 failing -S15.10.2.10_A2.1_T3 failing -S15.10.2.10_A4.1_T1 failing -S15.10.2.10_A4.1_T2 failing -S15.10.2.10_A4.1_T3 failing -S15.10.2.10_A5.1_T1 failing -S15.10.2.11_A1_T5 failing -S15.10.2.11_A1_T7 failing -S15.10.2.12_A1_T1 failing -S15.10.2.12_A1_T2 failing -S15.10.2.12_A1_T5 failing -S15.10.2.12_A2_T1 failing -S15.10.2.12_A2_T2 failing -S15.10.2.12_A2_T5 failing -S15.10.2.12_A3_T1 failing -S15.10.2.12_A4_T1 failing -S15.10.2.12_A5_T1 failing +S15.10.2.15_A1_T38 failing +S15.10.2.15_A1_T39 failing +S15.10.2.15_A1_T4 failing +S15.10.2.15_A1_T40 failing +S15.10.2.15_A1_T41 failing +S15.10.2.15_A1_T5 failing +S15.10.2.15_A1_T6 failing +S15.10.2.15_A1_T7 failing +S15.10.2.15_A1_T8 failing +S15.10.2.15_A1_T9 failing +S15.10.2.3_A1_T15 failing +S15.10.2.3_A1_T17 failing +S15.10.2.3_A1_T2 failing +S15.10.2.5_A1_T4 failing S15.10.2.12_A6_T1 failing S15.10.2.13_A2_T1 failing S15.10.2.13_A2_T2 failing @@ -1600,20 +678,40 @@ S15.10.2.15_A1_T34 failing S15.10.2.15_A1_T35 failing S15.10.2.15_A1_T36 failing S15.10.2.15_A1_T37 failing -S15.10.2.15_A1_T38 failing -S15.10.2.15_A1_T39 failing -S15.10.2.15_A1_T4 failing -S15.10.2.15_A1_T40 failing -S15.10.2.15_A1_T41 failing -S15.10.2.15_A1_T5 failing -S15.10.2.15_A1_T6 failing -S15.10.2.15_A1_T7 failing -S15.10.2.15_A1_T8 failing -S15.10.2.15_A1_T9 failing -S15.10.2.3_A1_T15 failing -S15.10.2.3_A1_T17 failing -S15.10.2.3_A1_T2 failing -S15.10.2.5_A1_T4 failing +S15.10.1_A1_T12 failing +S15.10.1_A1_T13 failing +S15.10.1_A1_T14 failing +S15.10.1_A1_T15 failing +S15.10.1_A1_T16 failing +S15.10.1_A1_T2 failing +S15.10.1_A1_T3 failing +S15.10.1_A1_T4 failing +S15.10.1_A1_T5 failing +S15.10.1_A1_T6 failing +S15.10.1_A1_T7 failing +S15.10.1_A1_T8 failing +S15.10.1_A1_T9 failing +15.10.2.15-3-1 failing +15.10.2.15-3-2 failing +15.10.2.15-6-1 failing +15.10.2.2-1 failing +15.10.2.5-3-1 failing +S15.10.2.10_A2.1_T3 failing +S15.10.2.10_A4.1_T1 failing +S15.10.2.10_A4.1_T2 failing +S15.10.2.10_A4.1_T3 failing +S15.10.2.10_A5.1_T1 failing +S15.10.2.11_A1_T5 failing +S15.10.2.11_A1_T7 failing +S15.10.2.12_A1_T1 failing +S15.10.2.12_A1_T2 failing +S15.10.2.12_A1_T5 failing +S15.10.2.12_A2_T1 failing +S15.10.2.12_A2_T2 failing +S15.10.2.12_A2_T5 failing +S15.10.2.12_A3_T1 failing +S15.10.2.12_A4_T1 failing +S15.10.2.12_A5_T1 failing S15.10.2.7_A5_T2 failing S15.10.2.8_A1_T4 failing S15.10.2.8_A2_T1 failing @@ -1630,10 +728,6 @@ S15.10.2.8_A3_T27 failing S15.10.2.8_A3_T28 failing S15.10.2.8_A3_T29 failing S15.10.2.9_A1_T2 failing -S15.10.3.1_A2_T1 failing -S15.10.3.1_A2_T2 failing -S15.10.4.1_A2_T1 failing -S15.10.4.1_A2_T2 failing S15.10.4.1_A5_T1 failing S15.10.4.1_A5_T2 failing S15.10.4.1_A5_T3 failing @@ -1642,19 +736,12 @@ S15.10.4.1_A5_T6 failing S15.10.4.1_A5_T7 failing S15.10.4.1_A5_T8 failing S15.10.4.1_A5_T9 failing -S15.10.4.1_A8_T12 failing -S15.10.4.1_A8_T13 failing S15.10.4.1_A8_T2 failing -S15.10.4.1_A8_T6 failing -S15.10.4.1_A8_T7 failing -S15.10.4.1_A8_T8 failing S15.10.4.1_A9_T1 failing S15.10.4.1_A9_T2 failing S15.10.4.1_A9_T3 failing -15.10.4.1-1 failing 15.10.4.1-2 failing 15.10.4.1-3 failing -15.10.4.1-4 failing S15.10.5.1_A2 failing S15.10.5.1_A3 failing S15.10.5.1_A4 failing @@ -1663,18 +750,6 @@ S15.10.6.2_A10 failing S15.10.6.2_A11 failing S15.10.6.2_A1_T2 failing S15.10.6.2_A1_T6 failing -S15.10.6.2_A1_T7 failing -S15.10.6.2_A1_T8 failing -S15.10.6.2_A2_T1 failing -S15.10.6.2_A2_T10 failing -S15.10.6.2_A2_T2 failing -S15.10.6.2_A2_T3 failing -S15.10.6.2_A2_T4 failing -S15.10.6.2_A2_T5 failing -S15.10.6.2_A2_T6 failing -S15.10.6.2_A2_T7 failing -S15.10.6.2_A2_T8 failing -S15.10.6.2_A2_T9 failing S15.10.6.2_A4_T1 failing S15.10.6.2_A4_T10 failing S15.10.6.2_A4_T11 failing @@ -1690,35 +765,17 @@ S15.10.6.2_A4_T9 failing S15.10.6.2_A5_T1 failing S15.10.6.2_A5_T2 failing S15.10.6.2_A5_T3 failing -S15.10.6.2_A7 failing S15.10.6.2_A8 failing S15.10.6.2_A9 failing S15.10.6.3_A10 failing S15.10.6.3_A11 failing -S15.10.6.3_A1_T7 failing -S15.10.6.3_A1_T8 failing -S15.10.6.3_A2_T1 failing -S15.10.6.3_A2_T10 failing -S15.10.6.3_A2_T2 failing -S15.10.6.3_A2_T3 failing -S15.10.6.3_A2_T4 failing -S15.10.6.3_A2_T5 failing -S15.10.6.3_A2_T6 failing -S15.10.6.3_A2_T7 failing -S15.10.6.3_A2_T8 failing -S15.10.6.3_A2_T9 failing -S15.10.6.3_A7 failing S15.10.6.3_A8 failing S15.10.6.3_A9 failing S15.10.6.4_A10 failing S15.10.6.4_A11 failing -S15.10.6.4_A7 failing S15.10.6.4_A8 failing S15.10.6.4_A9 failing -S15.10.7_A1_T1 failing -S15.10.7_A1_T2 failing S15.10.7_A2_T1 failing -S15.10.7_A2_T2 failing 15.10.7.1-2 failing S15.10.7.1_A10 failing S15.10.7.1_A8 failing @@ -1746,34 +803,18 @@ S15.11.4.2_A1 failing S15.11.4.2_A2 failing S15.11.4.3_A1 failing S15.11.4.3_A2 failing -S15.11.4.4_A2 failing -S15.11.4_A3 failing -S15.11.4_A4 failing 15.12-0-1 failing 15.12-0-2 failing 15.12-0-3 failing 15.12-0-4 failing 15.12.1.1-0-1 failing -15.12.1.1-0-2 failing -15.12.1.1-0-3 failing -15.12.1.1-0-4 failing -15.12.1.1-0-5 failing -15.12.1.1-0-6 failing -15.12.1.1-0-7 failing -15.12.1.1-0-8 failing 15.12.1.1-0-9 failing 15.12.1.1-g1-1 failing 15.12.1.1-g1-2 failing 15.12.1.1-g1-3 failing 15.12.1.1-g1-4 failing 15.12.1.1-g2-1 failing -15.12.1.1-g2-2 failing -15.12.1.1-g2-3 failing -15.12.1.1-g2-4 failing 15.12.1.1-g2-5 failing -15.12.1.1-g4-1 failing -15.12.1.1-g4-2 failing -15.12.1.1-g4-3 failing 15.12.1.1-g4-4 failing 15.12.1.1-g5-1 failing 15.12.1.1-g5-2 failing @@ -1799,6 +840,75 @@ S15.12.2_A1 failing 15.12.3-11-13 failing 15.12.3-11-14 failing 15.12.3-11-15 failing +S15.1.2.3_A4_T7 failing +S15.1.2.3_A5_T1 failing +S15.1.2.3_A5_T2 failing +S15.1.2.3_A5_T3 failing +S15.1.2.3_A5_T4 failing +S15.1.2.3_A6 failing +S15.1.2.3_A7.1 failing +S15.1.2.3_A7.2 failing +S15.1.2.3_A7.3 failing +S15.1.2.3_A7.4 failing +S15.1.2.3_A7.6 failing +S15.1.2.3_A7.7 failing +S15.1.2.4_A2.1 failing +S15.1.2.4_A2.2 failing +S15.1.2.4_A2.3 failing +S15.1.2.4_A2.4 failing +S15.1.2.4_A2.5 failing +S15.1.2.4_A2.7 failing +S15.1.2.5_A2.1 failing +S15.1.2.5_A2.2 failing +S15.1.2.5_A2.3 failing +S15.1.2.5_A2.4 failing +S15.1.2.5_A2.5 failing +S15.1.2.5_A2.7 failing +S15.1.3.1_A1.10_T1 failing +S15.1.3.1_A1.11_T1 failing +S15.1.3.1_A1.11_T2 failing +S15.1.3.1_A1.12_T1 failing +S15.1.3.1_A1.12_T2 failing +S15.1.3.1_A1.12_T3 failing +S15.1.3.1_A1.13_T1 failing +S15.1.3.1_A1.13_T2 failing +S15.1.3.1_A1.14_T1 failing +S15.1.3.1_A1.14_T2 failing +S15.1.3.1_A1.14_T3 failing +S15.1.3.1_A1.14_T4 failing +S15.1.3.1_A1.15_T1 failing +S15.1.3.1_A1.15_T2 failing +S15.1.3.1_A1.15_T3 failing +S15.1.3.1_A1.15_T4 failing +S15.1.3.1_A1.15_T5 failing +S15.1.3.1_A1.15_T6 failing +S15.1.3.1_A1.1_T1 failing +S15.1.3.1_A1.2_T1 failing +S15.1.3.1_A1.2_T2 failing +S15.1.3.1_A1.3_T1 failing +S15.1.3.1_A1.3_T2 failing +S15.1.3.1_A1.4_T1 failing +S15.1.3.1_A1.5_T1 failing +S15.1.3.1_A1.6_T1 failing +S15.1.3.1_A1.7_T1 failing +S15.1.3.1_A1.8_T1 failing +S15.1.3.1_A1.8_T2 failing +S15.1.3.1_A1.9_T1 failing +S15.1.3.1_A1.9_T2 failing +S15.1.3.1_A1.9_T3 failing +S15.1.3.1_A2.1_T1 failing +S15.2.3_A1 failing +S15.2.3_A2 failing +S15.2.3_A3 failing +15.2.3.1 failing +S15.2.3.1_A1 failing +S15.2.3.1_A2 failing +S15.2.3.1_A3 failing +15.2.3.10-0-2 failing +15.2.3.10-3-12 failing +15.2.3.11-0-2 failing +15.2.3.11-4-27 failing +15.2.3.12-0-2 failing 15.12.3-11-2 failing 15.12.3-11-26 failing 15.12.3-11-3 failing @@ -1829,47 +939,72 @@ S15.12.2_A1 failing 15.12.3_2-3-a-1 failing 15.12.3_2-3-a-2 failing 15.12.3_2-3-a-3 failing -15.12.3_4-1-1 failing 15.12.3_4-1-2 failing -15.12.3_4-1-3 failing -S15.2.3_A1 failing -S15.2.3_A2 failing -S15.2.3_A3 failing -15.2.3.1 failing -S15.2.3.1_A1 failing -S15.2.3.1_A2 failing -S15.2.3.1_A3 failing -15.2.3.10-0-2 failing -15.2.3.10-1-1 failing -15.2.3.10-1-2 failing -15.2.3.10-1-3 failing -15.2.3.10-1-4 failing -15.2.3.10-1 failing -15.2.3.10-3-12 failing -15.2.3.10-3-5 failing -15.2.3.11-0-2 failing -15.2.3.11-1 failing -15.2.3.11-4-27 failing -15.2.3.12-0-2 failing -15.2.3.12-1-1 failing -15.2.3.12-1-2 failing -15.2.3.12-1-3 failing -15.2.3.12-1-4 failing -15.2.3.12-1 failing +S15.1.3.2_A5.4 failing +S15.1.3.2_A5.6 failing +S15.1.3.2_A5.7 failing +S15.1.3.2_A6_T1 failing +S15.1.3.3_A1.1_T1 failing +S15.1.3.3_A1.1_T2 failing +S15.1.3.3_A1.2_T1 failing +S15.1.3.3_A1.2_T2 failing +S15.1.3.3_A1.3_T1 failing +S15.1.3.3_A2.1_T1 failing +S15.1.3.3_A2.2_T1 failing +S15.1.3.3_A2.3_T1 failing +S15.1.3.3_A2.4_T1 failing +S15.1.3.3_A2.4_T2 failing +S15.1.3.3_A2.5_T1 failing +S15.1.3.3_A3.1_T1 failing +S15.1.3.3_A3.2_T1 failing +S15.1.3.3_A3.2_T2 failing +S15.1.3.3_A3.2_T3 failing +S15.1.3.3_A3.3_T1 failing +S15.1.3.3_A4_T1 failing +S15.1.3.3_A4_T2 failing +S15.1.3.3_A4_T3 failing +S15.1.3.3_A4_T4 failing +S15.1.3.3_A5.1 failing +S15.1.3.3_A5.2 failing +S15.1.3.3_A5.3 failing +S15.1.3.3_A5.4 failing +S15.1.3.3_A5.6 failing +S15.1.3.3_A5.7 failing +S15.1.3.3_A6_T1 failing +S15.1.3.4_A1.1_T1 failing +S15.1.3.4_A1.1_T2 failing +S15.1.3.4_A1.2_T1 failing +S15.1.3.4_A1.2_T2 failing +S15.1.3.4_A1.3_T1 failing +S15.1.3.4_A2.1_T1 failing +S15.1.3.4_A2.2_T1 failing +S15.1.3.4_A2.3_T1 failing +S15.1.3.4_A2.4_T1 failing +S15.1.3.4_A2.4_T2 failing +S15.1.3.4_A2.5_T1 failing +S15.1.3.4_A3.1_T1 failing +S15.1.3.4_A3.2_T1 failing +S15.1.3.4_A3.2_T2 failing +S15.1.3.4_A3.2_T3 failing +S15.1.3.4_A3.3_T1 failing +S15.1.3.4_A4_T1 failing +S15.1.3.4_A4_T2 failing +S15.1.3.4_A4_T3 failing +S15.1.3.4_A4_T4 failing +S15.1.3.4_A5.1 failing +S15.1.3.4_A5.2 failing +S15.1.3.4_A5.3 failing +S15.1.3.4_A5.4 failing +S15.1.3.4_A5.6 failing +S15.1.3.4_A5.7 failing +S15.1.3.4_A6_T1 failing +S15.10.1_A1_T1 failing +S15.10.1_A1_T10 failing +S15.10.1_A1_T11 failing 15.2.3.12-3-27 failing 15.2.3.13-0-2 failing -15.2.3.13-1-1 failing -15.2.3.13-1-2 failing -15.2.3.13-1-3 failing -15.2.3.13-1-4 failing -15.2.3.13-1 failing 15.2.3.13-2-12 failing 15.2.3.14-0-2 failing -15.2.3.14-1-1 failing -15.2.3.14-1-2 failing -15.2.3.14-1-3 failing -15.2.3.14-1-4 failing -15.2.3.14-1-5 failing 15.2.3.14-2-1 failing 15.2.3.14-3-2 failing 15.2.3.14-3-3 failing @@ -1885,11 +1020,6 @@ S15.2.3.1_A3 failing 15.2.3.14-5-a-3 failing 15.2.3.14-5-a-4 failing 15.2.3.2-0-2 failing -15.2.3.2-0-3 failing -15.2.3.2-1-2 failing -15.2.3.2-1-3 failing -15.2.3.2-1-4 failing -15.2.3.2-1 failing 15.2.3.2-2-12 failing 15.2.3.2-2-13 failing 15.2.3.2-2-14 failing @@ -1900,12 +1030,69 @@ S15.2.3.1_A3 failing 15.2.3.2-2-3 failing 15.2.3.2-2-31 failing 15.2.3.3-0-2 failing -15.2.3.3-1-1 failing -15.2.3.3-1-2 failing -15.2.3.3-1-3 failing -15.2.3.3-1-4 failing -15.2.3.3-1 failing -15.2.3.3-2-46 failing +S15.1.3.1_A2.2_T1 failing +S15.1.3.1_A2.3_T1 failing +S15.1.3.1_A2.4_T1 failing +S15.1.3.1_A3_T1 failing +S15.1.3.1_A3_T2 failing +S15.1.3.1_A3_T3 failing +S15.1.3.1_A4_T1 failing +S15.1.3.1_A4_T2 failing +S15.1.3.1_A4_T3 failing +S15.1.3.1_A4_T4 failing +S15.1.3.1_A5.1 failing +S15.1.3.1_A5.2 failing +S15.1.3.1_A5.3 failing +S15.1.3.1_A5.4 failing +S15.1.3.1_A5.6 failing +S15.1.3.1_A5.7 failing +S15.1.3.1_A6_T1 failing +S15.1.3.2_A1.10_T1 failing +S15.1.3.2_A1.11_T1 failing +S15.1.3.2_A1.11_T2 failing +S15.1.3.2_A1.12_T1 failing +S15.1.3.2_A1.12_T2 failing +S15.1.3.2_A1.12_T3 failing +S15.1.3.2_A1.13_T1 failing +S15.1.3.2_A1.13_T2 failing +S15.1.3.2_A1.14_T1 failing +S15.1.3.2_A1.14_T2 failing +S15.1.3.2_A1.14_T3 failing +S15.1.3.2_A1.14_T4 failing +S15.1.3.2_A1.15_T1 failing +S15.1.3.2_A1.15_T2 failing +S15.1.3.2_A1.15_T3 failing +S15.1.3.2_A1.15_T4 failing +S15.1.3.2_A1.15_T5 failing +S15.1.3.2_A1.15_T6 failing +S15.1.3.2_A1.1_T1 failing +S15.1.3.2_A1.2_T1 failing +S15.1.3.2_A1.2_T2 failing +S15.1.3.2_A1.3_T1 failing +S15.1.3.2_A1.3_T2 failing +S15.1.3.2_A1.4_T1 failing +S15.1.3.2_A1.5_T1 failing +S15.1.3.2_A1.6_T1 failing +S15.1.3.2_A1.7_T1 failing +S15.1.3.2_A1.8_T1 failing +S15.1.3.2_A1.8_T2 failing +S15.1.3.2_A1.9_T1 failing +S15.1.3.2_A1.9_T2 failing +S15.1.3.2_A1.9_T3 failing +S15.1.3.2_A2.1_T1 failing +S15.1.3.2_A2.2_T1 failing +S15.1.3.2_A2.3_T1 failing +S15.1.3.2_A2.4_T1 failing +S15.1.3.2_A3_T1 failing +S15.1.3.2_A3_T2 failing +S15.1.3.2_A3_T3 failing +S15.1.3.2_A4_T1 failing +S15.1.3.2_A4_T2 failing +S15.1.3.2_A4_T3 failing +S15.1.3.2_A4_T4 failing +S15.1.3.2_A5.1 failing +S15.1.3.2_A5.2 failing +S15.1.3.2_A5.3 failing 15.2.3.3-3-13 failing 15.2.3.3-3-14 failing 15.2.3.3-4-10 failing @@ -1942,18 +1129,6 @@ S15.2.3.1_A3 failing 15.2.3.3-4-220 failing 15.2.3.3-4-221 failing 15.2.3.3-4-222 failing -15.2.3.3-4-224 failing -15.2.3.3-4-226 failing -15.2.3.3-4-228 failing -15.2.3.3-4-230 failing -15.2.3.3-4-232 failing -15.2.3.3-4-234 failing -15.2.3.3-4-236 failing -15.2.3.3-4-238 failing -15.2.3.3-4-240 failing -15.2.3.3-4-242 failing -15.2.3.3-4-244 failing -15.2.3.3-4-246 failing 15.2.3.3-4-5 failing 15.2.3.3-4-51 failing 15.2.3.3-4-6 failing @@ -1961,239 +1136,67 @@ S15.2.3.1_A3 failing 15.2.3.3-4-9 failing 15.2.3.3-4-90 failing 15.2.3.4-0-2 failing -15.2.3.4-1-2 failing -15.2.3.4-1-3 failing -15.2.3.4-1-4 failing -15.2.3.4-1-5 failing -15.2.3.4-1 failing 15.2.3.4-2-1 failing -15.2.3.4-2-4 failing 15.2.3.4-3-1 failing 15.2.3.4-4-2 failing -15.2.3.4-4-40 failing -15.2.3.4-4-41 failing 15.2.3.4-4-42 failing 15.2.3.4-4-43 failing 15.2.3.4-4-44 failing -15.2.3.4-4-45 failing -15.2.3.4-4-46 failing 15.2.3.4-4-47 failing 15.2.3.4-4-48 failing 15.2.3.4-4-49 failing 15.2.3.4-4-b-1 failing 15.2.3.4-4-b-3 failing -15.2.3.4-4-b-4 failing 15.2.3.4-4-b-5 failing 15.2.3.4-4-b-6 failing 15.2.3.5-0-2 failing -15.2.3.5-1-1 failing 15.2.3.5-1-2 failing -15.2.3.5-1-3 failing -15.2.3.5-1-4 failing -15.2.3.5-1 failing -15.2.3.5-4-10 failing -15.2.3.5-4-117 failing 15.2.3.5-4-120 failing -15.2.3.5-4-124 failing 15.2.3.5-4-13 failing 15.2.3.5-4-145 failing -15.2.3.5-4-170 failing 15.2.3.5-4-173 failing -15.2.3.5-4-177 failing -15.2.3.5-4-196 failing 15.2.3.5-4-199 failing 15.2.3.5-4-2 failing -15.2.3.5-4-203 failing 15.2.3.5-4-224 failing -15.2.3.5-4-251 failing 15.2.3.5-4-252 failing -15.2.3.5-4-256 failing -15.2.3.5-4-258 failing -15.2.3.5-4-259 failing -15.2.3.5-4-26 failing -15.2.3.5-4-260 failing -15.2.3.5-4-261 failing -15.2.3.5-4-262 failing -15.2.3.5-4-27 failing -15.2.3.5-4-284 failing -15.2.3.5-4-287 failing -15.2.3.5-4-291 failing -15.2.3.5-4-293 failing -15.2.3.5-4-294 failing -15.2.3.5-4-295 failing -15.2.3.5-4-296 failing -15.2.3.5-4-297 failing -15.2.3.5-4-3 failing -15.2.3.5-4-300 failing -15.2.3.5-4-301 failing -15.2.3.5-4-302 failing -15.2.3.5-4-303 failing -15.2.3.5-4-304 failing -15.2.3.5-4-305 failing -15.2.3.5-4-306 failing -15.2.3.5-4-309 failing -15.2.3.5-4-315 failing -15.2.3.5-4-33 failing -15.2.3.5-4-36 failing -15.2.3.5-4-40 failing -15.2.3.5-4-41 failing -15.2.3.5-4-42 failing -15.2.3.5-4-43 failing -15.2.3.5-4-44 failing -15.2.3.5-4-45 failing -15.2.3.5-4-64 failing 15.2.3.5-4-67 failing -15.2.3.5-4-71 failing 15.2.3.5-4-92 failing 15.2.3.6-0-2 failing -15.2.3.6-1-1 failing -15.2.3.6-1-2 failing -15.2.3.6-1-3 failing -15.2.3.6-1-4 failing -15.2.3.6-1 failing 15.2.3.6-2-17-1 failing -15.2.3.6-2-47 failing -15.2.3.6-3-1 failing -15.2.3.6-3-10 failing -15.2.3.6-3-11 failing +15.2.3.5-4-287 failing +15.2.3.5-4-315 failing +15.2.3.5-4-36 failing +15.2.3.5-4-40 failing 15.2.3.6-3-119 failing -15.2.3.6-3-12 failing -15.2.3.6-3-13 failing -15.2.3.6-3-139-1 failing -15.2.3.6-3-14 failing -15.2.3.6-3-140-1 failing -15.2.3.6-3-141-1 failing -15.2.3.6-3-142-1 failing -15.2.3.6-3-143-1 failing -15.2.3.6-3-144-1 failing -15.2.3.6-3-144 failing -15.2.3.6-3-145-1 failing -15.2.3.6-3-146-1 failing 15.2.3.6-3-147-1 failing 15.2.3.6-3-147 failing -15.2.3.6-3-148-1 failing -15.2.3.6-3-149-1 failing -15.2.3.6-3-15 failing -15.2.3.6-3-151 failing -15.2.3.6-3-16 failing -15.2.3.6-3-165-1 failing -15.2.3.6-3-166-1 failing -15.2.3.6-3-167-1 failing -15.2.3.6-3-168-1 failing -15.2.3.6-3-169-1 failing -15.2.3.6-3-17 failing -15.2.3.6-3-170-1 failing -15.2.3.6-3-170 failing -15.2.3.6-3-171-1 failing -15.2.3.6-3-172-1 failing 15.2.3.6-3-173-1 failing 15.2.3.6-3-173 failing -15.2.3.6-3-174-1 failing -15.2.3.6-3-175-1 failing -15.2.3.6-3-177 failing -15.2.3.6-3-18 failing -15.2.3.6-3-19 failing 15.2.3.6-3-198 failing -15.2.3.6-3-2 failing -15.2.3.6-3-218-1 failing -15.2.3.6-3-219-1 failing -15.2.3.6-3-220-1 failing -15.2.3.6-3-221-1 failing -15.2.3.6-3-222-1 failing -15.2.3.6-3-223-1 failing -15.2.3.6-3-223 failing -15.2.3.6-3-224-1 failing -15.2.3.6-3-225-1 failing 15.2.3.6-3-226-1 failing 15.2.3.6-3-226 failing -15.2.3.6-3-227-1 failing -15.2.3.6-3-228-1 failing -15.2.3.6-3-230 failing -15.2.3.6-3-248-1 failing -15.2.3.6-3-249-1 failing -15.2.3.6-3-250-1 failing -15.2.3.6-3-251-1 failing -15.2.3.6-3-252-1 failing -15.2.3.6-3-253-1 failing -15.2.3.6-3-253 failing -15.2.3.6-3-254-1 failing -15.2.3.6-3-255-1 failing 15.2.3.6-3-256-1 failing 15.2.3.6-3-256 failing -15.2.3.6-3-257-1 failing -15.2.3.6-3-258-1 failing -15.2.3.6-3-260 failing -15.2.3.6-3-3 failing -15.2.3.6-3-33-1 failing -15.2.3.6-3-34-1 failing -15.2.3.6-3-35-1 failing -15.2.3.6-3-36-1 failing -15.2.3.6-3-37-1 failing -15.2.3.6-3-38-1 failing -15.2.3.6-3-38 failing -15.2.3.6-3-39-1 failing -15.2.3.6-3-4 failing -15.2.3.6-3-40-1 failing 15.2.3.6-3-41-1 failing 15.2.3.6-3-41 failing -15.2.3.6-3-42-1 failing -15.2.3.6-3-43-1 failing -15.2.3.6-3-45 failing -15.2.3.6-3-5 failing -15.2.3.6-3-6 failing 15.2.3.6-3-66 failing -15.2.3.6-3-7 failing -15.2.3.6-3-8 failing -15.2.3.6-3-86-1 failing -15.2.3.6-3-87-1 failing -15.2.3.6-3-88-1 failing -15.2.3.6-3-89-1 failing -15.2.3.6-3-9 failing -15.2.3.6-3-90-1 failing -15.2.3.6-3-91-1 failing -15.2.3.6-3-91 failing -15.2.3.6-3-92-1 failing -15.2.3.6-3-93-1 failing 15.2.3.6-3-94-1 failing 15.2.3.6-3-94 failing -15.2.3.6-3-95-1 failing -15.2.3.6-3-96-1 failing -15.2.3.6-3-98 failing -15.2.3.6-4-1 failing -15.2.3.6-4-10 failing -15.2.3.6-4-100 failing -15.2.3.6-4-101 failing -15.2.3.6-4-102 failing -15.2.3.6-4-103 failing -15.2.3.6-4-104 failing 15.2.3.6-4-105 failing -15.2.3.6-4-106 failing -15.2.3.6-4-107 failing 15.2.3.6-4-108 failing -15.2.3.6-4-109 failing -15.2.3.6-4-11 failing -15.2.3.6-4-110 failing 15.2.3.6-4-111 failing -15.2.3.6-4-112 failing -15.2.3.6-4-113 failing -15.2.3.6-4-114 failing -15.2.3.6-4-115 failing 15.2.3.6-4-116 failing 15.2.3.6-4-117 failing 15.2.3.6-4-118 failing 15.2.3.6-4-119 failing -15.2.3.6-4-12 failing 15.2.3.6-4-120 failing 15.2.3.6-4-121 failing 15.2.3.6-4-122 failing -15.2.3.6-4-123 failing -15.2.3.6-4-124 failing 15.2.3.6-4-125 failing 15.2.3.6-4-126 failing 15.2.3.6-4-127 failing 15.2.3.6-4-128 failing 15.2.3.6-4-129 failing -15.2.3.6-4-13 failing 15.2.3.6-4-130 failing 15.2.3.6-4-131 failing 15.2.3.6-4-132 failing @@ -2223,17 +1226,13 @@ S15.2.3.1_A3 failing 15.2.3.6-4-156 failing 15.2.3.6-4-157 failing 15.2.3.6-4-159 failing -15.2.3.6-4-16 failing 15.2.3.6-4-161 failing -15.2.3.6-4-162 failing 15.2.3.6-4-163 failing -15.2.3.6-4-164 failing 15.2.3.6-4-165 failing 15.2.3.6-4-166 failing 15.2.3.6-4-167 failing 15.2.3.6-4-168 failing 15.2.3.6-4-169 failing -15.2.3.6-4-17 failing 15.2.3.6-4-170 failing 15.2.3.6-4-171 failing 15.2.3.6-4-172 failing @@ -2243,366 +1242,116 @@ S15.2.3.1_A3 failing 15.2.3.6-4-176 failing 15.2.3.6-4-177 failing 15.2.3.6-4-179-1 failing -15.2.3.6-4-18 failing 15.2.3.6-4-181 failing 15.2.3.6-4-182 failing 15.2.3.6-4-183 failing 15.2.3.6-4-184 failing 15.2.3.6-4-185 failing 15.2.3.6-4-186 failing -15.2.3.6-4-187 failing 15.2.3.6-4-188 failing 15.2.3.6-4-189 failing -15.2.3.6-4-190 failing 15.2.3.6-4-191 failing 15.2.3.6-4-192 failing 15.2.3.6-4-193 failing -15.2.3.6-4-194 failing -15.2.3.6-4-195 failing -15.2.3.6-4-196 failing -15.2.3.6-4-197 failing -15.2.3.6-4-198 failing -15.2.3.6-4-199 failing 15.2.3.6-4-20 failing -15.2.3.6-4-200 failing -15.2.3.6-4-201 failing -15.2.3.6-4-202 failing -15.2.3.6-4-203 failing -15.2.3.6-4-204 failing -15.2.3.6-4-205 failing -15.2.3.6-4-207 failing -15.2.3.6-4-208 failing -15.2.3.6-4-209 failing 15.2.3.6-4-210 failing -15.2.3.6-4-211 failing -15.2.3.6-4-212 failing -15.2.3.6-4-213 failing -15.2.3.6-4-214 failing -15.2.3.6-4-215 failing -15.2.3.6-4-216 failing 15.2.3.6-4-217 failing -15.2.3.6-4-218 failing -15.2.3.6-4-219 failing -15.2.3.6-4-22 failing -15.2.3.6-4-220 failing -15.2.3.6-4-221 failing -15.2.3.6-4-222 failing -15.2.3.6-4-223 failing -15.2.3.6-4-224 failing -15.2.3.6-4-225 failing -15.2.3.6-4-226 failing -15.2.3.6-4-227 failing -15.2.3.6-4-228 failing -15.2.3.6-4-229 failing -15.2.3.6-4-230 failing -15.2.3.6-4-231 failing -15.2.3.6-4-232 failing -15.2.3.6-4-233 failing -15.2.3.6-4-234 failing -15.2.3.6-4-235 failing -15.2.3.6-4-236 failing -15.2.3.6-4-237 failing -15.2.3.6-4-238 failing -15.2.3.6-4-239 failing -15.2.3.6-4-24 failing -15.2.3.6-4-240 failing -15.2.3.6-4-241 failing 15.2.3.6-4-242-1 failing 15.2.3.6-4-242 failing -15.2.3.6-4-243-1 failing -15.2.3.6-4-243 failing -15.2.3.6-4-244 failing -15.2.3.6-4-245 failing -15.2.3.6-4-246 failing -15.2.3.6-4-247 failing -15.2.3.6-4-248 failing -15.2.3.6-4-249 failing -15.2.3.6-4-25 failing -15.2.3.6-4-250 failing -15.2.3.6-4-251 failing -15.2.3.6-4-252 failing -15.2.3.6-4-253 failing 15.2.3.6-4-255 failing 15.2.3.6-4-256 failing -15.2.3.6-4-257 failing 15.2.3.6-4-258 failing 15.2.3.6-4-259 failing -15.2.3.6-4-26 failing 15.2.3.6-4-260 failing 15.2.3.6-4-261 failing 15.2.3.6-4-262 failing 15.2.3.6-4-263 failing 15.2.3.6-4-264 failing -15.2.3.6-4-265 failing 15.2.3.6-4-266 failing -15.2.3.6-4-267 failing -15.2.3.6-4-268 failing 15.2.3.6-4-269 failing -15.2.3.6-4-270 failing 15.2.3.6-4-271 failing 15.2.3.6-4-272 failing -15.2.3.6-4-273 failing 15.2.3.6-4-274 failing 15.2.3.6-4-275 failing 15.2.3.6-4-276 failing -15.2.3.6-4-277 failing -15.2.3.6-4-278 failing -15.2.3.6-4-279 failing -15.2.3.6-4-28 failing 15.2.3.6-4-280 failing -15.2.3.6-4-281 failing -15.2.3.6-4-282 failing -15.2.3.6-4-283 failing -15.2.3.6-4-284 failing -15.2.3.6-4-285 failing -15.2.3.6-4-286 failing -15.2.3.6-4-287 failing -15.2.3.6-4-288 failing -15.2.3.6-4-289-1 failing -15.2.3.6-4-289 failing -15.2.3.6-4-29 failing -15.2.3.6-4-290-1 failing -15.2.3.6-4-290 failing 15.2.3.6-4-291-1 failing -15.2.3.6-4-291 failing 15.2.3.6-4-292-1 failing -15.2.3.6-4-292 failing 15.2.3.6-4-293-1 failing 15.2.3.6-4-293-2 failing 15.2.3.6-4-293-3 failing -15.2.3.6-4-293 failing 15.2.3.6-4-294-1 failing -15.2.3.6-4-294 failing 15.2.3.6-4-295-1 failing -15.2.3.6-4-295 failing 15.2.3.6-4-296-1 failing -15.2.3.6-4-296 failing 15.2.3.6-4-297-1 failing -15.2.3.6-4-297 failing 15.2.3.6-4-298-1 failing -15.2.3.6-4-298 failing 15.2.3.6-4-299-1 failing -15.2.3.6-4-299 failing -15.2.3.6-4-30 failing 15.2.3.6-4-300-1 failing -15.2.3.6-4-300 failing -15.2.3.6-4-301-1 failing -15.2.3.6-4-301 failing -15.2.3.6-4-302-1 failing -15.2.3.6-4-302 failing -15.2.3.6-4-303 failing -15.2.3.6-4-304 failing -15.2.3.6-4-305 failing -15.2.3.6-4-306 failing -15.2.3.6-4-307 failing -15.2.3.6-4-308 failing -15.2.3.6-4-309 failing -15.2.3.6-4-31 failing -15.2.3.6-4-310 failing -15.2.3.6-4-311 failing -15.2.3.6-4-312 failing -15.2.3.6-4-313-1 failing -15.2.3.6-4-313 failing -15.2.3.6-4-314-1 failing -15.2.3.6-4-314 failing -15.2.3.6-4-315-1 failing -15.2.3.6-4-315 failing -15.2.3.6-4-316-1 failing -15.2.3.6-4-316 failing -15.2.3.6-4-317-1 failing -15.2.3.6-4-317 failing -15.2.3.6-4-318-1 failing -15.2.3.6-4-318 failing -15.2.3.6-4-319-1 failing -15.2.3.6-4-319 failing -15.2.3.6-4-320-1 failing -15.2.3.6-4-320 failing -15.2.3.6-4-321-1 failing -15.2.3.6-4-321 failing -15.2.3.6-4-322-1 failing -15.2.3.6-4-322 failing -15.2.3.6-4-323-1 failing -15.2.3.6-4-323 failing -15.2.3.6-4-324-1 failing -15.2.3.6-4-324 failing 15.2.3.6-4-329 failing -15.2.3.6-4-33 failing 15.2.3.6-4-330 failing 15.2.3.6-4-331 failing -15.2.3.6-4-333-1 failing -15.2.3.6-4-333-2 failing -15.2.3.6-4-333-3 failing 15.2.3.6-4-333-4 failing -15.2.3.6-4-333-6 failing -15.2.3.6-4-333-7 failing -15.2.3.6-4-333-8 failing 15.2.3.6-4-336 failing -15.2.3.6-4-337 failing -15.2.3.6-4-338 failing 15.2.3.6-4-339-1 failing -15.2.3.6-4-339-2 failing -15.2.3.6-4-339-3 failing -15.2.3.6-4-339-4 failing -15.2.3.6-4-339 failing -15.2.3.6-4-34 failing 15.2.3.6-4-343 failing 15.2.3.6-4-344 failing 15.2.3.6-4-345 failing -15.2.3.6-4-35 failing 15.2.3.6-4-350 failing -15.2.3.6-4-351 failing -15.2.3.6-4-352 failing -15.2.3.6-4-353 failing -15.2.3.6-4-354-1 failing -15.2.3.6-4-354-10 failing -15.2.3.6-4-354-11 failing -15.2.3.6-4-354-12 failing 15.2.3.6-4-354-13 failing -15.2.3.6-4-354-2 failing -15.2.3.6-4-354-3 failing 15.2.3.6-4-354-4 failing 15.2.3.6-4-354-6 failing 15.2.3.6-4-354-8 failing 15.2.3.6-4-357 failing 15.2.3.6-4-358 failing 15.2.3.6-4-359 failing -15.2.3.6-4-36 failing 15.2.3.6-4-360-1 failing 15.2.3.6-4-360-3 failing 15.2.3.6-4-360-7 failing -15.2.3.6-4-364 failing -15.2.3.6-4-365 failing -15.2.3.6-4-366 failing -15.2.3.6-4-367 failing -15.2.3.6-4-37 failing 15.2.3.6-4-371 failing 15.2.3.6-4-372 failing 15.2.3.6-4-373 failing -15.2.3.6-4-378 failing -15.2.3.6-4-379 failing 15.2.3.6-4-38 failing -15.2.3.6-4-380 failing -15.2.3.6-4-381 failing -15.2.3.6-4-39 failing -15.2.3.6-4-40 failing 15.2.3.6-4-402 failing -15.2.3.6-4-403 failing -15.2.3.6-4-404 failing 15.2.3.6-4-405 failing -15.2.3.6-4-406 failing 15.2.3.6-4-407 failing -15.2.3.6-4-408 failing -15.2.3.6-4-409 failing 15.2.3.6-4-41 failing 15.2.3.6-4-410 failing -15.2.3.6-4-411 failing 15.2.3.6-4-417 failing 15.2.3.6-4-418 failing 15.2.3.6-4-419 failing -15.2.3.6-4-42 failing 15.2.3.6-4-420 failing 15.2.3.6-4-421 failing -15.2.3.6-4-43 failing -15.2.3.6-4-435 failing -15.2.3.6-4-436 failing -15.2.3.6-4-437 failing -15.2.3.6-4-438 failing -15.2.3.6-4-439 failing 15.2.3.6-4-45 failing -15.2.3.6-4-453 failing -15.2.3.6-4-454 failing -15.2.3.6-4-455 failing -15.2.3.6-4-456 failing -15.2.3.6-4-457 failing 15.2.3.6-4-463 failing -15.2.3.6-4-47 failing -15.2.3.6-4-471 failing 15.2.3.6-4-472 failing -15.2.3.6-4-473 failing -15.2.3.6-4-474 failing -15.2.3.6-4-475 failing -15.2.3.6-4-48 failing 15.2.3.6-4-481 failing -15.2.3.6-4-489 failing -15.2.3.6-4-49 failing 15.2.3.6-4-490 failing -15.2.3.6-4-491 failing -15.2.3.6-4-492 failing -15.2.3.6-4-493 failing 15.2.3.6-4-498 failing -15.2.3.6-4-50 failing 15.2.3.6-4-507 failing -15.2.3.6-4-508 failing -15.2.3.6-4-509 failing -15.2.3.6-4-51 failing -15.2.3.6-4-510 failing -15.2.3.6-4-511 failing 15.2.3.6-4-516 failing -15.2.3.6-4-52 failing 15.2.3.6-4-525 failing -15.2.3.6-4-526 failing -15.2.3.6-4-527 failing -15.2.3.6-4-528 failing -15.2.3.6-4-529 failing -15.2.3.6-4-53 failing -15.2.3.6-4-531-1 failing -15.2.3.6-4-531-10 failing -15.2.3.6-4-531-11 failing -15.2.3.6-4-531-12 failing 15.2.3.6-4-531-13 failing 15.2.3.6-4-531-17 failing -15.2.3.6-4-531-2 failing -15.2.3.6-4-531-3 failing 15.2.3.6-4-531-4 failing 15.2.3.6-4-531-6 failing 15.2.3.6-4-531-8 failing 15.2.3.6-4-534 failing 15.2.3.6-4-535 failing -15.2.3.6-4-538-1 failing -15.2.3.6-4-538-2 failing 15.2.3.6-4-538-3 failing -15.2.3.6-4-538-5 failing -15.2.3.6-4-538-6 failing 15.2.3.6-4-538-7 failing -15.2.3.6-4-538 failing -15.2.3.6-4-540-1 failing -15.2.3.6-4-540-2 failing -15.2.3.6-4-540-3 failing 15.2.3.6-4-540-4 failing -15.2.3.6-4-540-6 failing -15.2.3.6-4-540-7 failing -15.2.3.6-4-540-8 failing 15.2.3.6-4-543 failing 15.2.3.6-4-544 failing -15.2.3.6-4-545 failing -15.2.3.6-4-546 failing -15.2.3.6-4-547-1 failing -15.2.3.6-4-547-2 failing -15.2.3.6-4-547-3 failing -15.2.3.6-4-547-4 failing -15.2.3.6-4-547 failing -15.2.3.6-4-55 failing 15.2.3.6-4-552 failing 15.2.3.6-4-553 failing -15.2.3.6-4-56 failing 15.2.3.6-4-561 failing 15.2.3.6-4-562 failing -15.2.3.6-4-563 failing -15.2.3.6-4-564 failing -15.2.3.6-4-565 failing -15.2.3.6-4-57 failing 15.2.3.6-4-578 failing 15.2.3.6-4-579 failing -15.2.3.6-4-58 failing -15.2.3.6-4-580 failing 15.2.3.6-4-581 failing -15.2.3.6-4-582 failing 15.2.3.6-4-583 failing 15.2.3.6-4-584 failing -15.2.3.6-4-585 failing 15.2.3.6-4-586 failing -15.2.3.6-4-587 failing -15.2.3.6-4-59 failing 15.2.3.6-4-593 failing 15.2.3.6-4-594 failing 15.2.3.6-4-595 failing @@ -2610,7 +1359,6 @@ S15.2.3.1_A3 failing 15.2.3.6-4-597 failing 15.2.3.6-4-598 failing 15.2.3.6-4-599 failing -15.2.3.6-4-60 failing 15.2.3.6-4-600 failing 15.2.3.6-4-601 failing 15.2.3.6-4-602 failing @@ -2621,43 +1369,11 @@ S15.2.3.1_A3 failing 15.2.3.6-4-607 failing 15.2.3.6-4-608 failing 15.2.3.6-4-609 failing -15.2.3.6-4-61 failing 15.2.3.6-4-610 failing -15.2.3.6-4-611 failing -15.2.3.6-4-612 failing -15.2.3.6-4-613 failing -15.2.3.6-4-614 failing -15.2.3.6-4-615 failing -15.2.3.6-4-616 failing -15.2.3.6-4-617 failing -15.2.3.6-4-618 failing -15.2.3.6-4-619 failing -15.2.3.6-4-62 failing -15.2.3.6-4-620 failing 15.2.3.6-4-621 failing 15.2.3.6-4-622 failing 15.2.3.6-4-623 failing 15.2.3.6-4-624 failing -15.2.3.6-4-64 failing -15.2.3.6-4-65 failing -15.2.3.6-4-66 failing -15.2.3.6-4-67 failing -15.2.3.6-4-68 failing -15.2.3.6-4-69 failing -15.2.3.6-4-7 failing -15.2.3.6-4-70 failing -15.2.3.6-4-71 failing -15.2.3.6-4-72 failing -15.2.3.6-4-73 failing -15.2.3.6-4-74 failing -15.2.3.6-4-75 failing -15.2.3.6-4-76 failing -15.2.3.6-4-77 failing -15.2.3.6-4-78 failing -15.2.3.6-4-79 failing -15.2.3.6-4-8 failing -15.2.3.6-4-80 failing -15.2.3.6-4-81 failing 15.2.3.6-4-82-1 failing 15.2.3.6-4-82-10 failing 15.2.3.6-4-82-11 failing @@ -2682,93 +1398,27 @@ S15.2.3.1_A3 failing 15.2.3.6-4-82-7 failing 15.2.3.6-4-82-8 failing 15.2.3.6-4-82-9 failing -15.2.3.6-4-82 failing -15.2.3.6-4-83 failing -15.2.3.6-4-84 failing -15.2.3.6-4-86 failing -15.2.3.6-4-87 failing -15.2.3.6-4-88 failing -15.2.3.6-4-89 failing -15.2.3.6-4-9 failing -15.2.3.6-4-90 failing -15.2.3.6-4-91 failing -15.2.3.6-4-92 failing -15.2.3.6-4-93 failing -15.2.3.6-4-94 failing -15.2.3.6-4-95 failing -15.2.3.6-4-96 failing -15.2.3.6-4-97 failing -15.2.3.6-4-98 failing -15.2.3.6-4-99 failing 15.2.3.7-0-2 failing -15.2.3.7-1-1 failing -15.2.3.7-1-2 failing -15.2.3.7-1-3 failing -15.2.3.7-1-4 failing -15.2.3.7-1 failing -15.2.3.7-2-1 failing -15.2.3.7-2-11 failing 15.2.3.7-2-14 failing 15.2.3.7-2-15 failing -15.2.3.7-2-18 failing -15.2.3.7-2-2 failing -15.2.3.7-5-a-12 failing 15.2.3.7-5-a-15 failing 15.2.3.7-5-a-16 failing -15.2.3.7-5-b-1 failing 15.2.3.7-5-b-105 failing -15.2.3.7-5-b-130 failing 15.2.3.7-5-b-133 failing -15.2.3.7-5-b-137 failing -15.2.3.7-5-b-156 failing 15.2.3.7-5-b-159 failing -15.2.3.7-5-b-163 failing 15.2.3.7-5-b-184 failing -15.2.3.7-5-b-2 failing -15.2.3.7-5-b-209 failing 15.2.3.7-5-b-212 failing -15.2.3.7-5-b-216 failing -15.2.3.7-5-b-218 failing -15.2.3.7-5-b-219 failing -15.2.3.7-5-b-220 failing -15.2.3.7-5-b-221 failing -15.2.3.7-5-b-222 failing -15.2.3.7-5-b-227 failing -15.2.3.7-5-b-24 failing -15.2.3.7-5-b-244 failing 15.2.3.7-5-b-247 failing 15.2.3.7-5-b-248 failing -15.2.3.7-5-b-252 failing -15.2.3.7-5-b-253 failing -15.2.3.7-5-b-254 failing -15.2.3.7-5-b-255 failing -15.2.3.7-5-b-256 failing -15.2.3.7-5-b-257 failing -15.2.3.7-5-b-261 failing -15.2.3.7-5-b-262 failing -15.2.3.7-5-b-263 failing -15.2.3.7-5-b-264 failing 15.2.3.7-5-b-27 failing -15.2.3.7-5-b-3 failing -15.2.3.7-5-b-31 failing -15.2.3.7-5-b-4 failing -15.2.3.7-5-b-5 failing 15.2.3.7-5-b-52 failing -15.2.3.7-5-b-77 failing 15.2.3.7-5-b-80 failing -15.2.3.7-5-b-84 failing 15.2.3.7-6-a-1 failing 15.2.3.7-6-a-10 failing -15.2.3.7-6-a-100 failing -15.2.3.7-6-a-101 failing 15.2.3.7-6-a-102 failing -15.2.3.7-6-a-103 failing -15.2.3.7-6-a-104 failing 15.2.3.7-6-a-105 failing -15.2.3.7-6-a-106 failing 15.2.3.7-6-a-107 failing 15.2.3.7-6-a-108 failing -15.2.3.7-6-a-109 failing 15.2.3.7-6-a-110 failing 15.2.3.7-6-a-112 failing 15.2.3.7-6-a-113 failing @@ -2779,7 +1429,6 @@ S15.2.3.1_A3 failing 15.2.3.7-6-a-118 failing 15.2.3.7-6-a-119 failing 15.2.3.7-6-a-12 failing -15.2.3.7-6-a-120 failing 15.2.3.7-6-a-121 failing 15.2.3.7-6-a-122 failing 15.2.3.7-6-a-123 failing @@ -2817,10 +1466,8 @@ S15.2.3.1_A3 failing 15.2.3.7-6-a-152 failing 15.2.3.7-6-a-153 failing 15.2.3.7-6-a-155 failing -15.2.3.7-6-a-156 failing 15.2.3.7-6-a-157 failing 15.2.3.7-6-a-158 failing -15.2.3.7-6-a-159 failing 15.2.3.7-6-a-16 failing 15.2.3.7-6-a-160 failing 15.2.3.7-6-a-161 failing @@ -2859,44 +1506,21 @@ S15.2.3.1_A3 failing 15.2.3.7-6-a-192 failing 15.2.3.7-6-a-193 failing 15.2.3.7-6-a-194 failing -15.2.3.7-6-a-195 failing 15.2.3.7-6-a-197 failing 15.2.3.7-6-a-198 failing 15.2.3.7-6-a-199 failing -15.2.3.7-6-a-2 failing 15.2.3.7-6-a-20 failing -15.2.3.7-6-a-200 failing 15.2.3.7-6-a-204 failing -15.2.3.7-6-a-205 failing 15.2.3.7-6-a-206 failing -15.2.3.7-6-a-207 failing -15.2.3.7-6-a-208 failing -15.2.3.7-6-a-209 failing 15.2.3.7-6-a-21 failing -15.2.3.7-6-a-210 failing -15.2.3.7-6-a-211 failing -15.2.3.7-6-a-212 failing 15.2.3.7-6-a-213 failing 15.2.3.7-6-a-214 failing -15.2.3.7-6-a-215 failing -15.2.3.7-6-a-216 failing -15.2.3.7-6-a-217 failing -15.2.3.7-6-a-218 failing -15.2.3.7-6-a-219 failing 15.2.3.7-6-a-22 failing -15.2.3.7-6-a-220 failing -15.2.3.7-6-a-221 failing -15.2.3.7-6-a-222 failing -15.2.3.7-6-a-223 failing -15.2.3.7-6-a-224 failing -15.2.3.7-6-a-225 failing -15.2.3.7-6-a-226 failing 15.2.3.7-6-a-227 failing 15.2.3.7-6-a-228 failing 15.2.3.7-6-a-229 failing 15.2.3.7-6-a-230 failing 15.2.3.7-6-a-231 failing -15.2.3.7-6-a-232 failing 15.2.3.7-6-a-233 failing 15.2.3.7-6-a-234 failing 15.2.3.7-6-a-235 failing @@ -2908,10 +1532,8 @@ S15.2.3.1_A3 failing 15.2.3.7-6-a-240 failing 15.2.3.7-6-a-241 failing 15.2.3.7-6-a-242 failing -15.2.3.7-6-a-243 failing 15.2.3.7-6-a-244 failing 15.2.3.7-6-a-245 failing -15.2.3.7-6-a-246 failing 15.2.3.7-6-a-247 failing 15.2.3.7-6-a-248 failing 15.2.3.7-6-a-249 failing @@ -2920,21 +1542,13 @@ S15.2.3.1_A3 failing 15.2.3.7-6-a-251 failing 15.2.3.7-6-a-252 failing 15.2.3.7-6-a-253 failing -15.2.3.7-6-a-254 failing 15.2.3.7-6-a-255 failing -15.2.3.7-6-a-256 failing -15.2.3.7-6-a-257 failing 15.2.3.7-6-a-258 failing -15.2.3.7-6-a-259 failing 15.2.3.7-6-a-260 failing 15.2.3.7-6-a-261 failing -15.2.3.7-6-a-262 failing 15.2.3.7-6-a-263 failing 15.2.3.7-6-a-264 failing 15.2.3.7-6-a-265 failing -15.2.3.7-6-a-266 failing -15.2.3.7-6-a-267 failing -15.2.3.7-6-a-268 failing 15.2.3.7-6-a-269 failing 15.2.3.7-6-a-270 failing 15.2.3.7-6-a-271 failing @@ -2944,10 +1558,7 @@ S15.2.3.1_A3 failing 15.2.3.7-6-a-275 failing 15.2.3.7-6-a-276 failing 15.2.3.7-6-a-277 failing -15.2.3.7-6-a-278 failing -15.2.3.7-6-a-279 failing 15.2.3.7-6-a-280 failing -15.2.3.7-6-a-281 failing 15.2.3.7-6-a-282 failing 15.2.3.7-6-a-283 failing 15.2.3.7-6-a-284 failing @@ -2956,10 +1567,6 @@ S15.2.3.1_A3 failing 15.2.3.7-6-a-287 failing 15.2.3.7-6-a-288 failing 15.2.3.7-6-a-289 failing -15.2.3.7-6-a-290 failing -15.2.3.7-6-a-291 failing -15.2.3.7-6-a-292 failing -15.2.3.7-6-a-293 failing 15.2.3.7-6-a-294 failing 15.2.3.7-6-a-295 failing 15.2.3.7-6-a-296 failing @@ -2969,46 +1576,18 @@ S15.2.3.1_A3 failing 15.2.3.7-6-a-3 failing 15.2.3.7-6-a-300 failing 15.2.3.7-6-a-301 failing -15.2.3.7-6-a-302 failing -15.2.3.7-6-a-303 failing -15.2.3.7-6-a-304 failing -15.2.3.7-6-a-305 failing 15.2.3.7-6-a-306 failing 15.2.3.7-6-a-307 failing 15.2.3.7-6-a-308 failing 15.2.3.7-6-a-309 failing -15.2.3.7-6-a-31 failing 15.2.3.7-6-a-310 failing 15.2.3.7-6-a-311 failing 15.2.3.7-6-a-312 failing 15.2.3.7-6-a-313 failing -15.2.3.7-6-a-33 failing -15.2.3.7-6-a-35 failing -15.2.3.7-6-a-36 failing -15.2.3.7-6-a-37 failing -15.2.3.7-6-a-38-1 failing -15.2.3.7-6-a-38 failing -15.2.3.7-6-a-39 failing 15.2.3.7-6-a-4 failing -15.2.3.7-6-a-40 failing -15.2.3.7-6-a-41 failing -15.2.3.7-6-a-42 failing -15.2.3.7-6-a-43 failing 15.2.3.7-6-a-45 failing 15.2.3.7-6-a-46 failing -15.2.3.7-6-a-47 failing -15.2.3.7-6-a-48 failing -15.2.3.7-6-a-49 failing 15.2.3.7-6-a-5 failing -15.2.3.7-6-a-50 failing -15.2.3.7-6-a-51 failing -15.2.3.7-6-a-52 failing -15.2.3.7-6-a-53 failing -15.2.3.7-6-a-54 failing -15.2.3.7-6-a-55 failing -15.2.3.7-6-a-56 failing -15.2.3.7-6-a-59 failing -15.2.3.7-6-a-60 failing 15.2.3.7-6-a-61 failing 15.2.3.7-6-a-62 failing 15.2.3.7-6-a-63 failing @@ -3019,69 +1598,33 @@ S15.2.3.1_A3 failing 15.2.3.7-6-a-67 failing 15.2.3.7-6-a-68 failing 15.2.3.7-6-a-7 failing -15.2.3.7-6-a-70 failing 15.2.3.7-6-a-71 failing 15.2.3.7-6-a-72 failing -15.2.3.7-6-a-73 failing -15.2.3.7-6-a-74 failing 15.2.3.7-6-a-76 failing 15.2.3.7-6-a-77 failing -15.2.3.7-6-a-78 failing 15.2.3.7-6-a-79 failing 15.2.3.7-6-a-8 failing -15.2.3.7-6-a-80 failing 15.2.3.7-6-a-81 failing -15.2.3.7-6-a-82 failing 15.2.3.7-6-a-83 failing -15.2.3.7-6-a-84-1 failing -15.2.3.7-6-a-84 failing 15.2.3.7-6-a-85 failing 15.2.3.7-6-a-86-1 failing -15.2.3.7-6-a-86 failing 15.2.3.7-6-a-87 failing 15.2.3.7-6-a-88 failing 15.2.3.7-6-a-9 failing -15.2.3.7-6-a-90 failing 15.2.3.7-6-a-91 failing 15.2.3.7-6-a-92 failing 15.2.3.7-6-a-93-1 failing 15.2.3.7-6-a-93-2 failing 15.2.3.7-6-a-93-3 failing 15.2.3.7-6-a-93-4 failing -15.2.3.7-6-a-93 failing -15.2.3.7-6-a-94 failing -15.2.3.7-6-a-95 failing -15.2.3.7-6-a-96 failing 15.2.3.7-6-a-97 failing 15.2.3.7-6-a-98 failing 15.2.3.7-6-a-99 failing 15.2.3.8-0-2 failing -15.2.3.8-1-1 failing -15.2.3.8-1-2 failing -15.2.3.8-1-3 failing -15.2.3.8-1-4 failing -15.2.3.8-1 failing -15.2.3.8-2-b-1 failing -15.2.3.8-2-b-2 failing -15.2.3.8-2-b-3 failing -15.2.3.8-2-b-4 failing 15.2.3.9-0-2 failing -15.2.3.9-1-1 failing -15.2.3.9-1-2 failing -15.2.3.9-1-3 failing -15.2.3.9-1-4 failing -15.2.3.9-1 failing 15.2.3.9-2-a-11 failing 15.2.3.9-2-a-12 failing 15.2.3.9-2-a-14 failing -15.2.3.9-2-b-i-1 failing -15.2.3.9-2-b-i-2 failing -15.2.3.9-2-c-1 failing -15.2.3.9-2-c-3 failing -15.2.3.9-2-c-4 failing -S15.2.4_A1_T2 failing -S15.2.4_A3 failing -S15.2.4_A4 failing 15.2.4.2-1-1 failing 15.2.4.2-1-2 failing 15.2.4.2-2-1 failing @@ -3090,50 +1633,33 @@ S15.2.4.2_A10 failing S15.2.4.2_A11 failing S15.2.4.2_A12 failing S15.2.4.2_A13 failing -S15.2.4.2_A7 failing S15.2.4.2_A8 failing S15.2.4.2_A9 failing S15.2.4.3_A10 failing S15.2.4.3_A11 failing -S15.2.4.3_A7 failing S15.2.4.3_A8 failing S15.2.4.3_A9 failing S15.2.4.4_A10 failing S15.2.4.4_A11 failing -S15.2.4.4_A7 failing S15.2.4.4_A8 failing S15.2.4.4_A9 failing S15.2.4.5_A10 failing S15.2.4.5_A11 failing -S15.2.4.5_A7 failing S15.2.4.5_A8 failing S15.2.4.5_A9 failing S15.2.4.6_A10 failing S15.2.4.6_A11 failing -S15.2.4.6_A7 failing S15.2.4.6_A8 failing S15.2.4.6_A9 failing S15.2.4.7_A10 failing S15.2.4.7_A11 failing S15.2.4.7_A2_T1 failing -S15.2.4.7_A7 failing S15.2.4.7_A8 failing S15.2.4.7_A9 failing S15.3.1_A1_T1 failing S15.3_A2_T1 failing S15.3_A2_T2 failing -S15.3_A3_T3 failing -S15.3.2.1_A1_T1 failing -S15.3.2.1_A1_T10 failing -S15.3.2.1_A1_T11 failing -S15.3.2.1_A1_T12 failing S15.3.2.1_A1_T13 failing -S15.3.2.1_A1_T2 failing -S15.3.2.1_A1_T3 failing -S15.3.2.1_A1_T4 failing -S15.3.2.1_A1_T5 failing -S15.3.2.1_A1_T6 failing -S15.3.2.1_A1_T7 failing S15.3.2.1_A1_T8 failing S15.3.2.1_A1_T9 failing S15.3.2.1_A2_T1 failing @@ -3144,38 +1670,20 @@ S15.3.2.1_A2_T5 failing S15.3.2.1_A2_T6 failing S15.3.2.1_A3_T1 failing S15.3.2.1_A3_T10 failing -S15.3.2.1_A3_T11 failing -S15.3.2.1_A3_T12 failing -S15.3.2.1_A3_T13 failing -S15.3.2.1_A3_T14 failing -S15.3.2.1_A3_T15 failing S15.3.2.1_A3_T2 failing S15.3.2.1_A3_T3 failing -S15.3.2.1_A3_T4 failing -S15.3.2.1_A3_T5 failing S15.3.2.1_A3_T6 failing S15.3.2.1_A3_T7 failing -S15.3.2.1_A3_T8 failing S15.3.2.1_A3_T9 failing 15.3.2.1-11-1-s failing -15.3.2.1-11-2-s failing -15.3.2.1-11-3-s failing -15.3.2.1-11-4-s failing 15.3.2.1-11-5-s failing -15.3.2.1-11-6-s failing -15.3.2.1-11-7-s failing S15.3.3_A3 failing S15.3.3.1_A1 failing S15.3.3.1_A2 failing S15.3.3.1_A3 failing 15.3.3.2-1 failing -S15.3.4_A2_T1 failing -S15.3.4_A2_T2 failing -S15.3.4_A2_T3 failing -S15.3.4_A5 failing S15.3.4.2_A10 failing S15.3.4.2_A11 failing -S15.3.4.2_A7 failing S15.3.4.2_A8 failing S15.3.4.2_A9 failing 15.3.4.3-1-s failing @@ -3183,9 +1691,6 @@ S15.3.4.2_A9 failing 15.3.4.3-3-s failing S15.3.4.3_A10 failing S15.3.4.3_A11 failing -S15.3.4.3_A16 failing -S15.3.4.3_A1_T1 failing -S15.3.4.3_A1_T2 failing S15.3.4.3_A2_T1 failing S15.3.4.3_A2_T2 failing S15.3.4.3_A3_T1 failing @@ -3199,7 +1704,6 @@ S15.3.4.3_A3_T7 failing S15.3.4.3_A3_T8 failing S15.3.4.3_A3_T9 failing S15.3.4.3_A5_T4 failing -S15.3.4.3_A6_T2 failing S15.3.4.3_A6_T3 failing S15.3.4.3_A7_T1 failing S15.3.4.3_A7_T10 failing @@ -3211,11 +1715,6 @@ S15.3.4.3_A7_T6 failing S15.3.4.3_A7_T7 failing S15.3.4.3_A7_T8 failing S15.3.4.3_A7_T9 failing -S15.3.4.3_A8_T1 failing -S15.3.4.3_A8_T2 failing -S15.3.4.3_A8_T3 failing -S15.3.4.3_A8_T4 failing -S15.3.4.3_A8_T5 failing S15.3.4.3_A8_T6 failing S15.3.4.3_A9 failing 15.3.4.4-1-s failing @@ -3223,9 +1722,6 @@ S15.3.4.3_A9 failing 15.3.4.4-3-s failing S15.3.4.4_A10 failing S15.3.4.4_A11 failing -S15.3.4.4_A16 failing -S15.3.4.4_A1_T1 failing -S15.3.4.4_A1_T2 failing S15.3.4.4_A2_T1 failing S15.3.4.4_A2_T2 failing S15.3.4.4_A6_T1 failing @@ -3238,12 +1734,6 @@ S15.3.4.4_A6_T6 failing S15.3.4.4_A6_T7 failing S15.3.4.4_A6_T8 failing S15.3.4.4_A6_T9 failing -S15.3.4.4_A7_T1 failing -S15.3.4.4_A7_T2 failing -S15.3.4.4_A7_T3 failing -S15.3.4.4_A7_T4 failing -S15.3.4.4_A7_T5 failing -S15.3.4.4_A7_T6 failing S15.3.4.4_A9 failing 15.3.4.5-0-2 failing 15.3.4.5-10-1 failing @@ -3261,15 +1751,7 @@ S15.3.4.4_A9 failing 15.3.4.5-15-5 failing 15.3.4.5-16-1 failing 15.3.4.5-16-2 failing -15.3.4.5-2-1 failing -15.3.4.5-2-10 failing -15.3.4.5-2-11 failing -15.3.4.5-2-12 failing -15.3.4.5-2-13 failing -15.3.4.5-2-14 failing -15.3.4.5-2-15 failing 15.3.4.5-2-16 failing -15.3.4.5-2-2 failing 15.3.4.5-2-3 failing 15.3.4.5-2-4 failing 15.3.4.5-2-5 failing @@ -3304,7 +1786,6 @@ S15.3.4.4_A9 failing 15.3.4.5-8-2 failing 15.3.4.5-9-1 failing 15.3.4.5-9-2 failing -S15.3.4.5_A16 failing S15.3.4.5_A4 failing S15.3.4.5_A5 failing 15.3.4.5.1-4-1 failing @@ -3358,9 +1839,6 @@ S15.3.5.3_A1_T5 failing S15.3.5.3_A1_T6 failing S15.3.5.3_A1_T7 failing S15.3.5.3_A1_T8 failing -S15.3.5.3_A2_T2 failing -S15.3.5.3_A2_T5 failing -S15.3.5.3_A2_T6 failing S15.3.5_A2_T2 failing S15.3.5_A3_T2 failing 15.3.5.4_2-79gs failing @@ -3443,7 +1921,6 @@ S15.4.4.11_A3_T2 failing S15.4.4.11_A4_T1 failing S15.4.4.11_A4_T2 failing S15.4.4.11_A4_T3 failing -S15.4.4.11_A5_T1 failing S15.4.4.11_A6_T2 failing S15.4.4.11_A7.1 failing S15.4.4.11_A7.2 failing @@ -3891,9 +2368,7 @@ S15.4.4.13_A5.7 failing 15.4.4.16-2-17 failing 15.4.4.16-2-18 failing 15.4.4.16-2-19 failing -15.4.4.16-2-2 failing 15.4.4.16-2-3 failing -15.4.4.16-2-4 failing 15.4.4.16-2-5 failing 15.4.4.16-2-6 failing 15.4.4.16-2-7 failing @@ -3927,8 +2402,6 @@ S15.4.4.13_A5.7 failing 15.4.4.16-3-8 failing 15.4.4.16-3-9 failing 15.4.4.16-4-1 failing -15.4.4.16-4-10 failing -15.4.4.16-4-11 failing 15.4.4.16-4-15 failing 15.4.4.16-4-3 failing 15.4.4.16-4-4 failing @@ -3945,7 +2418,6 @@ S15.4.4.13_A5.7 failing 15.4.4.16-7-9 failing 15.4.4.16-7-b-1 failing 15.4.4.16-7-b-10 failing -15.4.4.16-7-b-11 failing 15.4.4.16-7-b-12 failing 15.4.4.16-7-b-13 failing 15.4.4.16-7-b-14 failing @@ -3982,9 +2454,7 @@ S15.4.4.13_A5.7 failing 15.4.4.16-7-c-i-3 failing 15.4.4.16-7-c-i-30 failing 15.4.4.16-7-c-i-31 failing -15.4.4.16-7-c-i-4 failing 15.4.4.16-7-c-i-5 failing -15.4.4.16-7-c-i-6 failing 15.4.4.16-7-c-i-7 failing 15.4.4.16-7-c-i-8 failing 15.4.4.16-7-c-i-9 failing @@ -4042,9 +2512,7 @@ S15.4.4.13_A5.7 failing 15.4.4.17-2-17 failing 15.4.4.17-2-18 failing 15.4.4.17-2-19 failing -15.4.4.17-2-2 failing 15.4.4.17-2-3 failing -15.4.4.17-2-4 failing 15.4.4.17-2-5 failing 15.4.4.17-2-6 failing 15.4.4.17-2-7 failing @@ -4078,10 +2546,7 @@ S15.4.4.13_A5.7 failing 15.4.4.17-3-8 failing 15.4.4.17-3-9 failing 15.4.4.17-4-1 failing -15.4.4.17-4-10 failing -15.4.4.17-4-11 failing 15.4.4.17-4-15 failing -15.4.4.17-4-2 failing 15.4.4.17-4-3 failing 15.4.4.17-4-4 failing 15.4.4.17-4-5 failing @@ -4097,7 +2562,6 @@ S15.4.4.13_A5.7 failing 15.4.4.17-7-9 failing 15.4.4.17-7-b-1 failing 15.4.4.17-7-b-10 failing -15.4.4.17-7-b-11 failing 15.4.4.17-7-b-12 failing 15.4.4.17-7-b-13 failing 15.4.4.17-7-b-14 failing @@ -4134,9 +2598,7 @@ S15.4.4.13_A5.7 failing 15.4.4.17-7-c-i-3 failing 15.4.4.17-7-c-i-30 failing 15.4.4.17-7-c-i-31 failing -15.4.4.17-7-c-i-4 failing 15.4.4.17-7-c-i-5 failing -15.4.4.17-7-c-i-6 failing 15.4.4.17-7-c-i-7 failing 15.4.4.17-7-c-i-8 failing 15.4.4.17-7-c-i-9 failing @@ -4152,7 +2614,6 @@ S15.4.4.13_A5.7 failing 15.4.4.17-7-c-ii-23 failing 15.4.4.17-7-c-ii-5 failing 15.4.4.17-7-c-ii-6 failing -15.4.4.17-7-c-ii-7 failing 15.4.4.17-7-c-ii-8 failing 15.4.4.17-7-c-iii-1 failing 15.4.4.17-7-c-iii-2 failing @@ -4195,7 +2656,6 @@ S15.4.4.13_A5.7 failing 15.4.4.18-2-18 failing 15.4.4.18-2-19 failing 15.4.4.18-2-3 failing -15.4.4.18-2-4 failing 15.4.4.18-2-5 failing 15.4.4.18-2-6 failing 15.4.4.18-2-7 failing @@ -4229,10 +2689,7 @@ S15.4.4.13_A5.7 failing 15.4.4.18-3-8 failing 15.4.4.18-3-9 failing 15.4.4.18-4-1 failing -15.4.4.18-4-10 failing -15.4.4.18-4-11 failing 15.4.4.18-4-15 failing -15.4.4.18-4-2 failing 15.4.4.18-4-3 failing 15.4.4.18-4-4 failing 15.4.4.18-4-5 failing @@ -4249,7 +2706,6 @@ S15.4.4.13_A5.7 failing 15.4.4.18-7-9 failing 15.4.4.18-7-b-1 failing 15.4.4.18-7-b-10 failing -15.4.4.18-7-b-11 failing 15.4.4.18-7-b-12 failing 15.4.4.18-7-b-13 failing 15.4.4.18-7-b-14 failing @@ -4286,9 +2742,7 @@ S15.4.4.13_A5.7 failing 15.4.4.18-7-c-i-3 failing 15.4.4.18-7-c-i-30 failing 15.4.4.18-7-c-i-31 failing -15.4.4.18-7-c-i-4 failing 15.4.4.18-7-c-i-5 failing -15.4.4.18-7-c-i-6 failing 15.4.4.18-7-c-i-7 failing 15.4.4.18-7-c-i-8 failing 15.4.4.18-7-c-i-9 failing @@ -4304,7 +2758,6 @@ S15.4.4.13_A5.7 failing 15.4.4.18-7-c-ii-23 failing 15.4.4.18-7-c-ii-5 failing 15.4.4.18-7-c-ii-6 failing -15.4.4.18-7-c-ii-7 failing 15.4.4.18-7-c-ii-8 failing 15.4.4.18-8-10 failing 15.4.4.18-8-2 failing @@ -4342,7 +2795,6 @@ S15.4.4.13_A5.7 failing 15.4.4.19-2-18 failing 15.4.4.19-2-19 failing 15.4.4.19-2-3 failing -15.4.4.19-2-4 failing 15.4.4.19-2-5 failing 15.4.4.19-2-6 failing 15.4.4.19-2-7 failing @@ -4376,10 +2828,7 @@ S15.4.4.13_A5.7 failing 15.4.4.19-3-8 failing 15.4.4.19-3-9 failing 15.4.4.19-4-1 failing -15.4.4.19-4-10 failing -15.4.4.19-4-11 failing 15.4.4.19-4-15 failing -15.4.4.19-4-2 failing 15.4.4.19-4-3 failing 15.4.4.19-4-4 failing 15.4.4.19-4-5 failing @@ -4397,7 +2846,6 @@ S15.4.4.13_A5.7 failing 15.4.4.19-8-8 failing 15.4.4.19-8-b-1 failing 15.4.4.19-8-b-10 failing -15.4.4.19-8-b-11 failing 15.4.4.19-8-b-12 failing 15.4.4.19-8-b-13 failing 15.4.4.19-8-b-15 failing @@ -4432,9 +2880,7 @@ S15.4.4.13_A5.7 failing 15.4.4.19-8-c-i-3 failing 15.4.4.19-8-c-i-30 failing 15.4.4.19-8-c-i-31 failing -15.4.4.19-8-c-i-4 failing 15.4.4.19-8-c-i-5 failing -15.4.4.19-8-c-i-6 failing 15.4.4.19-8-c-i-7 failing 15.4.4.19-8-c-i-8 failing 15.4.4.19-8-c-i-9 failing @@ -4450,7 +2896,6 @@ S15.4.4.13_A5.7 failing 15.4.4.19-8-c-ii-23 failing 15.4.4.19-8-c-ii-5 failing 15.4.4.19-8-c-ii-6 failing -15.4.4.19-8-c-ii-7 failing 15.4.4.19-8-c-ii-8 failing 15.4.4.19-8-c-iii-1 failing 15.4.4.19-8-c-iii-2 failing @@ -4467,7 +2912,6 @@ S15.4.4.13_A5.7 failing 15.4.4.19-9-8 failing 15.4.4.19-9-9 failing S15.4.4.2_A1_T1 failing -S15.4.4.2_A1_T4 failing S15.4.4.2_A3_T1 failing S15.4.4.2_A4.2 failing S15.4.4.2_A4.3 failing @@ -4501,7 +2945,6 @@ S15.4.4.2_A4.7 failing 15.4.4.20-2-18 failing 15.4.4.20-2-19 failing 15.4.4.20-2-3 failing -15.4.4.20-2-4 failing 15.4.4.20-2-5 failing 15.4.4.20-2-6 failing 15.4.4.20-2-7 failing @@ -4535,10 +2978,7 @@ S15.4.4.2_A4.7 failing 15.4.4.20-3-8 failing 15.4.4.20-3-9 failing 15.4.4.20-4-1 failing -15.4.4.20-4-10 failing -15.4.4.20-4-11 failing 15.4.4.20-4-15 failing -15.4.4.20-4-2 failing 15.4.4.20-4-3 failing 15.4.4.20-4-4 failing 15.4.4.20-4-5 failing @@ -4564,7 +3004,6 @@ S15.4.4.2_A4.7 failing 15.4.4.20-9-9 failing 15.4.4.20-9-b-1 failing 15.4.4.20-9-b-10 failing -15.4.4.20-9-b-11 failing 15.4.4.20-9-b-12 failing 15.4.4.20-9-b-13 failing 15.4.4.20-9-b-14 failing @@ -4601,9 +3040,7 @@ S15.4.4.2_A4.7 failing 15.4.4.20-9-c-i-3 failing 15.4.4.20-9-c-i-30 failing 15.4.4.20-9-c-i-31 failing -15.4.4.20-9-c-i-4 failing 15.4.4.20-9-c-i-5 failing -15.4.4.20-9-c-i-6 failing 15.4.4.20-9-c-i-7 failing 15.4.4.20-9-c-i-8 failing 15.4.4.20-9-c-i-9 failing @@ -4662,7 +3099,6 @@ S15.4.4.2_A4.7 failing 15.4.4.21-2-18 failing 15.4.4.21-2-19 failing 15.4.4.21-2-3 failing -15.4.4.21-2-4 failing 15.4.4.21-2-5 failing 15.4.4.21-2-6 failing 15.4.4.21-2-7 failing @@ -4696,10 +3132,7 @@ S15.4.4.2_A4.7 failing 15.4.4.21-3-8 failing 15.4.4.21-3-9 failing 15.4.4.21-4-1 failing -15.4.4.21-4-10 failing -15.4.4.21-4-11 failing 15.4.4.21-4-15 failing -15.4.4.21-4-2 failing 15.4.4.21-4-3 failing 15.4.4.21-4-4 failing 15.4.4.21-4-5 failing @@ -4710,8 +3143,6 @@ S15.4.4.2_A4.7 failing 15.4.4.21-5-1 failing 15.4.4.21-5-10 failing 15.4.4.21-5-11 failing -15.4.4.21-5-12 failing -15.4.4.21-5-13 failing 15.4.4.21-5-2 failing 15.4.4.21-5-3 failing 15.4.4.21-5-4 failing @@ -4719,7 +3150,6 @@ S15.4.4.2_A4.7 failing 15.4.4.21-5-6 failing 15.4.4.21-5-7 failing 15.4.4.21-5-8 failing -15.4.4.21-7-1 failing 15.4.4.21-7-2 failing 15.4.4.21-7-3 failing 15.4.4.21-7-4 failing @@ -4728,7 +3158,6 @@ S15.4.4.2_A4.7 failing 15.4.4.21-7-7 failing 15.4.4.21-7-8 failing 15.4.4.21-7-9 failing -15.4.4.21-8-b-1 failing 15.4.4.21-8-b-2 failing 15.4.4.21-8-b-3 failing 15.4.4.21-8-b-ii-1 failing @@ -4758,20 +3187,15 @@ S15.4.4.2_A4.7 failing 15.4.4.21-8-b-iii-1-31 failing 15.4.4.21-8-b-iii-1-32 failing 15.4.4.21-8-b-iii-1-33 failing -15.4.4.21-8-b-iii-1-4 failing 15.4.4.21-8-b-iii-1-5 failing -15.4.4.21-8-b-iii-1-6 failing 15.4.4.21-8-b-iii-1-7 failing 15.4.4.21-8-b-iii-1-8 failing 15.4.4.21-8-b-iii-1-9 failing 15.4.4.21-8-c-1 failing 15.4.4.21-8-c-2 failing 15.4.4.21-8-c-3 failing -15.4.4.21-8-c-4 failing 15.4.4.21-8-c-5 failing 15.4.4.21-8-c-6 failing -15.4.4.21-8-c-7 failing -15.4.4.21-8-c-8 failing 15.4.4.21-9-1 failing 15.4.4.21-9-10 failing 15.4.4.21-9-4 failing @@ -4791,7 +3215,6 @@ S15.4.4.2_A4.7 failing 15.4.4.21-9-b-20 failing 15.4.4.21-9-b-21 failing 15.4.4.21-9-b-23 failing -15.4.4.21-9-b-24 failing 15.4.4.21-9-b-25 failing 15.4.4.21-9-b-26 failing 15.4.4.21-9-b-27 failing @@ -4827,58 +3250,6 @@ S15.4.4.2_A4.7 failing 15.4.4.21-9-c-i-28 failing 15.4.4.21-9-c-i-29 failing 15.4.4.21-9-c-i-3 failing -15.4.4.21-9-c-i-30 failing -15.4.4.21-9-c-i-31 failing -15.4.4.21-9-c-i-32 failing -15.4.4.21-9-c-i-33 failing -15.4.4.21-9-c-i-4 failing -15.4.4.21-9-c-i-5 failing -15.4.4.21-9-c-i-6 failing -15.4.4.21-9-c-i-7 failing -15.4.4.21-9-c-i-8 failing -15.4.4.21-9-c-i-9 failing -15.4.4.21-9-c-ii-12 failing -15.4.4.21-9-c-ii-14 failing -15.4.4.21-9-c-ii-16 failing -15.4.4.21-9-c-ii-18 failing -15.4.4.21-9-c-ii-20 failing -15.4.4.21-9-c-ii-21 failing -15.4.4.21-9-c-ii-22 failing -15.4.4.21-9-c-ii-23 failing -15.4.4.21-9-c-ii-24 failing -15.4.4.21-9-c-ii-25 failing -15.4.4.21-9-c-ii-26 failing -15.4.4.21-9-c-ii-27 failing -15.4.4.21-9-c-ii-28 failing -15.4.4.21-9-c-ii-29 failing -15.4.4.21-9-c-ii-30 failing -15.4.4.21-9-c-ii-31 failing -15.4.4.21-9-c-ii-32 failing -15.4.4.21-9-c-ii-33 failing -15.4.4.21-9-c-ii-34 failing -15.4.4.21-9-c-ii-35 failing -15.4.4.21-9-c-ii-37 failing -15.4.4.21-9-c-ii-4 failing -15.4.4.21-9-c-ii-5 failing -15.4.4.21-9-c-ii-7 failing -15.4.4.21-9-c-ii-8 failing -15.4.4.21-9-c-ii-9 failing -15.4.4.22-0-2 failing -15.4.4.22-1-1 failing -15.4.4.22-1-10 failing -15.4.4.22-1-11 failing -15.4.4.22-1-12 failing -15.4.4.22-1-13 failing -15.4.4.22-1-14 failing -15.4.4.22-1-15 failing -15.4.4.22-1-2 failing -15.4.4.22-1-3 failing -15.4.4.22-1-4 failing -15.4.4.22-1-5 failing -15.4.4.22-1-6 failing -15.4.4.22-1-7 failing -15.4.4.22-1-8 failing -15.4.4.22-1-9 failing 15.4.4.22-10-3 failing 15.4.4.22-10-4 failing 15.4.4.22-10-6 failing @@ -4895,7 +3266,6 @@ S15.4.4.2_A4.7 failing 15.4.4.22-2-18 failing 15.4.4.22-2-19 failing 15.4.4.22-2-3 failing -15.4.4.22-2-4 failing 15.4.4.22-2-5 failing 15.4.4.22-2-6 failing 15.4.4.22-2-7 failing @@ -4929,10 +3299,7 @@ S15.4.4.2_A4.7 failing 15.4.4.22-3-8 failing 15.4.4.22-3-9 failing 15.4.4.22-4-1 failing -15.4.4.22-4-10 failing -15.4.4.22-4-11 failing 15.4.4.22-4-15 failing -15.4.4.22-4-2 failing 15.4.4.22-4-3 failing 15.4.4.22-4-4 failing 15.4.4.22-4-5 failing @@ -4940,11 +3307,60 @@ S15.4.4.2_A4.7 failing 15.4.4.22-4-7 failing 15.4.4.22-4-8 failing 15.4.4.22-4-9 failing +15.4.4.21-9-c-i-30 failing +15.4.4.21-9-c-i-31 failing +15.4.4.21-9-c-i-32 failing +15.4.4.21-9-c-i-33 failing +15.4.4.21-9-c-i-4 failing +15.4.4.21-9-c-i-5 failing +15.4.4.21-9-c-i-6 failing +15.4.4.21-9-c-i-7 failing +15.4.4.21-9-c-i-8 failing +15.4.4.21-9-c-i-9 failing +15.4.4.21-9-c-ii-12 failing +15.4.4.21-9-c-ii-14 failing +15.4.4.21-9-c-ii-16 failing +15.4.4.21-9-c-ii-18 failing +15.4.4.21-9-c-ii-20 failing +15.4.4.21-9-c-ii-21 failing +15.4.4.21-9-c-ii-22 failing +15.4.4.21-9-c-ii-23 failing +15.4.4.21-9-c-ii-24 failing +15.4.4.21-9-c-ii-25 failing +15.4.4.21-9-c-ii-26 failing +15.4.4.21-9-c-ii-27 failing +15.4.4.21-9-c-ii-28 failing +15.4.4.21-9-c-ii-29 failing +15.4.4.21-9-c-ii-30 failing +15.4.4.21-9-c-ii-31 failing +15.4.4.21-9-c-ii-32 failing +15.4.4.21-9-c-ii-33 failing +15.4.4.21-9-c-ii-34 failing +15.4.4.21-9-c-ii-35 failing +15.4.4.21-9-c-ii-37 failing +15.4.4.21-9-c-ii-4 failing +15.4.4.21-9-c-ii-5 failing +15.4.4.21-9-c-ii-8 failing +15.4.4.21-9-c-ii-9 failing +15.4.4.22-0-2 failing +15.4.4.22-1-1 failing +15.4.4.22-1-10 failing +15.4.4.22-1-11 failing +15.4.4.22-1-12 failing +15.4.4.22-1-13 failing +15.4.4.22-1-14 failing +15.4.4.22-1-15 failing +15.4.4.22-1-2 failing +15.4.4.22-1-3 failing +15.4.4.22-1-4 failing +15.4.4.22-1-5 failing +15.4.4.22-1-6 failing +15.4.4.22-1-7 failing +15.4.4.22-1-8 failing +15.4.4.22-1-9 failing 15.4.4.22-5-1 failing 15.4.4.22-5-10 failing 15.4.4.22-5-11 failing -15.4.4.22-5-12 failing -15.4.4.22-5-13 failing 15.4.4.22-5-2 failing 15.4.4.22-5-3 failing 15.4.4.22-5-4 failing @@ -4952,7 +3368,6 @@ S15.4.4.2_A4.7 failing 15.4.4.22-5-6 failing 15.4.4.22-5-7 failing 15.4.4.22-5-8 failing -15.4.4.22-7-1 failing 15.4.4.22-7-2 failing 15.4.4.22-7-3 failing 15.4.4.22-7-4 failing @@ -4961,7 +3376,6 @@ S15.4.4.2_A4.7 failing 15.4.4.22-7-7 failing 15.4.4.22-7-8 failing 15.4.4.22-7-9 failing -15.4.4.22-8-b-1 failing 15.4.4.22-8-b-2 failing 15.4.4.22-8-b-3 failing 15.4.4.22-8-b-ii-1 failing @@ -4991,20 +3405,15 @@ S15.4.4.2_A4.7 failing 15.4.4.22-8-b-iii-1-31 failing 15.4.4.22-8-b-iii-1-32 failing 15.4.4.22-8-b-iii-1-33 failing -15.4.4.22-8-b-iii-1-4 failing 15.4.4.22-8-b-iii-1-5 failing -15.4.4.22-8-b-iii-1-6 failing 15.4.4.22-8-b-iii-1-7 failing 15.4.4.22-8-b-iii-1-8 failing 15.4.4.22-8-b-iii-1-9 failing 15.4.4.22-8-c-1 failing 15.4.4.22-8-c-2 failing 15.4.4.22-8-c-3 failing -15.4.4.22-8-c-4 failing 15.4.4.22-8-c-5 failing 15.4.4.22-8-c-6 failing -15.4.4.22-8-c-7 failing -15.4.4.22-8-c-8 failing 15.4.4.22-9-4 failing 15.4.4.22-9-6 failing 15.4.4.22-9-8 failing @@ -5023,7 +3432,6 @@ S15.4.4.2_A4.7 failing 15.4.4.22-9-b-20 failing 15.4.4.22-9-b-21 failing 15.4.4.22-9-b-23 failing -15.4.4.22-9-b-24 failing 15.4.4.22-9-b-25 failing 15.4.4.22-9-b-26 failing 15.4.4.22-9-b-28 failing @@ -5061,7 +3469,6 @@ S15.4.4.2_A4.7 failing 15.4.4.22-9-c-i-30 failing 15.4.4.22-9-c-i-31 failing 15.4.4.22-9-c-i-32 failing -15.4.4.22-9-c-i-33 failing 15.4.4.22-9-c-i-4 failing 15.4.4.22-9-c-i-5 failing 15.4.4.22-9-c-i-6 failing @@ -5090,7 +3497,6 @@ S15.4.4.2_A4.7 failing 15.4.4.22-9-c-ii-37 failing 15.4.4.22-9-c-ii-4 failing 15.4.4.22-9-c-ii-5 failing -15.4.4.22-9-c-ii-7 failing 15.4.4.22-9-c-ii-8 failing S15.4.4.3_A1_T1 failing S15.4.4.3_A3_T1 failing @@ -5107,101 +3513,40 @@ S15.4.4.4_A3_T1 failing S15.4.4.4_A4.1 failing S15.4.4.4_A4.2 failing S15.4.4.4_A4.3 failing -S15.4.4.4_A4.4 failing -S15.4.4.4_A4.7 failing -S15.4.4.5_A1.1_T1 failing -S15.4.4.5_A2_T4 failing -S15.4.4.5_A3.1_T2 failing -S15.4.4.5_A3.2_T2 failing -S15.4.4.5_A5_T1 failing -S15.4.4.5_A6.1 failing -S15.4.4.5_A6.2 failing -S15.4.4.5_A6.3 failing -S15.4.4.5_A6.4 failing -S15.4.4.5_A6.7 failing -S15.4.4.6_A1.1_T1 failing -S15.4.4.6_A1.2_T1 failing -S15.4.4.6_A2_T4 failing -S15.4.4.6_A3_T2 failing -S15.4.4.6_A3_T3 failing -S15.4.4.6_A4_T1 failing -S15.4.4.6_A4_T2 failing -S15.4.4.6_A5.1 failing -S15.4.4.6_A5.2 failing -S15.4.4.6_A5.3 failing -S15.4.4.6_A5.4 failing -S15.4.4.6_A5.7 failing -S15.4.4.7_A2_T3 failing -S15.4.4.7_A3 failing -S15.4.4.7_A4_T2 failing -S15.4.4.7_A4_T3 failing -S15.4.4.7_A6.1 failing -S15.4.4.7_A6.2 failing -S15.4.4.7_A6.3 failing -S15.4.4.7_A6.4 failing -S15.4.4.7_A6.7 failing -S15.4.4.8_A1_T1 failing -S15.4.4.8_A1_T2 failing -S15.4.4.8_A2_T1 failing -S15.4.4.8_A2_T2 failing -S15.4.4.8_A2_T3 failing -S15.4.4.8_A3_T1 failing -S15.4.4.8_A3_T2 failing -S15.4.4.8_A3_T3 failing -S15.4.4.8_A4_T1 failing -S15.4.4.8_A4_T2 failing -S15.4.4.8_A5.1 failing -S15.4.4.8_A5.2 failing -S15.4.4.8_A5.3 failing -S15.4.4.8_A5.4 failing -S15.4.4.8_A5.7 failing -S15.4.4.9_A1.1_T1 failing -S15.4.4.9_A1.2_T1 failing -S15.4.4.9_A2_T1 failing -S15.4.4.9_A2_T2 failing -S15.4.4.9_A2_T3 failing -S15.4.4.9_A2_T4 failing -S15.4.4.9_A2_T5 failing -S15.4.4.9_A3_T1 failing -S15.4.4.9_A3_T2 failing -S15.4.4.9_A3_T3 failing -S15.4.4.9_A4_T1 failing -S15.4.4.9_A4_T2 failing -S15.4.4.9_A5.1 failing -S15.4.4.9_A5.2 failing -S15.4.4.9_A5.3 failing -S15.4.4.9_A5.4 failing -S15.4.4.9_A5.7 failing -15.4.5.1-3.d-1 failing -15.4.5.1-3.d-2 failing -15.4.5.1-3.d-3 failing -15.4.5.1-5-1 failing -15.4.5.1-5-2 failing -S15.4.5.1_A1.1_T1 failing -S15.4.5.1_A1.1_T2 failing -S15.4.5.1_A1.2_T1 failing -S15.4.5.1_A1.2_T2 failing -S15.4.5.1_A1.2_T3 failing -S15.4.5.1_A1.3_T1 failing -S15.4.5.1_A1.3_T2 failing -S15.4.5.1_A2.1_T1 failing -S15.4.5.2_A1_T2 failing -S15.4.5.2_A3_T1 failing -S15.4.5.2_A3_T2 failing -S15.4.5.2_A3_T3 failing -S15.5.2.1_A1_T12 failing -S15.5.2.1_A1_T13 failing +S15.4.4.4_A4.4 failing +S15.4.4.4_A4.7 failing +S15.4.4.5_A1.1_T1 failing +S15.4.4.5_A5_T1 failing +S15.4.4.5_A6.1 failing +S15.4.4.5_A6.2 failing +S15.4.4.5_A6.3 failing +S15.4.4.5_A6.4 failing +S15.4.4.5_A6.7 failing +S15.4.4.6_A1.1_T1 failing +S15.4.4.6_A1.2_T1 failing +S15.4.4.6_A3_T2 failing +S15.4.4.6_A3_T3 failing +S15.4.4.6_A4_T1 failing +S15.4.4.6_A4_T2 failing +S15.4.4.6_A5.1 failing +S15.4.4.6_A5.2 failing +S15.4.4.6_A5.3 failing +S15.4.4.6_A5.4 failing +S15.4.4.6_A5.7 failing +S15.4.4.7_A3 failing +S15.4.4.7_A4_T2 failing +S15.4.4.7_A4_T3 failing +S15.4.4.7_A6.1 failing +S15.4.4.7_A6.2 failing +S15.4.4.7_A6.3 failing +S15.4.4.7_A6.4 failing S15.5.3.1_A2 failing S15.5.3.1_A3 failing S15.5.3.1_A4 failing S15.5.3.2_A1 failing S15.5.3.2_A4 failing S15.5.3_A1 failing -S15.5.4.2_A2_T1 failing -S15.5.4.2_A2_T2 failing S15.5.4.2_A4_T1 failing -S15.5.4.3_A2_T1 failing -S15.5.4.3_A2_T2 failing S15.5.4.10_A10 failing S15.5.4.10_A11 failing S15.5.4.10_A1_T1 failing @@ -5236,7 +3581,6 @@ S15.5.4.10_A2_T6 failing S15.5.4.10_A2_T7 failing S15.5.4.10_A2_T8 failing S15.5.4.10_A2_T9 failing -S15.5.4.10_A7 failing S15.5.4.10_A8 failing S15.5.4.10_A9 failing 15.5.4.11-1 failing @@ -5277,7 +3621,6 @@ S15.5.4.11_A4_T2 failing S15.5.4.11_A4_T3 failing S15.5.4.11_A4_T4 failing S15.5.4.11_A5_T1 failing -S15.5.4.11_A7 failing S15.5.4.11_A8 failing S15.5.4.11_A9 failing S15.5.4.12_A1.1_T1 failing @@ -5314,17 +3657,65 @@ S15.5.4.13_A1_T1 failing S15.5.4.13_A1_T10 failing S15.5.4.13_A1_T11 failing S15.5.4.13_A1_T12 failing -S15.5.4.13_A1_T13 failing S15.5.4.13_A1_T15 failing S15.5.4.13_A1_T2 failing S15.5.4.13_A1_T5 failing +S15.4.4.7_A6.7 failing +S15.4.4.8_A1_T1 failing +S15.4.4.8_A1_T2 failing +S15.4.4.8_A2_T1 failing +S15.4.4.8_A2_T2 failing +S15.4.4.8_A2_T3 failing +S15.4.4.8_A3_T1 failing +S15.4.4.8_A3_T2 failing +S15.4.4.8_A3_T3 failing +S15.4.4.8_A4_T1 failing +S15.4.4.8_A4_T2 failing +S15.4.4.8_A5.1 failing +S15.4.4.8_A5.2 failing +S15.4.4.8_A5.3 failing +S15.4.4.8_A5.4 failing +S15.4.4.8_A5.7 failing +S15.4.4.9_A1.1_T1 failing +S15.4.4.9_A1.2_T1 failing +S15.4.4.9_A2_T1 failing +S15.4.4.9_A2_T2 failing +S15.4.4.9_A2_T3 failing +S15.4.4.9_A2_T4 failing +S15.4.4.9_A2_T5 failing +S15.4.4.9_A3_T1 failing +S15.4.4.9_A3_T2 failing +S15.4.4.9_A3_T3 failing +S15.4.4.9_A4_T1 failing +S15.4.4.9_A4_T2 failing +S15.4.4.9_A5.1 failing +S15.4.4.9_A5.2 failing +S15.4.4.9_A5.3 failing +S15.4.4.9_A5.4 failing +S15.4.4.9_A5.7 failing +15.4.5.1-3.d-1 failing +15.4.5.1-3.d-2 failing +15.4.5.1-3.d-3 failing +15.4.5.1-5-1 failing +15.4.5.1-5-2 failing +S15.4.5.1_A1.1_T1 failing +S15.4.5.1_A1.1_T2 failing +S15.4.5.1_A1.2_T1 failing +S15.4.5.1_A1.2_T2 failing +S15.4.5.1_A1.2_T3 failing +S15.4.5.1_A1.3_T1 failing +S15.4.5.1_A1.3_T2 failing +S15.4.5.1_A2.1_T1 failing +S15.4.5.2_A1_T2 failing +S15.4.5.2_A3_T1 failing +S15.4.5.2_A3_T2 failing +S15.4.5.2_A3_T3 failing S15.5.4.13_A2_T2 failing S15.5.4.13_A2_T7 failing S15.5.4.13_A3_T1 failing S15.5.4.13_A3_T2 failing S15.5.4.13_A3_T3 failing S15.5.4.13_A3_T4 failing -S15.5.4.13_A7 failing S15.5.4.13_A8 failing S15.5.4.13_A9 failing S15.5.4.14_A10 failing @@ -5426,7 +3817,6 @@ S15.5.4.14_A4_T6 failing S15.5.4.14_A4_T7 failing S15.5.4.14_A4_T8 failing S15.5.4.14_A4_T9 failing -S15.5.4.14_A7 failing S15.5.4.14_A8 failing S15.5.4.14_A9 failing S15.5.4.15_A10 failing @@ -5435,7 +3825,6 @@ S15.5.4.15_A1_T1 failing S15.5.4.15_A1_T10 failing S15.5.4.15_A1_T11 failing S15.5.4.15_A1_T12 failing -S15.5.4.15_A1_T13 failing S15.5.4.15_A1_T15 failing S15.5.4.15_A1_T2 failing S15.5.4.15_A1_T5 failing @@ -5454,7 +3843,6 @@ S15.5.4.15_A3_T6 failing S15.5.4.15_A3_T7 failing S15.5.4.15_A3_T8 failing S15.5.4.15_A3_T9 failing -S15.5.4.15_A7 failing S15.5.4.15_A8 failing S15.5.4.15_A9 failing S15.5.4.16_A10 failing @@ -5469,7 +3857,6 @@ S15.5.4.16_A1_T2 failing S15.5.4.16_A1_T6 failing S15.5.4.16_A1_T7 failing S15.5.4.16_A1_T8 failing -S15.5.4.16_A7 failing S15.5.4.16_A8 failing S15.5.4.16_A9 failing S15.5.4.17_A10 failing @@ -5484,7 +3871,6 @@ S15.5.4.17_A1_T2 failing S15.5.4.17_A1_T6 failing S15.5.4.17_A1_T7 failing S15.5.4.17_A1_T8 failing -S15.5.4.17_A7 failing S15.5.4.17_A8 failing S15.5.4.17_A9 failing S15.5.4.18_A10 failing @@ -5499,7 +3885,6 @@ S15.5.4.18_A1_T2 failing S15.5.4.18_A1_T6 failing S15.5.4.18_A1_T7 failing S15.5.4.18_A1_T8 failing -S15.5.4.18_A7 failing S15.5.4.18_A8 failing S15.5.4.18_A9 failing S15.5.4.19_A10 failing @@ -5514,13 +3899,10 @@ S15.5.4.19_A1_T2 failing S15.5.4.19_A1_T6 failing S15.5.4.19_A1_T7 failing S15.5.4.19_A1_T8 failing -S15.5.4.19_A7 failing S15.5.4.19_A8 failing S15.5.4.19_A9 failing 15.5.4.20-0-1 failing 15.5.4.20-0-2 failing -15.5.4.20-1-1 failing -15.5.4.20-1-2 failing 15.5.4.20-1-3 failing 15.5.4.20-1-4 failing 15.5.4.20-1-5 failing @@ -5593,6 +3975,36 @@ S15.5.4.19_A9 failing 15.5.4.20-3-8 failing 15.5.4.20-3-9 failing 15.5.4.20-4-1 failing +S15.5.4.4_A5 failing +S15.5.4.4_A8 failing +S15.5.4.4_A9 failing +S15.5.4.5_A1.1 failing +S15.5.4.5_A10 failing +S15.5.4.5_A11 failing +S15.5.4.5_A1_T1 failing +S15.5.4.5_A1_T10 failing +S15.5.4.5_A1_T2 failing +S15.5.4.5_A2 failing +S15.5.4.5_A4 failing +S15.5.4.5_A8 failing +S15.5.4.5_A9 failing +S15.5.4.6_A10 failing +S15.5.4.6_A11 failing +S15.5.4.6_A1_T1 failing +S15.5.4.6_A1_T10 failing +S15.5.4.6_A1_T2 failing +S15.5.4.6_A2 failing +S15.5.4.6_A4_T1 failing +S15.5.4.6_A4_T2 failing +S15.5.4.6_A8 failing +S15.5.4.6_A9 failing +S15.5.4.7_A10 failing +S15.5.4.7_A11 failing +S15.5.4.7_A1_T1 failing +S15.5.4.7_A1_T10 failing +S15.5.4.7_A1_T11 failing +S15.5.4.7_A1_T12 failing +S15.5.4.7_A1_T2 failing 15.5.4.20-4-10 failing 15.5.4.20-4-11 failing 15.5.4.20-4-12 failing @@ -5650,44 +4062,10 @@ S15.5.4.4_A1_T1 failing S15.5.4.4_A1_T10 failing S15.5.4.4_A1_T2 failing S15.5.4.4_A2 failing -S15.5.4.4_A5 failing -S15.5.4.4_A7 failing -S15.5.4.4_A8 failing -S15.5.4.4_A9 failing -S15.5.4.5_A1.1 failing -S15.5.4.5_A10 failing -S15.5.4.5_A11 failing -S15.5.4.5_A1_T1 failing -S15.5.4.5_A1_T10 failing -S15.5.4.5_A1_T2 failing -S15.5.4.5_A2 failing -S15.5.4.5_A4 failing -S15.5.4.5_A7 failing -S15.5.4.5_A8 failing -S15.5.4.5_A9 failing -S15.5.4.6_A10 failing -S15.5.4.6_A11 failing -S15.5.4.6_A1_T1 failing -S15.5.4.6_A1_T10 failing -S15.5.4.6_A1_T2 failing -S15.5.4.6_A2 failing -S15.5.4.6_A4_T1 failing -S15.5.4.6_A4_T2 failing -S15.5.4.6_A7 failing -S15.5.4.6_A8 failing -S15.5.4.6_A9 failing -S15.5.4.7_A10 failing -S15.5.4.7_A11 failing -S15.5.4.7_A1_T1 failing -S15.5.4.7_A1_T10 failing -S15.5.4.7_A1_T11 failing -S15.5.4.7_A1_T12 failing -S15.5.4.7_A1_T2 failing S15.5.4.7_A4_T1 failing S15.5.4.7_A4_T2 failing S15.5.4.7_A4_T4 failing S15.5.4.7_A4_T5 failing -S15.5.4.7_A7 failing S15.5.4.7_A8 failing S15.5.4.7_A9 failing S15.5.4.8_A10 failing @@ -5701,12 +4079,10 @@ S15.5.4.8_A4_T1 failing S15.5.4.8_A4_T2 failing S15.5.4.8_A4_T4 failing S15.5.4.8_A4_T5 failing -S15.5.4.8_A7 failing S15.5.4.8_A8 failing S15.5.4.8_A9 failing S15.5.4.9_A10 failing S15.5.4.9_A11 failing -S15.5.4.9_A7 failing S15.5.4.9_A8 failing S15.5.4.9_A9 failing S15.5.5.1_A1 failing @@ -5714,10 +4090,6 @@ S15.5.5.1_A2 failing S15.5.5.1_A3 failing S15.5.5.1_A4 failing S15.5.5.1_A5 failing -S15.5.5_A1_T1 failing -S15.5.5_A1_T2 failing -S15.5.5_A2_T1 failing -S15.5.5_A2_T2 failing 15.5.5.5.2-3-6 failing 15.5.5.5.2-3-7 failing 15.5.5.5.2-7-4 failing @@ -5725,16 +4097,6 @@ S15.6.3_A3 failing S15.6.3.1_A2 failing S15.6.3.1_A3 failing S15.6.3.1_A4 failing -S15.6.4.2_A2_T1 failing -S15.6.4.2_A2_T2 failing -S15.6.4.2_A2_T3 failing -S15.6.4.2_A2_T4 failing -S15.6.4.2_A2_T5 failing -S15.6.4.3_A2_T1 failing -S15.6.4.3_A2_T2 failing -S15.6.4.3_A2_T3 failing -S15.6.4.3_A2_T4 failing -S15.6.4.3_A2_T5 failing S15.7.3_A8 failing 15.7.3.1-1 failing S15.7.3.1_A1_T1 failing @@ -5746,22 +4108,6 @@ S15.7.3.4_A4 failing S15.7.3.5_A4 failing S15.7.3.6_A4 failing S15.7.4_A3.3 failing -S15.7.4.2_A3_T01 failing -S15.7.4.2_A3_T02 failing -S15.7.4.2_A3_T03 failing -S15.7.4.2_A3_T04 failing -S15.7.4.2_A4_T01 failing -S15.7.4.2_A4_T02 failing -S15.7.4.2_A4_T03 failing -S15.7.4.2_A4_T04 failing -S15.7.4.2_A4_T05 failing -S15.7.4.4_A2_T01 failing -S15.7.4.4_A2_T02 failing -S15.7.4.4_A2_T03 failing -S15.7.4.4_A2_T04 failing -S15.7.4.4_A2_T05 failing -S15.7.4.5_A1.1_T01 failing -S15.7.4.5_A1.1_T02 failing S15.7.4.5_A1.3_T01 failing S15.7.4.5_A1.3_T02 failing S15.7.4.5_A1.4_T01 failing @@ -5771,25 +4117,6 @@ S15.8.2.12_A4 failing S15.8.2.16_A4 failing S15.8.2.16_A5 failing S15.8.2.17_A2 failing -S15.8.2.18_A4 failing -S15.8.2.18_A5 failing -S15.8.2.7_A4 failing -S15.8.2.7_A5 failing -15.9.1.15-1 failing -S15.9.2.1_A2 failing -S15.9.3.1_A4_T1 failing -S15.9.3.1_A4_T2 failing -S15.9.3.1_A4_T3 failing -S15.9.3.1_A4_T4 failing -S15.9.3.1_A4_T5 failing -S15.9.3.1_A4_T6 failing -S15.9.3.1_A5_T1 failing -S15.9.3.1_A5_T2 failing -S15.9.3.1_A5_T3 failing -S15.9.3.1_A5_T4 failing -S15.9.3.1_A5_T5 failing -S15.9.3.1_A5_T6 failing -S15.9.4_A5 failing S15.9.4.1_A1_T1 failing S15.9.4.1_A1_T2 failing S15.9.4.1_A1_T3 failing @@ -5805,6 +4132,19 @@ S15.9.4.3_A3_T2 failing 15.9.4.4-0-2 failing 15.9.4.4-0-3 failing 15.9.4.4-0-4 failing +15.9.1.15-1 failing +S15.9.2.1_A2 failing +S15.9.3.1_A5_T1 failing +S15.9.3.1_A5_T2 failing +S15.9.3.1_A5_T3 failing +S15.9.3.1_A5_T4 failing +S15.9.3.1_A5_T5 failing +S15.9.3.1_A5_T6 failing +S15.9.4_A5 failing +S15.8.2.18_A4 failing +S15.8.2.18_A5 failing +S15.8.2.7_A4 failing +S15.8.2.7_A5 failing S15.9.5.1_A1_T3 failing S15.9.5.1_A2_T1 failing S15.9.5.1_A3_T1 failing @@ -5841,6 +4181,7 @@ S15.9.5.17_A1_T3 failing S15.9.5.17_A2_T1 failing S15.9.5.17_A3_T1 failing S15.9.5.17_A3_T2 failing +13.3.3_L15 failing S15.9.5.18_A1_T3 failing S15.9.5.18_A2_T1 failing S15.9.5.18_A3_T1 failing @@ -5952,54 +4293,6 @@ S15.9.5.41_A3_T1 failing S15.9.5.41_A3_T2 failing S15.9.5.42_A1_T3 failing S15.9.5.42_A2_T1 failing -S15.9.5.42_A3_T1 failing -S15.9.5.42_A3_T2 failing -15.9.5.43-0-10 failing -15.9.5.43-0-11 failing -15.9.5.43-0-12 failing -15.9.5.43-0-13 failing -15.9.5.43-0-14 failing -15.9.5.43-0-15 failing -15.9.5.43-0-16 failing -15.9.5.43-0-2 failing -15.9.5.43-0-3 failing -15.9.5.43-0-4 failing -15.9.5.43-0-5 failing -15.9.5.43-0-6 failing -15.9.5.43-0-7 failing -15.9.5.43-0-8 failing -15.9.5.43-0-9 failing -15.9.5.44-0-1 failing -15.9.5.44-0-2 failing -S15.9.5.5_A1_T3 failing -S15.9.5.5_A2_T1 failing -S15.9.5.5_A3_T1 failing -S15.9.5.5_A3_T2 failing -S15.9.5.6_A1_T3 failing -S15.9.5.6_A2_T1 failing -S15.9.5.6_A3_T1 failing -S15.9.5.6_A3_T2 failing -S15.9.5.7_A1_T3 failing -S15.9.5.7_A2_T1 failing -S15.9.5.7_A3_T1 failing -S15.9.5.7_A3_T2 failing -S15.9.5.8_A1_T3 failing -S15.9.5.8_A2_T1 failing -S15.9.5.8_A3_T1 failing -S15.9.5.8_A3_T2 failing -S15.9.5.9_A1_T3 failing -S15.9.5.9_A2_T1 failing -S15.9.5.9_A3_T1 failing -S15.9.5.9_A3_T2 failing -6.2.2_a failing -6.2.2_b failing -6.2.2_c failing -6.2.3 failing -6.2.4 failing -6.3.1_a failing -6.3.1_b failing -6.4_a failing -6.4_b failing 6.4_c failing 8.0_L15 failing 9.1_a failing @@ -6111,7 +4404,6 @@ S15.9.5.9_A3_T2 failing 12.3_a failing 12.3_b failing 12.4_a failing -13.1.1_1 failing 13.1.1_2 failing 13.1.1_6 failing 13.1.1_7 failing @@ -6120,10 +4412,121 @@ S15.9.5.9_A3_T2 failing 13.2.1_4 failing 13.2.1_5 failing 13.2.1_L15 failing -13.3.0_1 failing 13.3.0_2 failing 13.3.0_6 failing 13.3.0_7 failing 13.3.1_L15 failing 13.3.2_L15 failing -13.3.3_L15 failing +S15.9.5.42_A3_T1 failing +S15.9.5.42_A3_T2 failing +15.9.5.43-0-10 failing +15.9.5.43-0-11 failing +15.9.5.43-0-12 failing +15.9.5.43-0-13 failing +15.9.5.43-0-14 failing +15.9.5.43-0-15 failing +15.9.5.43-0-2 failing +15.9.5.43-0-3 failing +15.9.5.43-0-4 failing +15.9.5.43-0-5 failing +15.9.5.43-0-8 failing +15.9.5.43-0-9 failing +15.9.5.44-0-1 failing +15.9.5.44-0-2 failing +S15.9.5.5_A1_T3 failing +S15.9.5.5_A2_T1 failing +S15.9.5.5_A3_T1 failing +S15.9.5.5_A3_T2 failing +S15.9.5.6_A1_T3 failing +S15.9.5.6_A2_T1 failing +S15.9.5.6_A3_T1 failing +S15.9.5.6_A3_T2 failing +S15.9.5.7_A1_T3 failing +S15.9.5.7_A2_T1 failing +S15.9.5.7_A3_T1 failing +S15.9.5.7_A3_T2 failing +S15.9.5.8_A1_T3 failing +S15.9.5.8_A2_T1 failing +S15.9.5.8_A3_T1 failing +S15.9.5.8_A3_T2 failing +S15.9.5.9_A1_T3 failing +S15.9.5.9_A2_T1 failing +S15.9.5.9_A3_T1 failing +S15.9.5.9_A3_T2 failing +6.2.2_a failing +6.2.2_b failing +6.2.2_c failing +6.2.3 failing +6.2.4 failing +6.3.1_a failing +6.3.1_b failing +6.4_a failing +6.4_b failing + +# Tests that are passing but are supposed to fail (@negative) +S7.8.3_A6.1_T1 failing +S7.8.3_A6.1_T2 failing +Sbp_12.5_A9_T3 failing +Sbp_12.6.2_A13_T3 failing +Sbp_7.8.4_A6.1_T4 failing +Sbp_7.8.4_A6.2_T1 failing +Sbp_7.8.4_A6.2_T2 failing +Sbp_A1_T1 failing +Sbp_A2_T1 failing +Sbp_A2_T2 failing +Sbp_A3_T1 failing +Sbp_A4_T1 failing +Sbp_A4_T2 failing +S7.8.4_A7.1_T4 failing +S12.4_A1 failing +S7.8.4_A7.2_T1 failing +S7.8.4_A7.2_T2 failing +S7.8.4_A7.2_T3 failing +S7.8.4_A7.2_T4 failing +S7.8.4_A7.2_T5 failing +S7.8.4_A7.2_T6 failing +S12.9_A1_T1 failing +S12.9_A1_T10 failing +S12.9_A1_T2 failing +S12.9_A1_T3 failing +S12.9_A1_T4 failing +S12.9_A1_T5 failing +S12.9_A1_T6 failing +S12.9_A1_T7 failing +S12.9_A1_T8 failing +S12.9_A1_T9 failing +S15.2.4.3_A12 failing +S15.2.4.3_A13 failing +S15.2.4.4_A12 failing +S15.2.4.4_A13 failing +S15.2.4.4_A14 failing +S15.2.4.4_A15 failing +S15.2.4.5_A12 failing +S15.2.4.5_A13 failing +S15.2.4.6_A12 failing +S15.2.4.6_A13 failing +S15.2.4.7_A12 failing +S15.2.4.7_A13 failing +15.3.5.4_2-11gs failing +15.3.5.4_2-13gs failing +15.3.5.4_2-15gs failing +15.3.5.4_2-17gs failing +15.3.5.4_2-19gs failing +15.3.5.4_2-1gs failing +15.3.5.4_2-21gs failing +15.3.5.4_2-22gs failing +15.3.5.4_2-23gs failing +15.3.5.4_2-24gs failing +15.3.5.4_2-25gs failing +15.3.5.4_2-26gs failing +15.3.5.4_2-27gs failing +15.3.5.4_2-28gs failing +15.3.5.4_2-29gs failing +15.3.5.4_2-3gs failing +15.3.5.4_2-48gs failing +15.3.5.4_2-50gs failing +15.3.5.4_2-52gs failing +15.3.5.4_2-54gs failing +15.3.5.4_2-5gs failing +15.3.5.4_2-7gs failing +15.3.5.4_2-9gs failing -- cgit v1.2.3 From 059115c009804c234e270509db6995e68aca9650 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 7 Jan 2013 15:49:32 +0100 Subject: Ignore Qt Creator .pro.user files. Change-Id: Ie6a68f2c1c9adb7d49681c05cc9e6c0db3ecc136 Reviewed-by: Lars Knoll --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index dfa1cc38a1..112af15744 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ Makefile v4 udis86_itab.* *.pyc +*.pro.user -- cgit v1.2.3 From a4593f30327d3a962b68a96e998d86be2c694749 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 7 Jan 2013 15:56:24 +0100 Subject: Eliminate one more forked file, TypeTraits.h Change-Id: Ia0f846a3e5ba0a9d8d1bdf743bc41859d1f4eb8e Reviewed-by: Lars Knoll --- masm/stubs/wtf/TypeTraits.h | 58 ++++++++++ masm/wtf/TypeTraits.h | 261 -------------------------------------------- 2 files changed, 58 insertions(+), 261 deletions(-) create mode 100644 masm/stubs/wtf/TypeTraits.h delete mode 100644 masm/wtf/TypeTraits.h diff --git a/masm/stubs/wtf/TypeTraits.h b/masm/stubs/wtf/TypeTraits.h new file mode 100644 index 0000000000..9b626a7a53 --- /dev/null +++ b/masm/stubs/wtf/TypeTraits.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef TYPETRAITS_H +#define TYPETRAITS_H + +namespace WTF { + +template +struct IsSameType { + static const bool value = false; +}; + +template +struct IsSameType { + static const bool value = true; +}; + +} + +#endif // TYPETRAITS_H diff --git a/masm/wtf/TypeTraits.h b/masm/wtf/TypeTraits.h deleted file mode 100644 index b9e46bc555..0000000000 --- a/masm/wtf/TypeTraits.h +++ /dev/null @@ -1,261 +0,0 @@ - /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. - * Copyright (C) 2009, 2010 Google Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef TypeTraits_h -#define TypeTraits_h - -#include - -#if (defined(__GLIBCXX__) && (__GLIBCXX__ >= 20070724) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || (defined(_MSC_VER) && (_MSC_VER >= 1600)) -#include -#if defined(__GLIBCXX__) && (__GLIBCXX__ >= 20070724) && defined(__GXX_EXPERIMENTAL_CXX0X__) -#include -#endif -#endif - -namespace WTF { - - // The following are provided in this file: - // - // Conditional::Type - // - // IsInteger::value - // IsPod::value, see the definition for a note about its limitations - // IsConvertibleToInteger::value - // - // IsArray::value - // - // IsSameType::value - // - // RemovePointer::Type - // RemoveReference::Type - // RemoveConst::Type - // RemoveVolatile::Type - // RemoveConstVolatile::Type - // RemoveExtent::Type - // - // DecayArray::Type - // - // COMPILE_ASSERT's in TypeTraits.cpp illustrate their usage and what they do. - - template struct Conditional { typedef If Type; }; - template struct Conditional { typedef Then Type; }; - - template struct IsInteger { static const bool value = false; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; -#if !COMPILER(MSVC) || defined(_NATIVE_WCHAR_T_DEFINED) - template<> struct IsInteger { static const bool value = true; }; -#endif - - template struct IsFloatingPoint { static const bool value = false; }; - template<> struct IsFloatingPoint { static const bool value = true; }; - template<> struct IsFloatingPoint { static const bool value = true; }; - template<> struct IsFloatingPoint { static const bool value = true; }; - - template struct IsArithmetic { static const bool value = IsInteger::value || IsFloatingPoint::value; }; - - // IsPod is misnamed as it doesn't cover all plain old data (pod) types. - // Specifically, it doesn't allow for enums or for structs. - template struct IsPod { static const bool value = IsArithmetic::value; }; - template struct IsPod { static const bool value = true; }; - - template class IsConvertibleToInteger { - // Avoid "possible loss of data" warning when using Microsoft's C++ compiler - // by not converting int's to doubles. - template class IsConvertibleToDouble; - template class IsConvertibleToDouble { - public: - static const bool value = false; - }; - - template class IsConvertibleToDouble { - typedef char YesType; - struct NoType { - char padding[8]; - }; - - static YesType floatCheck(long double); - static NoType floatCheck(...); - static T& t; - public: - static const bool value = sizeof(floatCheck(t)) == sizeof(YesType); - }; - - public: - static const bool value = IsInteger::value || IsConvertibleToDouble::value, T>::value; - }; - - - template struct IsArray { - static const bool value = false; - }; - - template struct IsArray { - static const bool value = true; - }; - - template struct IsArray { - static const bool value = true; - }; - - - template struct IsSameType { - static const bool value = false; - }; - - template struct IsSameType { - static const bool value = true; - }; - - template class IsSubclass { - typedef char YesType; - struct NoType { - char padding[8]; - }; - - static YesType subclassCheck(U*); - static NoType subclassCheck(...); - static T* t; - public: - static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); - }; - - template class U> class IsSubclassOfTemplate { - typedef char YesType; - struct NoType { - char padding[8]; - }; - - template static YesType subclassCheck(U*); - static NoType subclassCheck(...); - static T* t; - public: - static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); - }; - - template class OuterTemplate> struct RemoveTemplate { - typedef T Type; - }; - - template class OuterTemplate> struct RemoveTemplate, OuterTemplate> { - typedef T Type; - }; - - template struct RemoveConst { - typedef T Type; - }; - - template struct RemoveConst { - typedef T Type; - }; - - template struct RemoveVolatile { - typedef T Type; - }; - - template struct RemoveVolatile { - typedef T Type; - }; - - template struct RemoveConstVolatile { - typedef typename RemoveVolatile::Type>::Type Type; - }; - - template struct RemovePointer { - typedef T Type; - }; - - template struct RemovePointer { - typedef T Type; - }; - - template struct RemoveReference { - typedef T Type; - }; - - template struct RemoveReference { - typedef T Type; - }; - - template struct RemoveExtent { - typedef T Type; - }; - - template struct RemoveExtent { - typedef T Type; - }; - - template struct RemoveExtent { - typedef T Type; - }; - - template struct DecayArray { - typedef typename RemoveReference::Type U; - public: - typedef typename Conditional< - IsArray::value, - typename RemoveExtent::Type*, - typename RemoveConstVolatile::Type - >::Type Type; - }; - -#if COMPILER(CLANG) || GCC_VERSION_AT_LEAST(4, 6, 0) || (defined(_MSC_VER) && (_MSC_VER >= 1400) && (_MSC_VER < 1600) && !defined(__INTEL_COMPILER)) - // VC8 (VS2005) and later has __has_trivial_constructor and __has_trivial_destructor, - // but the implementation returns false for built-in types. We add the extra IsPod condition to - // work around this. - template struct HasTrivialConstructor { - static const bool value = __has_trivial_constructor(T) || IsPod >::value; - }; - template struct HasTrivialDestructor { - static const bool value = __has_trivial_destructor(T) || IsPod >::value; - }; -#elif (defined(__GLIBCXX__) && (__GLIBCXX__ >= 20070724) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || (defined(_MSC_VER) && (_MSC_VER >= 1600)) - // GCC's libstdc++ 20070724 and later supports C++ TR1 type_traits in the std namespace. - // VC10 (VS2010) and later support C++ TR1 type_traits in the std::tr1 namespace. - template struct HasTrivialConstructor : public std::tr1::has_trivial_constructor { }; - template struct HasTrivialDestructor : public std::tr1::has_trivial_destructor { }; -#else - // For compilers that don't support detection of trivial constructors and destructors in classes, - // we use a template that returns true for any POD type that IsPod can detect (see IsPod caveats above), - // but false for all other types (which includes all classes). This will give false negatives, which can hurt - // performance, but avoids false positives, which would result in incorrect behavior. - template struct HasTrivialConstructor { - static const bool value = IsPod >::value; - }; - template struct HasTrivialDestructor { - static const bool value = IsPod >::value; - }; -#endif - -} // namespace WTF - -#endif // TypeTraits_h -- cgit v1.2.3 From ad35a0b21fbb6d70558a789cf0f87de64a15829d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 7 Jan 2013 15:51:57 +0100 Subject: Replace OwnPtr with a simple QScopedPointer define and a PassOwnPtr wrapper Change-Id: I479cd9cf03700b912748baeb58021b8ec73af987 Reviewed-by: Lars Knoll --- masm/masm.pri | 1 - masm/stubs/wtf/OwnPtr.h | 46 +++++++++ masm/stubs/wtf/PassOwnPtr.h | 93 +++++++++++++++++++ masm/wtf/OwnPtr.h | 220 -------------------------------------------- masm/wtf/OwnPtrCommon.h | 88 ------------------ masm/wtf/PassOwnPtr.h | 172 ---------------------------------- 6 files changed, 139 insertions(+), 481 deletions(-) create mode 100644 masm/stubs/wtf/OwnPtr.h create mode 100644 masm/stubs/wtf/PassOwnPtr.h delete mode 100644 masm/wtf/OwnPtr.h delete mode 100644 masm/wtf/OwnPtrCommon.h delete mode 100644 masm/wtf/PassOwnPtr.h diff --git a/masm/masm.pri b/masm/masm.pri index edbb044f0e..e187fcf3d6 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -13,7 +13,6 @@ SOURCES += $$PWD/wtf/FilePrintStream.cpp HEADERS += $$PWD/wtf/FilePrintStream.h HEADERS += $$PWD/wtf/RawPointer.h -HEADERS += $$PWD/wtf/PassOwnPtr.h win32: SOURCES += $$PWD/wtf/OSAllocatorWin.cpp else: SOURCES += $$PWD/wtf/OSAllocatorPosix.cpp diff --git a/masm/stubs/wtf/OwnPtr.h b/masm/stubs/wtf/OwnPtr.h new file mode 100644 index 0000000000..31d2f1efa3 --- /dev/null +++ b/masm/stubs/wtf/OwnPtr.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef OWNPTR_H +#define OWNPTR_H + +#include "PassOwnPtr.h" + +#endif // OWNPTR_H diff --git a/masm/stubs/wtf/PassOwnPtr.h b/masm/stubs/wtf/PassOwnPtr.h new file mode 100644 index 0000000000..c8d3bf3f75 --- /dev/null +++ b/masm/stubs/wtf/PassOwnPtr.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef PASSOWNPTR_H +#define PASSOWNPTR_H + +#include + +#define OwnPtr QScopedPointer + +template +class PassOwnPtr { +public: + PassOwnPtr() {} + + PassOwnPtr(T* ptr) + : m_ptr(ptr) + { + } + + PassOwnPtr(const PassOwnPtr& other) + : m_ptr(other.leakRef()) + { + } + + PassOwnPtr(const OwnPtr& other) + : m_ptr(other.take()) + { + } + + ~PassOwnPtr() + { + } + + T* operator->() const { return m_ptr.data(); } + + T* leakRef() const { return m_ptr.take(); } + +private: + template friend PassOwnPtr adoptPtr(PtrType*); + + PassOwnPtr& operator=(const PassOwnPtr&) + {} + mutable QScopedPointer m_ptr; +}; + +template +PassOwnPtr adoptPtr(T* ptr) +{ + PassOwnPtr result; + result.m_ptr.reset(ptr); + return result; +} + + +#endif // PASSOWNPTR_H diff --git a/masm/wtf/OwnPtr.h b/masm/wtf/OwnPtr.h deleted file mode 100644 index fa79aa1e3a..0000000000 --- a/masm/wtf/OwnPtr.h +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef WTF_OwnPtr_h -#define WTF_OwnPtr_h - -#include -#include -#include -#include -#include -#include -#include - -namespace WTF { - - // Unlike most of our smart pointers, OwnPtr can take either the pointer type or the pointed-to type. - - template class PassOwnPtr; - template PassOwnPtr adoptPtr(T*); - - template class OwnPtr { -#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) - // If rvalue references are not supported, the copy constructor is - // public so OwnPtr cannot be marked noncopyable. See note below. - WTF_MAKE_NONCOPYABLE(OwnPtr); -#endif - public: - typedef typename RemovePointer::Type ValueType; - typedef ValueType* PtrType; - - OwnPtr() : m_ptr(0) { } - OwnPtr(std::nullptr_t) : m_ptr(0) { } - - // See comment in PassOwnPtr.h for why this takes a const reference. - template OwnPtr(const PassOwnPtr& o); - -#if !COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) - // This copy constructor is used implicitly by gcc when it generates - // transients for assigning a PassOwnPtr object to a stack-allocated - // OwnPtr object. It should never be called explicitly and gcc - // should optimize away the constructor when generating code. - OwnPtr(const OwnPtr&); -#endif - - ~OwnPtr() { deleteOwnedPtr(m_ptr); } - - PtrType get() const { return m_ptr; } - - void clear(); - PassOwnPtr release(); - PtrType leakPtr() WARN_UNUSED_RETURN; - - ValueType& operator*() const { ASSERT(m_ptr); return *m_ptr; } - PtrType operator->() const { ASSERT(m_ptr); return m_ptr; } - - bool operator!() const { return !m_ptr; } - - // This conversion operator allows implicit conversion to bool but not to other integer types. - typedef PtrType OwnPtr::*UnspecifiedBoolType; - operator UnspecifiedBoolType() const { return m_ptr ? &OwnPtr::m_ptr : 0; } - - OwnPtr& operator=(const PassOwnPtr&); - OwnPtr& operator=(std::nullptr_t) { clear(); return *this; } - template OwnPtr& operator=(const PassOwnPtr&); - -#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) - OwnPtr(OwnPtr&&); - template OwnPtr(OwnPtr&&); - - OwnPtr& operator=(OwnPtr&&); - template OwnPtr& operator=(OwnPtr&&); -#endif - - void swap(OwnPtr& o) { std::swap(m_ptr, o.m_ptr); } - - private: -#if !COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) - // If rvalue references are supported, noncopyable takes care of this. - OwnPtr& operator=(const OwnPtr&); -#endif - - // We should never have two OwnPtrs for the same underlying object (otherwise we'll get - // double-destruction), so these equality operators should never be needed. - template bool operator==(const OwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - template bool operator!=(const OwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - template bool operator==(const PassOwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - template bool operator!=(const PassOwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - - PtrType m_ptr; - }; - - template template inline OwnPtr::OwnPtr(const PassOwnPtr& o) - : m_ptr(o.leakPtr()) - { - } - - template inline void OwnPtr::clear() - { - PtrType ptr = m_ptr; - m_ptr = 0; - deleteOwnedPtr(ptr); - } - - template inline PassOwnPtr OwnPtr::release() - { - PtrType ptr = m_ptr; - m_ptr = 0; - return adoptPtr(ptr); - } - - template inline typename OwnPtr::PtrType OwnPtr::leakPtr() - { - PtrType ptr = m_ptr; - m_ptr = 0; - return ptr; - } - - template inline OwnPtr& OwnPtr::operator=(const PassOwnPtr& o) - { - PtrType ptr = m_ptr; - m_ptr = o.leakPtr(); - ASSERT(!ptr || m_ptr != ptr); - deleteOwnedPtr(ptr); - return *this; - } - - template template inline OwnPtr& OwnPtr::operator=(const PassOwnPtr& o) - { - PtrType ptr = m_ptr; - m_ptr = o.leakPtr(); - ASSERT(!ptr || m_ptr != ptr); - deleteOwnedPtr(ptr); - return *this; - } - -#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES) - template inline OwnPtr::OwnPtr(OwnPtr&& o) - : m_ptr(o.leakPtr()) - { - } - - template template inline OwnPtr::OwnPtr(OwnPtr&& o) - : m_ptr(o.leakPtr()) - { - } - - template inline OwnPtr& OwnPtr::operator=(OwnPtr&& o) - { - PtrType ptr = m_ptr; - m_ptr = o.leakPtr(); - ASSERT(!ptr || m_ptr != ptr); - deleteOwnedPtr(ptr); - - return *this; - } - - template template inline OwnPtr& OwnPtr::operator=(OwnPtr&& o) - { - PtrType ptr = m_ptr; - m_ptr = o.leakPtr(); - ASSERT(!ptr || m_ptr != ptr); - deleteOwnedPtr(ptr); - - return *this; - } -#endif - - template inline void swap(OwnPtr& a, OwnPtr& b) - { - a.swap(b); - } - - template inline bool operator==(const OwnPtr& a, U* b) - { - return a.get() == b; - } - - template inline bool operator==(T* a, const OwnPtr& b) - { - return a == b.get(); - } - - template inline bool operator!=(const OwnPtr& a, U* b) - { - return a.get() != b; - } - - template inline bool operator!=(T* a, const OwnPtr& b) - { - return a != b.get(); - } - - template inline typename OwnPtr::PtrType getPtr(const OwnPtr& p) - { - return p.get(); - } - -} // namespace WTF - -using WTF::OwnPtr; - -#endif // WTF_OwnPtr_h diff --git a/masm/wtf/OwnPtrCommon.h b/masm/wtf/OwnPtrCommon.h deleted file mode 100644 index c564001844..0000000000 --- a/masm/wtf/OwnPtrCommon.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * Copyright (C) 2009 Torch Mobile, Inc. - * Copyright (C) 2010 Company 100 Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_OwnPtrCommon_h -#define WTF_OwnPtrCommon_h - -#if OS(WINDOWS) -typedef struct HBITMAP__* HBITMAP; -typedef struct HBRUSH__* HBRUSH; -typedef struct HDC__* HDC; -typedef struct HFONT__* HFONT; -typedef struct HPALETTE__* HPALETTE; -typedef struct HPEN__* HPEN; -typedef struct HRGN__* HRGN; -#endif - -#if PLATFORM(EFL) -typedef struct _Ecore_Evas Ecore_Evas; -typedef struct _Ecore_IMF_Context Ecore_IMF_Context; -typedef struct _Ecore_Pipe Ecore_Pipe; -typedef struct _Ecore_Timer Ecore_Timer; -typedef struct _Eina_Hash Eina_Hash; -typedef struct _Eina_Module Eina_Module; -typedef struct _Evas_Object Evas_Object; -#if USE(ACCELERATED_COMPOSITING) -typedef struct _Evas_GL Evas_GL; -#endif -#endif - -namespace WTF { - - template inline void deleteOwnedPtr(T* ptr) - { - typedef char known[sizeof(T) ? 1 : -1]; - if (sizeof(known)) - delete ptr; - } - -#if OS(WINDOWS) - WTF_EXPORT_PRIVATE void deleteOwnedPtr(HBITMAP); - WTF_EXPORT_PRIVATE void deleteOwnedPtr(HBRUSH); - WTF_EXPORT_PRIVATE void deleteOwnedPtr(HDC); - WTF_EXPORT_PRIVATE void deleteOwnedPtr(HFONT); - WTF_EXPORT_PRIVATE void deleteOwnedPtr(HPALETTE); - WTF_EXPORT_PRIVATE void deleteOwnedPtr(HPEN); - WTF_EXPORT_PRIVATE void deleteOwnedPtr(HRGN); -#endif - -#if PLATFORM(EFL) - WTF_EXPORT_PRIVATE void deleteOwnedPtr(Ecore_Evas*); - WTF_EXPORT_PRIVATE void deleteOwnedPtr(Ecore_IMF_Context*); - WTF_EXPORT_PRIVATE void deleteOwnedPtr(Ecore_Pipe*); - WTF_EXPORT_PRIVATE void deleteOwnedPtr(Ecore_Timer*); - WTF_EXPORT_PRIVATE void deleteOwnedPtr(Eina_Hash*); - WTF_EXPORT_PRIVATE void deleteOwnedPtr(Eina_Module*); - WTF_EXPORT_PRIVATE void deleteOwnedPtr(Evas_Object*); -#if USE(ACCELERATED_COMPOSITING) - WTF_EXPORT_PRIVATE void deleteOwnedPtr(Evas_GL*); -#endif -#endif - -} // namespace WTF - -#endif // WTF_OwnPtrCommon_h diff --git a/masm/wtf/PassOwnPtr.h b/masm/wtf/PassOwnPtr.h deleted file mode 100644 index 5ebf83d65a..0000000000 --- a/masm/wtf/PassOwnPtr.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_PassOwnPtr_h -#define WTF_PassOwnPtr_h - -#include -#include -#include -#include - -namespace WTF { - - // Unlike most of our smart pointers, PassOwnPtr can take either the pointer type or the pointed-to type. - - template class OwnPtr; - template class PassOwnPtr; - template PassOwnPtr adoptPtr(T*); - - template class PassOwnPtr { - public: - typedef typename RemovePointer::Type ValueType; - typedef ValueType* PtrType; - - PassOwnPtr() : m_ptr(0) { } - PassOwnPtr(std::nullptr_t) : m_ptr(0) { } - - // It somewhat breaks the type system to allow transfer of ownership out of - // a const PassOwnPtr. However, it makes it much easier to work with PassOwnPtr - // temporaries, and we don't have a need to use real const PassOwnPtrs anyway. - PassOwnPtr(const PassOwnPtr& o) : m_ptr(o.leakPtr()) { } - template PassOwnPtr(const PassOwnPtr& o) : m_ptr(o.leakPtr()) { } - - ~PassOwnPtr() { deleteOwnedPtr(m_ptr); } - - PtrType get() const { return m_ptr; } - - PtrType leakPtr() const WARN_UNUSED_RETURN; - - ValueType& operator*() const { ASSERT(m_ptr); return *m_ptr; } - PtrType operator->() const { ASSERT(m_ptr); return m_ptr; } - - bool operator!() const { return !m_ptr; } - - // This conversion operator allows implicit conversion to bool but not to other integer types. - typedef PtrType PassOwnPtr::*UnspecifiedBoolType; - operator UnspecifiedBoolType() const { return m_ptr ? &PassOwnPtr::m_ptr : 0; } - - PassOwnPtr& operator=(const PassOwnPtr&) { COMPILE_ASSERT(!sizeof(T*), PassOwnPtr_should_never_be_assigned_to); return *this; } - - template friend PassOwnPtr adoptPtr(U*); - - private: - explicit PassOwnPtr(PtrType ptr) : m_ptr(ptr) { } - - // We should never have two OwnPtrs for the same underlying object (otherwise we'll get - // double-destruction), so these equality operators should never be needed. - template bool operator==(const PassOwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - template bool operator!=(const PassOwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - template bool operator==(const OwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - template bool operator!=(const OwnPtr&) { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - - mutable PtrType m_ptr; - }; - - template inline typename PassOwnPtr::PtrType PassOwnPtr::leakPtr() const - { - PtrType ptr = m_ptr; - m_ptr = 0; - return ptr; - } - - template inline bool operator==(const PassOwnPtr& a, const PassOwnPtr& b) - { - return a.get() == b.get(); - } - - template inline bool operator==(const PassOwnPtr& a, const OwnPtr& b) - { - return a.get() == b.get(); - } - - template inline bool operator==(const OwnPtr& a, const PassOwnPtr& b) - { - return a.get() == b.get(); - } - - template inline bool operator==(const PassOwnPtr& a, U* b) - { - return a.get() == b; - } - - template inline bool operator==(T* a, const PassOwnPtr& b) - { - return a == b.get(); - } - - template inline bool operator!=(const PassOwnPtr& a, const PassOwnPtr& b) - { - return a.get() != b.get(); - } - - template inline bool operator!=(const PassOwnPtr& a, const OwnPtr& b) - { - return a.get() != b.get(); - } - - template inline bool operator!=(const OwnPtr& a, const PassOwnPtr& b) - { - return a.get() != b.get(); - } - - template inline bool operator!=(const PassOwnPtr& a, U* b) - { - return a.get() != b; - } - - template inline bool operator!=(T* a, const PassOwnPtr& b) - { - return a != b.get(); - } - - template inline PassOwnPtr adoptPtr(T* ptr) - { - return PassOwnPtr(ptr); - } - - template inline PassOwnPtr static_pointer_cast(const PassOwnPtr& p) - { - return adoptPtr(static_cast(p.leakPtr())); - } - - template inline PassOwnPtr const_pointer_cast(const PassOwnPtr& p) - { - return adoptPtr(const_cast(p.leakPtr())); - } - - template inline T* getPtr(const PassOwnPtr& p) - { - return p.get(); - } - -} // namespace WTF - -using WTF::PassOwnPtr; -using WTF::adoptPtr; -using WTF::const_pointer_cast; -using WTF::static_pointer_cast; - -#endif // WTF_PassOwnPtr_h -- cgit v1.2.3 From d23e5cccd8602ad2c1144dbc1790b7584834ad73 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 7 Jan 2013 13:39:41 +0100 Subject: Map WTF_MAKE_NONCOPYABLE straight to Q_DISABLE_COPY Change-Id: Idf914acb41917de8667bb4dcbd1305276a29b8d6 Reviewed-by: Lars Knoll --- masm/stubs/wtf/Noncopyable.h | 48 ++++++++++++++++++++++++++++++++++++++++++++ masm/wtf/Noncopyable.h | 38 ----------------------------------- 2 files changed, 48 insertions(+), 38 deletions(-) create mode 100644 masm/stubs/wtf/Noncopyable.h delete mode 100644 masm/wtf/Noncopyable.h diff --git a/masm/stubs/wtf/Noncopyable.h b/masm/stubs/wtf/Noncopyable.h new file mode 100644 index 0000000000..d3d1eed6d1 --- /dev/null +++ b/masm/stubs/wtf/Noncopyable.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef NONCOPYABLE_H +#define NONCOPYABLE_H + +#include + +#define WTF_MAKE_NONCOPYABLE(x) Q_DISABLE_COPY(x) + +#endif // NONCOPYABLE_H diff --git a/masm/wtf/Noncopyable.h b/masm/wtf/Noncopyable.h deleted file mode 100644 index f6bdfbb40b..0000000000 --- a/masm/wtf/Noncopyable.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef WTF_Noncopyable_h -#define WTF_Noncopyable_h - -#include - -#if COMPILER_SUPPORTS(CXX_DELETED_FUNCTIONS) - #define WTF_MAKE_NONCOPYABLE(ClassName) \ - private: \ - ClassName(const ClassName&) = delete; \ - ClassName& operator=(const ClassName&) = delete; -#else - #define WTF_MAKE_NONCOPYABLE(ClassName) \ - private: \ - ClassName(const ClassName&); \ - ClassName& operator=(const ClassName&) -#endif - -#endif // WTF_Noncopyable_h -- cgit v1.2.3 From 9a6bf4173ef2234320d41ff146f33be74e08a477 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 7 Jan 2013 13:35:26 +0100 Subject: Replace WTF's UNUSED_PARAM with Q_UNUSED Change-Id: I23d75b418cd9d405c288af30475e44ff1724a4ed Reviewed-by: Lars Knoll --- masm/stubs/wtf/UnusedParam.h | 48 ++++++++++++++++++++++++++++++++++++++++++++ masm/wtf/UnusedParam.h | 44 ---------------------------------------- 2 files changed, 48 insertions(+), 44 deletions(-) create mode 100644 masm/stubs/wtf/UnusedParam.h delete mode 100644 masm/wtf/UnusedParam.h diff --git a/masm/stubs/wtf/UnusedParam.h b/masm/stubs/wtf/UnusedParam.h new file mode 100644 index 0000000000..a676bdf303 --- /dev/null +++ b/masm/stubs/wtf/UnusedParam.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef UNUSEDPARAM_H +#define UNUSEDPARAM_H + +#include + +#define UNUSED_PARAM(x) Q_UNUSED(x) + +#endif // UNUSEDPARAM_H diff --git a/masm/wtf/UnusedParam.h b/masm/wtf/UnusedParam.h deleted file mode 100644 index 31d70e7364..0000000000 --- a/masm/wtf/UnusedParam.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef WTF_UnusedParam_h -#define WTF_UnusedParam_h - -#include - -#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) -template -inline void unusedParam(T& x) { (void)x; } -#define UNUSED_PARAM(variable) unusedParam(variable) -#else -#define UNUSED_PARAM(variable) (void)variable -#endif - -/* This is to keep the compiler from complaining when for local labels are - declared but not referenced. For example, this can happen with code that - works with auto-generated code. -*/ -#if COMPILER(MSVC) -#define UNUSED_LABEL(label) if (false) goto label -#else -#define UNUSED_LABEL(label) UNUSED_PARAM(&& label) -#endif - -#endif /* WTF_UnusedParam_h */ -- cgit v1.2.3 From aeb4b6a46dbe88f62a6dcb562e1687e171960606 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 7 Jan 2013 13:28:57 +0100 Subject: Replace the WTF RefPtrs with simpler versions We do need less functionality, so we get away with less, based on code that's tested with the full version. This reduces the size of the binary and allows for the removal of many more forked files. Change-Id: I3292ad5bb6abed1d531a51c81d59b0ba18da4e72 Reviewed-by: Lars Knoll --- masm/masm.pri | 1 + masm/stubs/wtf/PassRefPtr.h | 101 ++++++++++++++ masm/stubs/wtf/RefCounted.h | 68 ++++++++++ masm/stubs/wtf/RefPtr.h | 93 +++++++++++++ masm/wtf/Alignment.h | 70 ---------- masm/wtf/AlwaysInline.h | 23 ---- masm/wtf/PassRefPtr.h | 188 -------------------------- masm/wtf/RefCounted.h | 253 ----------------------------------- masm/wtf/RefPtr.h | 207 ---------------------------- masm/wtf/ThreadRestrictionVerifier.h | 209 ----------------------------- masm/wtf/ThreadSafeRefCounted.h | 150 --------------------- masm/wtf/Threading.h | 117 ---------------- masm/wtf/ThreadingPrimitives.h | 144 -------------------- 13 files changed, 263 insertions(+), 1361 deletions(-) create mode 100644 masm/stubs/wtf/PassRefPtr.h create mode 100644 masm/stubs/wtf/RefCounted.h create mode 100644 masm/stubs/wtf/RefPtr.h delete mode 100644 masm/wtf/Alignment.h delete mode 100644 masm/wtf/AlwaysInline.h delete mode 100644 masm/wtf/PassRefPtr.h delete mode 100644 masm/wtf/RefCounted.h delete mode 100644 masm/wtf/RefPtr.h delete mode 100644 masm/wtf/ThreadRestrictionVerifier.h delete mode 100644 masm/wtf/ThreadSafeRefCounted.h delete mode 100644 masm/wtf/Threading.h delete mode 100644 masm/wtf/ThreadingPrimitives.h diff --git a/masm/masm.pri b/masm/masm.pri index e187fcf3d6..d0033f59c1 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -44,6 +44,7 @@ INCLUDEPATH += $$PWD/jit INCLUDEPATH += $$PWD/assembler INCLUDEPATH += $$PWD/wtf INCLUDEPATH += $$PWD/stubs +INCLUDEPATH += $$PWD/stubs/wtf INCLUDEPATH += $$PWD DEFINES += WTF_USE_UDIS86=1 diff --git a/masm/stubs/wtf/PassRefPtr.h b/masm/stubs/wtf/PassRefPtr.h new file mode 100644 index 0000000000..d97be1c330 --- /dev/null +++ b/masm/stubs/wtf/PassRefPtr.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef PASSREFPTR_H +#define PASSREFPTR_H + +template class RefPtr; + +template +class PassRefPtr { +public: + PassRefPtr() : m_ptr(0) {} + + PassRefPtr(T* ptr) + : m_ptr(ptr) + { + if (m_ptr) + m_ptr->ref(); + } + + PassRefPtr(const PassRefPtr& other) + : m_ptr(other.leakRef()) + { + } + + PassRefPtr(const RefPtr& other) + : m_ptr(other.get()) + { + if (m_ptr) + m_ptr->ref(); + } + + ~PassRefPtr() + { + if (m_ptr) + m_ptr->deref(); + } + + T* operator->() const { return m_ptr; } + + T* leakRef() const + { + T* result = m_ptr; + m_ptr = 0; + return result; + } + +private: + PassRefPtr& operator=(const PassRefPtr&) + {} + + template friend PassRefPtr adoptRef(PtrType*); + mutable T* m_ptr; +}; + +template +PassRefPtr adoptRef(T* ptr) +{ + PassRefPtr result; + result.m_ptr = ptr; + return result; +} + +#endif // PASSREFPTR_H diff --git a/masm/stubs/wtf/RefCounted.h b/masm/stubs/wtf/RefCounted.h new file mode 100644 index 0000000000..f905ace8ad --- /dev/null +++ b/masm/stubs/wtf/RefCounted.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef REFCOUNTED_H +#define REFCOUNTED_H + +template +class RefCounted { +public: + RefCounted() : m_refCount(1) {} + ~RefCounted() + { + deref(); + } + + void ref() + { + ++m_refCount; + } + + void deref() + { + if (!--m_refCount) + delete static_cast(this); + } + +protected: + int m_refCount; +}; + +#endif // REFCOUNTED_H diff --git a/masm/stubs/wtf/RefPtr.h b/masm/stubs/wtf/RefPtr.h new file mode 100644 index 0000000000..929b493b4b --- /dev/null +++ b/masm/stubs/wtf/RefPtr.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef REFPTR_H +#define REFPTR_H + +#include "PassRefPtr.h" + +template +class RefPtr { +public: + RefPtr() : m_ptr(0) {} + RefPtr(const RefPtr &other) + : m_ptr(other.m_ptr) + { + if (m_ptr) + m_ptr->ref(); + } + + RefPtr& operator=(const RefPtr& other) + { + if (other.m_ptr) + other.m_ptr->ref(); + if (m_ptr) + m_ptr->deref(); + m_ptr = other.m_ptr; + return *this; + } + + RefPtr(const PassRefPtr& other) + : m_ptr(other.leakRef()) + { + } + + ~RefPtr() + { + if (m_ptr) + m_ptr->deref(); + } + + T* operator->() const { return m_ptr; } + T* get() const { return m_ptr; } + bool operator!() const { return !m_ptr; } + + PassRefPtr release() + { + T* ptr = m_ptr; + m_ptr = 0; + return adoptRef(ptr); + } + +private: + T* m_ptr; +}; + +#endif // REFPTR_H diff --git a/masm/wtf/Alignment.h b/masm/wtf/Alignment.h deleted file mode 100644 index 7dfda209a8..0000000000 --- a/masm/wtf/Alignment.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef WTF_Alignment_h -#define WTF_Alignment_h - -#include -#include -#include - - -namespace WTF { - -#if COMPILER(GCC) || COMPILER(MINGW) || COMPILER(RVCT) || COMPILER(GCCE) || (COMPILER(SUNCC) && __SUNPRO_CC > 0x590) - #define WTF_ALIGN_OF(type) __alignof__(type) - #define WTF_ALIGNED(variable_type, variable, n) variable_type variable __attribute__((__aligned__(n))) -#elif COMPILER(MSVC) - #define WTF_ALIGN_OF(type) __alignof(type) - #define WTF_ALIGNED(variable_type, variable, n) __declspec(align(n)) variable_type variable -#else - #error WTF_ALIGN macros need alignment control. -#endif - -#if COMPILER(GCC) && !COMPILER(INTEL) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 303) - typedef char __attribute__((__may_alias__)) AlignedBufferChar; -#else - typedef char AlignedBufferChar; -#endif - - template struct AlignedBuffer; - template struct AlignedBuffer { AlignedBufferChar buffer[size]; }; - template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 2); }; - template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 4); }; - template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 8); }; - template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 16); }; - template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 32); }; - template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 64); }; - - template - void swap(AlignedBuffer& a, AlignedBuffer& b) - { - for (size_t i = 0; i < size; ++i) - std::swap(a.buffer[i], b.buffer[i]); - } - - template - inline bool isAlignedTo(const void* pointer) - { - return !(reinterpret_cast(pointer) & mask); - } -} - -#endif // WTF_Alignment_h diff --git a/masm/wtf/AlwaysInline.h b/masm/wtf/AlwaysInline.h deleted file mode 100644 index 68b7ae1a80..0000000000 --- a/masm/wtf/AlwaysInline.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2005, 2007, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -/* This file is no longer necessary, since all the functionality has been moved to Compiler.h. */ - -#include diff --git a/masm/wtf/PassRefPtr.h b/masm/wtf/PassRefPtr.h deleted file mode 100644 index 5f50671135..0000000000 --- a/masm/wtf/PassRefPtr.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef WTF_PassRefPtr_h -#define WTF_PassRefPtr_h - -#include -#include -#include - -namespace WTF { - - template class RefPtr; - template class PassRefPtr; - template PassRefPtr adoptRef(T*); - - inline void adopted(const void*) { } - -#if !(PLATFORM(QT) && CPU(ARM)) - #define REF_DEREF_INLINE ALWAYS_INLINE -#else - // Older version of gcc used by Harmattan SDK fails to build with ALWAYS_INLINE. - // See https://bugs.webkit.org/show_bug.cgi?id=37253 for details. - #define REF_DEREF_INLINE inline -#endif - - template REF_DEREF_INLINE void refIfNotNull(T* ptr) - { - if (LIKELY(ptr != 0)) - ptr->ref(); - } - - template REF_DEREF_INLINE void derefIfNotNull(T* ptr) - { - if (LIKELY(ptr != 0)) - ptr->deref(); - } - - #undef REF_DEREF_INLINE - - template class PassRefPtr { - public: - PassRefPtr() : m_ptr(0) { } - PassRefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); } - // It somewhat breaks the type system to allow transfer of ownership out of - // a const PassRefPtr. However, it makes it much easier to work with PassRefPtr - // temporaries, and we don't have a need to use real const PassRefPtrs anyway. - PassRefPtr(const PassRefPtr& o) : m_ptr(o.leakRef()) { } - template PassRefPtr(const PassRefPtr& o) : m_ptr(o.leakRef()) { } - - ALWAYS_INLINE ~PassRefPtr() { derefIfNotNull(m_ptr); } - - template PassRefPtr(const RefPtr&); - - T* get() const { return m_ptr; } - - T* leakRef() const WARN_UNUSED_RETURN; - - T& operator*() const { return *m_ptr; } - T* operator->() const { return m_ptr; } - - bool operator!() const { return !m_ptr; } - - // This conversion operator allows implicit conversion to bool but not to other integer types. - typedef T* (PassRefPtr::*UnspecifiedBoolType); - operator UnspecifiedBoolType() const { return m_ptr ? &PassRefPtr::m_ptr : 0; } - - PassRefPtr& operator=(const PassRefPtr&) { COMPILE_ASSERT(!sizeof(T*), PassRefPtr_should_never_be_assigned_to); return *this; } - - friend PassRefPtr adoptRef(T*); - - private: - // adopting constructor - PassRefPtr(T* ptr, bool) : m_ptr(ptr) { } - - mutable T* m_ptr; - }; - - template template inline PassRefPtr::PassRefPtr(const RefPtr& o) - : m_ptr(o.get()) - { - T* ptr = m_ptr; - refIfNotNull(ptr); - } - - template inline T* PassRefPtr::leakRef() const - { - T* ptr = m_ptr; - m_ptr = 0; - return ptr; - } - - template inline bool operator==(const PassRefPtr& a, const PassRefPtr& b) - { - return a.get() == b.get(); - } - - template inline bool operator==(const PassRefPtr& a, const RefPtr& b) - { - return a.get() == b.get(); - } - - template inline bool operator==(const RefPtr& a, const PassRefPtr& b) - { - return a.get() == b.get(); - } - - template inline bool operator==(const PassRefPtr& a, U* b) - { - return a.get() == b; - } - - template inline bool operator==(T* a, const PassRefPtr& b) - { - return a == b.get(); - } - - template inline bool operator!=(const PassRefPtr& a, const PassRefPtr& b) - { - return a.get() != b.get(); - } - - template inline bool operator!=(const PassRefPtr& a, const RefPtr& b) - { - return a.get() != b.get(); - } - - template inline bool operator!=(const RefPtr& a, const PassRefPtr& b) - { - return a.get() != b.get(); - } - - template inline bool operator!=(const PassRefPtr& a, U* b) - { - return a.get() != b; - } - - template inline bool operator!=(T* a, const PassRefPtr& b) - { - return a != b.get(); - } - - template inline PassRefPtr adoptRef(T* p) - { - adopted(p); - return PassRefPtr(p, true); - } - - template inline PassRefPtr static_pointer_cast(const PassRefPtr& p) - { - return adoptRef(static_cast(p.leakRef())); - } - - template inline PassRefPtr const_pointer_cast(const PassRefPtr& p) - { - return adoptRef(const_cast(p.leakRef())); - } - - template inline T* getPtr(const PassRefPtr& p) - { - return p.get(); - } - -} // namespace WTF - -using WTF::PassRefPtr; -using WTF::adoptRef; -using WTF::static_pointer_cast; -using WTF::const_pointer_cast; - -#endif // WTF_PassRefPtr_h diff --git a/masm/wtf/RefCounted.h b/masm/wtf/RefCounted.h deleted file mode 100644 index 0504b9ed28..0000000000 --- a/masm/wtf/RefCounted.h +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef RefCounted_h -#define RefCounted_h - -#include -#include -#include -#include -#include -#include - -namespace WTF { - -#ifdef NDEBUG -#define CHECK_REF_COUNTED_LIFECYCLE 0 -#else -#define CHECK_REF_COUNTED_LIFECYCLE 1 -#endif - -// This base class holds the non-template methods and attributes. -// The RefCounted class inherits from it reducing the template bloat -// generated by the compiler (technique called template hoisting). -class RefCountedBase { -public: - void ref() - { -#if CHECK_REF_COUNTED_LIFECYCLE - // Start thread verification as soon as the ref count gets to 2. This - // heuristic reflects the fact that items are often created on one thread - // and then given to another thread to be used. - // FIXME: Make this restriction tigher. Especially as we move to more - // common methods for sharing items across threads like CrossThreadCopier.h - // We should be able to add a "detachFromThread" method to make this explicit. - if (m_refCount == 1) - m_verifier.setShared(true); - // If this assert fires, it either indicates a thread safety issue or - // that the verification needs to change. See ThreadRestrictionVerifier for - // the different modes. - ASSERT(m_verifier.isSafeToUse()); - ASSERT(!m_deletionHasBegun); - ASSERT(!m_adoptionIsRequired); -#endif - ++m_refCount; - } - - bool hasOneRef() const - { -#if CHECK_REF_COUNTED_LIFECYCLE - ASSERT(m_verifier.isSafeToUse()); - ASSERT(!m_deletionHasBegun); -#endif - return m_refCount == 1; - } - - int refCount() const - { -#if CHECK_REF_COUNTED_LIFECYCLE - ASSERT(m_verifier.isSafeToUse()); -#endif - return m_refCount; - } - - void setMutexForVerifier(Mutex&); - -#if HAVE(DISPATCH_H) - void setDispatchQueueForVerifier(dispatch_queue_t); -#endif - - // Turns off verification. Use of this method is discouraged (instead extend - // ThreadRestrictionVerifier to verify your case). - // NB. It is necessary to call this in the constructor of many objects in - // JavaScriptCore, because JavaScriptCore objects may be used from multiple - // threads even if the reference counting is done in a racy manner. This is - // because a JSC instance may be used from multiple threads so long as all - // accesses into that instance are protected by a per-instance lock. It would - // be absolutely wrong to prohibit this pattern, and it would be a disastrous - // regression to require that the objects within that instance use a thread- - // safe version of reference counting. - void turnOffVerifier() - { -#if CHECK_REF_COUNTED_LIFECYCLE - m_verifier.turnOffVerification(); -#endif - } - - void relaxAdoptionRequirement() - { -#if CHECK_REF_COUNTED_LIFECYCLE - ASSERT(!m_deletionHasBegun); - ASSERT(m_adoptionIsRequired); - m_adoptionIsRequired = false; -#endif - } - - // Helper for generating JIT code. Please do not use for non-JIT purposes. - const int* addressOfCount() const - { - return &m_refCount; - } - -protected: - RefCountedBase() - : m_refCount(1) -#if CHECK_REF_COUNTED_LIFECYCLE - , m_deletionHasBegun(false) - , m_adoptionIsRequired(true) -#endif - { - } - - ~RefCountedBase() - { -#if CHECK_REF_COUNTED_LIFECYCLE - ASSERT(m_deletionHasBegun); - ASSERT(!m_adoptionIsRequired); -#endif - } - - // Returns whether the pointer should be freed or not. - bool derefBase() - { -#if CHECK_REF_COUNTED_LIFECYCLE - ASSERT(m_verifier.isSafeToUse()); - ASSERT(!m_deletionHasBegun); - ASSERT(!m_adoptionIsRequired); -#endif - - ASSERT(m_refCount > 0); - if (m_refCount == 1) { -#if CHECK_REF_COUNTED_LIFECYCLE - m_deletionHasBegun = true; -#endif - return true; - } - - --m_refCount; -#if CHECK_REF_COUNTED_LIFECYCLE - // Stop thread verification when the ref goes to 1 because it - // is safe to be passed to another thread at this point. - if (m_refCount == 1) - m_verifier.setShared(false); -#endif - return false; - } - -#if CHECK_REF_COUNTED_LIFECYCLE - bool deletionHasBegun() const - { - return m_deletionHasBegun; - } -#endif - -private: - -#if CHECK_REF_COUNTED_LIFECYCLE - friend void adopted(RefCountedBase*); -#endif - - int m_refCount; -#if CHECK_REF_COUNTED_LIFECYCLE - bool m_deletionHasBegun; - bool m_adoptionIsRequired; - ThreadRestrictionVerifier m_verifier; -#endif -}; - -#if CHECK_REF_COUNTED_LIFECYCLE -inline void adopted(RefCountedBase* object) -{ - if (!object) - return; - ASSERT(!object->m_deletionHasBegun); - object->m_adoptionIsRequired = false; -} -#endif - -template class RefCounted : public RefCountedBase { - WTF_MAKE_NONCOPYABLE(RefCounted); WTF_MAKE_FAST_ALLOCATED; -public: - void deref() - { - if (derefBase()) - delete static_cast(this); - } - -protected: - RefCounted() { } - ~RefCounted() - { - } -}; - -template class RefCountedCustomAllocated : public RefCountedBase { - WTF_MAKE_NONCOPYABLE(RefCountedCustomAllocated); - -public: - void deref() - { - if (derefBase()) - delete static_cast(this); - } - -protected: - ~RefCountedCustomAllocated() - { - } -}; - -#if CHECK_REF_COUNTED_LIFECYCLE -inline void RefCountedBase::setMutexForVerifier(Mutex& mutex) -{ - m_verifier.setMutexMode(mutex); -} -#else -inline void RefCountedBase::setMutexForVerifier(Mutex&) { } -#endif - -#if HAVE(DISPATCH_H) -#if CHECK_REF_COUNTED_LIFECYCLE -inline void RefCountedBase::setDispatchQueueForVerifier(dispatch_queue_t queue) -{ - m_verifier.setDispatchQueueMode(queue); -} -#else -inline void RefCountedBase::setDispatchQueueForVerifier(dispatch_queue_t) { } -#endif -#endif // HAVE(DISPATCH_H) - -} // namespace WTF - -using WTF::RefCounted; -using WTF::RefCountedCustomAllocated; - -#endif // RefCounted_h diff --git a/masm/wtf/RefPtr.h b/masm/wtf/RefPtr.h deleted file mode 100644 index 322cbd6f8f..0000000000 --- a/masm/wtf/RefPtr.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -// RefPtr and PassRefPtr are documented at http://webkit.org/coding/RefPtr.html - -#ifndef WTF_RefPtr_h -#define WTF_RefPtr_h - -#include -#include -#include - -namespace WTF { - - enum PlacementNewAdoptType { PlacementNewAdopt }; - - template class PassRefPtr; - - enum HashTableDeletedValueType { HashTableDeletedValue }; - - template class RefPtr { - WTF_MAKE_FAST_ALLOCATED; - public: - ALWAYS_INLINE RefPtr() : m_ptr(0) { } - ALWAYS_INLINE RefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); } - ALWAYS_INLINE RefPtr(const RefPtr& o) : m_ptr(o.m_ptr) { refIfNotNull(m_ptr); } - template RefPtr(const RefPtr& o) : m_ptr(o.get()) { refIfNotNull(m_ptr); } - - // See comments in PassRefPtr.h for an explanation of why this takes a const reference. - template RefPtr(const PassRefPtr&); - - // Special constructor for cases where we overwrite an object in place. - ALWAYS_INLINE RefPtr(PlacementNewAdoptType) { } - - // Hash table deleted values, which are only constructed and never copied or destroyed. - RefPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) { } - bool isHashTableDeletedValue() const { return m_ptr == hashTableDeletedValue(); } - - ALWAYS_INLINE ~RefPtr() { derefIfNotNull(m_ptr); } - - T* get() const { return m_ptr; } - - void clear(); - PassRefPtr release() { PassRefPtr tmp = adoptRef(m_ptr); m_ptr = 0; return tmp; } - - T& operator*() const { return *m_ptr; } - ALWAYS_INLINE T* operator->() const { return m_ptr; } - - bool operator!() const { return !m_ptr; } - - // This conversion operator allows implicit conversion to bool but not to other integer types. - typedef T* (RefPtr::*UnspecifiedBoolType); - operator UnspecifiedBoolType() const { return m_ptr ? &RefPtr::m_ptr : 0; } - - RefPtr& operator=(const RefPtr&); - RefPtr& operator=(T*); - RefPtr& operator=(const PassRefPtr&); -#if !COMPILER_SUPPORTS(CXX_NULLPTR) - RefPtr& operator=(std::nullptr_t) { clear(); return *this; } -#endif - template RefPtr& operator=(const RefPtr&); - template RefPtr& operator=(const PassRefPtr&); - - void swap(RefPtr&); - - static T* hashTableDeletedValue() { return reinterpret_cast(-1); } - - private: - T* m_ptr; - }; - - template template inline RefPtr::RefPtr(const PassRefPtr& o) - : m_ptr(o.leakRef()) - { - } - - template inline void RefPtr::clear() - { - T* ptr = m_ptr; - m_ptr = 0; - derefIfNotNull(ptr); - } - - template inline RefPtr& RefPtr::operator=(const RefPtr& o) - { - T* optr = o.get(); - refIfNotNull(optr); - T* ptr = m_ptr; - m_ptr = optr; - derefIfNotNull(ptr); - return *this; - } - - template template inline RefPtr& RefPtr::operator=(const RefPtr& o) - { - T* optr = o.get(); - refIfNotNull(optr); - T* ptr = m_ptr; - m_ptr = optr; - derefIfNotNull(ptr); - return *this; - } - - template inline RefPtr& RefPtr::operator=(T* optr) - { - refIfNotNull(optr); - T* ptr = m_ptr; - m_ptr = optr; - derefIfNotNull(ptr); - return *this; - } - - template inline RefPtr& RefPtr::operator=(const PassRefPtr& o) - { - T* ptr = m_ptr; - m_ptr = o.leakRef(); - derefIfNotNull(ptr); - return *this; - } - - template template inline RefPtr& RefPtr::operator=(const PassRefPtr& o) - { - T* ptr = m_ptr; - m_ptr = o.leakRef(); - derefIfNotNull(ptr); - return *this; - } - - template inline void RefPtr::swap(RefPtr& o) - { - std::swap(m_ptr, o.m_ptr); - } - - template inline void swap(RefPtr& a, RefPtr& b) - { - a.swap(b); - } - - template inline bool operator==(const RefPtr& a, const RefPtr& b) - { - return a.get() == b.get(); - } - - template inline bool operator==(const RefPtr& a, U* b) - { - return a.get() == b; - } - - template inline bool operator==(T* a, const RefPtr& b) - { - return a == b.get(); - } - - template inline bool operator!=(const RefPtr& a, const RefPtr& b) - { - return a.get() != b.get(); - } - - template inline bool operator!=(const RefPtr& a, U* b) - { - return a.get() != b; - } - - template inline bool operator!=(T* a, const RefPtr& b) - { - return a != b.get(); - } - - template inline RefPtr static_pointer_cast(const RefPtr& p) - { - return RefPtr(static_cast(p.get())); - } - - template inline RefPtr const_pointer_cast(const RefPtr& p) - { - return RefPtr(const_cast(p.get())); - } - - template inline T* getPtr(const RefPtr& p) - { - return p.get(); - } - -} // namespace WTF - -using WTF::RefPtr; -using WTF::static_pointer_cast; -using WTF::const_pointer_cast; - -#endif // WTF_RefPtr_h diff --git a/masm/wtf/ThreadRestrictionVerifier.h b/masm/wtf/ThreadRestrictionVerifier.h deleted file mode 100644 index cff49d3184..0000000000 --- a/masm/wtf/ThreadRestrictionVerifier.h +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ThreadRestrictionVerifier_h -#define ThreadRestrictionVerifier_h - -#include -#include -#include - -#if HAVE(DISPATCH_H) -#include -#endif - -#ifndef NDEBUG - -namespace WTF { - -// Verifies that a class is used in a way that respects its lack of thread-safety. -// The default mode is to verify that the object will only be used on a single thread. The -// thread gets captured when setShared(true) is called. -// The mode may be changed by calling useMutexMode (or turnOffVerification). -#if !USE(JSC) // This verifier is completely wrong for JavaScript implementations that use threads -class ThreadRestrictionVerifier { -public: - ThreadRestrictionVerifier() - : m_mode(SingleThreadVerificationMode) - , m_shared(false) - , m_owningThread(0) - , m_mutex(0) -#if HAVE(DISPATCH_H) - , m_owningQueue(0) -#endif - { - } - -#if HAVE(DISPATCH_H) - ~ThreadRestrictionVerifier() - { - if (m_owningQueue) - dispatch_release(m_owningQueue); - } -#endif - - void setMutexMode(Mutex& mutex) - { - m_mode = MutexVerificationMode; - m_mutex = &mutex; - } - -#if HAVE(DISPATCH_H) - void setDispatchQueueMode(dispatch_queue_t queue) - { - m_mode = SingleDispatchQueueVerificationMode; - m_owningQueue = queue; - dispatch_retain(m_owningQueue); - } -#endif - - void turnOffVerification() - { - m_mode = NoVerificationMode; - } - - // Indicates that the object may (or may not) be owned by more than one place. - void setShared(bool shared) - { -#if !ASSERT_DISABLED - bool previouslyShared = m_shared; -#endif - m_shared = shared; - - if (!m_shared) - return; - - switch (m_mode) { - case SingleThreadVerificationMode: - ASSERT(shared != previouslyShared); - // Capture the current thread to verify that subsequent ref/deref happen on this thread. - m_owningThread = currentThread(); - return; - -#if HAVE(DISPATCH_H) - case SingleDispatchQueueVerificationMode: -#endif - case MutexVerificationMode: - case NoVerificationMode: - return; - } - ASSERT_NOT_REACHED(); - } - - // Is it OK to use the object at this moment on the current thread? - bool isSafeToUse() const - { - if (!m_shared) - return true; - - switch (m_mode) { - case SingleThreadVerificationMode: - return m_owningThread == currentThread(); - - case MutexVerificationMode: - if (!m_mutex->tryLock()) - return true; - m_mutex->unlock(); - return false; - -#if HAVE(DISPATCH_H) - case SingleDispatchQueueVerificationMode: - return m_owningQueue == dispatch_get_current_queue(); -#endif - - case NoVerificationMode: - return true; - } - ASSERT_NOT_REACHED(); - return true; - } - -private: - enum VerificationMode { - SingleThreadVerificationMode, - MutexVerificationMode, - NoVerificationMode, -#if HAVE(DISPATCH_H) - SingleDispatchQueueVerificationMode, -#endif - }; - - VerificationMode m_mode; - bool m_shared; - - // Used by SingleThreadVerificationMode - ThreadIdentifier m_owningThread; - - // Used by MutexVerificationMode. - Mutex* m_mutex; - -#if HAVE(DISPATCH_H) - // Used by SingleDispatchQueueVerificationMode. - dispatch_queue_t m_owningQueue; -#endif -}; -#else // !USE(JSC) => so the JSC case -class ThreadRestrictionVerifier { -public: - ThreadRestrictionVerifier() - { - } - - void setMutexMode(Mutex&) - { - } - -#if HAVE(DISPATCH_H) - void setDispatchQueueMode(dispatch_queue_t) - { - } -#endif - - void turnOffVerification() - { - } - - // Indicates that the object may (or may not) be owned by more than one place. - void setShared(bool) - { - } - - // Is it OK to use the object at this moment on the current thread? - bool isSafeToUse() const - { - return true; - } -}; -#endif - -} - -#endif -#endif diff --git a/masm/wtf/ThreadSafeRefCounted.h b/masm/wtf/ThreadSafeRefCounted.h deleted file mode 100644 index 44035e5474..0000000000 --- a/masm/wtf/ThreadSafeRefCounted.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. - * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * Note: The implementations of InterlockedIncrement and InterlockedDecrement are based - * on atomic_increment and atomic_exchange_and_add from the Boost C++ Library. The license - * is virtually identical to the Apple license above but is included here for completeness. - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, - * must be included in all copies of the Software, in whole or in part, and - * all derivative works of the Software, unless such copies or derivative - * works are solely in the form of machine-executable object code generated by - * a source language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef ThreadSafeRefCounted_h -#define ThreadSafeRefCounted_h - -#include - -#include -#include -#include - -namespace WTF { - -class ThreadSafeRefCountedBase { - WTF_MAKE_NONCOPYABLE(ThreadSafeRefCountedBase); - WTF_MAKE_FAST_ALLOCATED; -public: - ThreadSafeRefCountedBase(int initialRefCount = 1) - : m_refCount(initialRefCount) - { - } - - void ref() - { -#if USE(LOCKFREE_THREADSAFEREFCOUNTED) - atomicIncrement(&m_refCount); -#else - MutexLocker locker(m_mutex); - ++m_refCount; -#endif - } - - bool hasOneRef() - { - return refCount() == 1; - } - - int refCount() const - { -#if !USE(LOCKFREE_THREADSAFEREFCOUNTED) - MutexLocker locker(m_mutex); -#endif - return static_cast(m_refCount); - } - -protected: - // Returns whether the pointer should be freed or not. - bool derefBase() - { -#if USE(LOCKFREE_THREADSAFEREFCOUNTED) - WTF_ANNOTATE_HAPPENS_BEFORE(&m_refCount); - if (atomicDecrement(&m_refCount) <= 0) { - WTF_ANNOTATE_HAPPENS_AFTER(&m_refCount); - return true; - } -#else - int refCount; - { - MutexLocker locker(m_mutex); - --m_refCount; - refCount = m_refCount; - } - if (refCount <= 0) - return true; -#endif - return false; - } - -private: - int m_refCount; -#if !USE(LOCKFREE_THREADSAFEREFCOUNTED) - mutable Mutex m_mutex; -#endif -}; - -template class ThreadSafeRefCounted : public ThreadSafeRefCountedBase { -public: - void deref() - { - if (derefBase()) - delete static_cast(this); - } - -protected: - ThreadSafeRefCounted() - { - } -}; - -} // namespace WTF - -using WTF::ThreadSafeRefCounted; - -#endif // ThreadSafeRefCounted_h diff --git a/masm/wtf/Threading.h b/masm/wtf/Threading.h deleted file mode 100644 index 3e558fc681..0000000000 --- a/masm/wtf/Threading.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. - * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * Note: The implementations of InterlockedIncrement and InterlockedDecrement are based - * on atomic_increment and atomic_exchange_and_add from the Boost C++ Library. The license - * is virtually identical to the Apple license above but is included here for completeness. - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, - * must be included in all copies of the Software, in whole or in part, and - * all derivative works of the Software, unless such copies or derivative - * works are solely in the form of machine-executable object code generated by - * a source language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef Threading_h -#define Threading_h - -#include - -#include -#include -#include -#include -#include -#include -#include - -// For portability, we do not use thread-safe statics natively supported by some compilers (e.g. gcc). -#define AtomicallyInitializedStatic(T, name) \ - WTF::lockAtomicallyInitializedStaticMutex(); \ - static T name; \ - WTF::unlockAtomicallyInitializedStaticMutex(); - -namespace WTF { - -typedef uint32_t ThreadIdentifier; -typedef void (*ThreadFunction)(void* argument); - -// This function must be called from the main thread. It is safe to call it repeatedly. -// Darwin is an exception to this rule: it is OK to call it from any thread, the only -// requirement is that the calls are not reentrant. -WTF_EXPORT_PRIVATE void initializeThreading(); - -// Returns 0 if thread creation failed. -// The thread name must be a literal since on some platforms it's passed in to the thread. -WTF_EXPORT_PRIVATE ThreadIdentifier createThread(ThreadFunction, void*, const char* threadName); - -// Internal platform-specific createThread implementation. -ThreadIdentifier createThreadInternal(ThreadFunction, void*, const char* threadName); - -// Called in the thread during initialization. -// Helpful for platforms where the thread name must be set from within the thread. -void initializeCurrentThreadInternal(const char* threadName); - -WTF_EXPORT_PRIVATE ThreadIdentifier currentThread(); -WTF_EXPORT_PRIVATE int waitForThreadCompletion(ThreadIdentifier); -WTF_EXPORT_PRIVATE void detachThread(ThreadIdentifier); - -WTF_EXPORT_PRIVATE void yield(); - -WTF_EXPORT_PRIVATE void lockAtomicallyInitializedStaticMutex(); -WTF_EXPORT_PRIVATE void unlockAtomicallyInitializedStaticMutex(); - -} // namespace WTF - -using WTF::ThreadIdentifier; -using WTF::createThread; -using WTF::currentThread; -using WTF::detachThread; -using WTF::waitForThreadCompletion; -using WTF::yield; - -#endif // Threading_h diff --git a/masm/wtf/ThreadingPrimitives.h b/masm/wtf/ThreadingPrimitives.h deleted file mode 100644 index abfc36def6..0000000000 --- a/masm/wtf/ThreadingPrimitives.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. - * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef ThreadingPrimitives_h -#define ThreadingPrimitives_h - -#include - -#include -#include -#include -#include - -#if OS(WINDOWS) -#include -#endif - -#if USE(PTHREADS) -#include -#endif - -namespace WTF { - -#if USE(PTHREADS) -typedef pthread_mutex_t PlatformMutex; -typedef pthread_cond_t PlatformCondition; -#elif OS(WINDOWS) -struct PlatformMutex { - CRITICAL_SECTION m_internalMutex; - size_t m_recursionCount; -}; -struct PlatformCondition { - size_t m_waitersGone; - size_t m_waitersBlocked; - size_t m_waitersToUnblock; - HANDLE m_blockLock; - HANDLE m_blockQueue; - HANDLE m_unblockLock; - - bool timedWait(PlatformMutex&, DWORD durationMilliseconds); - void signal(bool unblockAll); -}; -#else -typedef void* PlatformMutex; -typedef void* PlatformCondition; -#endif - -class Mutex { - WTF_MAKE_NONCOPYABLE(Mutex); WTF_MAKE_FAST_ALLOCATED; -public: - WTF_EXPORT_PRIVATE Mutex(); - WTF_EXPORT_PRIVATE ~Mutex(); - - WTF_EXPORT_PRIVATE void lock(); - WTF_EXPORT_PRIVATE bool tryLock(); - WTF_EXPORT_PRIVATE void unlock(); - -public: - PlatformMutex& impl() { return m_mutex; } -private: - PlatformMutex m_mutex; -}; - -typedef Locker MutexLocker; - -class MutexTryLocker { - WTF_MAKE_NONCOPYABLE(MutexTryLocker); -public: - MutexTryLocker(Mutex& mutex) : m_mutex(mutex), m_locked(mutex.tryLock()) { } - ~MutexTryLocker() - { - if (m_locked) - m_mutex.unlock(); - } - - bool locked() const { return m_locked; } - -private: - Mutex& m_mutex; - bool m_locked; -}; - -class ThreadCondition { - WTF_MAKE_NONCOPYABLE(ThreadCondition); -public: - WTF_EXPORT_PRIVATE ThreadCondition(); - WTF_EXPORT_PRIVATE ~ThreadCondition(); - - WTF_EXPORT_PRIVATE void wait(Mutex& mutex); - // Returns true if the condition was signaled before absoluteTime, false if the absoluteTime was reached or is in the past. - // The absoluteTime is in seconds, starting on January 1, 1970. The time is assumed to use the same time zone as WTF::currentTime(). - WTF_EXPORT_PRIVATE bool timedWait(Mutex&, double absoluteTime); - WTF_EXPORT_PRIVATE void signal(); - WTF_EXPORT_PRIVATE void broadcast(); - -private: - PlatformCondition m_condition; -}; - -#if OS(WINDOWS) -// The absoluteTime is in seconds, starting on January 1, 1970. The time is assumed to use the same time zone as WTF::currentTime(). -// Returns an interval in milliseconds suitable for passing to one of the Win32 wait functions (e.g., ::WaitForSingleObject). -WTF_EXPORT_PRIVATE DWORD absoluteTimeToWaitTimeoutInterval(double absoluteTime); -#endif - -} // namespace WTF - -using WTF::Mutex; -using WTF::MutexLocker; -using WTF::MutexTryLocker; -using WTF::ThreadCondition; - -#if OS(WINDOWS) -using WTF::absoluteTimeToWaitTimeoutInterval; -#endif - -#endif // ThreadingPrimitives_h -- cgit v1.2.3 From 90f96659521b76b71184753d1a393604c2fc7c86 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 7 Jan 2013 13:09:44 +0100 Subject: new implementation for array data The old implementation was using a std::deque, which doesn't quite scale as it needs to scale. The new implementation uses a modified red-black tree to store the array data. The modifications are similar to the ones in qfragmentmap, allowing for ver efficient removal from the front of the array. Change-Id: Iae343981f958bb5fafd0618597b67180d7834dbb Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 4 +- qmljs_objects.h | 2 +- qmljs_runtime.cpp | 7 +- qmljs_value.h | 1 - qv4array.cpp | 463 ++++++++++++++++++++++++++++++++++- qv4array.h | 694 +++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4array_p.h | 238 ------------------ qv4ecmaobjects.cpp | 81 ++++--- v4.pro | 8 +- 9 files changed, 1211 insertions(+), 287 deletions(-) create mode 100644 qv4array.h delete mode 100644 qv4array_p.h diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 9070128543..22c1eabf58 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -440,7 +440,7 @@ Value ArrayObject::__get__(ExecutionContext *ctx, String *name, bool *hasPropert if (name->isEqualTo(ctx->engine->id_length)) { if (hasProperty) *hasProperty = true; - return Value::fromDouble(value.size()); + return Value::fromDouble(value.length()); } return Object::__get__(ctx, name, hasProperty); } @@ -451,7 +451,7 @@ bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContex const quint32 idx = index.toUInt32(ctx); Value v = value.at(idx); v = op(v, rhs, ctx); - value.assign(idx, v); + value.insert(idx, v); return true; } return Object::inplaceBinOp(rhs, index, op, ctx); diff --git a/qmljs_objects.h b/qmljs_objects.h index 9645ea701b..d8fc2bf5b9 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -44,7 +44,7 @@ #include "qmljs_runtime.h" #include "qmljs_engine.h" #include "qmljs_environment.h" -#include "qv4array_p.h" +#include "qv4array.h" #include "qv4codegen_p.h" #include "qv4isel_p.h" #include "qv4managed.h" diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 80e4a9ddc1..2901960932 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -176,8 +176,9 @@ Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index) else if (index.isDouble()) n = index.doubleValue(); if (n >= 0) { - if (n < (int) a->value.size()) { - a->value.assign(n, Value::undefinedValue()); + if (n < (int) a->value.length()) { + // ### check writable/deletable property + a->value.remove(n); return Value::fromBoolean(true); } } @@ -585,7 +586,7 @@ void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value { if (index.isNumber()) { if (ArrayObject *a = object.asArrayObject()) { - a->value.assign(index.toUInt32(ctx), value); + a->value.insert(index.toUInt32(ctx), value); return; } } diff --git a/qmljs_value.h b/qmljs_value.h index 9446c79dc3..b8c78d3f1f 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -50,7 +50,6 @@ namespace QQmlJS { namespace VM { -class Array; struct String; struct Object; struct BooleanObject; diff --git a/qv4array.cpp b/qv4array.cpp index 7f05d018fa..9709e329e0 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the V4VM module of the Qt Toolkit. +** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -39,11 +39,18 @@ ** ****************************************************************************/ - -#include "qv4array_p.h" +#include "qv4array.h" +#include "qmljs_runtime.h" #include "qmljs_objects.h" +#include + +#ifdef QT_QMAP_DEBUG +# include +# include +#endif -using namespace QQmlJS::VM; +namespace QQmlJS { +namespace VM { bool ArrayElementLessThan::operator()(const Value &v1, const Value &v2) const { @@ -58,3 +65,451 @@ bool ArrayElementLessThan::operator()(const Value &v1, const Value &v2) const } return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); } + + +const SparseArrayNode *SparseArrayNode::nextNode() const +{ + const SparseArrayNode *n = this; + if (n->right) { + n = n->right; + while (n->left) + n = n->left; + } else { + const SparseArrayNode *y = n->parent(); + while (y && n == y->right) { + n = y; + y = n->parent(); + } + n = y; + } + return n; +} + +const SparseArrayNode *SparseArrayNode::previousNode() const +{ + const SparseArrayNode *n = this; + if (n->left) { + n = n->left; + while (n->right) + n = n->right; + } else { + const SparseArrayNode *y = n->parent(); + while (y && n == y->left) { + n = y; + y = n->parent(); + } + n = y; + } + return n; +} + +SparseArrayNode *SparseArrayNode::copy(SparseArrayData *d) const +{ + SparseArrayNode *n = d->createNode(size_left, value, 0, false); + n->setColor(color()); + if (left) { + n->left = left->copy(d); + n->left->setParent(n); + } else { + n->left = 0; + } + if (right) { + n->right = right->copy(d); + n->right->setParent(n); + } else { + n->right = 0; + } + return n; +} + +/* + x y + \ / \ + y --> x b + / \ \ + a b a +*/ +void SparseArrayData::rotateLeft(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = x->right; + x->right = y->left; + if (y->left != 0) + y->left->setParent(x); + y->setParent(x->parent()); + if (x == root) + root = y; + else if (x == x->parent()->left) + x->parent()->left = y; + else + x->parent()->right = y; + y->left = x; + x->setParent(y); + y->size_left += x->size_left; +} + + +/* + x y + / / \ + y --> a x + / \ / + a b b +*/ +void SparseArrayData::rotateRight(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = x->left; + x->left = y->right; + if (y->right != 0) + y->right->setParent(x); + y->setParent(x->parent()); + if (x == root) + root = y; + else if (x == x->parent()->right) + x->parent()->right = y; + else + x->parent()->left = y; + y->right = x; + x->setParent(y); + x->size_left -= y->size_left; +} + + +void SparseArrayData::rebalance(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + x->setColor(SparseArrayNode::Red); + while (x != root && x->parent()->color() == SparseArrayNode::Red) { + if (x->parent() == x->parent()->parent()->left) { + SparseArrayNode *y = x->parent()->parent()->right; + if (y && y->color() == SparseArrayNode::Red) { + x->parent()->setColor(SparseArrayNode::Black); + y->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + x = x->parent()->parent(); + } else { + if (x == x->parent()->right) { + x = x->parent(); + rotateLeft(x); + } + x->parent()->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + rotateRight (x->parent()->parent()); + } + } else { + SparseArrayNode *y = x->parent()->parent()->left; + if (y && y->color() == SparseArrayNode::Red) { + x->parent()->setColor(SparseArrayNode::Black); + y->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + x = x->parent()->parent(); + } else { + if (x == x->parent()->left) { + x = x->parent(); + rotateRight(x); + } + x->parent()->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + rotateLeft(x->parent()->parent()); + } + } + } + root->setColor(SparseArrayNode::Black); +} + +void SparseArrayData::deleteNode(SparseArrayNode *z) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = z; + SparseArrayNode *x; + SparseArrayNode *x_parent; + if (y->left == 0) { + x = y->right; + if (y == mostLeftNode) { + if (x) + mostLeftNode = x; // It cannot have (left) children due the red black invariant. + else + mostLeftNode = y->parent(); + } + } else { + if (y->right == 0) { + x = y->left; + } else { + y = y->right; + while (y->left != 0) + y = y->left; + x = y->right; + } + } + if (y != z) { + z->left->setParent(y); + y->left = z->left; + if (y != z->right) { + x_parent = y->parent(); + if (x) + x->setParent(y->parent()); + y->parent()->left = x; + y->right = z->right; + z->right->setParent(y); + } else { + x_parent = y; + } + if (root == z) + root = y; + else if (z->parent()->left == z) + z->parent()->left = y; + else + z->parent()->right = y; + y->setParent(z->parent()); + // Swap the colors + SparseArrayNode::Color c = y->color(); + y->setColor(z->color()); + z->setColor(c); + y = z; + } else { + x_parent = y->parent(); + if (x) + x->setParent(y->parent()); + if (root == z) + root = x; + else if (z->parent()->left == z) + z->parent()->left = x; + else + z->parent()->right = x; + } + if (y->color() != SparseArrayNode::Red) { + while (x != root && (x == 0 || x->color() == SparseArrayNode::Black)) { + if (x == x_parent->left) { + SparseArrayNode *w = x_parent->right; + if (w->color() == SparseArrayNode::Red) { + w->setColor(SparseArrayNode::Black); + x_parent->setColor(SparseArrayNode::Red); + rotateLeft(x_parent); + w = x_parent->right; + } + if ((w->left == 0 || w->left->color() == SparseArrayNode::Black) && + (w->right == 0 || w->right->color() == SparseArrayNode::Black)) { + w->setColor(SparseArrayNode::Red); + x = x_parent; + x_parent = x_parent->parent(); + } else { + if (w->right == 0 || w->right->color() == SparseArrayNode::Black) { + if (w->left) + w->left->setColor(SparseArrayNode::Black); + w->setColor(SparseArrayNode::Red); + rotateRight(w); + w = x_parent->right; + } + w->setColor(x_parent->color()); + x_parent->setColor(SparseArrayNode::Black); + if (w->right) + w->right->setColor(SparseArrayNode::Black); + rotateLeft(x_parent); + break; + } + } else { + SparseArrayNode *w = x_parent->left; + if (w->color() == SparseArrayNode::Red) { + w->setColor(SparseArrayNode::Black); + x_parent->setColor(SparseArrayNode::Red); + rotateRight(x_parent); + w = x_parent->left; + } + if ((w->right == 0 || w->right->color() == SparseArrayNode::Black) && + (w->left == 0 || w->left->color() == SparseArrayNode::Black)) { + w->setColor(SparseArrayNode::Red); + x = x_parent; + x_parent = x_parent->parent(); + } else { + if (w->left == 0 || w->left->color() == SparseArrayNode::Black) { + if (w->right) + w->right->setColor(SparseArrayNode::Black); + w->setColor(SparseArrayNode::Red); + rotateLeft(w); + w = x_parent->left; + } + w->setColor(x_parent->color()); + x_parent->setColor(SparseArrayNode::Black); + if (w->left) + w->left->setColor(SparseArrayNode::Black); + rotateRight(x_parent); + break; + } + } + } + if (x) + x->setColor(SparseArrayNode::Black); + } + free(y); + --numEntries; +} + +void SparseArrayData::recalcMostLeftNode() +{ + mostLeftNode = &header; + while (mostLeftNode->left) + mostLeftNode = mostLeftNode->left; +} + +static inline int qMapAlignmentThreshold() +{ + // malloc on 32-bit platforms should return pointers that are 8-byte + // aligned or more while on 64-bit platforms they should be 16-byte aligned + // or more + return 2 * sizeof(void*); +} + +static inline void *qMapAllocate(int alloc, int alignment) +{ + return alignment > qMapAlignmentThreshold() + ? qMallocAligned(alloc, alignment) + : ::malloc(alloc); +} + +static inline void qMapDeallocate(SparseArrayNode *node, int alignment) +{ + if (alignment > qMapAlignmentThreshold()) + qFreeAligned(node); + else + ::free(node); +} + +SparseArrayNode *SparseArrayData::createNode(uint sl, int value, SparseArrayNode *parent, bool left) +{ + SparseArrayNode *node = static_cast(qMapAllocate(sizeof(SparseArrayNode), Q_ALIGNOF(SparseArrayNode))); + Q_CHECK_PTR(node); + + node->p = (quintptr)parent; + node->left = 0; + node->right = 0; + node->size_left = sl; + node->value = value; + ++numEntries; + + if (parent) { + if (left) { + parent->left = node; + if (parent == mostLeftNode) + mostLeftNode = node; + } else { + parent->right = node; + } + node->setParent(parent); + rebalance(node); + } + return node; +} + +void SparseArrayData::freeTree(SparseArrayNode *root, int alignment) +{ + if (root->left) + freeTree(root->left, alignment); + if (root->right) + freeTree(root->right, alignment); + qMapDeallocate(root, alignment); +} + +SparseArrayData::SparseArrayData() + : numEntries(0) + , length(0) +{ + header.p = 0; + header.left = 0; + header.right = 0; + mostLeftNode = &header; +} + +Array::iterator Array::insert(uint akey, Value value) +{ + if (akey >= d->length) + d->length = akey + 1; + + SparseArrayNode *n = d->root(); + SparseArrayNode *y = d->end(); + bool left = true; + uint s = akey; + while (n) { + y = n; + if (s == n->size_left) { + values[n->value] = value; + return iterator(n); + } else if (s < n->size_left) { + left = true; + n = n->left; + } else { + left = false; + s -= n->size_left; + n = n->right; + } + } + + int idx = allocValue(); + SparseArrayNode *z = d->createNode(s, idx, y, left); + values[idx] = value; + return iterator(z); +} + +bool Array::operator==(const Array &other) const +{ + if (numEntries() != other.numEntries()) + return false; + if (d == other.d) + return true; + + const_iterator it1 = begin(); + const_iterator it2 = other.begin(); + + while (it1 != end()) { + if (it1.value() != it2.value() || it1.key() != it2.key()) + return false; + ++it2; + ++it1; + } + return true; +} + +void Array::setLength(uint l) +{ + if (l == 0) { + clear(); + return; + } + + iterator it = lowerBound(l); + while (it != end()) + it = erase(it); + d->length = l; +} + +void Array::splice(double start, double deleteCount, + const QVector &/*items*/, + Array &/*other*/) +{ + const double len = length(); + if (start < 0) + start = qMax(len + start, double(0)); + else if (start > len) + start = len; + deleteCount = qMax(qMin(deleteCount, len - start), double(0)); + + // ### +// const uint st = uint(start); +// const uint dc = uint(deleteCount); +// other.resize(dc); + +// const uint itemsSize = uint(items.size()); + +// for (uint i = 0; i < dc; ++i) +// other.assign(i, to_vector.at(st + i)); +// if (itemsSize > dc) +// to_vector.insert(to_vector.begin() + st, itemsSize - dc, Value::undefinedValue()); +// else if (itemsSize < dc) +// to_vector.erase(to_vector.begin() + st, to_vector.begin() + (dc - itemsSize)); +// for (uint i = 0; i < itemsSize; ++i) +// (*to_vector)[st + i] = items.at(i); +} + + +} +} diff --git a/qv4array.h b/qv4array.h new file mode 100644 index 0000000000..19d63871bc --- /dev/null +++ b/qv4array.h @@ -0,0 +1,694 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ARRAY_H +#define QV4ARRAY_H + +#include +#include + +#ifdef Q_MAP_DEBUG +#include +#endif + +#include + +namespace QQmlJS { +namespace VM { + +struct SparseArrayData; + +class ArrayElementLessThan +{ +public: + inline ArrayElementLessThan(ExecutionContext *context, const Value &comparefn) + : m_context(context), m_comparefn(comparefn) {} + + bool operator()(const Value &v1, const Value &v2) const; + +private: + ExecutionContext *m_context; + Value m_comparefn; +}; + + +struct SparseArrayNode +{ + quintptr p; + SparseArrayNode *left; + SparseArrayNode *right; + uint size_left; + int value; + + enum Color { Red = 0, Black = 1 }; + enum { Mask = 3 }; // reserve the second bit as well + + const SparseArrayNode *nextNode() const; + SparseArrayNode *nextNode() { return const_cast(const_cast(this)->nextNode()); } + const SparseArrayNode *previousNode() const; + SparseArrayNode *previousNode() { return const_cast(const_cast(this)->previousNode()); } + + Color color() const { return Color(p & 1); } + void setColor(Color c) { if (c == Black) p |= Black; else p &= ~Black; } + SparseArrayNode *parent() const { return reinterpret_cast(p & ~Mask); } + void setParent(SparseArrayNode *pp) { p = (p & Mask) | quintptr(pp); } + + uint key() const { + uint k = size_left; + const SparseArrayNode *n = this; + while (SparseArrayNode *p = n->parent()) { + if (p && p->right == n) + k += p->size_left; + n = p; + } + return k; + } + + SparseArrayNode *copy(SparseArrayData *d) const; + + SparseArrayNode *lowerBound(uint key); + SparseArrayNode *upperBound(uint key); +}; + + +inline SparseArrayNode *SparseArrayNode::lowerBound(uint akey) +{ + SparseArrayNode *n = this; + SparseArrayNode *last = 0; + while (n) { + if (akey <= n->size_left) { + last = n; + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + return last; +} + + +inline SparseArrayNode *SparseArrayNode::upperBound(uint akey) +{ + SparseArrayNode *n = this; + SparseArrayNode *last = 0; + while (n) { + if (akey < n->size_left) { + last = n; + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + return last; +} + + + +struct Q_CORE_EXPORT SparseArrayData +{ + SparseArrayData(); + ~SparseArrayData() { + if (root()) + freeTree(header.left, Q_ALIGNOF(SparseArrayNode)); + } + + int numEntries; + SparseArrayNode header; + SparseArrayNode *mostLeftNode; + uint length; + + void rotateLeft(SparseArrayNode *x); + void rotateRight(SparseArrayNode *x); + void rebalance(SparseArrayNode *x); + void recalcMostLeftNode(); + + SparseArrayNode *createNode(uint sl, int value, SparseArrayNode *parent, bool left); + void freeTree(SparseArrayNode *root, int alignment); + + SparseArrayNode *root() const { return header.left; } + + const SparseArrayNode *end() const { return &header; } + SparseArrayNode *end() { return &header; } + const SparseArrayNode *begin() const { if (root()) return mostLeftNode; return end(); } + SparseArrayNode *begin() { if (root()) return mostLeftNode; return end(); } + + void deleteNode( SparseArrayNode *z); + SparseArrayNode *findNode(uint akey) const; + +}; + +inline SparseArrayNode *SparseArrayData::findNode(uint akey) const +{ + SparseArrayNode *n = root(); + + while (n) { + if (akey == n->size_left) { + return n; + } else if (akey < n->size_left) { + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + + return 0; +} + + +class Array +{ + SparseArrayData *d; + int allocValue() { + int idx = freeList; + if (values.size() <= freeList) + values.resize(++freeList); + else + freeList = values.at(freeList).integerValue(); + + return idx; + } + void freeValue(int idx) { + values[idx] = Value::fromInt32(freeList); + freeList = idx; + } + + QVector values; + int freeList; + +public: + inline Array() : d(new SparseArrayData), freeList(0) {} + Array(const Array &other); + + inline ~Array() { delete d; } + + Array &operator=(const Array &other); + + bool operator==(const Array &other) const; + inline bool operator!=(const Array &other) const { return !(*this == other); } + + inline int numEntries() const { return d->numEntries; } + inline uint length() const { return d->length; } + void setLength(uint l); + + inline bool isEmpty() const { return d->numEntries == 0; } + + void clear(); + + bool remove(uint key); + Value take(uint key); + + bool contains(uint key) const; + Value at(uint key) const; + Value &operator[](uint key); + Value operator[](uint key) const; + + Value pop_front(); + void push_front(Value at); + Value pop_back(); + void push_back(Value at); + + QList keys() const; + + Value valueFromIndex(int idx) const { + if (idx == -1) + return Value::undefinedValue(); + return values.at(idx); + } + Value &valueRefFromIndex(int idx) { + return values[idx]; + } + + class const_iterator; + + class iterator + { + friend class const_iterator; + SparseArrayNode *i; + + public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef qptrdiff difference_type; + typedef int value_type; + typedef int *pointer; + typedef int &reference; + + inline iterator() : i(0) { } + inline iterator( SparseArrayNode *node) : i(node) { } + + inline uint key() const { return i->key(); } + inline int &value() const { return i->value; } + inline int &operator*() const { return i->value; } + inline int *operator->() const { return &i->value; } + inline bool operator==(const iterator &o) const { return i == o.i; } + inline bool operator!=(const iterator &o) const { return i != o.i; } + + inline iterator &operator++() { + i = i->nextNode(); + return *this; + } + inline iterator operator++(int) { + iterator r = *this; + i = i->nextNode(); + return r; + } + inline iterator &operator--() { + i = i->previousNode(); + return *this; + } + inline iterator operator--(int) { + iterator r = *this; + i = i->previousNode(); + return r; + } + inline iterator operator+(int j) const + { iterator r = *this; if (j > 0) while (j--) ++r; else while (j++) --r; return r; } + inline iterator operator-(int j) const { return operator+(-j); } + inline iterator &operator+=(int j) { return *this = *this + j; } + inline iterator &operator-=(int j) { return *this = *this - j; } + +#ifndef QT_STRICT_ITERATORS + public: + inline bool operator==(const const_iterator &o) const + { return i == o.i; } + inline bool operator!=(const const_iterator &o) const + { return i != o.i; } +#endif + friend class Array; + }; + friend class iterator; + + class const_iterator + { + friend class iterator; + const SparseArrayNode *i; + + public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef qptrdiff difference_type; + typedef int value_type; + typedef const int *pointer; + typedef int reference; + + inline const_iterator() : i(0) { } + inline const_iterator(const SparseArrayNode *node) : i(node) { } +#ifdef QT_STRICT_ITERATORS + explicit inline const_iterator(const iterator &o) +#else + inline const_iterator(const iterator &o) +#endif + { i = o.i; } + + inline uint key() const { return i->key(); } + inline int value() const { return i->value; } + inline int operator*() const { return i->value; } + inline const int *operator->() const { return &i->value; } + inline bool operator==(const const_iterator &o) const { return i == o.i; } + inline bool operator!=(const const_iterator &o) const { return i != o.i; } + + inline const_iterator &operator++() { + i = i->nextNode(); + return *this; + } + inline const_iterator operator++(int) { + const_iterator r = *this; + i = i->nextNode(); + return r; + } + inline const_iterator &operator--() { + i = i->previousNode(); + return *this; + } + inline const_iterator operator--(int) { + const_iterator r = *this; + i = i->previousNode(); + return r; + } + inline const_iterator operator+(int j) const + { const_iterator r = *this; if (j > 0) while (j--) ++r; else while (j++) --r; return r; } + inline const_iterator operator-(int j) const { return operator+(-j); } + inline const_iterator &operator+=(int j) { return *this = *this + j; } + inline const_iterator &operator-=(int j) { return *this = *this - j; } + +#ifdef QT_STRICT_ITERATORS + private: + inline bool operator==(const iterator &o) const { return operator==(const_iterator(o)); } + inline bool operator!=(const iterator &o) const { return operator!=(const_iterator(o)); } +#endif + friend class Array; + }; + friend class const_iterator; + + // STL style + inline iterator begin() { return iterator(d->begin()); } + inline const_iterator begin() const { return const_iterator(d->begin()); } + inline const_iterator constBegin() const { return const_iterator(d->begin()); } + inline const_iterator cbegin() const { return const_iterator(d->begin()); } + inline iterator end() { return iterator(d->end()); } + inline const_iterator end() const { return const_iterator(d->end()); } + inline const_iterator constEnd() const { return const_iterator(d->end()); } + inline const_iterator cend() const { return const_iterator(d->end()); } + iterator erase(iterator it); + + // more Qt + typedef iterator Iterator; + typedef const_iterator ConstIterator; + iterator find(uint key); + const_iterator find(uint key) const; + const_iterator constFind(uint key) const; + iterator lowerBound(uint key); + const_iterator lowerBound(uint key) const; + iterator upperBound(uint key); + const_iterator upperBound(uint key) const; + iterator insert(uint akey, Value at); + + // STL compatibility + typedef uint key_type; + typedef int mapped_type; + typedef qptrdiff difference_type; + typedef int size_type; + inline bool empty() const { return isEmpty(); } + +#ifdef Q_MAP_DEBUG + void dump() const; +#endif + + void concat(const Array &other); + void sort(ExecutionContext *context, const Value &comparefn); + void getCollectables(QVector &objects) const; + void splice(double start, double deleteCount, const QVector &, Array &); +}; + + +inline Array::Array(const Array &other) +{ + d = new SparseArrayData; + if (other.d->header.left) { + d->header.left = other.d->header.left->copy(d); + d->header.left->setParent(&d->header); + d->recalcMostLeftNode(); + d->length = other.d->length; + } +} + + +inline Array &Array::operator=(const Array &other) +{ + d = new SparseArrayData; + if (other.d->header.left) { + d->header.left = other.d->header.left->copy(d); + d->header.left->setParent(&d->header); + d->recalcMostLeftNode(); + d->length = other.d->length; + } + return *this; +} + + +inline void Array::clear() +{ + *this = Array(); +} + + + +inline Value Array::at(uint akey) const +{ + SparseArrayNode *n = d->findNode(akey); + int idx = n ? n->value : -1; + return valueFromIndex(idx); +} + + +inline Value Array::operator[](uint akey) const +{ + return at(akey); +} + + +inline Value &Array::operator[](uint akey) +{ + SparseArrayNode *n = d->findNode(akey); + if (!n) + n = insert(akey, Value::undefinedValue()).i; + return valueRefFromIndex(n->value); +} + +inline Value Array::pop_front() +{ + int idx = -1 ; + if (!d->length) + return Value::undefinedValue(); + + SparseArrayNode *n = d->findNode(0); + if (n) { + idx = n->value; + d->deleteNode(n); + // adjust all size_left indices on the path to leftmost item by 1 + SparseArrayNode *n = d->root(); + while (n) { + n->size_left -= 1; + n = n->left; + } + } + --d->length; + Value v = valueFromIndex(idx); + freeValue(idx); + return v; +} + +inline void Array::push_front(Value value) +{ + // adjust all size_left indices on the path to leftmost item by 1 + SparseArrayNode *n = d->root(); + while (n) { + n->size_left += 1; + n = n->left; + } + ++d->length; + insert(0, value); +} + +inline Value Array::pop_back() +{ + int idx = -1; + if (!d->length) + return Value::undefinedValue(); + + --d->length; + SparseArrayNode *n = d->findNode(d->length); + if (n) { + idx = n->value; + d->deleteNode(n); + } + Value v = valueFromIndex(idx); + freeValue(idx); + return v; +} + +inline void Array::push_back(Value value) +{ + insert(d->length, value); +} + + +inline bool Array::contains(uint akey) const +{ + return d->findNode(akey) != 0; +} + + +inline Array::const_iterator Array::constFind(uint akey) const +{ + SparseArrayNode *n = d->findNode(akey); + return const_iterator(n ? n : d->end()); +} + + +inline Array::const_iterator Array::find(uint akey) const +{ + return constFind(akey); +} + + +inline Array::iterator Array::find(uint akey) +{ + SparseArrayNode *n = d->findNode(akey); + return iterator(n ? n : d->end()); +} + +#ifdef Q_MAP_DEBUG + +void SparseArray::dump() const +{ + const_iterator it = begin(); + qDebug() << "map dump:"; + while (it != end()) { + const SparseArrayNode *n = it.i; + int depth = 0; + while (n && n != d->root()) { + ++depth; + n = n->parent(); + } + QByteArray space(4*depth, ' '); + qDebug() << space << (it.i->color() == SparseArrayNode::Red ? "Red " : "Black") << it.i << it.i->left << it.i->right + << it.key() << it.value(); + ++it; + } + qDebug() << "---------"; +} +#endif + + +inline bool Array::remove(uint akey) +{ + SparseArrayNode *node = d->findNode(akey); + if (node) { + d->deleteNode(node); + return true; + } + return false; +} + + +inline Value Array::take(uint akey) +{ + SparseArrayNode *node = d->findNode(akey); + int t; + if (node) { + t = node->value; + d->deleteNode(node); + } else { + t = -1; + } + return valueFromIndex(t); +} + + +inline Array::iterator Array::erase(iterator it) +{ + if (it == iterator(d->end())) + return it; + + SparseArrayNode *n = it.i; + ++it; + d->deleteNode(n); + return it; +} + +inline QList Array::keys() const +{ + QList res; + res.reserve(numEntries()); + const_iterator i = begin(); + while (i != end()) { + res.append(i.key()); + ++i; + } + return res; +} + +inline Array::const_iterator Array::lowerBound(uint akey) const +{ + SparseArrayNode *lb = d->root()->lowerBound(akey); + if (!lb) + lb = d->end(); + return const_iterator(lb); +} + + +inline Array::iterator Array::lowerBound(uint akey) +{ + SparseArrayNode *lb = d->root()->lowerBound(akey); + if (!lb) + lb = d->end(); + return iterator(lb); +} + + +inline Array::const_iterator Array::upperBound(uint akey) const +{ + SparseArrayNode *ub = d->root()->upperBound(akey); + if (!ub) + ub = d->end(); + return const_iterator(ub); +} + + +inline Array::iterator Array::upperBound(uint akey) +{ + SparseArrayNode *ub = d->root()->upperBound(akey); + if (!ub) + ub = d->end(); + return iterator(ub); +} + +inline void Array::concat(const Array &other) +{ + int newLen = d->length + other.d->length; + for (const_iterator it = other.constBegin(); it != other.constEnd(); ++it) { + insert(d->length + it.key(), other.valueFromIndex(it.value())); + } + d->length = newLen; +} + +inline void Array::sort(ExecutionContext *context, const Value &comparefn) +{ + ArrayElementLessThan lessThan(context, comparefn); + // ### + //std::sort(to_vector.begin(), to_vector.end(), lessThan); +} + +inline void Array::getCollectables(QVector &objects) const +{ + for (const_iterator it = constBegin(), eit = constEnd(); it != eit; ++it) { + if (Object *o = valueFromIndex(*it).asObject()) + objects.append(o); + } +} + + +} +} + +#endif // QMAP_H diff --git a/qv4array_p.h b/qv4array_p.h deleted file mode 100644 index 22c29f91b5..0000000000 --- a/qv4array_p.h +++ /dev/null @@ -1,238 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4ARRAY_P_H -#define QV4ARRAY_P_H - -#include "qmljs_runtime.h" -#include -#include -#include - -namespace QQmlJS { -namespace VM { - -class Array -{ -public: - inline Array(); - inline Array(const Array &other); - inline ~Array(); - - inline Array &operator = (const Array &other); - - inline bool isEmpty() const; - inline uint size() const; - inline uint count() const; - inline Value at(uint index) const; - inline void assign(uint index, const Value &v); - inline void clear(); - inline void resize(uint size); - inline void concat(const Array &other); - inline Value pop(); - inline Value takeFirst(); - inline void sort(ExecutionContext *context, const Value &comparefn); - inline void splice(double start, double deleteCount, - const QVector &items, - Array &other); - inline void push(const Value &value); - - void getCollectables(QVector &objects); - -private: - typedef std::deque ToVectorType; - ToVectorType *to_vector; -}; - -class ArrayElementLessThan -{ -public: - inline ArrayElementLessThan(ExecutionContext *context, const Value &comparefn) - : m_context(context), m_comparefn(comparefn) {} - - bool operator()(const Value &v1, const Value &v2) const; - -private: - ExecutionContext *m_context; - Value m_comparefn; -}; - -inline Array::Array() -{ - to_vector = new std::deque(); -} - -inline Array::Array(const Array &other) -{ - to_vector = new std::deque(*other.to_vector); -} - -inline Array::~Array() -{ - delete to_vector; -} - -inline Array &Array::operator = (const Array &other) -{ - *to_vector = *other.to_vector; - return *this; -} - -inline bool Array::isEmpty() const -{ - return to_vector->empty(); -} - -inline uint Array::size() const -{ - return to_vector->size(); -} - -inline uint Array::count() const -{ - return to_vector->size(); -} - -inline Value Array::at(uint index) const -{ - return index < to_vector->size() ? to_vector->at(index) : Value::undefinedValue(); -} - -inline void Array::assign(uint index, const Value &v) -{ - if (index == to_vector->size()) - to_vector->push_back(v); - else { - if (index > to_vector->size()) - resize(index + 1); - - (*to_vector)[index] = v; - } -} - -inline void Array::clear() -{ - to_vector->clear(); -} - -inline void Array::resize(uint s) -{ - to_vector->resize(s, Value::undefinedValue()); -} - -inline void Array::concat(const Array &other) -{ - for (std::deque::iterator it = other.to_vector->begin(); it != other.to_vector->end(); ++it) { - const Value &v = *it; - if (! v.isUndefined()) - to_vector->push_back(v); - } -} - -inline Value Array::pop() -{ - if (isEmpty()) - return Value::undefinedValue(); - - Value v = to_vector->back(); - to_vector->pop_back(); - return v; -} - -inline Value Array::takeFirst() -{ - if (isEmpty()) - return Value::undefinedValue(); - - Value v = to_vector->front(); - to_vector->pop_front(); - return v; -} - -inline void Array::sort(ExecutionContext *context, const Value &comparefn) -{ - ArrayElementLessThan lessThan(context, comparefn); - std::sort(to_vector->begin(), to_vector->end(), lessThan); -} - -inline void Array::push(const Value &value) -{ - to_vector->push_back(value); -} - -inline void Array::getCollectables(QVector &objects) -{ - for (ToVectorType::const_iterator it = to_vector->begin(), eit = to_vector->end(); it != eit; ++it) { - if (Object *o = it->asObject()) - objects.append(o); - } -} - -inline void Array::splice(double start, double deleteCount, - const QVector &items, - Array &other) -{ - const double len = size(); - if (start < 0) - start = qMax(len + start, double(0)); - else if (start > len) - start = len; - deleteCount = qMax(qMin(deleteCount, len - start), double(0)); - - const uint st = uint(start); - const uint dc = uint(deleteCount); - other.resize(dc); - - const uint itemsSize = uint(items.size()); - - for (uint i = 0; i < dc; ++i) - other.assign(i, to_vector->at(st + i)); - if (itemsSize > dc) - to_vector->insert(to_vector->begin() + st, itemsSize - dc, Value::undefinedValue()); - else if (itemsSize < dc) - to_vector->erase(to_vector->begin() + st, to_vector->begin() + (dc - itemsSize)); - for (uint i = 0; i < itemsSize; ++i) - (*to_vector)[st + i] = items.at(i); -} - -} // end of namespace VM -} // end of namespace QQmlJS - -#endif // QV4ARRAY_P_H diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 12ef269303..7596d77ee5 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -41,7 +41,6 @@ #include "qv4ecmaobjects_p.h" -#include "qv4array_p.h" #include "qv4mm.h" #include #include @@ -612,7 +611,7 @@ Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) if (PropertyTable *members = O.objectValue()->members.data()) { for (PropertyTableEntry **it = members->begin(), **end = members->end(); it != end; ++it) { if (PropertyTableEntry *prop = *it) { - a.push(Value::fromString(prop->name)); + a.push_back(Value::fromString(prop->name)); } } } @@ -790,7 +789,7 @@ Value ObjectPrototype::method_keys(ExecutionContext *ctx) PropertyTable::iterator it = o->members->begin(); while (it != o->members->end()) { if ((*it)->descriptor.isEnumerable()) - a->value.push(Value::fromString((*it)->name)); + a->value.push_back(Value::fromString((*it)->name)); ++it; } } @@ -1548,10 +1547,10 @@ Value ArrayCtor::call(ExecutionContext *ctx) return Value::undefinedValue(); } - value.resize(isize); + value.setLength(isize); } else { for (unsigned int i = 0; i < ctx->argumentCount; ++i) { - value.assign(i, ctx->argument(i)); + value.insert(i, ctx->argument(i)); } } @@ -1603,18 +1602,18 @@ Value ArrayPrototype::method_concat(ExecutionContext *ctx) result = instance->value; else { QString v = ctx->thisObject.toString(ctx)->toQString(); - result.assign(0, Value::fromString(ctx, v)); + result.insert(0, Value::fromString(ctx, v)); } for (uint i = 0; i < ctx->argumentCount; ++i) { - quint32 k = result.size(); + quint32 k = result.length(); Value arg = ctx->argument(i); if (ArrayObject *elt = arg.asArrayObject()) result.concat(elt->value); else - result.assign(k, arg); + result.insert(k, arg); } return Value::fromObject(ctx->engine->newArrayObject(result)); @@ -1644,8 +1643,9 @@ Value ArrayPrototype::method_join(ExecutionContext *ctx) QString R; + // ### FIXME if (ArrayObject *a = self.asArrayObject()) { - for (uint i = 0; i < a->value.size(); ++i) { + for (uint i = 0; i < a->value.length(); ++i) { if (i) R += r4; @@ -1680,7 +1680,7 @@ Value ArrayPrototype::method_pop(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) - return instance->value.pop(); + return instance->value.pop_back(); Value r1 = self.property(ctx, ctx->engine->id_length); quint32 r2 = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; @@ -1700,12 +1700,11 @@ Value ArrayPrototype::method_push(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) { - uint pos = instance->value.size(); for (unsigned int i = 0; i < ctx->argumentCount; ++i) { Value val = ctx->argument(i); - instance->value.assign(pos++, val); + instance->value.push_back(val); } - return Value::fromDouble(pos); + return Value::fromDouble(instance->value.length()); } Value r1 = self.property(ctx, ctx->engine->id_length); @@ -1726,12 +1725,12 @@ Value ArrayPrototype::method_reverse(ExecutionContext *ctx) if (!instance) ctx->throwUnimplemented(QStringLiteral("Array.prototype.reverse")); - int lo = 0, hi = instance->value.count() - 1; + int lo = 0, hi = instance->value.length() - 1; for (; lo < hi; ++lo, --hi) { Value tmp = instance->value.at(lo); - instance->value.assign(lo, instance->value.at(hi)); - instance->value.assign(hi, tmp); + instance->value.insert(lo, instance->value.at(hi)); + instance->value.insert(hi, tmp); } return Value::undefinedValue(); } @@ -1742,7 +1741,7 @@ Value ArrayPrototype::method_shift(ExecutionContext *ctx) if (!instance) ctx->throwUnimplemented(QStringLiteral("Array.prototype.shift")); - return instance->value.takeFirst(); + return instance->value.pop_front(); } Value ArrayPrototype::method_slice(ExecutionContext *ctx) @@ -1766,7 +1765,7 @@ Value ArrayPrototype::method_slice(ExecutionContext *ctx) String *r11 = Value::fromDouble(k).toString(ctx); Value v = self.property(ctx, r11); if (! v.isUndefined()) - result.assign(n++, v); + result.insert(n++, v); } return Value::fromObject(ctx->engine->newArrayObject(result)); } @@ -1806,8 +1805,19 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) Value ArrayPrototype::method_unshift(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); - return Value::undefinedValue(); + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) + ctx->throwUnimplemented(QStringLiteral("Array.prototype.shift")); + + for (int i = ctx->argumentCount - 1; i >= 0; --i) { + Value v = ctx->argument(i); + instance->value.push_front(v); + } + + uint l = instance->value.length(); + if (l < INT_MAX) + return Value::fromInt32(l); + return Value::fromDouble((double)l); } Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) @@ -1831,7 +1841,8 @@ Value ArrayPrototype::method_every(ExecutionContext *ctx) Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); bool ok = true; - for (uint k = 0; ok && k < instance->value.size(); ++k) { + // ### + for (uint k = 0; ok && k < instance->value.length(); ++k) { Value v = instance->value.at(k); if (v.isUndefined()) continue; @@ -1855,7 +1866,8 @@ Value ArrayPrototype::method_some(ExecutionContext *ctx) Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); bool ok = false; - for (uint k = 0; !ok && k < instance->value.size(); ++k) { + // ### + for (uint k = 0; !ok && k < instance->value.length(); ++k) { Value v = instance->value.at(k); if (v.isUndefined()) continue; @@ -1878,7 +1890,8 @@ Value ArrayPrototype::method_forEach(ExecutionContext *ctx) Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); - for (quint32 k = 0; k < instance->value.size(); ++k) { + // ### + for (quint32 k = 0; k < instance->value.length(); ++k) { Value v = instance->value.at(k); if (v.isUndefined()) continue; @@ -1900,8 +1913,8 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); - a->value.resize(instance->value.size()); - for (quint32 k = 0; k < instance->value.size(); ++k) { + a->value.setLength(instance->value.length()); + for (quint32 k = 0; k < instance->value.length(); ++k) { Value v = instance->value.at(k); if (v.isUndefined()) continue; @@ -1910,7 +1923,7 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); - a->value.assign(k, r); + a->value.insert(k, r); } return Value::fromObject(a); } @@ -1924,7 +1937,7 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); - for (quint32 k = 0; k < instance->value.size(); ++k) { + for (quint32 k = 0; k < instance->value.length(); ++k) { Value v = instance->value.at(k); if (v.isUndefined()) continue; @@ -1934,9 +1947,9 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) args[2] = ctx->thisObject; Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); if (__qmljs_to_boolean(r, ctx)) { - const uint index = a->value.size(); - a->value.resize(index + 1); - a->value.assign(index, v); + const uint index = a->value.length(); + a->value.setLength(index + 1); + a->value.insert(index, v); } } return Value::fromObject(a); @@ -1951,7 +1964,7 @@ Value ArrayPrototype::method_reduce(ExecutionContext *ctx) Value callback = ctx->argument(0); Value initialValue = ctx->argument(1); Value acc = initialValue; - for (quint32 k = 0; k < instance->value.size(); ++k) { + for (quint32 k = 0; k < instance->value.length(); ++k) { Value v = instance->value.at(k); if (v.isUndefined()) continue; @@ -1981,7 +1994,7 @@ Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) Value callback = ctx->argument(0); Value initialValue = ctx->argument(1); Value acc = initialValue; - for (int k = instance->value.size() - 1; k != -1; --k) { + for (int k = instance->value.length() - 1; k != -1; --k) { Value v = instance->value.at(k); if (v.isUndefined()) continue; @@ -2094,7 +2107,7 @@ Value FunctionPrototype::method_apply(ExecutionContext *ctx) if (ArrayObject *arr = arg.asArrayObject()) { const Array &actuals = arr->value; - for (quint32 i = 0; i < actuals.count(); ++i) { + for (quint32 i = 0; i < actuals.length(); ++i) { Value a = actuals.at(i); args.append(a); } @@ -2814,7 +2827,7 @@ Value RegExpPrototype::method_exec(ExecutionContext *ctx) ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); int captured = match.lastCapturedIndex(); for (int i = 0; i <= captured; ++i) - array->value.push(Value::fromString(ctx, match.captured(i))); + array->value.push_back(Value::fromString(ctx, match.captured(i))); array->__put__(ctx, QLatin1String("index"), Value::fromInt32(match.capturedStart(0))); array->__put__(ctx, QLatin1String("input"), arg); diff --git a/v4.pro b/v4.pro index a4acfc456d..b5e3bd6ddb 100644 --- a/v4.pro +++ b/v4.pro @@ -20,13 +20,13 @@ SOURCES += main.cpp \ qmljs_value.cpp \ qv4syntaxchecker.cpp \ qv4ecmaobjects.cpp \ - qv4array.cpp \ qv4isel_masm.cpp \ llvm_runtime.cpp \ qv4isel_p.cpp \ debugging.cpp \ qv4mm.cpp \ - qv4managed.cpp + qv4managed.cpp \ + qv4array.cpp HEADERS += \ qv4codegen_p.h \ @@ -39,13 +39,13 @@ HEADERS += \ qmljs_value.h \ qv4syntaxchecker_p.h \ qv4ecmaobjects_p.h \ - qv4array_p.h \ qv4isel_masm_p.h \ qv4isel_p.h \ qv4isel_util_p.h \ debugging.h \ qv4mm.h \ - qv4managed.h + qv4managed.h \ + qv4array.h llvm { -- cgit v1.2.3 From caa78909f49eff6337aea06bac4a2c79abf12b9f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 8 Jan 2013 11:06:08 +0100 Subject: Small array cleanups Remove duplicated code from Array copy constructor and protect assignment operator against self-assignment. Change-Id: I0de2cbc54cbfed570a2166c63237f264792dc144 Reviewed-by: Lars Knoll --- qv4array.h | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/qv4array.h b/qv4array.h index 19d63871bc..1dea8fd44e 100644 --- a/qv4array.h +++ b/qv4array.h @@ -422,24 +422,21 @@ public: inline Array::Array(const Array &other) { - d = new SparseArrayData; - if (other.d->header.left) { - d->header.left = other.d->header.left->copy(d); - d->header.left->setParent(&d->header); - d->recalcMostLeftNode(); - d->length = other.d->length; - } + d = 0; + *this = other; } inline Array &Array::operator=(const Array &other) { - d = new SparseArrayData; - if (other.d->header.left) { - d->header.left = other.d->header.left->copy(d); - d->header.left->setParent(&d->header); - d->recalcMostLeftNode(); - d->length = other.d->length; + if (this != &other) { + d = new SparseArrayData; + if (other.d->header.left) { + d->header.left = other.d->header.left->copy(d); + d->header.left->setParent(&d->header); + d->recalcMostLeftNode(); + d->length = other.d->length; + } } return *this; } -- cgit v1.2.3 From 9383dc555dfaa93982eee749f66972fab1f066bf Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 8 Jan 2013 11:19:42 +0100 Subject: Update test expectations after array changes Some tests are now passing, others we can run now (no OOM anymore) but they are failing. A few tests remain that need to be skipped because they still allocate too much memory. Change-Id: Ie944ffd7b9adee9df605e6411e1b52fc032e0019 Reviewed-by: Lars Knoll --- tests/TestExpectations | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index b7bec83b4a..4a90682d24 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -1,25 +1,23 @@ # Skipped because our array implementation isn't sparse and therefore this test causes OOM. -S15.4_A1.1_T10 -S15.4.2.2_A2.1_T1 S15.4.4.10_A1.3_T4 S15.4.4.10_A2.2_T3 S15.4.4.10_A2.2_T4 S15.4.4.10_A3_T1 S15.4.4.10_A3_T2 -15.4.4.14-5-12 -15.4.4.14-5-16 -15.4.4.14-9-9 -15.4.4.15-5-12 -15.4.4.15-5-16 -15.4.4.15-8-9 -S15.4.5.2_A1_T1 -S15.4.5.2_A3_T4 # Skipped because of slow string concatenation, test takes a looong time to execute. S15.1.3.1_A2.5_T1 S15.1.3.2_A2.5_T1 # Tests failing that are supposed to pass. +S15.4.5.2_A3_T4 failing +15.4.4.15-8-9 failing +15.4.4.15-5-16 failing +15.4.4.15-5-12 failing +15.4.4.14-9-9 failing +15.4.4.14-5-16 failing +15.4.4.14-5-12 failing +S15.4.2.2_A2.1_T1 failing S7.9_A5.8_T1 failing S8.8_A2_T1 failing S8.8_A2_T2 failing @@ -1949,8 +1947,6 @@ S15.4.4.12_A5.2 failing S15.4.4.12_A5.3 failing S15.4.4.12_A5.4 failing S15.4.4.12_A5.7 failing -S15.4.4.13_A1_T1 failing -S15.4.4.13_A1_T2 failing S15.4.4.13_A2_T1 failing S15.4.4.13_A2_T2 failing S15.4.4.13_A2_T3 failing @@ -3696,7 +3692,6 @@ S15.4.4.9_A5.7 failing 15.4.5.1-3.d-1 failing 15.4.5.1-3.d-2 failing 15.4.5.1-3.d-3 failing -15.4.5.1-5-1 failing 15.4.5.1-5-2 failing S15.4.5.1_A1.1_T1 failing S15.4.5.1_A1.1_T2 failing @@ -3705,7 +3700,6 @@ S15.4.5.1_A1.2_T2 failing S15.4.5.1_A1.2_T3 failing S15.4.5.1_A1.3_T1 failing S15.4.5.1_A1.3_T2 failing -S15.4.5.1_A2.1_T1 failing S15.4.5.2_A1_T2 failing S15.4.5.2_A3_T1 failing S15.4.5.2_A3_T2 failing -- cgit v1.2.3 From cd70aba3bf9cf03444dafa7138a3e4d928aa1890 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 8 Jan 2013 12:03:01 +0100 Subject: Implemented Array::indexOf and unskipped a bunch of tests that were failing Change-Id: Ic17cb6cb2f89ad18cb6b5313af735f8afda2385e Reviewed-by: Lars Knoll --- qv4ecmaobjects.cpp | 26 +++++++++++++++++++++--- tests/TestExpectations | 55 +------------------------------------------------- 2 files changed, 24 insertions(+), 57 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 7596d77ee5..f112f61da0 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1822,13 +1822,33 @@ Value ArrayPrototype::method_unshift(ExecutionContext *ctx) Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); - return Value::undefinedValue(); + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) + ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); + + Value searchValue; + int fromIndex = 0; + + if (ctx->argumentCount == 1) + searchValue = ctx->argument(0); + else if (ctx->argumentCount == 2) { + searchValue = ctx->argument(0); + fromIndex = ctx->argument(1).toInteger(ctx); + } else + __qmljs_throw_type_error(ctx); + + for (Array::ConstIterator it = instance->value.find(fromIndex), end = instance->value.constEnd(); + it != end; ++it) { + if (__qmljs_strict_equal(instance->value.valueRefFromIndex(*it), searchValue)) + return Value::fromInt32(it.key()); + } + + return Value::fromInt32(-1); } Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); + ctx->throwUnimplemented(QStringLiteral("Array.prototype.lastIndexOf")); return Value::undefinedValue(); } diff --git a/tests/TestExpectations b/tests/TestExpectations index 4a90682d24..3e07964922 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -15,8 +15,6 @@ S15.4.5.2_A3_T4 failing 15.4.4.15-5-16 failing 15.4.4.15-5-12 failing 15.4.4.14-9-9 failing -15.4.4.14-5-16 failing -15.4.4.14-5-12 failing S15.4.2.2_A2.1_T1 failing S7.9_A5.8_T1 failing S8.8_A2_T1 failing @@ -1989,9 +1987,7 @@ S15.4.4.13_A5.7 failing 15.4.4.14-2-17 failing 15.4.4.14-2-18 failing 15.4.4.14-2-19 failing -15.4.4.14-2-2 failing 15.4.4.14-2-3 failing -15.4.4.14-2-4 failing 15.4.4.14-2-5 failing 15.4.4.14-2-6 failing 15.4.4.14-2-7 failing @@ -2024,7 +2020,6 @@ S15.4.4.13_A5.7 failing 15.4.4.14-3-7 failing 15.4.4.14-3-8 failing 15.4.4.14-3-9 failing -15.4.4.14-4-1 failing 15.4.4.14-4-10 failing 15.4.4.14-4-11 failing 15.4.4.14-4-2 failing @@ -2035,50 +2030,21 @@ S15.4.4.13_A5.7 failing 15.4.4.14-4-7 failing 15.4.4.14-4-8 failing 15.4.4.14-4-9 failing -15.4.4.14-5-1 failing -15.4.4.14-5-10 failing 15.4.4.14-5-11 failing 15.4.4.14-5-13 failing 15.4.4.14-5-14 failing 15.4.4.14-5-15 failing 15.4.4.14-5-17 failing -15.4.4.14-5-18 failing -15.4.4.14-5-19 failing 15.4.4.14-5-2 failing -15.4.4.14-5-20 failing -15.4.4.14-5-21 failing -15.4.4.14-5-22 failing -15.4.4.14-5-23 failing -15.4.4.14-5-24 failing -15.4.4.14-5-25 failing 15.4.4.14-5-26 failing 15.4.4.14-5-27 failing 15.4.4.14-5-28 failing 15.4.4.14-5-29 failing -15.4.4.14-5-3 failing 15.4.4.14-5-30 failing -15.4.4.14-5-31 failing 15.4.4.14-5-32 failing -15.4.4.14-5-33 failing -15.4.4.14-5-4 failing -15.4.4.14-5-5 failing -15.4.4.14-5-6 failing -15.4.4.14-5-7 failing -15.4.4.14-5-8 failing -15.4.4.14-5-9 failing -15.4.4.14-6-1 failing -15.4.4.14-7-1 failing -15.4.4.14-7-2 failing -15.4.4.14-7-3 failing -15.4.4.14-7-4 failing -15.4.4.14-7-5 failing 15.4.4.14-8-1 failing 15.4.4.14-8-2 failing -15.4.4.14-8-3 failing -15.4.4.14-8-4 failing -15.4.4.14-9-1 failing 15.4.4.14-9-10 failing -15.4.4.14-9-11 failing 15.4.4.14-9-2 failing 15.4.4.14-9-3 failing 15.4.4.14-9-4 failing @@ -2089,9 +2055,7 @@ S15.4.4.13_A5.7 failing 15.4.4.14-9-a-1 failing 15.4.4.14-9-a-10 failing 15.4.4.14-9-a-11 failing -15.4.4.14-9-a-12 failing 15.4.4.14-9-a-13 failing -15.4.4.14-9-a-14 failing 15.4.4.14-9-a-15 failing 15.4.4.14-9-a-16 failing 15.4.4.14-9-a-17 failing @@ -2101,11 +2065,9 @@ S15.4.4.13_A5.7 failing 15.4.4.14-9-a-3 failing 15.4.4.14-9-a-4 failing 15.4.4.14-9-a-5 failing -15.4.4.14-9-a-6 failing 15.4.4.14-9-a-7 failing 15.4.4.14-9-a-8 failing 15.4.4.14-9-a-9 failing -15.4.4.14-9-b-1 failing 15.4.4.14-9-b-i-1 failing 15.4.4.14-9-b-i-10 failing 15.4.4.14-9-b-i-11 failing @@ -2117,9 +2079,7 @@ S15.4.4.13_A5.7 failing 15.4.4.14-9-b-i-17 failing 15.4.4.14-9-b-i-18 failing 15.4.4.14-9-b-i-19 failing -15.4.4.14-9-b-i-2 failing 15.4.4.14-9-b-i-20 failing -15.4.4.14-9-b-i-21 failing 15.4.4.14-9-b-i-22 failing 15.4.4.14-9-b-i-23 failing 15.4.4.14-9-b-i-25 failing @@ -2127,28 +2087,15 @@ S15.4.4.13_A5.7 failing 15.4.4.14-9-b-i-27 failing 15.4.4.14-9-b-i-28 failing 15.4.4.14-9-b-i-29 failing -15.4.4.14-9-b-i-3 failing 15.4.4.14-9-b-i-30 failing 15.4.4.14-9-b-i-31 failing 15.4.4.14-9-b-i-4 failing -15.4.4.14-9-b-i-5 failing 15.4.4.14-9-b-i-6 failing 15.4.4.14-9-b-i-7 failing 15.4.4.14-9-b-i-8 failing 15.4.4.14-9-b-i-9 failing -15.4.4.14-9-b-ii-1 failing -15.4.4.14-9-b-ii-10 failing -15.4.4.14-9-b-ii-11 failing 15.4.4.14-9-b-ii-2 failing -15.4.4.14-9-b-ii-3 failing -15.4.4.14-9-b-ii-4 failing 15.4.4.14-9-b-ii-5 failing -15.4.4.14-9-b-ii-6 failing -15.4.4.14-9-b-ii-7 failing -15.4.4.14-9-b-ii-8 failing -15.4.4.14-9-b-ii-9 failing -15.4.4.14-9-b-iii-1 failing -15.4.4.14-9-b-iii-2 failing 15.4.4.15-0-2 failing 15.4.4.15-1-1 failing 15.4.4.15-1-10 failing @@ -4523,4 +4470,4 @@ S15.2.4.7_A13 failing 15.3.5.4_2-54gs failing 15.3.5.4_2-5gs failing 15.3.5.4_2-7gs failing -15.3.5.4_2-9gs failing +15.3.5.4_2-9gs failing \ No newline at end of file -- cgit v1.2.3 From dcc38a57aeeab7ba0adf72a520dc7d06f5eaf14c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 8 Jan 2013 11:59:01 +0100 Subject: Add mode to test262.py to edit the test expectations file after unexpected passes Make it the default when running "make check", for convenience. Change-Id: Ia469d67dbcb81f27f243722182da8ab3b8bf88ef Reviewed-by: Lars Knoll --- tests/test262.py | 28 ++++++++++++++++++++++++++-- v4.pro | 2 +- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/tests/test262.py b/tests/test262.py index a348dff808..f936b8509a 100755 --- a/tests/test262.py +++ b/tests/test262.py @@ -73,6 +73,25 @@ class TestExpectations: self.failingTests.append(test) f.close() + def update(self, progress): + unexpectedPasses = [c for c in progress.failed_tests if c.case.IsNegative()] + + f = open(rootDir + "/TestExpectations") + lines = f.read().splitlines() + oldLen = len(lines) + for result in unexpectedPasses: + expectationLine = result.case.name[-1] + " failing" + try: + lines.remove(expectationLine) + except ValueError: + pass + f.close() + if len(lines) != oldLen: + f = open(rootDir + "/TestExpectations", "w") + f.write("\n".join(lines)) + f.close() + + if not os.path.exists(EXCLUDED_FILENAME): print "Cannot generate (JSON) test262 tests without a file," + \ " %s, showing which tests have been disabled!" % EXCLUDED_FILENAME @@ -99,6 +118,8 @@ def BuildOptions(): help="Test only non-strict mode") result.add_option("--parallel", default=False, action="store_true", help="Run tests in parallel") + result.add_option("--update-expectations", default=False, action="store_true", + help="Update test expectations fail when a test passes that was expected to fail") # TODO: Once enough tests are made strict compat, change the default # to "both" result.add_option("--unmarked_default", default="non_strict", @@ -455,7 +476,7 @@ class TestSuite(object): print result.ReportOutcome(False) - def Run(self, command_template, tests, print_summary, full_summary, parallel): + def Run(self, command_template, tests, print_summary, full_summary, parallel, update_expectations): if not "{{path}}" in command_template: command_template += " {{path}}" cases = self.EnumerateTests(tests) @@ -480,6 +501,8 @@ class TestSuite(object): print print "Use --full-summary to see output from failed tests" print + if update_expectations: + self.expectations.update(progress) def Print(self, tests): cases = self.EnumerateTests(tests) @@ -502,7 +525,8 @@ def Main(): test_suite.Run(options.command, args, options.summary or options.full_summary, options.full_summary, - options.parallel) + options.parallel, + options.update_expectations) if __name__ == '__main__': diff --git a/v4.pro b/v4.pro index b5e3bd6ddb..746cce86b9 100644 --- a/v4.pro +++ b/v4.pro @@ -80,7 +80,7 @@ DEFINES += QMLJS_NO_LLVM } checktarget.target = check -checktarget.commands = python tests/test262.py --parallel +checktarget.commands = python tests/test262.py --parallel --update-expectations checktarget.depends = all QMAKE_EXTRA_TARGETS += checktarget -- cgit v1.2.3 From 20d9277915bbfc2d907ca59392f3e7027c89e7d8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 8 Jan 2013 12:01:22 +0100 Subject: Some optimisation to the Array constructor This will need further changes, as copying an Array can be expensive. Change-Id: I32b5083dee6ffaa89687af30e4b3589ee72616da Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index f112f61da0..6247f2979d 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1537,12 +1537,14 @@ ArrayCtor::ArrayCtor(ExecutionContext *scope) Value ArrayCtor::call(ExecutionContext *ctx) { - Array value; + ArrayObject *a = ctx->engine->newArrayObject(); + Array &value = a->value; if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { double size = ctx->argument(0).asDouble(); quint32 isize = Value::toUInt32(size); if (size != double(isize)) { + // ### Should be a RangeError ctx->throwError(QStringLiteral("Invalid array length")); return Value::undefinedValue(); } @@ -1554,7 +1556,7 @@ Value ArrayCtor::call(ExecutionContext *ctx) } } - return Value::fromObject(ctx->engine->newArrayObject(value)); + return Value::fromObject(a); } void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) -- cgit v1.2.3 From 38f6e658c3b0049235b248f1a86a7e8574aeea77 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 8 Jan 2013 12:02:13 +0100 Subject: Some cleanups for Array Remove some unused code, move length into the Array (to prepare for a fast path), and fix a leak in the assignment operator. Change-Id: I823bb2dc4957ddb74d8fed9c819ea2b1c09c5a24 Reviewed-by: Simon Hausmann --- qv4array.cpp | 27 +++------------------------ qv4array.h | 39 ++++++++++++++++++--------------------- 2 files changed, 21 insertions(+), 45 deletions(-) diff --git a/qv4array.cpp b/qv4array.cpp index 9709e329e0..d70853d6fe 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -412,7 +412,6 @@ void SparseArrayData::freeTree(SparseArrayNode *root, int alignment) SparseArrayData::SparseArrayData() : numEntries(0) - , length(0) { header.p = 0; header.left = 0; @@ -422,8 +421,8 @@ SparseArrayData::SparseArrayData() Array::iterator Array::insert(uint akey, Value value) { - if (akey >= d->length) - d->length = akey + 1; + if (akey >= len) + len = akey + 1; SparseArrayNode *n = d->root(); SparseArrayNode *y = d->end(); @@ -450,25 +449,6 @@ Array::iterator Array::insert(uint akey, Value value) return iterator(z); } -bool Array::operator==(const Array &other) const -{ - if (numEntries() != other.numEntries()) - return false; - if (d == other.d) - return true; - - const_iterator it1 = begin(); - const_iterator it2 = other.begin(); - - while (it1 != end()) { - if (it1.value() != it2.value() || it1.key() != it2.key()) - return false; - ++it2; - ++it1; - } - return true; -} - void Array::setLength(uint l) { if (l == 0) { @@ -479,14 +459,13 @@ void Array::setLength(uint l) iterator it = lowerBound(l); while (it != end()) it = erase(it); - d->length = l; + len = l; } void Array::splice(double start, double deleteCount, const QVector &/*items*/, Array &/*other*/) { - const double len = length(); if (start < 0) start = qMax(len + start, double(0)); else if (start > len) diff --git a/qv4array.h b/qv4array.h index 1dea8fd44e..68692178a1 100644 --- a/qv4array.h +++ b/qv4array.h @@ -155,7 +155,6 @@ struct Q_CORE_EXPORT SparseArrayData int numEntries; SparseArrayNode header; SparseArrayNode *mostLeftNode; - uint length; void rotateLeft(SparseArrayNode *x); void rotateRight(SparseArrayNode *x); @@ -199,6 +198,8 @@ inline SparseArrayNode *SparseArrayData::findNode(uint akey) const class Array { SparseArrayData *d; + uint len; + int allocValue() { int idx = freeList; if (values.size() <= freeList) @@ -217,21 +218,16 @@ class Array int freeList; public: - inline Array() : d(new SparseArrayData), freeList(0) {} - Array(const Array &other); - + inline Array() : d(new SparseArrayData), len(0), freeList(0) {} inline ~Array() { delete d; } + Array(const Array &other); Array &operator=(const Array &other); - bool operator==(const Array &other) const; - inline bool operator!=(const Array &other) const { return !(*this == other); } - inline int numEntries() const { return d->numEntries; } - inline uint length() const { return d->length; } + inline uint length() const { return len; } void setLength(uint l); - inline bool isEmpty() const { return d->numEntries == 0; } void clear(); @@ -407,7 +403,6 @@ public: typedef int mapped_type; typedef qptrdiff difference_type; typedef int size_type; - inline bool empty() const { return isEmpty(); } #ifdef Q_MAP_DEBUG void dump() const; @@ -430,12 +425,14 @@ inline Array::Array(const Array &other) inline Array &Array::operator=(const Array &other) { if (this != &other) { + if (d) + delete d; d = new SparseArrayData; if (other.d->header.left) { d->header.left = other.d->header.left->copy(d); d->header.left->setParent(&d->header); d->recalcMostLeftNode(); - d->length = other.d->length; + len = other.len; } } return *this; @@ -474,7 +471,7 @@ inline Value &Array::operator[](uint akey) inline Value Array::pop_front() { int idx = -1 ; - if (!d->length) + if (!len) return Value::undefinedValue(); SparseArrayNode *n = d->findNode(0); @@ -488,7 +485,7 @@ inline Value Array::pop_front() n = n->left; } } - --d->length; + --len; Value v = valueFromIndex(idx); freeValue(idx); return v; @@ -502,18 +499,18 @@ inline void Array::push_front(Value value) n->size_left += 1; n = n->left; } - ++d->length; + ++len; insert(0, value); } inline Value Array::pop_back() { int idx = -1; - if (!d->length) + if (!len) return Value::undefinedValue(); - --d->length; - SparseArrayNode *n = d->findNode(d->length); + --len; + SparseArrayNode *n = d->findNode(len); if (n) { idx = n->value; d->deleteNode(n); @@ -525,7 +522,7 @@ inline Value Array::pop_back() inline void Array::push_back(Value value) { - insert(d->length, value); + insert(len, value); } @@ -662,11 +659,11 @@ inline Array::iterator Array::upperBound(uint akey) inline void Array::concat(const Array &other) { - int newLen = d->length + other.d->length; + int newLen = len + other.len; for (const_iterator it = other.constBegin(); it != other.constEnd(); ++it) { - insert(d->length + it.key(), other.valueFromIndex(it.value())); + insert(len + it.key(), other.valueFromIndex(it.value())); } - d->length = newLen; + len = newLen; } inline void Array::sort(ExecutionContext *context, const Value &comparefn) -- cgit v1.2.3 From 0282856c63bba88a8eeff692940bb76c80f4bca4 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 8 Jan 2013 12:35:39 +0100 Subject: Move our String class into a separate file. Change-Id: Ibcaa9e9e2675d3c628383c99282b281f9b6880b2 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 17 ----------- qv4string.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++ qv4string.h | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ v4.pro | 6 ++-- 4 files changed, 154 insertions(+), 19 deletions(-) create mode 100644 qv4string.cpp create mode 100644 qv4string.h diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 22c1eabf58..1c8d450b41 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -61,23 +61,6 @@ using namespace QQmlJS::VM; -uint String::asArrayIndex() const -{ - uint u = 0; - const QChar *ch = _text.constData(); - const QChar *end = ch + _text.length(); - while (ch < end) { - if (ch->unicode() < '0' && ch->unicode() > '9') - return InvalidArrayIndex; - uint n = u*10 + ch->unicode() - '0'; - if (n < u) - // overflow - return InvalidArrayIndex; - u = n; - } - return u; -} - // // Object // diff --git a/qv4string.cpp b/qv4string.cpp new file mode 100644 index 0000000000..8e9d02b231 --- /dev/null +++ b/qv4string.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4string.h" + +namespace QQmlJS { +namespace VM { + +uint String::asArrayIndex() const +{ + uint u = 0; + const QChar *ch = _text.constData(); + const QChar *end = ch + _text.length(); + while (ch < end) { + if (ch->unicode() < '0' && ch->unicode() > '9') + return InvalidArrayIndex; + uint n = u*10 + ch->unicode() - '0'; + if (n < u) + // overflow + return InvalidArrayIndex; + u = n; + } + return u; +} + +} +} diff --git a/qv4string.h b/qv4string.h new file mode 100644 index 0000000000..4f66ee5e83 --- /dev/null +++ b/qv4string.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STRING_H +#define QV4STRING_H + +#include +#include + +namespace QQmlJS { +namespace VM { + +struct String { + inline bool isEqualTo(const String *other) const { + if (this == other) + return true; + else if (other && hashValue() == other->hashValue()) + return toQString() == other->toQString(); + return false; + } + + inline const QString &toQString() const { + return _text; + } + + inline unsigned hashValue() const { + if (! _hashValue) + _hashValue = qHash(_text); + + return _hashValue; + } + enum { InvalidArrayIndex = 0xffffffff }; + uint asArrayIndex() const; + +private: + friend class StringPool; + String(const QString &text) + : _text(text), _hashValue(0) {} + +private: + QString _text; + mutable unsigned _hashValue; +}; + +} +} + +#endif diff --git a/v4.pro b/v4.pro index 746cce86b9..a3709091a4 100644 --- a/v4.pro +++ b/v4.pro @@ -26,7 +26,8 @@ SOURCES += main.cpp \ debugging.cpp \ qv4mm.cpp \ qv4managed.cpp \ - qv4array.cpp + qv4array.cpp \ + qv4string.cpp HEADERS += \ qv4codegen_p.h \ @@ -45,7 +46,8 @@ HEADERS += \ debugging.h \ qv4mm.h \ qv4managed.h \ - qv4array.h + qv4array.h \ + qv4string.h llvm { -- cgit v1.2.3 From 5ca05c9ee4573a3ee4f6a1f4916cb46a8600ed02 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 8 Jan 2013 12:39:09 +0100 Subject: Move the PropertyDescriptor into it's own file Change-Id: I27f47826a0a3fda85c6d87dbf49c9f86b7d5a0b0 Reviewed-by: Simon Hausmann --- qmljs_objects.h | 139 +---------------------------------------- qv4propertydescriptor.h | 160 ++++++++++++++++++++++++++++++++++++++++++++++++ v4.pro | 3 +- 3 files changed, 164 insertions(+), 138 deletions(-) create mode 100644 qv4propertydescriptor.h diff --git a/qmljs_objects.h b/qmljs_objects.h index d8fc2bf5b9..e1427b9e00 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -45,9 +45,11 @@ #include "qmljs_engine.h" #include "qmljs_environment.h" #include "qv4array.h" +#include "qv4string.h" #include "qv4codegen_p.h" #include "qv4isel_p.h" #include "qv4managed.h" +#include "qv4propertydescriptor.h" #include #include @@ -92,143 +94,6 @@ struct SyntaxErrorPrototype; struct TypeErrorPrototype; struct URIErrorPrototype; -struct String { - inline bool isEqualTo(const String *other) const { - if (this == other) - return true; - else if (other && hashValue() == other->hashValue()) - return toQString() == other->toQString(); - return false; - } - - inline const QString &toQString() const { - return _text; - } - - inline unsigned hashValue() const { - if (! _hashValue) - _hashValue = qHash(_text); - - return _hashValue; - } - enum { InvalidArrayIndex = 0xffffffff }; - uint asArrayIndex() const; - -private: - friend class StringPool; - String(const QString &text) - : _text(text), _hashValue(0) {} - -private: - QString _text; - mutable unsigned _hashValue; -}; - -struct PropertyDescriptor { - enum Type { - Generic, - Data, - Accessor - }; - enum State { - Undefined, - Disabled, - Enabled - }; - union { - Value value; - struct { - FunctionObject *get; - FunctionObject *set; - }; - }; - uint type : 8; - uint writable : 8; - uint enumberable : 8; - uint configurable : 8; - - static inline PropertyDescriptor fromValue(Value v) { - PropertyDescriptor pd; - pd.value = v; - pd.type = Data; - pd.writable = Undefined; - pd.enumberable = Undefined; - pd.configurable = Undefined; - return pd; - } - static inline PropertyDescriptor fromAccessor(FunctionObject *getter, FunctionObject *setter) { - PropertyDescriptor pd; - pd.get = getter; - pd.set = setter; - pd.type = Accessor; - pd.writable = Undefined; - pd.enumberable = Undefined; - pd.configurable = Undefined; - return pd; - } - - // Section 8.10 - inline void fullyPopulated() { - if (type == Generic) { - type = Data; - value = Value::undefinedValue(); - } - if (type == Data) { - if (writable == Undefined) - writable = Disabled; - } else { - writable = Undefined; - } - if (enumberable == Undefined) - enumberable = Disabled; - if (configurable == Undefined) - configurable = Disabled; - } - - inline bool isData() const { return type == Data; } - inline bool isAccessor() const { return type == Accessor; } - inline bool isGeneric() const { return type == Generic; } - - inline bool isWritable() const { return writable == Enabled; } - inline bool isEnumerable() const { return enumberable == Enabled; } - inline bool isConfigurable() const { return configurable == Enabled; } - - inline bool isEmpty() { - return type == Generic && writable == Undefined && enumberable == Undefined && configurable == Undefined; - } - inline bool isSubset(PropertyDescriptor *other) { - if (type != other->type) - return false; - if (enumberable != Undefined && enumberable != other->enumberable) - return false; - if (configurable != Undefined && configurable != other->configurable) - return false; - if (writable != Undefined && writable != other->writable) - return false; - if (type == Data && !value.sameValue(other->value)) - return false; - if (type == Accessor && (get != other->get || set != other->set)) - return false; - return true; - } - inline void operator+=(const PropertyDescriptor &other) { - type = other.type; - if (other.enumberable != Undefined) - enumberable = other.enumberable; - if (other.configurable != Undefined) - configurable = other.configurable; - if (other.writable != Undefined) - writable = other.writable; - if (type == Accessor) { - if (other.get) - get = other.get; - if (other.set) - set = other.set; - } else { - value = other.value; - } - } -}; struct PropertyTableEntry { PropertyDescriptor descriptor; diff --git a/qv4propertydescriptor.h b/qv4propertydescriptor.h new file mode 100644 index 0000000000..2361e16db9 --- /dev/null +++ b/qv4propertydescriptor.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4PROPERTYDESCRIPTOR_H +#define QV4PROPERTYDESCRIPTOR_H + +#include "qmljs_value.h" + +namespace QQmlJS { +namespace VM { + +struct FunctionObject; + +struct PropertyDescriptor { + enum Type { + Generic, + Data, + Accessor + }; + enum State { + Undefined, + Disabled, + Enabled + }; + union { + Value value; + struct { + FunctionObject *get; + FunctionObject *set; + }; + }; + uint type : 8; + uint writable : 8; + uint enumberable : 8; + uint configurable : 8; + + static inline PropertyDescriptor fromValue(Value v) { + PropertyDescriptor pd; + pd.value = v; + pd.type = Data; + pd.writable = Undefined; + pd.enumberable = Undefined; + pd.configurable = Undefined; + return pd; + } + static inline PropertyDescriptor fromAccessor(FunctionObject *getter, FunctionObject *setter) { + PropertyDescriptor pd; + pd.get = getter; + pd.set = setter; + pd.type = Accessor; + pd.writable = Undefined; + pd.enumberable = Undefined; + pd.configurable = Undefined; + return pd; + } + + // Section 8.10 + inline void fullyPopulated() { + if (type == Generic) { + type = Data; + value = Value::undefinedValue(); + } + if (type == Data) { + if (writable == Undefined) + writable = Disabled; + } else { + writable = Undefined; + } + if (enumberable == Undefined) + enumberable = Disabled; + if (configurable == Undefined) + configurable = Disabled; + } + + inline bool isData() const { return type == Data; } + inline bool isAccessor() const { return type == Accessor; } + inline bool isGeneric() const { return type == Generic; } + + inline bool isWritable() const { return writable == Enabled; } + inline bool isEnumerable() const { return enumberable == Enabled; } + inline bool isConfigurable() const { return configurable == Enabled; } + + inline bool isEmpty() { + return type == Generic && writable == Undefined && enumberable == Undefined && configurable == Undefined; + } + inline bool isSubset(PropertyDescriptor *other) { + if (type != other->type) + return false; + if (enumberable != Undefined && enumberable != other->enumberable) + return false; + if (configurable != Undefined && configurable != other->configurable) + return false; + if (writable != Undefined && writable != other->writable) + return false; + if (type == Data && !value.sameValue(other->value)) + return false; + if (type == Accessor && (get != other->get || set != other->set)) + return false; + return true; + } + inline void operator+=(const PropertyDescriptor &other) { + type = other.type; + if (other.enumberable != Undefined) + enumberable = other.enumberable; + if (other.configurable != Undefined) + configurable = other.configurable; + if (other.writable != Undefined) + writable = other.writable; + if (type == Accessor) { + if (other.get) + get = other.get; + if (other.set) + set = other.set; + } else { + value = other.value; + } + } +}; + +} +} + +#endif diff --git a/v4.pro b/v4.pro index a3709091a4..277d021832 100644 --- a/v4.pro +++ b/v4.pro @@ -47,7 +47,8 @@ HEADERS += \ qv4mm.h \ qv4managed.h \ qv4array.h \ - qv4string.h + qv4string.h \ + qv4propertydescriptor.h llvm { -- cgit v1.2.3 From 04075a667d6c61ab8a4eca7fd2b0cf0b55bf59d9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 8 Jan 2013 12:42:40 +0100 Subject: Move the PropertyTable into it's own file Change-Id: I310f53122ae17275f345c9c57ad70b8d5f48c307 Reviewed-by: Simon Hausmann --- qmljs_objects.h | 168 +--------------------------------------- qv4propertytable.h | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++++ v4.pro | 3 +- 3 files changed, 223 insertions(+), 168 deletions(-) create mode 100644 qv4propertytable.h diff --git a/qmljs_objects.h b/qmljs_objects.h index e1427b9e00..73a9bef6f6 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -50,6 +50,7 @@ #include "qv4isel_p.h" #include "qv4managed.h" #include "qv4propertydescriptor.h" +#include "qv4propertytable.h" #include #include @@ -95,173 +96,6 @@ struct TypeErrorPrototype; struct URIErrorPrototype; -struct PropertyTableEntry { - PropertyDescriptor descriptor; - String *name; - PropertyTableEntry *next; - int index; - - inline PropertyTableEntry(String *name) - : name(name), - next(0), - index(-1) - { } - - inline bool hasName(String *n) const { return name->isEqualTo(n); } - inline unsigned hashValue() const { return name->hashValue(); } -}; - -class PropertyTable -{ - Q_DISABLE_COPY(PropertyTable) - -public: - PropertyTable() - : _properties(0) - , _buckets(0) - , _freeList(0) - , _propertyCount(-1) - , _bucketCount(0) - , _primeIdx(-1) - , _allocated(0) - {} - - ~PropertyTable() - { - qDeleteAll(_properties, _properties + _propertyCount + 1); - delete[] _properties; - delete[] _buckets; - } - - inline bool isEmpty() const { return _propertyCount == -1; } - - typedef PropertyTableEntry **iterator; - inline iterator begin() const { return _properties; } - inline iterator end() const { return _properties + (_propertyCount + 1); } - - void remove(PropertyTableEntry *prop) - { - PropertyTableEntry **bucket = _buckets + (prop->hashValue() % _bucketCount); - if (*bucket == prop) { - *bucket = prop->next; - } else { - for (PropertyTableEntry *it = *bucket; it; it = it->next) { - if (it->next == prop) { - it->next = it->next->next; - break; - } - } - } - - _properties[prop->index] = 0; - prop->next = _freeList; - _freeList = prop; - } - - PropertyTableEntry *findEntry(String *name) const - { - if (_properties) { - for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { - if (prop && (prop->name == name || prop->hasName(name))) - return prop; - } - } - - return 0; - } - - PropertyDescriptor *find(String *name) const - { - if (_properties) { - for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { - if (prop && (prop->name == name || prop->hasName(name))) - return &prop->descriptor; - } - } - - return 0; - } - - PropertyDescriptor *insert(String *name) - { - if (PropertyTableEntry *prop = findEntry(name)) - return &prop->descriptor; - - if (++_propertyCount == _allocated) { - if (! _allocated) - _allocated = 4; - else - _allocated *= 2; - - PropertyTableEntry **properties = new PropertyTableEntry*[_allocated]; - std::copy(_properties, _properties + _propertyCount, properties); - delete[] _properties; - _properties = properties; - } - - PropertyTableEntry *prop; - if (_freeList) { - prop = _freeList; - _freeList = _freeList->next; - } else { - prop = new PropertyTableEntry(name); - } - - prop->index = _propertyCount; - _properties[_propertyCount] = prop; - - if (! _buckets || 3 * _propertyCount >= 2 * _bucketCount) { - rehash(); - } else { - PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; - prop->next = bucket; - bucket = prop; - } - - return &prop->descriptor; - } - -private: - void rehash() - { - _bucketCount = nextPrime(); - - delete[] _buckets; - _buckets = new PropertyTableEntry *[_bucketCount]; - std::fill(_buckets, _buckets + _bucketCount, (PropertyTableEntry *) 0); - - for (int i = 0; i <= _propertyCount; ++i) { - PropertyTableEntry *prop = _properties[i]; - PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; - prop->next = bucket; - bucket = prop; - } - } - - inline int nextPrime() - { - // IMPORTANT: do not add more primes without checking if _primeIdx needs more bits! - static const int primes[] = { - 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853, 25717, 51437, 102877 - }; - - if (_primeIdx < (int) (sizeof(primes)/sizeof(int))) - return primes[++_primeIdx]; - else - return _bucketCount * 2 + 1; // Yes, we're degrading here. But who needs more than about 68000 properties? - } - -private: - friend struct ForEachIteratorObject; - PropertyTableEntry **_properties; - PropertyTableEntry **_buckets; - PropertyTableEntry *_freeList; - int _propertyCount; - int _bucketCount; - int _primeIdx: 5; - int _allocated: 27; -}; - struct Object: Managed { Object *prototype; QScopedPointer members; diff --git a/qv4propertytable.h b/qv4propertytable.h new file mode 100644 index 0000000000..e194f52bfb --- /dev/null +++ b/qv4propertytable.h @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4PROPERTYTABLE_H +#define QV4PROPERTYTABLE_H + +#include "qmljs_value.h" +#include "qv4propertydescriptor.h" + +namespace QQmlJS { +namespace VM { + +struct PropertyTableEntry { + PropertyDescriptor descriptor; + String *name; + PropertyTableEntry *next; + int index; + + inline PropertyTableEntry(String *name) + : name(name), + next(0), + index(-1) + { } + + inline bool hasName(String *n) const { return name->isEqualTo(n); } + inline unsigned hashValue() const { return name->hashValue(); } +}; + +class PropertyTable +{ + Q_DISABLE_COPY(PropertyTable) + +public: + PropertyTable() + : _properties(0) + , _buckets(0) + , _freeList(0) + , _propertyCount(-1) + , _bucketCount(0) + , _primeIdx(-1) + , _allocated(0) + {} + + ~PropertyTable() + { + qDeleteAll(_properties, _properties + _propertyCount + 1); + delete[] _properties; + delete[] _buckets; + } + + inline bool isEmpty() const { return _propertyCount == -1; } + + typedef PropertyTableEntry **iterator; + inline iterator begin() const { return _properties; } + inline iterator end() const { return _properties + (_propertyCount + 1); } + + void remove(PropertyTableEntry *prop) + { + PropertyTableEntry **bucket = _buckets + (prop->hashValue() % _bucketCount); + if (*bucket == prop) { + *bucket = prop->next; + } else { + for (PropertyTableEntry *it = *bucket; it; it = it->next) { + if (it->next == prop) { + it->next = it->next->next; + break; + } + } + } + + _properties[prop->index] = 0; + prop->next = _freeList; + _freeList = prop; + } + + PropertyTableEntry *findEntry(String *name) const + { + if (_properties) { + for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { + if (prop && (prop->name == name || prop->hasName(name))) + return prop; + } + } + + return 0; + } + + PropertyDescriptor *find(String *name) const + { + if (_properties) { + for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { + if (prop && (prop->name == name || prop->hasName(name))) + return &prop->descriptor; + } + } + + return 0; + } + + PropertyDescriptor *insert(String *name) + { + if (PropertyTableEntry *prop = findEntry(name)) + return &prop->descriptor; + + if (++_propertyCount == _allocated) { + if (! _allocated) + _allocated = 4; + else + _allocated *= 2; + + PropertyTableEntry **properties = new PropertyTableEntry*[_allocated]; + std::copy(_properties, _properties + _propertyCount, properties); + delete[] _properties; + _properties = properties; + } + + PropertyTableEntry *prop; + if (_freeList) { + prop = _freeList; + _freeList = _freeList->next; + } else { + prop = new PropertyTableEntry(name); + } + + prop->index = _propertyCount; + _properties[_propertyCount] = prop; + + if (! _buckets || 3 * _propertyCount >= 2 * _bucketCount) { + rehash(); + } else { + PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; + prop->next = bucket; + bucket = prop; + } + + return &prop->descriptor; + } + +private: + void rehash() + { + _bucketCount = nextPrime(); + + delete[] _buckets; + _buckets = new PropertyTableEntry *[_bucketCount]; + std::fill(_buckets, _buckets + _bucketCount, (PropertyTableEntry *) 0); + + for (int i = 0; i <= _propertyCount; ++i) { + PropertyTableEntry *prop = _properties[i]; + PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; + prop->next = bucket; + bucket = prop; + } + } + + inline int nextPrime() + { + // IMPORTANT: do not add more primes without checking if _primeIdx needs more bits! + static const int primes[] = { + 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853, 25717, 51437, 102877 + }; + + if (_primeIdx < (int) (sizeof(primes)/sizeof(int))) + return primes[++_primeIdx]; + else + return _bucketCount * 2 + 1; // Yes, we're degrading here. But who needs more than about 68000 properties? + } + +private: + friend struct ForEachIteratorObject; + PropertyTableEntry **_properties; + PropertyTableEntry **_buckets; + PropertyTableEntry *_freeList; + int _propertyCount; + int _bucketCount; + int _primeIdx: 5; + int _allocated: 27; +}; + +} +} + +#endif diff --git a/v4.pro b/v4.pro index 277d021832..80687b4ca5 100644 --- a/v4.pro +++ b/v4.pro @@ -48,7 +48,8 @@ HEADERS += \ qv4managed.h \ qv4array.h \ qv4string.h \ - qv4propertydescriptor.h + qv4propertydescriptor.h \ + qv4propertytable.h llvm { -- cgit v1.2.3 From bf0c1a226af0325585cf3a27e63d47eae4d2b676 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 8 Jan 2013 14:16:13 +0100 Subject: Updated failing test case list Change-Id: Ic0d1ca2922c1aa0a3e791d2127ad38dab4edad95 Reviewed-by: Simon Hausmann --- tests/TestExpectations | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index 3e07964922..33eae376e4 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -15,7 +15,6 @@ S15.4.5.2_A3_T4 failing 15.4.4.15-5-16 failing 15.4.4.15-5-12 failing 15.4.4.14-9-9 failing -S15.4.2.2_A2.1_T1 failing S7.9_A5.8_T1 failing S8.8_A2_T1 failing S8.8_A2_T2 failing @@ -2035,7 +2034,6 @@ S15.4.4.13_A5.7 failing 15.4.4.14-5-14 failing 15.4.4.14-5-15 failing 15.4.4.14-5-17 failing -15.4.4.14-5-2 failing 15.4.4.14-5-26 failing 15.4.4.14-5-27 failing 15.4.4.14-5-28 failing @@ -2044,14 +2042,6 @@ S15.4.4.13_A5.7 failing 15.4.4.14-5-32 failing 15.4.4.14-8-1 failing 15.4.4.14-8-2 failing -15.4.4.14-9-10 failing -15.4.4.14-9-2 failing -15.4.4.14-9-3 failing -15.4.4.14-9-4 failing -15.4.4.14-9-5 failing -15.4.4.14-9-6 failing -15.4.4.14-9-7 failing -15.4.4.14-9-8 failing 15.4.4.14-9-a-1 failing 15.4.4.14-9-a-10 failing 15.4.4.14-9-a-11 failing @@ -3944,7 +3934,6 @@ S15.5.4.7_A11 failing S15.5.4.7_A1_T1 failing S15.5.4.7_A1_T10 failing S15.5.4.7_A1_T11 failing -S15.5.4.7_A1_T12 failing S15.5.4.7_A1_T2 failing 15.5.4.20-4-10 failing 15.5.4.20-4-11 failing -- cgit v1.2.3 From aa5842f21490d072a4f17f029294f04ab720fad9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 8 Jan 2013 14:16:28 +0100 Subject: Fix potential crashes The ProeprtyTable iterator can point to a 0 table entry. These need to be skipped when iterating over the table. Change-Id: Idea4d5d42488428d2930be0235538ae349f27142 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 2 +- qv4ecmaobjects.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 1c8d450b41..a3cfd15ddf 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -145,7 +145,7 @@ void Object::getCollectables(QVector &objects) if (members) { for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) { - if ((*it)->descriptor.isData()) + if ((*it) && (*it)->descriptor.isData()) if (Object *o = (*it)->descriptor.value.asObject()) objects.append(o); } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 6247f2979d..29a084ea29 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -667,7 +667,7 @@ Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) if (o->members) { PropertyTable::iterator it = o->members->begin(); while (it != o->members->end()) { - if ((*it)->descriptor.isEnumerable()) { + if ((*it) && (*it)->descriptor.isEnumerable()) { String *name = (*it)->name; PropertyDescriptor pd; toPropertyDescriptor(ctx, o->__get__(ctx, name), &pd); -- cgit v1.2.3 From 743249b409d5fb1bfd2098a93e3fa42c35e892aa Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 8 Jan 2013 14:25:36 +0100 Subject: Move the Array data up into Object Objects can just as well have indexed access as Arrays. They actually behave identical except for the fact that Array has an automatically updated length property. Like this we can unify the handling of named and indexed properties and make sure the methods of the array prototype work correctly and fast also for objects (as documented). Change-Id: I3ffb9c39faafb8857a6a676ab9f3fcceda091a99 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 8 +++--- qmljs_objects.h | 6 ++-- qmljs_runtime.cpp | 8 +++--- qv4ecmaobjects.cpp | 84 +++++++++++++++++++++++++++--------------------------- 4 files changed, 54 insertions(+), 52 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index a3cfd15ddf..a66c15a492 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -423,7 +423,7 @@ Value ArrayObject::__get__(ExecutionContext *ctx, String *name, bool *hasPropert if (name->isEqualTo(ctx->engine->id_length)) { if (hasProperty) *hasProperty = true; - return Value::fromDouble(value.length()); + return Value::fromDouble(array.length()); } return Object::__get__(ctx, name, hasProperty); } @@ -432,9 +432,9 @@ bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContex { if (index.isNumber()) { const quint32 idx = index.toUInt32(ctx); - Value v = value.at(idx); + Value v = array.at(idx); v = op(v, rhs, ctx); - value.insert(idx, v); + array.insert(idx, v); return true; } return Object::inplaceBinOp(rhs, index, op, ctx); @@ -443,7 +443,7 @@ bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContex void ArrayObject::getCollectables(QVector &objects) { Object::getCollectables(objects); - value.getCollectables(objects); + array.getCollectables(objects); } Function::~Function() diff --git a/qmljs_objects.h b/qmljs_objects.h index 73a9bef6f6..e489fa1933 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -99,9 +99,12 @@ struct URIErrorPrototype; struct Object: Managed { Object *prototype; QScopedPointer members; + Array array; Object() : prototype(0) {} + Object(const Array &a) + : prototype(0), array(a) {} virtual ~Object(); @@ -190,9 +193,8 @@ struct DateObject: Object { }; struct ArrayObject: Object { - Array value; ArrayObject() {} - ArrayObject(const Array &value): value(value) {} + ArrayObject(const Array &value): Object(value) {} virtual QString className() { return QStringLiteral("Array"); } virtual ArrayObject *asArrayObject() { return this; } virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 2901960932..e3c9d9cc73 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -176,9 +176,9 @@ Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index) else if (index.isDouble()) n = index.doubleValue(); if (n >= 0) { - if (n < (int) a->value.length()) { + if (n < (int) a->array.length()) { // ### check writable/deletable property - a->value.remove(n); + a->array.remove(n); return Value::fromBoolean(true); } } @@ -571,7 +571,7 @@ Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) } if (ArrayObject *a = object.asArrayObject()) - return a->value.at(index.toUInt32(ctx)); + return a->array.at(index.toUInt32(ctx)); } String *name = index.toString(ctx); @@ -586,7 +586,7 @@ void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value { if (index.isNumber()) { if (ArrayObject *a = object.asArrayObject()) { - a->value.insert(index.toUInt32(ctx), value); + a->array.insert(index.toUInt32(ctx), value); return; } } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 29a084ea29..14843ad058 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -607,7 +607,7 @@ Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) ctx->throwTypeError(); ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); - Array &a = array->value; + Array &a = array->array; if (PropertyTable *members = O.objectValue()->members.data()) { for (PropertyTableEntry **it = members->begin(), **end = members->end(); it != end; ++it) { if (PropertyTableEntry *prop = *it) { @@ -789,7 +789,7 @@ Value ObjectPrototype::method_keys(ExecutionContext *ctx) PropertyTable::iterator it = o->members->begin(); while (it != o->members->end()) { if ((*it)->descriptor.isEnumerable()) - a->value.push_back(Value::fromString((*it)->name)); + a->array.push_back(Value::fromString((*it)->name)); ++it; } } @@ -1538,7 +1538,7 @@ ArrayCtor::ArrayCtor(ExecutionContext *scope) Value ArrayCtor::call(ExecutionContext *ctx) { ArrayObject *a = ctx->engine->newArrayObject(); - Array &value = a->value; + Array &value = a->array; if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { double size = ctx->argument(0).asDouble(); quint32 isize = Value::toUInt32(size); @@ -1601,7 +1601,7 @@ Value ArrayPrototype::method_concat(ExecutionContext *ctx) Array result; if (ArrayObject *instance = ctx->thisObject.asArrayObject()) - result = instance->value; + result = instance->array; else { QString v = ctx->thisObject.toString(ctx)->toQString(); result.insert(0, Value::fromString(ctx, v)); @@ -1612,7 +1612,7 @@ Value ArrayPrototype::method_concat(ExecutionContext *ctx) Value arg = ctx->argument(i); if (ArrayObject *elt = arg.asArrayObject()) - result.concat(elt->value); + result.concat(elt->array); else result.insert(k, arg); @@ -1647,11 +1647,11 @@ Value ArrayPrototype::method_join(ExecutionContext *ctx) // ### FIXME if (ArrayObject *a = self.asArrayObject()) { - for (uint i = 0; i < a->value.length(); ++i) { + for (uint i = 0; i < a->array.length(); ++i) { if (i) R += r4; - Value e = a->value.at(i); + Value e = a->array.at(i); if (! (e.isUndefined() || e.isNull())) R += e.toString(ctx)->toQString(); } @@ -1682,7 +1682,7 @@ Value ArrayPrototype::method_pop(ExecutionContext *ctx) { Value self = ctx->thisObject; if (ArrayObject *instance = self.asArrayObject()) - return instance->value.pop_back(); + return instance->array.pop_back(); Value r1 = self.property(ctx, ctx->engine->id_length); quint32 r2 = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; @@ -1704,9 +1704,9 @@ Value ArrayPrototype::method_push(ExecutionContext *ctx) if (ArrayObject *instance = self.asArrayObject()) { for (unsigned int i = 0; i < ctx->argumentCount; ++i) { Value val = ctx->argument(i); - instance->value.push_back(val); + instance->array.push_back(val); } - return Value::fromDouble(instance->value.length()); + return Value::fromDouble(instance->array.length()); } Value r1 = self.property(ctx, ctx->engine->id_length); @@ -1727,12 +1727,12 @@ Value ArrayPrototype::method_reverse(ExecutionContext *ctx) if (!instance) ctx->throwUnimplemented(QStringLiteral("Array.prototype.reverse")); - int lo = 0, hi = instance->value.length() - 1; + int lo = 0, hi = instance->array.length() - 1; for (; lo < hi; ++lo, --hi) { - Value tmp = instance->value.at(lo); - instance->value.insert(lo, instance->value.at(hi)); - instance->value.insert(hi, tmp); + Value tmp = instance->array.at(lo); + instance->array.insert(lo, instance->array.at(hi)); + instance->array.insert(hi, tmp); } return Value::undefinedValue(); } @@ -1743,7 +1743,7 @@ Value ArrayPrototype::method_shift(ExecutionContext *ctx) if (!instance) ctx->throwUnimplemented(QStringLiteral("Array.prototype.shift")); - return instance->value.pop_front(); + return instance->array.pop_front(); } Value ArrayPrototype::method_slice(ExecutionContext *ctx) @@ -1779,7 +1779,7 @@ Value ArrayPrototype::method_sort(ExecutionContext *ctx) ctx->throwUnimplemented(QStringLiteral("Array.prototype.sort")); Value comparefn = ctx->argument(0); - instance->value.sort(ctx, comparefn); + instance->array.sort(ctx, comparefn); return ctx->thisObject; } @@ -1801,7 +1801,7 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) items << ctx->argument(i); ArrayObject *otherInstance = a.asArrayObject(); assert(otherInstance); - instance->value.splice(start, deleteCount, items, otherInstance->value); + instance->array.splice(start, deleteCount, items, otherInstance->array); return a; } @@ -1813,10 +1813,10 @@ Value ArrayPrototype::method_unshift(ExecutionContext *ctx) for (int i = ctx->argumentCount - 1; i >= 0; --i) { Value v = ctx->argument(i); - instance->value.push_front(v); + instance->array.push_front(v); } - uint l = instance->value.length(); + uint l = instance->array.length(); if (l < INT_MAX) return Value::fromInt32(l); return Value::fromDouble((double)l); @@ -1839,9 +1839,9 @@ Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) } else __qmljs_throw_type_error(ctx); - for (Array::ConstIterator it = instance->value.find(fromIndex), end = instance->value.constEnd(); + for (Array::ConstIterator it = instance->array.find(fromIndex), end = instance->array.constEnd(); it != end; ++it) { - if (__qmljs_strict_equal(instance->value.valueRefFromIndex(*it), searchValue)) + if (__qmljs_strict_equal(instance->array.valueRefFromIndex(*it), searchValue)) return Value::fromInt32(it.key()); } @@ -1864,8 +1864,8 @@ Value ArrayPrototype::method_every(ExecutionContext *ctx) Value thisArg = ctx->argument(1); bool ok = true; // ### - for (uint k = 0; ok && k < instance->value.length(); ++k) { - Value v = instance->value.at(k); + for (uint k = 0; ok && k < instance->array.length(); ++k) { + Value v = instance->array.at(k); if (v.isUndefined()) continue; @@ -1889,8 +1889,8 @@ Value ArrayPrototype::method_some(ExecutionContext *ctx) Value thisArg = ctx->argument(1); bool ok = false; // ### - for (uint k = 0; !ok && k < instance->value.length(); ++k) { - Value v = instance->value.at(k); + for (uint k = 0; !ok && k < instance->array.length(); ++k) { + Value v = instance->array.at(k); if (v.isUndefined()) continue; @@ -1913,8 +1913,8 @@ Value ArrayPrototype::method_forEach(ExecutionContext *ctx) Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); // ### - for (quint32 k = 0; k < instance->value.length(); ++k) { - Value v = instance->value.at(k); + for (quint32 k = 0; k < instance->array.length(); ++k) { + Value v = instance->array.at(k); if (v.isUndefined()) continue; Value args[3]; @@ -1935,9 +1935,9 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); - a->value.setLength(instance->value.length()); - for (quint32 k = 0; k < instance->value.length(); ++k) { - Value v = instance->value.at(k); + a->array.setLength(instance->array.length()); + for (quint32 k = 0; k < instance->array.length(); ++k) { + Value v = instance->array.at(k); if (v.isUndefined()) continue; Value args[3]; @@ -1945,7 +1945,7 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); - a->value.insert(k, r); + a->array.insert(k, r); } return Value::fromObject(a); } @@ -1959,8 +1959,8 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); - for (quint32 k = 0; k < instance->value.length(); ++k) { - Value v = instance->value.at(k); + for (quint32 k = 0; k < instance->array.length(); ++k) { + Value v = instance->array.at(k); if (v.isUndefined()) continue; Value args[3]; @@ -1969,9 +1969,9 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) args[2] = ctx->thisObject; Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); if (__qmljs_to_boolean(r, ctx)) { - const uint index = a->value.length(); - a->value.setLength(index + 1); - a->value.insert(index, v); + const uint index = a->array.length(); + a->array.setLength(index + 1); + a->array.insert(index, v); } } return Value::fromObject(a); @@ -1986,8 +1986,8 @@ Value ArrayPrototype::method_reduce(ExecutionContext *ctx) Value callback = ctx->argument(0); Value initialValue = ctx->argument(1); Value acc = initialValue; - for (quint32 k = 0; k < instance->value.length(); ++k) { - Value v = instance->value.at(k); + for (quint32 k = 0; k < instance->array.length(); ++k) { + Value v = instance->array.at(k); if (v.isUndefined()) continue; @@ -2016,8 +2016,8 @@ Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) Value callback = ctx->argument(0); Value initialValue = ctx->argument(1); Value acc = initialValue; - for (int k = instance->value.length() - 1; k != -1; --k) { - Value v = instance->value.at(k); + for (int k = instance->array.length() - 1; k != -1; --k) { + Value v = instance->array.at(k); if (v.isUndefined()) continue; @@ -2127,7 +2127,7 @@ Value FunctionPrototype::method_apply(ExecutionContext *ctx) QVector args; if (ArrayObject *arr = arg.asArrayObject()) { - const Array &actuals = arr->value; + const Array &actuals = arr->array; for (quint32 i = 0; i < actuals.length(); ++i) { Value a = actuals.at(i); @@ -2849,7 +2849,7 @@ Value RegExpPrototype::method_exec(ExecutionContext *ctx) ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); int captured = match.lastCapturedIndex(); for (int i = 0; i <= captured; ++i) - array->value.push_back(Value::fromString(ctx, match.captured(i))); + array->array.push_back(Value::fromString(ctx, match.captured(i))); array->__put__(ctx, QLatin1String("index"), Value::fromInt32(match.capturedStart(0))); array->__put__(ctx, QLatin1String("input"), arg); -- cgit v1.2.3 From 630d2928b72e98f7ff8c09cbbe13f37e55ece0cc Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 8 Jan 2013 16:09:48 +0100 Subject: moth: add builtin_define_getter_setter and builtin_define_property. Change-Id: Iffd8c79dbc99f9563235b440fa18e446cedabf0c Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 17 +++++++++++++++++ moth/qv4isel_moth.cpp | 33 +++++++++++++++++++++++++++++++++ moth/qv4vme_moth.cpp | 8 ++++++++ 3 files changed, 58 insertions(+) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index d1b2de6ca3..fd0dbb4cfc 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -28,6 +28,8 @@ F(CallBuiltinTypeofName, callBuiltinTypeofName) \ F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \ F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ + F(CallBuiltinDefineGetterSetter, callBuiltinDefineGetterSetter) \ + F(CallBuiltinDefineProperty, callBuiltinDefineProperty) \ F(CreateValue, createValue) \ F(CreateProperty, createProperty) \ F(CreateActivationProperty, createActivationProperty) \ @@ -215,6 +217,19 @@ union Instr bool isDeletable; VM::String *varName; }; + struct instr_callBuiltinDefineGetterSetter { + MOTH_INSTR_HEADER + int objectTemp; + VM::String *name; + int getterTemp; + int setterTemp; + }; + struct instr_callBuiltinDefineProperty { + MOTH_INSTR_HEADER + int objectTemp; + VM::String *name; + int valueTemp; + }; struct instr_createValue { MOTH_INSTR_HEADER int func; @@ -313,6 +328,8 @@ union Instr instr_callBuiltinTypeofName callBuiltinTypeofName; instr_callBuiltinTypeofValue callBuiltinTypeofValue; instr_callBuiltinDeclareVar callBuiltinDeclareVar; + instr_callBuiltinDefineGetterSetter callBuiltinDefineGetterSetter; + instr_callBuiltinDefineProperty callBuiltinDefineProperty; instr_createValue createValue; instr_createProperty createProperty; instr_createActivationProperty createActivationProperty; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 8f1dfa3143..6f06fe95cf 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -426,6 +426,39 @@ void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempInd } } break; + case IR::Name::builtin_define_getter_setter: { + if (!c->args) + return; + IR::ExprList *args = c->args; + Instruction::CallBuiltinDefineGetterSetter call; + call.objectTemp = args->expr->asTemp()->index; + args = args->next; + assert(args); + call.name = engine()->newString(*args->expr->asName()->id); + args = args->next; + assert(args); + call.getterTemp = args->expr->asTemp()->index; + args = args->next; + assert(args); + call.setterTemp = args->expr->asTemp()->index; + addInstruction(call); + } break; + + case IR::Name::builtin_define_property: { + if (!c->args) + return; + IR::ExprList *args = c->args; + Instruction::CallBuiltinDefineProperty call; + call.objectTemp = args->expr->asTemp()->index; + args = args->next; + assert(args); + call.name = engine()->newString(*args->expr->asName()->id); + args = args->next; + assert(args); + call.valueTemp = args->expr->asTemp()->index; + addInstruction(call); + } break; + default: Q_UNIMPLEMENTED(); c->dump(qout); qout << endl; diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 0156e4a8da..2d21b2f064 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -319,6 +319,14 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co __qmljs_builtin_declare_var(context, instr.isDeletable, instr.varName); MOTH_END_INSTR(CallBuiltinDeleteValue) + MOTH_BEGIN_INSTR(CallBuiltinDefineGetterSetter) + __qmljs_builtin_define_getter_setter(TEMP(instr.objectTemp), instr.name, TEMP(instr.getterTemp), TEMP(instr.setterTemp), context); + MOTH_END_INSTR(CallBuiltinDefineGetterSetter) + + MOTH_BEGIN_INSTR(CallBuiltinDefineProperty) + __qmljs_builtin_define_property(TEMP(instr.objectTemp), instr.name, TEMP(instr.valueTemp), context); + MOTH_END_INSTR(CallBuiltinDefineProperty) + MOTH_BEGIN_INSTR(CreateValue) quint32 argStart = instr.args - context->variableCount(); VM::Value *args = stack + argStart; -- cgit v1.2.3 From acfe6b5d45effc9c2eb081bedb8c794e6580e081 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 8 Jan 2013 16:34:05 +0100 Subject: Fix delete someArray[index] The current implementation resized the array, where as we should be poking a whole into the array. With this patch that's done using an explicit undefined value. In the future I guess the array will support sparse segments. Fixes currently failing tests: ch11/11.4/11.4.1/11.4.1-4.a-14 in non-strict mode ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-7-2 in non-strict mode ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-9-3 in non-strict mode Change-Id: Idb0a230e38af3ce1f8f80f71257d472434154f95 Reviewed-by: Lars Knoll --- qmljs_runtime.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index e3c9d9cc73..79cd4a6b85 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -178,7 +178,9 @@ Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index) if (n >= 0) { if (n < (int) a->array.length()) { // ### check writable/deletable property - a->array.remove(n); + Array::Iterator it = a->array.find(n); + if (it != a->array.end()) + a->array.valueRefFromIndex(*it) = Value::undefinedValue(); return Value::fromBoolean(true); } } -- cgit v1.2.3 From f5b31970de7346b1de7987c9c169ae21c4845243 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 8 Jan 2013 16:47:46 +0100 Subject: Mark array regressions as expected failures Change-Id: I9ad9462318ed4759a30f74270cd069cc43d86fcd Reviewed-by: Simon Hausmann --- tests/TestExpectations | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index 33eae376e4..8e25978e01 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -4459,4 +4459,50 @@ S15.2.4.7_A13 failing 15.3.5.4_2-54gs failing 15.3.5.4_2-5gs failing 15.3.5.4_2-7gs failing -15.3.5.4_2-9gs failing \ No newline at end of file +15.3.5.4_2-9gs failing + +# Array regressions +S12.14_A18_T7 failing +S12.13_A3_T4 failing +S13.2.1_A5_T1 failing +S15.4.4.11_A1.5_T1 failing +S15.4.4.11_A2.1_T1 failing +S15.4.4.11_A2.1_T2 failing +S15.4.4.11_A2.1_T3 failing +S15.4.4.11_A2.2_T1 failing +S15.4.4.11_A2.2_T2 failing +S15.4.4.11_A2.2_T3 failing +S15.4.4.11_A5_T1 failing +15.4.4.12-9-a-1 failing +S15.4.4.12_A1.1_T1 failing +S15.4.4.12_A1.1_T2 failing +S15.4.4.12_A1.1_T3 failing +S15.4.4.12_A1.1_T5 failing +S15.4.4.12_A1.2_T3 failing +S15.4.4.12_A1.2_T4 failing +S15.4.4.12_A1.2_T5 failing +S15.4.4.12_A1.3_T3 failing +S15.4.4.12_A1.3_T4 failing +S15.4.4.12_A1.3_T5 failing +S15.4.4.12_A1.4_T1 failing +S15.4.4.12_A1.4_T2 failing +S15.4.4.12_A1.4_T3 failing +S15.4.4.12_A1.4_T5 failing +S15.4.4.12_A2.1_T2 failing +S15.4.4.12_A2.1_T4 failing +S15.4.4.12_A2.1_T5 failing +S15.4.4.12_A2.2_T3 failing +S15.4.4.12_A2.2_T5 failing +S15.4.4.10_A2_T1 failing +S15.4.4.10_A2_T2 failing +S15.4.4.10_A2_T3 failing +S15.4.4.10_A2_T4 failing +S15.4.4.10_A2_T5 failing +S15.4.4.10_A2_T6 failing +S15.4.4.11_A1.2_T1 failing +S15.4.4.11_A1.2_T2 failing +S15.4.4.11_A1.4_T1 failing +S15.4.4.11_A1.4_T2 failing +S15.4.4.4_A1_T1 failing +S15.4.4.4_A1_T2 failing +S15.4.4.4_A1_T3 failing -- cgit v1.2.3 From 65357da1282d687bd59d6ee1ca8e1f535ba8107f Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 8 Jan 2013 12:57:45 +0100 Subject: Fix some compiler warnings. Change-Id: I5e2c87581d4c309490f12accfff32418700fbb71 Reviewed-by: Simon Hausmann --- main.cpp | 2 +- qmljs_value.h | 4 ++-- qv4mm.cpp | 2 +- v4.pro | 3 +++ 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/main.cpp b/main.cpp index 2404ee96c0..d115ee09c7 100644 --- a/main.cpp +++ b/main.cpp @@ -159,7 +159,7 @@ int executeLLVMCode(void *codePtr) void (*code)(VM::ExecutionContext *) = (void (*)(VM::ExecutionContext *)) codePtr; QScopedPointer iSelFactory(new QQmlJS::Moth::ISelFactory); - VM::ExecutionEngine vm(0, iSelFactory.data()); + VM::ExecutionEngine vm(iSelFactory.data()); VM::ExecutionContext *ctx = vm.rootContext; QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); diff --git a/qmljs_value.h b/qmljs_value.h index b8c78d3f1f..48ec2d4541 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -125,8 +125,8 @@ struct Value }; - inline ValueType type() const { - return (ValueType)(tag & Type_Mask); + inline unsigned type() const { + return tag & Type_Mask; } inline bool isUndefined() const { return tag == _Undefined_Type; } diff --git a/qv4mm.cpp b/qv4mm.cpp index fe652b4028..9339b38244 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -165,7 +165,7 @@ Managed *MemoryManager::alloc(std::size_t size) void MemoryManager::scribble(Managed *obj, int c, int size) const { if (m_d->scribble) - ::memset(obj + 1, c, size - sizeof(Managed)); + ::memset((void *)(obj + 1), c, size - sizeof(Managed)); } std::size_t MemoryManager::mark(const QVector &objects) diff --git a/v4.pro b/v4.pro index 80687b4ca5..e947da90bb 100644 --- a/v4.pro +++ b/v4.pro @@ -64,6 +64,8 @@ INCLUDEPATH += \ $$system($$LLVM_CONFIG --includedir) QMAKE_CXXFLAGS += $$system($$LLVM_CONFIG --cppflags) -fvisibility-inlines-hidden +QMAKE_CXXFLAGS -= -pedantic +QMAKE_CXXFLAGS -= -Wcovered-switch-default LIBS += \ $$system($$LLVM_CONFIG --ldflags) \ @@ -72,6 +74,7 @@ LIBS += \ QMAKE_EXTRA_TARGETS += gen_llvm_runtime GEN_LLVM_RUNTIME_FLAGS = $$system($$LLVM_CONFIG --cppflags) +GEN_LLVM_RUNTIME_FLAGS -= -pedantic gen_llvm_runtime.target = llvm_runtime gen_llvm_runtime.commands = clang -O2 -emit-llvm -c $(INCPATH) $$GEN_LLVM_RUNTIME_FLAGS -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o llvm_runtime.bc -- cgit v1.2.3 From 34b67d82435c3b2a38d21cc3eb79a8c26d1f1146 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 9 Jan 2013 10:36:19 +0100 Subject: Updated the test expectations. Change-Id: Ia54ed953d1c313b8c24bf195b5de0e52d3058976 Reviewed-by: Simon Hausmann --- tests/TestExpectations | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index 8e25978e01..b49b4713ce 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -15,7 +15,6 @@ S15.4.5.2_A3_T4 failing 15.4.4.15-5-16 failing 15.4.4.15-5-12 failing 15.4.4.14-9-9 failing -S7.9_A5.8_T1 failing S8.8_A2_T1 failing S8.8_A2_T2 failing S8.8_A2_T3 failing @@ -4505,4 +4504,4 @@ S15.4.4.11_A1.4_T1 failing S15.4.4.11_A1.4_T2 failing S15.4.4.4_A1_T1 failing S15.4.4.4_A1_T2 failing -S15.4.4.4_A1_T3 failing +S15.4.4.4_A1_T3 failing \ No newline at end of file -- cgit v1.2.3 From 82f59f156a1c5508383ad996b77903627910ee8f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 9 Jan 2013 11:05:31 +0100 Subject: Make auto-update of TestExpectations smarter ...by making it also recognize when a test that (1) is expected to fail, _and_ (2) we expected to fail and therefore (3) expected to _pass_ now suddenly "fails" the way it should fail in the first place. Change-Id: I78cbf637e3ac9c2e964752d509f9a5fcf1431abf Reviewed-by: Erik Verbruggen --- tests/test262.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/test262.py b/tests/test262.py index f936b8509a..e8df475649 100755 --- a/tests/test262.py +++ b/tests/test262.py @@ -74,22 +74,30 @@ class TestExpectations: f.close() def update(self, progress): - unexpectedPasses = [c for c in progress.failed_tests if c.case.IsNegative()] + unexpectedPasses = [c.case.name[-1] for c in progress.failed_tests if c.case.IsNegative()] + + # If a test fails that we expected to fail, then it actually passed unexpectedly. + failures = [c.case.name[-1] for c in progress.failed_tests if not c.case.IsNegative()] + for failure in failures: + if failure in self.failingTests: + unexpectedPasses.append(failure) f = open(rootDir + "/TestExpectations") lines = f.read().splitlines() oldLen = len(lines) for result in unexpectedPasses: - expectationLine = result.case.name[-1] + " failing" + expectationLine = result + " failing" try: lines.remove(expectationLine) except ValueError: pass + f.close() if len(lines) != oldLen: f = open(rootDir + "/TestExpectations", "w") f.write("\n".join(lines)) f.close() + print "Changes to TestExpectations written!" if not os.path.exists(EXCLUDED_FILENAME): -- cgit v1.2.3 From 2d30937b2aeb85d9e8abff01bcc49f4906039a1f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 9 Jan 2013 11:07:11 +0100 Subject: Remove more tests from the list of tests that we expect to fail They do pass now after the latest qtdeclarative updates Change-Id: Ice66d5b95853ea9211ab8da915fc04b57d937a0d Reviewed-by: Erik Verbruggen --- tests/TestExpectations | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index b49b4713ce..369a034f3e 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -4393,8 +4393,6 @@ S15.9.5.9_A3_T2 failing 6.4_b failing # Tests that are passing but are supposed to fail (@negative) -S7.8.3_A6.1_T1 failing -S7.8.3_A6.1_T2 failing Sbp_12.5_A9_T3 failing Sbp_12.6.2_A13_T3 failing Sbp_7.8.4_A6.1_T4 failing @@ -4406,14 +4404,7 @@ Sbp_A2_T2 failing Sbp_A3_T1 failing Sbp_A4_T1 failing Sbp_A4_T2 failing -S7.8.4_A7.1_T4 failing S12.4_A1 failing -S7.8.4_A7.2_T1 failing -S7.8.4_A7.2_T2 failing -S7.8.4_A7.2_T3 failing -S7.8.4_A7.2_T4 failing -S7.8.4_A7.2_T5 failing -S7.8.4_A7.2_T6 failing S12.9_A1_T1 failing S12.9_A1_T10 failing S12.9_A1_T2 failing -- cgit v1.2.3 From 0c4053d4ad907313119e1d90118e28814242d97d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 9 Jan 2013 11:38:11 +0100 Subject: Update expectations Mark test as known to fail that is a known regression for now Change-Id: Ic7b80e7623c379a742341fe23215eca1fad08c9d Reviewed-by: Simon Hausmann --- tests/TestExpectations | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index 369a034f3e..c411ad425d 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -4495,4 +4495,7 @@ S15.4.4.11_A1.4_T1 failing S15.4.4.11_A1.4_T2 failing S15.4.4.4_A1_T1 failing S15.4.4.4_A1_T2 failing -S15.4.4.4_A1_T3 failing \ No newline at end of file +S15.4.4.4_A1_T3 failing + +# Regression introduced by qtdeclarative parser changes +S7.9_A5.7_T1 failing -- cgit v1.2.3 From 8f132b89eb7a69478a863e65c729315c64c43127 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 9 Jan 2013 14:37:55 +0100 Subject: Added parseInt (15.1.2.2) and parseFloat (15.1.2.3). Change-Id: I0ff54a59d68596b55fb1add25c29f894b05252b9 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 4 +- qmljs_objects.cpp | 122 +++++++++++++++++++++++++++++++++++++++++++++++-- qmljs_objects.h | 14 ++++++ tests/TestExpectations | 112 --------------------------------------------- 4 files changed, 134 insertions(+), 118 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index e322cfa261..bac0dc813e 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -205,8 +205,8 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) glo->defineDefaultProperty(rootContext, QStringLiteral("eval"), Value::fromObject(new (memoryManager) EvalFunction(rootContext))); - // TODO: parseInt [15.1.2.2] - // TODO: parseFloat [15.1.2.3] + glo->defineDefaultProperty(rootContext, QStringLiteral("parseInt"), Value::fromObject(new (memoryManager) ParseIntFunction(rootContext))); + glo->defineDefaultProperty(rootContext, QStringLiteral("parseFloat"), Value::fromObject(new (memoryManager) ParseFloatFunction(rootContext))); glo->defineDefaultProperty(rootContext, QStringLiteral("isNaN"), Value::fromObject(new (memoryManager) IsNaNFunction(rootContext))); glo->defineDefaultProperty(rootContext, QStringLiteral("isFinite"), Value::fromObject(new (memoryManager) IsFiniteFunction(rootContext))); } diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index a66c15a492..c7d134c678 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -51,6 +51,7 @@ #include #include #include +#include "private/qlocale_tools_p.h" #include #include @@ -678,6 +679,119 @@ QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct return globalCode; } +// parseInt [15.1.2.2] +ParseIntFunction::ParseIntFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("parseInt")); +} + +static inline int toInt(const QChar &qc, int R) +{ + ushort c = qc.unicode(); + int v = -1; + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'A' && c <= 'Z') + v = c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + v = c - 'a' + 10; + if (v >= 0 && v < R) + return v; + else + return -1; +} + +Value ParseIntFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +{ + Q_UNUSED(thisObject); + + Value string = (argc > 0) ? args[0] : Value::undefinedValue(); + Value radix = (argc > 1) ? args[1] : Value::undefinedValue(); + int R = radix.isUndefined() ? 0 : radix.toInt32(context); + + // [15.1.2.2] step by step: + String *inputString = string.toString(context); // 1 + QString trimmed = inputString->toQString().trimmed(); // 2 + const QChar *pos = trimmed.constData(); + const QChar *end = pos + trimmed.length(); + + int sign = 1; // 3 + if (pos != end) { + if (*pos == QLatin1Char('-')) + sign = -1; // 4 + if (*pos == QLatin1Char('-') || *pos == QLatin1Char('+')) + ++pos; // 5 + } + bool stripPrefix = true; // 7 + if (R) { // 8 + if (R < 2 || R > 36) + return Value::fromDouble(nan("")); // 8a + if (R != 16) + stripPrefix = false; // 8b + } else { // 9 + R = 10; // 9a + } + if (stripPrefix) { // 10 + if ((end - pos >= 2) + && (pos[0] == QLatin1Char('0')) + && (pos[1] == QLatin1Char('x') || pos[1] == QLatin1Char('X'))) { // 10a + pos += 2; + R = 16; + } + } + // 11: Z is progressively built below + // 13: this is handled by the toInt function + if (pos == end) // 12 + return Value::fromDouble(nan("")); + int d = toInt(*pos++, R); + if (d == -1) + return Value::fromDouble(nan("")); + qint64 v = d; + while (pos != end) { + d = toInt(*pos++, R); + if (d == -1) + break; + v = v * R + d; + } + + return Value::fromDouble(v * sign); // 15 +} + +// parseFloat [15.1.2.3] +ParseFloatFunction::ParseFloatFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("parseFloat")); +} + +Value ParseFloatFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +{ + Q_UNUSED(context); + Q_UNUSED(thisObject); + + Value string = (argc > 0) ? args[0] : Value::undefinedValue(); + + // [15.1.2.3] step by step: + String *inputString = string.toString(context); // 1 + QString trimmed = inputString->toQString().trimmed(); // 2 + + // 4: + if (trimmed.startsWith(QLatin1String("Infinity")) + || trimmed.startsWith(QLatin1String("+Infinity"))) + return Value::fromDouble(INFINITY); + if (trimmed.startsWith("-Infinity")) + return Value::fromDouble(-INFINITY); + QByteArray ba = trimmed.toLatin1(); + bool ok; + const char *begin = ba.constData(); + const char *end = 0; + double d = qstrtod(begin, &end, &ok); + if (end - begin == 0) + return Value::fromDouble(nan("")); // 3 + else + return Value::fromDouble(d); +} /// isNaN [15.1.2.4] IsNaNFunction::IsNaNFunction(ExecutionContext *scope) @@ -686,9 +800,9 @@ IsNaNFunction::IsNaNFunction(ExecutionContext *scope) name = scope->engine->newString(QLatin1String("isNaN")); } -Value IsNaNFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int /*argc*/) +Value IsNaNFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) { - const Value &v = args[0]; + const Value &v = (argc > 0) ? args[0] : Value::undefinedValue(); if (v.integerCompatible()) return Value::fromBoolean(false); @@ -703,9 +817,9 @@ IsFiniteFunction::IsFiniteFunction(ExecutionContext *scope) name = scope->engine->newString(QLatin1String("isFinite")); } -Value IsFiniteFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int /*argc*/) +Value IsFiniteFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) { - const Value &v = args[0]; + const Value &v = (argc > 0) ? args[0] : Value::undefinedValue(); if (v.integerCompatible()) return Value::fromBoolean(true); diff --git a/qmljs_objects.h b/qmljs_objects.h index e489fa1933..60fb48a8a3 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -298,6 +298,20 @@ struct EvalFunction : FunctionObject virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); }; +struct ParseIntFunction: FunctionObject +{ + ParseIntFunction(ExecutionContext *scope); + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); +}; + +struct ParseFloatFunction: FunctionObject +{ + ParseFloatFunction(ExecutionContext *scope); + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); +}; + struct IsNaNFunction: FunctionObject { IsNaNFunction(ExecutionContext *scope); diff --git a/tests/TestExpectations b/tests/TestExpectations index c411ad425d..114443f6e6 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -18,7 +18,6 @@ S15.4.5.2_A3_T4 failing S8.8_A2_T1 failing S8.8_A2_T2 failing S8.8_A2_T3 failing -S10.1.1_A2_T1 failing S10.1.6_A1_T2 failing S10.2.3_A1.1_T2 failing S10.2.3_A1.2_T2 failing @@ -317,8 +316,6 @@ S12.14_A7_T3 failing 12.14.1-1-s failing 12.14.1-2-s failing 12.14.1-3-s failing -S12.10_A1.9_T1 failing -S12.10_A1.9_T2 failing S12.10_A3.11_T1 failing S12.10_A3.11_T2 failing S12.10_A3.11_T3 failing @@ -343,39 +340,16 @@ S12.13_A3_T3 failing 12.14-15 failing 12.14-16 failing 12.10-0-3 failing -S12.10_A1.10_T1 failing -S12.10_A1.10_T2 failing -S12.10_A1.10_T4 failing S12.10_A1.11_T1 failing S12.10_A1.11_T2 failing S12.10_A1.11_T4 failing S12.10_A1.12_T1 failing S12.10_A1.12_T2 failing S12.10_A1.12_T4 failing -S12.10_A1.1_T1 failing -S12.10_A1.1_T2 failing -S12.10_A1.2_T1 failing -S12.10_A1.2_T2 failing -S12.10_A1.2_T4 failing -S12.10_A1.3_T1 failing -S12.10_A1.3_T2 failing -S12.10_A1.3_T4 failing -S12.10_A1.4_T1 failing -S12.10_A1.4_T2 failing S12.10_A1.4_T4 failing S12.10_A1.4_T5 failing -S12.10_A1.5_T1 failing -S12.10_A1.5_T2 failing S12.10_A1.5_T4 failing S12.10_A1.5_T5 failing -S12.10_A1.6_T1 failing -S12.10_A1.6_T2 failing -S12.10_A1.7_T1 failing -S12.10_A1.7_T2 failing -S12.10_A1.7_T4 failing -S12.10_A1.8_T1 failing -S12.10_A1.8_T2 failing -S12.10_A1.8_T4 failing S12.6.1_A8 failing 12.2.1-1-s failing 12.2.1-12-s failing @@ -531,27 +505,6 @@ S15.1.2.1_A4.3 failing S15.1.2.1_A4.4 failing S15.1.2.1_A4.5 failing S15.1.2.1_A4.7 failing -15.1.2.2-2-1 failing -S15.1.2.2_A1_T1 failing -S15.1.2.2_A1_T2 failing -S15.1.2.2_A1_T3 failing -S15.1.2.2_A1_T4 failing -S15.1.2.2_A1_T5 failing -S15.1.2.2_A1_T6 failing -S15.1.2.2_A1_T7 failing -S15.1.2.2_A2_T1 failing -S15.1.2.2_A2_T10 failing -S15.1.2.2_A2_T2 failing -S15.1.2.2_A2_T3 failing -S15.1.2.2_A2_T4 failing -S15.1.2.2_A2_T5 failing -S15.1.2.2_A2_T6 failing -S15.1.2.2_A2_T7 failing -S15.1.2.2_A2_T8 failing -S15.1.2.2_A2_T9 failing -S15.1.2.2_A3.1_T1 failing -S15.1.2.2_A3.1_T2 failing -S15.1.2.2_A3.1_T3 failing S12.6.4_A1 failing S12.6.4_A2 failing S12.6.4_A3.1 failing @@ -560,69 +513,13 @@ S12.7_A7 failing S12.8_A4_T1 failing S12.8_A4_T2 failing S12.8_A4_T3 failing -S15.1.2.2_A3.1_T4 failing -S15.1.2.2_A3.1_T5 failing -S15.1.2.2_A3.1_T6 failing -S15.1.2.2_A3.1_T7 failing -S15.1.2.2_A3.2_T1 failing -S15.1.2.2_A3.2_T2 failing -S15.1.2.2_A3.2_T3 failing -S15.1.2.2_A4.1_T1 failing -S15.1.2.2_A4.1_T2 failing -S15.1.2.2_A4.2_T1 failing -S15.1.2.2_A4.2_T2 failing -S15.1.2.2_A4.2_T3 failing -S15.1.2.2_A5.1_T1 failing -S15.1.2.2_A5.2_T1 failing -S15.1.2.2_A5.2_T2 failing -S15.1.2.2_A6.1_T1 failing -S15.1.2.2_A6.1_T2 failing -S15.1.2.2_A6.1_T3 failing -S15.1.2.2_A6.1_T4 failing -S15.1.2.2_A6.1_T5 failing -S15.1.2.2_A6.1_T6 failing -S15.1.2.2_A7.1_T1 failing -S15.1.2.2_A7.1_T2 failing -S15.1.2.2_A7.2_T1 failing -S15.1.2.2_A7.2_T2 failing S15.1.2.2_A7.2_T3 failing -S15.1.2.2_A7.3_T1 failing -S15.1.2.2_A7.3_T2 failing S15.1.2.2_A7.3_T3 failing -S15.1.2.2_A8 failing S15.1.2.2_A9.1 failing S15.1.2.2_A9.2 failing S15.1.2.2_A9.3 failing S15.1.2.2_A9.4 failing -S15.1.2.2_A9.6 failing S15.1.2.2_A9.7 failing -15.1.2.3-2-1 failing -S15.1.2.3_A1_T1 failing -S15.1.2.3_A1_T2 failing -S15.1.2.3_A1_T3 failing -S15.1.2.3_A1_T4 failing -S15.1.2.3_A1_T5 failing -S15.1.2.3_A1_T6 failing -S15.1.2.3_A1_T7 failing -S15.1.2.3_A2_T1 failing -S15.1.2.3_A2_T10 failing -S15.1.2.3_A2_T2 failing -S15.1.2.3_A2_T3 failing -S15.1.2.3_A2_T4 failing -S15.1.2.3_A2_T5 failing -S15.1.2.3_A2_T6 failing -S15.1.2.3_A2_T7 failing -S15.1.2.3_A2_T8 failing -S15.1.2.3_A2_T9 failing -S15.1.2.3_A3_T1 failing -S15.1.2.3_A3_T2 failing -S15.1.2.3_A3_T3 failing -S15.1.2.3_A4_T1 failing -S15.1.2.3_A4_T2 failing -S15.1.2.3_A4_T3 failing -S15.1.2.3_A4_T4 failing -S15.1.2.3_A4_T5 failing -S15.1.2.3_A4_T6 failing S15.10.2.15_A1_T38 failing S15.10.2.15_A1_T39 failing S15.10.2.15_A1_T4 failing @@ -834,17 +731,10 @@ S15.12.2_A1 failing 15.12.3-11-13 failing 15.12.3-11-14 failing 15.12.3-11-15 failing -S15.1.2.3_A4_T7 failing -S15.1.2.3_A5_T1 failing -S15.1.2.3_A5_T2 failing -S15.1.2.3_A5_T3 failing -S15.1.2.3_A5_T4 failing -S15.1.2.3_A6 failing S15.1.2.3_A7.1 failing S15.1.2.3_A7.2 failing S15.1.2.3_A7.3 failing S15.1.2.3_A7.4 failing -S15.1.2.3_A7.6 failing S15.1.2.3_A7.7 failing S15.1.2.4_A2.1 failing S15.1.2.4_A2.2 failing @@ -1123,9 +1013,7 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-220 failing 15.2.3.3-4-221 failing 15.2.3.3-4-222 failing -15.2.3.3-4-5 failing 15.2.3.3-4-51 failing -15.2.3.3-4-6 failing 15.2.3.3-4-82 failing 15.2.3.3-4-9 failing 15.2.3.3-4-90 failing -- cgit v1.2.3 From 24da260d35992957c8003024e89c5336464e5785 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 9 Jan 2013 17:29:04 +0100 Subject: Updated test expectations Change-Id: I4458cabdc2ce2b6756146e196926071ddcabc449 Reviewed-by: Simon Hausmann --- tests/TestExpectations | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/TestExpectations b/tests/TestExpectations index 114443f6e6..7e5f90dc9f 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -4387,3 +4387,8 @@ S15.4.4.4_A1_T3 failing # Regression introduced by qtdeclarative parser changes S7.9_A5.7_T1 failing + +# Regression introduced by https://codereview.qt-project.org/#change,44302 +S15.1.2.2_A9.5 failing +S15.1.2.3_A7.5 failing + -- cgit v1.2.3 From c2e496b5696ad8b68bfa778c3f83ef36c612dc35 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 9 Jan 2013 17:52:23 +0100 Subject: Fix enumerable checking. Change-Id: Iff7e19e1eb9595586b3c540bf179773110f0fd3a Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 2 +- tests/TestExpectations | 66 -------------------------------------------------- 2 files changed, 1 insertion(+), 67 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 14843ad058..fd3383a258 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -844,7 +844,7 @@ Value ObjectPrototype::method_propertyIsEnumerable(ExecutionContext *ctx) Object *o = ctx->thisObject.toObject(ctx).objectValue(); PropertyDescriptor *pd = o->__getOwnProperty__(ctx, p); - return Value::fromBoolean(pd && pd->enumberable); + return Value::fromBoolean(pd && pd->isEnumerable()); } Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index 7e5f90dc9f..4344986530 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -503,7 +503,6 @@ S15.1.2.1_A4.1 failing S15.1.2.1_A4.2 failing S15.1.2.1_A4.3 failing S15.1.2.1_A4.4 failing -S15.1.2.1_A4.5 failing S15.1.2.1_A4.7 failing S12.6.4_A1 failing S12.6.4_A2 failing @@ -633,7 +632,6 @@ S15.10.4.1_A9_T2 failing S15.10.4.1_A9_T3 failing 15.10.4.1-2 failing 15.10.4.1-3 failing -S15.10.5.1_A2 failing S15.10.5.1_A3 failing S15.10.5.1_A4 failing S15.10.5_A1 failing @@ -687,7 +685,6 @@ S15.10.7.4_A9 failing S15.10.7.5_A8 failing S15.10.7.5_A9 failing S15.11.3.1_A1_T1 failing -S15.11.3.1_A2_T1 failing S15.11.3.1_A3_T1 failing S15.11.3_A2_T1 failing S15.11.4.2_A1 failing @@ -740,13 +737,11 @@ S15.1.2.4_A2.1 failing S15.1.2.4_A2.2 failing S15.1.2.4_A2.3 failing S15.1.2.4_A2.4 failing -S15.1.2.4_A2.5 failing S15.1.2.4_A2.7 failing S15.1.2.5_A2.1 failing S15.1.2.5_A2.2 failing S15.1.2.5_A2.3 failing S15.1.2.5_A2.4 failing -S15.1.2.5_A2.5 failing S15.1.2.5_A2.7 failing S15.1.3.1_A1.10_T1 failing S15.1.3.1_A1.11_T1 failing @@ -1535,7 +1530,6 @@ S15.2.4.6_A8 failing S15.2.4.6_A9 failing S15.2.4.7_A10 failing S15.2.4.7_A11 failing -S15.2.4.7_A2_T1 failing S15.2.4.7_A8 failing S15.2.4.7_A9 failing S15.3.1_A1_T1 failing @@ -1561,7 +1555,6 @@ S15.3.2.1_A3_T9 failing 15.3.2.1-11-5-s failing S15.3.3_A3 failing S15.3.3.1_A1 failing -S15.3.3.1_A2 failing S15.3.3.1_A3 failing 15.3.3.2-1 failing S15.3.4.2_A10 failing @@ -1743,7 +1736,6 @@ S15.4.2.2_A2.2_T3 failing S15.4.3_A2.2 failing S15.4.3_A2.3 failing S15.4.3_A2.4 failing -S15.4.3.1_A2 failing S15.4.3.1_A3 failing S15.4.3.1_A4 failing 15.4.3.2-0-1 failing @@ -3360,7 +3352,6 @@ S15.4.4.7_A6.1 failing S15.4.4.7_A6.2 failing S15.4.4.7_A6.3 failing S15.4.4.7_A6.4 failing -S15.5.3.1_A2 failing S15.5.3.1_A3 failing S15.5.3.1_A4 failing S15.5.3.2_A1 failing @@ -3913,17 +3904,10 @@ S15.5.5.1_A5 failing S15.6.3_A3 failing S15.6.3.1_A2 failing S15.6.3.1_A3 failing -S15.6.3.1_A4 failing S15.7.3_A8 failing 15.7.3.1-1 failing S15.7.3.1_A1_T1 failing S15.7.3.1_A1_T2 failing -S15.7.3.1_A1_T3 failing -S15.7.3.2_A4 failing -S15.7.3.3_A4 failing -S15.7.3.4_A4 failing -S15.7.3.5_A4 failing -S15.7.3.6_A4 failing S15.7.4_A3.3 failing S15.7.4.5_A1.3_T01 failing S15.7.4.5_A1.3_T02 failing @@ -3936,12 +3920,9 @@ S15.8.2.16_A5 failing S15.8.2.17_A2 failing S15.9.4.1_A1_T1 failing S15.9.4.1_A1_T2 failing -S15.9.4.1_A1_T3 failing -S15.9.4.2_A1_T3 failing S15.9.4.2_A2_T1 failing S15.9.4.2_A3_T1 failing S15.9.4.2_A3_T2 failing -S15.9.4.3_A1_T3 failing S15.9.4.3_A2_T1 failing S15.9.4.3_A3_T1 failing S15.9.4.3_A3_T2 failing @@ -3962,153 +3943,116 @@ S15.8.2.18_A4 failing S15.8.2.18_A5 failing S15.8.2.7_A4 failing S15.8.2.7_A5 failing -S15.9.5.1_A1_T3 failing S15.9.5.1_A2_T1 failing S15.9.5.1_A3_T1 failing S15.9.5.1_A3_T2 failing -S15.9.5.10_A1_T3 failing S15.9.5.10_A2_T1 failing S15.9.5.10_A3_T1 failing S15.9.5.10_A3_T2 failing -S15.9.5.11_A1_T3 failing S15.9.5.11_A2_T1 failing S15.9.5.11_A3_T1 failing S15.9.5.11_A3_T2 failing -S15.9.5.12_A1_T3 failing S15.9.5.12_A2_T1 failing S15.9.5.12_A3_T1 failing S15.9.5.12_A3_T2 failing -S15.9.5.13_A1_T3 failing S15.9.5.13_A2_T1 failing S15.9.5.13_A3_T1 failing S15.9.5.13_A3_T2 failing -S15.9.5.14_A1_T3 failing S15.9.5.14_A2_T1 failing S15.9.5.14_A3_T1 failing S15.9.5.14_A3_T2 failing -S15.9.5.15_A1_T3 failing S15.9.5.15_A2_T1 failing S15.9.5.15_A3_T1 failing S15.9.5.15_A3_T2 failing -S15.9.5.16_A1_T3 failing S15.9.5.16_A2_T1 failing S15.9.5.16_A3_T1 failing S15.9.5.16_A3_T2 failing -S15.9.5.17_A1_T3 failing S15.9.5.17_A2_T1 failing S15.9.5.17_A3_T1 failing S15.9.5.17_A3_T2 failing 13.3.3_L15 failing -S15.9.5.18_A1_T3 failing S15.9.5.18_A2_T1 failing S15.9.5.18_A3_T1 failing S15.9.5.18_A3_T2 failing -S15.9.5.19_A1_T3 failing S15.9.5.19_A2_T1 failing S15.9.5.19_A3_T1 failing S15.9.5.19_A3_T2 failing -S15.9.5.2_A1_T3 failing S15.9.5.2_A2_T1 failing S15.9.5.2_A3_T1 failing S15.9.5.2_A3_T2 failing -S15.9.5.20_A1_T3 failing S15.9.5.20_A2_T1 failing S15.9.5.20_A3_T1 failing S15.9.5.20_A3_T2 failing -S15.9.5.21_A1_T3 failing S15.9.5.21_A2_T1 failing S15.9.5.21_A3_T1 failing S15.9.5.21_A3_T2 failing -S15.9.5.22_A1_T3 failing S15.9.5.22_A2_T1 failing S15.9.5.22_A3_T1 failing S15.9.5.22_A3_T2 failing -S15.9.5.23_A1_T3 failing S15.9.5.23_A2_T1 failing S15.9.5.23_A3_T1 failing S15.9.5.23_A3_T2 failing -S15.9.5.24_A1_T3 failing S15.9.5.24_A2_T1 failing S15.9.5.24_A3_T1 failing S15.9.5.24_A3_T2 failing -S15.9.5.25_A1_T3 failing S15.9.5.25_A2_T1 failing S15.9.5.25_A3_T1 failing S15.9.5.25_A3_T2 failing -S15.9.5.26_A1_T3 failing S15.9.5.26_A2_T1 failing S15.9.5.26_A3_T1 failing S15.9.5.26_A3_T2 failing -S15.9.5.27_A1_T3 failing S15.9.5.27_A2_T1 failing S15.9.5.27_A3_T1 failing S15.9.5.27_A3_T2 failing -S15.9.5.28_A1_T3 failing S15.9.5.28_A2_T1 failing S15.9.5.28_A3_T1 failing S15.9.5.28_A3_T2 failing -S15.9.5.29_A1_T3 failing S15.9.5.29_A2_T1 failing S15.9.5.29_A3_T1 failing S15.9.5.29_A3_T2 failing -S15.9.5.3_A1_T3 failing S15.9.5.3_A2_T1 failing S15.9.5.3_A3_T1 failing S15.9.5.3_A3_T2 failing -S15.9.5.30_A1_T3 failing S15.9.5.30_A2_T1 failing S15.9.5.30_A3_T1 failing S15.9.5.30_A3_T2 failing -S15.9.5.31_A1_T3 failing S15.9.5.31_A2_T1 failing S15.9.5.31_A3_T1 failing S15.9.5.31_A3_T2 failing -S15.9.5.32_A1_T3 failing S15.9.5.32_A2_T1 failing S15.9.5.32_A3_T1 failing S15.9.5.32_A3_T2 failing -S15.9.5.33_A1_T3 failing S15.9.5.33_A2_T1 failing S15.9.5.33_A3_T1 failing S15.9.5.33_A3_T2 failing -S15.9.5.34_A1_T3 failing S15.9.5.34_A2_T1 failing S15.9.5.34_A3_T1 failing S15.9.5.34_A3_T2 failing -S15.9.5.35_A1_T3 failing S15.9.5.35_A2_T1 failing S15.9.5.35_A3_T1 failing S15.9.5.35_A3_T2 failing -S15.9.5.36_A1_T3 failing S15.9.5.36_A2_T1 failing S15.9.5.36_A3_T1 failing S15.9.5.36_A3_T2 failing -S15.9.5.37_A1_T3 failing S15.9.5.37_A2_T1 failing S15.9.5.37_A3_T1 failing S15.9.5.37_A3_T2 failing -S15.9.5.38_A1_T3 failing S15.9.5.38_A2_T1 failing S15.9.5.38_A3_T1 failing S15.9.5.38_A3_T2 failing -S15.9.5.39_A1_T3 failing S15.9.5.39_A2_T1 failing S15.9.5.39_A3_T1 failing S15.9.5.39_A3_T2 failing -S15.9.5.4_A1_T3 failing S15.9.5.4_A2_T1 failing S15.9.5.4_A3_T1 failing S15.9.5.4_A3_T2 failing 15.9.5.40_1 failing -S15.9.5.40_A1_T3 failing S15.9.5.40_A2_T1 failing S15.9.5.40_A3_T1 failing S15.9.5.40_A3_T2 failing -S15.9.5.41_A1_T3 failing S15.9.5.41_A2_T1 failing S15.9.5.41_A3_T1 failing S15.9.5.41_A3_T2 failing -S15.9.5.42_A1_T3 failing S15.9.5.42_A2_T1 failing 6.4_c failing 8.0_L15 failing @@ -4250,23 +4194,18 @@ S15.9.5.42_A3_T2 failing 15.9.5.43-0-9 failing 15.9.5.44-0-1 failing 15.9.5.44-0-2 failing -S15.9.5.5_A1_T3 failing S15.9.5.5_A2_T1 failing S15.9.5.5_A3_T1 failing S15.9.5.5_A3_T2 failing -S15.9.5.6_A1_T3 failing S15.9.5.6_A2_T1 failing S15.9.5.6_A3_T1 failing S15.9.5.6_A3_T2 failing -S15.9.5.7_A1_T3 failing S15.9.5.7_A2_T1 failing S15.9.5.7_A3_T1 failing S15.9.5.7_A3_T2 failing -S15.9.5.8_A1_T3 failing S15.9.5.8_A2_T1 failing S15.9.5.8_A3_T1 failing S15.9.5.8_A3_T2 failing -S15.9.5.9_A1_T3 failing S15.9.5.9_A2_T1 failing S15.9.5.9_A3_T1 failing S15.9.5.9_A3_T2 failing @@ -4387,8 +4326,3 @@ S15.4.4.4_A1_T3 failing # Regression introduced by qtdeclarative parser changes S7.9_A5.7_T1 failing - -# Regression introduced by https://codereview.qt-project.org/#change,44302 -S15.1.2.2_A9.5 failing -S15.1.2.3_A7.5 failing - -- cgit v1.2.3 From 6cd333a3812e003619b1c7f85a439e21d3e62f94 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 9 Jan 2013 14:44:09 +0100 Subject: Fix length of arguments object Initialize the argumentCount correctly for functions that are called with more parameters than formally declared. This fixes a whole bunch of tests and also shows that we are failing on some other tests that use defineProperty on the arguments object. These tests were "silently" passing before because the arguments object had the wrong length / content. The next commit is going to fix those, but it is an unrelated change. Change-Id: Ia5197e2ea3ea9629e46a5eda1d2d3d63f0cca645 Reviewed-by: Lars Knoll Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 4 ++-- tests/TestExpectations | 45 ++++++++------------------------------------- 2 files changed, 10 insertions(+), 39 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 2edf2a892f..7b00db8ae3 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -407,12 +407,12 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha arguments = args; argumentCount = argc; if (function->needsActivation || argc < function->formalParameterCount){ - arguments = new Value[qMax(argc, function->formalParameterCount)]; + argumentCount = qMax(argc, function->formalParameterCount); + arguments = new Value[argumentCount]; if (argc) std::copy(args, args + argc, arguments); if (argc < function->formalParameterCount) std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); - argumentCount = function->formalParameterCount; } locals = function->varCount ? new Value[function->varCount] : 0; diff --git a/tests/TestExpectations b/tests/TestExpectations index 4344986530..7520c12ae1 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -15,10 +15,6 @@ S15.4.5.2_A3_T4 failing 15.4.4.15-5-16 failing 15.4.4.15-5-12 failing 15.4.4.14-9-9 failing -S8.8_A2_T1 failing -S8.8_A2_T2 failing -S8.8_A2_T3 failing -S10.1.6_A1_T2 failing S10.2.3_A1.1_T2 failing S10.2.3_A1.2_T2 failing S10.2.3_A1.3_T2 failing @@ -72,11 +68,7 @@ S10.2.3_A1.3_T2 failing 10.4.3-1-99gs failing 10.5-1-s failing 10.5-7-b-1-s failing -10.5-7-b-4-s failing -10.6-11-b-1 failing 10.6-6-4 failing -S10.6_A6 failing -S10.6_A7 failing 11.1.4_4-5-1 failing 11.1.4_5-6-1 failing S11.10.1_A2.4_T1 failing @@ -198,7 +190,6 @@ S11.2.3_A3_T3 failing S11.2.3_A3_T4 failing S11.2.3_A3_T5 failing S11.2.4_A1.1_T2 failing -S11.2.4_A1.2_T1 failing S11.2.4_A1.2_T2 failing 11.3.1-2-1-s failing 11.3.1-2-2-s failing @@ -400,17 +391,12 @@ S12.6.3_A12_T2 failing 13.0-7-s failing 13.0-8-s failing 13.0-9-s failing -S13_A11_T3 failing S13_A15_T4 failing S13_A17_T1 failing S13_A17_T2 failing -S13_A2_T2 failing -S13_A2_T3 failing S13_A3_T1 failing -S13_A4_T4 failing S13_A6_T2 failing S13_A8_T1 failing -S13_A8_T2 failing 13.1-10-s failing 13.1-11-s failing 13.1-12-s failing @@ -472,8 +458,6 @@ S13_A8_T2 failing 13.2-6-s failing 13.2-8-s failing 13.2-9-s failing -S13.2.1_A4_T3 failing -S13.2.1_A4_T4 failing S13.2.2_A16_T2 failing S13.2.2_A16_T3 failing S13.2.2_A19_T1 failing @@ -482,12 +466,8 @@ S13.2.2_A19_T4 failing S13.2.2_A19_T5 failing S13.2.2_A19_T6 failing S13.2.2_A19_T8 failing -S13.2.2_A5_T1 failing -S13.2.2_A5_T2 failing S13.2.2_A8_T3 failing S13.2.3_A1 failing -S13.2_A2_T1 failing -S13.2_A2_T2 failing S13.2_A3 failing S13.2_A4_T1 failing S13.2_A4_T2 failing @@ -1154,7 +1134,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-280 failing 15.2.3.6-4-291-1 failing 15.2.3.6-4-292-1 failing -15.2.3.6-4-293-1 failing 15.2.3.6-4-293-2 failing 15.2.3.6-4-293-3 failing 15.2.3.6-4-294-1 failing @@ -1532,12 +1511,10 @@ S15.2.4.7_A10 failing S15.2.4.7_A11 failing S15.2.4.7_A8 failing S15.2.4.7_A9 failing -S15.3.1_A1_T1 failing S15.3_A2_T1 failing S15.3_A2_T2 failing S15.3.2.1_A1_T13 failing S15.3.2.1_A1_T8 failing -S15.3.2.1_A1_T9 failing S15.3.2.1_A2_T1 failing S15.3.2.1_A2_T2 failing S15.3.2.1_A2_T3 failing @@ -2270,8 +2247,6 @@ S15.4.4.13_A5.7 failing 15.4.4.16-7-c-i-7 failing 15.4.4.16-7-c-i-8 failing 15.4.4.16-7-c-i-9 failing -15.4.4.16-7-c-ii-11 failing -15.4.4.16-7-c-ii-13 failing 15.4.4.16-7-c-ii-16 failing 15.4.4.16-7-c-ii-17 failing 15.4.4.16-7-c-ii-18 failing @@ -2414,8 +2389,6 @@ S15.4.4.13_A5.7 failing 15.4.4.17-7-c-i-7 failing 15.4.4.17-7-c-i-8 failing 15.4.4.17-7-c-i-9 failing -15.4.4.17-7-c-ii-11 failing -15.4.4.17-7-c-ii-13 failing 15.4.4.17-7-c-ii-16 failing 15.4.4.17-7-c-ii-17 failing 15.4.4.17-7-c-ii-18 failing @@ -2558,8 +2531,6 @@ S15.4.4.13_A5.7 failing 15.4.4.18-7-c-i-7 failing 15.4.4.18-7-c-i-8 failing 15.4.4.18-7-c-i-9 failing -15.4.4.18-7-c-ii-11 failing -15.4.4.18-7-c-ii-13 failing 15.4.4.18-7-c-ii-16 failing 15.4.4.18-7-c-ii-17 failing 15.4.4.18-7-c-ii-18 failing @@ -2696,8 +2667,6 @@ S15.4.4.13_A5.7 failing 15.4.4.19-8-c-i-7 failing 15.4.4.19-8-c-i-8 failing 15.4.4.19-8-c-i-9 failing -15.4.4.19-8-c-ii-11 failing -15.4.4.19-8-c-ii-13 failing 15.4.4.19-8-c-ii-16 failing 15.4.4.19-8-c-ii-17 failing 15.4.4.19-8-c-ii-18 failing @@ -2856,8 +2825,6 @@ S15.4.4.2_A4.7 failing 15.4.4.20-9-c-i-7 failing 15.4.4.20-9-c-i-8 failing 15.4.4.20-9-c-i-9 failing -15.4.4.20-9-c-ii-11 failing -15.4.4.20-9-c-ii-13 failing 15.4.4.20-9-c-ii-16 failing 15.4.4.20-9-c-ii-17 failing 15.4.4.20-9-c-ii-18 failing @@ -3129,8 +3096,6 @@ S15.4.4.2_A4.7 failing 15.4.4.21-9-c-i-7 failing 15.4.4.21-9-c-i-8 failing 15.4.4.21-9-c-i-9 failing -15.4.4.21-9-c-ii-12 failing -15.4.4.21-9-c-ii-14 failing 15.4.4.21-9-c-ii-16 failing 15.4.4.21-9-c-ii-18 failing 15.4.4.21-9-c-ii-20 failing @@ -3287,8 +3252,6 @@ S15.4.4.2_A4.7 failing 15.4.4.22-9-c-i-7 failing 15.4.4.22-9-c-i-8 failing 15.4.4.22-9-c-i-9 failing -15.4.4.22-9-c-ii-12 failing -15.4.4.22-9-c-ii-14 failing 15.4.4.22-9-c-ii-16 failing 15.4.4.22-9-c-ii-20 failing 15.4.4.22-9-c-ii-21 failing @@ -4326,3 +4289,11 @@ S15.4.4.4_A1_T3 failing # Regression introduced by qtdeclarative parser changes S7.9_A5.7_T1 failing + +# Bugs with defineProperty on the arguments object +15.2.3.6-4-291 failing +15.2.3.6-4-297 failing +15.2.3.6-4-298 failing +15.2.3.6-4-299 failing +15.2.3.6-4-300 failing +15.2.3.7-6-a-292 failing -- cgit v1.2.3 From 72b87fe6b3f2561684d32b100780d28eaf210741 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 9 Jan 2013 16:45:31 +0100 Subject: Fix properties in arguments objects for parameters According to the spec the parameters that are declared formally need to have a getter/setter in non-strict mode. But parameters beyond what's formally declared are just stored by value and dont' have a setter / getter. This fixes the "regressions" uncovered by the previous commit and a bunch of other tests that were marked as failing. Change-Id: Ib87b84a8a389914f01a95a00ecec452455b17376 Reviewed-by: Lars Knoll Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 2 +- qmljs_objects.cpp | 6 ++++-- qmljs_objects.h | 2 +- tests/TestExpectations | 9 --------- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 7b00db8ae3..286eb9103f 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -423,7 +423,7 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha withObject = 0; if (function->usesArgumentsObject) { - ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this); + ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this, function->formalParameterCount); args->prototype = engine->objectPrototype; Value arguments = Value::fromObject(args); createMutableBinding(engine->id_arguments, false); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index c7d134c678..dd307a8910 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -884,7 +884,7 @@ SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *m } -ArgumentsObject::ArgumentsObject(ExecutionContext *context) +ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount) : context(context) , currentIndex(-1) { @@ -904,8 +904,10 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context) PropertyDescriptor pd = PropertyDescriptor::fromAccessor(get, set); pd.configurable = PropertyDescriptor::Enabled; pd.enumberable = PropertyDescriptor::Enabled; - for (uint i = 0; i < context->argumentCount; ++i) + for (uint i = 0; i < formalParameterCount; ++i) __defineOwnProperty__(context, QString::number(i), &pd); + for (uint i = formalParameterCount; i < context->argumentCount; ++i) + Object::__put__(context, QString::number(i), context->arguments[i]); defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); } } diff --git a/qmljs_objects.h b/qmljs_objects.h index 60fb48a8a3..a5479625ad 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -399,7 +399,7 @@ struct URIErrorObject: ErrorObject { struct ArgumentsObject: Object { ExecutionContext *context; int currentIndex; - ArgumentsObject(ExecutionContext *context); + ArgumentsObject(ExecutionContext *context, int formalParameterCount); virtual QString className() { return QStringLiteral("Arguments"); } virtual ArgumentsObject *asArgumentsObject() { return this; } diff --git a/tests/TestExpectations b/tests/TestExpectations index 7520c12ae1..5c32aa2628 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -952,7 +952,6 @@ S15.1.3.2_A4_T4 failing S15.1.3.2_A5.1 failing S15.1.3.2_A5.2 failing S15.1.3.2_A5.3 failing -15.2.3.3-3-13 failing 15.2.3.3-3-14 failing 15.2.3.3-4-10 failing 15.2.3.3-4-11 failing @@ -1478,7 +1477,6 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-99 failing 15.2.3.8-0-2 failing 15.2.3.9-0-2 failing -15.2.3.9-2-a-11 failing 15.2.3.9-2-a-12 failing 15.2.3.9-2-a-14 failing 15.2.4.2-1-1 failing @@ -4290,10 +4288,3 @@ S15.4.4.4_A1_T3 failing # Regression introduced by qtdeclarative parser changes S7.9_A5.7_T1 failing -# Bugs with defineProperty on the arguments object -15.2.3.6-4-291 failing -15.2.3.6-4-297 failing -15.2.3.6-4-298 failing -15.2.3.6-4-299 failing -15.2.3.6-4-300 failing -15.2.3.7-6-a-292 failing -- cgit v1.2.3 From 3bab3821e443250d03a3abe74fceca703b32a0fa Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 10 Jan 2013 08:55:31 +0100 Subject: Fix length property of arguments object The length should reflect the number of parameters _actually_ passed to the function. Change-Id: Ib38541f2175be6cee5b4ccaf7a9bc32b21531972 Reviewed-by: Lars Knoll --- qmljs_environment.cpp | 2 +- qmljs_objects.cpp | 4 ++-- qmljs_objects.h | 2 +- tests/TestExpectations | 3 --- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 286eb9103f..745554f2bf 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -423,7 +423,7 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha withObject = 0; if (function->usesArgumentsObject) { - ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this, function->formalParameterCount); + ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this, function->formalParameterCount, argc); args->prototype = engine->objectPrototype; Value arguments = Value::fromObject(args); createMutableBinding(engine->id_arguments, false); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index dd307a8910..a387ca74c4 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -884,11 +884,11 @@ SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *m } -ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount) +ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount) : context(context) , currentIndex(-1) { - defineDefaultProperty(context->engine->id_length, Value::fromInt32(context->argumentCount)); + defineDefaultProperty(context->engine->id_length, Value::fromInt32(actualParameterCount)); if (context->strictMode) { for (uint i = 0; i < context->argumentCount; ++i) Object::__put__(context, QString::number(i), context->arguments[i]); diff --git a/qmljs_objects.h b/qmljs_objects.h index a5479625ad..dfc2fe432a 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -399,7 +399,7 @@ struct URIErrorObject: ErrorObject { struct ArgumentsObject: Object { ExecutionContext *context; int currentIndex; - ArgumentsObject(ExecutionContext *context, int formalParameterCount); + ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount); virtual QString className() { return QStringLiteral("Arguments"); } virtual ArgumentsObject *asArgumentsObject() { return this; } diff --git a/tests/TestExpectations b/tests/TestExpectations index 5c32aa2628..cd4e7ed6e5 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -68,7 +68,6 @@ S10.2.3_A1.3_T2 failing 10.4.3-1-99gs failing 10.5-1-s failing 10.5-7-b-1-s failing -10.6-6-4 failing 11.1.4_4-5-1 failing 11.1.4_5-6-1 failing S11.10.1_A2.4_T1 failing @@ -396,7 +395,6 @@ S13_A17_T1 failing S13_A17_T2 failing S13_A3_T1 failing S13_A6_T2 failing -S13_A8_T1 failing 13.1-10-s failing 13.1-11-s failing 13.1-12-s failing @@ -4287,4 +4285,3 @@ S15.4.4.4_A1_T3 failing # Regression introduced by qtdeclarative parser changes S7.9_A5.7_T1 failing - -- cgit v1.2.3 From c7c856921481a36723eef8af9500c7adab6be2d5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 10 Jan 2013 09:03:56 +0100 Subject: Simplify test262.py work-flow Make test262.py work by default again like the original tool with regards to TestExpectations, i.e. not use them at all. Instead make it an option that is now passed via "make check". So after a change in code we run "make check" to check for any regressions or fixes. If there are any regressions, then we call tests/test262.py to debug it and by not reading TestExpectations by default the test will fail. Change-Id: I00b43c5d09c17c296dfa958293f769663ef49de1 Reviewed-by: Lars Knoll --- tests/test262.py | 15 ++++++++++----- v4.pro | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/test262.py b/tests/test262.py index e8df475649..7f958086f7 100755 --- a/tests/test262.py +++ b/tests/test262.py @@ -55,10 +55,12 @@ def ReportError(s): class TestExpectations: - def __init__(self): - f = open(rootDir + "/TestExpectations") + def __init__(self, enabled): self.testsToSkip = [] self.failingTests = [] + f = open(rootDir + "/TestExpectations") + if not enabled: + return for line in f.read().splitlines(): line = line.strip() if len(line) == 0 or line[0] == "#": @@ -126,6 +128,8 @@ def BuildOptions(): help="Test only non-strict mode") result.add_option("--parallel", default=False, action="store_true", help="Run tests in parallel") + result.add_option("--with-test-expectations", default=False, action="store_true", + help="Parse TestExpectations to deal with tests known to fail") result.add_option("--update-expectations", default=False, action="store_true", help="Update test expectations fail when a test passes that was expected to fail") # TODO: Once enough tests are made strict compat, change the default @@ -369,7 +373,7 @@ def MakePlural(n): class TestSuite(object): - def __init__(self, root, strict_only, non_strict_only, unmarked_default): + def __init__(self, root, strict_only, non_strict_only, unmarked_default, load_expectations): # TODO: derive from packagerConfig.py self.test_root = path.join(root, 'test', 'suite') self.lib_root = path.join(root, 'test', 'harness') @@ -377,7 +381,7 @@ class TestSuite(object): self.non_strict_only = non_strict_only self.unmarked_default = unmarked_default self.include_cache = { } - self.expectations = TestExpectations() + self.expectations = TestExpectations(load_expectations) def Validate(self): if not path.exists(self.test_root): @@ -525,7 +529,8 @@ def Main(): test_suite = TestSuite(options.tests, options.strict_only, options.non_strict_only, - options.unmarked_default) + options.unmarked_default, + options.with_test_expectations) test_suite.Validate() if options.cat: test_suite.Print(args) diff --git a/v4.pro b/v4.pro index e947da90bb..75da88a9b7 100644 --- a/v4.pro +++ b/v4.pro @@ -87,7 +87,7 @@ DEFINES += QMLJS_NO_LLVM } checktarget.target = check -checktarget.commands = python tests/test262.py --parallel --update-expectations +checktarget.commands = python tests/test262.py --parallel --with-test-expectations --update-expectations checktarget.depends = all QMAKE_EXTRA_TARGETS += checktarget -- cgit v1.2.3 From 4e9f33fe28430f795718a61344d6df2adb51cd60 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 10 Jan 2013 16:06:43 +0100 Subject: Define the length property correctly for built-in functions It corresponds to the number of formally declared arguments. Fixes 310 tests :) Change-Id: I826edb3002e279a95e03579483fcb08927b86629 Reviewed-by: Lars Knoll --- qmljs_objects.cpp | 16 ++- qmljs_objects.h | 1 + qv4ecmaobjects.cpp | 88 +++++++------- tests/TestExpectations | 311 +------------------------------------------------ 4 files changed, 57 insertions(+), 359 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index a387ca74c4..d5ac054da3 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -119,19 +119,25 @@ void Object::defineDefaultProperty(ExecutionContext *context, const QString &nam defineDefaultProperty(context->engine->identifier(name), value); } -void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int count) +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int argumentCount) { - Q_UNUSED(count); - // ### FIX count + Q_UNUSED(argumentCount); String *s = context->engine->identifier(name); - defineDefaultProperty(s, Value::fromObject(context->engine->newNativeFunction(context, s, code))); + FunctionObject* function = context->engine->newNativeFunction(context, s, code); + function->defineReadonlyProperty(context->engine, context->engine->id_length, Value::fromInt32(argumentCount)); + defineDefaultProperty(s, Value::fromObject(function)); } void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value) +{ + defineReadonlyProperty(engine, engine->identifier(name), value); +} + +void Object::defineReadonlyProperty(ExecutionEngine *engine, String *name, Value value) { if (!members) members.reset(new PropertyTable()); - PropertyDescriptor *pd = members->insert(engine->identifier(name)); + PropertyDescriptor *pd = members->insert(name); pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Disabled; pd->enumberable = PropertyDescriptor::Disabled; diff --git a/qmljs_objects.h b/qmljs_objects.h index dfc2fe432a..91e4e56384 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -146,6 +146,7 @@ struct Object: Managed { void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int count = 0); /* Fixed: Writable: false, Enumerable: false, Configurable: false */ void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value); + void defineReadonlyProperty(ExecutionEngine *engine, String *name, Value value); protected: virtual void getCollectables(QVector &objects); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index fd3383a258..0f06bbddaf 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -555,27 +555,27 @@ Value ObjectCtor::__get__(ExecutionContext *ctx, String *name, bool *hasProperty void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 0); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 0); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 0); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("create"), method_create, 0); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 0); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 0); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("seal"), method_seal, 0); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("freeze"), method_freeze, 0); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 0); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 0); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 0); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 0); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("keys"), method_keys, 0); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("create"), method_create, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 3); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("seal"), method_seal, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("freeze"), method_freeze, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("keys"), method_keys, 1); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); - defineDefaultProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 0); - defineDefaultProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 0); - defineDefaultProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 0); + defineDefaultProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1); + defineDefaultProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1); + defineDefaultProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1); defineDefaultProperty(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 0); defineDefaultProperty(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 0); } @@ -1017,24 +1017,24 @@ Value StringCtor::call(ExecutionContext *ctx) void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode, 1); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); - defineDefaultProperty(ctx, QStringLiteral("charAt"), method_charAt); - defineDefaultProperty(ctx, QStringLiteral("charCodeAt"), method_charCodeAt); - defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat); - defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf); - defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf); - defineDefaultProperty(ctx, QStringLiteral("localeCompare"), method_localeCompare); - defineDefaultProperty(ctx, QStringLiteral("match"), method_match); - defineDefaultProperty(ctx, QStringLiteral("replace"), method_replace); - defineDefaultProperty(ctx, QStringLiteral("search"), method_search); - defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice); - defineDefaultProperty(ctx, QStringLiteral("split"), method_split); - defineDefaultProperty(ctx, QStringLiteral("substr"), method_substr); - defineDefaultProperty(ctx, QStringLiteral("substring"), method_substring); + defineDefaultProperty(ctx, QStringLiteral("charAt"), method_charAt, 1); + defineDefaultProperty(ctx, QStringLiteral("charCodeAt"), method_charCodeAt, 1); + defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("localeCompare"), method_localeCompare, 1); + defineDefaultProperty(ctx, QStringLiteral("match"), method_match, 1); + defineDefaultProperty(ctx, QStringLiteral("replace"), method_replace, 2); + defineDefaultProperty(ctx, QStringLiteral("search"), method_search, 1); + defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); + defineDefaultProperty(ctx, QStringLiteral("split"), method_split, 2); + defineDefaultProperty(ctx, QStringLiteral("substr"), method_substr, 2); + defineDefaultProperty(ctx, QStringLiteral("substring"), method_substring, 2); defineDefaultProperty(ctx, QStringLiteral("toLowerCase"), method_toLowerCase); defineDefaultProperty(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); defineDefaultProperty(ctx, QStringLiteral("toUpperCase"), method_toUpperCase); @@ -1345,7 +1345,7 @@ void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); defineDefaultProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString); defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); - defineDefaultProperty(ctx, QStringLiteral("toFixed"), method_toFixed); + defineDefaultProperty(ctx, QStringLiteral("toFixed"), method_toFixed, 1); defineDefaultProperty(ctx, QStringLiteral("toExponential"), method_toExponential); defineDefaultProperty(ctx, QStringLiteral("toPrecision"), method_toPrecision); } @@ -1575,15 +1575,15 @@ void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) defineDefaultProperty(ctx, QStringLiteral("sort"), method_sort, 1); defineDefaultProperty(ctx, QStringLiteral("splice"), method_splice, 2); defineDefaultProperty(ctx, QStringLiteral("unshift"), method_unshift, 1); - defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 0); - defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 0); - defineDefaultProperty(ctx, QStringLiteral("every"), method_every, 0); - defineDefaultProperty(ctx, QStringLiteral("some"), method_some, 0); - defineDefaultProperty(ctx, QStringLiteral("forEach"), method_forEach, 0); - defineDefaultProperty(ctx, QStringLiteral("map"), method_map, 0); - defineDefaultProperty(ctx, QStringLiteral("filter"), method_filter, 0); - defineDefaultProperty(ctx, QStringLiteral("reduce"), method_reduce, 0); - defineDefaultProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 0); + defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("every"), method_every, 1); + defineDefaultProperty(ctx, QStringLiteral("some"), method_some, 1); + defineDefaultProperty(ctx, QStringLiteral("forEach"), method_forEach, 1); + defineDefaultProperty(ctx, QStringLiteral("map"), method_map, 1); + defineDefaultProperty(ctx, QStringLiteral("filter"), method_filter, 1); + defineDefaultProperty(ctx, QStringLiteral("reduce"), method_reduce, 1); + defineDefaultProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 1); } Value ArrayPrototype::method_toString(ExecutionContext *ctx) @@ -2104,8 +2104,8 @@ void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 0); - defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 0); - defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 0); + defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 1); + defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 1); } Value FunctionPrototype::method_toString(ExecutionContext *ctx) @@ -2822,8 +2822,8 @@ void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 0); - defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 0); + defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 1); + defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 1); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); } diff --git a/tests/TestExpectations b/tests/TestExpectations index cd4e7ed6e5..9690768efb 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -613,8 +613,6 @@ S15.10.4.1_A9_T3 failing S15.10.5.1_A3 failing S15.10.5.1_A4 failing S15.10.5_A1 failing -S15.10.6.2_A10 failing -S15.10.6.2_A11 failing S15.10.6.2_A1_T2 failing S15.10.6.2_A1_T6 failing S15.10.6.2_A4_T1 failing @@ -632,16 +630,6 @@ S15.10.6.2_A4_T9 failing S15.10.6.2_A5_T1 failing S15.10.6.2_A5_T2 failing S15.10.6.2_A5_T3 failing -S15.10.6.2_A8 failing -S15.10.6.2_A9 failing -S15.10.6.3_A10 failing -S15.10.6.3_A11 failing -S15.10.6.3_A8 failing -S15.10.6.3_A9 failing -S15.10.6.4_A10 failing -S15.10.6.4_A11 failing -S15.10.6.4_A8 failing -S15.10.6.4_A9 failing S15.10.7_A2_T1 failing 15.10.7.1-2 failing S15.10.7.1_A10 failing @@ -761,11 +749,8 @@ S15.2.3_A3 failing S15.2.3.1_A1 failing S15.2.3.1_A2 failing S15.2.3.1_A3 failing -15.2.3.10-0-2 failing 15.2.3.10-3-12 failing -15.2.3.11-0-2 failing 15.2.3.11-4-27 failing -15.2.3.12-0-2 failing 15.12.3-11-2 failing 15.12.3-11-26 failing 15.12.3-11-3 failing @@ -859,9 +844,7 @@ S15.10.1_A1_T1 failing S15.10.1_A1_T10 failing S15.10.1_A1_T11 failing 15.2.3.12-3-27 failing -15.2.3.13-0-2 failing 15.2.3.13-2-12 failing -15.2.3.14-0-2 failing 15.2.3.14-2-1 failing 15.2.3.14-3-2 failing 15.2.3.14-3-3 failing @@ -876,7 +859,6 @@ S15.10.1_A1_T11 failing 15.2.3.14-5-a-2 failing 15.2.3.14-5-a-3 failing 15.2.3.14-5-a-4 failing -15.2.3.2-0-2 failing 15.2.3.2-2-12 failing 15.2.3.2-2-13 failing 15.2.3.2-2-14 failing @@ -886,7 +868,6 @@ S15.10.1_A1_T11 failing 15.2.3.2-2-18 failing 15.2.3.2-2-3 failing 15.2.3.2-2-31 failing -15.2.3.3-0-2 failing S15.1.3.1_A2.2_T1 failing S15.1.3.1_A2.3_T1 failing S15.1.3.1_A2.4_T1 failing @@ -989,7 +970,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-82 failing 15.2.3.3-4-9 failing 15.2.3.3-4-90 failing -15.2.3.4-0-2 failing 15.2.3.4-2-1 failing 15.2.3.4-3-1 failing 15.2.3.4-4-2 failing @@ -1003,7 +983,6 @@ S15.1.3.2_A5.3 failing 15.2.3.4-4-b-3 failing 15.2.3.4-4-b-5 failing 15.2.3.4-4-b-6 failing -15.2.3.5-0-2 failing 15.2.3.5-1-2 failing 15.2.3.5-4-120 failing 15.2.3.5-4-13 failing @@ -1015,7 +994,6 @@ S15.1.3.2_A5.3 failing 15.2.3.5-4-252 failing 15.2.3.5-4-67 failing 15.2.3.5-4-92 failing -15.2.3.6-0-2 failing 15.2.3.6-2-17-1 failing 15.2.3.5-4-287 failing 15.2.3.5-4-315 failing @@ -1251,7 +1229,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-82-7 failing 15.2.3.6-4-82-8 failing 15.2.3.6-4-82-9 failing -15.2.3.7-0-2 failing 15.2.3.7-2-14 failing 15.2.3.7-2-15 failing 15.2.3.7-5-a-15 failing @@ -1473,40 +1450,14 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-97 failing 15.2.3.7-6-a-98 failing 15.2.3.7-6-a-99 failing -15.2.3.8-0-2 failing -15.2.3.9-0-2 failing 15.2.3.9-2-a-12 failing 15.2.3.9-2-a-14 failing 15.2.4.2-1-1 failing 15.2.4.2-1-2 failing 15.2.4.2-2-1 failing 15.2.4.2-2-2 failing -S15.2.4.2_A10 failing -S15.2.4.2_A11 failing S15.2.4.2_A12 failing S15.2.4.2_A13 failing -S15.2.4.2_A8 failing -S15.2.4.2_A9 failing -S15.2.4.3_A10 failing -S15.2.4.3_A11 failing -S15.2.4.3_A8 failing -S15.2.4.3_A9 failing -S15.2.4.4_A10 failing -S15.2.4.4_A11 failing -S15.2.4.4_A8 failing -S15.2.4.4_A9 failing -S15.2.4.5_A10 failing -S15.2.4.5_A11 failing -S15.2.4.5_A8 failing -S15.2.4.5_A9 failing -S15.2.4.6_A10 failing -S15.2.4.6_A11 failing -S15.2.4.6_A8 failing -S15.2.4.6_A9 failing -S15.2.4.7_A10 failing -S15.2.4.7_A11 failing -S15.2.4.7_A8 failing -S15.2.4.7_A9 failing S15.3_A2_T1 failing S15.3_A2_T2 failing S15.3.2.1_A1_T13 failing @@ -1530,15 +1481,9 @@ S15.3.3_A3 failing S15.3.3.1_A1 failing S15.3.3.1_A3 failing 15.3.3.2-1 failing -S15.3.4.2_A10 failing -S15.3.4.2_A11 failing -S15.3.4.2_A8 failing -S15.3.4.2_A9 failing 15.3.4.3-1-s failing 15.3.4.3-2-s failing 15.3.4.3-3-s failing -S15.3.4.3_A10 failing -S15.3.4.3_A11 failing S15.3.4.3_A2_T1 failing S15.3.4.3_A2_T2 failing S15.3.4.3_A3_T1 failing @@ -1564,14 +1509,9 @@ S15.3.4.3_A7_T7 failing S15.3.4.3_A7_T8 failing S15.3.4.3_A7_T9 failing S15.3.4.3_A8_T6 failing -S15.3.4.3_A9 failing 15.3.4.4-1-s failing 15.3.4.4-2-s failing 15.3.4.4-3-s failing -S15.3.4.4_A10 failing -S15.3.4.4_A11 failing -S15.3.4.4_A2_T1 failing -S15.3.4.4_A2_T2 failing S15.3.4.4_A6_T1 failing S15.3.4.4_A6_T10 failing S15.3.4.4_A6_T2 failing @@ -1582,8 +1522,6 @@ S15.3.4.4_A6_T6 failing S15.3.4.4_A6_T7 failing S15.3.4.4_A6_T8 failing S15.3.4.4_A6_T9 failing -S15.3.4.4_A9 failing -15.3.4.5-0-2 failing 15.3.4.5-10-1 failing 15.3.4.5-11-1 failing 15.3.4.5-13.b-1 failing @@ -1759,9 +1697,6 @@ S15.4.4.10_A2.2_T5 failing S15.4.4.10_A3_T3 failing S15.4.4.10_A4_T1 failing S15.4.4.10_A5.1 failing -S15.4.4.10_A5.2 failing -S15.4.4.10_A5.3 failing -S15.4.4.10_A5.4 failing S15.4.4.10_A5.7 failing S15.4.4.11_A3_T1 failing S15.4.4.11_A3_T2 failing @@ -1770,9 +1705,6 @@ S15.4.4.11_A4_T2 failing S15.4.4.11_A4_T3 failing S15.4.4.11_A6_T2 failing S15.4.4.11_A7.1 failing -S15.4.4.11_A7.2 failing -S15.4.4.11_A7.3 failing -S15.4.4.11_A7.4 failing S15.4.4.11_A7.7 failing 15.4.4.12-9-c-ii-1 failing S15.4.4.12_A1.1_T4 failing @@ -1792,9 +1724,6 @@ S15.4.4.12_A4_T1 failing S15.4.4.12_A4_T2 failing S15.4.4.12_A4_T3 failing S15.4.4.12_A5.1 failing -S15.4.4.12_A5.2 failing -S15.4.4.12_A5.3 failing -S15.4.4.12_A5.4 failing S15.4.4.12_A5.7 failing S15.4.4.13_A2_T1 failing S15.4.4.13_A2_T2 failing @@ -1805,11 +1734,7 @@ S15.4.4.13_A3_T3 failing S15.4.4.13_A4_T1 failing S15.4.4.13_A4_T2 failing S15.4.4.13_A5.1 failing -S15.4.4.13_A5.2 failing -S15.4.4.13_A5.3 failing -S15.4.4.13_A5.4 failing S15.4.4.13_A5.7 failing -15.4.4.14-0-2 failing 15.4.4.14-1-1 failing 15.4.4.14-1-10 failing 15.4.4.14-1-11 failing @@ -1938,7 +1863,6 @@ S15.4.4.13_A5.7 failing 15.4.4.14-9-b-i-9 failing 15.4.4.14-9-b-ii-2 failing 15.4.4.14-9-b-ii-5 failing -15.4.4.15-0-2 failing 15.4.4.15-1-1 failing 15.4.4.15-1-10 failing 15.4.4.15-1-11 failing @@ -2127,7 +2051,6 @@ S15.4.4.13_A5.7 failing 15.4.4.15-8-b-iii-2 failing 15.4.4.15-9-1 failing 15.4.4.15-9-2 failing -15.4.4.16-0-2 failing 15.4.4.16-1-1 failing 15.4.4.16-1-10 failing 15.4.4.16-1-11 failing @@ -2269,7 +2192,6 @@ S15.4.4.13_A5.7 failing 15.4.4.16-8-6 failing 15.4.4.16-8-7 failing 15.4.4.16-8-8 failing -15.4.4.17-0-2 failing 15.4.4.17-1-1 failing 15.4.4.17-1-10 failing 15.4.4.17-1-11 failing @@ -2410,7 +2332,6 @@ S15.4.4.13_A5.7 failing 15.4.4.17-8-6 failing 15.4.4.17-8-7 failing 15.4.4.17-8-8 failing -15.4.4.18-0-2 failing 15.4.4.18-1-1 failing 15.4.4.18-1-10 failing 15.4.4.18-1-11 failing @@ -2547,7 +2468,6 @@ S15.4.4.13_A5.7 failing 15.4.4.18-8-7 failing 15.4.4.18-8-8 failing 15.4.4.18-8-9 failing -15.4.4.19-0-2 failing 15.4.4.19-1-1 failing 15.4.4.19-1-10 failing 15.4.4.19-1-11 failing @@ -2690,11 +2610,7 @@ S15.4.4.13_A5.7 failing 15.4.4.19-9-9 failing S15.4.4.2_A1_T1 failing S15.4.4.2_A3_T1 failing -S15.4.4.2_A4.2 failing -S15.4.4.2_A4.3 failing -S15.4.4.2_A4.4 failing S15.4.4.2_A4.7 failing -15.4.4.20-0-2 failing 15.4.4.20-1-1 failing 15.4.4.20-1-10 failing 15.4.4.20-1-11 failing @@ -2843,7 +2759,6 @@ S15.4.4.2_A4.7 failing 15.4.4.20-9-c-iii-3 failing 15.4.4.20-9-c-iii-4 failing 15.4.4.20-9-c-iii-5 failing -15.4.4.21-0-2 failing 15.4.4.21-1-1 failing 15.4.4.21-1-10 failing 15.4.4.21-1-11 failing @@ -3115,7 +3030,6 @@ S15.4.4.2_A4.7 failing 15.4.4.21-9-c-ii-5 failing 15.4.4.21-9-c-ii-8 failing 15.4.4.21-9-c-ii-9 failing -15.4.4.22-0-2 failing 15.4.4.22-1-1 failing 15.4.4.22-1-10 failing 15.4.4.22-1-11 failing @@ -3271,9 +3185,6 @@ S15.4.4.2_A4.7 failing 15.4.4.22-9-c-ii-8 failing S15.4.4.3_A1_T1 failing S15.4.4.3_A3_T1 failing -S15.4.4.3_A4.2 failing -S15.4.4.3_A4.3 failing -S15.4.4.3_A4.4 failing S15.4.4.3_A4.7 failing 15.4.4.4-5-b-iii-3-b-1 failing 15.4.4.4-5-c-i-1 failing @@ -3282,16 +3193,10 @@ S15.4.4.4_A2_T1 failing S15.4.4.4_A2_T2 failing S15.4.4.4_A3_T1 failing S15.4.4.4_A4.1 failing -S15.4.4.4_A4.2 failing -S15.4.4.4_A4.3 failing -S15.4.4.4_A4.4 failing S15.4.4.4_A4.7 failing S15.4.4.5_A1.1_T1 failing S15.4.4.5_A5_T1 failing S15.4.4.5_A6.1 failing -S15.4.4.5_A6.2 failing -S15.4.4.5_A6.3 failing -S15.4.4.5_A6.4 failing S15.4.4.5_A6.7 failing S15.4.4.6_A1.1_T1 failing S15.4.4.6_A1.2_T1 failing @@ -3300,25 +3205,15 @@ S15.4.4.6_A3_T3 failing S15.4.4.6_A4_T1 failing S15.4.4.6_A4_T2 failing S15.4.4.6_A5.1 failing -S15.4.4.6_A5.2 failing -S15.4.4.6_A5.3 failing -S15.4.4.6_A5.4 failing S15.4.4.6_A5.7 failing S15.4.4.7_A3 failing S15.4.4.7_A4_T2 failing S15.4.4.7_A4_T3 failing S15.4.4.7_A6.1 failing -S15.4.4.7_A6.2 failing -S15.4.4.7_A6.3 failing -S15.4.4.7_A6.4 failing S15.5.3.1_A3 failing S15.5.3.1_A4 failing -S15.5.3.2_A1 failing S15.5.3.2_A4 failing S15.5.3_A1 failing -S15.5.4.2_A4_T1 failing -S15.5.4.10_A10 failing -S15.5.4.10_A11 failing S15.5.4.10_A1_T1 failing S15.5.4.10_A1_T10 failing S15.5.4.10_A1_T11 failing @@ -3351,11 +3246,7 @@ S15.5.4.10_A2_T6 failing S15.5.4.10_A2_T7 failing S15.5.4.10_A2_T8 failing S15.5.4.10_A2_T9 failing -S15.5.4.10_A8 failing -S15.5.4.10_A9 failing 15.5.4.11-1 failing -S15.5.4.11_A10 failing -S15.5.4.11_A11 failing S15.5.4.11_A12 failing S15.5.4.11_A1_T1 failing S15.5.4.11_A1_T10 failing @@ -3391,11 +3282,7 @@ S15.5.4.11_A4_T2 failing S15.5.4.11_A4_T3 failing S15.5.4.11_A4_T4 failing S15.5.4.11_A5_T1 failing -S15.5.4.11_A8 failing -S15.5.4.11_A9 failing S15.5.4.12_A1.1_T1 failing -S15.5.4.12_A10 failing -S15.5.4.12_A11 failing S15.5.4.12_A1_T1 failing S15.5.4.12_A1_T10 failing S15.5.4.12_A1_T11 failing @@ -3419,10 +3306,6 @@ S15.5.4.12_A2_T7 failing S15.5.4.12_A3_T1 failing S15.5.4.12_A3_T2 failing S15.5.4.12_A7 failing -S15.5.4.12_A8 failing -S15.5.4.12_A9 failing -S15.5.4.13_A10 failing -S15.5.4.13_A11 failing S15.5.4.13_A1_T1 failing S15.5.4.13_A1_T10 failing S15.5.4.13_A1_T11 failing @@ -3442,9 +3325,6 @@ S15.4.4.8_A3_T3 failing S15.4.4.8_A4_T1 failing S15.4.4.8_A4_T2 failing S15.4.4.8_A5.1 failing -S15.4.4.8_A5.2 failing -S15.4.4.8_A5.3 failing -S15.4.4.8_A5.4 failing S15.4.4.8_A5.7 failing S15.4.4.9_A1.1_T1 failing S15.4.4.9_A1.2_T1 failing @@ -3459,9 +3339,6 @@ S15.4.4.9_A3_T3 failing S15.4.4.9_A4_T1 failing S15.4.4.9_A4_T2 failing S15.4.4.9_A5.1 failing -S15.4.4.9_A5.2 failing -S15.4.4.9_A5.3 failing -S15.4.4.9_A5.4 failing S15.4.4.9_A5.7 failing 15.4.5.1-3.d-1 failing 15.4.5.1-3.d-2 failing @@ -3484,10 +3361,6 @@ S15.5.4.13_A3_T1 failing S15.5.4.13_A3_T2 failing S15.5.4.13_A3_T3 failing S15.5.4.13_A3_T4 failing -S15.5.4.13_A8 failing -S15.5.4.13_A9 failing -S15.5.4.14_A10 failing -S15.5.4.14_A11 failing S15.5.4.14_A1_T1 failing S15.5.4.14_A1_T10 failing S15.5.4.14_A1_T11 failing @@ -3585,10 +3458,6 @@ S15.5.4.14_A4_T6 failing S15.5.4.14_A4_T7 failing S15.5.4.14_A4_T8 failing S15.5.4.14_A4_T9 failing -S15.5.4.14_A8 failing -S15.5.4.14_A9 failing -S15.5.4.15_A10 failing -S15.5.4.15_A11 failing S15.5.4.15_A1_T1 failing S15.5.4.15_A1_T10 failing S15.5.4.15_A1_T11 failing @@ -3611,10 +3480,6 @@ S15.5.4.15_A3_T6 failing S15.5.4.15_A3_T7 failing S15.5.4.15_A3_T8 failing S15.5.4.15_A3_T9 failing -S15.5.4.15_A8 failing -S15.5.4.15_A9 failing -S15.5.4.16_A10 failing -S15.5.4.16_A11 failing S15.5.4.16_A1_T1 failing S15.5.4.16_A1_T10 failing S15.5.4.16_A1_T11 failing @@ -3625,10 +3490,6 @@ S15.5.4.16_A1_T2 failing S15.5.4.16_A1_T6 failing S15.5.4.16_A1_T7 failing S15.5.4.16_A1_T8 failing -S15.5.4.16_A8 failing -S15.5.4.16_A9 failing -S15.5.4.17_A10 failing -S15.5.4.17_A11 failing S15.5.4.17_A1_T1 failing S15.5.4.17_A1_T10 failing S15.5.4.17_A1_T11 failing @@ -3639,10 +3500,6 @@ S15.5.4.17_A1_T2 failing S15.5.4.17_A1_T6 failing S15.5.4.17_A1_T7 failing S15.5.4.17_A1_T8 failing -S15.5.4.17_A8 failing -S15.5.4.17_A9 failing -S15.5.4.18_A10 failing -S15.5.4.18_A11 failing S15.5.4.18_A1_T1 failing S15.5.4.18_A1_T10 failing S15.5.4.18_A1_T11 failing @@ -3653,10 +3510,6 @@ S15.5.4.18_A1_T2 failing S15.5.4.18_A1_T6 failing S15.5.4.18_A1_T7 failing S15.5.4.18_A1_T8 failing -S15.5.4.18_A8 failing -S15.5.4.18_A9 failing -S15.5.4.19_A10 failing -S15.5.4.19_A11 failing S15.5.4.19_A1_T1 failing S15.5.4.19_A1_T10 failing S15.5.4.19_A1_T11 failing @@ -3667,8 +3520,6 @@ S15.5.4.19_A1_T2 failing S15.5.4.19_A1_T6 failing S15.5.4.19_A1_T7 failing S15.5.4.19_A1_T8 failing -S15.5.4.19_A8 failing -S15.5.4.19_A9 failing 15.5.4.20-0-1 failing 15.5.4.20-0-2 failing 15.5.4.20-1-3 failing @@ -3744,30 +3595,18 @@ S15.5.4.19_A9 failing 15.5.4.20-3-9 failing 15.5.4.20-4-1 failing S15.5.4.4_A5 failing -S15.5.4.4_A8 failing -S15.5.4.4_A9 failing S15.5.4.5_A1.1 failing -S15.5.4.5_A10 failing -S15.5.4.5_A11 failing S15.5.4.5_A1_T1 failing S15.5.4.5_A1_T10 failing S15.5.4.5_A1_T2 failing S15.5.4.5_A2 failing S15.5.4.5_A4 failing -S15.5.4.5_A8 failing -S15.5.4.5_A9 failing -S15.5.4.6_A10 failing -S15.5.4.6_A11 failing S15.5.4.6_A1_T1 failing S15.5.4.6_A1_T10 failing S15.5.4.6_A1_T2 failing S15.5.4.6_A2 failing S15.5.4.6_A4_T1 failing S15.5.4.6_A4_T2 failing -S15.5.4.6_A8 failing -S15.5.4.6_A9 failing -S15.5.4.7_A10 failing -S15.5.4.7_A11 failing S15.5.4.7_A1_T1 failing S15.5.4.7_A1_T10 failing S15.5.4.7_A1_T11 failing @@ -3823,8 +3662,6 @@ S15.5.4.7_A1_T2 failing 15.5.4.20-4-60 failing 15.5.4.20-4-8 failing S15.5.4.4_A1.1 failing -S15.5.4.4_A10 failing -S15.5.4.4_A11 failing S15.5.4.4_A1_T1 failing S15.5.4.4_A1_T10 failing S15.5.4.4_A1_T2 failing @@ -3833,10 +3670,6 @@ S15.5.4.7_A4_T1 failing S15.5.4.7_A4_T2 failing S15.5.4.7_A4_T4 failing S15.5.4.7_A4_T5 failing -S15.5.4.7_A8 failing -S15.5.4.7_A9 failing -S15.5.4.8_A10 failing -S15.5.4.8_A11 failing S15.5.4.8_A1_T1 failing S15.5.4.8_A1_T10 failing S15.5.4.8_A1_T12 failing @@ -3846,12 +3679,6 @@ S15.5.4.8_A4_T1 failing S15.5.4.8_A4_T2 failing S15.5.4.8_A4_T4 failing S15.5.4.8_A4_T5 failing -S15.5.4.8_A8 failing -S15.5.4.8_A9 failing -S15.5.4.9_A10 failing -S15.5.4.9_A11 failing -S15.5.4.9_A8 failing -S15.5.4.9_A9 failing S15.5.5.1_A1 failing S15.5.5.1_A2 failing S15.5.5.1_A3 failing @@ -3871,20 +3698,11 @@ S15.7.4_A3.3 failing S15.7.4.5_A1.3_T01 failing S15.7.4.5_A1.3_T02 failing S15.7.4.5_A1.4_T01 failing -S15.7.4.5_A2_T01 failing -S15.8.2.11_A4 failing -S15.8.2.12_A4 failing S15.8.2.16_A4 failing S15.8.2.16_A5 failing S15.8.2.17_A2 failing S15.9.4.1_A1_T1 failing S15.9.4.1_A1_T2 failing -S15.9.4.2_A2_T1 failing -S15.9.4.2_A3_T1 failing -S15.9.4.2_A3_T2 failing -S15.9.4.3_A2_T1 failing -S15.9.4.3_A3_T1 failing -S15.9.4.3_A3_T2 failing 15.9.4.4-0-1 failing 15.9.4.4-0-2 failing 15.9.4.4-0-3 failing @@ -3905,114 +3723,7 @@ S15.8.2.7_A5 failing S15.9.5.1_A2_T1 failing S15.9.5.1_A3_T1 failing S15.9.5.1_A3_T2 failing -S15.9.5.10_A2_T1 failing -S15.9.5.10_A3_T1 failing -S15.9.5.10_A3_T2 failing -S15.9.5.11_A2_T1 failing -S15.9.5.11_A3_T1 failing -S15.9.5.11_A3_T2 failing -S15.9.5.12_A2_T1 failing -S15.9.5.12_A3_T1 failing -S15.9.5.12_A3_T2 failing -S15.9.5.13_A2_T1 failing -S15.9.5.13_A3_T1 failing -S15.9.5.13_A3_T2 failing -S15.9.5.14_A2_T1 failing -S15.9.5.14_A3_T1 failing -S15.9.5.14_A3_T2 failing -S15.9.5.15_A2_T1 failing -S15.9.5.15_A3_T1 failing -S15.9.5.15_A3_T2 failing -S15.9.5.16_A2_T1 failing -S15.9.5.16_A3_T1 failing -S15.9.5.16_A3_T2 failing -S15.9.5.17_A2_T1 failing -S15.9.5.17_A3_T1 failing -S15.9.5.17_A3_T2 failing -13.3.3_L15 failing -S15.9.5.18_A2_T1 failing -S15.9.5.18_A3_T1 failing -S15.9.5.18_A3_T2 failing -S15.9.5.19_A2_T1 failing -S15.9.5.19_A3_T1 failing -S15.9.5.19_A3_T2 failing -S15.9.5.2_A2_T1 failing -S15.9.5.2_A3_T1 failing -S15.9.5.2_A3_T2 failing -S15.9.5.20_A2_T1 failing -S15.9.5.20_A3_T1 failing -S15.9.5.20_A3_T2 failing -S15.9.5.21_A2_T1 failing -S15.9.5.21_A3_T1 failing -S15.9.5.21_A3_T2 failing -S15.9.5.22_A2_T1 failing -S15.9.5.22_A3_T1 failing -S15.9.5.22_A3_T2 failing -S15.9.5.23_A2_T1 failing -S15.9.5.23_A3_T1 failing -S15.9.5.23_A3_T2 failing -S15.9.5.24_A2_T1 failing -S15.9.5.24_A3_T1 failing -S15.9.5.24_A3_T2 failing -S15.9.5.25_A2_T1 failing -S15.9.5.25_A3_T1 failing -S15.9.5.25_A3_T2 failing -S15.9.5.26_A2_T1 failing -S15.9.5.26_A3_T1 failing -S15.9.5.26_A3_T2 failing -S15.9.5.27_A2_T1 failing -S15.9.5.27_A3_T1 failing -S15.9.5.27_A3_T2 failing -S15.9.5.28_A2_T1 failing -S15.9.5.28_A3_T1 failing -S15.9.5.28_A3_T2 failing -S15.9.5.29_A2_T1 failing -S15.9.5.29_A3_T1 failing -S15.9.5.29_A3_T2 failing -S15.9.5.3_A2_T1 failing -S15.9.5.3_A3_T1 failing -S15.9.5.3_A3_T2 failing -S15.9.5.30_A2_T1 failing -S15.9.5.30_A3_T1 failing -S15.9.5.30_A3_T2 failing -S15.9.5.31_A2_T1 failing -S15.9.5.31_A3_T1 failing -S15.9.5.31_A3_T2 failing -S15.9.5.32_A2_T1 failing -S15.9.5.32_A3_T1 failing -S15.9.5.32_A3_T2 failing -S15.9.5.33_A2_T1 failing -S15.9.5.33_A3_T1 failing -S15.9.5.33_A3_T2 failing -S15.9.5.34_A2_T1 failing -S15.9.5.34_A3_T1 failing -S15.9.5.34_A3_T2 failing -S15.9.5.35_A2_T1 failing -S15.9.5.35_A3_T1 failing -S15.9.5.35_A3_T2 failing -S15.9.5.36_A2_T1 failing -S15.9.5.36_A3_T1 failing -S15.9.5.36_A3_T2 failing -S15.9.5.37_A2_T1 failing -S15.9.5.37_A3_T1 failing -S15.9.5.37_A3_T2 failing -S15.9.5.38_A2_T1 failing -S15.9.5.38_A3_T1 failing -S15.9.5.38_A3_T2 failing -S15.9.5.39_A2_T1 failing -S15.9.5.39_A3_T1 failing -S15.9.5.39_A3_T2 failing -S15.9.5.4_A2_T1 failing -S15.9.5.4_A3_T1 failing -S15.9.5.4_A3_T2 failing 15.9.5.40_1 failing -S15.9.5.40_A2_T1 failing -S15.9.5.40_A3_T1 failing -S15.9.5.40_A3_T2 failing -S15.9.5.41_A2_T1 failing -S15.9.5.41_A3_T1 failing -S15.9.5.41_A3_T2 failing -S15.9.5.42_A2_T1 failing 6.4_c failing 8.0_L15 failing 9.1_a failing @@ -4127,7 +3838,6 @@ S15.9.5.42_A2_T1 failing 13.1.1_2 failing 13.1.1_6 failing 13.1.1_7 failing -13.1.1_L15 failing 13.2.1_1 failing 13.2.1_4 failing 13.2.1_5 failing @@ -4135,10 +3845,6 @@ S15.9.5.42_A2_T1 failing 13.3.0_2 failing 13.3.0_6 failing 13.3.0_7 failing -13.3.1_L15 failing -13.3.2_L15 failing -S15.9.5.42_A3_T1 failing -S15.9.5.42_A3_T2 failing 15.9.5.43-0-10 failing 15.9.5.43-0-11 failing 15.9.5.43-0-12 failing @@ -4153,21 +3859,6 @@ S15.9.5.42_A3_T2 failing 15.9.5.43-0-9 failing 15.9.5.44-0-1 failing 15.9.5.44-0-2 failing -S15.9.5.5_A2_T1 failing -S15.9.5.5_A3_T1 failing -S15.9.5.5_A3_T2 failing -S15.9.5.6_A2_T1 failing -S15.9.5.6_A3_T1 failing -S15.9.5.6_A3_T2 failing -S15.9.5.7_A2_T1 failing -S15.9.5.7_A3_T1 failing -S15.9.5.7_A3_T2 failing -S15.9.5.8_A2_T1 failing -S15.9.5.8_A3_T1 failing -S15.9.5.8_A3_T2 failing -S15.9.5.9_A2_T1 failing -S15.9.5.9_A3_T1 failing -S15.9.5.9_A3_T2 failing 6.2.2_a failing 6.2.2_b failing 6.2.2_c failing @@ -4284,4 +3975,4 @@ S15.4.4.4_A1_T2 failing S15.4.4.4_A1_T3 failing # Regression introduced by qtdeclarative parser changes -S7.9_A5.7_T1 failing +S7.9_A5.7_T1 failing \ No newline at end of file -- cgit v1.2.3 From 021704693a2f5a0c378c301fa75c5164fa8981a2 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 8 Jan 2013 22:52:51 +0100 Subject: Move all the SparseArray implementation into SparseArray Array is just a small wrapper, that allocated data on demand. This makes Object use less space as long as no indexed properties are being set. Change-Id: I338298eb13fd7899e52067399719f61bb5e99a13 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 8 +- qmljs_objects.h | 3 - qmljs_runtime.cpp | 18 +- qv4array.cpp | 47 +++-- qv4array.h | 502 +++++++++++++++++++++++------------------------------ qv4ecmaobjects.cpp | 21 ++- 6 files changed, 259 insertions(+), 340 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index d5ac054da3..e92040522b 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -157,6 +157,7 @@ void Object::getCollectables(QVector &objects) objects.append(o); } } + array.getCollectables(objects); } // Section 8.12.1 @@ -441,17 +442,12 @@ bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContex const quint32 idx = index.toUInt32(ctx); Value v = array.at(idx); v = op(v, rhs, ctx); - array.insert(idx, v); + array.set(idx, v); return true; } return Object::inplaceBinOp(rhs, index, op, ctx); } -void ArrayObject::getCollectables(QVector &objects) -{ - Object::getCollectables(objects); - array.getCollectables(objects); -} Function::~Function() { diff --git a/qmljs_objects.h b/qmljs_objects.h index 91e4e56384..7713779303 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -201,9 +201,6 @@ struct ArrayObject: Object { virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); - -protected: - virtual void getCollectables(QVector &objects); }; struct Function { diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 79cd4a6b85..74c5bfaa13 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -170,19 +170,17 @@ Value __qmljs_string_literal_function(ExecutionContext *ctx) Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index) { if (ArrayObject *a = base.asArrayObject()) { - int n = -1; + uint n = UINT_MAX; if (index.isInteger()) n = index.integerValue(); else if (index.isDouble()) n = index.doubleValue(); - if (n >= 0) { - if (n < (int) a->array.length()) { - // ### check writable/deletable property - Array::Iterator it = a->array.find(n); - if (it != a->array.end()) - a->array.valueRefFromIndex(*it) = Value::undefinedValue(); - return Value::fromBoolean(true); - } + if (n < a->array.length()) { + // ### check writable/deletable property + Array::iterator it = a->array.find(n); + if (it != a->array.end()) + a->array.set(n, Value::undefinedValue()); + return Value::fromBoolean(true); } } @@ -588,7 +586,7 @@ void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value { if (index.isNumber()) { if (ArrayObject *a = object.asArrayObject()) { - a->array.insert(index.toUInt32(ctx), value); + a->array.set(index.toUInt32(ctx), value); return; } } diff --git a/qv4array.cpp b/qv4array.cpp index d70853d6fe..7e53ba5386 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -103,7 +103,7 @@ const SparseArrayNode *SparseArrayNode::previousNode() const return n; } -SparseArrayNode *SparseArrayNode::copy(SparseArrayData *d) const +SparseArrayNode *SparseArrayNode::copy(SparseArray *d) const { SparseArrayNode *n = d->createNode(size_left, value, 0, false); n->setColor(color()); @@ -129,7 +129,7 @@ SparseArrayNode *SparseArrayNode::copy(SparseArrayData *d) const / \ \ a b a */ -void SparseArrayData::rotateLeft(SparseArrayNode *x) +void SparseArray::rotateLeft(SparseArrayNode *x) { SparseArrayNode *&root = header.left; SparseArrayNode *y = x->right; @@ -156,7 +156,7 @@ void SparseArrayData::rotateLeft(SparseArrayNode *x) / \ / a b b */ -void SparseArrayData::rotateRight(SparseArrayNode *x) +void SparseArray::rotateRight(SparseArrayNode *x) { SparseArrayNode *&root = header.left; SparseArrayNode *y = x->left; @@ -176,7 +176,7 @@ void SparseArrayData::rotateRight(SparseArrayNode *x) } -void SparseArrayData::rebalance(SparseArrayNode *x) +void SparseArray::rebalance(SparseArrayNode *x) { SparseArrayNode *&root = header.left; x->setColor(SparseArrayNode::Red); @@ -218,7 +218,7 @@ void SparseArrayData::rebalance(SparseArrayNode *x) root->setColor(SparseArrayNode::Black); } -void SparseArrayData::deleteNode(SparseArrayNode *z) +void SparseArray::deleteNode(SparseArrayNode *z) { SparseArrayNode *&root = header.left; SparseArrayNode *y = z; @@ -345,7 +345,7 @@ void SparseArrayData::deleteNode(SparseArrayNode *z) --numEntries; } -void SparseArrayData::recalcMostLeftNode() +void SparseArray::recalcMostLeftNode() { mostLeftNode = &header; while (mostLeftNode->left) @@ -375,7 +375,7 @@ static inline void qMapDeallocate(SparseArrayNode *node, int alignment) ::free(node); } -SparseArrayNode *SparseArrayData::createNode(uint sl, int value, SparseArrayNode *parent, bool left) +SparseArrayNode *SparseArray::createNode(uint sl, int value, SparseArrayNode *parent, bool left) { SparseArrayNode *node = static_cast(qMapAllocate(sizeof(SparseArrayNode), Q_ALIGNOF(SparseArrayNode))); Q_CHECK_PTR(node); @@ -401,7 +401,7 @@ SparseArrayNode *SparseArrayData::createNode(uint sl, int value, SparseArrayNode return node; } -void SparseArrayData::freeTree(SparseArrayNode *root, int alignment) +void SparseArray::freeTree(SparseArrayNode *root, int alignment) { if (root->left) freeTree(root->left, alignment); @@ -410,8 +410,10 @@ void SparseArrayData::freeTree(SparseArrayNode *root, int alignment) qMapDeallocate(root, alignment); } -SparseArrayData::SparseArrayData() +SparseArray::SparseArray() : numEntries(0) + , len(0) + , freeList(0) { header.p = 0; header.left = 0; @@ -419,20 +421,20 @@ SparseArrayData::SparseArrayData() mostLeftNode = &header; } -Array::iterator Array::insert(uint akey, Value value) +SparseArrayNode *SparseArray::insert(uint akey, Value value) { if (akey >= len) len = akey + 1; - SparseArrayNode *n = d->root(); - SparseArrayNode *y = d->end(); + SparseArrayNode *n = root(); + SparseArrayNode *y = end(); bool left = true; uint s = akey; while (n) { y = n; if (s == n->size_left) { values[n->value] = value; - return iterator(n); + return n; } else if (s < n->size_left) { left = true; n = n->left; @@ -444,28 +446,25 @@ Array::iterator Array::insert(uint akey, Value value) } int idx = allocValue(); - SparseArrayNode *z = d->createNode(s, idx, y, left); + SparseArrayNode *z = createNode(s, idx, y, left); values[idx] = value; - return iterator(z); + return z; } -void Array::setLength(uint l) +void SparseArray::setLength(uint l) { - if (l == 0) { - clear(); - return; - } - - iterator it = lowerBound(l); + SparseArrayNode *it = lowerBound(l); while (it != end()) it = erase(it); len = l; } void Array::splice(double start, double deleteCount, - const QVector &/*items*/, - Array &/*other*/) + const QVector &/*items*/, + Array &/*other*/) { + init(); + uint len = length(); if (start < 0) start = qMax(len + start, double(0)); else if (start > len) diff --git a/qv4array.h b/qv4array.h index 68692178a1..731d95a9c9 100644 --- a/qv4array.h +++ b/qv4array.h @@ -54,7 +54,7 @@ namespace QQmlJS { namespace VM { -struct SparseArrayData; +struct SparseArray; class ArrayElementLessThan { @@ -102,7 +102,7 @@ struct SparseArrayNode return k; } - SparseArrayNode *copy(SparseArrayData *d) const; + SparseArrayNode *copy(SparseArray *d) const; SparseArrayNode *lowerBound(uint key); SparseArrayNode *upperBound(uint key); @@ -144,14 +144,18 @@ inline SparseArrayNode *SparseArrayNode::upperBound(uint akey) -struct Q_CORE_EXPORT SparseArrayData +struct Q_CORE_EXPORT SparseArray { - SparseArrayData(); - ~SparseArrayData() { + SparseArray(); + ~SparseArray() { if (root()) freeTree(header.left, Q_ALIGNOF(SparseArrayNode)); } +private: + SparseArray(const SparseArray &other); + SparseArray &operator=(const SparseArray &other); + int numEntries; SparseArrayNode header; SparseArrayNode *mostLeftNode; @@ -161,43 +165,11 @@ struct Q_CORE_EXPORT SparseArrayData void rebalance(SparseArrayNode *x); void recalcMostLeftNode(); - SparseArrayNode *createNode(uint sl, int value, SparseArrayNode *parent, bool left); - void freeTree(SparseArrayNode *root, int alignment); - SparseArrayNode *root() const { return header.left; } - const SparseArrayNode *end() const { return &header; } - SparseArrayNode *end() { return &header; } - const SparseArrayNode *begin() const { if (root()) return mostLeftNode; return end(); } - SparseArrayNode *begin() { if (root()) return mostLeftNode; return end(); } - void deleteNode( SparseArrayNode *z); SparseArrayNode *findNode(uint akey) const; -}; - -inline SparseArrayNode *SparseArrayData::findNode(uint akey) const -{ - SparseArrayNode *n = root(); - - while (n) { - if (akey == n->size_left) { - return n; - } else if (akey < n->size_left) { - n = n->left; - } else { - akey -= n->size_left; - n = n->right; - } - } - - return 0; -} - - -class Array -{ - SparseArrayData *d; uint len; int allocValue() { @@ -218,19 +190,12 @@ class Array int freeList; public: - inline Array() : d(new SparseArrayData), len(0), freeList(0) {} - inline ~Array() { delete d; } - - Array(const Array &other); - Array &operator=(const Array &other); + SparseArrayNode *createNode(uint sl, int value, SparseArrayNode *parent, bool left); + void freeTree(SparseArrayNode *root, int alignment); - inline int numEntries() const { return d->numEntries; } inline uint length() const { return len; } void setLength(uint l); - - void clear(); - bool remove(uint key); Value take(uint key); @@ -255,148 +220,22 @@ public: return values[idx]; } - class const_iterator; - - class iterator - { - friend class const_iterator; - SparseArrayNode *i; - - public: - typedef std::bidirectional_iterator_tag iterator_category; - typedef qptrdiff difference_type; - typedef int value_type; - typedef int *pointer; - typedef int &reference; - - inline iterator() : i(0) { } - inline iterator( SparseArrayNode *node) : i(node) { } - - inline uint key() const { return i->key(); } - inline int &value() const { return i->value; } - inline int &operator*() const { return i->value; } - inline int *operator->() const { return &i->value; } - inline bool operator==(const iterator &o) const { return i == o.i; } - inline bool operator!=(const iterator &o) const { return i != o.i; } - - inline iterator &operator++() { - i = i->nextNode(); - return *this; - } - inline iterator operator++(int) { - iterator r = *this; - i = i->nextNode(); - return r; - } - inline iterator &operator--() { - i = i->previousNode(); - return *this; - } - inline iterator operator--(int) { - iterator r = *this; - i = i->previousNode(); - return r; - } - inline iterator operator+(int j) const - { iterator r = *this; if (j > 0) while (j--) ++r; else while (j++) --r; return r; } - inline iterator operator-(int j) const { return operator+(-j); } - inline iterator &operator+=(int j) { return *this = *this + j; } - inline iterator &operator-=(int j) { return *this = *this - j; } - -#ifndef QT_STRICT_ITERATORS - public: - inline bool operator==(const const_iterator &o) const - { return i == o.i; } - inline bool operator!=(const const_iterator &o) const - { return i != o.i; } -#endif - friend class Array; - }; - friend class iterator; + const SparseArrayNode *end() const { return &header; } + SparseArrayNode *end() { return &header; } + const SparseArrayNode *begin() const { if (root()) return mostLeftNode; return end(); } + SparseArrayNode *begin() { if (root()) return mostLeftNode; return end(); } - class const_iterator - { - friend class iterator; - const SparseArrayNode *i; - - public: - typedef std::bidirectional_iterator_tag iterator_category; - typedef qptrdiff difference_type; - typedef int value_type; - typedef const int *pointer; - typedef int reference; - - inline const_iterator() : i(0) { } - inline const_iterator(const SparseArrayNode *node) : i(node) { } -#ifdef QT_STRICT_ITERATORS - explicit inline const_iterator(const iterator &o) -#else - inline const_iterator(const iterator &o) -#endif - { i = o.i; } - - inline uint key() const { return i->key(); } - inline int value() const { return i->value; } - inline int operator*() const { return i->value; } - inline const int *operator->() const { return &i->value; } - inline bool operator==(const const_iterator &o) const { return i == o.i; } - inline bool operator!=(const const_iterator &o) const { return i != o.i; } - - inline const_iterator &operator++() { - i = i->nextNode(); - return *this; - } - inline const_iterator operator++(int) { - const_iterator r = *this; - i = i->nextNode(); - return r; - } - inline const_iterator &operator--() { - i = i->previousNode(); - return *this; - } - inline const_iterator operator--(int) { - const_iterator r = *this; - i = i->previousNode(); - return r; - } - inline const_iterator operator+(int j) const - { const_iterator r = *this; if (j > 0) while (j--) ++r; else while (j++) --r; return r; } - inline const_iterator operator-(int j) const { return operator+(-j); } - inline const_iterator &operator+=(int j) { return *this = *this + j; } - inline const_iterator &operator-=(int j) { return *this = *this - j; } - -#ifdef QT_STRICT_ITERATORS - private: - inline bool operator==(const iterator &o) const { return operator==(const_iterator(o)); } - inline bool operator!=(const iterator &o) const { return operator!=(const_iterator(o)); } -#endif - friend class Array; - }; - friend class const_iterator; - - // STL style - inline iterator begin() { return iterator(d->begin()); } - inline const_iterator begin() const { return const_iterator(d->begin()); } - inline const_iterator constBegin() const { return const_iterator(d->begin()); } - inline const_iterator cbegin() const { return const_iterator(d->begin()); } - inline iterator end() { return iterator(d->end()); } - inline const_iterator end() const { return const_iterator(d->end()); } - inline const_iterator constEnd() const { return const_iterator(d->end()); } - inline const_iterator cend() const { return const_iterator(d->end()); } - iterator erase(iterator it); + SparseArrayNode *erase(SparseArrayNode *n); // more Qt - typedef iterator Iterator; - typedef const_iterator ConstIterator; - iterator find(uint key); - const_iterator find(uint key) const; - const_iterator constFind(uint key) const; - iterator lowerBound(uint key); - const_iterator lowerBound(uint key) const; - iterator upperBound(uint key); - const_iterator upperBound(uint key) const; - iterator insert(uint akey, Value at); + SparseArrayNode *find(uint key); + const SparseArrayNode *find(uint key) const; + const SparseArrayNode *constFind(uint key) const; + SparseArrayNode *lowerBound(uint key); + const SparseArrayNode *lowerBound(uint key) const; + SparseArrayNode *upperBound(uint key); + const SparseArrayNode *upperBound(uint key) const; + SparseArrayNode *insert(uint akey, Value at); // STL compatibility typedef uint key_type; @@ -408,78 +247,61 @@ public: void dump() const; #endif - void concat(const Array &other); - void sort(ExecutionContext *context, const Value &comparefn); void getCollectables(QVector &objects) const; - void splice(double start, double deleteCount, const QVector &, Array &); }; - -inline Array::Array(const Array &other) +inline SparseArrayNode *SparseArray::findNode(uint akey) const { - d = 0; - *this = other; -} - + SparseArrayNode *n = root(); -inline Array &Array::operator=(const Array &other) -{ - if (this != &other) { - if (d) - delete d; - d = new SparseArrayData; - if (other.d->header.left) { - d->header.left = other.d->header.left->copy(d); - d->header.left->setParent(&d->header); - d->recalcMostLeftNode(); - len = other.len; + while (n) { + if (akey == n->size_left) { + return n; + } else if (akey < n->size_left) { + n = n->left; + } else { + akey -= n->size_left; + n = n->right; } } - return *this; -} - -inline void Array::clear() -{ - *this = Array(); + return 0; } - - -inline Value Array::at(uint akey) const +inline Value SparseArray::at(uint akey) const { - SparseArrayNode *n = d->findNode(akey); + SparseArrayNode *n = findNode(akey); int idx = n ? n->value : -1; return valueFromIndex(idx); } -inline Value Array::operator[](uint akey) const +inline Value SparseArray::operator[](uint akey) const { return at(akey); } -inline Value &Array::operator[](uint akey) +inline Value &SparseArray::operator[](uint akey) { - SparseArrayNode *n = d->findNode(akey); + SparseArrayNode *n = findNode(akey); if (!n) - n = insert(akey, Value::undefinedValue()).i; + n = insert(akey, Value::undefinedValue()); return valueRefFromIndex(n->value); } -inline Value Array::pop_front() +inline Value SparseArray::pop_front() { int idx = -1 ; if (!len) return Value::undefinedValue(); - SparseArrayNode *n = d->findNode(0); + SparseArrayNode *n = findNode(0); if (n) { idx = n->value; - d->deleteNode(n); + deleteNode(n); // adjust all size_left indices on the path to leftmost item by 1 - SparseArrayNode *n = d->root(); + SparseArrayNode *n = root(); while (n) { n->size_left -= 1; n = n->left; @@ -491,10 +313,10 @@ inline Value Array::pop_front() return v; } -inline void Array::push_front(Value value) +inline void SparseArray::push_front(Value value) { // adjust all size_left indices on the path to leftmost item by 1 - SparseArrayNode *n = d->root(); + SparseArrayNode *n = root(); while (n) { n->size_left += 1; n = n->left; @@ -503,52 +325,52 @@ inline void Array::push_front(Value value) insert(0, value); } -inline Value Array::pop_back() +inline Value SparseArray::pop_back() { int idx = -1; if (!len) return Value::undefinedValue(); --len; - SparseArrayNode *n = d->findNode(len); + SparseArrayNode *n = findNode(len); if (n) { idx = n->value; - d->deleteNode(n); + deleteNode(n); } Value v = valueFromIndex(idx); freeValue(idx); return v; } -inline void Array::push_back(Value value) +inline void SparseArray::push_back(Value value) { insert(len, value); } -inline bool Array::contains(uint akey) const +inline bool SparseArray::contains(uint akey) const { - return d->findNode(akey) != 0; + return findNode(akey) != 0; } -inline Array::const_iterator Array::constFind(uint akey) const +inline const SparseArrayNode *SparseArray::constFind(uint akey) const { - SparseArrayNode *n = d->findNode(akey); - return const_iterator(n ? n : d->end()); + SparseArrayNode *n = findNode(akey); + return n ? n : end(); } -inline Array::const_iterator Array::find(uint akey) const +inline const SparseArrayNode *SparseArray::find(uint akey) const { return constFind(akey); } -inline Array::iterator Array::find(uint akey) +inline SparseArrayNode *SparseArray::find(uint akey) { - SparseArrayNode *n = d->findNode(akey); - return iterator(n ? n : d->end()); + SparseArrayNode *n = findNode(akey); + return n ? n : end(); } #ifdef Q_MAP_DEBUG @@ -560,7 +382,7 @@ void SparseArray::dump() const while (it != end()) { const SparseArrayNode *n = it.i; int depth = 0; - while (n && n != d->root()) { + while (n && n != root()) { ++depth; n = n->parent(); } @@ -574,24 +396,24 @@ void SparseArray::dump() const #endif -inline bool Array::remove(uint akey) +inline bool SparseArray::remove(uint akey) { - SparseArrayNode *node = d->findNode(akey); + SparseArrayNode *node = findNode(akey); if (node) { - d->deleteNode(node); + deleteNode(node); return true; } return false; } -inline Value Array::take(uint akey) +inline Value SparseArray::take(uint akey) { - SparseArrayNode *node = d->findNode(akey); + SparseArrayNode *node = findNode(akey); int t; if (node) { t = node->value; - d->deleteNode(node); + deleteNode(node); } else { t = -1; } @@ -599,88 +421,196 @@ inline Value Array::take(uint akey) } -inline Array::iterator Array::erase(iterator it) +inline SparseArrayNode *SparseArray::erase(SparseArrayNode *n) { - if (it == iterator(d->end())) - return it; + if (n == end()) + return n; - SparseArrayNode *n = it.i; - ++it; - d->deleteNode(n); - return it; + SparseArrayNode *next = n->nextNode(); + deleteNode(n); + return next; } -inline QList Array::keys() const +inline QList SparseArray::keys() const { QList res; - res.reserve(numEntries()); - const_iterator i = begin(); - while (i != end()) { - res.append(i.key()); - ++i; + res.reserve(numEntries); + SparseArrayNode *n = mostLeftNode; + while (n != end()) { + res.append(n->key()); + n = n->nextNode(); } return res; } -inline Array::const_iterator Array::lowerBound(uint akey) const +inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const { - SparseArrayNode *lb = d->root()->lowerBound(akey); + const SparseArrayNode *lb = root()->lowerBound(akey); if (!lb) - lb = d->end(); - return const_iterator(lb); + lb = end(); + return lb; } -inline Array::iterator Array::lowerBound(uint akey) +inline SparseArrayNode *SparseArray::lowerBound(uint akey) { - SparseArrayNode *lb = d->root()->lowerBound(akey); + SparseArrayNode *lb = root()->lowerBound(akey); if (!lb) - lb = d->end(); - return iterator(lb); + lb = end(); + return lb; } -inline Array::const_iterator Array::upperBound(uint akey) const +inline const SparseArrayNode *SparseArray::upperBound(uint akey) const { - SparseArrayNode *ub = d->root()->upperBound(akey); + const SparseArrayNode *ub = root()->upperBound(akey); if (!ub) - ub = d->end(); - return const_iterator(ub); + ub = end(); + return ub; } -inline Array::iterator Array::upperBound(uint akey) +inline SparseArrayNode *SparseArray::upperBound(uint akey) { - SparseArrayNode *ub = d->root()->upperBound(akey); + SparseArrayNode *ub = root()->upperBound(akey); if (!ub) - ub = d->end(); - return iterator(ub); + ub = end(); + return ub; } +inline void SparseArray::getCollectables(QVector &objects) const +{ + for (const SparseArrayNode *it = begin(), *eit = end(); it != eit; it = it->nextNode()) { + if (Object *o = valueFromIndex(it->value).asObject()) + objects.append(o); + } +} + +class Array +{ + SparseArray *d; + +public: + Array() : d(0) {} + ~Array() { delete d; } + void init() { if (!d) d = new SparseArray; } + + uint length() const { return d ? d->length() : 0; } + void setLength(uint l) { + init(); + d->setLength(l); + } + + struct iterator + { + SparseArrayNode *i; + + inline iterator( SparseArrayNode *node) : i(node) { } + + inline uint key() const { return i->key(); } + inline int &value() const { return i->value; } + inline int &operator*() const { return i->value; } + inline int *operator->() const { return &i->value; } + inline bool operator==(const iterator &o) const { return i == o.i; } + inline bool operator!=(const iterator &o) const { return i != o.i; } + + inline iterator &operator++() { + i = i->nextNode(); + return *this; + } + inline iterator operator++(int) { + iterator r = *this; + i = i->nextNode(); + return r; + } + inline iterator &operator--() { + i = i->previousNode(); + return *this; + } + inline iterator operator--(int) { + iterator r = *this; + i = i->previousNode(); + return r; + } + inline iterator operator+(int j) const + { iterator r = *this; if (j > 0) while (j--) ++r; else while (j++) --r; return r; } + inline iterator operator-(int j) const { return operator+(-j); } + inline iterator &operator+=(int j) { return *this = *this + j; } + inline iterator &operator-=(int j) { return *this = *this - j; } + + friend class SparseArray; + }; + + iterator begin() const { + return iterator(d ? d->begin() : 0); + } + iterator end() const { + return iterator(d ? d->end() : 0); + } + + iterator find(uint index) const { + return iterator(d ? d->find(index) : 0); + } + + void set(uint index, Value value) { + init(); + (*d)[index] = value; + } + Value at(uint index) const { + return d ? d->at(index) : Value::undefinedValue(); + } + Value at(iterator it) const { + return it.i ? d->valueFromIndex(*it) : Value::undefinedValue(); + } + + void getCollectables(QVector &objects) const { + if (d) + d->getCollectables(objects); + } + + void push_front(Value v) { + init(); + d->push_front(v); + } + Value pop_front() { + init(); + return d->pop_front(); + } + void push_back(Value v) { + init(); + d->push_back(v); + } + Value pop_back() { + init(); + return d->pop_back(); + } + + void concat(const Array &other); + void sort(ExecutionContext *context, const Value &comparefn); + void splice(double start, double deleteCount, const QVector &, Array &); +}; + inline void Array::concat(const Array &other) { - int newLen = len + other.len; - for (const_iterator it = other.constBegin(); it != other.constEnd(); ++it) { - insert(len + it.key(), other.valueFromIndex(it.value())); + init(); + int len = length(); + int newLen = len + other.length(); + for (const SparseArrayNode *it = other.d->begin(); it != other.d->end(); it = it->nextNode()) { + set(len + it->key(), other.d->valueFromIndex(it->value)); } - len = newLen; + setLength(newLen); } inline void Array::sort(ExecutionContext *context, const Value &comparefn) { + if (!d) + return; + ArrayElementLessThan lessThan(context, comparefn); // ### //std::sort(to_vector.begin(), to_vector.end(), lessThan); } -inline void Array::getCollectables(QVector &objects) const -{ - for (const_iterator it = constBegin(), eit = constEnd(); it != eit; ++it) { - if (Object *o = valueFromIndex(*it).asObject()) - objects.append(o); - } -} - } } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 0f06bbddaf..a0dfdc8070 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1552,7 +1552,7 @@ Value ArrayCtor::call(ExecutionContext *ctx) value.setLength(isize); } else { for (unsigned int i = 0; i < ctx->argumentCount; ++i) { - value.insert(i, ctx->argument(i)); + value.set(i, ctx->argument(i)); } } @@ -1604,7 +1604,7 @@ Value ArrayPrototype::method_concat(ExecutionContext *ctx) result = instance->array; else { QString v = ctx->thisObject.toString(ctx)->toQString(); - result.insert(0, Value::fromString(ctx, v)); + result.set(0, Value::fromString(ctx, v)); } for (uint i = 0; i < ctx->argumentCount; ++i) { @@ -1615,7 +1615,7 @@ Value ArrayPrototype::method_concat(ExecutionContext *ctx) result.concat(elt->array); else - result.insert(k, arg); + result.set(k, arg); } return Value::fromObject(ctx->engine->newArrayObject(result)); @@ -1731,8 +1731,8 @@ Value ArrayPrototype::method_reverse(ExecutionContext *ctx) for (; lo < hi; ++lo, --hi) { Value tmp = instance->array.at(lo); - instance->array.insert(lo, instance->array.at(hi)); - instance->array.insert(hi, tmp); + instance->array.set(lo, instance->array.at(hi)); + instance->array.set(hi, tmp); } return Value::undefinedValue(); } @@ -1767,7 +1767,7 @@ Value ArrayPrototype::method_slice(ExecutionContext *ctx) String *r11 = Value::fromDouble(k).toString(ctx); Value v = self.property(ctx, r11); if (! v.isUndefined()) - result.insert(n++, v); + result.set(n++, v); } return Value::fromObject(ctx->engine->newArrayObject(result)); } @@ -1839,9 +1839,8 @@ Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) } else __qmljs_throw_type_error(ctx); - for (Array::ConstIterator it = instance->array.find(fromIndex), end = instance->array.constEnd(); - it != end; ++it) { - if (__qmljs_strict_equal(instance->array.valueRefFromIndex(*it), searchValue)) + for (Array::iterator it = instance->array.find(fromIndex), end = instance->array.end(); it != end; ++it) { + if (__qmljs_strict_equal(instance->array.at(*it), searchValue)) return Value::fromInt32(it.key()); } @@ -1945,7 +1944,7 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); - a->array.insert(k, r); + a->array.set(k, r); } return Value::fromObject(a); } @@ -1971,7 +1970,7 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) if (__qmljs_to_boolean(r, ctx)) { const uint index = a->array.length(); a->array.setLength(index + 1); - a->array.insert(index, v); + a->array.set(index, v); } } return Value::fromObject(a); -- cgit v1.2.3 From 938f91b8cd18570bf75bda4576e63233b1efb23a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 9 Jan 2013 14:19:35 +0100 Subject: Further refactoring of Array support Add direct support for dense arrays to speed up things. Store PropertyDescriptors to allow making array variables readonly etc., and to support JS getters and setters with array indices. Change-Id: I46741cb34c002b0c2c041d427f7b922ec374e115 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 24 ++- qmljs_objects.h | 6 +- qmljs_runtime.cpp | 10 +- qv4array.cpp | 142 ++++++++++++--- qv4array.h | 523 ++++++++++++++++++++++++++++++----------------------- qv4ecmaobjects.cpp | 51 +++--- 6 files changed, 469 insertions(+), 287 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index e92040522b..87d8a76569 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -74,7 +74,7 @@ void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &va __put__(ctx, ctx->engine->identifier(name), value); } -Value Object::getValue(ExecutionContext *ctx, PropertyDescriptor *p) const +Value Object::getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const { if (p->isData()) return p->value; @@ -84,6 +84,26 @@ Value Object::getValue(ExecutionContext *ctx, PropertyDescriptor *p) const return p->get->call(ctx, Value::fromObject(const_cast(this)), 0, 0); } +Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const +{ + if (!p || p->type == PropertyDescriptor::Generic) + return Value::undefinedValue(); + return getValue(ctx, p); +} + +Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const +{ + *exists = p && p->type != PropertyDescriptor::Generic; + if (!*exists) + return Value::undefinedValue(); + return getValue(ctx, p); +} + +Value Object::getElement(ExecutionContext *ctx, uint index) const +{ + return getValueChecked(ctx, array.at(index)); +} + bool Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx) { bool hasProperty = false; @@ -440,7 +460,7 @@ bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContex { if (index.isNumber()) { const quint32 idx = index.toUInt32(ctx); - Value v = array.at(idx); + Value v = getElement(ctx, idx); v = op(v, rhs, ctx); array.set(idx, v); return true; diff --git a/qmljs_objects.h b/qmljs_objects.h index 7713779303..55f120c843 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -136,7 +136,11 @@ struct Object: Managed { // void __put__(ExecutionContext *ctx, const QString &name, const Value &value); - Value getValue(ExecutionContext *ctx, PropertyDescriptor *p) const; + Value getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const; + Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const; + Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const; + Value getElement(ExecutionContext *ctx, uint index) const; + bool inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 74c5bfaa13..ed20de7cd6 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -175,13 +175,7 @@ Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index) n = index.integerValue(); else if (index.isDouble()) n = index.doubleValue(); - if (n < a->array.length()) { - // ### check writable/deletable property - Array::iterator it = a->array.find(n); - if (it != a->array.end()) - a->array.set(n, Value::undefinedValue()); - return Value::fromBoolean(true); - } + a->array.deleteIndex(n); } String *name = index.toString(ctx); @@ -571,7 +565,7 @@ Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) } if (ArrayObject *a = object.asArrayObject()) - return a->array.at(index.toUInt32(ctx)); + return a->getElement(ctx, index.toUInt32(ctx)); } String *name = index.toString(ctx); diff --git a/qv4array.cpp b/qv4array.cpp index 7e53ba5386..27977103c9 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -52,8 +52,15 @@ namespace QQmlJS { namespace VM { -bool ArrayElementLessThan::operator()(const Value &v1, const Value &v2) const +bool ArrayElementLessThan::operator()(const PropertyDescriptor &p1, const PropertyDescriptor &p2) const { + if (p1.type == PropertyDescriptor::Generic) + return false; + if (p2.type == PropertyDescriptor::Generic) + return true; + Value v1 = thisObject->getValue(m_context, &p1); + Value v2 = thisObject->getValue(m_context, &p2); + if (v1.isUndefined()) return false; if (v2.isUndefined()) @@ -105,7 +112,8 @@ const SparseArrayNode *SparseArrayNode::previousNode() const SparseArrayNode *SparseArrayNode::copy(SparseArray *d) const { - SparseArrayNode *n = d->createNode(size_left, value, 0, false); + SparseArrayNode *n = d->createNode(size_left, 0, false); + n->value = value; n->setColor(color()); if (left) { n->left = left->copy(d); @@ -375,7 +383,7 @@ static inline void qMapDeallocate(SparseArrayNode *node, int alignment) ::free(node); } -SparseArrayNode *SparseArray::createNode(uint sl, int value, SparseArrayNode *parent, bool left) +SparseArrayNode *SparseArray::createNode(uint sl, SparseArrayNode *parent, bool left) { SparseArrayNode *node = static_cast(qMapAllocate(sizeof(SparseArrayNode), Q_ALIGNOF(SparseArrayNode))); Q_CHECK_PTR(node); @@ -384,7 +392,6 @@ SparseArrayNode *SparseArray::createNode(uint sl, int value, SparseArrayNode *pa node->left = 0; node->right = 0; node->size_left = sl; - node->value = value; ++numEntries; if (parent) { @@ -412,8 +419,6 @@ void SparseArray::freeTree(SparseArrayNode *root, int alignment) SparseArray::SparseArray() : numEntries(0) - , len(0) - , freeList(0) { header.p = 0; header.left = 0; @@ -421,11 +426,19 @@ SparseArray::SparseArray() mostLeftNode = &header; } -SparseArrayNode *SparseArray::insert(uint akey, Value value) +SparseArray::SparseArray(const SparseArray &other) { - if (akey >= len) - len = akey + 1; + header.p = 0; + header.right = 0; + if (other.header.left) { + header.left = other.header.left->copy(this); + header.left->setParent(&header); + recalcMostLeftNode(); + } +} +SparseArrayNode *SparseArray::insert(uint akey) +{ SparseArrayNode *n = root(); SparseArrayNode *y = end(); bool left = true; @@ -433,7 +446,6 @@ SparseArrayNode *SparseArray::insert(uint akey, Value value) while (n) { y = n; if (s == n->size_left) { - values[n->value] = value; return n; } else if (s < n->size_left) { left = true; @@ -445,25 +457,25 @@ SparseArrayNode *SparseArray::insert(uint akey, Value value) } } - int idx = allocValue(); - SparseArrayNode *z = createNode(s, idx, y, left); - values[idx] = value; - return z; + return createNode(s, y, left); } -void SparseArray::setLength(uint l) +Array::Array(const Array & other) + : len(other.len) + , values(other.values) + , sparse(0) { - SparseArrayNode *it = lowerBound(l); - while (it != end()) - it = erase(it); - len = l; + freeList = other.freeList; + if (other.sparse) + sparse = new SparseArray(*other.sparse); } + void Array::splice(double start, double deleteCount, const QVector &/*items*/, Array &/*other*/) { - init(); + initSparse(); uint len = length(); if (start < 0) start = qMax(len + start, double(0)); @@ -485,7 +497,95 @@ void Array::splice(double start, double deleteCount, // else if (itemsSize < dc) // to_vector.erase(to_vector.begin() + st, to_vector.begin() + (dc - itemsSize)); // for (uint i = 0; i < itemsSize; ++i) -// (*to_vector)[st + i] = items.at(i); + // (*to_vector)[st + i] = items.at(i); +} + +Value Array::indexOf(Value v, uint fromIndex, ExecutionContext *ctx, Object *o) +{ + if (sparse) { + for (SparseArrayNode *n = sparse->findNode(fromIndex); n; n = n->nextNode()) { + bool exists; + Value value = o->getValueChecked(ctx, descriptor(n->value), &exists); + if (exists && __qmljs_strict_equal(value, v)) + return Value::fromDouble(n->key()); + } + } else { + PropertyDescriptor *pd = values.data(); + PropertyDescriptor *end = pd + values.size(); + pd += offset + fromIndex; + while (pd < end) { + bool exists; + Value value = o->getValueChecked(ctx, pd, &exists); + if (exists && __qmljs_strict_equal(value, v)) + return Value::fromDouble(pd - offset - values.constData()); + ++pd; + } + } + return Value::fromInt32(-1); +} + +void Array::concat(const Array &other) +{ + initSparse(); + int len = length(); + int newLen = len + other.length(); + if (other.sparse) + initSparse(); + if (sparse) { + if (other.sparse) { + for (const SparseArrayNode *it = other.sparse->begin(); it != other.sparse->end(); it = it->nextNode()) + set(len + it->key(), other.descriptor(it->value)); + } else { + int oldSize = values.size(); + values.resize(oldSize + other.length()); + memcpy(values.data() + oldSize, other.values.constData() + other.offset, other.length()*sizeof(PropertyDescriptor)); + for (uint i = 0; i < other.length(); ++i) { + SparseArrayNode *n = sparse->insert(len + i); + n->value = oldSize + i; + } + } + } else { + int oldSize = values.size(); + values.resize(oldSize + other.length()); + memcpy(values.data() + oldSize, other.values.constData() + other.offset, other.length()*sizeof(PropertyDescriptor)); + } + setLength(newLen); +} + +void Array::sort(ExecutionContext *context, Object *thisObject, const Value &comparefn) +{ + if (!sparse) + return; + + ArrayElementLessThan lessThan(context, thisObject, comparefn); + std::sort(values.begin(), values.end(), lessThan); + if (sparse) + delete sparse; +} + + +void Array::initSparse() +{ + if (!sparse) { + sparse = new SparseArray; + for (int i = offset; i < values.size(); ++i) { + SparseArrayNode *n = sparse->insert(i - offset); + n->value = i; + } + + if (offset) { + int o = offset; + for (int i = 0; i < o - 1; ++i) { + values[i].type = PropertyDescriptor::Generic; + values[i].value = Value::fromInt32(i + 1); + } + values[o - 1].type = PropertyDescriptor::Generic; + values[o - 1].value = Value::fromInt32(values.size()); + freeList = 0; + } else { + freeList = values.size(); + } + } } diff --git a/qv4array.h b/qv4array.h index 731d95a9c9..ad936f9ce6 100644 --- a/qv4array.h +++ b/qv4array.h @@ -44,6 +44,8 @@ #include #include +#include +#include #ifdef Q_MAP_DEBUG #include @@ -59,13 +61,14 @@ struct SparseArray; class ArrayElementLessThan { public: - inline ArrayElementLessThan(ExecutionContext *context, const Value &comparefn) - : m_context(context), m_comparefn(comparefn) {} + inline ArrayElementLessThan(ExecutionContext *context, Object *thisObject, const Value &comparefn) + : m_context(context), thisObject(thisObject), m_comparefn(comparefn) {} - bool operator()(const Value &v1, const Value &v2) const; + bool operator()(const PropertyDescriptor &v1, const PropertyDescriptor &v2) const; private: ExecutionContext *m_context; + Object *thisObject; Value m_comparefn; }; @@ -76,7 +79,7 @@ struct SparseArrayNode SparseArrayNode *left; SparseArrayNode *right; uint size_left; - int value; + uint value; enum Color { Red = 0, Black = 1 }; enum { Mask = 3 }; // reserve the second bit as well @@ -152,8 +155,8 @@ struct Q_CORE_EXPORT SparseArray freeTree(header.left, Q_ALIGNOF(SparseArrayNode)); } -private: SparseArray(const SparseArray &other); +private: SparseArray &operator=(const SparseArray &other); int numEntries; @@ -167,59 +170,22 @@ private: SparseArrayNode *root() const { return header.left; } - void deleteNode( SparseArrayNode *z); - SparseArrayNode *findNode(uint akey) const; - - uint len; - - int allocValue() { - int idx = freeList; - if (values.size() <= freeList) - values.resize(++freeList); - else - freeList = values.at(freeList).integerValue(); - - return idx; - } - void freeValue(int idx) { - values[idx] = Value::fromInt32(freeList); - freeList = idx; - } + void deleteNode(SparseArrayNode *z); - QVector values; - int freeList; public: - SparseArrayNode *createNode(uint sl, int value, SparseArrayNode *parent, bool left); + SparseArrayNode *createNode(uint sl, SparseArrayNode *parent, bool left); void freeTree(SparseArrayNode *root, int alignment); - inline uint length() const { return len; } - void setLength(uint l); - - bool remove(uint key); - Value take(uint key); - - bool contains(uint key) const; - Value at(uint key) const; - Value &operator[](uint key); - Value operator[](uint key) const; + SparseArrayNode *findNode(uint akey) const; - Value pop_front(); - void push_front(Value at); - Value pop_back(); - void push_back(Value at); + uint pop_front(); + void push_front(uint at); + uint pop_back(uint len); + void push_back(uint at, uint len); QList keys() const; - Value valueFromIndex(int idx) const { - if (idx == -1) - return Value::undefinedValue(); - return values.at(idx); - } - Value &valueRefFromIndex(int idx) { - return values[idx]; - } - const SparseArrayNode *end() const { return &header; } SparseArrayNode *end() { return &header; } const SparseArrayNode *begin() const { if (root()) return mostLeftNode; return end(); } @@ -227,15 +193,11 @@ public: SparseArrayNode *erase(SparseArrayNode *n); - // more Qt - SparseArrayNode *find(uint key); - const SparseArrayNode *find(uint key) const; - const SparseArrayNode *constFind(uint key) const; SparseArrayNode *lowerBound(uint key); const SparseArrayNode *lowerBound(uint key) const; SparseArrayNode *upperBound(uint key); const SparseArrayNode *upperBound(uint key) const; - SparseArrayNode *insert(uint akey, Value at); + SparseArrayNode *insert(uint akey); // STL compatibility typedef uint key_type; @@ -246,8 +208,6 @@ public: #ifdef Q_MAP_DEBUG void dump() const; #endif - - void getCollectables(QVector &objects) const; }; inline SparseArrayNode *SparseArray::findNode(uint akey) const @@ -268,33 +228,9 @@ inline SparseArrayNode *SparseArray::findNode(uint akey) const return 0; } -inline Value SparseArray::at(uint akey) const -{ - SparseArrayNode *n = findNode(akey); - int idx = n ? n->value : -1; - return valueFromIndex(idx); -} - - -inline Value SparseArray::operator[](uint akey) const -{ - return at(akey); -} - - -inline Value &SparseArray::operator[](uint akey) -{ - SparseArrayNode *n = findNode(akey); - if (!n) - n = insert(akey, Value::undefinedValue()); - return valueRefFromIndex(n->value); -} - -inline Value SparseArray::pop_front() +inline uint SparseArray::pop_front() { - int idx = -1 ; - if (!len) - return Value::undefinedValue(); + uint idx = UINT_MAX ; SparseArrayNode *n = findNode(0); if (n) { @@ -307,13 +243,10 @@ inline Value SparseArray::pop_front() n = n->left; } } - --len; - Value v = valueFromIndex(idx); - freeValue(idx); - return v; + return idx; } -inline void SparseArray::push_front(Value value) +inline void SparseArray::push_front(uint value) { // adjust all size_left indices on the path to leftmost item by 1 SparseArrayNode *n = root(); @@ -321,56 +254,28 @@ inline void SparseArray::push_front(Value value) n->size_left += 1; n = n->left; } - ++len; - insert(0, value); + n = insert(0); + n->value = value; } -inline Value SparseArray::pop_back() +inline uint SparseArray::pop_back(uint len) { - int idx = -1; + uint idx = UINT_MAX; if (!len) - return Value::undefinedValue(); + return idx; - --len; - SparseArrayNode *n = findNode(len); + SparseArrayNode *n = findNode(len - 1); if (n) { idx = n->value; deleteNode(n); } - Value v = valueFromIndex(idx); - freeValue(idx); - return v; -} - -inline void SparseArray::push_back(Value value) -{ - insert(len, value); -} - - -inline bool SparseArray::contains(uint akey) const -{ - return findNode(akey) != 0; -} - - -inline const SparseArrayNode *SparseArray::constFind(uint akey) const -{ - SparseArrayNode *n = findNode(akey); - return n ? n : end(); -} - - -inline const SparseArrayNode *SparseArray::find(uint akey) const -{ - return constFind(akey); + return idx; } - -inline SparseArrayNode *SparseArray::find(uint akey) +inline void SparseArray::push_back(uint index, uint len) { - SparseArrayNode *n = findNode(akey); - return n ? n : end(); + SparseArrayNode *n = insert(len); + n->value = index; } #ifdef Q_MAP_DEBUG @@ -396,31 +301,6 @@ void SparseArray::dump() const #endif -inline bool SparseArray::remove(uint akey) -{ - SparseArrayNode *node = findNode(akey); - if (node) { - deleteNode(node); - return true; - } - return false; -} - - -inline Value SparseArray::take(uint akey) -{ - SparseArrayNode *node = findNode(akey); - int t; - if (node) { - t = node->value; - deleteNode(node); - } else { - t = -1; - } - return valueFromIndex(t); -} - - inline SparseArrayNode *SparseArray::erase(SparseArrayNode *n) { if (n == end()) @@ -478,140 +358,325 @@ inline SparseArrayNode *SparseArray::upperBound(uint akey) return ub; } -inline void SparseArray::getCollectables(QVector &objects) const -{ - for (const SparseArrayNode *it = begin(), *eit = end(); it != eit; it = it->nextNode()) { - if (Object *o = valueFromIndex(it->value).asObject()) - objects.append(o); - } -} class Array { - SparseArray *d; + uint len; + union { + uint freeList; + uint offset; + }; + QVector values; + SparseArray *sparse; + + void fillDescriptor(PropertyDescriptor *pd, Value v) + { + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Enabled; + pd->configurable = PropertyDescriptor::Enabled; + pd->value = v; + } + + uint allocValue() { + uint idx = freeList; + if (values.size() <= (int)freeList) + values.resize(++freeList); + else + freeList = values.at(freeList).value.integerValue(); + return idx; + } + + uint allocValue(Value v) { + uint idx = allocValue(); + PropertyDescriptor *pd = &values[idx]; + fillDescriptor(pd, v); + return idx; + } + void freeValue(int idx) { + PropertyDescriptor &pd = values[idx]; + pd.type = PropertyDescriptor::Generic; + pd.value.tag = Value::_Undefined_Type; + pd.value.int_32 = freeList; + freeList = idx; + } + + PropertyDescriptor *descriptor(uint index) { + PropertyDescriptor *pd = values.data() + index; + if (!sparse) + pd += offset; + return pd; + } + const PropertyDescriptor *descriptor(uint index) const { + const PropertyDescriptor *pd = values.data() + index; + if (!sparse) + pd += offset; + return pd; + } + + void getHeadRoom() { + assert(!sparse && !offset); + offset = qMax(values.size() >> 2, 16); + QVector newValues(values.size() + offset); + memcpy(newValues.data() + offset, values.data(), values.size()*sizeof(PropertyDescriptor)); + values = newValues; + } public: - Array() : d(0) {} - ~Array() { delete d; } - void init() { if (!d) d = new SparseArray; } + Array() : len(0), offset(0), sparse(0) {} + Array(const Array &other); + ~Array() { delete sparse; } + void initSparse(); - uint length() const { return d ? d->length() : 0; } + uint length() const { return len; } void setLength(uint l) { - init(); - d->setLength(l); + len = l; + if (len > 0x100000) + initSparse(); + if (sparse) { + SparseArrayNode *it = sparse->lowerBound(l); + while (it != sparse->end()) { + PropertyDescriptor &pd = values[it->value]; + pd.type = PropertyDescriptor::Generic; + pd.value.tag = Value::_Undefined_Type; + pd.value.int_32 = freeList; + freeList = it->value; + it = sparse->erase(it); + } + } else if (values.size() > (int)len){ + values.resize(len); + } } - struct iterator +#if 0 + struct sparse_iterator { + Array *array; SparseArrayNode *i; + PropertyDescriptor *pd; - inline iterator( SparseArrayNode *node) : i(node) { } + inline sparse_iterator(SparseArrayNode *node) : i(node), pd(0) { } inline uint key() const { return i->key(); } - inline int &value() const { return i->value; } - inline int &operator*() const { return i->value; } - inline int *operator->() const { return &i->value; } - inline bool operator==(const iterator &o) const { return i == o.i; } - inline bool operator!=(const iterator &o) const { return i != o.i; } + inline uint &value() const { return i->value; } + inline uint &operator*() const { return i->value; } + inline uint *operator->() const { return &i->value; } + inline bool operator==(const sparse_iterator &o) const { return i == o.i; } + inline bool operator!=(const sparse_iterator &o) const { return i != o.i; } - inline iterator &operator++() { + inline sparse_iterator &operator++() { i = i->nextNode(); return *this; } - inline iterator operator++(int) { - iterator r = *this; + inline sparse_iterator operator++(int) { + sparse_iterator r = *this; i = i->nextNode(); return r; } - inline iterator &operator--() { + inline sparse_iterator &operator--() { i = i->previousNode(); return *this; } - inline iterator operator--(int) { - iterator r = *this; + inline sparse_iterator operator--(int) { + sparse_iterator r = *this; i = i->previousNode(); return r; } - inline iterator operator+(int j) const - { iterator r = *this; if (j > 0) while (j--) ++r; else while (j++) --r; return r; } - inline iterator operator-(int j) const { return operator+(-j); } - inline iterator &operator+=(int j) { return *this = *this + j; } - inline iterator &operator-=(int j) { return *this = *this - j; } + inline sparse_iterator operator+(int j) const + { sparse_iterator r = *this; if (j > 0) while (j--) ++r; else while (j++) --r; return r; } + inline sparse_iterator operator-(int j) const { return operator+(-j); } + inline sparse_iterator &operator+=(int j) { return *this = *this + j; } + inline sparse_iterator &operator-=(int j) { return *this = *this - j; } friend class SparseArray; }; - iterator begin() const { - return iterator(d ? d->begin() : 0); + sparse_iterator begin() const { + return sparse_iterator(sparse ? sparse->begin() : 0); + } + sparse_iterator end() const { + return sparse_iterator(sparse ? sparse->end() : 0); } - iterator end() const { - return iterator(d ? d->end() : 0); + + sparse_iterator find(uint index) const { + return sparse_iterator(sparse ? sparse->find(index) : 0); } - iterator find(uint index) const { - return iterator(d ? d->find(index) : 0); + PropertyDescriptor *at(sparse_iterator it) const { + return it.i ? descriptor(*it) : 0; } +#endif + void set(uint index, const PropertyDescriptor *pd) { + if (!sparse && (index < 0x1000 || index < len + (len >> 2))) { + if (index + offset >= (uint)values.size()) { + values.resize(offset + index + 1); + for (uint i = len + 1; i < index; ++i) { + values[i].type = PropertyDescriptor::Generic; + values[i].value.tag = Value::_Undefined_Type; + } + } + *descriptor(index) = *pd; + } else { + initSparse(); + SparseArrayNode *n = sparse->insert(index); + uint idx = allocValue(); + *descriptor(idx) = *pd; + n->value = idx; + } + if (index >= len) + len = index + 1; + } void set(uint index, Value value) { - init(); - (*d)[index] = value; + PropertyDescriptor pd; + fillDescriptor(&pd, value); + set(index, &pd); } - Value at(uint index) const { - return d ? d->at(index) : Value::undefinedValue(); + + bool deleteIndex(uint index) { + if (index >= len) + return true; + PropertyDescriptor *pd = 0; + if (!sparse) { + pd = at(index); + } else { + SparseArrayNode *n = sparse->findNode(index); + if (n) + pd = descriptor(n->value); + } + if (!pd) + return true; + if (!pd->isConfigurable()) + return false; + pd->type = PropertyDescriptor::Generic; + pd->value.tag = Value::_Undefined_Type; + if (sparse) { + pd->value.int_32 = freeList; + freeList = pd - values.constData(); + } + return true; } - Value at(iterator it) const { - return it.i ? d->valueFromIndex(*it) : Value::undefinedValue(); + + PropertyDescriptor *at(uint index) { + if (!sparse) { + if (index >= values.size() - offset) + return 0; + return values.data() + index - offset; + } else { + SparseArrayNode *n = sparse->findNode(index); + if (!n) + return 0; + return values.data() + n->value; + } + } + + const PropertyDescriptor *at(uint index) const { + if (!sparse) { + if (index >= values.size() - offset) + return 0; + return values.data() + index + offset; + } else { + SparseArrayNode *n = sparse->findNode(index); + if (!n) + return 0; + return values.data() + n->value; + } } void getCollectables(QVector &objects) const { - if (d) - d->getCollectables(objects); + uint i = sparse ? 0 : offset; + for (; i < (uint)values.size(); ++i) { + const PropertyDescriptor &pd = values.at(i); + if (pd.isData()) + if (Object *o = pd.value.asObject()) + objects.append(o); + } } void push_front(Value v) { - init(); - d->push_front(v); + if (!sparse) { + if (!offset) + getHeadRoom(); + + PropertyDescriptor pd; + fillDescriptor(&pd, v); + --offset; + values[offset] = pd; + } else { + uint idx = allocValue(v); + sparse->push_front(idx); + } + ++len; + } + PropertyDescriptor *front() { + PropertyDescriptor *pd = 0; + if (!sparse) { + if (len) + pd = values.data() + offset; + } else { + SparseArrayNode *n = sparse->findNode(0); + if (n) + pd = descriptor(n->value); + } + if (pd && pd->type == PropertyDescriptor::Generic) + return 0; + return pd; } - Value pop_front() { - init(); - return d->pop_front(); + void pop_front() { + if (!len) + return; + if (!sparse) { + ++offset; + } else { + uint idx = sparse->pop_front(); + freeValue(idx); + } + --len; } void push_back(Value v) { - init(); - d->push_back(v); + if (!sparse) { + PropertyDescriptor pd; + fillDescriptor(&pd, v); + values.append(pd); + } else { + uint idx = allocValue(v); + sparse->push_back(idx, len); + } + ++len; + } + PropertyDescriptor *back() { + PropertyDescriptor *pd = 0; + if (!sparse) { + if (len) + pd = values.data() + offset + len; + } else { + SparseArrayNode *n = sparse->findNode(len - 1); + if (n) + pd = descriptor(n->value); + } + if (pd && pd->type == PropertyDescriptor::Generic) + return 0; + return pd; } - Value pop_back() { - init(); - return d->pop_back(); + void pop_back() { + if (!len) + return; + if (!sparse) { + values.resize(values.size() - 1); + } else { + uint idx = sparse->pop_back(len); + if (idx != UINT_MAX) + freeValue(idx); + } + --len; } void concat(const Array &other); - void sort(ExecutionContext *context, const Value &comparefn); + void sort(ExecutionContext *context, Object *thisObject, const Value &comparefn); void splice(double start, double deleteCount, const QVector &, Array &); + Value indexOf(Value v, uint fromIndex, ExecutionContext *ctx, Object *o); }; -inline void Array::concat(const Array &other) -{ - init(); - int len = length(); - int newLen = len + other.length(); - for (const SparseArrayNode *it = other.d->begin(); it != other.d->end(); it = it->nextNode()) { - set(len + it->key(), other.d->valueFromIndex(it->value)); - } - setLength(newLen); -} - -inline void Array::sort(ExecutionContext *context, const Value &comparefn) -{ - if (!d) - return; - - ArrayElementLessThan lessThan(context, comparefn); - // ### - //std::sort(to_vector.begin(), to_vector.end(), lessThan); -} - - } } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index a0dfdc8070..85cd83d72a 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1651,7 +1651,7 @@ Value ArrayPrototype::method_join(ExecutionContext *ctx) if (i) R += r4; - Value e = a->array.at(i); + Value e = a->getElement(ctx, i); if (! (e.isUndefined() || e.isNull())) R += e.toString(ctx)->toQString(); } @@ -1681,8 +1681,11 @@ Value ArrayPrototype::method_join(ExecutionContext *ctx) Value ArrayPrototype::method_pop(ExecutionContext *ctx) { Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) - return instance->array.pop_back(); + if (ArrayObject *instance = self.asArrayObject()) { + Value v = instance->getValueChecked(ctx, instance->array.back()); + instance->array.pop_back(); + return v; + } Value r1 = self.property(ctx, ctx->engine->id_length); quint32 r2 = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; @@ -1729,9 +1732,10 @@ Value ArrayPrototype::method_reverse(ExecutionContext *ctx) int lo = 0, hi = instance->array.length() - 1; + // ### for (; lo < hi; ++lo, --hi) { - Value tmp = instance->array.at(lo); - instance->array.set(lo, instance->array.at(hi)); + Value tmp = instance->getElement(ctx, lo); + instance->array.set(lo, instance->getElement(ctx, hi)); instance->array.set(hi, tmp); } return Value::undefinedValue(); @@ -1743,7 +1747,9 @@ Value ArrayPrototype::method_shift(ExecutionContext *ctx) if (!instance) ctx->throwUnimplemented(QStringLiteral("Array.prototype.shift")); - return instance->array.pop_front(); + Value v = instance->getValueChecked(ctx, instance->array.front()); + instance->array.pop_front(); + return v; } Value ArrayPrototype::method_slice(ExecutionContext *ctx) @@ -1779,7 +1785,7 @@ Value ArrayPrototype::method_sort(ExecutionContext *ctx) ctx->throwUnimplemented(QStringLiteral("Array.prototype.sort")); Value comparefn = ctx->argument(0); - instance->array.sort(ctx, comparefn); + instance->array.sort(ctx, instance, comparefn); return ctx->thisObject; } @@ -1829,22 +1835,17 @@ Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); Value searchValue; - int fromIndex = 0; + uint fromIndex = 0; if (ctx->argumentCount == 1) searchValue = ctx->argument(0); else if (ctx->argumentCount == 2) { searchValue = ctx->argument(0); - fromIndex = ctx->argument(1).toInteger(ctx); + fromIndex = ctx->argument(1).toUInt32(ctx); } else __qmljs_throw_type_error(ctx); - for (Array::iterator it = instance->array.find(fromIndex), end = instance->array.end(); it != end; ++it) { - if (__qmljs_strict_equal(instance->array.at(*it), searchValue)) - return Value::fromInt32(it.key()); - } - - return Value::fromInt32(-1); + return instance->array.indexOf(searchValue, fromIndex, ctx, instance); } Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) @@ -1864,7 +1865,7 @@ Value ArrayPrototype::method_every(ExecutionContext *ctx) bool ok = true; // ### for (uint k = 0; ok && k < instance->array.length(); ++k) { - Value v = instance->array.at(k); + Value v = instance->getElement(ctx, k); if (v.isUndefined()) continue; @@ -1889,7 +1890,7 @@ Value ArrayPrototype::method_some(ExecutionContext *ctx) bool ok = false; // ### for (uint k = 0; !ok && k < instance->array.length(); ++k) { - Value v = instance->array.at(k); + Value v = instance->getElement(ctx, k); if (v.isUndefined()) continue; @@ -1913,7 +1914,7 @@ Value ArrayPrototype::method_forEach(ExecutionContext *ctx) Value thisArg = ctx->argument(1); // ### for (quint32 k = 0; k < instance->array.length(); ++k) { - Value v = instance->array.at(k); + Value v = instance->getElement(ctx, k); if (v.isUndefined()) continue; Value args[3]; @@ -1936,7 +1937,7 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); a->array.setLength(instance->array.length()); for (quint32 k = 0; k < instance->array.length(); ++k) { - Value v = instance->array.at(k); + Value v = instance->getElement(ctx, k); if (v.isUndefined()) continue; Value args[3]; @@ -1959,7 +1960,7 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); for (quint32 k = 0; k < instance->array.length(); ++k) { - Value v = instance->array.at(k); + Value v = instance->getElement(ctx, k); if (v.isUndefined()) continue; Value args[3]; @@ -1986,7 +1987,7 @@ Value ArrayPrototype::method_reduce(ExecutionContext *ctx) Value initialValue = ctx->argument(1); Value acc = initialValue; for (quint32 k = 0; k < instance->array.length(); ++k) { - Value v = instance->array.at(k); + Value v = instance->getElement(ctx, k); if (v.isUndefined()) continue; @@ -2016,7 +2017,7 @@ Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) Value initialValue = ctx->argument(1); Value acc = initialValue; for (int k = instance->array.length() - 1; k != -1; --k) { - Value v = instance->array.at(k); + Value v = instance->getElement(ctx, k); if (v.isUndefined()) continue; @@ -2126,10 +2127,8 @@ Value FunctionPrototype::method_apply(ExecutionContext *ctx) QVector args; if (ArrayObject *arr = arg.asArrayObject()) { - const Array &actuals = arr->array; - - for (quint32 i = 0; i < actuals.length(); ++i) { - Value a = actuals.at(i); + for (quint32 i = 0; i < arr->array.length(); ++i) { + Value a = arr->getElement(ctx, i); args.append(a); } } else if (!(arg.isUndefined() || arg.isNull())) { -- cgit v1.2.3 From 6c0a7558f6b90647abff730e71f78f0354d03413 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 10 Jan 2013 15:11:32 +0100 Subject: Always store array index properties in the array Property names that represent a valid array index are now always stored in the Array inside of Object. String hash values actually map to the index value for hashes smaller then INT_MAX, making the lookup rather of these properties rather fast. Change-Id: Ice4dcd9d92fb488e6bfe7c73838a8b4da67a1906 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 306 ++++++++++++++++++++++++++++++++++++++++++++++------- qmljs_objects.h | 31 ++++-- qmljs_runtime.cpp | 20 ++-- qv4array.cpp | 1 + qv4array.h | 27 +++-- qv4ecmaobjects.cpp | 22 ++-- qv4managed.h | 9 +- qv4string.cpp | 58 ++++++++-- qv4string.h | 24 ++++- 9 files changed, 399 insertions(+), 99 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 87d8a76569..bba472495b 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -99,11 +99,6 @@ Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p return getValue(ctx, p); } -Value Object::getElement(ExecutionContext *ctx, uint index) const -{ - return getValueChecked(ctx, array.at(index)); -} - bool Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx) { bool hasProperty = false; @@ -188,6 +183,14 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *, String *name) return 0; } +PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *, uint index) +{ + PropertyDescriptor *p = array.at(index); + if(p && p->type != PropertyDescriptor::Generic) + return p; + return 0; +} + // Section 8.12.2 PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name) { @@ -204,14 +207,35 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, Str return 0; } +PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *, uint index) +{ + Object *o = this; + while (o) { + PropertyDescriptor *p = array.at(index); + if(p && p->type != PropertyDescriptor::Generic) + return p; + o = o->prototype; + } + return 0; +} + // Section 8.12.3 Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __get__(ctx, idx, hasProperty); + if (name->isEqualTo(ctx->engine->id___proto__)) { if (hasProperty) *hasProperty = true; return Value::fromObject(prototype); } + if (isArray && name->isEqualTo(ctx->engine->id_length)) { + if (hasProperty) + *hasProperty = true; + return Value::fromDouble(array.length()); + } if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name)) { if (hasProperty) @@ -224,9 +248,27 @@ Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) return Value::undefinedValue(); } +Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) +{ + if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, index)) { + if (hasProperty) + *hasProperty = true; + return getValue(ctx, p); + } + + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); +} + + // Section 8.12.4 bool Object::__canPut__(ExecutionContext *ctx, String *name) { + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __canPut__(ctx, idx); + if (PropertyDescriptor *p = __getOwnProperty__(ctx, name)) { if (p->isAccessor()) return p->set != 0; @@ -247,9 +289,35 @@ bool Object::__canPut__(ExecutionContext *ctx, String *name) return extensible; } +bool Object::__canPut__(ExecutionContext *ctx, uint index) +{ + if (PropertyDescriptor *p = __getOwnProperty__(ctx, index)) { + if (p->isAccessor()) + return p->set != 0; + return p->isWritable(); + } + + if (! prototype) + return extensible; + + if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, index)) { + if (p->isAccessor()) + return p->set != 0; + if (!extensible) + return false; + return p->isWritable(); + } + + return extensible; +} + // Section 8.12.5 void Object::__put__(ExecutionContext *ctx, String *name, Value value) { + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __put__(ctx, idx, value); + // clause 1 if (!__canPut__(ctx, name)) goto reject; @@ -297,18 +365,80 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) __qmljs_throw_type_error(ctx); } +void Object::__put__(ExecutionContext *ctx, uint index, Value value) +{ + // clause 1 + if (!__canPut__(ctx, index)) + goto reject; + + if (!members) + members.reset(new PropertyTable()); + + { + // Clause 2 + PropertyDescriptor *pd = __getOwnProperty__(ctx, index); + // Clause 3 + if (pd && pd->isData()) { + // spec says to call [[DefineOwnProperty]] with { [[Value]]: value } + + // ### to simplify and speed up we should expand the relevant parts here (clauses 6,7,9,10,12,13) + PropertyDescriptor desc = PropertyDescriptor::fromValue(value); + __defineOwnProperty__(ctx, index, &desc); + return; + } + + // clause 4 + if (!pd && prototype) + pd = prototype->__getPropertyDescriptor__(ctx, index); + + // Clause 5 + if (pd && pd->isAccessor()) { + assert(pd->set != 0); + + Value args[1]; + args[0] = value; + pd->set->call(ctx, Value::fromObject(this), args, 1); + return; + } + + array.set(index, value); + return; + } + + reject: + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); +} + // Section 8.12.6 bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const { + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __hasProperty__(ctx, idx); + if (members && members->find(name) != 0) return true; return prototype ? prototype->__hasProperty__(ctx, name) : false; } +bool Object::__hasProperty__(const ExecutionContext *ctx, uint index) const +{ + const PropertyDescriptor *p = array.at(index); + if (p && p->type != PropertyDescriptor::Generic) + return true; + + return prototype ? prototype->__hasProperty__(ctx, index) : false; +} + // Section 8.12.7 bool Object::__delete__(ExecutionContext *ctx, String *name) { + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __delete__(ctx, idx); + if (members) { if (PropertyTableEntry *entry = members->findEntry(name)) { if (entry->descriptor.isConfigurable()) { @@ -323,9 +453,22 @@ bool Object::__delete__(ExecutionContext *ctx, String *name) return true; } +bool Object::__delete__(ExecutionContext *ctx, uint index) +{ + if (array.deleteIndex(index)) + return true; + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; +} + // Section 8.12.9 bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc) { + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __defineOwnProperty__(ctx, idx, desc); + if (!members) members.reset(new PropertyTable()); @@ -404,6 +547,83 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property return false; } +bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, PropertyDescriptor *desc) +{ + // Clause 1 + PropertyDescriptor *current = __getOwnProperty__(ctx, index); + if (!current) { + // clause 3 + if (!extensible) + goto reject; + // clause 4 + PropertyDescriptor *pd = array.insert(index); + *pd = *desc; + pd->fullyPopulated(); + return true; + } + + // clause 5 + if (desc->isEmpty()) + return true; + + // clause 6 + if (desc->isSubset(current)) + return true; + + // clause 7 + if (!current->isConfigurable()) { + if (desc->isConfigurable()) + goto reject; + if (desc->enumberable != PropertyDescriptor::Undefined && desc->enumberable != current->enumberable) + goto reject; + } + + // clause 8 + if (desc->isGeneric()) + goto accept; + + // clause 9 + if (current->isData() != desc->isData()) { + // 9a + if (!current->isConfigurable()) + goto reject; + if (current->isData()) { + // 9b + current->type = PropertyDescriptor::Accessor; + current->writable = PropertyDescriptor::Undefined; + current->get = 0; + current->set = 0; + } else { + // 9c + current->type = PropertyDescriptor::Data; + current->writable = PropertyDescriptor::Disabled; + current->value = Value::undefinedValue(); + } + } else if (current->isData() && desc->isData()) { // clause 10 + if (!current->isConfigurable() && !current->isWritable()) { + if (desc->isWritable() || !current->value.sameValue(desc->value)) + goto reject; + } + } else { // clause 10 + assert(current->isAccessor() && desc->isAccessor()); + if (!current->isConfigurable()) { + if ((desc->get && current->get != desc->get) || + (desc->set && current->set != desc->set)) + goto reject; + } + } + + accept: + + *current += *desc; + return true; + reject: + qDebug() << "___put__ rejected index=" << index; + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; +} + bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, PropertyDescriptor *desc) { return __defineOwnProperty__(ctx, ctx->engine->identifier(name), desc); @@ -416,24 +636,46 @@ Value Object::call(ExecutionContext *context, Value , Value *, int) return Value::undefinedValue(); } -String *ForEachIteratorObject::nextPropertyName() +Value ForEachIteratorObject::nextPropertyName() { - PropertyTableEntry *p = 0; while (1) { if (!current) - return 0; + return Value::nullValue(); + + if (!arrayIndex) + arrayNode = current->array.sparseBegin(); + + // sparse arrays + if (arrayNode) { + uint index = arrayNode->key(); + PropertyDescriptor *p = current->array.at(index); + arrayNode = arrayNode->nextNode(); + if (arrayNode == current->array.sparseEnd()) { + arrayNode = 0; + arrayIndex = UINT_MAX; + } + if (p && p->isEnumerable()) + return Value::fromDouble(index); + } + // dense arrays + if (arrayIndex < current->array.length()) { + PropertyDescriptor *p = current->array.at(arrayIndex); + ++arrayIndex; + if (p && p->isEnumerable()) + return Value::fromDouble(arrayIndex); + } - // ### index array data as well - ++tableIndex; - if (!current->members || tableIndex > current->members->_propertyCount) { + if (!current->members || tableIndex > (uint)current->members->_propertyCount) { current = current->prototype; - tableIndex = -1; + arrayIndex = 0; + tableIndex = 0; continue; } - p = current->members->_properties[tableIndex]; + PropertyTableEntry *p = current->members->_properties[tableIndex]; + ++tableIndex; // ### check that it's not a repeated attribute if (p && p->descriptor.isEnumerable()) - return p->name; + return Value::fromString(p->name); } } @@ -446,21 +688,11 @@ void ForEachIteratorObject::getCollectables(QVector &objects) objects.append(current); } -Value ArrayObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) -{ - if (name->isEqualTo(ctx->engine->id_length)) { - if (hasProperty) - *hasProperty = true; - return Value::fromDouble(array.length()); - } - return Object::__get__(ctx, name, hasProperty); -} - bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) { if (index.isNumber()) { const quint32 idx = index.toUInt32(ctx); - Value v = getElement(ctx, idx); + Value v = __get__(ctx, idx); v = op(v, rhs, ctx); array.set(idx, v); return true; @@ -934,28 +1166,20 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterC } } -Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) +Value ArgumentsObject::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) { - if (!ctx->strictMode) { - bool ok = false; - currentIndex = name->toQString().toInt(&ok); - if (!ok) - currentIndex = -1; - } - Value result = Object::__get__(ctx, name, hasProperty); + if (!ctx->strictMode) + currentIndex = index; + Value result = Object::__get__(ctx, index, hasProperty); currentIndex = -1; return result; } -void ArgumentsObject::__put__(ExecutionContext *ctx, String *name, Value value) +void ArgumentsObject::__put__(ExecutionContext *ctx, uint index, Value value) { - if (!ctx->strictMode) { - bool ok = false; - currentIndex = name->toQString().toInt(&ok); - if (!ok) - currentIndex = -1; - } - Object::__put__(ctx, name, value); + if (!ctx->strictMode) + currentIndex = index; + Object::__put__(ctx, index, value); currentIndex = -1; } diff --git a/qmljs_objects.h b/qmljs_objects.h index 55f120c843..df368dc0ee 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -119,14 +119,25 @@ struct Object: Managed { virtual ErrorObject *asErrorObject() { return 0; } virtual ArgumentsObject *asArgumentsObject() { return 0; } - virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); virtual PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); + virtual PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name); + PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, uint index); + + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); + virtual Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); + virtual void __put__(ExecutionContext *ctx, String *name, Value value); + virtual void __put__(ExecutionContext *ctx, uint index, Value value); + virtual bool __canPut__(ExecutionContext *ctx, String *name); + virtual bool __canPut__(ExecutionContext *ctx, uint index); virtual bool __hasProperty__(const ExecutionContext *ctx, String *name) const; + virtual bool __hasProperty__(const ExecutionContext *ctx, uint index) const; virtual bool __delete__(ExecutionContext *ctx, String *name); + virtual bool __delete__(ExecutionContext *ctx, uint index); virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, uint index, PropertyDescriptor *desc); bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, PropertyDescriptor *desc); virtual Value call(ExecutionContext *context, Value, Value *, int); @@ -139,7 +150,6 @@ struct Object: Managed { Value getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const; Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const; Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const; - Value getElement(ExecutionContext *ctx, uint index) const; bool inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); @@ -159,11 +169,13 @@ protected: struct ForEachIteratorObject: Object { Object *object; Object *current; // inside the prototype chain - int tableIndex; - ForEachIteratorObject(Object *o) : object(o), current(o), tableIndex(-1) {} + SparseArrayNode *arrayNode; + uint arrayIndex; + uint tableIndex; + ForEachIteratorObject(Object *o) : object(o), current(o), arrayNode(0), arrayIndex(0), tableIndex(0) {} virtual QString className() { return QStringLiteral("__ForEachIteratorObject"); } - String *nextPropertyName(); + Value nextPropertyName(); protected: virtual void getCollectables(QVector &objects); @@ -198,11 +210,10 @@ struct DateObject: Object { }; struct ArrayObject: Object { - ArrayObject() {} - ArrayObject(const Array &value): Object(value) {} + ArrayObject() { isArray = true; } + ArrayObject(const Array &value): Object(value) { isArray = true; } virtual QString className() { return QStringLiteral("Array"); } virtual ArrayObject *asArrayObject() { return this; } - virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); }; @@ -405,8 +416,8 @@ struct ArgumentsObject: Object { virtual QString className() { return QStringLiteral("Arguments"); } virtual ArgumentsObject *asArgumentsObject() { return this; } - virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); - virtual void __put__(ExecutionContext *ctx, String *name, Value value); + virtual Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); + virtual void __put__(ExecutionContext *ctx, uint index, Value value); static Value method_getArg(ExecutionContext *ctx); static Value method_setArg(ExecutionContext *ctx); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index ed20de7cd6..76c45ea25b 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -169,13 +169,14 @@ Value __qmljs_string_literal_function(ExecutionContext *ctx) Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index) { - if (ArrayObject *a = base.asArrayObject()) { + if (Object *o = base.asObject()) { uint n = UINT_MAX; if (index.isInteger()) n = index.integerValue(); else if (index.isDouble()) n = index.doubleValue(); - a->array.deleteIndex(n); + if (n < UINT_MAX) + o->__delete__(ctx, n); } String *name = index.toString(ctx); @@ -556,16 +557,18 @@ void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Val Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) { if (index.isNumber()) { + uint idx = index.toUInt32(ctx); + + if (Object *o = object.asObject()) + return o->__get__(ctx, idx); + if (object.isString()) { - const QString s = object.stringValue()->toQString().mid(index.toUInt32(ctx), 1); + const QString s = object.stringValue()->toQString().mid(idx, 1); if (s.isNull()) return Value::undefinedValue(); else return Value::fromString(ctx, s); } - - if (ArrayObject *a = object.asArrayObject()) - return a->getElement(ctx, index.toUInt32(ctx)); } String *name = index.toString(ctx); @@ -607,10 +610,7 @@ Value __qmljs_foreach_next_property_name(Value foreach_iterator) ForEachIteratorObject *it = static_cast(foreach_iterator.objectValue()); assert(it->className() == QLatin1String("__ForEachIteratorObject")); - String *s = it->nextPropertyName(); - if (!s) - return Value::nullValue(); - return Value::fromString(s); + return it->nextPropertyName(); } diff --git a/qv4array.cpp b/qv4array.cpp index 27977103c9..32b465d62d 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -392,6 +392,7 @@ SparseArrayNode *SparseArray::createNode(uint sl, SparseArrayNode *parent, bool node->left = 0; node->right = 0; node->size_left = sl; + node->value = UINT_MAX; ++numEntries; if (parent) { diff --git a/qv4array.h b/qv4array.h index ad936f9ce6..85c42b8f0f 100644 --- a/qv4array.h +++ b/qv4array.h @@ -507,7 +507,8 @@ public: } #endif - void set(uint index, const PropertyDescriptor *pd) { + PropertyDescriptor *insert(uint index) { + PropertyDescriptor *pd; if (!sparse && (index < 0x1000 || index < len + (len >> 2))) { if (index + offset >= (uint)values.size()) { values.resize(offset + index + 1); @@ -516,21 +517,26 @@ public: values[i].value.tag = Value::_Undefined_Type; } } - *descriptor(index) = *pd; + pd = descriptor(index); } else { initSparse(); SparseArrayNode *n = sparse->insert(index); - uint idx = allocValue(); - *descriptor(idx) = *pd; - n->value = idx; + if (n->value == UINT_MAX) + n->value = allocValue(); + pd = descriptor(n->value); } if (index >= len) len = index + 1; + return pd; + } + + void set(uint index, const PropertyDescriptor *pd) { + *insert(index) = *pd; } + void set(uint index, Value value) { - PropertyDescriptor pd; - fillDescriptor(&pd, value); - set(index, &pd); + PropertyDescriptor *pd = insert(index); + fillDescriptor(pd, value); } bool deleteIndex(uint index) { @@ -544,7 +550,7 @@ public: if (n) pd = descriptor(n->value); } - if (!pd) + if (!pd || pd->type == PropertyDescriptor::Generic) return true; if (!pd->isConfigurable()) return false; @@ -671,6 +677,9 @@ public: --len; } + SparseArrayNode *sparseBegin() { return sparse ? sparse->begin() : 0; } + SparseArrayNode *sparseEnd() { return sparse ? sparse->end() : 0; } + void concat(const Array &other); void sort(ExecutionContext *context, Object *thisObject, const Value &comparefn); void splice(double start, double deleteCount, const QVector &, Array &); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 85cd83d72a..405f2d7709 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1651,7 +1651,7 @@ Value ArrayPrototype::method_join(ExecutionContext *ctx) if (i) R += r4; - Value e = a->getElement(ctx, i); + Value e = a->__get__(ctx, i); if (! (e.isUndefined() || e.isNull())) R += e.toString(ctx)->toQString(); } @@ -1734,8 +1734,8 @@ Value ArrayPrototype::method_reverse(ExecutionContext *ctx) // ### for (; lo < hi; ++lo, --hi) { - Value tmp = instance->getElement(ctx, lo); - instance->array.set(lo, instance->getElement(ctx, hi)); + Value tmp = instance->__get__(ctx, lo); + instance->array.set(lo, instance->__get__(ctx, hi)); instance->array.set(hi, tmp); } return Value::undefinedValue(); @@ -1865,7 +1865,7 @@ Value ArrayPrototype::method_every(ExecutionContext *ctx) bool ok = true; // ### for (uint k = 0; ok && k < instance->array.length(); ++k) { - Value v = instance->getElement(ctx, k); + Value v = instance->__get__(ctx, k); if (v.isUndefined()) continue; @@ -1890,7 +1890,7 @@ Value ArrayPrototype::method_some(ExecutionContext *ctx) bool ok = false; // ### for (uint k = 0; !ok && k < instance->array.length(); ++k) { - Value v = instance->getElement(ctx, k); + Value v = instance->__get__(ctx, k); if (v.isUndefined()) continue; @@ -1914,7 +1914,7 @@ Value ArrayPrototype::method_forEach(ExecutionContext *ctx) Value thisArg = ctx->argument(1); // ### for (quint32 k = 0; k < instance->array.length(); ++k) { - Value v = instance->getElement(ctx, k); + Value v = instance->__get__(ctx, k); if (v.isUndefined()) continue; Value args[3]; @@ -1937,7 +1937,7 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); a->array.setLength(instance->array.length()); for (quint32 k = 0; k < instance->array.length(); ++k) { - Value v = instance->getElement(ctx, k); + Value v = instance->__get__(ctx, k); if (v.isUndefined()) continue; Value args[3]; @@ -1960,7 +1960,7 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); for (quint32 k = 0; k < instance->array.length(); ++k) { - Value v = instance->getElement(ctx, k); + Value v = instance->__get__(ctx, k); if (v.isUndefined()) continue; Value args[3]; @@ -1987,7 +1987,7 @@ Value ArrayPrototype::method_reduce(ExecutionContext *ctx) Value initialValue = ctx->argument(1); Value acc = initialValue; for (quint32 k = 0; k < instance->array.length(); ++k) { - Value v = instance->getElement(ctx, k); + Value v = instance->__get__(ctx, k); if (v.isUndefined()) continue; @@ -2017,7 +2017,7 @@ Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) Value initialValue = ctx->argument(1); Value acc = initialValue; for (int k = instance->array.length() - 1; k != -1; --k) { - Value v = instance->getElement(ctx, k); + Value v = instance->__get__(ctx, k); if (v.isUndefined()) continue; @@ -2128,7 +2128,7 @@ Value FunctionPrototype::method_apply(ExecutionContext *ctx) if (ArrayObject *arr = arg.asArrayObject()) { for (quint32 i = 0; i < arr->array.length(); ++i) { - Value a = arr->getElement(ctx, i); + Value a = arr->__get__(ctx, i); args.append(a); } } else if (!(arg.isUndefined() || arg.isNull())) { diff --git a/qv4managed.h b/qv4managed.h index 6ccc68c777..f1bc3e360f 100644 --- a/qv4managed.h +++ b/qv4managed.h @@ -64,7 +64,7 @@ private: void operator = (const Managed &other); protected: - Managed() : markBit(0), inUse(1), extensible(true), unused(0) { } + Managed() : markBit(0), inUse(1), extensible(true), isArray(false), unused(0) { } virtual ~Managed(); public: @@ -78,6 +78,8 @@ private: friend class MemoryManager; friend struct Object; friend struct ObjectPrototype; + friend struct Array; + friend struct ArrayPrototype; friend struct FunctionObject; friend struct ExecutionContext; friend struct ScriptFunction; @@ -88,13 +90,14 @@ private: quintptr markBit : 1; quintptr inUse : 1; quintptr extensible : 1; // used by Object + quintptr isArray : 1; // used by Object & Array quintptr needsActivation : 1; // used by FunctionObject quintptr usesArgumentsObject : 1; // used by FunctionObject quintptr strictMode : 1; // used by FunctionObject #if CPU(X86_64) - quintptr unused : 58; + quintptr unused : 57; #elif CPU(X86) - quintptr unused : 26; + quintptr unused : 25; #else #error "implement me" #endif diff --git a/qv4string.cpp b/qv4string.cpp index 8e9d02b231..eb1e2603c1 100644 --- a/qv4string.cpp +++ b/qv4string.cpp @@ -44,21 +44,59 @@ namespace QQmlJS { namespace VM { -uint String::asArrayIndex() const +static uint toArrayIndex(const QChar *ch, const QChar *end) { - uint u = 0; + uint i = ch->unicode() - '0'; + if (i > 9) + return String::InvalidArrayIndex; + ++ch; + if (i == 0 && ch == end) + return i; + while (ch < end) { + uint x = ch->unicode() - '0'; + if (x > 9) + return String::InvalidArrayIndex; + uint n = i*10 + x; + if (n < i) + // overflow + return String::InvalidArrayIndex; + i = n; + ++ch; + } + return i; +} + +uint String::asArrayIndexSlow() const +{ + if (_hashValue < LargestHashedArrayIndex) + return _hashValue; + const QChar *ch = _text.constData(); const QChar *end = ch + _text.length(); + return toArrayIndex(ch, end); +} + +void String::createHashValue() const +{ + const QChar *ch = _text.constData(); + const QChar *end = ch + _text.length(); + + // array indices get their number as hash value, for large numbers we set to INT_MAX + _hashValue = toArrayIndex(ch, end); + if (_hashValue < UINT_MAX) { + if (_hashValue > INT_MAX) + _hashValue = INT_MAX; + return; + } + + uint h = 0xffffffff; while (ch < end) { - if (ch->unicode() < '0' && ch->unicode() > '9') - return InvalidArrayIndex; - uint n = u*10 + ch->unicode() - '0'; - if (n < u) - // overflow - return InvalidArrayIndex; - u = n; + h = 31 * h + ch->unicode(); + ++ch; } - return u; + + // set highest bit to mark it as a non number + _hashValue = h | 0xf0000000; } } diff --git a/qv4string.h b/qv4string.h index 4f66ee5e83..3464af5688 100644 --- a/qv4string.h +++ b/qv4string.h @@ -61,20 +61,34 @@ struct String { } inline unsigned hashValue() const { - if (! _hashValue) - _hashValue = qHash(_text); + if (_hashValue == InvalidHashValue) + createHashValue(); return _hashValue; } - enum { InvalidArrayIndex = 0xffffffff }; - uint asArrayIndex() const; + enum { + InvalidArrayIndex = 0xffffffff, + LargestHashedArrayIndex = 0x7fffffff, + InvalidHashValue = 0xffffffff + }; + uint asArrayIndex() const { + if (_hashValue == InvalidHashValue) + createHashValue(); + if (_hashValue > LargestHashedArrayIndex) + return InvalidArrayIndex; + if (_hashValue < LargestHashedArrayIndex) + return _hashValue; + return asArrayIndexSlow(); + } + uint asArrayIndexSlow() const; private: friend class StringPool; String(const QString &text) - : _text(text), _hashValue(0) {} + : _text(text), _hashValue(InvalidHashValue) {} private: + void createHashValue() const; QString _text; mutable unsigned _hashValue; }; -- cgit v1.2.3 From 25fc37fa3894cb52e8ddee3ccaae0c55e8b9a11a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 10 Jan 2013 15:48:56 +0100 Subject: Fix the foreach iterator to also iterate over array values Change-Id: I883b82f5b62ba051a1926bf66e7461286ce10bb0 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index bba472495b..cc05e4e3a0 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -647,22 +647,22 @@ Value ForEachIteratorObject::nextPropertyName() // sparse arrays if (arrayNode) { - uint index = arrayNode->key(); - PropertyDescriptor *p = current->array.at(index); - arrayNode = arrayNode->nextNode(); - if (arrayNode == current->array.sparseEnd()) { - arrayNode = 0; - arrayIndex = UINT_MAX; + while (arrayNode != current->array.sparseEnd()) { + uint index = arrayNode->key(); + PropertyDescriptor *p = current->array.at(index); + arrayNode = arrayNode->nextNode(); + if (p && p->isEnumerable()) + return Value::fromDouble(index); } - if (p && p->isEnumerable()) - return Value::fromDouble(index); + arrayNode = 0; + arrayIndex = UINT_MAX; } // dense arrays - if (arrayIndex < current->array.length()) { + while (arrayIndex < current->array.length()) { PropertyDescriptor *p = current->array.at(arrayIndex); ++arrayIndex; if (p && p->isEnumerable()) - return Value::fromDouble(arrayIndex); + return Value::fromDouble(arrayIndex - 1); } if (!current->members || tableIndex > (uint)current->members->_propertyCount) { -- cgit v1.2.3 From cbc9306f705477396896aba17550f104c6c40d81 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 10 Jan 2013 17:33:06 +0100 Subject: Fix a bug in qmljs_set_element and some related to for...in Change-Id: Iec876f12529adeba3bc7b9c927e38100fff88042 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 4 ++-- qmljs_engine.h | 2 +- qmljs_objects.cpp | 15 +++++++++++---- qmljs_objects.h | 4 +++- qmljs_runtime.cpp | 12 ++++++------ 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index bac0dc813e..b558b45a88 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -413,9 +413,9 @@ Object *ExecutionEngine::newActivationObject() return new (memoryManager) Object(); } -Object *ExecutionEngine::newForEachIteratorObject(Object *o) +Object *ExecutionEngine::newForEachIteratorObject(ExecutionContext *ctx, Object *o) { - return new (memoryManager) ForEachIteratorObject(o); + return new (memoryManager) ForEachIteratorObject(ctx, o); } } // namespace VM diff --git a/qmljs_engine.h b/qmljs_engine.h index e281620b21..a2cba073ac 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -203,7 +203,7 @@ struct ExecutionEngine Object *newMathObject(ExecutionContext *ctx); Object *newActivationObject(); - Object *newForEachIteratorObject(Object *o); + Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o); }; } // namespace VM diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index cc05e4e3a0..441041783c 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -176,8 +176,12 @@ void Object::getCollectables(QVector &objects) } // Section 8.12.1 -PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *, String *name) +PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *name) { + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __getOwnProperty__(ctx, idx); + if (members) return members->find(name); return 0; @@ -194,7 +198,10 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *, uint index) // Section 8.12.2 PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name) { - Q_UNUSED(ctx); + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __getPropertyDescriptor__(ctx, idx); + Object *o = this; while (o) { @@ -652,7 +659,7 @@ Value ForEachIteratorObject::nextPropertyName() PropertyDescriptor *p = current->array.at(index); arrayNode = arrayNode->nextNode(); if (p && p->isEnumerable()) - return Value::fromDouble(index); + return __qmljs_to_string(Value::fromDouble(index), context); } arrayNode = 0; arrayIndex = UINT_MAX; @@ -662,7 +669,7 @@ Value ForEachIteratorObject::nextPropertyName() PropertyDescriptor *p = current->array.at(arrayIndex); ++arrayIndex; if (p && p->isEnumerable()) - return Value::fromDouble(arrayIndex - 1); + return __qmljs_to_string(Value::fromDouble(arrayIndex - 1), context); } if (!current->members || tableIndex > (uint)current->members->_propertyCount) { diff --git a/qmljs_objects.h b/qmljs_objects.h index df368dc0ee..ba3e9fffba 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -167,12 +167,14 @@ protected: }; struct ForEachIteratorObject: Object { + ExecutionContext *context; Object *object; Object *current; // inside the prototype chain SparseArrayNode *arrayNode; uint arrayIndex; uint tableIndex; - ForEachIteratorObject(Object *o) : object(o), current(o), arrayNode(0), arrayIndex(0), tableIndex(0) {} + ForEachIteratorObject(ExecutionContext *ctx, Object *o) + : context(ctx), object(o), current(o), arrayNode(0), arrayIndex(0), tableIndex(0) {} virtual QString className() { return QStringLiteral("__ForEachIteratorObject"); } Value nextPropertyName(); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 76c45ea25b..a96bfc0847 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -581,25 +581,25 @@ Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value value) { + if (! object.isObject()) + object = __qmljs_to_object(object, ctx); + if (index.isNumber()) { - if (ArrayObject *a = object.asArrayObject()) { - a->array.set(index.toUInt32(ctx), value); + if (Object *o = object.asObject()) { + o->__put__(ctx, index.toUInt32(ctx), value); return; } } String *name = index.toString(ctx); - if (! object.isObject()) - object = __qmljs_to_object(object, ctx); - object.objectValue()->__put__(ctx, name, value); } Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx) { in = __qmljs_to_object(in, ctx); - Object *it = ctx->engine->newForEachIteratorObject(in.objectValue()); + Object *it = ctx->engine->newForEachIteratorObject(ctx, in.objectValue()); return Value::fromObject(it); } -- cgit v1.2.3 From 1fd1672122f9770052e09c26ba26b7cfa72d17cd Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 10 Jan 2013 17:47:07 +0100 Subject: Don't initialize the property table when not needed Change-Id: Ibf6339080e27f2cba070529ca918aefe74393f7b Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 441041783c..1382433123 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -378,9 +378,6 @@ void Object::__put__(ExecutionContext *ctx, uint index, Value value) if (!__canPut__(ctx, index)) goto reject; - if (!members) - members.reset(new PropertyTable()); - { // Clause 2 PropertyDescriptor *pd = __getOwnProperty__(ctx, index); -- cgit v1.2.3 From 71a32c8135df4ceb0812413822ee37463a7756ed Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 10 Jan 2013 22:42:58 +0100 Subject: Refactored iteration over object properties Many methods didn't take index based properties into account when iterating. Change-Id: Ic4924ab20f0567e66d0beebd79b53f0daf51fc90 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 55 ++------------------ qmljs_objects.h | 14 ++--- qv4ecmaobjects.cpp | 132 ++++++++++++++++++++++++++-------------------- qv4objectiterator.cpp | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++ qv4objectiterator.h | 77 +++++++++++++++++++++++++++ qv4propertytable.h | 17 +++--- v4.pro | 6 ++- 7 files changed, 319 insertions(+), 123 deletions(-) create mode 100644 qv4objectiterator.cpp create mode 100644 qv4objectiterator.h diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 1382433123..b865fd1ed2 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -640,56 +640,11 @@ Value Object::call(ExecutionContext *context, Value , Value *, int) return Value::undefinedValue(); } -Value ForEachIteratorObject::nextPropertyName() -{ - while (1) { - if (!current) - return Value::nullValue(); - - if (!arrayIndex) - arrayNode = current->array.sparseBegin(); - - // sparse arrays - if (arrayNode) { - while (arrayNode != current->array.sparseEnd()) { - uint index = arrayNode->key(); - PropertyDescriptor *p = current->array.at(index); - arrayNode = arrayNode->nextNode(); - if (p && p->isEnumerable()) - return __qmljs_to_string(Value::fromDouble(index), context); - } - arrayNode = 0; - arrayIndex = UINT_MAX; - } - // dense arrays - while (arrayIndex < current->array.length()) { - PropertyDescriptor *p = current->array.at(arrayIndex); - ++arrayIndex; - if (p && p->isEnumerable()) - return __qmljs_to_string(Value::fromDouble(arrayIndex - 1), context); - } - - if (!current->members || tableIndex > (uint)current->members->_propertyCount) { - current = current->prototype; - arrayIndex = 0; - tableIndex = 0; - continue; - } - PropertyTableEntry *p = current->members->_properties[tableIndex]; - ++tableIndex; - // ### check that it's not a repeated attribute - if (p && p->descriptor.isEnumerable()) - return Value::fromString(p->name); - } -} - void ForEachIteratorObject::getCollectables(QVector &objects) { Object::getCollectables(objects); - if (object) - objects.append(object); - if (current) - objects.append(current); + if (it.object) + objects.append(it.object); } bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) @@ -1162,10 +1117,10 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterC PropertyDescriptor pd = PropertyDescriptor::fromAccessor(get, set); pd.configurable = PropertyDescriptor::Enabled; pd.enumberable = PropertyDescriptor::Enabled; - for (uint i = 0; i < formalParameterCount; ++i) - __defineOwnProperty__(context, QString::number(i), &pd); + for (uint i = 0; i < (uint)formalParameterCount; ++i) + __defineOwnProperty__(context, i, &pd); for (uint i = formalParameterCount; i < context->argumentCount; ++i) - Object::__put__(context, QString::number(i), context->arguments[i]); + Object::__put__(context, i, context->arguments[i]); defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); } } diff --git a/qmljs_objects.h b/qmljs_objects.h index ba3e9fffba..b1d55a6390 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -51,6 +51,7 @@ #include "qv4managed.h" #include "qv4propertydescriptor.h" #include "qv4propertytable.h" +#include "qv4objectiterator.h" #include #include @@ -66,6 +67,7 @@ namespace VM { struct Value; struct Function; struct Object; +struct ObjectIterator; struct BooleanObject; struct NumberObject; struct StringObject; @@ -164,20 +166,18 @@ struct Object: Managed { protected: virtual void getCollectables(QVector &objects); + + friend struct ObjectIterator; }; struct ForEachIteratorObject: Object { ExecutionContext *context; - Object *object; - Object *current; // inside the prototype chain - SparseArrayNode *arrayNode; - uint arrayIndex; - uint tableIndex; + ObjectIterator it; ForEachIteratorObject(ExecutionContext *ctx, Object *o) - : context(ctx), object(o), current(o), arrayNode(0), arrayIndex(0), tableIndex(0) {} + : context(ctx), it(o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) {} virtual QString className() { return QStringLiteral("__ForEachIteratorObject"); } - Value nextPropertyName(); + Value nextPropertyName() { return it.nextPropertyNameAsString(context); } protected: virtual void getCollectables(QVector &objects); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 405f2d7709..e349957094 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -602,18 +602,18 @@ Value ObjectPrototype::method_getOwnPropertyDescriptor(ExecutionContext *ctx) Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) { - Value O = ctx->argument(0); - if (! O.isObject()) + Object *O = ctx->argument(0).asObject(); + if (!O) ctx->throwTypeError(); ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); Array &a = array->array; - if (PropertyTable *members = O.objectValue()->members.data()) { - for (PropertyTableEntry **it = members->begin(), **end = members->end(); it != end; ++it) { - if (PropertyTableEntry *prop = *it) { - a.push_back(Value::fromString(prop->name)); - } - } + ObjectIterator it(O, ObjectIterator::NoFlags); + while (1) { + Value v = it.nextPropertyNameAsString(ctx); + if (v.isNull()) + break; + a.push_back(v); } return Value::fromObject(array); } @@ -621,7 +621,7 @@ Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) Value ObjectPrototype::method_create(ExecutionContext *ctx) { Value O = ctx->argument(0); - if (!O.isObject()) + if (!O.isObject() && !O.isNull()) ctx->throwTypeError(); Object *newObject = ctx->engine->newObject(); @@ -664,17 +664,19 @@ Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) Object *o = ctx->argument(1).toObject(ctx).objectValue(); - if (o->members) { - PropertyTable::iterator it = o->members->begin(); - while (it != o->members->end()) { - if ((*it) && (*it)->descriptor.isEnumerable()) { - String *name = (*it)->name; - PropertyDescriptor pd; - toPropertyDescriptor(ctx, o->__get__(ctx, name), &pd); - O.objectValue()->__defineOwnProperty__(ctx, name, &pd); - } - ++it; - } + ObjectIterator it(o, ObjectIterator::EnumberableOnly); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + PropertyDescriptor n; + toPropertyDescriptor(ctx, o->getValue(ctx, pd), &n); + if (name) + O.objectValue()->__defineOwnProperty__(ctx, name, &n); + else + O.objectValue()->__defineOwnProperty__(ctx, index, &n); } return O; @@ -687,12 +689,15 @@ Value ObjectPrototype::method_seal(ExecutionContext *ctx) Object *o = ctx->argument(0).objectValue(); o->extensible = false; - if (o->members) { - PropertyTable::iterator it = o->members->begin(); - while (it != o->members->end()) { - (*it)->descriptor.configurable = PropertyDescriptor::Disabled; - ++it; - } + + ObjectIterator it(o, ObjectIterator::NoFlags); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + pd->configurable = PropertyDescriptor::Disabled; } return ctx->argument(0); } @@ -704,14 +709,16 @@ Value ObjectPrototype::method_freeze(ExecutionContext *ctx) Object *o = ctx->argument(0).objectValue(); o->extensible = false; - if (o->members) { - PropertyTable::iterator it = o->members->begin(); - while (it != o->members->end()) { - if ((*it)->descriptor.isData()) - (*it)->descriptor.writable = PropertyDescriptor::Disabled; - (*it)->descriptor.configurable = PropertyDescriptor::Disabled; - ++it; - } + + ObjectIterator it(o, ObjectIterator::NoFlags); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + pd->writable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Disabled; } return ctx->argument(0); } @@ -734,13 +741,16 @@ Value ObjectPrototype::method_isSealed(ExecutionContext *ctx) Object *o = ctx->argument(0).objectValue(); if (o->extensible) return Value::fromBoolean(false); - if (o->members) { - PropertyTable::iterator it = o->members->begin(); - while (it != o->members->end()) { - if ((*it)->descriptor.configurable != PropertyDescriptor::Disabled) - return Value::fromBoolean(false); - ++it; - } + + ObjectIterator it(o, ObjectIterator::NoFlags); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + if (pd->configurable != PropertyDescriptor::Disabled) + return Value::fromBoolean(false); } return Value::fromBoolean(true); } @@ -753,16 +763,16 @@ Value ObjectPrototype::method_isFrozen(ExecutionContext *ctx) Object *o = ctx->argument(0).objectValue(); if (o->extensible) return Value::fromBoolean(false); - if (o->members) { - PropertyTable::iterator it = o->members->begin(); - while (it != o->members->end()) { - if ((*it)->descriptor.isData() && - ((*it)->descriptor.writable != PropertyDescriptor::Disabled)) - return Value::fromBoolean(false); - if ((*it)->descriptor.configurable != PropertyDescriptor::Disabled) - return Value::fromBoolean(false); - ++it; - } + + ObjectIterator it(o, ObjectIterator::NoFlags); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + if (pd->isWritable() || pd->isConfigurable()) + return Value::fromBoolean(false); } return Value::fromBoolean(true); } @@ -785,13 +795,21 @@ Value ObjectPrototype::method_keys(ExecutionContext *ctx) ArrayObject *a = ctx->engine->newArrayObject(); - if (o->members) { - PropertyTable::iterator it = o->members->begin(); - while (it != o->members->end()) { - if ((*it)->descriptor.isEnumerable()) - a->array.push_back(Value::fromString((*it)->name)); - ++it; + ObjectIterator it(o, ObjectIterator::EnumberableOnly); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + Value key; + if (name) { + key = Value::fromString(name); + } else { + key = Value::fromDouble(index); + key = __qmljs_to_string(key, ctx); } + a->array.push_back(key); } return Value::fromObject(a); diff --git a/qv4objectiterator.cpp b/qv4objectiterator.cpp new file mode 100644 index 0000000000..3a657180fc --- /dev/null +++ b/qv4objectiterator.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qv4objectiterator.h" +#include "qmljs_objects.h" + +namespace QQmlJS { +namespace VM { + +ObjectIterator::ObjectIterator(Object *o, uint flags) + : object(o) + , current(o) + , arrayNode(0) + , arrayIndex(0) + , tableIndex(0) + , flags(flags) +{ +} + +PropertyDescriptor *ObjectIterator::next(String **name, uint *index) +{ + PropertyDescriptor *p = 0; + *name = 0; + *index = UINT_MAX; + while (1) { + if (!current) + break; + + if (!arrayIndex) + arrayNode = current->array.sparseBegin(); + + // sparse arrays + if (arrayNode) { + while (arrayNode != current->array.sparseEnd()) { + int k = arrayNode->key(); + p = current->array.at(k); + arrayNode = arrayNode->nextNode(); + if (p && (!(flags & EnumberableOnly) || p->isEnumerable())) { + arrayIndex = k + 1; + *index = k; + return p; + } + } + arrayNode = 0; + arrayIndex = UINT_MAX; + } + // dense arrays + while (arrayIndex < current->array.length()) { + p = current->array.at(arrayIndex); + ++arrayIndex; + if (p && (!(flags & EnumberableOnly) || p->isEnumerable())) { + *index = arrayIndex - 1; + return p; + } + } + + if (!current->members || tableIndex >= (uint)current->members->_propertyCount) { + if (flags & WithProtoChain) + current = current->prototype; + else + current = 0; + arrayIndex = 0; + tableIndex = 0; + continue; + } + PropertyTableEntry *pt = current->members->_properties[tableIndex]; + ++tableIndex; + // ### check that it's not a repeated attribute + if (pt && (!(flags & EnumberableOnly) || pt->descriptor.isEnumerable())) { + *name = pt->name; + p = &pt->descriptor; + return p; + } + } + return 0; +} + +Value ObjectIterator::nextPropertyName() +{ + uint index; + String *name; + next(&name, &index); + if (name) + return Value::fromString(name); + if (index < UINT_MAX) + return Value::fromDouble(index); + return Value::nullValue(); +} + +Value ObjectIterator::nextPropertyNameAsString(ExecutionContext *context) +{ + uint index; + String *name; + next(&name, &index); + if (name) + return Value::fromString(name); + if (index < UINT_MAX) + return __qmljs_to_string(Value::fromDouble(index), context); + return Value::nullValue(); +} + +} +} + diff --git a/qv4objectiterator.h b/qv4objectiterator.h new file mode 100644 index 0000000000..db340de068 --- /dev/null +++ b/qv4objectiterator.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4OBJECTITERATOR_H +#define QV4OBJECTITERATOR_H + +#include "qmljs_value.h" + +namespace QQmlJS { +namespace VM { + +struct SparseArrayNode; +struct Object; +struct PropertyDescriptor; + +struct ObjectIterator +{ + enum Flags { + NoFlags = 0, + EnumberableOnly = 0x1, + WithProtoChain = 0x2 + }; + + Object *object; + Object *current; + SparseArrayNode *arrayNode; + uint arrayIndex; + uint tableIndex; + uint flags; + + ObjectIterator(Object *o, uint flags); + PropertyDescriptor *next(String **name, uint *index); + Value nextPropertyName(); + Value nextPropertyNameAsString(ExecutionContext *context); +}; + +} +} + +#endif diff --git a/qv4propertytable.h b/qv4propertytable.h index e194f52bfb..725598ab68 100644 --- a/qv4propertytable.h +++ b/qv4propertytable.h @@ -47,6 +47,8 @@ namespace QQmlJS { namespace VM { +struct ObjectIterator; + struct PropertyTableEntry { PropertyDescriptor descriptor; String *name; @@ -72,7 +74,7 @@ public: : _properties(0) , _buckets(0) , _freeList(0) - , _propertyCount(-1) + , _propertyCount(0) , _bucketCount(0) , _primeIdx(-1) , _allocated(0) @@ -80,16 +82,16 @@ public: ~PropertyTable() { - qDeleteAll(_properties, _properties + _propertyCount + 1); + qDeleteAll(_properties, _properties + _propertyCount); delete[] _properties; delete[] _buckets; } - inline bool isEmpty() const { return _propertyCount == -1; } + inline bool isEmpty() const { return _propertyCount == 0; } typedef PropertyTableEntry **iterator; inline iterator begin() const { return _properties; } - inline iterator end() const { return _properties + (_propertyCount + 1); } + inline iterator end() const { return _properties + _propertyCount; } void remove(PropertyTableEntry *prop) { @@ -139,7 +141,7 @@ public: if (PropertyTableEntry *prop = findEntry(name)) return &prop->descriptor; - if (++_propertyCount == _allocated) { + if (_propertyCount == _allocated) { if (! _allocated) _allocated = 4; else @@ -161,6 +163,7 @@ public: prop->index = _propertyCount; _properties[_propertyCount] = prop; + ++_propertyCount; if (! _buckets || 3 * _propertyCount >= 2 * _bucketCount) { rehash(); @@ -182,7 +185,7 @@ private: _buckets = new PropertyTableEntry *[_bucketCount]; std::fill(_buckets, _buckets + _bucketCount, (PropertyTableEntry *) 0); - for (int i = 0; i <= _propertyCount; ++i) { + for (int i = 0; i < _propertyCount; ++i) { PropertyTableEntry *prop = _properties[i]; PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; prop->next = bucket; @@ -204,7 +207,7 @@ private: } private: - friend struct ForEachIteratorObject; + friend struct ObjectIterator; PropertyTableEntry **_properties; PropertyTableEntry **_buckets; PropertyTableEntry *_freeList; diff --git a/v4.pro b/v4.pro index 75da88a9b7..b47a655ff1 100644 --- a/v4.pro +++ b/v4.pro @@ -27,7 +27,8 @@ SOURCES += main.cpp \ qv4mm.cpp \ qv4managed.cpp \ qv4array.cpp \ - qv4string.cpp + qv4string.cpp \ + qv4objectiterator.cpp HEADERS += \ qv4codegen_p.h \ @@ -49,7 +50,8 @@ HEADERS += \ qv4array.h \ qv4string.h \ qv4propertydescriptor.h \ - qv4propertytable.h + qv4propertytable.h \ + qv4objectiterator.h llvm { -- cgit v1.2.3 From c686fd7dc60cb249ffed7bb4bc9287c60a3504a1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 10 Jan 2013 23:22:04 +0100 Subject: Properly set the length property on arrays Change-Id: Ic9c571865ee9a2fae512dfe6d83c3805816fe4a1 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index b865fd1ed2..b067b3fc7b 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -461,7 +461,7 @@ bool Object::__delete__(ExecutionContext *ctx, uint index) { if (array.deleteIndex(index)) return true; - if (ctx->strictMode) + if (ctx->strictMode) __qmljs_throw_type_error(ctx); return false; } @@ -473,11 +473,19 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property if (idx != String::InvalidArrayIndex) return __defineOwnProperty__(ctx, idx, desc); + PropertyDescriptor *current; + + if (isArray && name->isEqualTo(ctx->engine->id_length)) { + if (desc->type != PropertyDescriptor::Data) + goto reject; + array.setLength(desc->value.toUInt32(ctx)); + } + if (!members) members.reset(new PropertyTable()); // Clause 1 - PropertyDescriptor *current = __getOwnProperty__(ctx, name); + current = __getOwnProperty__(ctx, name); if (!current) { // clause 3 if (!extensible) -- cgit v1.2.3 From b69ce09ef54737e68dd8e3eecddd5d3ded8328aa Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 11 Jan 2013 09:14:14 +0100 Subject: Update TestExpectations Change-Id: I1086287f666fba9decf19b3a5e7f2819a663dc95 Reviewed-by: Simon Hausmann --- tests/TestExpectations | 271 ++++++++----------------------------------------- 1 file changed, 44 insertions(+), 227 deletions(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index 9690768efb..cf899fb67c 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -294,9 +294,6 @@ S11.9.4_A2.4_T1 failing S11.9.4_A2.4_T3 failing S11.9.5_A2.4_T1 failing S11.9.5_A2.4_T3 failing -S12.14_A12_T2 failing -S12.14_A12_T3 failing -S12.14_A12_T4 failing S12.14_A13_T1 failing S12.14_A13_T3 failing S12.14_A19_T1 failing @@ -484,8 +481,6 @@ S15.1.2.1_A4.4 failing S15.1.2.1_A4.7 failing S12.6.4_A1 failing S12.6.4_A2 failing -S12.6.4_A3.1 failing -S12.6.4_A3 failing S12.7_A7 failing S12.8_A4_T1 failing S12.8_A4_T2 failing @@ -847,18 +842,7 @@ S15.10.1_A1_T11 failing 15.2.3.13-2-12 failing 15.2.3.14-2-1 failing 15.2.3.14-3-2 failing -15.2.3.14-3-3 failing 15.2.3.14-3-4 failing -15.2.3.14-4-1 failing -15.2.3.14-5-1 failing -15.2.3.14-5-12 failing -15.2.3.14-5-13 failing -15.2.3.14-5-14 failing -15.2.3.14-5-2 failing -15.2.3.14-5-a-1 failing -15.2.3.14-5-a-2 failing -15.2.3.14-5-a-3 failing -15.2.3.14-5-a-4 failing 15.2.3.2-2-12 failing 15.2.3.2-2-13 failing 15.2.3.2-2-14 failing @@ -971,19 +955,9 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-9 failing 15.2.3.3-4-90 failing 15.2.3.4-2-1 failing -15.2.3.4-3-1 failing 15.2.3.4-4-2 failing -15.2.3.4-4-42 failing -15.2.3.4-4-43 failing 15.2.3.4-4-44 failing -15.2.3.4-4-47 failing -15.2.3.4-4-48 failing 15.2.3.4-4-49 failing -15.2.3.4-4-b-1 failing -15.2.3.4-4-b-3 failing -15.2.3.4-4-b-5 failing -15.2.3.4-4-b-6 failing -15.2.3.5-1-2 failing 15.2.3.5-4-120 failing 15.2.3.5-4-13 failing 15.2.3.5-4-145 failing @@ -1020,63 +994,30 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-116 failing 15.2.3.6-4-117 failing 15.2.3.6-4-118 failing -15.2.3.6-4-119 failing -15.2.3.6-4-120 failing -15.2.3.6-4-121 failing -15.2.3.6-4-122 failing 15.2.3.6-4-125 failing -15.2.3.6-4-126 failing -15.2.3.6-4-127 failing -15.2.3.6-4-128 failing -15.2.3.6-4-129 failing -15.2.3.6-4-130 failing -15.2.3.6-4-131 failing -15.2.3.6-4-132 failing 15.2.3.6-4-133 failing 15.2.3.6-4-134 failing 15.2.3.6-4-135 failing 15.2.3.6-4-136 failing -15.2.3.6-4-137 failing 15.2.3.6-4-138 failing 15.2.3.6-4-139 failing 15.2.3.6-4-140 failing 15.2.3.6-4-141 failing -15.2.3.6-4-142 failing -15.2.3.6-4-143 failing -15.2.3.6-4-144 failing 15.2.3.6-4-145 failing -15.2.3.6-4-146 failing -15.2.3.6-4-147 failing -15.2.3.6-4-148 failing -15.2.3.6-4-149 failing -15.2.3.6-4-150 failing -15.2.3.6-4-151 failing 15.2.3.6-4-152 failing 15.2.3.6-4-153 failing -15.2.3.6-4-154 failing -15.2.3.6-4-155 failing 15.2.3.6-4-156 failing 15.2.3.6-4-157 failing -15.2.3.6-4-159 failing -15.2.3.6-4-161 failing 15.2.3.6-4-163 failing 15.2.3.6-4-165 failing -15.2.3.6-4-166 failing -15.2.3.6-4-167 failing 15.2.3.6-4-168 failing 15.2.3.6-4-169 failing 15.2.3.6-4-170 failing -15.2.3.6-4-171 failing 15.2.3.6-4-172 failing 15.2.3.6-4-173 failing 15.2.3.6-4-174 failing -15.2.3.6-4-175 failing 15.2.3.6-4-176 failing 15.2.3.6-4-177 failing -15.2.3.6-4-179-1 failing -15.2.3.6-4-181 failing -15.2.3.6-4-182 failing -15.2.3.6-4-183 failing 15.2.3.6-4-184 failing 15.2.3.6-4-185 failing 15.2.3.6-4-186 failing @@ -1086,15 +1027,7 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-192 failing 15.2.3.6-4-193 failing 15.2.3.6-4-20 failing -15.2.3.6-4-210 failing -15.2.3.6-4-217 failing -15.2.3.6-4-242-1 failing -15.2.3.6-4-242 failing -15.2.3.6-4-255 failing 15.2.3.6-4-256 failing -15.2.3.6-4-258 failing -15.2.3.6-4-259 failing -15.2.3.6-4-260 failing 15.2.3.6-4-261 failing 15.2.3.6-4-262 failing 15.2.3.6-4-263 failing @@ -1104,8 +1037,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-271 failing 15.2.3.6-4-272 failing 15.2.3.6-4-274 failing -15.2.3.6-4-275 failing -15.2.3.6-4-276 failing 15.2.3.6-4-280 failing 15.2.3.6-4-291-1 failing 15.2.3.6-4-292-1 failing @@ -1121,21 +1052,17 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-329 failing 15.2.3.6-4-330 failing 15.2.3.6-4-331 failing -15.2.3.6-4-333-4 failing 15.2.3.6-4-336 failing -15.2.3.6-4-339-1 failing 15.2.3.6-4-343 failing 15.2.3.6-4-344 failing 15.2.3.6-4-345 failing 15.2.3.6-4-350 failing 15.2.3.6-4-354-13 failing 15.2.3.6-4-354-4 failing -15.2.3.6-4-354-6 failing 15.2.3.6-4-354-8 failing 15.2.3.6-4-357 failing 15.2.3.6-4-358 failing 15.2.3.6-4-359 failing -15.2.3.6-4-360-1 failing 15.2.3.6-4-360-3 failing 15.2.3.6-4-360-7 failing 15.2.3.6-4-371 failing @@ -1164,13 +1091,11 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-531-13 failing 15.2.3.6-4-531-17 failing 15.2.3.6-4-531-4 failing -15.2.3.6-4-531-6 failing 15.2.3.6-4-531-8 failing 15.2.3.6-4-534 failing 15.2.3.6-4-535 failing 15.2.3.6-4-538-3 failing 15.2.3.6-4-538-7 failing -15.2.3.6-4-540-4 failing 15.2.3.6-4-543 failing 15.2.3.6-4-544 failing 15.2.3.6-4-552 failing @@ -1230,16 +1155,13 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-82-8 failing 15.2.3.6-4-82-9 failing 15.2.3.7-2-14 failing -15.2.3.7-2-15 failing 15.2.3.7-5-a-15 failing -15.2.3.7-5-a-16 failing 15.2.3.7-5-b-105 failing 15.2.3.7-5-b-133 failing 15.2.3.7-5-b-159 failing 15.2.3.7-5-b-184 failing 15.2.3.7-5-b-212 failing 15.2.3.7-5-b-247 failing -15.2.3.7-5-b-248 failing 15.2.3.7-5-b-27 failing 15.2.3.7-5-b-52 failing 15.2.3.7-5-b-80 failing @@ -1253,56 +1175,32 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-112 failing 15.2.3.7-6-a-113 failing 15.2.3.7-6-a-114 failing -15.2.3.7-6-a-115 failing 15.2.3.7-6-a-116 failing 15.2.3.7-6-a-117 failing 15.2.3.7-6-a-118 failing 15.2.3.7-6-a-119 failing 15.2.3.7-6-a-12 failing 15.2.3.7-6-a-121 failing -15.2.3.7-6-a-122 failing -15.2.3.7-6-a-123 failing -15.2.3.7-6-a-124 failing -15.2.3.7-6-a-125 failing -15.2.3.7-6-a-126 failing -15.2.3.7-6-a-127 failing -15.2.3.7-6-a-128 failing 15.2.3.7-6-a-129 failing 15.2.3.7-6-a-13 failing 15.2.3.7-6-a-130 failing 15.2.3.7-6-a-131 failing 15.2.3.7-6-a-132 failing -15.2.3.7-6-a-133 failing 15.2.3.7-6-a-134 failing 15.2.3.7-6-a-135 failing 15.2.3.7-6-a-136 failing 15.2.3.7-6-a-137 failing -15.2.3.7-6-a-138 failing -15.2.3.7-6-a-139 failing 15.2.3.7-6-a-14 failing -15.2.3.7-6-a-140 failing 15.2.3.7-6-a-141 failing -15.2.3.7-6-a-142 failing -15.2.3.7-6-a-143 failing -15.2.3.7-6-a-144 failing -15.2.3.7-6-a-145 failing -15.2.3.7-6-a-146 failing -15.2.3.7-6-a-147 failing 15.2.3.7-6-a-148 failing 15.2.3.7-6-a-149 failing 15.2.3.7-6-a-15 failing -15.2.3.7-6-a-150 failing -15.2.3.7-6-a-151 failing 15.2.3.7-6-a-152 failing 15.2.3.7-6-a-153 failing -15.2.3.7-6-a-155 failing -15.2.3.7-6-a-157 failing 15.2.3.7-6-a-158 failing 15.2.3.7-6-a-16 failing 15.2.3.7-6-a-160 failing 15.2.3.7-6-a-161 failing -15.2.3.7-6-a-162 failing -15.2.3.7-6-a-163 failing 15.2.3.7-6-a-164 failing 15.2.3.7-6-a-165 failing 15.2.3.7-6-a-166 failing @@ -1316,9 +1214,6 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-173 failing 15.2.3.7-6-a-175 failing 15.2.3.7-6-a-176 failing -15.2.3.7-6-a-177 failing -15.2.3.7-6-a-178 failing -15.2.3.7-6-a-179 failing 15.2.3.7-6-a-18 failing 15.2.3.7-6-a-180 failing 15.2.3.7-6-a-181 failing @@ -1336,12 +1231,7 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-192 failing 15.2.3.7-6-a-193 failing 15.2.3.7-6-a-194 failing -15.2.3.7-6-a-197 failing -15.2.3.7-6-a-198 failing -15.2.3.7-6-a-199 failing 15.2.3.7-6-a-20 failing -15.2.3.7-6-a-204 failing -15.2.3.7-6-a-206 failing 15.2.3.7-6-a-21 failing 15.2.3.7-6-a-213 failing 15.2.3.7-6-a-214 failing @@ -1350,7 +1240,6 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-228 failing 15.2.3.7-6-a-229 failing 15.2.3.7-6-a-230 failing -15.2.3.7-6-a-231 failing 15.2.3.7-6-a-233 failing 15.2.3.7-6-a-234 failing 15.2.3.7-6-a-235 failing @@ -1364,21 +1253,15 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-242 failing 15.2.3.7-6-a-244 failing 15.2.3.7-6-a-245 failing -15.2.3.7-6-a-247 failing -15.2.3.7-6-a-248 failing -15.2.3.7-6-a-249 failing 15.2.3.7-6-a-25 failing 15.2.3.7-6-a-250 failing 15.2.3.7-6-a-251 failing 15.2.3.7-6-a-252 failing -15.2.3.7-6-a-253 failing 15.2.3.7-6-a-255 failing 15.2.3.7-6-a-258 failing 15.2.3.7-6-a-260 failing 15.2.3.7-6-a-261 failing 15.2.3.7-6-a-263 failing -15.2.3.7-6-a-264 failing -15.2.3.7-6-a-265 failing 15.2.3.7-6-a-269 failing 15.2.3.7-6-a-270 failing 15.2.3.7-6-a-271 failing @@ -1451,7 +1334,6 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-98 failing 15.2.3.7-6-a-99 failing 15.2.3.9-2-a-12 failing -15.2.3.9-2-a-14 failing 15.2.4.2-1-1 failing 15.2.4.2-1-2 failing 15.2.4.2-2-1 failing @@ -1637,10 +1519,6 @@ S15.3.5_A3_T2 failing 15.3.5.4_2-93gs failing S15.4_A1.1_T2 failing S15.4_A1.1_T3 failing -S15.4_A1.1_T4 failing -S15.4_A1.1_T7 failing -S15.4_A1.1_T8 failing -S15.4_A1.1_T9 failing S15.4.2.2_A2.2_T1 failing S15.4.2.2_A2.2_T2 failing S15.4.2.2_A2.2_T3 failing @@ -1673,27 +1551,9 @@ S15.4.3.1_A4 failing 15.4.3.2-2-1 failing 15.4.3.2-2-2 failing 15.4.3.2-2-3 failing -15.4.4.10-10-c-ii-1 failing -S15.4.4.10_A1.1_T1 failing -S15.4.4.10_A1.1_T5 failing -S15.4.4.10_A1.1_T6 failing -S15.4.4.10_A1.1_T7 failing -S15.4.4.10_A1.2_T1 failing -S15.4.4.10_A1.2_T2 failing -S15.4.4.10_A1.2_T3 failing S15.4.4.10_A1.2_T4 failing -S15.4.4.10_A1.3_T1 failing -S15.4.4.10_A1.4_T1 failing -S15.4.4.10_A1.4_T2 failing S15.4.4.10_A1.4_T3 failing -S15.4.4.10_A1.5_T1 failing -S15.4.4.10_A1.5_T2 failing -S15.4.4.10_A2.1_T1 failing -S15.4.4.10_A2.1_T2 failing S15.4.4.10_A2.1_T4 failing -S15.4.4.10_A2.1_T5 failing -S15.4.4.10_A2.2_T1 failing -S15.4.4.10_A2.2_T5 failing S15.4.4.10_A3_T3 failing S15.4.4.10_A4_T1 failing S15.4.4.10_A5.1 failing @@ -1751,7 +1611,6 @@ S15.4.4.13_A5.7 failing 15.4.4.14-1-7 failing 15.4.4.14-1-8 failing 15.4.4.14-1-9 failing -15.4.4.14-10-1 failing 15.4.4.14-10-2 failing 15.4.4.14-2-1 failing 15.4.4.14-2-10 failing @@ -1807,10 +1666,8 @@ S15.4.4.13_A5.7 failing 15.4.4.14-4-8 failing 15.4.4.14-4-9 failing 15.4.4.14-5-11 failing -15.4.4.14-5-13 failing 15.4.4.14-5-14 failing 15.4.4.14-5-15 failing -15.4.4.14-5-17 failing 15.4.4.14-5-26 failing 15.4.4.14-5-27 failing 15.4.4.14-5-28 failing @@ -1827,32 +1684,24 @@ S15.4.4.13_A5.7 failing 15.4.4.14-9-a-16 failing 15.4.4.14-9-a-17 failing 15.4.4.14-9-a-18 failing -15.4.4.14-9-a-19 failing 15.4.4.14-9-a-2 failing -15.4.4.14-9-a-3 failing 15.4.4.14-9-a-4 failing 15.4.4.14-9-a-5 failing 15.4.4.14-9-a-7 failing -15.4.4.14-9-a-8 failing 15.4.4.14-9-a-9 failing 15.4.4.14-9-b-i-1 failing 15.4.4.14-9-b-i-10 failing -15.4.4.14-9-b-i-11 failing 15.4.4.14-9-b-i-12 failing -15.4.4.14-9-b-i-13 failing 15.4.4.14-9-b-i-14 failing 15.4.4.14-9-b-i-15 failing 15.4.4.14-9-b-i-16 failing -15.4.4.14-9-b-i-17 failing 15.4.4.14-9-b-i-18 failing -15.4.4.14-9-b-i-19 failing 15.4.4.14-9-b-i-20 failing 15.4.4.14-9-b-i-22 failing 15.4.4.14-9-b-i-23 failing 15.4.4.14-9-b-i-25 failing 15.4.4.14-9-b-i-26 failing 15.4.4.14-9-b-i-27 failing -15.4.4.14-9-b-i-28 failing 15.4.4.14-9-b-i-29 failing 15.4.4.14-9-b-i-30 failing 15.4.4.14-9-b-i-31 failing @@ -1860,7 +1709,6 @@ S15.4.4.13_A5.7 failing 15.4.4.14-9-b-i-6 failing 15.4.4.14-9-b-i-7 failing 15.4.4.14-9-b-i-8 failing -15.4.4.14-9-b-i-9 failing 15.4.4.14-9-b-ii-2 failing 15.4.4.14-9-b-ii-5 failing 15.4.4.15-1-1 failing @@ -2119,7 +1967,6 @@ S15.4.4.13_A5.7 failing 15.4.4.16-4-8 failing 15.4.4.16-4-9 failing 15.4.4.16-5-17 failing -15.4.4.16-7-4 failing 15.4.4.16-7-5 failing 15.4.4.16-7-6 failing 15.4.4.16-7-8 failing @@ -2130,21 +1977,15 @@ S15.4.4.13_A5.7 failing 15.4.4.16-7-b-13 failing 15.4.4.16-7-b-14 failing 15.4.4.16-7-b-15 failing -15.4.4.16-7-b-16 failing 15.4.4.16-7-b-2 failing 15.4.4.16-7-b-3 failing 15.4.4.16-7-b-4 failing -15.4.4.16-7-b-5 failing 15.4.4.16-7-b-6 failing 15.4.4.16-7-b-7 failing 15.4.4.16-7-b-8 failing -15.4.4.16-7-b-9 failing 15.4.4.16-7-c-i-1 failing -15.4.4.16-7-c-i-10 failing 15.4.4.16-7-c-i-11 failing -15.4.4.16-7-c-i-12 failing 15.4.4.16-7-c-i-13 failing -15.4.4.16-7-c-i-14 failing 15.4.4.16-7-c-i-15 failing 15.4.4.16-7-c-i-16 failing 15.4.4.16-7-c-i-17 failing @@ -2157,7 +1998,6 @@ S15.4.4.13_A5.7 failing 15.4.4.16-7-c-i-25 failing 15.4.4.16-7-c-i-26 failing 15.4.4.16-7-c-i-27 failing -15.4.4.16-7-c-i-28 failing 15.4.4.16-7-c-i-29 failing 15.4.4.16-7-c-i-3 failing 15.4.4.16-7-c-i-30 failing @@ -2174,7 +2014,6 @@ S15.4.4.13_A5.7 failing 15.4.4.16-7-c-ii-21 failing 15.4.4.16-7-c-ii-22 failing 15.4.4.16-7-c-ii-23 failing -15.4.4.16-7-c-ii-5 failing 15.4.4.16-7-c-ii-6 failing 15.4.4.16-7-c-ii-7 failing 15.4.4.16-7-c-ii-8 failing @@ -2260,7 +2099,6 @@ S15.4.4.13_A5.7 failing 15.4.4.17-4-8 failing 15.4.4.17-4-9 failing 15.4.4.17-5-17 failing -15.4.4.17-7-4 failing 15.4.4.17-7-5 failing 15.4.4.17-7-6 failing 15.4.4.17-7-8 failing @@ -2271,21 +2109,15 @@ S15.4.4.13_A5.7 failing 15.4.4.17-7-b-13 failing 15.4.4.17-7-b-14 failing 15.4.4.17-7-b-15 failing -15.4.4.17-7-b-16 failing 15.4.4.17-7-b-2 failing 15.4.4.17-7-b-3 failing 15.4.4.17-7-b-4 failing -15.4.4.17-7-b-5 failing 15.4.4.17-7-b-6 failing 15.4.4.17-7-b-7 failing 15.4.4.17-7-b-8 failing -15.4.4.17-7-b-9 failing 15.4.4.17-7-c-i-1 failing -15.4.4.17-7-c-i-10 failing 15.4.4.17-7-c-i-11 failing -15.4.4.17-7-c-i-12 failing 15.4.4.17-7-c-i-13 failing -15.4.4.17-7-c-i-14 failing 15.4.4.17-7-c-i-15 failing 15.4.4.17-7-c-i-16 failing 15.4.4.17-7-c-i-17 failing @@ -2298,7 +2130,6 @@ S15.4.4.13_A5.7 failing 15.4.4.17-7-c-i-25 failing 15.4.4.17-7-c-i-26 failing 15.4.4.17-7-c-i-27 failing -15.4.4.17-7-c-i-28 failing 15.4.4.17-7-c-i-29 failing 15.4.4.17-7-c-i-3 failing 15.4.4.17-7-c-i-30 failing @@ -2315,7 +2146,6 @@ S15.4.4.13_A5.7 failing 15.4.4.17-7-c-ii-21 failing 15.4.4.17-7-c-ii-22 failing 15.4.4.17-7-c-ii-23 failing -15.4.4.17-7-c-ii-5 failing 15.4.4.17-7-c-ii-6 failing 15.4.4.17-7-c-ii-8 failing 15.4.4.17-7-c-iii-1 failing @@ -2401,7 +2231,6 @@ S15.4.4.13_A5.7 failing 15.4.4.18-4-9 failing 15.4.4.18-5-17 failing 15.4.4.18-7-1 failing -15.4.4.18-7-3 failing 15.4.4.18-7-4 failing 15.4.4.18-7-5 failing 15.4.4.18-7-8 failing @@ -2412,21 +2241,15 @@ S15.4.4.13_A5.7 failing 15.4.4.18-7-b-13 failing 15.4.4.18-7-b-14 failing 15.4.4.18-7-b-15 failing -15.4.4.18-7-b-16 failing 15.4.4.18-7-b-2 failing 15.4.4.18-7-b-3 failing 15.4.4.18-7-b-4 failing -15.4.4.18-7-b-5 failing 15.4.4.18-7-b-6 failing 15.4.4.18-7-b-7 failing 15.4.4.18-7-b-8 failing -15.4.4.18-7-b-9 failing 15.4.4.18-7-c-i-1 failing -15.4.4.18-7-c-i-10 failing 15.4.4.18-7-c-i-11 failing -15.4.4.18-7-c-i-12 failing 15.4.4.18-7-c-i-13 failing -15.4.4.18-7-c-i-14 failing 15.4.4.18-7-c-i-15 failing 15.4.4.18-7-c-i-16 failing 15.4.4.18-7-c-i-17 failing @@ -2439,7 +2262,6 @@ S15.4.4.13_A5.7 failing 15.4.4.18-7-c-i-25 failing 15.4.4.18-7-c-i-26 failing 15.4.4.18-7-c-i-27 failing -15.4.4.18-7-c-i-28 failing 15.4.4.18-7-c-i-29 failing 15.4.4.18-7-c-i-3 failing 15.4.4.18-7-c-i-30 failing @@ -2456,7 +2278,6 @@ S15.4.4.13_A5.7 failing 15.4.4.18-7-c-ii-21 failing 15.4.4.18-7-c-ii-22 failing 15.4.4.18-7-c-ii-23 failing -15.4.4.18-7-c-ii-5 failing 15.4.4.18-7-c-ii-6 failing 15.4.4.18-7-c-ii-8 failing 15.4.4.18-8-10 failing @@ -2539,7 +2360,6 @@ S15.4.4.13_A5.7 failing 15.4.4.19-5-17 failing 15.4.4.19-6-1 failing 15.4.4.19-8-1 failing -15.4.4.19-8-4 failing 15.4.4.19-8-5 failing 15.4.4.19-8-6 failing 15.4.4.19-8-8 failing @@ -2548,20 +2368,15 @@ S15.4.4.13_A5.7 failing 15.4.4.19-8-b-12 failing 15.4.4.19-8-b-13 failing 15.4.4.19-8-b-15 failing -15.4.4.19-8-b-16 failing 15.4.4.19-8-b-2 failing 15.4.4.19-8-b-3 failing 15.4.4.19-8-b-4 failing -15.4.4.19-8-b-5 failing 15.4.4.19-8-b-6 failing 15.4.4.19-8-b-7 failing 15.4.4.19-8-b-8 failing 15.4.4.19-8-c-i-1 failing -15.4.4.19-8-c-i-10 failing 15.4.4.19-8-c-i-11 failing -15.4.4.19-8-c-i-12 failing 15.4.4.19-8-c-i-13 failing -15.4.4.19-8-c-i-14 failing 15.4.4.19-8-c-i-15 failing 15.4.4.19-8-c-i-16 failing 15.4.4.19-8-c-i-17 failing @@ -2574,7 +2389,6 @@ S15.4.4.13_A5.7 failing 15.4.4.19-8-c-i-25 failing 15.4.4.19-8-c-i-26 failing 15.4.4.19-8-c-i-27 failing -15.4.4.19-8-c-i-28 failing 15.4.4.19-8-c-i-29 failing 15.4.4.19-8-c-i-3 failing 15.4.4.19-8-c-i-30 failing @@ -2591,10 +2405,8 @@ S15.4.4.13_A5.7 failing 15.4.4.19-8-c-ii-21 failing 15.4.4.19-8-c-ii-22 failing 15.4.4.19-8-c-ii-23 failing -15.4.4.19-8-c-ii-5 failing 15.4.4.19-8-c-ii-6 failing 15.4.4.19-8-c-ii-8 failing -15.4.4.19-8-c-iii-1 failing 15.4.4.19-8-c-iii-2 failing 15.4.4.19-8-c-iii-3 failing 15.4.4.19-8-c-iii-4 failing @@ -2690,7 +2502,6 @@ S15.4.4.2_A4.7 failing 15.4.4.20-6-7 failing 15.4.4.20-6-8 failing 15.4.4.20-9-1 failing -15.4.4.20-9-4 failing 15.4.4.20-9-5 failing 15.4.4.20-9-6 failing 15.4.4.20-9-8 failing @@ -2701,21 +2512,15 @@ S15.4.4.2_A4.7 failing 15.4.4.20-9-b-13 failing 15.4.4.20-9-b-14 failing 15.4.4.20-9-b-15 failing -15.4.4.20-9-b-16 failing 15.4.4.20-9-b-2 failing 15.4.4.20-9-b-3 failing 15.4.4.20-9-b-4 failing -15.4.4.20-9-b-5 failing 15.4.4.20-9-b-6 failing 15.4.4.20-9-b-7 failing 15.4.4.20-9-b-8 failing -15.4.4.20-9-b-9 failing 15.4.4.20-9-c-i-1 failing -15.4.4.20-9-c-i-10 failing 15.4.4.20-9-c-i-11 failing -15.4.4.20-9-c-i-12 failing 15.4.4.20-9-c-i-13 failing -15.4.4.20-9-c-i-14 failing 15.4.4.20-9-c-i-15 failing 15.4.4.20-9-c-i-16 failing 15.4.4.20-9-c-i-17 failing @@ -2728,7 +2533,6 @@ S15.4.4.2_A4.7 failing 15.4.4.20-9-c-i-25 failing 15.4.4.20-9-c-i-26 failing 15.4.4.20-9-c-i-27 failing -15.4.4.20-9-c-i-28 failing 15.4.4.20-9-c-i-29 failing 15.4.4.20-9-c-i-3 failing 15.4.4.20-9-c-i-30 failing @@ -2752,7 +2556,6 @@ S15.4.4.2_A4.7 failing 15.4.4.20-9-c-iii-1-2 failing 15.4.4.20-9-c-iii-1-3 failing 15.4.4.20-9-c-iii-1-4 failing -15.4.4.20-9-c-iii-1 failing 15.4.4.20-9-c-iii-2 failing 15.4.4.20-9-c-iii-24 failing 15.4.4.20-9-c-iii-29 failing @@ -2849,15 +2652,11 @@ S15.4.4.2_A4.7 failing 15.4.4.21-7-8 failing 15.4.4.21-7-9 failing 15.4.4.21-8-b-2 failing -15.4.4.21-8-b-3 failing 15.4.4.21-8-b-ii-1 failing 15.4.4.21-8-b-ii-2 failing 15.4.4.21-8-b-iii-1-1 failing -15.4.4.21-8-b-iii-1-10 failing 15.4.4.21-8-b-iii-1-11 failing -15.4.4.21-8-b-iii-1-12 failing 15.4.4.21-8-b-iii-1-13 failing -15.4.4.21-8-b-iii-1-14 failing 15.4.4.21-8-b-iii-1-15 failing 15.4.4.21-8-b-iii-1-16 failing 15.4.4.21-8-b-iii-1-17 failing @@ -2873,7 +2672,6 @@ S15.4.4.2_A4.7 failing 15.4.4.21-8-b-iii-1-28 failing 15.4.4.21-8-b-iii-1-29 failing 15.4.4.21-8-b-iii-1-3 failing -15.4.4.21-8-b-iii-1-30 failing 15.4.4.21-8-b-iii-1-31 failing 15.4.4.21-8-b-iii-1-32 failing 15.4.4.21-8-b-iii-1-33 failing @@ -2893,7 +2691,6 @@ S15.4.4.2_A4.7 failing 15.4.4.21-9-8 failing 15.4.4.21-9-9 failing 15.4.4.21-9-b-10 failing -15.4.4.21-9-b-11 failing 15.4.4.21-9-b-12 failing 15.4.4.21-9-b-13 failing 15.4.4.21-9-b-15 failing @@ -2909,14 +2706,11 @@ S15.4.4.2_A4.7 failing 15.4.4.21-9-b-26 failing 15.4.4.21-9-b-27 failing 15.4.4.21-9-b-28 failing -15.4.4.21-9-b-29 failing 15.4.4.21-9-b-3 failing 15.4.4.21-9-b-4 failing -15.4.4.21-9-b-5 failing 15.4.4.21-9-b-6 failing 15.4.4.21-9-b-7 failing 15.4.4.21-9-b-8 failing -15.4.4.21-9-b-9 failing 15.4.4.21-9-c-1 failing 15.4.4.21-9-c-i-1 failing 15.4.4.21-9-c-i-10 failing @@ -3064,15 +2858,11 @@ S15.4.4.2_A4.7 failing 15.4.4.22-7-8 failing 15.4.4.22-7-9 failing 15.4.4.22-8-b-2 failing -15.4.4.22-8-b-3 failing 15.4.4.22-8-b-ii-1 failing 15.4.4.22-8-b-ii-2 failing 15.4.4.22-8-b-iii-1-1 failing -15.4.4.22-8-b-iii-1-10 failing 15.4.4.22-8-b-iii-1-11 failing -15.4.4.22-8-b-iii-1-12 failing 15.4.4.22-8-b-iii-1-13 failing -15.4.4.22-8-b-iii-1-14 failing 15.4.4.22-8-b-iii-1-15 failing 15.4.4.22-8-b-iii-1-16 failing 15.4.4.22-8-b-iii-1-17 failing @@ -3088,7 +2878,6 @@ S15.4.4.2_A4.7 failing 15.4.4.22-8-b-iii-1-28 failing 15.4.4.22-8-b-iii-1-29 failing 15.4.4.22-8-b-iii-1-3 failing -15.4.4.22-8-b-iii-1-30 failing 15.4.4.22-8-b-iii-1-31 failing 15.4.4.22-8-b-iii-1-32 failing 15.4.4.22-8-b-iii-1-33 failing @@ -3106,12 +2895,10 @@ S15.4.4.2_A4.7 failing 15.4.4.22-9-8 failing 15.4.4.22-9-9 failing 15.4.4.22-9-b-10 failing -15.4.4.22-9-b-11 failing 15.4.4.22-9-b-12 failing 15.4.4.22-9-b-13 failing 15.4.4.22-9-b-14 failing 15.4.4.22-9-b-15 failing -15.4.4.22-9-b-16 failing 15.4.4.22-9-b-17 failing 15.4.4.22-9-b-18 failing 15.4.4.22-9-b-19 failing @@ -3125,11 +2912,9 @@ S15.4.4.2_A4.7 failing 15.4.4.22-9-b-29 failing 15.4.4.22-9-b-3 failing 15.4.4.22-9-b-4 failing -15.4.4.22-9-b-5 failing 15.4.4.22-9-b-6 failing 15.4.4.22-9-b-7 failing 15.4.4.22-9-b-8 failing -15.4.4.22-9-b-9 failing 15.4.4.22-9-c-1 failing 15.4.4.22-9-c-i-1 failing 15.4.4.22-9-c-i-10 failing @@ -3188,7 +2973,6 @@ S15.4.4.3_A3_T1 failing S15.4.4.3_A4.7 failing 15.4.4.4-5-b-iii-3-b-1 failing 15.4.4.4-5-c-i-1 failing -S15.4.4.4_A1_T4 failing S15.4.4.4_A2_T1 failing S15.4.4.4_A2_T2 failing S15.4.4.4_A3_T1 failing @@ -3929,8 +3713,6 @@ S15.2.4.7_A13 failing 15.3.5.4_2-9gs failing # Array regressions -S12.14_A18_T7 failing -S12.13_A3_T4 failing S13.2.1_A5_T1 failing S15.4.4.11_A1.5_T1 failing S15.4.4.11_A2.1_T1 failing @@ -3960,19 +3742,54 @@ S15.4.4.12_A2.1_T4 failing S15.4.4.12_A2.1_T5 failing S15.4.4.12_A2.2_T3 failing S15.4.4.12_A2.2_T5 failing -S15.4.4.10_A2_T1 failing -S15.4.4.10_A2_T2 failing -S15.4.4.10_A2_T3 failing -S15.4.4.10_A2_T4 failing -S15.4.4.10_A2_T5 failing -S15.4.4.10_A2_T6 failing S15.4.4.11_A1.2_T1 failing S15.4.4.11_A1.2_T2 failing S15.4.4.11_A1.4_T1 failing S15.4.4.11_A1.4_T2 failing -S15.4.4.4_A1_T1 failing S15.4.4.4_A1_T2 failing -S15.4.4.4_A1_T3 failing # Regression introduced by qtdeclarative parser changes -S7.9_A5.7_T1 failing \ No newline at end of file +S7.9_A5.7_T1 failing + +# Regressions due to Object/property refactoring +15.12.2-2-1 failing +15.12.2-2-10 failing +15.12.2-2-2 failing +15.12.2-2-3 failing +15.12.2-2-4 failing +15.12.2-2-5 failing +15.12.2-2-6 failing +15.12.2-2-7 failing +15.12.2-2-8 failing +15.12.2-2-9 failing +15.12.3-11-16 failing +15.12.3-11-17 failing +15.12.3-11-18 failing +15.12.3-11-19 failing +15.12.3-11-20 failing +15.12.3-11-21 failing +15.12.3-11-22 failing +15.12.3-11-23 failing +15.12.3-11-24 failing +15.12.3-11-25 failing +15.2.3.4-4-1 failing +15.2.3.6-4-234 failing +15.2.3.6-4-235 failing +15.2.3.6-4-236 failing +15.2.3.6-4-237 failing +15.2.3.7-6-a-223 failing +15.2.3.7-6-a-224 failing +15.2.3.7-6-a-225 failing +15.2.3.7-6-a-226 failing +S15.4.4.13_A1_T1 failing +S15.4.4.13_A1_T2 failing +15.4.4.18-7-c-i-6 failing +15.4.4.19-8-9 failing +15.4.4.19-8-b-14 failing +15.4.4.19-8-c-i-6 failing +15.4.4.20-9-c-i-6 failing +15.4.4.21-8-b-iii-1-6 failing +15.4.4.22-9-c-i-33 failing +15.4.4.22-8-b-iii-1-6 failing +S15.4.4.5_A4_T2 failing +S15.4.4.7_A5_T1 failing -- cgit v1.2.3 From 2ef56b692de3cb7b696e340d4485c34c04940651 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 11 Jan 2013 09:29:16 +0100 Subject: Coding style fixes Change-Id: Idce62159028eadfec128a1f0f416b839ff204e82 Reviewed-by: Simon Hausmann --- qv4array.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/qv4array.cpp b/qv4array.cpp index 32b465d62d..da86de8014 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -461,7 +461,7 @@ SparseArrayNode *SparseArray::insert(uint akey) return createNode(s, y, left); } -Array::Array(const Array & other) +Array::Array(const Array &other) : len(other.len) , values(other.values) , sparse(0) @@ -560,8 +560,7 @@ void Array::sort(ExecutionContext *context, Object *thisObject, const Value &com ArrayElementLessThan lessThan(context, thisObject, comparefn); std::sort(values.begin(), values.end(), lessThan); - if (sparse) - delete sparse; + delete sparse; } -- cgit v1.2.3 From a061d96ada71f7fe9dab588855eab9937ef4f48e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 11 Jan 2013 09:31:51 +0100 Subject: Fix return value of qmljs_delete_subscript Change-Id: I526d64459c0171a9911c5b0b13e6136b35b92d17 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index a96bfc0847..53d6e2605b 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -176,7 +176,7 @@ Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index) else if (index.isDouble()) n = index.doubleValue(); if (n < UINT_MAX) - o->__delete__(ctx, n); + return Value::fromBoolean(o->__delete__(ctx, n)); } String *name = index.toString(ctx); -- cgit v1.2.3 From f375d81341f738e6f093833d470f68d84587e744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Thu, 10 Jan 2013 16:45:14 +0100 Subject: Remove unused MemoryManager::Data::stringPool Change-Id: Iffc993c2872329f055c28aefa067a81840c346e9 Reviewed-by: Erik Verbruggen --- qmljs_engine.cpp | 1 - qv4mm.cpp | 8 -------- 2 files changed, 9 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index b558b45a88..0d54c77011 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -76,7 +76,6 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) MemoryManager::GCBlocker gcBlocker(memoryManager); stringPool.reset(new StringPool); - memoryManager->setStringPool(stringPool.data()); memoryManager->setExecutionEngine(this); rootContext = newContext(); diff --git a/qv4mm.cpp b/qv4mm.cpp index 9339b38244..cf50e9dee6 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -48,7 +48,6 @@ using namespace WTF; static const std::size_t CHUNK_SIZE = 65536; -class StringPool; struct MemoryManager::Data { bool enableGC; @@ -56,7 +55,6 @@ struct MemoryManager::Data bool scribble; bool aggressiveGC; ExecutionEngine *engine; - StringPool *stringPool; enum { MaxItemSize = 128 }; Managed *smallItems[MaxItemSize/16]; @@ -76,7 +74,6 @@ struct MemoryManager::Data : enableGC(enableGC) , gcBlocked(false) , engine(0) - , stringPool(0) { memset(smallItems, 0, sizeof(smallItems)); scribble = qgetenv("MM_NO_SCRIBBLE").isEmpty(); @@ -300,11 +297,6 @@ void MemoryManager::setExecutionEngine(ExecutionEngine *engine) m_d->engine = engine; } -void MemoryManager::setStringPool(StringPool *stringPool) -{ - m_d->stringPool = stringPool; -} - void MemoryManager::dumpStats() const { std::cerr << "=================" << std::endl; -- cgit v1.2.3 From 52b662c97ee2612f30dee172577ca3f44672de86 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Sat, 12 Jan 2013 20:34:04 +0100 Subject: Fix compilation issues. Most importantly: make the bits protected instead of private, so subclasses can access them. Change-Id: I53f94102feb7ace73957562b40b0ca8514290760 Reviewed-by: Lars Knoll --- qmljs_objects.cpp | 2 ++ qv4managed.h | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index b067b3fc7b..9d84d0ad84 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -150,6 +150,8 @@ void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name void Object::defineReadonlyProperty(ExecutionEngine *engine, String *name, Value value) { + Q_UNUSED(engine); + if (!members) members.reset(new PropertyTable()); PropertyDescriptor *pd = members->insert(name); diff --git a/qv4managed.h b/qv4managed.h index f1bc3e360f..3c7f04dc3e 100644 --- a/qv4managed.h +++ b/qv4managed.h @@ -74,16 +74,6 @@ public: protected: virtual void getCollectables(QVector &objects) = 0; -private: - friend class MemoryManager; - friend struct Object; - friend struct ObjectPrototype; - friend struct Array; - friend struct ArrayPrototype; - friend struct FunctionObject; - friend struct ExecutionContext; - friend struct ScriptFunction; - union { Managed *nextFree; struct { @@ -103,6 +93,16 @@ private: #endif }; }; + +private: + friend class MemoryManager; + friend struct Object; + friend struct ObjectPrototype; + friend class Array; + friend struct ArrayPrototype; + friend struct FunctionObject; + friend struct ExecutionContext; + friend struct ScriptFunction; }; } -- cgit v1.2.3 From 0b7dce92b5907d68fde170d154c0ccd1132f97fc Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 11 Jan 2013 09:56:56 +0100 Subject: Add support for throwing RangeErrors Change-Id: I15bad9d7fe33f9a906d3b2d8da94a969d54d918c Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 7 +++++++ qmljs_engine.h | 1 + qmljs_environment.cpp | 7 +++++++ qmljs_environment.h | 1 + qmljs_objects.h | 2 ++ qv4ecmaobjects.cpp | 3 +-- 6 files changed, 19 insertions(+), 2 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 0d54c77011..e8a33893e0 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -400,6 +400,13 @@ Object *ExecutionEngine::newTypeErrorObject(ExecutionContext *ctx, const QString return object; } +Object *ExecutionEngine::newRangeErrorObject(ExecutionContext *ctx, const QString &message) +{ + RangeErrorObject *object = new (memoryManager) RangeErrorObject(ctx, message); + object->prototype = rangeErrorPrototype; + return object; +} + Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) { MathObject *object = new (memoryManager) MathObject(ctx); diff --git a/qmljs_engine.h b/qmljs_engine.h index a2cba073ac..776363a377 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -199,6 +199,7 @@ struct ExecutionEngine Object *newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message); Object *newReferenceErrorObject(ExecutionContext *ctx, const QString &message); Object *newTypeErrorObject(ExecutionContext *ctx, const QString &message); + Object *newRangeErrorObject(ExecutionContext *ctx, const QString &message); Object *newMathObject(ExecutionContext *ctx); Object *newActivationObject(); diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 745554f2bf..067116d469 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -385,6 +385,13 @@ void ExecutionContext::throwReferenceError(Value value) throwError(Value::fromObject(engine->newReferenceErrorObject(this, msg))); } +void ExecutionContext::throwRangeError(Value value) +{ + String *s = value.toString(this); + QString msg = s->toQString() + QStringLiteral(" out of range"); + throwError(Value::fromObject(engine->newRangeErrorObject(this, msg))); +} + void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc) { MemoryManager::GCBlocker blockGC(parent->engine->memoryManager); diff --git a/qmljs_environment.h b/qmljs_environment.h index 5ecc56e9ab..faa04aae0c 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -118,6 +118,7 @@ struct ExecutionContext void throwSyntaxError(DiagnosticMessage *message); void throwTypeError(); void throwReferenceError(Value value); + void throwRangeError(Value value); void throwUnimplemented(const QString &message); void setProperty(String *name, Value value); diff --git a/qmljs_objects.h b/qmljs_objects.h index b1d55a6390..522958b585 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -374,6 +374,8 @@ struct EvalErrorObject: ErrorObject { struct RangeErrorObject: ErrorObject { RangeErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + RangeErrorObject(ExecutionContext *ctx, const QString &msg) + : ErrorObject(Value::fromString(ctx,msg)) { setNameProperty(ctx); } virtual QString className() { return QStringLiteral("RangeError"); } }; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index e349957094..cd4d22d8fd 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1562,8 +1562,7 @@ Value ArrayCtor::call(ExecutionContext *ctx) quint32 isize = Value::toUInt32(size); if (size != double(isize)) { - // ### Should be a RangeError - ctx->throwError(QStringLiteral("Invalid array length")); + ctx->throwRangeError(ctx->argument(0)); return Value::undefinedValue(); } -- cgit v1.2.3 From 765122b96fbdbf9e2cda32e6ae383c2c3826d758 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 11 Jan 2013 10:13:02 +0100 Subject: More correct check for array indices Add a helper function to Value to check whether a value is a valid array index/length and use it to convert to array indices Change-Id: Ic216c0f062dd4d54efb3f55bd50c5921f65591c1 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 6 +++++- qmljs_runtime.cpp | 46 ++++++++++++++++++++++------------------------ qmljs_value.h | 32 ++++++++++++++++++++++++++++++++ qv4ecmaobjects.cpp | 9 +++++---- 4 files changed, 64 insertions(+), 29 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 9d84d0ad84..36e9d1e240 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -480,7 +480,11 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property if (isArray && name->isEqualTo(ctx->engine->id_length)) { if (desc->type != PropertyDescriptor::Data) goto reject; - array.setLength(desc->value.toUInt32(ctx)); + bool ok; + uint l = desc->value.asArrayLength(&ok); + if (!ok) + ctx->throwRangeError(desc->value); + array.setLength(l); } if (!members) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 53d6e2605b..048e8a5030 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -556,44 +556,42 @@ void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Val Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) { - if (index.isNumber()) { - uint idx = index.toUInt32(ctx); - - if (Object *o = object.asObject()) - return o->__get__(ctx, idx); - - if (object.isString()) { - const QString s = object.stringValue()->toQString().mid(idx, 1); - if (s.isNull()) - return Value::undefinedValue(); - else - return Value::fromString(ctx, s); - } + uint idx = index.asArrayIndex(); + if (object.isString() && idx < UINT_MAX) { + const QString s = object.stringValue()->toQString().mid(idx, 1); + if (s.isNull()) + return Value::undefinedValue(); + else + return Value::fromString(ctx, s); } - String *name = index.toString(ctx); - if (! object.isObject()) object = __qmljs_to_object(object, ctx); - return object.objectValue()->__get__(ctx, name); + Object *o = object.objectValue(); + + if (idx < UINT_MAX) + return o->__get__(ctx, idx); + + String *name = index.toString(ctx); + return o->__get__(ctx, name); } void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value value) { - if (! object.isObject()) + if (!object.isObject()) object = __qmljs_to_object(object, ctx); - if (index.isNumber()) { - if (Object *o = object.asObject()) { - o->__put__(ctx, index.toUInt32(ctx), value); - return; - } + Object *o = object.objectValue(); + + uint idx = index.asArrayIndex(); + if (idx < UINT_MAX) { + o->__put__(ctx, idx, value); + return; } String *name = index.toString(ctx); - - object.objectValue()->__put__(ctx, name, value); + o->__put__(ctx, name, value); } Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx) diff --git a/qmljs_value.h b/qmljs_value.h index 48ec2d4541..cbd7d5b63c 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -249,6 +249,8 @@ struct Value RegExpObject *asRegExpObject() const; ArrayObject *asArrayObject() const; ErrorObject *asErrorObject() const; + uint asArrayIndex() const; + uint asArrayLength(bool *ok) const; Value property(ExecutionContext *ctx, String *name) const; @@ -363,6 +365,36 @@ inline unsigned int Value::toUInt32(ExecutionContext *ctx) { return toUInt32(d); } +inline uint Value::asArrayIndex() const +{ + if (isInteger() && int_32 >= 0) + return (uint)int_32; + if (!isDouble()) + return UINT_MAX; + uint idx = (uint)dbl; + if (idx != dbl) + return UINT_MAX; + return idx; +} + +inline uint Value::asArrayLength(bool *ok) const +{ + *ok = true; + if (isInteger() && int_32 >= 0) + return (uint)int_32; + if (!isDouble()) { + *ok = false; + return UINT_MAX; + } + uint idx = (uint)dbl; + if ((double)idx != dbl) { + *ok = false; + return UINT_MAX; + } + return idx; +} + + } // namespace VM } // namespace QQmlJS diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index cd4d22d8fd..5f5abe62d3 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1558,15 +1558,16 @@ Value ArrayCtor::call(ExecutionContext *ctx) ArrayObject *a = ctx->engine->newArrayObject(); Array &value = a->array; if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { - double size = ctx->argument(0).asDouble(); - quint32 isize = Value::toUInt32(size); + bool ok; + uint len = ctx->argument(0).asArrayLength(&ok); + qDebug() << len << ok << (uint)ctx->argument(0).doubleValue(); - if (size != double(isize)) { + if (!ok) { ctx->throwRangeError(ctx->argument(0)); return Value::undefinedValue(); } - value.setLength(isize); + value.setLength(len); } else { for (unsigned int i = 0; i < ctx->argumentCount; ++i) { value.set(i, ctx->argument(i)); -- cgit v1.2.3 From 2e84d8fc90a8285ab4fc0fca800b9f1ae5401111 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 11 Jan 2013 10:19:29 +0100 Subject: Fix inplaceBinOp for index properties Change-Id: If72e46deacb22d290dd3bda4093a7807d33a097d Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 22 ++++++++++------------ qmljs_objects.h | 4 +--- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 36e9d1e240..ebbcebf60d 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -112,6 +112,16 @@ bool Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *c bool Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) { + uint idx = index.asArrayIndex(); + if (idx < UINT_MAX) { + bool hasProperty = false; + Value v = __get__(ctx, idx, &hasProperty); + if (!hasProperty) + return false; + v = op(v, rhs, ctx); + __put__(ctx, idx, v); + return true; + } String *name = index.toString(ctx); assert(name); return inplaceBinOp(rhs, name, op, ctx); @@ -661,18 +671,6 @@ void ForEachIteratorObject::getCollectables(QVector &objects) objects.append(it.object); } -bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) -{ - if (index.isNumber()) { - const quint32 idx = index.toUInt32(ctx); - Value v = __get__(ctx, idx); - v = op(v, rhs, ctx); - array.set(idx, v); - return true; - } - return Object::inplaceBinOp(rhs, index, op, ctx); -} - Function::~Function() { diff --git a/qmljs_objects.h b/qmljs_objects.h index 522958b585..6dc8d31abd 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -154,7 +154,7 @@ struct Object: Managed { Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const; bool inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); - virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); + bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); /* The spec default: Writable: true, Enumerable: false, Configurable: true */ void defineDefaultProperty(String *name, Value value); @@ -216,8 +216,6 @@ struct ArrayObject: Object { ArrayObject(const Array &value): Object(value) { isArray = true; } virtual QString className() { return QStringLiteral("Array"); } virtual ArrayObject *asArrayObject() { return this; } - - virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); }; struct Function { -- cgit v1.2.3 From ff9b319ae2da8cb88e4afb2ca053a48a3ba66946 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 11 Jan 2013 14:32:15 +0100 Subject: Add String::toUInt32() and fix has value for "01", ... Change-Id: I73526fdb4410d163b8a66fb5b467135fcb31d0d7 Reviewed-by: Simon Hausmann --- qv4string.cpp | 30 +++++++++++++++++++++++++++--- qv4string.h | 1 + 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/qv4string.cpp b/qv4string.cpp index eb1e2603c1..c6420d5664 100644 --- a/qv4string.cpp +++ b/qv4string.cpp @@ -44,14 +44,18 @@ namespace QQmlJS { namespace VM { -static uint toArrayIndex(const QChar *ch, const QChar *end) +static uint toArrayIndex(const QChar *ch, const QChar *end, bool *ok = 0) { + if (ok) + *ok = false; uint i = ch->unicode() - '0'; if (i > 9) return String::InvalidArrayIndex; ++ch; - if (i == 0 && ch == end) - return i; + // reject "01", "001", ... + if (i == 0 && ch != end) + return String::InvalidArrayIndex; + while (ch < end) { uint x = ch->unicode() - '0'; if (x > 9) @@ -63,6 +67,8 @@ static uint toArrayIndex(const QChar *ch, const QChar *end) i = n; ++ch; } + if (ok) + *ok = true; return i; } @@ -76,6 +82,24 @@ uint String::asArrayIndexSlow() const return toArrayIndex(ch, end); } +uint String::toUInt(bool *ok) const +{ + *ok = true; + + if (_hashValue == InvalidHashValue) + createHashValue(); + if (_hashValue > LargestHashedArrayIndex) { + *ok = false; + return InvalidArrayIndex; + } + if (_hashValue < LargestHashedArrayIndex) + return _hashValue; + + const QChar *ch = _text.constData(); + const QChar *end = ch + _text.length(); + return toArrayIndex(ch, end, ok); +} + void String::createHashValue() const { const QChar *ch = _text.constData(); diff --git a/qv4string.h b/qv4string.h index 3464af5688..0b35525304 100644 --- a/qv4string.h +++ b/qv4string.h @@ -81,6 +81,7 @@ struct String { return asArrayIndexSlow(); } uint asArrayIndexSlow() const; + uint toUInt(bool *ok) const; private: friend class StringPool; -- cgit v1.2.3 From f9682cb82e458667d9db1a5ae955a8943f937aae Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 11 Jan 2013 14:33:10 +0100 Subject: Make Array.length a real property with attributes Change-Id: Ib955e151c1d60145948eb5ac76283854dda2cefe Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 10 +++---- qmljs_engine.h | 4 +-- qmljs_objects.cpp | 40 ++++++++++++++++++-------- qmljs_objects.h | 5 ++-- qmljs_value.h | 17 +++++++++++ qv4array.cpp | 4 +-- qv4array.h | 82 ++++++++++++------------------------------------------ qv4ecmaobjects.cpp | 24 ++++++++-------- qv4ecmaobjects_p.h | 2 ++ qv4mm.cpp | 2 +- 10 files changed, 90 insertions(+), 100 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index e8a33893e0..fc7c8671ea 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -99,7 +99,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) stringPrototype = new (memoryManager) StringPrototype(rootContext); numberPrototype = new (memoryManager) NumberPrototype(); booleanPrototype = new (memoryManager) BooleanPrototype(); - arrayPrototype = new (memoryManager) ArrayPrototype(); + arrayPrototype = new (memoryManager) ArrayPrototype(rootContext); datePrototype = new (memoryManager) DatePrototype(); functionPrototype = new (memoryManager) FunctionPrototype(rootContext); regExpPrototype = new (memoryManager) RegExpPrototype(); @@ -316,16 +316,16 @@ Object *ExecutionEngine::newFunctionObject(ExecutionContext *ctx) return object; } -ArrayObject *ExecutionEngine::newArrayObject() +ArrayObject *ExecutionEngine::newArrayObject(ExecutionContext *ctx) { - ArrayObject *object = new (memoryManager) ArrayObject(); + ArrayObject *object = new (memoryManager) ArrayObject(ctx); object->prototype = arrayPrototype; return object; } -ArrayObject *ExecutionEngine::newArrayObject(const Array &value) +ArrayObject *ExecutionEngine::newArrayObject(ExecutionContext *ctx, const Array &value) { - ArrayObject *object = new (memoryManager) ArrayObject(value); + ArrayObject *object = new (memoryManager) ArrayObject(ctx, value); object->prototype = arrayPrototype; return object; } diff --git a/qmljs_engine.h b/qmljs_engine.h index 776363a377..a09117d9cb 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -184,8 +184,8 @@ struct ExecutionEngine Object *newFunctionObject(ExecutionContext *ctx); - ArrayObject *newArrayObject(); - ArrayObject *newArrayObject(const Array &value); + ArrayObject *newArrayObject(ExecutionContext *ctx); + ArrayObject *newArrayObject(ExecutionContext *ctx, const Array &value); FunctionObject *newArrayCtor(ExecutionContext *ctx); Object *newDateObject(const Value &value); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index ebbcebf60d..00fd02fb6f 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -250,11 +250,6 @@ Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) *hasProperty = true; return Value::fromObject(prototype); } - if (isArray && name->isEqualTo(ctx->engine->id_length)) { - if (hasProperty) - *hasProperty = true; - return Value::fromDouble(array.length()); - } if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name)) { if (hasProperty) @@ -488,13 +483,20 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property PropertyDescriptor *current; if (isArray && name->isEqualTo(ctx->engine->id_length)) { - if (desc->type != PropertyDescriptor::Data) + PropertyDescriptor *lp = array.getLengthProperty(); + if (!lp->isWritable() || desc->type != PropertyDescriptor::Data || desc->isConfigurable() || desc->isEnumerable()) goto reject; - bool ok; - uint l = desc->value.asArrayLength(&ok); - if (!ok) - ctx->throwRangeError(desc->value); - array.setLength(l); + if (!desc->value.isUndefined()) { + bool ok; + uint l = desc->value.asArrayLength(&ok); + if (!ok) + ctx->throwRangeError(desc->value); + if (!array.setLength(l)) + goto reject; + } + if (!desc->isWritable()) + lp->writable = PropertyDescriptor::Disabled; + return true; } if (!members) @@ -664,6 +666,22 @@ Value Object::call(ExecutionContext *context, Value , Value *, int) return Value::undefinedValue(); } +void ArrayObject::init(ExecutionContext *context) +{ + isArray = true; + if (!members) + members.reset(new PropertyTable()); + PropertyDescriptor *pd = members->insert(context->engine->id_length); + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Disabled; + pd->value = Value::fromInt32(0); + array.setLengthProperty(pd); +} + + + void ForEachIteratorObject::getCollectables(QVector &objects) { Object::getCollectables(objects); diff --git a/qmljs_objects.h b/qmljs_objects.h index 6dc8d31abd..5cd0de051c 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -212,8 +212,9 @@ struct DateObject: Object { }; struct ArrayObject: Object { - ArrayObject() { isArray = true; } - ArrayObject(const Array &value): Object(value) { isArray = true; } + ArrayObject(ExecutionContext *ctx) { init(ctx); } + ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); } + void init(ExecutionContext *context); virtual QString className() { return QStringLiteral("Array"); } virtual ArrayObject *asArrayObject() { return this; } }; diff --git a/qmljs_value.h b/qmljs_value.h index cbd7d5b63c..96d56aae23 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -45,6 +45,7 @@ #include #include +#include #include namespace QQmlJS { @@ -187,6 +188,7 @@ struct Value static Value fromBoolean(Bool b); static Value fromDouble(double d); static Value fromInt32(int i); + static Value fromUInt32(uint i); static Value fromString(String *s); static Value fromObject(Object *o); @@ -305,6 +307,18 @@ inline Value Value::fromInt32(int i) return v; } +inline Value Value::fromUInt32(uint i) +{ + Value v; + if (i < INT_MAX) { + v.tag = _Integer_Type; + v.int_32 = (int)i; + } else { + v.dbl = i; + } + return v; +} + inline Value Value::fromString(String *s) { Value v; @@ -382,6 +396,9 @@ inline uint Value::asArrayLength(bool *ok) const *ok = true; if (isInteger() && int_32 >= 0) return (uint)int_32; + if (isString()) + return stringValue()->toUInt(ok); + if (!isDouble()) { *ok = false; return UINT_MAX; diff --git a/qv4array.cpp b/qv4array.cpp index da86de8014..774908c56f 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -463,6 +463,7 @@ SparseArrayNode *SparseArray::insert(uint akey) Array::Array(const Array &other) : len(other.len) + , lengthProperty(0) , values(other.values) , sparse(0) { @@ -528,7 +529,6 @@ Value Array::indexOf(Value v, uint fromIndex, ExecutionContext *ctx, Object *o) void Array::concat(const Array &other) { initSparse(); - int len = length(); int newLen = len + other.length(); if (other.sparse) initSparse(); @@ -550,7 +550,7 @@ void Array::concat(const Array &other) values.resize(oldSize + other.length()); memcpy(values.data() + oldSize, other.values.constData() + other.offset, other.length()*sizeof(PropertyDescriptor)); } - setLength(newLen); + setLengthUnchecked(newLen); } void Array::sort(ExecutionContext *context, Object *thisObject, const Value &comparefn) diff --git a/qv4array.h b/qv4array.h index 85c42b8f0f..764e677a12 100644 --- a/qv4array.h +++ b/qv4array.h @@ -362,6 +362,7 @@ inline SparseArrayNode *SparseArray::upperBound(uint akey) class Array { uint len; + PropertyDescriptor *lengthProperty; union { uint freeList; uint offset; @@ -429,9 +430,11 @@ public: void initSparse(); uint length() const { return len; } - void setLength(uint l) { - len = l; - if (len > 0x100000) + bool setLength(uint l) { + if (lengthProperty && !lengthProperty->isWritable()) + return false; + setLengthUnchecked(l); + if (len >= 0x100000) initSparse(); if (sparse) { SparseArrayNode *it = sparse->lowerBound(l); @@ -446,66 +449,17 @@ public: } else if (values.size() > (int)len){ values.resize(len); } + return true; } -#if 0 - struct sparse_iterator - { - Array *array; - SparseArrayNode *i; - PropertyDescriptor *pd; - - inline sparse_iterator(SparseArrayNode *node) : i(node), pd(0) { } - - inline uint key() const { return i->key(); } - inline uint &value() const { return i->value; } - inline uint &operator*() const { return i->value; } - inline uint *operator->() const { return &i->value; } - inline bool operator==(const sparse_iterator &o) const { return i == o.i; } - inline bool operator!=(const sparse_iterator &o) const { return i != o.i; } - - inline sparse_iterator &operator++() { - i = i->nextNode(); - return *this; - } - inline sparse_iterator operator++(int) { - sparse_iterator r = *this; - i = i->nextNode(); - return r; - } - inline sparse_iterator &operator--() { - i = i->previousNode(); - return *this; - } - inline sparse_iterator operator--(int) { - sparse_iterator r = *this; - i = i->previousNode(); - return r; - } - inline sparse_iterator operator+(int j) const - { sparse_iterator r = *this; if (j > 0) while (j--) ++r; else while (j++) --r; return r; } - inline sparse_iterator operator-(int j) const { return operator+(-j); } - inline sparse_iterator &operator+=(int j) { return *this = *this + j; } - inline sparse_iterator &operator-=(int j) { return *this = *this - j; } - - friend class SparseArray; - }; - - sparse_iterator begin() const { - return sparse_iterator(sparse ? sparse->begin() : 0); - } - sparse_iterator end() const { - return sparse_iterator(sparse ? sparse->end() : 0); - } - - sparse_iterator find(uint index) const { - return sparse_iterator(sparse ? sparse->find(index) : 0); - } + void setLengthProperty(PropertyDescriptor *pd) { lengthProperty = pd; } + PropertyDescriptor *getLengthProperty() { return lengthProperty; } - PropertyDescriptor *at(sparse_iterator it) const { - return it.i ? descriptor(*it) : 0; + void setLengthUnchecked(uint l) { + len = l; + if (lengthProperty) + lengthProperty->value = Value::fromUInt32(l); } -#endif PropertyDescriptor *insert(uint index) { PropertyDescriptor *pd; @@ -526,7 +480,7 @@ public: pd = descriptor(n->value); } if (index >= len) - len = index + 1; + setLengthUnchecked(index + 1); return pd; } @@ -612,7 +566,7 @@ public: uint idx = allocValue(v); sparse->push_front(idx); } - ++len; + setLengthUnchecked(len + 1); } PropertyDescriptor *front() { PropertyDescriptor *pd = 0; @@ -637,7 +591,7 @@ public: uint idx = sparse->pop_front(); freeValue(idx); } - --len; + setLengthUnchecked(len - 1); } void push_back(Value v) { if (!sparse) { @@ -648,7 +602,7 @@ public: uint idx = allocValue(v); sparse->push_back(idx, len); } - ++len; + setLengthUnchecked(len + 1); } PropertyDescriptor *back() { PropertyDescriptor *pd = 0; @@ -674,7 +628,7 @@ public: if (idx != UINT_MAX) freeValue(idx); } - --len; + setLengthUnchecked(len - 1); } SparseArrayNode *sparseBegin() { return sparse ? sparse->begin() : 0; } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 5f5abe62d3..46cae29de7 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -606,7 +606,7 @@ Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) if (!O) ctx->throwTypeError(); - ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); + ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); Array &a = array->array; ObjectIterator it(O, ObjectIterator::NoFlags); while (1) { @@ -793,7 +793,7 @@ Value ObjectPrototype::method_keys(ExecutionContext *ctx) Object *o = ctx->argument(0).objectValue(); - ArrayObject *a = ctx->engine->newArrayObject(); + ArrayObject *a = ctx->engine->newArrayObject(ctx); ObjectIterator it(o, ObjectIterator::EnumberableOnly); while (1) { @@ -1555,19 +1555,18 @@ ArrayCtor::ArrayCtor(ExecutionContext *scope) Value ArrayCtor::call(ExecutionContext *ctx) { - ArrayObject *a = ctx->engine->newArrayObject(); + ArrayObject *a = ctx->engine->newArrayObject(ctx); Array &value = a->array; if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { bool ok; uint len = ctx->argument(0).asArrayLength(&ok); - qDebug() << len << ok << (uint)ctx->argument(0).doubleValue(); if (!ok) { ctx->throwRangeError(ctx->argument(0)); return Value::undefinedValue(); } - value.setLength(len); + value.setLengthUnchecked(len); } else { for (unsigned int i = 0; i < ctx->argumentCount; ++i) { value.set(i, ctx->argument(i)); @@ -1636,7 +1635,7 @@ Value ArrayPrototype::method_concat(ExecutionContext *ctx) result.set(k, arg); } - return Value::fromObject(ctx->engine->newArrayObject(result)); + return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); } Value ArrayPrototype::method_join(ExecutionContext *ctx) @@ -1793,7 +1792,7 @@ Value ArrayPrototype::method_slice(ExecutionContext *ctx) if (! v.isUndefined()) result.set(n++, v); } - return Value::fromObject(ctx->engine->newArrayObject(result)); + return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); } Value ArrayPrototype::method_sort(ExecutionContext *ctx) @@ -1819,7 +1818,7 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) double start = ctx->argument(0).toInteger(ctx); double deleteCount = ctx->argument(1).toInteger(ctx); - Value a = Value::fromObject(ctx->engine->newArrayObject()); + Value a = Value::fromObject(ctx->engine->newArrayObject(ctx)); QVector items; for (unsigned int i = 2; i < ctx->argumentCount; ++i) items << ctx->argument(i); @@ -1952,8 +1951,8 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); - ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); - a->array.setLength(instance->array.length()); + ArrayObject *a = ctx->engine->newArrayObject(ctx)->asArrayObject(); + a->array.setLengthUnchecked(instance->array.length()); for (quint32 k = 0; k < instance->array.length(); ++k) { Value v = instance->__get__(ctx, k); if (v.isUndefined()) @@ -1976,7 +1975,7 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); - ArrayObject *a = ctx->engine->newArrayObject()->asArrayObject(); + ArrayObject *a = ctx->engine->newArrayObject(ctx)->asArrayObject(); for (quint32 k = 0; k < instance->array.length(); ++k) { Value v = instance->__get__(ctx, k); if (v.isUndefined()) @@ -1988,7 +1987,6 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); if (__qmljs_to_boolean(r, ctx)) { const uint index = a->array.length(); - a->array.setLength(index + 1); a->array.set(index, v); } } @@ -2862,7 +2860,7 @@ Value RegExpPrototype::method_exec(ExecutionContext *ctx) return Value::nullValue(); // fill in result data - ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject(); + ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); int captured = match.lastCapturedIndex(); for (int i = 0; i <= captured; ++i) array->array.push_back(Value::fromString(ctx, match.captured(i))); diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 8b16b952d1..f81d7f7979 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -172,6 +172,8 @@ struct ArrayCtor: FunctionObject struct ArrayPrototype: ArrayObject { + ArrayPrototype(ExecutionContext *context) : ArrayObject(context) {} + void init(ExecutionContext *ctx, const Value &ctor); static Value method_toString(ExecutionContext *ctx); diff --git a/qv4mm.cpp b/qv4mm.cpp index cf50e9dee6..0af07d4701 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -56,7 +56,7 @@ struct MemoryManager::Data bool aggressiveGC; ExecutionEngine *engine; - enum { MaxItemSize = 128 }; + enum { MaxItemSize = 256 }; Managed *smallItems[MaxItemSize/16]; struct Chunk { PageAllocation memory; -- cgit v1.2.3 From 15946702b0c99b050bb6fa8d26d19b9d45fac900 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 11 Jan 2013 16:28:05 +0100 Subject: Fixes for defineProperty toPropertyDescriptor and PropertyDescriptor::type() were returning the wrong thing in case there wasn't an explicitly defined value set. Fix a few other corner cases. Change-Id: Iaa351a3e6e24c47e9d549ff113b6907762d11fd2 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 16 ++++++++-------- qmljs_runtime.cpp | 2 +- qmljs_runtime.h | 4 ++-- qmljs_value.h | 27 ++++++++++++++++----------- qv4ecmaobjects.cpp | 24 +++++++++++------------- qv4propertydescriptor.h | 30 ++++++++++++++++++++---------- qv4string.cpp | 21 +++++++++------------ 7 files changed, 67 insertions(+), 57 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 00fd02fb6f..78f51e92f2 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -484,17 +484,17 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property if (isArray && name->isEqualTo(ctx->engine->id_length)) { PropertyDescriptor *lp = array.getLengthProperty(); - if (!lp->isWritable() || desc->type != PropertyDescriptor::Data || desc->isConfigurable() || desc->isEnumerable()) + if (!lp->isWritable() || desc->type == PropertyDescriptor::Accessor || desc->isConfigurable() || desc->isEnumerable()) goto reject; - if (!desc->value.isUndefined()) { + if (desc->type == PropertyDescriptor::Data) { bool ok; - uint l = desc->value.asArrayLength(&ok); + uint l = desc->value.asArrayLength(ctx, &ok); if (!ok) ctx->throwRangeError(desc->value); if (!array.setLength(l)) goto reject; } - if (!desc->isWritable()) + if (desc->writable == PropertyDescriptor::Disabled) lp->writable = PropertyDescriptor::Disabled; return true; } @@ -560,8 +560,8 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property } else { // clause 10 assert(current->isAccessor() && desc->isAccessor()); if (!current->isConfigurable()) { - if ((desc->get && current->get != desc->get) || - (desc->set && current->set != desc->set)) + if (((quintptr)desc->get > 0x1 && current->get != desc->get) || + ((quintptr)desc->set > 0x1 && current->set != desc->set)) goto reject; } } @@ -637,8 +637,8 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, PropertyDe } else { // clause 10 assert(current->isAccessor() && desc->isAccessor()); if (!current->isConfigurable()) { - if ((desc->get && current->get != desc->get) || - (desc->set && current->set != desc->set)) + if (((quintptr)desc->get > 0x1 && current->get != desc->get) || + ((quintptr)desc->set > 0x1 && current->set != desc->set)) goto reject; } } diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 048e8a5030..70db6ffe9a 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -434,7 +434,7 @@ int __qmljs_string_length(ExecutionContext *, String *string) return string->toQString().length(); } -double __qmljs_string_to_number(ExecutionContext *, String *string) +double __qmljs_string_to_number(const String *string) { QString s = string->toQString(); s = s.trimmed(); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 66d9c780cc..48e4fa2f0d 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -127,7 +127,7 @@ Value __qmljs_string_literal_function(ExecutionContext *ctx); // strings String *__qmljs_string_from_utf8(ExecutionContext *ctx, const char *s); int __qmljs_string_length(ExecutionContext *ctx, String *string); -double __qmljs_string_to_number(ExecutionContext *ctx, String *string); +double __qmljs_string_to_number(const String *string); Value __qmljs_string_from_number(ExecutionContext *ctx, double number); Bool __qmljs_string_compare(ExecutionContext *ctx, String *left, String *right); Bool __qmljs_string_equal(String *left, String *right); @@ -304,7 +304,7 @@ inline double __qmljs_to_number(Value value, ExecutionContext *ctx) case Value::Integer_Type: return value.int_32; case Value::String_Type: - return __qmljs_string_to_number(ctx, value.stringValue()); + return __qmljs_string_to_number(value.stringValue()); case Value::Object_Type: { Value prim = __qmljs_to_primitive(value, ctx, NUMBER_HINT); return __qmljs_to_number(prim, ctx); diff --git a/qmljs_value.h b/qmljs_value.h index 96d56aae23..9a4fcdb44a 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -202,7 +202,7 @@ struct Value int toUInt16(ExecutionContext *ctx); int toInt32(ExecutionContext *ctx); - unsigned int toUInt32(ExecutionContext *ctx); + unsigned int toUInt32(ExecutionContext *ctx) const; Bool toBoolean(ExecutionContext *ctx) const; double toInteger(ExecutionContext *ctx) const; @@ -252,7 +252,7 @@ struct Value ArrayObject *asArrayObject() const; ErrorObject *asErrorObject() const; uint asArrayIndex() const; - uint asArrayLength(bool *ok) const; + uint asArrayLength(ExecutionContext *ctx, bool *ok) const; Value property(ExecutionContext *ctx, String *name) const; @@ -364,7 +364,7 @@ inline int Value::toInt32(ExecutionContext *ctx) return Value::toInt32(__qmljs_to_number(*this, ctx)); } -inline unsigned int Value::toUInt32(ExecutionContext *ctx) { +inline unsigned int Value::toUInt32(ExecutionContext *ctx) const { if (isConvertibleToInt()) return (unsigned) int_32; double d; @@ -391,20 +391,25 @@ inline uint Value::asArrayIndex() const return idx; } -inline uint Value::asArrayLength(bool *ok) const +inline uint Value::asArrayLength(ExecutionContext *ctx, bool *ok) const { *ok = true; - if (isInteger() && int_32 >= 0) + if (isConvertibleToInt() && int_32 >= 0) return (uint)int_32; + if (isDouble()) { + uint idx = (uint)dbl; + if ((double)idx != dbl) { + *ok = false; + return UINT_MAX; + } + return idx; + } if (isString()) return stringValue()->toUInt(ok); - if (!isDouble()) { - *ok = false; - return UINT_MAX; - } - uint idx = (uint)dbl; - if ((double)idx != dbl) { + uint idx = toUInt32(ctx); + double d = toNumber(ctx); + if (d != idx) { *ok = false; return UINT_MAX; } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 46cae29de7..eb46d429a1 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -717,7 +717,8 @@ Value ObjectPrototype::method_freeze(ExecutionContext *ctx) PropertyDescriptor *pd = it.next(&name, &index); if (!pd) break; - pd->writable = PropertyDescriptor::Disabled; + if (pd->type == PropertyDescriptor::Data) + pd->writable = PropertyDescriptor::Disabled; pd->configurable = PropertyDescriptor::Disabled; } return ctx->argument(0); @@ -878,7 +879,6 @@ Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx) Object *o = ctx->thisObject.toObject(ctx).objectValue(); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(f, 0); - pd.writable = PropertyDescriptor::Enabled; pd.configurable = PropertyDescriptor::Enabled; pd.enumberable = PropertyDescriptor::Enabled; o->__defineOwnProperty__(ctx, prop, &pd); @@ -898,7 +898,6 @@ Value ObjectPrototype::method_defineSetter(ExecutionContext *ctx) Object *o = ctx->thisObject.toObject(ctx).objectValue(); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(0, f); - pd.writable = PropertyDescriptor::Enabled; pd.configurable = PropertyDescriptor::Enabled; pd.enumberable = PropertyDescriptor::Enabled; o->__defineOwnProperty__(ctx, prop, &pd); @@ -928,24 +927,24 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Prope FunctionObject *f = get.asFunctionObject(); if (f) { desc->get = f; - } else if (!get.isUndefined()) { - __qmljs_throw_type_error(ctx); + } else if (get.isUndefined()) { + desc->get = (FunctionObject *)0x1; } else { - desc->get = 0; + __qmljs_throw_type_error(ctx); } desc->type = PropertyDescriptor::Accessor; } desc->set = 0; if (o->__hasProperty__(ctx, ctx->engine->id_set)) { - Value get = o->__get__(ctx, ctx->engine->id_set); - FunctionObject *f = get.asFunctionObject(); + Value set = o->__get__(ctx, ctx->engine->id_set); + FunctionObject *f = set.asFunctionObject(); if (f) { desc->set = f; - } else if (!get.isUndefined()) { - __qmljs_throw_type_error(ctx); + } else if (set.isUndefined()) { + desc->set = (FunctionObject *)0x1; } else { - desc->set = 0; + __qmljs_throw_type_error(ctx); } desc->type = PropertyDescriptor::Accessor; } @@ -956,7 +955,6 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Prope __qmljs_throw_type_error(ctx); desc->writable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_writable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; // writable forces it to be a data descriptor - desc->type = PropertyDescriptor::Data; desc->value = Value::undefinedValue(); } @@ -1559,7 +1557,7 @@ Value ArrayCtor::call(ExecutionContext *ctx) Array &value = a->array; if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { bool ok; - uint len = ctx->argument(0).asArrayLength(&ok); + uint len = ctx->argument(0).asArrayLength(ctx, &ok); if (!ok) { ctx->throwRangeError(ctx->argument(0)); diff --git a/qv4propertydescriptor.h b/qv4propertydescriptor.h index 2361e16db9..84582ef956 100644 --- a/qv4propertydescriptor.h +++ b/qv4propertydescriptor.h @@ -102,6 +102,10 @@ struct PropertyDescriptor { writable = Disabled; } else { writable = Undefined; + if ((quintptr)get == 0x1) + get = 0; + if ((quintptr)set == 0x1) + set = 0; } if (enumberable == Undefined) enumberable = Disabled; @@ -109,9 +113,9 @@ struct PropertyDescriptor { configurable = Disabled; } - inline bool isData() const { return type == Data; } + inline bool isData() const { return type == Data || writable != Undefined; } inline bool isAccessor() const { return type == Accessor; } - inline bool isGeneric() const { return type == Generic; } + inline bool isGeneric() const { return type == Generic && writable == Undefined; } inline bool isWritable() const { return writable == Enabled; } inline bool isEnumerable() const { return enumberable == Enabled; } @@ -121,7 +125,7 @@ struct PropertyDescriptor { return type == Generic && writable == Undefined && enumberable == Undefined && configurable == Undefined; } inline bool isSubset(PropertyDescriptor *other) { - if (type != other->type) + if (type != Generic && type != other->type) return false; if (enumberable != Undefined && enumberable != other->enumberable) return false; @@ -131,24 +135,30 @@ struct PropertyDescriptor { return false; if (type == Data && !value.sameValue(other->value)) return false; - if (type == Accessor && (get != other->get || set != other->set)) - return false; + if (type == Accessor) { + if ((quintptr)get != 0x1 && get != other->get) + return false; + qDebug() << "isSubset" << set << other->set; + if ((quintptr)set != 0x1 && set != other->set) + return false; + } return true; } inline void operator+=(const PropertyDescriptor &other) { - type = other.type; if (other.enumberable != Undefined) enumberable = other.enumberable; if (other.configurable != Undefined) configurable = other.configurable; if (other.writable != Undefined) writable = other.writable; - if (type == Accessor) { + if (other.type == Accessor) { + type = Accessor; if (other.get) - get = other.get; + get = ((quintptr)other.get == 0x1) ? 0 : other.get; if (other.set) - set = other.set; - } else { + set = ((quintptr)other.set == 0x1) ? 0 : other.set; + } else if (other.type == Data){ + type = Data; value = other.value; } } diff --git a/qv4string.cpp b/qv4string.cpp index c6420d5664..997f1dd0f2 100644 --- a/qv4string.cpp +++ b/qv4string.cpp @@ -40,14 +40,13 @@ ****************************************************************************/ #include "qv4string.h" +#include "qmljs_runtime.h" namespace QQmlJS { namespace VM { -static uint toArrayIndex(const QChar *ch, const QChar *end, bool *ok = 0) +static uint toArrayIndex(const QChar *ch, const QChar *end) { - if (ok) - *ok = false; uint i = ch->unicode() - '0'; if (i > 9) return String::InvalidArrayIndex; @@ -67,8 +66,6 @@ static uint toArrayIndex(const QChar *ch, const QChar *end, bool *ok = 0) i = n; ++ch; } - if (ok) - *ok = true; return i; } @@ -88,16 +85,16 @@ uint String::toUInt(bool *ok) const if (_hashValue == InvalidHashValue) createHashValue(); - if (_hashValue > LargestHashedArrayIndex) { - *ok = false; - return InvalidArrayIndex; - } if (_hashValue < LargestHashedArrayIndex) return _hashValue; - const QChar *ch = _text.constData(); - const QChar *end = ch + _text.length(); - return toArrayIndex(ch, end, ok); + double d = __qmljs_string_to_number(this); + qDebug() << "toUInt" << d; + uint l = (uint)d; + if (d == l) + return l; + *ok = false; + return UINT_MAX; } void String::createHashValue() const -- cgit v1.2.3 From 1df0ab8a7403423c6d7354dd19d2d4870dfa8b6d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 12 Jan 2013 15:32:58 +0100 Subject: Initialize variable Change-Id: I1dbb45ddca1ba906213b0ec26549d2f347fe6a7e Reviewed-by: Simon Hausmann --- qv4array.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4array.h b/qv4array.h index 764e677a12..de60ef6cad 100644 --- a/qv4array.h +++ b/qv4array.h @@ -424,7 +424,7 @@ class Array } public: - Array() : len(0), offset(0), sparse(0) {} + Array() : len(0), lengthProperty(0), offset(0), sparse(0) {} Array(const Array &other); ~Array() { delete sparse; } void initSparse(); -- cgit v1.2.3 From f0ba47dbaf89f6519175bf8a509db14a880fa0ae Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 12 Jan 2013 15:47:30 +0100 Subject: Correctly set the length property of Array objects Change-Id: Ie193cf869127eaad5302a5130acc9d16e4d18838 Reviewed-by: Simon Hausmann --- qmljs_objects.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmljs_objects.h b/qmljs_objects.h index 5cd0de051c..787a42fab9 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -213,7 +213,7 @@ struct DateObject: Object { struct ArrayObject: Object { ArrayObject(ExecutionContext *ctx) { init(ctx); } - ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); } + ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setLengthUnchecked(array.length()); } void init(ExecutionContext *context); virtual QString className() { return QStringLiteral("Array"); } virtual ArrayObject *asArrayObject() { return this; } -- cgit v1.2.3 From 0166ef4b38d6e98106dae24b68002d2b894bd24e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 12 Jan 2013 15:54:44 +0100 Subject: Update test results Change-Id: If6d29a3fcffae7985e4afea258551b9fa730155c Reviewed-by: Simon Hausmann --- tests/TestExpectations | 167 +++---------------------------------------------- 1 file changed, 9 insertions(+), 158 deletions(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index cf899fb67c..d7b6798ec9 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -10,7 +10,6 @@ S15.1.3.1_A2.5_T1 S15.1.3.2_A2.5_T1 # Tests failing that are supposed to pass. -S15.4.5.2_A3_T4 failing 15.4.4.15-8-9 failing 15.4.4.15-5-16 failing 15.4.4.15-5-12 failing @@ -212,7 +211,6 @@ S11.3.2_A4_T4 failing 11.4.1-2-5 failing 11.4.1-2-6 failing 11.4.1-4.a-10 failing -11.4.1-4.a-12 failing 11.4.1-4.a-13 failing 11.4.1-4.a-5 failing 11.4.1-4.a-7 failing @@ -957,7 +955,6 @@ S15.1.3.2_A5.3 failing 15.2.3.4-2-1 failing 15.2.3.4-4-2 failing 15.2.3.4-4-44 failing -15.2.3.4-4-49 failing 15.2.3.5-4-120 failing 15.2.3.5-4-13 failing 15.2.3.5-4-145 failing @@ -988,28 +985,11 @@ S15.1.3.2_A5.3 failing 15.2.3.6-3-66 failing 15.2.3.6-3-94-1 failing 15.2.3.6-3-94 failing -15.2.3.6-4-105 failing 15.2.3.6-4-108 failing 15.2.3.6-4-111 failing 15.2.3.6-4-116 failing 15.2.3.6-4-117 failing -15.2.3.6-4-118 failing -15.2.3.6-4-125 failing -15.2.3.6-4-133 failing -15.2.3.6-4-134 failing -15.2.3.6-4-135 failing -15.2.3.6-4-136 failing -15.2.3.6-4-138 failing -15.2.3.6-4-139 failing -15.2.3.6-4-140 failing -15.2.3.6-4-141 failing -15.2.3.6-4-145 failing -15.2.3.6-4-152 failing -15.2.3.6-4-153 failing -15.2.3.6-4-156 failing -15.2.3.6-4-157 failing 15.2.3.6-4-163 failing -15.2.3.6-4-165 failing 15.2.3.6-4-168 failing 15.2.3.6-4-169 failing 15.2.3.6-4-170 failing @@ -1018,9 +998,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-174 failing 15.2.3.6-4-176 failing 15.2.3.6-4-177 failing -15.2.3.6-4-184 failing -15.2.3.6-4-185 failing -15.2.3.6-4-186 failing 15.2.3.6-4-188 failing 15.2.3.6-4-189 failing 15.2.3.6-4-191 failing @@ -1028,16 +1005,8 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-193 failing 15.2.3.6-4-20 failing 15.2.3.6-4-256 failing -15.2.3.6-4-261 failing -15.2.3.6-4-262 failing -15.2.3.6-4-263 failing -15.2.3.6-4-264 failing 15.2.3.6-4-266 failing 15.2.3.6-4-269 failing -15.2.3.6-4-271 failing -15.2.3.6-4-272 failing -15.2.3.6-4-274 failing -15.2.3.6-4-280 failing 15.2.3.6-4-291-1 failing 15.2.3.6-4-292-1 failing 15.2.3.6-4-293-2 failing @@ -1046,28 +1015,13 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-295-1 failing 15.2.3.6-4-296-1 failing 15.2.3.6-4-297-1 failing -15.2.3.6-4-298-1 failing 15.2.3.6-4-299-1 failing 15.2.3.6-4-300-1 failing -15.2.3.6-4-329 failing -15.2.3.6-4-330 failing -15.2.3.6-4-331 failing -15.2.3.6-4-336 failing -15.2.3.6-4-343 failing -15.2.3.6-4-344 failing -15.2.3.6-4-345 failing -15.2.3.6-4-350 failing 15.2.3.6-4-354-13 failing 15.2.3.6-4-354-4 failing 15.2.3.6-4-354-8 failing -15.2.3.6-4-357 failing -15.2.3.6-4-358 failing -15.2.3.6-4-359 failing 15.2.3.6-4-360-3 failing 15.2.3.6-4-360-7 failing -15.2.3.6-4-371 failing -15.2.3.6-4-372 failing -15.2.3.6-4-373 failing 15.2.3.6-4-38 failing 15.2.3.6-4-402 failing 15.2.3.6-4-405 failing @@ -1092,14 +1046,10 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-531-17 failing 15.2.3.6-4-531-4 failing 15.2.3.6-4-531-8 failing -15.2.3.6-4-534 failing -15.2.3.6-4-535 failing 15.2.3.6-4-538-3 failing 15.2.3.6-4-538-7 failing 15.2.3.6-4-543 failing 15.2.3.6-4-544 failing -15.2.3.6-4-552 failing -15.2.3.6-4-553 failing 15.2.3.6-4-561 failing 15.2.3.6-4-562 failing 15.2.3.6-4-578 failing @@ -1130,30 +1080,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-622 failing 15.2.3.6-4-623 failing 15.2.3.6-4-624 failing -15.2.3.6-4-82-1 failing -15.2.3.6-4-82-10 failing -15.2.3.6-4-82-11 failing -15.2.3.6-4-82-12 failing -15.2.3.6-4-82-13 failing -15.2.3.6-4-82-14 failing -15.2.3.6-4-82-15 failing -15.2.3.6-4-82-16 failing -15.2.3.6-4-82-17 failing -15.2.3.6-4-82-18 failing -15.2.3.6-4-82-19 failing -15.2.3.6-4-82-2 failing -15.2.3.6-4-82-20 failing -15.2.3.6-4-82-21 failing -15.2.3.6-4-82-22 failing -15.2.3.6-4-82-23 failing -15.2.3.6-4-82-24 failing -15.2.3.6-4-82-3 failing -15.2.3.6-4-82-4 failing -15.2.3.6-4-82-5 failing -15.2.3.6-4-82-6 failing -15.2.3.6-4-82-7 failing -15.2.3.6-4-82-8 failing -15.2.3.6-4-82-9 failing 15.2.3.7-2-14 failing 15.2.3.7-5-a-15 failing 15.2.3.7-5-b-105 failing @@ -1167,40 +1093,20 @@ S15.1.3.2_A5.3 failing 15.2.3.7-5-b-80 failing 15.2.3.7-6-a-1 failing 15.2.3.7-6-a-10 failing -15.2.3.7-6-a-102 failing -15.2.3.7-6-a-105 failing -15.2.3.7-6-a-107 failing -15.2.3.7-6-a-108 failing 15.2.3.7-6-a-110 failing 15.2.3.7-6-a-112 failing 15.2.3.7-6-a-113 failing -15.2.3.7-6-a-114 failing 15.2.3.7-6-a-116 failing 15.2.3.7-6-a-117 failing 15.2.3.7-6-a-118 failing 15.2.3.7-6-a-119 failing 15.2.3.7-6-a-12 failing -15.2.3.7-6-a-121 failing -15.2.3.7-6-a-129 failing 15.2.3.7-6-a-13 failing -15.2.3.7-6-a-130 failing -15.2.3.7-6-a-131 failing -15.2.3.7-6-a-132 failing -15.2.3.7-6-a-134 failing -15.2.3.7-6-a-135 failing -15.2.3.7-6-a-136 failing -15.2.3.7-6-a-137 failing 15.2.3.7-6-a-14 failing -15.2.3.7-6-a-141 failing -15.2.3.7-6-a-148 failing -15.2.3.7-6-a-149 failing 15.2.3.7-6-a-15 failing -15.2.3.7-6-a-152 failing -15.2.3.7-6-a-153 failing 15.2.3.7-6-a-158 failing 15.2.3.7-6-a-16 failing 15.2.3.7-6-a-160 failing -15.2.3.7-6-a-161 failing 15.2.3.7-6-a-164 failing 15.2.3.7-6-a-165 failing 15.2.3.7-6-a-166 failing @@ -1215,10 +1121,6 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-175 failing 15.2.3.7-6-a-176 failing 15.2.3.7-6-a-18 failing -15.2.3.7-6-a-180 failing -15.2.3.7-6-a-181 failing -15.2.3.7-6-a-182 failing -15.2.3.7-6-a-183 failing 15.2.3.7-6-a-184 failing 15.2.3.7-6-a-185 failing 15.2.3.7-6-a-186 failing @@ -1254,15 +1156,8 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-244 failing 15.2.3.7-6-a-245 failing 15.2.3.7-6-a-25 failing -15.2.3.7-6-a-250 failing -15.2.3.7-6-a-251 failing -15.2.3.7-6-a-252 failing 15.2.3.7-6-a-255 failing 15.2.3.7-6-a-258 failing -15.2.3.7-6-a-260 failing -15.2.3.7-6-a-261 failing -15.2.3.7-6-a-263 failing -15.2.3.7-6-a-269 failing 15.2.3.7-6-a-270 failing 15.2.3.7-6-a-271 failing 15.2.3.7-6-a-272 failing @@ -1301,10 +1196,6 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-45 failing 15.2.3.7-6-a-46 failing 15.2.3.7-6-a-5 failing -15.2.3.7-6-a-61 failing -15.2.3.7-6-a-62 failing -15.2.3.7-6-a-63 failing -15.2.3.7-6-a-64 failing 15.2.3.7-6-a-65 failing 15.2.3.7-6-a-66-1 failing 15.2.3.7-6-a-66 failing @@ -1330,9 +1221,6 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-93-2 failing 15.2.3.7-6-a-93-3 failing 15.2.3.7-6-a-93-4 failing -15.2.3.7-6-a-97 failing -15.2.3.7-6-a-98 failing -15.2.3.7-6-a-99 failing 15.2.3.9-2-a-12 failing 15.2.4.2-1-1 failing 15.2.4.2-1-2 failing @@ -1517,11 +1405,6 @@ S15.3.5_A3_T2 failing 15.3.5.4_2-91gs failing 15.3.5.4_2-92gs failing 15.3.5.4_2-93gs failing -S15.4_A1.1_T2 failing -S15.4_A1.1_T3 failing -S15.4.2.2_A2.2_T1 failing -S15.4.2.2_A2.2_T2 failing -S15.4.2.2_A2.2_T3 failing S15.4.3_A2.2 failing S15.4.3_A2.3 failing S15.4.3_A2.4 failing @@ -1975,7 +1858,6 @@ S15.4.4.13_A5.7 failing 15.4.4.16-7-b-10 failing 15.4.4.16-7-b-12 failing 15.4.4.16-7-b-13 failing -15.4.4.16-7-b-14 failing 15.4.4.16-7-b-15 failing 15.4.4.16-7-b-2 failing 15.4.4.16-7-b-3 failing @@ -2107,7 +1989,6 @@ S15.4.4.13_A5.7 failing 15.4.4.17-7-b-10 failing 15.4.4.17-7-b-12 failing 15.4.4.17-7-b-13 failing -15.4.4.17-7-b-14 failing 15.4.4.17-7-b-15 failing 15.4.4.17-7-b-2 failing 15.4.4.17-7-b-3 failing @@ -2239,7 +2120,6 @@ S15.4.4.13_A5.7 failing 15.4.4.18-7-b-10 failing 15.4.4.18-7-b-12 failing 15.4.4.18-7-b-13 failing -15.4.4.18-7-b-14 failing 15.4.4.18-7-b-15 failing 15.4.4.18-7-b-2 failing 15.4.4.18-7-b-3 failing @@ -2420,7 +2300,6 @@ S15.4.4.13_A5.7 failing 15.4.4.19-9-7 failing 15.4.4.19-9-8 failing 15.4.4.19-9-9 failing -S15.4.4.2_A1_T1 failing S15.4.4.2_A3_T1 failing S15.4.4.2_A4.7 failing 15.4.4.20-1-1 failing @@ -2510,7 +2389,6 @@ S15.4.4.2_A4.7 failing 15.4.4.20-9-b-10 failing 15.4.4.20-9-b-12 failing 15.4.4.20-9-b-13 failing -15.4.4.20-9-b-14 failing 15.4.4.20-9-b-15 failing 15.4.4.20-9-b-2 failing 15.4.4.20-9-b-3 failing @@ -2686,10 +2564,8 @@ S15.4.4.2_A4.7 failing 15.4.4.21-8-c-6 failing 15.4.4.21-9-1 failing 15.4.4.21-9-10 failing -15.4.4.21-9-4 failing 15.4.4.21-9-6 failing 15.4.4.21-9-8 failing -15.4.4.21-9-9 failing 15.4.4.21-9-b-10 failing 15.4.4.21-9-b-12 failing 15.4.4.21-9-b-13 failing @@ -2704,7 +2580,6 @@ S15.4.4.2_A4.7 failing 15.4.4.21-9-b-23 failing 15.4.4.21-9-b-25 failing 15.4.4.21-9-b-26 failing -15.4.4.21-9-b-27 failing 15.4.4.21-9-b-28 failing 15.4.4.21-9-b-3 failing 15.4.4.21-9-b-4 failing @@ -2857,7 +2732,6 @@ S15.4.4.2_A4.7 failing 15.4.4.22-7-7 failing 15.4.4.22-7-8 failing 15.4.4.22-7-9 failing -15.4.4.22-8-b-2 failing 15.4.4.22-8-b-ii-1 failing 15.4.4.22-8-b-ii-2 failing 15.4.4.22-8-b-iii-1-1 failing @@ -2890,14 +2764,12 @@ S15.4.4.2_A4.7 failing 15.4.4.22-8-c-3 failing 15.4.4.22-8-c-5 failing 15.4.4.22-8-c-6 failing -15.4.4.22-9-4 failing 15.4.4.22-9-6 failing 15.4.4.22-9-8 failing 15.4.4.22-9-9 failing 15.4.4.22-9-b-10 failing 15.4.4.22-9-b-12 failing 15.4.4.22-9-b-13 failing -15.4.4.22-9-b-14 failing 15.4.4.22-9-b-15 failing 15.4.4.22-9-b-17 failing 15.4.4.22-9-b-18 failing @@ -2978,11 +2850,9 @@ S15.4.4.4_A2_T2 failing S15.4.4.4_A3_T1 failing S15.4.4.4_A4.1 failing S15.4.4.4_A4.7 failing -S15.4.4.5_A1.1_T1 failing S15.4.4.5_A5_T1 failing S15.4.4.5_A6.1 failing S15.4.4.5_A6.7 failing -S15.4.4.6_A1.1_T1 failing S15.4.4.6_A1.2_T1 failing S15.4.4.6_A3_T2 failing S15.4.4.6_A3_T3 failing @@ -3110,7 +2980,6 @@ S15.4.4.8_A4_T1 failing S15.4.4.8_A4_T2 failing S15.4.4.8_A5.1 failing S15.4.4.8_A5.7 failing -S15.4.4.9_A1.1_T1 failing S15.4.4.9_A1.2_T1 failing S15.4.4.9_A2_T1 failing S15.4.4.9_A2_T2 failing @@ -3124,21 +2993,8 @@ S15.4.4.9_A4_T1 failing S15.4.4.9_A4_T2 failing S15.4.4.9_A5.1 failing S15.4.4.9_A5.7 failing -15.4.5.1-3.d-1 failing -15.4.5.1-3.d-2 failing -15.4.5.1-3.d-3 failing -15.4.5.1-5-2 failing -S15.4.5.1_A1.1_T1 failing -S15.4.5.1_A1.1_T2 failing -S15.4.5.1_A1.2_T1 failing S15.4.5.1_A1.2_T2 failing S15.4.5.1_A1.2_T3 failing -S15.4.5.1_A1.3_T1 failing -S15.4.5.1_A1.3_T2 failing -S15.4.5.2_A1_T2 failing -S15.4.5.2_A3_T1 failing -S15.4.5.2_A3_T2 failing -S15.4.5.2_A3_T3 failing S15.5.4.13_A2_T2 failing S15.5.4.13_A2_T7 failing S15.5.4.13_A3_T1 failing @@ -3468,8 +3324,6 @@ S15.5.5.1_A2 failing S15.5.5.1_A3 failing S15.5.5.1_A4 failing S15.5.5.1_A5 failing -15.5.5.5.2-3-6 failing -15.5.5.5.2-3-7 failing 15.5.5.5.2-7-4 failing S15.6.3_A3 failing S15.6.3.1_A2 failing @@ -3749,7 +3603,6 @@ S15.4.4.11_A1.4_T2 failing S15.4.4.4_A1_T2 failing # Regression introduced by qtdeclarative parser changes -S7.9_A5.7_T1 failing # Regressions due to Object/property refactoring 15.12.2-2-1 failing @@ -3773,23 +3626,21 @@ S7.9_A5.7_T1 failing 15.12.3-11-24 failing 15.12.3-11-25 failing 15.2.3.4-4-1 failing -15.2.3.6-4-234 failing -15.2.3.6-4-235 failing -15.2.3.6-4-236 failing -15.2.3.6-4-237 failing -15.2.3.7-6-a-223 failing -15.2.3.7-6-a-224 failing -15.2.3.7-6-a-225 failing -15.2.3.7-6-a-226 failing S15.4.4.13_A1_T1 failing S15.4.4.13_A1_T2 failing 15.4.4.18-7-c-i-6 failing -15.4.4.19-8-9 failing -15.4.4.19-8-b-14 failing 15.4.4.19-8-c-i-6 failing 15.4.4.20-9-c-i-6 failing 15.4.4.21-8-b-iii-1-6 failing 15.4.4.22-9-c-i-33 failing 15.4.4.22-8-b-iii-1-6 failing -S15.4.4.5_A4_T2 failing S15.4.4.7_A5_T1 failing + +# decreasing length of array ignores non configurable properties +15.4.4.16-7-b-16 failing +15.4.4.17-7-b-16 failing +15.4.4.18-7-b-16 failing +15.4.4.19-8-b-16 failing +15.4.4.20-9-b-16 failing +15.4.4.21-9-b-29 failing +15.4.4.22-9-b-16 failing \ No newline at end of file -- cgit v1.2.3 From e0161a1e008fb0820a7cd869ee034a636a24cd70 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 12 Jan 2013 16:40:58 +0100 Subject: Fix Array.slice Change-Id: I236b56c020368305f0c714b32b1cd5aeec0efc22 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 46 ++++++++++++++++++++++++++++------------------ tests/TestExpectations | 11 ----------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index eb46d429a1..18c3def84f 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1769,26 +1769,36 @@ Value ArrayPrototype::method_shift(ExecutionContext *ctx) Value ArrayPrototype::method_slice(ExecutionContext *ctx) { - // ### TODO implement the fast non-generic version of slice. + Object *o = ctx->thisObject.toObject(ctx).objectValue(); Array result; - Value start = ctx->argument(0); - Value end = ctx->argument(1); - Value self = ctx->thisObject; - Value l = self.property(ctx, ctx->engine->id_length); - double r2 = !l.isUndefined() ? l.toNumber(ctx) : 0; - quint32 r3 = Value::toUInt32(r2); - qint32 r4 = qint32(start.toInteger(ctx)); - quint32 r5 = r4 < 0 ? qMax(quint32(r3 + r4), quint32(0)) : qMin(quint32(r4), r3); - quint32 k = r5; - qint32 r7 = end.isUndefined() ? r3 : qint32 (end.toInteger(ctx)); - quint32 r8 = r7 < 0 ? qMax(quint32(r3 + r7), quint32(0)) : qMin(quint32(r7), r3); - quint32 n = 0; - for (; k < r8; ++k) { - String *r11 = Value::fromDouble(k).toString(ctx); - Value v = self.property(ctx, r11); - if (! v.isUndefined()) - result.set(n++, v); + uint len = o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); + double s = ctx->argument(0).toInteger(ctx); + uint start; + if (s < 0) + start = (uint)qMax(len + s, 0.); + else if (s > len) + start = len; + else + start = (uint) s; + uint end = len; + if (!ctx->argument(1).isUndefined()) { + double e = ctx->argument(1).toInteger(ctx); + if (e < 0) + end = (uint)qMax(len + e, 0.); + else if (e > len) + end = len; + else + end = (uint) e; + } + + uint n = 0; + for (uint i = start; i < end; ++i) { + bool exists; + Value v = o->__get__(ctx, i, &exists); + if (exists) + result.set(n, v); + ++n; } return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); } diff --git a/tests/TestExpectations b/tests/TestExpectations index d7b6798ec9..09d92b27e2 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -1,10 +1,3 @@ -# Skipped because our array implementation isn't sparse and therefore this test causes OOM. -S15.4.4.10_A1.3_T4 -S15.4.4.10_A2.2_T3 -S15.4.4.10_A2.2_T4 -S15.4.4.10_A3_T1 -S15.4.4.10_A3_T2 - # Skipped because of slow string concatenation, test takes a looong time to execute. S15.1.3.1_A2.5_T1 S15.1.3.2_A2.5_T1 @@ -1434,10 +1427,6 @@ S15.4.3.1_A4 failing 15.4.3.2-2-1 failing 15.4.3.2-2-2 failing 15.4.3.2-2-3 failing -S15.4.4.10_A1.2_T4 failing -S15.4.4.10_A1.4_T3 failing -S15.4.4.10_A2.1_T4 failing -S15.4.4.10_A3_T3 failing S15.4.4.10_A4_T1 failing S15.4.4.10_A5.1 failing S15.4.4.10_A5.7 failing -- cgit v1.2.3 From fecd003d0f412d0e1adbc3425659f57a0deea7ba Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 11 Jan 2013 14:56:45 +0100 Subject: Fix exception type in Function.prototype.apply When Function.prototype.apply is called with the wrong second argument, a type error should be thrown instead of a generic exception. Change-Id: I6fceca49dbbb44cbc5a2f5cce1b4e3ce899976ed Reviewed-by: Lars Knoll --- qv4ecmaobjects.cpp | 2 +- tests/TestExpectations | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 18c3def84f..68cc07a719 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2156,7 +2156,7 @@ Value FunctionPrototype::method_apply(ExecutionContext *ctx) args.append(a); } } else if (!(arg.isUndefined() || arg.isNull())) { - ctx->throwError(QLatin1String("Function.prototype.apply: second argument is not an array")); + ctx->throwTypeError(); return Value::undefinedValue(); } diff --git a/tests/TestExpectations b/tests/TestExpectations index 09d92b27e2..bb57ac3881 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -1260,7 +1260,6 @@ S15.3.4.3_A3_T7 failing S15.3.4.3_A3_T8 failing S15.3.4.3_A3_T9 failing S15.3.4.3_A5_T4 failing -S15.3.4.3_A6_T3 failing S15.3.4.3_A7_T1 failing S15.3.4.3_A7_T10 failing S15.3.4.3_A7_T2 failing @@ -3624,12 +3623,3 @@ S15.4.4.13_A1_T2 failing 15.4.4.22-9-c-i-33 failing 15.4.4.22-8-b-iii-1-6 failing S15.4.4.7_A5_T1 failing - -# decreasing length of array ignores non configurable properties -15.4.4.16-7-b-16 failing -15.4.4.17-7-b-16 failing -15.4.4.18-7-b-16 failing -15.4.4.19-8-b-16 failing -15.4.4.20-9-b-16 failing -15.4.4.21-9-b-29 failing -15.4.4.22-9-b-16 failing \ No newline at end of file -- cgit v1.2.3 From 5a94774ab617acca46073c32651ce95cbdb61af5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 11 Jan 2013 15:01:48 +0100 Subject: Throw a type error when trying to construct a native function Change-Id: I8ebc225f1d2645683763a129d20415d58f089acb Reviewed-by: Lars Knoll --- qmljs_objects.cpp | 6 ++++++ qmljs_objects.h | 1 + tests/TestExpectations | 15 --------------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 78f51e92f2..435aa06170 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -1207,3 +1207,9 @@ NativeFunction::NativeFunction(ExecutionContext *scope, String *name, Value (*co { this->name = name; } + +Value NativeFunction::construct(ExecutionContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} diff --git a/qmljs_objects.h b/qmljs_objects.h index 787a42fab9..c9043a78e8 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -287,6 +287,7 @@ struct NativeFunction: FunctionObject { NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); virtual Value call(ExecutionContext *ctx) { return code(ctx); } + virtual Value construct(ExecutionContext *ctx); }; struct ScriptFunction: FunctionObject { diff --git a/tests/TestExpectations b/tests/TestExpectations index bb57ac3881..809b20cad0 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -1428,7 +1428,6 @@ S15.4.3.1_A4 failing 15.4.3.2-2-3 failing S15.4.4.10_A4_T1 failing S15.4.4.10_A5.1 failing -S15.4.4.10_A5.7 failing S15.4.4.11_A3_T1 failing S15.4.4.11_A3_T2 failing S15.4.4.11_A4_T1 failing @@ -1436,7 +1435,6 @@ S15.4.4.11_A4_T2 failing S15.4.4.11_A4_T3 failing S15.4.4.11_A6_T2 failing S15.4.4.11_A7.1 failing -S15.4.4.11_A7.7 failing 15.4.4.12-9-c-ii-1 failing S15.4.4.12_A1.1_T4 failing S15.4.4.12_A1.1_T6 failing @@ -1455,7 +1453,6 @@ S15.4.4.12_A4_T1 failing S15.4.4.12_A4_T2 failing S15.4.4.12_A4_T3 failing S15.4.4.12_A5.1 failing -S15.4.4.12_A5.7 failing S15.4.4.13_A2_T1 failing S15.4.4.13_A2_T2 failing S15.4.4.13_A2_T3 failing @@ -1465,7 +1462,6 @@ S15.4.4.13_A3_T3 failing S15.4.4.13_A4_T1 failing S15.4.4.13_A4_T2 failing S15.4.4.13_A5.1 failing -S15.4.4.13_A5.7 failing 15.4.4.14-1-1 failing 15.4.4.14-1-10 failing 15.4.4.14-1-11 failing @@ -2289,7 +2285,6 @@ S15.4.4.13_A5.7 failing 15.4.4.19-9-8 failing 15.4.4.19-9-9 failing S15.4.4.2_A3_T1 failing -S15.4.4.2_A4.7 failing 15.4.4.20-1-1 failing 15.4.4.20-1-10 failing 15.4.4.20-1-11 failing @@ -2830,31 +2825,26 @@ S15.4.4.2_A4.7 failing 15.4.4.22-9-c-ii-8 failing S15.4.4.3_A1_T1 failing S15.4.4.3_A3_T1 failing -S15.4.4.3_A4.7 failing 15.4.4.4-5-b-iii-3-b-1 failing 15.4.4.4-5-c-i-1 failing S15.4.4.4_A2_T1 failing S15.4.4.4_A2_T2 failing S15.4.4.4_A3_T1 failing S15.4.4.4_A4.1 failing -S15.4.4.4_A4.7 failing S15.4.4.5_A5_T1 failing S15.4.4.5_A6.1 failing -S15.4.4.5_A6.7 failing S15.4.4.6_A1.2_T1 failing S15.4.4.6_A3_T2 failing S15.4.4.6_A3_T3 failing S15.4.4.6_A4_T1 failing S15.4.4.6_A4_T2 failing S15.4.4.6_A5.1 failing -S15.4.4.6_A5.7 failing S15.4.4.7_A3 failing S15.4.4.7_A4_T2 failing S15.4.4.7_A4_T3 failing S15.4.4.7_A6.1 failing S15.5.3.1_A3 failing S15.5.3.1_A4 failing -S15.5.3.2_A4 failing S15.5.3_A1 failing S15.5.4.10_A1_T1 failing S15.5.4.10_A1_T10 failing @@ -2947,7 +2937,6 @@ S15.5.4.12_A2_T6 failing S15.5.4.12_A2_T7 failing S15.5.4.12_A3_T1 failing S15.5.4.12_A3_T2 failing -S15.5.4.12_A7 failing S15.5.4.13_A1_T1 failing S15.5.4.13_A1_T10 failing S15.5.4.13_A1_T11 failing @@ -2955,7 +2944,6 @@ S15.5.4.13_A1_T12 failing S15.5.4.13_A1_T15 failing S15.5.4.13_A1_T2 failing S15.5.4.13_A1_T5 failing -S15.4.4.7_A6.7 failing S15.4.4.8_A1_T1 failing S15.4.4.8_A1_T2 failing S15.4.4.8_A2_T1 failing @@ -2967,7 +2955,6 @@ S15.4.4.8_A3_T3 failing S15.4.4.8_A4_T1 failing S15.4.4.8_A4_T2 failing S15.4.4.8_A5.1 failing -S15.4.4.8_A5.7 failing S15.4.4.9_A1.2_T1 failing S15.4.4.9_A2_T1 failing S15.4.4.9_A2_T2 failing @@ -2980,7 +2967,6 @@ S15.4.4.9_A3_T3 failing S15.4.4.9_A4_T1 failing S15.4.4.9_A4_T2 failing S15.4.4.9_A5.1 failing -S15.4.4.9_A5.7 failing S15.4.5.1_A1.2_T2 failing S15.4.5.1_A1.2_T3 failing S15.5.4.13_A2_T2 failing @@ -3467,7 +3453,6 @@ S15.9.5.1_A3_T2 failing 13.2.1_1 failing 13.2.1_4 failing 13.2.1_5 failing -13.2.1_L15 failing 13.3.0_2 failing 13.3.0_6 failing 13.3.0_7 failing -- cgit v1.2.3 From e7ce364b161e32482be52f736e0d2804439950b0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 11 Jan 2013 16:51:34 +0100 Subject: Fix passing of null and undefined to built-in functions Usually passing null or undefined to function calls as the this object converts to the global object. This is not the case for built-in functions, where null and undefined are acceptable as this object, because it allows the implementation to decide what to do, i.e. return a specific value or throw for example a type error. This patch adds the hook & implementation to allow passing null/ undefined to built-in functions, even when called via Fuction.prototype.apply and call. Change-Id: I00b4b7393f89586dbd66ffa0b7972292e58411cb Reviewed-by: Lars Knoll --- qmljs_objects.cpp | 21 +++++++++++++++++++++ qmljs_objects.h | 4 ++++ qv4ecmaobjects.cpp | 18 +++++++++++++----- tests/TestExpectations | 37 ------------------------------------- 4 files changed, 38 insertions(+), 42 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 435aa06170..d0e2bb7691 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -751,6 +751,18 @@ Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *a return result; } +Value FunctionObject::callDirect(ExecutionContext *context, Value thisObject, Value *args, int argc) +{ + ExecutionContext k; + ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; + + ctx->initCallContext(context, thisObject, this, args, argc); + maybeAdjustThisObjectForDirectCall(ctx, thisObject); + Value result = call(ctx); + ctx->leaveCallContext(); + return result; +} + Value FunctionObject::call(ExecutionContext *ctx) { Q_UNUSED(ctx); @@ -1213,3 +1225,12 @@ Value NativeFunction::construct(ExecutionContext *ctx) ctx->throwTypeError(); return Value::undefinedValue(); } + +void NativeFunction::maybeAdjustThisObjectForDirectCall(ExecutionContext *context, Value thisArg) +{ + // Built-in functions allow for the this object to be null or undefined. This overrides + // the behaviour of changing thisObject to the global object if null/undefined and allows + // the built-in functions for example to throw a type error if null is passed. + if (thisArg.isNull() || thisArg.isUndefined()) + context->thisObject = thisArg; +} diff --git a/qmljs_objects.h b/qmljs_objects.h index c9043a78e8..384677c190 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -274,6 +274,9 @@ struct FunctionObject: Object { Value construct(ExecutionContext *context, Value *args, int argc); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); + // Nothing to do in the default implementation, only _native_ functions might change context->thisObject. + virtual void maybeAdjustThisObjectForDirectCall(ExecutionContext* /*context*/, Value /*thisArg*/) { } + Value callDirect(ExecutionContext *context, Value thisObject, Value *args, int argc); virtual struct ScriptFunction *asScriptFunction() { return 0; } @@ -288,6 +291,7 @@ struct NativeFunction: FunctionObject { NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); virtual Value call(ExecutionContext *ctx) { return code(ctx); } virtual Value construct(ExecutionContext *ctx); + virtual void maybeAdjustThisObjectForDirectCall(ExecutionContext *context, Value thisArg); }; struct ScriptFunction: FunctionObject { diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 68cc07a719..d92cf88c6d 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2143,9 +2143,7 @@ Value FunctionPrototype::method_toString(ExecutionContext *ctx) Value FunctionPrototype::method_apply(ExecutionContext *ctx) { - Value thisObject = ctx->argument(0).toObject(ctx); - if (thisObject.isNull() || thisObject.isUndefined()) - thisObject = ctx->engine->globalObject; + Value thisArg = ctx->argument(0); Value arg = ctx->argument(1); QVector args; @@ -2160,17 +2158,27 @@ Value FunctionPrototype::method_apply(ExecutionContext *ctx) return Value::undefinedValue(); } - return __qmljs_call_value(ctx, thisObject, ctx->thisObject, args.data(), args.size()); + FunctionObject *o = ctx->thisObject.asFunctionObject(); + if (!o) + ctx->throwTypeError(); + + return o->callDirect(ctx, thisArg, args.data(), args.size()); } Value FunctionPrototype::method_call(ExecutionContext *ctx) { Value thisArg = ctx->argument(0); + QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); if (ctx->argumentCount) qCopy(ctx->arguments + 1, ctx->arguments + ctx->argumentCount, args.begin()); - return __qmljs_call_value(ctx, thisArg, ctx->thisObject, args.data(), args.size()); + + FunctionObject *o = ctx->thisObject.asFunctionObject(); + if (!o) + ctx->throwTypeError(); + + return o->callDirect(ctx, thisArg, args.data(), args.size()); } Value FunctionPrototype::method_bind(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index 809b20cad0..0330726906 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -25,12 +25,6 @@ S10.2.3_A1.3_T2 failing 10.4.3-1-106 failing 10.4.3-1-17-s failing 10.4.3-1-63-s failing -10.4.3-1-66-s failing -10.4.3-1-66gs failing -10.4.3-1-67-s failing -10.4.3-1-67gs failing -10.4.3-1-68-s failing -10.4.3-1-68gs failing 10.4.3-1-76-s failing 10.4.3-1-76gs failing 10.4.3-1-77-s failing @@ -42,12 +36,6 @@ S10.2.3_A1.3_T2 failing 10.4.3-1-80-s failing 10.4.3-1-80gs failing 10.4.3-1-82-s failing -10.4.3-1-85-s failing -10.4.3-1-85gs failing -10.4.3-1-86-s failing -10.4.3-1-86gs failing -10.4.3-1-87-s failing -10.4.3-1-87gs failing 10.4.3-1-95-s failing 10.4.3-1-95gs failing 10.4.3-1-96-s failing @@ -1249,17 +1237,6 @@ S15.3.3.1_A3 failing 15.3.4.3-3-s failing S15.3.4.3_A2_T1 failing S15.3.4.3_A2_T2 failing -S15.3.4.3_A3_T1 failing -S15.3.4.3_A3_T10 failing -S15.3.4.3_A3_T2 failing -S15.3.4.3_A3_T3 failing -S15.3.4.3_A3_T4 failing -S15.3.4.3_A3_T5 failing -S15.3.4.3_A3_T6 failing -S15.3.4.3_A3_T7 failing -S15.3.4.3_A3_T8 failing -S15.3.4.3_A3_T9 failing -S15.3.4.3_A5_T4 failing S15.3.4.3_A7_T1 failing S15.3.4.3_A7_T10 failing S15.3.4.3_A7_T2 failing @@ -1270,7 +1247,6 @@ S15.3.4.3_A7_T6 failing S15.3.4.3_A7_T7 failing S15.3.4.3_A7_T8 failing S15.3.4.3_A7_T9 failing -S15.3.4.3_A8_T6 failing 15.3.4.4-1-s failing 15.3.4.4-2-s failing 15.3.4.4-3-s failing @@ -1389,9 +1365,6 @@ S15.3.5.3_A1_T7 failing S15.3.5.3_A1_T8 failing S15.3.5_A2_T2 failing S15.3.5_A3_T2 failing -15.3.5.4_2-79gs failing -15.3.5.4_2-80gs failing -15.3.5.4_2-81gs failing 15.3.5.4_2-89gs failing 15.3.5.4_2-90gs failing 15.3.5.4_2-91gs failing @@ -3503,18 +3476,8 @@ S12.9_A1_T6 failing S12.9_A1_T7 failing S12.9_A1_T8 failing S12.9_A1_T9 failing -S15.2.4.3_A12 failing -S15.2.4.3_A13 failing -S15.2.4.4_A12 failing -S15.2.4.4_A13 failing S15.2.4.4_A14 failing S15.2.4.4_A15 failing -S15.2.4.5_A12 failing -S15.2.4.5_A13 failing -S15.2.4.6_A12 failing -S15.2.4.6_A13 failing -S15.2.4.7_A12 failing -S15.2.4.7_A13 failing 15.3.5.4_2-11gs failing 15.3.5.4_2-13gs failing 15.3.5.4_2-15gs failing -- cgit v1.2.3 From 0c355bd4b95e5705592075959f2ee14c1fa5b8d9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 11 Jan 2013 16:54:35 +0100 Subject: Fix built-in String prototype functions when used with non-string objects The this object should be converted to a string if it's not a string already. That allows for the re-use of String.prototype functions with different objects. This is with the exception of null and undefined that should continue to throw type errors. Change-Id: Iaaad538b85f3a7da3e5fcf436236602f0f327dc7 Reviewed-by: Lars Knoll --- qv4ecmaobjects.cpp | 13 ++++--- tests/TestExpectations | 93 -------------------------------------------------- 2 files changed, 8 insertions(+), 98 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index d92cf88c6d..74493ee3a0 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1059,12 +1059,15 @@ void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) QString StringPrototype::getThisString(ExecutionContext *ctx) { - if (StringObject *thisObject = ctx->thisObject.asStringObject()) { - return thisObject->value.stringValue()->toQString(); - } else { + String* str = 0; + Value thisObject = ctx->thisObject; + if (StringObject *thisString = thisObject.asStringObject()) + str = thisString->value.stringValue(); + else if (thisObject.isUndefined() || thisObject.isNull()) ctx->throwTypeError(); - return QString(); - } + else + str = ctx->thisObject.toString(ctx); + return str->toQString(); } Value StringPrototype::method_toString(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index 0330726906..cf3f20e397 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -2910,13 +2910,7 @@ S15.5.4.12_A2_T6 failing S15.5.4.12_A2_T7 failing S15.5.4.12_A3_T1 failing S15.5.4.12_A3_T2 failing -S15.5.4.13_A1_T1 failing S15.5.4.13_A1_T10 failing -S15.5.4.13_A1_T11 failing -S15.5.4.13_A1_T12 failing -S15.5.4.13_A1_T15 failing -S15.5.4.13_A1_T2 failing -S15.5.4.13_A1_T5 failing S15.4.4.8_A1_T1 failing S15.4.4.8_A1_T2 failing S15.4.4.8_A2_T1 failing @@ -2944,10 +2938,7 @@ S15.4.5.1_A1.2_T2 failing S15.4.5.1_A1.2_T3 failing S15.5.4.13_A2_T2 failing S15.5.4.13_A2_T7 failing -S15.5.4.13_A3_T1 failing -S15.5.4.13_A3_T2 failing S15.5.4.13_A3_T3 failing -S15.5.4.13_A3_T4 failing S15.5.4.14_A1_T1 failing S15.5.4.14_A1_T10 failing S15.5.4.14_A1_T11 failing @@ -3045,68 +3036,12 @@ S15.5.4.14_A4_T6 failing S15.5.4.14_A4_T7 failing S15.5.4.14_A4_T8 failing S15.5.4.14_A4_T9 failing -S15.5.4.15_A1_T1 failing S15.5.4.15_A1_T10 failing -S15.5.4.15_A1_T11 failing -S15.5.4.15_A1_T12 failing -S15.5.4.15_A1_T15 failing S15.5.4.15_A1_T2 failing -S15.5.4.15_A1_T5 failing S15.5.4.15_A1_T7 failing S15.5.4.15_A1_T8 failing S15.5.4.15_A1_T9 failing S15.5.4.15_A2_T8 failing -S15.5.4.15_A3_T1 failing -S15.5.4.15_A3_T10 failing -S15.5.4.15_A3_T11 failing -S15.5.4.15_A3_T2 failing -S15.5.4.15_A3_T3 failing -S15.5.4.15_A3_T4 failing -S15.5.4.15_A3_T5 failing -S15.5.4.15_A3_T6 failing -S15.5.4.15_A3_T7 failing -S15.5.4.15_A3_T8 failing -S15.5.4.15_A3_T9 failing -S15.5.4.16_A1_T1 failing -S15.5.4.16_A1_T10 failing -S15.5.4.16_A1_T11 failing -S15.5.4.16_A1_T12 failing -S15.5.4.16_A1_T13 failing -S15.5.4.16_A1_T14 failing -S15.5.4.16_A1_T2 failing -S15.5.4.16_A1_T6 failing -S15.5.4.16_A1_T7 failing -S15.5.4.16_A1_T8 failing -S15.5.4.17_A1_T1 failing -S15.5.4.17_A1_T10 failing -S15.5.4.17_A1_T11 failing -S15.5.4.17_A1_T12 failing -S15.5.4.17_A1_T13 failing -S15.5.4.17_A1_T14 failing -S15.5.4.17_A1_T2 failing -S15.5.4.17_A1_T6 failing -S15.5.4.17_A1_T7 failing -S15.5.4.17_A1_T8 failing -S15.5.4.18_A1_T1 failing -S15.5.4.18_A1_T10 failing -S15.5.4.18_A1_T11 failing -S15.5.4.18_A1_T12 failing -S15.5.4.18_A1_T13 failing -S15.5.4.18_A1_T14 failing -S15.5.4.18_A1_T2 failing -S15.5.4.18_A1_T6 failing -S15.5.4.18_A1_T7 failing -S15.5.4.18_A1_T8 failing -S15.5.4.19_A1_T1 failing -S15.5.4.19_A1_T10 failing -S15.5.4.19_A1_T11 failing -S15.5.4.19_A1_T12 failing -S15.5.4.19_A1_T13 failing -S15.5.4.19_A1_T14 failing -S15.5.4.19_A1_T2 failing -S15.5.4.19_A1_T6 failing -S15.5.4.19_A1_T7 failing -S15.5.4.19_A1_T8 failing 15.5.4.20-0-1 failing 15.5.4.20-0-2 failing 15.5.4.20-1-3 failing @@ -3181,23 +3116,10 @@ S15.5.4.19_A1_T8 failing 15.5.4.20-3-8 failing 15.5.4.20-3-9 failing 15.5.4.20-4-1 failing -S15.5.4.4_A5 failing -S15.5.4.5_A1.1 failing -S15.5.4.5_A1_T1 failing S15.5.4.5_A1_T10 failing -S15.5.4.5_A1_T2 failing -S15.5.4.5_A2 failing -S15.5.4.5_A4 failing -S15.5.4.6_A1_T1 failing S15.5.4.6_A1_T10 failing -S15.5.4.6_A1_T2 failing -S15.5.4.6_A2 failing -S15.5.4.6_A4_T1 failing -S15.5.4.6_A4_T2 failing -S15.5.4.7_A1_T1 failing S15.5.4.7_A1_T10 failing S15.5.4.7_A1_T11 failing -S15.5.4.7_A1_T2 failing 15.5.4.20-4-10 failing 15.5.4.20-4-11 failing 15.5.4.20-4-12 failing @@ -3248,24 +3170,10 @@ S15.5.4.7_A1_T2 failing 15.5.4.20-4-6 failing 15.5.4.20-4-60 failing 15.5.4.20-4-8 failing -S15.5.4.4_A1.1 failing -S15.5.4.4_A1_T1 failing S15.5.4.4_A1_T10 failing -S15.5.4.4_A1_T2 failing -S15.5.4.4_A2 failing -S15.5.4.7_A4_T1 failing -S15.5.4.7_A4_T2 failing -S15.5.4.7_A4_T4 failing -S15.5.4.7_A4_T5 failing -S15.5.4.8_A1_T1 failing S15.5.4.8_A1_T10 failing S15.5.4.8_A1_T12 failing -S15.5.4.8_A1_T2 failing S15.5.4.8_A1_T4 failing -S15.5.4.8_A4_T1 failing -S15.5.4.8_A4_T2 failing -S15.5.4.8_A4_T4 failing -S15.5.4.8_A4_T5 failing S15.5.5.1_A1 failing S15.5.5.1_A2 failing S15.5.5.1_A3 failing @@ -3420,7 +3328,6 @@ S15.9.5.1_A3_T2 failing 12.3_a failing 12.3_b failing 12.4_a failing -13.1.1_2 failing 13.1.1_6 failing 13.1.1_7 failing 13.2.1_1 failing -- cgit v1.2.3 From 66fdad6a8c44dc65b6528f49bb52757088d7ac69 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 12 Jan 2013 23:24:50 +0100 Subject: fix some bugs in function construction Throw a syntax error instead of a type error in the Function constructor Fix evaluation order of arguments passed to the Function constructor Fix the code generator so that the parsed function doesn't look like it's being evaluated in the global scope. Fix the code generator to reject duplicated arguments in strict mode. Fix length property of the Function constructor and Function.prototype.apply Change-Id: I7f647b9023579c0e8179a62df380b8981a54c99c Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 27 ++++++++++++++------- qv4ecmaobjects.cpp | 12 ++++------ tests/TestExpectations | 64 +++++++------------------------------------------- 3 files changed, 31 insertions(+), 72 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 5c95cd0158..40397f351a 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -196,15 +196,10 @@ public: void operator()(Node *node) { - _env = 0; if (node) node->accept(this); } -protected: - using Visitor::visit; - using Visitor::endVisit; - inline void enterEnvironment(Node *node) { Environment *e = _cg->newEnvironment(node, _env); @@ -220,6 +215,10 @@ protected: _env = _envStack.isEmpty() ? 0 : _envStack.top(); } +protected: + using Visitor::visit; + using Visitor::endVisit; + void checkDirectivePrologue(SourceElements *ast) { for (SourceElements *it = ast; it; it = it->next) { @@ -382,10 +381,17 @@ private: if (body) checkDirectivePrologue(body->elements); - if (wasStrict || _env->isStrict) - for (FormalParameterList *it = formals; it; it = it->next) - if (it->name == QLatin1String("eval") || it->name == QLatin1String("arguments")) - _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "'%1' cannot be used as parameter name in strict mode").arg(it->name.toString())); + if (wasStrict || _env->isStrict) { + QStringList args; + for (FormalParameterList *it = formals; it; it = it->next) { + QString arg = it->name.toString(); + if (args.contains(arg)) + _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "Duplicate parameter name '%1' in strict mode").arg(arg)); + if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) + _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "'%1' cannot be used as parameter name in strict mode").arg(arg)); + args += arg; + } + } } @@ -447,7 +453,10 @@ IR::Function *Codegen::operator()(const QString &fileName, AST::FunctionExpressi _env = 0; ScanFunctions scan(this); + // fake a global environment + scan.enterEnvironment(0); scan(ast); + scan.leaveEnvironment(); IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); if (_debugger) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 74493ee3a0..48af470792 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2070,6 +2070,7 @@ Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) FunctionCtor::FunctionCtor(ExecutionContext *scope) : FunctionObject(scope) { + defineDefaultProperty(scope->engine->id_length, Value::fromInt32(1)); } // 15.3.2 @@ -2080,13 +2081,12 @@ Value FunctionCtor::construct(ExecutionContext *ctx) QString args; QString body; if (ctx->argumentCount > 0) { - body = ctx->argument(ctx->argumentCount - 1).toString(ctx)->toQString(); - for (uint i = 0; i < ctx->argumentCount - 1; ++i) { if (i) args += QLatin1String(", "); args += ctx->argument(i).toString(ctx)->toQString(); } + body = ctx->argument(ctx->argumentCount - 1).toString(ctx)->toQString(); } QString function = QLatin1String("function(") + args + QLatin1String("){") + body + QLatin1String("}"); @@ -2099,14 +2099,12 @@ Value FunctionCtor::construct(ExecutionContext *ctx) const bool parsed = parser.parseExpression(); if (!parsed) - // ### Syntax error - __qmljs_throw_type_error(ctx); + ctx->throwSyntaxError(0); using namespace AST; FunctionExpression *fe = AST::cast(parser.rootNode()); if (!fe) - // ### Syntax error - __qmljs_throw_type_error(ctx); + ctx->throwSyntaxError(0); IR::Module module; @@ -2130,7 +2128,7 @@ void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 0); + defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2); defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 1); defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 1); } diff --git a/tests/TestExpectations b/tests/TestExpectations index cf3f20e397..ceb10c76bf 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -371,23 +371,10 @@ S13_A17_T1 failing S13_A17_T2 failing S13_A3_T1 failing S13_A6_T2 failing -13.1-10-s failing 13.1-11-s failing 13.1-12-s failing 13.1-13-s failing 13.1-14-s failing -13.1-23-s failing -13.1-24-s failing -13.1-25-s failing -13.1-26-s failing -13.1-27-s failing -13.1-28-s failing -13.1-29-s failing -13.1-30-s failing -13.1-31-s failing -13.1-32-s failing -13.1-33-s failing -13.1-34-s failing 13.1-35-s failing 13.1-36-s failing 13.1-37-s failing @@ -396,11 +383,6 @@ S13_A6_T2 failing 13.1-40-s failing 13.1-41-s failing 13.1-42-s failing -13.1-5-s failing -13.1-6-s failing -13.1-7-s failing -13.1-8-s failing -13.1-9-s failing 13.2-10-s failing 13.2-12-s failing 13.2-13-s failing @@ -440,7 +422,6 @@ S13.2.2_A19_T4 failing S13.2.2_A19_T5 failing S13.2.2_A19_T6 failing S13.2.2_A19_T8 failing -S13.2.2_A8_T3 failing S13.2.3_A1 failing S13.2_A3 failing S13.2_A4_T1 failing @@ -1209,38 +1190,13 @@ S15.1.3.2_A5.3 failing 15.2.4.2-2-2 failing S15.2.4.2_A12 failing S15.2.4.2_A13 failing -S15.3_A2_T1 failing -S15.3_A2_T2 failing -S15.3.2.1_A1_T13 failing -S15.3.2.1_A1_T8 failing -S15.3.2.1_A2_T1 failing -S15.3.2.1_A2_T2 failing -S15.3.2.1_A2_T3 failing -S15.3.2.1_A2_T4 failing -S15.3.2.1_A2_T5 failing -S15.3.2.1_A2_T6 failing -S15.3.2.1_A3_T1 failing -S15.3.2.1_A3_T10 failing -S15.3.2.1_A3_T2 failing -S15.3.2.1_A3_T3 failing -S15.3.2.1_A3_T6 failing -S15.3.2.1_A3_T7 failing -S15.3.2.1_A3_T9 failing -15.3.2.1-11-1-s failing -15.3.2.1-11-5-s failing -S15.3.3_A3 failing S15.3.3.1_A1 failing S15.3.3.1_A3 failing 15.3.3.2-1 failing 15.3.4.3-1-s failing 15.3.4.3-2-s failing 15.3.4.3-3-s failing -S15.3.4.3_A2_T1 failing -S15.3.4.3_A2_T2 failing -S15.3.4.3_A7_T1 failing S15.3.4.3_A7_T10 failing -S15.3.4.3_A7_T2 failing -S15.3.4.3_A7_T3 failing S15.3.4.3_A7_T4 failing S15.3.4.3_A7_T5 failing S15.3.4.3_A7_T6 failing @@ -1250,16 +1206,6 @@ S15.3.4.3_A7_T9 failing 15.3.4.4-1-s failing 15.3.4.4-2-s failing 15.3.4.4-3-s failing -S15.3.4.4_A6_T1 failing -S15.3.4.4_A6_T10 failing -S15.3.4.4_A6_T2 failing -S15.3.4.4_A6_T3 failing -S15.3.4.4_A6_T4 failing -S15.3.4.4_A6_T5 failing -S15.3.4.4_A6_T6 failing -S15.3.4.4_A6_T7 failing -S15.3.4.4_A6_T8 failing -S15.3.4.4_A6_T9 failing 15.3.4.5-10-1 failing 15.3.4.5-11-1 failing 15.3.4.5-13.b-1 failing @@ -1363,8 +1309,6 @@ S15.3.5.3_A1_T5 failing S15.3.5.3_A1_T6 failing S15.3.5.3_A1_T7 failing S15.3.5.3_A1_T8 failing -S15.3.5_A2_T2 failing -S15.3.5_A3_T2 failing 15.3.5.4_2-89gs failing 15.3.5.4_2-90gs failing 15.3.5.4_2-91gs failing @@ -3478,3 +3422,11 @@ S15.4.4.13_A1_T2 failing 15.4.4.22-9-c-i-33 failing 15.4.4.22-8-b-iii-1-6 failing S15.4.4.7_A5_T1 failing + +15.4.4.16-7-b-16 failing +15.4.4.17-7-b-16 failing +15.4.4.18-7-b-16 failing +15.4.4.19-8-b-16 failing +15.4.4.20-9-b-16 failing +15.4.4.21-9-b-29 failing +15.4.4.22-9-b-16 failing -- cgit v1.2.3 From c22fbac218f137a30920cad89431296d7d6dda66 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 13 Jan 2013 16:17:03 +0100 Subject: The prototype of Constructor objects is readonly Change-Id: I43449a964e003c444a42ad51639a22f9b0cb73c5 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 8 +++----- qmljs_objects.h | 2 +- qv4ecmaobjects.cpp | 18 +++++++++--------- tests/TestExpectations | 36 +----------------------------------- 4 files changed, 14 insertions(+), 50 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index d0e2bb7691..fd4b07baa5 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -149,19 +149,17 @@ void Object::defineDefaultProperty(ExecutionContext *context, const QString &nam Q_UNUSED(argumentCount); String *s = context->engine->identifier(name); FunctionObject* function = context->engine->newNativeFunction(context, s, code); - function->defineReadonlyProperty(context->engine, context->engine->id_length, Value::fromInt32(argumentCount)); + function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); defineDefaultProperty(s, Value::fromObject(function)); } void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value) { - defineReadonlyProperty(engine, engine->identifier(name), value); + defineReadonlyProperty(engine->identifier(name), value); } -void Object::defineReadonlyProperty(ExecutionEngine *engine, String *name, Value value) +void Object::defineReadonlyProperty(String *name, Value value) { - Q_UNUSED(engine); - if (!members) members.reset(new PropertyTable()); PropertyDescriptor *pd = members->insert(name); diff --git a/qmljs_objects.h b/qmljs_objects.h index 384677c190..65812e990b 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -162,7 +162,7 @@ struct Object: Managed { void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int count = 0); /* Fixed: Writable: false, Enumerable: false, Configurable: false */ void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value); - void defineReadonlyProperty(ExecutionEngine *engine, String *name, Value value); + void defineReadonlyProperty(String *name, Value value); protected: virtual void getCollectables(QVector &objects); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 48af470792..6076b6df5c 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -554,7 +554,7 @@ Value ObjectCtor::__get__(ExecutionContext *ctx, String *name, bool *hasProperty void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); @@ -1032,7 +1032,7 @@ Value StringCtor::call(ExecutionContext *ctx) void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode, 1); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); @@ -1344,7 +1344,7 @@ Value NumberCtor::call(ExecutionContext *ctx) void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); @@ -1522,7 +1522,7 @@ Value BooleanCtor::call(ExecutionContext *ctx) void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); @@ -1579,7 +1579,7 @@ Value ArrayCtor::call(ExecutionContext *ctx) void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); defineDefaultProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString, 0); @@ -2125,7 +2125,7 @@ Value FunctionCtor::call(ExecutionContext *ctx) void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2); @@ -2246,7 +2246,7 @@ Value DateCtor::call(ExecutionContext *ctx) void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); LocalTZA = getLocalTZA(); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("parse"), method_parse, 1); @@ -2851,7 +2851,7 @@ Value RegExpCtor::call(ExecutionContext *ctx) void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 1); defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 1); @@ -2964,7 +2964,7 @@ Value URIErrorCtor::construct(ExecutionContext *ctx) void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) { - ctor.objectValue()->defineDefaultProperty(ctx->engine->id_prototype, Value::fromObject(obj)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(obj)); obj->defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); obj->defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); } diff --git a/tests/TestExpectations b/tests/TestExpectations index ceb10c76bf..15161fa34b 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -565,8 +565,6 @@ S15.10.4.1_A9_T2 failing S15.10.4.1_A9_T3 failing 15.10.4.1-2 failing 15.10.4.1-3 failing -S15.10.5.1_A3 failing -S15.10.5.1_A4 failing S15.10.5_A1 failing S15.10.6.2_A1_T2 failing S15.10.6.2_A1_T6 failing @@ -605,8 +603,6 @@ S15.10.7.4_A9 failing 15.10.7.5-2 failing S15.10.7.5_A8 failing S15.10.7.5_A9 failing -S15.11.3.1_A1_T1 failing -S15.11.3.1_A3_T1 failing S15.11.3_A2_T1 failing S15.11.4.2_A1 failing S15.11.4.2_A2 failing @@ -700,8 +696,6 @@ S15.1.3.1_A2.1_T1 failing S15.2.3_A1 failing S15.2.3_A2 failing S15.2.3_A3 failing -15.2.3.1 failing -S15.2.3.1_A1 failing S15.2.3.1_A2 failing S15.2.3.1_A3 failing 15.2.3.10-3-12 failing @@ -885,31 +879,16 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-164 failing 15.2.3.3-4-176 failing 15.2.3.3-4-177 failing -15.2.3.3-4-182 failing -15.2.3.3-4-185 failing 15.2.3.3-4-186 failing 15.2.3.3-4-187 failing -15.2.3.3-4-189 failing -15.2.3.3-4-190 failing 15.2.3.3-4-191 failing 15.2.3.3-4-192 failing -15.2.3.3-4-193 failing 15.2.3.3-4-194 failing -15.2.3.3-4-195 failing 15.2.3.3-4-201 failing -15.2.3.3-4-210 failing -15.2.3.3-4-211 failing 15.2.3.3-4-212 failing 15.2.3.3-4-213 failing 15.2.3.3-4-214 failing 15.2.3.3-4-215 failing -15.2.3.3-4-216 failing -15.2.3.3-4-217 failing -15.2.3.3-4-218 failing -15.2.3.3-4-219 failing -15.2.3.3-4-220 failing -15.2.3.3-4-221 failing -15.2.3.3-4-222 failing 15.2.3.3-4-51 failing 15.2.3.3-4-82 failing 15.2.3.3-4-9 failing @@ -1190,8 +1169,6 @@ S15.1.3.2_A5.3 failing 15.2.4.2-2-2 failing S15.2.4.2_A12 failing S15.2.4.2_A13 failing -S15.3.3.1_A1 failing -S15.3.3.1_A3 failing 15.3.3.2-1 failing 15.3.4.3-1-s failing 15.3.4.3-2-s failing @@ -1317,8 +1294,6 @@ S15.3.5.3_A1_T8 failing S15.4.3_A2.2 failing S15.4.3_A2.3 failing S15.4.3_A2.4 failing -S15.4.3.1_A3 failing -S15.4.3.1_A4 failing 15.4.3.2-0-1 failing 15.4.3.2-0-2 failing 15.4.3.2-0-3 failing @@ -2760,8 +2735,6 @@ S15.4.4.7_A3 failing S15.4.4.7_A4_T2 failing S15.4.4.7_A4_T3 failing S15.4.4.7_A6.1 failing -S15.5.3.1_A3 failing -S15.5.3.1_A4 failing S15.5.3_A1 failing S15.5.4.10_A1_T1 failing S15.5.4.10_A1_T10 failing @@ -3125,12 +3098,7 @@ S15.5.5.1_A4 failing S15.5.5.1_A5 failing 15.5.5.5.2-7-4 failing S15.6.3_A3 failing -S15.6.3.1_A2 failing -S15.6.3.1_A3 failing S15.7.3_A8 failing -15.7.3.1-1 failing -S15.7.3.1_A1_T1 failing -S15.7.3.1_A1_T2 failing S15.7.4_A3.3 failing S15.7.4.5_A1.3_T01 failing S15.7.4.5_A1.3_T02 failing @@ -3138,8 +3106,6 @@ S15.7.4.5_A1.4_T01 failing S15.8.2.16_A4 failing S15.8.2.16_A5 failing S15.8.2.17_A2 failing -S15.9.4.1_A1_T1 failing -S15.9.4.1_A1_T2 failing 15.9.4.4-0-1 failing 15.9.4.4-0-2 failing 15.9.4.4-0-3 failing @@ -3429,4 +3395,4 @@ S15.4.4.7_A5_T1 failing 15.4.4.19-8-b-16 failing 15.4.4.20-9-b-16 failing 15.4.4.21-9-b-29 failing -15.4.4.22-9-b-16 failing +15.4.4.22-9-b-16 failing \ No newline at end of file -- cgit v1.2.3 From 0260ce13992c945ca3982f4e517a191242c29035 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 13 Jan 2013 16:28:07 +0100 Subject: Function.length is readonly as well. Change-Id: I5eb555e98e047fb4a851934611d4db86aa5fa02a Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 2 +- tests/TestExpectations | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 6076b6df5c..e2c712cfb0 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2070,7 +2070,6 @@ Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) FunctionCtor::FunctionCtor(ExecutionContext *scope) : FunctionObject(scope) { - defineDefaultProperty(scope->engine->id_length, Value::fromInt32(1)); } // 15.3.2 @@ -2126,6 +2125,7 @@ Value FunctionCtor::call(ExecutionContext *ctx) void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2); diff --git a/tests/TestExpectations b/tests/TestExpectations index 15161fa34b..0bbbde56e3 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -78,7 +78,6 @@ S11.11.1_A3_T4 failing 11.13.1-4-29-s failing 11.13.1-4-30-s failing 11.13.1-4-31-s failing -11.13.1-4-6-s failing 11.13.2-45-s failing 11.13.2-46-s failing 11.13.2-47-s failing @@ -879,7 +878,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-164 failing 15.2.3.3-4-176 failing 15.2.3.3-4-177 failing -15.2.3.3-4-186 failing 15.2.3.3-4-187 failing 15.2.3.3-4-191 failing 15.2.3.3-4-192 failing @@ -1169,7 +1167,6 @@ S15.1.3.2_A5.3 failing 15.2.4.2-2-2 failing S15.2.4.2_A12 failing S15.2.4.2_A13 failing -15.3.3.2-1 failing 15.3.4.3-1-s failing 15.3.4.3-2-s failing 15.3.4.3-3-s failing -- cgit v1.2.3 From cfc35ca0095d5e6badfc6dcbff9a291d8839ba33 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 13 Jan 2013 17:17:28 +0100 Subject: Fix Function.prototype.apply The args object doesn't have to be an array. A simple object is enough. Change-Id: If22cacdcfd49472d6bd838461c2b8281bb4b7f99 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index e2c712cfb0..e79d571b9f 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2149,8 +2149,9 @@ Value FunctionPrototype::method_apply(ExecutionContext *ctx) Value arg = ctx->argument(1); QVector args; - if (ArrayObject *arr = arg.asArrayObject()) { - for (quint32 i = 0; i < arr->array.length(); ++i) { + if (Object *arr = arg.asObject()) { + quint32 len = arr->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); + for (quint32 i = 0; i < len; ++i) { Value a = arr->__get__(ctx, i); args.append(a); } -- cgit v1.2.3 From c47dc1b645ca81db33f8ba37c2a0fcd290eaeb13 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 13 Jan 2013 17:18:14 +0100 Subject: instanceof doesn't throw an exception if the rhs is a primitive value. Change-Id: I4f8fd4c8ca42878cfcb104bcf4b615f56e909240 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index fd4b07baa5..075d9dc826 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -695,10 +695,8 @@ Function::~Function() bool FunctionObject::hasInstance(ExecutionContext *ctx, const Value &value) { - if (! value.isObject()) { - ctx->throwTypeError(); + if (! value.isObject()) return false; - } Value o = __get__(ctx, ctx->engine->id_prototype); if (! o.isObject()) { -- cgit v1.2.3 From 08fe11966575ac09f192ee08424ef3f793de34cb Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 13 Jan 2013 17:25:04 +0100 Subject: Update TestExpectations Change-Id: If4cbe03a3b7777215a3aeb5dbe69edad6c2965a0 Reviewed-by: Simon Hausmann --- tests/TestExpectations | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index 0bbbde56e3..9fc929f71e 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -258,9 +258,6 @@ S11.8.4_A2.4_T3 failing S11.8.6_A2.4_T1 failing S11.8.6_A2.4_T3 failing S11.8.6_A3 failing -S11.8.6_A4_T1 failing -S11.8.6_A4_T2 failing -S11.8.6_A4_T3 failing S11.8.6_A5_T2 failing S11.8.7_A2.4_T1 failing S11.8.7_A2.4_T3 failing @@ -1167,19 +1164,6 @@ S15.1.3.2_A5.3 failing 15.2.4.2-2-2 failing S15.2.4.2_A12 failing S15.2.4.2_A13 failing -15.3.4.3-1-s failing -15.3.4.3-2-s failing -15.3.4.3-3-s failing -S15.3.4.3_A7_T10 failing -S15.3.4.3_A7_T4 failing -S15.3.4.3_A7_T5 failing -S15.3.4.3_A7_T6 failing -S15.3.4.3_A7_T7 failing -S15.3.4.3_A7_T8 failing -S15.3.4.3_A7_T9 failing -15.3.4.4-1-s failing -15.3.4.4-2-s failing -15.3.4.4-3-s failing 15.3.4.5-10-1 failing 15.3.4.5-11-1 failing 15.3.4.5-13.b-1 failing @@ -1275,14 +1259,6 @@ S15.3.5.1_A4_T2 failing S15.3.5.1_A4_T3 failing S15.3.5.2_A1_T1 failing S15.3.5.2_A1_T2 failing -S15.3.5.3_A1_T1 failing -S15.3.5.3_A1_T2 failing -S15.3.5.3_A1_T3 failing -S15.3.5.3_A1_T4 failing -S15.3.5.3_A1_T5 failing -S15.3.5.3_A1_T6 failing -S15.3.5.3_A1_T7 failing -S15.3.5.3_A1_T8 failing 15.3.5.4_2-89gs failing 15.3.5.4_2-90gs failing 15.3.5.4_2-91gs failing -- cgit v1.2.3 From fdbaa9804dbb42ee74dddc0c535cfeafadf5c45e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 13 Jan 2013 22:13:48 +0100 Subject: Fixes to the ScriptFunction constructor We now pass all of 15.3, with the exception of tests using the unimplemented Function.prototype.bind method. Change-Id: I95f6157ea99d63556c32ae7524ee5e14026bbd69 Reviewed-by: Erik Verbruggen --- qmljs_engine.cpp | 8 ++---- qmljs_engine.h | 2 ++ qmljs_objects.cpp | 25 ++++++++++++++++-- tests/TestExpectations | 69 -------------------------------------------------- 4 files changed, 27 insertions(+), 77 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index fc7c8671ea..a1b094607f 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -173,6 +173,8 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) typeErrorPrototype->init(rootContext, typeErrorCtor); uRIErrorPrototype->init(rootContext, uRIErrorCtor); + thrower = newNativeFunction(rootContext, 0, __qmljs_throw_type_error); + // // set up the global object // @@ -246,13 +248,7 @@ FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, VM:: { assert(function); - MemoryManager::GCBlocker gcBlocker(memoryManager); - ScriptFunction *f = new (memoryManager) ScriptFunction(scope, function); - Object *proto = scope->engine->newObject(); - proto->__put__(scope, scope->engine->id_constructor, Value::fromObject(f)); - f->__put__(scope, scope->engine->id_prototype, Value::fromObject(proto)); - f->prototype = scope->engine->functionPrototype; return f; } diff --git a/qmljs_engine.h b/qmljs_engine.h index a09117d9cb..6b528fb882 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -130,6 +130,8 @@ struct ExecutionEngine TypeErrorPrototype *typeErrorPrototype; URIErrorPrototype *uRIErrorPrototype; + FunctionObject *thrower; // function that throws a type error + String *id_length; String *id_prototype; String *id_constructor; diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 075d9dc826..fd5db87d36 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -781,6 +781,8 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) if (!scope) return; + MemoryManager::GCBlocker gcBlocker(scope->engine->memoryManager); + if (!function->name.isEmpty()) name = scope->engine->identifier(function->name); needsActivation = function->needsActivation(); @@ -793,6 +795,7 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) formalParameterList[i] = scope->engine->identifier(function->formals.at(i)); } } + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(formalParameterCount)); varCount = function->locals.size(); if (varCount) { @@ -801,6 +804,25 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) varList[i] = scope->engine->identifier(function->locals.at(i)); } } + + Object *proto = scope->engine->newObject(); + proto->defineDefaultProperty(scope->engine->id_constructor, Value::fromObject(this)); + PropertyDescriptor *pd = members->insert(scope->engine->id_prototype); + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Disabled; + pd->value = Value::fromObject(proto); + + prototype = scope->engine->functionPrototype; + + if (scope->strictMode) { + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(scope->engine->thrower, scope->engine->thrower); + pd.configurable = PropertyDescriptor::Disabled; + pd.enumberable = PropertyDescriptor::Disabled; + __defineOwnProperty__(scope, QStringLiteral("caller"), &pd); + __defineOwnProperty__(scope, QStringLiteral("arguments"), &pd); + } } ScriptFunction::~ScriptFunction() @@ -1143,8 +1165,7 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterC if (context->strictMode) { for (uint i = 0; i < context->argumentCount; ++i) Object::__put__(context, QString::number(i), context->arguments[i]); - FunctionObject *thrower = context->engine->newNativeFunction(context, 0, __qmljs_throw_type_error); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(context->engine->thrower, context->engine->thrower); pd.configurable = PropertyDescriptor::Disabled; pd.enumberable = PropertyDescriptor::Disabled; __defineOwnProperty__(context, QStringLiteral("callee"), &pd); diff --git a/tests/TestExpectations b/tests/TestExpectations index 9fc929f71e..820e83b698 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -167,8 +167,6 @@ S11.2.3_A3_T2 failing S11.2.3_A3_T3 failing S11.2.3_A3_T4 failing S11.2.3_A3_T5 failing -S11.2.4_A1.1_T2 failing -S11.2.4_A1.2_T2 failing 11.3.1-2-1-s failing 11.3.1-2-2-s failing S11.3.1_A2.2_T1 failing @@ -379,37 +377,12 @@ S13_A6_T2 failing 13.1-40-s failing 13.1-41-s failing 13.1-42-s failing -13.2-10-s failing 13.2-12-s failing -13.2-13-s failing -13.2-14-s failing -13.2-15-1 failing 13.2-16-s failing -13.2-17-1 failing -13.2-17-s failing -13.2-18-1 failing -13.2-18-s failing -13.2-2-s failing 13.2-20-s failing -13.2-21-s failing -13.2-22-s failing 13.2-24-s failing -13.2-25-s failing -13.2-26-s failing 13.2-28-s failing -13.2-29-s failing -13.2-30-s failing -13.2-31-s failing -13.2-32-s failing -13.2-33-s failing -13.2-34-s failing -13.2-35-s failing -13.2-36-s failing -13.2-4-s failing -13.2-5-s failing -13.2-6-s failing 13.2-8-s failing -13.2-9-s failing S13.2.2_A16_T2 failing S13.2.2_A16_T3 failing S13.2.2_A19_T1 failing @@ -419,9 +392,6 @@ S13.2.2_A19_T5 failing S13.2.2_A19_T6 failing S13.2.2_A19_T8 failing S13.2.3_A1 failing -S13.2_A3 failing -S13.2_A4_T1 failing -S13.2_A4_T2 failing S14_A2 failing S14_A5_T1 failing S14_A5_T2 failing @@ -791,7 +761,6 @@ S15.10.1_A1_T11 failing 15.2.3.12-3-27 failing 15.2.3.13-2-12 failing 15.2.3.14-2-1 failing -15.2.3.14-3-2 failing 15.2.3.14-3-4 failing 15.2.3.2-2-12 failing 15.2.3.2-2-13 failing @@ -875,7 +844,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-164 failing 15.2.3.3-4-176 failing 15.2.3.3-4-177 failing -15.2.3.3-4-187 failing 15.2.3.3-4-191 failing 15.2.3.3-4-192 failing 15.2.3.3-4-194 failing @@ -1245,20 +1213,6 @@ S15.3.4.5_A5 failing 15.3.4.5.2-4-7 failing 15.3.4.5.2-4-8 failing 15.3.4.5.2-4-9 failing -S15.3.5.1_A1_T1 failing -S15.3.5.1_A1_T2 failing -S15.3.5.1_A1_T3 failing -S15.3.5.1_A2_T1 failing -S15.3.5.1_A2_T2 failing -S15.3.5.1_A2_T3 failing -S15.3.5.1_A3_T1 failing -S15.3.5.1_A3_T2 failing -S15.3.5.1_A3_T3 failing -S15.3.5.1_A4_T1 failing -S15.3.5.1_A4_T2 failing -S15.3.5.1_A4_T3 failing -S15.3.5.2_A1_T1 failing -S15.3.5.2_A1_T2 failing 15.3.5.4_2-89gs failing 15.3.5.4_2-90gs failing 15.3.5.4_2-91gs failing @@ -3268,29 +3222,6 @@ S12.9_A1_T8 failing S12.9_A1_T9 failing S15.2.4.4_A14 failing S15.2.4.4_A15 failing -15.3.5.4_2-11gs failing -15.3.5.4_2-13gs failing -15.3.5.4_2-15gs failing -15.3.5.4_2-17gs failing -15.3.5.4_2-19gs failing -15.3.5.4_2-1gs failing -15.3.5.4_2-21gs failing -15.3.5.4_2-22gs failing -15.3.5.4_2-23gs failing -15.3.5.4_2-24gs failing -15.3.5.4_2-25gs failing -15.3.5.4_2-26gs failing -15.3.5.4_2-27gs failing -15.3.5.4_2-28gs failing -15.3.5.4_2-29gs failing -15.3.5.4_2-3gs failing -15.3.5.4_2-48gs failing -15.3.5.4_2-50gs failing -15.3.5.4_2-52gs failing -15.3.5.4_2-54gs failing -15.3.5.4_2-5gs failing -15.3.5.4_2-7gs failing -15.3.5.4_2-9gs failing # Array regressions S13.2.1_A5_T1 failing -- cgit v1.2.3 From 9da2b90a3f2b7ac3d86db174ca4c9134b4b6424b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 13 Jan 2013 23:57:35 +0100 Subject: Implement Function.prototype.bind Change-Id: Ia63f5064d76fecfc513737a0072bf5e3cdce474c Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 14 ++++++++++-- qmljs_engine.h | 5 +++-- qmljs_objects.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- qmljs_objects.h | 16 +++++++++++++- qv4ecmaobjects.cpp | 14 ++++++++---- 5 files changed, 102 insertions(+), 11 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index a1b094607f..a54c9c3fc7 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include "qv4mm.h" namespace QQmlJS { @@ -86,6 +87,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) id_prototype = identifier(QStringLiteral("prototype")); id_constructor = identifier(QStringLiteral("constructor")); id_arguments = identifier(QStringLiteral("arguments")); + id_caller = identifier(QStringLiteral("caller")); id_this = identifier(QStringLiteral("this")); id___proto__ = identifier(QStringLiteral("__proto__")); id_enumerable = identifier(QStringLiteral("enumerable")); @@ -142,6 +144,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) typeErrorCtor = Value::fromObject(new (memoryManager) TypeErrorCtor(rootContext)); uRIErrorCtor = Value::fromObject(new (memoryManager) URIErrorCtor(rootContext)); + objectCtor.objectValue()->prototype = functionPrototype; stringCtor.objectValue()->prototype = functionPrototype; numberCtor.objectValue()->prototype = functionPrototype; booleanCtor.objectValue()->prototype = functionPrototype; @@ -173,8 +176,6 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) typeErrorPrototype->init(rootContext, typeErrorCtor); uRIErrorPrototype->init(rootContext, uRIErrorCtor); - thrower = newNativeFunction(rootContext, 0, __qmljs_throw_type_error); - // // set up the global object // @@ -252,6 +253,15 @@ FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, VM:: return f; } +BoundFunction *ExecutionEngine::newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) +{ + assert(target); + + BoundFunction *f = new (memoryManager) BoundFunction(scope, target, boundThis, boundArgs); + return f; +} + + Object *ExecutionEngine::newObject() { Object *object = new (memoryManager) Object(); diff --git a/qmljs_engine.h b/qmljs_engine.h index 6b528fb882..3e098bead8 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -64,6 +64,7 @@ struct StringObject; struct ArrayObject; struct DateObject; struct FunctionObject; +struct BoundFunction; struct RegExpObject; struct ErrorObject; struct ArgumentsObject; @@ -130,12 +131,11 @@ struct ExecutionEngine TypeErrorPrototype *typeErrorPrototype; URIErrorPrototype *uRIErrorPrototype; - FunctionObject *thrower; // function that throws a type error - String *id_length; String *id_prototype; String *id_constructor; String *id_arguments; + String *id_caller; String *id_this; String *id___proto__; String *id_enumerable; @@ -170,6 +170,7 @@ struct ExecutionEngine FunctionObject *newNativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); FunctionObject *newScriptFunction(ExecutionContext *scope, VM::Function *function); + BoundFunction *newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); Object *newObject(); FunctionObject *newObjectCtor(ExecutionContext *ctx); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index fd5db87d36..c35436cc4e 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -58,6 +58,7 @@ #include #include #include +#include using namespace QQmlJS::VM; @@ -817,7 +818,8 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) prototype = scope->engine->functionPrototype; if (scope->strictMode) { - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(scope->engine->thrower, scope->engine->thrower); + FunctionObject *thrower = scope->engine->newNativeFunction(scope, 0, __qmljs_throw_type_error); + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); pd.configurable = PropertyDescriptor::Disabled; pd.enumberable = PropertyDescriptor::Disabled; __defineOwnProperty__(scope, QStringLiteral("caller"), &pd); @@ -1165,7 +1167,8 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterC if (context->strictMode) { for (uint i = 0; i < context->argumentCount; ++i) Object::__put__(context, QString::number(i), context->arguments[i]); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(context->engine->thrower, context->engine->thrower); + FunctionObject *thrower = context->engine->newNativeFunction(context, 0, __qmljs_throw_type_error); + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); pd.configurable = PropertyDescriptor::Disabled; pd.enumberable = PropertyDescriptor::Disabled; __defineOwnProperty__(context, QStringLiteral("callee"), &pd); @@ -1251,3 +1254,60 @@ void NativeFunction::maybeAdjustThisObjectForDirectCall(ExecutionContext *contex if (thisArg.isNull() || thisArg.isUndefined()) context->thisObject = thisArg; } + + +BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) + : FunctionObject(scope) + , target(target) + , boundThis(boundThis) + , boundArgs(boundArgs) +{ + prototype = scope->engine->functionPrototype; + + int len = target->__get__(scope, scope->engine->id_length).toUInt32(scope); + len -= boundArgs.size(); + if (len < 0) + len = 0; + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(len)); + + FunctionObject *thrower = scope->engine->newNativeFunction(scope, 0, __qmljs_throw_type_error); + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); + pd.configurable = PropertyDescriptor::Disabled; + pd.enumberable = PropertyDescriptor::Disabled; + *members->insert(scope->engine->id_arguments) = pd; + *members->insert(scope->engine->id_caller) = pd; +} + +Value BoundFunction::call(ExecutionContext *context, Value, Value *args, int argc) +{ + Value *newArgs = static_cast(alloca(sizeof(Value)*(boundArgs.size() + argc))); + memcpy(newArgs, boundArgs.constData(), boundArgs.size()*sizeof(Value)); + memcpy(newArgs + boundArgs.size(), args, argc*sizeof(Value)); + + return target->call(context, boundThis, newArgs, boundArgs.size() + argc); +} + +Value BoundFunction::construct(ExecutionContext *context, Value *args, int argc) +{ + Value *newArgs = static_cast(alloca(sizeof(Value)*(boundArgs.size() + argc))); + memcpy(newArgs, boundArgs.constData(), boundArgs.size()*sizeof(Value)); + memcpy(newArgs + boundArgs.size(), args, argc*sizeof(Value)); + + return target->construct(context, newArgs, boundArgs.size() + argc); +} + +bool BoundFunction::hasInstance(ExecutionContext *ctx, const Value &value) +{ + return target->hasInstance(ctx, value); +} + +void BoundFunction::getCollectables(QVector &objects) +{ + FunctionObject::getCollectables(objects); + objects.append(target); + if (Object *o = boundThis.asObject()) + objects.append(o); + for (int i = 0; i < boundArgs.size(); ++i) + if (Object *o = boundArgs.at(i).asObject()) + objects.append(o); +} diff --git a/qmljs_objects.h b/qmljs_objects.h index 65812e990b..d373573cca 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -272,7 +272,7 @@ struct FunctionObject: Object { virtual FunctionObject *asFunctionObject() { return this; } virtual bool hasInstance(ExecutionContext *ctx, const Value &value); - Value construct(ExecutionContext *context, Value *args, int argc); + virtual Value construct(ExecutionContext *context, Value *args, int argc); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); // Nothing to do in the default implementation, only _native_ functions might change context->thisObject. virtual void maybeAdjustThisObjectForDirectCall(ExecutionContext* /*context*/, Value /*thisArg*/) { } @@ -317,6 +317,20 @@ struct EvalFunction : FunctionObject virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); }; +struct BoundFunction: FunctionObject { + FunctionObject *target; + Value boundThis; + QVector boundArgs; + + BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); + virtual ~BoundFunction() {} + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); + virtual Value construct(ExecutionContext *context, Value *args, int argc); + virtual bool hasInstance(ExecutionContext *ctx, const Value &value); + virtual void getCollectables(QVector &objects); +}; + struct ParseIntFunction: FunctionObject { ParseIntFunction(ExecutionContext *scope); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index e79d571b9f..dc41d06dbb 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2185,12 +2185,18 @@ Value FunctionPrototype::method_call(ExecutionContext *ctx) Value FunctionPrototype::method_bind(ExecutionContext *ctx) { - FunctionObject *fun = ctx->thisObject.asFunctionObject(); - if (!fun) + FunctionObject *target = ctx->thisObject.asFunctionObject(); + if (!target) ctx->throwTypeError(); - ctx->throwUnimplemented(QStringLiteral("Function.prototype.bind")); - return Value::undefinedValue(); + Value boundThis = ctx->argument(0); + QVector boundArgs; + for (uint i = 1; i < ctx->argumentCount; ++i) + boundArgs += ctx->argument(i); + + + BoundFunction *f = ctx->engine->newBoundFunction(ctx, target, boundThis, boundArgs); + return Value::fromObject(f); } // -- cgit v1.2.3 From ae25139d0954ec43a8c3fad37304f01c1a744fe8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 09:29:32 +0100 Subject: Remove qdebug Change-Id: Ibc1607d036fa8bf551ff7938090d8dda08a232fe Reviewed-by: Simon Hausmann --- qv4propertydescriptor.h | 1 - 1 file changed, 1 deletion(-) diff --git a/qv4propertydescriptor.h b/qv4propertydescriptor.h index 84582ef956..8aa371651e 100644 --- a/qv4propertydescriptor.h +++ b/qv4propertydescriptor.h @@ -138,7 +138,6 @@ struct PropertyDescriptor { if (type == Accessor) { if ((quintptr)get != 0x1 && get != other->get) return false; - qDebug() << "isSubset" << set << other->set; if ((quintptr)set != 0x1 && set != other->set) return false; } -- cgit v1.2.3 From e96e3a5bb02f93dd9cc1c4fc4c570f89843c350c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 09:29:41 +0100 Subject: Evaluate the expression of the return statement before unwinding exceptions The expression contained in the return statement could throw an exception, so we can only unwind our expection stack after having evaluated that expression. Change-Id: I7b56d03bda46a38915120bdb237e374db707ef92 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 40397f351a..af6b5b08be 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -2196,11 +2196,11 @@ bool Codegen::visit(LocalForStatement *ast) bool Codegen::visit(ReturnStatement *ast) { - unwindException(0); if (ast->expression) { Result expr = expression(ast->expression); move(_block->TEMP(_returnAddress), *expr); } + unwindException(0); _block->JUMP(_exitBlock); return false; -- cgit v1.2.3 From 457ee02357c668bfeaf8ae220ed0dc47cfbb74f7 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 09:43:45 +0100 Subject: update TestExpectations Change-Id: I3d551bf9e618b2208077c3559b7c02552f381807 Reviewed-by: Simon Hausmann --- tests/TestExpectations | 166 ------------------------------------------------- 1 file changed, 166 deletions(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index 820e83b698..d63dfef146 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -25,27 +25,7 @@ S10.2.3_A1.3_T2 failing 10.4.3-1-106 failing 10.4.3-1-17-s failing 10.4.3-1-63-s failing -10.4.3-1-76-s failing -10.4.3-1-76gs failing -10.4.3-1-77-s failing -10.4.3-1-77gs failing -10.4.3-1-78-s failing -10.4.3-1-78gs failing -10.4.3-1-79-s failing -10.4.3-1-79gs failing -10.4.3-1-80-s failing -10.4.3-1-80gs failing 10.4.3-1-82-s failing -10.4.3-1-95-s failing -10.4.3-1-95gs failing -10.4.3-1-96-s failing -10.4.3-1-96gs failing -10.4.3-1-97-s failing -10.4.3-1-97gs failing -10.4.3-1-98-s failing -10.4.3-1-98gs failing -10.4.3-1-99-s failing -10.4.3-1-99gs failing 10.5-1-s failing 10.5-7-b-1-s failing 11.1.4_4-5-1 failing @@ -157,7 +137,6 @@ S11.2.1_A3_T1 failing S11.2.1_A3_T2 failing S11.2.1_A3_T3 failing S11.2.1_A4_T1 failing -S11.2.1_A4_T2 failing S11.2.1_A4_T3 failing S11.2.1_A4_T4 failing S11.2.1_A4_T5 failing @@ -267,8 +246,6 @@ S11.9.4_A2.4_T1 failing S11.9.4_A2.4_T3 failing S11.9.5_A2.4_T1 failing S11.9.5_A2.4_T3 failing -S12.14_A13_T1 failing -S12.14_A13_T3 failing S12.14_A19_T1 failing S12.14_A19_T2 failing S12.14_A7_T1 failing @@ -296,9 +273,6 @@ S12.11_A1_T3 failing S12.11_A1_T4 failing S12.13_A3_T3 failing 12.14-13 failing -12.14-14 failing -12.14-15 failing -12.14-16 failing 12.10-0-3 failing S12.10_A1.11_T1 failing S12.10_A1.11_T2 failing @@ -411,7 +385,6 @@ S12.7_A7 failing S12.8_A4_T1 failing S12.8_A4_T2 failing S12.8_A4_T3 failing -S15.1.2.2_A7.2_T3 failing S15.1.2.2_A7.3_T3 failing S15.1.2.2_A9.1 failing S15.1.2.2_A9.2 failing @@ -659,12 +632,7 @@ S15.1.3.1_A1.9_T1 failing S15.1.3.1_A1.9_T2 failing S15.1.3.1_A1.9_T3 failing S15.1.3.1_A2.1_T1 failing -S15.2.3_A1 failing -S15.2.3_A2 failing S15.2.3_A3 failing -S15.2.3.1_A2 failing -S15.2.3.1_A3 failing -15.2.3.10-3-12 failing 15.2.3.11-4-27 failing 15.12.3-11-2 failing 15.12.3-11-26 failing @@ -769,7 +737,6 @@ S15.10.1_A1_T11 failing 15.2.3.2-2-16 failing 15.2.3.2-2-17 failing 15.2.3.2-2-18 failing -15.2.3.2-2-3 failing 15.2.3.2-2-31 failing S15.1.3.1_A2.2_T1 failing S15.1.3.1_A2.3_T1 failing @@ -904,9 +871,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-177 failing 15.2.3.6-4-188 failing 15.2.3.6-4-189 failing -15.2.3.6-4-191 failing -15.2.3.6-4-192 failing -15.2.3.6-4-193 failing 15.2.3.6-4-20 failing 15.2.3.6-4-256 failing 15.2.3.6-4-266 failing @@ -921,23 +885,10 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-297-1 failing 15.2.3.6-4-299-1 failing 15.2.3.6-4-300-1 failing -15.2.3.6-4-354-13 failing -15.2.3.6-4-354-4 failing -15.2.3.6-4-354-8 failing 15.2.3.6-4-360-3 failing 15.2.3.6-4-360-7 failing -15.2.3.6-4-38 failing -15.2.3.6-4-402 failing -15.2.3.6-4-405 failing -15.2.3.6-4-407 failing 15.2.3.6-4-41 failing 15.2.3.6-4-410 failing -15.2.3.6-4-417 failing -15.2.3.6-4-418 failing -15.2.3.6-4-419 failing -15.2.3.6-4-420 failing -15.2.3.6-4-421 failing -15.2.3.6-4-45 failing 15.2.3.6-4-463 failing 15.2.3.6-4-472 failing 15.2.3.6-4-481 failing @@ -946,40 +897,14 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-507 failing 15.2.3.6-4-516 failing 15.2.3.6-4-525 failing -15.2.3.6-4-531-13 failing -15.2.3.6-4-531-17 failing -15.2.3.6-4-531-4 failing -15.2.3.6-4-531-8 failing -15.2.3.6-4-538-3 failing -15.2.3.6-4-538-7 failing 15.2.3.6-4-543 failing 15.2.3.6-4-544 failing 15.2.3.6-4-561 failing 15.2.3.6-4-562 failing -15.2.3.6-4-578 failing -15.2.3.6-4-579 failing -15.2.3.6-4-581 failing -15.2.3.6-4-583 failing -15.2.3.6-4-584 failing 15.2.3.6-4-586 failing 15.2.3.6-4-593 failing 15.2.3.6-4-594 failing -15.2.3.6-4-595 failing 15.2.3.6-4-596 failing -15.2.3.6-4-597 failing -15.2.3.6-4-598 failing -15.2.3.6-4-599 failing -15.2.3.6-4-600 failing -15.2.3.6-4-601 failing -15.2.3.6-4-602 failing -15.2.3.6-4-603 failing -15.2.3.6-4-604 failing -15.2.3.6-4-605 failing -15.2.3.6-4-606 failing -15.2.3.6-4-607 failing -15.2.3.6-4-608 failing -15.2.3.6-4-609 failing -15.2.3.6-4-610 failing 15.2.3.6-4-621 failing 15.2.3.6-4-622 failing 15.2.3.6-4-623 failing @@ -1014,12 +939,9 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-164 failing 15.2.3.7-6-a-165 failing 15.2.3.7-6-a-166 failing -15.2.3.7-6-a-167 failing 15.2.3.7-6-a-168 failing 15.2.3.7-6-a-169 failing -15.2.3.7-6-a-17 failing 15.2.3.7-6-a-170 failing -15.2.3.7-6-a-171 failing 15.2.3.7-6-a-172 failing 15.2.3.7-6-a-173 failing 15.2.3.7-6-a-175 failing @@ -1028,12 +950,10 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-184 failing 15.2.3.7-6-a-185 failing 15.2.3.7-6-a-186 failing -15.2.3.7-6-a-187 failing 15.2.3.7-6-a-188 failing 15.2.3.7-6-a-189 failing 15.2.3.7-6-a-19 failing 15.2.3.7-6-a-190 failing -15.2.3.7-6-a-191 failing 15.2.3.7-6-a-192 failing 15.2.3.7-6-a-193 failing 15.2.3.7-6-a-194 failing @@ -1053,7 +973,6 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-237 failing 15.2.3.7-6-a-238 failing 15.2.3.7-6-a-239 failing -15.2.3.7-6-a-24 failing 15.2.3.7-6-a-240 failing 15.2.3.7-6-a-241 failing 15.2.3.7-6-a-242 failing @@ -1132,92 +1051,7 @@ S15.1.3.2_A5.3 failing 15.2.4.2-2-2 failing S15.2.4.2_A12 failing S15.2.4.2_A13 failing -15.3.4.5-10-1 failing -15.3.4.5-11-1 failing -15.3.4.5-13.b-1 failing -15.3.4.5-13.b-2 failing -15.3.4.5-13.b-3 failing -15.3.4.5-13.b-4 failing -15.3.4.5-13.b-5 failing -15.3.4.5-13.b-6 failing -15.3.4.5-15-1 failing -15.3.4.5-15-2 failing -15.3.4.5-15-3 failing -15.3.4.5-15-4 failing -15.3.4.5-15-5 failing -15.3.4.5-16-1 failing -15.3.4.5-16-2 failing -15.3.4.5-2-16 failing -15.3.4.5-2-3 failing -15.3.4.5-2-4 failing -15.3.4.5-2-5 failing -15.3.4.5-2-6 failing 15.3.4.5-2-7 failing -15.3.4.5-2-8 failing -15.3.4.5-2-9 failing -15.3.4.5-20-1 failing -15.3.4.5-20-2 failing -15.3.4.5-20-3 failing -15.3.4.5-20-4 failing -15.3.4.5-20-5 failing -15.3.4.5-21-1 failing -15.3.4.5-21-2 failing -15.3.4.5-21-3 failing -15.3.4.5-21-4 failing -15.3.4.5-21-5 failing -15.3.4.5-3-1 failing -15.3.4.5-6-1 failing -15.3.4.5-6-10 failing -15.3.4.5-6-11 failing -15.3.4.5-6-12 failing -15.3.4.5-6-2 failing -15.3.4.5-6-3 failing -15.3.4.5-6-4 failing -15.3.4.5-6-5 failing -15.3.4.5-6-6 failing -15.3.4.5-6-7 failing -15.3.4.5-6-8 failing -15.3.4.5-6-9 failing -15.3.4.5-8-1 failing -15.3.4.5-8-2 failing -15.3.4.5-9-1 failing -15.3.4.5-9-2 failing -S15.3.4.5_A4 failing -S15.3.4.5_A5 failing -15.3.4.5.1-4-1 failing -15.3.4.5.1-4-10 failing -15.3.4.5.1-4-11 failing -15.3.4.5.1-4-12 failing -15.3.4.5.1-4-13 failing -15.3.4.5.1-4-14 failing -15.3.4.5.1-4-15 failing -15.3.4.5.1-4-2 failing -15.3.4.5.1-4-3 failing -15.3.4.5.1-4-4 failing -15.3.4.5.1-4-5 failing -15.3.4.5.1-4-6 failing -15.3.4.5.1-4-7 failing -15.3.4.5.1-4-8 failing -15.3.4.5.1-4-9 failing -15.3.4.5.2-4-1 failing -15.3.4.5.2-4-10 failing -15.3.4.5.2-4-11 failing -15.3.4.5.2-4-12 failing -15.3.4.5.2-4-13 failing -15.3.4.5.2-4-14 failing -15.3.4.5.2-4-2 failing -15.3.4.5.2-4-3 failing -15.3.4.5.2-4-4 failing -15.3.4.5.2-4-5 failing -15.3.4.5.2-4-6 failing -15.3.4.5.2-4-7 failing -15.3.4.5.2-4-8 failing -15.3.4.5.2-4-9 failing -15.3.5.4_2-89gs failing -15.3.5.4_2-90gs failing -15.3.5.4_2-91gs failing -15.3.5.4_2-92gs failing -15.3.5.4_2-93gs failing S15.4.3_A2.2 failing S15.4.3_A2.3 failing S15.4.3_A2.4 failing -- cgit v1.2.3 From 8ba96728df79bc7ef1c0641342160c0a3d9e0dd2 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 12:21:30 +0100 Subject: Fix Array.prototype.indexOf Make it work with index properties that are defined on the prototype as well. Change-Id: I0a76694207429ce38a921988284082e4e8cd54fd Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 2 +- qv4array.cpp | 28 ++++++++++++++++++++++------ qv4array.h | 2 +- qv4codegen.cpp | 3 ++- qv4ecmaobjects.cpp | 3 ++- tests/TestExpectations | 46 ---------------------------------------------- 6 files changed, 28 insertions(+), 56 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index c35436cc4e..7abd5cae29 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -229,7 +229,7 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *, uint i { Object *o = this; while (o) { - PropertyDescriptor *p = array.at(index); + PropertyDescriptor *p = o->array.at(index); if(p && p->type != PropertyDescriptor::Generic) return p; o = o->prototype; diff --git a/qv4array.cpp b/qv4array.cpp index 774908c56f..23164916cb 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -502,19 +502,35 @@ void Array::splice(double start, double deleteCount, // (*to_vector)[st + i] = items.at(i); } -Value Array::indexOf(Value v, uint fromIndex, ExecutionContext *ctx, Object *o) +Value Array::indexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o) { - if (sparse) { - for (SparseArrayNode *n = sparse->findNode(fromIndex); n; n = n->nextNode()) { + bool protoHasArray = false; + Object *p = o; + while ((p = p->prototype)) + if (p->array.length()) + protoHasArray = true; + + if (protoHasArray) { + // lets be safe and slow + for (uint i = fromIndex; i < endIndex; ++i) { + bool exists; + Value value = o->__get__(ctx, i, &exists); + if (exists && __qmljs_strict_equal(value, v)) + return Value::fromDouble(i); + } + } else if (sparse) { + for (SparseArrayNode *n = sparse->findNode(fromIndex); n && n->key() < endIndex; n = n->nextNode()) { bool exists; Value value = o->getValueChecked(ctx, descriptor(n->value), &exists); if (exists && __qmljs_strict_equal(value, v)) return Value::fromDouble(n->key()); } } else { - PropertyDescriptor *pd = values.data(); - PropertyDescriptor *end = pd + values.size(); - pd += offset + fromIndex; + if (endIndex > len) + endIndex = len; + PropertyDescriptor *pd = values.data() + offset; + PropertyDescriptor *end = pd + endIndex; + pd += fromIndex; while (pd < end) { bool exists; Value value = o->getValueChecked(ctx, pd, &exists); diff --git a/qv4array.h b/qv4array.h index de60ef6cad..1957d2c802 100644 --- a/qv4array.h +++ b/qv4array.h @@ -637,7 +637,7 @@ public: void concat(const Array &other); void sort(ExecutionContext *context, Object *thisObject, const Value &comparefn); void splice(double start, double deleteCount, const QVector &, Array &); - Value indexOf(Value v, uint fromIndex, ExecutionContext *ctx, Object *o); + Value indexOf(Value v, uint fromIndex, uint len, ExecutionContext *ctx, Object *o); }; } diff --git a/qv4codegen.cpp b/qv4codegen.cpp index af6b5b08be..817ac9616f 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1009,7 +1009,8 @@ bool Codegen::visit(ArrayLiteral *ast) if (ast->elision) { for (Elision *elision = ast->elision->next; elision; elision = elision->next) ++index; - move(subscript(_block->TEMP(t), _block->CONST(IR::NumberType, index)), _block->CONST(IR::UndefinedType, 0)); + // ### the new string leaks + move(member(_block->TEMP(t), new QString("length")), _block->CONST(IR::NumberType, index + 1)); } _expr.code = _block->TEMP(t); return false; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index dc41d06dbb..7fa8700a0f 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1873,7 +1873,8 @@ Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) } else __qmljs_throw_type_error(ctx); - return instance->array.indexOf(searchValue, fromIndex, ctx, instance); + uint len = instance->isArray ? instance->array.length() : instance->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); + return instance->array.indexOf(searchValue, fromIndex, len, ctx, instance); } Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index d63dfef146..b80ecdd72f 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -1079,7 +1079,6 @@ S15.4.3_A2.4 failing 15.4.3.2-2-1 failing 15.4.3.2-2-2 failing 15.4.3.2-2-3 failing -S15.4.4.10_A4_T1 failing S15.4.4.10_A5.1 failing S15.4.4.11_A3_T1 failing S15.4.4.11_A3_T2 failing @@ -1201,9 +1200,7 @@ S15.4.4.13_A5.1 failing 15.4.4.14-9-a-11 failing 15.4.4.14-9-a-13 failing 15.4.4.14-9-a-15 failing -15.4.4.14-9-a-16 failing 15.4.4.14-9-a-17 failing -15.4.4.14-9-a-18 failing 15.4.4.14-9-a-2 failing 15.4.4.14-9-a-4 failing 15.4.4.14-9-a-5 failing @@ -1213,7 +1210,6 @@ S15.4.4.13_A5.1 failing 15.4.4.14-9-b-i-10 failing 15.4.4.14-9-b-i-12 failing 15.4.4.14-9-b-i-14 failing -15.4.4.14-9-b-i-15 failing 15.4.4.14-9-b-i-16 failing 15.4.4.14-9-b-i-18 failing 15.4.4.14-9-b-i-20 failing @@ -1227,7 +1223,6 @@ S15.4.4.13_A5.1 failing 15.4.4.14-9-b-i-31 failing 15.4.4.14-9-b-i-4 failing 15.4.4.14-9-b-i-6 failing -15.4.4.14-9-b-i-7 failing 15.4.4.14-9-b-i-8 failing 15.4.4.14-9-b-ii-2 failing 15.4.4.14-9-b-ii-5 failing @@ -1488,25 +1483,21 @@ S15.4.4.13_A5.1 failing 15.4.4.16-4-9 failing 15.4.4.16-5-17 failing 15.4.4.16-7-5 failing -15.4.4.16-7-6 failing 15.4.4.16-7-8 failing 15.4.4.16-7-9 failing 15.4.4.16-7-b-1 failing 15.4.4.16-7-b-10 failing 15.4.4.16-7-b-12 failing -15.4.4.16-7-b-13 failing 15.4.4.16-7-b-15 failing 15.4.4.16-7-b-2 failing 15.4.4.16-7-b-3 failing 15.4.4.16-7-b-4 failing 15.4.4.16-7-b-6 failing -15.4.4.16-7-b-7 failing 15.4.4.16-7-b-8 failing 15.4.4.16-7-c-i-1 failing 15.4.4.16-7-c-i-11 failing 15.4.4.16-7-c-i-13 failing 15.4.4.16-7-c-i-15 failing -15.4.4.16-7-c-i-16 failing 15.4.4.16-7-c-i-17 failing 15.4.4.16-7-c-i-18 failing 15.4.4.16-7-c-i-19 failing @@ -1523,7 +1514,6 @@ S15.4.4.13_A5.1 failing 15.4.4.16-7-c-i-31 failing 15.4.4.16-7-c-i-5 failing 15.4.4.16-7-c-i-7 failing -15.4.4.16-7-c-i-8 failing 15.4.4.16-7-c-i-9 failing 15.4.4.16-7-c-ii-16 failing 15.4.4.16-7-c-ii-17 failing @@ -1619,25 +1609,21 @@ S15.4.4.13_A5.1 failing 15.4.4.17-4-9 failing 15.4.4.17-5-17 failing 15.4.4.17-7-5 failing -15.4.4.17-7-6 failing 15.4.4.17-7-8 failing 15.4.4.17-7-9 failing 15.4.4.17-7-b-1 failing 15.4.4.17-7-b-10 failing 15.4.4.17-7-b-12 failing -15.4.4.17-7-b-13 failing 15.4.4.17-7-b-15 failing 15.4.4.17-7-b-2 failing 15.4.4.17-7-b-3 failing 15.4.4.17-7-b-4 failing 15.4.4.17-7-b-6 failing -15.4.4.17-7-b-7 failing 15.4.4.17-7-b-8 failing 15.4.4.17-7-c-i-1 failing 15.4.4.17-7-c-i-11 failing 15.4.4.17-7-c-i-13 failing 15.4.4.17-7-c-i-15 failing -15.4.4.17-7-c-i-16 failing 15.4.4.17-7-c-i-17 failing 15.4.4.17-7-c-i-18 failing 15.4.4.17-7-c-i-19 failing @@ -1654,7 +1640,6 @@ S15.4.4.13_A5.1 failing 15.4.4.17-7-c-i-31 failing 15.4.4.17-7-c-i-5 failing 15.4.4.17-7-c-i-7 failing -15.4.4.17-7-c-i-8 failing 15.4.4.17-7-c-i-9 failing 15.4.4.17-7-c-ii-16 failing 15.4.4.17-7-c-ii-17 failing @@ -1750,25 +1735,21 @@ S15.4.4.13_A5.1 failing 15.4.4.18-5-17 failing 15.4.4.18-7-1 failing 15.4.4.18-7-4 failing -15.4.4.18-7-5 failing 15.4.4.18-7-8 failing 15.4.4.18-7-9 failing 15.4.4.18-7-b-1 failing 15.4.4.18-7-b-10 failing 15.4.4.18-7-b-12 failing -15.4.4.18-7-b-13 failing 15.4.4.18-7-b-15 failing 15.4.4.18-7-b-2 failing 15.4.4.18-7-b-3 failing 15.4.4.18-7-b-4 failing 15.4.4.18-7-b-6 failing -15.4.4.18-7-b-7 failing 15.4.4.18-7-b-8 failing 15.4.4.18-7-c-i-1 failing 15.4.4.18-7-c-i-11 failing 15.4.4.18-7-c-i-13 failing 15.4.4.18-7-c-i-15 failing -15.4.4.18-7-c-i-16 failing 15.4.4.18-7-c-i-17 failing 15.4.4.18-7-c-i-18 failing 15.4.4.18-7-c-i-19 failing @@ -1785,7 +1766,6 @@ S15.4.4.13_A5.1 failing 15.4.4.18-7-c-i-31 failing 15.4.4.18-7-c-i-5 failing 15.4.4.18-7-c-i-7 failing -15.4.4.18-7-c-i-8 failing 15.4.4.18-7-c-i-9 failing 15.4.4.18-7-c-ii-16 failing 15.4.4.18-7-c-ii-17 failing @@ -1878,24 +1858,20 @@ S15.4.4.13_A5.1 failing 15.4.4.19-6-1 failing 15.4.4.19-8-1 failing 15.4.4.19-8-5 failing -15.4.4.19-8-6 failing 15.4.4.19-8-8 failing 15.4.4.19-8-b-1 failing 15.4.4.19-8-b-10 failing 15.4.4.19-8-b-12 failing -15.4.4.19-8-b-13 failing 15.4.4.19-8-b-15 failing 15.4.4.19-8-b-2 failing 15.4.4.19-8-b-3 failing 15.4.4.19-8-b-4 failing 15.4.4.19-8-b-6 failing -15.4.4.19-8-b-7 failing 15.4.4.19-8-b-8 failing 15.4.4.19-8-c-i-1 failing 15.4.4.19-8-c-i-11 failing 15.4.4.19-8-c-i-13 failing 15.4.4.19-8-c-i-15 failing -15.4.4.19-8-c-i-16 failing 15.4.4.19-8-c-i-17 failing 15.4.4.19-8-c-i-18 failing 15.4.4.19-8-c-i-19 failing @@ -1912,7 +1888,6 @@ S15.4.4.13_A5.1 failing 15.4.4.19-8-c-i-31 failing 15.4.4.19-8-c-i-5 failing 15.4.4.19-8-c-i-7 failing -15.4.4.19-8-c-i-8 failing 15.4.4.19-8-c-i-9 failing 15.4.4.19-8-c-ii-16 failing 15.4.4.19-8-c-ii-17 failing @@ -1937,7 +1912,6 @@ S15.4.4.13_A5.1 failing 15.4.4.19-9-7 failing 15.4.4.19-9-8 failing 15.4.4.19-9-9 failing -S15.4.4.2_A3_T1 failing 15.4.4.20-1-1 failing 15.4.4.20-1-10 failing 15.4.4.20-1-11 failing @@ -2018,25 +1992,21 @@ S15.4.4.2_A3_T1 failing 15.4.4.20-6-8 failing 15.4.4.20-9-1 failing 15.4.4.20-9-5 failing -15.4.4.20-9-6 failing 15.4.4.20-9-8 failing 15.4.4.20-9-9 failing 15.4.4.20-9-b-1 failing 15.4.4.20-9-b-10 failing 15.4.4.20-9-b-12 failing -15.4.4.20-9-b-13 failing 15.4.4.20-9-b-15 failing 15.4.4.20-9-b-2 failing 15.4.4.20-9-b-3 failing 15.4.4.20-9-b-4 failing 15.4.4.20-9-b-6 failing -15.4.4.20-9-b-7 failing 15.4.4.20-9-b-8 failing 15.4.4.20-9-c-i-1 failing 15.4.4.20-9-c-i-11 failing 15.4.4.20-9-c-i-13 failing 15.4.4.20-9-c-i-15 failing -15.4.4.20-9-c-i-16 failing 15.4.4.20-9-c-i-17 failing 15.4.4.20-9-c-i-18 failing 15.4.4.20-9-c-i-19 failing @@ -2053,7 +2023,6 @@ S15.4.4.2_A3_T1 failing 15.4.4.20-9-c-i-31 failing 15.4.4.20-9-c-i-5 failing 15.4.4.20-9-c-i-7 failing -15.4.4.20-9-c-i-8 failing 15.4.4.20-9-c-i-9 failing 15.4.4.20-9-c-ii-16 failing 15.4.4.20-9-c-ii-17 failing @@ -2172,7 +2141,6 @@ S15.4.4.2_A3_T1 failing 15.4.4.21-8-b-iii-1-11 failing 15.4.4.21-8-b-iii-1-13 failing 15.4.4.21-8-b-iii-1-15 failing -15.4.4.21-8-b-iii-1-16 failing 15.4.4.21-8-b-iii-1-17 failing 15.4.4.21-8-b-iii-1-18 failing 15.4.4.21-8-b-iii-1-19 failing @@ -2191,7 +2159,6 @@ S15.4.4.2_A3_T1 failing 15.4.4.21-8-b-iii-1-33 failing 15.4.4.21-8-b-iii-1-5 failing 15.4.4.21-8-b-iii-1-7 failing -15.4.4.21-8-b-iii-1-8 failing 15.4.4.21-8-b-iii-1-9 failing 15.4.4.21-8-c-1 failing 15.4.4.21-8-c-2 failing @@ -2200,11 +2167,9 @@ S15.4.4.2_A3_T1 failing 15.4.4.21-8-c-6 failing 15.4.4.21-9-1 failing 15.4.4.21-9-10 failing -15.4.4.21-9-6 failing 15.4.4.21-9-8 failing 15.4.4.21-9-b-10 failing 15.4.4.21-9-b-12 failing -15.4.4.21-9-b-13 failing 15.4.4.21-9-b-15 failing 15.4.4.21-9-b-16 failing 15.4.4.21-9-b-17 failing @@ -2220,7 +2185,6 @@ S15.4.4.2_A3_T1 failing 15.4.4.21-9-b-3 failing 15.4.4.21-9-b-4 failing 15.4.4.21-9-b-6 failing -15.4.4.21-9-b-7 failing 15.4.4.21-9-b-8 failing 15.4.4.21-9-c-1 failing 15.4.4.21-9-c-i-1 failing @@ -2374,7 +2338,6 @@ S15.4.4.2_A3_T1 failing 15.4.4.22-8-b-iii-1-11 failing 15.4.4.22-8-b-iii-1-13 failing 15.4.4.22-8-b-iii-1-15 failing -15.4.4.22-8-b-iii-1-16 failing 15.4.4.22-8-b-iii-1-17 failing 15.4.4.22-8-b-iii-1-18 failing 15.4.4.22-8-b-iii-1-19 failing @@ -2393,20 +2356,16 @@ S15.4.4.2_A3_T1 failing 15.4.4.22-8-b-iii-1-33 failing 15.4.4.22-8-b-iii-1-5 failing 15.4.4.22-8-b-iii-1-7 failing -15.4.4.22-8-b-iii-1-8 failing 15.4.4.22-8-b-iii-1-9 failing 15.4.4.22-8-c-1 failing 15.4.4.22-8-c-2 failing 15.4.4.22-8-c-3 failing 15.4.4.22-8-c-5 failing 15.4.4.22-8-c-6 failing -15.4.4.22-9-6 failing 15.4.4.22-9-8 failing 15.4.4.22-9-9 failing 15.4.4.22-9-b-10 failing 15.4.4.22-9-b-12 failing -15.4.4.22-9-b-13 failing -15.4.4.22-9-b-15 failing 15.4.4.22-9-b-17 failing 15.4.4.22-9-b-18 failing 15.4.4.22-9-b-19 failing @@ -2421,7 +2380,6 @@ S15.4.4.2_A3_T1 failing 15.4.4.22-9-b-3 failing 15.4.4.22-9-b-4 failing 15.4.4.22-9-b-6 failing -15.4.4.22-9-b-7 failing 15.4.4.22-9-b-8 failing 15.4.4.22-9-c-1 failing 15.4.4.22-9-c-i-1 failing @@ -2484,7 +2442,6 @@ S15.4.4.4_A2_T1 failing S15.4.4.4_A2_T2 failing S15.4.4.4_A3_T1 failing S15.4.4.4_A4.1 failing -S15.4.4.5_A5_T1 failing S15.4.4.5_A6.1 failing S15.4.4.6_A1.2_T1 failing S15.4.4.6_A3_T2 failing @@ -2612,8 +2569,6 @@ S15.4.4.9_A3_T3 failing S15.4.4.9_A4_T1 failing S15.4.4.9_A4_T2 failing S15.4.4.9_A5.1 failing -S15.4.5.1_A1.2_T2 failing -S15.4.5.1_A1.2_T3 failing S15.5.4.13_A2_T2 failing S15.5.4.13_A2_T7 failing S15.5.4.13_A3_T3 failing @@ -3125,7 +3080,6 @@ S15.4.4.13_A1_T2 failing 15.4.4.21-8-b-iii-1-6 failing 15.4.4.22-9-c-i-33 failing 15.4.4.22-8-b-iii-1-6 failing -S15.4.4.7_A5_T1 failing 15.4.4.16-7-b-16 failing 15.4.4.17-7-b-16 failing -- cgit v1.2.3 From 3f1920b7a91c2a888312d0e6f0eef9fa63481ce8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 12:29:45 +0100 Subject: for(a in b) doesn't throw if b is null or undefined Change-Id: I5eab9dd7f6f831e83582585bd3afff1af587e117 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 3 ++- tests/TestExpectations | 17 ----------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 70db6ffe9a..c40318631f 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -596,7 +596,8 @@ void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx) { - in = __qmljs_to_object(in, ctx); + if (!in.isNull() && !in.isUndefined()) + in = __qmljs_to_object(in, ctx); Object *it = ctx->engine->newForEachIteratorObject(ctx, in.objectValue()); return Value::fromObject(it); } diff --git a/tests/TestExpectations b/tests/TestExpectations index b80ecdd72f..11b5e42824 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -351,12 +351,6 @@ S13_A6_T2 failing 13.1-40-s failing 13.1-41-s failing 13.1-42-s failing -13.2-12-s failing -13.2-16-s failing -13.2-20-s failing -13.2-24-s failing -13.2-28-s failing -13.2-8-s failing S13.2.2_A16_T2 failing S13.2.2_A16_T3 failing S13.2.2_A19_T1 failing @@ -379,7 +373,6 @@ S15.1.2.1_A4.2 failing S15.1.2.1_A4.3 failing S15.1.2.1_A4.4 failing S15.1.2.1_A4.7 failing -S12.6.4_A1 failing S12.6.4_A2 failing S12.7_A7 failing S12.8_A4_T1 failing @@ -1079,14 +1072,12 @@ S15.4.3_A2.4 failing 15.4.3.2-2-1 failing 15.4.3.2-2-2 failing 15.4.3.2-2-3 failing -S15.4.4.10_A5.1 failing S15.4.4.11_A3_T1 failing S15.4.4.11_A3_T2 failing S15.4.4.11_A4_T1 failing S15.4.4.11_A4_T2 failing S15.4.4.11_A4_T3 failing S15.4.4.11_A6_T2 failing -S15.4.4.11_A7.1 failing 15.4.4.12-9-c-ii-1 failing S15.4.4.12_A1.1_T4 failing S15.4.4.12_A1.1_T6 failing @@ -1104,7 +1095,6 @@ S15.4.4.12_A3_T3 failing S15.4.4.12_A4_T1 failing S15.4.4.12_A4_T2 failing S15.4.4.12_A4_T3 failing -S15.4.4.12_A5.1 failing S15.4.4.13_A2_T1 failing S15.4.4.13_A2_T2 failing S15.4.4.13_A2_T3 failing @@ -1113,7 +1103,6 @@ S15.4.4.13_A3_T2 failing S15.4.4.13_A3_T3 failing S15.4.4.13_A4_T1 failing S15.4.4.13_A4_T2 failing -S15.4.4.13_A5.1 failing 15.4.4.14-1-1 failing 15.4.4.14-1-10 failing 15.4.4.14-1-11 failing @@ -2441,18 +2430,14 @@ S15.4.4.3_A3_T1 failing S15.4.4.4_A2_T1 failing S15.4.4.4_A2_T2 failing S15.4.4.4_A3_T1 failing -S15.4.4.4_A4.1 failing -S15.4.4.5_A6.1 failing S15.4.4.6_A1.2_T1 failing S15.4.4.6_A3_T2 failing S15.4.4.6_A3_T3 failing S15.4.4.6_A4_T1 failing S15.4.4.6_A4_T2 failing -S15.4.4.6_A5.1 failing S15.4.4.7_A3 failing S15.4.4.7_A4_T2 failing S15.4.4.7_A4_T3 failing -S15.4.4.7_A6.1 failing S15.5.3_A1 failing S15.5.4.10_A1_T1 failing S15.5.4.10_A1_T10 failing @@ -2556,7 +2541,6 @@ S15.4.4.8_A3_T2 failing S15.4.4.8_A3_T3 failing S15.4.4.8_A4_T1 failing S15.4.4.8_A4_T2 failing -S15.4.4.8_A5.1 failing S15.4.4.9_A1.2_T1 failing S15.4.4.9_A2_T1 failing S15.4.4.9_A2_T2 failing @@ -2568,7 +2552,6 @@ S15.4.4.9_A3_T2 failing S15.4.4.9_A3_T3 failing S15.4.4.9_A4_T1 failing S15.4.4.9_A4_T2 failing -S15.4.4.9_A5.1 failing S15.5.4.13_A2_T2 failing S15.5.4.13_A2_T7 failing S15.5.4.13_A3_T3 failing -- cgit v1.2.3 From 40bb1731b28d846ceb7450d463c7bef86bd51ebf Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 12:55:24 +0100 Subject: Don't deref 0 pointers Change-Id: Ib876839266712047f975c98e23192ceda5cd13d4 Reviewed-by: Simon Hausmann --- qv4propertytable.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/qv4propertytable.h b/qv4propertytable.h index 725598ab68..6ee7f92233 100644 --- a/qv4propertytable.h +++ b/qv4propertytable.h @@ -187,9 +187,11 @@ private: for (int i = 0; i < _propertyCount; ++i) { PropertyTableEntry *prop = _properties[i]; - PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; - prop->next = bucket; - bucket = prop; + if (prop) { + PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; + prop->next = bucket; + bucket = prop; + } } } -- cgit v1.2.3 From 595878e4b54ee2e8659364343dfff91b376c9358 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 13:01:03 +0100 Subject: Fix length property of constructors Change-Id: If010f3fd6b001c29044bbf7cbdc10b0064008c10 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 20 +++++++++----------- qv4ecmaobjects_p.h | 1 - tests/TestExpectations | 16 ---------------- 3 files changed, 9 insertions(+), 28 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 7fa8700a0f..047bc52f7d 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -542,19 +542,10 @@ Value ObjectCtor::call(ExecutionContext *ctx) return __qmljs_to_object(ctx->argument(0), ctx); } -Value ObjectCtor::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) -{ - if (name == ctx->engine->id_length) { - if (hasProperty) - *hasProperty = true; - return Value::fromDouble(1); - } - return Object::__get__(ctx, name, hasProperty); -} - void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); @@ -1033,6 +1024,7 @@ Value StringCtor::call(ExecutionContext *ctx) void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode, 1); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); @@ -1345,6 +1337,7 @@ Value NumberCtor::call(ExecutionContext *ctx) void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); @@ -1523,6 +1516,7 @@ Value BooleanCtor::call(ExecutionContext *ctx) void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); @@ -1580,6 +1574,7 @@ Value ArrayCtor::call(ExecutionContext *ctx) void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); defineDefaultProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString, 0); @@ -2126,7 +2121,7 @@ Value FunctionCtor::call(ExecutionContext *ctx) void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2); @@ -2255,6 +2250,7 @@ Value DateCtor::call(ExecutionContext *ctx) void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); LocalTZA = getLocalTZA(); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("parse"), method_parse, 1); @@ -2860,6 +2856,7 @@ Value RegExpCtor::call(ExecutionContext *ctx) void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 1); defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 1); @@ -2973,6 +2970,7 @@ Value URIErrorCtor::construct(ExecutionContext *ctx) void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(obj)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); obj->defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); obj->defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); } diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index f81d7f7979..79a671d432 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -53,7 +53,6 @@ struct ObjectCtor: FunctionObject virtual Value construct(ExecutionContext *ctx); virtual Value call(ExecutionContext *ctx); - virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); }; struct ObjectPrototype: Object diff --git a/tests/TestExpectations b/tests/TestExpectations index 11b5e42824..268758f861 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -138,7 +138,6 @@ S11.2.1_A3_T2 failing S11.2.1_A3_T3 failing S11.2.1_A4_T1 failing S11.2.1_A4_T3 failing -S11.2.1_A4_T4 failing S11.2.1_A4_T5 failing 11.2.3-3_3 failing S11.2.3_A3_T1 failing @@ -173,7 +172,6 @@ S11.3.2_A4_T4 failing 11.4.1-4.a-7 failing 11.4.1-4.a-8 failing 11.4.1-5-2 failing -11.4.1-5-a-28-s failing S11.4.1_A1 failing S11.4.1_A2.1 failing S11.4.1_A3.3 failing @@ -535,7 +533,6 @@ S15.10.7.4_A9 failing 15.10.7.5-2 failing S15.10.7.5_A8 failing S15.10.7.5_A9 failing -S15.11.3_A2_T1 failing S15.11.4.2_A1 failing S15.11.4.2_A2 failing S15.11.4.3_A1 failing @@ -625,7 +622,6 @@ S15.1.3.1_A1.9_T1 failing S15.1.3.1_A1.9_T2 failing S15.1.3.1_A1.9_T3 failing S15.1.3.1_A2.1_T1 failing -S15.2.3_A3 failing 15.2.3.11-4-27 failing 15.12.3-11-2 failing 15.12.3-11-26 failing @@ -804,10 +800,7 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-164 failing 15.2.3.3-4-176 failing 15.2.3.3-4-177 failing -15.2.3.3-4-191 failing 15.2.3.3-4-192 failing -15.2.3.3-4-194 failing -15.2.3.3-4-201 failing 15.2.3.3-4-212 failing 15.2.3.3-4-213 failing 15.2.3.3-4-214 failing @@ -817,7 +810,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-9 failing 15.2.3.3-4-90 failing 15.2.3.4-2-1 failing -15.2.3.4-4-2 failing 15.2.3.4-4-44 failing 15.2.3.5-4-120 failing 15.2.3.5-4-13 failing @@ -1045,9 +1037,6 @@ S15.1.3.2_A5.3 failing S15.2.4.2_A12 failing S15.2.4.2_A13 failing 15.3.4.5-2-7 failing -S15.4.3_A2.2 failing -S15.4.3_A2.3 failing -S15.4.3_A2.4 failing 15.4.3.2-0-1 failing 15.4.3.2-0-2 failing 15.4.3.2-0-3 failing @@ -2438,7 +2427,6 @@ S15.4.4.6_A4_T2 failing S15.4.4.7_A3 failing S15.4.4.7_A4_T2 failing S15.4.4.7_A4_T3 failing -S15.5.3_A1 failing S15.5.4.10_A1_T1 failing S15.5.4.10_A1_T10 failing S15.5.4.10_A1_T11 failing @@ -2796,8 +2784,6 @@ S15.5.5.1_A3 failing S15.5.5.1_A4 failing S15.5.5.1_A5 failing 15.5.5.5.2-7-4 failing -S15.6.3_A3 failing -S15.7.3_A8 failing S15.7.4_A3.3 failing S15.7.4.5_A1.3_T01 failing S15.7.4.5_A1.3_T02 failing @@ -2823,8 +2809,6 @@ S15.8.2.18_A5 failing S15.8.2.7_A4 failing S15.8.2.7_A5 failing S15.9.5.1_A2_T1 failing -S15.9.5.1_A3_T1 failing -S15.9.5.1_A3_T2 failing 15.9.5.40_1 failing 6.4_c failing 8.0_L15 failing -- cgit v1.2.3 From aca764a401fa1e8aeab2b6633f2dee403ec8a58c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 13:10:43 +0100 Subject: Implement Array.isArray() Change-Id: Ibda9709265d551ea8462a0e624ff2dee7c16306e Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 8 ++++++++ qv4ecmaobjects_p.h | 1 + tests/TestExpectations | 29 ----------------------------- 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 047bc52f7d..18e8a553ff 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1575,6 +1575,7 @@ void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isArray"), method_isArray, 1); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); defineDefaultProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString, 0); @@ -1599,6 +1600,13 @@ void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) defineDefaultProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 1); } +Value ArrayPrototype::method_isArray(ExecutionContext *ctx) +{ + Value arg = ctx->argument(0); + bool isArray = arg.asArrayObject(); + return Value::fromBoolean(isArray); +} + Value ArrayPrototype::method_toString(ExecutionContext *ctx) { return method_join(ctx); diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 79a671d432..f1c7a0dfea 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -175,6 +175,7 @@ struct ArrayPrototype: ArrayObject void init(ExecutionContext *ctx, const Value &ctor); + static Value method_isArray(ExecutionContext *ctx); static Value method_toString(ExecutionContext *ctx); static Value method_toLocaleString(ExecutionContext *ctx); static Value method_concat(ExecutionContext *ctx); diff --git a/tests/TestExpectations b/tests/TestExpectations index 268758f861..2b87429855 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -167,7 +167,6 @@ S11.3.2_A4_T4 failing 11.4.1-2-5 failing 11.4.1-2-6 failing 11.4.1-4.a-10 failing -11.4.1-4.a-13 failing 11.4.1-4.a-5 failing 11.4.1-4.a-7 failing 11.4.1-4.a-8 failing @@ -717,7 +716,6 @@ S15.10.1_A1_T10 failing S15.10.1_A1_T11 failing 15.2.3.12-3-27 failing 15.2.3.13-2-12 failing -15.2.3.14-2-1 failing 15.2.3.14-3-4 failing 15.2.3.2-2-12 failing 15.2.3.2-2-13 failing @@ -809,7 +807,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-82 failing 15.2.3.3-4-9 failing 15.2.3.3-4-90 failing -15.2.3.4-2-1 failing 15.2.3.4-4-44 failing 15.2.3.5-4-120 failing 15.2.3.5-4-13 failing @@ -1037,30 +1034,7 @@ S15.1.3.2_A5.3 failing S15.2.4.2_A12 failing S15.2.4.2_A13 failing 15.3.4.5-2-7 failing -15.4.3.2-0-1 failing -15.4.3.2-0-2 failing -15.4.3.2-0-3 failing -15.4.3.2-0-4 failing -15.4.3.2-0-5 failing -15.4.3.2-0-6 failing -15.4.3.2-0-7 failing -15.4.3.2-1-1 failing -15.4.3.2-1-10 failing 15.4.3.2-1-11 failing -15.4.3.2-1-12 failing -15.4.3.2-1-13 failing -15.4.3.2-1-15 failing -15.4.3.2-1-2 failing -15.4.3.2-1-3 failing -15.4.3.2-1-4 failing -15.4.3.2-1-5 failing -15.4.3.2-1-6 failing -15.4.3.2-1-7 failing -15.4.3.2-1-8 failing -15.4.3.2-1-9 failing -15.4.3.2-2-1 failing -15.4.3.2-2-2 failing -15.4.3.2-2-3 failing S15.4.4.11_A3_T1 failing S15.4.4.11_A3_T2 failing S15.4.4.11_A4_T1 failing @@ -1833,7 +1807,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.19-4-9 failing 15.4.4.19-5-1 failing 15.4.4.19-5-17 failing -15.4.4.19-6-1 failing 15.4.4.19-8-1 failing 15.4.4.19-8-5 failing 15.4.4.19-8-8 failing @@ -1959,8 +1932,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.20-4-8 failing 15.4.4.20-4-9 failing 15.4.4.20-5-17 failing -15.4.4.20-5-27 failing -15.4.4.20-6-1 failing 15.4.4.20-6-2 failing 15.4.4.20-6-3 failing 15.4.4.20-6-4 failing -- cgit v1.2.3 From 43bd1297c7458c22b4cc4fa0d77cbcde5ffe9b19 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 14 Jan 2013 13:50:35 +0100 Subject: Make check improvements Add support for "make check-interpreter" and fix "make check" for shadow builds. Change-Id: I38f0bd9952961f143c31a7ae15bddf90245e7500 Reviewed-by: Lars Knoll --- v4.pro | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/v4.pro b/v4.pro index b47a655ff1..e840fc9a52 100644 --- a/v4.pro +++ b/v4.pro @@ -88,11 +88,20 @@ DEFINES += QMLJS_NO_LLVM } +TESTSCRIPT=$$PWD/tests/test262.py +V4CMD = $$OUT_PWD/v4 + checktarget.target = check -checktarget.commands = python tests/test262.py --parallel --with-test-expectations --update-expectations +checktarget.commands = python $$TESTSCRIPT --command=$$V4CMD --parallel --with-test-expectations --update-expectations checktarget.depends = all QMAKE_EXTRA_TARGETS += checktarget +checkmothtarget.target = check-interpreter +checkmothtarget.commands = python $$TESTSCRIPT --command=\"$$V4CMD --interpret\" --parallel --with-test-expectations --update-expectations +checkmothtarget.depends = all +QMAKE_EXTRA_TARGETS += checkmothtarget + + include(moth/moth.pri) include(masm/masm.pri) include(3rdparty/double-conversion/double-conversion.pri) -- cgit v1.2.3 From 502d9b4902a537d8aeaf4077d1a4ebaa9c19561f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 13:52:16 +0100 Subject: Fix a corner case in the arguments object Change-Id: I075e08b2629db47456a601656f298c7397a7fa67 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 11 ++++++++--- tests/TestExpectations | 1 - 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 7abd5cae29..631e90a0b2 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -1179,10 +1179,15 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterC PropertyDescriptor pd = PropertyDescriptor::fromAccessor(get, set); pd.configurable = PropertyDescriptor::Enabled; pd.enumberable = PropertyDescriptor::Enabled; - for (uint i = 0; i < (uint)formalParameterCount; ++i) + uint enumerableParams = qMin(formalParameterCount, actualParameterCount); + for (uint i = 0; i < (uint)enumerableParams; ++i) __defineOwnProperty__(context, i, &pd); - for (uint i = formalParameterCount; i < context->argumentCount; ++i) - Object::__put__(context, i, context->arguments[i]); + pd.type = PropertyDescriptor::Data; + pd.writable = PropertyDescriptor::Enabled; + for (uint i = enumerableParams; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) { + pd.value = context->argument(i); + __defineOwnProperty__(context, i, &pd); + } defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); } } diff --git a/tests/TestExpectations b/tests/TestExpectations index 2b87429855..5124cef835 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -716,7 +716,6 @@ S15.10.1_A1_T10 failing S15.10.1_A1_T11 failing 15.2.3.12-3-27 failing 15.2.3.13-2-12 failing -15.2.3.14-3-4 failing 15.2.3.2-2-12 failing 15.2.3.2-2-13 failing 15.2.3.2-2-14 failing -- cgit v1.2.3 From 960e86a5c7f8dcfb268a333f90ee2dc28f45ec9d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 14:02:03 +0100 Subject: The prototype of error constructors is the function prototype Change-Id: I9365d04b199f4cc7b75f347886b68ccc073564fb Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index a54c9c3fc7..07983b7daf 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -153,12 +153,12 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) dateCtor.objectValue()->prototype = functionPrototype; regExpCtor.objectValue()->prototype = functionPrototype; errorCtor.objectValue()->prototype = functionPrototype; - evalErrorCtor.objectValue()->prototype = errorPrototype; - rangeErrorCtor.objectValue()->prototype = errorPrototype; - referenceErrorCtor.objectValue()->prototype = errorPrototype; - syntaxErrorCtor.objectValue()->prototype = errorPrototype; - typeErrorCtor.objectValue()->prototype = errorPrototype; - uRIErrorCtor.objectValue()->prototype = errorPrototype; + evalErrorCtor.objectValue()->prototype = functionPrototype; + rangeErrorCtor.objectValue()->prototype = functionPrototype; + referenceErrorCtor.objectValue()->prototype = functionPrototype; + syntaxErrorCtor.objectValue()->prototype = functionPrototype; + typeErrorCtor.objectValue()->prototype = functionPrototype; + uRIErrorCtor.objectValue()->prototype = functionPrototype; objectPrototype->init(rootContext, objectCtor); stringPrototype->init(rootContext, stringCtor); -- cgit v1.2.3 From 418f09d7faa9a4c8948f075cfb2c18e79aed8f3f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 14:02:40 +0100 Subject: getPrototypeOf returns null if the object doesn't have a prototype Change-Id: I33b63bf19b3b8bb3292f3a2f783228f404ed7cac Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 3 ++- tests/TestExpectations | 7 ------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 18e8a553ff..30905051ea 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -577,7 +577,8 @@ Value ObjectPrototype::method_getPrototypeOf(ExecutionContext *ctx) if (! o.isObject()) ctx->throwTypeError(); - return Value::fromObject(o.objectValue()->prototype); + Object *p = o.objectValue()->prototype; + return p ? Value::fromObject(p) : Value::nullValue(); } Value ObjectPrototype::method_getOwnPropertyDescriptor(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index 5124cef835..4b2db80a99 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -716,14 +716,7 @@ S15.10.1_A1_T10 failing S15.10.1_A1_T11 failing 15.2.3.12-3-27 failing 15.2.3.13-2-12 failing -15.2.3.2-2-12 failing -15.2.3.2-2-13 failing -15.2.3.2-2-14 failing -15.2.3.2-2-15 failing -15.2.3.2-2-16 failing -15.2.3.2-2-17 failing 15.2.3.2-2-18 failing -15.2.3.2-2-31 failing S15.1.3.1_A2.2_T1 failing S15.1.3.1_A2.3_T1 failing S15.1.3.1_A2.4_T1 failing -- cgit v1.2.3 From a880ef56ff51355b202e3b931e126e73bbe8b158 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 14:51:53 +0100 Subject: Implement StringObject properly we now pass all of 15.5.5 Change-Id: Ia72bd5f064e6edfcd440e2c2ea72459a09efd1e8 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 4 ++-- qmljs_engine.h | 2 +- qmljs_objects.cpp | 28 ++++++++++++++++++++++++++++ qmljs_objects.h | 5 ++++- qmljs_runtime.cpp | 9 ++++----- qv4ecmaobjects.cpp | 2 +- qv4ecmaobjects_p.h | 2 +- tests/TestExpectations | 11 ----------- 8 files changed, 41 insertions(+), 22 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 07983b7daf..9264d8d32f 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -279,9 +279,9 @@ String *ExecutionEngine::newString(const QString &s) return stringPool->newString(s); } -Object *ExecutionEngine::newStringObject(const Value &value) +Object *ExecutionEngine::newStringObject(ExecutionContext *ctx, const Value &value) { - StringObject *object = new (memoryManager) StringObject(value); + StringObject *object = new (memoryManager) StringObject(ctx, value); object->prototype = stringPrototype; return object; } diff --git a/qmljs_engine.h b/qmljs_engine.h index 3e098bead8..babaf1a0b0 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -176,7 +176,7 @@ struct ExecutionEngine FunctionObject *newObjectCtor(ExecutionContext *ctx); String *newString(const QString &s); - Object *newStringObject(const Value &value); + Object *newStringObject(ExecutionContext *ctx, const Value &value); FunctionObject *newStringCtor(ExecutionContext *ctx); Object *newNumberObject(const Value &value); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 631e90a0b2..c0485bb492 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -1316,3 +1316,31 @@ void BoundFunction::getCollectables(QVector &objects) if (Object *o = boundArgs.at(i).asObject()) objects.append(o); } + + +StringObject::StringObject(ExecutionContext *ctx, const Value &value) + : value(value) +{ + tmpProperty.type = PropertyDescriptor::Data; + tmpProperty.enumberable = PropertyDescriptor::Enabled; + tmpProperty.writable = PropertyDescriptor::Disabled; + tmpProperty.configurable = PropertyDescriptor::Disabled; + tmpProperty.value = Value::undefinedValue(); + + assert(value.isString()); + defineReadonlyProperty(ctx->engine->id_length, Value::fromUInt32(value.stringValue()->toQString().length())); +} + +PropertyDescriptor *StringObject::__getOwnProperty__(ExecutionContext *ctx, uint index) +{ + PropertyDescriptor *pd = Object::__getOwnProperty__(ctx, index); + if (pd) + return pd; + assert(value.isString()); + QString str = value.stringValue()->toQString(); + if (index >= (uint)str.length()) + return 0; + String *result = ctx->engine->newString(str.mid(index, 1)); + tmpProperty.value = Value::fromString(result); + return &tmpProperty; +} diff --git a/qmljs_objects.h b/qmljs_objects.h index d373573cca..5d3b9ca6a9 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -199,9 +199,12 @@ struct NumberObject: Object { struct StringObject: Object { Value value; - StringObject(const Value &value): value(value) {} + PropertyDescriptor tmpProperty; + StringObject(ExecutionContext *ctx, const Value &value); virtual QString className() { return QStringLiteral("String"); } virtual StringObject *asStringObject() { return this; } + + virtual PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); }; struct DateObject: Object { diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index c40318631f..8833f7ba57 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -544,7 +544,7 @@ Value __qmljs_new_number_object(ExecutionContext *ctx, double number) Value __qmljs_new_string_object(ExecutionContext *ctx, String *string) { Value value = Value::fromString(string); - return Value::fromObject(ctx->engine->newStringObject(value)); + return Value::fromObject(ctx->engine->newStringObject(ctx, value)); } void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Value value) @@ -558,11 +558,10 @@ Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) { uint idx = index.asArrayIndex(); if (object.isString() && idx < UINT_MAX) { - const QString s = object.stringValue()->toQString().mid(idx, 1); - if (s.isNull()) + if (idx >= object.stringValue()->toQString().length()) return Value::undefinedValue(); - else - return Value::fromString(ctx, s); + const QString s = object.stringValue()->toQString().mid(idx, 1); + return Value::fromString(ctx, s); } if (! object.isObject()) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 30905051ea..73ff246176 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1009,7 +1009,7 @@ Value StringCtor::construct(ExecutionContext *ctx) value = Value::fromString(ctx->argument(0).toString(ctx)); else value = Value::fromString(ctx, QString()); - return Value::fromObject(ctx->engine->newStringObject(value)); + return Value::fromObject(ctx->engine->newStringObject(ctx, value)); } Value StringCtor::call(ExecutionContext *ctx) diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index f1c7a0dfea..53936be923 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -97,7 +97,7 @@ struct StringCtor: FunctionObject struct StringPrototype: StringObject { - StringPrototype(ExecutionContext *ctx): StringObject(Value::fromString(ctx, QString())) {} + StringPrototype(ExecutionContext *ctx): StringObject(ctx, Value::fromString(ctx, QString())) {} void init(ExecutionContext *ctx, const Value &ctor); static QString getThisString(ExecutionContext *ctx); diff --git a/tests/TestExpectations b/tests/TestExpectations index 4b2db80a99..e6349fe088 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -138,7 +138,6 @@ S11.2.1_A3_T2 failing S11.2.1_A3_T3 failing S11.2.1_A4_T1 failing S11.2.1_A4_T3 failing -S11.2.1_A4_T5 failing 11.2.3-3_3 failing S11.2.3_A3_T1 failing S11.2.3_A3_T2 failing @@ -780,7 +779,6 @@ S15.1.3.2_A4_T4 failing S15.1.3.2_A5.1 failing S15.1.3.2_A5.2 failing S15.1.3.2_A5.3 failing -15.2.3.3-3-14 failing 15.2.3.3-4-10 failing 15.2.3.3-4-11 failing 15.2.3.3-4-12 failing @@ -790,7 +788,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-164 failing 15.2.3.3-4-176 failing 15.2.3.3-4-177 failing -15.2.3.3-4-192 failing 15.2.3.3-4-212 failing 15.2.3.3-4-213 failing 15.2.3.3-4-214 failing @@ -2504,7 +2501,6 @@ S15.4.4.9_A3_T3 failing S15.4.4.9_A4_T1 failing S15.4.4.9_A4_T2 failing S15.5.4.13_A2_T2 failing -S15.5.4.13_A2_T7 failing S15.5.4.13_A3_T3 failing S15.5.4.14_A1_T1 failing S15.5.4.14_A1_T10 failing @@ -2608,7 +2604,6 @@ S15.5.4.15_A1_T2 failing S15.5.4.15_A1_T7 failing S15.5.4.15_A1_T8 failing S15.5.4.15_A1_T9 failing -S15.5.4.15_A2_T8 failing 15.5.4.20-0-1 failing 15.5.4.20-0-2 failing 15.5.4.20-1-3 failing @@ -2741,12 +2736,6 @@ S15.5.4.4_A1_T10 failing S15.5.4.8_A1_T10 failing S15.5.4.8_A1_T12 failing S15.5.4.8_A1_T4 failing -S15.5.5.1_A1 failing -S15.5.5.1_A2 failing -S15.5.5.1_A3 failing -S15.5.5.1_A4 failing -S15.5.5.1_A5 failing -15.5.5.5.2-7-4 failing S15.7.4_A3.3 failing S15.7.4.5_A1.3_T01 failing S15.7.4.5_A1.3_T02 failing -- cgit v1.2.3 From 262a9225230463c7d41846cde9318fec20ef9fd2 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 14:55:35 +0100 Subject: Fix an out of bounds read. Change-Id: Ie7eb4cdc9c0f5002b67145f23fe8a0a3b105b626 Reviewed-by: Simon Hausmann --- qv4array.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qv4array.cpp b/qv4array.cpp index 23164916cb..ab6a18e517 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -526,8 +526,8 @@ Value Array::indexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *c return Value::fromDouble(n->key()); } } else { - if (endIndex > len) - endIndex = len; + if (endIndex > values.size()) + endIndex = values.size(); PropertyDescriptor *pd = values.data() + offset; PropertyDescriptor *end = pd + endIndex; pd += fromIndex; -- cgit v1.2.3 From 1b445d35fa304e5e34722b1c46d0733d9b292419 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 15:54:07 +0100 Subject: Correctly iterator over properties of StringObjects Change-Id: I16a8065dd0bb9162811ced1bc6c73c07c02a331d Reviewed-by: Simon Hausmann --- qmljs_objects.h | 5 ++--- qv4ecmaobjects.cpp | 16 ++++++++-------- qv4objectiterator.cpp | 32 ++++++++++++++++++++++++++++---- qv4objectiterator.h | 8 +++++--- 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/qmljs_objects.h b/qmljs_objects.h index 5d3b9ca6a9..5837da923c 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -171,13 +171,12 @@ protected: }; struct ForEachIteratorObject: Object { - ExecutionContext *context; ObjectIterator it; ForEachIteratorObject(ExecutionContext *ctx, Object *o) - : context(ctx), it(o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) {} + : it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) {} virtual QString className() { return QStringLiteral("__ForEachIteratorObject"); } - Value nextPropertyName() { return it.nextPropertyNameAsString(context); } + Value nextPropertyName() { return it.nextPropertyNameAsString(); } protected: virtual void getCollectables(QVector &objects); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 73ff246176..99aeb21187 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -600,9 +600,9 @@ Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); Array &a = array->array; - ObjectIterator it(O, ObjectIterator::NoFlags); + ObjectIterator it(ctx, O, ObjectIterator::NoFlags); while (1) { - Value v = it.nextPropertyNameAsString(ctx); + Value v = it.nextPropertyNameAsString(); if (v.isNull()) break; a.push_back(v); @@ -656,7 +656,7 @@ Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) Object *o = ctx->argument(1).toObject(ctx).objectValue(); - ObjectIterator it(o, ObjectIterator::EnumberableOnly); + ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); while (1) { uint index; String *name; @@ -682,7 +682,7 @@ Value ObjectPrototype::method_seal(ExecutionContext *ctx) Object *o = ctx->argument(0).objectValue(); o->extensible = false; - ObjectIterator it(o, ObjectIterator::NoFlags); + ObjectIterator it(ctx, o, ObjectIterator::NoFlags); while (1) { uint index; String *name; @@ -702,7 +702,7 @@ Value ObjectPrototype::method_freeze(ExecutionContext *ctx) Object *o = ctx->argument(0).objectValue(); o->extensible = false; - ObjectIterator it(o, ObjectIterator::NoFlags); + ObjectIterator it(ctx, o, ObjectIterator::NoFlags); while (1) { uint index; String *name; @@ -735,7 +735,7 @@ Value ObjectPrototype::method_isSealed(ExecutionContext *ctx) if (o->extensible) return Value::fromBoolean(false); - ObjectIterator it(o, ObjectIterator::NoFlags); + ObjectIterator it(ctx, o, ObjectIterator::NoFlags); while (1) { uint index; String *name; @@ -757,7 +757,7 @@ Value ObjectPrototype::method_isFrozen(ExecutionContext *ctx) if (o->extensible) return Value::fromBoolean(false); - ObjectIterator it(o, ObjectIterator::NoFlags); + ObjectIterator it(ctx, o, ObjectIterator::NoFlags); while (1) { uint index; String *name; @@ -788,7 +788,7 @@ Value ObjectPrototype::method_keys(ExecutionContext *ctx) ArrayObject *a = ctx->engine->newArrayObject(ctx); - ObjectIterator it(o, ObjectIterator::EnumberableOnly); + ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); while (1) { uint index; String *name; diff --git a/qv4objectiterator.cpp b/qv4objectiterator.cpp index 3a657180fc..f324b1df62 100644 --- a/qv4objectiterator.cpp +++ b/qv4objectiterator.cpp @@ -44,14 +44,17 @@ namespace QQmlJS { namespace VM { -ObjectIterator::ObjectIterator(Object *o, uint flags) - : object(o) +ObjectIterator::ObjectIterator(ExecutionContext *context, Object *o, uint flags) + : context(context) + , object(o) , current(o) , arrayNode(0) , arrayIndex(0) , tableIndex(0) , flags(flags) { + if (current && current->asStringObject()) + this->flags |= CurrentIsString; } PropertyDescriptor *ObjectIterator::next(String **name, uint *index) @@ -63,6 +66,21 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) if (!current) break; + if (flags & CurrentIsString) { + StringObject *s = static_cast(current); + uint slen = s->value.stringValue()->toQString().length(); + while (arrayIndex < slen) { + *index = arrayIndex; + ++arrayIndex; + return s->__getOwnProperty__(context, *index); + } + flags &= ~CurrentIsString; + arrayNode = current->array.sparseBegin(); + // iterate until we're past the end of the string + while (arrayNode && arrayNode->key() < slen) + arrayNode = arrayNode->nextNode(); + } + if (!arrayIndex) arrayNode = current->array.sparseBegin(); @@ -85,7 +103,7 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) while (arrayIndex < current->array.length()) { p = current->array.at(arrayIndex); ++arrayIndex; - if (p && (!(flags & EnumberableOnly) || p->isEnumerable())) { + if (p && p->type != PropertyDescriptor::Generic && (!(flags & EnumberableOnly) || p->isEnumerable())) { *index = arrayIndex - 1; return p; } @@ -96,6 +114,12 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) current = current->prototype; else current = 0; + if (current && current->asStringObject()) + flags |= CurrentIsString; + else + flags &= ~CurrentIsString; + + arrayIndex = 0; tableIndex = 0; continue; @@ -124,7 +148,7 @@ Value ObjectIterator::nextPropertyName() return Value::nullValue(); } -Value ObjectIterator::nextPropertyNameAsString(ExecutionContext *context) +Value ObjectIterator::nextPropertyNameAsString() { uint index; String *name; diff --git a/qv4objectiterator.h b/qv4objectiterator.h index db340de068..baecc25424 100644 --- a/qv4objectiterator.h +++ b/qv4objectiterator.h @@ -55,9 +55,11 @@ struct ObjectIterator enum Flags { NoFlags = 0, EnumberableOnly = 0x1, - WithProtoChain = 0x2 + WithProtoChain = 0x2, + CurrentIsString = 0x4 }; + ExecutionContext *context; Object *object; Object *current; SparseArrayNode *arrayNode; @@ -65,10 +67,10 @@ struct ObjectIterator uint tableIndex; uint flags; - ObjectIterator(Object *o, uint flags); + ObjectIterator(ExecutionContext *context, Object *o, uint flags); PropertyDescriptor *next(String **name, uint *index); Value nextPropertyName(); - Value nextPropertyNameAsString(ExecutionContext *context); + Value nextPropertyNameAsString(); }; } -- cgit v1.2.3 From 2dba585a8dd25601fcc5d42298b99a68e46db00e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 16:28:32 +0100 Subject: Give our builtins a prototype, so toString() doesn't assert Change-Id: I43ff797787187caf8f5a01dee7b2dcc1008fd06f Reviewed-by: Simon Hausmann --- main.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index d115ee09c7..748294b9a1 100644 --- a/main.cpp +++ b/main.cpp @@ -350,10 +350,14 @@ int main(int argc, char *argv[]) QQmlJS::VM::ExecutionContext *ctx = vm.rootContext; QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); + QQmlJS::VM::Object *print = new (ctx->engine->memoryManager) builtins::Print(ctx); + print->prototype = ctx->engine->objectPrototype; globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), - QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::Print(ctx))); + QQmlJS::VM::Value::fromObject(print)); + QQmlJS::VM::Object *gc = new (ctx->engine->memoryManager) builtins::GC(ctx); + gc->prototype = ctx->engine->objectPrototype; globalObject->__put__(ctx, vm.identifier(QStringLiteral("gc")), - QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::GC(ctx))); + QQmlJS::VM::Value::fromObject(gc)); bool errorInTestHarness = false; if (!qgetenv("IN_TEST_HARNESS").isEmpty()) -- cgit v1.2.3 From cfc60b039c02ed10de5a210c5e23d95426e113fc Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 17:11:03 +0100 Subject: Fix a corner case in Object.create() Change-Id: Ie1c32178793b44c2d14630b849622bffc4a09d26 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 99aeb21187..fa1b398377 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -620,7 +620,7 @@ Value ObjectPrototype::method_create(ExecutionContext *ctx) newObject->prototype = O.objectValue(); Value objValue = Value::fromObject(newObject); - if (ctx->argumentCount > 1) { + if (ctx->argumentCount > 1 && !ctx->argument(1).isUndefined()) { ctx->arguments[0] = objValue; method_defineProperties(ctx); } -- cgit v1.2.3 From 88e328b09b6584878f5bd7c29c272a009b61992f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 17:13:25 +0100 Subject: Make sure our error objects always have the correct prototype set Change-Id: I26ed066d14d0fe6147bf043c35d41e6434bc8873 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 28 +++++++++--------------- qmljs_objects.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ qmljs_objects.h | 24 +++++++-------------- tests/TestExpectations | 14 ------------ 4 files changed, 76 insertions(+), 48 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 9264d8d32f..30756e335d 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -121,12 +121,12 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) functionPrototype->prototype = objectPrototype; regExpPrototype->prototype = objectPrototype; errorPrototype->prototype = objectPrototype; - evalErrorPrototype->prototype = errorPrototype; - rangeErrorPrototype->prototype = errorPrototype; - referenceErrorPrototype->prototype = errorPrototype; - syntaxErrorPrototype->prototype = errorPrototype; - typeErrorPrototype->prototype = errorPrototype; - uRIErrorPrototype->prototype = errorPrototype; + evalErrorPrototype->prototype = objectPrototype; + rangeErrorPrototype->prototype = objectPrototype; + referenceErrorPrototype->prototype = objectPrototype; + syntaxErrorPrototype->prototype = objectPrototype; + typeErrorPrototype->prototype = objectPrototype; + uRIErrorPrototype->prototype = objectPrototype; objectCtor = Value::fromObject(new (memoryManager) ObjectCtor(rootContext)); stringCtor = Value::fromObject(new (memoryManager) StringCtor(rootContext)); @@ -387,30 +387,22 @@ Object *ExecutionEngine::newErrorObject(const Value &value) Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) { - SyntaxErrorObject *object = new (memoryManager) SyntaxErrorObject(ctx, message); - object->prototype = syntaxErrorPrototype; - return object; + return new (memoryManager) SyntaxErrorObject(ctx, message); } Object *ExecutionEngine::newReferenceErrorObject(ExecutionContext *ctx, const QString &message) { - ReferenceErrorObject *object = new (memoryManager) ReferenceErrorObject(ctx, message); - object->prototype = referenceErrorPrototype; - return object; + return new (memoryManager) ReferenceErrorObject(ctx, message); } Object *ExecutionEngine::newTypeErrorObject(ExecutionContext *ctx, const QString &message) { - TypeErrorObject *object = new (memoryManager) TypeErrorObject(ctx, message); - object->prototype = typeErrorPrototype; - return object; + return new (memoryManager) TypeErrorObject(ctx, message); } Object *ExecutionEngine::newRangeErrorObject(ExecutionContext *ctx, const QString &message) { - RangeErrorObject *object = new (memoryManager) RangeErrorObject(ctx, message); - object->prototype = rangeErrorPrototype; - return object; + return new (memoryManager) RangeErrorObject(ctx, message); } Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index c0485bb492..f4ca55d8dc 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -1153,6 +1153,7 @@ SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *m : ErrorObject(ctx->argument(0)) , msg(message) { + prototype = ctx->engine->syntaxErrorPrototype; if (message) value = Value::fromString(message->buildFullMessage(ctx)); setNameProperty(ctx); @@ -1344,3 +1345,60 @@ PropertyDescriptor *StringObject::__getOwnProperty__(ExecutionContext *ctx, uint tmpProperty.value = Value::fromString(result); return &tmpProperty; } + + +EvalErrorObject::EvalErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->argument(0)) +{ + setNameProperty(ctx); + prototype = ctx->engine->evalErrorPrototype; +} + +RangeErrorObject::RangeErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->argument(0)) +{ + setNameProperty(ctx); + prototype = ctx->engine->rangeErrorPrototype; +} + +RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &msg) + : ErrorObject(Value::fromString(ctx,msg)) +{ + setNameProperty(ctx); + prototype = ctx->engine->rangeErrorPrototype; +} + +ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->argument(0)) +{ + setNameProperty(ctx); + prototype = ctx->engine->referenceErrorPrototype; +} + +ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString &msg) + : ErrorObject(Value::fromString(ctx,msg)) +{ + setNameProperty(ctx); + prototype = ctx->engine->referenceErrorPrototype; +} + +TypeErrorObject::TypeErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->argument(0)) +{ + setNameProperty(ctx); + prototype = ctx->engine->typeErrorPrototype; +} + +TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &msg) + : ErrorObject(Value::fromString(ctx,msg)) +{ + setNameProperty(ctx); + prototype = ctx->engine->typeErrorPrototype; +} + +URIErrorObject::URIErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->argument(0)) +{ + setNameProperty(ctx); + prototype = ctx->engine->uRIErrorPrototype; +} diff --git a/qmljs_objects.h b/qmljs_objects.h index 5837da923c..19fa492565 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -386,24 +386,19 @@ protected: }; struct EvalErrorObject: ErrorObject { - EvalErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + EvalErrorObject(ExecutionContext *ctx); virtual QString className() { return QStringLiteral("EvalError"); } }; struct RangeErrorObject: ErrorObject { - RangeErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } - RangeErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(Value::fromString(ctx,msg)) { setNameProperty(ctx); } + RangeErrorObject(ExecutionContext *ctx); + RangeErrorObject(ExecutionContext *ctx, const QString &msg); virtual QString className() { return QStringLiteral("RangeError"); } }; struct ReferenceErrorObject: ErrorObject { - ReferenceErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } - ReferenceErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(Value::fromString(ctx,msg)) { setNameProperty(ctx); } + ReferenceErrorObject(ExecutionContext *ctx); + ReferenceErrorObject(ExecutionContext *ctx, const QString &msg); virtual QString className() { return QStringLiteral("ReferenceError"); } }; @@ -420,16 +415,13 @@ private: }; struct TypeErrorObject: ErrorObject { - TypeErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } - TypeErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(Value::fromString(ctx,msg)) { setNameProperty(ctx); } + TypeErrorObject(ExecutionContext *ctx); + TypeErrorObject(ExecutionContext *ctx, const QString &msg); virtual QString className() { return QStringLiteral("TypeError"); } }; struct URIErrorObject: ErrorObject { - URIErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->argument(0)) { setNameProperty(ctx); } + URIErrorObject(ExecutionContext *ctx); virtual QString className() { return QStringLiteral("URIError"); } }; diff --git a/tests/TestExpectations b/tests/TestExpectations index e6349fe088..081d719aa3 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -242,8 +242,6 @@ S11.9.4_A2.4_T1 failing S11.9.4_A2.4_T3 failing S11.9.5_A2.4_T1 failing S11.9.5_A2.4_T3 failing -S12.14_A19_T1 failing -S12.14_A19_T2 failing S12.14_A7_T1 failing S12.14_A7_T3 failing 12.14.1-1-s failing @@ -802,7 +800,6 @@ S15.1.3.2_A5.3 failing 15.2.3.5-4-145 failing 15.2.3.5-4-173 failing 15.2.3.5-4-199 failing -15.2.3.5-4-2 failing 15.2.3.5-4-224 failing 15.2.3.5-4-252 failing 15.2.3.5-4-67 failing @@ -811,7 +808,6 @@ S15.1.3.2_A5.3 failing 15.2.3.5-4-287 failing 15.2.3.5-4-315 failing 15.2.3.5-4-36 failing -15.2.3.5-4-40 failing 15.2.3.6-3-119 failing 15.2.3.6-3-147-1 failing 15.2.3.6-3-147 failing @@ -1160,7 +1156,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.14-9-b-i-26 failing 15.4.4.14-9-b-i-27 failing 15.4.4.14-9-b-i-29 failing -15.4.4.14-9-b-i-30 failing 15.4.4.14-9-b-i-31 failing 15.4.4.14-9-b-i-4 failing 15.4.4.14-9-b-i-6 failing @@ -1452,7 +1447,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.16-7-c-i-29 failing 15.4.4.16-7-c-i-3 failing 15.4.4.16-7-c-i-30 failing -15.4.4.16-7-c-i-31 failing 15.4.4.16-7-c-i-5 failing 15.4.4.16-7-c-i-7 failing 15.4.4.16-7-c-i-9 failing @@ -1578,7 +1572,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.17-7-c-i-29 failing 15.4.4.17-7-c-i-3 failing 15.4.4.17-7-c-i-30 failing -15.4.4.17-7-c-i-31 failing 15.4.4.17-7-c-i-5 failing 15.4.4.17-7-c-i-7 failing 15.4.4.17-7-c-i-9 failing @@ -1704,7 +1697,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.18-7-c-i-29 failing 15.4.4.18-7-c-i-3 failing 15.4.4.18-7-c-i-30 failing -15.4.4.18-7-c-i-31 failing 15.4.4.18-7-c-i-5 failing 15.4.4.18-7-c-i-7 failing 15.4.4.18-7-c-i-9 failing @@ -1825,7 +1817,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.19-8-c-i-29 failing 15.4.4.19-8-c-i-3 failing 15.4.4.19-8-c-i-30 failing -15.4.4.19-8-c-i-31 failing 15.4.4.19-8-c-i-5 failing 15.4.4.19-8-c-i-7 failing 15.4.4.19-8-c-i-9 failing @@ -1958,7 +1949,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.20-9-c-i-29 failing 15.4.4.20-9-c-i-3 failing 15.4.4.20-9-c-i-30 failing -15.4.4.20-9-c-i-31 failing 15.4.4.20-9-c-i-5 failing 15.4.4.20-9-c-i-7 failing 15.4.4.20-9-c-i-9 failing @@ -2094,7 +2084,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.21-8-b-iii-1-3 failing 15.4.4.21-8-b-iii-1-31 failing 15.4.4.21-8-b-iii-1-32 failing -15.4.4.21-8-b-iii-1-33 failing 15.4.4.21-8-b-iii-1-5 failing 15.4.4.21-8-b-iii-1-7 failing 15.4.4.21-8-b-iii-1-9 failing @@ -2207,7 +2196,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.21-9-c-i-30 failing 15.4.4.21-9-c-i-31 failing 15.4.4.21-9-c-i-32 failing -15.4.4.21-9-c-i-33 failing 15.4.4.21-9-c-i-4 failing 15.4.4.21-9-c-i-5 failing 15.4.4.21-9-c-i-6 failing @@ -2291,7 +2279,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.22-8-b-iii-1-3 failing 15.4.4.22-8-b-iii-1-31 failing 15.4.4.22-8-b-iii-1-32 failing -15.4.4.22-8-b-iii-1-33 failing 15.4.4.22-8-b-iii-1-5 failing 15.4.4.22-8-b-iii-1-7 failing 15.4.4.22-8-b-iii-1-9 failing @@ -2997,7 +2984,6 @@ S15.4.4.13_A1_T2 failing 15.4.4.19-8-c-i-6 failing 15.4.4.20-9-c-i-6 failing 15.4.4.21-8-b-iii-1-6 failing -15.4.4.22-9-c-i-33 failing 15.4.4.22-8-b-iii-1-6 failing 15.4.4.16-7-b-16 failing -- cgit v1.2.3 From c4bc5f1b24d49a94368dd47d966da94dcde81d4c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 17:41:53 +0100 Subject: Correctly working Array.length setter The setter will remove elements from the array that have an index larger then the new length, but only if they are configurable. Change-Id: Id9f09d5ab038aaba6ca1c93be608c46383e1f2a1 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 6 ++++-- qv4array.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ qv4array.h | 23 ++--------------------- tests/TestExpectations | 18 ------------------ 4 files changed, 52 insertions(+), 41 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index f4ca55d8dc..3ea7edec73 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -485,16 +485,18 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property PropertyDescriptor *lp = array.getLengthProperty(); if (!lp->isWritable() || desc->type == PropertyDescriptor::Accessor || desc->isConfigurable() || desc->isEnumerable()) goto reject; + bool succeeded = false; if (desc->type == PropertyDescriptor::Data) { bool ok; uint l = desc->value.asArrayLength(ctx, &ok); if (!ok) ctx->throwRangeError(desc->value); - if (!array.setLength(l)) - goto reject; + succeeded = array.setLength(l); } if (desc->writable == PropertyDescriptor::Disabled) lp->writable = PropertyDescriptor::Disabled; + if (!succeeded) + goto reject; return true; } diff --git a/qv4array.cpp b/qv4array.cpp index ab6a18e517..a190c78633 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -604,6 +604,52 @@ void Array::initSparse() } } +bool Array::setLength(uint newLen) { + if (lengthProperty && !lengthProperty->isWritable()) + return false; + uint oldLen = length(); + bool ok = true; + if (newLen < oldLen) { + if (sparse) { + SparseArrayNode *begin = sparse->lowerBound(newLen); + SparseArrayNode *it = sparse->end()->previousNode(); + while (1) { + PropertyDescriptor &pd = values[it->value]; + if (pd.type != PropertyDescriptor::Generic && !pd.isConfigurable()) { + ok = false; + newLen = it->key() + 1; + break; + } + pd.type = PropertyDescriptor::Generic; + pd.value.tag = Value::_Undefined_Type; + pd.value.int_32 = freeList; + freeList = it->value; + bool brk = (it == begin); + SparseArrayNode *prev = it->previousNode(); + sparse->erase(it); + if (brk) + break; + it = prev; + } + } else { + PropertyDescriptor *it = values.data() + offset + values.size(); + const PropertyDescriptor *begin = values.constData() + offset + newLen; + while (--it >= begin) { + if (it->type != PropertyDescriptor::Generic && !it->isConfigurable()) { + ok = false; + newLen = it - values.data() + offset + 1; + break; + } + } + values.resize(newLen); + } + } else { + if (newLen >= 0x100000) + initSparse(); + } + setLengthUnchecked(newLen); + return ok; +} } } diff --git a/qv4array.h b/qv4array.h index 1957d2c802..c36d4a9ae7 100644 --- a/qv4array.h +++ b/qv4array.h @@ -430,27 +430,7 @@ public: void initSparse(); uint length() const { return len; } - bool setLength(uint l) { - if (lengthProperty && !lengthProperty->isWritable()) - return false; - setLengthUnchecked(l); - if (len >= 0x100000) - initSparse(); - if (sparse) { - SparseArrayNode *it = sparse->lowerBound(l); - while (it != sparse->end()) { - PropertyDescriptor &pd = values[it->value]; - pd.type = PropertyDescriptor::Generic; - pd.value.tag = Value::_Undefined_Type; - pd.value.int_32 = freeList; - freeList = it->value; - it = sparse->erase(it); - } - } else if (values.size() > (int)len){ - values.resize(len); - } - return true; - } + bool setLength(uint newLen); void setLengthProperty(PropertyDescriptor *pd) { lengthProperty = pd; } PropertyDescriptor *getLengthProperty() { return lengthProperty; } @@ -631,6 +611,7 @@ public: setLengthUnchecked(len - 1); } + SparseArrayNode *sparseLowerBound(uint idx) { return sparse ? sparse->lowerBound(idx) : 0; } SparseArrayNode *sparseBegin() { return sparse ? sparse->begin() : 0; } SparseArrayNode *sparseEnd() { return sparse ? sparse->end() : 0; } diff --git a/tests/TestExpectations b/tests/TestExpectations index 081d719aa3..349066af4b 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -825,17 +825,7 @@ S15.1.3.2_A5.3 failing 15.2.3.6-3-94 failing 15.2.3.6-4-108 failing 15.2.3.6-4-111 failing -15.2.3.6-4-116 failing -15.2.3.6-4-117 failing 15.2.3.6-4-163 failing -15.2.3.6-4-168 failing -15.2.3.6-4-169 failing -15.2.3.6-4-170 failing -15.2.3.6-4-172 failing -15.2.3.6-4-173 failing -15.2.3.6-4-174 failing -15.2.3.6-4-176 failing -15.2.3.6-4-177 failing 15.2.3.6-4-188 failing 15.2.3.6-4-189 failing 15.2.3.6-4-20 failing @@ -2985,11 +2975,3 @@ S15.4.4.13_A1_T2 failing 15.4.4.20-9-c-i-6 failing 15.4.4.21-8-b-iii-1-6 failing 15.4.4.22-8-b-iii-1-6 failing - -15.4.4.16-7-b-16 failing -15.4.4.17-7-b-16 failing -15.4.4.18-7-b-16 failing -15.4.4.19-8-b-16 failing -15.4.4.20-9-b-16 failing -15.4.4.21-9-b-29 failing -15.4.4.22-9-b-16 failing \ No newline at end of file -- cgit v1.2.3 From 73bcaf2318f20faca8a8a97f89ed2f8fb9fe12d4 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 22:01:01 +0100 Subject: Fix PropertyDescriptor::isSubset() This fixes a couple of test cases where an accessor gets reset to undefined. Change-Id: If37d67846a31bfcec5d285ad68337748a243b5ff Reviewed-by: Simon Hausmann --- qv4propertydescriptor.h | 4 ++-- tests/TestExpectations | 12 +----------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/qv4propertydescriptor.h b/qv4propertydescriptor.h index 8aa371651e..0f14a15318 100644 --- a/qv4propertydescriptor.h +++ b/qv4propertydescriptor.h @@ -136,9 +136,9 @@ struct PropertyDescriptor { if (type == Data && !value.sameValue(other->value)) return false; if (type == Accessor) { - if ((quintptr)get != 0x1 && get != other->get) + if (get != other->get) return false; - if ((quintptr)set != 0x1 && set != other->set) + if (set != other->set) return false; } return true; diff --git a/tests/TestExpectations b/tests/TestExpectations index 349066af4b..cb07b9784f 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -823,15 +823,11 @@ S15.1.3.2_A5.3 failing 15.2.3.6-3-66 failing 15.2.3.6-3-94-1 failing 15.2.3.6-3-94 failing -15.2.3.6-4-108 failing -15.2.3.6-4-111 failing 15.2.3.6-4-163 failing 15.2.3.6-4-188 failing 15.2.3.6-4-189 failing 15.2.3.6-4-20 failing 15.2.3.6-4-256 failing -15.2.3.6-4-266 failing -15.2.3.6-4-269 failing 15.2.3.6-4-291-1 failing 15.2.3.6-4-292-1 failing 15.2.3.6-4-293-2 failing @@ -846,13 +842,9 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-360-7 failing 15.2.3.6-4-41 failing 15.2.3.6-4-410 failing -15.2.3.6-4-463 failing 15.2.3.6-4-472 failing -15.2.3.6-4-481 failing 15.2.3.6-4-490 failing -15.2.3.6-4-498 failing 15.2.3.6-4-507 failing -15.2.3.6-4-516 failing 15.2.3.6-4-525 failing 15.2.3.6-4-543 failing 15.2.3.6-4-544 failing @@ -936,8 +928,6 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-244 failing 15.2.3.7-6-a-245 failing 15.2.3.7-6-a-25 failing -15.2.3.7-6-a-255 failing -15.2.3.7-6-a-258 failing 15.2.3.7-6-a-270 failing 15.2.3.7-6-a-271 failing 15.2.3.7-6-a-272 failing @@ -2974,4 +2964,4 @@ S15.4.4.13_A1_T2 failing 15.4.4.19-8-c-i-6 failing 15.4.4.20-9-c-i-6 failing 15.4.4.21-8-b-iii-1-6 failing -15.4.4.22-8-b-iii-1-6 failing +15.4.4.22-8-b-iii-1-6 failing \ No newline at end of file -- cgit v1.2.3 From e3ba3737081554488a4f1cd148b6e060384b1f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Mon, 14 Jan 2013 14:21:06 +0100 Subject: Remove qDebug. Change-Id: I3b7764ac5201de012e6b296cd4e562cffc762974 Reviewed-by: Simon Hausmann --- qv4string.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/qv4string.cpp b/qv4string.cpp index 997f1dd0f2..0f34fc0114 100644 --- a/qv4string.cpp +++ b/qv4string.cpp @@ -89,7 +89,6 @@ uint String::toUInt(bool *ok) const return _hashValue; double d = __qmljs_string_to_number(this); - qDebug() << "toUInt" << d; uint l = (uint)d; if (d == l) return l; -- cgit v1.2.3 From 516759a88ea8ba61fb0b0fde6117923c3c73e960 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 22:44:24 +0100 Subject: Fix memory leak Change-Id: I7fcebc671f9db66a25d486575563081b8ac5299f Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 817ac9616f..7acdde5356 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1010,7 +1010,7 @@ bool Codegen::visit(ArrayLiteral *ast) for (Elision *elision = ast->elision->next; elision; elision = elision->next) ++index; // ### the new string leaks - move(member(_block->TEMP(t), new QString("length")), _block->CONST(IR::NumberType, index + 1)); + move(member(_block->TEMP(t), _function->newString(QStringLiteral("length"))), _block->CONST(IR::NumberType, index + 1)); } _expr.code = _block->TEMP(t); return false; -- cgit v1.2.3 From 0d8deae65ed07b4a89d5a6b4a9f04201db07809a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 22:44:38 +0100 Subject: Fix sameValue() for integer vs double 0 Change-Id: Id56699a3e3624c644b14c6ece847a91da0ea7004 Reviewed-by: Simon Hausmann --- qmljs_value.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qmljs_value.cpp b/qmljs_value.cpp index 9ea4be1456..78c549fd84 100644 --- a/qmljs_value.cpp +++ b/qmljs_value.cpp @@ -84,6 +84,10 @@ bool Value::sameValue(Value other) { return true; if (isString() && other.isString()) return stringValue()->isEqualTo(other.stringValue()); + if (isInteger() && int_32 == 0 && other.dbl == 0) + return true; + if (dbl == 0 && other.isInteger() && other.int_32 == 0) + return true; return false; } -- cgit v1.2.3 From b30b131babfc177c2ca4fdf783ee9a539c84870a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 22:45:00 +0100 Subject: Smaller bug fixes to setting the length property for arrays Change-Id: I5f93d8e6648aad162bfafbcc558bd77e8d59b50f Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 4 +++- tests/TestExpectations | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 3ea7edec73..04107d0e31 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -483,9 +483,11 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property if (isArray && name->isEqualTo(ctx->engine->id_length)) { PropertyDescriptor *lp = array.getLengthProperty(); + if (desc->isEmpty() || desc->isSubset(lp)) + return true; if (!lp->isWritable() || desc->type == PropertyDescriptor::Accessor || desc->isConfigurable() || desc->isEnumerable()) goto reject; - bool succeeded = false; + bool succeeded = true; if (desc->type == PropertyDescriptor::Data) { bool ok; uint l = desc->value.asArrayLength(ctx, &ok); diff --git a/tests/TestExpectations b/tests/TestExpectations index cb07b9784f..f030ff549e 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -823,7 +823,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-3-66 failing 15.2.3.6-3-94-1 failing 15.2.3.6-3-94 failing -15.2.3.6-4-163 failing 15.2.3.6-4-188 failing 15.2.3.6-4-189 failing 15.2.3.6-4-20 failing -- cgit v1.2.3 From 8ff496b33d923f70daba0b87d42e7727a54e446d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Jan 2013 23:32:57 +0100 Subject: Some more fixes for defineOwnProperty Change-Id: If130677591bb7f655bcc5d35f1797ced08cd17f3 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 90 +++++++++++++++----------------------------------- qmljs_objects.h | 1 + tests/TestExpectations | 12 ------- 3 files changed, 27 insertions(+), 76 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 04107d0e31..eacf40bde8 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -518,72 +518,23 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, Property return true; } - // clause 5 - if (desc->isEmpty()) - return true; - - // clause 6 - if (desc->isSubset(current)) - return true; - - // clause 7 - if (!current->isConfigurable()) { - if (desc->isConfigurable()) - goto reject; - if (desc->enumberable != PropertyDescriptor::Undefined && desc->enumberable != current->enumberable) - goto reject; - } - - // clause 8 - if (desc->isGeneric()) - goto accept; - - // clause 9 - if (current->isData() != desc->isData()) { - // 9a - if (!current->isConfigurable()) - goto reject; - if (current->isData()) { - // 9b - current->type = PropertyDescriptor::Accessor; - current->writable = PropertyDescriptor::Undefined; - current->get = 0; - current->set = 0; - } else { - // 9c - current->type = PropertyDescriptor::Data; - current->writable = PropertyDescriptor::Disabled; - current->value = Value::undefinedValue(); - } - } else if (current->isData() && desc->isData()) { // clause 10 - if (!current->isConfigurable() && !current->isWritable()) { - if (desc->isWritable() || !current->value.sameValue(desc->value)) - goto reject; - } - } else { // clause 10 - assert(current->isAccessor() && desc->isAccessor()); - if (!current->isConfigurable()) { - if (((quintptr)desc->get > 0x1 && current->get != desc->get) || - ((quintptr)desc->set > 0x1 && current->set != desc->set)) - goto reject; - } - } - - accept: - - *current += *desc; - return true; - reject: - qDebug() << "___put__ rejected" << name->toQString(); - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); - return false; + return __defineOwnProperty__(ctx, current, desc); +reject: + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; } bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, PropertyDescriptor *desc) { + PropertyDescriptor *current; + + // 15.4.5.1, 4b + if (isArray && index >= array.length() && !array.getLengthProperty()->isWritable()) + goto reject; + // Clause 1 - PropertyDescriptor *current = __getOwnProperty__(ctx, index); + current = __getOwnProperty__(ctx, index); if (!current) { // clause 3 if (!extensible) @@ -595,6 +546,15 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, PropertyDe return true; } + return __defineOwnProperty__(ctx, current, desc); +reject: + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; +} + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, PropertyDescriptor *desc) +{ // clause 5 if (desc->isEmpty()) return true; @@ -640,8 +600,10 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, PropertyDe } else { // clause 10 assert(current->isAccessor() && desc->isAccessor()); if (!current->isConfigurable()) { - if (((quintptr)desc->get > 0x1 && current->get != desc->get) || - ((quintptr)desc->set > 0x1 && current->set != desc->set)) + if ((!current->get && (quintptr)desc->get > 0x1) || + (current->get && current->get != desc->get) || + (!current->set && (quintptr)desc->set > 0x1) || + (current->set && current->set != desc->set)) goto reject; } } @@ -651,12 +613,12 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, PropertyDe *current += *desc; return true; reject: - qDebug() << "___put__ rejected index=" << index; if (ctx->strictMode) __qmljs_throw_type_error(ctx); return false; } + bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, PropertyDescriptor *desc) { return __defineOwnProperty__(ctx, ctx->engine->identifier(name), desc); diff --git a/qmljs_objects.h b/qmljs_objects.h index 19fa492565..5a59f14db3 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -138,6 +138,7 @@ struct Object: Managed { virtual bool __hasProperty__(const ExecutionContext *ctx, uint index) const; virtual bool __delete__(ExecutionContext *ctx, String *name); virtual bool __delete__(ExecutionContext *ctx, uint index); + bool __defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, PropertyDescriptor *desc); virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc); bool __defineOwnProperty__(ExecutionContext *ctx, uint index, PropertyDescriptor *desc); bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, PropertyDescriptor *desc); diff --git a/tests/TestExpectations b/tests/TestExpectations index f030ff549e..1341803307 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -823,10 +823,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-3-66 failing 15.2.3.6-3-94-1 failing 15.2.3.6-3-94 failing -15.2.3.6-4-188 failing -15.2.3.6-4-189 failing -15.2.3.6-4-20 failing -15.2.3.6-4-256 failing 15.2.3.6-4-291-1 failing 15.2.3.6-4-292-1 failing 15.2.3.6-4-293-2 failing @@ -841,14 +837,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-360-7 failing 15.2.3.6-4-41 failing 15.2.3.6-4-410 failing -15.2.3.6-4-472 failing -15.2.3.6-4-490 failing -15.2.3.6-4-507 failing -15.2.3.6-4-525 failing -15.2.3.6-4-543 failing -15.2.3.6-4-544 failing -15.2.3.6-4-561 failing -15.2.3.6-4-562 failing 15.2.3.6-4-586 failing 15.2.3.6-4-593 failing 15.2.3.6-4-594 failing -- cgit v1.2.3 From 3f37ee017da75fc1f3a0c187dcac8aabd00c82ea Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 14 Jan 2013 19:32:54 +0100 Subject: Fix compiler warnings about signedness in comparisson. Change-Id: I1883d6f5f8c2bbedd07ba8791057eeb788b5b938 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 2 +- qv4array.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 8833f7ba57..f5853d9de0 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -558,7 +558,7 @@ Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) { uint idx = index.asArrayIndex(); if (object.isString() && idx < UINT_MAX) { - if (idx >= object.stringValue()->toQString().length()) + if (idx > INT_MAX || (int) idx >= object.stringValue()->toQString().length()) return Value::undefinedValue(); const QString s = object.stringValue()->toQString().mid(idx, 1); return Value::fromString(ctx, s); diff --git a/qv4array.cpp b/qv4array.cpp index a190c78633..dd34a7c464 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -526,7 +526,7 @@ Value Array::indexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *c return Value::fromDouble(n->key()); } } else { - if (endIndex > values.size()) + if ((int) endIndex > values.size()) endIndex = values.size(); PropertyDescriptor *pd = values.data() + offset; PropertyDescriptor *end = pd + endIndex; -- cgit v1.2.3 From a270ab6807e479d4189917b1d17476748da04fca Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Sun, 13 Jan 2013 22:17:26 +0100 Subject: Move common code from various ISel backends into a common base-class. Note: all warnings are due to backends that do not fully implement the required virtual methods. Or, rephrasing: these backends need work to get them up-to-speed/quality for all methods that do have warnings. Change-Id: Ib713f3d76832af42ebe893ad2896eec4bdd4bccb Reviewed-by: Simon Hausmann --- moth/qv4isel_moth.cpp | 681 ++++++++++++++++++++++++-------------------------- moth/qv4isel_moth_p.h | 36 ++- qv4isel_llvm.cpp | 235 +++++++++++++---- qv4isel_llvm_p.h | 81 ++++-- qv4isel_masm.cpp | 424 +++++++++++++------------------ qv4isel_masm_p.h | 40 ++- qv4isel_p.cpp | 161 +++++++++++- qv4isel_p.h | 46 +++- 8 files changed, 985 insertions(+), 719 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 6f06fe95cf..bfc04a39d9 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -191,6 +191,74 @@ private: int _pinnedCount; }; +typedef VM::Value (*ALUFunction)(const VM::Value, const VM::Value, VM::ExecutionContext*); +inline ALUFunction aluOpFunction(IR::AluOp op) +{ + switch (op) { + case IR::OpInvalid: + return 0; + case IR::OpIfTrue: + return 0; + case IR::OpNot: + return 0; + case IR::OpUMinus: + return 0; + case IR::OpUPlus: + return 0; + case IR::OpCompl: + return 0; + case IR::OpBitAnd: + return VM::__qmljs_bit_and; + case IR::OpBitOr: + return VM::__qmljs_bit_or; + case IR::OpBitXor: + return VM::__qmljs_bit_xor; + case IR::OpAdd: + return VM::__qmljs_add; + case IR::OpSub: + return VM::__qmljs_sub; + case IR::OpMul: + return VM::__qmljs_mul; + case IR::OpDiv: + return VM::__qmljs_div; + case IR::OpMod: + return VM::__qmljs_mod; + case IR::OpLShift: + return VM::__qmljs_shl; + case IR::OpRShift: + return VM::__qmljs_shr; + case IR::OpURShift: + return VM::__qmljs_ushr; + case IR::OpGt: + return VM::__qmljs_gt; + case IR::OpLt: + return VM::__qmljs_lt; + case IR::OpGe: + return VM::__qmljs_ge; + case IR::OpLe: + return VM::__qmljs_le; + case IR::OpEqual: + return VM::__qmljs_eq; + case IR::OpNotEqual: + return VM::__qmljs_ne; + case IR::OpStrictEqual: + return VM::__qmljs_se; + case IR::OpStrictNotEqual: + return VM::__qmljs_sne; + case IR::OpInstanceof: + return VM::__qmljs_instanceof; + case IR::OpIn: + return VM::__qmljs_in; + case IR::OpAnd: + return 0; + case IR::OpOr: + return 0; + default: + assert(!"Unknown AluOp"); + return 0; + } +}; + } // anonymous namespace InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module) @@ -269,8 +337,10 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) qSwap(ccode, _ccode); } -void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempIndex) +void InstructionSelection::callActivationProperty(IR::Call *c, IR::Temp *temp) { + const int targetTempIndex = temp ? temp->index : scratchTempIndex(); +; IR::Name *baseName = c->base->asName(); Q_ASSERT(baseName); @@ -465,8 +535,9 @@ void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempInd } } -void InstructionSelection::callValue(IR::Call *c, int targetTempIndex) +void InstructionSelection::callValue(IR::Call *c, IR::Temp *temp) { + const int targetTempIndex = temp ? temp->index : scratchTempIndex(); IR::Temp *t = c->base->asTemp(); Q_ASSERT(t); @@ -477,8 +548,9 @@ void InstructionSelection::callValue(IR::Call *c, int targetTempIndex) addInstruction(call); } -void InstructionSelection::callProperty(IR::Call *c, int targetTempIndex) +void InstructionSelection::callProperty(IR::Call *c, IR::Temp *temp) { + const int targetTempIndex = temp ? temp->index : scratchTempIndex(); IR::Member *m = c->base->asMember(); Q_ASSERT(m); @@ -491,35 +563,257 @@ void InstructionSelection::callProperty(IR::Call *c, int targetTempIndex) addInstruction(call); } -void InstructionSelection::construct(IR::New *ctor, int targetTempIndex) -{ - if (IR::Name *baseName = ctor->base->asName()) { - Instruction::CreateActivationProperty create; - create.name = engine()->newString(*baseName->id); - prepareCallArgs(ctor->args, create.argc, create.args); - create.targetTempIndex = targetTempIndex; - addInstruction(create); - } else if (IR::Member *member = ctor->base->asMember()) { - IR::Temp *base = member->base->asTemp(); - assert(base != 0); - - Instruction::CreateProperty create; - create.base = base->index; - create.name = engine()->newString(*member->name); - prepareCallArgs(ctor->args, create.argc, create.args); - create.targetTempIndex = targetTempIndex; - addInstruction(create); - } else if (IR::Temp *baseTemp = ctor->base->asTemp()) { - Instruction::CreateValue create; - create.func = baseTemp->index; - prepareCallArgs(ctor->args, create.argc, create.args); - create.targetTempIndex = targetTempIndex; - addInstruction(create); +void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) +{ + Instruction::CreateActivationProperty create; + create.name = engine()->newString(*call->base->asName()->id); + prepareCallArgs(call->args, create.argc, create.args); + create.targetTempIndex = result->index; + addInstruction(create); +} + +void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) +{ + IR::Member *member = call->base->asMember(); + assert(member != 0); + assert(member->base->asTemp() != 0); + + Instruction::CreateProperty create; + create.base = member->base->asTemp()->index; + create.name = engine()->newString(*member->name); + prepareCallArgs(call->args, create.argc, create.args); + create.targetTempIndex = result->index; + addInstruction(create); +} + +void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) +{ + Instruction::CreateValue create; + create.func = call->base->asTemp()->index; + prepareCallArgs(call->args, create.argc, create.args); + create.targetTempIndex = result->index; + addInstruction(create); +} + +void InstructionSelection::loadThisObject(IR::Temp *temp) +{ + Instruction::LoadThis load; + load.targetTempIndex = temp->index; + addInstruction(load); +} + +void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) +{ + assert(sourceConst); + + Instruction::LoadValue load; + load.targetTempIndex = targetTemp->index; + load.value = convertToValue(sourceConst); + addInstruction(load); +} + +void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) +{ + Instruction::LoadValue load; + load.value = VM::Value::fromString(engine()->newString(str)); + load.targetTempIndex = targetTemp->index; + addInstruction(load); +} + +void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) +{ + Instruction::LoadValue load; + load.value = VM::Value::fromObject(engine()->newRegExpObject( + *sourceRegexp->value, + sourceRegexp->flags)); + load.targetTempIndex = targetTemp->index; + addInstruction(load); +} + +void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) +{ + Instruction::LoadName load; + load.name = engine()->newString(name); + load.targetTempIndex = temp->index; + addInstruction(load); +} + +void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) +{ + Instruction::StoreName store; + store.sourceIsTemp = toValueOrTemp(source, store.source); + store.name = engine()->newString(targetName); + addInstruction(store); +} + +void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) +{ + VM::Function *vmFunc = vmFunction(closure->value); + assert(vmFunc); + Instruction::LoadClosure load; + load.value = vmFunc; + load.targetTempIndex = target->index; + addInstruction(load); +} + +void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::Temp *target) +{ + Instruction::LoadProperty load; + load.baseTemp = base->index; + load.name = engine()->newString(name); + load.targetTempIndex = target->index; + addInstruction(load); +} + +void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +{ + Instruction::StoreProperty store; + store.baseTemp = targetBase->index; + store.name = engine()->newString(targetName); + store.sourceIsTemp = toValueOrTemp(source, store.source); + addInstruction(store); +} + +void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) +{ + Instruction::LoadElement load; + load.base = base->index; + load.index = index->index; + load.targetTempIndex = target->index; + addInstruction(load); +} + +void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) +{ + Instruction::StoreElement store; + store.base = targetBase->index; + store.index = targetIndex->index; + store.sourceIsTemp = toValueOrTemp(source, store.source); + addInstruction(store); +} + +void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + Instruction::MoveTemp move; + move.fromTempIndex = sourceTemp->index; + move.toTempIndex = targetTemp->index; + addInstruction(move); +} + +void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + VM::Value (*op)(const VM::Value value, VM::ExecutionContext *ctx) = 0; + switch (oper) { + case IR::OpIfTrue: assert(!"unreachable"); break; + case IR::OpNot: op = VM::__qmljs_not; break; + case IR::OpUMinus: op = VM::__qmljs_uminus; break; + case IR::OpUPlus: op = VM::__qmljs_uplus; break; + case IR::OpCompl: op = VM::__qmljs_compl; break; + case IR::OpIncrement: op = VM::__qmljs_increment; break; + case IR::OpDecrement: op = VM::__qmljs_decrement; break; + default: assert(!"unreachable"); break; + } // switch + + if (op) { + Instruction::Unop unop; + unop.alu = op; + unop.e = sourceTemp->index; + unop.targetTempIndex = targetTemp->index; + addInstruction(unop); } else { - qWarning(" NEW"); + qWarning(" UNOP1"); } } +void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) +{ + Instruction::Binop binop; + binop.alu = aluOpFunction(oper); + binop.lhsIsTemp = toValueOrTemp(leftSource, binop.lhs); + binop.rhsIsTemp = toValueOrTemp(rightSource, binop.rhs); + binop.targetTempIndex = target->index; + addInstruction(binop); +} + +void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) +{ + void (*op)(VM::Value value, VM::String *name, VM::ExecutionContext *ctx) = 0; + switch (oper) { + case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_name; break; + case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_name; break; + case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_name; break; + case IR::OpAdd: op = VM::__qmljs_inplace_add_name; break; + case IR::OpSub: op = VM::__qmljs_inplace_sub_name; break; + case IR::OpMul: op = VM::__qmljs_inplace_mul_name; break; + case IR::OpDiv: op = VM::__qmljs_inplace_div_name; break; + case IR::OpMod: op = VM::__qmljs_inplace_mod_name; break; + case IR::OpLShift: op = VM::__qmljs_inplace_shl_name; break; + case IR::OpRShift: op = VM::__qmljs_inplace_shr_name; break; + case IR::OpURShift: op = VM::__qmljs_inplace_ushr_name; break; + default: break; + } + + if (op) { + Instruction::InplaceNameOp ieo; + ieo.alu = op; + ieo.targetName = engine()->newString(targetName); + ieo.sourceIsTemp = toValueOrTemp(sourceExpr, ieo.source); + addInstruction(ieo); + } +} + +void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) +{ + void (*op)(VM::Value base, VM::Value index, VM::Value value, VM::ExecutionContext *ctx) = 0; + switch (oper) { + case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_element; break; + case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_element; break; + case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_element; break; + case IR::OpAdd: op = VM::__qmljs_inplace_add_element; break; + case IR::OpSub: op = VM::__qmljs_inplace_sub_element; break; + case IR::OpMul: op = VM::__qmljs_inplace_mul_element; break; + case IR::OpDiv: op = VM::__qmljs_inplace_div_element; break; + case IR::OpMod: op = VM::__qmljs_inplace_mod_element; break; + case IR::OpLShift: op = VM::__qmljs_inplace_shl_element; break; + case IR::OpRShift: op = VM::__qmljs_inplace_shr_element; break; + case IR::OpURShift: op = VM::__qmljs_inplace_ushr_element; break; + default: break; + } + + Instruction::InplaceElementOp ieo; + ieo.alu = op; + ieo.targetBase = targetBaseTemp->index; + ieo.targetIndex = targetIndexTemp->index; + ieo.sourceIsTemp = toValueOrTemp(sourceExpr, ieo.source); + addInstruction(ieo); +} + +void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +{ + void (*op)(VM::Value value, VM::Value base, VM::String *name, VM::ExecutionContext *ctx) = 0; + switch (oper) { + case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_member; break; + case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_member; break; + case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_member; break; + case IR::OpAdd: op = VM::__qmljs_inplace_add_member; break; + case IR::OpSub: op = VM::__qmljs_inplace_sub_member; break; + case IR::OpMul: op = VM::__qmljs_inplace_mul_member; break; + case IR::OpDiv: op = VM::__qmljs_inplace_div_member; break; + case IR::OpMod: op = VM::__qmljs_inplace_mod_member; break; + case IR::OpLShift: op = VM::__qmljs_inplace_shl_member; break; + case IR::OpRShift: op = VM::__qmljs_inplace_shr_member; break; + case IR::OpURShift: op = VM::__qmljs_inplace_ushr_member; break; + default: break; + } + + Instruction::InplaceMemberOp imo; + imo.alu = op; + imo.targetBase = targetBase->index; + imo.targetMember = engine()->newString(targetName); + imo.sourceIsTemp = toValueOrTemp(source, imo.source); + addInstruction(imo); +} + void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 &args) { argc = 0; @@ -557,337 +851,6 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint } } -void InstructionSelection::visitExp(IR::Exp *s) -{ - if (IR::Call *c = s->expr->asCall()) { - // These are calls where the result is ignored. - const int targetTempIndex = scratchTempIndex(); - if (c->base->asName()) { - callActivationProperty(c, targetTempIndex); - } else if (c->base->asTemp()) { - callValue(c, targetTempIndex); - } else if (c->base->asMember()) { - callProperty(c, targetTempIndex); - } else { - Q_UNREACHABLE(); - } - } else { - Q_UNREACHABLE(); - } -} - -void InstructionSelection::visitEnter(IR::Enter *) -{ - qWarning("%s", __PRETTY_FUNCTION__); - Q_UNREACHABLE(); -} - -void InstructionSelection::visitLeave(IR::Leave *) -{ - qWarning("%s", __PRETTY_FUNCTION__); - Q_UNREACHABLE(); -} - -typedef VM::Value (*ALUFunction)(const VM::Value, const VM::Value, VM::ExecutionContext*); -static inline ALUFunction aluOpFunction(IR::AluOp op) -{ - switch (op) { - case IR::OpInvalid: - return 0; - case IR::OpIfTrue: - return 0; - case IR::OpNot: - return 0; - case IR::OpUMinus: - return 0; - case IR::OpUPlus: - return 0; - case IR::OpCompl: - return 0; - case IR::OpBitAnd: - return VM::__qmljs_bit_and; - case IR::OpBitOr: - return VM::__qmljs_bit_or; - case IR::OpBitXor: - return VM::__qmljs_bit_xor; - case IR::OpAdd: - return VM::__qmljs_add; - case IR::OpSub: - return VM::__qmljs_sub; - case IR::OpMul: - return VM::__qmljs_mul; - case IR::OpDiv: - return VM::__qmljs_div; - case IR::OpMod: - return VM::__qmljs_mod; - case IR::OpLShift: - return VM::__qmljs_shl; - case IR::OpRShift: - return VM::__qmljs_shr; - case IR::OpURShift: - return VM::__qmljs_ushr; - case IR::OpGt: - return VM::__qmljs_gt; - case IR::OpLt: - return VM::__qmljs_lt; - case IR::OpGe: - return VM::__qmljs_ge; - case IR::OpLe: - return VM::__qmljs_le; - case IR::OpEqual: - return VM::__qmljs_eq; - case IR::OpNotEqual: - return VM::__qmljs_ne; - case IR::OpStrictEqual: - return VM::__qmljs_se; - case IR::OpStrictNotEqual: - return VM::__qmljs_sne; - case IR::OpInstanceof: - return VM::__qmljs_instanceof; - case IR::OpIn: - return VM::__qmljs_in; - case IR::OpAnd: - return 0; - case IR::OpOr: - return 0; - default: - assert(!"Unknown AluOp"); - return 0; - } -}; - -void InstructionSelection::visitMove(IR::Move *s) -{ - if (IR::Temp *t = s->target->asTemp()) { - const int targetTempIndex = t->index; - // Check what kind of load it is, and generate the instruction for that. - // The store to the temp (the target) is done afterwards. - if (IR::Name *n = s->source->asName()) { - Q_UNUSED(n); - if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. - Instruction::LoadThis load; - load.targetTempIndex = targetTempIndex; - addInstruction(load); - } else { - Instruction::LoadName load; - load.name = engine()->newString(*n->id); - load.targetTempIndex = targetTempIndex; - addInstruction(load); - } - } else if (s->source->asTemp() || s->source->asConst()) { - if (s->op == IR::OpInvalid) { - if (IR::Temp *t2 = s->source->asTemp()) { - Instruction::MoveTemp move; - move.fromTempIndex = t2->index; - move.toTempIndex = targetTempIndex; - addInstruction(move); - } else { - IR::Const *c = s->source->asConst(); - assert(c); - Instruction::LoadValue load; - load.targetTempIndex = targetTempIndex; - load.value = convertToValue(c); - addInstruction(load); - } - } else { - Instruction::Binop binop; - binop.alu = aluOpFunction(s->op); - binop.lhsIsTemp = toValueOrTemp(t, binop.lhs); - binop.rhsIsTemp = toValueOrTemp(s->source, binop.rhs); - binop.targetTempIndex = targetTempIndex; - addInstruction(binop); - } - } else if (IR::String *str = s->source->asString()) { - Instruction::LoadValue load; - load.value = VM::Value::fromString(engine()->newString(*str->value)); - load.targetTempIndex = targetTempIndex; - addInstruction(load); - } else if (IR::Closure *clos = s->source->asClosure()) { - VM::Function *vmFunc = vmFunction(clos->value); - assert(vmFunc); - Instruction::LoadClosure load; - load.value = vmFunc; - load.targetTempIndex = targetTempIndex; - addInstruction(load); - } else if (IR::New *ctor = s->source->asNew()) { - construct(ctor, targetTempIndex); - } else if (IR::Member *m = s->source->asMember()) { - if (IR::Temp *base = m->base->asTemp()) { - Instruction::LoadProperty load; - load.baseTemp = base->index; - load.name = engine()->newString(*m->name); - load.targetTempIndex = targetTempIndex; - addInstruction(load); - } else { - qWarning(" MEMBER"); - } - } else if (IR::Subscript *ss = s->source->asSubscript()) { - Instruction::LoadElement load; - load.base = ss->base->asTemp()->index; - load.index = ss->index->asTemp()->index; - load.targetTempIndex = targetTempIndex; - addInstruction(load); - } else if (IR::Unop *u = s->source->asUnop()) { - if (IR::Temp *e = u->expr->asTemp()) { - VM::Value (*op)(const VM::Value value, VM::ExecutionContext *ctx) = 0; - switch (u->op) { - case IR::OpIfTrue: assert(!"unreachable"); break; - case IR::OpNot: op = VM::__qmljs_not; break; - case IR::OpUMinus: op = VM::__qmljs_uminus; break; - case IR::OpUPlus: op = VM::__qmljs_uplus; break; - case IR::OpCompl: op = VM::__qmljs_compl; break; - case IR::OpIncrement: op = VM::__qmljs_increment; break; - case IR::OpDecrement: op = VM::__qmljs_decrement; break; - default: assert(!"unreachable"); break; - } // switch - - if (op) { - Instruction::Unop unop; - unop.alu = op; - unop.e = e->index; - unop.targetTempIndex = targetTempIndex; - addInstruction(unop); - } else { - qWarning(" UNOP1"); - } - } else { - qWarning(" UNOP2"); - s->dump(qout, IR::Stmt::MIR); - qout << endl; - } - } else if (IR::Binop *b = s->source->asBinop()) { - Instruction::Binop binop; - binop.alu = aluOpFunction(b->op); - binop.lhsIsTemp = toValueOrTemp(b->left, binop.lhs); - binop.rhsIsTemp = toValueOrTemp(b->right, binop.rhs); - binop.targetTempIndex = targetTempIndex; - addInstruction(binop); - } else if (IR::Call *c = s->source->asCall()) { - if (c->base->asName()) { - callActivationProperty(c, targetTempIndex); - } else if (c->base->asMember()) { - callProperty(c, targetTempIndex); - } else if (c->base->asTemp()) { - callValue(c, targetTempIndex); - } else { - Q_UNREACHABLE(); - } - } - return; - } else if (IR::Name *n = s->target->asName()) { - if (s->source->asTemp() || s->source->asConst()) { - void (*op)(VM::Value value, VM::String *name, VM::ExecutionContext *ctx) = 0; - switch (s->op) { - case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_name; break; - case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_name; break; - case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_name; break; - case IR::OpAdd: op = VM::__qmljs_inplace_add_name; break; - case IR::OpSub: op = VM::__qmljs_inplace_sub_name; break; - case IR::OpMul: op = VM::__qmljs_inplace_mul_name; break; - case IR::OpDiv: op = VM::__qmljs_inplace_div_name; break; - case IR::OpMod: op = VM::__qmljs_inplace_mod_name; break; - case IR::OpLShift: op = VM::__qmljs_inplace_shl_name; break; - case IR::OpRShift: op = VM::__qmljs_inplace_shr_name; break; - case IR::OpURShift: op = VM::__qmljs_inplace_ushr_name; break; - default: break; - } - - if (op) { - Instruction::InplaceNameOp ieo; - ieo.alu = op; - ieo.targetName = engine()->newString(*n->id); - ieo.sourceIsTemp = toValueOrTemp(s->source, ieo.source); - addInstruction(ieo); - return; - } else if (s->op == IR::OpInvalid) { - Instruction::StoreName store; - store.sourceIsTemp = toValueOrTemp(s->source, store.source); - store.name = engine()->newString(*n->id); - addInstruction(store); - return; - } - } - qWarning("NAME"); - } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (s->source->asTemp() || s->source->asConst()) { - void (*op)(VM::Value base, VM::Value index, VM::Value value, VM::ExecutionContext *ctx) = 0; - switch (s->op) { - case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_element; break; - case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_element; break; - case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_element; break; - case IR::OpAdd: op = VM::__qmljs_inplace_add_element; break; - case IR::OpSub: op = VM::__qmljs_inplace_sub_element; break; - case IR::OpMul: op = VM::__qmljs_inplace_mul_element; break; - case IR::OpDiv: op = VM::__qmljs_inplace_div_element; break; - case IR::OpMod: op = VM::__qmljs_inplace_mod_element; break; - case IR::OpLShift: op = VM::__qmljs_inplace_shl_element; break; - case IR::OpRShift: op = VM::__qmljs_inplace_shr_element; break; - case IR::OpURShift: op = VM::__qmljs_inplace_ushr_element; break; - default: break; - } - - if (op) { - Instruction::InplaceElementOp ieo; - ieo.alu = op; - ieo.targetBase = ss->base->asTemp()->index; - ieo.targetIndex = ss->index->asTemp()->index; - ieo.sourceIsTemp = toValueOrTemp(s->source, ieo.source); - addInstruction(ieo); - return; - } else if (s->op == IR::OpInvalid) { - Instruction::StoreElement store; - store.base = ss->base->asTemp()->index; - store.index = ss->index->asTemp()->index; - store.sourceIsTemp = toValueOrTemp(s->source, store.source); - addInstruction(store); - return; - } - } - qWarning("SUBSCRIPT"); - } else if (IR::Member *m = s->target->asMember()) { - if (s->source->asTemp() || s->source->asConst()) { - void (*op)(VM::Value value, VM::Value base, VM::String *name, VM::ExecutionContext *ctx) = 0; - switch (s->op) { - case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_member; break; - case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_member; break; - case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_member; break; - case IR::OpAdd: op = VM::__qmljs_inplace_add_member; break; - case IR::OpSub: op = VM::__qmljs_inplace_sub_member; break; - case IR::OpMul: op = VM::__qmljs_inplace_mul_member; break; - case IR::OpDiv: op = VM::__qmljs_inplace_div_member; break; - case IR::OpMod: op = VM::__qmljs_inplace_mod_member; break; - case IR::OpLShift: op = VM::__qmljs_inplace_shl_member; break; - case IR::OpRShift: op = VM::__qmljs_inplace_shr_member; break; - case IR::OpURShift: op = VM::__qmljs_inplace_ushr_member; break; - default: break; - } - - if (op) { - Instruction::InplaceMemberOp imo; - imo.alu = op; - imo.targetBase = m->base->asTemp()->index; - imo.targetMember = engine()->newString(*m->name); - imo.sourceIsTemp = toValueOrTemp(s->source, imo.source); - addInstruction(imo); - return; - } else if (s->op == IR::OpInvalid) { - Instruction::StoreProperty store; - store.baseTemp = m->base->asTemp()->index; - store.name = engine()->newString(*m->name); - store.sourceIsTemp = toValueOrTemp(s->source, store.source); - addInstruction(store); - return; - } - } - qWarning("MEMBER"); - } - - Q_UNIMPLEMENTED(); - s->dump(qout, IR::Stmt::MIR); - qout << endl; - Q_UNREACHABLE(); -} - void InstructionSelection::visitJump(IR::Jump *s) { Instruction::Jump jump; diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index dece41323f..0456394f7a 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -9,7 +9,9 @@ namespace QQmlJS { namespace Moth { -class InstructionSelection : public IR::StmtVisitor, public EvalInstructionSelection +class InstructionSelection: + public IR::InstructionSelection, + public EvalInstructionSelection { public: InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); @@ -18,14 +20,34 @@ public: virtual void run(VM::Function *vmFunction, IR::Function *function); protected: - virtual void visitExp(IR::Exp *); - virtual void visitEnter(IR::Enter *); - virtual void visitLeave(IR::Leave *); - virtual void visitMove(IR::Move *); virtual void visitJump(IR::Jump *); virtual void visitCJump(IR::CJump *); virtual void visitRet(IR::Ret *); + virtual void callActivationProperty(IR::Call *c, IR::Temp *temp); + virtual void callValue(IR::Call *c, IR::Temp *temp); + virtual void callProperty(IR::Call *c, IR::Temp *temp); + virtual void constructActivationProperty(IR::New *call, IR::Temp *result); + virtual void constructProperty(IR::New *call, IR::Temp *result); + virtual void constructValue(IR::New *call, IR::Temp *result); + virtual void loadThisObject(IR::Temp *temp); + virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp); + virtual void loadString(const QString &str, IR::Temp *targetTemp); + virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); + virtual void getActivationProperty(const QString &name, IR::Temp *temp); + virtual void setActivationProperty(IR::Expr *source, const QString &targetName); + virtual void initClosure(IR::Closure *closure, IR::Temp *target); + virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); + virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target); + virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); + virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); + virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); + virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + private: struct Instruction { #define MOTH_INSTR_DATA_TYPEDEF(I, FMT) typedef InstrData I; @@ -36,10 +58,6 @@ private: }; void simpleMove(IR::Move *); - void callActivationProperty(IR::Call *c, int targetTempIndex); - void callValue(IR::Call *c, int targetTempIndex); - void callProperty(IR::Call *c, int targetTempIndex); - void construct(IR::New *ctor, int targetTempIndex); void prepareCallArgs(IR::ExprList *, quint32 &, quint32 &); int outgoingArgumentTempStart() const { return _function->tempCount; } diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index eea515cdea..3d95491bf3 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -98,7 +98,7 @@ int compileWithLLVM(IR::Module *module, const QString &fileName, LLVMOutputType //---- llvm::InitializeNativeTarget(); - LLVMInstructionSelection llvmIsel(llvm::getGlobalContext()); + LLVM::InstructionSelection llvmIsel(llvm::getGlobalContext()); const QString moduleName = QFileInfo(fileName).fileName(); llvm::StringRef moduleId(moduleName.toUtf8().constData()); @@ -230,12 +230,13 @@ int compileWithLLVM(IR::Module *module, const QString &fileName, LLVMOutputType } // QQmlJS using namespace QQmlJS; +using namespace QQmlJS::LLVM; namespace { QTextStream qerr(stderr, QIODevice::WriteOnly); } -LLVMInstructionSelection::LLVMInstructionSelection(llvm::LLVMContext &context) +InstructionSelection::InstructionSelection(llvm::LLVMContext &context) : llvm::IRBuilder<>(context) , _llvmModule(0) , _llvmFunction(0) @@ -252,7 +253,7 @@ LLVMInstructionSelection::LLVMInstructionSelection(llvm::LLVMContext &context) { } -void LLVMInstructionSelection::buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm) +void InstructionSelection::buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm) { qSwap(_llvmModule, llvmModule); qSwap(_fpm, fpm); @@ -297,7 +298,145 @@ void LLVMInstructionSelection::buildLLVMModule(IR::Module *module, llvm::Module qSwap(_llvmModule, llvmModule); } -llvm::Function *LLVMInstructionSelection::getLLVMFunction(IR::Function *function) +void InstructionSelection::callActivationProperty(IR::Call *c, IR::Temp *temp) +{ + // TODO: implement instead of visitExp + Q_UNREACHABLE(); +} + +void InstructionSelection::callValue(IR::Call *c, IR::Temp *temp) +{ + // TODO: implement instead of visitExp + Q_UNREACHABLE(); +} + +void InstructionSelection::callProperty(IR::Call *c, IR::Temp *temp) +{ + // TODO: implement instead of visitExp + Q_UNREACHABLE(); +} + +void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::loadThisObject(IR::Temp *temp) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::loadConst(IR::Const *con, IR::Temp *temp) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::Temp *target) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +{ + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +llvm::Function *InstructionSelection::getLLVMFunction(IR::Function *function) { llvm::Function *&f = _functionMap[function]; if (! f) { @@ -314,7 +453,7 @@ llvm::Function *LLVMInstructionSelection::getLLVMFunction(IR::Function *function return f; } -llvm::Function *LLVMInstructionSelection::compileLLVMFunction(IR::Function *function) +llvm::Function *InstructionSelection::compileLLVMFunction(IR::Function *function) { llvm::Function *llvmFunction = getLLVMFunction(function); @@ -375,7 +514,7 @@ llvm::Function *LLVMInstructionSelection::compileLLVMFunction(IR::Function *func return llvmFunction; } -llvm::BasicBlock *LLVMInstructionSelection::getLLVMBasicBlock(IR::BasicBlock *block) +llvm::BasicBlock *InstructionSelection::getLLVMBasicBlock(IR::BasicBlock *block) { llvm::BasicBlock *&llvmBlock = _blockMap[block]; if (! llvmBlock) @@ -384,7 +523,7 @@ llvm::BasicBlock *LLVMInstructionSelection::getLLVMBasicBlock(IR::BasicBlock *bl return llvmBlock; } -llvm::Value *LLVMInstructionSelection::getLLVMValue(IR::Expr *expr) +llvm::Value *InstructionSelection::getLLVMValue(IR::Expr *expr) { llvm::Value *llvmValue = 0; if (expr) { @@ -400,7 +539,7 @@ llvm::Value *LLVMInstructionSelection::getLLVMValue(IR::Expr *expr) return llvmValue; } -llvm::Value *LLVMInstructionSelection::getLLVMTempReference(IR::Expr *expr) +llvm::Value *InstructionSelection::getLLVMTempReference(IR::Expr *expr) { if (IR::Temp *t = expr->asTemp()) return getLLVMTemp(t); @@ -410,7 +549,7 @@ llvm::Value *LLVMInstructionSelection::getLLVMTempReference(IR::Expr *expr) return addr; } -llvm::Value *LLVMInstructionSelection::getLLVMCondition(IR::Expr *expr) +llvm::Value *InstructionSelection::getLLVMCondition(IR::Expr *expr) { llvm::Value *value = 0; if (IR::Temp *t = expr->asTemp()) { @@ -432,7 +571,7 @@ llvm::Value *LLVMInstructionSelection::getLLVMCondition(IR::Expr *expr) value); } -llvm::Value *LLVMInstructionSelection::getLLVMTemp(IR::Temp *temp) +llvm::Value *InstructionSelection::getLLVMTemp(IR::Temp *temp) { if (temp->index < 0) { const int index = -temp->index -1; @@ -443,7 +582,7 @@ llvm::Value *LLVMInstructionSelection::getLLVMTemp(IR::Temp *temp) return _tempMap[temp->index]; } -llvm::Value *LLVMInstructionSelection::getStringPtr(const QString &s) +llvm::Value *InstructionSelection::getStringPtr(const QString &s) { llvm::Value *&value = _stringMap[s]; if (! value) { @@ -454,7 +593,7 @@ llvm::Value *LLVMInstructionSelection::getStringPtr(const QString &s) return value; } -llvm::Value *LLVMInstructionSelection::getIdentifier(const QString &s) +llvm::Value *InstructionSelection::getIdentifier(const QString &s) { llvm::Value *str = getStringPtr(s); llvm::Value *id = CreateCall2(_llvmModule->getFunction("__qmljs_identifier_from_utf8"), @@ -462,22 +601,12 @@ llvm::Value *LLVMInstructionSelection::getIdentifier(const QString &s) return id; } -void LLVMInstructionSelection::visitExp(IR::Exp *s) +void InstructionSelection::visitExp(IR::Exp *s) { getLLVMValue(s->expr); } -void LLVMInstructionSelection::visitEnter(IR::Enter *) -{ - Q_UNREACHABLE(); -} - -void LLVMInstructionSelection::visitLeave(IR::Leave *) -{ - Q_UNREACHABLE(); -} - -void LLVMInstructionSelection::genMoveSubscript(IR::Move *s) +void InstructionSelection::genMoveSubscript(IR::Move *s) { IR::Subscript *subscript = s->target->asSubscript(); llvm::Value *base = getLLVMTempReference(subscript->base); @@ -487,7 +616,7 @@ void LLVMInstructionSelection::genMoveSubscript(IR::Move *s) _llvmFunction->arg_begin(), base, index, source); } -void LLVMInstructionSelection::genMoveMember(IR::Move *s) +void InstructionSelection::genMoveMember(IR::Move *s) { IR::Member *m = s->target->asMember(); llvm::Value *base = getLLVMTempReference(m->base); @@ -497,7 +626,7 @@ void LLVMInstructionSelection::genMoveMember(IR::Move *s) _llvmFunction->arg_begin(), base, name, source); } -void LLVMInstructionSelection::visitMove(IR::Move *s) +void InstructionSelection::visitMove(IR::Move *s) { if (s->op == IR::OpInvalid) { if (s->target->asSubscript()) { @@ -647,19 +776,19 @@ void LLVMInstructionSelection::visitMove(IR::Move *s) return; } -void LLVMInstructionSelection::visitJump(IR::Jump *s) +void InstructionSelection::visitJump(IR::Jump *s) { CreateBr(getLLVMBasicBlock(s->target)); } -void LLVMInstructionSelection::visitCJump(IR::CJump *s) +void InstructionSelection::visitCJump(IR::CJump *s) { CreateCondBr(getLLVMCondition(s->cond), getLLVMBasicBlock(s->iftrue), getLLVMBasicBlock(s->iffalse)); } -void LLVMInstructionSelection::visitRet(IR::Ret *s) +void InstructionSelection::visitRet(IR::Ret *s) { IR::Temp *t = s->expr->asTemp(); assert(t != 0); @@ -669,14 +798,14 @@ void LLVMInstructionSelection::visitRet(IR::Ret *s) CreateRetVoid(); } -void LLVMInstructionSelection::visitConst(IR::Const *e) +void InstructionSelection::visitConst(IR::Const *e) { llvm::Value *tmp = createValue(e); _llvmValue = CreateLoad(tmp); } -void LLVMInstructionSelection::visitString(IR::String *e) +void InstructionSelection::visitString(IR::String *e) { llvm::Value *tmp = newLLVMTemp(_valueTy); CreateCall3(_llvmModule->getFunction("__qmljs_llvm_init_string"), @@ -685,7 +814,7 @@ void LLVMInstructionSelection::visitString(IR::String *e) _llvmValue = CreateLoad(tmp); } -void LLVMInstructionSelection::visitRegExp(IR::RegExp *e) +void InstructionSelection::visitRegExp(IR::RegExp *e) { e->dump(qerr); qerr << endl; @@ -693,7 +822,7 @@ void LLVMInstructionSelection::visitRegExp(IR::RegExp *e) _llvmValue = llvm::Constant::getNullValue(_valueTy); } -void LLVMInstructionSelection::visitName(IR::Name *e) +void InstructionSelection::visitName(IR::Name *e) { llvm::Value *result = newLLVMTemp(_valueTy); @@ -709,14 +838,14 @@ void LLVMInstructionSelection::visitName(IR::Name *e) } -void LLVMInstructionSelection::visitTemp(IR::Temp *e) +void InstructionSelection::visitTemp(IR::Temp *e) { if (llvm::Value *t = getLLVMTemp(e)) { _llvmValue = CreateLoad(t); } } -void LLVMInstructionSelection::visitClosure(IR::Closure *e) +void InstructionSelection::visitClosure(IR::Closure *e) { llvm::Value *tmp = newLLVMTemp(_valueTy); llvm::Value *clos = getLLVMFunction(e->value); @@ -726,21 +855,21 @@ void LLVMInstructionSelection::visitClosure(IR::Closure *e) _llvmValue = CreateLoad(tmp); } -void LLVMInstructionSelection::visitUnop(IR::Unop *e) +void InstructionSelection::visitUnop(IR::Unop *e) { llvm::Value *result = newLLVMTemp(_valueTy); genUnop(result, e); _llvmValue = CreateLoad(result); } -void LLVMInstructionSelection::visitBinop(IR::Binop *e) +void InstructionSelection::visitBinop(IR::Binop *e) { llvm::Value *result = newLLVMTemp(_valueTy); genBinop(result, e); _llvmValue = CreateLoad(result); } -void LLVMInstructionSelection::genUnop(llvm::Value *result, IR::Unop *e) +void InstructionSelection::genUnop(llvm::Value *result, IR::Unop *e) { IR::Temp *t = e->expr->asTemp(); assert(t != 0); @@ -762,7 +891,7 @@ void LLVMInstructionSelection::genUnop(llvm::Value *result, IR::Unop *e) CreateCall3(op, _llvmFunction->arg_begin(), result, expr); } -void LLVMInstructionSelection::genBinop(llvm::Value *result, IR::Binop *e) +void InstructionSelection::genBinop(llvm::Value *result, IR::Binop *e) { assert(e->left->asTemp() || e->left->asConst()); assert(e->right->asTemp() || e->right->asConst()); @@ -816,13 +945,13 @@ void LLVMInstructionSelection::genBinop(llvm::Value *result, IR::Binop *e) CreateCall4(op, _llvmFunction->arg_begin(), result, left, right); } -llvm::AllocaInst *LLVMInstructionSelection::newLLVMTemp(llvm::Type *type, llvm::Value *size) +llvm::AllocaInst *InstructionSelection::newLLVMTemp(llvm::Type *type, llvm::Value *size) { llvm::AllocaInst *addr = new llvm::AllocaInst(type, size, llvm::Twine(), _allocaInsertPoint); return addr; } -llvm::Value * LLVMInstructionSelection::genArguments(IR::ExprList *exprs, int &argc) +llvm::Value * InstructionSelection::genArguments(IR::ExprList *exprs, int &argc) { llvm::Value *args = 0; @@ -844,7 +973,7 @@ llvm::Value * LLVMInstructionSelection::genArguments(IR::ExprList *exprs, int &a return args; } -void LLVMInstructionSelection::genCallMember(IR::Call *e, llvm::Value *result) +void InstructionSelection::genCallMember(IR::Call *e, llvm::Value *result) { if (! result) result = newLLVMTemp(_valueTy); @@ -869,7 +998,7 @@ void LLVMInstructionSelection::genCallMember(IR::Call *e, llvm::Value *result) _llvmValue = CreateLoad(result); } -void LLVMInstructionSelection::genConstructMember(IR::New *e, llvm::Value *result) +void InstructionSelection::genConstructMember(IR::New *e, llvm::Value *result) { if (! result) result = newLLVMTemp(_valueTy); @@ -894,7 +1023,7 @@ void LLVMInstructionSelection::genConstructMember(IR::New *e, llvm::Value *resul _llvmValue = CreateLoad(result); } -void LLVMInstructionSelection::genCallTemp(IR::Call *e, llvm::Value *result) +void InstructionSelection::genCallTemp(IR::Call *e, llvm::Value *result) { if (! result) result = newLLVMTemp(_valueTy); @@ -920,7 +1049,7 @@ void LLVMInstructionSelection::genCallTemp(IR::Call *e, llvm::Value *result) _llvmValue = CreateLoad(result); } -void LLVMInstructionSelection::genConstructTemp(IR::New *e, llvm::Value *result) +void InstructionSelection::genConstructTemp(IR::New *e, llvm::Value *result) { if (! result) result = newLLVMTemp(_valueTy); @@ -943,7 +1072,7 @@ void LLVMInstructionSelection::genConstructTemp(IR::New *e, llvm::Value *result) _llvmValue = CreateLoad(result); } -void LLVMInstructionSelection::genCallName(IR::Call *e, llvm::Value *result) +void InstructionSelection::genCallName(IR::Call *e, llvm::Value *result) { IR::Name *base = e->base->asName(); @@ -1046,7 +1175,7 @@ void LLVMInstructionSelection::genCallName(IR::Call *e, llvm::Value *result) } } -void LLVMInstructionSelection::genConstructName(IR::New *e, llvm::Value *result) +void InstructionSelection::genConstructName(IR::New *e, llvm::Value *result) { IR::Name *base = e->base->asName(); @@ -1068,7 +1197,7 @@ void LLVMInstructionSelection::genConstructName(IR::New *e, llvm::Value *result) } } -void LLVMInstructionSelection::visitCall(IR::Call *e) +void InstructionSelection::visitCall(IR::Call *e) { if (e->base->asMember()) { genCallMember(e); @@ -1092,7 +1221,7 @@ void LLVMInstructionSelection::visitCall(IR::Call *e) } } -void LLVMInstructionSelection::visitNew(IR::New *e) +void InstructionSelection::visitNew(IR::New *e) { if (e->base->asMember()) { genConstructMember(e); @@ -1116,7 +1245,7 @@ void LLVMInstructionSelection::visitNew(IR::New *e) } } -void LLVMInstructionSelection::visitSubscript(IR::Subscript *e) +void InstructionSelection::visitSubscript(IR::Subscript *e) { llvm::Value *result = newLLVMTemp(_valueTy); llvm::Value *base = getLLVMTempReference(e->base); @@ -1126,7 +1255,7 @@ void LLVMInstructionSelection::visitSubscript(IR::Subscript *e) _llvmValue = CreateLoad(result); } -void LLVMInstructionSelection::visitMember(IR::Member *e) +void InstructionSelection::visitMember(IR::Member *e) { llvm::Value *result = newLLVMTemp(_valueTy); llvm::Value *base = getLLVMTempReference(e->base); @@ -1137,7 +1266,7 @@ void LLVMInstructionSelection::visitMember(IR::Member *e) _llvmValue = CreateLoad(result); } -llvm::Value *LLVMInstructionSelection::createValue(IR::Const *e) +llvm::Value *InstructionSelection::createValue(IR::Const *e) { llvm::Value *tmp = newLLVMTemp(_valueTy); @@ -1167,7 +1296,7 @@ llvm::Value *LLVMInstructionSelection::createValue(IR::Const *e) return tmp; } -llvm::Value *LLVMInstructionSelection::toValuePtr(IR::Expr *e) +llvm::Value *InstructionSelection::toValuePtr(IR::Expr *e) { if (IR::Temp *t = e->asTemp()) { return getLLVMTemp(t); diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 3f51a21b3f..cb5775a25e 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -58,16 +58,65 @@ #include "qv4ir_p.h" namespace QQmlJS { +namespace LLVM { -class LLVMInstructionSelection: +class InstructionSelection: public llvm::IRBuilder<>, - protected IR::StmtVisitor, + public IR::InstructionSelection, protected IR::ExprVisitor { public: - LLVMInstructionSelection(llvm::LLVMContext &context); + InstructionSelection(llvm::LLVMContext &context); void buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm); + +public: // methods from InstructionSelection: + virtual void callActivationProperty(IR::Call *c, IR::Temp *temp); + virtual void callValue(IR::Call *c, IR::Temp *temp); + virtual void callProperty(IR::Call *c, IR::Temp *temp); + virtual void constructActivationProperty(IR::New *call, IR::Temp *result); + virtual void constructProperty(IR::New *call, IR::Temp *result); + virtual void constructValue(IR::New *call, IR::Temp *result); + virtual void loadThisObject(IR::Temp *temp); + virtual void loadConst(IR::Const *con, IR::Temp *temp); + virtual void loadString(const QString &str, IR::Temp *targetTemp); + virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); + virtual void getActivationProperty(const QString &name, IR::Temp *temp); + virtual void setActivationProperty(IR::Expr *source, const QString &targetName); + virtual void initClosure(IR::Closure *closure, IR::Temp *target); + virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); + virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target); + virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); + virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); + virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); + virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + +public: // visitor methods for StmtVisitor: + virtual void visitExp(IR::Exp *); // TODO: remove + virtual void visitMove(IR::Move *); + virtual void visitJump(IR::Jump *); + virtual void visitCJump(IR::CJump *); + virtual void visitRet(IR::Ret *); + +public: // visitor methods for ExprVisitor: + virtual void visitConst(IR::Const *); + virtual void visitString(IR::String *); + virtual void visitRegExp(IR::RegExp *); + virtual void visitName(IR::Name *); + virtual void visitTemp(IR::Temp *); + virtual void visitClosure(IR::Closure *); + virtual void visitUnop(IR::Unop *); + virtual void visitBinop(IR::Binop *); + virtual void visitCall(IR::Call *); + virtual void visitNew(IR::New *); + virtual void visitSubscript(IR::Subscript *); + virtual void visitMember(IR::Member *); + +private: llvm::Function *getLLVMFunction(IR::Function *function); llvm::Function *compileLLVMFunction(IR::Function *function); llvm::BasicBlock *getLLVMBasicBlock(IR::BasicBlock *block); @@ -89,29 +138,6 @@ public: void genConstructMember(IR::New *e, llvm::Value *result = 0); void genMoveSubscript(IR::Move *s); void genMoveMember(IR::Move *s); - - virtual void visitExp(IR::Exp *); - virtual void visitEnter(IR::Enter *); - virtual void visitLeave(IR::Leave *); - virtual void visitMove(IR::Move *); - virtual void visitJump(IR::Jump *); - virtual void visitCJump(IR::CJump *); - virtual void visitRet(IR::Ret *); - - virtual void visitConst(IR::Const *); - virtual void visitString(IR::String *); - virtual void visitRegExp(IR::RegExp *); - virtual void visitName(IR::Name *); - virtual void visitTemp(IR::Temp *); - virtual void visitClosure(IR::Closure *); - virtual void visitUnop(IR::Unop *); - virtual void visitBinop(IR::Binop *); - virtual void visitCall(IR::Call *); - virtual void visitNew(IR::New *); - virtual void visitSubscript(IR::Subscript *); - virtual void visitMember(IR::Member *); - -private: llvm::Value *createValue(IR::Const *e); llvm::Value *toValuePtr(IR::Expr *e); @@ -134,6 +160,7 @@ private: llvm::FunctionPassManager *_fpm; }; -} // end of namespace QQmlJS +} // LLVM namespace +} // QQmlJS namespace #endif // QV4ISEL_LLVM_P_H diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 3f44f260da..b73bddc988 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -58,10 +58,6 @@ using namespace QQmlJS; using namespace QQmlJS::MASM; using namespace QQmlJS::VM; -namespace { -QTextStream qout(stderr, QIODevice::WriteOnly); -} - Assembler::Assembler(IR::Function* function) : _function(function) { @@ -585,6 +581,179 @@ void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) generateFunctionCall(result, __qmljs_call_value, Assembler::ContextRegister, thisObject, baseTemp, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } +void InstructionSelection::loadThisObject(IR::Temp *temp) +{ + generateFunctionCall(temp, __qmljs_get_thisObject, Assembler::ContextRegister); +} + +void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) +{ + _asm->storeValue(convertToValue(sourceConst), targetTemp); +} + +void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) +{ + Value v = Value::fromString(engine()->newString(str)); + _asm->storeValue(v, targetTemp); +} + +void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) +{ + Value v = Value::fromObject(engine()->newRegExpObject(*sourceRegexp->value, + sourceRegexp->flags)); + _asm->storeValue(v, targetTemp); +} + +void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) +{ + String *propertyName = identifier(name); + generateFunctionCall(temp, __qmljs_get_activation_property, Assembler::ContextRegister, propertyName); +} + +void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) +{ + String *propertyName = identifier(targetName); + generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, Assembler::ContextRegister, propertyName, source); +} + +void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) +{ + VM::Function *vmFunc = vmFunction(closure->value); + assert(vmFunc); + generateFunctionCall(target, __qmljs_init_closure, Assembler::TrustedImmPtr(vmFunc), Assembler::ContextRegister); +} + +void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::Temp *target) +{ + generateFunctionCall(target, __qmljs_get_property, Assembler::ContextRegister, base, identifier(name)); +} + +void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +{ + generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, targetBase, identifier(targetName), source); +} + +void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) +{ + generateFunctionCall(target, __qmljs_get_element, Assembler::ContextRegister, base, index); +} + +void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) +{ + generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, targetBase, targetIndex, source); +} + +void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + _asm->copyValue(targetTemp, sourceTemp); +} + +#define setOp(op, opName, operation) \ + do { op = operation; opName = isel_stringIfy(operation); } while (0) + +void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + Value (*op)(const Value value, ExecutionContext *ctx) = 0; + const char *opName = 0; + switch (oper) { + case IR::OpIfTrue: assert(!"unreachable"); break; + case IR::OpNot: setOp(op, opName, __qmljs_not); break; + case IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break; + case IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break; + case IR::OpCompl: setOp(op, opName, __qmljs_compl); break; + case IR::OpIncrement: setOp(op, opName, __qmljs_increment); break; + case IR::OpDecrement: setOp(op, opName, __qmljs_decrement); break; + default: assert(!"unreachable"); break; + } // switch + + if (op) + _asm->generateFunctionCallImp(targetTemp, opName, op, sourceTemp, + Assembler::ContextRegister); +} + +void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) +{ + _asm->generateBinOp(oper, target, leftSource, rightSource); +} + +void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) +{ + void (*op)(const Value value, String *name, ExecutionContext *ctx) = 0; + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break; + case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_name); break; + case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_name); break; + case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_name); break; + case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_name); break; + case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_name); break; + case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_name); break; + case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_name); break; + case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_name); break; + case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_name); break; + case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_name); break; + default: + Q_UNREACHABLE(); + break; + } + if (op) { + _asm->generateFunctionCallImp(Assembler::Void, opName, op, sourceExpr, identifier(targetName), Assembler::ContextRegister); + } +} + +void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) +{ + void (*op)(Value base, Value index, Value value, ExecutionContext *ctx) = 0; + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; + case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_element); break; + case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_element); break; + case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_element); break; + case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_element); break; + case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_element); break; + case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_element); break; + case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_element); break; + case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_element); break; + case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_element); break; + case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_element); break; + default: + Q_UNREACHABLE(); + break; + } + + if (op) { + _asm->generateFunctionCallImp(Assembler::Void, opName, op, targetBaseTemp, targetIndexTemp, sourceExpr, Assembler::ContextRegister); + } +} + +void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +{ + void (*op)(Value value, Value base, String *name, ExecutionContext *ctx) = 0; + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; + case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_member); break; + case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_member); break; + case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_member); break; + case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_member); break; + case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_member); break; + case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_member); break; + case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_member); break; + case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_member); break; + case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_member); break; + case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_member); break; + default: + Q_UNREACHABLE(); + break; + } + + if (op) { + String* member = identifier(targetName); + _asm->generateFunctionCallImp(Assembler::Void, opName, op, source, targetBase, member, Assembler::ContextRegister); + } +} + void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) { IR::Member *member = call->base->asMember(); @@ -622,253 +791,6 @@ void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) generateFunctionCall(result, __qmljs_construct_value, Assembler::ContextRegister, baseTemp, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } -void InstructionSelection::visitExp(IR::Exp *s) -{ - if (IR::Call *c = s->expr->asCall()) { - if (c->base->asName()) { - callActivationProperty(c, 0); - return; - } else if (c->base->asTemp()) { - callValue(c, 0); - return; - } else if (c->base->asMember()) { - callProperty(c, 0); - return; - } - } - assert(!"TODO"); -} - -void InstructionSelection::visitEnter(IR::Enter *) -{ - Q_UNIMPLEMENTED(); - assert(!"TODO"); -} - -void InstructionSelection::visitLeave(IR::Leave *) -{ - Q_UNIMPLEMENTED(); - assert(!"TODO"); -} - -#define setOp(op, opName, operation) \ - do { op = operation; opName = isel_stringIfy(operation); } while (0) - -void InstructionSelection::visitMove(IR::Move *s) -{ - - if (s->op == IR::OpInvalid) { - if (IR::Name *n = s->target->asName()) { - String *propertyName = identifier(*n->id); - - if (s->source->asTemp() || s->source->asConst()) { - generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, Assembler::ContextRegister, propertyName, s->source); - return; - } else { - Q_UNREACHABLE(); - } - } else if (IR::Temp *t = s->target->asTemp()) { - if (IR::Name *n = s->source->asName()) { - if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. - generateFunctionCall(t, __qmljs_get_thisObject, Assembler::ContextRegister); - } else { - String *propertyName = identifier(*n->id); - generateFunctionCall(t, __qmljs_get_activation_property, Assembler::ContextRegister, propertyName); - } - return; - } else if (IR::Const *c = s->source->asConst()) { - Value v = convertToValue(c); - _asm->storeValue(v, t); - return; - } else if (IR::Temp *t2 = s->source->asTemp()) { - _asm->copyValue(t, t2); - return; - } else if (IR::String *str = s->source->asString()) { - Value v = Value::fromString(engine()->newString(*str->value)); - _asm->storeValue(v, t); - return; - } else if (IR::RegExp *re = s->source->asRegExp()) { - Value v = Value::fromObject(engine()->newRegExpObject(*re->value, re->flags)); - _asm->storeValue(v, t); - return; - } else if (IR::Closure *clos = s->source->asClosure()) { - VM::Function *vmFunc = vmFunction(clos->value); - assert(vmFunc); - generateFunctionCall(t, __qmljs_init_closure, Assembler::TrustedImmPtr(vmFunc), Assembler::ContextRegister); - return; - } else if (IR::New *ctor = s->source->asNew()) { - if (ctor->base->asName()) { - constructActivationProperty(ctor, t); - return; - } else if (ctor->base->asMember()) { - constructProperty(ctor, t); - return; - } else if (ctor->base->asTemp()) { - constructValue(ctor, t); - return; - } - } else if (IR::Member *m = s->source->asMember()) { - //__qmljs_get_property(ctx, result, object, name); - if (IR::Temp *base = m->base->asTemp()) { - generateFunctionCall(t, __qmljs_get_property, Assembler::ContextRegister, base, identifier(*m->name)); - return; - } - assert(!"wip"); - return; - } else if (IR::Subscript *ss = s->source->asSubscript()) { - generateFunctionCall(t, __qmljs_get_element, Assembler::ContextRegister, ss->base->asTemp(), ss->index->asTemp()); - return; - } else if (IR::Unop *u = s->source->asUnop()) { - if (IR::Temp *e = u->expr->asTemp()) { - Value (*op)(const Value value, ExecutionContext *ctx) = 0; - const char *opName = 0; - switch (u->op) { - case IR::OpIfTrue: assert(!"unreachable"); break; - case IR::OpNot: setOp(op, opName, __qmljs_not); break; - case IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break; - case IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break; - case IR::OpCompl: setOp(op, opName, __qmljs_compl); break; - case IR::OpIncrement: setOp(op, opName, __qmljs_increment); break; - case IR::OpDecrement: setOp(op, opName, __qmljs_decrement); break; - default: assert(!"unreachable"); break; - } // switch - - if (op) - _asm->generateFunctionCallImp(t, opName, op, e, Assembler::ContextRegister); - return; - } - } else if (IR::Binop *b = s->source->asBinop()) { - if ((b->left->asTemp() || b->left->asConst()) && - (b->right->asTemp() || b->right->asConst())) { - _asm->generateBinOp((IR::AluOp)b->op, t, b->left, b->right); - return; - } - } else if (IR::Call *c = s->source->asCall()) { - if (c->base->asName()) { - callActivationProperty(c, t); - return; - } else if (c->base->asMember()) { - callProperty(c, t); - return; - } else if (c->base->asTemp()) { - callValue(c, t); - return; - } - } - } else if (IR::Member *m = s->target->asMember()) { - if (IR::Temp *base = m->base->asTemp()) { - if (s->source->asTemp() || s->source->asConst()) { - generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, base, identifier(*m->name), s->source); - return; - } else { - Q_UNREACHABLE(); - } - } - } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (s->source->asTemp() || s->source->asConst()) { - generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, ss->base->asTemp(), ss->index->asTemp(), s->source); - return; - } else { - Q_UNIMPLEMENTED(); - } - } - } else { - // inplace assignment, e.g. x += 1, ++x, ... - if (IR::Temp *t = s->target->asTemp()) { - if (s->source->asTemp() || s->source->asConst()) { - _asm->generateBinOp((IR::AluOp)s->op, t, t, s->source); - return; - } - } else if (IR::Name *n = s->target->asName()) { - if (s->source->asTemp() || s->source->asConst()) { - void (*op)(const Value value, String *name, ExecutionContext *ctx) = 0; - const char *opName = 0; - switch (s->op) { - case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break; - case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_name); break; - case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_name); break; - case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_name); break; - case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_name); break; - case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_name); break; - case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_name); break; - case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_name); break; - case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_name); break; - case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_name); break; - case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_name); break; - default: - Q_UNREACHABLE(); - break; - } - if (op) { - _asm->generateFunctionCallImp(Assembler::Void, opName, op, s->source, identifier(*n->id), Assembler::ContextRegister); - } - return; - } - } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (s->source->asTemp() || s->source->asConst()) { - void (*op)(Value base, Value index, Value value, ExecutionContext *ctx) = 0; - const char *opName = 0; - switch (s->op) { - case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; - case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_element); break; - case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_element); break; - case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_element); break; - case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_element); break; - case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_element); break; - case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_element); break; - case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_element); break; - case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_element); break; - case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_element); break; - case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_element); break; - default: - Q_UNREACHABLE(); - break; - } - - if (op) { - IR::Temp* base = ss->base->asTemp(); - IR::Temp* index = ss->index->asTemp(); - _asm->generateFunctionCallImp(Assembler::Void, opName, op, base, index, s->source, Assembler::ContextRegister); - } - return; - } - } else if (IR::Member *m = s->target->asMember()) { - if (s->source->asTemp() || s->source->asConst()) { - void (*op)(Value value, Value base, String *name, ExecutionContext *ctx) = 0; - const char *opName = 0; - switch (s->op) { - case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; - case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_member); break; - case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_member); break; - case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_member); break; - case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_member); break; - case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_member); break; - case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_member); break; - case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_member); break; - case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_member); break; - case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_member); break; - case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_member); break; - default: - Q_UNREACHABLE(); - break; - } - - if (op) { - IR::Temp* base = m->base->asTemp(); - String* member = identifier(*m->name); - _asm->generateFunctionCallImp(Assembler::Void, opName, op, s->source, base, member, Assembler::ContextRegister); - } - return; - } - } - } - Q_UNIMPLEMENTED(); - s->dump(qout, IR::Stmt::MIR); - qout << endl; - assert(!"TODO"); - -} - void InstructionSelection::visitJump(IR::Jump *s) { _asm->jumpToBlock(_block, s->target); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 481392c571..c042bd626e 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -641,7 +641,9 @@ private: QList _callsToLink; }; -class InstructionSelection: protected IR::StmtVisitor, public EvalInstructionSelection +class InstructionSelection: + protected IR::InstructionSelection, + public EvalInstructionSelection { public: InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); @@ -650,6 +652,27 @@ public: virtual void run(VM::Function *vmFunction, IR::Function *function); protected: + virtual void callActivationProperty(IR::Call *call, IR::Temp *result); + virtual void callProperty(IR::Call *call, IR::Temp *result); + virtual void callValue(IR::Call *call, IR::Temp *result); + virtual void loadThisObject(IR::Temp *temp); + virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp); + virtual void loadString(const QString &str, IR::Temp *targetTemp); + virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); + virtual void getActivationProperty(const QString &name, IR::Temp *temp); + virtual void setActivationProperty(IR::Expr *source, const QString &targetName); + virtual void initClosure(IR::Closure *closure, IR::Temp *target); + virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); + virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target); + virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); + virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); + virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); + virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + typedef Assembler::Address Address; typedef Assembler::Pointer Pointer; @@ -679,17 +702,10 @@ protected: } VM::String *identifier(const QString &s); - void callActivationProperty(IR::Call *call, IR::Temp *result); - void callProperty(IR::Call *call, IR::Temp *result); - void constructActivationProperty(IR::New *call, IR::Temp *result); - void constructProperty(IR::New *ctor, IR::Temp *result); - void callValue(IR::Call *call, IR::Temp *result); - void constructValue(IR::New *call, IR::Temp *result); - - virtual void visitExp(IR::Exp *); - virtual void visitEnter(IR::Enter *); - virtual void visitLeave(IR::Leave *); - virtual void visitMove(IR::Move *s); + virtual void constructActivationProperty(IR::New *call, IR::Temp *result); + virtual void constructProperty(IR::New *ctor, IR::Temp *result); + virtual void constructValue(IR::New *call, IR::Temp *result); + virtual void visitJump(IR::Jump *); virtual void visitCJump(IR::CJump *); virtual void visitRet(IR::Ret *); diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 6d5d2297b8..bff9f4a381 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -8,9 +8,14 @@ #include +namespace { +QTextStream qout(stderr, QIODevice::WriteOnly); +} // anonymous namespace + using namespace QQmlJS; +using namespace QQmlJS::IR; -EvalInstructionSelection::EvalInstructionSelection(VM::ExecutionEngine *engine, IR::Module *module) +EvalInstructionSelection::EvalInstructionSelection(VM::ExecutionEngine *engine, Module *module) : _engine(engine) { assert(engine); @@ -28,7 +33,7 @@ EvalInstructionSelection::~EvalInstructionSelection() EvalISelFactory::~EvalISelFactory() {} -VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngine *engine, IR::Function *irFunction) +VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngine *engine, Function *irFunction) { VM::Function *vmFunction = engine->newFunction(irFunction->name ? *irFunction->name : QString()); _irToVM.insert(irFunction, vmFunction); @@ -53,9 +58,159 @@ VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngin return vmFunction; } -VM::Function *EvalInstructionSelection::vmFunction(IR::Function *f) { +VM::Function *EvalInstructionSelection::vmFunction(Function *f) { VM::Function *function = _irToVM[f]; if (!function->code) run(function, f); return function; } + +void InstructionSelection::visitMove(IR::Move *s) +{ + if (s->op == IR::OpInvalid) { + if (IR::Name *n = s->target->asName()) { + if (s->source->asTemp() || s->source->asConst()) { + setActivationProperty(s->source, *n->id); + return; + } + } else if (IR::Temp *t = s->target->asTemp()) { + if (IR::Name *n = s->source->asName()) { + if (*n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. + loadThisObject(t); + else + getActivationProperty(*n->id, t); + return; + } else if (IR::Const *c = s->source->asConst()) { + loadConst(c, t); + return; + } else if (IR::Temp *t2 = s->source->asTemp()) { + copyValue(t2, t); + return; + } else if (IR::String *str = s->source->asString()) { + loadString(*str->value, t); + return; + } else if (IR::RegExp *re = s->source->asRegExp()) { + loadRegexp(re, t); + return; + } else if (IR::Closure *clos = s->source->asClosure()) { + initClosure(clos, t); + return; + } else if (IR::New *ctor = s->source->asNew()) { + if (ctor->base->asName()) { + constructActivationProperty(ctor, t); + return; + } else if (ctor->base->asMember()) { + constructProperty(ctor, t); + return; + } else if (ctor->base->asTemp()) { + constructValue(ctor, t); + return; + } + } else if (IR::Member *m = s->source->asMember()) { + if (IR::Temp *base = m->base->asTemp()) { + getProperty(base, *m->name, t); + return; + } + } else if (IR::Subscript *ss = s->source->asSubscript()) { + getElement(ss->base->asTemp(), ss->index->asTemp(), t); + return; + } else if (IR::Unop *u = s->source->asUnop()) { + if (IR::Temp *e = u->expr->asTemp()) { + unop(u->op, e, t); + return; + } + } else if (IR::Binop *b = s->source->asBinop()) { + if ((b->left->asTemp() || b->left->asConst()) + && (b->right->asTemp() || b->right->asConst())) { + binop(b->op, b->left, b->right, t); + return; + } + } else if (IR::Call *c = s->source->asCall()) { + if (c->base->asName()) { + callActivationProperty(c, t); + return; + } else if (c->base->asMember()) { + callProperty(c, t); + return; + } else if (c->base->asTemp()) { + callValue(c, t); + return; + } + } + } else if (IR::Member *m = s->target->asMember()) { + if (IR::Temp *base = m->base->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { + setProperty(s->source, base, *m->name); + return; + } + } + } else if (IR::Subscript *ss = s->target->asSubscript()) { + if (s->source->asTemp() || s->source->asConst()) { + setElement(s->source, ss->base->asTemp(), ss->index->asTemp()); + return; + } + } + } else { + // inplace assignment, e.g. x += 1, ++x, ... + if (IR::Temp *t = s->target->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { + binop(s->op, t, s->source, t); + return; + } + } else if (IR::Name *n = s->target->asName()) { + if (s->source->asTemp() || s->source->asConst()) { + inplaceNameOp(s->op, s->source, *n->id); + return; + } + } else if (IR::Subscript *ss = s->target->asSubscript()) { + if (s->source->asTemp() || s->source->asConst()) { + inplaceElementOp(s->op, s->source, ss->base->asTemp(), + ss->index->asTemp()); + return; + } + } else if (IR::Member *m = s->target->asMember()) { + if (s->source->asTemp() || s->source->asConst()) { + inplaceMemberOp(s->op, s->source, m->base->asTemp(), *m->name); + return; + } + } + } + + // For anything else...: + Q_UNIMPLEMENTED(); + s->dump(qout, IR::Stmt::MIR); + qout << endl; + assert(!"TODO"); +} + +InstructionSelection::~InstructionSelection() +{ +} + +void InstructionSelection::visitEnter(Enter *) +{ + Q_UNREACHABLE(); +} + +void InstructionSelection::visitLeave(Leave *) +{ + Q_UNREACHABLE(); +} + +void InstructionSelection::visitExp(IR::Exp *s) +{ + if (IR::Call *c = s->expr->asCall()) { + // These are calls where the result is ignored. + if (c->base->asName()) { + callActivationProperty(c, 0); + } else if (c->base->asTemp()) { + callValue(c, 0); + } else if (c->base->asMember()) { + callProperty(c, 0); + } else { + Q_UNREACHABLE(); + } + } else { + Q_UNREACHABLE(); + } +} diff --git a/qv4isel_p.h b/qv4isel_p.h index 221a564f32..59464c6602 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -30,16 +30,13 @@ #ifndef QV4ISEL_P_H #define QV4ISEL_P_H +#include "qv4ir_p.h" + #include #include namespace QQmlJS { -namespace IR { -struct Function; -struct Module; -} // namespace IR; - namespace VM { struct ExecutionEngine; struct Function; @@ -70,6 +67,45 @@ public: virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module) = 0; }; +namespace IR { +class InstructionSelection: protected IR::StmtVisitor +{ +public: + virtual ~InstructionSelection() = 0; + +public: // visitor methods for StmtVisitor: + virtual void visitMove(IR::Move *s); + virtual void visitEnter(IR::Enter *); + virtual void visitLeave(IR::Leave *); + virtual void visitExp(IR::Exp *s); + +public: // to implement by subclasses: + virtual void callActivationProperty(IR::Call *c, IR::Temp *temp) = 0; + virtual void callValue(IR::Call *c, IR::Temp *temp) = 0; + virtual void callProperty(IR::Call *c, IR::Temp *temp) = 0; + virtual void constructActivationProperty(IR::New *call, IR::Temp *result) = 0; + virtual void constructProperty(IR::New *ctor, IR::Temp *result) = 0; + virtual void constructValue(IR::New *call, IR::Temp *result) = 0; + virtual void loadThisObject(IR::Temp *temp) = 0; + virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) = 0; + virtual void loadString(const QString &str, IR::Temp *targetTemp) = 0; + virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) = 0; + virtual void getActivationProperty(const QString &name, IR::Temp *temp) = 0; + virtual void setActivationProperty(IR::Expr *source, const QString &targetName) = 0; + virtual void initClosure(IR::Closure *closure, IR::Temp *target) = 0; + virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target) = 0; + virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) = 0; + virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) = 0; + virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) = 0; + virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0; + virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0; + virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) = 0; + virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) = 0; + virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) = 0; + virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) = 0; +}; +} // namespace IR + } // namespace QQmlJS #endif // QV4ISEL_P_H -- cgit v1.2.3 From 7b613abb14655af296b571d985c144756a8ee6ff Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 15 Jan 2013 10:47:21 +0100 Subject: Ignore LLVM generated files. Change-Id: Ibc753fe5576eb7f60c96a6d0d5407adb392ff3ba Reviewed-by: Simon Hausmann --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 112af15744..17784d7c7e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ v4 udis86_itab.* *.pyc *.pro.user +*.bc +*.ll -- cgit v1.2.3 From c94c1403546b286f06795120901d12f165a7ded8 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 15 Jan 2013 12:12:52 +0100 Subject: Fixed memory leaks. Change-Id: I641127b0d46f88291468af767cbbeb28ce5a75d9 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 1 + qmljs_environment.cpp | 6 ++++++ qmljs_environment.h | 2 +- qv4ecmaobjects.cpp | 6 +++--- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 30756e335d..6367dcccc1 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -216,6 +216,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) ExecutionEngine::~ExecutionEngine() { delete globalObject.asObject(); + rootContext->destroy(); delete rootContext; qDeleteAll(functions); delete memoryManager; diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 067116d469..15da3739ca 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -229,6 +229,12 @@ void ExecutionContext::init(ExecutionEngine *eng) eng->exception = Value::undefinedValue(); } +void ExecutionContext::destroy() +{ + delete[] arguments; + delete[] locals; +} + bool ExecutionContext::deleteProperty(String *name) { for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { diff --git a/qmljs_environment.h b/qmljs_environment.h index faa04aae0c..e4bb0b9b38 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -96,8 +96,8 @@ struct ExecutionContext With *next; } *withObject; - void init(ExecutionEngine *e); + void destroy(); bool hasBinding(String *name) const; void createMutableBinding(String *name, bool deletable); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index fa1b398377..5d743db2da 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -298,10 +298,10 @@ static inline double DaylightSavingTA(double t) { #ifndef Q_WS_WIN long int tt = (long int)(t / msPerSecond); - struct tm *tmtm = localtime((const time_t*)&tt); - if (! tmtm) + struct tm tmtm; + if (!localtime_r((const time_t*)&tt, &tmtm)) return 0; - return (tmtm->tm_isdst > 0) ? msPerHour : 0; + return (tmtm.tm_isdst > 0) ? msPerHour : 0; #else Q_UNUSED(t); /// ### implement me -- cgit v1.2.3 From e7cb13296711527ecf67fceaa7284515db712beb Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 15 Jan 2013 12:33:49 +0100 Subject: Dynamically grow/shrink MOTH code block. Instead of allocating 4000*pageSize, and not checking if it overflows. Change-Id: I38b37d60bb39bddbbd9a41a2e9c5958b1acc62eb Reviewed-by: Simon Hausmann --- moth/qv4isel_moth.cpp | 102 +++++++++++++++++++++++++++++++------------------ moth/qv4isel_moth_p.h | 7 +++- tests/TestExpectations | 5 +-- 3 files changed, 70 insertions(+), 44 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index bfc04a39d9..01a58667d9 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -265,8 +265,9 @@ InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Modu : EvalInstructionSelection(engine, module) , _function(0) , _block(0) - , _code(0) - , _ccode(0) + , _codeStart(0) + , _codeNext(0) + , _codeEnd(0) { } @@ -276,22 +277,23 @@ InstructionSelection::~InstructionSelection() void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) { - qSwap(_function, function); - IR::BasicBlock *block; QHash > patches; QHash addrs; - // FIXME: make the size dynamic. This requires changing the patching. - uchar *code = new uchar[getpagesize() * 4000]; - uchar *ccode = code; + int codeSize = 4096; + uchar *codeStart = new uchar[codeSize]; + uchar *codeNext = codeStart; + uchar *codeEnd = codeStart + codeSize; + qSwap(_function, function); qSwap(block, _block); qSwap(patches, _patches); qSwap(addrs, _addrs); - qSwap(code, _code); - qSwap(ccode, _ccode); + qSwap(codeStart, _codeStart); + qSwap(codeNext, _codeNext); + qSwap(codeEnd, _codeEnd); CompressTemps().run(_function); @@ -303,38 +305,26 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) addInstruction(push); foreach (_block, _function->basicBlocks) { - _addrs.insert(_block, _ccode - _code); + _addrs.insert(_block, _codeNext - _codeStart); foreach (IR::Stmt *s, _block->statements) s->accept(this); } - for (QHash >::ConstIterator iter = _patches.begin(); - iter != _patches.end(); ++iter) { - - Q_ASSERT(_addrs.contains(iter.key())); - ptrdiff_t target = _addrs.value(iter.key()); - - const QVector &patchList = iter.value(); - for (int ii = 0; ii < patchList.count(); ++ii) { - ptrdiff_t patch = patchList.at(ii); - - *((ptrdiff_t *)(_code + patch)) = target - patch; - } - } - - qSwap(_function, function); - _patches.clear(); - _addrs.clear(); + patchJumpAddresses(); vmFunction->code = VME::exec; - vmFunction->codeData = _code; + vmFunction->codeData = squeezeCode(); + qSwap(_function, function); qSwap(block, _block); qSwap(patches, _patches); qSwap(addrs, _addrs); - qSwap(code, _code); - qSwap(ccode, _ccode); + qSwap(codeStart, _codeStart); + qSwap(codeNext, _codeNext); + qSwap(codeEnd, _codeEnd); + + delete[] codeStart; } void InstructionSelection::callActivationProperty(IR::Call *c, IR::Temp *temp) @@ -880,14 +870,14 @@ void InstructionSelection::visitCJump(IR::CJump *s) Instruction::CJump jump; jump.offset = 0; jump.tempIndex = tempIndex; - ptrdiff_t tl = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); - _patches[s->iftrue].append(tl); + ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + _patches[s->iftrue].append(trueLoc); if (_block->index + 1 != s->iffalse->index) { Instruction::Jump jump; jump.offset = 0; - ptrdiff_t fl = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); - _patches[s->iffalse].append(fl); + ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + _patches[s->iffalse].append(falseLoc); } } @@ -906,12 +896,48 @@ ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &in instr.common.instructionType = type; #endif - ptrdiff_t ptrOffset = _ccode - _code; - int size = Instr::size(type); + int instructionSize = Instr::size(type); + if (_codeEnd - _codeNext < instructionSize) { + int currSize = _codeEnd - _codeStart; + uchar *newCode = new uchar[currSize * 2]; + ::memset(newCode + currSize, 0, currSize); + ::memcpy(newCode, _codeStart, currSize); + _codeNext = _codeNext - _codeStart + newCode; + delete[] _codeStart; + _codeStart = newCode; + _codeEnd = _codeStart + currSize * 2; + } - ::memcpy(_ccode, reinterpret_cast(&instr), size); - _ccode += size; + ::memcpy(_codeNext, reinterpret_cast(&instr), instructionSize); + ptrdiff_t ptrOffset = _codeNext - _codeStart; + _codeNext += instructionSize; return ptrOffset; } +void InstructionSelection::patchJumpAddresses() +{ + typedef QHash >::ConstIterator PatchIt; + for (PatchIt i = _patches.begin(), ei = _patches.end(); i != ei; ++i) { + Q_ASSERT(_addrs.contains(i.key())); + ptrdiff_t target = _addrs.value(i.key()); + + const QVector &patchList = i.value(); + for (int ii = 0, eii = patchList.count(); ii < eii; ++ii) { + ptrdiff_t patch = patchList.at(ii); + + *((ptrdiff_t *)(_codeStart + patch)) = target - patch; + } + } + + _patches.clear(); + _addrs.clear(); +} + +uchar *InstructionSelection::squeezeCode() const +{ + int codeSize = _codeNext - _codeStart; + uchar *squeezed = new uchar[codeSize]; + ::memcpy(squeezed, _codeStart, codeSize); + return squeezed; +} diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 0456394f7a..c0e859536b 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -67,6 +67,8 @@ private: template inline ptrdiff_t addInstruction(const InstrData &data); ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); + void patchJumpAddresses(); + uchar *squeezeCode() const; IR::Function *_function; IR::BasicBlock *_block; @@ -74,8 +76,9 @@ private: QHash > _patches; QHash _addrs; - uchar *_code; - uchar *_ccode; + uchar *_codeStart; + uchar *_codeNext; + uchar *_codeEnd; }; class ISelFactory: public EvalISelFactory diff --git a/tests/TestExpectations b/tests/TestExpectations index 1341803307..96c6b25c09 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -838,9 +838,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-41 failing 15.2.3.6-4-410 failing 15.2.3.6-4-586 failing -15.2.3.6-4-593 failing -15.2.3.6-4-594 failing -15.2.3.6-4-596 failing 15.2.3.6-4-621 failing 15.2.3.6-4-622 failing 15.2.3.6-4-623 failing @@ -2951,4 +2948,4 @@ S15.4.4.13_A1_T2 failing 15.4.4.19-8-c-i-6 failing 15.4.4.20-9-c-i-6 failing 15.4.4.21-8-b-iii-1-6 failing -15.4.4.22-8-b-iii-1-6 failing \ No newline at end of file +15.4.4.22-8-b-iii-1-6 failing -- cgit v1.2.3 From 6f0e8c0788192357b849b6a68f86a9c7cdfec10d Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 15 Jan 2013 15:01:09 +0100 Subject: Fix case in parseInt where qin64 would overflow. Change-Id: I2c9fbd3f7476d35ffef4abb408cac5ac89d85268 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 28 ++++++++++++++++++++++++++-- tests/TestExpectations | 1 - 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index eacf40bde8..f9b4df03c4 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -985,6 +985,9 @@ Value ParseIntFunction::call(ExecutionContext *context, Value thisObject, Value // 13: this is handled by the toInt function if (pos == end) // 12 return Value::fromDouble(nan("")); + bool overflow = false; + qint64 v_overflow; + unsigned overflow_digit_count = 0; int d = toInt(*pos++, R); if (d == -1) return Value::fromDouble(nan("")); @@ -993,10 +996,31 @@ Value ParseIntFunction::call(ExecutionContext *context, Value thisObject, Value d = toInt(*pos++, R); if (d == -1) break; - v = v * R + d; + if (overflow) { + if (overflow_digit_count == 0) { + v_overflow = v; + v = 0; + } + ++overflow_digit_count; + v = v * R + d; + } else { + qint64 vNew = v * R + d; + if (vNew < v) { + overflow = true; + --pos; + } else { + v = vNew; + } + } } - return Value::fromDouble(v * sign); // 15 + if (overflow) { + double result = (double) v_overflow * pow(R, overflow_digit_count); + result += v; + return Value::fromDouble(sign * result); + } else { + return Value::fromDouble(sign * (double) v); // 15 + } } // parseFloat [15.1.2.3] diff --git a/tests/TestExpectations b/tests/TestExpectations index 96c6b25c09..c8dedea78f 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -372,7 +372,6 @@ S12.7_A7 failing S12.8_A4_T1 failing S12.8_A4_T2 failing S12.8_A4_T3 failing -S15.1.2.2_A7.3_T3 failing S15.1.2.2_A9.1 failing S15.1.2.2_A9.2 failing S15.1.2.2_A9.3 failing -- cgit v1.2.3 From c7f71798c74af9731bc4356df8b8f616c10db949 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 16 Jan 2013 11:00:56 +0100 Subject: Fix the remaining issues with Object.defineProperty/defineProperties Change-Id: I4c2a28ad49aa00888d79aee3512b1ba54c16fcbd Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 7 ++- qv4ecmaobjects.cpp | 13 +++--- tests/TestExpectations | 118 +------------------------------------------------ 3 files changed, 11 insertions(+), 127 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index f9b4df03c4..749e8835ed 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -600,10 +600,9 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *cu } else { // clause 10 assert(current->isAccessor() && desc->isAccessor()); if (!current->isConfigurable()) { - if ((!current->get && (quintptr)desc->get > 0x1) || - (current->get && current->get != desc->get) || - (!current->set && (quintptr)desc->set > 0x1) || - (current->set && current->set != desc->set)) + if (desc->get && !(current->get == desc->get || (!current->get && (quintptr)desc->get == 0x1))) + goto reject; + if (desc->set && !(current->set == desc->set || (!current->set && (quintptr)desc->set == 0x1))) goto reject; } } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 5d743db2da..6fac204704 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -640,10 +640,8 @@ Value ObjectPrototype::method_defineProperty(ExecutionContext *ctx) PropertyDescriptor pd; toPropertyDescriptor(ctx, attributes, &pd); - bool strict = ctx->strictMode; - ctx->strictMode = true; - O.objectValue()->__defineOwnProperty__(ctx, name, &pd); - ctx->strictMode = strict; + if (!O.objectValue()->__defineOwnProperty__(ctx, name, &pd)) + __qmljs_throw_type_error(ctx); return O; } @@ -665,10 +663,13 @@ Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) break; PropertyDescriptor n; toPropertyDescriptor(ctx, o->getValue(ctx, pd), &n); + bool ok; if (name) - O.objectValue()->__defineOwnProperty__(ctx, name, &n); + ok = O.objectValue()->__defineOwnProperty__(ctx, name, &n); else - O.objectValue()->__defineOwnProperty__(ctx, index, &n); + ok = O.objectValue()->__defineOwnProperty__(ctx, index, &n); + if (!ok) + __qmljs_throw_type_error(ctx); } return O; diff --git a/tests/TestExpectations b/tests/TestExpectations index c8dedea78f..01b1d87b15 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -852,128 +852,12 @@ S15.1.3.2_A5.3 failing 15.2.3.7-5-b-27 failing 15.2.3.7-5-b-52 failing 15.2.3.7-5-b-80 failing -15.2.3.7-6-a-1 failing -15.2.3.7-6-a-10 failing 15.2.3.7-6-a-110 failing -15.2.3.7-6-a-112 failing -15.2.3.7-6-a-113 failing -15.2.3.7-6-a-116 failing -15.2.3.7-6-a-117 failing -15.2.3.7-6-a-118 failing -15.2.3.7-6-a-119 failing -15.2.3.7-6-a-12 failing -15.2.3.7-6-a-13 failing -15.2.3.7-6-a-14 failing -15.2.3.7-6-a-15 failing -15.2.3.7-6-a-158 failing -15.2.3.7-6-a-16 failing -15.2.3.7-6-a-160 failing -15.2.3.7-6-a-164 failing -15.2.3.7-6-a-165 failing -15.2.3.7-6-a-166 failing -15.2.3.7-6-a-168 failing -15.2.3.7-6-a-169 failing -15.2.3.7-6-a-170 failing -15.2.3.7-6-a-172 failing -15.2.3.7-6-a-173 failing -15.2.3.7-6-a-175 failing -15.2.3.7-6-a-176 failing -15.2.3.7-6-a-18 failing -15.2.3.7-6-a-184 failing -15.2.3.7-6-a-185 failing -15.2.3.7-6-a-186 failing -15.2.3.7-6-a-188 failing -15.2.3.7-6-a-189 failing -15.2.3.7-6-a-19 failing -15.2.3.7-6-a-190 failing -15.2.3.7-6-a-192 failing -15.2.3.7-6-a-193 failing -15.2.3.7-6-a-194 failing 15.2.3.7-6-a-20 failing -15.2.3.7-6-a-21 failing -15.2.3.7-6-a-213 failing -15.2.3.7-6-a-214 failing -15.2.3.7-6-a-22 failing -15.2.3.7-6-a-227 failing -15.2.3.7-6-a-228 failing -15.2.3.7-6-a-229 failing -15.2.3.7-6-a-230 failing -15.2.3.7-6-a-233 failing -15.2.3.7-6-a-234 failing -15.2.3.7-6-a-235 failing -15.2.3.7-6-a-236 failing -15.2.3.7-6-a-237 failing -15.2.3.7-6-a-238 failing -15.2.3.7-6-a-239 failing -15.2.3.7-6-a-240 failing -15.2.3.7-6-a-241 failing -15.2.3.7-6-a-242 failing -15.2.3.7-6-a-244 failing -15.2.3.7-6-a-245 failing -15.2.3.7-6-a-25 failing -15.2.3.7-6-a-270 failing -15.2.3.7-6-a-271 failing -15.2.3.7-6-a-272 failing -15.2.3.7-6-a-273 failing -15.2.3.7-6-a-274 failing -15.2.3.7-6-a-275 failing -15.2.3.7-6-a-276 failing -15.2.3.7-6-a-277 failing 15.2.3.7-6-a-280 failing -15.2.3.7-6-a-282 failing -15.2.3.7-6-a-283 failing -15.2.3.7-6-a-284 failing -15.2.3.7-6-a-285 failing 15.2.3.7-6-a-286 failing -15.2.3.7-6-a-287 failing 15.2.3.7-6-a-288 failing 15.2.3.7-6-a-289 failing -15.2.3.7-6-a-294 failing -15.2.3.7-6-a-295 failing -15.2.3.7-6-a-296 failing -15.2.3.7-6-a-297 failing -15.2.3.7-6-a-298 failing -15.2.3.7-6-a-299 failing -15.2.3.7-6-a-3 failing -15.2.3.7-6-a-300 failing -15.2.3.7-6-a-301 failing -15.2.3.7-6-a-306 failing -15.2.3.7-6-a-307 failing -15.2.3.7-6-a-308 failing -15.2.3.7-6-a-309 failing -15.2.3.7-6-a-310 failing -15.2.3.7-6-a-311 failing -15.2.3.7-6-a-312 failing -15.2.3.7-6-a-313 failing -15.2.3.7-6-a-4 failing -15.2.3.7-6-a-45 failing -15.2.3.7-6-a-46 failing -15.2.3.7-6-a-5 failing -15.2.3.7-6-a-65 failing -15.2.3.7-6-a-66-1 failing -15.2.3.7-6-a-66 failing -15.2.3.7-6-a-67 failing -15.2.3.7-6-a-68 failing -15.2.3.7-6-a-7 failing -15.2.3.7-6-a-71 failing -15.2.3.7-6-a-72 failing -15.2.3.7-6-a-76 failing -15.2.3.7-6-a-77 failing -15.2.3.7-6-a-79 failing -15.2.3.7-6-a-8 failing -15.2.3.7-6-a-81 failing -15.2.3.7-6-a-83 failing -15.2.3.7-6-a-85 failing -15.2.3.7-6-a-86-1 failing -15.2.3.7-6-a-87 failing -15.2.3.7-6-a-88 failing -15.2.3.7-6-a-9 failing -15.2.3.7-6-a-91 failing -15.2.3.7-6-a-92 failing -15.2.3.7-6-a-93-1 failing -15.2.3.7-6-a-93-2 failing -15.2.3.7-6-a-93-3 failing -15.2.3.7-6-a-93-4 failing 15.2.3.9-2-a-12 failing 15.2.4.2-1-1 failing 15.2.4.2-1-2 failing @@ -2947,4 +2831,4 @@ S15.4.4.13_A1_T2 failing 15.4.4.19-8-c-i-6 failing 15.4.4.20-9-c-i-6 failing 15.4.4.21-8-b-iii-1-6 failing -15.4.4.22-8-b-iii-1-6 failing +15.4.4.22-8-b-iii-1-6 failing \ No newline at end of file -- cgit v1.2.3 From ab736c08c20feac9033a1a78a5007ece49b74ec9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 16 Jan 2013 12:18:47 +0100 Subject: Properly mark accessor properties During the mark phase of the GC run, we didn't mark the getters and setters in accessor properties. This fixes a few crashes in the test suite. Change-Id: Ic58b317fe1fc5c923e8c114aee94c1981afd894f Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 13 +++++++++++-- qv4array.cpp | 16 ++++++++++++++++ qv4array.h | 10 +--------- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 749e8835ed..0cec5eabcf 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -178,9 +178,18 @@ void Object::getCollectables(QVector &objects) if (members) { for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) { - if ((*it) && (*it)->descriptor.isData()) - if (Object *o = (*it)->descriptor.value.asObject()) + if (!(*it)) + continue; + PropertyDescriptor &pd = (*it)->descriptor; + if (pd.isData()) { + if (Object *o = pd.value.asObject()) objects.append(o); + } else if (pd.isAccessor()) { + if (pd.get) + objects.append(pd.get); + if (pd.set) + objects.append(pd.set); + } } } array.getCollectables(objects); diff --git a/qv4array.cpp b/qv4array.cpp index dd34a7c464..42c7ed9168 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -651,5 +651,21 @@ bool Array::setLength(uint newLen) { return ok; } +void Array::getCollectables(QVector &objects) const { + uint i = sparse ? 0 : offset; + for (; i < (uint)values.size(); ++i) { + const PropertyDescriptor &pd = values.at(i); + if (pd.isData()) { + if (Object *o = pd.value.asObject()) + objects.append(o); + } else if (pd.isAccessor()) { + if (pd.get) + objects.append(pd.get); + if (pd.set) + objects.append(pd.set); + } + } +} + } } diff --git a/qv4array.h b/qv4array.h index c36d4a9ae7..bdd37442b3 100644 --- a/qv4array.h +++ b/qv4array.h @@ -523,15 +523,7 @@ public: } } - void getCollectables(QVector &objects) const { - uint i = sparse ? 0 : offset; - for (; i < (uint)values.size(); ++i) { - const PropertyDescriptor &pd = values.at(i); - if (pd.isData()) - if (Object *o = pd.value.asObject()) - objects.append(o); - } - } + void getCollectables(QVector &objects) const; void push_front(Value v) { if (!sparse) { -- cgit v1.2.3 From 22cf4de119a9c6079c3a58828009552f0a1ee1c9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 16 Jan 2013 13:34:46 +0100 Subject: Fix index access on StringObjects Change-Id: I5dbbc59f383e2a1e6630d1e0f0ca631890d4c8e7 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 21 +++++++++++++-------- qmljs_objects.h | 8 ++++---- qv4managed.h | 7 ++++--- tests/TestExpectations | 1 - 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 0cec5eabcf..faf3a86e58 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -207,11 +207,14 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *na return 0; } -PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *, uint index) +PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, uint index) { PropertyDescriptor *p = array.at(index); if(p && p->type != PropertyDescriptor::Generic) return p; + if (isString) + return static_cast(this)->getIndex(ctx, index); + return 0; } @@ -234,13 +237,18 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, Str return 0; } -PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *, uint index) +PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, uint index) { Object *o = this; while (o) { PropertyDescriptor *p = o->array.at(index); if(p && p->type != PropertyDescriptor::Generic) return p; + if (o->isString) { + p = static_cast(o)->getIndex(ctx, index); + if (p) + return p; + } o = o->prototype; } return 0; @@ -1320,6 +1328,8 @@ void BoundFunction::getCollectables(QVector &objects) StringObject::StringObject(ExecutionContext *ctx, const Value &value) : value(value) { + isString = true; + tmpProperty.type = PropertyDescriptor::Data; tmpProperty.enumberable = PropertyDescriptor::Enabled; tmpProperty.writable = PropertyDescriptor::Disabled; @@ -1330,12 +1340,8 @@ StringObject::StringObject(ExecutionContext *ctx, const Value &value) defineReadonlyProperty(ctx->engine->id_length, Value::fromUInt32(value.stringValue()->toQString().length())); } -PropertyDescriptor *StringObject::__getOwnProperty__(ExecutionContext *ctx, uint index) +PropertyDescriptor *StringObject::getIndex(ExecutionContext *ctx, uint index) { - PropertyDescriptor *pd = Object::__getOwnProperty__(ctx, index); - if (pd) - return pd; - assert(value.isString()); QString str = value.stringValue()->toQString(); if (index >= (uint)str.length()) return 0; @@ -1344,7 +1350,6 @@ PropertyDescriptor *StringObject::__getOwnProperty__(ExecutionContext *ctx, uint return &tmpProperty; } - EvalErrorObject::EvalErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->argument(0)) { diff --git a/qmljs_objects.h b/qmljs_objects.h index 5a59f14db3..8fae05c8b0 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -121,10 +121,10 @@ struct Object: Managed { virtual ErrorObject *asErrorObject() { return 0; } virtual ArgumentsObject *asArgumentsObject() { return 0; } - virtual PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); - virtual PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); + PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); + PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name); - PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, uint index); + virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, uint index); virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); virtual Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); @@ -204,7 +204,7 @@ struct StringObject: Object { virtual QString className() { return QStringLiteral("String"); } virtual StringObject *asStringObject() { return this; } - virtual PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); + PropertyDescriptor *getIndex(ExecutionContext *ctx, uint index); }; struct DateObject: Object { diff --git a/qv4managed.h b/qv4managed.h index 3c7f04dc3e..a0472cff3f 100644 --- a/qv4managed.h +++ b/qv4managed.h @@ -64,7 +64,7 @@ private: void operator = (const Managed &other); protected: - Managed() : markBit(0), inUse(1), extensible(true), isArray(false), unused(0) { } + Managed() : markBit(0), inUse(1), extensible(true), isArray(false), isString(false), unused(0) { } virtual ~Managed(); public: @@ -81,13 +81,14 @@ protected: quintptr inUse : 1; quintptr extensible : 1; // used by Object quintptr isArray : 1; // used by Object & Array + quintptr isString : 1; // used by Object & StringObject quintptr needsActivation : 1; // used by FunctionObject quintptr usesArgumentsObject : 1; // used by FunctionObject quintptr strictMode : 1; // used by FunctionObject #if CPU(X86_64) - quintptr unused : 57; + quintptr unused : 56; #elif CPU(X86) - quintptr unused : 25; + quintptr unused : 24; #else #error "implement me" #endif diff --git a/tests/TestExpectations b/tests/TestExpectations index 01b1d87b15..41f41a8e07 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -858,7 +858,6 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-286 failing 15.2.3.7-6-a-288 failing 15.2.3.7-6-a-289 failing -15.2.3.9-2-a-12 failing 15.2.4.2-1-1 failing 15.2.4.2-1-2 failing 15.2.4.2-2-1 failing -- cgit v1.2.3 From 61d8e66e8f68b42f6db47695b944b8c700f2e32e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 16 Jan 2013 13:58:29 +0100 Subject: Fix Object.prototype.toString Change-Id: Ib9d043c97cc2ee1fa75a2f768c7d2321bcaad766 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 12 +++++++++--- tests/TestExpectations | 6 ------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 6fac204704..fe40c5565e 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -811,9 +811,15 @@ Value ObjectPrototype::method_keys(ExecutionContext *ctx) Value ObjectPrototype::method_toString(ExecutionContext *ctx) { - if (! ctx->thisObject.isObject()) - ctx->throwTypeError(); - return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(ctx->thisObject.objectValue()->className())); + if (ctx->thisObject.isUndefined()) { + return Value::fromString(ctx, QStringLiteral("[object Undefined]")); + } else if (ctx->thisObject.isNull()) { + return Value::fromString(ctx, QStringLiteral("[object Null]")); + } else { + Value obj = __qmljs_to_object(ctx->thisObject, ctx); + QString className = obj.objectValue()->className(); + return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(className)); + } } Value ObjectPrototype::method_toLocaleString(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index 41f41a8e07..6764dfdef9 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -858,12 +858,6 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-286 failing 15.2.3.7-6-a-288 failing 15.2.3.7-6-a-289 failing -15.2.4.2-1-1 failing -15.2.4.2-1-2 failing -15.2.4.2-2-1 failing -15.2.4.2-2-2 failing -S15.2.4.2_A12 failing -S15.2.4.2_A13 failing 15.3.4.5-2-7 failing 15.4.3.2-1-11 failing S15.4.4.11_A3_T1 failing -- cgit v1.2.3 From 4162e674be0537d8804c8e2352c65b46c5118555 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 16 Jan 2013 14:10:04 +0100 Subject: Fix Object.prototype.toLocaleString Change-Id: Ie4202cd78599e5f13727a5c497e3dfabe65e87e0 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index fe40c5565e..f3fd4acda5 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -824,7 +824,12 @@ Value ObjectPrototype::method_toString(ExecutionContext *ctx) Value ObjectPrototype::method_toLocaleString(ExecutionContext *ctx) { - return method_toString(ctx); + Object *o = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Value ts = o->__get__(ctx, ctx->engine->identifier(QStringLiteral("toString"))); + FunctionObject *f = ts.asFunctionObject(); + if (!f) + __qmljs_throw_type_error(ctx); + return f->call(ctx, Value::fromObject(o), 0, 0); } Value ObjectPrototype::method_valueOf(ExecutionContext *ctx) -- cgit v1.2.3 From 821e5806d9743f2b1f97b13fb9877d63f559ea18 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 16 Jan 2013 11:41:23 +0100 Subject: Rename native functions to builtin functions. Both the JIT and the compiler generate native code, but the functions have to be registered as ScriptFunction objects, not NativeFunction objects. The name BuiltinFunction prevents confusion or errors. Change-Id: Ic6dca457362f916201b3e5178fbd36c6d754fa9c Reviewed-by: Simon Hausmann --- llvm_runtime.cpp | 5 ----- qmljs_engine.cpp | 4 ++-- qmljs_engine.h | 2 +- qmljs_objects.cpp | 18 +++++++++--------- qmljs_objects.h | 4 ++-- qmljs_runtime.cpp | 5 ----- qmljs_runtime.h | 1 - 7 files changed, 14 insertions(+), 25 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index 6a0c163c13..dd5a62514b 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -83,11 +83,6 @@ void __qmljs_llvm_init_string(ExecutionContext *ctx, Value *result, const char * *result = Value::fromString(__qmljs_string_from_utf8(ctx, str)); } -void __qmljs_llvm_init_native_function(ExecutionContext *ctx, Value *result, String *name, Value (*code)(ExecutionContext *)) -{ - *result = __qmljs_init_native_function(name, code, ctx); -} - bool __qmljs_llvm_to_boolean(ExecutionContext *ctx, const Value *value) { return __qmljs_to_boolean(*value, ctx); diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 6367dcccc1..2d34b3dc8d 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -239,9 +239,9 @@ Function *ExecutionEngine::newFunction(const QString &name) return f; } -FunctionObject *ExecutionEngine::newNativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) +FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) { - NativeFunction *f = new (memoryManager) NativeFunction(scope, name, code); + BuiltinFunction *f = new (memoryManager) BuiltinFunction(scope, name, code); f->prototype = scope->engine->functionPrototype; return f; } diff --git a/qmljs_engine.h b/qmljs_engine.h index babaf1a0b0..191268f4c4 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -168,7 +168,7 @@ struct ExecutionEngine VM::Function *newFunction(const QString &name); - FunctionObject *newNativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); + FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); FunctionObject *newScriptFunction(ExecutionContext *scope, VM::Function *function); BoundFunction *newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index faf3a86e58..fbc976d54b 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -149,7 +149,7 @@ void Object::defineDefaultProperty(ExecutionContext *context, const QString &nam { Q_UNUSED(argumentCount); String *s = context->engine->identifier(name); - FunctionObject* function = context->engine->newNativeFunction(context, s, code); + FunctionObject* function = context->engine->newBuiltinFunction(context, s, code); function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); defineDefaultProperty(s, Value::fromObject(function)); } @@ -800,7 +800,7 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) prototype = scope->engine->functionPrototype; if (scope->strictMode) { - FunctionObject *thrower = scope->engine->newNativeFunction(scope, 0, __qmljs_throw_type_error); + FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); pd.configurable = PropertyDescriptor::Disabled; pd.enumberable = PropertyDescriptor::Disabled; @@ -1174,15 +1174,15 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterC if (context->strictMode) { for (uint i = 0; i < context->argumentCount; ++i) Object::__put__(context, QString::number(i), context->arguments[i]); - FunctionObject *thrower = context->engine->newNativeFunction(context, 0, __qmljs_throw_type_error); + FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, __qmljs_throw_type_error); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); pd.configurable = PropertyDescriptor::Disabled; pd.enumberable = PropertyDescriptor::Disabled; __defineOwnProperty__(context, QStringLiteral("callee"), &pd); __defineOwnProperty__(context, QStringLiteral("caller"), &pd); } else { - FunctionObject *get = context->engine->newNativeFunction(context, 0, method_getArg); - FunctionObject *set = context->engine->newNativeFunction(context, 0, method_setArg); + FunctionObject *get = context->engine->newBuiltinFunction(context, 0, method_getArg); + FunctionObject *set = context->engine->newBuiltinFunction(context, 0, method_setArg); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(get, set); pd.configurable = PropertyDescriptor::Enabled; pd.enumberable = PropertyDescriptor::Enabled; @@ -1245,20 +1245,20 @@ Value ArgumentsObject::method_setArg(ExecutionContext *ctx) return Value::undefinedValue(); } -NativeFunction::NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) +BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) : FunctionObject(scope) , code(code) { this->name = name; } -Value NativeFunction::construct(ExecutionContext *ctx) +Value BuiltinFunction::construct(ExecutionContext *ctx) { ctx->throwTypeError(); return Value::undefinedValue(); } -void NativeFunction::maybeAdjustThisObjectForDirectCall(ExecutionContext *context, Value thisArg) +void BuiltinFunction::maybeAdjustThisObjectForDirectCall(ExecutionContext *context, Value thisArg) { // Built-in functions allow for the this object to be null or undefined. This overrides // the behaviour of changing thisObject to the global object if null/undefined and allows @@ -1282,7 +1282,7 @@ BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Va len = 0; defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(len)); - FunctionObject *thrower = scope->engine->newNativeFunction(scope, 0, __qmljs_throw_type_error); + FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); pd.configurable = PropertyDescriptor::Disabled; pd.enumberable = PropertyDescriptor::Disabled; diff --git a/qmljs_objects.h b/qmljs_objects.h index 8fae05c8b0..7d376e2aae 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -288,10 +288,10 @@ protected: virtual Value construct(ExecutionContext *ctx); }; -struct NativeFunction: FunctionObject { +struct BuiltinFunction: FunctionObject { Value (*code)(ExecutionContext *); - NativeFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); + BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); virtual Value call(ExecutionContext *ctx) { return code(ctx); } virtual Value construct(ExecutionContext *ctx); virtual void maybeAdjustThisObjectForDirectCall(ExecutionContext *context, Value thisArg); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index f5853d9de0..1c969dba24 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -117,11 +117,6 @@ Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx) return Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); } -Value __qmljs_init_native_function(String *name, Value (*code)(ExecutionContext *), ExecutionContext *ctx) -{ - return Value::fromObject(ctx->engine->newNativeFunction(ctx, name, code)); -} - Value __qmljs_string_literal_undefined(ExecutionContext *ctx) { return Value::fromString(ctx->engine->identifier(QStringLiteral("undefined"))); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 48e4fa2f0d..6bd22f6df3 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -109,7 +109,6 @@ void __qmljs_builtin_define_getter_setter(Value object, String *name, Value gett // constructors Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx); -Value __qmljs_init_native_function(String *name, Value (*code)(ExecutionContext *), ExecutionContext *ctx); Bool __qmljs_is_function(Value value); -- cgit v1.2.3 From bd515f7d3731b9866a5af1e44a83725ae2a8c162 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 16 Jan 2013 15:27:33 +0100 Subject: Skip tests known to fail Change-Id: Ia4677685000da172e97d6a98b97785e64934fd27 Reviewed-by: Simon Hausmann --- tests/TestExpectations | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index 6764dfdef9..d9f6088937 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -2794,8 +2794,6 @@ S15.4.4.11_A1.4_T1 failing S15.4.4.11_A1.4_T2 failing S15.4.4.4_A1_T2 failing -# Regression introduced by qtdeclarative parser changes - # Regressions due to Object/property refactoring 15.12.2-2-1 failing 15.12.2-2-10 failing @@ -2824,4 +2822,10 @@ S15.4.4.13_A1_T2 failing 15.4.4.19-8-c-i-6 failing 15.4.4.20-9-c-i-6 failing 15.4.4.21-8-b-iii-1-6 failing -15.4.4.22-8-b-iii-1-6 failing \ No newline at end of file +15.4.4.22-8-b-iii-1-6 failing + +# Bugs in Array.prototype +15.4.4.14-9-b-i-5 failing +15.4.4.16-7-c-i-6 failing +15.4.4.17-7-c-i-6 failing + -- cgit v1.2.3 From e5f5251a020be728015068f3cd033f55f1d6b693 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 16 Jan 2013 12:00:57 +0100 Subject: Replaced unnecessary vector with a boolean. Change-Id: Iea163e59b0a8efbe79065844b77e782330fc1564 Reviewed-by: Lars Knoll --- qmljs_objects.h | 11 +++++------ qv4isel_p.cpp | 3 +-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/qmljs_objects.h b/qmljs_objects.h index 7d376e2aae..93796622fd 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -231,25 +231,24 @@ struct Function { QList formals; QList locals; - QVector nestedFunctions; - bool hasDirectEval: 1; + bool hasNestedFunctions : 1; + bool hasDirectEval : 1; bool usesArgumentsObject : 1; - bool isStrict: 1; + bool isStrict : 1; Function(const QString &name) : name(name) , code(0) , codeData(0) + , hasNestedFunctions(0) , hasDirectEval(false) , usesArgumentsObject(false) , isStrict(false) {} ~Function(); - inline bool hasNestedFunctions() const { return !nestedFunctions.isEmpty(); } - - inline bool needsActivation() const { return hasNestedFunctions() || hasDirectEval || usesArgumentsObject; } + inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; } }; struct FunctionObject: Object { diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index bff9f4a381..9326a2f7a1 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -49,8 +49,7 @@ VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngin if (local) vmFunction->locals.append(*local); - foreach (IR::Function *function, irFunction->nestedFunctions) - vmFunction->nestedFunctions.append(createFunctionMapping(engine, function)); + vmFunction->hasNestedFunctions = !irFunction->nestedFunctions.isEmpty(); if (engine->debugger) engine->debugger->mapFunction(vmFunction, irFunction); -- cgit v1.2.3 From 1387a9abb38bb36a063f482e26a2e375acee3bf7 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 16 Jan 2013 13:33:36 +0100 Subject: Include path of llvm runtime file in executable. Change-Id: I139a2ea1c7b3e0a8aeb4eb4c093d98a03c79f603 Reviewed-by: Lars Knoll --- qv4isel_llvm.cpp | 3 ++- v4.pro | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 3d95491bf3..54301d9c19 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -263,7 +263,8 @@ void InstructionSelection::buildLLVMModule(IR::Module *module, llvm::Module *llv std::string err; llvm::OwningPtr buffer; - llvm::error_code ec = llvm::MemoryBuffer::getFile(llvm::StringRef("llvm_runtime.bc"), buffer); + qDebug()<<"llvm runtime:"< Date: Wed, 16 Jan 2013 13:36:14 +0100 Subject: Make CodeGen usable without a context. When running with the LLVM backend, there is no VM context. Change-Id: Ib4e95a3c3b92f20118269da1a9430a9278beb349 Reviewed-by: Lars Knoll --- main.cpp | 22 +++++++++++++++++++--- qv4codegen.cpp | 30 ++++++++++++++++++++++++++++-- qv4codegen_p.h | 10 ++++++++++ 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/main.cpp b/main.cpp index 748294b9a1..a9485442c3 100644 --- a/main.cpp +++ b/main.cpp @@ -54,6 +54,7 @@ #include "qv4ecmaobjects_p.h" #include "qv4isel_p.h" #include "qv4mm.h" +#include "qmljs_environment.h" #include #include @@ -189,8 +190,10 @@ int compile(const QString &fileName, const QString &source, QQmlJS::LLVMOutputTy const bool parsed = parser.parseProgram(); foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { - std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn - << ": error: " << qPrintable(m.message) << std::endl; + std::cerr << qPrintable(fileName) << ':' + << m.loc.startLine << ':' + << m.loc.startColumn << ": error: " + << qPrintable(m.message) << std::endl; } if (!parsed) @@ -199,7 +202,20 @@ int compile(const QString &fileName, const QString &source, QQmlJS::LLVMOutputTy using namespace AST; Program *program = AST::cast(parser.rootNode()); - Codegen cg(0); + class MyErrorHandler: public ErrorHandler { + public: + virtual void syntaxError(QQmlJS::VM::DiagnosticMessage *message) { + for (; message; message = message->next) { + std::cerr << qPrintable(message->fileName) << ':' + << message->startLine << ':' + << message->startColumn << ": " + << (message->type == QQmlJS::VM::DiagnosticMessage::Error ? "error" : "warning") << ": " + << qPrintable(message->message) << std::endl; + } + } + } errorHandler; + + Codegen cg(&errorHandler, false); // FIXME: if the program is empty, we should we generate an empty %entry, or give an error? /*IR::Function *globalCode =*/ cg(fileName, program, &module); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 7acdde5356..7461aaa616 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -204,7 +204,7 @@ public: { Environment *e = _cg->newEnvironment(node, _env); if (!e->isStrict) - e->isStrict = _cg->_context->strictMode; + e->isStrict = _cg->_strictMode; _envStack.append(e); _env = e; } @@ -413,7 +413,28 @@ Codegen::Codegen(VM::ExecutionContext *context) , _labelledStatement(0) , _tryCleanup(0) , _context(context) + , _strictMode(context->strictMode) , _debugger(context->engine->debugger) + , _errorHandler(0) +{ +} + +Codegen::Codegen(ErrorHandler *errorHandler, bool strictMode) + : _module(0) + , _function(0) + , _block(0) + , _exitBlock(0) + , _throwBlock(0) + , _returnAddress(0) + , _mode(GlobalCode) + , _env(0) + , _loop(0) + , _labelledStatement(0) + , _tryCleanup(0) + , _context(0) + , _strictMode(strictMode) + , _debugger(0) + , _errorHandler(errorHandler) { } @@ -2490,5 +2511,10 @@ void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) msg->startLine = loc.startLine; msg->startColumn = loc.startColumn; msg->message = detail; - _context->throwSyntaxError(msg); + if (_context) + _context->throwSyntaxError(msg); + else if (_errorHandler) + _errorHandler->syntaxError(msg); + else + Q_ASSERT(!"No error handler available."); } diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 4ee9c5ac08..e6308ac68a 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -52,6 +52,7 @@ class UiParameterList; } namespace VM { +struct DiagnosticMessage; struct ExecutionContext; } @@ -59,10 +60,17 @@ namespace Debugging { class Debugger; } // namespace Debugging +class ErrorHandler +{ +public: + virtual void syntaxError(VM::DiagnosticMessage *message) = 0; +}; + class Codegen: protected AST::Visitor { public: Codegen(VM::ExecutionContext *ctx); + Codegen(ErrorHandler *errorHandler, bool strictMode); enum Mode { GlobalCode, @@ -391,7 +399,9 @@ private: QHash _envMap; QHash _functionMap; VM::ExecutionContext *_context; + bool _strictMode; Debugging::Debugger *_debugger; + ErrorHandler *_errorHandler; class ScanFunctions; }; -- cgit v1.2.3 From 0db2415f0638ead1dfa392bdaed0a7ae2645a816 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 16 Jan 2013 13:38:35 +0100 Subject: Get closures back into a working state. This includes code generation and runtime support to register a VM::Function with the engine. Change-Id: Ifd124907be440c56b0f582ff3c1409094c501943 Reviewed-by: Lars Knoll --- llvm_runtime.cpp | 20 ++ qmljs_runtime.cpp | 24 ++ qmljs_runtime.h | 6 + qv4isel_llvm.cpp | 675 +++++++++++++++++++++++------------------------------- qv4isel_llvm_p.h | 32 +-- 5 files changed, 340 insertions(+), 417 deletions(-) diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp index dd5a62514b..212c8d17d5 100644 --- a/llvm_runtime.cpp +++ b/llvm_runtime.cpp @@ -83,6 +83,21 @@ void __qmljs_llvm_init_string(ExecutionContext *ctx, Value *result, const char * *result = Value::fromString(__qmljs_string_from_utf8(ctx, str)); } +void __qmljs_llvm_init_closure(ExecutionContext *ctx, Value *result, + String *name, bool hasDirectEval, + bool usesArgumentsObject, bool isStrict, + bool hasNestedFunctions, + String **formals, unsigned formalCount, + String **locals, unsigned localCount) +{ + Function *clos = __qmljs_register_function(ctx, name, hasDirectEval, + usesArgumentsObject, isStrict, + hasNestedFunctions, + formals, formalCount, + locals, localCount); + *result = __qmljs_init_closure(clos, ctx); +} + bool __qmljs_llvm_to_boolean(ExecutionContext *ctx, const Value *value) { return __qmljs_to_boolean(*value, ctx); @@ -444,6 +459,11 @@ void __qmljs_llvm_set_property(ExecutionContext *ctx, Value *object, String *nam __qmljs_set_property(ctx, *object, name, *value); } +void __qmljs_llvm_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) +{ + __qmljs_builtin_declare_var(ctx, deletable, name); +} + void __qmljs_llvm_typeof(ExecutionContext *ctx, Value *result, const Value *value) { *result = __qmljs_builtin_typeof(*value, ctx); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 1c969dba24..0bc37da66e 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -117,6 +117,30 @@ Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx) return Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); } +Function *__qmljs_register_function(ExecutionContext *ctx, String *name, + bool hasDirectEval, + bool usesArgumentsObject, bool isStrict, + bool hasNestedFunctions, + String **formals, unsigned formalCount, + String **locals, unsigned localCount) +{ + Function *f = ctx->engine->newFunction(name ? name->toQString() : QString()); + + f->hasDirectEval = hasDirectEval; + f->usesArgumentsObject = usesArgumentsObject; + f->isStrict = isStrict; + f->hasNestedFunctions = hasNestedFunctions; + + for (unsigned i = 0; i < formalCount; ++i) + if (formals[i]) + f->formals.append(formals[i]->toQString()); + for (unsigned i = 0; i < localCount; ++i) + if (locals[i]) + f->locals.append(locals[i]->toQString()); + + return f; +} + Value __qmljs_string_literal_undefined(ExecutionContext *ctx) { return Value::fromString(ctx->engine->identifier(QStringLiteral("undefined"))); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 6bd22f6df3..1b638ef184 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -109,6 +109,12 @@ void __qmljs_builtin_define_getter_setter(Value object, String *name, Value gett // constructors Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx); +VM::Function *__qmljs_register_function(ExecutionContext *ctx, String *name, + bool hasDirectEval, + bool usesArgumentsObject, bool isStrict, + bool hasNestedFunctions, + String **formals, unsigned formalCount, + String **locals, unsigned localCount); Bool __qmljs_is_function(Value value); diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 54301d9c19..de84722d5a 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -79,6 +79,11 @@ #include "qv4isel_llvm_p.h" #include "qv4_llvm_p.h" #include "qv4ir_p.h" +#include "qv4string.h" + +namespace { +QTextStream qout(stderr, QIODevice::WriteOnly); +} // anonymous namespace namespace QQmlJS { @@ -284,7 +289,7 @@ void InstructionSelection::buildLLVMModule(IR::Module *module, llvm::Module *llv } _valueTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::Value"); - _contextPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::Context")->getPointerTo(); + _contextPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::ExecutionContext")->getPointerTo(); _stringPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::String")->getPointerTo(); { @@ -301,7 +306,33 @@ void InstructionSelection::buildLLVMModule(IR::Module *module, llvm::Module *llv void InstructionSelection::callActivationProperty(IR::Call *c, IR::Temp *temp) { - // TODO: implement instead of visitExp + IR::Name *baseName = c->base->asName(); + Q_ASSERT(baseName); + + switch (baseName->builtin) { + case IR::Name::builtin_declare_vars: { + if (!c->args) + return; + IR::Const *deletable = c->args->expr->asConst(); + assert(deletable->type == IR::BoolType); + llvm::ConstantInt *isDeletable = getInt1(deletable->value != 0); + for (IR::ExprList *it = c->args->next; it; it = it->next) { + IR::Name *arg = it->expr->asName(); + assert(arg != 0); + + llvm::Value *name = getIdentifier(*arg->id); + CreateCall3(getRuntimeFunction("__qmljs_builtin_declare_var"), + _llvmFunction->arg_begin(), isDeletable, name); + } + } return; + default: + break; + } // switch + + Q_UNIMPLEMENTED(); + c->dump(qout); + qout << endl; + assert(!"TODO!"); Q_UNREACHABLE(); } @@ -343,8 +374,9 @@ void InstructionSelection::loadThisObject(IR::Temp *temp) void InstructionSelection::loadConst(IR::Const *con, IR::Temp *temp) { - assert(!"TODO!"); - Q_UNREACHABLE(); + llvm::Value *target = getLLVMTemp(temp); + llvm::Value *source = CreateLoad(createValue(con)); + CreateStore(source, target); } void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) @@ -367,74 +399,218 @@ void InstructionSelection::getActivationProperty(const QString &name, IR::Temp * void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) { - assert(!"TODO!"); - Q_UNREACHABLE(); + llvm::Value *name = getIdentifier(targetName); + llvm::Value *src = toValuePtr(source); + CreateCall3(getRuntimeFunction("__qmljs_llvm_set_activation_property"), + _llvmFunction->arg_begin(), name, src); } void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) { - assert(!"TODO!"); - Q_UNREACHABLE(); + IR::Function *f = closure->value; + QString name; + if (f->name) + name = *f->name; + + llvm::Value *args[] = { + _llvmFunction->arg_begin(), + getLLVMTemp(target), + getIdentifier(name), + getInt1(f->hasDirectEval), + getInt1(f->usesArgumentsObject), + getInt1(f->isStrict), + getInt1(!f->nestedFunctions.isEmpty()), + genStringList(f->formals, "formals", "formal"), + getInt32(f->formals.size()), + genStringList(f->locals, "locals", "local"), + getInt32(f->locals.size()) + }; + llvm::Function *callee = _llvmModule->getFunction("__qmljs_llvm_init_closure"); + CreateCall(callee, args); } -void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::Temp *target) +void InstructionSelection::getProperty(IR::Temp *sourceBase, const QString &sourceName, IR::Temp *target) { - assert(!"TODO!"); - Q_UNREACHABLE(); + llvm::Value *base = getLLVMTempReference(sourceBase); + llvm::Value *name = getIdentifier(sourceName); + llvm::Value *t = getLLVMTemp(target); + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_property"), + _llvmFunction->arg_begin(), t, base, name); } void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) { - assert(!"TODO!"); - Q_UNREACHABLE(); + llvm::Value *base = getLLVMTempReference(targetBase); + llvm::Value *name = getIdentifier(targetName); + llvm::Value *src = toValuePtr(source); + CreateCall4(getRuntimeFunction("__qmljs_llvm_set_property"), + _llvmFunction->arg_begin(), base, name, src); } -void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) +void InstructionSelection::getElement(IR::Temp *sourceBase, IR::Temp *sourceIndex, IR::Temp *target) { assert(!"TODO!"); Q_UNREACHABLE(); + + llvm::Value *base = getLLVMTempReference(sourceBase); + llvm::Value *index = getLLVMTempReference(sourceIndex); + llvm::Value *t = getLLVMTemp(target); + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_element"), + _llvmFunction->arg_begin(), t, base, index); } void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) { - assert(!"TODO!"); - Q_UNREACHABLE(); + llvm::Value *base = getLLVMTempReference(targetBase); + llvm::Value *index = getLLVMTempReference(targetIndex); + llvm::Value *src = toValuePtr(source); + CreateCall4(getRuntimeFunction("__qmljs_llvm_set_element"), + _llvmFunction->arg_begin(), base, index, src); } void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) { - assert(!"TODO!"); - Q_UNREACHABLE(); + llvm::Value *t = getLLVMTemp(targetTemp); + llvm::Value *s = getLLVMTemp(sourceTemp); + CreateStore(s, t); } void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) { - assert(!"TODO!"); - Q_UNREACHABLE(); + const char *opName = 0; + switch (oper) { + case IR::OpNot: opName = "__qmljs_not"; break; + case IR::OpUMinus: opName = "__qmljs_uminus"; break; + case IR::OpUPlus: opName = "__qmljs_uplus"; break; + case IR::OpCompl: opName = "__qmljs_compl"; break; + case IR::OpIncrement: opName = "__qmljs_increment"; break; + case IR::OpDecrement: opName = "__qmljs_decrement"; break; + default: assert(!"unreachable"); break; + } + + if (opName) { + llvm::Value *t = getLLVMTemp(targetTemp); + llvm::Value *s = getLLVMTemp(sourceTemp); + CreateCall3(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), t, s); + } } void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) { - assert(!"TODO!"); - Q_UNREACHABLE(); + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: opName = "__qmljs_llvm_bit_and"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_bit_or"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_bit_xor"; break; + case IR::OpAdd: opName = "__qmljs_llvm_add"; break; + case IR::OpSub: opName = "__qmljs_llvm_sub"; break; + case IR::OpMul: opName = "__qmljs_llvm_mul"; break; + case IR::OpDiv: opName = "__qmljs_llvm_div"; break; + case IR::OpMod: opName = "__qmljs_llvm_mod"; break; + case IR::OpLShift: opName = "__qmljs_llvm_shl"; break; + case IR::OpRShift: opName = "__qmljs_llvm_shr"; break; + case IR::OpURShift: opName = "__qmljs_llvm_ushr"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *t = getLLVMTemp(target); + llvm::Value *s1 = toValuePtr(leftSource); + llvm::Value *s2 = toValuePtr(rightSource); + CreateCall4(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), t, s1, s2); + return; + } } void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) { - assert(!"TODO!"); - Q_UNREACHABLE(); + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_name"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_name"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_name"; break; + case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_name"; break; + case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_name"; break; + case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_name"; break; + case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_name"; break; + case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_name"; break; + case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_name"; break; + case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_name"; break; + case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_name"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *dst = getIdentifier(targetName); + llvm::Value *src = toValuePtr(sourceExpr); + CreateCall3(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), dst, src); + return; + } } void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) { - assert(!"TODO!"); - Q_UNREACHABLE(); + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_element"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_element"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_element"; break; + case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_element"; break; + case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_element"; break; + case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_element"; break; + case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_element"; break; + case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_element"; break; + case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_element"; break; + case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_element"; break; + case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_element"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *base = getLLVMTemp(targetBaseTemp); + llvm::Value *index = getLLVMTemp(targetIndexTemp); + llvm::Value *value = toValuePtr(sourceExpr); + CreateCall4(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), base, index, value); + } } void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) { - assert(!"TODO!"); - Q_UNREACHABLE(); + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_member"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_member"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_member"; break; + case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_member"; break; + case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_member"; break; + case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_member"; break; + case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_member"; break; + case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_member"; break; + case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_member"; break; + case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_member"; break; + case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_member"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *base = getLLVMTemp(targetBase); + llvm::Value *member = getIdentifier(targetName); + llvm::Value *value = toValuePtr(source); + CreateCall4(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), value, base, member); + } } llvm::Function *InstructionSelection::getLLVMFunction(IR::Function *function) @@ -486,8 +662,8 @@ llvm::Function *InstructionSelection::compileLLVMFunction(IR::Function *function CreateStore(llvm::Constant::getNullValue(_valueTy), t); } - CreateCall(_llvmModule->getFunction("__qmljs_llvm_init_this_object"), - _llvmFunction->arg_begin()); +// CreateCall(getRuntimeFunction("__qmljs_llvm_init_this_object"), +// _llvmFunction->arg_begin()); foreach (IR::BasicBlock *block, _function->basicBlocks) { qSwap(_block, block); @@ -524,29 +700,14 @@ llvm::BasicBlock *InstructionSelection::getLLVMBasicBlock(IR::BasicBlock *block) return llvmBlock; } -llvm::Value *InstructionSelection::getLLVMValue(IR::Expr *expr) -{ - llvm::Value *llvmValue = 0; - if (expr) { - qSwap(_llvmValue, llvmValue); - expr->accept(this); - qSwap(_llvmValue, llvmValue); - } - if (! llvmValue) { - expr->dump(qerr);qerr<asTemp()) return getLLVMTemp(t); + assert(!"TODO!"); llvm::Value *addr = newLLVMTemp(_valueTy); - CreateStore(getLLVMValue(expr), addr); +// CreateStore(getLLVMValue(expr), addr); return addr; } @@ -556,6 +717,10 @@ llvm::Value *InstructionSelection::getLLVMCondition(IR::Expr *expr) if (IR::Temp *t = expr->asTemp()) { value = getLLVMTemp(t); } else { + assert(!"TODO!"); + Q_UNREACHABLE(); + +#if 0 value = getLLVMValue(expr); if (! value) { Q_UNIMPLEMENTED(); @@ -565,9 +730,10 @@ llvm::Value *InstructionSelection::getLLVMCondition(IR::Expr *expr) llvm::Value *tmp = newLLVMTemp(_valueTy); CreateStore(value, tmp); value = tmp; +#endif } - return CreateCall2(_llvmModule->getFunction("__qmljs_llvm_to_boolean"), + return CreateCall2(getRuntimeFunction("__qmljs_llvm_to_boolean"), _llvmFunction->arg_begin(), value); } @@ -576,7 +742,7 @@ llvm::Value *InstructionSelection::getLLVMTemp(IR::Temp *temp) { if (temp->index < 0) { const int index = -temp->index -1; - return CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_argument"), + return CreateCall2(getRuntimeFunction("__qmljs_llvm_get_argument"), _llvmFunction->arg_begin(), getInt32(index)); } @@ -597,186 +763,11 @@ llvm::Value *InstructionSelection::getStringPtr(const QString &s) llvm::Value *InstructionSelection::getIdentifier(const QString &s) { llvm::Value *str = getStringPtr(s); - llvm::Value *id = CreateCall2(_llvmModule->getFunction("__qmljs_identifier_from_utf8"), + llvm::Value *id = CreateCall2(getRuntimeFunction("__qmljs_identifier_from_utf8"), _llvmFunction->arg_begin(), str); return id; } -void InstructionSelection::visitExp(IR::Exp *s) -{ - getLLVMValue(s->expr); -} - -void InstructionSelection::genMoveSubscript(IR::Move *s) -{ - IR::Subscript *subscript = s->target->asSubscript(); - llvm::Value *base = getLLVMTempReference(subscript->base); - llvm::Value *index = getLLVMTempReference(subscript->index); - llvm::Value *source = toValuePtr(s->source); - CreateCall4(_llvmModule->getFunction("__qmljs_llvm_set_element"), - _llvmFunction->arg_begin(), base, index, source); -} - -void InstructionSelection::genMoveMember(IR::Move *s) -{ - IR::Member *m = s->target->asMember(); - llvm::Value *base = getLLVMTempReference(m->base); - llvm::Value *name = getIdentifier(*m->name); - llvm::Value *source = toValuePtr(s->source); - CreateCall4(_llvmModule->getFunction("__qmljs_llvm_set_property"), - _llvmFunction->arg_begin(), base, name, source); -} - -void InstructionSelection::visitMove(IR::Move *s) -{ - if (s->op == IR::OpInvalid) { - if (s->target->asSubscript()) { - genMoveSubscript(s); - return; - } else if (s->target->asMember()) { - genMoveMember(s); - return; - } else if (IR::Name *n = s->target->asName()) { - llvm::Value *name = getIdentifier(*n->id); - llvm::Value *source = toValuePtr(s->source); - CreateCall3(_llvmModule->getFunction("__qmljs_llvm_set_activation_property"), - _llvmFunction->arg_begin(), name, source); - return; - } else if (IR::Temp *t = s->target->asTemp()) { - llvm::Value *target = getLLVMTemp(t); - llvm::Value *source = getLLVMValue(s->source); - CreateStore(source, target); - return; - } - } else { - if (IR::Temp *t = s->target->asTemp()) { - if (s->source->asTemp() || s->source->asConst()) { - const char *opName = 0; - switch (s->op) { - case IR::OpBitAnd: opName = "__qmljs_llvm_bit_and"; break; - case IR::OpBitOr: opName = "__qmljs_llvm_bit_or"; break; - case IR::OpBitXor: opName = "__qmljs_llvm_bit_xor"; break; - case IR::OpAdd: opName = "__qmljs_llvm_add"; break; - case IR::OpSub: opName = "__qmljs_llvm_sub"; break; - case IR::OpMul: opName = "__qmljs_llvm_mul"; break; - case IR::OpDiv: opName = "__qmljs_llvm_div"; break; - case IR::OpMod: opName = "__qmljs_llvm_mod"; break; - case IR::OpLShift: opName = "__qmljs_llvm_shl"; break; - case IR::OpRShift: opName = "__qmljs_llvm_shr"; break; - case IR::OpURShift: opName = "__qmljs_llvm_ushr"; break; - default: - Q_UNREACHABLE(); - break; - } - - if (opName) { - llvm::Value *target = getLLVMTemp(t); - llvm::Value *s1 = toValuePtr(s->target); - llvm::Value *s2 = toValuePtr(s->source); - CreateCall4(_llvmModule->getFunction(opName), - _llvmFunction->arg_begin(), target, s1, s2); - return; - } - } - } else if (IR::Name *n = s->target->asName()) { - // inplace assignment, e.g. x += 1, ++x, ... - if (s->source->asTemp() || s->source->asConst()) { - const char *opName = 0; - switch (s->op) { - case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_name"; break; - case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_name"; break; - case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_name"; break; - case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_name"; break; - case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_name"; break; - case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_name"; break; - case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_name"; break; - case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_name"; break; - case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_name"; break; - case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_name"; break; - case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_name"; break; - default: - Q_UNREACHABLE(); - break; - } - - if (opName) { - llvm::Value *dst = getIdentifier(*n->id); - llvm::Value *src = toValuePtr(s->source); - CreateCall3(_llvmModule->getFunction(opName), - _llvmFunction->arg_begin(), dst, src); - return; - } - } - } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (s->source->asTemp() || s->source->asConst()) { - const char *opName = 0; - switch (s->op) { - case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_element"; break; - case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_element"; break; - case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_element"; break; - case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_element"; break; - case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_element"; break; - case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_element"; break; - case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_element"; break; - case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_element"; break; - case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_element"; break; - case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_element"; break; - case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_element"; break; - default: - Q_UNREACHABLE(); - break; - } - - if (opName) { - llvm::Value *base = getLLVMTemp(ss->base->asTemp()); - llvm::Value *index = getLLVMTemp(ss->index->asTemp()); - llvm::Value *value = toValuePtr(s->source); - CreateCall4(_llvmModule->getFunction(opName), - _llvmFunction->arg_begin(), base, index, value); - // TODO: checkExceptions(); - } - return; - } - } else if (IR::Member *m = s->target->asMember()) { - if (s->source->asTemp() || s->source->asConst()) { - const char *opName = 0; - switch (s->op) { - case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_member"; break; - case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_member"; break; - case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_member"; break; - case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_member"; break; - case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_member"; break; - case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_member"; break; - case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_member"; break; - case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_member"; break; - case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_member"; break; - case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_member"; break; - case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_member"; break; - default: - Q_UNREACHABLE(); - break; - } - - if (opName) { - llvm::Value *base = getLLVMTemp(m->base->asTemp()); - llvm::Value *member = getIdentifier(*m->name); - llvm::Value *value = toValuePtr(s->source); - CreateCall4(_llvmModule->getFunction(opName), - _llvmFunction->arg_begin(), value, base, member); - // TODO: checkExceptions(); - } - return; - } - } - } - - // For anything else: - s->dump(qerr, IR::Stmt::HIR); - qerr << endl; - Q_UNIMPLEMENTED(); - return; -} - void InstructionSelection::visitJump(IR::Jump *s) { CreateBr(getLLVMBasicBlock(s->target)); @@ -795,156 +786,20 @@ void InstructionSelection::visitRet(IR::Ret *s) assert(t != 0); llvm::Value *result = getLLVMTemp(t); llvm::Value *ctx = _llvmFunction->arg_begin(); - CreateCall2(_llvmModule->getFunction("__qmljs_llvm_return"), ctx, result); + CreateCall2(getRuntimeFunction("__qmljs_llvm_return"), ctx, result); CreateRetVoid(); } -void InstructionSelection::visitConst(IR::Const *e) -{ - llvm::Value *tmp = createValue(e); - - _llvmValue = CreateLoad(tmp); -} - +#if 0 void InstructionSelection::visitString(IR::String *e) { llvm::Value *tmp = newLLVMTemp(_valueTy); - CreateCall3(_llvmModule->getFunction("__qmljs_llvm_init_string"), + CreateCall3(getRuntimeFunction("__qmljs_llvm_init_string"), _llvmFunction->arg_begin(), tmp, getStringPtr(*e->value)); _llvmValue = CreateLoad(tmp); } - -void InstructionSelection::visitRegExp(IR::RegExp *e) -{ - e->dump(qerr); - qerr << endl; - Q_UNIMPLEMENTED(); - _llvmValue = llvm::Constant::getNullValue(_valueTy); -} - -void InstructionSelection::visitName(IR::Name *e) -{ - llvm::Value *result = newLLVMTemp(_valueTy); - - if (e->id == QStringLiteral("this")) { - CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_this_object"), - _llvmFunction->arg_begin(), result); - } else { - llvm::Value *name = getIdentifier(*e->id); - CreateCall3(_llvmModule->getFunction("__qmljs_llvm_get_activation_property"), - _llvmFunction->arg_begin(), result, name); - } - _llvmValue = CreateLoad(result); - -} - -void InstructionSelection::visitTemp(IR::Temp *e) -{ - if (llvm::Value *t = getLLVMTemp(e)) { - _llvmValue = CreateLoad(t); - } -} - -void InstructionSelection::visitClosure(IR::Closure *e) -{ - llvm::Value *tmp = newLLVMTemp(_valueTy); - llvm::Value *clos = getLLVMFunction(e->value); - assert("!broken: pass function name!"); - CreateCall3(_llvmModule->getFunction("__qmljs_llvm_init_native_function"), - _llvmFunction->arg_begin(), tmp, clos); - _llvmValue = CreateLoad(tmp); -} - -void InstructionSelection::visitUnop(IR::Unop *e) -{ - llvm::Value *result = newLLVMTemp(_valueTy); - genUnop(result, e); - _llvmValue = CreateLoad(result); -} - -void InstructionSelection::visitBinop(IR::Binop *e) -{ - llvm::Value *result = newLLVMTemp(_valueTy); - genBinop(result, e); - _llvmValue = CreateLoad(result); -} - -void InstructionSelection::genUnop(llvm::Value *result, IR::Unop *e) -{ - IR::Temp *t = e->expr->asTemp(); - assert(t != 0); - - llvm::Value *expr = getLLVMTemp(t); - llvm::Value *op = 0; - - switch (e->op) { - default: - Q_UNREACHABLE(); - break; - - case IR::OpNot: op = _llvmModule->getFunction("__qmljs_llvm_not"); break; - case IR::OpUMinus: op = _llvmModule->getFunction("__qmljs_llvm_uminus"); break; - case IR::OpUPlus: op = _llvmModule->getFunction("__qmljs_llvm_uplus"); break; - case IR::OpCompl: op = _llvmModule->getFunction("__qmljs_llvm_compl"); break; - } - - CreateCall3(op, _llvmFunction->arg_begin(), result, expr); -} - -void InstructionSelection::genBinop(llvm::Value *result, IR::Binop *e) -{ - assert(e->left->asTemp() || e->left->asConst()); - assert(e->right->asTemp() || e->right->asConst()); - - llvm::Value *left = toValuePtr(e->left); - llvm::Value *right = toValuePtr(e->right); - llvm::Value *op = 0; - switch (e->op) { - case IR::OpInvalid: - case IR::OpIfTrue: - case IR::OpNot: - case IR::OpUMinus: - case IR::OpUPlus: - case IR::OpCompl: - Q_UNREACHABLE(); - break; - - case IR::OpIncrement: - case IR::OpDecrement: - assert(!"TODO!"); - break; - - case IR::OpBitAnd: op = _llvmModule->getFunction("__qmljs_llvm_bit_and"); break; - case IR::OpBitOr: op = _llvmModule->getFunction("__qmljs_llvm_bit_or"); break; - case IR::OpBitXor: op = _llvmModule->getFunction("__qmljs_llvm_bit_xor"); break; - case IR::OpAdd: op = _llvmModule->getFunction("__qmljs_llvm_add"); break; - case IR::OpSub: op = _llvmModule->getFunction("__qmljs_llvm_sub"); break; - case IR::OpMul: op = _llvmModule->getFunction("__qmljs_llvm_mul"); break; - case IR::OpDiv: op = _llvmModule->getFunction("__qmljs_llvm_div"); break; - case IR::OpMod: op = _llvmModule->getFunction("__qmljs_llvm_mod"); break; - case IR::OpLShift: op = _llvmModule->getFunction("__qmljs_llvm_shl"); break; - case IR::OpRShift: op = _llvmModule->getFunction("__qmljs_llvm_shr"); break; - case IR::OpURShift: op = _llvmModule->getFunction("__qmljs_llvm_ushr"); break; - case IR::OpGt: op = _llvmModule->getFunction("__qmljs_llvm_gt"); break; - case IR::OpLt: op = _llvmModule->getFunction("__qmljs_llvm_lt"); break; - case IR::OpGe: op = _llvmModule->getFunction("__qmljs_llvm_ge"); break; - case IR::OpLe: op = _llvmModule->getFunction("__qmljs_llvm_le"); break; - case IR::OpEqual: op = _llvmModule->getFunction("__qmljs_llvm_eq"); break; - case IR::OpNotEqual: op = _llvmModule->getFunction("__qmljs_llvm_ne"); break; - case IR::OpStrictEqual: op = _llvmModule->getFunction("__qmljs_llvm_se"); break; - case IR::OpStrictNotEqual: op = _llvmModule->getFunction("__qmljs_llvm_sne"); break; - case IR::OpInstanceof: op = _llvmModule->getFunction("__qmljs_llvm_instanceof"); break; - case IR::OpIn: op = _llvmModule->getFunction("__qmljs_llvm_in"); break; - - case IR::OpAnd: - case IR::OpOr: - Q_UNREACHABLE(); - break; - } - - CreateCall4(op, _llvmFunction->arg_begin(), result, left, right); -} +#endif llvm::AllocaInst *InstructionSelection::newLLVMTemp(llvm::Type *type, llvm::Value *size) { @@ -967,8 +822,8 @@ llvm::Value * InstructionSelection::genArguments(IR::ExprList *exprs, int &argc) int i = 0; for (IR::ExprList *it = exprs; it; it = it->next) { - llvm::Value *arg = getLLVMValue(it->expr); - CreateStore(arg, CreateConstGEP1_32(args, i++)); +// llvm::Value *arg = getLLVMValue(it->expr); +// CreateStore(arg, CreateConstGEP1_32(args, i++)); } return args; @@ -995,7 +850,7 @@ void InstructionSelection::genCallMember(IR::Call *e, llvm::Value *result) getInt32(argc) }; - CreateCall(_llvmModule->getFunction("__qmljs_llvm_call_property"), llvm::ArrayRef(actuals)); + CreateCall(getRuntimeFunction("__qmljs_llvm_call_property"), llvm::ArrayRef(actuals)); _llvmValue = CreateLoad(result); } @@ -1020,7 +875,7 @@ void InstructionSelection::genConstructMember(IR::New *e, llvm::Value *result) getInt32(argc) }; - CreateCall(_llvmModule->getFunction("__qmljs_llvm_construct_property"), llvm::ArrayRef(actuals)); + CreateCall(getRuntimeFunction("__qmljs_llvm_construct_property"), llvm::ArrayRef(actuals)); _llvmValue = CreateLoad(result); } @@ -1045,7 +900,7 @@ void InstructionSelection::genCallTemp(IR::Call *e, llvm::Value *result) getInt32(argc) }; - CreateCall(_llvmModule->getFunction("__qmljs_llvm_call_value"), actuals); + CreateCall(getRuntimeFunction("__qmljs_llvm_call_value"), actuals); _llvmValue = CreateLoad(result); } @@ -1068,7 +923,7 @@ void InstructionSelection::genConstructTemp(IR::New *e, llvm::Value *result) getInt32(argc) }; - CreateCall(_llvmModule->getFunction("__qmljs_llvm_construct_value"), actuals); + CreateCall(getRuntimeFunction("__qmljs_llvm_construct_value"), actuals); _llvmValue = CreateLoad(result); } @@ -1086,49 +941,49 @@ void InstructionSelection::genCallName(IR::Call *e, llvm::Value *result) break; case IR::Name::builtin_typeof: - CreateCall3(_llvmModule->getFunction("__qmljs_llvm_typeof"), + CreateCall3(getRuntimeFunction("__qmljs_llvm_typeof"), _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); _llvmValue = CreateLoad(result); return; case IR::Name::builtin_throw: - CreateCall2(_llvmModule->getFunction("__qmljs_llvm_throw"), + CreateCall2(getRuntimeFunction("__qmljs_llvm_throw"), _llvmFunction->arg_begin(), getLLVMTempReference(e->args->expr)); _llvmValue = llvm::UndefValue::get(_valueTy); return; case IR::Name::builtin_create_exception_handler: - CreateCall2(_llvmModule->getFunction("__qmljs_llvm_create_exception_handler"), + CreateCall2(getRuntimeFunction("__qmljs_llvm_create_exception_handler"), _llvmFunction->arg_begin(), result); _llvmValue = CreateLoad(result); return; case IR::Name::builtin_delete_exception_handler: - CreateCall(_llvmModule->getFunction("__qmljs_llvm_delete_exception_handler"), + CreateCall(getRuntimeFunction("__qmljs_llvm_delete_exception_handler"), _llvmFunction->arg_begin()); return; case IR::Name::builtin_get_exception: - CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_exception"), + CreateCall2(getRuntimeFunction("__qmljs_llvm_get_exception"), _llvmFunction->arg_begin(), result); _llvmValue = CreateLoad(result); return; case IR::Name::builtin_foreach_iterator_object: - CreateCall3(_llvmModule->getFunction("__qmljs_llvm_foreach_iterator_object"), + CreateCall3(getRuntimeFunction("__qmljs_llvm_foreach_iterator_object"), _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); _llvmValue = CreateLoad(result); return; case IR::Name::builtin_foreach_next_property_name: - CreateCall2(_llvmModule->getFunction("__qmljs_llvm_foreach_next_property_name"), + CreateCall2(getRuntimeFunction("__qmljs_llvm_foreach_next_property_name"), result, getLLVMTempReference(e->args->expr)); _llvmValue = CreateLoad(result); return; case IR::Name::builtin_delete: { if (IR::Subscript *subscript = e->args->expr->asSubscript()) { - CreateCall4(_llvmModule->getFunction("__qmljs_llvm_delete_subscript"), + CreateCall4(getRuntimeFunction("__qmljs_llvm_delete_subscript"), _llvmFunction->arg_begin(), result, getLLVMTempReference(subscript->base), @@ -1136,7 +991,7 @@ void InstructionSelection::genCallName(IR::Call *e, llvm::Value *result) _llvmValue = CreateLoad(result); return; } else if (IR::Member *member = e->args->expr->asMember()) { - CreateCall4(_llvmModule->getFunction("__qmljs_llvm_delete_member"), + CreateCall4(getRuntimeFunction("__qmljs_llvm_delete_member"), _llvmFunction->arg_begin(), result, getLLVMTempReference(member->base), @@ -1144,14 +999,14 @@ void InstructionSelection::genCallName(IR::Call *e, llvm::Value *result) _llvmValue = CreateLoad(result); return; } else if (IR::Name *name = e->args->expr->asName()) { - CreateCall3(_llvmModule->getFunction("__qmljs_llvm_delete_property"), + CreateCall3(getRuntimeFunction("__qmljs_llvm_delete_property"), _llvmFunction->arg_begin(), result, getIdentifier(*name->id)); _llvmValue = CreateLoad(result); return; } else { - CreateCall3(_llvmModule->getFunction("__qmljs_llvm_delete_value"), + CreateCall3(getRuntimeFunction("__qmljs_llvm_delete_value"), _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); @@ -1169,7 +1024,7 @@ void InstructionSelection::genCallName(IR::Call *e, llvm::Value *result) int argc = 0; llvm::Value *args = genArguments(e->args, argc); - CreateCall5(_llvmModule->getFunction("__qmljs_llvm_call_activation_property"), + CreateCall5(getRuntimeFunction("__qmljs_llvm_call_activation_property"), _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); _llvmValue = CreateLoad(result); @@ -1191,13 +1046,14 @@ void InstructionSelection::genConstructName(IR::New *e, llvm::Value *result) int argc = 0; llvm::Value *args = genArguments(e->args, argc); - CreateCall5(_llvmModule->getFunction("__qmljs_llvm_construct_activation_property"), + CreateCall5(getRuntimeFunction("__qmljs_llvm_construct_activation_property"), _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); _llvmValue = CreateLoad(result); } } +#if 0 void InstructionSelection::visitCall(IR::Call *e) { if (e->base->asMember()) { @@ -1214,14 +1070,16 @@ void InstructionSelection::visitCall(IR::Call *e) llvm::Value *result = newLLVMTemp(_valueTy); CreateStore(llvm::Constant::getNullValue(_valueTy), result); - CreateCall5(_llvmModule->getFunction("__qmljs_llvm_call_value"), + CreateCall5(getRuntimeFunction("__qmljs_llvm_call_value"), _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); _llvmValue = CreateLoad(result); } else { Q_UNIMPLEMENTED(); } } +#endif +#if 0 void InstructionSelection::visitNew(IR::New *e) { if (e->base->asMember()) { @@ -1238,34 +1096,50 @@ void InstructionSelection::visitNew(IR::New *e) llvm::Value *result = newLLVMTemp(_valueTy); CreateStore(llvm::Constant::getNullValue(_valueTy), result); - CreateCall5(_llvmModule->getFunction("__qmljs_llvm_construct_value"), + CreateCall5(getRuntimeFunction("__qmljs_llvm_construct_value"), _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); _llvmValue = CreateLoad(result); } else { Q_UNIMPLEMENTED(); } } +#endif +#if 0 void InstructionSelection::visitSubscript(IR::Subscript *e) { llvm::Value *result = newLLVMTemp(_valueTy); llvm::Value *base = getLLVMTempReference(e->base); llvm::Value *index = getLLVMTempReference(e->index); - CreateCall4(_llvmModule->getFunction("__qmljs_llvm_get_element"), + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_element"), _llvmFunction->arg_begin(), result, base, index); _llvmValue = CreateLoad(result); } +#endif +#if 0 void InstructionSelection::visitMember(IR::Member *e) { llvm::Value *result = newLLVMTemp(_valueTy); llvm::Value *base = getLLVMTempReference(e->base); llvm::Value *name = getIdentifier(*e->name); - CreateCall4(_llvmModule->getFunction("__qmljs_llvm_get_property"), + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_property"), _llvmFunction->arg_begin(), result, base, name); _llvmValue = CreateLoad(result); } +#endif + +llvm::Function *InstructionSelection::getRuntimeFunction(llvm::StringRef str) +{ + llvm::Function *func = _llvmModule->getFunction(str); + if (!func) { + std::cerr << "Cannot find runtime function \"" + << str.str() << "\"!" << std::endl; + assert(func); + } + return func; +} llvm::Value *InstructionSelection::createValue(IR::Const *e) { @@ -1273,20 +1147,20 @@ llvm::Value *InstructionSelection::createValue(IR::Const *e) switch (e->type) { case IR::UndefinedType: - CreateCall(_llvmModule->getFunction("__qmljs_llvm_init_undefined"), tmp); + CreateCall(getRuntimeFunction("__qmljs_llvm_init_undefined"), tmp); break; case IR::NullType: - CreateCall(_llvmModule->getFunction("__qmljs_llvm_init_null"), tmp); + CreateCall(getRuntimeFunction("__qmljs_llvm_init_null"), tmp); break; case IR::BoolType: - CreateCall2(_llvmModule->getFunction("__qmljs_llvm_init_boolean"), tmp, + CreateCall2(getRuntimeFunction("__qmljs_llvm_init_boolean"), tmp, getInt1(e->value ? 1 : 0)); break; case IR::NumberType: - CreateCall2(_llvmModule->getFunction("__qmljs_llvm_init_number"), tmp, + CreateCall2(getRuntimeFunction("__qmljs_llvm_init_number"), tmp, llvm::ConstantFP::get(_numberTy, e->value)); break; @@ -1307,3 +1181,20 @@ llvm::Value *InstructionSelection::toValuePtr(IR::Expr *e) Q_UNREACHABLE(); } } + +llvm::Value *InstructionSelection::genStringList(const QList &strings, const char *arrayName, const char *elementName) +{ + llvm::Value *array = CreateAlloca(_stringPtrTy, getInt32(strings.size()), + arrayName); + for (int i = 0, ei = strings.size(); i < ei; ++i) { + llvm::Value *el; + if (const QString *string = strings.at(i)) + el = getIdentifier(*string); + else + el = llvm::Constant::getNullValue(_stringPtrTy); + llvm::Value *ptr = CreateGEP(array, getInt32(i), elementName); + CreateStore(el, ptr); + } + + return array; +} diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index cb5775a25e..53d9a8deca 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -62,8 +62,7 @@ namespace LLVM { class InstructionSelection: public llvm::IRBuilder<>, - public IR::InstructionSelection, - protected IR::ExprVisitor + public IR::InstructionSelection { public: InstructionSelection(llvm::LLVMContext &context); @@ -84,9 +83,9 @@ public: // methods from InstructionSelection: virtual void getActivationProperty(const QString &name, IR::Temp *temp); virtual void setActivationProperty(IR::Expr *source, const QString &targetName); virtual void initClosure(IR::Closure *closure, IR::Temp *target); - virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); + virtual void getProperty(IR::Temp *sourceBase, const QString &sourceName, IR::Temp *target); virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); - virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target); + virtual void getElement(IR::Temp *sourceBase, IR::Temp *sourceIndex, IR::Temp *target); virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); @@ -96,38 +95,20 @@ public: // methods from InstructionSelection: virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); public: // visitor methods for StmtVisitor: - virtual void visitExp(IR::Exp *); // TODO: remove - virtual void visitMove(IR::Move *); virtual void visitJump(IR::Jump *); virtual void visitCJump(IR::CJump *); virtual void visitRet(IR::Ret *); -public: // visitor methods for ExprVisitor: - virtual void visitConst(IR::Const *); - virtual void visitString(IR::String *); - virtual void visitRegExp(IR::RegExp *); - virtual void visitName(IR::Name *); - virtual void visitTemp(IR::Temp *); - virtual void visitClosure(IR::Closure *); - virtual void visitUnop(IR::Unop *); - virtual void visitBinop(IR::Binop *); - virtual void visitCall(IR::Call *); - virtual void visitNew(IR::New *); - virtual void visitSubscript(IR::Subscript *); - virtual void visitMember(IR::Member *); - private: + llvm::Function *getRuntimeFunction(llvm::StringRef str); llvm::Function *getLLVMFunction(IR::Function *function); llvm::Function *compileLLVMFunction(IR::Function *function); llvm::BasicBlock *getLLVMBasicBlock(IR::BasicBlock *block); - llvm::Value *getLLVMValue(IR::Expr *expr); llvm::Value *getLLVMTempReference(IR::Expr *expr); llvm::Value *getLLVMCondition(IR::Expr *expr); llvm::Value *getLLVMTemp(IR::Temp *temp); llvm::Value *getStringPtr(const QString &s); llvm::Value *getIdentifier(const QString &s); - void genUnop(llvm::Value *result, IR::Unop *e); - void genBinop(llvm::Value *result, IR::Binop *e); llvm::AllocaInst *newLLVMTemp(llvm::Type *type, llvm::Value *size = 0); llvm::Value * genArguments(IR::ExprList *args, int &argc); void genCallTemp(IR::Call *e, llvm::Value *result = 0); @@ -136,10 +117,11 @@ private: void genConstructTemp(IR::New *e, llvm::Value *result = 0); void genConstructName(IR::New *e, llvm::Value *result = 0); void genConstructMember(IR::New *e, llvm::Value *result = 0); - void genMoveSubscript(IR::Move *s); - void genMoveMember(IR::Move *s); llvm::Value *createValue(IR::Const *e); llvm::Value *toValuePtr(IR::Expr *e); + llvm::Value *genStringList(const QList &strings, + const char *arrayName, const char *elementName); + private: llvm::Module *_llvmModule; -- cgit v1.2.3 From 1925eb6c9a45ae10815d8bca0755ec873fe4eb59 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 16 Jan 2013 15:49:42 +0100 Subject: Moved callBuiltin into the common super class. Now the backends do not need to duplicate the tedious switch, but can just implement the specific methods. It also prevents backends from missing out on cases. Change-Id: I45a25c6955af584f60b6e69cf6a616ca3e2dc640 Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 7 - moth/qv4isel_moth.cpp | 407 ++++++++++++++++++++++++------------------------- moth/qv4isel_moth_p.h | 27 +++- moth/qv4vme_moth.cpp | 12 +- qv4isel_llvm.cpp | 184 ++++++++++++++++++---- qv4isel_llvm_p.h | 22 ++- qv4isel_masm.cpp | 241 +++++++++++++---------------- qv4isel_masm_p.h | 22 ++- qv4isel_p.cpp | 148 +++++++++++++++++- qv4isel_p.h | 25 ++- 10 files changed, 699 insertions(+), 396 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index fd0dbb4cfc..8136a68d24 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -22,7 +22,6 @@ F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ F(CallBuiltinDeleteName, callBuiltinDeleteName) \ - F(CallBuiltinDeleteValue, callBuiltinDeleteValue) \ F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \ F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \ F(CallBuiltinTypeofName, callBuiltinTypeofName) \ @@ -185,11 +184,6 @@ union Instr VM::String *name; int targetTempIndex; }; - struct instr_callBuiltinDeleteValue { - MOTH_INSTR_HEADER - int tempIndex; - int targetTempIndex; - }; struct instr_callBuiltinTypeofMember { MOTH_INSTR_HEADER int base; @@ -322,7 +316,6 @@ union Instr instr_callBuiltinDeleteMember callBuiltinDeleteMember; instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; instr_callBuiltinDeleteName callBuiltinDeleteName; - instr_callBuiltinDeleteValue callBuiltinDeleteValue; instr_callBuiltinTypeofMember callBuiltinTypeofMember; instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript; instr_callBuiltinTypeofName callBuiltinTypeofName; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 01a58667d9..12675344b4 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -327,220 +327,20 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) delete[] codeStart; } -void InstructionSelection::callActivationProperty(IR::Call *c, IR::Temp *temp) -{ - const int targetTempIndex = temp ? temp->index : scratchTempIndex(); -; - IR::Name *baseName = c->base->asName(); - Q_ASSERT(baseName); - - switch (baseName->builtin) { - case IR::Name::builtin_invalid: { - const int scratchIndex = scratchTempIndex(); - - Instruction::LoadName load; - load.name = engine()->newString(*baseName->id); - load.targetTempIndex = scratchIndex; - addInstruction(load); - - Instruction::CallValue call; - prepareCallArgs(c->args, call.argc, call.args); - call.destIndex = scratchIndex; - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } break; - - case IR::Name::builtin_typeof: { - if (IR::Member *m = c->args->expr->asMember()) { - Instruction::CallBuiltinTypeofMember call; - call.base = m->base->asTemp()->index; - call.member = engine()->identifier(*m->name); - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } else if (IR::Subscript *ss = c->args->expr->asSubscript()) { - Instruction::CallBuiltinTypeofSubscript call; - call.base = ss->base->asTemp()->index; - call.index = ss->index->asTemp()->index; - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } else if (IR::Name *n = c->args->expr->asName()) { - Instruction::CallBuiltinTypeofName call; - call.name = engine()->identifier(*n->id); - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } else if (IR::Temp *arg = c->args->expr->asTemp()){ - assert(arg != 0); - Instruction::CallBuiltinTypeofValue call; - call.tempIndex = arg->index; - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } else { - assert(false); - } - } break; - - case IR::Name::builtin_delete: { - if (IR::Member *m = c->args->expr->asMember()) { - Instruction::CallBuiltinDeleteMember call; - call.base = m->base->asTemp()->index; - call.member = engine()->newString(*m->name); - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } else if (IR::Subscript *ss = c->args->expr->asSubscript()) { - Instruction::CallBuiltinDeleteSubscript call; - call.base = ss->base->asTemp()->index; - call.index = ss->index->asTemp()->index; - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } else if (IR::Name *n = c->args->expr->asName()) { - Instruction::CallBuiltinDeleteName call; - call.name = engine()->newString(*n->id); - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } else { - Instruction::CallBuiltinDeleteValue call; - call.tempIndex = c->args->expr->asTemp()->index; - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } - } break; - - case IR::Name::builtin_throw: { - IR::Temp *arg = c->args->expr->asTemp(); - assert(arg != 0); - - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_throw; - prepareCallArgs(c->args, call.argc, call.args); - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } break; - - case IR::Name::builtin_rethrow: { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_rethrow; - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } break; - - case IR::Name::builtin_create_exception_handler: { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_create_exception_handler; - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } break; - - case IR::Name::builtin_delete_exception_handler: { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_delete_exception_handler; - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } break; - - case IR::Name::builtin_get_exception: { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_get_exception; - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } break; - - case IR::Name::builtin_foreach_iterator_object: { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_foreach_iterator_object; - prepareCallArgs(c->args, call.argc, call.args); - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } break; - - case IR::Name::builtin_foreach_next_property_name: { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_foreach_next_property_name; - prepareCallArgs(c->args, call.argc, call.args); - call.targetTempIndex = targetTempIndex; - addInstruction(call); - } break; - - case IR::Name::builtin_push_with: { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_push_with; - prepareCallArgs(c->args, call.argc, call.args); - assert(call.argc == 1); - addInstruction(call); - } break; - - case IR::Name::builtin_pop_with: { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_pop_with; - addInstruction(call); - } break; - - case IR::Name::builtin_declare_vars: if (c->args) { - IR::Const *deletable = c->args->expr->asConst(); - assert(deletable->type == IR::BoolType); - const bool isDeletable = deletable->value != 0; - for (IR::ExprList *it = c->args->next; it; it = it->next) { - Instruction::CallBuiltinDeclareVar call; - call.isDeletable = isDeletable; - call.varName = engine()->newString(*it->expr->asName()->id); - addInstruction(call); - } - } break; - - case IR::Name::builtin_define_getter_setter: { - if (!c->args) - return; - IR::ExprList *args = c->args; - Instruction::CallBuiltinDefineGetterSetter call; - call.objectTemp = args->expr->asTemp()->index; - args = args->next; - assert(args); - call.name = engine()->newString(*args->expr->asName()->id); - args = args->next; - assert(args); - call.getterTemp = args->expr->asTemp()->index; - args = args->next; - assert(args); - call.setterTemp = args->expr->asTemp()->index; - addInstruction(call); - } break; - - case IR::Name::builtin_define_property: { - if (!c->args) - return; - IR::ExprList *args = c->args; - Instruction::CallBuiltinDefineProperty call; - call.objectTemp = args->expr->asTemp()->index; - args = args->next; - assert(args); - call.name = engine()->newString(*args->expr->asName()->id); - args = args->next; - assert(args); - call.valueTemp = args->expr->asTemp()->index; - addInstruction(call); - } break; - - default: - Q_UNIMPLEMENTED(); - c->dump(qout); qout << endl; - } -} - -void InstructionSelection::callValue(IR::Call *c, IR::Temp *temp) +void InstructionSelection::callValue(IR::Call *c, IR::Temp *result) { - const int targetTempIndex = temp ? temp->index : scratchTempIndex(); IR::Temp *t = c->base->asTemp(); Q_ASSERT(t); Instruction::CallValue call; prepareCallArgs(c->args, call.argc, call.args); call.destIndex = t->index; - call.targetTempIndex = targetTempIndex; + call.targetTempIndex = result ? result->index : scratchTempIndex(); addInstruction(call); } -void InstructionSelection::callProperty(IR::Call *c, IR::Temp *temp) +void InstructionSelection::callProperty(IR::Call *c, IR::Temp *result) { - const int targetTempIndex = temp ? temp->index : scratchTempIndex(); IR::Member *m = c->base->asMember(); Q_ASSERT(m); @@ -549,7 +349,7 @@ void InstructionSelection::callProperty(IR::Call *c, IR::Temp *temp) call.baseTemp = m->base->asTemp()->index; call.name = engine()->newString(*m->name); prepareCallArgs(c->args, call.argc, call.args); - call.targetTempIndex = targetTempIndex; + call.targetTempIndex = result ? result->index : scratchTempIndex(); addInstruction(call); } @@ -804,11 +604,15 @@ void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR: addInstruction(imo); } -void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 &args) +void InstructionSelection::prepareCallArg(IR::Expr *e, quint32 &argc, quint32 &args) { - argc = 0; - args = 0; + // We pass single arguments as references to the stack, but only if it's not a local or an argument. + argc = 1; + args = e->asTemp()->index; +} +void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 &args) +{ bool singleArgIsTemp = false; if (e && e->next == 0) { // ok, only 1 argument in the cal... @@ -822,12 +626,12 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint if (singleArgIsTemp) { // We pass single arguments as references to the stack, but only if it's not a local or an argument. - argc = 1; - args = e->expr->asTemp()->index; + prepareCallArg(e->expr, argc, args); } else if (e) { // We need to move all the temps into the function arg array int argLocation = outgoingArgumentTempStart(); assert(argLocation >= 0); + argc = 0; args = argLocation; while (e) { Instruction::MoveTemp move; @@ -888,6 +692,191 @@ void InstructionSelection::visitRet(IR::Ret *s) addInstruction(ret); } +void InstructionSelection::callBuiltinInvalid(IR::Expr *func, IR::ExprList *args, IR::Temp *result) +{ + const int scratchIndex = scratchTempIndex(); + + Instruction::LoadName load; + load.name = engine()->newString(*func->asCall()->base->asName()->id); + load.targetTempIndex = scratchIndex; + addInstruction(load); + + const int targetTempIndex = result ? result->index : scratchTempIndex(); + + Instruction::CallValue call; + prepareCallArgs(args, call.argc, call.args); + call.destIndex = scratchIndex; + call.targetTempIndex = targetTempIndex; + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinTypeofMember call; + call.base = base->index; + call.member = engine()->identifier(name); + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + Instruction::CallBuiltinTypeofSubscript call; + call.base = base->index; + call.index = index->index; + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinTypeofName call; + call.name = engine()->identifier(name); + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) +{ + Instruction::CallBuiltinTypeofValue call; + call.tempIndex = value->index; + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinDeleteMember call; + call.base = base->index; + call.member = engine()->newString(name); + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + Instruction::CallBuiltinDeleteSubscript call; + call.base = base->index; + call.index = index->index; + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinDeleteName call; + call.name = engine()->newString(name); + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) +{ + Instruction::LoadValue load; + load.value = VM::Value::fromBoolean(false); + load.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(load); +} + +void InstructionSelection::callBuiltinThrow(IR::Temp *arg) +{ + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_throw; + prepareCallArg(arg, call.argc, call.args); + addInstruction(call); +} + +void InstructionSelection::callBuiltinRethrow() +{ + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_rethrow; + addInstruction(call); +} + +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) +{ + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_create_exception_handler; + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteExceptionHandler() +{ + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_delete_exception_handler; + addInstruction(call); +} + +void InstructionSelection::callBuiltinGetException(IR::Temp *result) +{ + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_get_exception; + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) +{ + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_foreach_iterator_object; + prepareCallArg(arg, call.argc, call.args); + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) +{ + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_foreach_next_property_name; + prepareCallArg(arg, call.argc, call.args); + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPushWith(IR::Temp *arg) +{ + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_push_with; + prepareCallArg(arg, call.argc, call.args); + assert(call.argc == 1); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPopWith() +{ + Instruction::CallBuiltin call; + call.builtin = Instruction::CallBuiltin::builtin_pop_with; + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + Instruction::CallBuiltinDeclareVar call; + call.isDeletable = deletable; + call.varName = engine()->newString(name); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) +{ + Instruction::CallBuiltinDefineGetterSetter call; + call.objectTemp = object->index; + call.name = engine()->newString(name); + call.getterTemp = getter->index; + call.setterTemp = setter->index; + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) +{ + Instruction::CallBuiltinDefineProperty call; + call.objectTemp = object->index; + call.name = engine()->newString(name); + call.valueTemp = value->index; + addInstruction(call); +} + ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) { #ifdef MOTH_THREADED_INTERPRETER diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index c0e859536b..58c7969f10 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -24,9 +24,29 @@ protected: virtual void visitCJump(IR::CJump *); virtual void visitRet(IR::Ret *); - virtual void callActivationProperty(IR::Call *c, IR::Temp *temp); - virtual void callValue(IR::Call *c, IR::Temp *temp); - virtual void callProperty(IR::Call *c, IR::Temp *temp); + virtual void callBuiltinInvalid(IR::Expr *func, IR::ExprList *args, IR::Temp *result); + virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteValue(IR::Temp *result); + virtual void callBuiltinThrow(IR::Temp *arg); + virtual void callBuiltinRethrow(); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); + virtual void callBuiltinDeleteExceptionHandler(); + virtual void callBuiltinGetException(IR::Temp *result); + virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinPushWith(IR::Temp *arg); + virtual void callBuiltinPopWith(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); + virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); + virtual void callValue(IR::Call *c, IR::Temp *result); + virtual void callProperty(IR::Call *c, IR::Temp *result); virtual void constructActivationProperty(IR::New *call, IR::Temp *result); virtual void constructProperty(IR::New *call, IR::Temp *result); virtual void constructValue(IR::New *call, IR::Temp *result); @@ -58,6 +78,7 @@ private: }; void simpleMove(IR::Move *); + void prepareCallArg(IR::Expr *e, quint32 &argc, quint32 &args); void prepareCallArgs(IR::ExprList *, quint32 &, quint32 &); int outgoingArgumentTempStart() const { return _function->tempCount; } diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 2d21b2f064..27b92a4ceb 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -156,7 +156,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(LoadValue) TEMP(instr.targetTempIndex) = instr.value; - MOTH_END_INSTR(LoadUndefined) + MOTH_END_INSTR(LoadValue) MOTH_BEGIN_INSTR(LoadClosure) VM::Value c = __qmljs_init_closure(instr.value, context); @@ -237,6 +237,9 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co Q_ASSERT(instr.argc == 1); __qmljs_builtin_throw(args[0], context); break; + case Instr::instr_callBuiltin::builtin_rethrow: + __qmljs_builtin_rethrow(context); + break; case Instr::instr_callBuiltin::builtin_create_exception_handler: { TRACE(builtin_create_exception_handler, "%s", ""); buf = __qmljs_create_exception_handler(context); @@ -279,6 +282,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co __qmljs_builtin_pop_with(context); break; default: + assert(!"TODO!"); Q_UNREACHABLE(); } MOTH_END_INSTR(CallBuiltin) @@ -295,10 +299,6 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co TEMP(instr.targetTempIndex) = __qmljs_delete_name(context, instr.name); MOTH_END_INSTR(CallBuiltinDeleteName) - MOTH_BEGIN_INSTR(CallBuiltinDeleteValue) - TEMP(instr.targetTempIndex) = VM::Value::fromBoolean(false); - MOTH_END_INSTR(CallBuiltinDeleteValue) - MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof_member(TEMP(instr.base), instr.member, context); MOTH_END_INSTR(CallBuiltinTypeofMember) @@ -317,7 +317,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) __qmljs_builtin_declare_var(context, instr.isDeletable, instr.varName); - MOTH_END_INSTR(CallBuiltinDeleteValue) + MOTH_END_INSTR(CallBuiltinDeclareVar) MOTH_BEGIN_INSTR(CallBuiltinDefineGetterSetter) __qmljs_builtin_define_getter_setter(TEMP(instr.objectTemp), instr.name, TEMP(instr.getterTemp), TEMP(instr.setterTemp), context); diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index de84722d5a..269dabd9c6 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -81,10 +81,6 @@ #include "qv4ir_p.h" #include "qv4string.h" -namespace { -QTextStream qout(stderr, QIODevice::WriteOnly); -} // anonymous namespace - namespace QQmlJS { int compileWithLLVM(IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*exec)(void *)) @@ -304,70 +300,192 @@ void InstructionSelection::buildLLVMModule(IR::Module *module, llvm::Module *llv qSwap(_llvmModule, llvmModule); } -void InstructionSelection::callActivationProperty(IR::Call *c, IR::Temp *temp) +void InstructionSelection::callBuiltinInvalid(IR::Expr *func, IR::ExprList *args, IR::Temp *result) { - IR::Name *baseName = c->base->asName(); - Q_ASSERT(baseName); + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} - switch (baseName->builtin) { - case IR::Name::builtin_declare_vars: { - if (!c->args) - return; - IR::Const *deletable = c->args->expr->asConst(); - assert(deletable->type == IR::BoolType); - llvm::ConstantInt *isDeletable = getInt1(deletable->value != 0); - for (IR::ExprList *it = c->args->next; it; it = it->next) { - IR::Name *arg = it->expr->asName(); - assert(arg != 0); - - llvm::Value *name = getIdentifier(*arg->id); - CreateCall3(getRuntimeFunction("__qmljs_builtin_declare_var"), - _llvmFunction->arg_begin(), isDeletable, name); - } - } return; - default: - break; - } // switch +void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinThrow(IR::Temp *arg) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinRethrow() +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteExceptionHandler() +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinGetException(IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} - Q_UNIMPLEMENTED(); - c->dump(qout); - qout << endl; +void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPushWith(IR::Temp *arg) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPopWith() +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + llvm::ConstantInt *isDeletable = getInt1(deletable != 0); + llvm::Value *varName = getIdentifier(name); + CreateCall3(getRuntimeFunction("__qmljs_builtin_declare_var"), + _llvmFunction->arg_begin(), isDeletable, varName); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) +{ + // TODO assert(!"TODO!"); Q_UNREACHABLE(); } void InstructionSelection::callValue(IR::Call *c, IR::Temp *temp) { - // TODO: implement instead of visitExp + // TODO + assert(!"TODO!"); Q_UNREACHABLE(); } void InstructionSelection::callProperty(IR::Call *c, IR::Temp *temp) { - // TODO: implement instead of visitExp + // TODO + assert(!"TODO!"); Q_UNREACHABLE(); } void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) { + // TODO assert(!"TODO!"); Q_UNREACHABLE(); } void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) { + // TODO assert(!"TODO!"); Q_UNREACHABLE(); } void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) { + // TODO assert(!"TODO!"); Q_UNREACHABLE(); } void InstructionSelection::loadThisObject(IR::Temp *temp) { + // TODO assert(!"TODO!"); Q_UNREACHABLE(); } @@ -381,18 +499,21 @@ void InstructionSelection::loadConst(IR::Const *con, IR::Temp *temp) void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) { + // TODO assert(!"TODO!"); Q_UNREACHABLE(); } void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) { + // TODO assert(!"TODO!"); Q_UNREACHABLE(); } void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) { + // TODO assert(!"TODO!"); Q_UNREACHABLE(); } @@ -449,6 +570,7 @@ void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, c void InstructionSelection::getElement(IR::Temp *sourceBase, IR::Temp *sourceIndex, IR::Temp *target) { + // TODO assert(!"TODO!"); Q_UNREACHABLE(); diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 53d9a8deca..1cd1004c73 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -70,7 +70,27 @@ public: void buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm); public: // methods from InstructionSelection: - virtual void callActivationProperty(IR::Call *c, IR::Temp *temp); + virtual void callBuiltinInvalid(IR::Expr *func, IR::ExprList *args, IR::Temp *result); + virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteValue(IR::Temp *result); + virtual void callBuiltinThrow(IR::Temp *arg); + virtual void callBuiltinRethrow(); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); + virtual void callBuiltinDeleteExceptionHandler(); + virtual void callBuiltinGetException(IR::Temp *result); + virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinPushWith(IR::Temp *arg); + virtual void callBuiltinPopWith(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); + virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); virtual void callValue(IR::Call *c, IR::Temp *temp); virtual void callProperty(IR::Call *c, IR::Temp *temp); virtual void constructActivationProperty(IR::New *call, IR::Temp *result); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index b73bddc988..1c78dc60e8 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -428,148 +428,114 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) _asm = oldAssembler; } -String *InstructionSelection::identifier(const QString &s) +void InstructionSelection::callBuiltinInvalid(IR::Expr *func, IR::ExprList *args, IR::Temp *result) { - return engine()->identifier(s); + callRuntimeMethod(result, __qmljs_call_activation_property, func, args); } -void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) +void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) { - IR::Name *baseName = call->base->asName(); - assert(baseName != 0); + generateFunctionCall(result, __qmljs_builtin_typeof_member, base, identifier(name), Assembler::ContextRegister); +} - switch (baseName->builtin) { - case IR::Name::builtin_invalid: - callRuntimeMethod(result, __qmljs_call_activation_property, call->base, call->args); - break; - case IR::Name::builtin_typeof: { - if (IR::Member *m = call->args->expr->asMember()) { - generateFunctionCall(result, __qmljs_builtin_typeof_member, m->base->asTemp(), identifier(*m->name), Assembler::ContextRegister); - return; - } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { - generateFunctionCall(result, __qmljs_builtin_typeof_element, ss->base->asTemp(), ss->index->asTemp(), Assembler::ContextRegister); - return; - } else if (IR::Name *n = call->args->expr->asName()) { - generateFunctionCall(result, __qmljs_builtin_typeof_name, identifier(*n->id), Assembler::ContextRegister); - return; - } else if (IR::Temp *arg = call->args->expr->asTemp()){ - assert(arg != 0); - generateFunctionCall(result, __qmljs_builtin_typeof, arg, Assembler::ContextRegister); - } else { - assert(false); - } - } - break; - case IR::Name::builtin_delete: { - if (IR::Member *m = call->args->expr->asMember()) { - generateFunctionCall(result, __qmljs_delete_member, Assembler::ContextRegister, m->base->asTemp(), identifier(*m->name)); - return; - } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { - generateFunctionCall(result, __qmljs_delete_subscript, Assembler::ContextRegister, ss->base->asTemp(), ss->index->asTemp()); - return; - } else if (IR::Name *n = call->args->expr->asName()) { - generateFunctionCall(result, __qmljs_delete_name, Assembler::ContextRegister, identifier(*n->id)); - return; - } else if (call->args->expr->asTemp()){ - // ### should throw in strict mode - _asm->storeValue(Value::fromBoolean(false), result); - return; - } - break; - } - case IR::Name::builtin_throw: { - IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, arg, Assembler::ContextRegister); - } - break; - case IR::Name::builtin_rethrow: - generateFunctionCall(Assembler::Void, __qmljs_builtin_rethrow, Assembler::ContextRegister); - break; - case IR::Name::builtin_create_exception_handler: - generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_create_exception_handler, Assembler::ContextRegister); - generateFunctionCall(result, setjmp, Assembler::ReturnValueRegister); - break; - case IR::Name::builtin_delete_exception_handler: - generateFunctionCall(Assembler::Void, __qmljs_delete_exception_handler, Assembler::ContextRegister); - break; - case IR::Name::builtin_get_exception: - generateFunctionCall(result, __qmljs_get_exception, Assembler::ContextRegister); - break; - case IR::Name::builtin_foreach_iterator_object: { - IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - generateFunctionCall(result, __qmljs_foreach_iterator_object, arg, Assembler::ContextRegister); - } - break; - case IR::Name::builtin_foreach_next_property_name: { - IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - generateFunctionCall(result, __qmljs_foreach_next_property_name, arg); - } - break; - case IR::Name::builtin_push_with: { - IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - generateFunctionCall(Assembler::Void, __qmljs_builtin_push_with, arg, Assembler::ContextRegister); - } - break; - case IR::Name::builtin_pop_with: - generateFunctionCall(Assembler::Void, __qmljs_builtin_pop_with, Assembler::ContextRegister); - break; - case IR::Name::builtin_declare_vars: { - if (!call->args) - return; - IR::Const *deletable = call->args->expr->asConst(); - assert(deletable->type == IR::BoolType); - for (IR::ExprList *it = call->args->next; it; it = it->next) { - IR::Name *arg = it->expr->asName(); - assert(arg != 0); - generateFunctionCall(Assembler::Void, __qmljs_builtin_declare_var, Assembler::ContextRegister, - Assembler::TrustedImm32(deletable->value != 0), identifier(*arg->id)); - } - break; - } - case IR::Name::builtin_define_getter_setter: { - if (!call->args) - return; - IR::ExprList *args = call->args; - IR::Temp *object = args->expr->asTemp(); - assert(object); - args = args->next; - assert(args); - IR::Name *name = args->expr->asName(); - args = args->next; - assert(args); - IR::Temp *getter = args->expr->asTemp(); - args = args->next; - assert(args); - IR::Temp *setter = args->expr->asTemp(); - - generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter, - object, identifier(*name->id), getter, setter, Assembler::ContextRegister); - break; - } - case IR::Name::builtin_define_property: { - if (!call->args) - return; - IR::ExprList *args = call->args; - IR::Temp *object = args->expr->asTemp(); - assert(object); - args = args->next; - assert(args); - IR::Name *name = args->expr->asName(); - args = args->next; - assert(args); - IR::Temp *value = args->expr->asTemp(); - - generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property, - object, identifier(*name->id), value, Assembler::ContextRegister); - break; - } - } +void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_typeof_element, base, index, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_typeof_name, identifier(name), Assembler::ContextRegister); } +void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_typeof, value, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_delete_member, Assembler::ContextRegister, base, identifier(name)); +} + +void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_delete_subscript, Assembler::ContextRegister, base, index); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_delete_name, Assembler::ContextRegister, identifier(name)); +} + +void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) +{ + _asm->storeValue(Value::fromBoolean(false), result); +} + +void InstructionSelection::callBuiltinThrow(IR::Temp *arg) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, arg, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinRethrow() +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_rethrow, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) +{ + generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_create_exception_handler, Assembler::ContextRegister); + generateFunctionCall(result, setjmp, Assembler::ReturnValueRegister); +} + +void InstructionSelection::callBuiltinDeleteExceptionHandler() +{ + generateFunctionCall(Assembler::Void, __qmljs_delete_exception_handler, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinGetException(IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_get_exception, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_foreach_iterator_object, arg, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_foreach_next_property_name, arg); +} + +void InstructionSelection::callBuiltinPushWith(IR::Temp *arg) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_push_with, arg, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPopWith() +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_pop_with, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_declare_var, Assembler::ContextRegister, + Assembler::TrustedImm32(deletable), identifier(name)); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter, + object, identifier(name), getter, setter, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property, + object, identifier(name), value, Assembler::ContextRegister); +} void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) { @@ -764,6 +730,11 @@ void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) generateFunctionCall(result, __qmljs_call_property, Assembler::ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } +String *InstructionSelection::identifier(const QString &s) +{ + return engine()->identifier(s); +} + void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) { IR::Name *baseName = call->base->asName(); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index c042bd626e..e1eb16b3ed 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -652,7 +652,27 @@ public: virtual void run(VM::Function *vmFunction, IR::Function *function); protected: - virtual void callActivationProperty(IR::Call *call, IR::Temp *result); + virtual void callBuiltinInvalid(IR::Expr *func, IR::ExprList *args, IR::Temp *result); + virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteValue(IR::Temp *result); + virtual void callBuiltinThrow(IR::Temp *arg); + virtual void callBuiltinRethrow(); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); + virtual void callBuiltinDeleteExceptionHandler(); + virtual void callBuiltinGetException(IR::Temp *result); + virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinPushWith(IR::Temp *arg); + virtual void callBuiltinPopWith(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); + virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); virtual void callProperty(IR::Call *call, IR::Temp *result); virtual void callValue(IR::Call *call, IR::Temp *result); virtual void loadThisObject(IR::Temp *temp); diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 9326a2f7a1..603c5933a9 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -126,7 +126,7 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Call *c = s->source->asCall()) { if (c->base->asName()) { - callActivationProperty(c, t); + callBuiltin(c, t); return; } else if (c->base->asMember()) { callProperty(c, t); @@ -201,7 +201,7 @@ void InstructionSelection::visitExp(IR::Exp *s) if (IR::Call *c = s->expr->asCall()) { // These are calls where the result is ignored. if (c->base->asName()) { - callActivationProperty(c, 0); + callBuiltin(c, 0); } else if (c->base->asTemp()) { callValue(c, 0); } else if (c->base->asMember()) { @@ -213,3 +213,147 @@ void InstructionSelection::visitExp(IR::Exp *s) Q_UNREACHABLE(); } } + +void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result) +{ + IR::Name *baseName = call->base->asName(); + assert(baseName != 0); + + switch (baseName->builtin) { + case IR::Name::builtin_invalid: + callBuiltinInvalid(call->base, call->args, result); + return; + + case IR::Name::builtin_typeof: { + if (IR::Member *m = call->args->expr->asMember()) { + callBuiltinTypeofMember(m->base->asTemp(), *m->name, result); + return; + } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinTypeofSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (IR::Name *n = call->args->expr->asName()) { + callBuiltinTypeofName(*n->id, result); + return; + } else if (IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinTypeofValue(arg, result); + return; + } + } break; + + case IR::Name::builtin_delete: { + if (IR::Member *m = call->args->expr->asMember()) { + callBuiltinDeleteMember(m->base->asTemp(), *m->name, result); + return; + } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinDeleteSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (IR::Name *n = call->args->expr->asName()) { + callBuiltinDeleteName(*n->id, result); + return; + } else if (call->args->expr->asTemp()){ + // TODO: should throw in strict mode + callBuiltinDeleteValue(result); + return; + } + } break; + + case IR::Name::builtin_throw: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinThrow(arg); + } return; + + case IR::Name::builtin_rethrow: + callBuiltinRethrow(); + return; + + case IR::Name::builtin_create_exception_handler: + callBuiltinCreateExceptionHandler(result); + return; + + case IR::Name::builtin_delete_exception_handler: + callBuiltinDeleteExceptionHandler(); + return; + + case IR::Name::builtin_get_exception: + callBuiltinGetException(result); + return; + + case IR::Name::builtin_foreach_iterator_object: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinForeachIteratorObject(arg, result); + } return; + + case IR::Name::builtin_foreach_next_property_name: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinForeachNextPropertyname(arg, result); + } return; + case IR::Name::builtin_push_with: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinPushWith(arg); + } return; + + case IR::Name::builtin_pop_with: + callBuiltinPopWith(); + return; + + case IR::Name::builtin_declare_vars: { + if (!call->args) + return; + IR::Const *deletable = call->args->expr->asConst(); + assert(deletable->type == IR::BoolType); + for (IR::ExprList *it = call->args->next; it; it = it->next) { + IR::Name *arg = it->expr->asName(); + assert(arg != 0); + callBuiltinDeclareVar(deletable->value != 0, *arg->id); + } + } return; + + case IR::Name::builtin_define_getter_setter: { + if (!call->args) + return; + IR::ExprList *args = call->args; + IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + IR::Name *name = args->expr->asName(); + args = args->next; + assert(args); + IR::Temp *getter = args->expr->asTemp(); + args = args->next; + assert(args); + IR::Temp *setter = args->expr->asTemp(); + + callBuiltinDefineGetterSetter(object, *name->id, getter, setter); + } return; + + case IR::Name::builtin_define_property: { + if (!call->args) + return; + IR::ExprList *args = call->args; + IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + IR::Name *name = args->expr->asName(); + args = args->next; + assert(args); + IR::Temp *value = args->expr->asTemp(); + + callBuiltinDefineProperty(object, *name->id, value); + } return; + + default: + break; + } + + Q_UNIMPLEMENTED(); + call->dump(qout); qout << endl; + assert(!"TODO!"); + Q_UNREACHABLE(); +} diff --git a/qv4isel_p.h b/qv4isel_p.h index 59464c6602..275c3ecefe 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -80,7 +80,27 @@ public: // visitor methods for StmtVisitor: virtual void visitExp(IR::Exp *s); public: // to implement by subclasses: - virtual void callActivationProperty(IR::Call *c, IR::Temp *temp) = 0; + virtual void callBuiltinInvalid(IR::Expr *func, IR::ExprList *args, IR::Temp *result) = 0; + virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0; + virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) = 0; + virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0; + virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinDeleteValue(IR::Temp *result) = 0; + virtual void callBuiltinThrow(IR::Temp *arg) = 0; + virtual void callBuiltinRethrow() = 0; + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result) = 0; + virtual void callBuiltinDeleteExceptionHandler() = 0; + virtual void callBuiltinGetException(IR::Temp *result) = 0; + virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) = 0; + virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) = 0; + virtual void callBuiltinPushWith(IR::Temp *arg) = 0; + virtual void callBuiltinPopWith() = 0; + virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0; + virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) = 0; + virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) = 0; virtual void callValue(IR::Call *c, IR::Temp *temp) = 0; virtual void callProperty(IR::Call *c, IR::Temp *temp) = 0; virtual void constructActivationProperty(IR::New *call, IR::Temp *result) = 0; @@ -103,6 +123,9 @@ public: // to implement by subclasses: virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) = 0; virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) = 0; virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) = 0; + +private: + void callBuiltin(IR::Call *c, IR::Temp *temp); }; } // namespace IR -- cgit v1.2.3 From 825dbba8c89be424765015d22754fe318647472f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 16 Jan 2013 16:09:54 +0100 Subject: Fix createFunctionMapping This fixes a bad regression introduced with e5f5251a020be728015068f3cd033f55f1d6b693. Change-Id: If4961f46171ea6c919daae7eaab9ef70c7b0608e Reviewed-by: Erik Verbruggen --- qv4isel_p.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 603c5933a9..3ced5784d0 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -49,6 +49,9 @@ VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngin if (local) vmFunction->locals.append(*local); + foreach (IR::Function *function, irFunction->nestedFunctions) + createFunctionMapping(engine, function); + vmFunction->hasNestedFunctions = !irFunction->nestedFunctions.isEmpty(); if (engine->debugger) -- cgit v1.2.3 From 3990d8a1364f5e1e9de2e6ab6ee28d1e1df82806 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 16 Jan 2013 15:41:02 +0100 Subject: Fix Array.prototype.sort for non sparse arrays Change-Id: Ibb055358dc953881842d43ebff4d361c949796a9 Reviewed-by: Simon Hausmann --- qv4array.cpp | 13 +++++++++---- qv4array.h | 2 +- qv4ecmaobjects.cpp | 15 +++++++++++---- qv4ecmaobjects_p.h | 2 ++ tests/TestExpectations | 20 -------------------- 5 files changed, 23 insertions(+), 29 deletions(-) diff --git a/qv4array.cpp b/qv4array.cpp index 42c7ed9168..c9942b0bbe 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -569,14 +569,19 @@ void Array::concat(const Array &other) setLengthUnchecked(newLen); } -void Array::sort(ExecutionContext *context, Object *thisObject, const Value &comparefn) +void Array::sort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len) { - if (!sparse) + if (sparse) { + context->throwUnimplemented("Array::sort unimplemented for sparse arrays"); return; + delete sparse; + } ArrayElementLessThan lessThan(context, thisObject, comparefn); - std::sort(values.begin(), values.end(), lessThan); - delete sparse; + if (len > values.size() - offset) + len = values.size() - offset; + PropertyDescriptor *begin = values.begin() + offset; + std::sort(begin, begin + len, lessThan); } diff --git a/qv4array.h b/qv4array.h index bdd37442b3..28601ba2b5 100644 --- a/qv4array.h +++ b/qv4array.h @@ -608,7 +608,7 @@ public: SparseArrayNode *sparseEnd() { return sparse ? sparse->end() : 0; } void concat(const Array &other); - void sort(ExecutionContext *context, Object *thisObject, const Value &comparefn); + void sort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len); void splice(double start, double deleteCount, const QVector &, Array &); Value indexOf(Value v, uint fromIndex, uint len, ExecutionContext *ctx, Object *o); }; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index f3fd4acda5..9310676603 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1613,6 +1613,13 @@ void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) defineDefaultProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 1); } +uint ArrayPrototype::getLength(ExecutionContext *ctx, Object *o) +{ + if (o->isArray) + return o->array.length(); + return o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); +} + Value ArrayPrototype::method_isArray(ExecutionContext *ctx) { Value arg = ctx->argument(0); @@ -1824,12 +1831,12 @@ Value ArrayPrototype::method_slice(ExecutionContext *ctx) Value ArrayPrototype::method_sort(ExecutionContext *ctx) { - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.sort")); + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); Value comparefn = ctx->argument(0); - instance->array.sort(ctx, instance, comparefn); + instance->array.sort(ctx, instance, comparefn, len); return ctx->thisObject; } diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 53936be923..35a3f6529e 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -175,6 +175,8 @@ struct ArrayPrototype: ArrayObject void init(ExecutionContext *ctx, const Value &ctor); + static uint getLength(ExecutionContext *ctx, Object *o); + static Value method_isArray(ExecutionContext *ctx); static Value method_toString(ExecutionContext *ctx); static Value method_toLocaleString(ExecutionContext *ctx); diff --git a/tests/TestExpectations b/tests/TestExpectations index d9f6088937..839ba7c3a2 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -793,7 +793,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-82 failing 15.2.3.3-4-9 failing 15.2.3.3-4-90 failing -15.2.3.4-4-44 failing 15.2.3.5-4-120 failing 15.2.3.5-4-13 failing 15.2.3.5-4-145 failing @@ -860,12 +859,6 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-289 failing 15.3.4.5-2-7 failing 15.4.3.2-1-11 failing -S15.4.4.11_A3_T1 failing -S15.4.4.11_A3_T2 failing -S15.4.4.11_A4_T1 failing -S15.4.4.11_A4_T2 failing -S15.4.4.11_A4_T3 failing -S15.4.4.11_A6_T2 failing 15.4.4.12-9-c-ii-1 failing S15.4.4.12_A1.1_T4 failing S15.4.4.12_A1.1_T6 failing @@ -2759,15 +2752,6 @@ S15.2.4.4_A14 failing S15.2.4.4_A15 failing # Array regressions -S13.2.1_A5_T1 failing -S15.4.4.11_A1.5_T1 failing -S15.4.4.11_A2.1_T1 failing -S15.4.4.11_A2.1_T2 failing -S15.4.4.11_A2.1_T3 failing -S15.4.4.11_A2.2_T1 failing -S15.4.4.11_A2.2_T2 failing -S15.4.4.11_A2.2_T3 failing -S15.4.4.11_A5_T1 failing 15.4.4.12-9-a-1 failing S15.4.4.12_A1.1_T1 failing S15.4.4.12_A1.1_T2 failing @@ -2788,10 +2772,6 @@ S15.4.4.12_A2.1_T4 failing S15.4.4.12_A2.1_T5 failing S15.4.4.12_A2.2_T3 failing S15.4.4.12_A2.2_T5 failing -S15.4.4.11_A1.2_T1 failing -S15.4.4.11_A1.2_T2 failing -S15.4.4.11_A1.4_T1 failing -S15.4.4.11_A1.4_T2 failing S15.4.4.4_A1_T2 failing # Regressions due to Object/property refactoring -- cgit v1.2.3 From 94f5a6ec6d81f262be6fc3de1c11f2742bb05196 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 14 Jan 2013 16:53:43 +0100 Subject: Add Yarr regex engine from JSC Only the interpreter is enabled right now, the JIT needs more compile fixes. This is the initial port that is functionally equivalent to the QRegularExpression based implementation except for the tests that pass now because of the actual JS compatible regex implementation. Change-Id: Ieb7e66e9b38071ea1d32effe045c70023b17fabd Reviewed-by: Lars Knoll --- .gitignore | 1 + masm/config.h | 2 + masm/create_regex_tables | 121 ++ masm/masm.pri | 18 +- masm/runtime/MatchResult.h | 71 + masm/stubs/wtf/PassOwnPtr.h | 25 +- masm/stubs/wtf/RefCounted.h | 2 + masm/stubs/wtf/Vector.h | 29 + masm/stubs/wtf/text/CString.h | 44 + masm/stubs/wtf/text/WTFString.h | 75 + masm/stubs/wtf/unicode/Unicode.h | 59 + masm/wtf/ASCIICType.h | 181 +++ masm/wtf/BumpPointerAllocator.h | 252 ++++ masm/yarr/Yarr.h | 69 + masm/yarr/YarrCanonicalizeUCS2.cpp | 463 +++++++ masm/yarr/YarrCanonicalizeUCS2.h | 138 ++ masm/yarr/YarrCanonicalizeUCS2.js | 219 +++ masm/yarr/YarrInterpreter.cpp | 1964 ++++++++++++++++++++++++++ masm/yarr/YarrInterpreter.h | 385 ++++++ masm/yarr/YarrJIT.cpp | 2667 ++++++++++++++++++++++++++++++++++++ masm/yarr/YarrJIT.h | 141 ++ masm/yarr/YarrParser.h | 880 ++++++++++++ masm/yarr/YarrPattern.cpp | 874 ++++++++++++ masm/yarr/YarrPattern.h | 421 ++++++ masm/yarr/YarrSyntaxChecker.cpp | 59 + masm/yarr/YarrSyntaxChecker.h | 38 + masm/yarr/yarr.pri | 12 + qmljs_engine.cpp | 12 +- qmljs_engine.h | 8 +- qmljs_objects.cpp | 7 +- qmljs_objects.h | 6 +- qv4ecmaobjects.cpp | 41 +- qv4ecmaobjects_p.h | 2 +- qv4regexp.cpp | 76 + qv4regexp.h | 92 ++ tests/TestExpectations | 16 - v4.pro | 6 +- 37 files changed, 9422 insertions(+), 54 deletions(-) create mode 100644 masm/create_regex_tables create mode 100644 masm/runtime/MatchResult.h create mode 100644 masm/stubs/wtf/text/CString.h create mode 100644 masm/stubs/wtf/text/WTFString.h create mode 100644 masm/stubs/wtf/unicode/Unicode.h create mode 100644 masm/wtf/ASCIICType.h create mode 100644 masm/wtf/BumpPointerAllocator.h create mode 100644 masm/yarr/Yarr.h create mode 100644 masm/yarr/YarrCanonicalizeUCS2.cpp create mode 100644 masm/yarr/YarrCanonicalizeUCS2.h create mode 100644 masm/yarr/YarrCanonicalizeUCS2.js create mode 100644 masm/yarr/YarrInterpreter.cpp create mode 100644 masm/yarr/YarrInterpreter.h create mode 100644 masm/yarr/YarrJIT.cpp create mode 100644 masm/yarr/YarrJIT.h create mode 100644 masm/yarr/YarrParser.h create mode 100644 masm/yarr/YarrPattern.cpp create mode 100644 masm/yarr/YarrPattern.h create mode 100644 masm/yarr/YarrSyntaxChecker.cpp create mode 100644 masm/yarr/YarrSyntaxChecker.h create mode 100644 masm/yarr/yarr.pri create mode 100644 qv4regexp.cpp create mode 100644 qv4regexp.h diff --git a/.gitignore b/.gitignore index 17784d7c7e..ec496ad579 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ udis86_itab.* *.pro.user *.bc *.ll +RegExpJitTables.h diff --git a/masm/config.h b/masm/config.h index 1ced4e454c..5f59f311e3 100644 --- a/masm/config.h +++ b/masm/config.h @@ -45,6 +45,8 @@ #include #ifdef __cplusplus #include +#include +#include #include #else #include diff --git a/masm/create_regex_tables b/masm/create_regex_tables new file mode 100644 index 0000000000..bd799ba044 --- /dev/null +++ b/masm/create_regex_tables @@ -0,0 +1,121 @@ +# Copyright (C) 2010 Apple Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import sys + +types = { + "wordchar": { "UseTable" : True, "data": ['_', ('0','9'), ('A', 'Z'), ('a','z')]}, + "nonwordchar": { "UseTable" : True, "Inverse": "wordchar", "data": ['`', (0, ord('0') - 1), (ord('9') + 1, ord('A') - 1), (ord('Z') + 1, ord('_') - 1), (ord('z') + 1, 0xffff)]}, + "newline": { "UseTable" : False, "data": ['\n', '\r', 0x2028, 0x2029]}, + "spaces": { "UseTable" : True, "data": [' ', ('\t', '\r'), 0xa0, 0x1680, 0x180e, 0x2028, 0x2029, 0x202f, 0x205f, 0x3000, (0x2000, 0x200a), 0xfeff]}, + "nonspaces": { "UseTable" : True, "Inverse": "spaces", "data": [(0, ord('\t') - 1), (ord('\r') + 1, ord(' ') - 1), (ord(' ') + 1, 0x009f), (0x00a1, 0x167f), (0x1681, 0x180d), (0x180f, 0x1fff), (0x200b, 0x2027), (0x202a, 0x202e), (0x2030, 0x205e), (0x2060, 0x2fff), (0x3001, 0xfefe), (0xff00, 0xffff)]}, + "digits": { "UseTable" : False, "data": [('0', '9')]}, + "nondigits": { "UseTable" : False, "Inverse": "digits", "data": [(0, ord('0') - 1), (ord('9') + 1, 0xffff)] } +} +entriesPerLine = 50 +arrays = ""; +functions = ""; +emitTables = (len(sys.argv) < 2 or sys.argv[1] != "--no-tables") + +for name, classes in types.items(): + ranges = []; + size = 0; + for _class in classes["data"]: + if type(_class) == str: + ranges.append((ord(_class), ord(_class))) + elif type(_class) == int: + ranges.append((_class, _class)) + else: + (min, max) = _class; + if type(min) == str: + min = ord(min) + if type(max) == str: + max = ord(max) + if max > 0x7f and min <= 0x7f: + ranges.append((min, 0x7f)) + min = 0x80 + ranges.append((min,max)) + ranges.sort(); + + if emitTables and classes["UseTable"] and (not "Inverse" in classes): + array = ("static const char _%sData[65536] = {\n" % name); + i = 0 + for (min,max) in ranges: + while i < min: + i = i + 1 + array += ('0,') + if (i % entriesPerLine == 0) and (i != 0): + array += ('\n') + while i <= max: + i = i + 1 + if (i == 65536): + array += ("1") + else: + array += ('1,') + if (i % entriesPerLine == 0) and (i != 0): + array += ('\n') + while i < 0xffff: + array += ("0,") + i = i + 1; + if (i % entriesPerLine == 0) and (i != 0): + array += ('\n') + if i == 0xffff: + array += ("0") + array += ("\n};\n\n"); + arrays += array + + # Generate createFunction: + function = ""; + function += ("CharacterClass* %sCreate()\n" % name) + function += ("{\n") + if emitTables and classes["UseTable"]: + if "Inverse" in classes: + function += (" CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_%sData, true));\n" % (classes["Inverse"])) + else: + function += (" CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_%sData, false));\n" % (name)) + else: + function += (" CharacterClass* characterClass = new CharacterClass(0);\n") + for (min, max) in ranges: + if (min == max): + if (min > 127): + function += (" characterClass->m_matchesUnicode.append(0x%04x);\n" % min) + else: + function += (" characterClass->m_matches.append(0x%02x);\n" % min) + continue + if (min > 127) or (max > 127): + function += (" characterClass->m_rangesUnicode.append(CharacterRange(0x%04x, 0x%04x));\n" % (min, max)) + else: + function += (" characterClass->m_ranges.append(CharacterRange(0x%02x, 0x%02x));\n" % (min, max)) + function += (" return characterClass;\n") + function += ("}\n\n") + functions += function + +if (len(sys.argv) > 1): + f = open(sys.argv[-1], "w") + f.write(arrays) + f.write(functions) + f.close() +else: + print(arrays) + print(functions) + diff --git a/masm/masm.pri b/masm/masm.pri index d0033f59c1..a6d11f633c 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -30,7 +30,7 @@ HEADERS += $$PWD/wtf/PageReservation.h SOURCES += $$PWD/stubs/WTFStubs.cpp HEADERS += $$PWD/stubs/WTFStubs.h -DEFINES += WTF_EXPORT_PRIVATE="" +DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" DEFINES += ENABLE_LLINT=0 DEFINES += ENABLE_DFG_JIT=0 @@ -42,6 +42,7 @@ DEFINES += BUILDING_QT__ INCLUDEPATH += $$PWD/jit INCLUDEPATH += $$PWD/assembler +INCLUDEPATH += $$PWD/runtime INCLUDEPATH += $$PWD/wtf INCLUDEPATH += $$PWD/stubs INCLUDEPATH += $$PWD/stubs/wtf @@ -61,6 +62,21 @@ SOURCES += $$PWD/disassembler/udis86/udis86_syn-att.c SOURCES += $$PWD/disassembler/udis86/udis86_syn.c SOURCES += $$PWD/disassembler/udis86/udis86_syn-intel.c +DEFINES += ENABLE_YARR_JIT=0 +SOURCES += \ + $$PWD/yarr/YarrCanonicalizeUCS2.cpp \ + $$PWD/yarr/YarrInterpreter.cpp \ + $$PWD/yarr/YarrPattern.cpp \ + $$PWD/yarr/YarrSyntaxChecker.cpp + +HEADERS += $$PWD/yarr/*.h + +retgen.output = RegExpJitTables.h +retgen.script = $$PWD/create_regex_tables +retgen.input = retgen.script +retgen.CONFIG += no_link +retgen.commands = python $$retgen.script > ${QMAKE_FILE_OUT} +QMAKE_EXTRA_COMPILERS += retgen ITAB = $$PWD/disassembler/udis86/optable.xml udis86.output = udis86_itab.h diff --git a/masm/runtime/MatchResult.h b/masm/runtime/MatchResult.h new file mode 100644 index 0000000000..d87c8516b0 --- /dev/null +++ b/masm/runtime/MatchResult.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MatchResult_h +#define MatchResult_h + +typedef uint64_t EncodedMatchResult; + +struct MatchResult { + ALWAYS_INLINE MatchResult(size_t start, size_t end) + : start(start) + , end(end) + { + } + + explicit ALWAYS_INLINE MatchResult(EncodedMatchResult encoded) + { + union u { + uint64_t encoded; + struct s { + size_t start; + size_t end; + } split; + } value; + value.encoded = encoded; + start = value.split.start; + end = value.split.end; + } + + ALWAYS_INLINE static MatchResult failed() + { + return MatchResult(WTF::notFound, 0); + } + + ALWAYS_INLINE operator bool() + { + return start != WTF::notFound; + } + + ALWAYS_INLINE bool empty() + { + return start == end; + } + + size_t start; + size_t end; +}; + +#endif diff --git a/masm/stubs/wtf/PassOwnPtr.h b/masm/stubs/wtf/PassOwnPtr.h index c8d3bf3f75..f9b84e7b57 100644 --- a/masm/stubs/wtf/PassOwnPtr.h +++ b/masm/stubs/wtf/PassOwnPtr.h @@ -43,7 +43,30 @@ #include -#define OwnPtr QScopedPointer +template class PassOwnPtr; +template PassOwnPtr adoptPtr(PtrType*); + +template +struct OwnPtr : public QScopedPointer +{ + OwnPtr() {} + OwnPtr(const PassOwnPtr &ptr) + : QScopedPointer(ptr.leakRef()) + {} + + OwnPtr& operator=(const OwnPtr& other) + { + this->reset(const_cast &>(other).take()); + return *this; + } + + T* get() const { return this->data(); } + + PassOwnPtr release() + { + return adoptPtr(this->take()); + } +}; template class PassOwnPtr { diff --git a/masm/stubs/wtf/RefCounted.h b/masm/stubs/wtf/RefCounted.h index f905ace8ad..4fc9ad9074 100644 --- a/masm/stubs/wtf/RefCounted.h +++ b/masm/stubs/wtf/RefCounted.h @@ -41,6 +41,8 @@ #ifndef REFCOUNTED_H #define REFCOUNTED_H +#include "PassRefPtr.h" + template class RefCounted { public: diff --git a/masm/stubs/wtf/Vector.h b/masm/stubs/wtf/Vector.h index 2682824da8..1feea851e1 100644 --- a/masm/stubs/wtf/Vector.h +++ b/masm/stubs/wtf/Vector.h @@ -43,6 +43,8 @@ #include #include +#include +#include namespace WTF { @@ -55,12 +57,39 @@ public: inline void append(const T& value) { this->push_back(value); } + inline void append(const Vector& vector) + { + this->insert(this->end(), vector.begin(), vector.end()); + } + + using std::vector::insert; + + inline void insert(size_t position, T value) + { this->insert(this->begin() + position, value); } + inline void grow(size_t size) { this->resize(size); } + + inline void shrink(size_t size) + { this->erase(this->begin() + size, this->end()); } + + inline void remove(size_t position) + { this->erase(this->begin() + position); } + + inline bool isEmpty() const { return this->empty(); } + + inline T &last() { return *(this->begin() + this->size() - 1); } }; +template +void deleteAllValues(const Vector &vector) +{ + qDeleteAll(vector); +} + } using WTF::Vector; +using WTF::deleteAllValues; #endif // VECTOR_H diff --git a/masm/stubs/wtf/text/CString.h b/masm/stubs/wtf/text/CString.h new file mode 100644 index 0000000000..c9a65e5c0b --- /dev/null +++ b/masm/stubs/wtf/text/CString.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CSTRING_H +#define CSTRING_H + +#endif // CSTRING_H diff --git a/masm/stubs/wtf/text/WTFString.h b/masm/stubs/wtf/text/WTFString.h new file mode 100644 index 0000000000..d157dc7adc --- /dev/null +++ b/masm/stubs/wtf/text/WTFString.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef WTFSTRING_H +#define WTFSTRING_H + +#include +#include +#include + +namespace WTF { + +class String : public QString +{ +public: + String(const QString& s) : QString(s) {} + bool is8Bit() const { return false; } + const unsigned char *characters8() const { return 0; } + const UChar *characters16() const { return reinterpret_cast(constData()); } + + template + const T* getCharacters() const; + +}; + +template <> +inline const unsigned char* String::getCharacters() const { return characters8(); } +template <> +inline const UChar* String::getCharacters() const { return characters16(); } + +} + +// Don't import WTF::String into the global namespace to avoid conflicts with QQmlJS::VM::String +namespace JSC { + using WTF::String; +} + +#endif // WTFSTRING_H diff --git a/masm/stubs/wtf/unicode/Unicode.h b/masm/stubs/wtf/unicode/Unicode.h new file mode 100644 index 0000000000..d61bc64c5a --- /dev/null +++ b/masm/stubs/wtf/unicode/Unicode.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef UNICODE_H +#define UNICODE_H + +#include + +typedef unsigned char LChar; +typedef uint16_t UChar; + +namespace Unicode { + inline UChar toLower(UChar ch) { + return QChar::toLower(ch); + } + + inline UChar toUpper(UChar ch) { + return QChar::toUpper(ch); + } +} + +#endif // UNICODE_H diff --git a/masm/wtf/ASCIICType.h b/masm/wtf/ASCIICType.h new file mode 100644 index 0000000000..18e108e1bf --- /dev/null +++ b/masm/wtf/ASCIICType.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_ASCIICType_h +#define WTF_ASCIICType_h + +#include + +// The behavior of many of the functions in the header is dependent +// on the current locale. But in the WebKit project, all uses of those functions +// are in code processing something that's not locale-specific. These equivalents +// for some of the functions are named more explicitly, not dependent +// on the C library locale, and we should also optimize them as needed. + +// All functions return false or leave the character unchanged if passed a character +// that is outside the range 0-7F. So they can be used on Unicode strings or +// characters if the intent is to do processing only if the character is ASCII. + +namespace WTF { + +template inline bool isASCII(CharType c) +{ + return !(c & ~0x7F); +} + +template inline bool isASCIIAlpha(CharType c) +{ + return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; +} + +template inline bool isASCIIDigit(CharType c) +{ + return c >= '0' && c <= '9'; +} + +template inline bool isASCIIAlphanumeric(CharType c) +{ + return isASCIIDigit(c) || isASCIIAlpha(c); +} + +template inline bool isASCIIHexDigit(CharType c) +{ + return isASCIIDigit(c) || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); +} + +template inline bool isASCIILower(CharType c) +{ + return c >= 'a' && c <= 'z'; +} + +template inline bool isASCIIOctalDigit(CharType c) +{ + return (c >= '0') & (c <= '7'); +} + +template inline bool isASCIIPrintable(CharType c) +{ + return c >= ' ' && c <= '~'; +} + +/* + Statistics from a run of Apple's page load test for callers of isASCIISpace: + + character count + --------- ----- + non-spaces 689383 + 20 space 294720 + 0A \n 89059 + 09 \t 28320 + 0D \r 0 + 0C \f 0 + 0B \v 0 + */ +template inline bool isASCIISpace(CharType c) +{ + return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); +} + +template inline bool isASCIIUpper(CharType c) +{ + return c >= 'A' && c <= 'Z'; +} + +template inline CharType toASCIILower(CharType c) +{ + return c | ((c >= 'A' && c <= 'Z') << 5); +} + +template inline CharType toASCIILowerUnchecked(CharType character) +{ + // This function can be used for comparing any input character + // to a lowercase English character. The isASCIIAlphaCaselessEqual + // below should be used for regular comparison of ASCII alpha + // characters, but switch statements in CSS tokenizer require + // direct use of this function. + return character | 0x20; +} + +template inline CharType toASCIIUpper(CharType c) +{ + return c & ~((c >= 'a' && c <= 'z') << 5); +} + +template inline int toASCIIHexValue(CharType c) +{ + ASSERT(isASCIIHexDigit(c)); + return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; +} + +template inline int toASCIIHexValue(CharType upperValue, CharType lowerValue) +{ + ASSERT(isASCIIHexDigit(upperValue) && isASCIIHexDigit(lowerValue)); + return ((toASCIIHexValue(upperValue) << 4) & 0xF0) | toASCIIHexValue(lowerValue); +} + +inline char lowerNibbleToASCIIHexDigit(char c) +{ + char nibble = c & 0xF; + return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; +} + +inline char upperNibbleToASCIIHexDigit(char c) +{ + char nibble = (c >> 4) & 0xF; + return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; +} + +template inline bool isASCIIAlphaCaselessEqual(CharType cssCharacter, char character) +{ + // This function compares a (preferrably) constant ASCII + // lowercase letter to any input character. + ASSERT(character >= 'a' && character <= 'z'); + return LIKELY(toASCIILowerUnchecked(cssCharacter) == character); +} + +} + +using WTF::isASCII; +using WTF::isASCIIAlpha; +using WTF::isASCIIAlphanumeric; +using WTF::isASCIIDigit; +using WTF::isASCIIHexDigit; +using WTF::isASCIILower; +using WTF::isASCIIOctalDigit; +using WTF::isASCIIPrintable; +using WTF::isASCIISpace; +using WTF::isASCIIUpper; +using WTF::toASCIIHexValue; +using WTF::toASCIILower; +using WTF::toASCIILowerUnchecked; +using WTF::toASCIIUpper; +using WTF::lowerNibbleToASCIIHexDigit; +using WTF::upperNibbleToASCIIHexDigit; +using WTF::isASCIIAlphaCaselessEqual; + +#endif diff --git a/masm/wtf/BumpPointerAllocator.h b/masm/wtf/BumpPointerAllocator.h new file mode 100644 index 0000000000..3b2cfd974a --- /dev/null +++ b/masm/wtf/BumpPointerAllocator.h @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BumpPointerAllocator_h +#define BumpPointerAllocator_h + +#include +#include +#include + +namespace WTF { + +#define MINIMUM_BUMP_POOL_SIZE 0x1000 + +class BumpPointerPool { +public: + // ensureCapacity will check whether the current pool has capacity to + // allocate 'size' bytes of memory If it does not, it will attempt to + // allocate a new pool (which will be added to this one in a chain). + // + // If allocation fails (out of memory) this method will return null. + // If the return value is non-null, then callers should update any + // references they have to this current (possibly full) BumpPointerPool + // to instead point to the newly returned BumpPointerPool. + BumpPointerPool* ensureCapacity(size_t size) + { + void* allocationEnd = static_cast(m_current) + size; + ASSERT(allocationEnd > m_current); // check for overflow + if (allocationEnd <= static_cast(this)) + return this; + return ensureCapacityCrossPool(this, size); + } + + // alloc should only be called after calling ensureCapacity; as such + // alloc will never fail. + void* alloc(size_t size) + { + void* current = m_current; + void* allocationEnd = static_cast(current) + size; + ASSERT(allocationEnd > current); // check for overflow + ASSERT(allocationEnd <= static_cast(this)); + m_current = allocationEnd; + return current; + } + + // The dealloc method releases memory allocated using alloc. Memory + // must be released in a LIFO fashion, e.g. if the client calls alloc + // four times, returning pointer A, B, C, D, then the only valid order + // in which these may be deallocaed is D, C, B, A. + // + // The client may optionally skip some deallocations. In the example + // above, it would be valid to only explicitly dealloc C, A (D being + // dealloced along with C, B along with A). + // + // If pointer was not allocated from this pool (or pools) then dealloc + // will CRASH(). Callers should update any references they have to + // this current BumpPointerPool to instead point to the returned + // BumpPointerPool. + BumpPointerPool* dealloc(void* position) + { + if ((position >= m_start) && (position <= static_cast(this))) { + ASSERT(position <= m_current); + m_current = position; + return this; + } + return deallocCrossPool(this, position); + } + +private: + // Placement operator new, returns the last 'size' bytes of allocation for use as this. + void* operator new(size_t size, const PageAllocation& allocation) + { + ASSERT(size < allocation.size()); + return reinterpret_cast(reinterpret_cast(allocation.base()) + allocation.size()) - size; + } + + BumpPointerPool(const PageAllocation& allocation) + : m_current(allocation.base()) + , m_start(allocation.base()) + , m_next(0) + , m_previous(0) + , m_allocation(allocation) + { + } + + static BumpPointerPool* create(size_t minimumCapacity = 0) + { + // Add size of BumpPointerPool object, check for overflow. + minimumCapacity += sizeof(BumpPointerPool); + if (minimumCapacity < sizeof(BumpPointerPool)) + return 0; + + size_t poolSize = std::max(static_cast(MINIMUM_BUMP_POOL_SIZE), WTF::pageSize()); + while (poolSize < minimumCapacity) { + poolSize <<= 1; + // The following if check relies on MINIMUM_BUMP_POOL_SIZE being a power of 2! + ASSERT(!(MINIMUM_BUMP_POOL_SIZE & (MINIMUM_BUMP_POOL_SIZE - 1))); + if (!poolSize) + return 0; + } + + PageAllocation allocation = PageAllocation::allocate(poolSize); + if (!!allocation) + return new (allocation) BumpPointerPool(allocation); + return 0; + } + + void shrink() + { + ASSERT(!m_previous); + m_current = m_start; + while (m_next) { + BumpPointerPool* nextNext = m_next->m_next; + m_next->destroy(); + m_next = nextNext; + } + } + + void destroy() + { + m_allocation.deallocate(); + } + + static BumpPointerPool* ensureCapacityCrossPool(BumpPointerPool* previousPool, size_t size) + { + // The pool passed should not have capacity, so we'll start with the next one. + ASSERT(previousPool); + ASSERT((static_cast(previousPool->m_current) + size) > previousPool->m_current); // check for overflow + ASSERT((static_cast(previousPool->m_current) + size) > static_cast(previousPool)); + BumpPointerPool* pool = previousPool->m_next; + + while (true) { + if (!pool) { + // We've run to the end; allocate a new pool. + pool = BumpPointerPool::create(size); + previousPool->m_next = pool; + pool->m_previous = previousPool; + return pool; + } + + // + void* current = pool->m_current; + void* allocationEnd = static_cast(current) + size; + ASSERT(allocationEnd > current); // check for overflow + if (allocationEnd <= static_cast(pool)) + return pool; + } + } + + static BumpPointerPool* deallocCrossPool(BumpPointerPool* pool, void* position) + { + // Should only be called if position is not in the current pool. + ASSERT((position < pool->m_start) || (position > static_cast(pool))); + + while (true) { + // Unwind the current pool to the start, move back in the chain to the previous pool. + pool->m_current = pool->m_start; + pool = pool->m_previous; + + // position was nowhere in the chain! + if (!pool) + CRASH(); + + if ((position >= pool->m_start) && (position <= static_cast(pool))) { + ASSERT(position <= pool->m_current); + pool->m_current = position; + return pool; + } + } + } + + void* m_current; + void* m_start; + BumpPointerPool* m_next; + BumpPointerPool* m_previous; + PageAllocation m_allocation; + + friend class BumpPointerAllocator; +}; + +// A BumpPointerAllocator manages a set of BumpPointerPool objects, which +// can be used for LIFO (stack like) allocation. +// +// To begin allocating using this class call startAllocator(). The result +// of this method will be null if the initial pool allocation fails, or a +// pointer to a BumpPointerPool object that can be used to perform +// allocations. Whilst running no memory will be released until +// stopAllocator() is called. At this point all allocations made through +// this allocator will be reaped, and underlying memory may be freed. +// +// (In practice we will still hold on to the initial pool to allow allocation +// to be quickly restared, but aditional pools will be freed). +// +// This allocator is non-renetrant, it is encumbant on the clients to ensure +// startAllocator() is not called again until stopAllocator() has been called. +class BumpPointerAllocator { +public: + BumpPointerAllocator() + : m_head(0) + { + } + + ~BumpPointerAllocator() + { + if (m_head) + m_head->destroy(); + } + + BumpPointerPool* startAllocator() + { + if (!m_head) + m_head = BumpPointerPool::create(); + return m_head; + } + + void stopAllocator() + { + if (m_head) + m_head->shrink(); + } + +private: + BumpPointerPool* m_head; +}; + +} + +using WTF::BumpPointerAllocator; + +#endif // BumpPointerAllocator_h diff --git a/masm/yarr/Yarr.h b/masm/yarr/Yarr.h new file mode 100644 index 0000000000..d393e9fa90 --- /dev/null +++ b/masm/yarr/Yarr.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Yarr_h +#define Yarr_h + +#include "YarrInterpreter.h" +#include "YarrPattern.h" + +namespace JSC { namespace Yarr { + +#define YarrStackSpaceForBackTrackInfoPatternCharacter 1 // Only for !fixed quantifiers. +#define YarrStackSpaceForBackTrackInfoCharacterClass 1 // Only for !fixed quantifiers. +#define YarrStackSpaceForBackTrackInfoBackReference 2 +#define YarrStackSpaceForBackTrackInfoAlternative 1 // One per alternative. +#define YarrStackSpaceForBackTrackInfoParentheticalAssertion 1 +#define YarrStackSpaceForBackTrackInfoParenthesesOnce 1 // Only for !fixed quantifiers. +#define YarrStackSpaceForBackTrackInfoParenthesesTerminal 1 +#define YarrStackSpaceForBackTrackInfoParentheses 2 + +static const unsigned quantifyInfinite = UINT_MAX; +static const unsigned offsetNoMatch = (unsigned)-1; + +// The below limit restricts the number of "recursive" match calls in order to +// avoid spending exponential time on complex regular expressions. +static const unsigned matchLimit = 1000000; + +enum JSRegExpResult { + JSRegExpMatch = 1, + JSRegExpNoMatch = 0, + JSRegExpErrorNoMatch = -1, + JSRegExpErrorHitLimit = -2, + JSRegExpErrorNoMemory = -3, + JSRegExpErrorInternal = -4 +}; + +enum YarrCharSize { + Char8, + Char16 +}; + +} } // namespace JSC::Yarr + +#endif // Yarr_h + diff --git a/masm/yarr/YarrCanonicalizeUCS2.cpp b/masm/yarr/YarrCanonicalizeUCS2.cpp new file mode 100644 index 0000000000..7bb3d08eb5 --- /dev/null +++ b/masm/yarr/YarrCanonicalizeUCS2.cpp @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// DO NOT EDIT! - this file autogenerated by YarrCanonicalizeUCS2.js + +#include "config.h" +#include "YarrCanonicalizeUCS2.h" + +namespace JSC { namespace Yarr { + +#include + +uint16_t ucs2CharacterSet0[] = { 0x01c4u, 0x01c5u, 0x01c6u, 0 }; +uint16_t ucs2CharacterSet1[] = { 0x01c7u, 0x01c8u, 0x01c9u, 0 }; +uint16_t ucs2CharacterSet2[] = { 0x01cau, 0x01cbu, 0x01ccu, 0 }; +uint16_t ucs2CharacterSet3[] = { 0x01f1u, 0x01f2u, 0x01f3u, 0 }; +uint16_t ucs2CharacterSet4[] = { 0x0392u, 0x03b2u, 0x03d0u, 0 }; +uint16_t ucs2CharacterSet5[] = { 0x0395u, 0x03b5u, 0x03f5u, 0 }; +uint16_t ucs2CharacterSet6[] = { 0x0398u, 0x03b8u, 0x03d1u, 0 }; +uint16_t ucs2CharacterSet7[] = { 0x0345u, 0x0399u, 0x03b9u, 0x1fbeu, 0 }; +uint16_t ucs2CharacterSet8[] = { 0x039au, 0x03bau, 0x03f0u, 0 }; +uint16_t ucs2CharacterSet9[] = { 0x00b5u, 0x039cu, 0x03bcu, 0 }; +uint16_t ucs2CharacterSet10[] = { 0x03a0u, 0x03c0u, 0x03d6u, 0 }; +uint16_t ucs2CharacterSet11[] = { 0x03a1u, 0x03c1u, 0x03f1u, 0 }; +uint16_t ucs2CharacterSet12[] = { 0x03a3u, 0x03c2u, 0x03c3u, 0 }; +uint16_t ucs2CharacterSet13[] = { 0x03a6u, 0x03c6u, 0x03d5u, 0 }; +uint16_t ucs2CharacterSet14[] = { 0x1e60u, 0x1e61u, 0x1e9bu, 0 }; + +static const size_t UCS2_CANONICALIZATION_SETS = 15; +uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = { + ucs2CharacterSet0, + ucs2CharacterSet1, + ucs2CharacterSet2, + ucs2CharacterSet3, + ucs2CharacterSet4, + ucs2CharacterSet5, + ucs2CharacterSet6, + ucs2CharacterSet7, + ucs2CharacterSet8, + ucs2CharacterSet9, + ucs2CharacterSet10, + ucs2CharacterSet11, + ucs2CharacterSet12, + ucs2CharacterSet13, + ucs2CharacterSet14, +}; + +const size_t UCS2_CANONICALIZATION_RANGES = 364; +UCS2CanonicalizationRange rangeInfo[UCS2_CANONICALIZATION_RANGES] = { + { 0x0000u, 0x0040u, 0x0000u, CanonicalizeUnique }, + { 0x0041u, 0x005au, 0x0020u, CanonicalizeRangeLo }, + { 0x005bu, 0x0060u, 0x0000u, CanonicalizeUnique }, + { 0x0061u, 0x007au, 0x0020u, CanonicalizeRangeHi }, + { 0x007bu, 0x00b4u, 0x0000u, CanonicalizeUnique }, + { 0x00b5u, 0x00b5u, 0x0009u, CanonicalizeSet }, + { 0x00b6u, 0x00bfu, 0x0000u, CanonicalizeUnique }, + { 0x00c0u, 0x00d6u, 0x0020u, CanonicalizeRangeLo }, + { 0x00d7u, 0x00d7u, 0x0000u, CanonicalizeUnique }, + { 0x00d8u, 0x00deu, 0x0020u, CanonicalizeRangeLo }, + { 0x00dfu, 0x00dfu, 0x0000u, CanonicalizeUnique }, + { 0x00e0u, 0x00f6u, 0x0020u, CanonicalizeRangeHi }, + { 0x00f7u, 0x00f7u, 0x0000u, CanonicalizeUnique }, + { 0x00f8u, 0x00feu, 0x0020u, CanonicalizeRangeHi }, + { 0x00ffu, 0x00ffu, 0x0079u, CanonicalizeRangeLo }, + { 0x0100u, 0x012fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0130u, 0x0131u, 0x0000u, CanonicalizeUnique }, + { 0x0132u, 0x0137u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0138u, 0x0138u, 0x0000u, CanonicalizeUnique }, + { 0x0139u, 0x0148u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0149u, 0x0149u, 0x0000u, CanonicalizeUnique }, + { 0x014au, 0x0177u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0178u, 0x0178u, 0x0079u, CanonicalizeRangeHi }, + { 0x0179u, 0x017eu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x017fu, 0x017fu, 0x0000u, CanonicalizeUnique }, + { 0x0180u, 0x0180u, 0x00c3u, CanonicalizeRangeLo }, + { 0x0181u, 0x0181u, 0x00d2u, CanonicalizeRangeLo }, + { 0x0182u, 0x0185u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0186u, 0x0186u, 0x00ceu, CanonicalizeRangeLo }, + { 0x0187u, 0x0188u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0189u, 0x018au, 0x00cdu, CanonicalizeRangeLo }, + { 0x018bu, 0x018cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x018du, 0x018du, 0x0000u, CanonicalizeUnique }, + { 0x018eu, 0x018eu, 0x004fu, CanonicalizeRangeLo }, + { 0x018fu, 0x018fu, 0x00cau, CanonicalizeRangeLo }, + { 0x0190u, 0x0190u, 0x00cbu, CanonicalizeRangeLo }, + { 0x0191u, 0x0192u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0193u, 0x0193u, 0x00cdu, CanonicalizeRangeLo }, + { 0x0194u, 0x0194u, 0x00cfu, CanonicalizeRangeLo }, + { 0x0195u, 0x0195u, 0x0061u, CanonicalizeRangeLo }, + { 0x0196u, 0x0196u, 0x00d3u, CanonicalizeRangeLo }, + { 0x0197u, 0x0197u, 0x00d1u, CanonicalizeRangeLo }, + { 0x0198u, 0x0199u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x019au, 0x019au, 0x00a3u, CanonicalizeRangeLo }, + { 0x019bu, 0x019bu, 0x0000u, CanonicalizeUnique }, + { 0x019cu, 0x019cu, 0x00d3u, CanonicalizeRangeLo }, + { 0x019du, 0x019du, 0x00d5u, CanonicalizeRangeLo }, + { 0x019eu, 0x019eu, 0x0082u, CanonicalizeRangeLo }, + { 0x019fu, 0x019fu, 0x00d6u, CanonicalizeRangeLo }, + { 0x01a0u, 0x01a5u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01a6u, 0x01a6u, 0x00dau, CanonicalizeRangeLo }, + { 0x01a7u, 0x01a8u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01a9u, 0x01a9u, 0x00dau, CanonicalizeRangeLo }, + { 0x01aau, 0x01abu, 0x0000u, CanonicalizeUnique }, + { 0x01acu, 0x01adu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01aeu, 0x01aeu, 0x00dau, CanonicalizeRangeLo }, + { 0x01afu, 0x01b0u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01b1u, 0x01b2u, 0x00d9u, CanonicalizeRangeLo }, + { 0x01b3u, 0x01b6u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01b7u, 0x01b7u, 0x00dbu, CanonicalizeRangeLo }, + { 0x01b8u, 0x01b9u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01bau, 0x01bbu, 0x0000u, CanonicalizeUnique }, + { 0x01bcu, 0x01bdu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01beu, 0x01beu, 0x0000u, CanonicalizeUnique }, + { 0x01bfu, 0x01bfu, 0x0038u, CanonicalizeRangeLo }, + { 0x01c0u, 0x01c3u, 0x0000u, CanonicalizeUnique }, + { 0x01c4u, 0x01c6u, 0x0000u, CanonicalizeSet }, + { 0x01c7u, 0x01c9u, 0x0001u, CanonicalizeSet }, + { 0x01cau, 0x01ccu, 0x0002u, CanonicalizeSet }, + { 0x01cdu, 0x01dcu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01ddu, 0x01ddu, 0x004fu, CanonicalizeRangeHi }, + { 0x01deu, 0x01efu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01f0u, 0x01f0u, 0x0000u, CanonicalizeUnique }, + { 0x01f1u, 0x01f3u, 0x0003u, CanonicalizeSet }, + { 0x01f4u, 0x01f5u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01f6u, 0x01f6u, 0x0061u, CanonicalizeRangeHi }, + { 0x01f7u, 0x01f7u, 0x0038u, CanonicalizeRangeHi }, + { 0x01f8u, 0x021fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0220u, 0x0220u, 0x0082u, CanonicalizeRangeHi }, + { 0x0221u, 0x0221u, 0x0000u, CanonicalizeUnique }, + { 0x0222u, 0x0233u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0234u, 0x0239u, 0x0000u, CanonicalizeUnique }, + { 0x023au, 0x023au, 0x2a2bu, CanonicalizeRangeLo }, + { 0x023bu, 0x023cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x023du, 0x023du, 0x00a3u, CanonicalizeRangeHi }, + { 0x023eu, 0x023eu, 0x2a28u, CanonicalizeRangeLo }, + { 0x023fu, 0x0240u, 0x2a3fu, CanonicalizeRangeLo }, + { 0x0241u, 0x0242u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0243u, 0x0243u, 0x00c3u, CanonicalizeRangeHi }, + { 0x0244u, 0x0244u, 0x0045u, CanonicalizeRangeLo }, + { 0x0245u, 0x0245u, 0x0047u, CanonicalizeRangeLo }, + { 0x0246u, 0x024fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0250u, 0x0250u, 0x2a1fu, CanonicalizeRangeLo }, + { 0x0251u, 0x0251u, 0x2a1cu, CanonicalizeRangeLo }, + { 0x0252u, 0x0252u, 0x2a1eu, CanonicalizeRangeLo }, + { 0x0253u, 0x0253u, 0x00d2u, CanonicalizeRangeHi }, + { 0x0254u, 0x0254u, 0x00ceu, CanonicalizeRangeHi }, + { 0x0255u, 0x0255u, 0x0000u, CanonicalizeUnique }, + { 0x0256u, 0x0257u, 0x00cdu, CanonicalizeRangeHi }, + { 0x0258u, 0x0258u, 0x0000u, CanonicalizeUnique }, + { 0x0259u, 0x0259u, 0x00cau, CanonicalizeRangeHi }, + { 0x025au, 0x025au, 0x0000u, CanonicalizeUnique }, + { 0x025bu, 0x025bu, 0x00cbu, CanonicalizeRangeHi }, + { 0x025cu, 0x025fu, 0x0000u, CanonicalizeUnique }, + { 0x0260u, 0x0260u, 0x00cdu, CanonicalizeRangeHi }, + { 0x0261u, 0x0262u, 0x0000u, CanonicalizeUnique }, + { 0x0263u, 0x0263u, 0x00cfu, CanonicalizeRangeHi }, + { 0x0264u, 0x0264u, 0x0000u, CanonicalizeUnique }, + { 0x0265u, 0x0265u, 0xa528u, CanonicalizeRangeLo }, + { 0x0266u, 0x0267u, 0x0000u, CanonicalizeUnique }, + { 0x0268u, 0x0268u, 0x00d1u, CanonicalizeRangeHi }, + { 0x0269u, 0x0269u, 0x00d3u, CanonicalizeRangeHi }, + { 0x026au, 0x026au, 0x0000u, CanonicalizeUnique }, + { 0x026bu, 0x026bu, 0x29f7u, CanonicalizeRangeLo }, + { 0x026cu, 0x026eu, 0x0000u, CanonicalizeUnique }, + { 0x026fu, 0x026fu, 0x00d3u, CanonicalizeRangeHi }, + { 0x0270u, 0x0270u, 0x0000u, CanonicalizeUnique }, + { 0x0271u, 0x0271u, 0x29fdu, CanonicalizeRangeLo }, + { 0x0272u, 0x0272u, 0x00d5u, CanonicalizeRangeHi }, + { 0x0273u, 0x0274u, 0x0000u, CanonicalizeUnique }, + { 0x0275u, 0x0275u, 0x00d6u, CanonicalizeRangeHi }, + { 0x0276u, 0x027cu, 0x0000u, CanonicalizeUnique }, + { 0x027du, 0x027du, 0x29e7u, CanonicalizeRangeLo }, + { 0x027eu, 0x027fu, 0x0000u, CanonicalizeUnique }, + { 0x0280u, 0x0280u, 0x00dau, CanonicalizeRangeHi }, + { 0x0281u, 0x0282u, 0x0000u, CanonicalizeUnique }, + { 0x0283u, 0x0283u, 0x00dau, CanonicalizeRangeHi }, + { 0x0284u, 0x0287u, 0x0000u, CanonicalizeUnique }, + { 0x0288u, 0x0288u, 0x00dau, CanonicalizeRangeHi }, + { 0x0289u, 0x0289u, 0x0045u, CanonicalizeRangeHi }, + { 0x028au, 0x028bu, 0x00d9u, CanonicalizeRangeHi }, + { 0x028cu, 0x028cu, 0x0047u, CanonicalizeRangeHi }, + { 0x028du, 0x0291u, 0x0000u, CanonicalizeUnique }, + { 0x0292u, 0x0292u, 0x00dbu, CanonicalizeRangeHi }, + { 0x0293u, 0x0344u, 0x0000u, CanonicalizeUnique }, + { 0x0345u, 0x0345u, 0x0007u, CanonicalizeSet }, + { 0x0346u, 0x036fu, 0x0000u, CanonicalizeUnique }, + { 0x0370u, 0x0373u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0374u, 0x0375u, 0x0000u, CanonicalizeUnique }, + { 0x0376u, 0x0377u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0378u, 0x037au, 0x0000u, CanonicalizeUnique }, + { 0x037bu, 0x037du, 0x0082u, CanonicalizeRangeLo }, + { 0x037eu, 0x0385u, 0x0000u, CanonicalizeUnique }, + { 0x0386u, 0x0386u, 0x0026u, CanonicalizeRangeLo }, + { 0x0387u, 0x0387u, 0x0000u, CanonicalizeUnique }, + { 0x0388u, 0x038au, 0x0025u, CanonicalizeRangeLo }, + { 0x038bu, 0x038bu, 0x0000u, CanonicalizeUnique }, + { 0x038cu, 0x038cu, 0x0040u, CanonicalizeRangeLo }, + { 0x038du, 0x038du, 0x0000u, CanonicalizeUnique }, + { 0x038eu, 0x038fu, 0x003fu, CanonicalizeRangeLo }, + { 0x0390u, 0x0390u, 0x0000u, CanonicalizeUnique }, + { 0x0391u, 0x0391u, 0x0020u, CanonicalizeRangeLo }, + { 0x0392u, 0x0392u, 0x0004u, CanonicalizeSet }, + { 0x0393u, 0x0394u, 0x0020u, CanonicalizeRangeLo }, + { 0x0395u, 0x0395u, 0x0005u, CanonicalizeSet }, + { 0x0396u, 0x0397u, 0x0020u, CanonicalizeRangeLo }, + { 0x0398u, 0x0398u, 0x0006u, CanonicalizeSet }, + { 0x0399u, 0x0399u, 0x0007u, CanonicalizeSet }, + { 0x039au, 0x039au, 0x0008u, CanonicalizeSet }, + { 0x039bu, 0x039bu, 0x0020u, CanonicalizeRangeLo }, + { 0x039cu, 0x039cu, 0x0009u, CanonicalizeSet }, + { 0x039du, 0x039fu, 0x0020u, CanonicalizeRangeLo }, + { 0x03a0u, 0x03a0u, 0x000au, CanonicalizeSet }, + { 0x03a1u, 0x03a1u, 0x000bu, CanonicalizeSet }, + { 0x03a2u, 0x03a2u, 0x0000u, CanonicalizeUnique }, + { 0x03a3u, 0x03a3u, 0x000cu, CanonicalizeSet }, + { 0x03a4u, 0x03a5u, 0x0020u, CanonicalizeRangeLo }, + { 0x03a6u, 0x03a6u, 0x000du, CanonicalizeSet }, + { 0x03a7u, 0x03abu, 0x0020u, CanonicalizeRangeLo }, + { 0x03acu, 0x03acu, 0x0026u, CanonicalizeRangeHi }, + { 0x03adu, 0x03afu, 0x0025u, CanonicalizeRangeHi }, + { 0x03b0u, 0x03b0u, 0x0000u, CanonicalizeUnique }, + { 0x03b1u, 0x03b1u, 0x0020u, CanonicalizeRangeHi }, + { 0x03b2u, 0x03b2u, 0x0004u, CanonicalizeSet }, + { 0x03b3u, 0x03b4u, 0x0020u, CanonicalizeRangeHi }, + { 0x03b5u, 0x03b5u, 0x0005u, CanonicalizeSet }, + { 0x03b6u, 0x03b7u, 0x0020u, CanonicalizeRangeHi }, + { 0x03b8u, 0x03b8u, 0x0006u, CanonicalizeSet }, + { 0x03b9u, 0x03b9u, 0x0007u, CanonicalizeSet }, + { 0x03bau, 0x03bau, 0x0008u, CanonicalizeSet }, + { 0x03bbu, 0x03bbu, 0x0020u, CanonicalizeRangeHi }, + { 0x03bcu, 0x03bcu, 0x0009u, CanonicalizeSet }, + { 0x03bdu, 0x03bfu, 0x0020u, CanonicalizeRangeHi }, + { 0x03c0u, 0x03c0u, 0x000au, CanonicalizeSet }, + { 0x03c1u, 0x03c1u, 0x000bu, CanonicalizeSet }, + { 0x03c2u, 0x03c3u, 0x000cu, CanonicalizeSet }, + { 0x03c4u, 0x03c5u, 0x0020u, CanonicalizeRangeHi }, + { 0x03c6u, 0x03c6u, 0x000du, CanonicalizeSet }, + { 0x03c7u, 0x03cbu, 0x0020u, CanonicalizeRangeHi }, + { 0x03ccu, 0x03ccu, 0x0040u, CanonicalizeRangeHi }, + { 0x03cdu, 0x03ceu, 0x003fu, CanonicalizeRangeHi }, + { 0x03cfu, 0x03cfu, 0x0008u, CanonicalizeRangeLo }, + { 0x03d0u, 0x03d0u, 0x0004u, CanonicalizeSet }, + { 0x03d1u, 0x03d1u, 0x0006u, CanonicalizeSet }, + { 0x03d2u, 0x03d4u, 0x0000u, CanonicalizeUnique }, + { 0x03d5u, 0x03d5u, 0x000du, CanonicalizeSet }, + { 0x03d6u, 0x03d6u, 0x000au, CanonicalizeSet }, + { 0x03d7u, 0x03d7u, 0x0008u, CanonicalizeRangeHi }, + { 0x03d8u, 0x03efu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x03f0u, 0x03f0u, 0x0008u, CanonicalizeSet }, + { 0x03f1u, 0x03f1u, 0x000bu, CanonicalizeSet }, + { 0x03f2u, 0x03f2u, 0x0007u, CanonicalizeRangeLo }, + { 0x03f3u, 0x03f4u, 0x0000u, CanonicalizeUnique }, + { 0x03f5u, 0x03f5u, 0x0005u, CanonicalizeSet }, + { 0x03f6u, 0x03f6u, 0x0000u, CanonicalizeUnique }, + { 0x03f7u, 0x03f8u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x03f9u, 0x03f9u, 0x0007u, CanonicalizeRangeHi }, + { 0x03fau, 0x03fbu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x03fcu, 0x03fcu, 0x0000u, CanonicalizeUnique }, + { 0x03fdu, 0x03ffu, 0x0082u, CanonicalizeRangeHi }, + { 0x0400u, 0x040fu, 0x0050u, CanonicalizeRangeLo }, + { 0x0410u, 0x042fu, 0x0020u, CanonicalizeRangeLo }, + { 0x0430u, 0x044fu, 0x0020u, CanonicalizeRangeHi }, + { 0x0450u, 0x045fu, 0x0050u, CanonicalizeRangeHi }, + { 0x0460u, 0x0481u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0482u, 0x0489u, 0x0000u, CanonicalizeUnique }, + { 0x048au, 0x04bfu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x04c0u, 0x04c0u, 0x000fu, CanonicalizeRangeLo }, + { 0x04c1u, 0x04ceu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x04cfu, 0x04cfu, 0x000fu, CanonicalizeRangeHi }, + { 0x04d0u, 0x0527u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0528u, 0x0530u, 0x0000u, CanonicalizeUnique }, + { 0x0531u, 0x0556u, 0x0030u, CanonicalizeRangeLo }, + { 0x0557u, 0x0560u, 0x0000u, CanonicalizeUnique }, + { 0x0561u, 0x0586u, 0x0030u, CanonicalizeRangeHi }, + { 0x0587u, 0x109fu, 0x0000u, CanonicalizeUnique }, + { 0x10a0u, 0x10c5u, 0x1c60u, CanonicalizeRangeLo }, + { 0x10c6u, 0x1d78u, 0x0000u, CanonicalizeUnique }, + { 0x1d79u, 0x1d79u, 0x8a04u, CanonicalizeRangeLo }, + { 0x1d7au, 0x1d7cu, 0x0000u, CanonicalizeUnique }, + { 0x1d7du, 0x1d7du, 0x0ee6u, CanonicalizeRangeLo }, + { 0x1d7eu, 0x1dffu, 0x0000u, CanonicalizeUnique }, + { 0x1e00u, 0x1e5fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x1e60u, 0x1e61u, 0x000eu, CanonicalizeSet }, + { 0x1e62u, 0x1e95u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x1e96u, 0x1e9au, 0x0000u, CanonicalizeUnique }, + { 0x1e9bu, 0x1e9bu, 0x000eu, CanonicalizeSet }, + { 0x1e9cu, 0x1e9fu, 0x0000u, CanonicalizeUnique }, + { 0x1ea0u, 0x1effu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x1f00u, 0x1f07u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f08u, 0x1f0fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f10u, 0x1f15u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f16u, 0x1f17u, 0x0000u, CanonicalizeUnique }, + { 0x1f18u, 0x1f1du, 0x0008u, CanonicalizeRangeHi }, + { 0x1f1eu, 0x1f1fu, 0x0000u, CanonicalizeUnique }, + { 0x1f20u, 0x1f27u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f28u, 0x1f2fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f30u, 0x1f37u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f38u, 0x1f3fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f40u, 0x1f45u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f46u, 0x1f47u, 0x0000u, CanonicalizeUnique }, + { 0x1f48u, 0x1f4du, 0x0008u, CanonicalizeRangeHi }, + { 0x1f4eu, 0x1f50u, 0x0000u, CanonicalizeUnique }, + { 0x1f51u, 0x1f51u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f52u, 0x1f52u, 0x0000u, CanonicalizeUnique }, + { 0x1f53u, 0x1f53u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f54u, 0x1f54u, 0x0000u, CanonicalizeUnique }, + { 0x1f55u, 0x1f55u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f56u, 0x1f56u, 0x0000u, CanonicalizeUnique }, + { 0x1f57u, 0x1f57u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f58u, 0x1f58u, 0x0000u, CanonicalizeUnique }, + { 0x1f59u, 0x1f59u, 0x0008u, CanonicalizeRangeHi }, + { 0x1f5au, 0x1f5au, 0x0000u, CanonicalizeUnique }, + { 0x1f5bu, 0x1f5bu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f5cu, 0x1f5cu, 0x0000u, CanonicalizeUnique }, + { 0x1f5du, 0x1f5du, 0x0008u, CanonicalizeRangeHi }, + { 0x1f5eu, 0x1f5eu, 0x0000u, CanonicalizeUnique }, + { 0x1f5fu, 0x1f5fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f60u, 0x1f67u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f68u, 0x1f6fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f70u, 0x1f71u, 0x004au, CanonicalizeRangeLo }, + { 0x1f72u, 0x1f75u, 0x0056u, CanonicalizeRangeLo }, + { 0x1f76u, 0x1f77u, 0x0064u, CanonicalizeRangeLo }, + { 0x1f78u, 0x1f79u, 0x0080u, CanonicalizeRangeLo }, + { 0x1f7au, 0x1f7bu, 0x0070u, CanonicalizeRangeLo }, + { 0x1f7cu, 0x1f7du, 0x007eu, CanonicalizeRangeLo }, + { 0x1f7eu, 0x1fafu, 0x0000u, CanonicalizeUnique }, + { 0x1fb0u, 0x1fb1u, 0x0008u, CanonicalizeRangeLo }, + { 0x1fb2u, 0x1fb7u, 0x0000u, CanonicalizeUnique }, + { 0x1fb8u, 0x1fb9u, 0x0008u, CanonicalizeRangeHi }, + { 0x1fbau, 0x1fbbu, 0x004au, CanonicalizeRangeHi }, + { 0x1fbcu, 0x1fbdu, 0x0000u, CanonicalizeUnique }, + { 0x1fbeu, 0x1fbeu, 0x0007u, CanonicalizeSet }, + { 0x1fbfu, 0x1fc7u, 0x0000u, CanonicalizeUnique }, + { 0x1fc8u, 0x1fcbu, 0x0056u, CanonicalizeRangeHi }, + { 0x1fccu, 0x1fcfu, 0x0000u, CanonicalizeUnique }, + { 0x1fd0u, 0x1fd1u, 0x0008u, CanonicalizeRangeLo }, + { 0x1fd2u, 0x1fd7u, 0x0000u, CanonicalizeUnique }, + { 0x1fd8u, 0x1fd9u, 0x0008u, CanonicalizeRangeHi }, + { 0x1fdau, 0x1fdbu, 0x0064u, CanonicalizeRangeHi }, + { 0x1fdcu, 0x1fdfu, 0x0000u, CanonicalizeUnique }, + { 0x1fe0u, 0x1fe1u, 0x0008u, CanonicalizeRangeLo }, + { 0x1fe2u, 0x1fe4u, 0x0000u, CanonicalizeUnique }, + { 0x1fe5u, 0x1fe5u, 0x0007u, CanonicalizeRangeLo }, + { 0x1fe6u, 0x1fe7u, 0x0000u, CanonicalizeUnique }, + { 0x1fe8u, 0x1fe9u, 0x0008u, CanonicalizeRangeHi }, + { 0x1feau, 0x1febu, 0x0070u, CanonicalizeRangeHi }, + { 0x1fecu, 0x1fecu, 0x0007u, CanonicalizeRangeHi }, + { 0x1fedu, 0x1ff7u, 0x0000u, CanonicalizeUnique }, + { 0x1ff8u, 0x1ff9u, 0x0080u, CanonicalizeRangeHi }, + { 0x1ffau, 0x1ffbu, 0x007eu, CanonicalizeRangeHi }, + { 0x1ffcu, 0x2131u, 0x0000u, CanonicalizeUnique }, + { 0x2132u, 0x2132u, 0x001cu, CanonicalizeRangeLo }, + { 0x2133u, 0x214du, 0x0000u, CanonicalizeUnique }, + { 0x214eu, 0x214eu, 0x001cu, CanonicalizeRangeHi }, + { 0x214fu, 0x215fu, 0x0000u, CanonicalizeUnique }, + { 0x2160u, 0x216fu, 0x0010u, CanonicalizeRangeLo }, + { 0x2170u, 0x217fu, 0x0010u, CanonicalizeRangeHi }, + { 0x2180u, 0x2182u, 0x0000u, CanonicalizeUnique }, + { 0x2183u, 0x2184u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2185u, 0x24b5u, 0x0000u, CanonicalizeUnique }, + { 0x24b6u, 0x24cfu, 0x001au, CanonicalizeRangeLo }, + { 0x24d0u, 0x24e9u, 0x001au, CanonicalizeRangeHi }, + { 0x24eau, 0x2bffu, 0x0000u, CanonicalizeUnique }, + { 0x2c00u, 0x2c2eu, 0x0030u, CanonicalizeRangeLo }, + { 0x2c2fu, 0x2c2fu, 0x0000u, CanonicalizeUnique }, + { 0x2c30u, 0x2c5eu, 0x0030u, CanonicalizeRangeHi }, + { 0x2c5fu, 0x2c5fu, 0x0000u, CanonicalizeUnique }, + { 0x2c60u, 0x2c61u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x2c62u, 0x2c62u, 0x29f7u, CanonicalizeRangeHi }, + { 0x2c63u, 0x2c63u, 0x0ee6u, CanonicalizeRangeHi }, + { 0x2c64u, 0x2c64u, 0x29e7u, CanonicalizeRangeHi }, + { 0x2c65u, 0x2c65u, 0x2a2bu, CanonicalizeRangeHi }, + { 0x2c66u, 0x2c66u, 0x2a28u, CanonicalizeRangeHi }, + { 0x2c67u, 0x2c6cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2c6du, 0x2c6du, 0x2a1cu, CanonicalizeRangeHi }, + { 0x2c6eu, 0x2c6eu, 0x29fdu, CanonicalizeRangeHi }, + { 0x2c6fu, 0x2c6fu, 0x2a1fu, CanonicalizeRangeHi }, + { 0x2c70u, 0x2c70u, 0x2a1eu, CanonicalizeRangeHi }, + { 0x2c71u, 0x2c71u, 0x0000u, CanonicalizeUnique }, + { 0x2c72u, 0x2c73u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x2c74u, 0x2c74u, 0x0000u, CanonicalizeUnique }, + { 0x2c75u, 0x2c76u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2c77u, 0x2c7du, 0x0000u, CanonicalizeUnique }, + { 0x2c7eu, 0x2c7fu, 0x2a3fu, CanonicalizeRangeHi }, + { 0x2c80u, 0x2ce3u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x2ce4u, 0x2ceau, 0x0000u, CanonicalizeUnique }, + { 0x2cebu, 0x2ceeu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2cefu, 0x2cffu, 0x0000u, CanonicalizeUnique }, + { 0x2d00u, 0x2d25u, 0x1c60u, CanonicalizeRangeHi }, + { 0x2d26u, 0xa63fu, 0x0000u, CanonicalizeUnique }, + { 0xa640u, 0xa66du, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa66eu, 0xa67fu, 0x0000u, CanonicalizeUnique }, + { 0xa680u, 0xa697u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa698u, 0xa721u, 0x0000u, CanonicalizeUnique }, + { 0xa722u, 0xa72fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa730u, 0xa731u, 0x0000u, CanonicalizeUnique }, + { 0xa732u, 0xa76fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa770u, 0xa778u, 0x0000u, CanonicalizeUnique }, + { 0xa779u, 0xa77cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0xa77du, 0xa77du, 0x8a04u, CanonicalizeRangeHi }, + { 0xa77eu, 0xa787u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa788u, 0xa78au, 0x0000u, CanonicalizeUnique }, + { 0xa78bu, 0xa78cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0xa78du, 0xa78du, 0xa528u, CanonicalizeRangeHi }, + { 0xa78eu, 0xa78fu, 0x0000u, CanonicalizeUnique }, + { 0xa790u, 0xa791u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa792u, 0xa79fu, 0x0000u, CanonicalizeUnique }, + { 0xa7a0u, 0xa7a9u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa7aau, 0xff20u, 0x0000u, CanonicalizeUnique }, + { 0xff21u, 0xff3au, 0x0020u, CanonicalizeRangeLo }, + { 0xff3bu, 0xff40u, 0x0000u, CanonicalizeUnique }, + { 0xff41u, 0xff5au, 0x0020u, CanonicalizeRangeHi }, + { 0xff5bu, 0xffffu, 0x0000u, CanonicalizeUnique }, +}; + +const size_t LATIN_CANONICALIZATION_RANGES = 20; +LatinCanonicalizationRange latinRangeInfo[LATIN_CANONICALIZATION_RANGES] = { + { 0x0000u, 0x0040u, 0x0000u, CanonicalizeLatinSelf }, + { 0x0041u, 0x005au, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x005bu, 0x0060u, 0x0000u, CanonicalizeLatinSelf }, + { 0x0061u, 0x007au, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x007bu, 0x00bfu, 0x0000u, CanonicalizeLatinSelf }, + { 0x00c0u, 0x00d6u, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00d7u, 0x00d7u, 0x0000u, CanonicalizeLatinSelf }, + { 0x00d8u, 0x00deu, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00dfu, 0x00dfu, 0x0000u, CanonicalizeLatinSelf }, + { 0x00e0u, 0x00f6u, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00f7u, 0x00f7u, 0x0000u, CanonicalizeLatinSelf }, + { 0x00f8u, 0x00feu, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00ffu, 0x00ffu, 0x0000u, CanonicalizeLatinSelf }, + { 0x0100u, 0x0177u, 0x0000u, CanonicalizeLatinInvalid }, + { 0x0178u, 0x0178u, 0x00ffu, CanonicalizeLatinOther }, + { 0x0179u, 0x039bu, 0x0000u, CanonicalizeLatinInvalid }, + { 0x039cu, 0x039cu, 0x00b5u, CanonicalizeLatinOther }, + { 0x039du, 0x03bbu, 0x0000u, CanonicalizeLatinInvalid }, + { 0x03bcu, 0x03bcu, 0x00b5u, CanonicalizeLatinOther }, + { 0x03bdu, 0xffffu, 0x0000u, CanonicalizeLatinInvalid }, +}; + +} } // JSC::Yarr + diff --git a/masm/yarr/YarrCanonicalizeUCS2.h b/masm/yarr/YarrCanonicalizeUCS2.h new file mode 100644 index 0000000000..be0ead43d2 --- /dev/null +++ b/masm/yarr/YarrCanonicalizeUCS2.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrCanonicalizeUCS2_H +#define YarrCanonicalizeUCS2_H + +#include +#include + +namespace JSC { namespace Yarr { + +// This set of data (autogenerated using YarrCanonicalizeUCS2.js into YarrCanonicalizeUCS2.cpp) +// provides information for each UCS2 code point as to the set of code points that it should +// match under the ES5.1 case insensitive RegExp matching rules, specified in 15.10.2.8. +enum UCS2CanonicalizationType { + CanonicalizeUnique, // No canonically equal values, e.g. 0x0. + CanonicalizeSet, // Value indicates a set in characterSetInfo. + CanonicalizeRangeLo, // Value is positive delta to pair, E.g. 0x41 has value 0x20, -> 0x61. + CanonicalizeRangeHi, // Value is positive delta to pair, E.g. 0x61 has value 0x20, -> 0x41. + CanonicalizeAlternatingAligned, // Aligned consequtive pair, e.g. 0x1f4,0x1f5. + CanonicalizeAlternatingUnaligned, // Unaligned consequtive pair, e.g. 0x241,0x242. +}; +struct UCS2CanonicalizationRange { uint16_t begin, end, value, type; }; +extern const size_t UCS2_CANONICALIZATION_RANGES; +extern uint16_t* characterSetInfo[]; +extern UCS2CanonicalizationRange rangeInfo[]; + +// This table is similar to the full rangeInfo table, however this maps from UCS2 codepoints to +// the set of Latin1 codepoints that could match. +enum LatinCanonicalizationType { + CanonicalizeLatinSelf, // This character is in the Latin1 range, but has no canonical equivalent in the range. + CanonicalizeLatinMask0x20, // One of a pair of characters, under the mask 0x20. + CanonicalizeLatinOther, // This character is not in the Latin1 range, but canonicalizes to another that is. + CanonicalizeLatinInvalid, // Cannot match against Latin1 input. +}; +struct LatinCanonicalizationRange { uint16_t begin, end, value, type; }; +extern const size_t LATIN_CANONICALIZATION_RANGES; +extern LatinCanonicalizationRange latinRangeInfo[]; + +// This searches in log2 time over ~364 entries, so should typically result in 8 compares. +inline UCS2CanonicalizationRange* rangeInfoFor(UChar ch) +{ + UCS2CanonicalizationRange* info = rangeInfo; + size_t entries = UCS2_CANONICALIZATION_RANGES; + + while (true) { + size_t candidate = entries >> 1; + UCS2CanonicalizationRange* candidateInfo = info + candidate; + if (ch < candidateInfo->begin) + entries = candidate; + else if (ch <= candidateInfo->end) + return candidateInfo; + else { + info = candidateInfo + 1; + entries -= (candidate + 1); + } + } +} + +// Should only be called for characters that have one canonically matching value. +inline UChar getCanonicalPair(UCS2CanonicalizationRange* info, UChar ch) +{ + ASSERT(ch >= info->begin && ch <= info->end); + switch (info->type) { + case CanonicalizeRangeLo: + return ch + info->value; + case CanonicalizeRangeHi: + return ch - info->value; + case CanonicalizeAlternatingAligned: + return ch ^ 1; + case CanonicalizeAlternatingUnaligned: + return ((ch - 1) ^ 1) + 1; + default: + ASSERT_NOT_REACHED(); + } + ASSERT_NOT_REACHED(); + return 0; +} + +// Returns true if no other UCS2 codepoint can match this value. +inline bool isCanonicallyUnique(UChar ch) +{ + return rangeInfoFor(ch)->type == CanonicalizeUnique; +} + +// Returns true if values are equal, under the canonicalization rules. +inline bool areCanonicallyEquivalent(UChar a, UChar b) +{ + UCS2CanonicalizationRange* info = rangeInfoFor(a); + switch (info->type) { + case CanonicalizeUnique: + return a == b; + case CanonicalizeSet: { + for (uint16_t* set = characterSetInfo[info->value]; (a = *set); ++set) { + if (a == b) + return true; + } + return false; + } + case CanonicalizeRangeLo: + return (a == b) || (a + info->value == b); + case CanonicalizeRangeHi: + return (a == b) || (a - info->value == b); + case CanonicalizeAlternatingAligned: + return (a | 1) == (b | 1); + case CanonicalizeAlternatingUnaligned: + return ((a - 1) | 1) == ((b - 1) | 1); + } + + ASSERT_NOT_REACHED(); + return false; +} + +} } // JSC::Yarr + +#endif diff --git a/masm/yarr/YarrCanonicalizeUCS2.js b/masm/yarr/YarrCanonicalizeUCS2.js new file mode 100644 index 0000000000..00361dd46e --- /dev/null +++ b/masm/yarr/YarrCanonicalizeUCS2.js @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// See ES 5.1, 15.10.2.8 +function canonicalize(ch) +{ + var u = String.fromCharCode(ch).toUpperCase(); + if (u.length > 1) + return ch; + var cu = u.charCodeAt(0); + if (ch >= 128 && cu < 128) + return ch; + return cu; +} + +var MAX_UCS2 = 0xFFFF; +var MAX_LATIN = 0xFF; + +var groupedCanonically = []; +// Pass 1: populate groupedCanonically - this is mapping from canonicalized +// values back to the set of character code that canonicalize to them. +for (var i = 0; i <= MAX_UCS2; ++i) { + var ch = canonicalize(i); + if (!groupedCanonically[ch]) + groupedCanonically[ch] = []; + groupedCanonically[ch].push(i); +} + +var typeInfo = []; +var latinTypeInfo = []; +var characterSetInfo = []; +// Pass 2: populate typeInfo & characterSetInfo. For every character calculate +// a typeInfo value, described by the types above, and a value payload. +for (cu in groupedCanonically) { + // The set of characters that canonicalize to cu + var characters = groupedCanonically[cu]; + + // If there is only one, it is unique. + if (characters.length == 1) { + typeInfo[characters[0]] = "CanonicalizeUnique:0"; + latinTypeInfo[characters[0]] = characters[0] <= MAX_LATIN ? "CanonicalizeLatinSelf:0" : "CanonicalizeLatinInvalid:0"; + continue; + } + + // Sort the array. + characters.sort(function(x,y){return x-y;}); + + // If there are more than two characters, create an entry in characterSetInfo. + if (characters.length > 2) { + for (i in characters) + typeInfo[characters[i]] = "CanonicalizeSet:" + characterSetInfo.length; + characterSetInfo.push(characters); + + if (characters[1] <= MAX_LATIN) + throw new Error("sets with more than one latin character not supported!"); + if (characters[0] <= MAX_LATIN) { + for (i in characters) + latinTypeInfo[characters[i]] = "CanonicalizeLatinOther:" + characters[0]; + latinTypeInfo[characters[0]] = "CanonicalizeLatinSelf:0"; + } else { + for (i in characters) + latinTypeInfo[characters[i]] = "CanonicalizeLatinInvalid:0"; + } + + continue; + } + + // We have a pair, mark alternating ranges, otherwise track whether this is the low or high partner. + var lo = characters[0]; + var hi = characters[1]; + var delta = hi - lo; + if (delta == 1) { + var type = lo & 1 ? "CanonicalizeAlternatingUnaligned:0" : "CanonicalizeAlternatingAligned:0"; + typeInfo[lo] = type; + typeInfo[hi] = type; + } else { + typeInfo[lo] = "CanonicalizeRangeLo:" + delta; + typeInfo[hi] = "CanonicalizeRangeHi:" + delta; + } + + if (lo > MAX_LATIN) { + latinTypeInfo[lo] = "CanonicalizeLatinInvalid:0"; + latinTypeInfo[hi] = "CanonicalizeLatinInvalid:0"; + } else if (hi > MAX_LATIN) { + latinTypeInfo[lo] = "CanonicalizeLatinSelf:0"; + latinTypeInfo[hi] = "CanonicalizeLatinOther:" + lo; + } else { + if (delta != 0x20 || lo & 0x20) + throw new Error("pairs of latin characters that don't mask with 0x20 not supported!"); + latinTypeInfo[lo] = "CanonicalizeLatinMask0x20:0"; + latinTypeInfo[hi] = "CanonicalizeLatinMask0x20:0"; + } +} + +var rangeInfo = []; +// Pass 3: coallesce types into ranges. +for (var end = 0; end <= MAX_UCS2; ++end) { + var begin = end; + var type = typeInfo[end]; + while (end < MAX_UCS2 && typeInfo[end + 1] == type) + ++end; + rangeInfo.push({begin:begin, end:end, type:type}); +} + +var latinRangeInfo = []; +// Pass 4: coallesce latin-1 types into ranges. +for (var end = 0; end <= MAX_UCS2; ++end) { + var begin = end; + var type = latinTypeInfo[end]; + while (end < MAX_UCS2 && latinTypeInfo[end + 1] == type) + ++end; + latinRangeInfo.push({begin:begin, end:end, type:type}); +} + + +// Helper function to convert a number to a fixed width hex representation of a C uint16_t. +function hex(x) +{ + var s = Number(x).toString(16); + while (s.length < 4) + s = 0 + s; + return "0x" + s + "u"; +} + +var copyright = ( + "/*" + "\n" + + " * Copyright (C) 2012 Apple Inc. All rights reserved." + "\n" + + " *" + "\n" + + " * Redistribution and use in source and binary forms, with or without" + "\n" + + " * modification, are permitted provided that the following conditions" + "\n" + + " * are met:" + "\n" + + " * 1. Redistributions of source code must retain the above copyright" + "\n" + + " * notice, this list of conditions and the following disclaimer." + "\n" + + " * 2. Redistributions in binary form must reproduce the above copyright" + "\n" + + " * notice, this list of conditions and the following disclaimer in the" + "\n" + + " * documentation and/or other materials provided with the distribution." + "\n" + + " *" + "\n" + + " * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY" + "\n" + + " * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE" + "\n" + + " * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR" + "\n" + + " * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR" + "\n" + + " * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL," + "\n" + + " * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO," + "\n" + + " * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR" + "\n" + + " * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY" + "\n" + + " * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" + "\n" + + " * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" + "\n" + + " * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. " + "\n" + + " */"); + +print(copyright); +print(); +print("// DO NOT EDIT! - this file autogenerated by YarrCanonicalizeUCS2.js"); +print(); +print('#include "config.h"'); +print('#include "YarrCanonicalizeUCS2.h"'); +print(); +print("namespace JSC { namespace Yarr {"); +print(); +print("#include "); +print(); + +for (i in characterSetInfo) { + var characters = "" + var set = characterSetInfo[i]; + for (var j in set) + characters += hex(set[j]) + ", "; + print("uint16_t ucs2CharacterSet" + i + "[] = { " + characters + "0 };"); +} +print(); +print("static const size_t UCS2_CANONICALIZATION_SETS = " + characterSetInfo.length + ";"); +print("uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = {"); +for (i in characterSetInfo) +print(" ucs2CharacterSet" + i + ","); +print("};"); +print(); +print("const size_t UCS2_CANONICALIZATION_RANGES = " + rangeInfo.length + ";"); +print("UCS2CanonicalizationRange rangeInfo[UCS2_CANONICALIZATION_RANGES] = {"); +for (i in rangeInfo) { + var info = rangeInfo[i]; + var typeAndValue = info.type.split(':'); + print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },"); +} +print("};"); +print(); +print("const size_t LATIN_CANONICALIZATION_RANGES = " + latinRangeInfo.length + ";"); +print("LatinCanonicalizationRange latinRangeInfo[LATIN_CANONICALIZATION_RANGES] = {"); +for (i in latinRangeInfo) { + var info = latinRangeInfo[i]; + var typeAndValue = info.type.split(':'); + print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },"); +} +print("};"); +print(); +print("} } // JSC::Yarr"); +print(); + diff --git a/masm/yarr/YarrInterpreter.cpp b/masm/yarr/YarrInterpreter.cpp new file mode 100644 index 0000000000..31603f6d34 --- /dev/null +++ b/masm/yarr/YarrInterpreter.cpp @@ -0,0 +1,1964 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "YarrInterpreter.h" + +#include "Yarr.h" +#include "YarrCanonicalizeUCS2.h" +#include +#include +#include +#include + +#ifndef NDEBUG +#include +#endif + +using namespace WTF; + +namespace JSC { namespace Yarr { + +template +class Interpreter { +public: + struct ParenthesesDisjunctionContext; + + struct BackTrackInfoPatternCharacter { + uintptr_t matchAmount; + }; + struct BackTrackInfoCharacterClass { + uintptr_t matchAmount; + }; + struct BackTrackInfoBackReference { + uintptr_t begin; // Not really needed for greedy quantifiers. + uintptr_t matchAmount; // Not really needed for fixed quantifiers. + }; + struct BackTrackInfoAlternative { + uintptr_t offset; + }; + struct BackTrackInfoParentheticalAssertion { + uintptr_t begin; + }; + struct BackTrackInfoParenthesesOnce { + uintptr_t begin; + }; + struct BackTrackInfoParenthesesTerminal { + uintptr_t begin; + }; + struct BackTrackInfoParentheses { + uintptr_t matchAmount; + ParenthesesDisjunctionContext* lastContext; + }; + + static inline void appendParenthesesDisjunctionContext(BackTrackInfoParentheses* backTrack, ParenthesesDisjunctionContext* context) + { + context->next = backTrack->lastContext; + backTrack->lastContext = context; + ++backTrack->matchAmount; + } + + static inline void popParenthesesDisjunctionContext(BackTrackInfoParentheses* backTrack) + { + ASSERT(backTrack->matchAmount); + ASSERT(backTrack->lastContext); + backTrack->lastContext = backTrack->lastContext->next; + --backTrack->matchAmount; + } + + struct DisjunctionContext + { + DisjunctionContext() + : term(0) + { + } + + void* operator new(size_t, void* where) + { + return where; + } + + int term; + unsigned matchBegin; + unsigned matchEnd; + uintptr_t frame[1]; + }; + + DisjunctionContext* allocDisjunctionContext(ByteDisjunction* disjunction) + { + size_t size = sizeof(DisjunctionContext) - sizeof(uintptr_t) + disjunction->m_frameSize * sizeof(uintptr_t); + allocatorPool = allocatorPool->ensureCapacity(size); + if (!allocatorPool) + CRASH(); + return new (allocatorPool->alloc(size)) DisjunctionContext(); + } + + void freeDisjunctionContext(DisjunctionContext* context) + { + allocatorPool = allocatorPool->dealloc(context); + } + + struct ParenthesesDisjunctionContext + { + ParenthesesDisjunctionContext(unsigned* output, ByteTerm& term) + : next(0) + { + unsigned firstSubpatternId = term.atom.subpatternId; + unsigned numNestedSubpatterns = term.atom.parenthesesDisjunction->m_numSubpatterns; + + for (unsigned i = 0; i < (numNestedSubpatterns << 1); ++i) { + subpatternBackup[i] = output[(firstSubpatternId << 1) + i]; + output[(firstSubpatternId << 1) + i] = offsetNoMatch; + } + + new (getDisjunctionContext(term)) DisjunctionContext(); + } + + void* operator new(size_t, void* where) + { + return where; + } + + void restoreOutput(unsigned* output, unsigned firstSubpatternId, unsigned numNestedSubpatterns) + { + for (unsigned i = 0; i < (numNestedSubpatterns << 1); ++i) + output[(firstSubpatternId << 1) + i] = subpatternBackup[i]; + } + + DisjunctionContext* getDisjunctionContext(ByteTerm& term) + { + return reinterpret_cast(&(subpatternBackup[term.atom.parenthesesDisjunction->m_numSubpatterns << 1])); + } + + ParenthesesDisjunctionContext* next; + unsigned subpatternBackup[1]; + }; + + ParenthesesDisjunctionContext* allocParenthesesDisjunctionContext(ByteDisjunction* disjunction, unsigned* output, ByteTerm& term) + { + size_t size = sizeof(ParenthesesDisjunctionContext) - sizeof(unsigned) + (term.atom.parenthesesDisjunction->m_numSubpatterns << 1) * sizeof(unsigned) + sizeof(DisjunctionContext) - sizeof(uintptr_t) + disjunction->m_frameSize * sizeof(uintptr_t); + allocatorPool = allocatorPool->ensureCapacity(size); + if (!allocatorPool) + CRASH(); + return new (allocatorPool->alloc(size)) ParenthesesDisjunctionContext(output, term); + } + + void freeParenthesesDisjunctionContext(ParenthesesDisjunctionContext* context) + { + allocatorPool = allocatorPool->dealloc(context); + } + + class InputStream { + public: + InputStream(const CharType* input, unsigned start, unsigned length) + : input(input) + , pos(start) + , length(length) + { + } + + void next() + { + ++pos; + } + + void rewind(unsigned amount) + { + ASSERT(pos >= amount); + pos -= amount; + } + + int read() + { + ASSERT(pos < length); + if (pos < length) + return input[pos]; + return -1; + } + + int readPair() + { + ASSERT(pos + 1 < length); + return input[pos] | input[pos + 1] << 16; + } + + int readChecked(unsigned negativePositionOffest) + { + if (pos < negativePositionOffest) + CRASH(); + unsigned p = pos - negativePositionOffest; + ASSERT(p < length); + return input[p]; + } + + int reread(unsigned from) + { + ASSERT(from < length); + return input[from]; + } + + int prev() + { + ASSERT(!(pos > length)); + if (pos && length) + return input[pos - 1]; + return -1; + } + + unsigned getPos() + { + return pos; + } + + void setPos(unsigned p) + { + pos = p; + } + + bool atStart() + { + return pos == 0; + } + + bool atEnd() + { + return pos == length; + } + + unsigned end() + { + return length; + } + + bool checkInput(unsigned count) + { + if (((pos + count) <= length) && ((pos + count) >= pos)) { + pos += count; + return true; + } + return false; + } + + void uncheckInput(unsigned count) + { + if (pos < count) + CRASH(); + pos -= count; + } + + bool atStart(unsigned negativePositionOffest) + { + return pos == negativePositionOffest; + } + + bool atEnd(unsigned negativePositionOffest) + { + if (pos < negativePositionOffest) + CRASH(); + return (pos - negativePositionOffest) == length; + } + + bool isAvailableInput(unsigned offset) + { + return (((pos + offset) <= length) && ((pos + offset) >= pos)); + } + + private: + const CharType* input; + unsigned pos; + unsigned length; + }; + + bool testCharacterClass(CharacterClass* characterClass, int ch) + { + if (ch & 0xFF80) { + for (unsigned i = 0; i < characterClass->m_matchesUnicode.size(); ++i) + if (ch == characterClass->m_matchesUnicode[i]) + return true; + for (unsigned i = 0; i < characterClass->m_rangesUnicode.size(); ++i) + if ((ch >= characterClass->m_rangesUnicode[i].begin) && (ch <= characterClass->m_rangesUnicode[i].end)) + return true; + } else { + for (unsigned i = 0; i < characterClass->m_matches.size(); ++i) + if (ch == characterClass->m_matches[i]) + return true; + for (unsigned i = 0; i < characterClass->m_ranges.size(); ++i) + if ((ch >= characterClass->m_ranges[i].begin) && (ch <= characterClass->m_ranges[i].end)) + return true; + } + + return false; + } + + bool checkCharacter(int testChar, unsigned negativeInputOffset) + { + return testChar == input.readChecked(negativeInputOffset); + } + + bool checkCasedCharacter(int loChar, int hiChar, unsigned negativeInputOffset) + { + int ch = input.readChecked(negativeInputOffset); + return (loChar == ch) || (hiChar == ch); + } + + bool checkCharacterClass(CharacterClass* characterClass, bool invert, unsigned negativeInputOffset) + { + bool match = testCharacterClass(characterClass, input.readChecked(negativeInputOffset)); + return invert ? !match : match; + } + + bool tryConsumeBackReference(int matchBegin, int matchEnd, unsigned negativeInputOffset) + { + unsigned matchSize = (unsigned)(matchEnd - matchBegin); + + if (!input.checkInput(matchSize)) + return false; + + if (pattern->m_ignoreCase) { + for (unsigned i = 0; i < matchSize; ++i) { + int oldCh = input.reread(matchBegin + i); + int ch = input.readChecked(negativeInputOffset + matchSize - i); + + if (oldCh == ch) + continue; + + // The definition for canonicalize (see ES 5.1, 15.10.2.8) means that + // unicode values are never allowed to match against ascii ones. + if (isASCII(oldCh) || isASCII(ch)) { + if (toASCIIUpper(oldCh) == toASCIIUpper(ch)) + continue; + } else if (areCanonicallyEquivalent(oldCh, ch)) + continue; + + input.uncheckInput(matchSize); + return false; + } + } else { + for (unsigned i = 0; i < matchSize; ++i) { + if (!checkCharacter(input.reread(matchBegin + i), negativeInputOffset + matchSize - i)) { + input.uncheckInput(matchSize); + return false; + } + } + } + + return true; + } + + bool matchAssertionBOL(ByteTerm& term) + { + return (input.atStart(term.inputPosition)) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition + 1))); + } + + bool matchAssertionEOL(ByteTerm& term) + { + if (term.inputPosition) + return (input.atEnd(term.inputPosition)) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition))); + + return (input.atEnd()) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.read())); + } + + bool matchAssertionWordBoundary(ByteTerm& term) + { + bool prevIsWordchar = !input.atStart(term.inputPosition) && testCharacterClass(pattern->wordcharCharacterClass, input.readChecked(term.inputPosition + 1)); + bool readIsWordchar; + if (term.inputPosition) + readIsWordchar = !input.atEnd(term.inputPosition) && testCharacterClass(pattern->wordcharCharacterClass, input.readChecked(term.inputPosition)); + else + readIsWordchar = !input.atEnd() && testCharacterClass(pattern->wordcharCharacterClass, input.read()); + + bool wordBoundary = prevIsWordchar != readIsWordchar; + return term.invert() ? !wordBoundary : wordBoundary; + } + + bool backtrackPatternCharacter(ByteTerm& term, DisjunctionContext* context) + { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierFixedCount: + break; + + case QuantifierGreedy: + if (backTrack->matchAmount) { + --backTrack->matchAmount; + input.uncheckInput(1); + return true; + } + break; + + case QuantifierNonGreedy: + if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { + ++backTrack->matchAmount; + if (checkCharacter(term.atom.patternCharacter, term.inputPosition + 1)) + return true; + } + input.uncheckInput(backTrack->matchAmount); + break; + } + + return false; + } + + bool backtrackPatternCasedCharacter(ByteTerm& term, DisjunctionContext* context) + { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierFixedCount: + break; + + case QuantifierGreedy: + if (backTrack->matchAmount) { + --backTrack->matchAmount; + input.uncheckInput(1); + return true; + } + break; + + case QuantifierNonGreedy: + if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { + ++backTrack->matchAmount; + if (checkCasedCharacter(term.atom.casedCharacter.lo, term.atom.casedCharacter.hi, term.inputPosition + 1)) + return true; + } + input.uncheckInput(backTrack->matchAmount); + break; + } + + return false; + } + + bool matchCharacterClass(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeCharacterClass); + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierFixedCount: { + for (unsigned matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) { + if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition - matchAmount)) + return false; + } + return true; + } + + case QuantifierGreedy: { + unsigned matchAmount = 0; + while ((matchAmount < term.atom.quantityCount) && input.checkInput(1)) { + if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) { + input.uncheckInput(1); + break; + } + ++matchAmount; + } + backTrack->matchAmount = matchAmount; + + return true; + } + + case QuantifierNonGreedy: + backTrack->matchAmount = 0; + return true; + } + + ASSERT_NOT_REACHED(); + return false; + } + + bool backtrackCharacterClass(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeCharacterClass); + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierFixedCount: + break; + + case QuantifierGreedy: + if (backTrack->matchAmount) { + --backTrack->matchAmount; + input.uncheckInput(1); + return true; + } + break; + + case QuantifierNonGreedy: + if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { + ++backTrack->matchAmount; + if (checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) + return true; + } + input.uncheckInput(backTrack->matchAmount); + break; + } + + return false; + } + + bool matchBackReference(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeBackReference); + BackTrackInfoBackReference* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + unsigned matchBegin = output[(term.atom.subpatternId << 1)]; + unsigned matchEnd = output[(term.atom.subpatternId << 1) + 1]; + + // If the end position of the referenced match hasn't set yet then the backreference in the same parentheses where it references to that. + // In this case the result of match is empty string like when it references to a parentheses with zero-width match. + // Eg.: /(a\1)/ + if (matchEnd == offsetNoMatch) + return true; + + if (matchBegin == offsetNoMatch) + return true; + + ASSERT(matchBegin <= matchEnd); + + if (matchBegin == matchEnd) + return true; + + switch (term.atom.quantityType) { + case QuantifierFixedCount: { + backTrack->begin = input.getPos(); + for (unsigned matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) { + if (!tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) { + input.setPos(backTrack->begin); + return false; + } + } + return true; + } + + case QuantifierGreedy: { + unsigned matchAmount = 0; + while ((matchAmount < term.atom.quantityCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) + ++matchAmount; + backTrack->matchAmount = matchAmount; + return true; + } + + case QuantifierNonGreedy: + backTrack->begin = input.getPos(); + backTrack->matchAmount = 0; + return true; + } + + ASSERT_NOT_REACHED(); + return false; + } + + bool backtrackBackReference(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeBackReference); + BackTrackInfoBackReference* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + unsigned matchBegin = output[(term.atom.subpatternId << 1)]; + unsigned matchEnd = output[(term.atom.subpatternId << 1) + 1]; + + if (matchBegin == offsetNoMatch) + return false; + + ASSERT(matchBegin <= matchEnd); + + if (matchBegin == matchEnd) + return false; + + switch (term.atom.quantityType) { + case QuantifierFixedCount: + // for quantityCount == 1, could rewind. + input.setPos(backTrack->begin); + break; + + case QuantifierGreedy: + if (backTrack->matchAmount) { + --backTrack->matchAmount; + input.rewind(matchEnd - matchBegin); + return true; + } + break; + + case QuantifierNonGreedy: + if ((backTrack->matchAmount < term.atom.quantityCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) { + ++backTrack->matchAmount; + return true; + } + input.setPos(backTrack->begin); + break; + } + + return false; + } + + void recordParenthesesMatch(ByteTerm& term, ParenthesesDisjunctionContext* context) + { + if (term.capture()) { + unsigned subpatternId = term.atom.subpatternId; + output[(subpatternId << 1)] = context->getDisjunctionContext(term)->matchBegin + term.inputPosition; + output[(subpatternId << 1) + 1] = context->getDisjunctionContext(term)->matchEnd + term.inputPosition; + } + } + void resetMatches(ByteTerm& term, ParenthesesDisjunctionContext* context) + { + unsigned firstSubpatternId = term.atom.subpatternId; + unsigned count = term.atom.parenthesesDisjunction->m_numSubpatterns; + context->restoreOutput(output, firstSubpatternId, count); + } + JSRegExpResult parenthesesDoBacktrack(ByteTerm& term, BackTrackInfoParentheses* backTrack) + { + while (backTrack->matchAmount) { + ParenthesesDisjunctionContext* context = backTrack->lastContext; + + JSRegExpResult result = matchDisjunction(term.atom.parenthesesDisjunction, context->getDisjunctionContext(term), true); + if (result == JSRegExpMatch) + return JSRegExpMatch; + + resetMatches(term, context); + popParenthesesDisjunctionContext(backTrack); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + } + + return JSRegExpNoMatch; + } + + bool matchParenthesesOnceBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierGreedy: { + // set this speculatively; if we get to the parens end this will be true. + backTrack->begin = input.getPos(); + break; + } + case QuantifierNonGreedy: { + backTrack->begin = notFound; + context->term += term.atom.parenthesesWidth; + return true; + } + case QuantifierFixedCount: + break; + } + + if (term.capture()) { + unsigned subpatternId = term.atom.subpatternId; + output[(subpatternId << 1)] = input.getPos() - term.inputPosition; + } + + return true; + } + + bool matchParenthesesOnceEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceEnd); + ASSERT(term.atom.quantityCount == 1); + + if (term.capture()) { + unsigned subpatternId = term.atom.subpatternId; + output[(subpatternId << 1) + 1] = input.getPos() + term.inputPosition; + } + + if (term.atom.quantityType == QuantifierFixedCount) + return true; + + BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); + return backTrack->begin != input.getPos(); + } + + bool backtrackParenthesesOnceBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + if (term.capture()) { + unsigned subpatternId = term.atom.subpatternId; + output[(subpatternId << 1)] = offsetNoMatch; + output[(subpatternId << 1) + 1] = offsetNoMatch; + } + + switch (term.atom.quantityType) { + case QuantifierGreedy: + // if we backtrack to this point, there is another chance - try matching nothing. + ASSERT(backTrack->begin != notFound); + backTrack->begin = notFound; + context->term += term.atom.parenthesesWidth; + return true; + case QuantifierNonGreedy: + ASSERT(backTrack->begin != notFound); + case QuantifierFixedCount: + break; + } + + return false; + } + + bool backtrackParenthesesOnceEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceEnd); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierGreedy: + if (backTrack->begin == notFound) { + context->term -= term.atom.parenthesesWidth; + return false; + } + case QuantifierNonGreedy: + if (backTrack->begin == notFound) { + backTrack->begin = input.getPos(); + if (term.capture()) { + // Technically this access to inputPosition should be accessing the begin term's + // inputPosition, but for repeats other than fixed these values should be + // the same anyway! (We don't pre-check for greedy or non-greedy matches.) + ASSERT((&term - term.atom.parenthesesWidth)->type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + ASSERT((&term - term.atom.parenthesesWidth)->inputPosition == term.inputPosition); + unsigned subpatternId = term.atom.subpatternId; + output[subpatternId << 1] = input.getPos() + term.inputPosition; + } + context->term -= term.atom.parenthesesWidth; + return true; + } + case QuantifierFixedCount: + break; + } + + return false; + } + + bool matchParenthesesTerminalBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); + ASSERT(term.atom.quantityType == QuantifierGreedy); + ASSERT(term.atom.quantityCount == quantifyInfinite); + ASSERT(!term.capture()); + + BackTrackInfoParenthesesTerminal* backTrack = reinterpret_cast(context->frame + term.frameLocation); + backTrack->begin = input.getPos(); + return true; + } + + bool matchParenthesesTerminalEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalEnd); + + BackTrackInfoParenthesesTerminal* backTrack = reinterpret_cast(context->frame + term.frameLocation); + // Empty match is a failed match. + if (backTrack->begin == input.getPos()) + return false; + + // Successful match! Okay, what's next? - loop around and try to match moar! + context->term -= (term.atom.parenthesesWidth + 1); + return true; + } + + bool backtrackParenthesesTerminalBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); + ASSERT(term.atom.quantityType == QuantifierGreedy); + ASSERT(term.atom.quantityCount == quantifyInfinite); + ASSERT(!term.capture()); + + // If we backtrack to this point, we have failed to match this iteration of the parens. + // Since this is greedy / zero minimum a failed is also accepted as a match! + context->term += term.atom.parenthesesWidth; + return true; + } + + bool backtrackParenthesesTerminalEnd(ByteTerm&, DisjunctionContext*) + { + // 'Terminal' parentheses are at the end of the regex, and as such a match past end + // should always be returned as a successful match - we should never backtrack to here. + ASSERT_NOT_REACHED(); + return false; + } + + bool matchParentheticalAssertionBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParentheticalAssertionBegin); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + backTrack->begin = input.getPos(); + return true; + } + + bool matchParentheticalAssertionEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParentheticalAssertionEnd); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + input.setPos(backTrack->begin); + + // We've reached the end of the parens; if they are inverted, this is failure. + if (term.invert()) { + context->term -= term.atom.parenthesesWidth; + return false; + } + + return true; + } + + bool backtrackParentheticalAssertionBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParentheticalAssertionBegin); + ASSERT(term.atom.quantityCount == 1); + + // We've failed to match parens; if they are inverted, this is win! + if (term.invert()) { + context->term += term.atom.parenthesesWidth; + return true; + } + + return false; + } + + bool backtrackParentheticalAssertionEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParentheticalAssertionEnd); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + input.setPos(backTrack->begin); + + context->term -= term.atom.parenthesesWidth; + return false; + } + + JSRegExpResult matchParentheses(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpattern); + + BackTrackInfoParentheses* backTrack = reinterpret_cast(context->frame + term.frameLocation); + ByteDisjunction* disjunctionBody = term.atom.parenthesesDisjunction; + + backTrack->matchAmount = 0; + backTrack->lastContext = 0; + + switch (term.atom.quantityType) { + case QuantifierFixedCount: { + // While we haven't yet reached our fixed limit, + while (backTrack->matchAmount < term.atom.quantityCount) { + // Try to do a match, and it it succeeds, add it to the list. + ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + JSRegExpResult result = matchDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + if (result == JSRegExpMatch) + appendParenthesesDisjunctionContext(backTrack, context); + else { + // The match failed; try to find an alternate point to carry on from. + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack); + if (backtrackResult != JSRegExpMatch) + return backtrackResult; + } + } + + ASSERT(backTrack->matchAmount == term.atom.quantityCount); + ParenthesesDisjunctionContext* context = backTrack->lastContext; + recordParenthesesMatch(term, context); + return JSRegExpMatch; + } + + case QuantifierGreedy: { + while (backTrack->matchAmount < term.atom.quantityCount) { + ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + if (result == JSRegExpMatch) + appendParenthesesDisjunctionContext(backTrack, context); + else { + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + + break; + } + } + + if (backTrack->matchAmount) { + ParenthesesDisjunctionContext* context = backTrack->lastContext; + recordParenthesesMatch(term, context); + } + return JSRegExpMatch; + } + + case QuantifierNonGreedy: + return JSRegExpMatch; + } + + ASSERT_NOT_REACHED(); + return JSRegExpErrorNoMatch; + } + + // Rules for backtracking differ depending on whether this is greedy or non-greedy. + // + // Greedy matches never should try just adding more - you should already have done + // the 'more' cases. Always backtrack, at least a leetle bit. However cases where + // you backtrack an item off the list needs checking, since we'll never have matched + // the one less case. Tracking forwards, still add as much as possible. + // + // Non-greedy, we've already done the one less case, so don't match on popping. + // We haven't done the one more case, so always try to add that. + // + JSRegExpResult backtrackParentheses(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpattern); + + BackTrackInfoParentheses* backTrack = reinterpret_cast(context->frame + term.frameLocation); + ByteDisjunction* disjunctionBody = term.atom.parenthesesDisjunction; + + switch (term.atom.quantityType) { + case QuantifierFixedCount: { + ASSERT(backTrack->matchAmount == term.atom.quantityCount); + + ParenthesesDisjunctionContext* context = 0; + JSRegExpResult result = parenthesesDoBacktrack(term, backTrack); + + if (result != JSRegExpMatch) + return result; + + // While we haven't yet reached our fixed limit, + while (backTrack->matchAmount < term.atom.quantityCount) { + // Try to do a match, and it it succeeds, add it to the list. + context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + result = matchDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + + if (result == JSRegExpMatch) + appendParenthesesDisjunctionContext(backTrack, context); + else { + // The match failed; try to find an alternate point to carry on from. + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack); + if (backtrackResult != JSRegExpMatch) + return backtrackResult; + } + } + + ASSERT(backTrack->matchAmount == term.atom.quantityCount); + context = backTrack->lastContext; + recordParenthesesMatch(term, context); + return JSRegExpMatch; + } + + case QuantifierGreedy: { + if (!backTrack->matchAmount) + return JSRegExpNoMatch; + + ParenthesesDisjunctionContext* context = backTrack->lastContext; + JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term), true); + if (result == JSRegExpMatch) { + while (backTrack->matchAmount < term.atom.quantityCount) { + ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + JSRegExpResult parenthesesResult = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + if (parenthesesResult == JSRegExpMatch) + appendParenthesesDisjunctionContext(backTrack, context); + else { + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (parenthesesResult != JSRegExpNoMatch) + return parenthesesResult; + + break; + } + } + } else { + resetMatches(term, context); + popParenthesesDisjunctionContext(backTrack); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + } + + if (backTrack->matchAmount) { + ParenthesesDisjunctionContext* context = backTrack->lastContext; + recordParenthesesMatch(term, context); + } + return JSRegExpMatch; + } + + case QuantifierNonGreedy: { + // If we've not reached the limit, try to add one more match. + if (backTrack->matchAmount < term.atom.quantityCount) { + ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + if (result == JSRegExpMatch) { + appendParenthesesDisjunctionContext(backTrack, context); + recordParenthesesMatch(term, context); + return JSRegExpMatch; + } + + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + } + + // Nope - okay backtrack looking for an alternative. + while (backTrack->matchAmount) { + ParenthesesDisjunctionContext* context = backTrack->lastContext; + JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term), true); + if (result == JSRegExpMatch) { + // successful backtrack! we're back in the game! + if (backTrack->matchAmount) { + context = backTrack->lastContext; + recordParenthesesMatch(term, context); + } + return JSRegExpMatch; + } + + // pop a match off the stack + resetMatches(term, context); + popParenthesesDisjunctionContext(backTrack); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + } + + return JSRegExpNoMatch; + } + } + + ASSERT_NOT_REACHED(); + return JSRegExpErrorNoMatch; + } + + bool matchDotStarEnclosure(ByteTerm& term, DisjunctionContext* context) + { + UNUSED_PARAM(term); + unsigned matchBegin = context->matchBegin; + + if (matchBegin) { + for (matchBegin--; true; matchBegin--) { + if (testCharacterClass(pattern->newlineCharacterClass, input.reread(matchBegin))) { + ++matchBegin; + break; + } + + if (!matchBegin) + break; + } + } + + unsigned matchEnd = input.getPos(); + + for (; (matchEnd != input.end()) + && (!testCharacterClass(pattern->newlineCharacterClass, input.reread(matchEnd))); matchEnd++) { } + + if (((matchBegin && term.anchors.m_bol) + || ((matchEnd != input.end()) && term.anchors.m_eol)) + && !pattern->m_multiline) + return false; + + context->matchBegin = matchBegin; + context->matchEnd = matchEnd; + return true; + } + +#define MATCH_NEXT() { ++context->term; goto matchAgain; } +#define BACKTRACK() { --context->term; goto backtrack; } +#define currentTerm() (disjunction->terms[context->term]) + JSRegExpResult matchDisjunction(ByteDisjunction* disjunction, DisjunctionContext* context, bool btrack = false) + { + if (!--remainingMatchCount) + return JSRegExpErrorHitLimit; + + if (btrack) + BACKTRACK(); + + context->matchBegin = input.getPos(); + context->term = 0; + + matchAgain: + ASSERT(context->term < static_cast(disjunction->terms.size())); + + switch (currentTerm().type) { + case ByteTerm::TypeSubpatternBegin: + MATCH_NEXT(); + case ByteTerm::TypeSubpatternEnd: + context->matchEnd = input.getPos(); + return JSRegExpMatch; + + case ByteTerm::TypeBodyAlternativeBegin: + MATCH_NEXT(); + case ByteTerm::TypeBodyAlternativeDisjunction: + case ByteTerm::TypeBodyAlternativeEnd: + context->matchEnd = input.getPos(); + return JSRegExpMatch; + + case ByteTerm::TypeAlternativeBegin: + MATCH_NEXT(); + case ByteTerm::TypeAlternativeDisjunction: + case ByteTerm::TypeAlternativeEnd: { + int offset = currentTerm().alternative.end; + BackTrackInfoAlternative* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + backTrack->offset = offset; + context->term += offset; + MATCH_NEXT(); + } + + case ByteTerm::TypeAssertionBOL: + if (matchAssertionBOL(currentTerm())) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeAssertionEOL: + if (matchAssertionEOL(currentTerm())) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeAssertionWordBoundary: + if (matchAssertionWordBoundary(currentTerm())) + MATCH_NEXT(); + BACKTRACK(); + + case ByteTerm::TypePatternCharacterOnce: + case ByteTerm::TypePatternCharacterFixed: { + for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityCount; ++matchAmount) { + if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition - matchAmount)) + BACKTRACK(); + } + MATCH_NEXT(); + } + case ByteTerm::TypePatternCharacterGreedy: { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + unsigned matchAmount = 0; + while ((matchAmount < currentTerm().atom.quantityCount) && input.checkInput(1)) { + if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition + 1)) { + input.uncheckInput(1); + break; + } + ++matchAmount; + } + backTrack->matchAmount = matchAmount; + + MATCH_NEXT(); + } + case ByteTerm::TypePatternCharacterNonGreedy: { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + backTrack->matchAmount = 0; + MATCH_NEXT(); + } + + case ByteTerm::TypePatternCasedCharacterOnce: + case ByteTerm::TypePatternCasedCharacterFixed: { + for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityCount; ++matchAmount) { + if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition - matchAmount)) + BACKTRACK(); + } + MATCH_NEXT(); + } + case ByteTerm::TypePatternCasedCharacterGreedy: { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + unsigned matchAmount = 0; + while ((matchAmount < currentTerm().atom.quantityCount) && input.checkInput(1)) { + if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition + 1)) { + input.uncheckInput(1); + break; + } + ++matchAmount; + } + backTrack->matchAmount = matchAmount; + + MATCH_NEXT(); + } + case ByteTerm::TypePatternCasedCharacterNonGreedy: { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + backTrack->matchAmount = 0; + MATCH_NEXT(); + } + + case ByteTerm::TypeCharacterClass: + if (matchCharacterClass(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeBackReference: + if (matchBackReference(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpattern: { + JSRegExpResult result = matchParentheses(currentTerm(), context); + + if (result == JSRegExpMatch) { + MATCH_NEXT(); + } else if (result != JSRegExpNoMatch) + return result; + + BACKTRACK(); + } + case ByteTerm::TypeParenthesesSubpatternOnceBegin: + if (matchParenthesesOnceBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternOnceEnd: + if (matchParenthesesOnceEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternTerminalBegin: + if (matchParenthesesTerminalBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternTerminalEnd: + if (matchParenthesesTerminalEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParentheticalAssertionBegin: + if (matchParentheticalAssertionBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParentheticalAssertionEnd: + if (matchParentheticalAssertionEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + + case ByteTerm::TypeCheckInput: + if (input.checkInput(currentTerm().checkInputCount)) + MATCH_NEXT(); + BACKTRACK(); + + case ByteTerm::TypeUncheckInput: + input.uncheckInput(currentTerm().checkInputCount); + MATCH_NEXT(); + + case ByteTerm::TypeDotStarEnclosure: + if (matchDotStarEnclosure(currentTerm(), context)) + return JSRegExpMatch; + BACKTRACK(); + } + + // We should never fall-through to here. + ASSERT_NOT_REACHED(); + + backtrack: + ASSERT(context->term < static_cast(disjunction->terms.size())); + + switch (currentTerm().type) { + case ByteTerm::TypeSubpatternBegin: + return JSRegExpNoMatch; + case ByteTerm::TypeSubpatternEnd: + ASSERT_NOT_REACHED(); + + case ByteTerm::TypeBodyAlternativeBegin: + case ByteTerm::TypeBodyAlternativeDisjunction: { + int offset = currentTerm().alternative.next; + context->term += offset; + if (offset > 0) + MATCH_NEXT(); + + if (input.atEnd()) + return JSRegExpNoMatch; + + input.next(); + + context->matchBegin = input.getPos(); + + if (currentTerm().alternative.onceThrough) + context->term += currentTerm().alternative.next; + + MATCH_NEXT(); + } + case ByteTerm::TypeBodyAlternativeEnd: + ASSERT_NOT_REACHED(); + + case ByteTerm::TypeAlternativeBegin: + case ByteTerm::TypeAlternativeDisjunction: { + int offset = currentTerm().alternative.next; + context->term += offset; + if (offset > 0) + MATCH_NEXT(); + BACKTRACK(); + } + case ByteTerm::TypeAlternativeEnd: { + // We should never backtrack back into an alternative of the main body of the regex. + BackTrackInfoAlternative* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + unsigned offset = backTrack->offset; + context->term -= offset; + BACKTRACK(); + } + + case ByteTerm::TypeAssertionBOL: + case ByteTerm::TypeAssertionEOL: + case ByteTerm::TypeAssertionWordBoundary: + BACKTRACK(); + + case ByteTerm::TypePatternCharacterOnce: + case ByteTerm::TypePatternCharacterFixed: + case ByteTerm::TypePatternCharacterGreedy: + case ByteTerm::TypePatternCharacterNonGreedy: + if (backtrackPatternCharacter(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypePatternCasedCharacterOnce: + case ByteTerm::TypePatternCasedCharacterFixed: + case ByteTerm::TypePatternCasedCharacterGreedy: + case ByteTerm::TypePatternCasedCharacterNonGreedy: + if (backtrackPatternCasedCharacter(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeCharacterClass: + if (backtrackCharacterClass(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeBackReference: + if (backtrackBackReference(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpattern: { + JSRegExpResult result = backtrackParentheses(currentTerm(), context); + + if (result == JSRegExpMatch) { + MATCH_NEXT(); + } else if (result != JSRegExpNoMatch) + return result; + + BACKTRACK(); + } + case ByteTerm::TypeParenthesesSubpatternOnceBegin: + if (backtrackParenthesesOnceBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternOnceEnd: + if (backtrackParenthesesOnceEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternTerminalBegin: + if (backtrackParenthesesTerminalBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternTerminalEnd: + if (backtrackParenthesesTerminalEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParentheticalAssertionBegin: + if (backtrackParentheticalAssertionBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParentheticalAssertionEnd: + if (backtrackParentheticalAssertionEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + + case ByteTerm::TypeCheckInput: + input.uncheckInput(currentTerm().checkInputCount); + BACKTRACK(); + + case ByteTerm::TypeUncheckInput: + input.checkInput(currentTerm().checkInputCount); + BACKTRACK(); + + case ByteTerm::TypeDotStarEnclosure: + ASSERT_NOT_REACHED(); + } + + ASSERT_NOT_REACHED(); + return JSRegExpErrorNoMatch; + } + + JSRegExpResult matchNonZeroDisjunction(ByteDisjunction* disjunction, DisjunctionContext* context, bool btrack = false) + { + JSRegExpResult result = matchDisjunction(disjunction, context, btrack); + + if (result == JSRegExpMatch) { + while (context->matchBegin == context->matchEnd) { + result = matchDisjunction(disjunction, context, true); + if (result != JSRegExpMatch) + return result; + } + return JSRegExpMatch; + } + + return result; + } + + unsigned interpret() + { + if (!input.isAvailableInput(0)) + return offsetNoMatch; + + for (unsigned i = 0; i < pattern->m_body->m_numSubpatterns + 1; ++i) + output[i << 1] = offsetNoMatch; + + allocatorPool = pattern->m_allocator->startAllocator(); + if (!allocatorPool) + CRASH(); + + DisjunctionContext* context = allocDisjunctionContext(pattern->m_body.get()); + + JSRegExpResult result = matchDisjunction(pattern->m_body.get(), context, false); + if (result == JSRegExpMatch) { + output[0] = context->matchBegin; + output[1] = context->matchEnd; + } + + freeDisjunctionContext(context); + + pattern->m_allocator->stopAllocator(); + + ASSERT((result == JSRegExpMatch) == (output[0] != offsetNoMatch)); + return output[0]; + } + + Interpreter(BytecodePattern* pattern, unsigned* output, const CharType* input, unsigned length, unsigned start) + : pattern(pattern) + , output(output) + , input(input, start, length) + , allocatorPool(0) + , remainingMatchCount(matchLimit) + { + } + +private: + BytecodePattern* pattern; + unsigned* output; + InputStream input; + BumpPointerPool* allocatorPool; + unsigned remainingMatchCount; +}; + + + +class ByteCompiler { + struct ParenthesesStackEntry { + unsigned beginTerm; + unsigned savedAlternativeIndex; + ParenthesesStackEntry(unsigned beginTerm, unsigned savedAlternativeIndex/*, unsigned subpatternId, bool capture = false*/) + : beginTerm(beginTerm) + , savedAlternativeIndex(savedAlternativeIndex) + { + } + }; + +public: + ByteCompiler(YarrPattern& pattern) + : m_pattern(pattern) + { + m_currentAlternativeIndex = 0; + } + + PassOwnPtr compile(BumpPointerAllocator* allocator) + { + regexBegin(m_pattern.m_numSubpatterns, m_pattern.m_body->m_callFrameSize, m_pattern.m_body->m_alternatives[0]->onceThrough()); + emitDisjunction(m_pattern.m_body); + regexEnd(); + + return adoptPtr(new BytecodePattern(m_bodyDisjunction.release(), m_allParenthesesInfo, m_pattern, allocator)); + } + + void checkInput(unsigned count) + { + m_bodyDisjunction->terms.append(ByteTerm::CheckInput(count)); + } + + void uncheckInput(unsigned count) + { + m_bodyDisjunction->terms.append(ByteTerm::UncheckInput(count)); + } + + void assertionBOL(unsigned inputPosition) + { + m_bodyDisjunction->terms.append(ByteTerm::BOL(inputPosition)); + } + + void assertionEOL(unsigned inputPosition) + { + m_bodyDisjunction->terms.append(ByteTerm::EOL(inputPosition)); + } + + void assertionWordBoundary(bool invert, unsigned inputPosition) + { + m_bodyDisjunction->terms.append(ByteTerm::WordBoundary(invert, inputPosition)); + } + + void atomPatternCharacter(UChar ch, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + if (m_pattern.m_ignoreCase) { + UChar lo = Unicode::toLower(ch); + UChar hi = Unicode::toUpper(ch); + + if (lo != hi) { + m_bodyDisjunction->terms.append(ByteTerm(lo, hi, inputPosition, frameLocation, quantityCount, quantityType)); + return; + } + } + + m_bodyDisjunction->terms.append(ByteTerm(ch, inputPosition, frameLocation, quantityCount, quantityType)); + } + + void atomCharacterClass(CharacterClass* characterClass, bool invert, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + m_bodyDisjunction->terms.append(ByteTerm(characterClass, invert, inputPosition)); + + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType; + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + } + + void atomBackReference(unsigned subpatternId, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + ASSERT(subpatternId); + + m_bodyDisjunction->terms.append(ByteTerm::BackReference(subpatternId, inputPosition)); + + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType; + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + } + + void atomParenthesesOnceBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) + { + int beginTerm = m_bodyDisjunction->terms.size(); + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceBegin, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; + + m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); + m_currentAlternativeIndex = beginTerm + 1; + } + + void atomParenthesesTerminalBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) + { + int beginTerm = m_bodyDisjunction->terms.size(); + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternTerminalBegin, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; + + m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); + m_currentAlternativeIndex = beginTerm + 1; + } + + void atomParenthesesSubpatternBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) + { + // Errrk! - this is a little crazy, we initially generate as a TypeParenthesesSubpatternOnceBegin, + // then fix this up at the end! - simplifying this should make it much clearer. + // https://bugs.webkit.org/show_bug.cgi?id=50136 + + int beginTerm = m_bodyDisjunction->terms.size(); + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceBegin, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; + + m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); + m_currentAlternativeIndex = beginTerm + 1; + } + + void atomParentheticalAssertionBegin(unsigned subpatternId, bool invert, unsigned frameLocation, unsigned alternativeFrameLocation) + { + int beginTerm = m_bodyDisjunction->terms.size(); + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParentheticalAssertionBegin, subpatternId, false, invert, 0)); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; + + m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); + m_currentAlternativeIndex = beginTerm + 1; + } + + void atomParentheticalAssertionEnd(unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + unsigned beginTerm = popParenthesesStack(); + closeAlternative(beginTerm + 1); + unsigned endTerm = m_bodyDisjunction->terms.size(); + + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParentheticalAssertionBegin); + + bool invert = m_bodyDisjunction->terms[beginTerm].invert(); + unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParentheticalAssertionEnd, subpatternId, false, invert, inputPosition)); + m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; + + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; + m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; + } + + void assertionDotStarEnclosure(bool bolAnchored, bool eolAnchored) + { + m_bodyDisjunction->terms.append(ByteTerm::DotStarEnclosure(bolAnchored, eolAnchored)); + } + + unsigned popParenthesesStack() + { + ASSERT(m_parenthesesStack.size()); + int stackEnd = m_parenthesesStack.size() - 1; + unsigned beginTerm = m_parenthesesStack[stackEnd].beginTerm; + m_currentAlternativeIndex = m_parenthesesStack[stackEnd].savedAlternativeIndex; + m_parenthesesStack.shrink(stackEnd); + + ASSERT(beginTerm < m_bodyDisjunction->terms.size()); + ASSERT(m_currentAlternativeIndex < m_bodyDisjunction->terms.size()); + + return beginTerm; + } + +#ifndef NDEBUG + void dumpDisjunction(ByteDisjunction* disjunction) + { + dataLogF("ByteDisjunction(%p):\n\t", disjunction); + for (unsigned i = 0; i < disjunction->terms.size(); ++i) + dataLogF("{ %d } ", disjunction->terms[i].type); + dataLogF("\n"); + } +#endif + + void closeAlternative(int beginTerm) + { + int origBeginTerm = beginTerm; + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeAlternativeBegin); + int endIndex = m_bodyDisjunction->terms.size(); + + unsigned frameLocation = m_bodyDisjunction->terms[beginTerm].frameLocation; + + if (!m_bodyDisjunction->terms[beginTerm].alternative.next) + m_bodyDisjunction->terms.remove(beginTerm); + else { + while (m_bodyDisjunction->terms[beginTerm].alternative.next) { + beginTerm += m_bodyDisjunction->terms[beginTerm].alternative.next; + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeAlternativeDisjunction); + m_bodyDisjunction->terms[beginTerm].alternative.end = endIndex - beginTerm; + m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; + } + + m_bodyDisjunction->terms[beginTerm].alternative.next = origBeginTerm - beginTerm; + + m_bodyDisjunction->terms.append(ByteTerm::AlternativeEnd()); + m_bodyDisjunction->terms[endIndex].frameLocation = frameLocation; + } + } + + void closeBodyAlternative() + { + int beginTerm = 0; + int origBeginTerm = 0; + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeBodyAlternativeBegin); + int endIndex = m_bodyDisjunction->terms.size(); + + unsigned frameLocation = m_bodyDisjunction->terms[beginTerm].frameLocation; + + while (m_bodyDisjunction->terms[beginTerm].alternative.next) { + beginTerm += m_bodyDisjunction->terms[beginTerm].alternative.next; + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeBodyAlternativeDisjunction); + m_bodyDisjunction->terms[beginTerm].alternative.end = endIndex - beginTerm; + m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; + } + + m_bodyDisjunction->terms[beginTerm].alternative.next = origBeginTerm - beginTerm; + + m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeEnd()); + m_bodyDisjunction->terms[endIndex].frameLocation = frameLocation; + } + + void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType, unsigned callFrameSize = 0) + { + unsigned beginTerm = popParenthesesStack(); + closeAlternative(beginTerm + 1); + unsigned endTerm = m_bodyDisjunction->terms.size(); + + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + + ByteTerm& parenthesesBegin = m_bodyDisjunction->terms[beginTerm]; + + bool capture = parenthesesBegin.capture(); + unsigned subpatternId = parenthesesBegin.atom.subpatternId; + + unsigned numSubpatterns = lastSubpatternId - subpatternId + 1; + ByteDisjunction* parenthesesDisjunction = new ByteDisjunction(numSubpatterns, callFrameSize); + + parenthesesDisjunction->terms.append(ByteTerm::SubpatternBegin()); + for (unsigned termInParentheses = beginTerm + 1; termInParentheses < endTerm; ++termInParentheses) + parenthesesDisjunction->terms.append(m_bodyDisjunction->terms[termInParentheses]); + parenthesesDisjunction->terms.append(ByteTerm::SubpatternEnd()); + + m_bodyDisjunction->terms.shrink(beginTerm); + + m_allParenthesesInfo.append(parenthesesDisjunction); + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture, inputPosition)); + + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; + m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; + } + + void atomParenthesesOnceEnd(int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + unsigned beginTerm = popParenthesesStack(); + closeAlternative(beginTerm + 1); + unsigned endTerm = m_bodyDisjunction->terms.size(); + + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + + bool capture = m_bodyDisjunction->terms[beginTerm].capture(); + unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceEnd, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; + + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; + m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; + } + + void atomParenthesesTerminalEnd(int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + unsigned beginTerm = popParenthesesStack(); + closeAlternative(beginTerm + 1); + unsigned endTerm = m_bodyDisjunction->terms.size(); + + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); + + bool capture = m_bodyDisjunction->terms[beginTerm].capture(); + unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternTerminalEnd, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; + + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; + m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; + } + + void regexBegin(unsigned numSubpatterns, unsigned callFrameSize, bool onceThrough) + { + m_bodyDisjunction = adoptPtr(new ByteDisjunction(numSubpatterns, callFrameSize)); + m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeBegin(onceThrough)); + m_bodyDisjunction->terms[0].frameLocation = 0; + m_currentAlternativeIndex = 0; + } + + void regexEnd() + { + closeBodyAlternative(); + } + + void alternativeBodyDisjunction(bool onceThrough) + { + int newAlternativeIndex = m_bodyDisjunction->terms.size(); + m_bodyDisjunction->terms[m_currentAlternativeIndex].alternative.next = newAlternativeIndex - m_currentAlternativeIndex; + m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeDisjunction(onceThrough)); + + m_currentAlternativeIndex = newAlternativeIndex; + } + + void alternativeDisjunction() + { + int newAlternativeIndex = m_bodyDisjunction->terms.size(); + m_bodyDisjunction->terms[m_currentAlternativeIndex].alternative.next = newAlternativeIndex - m_currentAlternativeIndex; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeDisjunction()); + + m_currentAlternativeIndex = newAlternativeIndex; + } + + void emitDisjunction(PatternDisjunction* disjunction, unsigned inputCountAlreadyChecked = 0, unsigned parenthesesInputCountAlreadyChecked = 0) + { + for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { + unsigned currentCountAlreadyChecked = inputCountAlreadyChecked; + + PatternAlternative* alternative = disjunction->m_alternatives[alt]; + + if (alt) { + if (disjunction == m_pattern.m_body) + alternativeBodyDisjunction(alternative->onceThrough()); + else + alternativeDisjunction(); + } + + unsigned minimumSize = alternative->m_minimumSize; + ASSERT(minimumSize >= parenthesesInputCountAlreadyChecked); + unsigned countToCheck = minimumSize - parenthesesInputCountAlreadyChecked; + + if (countToCheck) { + checkInput(countToCheck); + currentCountAlreadyChecked += countToCheck; + } + + for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { + PatternTerm& term = alternative->m_terms[i]; + + switch (term.type) { + case PatternTerm::TypeAssertionBOL: + assertionBOL(currentCountAlreadyChecked - term.inputPosition); + break; + + case PatternTerm::TypeAssertionEOL: + assertionEOL(currentCountAlreadyChecked - term.inputPosition); + break; + + case PatternTerm::TypeAssertionWordBoundary: + assertionWordBoundary(term.invert(), currentCountAlreadyChecked - term.inputPosition); + break; + + case PatternTerm::TypePatternCharacter: + atomPatternCharacter(term.patternCharacter, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); + break; + + case PatternTerm::TypeCharacterClass: + atomCharacterClass(term.characterClass, term.invert(), currentCountAlreadyChecked- term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); + break; + + case PatternTerm::TypeBackReference: + atomBackReference(term.backReferenceSubpatternId, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); + break; + + case PatternTerm::TypeForwardReference: + break; + + case PatternTerm::TypeParenthesesSubpattern: { + unsigned disjunctionAlreadyCheckedCount = 0; + if (term.quantityCount == 1 && !term.parentheses.isCopy) { + unsigned alternativeFrameLocation = term.frameLocation; + // For QuantifierFixedCount we pre-check the minimum size; for greedy/non-greedy we reserve a slot in the frame. + if (term.quantityType == QuantifierFixedCount) + disjunctionAlreadyCheckedCount = term.parentheses.disjunction->m_minimumSize; + else + alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; + unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; + atomParenthesesOnceBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, alternativeFrameLocation); + emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount); + atomParenthesesOnceEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType); + } else if (term.parentheses.isTerminal) { + unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; + atomParenthesesTerminalBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, term.frameLocation + YarrStackSpaceForBackTrackInfoParenthesesOnce); + emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount); + atomParenthesesTerminalEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType); + } else { + unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; + atomParenthesesSubpatternBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, 0); + emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, 0); + atomParenthesesSubpatternEnd(term.parentheses.lastSubpatternId, delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType, term.parentheses.disjunction->m_callFrameSize); + } + break; + } + + case PatternTerm::TypeParentheticalAssertion: { + unsigned alternativeFrameLocation = term.frameLocation + YarrStackSpaceForBackTrackInfoParentheticalAssertion; + + ASSERT(currentCountAlreadyChecked >= static_cast(term.inputPosition)); + unsigned positiveInputOffset = currentCountAlreadyChecked - static_cast(term.inputPosition); + unsigned uncheckAmount = 0; + if (positiveInputOffset > term.parentheses.disjunction->m_minimumSize) { + uncheckAmount = positiveInputOffset - term.parentheses.disjunction->m_minimumSize; + uncheckInput(uncheckAmount); + currentCountAlreadyChecked -= uncheckAmount; + } + + atomParentheticalAssertionBegin(term.parentheses.subpatternId, term.invert(), term.frameLocation, alternativeFrameLocation); + emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, positiveInputOffset - uncheckAmount); + atomParentheticalAssertionEnd(0, term.frameLocation, term.quantityCount, term.quantityType); + if (uncheckAmount) { + checkInput(uncheckAmount); + currentCountAlreadyChecked += uncheckAmount; + } + break; + } + + case PatternTerm::TypeDotStarEnclosure: + assertionDotStarEnclosure(term.anchors.bolAnchor, term.anchors.eolAnchor); + break; + } + } + } + } + +private: + YarrPattern& m_pattern; + OwnPtr m_bodyDisjunction; + unsigned m_currentAlternativeIndex; + Vector m_parenthesesStack; + Vector m_allParenthesesInfo; +}; + +PassOwnPtr byteCompile(YarrPattern& pattern, BumpPointerAllocator* allocator) +{ + return ByteCompiler(pattern).compile(allocator); +} + +unsigned interpret(BytecodePattern* bytecode, const String& input, unsigned start, unsigned* output) +{ + if (input.is8Bit()) + return Interpreter(bytecode, output, input.characters8(), input.length(), start).interpret(); + return Interpreter(bytecode, output, input.characters16(), input.length(), start).interpret(); +} + +unsigned interpret(BytecodePattern* bytecode, const LChar* input, unsigned length, unsigned start, unsigned* output) +{ + return Interpreter(bytecode, output, input, length, start).interpret(); +} + +unsigned interpret(BytecodePattern* bytecode, const UChar* input, unsigned length, unsigned start, unsigned* output) +{ + return Interpreter(bytecode, output, input, length, start).interpret(); +} + +// These should be the same for both UChar & LChar. +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoPatternCharacter) == (YarrStackSpaceForBackTrackInfoPatternCharacter * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoPatternCharacter); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoCharacterClass) == (YarrStackSpaceForBackTrackInfoCharacterClass * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoCharacterClass); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoBackReference) == (YarrStackSpaceForBackTrackInfoBackReference * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoBackReference); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoAlternative) == (YarrStackSpaceForBackTrackInfoAlternative * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoAlternative); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParentheticalAssertion) == (YarrStackSpaceForBackTrackInfoParentheticalAssertion * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheticalAssertion); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParenthesesOnce) == (YarrStackSpaceForBackTrackInfoParenthesesOnce * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParenthesesOnce); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParentheses) == (YarrStackSpaceForBackTrackInfoParentheses * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheses); + + +} } diff --git a/masm/yarr/YarrInterpreter.h b/masm/yarr/YarrInterpreter.h new file mode 100644 index 0000000000..fb60bd979d --- /dev/null +++ b/masm/yarr/YarrInterpreter.h @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrInterpreter_h +#define YarrInterpreter_h + +#include "YarrPattern.h" +#include +#include + +namespace WTF { +class BumpPointerAllocator; +} +using WTF::BumpPointerAllocator; + +namespace JSC { namespace Yarr { + +class ByteDisjunction; + +struct ByteTerm { + enum Type { + TypeBodyAlternativeBegin, + TypeBodyAlternativeDisjunction, + TypeBodyAlternativeEnd, + TypeAlternativeBegin, + TypeAlternativeDisjunction, + TypeAlternativeEnd, + TypeSubpatternBegin, + TypeSubpatternEnd, + TypeAssertionBOL, + TypeAssertionEOL, + TypeAssertionWordBoundary, + TypePatternCharacterOnce, + TypePatternCharacterFixed, + TypePatternCharacterGreedy, + TypePatternCharacterNonGreedy, + TypePatternCasedCharacterOnce, + TypePatternCasedCharacterFixed, + TypePatternCasedCharacterGreedy, + TypePatternCasedCharacterNonGreedy, + TypeCharacterClass, + TypeBackReference, + TypeParenthesesSubpattern, + TypeParenthesesSubpatternOnceBegin, + TypeParenthesesSubpatternOnceEnd, + TypeParenthesesSubpatternTerminalBegin, + TypeParenthesesSubpatternTerminalEnd, + TypeParentheticalAssertionBegin, + TypeParentheticalAssertionEnd, + TypeCheckInput, + TypeUncheckInput, + TypeDotStarEnclosure, + } type; + union { + struct { + union { + UChar patternCharacter; + struct { + UChar lo; + UChar hi; + } casedCharacter; + CharacterClass* characterClass; + unsigned subpatternId; + }; + union { + ByteDisjunction* parenthesesDisjunction; + unsigned parenthesesWidth; + }; + QuantifierType quantityType; + unsigned quantityCount; + } atom; + struct { + int next; + int end; + bool onceThrough; + } alternative; + struct { + bool m_bol : 1; + bool m_eol : 1; + } anchors; + unsigned checkInputCount; + }; + unsigned frameLocation; + bool m_capture : 1; + bool m_invert : 1; + unsigned inputPosition; + + ByteTerm(UChar ch, int inputPos, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + : frameLocation(frameLocation) + , m_capture(false) + , m_invert(false) + { + switch (quantityType) { + case QuantifierFixedCount: + type = (quantityCount == 1) ? ByteTerm::TypePatternCharacterOnce : ByteTerm::TypePatternCharacterFixed; + break; + case QuantifierGreedy: + type = ByteTerm::TypePatternCharacterGreedy; + break; + case QuantifierNonGreedy: + type = ByteTerm::TypePatternCharacterNonGreedy; + break; + } + + atom.patternCharacter = ch; + atom.quantityType = quantityType; + atom.quantityCount = quantityCount.unsafeGet(); + inputPosition = inputPos; + } + + ByteTerm(UChar lo, UChar hi, int inputPos, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + : frameLocation(frameLocation) + , m_capture(false) + , m_invert(false) + { + switch (quantityType) { + case QuantifierFixedCount: + type = (quantityCount == 1) ? ByteTerm::TypePatternCasedCharacterOnce : ByteTerm::TypePatternCasedCharacterFixed; + break; + case QuantifierGreedy: + type = ByteTerm::TypePatternCasedCharacterGreedy; + break; + case QuantifierNonGreedy: + type = ByteTerm::TypePatternCasedCharacterNonGreedy; + break; + } + + atom.casedCharacter.lo = lo; + atom.casedCharacter.hi = hi; + atom.quantityType = quantityType; + atom.quantityCount = quantityCount.unsafeGet(); + inputPosition = inputPos; + } + + ByteTerm(CharacterClass* characterClass, bool invert, int inputPos) + : type(ByteTerm::TypeCharacterClass) + , m_capture(false) + , m_invert(invert) + { + atom.characterClass = characterClass; + atom.quantityType = QuantifierFixedCount; + atom.quantityCount = 1; + inputPosition = inputPos; + } + + ByteTerm(Type type, unsigned subpatternId, ByteDisjunction* parenthesesInfo, bool capture, int inputPos) + : type(type) + , m_capture(capture) + , m_invert(false) + { + atom.subpatternId = subpatternId; + atom.parenthesesDisjunction = parenthesesInfo; + atom.quantityType = QuantifierFixedCount; + atom.quantityCount = 1; + inputPosition = inputPos; + } + + ByteTerm(Type type, bool invert = false) + : type(type) + , m_capture(false) + , m_invert(invert) + { + atom.quantityType = QuantifierFixedCount; + atom.quantityCount = 1; + } + + ByteTerm(Type type, unsigned subpatternId, bool capture, bool invert, int inputPos) + : type(type) + , m_capture(capture) + , m_invert(invert) + { + atom.subpatternId = subpatternId; + atom.quantityType = QuantifierFixedCount; + atom.quantityCount = 1; + inputPosition = inputPos; + } + + static ByteTerm BOL(int inputPos) + { + ByteTerm term(TypeAssertionBOL); + term.inputPosition = inputPos; + return term; + } + + static ByteTerm CheckInput(Checked count) + { + ByteTerm term(TypeCheckInput); + term.checkInputCount = count.unsafeGet(); + return term; + } + + static ByteTerm UncheckInput(Checked count) + { + ByteTerm term(TypeUncheckInput); + term.checkInputCount = count.unsafeGet(); + return term; + } + + static ByteTerm EOL(int inputPos) + { + ByteTerm term(TypeAssertionEOL); + term.inputPosition = inputPos; + return term; + } + + static ByteTerm WordBoundary(bool invert, int inputPos) + { + ByteTerm term(TypeAssertionWordBoundary, invert); + term.inputPosition = inputPos; + return term; + } + + static ByteTerm BackReference(unsigned subpatternId, int inputPos) + { + return ByteTerm(TypeBackReference, subpatternId, false, false, inputPos); + } + + static ByteTerm BodyAlternativeBegin(bool onceThrough) + { + ByteTerm term(TypeBodyAlternativeBegin); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = onceThrough; + return term; + } + + static ByteTerm BodyAlternativeDisjunction(bool onceThrough) + { + ByteTerm term(TypeBodyAlternativeDisjunction); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = onceThrough; + return term; + } + + static ByteTerm BodyAlternativeEnd() + { + ByteTerm term(TypeBodyAlternativeEnd); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = false; + return term; + } + + static ByteTerm AlternativeBegin() + { + ByteTerm term(TypeAlternativeBegin); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = false; + return term; + } + + static ByteTerm AlternativeDisjunction() + { + ByteTerm term(TypeAlternativeDisjunction); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = false; + return term; + } + + static ByteTerm AlternativeEnd() + { + ByteTerm term(TypeAlternativeEnd); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = false; + return term; + } + + static ByteTerm SubpatternBegin() + { + return ByteTerm(TypeSubpatternBegin); + } + + static ByteTerm SubpatternEnd() + { + return ByteTerm(TypeSubpatternEnd); + } + + static ByteTerm DotStarEnclosure(bool bolAnchor, bool eolAnchor) + { + ByteTerm term(TypeDotStarEnclosure); + term.anchors.m_bol = bolAnchor; + term.anchors.m_eol = eolAnchor; + return term; + } + + bool invert() + { + return m_invert; + } + + bool capture() + { + return m_capture; + } +}; + +class ByteDisjunction { + WTF_MAKE_FAST_ALLOCATED; +public: + ByteDisjunction(unsigned numSubpatterns, unsigned frameSize) + : m_numSubpatterns(numSubpatterns) + , m_frameSize(frameSize) + { + } + + Vector terms; + unsigned m_numSubpatterns; + unsigned m_frameSize; +}; + +struct BytecodePattern { + WTF_MAKE_FAST_ALLOCATED; +public: + BytecodePattern(PassOwnPtr body, Vector allParenthesesInfo, YarrPattern& pattern, BumpPointerAllocator* allocator) + : m_body(body) + , m_ignoreCase(pattern.m_ignoreCase) + , m_multiline(pattern.m_multiline) + , m_allocator(allocator) + { + newlineCharacterClass = pattern.newlineCharacterClass(); + wordcharCharacterClass = pattern.wordcharCharacterClass(); + + m_allParenthesesInfo.append(allParenthesesInfo); + m_userCharacterClasses.append(pattern.m_userCharacterClasses); + // 'Steal' the YarrPattern's CharacterClasses! We clear its + // array, so that it won't delete them on destruction. We'll + // take responsibility for that. + pattern.m_userCharacterClasses.clear(); + } + + ~BytecodePattern() + { + deleteAllValues(m_allParenthesesInfo); + deleteAllValues(m_userCharacterClasses); + } + + OwnPtr m_body; + bool m_ignoreCase; + bool m_multiline; + // Each BytecodePattern is associated with a RegExp, each RegExp is associated + // with a JSGlobalData. Cache a pointer to out JSGlobalData's m_regExpAllocator. + BumpPointerAllocator* m_allocator; + + CharacterClass* newlineCharacterClass; + CharacterClass* wordcharCharacterClass; + +private: + Vector m_allParenthesesInfo; + Vector m_userCharacterClasses; +}; + +JS_EXPORT_PRIVATE PassOwnPtr byteCompile(YarrPattern&, BumpPointerAllocator*); +JS_EXPORT_PRIVATE unsigned interpret(BytecodePattern*, const String& input, unsigned start, unsigned* output); +unsigned interpret(BytecodePattern*, const LChar* input, unsigned length, unsigned start, unsigned* output); +unsigned interpret(BytecodePattern*, const UChar* input, unsigned length, unsigned start, unsigned* output); + +} } // namespace JSC::Yarr + +#endif // YarrInterpreter_h diff --git a/masm/yarr/YarrJIT.cpp b/masm/yarr/YarrJIT.cpp new file mode 100644 index 0000000000..ce84e2c74f --- /dev/null +++ b/masm/yarr/YarrJIT.cpp @@ -0,0 +1,2667 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "YarrJIT.h" + +#include +#include "LinkBuffer.h" +#include "Options.h" +#include "Yarr.h" +#include "YarrCanonicalizeUCS2.h" + +#if ENABLE(YARR_JIT) + +using namespace WTF; + +namespace JSC { namespace Yarr { + +template +class YarrGenerator : private MacroAssembler { + friend void jitCompile(JSGlobalData*, YarrCodeBlock& jitObject, const String& pattern, unsigned& numSubpatterns, const char*& error, bool ignoreCase, bool multiline); + +#if CPU(ARM) + static const RegisterID input = ARMRegisters::r0; + static const RegisterID index = ARMRegisters::r1; + static const RegisterID length = ARMRegisters::r2; + static const RegisterID output = ARMRegisters::r4; + + static const RegisterID regT0 = ARMRegisters::r5; + static const RegisterID regT1 = ARMRegisters::r6; + + static const RegisterID returnRegister = ARMRegisters::r0; + static const RegisterID returnRegister2 = ARMRegisters::r1; +#elif CPU(MIPS) + static const RegisterID input = MIPSRegisters::a0; + static const RegisterID index = MIPSRegisters::a1; + static const RegisterID length = MIPSRegisters::a2; + static const RegisterID output = MIPSRegisters::a3; + + static const RegisterID regT0 = MIPSRegisters::t4; + static const RegisterID regT1 = MIPSRegisters::t5; + + static const RegisterID returnRegister = MIPSRegisters::v0; + static const RegisterID returnRegister2 = MIPSRegisters::v1; +#elif CPU(SH4) + static const RegisterID input = SH4Registers::r4; + static const RegisterID index = SH4Registers::r5; + static const RegisterID length = SH4Registers::r6; + static const RegisterID output = SH4Registers::r7; + + static const RegisterID regT0 = SH4Registers::r0; + static const RegisterID regT1 = SH4Registers::r1; + + static const RegisterID returnRegister = SH4Registers::r0; + static const RegisterID returnRegister2 = SH4Registers::r1; +#elif CPU(X86) + static const RegisterID input = X86Registers::eax; + static const RegisterID index = X86Registers::edx; + static const RegisterID length = X86Registers::ecx; + static const RegisterID output = X86Registers::edi; + + static const RegisterID regT0 = X86Registers::ebx; + static const RegisterID regT1 = X86Registers::esi; + + static const RegisterID returnRegister = X86Registers::eax; + static const RegisterID returnRegister2 = X86Registers::edx; +#elif CPU(X86_64) + static const RegisterID input = X86Registers::edi; + static const RegisterID index = X86Registers::esi; + static const RegisterID length = X86Registers::edx; + static const RegisterID output = X86Registers::ecx; + + static const RegisterID regT0 = X86Registers::eax; + static const RegisterID regT1 = X86Registers::ebx; + + static const RegisterID returnRegister = X86Registers::eax; + static const RegisterID returnRegister2 = X86Registers::edx; +#endif + + void optimizeAlternative(PatternAlternative* alternative) + { + if (!alternative->m_terms.size()) + return; + + for (unsigned i = 0; i < alternative->m_terms.size() - 1; ++i) { + PatternTerm& term = alternative->m_terms[i]; + PatternTerm& nextTerm = alternative->m_terms[i + 1]; + + if ((term.type == PatternTerm::TypeCharacterClass) + && (term.quantityType == QuantifierFixedCount) + && (nextTerm.type == PatternTerm::TypePatternCharacter) + && (nextTerm.quantityType == QuantifierFixedCount)) { + PatternTerm termCopy = term; + alternative->m_terms[i] = nextTerm; + alternative->m_terms[i + 1] = termCopy; + } + } + } + + void matchCharacterClassRange(RegisterID character, JumpList& failures, JumpList& matchDest, const CharacterRange* ranges, unsigned count, unsigned* matchIndex, const UChar* matches, unsigned matchCount) + { + do { + // pick which range we're going to generate + int which = count >> 1; + char lo = ranges[which].begin; + char hi = ranges[which].end; + + // check if there are any ranges or matches below lo. If not, just jl to failure - + // if there is anything else to check, check that first, if it falls through jmp to failure. + if ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) { + Jump loOrAbove = branch32(GreaterThanOrEqual, character, Imm32((unsigned short)lo)); + + // generate code for all ranges before this one + if (which) + matchCharacterClassRange(character, failures, matchDest, ranges, which, matchIndex, matches, matchCount); + + while ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) { + matchDest.append(branch32(Equal, character, Imm32((unsigned short)matches[*matchIndex]))); + ++*matchIndex; + } + failures.append(jump()); + + loOrAbove.link(this); + } else if (which) { + Jump loOrAbove = branch32(GreaterThanOrEqual, character, Imm32((unsigned short)lo)); + + matchCharacterClassRange(character, failures, matchDest, ranges, which, matchIndex, matches, matchCount); + failures.append(jump()); + + loOrAbove.link(this); + } else + failures.append(branch32(LessThan, character, Imm32((unsigned short)lo))); + + while ((*matchIndex < matchCount) && (matches[*matchIndex] <= hi)) + ++*matchIndex; + + matchDest.append(branch32(LessThanOrEqual, character, Imm32((unsigned short)hi))); + // fall through to here, the value is above hi. + + // shuffle along & loop around if there are any more matches to handle. + unsigned next = which + 1; + ranges += next; + count -= next; + } while (count); + } + + void matchCharacterClass(RegisterID character, JumpList& matchDest, const CharacterClass* charClass) + { + if (charClass->m_table) { + ExtendedAddress tableEntry(character, reinterpret_cast(charClass->m_table->m_table)); + matchDest.append(branchTest8(charClass->m_table->m_inverted ? Zero : NonZero, tableEntry)); + return; + } + Jump unicodeFail; + if (charClass->m_matchesUnicode.size() || charClass->m_rangesUnicode.size()) { + Jump isAscii = branch32(LessThanOrEqual, character, TrustedImm32(0x7f)); + + if (charClass->m_matchesUnicode.size()) { + for (unsigned i = 0; i < charClass->m_matchesUnicode.size(); ++i) { + UChar ch = charClass->m_matchesUnicode[i]; + matchDest.append(branch32(Equal, character, Imm32(ch))); + } + } + + if (charClass->m_rangesUnicode.size()) { + for (unsigned i = 0; i < charClass->m_rangesUnicode.size(); ++i) { + UChar lo = charClass->m_rangesUnicode[i].begin; + UChar hi = charClass->m_rangesUnicode[i].end; + + Jump below = branch32(LessThan, character, Imm32(lo)); + matchDest.append(branch32(LessThanOrEqual, character, Imm32(hi))); + below.link(this); + } + } + + unicodeFail = jump(); + isAscii.link(this); + } + + if (charClass->m_ranges.size()) { + unsigned matchIndex = 0; + JumpList failures; + matchCharacterClassRange(character, failures, matchDest, charClass->m_ranges.begin(), charClass->m_ranges.size(), &matchIndex, charClass->m_matches.begin(), charClass->m_matches.size()); + while (matchIndex < charClass->m_matches.size()) + matchDest.append(branch32(Equal, character, Imm32((unsigned short)charClass->m_matches[matchIndex++]))); + + failures.link(this); + } else if (charClass->m_matches.size()) { + // optimization: gather 'a','A' etc back together, can mask & test once. + Vector matchesAZaz; + + for (unsigned i = 0; i < charClass->m_matches.size(); ++i) { + char ch = charClass->m_matches[i]; + if (m_pattern.m_ignoreCase) { + if (isASCIILower(ch)) { + matchesAZaz.append(ch); + continue; + } + if (isASCIIUpper(ch)) + continue; + } + matchDest.append(branch32(Equal, character, Imm32((unsigned short)ch))); + } + + if (unsigned countAZaz = matchesAZaz.size()) { + or32(TrustedImm32(32), character); + for (unsigned i = 0; i < countAZaz; ++i) + matchDest.append(branch32(Equal, character, TrustedImm32(matchesAZaz[i]))); + } + } + + if (charClass->m_matchesUnicode.size() || charClass->m_rangesUnicode.size()) + unicodeFail.link(this); + } + + // Jumps if input not available; will have (incorrectly) incremented already! + Jump jumpIfNoAvailableInput(unsigned countToCheck = 0) + { + if (countToCheck) + add32(Imm32(countToCheck), index); + return branch32(Above, index, length); + } + + Jump jumpIfAvailableInput(unsigned countToCheck) + { + add32(Imm32(countToCheck), index); + return branch32(BelowOrEqual, index, length); + } + + Jump checkInput() + { + return branch32(BelowOrEqual, index, length); + } + + Jump atEndOfInput() + { + return branch32(Equal, index, length); + } + + Jump notAtEndOfInput() + { + return branch32(NotEqual, index, length); + } + + Jump jumpIfCharNotEquals(UChar ch, int inputPosition, RegisterID character) + { + readCharacter(inputPosition, character); + + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { + or32(TrustedImm32(0x20), character); + ch |= 0x20; + } + + return branch32(NotEqual, character, Imm32(ch)); + } + + void readCharacter(int inputPosition, RegisterID reg) + { + if (m_charSize == Char8) + load8(BaseIndex(input, index, TimesOne, inputPosition * sizeof(char)), reg); + else + load16(BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), reg); + } + + void storeToFrame(RegisterID reg, unsigned frameLocation) + { + poke(reg, frameLocation); + } + + void storeToFrame(TrustedImm32 imm, unsigned frameLocation) + { + poke(imm, frameLocation); + } + + DataLabelPtr storeToFrameWithPatch(unsigned frameLocation) + { + return storePtrWithPatch(TrustedImmPtr(0), Address(stackPointerRegister, frameLocation * sizeof(void*))); + } + + void loadFromFrame(unsigned frameLocation, RegisterID reg) + { + peek(reg, frameLocation); + } + + void loadFromFrameAndJump(unsigned frameLocation) + { + jump(Address(stackPointerRegister, frameLocation * sizeof(void*))); + } + + void initCallFrame() + { + unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; + if (callFrameSize) + subPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); + } + void removeCallFrame() + { + unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; + if (callFrameSize) + addPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); + } + + // Used to record subpatters, should only be called if compileMode is IncludeSubpatterns. + void setSubpatternStart(RegisterID reg, unsigned subpattern) + { + ASSERT(subpattern); + // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( + store32(reg, Address(output, (subpattern << 1) * sizeof(int))); + } + void setSubpatternEnd(RegisterID reg, unsigned subpattern) + { + ASSERT(subpattern); + // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( + store32(reg, Address(output, ((subpattern << 1) + 1) * sizeof(int))); + } + void clearSubpatternStart(unsigned subpattern) + { + ASSERT(subpattern); + // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( + store32(TrustedImm32(-1), Address(output, (subpattern << 1) * sizeof(int))); + } + + // We use one of three different strategies to track the start of the current match, + // while matching. + // 1) If the pattern has a fixed size, do nothing! - we calculate the value lazily + // at the end of matching. This is irrespective of compileMode, and in this case + // these methods should never be called. + // 2) If we're compiling IncludeSubpatterns, 'output' contains a pointer to an output + // vector, store the match start in the output vector. + // 3) If we're compiling MatchOnly, 'output' is unused, store the match start directly + // in this register. + void setMatchStart(RegisterID reg) + { + ASSERT(!m_pattern.m_body->m_hasFixedSize); + if (compileMode == IncludeSubpatterns) + store32(reg, output); + else + move(reg, output); + } + void getMatchStart(RegisterID reg) + { + ASSERT(!m_pattern.m_body->m_hasFixedSize); + if (compileMode == IncludeSubpatterns) + load32(output, reg); + else + move(output, reg); + } + + enum YarrOpCode { + // These nodes wrap body alternatives - those in the main disjunction, + // rather than subpatterns or assertions. These are chained together in + // a doubly linked list, with a 'begin' node for the first alternative, + // a 'next' node for each subsequent alternative, and an 'end' node at + // the end. In the case of repeating alternatives, the 'end' node also + // has a reference back to 'begin'. + OpBodyAlternativeBegin, + OpBodyAlternativeNext, + OpBodyAlternativeEnd, + // Similar to the body alternatives, but used for subpatterns with two + // or more alternatives. + OpNestedAlternativeBegin, + OpNestedAlternativeNext, + OpNestedAlternativeEnd, + // Used for alternatives in subpatterns where there is only a single + // alternative (backtrackingis easier in these cases), or for alternatives + // which never need to be backtracked (those in parenthetical assertions, + // terminal subpatterns). + OpSimpleNestedAlternativeBegin, + OpSimpleNestedAlternativeNext, + OpSimpleNestedAlternativeEnd, + // Used to wrap 'Once' subpattern matches (quantityCount == 1). + OpParenthesesSubpatternOnceBegin, + OpParenthesesSubpatternOnceEnd, + // Used to wrap 'Terminal' subpattern matches (at the end of the regexp). + OpParenthesesSubpatternTerminalBegin, + OpParenthesesSubpatternTerminalEnd, + // Used to wrap parenthetical assertions. + OpParentheticalAssertionBegin, + OpParentheticalAssertionEnd, + // Wraps all simple terms (pattern characters, character classes). + OpTerm, + // Where an expression contains only 'once through' body alternatives + // and no repeating ones, this op is used to return match failure. + OpMatchFailed + }; + + // This structure is used to hold the compiled opcode information, + // including reference back to the original PatternTerm/PatternAlternatives, + // and JIT compilation data structures. + struct YarrOp { + explicit YarrOp(PatternTerm* term) + : m_op(OpTerm) + , m_term(term) + , m_isDeadCode(false) + { + } + + explicit YarrOp(YarrOpCode op) + : m_op(op) + , m_isDeadCode(false) + { + } + + // The operation, as a YarrOpCode, and also a reference to the PatternTerm. + YarrOpCode m_op; + PatternTerm* m_term; + + // For alternatives, this holds the PatternAlternative and doubly linked + // references to this alternative's siblings. In the case of the + // OpBodyAlternativeEnd node at the end of a section of repeating nodes, + // m_nextOp will reference the OpBodyAlternativeBegin node of the first + // repeating alternative. + PatternAlternative* m_alternative; + size_t m_previousOp; + size_t m_nextOp; + + // Used to record a set of Jumps out of the generated code, typically + // used for jumps out to backtracking code, and a single reentry back + // into the code for a node (likely where a backtrack will trigger + // rematching). + Label m_reentry; + JumpList m_jumps; + + // Used for backtracking when the prior alternative did not consume any + // characters but matched. + Jump m_zeroLengthMatch; + + // This flag is used to null out the second pattern character, when + // two are fused to match a pair together. + bool m_isDeadCode; + + // Currently used in the case of some of the more complex management of + // 'm_checked', to cache the offset used in this alternative, to avoid + // recalculating it. + int m_checkAdjust; + + // Used by OpNestedAlternativeNext/End to hold the pointer to the + // value that will be pushed into the pattern's frame to return to, + // upon backtracking back into the disjunction. + DataLabelPtr m_returnAddress; + }; + + // BacktrackingState + // This class encapsulates information about the state of code generation + // whilst generating the code for backtracking, when a term fails to match. + // Upon entry to code generation of the backtracking code for a given node, + // the Backtracking state will hold references to all control flow sources + // that are outputs in need of further backtracking from the prior node + // generated (which is the subsequent operation in the regular expression, + // and in the m_ops Vector, since we generated backtracking backwards). + // These references to control flow take the form of: + // - A jump list of jumps, to be linked to code that will backtrack them + // further. + // - A set of DataLabelPtr values, to be populated with values to be + // treated effectively as return addresses backtracking into complex + // subpatterns. + // - A flag indicating that the current sequence of generated code up to + // this point requires backtracking. + class BacktrackingState { + public: + BacktrackingState() + : m_pendingFallthrough(false) + { + } + + // Add a jump or jumps, a return address, or set the flag indicating + // that the current 'fallthrough' control flow requires backtracking. + void append(const Jump& jump) + { + m_laterFailures.append(jump); + } + void append(JumpList& jumpList) + { + m_laterFailures.append(jumpList); + } + void append(const DataLabelPtr& returnAddress) + { + m_pendingReturns.append(returnAddress); + } + void fallthrough() + { + ASSERT(!m_pendingFallthrough); + m_pendingFallthrough = true; + } + + // These methods clear the backtracking state, either linking to the + // current location, a provided label, or copying the backtracking out + // to a JumpList. All actions may require code generation to take place, + // and as such are passed a pointer to the assembler. + void link(MacroAssembler* assembler) + { + if (m_pendingReturns.size()) { + Label here(assembler); + for (unsigned i = 0; i < m_pendingReturns.size(); ++i) + m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], here)); + m_pendingReturns.clear(); + } + m_laterFailures.link(assembler); + m_laterFailures.clear(); + m_pendingFallthrough = false; + } + void linkTo(Label label, MacroAssembler* assembler) + { + if (m_pendingReturns.size()) { + for (unsigned i = 0; i < m_pendingReturns.size(); ++i) + m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], label)); + m_pendingReturns.clear(); + } + if (m_pendingFallthrough) + assembler->jump(label); + m_laterFailures.linkTo(label, assembler); + m_laterFailures.clear(); + m_pendingFallthrough = false; + } + void takeBacktracksToJumpList(JumpList& jumpList, MacroAssembler* assembler) + { + if (m_pendingReturns.size()) { + Label here(assembler); + for (unsigned i = 0; i < m_pendingReturns.size(); ++i) + m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], here)); + m_pendingReturns.clear(); + m_pendingFallthrough = true; + } + if (m_pendingFallthrough) + jumpList.append(assembler->jump()); + jumpList.append(m_laterFailures); + m_laterFailures.clear(); + m_pendingFallthrough = false; + } + + bool isEmpty() + { + return m_laterFailures.empty() && m_pendingReturns.isEmpty() && !m_pendingFallthrough; + } + + // Called at the end of code generation to link all return addresses. + void linkDataLabels(LinkBuffer& linkBuffer) + { + ASSERT(isEmpty()); + for (unsigned i = 0; i < m_backtrackRecords.size(); ++i) + linkBuffer.patch(m_backtrackRecords[i].m_dataLabel, linkBuffer.locationOf(m_backtrackRecords[i].m_backtrackLocation)); + } + + private: + struct ReturnAddressRecord { + ReturnAddressRecord(DataLabelPtr dataLabel, Label backtrackLocation) + : m_dataLabel(dataLabel) + , m_backtrackLocation(backtrackLocation) + { + } + + DataLabelPtr m_dataLabel; + Label m_backtrackLocation; + }; + + JumpList m_laterFailures; + bool m_pendingFallthrough; + Vector m_pendingReturns; + Vector m_backtrackRecords; + }; + + // Generation methods: + // =================== + + // This method provides a default implementation of backtracking common + // to many terms; terms commonly jump out of the forwards matching path + // on any failed conditions, and add these jumps to the m_jumps list. If + // no special handling is required we can often just backtrack to m_jumps. + void backtrackTermDefault(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + m_backtrackingState.append(op.m_jumps); + } + + void generateAssertionBOL(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + if (m_pattern.m_multiline) { + const RegisterID character = regT0; + + JumpList matchDest; + if (!term->inputPosition) + matchDest.append(branch32(Equal, index, Imm32(m_checked))); + + readCharacter((term->inputPosition - m_checked) - 1, character); + matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass()); + op.m_jumps.append(jump()); + + matchDest.link(this); + } else { + // Erk, really should poison out these alternatives early. :-/ + if (term->inputPosition) + op.m_jumps.append(jump()); + else + op.m_jumps.append(branch32(NotEqual, index, Imm32(m_checked))); + } + } + void backtrackAssertionBOL(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generateAssertionEOL(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + if (m_pattern.m_multiline) { + const RegisterID character = regT0; + + JumpList matchDest; + if (term->inputPosition == m_checked) + matchDest.append(atEndOfInput()); + + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass()); + op.m_jumps.append(jump()); + + matchDest.link(this); + } else { + if (term->inputPosition == m_checked) + op.m_jumps.append(notAtEndOfInput()); + // Erk, really should poison out these alternatives early. :-/ + else + op.m_jumps.append(jump()); + } + } + void backtrackAssertionEOL(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + // Also falls though on nextIsNotWordChar. + void matchAssertionWordchar(size_t opIndex, JumpList& nextIsWordChar, JumpList& nextIsNotWordChar) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + + if (term->inputPosition == m_checked) + nextIsNotWordChar.append(atEndOfInput()); + + readCharacter((term->inputPosition - m_checked), character); + matchCharacterClass(character, nextIsWordChar, m_pattern.wordcharCharacterClass()); + } + + void generateAssertionWordBoundary(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + + Jump atBegin; + JumpList matchDest; + if (!term->inputPosition) + atBegin = branch32(Equal, index, Imm32(m_checked)); + readCharacter((term->inputPosition - m_checked) - 1, character); + matchCharacterClass(character, matchDest, m_pattern.wordcharCharacterClass()); + if (!term->inputPosition) + atBegin.link(this); + + // We fall through to here if the last character was not a wordchar. + JumpList nonWordCharThenWordChar; + JumpList nonWordCharThenNonWordChar; + if (term->invert()) { + matchAssertionWordchar(opIndex, nonWordCharThenNonWordChar, nonWordCharThenWordChar); + nonWordCharThenWordChar.append(jump()); + } else { + matchAssertionWordchar(opIndex, nonWordCharThenWordChar, nonWordCharThenNonWordChar); + nonWordCharThenNonWordChar.append(jump()); + } + op.m_jumps.append(nonWordCharThenNonWordChar); + + // We jump here if the last character was a wordchar. + matchDest.link(this); + JumpList wordCharThenWordChar; + JumpList wordCharThenNonWordChar; + if (term->invert()) { + matchAssertionWordchar(opIndex, wordCharThenNonWordChar, wordCharThenWordChar); + wordCharThenWordChar.append(jump()); + } else { + matchAssertionWordchar(opIndex, wordCharThenWordChar, wordCharThenNonWordChar); + // This can fall-though! + } + + op.m_jumps.append(wordCharThenWordChar); + + nonWordCharThenWordChar.link(this); + wordCharThenNonWordChar.link(this); + } + void backtrackAssertionWordBoundary(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generatePatternCharacterOnce(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + + if (op.m_isDeadCode) + return; + + // m_ops always ends with a OpBodyAlternativeEnd or OpMatchFailed + // node, so there must always be at least one more node. + ASSERT(opIndex + 1 < m_ops.size()); + YarrOp* nextOp = &m_ops[opIndex + 1]; + + PatternTerm* term = op.m_term; + UChar ch = term->patternCharacter; + + if ((ch > 0xff) && (m_charSize == Char8)) { + // Have a 16 bit pattern character and an 8 bit string - short circuit + op.m_jumps.append(jump()); + return; + } + + const RegisterID character = regT0; + int maxCharactersAtOnce = m_charSize == Char8 ? 4 : 2; + unsigned ignoreCaseMask = 0; + int allCharacters = ch; + int numberCharacters; + int startTermPosition = term->inputPosition; + + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); + + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) + ignoreCaseMask |= 32; + + for (numberCharacters = 1; numberCharacters < maxCharactersAtOnce && nextOp->m_op == OpTerm; ++numberCharacters, nextOp = &m_ops[opIndex + numberCharacters]) { + PatternTerm* nextTerm = nextOp->m_term; + + if (nextTerm->type != PatternTerm::TypePatternCharacter + || nextTerm->quantityType != QuantifierFixedCount + || nextTerm->quantityCount != 1 + || nextTerm->inputPosition != (startTermPosition + numberCharacters)) + break; + + nextOp->m_isDeadCode = true; + + int shiftAmount = (m_charSize == Char8 ? 8 : 16) * numberCharacters; + + UChar currentCharacter = nextTerm->patternCharacter; + + if ((currentCharacter > 0xff) && (m_charSize == Char8)) { + // Have a 16 bit pattern character and an 8 bit string - short circuit + op.m_jumps.append(jump()); + return; + } + + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(currentCharacter) || isCanonicallyUnique(currentCharacter)); + + allCharacters |= (currentCharacter << shiftAmount); + + if ((m_pattern.m_ignoreCase) && (isASCIIAlpha(currentCharacter))) + ignoreCaseMask |= 32 << shiftAmount; + } + + if (m_charSize == Char8) { + switch (numberCharacters) { + case 1: + op.m_jumps.append(jumpIfCharNotEquals(ch, startTermPosition - m_checked, character)); + return; + case 2: { + BaseIndex address(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); + load16Unaligned(address, character); + break; + } + case 3: { + BaseIndex highAddress(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); + load16Unaligned(highAddress, character); + if (ignoreCaseMask) + or32(Imm32(ignoreCaseMask), character); + op.m_jumps.append(branch32(NotEqual, character, Imm32((allCharacters & 0xffff) | ignoreCaseMask))); + op.m_jumps.append(jumpIfCharNotEquals(allCharacters >> 16, startTermPosition + 2 - m_checked, character)); + return; + } + case 4: { + BaseIndex address(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); + load32WithUnalignedHalfWords(address, character); + break; + } + } + } else { + switch (numberCharacters) { + case 1: + op.m_jumps.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); + return; + case 2: + BaseIndex address(input, index, TimesTwo, (term->inputPosition - m_checked) * sizeof(UChar)); + load32WithUnalignedHalfWords(address, character); + break; + } + } + + if (ignoreCaseMask) + or32(Imm32(ignoreCaseMask), character); + op.m_jumps.append(branch32(NotEqual, character, Imm32(allCharacters | ignoreCaseMask))); + return; + } + void backtrackPatternCharacterOnce(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generatePatternCharacterFixed(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + UChar ch = term->patternCharacter; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + move(index, countRegister); + sub32(Imm32(term->quantityCount.unsafeGet()), countRegister); + + Label loop(this); + BaseIndex address(input, countRegister, m_charScale, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(m_charSize == Char8 ? sizeof(char) : sizeof(UChar))).unsafeGet()); + + if (m_charSize == Char8) + load8(address, character); + else + load16(address, character); + + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { + or32(TrustedImm32(0x20), character); + ch |= 0x20; + } + + op.m_jumps.append(branch32(NotEqual, character, Imm32(ch))); + add32(TrustedImm32(1), countRegister); + branch32(NotEqual, countRegister, index).linkTo(loop, this); + } + void backtrackPatternCharacterFixed(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generatePatternCharacterGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + UChar ch = term->patternCharacter; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + move(TrustedImm32(0), countRegister); + + // Unless have a 16 bit pattern character and an 8 bit string - short circuit + if (!((ch > 0xff) && (m_charSize == Char8))) { + JumpList failures; + Label loop(this); + failures.append(atEndOfInput()); + failures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); + + add32(TrustedImm32(1), countRegister); + add32(TrustedImm32(1), index); + if (term->quantityCount == quantifyInfinite) + jump(loop); + else + branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this); + + failures.link(this); + } + op.m_reentry = label(); + + storeToFrame(countRegister, term->frameLocation); + } + void backtrackPatternCharacterGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID countRegister = regT1; + + m_backtrackingState.link(this); + + loadFromFrame(term->frameLocation, countRegister); + m_backtrackingState.append(branchTest32(Zero, countRegister)); + sub32(TrustedImm32(1), countRegister); + sub32(TrustedImm32(1), index); + jump(op.m_reentry); + } + + void generatePatternCharacterNonGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID countRegister = regT1; + + move(TrustedImm32(0), countRegister); + op.m_reentry = label(); + storeToFrame(countRegister, term->frameLocation); + } + void backtrackPatternCharacterNonGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + UChar ch = term->patternCharacter; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + m_backtrackingState.link(this); + + loadFromFrame(term->frameLocation, countRegister); + + // Unless have a 16 bit pattern character and an 8 bit string - short circuit + if (!((ch > 0xff) && (m_charSize == Char8))) { + JumpList nonGreedyFailures; + nonGreedyFailures.append(atEndOfInput()); + if (term->quantityCount != quantifyInfinite) + nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet()))); + nonGreedyFailures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); + + add32(TrustedImm32(1), countRegister); + add32(TrustedImm32(1), index); + + jump(op.m_reentry); + nonGreedyFailures.link(this); + } + + sub32(countRegister, index); + m_backtrackingState.fallthrough(); + } + + void generateCharacterClassOnce(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + + JumpList matchDest; + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, matchDest, term->characterClass); + + if (term->invert()) + op.m_jumps.append(matchDest); + else { + op.m_jumps.append(jump()); + matchDest.link(this); + } + } + void backtrackCharacterClassOnce(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generateCharacterClassFixed(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + move(index, countRegister); + sub32(Imm32(term->quantityCount.unsafeGet()), countRegister); + + Label loop(this); + JumpList matchDest; + if (m_charSize == Char8) + load8(BaseIndex(input, countRegister, TimesOne, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(sizeof(char))).unsafeGet()), character); + else + load16(BaseIndex(input, countRegister, TimesTwo, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(sizeof(UChar))).unsafeGet()), character); + matchCharacterClass(character, matchDest, term->characterClass); + + if (term->invert()) + op.m_jumps.append(matchDest); + else { + op.m_jumps.append(jump()); + matchDest.link(this); + } + + add32(TrustedImm32(1), countRegister); + branch32(NotEqual, countRegister, index).linkTo(loop, this); + } + void backtrackCharacterClassFixed(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generateCharacterClassGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + move(TrustedImm32(0), countRegister); + + JumpList failures; + Label loop(this); + failures.append(atEndOfInput()); + + if (term->invert()) { + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, failures, term->characterClass); + } else { + JumpList matchDest; + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, matchDest, term->characterClass); + failures.append(jump()); + matchDest.link(this); + } + + add32(TrustedImm32(1), countRegister); + add32(TrustedImm32(1), index); + if (term->quantityCount != quantifyInfinite) { + branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this); + failures.append(jump()); + } else + jump(loop); + + failures.link(this); + op.m_reentry = label(); + + storeToFrame(countRegister, term->frameLocation); + } + void backtrackCharacterClassGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID countRegister = regT1; + + m_backtrackingState.link(this); + + loadFromFrame(term->frameLocation, countRegister); + m_backtrackingState.append(branchTest32(Zero, countRegister)); + sub32(TrustedImm32(1), countRegister); + sub32(TrustedImm32(1), index); + jump(op.m_reentry); + } + + void generateCharacterClassNonGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID countRegister = regT1; + + move(TrustedImm32(0), countRegister); + op.m_reentry = label(); + storeToFrame(countRegister, term->frameLocation); + } + void backtrackCharacterClassNonGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + JumpList nonGreedyFailures; + + m_backtrackingState.link(this); + + loadFromFrame(term->frameLocation, countRegister); + + nonGreedyFailures.append(atEndOfInput()); + nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet()))); + + JumpList matchDest; + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, matchDest, term->characterClass); + + if (term->invert()) + nonGreedyFailures.append(matchDest); + else { + nonGreedyFailures.append(jump()); + matchDest.link(this); + } + + add32(TrustedImm32(1), countRegister); + add32(TrustedImm32(1), index); + + jump(op.m_reentry); + + nonGreedyFailures.link(this); + sub32(countRegister, index); + m_backtrackingState.fallthrough(); + } + + void generateDotStarEnclosure(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + const RegisterID matchPos = regT1; + + JumpList foundBeginningNewLine; + JumpList saveStartIndex; + JumpList foundEndingNewLine; + + ASSERT(!m_pattern.m_body->m_hasFixedSize); + getMatchStart(matchPos); + + saveStartIndex.append(branchTest32(Zero, matchPos)); + Label findBOLLoop(this); + sub32(TrustedImm32(1), matchPos); + if (m_charSize == Char8) + load8(BaseIndex(input, matchPos, TimesOne, 0), character); + else + load16(BaseIndex(input, matchPos, TimesTwo, 0), character); + matchCharacterClass(character, foundBeginningNewLine, m_pattern.newlineCharacterClass()); + branchTest32(NonZero, matchPos).linkTo(findBOLLoop, this); + saveStartIndex.append(jump()); + + foundBeginningNewLine.link(this); + add32(TrustedImm32(1), matchPos); // Advance past newline + saveStartIndex.link(this); + + if (!m_pattern.m_multiline && term->anchors.bolAnchor) + op.m_jumps.append(branchTest32(NonZero, matchPos)); + + ASSERT(!m_pattern.m_body->m_hasFixedSize); + setMatchStart(matchPos); + + move(index, matchPos); + + Label findEOLLoop(this); + foundEndingNewLine.append(branch32(Equal, matchPos, length)); + if (m_charSize == Char8) + load8(BaseIndex(input, matchPos, TimesOne, 0), character); + else + load16(BaseIndex(input, matchPos, TimesTwo, 0), character); + matchCharacterClass(character, foundEndingNewLine, m_pattern.newlineCharacterClass()); + add32(TrustedImm32(1), matchPos); + jump(findEOLLoop); + + foundEndingNewLine.link(this); + + if (!m_pattern.m_multiline && term->anchors.eolAnchor) + op.m_jumps.append(branch32(NotEqual, matchPos, length)); + + move(matchPos, index); + } + + void backtrackDotStarEnclosure(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + // Code generation/backtracking for simple terms + // (pattern characters, character classes, and assertions). + // These methods farm out work to the set of functions above. + void generateTerm(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + switch (term->type) { + case PatternTerm::TypePatternCharacter: + switch (term->quantityType) { + case QuantifierFixedCount: + if (term->quantityCount == 1) + generatePatternCharacterOnce(opIndex); + else + generatePatternCharacterFixed(opIndex); + break; + case QuantifierGreedy: + generatePatternCharacterGreedy(opIndex); + break; + case QuantifierNonGreedy: + generatePatternCharacterNonGreedy(opIndex); + break; + } + break; + + case PatternTerm::TypeCharacterClass: + switch (term->quantityType) { + case QuantifierFixedCount: + if (term->quantityCount == 1) + generateCharacterClassOnce(opIndex); + else + generateCharacterClassFixed(opIndex); + break; + case QuantifierGreedy: + generateCharacterClassGreedy(opIndex); + break; + case QuantifierNonGreedy: + generateCharacterClassNonGreedy(opIndex); + break; + } + break; + + case PatternTerm::TypeAssertionBOL: + generateAssertionBOL(opIndex); + break; + + case PatternTerm::TypeAssertionEOL: + generateAssertionEOL(opIndex); + break; + + case PatternTerm::TypeAssertionWordBoundary: + generateAssertionWordBoundary(opIndex); + break; + + case PatternTerm::TypeForwardReference: + break; + + case PatternTerm::TypeParenthesesSubpattern: + case PatternTerm::TypeParentheticalAssertion: + ASSERT_NOT_REACHED(); + case PatternTerm::TypeBackReference: + m_shouldFallBack = true; + break; + case PatternTerm::TypeDotStarEnclosure: + generateDotStarEnclosure(opIndex); + break; + } + } + void backtrackTerm(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + switch (term->type) { + case PatternTerm::TypePatternCharacter: + switch (term->quantityType) { + case QuantifierFixedCount: + if (term->quantityCount == 1) + backtrackPatternCharacterOnce(opIndex); + else + backtrackPatternCharacterFixed(opIndex); + break; + case QuantifierGreedy: + backtrackPatternCharacterGreedy(opIndex); + break; + case QuantifierNonGreedy: + backtrackPatternCharacterNonGreedy(opIndex); + break; + } + break; + + case PatternTerm::TypeCharacterClass: + switch (term->quantityType) { + case QuantifierFixedCount: + if (term->quantityCount == 1) + backtrackCharacterClassOnce(opIndex); + else + backtrackCharacterClassFixed(opIndex); + break; + case QuantifierGreedy: + backtrackCharacterClassGreedy(opIndex); + break; + case QuantifierNonGreedy: + backtrackCharacterClassNonGreedy(opIndex); + break; + } + break; + + case PatternTerm::TypeAssertionBOL: + backtrackAssertionBOL(opIndex); + break; + + case PatternTerm::TypeAssertionEOL: + backtrackAssertionEOL(opIndex); + break; + + case PatternTerm::TypeAssertionWordBoundary: + backtrackAssertionWordBoundary(opIndex); + break; + + case PatternTerm::TypeForwardReference: + break; + + case PatternTerm::TypeParenthesesSubpattern: + case PatternTerm::TypeParentheticalAssertion: + ASSERT_NOT_REACHED(); + + case PatternTerm::TypeDotStarEnclosure: + backtrackDotStarEnclosure(opIndex); + break; + + case PatternTerm::TypeBackReference: + m_shouldFallBack = true; + break; + } + } + + void generate() + { + // Forwards generate the matching code. + ASSERT(m_ops.size()); + size_t opIndex = 0; + + do { + YarrOp& op = m_ops[opIndex]; + switch (op.m_op) { + + case OpTerm: + generateTerm(opIndex); + break; + + // OpBodyAlternativeBegin/Next/End + // + // These nodes wrap the set of alternatives in the body of the regular expression. + // There may be either one or two chains of OpBodyAlternative nodes, one representing + // the 'once through' sequence of alternatives (if any exist), and one representing + // the repeating alternatives (again, if any exist). + // + // Upon normal entry to the Begin alternative, we will check that input is available. + // Reentry to the Begin alternative will take place after the check has taken place, + // and will assume that the input position has already been progressed as appropriate. + // + // Entry to subsequent Next/End alternatives occurs when the prior alternative has + // successfully completed a match - return a success state from JIT code. + // + // Next alternatives allow for reentry optimized to suit backtracking from its + // preceding alternative. It expects the input position to still be set to a position + // appropriate to its predecessor, and it will only perform an input check if the + // predecessor had a minimum size less than its own. + // + // In the case 'once through' expressions, the End node will also have a reentry + // point to jump to when the last alternative fails. Again, this expects the input + // position to still reflect that expected by the prior alternative. + case OpBodyAlternativeBegin: { + PatternAlternative* alternative = op.m_alternative; + + // Upon entry at the head of the set of alternatives, check if input is available + // to run the first alternative. (This progresses the input position). + op.m_jumps.append(jumpIfNoAvailableInput(alternative->m_minimumSize)); + // We will reenter after the check, and assume the input position to have been + // set as appropriate to this alternative. + op.m_reentry = label(); + + m_checked += alternative->m_minimumSize; + break; + } + case OpBodyAlternativeNext: + case OpBodyAlternativeEnd: { + PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; + PatternAlternative* alternative = op.m_alternative; + + // If we get here, the prior alternative matched - return success. + + // Adjust the stack pointer to remove the pattern's frame. + removeCallFrame(); + + // Load appropriate values into the return register and the first output + // slot, and return. In the case of pattern with a fixed size, we will + // not have yet set the value in the first + ASSERT(index != returnRegister); + if (m_pattern.m_body->m_hasFixedSize) { + move(index, returnRegister); + if (priorAlternative->m_minimumSize) + sub32(Imm32(priorAlternative->m_minimumSize), returnRegister); + if (compileMode == IncludeSubpatterns) + store32(returnRegister, output); + } else + getMatchStart(returnRegister); + if (compileMode == IncludeSubpatterns) + store32(index, Address(output, 4)); + move(index, returnRegister2); + + generateReturn(); + + // This is the divide between the tail of the prior alternative, above, and + // the head of the subsequent alternative, below. + + if (op.m_op == OpBodyAlternativeNext) { + // This is the reentry point for the Next alternative. We expect any code + // that jumps here to do so with the input position matching that of the + // PRIOR alteranative, and we will only check input availability if we + // need to progress it forwards. + op.m_reentry = label(); + if (alternative->m_minimumSize > priorAlternative->m_minimumSize) { + add32(Imm32(alternative->m_minimumSize - priorAlternative->m_minimumSize), index); + op.m_jumps.append(jumpIfNoAvailableInput()); + } else if (priorAlternative->m_minimumSize > alternative->m_minimumSize) + sub32(Imm32(priorAlternative->m_minimumSize - alternative->m_minimumSize), index); + } else if (op.m_nextOp == notFound) { + // This is the reentry point for the End of 'once through' alternatives, + // jumped to when the last alternative fails to match. + op.m_reentry = label(); + sub32(Imm32(priorAlternative->m_minimumSize), index); + } + + if (op.m_op == OpBodyAlternativeNext) + m_checked += alternative->m_minimumSize; + m_checked -= priorAlternative->m_minimumSize; + break; + } + + // OpSimpleNestedAlternativeBegin/Next/End + // OpNestedAlternativeBegin/Next/End + // + // These nodes are used to handle sets of alternatives that are nested within + // subpatterns and parenthetical assertions. The 'simple' forms are used where + // we do not need to be able to backtrack back into any alternative other than + // the last, the normal forms allow backtracking into any alternative. + // + // Each Begin/Next node is responsible for planting an input check to ensure + // sufficient input is available on entry. Next nodes additionally need to + // jump to the end - Next nodes use the End node's m_jumps list to hold this + // set of jumps. + // + // In the non-simple forms, successful alternative matches must store a + // 'return address' using a DataLabelPtr, used to store the address to jump + // to when backtracking, to get to the code for the appropriate alternative. + case OpSimpleNestedAlternativeBegin: + case OpNestedAlternativeBegin: { + PatternTerm* term = op.m_term; + PatternAlternative* alternative = op.m_alternative; + PatternDisjunction* disjunction = term->parentheses.disjunction; + + // Calculate how much input we need to check for, and if non-zero check. + op.m_checkAdjust = alternative->m_minimumSize; + if ((term->quantityType == QuantifierFixedCount) && (term->type != PatternTerm::TypeParentheticalAssertion)) + op.m_checkAdjust -= disjunction->m_minimumSize; + if (op.m_checkAdjust) + op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust)); + + m_checked += op.m_checkAdjust; + break; + } + case OpSimpleNestedAlternativeNext: + case OpNestedAlternativeNext: { + PatternTerm* term = op.m_term; + PatternAlternative* alternative = op.m_alternative; + PatternDisjunction* disjunction = term->parentheses.disjunction; + + // In the non-simple case, store a 'return address' so we can backtrack correctly. + if (op.m_op == OpNestedAlternativeNext) { + unsigned parenthesesFrameLocation = term->frameLocation; + unsigned alternativeFrameLocation = parenthesesFrameLocation; + if (term->quantityType != QuantifierFixedCount) + alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; + op.m_returnAddress = storeToFrameWithPatch(alternativeFrameLocation); + } + + if (term->quantityType != QuantifierFixedCount && !m_ops[op.m_previousOp].m_alternative->m_minimumSize) { + // If the previous alternative matched without consuming characters then + // backtrack to try to match while consumming some input. + op.m_zeroLengthMatch = branch32(Equal, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + } + + // If we reach here then the last alternative has matched - jump to the + // End node, to skip over any further alternatives. + // + // FIXME: this is logically O(N^2) (though N can be expected to be very + // small). We could avoid this either by adding an extra jump to the JIT + // data structures, or by making backtracking code that jumps to Next + // alternatives are responsible for checking that input is available (if + // we didn't need to plant the input checks, then m_jumps would be free). + YarrOp* endOp = &m_ops[op.m_nextOp]; + while (endOp->m_nextOp != notFound) { + ASSERT(endOp->m_op == OpSimpleNestedAlternativeNext || endOp->m_op == OpNestedAlternativeNext); + endOp = &m_ops[endOp->m_nextOp]; + } + ASSERT(endOp->m_op == OpSimpleNestedAlternativeEnd || endOp->m_op == OpNestedAlternativeEnd); + endOp->m_jumps.append(jump()); + + // This is the entry point for the next alternative. + op.m_reentry = label(); + + // Calculate how much input we need to check for, and if non-zero check. + op.m_checkAdjust = alternative->m_minimumSize; + if ((term->quantityType == QuantifierFixedCount) && (term->type != PatternTerm::TypeParentheticalAssertion)) + op.m_checkAdjust -= disjunction->m_minimumSize; + if (op.m_checkAdjust) + op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust)); + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked -= lastOp.m_checkAdjust; + m_checked += op.m_checkAdjust; + break; + } + case OpSimpleNestedAlternativeEnd: + case OpNestedAlternativeEnd: { + PatternTerm* term = op.m_term; + + // In the non-simple case, store a 'return address' so we can backtrack correctly. + if (op.m_op == OpNestedAlternativeEnd) { + unsigned parenthesesFrameLocation = term->frameLocation; + unsigned alternativeFrameLocation = parenthesesFrameLocation; + if (term->quantityType != QuantifierFixedCount) + alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; + op.m_returnAddress = storeToFrameWithPatch(alternativeFrameLocation); + } + + if (term->quantityType != QuantifierFixedCount && !m_ops[op.m_previousOp].m_alternative->m_minimumSize) { + // If the previous alternative matched without consuming characters then + // backtrack to try to match while consumming some input. + op.m_zeroLengthMatch = branch32(Equal, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + } + + // If this set of alternatives contains more than one alternative, + // then the Next nodes will have planted jumps to the End, and added + // them to this node's m_jumps list. + op.m_jumps.link(this); + op.m_jumps.clear(); + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked -= lastOp.m_checkAdjust; + break; + } + + // OpParenthesesSubpatternOnceBegin/End + // + // These nodes support (optionally) capturing subpatterns, that have a + // quantity count of 1 (this covers fixed once, and ?/?? quantifiers). + case OpParenthesesSubpatternOnceBegin: { + PatternTerm* term = op.m_term; + unsigned parenthesesFrameLocation = term->frameLocation; + const RegisterID indexTemporary = regT0; + ASSERT(term->quantityCount == 1); + + // Upon entry to a Greedy quantified set of parenthese store the index. + // We'll use this for two purposes: + // - To indicate which iteration we are on of mathing the remainder of + // the expression after the parentheses - the first, including the + // match within the parentheses, or the second having skipped over them. + // - To check for empty matches, which must be rejected. + // + // At the head of a NonGreedy set of parentheses we'll immediately set the + // value on the stack to -1 (indicating a match skipping the subpattern), + // and plant a jump to the end. We'll also plant a label to backtrack to + // to reenter the subpattern later, with a store to set up index on the + // second iteration. + // + // FIXME: for capturing parens, could use the index in the capture array? + if (term->quantityType == QuantifierGreedy) + storeToFrame(index, parenthesesFrameLocation); + else if (term->quantityType == QuantifierNonGreedy) { + storeToFrame(TrustedImm32(-1), parenthesesFrameLocation); + op.m_jumps.append(jump()); + op.m_reentry = label(); + storeToFrame(index, parenthesesFrameLocation); + } + + // If the parenthese are capturing, store the starting index value to the + // captures array, offsetting as necessary. + // + // FIXME: could avoid offsetting this value in JIT code, apply + // offsets only afterwards, at the point the results array is + // being accessed. + if (term->capture() && compileMode == IncludeSubpatterns) { + int inputOffset = term->inputPosition - m_checked; + if (term->quantityType == QuantifierFixedCount) + inputOffset -= term->parentheses.disjunction->m_minimumSize; + if (inputOffset) { + move(index, indexTemporary); + add32(Imm32(inputOffset), indexTemporary); + setSubpatternStart(indexTemporary, term->parentheses.subpatternId); + } else + setSubpatternStart(index, term->parentheses.subpatternId); + } + break; + } + case OpParenthesesSubpatternOnceEnd: { + PatternTerm* term = op.m_term; + const RegisterID indexTemporary = regT0; + ASSERT(term->quantityCount == 1); + +#ifndef NDEBUG + // Runtime ASSERT to make sure that the nested alternative handled the + // "no input consumed" check. + if (term->quantityType != QuantifierFixedCount && !term->parentheses.disjunction->m_minimumSize) { + Jump pastBreakpoint; + pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + breakpoint(); + pastBreakpoint.link(this); + } +#endif + + // If the parenthese are capturing, store the ending index value to the + // captures array, offsetting as necessary. + // + // FIXME: could avoid offsetting this value in JIT code, apply + // offsets only afterwards, at the point the results array is + // being accessed. + if (term->capture() && compileMode == IncludeSubpatterns) { + int inputOffset = term->inputPosition - m_checked; + if (inputOffset) { + move(index, indexTemporary); + add32(Imm32(inputOffset), indexTemporary); + setSubpatternEnd(indexTemporary, term->parentheses.subpatternId); + } else + setSubpatternEnd(index, term->parentheses.subpatternId); + } + + // If the parentheses are quantified Greedy then add a label to jump back + // to if get a failed match from after the parentheses. For NonGreedy + // parentheses, link the jump from before the subpattern to here. + if (term->quantityType == QuantifierGreedy) + op.m_reentry = label(); + else if (term->quantityType == QuantifierNonGreedy) { + YarrOp& beginOp = m_ops[op.m_previousOp]; + beginOp.m_jumps.link(this); + } + break; + } + + // OpParenthesesSubpatternTerminalBegin/End + case OpParenthesesSubpatternTerminalBegin: { + PatternTerm* term = op.m_term; + ASSERT(term->quantityType == QuantifierGreedy); + ASSERT(term->quantityCount == quantifyInfinite); + ASSERT(!term->capture()); + + // Upon entry set a label to loop back to. + op.m_reentry = label(); + + // Store the start index of the current match; we need to reject zero + // length matches. + storeToFrame(index, term->frameLocation); + break; + } + case OpParenthesesSubpatternTerminalEnd: { + YarrOp& beginOp = m_ops[op.m_previousOp]; +#ifndef NDEBUG + PatternTerm* term = op.m_term; + + // Runtime ASSERT to make sure that the nested alternative handled the + // "no input consumed" check. + Jump pastBreakpoint; + pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + breakpoint(); + pastBreakpoint.link(this); +#endif + + // We know that the match is non-zero, we can accept it and + // loop back up to the head of the subpattern. + jump(beginOp.m_reentry); + + // This is the entry point to jump to when we stop matching - we will + // do so once the subpattern cannot match any more. + op.m_reentry = label(); + break; + } + + // OpParentheticalAssertionBegin/End + case OpParentheticalAssertionBegin: { + PatternTerm* term = op.m_term; + + // Store the current index - assertions should not update index, so + // we will need to restore it upon a successful match. + unsigned parenthesesFrameLocation = term->frameLocation; + storeToFrame(index, parenthesesFrameLocation); + + // Check + op.m_checkAdjust = m_checked - term->inputPosition; + if (op.m_checkAdjust) + sub32(Imm32(op.m_checkAdjust), index); + + m_checked -= op.m_checkAdjust; + break; + } + case OpParentheticalAssertionEnd: { + PatternTerm* term = op.m_term; + + // Restore the input index value. + unsigned parenthesesFrameLocation = term->frameLocation; + loadFromFrame(parenthesesFrameLocation, index); + + // If inverted, a successful match of the assertion must be treated + // as a failure, so jump to backtracking. + if (term->invert()) { + op.m_jumps.append(jump()); + op.m_reentry = label(); + } + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked += lastOp.m_checkAdjust; + break; + } + + case OpMatchFailed: + removeCallFrame(); + move(TrustedImmPtr((void*)WTF::notFound), returnRegister); + move(TrustedImm32(0), returnRegister2); + generateReturn(); + break; + } + + ++opIndex; + } while (opIndex < m_ops.size()); + } + + void backtrack() + { + // Backwards generate the backtracking code. + size_t opIndex = m_ops.size(); + ASSERT(opIndex); + + do { + --opIndex; + YarrOp& op = m_ops[opIndex]; + switch (op.m_op) { + + case OpTerm: + backtrackTerm(opIndex); + break; + + // OpBodyAlternativeBegin/Next/End + // + // For each Begin/Next node representing an alternative, we need to decide what to do + // in two circumstances: + // - If we backtrack back into this node, from within the alternative. + // - If the input check at the head of the alternative fails (if this exists). + // + // We treat these two cases differently since in the former case we have slightly + // more information - since we are backtracking out of a prior alternative we know + // that at least enough input was available to run it. For example, given the regular + // expression /a|b/, if we backtrack out of the first alternative (a failed pattern + // character match of 'a'), then we need not perform an additional input availability + // check before running the second alternative. + // + // Backtracking required differs for the last alternative, which in the case of the + // repeating set of alternatives must loop. The code generated for the last alternative + // will also be used to handle all input check failures from any prior alternatives - + // these require similar functionality, in seeking the next available alternative for + // which there is sufficient input. + // + // Since backtracking of all other alternatives simply requires us to link backtracks + // to the reentry point for the subsequent alternative, we will only be generating any + // code when backtracking the last alternative. + case OpBodyAlternativeBegin: + case OpBodyAlternativeNext: { + PatternAlternative* alternative = op.m_alternative; + + if (op.m_op == OpBodyAlternativeNext) { + PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; + m_checked += priorAlternative->m_minimumSize; + } + m_checked -= alternative->m_minimumSize; + + // Is this the last alternative? If not, then if we backtrack to this point we just + // need to jump to try to match the next alternative. + if (m_ops[op.m_nextOp].m_op != OpBodyAlternativeEnd) { + m_backtrackingState.linkTo(m_ops[op.m_nextOp].m_reentry, this); + break; + } + YarrOp& endOp = m_ops[op.m_nextOp]; + + YarrOp* beginOp = &op; + while (beginOp->m_op != OpBodyAlternativeBegin) { + ASSERT(beginOp->m_op == OpBodyAlternativeNext); + beginOp = &m_ops[beginOp->m_previousOp]; + } + + bool onceThrough = endOp.m_nextOp == notFound; + + // First, generate code to handle cases where we backtrack out of an attempted match + // of the last alternative. If this is a 'once through' set of alternatives then we + // have nothing to do - link this straight through to the End. + if (onceThrough) + m_backtrackingState.linkTo(endOp.m_reentry, this); + else { + // If we don't need to move the input poistion, and the pattern has a fixed size + // (in which case we omit the store of the start index until the pattern has matched) + // then we can just link the backtrack out of the last alternative straight to the + // head of the first alternative. + if (m_pattern.m_body->m_hasFixedSize + && (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize) + && (alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize == 1)) + m_backtrackingState.linkTo(beginOp->m_reentry, this); + else { + // We need to generate a trampoline of code to execute before looping back + // around to the first alternative. + m_backtrackingState.link(this); + + // If the pattern size is not fixed, then store the start index, for use if we match. + if (!m_pattern.m_body->m_hasFixedSize) { + if (alternative->m_minimumSize == 1) + setMatchStart(index); + else { + move(index, regT0); + if (alternative->m_minimumSize) + sub32(Imm32(alternative->m_minimumSize - 1), regT0); + else + add32(TrustedImm32(1), regT0); + setMatchStart(regT0); + } + } + + // Generate code to loop. Check whether the last alternative is longer than the + // first (e.g. /a|xy/ or /a|xyz/). + if (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize) { + // We want to loop, and increment input position. If the delta is 1, it is + // already correctly incremented, if more than one then decrement as appropriate. + unsigned delta = alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize; + ASSERT(delta); + if (delta != 1) + sub32(Imm32(delta - 1), index); + jump(beginOp->m_reentry); + } else { + // If the first alternative has minimum size 0xFFFFFFFFu, then there cannot + // be sufficent input available to handle this, so just fall through. + unsigned delta = beginOp->m_alternative->m_minimumSize - alternative->m_minimumSize; + if (delta != 0xFFFFFFFFu) { + // We need to check input because we are incrementing the input. + add32(Imm32(delta + 1), index); + checkInput().linkTo(beginOp->m_reentry, this); + } + } + } + } + + // We can reach this point in the code in two ways: + // - Fallthrough from the code above (a repeating alternative backtracked out of its + // last alternative, and did not have sufficent input to run the first). + // - We will loop back up to the following label when a releating alternative loops, + // following a failed input check. + // + // Either way, we have just failed the input check for the first alternative. + Label firstInputCheckFailed(this); + + // Generate code to handle input check failures from alternatives except the last. + // prevOp is the alternative we're handling a bail out from (initially Begin), and + // nextOp is the alternative we will be attempting to reenter into. + // + // We will link input check failures from the forwards matching path back to the code + // that can handle them. + YarrOp* prevOp = beginOp; + YarrOp* nextOp = &m_ops[beginOp->m_nextOp]; + while (nextOp->m_op != OpBodyAlternativeEnd) { + prevOp->m_jumps.link(this); + + // We only get here if an input check fails, it is only worth checking again + // if the next alternative has a minimum size less than the last. + if (prevOp->m_alternative->m_minimumSize > nextOp->m_alternative->m_minimumSize) { + // FIXME: if we added an extra label to YarrOp, we could avoid needing to + // subtract delta back out, and reduce this code. Should performance test + // the benefit of this. + unsigned delta = prevOp->m_alternative->m_minimumSize - nextOp->m_alternative->m_minimumSize; + sub32(Imm32(delta), index); + Jump fail = jumpIfNoAvailableInput(); + add32(Imm32(delta), index); + jump(nextOp->m_reentry); + fail.link(this); + } else if (prevOp->m_alternative->m_minimumSize < nextOp->m_alternative->m_minimumSize) + add32(Imm32(nextOp->m_alternative->m_minimumSize - prevOp->m_alternative->m_minimumSize), index); + prevOp = nextOp; + nextOp = &m_ops[nextOp->m_nextOp]; + } + + // We fall through to here if there is insufficient input to run the last alternative. + + // If there is insufficient input to run the last alternative, then for 'once through' + // alternatives we are done - just jump back up into the forwards matching path at the End. + if (onceThrough) { + op.m_jumps.linkTo(endOp.m_reentry, this); + jump(endOp.m_reentry); + break; + } + + // For repeating alternatives, link any input check failure from the last alternative to + // this point. + op.m_jumps.link(this); + + bool needsToUpdateMatchStart = !m_pattern.m_body->m_hasFixedSize; + + // Check for cases where input position is already incremented by 1 for the last + // alternative (this is particularly useful where the minimum size of the body + // disjunction is 0, e.g. /a*|b/). + if (needsToUpdateMatchStart && alternative->m_minimumSize == 1) { + // index is already incremented by 1, so just store it now! + setMatchStart(index); + needsToUpdateMatchStart = false; + } + + // Check whether there is sufficient input to loop. Increment the input position by + // one, and check. Also add in the minimum disjunction size before checking - there + // is no point in looping if we're just going to fail all the input checks around + // the next iteration. + ASSERT(alternative->m_minimumSize >= m_pattern.m_body->m_minimumSize); + if (alternative->m_minimumSize == m_pattern.m_body->m_minimumSize) { + // If the last alternative had the same minimum size as the disjunction, + // just simply increment input pos by 1, no adjustment based on minimum size. + add32(TrustedImm32(1), index); + } else { + // If the minumum for the last alternative was one greater than than that + // for the disjunction, we're already progressed by 1, nothing to do! + unsigned delta = (alternative->m_minimumSize - m_pattern.m_body->m_minimumSize) - 1; + if (delta) + sub32(Imm32(delta), index); + } + Jump matchFailed = jumpIfNoAvailableInput(); + + if (needsToUpdateMatchStart) { + if (!m_pattern.m_body->m_minimumSize) + setMatchStart(index); + else { + move(index, regT0); + sub32(Imm32(m_pattern.m_body->m_minimumSize), regT0); + setMatchStart(regT0); + } + } + + // Calculate how much more input the first alternative requires than the minimum + // for the body as a whole. If no more is needed then we dont need an additional + // input check here - jump straight back up to the start of the first alternative. + if (beginOp->m_alternative->m_minimumSize == m_pattern.m_body->m_minimumSize) + jump(beginOp->m_reentry); + else { + if (beginOp->m_alternative->m_minimumSize > m_pattern.m_body->m_minimumSize) + add32(Imm32(beginOp->m_alternative->m_minimumSize - m_pattern.m_body->m_minimumSize), index); + else + sub32(Imm32(m_pattern.m_body->m_minimumSize - beginOp->m_alternative->m_minimumSize), index); + checkInput().linkTo(beginOp->m_reentry, this); + jump(firstInputCheckFailed); + } + + // We jump to here if we iterate to the point that there is insufficient input to + // run any matches, and need to return a failure state from JIT code. + matchFailed.link(this); + + removeCallFrame(); + move(TrustedImmPtr((void*)WTF::notFound), returnRegister); + move(TrustedImm32(0), returnRegister2); + generateReturn(); + break; + } + case OpBodyAlternativeEnd: { + // We should never backtrack back into a body disjunction. + ASSERT(m_backtrackingState.isEmpty()); + + PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; + m_checked += priorAlternative->m_minimumSize; + break; + } + + // OpSimpleNestedAlternativeBegin/Next/End + // OpNestedAlternativeBegin/Next/End + // + // Generate code for when we backtrack back out of an alternative into + // a Begin or Next node, or when the entry input count check fails. If + // there are more alternatives we need to jump to the next alternative, + // if not we backtrack back out of the current set of parentheses. + // + // In the case of non-simple nested assertions we need to also link the + // 'return address' appropriately to backtrack back out into the correct + // alternative. + case OpSimpleNestedAlternativeBegin: + case OpSimpleNestedAlternativeNext: + case OpNestedAlternativeBegin: + case OpNestedAlternativeNext: { + YarrOp& nextOp = m_ops[op.m_nextOp]; + bool isBegin = op.m_previousOp == notFound; + bool isLastAlternative = nextOp.m_nextOp == notFound; + ASSERT(isBegin == (op.m_op == OpSimpleNestedAlternativeBegin || op.m_op == OpNestedAlternativeBegin)); + ASSERT(isLastAlternative == (nextOp.m_op == OpSimpleNestedAlternativeEnd || nextOp.m_op == OpNestedAlternativeEnd)); + + // Treat an input check failure the same as a failed match. + m_backtrackingState.append(op.m_jumps); + + // Set the backtracks to jump to the appropriate place. We may need + // to link the backtracks in one of three different way depending on + // the type of alternative we are dealing with: + // - A single alternative, with no simplings. + // - The last alternative of a set of two or more. + // - An alternative other than the last of a set of two or more. + // + // In the case of a single alternative on its own, we don't need to + // jump anywhere - if the alternative fails to match we can just + // continue to backtrack out of the parentheses without jumping. + // + // In the case of the last alternative in a set of more than one, we + // need to jump to return back out to the beginning. We'll do so by + // adding a jump to the End node's m_jumps list, and linking this + // when we come to generate the Begin node. For alternatives other + // than the last, we need to jump to the next alternative. + // + // If the alternative had adjusted the input position we must link + // backtracking to here, correct, and then jump on. If not we can + // link the backtracks directly to their destination. + if (op.m_checkAdjust) { + // Handle the cases where we need to link the backtracks here. + m_backtrackingState.link(this); + sub32(Imm32(op.m_checkAdjust), index); + if (!isLastAlternative) { + // An alternative that is not the last should jump to its successor. + jump(nextOp.m_reentry); + } else if (!isBegin) { + // The last of more than one alternatives must jump back to the beginning. + nextOp.m_jumps.append(jump()); + } else { + // A single alternative on its own can fall through. + m_backtrackingState.fallthrough(); + } + } else { + // Handle the cases where we can link the backtracks directly to their destinations. + if (!isLastAlternative) { + // An alternative that is not the last should jump to its successor. + m_backtrackingState.linkTo(nextOp.m_reentry, this); + } else if (!isBegin) { + // The last of more than one alternatives must jump back to the beginning. + m_backtrackingState.takeBacktracksToJumpList(nextOp.m_jumps, this); + } + // In the case of a single alternative on its own do nothing - it can fall through. + } + + // If there is a backtrack jump from a zero length match link it here. + if (op.m_zeroLengthMatch.isSet()) + m_backtrackingState.append(op.m_zeroLengthMatch); + + // At this point we've handled the backtracking back into this node. + // Now link any backtracks that need to jump to here. + + // For non-simple alternatives, link the alternative's 'return address' + // so that we backtrack back out into the previous alternative. + if (op.m_op == OpNestedAlternativeNext) + m_backtrackingState.append(op.m_returnAddress); + + // If there is more than one alternative, then the last alternative will + // have planted a jump to be linked to the end. This jump was added to the + // End node's m_jumps list. If we are back at the beginning, link it here. + if (isBegin) { + YarrOp* endOp = &m_ops[op.m_nextOp]; + while (endOp->m_nextOp != notFound) { + ASSERT(endOp->m_op == OpSimpleNestedAlternativeNext || endOp->m_op == OpNestedAlternativeNext); + endOp = &m_ops[endOp->m_nextOp]; + } + ASSERT(endOp->m_op == OpSimpleNestedAlternativeEnd || endOp->m_op == OpNestedAlternativeEnd); + m_backtrackingState.append(endOp->m_jumps); + } + + if (!isBegin) { + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked += lastOp.m_checkAdjust; + } + m_checked -= op.m_checkAdjust; + break; + } + case OpSimpleNestedAlternativeEnd: + case OpNestedAlternativeEnd: { + PatternTerm* term = op.m_term; + + // If there is a backtrack jump from a zero length match link it here. + if (op.m_zeroLengthMatch.isSet()) + m_backtrackingState.append(op.m_zeroLengthMatch); + + // If we backtrack into the end of a simple subpattern do nothing; + // just continue through into the last alternative. If we backtrack + // into the end of a non-simple set of alterntives we need to jump + // to the backtracking return address set up during generation. + if (op.m_op == OpNestedAlternativeEnd) { + m_backtrackingState.link(this); + + // Plant a jump to the return address. + unsigned parenthesesFrameLocation = term->frameLocation; + unsigned alternativeFrameLocation = parenthesesFrameLocation; + if (term->quantityType != QuantifierFixedCount) + alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; + loadFromFrameAndJump(alternativeFrameLocation); + + // Link the DataLabelPtr associated with the end of the last + // alternative to this point. + m_backtrackingState.append(op.m_returnAddress); + } + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked += lastOp.m_checkAdjust; + break; + } + + // OpParenthesesSubpatternOnceBegin/End + // + // When we are backtracking back out of a capturing subpattern we need + // to clear the start index in the matches output array, to record that + // this subpattern has not been captured. + // + // When backtracking back out of a Greedy quantified subpattern we need + // to catch this, and try running the remainder of the alternative after + // the subpattern again, skipping the parentheses. + // + // Upon backtracking back into a quantified set of parentheses we need to + // check whether we were currently skipping the subpattern. If not, we + // can backtrack into them, if we were we need to either backtrack back + // out of the start of the parentheses, or jump back to the forwards + // matching start, depending of whether the match is Greedy or NonGreedy. + case OpParenthesesSubpatternOnceBegin: { + PatternTerm* term = op.m_term; + ASSERT(term->quantityCount == 1); + + // We only need to backtrack to thispoint if capturing or greedy. + if ((term->capture() && compileMode == IncludeSubpatterns) || term->quantityType == QuantifierGreedy) { + m_backtrackingState.link(this); + + // If capturing, clear the capture (we only need to reset start). + if (term->capture() && compileMode == IncludeSubpatterns) + clearSubpatternStart(term->parentheses.subpatternId); + + // If Greedy, jump to the end. + if (term->quantityType == QuantifierGreedy) { + // Clear the flag in the stackframe indicating we ran through the subpattern. + unsigned parenthesesFrameLocation = term->frameLocation; + storeToFrame(TrustedImm32(-1), parenthesesFrameLocation); + // Jump to after the parentheses, skipping the subpattern. + jump(m_ops[op.m_nextOp].m_reentry); + // A backtrack from after the parentheses, when skipping the subpattern, + // will jump back to here. + op.m_jumps.link(this); + } + + m_backtrackingState.fallthrough(); + } + break; + } + case OpParenthesesSubpatternOnceEnd: { + PatternTerm* term = op.m_term; + + if (term->quantityType != QuantifierFixedCount) { + m_backtrackingState.link(this); + + // Check whether we should backtrack back into the parentheses, or if we + // are currently in a state where we had skipped over the subpattern + // (in which case the flag value on the stack will be -1). + unsigned parenthesesFrameLocation = term->frameLocation; + Jump hadSkipped = branch32(Equal, Address(stackPointerRegister, parenthesesFrameLocation * sizeof(void*)), TrustedImm32(-1)); + + if (term->quantityType == QuantifierGreedy) { + // For Greedy parentheses, we skip after having already tried going + // through the subpattern, so if we get here we're done. + YarrOp& beginOp = m_ops[op.m_previousOp]; + beginOp.m_jumps.append(hadSkipped); + } else { + // For NonGreedy parentheses, we try skipping the subpattern first, + // so if we get here we need to try running through the subpattern + // next. Jump back to the start of the parentheses in the forwards + // matching path. + ASSERT(term->quantityType == QuantifierNonGreedy); + YarrOp& beginOp = m_ops[op.m_previousOp]; + hadSkipped.linkTo(beginOp.m_reentry, this); + } + + m_backtrackingState.fallthrough(); + } + + m_backtrackingState.append(op.m_jumps); + break; + } + + // OpParenthesesSubpatternTerminalBegin/End + // + // Terminal subpatterns will always match - there is nothing after them to + // force a backtrack, and they have a minimum count of 0, and as such will + // always produce an acceptable result. + case OpParenthesesSubpatternTerminalBegin: { + // We will backtrack to this point once the subpattern cannot match any + // more. Since no match is accepted as a successful match (we are Greedy + // quantified with a minimum of zero) jump back to the forwards matching + // path at the end. + YarrOp& endOp = m_ops[op.m_nextOp]; + m_backtrackingState.linkTo(endOp.m_reentry, this); + break; + } + case OpParenthesesSubpatternTerminalEnd: + // We should never be backtracking to here (hence the 'terminal' in the name). + ASSERT(m_backtrackingState.isEmpty()); + m_backtrackingState.append(op.m_jumps); + break; + + // OpParentheticalAssertionBegin/End + case OpParentheticalAssertionBegin: { + PatternTerm* term = op.m_term; + YarrOp& endOp = m_ops[op.m_nextOp]; + + // We need to handle the backtracks upon backtracking back out + // of a parenthetical assertion if either we need to correct + // the input index, or the assertion was inverted. + if (op.m_checkAdjust || term->invert()) { + m_backtrackingState.link(this); + + if (op.m_checkAdjust) + add32(Imm32(op.m_checkAdjust), index); + + // In an inverted assertion failure to match the subpattern + // is treated as a successful match - jump to the end of the + // subpattern. We already have adjusted the input position + // back to that before the assertion, which is correct. + if (term->invert()) + jump(endOp.m_reentry); + + m_backtrackingState.fallthrough(); + } + + // The End node's jump list will contain any backtracks into + // the end of the assertion. Also, if inverted, we will have + // added the failure caused by a successful match to this. + m_backtrackingState.append(endOp.m_jumps); + + m_checked += op.m_checkAdjust; + break; + } + case OpParentheticalAssertionEnd: { + // FIXME: We should really be clearing any nested subpattern + // matches on bailing out from after the pattern. Firefox has + // this bug too (presumably because they use YARR!) + + // Never backtrack into an assertion; later failures bail to before the begin. + m_backtrackingState.takeBacktracksToJumpList(op.m_jumps, this); + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked -= lastOp.m_checkAdjust; + break; + } + + case OpMatchFailed: + break; + } + + } while (opIndex); + } + + // Compilation methods: + // ==================== + + // opCompileParenthesesSubpattern + // Emits ops for a subpattern (set of parentheses). These consist + // of a set of alternatives wrapped in an outer set of nodes for + // the parentheses. + // Supported types of parentheses are 'Once' (quantityCount == 1) + // and 'Terminal' (non-capturing parentheses quantified as greedy + // and infinite). + // Alternatives will use the 'Simple' set of ops if either the + // subpattern is terminal (in which case we will never need to + // backtrack), or if the subpattern only contains one alternative. + void opCompileParenthesesSubpattern(PatternTerm* term) + { + YarrOpCode parenthesesBeginOpCode; + YarrOpCode parenthesesEndOpCode; + YarrOpCode alternativeBeginOpCode = OpSimpleNestedAlternativeBegin; + YarrOpCode alternativeNextOpCode = OpSimpleNestedAlternativeNext; + YarrOpCode alternativeEndOpCode = OpSimpleNestedAlternativeEnd; + + // We can currently only compile quantity 1 subpatterns that are + // not copies. We generate a copy in the case of a range quantifier, + // e.g. /(?:x){3,9}/, or /(?:x)+/ (These are effectively expanded to + // /(?:x){3,3}(?:x){0,6}/ and /(?:x)(?:x)*/ repectively). The problem + // comes where the subpattern is capturing, in which case we would + // need to restore the capture from the first subpattern upon a + // failure in the second. + if (term->quantityCount == 1 && !term->parentheses.isCopy) { + // Select the 'Once' nodes. + parenthesesBeginOpCode = OpParenthesesSubpatternOnceBegin; + parenthesesEndOpCode = OpParenthesesSubpatternOnceEnd; + + // If there is more than one alternative we cannot use the 'simple' nodes. + if (term->parentheses.disjunction->m_alternatives.size() != 1) { + alternativeBeginOpCode = OpNestedAlternativeBegin; + alternativeNextOpCode = OpNestedAlternativeNext; + alternativeEndOpCode = OpNestedAlternativeEnd; + } + } else if (term->parentheses.isTerminal) { + // Select the 'Terminal' nodes. + parenthesesBeginOpCode = OpParenthesesSubpatternTerminalBegin; + parenthesesEndOpCode = OpParenthesesSubpatternTerminalEnd; + } else { + // This subpattern is not supported by the JIT. + m_shouldFallBack = true; + return; + } + + size_t parenBegin = m_ops.size(); + m_ops.append(parenthesesBeginOpCode); + + m_ops.append(alternativeBeginOpCode); + m_ops.last().m_previousOp = notFound; + m_ops.last().m_term = term; + Vector& alternatives = term->parentheses.disjunction->m_alternatives; + for (unsigned i = 0; i < alternatives.size(); ++i) { + size_t lastOpIndex = m_ops.size() - 1; + + PatternAlternative* nestedAlternative = alternatives[i]; + opCompileAlternative(nestedAlternative); + + size_t thisOpIndex = m_ops.size(); + m_ops.append(YarrOp(alternativeNextOpCode)); + + YarrOp& lastOp = m_ops[lastOpIndex]; + YarrOp& thisOp = m_ops[thisOpIndex]; + + lastOp.m_alternative = nestedAlternative; + lastOp.m_nextOp = thisOpIndex; + thisOp.m_previousOp = lastOpIndex; + thisOp.m_term = term; + } + YarrOp& lastOp = m_ops.last(); + ASSERT(lastOp.m_op == alternativeNextOpCode); + lastOp.m_op = alternativeEndOpCode; + lastOp.m_alternative = 0; + lastOp.m_nextOp = notFound; + + size_t parenEnd = m_ops.size(); + m_ops.append(parenthesesEndOpCode); + + m_ops[parenBegin].m_term = term; + m_ops[parenBegin].m_previousOp = notFound; + m_ops[parenBegin].m_nextOp = parenEnd; + m_ops[parenEnd].m_term = term; + m_ops[parenEnd].m_previousOp = parenBegin; + m_ops[parenEnd].m_nextOp = notFound; + } + + // opCompileParentheticalAssertion + // Emits ops for a parenthetical assertion. These consist of an + // OpSimpleNestedAlternativeBegin/Next/End set of nodes wrapping + // the alternatives, with these wrapped by an outer pair of + // OpParentheticalAssertionBegin/End nodes. + // We can always use the OpSimpleNestedAlternative nodes in the + // case of parenthetical assertions since these only ever match + // once, and will never backtrack back into the assertion. + void opCompileParentheticalAssertion(PatternTerm* term) + { + size_t parenBegin = m_ops.size(); + m_ops.append(OpParentheticalAssertionBegin); + + m_ops.append(OpSimpleNestedAlternativeBegin); + m_ops.last().m_previousOp = notFound; + m_ops.last().m_term = term; + Vector& alternatives = term->parentheses.disjunction->m_alternatives; + for (unsigned i = 0; i < alternatives.size(); ++i) { + size_t lastOpIndex = m_ops.size() - 1; + + PatternAlternative* nestedAlternative = alternatives[i]; + opCompileAlternative(nestedAlternative); + + size_t thisOpIndex = m_ops.size(); + m_ops.append(YarrOp(OpSimpleNestedAlternativeNext)); + + YarrOp& lastOp = m_ops[lastOpIndex]; + YarrOp& thisOp = m_ops[thisOpIndex]; + + lastOp.m_alternative = nestedAlternative; + lastOp.m_nextOp = thisOpIndex; + thisOp.m_previousOp = lastOpIndex; + thisOp.m_term = term; + } + YarrOp& lastOp = m_ops.last(); + ASSERT(lastOp.m_op == OpSimpleNestedAlternativeNext); + lastOp.m_op = OpSimpleNestedAlternativeEnd; + lastOp.m_alternative = 0; + lastOp.m_nextOp = notFound; + + size_t parenEnd = m_ops.size(); + m_ops.append(OpParentheticalAssertionEnd); + + m_ops[parenBegin].m_term = term; + m_ops[parenBegin].m_previousOp = notFound; + m_ops[parenBegin].m_nextOp = parenEnd; + m_ops[parenEnd].m_term = term; + m_ops[parenEnd].m_previousOp = parenBegin; + m_ops[parenEnd].m_nextOp = notFound; + } + + // opCompileAlternative + // Called to emit nodes for all terms in an alternative. + void opCompileAlternative(PatternAlternative* alternative) + { + optimizeAlternative(alternative); + + for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { + PatternTerm* term = &alternative->m_terms[i]; + + switch (term->type) { + case PatternTerm::TypeParenthesesSubpattern: + opCompileParenthesesSubpattern(term); + break; + + case PatternTerm::TypeParentheticalAssertion: + opCompileParentheticalAssertion(term); + break; + + default: + m_ops.append(term); + } + } + } + + // opCompileBody + // This method compiles the body disjunction of the regular expression. + // The body consists of two sets of alternatives - zero or more 'once + // through' (BOL anchored) alternatives, followed by zero or more + // repeated alternatives. + // For each of these two sets of alteratives, if not empty they will be + // wrapped in a set of OpBodyAlternativeBegin/Next/End nodes (with the + // 'begin' node referencing the first alternative, and 'next' nodes + // referencing any further alternatives. The begin/next/end nodes are + // linked together in a doubly linked list. In the case of repeating + // alternatives, the end node is also linked back to the beginning. + // If no repeating alternatives exist, then a OpMatchFailed node exists + // to return the failing result. + void opCompileBody(PatternDisjunction* disjunction) + { + Vector& alternatives = disjunction->m_alternatives; + size_t currentAlternativeIndex = 0; + + // Emit the 'once through' alternatives. + if (alternatives.size() && alternatives[0]->onceThrough()) { + m_ops.append(YarrOp(OpBodyAlternativeBegin)); + m_ops.last().m_previousOp = notFound; + + do { + size_t lastOpIndex = m_ops.size() - 1; + PatternAlternative* alternative = alternatives[currentAlternativeIndex]; + opCompileAlternative(alternative); + + size_t thisOpIndex = m_ops.size(); + m_ops.append(YarrOp(OpBodyAlternativeNext)); + + YarrOp& lastOp = m_ops[lastOpIndex]; + YarrOp& thisOp = m_ops[thisOpIndex]; + + lastOp.m_alternative = alternative; + lastOp.m_nextOp = thisOpIndex; + thisOp.m_previousOp = lastOpIndex; + + ++currentAlternativeIndex; + } while (currentAlternativeIndex < alternatives.size() && alternatives[currentAlternativeIndex]->onceThrough()); + + YarrOp& lastOp = m_ops.last(); + + ASSERT(lastOp.m_op == OpBodyAlternativeNext); + lastOp.m_op = OpBodyAlternativeEnd; + lastOp.m_alternative = 0; + lastOp.m_nextOp = notFound; + } + + if (currentAlternativeIndex == alternatives.size()) { + m_ops.append(YarrOp(OpMatchFailed)); + return; + } + + // Emit the repeated alternatives. + size_t repeatLoop = m_ops.size(); + m_ops.append(YarrOp(OpBodyAlternativeBegin)); + m_ops.last().m_previousOp = notFound; + do { + size_t lastOpIndex = m_ops.size() - 1; + PatternAlternative* alternative = alternatives[currentAlternativeIndex]; + ASSERT(!alternative->onceThrough()); + opCompileAlternative(alternative); + + size_t thisOpIndex = m_ops.size(); + m_ops.append(YarrOp(OpBodyAlternativeNext)); + + YarrOp& lastOp = m_ops[lastOpIndex]; + YarrOp& thisOp = m_ops[thisOpIndex]; + + lastOp.m_alternative = alternative; + lastOp.m_nextOp = thisOpIndex; + thisOp.m_previousOp = lastOpIndex; + + ++currentAlternativeIndex; + } while (currentAlternativeIndex < alternatives.size()); + YarrOp& lastOp = m_ops.last(); + ASSERT(lastOp.m_op == OpBodyAlternativeNext); + lastOp.m_op = OpBodyAlternativeEnd; + lastOp.m_alternative = 0; + lastOp.m_nextOp = repeatLoop; + } + + void generateEnter() + { +#if CPU(X86_64) + push(X86Registers::ebp); + move(stackPointerRegister, X86Registers::ebp); + push(X86Registers::ebx); +#elif CPU(X86) + push(X86Registers::ebp); + move(stackPointerRegister, X86Registers::ebp); + // TODO: do we need spill registers to fill the output pointer if there are no sub captures? + push(X86Registers::ebx); + push(X86Registers::edi); + push(X86Registers::esi); + // load output into edi (2 = saved ebp + return address). + #if COMPILER(MSVC) + loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), input); + loadPtr(Address(X86Registers::ebp, 3 * sizeof(void*)), index); + loadPtr(Address(X86Registers::ebp, 4 * sizeof(void*)), length); + if (compileMode == IncludeSubpatterns) + loadPtr(Address(X86Registers::ebp, 5 * sizeof(void*)), output); + #else + if (compileMode == IncludeSubpatterns) + loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), output); + #endif +#elif CPU(ARM) + push(ARMRegisters::r4); + push(ARMRegisters::r5); + push(ARMRegisters::r6); +#if CPU(ARM_TRADITIONAL) + push(ARMRegisters::r8); // scratch register +#endif + if (compileMode == IncludeSubpatterns) + move(ARMRegisters::r3, output); +#elif CPU(SH4) + push(SH4Registers::r11); + push(SH4Registers::r13); +#elif CPU(MIPS) + // Do nothing. +#endif + } + + void generateReturn() + { +#if CPU(X86_64) + pop(X86Registers::ebx); + pop(X86Registers::ebp); +#elif CPU(X86) + pop(X86Registers::esi); + pop(X86Registers::edi); + pop(X86Registers::ebx); + pop(X86Registers::ebp); +#elif CPU(ARM) +#if CPU(ARM_TRADITIONAL) + pop(ARMRegisters::r8); // scratch register +#endif + pop(ARMRegisters::r6); + pop(ARMRegisters::r5); + pop(ARMRegisters::r4); +#elif CPU(SH4) + pop(SH4Registers::r13); + pop(SH4Registers::r11); +#elif CPU(MIPS) + // Do nothing +#endif + ret(); + } + +public: + YarrGenerator(YarrPattern& pattern, YarrCharSize charSize) + : m_pattern(pattern) + , m_charSize(charSize) + , m_charScale(m_charSize == Char8 ? TimesOne: TimesTwo) + , m_shouldFallBack(false) + , m_checked(0) + { + } + + void compile(JSGlobalData* globalData, YarrCodeBlock& jitObject) + { + generateEnter(); + + Jump hasInput = checkInput(); + move(TrustedImmPtr((void*)WTF::notFound), returnRegister); + move(TrustedImm32(0), returnRegister2); + generateReturn(); + hasInput.link(this); + + if (compileMode == IncludeSubpatterns) { + for (unsigned i = 0; i < m_pattern.m_numSubpatterns + 1; ++i) + store32(TrustedImm32(-1), Address(output, (i << 1) * sizeof(int))); + } + + if (!m_pattern.m_body->m_hasFixedSize) + setMatchStart(index); + + initCallFrame(); + + // Compile the pattern to the internal 'YarrOp' representation. + opCompileBody(m_pattern.m_body); + + // If we encountered anything we can't handle in the JIT code + // (e.g. backreferences) then return early. + if (m_shouldFallBack) { + jitObject.setFallBack(true); + return; + } + + generate(); + backtrack(); + + // Link & finalize the code. + LinkBuffer linkBuffer(*globalData, this, REGEXP_CODE_ID); + m_backtrackingState.linkDataLabels(linkBuffer); + + if (compileMode == MatchOnly) { + if (m_charSize == Char8) + jitObject.set8BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, ("Match-only 8-bit regular expression"))); + else + jitObject.set16BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, ("Match-only 16-bit regular expression"))); + } else { + if (m_charSize == Char8) + jitObject.set8BitCode(FINALIZE_CODE(linkBuffer, ("8-bit regular expression"))); + else + jitObject.set16BitCode(FINALIZE_CODE(linkBuffer, ("16-bit regular expression"))); + } + jitObject.setFallBack(m_shouldFallBack); + } + +private: + YarrPattern& m_pattern; + + YarrCharSize m_charSize; + + Scale m_charScale; + + // Used to detect regular expression constructs that are not currently + // supported in the JIT; fall back to the interpreter when this is detected. + bool m_shouldFallBack; + + // The regular expression expressed as a linear sequence of operations. + Vector m_ops; + + // This records the current input offset being applied due to the current + // set of alternatives we are nested within. E.g. when matching the + // character 'b' within the regular expression /abc/, we will know that + // the minimum size for the alternative is 3, checked upon entry to the + // alternative, and that 'b' is at offset 1 from the start, and as such + // when matching 'b' we need to apply an offset of -2 to the load. + // + // FIXME: This should go away. Rather than tracking this value throughout + // code generation, we should gather this information up front & store it + // on the YarrOp structure. + int m_checked; + + // This class records state whilst generating the backtracking path of code. + BacktrackingState m_backtrackingState; +}; + +void jitCompile(YarrPattern& pattern, YarrCharSize charSize, JSGlobalData* globalData, YarrCodeBlock& jitObject, YarrJITCompileMode mode) +{ + if (mode == MatchOnly) + YarrGenerator(pattern, charSize).compile(globalData, jitObject); + else + YarrGenerator(pattern, charSize).compile(globalData, jitObject); +} + +}} + +#endif diff --git a/masm/yarr/YarrJIT.h b/masm/yarr/YarrJIT.h new file mode 100644 index 0000000000..bb7033fdea --- /dev/null +++ b/masm/yarr/YarrJIT.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrJIT_h +#define YarrJIT_h + +#if ENABLE(YARR_JIT) + +#include "JSGlobalData.h" +#include "MacroAssemblerCodeRef.h" +#include "MatchResult.h" +#include "Yarr.h" +#include "YarrPattern.h" + +#if CPU(X86) && !COMPILER(MSVC) +#define YARR_CALL __attribute__ ((regparm (3))) +#else +#define YARR_CALL +#endif + +namespace JSC { + +class JSGlobalData; +class ExecutablePool; + +namespace Yarr { + +class YarrCodeBlock { +#if CPU(X86_64) + typedef MatchResult (*YarrJITCode8)(const LChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + typedef MatchResult (*YarrJITCode16)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + typedef MatchResult (*YarrJITCodeMatchOnly8)(const LChar* input, unsigned start, unsigned length) YARR_CALL; + typedef MatchResult (*YarrJITCodeMatchOnly16)(const UChar* input, unsigned start, unsigned length) YARR_CALL; +#else + typedef EncodedMatchResult (*YarrJITCode8)(const LChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + typedef EncodedMatchResult (*YarrJITCode16)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + typedef EncodedMatchResult (*YarrJITCodeMatchOnly8)(const LChar* input, unsigned start, unsigned length) YARR_CALL; + typedef EncodedMatchResult (*YarrJITCodeMatchOnly16)(const UChar* input, unsigned start, unsigned length) YARR_CALL; +#endif + +public: + YarrCodeBlock() + : m_needFallBack(false) + { + } + + ~YarrCodeBlock() + { + } + + void setFallBack(bool fallback) { m_needFallBack = fallback; } + bool isFallBack() { return m_needFallBack; } + + bool has8BitCode() { return m_ref8.size(); } + bool has16BitCode() { return m_ref16.size(); } + void set8BitCode(MacroAssemblerCodeRef ref) { m_ref8 = ref; } + void set16BitCode(MacroAssemblerCodeRef ref) { m_ref16 = ref; } + + bool has8BitCodeMatchOnly() { return m_matchOnly8.size(); } + bool has16BitCodeMatchOnly() { return m_matchOnly16.size(); } + void set8BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly8 = matchOnly; } + void set16BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly16 = matchOnly; } + + MatchResult execute(const LChar* input, unsigned start, unsigned length, int* output) + { + ASSERT(has8BitCode()); + return MatchResult(reinterpret_cast(m_ref8.code().executableAddress())(input, start, length, output)); + } + + MatchResult execute(const UChar* input, unsigned start, unsigned length, int* output) + { + ASSERT(has16BitCode()); + return MatchResult(reinterpret_cast(m_ref16.code().executableAddress())(input, start, length, output)); + } + + MatchResult execute(const LChar* input, unsigned start, unsigned length) + { + ASSERT(has8BitCodeMatchOnly()); + return MatchResult(reinterpret_cast(m_matchOnly8.code().executableAddress())(input, start, length)); + } + + MatchResult execute(const UChar* input, unsigned start, unsigned length) + { + ASSERT(has16BitCodeMatchOnly()); + return MatchResult(reinterpret_cast(m_matchOnly16.code().executableAddress())(input, start, length)); + } + +#if ENABLE(REGEXP_TRACING) + void *getAddr() { return m_ref.code().executableAddress(); } +#endif + + void clear() + { + m_ref8 = MacroAssemblerCodeRef(); + m_ref16 = MacroAssemblerCodeRef(); + m_matchOnly8 = MacroAssemblerCodeRef(); + m_matchOnly16 = MacroAssemblerCodeRef(); + m_needFallBack = false; + } + +private: + MacroAssemblerCodeRef m_ref8; + MacroAssemblerCodeRef m_ref16; + MacroAssemblerCodeRef m_matchOnly8; + MacroAssemblerCodeRef m_matchOnly16; + bool m_needFallBack; +}; + +enum YarrJITCompileMode { + MatchOnly, + IncludeSubpatterns +}; +void jitCompile(YarrPattern&, YarrCharSize, JSGlobalData*, YarrCodeBlock& jitObject, YarrJITCompileMode = IncludeSubpatterns); + +} } // namespace JSC::Yarr + +#endif + +#endif // YarrJIT_h diff --git a/masm/yarr/YarrParser.h b/masm/yarr/YarrParser.h new file mode 100644 index 0000000000..4bab1a0903 --- /dev/null +++ b/masm/yarr/YarrParser.h @@ -0,0 +1,880 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrParser_h +#define YarrParser_h + +#include "Yarr.h" +#include +#include +#include + +namespace JSC { namespace Yarr { + +#define REGEXP_ERROR_PREFIX "Invalid regular expression: " + +enum BuiltInCharacterClassID { + DigitClassID, + SpaceClassID, + WordClassID, + NewlineClassID, +}; + +// The Parser class should not be used directly - only via the Yarr::parse() method. +template +class Parser { +private: + template + friend const char* parse(FriendDelegate&, const String& pattern, unsigned backReferenceLimit); + + enum ErrorCode { + NoError, + PatternTooLarge, + QuantifierOutOfOrder, + QuantifierWithoutAtom, + QuantifierTooLarge, + MissingParentheses, + ParenthesesUnmatched, + ParenthesesTypeInvalid, + CharacterClassUnmatched, + CharacterClassOutOfOrder, + EscapeUnterminated, + NumberOfErrorCodes + }; + + /* + * CharacterClassParserDelegate: + * + * The class CharacterClassParserDelegate is used in the parsing of character + * classes. This class handles detection of character ranges. This class + * implements enough of the delegate interface such that it can be passed to + * parseEscape() as an EscapeDelegate. This allows parseEscape() to be reused + * to perform the parsing of escape characters in character sets. + */ + class CharacterClassParserDelegate { + public: + CharacterClassParserDelegate(Delegate& delegate, ErrorCode& err) + : m_delegate(delegate) + , m_err(err) + , m_state(Empty) + , m_character(0) + { + } + + /* + * begin(): + * + * Called at beginning of construction. + */ + void begin(bool invert) + { + m_delegate.atomCharacterClassBegin(invert); + } + + /* + * atomPatternCharacter(): + * + * This method is called either from parseCharacterClass() (for an unescaped + * character in a character class), or from parseEscape(). In the former case + * the value true will be passed for the argument 'hyphenIsRange', and in this + * mode we will allow a hypen to be treated as indicating a range (i.e. /[a-z]/ + * is different to /[a\-z]/). + */ + void atomPatternCharacter(UChar ch, bool hyphenIsRange = false) + { + switch (m_state) { + case AfterCharacterClass: + // Following a builtin character class we need look out for a hyphen. + // We're looking for invalid ranges, such as /[\d-x]/ or /[\d-\d]/. + // If we see a hyphen following a charater class then unlike usual + // we'll report it to the delegate immediately, and put ourself into + // a poisoned state. Any following calls to add another character or + // character class will result in an error. (A hypen following a + // character-class is itself valid, but only at the end of a regex). + if (hyphenIsRange && ch == '-') { + m_delegate.atomCharacterClassAtom('-'); + m_state = AfterCharacterClassHyphen; + return; + } + // Otherwise just fall through - cached character so treat this as Empty. + + case Empty: + m_character = ch; + m_state = CachedCharacter; + return; + + case CachedCharacter: + if (hyphenIsRange && ch == '-') + m_state = CachedCharacterHyphen; + else { + m_delegate.atomCharacterClassAtom(m_character); + m_character = ch; + } + return; + + case CachedCharacterHyphen: + if (ch < m_character) { + m_err = CharacterClassOutOfOrder; + return; + } + m_delegate.atomCharacterClassRange(m_character, ch); + m_state = Empty; + return; + + // See coment in atomBuiltInCharacterClass below. + // This too is technically an error, per ECMA-262, and again we + // we chose to allow this. Note a subtlely here that while we + // diverge from the spec's definition of CharacterRange we do + // remain in compliance with the grammar. For example, consider + // the expression /[\d-a-z]/. We comply with the grammar in + // this case by not allowing a-z to be matched as a range. + case AfterCharacterClassHyphen: + m_delegate.atomCharacterClassAtom(ch); + m_state = Empty; + return; + } + } + + /* + * atomBuiltInCharacterClass(): + * + * Adds a built-in character class, called by parseEscape(). + */ + void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert) + { + switch (m_state) { + case CachedCharacter: + // Flush the currently cached character, then fall through. + m_delegate.atomCharacterClassAtom(m_character); + + case Empty: + case AfterCharacterClass: + m_state = AfterCharacterClass; + m_delegate.atomCharacterClassBuiltIn(classID, invert); + return; + + // If we hit either of these cases, we have an invalid range that + // looks something like /[x-\d]/ or /[\d-\d]/. + // According to ECMA-262 this should be a syntax error, but + // empirical testing shows this to break teh webz. Instead we + // comply with to the ECMA-262 grammar, and assume the grammar to + // have matched the range correctly, but tweak our interpretation + // of CharacterRange. Effectively we implicitly handle the hyphen + // as if it were escaped, e.g. /[\w-_]/ is treated as /[\w\-_]/. + case CachedCharacterHyphen: + m_delegate.atomCharacterClassAtom(m_character); + m_delegate.atomCharacterClassAtom('-'); + // fall through + case AfterCharacterClassHyphen: + m_delegate.atomCharacterClassBuiltIn(classID, invert); + m_state = Empty; + return; + } + } + + /* + * end(): + * + * Called at end of construction. + */ + void end() + { + if (m_state == CachedCharacter) + m_delegate.atomCharacterClassAtom(m_character); + else if (m_state == CachedCharacterHyphen) { + m_delegate.atomCharacterClassAtom(m_character); + m_delegate.atomCharacterClassAtom('-'); + } + m_delegate.atomCharacterClassEnd(); + } + + // parseEscape() should never call these delegate methods when + // invoked with inCharacterClass set. + NO_RETURN_DUE_TO_ASSERT void assertionWordBoundary(bool) { ASSERT_NOT_REACHED(); } + NO_RETURN_DUE_TO_ASSERT void atomBackReference(unsigned) { ASSERT_NOT_REACHED(); } + + private: + Delegate& m_delegate; + ErrorCode& m_err; + enum CharacterClassConstructionState { + Empty, + CachedCharacter, + CachedCharacterHyphen, + AfterCharacterClass, + AfterCharacterClassHyphen, + } m_state; + UChar m_character; + }; + + Parser(Delegate& delegate, const String& pattern, unsigned backReferenceLimit) + : m_delegate(delegate) + , m_backReferenceLimit(backReferenceLimit) + , m_err(NoError) + , m_data(pattern.getCharacters()) + , m_size(pattern.length()) + , m_index(0) + , m_parenthesesNestingDepth(0) + { + } + + /* + * parseEscape(): + * + * Helper for parseTokens() AND parseCharacterClass(). + * Unlike the other parser methods, this function does not report tokens + * directly to the member delegate (m_delegate), instead tokens are + * emitted to the delegate provided as an argument. In the case of atom + * escapes, parseTokens() will call parseEscape() passing m_delegate as + * an argument, and as such the escape will be reported to the delegate. + * + * However this method may also be used by parseCharacterClass(), in which + * case a CharacterClassParserDelegate will be passed as the delegate that + * tokens should be added to. A boolean flag is also provided to indicate + * whether that an escape in a CharacterClass is being parsed (some parsing + * rules change in this context). + * + * The boolean value returned by this method indicates whether the token + * parsed was an atom (outside of a characted class \b and \B will be + * interpreted as assertions). + */ + template + bool parseEscape(EscapeDelegate& delegate) + { + ASSERT(!m_err); + ASSERT(peek() == '\\'); + consume(); + + if (atEndOfPattern()) { + m_err = EscapeUnterminated; + return false; + } + + switch (peek()) { + // Assertions + case 'b': + consume(); + if (inCharacterClass) + delegate.atomPatternCharacter('\b'); + else { + delegate.assertionWordBoundary(false); + return false; + } + break; + case 'B': + consume(); + if (inCharacterClass) + delegate.atomPatternCharacter('B'); + else { + delegate.assertionWordBoundary(true); + return false; + } + break; + + // CharacterClassEscape + case 'd': + consume(); + delegate.atomBuiltInCharacterClass(DigitClassID, false); + break; + case 's': + consume(); + delegate.atomBuiltInCharacterClass(SpaceClassID, false); + break; + case 'w': + consume(); + delegate.atomBuiltInCharacterClass(WordClassID, false); + break; + case 'D': + consume(); + delegate.atomBuiltInCharacterClass(DigitClassID, true); + break; + case 'S': + consume(); + delegate.atomBuiltInCharacterClass(SpaceClassID, true); + break; + case 'W': + consume(); + delegate.atomBuiltInCharacterClass(WordClassID, true); + break; + + // DecimalEscape + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + // To match Firefox, we parse an invalid backreference in the range [1-7] as an octal escape. + // First, try to parse this as backreference. + if (!inCharacterClass) { + ParseState state = saveState(); + + unsigned backReference = consumeNumber(); + if (backReference <= m_backReferenceLimit) { + delegate.atomBackReference(backReference); + break; + } + + restoreState(state); + } + + // Not a backreference, and not octal. + if (peek() >= '8') { + delegate.atomPatternCharacter('\\'); + break; + } + + // Fall-through to handle this as an octal escape. + } + + // Octal escape + case '0': + delegate.atomPatternCharacter(consumeOctal()); + break; + + // ControlEscape + case 'f': + consume(); + delegate.atomPatternCharacter('\f'); + break; + case 'n': + consume(); + delegate.atomPatternCharacter('\n'); + break; + case 'r': + consume(); + delegate.atomPatternCharacter('\r'); + break; + case 't': + consume(); + delegate.atomPatternCharacter('\t'); + break; + case 'v': + consume(); + delegate.atomPatternCharacter('\v'); + break; + + // ControlLetter + case 'c': { + ParseState state = saveState(); + consume(); + if (!atEndOfPattern()) { + int control = consume(); + + // To match Firefox, inside a character class, we also accept numbers and '_' as control characters. + if (inCharacterClass ? WTF::isASCIIAlphanumeric(control) || (control == '_') : WTF::isASCIIAlpha(control)) { + delegate.atomPatternCharacter(control & 0x1f); + break; + } + } + restoreState(state); + delegate.atomPatternCharacter('\\'); + break; + } + + // HexEscape + case 'x': { + consume(); + int x = tryConsumeHex(2); + if (x == -1) + delegate.atomPatternCharacter('x'); + else + delegate.atomPatternCharacter(x); + break; + } + + // UnicodeEscape + case 'u': { + consume(); + int u = tryConsumeHex(4); + if (u == -1) + delegate.atomPatternCharacter('u'); + else + delegate.atomPatternCharacter(u); + break; + } + + // IdentityEscape + default: + delegate.atomPatternCharacter(consume()); + } + + return true; + } + + /* + * parseAtomEscape(), parseCharacterClassEscape(): + * + * These methods alias to parseEscape(). + */ + bool parseAtomEscape() + { + return parseEscape(m_delegate); + } + void parseCharacterClassEscape(CharacterClassParserDelegate& delegate) + { + parseEscape(delegate); + } + + /* + * parseCharacterClass(): + * + * Helper for parseTokens(); calls dirctly and indirectly (via parseCharacterClassEscape) + * to an instance of CharacterClassParserDelegate, to describe the character class to the + * delegate. + */ + void parseCharacterClass() + { + ASSERT(!m_err); + ASSERT(peek() == '['); + consume(); + + CharacterClassParserDelegate characterClassConstructor(m_delegate, m_err); + + characterClassConstructor.begin(tryConsume('^')); + + while (!atEndOfPattern()) { + switch (peek()) { + case ']': + consume(); + characterClassConstructor.end(); + return; + + case '\\': + parseCharacterClassEscape(characterClassConstructor); + break; + + default: + characterClassConstructor.atomPatternCharacter(consume(), true); + } + + if (m_err) + return; + } + + m_err = CharacterClassUnmatched; + } + + /* + * parseParenthesesBegin(): + * + * Helper for parseTokens(); checks for parentheses types other than regular capturing subpatterns. + */ + void parseParenthesesBegin() + { + ASSERT(!m_err); + ASSERT(peek() == '('); + consume(); + + if (tryConsume('?')) { + if (atEndOfPattern()) { + m_err = ParenthesesTypeInvalid; + return; + } + + switch (consume()) { + case ':': + m_delegate.atomParenthesesSubpatternBegin(false); + break; + + case '=': + m_delegate.atomParentheticalAssertionBegin(); + break; + + case '!': + m_delegate.atomParentheticalAssertionBegin(true); + break; + + default: + m_err = ParenthesesTypeInvalid; + } + } else + m_delegate.atomParenthesesSubpatternBegin(); + + ++m_parenthesesNestingDepth; + } + + /* + * parseParenthesesEnd(): + * + * Helper for parseTokens(); checks for parse errors (due to unmatched parentheses). + */ + void parseParenthesesEnd() + { + ASSERT(!m_err); + ASSERT(peek() == ')'); + consume(); + + if (m_parenthesesNestingDepth > 0) + m_delegate.atomParenthesesEnd(); + else + m_err = ParenthesesUnmatched; + + --m_parenthesesNestingDepth; + } + + /* + * parseQuantifier(): + * + * Helper for parseTokens(); checks for parse errors and non-greedy quantifiers. + */ + void parseQuantifier(bool lastTokenWasAnAtom, unsigned min, unsigned max) + { + ASSERT(!m_err); + ASSERT(min <= max); + + if (min == UINT_MAX) { + m_err = QuantifierTooLarge; + return; + } + + if (lastTokenWasAnAtom) + m_delegate.quantifyAtom(min, max, !tryConsume('?')); + else + m_err = QuantifierWithoutAtom; + } + + /* + * parseTokens(): + * + * This method loops over the input pattern reporting tokens to the delegate. + * The method returns when a parse error is detected, or the end of the pattern + * is reached. One piece of state is tracked around the loop, which is whether + * the last token passed to the delegate was an atom (this is necessary to detect + * a parse error when a quantifier provided without an atom to quantify). + */ + void parseTokens() + { + bool lastTokenWasAnAtom = false; + + while (!atEndOfPattern()) { + switch (peek()) { + case '|': + consume(); + m_delegate.disjunction(); + lastTokenWasAnAtom = false; + break; + + case '(': + parseParenthesesBegin(); + lastTokenWasAnAtom = false; + break; + + case ')': + parseParenthesesEnd(); + lastTokenWasAnAtom = true; + break; + + case '^': + consume(); + m_delegate.assertionBOL(); + lastTokenWasAnAtom = false; + break; + + case '$': + consume(); + m_delegate.assertionEOL(); + lastTokenWasAnAtom = false; + break; + + case '.': + consume(); + m_delegate.atomBuiltInCharacterClass(NewlineClassID, true); + lastTokenWasAnAtom = true; + break; + + case '[': + parseCharacterClass(); + lastTokenWasAnAtom = true; + break; + + case '\\': + lastTokenWasAnAtom = parseAtomEscape(); + break; + + case '*': + consume(); + parseQuantifier(lastTokenWasAnAtom, 0, quantifyInfinite); + lastTokenWasAnAtom = false; + break; + + case '+': + consume(); + parseQuantifier(lastTokenWasAnAtom, 1, quantifyInfinite); + lastTokenWasAnAtom = false; + break; + + case '?': + consume(); + parseQuantifier(lastTokenWasAnAtom, 0, 1); + lastTokenWasAnAtom = false; + break; + + case '{': { + ParseState state = saveState(); + + consume(); + if (peekIsDigit()) { + unsigned min = consumeNumber(); + unsigned max = min; + + if (tryConsume(',')) + max = peekIsDigit() ? consumeNumber() : quantifyInfinite; + + if (tryConsume('}')) { + if (min <= max) + parseQuantifier(lastTokenWasAnAtom, min, max); + else + m_err = QuantifierOutOfOrder; + lastTokenWasAnAtom = false; + break; + } + } + + restoreState(state); + } // if we did not find a complete quantifer, fall through to the default case. + + default: + m_delegate.atomPatternCharacter(consume()); + lastTokenWasAnAtom = true; + } + + if (m_err) + return; + } + + if (m_parenthesesNestingDepth > 0) + m_err = MissingParentheses; + } + + /* + * parse(): + * + * This method calls parseTokens() to parse over the input and converts any + * error code to a const char* for a result. + */ + const char* parse() + { + if (m_size > MAX_PATTERN_SIZE) + m_err = PatternTooLarge; + else + parseTokens(); + ASSERT(atEndOfPattern() || m_err); + + // The order of this array must match the ErrorCode enum. + static const char* errorMessages[NumberOfErrorCodes] = { + 0, // NoError + REGEXP_ERROR_PREFIX "regular expression too large", + REGEXP_ERROR_PREFIX "numbers out of order in {} quantifier", + REGEXP_ERROR_PREFIX "nothing to repeat", + REGEXP_ERROR_PREFIX "number too large in {} quantifier", + REGEXP_ERROR_PREFIX "missing )", + REGEXP_ERROR_PREFIX "unmatched parentheses", + REGEXP_ERROR_PREFIX "unrecognized character after (?", + REGEXP_ERROR_PREFIX "missing terminating ] for character class", + REGEXP_ERROR_PREFIX "range out of order in character class", + REGEXP_ERROR_PREFIX "\\ at end of pattern" + }; + + return errorMessages[m_err]; + } + + // Misc helper functions: + + typedef unsigned ParseState; + + ParseState saveState() + { + return m_index; + } + + void restoreState(ParseState state) + { + m_index = state; + } + + bool atEndOfPattern() + { + ASSERT(m_index <= m_size); + return m_index == m_size; + } + + int peek() + { + ASSERT(m_index < m_size); + return m_data[m_index]; + } + + bool peekIsDigit() + { + return !atEndOfPattern() && WTF::isASCIIDigit(peek()); + } + + unsigned peekDigit() + { + ASSERT(peekIsDigit()); + return peek() - '0'; + } + + int consume() + { + ASSERT(m_index < m_size); + return m_data[m_index++]; + } + + unsigned consumeDigit() + { + ASSERT(peekIsDigit()); + return consume() - '0'; + } + + unsigned consumeNumber() + { + unsigned n = consumeDigit(); + // check for overflow. + for (unsigned newValue; peekIsDigit() && ((newValue = n * 10 + peekDigit()) >= n); ) { + n = newValue; + consume(); + } + return n; + } + + unsigned consumeOctal() + { + ASSERT(WTF::isASCIIOctalDigit(peek())); + + unsigned n = consumeDigit(); + while (n < 32 && !atEndOfPattern() && WTF::isASCIIOctalDigit(peek())) + n = n * 8 + consumeDigit(); + return n; + } + + bool tryConsume(UChar ch) + { + if (atEndOfPattern() || (m_data[m_index] != ch)) + return false; + ++m_index; + return true; + } + + int tryConsumeHex(int count) + { + ParseState state = saveState(); + + int n = 0; + while (count--) { + if (atEndOfPattern() || !WTF::isASCIIHexDigit(peek())) { + restoreState(state); + return -1; + } + n = (n << 4) | WTF::toASCIIHexValue(consume()); + } + return n; + } + + Delegate& m_delegate; + unsigned m_backReferenceLimit; + ErrorCode m_err; + const CharType* m_data; + unsigned m_size; + unsigned m_index; + unsigned m_parenthesesNestingDepth; + + // Derived by empirical testing of compile time in PCRE and WREC. + static const unsigned MAX_PATTERN_SIZE = 1024 * 1024; +}; + +/* + * Yarr::parse(): + * + * The parse method is passed a pattern to be parsed and a delegate upon which + * callbacks will be made to record the parsed tokens forming the regex. + * Yarr::parse() returns null on success, or a const C string providing an error + * message where a parse error occurs. + * + * The Delegate must implement the following interface: + * + * void assertionBOL(); + * void assertionEOL(); + * void assertionWordBoundary(bool invert); + * + * void atomPatternCharacter(UChar ch); + * void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert); + * void atomCharacterClassBegin(bool invert) + * void atomCharacterClassAtom(UChar ch) + * void atomCharacterClassRange(UChar begin, UChar end) + * void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert) + * void atomCharacterClassEnd() + * void atomParenthesesSubpatternBegin(bool capture = true); + * void atomParentheticalAssertionBegin(bool invert = false); + * void atomParenthesesEnd(); + * void atomBackReference(unsigned subpatternId); + * + * void quantifyAtom(unsigned min, unsigned max, bool greedy); + * + * void disjunction(); + * + * The regular expression is described by a sequence of assertion*() and atom*() + * callbacks to the delegate, describing the terms in the regular expression. + * Following an atom a quantifyAtom() call may occur to indicate that the previous + * atom should be quantified. In the case of atoms described across multiple + * calls (parentheses and character classes) the call to quantifyAtom() will come + * after the call to the atom*End() method, never after atom*Begin(). + * + * Character classes may either be described by a single call to + * atomBuiltInCharacterClass(), or by a sequence of atomCharacterClass*() calls. + * In the latter case, ...Begin() will be called, followed by a sequence of + * calls to ...Atom(), ...Range(), and ...BuiltIn(), followed by a call to ...End(). + * + * Sequences of atoms and assertions are broken into alternatives via calls to + * disjunction(). Assertions, atoms, and disjunctions emitted between calls to + * atomParenthesesBegin() and atomParenthesesEnd() form the body of a subpattern. + * atomParenthesesBegin() is passed a subpatternId. In the case of a regular + * capturing subpattern, this will be the subpatternId associated with these + * parentheses, and will also by definition be the lowest subpatternId of these + * parentheses and of any nested paretheses. The atomParenthesesEnd() method + * is passed the subpatternId of the last capturing subexpression nested within + * these paretheses. In the case of a capturing subpattern with no nested + * capturing subpatterns, the same subpatternId will be passed to the begin and + * end functions. In the case of non-capturing subpatterns the subpatternId + * passed to the begin method is also the first possible subpatternId that might + * be nested within these paretheses. If a set of non-capturing parentheses does + * not contain any capturing subpatterns, then the subpatternId passed to begin + * will be greater than the subpatternId passed to end. + */ + +template +const char* parse(Delegate& delegate, const String& pattern, unsigned backReferenceLimit = quantifyInfinite) +{ + if (pattern.is8Bit()) + return Parser(delegate, pattern, backReferenceLimit).parse(); + return Parser(delegate, pattern, backReferenceLimit).parse(); +} + +} } // namespace JSC::Yarr + +#endif // YarrParser_h diff --git a/masm/yarr/YarrPattern.cpp b/masm/yarr/YarrPattern.cpp new file mode 100644 index 0000000000..c953a38d2f --- /dev/null +++ b/masm/yarr/YarrPattern.cpp @@ -0,0 +1,874 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "YarrPattern.h" + +#include "Yarr.h" +#include "YarrCanonicalizeUCS2.h" +#include "YarrParser.h" +#include + +using namespace WTF; + +namespace JSC { namespace Yarr { + +#include "RegExpJitTables.h" + +class CharacterClassConstructor { +public: + CharacterClassConstructor(bool isCaseInsensitive = false) + : m_isCaseInsensitive(isCaseInsensitive) + { + } + + void reset() + { + m_matches.clear(); + m_ranges.clear(); + m_matchesUnicode.clear(); + m_rangesUnicode.clear(); + } + + void append(const CharacterClass* other) + { + for (size_t i = 0; i < other->m_matches.size(); ++i) + addSorted(m_matches, other->m_matches[i]); + for (size_t i = 0; i < other->m_ranges.size(); ++i) + addSortedRange(m_ranges, other->m_ranges[i].begin, other->m_ranges[i].end); + for (size_t i = 0; i < other->m_matchesUnicode.size(); ++i) + addSorted(m_matchesUnicode, other->m_matchesUnicode[i]); + for (size_t i = 0; i < other->m_rangesUnicode.size(); ++i) + addSortedRange(m_rangesUnicode, other->m_rangesUnicode[i].begin, other->m_rangesUnicode[i].end); + } + + void putChar(UChar ch) + { + // Handle ascii cases. + if (ch <= 0x7f) { + if (m_isCaseInsensitive && isASCIIAlpha(ch)) { + addSorted(m_matches, toASCIIUpper(ch)); + addSorted(m_matches, toASCIILower(ch)); + } else + addSorted(m_matches, ch); + return; + } + + // Simple case, not a case-insensitive match. + if (!m_isCaseInsensitive) { + addSorted(m_matchesUnicode, ch); + return; + } + + // Add multiple matches, if necessary. + UCS2CanonicalizationRange* info = rangeInfoFor(ch); + if (info->type == CanonicalizeUnique) + addSorted(m_matchesUnicode, ch); + else + putUnicodeIgnoreCase(ch, info); + } + + void putUnicodeIgnoreCase(UChar ch, UCS2CanonicalizationRange* info) + { + ASSERT(m_isCaseInsensitive); + ASSERT(ch > 0x7f); + ASSERT(ch >= info->begin && ch <= info->end); + ASSERT(info->type != CanonicalizeUnique); + if (info->type == CanonicalizeSet) { + for (uint16_t* set = characterSetInfo[info->value]; (ch = *set); ++set) + addSorted(m_matchesUnicode, ch); + } else { + addSorted(m_matchesUnicode, ch); + addSorted(m_matchesUnicode, getCanonicalPair(info, ch)); + } + } + + void putRange(UChar lo, UChar hi) + { + if (lo <= 0x7f) { + char asciiLo = lo; + char asciiHi = std::min(hi, (UChar)0x7f); + addSortedRange(m_ranges, lo, asciiHi); + + if (m_isCaseInsensitive) { + if ((asciiLo <= 'Z') && (asciiHi >= 'A')) + addSortedRange(m_ranges, std::max(asciiLo, 'A')+('a'-'A'), std::min(asciiHi, 'Z')+('a'-'A')); + if ((asciiLo <= 'z') && (asciiHi >= 'a')) + addSortedRange(m_ranges, std::max(asciiLo, 'a')+('A'-'a'), std::min(asciiHi, 'z')+('A'-'a')); + } + } + if (hi <= 0x7f) + return; + + lo = std::max(lo, (UChar)0x80); + addSortedRange(m_rangesUnicode, lo, hi); + + if (!m_isCaseInsensitive) + return; + + UCS2CanonicalizationRange* info = rangeInfoFor(lo); + while (true) { + // Handle the range [lo .. end] + UChar end = std::min(info->end, hi); + + switch (info->type) { + case CanonicalizeUnique: + // Nothing to do - no canonical equivalents. + break; + case CanonicalizeSet: { + UChar ch; + for (uint16_t* set = characterSetInfo[info->value]; (ch = *set); ++set) + addSorted(m_matchesUnicode, ch); + break; + } + case CanonicalizeRangeLo: + addSortedRange(m_rangesUnicode, lo + info->value, end + info->value); + break; + case CanonicalizeRangeHi: + addSortedRange(m_rangesUnicode, lo - info->value, end - info->value); + break; + case CanonicalizeAlternatingAligned: + // Use addSortedRange since there is likely an abutting range to combine with. + if (lo & 1) + addSortedRange(m_rangesUnicode, lo - 1, lo - 1); + if (!(end & 1)) + addSortedRange(m_rangesUnicode, end + 1, end + 1); + break; + case CanonicalizeAlternatingUnaligned: + // Use addSortedRange since there is likely an abutting range to combine with. + if (!(lo & 1)) + addSortedRange(m_rangesUnicode, lo - 1, lo - 1); + if (end & 1) + addSortedRange(m_rangesUnicode, end + 1, end + 1); + break; + } + + if (hi == end) + return; + + ++info; + lo = info->begin; + }; + + } + + CharacterClass* charClass() + { + CharacterClass* characterClass = new CharacterClass(0); + + characterClass->m_matches.swap(m_matches); + characterClass->m_ranges.swap(m_ranges); + characterClass->m_matchesUnicode.swap(m_matchesUnicode); + characterClass->m_rangesUnicode.swap(m_rangesUnicode); + + return characterClass; + } + +private: + void addSorted(Vector& matches, UChar ch) + { + unsigned pos = 0; + unsigned range = matches.size(); + + // binary chop, find position to insert char. + while (range) { + unsigned index = range >> 1; + + int val = matches[pos+index] - ch; + if (!val) + return; + else if (val > 0) + range = index; + else { + pos += (index+1); + range -= (index+1); + } + } + + if (pos == matches.size()) + matches.append(ch); + else + matches.insert(pos, ch); + } + + void addSortedRange(Vector& ranges, UChar lo, UChar hi) + { + unsigned end = ranges.size(); + + // Simple linear scan - I doubt there are that many ranges anyway... + // feel free to fix this with something faster (eg binary chop). + for (unsigned i = 0; i < end; ++i) { + // does the new range fall before the current position in the array + if (hi < ranges[i].begin) { + // optional optimization: concatenate appending ranges? - may not be worthwhile. + if (hi == (ranges[i].begin - 1)) { + ranges[i].begin = lo; + return; + } + ranges.insert(i, CharacterRange(lo, hi)); + return; + } + // Okay, since we didn't hit the last case, the end of the new range is definitely at or after the begining + // If the new range start at or before the end of the last range, then the overlap (if it starts one after the + // end of the last range they concatenate, which is just as good. + if (lo <= (ranges[i].end + 1)) { + // found an intersect! we'll replace this entry in the array. + ranges[i].begin = std::min(ranges[i].begin, lo); + ranges[i].end = std::max(ranges[i].end, hi); + + // now check if the new range can subsume any subsequent ranges. + unsigned next = i+1; + // each iteration of the loop we will either remove something from the list, or break the loop. + while (next < ranges.size()) { + if (ranges[next].begin <= (ranges[i].end + 1)) { + // the next entry now overlaps / concatenates this one. + ranges[i].end = std::max(ranges[i].end, ranges[next].end); + ranges.remove(next); + } else + break; + } + + return; + } + } + + // CharacterRange comes after all existing ranges. + ranges.append(CharacterRange(lo, hi)); + } + + bool m_isCaseInsensitive; + + Vector m_matches; + Vector m_ranges; + Vector m_matchesUnicode; + Vector m_rangesUnicode; +}; + +class YarrPatternConstructor { +public: + YarrPatternConstructor(YarrPattern& pattern) + : m_pattern(pattern) + , m_characterClassConstructor(pattern.m_ignoreCase) + , m_invertParentheticalAssertion(false) + { + m_pattern.m_body = new PatternDisjunction(); + m_alternative = m_pattern.m_body->addNewAlternative(); + m_pattern.m_disjunctions.append(m_pattern.m_body); + } + + ~YarrPatternConstructor() + { + } + + void reset() + { + m_pattern.reset(); + m_characterClassConstructor.reset(); + + m_pattern.m_body = new PatternDisjunction(); + m_alternative = m_pattern.m_body->addNewAlternative(); + m_pattern.m_disjunctions.append(m_pattern.m_body); + } + + void assertionBOL() + { + if (!m_alternative->m_terms.size() & !m_invertParentheticalAssertion) { + m_alternative->m_startsWithBOL = true; + m_alternative->m_containsBOL = true; + m_pattern.m_containsBOL = true; + } + m_alternative->m_terms.append(PatternTerm::BOL()); + } + void assertionEOL() + { + m_alternative->m_terms.append(PatternTerm::EOL()); + } + void assertionWordBoundary(bool invert) + { + m_alternative->m_terms.append(PatternTerm::WordBoundary(invert)); + } + + void atomPatternCharacter(UChar ch) + { + // We handle case-insensitive checking of unicode characters which do have both + // cases by handling them as if they were defined using a CharacterClass. + if (!m_pattern.m_ignoreCase || isASCII(ch)) { + m_alternative->m_terms.append(PatternTerm(ch)); + return; + } + + UCS2CanonicalizationRange* info = rangeInfoFor(ch); + if (info->type == CanonicalizeUnique) { + m_alternative->m_terms.append(PatternTerm(ch)); + return; + } + + m_characterClassConstructor.putUnicodeIgnoreCase(ch, info); + CharacterClass* newCharacterClass = m_characterClassConstructor.charClass(); + m_pattern.m_userCharacterClasses.append(newCharacterClass); + m_alternative->m_terms.append(PatternTerm(newCharacterClass, false)); + } + + void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert) + { + switch (classID) { + case DigitClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.digitsCharacterClass(), invert)); + break; + case SpaceClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.spacesCharacterClass(), invert)); + break; + case WordClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.wordcharCharacterClass(), invert)); + break; + case NewlineClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.newlineCharacterClass(), invert)); + break; + } + } + + void atomCharacterClassBegin(bool invert = false) + { + m_invertCharacterClass = invert; + } + + void atomCharacterClassAtom(UChar ch) + { + m_characterClassConstructor.putChar(ch); + } + + void atomCharacterClassRange(UChar begin, UChar end) + { + m_characterClassConstructor.putRange(begin, end); + } + + void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert) + { + ASSERT(classID != NewlineClassID); + + switch (classID) { + case DigitClassID: + m_characterClassConstructor.append(invert ? m_pattern.nondigitsCharacterClass() : m_pattern.digitsCharacterClass()); + break; + + case SpaceClassID: + m_characterClassConstructor.append(invert ? m_pattern.nonspacesCharacterClass() : m_pattern.spacesCharacterClass()); + break; + + case WordClassID: + m_characterClassConstructor.append(invert ? m_pattern.nonwordcharCharacterClass() : m_pattern.wordcharCharacterClass()); + break; + + default: + ASSERT_NOT_REACHED(); + } + } + + void atomCharacterClassEnd() + { + CharacterClass* newCharacterClass = m_characterClassConstructor.charClass(); + m_pattern.m_userCharacterClasses.append(newCharacterClass); + m_alternative->m_terms.append(PatternTerm(newCharacterClass, m_invertCharacterClass)); + } + + void atomParenthesesSubpatternBegin(bool capture = true) + { + unsigned subpatternId = m_pattern.m_numSubpatterns + 1; + if (capture) + m_pattern.m_numSubpatterns++; + + PatternDisjunction* parenthesesDisjunction = new PatternDisjunction(m_alternative); + m_pattern.m_disjunctions.append(parenthesesDisjunction); + m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture, false)); + m_alternative = parenthesesDisjunction->addNewAlternative(); + } + + void atomParentheticalAssertionBegin(bool invert = false) + { + PatternDisjunction* parenthesesDisjunction = new PatternDisjunction(m_alternative); + m_pattern.m_disjunctions.append(parenthesesDisjunction); + m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParentheticalAssertion, m_pattern.m_numSubpatterns + 1, parenthesesDisjunction, false, invert)); + m_alternative = parenthesesDisjunction->addNewAlternative(); + m_invertParentheticalAssertion = invert; + } + + void atomParenthesesEnd() + { + ASSERT(m_alternative->m_parent); + ASSERT(m_alternative->m_parent->m_parent); + + PatternDisjunction* parenthesesDisjunction = m_alternative->m_parent; + m_alternative = m_alternative->m_parent->m_parent; + + PatternTerm& lastTerm = m_alternative->lastTerm(); + + unsigned numParenAlternatives = parenthesesDisjunction->m_alternatives.size(); + unsigned numBOLAnchoredAlts = 0; + + for (unsigned i = 0; i < numParenAlternatives; i++) { + // Bubble up BOL flags + if (parenthesesDisjunction->m_alternatives[i]->m_startsWithBOL) + numBOLAnchoredAlts++; + } + + if (numBOLAnchoredAlts) { + m_alternative->m_containsBOL = true; + // If all the alternatives in parens start with BOL, then so does this one + if (numBOLAnchoredAlts == numParenAlternatives) + m_alternative->m_startsWithBOL = true; + } + + lastTerm.parentheses.lastSubpatternId = m_pattern.m_numSubpatterns; + m_invertParentheticalAssertion = false; + } + + void atomBackReference(unsigned subpatternId) + { + ASSERT(subpatternId); + m_pattern.m_containsBackreferences = true; + m_pattern.m_maxBackReference = std::max(m_pattern.m_maxBackReference, subpatternId); + + if (subpatternId > m_pattern.m_numSubpatterns) { + m_alternative->m_terms.append(PatternTerm::ForwardReference()); + return; + } + + PatternAlternative* currentAlternative = m_alternative; + ASSERT(currentAlternative); + + // Note to self: if we waited until the AST was baked, we could also remove forwards refs + while ((currentAlternative = currentAlternative->m_parent->m_parent)) { + PatternTerm& term = currentAlternative->lastTerm(); + ASSERT((term.type == PatternTerm::TypeParenthesesSubpattern) || (term.type == PatternTerm::TypeParentheticalAssertion)); + + if ((term.type == PatternTerm::TypeParenthesesSubpattern) && term.capture() && (subpatternId == term.parentheses.subpatternId)) { + m_alternative->m_terms.append(PatternTerm::ForwardReference()); + return; + } + } + + m_alternative->m_terms.append(PatternTerm(subpatternId)); + } + + // deep copy the argument disjunction. If filterStartsWithBOL is true, + // skip alternatives with m_startsWithBOL set true. + PatternDisjunction* copyDisjunction(PatternDisjunction* disjunction, bool filterStartsWithBOL = false) + { + PatternDisjunction* newDisjunction = 0; + for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { + PatternAlternative* alternative = disjunction->m_alternatives[alt]; + if (!filterStartsWithBOL || !alternative->m_startsWithBOL) { + if (!newDisjunction) { + newDisjunction = new PatternDisjunction(); + newDisjunction->m_parent = disjunction->m_parent; + } + PatternAlternative* newAlternative = newDisjunction->addNewAlternative(); + for (unsigned i = 0; i < alternative->m_terms.size(); ++i) + newAlternative->m_terms.append(copyTerm(alternative->m_terms[i], filterStartsWithBOL)); + } + } + + if (newDisjunction) + m_pattern.m_disjunctions.append(newDisjunction); + return newDisjunction; + } + + PatternTerm copyTerm(PatternTerm& term, bool filterStartsWithBOL = false) + { + if ((term.type != PatternTerm::TypeParenthesesSubpattern) && (term.type != PatternTerm::TypeParentheticalAssertion)) + return PatternTerm(term); + + PatternTerm termCopy = term; + termCopy.parentheses.disjunction = copyDisjunction(termCopy.parentheses.disjunction, filterStartsWithBOL); + return termCopy; + } + + void quantifyAtom(unsigned min, unsigned max, bool greedy) + { + ASSERT(min <= max); + ASSERT(m_alternative->m_terms.size()); + + if (!max) { + m_alternative->removeLastTerm(); + return; + } + + PatternTerm& term = m_alternative->lastTerm(); + ASSERT(term.type > PatternTerm::TypeAssertionWordBoundary); + ASSERT((term.quantityCount == 1) && (term.quantityType == QuantifierFixedCount)); + + if (term.type == PatternTerm::TypeParentheticalAssertion) { + // If an assertion is quantified with a minimum count of zero, it can simply be removed. + // This arises from the RepeatMatcher behaviour in the spec. Matching an assertion never + // results in any input being consumed, however the continuation passed to the assertion + // (called in steps, 8c and 9 of the RepeatMatcher definition, ES5.1 15.10.2.5) will + // reject all zero length matches (see step 2.1). A match from the continuation of the + // expression will still be accepted regardless (via steps 8a and 11) - the upshot of all + // this is that matches from the assertion are not required, and won't be accepted anyway, + // so no need to ever run it. + if (!min) + m_alternative->removeLastTerm(); + // We never need to run an assertion more than once. Subsequent interations will be run + // with the same start index (since assertions are non-capturing) and the same captures + // (per step 4 of RepeatMatcher in ES5.1 15.10.2.5), and as such will always produce the + // same result and captures. If the first match succeeds then the subsequent (min - 1) + // matches will too. Any additional optional matches will fail (on the same basis as the + // minimum zero quantified assertions, above), but this will still result in a match. + return; + } + + if (min == 0) + term.quantify(max, greedy ? QuantifierGreedy : QuantifierNonGreedy); + else if (min == max) + term.quantify(min, QuantifierFixedCount); + else { + term.quantify(min, QuantifierFixedCount); + m_alternative->m_terms.append(copyTerm(term)); + // NOTE: this term is interesting from an analysis perspective, in that it can be ignored..... + m_alternative->lastTerm().quantify((max == quantifyInfinite) ? max : max - min, greedy ? QuantifierGreedy : QuantifierNonGreedy); + if (m_alternative->lastTerm().type == PatternTerm::TypeParenthesesSubpattern) + m_alternative->lastTerm().parentheses.isCopy = true; + } + } + + void disjunction() + { + m_alternative = m_alternative->m_parent->addNewAlternative(); + } + + unsigned setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition) + { + alternative->m_hasFixedSize = true; + Checked currentInputPosition = initialInputPosition; + + for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { + PatternTerm& term = alternative->m_terms[i]; + + switch (term.type) { + case PatternTerm::TypeAssertionBOL: + case PatternTerm::TypeAssertionEOL: + case PatternTerm::TypeAssertionWordBoundary: + term.inputPosition = currentInputPosition.unsafeGet(); + break; + + case PatternTerm::TypeBackReference: + term.inputPosition = currentInputPosition.unsafeGet(); + term.frameLocation = currentCallFrameSize; + currentCallFrameSize += YarrStackSpaceForBackTrackInfoBackReference; + alternative->m_hasFixedSize = false; + break; + + case PatternTerm::TypeForwardReference: + break; + + case PatternTerm::TypePatternCharacter: + term.inputPosition = currentInputPosition.unsafeGet(); + if (term.quantityType != QuantifierFixedCount) { + term.frameLocation = currentCallFrameSize; + currentCallFrameSize += YarrStackSpaceForBackTrackInfoPatternCharacter; + alternative->m_hasFixedSize = false; + } else + currentInputPosition += term.quantityCount; + break; + + case PatternTerm::TypeCharacterClass: + term.inputPosition = currentInputPosition.unsafeGet(); + if (term.quantityType != QuantifierFixedCount) { + term.frameLocation = currentCallFrameSize; + currentCallFrameSize += YarrStackSpaceForBackTrackInfoCharacterClass; + alternative->m_hasFixedSize = false; + } else + currentInputPosition += term.quantityCount; + break; + + case PatternTerm::TypeParenthesesSubpattern: + // Note: for fixed once parentheses we will ensure at least the minimum is available; others are on their own. + term.frameLocation = currentCallFrameSize; + if (term.quantityCount == 1 && !term.parentheses.isCopy) { + if (term.quantityType != QuantifierFixedCount) + currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesOnce; + currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet()); + // If quantity is fixed, then pre-check its minimum size. + if (term.quantityType == QuantifierFixedCount) + currentInputPosition += term.parentheses.disjunction->m_minimumSize; + term.inputPosition = currentInputPosition.unsafeGet(); + } else if (term.parentheses.isTerminal) { + currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesTerminal; + currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet()); + term.inputPosition = currentInputPosition.unsafeGet(); + } else { + term.inputPosition = currentInputPosition.unsafeGet(); + setupDisjunctionOffsets(term.parentheses.disjunction, 0, currentInputPosition.unsafeGet()); + currentCallFrameSize += YarrStackSpaceForBackTrackInfoParentheses; + } + // Fixed count of 1 could be accepted, if they have a fixed size *AND* if all alternatives are of the same length. + alternative->m_hasFixedSize = false; + break; + + case PatternTerm::TypeParentheticalAssertion: + term.inputPosition = currentInputPosition.unsafeGet(); + term.frameLocation = currentCallFrameSize; + currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition.unsafeGet()); + break; + + case PatternTerm::TypeDotStarEnclosure: + alternative->m_hasFixedSize = false; + term.inputPosition = initialInputPosition; + break; + } + } + + alternative->m_minimumSize = (currentInputPosition - initialInputPosition).unsafeGet(); + return currentCallFrameSize; + } + + unsigned setupDisjunctionOffsets(PatternDisjunction* disjunction, unsigned initialCallFrameSize, unsigned initialInputPosition) + { + if ((disjunction != m_pattern.m_body) && (disjunction->m_alternatives.size() > 1)) + initialCallFrameSize += YarrStackSpaceForBackTrackInfoAlternative; + + unsigned minimumInputSize = UINT_MAX; + unsigned maximumCallFrameSize = 0; + bool hasFixedSize = true; + + for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { + PatternAlternative* alternative = disjunction->m_alternatives[alt]; + unsigned currentAlternativeCallFrameSize = setupAlternativeOffsets(alternative, initialCallFrameSize, initialInputPosition); + minimumInputSize = std::min(minimumInputSize, alternative->m_minimumSize); + maximumCallFrameSize = std::max(maximumCallFrameSize, currentAlternativeCallFrameSize); + hasFixedSize &= alternative->m_hasFixedSize; + } + + ASSERT(minimumInputSize != UINT_MAX); + ASSERT(maximumCallFrameSize >= initialCallFrameSize); + + disjunction->m_hasFixedSize = hasFixedSize; + disjunction->m_minimumSize = minimumInputSize; + disjunction->m_callFrameSize = maximumCallFrameSize; + return maximumCallFrameSize; + } + + void setupOffsets() + { + setupDisjunctionOffsets(m_pattern.m_body, 0, 0); + } + + // This optimization identifies sets of parentheses that we will never need to backtrack. + // In these cases we do not need to store state from prior iterations. + // We can presently avoid backtracking for: + // * where the parens are at the end of the regular expression (last term in any of the + // alternatives of the main body disjunction). + // * where the parens are non-capturing, and quantified unbounded greedy (*). + // * where the parens do not contain any capturing subpatterns. + void checkForTerminalParentheses() + { + // This check is much too crude; should be just checking whether the candidate + // node contains nested capturing subpatterns, not the whole expression! + if (m_pattern.m_numSubpatterns) + return; + + Vector& alternatives = m_pattern.m_body->m_alternatives; + for (size_t i = 0; i < alternatives.size(); ++i) { + Vector& terms = alternatives[i]->m_terms; + if (terms.size()) { + PatternTerm& term = terms.last(); + if (term.type == PatternTerm::TypeParenthesesSubpattern + && term.quantityType == QuantifierGreedy + && term.quantityCount == quantifyInfinite + && !term.capture()) + term.parentheses.isTerminal = true; + } + } + } + + void optimizeBOL() + { + // Look for expressions containing beginning of line (^) anchoring and unroll them. + // e.g. /^a|^b|c/ becomes /^a|^b|c/ which is executed once followed by /c/ which loops + // This code relies on the parsing code tagging alternatives with m_containsBOL and + // m_startsWithBOL and rolling those up to containing alternatives. + // At this point, this is only valid for non-multiline expressions. + PatternDisjunction* disjunction = m_pattern.m_body; + + if (!m_pattern.m_containsBOL || m_pattern.m_multiline) + return; + + PatternDisjunction* loopDisjunction = copyDisjunction(disjunction, true); + + // Set alternatives in disjunction to "onceThrough" + for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) + disjunction->m_alternatives[alt]->setOnceThrough(); + + if (loopDisjunction) { + // Move alternatives from loopDisjunction to disjunction + for (unsigned alt = 0; alt < loopDisjunction->m_alternatives.size(); ++alt) + disjunction->m_alternatives.append(loopDisjunction->m_alternatives[alt]); + + loopDisjunction->m_alternatives.clear(); + } + } + + bool containsCapturingTerms(PatternAlternative* alternative, size_t firstTermIndex, size_t lastTermIndex) + { + Vector& terms = alternative->m_terms; + + for (size_t termIndex = firstTermIndex; termIndex <= lastTermIndex; ++termIndex) { + PatternTerm& term = terms[termIndex]; + + if (term.m_capture) + return true; + + if (term.type == PatternTerm::TypeParenthesesSubpattern) { + PatternDisjunction* nestedDisjunction = term.parentheses.disjunction; + for (unsigned alt = 0; alt < nestedDisjunction->m_alternatives.size(); ++alt) { + if (containsCapturingTerms(nestedDisjunction->m_alternatives[alt], 0, nestedDisjunction->m_alternatives[alt]->m_terms.size() - 1)) + return true; + } + } + } + + return false; + } + + // This optimization identifies alternatives in the form of + // [^].*[?].*[$] for expressions that don't have any + // capturing terms. The alternative is changed to + // followed by processing of the dot stars to find and adjust the + // beginning and the end of the match. + void optimizeDotStarWrappedExpressions() + { + Vector& alternatives = m_pattern.m_body->m_alternatives; + if (alternatives.size() != 1) + return; + + PatternAlternative* alternative = alternatives[0]; + Vector& terms = alternative->m_terms; + if (terms.size() >= 3) { + bool startsWithBOL = false; + bool endsWithEOL = false; + size_t termIndex, firstExpressionTerm, lastExpressionTerm; + + termIndex = 0; + if (terms[termIndex].type == PatternTerm::TypeAssertionBOL) { + startsWithBOL = true; + ++termIndex; + } + + PatternTerm& firstNonAnchorTerm = terms[termIndex]; + if ((firstNonAnchorTerm.type != PatternTerm::TypeCharacterClass) || (firstNonAnchorTerm.characterClass != m_pattern.newlineCharacterClass()) || !((firstNonAnchorTerm.quantityType == QuantifierGreedy) || (firstNonAnchorTerm.quantityType == QuantifierNonGreedy))) + return; + + firstExpressionTerm = termIndex + 1; + + termIndex = terms.size() - 1; + if (terms[termIndex].type == PatternTerm::TypeAssertionEOL) { + endsWithEOL = true; + --termIndex; + } + + PatternTerm& lastNonAnchorTerm = terms[termIndex]; + if ((lastNonAnchorTerm.type != PatternTerm::TypeCharacterClass) || (lastNonAnchorTerm.characterClass != m_pattern.newlineCharacterClass()) || (lastNonAnchorTerm.quantityType != QuantifierGreedy)) + return; + + lastExpressionTerm = termIndex - 1; + + if (firstExpressionTerm > lastExpressionTerm) + return; + + if (!containsCapturingTerms(alternative, firstExpressionTerm, lastExpressionTerm)) { + for (termIndex = terms.size() - 1; termIndex > lastExpressionTerm; --termIndex) + terms.remove(termIndex); + + for (termIndex = firstExpressionTerm; termIndex > 0; --termIndex) + terms.remove(termIndex - 1); + + terms.append(PatternTerm(startsWithBOL, endsWithEOL)); + + m_pattern.m_containsBOL = false; + } + } + } + +private: + YarrPattern& m_pattern; + PatternAlternative* m_alternative; + CharacterClassConstructor m_characterClassConstructor; + bool m_invertCharacterClass; + bool m_invertParentheticalAssertion; +}; + +const char* YarrPattern::compile(const String& patternString) +{ + YarrPatternConstructor constructor(*this); + + if (const char* error = parse(constructor, patternString)) + return error; + + // If the pattern contains illegal backreferences reset & reparse. + // Quoting Netscape's "What's new in JavaScript 1.2", + // "Note: if the number of left parentheses is less than the number specified + // in \#, the \# is taken as an octal escape as described in the next row." + if (containsIllegalBackReference()) { + unsigned numSubpatterns = m_numSubpatterns; + + constructor.reset(); +#if !ASSERT_DISABLED + const char* error = +#endif + parse(constructor, patternString, numSubpatterns); + + ASSERT(!error); + ASSERT(numSubpatterns == m_numSubpatterns); + } + + constructor.checkForTerminalParentheses(); + constructor.optimizeDotStarWrappedExpressions(); + constructor.optimizeBOL(); + + constructor.setupOffsets(); + + return 0; +} + +YarrPattern::YarrPattern(const String& pattern, bool ignoreCase, bool multiline, const char** error) + : m_ignoreCase(ignoreCase) + , m_multiline(multiline) + , m_containsBackreferences(false) + , m_containsBOL(false) + , m_numSubpatterns(0) + , m_maxBackReference(0) + , newlineCached(0) + , digitsCached(0) + , spacesCached(0) + , wordcharCached(0) + , nondigitsCached(0) + , nonspacesCached(0) + , nonwordcharCached(0) +{ + *error = compile(pattern); +} + +} } diff --git a/masm/yarr/YarrPattern.h b/masm/yarr/YarrPattern.h new file mode 100644 index 0000000000..14e89b8e09 --- /dev/null +++ b/masm/yarr/YarrPattern.h @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrPattern_h +#define YarrPattern_h + +#include +#include +#include +#include +#include + +namespace JSC { namespace Yarr { + +struct PatternDisjunction; + +struct CharacterRange { + UChar begin; + UChar end; + + CharacterRange(UChar begin, UChar end) + : begin(begin) + , end(end) + { + } +}; + +struct CharacterClassTable : RefCounted { + const char* m_table; + bool m_inverted; + static PassRefPtr create(const char* table, bool inverted) + { + return adoptRef(new CharacterClassTable(table, inverted)); + } + +private: + CharacterClassTable(const char* table, bool inverted) + : m_table(table) + , m_inverted(inverted) + { + } +}; + +struct CharacterClass { + WTF_MAKE_FAST_ALLOCATED; +public: + // All CharacterClass instances have to have the full set of matches and ranges, + // they may have an optional table for faster lookups (which must match the + // specified matches and ranges) + CharacterClass(PassRefPtr table) + : m_table(table) + { + } + Vector m_matches; + Vector m_ranges; + Vector m_matchesUnicode; + Vector m_rangesUnicode; + RefPtr m_table; +}; + +enum QuantifierType { + QuantifierFixedCount, + QuantifierGreedy, + QuantifierNonGreedy, +}; + +struct PatternTerm { + enum Type { + TypeAssertionBOL, + TypeAssertionEOL, + TypeAssertionWordBoundary, + TypePatternCharacter, + TypeCharacterClass, + TypeBackReference, + TypeForwardReference, + TypeParenthesesSubpattern, + TypeParentheticalAssertion, + TypeDotStarEnclosure, + } type; + bool m_capture :1; + bool m_invert :1; + union { + UChar patternCharacter; + CharacterClass* characterClass; + unsigned backReferenceSubpatternId; + struct { + PatternDisjunction* disjunction; + unsigned subpatternId; + unsigned lastSubpatternId; + bool isCopy; + bool isTerminal; + } parentheses; + struct { + bool bolAnchor : 1; + bool eolAnchor : 1; + } anchors; + }; + QuantifierType quantityType; + Checked quantityCount; + int inputPosition; + unsigned frameLocation; + + PatternTerm(UChar ch) + : type(PatternTerm::TypePatternCharacter) + , m_capture(false) + , m_invert(false) + { + patternCharacter = ch; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(CharacterClass* charClass, bool invert) + : type(PatternTerm::TypeCharacterClass) + , m_capture(false) + , m_invert(invert) + { + characterClass = charClass; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(Type type, unsigned subpatternId, PatternDisjunction* disjunction, bool capture = false, bool invert = false) + : type(type) + , m_capture(capture) + , m_invert(invert) + { + parentheses.disjunction = disjunction; + parentheses.subpatternId = subpatternId; + parentheses.isCopy = false; + parentheses.isTerminal = false; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(Type type, bool invert = false) + : type(type) + , m_capture(false) + , m_invert(invert) + { + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(unsigned spatternId) + : type(TypeBackReference) + , m_capture(false) + , m_invert(false) + { + backReferenceSubpatternId = spatternId; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(bool bolAnchor, bool eolAnchor) + : type(TypeDotStarEnclosure) + , m_capture(false) + , m_invert(false) + { + anchors.bolAnchor = bolAnchor; + anchors.eolAnchor = eolAnchor; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + static PatternTerm ForwardReference() + { + return PatternTerm(TypeForwardReference); + } + + static PatternTerm BOL() + { + return PatternTerm(TypeAssertionBOL); + } + + static PatternTerm EOL() + { + return PatternTerm(TypeAssertionEOL); + } + + static PatternTerm WordBoundary(bool invert) + { + return PatternTerm(TypeAssertionWordBoundary, invert); + } + + bool invert() + { + return m_invert; + } + + bool capture() + { + return m_capture; + } + + void quantify(unsigned count, QuantifierType type) + { + quantityCount = count; + quantityType = type; + } +}; + +struct PatternAlternative { + WTF_MAKE_FAST_ALLOCATED; +public: + PatternAlternative(PatternDisjunction* disjunction) + : m_parent(disjunction) + , m_onceThrough(false) + , m_hasFixedSize(false) + , m_startsWithBOL(false) + , m_containsBOL(false) + { + } + + PatternTerm& lastTerm() + { + ASSERT(m_terms.size()); + return m_terms[m_terms.size() - 1]; + } + + void removeLastTerm() + { + ASSERT(m_terms.size()); + m_terms.shrink(m_terms.size() - 1); + } + + void setOnceThrough() + { + m_onceThrough = true; + } + + bool onceThrough() + { + return m_onceThrough; + } + + Vector m_terms; + PatternDisjunction* m_parent; + unsigned m_minimumSize; + bool m_onceThrough : 1; + bool m_hasFixedSize : 1; + bool m_startsWithBOL : 1; + bool m_containsBOL : 1; +}; + +struct PatternDisjunction { + WTF_MAKE_FAST_ALLOCATED; +public: + PatternDisjunction(PatternAlternative* parent = 0) + : m_parent(parent) + , m_hasFixedSize(false) + { + } + + ~PatternDisjunction() + { + deleteAllValues(m_alternatives); + } + + PatternAlternative* addNewAlternative() + { + PatternAlternative* alternative = new PatternAlternative(this); + m_alternatives.append(alternative); + return alternative; + } + + Vector m_alternatives; + PatternAlternative* m_parent; + unsigned m_minimumSize; + unsigned m_callFrameSize; + bool m_hasFixedSize; +}; + +// You probably don't want to be calling these functions directly +// (please to be calling newlineCharacterClass() et al on your +// friendly neighborhood YarrPattern instance to get nicely +// cached copies). +CharacterClass* newlineCreate(); +CharacterClass* digitsCreate(); +CharacterClass* spacesCreate(); +CharacterClass* wordcharCreate(); +CharacterClass* nondigitsCreate(); +CharacterClass* nonspacesCreate(); +CharacterClass* nonwordcharCreate(); + +struct TermChain { + TermChain(PatternTerm term) + : term(term) + {} + + PatternTerm term; + Vector hotTerms; +}; + +struct YarrPattern { + JS_EXPORT_PRIVATE YarrPattern(const String& pattern, bool ignoreCase, bool multiline, const char** error); + + ~YarrPattern() + { + deleteAllValues(m_disjunctions); + deleteAllValues(m_userCharacterClasses); + } + + void reset() + { + m_numSubpatterns = 0; + m_maxBackReference = 0; + + m_containsBackreferences = false; + m_containsBOL = false; + + newlineCached = 0; + digitsCached = 0; + spacesCached = 0; + wordcharCached = 0; + nondigitsCached = 0; + nonspacesCached = 0; + nonwordcharCached = 0; + + deleteAllValues(m_disjunctions); + m_disjunctions.clear(); + deleteAllValues(m_userCharacterClasses); + m_userCharacterClasses.clear(); + } + + bool containsIllegalBackReference() + { + return m_maxBackReference > m_numSubpatterns; + } + + CharacterClass* newlineCharacterClass() + { + if (!newlineCached) + m_userCharacterClasses.append(newlineCached = newlineCreate()); + return newlineCached; + } + CharacterClass* digitsCharacterClass() + { + if (!digitsCached) + m_userCharacterClasses.append(digitsCached = digitsCreate()); + return digitsCached; + } + CharacterClass* spacesCharacterClass() + { + if (!spacesCached) + m_userCharacterClasses.append(spacesCached = spacesCreate()); + return spacesCached; + } + CharacterClass* wordcharCharacterClass() + { + if (!wordcharCached) + m_userCharacterClasses.append(wordcharCached = wordcharCreate()); + return wordcharCached; + } + CharacterClass* nondigitsCharacterClass() + { + if (!nondigitsCached) + m_userCharacterClasses.append(nondigitsCached = nondigitsCreate()); + return nondigitsCached; + } + CharacterClass* nonspacesCharacterClass() + { + if (!nonspacesCached) + m_userCharacterClasses.append(nonspacesCached = nonspacesCreate()); + return nonspacesCached; + } + CharacterClass* nonwordcharCharacterClass() + { + if (!nonwordcharCached) + m_userCharacterClasses.append(nonwordcharCached = nonwordcharCreate()); + return nonwordcharCached; + } + + bool m_ignoreCase : 1; + bool m_multiline : 1; + bool m_containsBackreferences : 1; + bool m_containsBOL : 1; + unsigned m_numSubpatterns; + unsigned m_maxBackReference; + PatternDisjunction* m_body; + Vector m_disjunctions; + Vector m_userCharacterClasses; + +private: + const char* compile(const String& patternString); + + CharacterClass* newlineCached; + CharacterClass* digitsCached; + CharacterClass* spacesCached; + CharacterClass* wordcharCached; + CharacterClass* nondigitsCached; + CharacterClass* nonspacesCached; + CharacterClass* nonwordcharCached; +}; + +} } // namespace JSC::Yarr + +#endif // YarrPattern_h diff --git a/masm/yarr/YarrSyntaxChecker.cpp b/masm/yarr/YarrSyntaxChecker.cpp new file mode 100644 index 0000000000..aa98c4a354 --- /dev/null +++ b/masm/yarr/YarrSyntaxChecker.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "YarrSyntaxChecker.h" + +#include "YarrParser.h" + +namespace JSC { namespace Yarr { + +class SyntaxChecker { +public: + void assertionBOL() {} + void assertionEOL() {} + void assertionWordBoundary(bool) {} + void atomPatternCharacter(UChar) {} + void atomBuiltInCharacterClass(BuiltInCharacterClassID, bool) {} + void atomCharacterClassBegin(bool = false) {} + void atomCharacterClassAtom(UChar) {} + void atomCharacterClassRange(UChar, UChar) {} + void atomCharacterClassBuiltIn(BuiltInCharacterClassID, bool) {} + void atomCharacterClassEnd() {} + void atomParenthesesSubpatternBegin(bool = true) {} + void atomParentheticalAssertionBegin(bool = false) {} + void atomParenthesesEnd() {} + void atomBackReference(unsigned) {} + void quantifyAtom(unsigned, unsigned, bool) {} + void disjunction() {} +}; + +const char* checkSyntax(const String& pattern) +{ + SyntaxChecker syntaxChecker; + return parse(syntaxChecker, pattern); +} + +}} // JSC::YARR diff --git a/masm/yarr/YarrSyntaxChecker.h b/masm/yarr/YarrSyntaxChecker.h new file mode 100644 index 0000000000..104ced3ab4 --- /dev/null +++ b/masm/yarr/YarrSyntaxChecker.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrSyntaxChecker_h +#define YarrSyntaxChecker_h + +#include + +namespace JSC { namespace Yarr { + +const char* checkSyntax(const String& pattern); + +}} // JSC::YARR + +#endif // YarrSyntaxChecker_h + diff --git a/masm/yarr/yarr.pri b/masm/yarr/yarr.pri new file mode 100644 index 0000000000..7e9b4d3f3b --- /dev/null +++ b/masm/yarr/yarr.pri @@ -0,0 +1,12 @@ +# ------------------------------------------------------------------- +# Project file for YARR +# +# See 'Tools/qmake/README' for an overview of the build system +# ------------------------------------------------------------------- + +SOURCES += \ + $$PWD/YarrInterpreter.cpp \ + $$PWD/YarrPattern.cpp \ + $$PWD/YarrSyntaxChecker.cpp \ + $$PWD/YarrCanonicalizeUCS2.cpp + diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 2d34b3dc8d..a1f99f52c9 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -357,17 +357,17 @@ FunctionObject *ExecutionEngine::newDateCtor(ExecutionContext *ctx) RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) { bool global = (flags & IR::RegExp::RegExp_Global); - QRegularExpression::PatternOptions options = 0; + bool ignoreCase = false; + bool multiline = false; if (flags & IR::RegExp::RegExp_IgnoreCase) - options |= QRegularExpression::CaseInsensitiveOption; + ignoreCase = true; if (flags & IR::RegExp::RegExp_Multiline) - options |= QRegularExpression::MultilineOption; + multiline = true; - QRegularExpression re(pattern, options); - return newRegExpObject(re, global); + return newRegExpObject(RegExp::create(this, pattern, ignoreCase, multiline), global); } -RegExpObject *ExecutionEngine::newRegExpObject(const QRegularExpression &re, bool global) +RegExpObject *ExecutionEngine::newRegExpObject(PassRefPtr re, bool global) { RegExpObject *object = new (memoryManager) RegExpObject(re, global); object->prototype = regExpPrototype; diff --git a/qmljs_engine.h b/qmljs_engine.h index 191268f4c4..83e8e62bb8 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -46,6 +46,9 @@ #include #include +#include +#include + namespace QQmlJS { namespace Debugging { @@ -88,12 +91,15 @@ struct SyntaxErrorPrototype; struct TypeErrorPrototype; struct URIErrorPrototype; +class RegExp; + struct ExecutionEngine { MemoryManager *memoryManager; EvalISelFactory *iselFactory; ExecutionContext *current; ExecutionContext *rootContext; + WTF::BumpPointerAllocator bumperPointerAllocator; // Used by Yarr Regex engine. Debugging::Debugger *debugger; @@ -195,7 +201,7 @@ struct ExecutionEngine FunctionObject *newDateCtor(ExecutionContext *ctx); RegExpObject *newRegExpObject(const QString &pattern, int flags); - RegExpObject *newRegExpObject(const QRegularExpression &re, bool global); + RegExpObject *newRegExpObject(PassRefPtr re, bool global); FunctionObject *newRegExpCtor(ExecutionContext *ctx); Object *newErrorObject(const Value &value); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index fbc976d54b..fee5378421 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -1114,13 +1114,13 @@ Value RegExpObject::__get__(ExecutionContext *ctx, String *name, bool *hasProper QString n = name->toQString(); Value v = Value::undefinedValue(); if (n == QLatin1String("source")) - v = Value::fromString(ctx, value.pattern()); + v = Value::fromString(ctx, value->pattern()); else if (n == QLatin1String("global")) v = Value::fromBoolean(global); else if (n == QLatin1String("ignoreCase")) - v = Value::fromBoolean(value.patternOptions() & QRegularExpression::CaseInsensitiveOption); + v = Value::fromBoolean(value->ignoreCase()); else if (n == QLatin1String("multiline")) - v = Value::fromBoolean(value.patternOptions() & QRegularExpression::MultilineOption); + v = Value::fromBoolean(value->multiLine()); else if (n == QLatin1String("lastIndex")) v = lastIndex; if (v.type() != Value::Undefined_Type) { @@ -1128,7 +1128,6 @@ Value RegExpObject::__get__(ExecutionContext *ctx, String *name, bool *hasProper *hasProperty = true; return v; } - return Object::__get__(ctx, name, hasProperty); } diff --git a/qmljs_objects.h b/qmljs_objects.h index 93796622fd..d00486816f 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -52,10 +52,10 @@ #include "qv4propertydescriptor.h" #include "qv4propertytable.h" #include "qv4objectiterator.h" +#include "qv4regexp.h" #include #include -#include #include #include #include @@ -362,10 +362,10 @@ struct IsFiniteFunction: FunctionObject }; struct RegExpObject: Object { - QRegularExpression value; + RefPtr value; Value lastIndex; bool global; - RegExpObject(const QRegularExpression &value, bool global): value(value), lastIndex(Value::fromInt32(0)), global(global) {} + RegExpObject(PassRefPtr value, bool global): value(value), lastIndex(Value::fromInt32(0)), global(global) {} virtual QString className() { return QStringLiteral("RegExp"); } virtual RegExpObject *asRegExpObject() { return this; } virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 9310676603..3392fae6ce 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include @@ -2846,25 +2845,26 @@ Value RegExpCtor::construct(ExecutionContext *ctx) r = __qmljs_to_string(r, ctx); bool global = false; - QRegularExpression::PatternOptions options = QRegularExpression::NoPatternOption; + bool ignoreCase = false; + bool multiLine = false; if (!f.isUndefined()) { f = __qmljs_to_string(f, ctx); QString str = f.stringValue()->toQString(); for (int i = 0; i < str.length(); ++i) { if (str.at(i) == QChar('g') && !global) { global = true; - } else if (str.at(i) == QChar('i') && !(options & QRegularExpression::CaseInsensitiveOption)) { - options |= QRegularExpression::CaseInsensitiveOption; - } else if (str.at(i) == QChar('m') && !(options & QRegularExpression::MultilineOption)) { - options |= QRegularExpression::MultilineOption; + } else if (str.at(i) == QChar('i') && !ignoreCase) { + ignoreCase = true; + } else if (str.at(i) == QChar('m') && !multiLine) { + multiLine = true; } else { ctx->throwTypeError(); } } } - QRegularExpression re(r.stringValue()->toQString(), options); - if (!re.isValid()) + RefPtr re = RegExp::create(ctx->engine, r.stringValue()->toQString(), ignoreCase, multiLine); + if (!re->isValid()) ctx->throwTypeError(); RegExpObject *o = ctx->engine->newRegExpObject(re, global); @@ -2905,21 +2905,25 @@ Value RegExpPrototype::method_exec(ExecutionContext *ctx) if (offset < 0 || offset > s.length()) return Value::nullValue(); - QRegularExpressionMatch match = r->value.match(s, offset); - if (!match.hasMatch()) + uint* matchOffsets = (uint*)alloca(r->value->captureCount() * 2 * sizeof(uint)); + int result = r->value->match(s, offset, matchOffsets); + if (result == -1) return Value::nullValue(); // fill in result data ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); - int captured = match.lastCapturedIndex(); - for (int i = 0; i <= captured; ++i) - array->array.push_back(Value::fromString(ctx, match.captured(i))); + for (int i = 0; i < r->value->captureCount(); ++i) { + int start = matchOffsets[i * 2]; + int end = matchOffsets[i * 2 + 1]; + if (start != -1 && end != -1) + array->array.push_back(Value::fromString(ctx, s.mid(start, end - start))); + } - array->__put__(ctx, QLatin1String("index"), Value::fromInt32(match.capturedStart(0))); + array->__put__(ctx, QLatin1String("index"), Value::fromInt32(result)); array->__put__(ctx, QLatin1String("input"), arg); if (r->global) - r->lastIndex = Value::fromInt32(match.capturedEnd(0)); + r->lastIndex = Value::fromInt32(matchOffsets[1]); return Value::fromObject(array); } @@ -2936,13 +2940,12 @@ Value RegExpPrototype::method_toString(ExecutionContext *ctx) if (!r) ctx->throwTypeError(); - QString result = QChar('/') + r->value.pattern(); + QString result = QChar('/') + r->value->pattern(); result += QChar('/'); - QRegularExpression::PatternOptions o = r->value.patternOptions(); // ### 'g' option missing - if (o & QRegularExpression::CaseInsensitiveOption) + if (r->value->ignoreCase()) result += QChar('i'); - if (o & QRegularExpression::MultilineOption) + if (r->value->multiLine()) result += QChar('m'); return Value::fromString(ctx, result); } diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 35a3f6529e..0576eb45f3 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -295,7 +295,7 @@ struct RegExpCtor: FunctionObject struct RegExpPrototype: RegExpObject { - RegExpPrototype(): RegExpObject(QRegularExpression(), false) {} + RegExpPrototype(): RegExpObject(RegExp::create(0, QString()), false) {} void init(ExecutionContext *ctx, const Value &ctor); static Value method_exec(ExecutionContext *ctx); diff --git a/qv4regexp.cpp b/qv4regexp.cpp new file mode 100644 index 0000000000..64d9fef942 --- /dev/null +++ b/qv4regexp.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4regexp.h" + +#include "qmljs_engine.h" + +namespace QQmlJS { +namespace VM { + +int RegExp::match(const QString &string, int start, uint *matchOffsets) +{ + if (!isValid()) + return JSC::Yarr::offsetNoMatch; + + return JSC::Yarr::interpret(m_byteCode.get(), WTF::String(string).characters16(), string.length(), start, matchOffsets); +} + +RegExp::RegExp(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline) + : m_pattern(pattern) + , m_subPatternCount(0) + , m_ignoreCase(ignoreCase) + , m_multiLine(multiline) +{ + if (!engine) + return; + const char* error = 0; + JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiline, &error); + if (error) + return; + m_subPatternCount = yarrPattern.m_numSubpatterns; + m_byteCode = JSC::Yarr::byteCompile(yarrPattern, &engine->bumperPointerAllocator); +} + +} // end of namespace VM +} // end of namespace QQmlJS + + diff --git a/qv4regexp.h b/qv4regexp.h new file mode 100644 index 0000000000..3b7a9804be --- /dev/null +++ b/qv4regexp.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4REGEXP_H +#define QV4REGEXP_H + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +namespace QQmlJS { +namespace VM { + +struct ExecutionEngine; + +class RegExp : public RefCounted +{ +public: + static PassRefPtr create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false) + { return adoptRef(new RegExp(engine, pattern, ignoreCase, multiline)); } + + QString pattern() const { return m_pattern; } + + bool isValid() const { return m_byteCode.get(); } + + int match(const QString& string, int start, uint *matchOffsets); + + bool ignoreCase() const { return m_ignoreCase; } + bool multiLine() const { return m_multiLine; } + int captureCount() const { return m_subPatternCount + 1; } + +private: + Q_DISABLE_COPY(RegExp); + RegExp(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline); + + QString m_pattern; + OwnPtr m_byteCode; + int m_subPatternCount; + bool m_ignoreCase; + bool m_multiLine; +}; + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4REGEXP_H diff --git a/tests/TestExpectations b/tests/TestExpectations index 839ba7c3a2..56b55097f6 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -392,9 +392,6 @@ S15.10.2.3_A1_T17 failing S15.10.2.3_A1_T2 failing S15.10.2.5_A1_T4 failing S15.10.2.12_A6_T1 failing -S15.10.2.13_A2_T1 failing -S15.10.2.13_A2_T2 failing -S15.10.2.13_A2_T8 failing S15.10.2.15_A1_T1 failing S15.10.2.15_A1_T10 failing S15.10.2.15_A1_T11 failing @@ -444,19 +441,8 @@ S15.10.1_A1_T9 failing 15.10.2.15-6-1 failing 15.10.2.2-1 failing 15.10.2.5-3-1 failing -S15.10.2.10_A2.1_T3 failing -S15.10.2.10_A4.1_T1 failing -S15.10.2.10_A4.1_T2 failing -S15.10.2.10_A4.1_T3 failing -S15.10.2.10_A5.1_T1 failing -S15.10.2.11_A1_T5 failing -S15.10.2.11_A1_T7 failing S15.10.2.12_A1_T1 failing -S15.10.2.12_A1_T2 failing -S15.10.2.12_A1_T5 failing S15.10.2.12_A2_T1 failing -S15.10.2.12_A2_T2 failing -S15.10.2.12_A2_T5 failing S15.10.2.12_A3_T1 failing S15.10.2.12_A4_T1 failing S15.10.2.12_A5_T1 failing @@ -484,7 +470,6 @@ S15.10.4.1_A5_T6 failing S15.10.4.1_A5_T7 failing S15.10.4.1_A5_T8 failing S15.10.4.1_A5_T9 failing -S15.10.4.1_A8_T2 failing S15.10.4.1_A9_T1 failing S15.10.4.1_A9_T2 failing S15.10.4.1_A9_T3 failing @@ -2808,4 +2793,3 @@ S15.4.4.13_A1_T2 failing 15.4.4.14-9-b-i-5 failing 15.4.4.16-7-c-i-6 failing 15.4.4.17-7-c-i-6 failing - diff --git a/v4.pro b/v4.pro index 2132f12664..7213e6c7ad 100644 --- a/v4.pro +++ b/v4.pro @@ -28,7 +28,8 @@ SOURCES += main.cpp \ qv4managed.cpp \ qv4array.cpp \ qv4string.cpp \ - qv4objectiterator.cpp + qv4objectiterator.cpp \ + qv4regexp.cpp HEADERS += \ qv4codegen_p.h \ @@ -51,7 +52,8 @@ HEADERS += \ qv4string.h \ qv4propertydescriptor.h \ qv4propertytable.h \ - qv4objectiterator.h + qv4objectiterator.h \ + qv4regexp.h llvm { -- cgit v1.2.3 From 790156d2a90e2fbe532d43a616704ec161b6bfb4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 Jan 2013 14:08:17 +0100 Subject: Fix exception handling of RegExp constructor Invalid regular expressions should throw a syntax error instead of a type error. Change-Id: Ic7357f423703646a3941aa780d05f81faefec791 Reviewed-by: Lars Knoll --- qv4ecmaobjects.cpp | 4 +-- tests/TestExpectations | 77 +------------------------------------------------- 2 files changed, 3 insertions(+), 78 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 3392fae6ce..3f1ad3cde0 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2858,14 +2858,14 @@ Value RegExpCtor::construct(ExecutionContext *ctx) } else if (str.at(i) == QChar('m') && !multiLine) { multiLine = true; } else { - ctx->throwTypeError(); + ctx->throwSyntaxError(0); } } } RefPtr re = RegExp::create(ctx->engine, r.stringValue()->toQString(), ignoreCase, multiLine); if (!re->isValid()) - ctx->throwTypeError(); + ctx->throwSyntaxError(0); RegExpObject *o = ctx->engine->newRegExpObject(re, global); return Value::fromObject(o); diff --git a/tests/TestExpectations b/tests/TestExpectations index 56b55097f6..7284105e0b 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -377,70 +377,11 @@ S15.1.2.2_A9.2 failing S15.1.2.2_A9.3 failing S15.1.2.2_A9.4 failing S15.1.2.2_A9.7 failing -S15.10.2.15_A1_T38 failing -S15.10.2.15_A1_T39 failing -S15.10.2.15_A1_T4 failing -S15.10.2.15_A1_T40 failing -S15.10.2.15_A1_T41 failing -S15.10.2.15_A1_T5 failing -S15.10.2.15_A1_T6 failing -S15.10.2.15_A1_T7 failing -S15.10.2.15_A1_T8 failing -S15.10.2.15_A1_T9 failing S15.10.2.3_A1_T15 failing S15.10.2.3_A1_T17 failing S15.10.2.3_A1_T2 failing S15.10.2.5_A1_T4 failing S15.10.2.12_A6_T1 failing -S15.10.2.15_A1_T1 failing -S15.10.2.15_A1_T10 failing -S15.10.2.15_A1_T11 failing -S15.10.2.15_A1_T12 failing -S15.10.2.15_A1_T13 failing -S15.10.2.15_A1_T14 failing -S15.10.2.15_A1_T15 failing -S15.10.2.15_A1_T16 failing -S15.10.2.15_A1_T17 failing -S15.10.2.15_A1_T18 failing -S15.10.2.15_A1_T19 failing -S15.10.2.15_A1_T2 failing -S15.10.2.15_A1_T20 failing -S15.10.2.15_A1_T21 failing -S15.10.2.15_A1_T22 failing -S15.10.2.15_A1_T23 failing -S15.10.2.15_A1_T24 failing -S15.10.2.15_A1_T25 failing -S15.10.2.15_A1_T26 failing -S15.10.2.15_A1_T27 failing -S15.10.2.15_A1_T28 failing -S15.10.2.15_A1_T29 failing -S15.10.2.15_A1_T3 failing -S15.10.2.15_A1_T30 failing -S15.10.2.15_A1_T31 failing -S15.10.2.15_A1_T32 failing -S15.10.2.15_A1_T33 failing -S15.10.2.15_A1_T34 failing -S15.10.2.15_A1_T35 failing -S15.10.2.15_A1_T36 failing -S15.10.2.15_A1_T37 failing -S15.10.1_A1_T12 failing -S15.10.1_A1_T13 failing -S15.10.1_A1_T14 failing -S15.10.1_A1_T15 failing -S15.10.1_A1_T16 failing -S15.10.1_A1_T2 failing -S15.10.1_A1_T3 failing -S15.10.1_A1_T4 failing -S15.10.1_A1_T5 failing -S15.10.1_A1_T6 failing -S15.10.1_A1_T7 failing -S15.10.1_A1_T8 failing -S15.10.1_A1_T9 failing -15.10.2.15-3-1 failing -15.10.2.15-3-2 failing -15.10.2.15-6-1 failing -15.10.2.2-1 failing -15.10.2.5-3-1 failing S15.10.2.12_A1_T1 failing S15.10.2.12_A2_T1 failing S15.10.2.12_A3_T1 failing @@ -462,19 +403,6 @@ S15.10.2.8_A3_T27 failing S15.10.2.8_A3_T28 failing S15.10.2.8_A3_T29 failing S15.10.2.9_A1_T2 failing -S15.10.4.1_A5_T1 failing -S15.10.4.1_A5_T2 failing -S15.10.4.1_A5_T3 failing -S15.10.4.1_A5_T4 failing -S15.10.4.1_A5_T6 failing -S15.10.4.1_A5_T7 failing -S15.10.4.1_A5_T8 failing -S15.10.4.1_A5_T9 failing -S15.10.4.1_A9_T1 failing -S15.10.4.1_A9_T2 failing -S15.10.4.1_A9_T3 failing -15.10.4.1-2 failing -15.10.4.1-3 failing S15.10.5_A1 failing S15.10.6.2_A1_T2 failing S15.10.6.2_A1_T6 failing @@ -692,9 +620,6 @@ S15.1.3.4_A5.4 failing S15.1.3.4_A5.6 failing S15.1.3.4_A5.7 failing S15.1.3.4_A6_T1 failing -S15.10.1_A1_T1 failing -S15.10.1_A1_T10 failing -S15.10.1_A1_T11 failing 15.2.3.12-3-27 failing 15.2.3.13-2-12 failing 15.2.3.2-2-18 failing @@ -2792,4 +2717,4 @@ S15.4.4.13_A1_T2 failing # Bugs in Array.prototype 15.4.4.14-9-b-i-5 failing 15.4.4.16-7-c-i-6 failing -15.4.4.17-7-c-i-6 failing +15.4.4.17-7-c-i-6 failing \ No newline at end of file -- cgit v1.2.3 From 1197d224fb741706ade9c2da38fb9a0557c94a8e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 Jan 2013 14:28:31 +0100 Subject: Fix entries for non-matches in the results array of RegExp.exec A capture that failed to match should result an entry in the results array with value undefined. Change-Id: I08fedfc59cda752b209e79ff8abf7ee105a0c527 Reviewed-by: Lars Knoll --- qv4ecmaobjects.cpp | 4 +++- tests/TestExpectations | 17 ----------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 3f1ad3cde0..61abadc90f 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2915,8 +2915,10 @@ Value RegExpPrototype::method_exec(ExecutionContext *ctx) for (int i = 0; i < r->value->captureCount(); ++i) { int start = matchOffsets[i * 2]; int end = matchOffsets[i * 2 + 1]; + Value entry = Value::undefinedValue(); if (start != -1 && end != -1) - array->array.push_back(Value::fromString(ctx, s.mid(start, end - start))); + entry = Value::fromString(ctx, s.mid(start, end - start)); + array->array.push_back(entry); } array->__put__(ctx, QLatin1String("index"), Value::fromInt32(result)); diff --git a/tests/TestExpectations b/tests/TestExpectations index 7284105e0b..499caca091 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -377,35 +377,18 @@ S15.1.2.2_A9.2 failing S15.1.2.2_A9.3 failing S15.1.2.2_A9.4 failing S15.1.2.2_A9.7 failing -S15.10.2.3_A1_T15 failing -S15.10.2.3_A1_T17 failing -S15.10.2.3_A1_T2 failing -S15.10.2.5_A1_T4 failing S15.10.2.12_A6_T1 failing S15.10.2.12_A1_T1 failing S15.10.2.12_A2_T1 failing S15.10.2.12_A3_T1 failing S15.10.2.12_A4_T1 failing S15.10.2.12_A5_T1 failing -S15.10.2.7_A5_T2 failing -S15.10.2.8_A1_T4 failing -S15.10.2.8_A2_T1 failing S15.10.2.8_A3_T15 failing S15.10.2.8_A3_T16 failing S15.10.2.8_A3_T17 failing S15.10.2.8_A3_T18 failing -S15.10.2.8_A3_T19 failing -S15.10.2.8_A3_T2 failing -S15.10.2.8_A3_T22 failing -S15.10.2.8_A3_T25 failing -S15.10.2.8_A3_T26 failing -S15.10.2.8_A3_T27 failing -S15.10.2.8_A3_T28 failing -S15.10.2.8_A3_T29 failing -S15.10.2.9_A1_T2 failing S15.10.5_A1 failing S15.10.6.2_A1_T2 failing -S15.10.6.2_A1_T6 failing S15.10.6.2_A4_T1 failing S15.10.6.2_A4_T10 failing S15.10.6.2_A4_T11 failing -- cgit v1.2.3 From f7168afb4e907a347dd1e1ff9e3547876aa93079 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 Jan 2013 14:38:01 +0100 Subject: Fix inplace add for strings Lhs and rhs got fixed up, breaking simple string addition: a = "a" b = "b" a += b would produce "ba" instead of "ab" Change-Id: I0f666cace13de8f6ec5e4d4537fc97b3d25989ed Reviewed-by: Lars Knoll --- qmljs_environment.cpp | 4 ++-- tests/TestExpectations | 60 -------------------------------------------------- 2 files changed, 2 insertions(+), 62 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 15da3739ca..3ab9ff593a 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -352,8 +352,8 @@ Value ExecutionContext::getPropertyNoThrow(String *name) void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) { - Value rhs = getProperty(name); - value = op(value, rhs, this); + Value lhs = getProperty(name); + value = op(lhs, value, this); setProperty(name, value); } diff --git a/tests/TestExpectations b/tests/TestExpectations index 499caca091..755d99eef6 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -34,16 +34,6 @@ S11.10.1_A2.4_T1 failing S11.10.1_A2.4_T3 failing S11.10.2_A2.4_T1 failing S11.10.2_A2.4_T3 failing -S11.13.2_A2.1_T1.7 failing -S11.13.2_A2.1_T1.8 failing -S11.13.2_A3.1_T3 failing -S11.13.2_A3.1_T5 failing -S11.13.2_A3.1_T7 failing -S11.13.2_A3.1_T8 failing -S11.13.2_A3.2_T3 failing -S11.13.2_A3.2_T5 failing -S11.13.2_A3.2_T7 failing -S11.13.2_A3.2_T8 failing S11.10.3_A2.4_T1 failing S11.10.3_A2.4_T3 failing S11.11.1_A2.1_T1 failing @@ -91,48 +81,11 @@ S11.11.1_A3_T4 failing 11.13.2-6-7-s failing 11.13.2-6-8-s failing 11.13.2-6-9-s failing -S11.13.2_A1_T5 failing -S11.13.2_A2.1_T1.3 failing S11.13.2_A4.2_T1.4 failing -S11.13.2_A4.2_T2.3 failing -S11.13.2_A4.2_T2.7 failing -S11.13.2_A4.2_T2.9 failing S11.13.2_A4.3_T1.4 failing S11.13.2_A4.3_T2.3 failing S11.13.2_A4.3_T2.7 failing S11.13.2_A4.3_T2.9 failing -S11.13.2_A4.4_T1.4 failing -S11.13.2_A4.4_T2.6 failing -S11.13.2_A4.4_T2.7 failing -S11.13.2_A4.4_T2.8 failing -S11.13.2_A4.4_T2.9 failing -S11.13.2_A4.5_T2.3 failing -S11.13.2_A4.5_T2.7 failing -S11.13.2_A4.5_T2.9 failing -S11.13.2_A4.6_T1.3 failing -S11.13.2_A4.6_T2.2 failing -S11.13.2_A4.6_T2.3 failing -S11.13.2_A4.6_T2.4 failing -S11.13.2_A4.6_T2.6 failing -S11.13.2_A4.6_T2.7 failing -S11.13.2_A4.6_T2.8 failing -S11.13.2_A4.6_T2.9 failing -S11.13.2_A4.7_T1.3 failing -S11.13.2_A4.7_T2.2 failing -S11.13.2_A4.7_T2.3 failing -S11.13.2_A4.7_T2.4 failing -S11.13.2_A4.7_T2.6 failing -S11.13.2_A4.7_T2.7 failing -S11.13.2_A4.7_T2.8 failing -S11.13.2_A4.7_T2.9 failing -S11.13.2_A4.8_T1.3 failing -S11.13.2_A4.8_T2.2 failing -S11.13.2_A4.8_T2.3 failing -S11.13.2_A4.8_T2.4 failing -S11.13.2_A4.8_T2.6 failing -S11.13.2_A4.8_T2.7 failing -S11.13.2_A4.8_T2.8 failing -S11.13.2_A4.8_T2.9 failing S11.2.1_A3_T1 failing S11.2.1_A3_T2 failing S11.2.1_A3_T3 failing @@ -309,16 +262,6 @@ S12.6.1_A8 failing 12.2.1-7-s failing 12.2.1-8-s failing S12.6.2_A8 failing -S12.6.3_A10.1 failing -S12.6.3_A10 failing -S12.6.3_A11.1_T1 failing -S12.6.3_A11.1_T2 failing -S12.6.3_A11_T1 failing -S12.6.3_A11_T2 failing -S12.6.3_A12.1_T1 failing -S12.6.3_A12.1_T2 failing -S12.6.3_A12_T1 failing -S12.6.3_A12_T2 failing 13.0-10-s failing 13.0-11-s failing 13.0-13-s failing @@ -383,9 +326,6 @@ S15.10.2.12_A2_T1 failing S15.10.2.12_A3_T1 failing S15.10.2.12_A4_T1 failing S15.10.2.12_A5_T1 failing -S15.10.2.8_A3_T15 failing -S15.10.2.8_A3_T16 failing -S15.10.2.8_A3_T17 failing S15.10.2.8_A3_T18 failing S15.10.5_A1 failing S15.10.6.2_A1_T2 failing -- cgit v1.2.3 From 970c08a9fc2cf12cf0d05b22935f54e605aa6896 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 Jan 2013 14:44:47 +0100 Subject: Fix length property of RegExp constructor Change-Id: Ia53711914957ed2f6b02dc678dc98161177c4f90 Reviewed-by: Lars Knoll --- qv4ecmaobjects.cpp | 2 +- tests/TestExpectations | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 61abadc90f..d898783641 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2884,7 +2884,7 @@ Value RegExpCtor::call(ExecutionContext *ctx) void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(2)); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 1); defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 1); diff --git a/tests/TestExpectations b/tests/TestExpectations index 755d99eef6..e00b0bbc57 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -327,7 +327,6 @@ S15.10.2.12_A3_T1 failing S15.10.2.12_A4_T1 failing S15.10.2.12_A5_T1 failing S15.10.2.8_A3_T18 failing -S15.10.5_A1 failing S15.10.6.2_A1_T2 failing S15.10.6.2_A4_T1 failing S15.10.6.2_A4_T10 failing -- cgit v1.2.3 From ecb3f397b6c7d0fc49b85b36789834721cd05304 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 16 Jan 2013 16:29:28 +0100 Subject: Fix interpreter regression in callBuiltinInvalid. Also changed the method signature to be more precise and less error-prone. Change-Id: I131a0cf90167743341faa5de3e70815c90ccd989 Reviewed-by: Simon Hausmann --- moth/qv4isel_moth.cpp | 4 ++-- moth/qv4isel_moth_p.h | 2 +- qv4isel_llvm.cpp | 2 +- qv4isel_llvm_p.h | 2 +- qv4isel_masm.cpp | 2 +- qv4isel_masm_p.h | 2 +- qv4isel_p.cpp | 2 +- qv4isel_p.h | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 12675344b4..db8d61ce15 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -692,12 +692,12 @@ void InstructionSelection::visitRet(IR::Ret *s) addInstruction(ret); } -void InstructionSelection::callBuiltinInvalid(IR::Expr *func, IR::ExprList *args, IR::Temp *result) +void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) { const int scratchIndex = scratchTempIndex(); Instruction::LoadName load; - load.name = engine()->newString(*func->asCall()->base->asName()->id); + load.name = engine()->newString(*func->id); load.targetTempIndex = scratchIndex; addInstruction(load); diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 58c7969f10..9a893a2d01 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -24,7 +24,7 @@ protected: virtual void visitCJump(IR::CJump *); virtual void visitRet(IR::Ret *); - virtual void callBuiltinInvalid(IR::Expr *func, IR::ExprList *args, IR::Temp *result); + virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result); virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result); virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 269dabd9c6..cc39ed8798 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -300,7 +300,7 @@ void InstructionSelection::buildLLVMModule(IR::Module *module, llvm::Module *llv qSwap(_llvmModule, llvmModule); } -void InstructionSelection::callBuiltinInvalid(IR::Expr *func, IR::ExprList *args, IR::Temp *result) +void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) { // TODO assert(!"TODO!"); diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 1cd1004c73..c41eead47c 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -70,7 +70,7 @@ public: void buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm); public: // methods from InstructionSelection: - virtual void callBuiltinInvalid(IR::Expr *func, IR::ExprList *args, IR::Temp *result); + virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result); virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result); virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 1c78dc60e8..cbaf24c7e1 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -428,7 +428,7 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) _asm = oldAssembler; } -void InstructionSelection::callBuiltinInvalid(IR::Expr *func, IR::ExprList *args, IR::Temp *result) +void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) { callRuntimeMethod(result, __qmljs_call_activation_property, func, args); } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index e1eb16b3ed..bf78cfca9c 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -652,7 +652,7 @@ public: virtual void run(VM::Function *vmFunction, IR::Function *function); protected: - virtual void callBuiltinInvalid(IR::Expr *func, IR::ExprList *args, IR::Temp *result); + virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result); virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result); virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 3ced5784d0..42ffbcf077 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -224,7 +224,7 @@ void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result) switch (baseName->builtin) { case IR::Name::builtin_invalid: - callBuiltinInvalid(call->base, call->args, result); + callBuiltinInvalid(baseName, call->args, result); return; case IR::Name::builtin_typeof: { diff --git a/qv4isel_p.h b/qv4isel_p.h index 275c3ecefe..810443c948 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -80,7 +80,7 @@ public: // visitor methods for StmtVisitor: virtual void visitExp(IR::Exp *s); public: // to implement by subclasses: - virtual void callBuiltinInvalid(IR::Expr *func, IR::ExprList *args, IR::Temp *result) = 0; + virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) = 0; virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0; virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0; virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result) = 0; -- cgit v1.2.3 From 47a2b31bc982cf75e98d9fbef0abf98ec68b6eab Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 16 Jan 2013 16:48:48 +0100 Subject: Fix prepareCallArg and prepareCallArgs. Change-Id: Id8b1e16de2a572e2c6e4dfe4d55adcbd63a92fa1 Reviewed-by: Simon Hausmann --- moth/qv4isel_moth.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index db8d61ce15..db5d774fcf 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -606,9 +606,9 @@ void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR: void InstructionSelection::prepareCallArg(IR::Expr *e, quint32 &argc, quint32 &args) { - // We pass single arguments as references to the stack, but only if it's not a local or an argument. - argc = 1; - args = e->asTemp()->index; + IR::ExprList exprs; + exprs.init(e); + prepareCallArgs(&exprs, argc, args); } void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 &args) @@ -626,7 +626,8 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint if (singleArgIsTemp) { // We pass single arguments as references to the stack, but only if it's not a local or an argument. - prepareCallArg(e->expr, argc, args); + argc = 1; + args = e->expr->asTemp()->index; } else if (e) { // We need to move all the temps into the function arg array int argLocation = outgoingArgumentTempStart(); @@ -642,6 +643,9 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint ++argc; e = e->next; } + } else { + argc = 0; + args = 0; } } -- cgit v1.2.3 From ee430036fbc32ccb1e026faa0582f1fba7dd0e58 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 16 Jan 2013 16:55:42 +0100 Subject: Changed signature of constructActivationProperty. Change-Id: Ic4aa2b85dec0a732382dd5ae5a7e37d54476db3a Reviewed-by: Simon Hausmann --- moth/qv4isel_moth.cpp | 8 +++++--- moth/qv4isel_moth_p.h | 2 +- qv4isel_llvm.cpp | 4 +++- qv4isel_llvm_p.h | 2 +- qv4isel_masm.cpp | 7 +++---- qv4isel_masm_p.h | 2 +- qv4isel_p.cpp | 4 ++-- qv4isel_p.h | 2 +- 8 files changed, 17 insertions(+), 14 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index db5d774fcf..50c4c2b661 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -353,11 +353,13 @@ void InstructionSelection::callProperty(IR::Call *c, IR::Temp *result) addInstruction(call); } -void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) +void InstructionSelection::constructActivationProperty(IR::Name *func, + IR::ExprList *args, + IR::Temp *result) { Instruction::CreateActivationProperty create; - create.name = engine()->newString(*call->base->asName()->id); - prepareCallArgs(call->args, create.argc, create.args); + create.name = engine()->newString(*func->id); + prepareCallArgs(args, create.argc, create.args); create.targetTempIndex = result->index; addInstruction(create); } diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 9a893a2d01..f02914fea6 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -47,7 +47,7 @@ protected: virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); virtual void callValue(IR::Call *c, IR::Temp *result); virtual void callProperty(IR::Call *c, IR::Temp *result); - virtual void constructActivationProperty(IR::New *call, IR::Temp *result); + virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); virtual void constructProperty(IR::New *call, IR::Temp *result); virtual void constructValue(IR::New *call, IR::Temp *result); virtual void loadThisObject(IR::Temp *temp); diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index cc39ed8798..a4a20a5cd8 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -462,7 +462,9 @@ void InstructionSelection::callProperty(IR::Call *c, IR::Temp *temp) Q_UNREACHABLE(); } -void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) +void InstructionSelection::constructActivationProperty(IR::Name *func, + IR::ExprList *args, + IR::Temp *result) { // TODO assert(!"TODO!"); diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index c41eead47c..777bfcd460 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -93,7 +93,7 @@ public: // methods from InstructionSelection: virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); virtual void callValue(IR::Call *c, IR::Temp *temp); virtual void callProperty(IR::Call *c, IR::Temp *temp); - virtual void constructActivationProperty(IR::New *call, IR::Temp *result); + virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); virtual void constructProperty(IR::New *call, IR::Temp *result); virtual void constructValue(IR::New *call, IR::Temp *result); virtual void loadThisObject(IR::Temp *temp); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index cbaf24c7e1..73a8c6278a 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -735,12 +735,11 @@ String *InstructionSelection::identifier(const QString &s) return engine()->identifier(s); } -void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) +void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result) { - IR::Name *baseName = call->base->asName(); - assert(baseName != 0); + assert(func != 0); - callRuntimeMethod(result, __qmljs_construct_activation_property, call->base, call->args); + callRuntimeMethod(result, __qmljs_construct_activation_property, func, args); } void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index bf78cfca9c..6bc0420b8e 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -722,7 +722,7 @@ protected: } VM::String *identifier(const QString &s); - virtual void constructActivationProperty(IR::New *call, IR::Temp *result); + virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); virtual void constructProperty(IR::New *ctor, IR::Temp *result); virtual void constructValue(IR::New *call, IR::Temp *result); diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 42ffbcf077..1f564ef3a7 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -98,8 +98,8 @@ void InstructionSelection::visitMove(IR::Move *s) initClosure(clos, t); return; } else if (IR::New *ctor = s->source->asNew()) { - if (ctor->base->asName()) { - constructActivationProperty(ctor, t); + if (Name *func = ctor->base->asName()) { + constructActivationProperty(func, ctor->args, t); return; } else if (ctor->base->asMember()) { constructProperty(ctor, t); diff --git a/qv4isel_p.h b/qv4isel_p.h index 810443c948..ca5eb69d66 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -103,7 +103,7 @@ public: // to implement by subclasses: virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) = 0; virtual void callValue(IR::Call *c, IR::Temp *temp) = 0; virtual void callProperty(IR::Call *c, IR::Temp *temp) = 0; - virtual void constructActivationProperty(IR::New *call, IR::Temp *result) = 0; + virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result) = 0; virtual void constructProperty(IR::New *ctor, IR::Temp *result) = 0; virtual void constructValue(IR::New *call, IR::Temp *result) = 0; virtual void loadThisObject(IR::Temp *temp) = 0; -- cgit v1.2.3 From 12d89c59b47b1d618dbd16aef3bbb344c8f42707 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 16 Jan 2013 17:56:29 +0100 Subject: Implement Array.prototype.splice Change-Id: I678e2836b677066b42a05f5b7b5b30eca7e8120c Reviewed-by: Simon Hausmann --- qv4array.cpp | 29 ------------------- qv4array.h | 3 +- qv4ecmaobjects.cpp | 75 ++++++++++++++++++++++++++++++++++++++------------ tests/TestExpectations | 39 +------------------------- 4 files changed, 61 insertions(+), 85 deletions(-) diff --git a/qv4array.cpp b/qv4array.cpp index c9942b0bbe..fae357b5c8 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -473,35 +473,6 @@ Array::Array(const Array &other) } -void Array::splice(double start, double deleteCount, - const QVector &/*items*/, - Array &/*other*/) -{ - initSparse(); - uint len = length(); - if (start < 0) - start = qMax(len + start, double(0)); - else if (start > len) - start = len; - deleteCount = qMax(qMin(deleteCount, len - start), double(0)); - - // ### -// const uint st = uint(start); -// const uint dc = uint(deleteCount); -// other.resize(dc); - -// const uint itemsSize = uint(items.size()); - -// for (uint i = 0; i < dc; ++i) -// other.assign(i, to_vector.at(st + i)); -// if (itemsSize > dc) -// to_vector.insert(to_vector.begin() + st, itemsSize - dc, Value::undefinedValue()); -// else if (itemsSize < dc) -// to_vector.erase(to_vector.begin() + st, to_vector.begin() + (dc - itemsSize)); -// for (uint i = 0; i < itemsSize; ++i) - // (*to_vector)[st + i] = items.at(i); -} - Value Array::indexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o) { bool protoHasArray = false; diff --git a/qv4array.h b/qv4array.h index 28601ba2b5..a7fc6b7058 100644 --- a/qv4array.h +++ b/qv4array.h @@ -361,6 +361,8 @@ inline SparseArrayNode *SparseArray::upperBound(uint akey) class Array { + friend class ArrayPrototype; + uint len; PropertyDescriptor *lengthProperty; union { @@ -609,7 +611,6 @@ public: void concat(const Array &other); void sort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len); - void splice(double start, double deleteCount, const QVector &, Array &); Value indexOf(Value v, uint fromIndex, uint len, ExecutionContext *ctx, Object *o); }; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index d898783641..b0e7bc32bf 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1841,24 +1841,65 @@ Value ArrayPrototype::method_sort(ExecutionContext *ctx) Value ArrayPrototype::method_splice(ExecutionContext *ctx) { - if (ctx->argumentCount < 2) - // ### check - return Value::undefinedValue(); + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.splice")); - - double start = ctx->argument(0).toInteger(ctx); - double deleteCount = ctx->argument(1).toInteger(ctx); - Value a = Value::fromObject(ctx->engine->newArrayObject(ctx)); - QVector items; - for (unsigned int i = 2; i < ctx->argumentCount; ++i) - items << ctx->argument(i); - ArrayObject *otherInstance = a.asArrayObject(); - assert(otherInstance); - instance->array.splice(start, deleteCount, items, otherInstance->array); - return a; + ArrayObject *newArray = ctx->engine->newArrayObject(ctx); + + double rs = ctx->argument(0).toInteger(ctx); + uint start; + if (rs < 0) + start = (uint) qMax(0., len + rs); + else + start = (uint) qMin(rs, (double)len); + + uint deleteCount = (uint)qMin(qMax(ctx->argument(1).toInteger(ctx), 0.), (double)(len - start)); + + newArray->array.values.resize(deleteCount); + PropertyDescriptor *pd = newArray->array.values.data(); + for (uint i = 0; i < deleteCount; ++i) { + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Enabled; + pd->configurable = PropertyDescriptor::Enabled; + pd->value = instance->__get__(ctx, start + i); + ++pd; + } + newArray->array.setLengthUnchecked(deleteCount); + + uint itemCount = ctx->argumentCount < 2 ? 0 : ctx->argumentCount - 2; + + if (itemCount < deleteCount) { + for (uint k = start; k < len - deleteCount; ++k) { + bool exists; + Value v = instance->__get__(ctx, k + deleteCount, &exists); + if (exists) + instance->array.set(k + itemCount, v); + else + instance->__delete__(ctx, k + itemCount); + } + for (uint k = len; k > len - deleteCount + itemCount; --k) + instance->__delete__(ctx, k - 1); + } else if (itemCount > deleteCount) { + uint k = len - deleteCount; + while (k > start) { + bool exists; + Value v = instance->__get__(ctx, k + deleteCount - 1, &exists); + if (exists) + instance->array.set(k + itemCount - 1, v); + else + instance->__delete__(ctx, k + itemCount - 1); + --k; + } + } + + for (uint i = 0; i < itemCount; ++i) + instance->array.set(start + i, ctx->argument(i + 2)); + + ctx->strictMode = true; + instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - deleteCount + itemCount)); + + return Value::fromObject(newArray); } Value ArrayPrototype::method_unshift(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index e00b0bbc57..a3d65d6809 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -691,23 +691,6 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-289 failing 15.3.4.5-2-7 failing 15.4.3.2-1-11 failing -15.4.4.12-9-c-ii-1 failing -S15.4.4.12_A1.1_T4 failing -S15.4.4.12_A1.1_T6 failing -S15.4.4.12_A1.4_T4 failing -S15.4.4.12_A1.4_T6 failing -S15.4.4.12_A2.1_T1 failing -S15.4.4.12_A2.2_T1 failing -S15.4.4.12_A2_T1 failing -S15.4.4.12_A2_T2 failing -S15.4.4.12_A2_T3 failing -S15.4.4.12_A2_T4 failing -S15.4.4.12_A3_T1 failing -S15.4.4.12_A3_T2 failing -S15.4.4.12_A3_T3 failing -S15.4.4.12_A4_T1 failing -S15.4.4.12_A4_T2 failing -S15.4.4.12_A4_T3 failing S15.4.4.13_A2_T1 failing S15.4.4.13_A2_T2 failing S15.4.4.13_A2_T3 failing @@ -2584,26 +2567,6 @@ S15.2.4.4_A14 failing S15.2.4.4_A15 failing # Array regressions -15.4.4.12-9-a-1 failing -S15.4.4.12_A1.1_T1 failing -S15.4.4.12_A1.1_T2 failing -S15.4.4.12_A1.1_T3 failing -S15.4.4.12_A1.1_T5 failing -S15.4.4.12_A1.2_T3 failing -S15.4.4.12_A1.2_T4 failing -S15.4.4.12_A1.2_T5 failing -S15.4.4.12_A1.3_T3 failing -S15.4.4.12_A1.3_T4 failing -S15.4.4.12_A1.3_T5 failing -S15.4.4.12_A1.4_T1 failing -S15.4.4.12_A1.4_T2 failing -S15.4.4.12_A1.4_T3 failing -S15.4.4.12_A1.4_T5 failing -S15.4.4.12_A2.1_T2 failing -S15.4.4.12_A2.1_T4 failing -S15.4.4.12_A2.1_T5 failing -S15.4.4.12_A2.2_T3 failing -S15.4.4.12_A2.2_T5 failing S15.4.4.4_A1_T2 failing # Regressions due to Object/property refactoring @@ -2639,4 +2602,4 @@ S15.4.4.13_A1_T2 failing # Bugs in Array.prototype 15.4.4.14-9-b-i-5 failing 15.4.4.16-7-c-i-6 failing -15.4.4.17-7-c-i-6 failing \ No newline at end of file +15.4.4.17-7-c-i-6 failing -- cgit v1.2.3 From 69a526d37f275531fde3afc7647dd7dc338f5c92 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 17 Jan 2013 00:45:11 +0100 Subject: optimise property access Inline the canPut method and simplify our code path for [[put]]. Change-Id: I4198b0bdef16a4fdf6113a8e015915492c9c301d Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 200 ++++++++++++++++++++++++------------------------- qmljs_objects.h | 4 +- qmljs_runtime.cpp | 8 +- qv4array.h | 13 +++- tests/TestExpectations | 9 +-- 5 files changed, 116 insertions(+), 118 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index fee5378421..3a700e21dc 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -280,7 +280,7 @@ Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) { - if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, index)) { + if (const PropertyDescriptor *p = __getPropertyDescriptor__(ctx, index)) { if (hasProperty) *hasProperty = true; return getValue(ctx, p); @@ -292,55 +292,6 @@ Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) } -// Section 8.12.4 -bool Object::__canPut__(ExecutionContext *ctx, String *name) -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __canPut__(ctx, idx); - - if (PropertyDescriptor *p = __getOwnProperty__(ctx, name)) { - if (p->isAccessor()) - return p->set != 0; - return p->isWritable(); - } - - if (! prototype) - return extensible; - - if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, name)) { - if (p->isAccessor()) - return p->set != 0; - if (!extensible) - return false; - return p->isWritable(); - } - - return extensible; -} - -bool Object::__canPut__(ExecutionContext *ctx, uint index) -{ - if (PropertyDescriptor *p = __getOwnProperty__(ctx, index)) { - if (p->isAccessor()) - return p->set != 0; - return p->isWritable(); - } - - if (! prototype) - return extensible; - - if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, index)) { - if (p->isAccessor()) - return p->set != 0; - if (!extensible) - return false; - return p->isWritable(); - } - - return extensible; -} - // Section 8.12.5 void Object::__put__(ExecutionContext *ctx, String *name, Value value) { @@ -348,42 +299,71 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) if (idx != String::InvalidArrayIndex) return __put__(ctx, idx, value); + PropertyDescriptor *pd = __getOwnProperty__(ctx, name); // clause 1 - if (!__canPut__(ctx, name)) - goto reject; + if (pd) { + if (pd->isAccessor()) { + if (pd->set) + goto cont; + goto reject; + } else if (!pd->isWritable()) + goto reject; + else if (isArray && name->isEqualTo(ctx->engine->id_length)) { + bool ok; + uint l = value.asArrayLength(ctx, &ok); + if (!ok) + ctx->throwRangeError(value); + ok = array.setLength(l); + if (!ok) + goto reject; + } else { + pd->value = value; + } + return; + } else if (!prototype) { + if (!extensible) + goto reject; + } else { + if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, name)) { + if (p->isAccessor()) { + if (p->set) + goto cont; + goto reject; + } + if (!extensible) + goto reject; + if (!p->isWritable()) + goto reject; + } else { + if (!extensible) + goto reject; + } + } + + cont: if (!members) members.reset(new PropertyTable()); - { - // Clause 2 - PropertyDescriptor *pd = __getOwnProperty__(ctx, name); - // Clause 3 - if (pd && pd->isData()) { - // spec says to call [[DefineOwnProperty]] with { [[Value]]: value } - - // ### to simplify and speed up we should expand the relevant parts here (clauses 6,7,9,10,12,13) - PropertyDescriptor desc = PropertyDescriptor::fromValue(value); - __defineOwnProperty__(ctx, name, &desc); - return; - } - // clause 4 - if (!pd && prototype) - pd = prototype->__getPropertyDescriptor__(ctx, name); + // clause 4 + if (!pd && prototype) + pd = prototype->__getPropertyDescriptor__(ctx, name); - // Clause 5 - if (pd && pd->isAccessor()) { - assert(pd->set != 0); + // Clause 5 + if (pd && pd->isAccessor()) { + assert(pd->set != 0); - Value args[1]; - args[0] = value; - pd->set->call(ctx, Value::fromObject(this), args, 1); - return; - } + Value args[1]; + args[0] = value; + pd->set->call(ctx, Value::fromObject(this), args, 1); + return; + } + { PropertyDescriptor *p = members->insert(name); - *p = PropertyDescriptor::fromValue(value); + p->type = PropertyDescriptor::Data; + p->value = value; p->configurable = PropertyDescriptor::Enabled; p->enumberable = PropertyDescriptor::Enabled; p->writable = PropertyDescriptor::Enabled; @@ -397,41 +377,57 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) void Object::__put__(ExecutionContext *ctx, uint index, Value value) { + PropertyDescriptor *pd = __getOwnProperty__(ctx, index); // clause 1 - if (!__canPut__(ctx, index)) - goto reject; - - { - // Clause 2 - PropertyDescriptor *pd = __getOwnProperty__(ctx, index); - // Clause 3 - if (pd && pd->isData()) { - // spec says to call [[DefineOwnProperty]] with { [[Value]]: value } - - // ### to simplify and speed up we should expand the relevant parts here (clauses 6,7,9,10,12,13) - PropertyDescriptor desc = PropertyDescriptor::fromValue(value); - __defineOwnProperty__(ctx, index, &desc); - return; + if (pd) { + if (pd->isAccessor()) { + if (pd->set) + goto cont; + goto reject; + } else if (!pd->isWritable()) + goto reject; + else + pd->value = value; + return; + } else if (!prototype) { + if (!extensible) + goto reject; + } else { + if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, index)) { + if (p->isAccessor()) { + if (p->set) + goto cont; + goto reject; + } + if (!extensible) + goto reject; + if (!p->isWritable()) + goto reject; + } else { + if (!extensible) + goto reject; } + } - // clause 4 - if (!pd && prototype) - pd = prototype->__getPropertyDescriptor__(ctx, index); + cont: - // Clause 5 - if (pd && pd->isAccessor()) { - assert(pd->set != 0); + // clause 4 + if (!pd && prototype) + pd = prototype->__getPropertyDescriptor__(ctx, index); - Value args[1]; - args[0] = value; - pd->set->call(ctx, Value::fromObject(this), args, 1); - return; - } + // Clause 5 + if (pd && pd->isAccessor()) { + assert(pd->set != 0); - array.set(index, value); + Value args[1]; + args[0] = value; + pd->set->call(ctx, Value::fromObject(this), args, 1); return; } + array.set(index, value); + return; + reject: if (ctx->strictMode) __qmljs_throw_type_error(ctx); diff --git a/qmljs_objects.h b/qmljs_objects.h index d00486816f..362fe7098c 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -124,7 +124,7 @@ struct Object: Managed { PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name); - virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, uint index); + PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, uint index); virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); virtual Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); @@ -132,8 +132,6 @@ struct Object: Managed { virtual void __put__(ExecutionContext *ctx, String *name, Value value); virtual void __put__(ExecutionContext *ctx, uint index, Value value); - virtual bool __canPut__(ExecutionContext *ctx, String *name); - virtual bool __canPut__(ExecutionContext *ctx, uint index); virtual bool __hasProperty__(const ExecutionContext *ctx, String *name) const; virtual bool __hasProperty__(const ExecutionContext *ctx, uint index) const; virtual bool __delete__(ExecutionContext *ctx, String *name); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 0bc37da66e..d7829e308c 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -576,14 +576,16 @@ void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Val Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) { uint idx = index.asArrayIndex(); + if (object.isString() && idx < UINT_MAX) { - if (idx > INT_MAX || (int) idx >= object.stringValue()->toQString().length()) + String *str = object.stringValue(); + if (idx >= (uint)str->toQString().length()) return Value::undefinedValue(); - const QString s = object.stringValue()->toQString().mid(idx, 1); + const QString s = str->toQString().mid(idx, 1); return Value::fromString(ctx, s); } - if (! object.isObject()) + if (!object.isObject()) object = __qmljs_to_object(object, ctx); Object *o = object.objectValue(); diff --git a/qv4array.h b/qv4array.h index a7fc6b7058..58529ef4f8 100644 --- a/qv4array.h +++ b/qv4array.h @@ -512,16 +512,25 @@ public: } } + const PropertyDescriptor *nonSparseAt(uint index) const { + if (sparse) + return 0; + index += offset; + if (index >= (uint)values.size()) + return 0; + return values.constData() + index; + } + const PropertyDescriptor *at(uint index) const { if (!sparse) { if (index >= values.size() - offset) return 0; - return values.data() + index + offset; + return values.constData() + index + offset; } else { SparseArrayNode *n = sparse->findNode(index); if (!n) return 0; - return values.data() + n->value; + return values.constData() + n->value; } } diff --git a/tests/TestExpectations b/tests/TestExpectations index a3d65d6809..7764282c5b 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -1069,7 +1069,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.16-7-5 failing 15.4.4.16-7-8 failing 15.4.4.16-7-9 failing -15.4.4.16-7-b-1 failing 15.4.4.16-7-b-10 failing 15.4.4.16-7-b-12 failing 15.4.4.16-7-b-15 failing @@ -1194,7 +1193,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.17-7-5 failing 15.4.4.17-7-8 failing 15.4.4.17-7-9 failing -15.4.4.17-7-b-1 failing 15.4.4.17-7-b-10 failing 15.4.4.17-7-b-12 failing 15.4.4.17-7-b-15 failing @@ -1319,7 +1317,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.18-7-4 failing 15.4.4.18-7-8 failing 15.4.4.18-7-9 failing -15.4.4.18-7-b-1 failing 15.4.4.18-7-b-10 failing 15.4.4.18-7-b-12 failing 15.4.4.18-7-b-15 failing @@ -1439,7 +1436,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.19-8-1 failing 15.4.4.19-8-5 failing 15.4.4.19-8-8 failing -15.4.4.19-8-b-1 failing 15.4.4.19-8-b-10 failing 15.4.4.19-8-b-12 failing 15.4.4.19-8-b-15 failing @@ -1571,7 +1567,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.20-9-5 failing 15.4.4.20-9-8 failing 15.4.4.20-9-9 failing -15.4.4.20-9-b-1 failing 15.4.4.20-9-b-10 failing 15.4.4.20-9-b-12 failing 15.4.4.20-9-b-15 failing @@ -1936,7 +1931,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.22-8-c-5 failing 15.4.4.22-8-c-6 failing 15.4.4.22-9-8 failing -15.4.4.22-9-9 failing 15.4.4.22-9-b-10 failing 15.4.4.22-9-b-12 failing 15.4.4.22-9-b-17 failing @@ -2596,10 +2590,9 @@ S15.4.4.13_A1_T2 failing 15.4.4.18-7-c-i-6 failing 15.4.4.19-8-c-i-6 failing 15.4.4.20-9-c-i-6 failing -15.4.4.21-8-b-iii-1-6 failing 15.4.4.22-8-b-iii-1-6 failing # Bugs in Array.prototype 15.4.4.14-9-b-i-5 failing 15.4.4.16-7-c-i-6 failing -15.4.4.17-7-c-i-6 failing +15.4.4.17-7-c-i-6 failing \ No newline at end of file -- cgit v1.2.3 From dec5371fcdc8a56d89677cb33e5535c3b675eaad Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 17 Jan 2013 13:42:13 +0100 Subject: Fixes to Array.prototype.indexOf Change-Id: Ia9a2a8e25223c4b4d72a59c0c2ea449dc544f796 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index b0e7bc32bf..602b64e570 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1921,22 +1921,37 @@ Value ArrayPrototype::method_unshift(ExecutionContext *ctx) Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) { - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.indexOf")); + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); Value searchValue; uint fromIndex = 0; - if (ctx->argumentCount == 1) - searchValue = ctx->argument(0); - else if (ctx->argumentCount == 2) { + if (ctx->argumentCount >= 1) searchValue = ctx->argument(0); - fromIndex = ctx->argument(1).toUInt32(ctx); - } else - __qmljs_throw_type_error(ctx); + else + return Value::fromInt32(-1); uint len = instance->isArray ? instance->array.length() : instance->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); + + if (ctx->argumentCount >= 2) { + double f = ctx->argument(1).toInteger(ctx); + if (f >= len) + return Value::fromInt32(-1); + if (f < 0) + f = qMax(len + f, 0.); + fromIndex = (uint) f; + } + + if (instance->isString) { + for (uint k = fromIndex; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (exists && __qmljs_strict_equal(v, searchValue)) + return Value::fromDouble(k); + } + return Value::fromInt32(-1); + } + return instance->array.indexOf(searchValue, fromIndex, len, ctx, instance); } -- cgit v1.2.3 From e48d4661d868c38cbdf11fd150fc54546eb1abf9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 17 Jan 2013 13:42:42 +0100 Subject: Fix our NaN boxing. -NaN was not recognised as a double, but as a pointer, leading to wrong behavior and crashes. We actually have one more bit available, so let's use it. Change-Id: I505ed5748228e4337d99da3b4eda43960967e117 Reviewed-by: Simon Hausmann --- qmljs_value.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qmljs_value.h b/qmljs_value.h index 9a4fcdb44a..1b9461582a 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -98,9 +98,9 @@ struct Value }; enum Masks { - NotDouble_Mask = 0xfff80000, - Type_Mask = 0xffff0000, - Immediate_Mask = NotDouble_Mask | 0x00040000, + NotDouble_Mask = 0xfffc0000, + Type_Mask = 0xffff8000, + Immediate_Mask = NotDouble_Mask | 0x00008000, Tag_Shift = 32 }; enum ValueType { @@ -113,7 +113,7 @@ struct Value }; enum ImmediateFlags { - ConvertibleToInt = Immediate_Mask | (0x1 << 15) + ConvertibleToInt = Immediate_Mask | 0x1 }; enum ValueTypeInternal { -- cgit v1.2.3 From 081a3815d8bc629d787d9ac9bbb7f7482314abd0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 17 Jan 2013 14:01:16 +0100 Subject: Update test results Change-Id: Ib7ce4d5907b9c02caad933e8159e2cca7bab6230 Reviewed-by: Simon Hausmann --- tests/TestExpectations | 122 +------------------------------------------------ 1 file changed, 1 insertion(+), 121 deletions(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index 7764282c5b..0bd2f07779 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -81,7 +81,6 @@ S11.11.1_A3_T4 failing 11.13.2-6-7-s failing 11.13.2-6-8-s failing 11.13.2-6-9-s failing -S11.13.2_A4.2_T1.4 failing S11.13.2_A4.3_T1.4 failing S11.13.2_A4.3_T2.3 failing S11.13.2_A4.3_T2.7 failing @@ -130,26 +129,16 @@ S11.4.1_A3.3 failing 11.4.4-2-2-s failing 11.4.5-2-1-s failing 11.4.5-2-2-s failing -S11.4.7_A3_T3 failing -S11.4.7_A3_T4 failing -S11.4.7_A3_T5 failing -S11.4.7_A4.1 failing S11.5.1_A2.4_T1 failing S11.5.1_A2.4_T3 failing -S11.5.1_A4_T3 failing S11.5.2_A2.4_T1 failing S11.5.2_A2.4_T3 failing -S11.5.2_A3_T1.4 failing -S11.5.2_A4_T4 failing -S11.5.2_A4_T7 failing S11.5.3_A2.4_T1 failing S11.5.3_A2.4_T3 failing S11.5.3_A3_T1.4 failing S11.5.3_A3_T2.3 failing -S11.5.3_A3_T2.7 failing S11.5.3_A3_T2.9 failing S11.5.3_A4_T2 failing -S11.6.2_A4_T3 failing S11.7.1_A2.4_T1 failing S11.7.1_A2.4_T3 failing S11.7.2_A2.4_T1 failing @@ -158,11 +147,8 @@ S11.7.2_A3_T1.2 failing S11.7.2_A4_T3 failing S11.7.2_A4_T4 failing S11.7.2_A5.2_T1 failing -S11.5.3_A4_T3 failing -S11.5.3_A4_T4 failing S11.6.1_A2.4_T1 failing S11.6.1_A2.4_T3 failing -S11.6.1_A4_T2 failing S11.6.2_A2.4_T1 failing S11.6.2_A2.4_T3 failing S11.8.2_A2.4_T1 failing @@ -699,117 +685,18 @@ S15.4.4.13_A3_T2 failing S15.4.4.13_A3_T3 failing S15.4.4.13_A4_T1 failing S15.4.4.13_A4_T2 failing -15.4.4.14-1-1 failing -15.4.4.14-1-10 failing -15.4.4.14-1-11 failing -15.4.4.14-1-12 failing 15.4.4.14-1-13 failing -15.4.4.14-1-14 failing -15.4.4.14-1-15 failing -15.4.4.14-1-17 failing -15.4.4.14-1-2 failing -15.4.4.14-1-3 failing -15.4.4.14-1-4 failing -15.4.4.14-1-5 failing -15.4.4.14-1-6 failing -15.4.4.14-1-7 failing -15.4.4.14-1-8 failing -15.4.4.14-1-9 failing -15.4.4.14-10-2 failing -15.4.4.14-2-1 failing -15.4.4.14-2-10 failing -15.4.4.14-2-11 failing -15.4.4.14-2-12 failing -15.4.4.14-2-13 failing -15.4.4.14-2-14 failing -15.4.4.14-2-15 failing 15.4.4.14-2-17 failing -15.4.4.14-2-18 failing -15.4.4.14-2-19 failing -15.4.4.14-2-3 failing -15.4.4.14-2-5 failing -15.4.4.14-2-6 failing -15.4.4.14-2-7 failing -15.4.4.14-2-8 failing -15.4.4.14-2-9 failing -15.4.4.14-3-1 failing -15.4.4.14-3-10 failing -15.4.4.14-3-11 failing -15.4.4.14-3-12 failing -15.4.4.14-3-13 failing -15.4.4.14-3-14 failing -15.4.4.14-3-15 failing -15.4.4.14-3-16 failing -15.4.4.14-3-17 failing -15.4.4.14-3-18 failing -15.4.4.14-3-19 failing -15.4.4.14-3-2 failing -15.4.4.14-3-20 failing -15.4.4.14-3-21 failing 15.4.4.14-3-22 failing -15.4.4.14-3-23 failing -15.4.4.14-3-24 failing -15.4.4.14-3-25 failing -15.4.4.14-3-28 failing -15.4.4.14-3-29 failing -15.4.4.14-3-3 failing -15.4.4.14-3-4 failing -15.4.4.14-3-5 failing -15.4.4.14-3-6 failing -15.4.4.14-3-7 failing -15.4.4.14-3-8 failing -15.4.4.14-3-9 failing -15.4.4.14-4-10 failing -15.4.4.14-4-11 failing -15.4.4.14-4-2 failing -15.4.4.14-4-3 failing 15.4.4.14-4-4 failing -15.4.4.14-4-5 failing -15.4.4.14-4-6 failing -15.4.4.14-4-7 failing -15.4.4.14-4-8 failing -15.4.4.14-4-9 failing -15.4.4.14-5-11 failing -15.4.4.14-5-14 failing -15.4.4.14-5-15 failing -15.4.4.14-5-26 failing -15.4.4.14-5-27 failing -15.4.4.14-5-28 failing -15.4.4.14-5-29 failing -15.4.4.14-5-30 failing -15.4.4.14-5-32 failing -15.4.4.14-8-1 failing -15.4.4.14-8-2 failing -15.4.4.14-9-a-1 failing 15.4.4.14-9-a-10 failing -15.4.4.14-9-a-11 failing -15.4.4.14-9-a-13 failing -15.4.4.14-9-a-15 failing 15.4.4.14-9-a-17 failing -15.4.4.14-9-a-2 failing -15.4.4.14-9-a-4 failing -15.4.4.14-9-a-5 failing 15.4.4.14-9-a-7 failing 15.4.4.14-9-a-9 failing -15.4.4.14-9-b-i-1 failing -15.4.4.14-9-b-i-10 failing -15.4.4.14-9-b-i-12 failing -15.4.4.14-9-b-i-14 failing -15.4.4.14-9-b-i-16 failing -15.4.4.14-9-b-i-18 failing -15.4.4.14-9-b-i-20 failing -15.4.4.14-9-b-i-22 failing -15.4.4.14-9-b-i-23 failing 15.4.4.14-9-b-i-25 failing 15.4.4.14-9-b-i-26 failing 15.4.4.14-9-b-i-27 failing -15.4.4.14-9-b-i-29 failing -15.4.4.14-9-b-i-31 failing -15.4.4.14-9-b-i-4 failing -15.4.4.14-9-b-i-6 failing -15.4.4.14-9-b-i-8 failing 15.4.4.14-9-b-ii-2 failing -15.4.4.14-9-b-ii-5 failing 15.4.4.15-1-1 failing 15.4.4.15-1-10 failing 15.4.4.15-1-11 failing @@ -2369,9 +2256,6 @@ S15.7.4_A3.3 failing S15.7.4.5_A1.3_T01 failing S15.7.4.5_A1.3_T02 failing S15.7.4.5_A1.4_T01 failing -S15.8.2.16_A4 failing -S15.8.2.16_A5 failing -S15.8.2.17_A2 failing 15.9.4.4-0-1 failing 15.9.4.4-0-2 failing 15.9.4.4-0-3 failing @@ -2385,10 +2269,6 @@ S15.9.3.1_A5_T4 failing S15.9.3.1_A5_T5 failing S15.9.3.1_A5_T6 failing S15.9.4_A5 failing -S15.8.2.18_A4 failing -S15.8.2.18_A5 failing -S15.8.2.7_A4 failing -S15.8.2.7_A5 failing S15.9.5.1_A2_T1 failing 15.9.5.40_1 failing 6.4_c failing @@ -2595,4 +2475,4 @@ S15.4.4.13_A1_T2 failing # Bugs in Array.prototype 15.4.4.14-9-b-i-5 failing 15.4.4.16-7-c-i-6 failing -15.4.4.17-7-c-i-6 failing \ No newline at end of file +15.4.4.17-7-c-i-6 failing -- cgit v1.2.3 From a854e96f34a9c98e77c2fa0615a85ada15fc8d39 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 17 Jan 2013 14:26:01 +0100 Subject: further fixes to indexOf Change-Id: Iae083e42a38f4b3d16163bce51ff0ad9a97e48dc Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 7 ++++--- tests/TestExpectations | 5 +---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 602b64e570..588149bb7a 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1922,6 +1922,9 @@ Value ArrayPrototype::method_unshift(ExecutionContext *ctx) Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) { Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = instance->isArray ? instance->array.length() : instance->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); + if (!len) + return Value::fromInt32(-1); Value searchValue; uint fromIndex = 0; @@ -1929,9 +1932,7 @@ Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) if (ctx->argumentCount >= 1) searchValue = ctx->argument(0); else - return Value::fromInt32(-1); - - uint len = instance->isArray ? instance->array.length() : instance->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); + searchValue = Value::undefinedValue(); if (ctx->argumentCount >= 2) { double f = ctx->argument(1).toInteger(ctx); diff --git a/tests/TestExpectations b/tests/TestExpectations index 0bd2f07779..5e671d65dc 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -83,7 +83,6 @@ S11.11.1_A3_T4 failing 11.13.2-6-9-s failing S11.13.2_A4.3_T1.4 failing S11.13.2_A4.3_T2.3 failing -S11.13.2_A4.3_T2.7 failing S11.13.2_A4.3_T2.9 failing S11.2.1_A3_T1 failing S11.2.1_A3_T2 failing @@ -687,7 +686,6 @@ S15.4.4.13_A4_T1 failing S15.4.4.13_A4_T2 failing 15.4.4.14-1-13 failing 15.4.4.14-2-17 failing -15.4.4.14-3-22 failing 15.4.4.14-4-4 failing 15.4.4.14-9-a-10 failing 15.4.4.14-9-a-17 failing @@ -696,7 +694,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.14-9-b-i-25 failing 15.4.4.14-9-b-i-26 failing 15.4.4.14-9-b-i-27 failing -15.4.4.14-9-b-ii-2 failing 15.4.4.15-1-1 failing 15.4.4.15-1-10 failing 15.4.4.15-1-11 failing @@ -2475,4 +2472,4 @@ S15.4.4.13_A1_T2 failing # Bugs in Array.prototype 15.4.4.14-9-b-i-5 failing 15.4.4.16-7-c-i-6 failing -15.4.4.17-7-c-i-6 failing +15.4.4.17-7-c-i-6 failing \ No newline at end of file -- cgit v1.2.3 From f8bfd3c96cdf4e4d50abcfbcb16802b2db82c5f1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 17 Jan 2013 15:33:21 +0100 Subject: Implement Array.prototype.lastIndexOf Change-Id: I474dc9fe1f303cb5f77ba5efaa219f1c0f411cb6 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 33 ++++++++++- tests/TestExpectations | 146 ------------------------------------------------- 2 files changed, 30 insertions(+), 149 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 588149bb7a..84b536ddaa 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1922,7 +1922,7 @@ Value ArrayPrototype::method_unshift(ExecutionContext *ctx) Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) { Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - uint len = instance->isArray ? instance->array.length() : instance->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); + uint len = getLength(ctx, instance); if (!len) return Value::fromInt32(-1); @@ -1958,8 +1958,35 @@ Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("Array.prototype.lastIndexOf")); - return Value::undefinedValue(); + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); + if (!len) + return Value::fromInt32(-1); + + Value searchValue; + uint fromIndex = len - 1; + + if (ctx->argumentCount >= 1) + searchValue = ctx->argument(0); + else + searchValue = Value::undefinedValue(); + + if (ctx->argumentCount >= 2) { + double f = ctx->argument(1).toInteger(ctx); + if (f >= len) + return Value::fromInt32(-1); + if (f < 0) + f = qMax(len + f, 0.); + fromIndex = (uint) f; + } + + for (uint k = fromIndex; k > 0; --k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (exists && __qmljs_strict_equal(v, searchValue)) + return Value::fromDouble(k); + } + return Value::fromInt32(-1); } Value ArrayPrototype::method_every(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index 5e671d65dc..c903e21ad4 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -3,7 +3,6 @@ S15.1.3.1_A2.5_T1 S15.1.3.2_A2.5_T1 # Tests failing that are supposed to pass. -15.4.4.15-8-9 failing 15.4.4.15-5-16 failing 15.4.4.15-5-12 failing 15.4.4.14-9-9 failing @@ -686,7 +685,6 @@ S15.4.4.13_A4_T1 failing S15.4.4.13_A4_T2 failing 15.4.4.14-1-13 failing 15.4.4.14-2-17 failing -15.4.4.14-4-4 failing 15.4.4.14-9-a-10 failing 15.4.4.14-9-a-17 failing 15.4.4.14-9-a-7 failing @@ -694,149 +692,18 @@ S15.4.4.13_A4_T2 failing 15.4.4.14-9-b-i-25 failing 15.4.4.14-9-b-i-26 failing 15.4.4.14-9-b-i-27 failing -15.4.4.15-1-1 failing -15.4.4.15-1-10 failing -15.4.4.15-1-11 failing -15.4.4.15-1-12 failing 15.4.4.15-1-13 failing -15.4.4.15-1-14 failing -15.4.4.15-1-15 failing -15.4.4.15-1-17 failing -15.4.4.15-1-2 failing -15.4.4.15-1-3 failing -15.4.4.15-1-4 failing -15.4.4.15-1-5 failing -15.4.4.15-1-6 failing -15.4.4.15-1-7 failing -15.4.4.15-1-8 failing -15.4.4.15-1-9 failing -15.4.4.15-2-1 failing -15.4.4.15-2-10 failing -15.4.4.15-2-11 failing -15.4.4.15-2-12 failing -15.4.4.15-2-13 failing -15.4.4.15-2-14 failing -15.4.4.15-2-15 failing -15.4.4.15-2-17 failing -15.4.4.15-2-18 failing -15.4.4.15-2-19 failing -15.4.4.15-2-2 failing -15.4.4.15-2-3 failing -15.4.4.15-2-4 failing -15.4.4.15-2-5 failing -15.4.4.15-2-6 failing -15.4.4.15-2-7 failing -15.4.4.15-2-8 failing -15.4.4.15-2-9 failing -15.4.4.15-3-1 failing -15.4.4.15-3-10 failing -15.4.4.15-3-11 failing -15.4.4.15-3-12 failing -15.4.4.15-3-13 failing -15.4.4.15-3-14 failing -15.4.4.15-3-15 failing -15.4.4.15-3-16 failing -15.4.4.15-3-17 failing -15.4.4.15-3-18 failing -15.4.4.15-3-19 failing 15.4.4.15-3-2 failing -15.4.4.15-3-20 failing -15.4.4.15-3-21 failing -15.4.4.15-3-22 failing -15.4.4.15-3-23 failing -15.4.4.15-3-24 failing -15.4.4.15-3-25 failing -15.4.4.15-3-28 failing 15.4.4.15-3-29 failing -15.4.4.15-3-3 failing -15.4.4.15-3-4 failing -15.4.4.15-3-5 failing -15.4.4.15-3-6 failing -15.4.4.15-3-7 failing -15.4.4.15-3-8 failing -15.4.4.15-3-9 failing -15.4.4.15-4-1 failing -15.4.4.15-4-10 failing -15.4.4.15-4-11 failing -15.4.4.15-4-2 failing -15.4.4.15-4-3 failing -15.4.4.15-4-4 failing -15.4.4.15-4-5 failing -15.4.4.15-4-6 failing -15.4.4.15-4-7 failing -15.4.4.15-4-8 failing -15.4.4.15-4-9 failing -15.4.4.15-5-1 failing -15.4.4.15-5-10 failing -15.4.4.15-5-11 failing -15.4.4.15-5-13 failing 15.4.4.15-5-14 failing -15.4.4.15-5-15 failing -15.4.4.15-5-17 failing -15.4.4.15-5-18 failing -15.4.4.15-5-19 failing 15.4.4.15-5-2 failing -15.4.4.15-5-20 failing -15.4.4.15-5-21 failing -15.4.4.15-5-22 failing -15.4.4.15-5-23 failing -15.4.4.15-5-24 failing -15.4.4.15-5-25 failing -15.4.4.15-5-26 failing -15.4.4.15-5-27 failing -15.4.4.15-5-28 failing -15.4.4.15-5-29 failing -15.4.4.15-5-3 failing -15.4.4.15-5-30 failing -15.4.4.15-5-31 failing -15.4.4.15-5-32 failing 15.4.4.15-5-33 failing 15.4.4.15-5-4 failing 15.4.4.15-5-5 failing -15.4.4.15-5-6 failing 15.4.4.15-5-7 failing 15.4.4.15-5-8 failing 15.4.4.15-5-9 failing 15.4.4.15-6-1 failing -15.4.4.15-6-2 failing -15.4.4.15-6-3 failing -15.4.4.15-6-4 failing -15.4.4.15-6-5 failing -15.4.4.15-6-6 failing -15.4.4.15-7-1 failing -15.4.4.15-7-2 failing -15.4.4.15-7-3 failing -15.4.4.15-7-4 failing -15.4.4.15-8-1 failing -15.4.4.15-8-10 failing -15.4.4.15-8-11 failing -15.4.4.15-8-2 failing -15.4.4.15-8-3 failing -15.4.4.15-8-4 failing -15.4.4.15-8-5 failing -15.4.4.15-8-6 failing -15.4.4.15-8-7 failing -15.4.4.15-8-8 failing -15.4.4.15-8-a-1 failing -15.4.4.15-8-a-10 failing -15.4.4.15-8-a-11 failing -15.4.4.15-8-a-12 failing -15.4.4.15-8-a-13 failing -15.4.4.15-8-a-14 failing -15.4.4.15-8-a-15 failing -15.4.4.15-8-a-16 failing -15.4.4.15-8-a-17 failing -15.4.4.15-8-a-18 failing -15.4.4.15-8-a-19 failing -15.4.4.15-8-a-2 failing -15.4.4.15-8-a-3 failing -15.4.4.15-8-a-4 failing -15.4.4.15-8-a-5 failing -15.4.4.15-8-a-6 failing -15.4.4.15-8-a-7 failing -15.4.4.15-8-a-8 failing -15.4.4.15-8-a-9 failing -15.4.4.15-8-b-1 failing 15.4.4.15-8-b-i-1 failing 15.4.4.15-8-b-i-10 failing 15.4.4.15-8-b-i-11 failing @@ -855,33 +722,20 @@ S15.4.4.13_A4_T2 failing 15.4.4.15-8-b-i-23 failing 15.4.4.15-8-b-i-25 failing 15.4.4.15-8-b-i-26 failing -15.4.4.15-8-b-i-27 failing -15.4.4.15-8-b-i-28 failing -15.4.4.15-8-b-i-29 failing 15.4.4.15-8-b-i-3 failing -15.4.4.15-8-b-i-30 failing -15.4.4.15-8-b-i-31 failing 15.4.4.15-8-b-i-4 failing 15.4.4.15-8-b-i-5 failing 15.4.4.15-8-b-i-6 failing 15.4.4.15-8-b-i-7 failing 15.4.4.15-8-b-i-8 failing 15.4.4.15-8-b-i-9 failing -15.4.4.15-8-b-ii-1 failing -15.4.4.15-8-b-ii-10 failing 15.4.4.15-8-b-ii-11 failing 15.4.4.15-8-b-ii-2 failing 15.4.4.15-8-b-ii-3 failing -15.4.4.15-8-b-ii-4 failing -15.4.4.15-8-b-ii-5 failing 15.4.4.15-8-b-ii-6 failing 15.4.4.15-8-b-ii-7 failing 15.4.4.15-8-b-ii-8 failing 15.4.4.15-8-b-ii-9 failing -15.4.4.15-8-b-iii-1 failing -15.4.4.15-8-b-iii-2 failing -15.4.4.15-9-1 failing -15.4.4.15-9-2 failing 15.4.4.16-1-1 failing 15.4.4.16-1-10 failing 15.4.4.16-1-11 failing -- cgit v1.2.3 From f6f8f48326de33f12b603697172915e075aabe77 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 Jan 2013 16:21:44 +0100 Subject: Update test expectations Change-Id: I202e5e6ce8648266a48f12c2663a07816f0f9e10 Reviewed-by: Simon Hausmann --- tests/TestExpectations | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index c903e21ad4..7d90fd6932 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -2326,4 +2326,14 @@ S15.4.4.13_A1_T2 failing # Bugs in Array.prototype 15.4.4.14-9-b-i-5 failing 15.4.4.16-7-c-i-6 failing -15.4.4.17-7-c-i-6 failing \ No newline at end of file +15.4.4.17-7-c-i-6 failing + +# Regressions introduced with https://codereview.qt-project.org/#change,45044 +15.4.4.16-7-b-1 failing +15.4.4.17-7-b-1 failing +15.4.4.18-7-b-1 failing +15.4.4.19-8-b-1 failing +15.4.4.20-9-b-1 failing +15.4.4.21-8-b-iii-1-6 failing +15.4.4.22-9-9 failing + -- cgit v1.2.3 From e7729e28f0c4c27bcbc780938bf47385877c2d60 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 Jan 2013 15:24:58 +0100 Subject: Fix values of RegExp.lastIndex property The property should be writable and also set to 0 if no match is found. Change-Id: I4288ad239980260c7610c5de0061cc42ac38bb7a Reviewed-by: Lars Knoll --- qmljs_engine.cpp | 4 ++-- qmljs_objects.cpp | 16 ++++++++++++++-- qmljs_objects.h | 4 ++-- qv4ecmaobjects.cpp | 12 ++++++++---- qv4ecmaobjects_p.h | 2 +- tests/TestExpectations | 18 ------------------ 6 files changed, 27 insertions(+), 29 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index a1f99f52c9..b58ff12d7a 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -104,7 +104,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) arrayPrototype = new (memoryManager) ArrayPrototype(rootContext); datePrototype = new (memoryManager) DatePrototype(); functionPrototype = new (memoryManager) FunctionPrototype(rootContext); - regExpPrototype = new (memoryManager) RegExpPrototype(); + regExpPrototype = new (memoryManager) RegExpPrototype(this); errorPrototype = new (memoryManager) ErrorPrototype(); evalErrorPrototype = new (memoryManager) EvalErrorPrototype(rootContext); rangeErrorPrototype = new (memoryManager) RangeErrorPrototype(rootContext); @@ -369,7 +369,7 @@ RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags RegExpObject *ExecutionEngine::newRegExpObject(PassRefPtr re, bool global) { - RegExpObject *object = new (memoryManager) RegExpObject(re, global); + RegExpObject *object = new (memoryManager) RegExpObject(this, re, global); object->prototype = regExpPrototype; return object; } diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 3a700e21dc..5e3e0efc0a 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -1105,6 +1105,20 @@ Value IsFiniteFunction::call(ExecutionContext *context, Value /*thisObject*/, Va } +RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global) + : value(value) + , global(global) +{ + if (!members) + members.reset(new PropertyTable()); + lastIndexProperty = members->insert(engine->identifier("lastIndex")); + lastIndexProperty->type = PropertyDescriptor::Data; + lastIndexProperty->writable = PropertyDescriptor::Enabled; + lastIndexProperty->enumberable = PropertyDescriptor::Disabled; + lastIndexProperty->configurable = PropertyDescriptor::Disabled; + lastIndexProperty->value = Value::fromInt32(0); +} + Value RegExpObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { QString n = name->toQString(); @@ -1117,8 +1131,6 @@ Value RegExpObject::__get__(ExecutionContext *ctx, String *name, bool *hasProper v = Value::fromBoolean(value->ignoreCase()); else if (n == QLatin1String("multiline")) v = Value::fromBoolean(value->multiLine()); - else if (n == QLatin1String("lastIndex")) - v = lastIndex; if (v.type() != Value::Undefined_Type) { if (hasProperty) *hasProperty = true; diff --git a/qmljs_objects.h b/qmljs_objects.h index 362fe7098c..01773ee43e 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -361,9 +361,9 @@ struct IsFiniteFunction: FunctionObject struct RegExpObject: Object { RefPtr value; - Value lastIndex; + PropertyDescriptor *lastIndexProperty; bool global; - RegExpObject(PassRefPtr value, bool global): value(value), lastIndex(Value::fromInt32(0)), global(global) {} + RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global); virtual QString className() { return QStringLiteral("RegExp"); } virtual RegExpObject *asRegExpObject() { return this; } virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 84b536ddaa..7cf5f52494 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2985,14 +2985,18 @@ Value RegExpPrototype::method_exec(ExecutionContext *ctx) arg = __qmljs_to_string(arg, ctx); QString s = arg.stringValue()->toQString(); - int offset = r->global ? r->lastIndex.toInt32(ctx) : 0; - if (offset < 0 || offset > s.length()) + int offset = r->global ? r->lastIndexProperty->value.toInt32(ctx) : 0; + if (offset < 0 || offset > s.length()) { + r->lastIndexProperty->value = Value::fromInt32(0); return Value::nullValue(); + } uint* matchOffsets = (uint*)alloca(r->value->captureCount() * 2 * sizeof(uint)); int result = r->value->match(s, offset, matchOffsets); - if (result == -1) + if (result == -1) { + r->lastIndexProperty->value = Value::fromInt32(0); return Value::nullValue(); + } // fill in result data ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); @@ -3009,7 +3013,7 @@ Value RegExpPrototype::method_exec(ExecutionContext *ctx) array->__put__(ctx, QLatin1String("input"), arg); if (r->global) - r->lastIndex = Value::fromInt32(matchOffsets[1]); + r->lastIndexProperty->value = Value::fromInt32(matchOffsets[1]); return Value::fromObject(array); } diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 0576eb45f3..55890efe53 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -295,7 +295,7 @@ struct RegExpCtor: FunctionObject struct RegExpPrototype: RegExpObject { - RegExpPrototype(): RegExpObject(RegExp::create(0, QString()), false) {} + RegExpPrototype(ExecutionEngine* engine): RegExpObject(engine, RegExp::create(0, QString()), false) {} void init(ExecutionContext *ctx, const Value &ctor); static Value method_exec(ExecutionContext *ctx); diff --git a/tests/TestExpectations b/tests/TestExpectations index 7d90fd6932..4319e5bc30 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -312,21 +312,6 @@ S15.10.2.12_A4_T1 failing S15.10.2.12_A5_T1 failing S15.10.2.8_A3_T18 failing S15.10.6.2_A1_T2 failing -S15.10.6.2_A4_T1 failing -S15.10.6.2_A4_T10 failing -S15.10.6.2_A4_T11 failing -S15.10.6.2_A4_T12 failing -S15.10.6.2_A4_T2 failing -S15.10.6.2_A4_T3 failing -S15.10.6.2_A4_T4 failing -S15.10.6.2_A4_T5 failing -S15.10.6.2_A4_T6 failing -S15.10.6.2_A4_T7 failing -S15.10.6.2_A4_T8 failing -S15.10.6.2_A4_T9 failing -S15.10.6.2_A5_T1 failing -S15.10.6.2_A5_T2 failing -S15.10.6.2_A5_T3 failing S15.10.7_A2_T1 failing 15.10.7.1-2 failing S15.10.7.1_A10 failing @@ -344,9 +329,6 @@ S15.10.7.3_A9 failing S15.10.7.4_A10 failing S15.10.7.4_A8 failing S15.10.7.4_A9 failing -15.10.7.5-2 failing -S15.10.7.5_A8 failing -S15.10.7.5_A9 failing S15.11.4.2_A1 failing S15.11.4.2_A2 failing S15.11.4.3_A1 failing -- cgit v1.2.3 From 4a30de5fd0b28d7b1c529e8fc16275fb91a3e2db Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 17 Jan 2013 16:49:19 +0100 Subject: Changed reachable Q_UNREACHABLE into Q_UNIMPLEMENTED. They are probably unreachable, but this way we get notified if they are reachable after all. Easier to fix than silent ignoration. Change-Id: I2eafb6ad9fe4bdd7cba9490e9e4f991bb96c5715 Reviewed-by: Lars Knoll --- qv4isel_p.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 1f564ef3a7..da9b6cd51f 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -210,10 +210,10 @@ void InstructionSelection::visitExp(IR::Exp *s) } else if (c->base->asMember()) { callProperty(c, 0); } else { - Q_UNREACHABLE(); + Q_UNIMPLEMENTED(); } } else { - Q_UNREACHABLE(); + Q_UNIMPLEMENTED(); } } -- cgit v1.2.3 From 13a77fd6726a3ea0e53635ddc922059a1997d9bc Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 17 Jan 2013 21:19:53 +0100 Subject: Implement Array.prototype.every/some/forEach The old implementations of these methods were pretty broken. These fix 99% of the test failures related to these methods. Change-Id: I09d45411c85252a9ac7db8dccc644d51b3a31503 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 73 +++++----- tests/TestExpectations | 359 ------------------------------------------------- 2 files changed, 43 insertions(+), 389 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 7cf5f52494..da16f7c0bb 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1991,24 +1991,28 @@ Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) Value ArrayPrototype::method_every(ExecutionContext *ctx) { - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.every")); + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); - Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); + bool ok = true; - // ### - for (uint k = 0; ok && k < instance->array.length(); ++k) { - Value v = instance->__get__(ctx, k); - if (v.isUndefined()) + for (uint k = 0; ok && k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) continue; Value args[3]; args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); + Value r = callback->call(ctx, thisArg, args, 3); ok = __qmljs_to_boolean(r, ctx); } return Value::fromBoolean(ok); @@ -2016,47 +2020,56 @@ Value ArrayPrototype::method_every(ExecutionContext *ctx) Value ArrayPrototype::method_some(ExecutionContext *ctx) { - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.some")); + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); - Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); - bool ok = false; - // ### - for (uint k = 0; !ok && k < instance->array.length(); ++k) { - Value v = instance->__get__(ctx, k); - if (v.isUndefined()) + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) continue; Value args[3]; args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); - ok = __qmljs_to_boolean(r, ctx); + Value r = callback->call(ctx, thisArg, args, 3); + if (__qmljs_to_boolean(r, ctx)) + return Value::fromBoolean(true); } - return Value::fromBoolean(ok); + return Value::fromBoolean(false); } Value ArrayPrototype::method_forEach(ExecutionContext *ctx) { - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.forEach")); + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); - Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); - // ### - for (quint32 k = 0; k < instance->array.length(); ++k) { - Value v = instance->__get__(ctx, k); - if (v.isUndefined()) + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) continue; + Value args[3]; args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - /*Value r =*/ __qmljs_call_value(ctx, thisArg, callback, args, 3); + callback->call(ctx, thisArg, args, 3); } return Value::undefinedValue(); } diff --git a/tests/TestExpectations b/tests/TestExpectations index 4319e5bc30..c60b3ac53b 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -718,372 +718,17 @@ S15.4.4.13_A4_T2 failing 15.4.4.15-8-b-ii-7 failing 15.4.4.15-8-b-ii-8 failing 15.4.4.15-8-b-ii-9 failing -15.4.4.16-1-1 failing 15.4.4.16-1-10 failing -15.4.4.16-1-11 failing -15.4.4.16-1-12 failing 15.4.4.16-1-13 failing -15.4.4.16-1-14 failing -15.4.4.16-1-15 failing -15.4.4.16-1-2 failing -15.4.4.16-1-3 failing -15.4.4.16-1-4 failing -15.4.4.16-1-5 failing -15.4.4.16-1-6 failing -15.4.4.16-1-7 failing -15.4.4.16-1-8 failing -15.4.4.16-1-9 failing -15.4.4.16-2-1 failing -15.4.4.16-2-10 failing -15.4.4.16-2-11 failing -15.4.4.16-2-12 failing -15.4.4.16-2-13 failing -15.4.4.16-2-14 failing -15.4.4.16-2-15 failing -15.4.4.16-2-17 failing -15.4.4.16-2-18 failing -15.4.4.16-2-19 failing -15.4.4.16-2-3 failing -15.4.4.16-2-5 failing -15.4.4.16-2-6 failing -15.4.4.16-2-7 failing -15.4.4.16-2-8 failing -15.4.4.16-2-9 failing -15.4.4.16-3-1 failing -15.4.4.16-3-10 failing -15.4.4.16-3-11 failing -15.4.4.16-3-12 failing -15.4.4.16-3-13 failing -15.4.4.16-3-14 failing -15.4.4.16-3-15 failing -15.4.4.16-3-16 failing -15.4.4.16-3-17 failing -15.4.4.16-3-18 failing -15.4.4.16-3-19 failing -15.4.4.16-3-2 failing -15.4.4.16-3-20 failing -15.4.4.16-3-21 failing -15.4.4.16-3-22 failing -15.4.4.16-3-23 failing -15.4.4.16-3-24 failing -15.4.4.16-3-25 failing -15.4.4.16-3-28 failing -15.4.4.16-3-29 failing -15.4.4.16-3-3 failing -15.4.4.16-3-4 failing -15.4.4.16-3-5 failing -15.4.4.16-3-6 failing -15.4.4.16-3-7 failing -15.4.4.16-3-8 failing -15.4.4.16-3-9 failing -15.4.4.16-4-1 failing -15.4.4.16-4-15 failing -15.4.4.16-4-3 failing -15.4.4.16-4-4 failing -15.4.4.16-4-5 failing -15.4.4.16-4-6 failing -15.4.4.16-4-7 failing -15.4.4.16-4-8 failing -15.4.4.16-4-9 failing 15.4.4.16-5-17 failing -15.4.4.16-7-5 failing -15.4.4.16-7-8 failing -15.4.4.16-7-9 failing -15.4.4.16-7-b-10 failing -15.4.4.16-7-b-12 failing -15.4.4.16-7-b-15 failing -15.4.4.16-7-b-2 failing -15.4.4.16-7-b-3 failing -15.4.4.16-7-b-4 failing -15.4.4.16-7-b-6 failing -15.4.4.16-7-b-8 failing -15.4.4.16-7-c-i-1 failing -15.4.4.16-7-c-i-11 failing -15.4.4.16-7-c-i-13 failing -15.4.4.16-7-c-i-15 failing -15.4.4.16-7-c-i-17 failing -15.4.4.16-7-c-i-18 failing -15.4.4.16-7-c-i-19 failing -15.4.4.16-7-c-i-20 failing -15.4.4.16-7-c-i-21 failing -15.4.4.16-7-c-i-22 failing -15.4.4.16-7-c-i-23 failing -15.4.4.16-7-c-i-25 failing -15.4.4.16-7-c-i-26 failing -15.4.4.16-7-c-i-27 failing -15.4.4.16-7-c-i-29 failing -15.4.4.16-7-c-i-3 failing -15.4.4.16-7-c-i-30 failing -15.4.4.16-7-c-i-5 failing -15.4.4.16-7-c-i-7 failing -15.4.4.16-7-c-i-9 failing -15.4.4.16-7-c-ii-16 failing -15.4.4.16-7-c-ii-17 failing -15.4.4.16-7-c-ii-18 failing -15.4.4.16-7-c-ii-19 failing -15.4.4.16-7-c-ii-20 failing -15.4.4.16-7-c-ii-21 failing -15.4.4.16-7-c-ii-22 failing -15.4.4.16-7-c-ii-23 failing -15.4.4.16-7-c-ii-6 failing -15.4.4.16-7-c-ii-7 failing -15.4.4.16-7-c-ii-8 failing -15.4.4.16-7-c-iii-1 failing -15.4.4.16-7-c-iii-2 failing 15.4.4.16-7-c-iii-23 failing -15.4.4.16-7-c-iii-28 failing -15.4.4.16-7-c-iii-3 failing -15.4.4.16-7-c-iii-4 failing -15.4.4.16-8-10 failing -15.4.4.16-8-2 failing -15.4.4.16-8-3 failing -15.4.4.16-8-4 failing -15.4.4.16-8-5 failing -15.4.4.16-8-6 failing -15.4.4.16-8-7 failing -15.4.4.16-8-8 failing -15.4.4.17-1-1 failing 15.4.4.17-1-10 failing -15.4.4.17-1-11 failing -15.4.4.17-1-12 failing 15.4.4.17-1-13 failing -15.4.4.17-1-14 failing -15.4.4.17-1-15 failing -15.4.4.17-1-2 failing -15.4.4.17-1-3 failing -15.4.4.17-1-4 failing -15.4.4.17-1-5 failing -15.4.4.17-1-6 failing -15.4.4.17-1-7 failing -15.4.4.17-1-8 failing -15.4.4.17-1-9 failing -15.4.4.17-2-1 failing -15.4.4.17-2-10 failing -15.4.4.17-2-11 failing -15.4.4.17-2-12 failing -15.4.4.17-2-13 failing -15.4.4.17-2-14 failing -15.4.4.17-2-15 failing -15.4.4.17-2-17 failing -15.4.4.17-2-18 failing -15.4.4.17-2-19 failing -15.4.4.17-2-3 failing -15.4.4.17-2-5 failing -15.4.4.17-2-6 failing -15.4.4.17-2-7 failing -15.4.4.17-2-8 failing -15.4.4.17-2-9 failing -15.4.4.17-3-1 failing -15.4.4.17-3-10 failing -15.4.4.17-3-11 failing -15.4.4.17-3-12 failing -15.4.4.17-3-13 failing -15.4.4.17-3-14 failing -15.4.4.17-3-15 failing -15.4.4.17-3-16 failing -15.4.4.17-3-17 failing -15.4.4.17-3-18 failing -15.4.4.17-3-19 failing -15.4.4.17-3-2 failing -15.4.4.17-3-20 failing -15.4.4.17-3-21 failing -15.4.4.17-3-22 failing -15.4.4.17-3-23 failing -15.4.4.17-3-24 failing -15.4.4.17-3-25 failing -15.4.4.17-3-28 failing -15.4.4.17-3-29 failing -15.4.4.17-3-3 failing -15.4.4.17-3-4 failing -15.4.4.17-3-5 failing -15.4.4.17-3-6 failing -15.4.4.17-3-7 failing -15.4.4.17-3-8 failing -15.4.4.17-3-9 failing -15.4.4.17-4-1 failing -15.4.4.17-4-15 failing -15.4.4.17-4-3 failing -15.4.4.17-4-4 failing -15.4.4.17-4-5 failing -15.4.4.17-4-6 failing -15.4.4.17-4-7 failing -15.4.4.17-4-8 failing -15.4.4.17-4-9 failing 15.4.4.17-5-17 failing -15.4.4.17-7-5 failing -15.4.4.17-7-8 failing -15.4.4.17-7-9 failing -15.4.4.17-7-b-10 failing -15.4.4.17-7-b-12 failing -15.4.4.17-7-b-15 failing -15.4.4.17-7-b-2 failing -15.4.4.17-7-b-3 failing -15.4.4.17-7-b-4 failing -15.4.4.17-7-b-6 failing -15.4.4.17-7-b-8 failing -15.4.4.17-7-c-i-1 failing -15.4.4.17-7-c-i-11 failing -15.4.4.17-7-c-i-13 failing -15.4.4.17-7-c-i-15 failing -15.4.4.17-7-c-i-17 failing -15.4.4.17-7-c-i-18 failing -15.4.4.17-7-c-i-19 failing -15.4.4.17-7-c-i-20 failing -15.4.4.17-7-c-i-21 failing -15.4.4.17-7-c-i-22 failing -15.4.4.17-7-c-i-23 failing -15.4.4.17-7-c-i-25 failing -15.4.4.17-7-c-i-26 failing -15.4.4.17-7-c-i-27 failing -15.4.4.17-7-c-i-29 failing -15.4.4.17-7-c-i-3 failing -15.4.4.17-7-c-i-30 failing -15.4.4.17-7-c-i-5 failing -15.4.4.17-7-c-i-7 failing -15.4.4.17-7-c-i-9 failing -15.4.4.17-7-c-ii-16 failing -15.4.4.17-7-c-ii-17 failing -15.4.4.17-7-c-ii-18 failing -15.4.4.17-7-c-ii-19 failing -15.4.4.17-7-c-ii-20 failing -15.4.4.17-7-c-ii-21 failing -15.4.4.17-7-c-ii-22 failing -15.4.4.17-7-c-ii-23 failing -15.4.4.17-7-c-ii-6 failing -15.4.4.17-7-c-ii-8 failing -15.4.4.17-7-c-iii-1 failing -15.4.4.17-7-c-iii-2 failing 15.4.4.17-7-c-iii-23 failing -15.4.4.17-7-c-iii-28 failing -15.4.4.17-7-c-iii-3 failing -15.4.4.17-7-c-iii-4 failing -15.4.4.17-8-10 failing -15.4.4.17-8-2 failing -15.4.4.17-8-3 failing -15.4.4.17-8-4 failing -15.4.4.17-8-5 failing -15.4.4.17-8-6 failing -15.4.4.17-8-7 failing -15.4.4.17-8-8 failing -15.4.4.18-1-1 failing 15.4.4.18-1-10 failing -15.4.4.18-1-11 failing -15.4.4.18-1-12 failing 15.4.4.18-1-13 failing -15.4.4.18-1-14 failing -15.4.4.18-1-15 failing -15.4.4.18-1-2 failing -15.4.4.18-1-3 failing -15.4.4.18-1-4 failing -15.4.4.18-1-5 failing -15.4.4.18-1-6 failing -15.4.4.18-1-7 failing -15.4.4.18-1-8 failing -15.4.4.18-1-9 failing -15.4.4.18-2-1 failing -15.4.4.18-2-10 failing -15.4.4.18-2-11 failing -15.4.4.18-2-12 failing -15.4.4.18-2-13 failing -15.4.4.18-2-14 failing -15.4.4.18-2-15 failing -15.4.4.18-2-17 failing -15.4.4.18-2-18 failing -15.4.4.18-2-19 failing -15.4.4.18-2-3 failing -15.4.4.18-2-5 failing -15.4.4.18-2-6 failing -15.4.4.18-2-7 failing -15.4.4.18-2-8 failing -15.4.4.18-2-9 failing -15.4.4.18-3-1 failing -15.4.4.18-3-10 failing -15.4.4.18-3-11 failing -15.4.4.18-3-12 failing -15.4.4.18-3-13 failing -15.4.4.18-3-14 failing -15.4.4.18-3-15 failing -15.4.4.18-3-16 failing -15.4.4.18-3-17 failing -15.4.4.18-3-18 failing -15.4.4.18-3-19 failing -15.4.4.18-3-2 failing -15.4.4.18-3-20 failing -15.4.4.18-3-21 failing -15.4.4.18-3-22 failing -15.4.4.18-3-23 failing -15.4.4.18-3-24 failing -15.4.4.18-3-25 failing -15.4.4.18-3-28 failing -15.4.4.18-3-29 failing -15.4.4.18-3-3 failing -15.4.4.18-3-4 failing -15.4.4.18-3-5 failing -15.4.4.18-3-6 failing -15.4.4.18-3-7 failing -15.4.4.18-3-8 failing -15.4.4.18-3-9 failing -15.4.4.18-4-1 failing -15.4.4.18-4-15 failing -15.4.4.18-4-3 failing -15.4.4.18-4-4 failing -15.4.4.18-4-5 failing -15.4.4.18-4-6 failing -15.4.4.18-4-7 failing -15.4.4.18-4-8 failing -15.4.4.18-4-9 failing 15.4.4.18-5-17 failing -15.4.4.18-7-1 failing -15.4.4.18-7-4 failing -15.4.4.18-7-8 failing -15.4.4.18-7-9 failing -15.4.4.18-7-b-10 failing -15.4.4.18-7-b-12 failing -15.4.4.18-7-b-15 failing -15.4.4.18-7-b-2 failing -15.4.4.18-7-b-3 failing -15.4.4.18-7-b-4 failing -15.4.4.18-7-b-6 failing -15.4.4.18-7-b-8 failing -15.4.4.18-7-c-i-1 failing -15.4.4.18-7-c-i-11 failing -15.4.4.18-7-c-i-13 failing -15.4.4.18-7-c-i-15 failing -15.4.4.18-7-c-i-17 failing -15.4.4.18-7-c-i-18 failing -15.4.4.18-7-c-i-19 failing -15.4.4.18-7-c-i-20 failing -15.4.4.18-7-c-i-21 failing -15.4.4.18-7-c-i-22 failing -15.4.4.18-7-c-i-23 failing -15.4.4.18-7-c-i-25 failing -15.4.4.18-7-c-i-26 failing -15.4.4.18-7-c-i-27 failing -15.4.4.18-7-c-i-29 failing -15.4.4.18-7-c-i-3 failing -15.4.4.18-7-c-i-30 failing -15.4.4.18-7-c-i-5 failing -15.4.4.18-7-c-i-7 failing -15.4.4.18-7-c-i-9 failing -15.4.4.18-7-c-ii-16 failing -15.4.4.18-7-c-ii-17 failing -15.4.4.18-7-c-ii-18 failing -15.4.4.18-7-c-ii-19 failing -15.4.4.18-7-c-ii-20 failing -15.4.4.18-7-c-ii-21 failing -15.4.4.18-7-c-ii-22 failing -15.4.4.18-7-c-ii-23 failing -15.4.4.18-7-c-ii-6 failing -15.4.4.18-7-c-ii-8 failing -15.4.4.18-8-10 failing -15.4.4.18-8-2 failing -15.4.4.18-8-3 failing -15.4.4.18-8-4 failing -15.4.4.18-8-5 failing -15.4.4.18-8-6 failing -15.4.4.18-8-7 failing -15.4.4.18-8-8 failing -15.4.4.18-8-9 failing 15.4.4.19-1-1 failing 15.4.4.19-1-10 failing 15.4.4.19-1-11 failing @@ -2311,11 +1956,7 @@ S15.4.4.13_A1_T2 failing 15.4.4.17-7-c-i-6 failing # Regressions introduced with https://codereview.qt-project.org/#change,45044 -15.4.4.16-7-b-1 failing -15.4.4.17-7-b-1 failing -15.4.4.18-7-b-1 failing 15.4.4.19-8-b-1 failing 15.4.4.20-9-b-1 failing 15.4.4.21-8-b-iii-1-6 failing 15.4.4.22-9-9 failing - -- cgit v1.2.3 From 404da13209b39c136a2e0de727e22511e15044dc Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 17 Jan 2013 21:58:24 +0100 Subject: Implement Array.prototype.map/filter correctly Fixes another 260 test cases. Change-Id: I4f6299119c1859dced1fd2f50c3e5d7f0ed133ed Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 61 +++++++----- tests/TestExpectations | 248 +------------------------------------------------ 2 files changed, 39 insertions(+), 270 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index da16f7c0bb..fc4cd92986 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2076,49 +2076,64 @@ Value ArrayPrototype::method_forEach(ExecutionContext *ctx) Value ArrayPrototype::method_map(ExecutionContext *ctx) { - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.map")); + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); - Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); - ArrayObject *a = ctx->engine->newArrayObject(ctx)->asArrayObject(); - a->array.setLengthUnchecked(instance->array.length()); - for (quint32 k = 0; k < instance->array.length(); ++k) { - Value v = instance->__get__(ctx, k); - if (v.isUndefined()) + + ArrayObject *a = ctx->engine->newArrayObject(ctx); + a->array.setLength(len); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) continue; + Value args[3]; args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); - a->array.set(k, r); + Value mapped = callback->call(ctx, thisArg, args, 3); + a->array.set(k, mapped); } return Value::fromObject(a); } Value ArrayPrototype::method_filter(ExecutionContext *ctx) { - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.filter")); + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); - Value callback = ctx->argument(0); Value thisArg = ctx->argument(1); - ArrayObject *a = ctx->engine->newArrayObject(ctx)->asArrayObject(); - for (quint32 k = 0; k < instance->array.length(); ++k) { - Value v = instance->__get__(ctx, k); - if (v.isUndefined()) + + ArrayObject *a = ctx->engine->newArrayObject(ctx); + + uint to = 0; + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) continue; + Value args[3]; args[0] = v; args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, thisArg, callback, args, 3); - if (__qmljs_to_boolean(r, ctx)) { - const uint index = a->array.length(); - a->array.set(index, v); + Value selected = callback->call(ctx, thisArg, args, 3); + if (__qmljs_to_boolean(selected, ctx)) { + a->array.set(to, v); + ++to; } } return Value::fromObject(a); diff --git a/tests/TestExpectations b/tests/TestExpectations index c60b3ac53b..2591b7c8cc 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -729,258 +729,14 @@ S15.4.4.13_A4_T2 failing 15.4.4.18-1-10 failing 15.4.4.18-1-13 failing 15.4.4.18-5-17 failing -15.4.4.19-1-1 failing 15.4.4.19-1-10 failing -15.4.4.19-1-11 failing -15.4.4.19-1-12 failing 15.4.4.19-1-13 failing -15.4.4.19-1-14 failing -15.4.4.19-1-15 failing -15.4.4.19-1-2 failing -15.4.4.19-1-3 failing -15.4.4.19-1-4 failing -15.4.4.19-1-5 failing -15.4.4.19-1-6 failing -15.4.4.19-1-7 failing -15.4.4.19-1-8 failing -15.4.4.19-1-9 failing -15.4.4.19-2-1 failing -15.4.4.19-2-10 failing -15.4.4.19-2-11 failing -15.4.4.19-2-12 failing -15.4.4.19-2-13 failing -15.4.4.19-2-14 failing -15.4.4.19-2-15 failing -15.4.4.19-2-17 failing -15.4.4.19-2-18 failing -15.4.4.19-2-19 failing -15.4.4.19-2-3 failing -15.4.4.19-2-5 failing -15.4.4.19-2-6 failing -15.4.4.19-2-7 failing -15.4.4.19-2-8 failing -15.4.4.19-2-9 failing -15.4.4.19-3-1 failing -15.4.4.19-3-10 failing -15.4.4.19-3-11 failing -15.4.4.19-3-12 failing -15.4.4.19-3-13 failing -15.4.4.19-3-14 failing -15.4.4.19-3-15 failing -15.4.4.19-3-16 failing -15.4.4.19-3-17 failing -15.4.4.19-3-18 failing -15.4.4.19-3-19 failing -15.4.4.19-3-2 failing -15.4.4.19-3-20 failing -15.4.4.19-3-21 failing -15.4.4.19-3-22 failing -15.4.4.19-3-23 failing -15.4.4.19-3-24 failing -15.4.4.19-3-25 failing -15.4.4.19-3-28 failing -15.4.4.19-3-29 failing -15.4.4.19-3-3 failing -15.4.4.19-3-4 failing -15.4.4.19-3-5 failing -15.4.4.19-3-6 failing -15.4.4.19-3-7 failing -15.4.4.19-3-8 failing -15.4.4.19-3-9 failing -15.4.4.19-4-1 failing -15.4.4.19-4-15 failing -15.4.4.19-4-3 failing -15.4.4.19-4-4 failing -15.4.4.19-4-5 failing -15.4.4.19-4-6 failing -15.4.4.19-4-7 failing -15.4.4.19-4-8 failing -15.4.4.19-4-9 failing 15.4.4.19-5-1 failing 15.4.4.19-5-17 failing -15.4.4.19-8-1 failing -15.4.4.19-8-5 failing -15.4.4.19-8-8 failing -15.4.4.19-8-b-10 failing -15.4.4.19-8-b-12 failing -15.4.4.19-8-b-15 failing -15.4.4.19-8-b-2 failing -15.4.4.19-8-b-3 failing -15.4.4.19-8-b-4 failing -15.4.4.19-8-b-6 failing -15.4.4.19-8-b-8 failing -15.4.4.19-8-c-i-1 failing -15.4.4.19-8-c-i-11 failing -15.4.4.19-8-c-i-13 failing -15.4.4.19-8-c-i-15 failing -15.4.4.19-8-c-i-17 failing -15.4.4.19-8-c-i-18 failing -15.4.4.19-8-c-i-19 failing -15.4.4.19-8-c-i-20 failing -15.4.4.19-8-c-i-21 failing -15.4.4.19-8-c-i-22 failing -15.4.4.19-8-c-i-23 failing -15.4.4.19-8-c-i-25 failing -15.4.4.19-8-c-i-26 failing -15.4.4.19-8-c-i-27 failing -15.4.4.19-8-c-i-29 failing -15.4.4.19-8-c-i-3 failing -15.4.4.19-8-c-i-30 failing -15.4.4.19-8-c-i-5 failing -15.4.4.19-8-c-i-7 failing -15.4.4.19-8-c-i-9 failing -15.4.4.19-8-c-ii-16 failing -15.4.4.19-8-c-ii-17 failing -15.4.4.19-8-c-ii-18 failing -15.4.4.19-8-c-ii-19 failing -15.4.4.19-8-c-ii-20 failing -15.4.4.19-8-c-ii-21 failing -15.4.4.19-8-c-ii-22 failing -15.4.4.19-8-c-ii-23 failing -15.4.4.19-8-c-ii-6 failing -15.4.4.19-8-c-ii-8 failing -15.4.4.19-8-c-iii-2 failing -15.4.4.19-8-c-iii-3 failing -15.4.4.19-8-c-iii-4 failing -15.4.4.19-8-c-iii-5 failing -15.4.4.19-9-10 failing -15.4.4.19-9-11 failing -15.4.4.19-9-12 failing -15.4.4.19-9-3 failing -15.4.4.19-9-5 failing -15.4.4.19-9-6 failing -15.4.4.19-9-7 failing -15.4.4.19-9-8 failing -15.4.4.19-9-9 failing -15.4.4.20-1-1 failing 15.4.4.20-1-10 failing -15.4.4.20-1-11 failing -15.4.4.20-1-12 failing 15.4.4.20-1-13 failing -15.4.4.20-1-14 failing -15.4.4.20-1-15 failing -15.4.4.20-1-2 failing -15.4.4.20-1-3 failing -15.4.4.20-1-4 failing -15.4.4.20-1-5 failing -15.4.4.20-1-6 failing -15.4.4.20-1-7 failing -15.4.4.20-1-8 failing -15.4.4.20-1-9 failing -15.4.4.20-10-3 failing -15.4.4.20-2-1 failing -15.4.4.20-2-10 failing -15.4.4.20-2-11 failing -15.4.4.20-2-12 failing -15.4.4.20-2-13 failing -15.4.4.20-2-14 failing -15.4.4.20-2-15 failing -15.4.4.20-2-17 failing -15.4.4.20-2-18 failing -15.4.4.20-2-19 failing -15.4.4.20-2-3 failing -15.4.4.20-2-5 failing -15.4.4.20-2-6 failing -15.4.4.20-2-7 failing -15.4.4.20-2-8 failing -15.4.4.20-2-9 failing -15.4.4.20-3-1 failing -15.4.4.20-3-10 failing -15.4.4.20-3-11 failing -15.4.4.20-3-12 failing -15.4.4.20-3-13 failing -15.4.4.20-3-14 failing -15.4.4.20-3-15 failing -15.4.4.20-3-16 failing -15.4.4.20-3-17 failing -15.4.4.20-3-18 failing -15.4.4.20-3-19 failing -15.4.4.20-3-2 failing -15.4.4.20-3-20 failing -15.4.4.20-3-21 failing -15.4.4.20-3-22 failing -15.4.4.20-3-23 failing -15.4.4.20-3-24 failing -15.4.4.20-3-25 failing -15.4.4.20-3-28 failing -15.4.4.20-3-29 failing -15.4.4.20-3-3 failing -15.4.4.20-3-4 failing -15.4.4.20-3-5 failing -15.4.4.20-3-6 failing -15.4.4.20-3-7 failing -15.4.4.20-3-8 failing -15.4.4.20-3-9 failing -15.4.4.20-4-1 failing -15.4.4.20-4-15 failing -15.4.4.20-4-3 failing -15.4.4.20-4-4 failing -15.4.4.20-4-5 failing -15.4.4.20-4-6 failing -15.4.4.20-4-7 failing -15.4.4.20-4-8 failing -15.4.4.20-4-9 failing 15.4.4.20-5-17 failing -15.4.4.20-6-2 failing -15.4.4.20-6-3 failing -15.4.4.20-6-4 failing -15.4.4.20-6-5 failing -15.4.4.20-6-6 failing -15.4.4.20-6-7 failing -15.4.4.20-6-8 failing -15.4.4.20-9-1 failing -15.4.4.20-9-5 failing -15.4.4.20-9-8 failing -15.4.4.20-9-9 failing -15.4.4.20-9-b-10 failing -15.4.4.20-9-b-12 failing -15.4.4.20-9-b-15 failing -15.4.4.20-9-b-2 failing -15.4.4.20-9-b-3 failing -15.4.4.20-9-b-4 failing -15.4.4.20-9-b-6 failing -15.4.4.20-9-b-8 failing -15.4.4.20-9-c-i-1 failing -15.4.4.20-9-c-i-11 failing -15.4.4.20-9-c-i-13 failing -15.4.4.20-9-c-i-15 failing -15.4.4.20-9-c-i-17 failing -15.4.4.20-9-c-i-18 failing -15.4.4.20-9-c-i-19 failing -15.4.4.20-9-c-i-20 failing -15.4.4.20-9-c-i-21 failing -15.4.4.20-9-c-i-22 failing -15.4.4.20-9-c-i-23 failing -15.4.4.20-9-c-i-25 failing -15.4.4.20-9-c-i-26 failing -15.4.4.20-9-c-i-27 failing -15.4.4.20-9-c-i-29 failing -15.4.4.20-9-c-i-3 failing -15.4.4.20-9-c-i-30 failing -15.4.4.20-9-c-i-5 failing -15.4.4.20-9-c-i-7 failing -15.4.4.20-9-c-i-9 failing -15.4.4.20-9-c-ii-16 failing -15.4.4.20-9-c-ii-17 failing -15.4.4.20-9-c-ii-18 failing -15.4.4.20-9-c-ii-19 failing -15.4.4.20-9-c-ii-20 failing -15.4.4.20-9-c-ii-21 failing -15.4.4.20-9-c-ii-22 failing -15.4.4.20-9-c-ii-23 failing -15.4.4.20-9-c-ii-6 failing -15.4.4.20-9-c-ii-7 failing -15.4.4.20-9-c-ii-8 failing -15.4.4.20-9-c-iii-1-1 failing -15.4.4.20-9-c-iii-1-2 failing -15.4.4.20-9-c-iii-1-3 failing -15.4.4.20-9-c-iii-1-4 failing -15.4.4.20-9-c-iii-2 failing 15.4.4.20-9-c-iii-24 failing -15.4.4.20-9-c-iii-29 failing -15.4.4.20-9-c-iii-3 failing -15.4.4.20-9-c-iii-4 failing -15.4.4.20-9-c-iii-5 failing 15.4.4.21-1-1 failing 15.4.4.21-1-10 failing 15.4.4.21-1-11 failing @@ -1956,7 +1712,5 @@ S15.4.4.13_A1_T2 failing 15.4.4.17-7-c-i-6 failing # Regressions introduced with https://codereview.qt-project.org/#change,45044 -15.4.4.19-8-b-1 failing -15.4.4.20-9-b-1 failing 15.4.4.21-8-b-iii-1-6 failing -15.4.4.22-9-9 failing +15.4.4.22-9-9 failing \ No newline at end of file -- cgit v1.2.3 From 0dd52c91a2ed0c56f3660ec7d9d618a748fbbc25 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 17 Jan 2013 22:22:39 +0100 Subject: Implement Array.prototype.reduce/reduceRight This fixes another 380 test cases Change-Id: I9a09bf1cd992a3a370d8e3156a612c91dc95d3b3 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 114 +++++++++------ tests/TestExpectations | 380 +------------------------------------------------ 2 files changed, 73 insertions(+), 421 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index fc4cd92986..0ef0d2a8de 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -2141,60 +2141,90 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) Value ArrayPrototype::method_reduce(ExecutionContext *ctx) { - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.reduce")); - - Value callback = ctx->argument(0); - Value initialValue = ctx->argument(1); - Value acc = initialValue; - for (quint32 k = 0; k < instance->array.length(); ++k) { - Value v = instance->__get__(ctx, k); - if (v.isUndefined()) - continue; + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - if (acc.isUndefined()) { - acc = v; - continue; + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + uint k = 0; + Value acc; + if (ctx->argumentCount > 1) { + acc = ctx->argument(1); + } else { + bool kPresent = false; + while (k < len && !kPresent) { + Value v = instance->__get__(ctx, k, &kPresent); + if (kPresent) + acc = v; + ++k; } + if (!kPresent) + __qmljs_throw_type_error(ctx); + } - Value args[4]; - args[0] = acc; - args[1] = v; - args[2] = Value::fromDouble(k); - args[3] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, Value::undefinedValue(), callback, args, 4); - acc = r; + while (k < len) { + bool kPresent; + Value v = instance->__get__(ctx, k, &kPresent); + if (kPresent) { + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k); + args[3] = ctx->thisObject; + acc = callback->call(ctx, Value::undefinedValue(), args, 4); + } + ++k; } return acc; } Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) { - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.reduceRight")); - - Value callback = ctx->argument(0); - Value initialValue = ctx->argument(1); - Value acc = initialValue; - for (int k = instance->array.length() - 1; k != -1; --k) { - Value v = instance->__get__(ctx, k); - if (v.isUndefined()) - continue; + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - if (acc.isUndefined()) { - acc = v; - continue; + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + if (len == 0) { + if (ctx->argumentCount == 1) + __qmljs_throw_type_error(ctx); + return ctx->argument(1); + } + + uint k = len; + Value acc; + if (ctx->argumentCount > 1) { + acc = ctx->argument(1); + } else { + bool kPresent = false; + while (k > 0 && !kPresent) { + Value v = instance->__get__(ctx, k - 1, &kPresent); + if (kPresent) + acc = v; + --k; } + if (!kPresent) + __qmljs_throw_type_error(ctx); + } - Value args[4]; - args[0] = acc; - args[1] = v; - args[2] = Value::fromDouble(k); - args[3] = ctx->thisObject; - Value r = __qmljs_call_value(ctx, Value::undefinedValue(), callback, args, 4); - acc = r; + while (k > 0) { + bool kPresent; + Value v = instance->__get__(ctx, k - 1, &kPresent); + if (kPresent) { + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k - 1); + args[3] = ctx->thisObject; + acc = callback->call(ctx, Value::undefinedValue(), args, 4); + } + --k; } return acc; } diff --git a/tests/TestExpectations b/tests/TestExpectations index 2591b7c8cc..7f29ea9b45 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -737,391 +737,14 @@ S15.4.4.13_A4_T2 failing 15.4.4.20-1-13 failing 15.4.4.20-5-17 failing 15.4.4.20-9-c-iii-24 failing -15.4.4.21-1-1 failing 15.4.4.21-1-10 failing -15.4.4.21-1-11 failing -15.4.4.21-1-12 failing 15.4.4.21-1-13 failing -15.4.4.21-1-14 failing -15.4.4.21-1-15 failing -15.4.4.21-1-2 failing -15.4.4.21-1-3 failing -15.4.4.21-1-4 failing -15.4.4.21-1-5 failing -15.4.4.21-1-6 failing -15.4.4.21-1-7 failing -15.4.4.21-1-8 failing -15.4.4.21-1-9 failing -15.4.4.21-10-3 failing -15.4.4.21-10-4 failing -15.4.4.21-10-6 failing -15.4.4.21-10-7 failing -15.4.4.21-2-1 failing -15.4.4.21-2-10 failing -15.4.4.21-2-11 failing -15.4.4.21-2-12 failing -15.4.4.21-2-13 failing -15.4.4.21-2-14 failing -15.4.4.21-2-15 failing -15.4.4.21-2-17 failing -15.4.4.21-2-18 failing -15.4.4.21-2-19 failing -15.4.4.21-2-3 failing -15.4.4.21-2-5 failing -15.4.4.21-2-6 failing -15.4.4.21-2-7 failing -15.4.4.21-2-8 failing -15.4.4.21-2-9 failing -15.4.4.21-3-1 failing -15.4.4.21-3-10 failing -15.4.4.21-3-11 failing -15.4.4.21-3-12 failing -15.4.4.21-3-13 failing -15.4.4.21-3-14 failing -15.4.4.21-3-15 failing -15.4.4.21-3-16 failing -15.4.4.21-3-17 failing -15.4.4.21-3-18 failing -15.4.4.21-3-19 failing -15.4.4.21-3-2 failing -15.4.4.21-3-20 failing -15.4.4.21-3-21 failing -15.4.4.21-3-22 failing -15.4.4.21-3-23 failing -15.4.4.21-3-24 failing -15.4.4.21-3-25 failing -15.4.4.21-3-28 failing -15.4.4.21-3-29 failing -15.4.4.21-3-3 failing -15.4.4.21-3-4 failing -15.4.4.21-3-5 failing -15.4.4.21-3-6 failing -15.4.4.21-3-7 failing -15.4.4.21-3-8 failing -15.4.4.21-3-9 failing -15.4.4.21-4-1 failing -15.4.4.21-4-15 failing -15.4.4.21-4-3 failing -15.4.4.21-4-4 failing -15.4.4.21-4-5 failing -15.4.4.21-4-6 failing -15.4.4.21-4-7 failing -15.4.4.21-4-8 failing -15.4.4.21-4-9 failing -15.4.4.21-5-1 failing -15.4.4.21-5-10 failing -15.4.4.21-5-11 failing -15.4.4.21-5-2 failing -15.4.4.21-5-3 failing -15.4.4.21-5-4 failing -15.4.4.21-5-5 failing -15.4.4.21-5-6 failing -15.4.4.21-5-7 failing -15.4.4.21-5-8 failing -15.4.4.21-7-2 failing -15.4.4.21-7-3 failing -15.4.4.21-7-4 failing -15.4.4.21-7-5 failing -15.4.4.21-7-6 failing -15.4.4.21-7-7 failing -15.4.4.21-7-8 failing -15.4.4.21-7-9 failing -15.4.4.21-8-b-2 failing -15.4.4.21-8-b-ii-1 failing -15.4.4.21-8-b-ii-2 failing -15.4.4.21-8-b-iii-1-1 failing -15.4.4.21-8-b-iii-1-11 failing -15.4.4.21-8-b-iii-1-13 failing -15.4.4.21-8-b-iii-1-15 failing -15.4.4.21-8-b-iii-1-17 failing -15.4.4.21-8-b-iii-1-18 failing -15.4.4.21-8-b-iii-1-19 failing -15.4.4.21-8-b-iii-1-20 failing -15.4.4.21-8-b-iii-1-21 failing -15.4.4.21-8-b-iii-1-22 failing -15.4.4.21-8-b-iii-1-23 failing -15.4.4.21-8-b-iii-1-25 failing -15.4.4.21-8-b-iii-1-26 failing -15.4.4.21-8-b-iii-1-27 failing -15.4.4.21-8-b-iii-1-28 failing -15.4.4.21-8-b-iii-1-29 failing -15.4.4.21-8-b-iii-1-3 failing -15.4.4.21-8-b-iii-1-31 failing -15.4.4.21-8-b-iii-1-32 failing -15.4.4.21-8-b-iii-1-5 failing -15.4.4.21-8-b-iii-1-7 failing -15.4.4.21-8-b-iii-1-9 failing -15.4.4.21-8-c-1 failing -15.4.4.21-8-c-2 failing -15.4.4.21-8-c-3 failing -15.4.4.21-8-c-5 failing -15.4.4.21-8-c-6 failing -15.4.4.21-9-1 failing -15.4.4.21-9-10 failing -15.4.4.21-9-8 failing -15.4.4.21-9-b-10 failing -15.4.4.21-9-b-12 failing -15.4.4.21-9-b-15 failing -15.4.4.21-9-b-16 failing -15.4.4.21-9-b-17 failing -15.4.4.21-9-b-18 failing -15.4.4.21-9-b-19 failing -15.4.4.21-9-b-2 failing -15.4.4.21-9-b-20 failing -15.4.4.21-9-b-21 failing -15.4.4.21-9-b-23 failing -15.4.4.21-9-b-25 failing -15.4.4.21-9-b-26 failing -15.4.4.21-9-b-28 failing -15.4.4.21-9-b-3 failing -15.4.4.21-9-b-4 failing -15.4.4.21-9-b-6 failing -15.4.4.21-9-b-8 failing -15.4.4.21-9-c-1 failing -15.4.4.21-9-c-i-1 failing -15.4.4.21-9-c-i-10 failing -15.4.4.21-9-c-i-11 failing -15.4.4.21-9-c-i-12 failing -15.4.4.21-9-c-i-13 failing -15.4.4.21-9-c-i-14 failing -15.4.4.21-9-c-i-15 failing -15.4.4.21-9-c-i-16 failing -15.4.4.21-9-c-i-17 failing -15.4.4.21-9-c-i-18 failing -15.4.4.21-9-c-i-19 failing -15.4.4.21-9-c-i-2 failing -15.4.4.21-9-c-i-20 failing -15.4.4.21-9-c-i-21 failing -15.4.4.21-9-c-i-22 failing -15.4.4.21-9-c-i-23 failing -15.4.4.21-9-c-i-25 failing -15.4.4.21-9-c-i-26 failing -15.4.4.21-9-c-i-27 failing -15.4.4.21-9-c-i-28 failing -15.4.4.21-9-c-i-29 failing -15.4.4.21-9-c-i-3 failing -15.4.4.22-10-3 failing -15.4.4.22-10-4 failing -15.4.4.22-10-6 failing -15.4.4.22-10-7 failing -15.4.4.22-10-8 failing -15.4.4.22-2-1 failing -15.4.4.22-2-10 failing -15.4.4.22-2-11 failing -15.4.4.22-2-12 failing -15.4.4.22-2-13 failing -15.4.4.22-2-14 failing -15.4.4.22-2-15 failing -15.4.4.22-2-17 failing -15.4.4.22-2-18 failing -15.4.4.22-2-19 failing -15.4.4.22-2-3 failing -15.4.4.22-2-5 failing -15.4.4.22-2-6 failing -15.4.4.22-2-7 failing -15.4.4.22-2-8 failing -15.4.4.22-2-9 failing -15.4.4.22-3-1 failing -15.4.4.22-3-10 failing -15.4.4.22-3-11 failing -15.4.4.22-3-12 failing -15.4.4.22-3-13 failing -15.4.4.22-3-14 failing -15.4.4.22-3-15 failing -15.4.4.22-3-16 failing -15.4.4.22-3-17 failing -15.4.4.22-3-18 failing -15.4.4.22-3-19 failing -15.4.4.22-3-2 failing -15.4.4.22-3-20 failing -15.4.4.22-3-21 failing -15.4.4.22-3-22 failing -15.4.4.22-3-23 failing -15.4.4.22-3-24 failing -15.4.4.22-3-25 failing -15.4.4.22-3-28 failing -15.4.4.22-3-29 failing -15.4.4.22-3-3 failing -15.4.4.22-3-4 failing -15.4.4.22-3-5 failing -15.4.4.22-3-6 failing -15.4.4.22-3-7 failing -15.4.4.22-3-8 failing -15.4.4.22-3-9 failing -15.4.4.22-4-1 failing -15.4.4.22-4-15 failing -15.4.4.22-4-3 failing -15.4.4.22-4-4 failing -15.4.4.22-4-5 failing -15.4.4.22-4-6 failing -15.4.4.22-4-7 failing -15.4.4.22-4-8 failing -15.4.4.22-4-9 failing -15.4.4.21-9-c-i-30 failing -15.4.4.21-9-c-i-31 failing -15.4.4.21-9-c-i-32 failing -15.4.4.21-9-c-i-4 failing -15.4.4.21-9-c-i-5 failing 15.4.4.21-9-c-i-6 failing -15.4.4.21-9-c-i-7 failing -15.4.4.21-9-c-i-8 failing -15.4.4.21-9-c-i-9 failing -15.4.4.21-9-c-ii-16 failing -15.4.4.21-9-c-ii-18 failing -15.4.4.21-9-c-ii-20 failing -15.4.4.21-9-c-ii-21 failing -15.4.4.21-9-c-ii-22 failing -15.4.4.21-9-c-ii-23 failing -15.4.4.21-9-c-ii-24 failing -15.4.4.21-9-c-ii-25 failing -15.4.4.21-9-c-ii-26 failing -15.4.4.21-9-c-ii-27 failing -15.4.4.21-9-c-ii-28 failing -15.4.4.21-9-c-ii-29 failing -15.4.4.21-9-c-ii-30 failing -15.4.4.21-9-c-ii-31 failing -15.4.4.21-9-c-ii-32 failing 15.4.4.21-9-c-ii-33 failing -15.4.4.21-9-c-ii-34 failing -15.4.4.21-9-c-ii-35 failing -15.4.4.21-9-c-ii-37 failing -15.4.4.21-9-c-ii-4 failing -15.4.4.21-9-c-ii-5 failing -15.4.4.21-9-c-ii-8 failing -15.4.4.21-9-c-ii-9 failing -15.4.4.22-1-1 failing 15.4.4.22-1-10 failing -15.4.4.22-1-11 failing -15.4.4.22-1-12 failing 15.4.4.22-1-13 failing -15.4.4.22-1-14 failing -15.4.4.22-1-15 failing -15.4.4.22-1-2 failing -15.4.4.22-1-3 failing -15.4.4.22-1-4 failing -15.4.4.22-1-5 failing -15.4.4.22-1-6 failing -15.4.4.22-1-7 failing -15.4.4.22-1-8 failing -15.4.4.22-1-9 failing -15.4.4.22-5-1 failing -15.4.4.22-5-10 failing -15.4.4.22-5-11 failing -15.4.4.22-5-2 failing -15.4.4.22-5-3 failing -15.4.4.22-5-4 failing -15.4.4.22-5-5 failing -15.4.4.22-5-6 failing -15.4.4.22-5-7 failing -15.4.4.22-5-8 failing -15.4.4.22-7-2 failing -15.4.4.22-7-3 failing -15.4.4.22-7-4 failing -15.4.4.22-7-5 failing -15.4.4.22-7-6 failing -15.4.4.22-7-7 failing -15.4.4.22-7-8 failing -15.4.4.22-7-9 failing -15.4.4.22-8-b-ii-1 failing -15.4.4.22-8-b-ii-2 failing -15.4.4.22-8-b-iii-1-1 failing -15.4.4.22-8-b-iii-1-11 failing -15.4.4.22-8-b-iii-1-13 failing -15.4.4.22-8-b-iii-1-15 failing -15.4.4.22-8-b-iii-1-17 failing -15.4.4.22-8-b-iii-1-18 failing -15.4.4.22-8-b-iii-1-19 failing -15.4.4.22-8-b-iii-1-20 failing -15.4.4.22-8-b-iii-1-21 failing -15.4.4.22-8-b-iii-1-22 failing -15.4.4.22-8-b-iii-1-23 failing -15.4.4.22-8-b-iii-1-25 failing -15.4.4.22-8-b-iii-1-26 failing -15.4.4.22-8-b-iii-1-27 failing -15.4.4.22-8-b-iii-1-28 failing -15.4.4.22-8-b-iii-1-29 failing -15.4.4.22-8-b-iii-1-3 failing -15.4.4.22-8-b-iii-1-31 failing -15.4.4.22-8-b-iii-1-32 failing -15.4.4.22-8-b-iii-1-5 failing -15.4.4.22-8-b-iii-1-7 failing -15.4.4.22-8-b-iii-1-9 failing -15.4.4.22-8-c-1 failing -15.4.4.22-8-c-2 failing -15.4.4.22-8-c-3 failing -15.4.4.22-8-c-5 failing -15.4.4.22-8-c-6 failing -15.4.4.22-9-8 failing -15.4.4.22-9-b-10 failing -15.4.4.22-9-b-12 failing -15.4.4.22-9-b-17 failing -15.4.4.22-9-b-18 failing -15.4.4.22-9-b-19 failing -15.4.4.22-9-b-2 failing -15.4.4.22-9-b-20 failing -15.4.4.22-9-b-21 failing -15.4.4.22-9-b-23 failing -15.4.4.22-9-b-25 failing -15.4.4.22-9-b-26 failing -15.4.4.22-9-b-28 failing -15.4.4.22-9-b-29 failing -15.4.4.22-9-b-3 failing -15.4.4.22-9-b-4 failing -15.4.4.22-9-b-6 failing -15.4.4.22-9-b-8 failing -15.4.4.22-9-c-1 failing -15.4.4.22-9-c-i-1 failing -15.4.4.22-9-c-i-10 failing -15.4.4.22-9-c-i-11 failing -15.4.4.22-9-c-i-12 failing -15.4.4.22-9-c-i-13 failing -15.4.4.22-9-c-i-14 failing -15.4.4.22-9-c-i-15 failing -15.4.4.22-9-c-i-16 failing -15.4.4.22-9-c-i-17 failing -15.4.4.22-9-c-i-18 failing -15.4.4.22-9-c-i-19 failing -15.4.4.22-9-c-i-2 failing -15.4.4.22-9-c-i-20 failing -15.4.4.22-9-c-i-21 failing -15.4.4.22-9-c-i-22 failing -15.4.4.22-9-c-i-23 failing -15.4.4.22-9-c-i-25 failing -15.4.4.22-9-c-i-26 failing -15.4.4.22-9-c-i-27 failing -15.4.4.22-9-c-i-28 failing -15.4.4.22-9-c-i-29 failing -15.4.4.22-9-c-i-3 failing -15.4.4.22-9-c-i-30 failing -15.4.4.22-9-c-i-31 failing -15.4.4.22-9-c-i-32 failing -15.4.4.22-9-c-i-4 failing -15.4.4.22-9-c-i-5 failing 15.4.4.22-9-c-i-6 failing -15.4.4.22-9-c-i-7 failing -15.4.4.22-9-c-i-8 failing -15.4.4.22-9-c-i-9 failing -15.4.4.22-9-c-ii-16 failing -15.4.4.22-9-c-ii-20 failing -15.4.4.22-9-c-ii-21 failing -15.4.4.22-9-c-ii-22 failing -15.4.4.22-9-c-ii-23 failing -15.4.4.22-9-c-ii-24 failing -15.4.4.22-9-c-ii-25 failing -15.4.4.22-9-c-ii-26 failing -15.4.4.22-9-c-ii-27 failing -15.4.4.22-9-c-ii-28 failing -15.4.4.22-9-c-ii-29 failing -15.4.4.22-9-c-ii-30 failing -15.4.4.22-9-c-ii-31 failing -15.4.4.22-9-c-ii-32 failing 15.4.4.22-9-c-ii-33 failing -15.4.4.22-9-c-ii-34 failing -15.4.4.22-9-c-ii-35 failing -15.4.4.22-9-c-ii-37 failing -15.4.4.22-9-c-ii-4 failing -15.4.4.22-9-c-ii-5 failing -15.4.4.22-9-c-ii-8 failing S15.4.4.3_A1_T1 failing S15.4.4.3_A3_T1 failing 15.4.4.4-5-b-iii-3-b-1 failing @@ -1712,5 +1335,4 @@ S15.4.4.13_A1_T2 failing 15.4.4.17-7-c-i-6 failing # Regressions introduced with https://codereview.qt-project.org/#change,45044 -15.4.4.21-8-b-iii-1-6 failing -15.4.4.22-9-9 failing \ No newline at end of file +15.4.4.21-8-b-iii-1-6 failing \ No newline at end of file -- cgit v1.2.3 From e7c3516dc4a5b49029b775f8a52a893bda1dbb03 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 17 Jan 2013 23:03:07 +0100 Subject: Implement String.prototype.match The implementation is not very performant, as it simply is a straight implementation of the standard using RegExpObject. In the long term this should probably use a RegExp directly instead. Change-Id: I532909b3fa8555bf1c16378e2650ca4debedd4b2 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++++++--- tests/TestExpectations | 31 ------------------------------ 2 files changed, 49 insertions(+), 34 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 0ef0d2a8de..462678f5ab 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1186,9 +1186,55 @@ Value StringPrototype::method_localeCompare(ExecutionContext *ctx) Value StringPrototype::method_match(ExecutionContext *ctx) { - // requires Regexp - ctx->throwUnimplemented(QStringLiteral("String.prototype.match")); - return Value::undefinedValue(); + if (ctx->thisObject.isUndefined() || ctx->thisObject.isNull()) + __qmljs_throw_type_error(ctx); + + String *s = ctx->thisObject.toString(ctx); + + Value regexp = ctx->argument(0); + RegExpObject *rx = regexp.asRegExpObject(); + if (!rx) + rx = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ®exp, 1).asRegExpObject(); + + if (!rx) + // ### CHECK + __qmljs_throw_type_error(ctx); + + bool global = rx->global; + + FunctionObject *exec = ctx->engine->regExpPrototype->__get__(ctx, ctx->engine->identifier(QStringLiteral("exec")), 0).asFunctionObject(); + + Value arg = Value::fromString(s); + if (!global) + return exec->call(ctx, Value::fromObject(rx), &arg, 1); + + String *lastIndex = ctx->engine->identifier(QStringLiteral("lastIndex")); + rx->__put__(ctx, lastIndex, Value::fromDouble(0.)); + ArrayObject *a = ctx->engine->newArrayObject(ctx); + + double previousLastIndex = 0; + uint n = 0; + while (1) { + Value result = exec->call(ctx, Value::fromObject(rx), &arg, 1); + if (result.isNull()) + break; + assert(result.isObject()); + double thisIndex = rx->__get__(ctx, lastIndex, 0).toInteger(ctx); + if (previousLastIndex == thisIndex) { + previousLastIndex = thisIndex + 1; + rx->__put__(ctx, lastIndex, Value::fromDouble(previousLastIndex)); + } else { + previousLastIndex = thisIndex; + } + Value matchStr = result.objectValue()->__get__(ctx, (uint)0, (bool *)0); + a->array.set(n, matchStr); + ++n; + } + if (!n) + return Value::nullValue(); + + return Value::fromObject(a); + } Value StringPrototype::method_replace(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index 7f29ea9b45..4c1938b82b 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -760,38 +760,7 @@ S15.4.4.6_A4_T2 failing S15.4.4.7_A3 failing S15.4.4.7_A4_T2 failing S15.4.4.7_A4_T3 failing -S15.5.4.10_A1_T1 failing S15.5.4.10_A1_T10 failing -S15.5.4.10_A1_T11 failing -S15.5.4.10_A1_T12 failing -S15.5.4.10_A1_T13 failing -S15.5.4.10_A1_T14 failing -S15.5.4.10_A1_T2 failing -S15.5.4.10_A1_T3 failing -S15.5.4.10_A1_T4 failing -S15.5.4.10_A1_T5 failing -S15.5.4.10_A1_T6 failing -S15.5.4.10_A1_T7 failing -S15.5.4.10_A1_T8 failing -S15.5.4.10_A1_T9 failing -S15.5.4.10_A2_T1 failing -S15.5.4.10_A2_T10 failing -S15.5.4.10_A2_T11 failing -S15.5.4.10_A2_T12 failing -S15.5.4.10_A2_T13 failing -S15.5.4.10_A2_T14 failing -S15.5.4.10_A2_T15 failing -S15.5.4.10_A2_T16 failing -S15.5.4.10_A2_T17 failing -S15.5.4.10_A2_T18 failing -S15.5.4.10_A2_T2 failing -S15.5.4.10_A2_T3 failing -S15.5.4.10_A2_T4 failing -S15.5.4.10_A2_T5 failing -S15.5.4.10_A2_T6 failing -S15.5.4.10_A2_T7 failing -S15.5.4.10_A2_T8 failing -S15.5.4.10_A2_T9 failing 15.5.4.11-1 failing S15.5.4.11_A12 failing S15.5.4.11_A1_T1 failing -- cgit v1.2.3 From 4a52bc9867b92843b2cd839269132aba0e104e7c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 18 Jan 2013 12:47:43 +0100 Subject: Remove the need for virtual __get/set__ methods in ArgumentsObject In addition to not needing virtuals anymore, it also fixes a few corner cases in the test suite. Change-Id: I9f93d820aa7700c038c60a55daa57e1567d42b17 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 20 +++++++++ qmljs_engine.h | 4 ++ qmljs_environment.cpp | 1 + qmljs_objects.cpp | 86 ++------------------------------------- qmljs_objects.h | 28 ++++--------- qmljs_value.cpp | 2 +- qmljs_value.h | 2 +- qv4argumentsobject.cpp | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ qv4argumentsobject.h | 81 ++++++++++++++++++++++++++++++++++++ qv4mm.cpp | 6 +++ qv4propertydescriptor.h | 4 +- tests/TestExpectations | 6 +-- v4.pro | 2 + 13 files changed, 236 insertions(+), 112 deletions(-) create mode 100644 qv4argumentsobject.cpp create mode 100644 qv4argumentsobject.h diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index b58ff12d7a..8bbff13316 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -43,6 +43,7 @@ #include #include #include "qv4mm.h" +#include namespace QQmlJS { namespace VM { @@ -423,5 +424,24 @@ Object *ExecutionEngine::newForEachIteratorObject(ExecutionContext *ctx, Object return new (memoryManager) ForEachIteratorObject(ctx, o); } +void ExecutionEngine::requireArgumentsAccessors(int n) +{ + if (n <= argumentsAccessors.size()) + return; + + uint oldSize = argumentsAccessors.size(); + argumentsAccessors.resize(n); + for (int i = oldSize; i < n; ++i) { + FunctionObject *get = new (memoryManager) ArgumentsGetterFunction(rootContext, i); + get->prototype = functionPrototype; + FunctionObject *set = new (memoryManager) ArgumentsSetterFunction(rootContext, i); + set->prototype = functionPrototype; + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(get, set); + pd.configurable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + argumentsAccessors[i] = pd; + } +} + } // namespace VM } // namespace QQmlJS diff --git a/qmljs_engine.h b/qmljs_engine.h index 83e8e62bb8..72722e0fd0 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -137,6 +137,8 @@ struct ExecutionEngine TypeErrorPrototype *typeErrorPrototype; URIErrorPrototype *uRIErrorPrototype; + QVector argumentsAccessors; + String *id_length; String *id_prototype; String *id_constructor; @@ -214,6 +216,8 @@ struct ExecutionEngine Object *newActivationObject(); Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o); + + void requireArgumentsAccessors(int n); }; } // namespace VM diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 3ab9ff593a..9dc176187c 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -45,6 +45,7 @@ #include #include #include "qv4mm.h" +#include namespace QQmlJS { namespace VM { diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 5e3e0efc0a..d6a0640d48 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -486,7 +486,7 @@ bool Object::__delete__(ExecutionContext *ctx, uint index) } // Section 8.12.9 -bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc) +bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc) { uint idx = name->asArrayIndex(); if (idx != String::InvalidArrayIndex) @@ -538,7 +538,7 @@ reject: return false; } -bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, PropertyDescriptor *desc) +bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc) { PropertyDescriptor *current; @@ -566,7 +566,7 @@ reject: return false; } -bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, PropertyDescriptor *desc) +bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc) { // clause 5 if (desc->isEmpty()) @@ -631,7 +631,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *cu } -bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, PropertyDescriptor *desc) +bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc) { return __defineOwnProperty__(ctx, ctx->engine->identifier(name), desc); } @@ -1173,84 +1173,6 @@ SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *m } -ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount) - : context(context) - , currentIndex(-1) -{ - defineDefaultProperty(context->engine->id_length, Value::fromInt32(actualParameterCount)); - if (context->strictMode) { - for (uint i = 0; i < context->argumentCount; ++i) - Object::__put__(context, QString::number(i), context->arguments[i]); - FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, __qmljs_throw_type_error); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); - pd.configurable = PropertyDescriptor::Disabled; - pd.enumberable = PropertyDescriptor::Disabled; - __defineOwnProperty__(context, QStringLiteral("callee"), &pd); - __defineOwnProperty__(context, QStringLiteral("caller"), &pd); - } else { - FunctionObject *get = context->engine->newBuiltinFunction(context, 0, method_getArg); - FunctionObject *set = context->engine->newBuiltinFunction(context, 0, method_setArg); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(get, set); - pd.configurable = PropertyDescriptor::Enabled; - pd.enumberable = PropertyDescriptor::Enabled; - uint enumerableParams = qMin(formalParameterCount, actualParameterCount); - for (uint i = 0; i < (uint)enumerableParams; ++i) - __defineOwnProperty__(context, i, &pd); - pd.type = PropertyDescriptor::Data; - pd.writable = PropertyDescriptor::Enabled; - for (uint i = enumerableParams; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) { - pd.value = context->argument(i); - __defineOwnProperty__(context, i, &pd); - } - defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); - } -} - -Value ArgumentsObject::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) -{ - if (!ctx->strictMode) - currentIndex = index; - Value result = Object::__get__(ctx, index, hasProperty); - currentIndex = -1; - return result; -} - -void ArgumentsObject::__put__(ExecutionContext *ctx, uint index, Value value) -{ - if (!ctx->strictMode) - currentIndex = index; - Object::__put__(ctx, index, value); - currentIndex = -1; -} - -Value ArgumentsObject::method_getArg(ExecutionContext *ctx) -{ - Object *that = ctx->thisObject.asObject(); - if (!that) - __qmljs_throw_type_error(ctx); - ArgumentsObject *args = that->asArgumentsObject(); - if (!args) - __qmljs_throw_type_error(ctx); - - assert(ctx != args->context); - assert(args->currentIndex >= 0 && args->currentIndex < (int)args->context->argumentCount); - return args->context->argument(args->currentIndex); -} - -Value ArgumentsObject::method_setArg(ExecutionContext *ctx) -{ - Object *that = ctx->thisObject.asObject(); - if (!that) - __qmljs_throw_type_error(ctx); - ArgumentsObject *args = that->asArgumentsObject(); - if (!args) - __qmljs_throw_type_error(ctx); - - assert(ctx != args->context); - assert(args->currentIndex >= 0 && args->currentIndex < (int)args->context->argumentCount); - args->context->arguments[args->currentIndex] = ctx->arguments[0]; - return Value::undefinedValue(); -} BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) : FunctionObject(scope) diff --git a/qmljs_objects.h b/qmljs_objects.h index 01773ee43e..9c40adad83 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -127,19 +127,19 @@ struct Object: Managed { PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, uint index); virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); - virtual Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); + Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); - virtual void __put__(ExecutionContext *ctx, String *name, Value value); - virtual void __put__(ExecutionContext *ctx, uint index, Value value); + void __put__(ExecutionContext *ctx, String *name, Value value); + void __put__(ExecutionContext *ctx, uint index, Value value); virtual bool __hasProperty__(const ExecutionContext *ctx, String *name) const; virtual bool __hasProperty__(const ExecutionContext *ctx, uint index) const; virtual bool __delete__(ExecutionContext *ctx, String *name); virtual bool __delete__(ExecutionContext *ctx, uint index); - bool __defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, PropertyDescriptor *desc); - virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc); - bool __defineOwnProperty__(ExecutionContext *ctx, uint index, PropertyDescriptor *desc); - bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc); + virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc); virtual Value call(ExecutionContext *context, Value, Value *, int); @@ -423,20 +423,6 @@ struct URIErrorObject: ErrorObject { virtual QString className() { return QStringLiteral("URIError"); } }; -struct ArgumentsObject: Object { - ExecutionContext *context; - int currentIndex; - ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount); - virtual QString className() { return QStringLiteral("Arguments"); } - virtual ArgumentsObject *asArgumentsObject() { return this; } - - virtual Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); - virtual void __put__(ExecutionContext *ctx, uint index, Value value); - - static Value method_getArg(ExecutionContext *ctx); - static Value method_setArg(ExecutionContext *ctx); -}; - } // namespace VM } // namespace QQmlJS diff --git a/qmljs_value.cpp b/qmljs_value.cpp index 78c549fd84..9121ea8ec8 100644 --- a/qmljs_value.cpp +++ b/qmljs_value.cpp @@ -79,7 +79,7 @@ Value Value::toObject(ExecutionContext *ctx) const } -bool Value::sameValue(Value other) { +bool Value::sameValue(Value other) const { if (val == other.val) return true; if (isString() && other.isString()) diff --git a/qmljs_value.h b/qmljs_value.h index 1b9461582a..9e19788c41 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -257,7 +257,7 @@ struct Value Value property(ExecutionContext *ctx, String *name) const; // Section 9.12 - bool sameValue(Value other); + bool sameValue(Value other) const; }; inline Value Value::undefinedValue() diff --git a/qv4argumentsobject.cpp b/qv4argumentsobject.cpp new file mode 100644 index 0000000000..7453b7a817 --- /dev/null +++ b/qv4argumentsobject.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include + +namespace QQmlJS { +namespace VM { + + +ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount) + : context(context) +{ + defineDefaultProperty(context->engine->id_length, Value::fromInt32(actualParameterCount)); + if (context->strictMode) { + for (uint i = 0; i < context->argumentCount; ++i) + Object::__put__(context, QString::number(i), context->arguments[i]); + FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, __qmljs_throw_type_error); + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); + pd.configurable = PropertyDescriptor::Disabled; + pd.enumberable = PropertyDescriptor::Disabled; + __defineOwnProperty__(context, QStringLiteral("callee"), &pd); + __defineOwnProperty__(context, QStringLiteral("caller"), &pd); + } else { + uint enumerableParams = qMin(formalParameterCount, actualParameterCount); + context->engine->requireArgumentsAccessors(enumerableParams); + for (uint i = 0; i < (uint)enumerableParams; ++i) + __defineOwnProperty__(context, i, &context->engine->argumentsAccessors.at(i)); + PropertyDescriptor pd; + pd.type = PropertyDescriptor::Data; + pd.writable = PropertyDescriptor::Enabled; + pd.configurable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + for (uint i = enumerableParams; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) { + pd.value = context->argument(i); + __defineOwnProperty__(context, i, &pd); + } + defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); + } +} + +Value ArgumentsGetterFunction::call(ExecutionContext *ctx, Value thisObject, Value *, int) +{ + Object *that = thisObject.asObject(); + if (!that) + __qmljs_throw_type_error(ctx); + ArgumentsObject *o = that->asArgumentsObject(); + if (!o) + __qmljs_throw_type_error(ctx); + + assert(index < o->context->argumentCount); + return o->context->argument(index); +} + +Value ArgumentsSetterFunction::call(ExecutionContext *ctx, Value thisObject, Value *args, int argc) +{ + Object *that = thisObject.asObject(); + if (!that) + __qmljs_throw_type_error(ctx); + ArgumentsObject *o = that->asArgumentsObject(); + if (!o) + __qmljs_throw_type_error(ctx); + + assert(index < o->context->argumentCount); + o->context->arguments[index] = argc ? args[0] : Value::undefinedValue(); + return Value::undefinedValue(); +} + +} +} diff --git a/qv4argumentsobject.h b/qv4argumentsobject.h new file mode 100644 index 0000000000..ce47cfae11 --- /dev/null +++ b/qv4argumentsobject.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ARGUMENTSOBJECTS_H +#define QV4ARGUMENTSOBJECTS_H + +#include + +namespace QQmlJS { +namespace VM { + +struct ArgumentsGetterFunction: FunctionObject +{ + uint index; + + ArgumentsGetterFunction(ExecutionContext *scope, uint index) + : FunctionObject(scope), index(index) {} + + virtual Value call(ExecutionContext *ctx, Value thisObject, Value *, int); +}; + +struct ArgumentsSetterFunction: FunctionObject +{ + uint index; + + ArgumentsSetterFunction(ExecutionContext *scope, uint index) + : FunctionObject(scope), index(index) {} + + virtual Value call(ExecutionContext *ctx, Value thisObject, Value *args, int argc); +}; + + +struct ArgumentsObject: Object { + ExecutionContext *context; + ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount); + virtual QString className() { return QStringLiteral("Arguments"); } + virtual ArgumentsObject *asArgumentsObject() { return this; } +}; + +} +} + +#endif + diff --git a/qv4mm.cpp b/qv4mm.cpp index 0af07d4701..a05e593b5f 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -333,6 +333,12 @@ void MemoryManager::collectRoots(QVector &roots) const add(roots, m_d->engine->globalObject); add(roots, m_d->engine->exception); + for (int i = 0; i < m_d->engine->argumentsAccessors.size(); ++i) { + const PropertyDescriptor &pd = m_d->engine->argumentsAccessors.at(i); + add(roots, Value::fromObject(pd.get)); + add(roots, Value::fromObject(pd.set)); + } + for (ExecutionContext *ctxt = engine()->current; ctxt; ctxt = ctxt->parent) { add(roots, ctxt->thisObject); if (ctxt->function) diff --git a/qv4propertydescriptor.h b/qv4propertydescriptor.h index 0f14a15318..dc9e1c556d 100644 --- a/qv4propertydescriptor.h +++ b/qv4propertydescriptor.h @@ -121,10 +121,10 @@ struct PropertyDescriptor { inline bool isEnumerable() const { return enumberable == Enabled; } inline bool isConfigurable() const { return configurable == Enabled; } - inline bool isEmpty() { + inline bool isEmpty() const { return type == Generic && writable == Undefined && enumberable == Undefined && configurable == Undefined; } - inline bool isSubset(PropertyDescriptor *other) { + inline bool isSubset(PropertyDescriptor *other) const { if (type != Generic && type != other->type) return false; if (enumberable != Undefined && enumberable != other->enumberable) diff --git a/tests/TestExpectations b/tests/TestExpectations index 4c1938b82b..48ae47accf 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -666,14 +666,10 @@ S15.4.4.13_A3_T3 failing S15.4.4.13_A4_T1 failing S15.4.4.13_A4_T2 failing 15.4.4.14-1-13 failing -15.4.4.14-2-17 failing 15.4.4.14-9-a-10 failing 15.4.4.14-9-a-17 failing 15.4.4.14-9-a-7 failing 15.4.4.14-9-a-9 failing -15.4.4.14-9-b-i-25 failing -15.4.4.14-9-b-i-26 failing -15.4.4.14-9-b-i-27 failing 15.4.4.15-1-13 failing 15.4.4.15-3-2 failing 15.4.4.15-3-29 failing @@ -1304,4 +1300,4 @@ S15.4.4.13_A1_T2 failing 15.4.4.17-7-c-i-6 failing # Regressions introduced with https://codereview.qt-project.org/#change,45044 -15.4.4.21-8-b-iii-1-6 failing \ No newline at end of file +15.4.4.21-8-b-iii-1-6 failing diff --git a/v4.pro b/v4.pro index 7213e6c7ad..9d6f5bda7f 100644 --- a/v4.pro +++ b/v4.pro @@ -27,6 +27,7 @@ SOURCES += main.cpp \ qv4mm.cpp \ qv4managed.cpp \ qv4array.cpp \ + qv4argumentsobject.cpp \ qv4string.cpp \ qv4objectiterator.cpp \ qv4regexp.cpp @@ -49,6 +50,7 @@ HEADERS += \ qv4mm.h \ qv4managed.h \ qv4array.h \ + qv4argumentsobject.h \ qv4string.h \ qv4propertydescriptor.h \ qv4propertytable.h \ -- cgit v1.2.3 From 8ac781f35e7eb7c7d36ab40c86b604c3a8c52717 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 18 Jan 2013 13:28:00 +0100 Subject: Get rid of __get__ implementation in ErrorObject As a step towards making __get__ non-virtual we can implement the message property instead using run-time initialization. The Error.prototype object must also provide a message property with the empty string as initial value (15.11.4.3). Change-Id: If8a7fd828e6c6fa6e20607505b392d0bda71f90a Reviewed-by: Lars Knoll --- main.cpp | 2 +- qmljs_engine.cpp | 4 ++-- qmljs_objects.cpp | 38 ++++++++++++-------------------------- qmljs_objects.h | 5 +---- qv4ecmaobjects.cpp | 1 + qv4ecmaobjects_p.h | 2 +- tests/TestExpectations | 2 -- 7 files changed, 18 insertions(+), 36 deletions(-) diff --git a/main.cpp b/main.cpp index a9485442c3..eb3eb0f94f 100644 --- a/main.cpp +++ b/main.cpp @@ -146,7 +146,7 @@ static void showException(QQmlJS::VM::ExecutionContext *ctx) std::cerr << qPrintable(msg->buildFullMessage(ctx)->toQString()) << std::endl; } } else { - std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl; + std::cerr << "Uncaught exception: " << qPrintable(e->__get__(ctx, ctx->engine->identifier(QStringLiteral("message")), 0).toString(ctx)->toQString()) << std::endl; } } diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 8bbff13316..8f0fa63071 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -106,7 +106,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) datePrototype = new (memoryManager) DatePrototype(); functionPrototype = new (memoryManager) FunctionPrototype(rootContext); regExpPrototype = new (memoryManager) RegExpPrototype(this); - errorPrototype = new (memoryManager) ErrorPrototype(); + errorPrototype = new (memoryManager) ErrorPrototype(this); evalErrorPrototype = new (memoryManager) EvalErrorPrototype(rootContext); rangeErrorPrototype = new (memoryManager) RangeErrorPrototype(rootContext); referenceErrorPrototype = new (memoryManager) ReferenceErrorPrototype(rootContext); @@ -382,7 +382,7 @@ FunctionObject *ExecutionEngine::newRegExpCtor(ExecutionContext *ctx) Object *ExecutionEngine::newErrorObject(const Value &value) { - ErrorObject *object = new (memoryManager) ErrorObject(value); + ErrorObject *object = new (memoryManager) ErrorObject(this, value); object->prototype = errorPrototype; return object; } diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index d6a0640d48..5351c3f6d2 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -1139,15 +1139,10 @@ Value RegExpObject::__get__(ExecutionContext *ctx, String *name, bool *hasProper return Object::__get__(ctx, name, hasProperty); } -Value ErrorObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) +ErrorObject::ErrorObject(ExecutionEngine* engine, const Value &message) { - QString n = name->toQString(); - if (n == QLatin1String("message")) { - if (hasProperty) - *hasProperty = true; - return value; - } - return Object::__get__(ctx, name, hasProperty); + if (message.type() != Value::Undefined_Type) + defineDefaultProperty(engine->identifier(QStringLiteral("message")), message); } void ErrorObject::setNameProperty(ExecutionContext *ctx) @@ -1155,20 +1150,11 @@ void ErrorObject::setNameProperty(ExecutionContext *ctx) defineDefaultProperty(ctx, QLatin1String("name"), Value::fromString(ctx, className())); } -void ErrorObject::getCollectables(QVector &objects) -{ - Object::getCollectables(objects); - if (Object *o = value.asObject()) - objects.append(o); -} - SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) - : ErrorObject(ctx->argument(0)) + : ErrorObject(ctx->engine, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0)) , msg(message) { prototype = ctx->engine->syntaxErrorPrototype; - if (message) - value = Value::fromString(message->buildFullMessage(ctx)); setNameProperty(ctx); } @@ -1280,56 +1266,56 @@ PropertyDescriptor *StringObject::getIndex(ExecutionContext *ctx, uint index) } EvalErrorObject::EvalErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->argument(0)) + : ErrorObject(ctx->engine, ctx->argument(0)) { setNameProperty(ctx); prototype = ctx->engine->evalErrorPrototype; } RangeErrorObject::RangeErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->argument(0)) + : ErrorObject(ctx->engine, ctx->argument(0)) { setNameProperty(ctx); prototype = ctx->engine->rangeErrorPrototype; } RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(Value::fromString(ctx,msg)) + : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) { setNameProperty(ctx); prototype = ctx->engine->rangeErrorPrototype; } ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->argument(0)) + : ErrorObject(ctx->engine, ctx->argument(0)) { setNameProperty(ctx); prototype = ctx->engine->referenceErrorPrototype; } ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(Value::fromString(ctx,msg)) + : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) { setNameProperty(ctx); prototype = ctx->engine->referenceErrorPrototype; } TypeErrorObject::TypeErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->argument(0)) + : ErrorObject(ctx->engine, ctx->argument(0)) { setNameProperty(ctx); prototype = ctx->engine->typeErrorPrototype; } TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(Value::fromString(ctx,msg)) + : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) { setNameProperty(ctx); prototype = ctx->engine->typeErrorPrototype; } URIErrorObject::URIErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->argument(0)) + : ErrorObject(ctx->engine, ctx->argument(0)) { setNameProperty(ctx); prototype = ctx->engine->uRIErrorPrototype; diff --git a/qmljs_objects.h b/qmljs_objects.h index 9c40adad83..54b826ca68 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -370,17 +370,14 @@ struct RegExpObject: Object { }; struct ErrorObject: Object { - Value value; - ErrorObject(const Value &message): value(message) {} + ErrorObject(ExecutionEngine* engine, const Value &message); virtual QString className() { return QStringLiteral("Error"); } virtual ErrorObject *asErrorObject() { return this; } - virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); virtual struct SyntaxErrorObject *asSyntaxError() { return 0; } protected: void setNameProperty(ExecutionContext *ctx); - virtual void getCollectables(QVector &objects); }; struct EvalErrorObject: ErrorObject { diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 462678f5ab..c3bd81cbd2 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -3198,6 +3198,7 @@ void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); obj->defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); obj->defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + obj->defineDefaultProperty(ctx, QStringLiteral("message"), Value::fromString(ctx, QString())); } Value ErrorPrototype::method_toString(ExecutionContext *ctx) diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 55890efe53..ffaf803977 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -357,7 +357,7 @@ struct URIErrorCtor: ErrorCtor struct ErrorPrototype: ErrorObject { // ### shouldn't be undefined - ErrorPrototype(): ErrorObject(Value::undefinedValue()) {} + ErrorPrototype(ExecutionEngine* engine): ErrorObject(engine, Value::undefinedValue()) {} void init(ExecutionContext *ctx, const Value &ctor) { init(ctx, ctor, this); } static void init(ExecutionContext *ctx, const Value &ctor, Object *obj); diff --git a/tests/TestExpectations b/tests/TestExpectations index 48ae47accf..1b6d87dfd3 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -331,8 +331,6 @@ S15.10.7.4_A8 failing S15.10.7.4_A9 failing S15.11.4.2_A1 failing S15.11.4.2_A2 failing -S15.11.4.3_A1 failing -S15.11.4.3_A2 failing 15.12-0-1 failing 15.12-0-2 failing 15.12-0-3 failing -- cgit v1.2.3 From 0c08ca8cf5fadef2bf182aa7d8837ee826dc68a8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 18 Jan 2013 14:38:29 +0100 Subject: Get rid of virtual __get__ in RegExpObject Instead implement the properties as run-time initialized properties. Fixes also a bunch of tests that verifies that they are own properties. Change-Id: I3c4ba52edcec2b59020a2966436b33b2ca5d7f46 Reviewed-by: Lars Knoll --- qmljs_objects.cpp | 28 +++++++--------------------- qmljs_objects.h | 3 +-- qv4regexp.h | 6 +++--- tests/TestExpectations | 6 +----- 4 files changed, 12 insertions(+), 31 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 5351c3f6d2..0236d3ca70 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -1111,32 +1111,18 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bo { if (!members) members.reset(new PropertyTable()); - lastIndexProperty = members->insert(engine->identifier("lastIndex")); + lastIndexProperty = members->insert(engine->identifier(QStringLiteral("lastIndex"))); lastIndexProperty->type = PropertyDescriptor::Data; lastIndexProperty->writable = PropertyDescriptor::Enabled; lastIndexProperty->enumberable = PropertyDescriptor::Disabled; lastIndexProperty->configurable = PropertyDescriptor::Disabled; lastIndexProperty->value = Value::fromInt32(0); -} - -Value RegExpObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) -{ - QString n = name->toQString(); - Value v = Value::undefinedValue(); - if (n == QLatin1String("source")) - v = Value::fromString(ctx, value->pattern()); - else if (n == QLatin1String("global")) - v = Value::fromBoolean(global); - else if (n == QLatin1String("ignoreCase")) - v = Value::fromBoolean(value->ignoreCase()); - else if (n == QLatin1String("multiline")) - v = Value::fromBoolean(value->multiLine()); - if (v.type() != Value::Undefined_Type) { - if (hasProperty) - *hasProperty = true; - return v; - } - return Object::__get__(ctx, name, hasProperty); + if (!this->value.get()) + return; + defineDefaultProperty(engine->identifier(QStringLiteral("source")), Value::fromString(engine->newString(this->value->pattern()))); + defineDefaultProperty(engine->identifier(QStringLiteral("global")), Value::fromBoolean(global)); + defineDefaultProperty(engine->identifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase())); + defineDefaultProperty(engine->identifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); } ErrorObject::ErrorObject(ExecutionEngine* engine, const Value &message) diff --git a/qmljs_objects.h b/qmljs_objects.h index 54b826ca68..f1cbf4b19d 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -362,11 +362,10 @@ struct IsFiniteFunction: FunctionObject struct RegExpObject: Object { RefPtr value; PropertyDescriptor *lastIndexProperty; - bool global; + const bool global; RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global); virtual QString className() { return QStringLiteral("RegExp"); } virtual RegExpObject *asRegExpObject() { return this; } - virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); }; struct ErrorObject: Object { diff --git a/qv4regexp.h b/qv4regexp.h index 3b7a9804be..7fd8225afb 100644 --- a/qv4regexp.h +++ b/qv4regexp.h @@ -79,11 +79,11 @@ private: Q_DISABLE_COPY(RegExp); RegExp(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline); - QString m_pattern; + const QString m_pattern; OwnPtr m_byteCode; int m_subPatternCount; - bool m_ignoreCase; - bool m_multiLine; + const bool m_ignoreCase; + const bool m_multiLine; }; } // end of namespace VM diff --git a/tests/TestExpectations b/tests/TestExpectations index 1b6d87dfd3..90a288361b 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -315,19 +315,15 @@ S15.10.6.2_A1_T2 failing S15.10.7_A2_T1 failing 15.10.7.1-2 failing S15.10.7.1_A10 failing -S15.10.7.1_A8 failing S15.10.7.1_A9 failing 15.10.7.2-2 failing S15.10.7.2_A10 failing -S15.10.7.2_A8 failing S15.10.7.2_A9 failing 15.10.7.3-2 failing S15.10.7.3_A10 failing -S15.10.7.3_A8 failing S15.10.7.3_A9 failing 15.10.7.4-2 failing S15.10.7.4_A10 failing -S15.10.7.4_A8 failing S15.10.7.4_A9 failing S15.11.4.2_A1 failing S15.11.4.2_A2 failing @@ -1298,4 +1294,4 @@ S15.4.4.13_A1_T2 failing 15.4.4.17-7-c-i-6 failing # Regressions introduced with https://codereview.qt-project.org/#change,45044 -15.4.4.21-8-b-iii-1-6 failing +15.4.4.21-8-b-iii-1-6 failing \ No newline at end of file -- cgit v1.2.3 From 5ed9321942dc8c55fffdb57ce46b392dd10f4b52 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 18 Jan 2013 16:41:42 +0100 Subject: Fix wrong index access in one variant of Array::at() Change-Id: I94dc9b08a446ec8d6e26b672a3272365ba9ed320 Reviewed-by: Simon Hausmann --- qv4array.h | 4 ++-- tests/TestExpectations | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/qv4array.h b/qv4array.h index 58529ef4f8..b33163cea6 100644 --- a/qv4array.h +++ b/qv4array.h @@ -421,7 +421,7 @@ class Array assert(!sparse && !offset); offset = qMax(values.size() >> 2, 16); QVector newValues(values.size() + offset); - memcpy(newValues.data() + offset, values.data(), values.size()*sizeof(PropertyDescriptor)); + memcpy(newValues.data() + offset, values.constData(), values.size()*sizeof(PropertyDescriptor)); values = newValues; } @@ -503,7 +503,7 @@ public: if (!sparse) { if (index >= values.size() - offset) return 0; - return values.data() + index - offset; + return values.data() + index + offset; } else { SparseArrayNode *n = sparse->findNode(index); if (!n) diff --git a/tests/TestExpectations b/tests/TestExpectations index 90a288361b..e775a85ace 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -1281,8 +1281,6 @@ S15.4.4.4_A1_T2 failing 15.12.3-11-24 failing 15.12.3-11-25 failing 15.2.3.4-4-1 failing -S15.4.4.13_A1_T1 failing -S15.4.4.13_A1_T2 failing 15.4.4.18-7-c-i-6 failing 15.4.4.19-8-c-i-6 failing 15.4.4.20-9-c-i-6 failing -- cgit v1.2.3 From 403ddb70fb07e7cd60aa18531253ae76d228873b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 18 Jan 2013 13:48:03 +0100 Subject: Optimise array access Optimise accessing indexed properties in Objects and Arrays Change-Id: I3330a4151a13e8f34fee1c4641e64d00a52625e2 Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 3 ++- qmljs_runtime.cpp | 30 +++++++++++++++++++++--------- qv4array.h | 9 +++++++++ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 0236d3ca70..c57355da05 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -280,7 +280,8 @@ Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) { - if (const PropertyDescriptor *p = __getPropertyDescriptor__(ctx, index)) { + const PropertyDescriptor *p = __getPropertyDescriptor__(ctx, index); + if (p && p->type != PropertyDescriptor::Generic) { if (hasProperty) *hasProperty = true; return getValue(ctx, p); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index d7829e308c..58e6cdb552 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -575,23 +575,30 @@ void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Val Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) { + uint type = object.type(); uint idx = index.asArrayIndex(); - if (object.isString() && idx < UINT_MAX) { - String *str = object.stringValue(); - if (idx >= (uint)str->toQString().length()) - return Value::undefinedValue(); - const QString s = str->toQString().mid(idx, 1); - return Value::fromString(ctx, s); - } + if (type != Value::Object_Type) { + if (type == Value::String_Type) { + String *str = object.stringValue(); + if (idx >= (uint)str->toQString().length()) + return Value::undefinedValue(); + const QString s = str->toQString().mid(idx, 1); + return Value::fromString(ctx, s); + } - if (!object.isObject()) object = __qmljs_to_object(object, ctx); + } Object *o = object.objectValue(); - if (idx < UINT_MAX) + if (idx < UINT_MAX) { + const PropertyDescriptor *p = o->array.nonSparseAt(idx); + if (p && p->type == PropertyDescriptor::Data) + return p->value; + return o->__get__(ctx, idx); + } String *name = index.toString(ctx); return o->__get__(ctx, name); @@ -606,6 +613,11 @@ void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value uint idx = index.asArrayIndex(); if (idx < UINT_MAX) { + PropertyDescriptor *p = o->array.nonSparseAtRef(idx); + if (p && p->type == PropertyDescriptor::Data && p->isWritable()) { + p->value = value; + return; + } o->__put__(ctx, idx, value); return; } diff --git a/qv4array.h b/qv4array.h index b33163cea6..d055923d90 100644 --- a/qv4array.h +++ b/qv4array.h @@ -521,6 +521,15 @@ public: return values.constData() + index; } + PropertyDescriptor *nonSparseAtRef(uint index) { + if (sparse) + return 0; + index += offset; + if (index >= (uint)values.size()) + return 0; + return values.data() + index; + } + const PropertyDescriptor *at(uint index) const { if (!sparse) { if (index >= values.size() - offset) -- cgit v1.2.3 From 6e0d162bdfcf85d825444672ee00e40ba423ba3a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 18 Jan 2013 20:22:07 +0100 Subject: Fix valgrind warnings about uninitialised variable Apparently gcc was miscompiling the code with boolean bitfields. Changing them to uint's makes all errors go away. Change-Id: I95e856e4b5ebca0d10fa250a8a4640697998ff38 Reviewed-by: Simon Hausmann --- qv4ir_p.h | 8 +++++--- qv4isel_p.cpp | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/qv4ir_p.h b/qv4ir_p.h index 962aa452dc..03ac2ad29a 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -612,9 +612,10 @@ struct Function { int insideWith; - bool hasDirectEval: 1; - bool usesArgumentsObject : 1; - bool isStrict: 1; + uint hasDirectEval: 1; + uint usesArgumentsObject : 1; + uint isStrict: 1; + uint unused : 29; template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } @@ -627,6 +628,7 @@ struct Function { , hasDirectEval(false) , usesArgumentsObject(false) , isStrict(false) + , unused(0) { this->name = newString(name); } ~Function(); diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index da9b6cd51f..7c1c6cc0a4 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -40,6 +40,7 @@ VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngin vmFunction->hasDirectEval = irFunction->hasDirectEval; vmFunction->usesArgumentsObject = irFunction->usesArgumentsObject; + vmFunction->hasNestedFunctions = !irFunction->nestedFunctions.isEmpty(); vmFunction->isStrict = irFunction->isStrict; foreach (const QString *formal, irFunction->formals) @@ -52,7 +53,6 @@ VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngin foreach (IR::Function *function, irFunction->nestedFunctions) createFunctionMapping(engine, function); - vmFunction->hasNestedFunctions = !irFunction->nestedFunctions.isEmpty(); if (engine->debugger) engine->debugger->mapFunction(vmFunction, irFunction); -- cgit v1.2.3 From 45de7baed5be6d74a524d40ee68d2fbf4e47d2af Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 18 Jan 2013 22:19:33 +0100 Subject: Implement JSON.parse Change-Id: Ibb5f9226aaaea8653842c5a452e006dc05210a2c Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 2 + qv4jsonobject.cpp | 169 +++++++++++++++++++++++++++++++++++++++++++++++++ qv4jsonobject.h | 67 ++++++++++++++++++++ tests/TestExpectations | 108 +------------------------------ v4.pro | 2 + 5 files changed, 243 insertions(+), 105 deletions(-) create mode 100644 qv4jsonobject.cpp create mode 100644 qv4jsonobject.h diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 8f0fa63071..0145238e65 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -44,6 +44,7 @@ #include #include "qv4mm.h" #include +#include namespace QQmlJS { namespace VM { @@ -201,6 +202,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) glo->defineDefaultProperty(rootContext, QStringLiteral("TypeError"), typeErrorCtor); glo->defineDefaultProperty(rootContext, QStringLiteral("URIError"), uRIErrorCtor); glo->defineDefaultProperty(rootContext, QStringLiteral("Math"), Value::fromObject(newMathObject(rootContext))); + glo->defineDefaultProperty(rootContext, QStringLiteral("JSON"), Value::fromObject(new (memoryManager) JsonObject(rootContext))); glo->defineReadonlyProperty(this, QStringLiteral("undefined"), Value::undefinedValue()); glo->defineReadonlyProperty(this, QStringLiteral("NaN"), Value::fromDouble(nan(""))); diff --git a/qv4jsonobject.cpp b/qv4jsonobject.cpp new file mode 100644 index 0000000000..91013e9e24 --- /dev/null +++ b/qv4jsonobject.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include +#include +#include + +namespace QQmlJS { +namespace VM { + +JsonObject::JsonObject(ExecutionContext *context) + : Object() +{ + prototype = context->engine->objectPrototype; + + defineDefaultProperty(context, QStringLiteral("parse"), method_parse, 2); + defineDefaultProperty(context, QStringLiteral("stringify"), method_stringify); +} + + +Value JsonObject::method_parse(ExecutionContext *ctx) +{ + QString jtext = ctx->argument(0).toString(ctx)->toQString(); + + const QChar *ch = jtext.constData(); + const QChar *end = ch + jtext.size(); + + bool simple = false; + while (ch < end) { + if (*ch == ' ' || *ch == '\t' || *ch == '\n' || *ch == '\r') { + ++ch; + } else if (*ch == '[' || *ch == '{') { + break; + } else { + // simple type + jtext.prepend('['); + jtext.append(']'); + simple = true; + break; + } + } + + QJsonParseError e; + QJsonDocument doc = QJsonDocument::fromJson(jtext.toUtf8(), &e); + if (e.error != QJsonParseError::NoError) + ctx->throwSyntaxError(0); + + // iterate over the doc and convert to V4 types + Value result; + if (doc.isArray()) + result = convertArray(ctx, doc.array()); + else if (doc.isObject()) + result = convertObject(ctx, doc.object()); + else + result = Value::undefinedValue(); + + if (simple) { + result = result.objectValue()->__get__(ctx, (uint)0); + } + + return result; +} + +Value JsonObject::method_stringify(ExecutionContext *ctx) +{ + assert(!"Not implemented"); +} + +static void checkString(ExecutionContext *context, const QString &s) +{ + const QChar *ch = s.constData(); + const QChar *end = ch + s.length(); + while (ch < end) { + if (ch->unicode() <= 0x1f) + context->throwSyntaxError(0); + ++ch; + } +} + +Value JsonObject::convertValue(ExecutionContext *context, const QJsonValue &value) +{ + switch (value.type()) { + case QJsonValue::Null: + return Value::nullValue(); + case QJsonValue::Bool: + return Value::fromBoolean(value.toBool()); + case QJsonValue::Double: + return Value::fromDouble(value.toDouble()); + case QJsonValue::String: { + Value v = Value::fromString(context, value.toString()); + checkString(context, v.stringValue()->toQString()); + return v; + } + case QJsonValue::Array: + return convertArray(context, value.toArray()); + case QJsonValue::Object: + return convertObject(context, value.toObject()); + default: + assert(!"internal error in JSON conversion"); + return Value::undefinedValue(); + } +} + +Value JsonObject::convertArray(ExecutionContext *context, const QJsonArray &array) +{ + ArrayObject *o = context->engine->newArrayObject(context); + for (int i = 0; i < array.size(); ++i) { + QJsonValue v = array.at(i); + o->array.set(i, convertValue(context, v)); + } + o->array.setLengthUnchecked(array.size()); + return Value::fromObject(o); +} + +Value JsonObject::convertObject(ExecutionContext *context, const QJsonObject &object) +{ + Object *o = context->engine->newObject(); + for (QJsonObject::const_iterator it = object.constBegin(); it != object.constEnd(); ++it) { + QString key = it.key(); + checkString(context, key); + QJsonValue v = it.value(); + o->__put__(context, key, convertValue(context, v)); + } + return Value::fromObject(o); +} + + +} +} diff --git a/qv4jsonobject.h b/qv4jsonobject.h new file mode 100644 index 0000000000..1a6f24a0b2 --- /dev/null +++ b/qv4jsonobject.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4JSONOBJECTS_H +#define QV4SJONOBJECTS_H + +#include + +namespace QQmlJS { +namespace VM { + +struct JsonObject : Object { + JsonObject(ExecutionContext *context); + virtual QString className() { return QStringLiteral("JSON"); } + + static Value method_parse(ExecutionContext *ctx); + static Value method_stringify(ExecutionContext *ctx); + +private: + static Value convertArray(ExecutionContext *context, const QJsonArray &array); + static Value convertObject(ExecutionContext *context, const QJsonObject &object); + static Value convertValue(ExecutionContext *context, const QJsonValue &value); + +}; + +} +} + +#endif + diff --git a/tests/TestExpectations b/tests/TestExpectations index e775a85ace..a125833a59 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -115,10 +115,8 @@ S11.3.2_A4_T4 failing 11.4.1-2-4 failing 11.4.1-2-5 failing 11.4.1-2-6 failing -11.4.1-4.a-10 failing 11.4.1-4.a-5 failing 11.4.1-4.a-7 failing -11.4.1-4.a-8 failing 11.4.1-5-2 failing S11.4.1_A1 failing S11.4.1_A2.1 failing @@ -327,36 +325,13 @@ S15.10.7.4_A10 failing S15.10.7.4_A9 failing S15.11.4.2_A1 failing S15.11.4.2_A2 failing -15.12-0-1 failing -15.12-0-2 failing -15.12-0-3 failing -15.12-0-4 failing -15.12.1.1-0-1 failing -15.12.1.1-0-9 failing -15.12.1.1-g1-1 failing -15.12.1.1-g1-2 failing -15.12.1.1-g1-3 failing -15.12.1.1-g1-4 failing -15.12.1.1-g2-1 failing -15.12.1.1-g2-5 failing -15.12.1.1-g4-4 failing -15.12.1.1-g5-1 failing -15.12.1.1-g5-2 failing -15.12.1.1-g5-3 failing -15.12.1.1-g6-1 failing -15.12.1.1-g6-2 failing 15.12.1.1-g6-3 failing 15.12.1.1-g6-4 failing 15.12.1.1-g6-5 failing 15.12.1.1-g6-6 failing 15.12.1.1-g6-7 failing -15.12.2-0-1 failing -15.12.2-0-2 failing -15.12.2-0-3 failing S15.12.2_A1 failing -15.12.3-0-1 failing 15.12.3-0-2 failing -15.12.3-0-3 failing 15.12.3-11-1 failing 15.12.3-11-10 failing 15.12.3-11-11 failing @@ -412,7 +387,6 @@ S15.1.3.1_A1.9_T1 failing S15.1.3.1_A1.9_T2 failing S15.1.3.1_A1.9_T3 failing S15.1.3.1_A2.1_T1 failing -15.2.3.11-4-27 failing 15.12.3-11-2 failing 15.12.3-11-26 failing 15.12.3-11-3 failing @@ -502,9 +476,6 @@ S15.1.3.4_A5.4 failing S15.1.3.4_A5.6 failing S15.1.3.4_A5.7 failing S15.1.3.4_A6_T1 failing -15.2.3.12-3-27 failing -15.2.3.13-2-12 failing -15.2.3.2-2-18 failing S15.1.3.1_A2.2_T1 failing S15.1.3.1_A2.3_T1 failing S15.1.3.1_A2.4_T1 failing @@ -575,8 +546,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-161 failing 15.2.3.3-4-162 failing 15.2.3.3-4-164 failing -15.2.3.3-4-176 failing -15.2.3.3-4-177 failing 15.2.3.3-4-212 failing 15.2.3.3-4-213 failing 15.2.3.3-4-214 failing @@ -585,34 +554,8 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-82 failing 15.2.3.3-4-9 failing 15.2.3.3-4-90 failing -15.2.3.5-4-120 failing -15.2.3.5-4-13 failing -15.2.3.5-4-145 failing -15.2.3.5-4-173 failing -15.2.3.5-4-199 failing -15.2.3.5-4-224 failing -15.2.3.5-4-252 failing -15.2.3.5-4-67 failing -15.2.3.5-4-92 failing 15.2.3.6-2-17-1 failing -15.2.3.5-4-287 failing 15.2.3.5-4-315 failing -15.2.3.5-4-36 failing -15.2.3.6-3-119 failing -15.2.3.6-3-147-1 failing -15.2.3.6-3-147 failing -15.2.3.6-3-173-1 failing -15.2.3.6-3-173 failing -15.2.3.6-3-198 failing -15.2.3.6-3-226-1 failing -15.2.3.6-3-226 failing -15.2.3.6-3-256-1 failing -15.2.3.6-3-256 failing -15.2.3.6-3-41-1 failing -15.2.3.6-3-41 failing -15.2.3.6-3-66 failing -15.2.3.6-3-94-1 failing -15.2.3.6-3-94 failing 15.2.3.6-4-291-1 failing 15.2.3.6-4-292-1 failing 15.2.3.6-4-293-2 failing @@ -625,32 +568,15 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-300-1 failing 15.2.3.6-4-360-3 failing 15.2.3.6-4-360-7 failing -15.2.3.6-4-41 failing -15.2.3.6-4-410 failing -15.2.3.6-4-586 failing 15.2.3.6-4-621 failing 15.2.3.6-4-622 failing 15.2.3.6-4-623 failing 15.2.3.6-4-624 failing -15.2.3.7-2-14 failing -15.2.3.7-5-a-15 failing -15.2.3.7-5-b-105 failing -15.2.3.7-5-b-133 failing -15.2.3.7-5-b-159 failing -15.2.3.7-5-b-184 failing -15.2.3.7-5-b-212 failing -15.2.3.7-5-b-247 failing -15.2.3.7-5-b-27 failing -15.2.3.7-5-b-52 failing -15.2.3.7-5-b-80 failing 15.2.3.7-6-a-110 failing -15.2.3.7-6-a-20 failing 15.2.3.7-6-a-280 failing 15.2.3.7-6-a-286 failing 15.2.3.7-6-a-288 failing 15.2.3.7-6-a-289 failing -15.3.4.5-2-7 failing -15.4.3.2-1-11 failing S15.4.4.13_A2_T1 failing S15.4.4.13_A2_T2 failing S15.4.4.13_A2_T3 failing @@ -659,12 +585,10 @@ S15.4.4.13_A3_T2 failing S15.4.4.13_A3_T3 failing S15.4.4.13_A4_T1 failing S15.4.4.13_A4_T2 failing -15.4.4.14-1-13 failing 15.4.4.14-9-a-10 failing 15.4.4.14-9-a-17 failing 15.4.4.14-9-a-7 failing 15.4.4.14-9-a-9 failing -15.4.4.15-1-13 failing 15.4.4.15-3-2 failing 15.4.4.15-3-29 failing 15.4.4.15-5-14 failing @@ -709,32 +633,15 @@ S15.4.4.13_A4_T2 failing 15.4.4.15-8-b-ii-8 failing 15.4.4.15-8-b-ii-9 failing 15.4.4.16-1-10 failing -15.4.4.16-1-13 failing -15.4.4.16-5-17 failing -15.4.4.16-7-c-iii-23 failing 15.4.4.17-1-10 failing -15.4.4.17-1-13 failing -15.4.4.17-5-17 failing -15.4.4.17-7-c-iii-23 failing 15.4.4.18-1-10 failing -15.4.4.18-1-13 failing -15.4.4.18-5-17 failing 15.4.4.19-1-10 failing -15.4.4.19-1-13 failing 15.4.4.19-5-1 failing -15.4.4.19-5-17 failing 15.4.4.20-1-10 failing -15.4.4.20-1-13 failing -15.4.4.20-5-17 failing -15.4.4.20-9-c-iii-24 failing 15.4.4.21-1-10 failing -15.4.4.21-1-13 failing 15.4.4.21-9-c-i-6 failing -15.4.4.21-9-c-ii-33 failing 15.4.4.22-1-10 failing -15.4.4.22-1-13 failing 15.4.4.22-9-c-i-6 failing -15.4.4.22-9-c-ii-33 failing S15.4.4.3_A1_T1 failing S15.4.4.3_A3_T1 failing 15.4.4.4-5-b-iii-3-b-1 failing @@ -1260,16 +1167,6 @@ S15.2.4.4_A15 failing S15.4.4.4_A1_T2 failing # Regressions due to Object/property refactoring -15.12.2-2-1 failing -15.12.2-2-10 failing -15.12.2-2-2 failing -15.12.2-2-3 failing -15.12.2-2-4 failing -15.12.2-2-5 failing -15.12.2-2-6 failing -15.12.2-2-7 failing -15.12.2-2-8 failing -15.12.2-2-9 failing 15.12.3-11-16 failing 15.12.3-11-17 failing 15.12.3-11-18 failing @@ -1291,5 +1188,6 @@ S15.4.4.4_A1_T2 failing 15.4.4.16-7-c-i-6 failing 15.4.4.17-7-c-i-6 failing -# Regressions introduced with https://codereview.qt-project.org/#change,45044 -15.4.4.21-8-b-iii-1-6 failing \ No newline at end of file +15.4.4.21-8-b-iii-1-6 failing +15.12.3_4-1-1 +15.12.3_4-1-3 diff --git a/v4.pro b/v4.pro index 9d6f5bda7f..b195d50364 100644 --- a/v4.pro +++ b/v4.pro @@ -28,6 +28,7 @@ SOURCES += main.cpp \ qv4managed.cpp \ qv4array.cpp \ qv4argumentsobject.cpp \ + qv4jsonobject.cpp \ qv4string.cpp \ qv4objectiterator.cpp \ qv4regexp.cpp @@ -51,6 +52,7 @@ HEADERS += \ qv4managed.h \ qv4array.h \ qv4argumentsobject.h \ + qv4jsonobject.h \ qv4string.h \ qv4propertydescriptor.h \ qv4propertytable.h \ -- cgit v1.2.3 From 25563eaab1ed5f9762075c6beba3efefbb847f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Mon, 14 Jan 2013 16:15:01 +0100 Subject: There is no point in checking twice for a space if gc was not run. Change-Id: I60234eaba3db7ee6c52b4487a12e403116dbf254 Reviewed-by: Lars Knoll --- qv4mm.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qv4mm.cpp b/qv4mm.cpp index a05e593b5f..fd7d86a92a 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -123,12 +123,12 @@ Managed *MemoryManager::alloc(std::size_t size) goto found; // try to free up space, otherwise allocate - if (!m_d->aggressiveGC) + if (!m_d->aggressiveGC) { runGC(); - - m = m_d->smallItems[pos]; - if (m) - goto found; + m = m_d->smallItems[pos]; + if (m) + goto found; + } // no free item available, allocate a new chunk { -- cgit v1.2.3 From 4e3433f2b7d98f081ce9a7da3e71943a63e9e3da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Mon, 14 Jan 2013 16:10:56 +0100 Subject: Remove broken result of an automatic merge resolution. Change-Id: Idc86a0cc2175c11e5b40f9c28e294bd0a7d033eb Reviewed-by: Lars Knoll --- qv4mm.h | 1 - 1 file changed, 1 deletion(-) diff --git a/qv4mm.h b/qv4mm.h index 10b64c5a82..33ffc883c0 100644 --- a/qv4mm.h +++ b/qv4mm.h @@ -91,7 +91,6 @@ public: void setEnableGC(bool enableGC); void setExecutionEngine(ExecutionEngine *engine); - void setStringPool(StringPool *stringPool); void dumpStats() const; -- cgit v1.2.3 From be00d9a5c4968317f07df2bff86c5272c83f10d9 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 17 Jan 2013 22:10:35 +0100 Subject: Fix compiler warning. The usual class v.s. struct forward decls. Only fatal on Windows, not on any other platform. Change-Id: I902dd82b196663fc66e3bd914002a2961668455f Reviewed-by: Lars Knoll --- qv4array.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4array.h b/qv4array.h index d055923d90..ead2f34ce1 100644 --- a/qv4array.h +++ b/qv4array.h @@ -361,7 +361,7 @@ inline SparseArrayNode *SparseArray::upperBound(uint akey) class Array { - friend class ArrayPrototype; + friend struct ArrayPrototype; uint len; PropertyDescriptor *lengthProperty; -- cgit v1.2.3 From 59467a0f29972076712f27d0696cb818ff84f8a7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 Jan 2013 20:38:35 +0100 Subject: Fix exception handling in masm backend The return value of setjmp is an int, which we interpret as a boolean later. We cannot put it directly into a VM::Value, because it isn't one. Instead we have to store it properly with value and tag. This worked by "accident" on x86-64 where the return value is in eax and not clobbered until we do the type test. But on ia32 we do rely on a proper value representation. Change-Id: Id54072c31baf6ad4607626dad939c715d2092d3d Reviewed-by: Lars Knoll --- qv4isel_masm.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 73a8c6278a..29bd3ecb48 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -486,7 +486,11 @@ void InstructionSelection::callBuiltinRethrow() void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) { generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_create_exception_handler, Assembler::ContextRegister); - generateFunctionCall(result, setjmp, Assembler::ReturnValueRegister); + generateFunctionCall(Assembler::ReturnValueRegister, setjmp, Assembler::ReturnValueRegister); + Address addr = _asm->loadTempAddress(Assembler::ScratchRegister, result); + _asm->store32(Assembler::ReturnValueRegister, addr); + addr.offset += 4; + _asm->store32(Assembler::TrustedImm32(Value::Boolean_Type), addr); } void InstructionSelection::callBuiltinDeleteExceptionHandler() -- cgit v1.2.3 From 36efcbe1ecc8ed95912be1d5c6da89b1b3813e94 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 Jan 2013 21:54:29 +0100 Subject: Fix writable attribute of RegExp properties Most of the properties are in fact not writable. Change-Id: I21a8c97fb98ef3c583b284093ec9be52b72b1544 Reviewed-by: Lars Knoll --- qmljs_objects.cpp | 8 ++++---- tests/TestExpectations | 14 +------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index c57355da05..cf24c00b1a 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -1120,10 +1120,10 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bo lastIndexProperty->value = Value::fromInt32(0); if (!this->value.get()) return; - defineDefaultProperty(engine->identifier(QStringLiteral("source")), Value::fromString(engine->newString(this->value->pattern()))); - defineDefaultProperty(engine->identifier(QStringLiteral("global")), Value::fromBoolean(global)); - defineDefaultProperty(engine->identifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase())); - defineDefaultProperty(engine->identifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); + defineReadonlyProperty(engine->identifier(QStringLiteral("source")), Value::fromString(engine->newString(this->value->pattern()))); + defineReadonlyProperty(engine->identifier(QStringLiteral("global")), Value::fromBoolean(global)); + defineReadonlyProperty(engine->identifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase())); + defineReadonlyProperty(engine->identifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); } ErrorObject::ErrorObject(ExecutionEngine* engine, const Value &message) diff --git a/tests/TestExpectations b/tests/TestExpectations index a125833a59..5d7b573f37 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -311,18 +311,6 @@ S15.10.2.12_A5_T1 failing S15.10.2.8_A3_T18 failing S15.10.6.2_A1_T2 failing S15.10.7_A2_T1 failing -15.10.7.1-2 failing -S15.10.7.1_A10 failing -S15.10.7.1_A9 failing -15.10.7.2-2 failing -S15.10.7.2_A10 failing -S15.10.7.2_A9 failing -15.10.7.3-2 failing -S15.10.7.3_A10 failing -S15.10.7.3_A9 failing -15.10.7.4-2 failing -S15.10.7.4_A10 failing -S15.10.7.4_A9 failing S15.11.4.2_A1 failing S15.11.4.2_A2 failing 15.12.1.1-g6-3 failing @@ -1190,4 +1178,4 @@ S15.4.4.4_A1_T2 failing 15.4.4.21-8-b-iii-1-6 failing 15.12.3_4-1-1 -15.12.3_4-1-3 +15.12.3_4-1-3 \ No newline at end of file -- cgit v1.2.3 From 4c72f0f01fb22c6593c63182e9ac8269e818e7f1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 Jan 2013 10:57:09 +0100 Subject: Member expressions on non-objects should throw a type error This is covered by the 11.2.3 tests Change-Id: I74ccda815a0d6c4ba1f7eb25ce327cbb3bb30d48 Reviewed-by: Lars Knoll --- qmljs_runtime.cpp | 2 +- tests/TestExpectations | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 58e6cdb552..40fc075643 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -736,7 +736,7 @@ Value __qmljs_call_activation_property(ExecutionContext *context, String *name, Value func = context->getProperty(name); Object *o = func.asObject(); if (!o) - context->throwReferenceError(Value::fromString(name)); + context->throwTypeError(); return o->call(context, Value::undefinedValue(), args, argc); } diff --git a/tests/TestExpectations b/tests/TestExpectations index 5d7b573f37..6b3a232b3a 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -89,11 +89,6 @@ S11.2.1_A3_T3 failing S11.2.1_A4_T1 failing S11.2.1_A4_T3 failing 11.2.3-3_3 failing -S11.2.3_A3_T1 failing -S11.2.3_A3_T2 failing -S11.2.3_A3_T3 failing -S11.2.3_A3_T4 failing -S11.2.3_A3_T5 failing 11.3.1-2-1-s failing 11.3.1-2-2-s failing S11.3.1_A2.2_T1 failing @@ -254,8 +249,6 @@ S12.6.2_A8 failing 13.0-8-s failing 13.0-9-s failing S13_A15_T4 failing -S13_A17_T1 failing -S13_A17_T2 failing S13_A3_T1 failing S13_A6_T2 failing 13.1-11-s failing @@ -534,10 +527,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-161 failing 15.2.3.3-4-162 failing 15.2.3.3-4-164 failing -15.2.3.3-4-212 failing -15.2.3.3-4-213 failing -15.2.3.3-4-214 failing -15.2.3.3-4-215 failing 15.2.3.3-4-51 failing 15.2.3.3-4-82 failing 15.2.3.3-4-9 failing -- cgit v1.2.3 From 9b5112258b06f87be919f4ac4b7d72fee83cb0d2 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 11:39:08 +0100 Subject: Fix Date constructor and add Date.prototype.toISOString Change-Id: Ib905639ed903cccdeb2649374f744a1fc64f3c1c Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 210 ++++++++++++++++++++++++++++++++++++++++++++----- qv4ecmaobjects_p.h | 5 +- tests/TestExpectations | 15 ---- 3 files changed, 193 insertions(+), 37 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index c3bd81cbd2..df3ff9bea1 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -366,6 +366,144 @@ static inline double FromDateTime(const QDateTime &dt) static inline double ParseString(const QString &s) { + // first try the format defined in 15.9.1.15, only if that fails fall back to + // QDateTime for parsing + + // the define string format is YYYY-MM-DDTHH:mm:ss.sssZ + // It can be date or time only, and the second and later components + // of both fields are optional + // and extended syntax for negative and large positive years exists: +/-YYYYYY + + enum Format { + Year, + Month, + Day, + Hour, + Minute, + Second, + MilliSecond, + TimezoneHour, + TimezoneMinute, + Done + }; + + const QChar *ch = s.constData(); + const QChar *end = ch + s.length(); + + uint format = Year; + int current = 0; + int currentSize = 0; + bool extendedYear = false; + + int yearSign = 1; + int year = 0; + int month = 0; + int day = 1; + int hour = 0; + int minute = 0; + int second = 0; + int msec = 0; + int offsetSign = 1; + int offset = 0; + + bool error = false; + if (*ch == '+' || *ch == '-') { + extendedYear = true; + if (*ch == '-') + yearSign = -1; + ++ch; + } + while (ch <= end) { + if (*ch >= '0' && *ch <= '9') { + current *= 10; + current += ch->unicode() - '0'; + ++currentSize; + } else { // other char, delimits field + switch (format) { + case Year: + year = current; + if (extendedYear) + error = (currentSize != 6); + else + error = (currentSize != 4); + break; + case Month: + month = current - 1; + error = (currentSize != 2) || month > 11; + break; + case Day: + day = current; + error = (currentSize != 2) || day > 31; + break; + case Hour: + hour = current; + error = (currentSize != 2) || hour > 24; + break; + case Minute: + minute = current; + error = (currentSize != 2) || minute > 60; + break; + case Second: + second = current; + error = (currentSize != 2) || second > 60; + break; + case MilliSecond: + msec = current; + error = (currentSize != 3); + break; + case TimezoneHour: + offset = current*60; + error = (currentSize != 2) || offset > 23*60; + break; + case TimezoneMinute: + offset += current; + error = (currentSize != 2) || current >= 60; + break; + } + if (*ch == 'T') { + if (format >= Hour) + error = true; + format = Hour; + } else if (*ch == '-') { + if (format < Day) + ++format; + else if (format < Minute) + error = true; + else if (format >= TimezoneHour) + error = true; + else { + offsetSign = -1; + format = TimezoneHour; + } + } else if (*ch == ':') { + if (format != Hour && format != Minute && format != TimezoneHour) + error = true; + ++format; + } else if (*ch == '.') { + if (format != Second) + error = true; + ++format; + } else if (*ch == '+') { + if (format < Minute || format >= TimezoneHour) + error = true; + format = TimezoneHour; + } else if (*ch == 'Z' || *ch == 0) { + format = Done; + } + current = 0; + currentSize = 0; + } + if (error || format == Done) + break; + ++ch; + } + + if (!error) { + double t = MakeDate(MakeDay(year * yearSign, month, day), MakeTime(hour, minute, second, msec)); + t += offset * offsetSign * 60 * 1000; + return t; + } + QDateTime dt = QDateTime::fromString(s, Qt::TextDate); if (!dt.isValid()) dt = QDateTime::fromString(s, Qt::ISODate); @@ -2433,7 +2571,7 @@ Value DateCtor::construct(ExecutionContext *ctx) arg = __qmljs_to_primitive(arg, ctx, PREFERREDTYPE_HINT); if (arg.isString()) - t = ParseString(arg.toString(ctx)->toQString()); + t = ParseString(arg.stringValue()->toQString()); else t = TimeClip(arg.toNumber(ctx)); } @@ -2516,6 +2654,7 @@ void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) defineDefaultProperty(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); defineDefaultProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); defineDefaultProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); + defineDefaultProperty(ctx, QStringLiteral("toISOString"), method_toISOString, 0); } double DatePrototype::getThisDate(ExecutionContext *ctx) @@ -2528,24 +2667,6 @@ double DatePrototype::getThisDate(ExecutionContext *ctx) } } -Value DatePrototype::method_MakeTime(ExecutionContext *ctx) -{ - ctx->throwUnimplemented(QStringLiteral("Data.MakeTime")); - return Value::undefinedValue(); -} - -Value DatePrototype::method_MakeDate(ExecutionContext *ctx) -{ - ctx->throwUnimplemented(QStringLiteral("Data.MakeDate")); - return Value::undefinedValue(); -} - -Value DatePrototype::method_TimeClip(ExecutionContext *ctx) -{ - ctx->throwUnimplemented(QStringLiteral("Data.TimeClip")); - return Value::undefinedValue(); -} - Value DatePrototype::method_parse(ExecutionContext *ctx) { return Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString())); @@ -3007,6 +3128,57 @@ Value DatePrototype::method_toUTCString(ExecutionContext *ctx) return Value::fromString(ctx, ToUTCString(t)); } +static void addZeroPrefixedInt(QString &str, int num, int nDigits) +{ + str.resize(str.size() + nDigits); + + QChar *c = str.data() + str.size() - 1; + while (nDigits) { + *c = QChar(num % 10 + '0'); + num /= 10; + --c; + --nDigits; + } +} + +Value DatePrototype::method_toISOString(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + if (!std::isfinite(t)) + ctx->throwRangeError(ctx->thisObject); + + QString result; + int year = (int)YearFromTime(t); + if (year < 0 || year > 9999) { + if (qAbs(year) >= 1000000) + return Value::fromString(ctx, QStringLiteral("Invalid Date")); + result += year < 0 ? '-' : '+'; + year = qAbs(year); + addZeroPrefixedInt(result, year, 6); + } else { + addZeroPrefixedInt(result, year, 4); + } + result += '-'; + addZeroPrefixedInt(result, (int)MonthFromTime(t) + 1, 2); + result += '-'; + addZeroPrefixedInt(result, (int)DateFromTime(t), 2); + result += 'T'; + addZeroPrefixedInt(result, HourFromTime(t), 2); + result += ':'; + addZeroPrefixedInt(result, MinFromTime(t), 2); + result += ':'; + addZeroPrefixedInt(result, SecFromTime(t), 2); + result += '.'; + addZeroPrefixedInt(result, msFromTime(t), 3); + result += 'Z'; + + return Value::fromString(ctx, result); +} + // // RegExp object // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index ffaf803977..571dc17b35 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -235,11 +235,9 @@ struct DatePrototype: DateObject static double getThisDate(ExecutionContext *ctx); - static Value method_MakeTime(ExecutionContext *ctx); - static Value method_MakeDate(ExecutionContext *ctx); - static Value method_TimeClip(ExecutionContext *ctx); static Value method_parse(ExecutionContext *ctx); static Value method_UTC(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *ctx); static Value method_toDateString(ExecutionContext *ctx); static Value method_toTimeString(ExecutionContext *ctx); @@ -283,6 +281,7 @@ struct DatePrototype: DateObject static Value method_setFullYear(ExecutionContext *ctx); static Value method_setUTCFullYear(ExecutionContext *ctx); static Value method_toUTCString(ExecutionContext *ctx); + static Value method_toISOString(ExecutionContext *ctx); }; struct RegExpCtor: FunctionObject diff --git a/tests/TestExpectations b/tests/TestExpectations index 6b3a232b3a..39c0a49958 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -524,7 +524,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-11 failing 15.2.3.3-4-12 failing 15.2.3.3-4-13 failing -15.2.3.3-4-161 failing 15.2.3.3-4-162 failing 15.2.3.3-4-164 failing 15.2.3.3-4-51 failing @@ -547,7 +546,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-360-7 failing 15.2.3.6-4-621 failing 15.2.3.6-4-622 failing -15.2.3.6-4-623 failing 15.2.3.6-4-624 failing 15.2.3.7-6-a-110 failing 15.2.3.7-6-a-280 failing @@ -960,7 +958,6 @@ S15.7.4.5_A1.4_T01 failing 15.9.4.4-0-2 failing 15.9.4.4-0-3 failing 15.9.4.4-0-4 failing -15.9.1.15-1 failing S15.9.2.1_A2 failing S15.9.3.1_A5_T1 failing S15.9.3.1_A5_T2 failing @@ -1090,18 +1087,6 @@ S15.9.5.1_A2_T1 failing 13.3.0_2 failing 13.3.0_6 failing 13.3.0_7 failing -15.9.5.43-0-10 failing -15.9.5.43-0-11 failing -15.9.5.43-0-12 failing -15.9.5.43-0-13 failing -15.9.5.43-0-14 failing -15.9.5.43-0-15 failing -15.9.5.43-0-2 failing -15.9.5.43-0-3 failing -15.9.5.43-0-4 failing -15.9.5.43-0-5 failing -15.9.5.43-0-8 failing -15.9.5.43-0-9 failing 15.9.5.44-0-1 failing 15.9.5.44-0-2 failing 6.2.2_a failing -- cgit v1.2.3 From 659fc03c429f57656cecad399e97f115448de04b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 11:57:17 +0100 Subject: Move the Date Object into it's own file Change-Id: I3f94fa1dcfc8e23414ee2b3f96cffffa1f753bc1 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 1 + qmljs_objects.h | 7 - qv4dateobject.cpp | 1285 ++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4dateobject.h | 125 +++++ qv4ecmaobjects.cpp | 1213 ------------------------------------------------- qv4ecmaobjects_p.h | 64 --- v4.pro | 2 + 7 files changed, 1413 insertions(+), 1284 deletions(-) create mode 100644 qv4dateobject.cpp create mode 100644 qv4dateobject.h diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 0145238e65..a6ee9ab6c8 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -44,6 +44,7 @@ #include #include "qv4mm.h" #include +#include #include namespace QQmlJS { diff --git a/qmljs_objects.h b/qmljs_objects.h index f1cbf4b19d..91b1241fb2 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -205,13 +205,6 @@ struct StringObject: Object { PropertyDescriptor *getIndex(ExecutionContext *ctx, uint index); }; -struct DateObject: Object { - Value value; - DateObject(const Value &value): value(value) {} - virtual QString className() { return QStringLiteral("Date"); } - virtual DateObject *asDateObject() { return this; } -}; - struct ArrayObject: Object { ArrayObject(ExecutionContext *ctx) { init(ctx); } ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setLengthUnchecked(array.length()); } diff --git a/qv4dateobject.cpp b/qv4dateobject.cpp new file mode 100644 index 0000000000..d9c6f5ed6b --- /dev/null +++ b/qv4dateobject.cpp @@ -0,0 +1,1285 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4dateobject.h" +#include "qv4mm.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef Q_WS_WIN +# include +# ifndef Q_OS_VXWORKS +# include +# else +# include "qplatformdefs.h" +# endif +#else +# include +#endif + +using namespace QQmlJS::VM; + +static const double HoursPerDay = 24.0; +static const double MinutesPerHour = 60.0; +static const double SecondsPerMinute = 60.0; +static const double msPerSecond = 1000.0; +static const double msPerMinute = 60000.0; +static const double msPerHour = 3600000.0; +static const double msPerDay = 86400000.0; + +static double LocalTZA = 0.0; // initialized at startup + +static inline double TimeWithinDay(double t) +{ + double r = ::fmod(t, msPerDay); + return (r >= 0) ? r : r + msPerDay; +} + +static inline int HourFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerHour), HoursPerDay)); + return (r >= 0) ? r : r + int(HoursPerDay); +} + +static inline int MinFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerMinute), MinutesPerHour)); + return (r >= 0) ? r : r + int(MinutesPerHour); +} + +static inline int SecFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerSecond), SecondsPerMinute)); + return (r >= 0) ? r : r + int(SecondsPerMinute); +} + +static inline int msFromTime(double t) +{ + int r = int(::fmod(t, msPerSecond)); + return (r >= 0) ? r : r + int(msPerSecond); +} + +static inline double Day(double t) +{ + return ::floor(t / msPerDay); +} + +static inline double DaysInYear(double y) +{ + if (::fmod(y, 4)) + return 365; + + else if (::fmod(y, 100)) + return 366; + + else if (::fmod(y, 400)) + return 365; + + return 366; +} + +static inline double DayFromYear(double y) +{ + return 365 * (y - 1970) + + ::floor((y - 1969) / 4) + - ::floor((y - 1901) / 100) + + ::floor((y - 1601) / 400); +} + +static inline double TimeFromYear(double y) +{ + return msPerDay * DayFromYear(y); +} + +static inline double YearFromTime(double t) +{ + int y = 1970; + y += (int) ::floor(t / (msPerDay * 365.2425)); + + double t2 = TimeFromYear(y); + return (t2 > t) ? y - 1 : ((t2 + msPerDay * DaysInYear(y)) <= t) ? y + 1 : y; +} + +static inline bool InLeapYear(double t) +{ + double x = DaysInYear(YearFromTime(t)); + if (x == 365) + return 0; + + assert(x == 366); + return 1; +} + +static inline double DayWithinYear(double t) +{ + return Day(t) - DayFromYear(YearFromTime(t)); +} + +static inline double MonthFromTime(double t) +{ + double d = DayWithinYear(t); + double l = InLeapYear(t); + + if (d < 31.0) + return 0; + + else if (d < 59.0 + l) + return 1; + + else if (d < 90.0 + l) + return 2; + + else if (d < 120.0 + l) + return 3; + + else if (d < 151.0 + l) + return 4; + + else if (d < 181.0 + l) + return 5; + + else if (d < 212.0 + l) + return 6; + + else if (d < 243.0 + l) + return 7; + + else if (d < 273.0 + l) + return 8; + + else if (d < 304.0 + l) + return 9; + + else if (d < 334.0 + l) + return 10; + + else if (d < 365.0 + l) + return 11; + + return qSNaN(); // ### assert? +} + +static inline double DateFromTime(double t) +{ + int m = (int) Value::toInteger(MonthFromTime(t)); + double d = DayWithinYear(t); + double l = InLeapYear(t); + + switch (m) { + case 0: return d + 1.0; + case 1: return d - 30.0; + case 2: return d - 58.0 - l; + case 3: return d - 89.0 - l; + case 4: return d - 119.0 - l; + case 5: return d - 150.0 - l; + case 6: return d - 180.0 - l; + case 7: return d - 211.0 - l; + case 8: return d - 242.0 - l; + case 9: return d - 272.0 - l; + case 10: return d - 303.0 - l; + case 11: return d - 333.0 - l; + } + + return qSNaN(); // ### assert +} + +static inline double WeekDay(double t) +{ + double r = ::fmod (Day(t) + 4.0, 7.0); + return (r >= 0) ? r : r + 7.0; +} + + +static inline double MakeTime(double hour, double min, double sec, double ms) +{ + return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms; +} + +static inline double DayFromMonth(double month, double leap) +{ + switch ((int) month) { + case 0: return 0; + case 1: return 31.0; + case 2: return 59.0 + leap; + case 3: return 90.0 + leap; + case 4: return 120.0 + leap; + case 5: return 151.0 + leap; + case 6: return 181.0 + leap; + case 7: return 212.0 + leap; + case 8: return 243.0 + leap; + case 9: return 273.0 + leap; + case 10: return 304.0 + leap; + case 11: return 334.0 + leap; + } + + return qSNaN(); // ### assert? +} + +static double MakeDay(double year, double month, double day) +{ + year += ::floor(month / 12.0); + + month = ::fmod(month, 12.0); + if (month < 0) + month += 12.0; + + double t = TimeFromYear(year); + double leap = InLeapYear(t); + + day += ::floor(t / msPerDay); + day += DayFromMonth(month, leap); + + return day - 1; +} + +static inline double MakeDate(double day, double time) +{ + return day * msPerDay + time; +} + +static inline double DaylightSavingTA(double t) +{ +#ifndef Q_WS_WIN + long int tt = (long int)(t / msPerSecond); + struct tm tmtm; + if (!localtime_r((const time_t*)&tt, &tmtm)) + return 0; + return (tmtm.tm_isdst > 0) ? msPerHour : 0; +#else + Q_UNUSED(t); + /// ### implement me + return 0; +#endif +} + +static inline double LocalTime(double t) +{ + return t + LocalTZA + DaylightSavingTA(t); +} + +static inline double UTC(double t) +{ + return t - LocalTZA - DaylightSavingTA(t - LocalTZA); +} + +static inline double currentTime() +{ +#ifndef Q_WS_WIN + struct timeval tv; + + gettimeofday(&tv, 0); + return ::floor(tv.tv_sec * msPerSecond + (tv.tv_usec / 1000.0)); +#else + SYSTEMTIME st; + GetSystemTime(&st); + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + LARGE_INTEGER li; + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + return double(li.QuadPart - Q_INT64_C(116444736000000000)) / 10000.0; +#endif +} + +static inline double TimeClip(double t) +{ + if (! qIsFinite(t) || fabs(t) > 8.64e15) + return qSNaN(); + return Value::toInteger(t); +} + +static inline double FromDateTime(const QDateTime &dt) +{ + if (!dt.isValid()) + return qSNaN(); + QDate date = dt.date(); + QTime taim = dt.time(); + int year = date.year(); + int month = date.month() - 1; + int day = date.day(); + int hours = taim.hour(); + int mins = taim.minute(); + int secs = taim.second(); + int ms = taim.msec(); + double t = MakeDate(MakeDay(year, month, day), + MakeTime(hours, mins, secs, ms)); + if (dt.timeSpec() == Qt::LocalTime) + t = UTC(t); + return TimeClip(t); +} + +static inline double ParseString(const QString &s) +{ + // first try the format defined in 15.9.1.15, only if that fails fall back to + // QDateTime for parsing + + // the define string format is YYYY-MM-DDTHH:mm:ss.sssZ + // It can be date or time only, and the second and later components + // of both fields are optional + // and extended syntax for negative and large positive years exists: +/-YYYYYY + + enum Format { + Year, + Month, + Day, + Hour, + Minute, + Second, + MilliSecond, + TimezoneHour, + TimezoneMinute, + Done + }; + + const QChar *ch = s.constData(); + const QChar *end = ch + s.length(); + + uint format = Year; + int current = 0; + int currentSize = 0; + bool extendedYear = false; + + int yearSign = 1; + int year = 0; + int month = 0; + int day = 1; + int hour = 0; + int minute = 0; + int second = 0; + int msec = 0; + int offsetSign = 1; + int offset = 0; + + bool error = false; + if (*ch == '+' || *ch == '-') { + extendedYear = true; + if (*ch == '-') + yearSign = -1; + ++ch; + } + while (ch <= end) { + if (*ch >= '0' && *ch <= '9') { + current *= 10; + current += ch->unicode() - '0'; + ++currentSize; + } else { // other char, delimits field + switch (format) { + case Year: + year = current; + if (extendedYear) + error = (currentSize != 6); + else + error = (currentSize != 4); + break; + case Month: + month = current - 1; + error = (currentSize != 2) || month > 11; + break; + case Day: + day = current; + error = (currentSize != 2) || day > 31; + break; + case Hour: + hour = current; + error = (currentSize != 2) || hour > 24; + break; + case Minute: + minute = current; + error = (currentSize != 2) || minute > 60; + break; + case Second: + second = current; + error = (currentSize != 2) || second > 60; + break; + case MilliSecond: + msec = current; + error = (currentSize != 3); + break; + case TimezoneHour: + offset = current*60; + error = (currentSize != 2) || offset > 23*60; + break; + case TimezoneMinute: + offset += current; + error = (currentSize != 2) || current >= 60; + break; + } + if (*ch == 'T') { + if (format >= Hour) + error = true; + format = Hour; + } else if (*ch == '-') { + if (format < Day) + ++format; + else if (format < Minute) + error = true; + else if (format >= TimezoneHour) + error = true; + else { + offsetSign = -1; + format = TimezoneHour; + } + } else if (*ch == ':') { + if (format != Hour && format != Minute && format != TimezoneHour) + error = true; + ++format; + } else if (*ch == '.') { + if (format != Second) + error = true; + ++format; + } else if (*ch == '+') { + if (format < Minute || format >= TimezoneHour) + error = true; + format = TimezoneHour; + } else if (*ch == 'Z' || *ch == 0) { + format = Done; + } + current = 0; + currentSize = 0; + } + if (error || format == Done) + break; + ++ch; + } + + if (!error) { + double t = MakeDate(MakeDay(year * yearSign, month, day), MakeTime(hour, minute, second, msec)); + t += offset * offsetSign * 60 * 1000; + return t; + } + + QDateTime dt = QDateTime::fromString(s, Qt::TextDate); + if (!dt.isValid()) + dt = QDateTime::fromString(s, Qt::ISODate); + if (!dt.isValid()) { + QStringList formats; + formats << QStringLiteral("M/d/yyyy") + << QStringLiteral("M/d/yyyy hh:mm") + << QStringLiteral("M/d/yyyy hh:mm A") + + << QStringLiteral("M/d/yyyy, hh:mm") + << QStringLiteral("M/d/yyyy, hh:mm A") + + << QStringLiteral("MMM d yyyy") + << QStringLiteral("MMM d yyyy hh:mm") + << QStringLiteral("MMM d yyyy hh:mm:ss") + << QStringLiteral("MMM d yyyy, hh:mm") + << QStringLiteral("MMM d yyyy, hh:mm:ss") + + << QStringLiteral("MMMM d yyyy") + << QStringLiteral("MMMM d yyyy hh:mm") + << QStringLiteral("MMMM d yyyy hh:mm:ss") + << QStringLiteral("MMMM d yyyy, hh:mm") + << QStringLiteral("MMMM d yyyy, hh:mm:ss") + + << QStringLiteral("MMM d, yyyy") + << QStringLiteral("MMM d, yyyy hh:mm") + << QStringLiteral("MMM d, yyyy hh:mm:ss") + + << QStringLiteral("MMMM d, yyyy") + << QStringLiteral("MMMM d, yyyy hh:mm") + << QStringLiteral("MMMM d, yyyy hh:mm:ss") + + << QStringLiteral("d MMM yyyy") + << QStringLiteral("d MMM yyyy hh:mm") + << QStringLiteral("d MMM yyyy hh:mm:ss") + << QStringLiteral("d MMM yyyy, hh:mm") + << QStringLiteral("d MMM yyyy, hh:mm:ss") + + << QStringLiteral("d MMMM yyyy") + << QStringLiteral("d MMMM yyyy hh:mm") + << QStringLiteral("d MMMM yyyy hh:mm:ss") + << QStringLiteral("d MMMM yyyy, hh:mm") + << QStringLiteral("d MMMM yyyy, hh:mm:ss") + + << QStringLiteral("d MMM, yyyy") + << QStringLiteral("d MMM, yyyy hh:mm") + << QStringLiteral("d MMM, yyyy hh:mm:ss") + + << QStringLiteral("d MMMM, yyyy") + << QStringLiteral("d MMMM, yyyy hh:mm") + << QStringLiteral("d MMMM, yyyy hh:mm:ss"); + + for (int i = 0; i < formats.size(); ++i) { + dt = QDateTime::fromString(s, formats.at(i)); + if (dt.isValid()) + break; + } + } + return FromDateTime(dt); +} + +/*! + \internal + + Converts the ECMA Date value \tt (in UTC form) to QDateTime + according to \a spec. +*/ +static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) +{ + if (std::isnan(t)) + return QDateTime(); + if (spec == Qt::LocalTime) + t = LocalTime(t); + int year = int(YearFromTime(t)); + int month = int(MonthFromTime(t) + 1); + int day = int(DateFromTime(t)); + int hours = HourFromTime(t); + int mins = MinFromTime(t); + int secs = SecFromTime(t); + int ms = msFromTime(t); + return QDateTime(QDate(year, month, day), QTime(hours, mins, secs, ms), spec); +} + +static inline QString ToString(double t) +{ + if (std::isnan(t)) + return QStringLiteral("Invalid Date"); + QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT"); + double tzoffset = LocalTZA + DaylightSavingTA(t); + if (tzoffset) { + int hours = static_cast(::fabs(tzoffset) / 1000 / 60 / 60); + int mins = int(::fabs(tzoffset) / 1000 / 60) % 60; + str.append(QLatin1Char((tzoffset > 0) ? '+' : '-')); + if (hours < 10) + str.append(QLatin1Char('0')); + str.append(QString::number(hours)); + if (mins < 10) + str.append(QLatin1Char('0')); + str.append(QString::number(mins)); + } + return str; +} + +static inline QString ToUTCString(double t) +{ + if (std::isnan(t)) + return QStringLiteral("Invalid Date"); + return ToDateTime(t, Qt::UTC).toString() + QStringLiteral(" GMT"); +} + +static inline QString ToDateString(double t) +{ + return ToDateTime(t, Qt::LocalTime).date().toString(); +} + +static inline QString ToTimeString(double t) +{ + return ToDateTime(t, Qt::LocalTime).time().toString(); +} + +static inline QString ToLocaleString(double t) +{ + return ToDateTime(t, Qt::LocalTime).toString(Qt::LocaleDate); +} + +static inline QString ToLocaleDateString(double t) +{ + return ToDateTime(t, Qt::LocalTime).date().toString(Qt::LocaleDate); +} + +static inline QString ToLocaleTimeString(double t) +{ + return ToDateTime(t, Qt::LocalTime).time().toString(Qt::LocaleDate); +} + +static double getLocalTZA() +{ +#ifndef Q_WS_WIN + struct tm t; + time_t curr; + time(&curr); + localtime_r(&curr, &t); + time_t locl = mktime(&t); + gmtime_r(&curr, &t); + time_t globl = mktime(&t); + return double(locl - globl) * 1000.0; +#else + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + return -tzInfo.Bias * 60.0 * 1000.0; +#endif +} + + +DateCtor::DateCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value DateCtor::construct(ExecutionContext *ctx) +{ + double t = 0; + + if (ctx->argumentCount == 0) + t = currentTime(); + + else if (ctx->argumentCount == 1) { + Value arg = ctx->argument(0); + if (DateObject *d = arg.asDateObject()) + arg = d->value; + else + arg = __qmljs_to_primitive(arg, ctx, PREFERREDTYPE_HINT); + + if (arg.isString()) + t = ParseString(arg.stringValue()->toQString()); + else + t = TimeClip(arg.toNumber(ctx)); + } + + else { // ctx->argumentCount > 1 + double year = ctx->argument(0).toNumber(ctx); + double month = ctx->argument(1).toNumber(ctx); + double day = ctx->argumentCount >= 3 ? ctx->argument(2).toNumber(ctx) : 1; + double hours = ctx->argumentCount >= 4 ? ctx->argument(3).toNumber(ctx) : 0; + double mins = ctx->argumentCount >= 5 ? ctx->argument(4).toNumber(ctx) : 0; + double secs = ctx->argumentCount >= 6 ? ctx->argument(5).toNumber(ctx) : 0; + double ms = ctx->argumentCount >= 7 ? ctx->argument(6).toNumber(ctx) : 0; + if (year >= 0 && year <= 99) + year += 1900; + t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); + t = TimeClip(UTC(t)); + } + + Object *d = ctx->engine->newDateObject(Value::fromDouble(t)); + return Value::fromObject(d); +} + +Value DateCtor::call(ExecutionContext *ctx) +{ + double t = currentTime(); + return Value::fromString(ctx, ToString(t)); +} + +void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + LocalTZA = getLocalTZA(); + + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("parse"), method_parse, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toDateString"), method_toDateString, 0); + defineDefaultProperty(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + defineDefaultProperty(ctx, QStringLiteral("getTime"), method_getTime, 0); + defineDefaultProperty(ctx, QStringLiteral("getYear"), method_getYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getFullYear"), method_getFullYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getMonth"), method_getMonth, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMonth"), method_getUTCMonth, 0); + defineDefaultProperty(ctx, QStringLiteral("getDate"), method_getDate, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCDate"), method_getUTCDate, 0); + defineDefaultProperty(ctx, QStringLiteral("getDay"), method_getDay, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCDay"), method_getUTCDay, 0); + defineDefaultProperty(ctx, QStringLiteral("getHours"), method_getHours, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCHours"), method_getUTCHours, 0); + defineDefaultProperty(ctx, QStringLiteral("getMinutes"), method_getMinutes, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0); + defineDefaultProperty(ctx, QStringLiteral("getSeconds"), method_getSeconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getMilliseconds"), method_getMilliseconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0); + defineDefaultProperty(ctx, QStringLiteral("setTime"), method_setTime, 1); + defineDefaultProperty(ctx, QStringLiteral("setMilliseconds"), method_setMilliseconds, 1); + defineDefaultProperty(ctx, QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1); + defineDefaultProperty(ctx, QStringLiteral("setSeconds"), method_setSeconds, 2); + defineDefaultProperty(ctx, QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2); + defineDefaultProperty(ctx, QStringLiteral("setMinutes"), method_setMinutes, 3); + defineDefaultProperty(ctx, QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3); + defineDefaultProperty(ctx, QStringLiteral("setHours"), method_setHours, 4); + defineDefaultProperty(ctx, QStringLiteral("setUTCHours"), method_setUTCHours, 4); + defineDefaultProperty(ctx, QStringLiteral("setDate"), method_setDate, 1); + defineDefaultProperty(ctx, QStringLiteral("setUTCDate"), method_setUTCDate, 1); + defineDefaultProperty(ctx, QStringLiteral("setMonth"), method_setMonth, 2); + defineDefaultProperty(ctx, QStringLiteral("setUTCMonth"), method_setUTCMonth, 2); + defineDefaultProperty(ctx, QStringLiteral("setYear"), method_setYear, 1); + defineDefaultProperty(ctx, QStringLiteral("setFullYear"), method_setFullYear, 3); + defineDefaultProperty(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); + defineDefaultProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); + defineDefaultProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); + defineDefaultProperty(ctx, QStringLiteral("toISOString"), method_toISOString, 0); +} + +double DatePrototype::getThisDate(ExecutionContext *ctx) +{ + if (DateObject *thisObject = ctx->thisObject.asDateObject()) + return thisObject->value.asDouble(); + else { + ctx->throwTypeError(); + return 0; + } +} + +Value DatePrototype::method_parse(ExecutionContext *ctx) +{ + return Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString())); +} + +Value DatePrototype::method_UTC(ExecutionContext *ctx) +{ + const int numArgs = ctx->argumentCount; + if (numArgs >= 2) { + double year = ctx->argument(0).toNumber(ctx); + double month = ctx->argument(1).toNumber(ctx); + double day = numArgs >= 3 ? ctx->argument(2).toNumber(ctx) : 1; + double hours = numArgs >= 4 ? ctx->argument(3).toNumber(ctx) : 0; + double mins = numArgs >= 5 ? ctx->argument(4).toNumber(ctx) : 0; + double secs = numArgs >= 6 ? ctx->argument(5).toNumber(ctx) : 0; + double ms = numArgs >= 7 ? ctx->argument(6).toNumber(ctx) : 0; + if (year >= 0 && year <= 99) + year += 1900; + double t = MakeDate(MakeDay(year, month, day), + MakeTime(hours, mins, secs, ms)); + return Value::fromDouble(TimeClip(t)); + } + return Value::undefinedValue(); +} + +Value DatePrototype::method_toString(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToString(t)); +} + +Value DatePrototype::method_toDateString(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToDateString(t)); +} + +Value DatePrototype::method_toTimeString(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToTimeString(t)); +} + +Value DatePrototype::method_toLocaleString(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleString(t)); +} + +Value DatePrototype::method_toLocaleDateString(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleDateString(t)); +} + +Value DatePrototype::method_toLocaleTimeString(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleTimeString(t)); +} + +Value DatePrototype::method_valueOf(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getTime(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getYear(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = YearFromTime(LocalTime(t)) - 1900; + return Value::fromDouble(t); +} + +Value DatePrototype::method_getFullYear(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = YearFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCFullYear(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = YearFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMonth(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MonthFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMonth(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MonthFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getDate(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = DateFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCDate(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = DateFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getDay(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = WeekDay(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCDay(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = WeekDay(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getHours(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = HourFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCHours(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = HourFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMinutes(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MinFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMinutes(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MinFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getSeconds(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = SecFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCSeconds(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = SecFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMilliseconds(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = msFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMilliseconds(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = msFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getTimezoneOffset(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = (t - LocalTime(t)) / msPerMinute; + return Value::fromDouble(t); +} + +Value DatePrototype::method_setTime(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + self->value.setDouble(TimeClip(ctx->argument(0).toNumber(ctx))); + return self->value; +} + +Value DatePrototype::method_setMilliseconds(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double ms = ctx->argument(0).toNumber(ctx); + self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + return self->value; +} + +Value DatePrototype::method_setUTCMilliseconds(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double ms = ctx->argument(0).toNumber(ctx); + self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + return self->value; +} + +Value DatePrototype::method_setSeconds(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double sec = ctx->argument(0).toNumber(ctx); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCSeconds(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double sec = ctx->argument(0).toNumber(ctx); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setMinutes(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double min = ctx->argument(0).toNumber(ctx); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCMinutes(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double min = ctx->argument(0).toNumber(ctx); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setHours(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double hour = ctx->argument(0).toNumber(ctx); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCHours(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double hour = ctx->argument(0).toNumber(ctx); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setDate(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double date = ctx->argument(0).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCDate(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double date = ctx->argument(0).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setMonth(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double month = ctx->argument(0).toNumber(ctx); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCMonth(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double month = ctx->argument(0).toNumber(ctx); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setYear(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + if (std::isnan(t)) + t = 0; + else + t = LocalTime(t); + double year = ctx->argument(0).toNumber(ctx); + double r; + if (std::isnan(year)) { + r = qSNaN(); + } else { + if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99)) + year += 1900; + r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); + r = UTC(MakeDate(r, TimeWithinDay(t))); + r = TimeClip(r); + } + self->value.setDouble(r); + return self->value; +} + +Value DatePrototype::method_setUTCFullYear(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double year = ctx->argument(0).toNumber(ctx); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setFullYear(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double year = ctx->argument(0).toNumber(ctx); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_toUTCString(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + return Value::fromString(ctx, ToUTCString(t)); +} + +static void addZeroPrefixedInt(QString &str, int num, int nDigits) +{ + str.resize(str.size() + nDigits); + + QChar *c = str.data() + str.size() - 1; + while (nDigits) { + *c = QChar(num % 10 + '0'); + num /= 10; + --c; + --nDigits; + } +} + +Value DatePrototype::method_toISOString(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + if (!std::isfinite(t)) + ctx->throwRangeError(ctx->thisObject); + + QString result; + int year = (int)YearFromTime(t); + if (year < 0 || year > 9999) { + if (qAbs(year) >= 1000000) + return Value::fromString(ctx, QStringLiteral("Invalid Date")); + result += year < 0 ? '-' : '+'; + year = qAbs(year); + addZeroPrefixedInt(result, year, 6); + } else { + addZeroPrefixedInt(result, year, 4); + } + result += '-'; + addZeroPrefixedInt(result, (int)MonthFromTime(t) + 1, 2); + result += '-'; + addZeroPrefixedInt(result, (int)DateFromTime(t), 2); + result += 'T'; + addZeroPrefixedInt(result, HourFromTime(t), 2); + result += ':'; + addZeroPrefixedInt(result, MinFromTime(t), 2); + result += ':'; + addZeroPrefixedInt(result, SecFromTime(t), 2); + result += '.'; + addZeroPrefixedInt(result, msFromTime(t), 3); + result += 'Z'; + + return Value::fromString(ctx, result); +} diff --git a/qv4dateobject.h b/qv4dateobject.h new file mode 100644 index 0000000000..3a67c47f61 --- /dev/null +++ b/qv4dateobject.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4DATEOBJECT_P_H +#define QV4DATEOBJECT_P_H + +#include "qmljs_objects.h" +#include "qv4ecmaobjects_p.h" +#include + +namespace QQmlJS { +namespace VM { + +struct DateObject: Object { + Value value; + DateObject(const Value &value): value(value) {} + virtual QString className() { return QStringLiteral("Date"); } + virtual DateObject *asDateObject() { return this; } +}; + +struct DateCtor: FunctionObject +{ + DateCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct DatePrototype: DateObject +{ + DatePrototype(): DateObject(Value::fromDouble(qSNaN())) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static double getThisDate(ExecutionContext *ctx); + + static Value method_parse(ExecutionContext *ctx); + static Value method_UTC(ExecutionContext *ctx); + + static Value method_toString(ExecutionContext *ctx); + static Value method_toDateString(ExecutionContext *ctx); + static Value method_toTimeString(ExecutionContext *ctx); + static Value method_toLocaleString(ExecutionContext *ctx); + static Value method_toLocaleDateString(ExecutionContext *ctx); + static Value method_toLocaleTimeString(ExecutionContext *ctx); + static Value method_valueOf(ExecutionContext *ctx); + static Value method_getTime(ExecutionContext *ctx); + static Value method_getYear(ExecutionContext *ctx); + static Value method_getFullYear(ExecutionContext *ctx); + static Value method_getUTCFullYear(ExecutionContext *ctx); + static Value method_getMonth(ExecutionContext *ctx); + static Value method_getUTCMonth(ExecutionContext *ctx); + static Value method_getDate(ExecutionContext *ctx); + static Value method_getUTCDate(ExecutionContext *ctx); + static Value method_getDay(ExecutionContext *ctx); + static Value method_getUTCDay(ExecutionContext *ctx); + static Value method_getHours(ExecutionContext *ctx); + static Value method_getUTCHours(ExecutionContext *ctx); + static Value method_getMinutes(ExecutionContext *ctx); + static Value method_getUTCMinutes(ExecutionContext *ctx); + static Value method_getSeconds(ExecutionContext *ctx); + static Value method_getUTCSeconds(ExecutionContext *ctx); + static Value method_getMilliseconds(ExecutionContext *ctx); + static Value method_getUTCMilliseconds(ExecutionContext *ctx); + static Value method_getTimezoneOffset(ExecutionContext *ctx); + static Value method_setTime(ExecutionContext *ctx); + static Value method_setMilliseconds(ExecutionContext *ctx); + static Value method_setUTCMilliseconds(ExecutionContext *ctx); + static Value method_setSeconds(ExecutionContext *ctx); + static Value method_setUTCSeconds(ExecutionContext *ctx); + static Value method_setMinutes(ExecutionContext *ctx); + static Value method_setUTCMinutes(ExecutionContext *ctx); + static Value method_setHours(ExecutionContext *ctx); + static Value method_setUTCHours(ExecutionContext *ctx); + static Value method_setDate(ExecutionContext *ctx); + static Value method_setUTCDate(ExecutionContext *ctx); + static Value method_setMonth(ExecutionContext *ctx); + static Value method_setUTCMonth(ExecutionContext *ctx); + static Value method_setYear(ExecutionContext *ctx); + static Value method_setFullYear(ExecutionContext *ctx); + static Value method_setUTCFullYear(ExecutionContext *ctx); + static Value method_toUTCString(ExecutionContext *ctx); + static Value method_toISOString(ExecutionContext *ctx); +}; + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index df3ff9bea1..933cb1840a 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -75,588 +75,6 @@ using namespace QQmlJS::VM; static const double qt_PI = 2.0 * ::asin(1.0); -static const double HoursPerDay = 24.0; -static const double MinutesPerHour = 60.0; -static const double SecondsPerMinute = 60.0; -static const double msPerSecond = 1000.0; -static const double msPerMinute = 60000.0; -static const double msPerHour = 3600000.0; -static const double msPerDay = 86400000.0; - -static double LocalTZA = 0.0; // initialized at startup - -static inline double TimeWithinDay(double t) -{ - double r = ::fmod(t, msPerDay); - return (r >= 0) ? r : r + msPerDay; -} - -static inline int HourFromTime(double t) -{ - int r = int(::fmod(::floor(t / msPerHour), HoursPerDay)); - return (r >= 0) ? r : r + int(HoursPerDay); -} - -static inline int MinFromTime(double t) -{ - int r = int(::fmod(::floor(t / msPerMinute), MinutesPerHour)); - return (r >= 0) ? r : r + int(MinutesPerHour); -} - -static inline int SecFromTime(double t) -{ - int r = int(::fmod(::floor(t / msPerSecond), SecondsPerMinute)); - return (r >= 0) ? r : r + int(SecondsPerMinute); -} - -static inline int msFromTime(double t) -{ - int r = int(::fmod(t, msPerSecond)); - return (r >= 0) ? r : r + int(msPerSecond); -} - -static inline double Day(double t) -{ - return ::floor(t / msPerDay); -} - -static inline double DaysInYear(double y) -{ - if (::fmod(y, 4)) - return 365; - - else if (::fmod(y, 100)) - return 366; - - else if (::fmod(y, 400)) - return 365; - - return 366; -} - -static inline double DayFromYear(double y) -{ - return 365 * (y - 1970) - + ::floor((y - 1969) / 4) - - ::floor((y - 1901) / 100) - + ::floor((y - 1601) / 400); -} - -static inline double TimeFromYear(double y) -{ - return msPerDay * DayFromYear(y); -} - -static inline double YearFromTime(double t) -{ - int y = 1970; - y += (int) ::floor(t / (msPerDay * 365.2425)); - - double t2 = TimeFromYear(y); - return (t2 > t) ? y - 1 : ((t2 + msPerDay * DaysInYear(y)) <= t) ? y + 1 : y; -} - -static inline bool InLeapYear(double t) -{ - double x = DaysInYear(YearFromTime(t)); - if (x == 365) - return 0; - - assert(x == 366); - return 1; -} - -static inline double DayWithinYear(double t) -{ - return Day(t) - DayFromYear(YearFromTime(t)); -} - -static inline double MonthFromTime(double t) -{ - double d = DayWithinYear(t); - double l = InLeapYear(t); - - if (d < 31.0) - return 0; - - else if (d < 59.0 + l) - return 1; - - else if (d < 90.0 + l) - return 2; - - else if (d < 120.0 + l) - return 3; - - else if (d < 151.0 + l) - return 4; - - else if (d < 181.0 + l) - return 5; - - else if (d < 212.0 + l) - return 6; - - else if (d < 243.0 + l) - return 7; - - else if (d < 273.0 + l) - return 8; - - else if (d < 304.0 + l) - return 9; - - else if (d < 334.0 + l) - return 10; - - else if (d < 365.0 + l) - return 11; - - return qSNaN(); // ### assert? -} - -static inline double DateFromTime(double t) -{ - int m = (int) Value::toInteger(MonthFromTime(t)); - double d = DayWithinYear(t); - double l = InLeapYear(t); - - switch (m) { - case 0: return d + 1.0; - case 1: return d - 30.0; - case 2: return d - 58.0 - l; - case 3: return d - 89.0 - l; - case 4: return d - 119.0 - l; - case 5: return d - 150.0 - l; - case 6: return d - 180.0 - l; - case 7: return d - 211.0 - l; - case 8: return d - 242.0 - l; - case 9: return d - 272.0 - l; - case 10: return d - 303.0 - l; - case 11: return d - 333.0 - l; - } - - return qSNaN(); // ### assert -} - -static inline double WeekDay(double t) -{ - double r = ::fmod (Day(t) + 4.0, 7.0); - return (r >= 0) ? r : r + 7.0; -} - - -static inline double MakeTime(double hour, double min, double sec, double ms) -{ - return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms; -} - -static inline double DayFromMonth(double month, double leap) -{ - switch ((int) month) { - case 0: return 0; - case 1: return 31.0; - case 2: return 59.0 + leap; - case 3: return 90.0 + leap; - case 4: return 120.0 + leap; - case 5: return 151.0 + leap; - case 6: return 181.0 + leap; - case 7: return 212.0 + leap; - case 8: return 243.0 + leap; - case 9: return 273.0 + leap; - case 10: return 304.0 + leap; - case 11: return 334.0 + leap; - } - - return qSNaN(); // ### assert? -} - -static double MakeDay(double year, double month, double day) -{ - year += ::floor(month / 12.0); - - month = ::fmod(month, 12.0); - if (month < 0) - month += 12.0; - - double t = TimeFromYear(year); - double leap = InLeapYear(t); - - day += ::floor(t / msPerDay); - day += DayFromMonth(month, leap); - - return day - 1; -} - -static inline double MakeDate(double day, double time) -{ - return day * msPerDay + time; -} - -static inline double DaylightSavingTA(double t) -{ -#ifndef Q_WS_WIN - long int tt = (long int)(t / msPerSecond); - struct tm tmtm; - if (!localtime_r((const time_t*)&tt, &tmtm)) - return 0; - return (tmtm.tm_isdst > 0) ? msPerHour : 0; -#else - Q_UNUSED(t); - /// ### implement me - return 0; -#endif -} - -static inline double LocalTime(double t) -{ - return t + LocalTZA + DaylightSavingTA(t); -} - -static inline double UTC(double t) -{ - return t - LocalTZA - DaylightSavingTA(t - LocalTZA); -} - -static inline double currentTime() -{ -#ifndef Q_WS_WIN - struct timeval tv; - - gettimeofday(&tv, 0); - return ::floor(tv.tv_sec * msPerSecond + (tv.tv_usec / 1000.0)); -#else - SYSTEMTIME st; - GetSystemTime(&st); - FILETIME ft; - SystemTimeToFileTime(&st, &ft); - LARGE_INTEGER li; - li.LowPart = ft.dwLowDateTime; - li.HighPart = ft.dwHighDateTime; - return double(li.QuadPart - Q_INT64_C(116444736000000000)) / 10000.0; -#endif -} - -static inline double TimeClip(double t) -{ - if (! qIsFinite(t) || fabs(t) > 8.64e15) - return qSNaN(); - return Value::toInteger(t); -} - -static inline double FromDateTime(const QDateTime &dt) -{ - if (!dt.isValid()) - return qSNaN(); - QDate date = dt.date(); - QTime taim = dt.time(); - int year = date.year(); - int month = date.month() - 1; - int day = date.day(); - int hours = taim.hour(); - int mins = taim.minute(); - int secs = taim.second(); - int ms = taim.msec(); - double t = MakeDate(MakeDay(year, month, day), - MakeTime(hours, mins, secs, ms)); - if (dt.timeSpec() == Qt::LocalTime) - t = UTC(t); - return TimeClip(t); -} - -static inline double ParseString(const QString &s) -{ - // first try the format defined in 15.9.1.15, only if that fails fall back to - // QDateTime for parsing - - // the define string format is YYYY-MM-DDTHH:mm:ss.sssZ - // It can be date or time only, and the second and later components - // of both fields are optional - // and extended syntax for negative and large positive years exists: +/-YYYYYY - - enum Format { - Year, - Month, - Day, - Hour, - Minute, - Second, - MilliSecond, - TimezoneHour, - TimezoneMinute, - Done - }; - - const QChar *ch = s.constData(); - const QChar *end = ch + s.length(); - - uint format = Year; - int current = 0; - int currentSize = 0; - bool extendedYear = false; - - int yearSign = 1; - int year = 0; - int month = 0; - int day = 1; - int hour = 0; - int minute = 0; - int second = 0; - int msec = 0; - int offsetSign = 1; - int offset = 0; - - bool error = false; - if (*ch == '+' || *ch == '-') { - extendedYear = true; - if (*ch == '-') - yearSign = -1; - ++ch; - } - while (ch <= end) { - if (*ch >= '0' && *ch <= '9') { - current *= 10; - current += ch->unicode() - '0'; - ++currentSize; - } else { // other char, delimits field - switch (format) { - case Year: - year = current; - if (extendedYear) - error = (currentSize != 6); - else - error = (currentSize != 4); - break; - case Month: - month = current - 1; - error = (currentSize != 2) || month > 11; - break; - case Day: - day = current; - error = (currentSize != 2) || day > 31; - break; - case Hour: - hour = current; - error = (currentSize != 2) || hour > 24; - break; - case Minute: - minute = current; - error = (currentSize != 2) || minute > 60; - break; - case Second: - second = current; - error = (currentSize != 2) || second > 60; - break; - case MilliSecond: - msec = current; - error = (currentSize != 3); - break; - case TimezoneHour: - offset = current*60; - error = (currentSize != 2) || offset > 23*60; - break; - case TimezoneMinute: - offset += current; - error = (currentSize != 2) || current >= 60; - break; - } - if (*ch == 'T') { - if (format >= Hour) - error = true; - format = Hour; - } else if (*ch == '-') { - if (format < Day) - ++format; - else if (format < Minute) - error = true; - else if (format >= TimezoneHour) - error = true; - else { - offsetSign = -1; - format = TimezoneHour; - } - } else if (*ch == ':') { - if (format != Hour && format != Minute && format != TimezoneHour) - error = true; - ++format; - } else if (*ch == '.') { - if (format != Second) - error = true; - ++format; - } else if (*ch == '+') { - if (format < Minute || format >= TimezoneHour) - error = true; - format = TimezoneHour; - } else if (*ch == 'Z' || *ch == 0) { - format = Done; - } - current = 0; - currentSize = 0; - } - if (error || format == Done) - break; - ++ch; - } - - if (!error) { - double t = MakeDate(MakeDay(year * yearSign, month, day), MakeTime(hour, minute, second, msec)); - t += offset * offsetSign * 60 * 1000; - return t; - } - - QDateTime dt = QDateTime::fromString(s, Qt::TextDate); - if (!dt.isValid()) - dt = QDateTime::fromString(s, Qt::ISODate); - if (!dt.isValid()) { - QStringList formats; - formats << QStringLiteral("M/d/yyyy") - << QStringLiteral("M/d/yyyy hh:mm") - << QStringLiteral("M/d/yyyy hh:mm A") - - << QStringLiteral("M/d/yyyy, hh:mm") - << QStringLiteral("M/d/yyyy, hh:mm A") - - << QStringLiteral("MMM d yyyy") - << QStringLiteral("MMM d yyyy hh:mm") - << QStringLiteral("MMM d yyyy hh:mm:ss") - << QStringLiteral("MMM d yyyy, hh:mm") - << QStringLiteral("MMM d yyyy, hh:mm:ss") - - << QStringLiteral("MMMM d yyyy") - << QStringLiteral("MMMM d yyyy hh:mm") - << QStringLiteral("MMMM d yyyy hh:mm:ss") - << QStringLiteral("MMMM d yyyy, hh:mm") - << QStringLiteral("MMMM d yyyy, hh:mm:ss") - - << QStringLiteral("MMM d, yyyy") - << QStringLiteral("MMM d, yyyy hh:mm") - << QStringLiteral("MMM d, yyyy hh:mm:ss") - - << QStringLiteral("MMMM d, yyyy") - << QStringLiteral("MMMM d, yyyy hh:mm") - << QStringLiteral("MMMM d, yyyy hh:mm:ss") - - << QStringLiteral("d MMM yyyy") - << QStringLiteral("d MMM yyyy hh:mm") - << QStringLiteral("d MMM yyyy hh:mm:ss") - << QStringLiteral("d MMM yyyy, hh:mm") - << QStringLiteral("d MMM yyyy, hh:mm:ss") - - << QStringLiteral("d MMMM yyyy") - << QStringLiteral("d MMMM yyyy hh:mm") - << QStringLiteral("d MMMM yyyy hh:mm:ss") - << QStringLiteral("d MMMM yyyy, hh:mm") - << QStringLiteral("d MMMM yyyy, hh:mm:ss") - - << QStringLiteral("d MMM, yyyy") - << QStringLiteral("d MMM, yyyy hh:mm") - << QStringLiteral("d MMM, yyyy hh:mm:ss") - - << QStringLiteral("d MMMM, yyyy") - << QStringLiteral("d MMMM, yyyy hh:mm") - << QStringLiteral("d MMMM, yyyy hh:mm:ss"); - - for (int i = 0; i < formats.size(); ++i) { - dt = QDateTime::fromString(s, formats.at(i)); - if (dt.isValid()) - break; - } - } - return FromDateTime(dt); -} - -/*! - \internal - - Converts the ECMA Date value \tt (in UTC form) to QDateTime - according to \a spec. -*/ -static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) -{ - if (std::isnan(t)) - return QDateTime(); - if (spec == Qt::LocalTime) - t = LocalTime(t); - int year = int(YearFromTime(t)); - int month = int(MonthFromTime(t) + 1); - int day = int(DateFromTime(t)); - int hours = HourFromTime(t); - int mins = MinFromTime(t); - int secs = SecFromTime(t); - int ms = msFromTime(t); - return QDateTime(QDate(year, month, day), QTime(hours, mins, secs, ms), spec); -} - -static inline QString ToString(double t) -{ - if (std::isnan(t)) - return QStringLiteral("Invalid Date"); - QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT"); - double tzoffset = LocalTZA + DaylightSavingTA(t); - if (tzoffset) { - int hours = static_cast(::fabs(tzoffset) / 1000 / 60 / 60); - int mins = int(::fabs(tzoffset) / 1000 / 60) % 60; - str.append(QLatin1Char((tzoffset > 0) ? '+' : '-')); - if (hours < 10) - str.append(QLatin1Char('0')); - str.append(QString::number(hours)); - if (mins < 10) - str.append(QLatin1Char('0')); - str.append(QString::number(mins)); - } - return str; -} - -static inline QString ToUTCString(double t) -{ - if (std::isnan(t)) - return QStringLiteral("Invalid Date"); - return ToDateTime(t, Qt::UTC).toString() + QStringLiteral(" GMT"); -} - -static inline QString ToDateString(double t) -{ - return ToDateTime(t, Qt::LocalTime).date().toString(); -} - -static inline QString ToTimeString(double t) -{ - return ToDateTime(t, Qt::LocalTime).time().toString(); -} - -static inline QString ToLocaleString(double t) -{ - return ToDateTime(t, Qt::LocalTime).toString(Qt::LocaleDate); -} - -static inline QString ToLocaleDateString(double t) -{ - return ToDateTime(t, Qt::LocalTime).date().toString(Qt::LocaleDate); -} - -static inline QString ToLocaleTimeString(double t) -{ - return ToDateTime(t, Qt::LocalTime).time().toString(Qt::LocaleDate); -} - -static double getLocalTZA() -{ -#ifndef Q_WS_WIN - struct tm t; - time_t curr; - time(&curr); - localtime_r(&curr, &t); - time_t locl = mktime(&t); - gmtime_r(&curr, &t); - time_t globl = mktime(&t); - return double(locl - globl) * 1000.0; -#else - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - return -tzInfo.Bias * 60.0 * 1000.0; -#endif -} - // // Object // @@ -2548,637 +1966,6 @@ Value FunctionPrototype::method_bind(ExecutionContext *ctx) return Value::fromObject(f); } -// -// Date object -// -DateCtor::DateCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value DateCtor::construct(ExecutionContext *ctx) -{ - double t = 0; - - if (ctx->argumentCount == 0) - t = currentTime(); - - else if (ctx->argumentCount == 1) { - Value arg = ctx->argument(0); - if (DateObject *d = arg.asDateObject()) - arg = d->value; - else - arg = __qmljs_to_primitive(arg, ctx, PREFERREDTYPE_HINT); - - if (arg.isString()) - t = ParseString(arg.stringValue()->toQString()); - else - t = TimeClip(arg.toNumber(ctx)); - } - - else { // ctx->argumentCount > 1 - double year = ctx->argument(0).toNumber(ctx); - double month = ctx->argument(1).toNumber(ctx); - double day = ctx->argumentCount >= 3 ? ctx->argument(2).toNumber(ctx) : 1; - double hours = ctx->argumentCount >= 4 ? ctx->argument(3).toNumber(ctx) : 0; - double mins = ctx->argumentCount >= 5 ? ctx->argument(4).toNumber(ctx) : 0; - double secs = ctx->argumentCount >= 6 ? ctx->argument(5).toNumber(ctx) : 0; - double ms = ctx->argumentCount >= 7 ? ctx->argument(6).toNumber(ctx) : 0; - if (year >= 0 && year <= 99) - year += 1900; - t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); - t = TimeClip(UTC(t)); - } - - Object *d = ctx->engine->newDateObject(Value::fromDouble(t)); - return Value::fromObject(d); -} - -Value DateCtor::call(ExecutionContext *ctx) -{ - double t = currentTime(); - return Value::fromString(ctx, ToString(t)); -} - -void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - LocalTZA = getLocalTZA(); - - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("parse"), method_parse, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); - - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("toDateString"), method_toDateString, 0); - defineDefaultProperty(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); - defineDefaultProperty(ctx, QStringLiteral("getTime"), method_getTime, 0); - defineDefaultProperty(ctx, QStringLiteral("getYear"), method_getYear, 0); - defineDefaultProperty(ctx, QStringLiteral("getFullYear"), method_getFullYear, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0); - defineDefaultProperty(ctx, QStringLiteral("getMonth"), method_getMonth, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCMonth"), method_getUTCMonth, 0); - defineDefaultProperty(ctx, QStringLiteral("getDate"), method_getDate, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCDate"), method_getUTCDate, 0); - defineDefaultProperty(ctx, QStringLiteral("getDay"), method_getDay, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCDay"), method_getUTCDay, 0); - defineDefaultProperty(ctx, QStringLiteral("getHours"), method_getHours, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCHours"), method_getUTCHours, 0); - defineDefaultProperty(ctx, QStringLiteral("getMinutes"), method_getMinutes, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0); - defineDefaultProperty(ctx, QStringLiteral("getSeconds"), method_getSeconds, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0); - defineDefaultProperty(ctx, QStringLiteral("getMilliseconds"), method_getMilliseconds, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0); - defineDefaultProperty(ctx, QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0); - defineDefaultProperty(ctx, QStringLiteral("setTime"), method_setTime, 1); - defineDefaultProperty(ctx, QStringLiteral("setMilliseconds"), method_setMilliseconds, 1); - defineDefaultProperty(ctx, QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1); - defineDefaultProperty(ctx, QStringLiteral("setSeconds"), method_setSeconds, 2); - defineDefaultProperty(ctx, QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2); - defineDefaultProperty(ctx, QStringLiteral("setMinutes"), method_setMinutes, 3); - defineDefaultProperty(ctx, QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3); - defineDefaultProperty(ctx, QStringLiteral("setHours"), method_setHours, 4); - defineDefaultProperty(ctx, QStringLiteral("setUTCHours"), method_setUTCHours, 4); - defineDefaultProperty(ctx, QStringLiteral("setDate"), method_setDate, 1); - defineDefaultProperty(ctx, QStringLiteral("setUTCDate"), method_setUTCDate, 1); - defineDefaultProperty(ctx, QStringLiteral("setMonth"), method_setMonth, 2); - defineDefaultProperty(ctx, QStringLiteral("setUTCMonth"), method_setUTCMonth, 2); - defineDefaultProperty(ctx, QStringLiteral("setYear"), method_setYear, 1); - defineDefaultProperty(ctx, QStringLiteral("setFullYear"), method_setFullYear, 3); - defineDefaultProperty(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); - defineDefaultProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); - defineDefaultProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); - defineDefaultProperty(ctx, QStringLiteral("toISOString"), method_toISOString, 0); -} - -double DatePrototype::getThisDate(ExecutionContext *ctx) -{ - if (DateObject *thisObject = ctx->thisObject.asDateObject()) - return thisObject->value.asDouble(); - else { - ctx->throwTypeError(); - return 0; - } -} - -Value DatePrototype::method_parse(ExecutionContext *ctx) -{ - return Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString())); -} - -Value DatePrototype::method_UTC(ExecutionContext *ctx) -{ - const int numArgs = ctx->argumentCount; - if (numArgs >= 2) { - double year = ctx->argument(0).toNumber(ctx); - double month = ctx->argument(1).toNumber(ctx); - double day = numArgs >= 3 ? ctx->argument(2).toNumber(ctx) : 1; - double hours = numArgs >= 4 ? ctx->argument(3).toNumber(ctx) : 0; - double mins = numArgs >= 5 ? ctx->argument(4).toNumber(ctx) : 0; - double secs = numArgs >= 6 ? ctx->argument(5).toNumber(ctx) : 0; - double ms = numArgs >= 7 ? ctx->argument(6).toNumber(ctx) : 0; - if (year >= 0 && year <= 99) - year += 1900; - double t = MakeDate(MakeDay(year, month, day), - MakeTime(hours, mins, secs, ms)); - return Value::fromDouble(TimeClip(t)); - } - return Value::undefinedValue(); -} - -Value DatePrototype::method_toString(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToString(t)); -} - -Value DatePrototype::method_toDateString(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToDateString(t)); -} - -Value DatePrototype::method_toTimeString(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToTimeString(t)); -} - -Value DatePrototype::method_toLocaleString(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToLocaleString(t)); -} - -Value DatePrototype::method_toLocaleDateString(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToLocaleDateString(t)); -} - -Value DatePrototype::method_toLocaleTimeString(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToLocaleTimeString(t)); -} - -Value DatePrototype::method_valueOf(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getTime(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getYear(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = YearFromTime(LocalTime(t)) - 1900; - return Value::fromDouble(t); -} - -Value DatePrototype::method_getFullYear(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = YearFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCFullYear(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = YearFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getMonth(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = MonthFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCMonth(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = MonthFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getDate(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = DateFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCDate(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = DateFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getDay(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = WeekDay(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCDay(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = WeekDay(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getHours(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = HourFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCHours(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = HourFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getMinutes(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = MinFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCMinutes(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = MinFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getSeconds(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = SecFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCSeconds(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = SecFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getMilliseconds(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = msFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCMilliseconds(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = msFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getTimezoneOffset(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = (t - LocalTime(t)) / msPerMinute; - return Value::fromDouble(t); -} - -Value DatePrototype::method_setTime(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - self->value.setDouble(TimeClip(ctx->argument(0).toNumber(ctx))); - return self->value; -} - -Value DatePrototype::method_setMilliseconds(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double ms = ctx->argument(0).toNumber(ctx); - self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); - return self->value; -} - -Value DatePrototype::method_setUTCMilliseconds(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double ms = ctx->argument(0).toNumber(ctx); - self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); - return self->value; -} - -Value DatePrototype::method_setSeconds(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double sec = ctx->argument(0).toNumber(ctx); - double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCSeconds(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double sec = ctx->argument(0).toNumber(ctx); - double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setMinutes(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double min = ctx->argument(0).toNumber(ctx); - double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); - double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCMinutes(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double min = ctx->argument(0).toNumber(ctx); - double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); - double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setHours(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double hour = ctx->argument(0).toNumber(ctx); - double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); - double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); - double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCHours(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double hour = ctx->argument(0).toNumber(ctx); - double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); - double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); - double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setDate(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double date = ctx->argument(0).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCDate(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double date = ctx->argument(0).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setMonth(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double month = ctx->argument(0).toNumber(ctx); - double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCMonth(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double month = ctx->argument(0).toNumber(ctx); - double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setYear(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - if (std::isnan(t)) - t = 0; - else - t = LocalTime(t); - double year = ctx->argument(0).toNumber(ctx); - double r; - if (std::isnan(year)) { - r = qSNaN(); - } else { - if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99)) - year += 1900; - r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); - r = UTC(MakeDate(r, TimeWithinDay(t))); - r = TimeClip(r); - } - self->value.setDouble(r); - return self->value; -} - -Value DatePrototype::method_setUTCFullYear(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double year = ctx->argument(0).toNumber(ctx); - double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); - double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setFullYear(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double year = ctx->argument(0).toNumber(ctx); - double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); - double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_toUTCString(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - return Value::fromString(ctx, ToUTCString(t)); -} - -static void addZeroPrefixedInt(QString &str, int num, int nDigits) -{ - str.resize(str.size() + nDigits); - - QChar *c = str.data() + str.size() - 1; - while (nDigits) { - *c = QChar(num % 10 + '0'); - num /= 10; - --c; - --nDigits; - } -} - -Value DatePrototype::method_toISOString(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - if (!std::isfinite(t)) - ctx->throwRangeError(ctx->thisObject); - - QString result; - int year = (int)YearFromTime(t); - if (year < 0 || year > 9999) { - if (qAbs(year) >= 1000000) - return Value::fromString(ctx, QStringLiteral("Invalid Date")); - result += year < 0 ? '-' : '+'; - year = qAbs(year); - addZeroPrefixedInt(result, year, 6); - } else { - addZeroPrefixedInt(result, year, 4); - } - result += '-'; - addZeroPrefixedInt(result, (int)MonthFromTime(t) + 1, 2); - result += '-'; - addZeroPrefixedInt(result, (int)DateFromTime(t), 2); - result += 'T'; - addZeroPrefixedInt(result, HourFromTime(t), 2); - result += ':'; - addZeroPrefixedInt(result, MinFromTime(t), 2); - result += ':'; - addZeroPrefixedInt(result, SecFromTime(t), 2); - result += '.'; - addZeroPrefixedInt(result, msFromTime(t), 3); - result += 'Z'; - - return Value::fromString(ctx, result); -} - // // RegExp object // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 571dc17b35..a2726c7fc2 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -220,70 +220,6 @@ struct FunctionPrototype: FunctionObject static Value method_bind(ExecutionContext *ctx); }; -struct DateCtor: FunctionObject -{ - DateCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct DatePrototype: DateObject -{ - DatePrototype(): DateObject(Value::fromDouble(qSNaN())) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static double getThisDate(ExecutionContext *ctx); - - static Value method_parse(ExecutionContext *ctx); - static Value method_UTC(ExecutionContext *ctx); - - static Value method_toString(ExecutionContext *ctx); - static Value method_toDateString(ExecutionContext *ctx); - static Value method_toTimeString(ExecutionContext *ctx); - static Value method_toLocaleString(ExecutionContext *ctx); - static Value method_toLocaleDateString(ExecutionContext *ctx); - static Value method_toLocaleTimeString(ExecutionContext *ctx); - static Value method_valueOf(ExecutionContext *ctx); - static Value method_getTime(ExecutionContext *ctx); - static Value method_getYear(ExecutionContext *ctx); - static Value method_getFullYear(ExecutionContext *ctx); - static Value method_getUTCFullYear(ExecutionContext *ctx); - static Value method_getMonth(ExecutionContext *ctx); - static Value method_getUTCMonth(ExecutionContext *ctx); - static Value method_getDate(ExecutionContext *ctx); - static Value method_getUTCDate(ExecutionContext *ctx); - static Value method_getDay(ExecutionContext *ctx); - static Value method_getUTCDay(ExecutionContext *ctx); - static Value method_getHours(ExecutionContext *ctx); - static Value method_getUTCHours(ExecutionContext *ctx); - static Value method_getMinutes(ExecutionContext *ctx); - static Value method_getUTCMinutes(ExecutionContext *ctx); - static Value method_getSeconds(ExecutionContext *ctx); - static Value method_getUTCSeconds(ExecutionContext *ctx); - static Value method_getMilliseconds(ExecutionContext *ctx); - static Value method_getUTCMilliseconds(ExecutionContext *ctx); - static Value method_getTimezoneOffset(ExecutionContext *ctx); - static Value method_setTime(ExecutionContext *ctx); - static Value method_setMilliseconds(ExecutionContext *ctx); - static Value method_setUTCMilliseconds(ExecutionContext *ctx); - static Value method_setSeconds(ExecutionContext *ctx); - static Value method_setUTCSeconds(ExecutionContext *ctx); - static Value method_setMinutes(ExecutionContext *ctx); - static Value method_setUTCMinutes(ExecutionContext *ctx); - static Value method_setHours(ExecutionContext *ctx); - static Value method_setUTCHours(ExecutionContext *ctx); - static Value method_setDate(ExecutionContext *ctx); - static Value method_setUTCDate(ExecutionContext *ctx); - static Value method_setMonth(ExecutionContext *ctx); - static Value method_setUTCMonth(ExecutionContext *ctx); - static Value method_setYear(ExecutionContext *ctx); - static Value method_setFullYear(ExecutionContext *ctx); - static Value method_setUTCFullYear(ExecutionContext *ctx); - static Value method_toUTCString(ExecutionContext *ctx); - static Value method_toISOString(ExecutionContext *ctx); -}; - struct RegExpCtor: FunctionObject { RegExpCtor(ExecutionContext *scope); diff --git a/v4.pro b/v4.pro index b195d50364..723f331c97 100644 --- a/v4.pro +++ b/v4.pro @@ -28,6 +28,7 @@ SOURCES += main.cpp \ qv4managed.cpp \ qv4array.cpp \ qv4argumentsobject.cpp \ + qv4dateobject.cpp \ qv4jsonobject.cpp \ qv4string.cpp \ qv4objectiterator.cpp \ @@ -52,6 +53,7 @@ HEADERS += \ qv4managed.h \ qv4array.h \ qv4argumentsobject.h \ + qv4dateobject.h \ qv4jsonobject.h \ qv4string.h \ qv4propertydescriptor.h \ -- cgit v1.2.3 From afff338ff3a5fba3dea8714ecd942117324a2727 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 12:33:59 +0100 Subject: If a function is defined twice the 2nd one should be used Change-Id: Id84663c319dd045e0f1e60b3300523a59f898b0e Reviewed-by: Simon Hausmann --- qv4codegen_p.h | 2 +- tests/TestExpectations | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/qv4codegen_p.h b/qv4codegen_p.h index e6308ac68a..f84c542f76 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -193,7 +193,7 @@ protected: m.function = function; members.insert(name, m); } else { - if ((*it).type < type) { + if ((*it).type <= type) { (*it).type = type; (*it).function = function; } diff --git a/tests/TestExpectations b/tests/TestExpectations index 39c0a49958..c2e77f5998 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -250,7 +250,6 @@ S12.6.2_A8 failing 13.0-9-s failing S13_A15_T4 failing S13_A3_T1 failing -S13_A6_T2 failing 13.1-11-s failing 13.1-12-s failing 13.1-13-s failing @@ -273,8 +272,6 @@ S13.2.2_A19_T6 failing S13.2.2_A19_T8 failing S13.2.3_A1 failing S14_A2 failing -S14_A5_T1 failing -S14_A5_T2 failing 14.1-5-s failing 15.1.1.3-3 failing S15.1.2.1_A3.2_T8 failing @@ -958,7 +955,6 @@ S15.7.4.5_A1.4_T01 failing 15.9.4.4-0-2 failing 15.9.4.4-0-3 failing 15.9.4.4-0-4 failing -S15.9.2.1_A2 failing S15.9.3.1_A5_T1 failing S15.9.3.1_A5_T2 failing S15.9.3.1_A5_T3 failing -- cgit v1.2.3 From b3dc62609f27a6933eee8621f4eac57f9b90d100 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 Jan 2013 09:20:03 +0100 Subject: Implement String.prototype.replace Change-Id: I60806b6563337c1c18a6b737e860deca4093c8ff Reviewed-by: Lars Knoll --- qv4ecmaobjects.cpp | 132 +++++++++++++++++++++++++++++++++++++++++++++++-- qv4regexp.cpp | 2 +- qv4regexp.h | 2 +- tests/TestExpectations | 49 +----------------- 4 files changed, 132 insertions(+), 53 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 933cb1840a..5024f86c50 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -793,11 +793,137 @@ Value StringPrototype::method_match(ExecutionContext *ctx) } +static QString makeReplacementString(const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) +{ + QString result; + result.reserve(replaceValue.length()); + for (int i = 0; i < replaceValue.length(); ++i) { + if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) { + char ch = replaceValue.at(++i).toLatin1(); + uint substStart = JSC::Yarr::offsetNoMatch; + uint substEnd = JSC::Yarr::offsetNoMatch; + if (ch == '$') { + result += ch; + continue; + } else if (ch == '&') { + substStart = matchOffsets[0]; + substEnd = matchOffsets[1]; + } else if (ch == '`') { + substStart = 0; + substEnd = matchOffsets[0]; + } else if (ch == '\'') { + substStart = matchOffsets[1]; + substEnd = input.length(); + } else if (ch >= '1' && ch <= '9') { + char capture = ch - '0'; + if (capture > 0 && capture < captureCount) { + substStart = matchOffsets[capture * 2]; + substEnd = matchOffsets[capture * 2 + 1]; + } + } else if (ch == '0' && i < replaceValue.length() - 1) { + int capture = (ch - '0') * 10; + ch = replaceValue.at(++i).toLatin1(); + capture += ch - '0'; + if (capture > 0 && capture < captureCount) { + substStart = matchOffsets[capture * 2]; + substEnd = matchOffsets[capture * 2 + 1]; + } + } + if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch) + result += input.midRef(substStart, substEnd - substStart); + } else { + result += replaceValue.at(i); + } + } + return result; +} + Value StringPrototype::method_replace(ExecutionContext *ctx) { - // requires Regexp - ctx->throwUnimplemented(QStringLiteral("String.prototype.replace")); - return Value::undefinedValue(); + QString string; + if (StringObject *thisString = ctx->thisObject.asStringObject()) + string = thisString->value.stringValue()->toQString(); + else + string = ctx->thisObject.toString(ctx)->toQString(); + + int numCaptures = 0; + QVarLengthArray matchOffsets; + int numStringMatches = 0; + + Value searchValue = ctx->argument(0); + RegExpObject *regExp = searchValue.asRegExpObject(); + if (regExp) { + uint offset = 0; + while (true) { + int oldSize = matchOffsets.size(); + matchOffsets.resize(matchOffsets.size() + regExp->value->captureCount() * 2); + if (regExp->value->match(string, offset, matchOffsets.data() + oldSize) == JSC::Yarr::offsetNoMatch) { + matchOffsets.resize(oldSize); + break; + } + if (!regExp->global) + break; + offset = qMax(offset + 1, matchOffsets[oldSize + 1]); + } + if (regExp->global) + regExp->lastIndexProperty->value = Value::fromUInt32(0); + numStringMatches = matchOffsets.size() / (regExp->value->captureCount() * 2); + numCaptures = regExp->value->captureCount(); + } else { + numCaptures = 1; + QString searchString = searchValue.toString(ctx)->toQString(); + int idx = string.indexOf(searchString); + if (idx != -1) { + numStringMatches = 1; + matchOffsets.resize(2); + matchOffsets[0] = idx; + matchOffsets[1] = idx + searchString.length(); + } + } + + QString result = string; + Value replaceValue = ctx->argument(1); + if (FunctionObject* searchCallback = replaceValue.asFunctionObject()) { + int replacementDelta = 0; + int argc = numCaptures + 2; + Value *args = (Value*)alloca((numCaptures + 2) * sizeof(Value)); + for (int i = 0; i < numStringMatches; ++i) { + for (int k = 0; k < numCaptures; ++k) { + int idx = (i * numCaptures + k) * 2; + uint start = matchOffsets[idx]; + uint end = matchOffsets[idx + 1]; + Value entry = Value::undefinedValue(); + if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch) + entry = Value::fromString(ctx, string.mid(start, end - start)); + args[k] = entry; + } + uint matchStart = matchOffsets[i * numCaptures * 2]; + uint matchEnd = matchOffsets[i * numCaptures * 2 + 1]; + args[numCaptures] = Value::fromUInt32(matchStart); + args[numCaptures + 1] = Value::fromString(ctx, string); + Value replacement = searchCallback->call(ctx, Value::undefinedValue(), args, argc); + QString replacementString = replacement.toString(ctx)->toQString(); + result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacementString); + replacementDelta += replacementString.length() - matchEnd + matchStart; + } + } else { + QString newString = replaceValue.toString(ctx)->toQString(); + int replacementDelta = 0; + + for (int i = 0; i < numStringMatches; ++i) { + int baseIndex = i * numCaptures * 2; + uint matchStart = matchOffsets[baseIndex]; + uint matchEnd = matchOffsets[baseIndex + 1]; + if (matchStart == JSC::Yarr::offsetNoMatch) + continue; + + QString replacement = makeReplacementString(string, newString, matchOffsets.data() + baseIndex, numCaptures); + result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacement); + replacementDelta += replacement.length() - matchEnd + matchStart; + } + } + + return Value::fromString(ctx, result); } Value StringPrototype::method_search(ExecutionContext *ctx) diff --git a/qv4regexp.cpp b/qv4regexp.cpp index 64d9fef942..bcef815e25 100644 --- a/qv4regexp.cpp +++ b/qv4regexp.cpp @@ -46,7 +46,7 @@ namespace QQmlJS { namespace VM { -int RegExp::match(const QString &string, int start, uint *matchOffsets) +uint RegExp::match(const QString &string, int start, uint *matchOffsets) { if (!isValid()) return JSC::Yarr::offsetNoMatch; diff --git a/qv4regexp.h b/qv4regexp.h index 7fd8225afb..7eae033bec 100644 --- a/qv4regexp.h +++ b/qv4regexp.h @@ -69,7 +69,7 @@ public: bool isValid() const { return m_byteCode.get(); } - int match(const QString& string, int start, uint *matchOffsets); + uint match(const QString& string, int start, uint *matchOffsets); bool ignoreCase() const { return m_ignoreCase; } bool multiLine() const { return m_multiLine; } diff --git a/tests/TestExpectations b/tests/TestExpectations index c2e77f5998..047d587ac4 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -14,12 +14,6 @@ S10.2.3_A1.3_T2 failing 10.4.2-1-3 failing 10.4.2-1-4 failing 10.4.2-1-5 failing -10.4.3-1-100-s failing -10.4.3-1-100gs failing -10.4.3-1-101-s failing -10.4.3-1-101gs failing -10.4.3-1-102-s failing -10.4.3-1-102gs failing 10.4.3-1-104 failing 10.4.3-1-106 failing 10.4.3-1-17-s failing @@ -292,13 +286,6 @@ S15.1.2.2_A9.2 failing S15.1.2.2_A9.3 failing S15.1.2.2_A9.4 failing S15.1.2.2_A9.7 failing -S15.10.2.12_A6_T1 failing -S15.10.2.12_A1_T1 failing -S15.10.2.12_A2_T1 failing -S15.10.2.12_A3_T1 failing -S15.10.2.12_A4_T1 failing -S15.10.2.12_A5_T1 failing -S15.10.2.8_A3_T18 failing S15.10.6.2_A1_T2 failing S15.10.7_A2_T1 failing S15.11.4.2_A1 failing @@ -630,41 +617,7 @@ S15.4.4.7_A3 failing S15.4.4.7_A4_T2 failing S15.4.4.7_A4_T3 failing S15.5.4.10_A1_T10 failing -15.5.4.11-1 failing -S15.5.4.11_A12 failing -S15.5.4.11_A1_T1 failing S15.5.4.11_A1_T10 failing -S15.5.4.11_A1_T11 failing -S15.5.4.11_A1_T12 failing -S15.5.4.11_A1_T13 failing -S15.5.4.11_A1_T14 failing -S15.5.4.11_A1_T15 failing -S15.5.4.11_A1_T16 failing -S15.5.4.11_A1_T17 failing -S15.5.4.11_A1_T2 failing -S15.5.4.11_A1_T4 failing -S15.5.4.11_A1_T5 failing -S15.5.4.11_A1_T6 failing -S15.5.4.11_A1_T7 failing -S15.5.4.11_A1_T8 failing -S15.5.4.11_A1_T9 failing -S15.5.4.11_A2_T1 failing -S15.5.4.11_A2_T10 failing -S15.5.4.11_A2_T2 failing -S15.5.4.11_A2_T3 failing -S15.5.4.11_A2_T4 failing -S15.5.4.11_A2_T5 failing -S15.5.4.11_A2_T6 failing -S15.5.4.11_A2_T7 failing -S15.5.4.11_A2_T8 failing -S15.5.4.11_A2_T9 failing -S15.5.4.11_A3_T1 failing -S15.5.4.11_A3_T2 failing -S15.5.4.11_A3_T3 failing -S15.5.4.11_A4_T1 failing -S15.5.4.11_A4_T2 failing -S15.5.4.11_A4_T3 failing -S15.5.4.11_A4_T4 failing S15.5.4.11_A5_T1 failing S15.5.4.12_A1.1_T1 failing S15.5.4.12_A1_T1 failing @@ -1148,4 +1101,4 @@ S15.4.4.4_A1_T2 failing 15.4.4.21-8-b-iii-1-6 failing 15.12.3_4-1-1 -15.12.3_4-1-3 \ No newline at end of file +15.12.3_4-1-3 -- cgit v1.2.3 From 685c0111b92f32a280df958f78d861fc2e38b790 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 13:27:42 +0100 Subject: Fix the remaining test failures for the Date object Fix the length property of the Date constructor add Date.now() and Date.prototype.toJSON() Change-Id: I968babfe17f6e45c8fd08f32b36eb5facc8601ca Reviewed-by: Simon Hausmann --- qv4dateobject.cpp | 38 ++++++++++++++++++++++++++++++++------ qv4dateobject.h | 2 ++ tests/TestExpectations | 28 ++++++++++------------------ 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/qv4dateobject.cpp b/qv4dateobject.cpp index d9c6f5ed6b..96d7338c28 100644 --- a/qv4dateobject.cpp +++ b/qv4dateobject.cpp @@ -277,13 +277,13 @@ static double MakeDay(double year, double month, double day) if (month < 0) month += 12.0; - double t = TimeFromYear(year); - double leap = InLeapYear(t); + double d = DayFromYear(year); + bool leap = InLeapYear(d*msPerDay); - day += ::floor(t / msPerDay); - day += DayFromMonth(month, leap); + d += DayFromMonth(month, leap); + d += day - 1; - return day - 1; + return d; } static inline double MakeDate(double day, double time) @@ -708,11 +708,12 @@ Value DateCtor::call(ExecutionContext *ctx) void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(7)); LocalTZA = getLocalTZA(); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("parse"), method_parse, 1); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("now"), method_now, 0); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); @@ -760,6 +761,7 @@ void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) defineDefaultProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); defineDefaultProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); defineDefaultProperty(ctx, QStringLiteral("toISOString"), method_toISOString, 0); + defineDefaultProperty(ctx, QStringLiteral("toJSON"), method_toJSON, 1); } double DatePrototype::getThisDate(ExecutionContext *ctx) @@ -797,6 +799,12 @@ Value DatePrototype::method_UTC(ExecutionContext *ctx) return Value::undefinedValue(); } +Value DatePrototype::method_now(ExecutionContext *ctx) +{ + double t = currentTime(); + return Value::fromDouble(t); +} + Value DatePrototype::method_toString(ExecutionContext *ctx) { double t = getThisDate(ctx); @@ -1215,6 +1223,8 @@ Value DatePrototype::method_setFullYear(ExecutionContext *ctx) ctx->throwTypeError(); double t = LocalTime(self->value.asDouble()); + if (isnan(t)) + t = 0; double year = ctx->argument(0).toNumber(ctx); double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); @@ -1283,3 +1293,19 @@ Value DatePrototype::method_toISOString(ExecutionContext *ctx) return Value::fromString(ctx, result); } + +Value DatePrototype::method_toJSON(ExecutionContext *ctx) +{ + Value O = __qmljs_to_object(ctx->thisObject, ctx); + Value tv = __qmljs_to_primitive(O, ctx, NUMBER_HINT); + + if (tv.isNumber() && !std::isfinite(tv.toNumber(ctx))) + return Value::nullValue(); + + FunctionObject *toIso = O.objectValue()->__get__(ctx, ctx->engine->identifier(QStringLiteral("toISOString"))).asFunctionObject(); + + if (!toIso) + __qmljs_throw_type_error(ctx); + + return toIso->call(ctx, ctx->thisObject, 0, 0); +} diff --git a/qv4dateobject.h b/qv4dateobject.h index 3a67c47f61..28b18b25bc 100644 --- a/qv4dateobject.h +++ b/qv4dateobject.h @@ -72,6 +72,7 @@ struct DatePrototype: DateObject static Value method_parse(ExecutionContext *ctx); static Value method_UTC(ExecutionContext *ctx); + static Value method_now(ExecutionContext *ctx); static Value method_toString(ExecutionContext *ctx); static Value method_toDateString(ExecutionContext *ctx); @@ -117,6 +118,7 @@ struct DatePrototype: DateObject static Value method_setUTCFullYear(ExecutionContext *ctx); static Value method_toUTCString(ExecutionContext *ctx); static Value method_toISOString(ExecutionContext *ctx); + static Value method_toJSON(ExecutionContext *ctx); }; } // end of namespace VM diff --git a/tests/TestExpectations b/tests/TestExpectations index 047d587ac4..ee19fde3df 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -2,6 +2,16 @@ S15.1.3.1_A2.5_T1 S15.1.3.2_A2.5_T1 +# broken test cases +# The test data in the 6 tests below is wrong. V8 agrees with our results and the test suite is 0.375 days off +S15.9.3.1_A5_T1 failing +S15.9.3.1_A5_T2 failing +S15.9.3.1_A5_T3 failing +S15.9.3.1_A5_T4 failing +S15.9.3.1_A5_T5 failing +S15.9.3.1_A5_T6 failing + + # Tests failing that are supposed to pass. 15.4.4.15-5-16 failing 15.4.4.15-5-12 failing @@ -508,7 +518,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-11 failing 15.2.3.3-4-12 failing 15.2.3.3-4-13 failing -15.2.3.3-4-162 failing 15.2.3.3-4-164 failing 15.2.3.3-4-51 failing 15.2.3.3-4-82 failing @@ -529,8 +538,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-360-3 failing 15.2.3.6-4-360-7 failing 15.2.3.6-4-621 failing -15.2.3.6-4-622 failing -15.2.3.6-4-624 failing 15.2.3.7-6-a-110 failing 15.2.3.7-6-a-280 failing 15.2.3.7-6-a-286 failing @@ -904,19 +911,6 @@ S15.7.4_A3.3 failing S15.7.4.5_A1.3_T01 failing S15.7.4.5_A1.3_T02 failing S15.7.4.5_A1.4_T01 failing -15.9.4.4-0-1 failing -15.9.4.4-0-2 failing -15.9.4.4-0-3 failing -15.9.4.4-0-4 failing -S15.9.3.1_A5_T1 failing -S15.9.3.1_A5_T2 failing -S15.9.3.1_A5_T3 failing -S15.9.3.1_A5_T4 failing -S15.9.3.1_A5_T5 failing -S15.9.3.1_A5_T6 failing -S15.9.4_A5 failing -S15.9.5.1_A2_T1 failing -15.9.5.40_1 failing 6.4_c failing 8.0_L15 failing 9.1_a failing @@ -1036,8 +1030,6 @@ S15.9.5.1_A2_T1 failing 13.3.0_2 failing 13.3.0_6 failing 13.3.0_7 failing -15.9.5.44-0-1 failing -15.9.5.44-0-2 failing 6.2.2_a failing 6.2.2_b failing 6.2.2_c failing -- cgit v1.2.3 From ba8676b15f8afc28e9cc2a0fff9579bc9d1ac2df Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 13:46:39 +0100 Subject: Implement String.prototype.trim Change-Id: I6bbe52f77221a0e5bd8d9f275983a0513c7c50e0 Reviewed-by: Simon Hausmann --- qv4ecmaobjects.cpp | 21 ++++++++ qv4ecmaobjects_p.h | 1 + tests/TestExpectations | 127 +------------------------------------------------ 3 files changed, 23 insertions(+), 126 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 5024f86c50..8ce8bcee48 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -615,6 +615,7 @@ void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) defineDefaultProperty(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); defineDefaultProperty(ctx, QStringLiteral("toUpperCase"), method_toUpperCase); defineDefaultProperty(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); + defineDefaultProperty(ctx, QStringLiteral("trim"), method_trim); } QString StringPrototype::getThisString(ExecutionContext *ctx) @@ -1054,6 +1055,26 @@ Value StringPrototype::method_fromCharCode(ExecutionContext *ctx) return Value::fromString(ctx, str); } +Value StringPrototype::method_trim(ExecutionContext *ctx) +{ + if (ctx->thisObject.isNull() || ctx->thisObject.isUndefined()) + __qmljs_throw_type_error(ctx); + + QString s = __qmljs_to_string(ctx->thisObject, ctx).stringValue()->toQString(); + const QChar *chars = s.constData(); + int start, end; + for (start = 0; start < s.length(); ++start) { + if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff) + break; + } + for (end = s.length() - 1; end >= start; --end) { + if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff) + break; + } + + return Value::fromString(ctx, QString(chars + start, end - start + 1)); +} + // // Number object // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index a2726c7fc2..aaef393141 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -122,6 +122,7 @@ struct StringPrototype: StringObject static Value method_toUpperCase(ExecutionContext *ctx); static Value method_toLocaleUpperCase(ExecutionContext *ctx); static Value method_fromCharCode(ExecutionContext *ctx); + static Value method_trim(ExecutionContext *ctx); }; struct NumberCtor: FunctionObject diff --git a/tests/TestExpectations b/tests/TestExpectations index ee19fde3df..87f9f96a67 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -520,7 +520,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-13 failing 15.2.3.3-4-164 failing 15.2.3.3-4-51 failing -15.2.3.3-4-82 failing 15.2.3.3-4-9 failing 15.2.3.3-4-90 failing 15.2.3.6-2-17-1 failing @@ -537,7 +536,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-300-1 failing 15.2.3.6-4-360-3 failing 15.2.3.6-4-360-7 failing -15.2.3.6-4-621 failing 15.2.3.7-6-a-110 failing 15.2.3.7-6-a-280 failing 15.2.3.7-6-a-286 failing @@ -775,134 +773,11 @@ S15.5.4.15_A1_T2 failing S15.5.4.15_A1_T7 failing S15.5.4.15_A1_T8 failing S15.5.4.15_A1_T9 failing -15.5.4.20-0-1 failing -15.5.4.20-0-2 failing -15.5.4.20-1-3 failing -15.5.4.20-1-4 failing -15.5.4.20-1-5 failing -15.5.4.20-1-6 failing -15.5.4.20-1-7 failing -15.5.4.20-1-8 failing -15.5.4.20-1-9 failing -15.5.4.20-2-1 failing -15.5.4.20-2-10 failing -15.5.4.20-2-11 failing -15.5.4.20-2-12 failing -15.5.4.20-2-13 failing -15.5.4.20-2-14 failing -15.5.4.20-2-15 failing -15.5.4.20-2-16 failing -15.5.4.20-2-17 failing -15.5.4.20-2-18 failing -15.5.4.20-2-19 failing -15.5.4.20-2-2 failing -15.5.4.20-2-20 failing -15.5.4.20-2-21 failing -15.5.4.20-2-22 failing -15.5.4.20-2-23 failing -15.5.4.20-2-24 failing -15.5.4.20-2-25 failing -15.5.4.20-2-26 failing -15.5.4.20-2-27 failing -15.5.4.20-2-28 failing -15.5.4.20-2-29 failing -15.5.4.20-2-3 failing -15.5.4.20-2-30 failing -15.5.4.20-2-31 failing -15.5.4.20-2-32 failing -15.5.4.20-2-33 failing -15.5.4.20-2-34 failing -15.5.4.20-2-35 failing -15.5.4.20-2-36 failing -15.5.4.20-2-37 failing -15.5.4.20-2-38 failing -15.5.4.20-2-39 failing -15.5.4.20-2-4 failing -15.5.4.20-2-40 failing -15.5.4.20-2-41 failing -15.5.4.20-2-42 failing -15.5.4.20-2-43 failing -15.5.4.20-2-44 failing -15.5.4.20-2-45 failing -15.5.4.20-2-46 failing -15.5.4.20-2-47 failing -15.5.4.20-2-49 failing -15.5.4.20-2-5 failing -15.5.4.20-2-50 failing -15.5.4.20-2-51 failing -15.5.4.20-2-6 failing -15.5.4.20-2-7 failing -15.5.4.20-2-8 failing -15.5.4.20-2-9 failing -15.5.4.20-3-1 failing -15.5.4.20-3-10 failing -15.5.4.20-3-11 failing -15.5.4.20-3-12 failing -15.5.4.20-3-13 failing -15.5.4.20-3-14 failing -15.5.4.20-3-2 failing -15.5.4.20-3-3 failing -15.5.4.20-3-4 failing -15.5.4.20-3-5 failing -15.5.4.20-3-6 failing -15.5.4.20-3-7 failing -15.5.4.20-3-8 failing -15.5.4.20-3-9 failing 15.5.4.20-4-1 failing S15.5.4.5_A1_T10 failing S15.5.4.6_A1_T10 failing S15.5.4.7_A1_T10 failing S15.5.4.7_A1_T11 failing -15.5.4.20-4-10 failing -15.5.4.20-4-11 failing -15.5.4.20-4-12 failing -15.5.4.20-4-13 failing -15.5.4.20-4-14 failing -15.5.4.20-4-16 failing -15.5.4.20-4-18 failing -15.5.4.20-4-19 failing -15.5.4.20-4-2 failing -15.5.4.20-4-20 failing -15.5.4.20-4-21 failing -15.5.4.20-4-22 failing -15.5.4.20-4-24 failing -15.5.4.20-4-27 failing -15.5.4.20-4-28 failing -15.5.4.20-4-29 failing -15.5.4.20-4-3 failing -15.5.4.20-4-30 failing -15.5.4.20-4-32 failing -15.5.4.20-4-34 failing -15.5.4.20-4-35 failing -15.5.4.20-4-36 failing -15.5.4.20-4-37 failing -15.5.4.20-4-38 failing -15.5.4.20-4-39 failing -15.5.4.20-4-4 failing -15.5.4.20-4-40 failing -15.5.4.20-4-41 failing -15.5.4.20-4-42 failing -15.5.4.20-4-43 failing -15.5.4.20-4-44 failing -15.5.4.20-4-45 failing -15.5.4.20-4-46 failing -15.5.4.20-4-47 failing -15.5.4.20-4-48 failing -15.5.4.20-4-49 failing -15.5.4.20-4-5 failing -15.5.4.20-4-50 failing -15.5.4.20-4-51 failing -15.5.4.20-4-52 failing -15.5.4.20-4-53 failing -15.5.4.20-4-54 failing -15.5.4.20-4-55 failing -15.5.4.20-4-56 failing -15.5.4.20-4-57 failing -15.5.4.20-4-58 failing -15.5.4.20-4-59 failing -15.5.4.20-4-6 failing -15.5.4.20-4-60 failing -15.5.4.20-4-8 failing S15.5.4.4_A1_T10 failing S15.5.4.8_A1_T10 failing S15.5.4.8_A1_T12 failing @@ -1093,4 +968,4 @@ S15.4.4.4_A1_T2 failing 15.4.4.21-8-b-iii-1-6 failing 15.12.3_4-1-1 -15.12.3_4-1-3 +15.12.3_4-1-3 \ No newline at end of file -- cgit v1.2.3 From 419ed1be5afce1d507916e6f52656c35a67cfe8a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 Jan 2013 13:46:44 +0100 Subject: Implement String.prototype.search Change-Id: Id151161bda4ab5012af33a49fac11a9b7e82ec0e Reviewed-by: Lars Knoll --- qv4ecmaobjects.cpp | 20 +++++++++++++++++--- tests/TestExpectations | 24 +----------------------- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 8ce8bcee48..e330f7db00 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -929,9 +929,23 @@ Value StringPrototype::method_replace(ExecutionContext *ctx) Value StringPrototype::method_search(ExecutionContext *ctx) { - // requires Regexp - ctx->throwUnimplemented(QStringLiteral("String.prototype.search")); - return Value::undefinedValue(); + QString string; + if (StringObject *thisString = ctx->thisObject.asStringObject()) + string = thisString->value.stringValue()->toQString(); + else + string = ctx->thisObject.toString(ctx)->toQString(); + + Value regExpValue = ctx->argument(0); + RegExpObject *regExp = regExpValue.asRegExpObject(); + if (!regExp) { + regExpValue = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ®ExpValue, 1); + regExp = regExpValue.asRegExpObject(); + } + uint* matchOffsets = (uint*)alloca(regExp->value->captureCount() * 2 * sizeof(uint)); + uint result = regExp->value->match(string, /*offset*/0, matchOffsets); + if (result == JSC::Yarr::offsetNoMatch) + return Value::fromInt32(-1); + return Value::fromUInt32(result); } Value StringPrototype::method_slice(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index 87f9f96a67..fd6a56602b 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -624,29 +624,7 @@ S15.4.4.7_A4_T3 failing S15.5.4.10_A1_T10 failing S15.5.4.11_A1_T10 failing S15.5.4.11_A5_T1 failing -S15.5.4.12_A1.1_T1 failing -S15.5.4.12_A1_T1 failing S15.5.4.12_A1_T10 failing -S15.5.4.12_A1_T11 failing -S15.5.4.12_A1_T12 failing -S15.5.4.12_A1_T13 failing -S15.5.4.12_A1_T14 failing -S15.5.4.12_A1_T2 failing -S15.5.4.12_A1_T4 failing -S15.5.4.12_A1_T5 failing -S15.5.4.12_A1_T6 failing -S15.5.4.12_A1_T7 failing -S15.5.4.12_A1_T8 failing -S15.5.4.12_A1_T9 failing -S15.5.4.12_A2_T1 failing -S15.5.4.12_A2_T2 failing -S15.5.4.12_A2_T3 failing -S15.5.4.12_A2_T4 failing -S15.5.4.12_A2_T5 failing -S15.5.4.12_A2_T6 failing -S15.5.4.12_A2_T7 failing -S15.5.4.12_A3_T1 failing -S15.5.4.12_A3_T2 failing S15.5.4.13_A1_T10 failing S15.4.4.8_A1_T1 failing S15.4.4.8_A1_T2 failing @@ -968,4 +946,4 @@ S15.4.4.4_A1_T2 failing 15.4.4.21-8-b-iii-1-6 failing 15.12.3_4-1-1 -15.12.3_4-1-3 \ No newline at end of file +15.12.3_4-1-3 -- cgit v1.2.3 From 32a914ab001050be52aed7b377394bf96b0bebfd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 Jan 2013 14:07:48 +0100 Subject: Fix certain new member expressions Non-temp/name/member expressions need an intermediate move/temporary, in visit(NewExpression *ast) as well as in visit(NewMemberExpression *ast) Change-Id: I2f367c6285bb98d8445b93f924979431f1e5ff11 Reviewed-by: Lars Knoll --- qv4codegen.cpp | 9 ++++++++- tests/TestExpectations | 3 --- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 7461aaa616..41d1d6b1e4 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1349,6 +1349,13 @@ bool Codegen::visit(NewExpression *ast) bool Codegen::visit(NewMemberExpression *ast) { Result base = expression(ast->base); + IR::Expr *expr = *base; + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + IR::ExprList *args = 0, **args_it = &args; for (ArgumentList *it = ast->arguments; it; it = it->next) { Result arg = expression(it->expression); @@ -1358,7 +1365,7 @@ bool Codegen::visit(NewMemberExpression *ast) args_it = &(*args_it)->next; } const unsigned t = _block->newTemp(); - move(_block->TEMP(t), _block->NEW(*base, args)); + move(_block->TEMP(t), _block->NEW(expr, args)); _expr.code = _block->TEMP(t); return false; } diff --git a/tests/TestExpectations b/tests/TestExpectations index fd6a56602b..e85eecf078 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -266,8 +266,6 @@ S13_A3_T1 failing 13.1-40-s failing 13.1-41-s failing 13.1-42-s failing -S13.2.2_A16_T2 failing -S13.2.2_A16_T3 failing S13.2.2_A19_T1 failing S13.2.2_A19_T3 failing S13.2.2_A19_T4 failing @@ -297,7 +295,6 @@ S15.1.2.2_A9.3 failing S15.1.2.2_A9.4 failing S15.1.2.2_A9.7 failing S15.10.6.2_A1_T2 failing -S15.10.7_A2_T1 failing S15.11.4.2_A1 failing S15.11.4.2_A2 failing 15.12.1.1-g6-3 failing -- cgit v1.2.3 From 11bfd4e06221733f8fa2d80a22b4024ad946b7ff Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 14:20:19 +0100 Subject: Move the String object into it's own file Change-Id: I3efc9aeaaa7c851715a996e5cbc149fa87cf5503 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 1 + qmljs_objects.cpp | 26 +-- qmljs_objects.h | 10 - qv4dateobject.cpp | 1 + qv4dateobject.h | 1 - qv4ecmaobjects.cpp | 527 ------------------------------------------ qv4ecmaobjects_p.h | 38 --- qv4objectiterator.cpp | 1 + qv4stringobject.cpp | 626 ++++++++++++++++++++++++++++++++++++++++++++++++++ qv4stringobject.h | 101 ++++++++ v4.pro | 2 + 11 files changed, 733 insertions(+), 601 deletions(-) create mode 100644 qv4stringobject.cpp create mode 100644 qv4stringobject.h diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index a6ee9ab6c8..136711aef7 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -46,6 +46,7 @@ #include #include #include +#include namespace QQmlJS { namespace VM { diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index cf24c00b1a..69c5d96382 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -43,6 +43,7 @@ #include "qv4ir_p.h" #include "qv4isel_p.h" #include "qv4ecmaobjects_p.h" +#include "qv4stringobject.h" #include "qv4mm.h" #include @@ -1227,31 +1228,6 @@ void BoundFunction::getCollectables(QVector &objects) } -StringObject::StringObject(ExecutionContext *ctx, const Value &value) - : value(value) -{ - isString = true; - - tmpProperty.type = PropertyDescriptor::Data; - tmpProperty.enumberable = PropertyDescriptor::Enabled; - tmpProperty.writable = PropertyDescriptor::Disabled; - tmpProperty.configurable = PropertyDescriptor::Disabled; - tmpProperty.value = Value::undefinedValue(); - - assert(value.isString()); - defineReadonlyProperty(ctx->engine->id_length, Value::fromUInt32(value.stringValue()->toQString().length())); -} - -PropertyDescriptor *StringObject::getIndex(ExecutionContext *ctx, uint index) -{ - QString str = value.stringValue()->toQString(); - if (index >= (uint)str.length()) - return 0; - String *result = ctx->engine->newString(str.mid(index, 1)); - tmpProperty.value = Value::fromString(result); - return &tmpProperty; -} - EvalErrorObject::EvalErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->engine, ctx->argument(0)) { diff --git a/qmljs_objects.h b/qmljs_objects.h index 91b1241fb2..429cc0a91b 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -195,16 +195,6 @@ struct NumberObject: Object { virtual NumberObject *asNumberObject() { return this; } }; -struct StringObject: Object { - Value value; - PropertyDescriptor tmpProperty; - StringObject(ExecutionContext *ctx, const Value &value); - virtual QString className() { return QStringLiteral("String"); } - virtual StringObject *asStringObject() { return this; } - - PropertyDescriptor *getIndex(ExecutionContext *ctx, uint index); -}; - struct ArrayObject: Object { ArrayObject(ExecutionContext *ctx) { init(ctx); } ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setLengthUnchecked(array.length()); } diff --git a/qv4dateobject.cpp b/qv4dateobject.cpp index 96d7338c28..d9a40c9173 100644 --- a/qv4dateobject.cpp +++ b/qv4dateobject.cpp @@ -41,6 +41,7 @@ #include "qv4dateobject.h" +#include "qv4ecmaobjects_p.h" #include "qv4mm.h" #include #include diff --git a/qv4dateobject.h b/qv4dateobject.h index 28b18b25bc..a0fba20e59 100644 --- a/qv4dateobject.h +++ b/qv4dateobject.h @@ -42,7 +42,6 @@ #define QV4DATEOBJECT_P_H #include "qmljs_objects.h" -#include "qv4ecmaobjects_p.h" #include namespace QQmlJS { diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index e330f7db00..80f27b4ee5 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -561,533 +561,6 @@ Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Prope return Value::fromObject(o); } -// -// String -// -StringCtor::StringCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value StringCtor::construct(ExecutionContext *ctx) -{ - Value value; - if (ctx->argumentCount) - value = Value::fromString(ctx->argument(0).toString(ctx)); - else - value = Value::fromString(ctx, QString()); - return Value::fromObject(ctx->engine->newStringObject(ctx, value)); -} - -Value StringCtor::call(ExecutionContext *ctx) -{ - Value value; - if (ctx->argumentCount) - value = Value::fromString(ctx->argument(0).toString(ctx)); - else - value = Value::fromString(ctx, QString()); - return value; -} - -void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode, 1); - - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); - defineDefaultProperty(ctx, QStringLiteral("charAt"), method_charAt, 1); - defineDefaultProperty(ctx, QStringLiteral("charCodeAt"), method_charCodeAt, 1); - defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); - defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); - defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); - defineDefaultProperty(ctx, QStringLiteral("localeCompare"), method_localeCompare, 1); - defineDefaultProperty(ctx, QStringLiteral("match"), method_match, 1); - defineDefaultProperty(ctx, QStringLiteral("replace"), method_replace, 2); - defineDefaultProperty(ctx, QStringLiteral("search"), method_search, 1); - defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); - defineDefaultProperty(ctx, QStringLiteral("split"), method_split, 2); - defineDefaultProperty(ctx, QStringLiteral("substr"), method_substr, 2); - defineDefaultProperty(ctx, QStringLiteral("substring"), method_substring, 2); - defineDefaultProperty(ctx, QStringLiteral("toLowerCase"), method_toLowerCase); - defineDefaultProperty(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); - defineDefaultProperty(ctx, QStringLiteral("toUpperCase"), method_toUpperCase); - defineDefaultProperty(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); - defineDefaultProperty(ctx, QStringLiteral("trim"), method_trim); -} - -QString StringPrototype::getThisString(ExecutionContext *ctx) -{ - String* str = 0; - Value thisObject = ctx->thisObject; - if (StringObject *thisString = thisObject.asStringObject()) - str = thisString->value.stringValue(); - else if (thisObject.isUndefined() || thisObject.isNull()) - ctx->throwTypeError(); - else - str = ctx->thisObject.toString(ctx); - return str->toQString(); -} - -Value StringPrototype::method_toString(ExecutionContext *ctx) -{ - StringObject *o = ctx->thisObject.asStringObject(); - if (!o) - ctx->throwTypeError(); - return o->value; -} - -Value StringPrototype::method_valueOf(ExecutionContext *ctx) -{ - StringObject *o = ctx->thisObject.asStringObject(); - if (!o) - ctx->throwTypeError(); - return o->value; -} - -Value StringPrototype::method_charAt(ExecutionContext *ctx) -{ - const QString str = getThisString(ctx); - - int pos = 0; - if (ctx->argumentCount > 0) - pos = (int) ctx->argument(0).toInteger(ctx); - - QString result; - if (pos >= 0 && pos < str.length()) - result += str.at(pos); - - return Value::fromString(ctx, result); -} - -Value StringPrototype::method_charCodeAt(ExecutionContext *ctx) -{ - const QString str = getThisString(ctx); - - int pos = 0; - if (ctx->argumentCount > 0) - pos = (int) ctx->argument(0).toInteger(ctx); - - double result = qSNaN(); - - if (pos >= 0 && pos < str.length()) - result = str.at(pos).unicode(); - - return Value::fromDouble(result); -} - -Value StringPrototype::method_concat(ExecutionContext *ctx) -{ - QString value = getThisString(ctx); - - for (unsigned i = 0; i < ctx->argumentCount; ++i) { - Value v = __qmljs_to_string(ctx->argument(i), ctx); - assert(v.isString()); - value += v.stringValue()->toQString(); - } - - return Value::fromString(ctx, value); -} - -Value StringPrototype::method_indexOf(ExecutionContext *ctx) -{ - QString value = getThisString(ctx); - - QString searchString; - if (ctx->argumentCount) - searchString = ctx->argument(0).toString(ctx)->toQString(); - - int pos = 0; - if (ctx->argumentCount > 1) - pos = (int) ctx->argument(1).toInteger(ctx); - - int index = -1; - if (! value.isEmpty()) - index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); - - return Value::fromDouble(index); -} - -Value StringPrototype::method_lastIndexOf(ExecutionContext *ctx) -{ - const QString value = getThisString(ctx); - - QString searchString; - if (ctx->argumentCount) { - Value v = __qmljs_to_string(ctx->argument(0), ctx); - searchString = v.stringValue()->toQString(); - } - - Value posArg = ctx->argument(1); - double position = __qmljs_to_number(posArg, ctx); - if (std::isnan(position)) - position = +qInf(); - else - position = trunc(position); - - int pos = trunc(qMin(qMax(position, 0.0), double(value.length()))); - if (!searchString.isEmpty() && pos == value.length()) - --pos; - int index = value.lastIndexOf(searchString, pos); - return Value::fromDouble(index); -} - -Value StringPrototype::method_localeCompare(ExecutionContext *ctx) -{ - const QString value = getThisString(ctx); - const QString that = ctx->argument(0).toString(ctx)->toQString(); - return Value::fromDouble(QString::localeAwareCompare(value, that)); -} - -Value StringPrototype::method_match(ExecutionContext *ctx) -{ - if (ctx->thisObject.isUndefined() || ctx->thisObject.isNull()) - __qmljs_throw_type_error(ctx); - - String *s = ctx->thisObject.toString(ctx); - - Value regexp = ctx->argument(0); - RegExpObject *rx = regexp.asRegExpObject(); - if (!rx) - rx = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ®exp, 1).asRegExpObject(); - - if (!rx) - // ### CHECK - __qmljs_throw_type_error(ctx); - - bool global = rx->global; - - FunctionObject *exec = ctx->engine->regExpPrototype->__get__(ctx, ctx->engine->identifier(QStringLiteral("exec")), 0).asFunctionObject(); - - Value arg = Value::fromString(s); - if (!global) - return exec->call(ctx, Value::fromObject(rx), &arg, 1); - - String *lastIndex = ctx->engine->identifier(QStringLiteral("lastIndex")); - rx->__put__(ctx, lastIndex, Value::fromDouble(0.)); - ArrayObject *a = ctx->engine->newArrayObject(ctx); - - double previousLastIndex = 0; - uint n = 0; - while (1) { - Value result = exec->call(ctx, Value::fromObject(rx), &arg, 1); - if (result.isNull()) - break; - assert(result.isObject()); - double thisIndex = rx->__get__(ctx, lastIndex, 0).toInteger(ctx); - if (previousLastIndex == thisIndex) { - previousLastIndex = thisIndex + 1; - rx->__put__(ctx, lastIndex, Value::fromDouble(previousLastIndex)); - } else { - previousLastIndex = thisIndex; - } - Value matchStr = result.objectValue()->__get__(ctx, (uint)0, (bool *)0); - a->array.set(n, matchStr); - ++n; - } - if (!n) - return Value::nullValue(); - - return Value::fromObject(a); - -} - -static QString makeReplacementString(const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) -{ - QString result; - result.reserve(replaceValue.length()); - for (int i = 0; i < replaceValue.length(); ++i) { - if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) { - char ch = replaceValue.at(++i).toLatin1(); - uint substStart = JSC::Yarr::offsetNoMatch; - uint substEnd = JSC::Yarr::offsetNoMatch; - if (ch == '$') { - result += ch; - continue; - } else if (ch == '&') { - substStart = matchOffsets[0]; - substEnd = matchOffsets[1]; - } else if (ch == '`') { - substStart = 0; - substEnd = matchOffsets[0]; - } else if (ch == '\'') { - substStart = matchOffsets[1]; - substEnd = input.length(); - } else if (ch >= '1' && ch <= '9') { - char capture = ch - '0'; - if (capture > 0 && capture < captureCount) { - substStart = matchOffsets[capture * 2]; - substEnd = matchOffsets[capture * 2 + 1]; - } - } else if (ch == '0' && i < replaceValue.length() - 1) { - int capture = (ch - '0') * 10; - ch = replaceValue.at(++i).toLatin1(); - capture += ch - '0'; - if (capture > 0 && capture < captureCount) { - substStart = matchOffsets[capture * 2]; - substEnd = matchOffsets[capture * 2 + 1]; - } - } - if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch) - result += input.midRef(substStart, substEnd - substStart); - } else { - result += replaceValue.at(i); - } - } - return result; -} - -Value StringPrototype::method_replace(ExecutionContext *ctx) -{ - QString string; - if (StringObject *thisString = ctx->thisObject.asStringObject()) - string = thisString->value.stringValue()->toQString(); - else - string = ctx->thisObject.toString(ctx)->toQString(); - - int numCaptures = 0; - QVarLengthArray matchOffsets; - int numStringMatches = 0; - - Value searchValue = ctx->argument(0); - RegExpObject *regExp = searchValue.asRegExpObject(); - if (regExp) { - uint offset = 0; - while (true) { - int oldSize = matchOffsets.size(); - matchOffsets.resize(matchOffsets.size() + regExp->value->captureCount() * 2); - if (regExp->value->match(string, offset, matchOffsets.data() + oldSize) == JSC::Yarr::offsetNoMatch) { - matchOffsets.resize(oldSize); - break; - } - if (!regExp->global) - break; - offset = qMax(offset + 1, matchOffsets[oldSize + 1]); - } - if (regExp->global) - regExp->lastIndexProperty->value = Value::fromUInt32(0); - numStringMatches = matchOffsets.size() / (regExp->value->captureCount() * 2); - numCaptures = regExp->value->captureCount(); - } else { - numCaptures = 1; - QString searchString = searchValue.toString(ctx)->toQString(); - int idx = string.indexOf(searchString); - if (idx != -1) { - numStringMatches = 1; - matchOffsets.resize(2); - matchOffsets[0] = idx; - matchOffsets[1] = idx + searchString.length(); - } - } - - QString result = string; - Value replaceValue = ctx->argument(1); - if (FunctionObject* searchCallback = replaceValue.asFunctionObject()) { - int replacementDelta = 0; - int argc = numCaptures + 2; - Value *args = (Value*)alloca((numCaptures + 2) * sizeof(Value)); - for (int i = 0; i < numStringMatches; ++i) { - for (int k = 0; k < numCaptures; ++k) { - int idx = (i * numCaptures + k) * 2; - uint start = matchOffsets[idx]; - uint end = matchOffsets[idx + 1]; - Value entry = Value::undefinedValue(); - if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch) - entry = Value::fromString(ctx, string.mid(start, end - start)); - args[k] = entry; - } - uint matchStart = matchOffsets[i * numCaptures * 2]; - uint matchEnd = matchOffsets[i * numCaptures * 2 + 1]; - args[numCaptures] = Value::fromUInt32(matchStart); - args[numCaptures + 1] = Value::fromString(ctx, string); - Value replacement = searchCallback->call(ctx, Value::undefinedValue(), args, argc); - QString replacementString = replacement.toString(ctx)->toQString(); - result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacementString); - replacementDelta += replacementString.length() - matchEnd + matchStart; - } - } else { - QString newString = replaceValue.toString(ctx)->toQString(); - int replacementDelta = 0; - - for (int i = 0; i < numStringMatches; ++i) { - int baseIndex = i * numCaptures * 2; - uint matchStart = matchOffsets[baseIndex]; - uint matchEnd = matchOffsets[baseIndex + 1]; - if (matchStart == JSC::Yarr::offsetNoMatch) - continue; - - QString replacement = makeReplacementString(string, newString, matchOffsets.data() + baseIndex, numCaptures); - result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacement); - replacementDelta += replacement.length() - matchEnd + matchStart; - } - } - - return Value::fromString(ctx, result); -} - -Value StringPrototype::method_search(ExecutionContext *ctx) -{ - QString string; - if (StringObject *thisString = ctx->thisObject.asStringObject()) - string = thisString->value.stringValue()->toQString(); - else - string = ctx->thisObject.toString(ctx)->toQString(); - - Value regExpValue = ctx->argument(0); - RegExpObject *regExp = regExpValue.asRegExpObject(); - if (!regExp) { - regExpValue = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ®ExpValue, 1); - regExp = regExpValue.asRegExpObject(); - } - uint* matchOffsets = (uint*)alloca(regExp->value->captureCount() * 2 * sizeof(uint)); - uint result = regExp->value->match(string, /*offset*/0, matchOffsets); - if (result == JSC::Yarr::offsetNoMatch) - return Value::fromInt32(-1); - return Value::fromUInt32(result); -} - -Value StringPrototype::method_slice(ExecutionContext *ctx) -{ - const QString text = getThisString(ctx); - const int length = text.length(); - - int start = int (ctx->argument(0).toInteger(ctx)); - int end = ctx->argument(1).isUndefined() - ? length : int (ctx->argument(1).toInteger(ctx)); - - if (start < 0) - start = qMax(length + start, 0); - else - start = qMin(start, length); - - if (end < 0) - end = qMax(length + end, 0); - else - end = qMin(end, length); - - int count = qMax(0, end - start); - return Value::fromString(ctx, text.mid(start, count)); -} - -Value StringPrototype::method_split(ExecutionContext *ctx) -{ - ctx->throwUnimplemented(QStringLiteral("String.prototype.splt")); - return Value::undefinedValue(); -} - -Value StringPrototype::method_substr(ExecutionContext *ctx) -{ - const QString value = getThisString(ctx); - - double start = 0; - if (ctx->argumentCount > 0) - start = ctx->argument(0).toInteger(ctx); - - double length = +qInf(); - if (ctx->argumentCount > 1) - length = ctx->argument(1).toInteger(ctx); - - double count = value.length(); - if (start < 0) - start = qMax(count + start, 0.0); - - length = qMin(qMax(length, 0.0), count - start); - - qint32 x = Value::toInt32(start); - qint32 y = Value::toInt32(length); - return Value::fromString(ctx, value.mid(x, y)); -} - -Value StringPrototype::method_substring(ExecutionContext *ctx) -{ - QString value = getThisString(ctx); - int length = value.length(); - - double start = 0; - double end = length; - - if (ctx->argumentCount > 0) - start = ctx->argument(0).toInteger(ctx); - - if (ctx->argumentCount > 1) - end = ctx->argument(1).toInteger(ctx); - - if (std::isnan(start) || start < 0) - start = 0; - - if (std::isnan(end) || end < 0) - end = 0; - - if (start > length) - start = length; - - if (end > length) - end = length; - - if (start > end) { - double was = start; - start = end; - end = was; - } - - qint32 x = Value::toInt32(start); - qint32 y = Value::toInt32(end - start); - return Value::fromString(ctx, value.mid(x, y)); -} - -Value StringPrototype::method_toLowerCase(ExecutionContext *ctx) -{ - QString value = getThisString(ctx); - return Value::fromString(ctx, value.toLower()); -} - -Value StringPrototype::method_toLocaleLowerCase(ExecutionContext *ctx) -{ - return method_toLowerCase(ctx); -} - -Value StringPrototype::method_toUpperCase(ExecutionContext *ctx) -{ - QString value = getThisString(ctx); - return Value::fromString(ctx, value.toUpper()); -} - -Value StringPrototype::method_toLocaleUpperCase(ExecutionContext *ctx) -{ - return method_toUpperCase(ctx); -} - -Value StringPrototype::method_fromCharCode(ExecutionContext *ctx) -{ - QString str; - for (unsigned i = 0; i < ctx->argumentCount; ++i) { - QChar c(ctx->argument(i).toUInt16(ctx)); - str += c; - } - return Value::fromString(ctx, str); -} - -Value StringPrototype::method_trim(ExecutionContext *ctx) -{ - if (ctx->thisObject.isNull() || ctx->thisObject.isUndefined()) - __qmljs_throw_type_error(ctx); - - QString s = __qmljs_to_string(ctx->thisObject, ctx).stringValue()->toQString(); - const QChar *chars = s.constData(); - int start, end; - for (start = 0; start < s.length(); ++start) { - if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff) - break; - } - for (end = s.length() - 1; end >= start; --end) { - if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff) - break; - } - - return Value::fromString(ctx, QString(chars + start, end - start + 1)); -} // // Number object diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index aaef393141..46f6140b1f 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -87,44 +87,6 @@ struct ObjectPrototype: Object static Value fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc); }; -struct StringCtor: FunctionObject -{ - StringCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct StringPrototype: StringObject -{ - StringPrototype(ExecutionContext *ctx): StringObject(ctx, Value::fromString(ctx, QString())) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static QString getThisString(ExecutionContext *ctx); - - static Value method_toString(ExecutionContext *ctx); - static Value method_valueOf(ExecutionContext *ctx); - static Value method_charAt(ExecutionContext *ctx); - static Value method_charCodeAt(ExecutionContext *ctx); - static Value method_concat(ExecutionContext *ctx); - static Value method_indexOf(ExecutionContext *ctx); - static Value method_lastIndexOf(ExecutionContext *ctx); - static Value method_localeCompare(ExecutionContext *ctx); - static Value method_match(ExecutionContext *ctx); - static Value method_replace(ExecutionContext *ctx); - static Value method_search(ExecutionContext *ctx); - static Value method_slice(ExecutionContext *ctx); - static Value method_split(ExecutionContext *ctx); - static Value method_substr(ExecutionContext *ctx); - static Value method_substring(ExecutionContext *ctx); - static Value method_toLowerCase(ExecutionContext *ctx); - static Value method_toLocaleLowerCase(ExecutionContext *ctx); - static Value method_toUpperCase(ExecutionContext *ctx); - static Value method_toLocaleUpperCase(ExecutionContext *ctx); - static Value method_fromCharCode(ExecutionContext *ctx); - static Value method_trim(ExecutionContext *ctx); -}; - struct NumberCtor: FunctionObject { NumberCtor(ExecutionContext *scope); diff --git a/qv4objectiterator.cpp b/qv4objectiterator.cpp index f324b1df62..271a3c59b8 100644 --- a/qv4objectiterator.cpp +++ b/qv4objectiterator.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qv4objectiterator.h" #include "qmljs_objects.h" +#include "qv4stringobject.h" namespace QQmlJS { namespace VM { diff --git a/qv4stringobject.cpp b/qv4stringobject.cpp new file mode 100644 index 0000000000..680a101049 --- /dev/null +++ b/qv4stringobject.cpp @@ -0,0 +1,626 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4stringobject.h" +#include "qv4ecmaobjects_p.h" +#include "qv4mm.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef Q_WS_WIN +# include +# ifndef Q_OS_VXWORKS +# include +# else +# include "qplatformdefs.h" +# endif +#else +# include +#endif + +using namespace QQmlJS::VM; + +StringObject::StringObject(ExecutionContext *ctx, const Value &value) + : value(value) +{ + isString = true; + + tmpProperty.type = PropertyDescriptor::Data; + tmpProperty.enumberable = PropertyDescriptor::Enabled; + tmpProperty.writable = PropertyDescriptor::Disabled; + tmpProperty.configurable = PropertyDescriptor::Disabled; + tmpProperty.value = Value::undefinedValue(); + + assert(value.isString()); + defineReadonlyProperty(ctx->engine->id_length, Value::fromUInt32(value.stringValue()->toQString().length())); +} + +PropertyDescriptor *StringObject::getIndex(ExecutionContext *ctx, uint index) +{ + QString str = value.stringValue()->toQString(); + if (index >= (uint)str.length()) + return 0; + String *result = ctx->engine->newString(str.mid(index, 1)); + tmpProperty.value = Value::fromString(result); + return &tmpProperty; +} + + +StringCtor::StringCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value StringCtor::construct(ExecutionContext *ctx) +{ + Value value; + if (ctx->argumentCount) + value = Value::fromString(ctx->argument(0).toString(ctx)); + else + value = Value::fromString(ctx, QString()); + return Value::fromObject(ctx->engine->newStringObject(ctx, value)); +} + +Value StringCtor::call(ExecutionContext *ctx) +{ + Value value; + if (ctx->argumentCount) + value = Value::fromString(ctx->argument(0).toString(ctx)); + else + value = Value::fromString(ctx, QString()); + return value; +} + +void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode, 1); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); + defineDefaultProperty(ctx, QStringLiteral("charAt"), method_charAt, 1); + defineDefaultProperty(ctx, QStringLiteral("charCodeAt"), method_charCodeAt, 1); + defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("localeCompare"), method_localeCompare, 1); + defineDefaultProperty(ctx, QStringLiteral("match"), method_match, 1); + defineDefaultProperty(ctx, QStringLiteral("replace"), method_replace, 2); + defineDefaultProperty(ctx, QStringLiteral("search"), method_search, 1); + defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); + defineDefaultProperty(ctx, QStringLiteral("split"), method_split, 2); + defineDefaultProperty(ctx, QStringLiteral("substr"), method_substr, 2); + defineDefaultProperty(ctx, QStringLiteral("substring"), method_substring, 2); + defineDefaultProperty(ctx, QStringLiteral("toLowerCase"), method_toLowerCase); + defineDefaultProperty(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); + defineDefaultProperty(ctx, QStringLiteral("toUpperCase"), method_toUpperCase); + defineDefaultProperty(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); + defineDefaultProperty(ctx, QStringLiteral("trim"), method_trim); +} + +QString StringPrototype::getThisString(ExecutionContext *ctx) +{ + String* str = 0; + Value thisObject = ctx->thisObject; + if (StringObject *thisString = thisObject.asStringObject()) + str = thisString->value.stringValue(); + else if (thisObject.isUndefined() || thisObject.isNull()) + ctx->throwTypeError(); + else + str = ctx->thisObject.toString(ctx); + return str->toQString(); +} + +Value StringPrototype::method_toString(ExecutionContext *ctx) +{ + StringObject *o = ctx->thisObject.asStringObject(); + if (!o) + ctx->throwTypeError(); + return o->value; +} + +Value StringPrototype::method_valueOf(ExecutionContext *ctx) +{ + StringObject *o = ctx->thisObject.asStringObject(); + if (!o) + ctx->throwTypeError(); + return o->value; +} + +Value StringPrototype::method_charAt(ExecutionContext *ctx) +{ + const QString str = getThisString(ctx); + + int pos = 0; + if (ctx->argumentCount > 0) + pos = (int) ctx->argument(0).toInteger(ctx); + + QString result; + if (pos >= 0 && pos < str.length()) + result += str.at(pos); + + return Value::fromString(ctx, result); +} + +Value StringPrototype::method_charCodeAt(ExecutionContext *ctx) +{ + const QString str = getThisString(ctx); + + int pos = 0; + if (ctx->argumentCount > 0) + pos = (int) ctx->argument(0).toInteger(ctx); + + double result = qSNaN(); + + if (pos >= 0 && pos < str.length()) + result = str.at(pos).unicode(); + + return Value::fromDouble(result); +} + +Value StringPrototype::method_concat(ExecutionContext *ctx) +{ + QString value = getThisString(ctx); + + for (unsigned i = 0; i < ctx->argumentCount; ++i) { + Value v = __qmljs_to_string(ctx->argument(i), ctx); + assert(v.isString()); + value += v.stringValue()->toQString(); + } + + return Value::fromString(ctx, value); +} + +Value StringPrototype::method_indexOf(ExecutionContext *ctx) +{ + QString value = getThisString(ctx); + + QString searchString; + if (ctx->argumentCount) + searchString = ctx->argument(0).toString(ctx)->toQString(); + + int pos = 0; + if (ctx->argumentCount > 1) + pos = (int) ctx->argument(1).toInteger(ctx); + + int index = -1; + if (! value.isEmpty()) + index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); + + return Value::fromDouble(index); +} + +Value StringPrototype::method_lastIndexOf(ExecutionContext *ctx) +{ + const QString value = getThisString(ctx); + + QString searchString; + if (ctx->argumentCount) { + Value v = __qmljs_to_string(ctx->argument(0), ctx); + searchString = v.stringValue()->toQString(); + } + + Value posArg = ctx->argument(1); + double position = __qmljs_to_number(posArg, ctx); + if (std::isnan(position)) + position = +qInf(); + else + position = trunc(position); + + int pos = trunc(qMin(qMax(position, 0.0), double(value.length()))); + if (!searchString.isEmpty() && pos == value.length()) + --pos; + int index = value.lastIndexOf(searchString, pos); + return Value::fromDouble(index); +} + +Value StringPrototype::method_localeCompare(ExecutionContext *ctx) +{ + const QString value = getThisString(ctx); + const QString that = ctx->argument(0).toString(ctx)->toQString(); + return Value::fromDouble(QString::localeAwareCompare(value, that)); +} + +Value StringPrototype::method_match(ExecutionContext *ctx) +{ + if (ctx->thisObject.isUndefined() || ctx->thisObject.isNull()) + __qmljs_throw_type_error(ctx); + + String *s = ctx->thisObject.toString(ctx); + + Value regexp = ctx->argument(0); + RegExpObject *rx = regexp.asRegExpObject(); + if (!rx) + rx = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ®exp, 1).asRegExpObject(); + + if (!rx) + // ### CHECK + __qmljs_throw_type_error(ctx); + + bool global = rx->global; + + FunctionObject *exec = ctx->engine->regExpPrototype->__get__(ctx, ctx->engine->identifier(QStringLiteral("exec")), 0).asFunctionObject(); + + Value arg = Value::fromString(s); + if (!global) + return exec->call(ctx, Value::fromObject(rx), &arg, 1); + + String *lastIndex = ctx->engine->identifier(QStringLiteral("lastIndex")); + rx->__put__(ctx, lastIndex, Value::fromDouble(0.)); + ArrayObject *a = ctx->engine->newArrayObject(ctx); + + double previousLastIndex = 0; + uint n = 0; + while (1) { + Value result = exec->call(ctx, Value::fromObject(rx), &arg, 1); + if (result.isNull()) + break; + assert(result.isObject()); + double thisIndex = rx->__get__(ctx, lastIndex, 0).toInteger(ctx); + if (previousLastIndex == thisIndex) { + previousLastIndex = thisIndex + 1; + rx->__put__(ctx, lastIndex, Value::fromDouble(previousLastIndex)); + } else { + previousLastIndex = thisIndex; + } + Value matchStr = result.objectValue()->__get__(ctx, (uint)0, (bool *)0); + a->array.set(n, matchStr); + ++n; + } + if (!n) + return Value::nullValue(); + + return Value::fromObject(a); + +} + +static QString makeReplacementString(const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) +{ + QString result; + result.reserve(replaceValue.length()); + for (int i = 0; i < replaceValue.length(); ++i) { + if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) { + char ch = replaceValue.at(++i).toLatin1(); + uint substStart = JSC::Yarr::offsetNoMatch; + uint substEnd = JSC::Yarr::offsetNoMatch; + if (ch == '$') { + result += ch; + continue; + } else if (ch == '&') { + substStart = matchOffsets[0]; + substEnd = matchOffsets[1]; + } else if (ch == '`') { + substStart = 0; + substEnd = matchOffsets[0]; + } else if (ch == '\'') { + substStart = matchOffsets[1]; + substEnd = input.length(); + } else if (ch >= '1' && ch <= '9') { + char capture = ch - '0'; + if (capture > 0 && capture < captureCount) { + substStart = matchOffsets[capture * 2]; + substEnd = matchOffsets[capture * 2 + 1]; + } + } else if (ch == '0' && i < replaceValue.length() - 1) { + int capture = (ch - '0') * 10; + ch = replaceValue.at(++i).toLatin1(); + capture += ch - '0'; + if (capture > 0 && capture < captureCount) { + substStart = matchOffsets[capture * 2]; + substEnd = matchOffsets[capture * 2 + 1]; + } + } + if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch) + result += input.midRef(substStart, substEnd - substStart); + } else { + result += replaceValue.at(i); + } + } + return result; +} + +Value StringPrototype::method_replace(ExecutionContext *ctx) +{ + QString string; + if (StringObject *thisString = ctx->thisObject.asStringObject()) + string = thisString->value.stringValue()->toQString(); + else + string = ctx->thisObject.toString(ctx)->toQString(); + + int numCaptures = 0; + QVarLengthArray matchOffsets; + int numStringMatches = 0; + + Value searchValue = ctx->argument(0); + RegExpObject *regExp = searchValue.asRegExpObject(); + if (regExp) { + uint offset = 0; + while (true) { + int oldSize = matchOffsets.size(); + matchOffsets.resize(matchOffsets.size() + regExp->value->captureCount() * 2); + if (regExp->value->match(string, offset, matchOffsets.data() + oldSize) == JSC::Yarr::offsetNoMatch) { + matchOffsets.resize(oldSize); + break; + } + if (!regExp->global) + break; + offset = qMax(offset + 1, matchOffsets[oldSize + 1]); + } + if (regExp->global) + regExp->lastIndexProperty->value = Value::fromUInt32(0); + numStringMatches = matchOffsets.size() / (regExp->value->captureCount() * 2); + numCaptures = regExp->value->captureCount(); + } else { + numCaptures = 1; + QString searchString = searchValue.toString(ctx)->toQString(); + int idx = string.indexOf(searchString); + if (idx != -1) { + numStringMatches = 1; + matchOffsets.resize(2); + matchOffsets[0] = idx; + matchOffsets[1] = idx + searchString.length(); + } + } + + QString result = string; + Value replaceValue = ctx->argument(1); + if (FunctionObject* searchCallback = replaceValue.asFunctionObject()) { + int replacementDelta = 0; + int argc = numCaptures + 2; + Value *args = (Value*)alloca((numCaptures + 2) * sizeof(Value)); + for (int i = 0; i < numStringMatches; ++i) { + for (int k = 0; k < numCaptures; ++k) { + int idx = (i * numCaptures + k) * 2; + uint start = matchOffsets[idx]; + uint end = matchOffsets[idx + 1]; + Value entry = Value::undefinedValue(); + if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch) + entry = Value::fromString(ctx, string.mid(start, end - start)); + args[k] = entry; + } + uint matchStart = matchOffsets[i * numCaptures * 2]; + uint matchEnd = matchOffsets[i * numCaptures * 2 + 1]; + args[numCaptures] = Value::fromUInt32(matchStart); + args[numCaptures + 1] = Value::fromString(ctx, string); + Value replacement = searchCallback->call(ctx, Value::undefinedValue(), args, argc); + QString replacementString = replacement.toString(ctx)->toQString(); + result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacementString); + replacementDelta += replacementString.length() - matchEnd + matchStart; + } + } else { + QString newString = replaceValue.toString(ctx)->toQString(); + int replacementDelta = 0; + + for (int i = 0; i < numStringMatches; ++i) { + int baseIndex = i * numCaptures * 2; + uint matchStart = matchOffsets[baseIndex]; + uint matchEnd = matchOffsets[baseIndex + 1]; + if (matchStart == JSC::Yarr::offsetNoMatch) + continue; + + QString replacement = makeReplacementString(string, newString, matchOffsets.data() + baseIndex, numCaptures); + result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacement); + replacementDelta += replacement.length() - matchEnd + matchStart; + } + } + + return Value::fromString(ctx, result); +} + +Value StringPrototype::method_search(ExecutionContext *ctx) +{ + QString string; + if (StringObject *thisString = ctx->thisObject.asStringObject()) + string = thisString->value.stringValue()->toQString(); + else + string = ctx->thisObject.toString(ctx)->toQString(); + + Value regExpValue = ctx->argument(0); + RegExpObject *regExp = regExpValue.asRegExpObject(); + if (!regExp) { + regExpValue = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ®ExpValue, 1); + regExp = regExpValue.asRegExpObject(); + } + uint* matchOffsets = (uint*)alloca(regExp->value->captureCount() * 2 * sizeof(uint)); + uint result = regExp->value->match(string, /*offset*/0, matchOffsets); + if (result == JSC::Yarr::offsetNoMatch) + return Value::fromInt32(-1); + return Value::fromUInt32(result); +} + +Value StringPrototype::method_slice(ExecutionContext *ctx) +{ + const QString text = getThisString(ctx); + const int length = text.length(); + + int start = int (ctx->argument(0).toInteger(ctx)); + int end = ctx->argument(1).isUndefined() + ? length : int (ctx->argument(1).toInteger(ctx)); + + if (start < 0) + start = qMax(length + start, 0); + else + start = qMin(start, length); + + if (end < 0) + end = qMax(length + end, 0); + else + end = qMin(end, length); + + int count = qMax(0, end - start); + return Value::fromString(ctx, text.mid(start, count)); +} + +Value StringPrototype::method_split(ExecutionContext *ctx) +{ + ctx->throwUnimplemented(QStringLiteral("String.prototype.splt")); + return Value::undefinedValue(); +} + +Value StringPrototype::method_substr(ExecutionContext *ctx) +{ + const QString value = getThisString(ctx); + + double start = 0; + if (ctx->argumentCount > 0) + start = ctx->argument(0).toInteger(ctx); + + double length = +qInf(); + if (ctx->argumentCount > 1) + length = ctx->argument(1).toInteger(ctx); + + double count = value.length(); + if (start < 0) + start = qMax(count + start, 0.0); + + length = qMin(qMax(length, 0.0), count - start); + + qint32 x = Value::toInt32(start); + qint32 y = Value::toInt32(length); + return Value::fromString(ctx, value.mid(x, y)); +} + +Value StringPrototype::method_substring(ExecutionContext *ctx) +{ + QString value = getThisString(ctx); + int length = value.length(); + + double start = 0; + double end = length; + + if (ctx->argumentCount > 0) + start = ctx->argument(0).toInteger(ctx); + + if (ctx->argumentCount > 1) + end = ctx->argument(1).toInteger(ctx); + + if (std::isnan(start) || start < 0) + start = 0; + + if (std::isnan(end) || end < 0) + end = 0; + + if (start > length) + start = length; + + if (end > length) + end = length; + + if (start > end) { + double was = start; + start = end; + end = was; + } + + qint32 x = Value::toInt32(start); + qint32 y = Value::toInt32(end - start); + return Value::fromString(ctx, value.mid(x, y)); +} + +Value StringPrototype::method_toLowerCase(ExecutionContext *ctx) +{ + QString value = getThisString(ctx); + return Value::fromString(ctx, value.toLower()); +} + +Value StringPrototype::method_toLocaleLowerCase(ExecutionContext *ctx) +{ + return method_toLowerCase(ctx); +} + +Value StringPrototype::method_toUpperCase(ExecutionContext *ctx) +{ + QString value = getThisString(ctx); + return Value::fromString(ctx, value.toUpper()); +} + +Value StringPrototype::method_toLocaleUpperCase(ExecutionContext *ctx) +{ + return method_toUpperCase(ctx); +} + +Value StringPrototype::method_fromCharCode(ExecutionContext *ctx) +{ + QString str; + for (unsigned i = 0; i < ctx->argumentCount; ++i) { + QChar c(ctx->argument(i).toUInt16(ctx)); + str += c; + } + return Value::fromString(ctx, str); +} + +Value StringPrototype::method_trim(ExecutionContext *ctx) +{ + if (ctx->thisObject.isNull() || ctx->thisObject.isUndefined()) + __qmljs_throw_type_error(ctx); + + QString s = __qmljs_to_string(ctx->thisObject, ctx).stringValue()->toQString(); + const QChar *chars = s.constData(); + int start, end; + for (start = 0; start < s.length(); ++start) { + if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff) + break; + } + for (end = s.length() - 1; end >= start; --end) { + if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff) + break; + } + + return Value::fromString(ctx, QString(chars + start, end - start + 1)); +} diff --git a/qv4stringobject.h b/qv4stringobject.h new file mode 100644 index 0000000000..3a9ee531a9 --- /dev/null +++ b/qv4stringobject.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STRINGOBJECT_P_H +#define QV4STRINGOBJECT_P_H + +#include "qmljs_objects.h" +#include + +namespace QQmlJS { +namespace VM { + +struct StringObject: Object { + Value value; + PropertyDescriptor tmpProperty; + StringObject(ExecutionContext *ctx, const Value &value); + virtual QString className() { return QStringLiteral("String"); } + virtual StringObject *asStringObject() { return this; } + + PropertyDescriptor *getIndex(ExecutionContext *ctx, uint index); +}; + +struct StringCtor: FunctionObject +{ + StringCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct StringPrototype: StringObject +{ + StringPrototype(ExecutionContext *ctx): StringObject(ctx, Value::fromString(ctx, QString())) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static QString getThisString(ExecutionContext *ctx); + + static Value method_toString(ExecutionContext *ctx); + static Value method_valueOf(ExecutionContext *ctx); + static Value method_charAt(ExecutionContext *ctx); + static Value method_charCodeAt(ExecutionContext *ctx); + static Value method_concat(ExecutionContext *ctx); + static Value method_indexOf(ExecutionContext *ctx); + static Value method_lastIndexOf(ExecutionContext *ctx); + static Value method_localeCompare(ExecutionContext *ctx); + static Value method_match(ExecutionContext *ctx); + static Value method_replace(ExecutionContext *ctx); + static Value method_search(ExecutionContext *ctx); + static Value method_slice(ExecutionContext *ctx); + static Value method_split(ExecutionContext *ctx); + static Value method_substr(ExecutionContext *ctx); + static Value method_substring(ExecutionContext *ctx); + static Value method_toLowerCase(ExecutionContext *ctx); + static Value method_toLocaleLowerCase(ExecutionContext *ctx); + static Value method_toUpperCase(ExecutionContext *ctx); + static Value method_toLocaleUpperCase(ExecutionContext *ctx); + static Value method_fromCharCode(ExecutionContext *ctx); + static Value method_trim(ExecutionContext *ctx); +}; + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/v4.pro b/v4.pro index 723f331c97..d49bc62d0f 100644 --- a/v4.pro +++ b/v4.pro @@ -30,6 +30,7 @@ SOURCES += main.cpp \ qv4argumentsobject.cpp \ qv4dateobject.cpp \ qv4jsonobject.cpp \ + qv4stringobject.cpp \ qv4string.cpp \ qv4objectiterator.cpp \ qv4regexp.cpp @@ -55,6 +56,7 @@ HEADERS += \ qv4argumentsobject.h \ qv4dateobject.h \ qv4jsonobject.h \ + qv4stringobject.h \ qv4string.h \ qv4propertydescriptor.h \ qv4propertytable.h \ -- cgit v1.2.3 From 86bd4300f16fe14f8ac3b4f85855f4bc9fcff58e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 16:01:07 +0100 Subject: Minor optimisation Change-Id: I8a13ab2e7bc74f3e467106880232a795c6c4404c Reviewed-by: Simon Hausmann --- qv4stringobject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qv4stringobject.cpp b/qv4stringobject.cpp index 680a101049..4f68e9d3f3 100644 --- a/qv4stringobject.cpp +++ b/qv4stringobject.cpp @@ -568,8 +568,8 @@ Value StringPrototype::method_substring(ExecutionContext *ctx) end = was; } - qint32 x = Value::toInt32(start); - qint32 y = Value::toInt32(end - start); + qint32 x = (int)start; + qint32 y = (int)(end - start); return Value::fromString(ctx, value.mid(x, y)); } -- cgit v1.2.3 From 6b69c29d802d2b9e3732fbffdcc73c1c12c67d6b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 16:01:28 +0100 Subject: Fix implicit this object for value based calls inside with() blocks Change-Id: Id1d18623ff84e41f2a3c08df8a13648ae49fc4ae Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ qmljs_environment.h | 1 + qmljs_runtime.cpp | 8 +++++--- tests/TestExpectations | 13 +------------ 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 9dc176187c..bdfd68519b 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -349,6 +349,47 @@ Value ExecutionContext::getPropertyNoThrow(String *name) return Value::undefinedValue(); } +Value ExecutionContext::getPropertyAndBase(String *name, Object **base) +{ + *base = 0; + + if (name == engine->id_this) + return thisObject; + + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { + if (With *w = ctx->withObject) { + while (w) { + bool hasProperty = false; + Value v = w->object->__get__(ctx, name, &hasProperty); + if (hasProperty) { + *base = w->object; + return v; + } + w = w->next; + } + } + + if (FunctionObject *f = ctx->function) { + if (f->needsActivation || ctx->withObject) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return ctx->locals[i]; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return ctx->arguments[i]; + } + } + if (ctx->activation) { + bool hasProperty = false; + Value v = ctx->activation->__get__(ctx, name, &hasProperty); + if (hasProperty) + return v; + } + } + throwReferenceError(Value::fromString(name)); + return Value::undefinedValue(); +} + void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) diff --git a/qmljs_environment.h b/qmljs_environment.h index e4bb0b9b38..f043b06da6 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -124,6 +124,7 @@ struct ExecutionContext void setProperty(String *name, Value value); Value getProperty(String *name); Value getPropertyNoThrow(String *name); + Value getPropertyAndBase(String *name, Object **base); void inplaceBitOp(Value value, String *name, BinOp op); bool deleteProperty(String *name); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 40fc075643..9ba9b89b64 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -730,15 +730,17 @@ uint __qmljs_equal(Value x, Value y, ExecutionContext *ctx) return false; } -// TODO: remove this function. Backends should just generate a __qmljs_get_activation_property followed by a __qmljs_call_value Value __qmljs_call_activation_property(ExecutionContext *context, String *name, Value *args, int argc) { - Value func = context->getProperty(name); + Object *base; + Value func = context->getPropertyAndBase(name, &base); Object *o = func.asObject(); if (!o) context->throwTypeError(); - return o->call(context, Value::undefinedValue(), args, argc); + Value thisObject = base ? Value::fromObject(base) : Value::undefinedValue(); + + return o->call(context, thisObject, args, argc); } Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, Value *args, int argc) diff --git a/tests/TestExpectations b/tests/TestExpectations index e85eecf078..e7e8a569f3 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -294,7 +294,6 @@ S15.1.2.2_A9.2 failing S15.1.2.2_A9.3 failing S15.1.2.2_A9.4 failing S15.1.2.2_A9.7 failing -S15.10.6.2_A1_T2 failing S15.11.4.2_A1 failing S15.11.4.2_A2 failing 15.12.1.1-g6-3 failing @@ -618,11 +617,7 @@ S15.4.4.6_A4_T2 failing S15.4.4.7_A3 failing S15.4.4.7_A4_T2 failing S15.4.4.7_A4_T3 failing -S15.5.4.10_A1_T10 failing -S15.5.4.11_A1_T10 failing S15.5.4.11_A5_T1 failing -S15.5.4.12_A1_T10 failing -S15.5.4.13_A1_T10 failing S15.4.4.8_A1_T1 failing S15.4.4.8_A1_T2 failing S15.4.4.8_A2_T1 failing @@ -743,18 +738,12 @@ S15.5.4.14_A4_T6 failing S15.5.4.14_A4_T7 failing S15.5.4.14_A4_T8 failing S15.5.4.14_A4_T9 failing -S15.5.4.15_A1_T10 failing S15.5.4.15_A1_T2 failing S15.5.4.15_A1_T7 failing S15.5.4.15_A1_T8 failing S15.5.4.15_A1_T9 failing 15.5.4.20-4-1 failing -S15.5.4.5_A1_T10 failing -S15.5.4.6_A1_T10 failing -S15.5.4.7_A1_T10 failing S15.5.4.7_A1_T11 failing -S15.5.4.4_A1_T10 failing -S15.5.4.8_A1_T10 failing S15.5.4.8_A1_T12 failing S15.5.4.8_A1_T4 failing S15.7.4_A3.3 failing @@ -943,4 +932,4 @@ S15.4.4.4_A1_T2 failing 15.4.4.21-8-b-iii-1-6 failing 15.12.3_4-1-1 -15.12.3_4-1-3 +15.12.3_4-1-3 \ No newline at end of file -- cgit v1.2.3 From d265df4608542f4ca1e8f2ef756cca930bb55d2a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 18:43:03 +0100 Subject: Implement RegExp.prototype.compile This method is a non standard extension, but supported by all engines, and even used in the test suite. Change-Id: Ie75c840d4da13bd1a62aa7567e8ea2737d31d33e Reviewed-by: Simon Hausmann --- qmljs_objects.h | 2 +- qv4ecmaobjects.cpp | 14 ++++++++++++++ qv4ecmaobjects_p.h | 1 + tests/TestExpectations | 1 - 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/qmljs_objects.h b/qmljs_objects.h index 429cc0a91b..27a9e0b14b 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -345,7 +345,7 @@ struct IsFiniteFunction: FunctionObject struct RegExpObject: Object { RefPtr value; PropertyDescriptor *lastIndexProperty; - const bool global; + bool global; RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global); virtual QString className() { return QStringLiteral("RegExp"); } virtual RegExpObject *asRegExpObject() { return this; } diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 80f27b4ee5..c9976cb930 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1670,6 +1670,7 @@ void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 1); defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 1); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("compile"), method_compile, 2); } Value RegExpPrototype::method_exec(ExecutionContext *ctx) @@ -1737,6 +1738,19 @@ Value RegExpPrototype::method_toString(ExecutionContext *ctx) return Value::fromString(ctx, result); } +Value RegExpPrototype::method_compile(ExecutionContext *ctx) +{ + RegExpObject *r = ctx->thisObject.asRegExpObject(); + if (!r) + ctx->throwTypeError(); + + RegExpObject *re = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ctx->arguments, ctx->argumentCount).asRegExpObject(); + + r->value = re->value; + r->global = re->global; + return Value::undefinedValue(); +} + // // ErrorCtr // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 46f6140b1f..6ee8e0d76b 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -199,6 +199,7 @@ struct RegExpPrototype: RegExpObject static Value method_exec(ExecutionContext *ctx); static Value method_test(ExecutionContext *ctx); static Value method_toString(ExecutionContext *ctx); + static Value method_compile(ExecutionContext *ctx); }; struct ErrorCtor: FunctionObject diff --git a/tests/TestExpectations b/tests/TestExpectations index e7e8a569f3..8143b46464 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -514,7 +514,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-11 failing 15.2.3.3-4-12 failing 15.2.3.3-4-13 failing -15.2.3.3-4-164 failing 15.2.3.3-4-51 failing 15.2.3.3-4-9 failing 15.2.3.3-4-90 failing -- cgit v1.2.3 From f6f6f5d1d31248f07c47147ba9be4d43ff5af34d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 21:26:25 +0100 Subject: Further split up qv4ecmaobjects and qmljs_objects. Change-Id: I3dbcf7f9209a2c8ff601b64ef722640181dbb6f8 Reviewed-by: Simon Hausmann --- debugging.cpp | 2 +- main.cpp | 2 + moth/qv4isel_moth.cpp | 1 + qmljs_engine.cpp | 2 + qmljs_objects.cpp | 518 -------------------------------------------------- qmljs_objects.h | 139 -------------- qv4argumentsobject.h | 1 + qv4array.cpp | 1 + qv4dateobject.h | 1 + qv4ecmaobjects.cpp | 135 ------------- qv4ecmaobjects_p.h | 20 +- qv4functionobject.cpp | 428 +++++++++++++++++++++++++++++++++++++++++ qv4functionobject.h | 222 ++++++++++++++++++++++ qv4globalobject.cpp | 351 ++++++++++++++++++++++++++++++++++ qv4globalobject.h | 146 ++++++++++++++ qv4isel_masm.cpp | 1 + qv4isel_p.cpp | 1 + qv4stringobject.h | 1 + v4.pro | 4 + 19 files changed, 1164 insertions(+), 812 deletions(-) create mode 100644 qv4functionobject.cpp create mode 100644 qv4functionobject.h create mode 100644 qv4globalobject.cpp create mode 100644 qv4globalobject.h diff --git a/debugging.cpp b/debugging.cpp index 8033f36592..bcebe445c4 100644 --- a/debugging.cpp +++ b/debugging.cpp @@ -29,7 +29,7 @@ #include "debugging.h" #include "qmljs_objects.h" - +#include "qv4functionobject.h" #include #define LOW_LEVEL_DEBUGGING_HELPERS diff --git a/main.cpp b/main.cpp index eb3eb0f94f..d0c26a8a37 100644 --- a/main.cpp +++ b/main.cpp @@ -46,6 +46,8 @@ #include "debugging.h" #include "qmljs_objects.h" #include "qmljs_runtime.h" +#include "qv4functionobject.h" +#include "qv4globalobject.h" #include "qv4codegen_p.h" #include "qv4isel_masm_p.h" #include "qv4isel_moth_p.h" diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 50c4c2b661..03167a0220 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -1,6 +1,7 @@ #include "qv4isel_util_p.h" #include "qv4isel_moth_p.h" #include "qv4vme_moth_p.h" +#include "qv4functionobject.h" #include "debugging.h" using namespace QQmlJS; diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 136711aef7..d5cfb27dd6 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include #include "qv4mm.h" #include diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 69c5d96382..3f59d56c4b 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -669,444 +669,6 @@ void ForEachIteratorObject::getCollectables(QVector &objects) } -Function::~Function() -{ - delete[] codeData; -} - -bool FunctionObject::hasInstance(ExecutionContext *ctx, const Value &value) -{ - if (! value.isObject()) - return false; - - Value o = __get__(ctx, ctx->engine->id_prototype); - if (! o.isObject()) { - ctx->throwTypeError(); - return false; - } - - Object *v = value.objectValue(); - while (v) { - v = v->prototype; - - if (! v) - break; - else if (o.objectValue() == v) - return true; - } - - return false; -} - -Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc) -{ - Object *obj = context->engine->newObject(); - Value proto = __get__(context, context->engine->id_prototype); - if (proto.isObject()) - obj->prototype = proto.objectValue(); - - ExecutionContext k; - ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; - - ctx->initCallContext(context, Value::fromObject(obj), this, args, argc); - Value result = construct(ctx); - ctx->leaveCallContext(); - - if (result.isObject()) - return result; - return Value::fromObject(obj); -} - -Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *args, int argc) -{ - ExecutionContext k; - ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; - - ctx->initCallContext(context, thisObject, this, args, argc); - Value result = call(ctx); - ctx->leaveCallContext(); - return result; -} - -Value FunctionObject::callDirect(ExecutionContext *context, Value thisObject, Value *args, int argc) -{ - ExecutionContext k; - ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; - - ctx->initCallContext(context, thisObject, this, args, argc); - maybeAdjustThisObjectForDirectCall(ctx, thisObject); - Value result = call(ctx); - ctx->leaveCallContext(); - return result; -} - -Value FunctionObject::call(ExecutionContext *ctx) -{ - Q_UNUSED(ctx); - return Value::undefinedValue(); -} - -Value FunctionObject::construct(ExecutionContext *ctx) -{ - return call(ctx); -} - -ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) - : FunctionObject(scope) - , function(function) -{ - assert(function); - assert(function->code); - - // global function - if (!scope) - return; - - MemoryManager::GCBlocker gcBlocker(scope->engine->memoryManager); - - if (!function->name.isEmpty()) - name = scope->engine->identifier(function->name); - needsActivation = function->needsActivation(); - usesArgumentsObject = function->usesArgumentsObject; - strictMode = function->isStrict; - formalParameterCount = function->formals.size(); - if (formalParameterCount) { - formalParameterList = new String*[formalParameterCount]; - for (unsigned int i = 0; i < formalParameterCount; ++i) { - formalParameterList[i] = scope->engine->identifier(function->formals.at(i)); - } - } - defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(formalParameterCount)); - - varCount = function->locals.size(); - if (varCount) { - varList = new String*[varCount]; - for (unsigned int i = 0; i < varCount; ++i) { - varList[i] = scope->engine->identifier(function->locals.at(i)); - } - } - - Object *proto = scope->engine->newObject(); - proto->defineDefaultProperty(scope->engine->id_constructor, Value::fromObject(this)); - PropertyDescriptor *pd = members->insert(scope->engine->id_prototype); - pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Enabled; - pd->enumberable = PropertyDescriptor::Disabled; - pd->configurable = PropertyDescriptor::Disabled; - pd->value = Value::fromObject(proto); - - prototype = scope->engine->functionPrototype; - - if (scope->strictMode) { - FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); - pd.configurable = PropertyDescriptor::Disabled; - pd.enumberable = PropertyDescriptor::Disabled; - __defineOwnProperty__(scope, QStringLiteral("caller"), &pd); - __defineOwnProperty__(scope, QStringLiteral("arguments"), &pd); - } -} - -ScriptFunction::~ScriptFunction() -{ - delete[] formalParameterList; - delete[] varList; -} - -Value ScriptFunction::call(VM::ExecutionContext *ctx) -{ - assert(function->code); - return function->code(ctx, function->codeData); -} - - -Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) -{ - if (argc < 1) - return Value::undefinedValue(); - - if (!args[0].isString()) - return args[0]; - - // ### how to determine this correctly? - bool directCall = true; - - const QString code = args[0].stringValue()->toQString(); - QQmlJS::VM::Function *f = parseSource(context, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode); - if (!f) - return Value::undefinedValue(); - - bool strict = f->isStrict || context->strictMode; - - ExecutionContext k, *ctx; - if (!directCall) { - qDebug() << "!direct"; - // ### - } else if (strict) { - ctx = &k; - ctx->initCallContext(context, context->thisObject, this, args, argc); - } else { - // use the surrounding context - ctx = context; - } - - // set the correct strict mode flag on the context - bool cstrict = ctx->strictMode; - ctx->strictMode = strict; - - Value result = f->code(ctx, f->codeData); - - ctx->strictMode = cstrict; - - if (strict) - ctx->leaveCallContext(); - - return result; -} - -EvalFunction::EvalFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->newString(QLatin1String("eval")); -} - -QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, - const QString &fileName, const QString &source, - QQmlJS::Codegen::Mode mode) -{ - using namespace QQmlJS; - - MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); - - VM::ExecutionEngine *vm = ctx->engine; - IR::Module module; - VM::Function *globalCode = 0; - - { - QQmlJS::Engine ee, *engine = ⅇ - Lexer lexer(engine); - lexer.setCode(source, 1, false); - Parser parser(engine); - - const bool parsed = parser.parseProgram(); - - VM::DiagnosticMessage *error = 0, **errIt = &error; - foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { - if (m.isError()) { - *errIt = new VM::DiagnosticMessage; - (*errIt)->fileName = fileName; - (*errIt)->offset = m.loc.offset; - (*errIt)->length = m.loc.length; - (*errIt)->startLine = m.loc.startLine; - (*errIt)->startColumn = m.loc.startColumn; - (*errIt)->type = VM::DiagnosticMessage::Error; - (*errIt)->message = m.message; - errIt = &(*errIt)->next; - } else { - std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn - << ": warning: " << qPrintable(m.message) << std::endl; - } - } - if (error) - ctx->throwSyntaxError(error); - - if (parsed) { - using namespace AST; - Program *program = AST::cast(parser.rootNode()); - if (!program) { - // if parsing was successful, and we have no program, then - // we're done...: - return 0; - } - - Codegen cg(ctx); - IR::Function *globalIRCode = cg(fileName, program, &module, mode); - QScopedPointer isel(ctx->engine->iselFactory->create(vm, &module)); - if (globalIRCode) - globalCode = isel->vmFunction(globalIRCode); - } - - if (! globalCode) - // ### should be a syntax error - __qmljs_throw_type_error(ctx); - } - - return globalCode; -} - -// parseInt [15.1.2.2] -ParseIntFunction::ParseIntFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->newString(QLatin1String("parseInt")); -} - -static inline int toInt(const QChar &qc, int R) -{ - ushort c = qc.unicode(); - int v = -1; - if (c >= '0' && c <= '9') - v = c - '0'; - else if (c >= 'A' && c <= 'Z') - v = c - 'A' + 10; - else if (c >= 'a' && c <= 'z') - v = c - 'a' + 10; - if (v >= 0 && v < R) - return v; - else - return -1; -} - -Value ParseIntFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) -{ - Q_UNUSED(thisObject); - - Value string = (argc > 0) ? args[0] : Value::undefinedValue(); - Value radix = (argc > 1) ? args[1] : Value::undefinedValue(); - int R = radix.isUndefined() ? 0 : radix.toInt32(context); - - // [15.1.2.2] step by step: - String *inputString = string.toString(context); // 1 - QString trimmed = inputString->toQString().trimmed(); // 2 - const QChar *pos = trimmed.constData(); - const QChar *end = pos + trimmed.length(); - - int sign = 1; // 3 - if (pos != end) { - if (*pos == QLatin1Char('-')) - sign = -1; // 4 - if (*pos == QLatin1Char('-') || *pos == QLatin1Char('+')) - ++pos; // 5 - } - bool stripPrefix = true; // 7 - if (R) { // 8 - if (R < 2 || R > 36) - return Value::fromDouble(nan("")); // 8a - if (R != 16) - stripPrefix = false; // 8b - } else { // 9 - R = 10; // 9a - } - if (stripPrefix) { // 10 - if ((end - pos >= 2) - && (pos[0] == QLatin1Char('0')) - && (pos[1] == QLatin1Char('x') || pos[1] == QLatin1Char('X'))) { // 10a - pos += 2; - R = 16; - } - } - // 11: Z is progressively built below - // 13: this is handled by the toInt function - if (pos == end) // 12 - return Value::fromDouble(nan("")); - bool overflow = false; - qint64 v_overflow; - unsigned overflow_digit_count = 0; - int d = toInt(*pos++, R); - if (d == -1) - return Value::fromDouble(nan("")); - qint64 v = d; - while (pos != end) { - d = toInt(*pos++, R); - if (d == -1) - break; - if (overflow) { - if (overflow_digit_count == 0) { - v_overflow = v; - v = 0; - } - ++overflow_digit_count; - v = v * R + d; - } else { - qint64 vNew = v * R + d; - if (vNew < v) { - overflow = true; - --pos; - } else { - v = vNew; - } - } - } - - if (overflow) { - double result = (double) v_overflow * pow(R, overflow_digit_count); - result += v; - return Value::fromDouble(sign * result); - } else { - return Value::fromDouble(sign * (double) v); // 15 - } -} - -// parseFloat [15.1.2.3] -ParseFloatFunction::ParseFloatFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->newString(QLatin1String("parseFloat")); -} - -Value ParseFloatFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) -{ - Q_UNUSED(context); - Q_UNUSED(thisObject); - - Value string = (argc > 0) ? args[0] : Value::undefinedValue(); - - // [15.1.2.3] step by step: - String *inputString = string.toString(context); // 1 - QString trimmed = inputString->toQString().trimmed(); // 2 - - // 4: - if (trimmed.startsWith(QLatin1String("Infinity")) - || trimmed.startsWith(QLatin1String("+Infinity"))) - return Value::fromDouble(INFINITY); - if (trimmed.startsWith("-Infinity")) - return Value::fromDouble(-INFINITY); - QByteArray ba = trimmed.toLatin1(); - bool ok; - const char *begin = ba.constData(); - const char *end = 0; - double d = qstrtod(begin, &end, &ok); - if (end - begin == 0) - return Value::fromDouble(nan("")); // 3 - else - return Value::fromDouble(d); -} - -/// isNaN [15.1.2.4] -IsNaNFunction::IsNaNFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->newString(QLatin1String("isNaN")); -} - -Value IsNaNFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) -{ - const Value &v = (argc > 0) ? args[0] : Value::undefinedValue(); - if (v.integerCompatible()) - return Value::fromBoolean(false); - - double d = v.toNumber(context); - return Value::fromBoolean(std::isnan(d)); -} - -/// isFinite [15.1.2.5] -IsFiniteFunction::IsFiniteFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->newString(QLatin1String("isFinite")); -} - -Value IsFiniteFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) -{ - const Value &v = (argc > 0) ? args[0] : Value::undefinedValue(); - if (v.integerCompatible()) - return Value::fromBoolean(true); - - double d = v.toNumber(context); - return Value::fromBoolean(std::isfinite(d)); -} - - RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global) : value(value) , global(global) @@ -1148,86 +710,6 @@ SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *m -BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) - : FunctionObject(scope) - , code(code) -{ - this->name = name; -} - -Value BuiltinFunction::construct(ExecutionContext *ctx) -{ - ctx->throwTypeError(); - return Value::undefinedValue(); -} - -void BuiltinFunction::maybeAdjustThisObjectForDirectCall(ExecutionContext *context, Value thisArg) -{ - // Built-in functions allow for the this object to be null or undefined. This overrides - // the behaviour of changing thisObject to the global object if null/undefined and allows - // the built-in functions for example to throw a type error if null is passed. - if (thisArg.isNull() || thisArg.isUndefined()) - context->thisObject = thisArg; -} - - -BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) - : FunctionObject(scope) - , target(target) - , boundThis(boundThis) - , boundArgs(boundArgs) -{ - prototype = scope->engine->functionPrototype; - - int len = target->__get__(scope, scope->engine->id_length).toUInt32(scope); - len -= boundArgs.size(); - if (len < 0) - len = 0; - defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(len)); - - FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); - pd.configurable = PropertyDescriptor::Disabled; - pd.enumberable = PropertyDescriptor::Disabled; - *members->insert(scope->engine->id_arguments) = pd; - *members->insert(scope->engine->id_caller) = pd; -} - -Value BoundFunction::call(ExecutionContext *context, Value, Value *args, int argc) -{ - Value *newArgs = static_cast(alloca(sizeof(Value)*(boundArgs.size() + argc))); - memcpy(newArgs, boundArgs.constData(), boundArgs.size()*sizeof(Value)); - memcpy(newArgs + boundArgs.size(), args, argc*sizeof(Value)); - - return target->call(context, boundThis, newArgs, boundArgs.size() + argc); -} - -Value BoundFunction::construct(ExecutionContext *context, Value *args, int argc) -{ - Value *newArgs = static_cast(alloca(sizeof(Value)*(boundArgs.size() + argc))); - memcpy(newArgs, boundArgs.constData(), boundArgs.size()*sizeof(Value)); - memcpy(newArgs + boundArgs.size(), args, argc*sizeof(Value)); - - return target->construct(context, newArgs, boundArgs.size() + argc); -} - -bool BoundFunction::hasInstance(ExecutionContext *ctx, const Value &value) -{ - return target->hasInstance(ctx, value); -} - -void BoundFunction::getCollectables(QVector &objects) -{ - FunctionObject::getCollectables(objects); - objects.append(target); - if (Object *o = boundThis.asObject()) - objects.append(o); - for (int i = 0; i < boundArgs.size(); ++i) - if (Object *o = boundArgs.at(i).asObject()) - objects.append(o); -} - - EvalErrorObject::EvalErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->engine, ctx->argument(0)) { diff --git a/qmljs_objects.h b/qmljs_objects.h index 27a9e0b14b..bdc21e5a04 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -203,145 +203,6 @@ struct ArrayObject: Object { virtual ArrayObject *asArrayObject() { return this; } }; -struct Function { - QString name; - - VM::Value (*code)(VM::ExecutionContext *, const uchar *); - const uchar *codeData; - JSC::MacroAssemblerCodeRef codeRef; - - QList formals; - QList locals; - - bool hasNestedFunctions : 1; - bool hasDirectEval : 1; - bool usesArgumentsObject : 1; - bool isStrict : 1; - - Function(const QString &name) - : name(name) - , code(0) - , codeData(0) - , hasNestedFunctions(0) - , hasDirectEval(false) - , usesArgumentsObject(false) - , isStrict(false) - {} - ~Function(); - - inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; } -}; - -struct FunctionObject: Object { - ExecutionContext *scope; - String *name; - String **formalParameterList; - String **varList; - unsigned int formalParameterCount; - unsigned int varCount; - - FunctionObject(ExecutionContext *scope) - : scope(scope) - , name(0) - , formalParameterList(0) - , varList(0) - , formalParameterCount(0) - , varCount(0) - { needsActivation = false; - usesArgumentsObject = false; - strictMode = false; } - - virtual QString className() { return QStringLiteral("Function"); } - virtual FunctionObject *asFunctionObject() { return this; } - virtual bool hasInstance(ExecutionContext *ctx, const Value &value); - - virtual Value construct(ExecutionContext *context, Value *args, int argc); - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); - // Nothing to do in the default implementation, only _native_ functions might change context->thisObject. - virtual void maybeAdjustThisObjectForDirectCall(ExecutionContext* /*context*/, Value /*thisArg*/) { } - Value callDirect(ExecutionContext *context, Value thisObject, Value *args, int argc); - - virtual struct ScriptFunction *asScriptFunction() { return 0; } - -protected: - virtual Value call(ExecutionContext *ctx); - virtual Value construct(ExecutionContext *ctx); -}; - -struct BuiltinFunction: FunctionObject { - Value (*code)(ExecutionContext *); - - BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); - virtual Value call(ExecutionContext *ctx) { return code(ctx); } - virtual Value construct(ExecutionContext *ctx); - virtual void maybeAdjustThisObjectForDirectCall(ExecutionContext *context, Value thisArg); -}; - -struct ScriptFunction: FunctionObject { - VM::Function *function; - - ScriptFunction(ExecutionContext *scope, VM::Function *function); - virtual ~ScriptFunction(); - - virtual Value call(ExecutionContext *ctx); - - virtual ScriptFunction *asScriptFunction() { return this; } -}; - -struct EvalFunction : FunctionObject -{ - EvalFunction(ExecutionContext *scope); - - static QQmlJS::VM::Function *parseSource(QQmlJS::VM::ExecutionContext *ctx, - const QString &fileName, - const QString &source, - QQmlJS::Codegen::Mode mode); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); -}; - -struct BoundFunction: FunctionObject { - FunctionObject *target; - Value boundThis; - QVector boundArgs; - - BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); - virtual ~BoundFunction() {} - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); - virtual Value construct(ExecutionContext *context, Value *args, int argc); - virtual bool hasInstance(ExecutionContext *ctx, const Value &value); - virtual void getCollectables(QVector &objects); -}; - -struct ParseIntFunction: FunctionObject -{ - ParseIntFunction(ExecutionContext *scope); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); -}; - -struct ParseFloatFunction: FunctionObject -{ - ParseFloatFunction(ExecutionContext *scope); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); -}; - -struct IsNaNFunction: FunctionObject -{ - IsNaNFunction(ExecutionContext *scope); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); -}; - -struct IsFiniteFunction: FunctionObject -{ - IsFiniteFunction(ExecutionContext *scope); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); -}; - struct RegExpObject: Object { RefPtr value; PropertyDescriptor *lastIndexProperty; diff --git a/qv4argumentsobject.h b/qv4argumentsobject.h index ce47cfae11..b8f15b35f6 100644 --- a/qv4argumentsobject.h +++ b/qv4argumentsobject.h @@ -42,6 +42,7 @@ #define QV4ARGUMENTSOBJECTS_H #include +#include namespace QQmlJS { namespace VM { diff --git a/qv4array.cpp b/qv4array.cpp index fae357b5c8..1bbd266d62 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -42,6 +42,7 @@ #include "qv4array.h" #include "qmljs_runtime.h" #include "qmljs_objects.h" +#include "qv4functionobject.h" #include #ifdef QT_QMAP_DEBUG diff --git a/qv4dateobject.h b/qv4dateobject.h index a0fba20e59..11137555b3 100644 --- a/qv4dateobject.h +++ b/qv4dateobject.h @@ -42,6 +42,7 @@ #define QV4DATEOBJECT_P_H #include "qmljs_objects.h" +#include "qv4functionobject.h" #include namespace QQmlJS { diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index c9976cb930..de254e3775 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1465,141 +1465,6 @@ Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) return acc; } -// -// Function object -// -FunctionCtor::FunctionCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -// 15.3.2 -Value FunctionCtor::construct(ExecutionContext *ctx) -{ - MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); - - QString args; - QString body; - if (ctx->argumentCount > 0) { - for (uint i = 0; i < ctx->argumentCount - 1; ++i) { - if (i) - args += QLatin1String(", "); - args += ctx->argument(i).toString(ctx)->toQString(); - } - body = ctx->argument(ctx->argumentCount - 1).toString(ctx)->toQString(); - } - - QString function = QLatin1String("function(") + args + QLatin1String("){") + body + QLatin1String("}"); - - QQmlJS::Engine ee, *engine = ⅇ - Lexer lexer(engine); - lexer.setCode(function, 1, false); - Parser parser(engine); - - const bool parsed = parser.parseExpression(); - - if (!parsed) - ctx->throwSyntaxError(0); - - using namespace AST; - FunctionExpression *fe = AST::cast(parser.rootNode()); - if (!fe) - ctx->throwSyntaxError(0); - - IR::Module module; - - Codegen cg(ctx); - IR::Function *irf = cg(QString(), fe, &module); - - QScopedPointer isel(ctx->engine->iselFactory->create(ctx->engine, &module)); - VM::Function *vmf = isel->vmFunction(irf); - - return Value::fromObject(ctx->engine->newScriptFunction(ctx->engine->rootContext, vmf)); -} - -// 15.3.1: This is equivalent to new Function(...) -Value FunctionCtor::call(ExecutionContext *ctx) -{ - return construct(ctx); -} - -void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2); - defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 1); - defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 1); -} - -Value FunctionPrototype::method_toString(ExecutionContext *ctx) -{ - FunctionObject *fun = ctx->thisObject.asFunctionObject(); - if (!fun) - ctx->throwTypeError(); - - return Value::fromString(ctx, QStringLiteral("function() { [code] }")); -} - -Value FunctionPrototype::method_apply(ExecutionContext *ctx) -{ - Value thisArg = ctx->argument(0); - - Value arg = ctx->argument(1); - QVector args; - - if (Object *arr = arg.asObject()) { - quint32 len = arr->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); - for (quint32 i = 0; i < len; ++i) { - Value a = arr->__get__(ctx, i); - args.append(a); - } - } else if (!(arg.isUndefined() || arg.isNull())) { - ctx->throwTypeError(); - return Value::undefinedValue(); - } - - FunctionObject *o = ctx->thisObject.asFunctionObject(); - if (!o) - ctx->throwTypeError(); - - return o->callDirect(ctx, thisArg, args.data(), args.size()); -} - -Value FunctionPrototype::method_call(ExecutionContext *ctx) -{ - Value thisArg = ctx->argument(0); - - QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); - if (ctx->argumentCount) - qCopy(ctx->arguments + 1, - ctx->arguments + ctx->argumentCount, args.begin()); - - FunctionObject *o = ctx->thisObject.asFunctionObject(); - if (!o) - ctx->throwTypeError(); - - return o->callDirect(ctx, thisArg, args.data(), args.size()); -} - -Value FunctionPrototype::method_bind(ExecutionContext *ctx) -{ - FunctionObject *target = ctx->thisObject.asFunctionObject(); - if (!target) - ctx->throwTypeError(); - - Value boundThis = ctx->argument(0); - QVector boundArgs; - for (uint i = 1; i < ctx->argumentCount; ++i) - boundArgs += ctx->argument(i); - - - BoundFunction *f = ctx->engine->newBoundFunction(ctx, target, boundThis, boundArgs); - return Value::fromObject(f); -} - // // RegExp object // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 6ee8e0d76b..2af74ee80a 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -42,6 +42,7 @@ #define QV4ECMAOBJECTS_P_H #include "qmljs_objects.h" +#include "qv4functionobject.h" #include namespace QQmlJS { @@ -164,25 +165,6 @@ struct ArrayPrototype: ArrayObject static Value method_reduceRight(ExecutionContext *ctx); }; -struct FunctionCtor: FunctionObject -{ - FunctionCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct FunctionPrototype: FunctionObject -{ - FunctionPrototype(ExecutionContext *ctx): FunctionObject(ctx) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_toString(ExecutionContext *ctx); - static Value method_apply(ExecutionContext *ctx); - static Value method_call(ExecutionContext *ctx); - static Value method_bind(ExecutionContext *ctx); -}; - struct RegExpCtor: FunctionObject { RegExpCtor(ExecutionContext *scope); diff --git a/qv4functionobject.cpp b/qv4functionobject.cpp new file mode 100644 index 0000000000..6d8b19f31b --- /dev/null +++ b/qv4functionobject.cpp @@ -0,0 +1,428 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmljs_objects.h" +#include "qv4ir_p.h" +#include "qv4isel_p.h" +#include "qv4ecmaobjects_p.h" +#include "qv4stringobject.h" +#include "qv4mm.h" + +#include +#include +#include +#include +#include +#include +#include "private/qlocale_tools_p.h" + +#include +#include +#include +#include +#include +#include + +using namespace QQmlJS::VM; + + +Function::~Function() +{ + delete[] codeData; +} + +bool FunctionObject::hasInstance(ExecutionContext *ctx, const Value &value) +{ + if (! value.isObject()) + return false; + + Value o = __get__(ctx, ctx->engine->id_prototype); + if (! o.isObject()) { + ctx->throwTypeError(); + return false; + } + + Object *v = value.objectValue(); + while (v) { + v = v->prototype; + + if (! v) + break; + else if (o.objectValue() == v) + return true; + } + + return false; +} + +Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc) +{ + Object *obj = context->engine->newObject(); + Value proto = __get__(context, context->engine->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + + ExecutionContext k; + ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; + + ctx->initCallContext(context, Value::fromObject(obj), this, args, argc); + Value result = construct(ctx); + ctx->leaveCallContext(); + + if (result.isObject()) + return result; + return Value::fromObject(obj); +} + +Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +{ + ExecutionContext k; + ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; + + ctx->initCallContext(context, thisObject, this, args, argc); + Value result = call(ctx); + ctx->leaveCallContext(); + return result; +} + +Value FunctionObject::callDirect(ExecutionContext *context, Value thisObject, Value *args, int argc) +{ + ExecutionContext k; + ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; + + ctx->initCallContext(context, thisObject, this, args, argc); + maybeAdjustThisObjectForDirectCall(ctx, thisObject); + Value result = call(ctx); + ctx->leaveCallContext(); + return result; +} + +Value FunctionObject::call(ExecutionContext *ctx) +{ + Q_UNUSED(ctx); + return Value::undefinedValue(); +} + +Value FunctionObject::construct(ExecutionContext *ctx) +{ + return call(ctx); +} + + +FunctionCtor::FunctionCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +// 15.3.2 +Value FunctionCtor::construct(ExecutionContext *ctx) +{ + MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); + + QString args; + QString body; + if (ctx->argumentCount > 0) { + for (uint i = 0; i < ctx->argumentCount - 1; ++i) { + if (i) + args += QLatin1String(", "); + args += ctx->argument(i).toString(ctx)->toQString(); + } + body = ctx->argument(ctx->argumentCount - 1).toString(ctx)->toQString(); + } + + QString function = QLatin1String("function(") + args + QLatin1String("){") + body + QLatin1String("}"); + + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(function, 1, false); + Parser parser(engine); + + const bool parsed = parser.parseExpression(); + + if (!parsed) + ctx->throwSyntaxError(0); + + using namespace AST; + FunctionExpression *fe = AST::cast(parser.rootNode()); + if (!fe) + ctx->throwSyntaxError(0); + + IR::Module module; + + Codegen cg(ctx); + IR::Function *irf = cg(QString(), fe, &module); + + QScopedPointer isel(ctx->engine->iselFactory->create(ctx->engine, &module)); + VM::Function *vmf = isel->vmFunction(irf); + + return Value::fromObject(ctx->engine->newScriptFunction(ctx->engine->rootContext, vmf)); +} + +// 15.3.1: This is equivalent to new Function(...) +Value FunctionCtor::call(ExecutionContext *ctx) +{ + return construct(ctx); +} + +void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2); + defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 1); + defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 1); +} + +Value FunctionPrototype::method_toString(ExecutionContext *ctx) +{ + FunctionObject *fun = ctx->thisObject.asFunctionObject(); + if (!fun) + ctx->throwTypeError(); + + return Value::fromString(ctx, QStringLiteral("function() { [code] }")); +} + +Value FunctionPrototype::method_apply(ExecutionContext *ctx) +{ + Value thisArg = ctx->argument(0); + + Value arg = ctx->argument(1); + QVector args; + + if (Object *arr = arg.asObject()) { + quint32 len = arr->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); + for (quint32 i = 0; i < len; ++i) { + Value a = arr->__get__(ctx, i); + args.append(a); + } + } else if (!(arg.isUndefined() || arg.isNull())) { + ctx->throwTypeError(); + return Value::undefinedValue(); + } + + FunctionObject *o = ctx->thisObject.asFunctionObject(); + if (!o) + ctx->throwTypeError(); + + return o->callDirect(ctx, thisArg, args.data(), args.size()); +} + +Value FunctionPrototype::method_call(ExecutionContext *ctx) +{ + Value thisArg = ctx->argument(0); + + QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); + if (ctx->argumentCount) + qCopy(ctx->arguments + 1, + ctx->arguments + ctx->argumentCount, args.begin()); + + FunctionObject *o = ctx->thisObject.asFunctionObject(); + if (!o) + ctx->throwTypeError(); + + return o->callDirect(ctx, thisArg, args.data(), args.size()); +} + +Value FunctionPrototype::method_bind(ExecutionContext *ctx) +{ + FunctionObject *target = ctx->thisObject.asFunctionObject(); + if (!target) + ctx->throwTypeError(); + + Value boundThis = ctx->argument(0); + QVector boundArgs; + for (uint i = 1; i < ctx->argumentCount; ++i) + boundArgs += ctx->argument(i); + + + BoundFunction *f = ctx->engine->newBoundFunction(ctx, target, boundThis, boundArgs); + return Value::fromObject(f); +} + +ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) + : FunctionObject(scope) + , function(function) +{ + assert(function); + assert(function->code); + + // global function + if (!scope) + return; + + MemoryManager::GCBlocker gcBlocker(scope->engine->memoryManager); + + if (!function->name.isEmpty()) + name = scope->engine->identifier(function->name); + needsActivation = function->needsActivation(); + usesArgumentsObject = function->usesArgumentsObject; + strictMode = function->isStrict; + formalParameterCount = function->formals.size(); + if (formalParameterCount) { + formalParameterList = new String*[formalParameterCount]; + for (unsigned int i = 0; i < formalParameterCount; ++i) { + formalParameterList[i] = scope->engine->identifier(function->formals.at(i)); + } + } + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(formalParameterCount)); + + varCount = function->locals.size(); + if (varCount) { + varList = new String*[varCount]; + for (unsigned int i = 0; i < varCount; ++i) { + varList[i] = scope->engine->identifier(function->locals.at(i)); + } + } + + Object *proto = scope->engine->newObject(); + proto->defineDefaultProperty(scope->engine->id_constructor, Value::fromObject(this)); + PropertyDescriptor *pd = members->insert(scope->engine->id_prototype); + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Disabled; + pd->value = Value::fromObject(proto); + + prototype = scope->engine->functionPrototype; + + if (scope->strictMode) { + FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error); + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); + pd.configurable = PropertyDescriptor::Disabled; + pd.enumberable = PropertyDescriptor::Disabled; + __defineOwnProperty__(scope, QStringLiteral("caller"), &pd); + __defineOwnProperty__(scope, QStringLiteral("arguments"), &pd); + } +} + +ScriptFunction::~ScriptFunction() +{ + delete[] formalParameterList; + delete[] varList; +} + +Value ScriptFunction::call(VM::ExecutionContext *ctx) +{ + assert(function->code); + return function->code(ctx, function->codeData); +} + + +BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) + : FunctionObject(scope) + , code(code) +{ + this->name = name; +} + +Value BuiltinFunction::construct(ExecutionContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +void BuiltinFunction::maybeAdjustThisObjectForDirectCall(ExecutionContext *context, Value thisArg) +{ + // Built-in functions allow for the this object to be null or undefined. This overrides + // the behaviour of changing thisObject to the global object if null/undefined and allows + // the built-in functions for example to throw a type error if null is passed. + if (thisArg.isNull() || thisArg.isUndefined()) + context->thisObject = thisArg; +} + + +BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) + : FunctionObject(scope) + , target(target) + , boundThis(boundThis) + , boundArgs(boundArgs) +{ + prototype = scope->engine->functionPrototype; + + int len = target->__get__(scope, scope->engine->id_length).toUInt32(scope); + len -= boundArgs.size(); + if (len < 0) + len = 0; + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(len)); + + FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error); + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); + pd.configurable = PropertyDescriptor::Disabled; + pd.enumberable = PropertyDescriptor::Disabled; + *members->insert(scope->engine->id_arguments) = pd; + *members->insert(scope->engine->id_caller) = pd; +} + +Value BoundFunction::call(ExecutionContext *context, Value, Value *args, int argc) +{ + Value *newArgs = static_cast(alloca(sizeof(Value)*(boundArgs.size() + argc))); + memcpy(newArgs, boundArgs.constData(), boundArgs.size()*sizeof(Value)); + memcpy(newArgs + boundArgs.size(), args, argc*sizeof(Value)); + + return target->call(context, boundThis, newArgs, boundArgs.size() + argc); +} + +Value BoundFunction::construct(ExecutionContext *context, Value *args, int argc) +{ + Value *newArgs = static_cast(alloca(sizeof(Value)*(boundArgs.size() + argc))); + memcpy(newArgs, boundArgs.constData(), boundArgs.size()*sizeof(Value)); + memcpy(newArgs + boundArgs.size(), args, argc*sizeof(Value)); + + return target->construct(context, newArgs, boundArgs.size() + argc); +} + +bool BoundFunction::hasInstance(ExecutionContext *ctx, const Value &value) +{ + return target->hasInstance(ctx, value); +} + +void BoundFunction::getCollectables(QVector &objects) +{ + FunctionObject::getCollectables(objects); + objects.append(target); + if (Object *o = boundThis.asObject()) + objects.append(o); + for (int i = 0; i < boundArgs.size(); ++i) + if (Object *o = boundArgs.at(i).asObject()) + objects.append(o); +} diff --git a/qv4functionobject.h b/qv4functionobject.h new file mode 100644 index 0000000000..775c84f8b8 --- /dev/null +++ b/qv4functionobject.h @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4FUNCTIONOBJECT_H +#define QV4FUNCTIONOBJECT_H + +#include "qmljs_runtime.h" +#include "qmljs_engine.h" +#include "qmljs_environment.h" +#include "qmljs_objects.h" +#include "qv4array.h" +#include "qv4string.h" +#include "qv4codegen_p.h" +#include "qv4isel_p.h" +#include "qv4managed.h" +#include "qv4propertydescriptor.h" +#include "qv4propertytable.h" +#include "qv4objectiterator.h" +#include "qv4regexp.h" + +#include +#include +#include +#include +#include + +namespace QQmlJS { + +namespace VM { + +struct Value; +struct Function; +struct Object; +struct ObjectIterator; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct ExecutionEngine; +class MemoryManager; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct RegExpPrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; + +struct Function { + QString name; + + VM::Value (*code)(VM::ExecutionContext *, const uchar *); + const uchar *codeData; + JSC::MacroAssemblerCodeRef codeRef; + + QList formals; + QList locals; + + bool hasNestedFunctions : 1; + bool hasDirectEval : 1; + bool usesArgumentsObject : 1; + bool isStrict : 1; + + Function(const QString &name) + : name(name) + , code(0) + , codeData(0) + , hasNestedFunctions(0) + , hasDirectEval(false) + , usesArgumentsObject(false) + , isStrict(false) + {} + ~Function(); + + inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; } +}; + +struct FunctionObject: Object { + ExecutionContext *scope; + String *name; + String **formalParameterList; + String **varList; + unsigned int formalParameterCount; + unsigned int varCount; + + FunctionObject(ExecutionContext *scope) + : scope(scope) + , name(0) + , formalParameterList(0) + , varList(0) + , formalParameterCount(0) + , varCount(0) + { needsActivation = false; + usesArgumentsObject = false; + strictMode = false; } + + virtual QString className() { return QStringLiteral("Function"); } + virtual FunctionObject *asFunctionObject() { return this; } + virtual bool hasInstance(ExecutionContext *ctx, const Value &value); + + virtual Value construct(ExecutionContext *context, Value *args, int argc); + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); + // Nothing to do in the default implementation, only _native_ functions might change context->thisObject. + virtual void maybeAdjustThisObjectForDirectCall(ExecutionContext* /*context*/, Value /*thisArg*/) { } + Value callDirect(ExecutionContext *context, Value thisObject, Value *args, int argc); + + virtual struct ScriptFunction *asScriptFunction() { return 0; } + +protected: + virtual Value call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); +}; + +struct FunctionCtor: FunctionObject +{ + FunctionCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct FunctionPrototype: FunctionObject +{ + FunctionPrototype(ExecutionContext *ctx): FunctionObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(ExecutionContext *ctx); + static Value method_apply(ExecutionContext *ctx); + static Value method_call(ExecutionContext *ctx); + static Value method_bind(ExecutionContext *ctx); +}; + +struct BuiltinFunction: FunctionObject { + Value (*code)(ExecutionContext *); + + BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); + virtual Value call(ExecutionContext *ctx) { return code(ctx); } + virtual Value construct(ExecutionContext *ctx); + virtual void maybeAdjustThisObjectForDirectCall(ExecutionContext *context, Value thisArg); +}; + +struct ScriptFunction: FunctionObject { + VM::Function *function; + + ScriptFunction(ExecutionContext *scope, VM::Function *function); + virtual ~ScriptFunction(); + + virtual Value call(ExecutionContext *ctx); + + virtual ScriptFunction *asScriptFunction() { return this; } +}; + +struct BoundFunction: FunctionObject { + FunctionObject *target; + Value boundThis; + QVector boundArgs; + + BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); + virtual ~BoundFunction() {} + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); + virtual Value construct(ExecutionContext *context, Value *args, int argc); + virtual bool hasInstance(ExecutionContext *ctx, const Value &value); + virtual void getCollectables(QVector &objects); +}; + +} // namespace VM +} // namespace QQmlJS + +#endif // QMLJS_OBJECTS_H diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp new file mode 100644 index 0000000000..4b2c1911f9 --- /dev/null +++ b/qv4globalobject.cpp @@ -0,0 +1,351 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4globalobject.h" +#include "qv4ir_p.h" +#include "qv4isel_p.h" +#include "qv4ecmaobjects_p.h" +#include "qv4stringobject.h" +#include "qv4mm.h" + +#include +#include +#include +#include +#include +#include +#include "private/qlocale_tools_p.h" + +#include +#include +#include +#include +#include +#include + +using namespace QQmlJS::VM; + + +Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +{ + if (argc < 1) + return Value::undefinedValue(); + + if (!args[0].isString()) + return args[0]; + + // ### how to determine this correctly? + bool directCall = true; + + const QString code = args[0].stringValue()->toQString(); + QQmlJS::VM::Function *f = parseSource(context, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode); + if (!f) + return Value::undefinedValue(); + + bool strict = f->isStrict || context->strictMode; + + ExecutionContext k, *ctx; + if (!directCall) { + qDebug() << "!direct"; + // ### + } else if (strict) { + ctx = &k; + ctx->initCallContext(context, context->thisObject, this, args, argc); + } else { + // use the surrounding context + ctx = context; + } + + // set the correct strict mode flag on the context + bool cstrict = ctx->strictMode; + ctx->strictMode = strict; + + Value result = f->code(ctx, f->codeData); + + ctx->strictMode = cstrict; + + if (strict) + ctx->leaveCallContext(); + + return result; +} + +EvalFunction::EvalFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("eval")); +} + +QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, + const QString &fileName, const QString &source, + QQmlJS::Codegen::Mode mode) +{ + using namespace QQmlJS; + + MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); + + VM::ExecutionEngine *vm = ctx->engine; + IR::Module module; + VM::Function *globalCode = 0; + + { + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(source, 1, false); + Parser parser(engine); + + const bool parsed = parser.parseProgram(); + + VM::DiagnosticMessage *error = 0, **errIt = &error; + foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { + if (m.isError()) { + *errIt = new VM::DiagnosticMessage; + (*errIt)->fileName = fileName; + (*errIt)->offset = m.loc.offset; + (*errIt)->length = m.loc.length; + (*errIt)->startLine = m.loc.startLine; + (*errIt)->startColumn = m.loc.startColumn; + (*errIt)->type = VM::DiagnosticMessage::Error; + (*errIt)->message = m.message; + errIt = &(*errIt)->next; + } else { + std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn + << ": warning: " << qPrintable(m.message) << std::endl; + } + } + if (error) + ctx->throwSyntaxError(error); + + if (parsed) { + using namespace AST; + Program *program = AST::cast(parser.rootNode()); + if (!program) { + // if parsing was successful, and we have no program, then + // we're done...: + return 0; + } + + Codegen cg(ctx); + IR::Function *globalIRCode = cg(fileName, program, &module, mode); + QScopedPointer isel(ctx->engine->iselFactory->create(vm, &module)); + if (globalIRCode) + globalCode = isel->vmFunction(globalIRCode); + } + + if (! globalCode) + // ### should be a syntax error + __qmljs_throw_type_error(ctx); + } + + return globalCode; +} + +// parseInt [15.1.2.2] +ParseIntFunction::ParseIntFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("parseInt")); +} + +static inline int toInt(const QChar &qc, int R) +{ + ushort c = qc.unicode(); + int v = -1; + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'A' && c <= 'Z') + v = c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + v = c - 'a' + 10; + if (v >= 0 && v < R) + return v; + else + return -1; +} + +Value ParseIntFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +{ + Q_UNUSED(thisObject); + + Value string = (argc > 0) ? args[0] : Value::undefinedValue(); + Value radix = (argc > 1) ? args[1] : Value::undefinedValue(); + int R = radix.isUndefined() ? 0 : radix.toInt32(context); + + // [15.1.2.2] step by step: + String *inputString = string.toString(context); // 1 + QString trimmed = inputString->toQString().trimmed(); // 2 + const QChar *pos = trimmed.constData(); + const QChar *end = pos + trimmed.length(); + + int sign = 1; // 3 + if (pos != end) { + if (*pos == QLatin1Char('-')) + sign = -1; // 4 + if (*pos == QLatin1Char('-') || *pos == QLatin1Char('+')) + ++pos; // 5 + } + bool stripPrefix = true; // 7 + if (R) { // 8 + if (R < 2 || R > 36) + return Value::fromDouble(nan("")); // 8a + if (R != 16) + stripPrefix = false; // 8b + } else { // 9 + R = 10; // 9a + } + if (stripPrefix) { // 10 + if ((end - pos >= 2) + && (pos[0] == QLatin1Char('0')) + && (pos[1] == QLatin1Char('x') || pos[1] == QLatin1Char('X'))) { // 10a + pos += 2; + R = 16; + } + } + // 11: Z is progressively built below + // 13: this is handled by the toInt function + if (pos == end) // 12 + return Value::fromDouble(nan("")); + bool overflow = false; + qint64 v_overflow; + unsigned overflow_digit_count = 0; + int d = toInt(*pos++, R); + if (d == -1) + return Value::fromDouble(nan("")); + qint64 v = d; + while (pos != end) { + d = toInt(*pos++, R); + if (d == -1) + break; + if (overflow) { + if (overflow_digit_count == 0) { + v_overflow = v; + v = 0; + } + ++overflow_digit_count; + v = v * R + d; + } else { + qint64 vNew = v * R + d; + if (vNew < v) { + overflow = true; + --pos; + } else { + v = vNew; + } + } + } + + if (overflow) { + double result = (double) v_overflow * pow(R, overflow_digit_count); + result += v; + return Value::fromDouble(sign * result); + } else { + return Value::fromDouble(sign * (double) v); // 15 + } +} + +// parseFloat [15.1.2.3] +ParseFloatFunction::ParseFloatFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("parseFloat")); +} + +Value ParseFloatFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +{ + Q_UNUSED(context); + Q_UNUSED(thisObject); + + Value string = (argc > 0) ? args[0] : Value::undefinedValue(); + + // [15.1.2.3] step by step: + String *inputString = string.toString(context); // 1 + QString trimmed = inputString->toQString().trimmed(); // 2 + + // 4: + if (trimmed.startsWith(QLatin1String("Infinity")) + || trimmed.startsWith(QLatin1String("+Infinity"))) + return Value::fromDouble(INFINITY); + if (trimmed.startsWith("-Infinity")) + return Value::fromDouble(-INFINITY); + QByteArray ba = trimmed.toLatin1(); + bool ok; + const char *begin = ba.constData(); + const char *end = 0; + double d = qstrtod(begin, &end, &ok); + if (end - begin == 0) + return Value::fromDouble(nan("")); // 3 + else + return Value::fromDouble(d); +} + +/// isNaN [15.1.2.4] +IsNaNFunction::IsNaNFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("isNaN")); +} + +Value IsNaNFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +{ + const Value &v = (argc > 0) ? args[0] : Value::undefinedValue(); + if (v.integerCompatible()) + return Value::fromBoolean(false); + + double d = v.toNumber(context); + return Value::fromBoolean(std::isnan(d)); +} + +/// isFinite [15.1.2.5] +IsFiniteFunction::IsFiniteFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("isFinite")); +} + +Value IsFiniteFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +{ + const Value &v = (argc > 0) ? args[0] : Value::undefinedValue(); + if (v.integerCompatible()) + return Value::fromBoolean(true); + + double d = v.toNumber(context); + return Value::fromBoolean(std::isfinite(d)); +} diff --git a/qv4globalobject.h b/qv4globalobject.h new file mode 100644 index 0000000000..822ec97326 --- /dev/null +++ b/qv4globalobject.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4GLOBALOBJECT_H +#define QV4GLOBALOBJECT_H + +#include "qmljs_runtime.h" +#include "qmljs_engine.h" +#include "qmljs_environment.h" +#include "qv4functionobject.h" +#include "qv4array.h" +#include "qv4string.h" +#include "qv4codegen_p.h" +#include "qv4isel_p.h" +#include "qv4managed.h" +#include "qv4propertydescriptor.h" +#include "qv4propertytable.h" +#include "qv4objectiterator.h" +#include "qv4regexp.h" + +#include +#include +#include +#include +#include + +namespace QQmlJS { + +namespace VM { + +struct Value; +struct Function; +struct Object; +struct ObjectIterator; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct ExecutionEngine; +class MemoryManager; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct RegExpPrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; + + +struct EvalFunction : FunctionObject +{ + EvalFunction(ExecutionContext *scope); + + static QQmlJS::VM::Function *parseSource(QQmlJS::VM::ExecutionContext *ctx, + const QString &fileName, + const QString &source, + QQmlJS::Codegen::Mode mode); + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); +}; + +struct ParseIntFunction: FunctionObject +{ + ParseIntFunction(ExecutionContext *scope); + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); +}; + +struct ParseFloatFunction: FunctionObject +{ + ParseFloatFunction(ExecutionContext *scope); + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); +}; + +struct IsNaNFunction: FunctionObject +{ + IsNaNFunction(ExecutionContext *scope); + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); +}; + +struct IsFiniteFunction: FunctionObject +{ + IsFiniteFunction(ExecutionContext *scope); + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); +}; + + +} // namespace VM +} // namespace QQmlJS + +#endif // QMLJS_OBJECTS_H diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 29bd3ecb48..a49c043960 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -42,6 +42,7 @@ #include "qv4isel_masm_p.h" #include "qmljs_runtime.h" #include "qmljs_objects.h" +#include "qv4functionobject.h" #include #include diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 7c1c6cc0a4..28c35b0109 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -3,6 +3,7 @@ #include "qv4ir_p.h" #include "qv4isel_p.h" #include "qv4isel_util_p.h" +#include "qv4functionobject.h" #include diff --git a/qv4stringobject.h b/qv4stringobject.h index 3a9ee531a9..465ab34c4a 100644 --- a/qv4stringobject.h +++ b/qv4stringobject.h @@ -42,6 +42,7 @@ #define QV4STRINGOBJECT_P_H #include "qmljs_objects.h" +#include "qv4functionobject.h" #include namespace QQmlJS { diff --git a/v4.pro b/v4.pro index d49bc62d0f..98e50c9d8d 100644 --- a/v4.pro +++ b/v4.pro @@ -29,6 +29,8 @@ SOURCES += main.cpp \ qv4array.cpp \ qv4argumentsobject.cpp \ qv4dateobject.cpp \ + qv4functionobject.cpp \ + qv4globalobject.cpp \ qv4jsonobject.cpp \ qv4stringobject.cpp \ qv4string.cpp \ @@ -55,6 +57,8 @@ HEADERS += \ qv4array.h \ qv4argumentsobject.h \ qv4dateobject.h \ + qv4functionobject.h \ + qv4globalobject.h \ qv4jsonobject.h \ qv4stringobject.h \ qv4string.h \ -- cgit v1.2.3 From 6e149e2ecfe1de97cd1ebddc9c192b7936256fe1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 21:36:12 +0100 Subject: Move Error Objects into their own file Change-Id: Id45f3c38b3effb0087a4782049b5d3184d7f9891 Reviewed-by: Simon Hausmann --- main.cpp | 1 + qmljs_engine.cpp | 1 + qmljs_objects.cpp | 77 ----------------- qmljs_objects.h | 50 ------------ qv4ecmaobjects.cpp | 87 -------------------- qv4ecmaobjects_p.h | 97 ---------------------- qv4errorobject.cpp | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4errorobject.h | 202 +++++++++++++++++++++++++++++++++++++++++++++ v4.pro | 2 + 9 files changed, 442 insertions(+), 311 deletions(-) create mode 100644 qv4errorobject.cpp create mode 100644 qv4errorobject.h diff --git a/main.cpp b/main.cpp index d0c26a8a37..93f43128e1 100644 --- a/main.cpp +++ b/main.cpp @@ -47,6 +47,7 @@ #include "qmljs_objects.h" #include "qmljs_runtime.h" #include "qv4functionobject.h" +#include "qv4errorobject.h" #include "qv4globalobject.h" #include "qv4codegen_p.h" #include "qv4isel_masm_p.h" diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index d5cfb27dd6..0a47fe5bb4 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include "qv4mm.h" diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 3f59d56c4b..95f098876a 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -688,80 +688,3 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bo defineReadonlyProperty(engine->identifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase())); defineReadonlyProperty(engine->identifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); } - -ErrorObject::ErrorObject(ExecutionEngine* engine, const Value &message) -{ - if (message.type() != Value::Undefined_Type) - defineDefaultProperty(engine->identifier(QStringLiteral("message")), message); -} - -void ErrorObject::setNameProperty(ExecutionContext *ctx) -{ - defineDefaultProperty(ctx, QLatin1String("name"), Value::fromString(ctx, className())); -} - -SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) - : ErrorObject(ctx->engine, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0)) - , msg(message) -{ - prototype = ctx->engine->syntaxErrorPrototype; - setNameProperty(ctx); -} - - - -EvalErrorObject::EvalErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) -{ - setNameProperty(ctx); - prototype = ctx->engine->evalErrorPrototype; -} - -RangeErrorObject::RangeErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) -{ - setNameProperty(ctx); - prototype = ctx->engine->rangeErrorPrototype; -} - -RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) -{ - setNameProperty(ctx); - prototype = ctx->engine->rangeErrorPrototype; -} - -ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) -{ - setNameProperty(ctx); - prototype = ctx->engine->referenceErrorPrototype; -} - -ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) -{ - setNameProperty(ctx); - prototype = ctx->engine->referenceErrorPrototype; -} - -TypeErrorObject::TypeErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) -{ - setNameProperty(ctx); - prototype = ctx->engine->typeErrorPrototype; -} - -TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) -{ - setNameProperty(ctx); - prototype = ctx->engine->typeErrorPrototype; -} - -URIErrorObject::URIErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) -{ - setNameProperty(ctx); - prototype = ctx->engine->uRIErrorPrototype; -} diff --git a/qmljs_objects.h b/qmljs_objects.h index bdc21e5a04..2c80aa9ea4 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -212,56 +212,6 @@ struct RegExpObject: Object { virtual RegExpObject *asRegExpObject() { return this; } }; -struct ErrorObject: Object { - ErrorObject(ExecutionEngine* engine, const Value &message); - virtual QString className() { return QStringLiteral("Error"); } - virtual ErrorObject *asErrorObject() { return this; } - - virtual struct SyntaxErrorObject *asSyntaxError() { return 0; } - -protected: - void setNameProperty(ExecutionContext *ctx); -}; - -struct EvalErrorObject: ErrorObject { - EvalErrorObject(ExecutionContext *ctx); - virtual QString className() { return QStringLiteral("EvalError"); } -}; - -struct RangeErrorObject: ErrorObject { - RangeErrorObject(ExecutionContext *ctx); - RangeErrorObject(ExecutionContext *ctx, const QString &msg); - virtual QString className() { return QStringLiteral("RangeError"); } -}; - -struct ReferenceErrorObject: ErrorObject { - ReferenceErrorObject(ExecutionContext *ctx); - ReferenceErrorObject(ExecutionContext *ctx, const QString &msg); - virtual QString className() { return QStringLiteral("ReferenceError"); } -}; - -struct SyntaxErrorObject: ErrorObject { - SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg); - ~SyntaxErrorObject() { delete msg; } - virtual QString className() { return QStringLiteral("SyntaxError"); } - - virtual SyntaxErrorObject *asSyntaxError() { return this; } - DiagnosticMessage *message() { return msg; } - -private: - DiagnosticMessage *msg; -}; - -struct TypeErrorObject: ErrorObject { - TypeErrorObject(ExecutionContext *ctx); - TypeErrorObject(ExecutionContext *ctx, const QString &msg); - virtual QString className() { return QStringLiteral("TypeError"); } -}; - -struct URIErrorObject: ErrorObject { - URIErrorObject(ExecutionContext *ctx); - virtual QString className() { return QStringLiteral("URIError"); } -}; } // namespace VM } // namespace QQmlJS diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index de254e3775..8b36102d66 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1616,93 +1616,6 @@ Value RegExpPrototype::method_compile(ExecutionContext *ctx) return Value::undefinedValue(); } -// -// ErrorCtr -// -ErrorCtor::ErrorCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value ErrorCtor::construct(ExecutionContext *ctx) -{ - return Value::fromObject(ctx->engine->newErrorObject(ctx->argument(0))); -} - -Value ErrorCtor::call(ExecutionContext *ctx) -{ - return construct(ctx); -} - -Value EvalErrorCtor::construct(ExecutionContext *ctx) -{ - return Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx)); -} - -Value RangeErrorCtor::construct(ExecutionContext *ctx) -{ - return Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx)); -} - -Value ReferenceErrorCtor::construct(ExecutionContext *ctx) -{ - return Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx)); -} - -Value SyntaxErrorCtor::construct(ExecutionContext *ctx) -{ - return Value::fromObject(new (ctx->engine->memoryManager) SyntaxErrorObject(ctx, 0)); -} - -Value TypeErrorCtor::construct(ExecutionContext *ctx) -{ - return Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx)); -} - -Value URIErrorCtor::construct(ExecutionContext *ctx) -{ - return Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx)); -} - -void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(obj)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - obj->defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - obj->defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - obj->defineDefaultProperty(ctx, QStringLiteral("message"), Value::fromString(ctx, QString())); -} - -Value ErrorPrototype::method_toString(ExecutionContext *ctx) -{ - Object *o = ctx->thisObject.asObject(); - if (!o) - __qmljs_throw_type_error(ctx); - - Value name = o->__get__(ctx, ctx->engine->newString(QString::fromLatin1("name"))); - QString qname; - if (name.isUndefined()) - qname = QString::fromLatin1("Error"); - else - qname = __qmljs_to_string(name, ctx).stringValue()->toQString(); - - Value message = o->__get__(ctx, ctx->engine->newString(QString::fromLatin1("message"))); - QString qmessage; - if (!message.isUndefined()) - qmessage = __qmljs_to_string(message, ctx).stringValue()->toQString(); - - QString str; - if (qname.isEmpty()) { - str = qmessage; - } else if (qmessage.isEmpty()) { - str = qname; - } else { - str = qname + QLatin1String(": ") + qmessage; - } - - return Value::fromString(ctx, str); -} - // // Math object diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 2af74ee80a..0a65dff50b 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -184,103 +184,6 @@ struct RegExpPrototype: RegExpObject static Value method_compile(ExecutionContext *ctx); }; -struct ErrorCtor: FunctionObject -{ - ErrorCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct EvalErrorCtor: ErrorCtor -{ - EvalErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - - virtual Value construct(ExecutionContext *ctx); -}; - -struct RangeErrorCtor: ErrorCtor -{ - RangeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - - virtual Value construct(ExecutionContext *ctx); -}; - -struct ReferenceErrorCtor: ErrorCtor -{ - ReferenceErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - - virtual Value construct(ExecutionContext *ctx); -}; - -struct SyntaxErrorCtor: ErrorCtor -{ - SyntaxErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - - virtual Value construct(ExecutionContext *ctx); -}; - -struct TypeErrorCtor: ErrorCtor -{ - TypeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - - virtual Value construct(ExecutionContext *ctx); -}; - -struct URIErrorCtor: ErrorCtor -{ - URIErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - - virtual Value construct(ExecutionContext *ctx); -}; - - -struct ErrorPrototype: ErrorObject -{ - // ### shouldn't be undefined - ErrorPrototype(ExecutionEngine* engine): ErrorObject(engine, Value::undefinedValue()) {} - void init(ExecutionContext *ctx, const Value &ctor) { init(ctx, ctor, this); } - - static void init(ExecutionContext *ctx, const Value &ctor, Object *obj); - static Value method_toString(ExecutionContext *ctx); -}; - -struct EvalErrorPrototype: EvalErrorObject -{ - EvalErrorPrototype(ExecutionContext *ctx): EvalErrorObject(ctx) {} - void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } -}; - -struct RangeErrorPrototype: RangeErrorObject -{ - RangeErrorPrototype(ExecutionContext *ctx): RangeErrorObject(ctx) {} - void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } -}; - -struct ReferenceErrorPrototype: ReferenceErrorObject -{ - ReferenceErrorPrototype(ExecutionContext *ctx): ReferenceErrorObject(ctx) {} - void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } -}; - -struct SyntaxErrorPrototype: SyntaxErrorObject -{ - SyntaxErrorPrototype(ExecutionContext *ctx): SyntaxErrorObject(ctx, 0) {} - void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } -}; - -struct TypeErrorPrototype: TypeErrorObject -{ - TypeErrorPrototype(ExecutionContext *ctx): TypeErrorObject(ctx) {} - void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } -}; - -struct URIErrorPrototype: URIErrorObject -{ - URIErrorPrototype(ExecutionContext *ctx): URIErrorObject(ctx) {} - void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } -}; - struct MathObject: Object { diff --git a/qv4errorobject.cpp b/qv4errorobject.cpp new file mode 100644 index 0000000000..d59fa23563 --- /dev/null +++ b/qv4errorobject.cpp @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4errorobject.h" +#include "qv4mm.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef Q_WS_WIN +# include +# ifndef Q_OS_VXWORKS +# include +# else +# include "qplatformdefs.h" +# endif +#else +# include +#endif + +using namespace QQmlJS::VM; + +ErrorObject::ErrorObject(ExecutionEngine* engine, const Value &message) +{ + if (message.type() != Value::Undefined_Type) + defineDefaultProperty(engine->identifier(QStringLiteral("message")), message); +} + +void ErrorObject::setNameProperty(ExecutionContext *ctx) +{ + defineDefaultProperty(ctx, QLatin1String("name"), Value::fromString(ctx, className())); +} + +SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) + : ErrorObject(ctx->engine, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0)) + , msg(message) +{ + prototype = ctx->engine->syntaxErrorPrototype; + setNameProperty(ctx); +} + + + +EvalErrorObject::EvalErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->engine, ctx->argument(0)) +{ + setNameProperty(ctx); + prototype = ctx->engine->evalErrorPrototype; +} + +RangeErrorObject::RangeErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->engine, ctx->argument(0)) +{ + setNameProperty(ctx); + prototype = ctx->engine->rangeErrorPrototype; +} + +RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &msg) + : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) +{ + setNameProperty(ctx); + prototype = ctx->engine->rangeErrorPrototype; +} + +ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->engine, ctx->argument(0)) +{ + setNameProperty(ctx); + prototype = ctx->engine->referenceErrorPrototype; +} + +ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString &msg) + : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) +{ + setNameProperty(ctx); + prototype = ctx->engine->referenceErrorPrototype; +} + +TypeErrorObject::TypeErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->engine, ctx->argument(0)) +{ + setNameProperty(ctx); + prototype = ctx->engine->typeErrorPrototype; +} + +TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &msg) + : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) +{ + setNameProperty(ctx); + prototype = ctx->engine->typeErrorPrototype; +} + +URIErrorObject::URIErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->engine, ctx->argument(0)) +{ + setNameProperty(ctx); + prototype = ctx->engine->uRIErrorPrototype; +} + + +ErrorCtor::ErrorCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value ErrorCtor::construct(ExecutionContext *ctx) +{ + return Value::fromObject(ctx->engine->newErrorObject(ctx->argument(0))); +} + +Value ErrorCtor::call(ExecutionContext *ctx) +{ + return construct(ctx); +} + +Value EvalErrorCtor::construct(ExecutionContext *ctx) +{ + return Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx)); +} + +Value RangeErrorCtor::construct(ExecutionContext *ctx) +{ + return Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx)); +} + +Value ReferenceErrorCtor::construct(ExecutionContext *ctx) +{ + return Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx)); +} + +Value SyntaxErrorCtor::construct(ExecutionContext *ctx) +{ + return Value::fromObject(new (ctx->engine->memoryManager) SyntaxErrorObject(ctx, 0)); +} + +Value TypeErrorCtor::construct(ExecutionContext *ctx) +{ + return Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx)); +} + +Value URIErrorCtor::construct(ExecutionContext *ctx) +{ + return Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx)); +} + +void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(obj)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + obj->defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + obj->defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + obj->defineDefaultProperty(ctx, QStringLiteral("message"), Value::fromString(ctx, QString())); +} + +Value ErrorPrototype::method_toString(ExecutionContext *ctx) +{ + Object *o = ctx->thisObject.asObject(); + if (!o) + __qmljs_throw_type_error(ctx); + + Value name = o->__get__(ctx, ctx->engine->newString(QString::fromLatin1("name"))); + QString qname; + if (name.isUndefined()) + qname = QString::fromLatin1("Error"); + else + qname = __qmljs_to_string(name, ctx).stringValue()->toQString(); + + Value message = o->__get__(ctx, ctx->engine->newString(QString::fromLatin1("message"))); + QString qmessage; + if (!message.isUndefined()) + qmessage = __qmljs_to_string(message, ctx).stringValue()->toQString(); + + QString str; + if (qname.isEmpty()) { + str = qmessage; + } else if (qmessage.isEmpty()) { + str = qname; + } else { + str = qname + QLatin1String(": ") + qmessage; + } + + return Value::fromString(ctx, str); +} diff --git a/qv4errorobject.h b/qv4errorobject.h new file mode 100644 index 0000000000..95867e1260 --- /dev/null +++ b/qv4errorobject.h @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ERROROBJECT_H +#define QV4ERROROBJECT_H + +#include "qmljs_objects.h" +#include "qv4functionobject.h" + +namespace QQmlJS { +namespace VM { + +struct ErrorObject: Object { + ErrorObject(ExecutionEngine* engine, const Value &message); + virtual QString className() { return QStringLiteral("Error"); } + virtual ErrorObject *asErrorObject() { return this; } + + virtual struct SyntaxErrorObject *asSyntaxError() { return 0; } + +protected: + void setNameProperty(ExecutionContext *ctx); +}; + +struct EvalErrorObject: ErrorObject { + EvalErrorObject(ExecutionContext *ctx); + virtual QString className() { return QStringLiteral("EvalError"); } +}; + +struct RangeErrorObject: ErrorObject { + RangeErrorObject(ExecutionContext *ctx); + RangeErrorObject(ExecutionContext *ctx, const QString &msg); + virtual QString className() { return QStringLiteral("RangeError"); } +}; + +struct ReferenceErrorObject: ErrorObject { + ReferenceErrorObject(ExecutionContext *ctx); + ReferenceErrorObject(ExecutionContext *ctx, const QString &msg); + virtual QString className() { return QStringLiteral("ReferenceError"); } +}; + +struct SyntaxErrorObject: ErrorObject { + SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg); + ~SyntaxErrorObject() { delete msg; } + virtual QString className() { return QStringLiteral("SyntaxError"); } + + virtual SyntaxErrorObject *asSyntaxError() { return this; } + DiagnosticMessage *message() { return msg; } + +private: + DiagnosticMessage *msg; +}; + +struct TypeErrorObject: ErrorObject { + TypeErrorObject(ExecutionContext *ctx); + TypeErrorObject(ExecutionContext *ctx, const QString &msg); + virtual QString className() { return QStringLiteral("TypeError"); } +}; + +struct URIErrorObject: ErrorObject { + URIErrorObject(ExecutionContext *ctx); + virtual QString className() { return QStringLiteral("URIError"); } +}; + +struct ErrorCtor: FunctionObject +{ + ErrorCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct EvalErrorCtor: ErrorCtor +{ + EvalErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + + virtual Value construct(ExecutionContext *ctx); +}; + +struct RangeErrorCtor: ErrorCtor +{ + RangeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + + virtual Value construct(ExecutionContext *ctx); +}; + +struct ReferenceErrorCtor: ErrorCtor +{ + ReferenceErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + + virtual Value construct(ExecutionContext *ctx); +}; + +struct SyntaxErrorCtor: ErrorCtor +{ + SyntaxErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + + virtual Value construct(ExecutionContext *ctx); +}; + +struct TypeErrorCtor: ErrorCtor +{ + TypeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + + virtual Value construct(ExecutionContext *ctx); +}; + +struct URIErrorCtor: ErrorCtor +{ + URIErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + + virtual Value construct(ExecutionContext *ctx); +}; + + +struct ErrorPrototype: ErrorObject +{ + // ### shouldn't be undefined + ErrorPrototype(ExecutionEngine* engine): ErrorObject(engine, Value::undefinedValue()) {} + void init(ExecutionContext *ctx, const Value &ctor) { init(ctx, ctor, this); } + + static void init(ExecutionContext *ctx, const Value &ctor, Object *obj); + static Value method_toString(ExecutionContext *ctx); +}; + +struct EvalErrorPrototype: EvalErrorObject +{ + EvalErrorPrototype(ExecutionContext *ctx): EvalErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct RangeErrorPrototype: RangeErrorObject +{ + RangeErrorPrototype(ExecutionContext *ctx): RangeErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct ReferenceErrorPrototype: ReferenceErrorObject +{ + ReferenceErrorPrototype(ExecutionContext *ctx): ReferenceErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct SyntaxErrorPrototype: SyntaxErrorObject +{ + SyntaxErrorPrototype(ExecutionContext *ctx): SyntaxErrorObject(ctx, 0) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct TypeErrorPrototype: TypeErrorObject +{ + TypeErrorPrototype(ExecutionContext *ctx): TypeErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct URIErrorPrototype: URIErrorObject +{ + URIErrorPrototype(ExecutionContext *ctx): URIErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/v4.pro b/v4.pro index 98e50c9d8d..42c4924288 100644 --- a/v4.pro +++ b/v4.pro @@ -29,6 +29,7 @@ SOURCES += main.cpp \ qv4array.cpp \ qv4argumentsobject.cpp \ qv4dateobject.cpp \ + qv4errorobject.cpp \ qv4functionobject.cpp \ qv4globalobject.cpp \ qv4jsonobject.cpp \ @@ -57,6 +58,7 @@ HEADERS += \ qv4array.h \ qv4argumentsobject.h \ qv4dateobject.h \ + qv4errorobject.h \ qv4functionobject.h \ qv4globalobject.h \ qv4jsonobject.h \ -- cgit v1.2.3 From 6681dde8b70de79a84ea390914c4dd2013876da7 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 21:43:15 +0100 Subject: Split Regexp object into it's own file Change-Id: Ib24f4a42d2312291b3a10d2a02fad33a1eb4b5b5 Reviewed-by: Simon Hausmann --- moth/qv4isel_moth.cpp | 1 + qmljs_engine.cpp | 1 + qmljs_objects.cpp | 20 ----- qmljs_objects.h | 9 -- qv4ecmaobjects.cpp | 152 -------------------------------- qv4ecmaobjects_p.h | 20 ----- qv4isel_masm.cpp | 1 + qv4regexpobject.cpp | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++ qv4regexpobject.h | 102 ++++++++++++++++++++++ qv4stringobject.cpp | 1 + v4.pro | 2 + 11 files changed, 343 insertions(+), 201 deletions(-) create mode 100644 qv4regexpobject.cpp create mode 100644 qv4regexpobject.h diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 03167a0220..ce890e08fc 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -2,6 +2,7 @@ #include "qv4isel_moth_p.h" #include "qv4vme_moth_p.h" #include "qv4functionobject.h" +#include "qv4regexpobject.h" #include "debugging.h" using namespace QQmlJS; diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 0a47fe5bb4..2ad3b97f75 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include "qv4mm.h" #include diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 95f098876a..eb93cf8eb7 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -668,23 +668,3 @@ void ForEachIteratorObject::getCollectables(QVector &objects) objects.append(it.object); } - -RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global) - : value(value) - , global(global) -{ - if (!members) - members.reset(new PropertyTable()); - lastIndexProperty = members->insert(engine->identifier(QStringLiteral("lastIndex"))); - lastIndexProperty->type = PropertyDescriptor::Data; - lastIndexProperty->writable = PropertyDescriptor::Enabled; - lastIndexProperty->enumberable = PropertyDescriptor::Disabled; - lastIndexProperty->configurable = PropertyDescriptor::Disabled; - lastIndexProperty->value = Value::fromInt32(0); - if (!this->value.get()) - return; - defineReadonlyProperty(engine->identifier(QStringLiteral("source")), Value::fromString(engine->newString(this->value->pattern()))); - defineReadonlyProperty(engine->identifier(QStringLiteral("global")), Value::fromBoolean(global)); - defineReadonlyProperty(engine->identifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase())); - defineReadonlyProperty(engine->identifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); -} diff --git a/qmljs_objects.h b/qmljs_objects.h index 2c80aa9ea4..819b5d7f2a 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -203,15 +203,6 @@ struct ArrayObject: Object { virtual ArrayObject *asArrayObject() { return this; } }; -struct RegExpObject: Object { - RefPtr value; - PropertyDescriptor *lastIndexProperty; - bool global; - RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global); - virtual QString className() { return QStringLiteral("RegExp"); } - virtual RegExpObject *asRegExpObject() { return this; } -}; - } // namespace VM } // namespace QQmlJS diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 8b36102d66..d92ada9e34 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -1465,158 +1465,6 @@ Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) return acc; } -// -// RegExp object -// -RegExpCtor::RegExpCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value RegExpCtor::construct(ExecutionContext *ctx) -{ - Value r = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); - Value f = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); - if (RegExpObject *re = r.asRegExpObject()) { - if (!f.isUndefined()) - ctx->throwTypeError(); - - RegExpObject *o = ctx->engine->newRegExpObject(re->value, re->global); - return Value::fromObject(o); - } - - if (r.isUndefined()) - r = Value::fromString(ctx, QString()); - else if (!r.isString()) - r = __qmljs_to_string(r, ctx); - - bool global = false; - bool ignoreCase = false; - bool multiLine = false; - if (!f.isUndefined()) { - f = __qmljs_to_string(f, ctx); - QString str = f.stringValue()->toQString(); - for (int i = 0; i < str.length(); ++i) { - if (str.at(i) == QChar('g') && !global) { - global = true; - } else if (str.at(i) == QChar('i') && !ignoreCase) { - ignoreCase = true; - } else if (str.at(i) == QChar('m') && !multiLine) { - multiLine = true; - } else { - ctx->throwSyntaxError(0); - } - } - } - - RefPtr re = RegExp::create(ctx->engine, r.stringValue()->toQString(), ignoreCase, multiLine); - if (!re->isValid()) - ctx->throwSyntaxError(0); - - RegExpObject *o = ctx->engine->newRegExpObject(re, global); - return Value::fromObject(o); -} - -Value RegExpCtor::call(ExecutionContext *ctx) -{ - if (ctx->argumentCount > 0 && ctx->argument(0).asRegExpObject()) { - if (ctx->argumentCount == 1 || ctx->argument(1).isUndefined()) - return ctx->argument(0); - } - - return construct(ctx); -} - -void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(2)); - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 1); - defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 1); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("compile"), method_compile, 2); -} - -Value RegExpPrototype::method_exec(ExecutionContext *ctx) -{ - RegExpObject *r = ctx->thisObject.asRegExpObject(); - if (!r) - ctx->throwTypeError(); - - Value arg = ctx->argument(0); - arg = __qmljs_to_string(arg, ctx); - QString s = arg.stringValue()->toQString(); - - int offset = r->global ? r->lastIndexProperty->value.toInt32(ctx) : 0; - if (offset < 0 || offset > s.length()) { - r->lastIndexProperty->value = Value::fromInt32(0); - return Value::nullValue(); - } - - uint* matchOffsets = (uint*)alloca(r->value->captureCount() * 2 * sizeof(uint)); - int result = r->value->match(s, offset, matchOffsets); - if (result == -1) { - r->lastIndexProperty->value = Value::fromInt32(0); - return Value::nullValue(); - } - - // fill in result data - ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); - for (int i = 0; i < r->value->captureCount(); ++i) { - int start = matchOffsets[i * 2]; - int end = matchOffsets[i * 2 + 1]; - Value entry = Value::undefinedValue(); - if (start != -1 && end != -1) - entry = Value::fromString(ctx, s.mid(start, end - start)); - array->array.push_back(entry); - } - - array->__put__(ctx, QLatin1String("index"), Value::fromInt32(result)); - array->__put__(ctx, QLatin1String("input"), arg); - - if (r->global) - r->lastIndexProperty->value = Value::fromInt32(matchOffsets[1]); - - return Value::fromObject(array); -} - -Value RegExpPrototype::method_test(ExecutionContext *ctx) -{ - Value r = method_exec(ctx); - return Value::fromBoolean(!r.isNull()); -} - -Value RegExpPrototype::method_toString(ExecutionContext *ctx) -{ - RegExpObject *r = ctx->thisObject.asRegExpObject(); - if (!r) - ctx->throwTypeError(); - - QString result = QChar('/') + r->value->pattern(); - result += QChar('/'); - // ### 'g' option missing - if (r->value->ignoreCase()) - result += QChar('i'); - if (r->value->multiLine()) - result += QChar('m'); - return Value::fromString(ctx, result); -} - -Value RegExpPrototype::method_compile(ExecutionContext *ctx) -{ - RegExpObject *r = ctx->thisObject.asRegExpObject(); - if (!r) - ctx->throwTypeError(); - - RegExpObject *re = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ctx->arguments, ctx->argumentCount).asRegExpObject(); - - r->value = re->value; - r->global = re->global; - return Value::undefinedValue(); -} - - // // Math object // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 0a65dff50b..dc4fcd5164 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -165,26 +165,6 @@ struct ArrayPrototype: ArrayObject static Value method_reduceRight(ExecutionContext *ctx); }; -struct RegExpCtor: FunctionObject -{ - RegExpCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct RegExpPrototype: RegExpObject -{ - RegExpPrototype(ExecutionEngine* engine): RegExpObject(engine, RegExp::create(0, QString()), false) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_exec(ExecutionContext *ctx); - static Value method_test(ExecutionContext *ctx); - static Value method_toString(ExecutionContext *ctx); - static Value method_compile(ExecutionContext *ctx); -}; - - struct MathObject: Object { MathObject(ExecutionContext *ctx); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index a49c043960..8998618f15 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -43,6 +43,7 @@ #include "qmljs_runtime.h" #include "qmljs_objects.h" #include "qv4functionobject.h" +#include "qv4regexpobject.h" #include #include diff --git a/qv4regexpobject.cpp b/qv4regexpobject.cpp new file mode 100644 index 0000000000..20d0088185 --- /dev/null +++ b/qv4regexpobject.cpp @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4regexpobject.h" +#include "qv4ir_p.h" +#include "qv4isel_p.h" +#include "qv4ecmaobjects_p.h" +#include "qv4stringobject.h" +#include "qv4mm.h" + +#include +#include +#include +#include +#include +#include +#include "private/qlocale_tools_p.h" + +#include +#include +#include +#include +#include +#include + +using namespace QQmlJS::VM; + + +RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global) + : value(value) + , global(global) +{ + if (!members) + members.reset(new PropertyTable()); + lastIndexProperty = members->insert(engine->identifier(QStringLiteral("lastIndex"))); + lastIndexProperty->type = PropertyDescriptor::Data; + lastIndexProperty->writable = PropertyDescriptor::Enabled; + lastIndexProperty->enumberable = PropertyDescriptor::Disabled; + lastIndexProperty->configurable = PropertyDescriptor::Disabled; + lastIndexProperty->value = Value::fromInt32(0); + if (!this->value.get()) + return; + defineReadonlyProperty(engine->identifier(QStringLiteral("source")), Value::fromString(engine->newString(this->value->pattern()))); + defineReadonlyProperty(engine->identifier(QStringLiteral("global")), Value::fromBoolean(global)); + defineReadonlyProperty(engine->identifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase())); + defineReadonlyProperty(engine->identifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); +} + + +RegExpCtor::RegExpCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value RegExpCtor::construct(ExecutionContext *ctx) +{ + Value r = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); + Value f = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); + if (RegExpObject *re = r.asRegExpObject()) { + if (!f.isUndefined()) + ctx->throwTypeError(); + + RegExpObject *o = ctx->engine->newRegExpObject(re->value, re->global); + return Value::fromObject(o); + } + + if (r.isUndefined()) + r = Value::fromString(ctx, QString()); + else if (!r.isString()) + r = __qmljs_to_string(r, ctx); + + bool global = false; + bool ignoreCase = false; + bool multiLine = false; + if (!f.isUndefined()) { + f = __qmljs_to_string(f, ctx); + QString str = f.stringValue()->toQString(); + for (int i = 0; i < str.length(); ++i) { + if (str.at(i) == QChar('g') && !global) { + global = true; + } else if (str.at(i) == QChar('i') && !ignoreCase) { + ignoreCase = true; + } else if (str.at(i) == QChar('m') && !multiLine) { + multiLine = true; + } else { + ctx->throwSyntaxError(0); + } + } + } + + RefPtr re = RegExp::create(ctx->engine, r.stringValue()->toQString(), ignoreCase, multiLine); + if (!re->isValid()) + ctx->throwSyntaxError(0); + + RegExpObject *o = ctx->engine->newRegExpObject(re, global); + return Value::fromObject(o); +} + +Value RegExpCtor::call(ExecutionContext *ctx) +{ + if (ctx->argumentCount > 0 && ctx->argument(0).asRegExpObject()) { + if (ctx->argumentCount == 1 || ctx->argument(1).isUndefined()) + return ctx->argument(0); + } + + return construct(ctx); +} + +void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(2)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 1); + defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 1); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("compile"), method_compile, 2); +} + +Value RegExpPrototype::method_exec(ExecutionContext *ctx) +{ + RegExpObject *r = ctx->thisObject.asRegExpObject(); + if (!r) + ctx->throwTypeError(); + + Value arg = ctx->argument(0); + arg = __qmljs_to_string(arg, ctx); + QString s = arg.stringValue()->toQString(); + + int offset = r->global ? r->lastIndexProperty->value.toInt32(ctx) : 0; + if (offset < 0 || offset > s.length()) { + r->lastIndexProperty->value = Value::fromInt32(0); + return Value::nullValue(); + } + + uint* matchOffsets = (uint*)alloca(r->value->captureCount() * 2 * sizeof(uint)); + int result = r->value->match(s, offset, matchOffsets); + if (result == -1) { + r->lastIndexProperty->value = Value::fromInt32(0); + return Value::nullValue(); + } + + // fill in result data + ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); + for (int i = 0; i < r->value->captureCount(); ++i) { + int start = matchOffsets[i * 2]; + int end = matchOffsets[i * 2 + 1]; + Value entry = Value::undefinedValue(); + if (start != -1 && end != -1) + entry = Value::fromString(ctx, s.mid(start, end - start)); + array->array.push_back(entry); + } + + array->__put__(ctx, QLatin1String("index"), Value::fromInt32(result)); + array->__put__(ctx, QLatin1String("input"), arg); + + if (r->global) + r->lastIndexProperty->value = Value::fromInt32(matchOffsets[1]); + + return Value::fromObject(array); +} + +Value RegExpPrototype::method_test(ExecutionContext *ctx) +{ + Value r = method_exec(ctx); + return Value::fromBoolean(!r.isNull()); +} + +Value RegExpPrototype::method_toString(ExecutionContext *ctx) +{ + RegExpObject *r = ctx->thisObject.asRegExpObject(); + if (!r) + ctx->throwTypeError(); + + QString result = QChar('/') + r->value->pattern(); + result += QChar('/'); + // ### 'g' option missing + if (r->value->ignoreCase()) + result += QChar('i'); + if (r->value->multiLine()) + result += QChar('m'); + return Value::fromString(ctx, result); +} + +Value RegExpPrototype::method_compile(ExecutionContext *ctx) +{ + RegExpObject *r = ctx->thisObject.asRegExpObject(); + if (!r) + ctx->throwTypeError(); + + RegExpObject *re = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ctx->arguments, ctx->argumentCount).asRegExpObject(); + + r->value = re->value; + r->global = re->global; + return Value::undefinedValue(); +} + diff --git a/qv4regexpobject.h b/qv4regexpobject.h new file mode 100644 index 0000000000..e5a7ca9e37 --- /dev/null +++ b/qv4regexpobject.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4REGEXPOBJECT_H +#define QV4REGEXPOBJECT_H + +#include "qmljs_runtime.h" +#include "qmljs_engine.h" +#include "qmljs_environment.h" +#include "qv4functionobject.h" +#include "qv4array.h" +#include "qv4string.h" +#include "qv4codegen_p.h" +#include "qv4isel_p.h" +#include "qv4managed.h" +#include "qv4propertydescriptor.h" +#include "qv4propertytable.h" +#include "qv4objectiterator.h" +#include "qv4regexp.h" + +#include +#include +#include +#include +#include + +namespace QQmlJS { + +namespace VM { + +struct RegExpObject: Object { + RefPtr value; + PropertyDescriptor *lastIndexProperty; + bool global; + RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global); + virtual QString className() { return QStringLiteral("RegExp"); } + virtual RegExpObject *asRegExpObject() { return this; } +}; + + +struct RegExpCtor: FunctionObject +{ + RegExpCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct RegExpPrototype: RegExpObject +{ + RegExpPrototype(ExecutionEngine* engine): RegExpObject(engine, RegExp::create(0, QString()), false) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_exec(ExecutionContext *ctx); + static Value method_test(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *ctx); + static Value method_compile(ExecutionContext *ctx); +}; + + + +} // namespace VM +} // namespace QQmlJS + +#endif // QMLJS_OBJECTS_H diff --git a/qv4stringobject.cpp b/qv4stringobject.cpp index 4f68e9d3f3..36030fbd46 100644 --- a/qv4stringobject.cpp +++ b/qv4stringobject.cpp @@ -41,6 +41,7 @@ #include "qv4stringobject.h" +#include "qv4regexpobject.h" #include "qv4ecmaobjects_p.h" #include "qv4mm.h" #include diff --git a/v4.pro b/v4.pro index 42c4924288..2d1f575d1c 100644 --- a/v4.pro +++ b/v4.pro @@ -33,6 +33,7 @@ SOURCES += main.cpp \ qv4functionobject.cpp \ qv4globalobject.cpp \ qv4jsonobject.cpp \ + qv4regexpobject.cpp \ qv4stringobject.cpp \ qv4string.cpp \ qv4objectiterator.cpp \ @@ -62,6 +63,7 @@ HEADERS += \ qv4functionobject.h \ qv4globalobject.h \ qv4jsonobject.h \ + qv4regexpobject.h \ qv4stringobject.h \ qv4string.h \ qv4propertydescriptor.h \ -- cgit v1.2.3 From fc0ce315b38dfb752cf920c4429ce74a54876cb4 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 21:48:51 +0100 Subject: Move math object into it's own file Change-Id: I01f00659f01ff11a401dedf1ce27b16d31d0f4f9 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 1 + qv4ecmaobjects.cpp | 253 --------------------------------------------- qv4ecmaobjects_p.h | 23 ----- qv4mathobject.cpp | 297 +++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4mathobject.h | 77 ++++++++++++++ v4.pro | 2 + 6 files changed, 377 insertions(+), 276 deletions(-) create mode 100644 qv4mathobject.cpp create mode 100644 qv4mathobject.h diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 2ad3b97f75..9dc0eb3a0f 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include "qv4mm.h" diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index d92ada9e34..206d349b9d 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -47,9 +47,6 @@ #include #include #include -#include -#include -#include #include #include @@ -73,7 +70,6 @@ using namespace QQmlJS::VM; -static const double qt_PI = 2.0 * ::asin(1.0); // // Object @@ -1465,252 +1461,3 @@ Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) return acc; } -// -// Math object -// -MathObject::MathObject(ExecutionContext *ctx) -{ - defineReadonlyProperty(ctx->engine, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("LN10"), Value::fromDouble(::log(10.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("LOG2E"), Value::fromDouble(1.0/::log(2.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("LOG10E"), Value::fromDouble(1.0/::log(10.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("PI"), Value::fromDouble(qt_PI)); - defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT1_2"), Value::fromDouble(::sqrt(0.5))); - defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT2"), Value::fromDouble(::sqrt(2.0))); - - defineDefaultProperty(ctx, QStringLiteral("abs"), method_abs, 1); - defineDefaultProperty(ctx, QStringLiteral("acos"), method_acos, 1); - defineDefaultProperty(ctx, QStringLiteral("asin"), method_asin, 0); - defineDefaultProperty(ctx, QStringLiteral("atan"), method_atan, 1); - defineDefaultProperty(ctx, QStringLiteral("atan2"), method_atan2, 2); - defineDefaultProperty(ctx, QStringLiteral("ceil"), method_ceil, 1); - defineDefaultProperty(ctx, QStringLiteral("cos"), method_cos, 1); - defineDefaultProperty(ctx, QStringLiteral("exp"), method_exp, 1); - defineDefaultProperty(ctx, QStringLiteral("floor"), method_floor, 1); - defineDefaultProperty(ctx, QStringLiteral("log"), method_log, 1); - defineDefaultProperty(ctx, QStringLiteral("max"), method_max, 2); - defineDefaultProperty(ctx, QStringLiteral("min"), method_min, 2); - defineDefaultProperty(ctx, QStringLiteral("pow"), method_pow, 2); - defineDefaultProperty(ctx, QStringLiteral("random"), method_random, 0); - defineDefaultProperty(ctx, QStringLiteral("round"), method_round, 1); - defineDefaultProperty(ctx, QStringLiteral("sin"), method_sin, 1); - defineDefaultProperty(ctx, QStringLiteral("sqrt"), method_sqrt, 1); - defineDefaultProperty(ctx, QStringLiteral("tan"), method_tan, 1); -} - -/* copies the sign from y to x and returns the result */ -static double copySign(double x, double y) -{ - uchar *xch = (uchar *)&x; - uchar *ych = (uchar *)&y; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - xch[0] = (xch[0] & 0x7f) | (ych[0] & 0x80); - else - xch[7] = (xch[7] & 0x7f) | (ych[7] & 0x80); - return x; -} - -Value MathObject::method_abs(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (v == 0) // 0 | -0 - return Value::fromDouble(0); - - return Value::fromDouble(v < 0 ? -v : v); -} - -Value MathObject::method_acos(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (v > 1) - return Value::fromDouble(qSNaN()); - - return Value::fromDouble(::acos(v)); -} - -Value MathObject::method_asin(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (v > 1) - return Value::fromDouble(qSNaN()); - else - return Value::fromDouble(::asin(v)); -} - -Value MathObject::method_atan(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (v == 0.0) - return Value::fromDouble(v); - else - return Value::fromDouble(::atan(v)); -} - -Value MathObject::method_atan2(ExecutionContext *ctx) -{ - double v1 = ctx->argument(0).toNumber(ctx); - double v2 = ctx->argument(1).toNumber(ctx); - if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) { - return Value::fromDouble(copySign(0, -1.0)); - } - if ((v1 == 0.0) && (v2 == 0.0)) { - if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) { - return Value::fromDouble(qt_PI); - } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) { - return Value::fromDouble(-qt_PI); - } - } - return Value::fromDouble(::atan2(v1, v2)); -} - -Value MathObject::method_ceil(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (v < 0.0 && v > -1.0) - return Value::fromDouble(copySign(0, -1.0)); - else - return Value::fromDouble(::ceil(v)); -} - -Value MathObject::method_cos(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - return Value::fromDouble(::cos(v)); -} - -Value MathObject::method_exp(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (qIsInf(v)) { - if (copySign(1.0, v) == -1.0) - return Value::fromDouble(0); - else - return Value::fromDouble(qInf()); - } else { - return Value::fromDouble(::exp(v)); - } -} - -Value MathObject::method_floor(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - return Value::fromDouble(::floor(v)); -} - -Value MathObject::method_log(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (v < 0) - return Value::fromDouble(qSNaN()); - else - return Value::fromDouble(::log(v)); -} - -Value MathObject::method_max(ExecutionContext *ctx) -{ - double mx = -qInf(); - for (unsigned i = 0; i < ctx->argumentCount; ++i) { - double x = ctx->argument(i).toNumber(ctx); - if (x > mx || std::isnan(x)) - mx = x; - } - return Value::fromDouble(mx); -} - -Value MathObject::method_min(ExecutionContext *ctx) -{ - double mx = qInf(); - for (unsigned i = 0; i < ctx->argumentCount; ++i) { - double x = ctx->argument(i).toNumber(ctx); - if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) - || (x < mx) || std::isnan(x)) { - mx = x; - } - } - return Value::fromDouble(mx); -} - -Value MathObject::method_pow(ExecutionContext *ctx) -{ - double x = ctx->argument(0).toNumber(ctx); - double y = ctx->argument(1).toNumber(ctx); - - if (std::isnan(y)) - return Value::fromDouble(qSNaN()); - - if (y == 0) { - return Value::fromDouble(1); - } else if (((x == 1) || (x == -1)) && std::isinf(y)) { - return Value::fromDouble(qSNaN()); - } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { - return Value::fromDouble(qInf()); - } else if ((x == 0) && copySign(1.0, x) == -1.0) { - if (y < 0) { - if (::fmod(-y, 2.0) == 1.0) - return Value::fromDouble(-qInf()); - else - return Value::fromDouble(qInf()); - } else if (y > 0) { - if (::fmod(y, 2.0) == 1.0) - return Value::fromDouble(copySign(0, -1.0)); - else - return Value::fromDouble(0); - } - } - -#ifdef Q_OS_AIX - else if (qIsInf(x) && copySign(1.0, x) == -1.0) { - if (y > 0) { - if (::fmod(y, 2.0) == 1.0) - return Value::number(ctx, -qInf()); - else - return Value::number(ctx, qInf()); - } else if (y < 0) { - if (::fmod(-y, 2.0) == 1.0) - return Value::number(ctx, copySign(0, -1.0)); - else - return Value::number(ctx, 0); - } - } -#endif - else { - return Value::fromDouble(::pow(x, y)); - } - // ### - return Value::fromDouble(qSNaN()); -} - -Value MathObject::method_random(ExecutionContext */*ctx*/) -{ - return Value::fromDouble(qrand() / (double) RAND_MAX); -} - -Value MathObject::method_round(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - v = copySign(::floor(v + 0.5), v); - return Value::fromDouble(v); -} - -Value MathObject::method_sin(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - return Value::fromDouble(::sin(v)); -} - -Value MathObject::method_sqrt(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - return Value::fromDouble(::sqrt(v)); -} - -Value MathObject::method_tan(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (v == 0.0) - return Value::fromDouble(v); - else - return Value::fromDouble(::tan(v)); -} - diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index dc4fcd5164..d17bfc433e 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -165,29 +165,6 @@ struct ArrayPrototype: ArrayObject static Value method_reduceRight(ExecutionContext *ctx); }; -struct MathObject: Object -{ - MathObject(ExecutionContext *ctx); - - static Value method_abs(ExecutionContext *ctx); - static Value method_acos(ExecutionContext *ctx); - static Value method_asin(ExecutionContext *ctx); - static Value method_atan(ExecutionContext *ctx); - static Value method_atan2(ExecutionContext *ctx); - static Value method_ceil(ExecutionContext *ctx); - static Value method_cos(ExecutionContext *ctx); - static Value method_exp(ExecutionContext *ctx); - static Value method_floor(ExecutionContext *ctx); - static Value method_log(ExecutionContext *ctx); - static Value method_max(ExecutionContext *ctx); - static Value method_min(ExecutionContext *ctx); - static Value method_pow(ExecutionContext *ctx); - static Value method_random(ExecutionContext *ctx); - static Value method_round(ExecutionContext *ctx); - static Value method_sin(ExecutionContext *ctx); - static Value method_sqrt(ExecutionContext *ctx); - static Value method_tan(ExecutionContext *ctx); -}; } // end of namespace VM } // end of namespace QQmlJS diff --git a/qv4mathobject.cpp b/qv4mathobject.cpp new file mode 100644 index 0000000000..2eefb51616 --- /dev/null +++ b/qv4mathobject.cpp @@ -0,0 +1,297 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4mathobject.h" + +#include +#include +#include + +using namespace QQmlJS::VM; + +static const double qt_PI = 2.0 * ::asin(1.0); + +MathObject::MathObject(ExecutionContext *ctx) +{ + defineReadonlyProperty(ctx->engine, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LN10"), Value::fromDouble(::log(10.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LOG2E"), Value::fromDouble(1.0/::log(2.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LOG10E"), Value::fromDouble(1.0/::log(10.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("PI"), Value::fromDouble(qt_PI)); + defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT1_2"), Value::fromDouble(::sqrt(0.5))); + defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT2"), Value::fromDouble(::sqrt(2.0))); + + defineDefaultProperty(ctx, QStringLiteral("abs"), method_abs, 1); + defineDefaultProperty(ctx, QStringLiteral("acos"), method_acos, 1); + defineDefaultProperty(ctx, QStringLiteral("asin"), method_asin, 0); + defineDefaultProperty(ctx, QStringLiteral("atan"), method_atan, 1); + defineDefaultProperty(ctx, QStringLiteral("atan2"), method_atan2, 2); + defineDefaultProperty(ctx, QStringLiteral("ceil"), method_ceil, 1); + defineDefaultProperty(ctx, QStringLiteral("cos"), method_cos, 1); + defineDefaultProperty(ctx, QStringLiteral("exp"), method_exp, 1); + defineDefaultProperty(ctx, QStringLiteral("floor"), method_floor, 1); + defineDefaultProperty(ctx, QStringLiteral("log"), method_log, 1); + defineDefaultProperty(ctx, QStringLiteral("max"), method_max, 2); + defineDefaultProperty(ctx, QStringLiteral("min"), method_min, 2); + defineDefaultProperty(ctx, QStringLiteral("pow"), method_pow, 2); + defineDefaultProperty(ctx, QStringLiteral("random"), method_random, 0); + defineDefaultProperty(ctx, QStringLiteral("round"), method_round, 1); + defineDefaultProperty(ctx, QStringLiteral("sin"), method_sin, 1); + defineDefaultProperty(ctx, QStringLiteral("sqrt"), method_sqrt, 1); + defineDefaultProperty(ctx, QStringLiteral("tan"), method_tan, 1); +} + +/* copies the sign from y to x and returns the result */ +static double copySign(double x, double y) +{ + uchar *xch = (uchar *)&x; + uchar *ych = (uchar *)&y; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + xch[0] = (xch[0] & 0x7f) | (ych[0] & 0x80); + else + xch[7] = (xch[7] & 0x7f) | (ych[7] & 0x80); + return x; +} + +Value MathObject::method_abs(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v == 0) // 0 | -0 + return Value::fromDouble(0); + + return Value::fromDouble(v < 0 ? -v : v); +} + +Value MathObject::method_acos(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v > 1) + return Value::fromDouble(qSNaN()); + + return Value::fromDouble(::acos(v)); +} + +Value MathObject::method_asin(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v > 1) + return Value::fromDouble(qSNaN()); + else + return Value::fromDouble(::asin(v)); +} + +Value MathObject::method_atan(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v == 0.0) + return Value::fromDouble(v); + else + return Value::fromDouble(::atan(v)); +} + +Value MathObject::method_atan2(ExecutionContext *ctx) +{ + double v1 = ctx->argument(0).toNumber(ctx); + double v2 = ctx->argument(1).toNumber(ctx); + if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) { + return Value::fromDouble(copySign(0, -1.0)); + } + if ((v1 == 0.0) && (v2 == 0.0)) { + if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) { + return Value::fromDouble(qt_PI); + } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) { + return Value::fromDouble(-qt_PI); + } + } + return Value::fromDouble(::atan2(v1, v2)); +} + +Value MathObject::method_ceil(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v < 0.0 && v > -1.0) + return Value::fromDouble(copySign(0, -1.0)); + else + return Value::fromDouble(::ceil(v)); +} + +Value MathObject::method_cos(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + return Value::fromDouble(::cos(v)); +} + +Value MathObject::method_exp(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (qIsInf(v)) { + if (copySign(1.0, v) == -1.0) + return Value::fromDouble(0); + else + return Value::fromDouble(qInf()); + } else { + return Value::fromDouble(::exp(v)); + } +} + +Value MathObject::method_floor(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + return Value::fromDouble(::floor(v)); +} + +Value MathObject::method_log(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v < 0) + return Value::fromDouble(qSNaN()); + else + return Value::fromDouble(::log(v)); +} + +Value MathObject::method_max(ExecutionContext *ctx) +{ + double mx = -qInf(); + for (unsigned i = 0; i < ctx->argumentCount; ++i) { + double x = ctx->argument(i).toNumber(ctx); + if (x > mx || std::isnan(x)) + mx = x; + } + return Value::fromDouble(mx); +} + +Value MathObject::method_min(ExecutionContext *ctx) +{ + double mx = qInf(); + for (unsigned i = 0; i < ctx->argumentCount; ++i) { + double x = ctx->argument(i).toNumber(ctx); + if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) + || (x < mx) || std::isnan(x)) { + mx = x; + } + } + return Value::fromDouble(mx); +} + +Value MathObject::method_pow(ExecutionContext *ctx) +{ + double x = ctx->argument(0).toNumber(ctx); + double y = ctx->argument(1).toNumber(ctx); + + if (std::isnan(y)) + return Value::fromDouble(qSNaN()); + + if (y == 0) { + return Value::fromDouble(1); + } else if (((x == 1) || (x == -1)) && std::isinf(y)) { + return Value::fromDouble(qSNaN()); + } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { + return Value::fromDouble(qInf()); + } else if ((x == 0) && copySign(1.0, x) == -1.0) { + if (y < 0) { + if (::fmod(-y, 2.0) == 1.0) + return Value::fromDouble(-qInf()); + else + return Value::fromDouble(qInf()); + } else if (y > 0) { + if (::fmod(y, 2.0) == 1.0) + return Value::fromDouble(copySign(0, -1.0)); + else + return Value::fromDouble(0); + } + } + +#ifdef Q_OS_AIX + else if (qIsInf(x) && copySign(1.0, x) == -1.0) { + if (y > 0) { + if (::fmod(y, 2.0) == 1.0) + return Value::number(ctx, -qInf()); + else + return Value::number(ctx, qInf()); + } else if (y < 0) { + if (::fmod(-y, 2.0) == 1.0) + return Value::number(ctx, copySign(0, -1.0)); + else + return Value::number(ctx, 0); + } + } +#endif + else { + return Value::fromDouble(::pow(x, y)); + } + // ### + return Value::fromDouble(qSNaN()); +} + +Value MathObject::method_random(ExecutionContext */*ctx*/) +{ + return Value::fromDouble(qrand() / (double) RAND_MAX); +} + +Value MathObject::method_round(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + v = copySign(::floor(v + 0.5), v); + return Value::fromDouble(v); +} + +Value MathObject::method_sin(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + return Value::fromDouble(::sin(v)); +} + +Value MathObject::method_sqrt(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + return Value::fromDouble(::sqrt(v)); +} + +Value MathObject::method_tan(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v == 0.0) + return Value::fromDouble(v); + else + return Value::fromDouble(::tan(v)); +} + diff --git a/qv4mathobject.h b/qv4mathobject.h new file mode 100644 index 0000000000..5f3f56f7d6 --- /dev/null +++ b/qv4mathobject.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4MATHOBJECT_H +#define QV$MATHOBJECT_H + +#include "qmljs_objects.h" + +namespace QQmlJS { + +namespace VM { + +struct MathObject: Object +{ + MathObject(ExecutionContext *ctx); + + static Value method_abs(ExecutionContext *ctx); + static Value method_acos(ExecutionContext *ctx); + static Value method_asin(ExecutionContext *ctx); + static Value method_atan(ExecutionContext *ctx); + static Value method_atan2(ExecutionContext *ctx); + static Value method_ceil(ExecutionContext *ctx); + static Value method_cos(ExecutionContext *ctx); + static Value method_exp(ExecutionContext *ctx); + static Value method_floor(ExecutionContext *ctx); + static Value method_log(ExecutionContext *ctx); + static Value method_max(ExecutionContext *ctx); + static Value method_min(ExecutionContext *ctx); + static Value method_pow(ExecutionContext *ctx); + static Value method_random(ExecutionContext *ctx); + static Value method_round(ExecutionContext *ctx); + static Value method_sin(ExecutionContext *ctx); + static Value method_sqrt(ExecutionContext *ctx); + static Value method_tan(ExecutionContext *ctx); +}; + +} // namespace VM +} // namespace QQmlJS + +#endif // QMLJS_OBJECTS_H diff --git a/v4.pro b/v4.pro index 2d1f575d1c..69c4a0c1d3 100644 --- a/v4.pro +++ b/v4.pro @@ -33,6 +33,7 @@ SOURCES += main.cpp \ qv4functionobject.cpp \ qv4globalobject.cpp \ qv4jsonobject.cpp \ + qv4mathobject.cpp \ qv4regexpobject.cpp \ qv4stringobject.cpp \ qv4string.cpp \ @@ -63,6 +64,7 @@ HEADERS += \ qv4functionobject.h \ qv4globalobject.h \ qv4jsonobject.h \ + qv4mathobject.h \ qv4regexpobject.h \ qv4stringobject.h \ qv4string.h \ -- cgit v1.2.3 From 8d3c58600a356b447e56fa7d7d09ebc40cafe69f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 21:54:53 +0100 Subject: Move qmljs_object.* to qv4object.* Change-Id: I846958875eaa2feae51e3a70290a197dd40bcb12 Reviewed-by: Simon Hausmann --- debugging.cpp | 2 +- main.cpp | 2 +- moth/qv4instr_moth_p.h | 2 +- moth/qv4isel_moth_p.h | 2 +- qmljs_engine.cpp | 2 +- qmljs_engine.h | 2 +- qmljs_environment.cpp | 2 +- qmljs_objects.cpp | 670 ------------------------------------------------- qmljs_objects.h | 210 ---------------- qmljs_runtime.cpp | 2 +- qmljs_value.cpp | 2 +- qv4argumentsobject.h | 2 +- qv4array.cpp | 2 +- qv4dateobject.h | 2 +- qv4ecmaobjects_p.h | 2 +- qv4errorobject.h | 2 +- qv4functionobject.cpp | 2 +- qv4functionobject.h | 2 +- qv4isel_masm.cpp | 2 +- qv4isel_masm_p.h | 2 +- qv4jsonobject.h | 2 +- qv4mathobject.h | 2 +- qv4mm.cpp | 2 +- qv4mm.h | 2 +- qv4object.cpp | 670 +++++++++++++++++++++++++++++++++++++++++++++++++ qv4object.h | 210 ++++++++++++++++ qv4objectiterator.cpp | 2 +- qv4stringobject.h | 2 +- v4.pro | 4 +- 29 files changed, 906 insertions(+), 906 deletions(-) delete mode 100644 qmljs_objects.cpp delete mode 100644 qmljs_objects.h create mode 100644 qv4object.cpp create mode 100644 qv4object.h diff --git a/debugging.cpp b/debugging.cpp index bcebe445c4..61ae0e62d0 100644 --- a/debugging.cpp +++ b/debugging.cpp @@ -28,7 +28,7 @@ ****************************************************************************/ #include "debugging.h" -#include "qmljs_objects.h" +#include "qv4object.h" #include "qv4functionobject.h" #include diff --git a/main.cpp b/main.cpp index 93f43128e1..3f3e2df695 100644 --- a/main.cpp +++ b/main.cpp @@ -44,7 +44,7 @@ #endif #include "debugging.h" -#include "qmljs_objects.h" +#include "qv4object.h" #include "qmljs_runtime.h" #include "qv4functionobject.h" #include "qv4errorobject.h" diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 8136a68d24..e3cc8430e5 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -2,7 +2,7 @@ #define QV4INSTR_MOTH_P_H #include -#include "qmljs_objects.h" +#include "qv4object.h" #define FOR_EACH_MOTH_INSTR(F) \ F(Ret, ret) \ diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index f02914fea6..e1090645ba 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -3,7 +3,7 @@ #include "qv4isel_p.h" #include "qv4ir_p.h" -#include "qmljs_objects.h" +#include "qv4object.h" #include "qv4instr_moth_p.h" namespace QQmlJS { diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 9dc0eb3a0f..231c6b843e 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ #include -#include +#include #include #include #include diff --git a/qmljs_engine.h b/qmljs_engine.h index 72722e0fd0..fbb6e48134 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -42,7 +42,7 @@ #define QMLJS_ENGINE_H #include -#include +#include #include #include diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index bdfd68519b..b756f9796d 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -42,7 +42,7 @@ #include #include "debugging.h" #include -#include +#include #include #include "qv4mm.h" #include diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp deleted file mode 100644 index eb93cf8eb7..0000000000 --- a/qmljs_objects.cpp +++ /dev/null @@ -1,670 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qmljs_objects.h" -#include "qv4ir_p.h" -#include "qv4isel_p.h" -#include "qv4ecmaobjects_p.h" -#include "qv4stringobject.h" -#include "qv4mm.h" - -#include -#include -#include -#include -#include -#include -#include "private/qlocale_tools_p.h" - -#include -#include -#include -#include -#include -#include - -using namespace QQmlJS::VM; - - -// -// Object -// -Object::~Object() -{ -} - -void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &value) -{ - __put__(ctx, ctx->engine->identifier(name), value); -} - -Value Object::getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const -{ - if (p->isData()) - return p->value; - if (!p->get) - return Value::undefinedValue(); - - return p->get->call(ctx, Value::fromObject(const_cast(this)), 0, 0); -} - -Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const -{ - if (!p || p->type == PropertyDescriptor::Generic) - return Value::undefinedValue(); - return getValue(ctx, p); -} - -Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const -{ - *exists = p && p->type != PropertyDescriptor::Generic; - if (!*exists) - return Value::undefinedValue(); - return getValue(ctx, p); -} - -bool Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx) -{ - bool hasProperty = false; - Value v = __get__(ctx, name, &hasProperty); - if (!hasProperty) - return false; - Value result = op(v, rhs, ctx); - __put__(ctx, name, result); - return true; -} - -bool Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) -{ - uint idx = index.asArrayIndex(); - if (idx < UINT_MAX) { - bool hasProperty = false; - Value v = __get__(ctx, idx, &hasProperty); - if (!hasProperty) - return false; - v = op(v, rhs, ctx); - __put__(ctx, idx, v); - return true; - } - String *name = index.toString(ctx); - assert(name); - return inplaceBinOp(rhs, name, op, ctx); -} - -void Object::defineDefaultProperty(String *name, Value value) -{ - if (!members) - members.reset(new PropertyTable()); - PropertyDescriptor *pd = members->insert(name); - pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Enabled; - pd->enumberable = PropertyDescriptor::Disabled; - pd->configurable = PropertyDescriptor::Enabled; - pd->value = value; -} - -void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value value) -{ - defineDefaultProperty(context->engine->identifier(name), value); -} - -void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int argumentCount) -{ - Q_UNUSED(argumentCount); - String *s = context->engine->identifier(name); - FunctionObject* function = context->engine->newBuiltinFunction(context, s, code); - function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); - defineDefaultProperty(s, Value::fromObject(function)); -} - -void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value) -{ - defineReadonlyProperty(engine->identifier(name), value); -} - -void Object::defineReadonlyProperty(String *name, Value value) -{ - if (!members) - members.reset(new PropertyTable()); - PropertyDescriptor *pd = members->insert(name); - pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Disabled; - pd->enumberable = PropertyDescriptor::Disabled; - pd->configurable = PropertyDescriptor::Disabled; - pd->value = value; -} - -void Object::getCollectables(QVector &objects) -{ - if (prototype) - objects.append(prototype); - - if (members) { - for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) { - if (!(*it)) - continue; - PropertyDescriptor &pd = (*it)->descriptor; - if (pd.isData()) { - if (Object *o = pd.value.asObject()) - objects.append(o); - } else if (pd.isAccessor()) { - if (pd.get) - objects.append(pd.get); - if (pd.set) - objects.append(pd.set); - } - } - } - array.getCollectables(objects); -} - -// Section 8.12.1 -PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *name) -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __getOwnProperty__(ctx, idx); - - if (members) - return members->find(name); - return 0; -} - -PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, uint index) -{ - PropertyDescriptor *p = array.at(index); - if(p && p->type != PropertyDescriptor::Generic) - return p; - if (isString) - return static_cast(this)->getIndex(ctx, index); - - return 0; -} - -// Section 8.12.2 -PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name) -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __getPropertyDescriptor__(ctx, idx); - - - Object *o = this; - while (o) { - if (o->members) { - if (PropertyDescriptor *p = o->members->find(name)) - return p; - } - o = o->prototype; - } - return 0; -} - -PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, uint index) -{ - Object *o = this; - while (o) { - PropertyDescriptor *p = o->array.at(index); - if(p && p->type != PropertyDescriptor::Generic) - return p; - if (o->isString) { - p = static_cast(o)->getIndex(ctx, index); - if (p) - return p; - } - o = o->prototype; - } - return 0; -} - -// Section 8.12.3 -Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __get__(ctx, idx, hasProperty); - - if (name->isEqualTo(ctx->engine->id___proto__)) { - if (hasProperty) - *hasProperty = true; - return Value::fromObject(prototype); - } - - if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name)) { - if (hasProperty) - *hasProperty = true; - return getValue(ctx, p); - } - - if (hasProperty) - *hasProperty = false; - return Value::undefinedValue(); -} - -Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) -{ - const PropertyDescriptor *p = __getPropertyDescriptor__(ctx, index); - if (p && p->type != PropertyDescriptor::Generic) { - if (hasProperty) - *hasProperty = true; - return getValue(ctx, p); - } - - if (hasProperty) - *hasProperty = false; - return Value::undefinedValue(); -} - - -// Section 8.12.5 -void Object::__put__(ExecutionContext *ctx, String *name, Value value) -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __put__(ctx, idx, value); - - PropertyDescriptor *pd = __getOwnProperty__(ctx, name); - // clause 1 - if (pd) { - if (pd->isAccessor()) { - if (pd->set) - goto cont; - goto reject; - } else if (!pd->isWritable()) - goto reject; - else if (isArray && name->isEqualTo(ctx->engine->id_length)) { - bool ok; - uint l = value.asArrayLength(ctx, &ok); - if (!ok) - ctx->throwRangeError(value); - ok = array.setLength(l); - if (!ok) - goto reject; - } else { - pd->value = value; - } - return; - } else if (!prototype) { - if (!extensible) - goto reject; - } else { - if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, name)) { - if (p->isAccessor()) { - if (p->set) - goto cont; - goto reject; - } - if (!extensible) - goto reject; - if (!p->isWritable()) - goto reject; - } else { - if (!extensible) - goto reject; - } - } - - cont: - - if (!members) - members.reset(new PropertyTable()); - - - // clause 4 - if (!pd && prototype) - pd = prototype->__getPropertyDescriptor__(ctx, name); - - // Clause 5 - if (pd && pd->isAccessor()) { - assert(pd->set != 0); - - Value args[1]; - args[0] = value; - pd->set->call(ctx, Value::fromObject(this), args, 1); - return; - } - - { - PropertyDescriptor *p = members->insert(name); - p->type = PropertyDescriptor::Data; - p->value = value; - p->configurable = PropertyDescriptor::Enabled; - p->enumberable = PropertyDescriptor::Enabled; - p->writable = PropertyDescriptor::Enabled; - return; - } - - reject: - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); -} - -void Object::__put__(ExecutionContext *ctx, uint index, Value value) -{ - PropertyDescriptor *pd = __getOwnProperty__(ctx, index); - // clause 1 - if (pd) { - if (pd->isAccessor()) { - if (pd->set) - goto cont; - goto reject; - } else if (!pd->isWritable()) - goto reject; - else - pd->value = value; - return; - } else if (!prototype) { - if (!extensible) - goto reject; - } else { - if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, index)) { - if (p->isAccessor()) { - if (p->set) - goto cont; - goto reject; - } - if (!extensible) - goto reject; - if (!p->isWritable()) - goto reject; - } else { - if (!extensible) - goto reject; - } - } - - cont: - - // clause 4 - if (!pd && prototype) - pd = prototype->__getPropertyDescriptor__(ctx, index); - - // Clause 5 - if (pd && pd->isAccessor()) { - assert(pd->set != 0); - - Value args[1]; - args[0] = value; - pd->set->call(ctx, Value::fromObject(this), args, 1); - return; - } - - array.set(index, value); - return; - - reject: - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); -} - -// Section 8.12.6 -bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __hasProperty__(ctx, idx); - - if (members && members->find(name) != 0) - return true; - - return prototype ? prototype->__hasProperty__(ctx, name) : false; -} - -bool Object::__hasProperty__(const ExecutionContext *ctx, uint index) const -{ - const PropertyDescriptor *p = array.at(index); - if (p && p->type != PropertyDescriptor::Generic) - return true; - - return prototype ? prototype->__hasProperty__(ctx, index) : false; -} - -// Section 8.12.7 -bool Object::__delete__(ExecutionContext *ctx, String *name) -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __delete__(ctx, idx); - - if (members) { - if (PropertyTableEntry *entry = members->findEntry(name)) { - if (entry->descriptor.isConfigurable()) { - members->remove(entry); - return true; - } - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); - return false; - } - } - return true; -} - -bool Object::__delete__(ExecutionContext *ctx, uint index) -{ - if (array.deleteIndex(index)) - return true; - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); - return false; -} - -// Section 8.12.9 -bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc) -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __defineOwnProperty__(ctx, idx, desc); - - PropertyDescriptor *current; - - if (isArray && name->isEqualTo(ctx->engine->id_length)) { - PropertyDescriptor *lp = array.getLengthProperty(); - if (desc->isEmpty() || desc->isSubset(lp)) - return true; - if (!lp->isWritable() || desc->type == PropertyDescriptor::Accessor || desc->isConfigurable() || desc->isEnumerable()) - goto reject; - bool succeeded = true; - if (desc->type == PropertyDescriptor::Data) { - bool ok; - uint l = desc->value.asArrayLength(ctx, &ok); - if (!ok) - ctx->throwRangeError(desc->value); - succeeded = array.setLength(l); - } - if (desc->writable == PropertyDescriptor::Disabled) - lp->writable = PropertyDescriptor::Disabled; - if (!succeeded) - goto reject; - return true; - } - - if (!members) - members.reset(new PropertyTable()); - - // Clause 1 - current = __getOwnProperty__(ctx, name); - if (!current) { - // clause 3 - if (!extensible) - goto reject; - // clause 4 - PropertyDescriptor *pd = members->insert(name); - *pd = *desc; - pd->fullyPopulated(); - return true; - } - - return __defineOwnProperty__(ctx, current, desc); -reject: - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); - return false; -} - -bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc) -{ - PropertyDescriptor *current; - - // 15.4.5.1, 4b - if (isArray && index >= array.length() && !array.getLengthProperty()->isWritable()) - goto reject; - - // Clause 1 - current = __getOwnProperty__(ctx, index); - if (!current) { - // clause 3 - if (!extensible) - goto reject; - // clause 4 - PropertyDescriptor *pd = array.insert(index); - *pd = *desc; - pd->fullyPopulated(); - return true; - } - - return __defineOwnProperty__(ctx, current, desc); -reject: - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); - return false; -} - -bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc) -{ - // clause 5 - if (desc->isEmpty()) - return true; - - // clause 6 - if (desc->isSubset(current)) - return true; - - // clause 7 - if (!current->isConfigurable()) { - if (desc->isConfigurable()) - goto reject; - if (desc->enumberable != PropertyDescriptor::Undefined && desc->enumberable != current->enumberable) - goto reject; - } - - // clause 8 - if (desc->isGeneric()) - goto accept; - - // clause 9 - if (current->isData() != desc->isData()) { - // 9a - if (!current->isConfigurable()) - goto reject; - if (current->isData()) { - // 9b - current->type = PropertyDescriptor::Accessor; - current->writable = PropertyDescriptor::Undefined; - current->get = 0; - current->set = 0; - } else { - // 9c - current->type = PropertyDescriptor::Data; - current->writable = PropertyDescriptor::Disabled; - current->value = Value::undefinedValue(); - } - } else if (current->isData() && desc->isData()) { // clause 10 - if (!current->isConfigurable() && !current->isWritable()) { - if (desc->isWritable() || !current->value.sameValue(desc->value)) - goto reject; - } - } else { // clause 10 - assert(current->isAccessor() && desc->isAccessor()); - if (!current->isConfigurable()) { - if (desc->get && !(current->get == desc->get || (!current->get && (quintptr)desc->get == 0x1))) - goto reject; - if (desc->set && !(current->set == desc->set || (!current->set && (quintptr)desc->set == 0x1))) - goto reject; - } - } - - accept: - - *current += *desc; - return true; - reject: - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); - return false; -} - - -bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc) -{ - return __defineOwnProperty__(ctx, ctx->engine->identifier(name), desc); -} - - -Value Object::call(ExecutionContext *context, Value , Value *, int) -{ - context->throwTypeError(); - return Value::undefinedValue(); -} - -void ArrayObject::init(ExecutionContext *context) -{ - isArray = true; - if (!members) - members.reset(new PropertyTable()); - PropertyDescriptor *pd = members->insert(context->engine->id_length); - pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Enabled; - pd->enumberable = PropertyDescriptor::Disabled; - pd->configurable = PropertyDescriptor::Disabled; - pd->value = Value::fromInt32(0); - array.setLengthProperty(pd); -} - - - -void ForEachIteratorObject::getCollectables(QVector &objects) -{ - Object::getCollectables(objects); - if (it.object) - objects.append(it.object); -} - diff --git a/qmljs_objects.h b/qmljs_objects.h deleted file mode 100644 index 819b5d7f2a..0000000000 --- a/qmljs_objects.h +++ /dev/null @@ -1,210 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QMLJS_OBJECTS_H -#define QMLJS_OBJECTS_H - -#include "qmljs_runtime.h" -#include "qmljs_engine.h" -#include "qmljs_environment.h" -#include "qv4array.h" -#include "qv4string.h" -#include "qv4codegen_p.h" -#include "qv4isel_p.h" -#include "qv4managed.h" -#include "qv4propertydescriptor.h" -#include "qv4propertytable.h" -#include "qv4objectiterator.h" -#include "qv4regexp.h" - -#include -#include -#include -#include -#include - -namespace QQmlJS { - -namespace VM { - -struct Value; -struct Function; -struct Object; -struct ObjectIterator; -struct BooleanObject; -struct NumberObject; -struct StringObject; -struct ArrayObject; -struct DateObject; -struct FunctionObject; -struct RegExpObject; -struct ErrorObject; -struct ArgumentsObject; -struct ExecutionContext; -struct ExecutionEngine; -class MemoryManager; - -struct ObjectPrototype; -struct StringPrototype; -struct NumberPrototype; -struct BooleanPrototype; -struct ArrayPrototype; -struct FunctionPrototype; -struct DatePrototype; -struct RegExpPrototype; -struct ErrorPrototype; -struct EvalErrorPrototype; -struct RangeErrorPrototype; -struct ReferenceErrorPrototype; -struct SyntaxErrorPrototype; -struct TypeErrorPrototype; -struct URIErrorPrototype; - - -struct Object: Managed { - Object *prototype; - QScopedPointer members; - Array array; - - Object() - : prototype(0) {} - Object(const Array &a) - : prototype(0), array(a) {} - - virtual ~Object(); - - virtual QString className() { return QStringLiteral("Object"); } - virtual BooleanObject *asBooleanObject() { return 0; } - virtual NumberObject *asNumberObject() { return 0; } - virtual StringObject *asStringObject() { return 0; } - virtual DateObject *asDateObject() { return 0; } - virtual ArrayObject *asArrayObject() { return 0; } - virtual FunctionObject *asFunctionObject() { return 0; } - virtual RegExpObject *asRegExpObject() { return 0; } - virtual ErrorObject *asErrorObject() { return 0; } - virtual ArgumentsObject *asArgumentsObject() { return 0; } - - PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); - PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); - PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name); - PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, uint index); - - virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); - Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); - - void __put__(ExecutionContext *ctx, String *name, Value value); - void __put__(ExecutionContext *ctx, uint index, Value value); - - virtual bool __hasProperty__(const ExecutionContext *ctx, String *name) const; - virtual bool __hasProperty__(const ExecutionContext *ctx, uint index) const; - virtual bool __delete__(ExecutionContext *ctx, String *name); - virtual bool __delete__(ExecutionContext *ctx, uint index); - bool __defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc); - virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc); - bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); - bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc); - - virtual Value call(ExecutionContext *context, Value, Value *, int); - - // - // helpers - // - void __put__(ExecutionContext *ctx, const QString &name, const Value &value); - - Value getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const; - Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const; - Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const; - - bool inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); - bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); - - /* The spec default: Writable: true, Enumerable: false, Configurable: true */ - void defineDefaultProperty(String *name, Value value); - void defineDefaultProperty(ExecutionContext *context, const QString &name, Value value); - void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int count = 0); - /* Fixed: Writable: false, Enumerable: false, Configurable: false */ - void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value); - void defineReadonlyProperty(String *name, Value value); - -protected: - virtual void getCollectables(QVector &objects); - - friend struct ObjectIterator; -}; - -struct ForEachIteratorObject: Object { - ObjectIterator it; - ForEachIteratorObject(ExecutionContext *ctx, Object *o) - : it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) {} - virtual QString className() { return QStringLiteral("__ForEachIteratorObject"); } - - Value nextPropertyName() { return it.nextPropertyNameAsString(); } - -protected: - virtual void getCollectables(QVector &objects); -}; - -struct BooleanObject: Object { - Value value; - BooleanObject(const Value &value): value(value) {} - virtual QString className() { return QStringLiteral("Boolean"); } - virtual BooleanObject *asBooleanObject() { return this; } -}; - -struct NumberObject: Object { - Value value; - NumberObject(const Value &value): value(value) {} - virtual QString className() { return QStringLiteral("Number"); } - virtual NumberObject *asNumberObject() { return this; } -}; - -struct ArrayObject: Object { - ArrayObject(ExecutionContext *ctx) { init(ctx); } - ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setLengthUnchecked(array.length()); } - void init(ExecutionContext *context); - virtual QString className() { return QStringLiteral("Array"); } - virtual ArrayObject *asArrayObject() { return this; } -}; - - -} // namespace VM -} // namespace QQmlJS - -#endif // QMLJS_OBJECTS_H diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 9ba9b89b64..2c03f47523 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -41,7 +41,7 @@ #include "debugging.h" #include "qmljs_runtime.h" -#include "qmljs_objects.h" +#include "qv4object.h" #include "qv4ir_p.h" #include "qv4ecmaobjects_p.h" #include "private/qlocale_tools_p.h" diff --git a/qmljs_value.cpp b/qmljs_value.cpp index 9121ea8ec8..ebea41e0ff 100644 --- a/qmljs_value.cpp +++ b/qmljs_value.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ #include -#include +#include #include namespace QQmlJS { diff --git a/qv4argumentsobject.h b/qv4argumentsobject.h index b8f15b35f6..90af02f1b8 100644 --- a/qv4argumentsobject.h +++ b/qv4argumentsobject.h @@ -41,7 +41,7 @@ #ifndef QV4ARGUMENTSOBJECTS_H #define QV4ARGUMENTSOBJECTS_H -#include +#include #include namespace QQmlJS { diff --git a/qv4array.cpp b/qv4array.cpp index 1bbd266d62..15150cc9e3 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -41,7 +41,7 @@ #include "qv4array.h" #include "qmljs_runtime.h" -#include "qmljs_objects.h" +#include "qv4object.h" #include "qv4functionobject.h" #include diff --git a/qv4dateobject.h b/qv4dateobject.h index 11137555b3..204b8379b8 100644 --- a/qv4dateobject.h +++ b/qv4dateobject.h @@ -41,7 +41,7 @@ #ifndef QV4DATEOBJECT_P_H #define QV4DATEOBJECT_P_H -#include "qmljs_objects.h" +#include "qv4object.h" #include "qv4functionobject.h" #include diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index d17bfc433e..687601cce1 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -41,7 +41,7 @@ #ifndef QV4ECMAOBJECTS_P_H #define QV4ECMAOBJECTS_P_H -#include "qmljs_objects.h" +#include "qv4object.h" #include "qv4functionobject.h" #include diff --git a/qv4errorobject.h b/qv4errorobject.h index 95867e1260..66f2676a2a 100644 --- a/qv4errorobject.h +++ b/qv4errorobject.h @@ -41,7 +41,7 @@ #ifndef QV4ERROROBJECT_H #define QV4ERROROBJECT_H -#include "qmljs_objects.h" +#include "qv4object.h" #include "qv4functionobject.h" namespace QQmlJS { diff --git a/qv4functionobject.cpp b/qv4functionobject.cpp index 6d8b19f31b..01be72624e 100644 --- a/qv4functionobject.cpp +++ b/qv4functionobject.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ -#include "qmljs_objects.h" +#include "qv4object.h" #include "qv4ir_p.h" #include "qv4isel_p.h" #include "qv4ecmaobjects_p.h" diff --git a/qv4functionobject.h b/qv4functionobject.h index 775c84f8b8..727361ff69 100644 --- a/qv4functionobject.h +++ b/qv4functionobject.h @@ -44,7 +44,7 @@ #include "qmljs_runtime.h" #include "qmljs_engine.h" #include "qmljs_environment.h" -#include "qmljs_objects.h" +#include "qv4object.h" #include "qv4array.h" #include "qv4string.h" #include "qv4codegen_p.h" diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 8998618f15..085706b57e 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -41,7 +41,7 @@ #include "qv4isel_masm_p.h" #include "qmljs_runtime.h" -#include "qmljs_objects.h" +#include "qv4object.h" #include "qv4functionobject.h" #include "qv4regexpobject.h" diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 6bc0420b8e..192439bea6 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -44,7 +44,7 @@ #include "qv4ir_p.h" #include "qv4isel_p.h" #include "qv4isel_util_p.h" -#include "qmljs_objects.h" +#include "qv4object.h" #include "qmljs_runtime.h" #include diff --git a/qv4jsonobject.h b/qv4jsonobject.h index 1a6f24a0b2..193d4900a5 100644 --- a/qv4jsonobject.h +++ b/qv4jsonobject.h @@ -41,7 +41,7 @@ #ifndef QV4JSONOBJECTS_H #define QV4SJONOBJECTS_H -#include +#include namespace QQmlJS { namespace VM { diff --git a/qv4mathobject.h b/qv4mathobject.h index 5f3f56f7d6..68014f1f61 100644 --- a/qv4mathobject.h +++ b/qv4mathobject.h @@ -41,7 +41,7 @@ #ifndef QV4MATHOBJECT_H #define QV$MATHOBJECT_H -#include "qmljs_objects.h" +#include "qv4object.h" namespace QQmlJS { diff --git a/qv4mm.cpp b/qv4mm.cpp index fd7d86a92a..daac6e8a24 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -28,7 +28,7 @@ ****************************************************************************/ #include "qmljs_engine.h" -#include "qmljs_objects.h" +#include "qv4object.h" #include "qv4ecmaobjects_p.h" #include "qv4mm.h" #include "PageAllocation.h" diff --git a/qv4mm.h b/qv4mm.h index 33ffc883c0..5790ba4b61 100644 --- a/qv4mm.h +++ b/qv4mm.h @@ -30,7 +30,7 @@ #ifndef QV4GC_H #define QV4GC_H -#include "qmljs_objects.h" +#include "qv4object.h" #include diff --git a/qv4object.cpp b/qv4object.cpp new file mode 100644 index 0000000000..a8814b368c --- /dev/null +++ b/qv4object.cpp @@ -0,0 +1,670 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4object.h" +#include "qv4ir_p.h" +#include "qv4isel_p.h" +#include "qv4ecmaobjects_p.h" +#include "qv4stringobject.h" +#include "qv4mm.h" + +#include +#include +#include +#include +#include +#include +#include "private/qlocale_tools_p.h" + +#include +#include +#include +#include +#include +#include + +using namespace QQmlJS::VM; + + +// +// Object +// +Object::~Object() +{ +} + +void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &value) +{ + __put__(ctx, ctx->engine->identifier(name), value); +} + +Value Object::getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const +{ + if (p->isData()) + return p->value; + if (!p->get) + return Value::undefinedValue(); + + return p->get->call(ctx, Value::fromObject(const_cast(this)), 0, 0); +} + +Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const +{ + if (!p || p->type == PropertyDescriptor::Generic) + return Value::undefinedValue(); + return getValue(ctx, p); +} + +Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const +{ + *exists = p && p->type != PropertyDescriptor::Generic; + if (!*exists) + return Value::undefinedValue(); + return getValue(ctx, p); +} + +bool Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx) +{ + bool hasProperty = false; + Value v = __get__(ctx, name, &hasProperty); + if (!hasProperty) + return false; + Value result = op(v, rhs, ctx); + __put__(ctx, name, result); + return true; +} + +bool Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) +{ + uint idx = index.asArrayIndex(); + if (idx < UINT_MAX) { + bool hasProperty = false; + Value v = __get__(ctx, idx, &hasProperty); + if (!hasProperty) + return false; + v = op(v, rhs, ctx); + __put__(ctx, idx, v); + return true; + } + String *name = index.toString(ctx); + assert(name); + return inplaceBinOp(rhs, name, op, ctx); +} + +void Object::defineDefaultProperty(String *name, Value value) +{ + if (!members) + members.reset(new PropertyTable()); + PropertyDescriptor *pd = members->insert(name); + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Enabled; + pd->value = value; +} + +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value value) +{ + defineDefaultProperty(context->engine->identifier(name), value); +} + +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int argumentCount) +{ + Q_UNUSED(argumentCount); + String *s = context->engine->identifier(name); + FunctionObject* function = context->engine->newBuiltinFunction(context, s, code); + function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); + defineDefaultProperty(s, Value::fromObject(function)); +} + +void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value) +{ + defineReadonlyProperty(engine->identifier(name), value); +} + +void Object::defineReadonlyProperty(String *name, Value value) +{ + if (!members) + members.reset(new PropertyTable()); + PropertyDescriptor *pd = members->insert(name); + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Disabled; + pd->enumberable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Disabled; + pd->value = value; +} + +void Object::getCollectables(QVector &objects) +{ + if (prototype) + objects.append(prototype); + + if (members) { + for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) { + if (!(*it)) + continue; + PropertyDescriptor &pd = (*it)->descriptor; + if (pd.isData()) { + if (Object *o = pd.value.asObject()) + objects.append(o); + } else if (pd.isAccessor()) { + if (pd.get) + objects.append(pd.get); + if (pd.set) + objects.append(pd.set); + } + } + } + array.getCollectables(objects); +} + +// Section 8.12.1 +PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *name) +{ + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __getOwnProperty__(ctx, idx); + + if (members) + return members->find(name); + return 0; +} + +PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, uint index) +{ + PropertyDescriptor *p = array.at(index); + if(p && p->type != PropertyDescriptor::Generic) + return p; + if (isString) + return static_cast(this)->getIndex(ctx, index); + + return 0; +} + +// Section 8.12.2 +PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name) +{ + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __getPropertyDescriptor__(ctx, idx); + + + Object *o = this; + while (o) { + if (o->members) { + if (PropertyDescriptor *p = o->members->find(name)) + return p; + } + o = o->prototype; + } + return 0; +} + +PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, uint index) +{ + Object *o = this; + while (o) { + PropertyDescriptor *p = o->array.at(index); + if(p && p->type != PropertyDescriptor::Generic) + return p; + if (o->isString) { + p = static_cast(o)->getIndex(ctx, index); + if (p) + return p; + } + o = o->prototype; + } + return 0; +} + +// Section 8.12.3 +Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) +{ + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __get__(ctx, idx, hasProperty); + + if (name->isEqualTo(ctx->engine->id___proto__)) { + if (hasProperty) + *hasProperty = true; + return Value::fromObject(prototype); + } + + if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name)) { + if (hasProperty) + *hasProperty = true; + return getValue(ctx, p); + } + + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); +} + +Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) +{ + const PropertyDescriptor *p = __getPropertyDescriptor__(ctx, index); + if (p && p->type != PropertyDescriptor::Generic) { + if (hasProperty) + *hasProperty = true; + return getValue(ctx, p); + } + + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); +} + + +// Section 8.12.5 +void Object::__put__(ExecutionContext *ctx, String *name, Value value) +{ + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __put__(ctx, idx, value); + + PropertyDescriptor *pd = __getOwnProperty__(ctx, name); + // clause 1 + if (pd) { + if (pd->isAccessor()) { + if (pd->set) + goto cont; + goto reject; + } else if (!pd->isWritable()) + goto reject; + else if (isArray && name->isEqualTo(ctx->engine->id_length)) { + bool ok; + uint l = value.asArrayLength(ctx, &ok); + if (!ok) + ctx->throwRangeError(value); + ok = array.setLength(l); + if (!ok) + goto reject; + } else { + pd->value = value; + } + return; + } else if (!prototype) { + if (!extensible) + goto reject; + } else { + if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, name)) { + if (p->isAccessor()) { + if (p->set) + goto cont; + goto reject; + } + if (!extensible) + goto reject; + if (!p->isWritable()) + goto reject; + } else { + if (!extensible) + goto reject; + } + } + + cont: + + if (!members) + members.reset(new PropertyTable()); + + + // clause 4 + if (!pd && prototype) + pd = prototype->__getPropertyDescriptor__(ctx, name); + + // Clause 5 + if (pd && pd->isAccessor()) { + assert(pd->set != 0); + + Value args[1]; + args[0] = value; + pd->set->call(ctx, Value::fromObject(this), args, 1); + return; + } + + { + PropertyDescriptor *p = members->insert(name); + p->type = PropertyDescriptor::Data; + p->value = value; + p->configurable = PropertyDescriptor::Enabled; + p->enumberable = PropertyDescriptor::Enabled; + p->writable = PropertyDescriptor::Enabled; + return; + } + + reject: + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); +} + +void Object::__put__(ExecutionContext *ctx, uint index, Value value) +{ + PropertyDescriptor *pd = __getOwnProperty__(ctx, index); + // clause 1 + if (pd) { + if (pd->isAccessor()) { + if (pd->set) + goto cont; + goto reject; + } else if (!pd->isWritable()) + goto reject; + else + pd->value = value; + return; + } else if (!prototype) { + if (!extensible) + goto reject; + } else { + if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, index)) { + if (p->isAccessor()) { + if (p->set) + goto cont; + goto reject; + } + if (!extensible) + goto reject; + if (!p->isWritable()) + goto reject; + } else { + if (!extensible) + goto reject; + } + } + + cont: + + // clause 4 + if (!pd && prototype) + pd = prototype->__getPropertyDescriptor__(ctx, index); + + // Clause 5 + if (pd && pd->isAccessor()) { + assert(pd->set != 0); + + Value args[1]; + args[0] = value; + pd->set->call(ctx, Value::fromObject(this), args, 1); + return; + } + + array.set(index, value); + return; + + reject: + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); +} + +// Section 8.12.6 +bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const +{ + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __hasProperty__(ctx, idx); + + if (members && members->find(name) != 0) + return true; + + return prototype ? prototype->__hasProperty__(ctx, name) : false; +} + +bool Object::__hasProperty__(const ExecutionContext *ctx, uint index) const +{ + const PropertyDescriptor *p = array.at(index); + if (p && p->type != PropertyDescriptor::Generic) + return true; + + return prototype ? prototype->__hasProperty__(ctx, index) : false; +} + +// Section 8.12.7 +bool Object::__delete__(ExecutionContext *ctx, String *name) +{ + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __delete__(ctx, idx); + + if (members) { + if (PropertyTableEntry *entry = members->findEntry(name)) { + if (entry->descriptor.isConfigurable()) { + members->remove(entry); + return true; + } + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; + } + } + return true; +} + +bool Object::__delete__(ExecutionContext *ctx, uint index) +{ + if (array.deleteIndex(index)) + return true; + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; +} + +// Section 8.12.9 +bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc) +{ + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __defineOwnProperty__(ctx, idx, desc); + + PropertyDescriptor *current; + + if (isArray && name->isEqualTo(ctx->engine->id_length)) { + PropertyDescriptor *lp = array.getLengthProperty(); + if (desc->isEmpty() || desc->isSubset(lp)) + return true; + if (!lp->isWritable() || desc->type == PropertyDescriptor::Accessor || desc->isConfigurable() || desc->isEnumerable()) + goto reject; + bool succeeded = true; + if (desc->type == PropertyDescriptor::Data) { + bool ok; + uint l = desc->value.asArrayLength(ctx, &ok); + if (!ok) + ctx->throwRangeError(desc->value); + succeeded = array.setLength(l); + } + if (desc->writable == PropertyDescriptor::Disabled) + lp->writable = PropertyDescriptor::Disabled; + if (!succeeded) + goto reject; + return true; + } + + if (!members) + members.reset(new PropertyTable()); + + // Clause 1 + current = __getOwnProperty__(ctx, name); + if (!current) { + // clause 3 + if (!extensible) + goto reject; + // clause 4 + PropertyDescriptor *pd = members->insert(name); + *pd = *desc; + pd->fullyPopulated(); + return true; + } + + return __defineOwnProperty__(ctx, current, desc); +reject: + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; +} + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc) +{ + PropertyDescriptor *current; + + // 15.4.5.1, 4b + if (isArray && index >= array.length() && !array.getLengthProperty()->isWritable()) + goto reject; + + // Clause 1 + current = __getOwnProperty__(ctx, index); + if (!current) { + // clause 3 + if (!extensible) + goto reject; + // clause 4 + PropertyDescriptor *pd = array.insert(index); + *pd = *desc; + pd->fullyPopulated(); + return true; + } + + return __defineOwnProperty__(ctx, current, desc); +reject: + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; +} + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc) +{ + // clause 5 + if (desc->isEmpty()) + return true; + + // clause 6 + if (desc->isSubset(current)) + return true; + + // clause 7 + if (!current->isConfigurable()) { + if (desc->isConfigurable()) + goto reject; + if (desc->enumberable != PropertyDescriptor::Undefined && desc->enumberable != current->enumberable) + goto reject; + } + + // clause 8 + if (desc->isGeneric()) + goto accept; + + // clause 9 + if (current->isData() != desc->isData()) { + // 9a + if (!current->isConfigurable()) + goto reject; + if (current->isData()) { + // 9b + current->type = PropertyDescriptor::Accessor; + current->writable = PropertyDescriptor::Undefined; + current->get = 0; + current->set = 0; + } else { + // 9c + current->type = PropertyDescriptor::Data; + current->writable = PropertyDescriptor::Disabled; + current->value = Value::undefinedValue(); + } + } else if (current->isData() && desc->isData()) { // clause 10 + if (!current->isConfigurable() && !current->isWritable()) { + if (desc->isWritable() || !current->value.sameValue(desc->value)) + goto reject; + } + } else { // clause 10 + assert(current->isAccessor() && desc->isAccessor()); + if (!current->isConfigurable()) { + if (desc->get && !(current->get == desc->get || (!current->get && (quintptr)desc->get == 0x1))) + goto reject; + if (desc->set && !(current->set == desc->set || (!current->set && (quintptr)desc->set == 0x1))) + goto reject; + } + } + + accept: + + *current += *desc; + return true; + reject: + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; +} + + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc) +{ + return __defineOwnProperty__(ctx, ctx->engine->identifier(name), desc); +} + + +Value Object::call(ExecutionContext *context, Value , Value *, int) +{ + context->throwTypeError(); + return Value::undefinedValue(); +} + +void ArrayObject::init(ExecutionContext *context) +{ + isArray = true; + if (!members) + members.reset(new PropertyTable()); + PropertyDescriptor *pd = members->insert(context->engine->id_length); + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Disabled; + pd->value = Value::fromInt32(0); + array.setLengthProperty(pd); +} + + + +void ForEachIteratorObject::getCollectables(QVector &objects) +{ + Object::getCollectables(objects); + if (it.object) + objects.append(it.object); +} + diff --git a/qv4object.h b/qv4object.h new file mode 100644 index 0000000000..819b5d7f2a --- /dev/null +++ b/qv4object.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_OBJECTS_H +#define QMLJS_OBJECTS_H + +#include "qmljs_runtime.h" +#include "qmljs_engine.h" +#include "qmljs_environment.h" +#include "qv4array.h" +#include "qv4string.h" +#include "qv4codegen_p.h" +#include "qv4isel_p.h" +#include "qv4managed.h" +#include "qv4propertydescriptor.h" +#include "qv4propertytable.h" +#include "qv4objectiterator.h" +#include "qv4regexp.h" + +#include +#include +#include +#include +#include + +namespace QQmlJS { + +namespace VM { + +struct Value; +struct Function; +struct Object; +struct ObjectIterator; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct ExecutionEngine; +class MemoryManager; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct RegExpPrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; + + +struct Object: Managed { + Object *prototype; + QScopedPointer members; + Array array; + + Object() + : prototype(0) {} + Object(const Array &a) + : prototype(0), array(a) {} + + virtual ~Object(); + + virtual QString className() { return QStringLiteral("Object"); } + virtual BooleanObject *asBooleanObject() { return 0; } + virtual NumberObject *asNumberObject() { return 0; } + virtual StringObject *asStringObject() { return 0; } + virtual DateObject *asDateObject() { return 0; } + virtual ArrayObject *asArrayObject() { return 0; } + virtual FunctionObject *asFunctionObject() { return 0; } + virtual RegExpObject *asRegExpObject() { return 0; } + virtual ErrorObject *asErrorObject() { return 0; } + virtual ArgumentsObject *asArgumentsObject() { return 0; } + + PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); + PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); + PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name); + PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, uint index); + + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); + Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); + + void __put__(ExecutionContext *ctx, String *name, Value value); + void __put__(ExecutionContext *ctx, uint index, Value value); + + virtual bool __hasProperty__(const ExecutionContext *ctx, String *name) const; + virtual bool __hasProperty__(const ExecutionContext *ctx, uint index) const; + virtual bool __delete__(ExecutionContext *ctx, String *name); + virtual bool __delete__(ExecutionContext *ctx, uint index); + bool __defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc); + virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc); + + virtual Value call(ExecutionContext *context, Value, Value *, int); + + // + // helpers + // + void __put__(ExecutionContext *ctx, const QString &name, const Value &value); + + Value getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const; + Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const; + Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const; + + bool inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); + bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); + + /* The spec default: Writable: true, Enumerable: false, Configurable: true */ + void defineDefaultProperty(String *name, Value value); + void defineDefaultProperty(ExecutionContext *context, const QString &name, Value value); + void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int count = 0); + /* Fixed: Writable: false, Enumerable: false, Configurable: false */ + void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value); + void defineReadonlyProperty(String *name, Value value); + +protected: + virtual void getCollectables(QVector &objects); + + friend struct ObjectIterator; +}; + +struct ForEachIteratorObject: Object { + ObjectIterator it; + ForEachIteratorObject(ExecutionContext *ctx, Object *o) + : it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) {} + virtual QString className() { return QStringLiteral("__ForEachIteratorObject"); } + + Value nextPropertyName() { return it.nextPropertyNameAsString(); } + +protected: + virtual void getCollectables(QVector &objects); +}; + +struct BooleanObject: Object { + Value value; + BooleanObject(const Value &value): value(value) {} + virtual QString className() { return QStringLiteral("Boolean"); } + virtual BooleanObject *asBooleanObject() { return this; } +}; + +struct NumberObject: Object { + Value value; + NumberObject(const Value &value): value(value) {} + virtual QString className() { return QStringLiteral("Number"); } + virtual NumberObject *asNumberObject() { return this; } +}; + +struct ArrayObject: Object { + ArrayObject(ExecutionContext *ctx) { init(ctx); } + ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setLengthUnchecked(array.length()); } + void init(ExecutionContext *context); + virtual QString className() { return QStringLiteral("Array"); } + virtual ArrayObject *asArrayObject() { return this; } +}; + + +} // namespace VM +} // namespace QQmlJS + +#endif // QMLJS_OBJECTS_H diff --git a/qv4objectiterator.cpp b/qv4objectiterator.cpp index 271a3c59b8..8da2e7bdba 100644 --- a/qv4objectiterator.cpp +++ b/qv4objectiterator.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ #include "qv4objectiterator.h" -#include "qmljs_objects.h" +#include "qv4object.h" #include "qv4stringobject.h" namespace QQmlJS { diff --git a/qv4stringobject.h b/qv4stringobject.h index 465ab34c4a..7e2fc84dbe 100644 --- a/qv4stringobject.h +++ b/qv4stringobject.h @@ -41,7 +41,7 @@ #ifndef QV4STRINGOBJECT_P_H #define QV4STRINGOBJECT_P_H -#include "qmljs_objects.h" +#include "qv4object.h" #include "qv4functionobject.h" #include diff --git a/v4.pro b/v4.pro index 69c4a0c1d3..d85e283ef0 100644 --- a/v4.pro +++ b/v4.pro @@ -16,7 +16,6 @@ SOURCES += main.cpp \ qmljs_engine.cpp \ qmljs_environment.cpp \ qmljs_runtime.cpp \ - qmljs_objects.cpp \ qmljs_value.cpp \ qv4syntaxchecker.cpp \ qv4ecmaobjects.cpp \ @@ -34,6 +33,7 @@ SOURCES += main.cpp \ qv4globalobject.cpp \ qv4jsonobject.cpp \ qv4mathobject.cpp \ + qv4object.cpp \ qv4regexpobject.cpp \ qv4stringobject.cpp \ qv4string.cpp \ @@ -46,7 +46,6 @@ HEADERS += \ qmljs_engine.h \ qmljs_environment.h \ qmljs_runtime.h \ - qmljs_objects.h \ qmljs_math.h \ qmljs_value.h \ qv4syntaxchecker_p.h \ @@ -65,6 +64,7 @@ HEADERS += \ qv4globalobject.h \ qv4jsonobject.h \ qv4mathobject.h \ + qv4object.h \ qv4regexpobject.h \ qv4stringobject.h \ qv4string.h \ -- cgit v1.2.3 From b7d733813f4829dd3ddc890712fc175252819337 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 21:56:04 +0100 Subject: Move masm into 3rdparty Change-Id: Ie3645603149a4e054ebe658ef216fb6cd207b740 Reviewed-by: Simon Hausmann --- 3rdparty/masm/WeakRandom.h | 52 + 3rdparty/masm/assembler/ARMAssembler.cpp | 428 + 3rdparty/masm/assembler/ARMAssembler.h | 1108 +++ 3rdparty/masm/assembler/ARMv7Assembler.cpp | 36 + 3rdparty/masm/assembler/ARMv7Assembler.h | 2706 ++++++ 3rdparty/masm/assembler/AbstractMacroAssembler.h | 792 ++ 3rdparty/masm/assembler/AssemblerBuffer.h | 181 + .../assembler/AssemblerBufferWithConstantPool.h | 342 + 3rdparty/masm/assembler/CodeLocation.h | 218 + 3rdparty/masm/assembler/LinkBuffer.cpp | 230 + 3rdparty/masm/assembler/LinkBuffer.h | 297 + 3rdparty/masm/assembler/MIPSAssembler.h | 1032 +++ 3rdparty/masm/assembler/MacroAssembler.h | 1464 ++++ 3rdparty/masm/assembler/MacroAssemblerARM.cpp | 99 + 3rdparty/masm/assembler/MacroAssemblerARM.h | 1383 +++ 3rdparty/masm/assembler/MacroAssemblerARMv7.h | 1903 +++++ 3rdparty/masm/assembler/MacroAssemblerCodeRef.h | 399 + 3rdparty/masm/assembler/MacroAssemblerMIPS.h | 2316 +++++ 3rdparty/masm/assembler/MacroAssemblerSH4.cpp | 52 + 3rdparty/masm/assembler/MacroAssemblerSH4.h | 2293 +++++ 3rdparty/masm/assembler/MacroAssemblerX86.h | 314 + 3rdparty/masm/assembler/MacroAssemblerX86Common.h | 1541 ++++ 3rdparty/masm/assembler/MacroAssemblerX86_64.h | 643 ++ 3rdparty/masm/assembler/RepatchBuffer.h | 181 + 3rdparty/masm/assembler/SH4Assembler.h | 2152 +++++ 3rdparty/masm/assembler/X86Assembler.h | 2540 ++++++ 3rdparty/masm/config.h | 56 + 3rdparty/masm/create_regex_tables | 121 + 3rdparty/masm/disassembler/Disassembler.cpp | 43 + 3rdparty/masm/disassembler/Disassembler.h | 52 + 3rdparty/masm/disassembler/UDis86Disassembler.cpp | 63 + 3rdparty/masm/disassembler/udis86/differences.txt | 24 + 3rdparty/masm/disassembler/udis86/itab.py | 360 + 3rdparty/masm/disassembler/udis86/optable.xml | 8959 ++++++++++++++++++++ 3rdparty/masm/disassembler/udis86/ud_opcode.py | 235 + 3rdparty/masm/disassembler/udis86/ud_optable.py | 103 + 3rdparty/masm/disassembler/udis86/udis86.c | 183 + 3rdparty/masm/disassembler/udis86/udis86.h | 33 + 3rdparty/masm/disassembler/udis86/udis86_decode.c | 1142 +++ 3rdparty/masm/disassembler/udis86/udis86_decode.h | 258 + 3rdparty/masm/disassembler/udis86/udis86_extern.h | 88 + 3rdparty/masm/disassembler/udis86/udis86_input.c | 263 + 3rdparty/masm/disassembler/udis86/udis86_input.h | 67 + .../masm/disassembler/udis86/udis86_itab_holder.c | 34 + 3rdparty/masm/disassembler/udis86/udis86_syn-att.c | 253 + .../masm/disassembler/udis86/udis86_syn-intel.c | 279 + 3rdparty/masm/disassembler/udis86/udis86_syn.c | 87 + 3rdparty/masm/disassembler/udis86/udis86_syn.h | 47 + 3rdparty/masm/disassembler/udis86/udis86_types.h | 238 + 3rdparty/masm/jit/JITCompilationEffort.h | 39 + 3rdparty/masm/masm.pri | 107 + 3rdparty/masm/runtime/MatchResult.h | 71 + 3rdparty/masm/stubs/ExecutableAllocator.h | 105 + 3rdparty/masm/stubs/JSGlobalData.h | 56 + 3rdparty/masm/stubs/LLIntData.h | 0 3rdparty/masm/stubs/Options.h | 53 + 3rdparty/masm/stubs/WTFStubs.cpp | 131 + 3rdparty/masm/stubs/WTFStubs.h | 50 + 3rdparty/masm/stubs/wtf/FastAllocBase.h | 48 + 3rdparty/masm/stubs/wtf/FastMalloc.h | 46 + 3rdparty/masm/stubs/wtf/Noncopyable.h | 48 + 3rdparty/masm/stubs/wtf/OwnPtr.h | 46 + 3rdparty/masm/stubs/wtf/PassOwnPtr.h | 116 + 3rdparty/masm/stubs/wtf/PassRefPtr.h | 101 + 3rdparty/masm/stubs/wtf/RefCounted.h | 70 + 3rdparty/masm/stubs/wtf/RefPtr.h | 93 + 3rdparty/masm/stubs/wtf/TypeTraits.h | 58 + 3rdparty/masm/stubs/wtf/UnusedParam.h | 48 + 3rdparty/masm/stubs/wtf/Vector.h | 95 + 3rdparty/masm/stubs/wtf/text/CString.h | 44 + 3rdparty/masm/stubs/wtf/text/WTFString.h | 75 + 3rdparty/masm/stubs/wtf/unicode/Unicode.h | 59 + 3rdparty/masm/wtf/ASCIICType.h | 181 + 3rdparty/masm/wtf/Assertions.h | 393 + 3rdparty/masm/wtf/Atomics.h | 241 + 3rdparty/masm/wtf/BumpPointerAllocator.h | 252 + 3rdparty/masm/wtf/CheckedArithmetic.h | 708 ++ 3rdparty/masm/wtf/Compiler.h | 284 + 3rdparty/masm/wtf/CryptographicallyRandomNumber.h | 45 + 3rdparty/masm/wtf/DataLog.h | 128 + 3rdparty/masm/wtf/DynamicAnnotations.h | 96 + 3rdparty/masm/wtf/FilePrintStream.cpp | 64 + 3rdparty/masm/wtf/FilePrintStream.h | 62 + 3rdparty/masm/wtf/Locker.h | 48 + 3rdparty/masm/wtf/NotFound.h | 37 + 3rdparty/masm/wtf/NullPtr.h | 56 + 3rdparty/masm/wtf/OSAllocator.h | 115 + 3rdparty/masm/wtf/OSAllocatorPosix.cpp | 184 + 3rdparty/masm/wtf/OSAllocatorWin.cpp | 80 + 3rdparty/masm/wtf/PageAllocation.h | 120 + 3rdparty/masm/wtf/PageAllocationAligned.cpp | 87 + 3rdparty/masm/wtf/PageAllocationAligned.h | 70 + 3rdparty/masm/wtf/PageBlock.cpp | 78 + 3rdparty/masm/wtf/PageBlock.h | 88 + 3rdparty/masm/wtf/PageReservation.h | 149 + 3rdparty/masm/wtf/Platform.h | 1212 +++ 3rdparty/masm/wtf/PossiblyNull.h | 59 + 3rdparty/masm/wtf/PrintStream.cpp | 112 + 3rdparty/masm/wtf/PrintStream.h | 300 + 3rdparty/masm/wtf/RawPointer.h | 58 + 3rdparty/masm/wtf/StdLibExtras.h | 282 + 3rdparty/masm/wtf/VMTags.h | 75 + 3rdparty/masm/yarr/Yarr.h | 69 + 3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp | 463 + 3rdparty/masm/yarr/YarrCanonicalizeUCS2.h | 138 + 3rdparty/masm/yarr/YarrCanonicalizeUCS2.js | 219 + 3rdparty/masm/yarr/YarrInterpreter.cpp | 1964 +++++ 3rdparty/masm/yarr/YarrInterpreter.h | 385 + 3rdparty/masm/yarr/YarrJIT.cpp | 2667 ++++++ 3rdparty/masm/yarr/YarrJIT.h | 141 + 3rdparty/masm/yarr/YarrParser.h | 880 ++ 3rdparty/masm/yarr/YarrPattern.cpp | 874 ++ 3rdparty/masm/yarr/YarrPattern.h | 421 + 3rdparty/masm/yarr/YarrSyntaxChecker.cpp | 59 + 3rdparty/masm/yarr/YarrSyntaxChecker.h | 38 + 3rdparty/masm/yarr/yarr.pri | 12 + masm/WeakRandom.h | 52 - masm/assembler/ARMAssembler.cpp | 428 - masm/assembler/ARMAssembler.h | 1108 --- masm/assembler/ARMv7Assembler.cpp | 36 - masm/assembler/ARMv7Assembler.h | 2706 ------ masm/assembler/AbstractMacroAssembler.h | 792 -- masm/assembler/AssemblerBuffer.h | 181 - masm/assembler/AssemblerBufferWithConstantPool.h | 342 - masm/assembler/CodeLocation.h | 218 - masm/assembler/LinkBuffer.cpp | 230 - masm/assembler/LinkBuffer.h | 297 - masm/assembler/MIPSAssembler.h | 1032 --- masm/assembler/MacroAssembler.h | 1464 ---- masm/assembler/MacroAssemblerARM.cpp | 99 - masm/assembler/MacroAssemblerARM.h | 1383 --- masm/assembler/MacroAssemblerARMv7.h | 1903 ----- masm/assembler/MacroAssemblerCodeRef.h | 399 - masm/assembler/MacroAssemblerMIPS.h | 2316 ----- masm/assembler/MacroAssemblerSH4.cpp | 52 - masm/assembler/MacroAssemblerSH4.h | 2293 ----- masm/assembler/MacroAssemblerX86.h | 314 - masm/assembler/MacroAssemblerX86Common.h | 1541 ---- masm/assembler/MacroAssemblerX86_64.h | 643 -- masm/assembler/RepatchBuffer.h | 181 - masm/assembler/SH4Assembler.h | 2152 ----- masm/assembler/X86Assembler.h | 2540 ------ masm/config.h | 56 - masm/create_regex_tables | 121 - masm/disassembler/Disassembler.cpp | 43 - masm/disassembler/Disassembler.h | 52 - masm/disassembler/UDis86Disassembler.cpp | 63 - masm/disassembler/udis86/differences.txt | 24 - masm/disassembler/udis86/itab.py | 360 - masm/disassembler/udis86/optable.xml | 8959 -------------------- masm/disassembler/udis86/ud_opcode.py | 235 - masm/disassembler/udis86/ud_optable.py | 103 - masm/disassembler/udis86/udis86.c | 183 - masm/disassembler/udis86/udis86.h | 33 - masm/disassembler/udis86/udis86_decode.c | 1142 --- masm/disassembler/udis86/udis86_decode.h | 258 - masm/disassembler/udis86/udis86_extern.h | 88 - masm/disassembler/udis86/udis86_input.c | 263 - masm/disassembler/udis86/udis86_input.h | 67 - masm/disassembler/udis86/udis86_itab_holder.c | 34 - masm/disassembler/udis86/udis86_syn-att.c | 253 - masm/disassembler/udis86/udis86_syn-intel.c | 279 - masm/disassembler/udis86/udis86_syn.c | 87 - masm/disassembler/udis86/udis86_syn.h | 47 - masm/disassembler/udis86/udis86_types.h | 238 - masm/jit/JITCompilationEffort.h | 39 - masm/masm.pri | 107 - masm/runtime/MatchResult.h | 71 - masm/stubs/ExecutableAllocator.h | 105 - masm/stubs/JSGlobalData.h | 56 - masm/stubs/LLIntData.h | 0 masm/stubs/Options.h | 53 - masm/stubs/WTFStubs.cpp | 131 - masm/stubs/WTFStubs.h | 50 - masm/stubs/wtf/FastAllocBase.h | 48 - masm/stubs/wtf/FastMalloc.h | 46 - masm/stubs/wtf/Noncopyable.h | 48 - masm/stubs/wtf/OwnPtr.h | 46 - masm/stubs/wtf/PassOwnPtr.h | 116 - masm/stubs/wtf/PassRefPtr.h | 101 - masm/stubs/wtf/RefCounted.h | 70 - masm/stubs/wtf/RefPtr.h | 93 - masm/stubs/wtf/TypeTraits.h | 58 - masm/stubs/wtf/UnusedParam.h | 48 - masm/stubs/wtf/Vector.h | 95 - masm/stubs/wtf/text/CString.h | 44 - masm/stubs/wtf/text/WTFString.h | 75 - masm/stubs/wtf/unicode/Unicode.h | 59 - masm/wtf/ASCIICType.h | 181 - masm/wtf/Assertions.h | 393 - masm/wtf/Atomics.h | 241 - masm/wtf/BumpPointerAllocator.h | 252 - masm/wtf/CheckedArithmetic.h | 708 -- masm/wtf/Compiler.h | 284 - masm/wtf/CryptographicallyRandomNumber.h | 45 - masm/wtf/DataLog.h | 128 - masm/wtf/DynamicAnnotations.h | 96 - masm/wtf/FilePrintStream.cpp | 64 - masm/wtf/FilePrintStream.h | 62 - masm/wtf/Locker.h | 48 - masm/wtf/NotFound.h | 37 - masm/wtf/NullPtr.h | 56 - masm/wtf/OSAllocator.h | 115 - masm/wtf/OSAllocatorPosix.cpp | 184 - masm/wtf/OSAllocatorWin.cpp | 80 - masm/wtf/PageAllocation.h | 120 - masm/wtf/PageAllocationAligned.cpp | 87 - masm/wtf/PageAllocationAligned.h | 70 - masm/wtf/PageBlock.cpp | 78 - masm/wtf/PageBlock.h | 88 - masm/wtf/PageReservation.h | 149 - masm/wtf/Platform.h | 1212 --- masm/wtf/PossiblyNull.h | 59 - masm/wtf/PrintStream.cpp | 112 - masm/wtf/PrintStream.h | 300 - masm/wtf/RawPointer.h | 58 - masm/wtf/StdLibExtras.h | 282 - masm/wtf/VMTags.h | 75 - masm/yarr/Yarr.h | 69 - masm/yarr/YarrCanonicalizeUCS2.cpp | 463 - masm/yarr/YarrCanonicalizeUCS2.h | 138 - masm/yarr/YarrCanonicalizeUCS2.js | 219 - masm/yarr/YarrInterpreter.cpp | 1964 ----- masm/yarr/YarrInterpreter.h | 385 - masm/yarr/YarrJIT.cpp | 2667 ------ masm/yarr/YarrJIT.h | 141 - masm/yarr/YarrParser.h | 880 -- masm/yarr/YarrPattern.cpp | 874 -- masm/yarr/YarrPattern.h | 421 - masm/yarr/YarrSyntaxChecker.cpp | 59 - masm/yarr/YarrSyntaxChecker.h | 38 - masm/yarr/yarr.pri | 12 - v4.pro | 2 +- 233 files changed, 53244 insertions(+), 53244 deletions(-) create mode 100644 3rdparty/masm/WeakRandom.h create mode 100644 3rdparty/masm/assembler/ARMAssembler.cpp create mode 100644 3rdparty/masm/assembler/ARMAssembler.h create mode 100644 3rdparty/masm/assembler/ARMv7Assembler.cpp create mode 100644 3rdparty/masm/assembler/ARMv7Assembler.h create mode 100644 3rdparty/masm/assembler/AbstractMacroAssembler.h create mode 100644 3rdparty/masm/assembler/AssemblerBuffer.h create mode 100644 3rdparty/masm/assembler/AssemblerBufferWithConstantPool.h create mode 100644 3rdparty/masm/assembler/CodeLocation.h create mode 100644 3rdparty/masm/assembler/LinkBuffer.cpp create mode 100644 3rdparty/masm/assembler/LinkBuffer.h create mode 100644 3rdparty/masm/assembler/MIPSAssembler.h create mode 100644 3rdparty/masm/assembler/MacroAssembler.h create mode 100644 3rdparty/masm/assembler/MacroAssemblerARM.cpp create mode 100644 3rdparty/masm/assembler/MacroAssemblerARM.h create mode 100644 3rdparty/masm/assembler/MacroAssemblerARMv7.h create mode 100644 3rdparty/masm/assembler/MacroAssemblerCodeRef.h create mode 100644 3rdparty/masm/assembler/MacroAssemblerMIPS.h create mode 100644 3rdparty/masm/assembler/MacroAssemblerSH4.cpp create mode 100644 3rdparty/masm/assembler/MacroAssemblerSH4.h create mode 100644 3rdparty/masm/assembler/MacroAssemblerX86.h create mode 100644 3rdparty/masm/assembler/MacroAssemblerX86Common.h create mode 100644 3rdparty/masm/assembler/MacroAssemblerX86_64.h create mode 100644 3rdparty/masm/assembler/RepatchBuffer.h create mode 100644 3rdparty/masm/assembler/SH4Assembler.h create mode 100644 3rdparty/masm/assembler/X86Assembler.h create mode 100644 3rdparty/masm/config.h create mode 100644 3rdparty/masm/create_regex_tables create mode 100644 3rdparty/masm/disassembler/Disassembler.cpp create mode 100644 3rdparty/masm/disassembler/Disassembler.h create mode 100644 3rdparty/masm/disassembler/UDis86Disassembler.cpp create mode 100644 3rdparty/masm/disassembler/udis86/differences.txt create mode 100644 3rdparty/masm/disassembler/udis86/itab.py create mode 100644 3rdparty/masm/disassembler/udis86/optable.xml create mode 100644 3rdparty/masm/disassembler/udis86/ud_opcode.py create mode 100644 3rdparty/masm/disassembler/udis86/ud_optable.py create mode 100644 3rdparty/masm/disassembler/udis86/udis86.c create mode 100644 3rdparty/masm/disassembler/udis86/udis86.h create mode 100644 3rdparty/masm/disassembler/udis86/udis86_decode.c create mode 100644 3rdparty/masm/disassembler/udis86/udis86_decode.h create mode 100644 3rdparty/masm/disassembler/udis86/udis86_extern.h create mode 100644 3rdparty/masm/disassembler/udis86/udis86_input.c create mode 100644 3rdparty/masm/disassembler/udis86/udis86_input.h create mode 100644 3rdparty/masm/disassembler/udis86/udis86_itab_holder.c create mode 100644 3rdparty/masm/disassembler/udis86/udis86_syn-att.c create mode 100644 3rdparty/masm/disassembler/udis86/udis86_syn-intel.c create mode 100644 3rdparty/masm/disassembler/udis86/udis86_syn.c create mode 100644 3rdparty/masm/disassembler/udis86/udis86_syn.h create mode 100644 3rdparty/masm/disassembler/udis86/udis86_types.h create mode 100644 3rdparty/masm/jit/JITCompilationEffort.h create mode 100644 3rdparty/masm/masm.pri create mode 100644 3rdparty/masm/runtime/MatchResult.h create mode 100644 3rdparty/masm/stubs/ExecutableAllocator.h create mode 100644 3rdparty/masm/stubs/JSGlobalData.h create mode 100644 3rdparty/masm/stubs/LLIntData.h create mode 100644 3rdparty/masm/stubs/Options.h create mode 100644 3rdparty/masm/stubs/WTFStubs.cpp create mode 100644 3rdparty/masm/stubs/WTFStubs.h create mode 100644 3rdparty/masm/stubs/wtf/FastAllocBase.h create mode 100644 3rdparty/masm/stubs/wtf/FastMalloc.h create mode 100644 3rdparty/masm/stubs/wtf/Noncopyable.h create mode 100644 3rdparty/masm/stubs/wtf/OwnPtr.h create mode 100644 3rdparty/masm/stubs/wtf/PassOwnPtr.h create mode 100644 3rdparty/masm/stubs/wtf/PassRefPtr.h create mode 100644 3rdparty/masm/stubs/wtf/RefCounted.h create mode 100644 3rdparty/masm/stubs/wtf/RefPtr.h create mode 100644 3rdparty/masm/stubs/wtf/TypeTraits.h create mode 100644 3rdparty/masm/stubs/wtf/UnusedParam.h create mode 100644 3rdparty/masm/stubs/wtf/Vector.h create mode 100644 3rdparty/masm/stubs/wtf/text/CString.h create mode 100644 3rdparty/masm/stubs/wtf/text/WTFString.h create mode 100644 3rdparty/masm/stubs/wtf/unicode/Unicode.h create mode 100644 3rdparty/masm/wtf/ASCIICType.h create mode 100644 3rdparty/masm/wtf/Assertions.h create mode 100644 3rdparty/masm/wtf/Atomics.h create mode 100644 3rdparty/masm/wtf/BumpPointerAllocator.h create mode 100644 3rdparty/masm/wtf/CheckedArithmetic.h create mode 100644 3rdparty/masm/wtf/Compiler.h create mode 100644 3rdparty/masm/wtf/CryptographicallyRandomNumber.h create mode 100644 3rdparty/masm/wtf/DataLog.h create mode 100644 3rdparty/masm/wtf/DynamicAnnotations.h create mode 100644 3rdparty/masm/wtf/FilePrintStream.cpp create mode 100644 3rdparty/masm/wtf/FilePrintStream.h create mode 100644 3rdparty/masm/wtf/Locker.h create mode 100644 3rdparty/masm/wtf/NotFound.h create mode 100644 3rdparty/masm/wtf/NullPtr.h create mode 100644 3rdparty/masm/wtf/OSAllocator.h create mode 100644 3rdparty/masm/wtf/OSAllocatorPosix.cpp create mode 100644 3rdparty/masm/wtf/OSAllocatorWin.cpp create mode 100644 3rdparty/masm/wtf/PageAllocation.h create mode 100644 3rdparty/masm/wtf/PageAllocationAligned.cpp create mode 100644 3rdparty/masm/wtf/PageAllocationAligned.h create mode 100644 3rdparty/masm/wtf/PageBlock.cpp create mode 100644 3rdparty/masm/wtf/PageBlock.h create mode 100644 3rdparty/masm/wtf/PageReservation.h create mode 100644 3rdparty/masm/wtf/Platform.h create mode 100644 3rdparty/masm/wtf/PossiblyNull.h create mode 100644 3rdparty/masm/wtf/PrintStream.cpp create mode 100644 3rdparty/masm/wtf/PrintStream.h create mode 100644 3rdparty/masm/wtf/RawPointer.h create mode 100644 3rdparty/masm/wtf/StdLibExtras.h create mode 100644 3rdparty/masm/wtf/VMTags.h create mode 100644 3rdparty/masm/yarr/Yarr.h create mode 100644 3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp create mode 100644 3rdparty/masm/yarr/YarrCanonicalizeUCS2.h create mode 100644 3rdparty/masm/yarr/YarrCanonicalizeUCS2.js create mode 100644 3rdparty/masm/yarr/YarrInterpreter.cpp create mode 100644 3rdparty/masm/yarr/YarrInterpreter.h create mode 100644 3rdparty/masm/yarr/YarrJIT.cpp create mode 100644 3rdparty/masm/yarr/YarrJIT.h create mode 100644 3rdparty/masm/yarr/YarrParser.h create mode 100644 3rdparty/masm/yarr/YarrPattern.cpp create mode 100644 3rdparty/masm/yarr/YarrPattern.h create mode 100644 3rdparty/masm/yarr/YarrSyntaxChecker.cpp create mode 100644 3rdparty/masm/yarr/YarrSyntaxChecker.h create mode 100644 3rdparty/masm/yarr/yarr.pri delete mode 100644 masm/WeakRandom.h delete mode 100644 masm/assembler/ARMAssembler.cpp delete mode 100644 masm/assembler/ARMAssembler.h delete mode 100644 masm/assembler/ARMv7Assembler.cpp delete mode 100644 masm/assembler/ARMv7Assembler.h delete mode 100644 masm/assembler/AbstractMacroAssembler.h delete mode 100644 masm/assembler/AssemblerBuffer.h delete mode 100644 masm/assembler/AssemblerBufferWithConstantPool.h delete mode 100644 masm/assembler/CodeLocation.h delete mode 100644 masm/assembler/LinkBuffer.cpp delete mode 100644 masm/assembler/LinkBuffer.h delete mode 100644 masm/assembler/MIPSAssembler.h delete mode 100644 masm/assembler/MacroAssembler.h delete mode 100644 masm/assembler/MacroAssemblerARM.cpp delete mode 100644 masm/assembler/MacroAssemblerARM.h delete mode 100644 masm/assembler/MacroAssemblerARMv7.h delete mode 100644 masm/assembler/MacroAssemblerCodeRef.h delete mode 100644 masm/assembler/MacroAssemblerMIPS.h delete mode 100644 masm/assembler/MacroAssemblerSH4.cpp delete mode 100644 masm/assembler/MacroAssemblerSH4.h delete mode 100644 masm/assembler/MacroAssemblerX86.h delete mode 100644 masm/assembler/MacroAssemblerX86Common.h delete mode 100644 masm/assembler/MacroAssemblerX86_64.h delete mode 100644 masm/assembler/RepatchBuffer.h delete mode 100644 masm/assembler/SH4Assembler.h delete mode 100644 masm/assembler/X86Assembler.h delete mode 100644 masm/config.h delete mode 100644 masm/create_regex_tables delete mode 100644 masm/disassembler/Disassembler.cpp delete mode 100644 masm/disassembler/Disassembler.h delete mode 100644 masm/disassembler/UDis86Disassembler.cpp delete mode 100644 masm/disassembler/udis86/differences.txt delete mode 100644 masm/disassembler/udis86/itab.py delete mode 100644 masm/disassembler/udis86/optable.xml delete mode 100644 masm/disassembler/udis86/ud_opcode.py delete mode 100644 masm/disassembler/udis86/ud_optable.py delete mode 100644 masm/disassembler/udis86/udis86.c delete mode 100644 masm/disassembler/udis86/udis86.h delete mode 100644 masm/disassembler/udis86/udis86_decode.c delete mode 100644 masm/disassembler/udis86/udis86_decode.h delete mode 100644 masm/disassembler/udis86/udis86_extern.h delete mode 100644 masm/disassembler/udis86/udis86_input.c delete mode 100644 masm/disassembler/udis86/udis86_input.h delete mode 100644 masm/disassembler/udis86/udis86_itab_holder.c delete mode 100644 masm/disassembler/udis86/udis86_syn-att.c delete mode 100644 masm/disassembler/udis86/udis86_syn-intel.c delete mode 100644 masm/disassembler/udis86/udis86_syn.c delete mode 100644 masm/disassembler/udis86/udis86_syn.h delete mode 100644 masm/disassembler/udis86/udis86_types.h delete mode 100644 masm/jit/JITCompilationEffort.h delete mode 100644 masm/masm.pri delete mode 100644 masm/runtime/MatchResult.h delete mode 100644 masm/stubs/ExecutableAllocator.h delete mode 100644 masm/stubs/JSGlobalData.h delete mode 100644 masm/stubs/LLIntData.h delete mode 100644 masm/stubs/Options.h delete mode 100644 masm/stubs/WTFStubs.cpp delete mode 100644 masm/stubs/WTFStubs.h delete mode 100644 masm/stubs/wtf/FastAllocBase.h delete mode 100644 masm/stubs/wtf/FastMalloc.h delete mode 100644 masm/stubs/wtf/Noncopyable.h delete mode 100644 masm/stubs/wtf/OwnPtr.h delete mode 100644 masm/stubs/wtf/PassOwnPtr.h delete mode 100644 masm/stubs/wtf/PassRefPtr.h delete mode 100644 masm/stubs/wtf/RefCounted.h delete mode 100644 masm/stubs/wtf/RefPtr.h delete mode 100644 masm/stubs/wtf/TypeTraits.h delete mode 100644 masm/stubs/wtf/UnusedParam.h delete mode 100644 masm/stubs/wtf/Vector.h delete mode 100644 masm/stubs/wtf/text/CString.h delete mode 100644 masm/stubs/wtf/text/WTFString.h delete mode 100644 masm/stubs/wtf/unicode/Unicode.h delete mode 100644 masm/wtf/ASCIICType.h delete mode 100644 masm/wtf/Assertions.h delete mode 100644 masm/wtf/Atomics.h delete mode 100644 masm/wtf/BumpPointerAllocator.h delete mode 100644 masm/wtf/CheckedArithmetic.h delete mode 100644 masm/wtf/Compiler.h delete mode 100644 masm/wtf/CryptographicallyRandomNumber.h delete mode 100644 masm/wtf/DataLog.h delete mode 100644 masm/wtf/DynamicAnnotations.h delete mode 100644 masm/wtf/FilePrintStream.cpp delete mode 100644 masm/wtf/FilePrintStream.h delete mode 100644 masm/wtf/Locker.h delete mode 100644 masm/wtf/NotFound.h delete mode 100644 masm/wtf/NullPtr.h delete mode 100644 masm/wtf/OSAllocator.h delete mode 100644 masm/wtf/OSAllocatorPosix.cpp delete mode 100644 masm/wtf/OSAllocatorWin.cpp delete mode 100644 masm/wtf/PageAllocation.h delete mode 100644 masm/wtf/PageAllocationAligned.cpp delete mode 100644 masm/wtf/PageAllocationAligned.h delete mode 100644 masm/wtf/PageBlock.cpp delete mode 100644 masm/wtf/PageBlock.h delete mode 100644 masm/wtf/PageReservation.h delete mode 100644 masm/wtf/Platform.h delete mode 100644 masm/wtf/PossiblyNull.h delete mode 100644 masm/wtf/PrintStream.cpp delete mode 100644 masm/wtf/PrintStream.h delete mode 100644 masm/wtf/RawPointer.h delete mode 100644 masm/wtf/StdLibExtras.h delete mode 100644 masm/wtf/VMTags.h delete mode 100644 masm/yarr/Yarr.h delete mode 100644 masm/yarr/YarrCanonicalizeUCS2.cpp delete mode 100644 masm/yarr/YarrCanonicalizeUCS2.h delete mode 100644 masm/yarr/YarrCanonicalizeUCS2.js delete mode 100644 masm/yarr/YarrInterpreter.cpp delete mode 100644 masm/yarr/YarrInterpreter.h delete mode 100644 masm/yarr/YarrJIT.cpp delete mode 100644 masm/yarr/YarrJIT.h delete mode 100644 masm/yarr/YarrParser.h delete mode 100644 masm/yarr/YarrPattern.cpp delete mode 100644 masm/yarr/YarrPattern.h delete mode 100644 masm/yarr/YarrSyntaxChecker.cpp delete mode 100644 masm/yarr/YarrSyntaxChecker.h delete mode 100644 masm/yarr/yarr.pri diff --git a/3rdparty/masm/WeakRandom.h b/3rdparty/masm/WeakRandom.h new file mode 100644 index 0000000000..325d1f6ac6 --- /dev/null +++ b/3rdparty/masm/WeakRandom.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MASM_WEAKRANDOM_H +#define MASM_WEAKRANDOM_H + +#include + +struct WeakRandom { + WeakRandom(int) {} + uint32_t getUint32() { return 0; } +}; + +#endif // MASM_WEAKRANDOM_H diff --git a/3rdparty/masm/assembler/ARMAssembler.cpp b/3rdparty/masm/assembler/ARMAssembler.cpp new file mode 100644 index 0000000000..9655557a5d --- /dev/null +++ b/3rdparty/masm/assembler/ARMAssembler.cpp @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2009 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#include "ARMAssembler.h" + +namespace JSC { + +// Patching helpers + +void ARMAssembler::patchConstantPoolLoad(void* loadAddr, void* constPoolAddr) +{ + ARMWord *ldr = reinterpret_cast(loadAddr); + ARMWord diff = reinterpret_cast(constPoolAddr) - ldr; + ARMWord index = (*ldr & 0xfff) >> 1; + + ASSERT(diff >= 1); + if (diff >= 2 || index > 0) { + diff = (diff + index - 2) * sizeof(ARMWord); + ASSERT(diff <= 0xfff); + *ldr = (*ldr & ~0xfff) | diff; + } else + *ldr = (*ldr & ~(0xfff | ARMAssembler::DataTransferUp)) | sizeof(ARMWord); +} + +// Handle immediates + +ARMWord ARMAssembler::getOp2(ARMWord imm) +{ + int rol; + + if (imm <= 0xff) + return Op2Immediate | imm; + + if ((imm & 0xff000000) == 0) { + imm <<= 8; + rol = 8; + } + else { + imm = (imm << 24) | (imm >> 8); + rol = 0; + } + + if ((imm & 0xff000000) == 0) { + imm <<= 8; + rol += 4; + } + + if ((imm & 0xf0000000) == 0) { + imm <<= 4; + rol += 2; + } + + if ((imm & 0xc0000000) == 0) { + imm <<= 2; + rol += 1; + } + + if ((imm & 0x00ffffff) == 0) + return Op2Immediate | (imm >> 24) | (rol << 8); + + return InvalidImmediate; +} + +int ARMAssembler::genInt(int reg, ARMWord imm, bool positive) +{ + // Step1: Search a non-immediate part + ARMWord mask; + ARMWord imm1; + ARMWord imm2; + int rol; + + mask = 0xff000000; + rol = 8; + while(1) { + if ((imm & mask) == 0) { + imm = (imm << rol) | (imm >> (32 - rol)); + rol = 4 + (rol >> 1); + break; + } + rol += 2; + mask >>= 2; + if (mask & 0x3) { + // rol 8 + imm = (imm << 8) | (imm >> 24); + mask = 0xff00; + rol = 24; + while (1) { + if ((imm & mask) == 0) { + imm = (imm << rol) | (imm >> (32 - rol)); + rol = (rol >> 1) - 8; + break; + } + rol += 2; + mask >>= 2; + if (mask & 0x3) + return 0; + } + break; + } + } + + ASSERT((imm & 0xff) == 0); + + if ((imm & 0xff000000) == 0) { + imm1 = Op2Immediate | ((imm >> 16) & 0xff) | (((rol + 4) & 0xf) << 8); + imm2 = Op2Immediate | ((imm >> 8) & 0xff) | (((rol + 8) & 0xf) << 8); + } else if (imm & 0xc0000000) { + imm1 = Op2Immediate | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8); + imm <<= 8; + rol += 4; + + if ((imm & 0xff000000) == 0) { + imm <<= 8; + rol += 4; + } + + if ((imm & 0xf0000000) == 0) { + imm <<= 4; + rol += 2; + } + + if ((imm & 0xc0000000) == 0) { + imm <<= 2; + rol += 1; + } + + if ((imm & 0x00ffffff) == 0) + imm2 = Op2Immediate | (imm >> 24) | ((rol & 0xf) << 8); + else + return 0; + } else { + if ((imm & 0xf0000000) == 0) { + imm <<= 4; + rol += 2; + } + + if ((imm & 0xc0000000) == 0) { + imm <<= 2; + rol += 1; + } + + imm1 = Op2Immediate | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8); + imm <<= 8; + rol += 4; + + if ((imm & 0xf0000000) == 0) { + imm <<= 4; + rol += 2; + } + + if ((imm & 0xc0000000) == 0) { + imm <<= 2; + rol += 1; + } + + if ((imm & 0x00ffffff) == 0) + imm2 = Op2Immediate | (imm >> 24) | ((rol & 0xf) << 8); + else + return 0; + } + + if (positive) { + mov(reg, imm1); + orr(reg, reg, imm2); + } else { + mvn(reg, imm1); + bic(reg, reg, imm2); + } + + return 1; +} + +ARMWord ARMAssembler::getImm(ARMWord imm, int tmpReg, bool invert) +{ + ARMWord tmp; + + // Do it by 1 instruction + tmp = getOp2(imm); + if (tmp != InvalidImmediate) + return tmp; + + tmp = getOp2(~imm); + if (tmp != InvalidImmediate) { + if (invert) + return tmp | Op2InvertedImmediate; + mvn(tmpReg, tmp); + return tmpReg; + } + + return encodeComplexImm(imm, tmpReg); +} + +void ARMAssembler::moveImm(ARMWord imm, int dest) +{ + ARMWord tmp; + + // Do it by 1 instruction + tmp = getOp2(imm); + if (tmp != InvalidImmediate) { + mov(dest, tmp); + return; + } + + tmp = getOp2(~imm); + if (tmp != InvalidImmediate) { + mvn(dest, tmp); + return; + } + + encodeComplexImm(imm, dest); +} + +ARMWord ARMAssembler::encodeComplexImm(ARMWord imm, int dest) +{ +#if WTF_ARM_ARCH_AT_LEAST(7) + ARMWord tmp = getImm16Op2(imm); + if (tmp != InvalidImmediate) { + movw(dest, tmp); + return dest; + } + movw(dest, getImm16Op2(imm & 0xffff)); + movt(dest, getImm16Op2(imm >> 16)); + return dest; +#else + // Do it by 2 instruction + if (genInt(dest, imm, true)) + return dest; + if (genInt(dest, ~imm, false)) + return dest; + + ldrImmediate(dest, imm); + return dest; +#endif +} + +// Memory load/store helpers + +void ARMAssembler::dataTransfer32(DataTransferTypeA transferType, RegisterID srcDst, RegisterID base, int32_t offset) +{ + if (offset >= 0) { + if (offset <= 0xfff) + dtrUp(transferType, srcDst, base, offset); + else if (offset <= 0xfffff) { + add(ARMRegisters::S0, base, Op2Immediate | (offset >> 12) | (10 << 8)); + dtrUp(transferType, srcDst, ARMRegisters::S0, (offset & 0xfff)); + } else { + moveImm(offset, ARMRegisters::S0); + dtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); + } + } else { + if (offset >= -0xfff) + dtrDown(transferType, srcDst, base, -offset); + else if (offset >= -0xfffff) { + sub(ARMRegisters::S0, base, Op2Immediate | (-offset >> 12) | (10 << 8)); + dtrDown(transferType, srcDst, ARMRegisters::S0, (-offset & 0xfff)); + } else { + moveImm(offset, ARMRegisters::S0); + dtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); + } + } +} + +void ARMAssembler::baseIndexTransfer32(DataTransferTypeA transferType, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) +{ + ASSERT(scale >= 0 && scale <= 3); + ARMWord op2 = lsl(index, scale); + + if (!offset) { + dtrUpRegister(transferType, srcDst, base, op2); + return; + } + + add(ARMRegisters::S1, base, op2); + dataTransfer32(transferType, srcDst, ARMRegisters::S1, offset); +} + +void ARMAssembler::dataTransfer16(DataTransferTypeB transferType, RegisterID srcDst, RegisterID base, int32_t offset) +{ + if (offset >= 0) { + if (offset <= 0xff) + halfDtrUp(transferType, srcDst, base, getOp2Half(offset)); + else if (offset <= 0xffff) { + add(ARMRegisters::S0, base, Op2Immediate | (offset >> 8) | (12 << 8)); + halfDtrUp(transferType, srcDst, ARMRegisters::S0, getOp2Half(offset & 0xff)); + } else { + moveImm(offset, ARMRegisters::S0); + halfDtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); + } + } else { + if (offset >= -0xff) + halfDtrDown(transferType, srcDst, base, getOp2Half(-offset)); + else if (offset >= -0xffff) { + sub(ARMRegisters::S0, base, Op2Immediate | (-offset >> 8) | (12 << 8)); + halfDtrDown(transferType, srcDst, ARMRegisters::S0, getOp2Half(-offset & 0xff)); + } else { + moveImm(offset, ARMRegisters::S0); + halfDtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); + } + } +} + +void ARMAssembler::baseIndexTransfer16(DataTransferTypeB transferType, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) +{ + if (!scale && !offset) { + halfDtrUpRegister(transferType, srcDst, base, index); + return; + } + + add(ARMRegisters::S1, base, lsl(index, scale)); + dataTransfer16(transferType, srcDst, ARMRegisters::S1, offset); +} + +void ARMAssembler::dataTransferFloat(DataTransferTypeFloat transferType, FPRegisterID srcDst, RegisterID base, int32_t offset) +{ + // VFP cannot directly access memory that is not four-byte-aligned + if (!(offset & 0x3)) { + if (offset <= 0x3ff && offset >= 0) { + doubleDtrUp(transferType, srcDst, base, offset >> 2); + return; + } + if (offset <= 0x3ffff && offset >= 0) { + add(ARMRegisters::S0, base, Op2Immediate | (offset >> 10) | (11 << 8)); + doubleDtrUp(transferType, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff); + return; + } + offset = -offset; + + if (offset <= 0x3ff && offset >= 0) { + doubleDtrDown(transferType, srcDst, base, offset >> 2); + return; + } + if (offset <= 0x3ffff && offset >= 0) { + sub(ARMRegisters::S0, base, Op2Immediate | (offset >> 10) | (11 << 8)); + doubleDtrDown(transferType, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff); + return; + } + offset = -offset; + } + + moveImm(offset, ARMRegisters::S0); + add(ARMRegisters::S0, ARMRegisters::S0, base); + doubleDtrUp(transferType, srcDst, ARMRegisters::S0, 0); +} + +void ARMAssembler::baseIndexTransferFloat(DataTransferTypeFloat transferType, FPRegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) +{ + add(ARMRegisters::S1, base, lsl(index, scale)); + dataTransferFloat(transferType, srcDst, ARMRegisters::S1, offset); +} + +PassRefPtr ARMAssembler::executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) +{ + // 64-bit alignment is required for next constant pool and JIT code as well + m_buffer.flushWithoutBarrier(true); + if (!m_buffer.isAligned(8)) + bkpt(0); + + RefPtr result = m_buffer.executableCopy(globalData, ownerUID, effort); + char* data = reinterpret_cast(result->start()); + + for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) { + // The last bit is set if the constant must be placed on constant pool. + int pos = (iter->m_offset) & (~0x1); + ARMWord* ldrAddr = reinterpret_cast_ptr(data + pos); + ARMWord* addr = getLdrImmAddress(ldrAddr); + if (*addr != InvalidBranchTarget) { + if (!(iter->m_offset & 1)) { + intptr_t difference = reinterpret_cast_ptr(data + *addr) - (ldrAddr + DefaultPrefetchOffset); + + if ((difference <= MaximumBranchOffsetDistance && difference >= MinimumBranchOffsetDistance)) { + *ldrAddr = B | getConditionalField(*ldrAddr) | (difference & BranchOffsetMask); + continue; + } + } + *addr = reinterpret_cast(data + *addr); + } + } + + return result; +} + +#if OS(LINUX) && COMPILER(RVCT) + +__asm void ARMAssembler::cacheFlush(void* code, size_t size) +{ + ARM + push {r7} + add r1, r1, r0 + mov r7, #0xf0000 + add r7, r7, #0x2 + mov r2, #0x0 + svc #0x0 + pop {r7} + bx lr +} + +#endif + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) diff --git a/3rdparty/masm/assembler/ARMAssembler.h b/3rdparty/masm/assembler/ARMAssembler.h new file mode 100644 index 0000000000..ebab46d98a --- /dev/null +++ b/3rdparty/masm/assembler/ARMAssembler.h @@ -0,0 +1,1108 @@ +/* + * Copyright (C) 2009, 2010 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ARMAssembler_h +#define ARMAssembler_h + +#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#include "AssemblerBufferWithConstantPool.h" +#include "JITCompilationEffort.h" +#include +namespace JSC { + + typedef uint32_t ARMWord; + + namespace ARMRegisters { + typedef enum { + r0 = 0, + r1, + r2, + r3, S0 = r3, /* Same as thumb assembler. */ + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, S1 = r12, + r13, sp = r13, + r14, lr = r14, + r15, pc = r15 + } RegisterID; + + typedef enum { + d0, + d1, + d2, + d3, + d4, + d5, + d6, + d7, SD0 = d7, /* Same as thumb assembler. */ + d8, + d9, + d10, + d11, + d12, + d13, + d14, + d15, + d16, + d17, + d18, + d19, + d20, + d21, + d22, + d23, + d24, + d25, + d26, + d27, + d28, + d29, + d30, + d31 + } FPRegisterID; + + } // namespace ARMRegisters + + class ARMAssembler { + public: + typedef ARMRegisters::RegisterID RegisterID; + typedef ARMRegisters::FPRegisterID FPRegisterID; + typedef AssemblerBufferWithConstantPool<2048, 4, 4, ARMAssembler> ARMBuffer; + typedef SegmentedVector Jumps; + + ARMAssembler() + : m_indexOfTailOfLastWatchpoint(1) + { + } + + // ARM conditional constants + typedef enum { + EQ = 0x00000000, // Zero + NE = 0x10000000, // Non-zero + CS = 0x20000000, + CC = 0x30000000, + MI = 0x40000000, + PL = 0x50000000, + VS = 0x60000000, + VC = 0x70000000, + HI = 0x80000000, + LS = 0x90000000, + GE = 0xa0000000, + LT = 0xb0000000, + GT = 0xc0000000, + LE = 0xd0000000, + AL = 0xe0000000 + } Condition; + + // ARM instruction constants + enum { + AND = (0x0 << 21), + EOR = (0x1 << 21), + SUB = (0x2 << 21), + RSB = (0x3 << 21), + ADD = (0x4 << 21), + ADC = (0x5 << 21), + SBC = (0x6 << 21), + RSC = (0x7 << 21), + TST = (0x8 << 21), + TEQ = (0x9 << 21), + CMP = (0xa << 21), + CMN = (0xb << 21), + ORR = (0xc << 21), + MOV = (0xd << 21), + BIC = (0xe << 21), + MVN = (0xf << 21), + MUL = 0x00000090, + MULL = 0x00c00090, + VMOV_F64 = 0x0eb00b40, + VADD_F64 = 0x0e300b00, + VDIV_F64 = 0x0e800b00, + VSUB_F64 = 0x0e300b40, + VMUL_F64 = 0x0e200b00, + VCMP_F64 = 0x0eb40b40, + VSQRT_F64 = 0x0eb10bc0, + VABS_F64 = 0x0eb00bc0, + VNEG_F64 = 0x0eb10b40, + STMDB = 0x09200000, + LDMIA = 0x08b00000, + B = 0x0a000000, + BL = 0x0b000000, + BX = 0x012fff10, + VMOV_VFP64 = 0x0c400a10, + VMOV_ARM64 = 0x0c500a10, + VMOV_VFP32 = 0x0e000a10, + VMOV_ARM32 = 0x0e100a10, + VCVT_F64_S32 = 0x0eb80bc0, + VCVT_S32_F64 = 0x0ebd0b40, + VCVT_U32_F64 = 0x0ebc0b40, + VCVT_F32_F64 = 0x0eb70bc0, + VCVT_F64_F32 = 0x0eb70ac0, + VMRS_APSR = 0x0ef1fa10, + CLZ = 0x016f0f10, + BKPT = 0xe1200070, + BLX = 0x012fff30, +#if WTF_ARM_ARCH_AT_LEAST(7) + MOVW = 0x03000000, + MOVT = 0x03400000, +#endif + NOP = 0xe1a00000, + }; + + enum { + Op2Immediate = (1 << 25), + ImmediateForHalfWordTransfer = (1 << 22), + Op2InvertedImmediate = (1 << 26), + SetConditionalCodes = (1 << 20), + Op2IsRegisterArgument = (1 << 25), + // Data transfer flags. + DataTransferUp = (1 << 23), + DataTransferWriteBack = (1 << 21), + DataTransferPostUpdate = (1 << 24), + DataTransferLoad = (1 << 20), + ByteDataTransfer = (1 << 22), + }; + + enum DataTransferTypeA { + LoadUint32 = 0x05000000 | DataTransferLoad, + LoadUint8 = 0x05400000 | DataTransferLoad, + StoreUint32 = 0x05000000, + StoreUint8 = 0x05400000, + }; + + enum DataTransferTypeB { + LoadUint16 = 0x010000b0 | DataTransferLoad, + LoadInt16 = 0x010000f0 | DataTransferLoad, + LoadInt8 = 0x010000d0 | DataTransferLoad, + StoreUint16 = 0x010000b0, + }; + + enum DataTransferTypeFloat { + LoadFloat = 0x0d000a00 | DataTransferLoad, + LoadDouble = 0x0d000b00 | DataTransferLoad, + StoreFloat = 0x0d000a00, + StoreDouble = 0x0d000b00, + }; + + // Masks of ARM instructions + enum { + BranchOffsetMask = 0x00ffffff, + ConditionalFieldMask = 0xf0000000, + DataTransferOffsetMask = 0xfff, + }; + + enum { + MinimumBranchOffsetDistance = -0x00800000, + MaximumBranchOffsetDistance = 0x007fffff, + }; + + enum { + padForAlign8 = 0x00, + padForAlign16 = 0x0000, + padForAlign32 = 0xe12fff7f // 'bkpt 0xffff' instruction. + }; + + static const ARMWord InvalidImmediate = 0xf0000000; + static const ARMWord InvalidBranchTarget = 0xffffffff; + static const int DefaultPrefetchOffset = 2; + + static const ARMWord BlxInstructionMask = 0x012fff30; + static const ARMWord LdrOrAddInstructionMask = 0x0ff00000; + static const ARMWord LdrPcImmediateInstructionMask = 0x0f7f0000; + + static const ARMWord AddImmediateInstruction = 0x02800000; + static const ARMWord BlxInstruction = 0x012fff30; + static const ARMWord LdrImmediateInstruction = 0x05900000; + static const ARMWord LdrPcImmediateInstruction = 0x051f0000; + + // Instruction formating + + void emitInstruction(ARMWord op, int rd, int rn, ARMWord op2) + { + ASSERT(((op2 & ~Op2Immediate) <= 0xfff) || (((op2 & ~ImmediateForHalfWordTransfer) <= 0xfff))); + m_buffer.putInt(op | RN(rn) | RD(rd) | op2); + } + + void emitDoublePrecisionInstruction(ARMWord op, int dd, int dn, int dm) + { + ASSERT((dd >= 0 && dd <= 31) && (dn >= 0 && dn <= 31) && (dm >= 0 && dm <= 31)); + m_buffer.putInt(op | ((dd & 0xf) << 12) | ((dd & 0x10) << (22 - 4)) + | ((dn & 0xf) << 16) | ((dn & 0x10) << (7 - 4)) + | (dm & 0xf) | ((dm & 0x10) << (5 - 4))); + } + + void emitSinglePrecisionInstruction(ARMWord op, int sd, int sn, int sm) + { + ASSERT((sd >= 0 && sd <= 31) && (sn >= 0 && sn <= 31) && (sm >= 0 && sm <= 31)); + m_buffer.putInt(op | ((sd >> 1) << 12) | ((sd & 0x1) << 22) + | ((sn >> 1) << 16) | ((sn & 0x1) << 7) + | (sm >> 1) | ((sm & 0x1) << 5)); + } + + void bitAnd(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | AND, rd, rn, op2); + } + + void bitAnds(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | AND | SetConditionalCodes, rd, rn, op2); + } + + void eor(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | EOR, rd, rn, op2); + } + + void eors(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | EOR | SetConditionalCodes, rd, rn, op2); + } + + void sub(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | SUB, rd, rn, op2); + } + + void subs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | SUB | SetConditionalCodes, rd, rn, op2); + } + + void rsb(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | RSB, rd, rn, op2); + } + + void rsbs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | RSB | SetConditionalCodes, rd, rn, op2); + } + + void add(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ADD, rd, rn, op2); + } + + void adds(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ADD | SetConditionalCodes, rd, rn, op2); + } + + void adc(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ADC, rd, rn, op2); + } + + void adcs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ADC | SetConditionalCodes, rd, rn, op2); + } + + void sbc(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | SBC, rd, rn, op2); + } + + void sbcs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | SBC | SetConditionalCodes, rd, rn, op2); + } + + void rsc(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | RSC, rd, rn, op2); + } + + void rscs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | RSC | SetConditionalCodes, rd, rn, op2); + } + + void tst(int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | TST | SetConditionalCodes, 0, rn, op2); + } + + void teq(int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | TEQ | SetConditionalCodes, 0, rn, op2); + } + + void cmp(int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | CMP | SetConditionalCodes, 0, rn, op2); + } + + void cmn(int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | CMN | SetConditionalCodes, 0, rn, op2); + } + + void orr(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ORR, rd, rn, op2); + } + + void orrs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ORR | SetConditionalCodes, rd, rn, op2); + } + + void mov(int rd, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | MOV, rd, ARMRegisters::r0, op2); + } + +#if WTF_ARM_ARCH_AT_LEAST(7) + void movw(int rd, ARMWord op2, Condition cc = AL) + { + ASSERT((op2 | 0xf0fff) == 0xf0fff); + m_buffer.putInt(toARMWord(cc) | MOVW | RD(rd) | op2); + } + + void movt(int rd, ARMWord op2, Condition cc = AL) + { + ASSERT((op2 | 0xf0fff) == 0xf0fff); + m_buffer.putInt(toARMWord(cc) | MOVT | RD(rd) | op2); + } +#endif + + void movs(int rd, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | MOV | SetConditionalCodes, rd, ARMRegisters::r0, op2); + } + + static void revertJump(void* instructionStart, RegisterID rd, ARMWord imm) + { + ARMWord* insn = reinterpret_cast(instructionStart); + ARMWord* address = getLdrImmAddress(insn); + *address = imm; + } + + void bic(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | BIC, rd, rn, op2); + } + + void bics(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | BIC | SetConditionalCodes, rd, rn, op2); + } + + void mvn(int rd, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | MVN, rd, ARMRegisters::r0, op2); + } + + void mvns(int rd, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | MVN | SetConditionalCodes, rd, ARMRegisters::r0, op2); + } + + void mul(int rd, int rn, int rm, Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | MUL | RN(rd) | RS(rn) | RM(rm)); + } + + void muls(int rd, int rn, int rm, Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | MUL | SetConditionalCodes | RN(rd) | RS(rn) | RM(rm)); + } + + void mull(int rdhi, int rdlo, int rn, int rm, Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | MULL | RN(rdhi) | RD(rdlo) | RS(rn) | RM(rm)); + } + + void vmov_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VMOV_F64, dd, 0, dm); + } + + void vadd_f64(int dd, int dn, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VADD_F64, dd, dn, dm); + } + + void vdiv_f64(int dd, int dn, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VDIV_F64, dd, dn, dm); + } + + void vsub_f64(int dd, int dn, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VSUB_F64, dd, dn, dm); + } + + void vmul_f64(int dd, int dn, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VMUL_F64, dd, dn, dm); + } + + void vcmp_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VCMP_F64, dd, 0, dm); + } + + void vsqrt_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VSQRT_F64, dd, 0, dm); + } + + void vabs_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VABS_F64, dd, 0, dm); + } + + void vneg_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VNEG_F64, dd, 0, dm); + } + + void ldrImmediate(int rd, ARMWord imm, Condition cc = AL) + { + m_buffer.putIntWithConstantInt(toARMWord(cc) | LoadUint32 | DataTransferUp | RN(ARMRegisters::pc) | RD(rd), imm, true); + } + + void ldrUniqueImmediate(int rd, ARMWord imm, Condition cc = AL) + { + m_buffer.putIntWithConstantInt(toARMWord(cc) | LoadUint32 | DataTransferUp | RN(ARMRegisters::pc) | RD(rd), imm); + } + + void dtrUp(DataTransferTypeA transferType, int rd, int rb, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rb, op2); + } + + void dtrUpRegister(DataTransferTypeA transferType, int rd, int rb, int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | DataTransferUp | Op2IsRegisterArgument, rd, rb, rm); + } + + void dtrDown(DataTransferTypeA transferType, int rd, int rb, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType, rd, rb, op2); + } + + void dtrDownRegister(DataTransferTypeA transferType, int rd, int rb, int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | Op2IsRegisterArgument, rd, rb, rm); + } + + void halfDtrUp(DataTransferTypeB transferType, int rd, int rb, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rb, op2); + } + + void halfDtrUpRegister(DataTransferTypeB transferType, int rd, int rn, int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rn, rm); + } + + void halfDtrDown(DataTransferTypeB transferType, int rd, int rb, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType, rd, rb, op2); + } + + void halfDtrDownRegister(DataTransferTypeB transferType, int rd, int rn, int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType, rd, rn, rm); + } + + void doubleDtrUp(DataTransferTypeFloat type, int rd, int rb, ARMWord op2, Condition cc = AL) + { + ASSERT(op2 <= 0xff && rd <= 15); + /* Only d0-d15 and s0, s2, s4 ... s30 are supported. */ + m_buffer.putInt(toARMWord(cc) | DataTransferUp | type | (rd << 12) | RN(rb) | op2); + } + + void doubleDtrDown(DataTransferTypeFloat type, int rd, int rb, ARMWord op2, Condition cc = AL) + { + ASSERT(op2 <= 0xff && rd <= 15); + /* Only d0-d15 and s0, s2, s4 ... s30 are supported. */ + m_buffer.putInt(toARMWord(cc) | type | (rd << 12) | RN(rb) | op2); + } + + void push(int reg, Condition cc = AL) + { + ASSERT(ARMWord(reg) <= 0xf); + m_buffer.putInt(toARMWord(cc) | StoreUint32 | DataTransferWriteBack | RN(ARMRegisters::sp) | RD(reg) | 0x4); + } + + void pop(int reg, Condition cc = AL) + { + ASSERT(ARMWord(reg) <= 0xf); + m_buffer.putInt(toARMWord(cc) | (LoadUint32 ^ DataTransferPostUpdate) | DataTransferUp | RN(ARMRegisters::sp) | RD(reg) | 0x4); + } + + inline void poke(int reg, Condition cc = AL) + { + dtrDown(StoreUint32, ARMRegisters::sp, 0, reg, cc); + } + + inline void peek(int reg, Condition cc = AL) + { + dtrUp(LoadUint32, reg, ARMRegisters::sp, 0, cc); + } + + void vmov_vfp64(int sm, int rt, int rt2, Condition cc = AL) + { + ASSERT(rt != rt2); + m_buffer.putInt(toARMWord(cc) | VMOV_VFP64 | RN(rt2) | RD(rt) | (sm & 0xf) | ((sm & 0x10) << (5 - 4))); + } + + void vmov_arm64(int rt, int rt2, int sm, Condition cc = AL) + { + ASSERT(rt != rt2); + m_buffer.putInt(toARMWord(cc) | VMOV_ARM64 | RN(rt2) | RD(rt) | (sm & 0xf) | ((sm & 0x10) << (5 - 4))); + } + + void vmov_vfp32(int sn, int rt, Condition cc = AL) + { + ASSERT(rt <= 15); + emitSinglePrecisionInstruction(toARMWord(cc) | VMOV_VFP32, rt << 1, sn, 0); + } + + void vmov_arm32(int rt, int sn, Condition cc = AL) + { + ASSERT(rt <= 15); + emitSinglePrecisionInstruction(toARMWord(cc) | VMOV_ARM32, rt << 1, sn, 0); + } + + void vcvt_f64_s32(int dd, int sm, Condition cc = AL) + { + ASSERT(!(sm & 0x1)); // sm must be divisible by 2 + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F64_S32, dd, 0, (sm >> 1)); + } + + void vcvt_s32_f64(int sd, int dm, Condition cc = AL) + { + ASSERT(!(sd & 0x1)); // sd must be divisible by 2 + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_S32_F64, (sd >> 1), 0, dm); + } + + void vcvt_u32_f64(int sd, int dm, Condition cc = AL) + { + ASSERT(!(sd & 0x1)); // sd must be divisible by 2 + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_U32_F64, (sd >> 1), 0, dm); + } + + void vcvt_f64_f32(int dd, int sm, Condition cc = AL) + { + ASSERT(dd <= 15 && sm <= 15); + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F64_F32, dd, 0, sm); + } + + void vcvt_f32_f64(int dd, int sm, Condition cc = AL) + { + ASSERT(dd <= 15 && sm <= 15); + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F32_F64, dd, 0, sm); + } + + void vmrs_apsr(Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | VMRS_APSR); + } + + void clz(int rd, int rm, Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | CLZ | RD(rd) | RM(rm)); + } + + void bkpt(ARMWord value) + { + m_buffer.putInt(BKPT | ((value & 0xff0) << 4) | (value & 0xf)); + } + + void nop() + { + m_buffer.putInt(NOP); + } + + void bx(int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | BX, 0, 0, RM(rm)); + } + + AssemblerLabel blx(int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | BLX, 0, 0, RM(rm)); + return m_buffer.label(); + } + + static ARMWord lsl(int reg, ARMWord value) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(value <= 0x1f); + return reg | (value << 7) | 0x00; + } + + static ARMWord lsr(int reg, ARMWord value) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(value <= 0x1f); + return reg | (value << 7) | 0x20; + } + + static ARMWord asr(int reg, ARMWord value) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(value <= 0x1f); + return reg | (value << 7) | 0x40; + } + + static ARMWord lslRegister(int reg, int shiftReg) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(shiftReg <= ARMRegisters::pc); + return reg | (shiftReg << 8) | 0x10; + } + + static ARMWord lsrRegister(int reg, int shiftReg) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(shiftReg <= ARMRegisters::pc); + return reg | (shiftReg << 8) | 0x30; + } + + static ARMWord asrRegister(int reg, int shiftReg) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(shiftReg <= ARMRegisters::pc); + return reg | (shiftReg << 8) | 0x50; + } + + // General helpers + + size_t codeSize() const + { + return m_buffer.codeSize(); + } + + void ensureSpace(int insnSpace, int constSpace) + { + m_buffer.ensureSpace(insnSpace, constSpace); + } + + int sizeOfConstantPool() + { + return m_buffer.sizeOfConstantPool(); + } + + AssemblerLabel labelIgnoringWatchpoints() + { + m_buffer.ensureSpaceForAnyInstruction(); + return m_buffer.label(); + } + + AssemblerLabel labelForWatchpoint() + { + m_buffer.ensureSpaceForAnyInstruction(maxJumpReplacementSize() / sizeof(ARMWord)); + AssemblerLabel result = m_buffer.label(); + if (result.m_offset != (m_indexOfTailOfLastWatchpoint - maxJumpReplacementSize())) + result = label(); + m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); + return label(); + } + + AssemblerLabel label() + { + AssemblerLabel result = labelIgnoringWatchpoints(); + while (result.m_offset + 1 < m_indexOfTailOfLastWatchpoint) { + nop(); + // The available number of instructions are ensured by labelForWatchpoint. + result = m_buffer.label(); + } + return result; + } + + AssemblerLabel align(int alignment) + { + while (!m_buffer.isAligned(alignment)) + mov(ARMRegisters::r0, ARMRegisters::r0); + + return label(); + } + + AssemblerLabel loadBranchTarget(int rd, Condition cc = AL, int useConstantPool = 0) + { + ensureSpace(sizeof(ARMWord), sizeof(ARMWord)); + m_jumps.append(m_buffer.codeSize() | (useConstantPool & 0x1)); + ldrUniqueImmediate(rd, InvalidBranchTarget, cc); + return m_buffer.label(); + } + + AssemblerLabel jmp(Condition cc = AL, int useConstantPool = 0) + { + return loadBranchTarget(ARMRegisters::pc, cc, useConstantPool); + } + + PassRefPtr executableCopy(JSGlobalData&, void* ownerUID, JITCompilationEffort); + + unsigned debugOffset() { return m_buffer.debugOffset(); } + + // DFG assembly helpers for moving data between fp and registers. + void vmov(RegisterID rd1, RegisterID rd2, FPRegisterID rn) + { + vmov_arm64(rd1, rd2, rn); + } + + void vmov(FPRegisterID rd, RegisterID rn1, RegisterID rn2) + { + vmov_vfp64(rd, rn1, rn2); + } + + // Patching helpers + + static ARMWord* getLdrImmAddress(ARMWord* insn) + { + // Check for call + if ((*insn & LdrPcImmediateInstructionMask) != LdrPcImmediateInstruction) { + // Must be BLX + ASSERT((*insn & BlxInstructionMask) == BlxInstruction); + insn--; + } + + // Must be an ldr ..., [pc +/- imm] + ASSERT((*insn & LdrPcImmediateInstructionMask) == LdrPcImmediateInstruction); + + ARMWord addr = reinterpret_cast(insn) + DefaultPrefetchOffset * sizeof(ARMWord); + if (*insn & DataTransferUp) + return reinterpret_cast(addr + (*insn & DataTransferOffsetMask)); + return reinterpret_cast(addr - (*insn & DataTransferOffsetMask)); + } + + static ARMWord* getLdrImmAddressOnPool(ARMWord* insn, uint32_t* constPool) + { + // Must be an ldr ..., [pc +/- imm] + ASSERT((*insn & LdrPcImmediateInstructionMask) == LdrPcImmediateInstruction); + + if (*insn & 0x1) + return reinterpret_cast(constPool + ((*insn & DataTransferOffsetMask) >> 1)); + return getLdrImmAddress(insn); + } + + static void patchPointerInternal(intptr_t from, void* to) + { + ARMWord* insn = reinterpret_cast(from); + ARMWord* addr = getLdrImmAddress(insn); + *addr = reinterpret_cast(to); + } + + static ARMWord patchConstantPoolLoad(ARMWord load, ARMWord value) + { + value = (value << 1) + 1; + ASSERT(!(value & ~DataTransferOffsetMask)); + return (load & ~DataTransferOffsetMask) | value; + } + + static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr); + + // Read pointers + static void* readPointer(void* from) + { + ARMWord* instruction = reinterpret_cast(from); + ARMWord* address = getLdrImmAddress(instruction); + return *reinterpret_cast(address); + } + + // Patch pointers + + static void linkPointer(void* code, AssemblerLabel from, void* to) + { + patchPointerInternal(reinterpret_cast(code) + from.m_offset, to); + } + + static void repatchInt32(void* where, int32_t to) + { + patchPointerInternal(reinterpret_cast(where), reinterpret_cast(to)); + } + + static void repatchCompact(void* where, int32_t value) + { + ARMWord* instruction = reinterpret_cast(where); + ASSERT((*instruction & 0x0f700000) == LoadUint32); + if (value >= 0) + *instruction = (*instruction & 0xff7ff000) | DataTransferUp | value; + else + *instruction = (*instruction & 0xff7ff000) | -value; + cacheFlush(instruction, sizeof(ARMWord)); + } + + static void repatchPointer(void* from, void* to) + { + patchPointerInternal(reinterpret_cast(from), to); + } + + // Linkers + static intptr_t getAbsoluteJumpAddress(void* base, int offset = 0) + { + return reinterpret_cast(base) + offset - sizeof(ARMWord); + } + + void linkJump(AssemblerLabel from, AssemblerLabel to) + { + ARMWord* insn = reinterpret_cast(getAbsoluteJumpAddress(m_buffer.data(), from.m_offset)); + ARMWord* addr = getLdrImmAddressOnPool(insn, m_buffer.poolAddress()); + *addr = toARMWord(to.m_offset); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + patchPointerInternal(getAbsoluteJumpAddress(code, from.m_offset), to); + } + + static void relinkJump(void* from, void* to) + { + patchPointerInternal(getAbsoluteJumpAddress(from), to); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + patchPointerInternal(getAbsoluteJumpAddress(code, from.m_offset), to); + } + + static void relinkCall(void* from, void* to) + { + patchPointerInternal(getAbsoluteJumpAddress(from), to); + } + + static void* readCallTarget(void* from) + { + return reinterpret_cast(readPointer(reinterpret_cast(getAbsoluteJumpAddress(from)))); + } + + static void replaceWithJump(void* instructionStart, void* to) + { + ARMWord* instruction = reinterpret_cast(instructionStart) - 1; + intptr_t difference = reinterpret_cast(to) - (reinterpret_cast(instruction) + DefaultPrefetchOffset * sizeof(ARMWord)); + + if (!(difference & 1)) { + difference >>= 2; + if ((difference <= MaximumBranchOffsetDistance && difference >= MinimumBranchOffsetDistance)) { + // Direct branch. + instruction[0] = B | AL | (difference & BranchOffsetMask); + cacheFlush(instruction, sizeof(ARMWord)); + return; + } + } + + // Load target. + instruction[0] = LoadUint32 | AL | RN(ARMRegisters::pc) | RD(ARMRegisters::pc) | 4; + instruction[1] = reinterpret_cast(to); + cacheFlush(instruction, sizeof(ARMWord) * 2); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return sizeof(ARMWord) * 2; + } + + static void replaceWithLoad(void* instructionStart) + { + ARMWord* instruction = reinterpret_cast(instructionStart); + cacheFlush(instruction, sizeof(ARMWord)); + + ASSERT((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction || (*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction); + if ((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction) { + *instruction = (*instruction & ~LdrOrAddInstructionMask) | LdrImmediateInstruction; + cacheFlush(instruction, sizeof(ARMWord)); + } + } + + static void replaceWithAddressComputation(void* instructionStart) + { + ARMWord* instruction = reinterpret_cast(instructionStart); + cacheFlush(instruction, sizeof(ARMWord)); + + ASSERT((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction || (*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction); + if ((*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction) { + *instruction = (*instruction & ~LdrOrAddInstructionMask) | AddImmediateInstruction; + cacheFlush(instruction, sizeof(ARMWord)); + } + } + + // Address operations + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + // Address differences + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + return call.m_offset; + } + + // Handle immediates + + static ARMWord getOp2(ARMWord imm); + + // Fast case if imm is known to be between 0 and 0xff + static ARMWord getOp2Byte(ARMWord imm) + { + ASSERT(imm <= 0xff); + return Op2Immediate | imm; + } + + static ARMWord getOp2Half(ARMWord imm) + { + ASSERT(imm <= 0xff); + return ImmediateForHalfWordTransfer | (imm & 0x0f) | ((imm & 0xf0) << 4); + } + +#if WTF_ARM_ARCH_AT_LEAST(7) + static ARMWord getImm16Op2(ARMWord imm) + { + if (imm <= 0xffff) + return (imm & 0xf000) << 4 | (imm & 0xfff); + return InvalidImmediate; + } +#endif + ARMWord getImm(ARMWord imm, int tmpReg, bool invert = false); + void moveImm(ARMWord imm, int dest); + ARMWord encodeComplexImm(ARMWord imm, int dest); + + // Memory load/store helpers + + void dataTransfer32(DataTransferTypeA, RegisterID srcDst, RegisterID base, int32_t offset); + void baseIndexTransfer32(DataTransferTypeA, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); + void dataTransfer16(DataTransferTypeB, RegisterID srcDst, RegisterID base, int32_t offset); + void baseIndexTransfer16(DataTransferTypeB, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); + void dataTransferFloat(DataTransferTypeFloat, FPRegisterID srcDst, RegisterID base, int32_t offset); + void baseIndexTransferFloat(DataTransferTypeFloat, FPRegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); + + // Constant pool hnadlers + + static ARMWord placeConstantPoolBarrier(int offset) + { + offset = (offset - sizeof(ARMWord)) >> 2; + ASSERT((offset <= MaximumBranchOffsetDistance && offset >= MinimumBranchOffsetDistance)); + return AL | B | (offset & BranchOffsetMask); + } + +#if OS(LINUX) && COMPILER(RVCT) + static __asm void cacheFlush(void* code, size_t); +#else + static void cacheFlush(void* code, size_t size) + { +#if OS(LINUX) && COMPILER(GCC) + uintptr_t currentPage = reinterpret_cast(code) & ~(pageSize() - 1); + uintptr_t lastPage = (reinterpret_cast(code) + size) & ~(pageSize() - 1); + do { + asm volatile( + "push {r7}\n" + "mov r0, %0\n" + "mov r1, %1\n" + "mov r7, #0xf0000\n" + "add r7, r7, #0x2\n" + "mov r2, #0x0\n" + "svc 0x0\n" + "pop {r7}\n" + : + : "r" (currentPage), "r" (currentPage + pageSize()) + : "r0", "r1", "r2"); + currentPage += pageSize(); + } while (lastPage >= currentPage); +#elif OS(WINCE) + CacheRangeFlush(code, size, CACHE_SYNC_ALL); +#elif OS(QNX) && ENABLE(ASSEMBLER_WX_EXCLUSIVE) + UNUSED_PARAM(code); + UNUSED_PARAM(size); +#elif OS(QNX) + msync(code, size, MS_INVALIDATE_ICACHE); +#else +#error "The cacheFlush support is missing on this platform." +#endif + } +#endif + + private: + static ARMWord RM(int reg) + { + ASSERT(reg <= ARMRegisters::pc); + return reg; + } + + static ARMWord RS(int reg) + { + ASSERT(reg <= ARMRegisters::pc); + return reg << 8; + } + + static ARMWord RD(int reg) + { + ASSERT(reg <= ARMRegisters::pc); + return reg << 12; + } + + static ARMWord RN(int reg) + { + ASSERT(reg <= ARMRegisters::pc); + return reg << 16; + } + + static ARMWord getConditionalField(ARMWord i) + { + return i & ConditionalFieldMask; + } + + static ARMWord toARMWord(Condition cc) + { + return static_cast(cc); + } + + static ARMWord toARMWord(uint32_t u) + { + return static_cast(u); + } + + int genInt(int reg, ARMWord imm, bool positive); + + ARMBuffer m_buffer; + Jumps m_jumps; + uint32_t m_indexOfTailOfLastWatchpoint; + }; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#endif // ARMAssembler_h diff --git a/3rdparty/masm/assembler/ARMv7Assembler.cpp b/3rdparty/masm/assembler/ARMv7Assembler.cpp new file mode 100644 index 0000000000..faca66421b --- /dev/null +++ b/3rdparty/masm/assembler/ARMv7Assembler.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) + +#include "ARMv7Assembler.h" + +namespace JSC { + +} + +#endif diff --git a/3rdparty/masm/assembler/ARMv7Assembler.h b/3rdparty/masm/assembler/ARMv7Assembler.h new file mode 100644 index 0000000000..b93ec6e63f --- /dev/null +++ b/3rdparty/masm/assembler/ARMv7Assembler.h @@ -0,0 +1,2706 @@ +/* + * Copyright (C) 2009, 2010, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2010 University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ARMAssembler_h +#define ARMAssembler_h + +#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) + +#include "AssemblerBuffer.h" +#include +#include +#include + +namespace JSC { + +namespace ARMRegisters { + typedef enum { + r0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, wr = r7, // thumb work register + r8, + r9, sb = r9, // static base + r10, sl = r10, // stack limit + r11, fp = r11, // frame pointer + r12, ip = r12, + r13, sp = r13, + r14, lr = r14, + r15, pc = r15, + } RegisterID; + + typedef enum { + s0, + s1, + s2, + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + s12, + s13, + s14, + s15, + s16, + s17, + s18, + s19, + s20, + s21, + s22, + s23, + s24, + s25, + s26, + s27, + s28, + s29, + s30, + s31, + } FPSingleRegisterID; + + typedef enum { + d0, + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + d14, + d15, + d16, + d17, + d18, + d19, + d20, + d21, + d22, + d23, + d24, + d25, + d26, + d27, + d28, + d29, + d30, + d31, + } FPDoubleRegisterID; + + typedef enum { + q0, + q1, + q2, + q3, + q4, + q5, + q6, + q7, + q8, + q9, + q10, + q11, + q12, + q13, + q14, + q15, + q16, + q17, + q18, + q19, + q20, + q21, + q22, + q23, + q24, + q25, + q26, + q27, + q28, + q29, + q30, + q31, + } FPQuadRegisterID; + + inline FPSingleRegisterID asSingle(FPDoubleRegisterID reg) + { + ASSERT(reg < d16); + return (FPSingleRegisterID)(reg << 1); + } + + inline FPDoubleRegisterID asDouble(FPSingleRegisterID reg) + { + ASSERT(!(reg & 1)); + return (FPDoubleRegisterID)(reg >> 1); + } +} + +class ARMv7Assembler; +class ARMThumbImmediate { + friend class ARMv7Assembler; + + typedef uint8_t ThumbImmediateType; + static const ThumbImmediateType TypeInvalid = 0; + static const ThumbImmediateType TypeEncoded = 1; + static const ThumbImmediateType TypeUInt16 = 2; + + typedef union { + int16_t asInt; + struct { + unsigned imm8 : 8; + unsigned imm3 : 3; + unsigned i : 1; + unsigned imm4 : 4; + }; + // If this is an encoded immediate, then it may describe a shift, or a pattern. + struct { + unsigned shiftValue7 : 7; + unsigned shiftAmount : 5; + }; + struct { + unsigned immediate : 8; + unsigned pattern : 4; + }; + } ThumbImmediateValue; + + // byte0 contains least significant bit; not using an array to make client code endian agnostic. + typedef union { + int32_t asInt; + struct { + uint8_t byte0; + uint8_t byte1; + uint8_t byte2; + uint8_t byte3; + }; + } PatternBytes; + + ALWAYS_INLINE static void countLeadingZerosPartial(uint32_t& value, int32_t& zeros, const int N) + { + if (value & ~((1 << N) - 1)) /* check for any of the top N bits (of 2N bits) are set */ + value >>= N; /* if any were set, lose the bottom N */ + else /* if none of the top N bits are set, */ + zeros += N; /* then we have identified N leading zeros */ + } + + static int32_t countLeadingZeros(uint32_t value) + { + if (!value) + return 32; + + int32_t zeros = 0; + countLeadingZerosPartial(value, zeros, 16); + countLeadingZerosPartial(value, zeros, 8); + countLeadingZerosPartial(value, zeros, 4); + countLeadingZerosPartial(value, zeros, 2); + countLeadingZerosPartial(value, zeros, 1); + return zeros; + } + + ARMThumbImmediate() + : m_type(TypeInvalid) + { + m_value.asInt = 0; + } + + ARMThumbImmediate(ThumbImmediateType type, ThumbImmediateValue value) + : m_type(type) + , m_value(value) + { + } + + ARMThumbImmediate(ThumbImmediateType type, uint16_t value) + : m_type(TypeUInt16) + { + // Make sure this constructor is only reached with type TypeUInt16; + // this extra parameter makes the code a little clearer by making it + // explicit at call sites which type is being constructed + ASSERT_UNUSED(type, type == TypeUInt16); + + m_value.asInt = value; + } + +public: + static ARMThumbImmediate makeEncodedImm(uint32_t value) + { + ThumbImmediateValue encoding; + encoding.asInt = 0; + + // okay, these are easy. + if (value < 256) { + encoding.immediate = value; + encoding.pattern = 0; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + int32_t leadingZeros = countLeadingZeros(value); + // if there were 24 or more leading zeros, then we'd have hit the (value < 256) case. + ASSERT(leadingZeros < 24); + + // Given a number with bit fields Z:B:C, where count(Z)+count(B)+count(C) == 32, + // Z are the bits known zero, B is the 8-bit immediate, C are the bits to check for + // zero. count(B) == 8, so the count of bits to be checked is 24 - count(Z). + int32_t rightShiftAmount = 24 - leadingZeros; + if (value == ((value >> rightShiftAmount) << rightShiftAmount)) { + // Shift the value down to the low byte position. The assign to + // shiftValue7 drops the implicit top bit. + encoding.shiftValue7 = value >> rightShiftAmount; + // The endoded shift amount is the magnitude of a right rotate. + encoding.shiftAmount = 8 + leadingZeros; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + PatternBytes bytes; + bytes.asInt = value; + + if ((bytes.byte0 == bytes.byte1) && (bytes.byte0 == bytes.byte2) && (bytes.byte0 == bytes.byte3)) { + encoding.immediate = bytes.byte0; + encoding.pattern = 3; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + if ((bytes.byte0 == bytes.byte2) && !(bytes.byte1 | bytes.byte3)) { + encoding.immediate = bytes.byte0; + encoding.pattern = 1; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + if ((bytes.byte1 == bytes.byte3) && !(bytes.byte0 | bytes.byte2)) { + encoding.immediate = bytes.byte1; + encoding.pattern = 2; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + return ARMThumbImmediate(); + } + + static ARMThumbImmediate makeUInt12(int32_t value) + { + return (!(value & 0xfffff000)) + ? ARMThumbImmediate(TypeUInt16, (uint16_t)value) + : ARMThumbImmediate(); + } + + static ARMThumbImmediate makeUInt12OrEncodedImm(int32_t value) + { + // If this is not a 12-bit unsigned it, try making an encoded immediate. + return (!(value & 0xfffff000)) + ? ARMThumbImmediate(TypeUInt16, (uint16_t)value) + : makeEncodedImm(value); + } + + // The 'make' methods, above, return a !isValid() value if the argument + // cannot be represented as the requested type. This methods is called + // 'get' since the argument can always be represented. + static ARMThumbImmediate makeUInt16(uint16_t value) + { + return ARMThumbImmediate(TypeUInt16, value); + } + + bool isValid() + { + return m_type != TypeInvalid; + } + + uint16_t asUInt16() const { return m_value.asInt; } + + // These methods rely on the format of encoded byte values. + bool isUInt3() { return !(m_value.asInt & 0xfff8); } + bool isUInt4() { return !(m_value.asInt & 0xfff0); } + bool isUInt5() { return !(m_value.asInt & 0xffe0); } + bool isUInt6() { return !(m_value.asInt & 0xffc0); } + bool isUInt7() { return !(m_value.asInt & 0xff80); } + bool isUInt8() { return !(m_value.asInt & 0xff00); } + bool isUInt9() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xfe00); } + bool isUInt10() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xfc00); } + bool isUInt12() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xf000); } + bool isUInt16() { return m_type == TypeUInt16; } + uint8_t getUInt3() { ASSERT(isUInt3()); return m_value.asInt; } + uint8_t getUInt4() { ASSERT(isUInt4()); return m_value.asInt; } + uint8_t getUInt5() { ASSERT(isUInt5()); return m_value.asInt; } + uint8_t getUInt6() { ASSERT(isUInt6()); return m_value.asInt; } + uint8_t getUInt7() { ASSERT(isUInt7()); return m_value.asInt; } + uint8_t getUInt8() { ASSERT(isUInt8()); return m_value.asInt; } + uint16_t getUInt9() { ASSERT(isUInt9()); return m_value.asInt; } + uint16_t getUInt10() { ASSERT(isUInt10()); return m_value.asInt; } + uint16_t getUInt12() { ASSERT(isUInt12()); return m_value.asInt; } + uint16_t getUInt16() { ASSERT(isUInt16()); return m_value.asInt; } + + bool isEncodedImm() { return m_type == TypeEncoded; } + +private: + ThumbImmediateType m_type; + ThumbImmediateValue m_value; +}; + +typedef enum { + SRType_LSL, + SRType_LSR, + SRType_ASR, + SRType_ROR, + + SRType_RRX = SRType_ROR +} ARMShiftType; + +class ShiftTypeAndAmount { + friend class ARMv7Assembler; + +public: + ShiftTypeAndAmount() + { + m_u.type = (ARMShiftType)0; + m_u.amount = 0; + } + + ShiftTypeAndAmount(ARMShiftType type, unsigned amount) + { + m_u.type = type; + m_u.amount = amount & 31; + } + + unsigned lo4() { return m_u.lo4; } + unsigned hi4() { return m_u.hi4; } + +private: + union { + struct { + unsigned lo4 : 4; + unsigned hi4 : 4; + }; + struct { + unsigned type : 2; + unsigned amount : 6; + }; + } m_u; +}; + +class ARMv7Assembler { +public: + typedef ARMRegisters::RegisterID RegisterID; + typedef ARMRegisters::FPSingleRegisterID FPSingleRegisterID; + typedef ARMRegisters::FPDoubleRegisterID FPDoubleRegisterID; + typedef ARMRegisters::FPQuadRegisterID FPQuadRegisterID; + + // (HS, LO, HI, LS) -> (AE, B, A, BE) + // (VS, VC) -> (O, NO) + typedef enum { + ConditionEQ, + ConditionNE, + ConditionHS, ConditionCS = ConditionHS, + ConditionLO, ConditionCC = ConditionLO, + ConditionMI, + ConditionPL, + ConditionVS, + ConditionVC, + ConditionHI, + ConditionLS, + ConditionGE, + ConditionLT, + ConditionGT, + ConditionLE, + ConditionAL, + ConditionInvalid + } Condition; + +#define JUMP_ENUM_WITH_SIZE(index, value) (((value) << 3) | (index)) +#define JUMP_ENUM_SIZE(jump) ((jump) >> 3) + enum JumpType { JumpFixed = JUMP_ENUM_WITH_SIZE(0, 0), + JumpNoCondition = JUMP_ENUM_WITH_SIZE(1, 5 * sizeof(uint16_t)), + JumpCondition = JUMP_ENUM_WITH_SIZE(2, 6 * sizeof(uint16_t)), + JumpNoConditionFixedSize = JUMP_ENUM_WITH_SIZE(3, 5 * sizeof(uint16_t)), + JumpConditionFixedSize = JUMP_ENUM_WITH_SIZE(4, 6 * sizeof(uint16_t)) + }; + enum JumpLinkType { + LinkInvalid = JUMP_ENUM_WITH_SIZE(0, 0), + LinkJumpT1 = JUMP_ENUM_WITH_SIZE(1, sizeof(uint16_t)), + LinkJumpT2 = JUMP_ENUM_WITH_SIZE(2, sizeof(uint16_t)), + LinkJumpT3 = JUMP_ENUM_WITH_SIZE(3, 2 * sizeof(uint16_t)), + LinkJumpT4 = JUMP_ENUM_WITH_SIZE(4, 2 * sizeof(uint16_t)), + LinkConditionalJumpT4 = JUMP_ENUM_WITH_SIZE(5, 3 * sizeof(uint16_t)), + LinkBX = JUMP_ENUM_WITH_SIZE(6, 5 * sizeof(uint16_t)), + LinkConditionalBX = JUMP_ENUM_WITH_SIZE(7, 6 * sizeof(uint16_t)) + }; + + class LinkRecord { + public: + LinkRecord(intptr_t from, intptr_t to, JumpType type, Condition condition) + { + data.realTypes.m_from = from; + data.realTypes.m_to = to; + data.realTypes.m_type = type; + data.realTypes.m_linkType = LinkInvalid; + data.realTypes.m_condition = condition; + } + void operator=(const LinkRecord& other) + { + data.copyTypes.content[0] = other.data.copyTypes.content[0]; + data.copyTypes.content[1] = other.data.copyTypes.content[1]; + data.copyTypes.content[2] = other.data.copyTypes.content[2]; + } + intptr_t from() const { return data.realTypes.m_from; } + void setFrom(intptr_t from) { data.realTypes.m_from = from; } + intptr_t to() const { return data.realTypes.m_to; } + JumpType type() const { return data.realTypes.m_type; } + JumpLinkType linkType() const { return data.realTypes.m_linkType; } + void setLinkType(JumpLinkType linkType) { ASSERT(data.realTypes.m_linkType == LinkInvalid); data.realTypes.m_linkType = linkType; } + Condition condition() const { return data.realTypes.m_condition; } + private: + union { + struct RealTypes { + intptr_t m_from : 31; + intptr_t m_to : 31; + JumpType m_type : 8; + JumpLinkType m_linkType : 8; + Condition m_condition : 16; + } realTypes; + struct CopyTypes { + uint32_t content[3]; + } copyTypes; + COMPILE_ASSERT(sizeof(RealTypes) == sizeof(CopyTypes), LinkRecordCopyStructSizeEqualsRealStruct); + } data; + }; + + ARMv7Assembler() + : m_indexOfLastWatchpoint(INT_MIN) + , m_indexOfTailOfLastWatchpoint(INT_MIN) + { + } + +private: + + // ARMv7, Appx-A.6.3 + static bool BadReg(RegisterID reg) + { + return (reg == ARMRegisters::sp) || (reg == ARMRegisters::pc); + } + + uint32_t singleRegisterMask(FPSingleRegisterID rdNum, int highBitsShift, int lowBitShift) + { + uint32_t rdMask = (rdNum >> 1) << highBitsShift; + if (rdNum & 1) + rdMask |= 1 << lowBitShift; + return rdMask; + } + + uint32_t doubleRegisterMask(FPDoubleRegisterID rdNum, int highBitShift, int lowBitsShift) + { + uint32_t rdMask = (rdNum & 0xf) << lowBitsShift; + if (rdNum & 16) + rdMask |= 1 << highBitShift; + return rdMask; + } + + typedef enum { + OP_ADD_reg_T1 = 0x1800, + OP_SUB_reg_T1 = 0x1A00, + OP_ADD_imm_T1 = 0x1C00, + OP_SUB_imm_T1 = 0x1E00, + OP_MOV_imm_T1 = 0x2000, + OP_CMP_imm_T1 = 0x2800, + OP_ADD_imm_T2 = 0x3000, + OP_SUB_imm_T2 = 0x3800, + OP_AND_reg_T1 = 0x4000, + OP_EOR_reg_T1 = 0x4040, + OP_TST_reg_T1 = 0x4200, + OP_RSB_imm_T1 = 0x4240, + OP_CMP_reg_T1 = 0x4280, + OP_ORR_reg_T1 = 0x4300, + OP_MVN_reg_T1 = 0x43C0, + OP_ADD_reg_T2 = 0x4400, + OP_MOV_reg_T1 = 0x4600, + OP_BLX = 0x4700, + OP_BX = 0x4700, + OP_STR_reg_T1 = 0x5000, + OP_STRH_reg_T1 = 0x5200, + OP_STRB_reg_T1 = 0x5400, + OP_LDRSB_reg_T1 = 0x5600, + OP_LDR_reg_T1 = 0x5800, + OP_LDRH_reg_T1 = 0x5A00, + OP_LDRB_reg_T1 = 0x5C00, + OP_LDRSH_reg_T1 = 0x5E00, + OP_STR_imm_T1 = 0x6000, + OP_LDR_imm_T1 = 0x6800, + OP_STRB_imm_T1 = 0x7000, + OP_LDRB_imm_T1 = 0x7800, + OP_STRH_imm_T1 = 0x8000, + OP_LDRH_imm_T1 = 0x8800, + OP_STR_imm_T2 = 0x9000, + OP_LDR_imm_T2 = 0x9800, + OP_ADD_SP_imm_T1 = 0xA800, + OP_ADD_SP_imm_T2 = 0xB000, + OP_SUB_SP_imm_T1 = 0xB080, + OP_BKPT = 0xBE00, + OP_IT = 0xBF00, + OP_NOP_T1 = 0xBF00, + } OpcodeID; + + typedef enum { + OP_B_T1 = 0xD000, + OP_B_T2 = 0xE000, + OP_AND_reg_T2 = 0xEA00, + OP_TST_reg_T2 = 0xEA10, + OP_ORR_reg_T2 = 0xEA40, + OP_ORR_S_reg_T2 = 0xEA50, + OP_ASR_imm_T1 = 0xEA4F, + OP_LSL_imm_T1 = 0xEA4F, + OP_LSR_imm_T1 = 0xEA4F, + OP_ROR_imm_T1 = 0xEA4F, + OP_MVN_reg_T2 = 0xEA6F, + OP_EOR_reg_T2 = 0xEA80, + OP_ADD_reg_T3 = 0xEB00, + OP_ADD_S_reg_T3 = 0xEB10, + OP_SUB_reg_T2 = 0xEBA0, + OP_SUB_S_reg_T2 = 0xEBB0, + OP_CMP_reg_T2 = 0xEBB0, + OP_VMOV_CtoD = 0xEC00, + OP_VMOV_DtoC = 0xEC10, + OP_FSTS = 0xED00, + OP_VSTR = 0xED00, + OP_FLDS = 0xED10, + OP_VLDR = 0xED10, + OP_VMOV_CtoS = 0xEE00, + OP_VMOV_StoC = 0xEE10, + OP_VMUL_T2 = 0xEE20, + OP_VADD_T2 = 0xEE30, + OP_VSUB_T2 = 0xEE30, + OP_VDIV = 0xEE80, + OP_VABS_T2 = 0xEEB0, + OP_VCMP = 0xEEB0, + OP_VCVT_FPIVFP = 0xEEB0, + OP_VMOV_T2 = 0xEEB0, + OP_VMOV_IMM_T2 = 0xEEB0, + OP_VMRS = 0xEEB0, + OP_VNEG_T2 = 0xEEB0, + OP_VSQRT_T1 = 0xEEB0, + OP_VCVTSD_T1 = 0xEEB0, + OP_VCVTDS_T1 = 0xEEB0, + OP_B_T3a = 0xF000, + OP_B_T4a = 0xF000, + OP_AND_imm_T1 = 0xF000, + OP_TST_imm = 0xF010, + OP_ORR_imm_T1 = 0xF040, + OP_MOV_imm_T2 = 0xF040, + OP_MVN_imm = 0xF060, + OP_EOR_imm_T1 = 0xF080, + OP_ADD_imm_T3 = 0xF100, + OP_ADD_S_imm_T3 = 0xF110, + OP_CMN_imm = 0xF110, + OP_ADC_imm = 0xF140, + OP_SUB_imm_T3 = 0xF1A0, + OP_SUB_S_imm_T3 = 0xF1B0, + OP_CMP_imm_T2 = 0xF1B0, + OP_RSB_imm_T2 = 0xF1C0, + OP_RSB_S_imm_T2 = 0xF1D0, + OP_ADD_imm_T4 = 0xF200, + OP_MOV_imm_T3 = 0xF240, + OP_SUB_imm_T4 = 0xF2A0, + OP_MOVT = 0xF2C0, + OP_UBFX_T1 = 0xF3C0, + OP_NOP_T2a = 0xF3AF, + OP_STRB_imm_T3 = 0xF800, + OP_STRB_reg_T2 = 0xF800, + OP_LDRB_imm_T3 = 0xF810, + OP_LDRB_reg_T2 = 0xF810, + OP_STRH_imm_T3 = 0xF820, + OP_STRH_reg_T2 = 0xF820, + OP_LDRH_reg_T2 = 0xF830, + OP_LDRH_imm_T3 = 0xF830, + OP_STR_imm_T4 = 0xF840, + OP_STR_reg_T2 = 0xF840, + OP_LDR_imm_T4 = 0xF850, + OP_LDR_reg_T2 = 0xF850, + OP_STRB_imm_T2 = 0xF880, + OP_LDRB_imm_T2 = 0xF890, + OP_STRH_imm_T2 = 0xF8A0, + OP_LDRH_imm_T2 = 0xF8B0, + OP_STR_imm_T3 = 0xF8C0, + OP_LDR_imm_T3 = 0xF8D0, + OP_LDRSB_reg_T2 = 0xF910, + OP_LDRSH_reg_T2 = 0xF930, + OP_LSL_reg_T2 = 0xFA00, + OP_LSR_reg_T2 = 0xFA20, + OP_ASR_reg_T2 = 0xFA40, + OP_ROR_reg_T2 = 0xFA60, + OP_CLZ = 0xFAB0, + OP_SMULL_T1 = 0xFB80, + } OpcodeID1; + + typedef enum { + OP_VADD_T2b = 0x0A00, + OP_VDIVb = 0x0A00, + OP_FLDSb = 0x0A00, + OP_VLDRb = 0x0A00, + OP_VMOV_IMM_T2b = 0x0A00, + OP_VMOV_T2b = 0x0A40, + OP_VMUL_T2b = 0x0A00, + OP_FSTSb = 0x0A00, + OP_VSTRb = 0x0A00, + OP_VMOV_StoCb = 0x0A10, + OP_VMOV_CtoSb = 0x0A10, + OP_VMOV_DtoCb = 0x0A10, + OP_VMOV_CtoDb = 0x0A10, + OP_VMRSb = 0x0A10, + OP_VABS_T2b = 0x0A40, + OP_VCMPb = 0x0A40, + OP_VCVT_FPIVFPb = 0x0A40, + OP_VNEG_T2b = 0x0A40, + OP_VSUB_T2b = 0x0A40, + OP_VSQRT_T1b = 0x0A40, + OP_VCVTSD_T1b = 0x0A40, + OP_VCVTDS_T1b = 0x0A40, + OP_NOP_T2b = 0x8000, + OP_B_T3b = 0x8000, + OP_B_T4b = 0x9000, + } OpcodeID2; + + struct FourFours { + FourFours(unsigned f3, unsigned f2, unsigned f1, unsigned f0) + { + m_u.f0 = f0; + m_u.f1 = f1; + m_u.f2 = f2; + m_u.f3 = f3; + } + + union { + unsigned value; + struct { + unsigned f0 : 4; + unsigned f1 : 4; + unsigned f2 : 4; + unsigned f3 : 4; + }; + } m_u; + }; + + class ARMInstructionFormatter; + + // false means else! + bool ifThenElseConditionBit(Condition condition, bool isIf) + { + return isIf ? (condition & 1) : !(condition & 1); + } + uint8_t ifThenElse(Condition condition, bool inst2if, bool inst3if, bool inst4if) + { + int mask = (ifThenElseConditionBit(condition, inst2if) << 3) + | (ifThenElseConditionBit(condition, inst3if) << 2) + | (ifThenElseConditionBit(condition, inst4if) << 1) + | 1; + ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); + return (condition << 4) | mask; + } + uint8_t ifThenElse(Condition condition, bool inst2if, bool inst3if) + { + int mask = (ifThenElseConditionBit(condition, inst2if) << 3) + | (ifThenElseConditionBit(condition, inst3if) << 2) + | 2; + ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); + return (condition << 4) | mask; + } + uint8_t ifThenElse(Condition condition, bool inst2if) + { + int mask = (ifThenElseConditionBit(condition, inst2if) << 3) + | 4; + ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); + return (condition << 4) | mask; + } + + uint8_t ifThenElse(Condition condition) + { + int mask = 8; + return (condition << 4) | mask; + } + +public: + + void adc(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isEncodedImm()); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADC_imm, rn, rd, imm); + } + + void add(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + + if (rn == ARMRegisters::sp) { + ASSERT(!(imm.getUInt16() & 3)); + if (!(rd & 8) && imm.isUInt10()) { + m_formatter.oneWordOp5Reg3Imm8(OP_ADD_SP_imm_T1, rd, static_cast(imm.getUInt10() >> 2)); + return; + } else if ((rd == ARMRegisters::sp) && imm.isUInt9()) { + m_formatter.oneWordOp9Imm7(OP_ADD_SP_imm_T2, static_cast(imm.getUInt9() >> 2)); + return; + } + } else if (!((rd | rn) & 8)) { + if (imm.isUInt3()) { + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); + return; + } else if ((rd == rn) && imm.isUInt8()) { + m_formatter.oneWordOp5Reg3Imm8(OP_ADD_imm_T2, rd, imm.getUInt8()); + return; + } + } + + if (imm.isEncodedImm()) + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_imm_T3, rn, rd, imm); + else { + ASSERT(imm.isUInt12()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_imm_T4, rn, rd, imm); + } + } + + ALWAYS_INLINE void add(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ADD_reg_T3, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // NOTE: In an IT block, add doesn't modify the flags register. + ALWAYS_INLINE void add(RegisterID rd, RegisterID rn, RegisterID rm) + { + if (rd == rn) + m_formatter.oneWordOp8RegReg143(OP_ADD_reg_T2, rm, rd); + else if (rd == rm) + m_formatter.oneWordOp8RegReg143(OP_ADD_reg_T2, rn, rd); + else if (!((rd | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_reg_T1, rm, rn, rd); + else + add(rd, rn, rm, ShiftTypeAndAmount()); + } + + // Not allowed in an IT (if then) block. + ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isEncodedImm()); + + if (!((rd | rn) & 8)) { + if (imm.isUInt3()) { + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); + return; + } else if ((rd == rn) && imm.isUInt8()) { + m_formatter.oneWordOp5Reg3Imm8(OP_ADD_imm_T2, rd, imm.getUInt8()); + return; + } + } + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_S_imm_T3, rn, rd, imm); + } + + // Not allowed in an IT (if then) block? + ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ADD_S_reg_T3, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // Not allowed in an IT (if then) block. + ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, RegisterID rm) + { + if (!((rd | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_reg_T1, rm, rn, rd); + else + add_S(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(imm.isEncodedImm()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_AND_imm_T1, rn, rd, imm); + } + + ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_AND_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, RegisterID rm) + { + if ((rd == rn) && !((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_AND_reg_T1, rm, rd); + else if ((rd == rm) && !((rd | rn) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_AND_reg_T1, rn, rd); + else + ARM_and(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void asr(RegisterID rd, RegisterID rm, int32_t shiftAmount) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + ShiftTypeAndAmount shift(SRType_ASR, shiftAmount); + m_formatter.twoWordOp16FourFours(OP_ASR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void asr(RegisterID rd, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ASR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); + } + + // Only allowed in IT (if then) block if last instruction. + ALWAYS_INLINE AssemblerLabel b() + { + m_formatter.twoWordOp16Op16(OP_B_T4a, OP_B_T4b); + return m_formatter.label(); + } + + // Only allowed in IT (if then) block if last instruction. + ALWAYS_INLINE AssemblerLabel blx(RegisterID rm) + { + ASSERT(rm != ARMRegisters::pc); + m_formatter.oneWordOp8RegReg143(OP_BLX, rm, (RegisterID)8); + return m_formatter.label(); + } + + // Only allowed in IT (if then) block if last instruction. + ALWAYS_INLINE AssemblerLabel bx(RegisterID rm) + { + m_formatter.oneWordOp8RegReg143(OP_BX, rm, (RegisterID)0); + return m_formatter.label(); + } + + void bkpt(uint8_t imm = 0) + { + m_formatter.oneWordOp8Imm8(OP_BKPT, imm); + } + + ALWAYS_INLINE void clz(RegisterID rd, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_CLZ, rm, FourFours(0xf, rd, 8, rm)); + } + + ALWAYS_INLINE void cmn(RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isEncodedImm()); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_CMN_imm, rn, (RegisterID)0xf, imm); + } + + ALWAYS_INLINE void cmp(RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isEncodedImm()); + + if (!(rn & 8) && imm.isUInt8()) + m_formatter.oneWordOp5Reg3Imm8(OP_CMP_imm_T1, rn, imm.getUInt8()); + else + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_CMP_imm_T2, rn, (RegisterID)0xf, imm); + } + + ALWAYS_INLINE void cmp(RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_CMP_reg_T2, rn, FourFours(shift.hi4(), 0xf, shift.lo4(), rm)); + } + + ALWAYS_INLINE void cmp(RegisterID rn, RegisterID rm) + { + if ((rn | rm) & 8) + cmp(rn, rm, ShiftTypeAndAmount()); + else + m_formatter.oneWordOp10Reg3Reg3(OP_CMP_reg_T1, rm, rn); + } + + // xor is not spelled with an 'e'. :-( + ALWAYS_INLINE void eor(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(imm.isEncodedImm()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_EOR_imm_T1, rn, rd, imm); + } + + // xor is not spelled with an 'e'. :-( + ALWAYS_INLINE void eor(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_EOR_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // xor is not spelled with an 'e'. :-( + void eor(RegisterID rd, RegisterID rn, RegisterID rm) + { + if ((rd == rn) && !((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_EOR_reg_T1, rm, rd); + else if ((rd == rm) && !((rd | rn) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_EOR_reg_T1, rn, rd); + else + eor(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void it(Condition cond) + { + m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond)); + } + + ALWAYS_INLINE void it(Condition cond, bool inst2if) + { + m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if)); + } + + ALWAYS_INLINE void it(Condition cond, bool inst2if, bool inst3if) + { + m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if, inst3if)); + } + + ALWAYS_INLINE void it(Condition cond, bool inst2if, bool inst3if, bool inst4if) + { + m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if, inst3if, inst4if)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt7()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDR_imm_T1, imm.getUInt7() >> 2, rn, rt); + else if ((rn == ARMRegisters::sp) && !(rt & 8) && imm.isUInt10()) + m_formatter.oneWordOp5Reg3Imm8(OP_LDR_imm_T2, rt, static_cast(imm.getUInt10() >> 2)); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T3, rn, rt, imm.getUInt12()); + } + + ALWAYS_INLINE void ldrWide8BitImmediate(RegisterID rt, RegisterID rn, uint8_t immediate) + { + ASSERT(rn != ARMRegisters::pc); + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T3, rn, rt, immediate); + } + + ALWAYS_INLINE void ldrCompact(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(imm.isUInt7()); + ASSERT(!((rt | rn) & 8)); + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDR_imm_T1, imm.getUInt7() >> 2, rn, rt); + } + + // If index is set, this is a regular offset or a pre-indexed load; + // if index is not set then is is a post-index load. + // + // If wback is set rn is updated - this is a pre or post index load, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT((offset & ~0xff) == 0); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T4, rn, rt, offset); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDR_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDR_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt6()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDRH_imm_T1, imm.getUInt6() >> 2, rn, rt); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRH_imm_T2, rn, rt, imm.getUInt12()); + } + + // If index is set, this is a regular offset or a pre-indexed load; + // if index is not set then is is a post-index load. + // + // If wback is set rn is updated - this is a pre or post index load, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT((offset & ~0xff) == 0); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRH_imm_T3, rn, rt, offset); + } + + ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(!BadReg(rt)); // Memory hint + ASSERT(rn != ARMRegisters::pc); // LDRH (literal) + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRH_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDRH_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + void ldrb(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt5()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDRB_imm_T1, imm.getUInt5(), rn, rt); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRB_imm_T2, rn, rt, imm.getUInt12()); + } + + void ldrb(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + + ASSERT(!(offset & ~0xff)); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRB_imm_T3, rn, rt, offset); + } + + ALWAYS_INLINE void ldrb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRB_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDRB_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + void ldrsb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRSB_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDRSB_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + void ldrsh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRSH_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDRSH_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + void lsl(RegisterID rd, RegisterID rm, int32_t shiftAmount) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + ShiftTypeAndAmount shift(SRType_LSL, shiftAmount); + m_formatter.twoWordOp16FourFours(OP_LSL_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void lsl(RegisterID rd, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_LSL_reg_T2, rn, FourFours(0xf, rd, 0, rm)); + } + + ALWAYS_INLINE void lsr(RegisterID rd, RegisterID rm, int32_t shiftAmount) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + ShiftTypeAndAmount shift(SRType_LSR, shiftAmount); + m_formatter.twoWordOp16FourFours(OP_LSR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void lsr(RegisterID rd, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_LSR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); + } + + ALWAYS_INLINE void movT3(RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isValid()); + ASSERT(!imm.isEncodedImm()); + ASSERT(!BadReg(rd)); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOV_imm_T3, imm.m_value.imm4, rd, imm); + } + + static void revertJumpTo_movT3(void* instructionStart, RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isValid()); + ASSERT(!imm.isEncodedImm()); + ASSERT(!BadReg(rd)); + + uint16_t* address = static_cast(instructionStart); + address[0] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, imm); + address[1] = twoWordOp5i6Imm4Reg4EncodedImmSecond(rd, imm); + cacheFlush(address, sizeof(uint16_t) * 2); + } + + ALWAYS_INLINE void mov(RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isValid()); + ASSERT(!BadReg(rd)); + + if ((rd < 8) && imm.isUInt8()) + m_formatter.oneWordOp5Reg3Imm8(OP_MOV_imm_T1, rd, imm.getUInt8()); + else if (imm.isEncodedImm()) + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOV_imm_T2, 0xf, rd, imm); + else + movT3(rd, imm); + } + + ALWAYS_INLINE void mov(RegisterID rd, RegisterID rm) + { + m_formatter.oneWordOp8RegReg143(OP_MOV_reg_T1, rm, rd); + } + + ALWAYS_INLINE void movt(RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isUInt16()); + ASSERT(!BadReg(rd)); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOVT, imm.m_value.imm4, rd, imm); + } + + ALWAYS_INLINE void mvn(RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isEncodedImm()); + ASSERT(!BadReg(rd)); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MVN_imm, 0xf, rd, imm); + } + + ALWAYS_INLINE void mvn(RegisterID rd, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp16FourFours(OP_MVN_reg_T2, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void mvn(RegisterID rd, RegisterID rm) + { + if (!((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_MVN_reg_T1, rm, rd); + else + mvn(rd, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void neg(RegisterID rd, RegisterID rm) + { + ARMThumbImmediate zero = ARMThumbImmediate::makeUInt12(0); + sub(rd, zero, rm); + } + + ALWAYS_INLINE void orr(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(imm.isEncodedImm()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ORR_imm_T1, rn, rd, imm); + } + + ALWAYS_INLINE void orr(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ORR_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + void orr(RegisterID rd, RegisterID rn, RegisterID rm) + { + if ((rd == rn) && !((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rm, rd); + else if ((rd == rm) && !((rd | rn) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rn, rd); + else + orr(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void orr_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ORR_S_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + void orr_S(RegisterID rd, RegisterID rn, RegisterID rm) + { + if ((rd == rn) && !((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rm, rd); + else if ((rd == rm) && !((rd | rn) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rn, rd); + else + orr_S(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void ror(RegisterID rd, RegisterID rm, int32_t shiftAmount) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + ShiftTypeAndAmount shift(SRType_ROR, shiftAmount); + m_formatter.twoWordOp16FourFours(OP_ROR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void ror(RegisterID rd, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ROR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); + } + + ALWAYS_INLINE void smull(RegisterID rdLo, RegisterID rdHi, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rdLo)); + ASSERT(!BadReg(rdHi)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + ASSERT(rdLo != rdHi); + m_formatter.twoWordOp12Reg4FourFours(OP_SMULL_T1, rn, FourFours(rdLo, rdHi, 0, rm)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt7()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STR_imm_T1, imm.getUInt7() >> 2, rn, rt); + else if ((rn == ARMRegisters::sp) && !(rt & 8) && imm.isUInt10()) + m_formatter.oneWordOp5Reg3Imm8(OP_STR_imm_T2, rt, static_cast(imm.getUInt10() >> 2)); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STR_imm_T3, rn, rt, imm.getUInt12()); + } + + // If index is set, this is a regular offset or a pre-indexed store; + // if index is not set then is is a post-index store. + // + // If wback is set rn is updated - this is a pre or post index store, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT((offset & ~0xff) == 0); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STR_imm_T4, rn, rt, offset); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STR_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_STR_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt7()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STRB_imm_T1, imm.getUInt7() >> 2, rn, rt); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRB_imm_T2, rn, rt, imm.getUInt12()); + } + + // If index is set, this is a regular offset or a pre-indexed store; + // if index is not set then is is a post-index store. + // + // If wback is set rn is updated - this is a pre or post index store, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT((offset & ~0xff) == 0); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRB_imm_T3, rn, rt, offset); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STRB_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_STRB_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt7()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STRH_imm_T1, imm.getUInt7() >> 2, rn, rt); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRH_imm_T2, rn, rt, imm.getUInt12()); + } + + // If index is set, this is a regular offset or a pre-indexed store; + // if index is not set then is is a post-index store. + // + // If wback is set rn is updated - this is a pre or post index store, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT(!(offset & ~0xff)); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRH_imm_T3, rn, rt, offset); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STRH_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_STRH_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + + if ((rn == ARMRegisters::sp) && (rd == ARMRegisters::sp) && imm.isUInt9()) { + ASSERT(!(imm.getUInt16() & 3)); + m_formatter.oneWordOp9Imm7(OP_SUB_SP_imm_T1, static_cast(imm.getUInt9() >> 2)); + return; + } else if (!((rd | rn) & 8)) { + if (imm.isUInt3()) { + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); + return; + } else if ((rd == rn) && imm.isUInt8()) { + m_formatter.oneWordOp5Reg3Imm8(OP_SUB_imm_T2, rd, imm.getUInt8()); + return; + } + } + + if (imm.isEncodedImm()) + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_imm_T3, rn, rd, imm); + else { + ASSERT(imm.isUInt12()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_imm_T4, rn, rd, imm); + } + } + + ALWAYS_INLINE void sub(RegisterID rd, ARMThumbImmediate imm, RegisterID rn) + { + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + ASSERT(imm.isUInt12()); + + if (!((rd | rn) & 8) && !imm.getUInt12()) + m_formatter.oneWordOp10Reg3Reg3(OP_RSB_imm_T1, rn, rd); + else + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_RSB_imm_T2, rn, rd, imm); + } + + ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_SUB_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // NOTE: In an IT block, add doesn't modify the flags register. + ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, RegisterID rm) + { + if (!((rd | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_reg_T1, rm, rn, rd); + else + sub(rd, rn, rm, ShiftTypeAndAmount()); + } + + // Not allowed in an IT (if then) block. + void sub_S(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + + if ((rn == ARMRegisters::sp) && (rd == ARMRegisters::sp) && imm.isUInt9()) { + ASSERT(!(imm.getUInt16() & 3)); + m_formatter.oneWordOp9Imm7(OP_SUB_SP_imm_T1, static_cast(imm.getUInt9() >> 2)); + return; + } else if (!((rd | rn) & 8)) { + if (imm.isUInt3()) { + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); + return; + } else if ((rd == rn) && imm.isUInt8()) { + m_formatter.oneWordOp5Reg3Imm8(OP_SUB_imm_T2, rd, imm.getUInt8()); + return; + } + } + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_S_imm_T3, rn, rd, imm); + } + + ALWAYS_INLINE void sub_S(RegisterID rd, ARMThumbImmediate imm, RegisterID rn) + { + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + ASSERT(imm.isUInt12()); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_RSB_S_imm_T2, rn, rd, imm); + } + + // Not allowed in an IT (if then) block? + ALWAYS_INLINE void sub_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_SUB_S_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // Not allowed in an IT (if then) block. + ALWAYS_INLINE void sub_S(RegisterID rd, RegisterID rn, RegisterID rm) + { + if (!((rd | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_reg_T1, rm, rn, rd); + else + sub_S(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void tst(RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(!BadReg(rn)); + ASSERT(imm.isEncodedImm()); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_TST_imm, rn, (RegisterID)0xf, imm); + } + + ALWAYS_INLINE void tst(RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_TST_reg_T2, rn, FourFours(shift.hi4(), 0xf, shift.lo4(), rm)); + } + + ALWAYS_INLINE void tst(RegisterID rn, RegisterID rm) + { + if ((rn | rm) & 8) + tst(rn, rm, ShiftTypeAndAmount()); + else + m_formatter.oneWordOp10Reg3Reg3(OP_TST_reg_T1, rm, rn); + } + + ALWAYS_INLINE void ubfx(RegisterID rd, RegisterID rn, unsigned lsb, unsigned width) + { + ASSERT(lsb < 32); + ASSERT((width >= 1) && (width <= 32)); + ASSERT((lsb + width) <= 32); + m_formatter.twoWordOp12Reg40Imm3Reg4Imm20Imm5(OP_UBFX_T1, rd, rn, (lsb & 0x1c) << 10, (lsb & 0x3) << 6, (width - 1) & 0x1f); + } + + void vadd(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VADD_T2, OP_VADD_T2b, true, rn, rd, rm); + } + + void vcmp(FPDoubleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VCMP, OP_VCMPb, true, VFPOperand(4), rd, rm); + } + + void vcmpz(FPDoubleRegisterID rd) + { + m_formatter.vfpOp(OP_VCMP, OP_VCMPb, true, VFPOperand(5), rd, VFPOperand(0)); + } + + void vcvt_signedToFloatingPoint(FPDoubleRegisterID rd, FPSingleRegisterID rm) + { + // boolean values are 64bit (toInt, unsigned, roundZero) + m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(false, false, false), rd, rm); + } + + void vcvt_floatingPointToSigned(FPSingleRegisterID rd, FPDoubleRegisterID rm) + { + // boolean values are 64bit (toInt, unsigned, roundZero) + m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(true, false, true), rd, rm); + } + + void vcvt_floatingPointToUnsigned(FPSingleRegisterID rd, FPDoubleRegisterID rm) + { + // boolean values are 64bit (toInt, unsigned, roundZero) + m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(true, true, true), rd, rm); + } + + void vdiv(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VDIV, OP_VDIVb, true, rn, rd, rm); + } + + void vldr(FPDoubleRegisterID rd, RegisterID rn, int32_t imm) + { + m_formatter.vfpMemOp(OP_VLDR, OP_VLDRb, true, rn, rd, imm); + } + + void flds(FPSingleRegisterID rd, RegisterID rn, int32_t imm) + { + m_formatter.vfpMemOp(OP_FLDS, OP_FLDSb, false, rn, rd, imm); + } + + void vmov(RegisterID rd, FPSingleRegisterID rn) + { + ASSERT(!BadReg(rd)); + m_formatter.vfpOp(OP_VMOV_StoC, OP_VMOV_StoCb, false, rn, rd, VFPOperand(0)); + } + + void vmov(FPSingleRegisterID rd, RegisterID rn) + { + ASSERT(!BadReg(rn)); + m_formatter.vfpOp(OP_VMOV_CtoS, OP_VMOV_CtoSb, false, rd, rn, VFPOperand(0)); + } + + void vmov(RegisterID rd1, RegisterID rd2, FPDoubleRegisterID rn) + { + ASSERT(!BadReg(rd1)); + ASSERT(!BadReg(rd2)); + m_formatter.vfpOp(OP_VMOV_DtoC, OP_VMOV_DtoCb, true, rd2, VFPOperand(rd1 | 16), rn); + } + + void vmov(FPDoubleRegisterID rd, RegisterID rn1, RegisterID rn2) + { + ASSERT(!BadReg(rn1)); + ASSERT(!BadReg(rn2)); + m_formatter.vfpOp(OP_VMOV_CtoD, OP_VMOV_CtoDb, true, rn2, VFPOperand(rn1 | 16), rd); + } + + void vmov(FPDoubleRegisterID rd, FPDoubleRegisterID rn) + { + m_formatter.vfpOp(OP_VMOV_T2, OP_VMOV_T2b, true, VFPOperand(0), rd, rn); + } + + void vmrs(RegisterID reg = ARMRegisters::pc) + { + ASSERT(reg != ARMRegisters::sp); + m_formatter.vfpOp(OP_VMRS, OP_VMRSb, false, VFPOperand(1), VFPOperand(0x10 | reg), VFPOperand(0)); + } + + void vmul(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VMUL_T2, OP_VMUL_T2b, true, rn, rd, rm); + } + + void vstr(FPDoubleRegisterID rd, RegisterID rn, int32_t imm) + { + m_formatter.vfpMemOp(OP_VSTR, OP_VSTRb, true, rn, rd, imm); + } + + void fsts(FPSingleRegisterID rd, RegisterID rn, int32_t imm) + { + m_formatter.vfpMemOp(OP_FSTS, OP_FSTSb, false, rn, rd, imm); + } + + void vsub(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VSUB_T2, OP_VSUB_T2b, true, rn, rd, rm); + } + + void vabs(FPDoubleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VABS_T2, OP_VABS_T2b, true, VFPOperand(16), rd, rm); + } + + void vneg(FPDoubleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VNEG_T2, OP_VNEG_T2b, true, VFPOperand(1), rd, rm); + } + + void vsqrt(FPDoubleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VSQRT_T1, OP_VSQRT_T1b, true, VFPOperand(17), rd, rm); + } + + void vcvtds(FPDoubleRegisterID rd, FPSingleRegisterID rm) + { + m_formatter.vfpOp(OP_VCVTDS_T1, OP_VCVTDS_T1b, false, VFPOperand(23), rd, rm); + } + + void vcvtsd(FPSingleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VCVTSD_T1, OP_VCVTSD_T1b, true, VFPOperand(23), rd, rm); + } + + void nop() + { + m_formatter.oneWordOp8Imm8(OP_NOP_T1, 0); + } + + AssemblerLabel labelIgnoringWatchpoints() + { + return m_formatter.label(); + } + + AssemblerLabel labelForWatchpoint() + { + AssemblerLabel result = m_formatter.label(); + if (static_cast(result.m_offset) != m_indexOfLastWatchpoint) + result = label(); + m_indexOfLastWatchpoint = result.m_offset; + m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); + return result; + } + + AssemblerLabel label() + { + AssemblerLabel result = m_formatter.label(); + while (UNLIKELY(static_cast(result.m_offset) < m_indexOfTailOfLastWatchpoint)) { + nop(); + result = m_formatter.label(); + } + return result; + } + + AssemblerLabel align(int alignment) + { + while (!m_formatter.isAligned(alignment)) + bkpt(); + + return label(); + } + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + ASSERT(label.isSet()); + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + int executableOffsetFor(int location) + { + if (!location) + return 0; + return static_cast(m_formatter.data())[location / sizeof(int32_t) - 1]; + } + + int jumpSizeDelta(JumpType jumpType, JumpLinkType jumpLinkType) { return JUMP_ENUM_SIZE(jumpType) - JUMP_ENUM_SIZE(jumpLinkType); } + + // Assembler admin methods: + + static ALWAYS_INLINE bool linkRecordSourceComparator(const LinkRecord& a, const LinkRecord& b) + { + return a.from() < b.from(); + } + + bool canCompact(JumpType jumpType) + { + // The following cannot be compacted: + // JumpFixed: represents custom jump sequence + // JumpNoConditionFixedSize: represents unconditional jump that must remain a fixed size + // JumpConditionFixedSize: represents conditional jump that must remain a fixed size + return (jumpType == JumpNoCondition) || (jumpType == JumpCondition); + } + + JumpLinkType computeJumpType(JumpType jumpType, const uint8_t* from, const uint8_t* to) + { + if (jumpType == JumpFixed) + return LinkInvalid; + + // for patchable jump we must leave space for the longest code sequence + if (jumpType == JumpNoConditionFixedSize) + return LinkBX; + if (jumpType == JumpConditionFixedSize) + return LinkConditionalBX; + + const int paddingSize = JUMP_ENUM_SIZE(jumpType); + + if (jumpType == JumpCondition) { + // 2-byte conditional T1 + const uint16_t* jumpT1Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT1))); + if (canBeJumpT1(jumpT1Location, to)) + return LinkJumpT1; + // 4-byte conditional T3 + const uint16_t* jumpT3Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT3))); + if (canBeJumpT3(jumpT3Location, to)) + return LinkJumpT3; + // 4-byte conditional T4 with IT + const uint16_t* conditionalJumpT4Location = + reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkConditionalJumpT4))); + if (canBeJumpT4(conditionalJumpT4Location, to)) + return LinkConditionalJumpT4; + } else { + // 2-byte unconditional T2 + const uint16_t* jumpT2Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT2))); + if (canBeJumpT2(jumpT2Location, to)) + return LinkJumpT2; + // 4-byte unconditional T4 + const uint16_t* jumpT4Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT4))); + if (canBeJumpT4(jumpT4Location, to)) + return LinkJumpT4; + // use long jump sequence + return LinkBX; + } + + ASSERT(jumpType == JumpCondition); + return LinkConditionalBX; + } + + JumpLinkType computeJumpType(LinkRecord& record, const uint8_t* from, const uint8_t* to) + { + JumpLinkType linkType = computeJumpType(record.type(), from, to); + record.setLinkType(linkType); + return linkType; + } + + void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset) + { + int32_t ptr = regionStart / sizeof(int32_t); + const int32_t end = regionEnd / sizeof(int32_t); + int32_t* offsets = static_cast(m_formatter.data()); + while (ptr < end) + offsets[ptr++] = offset; + } + + Vector& jumpsToLink() + { + std::sort(m_jumpsToLink.begin(), m_jumpsToLink.end(), linkRecordSourceComparator); + return m_jumpsToLink; + } + + void ALWAYS_INLINE link(LinkRecord& record, uint8_t* from, uint8_t* to) + { + switch (record.linkType()) { + case LinkJumpT1: + linkJumpT1(record.condition(), reinterpret_cast_ptr(from), to); + break; + case LinkJumpT2: + linkJumpT2(reinterpret_cast_ptr(from), to); + break; + case LinkJumpT3: + linkJumpT3(record.condition(), reinterpret_cast_ptr(from), to); + break; + case LinkJumpT4: + linkJumpT4(reinterpret_cast_ptr(from), to); + break; + case LinkConditionalJumpT4: + linkConditionalJumpT4(record.condition(), reinterpret_cast_ptr(from), to); + break; + case LinkConditionalBX: + linkConditionalBX(record.condition(), reinterpret_cast_ptr(from), to); + break; + case LinkBX: + linkBX(reinterpret_cast_ptr(from), to); + break; + default: + ASSERT_NOT_REACHED(); + break; + } + } + + void* unlinkedCode() { return m_formatter.data(); } + size_t codeSize() const { return m_formatter.codeSize(); } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + ASSERT(call.isSet()); + return call.m_offset; + } + + // Linking & patching: + // + // 'link' and 'patch' methods are for use on unprotected code - such as the code + // within the AssemblerBuffer, and code being patched by the patch buffer. Once + // code has been finalized it is (platform support permitting) within a non- + // writable region of memory; to modify the code in an execute-only execuable + // pool the 'repatch' and 'relink' methods should be used. + + void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type, Condition condition) + { + ASSERT(to.isSet()); + ASSERT(from.isSet()); + m_jumpsToLink.append(LinkRecord(from.m_offset, to.m_offset, type, condition)); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + + uint16_t* location = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + linkJumpAbsolute(location, to); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + ASSERT(!(reinterpret_cast(code) & 1)); + ASSERT(from.isSet()); + ASSERT(reinterpret_cast(to) & 1); + + setPointer(reinterpret_cast(reinterpret_cast(code) + from.m_offset) - 1, to, false); + } + + static void linkPointer(void* code, AssemblerLabel where, void* value) + { + setPointer(reinterpret_cast(code) + where.m_offset, value, false); + } + + static void relinkJump(void* from, void* to) + { + ASSERT(!(reinterpret_cast(from) & 1)); + ASSERT(!(reinterpret_cast(to) & 1)); + + linkJumpAbsolute(reinterpret_cast(from), to); + + cacheFlush(reinterpret_cast(from) - 5, 5 * sizeof(uint16_t)); + } + + static void relinkCall(void* from, void* to) + { + ASSERT(!(reinterpret_cast(from) & 1)); + ASSERT(reinterpret_cast(to) & 1); + + setPointer(reinterpret_cast(from) - 1, to, true); + } + + static void* readCallTarget(void* from) + { + return readPointer(reinterpret_cast(from) - 1); + } + + static void repatchInt32(void* where, int32_t value) + { + ASSERT(!(reinterpret_cast(where) & 1)); + + setInt32(where, value, true); + } + + static void repatchCompact(void* where, int32_t offset) + { + ASSERT(offset >= -255 && offset <= 255); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + + offset |= (add << 9); + offset |= (1 << 10); + offset |= (1 << 11); + + uint16_t* location = reinterpret_cast(where); + location[1] &= ~((1 << 12) - 1); + location[1] |= offset; + cacheFlush(location, sizeof(uint16_t) * 2); + } + + static void repatchPointer(void* where, void* value) + { + ASSERT(!(reinterpret_cast(where) & 1)); + + setPointer(where, value, true); + } + + static void* readPointer(void* where) + { + return reinterpret_cast(readInt32(where)); + } + + static void replaceWithJump(void* instructionStart, void* to) + { + ASSERT(!(bitwise_cast(instructionStart) & 1)); + ASSERT(!(bitwise_cast(to) & 1)); + uint16_t* ptr = reinterpret_cast(instructionStart) + 2; + + linkJumpT4(ptr, to); + cacheFlush(ptr - 2, sizeof(uint16_t) * 2); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return 4; + } + + static void replaceWithLoad(void* instructionStart) + { + ASSERT(!(bitwise_cast(instructionStart) & 1)); + uint16_t* ptr = reinterpret_cast(instructionStart); + switch (ptr[0] & 0xFFF0) { + case OP_LDR_imm_T3: + break; + case OP_ADD_imm_T3: + ASSERT(!(ptr[1] & 0xF000)); + ptr[0] &= 0x000F; + ptr[0] |= OP_LDR_imm_T3; + ptr[1] |= (ptr[1] & 0x0F00) << 4; + ptr[1] &= 0xF0FF; + cacheFlush(ptr, sizeof(uint16_t) * 2); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + static void replaceWithAddressComputation(void* instructionStart) + { + ASSERT(!(bitwise_cast(instructionStart) & 1)); + uint16_t* ptr = reinterpret_cast(instructionStart); + switch (ptr[0] & 0xFFF0) { + case OP_LDR_imm_T3: + ASSERT(!(ptr[1] & 0x0F00)); + ptr[0] &= 0x000F; + ptr[0] |= OP_ADD_imm_T3; + ptr[1] |= (ptr[1] & 0xF000) >> 4; + ptr[1] &= 0x0FFF; + cacheFlush(ptr, sizeof(uint16_t) * 2); + break; + case OP_ADD_imm_T3: + break; + default: + ASSERT_NOT_REACHED(); + } + } + + unsigned debugOffset() { return m_formatter.debugOffset(); } + + static void cacheFlush(void* code, size_t size) + { +#if OS(IOS) + sys_cache_control(kCacheFunctionPrepareForExecution, code, size); +#elif OS(LINUX) + asm volatile( + "push {r7}\n" + "mov r0, %0\n" + "mov r1, %1\n" + "movw r7, #0x2\n" + "movt r7, #0xf\n" + "movs r2, #0x0\n" + "svc 0x0\n" + "pop {r7}\n" + : + : "r" (code), "r" (reinterpret_cast(code) + size) + : "r0", "r1", "r2"); +#elif OS(WINCE) + CacheRangeFlush(code, size, CACHE_SYNC_ALL); +#elif OS(QNX) +#if !ENABLE(ASSEMBLER_WX_EXCLUSIVE) + msync(code, size, MS_INVALIDATE_ICACHE); +#else + UNUSED_PARAM(code); + UNUSED_PARAM(size); +#endif +#else +#error "The cacheFlush support is missing on this platform." +#endif + } + +private: + // VFP operations commonly take one or more 5-bit operands, typically representing a + // floating point register number. This will commonly be encoded in the instruction + // in two parts, with one single bit field, and one 4-bit field. In the case of + // double precision operands the high bit of the register number will be encoded + // separately, and for single precision operands the high bit of the register number + // will be encoded individually. + // VFPOperand encapsulates a 5-bit VFP operand, with bits 0..3 containing the 4-bit + // field to be encoded together in the instruction (the low 4-bits of a double + // register number, or the high 4-bits of a single register number), and bit 4 + // contains the bit value to be encoded individually. + struct VFPOperand { + explicit VFPOperand(uint32_t value) + : m_value(value) + { + ASSERT(!(m_value & ~0x1f)); + } + + VFPOperand(FPDoubleRegisterID reg) + : m_value(reg) + { + } + + VFPOperand(RegisterID reg) + : m_value(reg) + { + } + + VFPOperand(FPSingleRegisterID reg) + : m_value(((reg & 1) << 4) | (reg >> 1)) // rotate the lowest bit of 'reg' to the top. + { + } + + uint32_t bits1() + { + return m_value >> 4; + } + + uint32_t bits4() + { + return m_value & 0xf; + } + + uint32_t m_value; + }; + + VFPOperand vcvtOp(bool toInteger, bool isUnsigned, bool isRoundZero) + { + // Cannot specify rounding when converting to float. + ASSERT(toInteger || !isRoundZero); + + uint32_t op = 0x8; + if (toInteger) { + // opc2 indicates both toInteger & isUnsigned. + op |= isUnsigned ? 0x4 : 0x5; + // 'op' field in instruction is isRoundZero + if (isRoundZero) + op |= 0x10; + } else { + ASSERT(!isRoundZero); + // 'op' field in instruction is isUnsigned + if (!isUnsigned) + op |= 0x10; + } + return VFPOperand(op); + } + + static void setInt32(void* code, uint32_t value, bool flush) + { + uint16_t* location = reinterpret_cast(code); + ASSERT(isMOV_imm_T3(location - 4) && isMOVT(location - 2)); + + ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(value)); + ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(value >> 16)); + location[-4] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); + location[-3] = twoWordOp5i6Imm4Reg4EncodedImmSecond((location[-3] >> 8) & 0xf, lo16); + location[-2] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); + location[-1] = twoWordOp5i6Imm4Reg4EncodedImmSecond((location[-1] >> 8) & 0xf, hi16); + + if (flush) + cacheFlush(location - 4, 4 * sizeof(uint16_t)); + } + + static int32_t readInt32(void* code) + { + uint16_t* location = reinterpret_cast(code); + ASSERT(isMOV_imm_T3(location - 4) && isMOVT(location - 2)); + + ARMThumbImmediate lo16; + ARMThumbImmediate hi16; + decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(lo16, location[-4]); + decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(lo16, location[-3]); + decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(hi16, location[-2]); + decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(hi16, location[-1]); + uint32_t result = hi16.asUInt16(); + result <<= 16; + result |= lo16.asUInt16(); + return static_cast(result); + } + + static void setUInt7ForLoad(void* code, ARMThumbImmediate imm) + { + // Requires us to have planted a LDR_imm_T1 + ASSERT(imm.isValid()); + ASSERT(imm.isUInt7()); + uint16_t* location = reinterpret_cast(code); + location[0] &= ~((static_cast(0x7f) >> 2) << 6); + location[0] |= (imm.getUInt7() >> 2) << 6; + cacheFlush(location, sizeof(uint16_t)); + } + + static void setPointer(void* code, void* value, bool flush) + { + setInt32(code, reinterpret_cast(value), flush); + } + + static bool isB(void* address) + { + uint16_t* instruction = static_cast(address); + return ((instruction[0] & 0xf800) == OP_B_T4a) && ((instruction[1] & 0xd000) == OP_B_T4b); + } + + static bool isBX(void* address) + { + uint16_t* instruction = static_cast(address); + return (instruction[0] & 0xff87) == OP_BX; + } + + static bool isMOV_imm_T3(void* address) + { + uint16_t* instruction = static_cast(address); + return ((instruction[0] & 0xFBF0) == OP_MOV_imm_T3) && ((instruction[1] & 0x8000) == 0); + } + + static bool isMOVT(void* address) + { + uint16_t* instruction = static_cast(address); + return ((instruction[0] & 0xFBF0) == OP_MOVT) && ((instruction[1] & 0x8000) == 0); + } + + static bool isNOP_T1(void* address) + { + uint16_t* instruction = static_cast(address); + return instruction[0] == OP_NOP_T1; + } + + static bool isNOP_T2(void* address) + { + uint16_t* instruction = static_cast(address); + return (instruction[0] == OP_NOP_T2a) && (instruction[1] == OP_NOP_T2b); + } + + static bool canBeJumpT1(const uint16_t* instruction, const void* target) + { + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T1 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + return ((relative << 23) >> 23) == relative; + } + + static bool canBeJumpT2(const uint16_t* instruction, const void* target) + { + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T2 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + return ((relative << 20) >> 20) == relative; + } + + static bool canBeJumpT3(const uint16_t* instruction, const void* target) + { + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + return ((relative << 11) >> 11) == relative; + } + + static bool canBeJumpT4(const uint16_t* instruction, const void* target) + { + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + return ((relative << 7) >> 7) == relative; + } + + void linkJumpT1(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + ASSERT(canBeJumpT1(instruction, target)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T1 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-1] = OP_B_T1 | ((cond & 0xf) << 8) | ((relative & 0x1fe) >> 1); + } + + static void linkJumpT2(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + ASSERT(canBeJumpT2(instruction, target)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T2 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-1] = OP_B_T2 | ((relative & 0xffe) >> 1); + } + + void linkJumpT3(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + ASSERT(canBeJumpT3(instruction, target)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-2] = OP_B_T3a | ((relative & 0x100000) >> 10) | ((cond & 0xf) << 6) | ((relative & 0x3f000) >> 12); + instruction[-1] = OP_B_T3b | ((relative & 0x80000) >> 8) | ((relative & 0x40000) >> 5) | ((relative & 0xffe) >> 1); + } + + static void linkJumpT4(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + ASSERT(canBeJumpT4(instruction, target)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // ARM encoding for the top two bits below the sign bit is 'peculiar'. + if (relative >= 0) + relative ^= 0xC00000; + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-2] = OP_B_T4a | ((relative & 0x1000000) >> 14) | ((relative & 0x3ff000) >> 12); + instruction[-1] = OP_B_T4b | ((relative & 0x800000) >> 10) | ((relative & 0x400000) >> 11) | ((relative & 0xffe) >> 1); + } + + void linkConditionalJumpT4(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + instruction[-3] = ifThenElse(cond) | OP_IT; + linkJumpT4(instruction, target); + } + + static void linkBX(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; + ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) + 1)); + ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) >> 16)); + instruction[-5] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); + instruction[-4] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, lo16); + instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); + instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); + instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); + } + + void linkConditionalBX(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + linkBX(instruction, target); + instruction[-6] = ifThenElse(cond, true, true) | OP_IT; + } + + static void linkJumpAbsolute(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + ASSERT((isMOV_imm_T3(instruction - 5) && isMOVT(instruction - 3) && isBX(instruction - 1)) + || (isNOP_T1(instruction - 5) && isNOP_T2(instruction - 4) && isB(instruction - 2))); + + if (canBeJumpT4(instruction, target)) { + // There may be a better way to fix this, but right now put the NOPs first, since in the + // case of an conditional branch this will be coming after an ITTT predicating *three* + // instructions! Looking backwards to modify the ITTT to an IT is not easy, due to + // variable wdith encoding - the previous instruction might *look* like an ITTT but + // actually be the second half of a 2-word op. + instruction[-5] = OP_NOP_T1; + instruction[-4] = OP_NOP_T2a; + instruction[-3] = OP_NOP_T2b; + linkJumpT4(instruction, target); + } else { + const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; + ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) + 1)); + ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) >> 16)); + instruction[-5] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); + instruction[-4] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, lo16); + instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); + instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); + instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); + } + } + + static uint16_t twoWordOp5i6Imm4Reg4EncodedImmFirst(uint16_t op, ARMThumbImmediate imm) + { + return op | (imm.m_value.i << 10) | imm.m_value.imm4; + } + + static void decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(ARMThumbImmediate& result, uint16_t value) + { + result.m_value.i = (value >> 10) & 1; + result.m_value.imm4 = value & 15; + } + + static uint16_t twoWordOp5i6Imm4Reg4EncodedImmSecond(uint16_t rd, ARMThumbImmediate imm) + { + return (imm.m_value.imm3 << 12) | (rd << 8) | imm.m_value.imm8; + } + + static void decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(ARMThumbImmediate& result, uint16_t value) + { + result.m_value.imm3 = (value >> 12) & 7; + result.m_value.imm8 = value & 255; + } + + class ARMInstructionFormatter { + public: + ALWAYS_INLINE void oneWordOp5Reg3Imm8(OpcodeID op, RegisterID rd, uint8_t imm) + { + m_buffer.putShort(op | (rd << 8) | imm); + } + + ALWAYS_INLINE void oneWordOp5Imm5Reg3Reg3(OpcodeID op, uint8_t imm, RegisterID reg1, RegisterID reg2) + { + m_buffer.putShort(op | (imm << 6) | (reg1 << 3) | reg2); + } + + ALWAYS_INLINE void oneWordOp7Reg3Reg3Reg3(OpcodeID op, RegisterID reg1, RegisterID reg2, RegisterID reg3) + { + m_buffer.putShort(op | (reg1 << 6) | (reg2 << 3) | reg3); + } + + ALWAYS_INLINE void oneWordOp8Imm8(OpcodeID op, uint8_t imm) + { + m_buffer.putShort(op | imm); + } + + ALWAYS_INLINE void oneWordOp8RegReg143(OpcodeID op, RegisterID reg1, RegisterID reg2) + { + m_buffer.putShort(op | ((reg2 & 8) << 4) | (reg1 << 3) | (reg2 & 7)); + } + + ALWAYS_INLINE void oneWordOp9Imm7(OpcodeID op, uint8_t imm) + { + m_buffer.putShort(op | imm); + } + + ALWAYS_INLINE void oneWordOp10Reg3Reg3(OpcodeID op, RegisterID reg1, RegisterID reg2) + { + m_buffer.putShort(op | (reg1 << 3) | reg2); + } + + ALWAYS_INLINE void twoWordOp12Reg4FourFours(OpcodeID1 op, RegisterID reg, FourFours ff) + { + m_buffer.putShort(op | reg); + m_buffer.putShort(ff.m_u.value); + } + + ALWAYS_INLINE void twoWordOp16FourFours(OpcodeID1 op, FourFours ff) + { + m_buffer.putShort(op); + m_buffer.putShort(ff.m_u.value); + } + + ALWAYS_INLINE void twoWordOp16Op16(OpcodeID1 op1, OpcodeID2 op2) + { + m_buffer.putShort(op1); + m_buffer.putShort(op2); + } + + ALWAYS_INLINE void twoWordOp5i6Imm4Reg4EncodedImm(OpcodeID1 op, int imm4, RegisterID rd, ARMThumbImmediate imm) + { + ARMThumbImmediate newImm = imm; + newImm.m_value.imm4 = imm4; + + m_buffer.putShort(ARMv7Assembler::twoWordOp5i6Imm4Reg4EncodedImmFirst(op, newImm)); + m_buffer.putShort(ARMv7Assembler::twoWordOp5i6Imm4Reg4EncodedImmSecond(rd, newImm)); + } + + ALWAYS_INLINE void twoWordOp12Reg4Reg4Imm12(OpcodeID1 op, RegisterID reg1, RegisterID reg2, uint16_t imm) + { + m_buffer.putShort(op | reg1); + m_buffer.putShort((reg2 << 12) | imm); + } + + ALWAYS_INLINE void twoWordOp12Reg40Imm3Reg4Imm20Imm5(OpcodeID1 op, RegisterID reg1, RegisterID reg2, uint16_t imm1, uint16_t imm2, uint16_t imm3) + { + m_buffer.putShort(op | reg1); + m_buffer.putShort((imm1 << 12) | (reg2 << 8) | (imm2 << 6) | imm3); + } + + // Formats up instructions of the pattern: + // 111111111B11aaaa:bbbb222SA2C2cccc + // Where 1s in the pattern come from op1, 2s in the pattern come from op2, S is the provided size bit. + // Operands provide 5 bit values of the form Aaaaa, Bbbbb, Ccccc. + ALWAYS_INLINE void vfpOp(OpcodeID1 op1, OpcodeID2 op2, bool size, VFPOperand a, VFPOperand b, VFPOperand c) + { + ASSERT(!(op1 & 0x004f)); + ASSERT(!(op2 & 0xf1af)); + m_buffer.putShort(op1 | b.bits1() << 6 | a.bits4()); + m_buffer.putShort(op2 | b.bits4() << 12 | size << 8 | a.bits1() << 7 | c.bits1() << 5 | c.bits4()); + } + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + // (i.e. +/-(0..255) 32-bit words) + ALWAYS_INLINE void vfpMemOp(OpcodeID1 op1, OpcodeID2 op2, bool size, RegisterID rn, VFPOperand rd, int32_t imm) + { + bool up = true; + if (imm < 0) { + imm = -imm; + up = false; + } + + uint32_t offset = imm; + ASSERT(!(offset & ~0x3fc)); + offset >>= 2; + + m_buffer.putShort(op1 | (up << 7) | rd.bits1() << 6 | rn); + m_buffer.putShort(op2 | rd.bits4() << 12 | size << 8 | offset); + } + + // Administrative methods: + + size_t codeSize() const { return m_buffer.codeSize(); } + AssemblerLabel label() const { return m_buffer.label(); } + bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); } + void* data() const { return m_buffer.data(); } + + unsigned debugOffset() { return m_buffer.debugOffset(); } + + private: + AssemblerBuffer m_buffer; + } m_formatter; + + Vector m_jumpsToLink; + Vector m_offsets; + int m_indexOfLastWatchpoint; + int m_indexOfTailOfLastWatchpoint; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) + +#endif // ARMAssembler_h diff --git a/3rdparty/masm/assembler/AbstractMacroAssembler.h b/3rdparty/masm/assembler/AbstractMacroAssembler.h new file mode 100644 index 0000000000..ee78ef84eb --- /dev/null +++ b/3rdparty/masm/assembler/AbstractMacroAssembler.h @@ -0,0 +1,792 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AbstractMacroAssembler_h +#define AbstractMacroAssembler_h + +#include "AssemblerBuffer.h" +#include "CodeLocation.h" +#include "MacroAssemblerCodeRef.h" +#include +#include +#include + +#if ENABLE(ASSEMBLER) + + +#if PLATFORM(QT) +#define ENABLE_JIT_CONSTANT_BLINDING 0 +#endif + +#ifndef ENABLE_JIT_CONSTANT_BLINDING +#define ENABLE_JIT_CONSTANT_BLINDING 1 +#endif + +namespace JSC { + +class JumpReplacementWatchpoint; +class LinkBuffer; +class RepatchBuffer; +class Watchpoint; +namespace DFG { +struct OSRExit; +} + +template +class AbstractMacroAssembler { +public: + friend class JITWriteBarrierBase; + typedef AssemblerType AssemblerType_T; + + typedef MacroAssemblerCodePtr CodePtr; + typedef MacroAssemblerCodeRef CodeRef; + + class Jump; + + typedef typename AssemblerType::RegisterID RegisterID; + + // Section 1: MacroAssembler operand types + // + // The following types are used as operands to MacroAssembler operations, + // describing immediate and memory operands to the instructions to be planted. + + enum Scale { + TimesOne, + TimesTwo, + TimesFour, + TimesEight, + }; + + // Address: + // + // Describes a simple base-offset address. + struct Address { + explicit Address(RegisterID base, int32_t offset = 0) + : base(base) + , offset(offset) + { + } + + RegisterID base; + int32_t offset; + }; + + struct ExtendedAddress { + explicit ExtendedAddress(RegisterID base, intptr_t offset = 0) + : base(base) + , offset(offset) + { + } + + RegisterID base; + intptr_t offset; + }; + + // ImplicitAddress: + // + // This class is used for explicit 'load' and 'store' operations + // (as opposed to situations in which a memory operand is provided + // to a generic operation, such as an integer arithmetic instruction). + // + // In the case of a load (or store) operation we want to permit + // addresses to be implicitly constructed, e.g. the two calls: + // + // load32(Address(addrReg), destReg); + // load32(addrReg, destReg); + // + // Are equivalent, and the explicit wrapping of the Address in the former + // is unnecessary. + struct ImplicitAddress { + ImplicitAddress(RegisterID base) + : base(base) + , offset(0) + { + } + + ImplicitAddress(Address address) + : base(address.base) + , offset(address.offset) + { + } + + RegisterID base; + int32_t offset; + }; + + // BaseIndex: + // + // Describes a complex addressing mode. + struct BaseIndex { + BaseIndex(RegisterID base, RegisterID index, Scale scale, int32_t offset = 0) + : base(base) + , index(index) + , scale(scale) + , offset(offset) + { + } + + RegisterID base; + RegisterID index; + Scale scale; + int32_t offset; + }; + + // AbsoluteAddress: + // + // Describes an memory operand given by a pointer. For regular load & store + // operations an unwrapped void* will be used, rather than using this. + struct AbsoluteAddress { + explicit AbsoluteAddress(const void* ptr) + : m_ptr(ptr) + { + } + + const void* m_ptr; + }; + + // TrustedImmPtr: + // + // A pointer sized immediate operand to an instruction - this is wrapped + // in a class requiring explicit construction in order to differentiate + // from pointers used as absolute addresses to memory operations + struct TrustedImmPtr { + TrustedImmPtr() { } + + explicit TrustedImmPtr(const void* value) + : m_value(value) + { + } + + // This is only here so that TrustedImmPtr(0) does not confuse the C++ + // overload handling rules. + explicit TrustedImmPtr(int value) + : m_value(0) + { + ASSERT_UNUSED(value, !value); + } + + explicit TrustedImmPtr(size_t value) + : m_value(reinterpret_cast(value)) + { + } + + intptr_t asIntptr() + { + return reinterpret_cast(m_value); + } + + const void* m_value; + }; + + struct ImmPtr : +#if ENABLE(JIT_CONSTANT_BLINDING) + private TrustedImmPtr +#else + public TrustedImmPtr +#endif + { + explicit ImmPtr(const void* value) + : TrustedImmPtr(value) + { + } + + TrustedImmPtr asTrustedImmPtr() { return *this; } + }; + + // TrustedImm32: + // + // A 32bit immediate operand to an instruction - this is wrapped in a + // class requiring explicit construction in order to prevent RegisterIDs + // (which are implemented as an enum) from accidentally being passed as + // immediate values. + struct TrustedImm32 { + TrustedImm32() { } + + explicit TrustedImm32(int32_t value) + : m_value(value) + { + } + +#if !CPU(X86_64) + explicit TrustedImm32(TrustedImmPtr ptr) + : m_value(ptr.asIntptr()) + { + } +#endif + + int32_t m_value; + }; + + + struct Imm32 : +#if ENABLE(JIT_CONSTANT_BLINDING) + private TrustedImm32 +#else + public TrustedImm32 +#endif + { + explicit Imm32(int32_t value) + : TrustedImm32(value) + { + } +#if !CPU(X86_64) + explicit Imm32(TrustedImmPtr ptr) + : TrustedImm32(ptr) + { + } +#endif + const TrustedImm32& asTrustedImm32() const { return *this; } + + }; + + // TrustedImm64: + // + // A 64bit immediate operand to an instruction - this is wrapped in a + // class requiring explicit construction in order to prevent RegisterIDs + // (which are implemented as an enum) from accidentally being passed as + // immediate values. + struct TrustedImm64 { + TrustedImm64() { } + + explicit TrustedImm64(int64_t value) + : m_value(value) + { + } + +#if CPU(X86_64) + explicit TrustedImm64(TrustedImmPtr ptr) + : m_value(ptr.asIntptr()) + { + } +#endif + + int64_t m_value; + }; + + struct Imm64 : +#if ENABLE(JIT_CONSTANT_BLINDING) + private TrustedImm64 +#else + public TrustedImm64 +#endif + { + explicit Imm64(int64_t value) + : TrustedImm64(value) + { + } +#if CPU(X86_64) + explicit Imm64(TrustedImmPtr ptr) + : TrustedImm64(ptr) + { + } +#endif + const TrustedImm64& asTrustedImm64() const { return *this; } + }; + + // Section 2: MacroAssembler code buffer handles + // + // The following types are used to reference items in the code buffer + // during JIT code generation. For example, the type Jump is used to + // track the location of a jump instruction so that it may later be + // linked to a label marking its destination. + + + // Label: + // + // A Label records a point in the generated instruction stream, typically such that + // it may be used as a destination for a jump. + class Label { + template + friend class AbstractMacroAssembler; + friend struct DFG::OSRExit; + friend class Jump; + friend class JumpReplacementWatchpoint; + friend class MacroAssemblerCodeRef; + friend class LinkBuffer; + friend class Watchpoint; + + public: + Label() + { + } + + Label(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.label()) + { + } + + bool isSet() const { return m_label.isSet(); } + private: + AssemblerLabel m_label; + }; + + // ConvertibleLoadLabel: + // + // A ConvertibleLoadLabel records a loadPtr instruction that can be patched to an addPtr + // so that: + // + // loadPtr(Address(a, i), b) + // + // becomes: + // + // addPtr(TrustedImmPtr(i), a, b) + class ConvertibleLoadLabel { + template + friend class AbstractMacroAssembler; + friend class LinkBuffer; + + public: + ConvertibleLoadLabel() + { + } + + ConvertibleLoadLabel(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.labelIgnoringWatchpoints()) + { + } + + bool isSet() const { return m_label.isSet(); } + private: + AssemblerLabel m_label; + }; + + // DataLabelPtr: + // + // A DataLabelPtr is used to refer to a location in the code containing a pointer to be + // patched after the code has been generated. + class DataLabelPtr { + template + friend class AbstractMacroAssembler; + friend class LinkBuffer; + public: + DataLabelPtr() + { + } + + DataLabelPtr(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.label()) + { + } + + bool isSet() const { return m_label.isSet(); } + + private: + AssemblerLabel m_label; + }; + + // DataLabel32: + // + // A DataLabelPtr is used to refer to a location in the code containing a pointer to be + // patched after the code has been generated. + class DataLabel32 { + template + friend class AbstractMacroAssembler; + friend class LinkBuffer; + public: + DataLabel32() + { + } + + DataLabel32(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.label()) + { + } + + AssemblerLabel label() const { return m_label; } + + private: + AssemblerLabel m_label; + }; + + // DataLabelCompact: + // + // A DataLabelCompact is used to refer to a location in the code containing a + // compact immediate to be patched after the code has been generated. + class DataLabelCompact { + template + friend class AbstractMacroAssembler; + friend class LinkBuffer; + public: + DataLabelCompact() + { + } + + DataLabelCompact(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.label()) + { + } + + DataLabelCompact(AssemblerLabel label) + : m_label(label) + { + } + + private: + AssemblerLabel m_label; + }; + + // Call: + // + // A Call object is a reference to a call instruction that has been planted + // into the code buffer - it is typically used to link the call, setting the + // relative offset such that when executed it will call to the desired + // destination. + class Call { + template + friend class AbstractMacroAssembler; + + public: + enum Flags { + None = 0x0, + Linkable = 0x1, + Near = 0x2, + LinkableNear = 0x3, + }; + + Call() + : m_flags(None) + { + } + + Call(AssemblerLabel jmp, Flags flags) + : m_label(jmp) + , m_flags(flags) + { + } + + bool isFlagSet(Flags flag) + { + return m_flags & flag; + } + + static Call fromTailJump(Jump jump) + { + return Call(jump.m_label, Linkable); + } + + AssemblerLabel m_label; + private: + Flags m_flags; + }; + + // Jump: + // + // A jump object is a reference to a jump instruction that has been planted + // into the code buffer - it is typically used to link the jump, setting the + // relative offset such that when executed it will jump to the desired + // destination. + class Jump { + template + friend class AbstractMacroAssembler; + friend class Call; + friend struct DFG::OSRExit; + friend class LinkBuffer; + public: + Jump() + { + } + +#if CPU(ARM_THUMB2) + // Fixme: this information should be stored in the instruction stream, not in the Jump object. + Jump(AssemblerLabel jmp, ARMv7Assembler::JumpType type, ARMv7Assembler::Condition condition = ARMv7Assembler::ConditionInvalid) + : m_label(jmp) + , m_type(type) + , m_condition(condition) + { + } +#elif CPU(SH4) + Jump(AssemblerLabel jmp, SH4Assembler::JumpType type = SH4Assembler::JumpFar) + : m_label(jmp) + , m_type(type) + { + } +#else + Jump(AssemblerLabel jmp) + : m_label(jmp) + { + } +#endif + + Label label() const + { + Label result; + result.m_label = m_label; + return result; + } + + void link(AbstractMacroAssembler* masm) const + { +#if CPU(ARM_THUMB2) + masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type, m_condition); +#elif CPU(SH4) + masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type); +#else + masm->m_assembler.linkJump(m_label, masm->m_assembler.label()); +#endif + } + + void linkTo(Label label, AbstractMacroAssembler* masm) const + { +#if CPU(ARM_THUMB2) + masm->m_assembler.linkJump(m_label, label.m_label, m_type, m_condition); +#else + masm->m_assembler.linkJump(m_label, label.m_label); +#endif + } + + bool isSet() const { return m_label.isSet(); } + + private: + AssemblerLabel m_label; +#if CPU(ARM_THUMB2) + ARMv7Assembler::JumpType m_type; + ARMv7Assembler::Condition m_condition; +#endif +#if CPU(SH4) + SH4Assembler::JumpType m_type; +#endif + }; + + struct PatchableJump { + PatchableJump() + { + } + + explicit PatchableJump(Jump jump) + : m_jump(jump) + { + } + + operator Jump&() { return m_jump; } + + Jump m_jump; + }; + + // JumpList: + // + // A JumpList is a set of Jump objects. + // All jumps in the set will be linked to the same destination. + class JumpList { + friend class LinkBuffer; + + public: + typedef Vector JumpVector; + + JumpList() { } + + JumpList(Jump jump) + { + append(jump); + } + + void link(AbstractMacroAssembler* masm) + { + size_t size = m_jumps.size(); + for (size_t i = 0; i < size; ++i) + m_jumps[i].link(masm); + m_jumps.clear(); + } + + void linkTo(Label label, AbstractMacroAssembler* masm) + { + size_t size = m_jumps.size(); + for (size_t i = 0; i < size; ++i) + m_jumps[i].linkTo(label, masm); + m_jumps.clear(); + } + + void append(Jump jump) + { + m_jumps.append(jump); + } + + void append(const JumpList& other) + { + m_jumps.append(other.m_jumps.begin(), other.m_jumps.size()); + } + + bool empty() + { + return !m_jumps.size(); + } + + void clear() + { + m_jumps.clear(); + } + + const JumpVector& jumps() const { return m_jumps; } + + private: + JumpVector m_jumps; + }; + + + // Section 3: Misc admin methods +#if ENABLE(DFG_JIT) + Label labelIgnoringWatchpoints() + { + Label result; + result.m_label = m_assembler.labelIgnoringWatchpoints(); + return result; + } +#else + Label labelIgnoringWatchpoints() + { + return label(); + } +#endif + + Label label() + { + return Label(this); + } + + void padBeforePatch() + { + // Rely on the fact that asking for a label already does the padding. + (void)label(); + } + + Label watchpointLabel() + { + Label result; + result.m_label = m_assembler.labelForWatchpoint(); + return result; + } + + Label align() + { + m_assembler.align(16); + return Label(this); + } + + template + static ptrdiff_t differenceBetween(T from, U to) + { + return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_label); + } + + static ptrdiff_t differenceBetweenCodePtr(const MacroAssemblerCodePtr& a, const MacroAssemblerCodePtr& b) + { + return reinterpret_cast(b.executableAddress()) - reinterpret_cast(a.executableAddress()); + } + + unsigned debugOffset() { return m_assembler.debugOffset(); } + + ALWAYS_INLINE static void cacheFlush(void* code, size_t size) + { + AssemblerType::cacheFlush(code, size); + } +protected: + AbstractMacroAssembler() + : m_randomSource(cryptographicallyRandomNumber()) + { + } + + AssemblerType m_assembler; + + uint32_t random() + { + return m_randomSource.getUint32(); + } + + WeakRandom m_randomSource; + +#if ENABLE(JIT_CONSTANT_BLINDING) + static bool scratchRegisterForBlinding() { return false; } + static bool shouldBlindForSpecificArch(uint32_t) { return true; } + static bool shouldBlindForSpecificArch(uint64_t) { return true; } +#endif + + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkJump(void* code, Jump jump, CodeLocationLabel target) + { + AssemblerType::linkJump(code, jump.m_label, target.dataLocation()); + } + + static void linkPointer(void* code, AssemblerLabel label, void* value) + { + AssemblerType::linkPointer(code, label, value); + } + + static void* getLinkerAddress(void* code, AssemblerLabel label) + { + return AssemblerType::getRelocatedAddress(code, label); + } + + static unsigned getLinkerCallReturnOffset(Call call) + { + return AssemblerType::getCallReturnOffset(call.m_label); + } + + static void repatchJump(CodeLocationJump jump, CodeLocationLabel destination) + { + AssemblerType::relinkJump(jump.dataLocation(), destination.dataLocation()); + } + + static void repatchNearCall(CodeLocationNearCall nearCall, CodeLocationLabel destination) + { + AssemblerType::relinkCall(nearCall.dataLocation(), destination.executableAddress()); + } + + static void repatchCompact(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) + { + AssemblerType::repatchCompact(dataLabelCompact.dataLocation(), value); + } + + static void repatchInt32(CodeLocationDataLabel32 dataLabel32, int32_t value) + { + AssemblerType::repatchInt32(dataLabel32.dataLocation(), value); + } + + static void repatchPointer(CodeLocationDataLabelPtr dataLabelPtr, void* value) + { + AssemblerType::repatchPointer(dataLabelPtr.dataLocation(), value); + } + + static void* readPointer(CodeLocationDataLabelPtr dataLabelPtr) + { + return AssemblerType::readPointer(dataLabelPtr.dataLocation()); + } + + static void replaceWithLoad(CodeLocationConvertibleLoad label) + { + AssemblerType::replaceWithLoad(label.dataLocation()); + } + + static void replaceWithAddressComputation(CodeLocationConvertibleLoad label) + { + AssemblerType::replaceWithAddressComputation(label.dataLocation()); + } +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // AbstractMacroAssembler_h diff --git a/3rdparty/masm/assembler/AssemblerBuffer.h b/3rdparty/masm/assembler/AssemblerBuffer.h new file mode 100644 index 0000000000..bc52801ba7 --- /dev/null +++ b/3rdparty/masm/assembler/AssemblerBuffer.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AssemblerBuffer_h +#define AssemblerBuffer_h + +#if ENABLE(ASSEMBLER) + +#include "ExecutableAllocator.h" +#include "JITCompilationEffort.h" +#include "JSGlobalData.h" +#include "stdint.h" +#include +#include +#include +#include + +namespace JSC { + + struct AssemblerLabel { + AssemblerLabel() + : m_offset(std::numeric_limits::max()) + { + } + + explicit AssemblerLabel(uint32_t offset) + : m_offset(offset) + { + } + + bool isSet() const { return (m_offset != std::numeric_limits::max()); } + + AssemblerLabel labelAtOffset(int offset) const + { + return AssemblerLabel(m_offset + offset); + } + + uint32_t m_offset; + }; + + class AssemblerBuffer { + static const int inlineCapacity = 128; + public: + AssemblerBuffer() + : m_storage(inlineCapacity) + , m_buffer(&(*m_storage.begin())) + , m_capacity(inlineCapacity) + , m_index(0) + { + } + + ~AssemblerBuffer() + { + } + + bool isAvailable(int space) + { + return m_index + space <= m_capacity; + } + + void ensureSpace(int space) + { + if (!isAvailable(space)) + grow(); + } + + bool isAligned(int alignment) const + { + return !(m_index & (alignment - 1)); + } + + template + void putIntegral(IntegralType value) + { + ensureSpace(sizeof(IntegralType)); + putIntegralUnchecked(value); + } + + template + void putIntegralUnchecked(IntegralType value) + { + ASSERT(isAvailable(sizeof(IntegralType))); + *reinterpret_cast_ptr(m_buffer + m_index) = value; + m_index += sizeof(IntegralType); + } + + void putByteUnchecked(int8_t value) { putIntegralUnchecked(value); } + void putByte(int8_t value) { putIntegral(value); } + void putShortUnchecked(int16_t value) { putIntegralUnchecked(value); } + void putShort(int16_t value) { putIntegral(value); } + void putIntUnchecked(int32_t value) { putIntegralUnchecked(value); } + void putInt(int32_t value) { putIntegral(value); } + void putInt64Unchecked(int64_t value) { putIntegralUnchecked(value); } + void putInt64(int64_t value) { putIntegral(value); } + + void* data() const + { + return m_buffer; + } + + size_t codeSize() const + { + return m_index; + } + + AssemblerLabel label() const + { + return AssemblerLabel(m_index); + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + if (!m_index) + return 0; + + RefPtr result = globalData.executableAllocator.allocate(globalData, m_index, ownerUID, effort); + + if (!result) + return 0; + + ExecutableAllocator::makeWritable(result->start(), result->sizeInBytes()); + + memcpy(result->start(), m_buffer, m_index); + + return result.release(); + } + + unsigned debugOffset() { return m_index; } + + protected: + void append(const char* data, int size) + { + if (!isAvailable(size)) + grow(size); + + memcpy(m_buffer + m_index, data, size); + m_index += size; + } + + void grow(int extraCapacity = 0) + { + m_capacity += m_capacity / 2 + extraCapacity; + + m_storage.grow(m_capacity); + m_buffer = &(*m_storage.begin()); + } + + private: + Vector m_storage; + char* m_buffer; + int m_capacity; + int m_index; + }; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // AssemblerBuffer_h diff --git a/3rdparty/masm/assembler/AssemblerBufferWithConstantPool.h b/3rdparty/masm/assembler/AssemblerBufferWithConstantPool.h new file mode 100644 index 0000000000..5377ef0c7a --- /dev/null +++ b/3rdparty/masm/assembler/AssemblerBufferWithConstantPool.h @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2009 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AssemblerBufferWithConstantPool_h +#define AssemblerBufferWithConstantPool_h + +#if ENABLE(ASSEMBLER) + +#include "AssemblerBuffer.h" +#include + +#define ASSEMBLER_HAS_CONSTANT_POOL 1 + +namespace JSC { + +/* + On a constant pool 4 or 8 bytes data can be stored. The values can be + constants or addresses. The addresses should be 32 or 64 bits. The constants + should be double-precisions float or integer numbers which are hard to be + encoded as few machine instructions. + + TODO: The pool is desinged to handle both 32 and 64 bits values, but + currently only the 4 bytes constants are implemented and tested. + + The AssemblerBuffer can contain multiple constant pools. Each pool is inserted + into the instruction stream - protected by a jump instruction from the + execution flow. + + The flush mechanism is called when no space remain to insert the next instruction + into the pool. Three values are used to determine when the constant pool itself + have to be inserted into the instruction stream (Assembler Buffer): + + - maxPoolSize: size of the constant pool in bytes, this value cannot be + larger than the maximum offset of a PC relative memory load + + - barrierSize: size of jump instruction in bytes which protects the + constant pool from execution + + - maxInstructionSize: maximum length of a machine instruction in bytes + + There are some callbacks which solve the target architecture specific + address handling: + + - TYPE patchConstantPoolLoad(TYPE load, int value): + patch the 'load' instruction with the index of the constant in the + constant pool and return the patched instruction. + + - void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr): + patch the a PC relative load instruction at 'loadAddr' address with the + final relative offset. The offset can be computed with help of + 'constPoolAddr' (the address of the constant pool) and index of the + constant (which is stored previously in the load instruction itself). + + - TYPE placeConstantPoolBarrier(int size): + return with a constant pool barrier instruction which jumps over the + constant pool. + + The 'put*WithConstant*' functions should be used to place a data into the + constant pool. +*/ + +template +class AssemblerBufferWithConstantPool : public AssemblerBuffer { + typedef SegmentedVector LoadOffsets; + using AssemblerBuffer::putIntegral; + using AssemblerBuffer::putIntegralUnchecked; +public: + typedef struct { + short high; + short low; + } TwoShorts; + + enum { + UniqueConst, + ReusableConst, + UnusedEntry, + }; + + AssemblerBufferWithConstantPool() + : AssemblerBuffer() + , m_numConsts(0) + , m_maxDistance(maxPoolSize) + , m_lastConstDelta(0) + { + m_pool = static_cast(fastMalloc(maxPoolSize)); + m_mask = static_cast(fastMalloc(maxPoolSize / sizeof(uint32_t))); + } + + ~AssemblerBufferWithConstantPool() + { + fastFree(m_mask); + fastFree(m_pool); + } + + void ensureSpace(int space) + { + flushIfNoSpaceFor(space); + AssemblerBuffer::ensureSpace(space); + } + + void ensureSpace(int insnSpace, int constSpace) + { + flushIfNoSpaceFor(insnSpace, constSpace); + AssemblerBuffer::ensureSpace(insnSpace); + } + + void ensureSpaceForAnyInstruction(int amount = 1) + { + flushIfNoSpaceFor(amount * maxInstructionSize, amount * sizeof(uint64_t)); + } + + bool isAligned(int alignment) + { + flushIfNoSpaceFor(alignment); + return AssemblerBuffer::isAligned(alignment); + } + + void putByteUnchecked(int value) + { + AssemblerBuffer::putByteUnchecked(value); + correctDeltas(1); + } + + void putByte(int value) + { + flushIfNoSpaceFor(1); + AssemblerBuffer::putByte(value); + correctDeltas(1); + } + + void putShortUnchecked(int value) + { + AssemblerBuffer::putShortUnchecked(value); + correctDeltas(2); + } + + void putShort(int value) + { + flushIfNoSpaceFor(2); + AssemblerBuffer::putShort(value); + correctDeltas(2); + } + + void putIntUnchecked(int value) + { + AssemblerBuffer::putIntUnchecked(value); + correctDeltas(4); + } + + void putInt(int value) + { + flushIfNoSpaceFor(4); + AssemblerBuffer::putInt(value); + correctDeltas(4); + } + + void putInt64Unchecked(int64_t value) + { + AssemblerBuffer::putInt64Unchecked(value); + correctDeltas(8); + } + + void putIntegral(TwoShorts value) + { + putIntegral(value.high); + putIntegral(value.low); + } + + void putIntegralUnchecked(TwoShorts value) + { + putIntegralUnchecked(value.high); + putIntegralUnchecked(value.low); + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + flushConstantPool(false); + return AssemblerBuffer::executableCopy(globalData, ownerUID, effort); + } + + void putShortWithConstantInt(uint16_t insn, uint32_t constant, bool isReusable = false) + { + putIntegralWithConstantInt(insn, constant, isReusable); + } + + void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false) + { + putIntegralWithConstantInt(insn, constant, isReusable); + } + + // This flushing mechanism can be called after any unconditional jumps. + void flushWithoutBarrier(bool isForced = false) + { + // Flush if constant pool is more than 60% full to avoid overuse of this function. + if (isForced || 5 * static_cast(m_numConsts) > 3 * maxPoolSize / sizeof(uint32_t)) + flushConstantPool(false); + } + + uint32_t* poolAddress() + { + return m_pool; + } + + int sizeOfConstantPool() + { + return m_numConsts; + } + +private: + void correctDeltas(int insnSize) + { + m_maxDistance -= insnSize; + m_lastConstDelta -= insnSize; + if (m_lastConstDelta < 0) + m_lastConstDelta = 0; + } + + void correctDeltas(int insnSize, int constSize) + { + correctDeltas(insnSize); + + m_maxDistance -= m_lastConstDelta; + m_lastConstDelta = constSize; + } + + template + void putIntegralWithConstantInt(IntegralType insn, uint32_t constant, bool isReusable) + { + if (!m_numConsts) + m_maxDistance = maxPoolSize; + flushIfNoSpaceFor(sizeof(IntegralType), 4); + + m_loadOffsets.append(codeSize()); + if (isReusable) { + for (int i = 0; i < m_numConsts; ++i) { + if (m_mask[i] == ReusableConst && m_pool[i] == constant) { + putIntegral(static_cast(AssemblerType::patchConstantPoolLoad(insn, i))); + correctDeltas(sizeof(IntegralType)); + return; + } + } + } + + m_pool[m_numConsts] = constant; + m_mask[m_numConsts] = static_cast(isReusable ? ReusableConst : UniqueConst); + + putIntegral(static_cast(AssemblerType::patchConstantPoolLoad(insn, m_numConsts))); + ++m_numConsts; + + correctDeltas(sizeof(IntegralType), 4); + } + + void flushConstantPool(bool useBarrier = true) + { + if (m_numConsts == 0) + return; + int alignPool = (codeSize() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1); + + if (alignPool) + alignPool = sizeof(uint64_t) - alignPool; + + // Callback to protect the constant pool from execution + if (useBarrier) + putIntegral(AssemblerType::placeConstantPoolBarrier(m_numConsts * sizeof(uint32_t) + alignPool)); + + if (alignPool) { + if (alignPool & 1) + AssemblerBuffer::putByte(AssemblerType::padForAlign8); + if (alignPool & 2) + AssemblerBuffer::putShort(AssemblerType::padForAlign16); + if (alignPool & 4) + AssemblerBuffer::putInt(AssemblerType::padForAlign32); + } + + int constPoolOffset = codeSize(); + append(reinterpret_cast(m_pool), m_numConsts * sizeof(uint32_t)); + + // Patch each PC relative load + for (LoadOffsets::Iterator iter = m_loadOffsets.begin(); iter != m_loadOffsets.end(); ++iter) { + void* loadAddr = reinterpret_cast(data()) + *iter; + AssemblerType::patchConstantPoolLoad(loadAddr, reinterpret_cast(data()) + constPoolOffset); + } + + m_loadOffsets.clear(); + m_numConsts = 0; + } + + void flushIfNoSpaceFor(int nextInsnSize) + { + if (m_numConsts == 0) + return; + int lastConstDelta = m_lastConstDelta > nextInsnSize ? m_lastConstDelta - nextInsnSize : 0; + if ((m_maxDistance < nextInsnSize + lastConstDelta + barrierSize + (int)sizeof(uint32_t))) + flushConstantPool(); + } + + void flushIfNoSpaceFor(int nextInsnSize, int nextConstSize) + { + if (m_numConsts == 0) + return; + if ((m_maxDistance < nextInsnSize + m_lastConstDelta + nextConstSize + barrierSize + (int)sizeof(uint32_t)) || + (m_numConsts * sizeof(uint32_t) + nextConstSize >= maxPoolSize)) + flushConstantPool(); + } + + uint32_t* m_pool; + char* m_mask; + LoadOffsets m_loadOffsets; + + int m_numConsts; + int m_maxDistance; + int m_lastConstDelta; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // AssemblerBufferWithConstantPool_h diff --git a/3rdparty/masm/assembler/CodeLocation.h b/3rdparty/masm/assembler/CodeLocation.h new file mode 100644 index 0000000000..86d1f2b755 --- /dev/null +++ b/3rdparty/masm/assembler/CodeLocation.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CodeLocation_h +#define CodeLocation_h + +#include "MacroAssemblerCodeRef.h" + +#if ENABLE(ASSEMBLER) + +namespace JSC { + +class CodeLocationInstruction; +class CodeLocationLabel; +class CodeLocationJump; +class CodeLocationCall; +class CodeLocationNearCall; +class CodeLocationDataLabelCompact; +class CodeLocationDataLabel32; +class CodeLocationDataLabelPtr; +class CodeLocationConvertibleLoad; + +// The CodeLocation* types are all pretty much do-nothing wrappers around +// CodePtr (or MacroAssemblerCodePtr, to give it its full name). These +// classes only exist to provide type-safety when linking and patching code. +// +// The one new piece of functionallity introduced by these classes is the +// ability to create (or put another way, to re-discover) another CodeLocation +// at an offset from one you already know. When patching code to optimize it +// we often want to patch a number of instructions that are short, fixed +// offsets apart. To reduce memory overhead we will only retain a pointer to +// one of the instructions, and we will use the *AtOffset methods provided by +// CodeLocationCommon to find the other points in the code to modify. +class CodeLocationCommon : public MacroAssemblerCodePtr { +public: + CodeLocationInstruction instructionAtOffset(int offset); + CodeLocationLabel labelAtOffset(int offset); + CodeLocationJump jumpAtOffset(int offset); + CodeLocationCall callAtOffset(int offset); + CodeLocationNearCall nearCallAtOffset(int offset); + CodeLocationDataLabelPtr dataLabelPtrAtOffset(int offset); + CodeLocationDataLabel32 dataLabel32AtOffset(int offset); + CodeLocationDataLabelCompact dataLabelCompactAtOffset(int offset); + CodeLocationConvertibleLoad convertibleLoadAtOffset(int offset); + +protected: + CodeLocationCommon() + { + } + + CodeLocationCommon(MacroAssemblerCodePtr location) + : MacroAssemblerCodePtr(location) + { + } +}; + +class CodeLocationInstruction : public CodeLocationCommon { +public: + CodeLocationInstruction() {} + explicit CodeLocationInstruction(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationInstruction(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationLabel : public CodeLocationCommon { +public: + CodeLocationLabel() {} + explicit CodeLocationLabel(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationLabel(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationJump : public CodeLocationCommon { +public: + CodeLocationJump() {} + explicit CodeLocationJump(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationJump(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationCall : public CodeLocationCommon { +public: + CodeLocationCall() {} + explicit CodeLocationCall(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationCall(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationNearCall : public CodeLocationCommon { +public: + CodeLocationNearCall() {} + explicit CodeLocationNearCall(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationNearCall(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationDataLabel32 : public CodeLocationCommon { +public: + CodeLocationDataLabel32() {} + explicit CodeLocationDataLabel32(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationDataLabel32(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationDataLabelCompact : public CodeLocationCommon { +public: + CodeLocationDataLabelCompact() { } + explicit CodeLocationDataLabelCompact(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) { } + explicit CodeLocationDataLabelCompact(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) { } +}; + +class CodeLocationDataLabelPtr : public CodeLocationCommon { +public: + CodeLocationDataLabelPtr() {} + explicit CodeLocationDataLabelPtr(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationDataLabelPtr(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationConvertibleLoad : public CodeLocationCommon { +public: + CodeLocationConvertibleLoad() { } + explicit CodeLocationConvertibleLoad(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) { } + explicit CodeLocationConvertibleLoad(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) { } +}; + +inline CodeLocationInstruction CodeLocationCommon::instructionAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationInstruction(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationLabel CodeLocationCommon::labelAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationLabel(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationJump CodeLocationCommon::jumpAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationJump(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationCall CodeLocationCommon::callAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationCall(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationNearCall CodeLocationCommon::nearCallAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationNearCall(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationDataLabelPtr CodeLocationCommon::dataLabelPtrAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationDataLabelPtr(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationDataLabel32 CodeLocationCommon::dataLabel32AtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationDataLabel32(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationDataLabelCompact CodeLocationCommon::dataLabelCompactAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationDataLabelCompact(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationConvertibleLoad CodeLocationCommon::convertibleLoadAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationConvertibleLoad(reinterpret_cast(dataLocation()) + offset); +} + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // CodeLocation_h diff --git a/3rdparty/masm/assembler/LinkBuffer.cpp b/3rdparty/masm/assembler/LinkBuffer.cpp new file mode 100644 index 0000000000..c269157ba5 --- /dev/null +++ b/3rdparty/masm/assembler/LinkBuffer.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "LinkBuffer.h" + +#if ENABLE(ASSEMBLER) + +#include "Options.h" + +namespace JSC { + +LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithoutDisassembly() +{ + performFinalization(); + + return CodeRef(m_executableMemory); +} + +LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithDisassembly(const char* format, ...) +{ + ASSERT(Options::showDisassembly() || Options::showDFGDisassembly()); + + CodeRef result = finalizeCodeWithoutDisassembly(); + + dataLogF("Generated JIT code for "); + va_list argList; + va_start(argList, format); + WTF::dataLogFV(format, argList); + va_end(argList); + dataLogF(":\n"); + + dataLogF(" Code at [%p, %p):\n", result.code().executableAddress(), static_cast(result.code().executableAddress()) + result.size()); + disassemble(result.code(), m_size, " ", WTF::dataFile()); + + return result; +} + +void LinkBuffer::linkCode(void* ownerUID, JITCompilationEffort effort) +{ + ASSERT(!m_code); +#if !ENABLE(BRANCH_COMPACTION) + m_executableMemory = m_assembler->m_assembler.executableCopy(*m_globalData, ownerUID, effort); + if (!m_executableMemory) + return; + m_code = m_executableMemory->start(); + m_size = m_assembler->m_assembler.codeSize(); + ASSERT(m_code); +#else + m_initialSize = m_assembler->m_assembler.codeSize(); + m_executableMemory = m_globalData->executableAllocator.allocate(*m_globalData, m_initialSize, ownerUID, effort); + if (!m_executableMemory) + return; + m_code = (uint8_t*)m_executableMemory->start(); + ASSERT(m_code); + ExecutableAllocator::makeWritable(m_code, m_initialSize); + uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode(); + uint8_t* outData = reinterpret_cast(m_code); + int readPtr = 0; + int writePtr = 0; + Vector& jumpsToLink = m_assembler->jumpsToLink(); + unsigned jumpCount = jumpsToLink.size(); + for (unsigned i = 0; i < jumpCount; ++i) { + int offset = readPtr - writePtr; + ASSERT(!(offset & 1)); + + // Copy the instructions from the last jump to the current one. + size_t regionSize = jumpsToLink[i].from() - readPtr; + uint16_t* copySource = reinterpret_cast_ptr(inData + readPtr); + uint16_t* copyEnd = reinterpret_cast_ptr(inData + readPtr + regionSize); + uint16_t* copyDst = reinterpret_cast_ptr(outData + writePtr); + ASSERT(!(regionSize % 2)); + ASSERT(!(readPtr % 2)); + ASSERT(!(writePtr % 2)); + while (copySource != copyEnd) + *copyDst++ = *copySource++; + m_assembler->recordLinkOffsets(readPtr, jumpsToLink[i].from(), offset); + readPtr += regionSize; + writePtr += regionSize; + + // Calculate absolute address of the jump target, in the case of backwards + // branches we need to be precise, forward branches we are pessimistic + const uint8_t* target; + if (jumpsToLink[i].to() >= jumpsToLink[i].from()) + target = outData + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far + else + target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); + + JumpLinkType jumpLinkType = m_assembler->computeJumpType(jumpsToLink[i], outData + writePtr, target); + // Compact branch if we can... + if (m_assembler->canCompact(jumpsToLink[i].type())) { + // Step back in the write stream + int32_t delta = m_assembler->jumpSizeDelta(jumpsToLink[i].type(), jumpLinkType); + if (delta) { + writePtr -= delta; + m_assembler->recordLinkOffsets(jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr); + } + } + jumpsToLink[i].setFrom(writePtr); + } + // Copy everything after the last jump + memcpy(outData + writePtr, inData + readPtr, m_initialSize - readPtr); + m_assembler->recordLinkOffsets(readPtr, m_initialSize, readPtr - writePtr); + + for (unsigned i = 0; i < jumpCount; ++i) { + uint8_t* location = outData + jumpsToLink[i].from(); + uint8_t* target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); + m_assembler->link(jumpsToLink[i], location, target); + } + + jumpsToLink.clear(); + m_size = writePtr + m_initialSize - readPtr; + m_executableMemory->shrink(m_size); + +#if DUMP_LINK_STATISTICS + dumpLinkStatistics(m_code, m_initialSize, m_size); +#endif +#if DUMP_CODE + dumpCode(m_code, m_size); +#endif +#endif +} + +void LinkBuffer::performFinalization() +{ +#ifndef NDEBUG + ASSERT(!m_completed); + ASSERT(isValid()); + m_completed = true; +#endif + +#if ENABLE(BRANCH_COMPACTION) + ExecutableAllocator::makeExecutable(code(), m_initialSize); +#else + ExecutableAllocator::makeExecutable(code(), m_size); +#endif + MacroAssembler::cacheFlush(code(), m_size); +} + +#if DUMP_LINK_STATISTICS +void LinkBuffer::dumpLinkStatistics(void* code, size_t initializeSize, size_t finalSize) +{ + static unsigned linkCount = 0; + static unsigned totalInitialSize = 0; + static unsigned totalFinalSize = 0; + linkCount++; + totalInitialSize += initialSize; + totalFinalSize += finalSize; + dataLogF("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", + code, static_cast(initialSize), static_cast(finalSize), + static_cast(initialSize - finalSize), + 100.0 * (initialSize - finalSize) / initialSize); + dataLogF("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", + linkCount, totalInitialSize, totalFinalSize, totalInitialSize - totalFinalSize, + 100.0 * (totalInitialSize - totalFinalSize) / totalInitialSize); +} +#endif + +#if DUMP_CODE +void LinkBuffer::dumpCode(void* code, size_t size) +{ +#if CPU(ARM_THUMB2) + // Dump the generated code in an asm file format that can be assembled and then disassembled + // for debugging purposes. For example, save this output as jit.s: + // gcc -arch armv7 -c jit.s + // otool -tv jit.o + static unsigned codeCount = 0; + unsigned short* tcode = static_cast(code); + size_t tsize = size / sizeof(short); + char nameBuf[128]; + snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); + dataLogF("\t.syntax unified\n" + "\t.section\t__TEXT,__text,regular,pure_instructions\n" + "\t.globl\t%s\n" + "\t.align 2\n" + "\t.code 16\n" + "\t.thumb_func\t%s\n" + "# %p\n" + "%s:\n", nameBuf, nameBuf, code, nameBuf); + + for (unsigned i = 0; i < tsize; i++) + dataLogF("\t.short\t0x%x\n", tcode[i]); +#elif CPU(ARM_TRADITIONAL) + // gcc -c jit.s + // objdump -D jit.o + static unsigned codeCount = 0; + unsigned int* tcode = static_cast(code); + size_t tsize = size / sizeof(unsigned int); + char nameBuf[128]; + snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); + dataLogF("\t.globl\t%s\n" + "\t.align 4\n" + "\t.code 32\n" + "\t.text\n" + "# %p\n" + "%s:\n", nameBuf, code, nameBuf); + + for (unsigned i = 0; i < tsize; i++) + dataLogF("\t.long\t0x%x\n", tcode[i]); +#endif +} +#endif + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + + diff --git a/3rdparty/masm/assembler/LinkBuffer.h b/3rdparty/masm/assembler/LinkBuffer.h new file mode 100644 index 0000000000..e1882433c1 --- /dev/null +++ b/3rdparty/masm/assembler/LinkBuffer.h @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2009, 2010, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LinkBuffer_h +#define LinkBuffer_h + +#if ENABLE(ASSEMBLER) + +#define DUMP_LINK_STATISTICS 0 +#define DUMP_CODE 0 + +#define GLOBAL_THUNK_ID reinterpret_cast(static_cast(-1)) +#define REGEXP_CODE_ID reinterpret_cast(static_cast(-2)) + +#include "JITCompilationEffort.h" +#include "MacroAssembler.h" +#include +#include + +namespace JSC { + +class JSGlobalData; + +// LinkBuffer: +// +// This class assists in linking code generated by the macro assembler, once code generation +// has been completed, and the code has been copied to is final location in memory. At this +// time pointers to labels within the code may be resolved, and relative offsets to external +// addresses may be fixed. +// +// Specifically: +// * Jump objects may be linked to external targets, +// * The address of Jump objects may taken, such that it can later be relinked. +// * The return address of a Call may be acquired. +// * The address of a Label pointing into the code may be resolved. +// * The value referenced by a DataLabel may be set. +// +class LinkBuffer { + WTF_MAKE_NONCOPYABLE(LinkBuffer); + typedef MacroAssemblerCodeRef CodeRef; + typedef MacroAssemblerCodePtr CodePtr; + typedef MacroAssembler::Label Label; + typedef MacroAssembler::Jump Jump; + typedef MacroAssembler::PatchableJump PatchableJump; + typedef MacroAssembler::JumpList JumpList; + typedef MacroAssembler::Call Call; + typedef MacroAssembler::DataLabelCompact DataLabelCompact; + typedef MacroAssembler::DataLabel32 DataLabel32; + typedef MacroAssembler::DataLabelPtr DataLabelPtr; + typedef MacroAssembler::ConvertibleLoadLabel ConvertibleLoadLabel; +#if ENABLE(BRANCH_COMPACTION) + typedef MacroAssembler::LinkRecord LinkRecord; + typedef MacroAssembler::JumpLinkType JumpLinkType; +#endif + +public: + LinkBuffer(JSGlobalData& globalData, MacroAssembler* masm, void* ownerUID, JITCompilationEffort effort = JITCompilationMustSucceed) + : m_size(0) +#if ENABLE(BRANCH_COMPACTION) + , m_initialSize(0) +#endif + , m_code(0) + , m_assembler(masm) + , m_globalData(&globalData) +#ifndef NDEBUG + , m_completed(false) + , m_effort(effort) +#endif + { + linkCode(ownerUID, effort); + } + + ~LinkBuffer() + { + ASSERT(m_completed || (!m_executableMemory && m_effort == JITCompilationCanFail)); + } + + bool didFailToAllocate() const + { + return !m_executableMemory; + } + + bool isValid() const + { + return !didFailToAllocate(); + } + + // These methods are used to link or set values at code generation time. + + void link(Call call, FunctionPtr function) + { + ASSERT(call.isFlagSet(Call::Linkable)); + call.m_label = applyOffset(call.m_label); + MacroAssembler::linkCall(code(), call, function); + } + + void link(Jump jump, CodeLocationLabel label) + { + jump.m_label = applyOffset(jump.m_label); + MacroAssembler::linkJump(code(), jump, label); + } + + void link(JumpList list, CodeLocationLabel label) + { + for (unsigned i = 0; i < list.m_jumps.size(); ++i) + link(list.m_jumps[i], label); + } + + void patch(DataLabelPtr label, void* value) + { + AssemblerLabel target = applyOffset(label.m_label); + MacroAssembler::linkPointer(code(), target, value); + } + + void patch(DataLabelPtr label, CodeLocationLabel value) + { + AssemblerLabel target = applyOffset(label.m_label); + MacroAssembler::linkPointer(code(), target, value.executableAddress()); + } + + // These methods are used to obtain handles to allow the code to be relinked / repatched later. + + CodeLocationCall locationOf(Call call) + { + ASSERT(call.isFlagSet(Call::Linkable)); + ASSERT(!call.isFlagSet(Call::Near)); + return CodeLocationCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_label))); + } + + CodeLocationNearCall locationOfNearCall(Call call) + { + ASSERT(call.isFlagSet(Call::Linkable)); + ASSERT(call.isFlagSet(Call::Near)); + return CodeLocationNearCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_label))); + } + + CodeLocationLabel locationOf(PatchableJump jump) + { + return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(jump.m_jump.m_label))); + } + + CodeLocationLabel locationOf(Label label) + { + return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + CodeLocationDataLabelPtr locationOf(DataLabelPtr label) + { + return CodeLocationDataLabelPtr(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + CodeLocationDataLabel32 locationOf(DataLabel32 label) + { + return CodeLocationDataLabel32(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + CodeLocationDataLabelCompact locationOf(DataLabelCompact label) + { + return CodeLocationDataLabelCompact(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + CodeLocationConvertibleLoad locationOf(ConvertibleLoadLabel label) + { + return CodeLocationConvertibleLoad(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + // This method obtains the return address of the call, given as an offset from + // the start of the code. + unsigned returnAddressOffset(Call call) + { + call.m_label = applyOffset(call.m_label); + return MacroAssembler::getLinkerCallReturnOffset(call); + } + + uint32_t offsetOf(Label label) + { + return applyOffset(label.m_label).m_offset; + } + + // Upon completion of all patching 'FINALIZE_CODE()' should be called once to + // complete generation of the code. Alternatively, call + // finalizeCodeWithoutDisassembly() directly if you have your own way of + // displaying disassembly. + + CodeRef finalizeCodeWithoutDisassembly(); + CodeRef finalizeCodeWithDisassembly(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); + + CodePtr trampolineAt(Label label) + { + return CodePtr(MacroAssembler::AssemblerType_T::getRelocatedAddress(code(), applyOffset(label.m_label))); + } + + void* debugAddress() + { + return m_code; + } + + size_t debugSize() + { + return m_size; + } + +private: + template T applyOffset(T src) + { +#if ENABLE(BRANCH_COMPACTION) + src.m_offset -= m_assembler->executableOffsetFor(src.m_offset); +#endif + return src; + } + + // Keep this private! - the underlying code should only be obtained externally via finalizeCode(). + void* code() + { + return m_code; + } + + void linkCode(void* ownerUID, JITCompilationEffort); + + void performFinalization(); + +#if DUMP_LINK_STATISTICS + static void dumpLinkStatistics(void* code, size_t initialSize, size_t finalSize); +#endif + +#if DUMP_CODE + static void dumpCode(void* code, size_t); +#endif + + RefPtr m_executableMemory; + size_t m_size; +#if ENABLE(BRANCH_COMPACTION) + size_t m_initialSize; +#endif + void* m_code; + MacroAssembler* m_assembler; + JSGlobalData* m_globalData; +#ifndef NDEBUG + bool m_completed; + JITCompilationEffort m_effort; +#endif +}; + +#define FINALIZE_CODE_IF(condition, linkBufferReference, dataLogFArgumentsForHeading) \ + (UNLIKELY((condition)) \ + ? ((linkBufferReference).finalizeCodeWithDisassembly dataLogFArgumentsForHeading) \ + : (linkBufferReference).finalizeCodeWithoutDisassembly()) + +// Use this to finalize code, like so: +// +// CodeRef code = FINALIZE_CODE(linkBuffer, ("my super thingy number %d", number)); +// +// Which, in disassembly mode, will print: +// +// Generated JIT code for my super thingy number 42: +// Code at [0x123456, 0x234567]: +// 0x123456: mov $0, 0 +// 0x12345a: ret +// +// ... and so on. +// +// Note that the dataLogFArgumentsForHeading are only evaluated when showDisassembly +// is true, so you can hide expensive disassembly-only computations inside there. + +#define FINALIZE_CODE(linkBufferReference, dataLogFArgumentsForHeading) \ + FINALIZE_CODE_IF(Options::showDisassembly(), linkBufferReference, dataLogFArgumentsForHeading) + +#define FINALIZE_DFG_CODE(linkBufferReference, dataLogFArgumentsForHeading) \ + FINALIZE_CODE_IF((Options::showDisassembly() || Options::showDFGDisassembly()), linkBufferReference, dataLogFArgumentsForHeading) + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // LinkBuffer_h diff --git a/3rdparty/masm/assembler/MIPSAssembler.h b/3rdparty/masm/assembler/MIPSAssembler.h new file mode 100644 index 0000000000..026f87e52a --- /dev/null +++ b/3rdparty/masm/assembler/MIPSAssembler.h @@ -0,0 +1,1032 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 University of Szeged + * All rights reserved. + * Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY MIPS TECHNOLOGIES, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MIPS TECHNOLOGIES, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MIPSAssembler_h +#define MIPSAssembler_h + +#if ENABLE(ASSEMBLER) && CPU(MIPS) + +#include "AssemblerBuffer.h" +#include "JITCompilationEffort.h" +#include +#include + +namespace JSC { + +typedef uint32_t MIPSWord; + +namespace MIPSRegisters { +typedef enum { + r0 = 0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, + r16, + r17, + r18, + r19, + r20, + r21, + r22, + r23, + r24, + r25, + r26, + r27, + r28, + r29, + r30, + r31, + zero = r0, + at = r1, + v0 = r2, + v1 = r3, + a0 = r4, + a1 = r5, + a2 = r6, + a3 = r7, + t0 = r8, + t1 = r9, + t2 = r10, + t3 = r11, + t4 = r12, + t5 = r13, + t6 = r14, + t7 = r15, + s0 = r16, + s1 = r17, + s2 = r18, + s3 = r19, + s4 = r20, + s5 = r21, + s6 = r22, + s7 = r23, + t8 = r24, + t9 = r25, + k0 = r26, + k1 = r27, + gp = r28, + sp = r29, + fp = r30, + ra = r31 +} RegisterID; + +typedef enum { + f0, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + f22, + f23, + f24, + f25, + f26, + f27, + f28, + f29, + f30, + f31 +} FPRegisterID; + +} // namespace MIPSRegisters + +class MIPSAssembler { +public: + typedef MIPSRegisters::RegisterID RegisterID; + typedef MIPSRegisters::FPRegisterID FPRegisterID; + typedef SegmentedVector Jumps; + + MIPSAssembler() + { + } + + // MIPS instruction opcode field position + enum { + OP_SH_RD = 11, + OP_SH_RT = 16, + OP_SH_RS = 21, + OP_SH_SHAMT = 6, + OP_SH_CODE = 16, + OP_SH_FD = 6, + OP_SH_FS = 11, + OP_SH_FT = 16 + }; + + void emitInst(MIPSWord op) + { + void* oldBase = m_buffer.data(); + + m_buffer.putInt(op); + + void* newBase = m_buffer.data(); + if (oldBase != newBase) + relocateJumps(oldBase, newBase); + } + + void nop() + { + emitInst(0x00000000); + } + + /* Need to insert one load data delay nop for mips1. */ + void loadDelayNop() + { +#if WTF_MIPS_ISA(1) + nop(); +#endif + } + + /* Need to insert one coprocessor access delay nop for mips1. */ + void copDelayNop() + { +#if WTF_MIPS_ISA(1) + nop(); +#endif + } + + void move(RegisterID rd, RegisterID rs) + { + /* addu */ + emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS)); + } + + /* Set an immediate value to a register. This may generate 1 or 2 + instructions. */ + void li(RegisterID dest, int imm) + { + if (imm >= -32768 && imm <= 32767) + addiu(dest, MIPSRegisters::zero, imm); + else if (imm >= 0 && imm < 65536) + ori(dest, MIPSRegisters::zero, imm); + else { + lui(dest, imm >> 16); + if (imm & 0xffff) + ori(dest, dest, imm); + } + } + + void lui(RegisterID rt, int imm) + { + emitInst(0x3c000000 | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void addiu(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x24000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void addu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void subu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000023 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void mult(RegisterID rs, RegisterID rt) + { + emitInst(0x00000018 | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void div(RegisterID rs, RegisterID rt) + { + emitInst(0x0000001a | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void mfhi(RegisterID rd) + { + emitInst(0x00000010 | (rd << OP_SH_RD)); + } + + void mflo(RegisterID rd) + { + emitInst(0x00000012 | (rd << OP_SH_RD)); + } + + void mul(RegisterID rd, RegisterID rs, RegisterID rt) + { +#if WTF_MIPS_ISA_AT_LEAST(32) + emitInst(0x70000002 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); +#else + mult(rs, rt); + mflo(rd); +#endif + } + + void andInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000024 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void andi(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x30000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void nor(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000027 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void orInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000025 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void ori(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x34000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void xorInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000026 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void xori(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x38000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void slt(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x0000002a | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void sltu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x0000002b | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void sltiu(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x2c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void sll(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000000 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void sllv(RegisterID rd, RegisterID rt, int rs) + { + emitInst(0x00000004 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); + } + + void sra(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000003 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void srav(RegisterID rd, RegisterID rt, RegisterID rs) + { + emitInst(0x00000007 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); + } + + void srl(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000002 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void srlv(RegisterID rd, RegisterID rt, RegisterID rs) + { + emitInst(0x00000006 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); + } + + void lb(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x80000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void lbu(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x90000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void lw(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x8c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void lwl(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x88000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void lwr(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x98000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void lh(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x84000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void lhu(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x94000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void sb(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0xa0000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + } + + void sh(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0xa4000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + } + + void sw(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0xac000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + } + + void jr(RegisterID rs) + { + emitInst(0x00000008 | (rs << OP_SH_RS)); + } + + void jalr(RegisterID rs) + { + emitInst(0x0000f809 | (rs << OP_SH_RS)); + } + + void jal() + { + emitInst(0x0c000000); + } + + void bkpt() + { + int value = 512; /* BRK_BUG */ + emitInst(0x0000000d | ((value & 0x3ff) << OP_SH_CODE)); + } + + void bgez(RegisterID rs, int imm) + { + emitInst(0x04010000 | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void bltz(RegisterID rs, int imm) + { + emitInst(0x04000000 | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void beq(RegisterID rs, RegisterID rt, int imm) + { + emitInst(0x10000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void bne(RegisterID rs, RegisterID rt, int imm) + { + emitInst(0x14000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void bc1t() + { + emitInst(0x45010000); + } + + void bc1f() + { + emitInst(0x45000000); + } + + void appendJump() + { + m_jumps.append(m_buffer.label()); + } + + void addd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200000 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + } + + void subd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200001 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + } + + void muld(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200002 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + } + + void divd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200003 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + } + + void lwc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xc4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); + copDelayNop(); + } + + void ldc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xd4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); + } + + void swc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xe4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); + } + + void sdc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xf4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); + } + + void mtc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44800000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void mthc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44e00000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void mfc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44000000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void sqrtd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200004 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void truncwd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x4620000d | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtdw(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46800021 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtds(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46000021 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtwd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200024 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtsd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200020 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void ceqd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200032 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cngtd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003f | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cnged(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003d | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cltd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003c | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cled(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003e | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cueqd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200033 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void coled(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200036 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void coltd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200034 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void culed(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200037 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cultd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200035 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + // General helpers + + AssemblerLabel labelIgnoringWatchpoints() + { + return m_buffer.label(); + } + + AssemblerLabel label() + { + return m_buffer.label(); + } + + AssemblerLabel align(int alignment) + { + while (!m_buffer.isAligned(alignment)) + bkpt(); + + return label(); + } + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + // Assembler admin methods: + + size_t codeSize() const + { + return m_buffer.codeSize(); + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + RefPtr result = m_buffer.executableCopy(globalData, ownerUID, effort); + if (!result) + return 0; + + relocateJumps(m_buffer.data(), result->start()); + return result.release(); + } + + unsigned debugOffset() { return m_buffer.debugOffset(); } + + // Assembly helpers for moving data between fp and registers. + void vmov(RegisterID rd1, RegisterID rd2, FPRegisterID rn) + { + mfc1(rd1, rn); + mfc1(rd2, FPRegisterID(rn + 1)); + } + + void vmov(FPRegisterID rd, RegisterID rn1, RegisterID rn2) + { + mtc1(rn1, rd); + mtc1(rn2, FPRegisterID(rd + 1)); + } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + // The return address is after a call and a delay slot instruction + return call.m_offset; + } + + // Linking & patching: + // + // 'link' and 'patch' methods are for use on unprotected code - such as the code + // within the AssemblerBuffer, and code being patched by the patch buffer. Once + // code has been finalized it is (platform support permitting) within a non- + // writable region of memory; to modify the code in an execute-only execuable + // pool the 'repatch' and 'relink' methods should be used. + + void linkJump(AssemblerLabel from, AssemblerLabel to) + { + ASSERT(to.isSet()); + ASSERT(from.isSet()); + MIPSWord* insn = reinterpret_cast(reinterpret_cast(m_buffer.data()) + from.m_offset); + MIPSWord* toPos = reinterpret_cast(reinterpret_cast(m_buffer.data()) + to.m_offset); + + ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5))); + insn = insn - 6; + linkWithOffset(insn, toPos); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + + ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5))); + insn = insn - 6; + linkWithOffset(insn, to); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + linkCallInternal(insn, to); + } + + static void linkPointer(void* code, AssemblerLabel from, void* to) + { + MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + *insn = (*insn & 0xffff0000) | ((reinterpret_cast(to) >> 16) & 0xffff); + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + *insn = (*insn & 0xffff0000) | (reinterpret_cast(to) & 0xffff); + } + + static void relinkJump(void* from, void* to) + { + MIPSWord* insn = reinterpret_cast(from); + + ASSERT(!(*(insn - 1)) && !(*(insn - 5))); + insn = insn - 6; + int flushSize = linkWithOffset(insn, to); + + cacheFlush(insn, flushSize); + } + + static void relinkCall(void* from, void* to) + { + void* start; + int size = linkCallInternal(from, to); + if (size == sizeof(MIPSWord)) + start = reinterpret_cast(reinterpret_cast(from) - 2 * sizeof(MIPSWord)); + else + start = reinterpret_cast(reinterpret_cast(from) - 4 * sizeof(MIPSWord)); + + cacheFlush(start, size); + } + + static void repatchInt32(void* from, int32_t to) + { + MIPSWord* insn = reinterpret_cast(from); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + *insn = (*insn & 0xffff0000) | ((to >> 16) & 0xffff); + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + *insn = (*insn & 0xffff0000) | (to & 0xffff); + insn--; + cacheFlush(insn, 2 * sizeof(MIPSWord)); + } + + static int32_t readInt32(void* from) + { + MIPSWord* insn = reinterpret_cast(from); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + int32_t result = (*insn & 0x0000ffff) << 16; + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + result |= *insn & 0x0000ffff; + return result; + } + + static void repatchCompact(void* where, int32_t value) + { + repatchInt32(where, value); + } + + static void repatchPointer(void* from, void* to) + { + repatchInt32(from, reinterpret_cast(to)); + } + + static void* readPointer(void* from) + { + return reinterpret_cast(readInt32(from)); + } + + static void* readCallTarget(void* from) + { + MIPSWord* insn = reinterpret_cast(from); + insn -= 4; + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + int32_t result = (*insn & 0x0000ffff) << 16; + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + result |= *insn & 0x0000ffff; + return reinterpret_cast(result); + } + + static void cacheFlush(void* code, size_t size) + { +#if GCC_VERSION_AT_LEAST(4, 3, 0) +#if WTF_MIPS_ISA_REV(2) && !GCC_VERSION_AT_LEAST(4, 4, 3) + int lineSize; + asm("rdhwr %0, $1" : "=r" (lineSize)); + // + // Modify "start" and "end" to avoid GCC 4.3.0-4.4.2 bug in + // mips_expand_synci_loop that may execute synci one more time. + // "start" points to the fisrt byte of the cache line. + // "end" points to the last byte of the line before the last cache line. + // Because size is always a multiple of 4, this is safe to set + // "end" to the last byte. + // + intptr_t start = reinterpret_cast(code) & (-lineSize); + intptr_t end = ((reinterpret_cast(code) + size - 1) & (-lineSize)) - 1; + __builtin___clear_cache(reinterpret_cast(start), reinterpret_cast(end)); +#else + intptr_t end = reinterpret_cast(code) + size; + __builtin___clear_cache(reinterpret_cast(code), reinterpret_cast(end)); +#endif +#else + _flush_cache(reinterpret_cast(code), size, BCACHE); +#endif + } + + static void revertJumpToMove(void* instructionStart, RegisterID rt, int imm) + { + MIPSWord* insn = static_cast(instructionStart) + 1; + ASSERT((*insn & 0xfc000000) == 0x34000000); + *insn = (*insn & 0xfc1f0000) | (imm & 0xffff); + cacheFlush(insn, sizeof(MIPSWord)); + } + + static void replaceWithJump(void* instructionStart, void* to) + { + MIPSWord* instruction = reinterpret_cast(instructionStart); + intptr_t jumpTo = reinterpret_cast(to); + + // lui + instruction[0] = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((jumpTo >> 16) & 0xffff); + // ori + instruction[1] = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (jumpTo & 0xffff); + // jr + instruction[2] = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); + // nop + instruction[3] = 0x0; + + cacheFlush(instruction, sizeof(MIPSWord) * 4); + } + + static void replaceWithLoad(void* instructionStart) + { + MIPSWord* insn = reinterpret_cast(instructionStart); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + insn++; + ASSERT((*insn & 0xfc0007ff) == 0x00000021); // addu + insn++; + *insn = 0x8c000000 | ((*insn) & 0x3ffffff); // lw + cacheFlush(insn, 4); + } + + static void replaceWithAddressComputation(void* instructionStart) + { + MIPSWord* insn = reinterpret_cast(instructionStart); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + insn++; + ASSERT((*insn & 0xfc0007ff) == 0x00000021); // addu + insn++; + *insn = 0x24000000 | ((*insn) & 0x3ffffff); // addiu + cacheFlush(insn, 4); + } + +private: + /* Update each jump in the buffer of newBase. */ + void relocateJumps(void* oldBase, void* newBase) + { + // Check each jump + for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) { + int pos = iter->m_offset; + MIPSWord* insn = reinterpret_cast(reinterpret_cast(newBase) + pos); + insn = insn + 2; + // Need to make sure we have 5 valid instructions after pos + if ((unsigned)pos >= m_buffer.codeSize() - 5 * sizeof(MIPSWord)) + continue; + + if ((*insn & 0xfc000000) == 0x08000000) { // j + int offset = *insn & 0x03ffffff; + int oldInsnAddress = (int)insn - (int)newBase + (int)oldBase; + int topFourBits = (oldInsnAddress + 4) >> 28; + int oldTargetAddress = (topFourBits << 28) | (offset << 2); + int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase; + int newInsnAddress = (int)insn; + if (((newInsnAddress + 4) >> 28) == (newTargetAddress >> 28)) + *insn = 0x08000000 | ((newTargetAddress >> 2) & 0x3ffffff); + else { + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + /* jr */ + *(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); + } + } else if ((*insn & 0xffe00000) == 0x3c000000) { // lui + int high = (*insn & 0xffff) << 16; + int low = *(insn + 1) & 0xffff; + int oldTargetAddress = high | low; + int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase; + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + } + } + } + + static int linkWithOffset(MIPSWord* insn, void* to) + { + ASSERT((*insn & 0xfc000000) == 0x10000000 // beq + || (*insn & 0xfc000000) == 0x14000000 // bne + || (*insn & 0xffff0000) == 0x45010000 // bc1t + || (*insn & 0xffff0000) == 0x45000000); // bc1f + intptr_t diff = (reinterpret_cast(to) - reinterpret_cast(insn) - 4) >> 2; + + if (diff < -32768 || diff > 32767 || *(insn + 2) != 0x10000003) { + /* + Convert the sequence: + beq $2, $3, target + nop + b 1f + nop + nop + nop + 1: + + to the new sequence if possible: + bne $2, $3, 1f + nop + j target + nop + nop + nop + 1: + + OR to the new sequence: + bne $2, $3, 1f + nop + lui $25, target >> 16 + ori $25, $25, target & 0xffff + jr $25 + nop + 1: + + Note: beq/bne/bc1t/bc1f are converted to bne/beq/bc1f/bc1t. + */ + + if (*(insn + 2) == 0x10000003) { + if ((*insn & 0xfc000000) == 0x10000000) // beq + *insn = (*insn & 0x03ff0000) | 0x14000005; // bne + else if ((*insn & 0xfc000000) == 0x14000000) // bne + *insn = (*insn & 0x03ff0000) | 0x10000005; // beq + else if ((*insn & 0xffff0000) == 0x45010000) // bc1t + *insn = 0x45000005; // bc1f + else if ((*insn & 0xffff0000) == 0x45000000) // bc1f + *insn = 0x45010005; // bc1t + else + ASSERT(0); + } + + insn = insn + 2; + if ((reinterpret_cast(insn) + 4) >> 28 + == reinterpret_cast(to) >> 28) { + *insn = 0x08000000 | ((reinterpret_cast(to) >> 2) & 0x3ffffff); + *(insn + 1) = 0; + return 4 * sizeof(MIPSWord); + } + + intptr_t newTargetAddress = reinterpret_cast(to); + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + /* jr */ + *(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); + return 5 * sizeof(MIPSWord); + } + + *insn = (*insn & 0xffff0000) | (diff & 0xffff); + return sizeof(MIPSWord); + } + + static int linkCallInternal(void* from, void* to) + { + MIPSWord* insn = reinterpret_cast(from); + insn = insn - 4; + + if ((*(insn + 2) & 0xfc000000) == 0x0c000000) { // jal + if ((reinterpret_cast(from) - 4) >> 28 + == reinterpret_cast(to) >> 28) { + *(insn + 2) = 0x0c000000 | ((reinterpret_cast(to) >> 2) & 0x3ffffff); + return sizeof(MIPSWord); + } + + /* lui $25, (to >> 16) & 0xffff */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((reinterpret_cast(to) >> 16) & 0xffff); + /* ori $25, $25, to & 0xffff */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (reinterpret_cast(to) & 0xffff); + /* jalr $25 */ + *(insn + 2) = 0x0000f809 | (MIPSRegisters::t9 << OP_SH_RS); + return 3 * sizeof(MIPSWord); + } + + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + ASSERT((*(insn + 1) & 0xfc000000) == 0x34000000); // ori + + /* lui */ + *insn = (*insn & 0xffff0000) | ((reinterpret_cast(to) >> 16) & 0xffff); + /* ori */ + *(insn + 1) = (*(insn + 1) & 0xffff0000) | (reinterpret_cast(to) & 0xffff); + return 2 * sizeof(MIPSWord); + } + + AssemblerBuffer m_buffer; + Jumps m_jumps; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(MIPS) + +#endif // MIPSAssembler_h diff --git a/3rdparty/masm/assembler/MacroAssembler.h b/3rdparty/masm/assembler/MacroAssembler.h new file mode 100644 index 0000000000..3d57340f93 --- /dev/null +++ b/3rdparty/masm/assembler/MacroAssembler.h @@ -0,0 +1,1464 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssembler_h +#define MacroAssembler_h + +#include + +#if ENABLE(ASSEMBLER) + +#if CPU(ARM_THUMB2) +#include "MacroAssemblerARMv7.h" +namespace JSC { typedef MacroAssemblerARMv7 MacroAssemblerBase; }; + +#elif CPU(ARM_TRADITIONAL) +#include "MacroAssemblerARM.h" +namespace JSC { typedef MacroAssemblerARM MacroAssemblerBase; }; + +#elif CPU(MIPS) +#include "MacroAssemblerMIPS.h" +namespace JSC { +typedef MacroAssemblerMIPS MacroAssemblerBase; +}; + +#elif CPU(X86) +#include "MacroAssemblerX86.h" +namespace JSC { typedef MacroAssemblerX86 MacroAssemblerBase; }; + +#elif CPU(X86_64) +#include "MacroAssemblerX86_64.h" +namespace JSC { typedef MacroAssemblerX86_64 MacroAssemblerBase; }; + +#elif CPU(SH4) +#include "MacroAssemblerSH4.h" +namespace JSC { +typedef MacroAssemblerSH4 MacroAssemblerBase; +}; + +#else +#error "The MacroAssembler is not supported on this platform." +#endif + +namespace JSC { + +class MacroAssembler : public MacroAssemblerBase { +public: + + using MacroAssemblerBase::pop; + using MacroAssemblerBase::jump; + using MacroAssemblerBase::branch32; + using MacroAssemblerBase::move; + +#if ENABLE(JIT_CONSTANT_BLINDING) + using MacroAssemblerBase::add32; + using MacroAssemblerBase::and32; + using MacroAssemblerBase::branchAdd32; + using MacroAssemblerBase::branchMul32; + using MacroAssemblerBase::branchSub32; + using MacroAssemblerBase::lshift32; + using MacroAssemblerBase::or32; + using MacroAssemblerBase::rshift32; + using MacroAssemblerBase::store32; + using MacroAssemblerBase::sub32; + using MacroAssemblerBase::urshift32; + using MacroAssemblerBase::xor32; +#endif + + static const double twoToThe32; // This is super useful for some double code. + + // Utilities used by the DFG JIT. +#if ENABLE(DFG_JIT) + using MacroAssemblerBase::invert; + + static DoubleCondition invert(DoubleCondition cond) + { + switch (cond) { + case DoubleEqual: + return DoubleNotEqualOrUnordered; + case DoubleNotEqual: + return DoubleEqualOrUnordered; + case DoubleGreaterThan: + return DoubleLessThanOrEqualOrUnordered; + case DoubleGreaterThanOrEqual: + return DoubleLessThanOrUnordered; + case DoubleLessThan: + return DoubleGreaterThanOrEqualOrUnordered; + case DoubleLessThanOrEqual: + return DoubleGreaterThanOrUnordered; + case DoubleEqualOrUnordered: + return DoubleNotEqual; + case DoubleNotEqualOrUnordered: + return DoubleEqual; + case DoubleGreaterThanOrUnordered: + return DoubleLessThanOrEqual; + case DoubleGreaterThanOrEqualOrUnordered: + return DoubleLessThan; + case DoubleLessThanOrUnordered: + return DoubleGreaterThanOrEqual; + case DoubleLessThanOrEqualOrUnordered: + return DoubleGreaterThan; + default: + ASSERT_NOT_REACHED(); + return DoubleEqual; // make compiler happy + } + } + + static bool isInvertible(ResultCondition cond) + { + switch (cond) { + case Zero: + case NonZero: + return true; + default: + return false; + } + } + + static ResultCondition invert(ResultCondition cond) + { + switch (cond) { + case Zero: + return NonZero; + case NonZero: + return Zero; + default: + ASSERT_NOT_REACHED(); + return Zero; // Make compiler happy for release builds. + } + } +#endif + + // Platform agnostic onvenience functions, + // described in terms of other macro assembly methods. + void pop() + { + addPtr(TrustedImm32(sizeof(void*)), stackPointerRegister); + } + + void peek(RegisterID dest, int index = 0) + { + loadPtr(Address(stackPointerRegister, (index * sizeof(void*))), dest); + } + + Address addressForPoke(int index) + { + return Address(stackPointerRegister, (index * sizeof(void*))); + } + + void poke(RegisterID src, int index = 0) + { + storePtr(src, addressForPoke(index)); + } + + void poke(TrustedImm32 value, int index = 0) + { + store32(value, addressForPoke(index)); + } + + void poke(TrustedImmPtr imm, int index = 0) + { + storePtr(imm, addressForPoke(index)); + } + +#if CPU(X86_64) + void peek64(RegisterID dest, int index = 0) + { + load64(Address(stackPointerRegister, (index * sizeof(void*))), dest); + } + + void poke(TrustedImm64 value, int index = 0) + { + store64(value, addressForPoke(index)); + } + + void poke64(RegisterID src, int index = 0) + { + store64(src, addressForPoke(index)); + } +#endif + + + // Backwards banches, these are currently all implemented using existing forwards branch mechanisms. + void branchPtr(RelationalCondition cond, RegisterID op1, TrustedImmPtr imm, Label target) + { + branchPtr(cond, op1, imm).linkTo(target, this); + } + void branchPtr(RelationalCondition cond, RegisterID op1, ImmPtr imm, Label target) + { + branchPtr(cond, op1, imm).linkTo(target, this); + } + + void branch32(RelationalCondition cond, RegisterID op1, RegisterID op2, Label target) + { + branch32(cond, op1, op2).linkTo(target, this); + } + + void branch32(RelationalCondition cond, RegisterID op1, TrustedImm32 imm, Label target) + { + branch32(cond, op1, imm).linkTo(target, this); + } + + void branch32(RelationalCondition cond, RegisterID op1, Imm32 imm, Label target) + { + branch32(cond, op1, imm).linkTo(target, this); + } + + void branch32(RelationalCondition cond, RegisterID left, Address right, Label target) + { + branch32(cond, left, right).linkTo(target, this); + } + + Jump branch32(RelationalCondition cond, TrustedImm32 left, RegisterID right) + { + return branch32(commute(cond), right, left); + } + + Jump branch32(RelationalCondition cond, Imm32 left, RegisterID right) + { + return branch32(commute(cond), right, left); + } + + void branchTestPtr(ResultCondition cond, RegisterID reg, Label target) + { + branchTestPtr(cond, reg).linkTo(target, this); + } + +#if !CPU(ARM_THUMB2) + PatchableJump patchableBranchPtr(RelationalCondition cond, Address left, TrustedImmPtr right = TrustedImmPtr(0)) + { + return PatchableJump(branchPtr(cond, left, right)); + } + + PatchableJump patchableBranchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + return PatchableJump(branchPtrWithPatch(cond, left, dataLabel, initialRightValue)); + } + + PatchableJump patchableJump() + { + return PatchableJump(jump()); + } + + PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + return PatchableJump(branchTest32(cond, reg, mask)); + } +#endif // !CPU(ARM_THUMB2) + +#if !CPU(ARM) + PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) + { + return PatchableJump(branch32(cond, reg, imm)); + } +#endif // !(CPU(ARM) + + void jump(Label target) + { + jump().linkTo(target, this); + } + + // Commute a relational condition, returns a new condition that will produce + // the same results given the same inputs but with their positions exchanged. + static RelationalCondition commute(RelationalCondition condition) + { + switch (condition) { + case Above: + return Below; + case AboveOrEqual: + return BelowOrEqual; + case Below: + return Above; + case BelowOrEqual: + return AboveOrEqual; + case GreaterThan: + return LessThan; + case GreaterThanOrEqual: + return LessThanOrEqual; + case LessThan: + return GreaterThan; + case LessThanOrEqual: + return GreaterThanOrEqual; + default: + break; + } + + ASSERT(condition == Equal || condition == NotEqual); + return condition; + } + + static const unsigned BlindingModulus = 64; + bool shouldConsiderBlinding() + { + return !(random() & (BlindingModulus - 1)); + } + + // Ptr methods + // On 32-bit platforms (i.e. x86), these methods directly map onto their 32-bit equivalents. + // FIXME: should this use a test for 32-bitness instead of this specific exception? +#if !CPU(X86_64) + void addPtr(Address src, RegisterID dest) + { + add32(src, dest); + } + + void addPtr(AbsoluteAddress src, RegisterID dest) + { + add32(src, dest); + } + + void addPtr(RegisterID src, RegisterID dest) + { + add32(src, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID srcDest) + { + add32(imm, srcDest); + } + + void addPtr(TrustedImmPtr imm, RegisterID dest) + { + add32(TrustedImm32(imm), dest); + } + + void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + add32(imm, src, dest); + } + + void addPtr(TrustedImm32 imm, AbsoluteAddress address) + { + add32(imm, address); + } + + void andPtr(RegisterID src, RegisterID dest) + { + and32(src, dest); + } + + void andPtr(TrustedImm32 imm, RegisterID srcDest) + { + and32(imm, srcDest); + } + + void negPtr(RegisterID dest) + { + neg32(dest); + } + + void orPtr(RegisterID src, RegisterID dest) + { + or32(src, dest); + } + + void orPtr(RegisterID op1, RegisterID op2, RegisterID dest) + { + or32(op1, op2, dest); + } + + void orPtr(TrustedImmPtr imm, RegisterID dest) + { + or32(TrustedImm32(imm), dest); + } + + void orPtr(TrustedImm32 imm, RegisterID dest) + { + or32(imm, dest); + } + + void subPtr(RegisterID src, RegisterID dest) + { + sub32(src, dest); + } + + void subPtr(TrustedImm32 imm, RegisterID dest) + { + sub32(imm, dest); + } + + void subPtr(TrustedImmPtr imm, RegisterID dest) + { + sub32(TrustedImm32(imm), dest); + } + + void xorPtr(RegisterID src, RegisterID dest) + { + xor32(src, dest); + } + + void xorPtr(TrustedImm32 imm, RegisterID srcDest) + { + xor32(imm, srcDest); + } + + + void loadPtr(ImplicitAddress address, RegisterID dest) + { + load32(address, dest); + } + + void loadPtr(BaseIndex address, RegisterID dest) + { + load32(address, dest); + } + + void loadPtr(const void* address, RegisterID dest) + { + load32(address, dest); + } + + DataLabel32 loadPtrWithAddressOffsetPatch(Address address, RegisterID dest) + { + return load32WithAddressOffsetPatch(address, dest); + } + + DataLabelCompact loadPtrWithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + return load32WithCompactAddressOffsetPatch(address, dest); + } + + void move(ImmPtr imm, RegisterID dest) + { + move(Imm32(imm.asTrustedImmPtr()), dest); + } + + void comparePtr(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + compare32(cond, left, right, dest); + } + + void storePtr(RegisterID src, ImplicitAddress address) + { + store32(src, address); + } + + void storePtr(RegisterID src, BaseIndex address) + { + store32(src, address); + } + + void storePtr(RegisterID src, void* address) + { + store32(src, address); + } + + void storePtr(TrustedImmPtr imm, ImplicitAddress address) + { + store32(TrustedImm32(imm), address); + } + + void storePtr(ImmPtr imm, Address address) + { + store32(Imm32(imm.asTrustedImmPtr()), address); + } + + void storePtr(TrustedImmPtr imm, void* address) + { + store32(TrustedImm32(imm), address); + } + + DataLabel32 storePtrWithAddressOffsetPatch(RegisterID src, Address address) + { + return store32WithAddressOffsetPatch(src, address); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, RegisterID right) + { + return branch32(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, TrustedImmPtr right) + { + return branch32(cond, left, TrustedImm32(right)); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, ImmPtr right) + { + return branch32(cond, left, Imm32(right.asTrustedImmPtr())); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, Address right) + { + return branch32(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, Address left, RegisterID right) + { + return branch32(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + return branch32(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, Address left, TrustedImmPtr right) + { + return branch32(cond, left, TrustedImm32(right)); + } + + Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, TrustedImmPtr right) + { + return branch32(cond, left, TrustedImm32(right)); + } + + Jump branchSubPtr(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchSub32(cond, src, dest); + } + + Jump branchTestPtr(ResultCondition cond, RegisterID reg, RegisterID mask) + { + return branchTest32(cond, reg, mask); + } + + Jump branchTestPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest32(cond, reg, mask); + } + + Jump branchTestPtr(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest32(cond, address, mask); + } + + Jump branchTestPtr(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest32(cond, address, mask); + } + + Jump branchAddPtr(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchAdd32(cond, src, dest); + } + + Jump branchSubPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchSub32(cond, imm, dest); + } + using MacroAssemblerBase::branchTest8; + Jump branchTest8(ResultCondition cond, ExtendedAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + return MacroAssemblerBase::branchTest8(cond, Address(address.base, address.offset), mask); + } +#else + void addPtr(RegisterID src, RegisterID dest) + { + add64(src, dest); + } + + void addPtr(Address src, RegisterID dest) + { + add64(src, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID srcDest) + { + add64(imm, srcDest); + } + + void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + add64(imm, src, dest); + } + + void addPtr(TrustedImm32 imm, Address address) + { + add64(imm, address); + } + + void addPtr(AbsoluteAddress src, RegisterID dest) + { + add64(src, dest); + } + + void addPtr(TrustedImmPtr imm, RegisterID dest) + { + add64(TrustedImm64(imm), dest); + } + + void addPtr(TrustedImm32 imm, AbsoluteAddress address) + { + add64(imm, address); + } + + void andPtr(RegisterID src, RegisterID dest) + { + and64(src, dest); + } + + void andPtr(TrustedImm32 imm, RegisterID srcDest) + { + and64(imm, srcDest); + } + + void negPtr(RegisterID dest) + { + neg64(dest); + } + + void orPtr(RegisterID src, RegisterID dest) + { + or64(src, dest); + } + + void orPtr(TrustedImm32 imm, RegisterID dest) + { + or64(imm, dest); + } + + void orPtr(TrustedImmPtr imm, RegisterID dest) + { + or64(TrustedImm64(imm), dest); + } + + void orPtr(RegisterID op1, RegisterID op2, RegisterID dest) + { + or64(op1, op2, dest); + } + + void orPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + or64(imm, src, dest); + } + + void rotateRightPtr(TrustedImm32 imm, RegisterID srcDst) + { + rotateRight64(imm, srcDst); + } + + void subPtr(RegisterID src, RegisterID dest) + { + sub64(src, dest); + } + + void subPtr(TrustedImm32 imm, RegisterID dest) + { + sub64(imm, dest); + } + + void subPtr(TrustedImmPtr imm, RegisterID dest) + { + sub64(TrustedImm64(imm), dest); + } + + void xorPtr(RegisterID src, RegisterID dest) + { + xor64(src, dest); + } + + void xorPtr(RegisterID src, Address dest) + { + xor64(src, dest); + } + + void xorPtr(TrustedImm32 imm, RegisterID srcDest) + { + xor64(imm, srcDest); + } + + void loadPtr(ImplicitAddress address, RegisterID dest) + { + load64(address, dest); + } + + void loadPtr(BaseIndex address, RegisterID dest) + { + load64(address, dest); + } + + void loadPtr(const void* address, RegisterID dest) + { + load64(address, dest); + } + + DataLabel32 loadPtrWithAddressOffsetPatch(Address address, RegisterID dest) + { + return load64WithAddressOffsetPatch(address, dest); + } + + DataLabelCompact loadPtrWithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + return load64WithCompactAddressOffsetPatch(address, dest); + } + + void storePtr(RegisterID src, ImplicitAddress address) + { + store64(src, address); + } + + void storePtr(RegisterID src, BaseIndex address) + { + store64(src, address); + } + + void storePtr(RegisterID src, void* address) + { + store64(src, address); + } + + void storePtr(TrustedImmPtr imm, ImplicitAddress address) + { + store64(TrustedImm64(imm), address); + } + + void storePtr(TrustedImmPtr imm, BaseIndex address) + { + store64(TrustedImm64(imm), address); + } + + DataLabel32 storePtrWithAddressOffsetPatch(RegisterID src, Address address) + { + return store64WithAddressOffsetPatch(src, address); + } + + void comparePtr(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + compare64(cond, left, right, dest); + } + + void comparePtr(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + compare64(cond, left, right, dest); + } + + void testPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) + { + test64(cond, reg, mask, dest); + } + + void testPtr(ResultCondition cond, RegisterID reg, RegisterID mask, RegisterID dest) + { + test64(cond, reg, mask, dest); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, RegisterID right) + { + return branch64(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, TrustedImmPtr right) + { + return branch64(cond, left, TrustedImm64(right)); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, Address right) + { + return branch64(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, Address left, RegisterID right) + { + return branch64(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + return branch64(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, Address left, TrustedImmPtr right) + { + return branch64(cond, left, TrustedImm64(right)); + } + + Jump branchTestPtr(ResultCondition cond, RegisterID reg, RegisterID mask) + { + return branchTest64(cond, reg, mask); + } + + Jump branchTestPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest64(cond, reg, mask); + } + + Jump branchTestPtr(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest64(cond, address, mask); + } + + Jump branchTestPtr(ResultCondition cond, Address address, RegisterID reg) + { + return branchTest64(cond, address, reg); + } + + Jump branchTestPtr(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest64(cond, address, mask); + } + + Jump branchTestPtr(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest64(cond, address, mask); + } + + Jump branchAddPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchAdd64(cond, imm, dest); + } + + Jump branchAddPtr(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchAdd64(cond, src, dest); + } + + Jump branchSubPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchSub64(cond, imm, dest); + } + + Jump branchSubPtr(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchSub64(cond, src, dest); + } + + Jump branchSubPtr(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) + { + return branchSub64(cond, src1, src2, dest); + } + +#if ENABLE(JIT_CONSTANT_BLINDING) + using MacroAssemblerBase::and64; + using MacroAssemblerBase::convertInt32ToDouble; + using MacroAssemblerBase::store64; + bool shouldBlindDouble(double value) + { + // Don't trust NaN or +/-Infinity + if (!isfinite(value)) + return shouldConsiderBlinding(); + + // Try to force normalisation, and check that there's no change + // in the bit pattern + if (bitwise_cast(value * 1.0) != bitwise_cast(value)) + return shouldConsiderBlinding(); + + value = abs(value); + // Only allow a limited set of fractional components + double scaledValue = value * 8; + if (scaledValue / 8 != value) + return shouldConsiderBlinding(); + double frac = scaledValue - floor(scaledValue); + if (frac != 0.0) + return shouldConsiderBlinding(); + + return value > 0xff; + } + + bool shouldBlind(ImmPtr imm) + { +#if !defined(NDEBUG) + UNUSED_PARAM(imm); + // Debug always blind all constants, if only so we know + // if we've broken blinding during patch development. + return true; +#endif + + // First off we'll special case common, "safe" values to avoid hurting + // performance too much + uintptr_t value = imm.asTrustedImmPtr().asIntptr(); + switch (value) { + case 0xffff: + case 0xffffff: + case 0xffffffffL: + case 0xffffffffffL: + case 0xffffffffffffL: + case 0xffffffffffffffL: + case 0xffffffffffffffffL: + return false; + default: { + if (value <= 0xff) + return false; + if (~value <= 0xff) + return false; + } + } + + if (!shouldConsiderBlinding()) + return false; + + return shouldBlindForSpecificArch(value); + } + + struct RotatedImmPtr { + RotatedImmPtr(uintptr_t v1, uint8_t v2) + : value(v1) + , rotation(v2) + { + } + TrustedImmPtr value; + TrustedImm32 rotation; + }; + + RotatedImmPtr rotationBlindConstant(ImmPtr imm) + { + uint8_t rotation = random() % (sizeof(void*) * 8); + uintptr_t value = imm.asTrustedImmPtr().asIntptr(); + value = (value << rotation) | (value >> (sizeof(void*) * 8 - rotation)); + return RotatedImmPtr(value, rotation); + } + + void loadRotationBlindedConstant(RotatedImmPtr constant, RegisterID dest) + { + move(constant.value, dest); + rotateRightPtr(constant.rotation, dest); + } + + bool shouldBlind(Imm64 imm) + { +#if !defined(NDEBUG) + UNUSED_PARAM(imm); + // Debug always blind all constants, if only so we know + // if we've broken blinding during patch development. + return true; +#endif + + // First off we'll special case common, "safe" values to avoid hurting + // performance too much + uint64_t value = imm.asTrustedImm64().m_value; + switch (value) { + case 0xffff: + case 0xffffff: + case 0xffffffffL: + case 0xffffffffffL: + case 0xffffffffffffL: + case 0xffffffffffffffL: + case 0xffffffffffffffffL: + return false; + default: { + if (value <= 0xff) + return false; + if (~value <= 0xff) + return false; + + JSValue jsValue = JSValue::decode(value); + if (jsValue.isInt32()) + return shouldBlind(Imm32(jsValue.asInt32())); + if (jsValue.isDouble() && !shouldBlindDouble(jsValue.asDouble())) + return false; + + if (!shouldBlindDouble(bitwise_cast(value))) + return false; + } + } + + if (!shouldConsiderBlinding()) + return false; + + return shouldBlindForSpecificArch(value); + } + + struct RotatedImm64 { + RotatedImm64(uint64_t v1, uint8_t v2) + : value(v1) + , rotation(v2) + { + } + TrustedImm64 value; + TrustedImm32 rotation; + }; + + RotatedImm64 rotationBlindConstant(Imm64 imm) + { + uint8_t rotation = random() % (sizeof(int64_t) * 8); + uint64_t value = imm.asTrustedImm64().m_value; + value = (value << rotation) | (value >> (sizeof(int64_t) * 8 - rotation)); + return RotatedImm64(value, rotation); + } + + void loadRotationBlindedConstant(RotatedImm64 constant, RegisterID dest) + { + move(constant.value, dest); + rotateRight64(constant.rotation, dest); + } + + void convertInt32ToDouble(Imm32 imm, FPRegisterID dest) + { + if (shouldBlind(imm)) { + RegisterID scratchRegister = scratchRegisterForBlinding(); + loadXorBlindedConstant(xorBlindConstant(imm), scratchRegister); + convertInt32ToDouble(scratchRegister, dest); + } else + convertInt32ToDouble(imm.asTrustedImm32(), dest); + } + + void move(ImmPtr imm, RegisterID dest) + { + if (shouldBlind(imm)) + loadRotationBlindedConstant(rotationBlindConstant(imm), dest); + else + move(imm.asTrustedImmPtr(), dest); + } + + void move(Imm64 imm, RegisterID dest) + { + if (shouldBlind(imm)) + loadRotationBlindedConstant(rotationBlindConstant(imm), dest); + else + move(imm.asTrustedImm64(), dest); + } + + void and64(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = andBlindedConstant(imm); + and64(key.value1, dest); + and64(key.value2, dest); + } else + and64(imm.asTrustedImm32(), dest); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, ImmPtr right) + { + if (shouldBlind(right)) { + RegisterID scratchRegister = scratchRegisterForBlinding(); + loadRotationBlindedConstant(rotationBlindConstant(right), scratchRegister); + return branchPtr(cond, left, scratchRegister); + } + return branchPtr(cond, left, right.asTrustedImmPtr()); + } + + void storePtr(ImmPtr imm, Address dest) + { + if (shouldBlind(imm)) { + RegisterID scratchRegister = scratchRegisterForBlinding(); + loadRotationBlindedConstant(rotationBlindConstant(imm), scratchRegister); + storePtr(scratchRegister, dest); + } else + storePtr(imm.asTrustedImmPtr(), dest); + } + + void store64(Imm64 imm, Address dest) + { + if (shouldBlind(imm)) { + RegisterID scratchRegister = scratchRegisterForBlinding(); + loadRotationBlindedConstant(rotationBlindConstant(imm), scratchRegister); + store64(scratchRegister, dest); + } else + store64(imm.asTrustedImm64(), dest); + } + +#endif + +#endif // !CPU(X86_64) + +#if ENABLE(JIT_CONSTANT_BLINDING) + bool shouldBlind(Imm32 imm) + { +#if !defined(NDEBUG) + UNUSED_PARAM(imm); + // Debug always blind all constants, if only so we know + // if we've broken blinding during patch development. + return true; +#else + + // First off we'll special case common, "safe" values to avoid hurting + // performance too much + uint32_t value = imm.asTrustedImm32().m_value; + switch (value) { + case 0xffff: + case 0xffffff: + case 0xffffffff: + return false; + default: + if (value <= 0xff) + return false; + if (~value <= 0xff) + return false; + } + + if (!shouldConsiderBlinding()) + return false; + + return shouldBlindForSpecificArch(value); +#endif + } + + struct BlindedImm32 { + BlindedImm32(int32_t v1, int32_t v2) + : value1(v1) + , value2(v2) + { + } + TrustedImm32 value1; + TrustedImm32 value2; + }; + + uint32_t keyForConstant(uint32_t value, uint32_t& mask) + { + uint32_t key = random(); + if (value <= 0xff) + mask = 0xff; + else if (value <= 0xffff) + mask = 0xffff; + else if (value <= 0xffffff) + mask = 0xffffff; + else + mask = 0xffffffff; + return key & mask; + } + + uint32_t keyForConstant(uint32_t value) + { + uint32_t mask = 0; + return keyForConstant(value, mask); + } + + BlindedImm32 xorBlindConstant(Imm32 imm) + { + uint32_t baseValue = imm.asTrustedImm32().m_value; + uint32_t key = keyForConstant(baseValue); + return BlindedImm32(baseValue ^ key, key); + } + + BlindedImm32 additionBlindedConstant(Imm32 imm) + { + // The addition immediate may be used as a pointer offset. Keep aligned based on "imm". + static uint32_t maskTable[4] = { 0xfffffffc, 0xffffffff, 0xfffffffe, 0xffffffff }; + + uint32_t baseValue = imm.asTrustedImm32().m_value; + uint32_t key = keyForConstant(baseValue) & maskTable[baseValue & 3]; + if (key > baseValue) + key = key - baseValue; + return BlindedImm32(baseValue - key, key); + } + + BlindedImm32 andBlindedConstant(Imm32 imm) + { + uint32_t baseValue = imm.asTrustedImm32().m_value; + uint32_t mask = 0; + uint32_t key = keyForConstant(baseValue, mask); + ASSERT((baseValue & mask) == baseValue); + return BlindedImm32(((baseValue & key) | ~key) & mask, ((baseValue & ~key) | key) & mask); + } + + BlindedImm32 orBlindedConstant(Imm32 imm) + { + uint32_t baseValue = imm.asTrustedImm32().m_value; + uint32_t mask = 0; + uint32_t key = keyForConstant(baseValue, mask); + ASSERT((baseValue & mask) == baseValue); + return BlindedImm32((baseValue & key) & mask, (baseValue & ~key) & mask); + } + + void loadXorBlindedConstant(BlindedImm32 constant, RegisterID dest) + { + move(constant.value1, dest); + xor32(constant.value2, dest); + } + + void add32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = additionBlindedConstant(imm); + add32(key.value1, dest); + add32(key.value2, dest); + } else + add32(imm.asTrustedImm32(), dest); + } + + void addPtr(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = additionBlindedConstant(imm); + addPtr(key.value1, dest); + addPtr(key.value2, dest); + } else + addPtr(imm.asTrustedImm32(), dest); + } + + void and32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = andBlindedConstant(imm); + and32(key.value1, dest); + and32(key.value2, dest); + } else + and32(imm.asTrustedImm32(), dest); + } + + void andPtr(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = andBlindedConstant(imm); + andPtr(key.value1, dest); + andPtr(key.value2, dest); + } else + andPtr(imm.asTrustedImm32(), dest); + } + + void and32(Imm32 imm, RegisterID src, RegisterID dest) + { + if (shouldBlind(imm)) { + if (src == dest) + return and32(imm.asTrustedImm32(), dest); + loadXorBlindedConstant(xorBlindConstant(imm), dest); + and32(src, dest); + } else + and32(imm.asTrustedImm32(), src, dest); + } + + void move(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) + loadXorBlindedConstant(xorBlindConstant(imm), dest); + else + move(imm.asTrustedImm32(), dest); + } + + void or32(Imm32 imm, RegisterID src, RegisterID dest) + { + if (shouldBlind(imm)) { + if (src == dest) + return or32(imm, dest); + loadXorBlindedConstant(xorBlindConstant(imm), dest); + or32(src, dest); + } else + or32(imm.asTrustedImm32(), src, dest); + } + + void or32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = orBlindedConstant(imm); + or32(key.value1, dest); + or32(key.value2, dest); + } else + or32(imm.asTrustedImm32(), dest); + } + + void poke(Imm32 value, int index = 0) + { + store32(value, addressForPoke(index)); + } + + void poke(ImmPtr value, int index = 0) + { + storePtr(value, addressForPoke(index)); + } + +#if CPU(X86_64) + void poke(Imm64 value, int index = 0) + { + store64(value, addressForPoke(index)); + } +#endif + + void store32(Imm32 imm, Address dest) + { + if (shouldBlind(imm)) { +#if CPU(X86) || CPU(X86_64) + BlindedImm32 blind = xorBlindConstant(imm); + store32(blind.value1, dest); + xor32(blind.value2, dest); +#else + if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { + loadXorBlindedConstant(xorBlindConstant(imm), scratchRegister); + store32(scratchRegister, dest); + } else { + // If we don't have a scratch register available for use, we'll just + // place a random number of nops. + uint32_t nopCount = random() & 3; + while (nopCount--) + nop(); + store32(imm.asTrustedImm32(), dest); + } +#endif + } else + store32(imm.asTrustedImm32(), dest); + } + + void sub32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = additionBlindedConstant(imm); + sub32(key.value1, dest); + sub32(key.value2, dest); + } else + sub32(imm.asTrustedImm32(), dest); + } + + void subPtr(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = additionBlindedConstant(imm); + subPtr(key.value1, dest); + subPtr(key.value2, dest); + } else + subPtr(imm.asTrustedImm32(), dest); + } + + void xor32(Imm32 imm, RegisterID src, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 blind = xorBlindConstant(imm); + xor32(blind.value1, src, dest); + xor32(blind.value2, dest); + } else + xor32(imm.asTrustedImm32(), src, dest); + } + + void xor32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 blind = xorBlindConstant(imm); + xor32(blind.value1, dest); + xor32(blind.value2, dest); + } else + xor32(imm.asTrustedImm32(), dest); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Imm32 right) + { + if (shouldBlind(right)) { + if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { + loadXorBlindedConstant(xorBlindConstant(right), scratchRegister); + return branch32(cond, left, scratchRegister); + } + // If we don't have a scratch register available for use, we'll just + // place a random number of nops. + uint32_t nopCount = random() & 3; + while (nopCount--) + nop(); + return branch32(cond, left, right.asTrustedImm32()); + } + + return branch32(cond, left, right.asTrustedImm32()); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, Imm32 imm, RegisterID dest) + { + if (src == dest) { + if (!scratchRegisterForBlinding()) { + // Release mode ASSERT, if this fails we will perform incorrect codegen. + CRASH(); + } + } + if (shouldBlind(imm)) { + if (src == dest) { + if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { + move(src, scratchRegister); + src = scratchRegister; + } + } + loadXorBlindedConstant(xorBlindConstant(imm), dest); + return branchAdd32(cond, src, dest); + } + return branchAdd32(cond, src, imm.asTrustedImm32(), dest); + } + + Jump branchMul32(ResultCondition cond, Imm32 imm, RegisterID src, RegisterID dest) + { + if (src == dest) { + if (!scratchRegisterForBlinding()) { + // Release mode ASSERT, if this fails we will perform incorrect codegen. + CRASH(); + } + } + if (shouldBlind(imm)) { + if (src == dest) { + if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { + move(src, scratchRegister); + src = scratchRegister; + } + } + loadXorBlindedConstant(xorBlindConstant(imm), dest); + return branchMul32(cond, src, dest); + } + return branchMul32(cond, imm.asTrustedImm32(), src, dest); + } + + // branchSub32 takes a scratch register as 32 bit platforms make use of this, + // with src == dst, and on x86-32 we don't have a platform scratch register. + Jump branchSub32(ResultCondition cond, RegisterID src, Imm32 imm, RegisterID dest, RegisterID scratch) + { + if (shouldBlind(imm)) { + ASSERT(scratch != dest); + ASSERT(scratch != src); + loadXorBlindedConstant(xorBlindConstant(imm), scratch); + return branchSub32(cond, src, scratch, dest); + } + return branchSub32(cond, src, imm.asTrustedImm32(), dest); + } + + // Immediate shifts only have 5 controllable bits + // so we'll consider them safe for now. + TrustedImm32 trustedImm32ForShift(Imm32 imm) + { + return TrustedImm32(imm.asTrustedImm32().m_value & 31); + } + + void lshift32(Imm32 imm, RegisterID dest) + { + lshift32(trustedImm32ForShift(imm), dest); + } + + void lshift32(RegisterID src, Imm32 amount, RegisterID dest) + { + lshift32(src, trustedImm32ForShift(amount), dest); + } + + void rshift32(Imm32 imm, RegisterID dest) + { + rshift32(trustedImm32ForShift(imm), dest); + } + + void rshift32(RegisterID src, Imm32 amount, RegisterID dest) + { + rshift32(src, trustedImm32ForShift(amount), dest); + } + + void urshift32(Imm32 imm, RegisterID dest) + { + urshift32(trustedImm32ForShift(imm), dest); + } + + void urshift32(RegisterID src, Imm32 amount, RegisterID dest) + { + urshift32(src, trustedImm32ForShift(amount), dest); + } +#endif +}; + +} // namespace JSC + +#else // ENABLE(ASSEMBLER) + +// If there is no assembler for this platform, at least allow code to make references to +// some of the things it would otherwise define, albeit without giving that code any way +// of doing anything useful. +class MacroAssembler { +private: + MacroAssembler() { } + +public: + + enum RegisterID { NoRegister }; + enum FPRegisterID { NoFPRegister }; +}; + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssembler_h diff --git a/3rdparty/masm/assembler/MacroAssemblerARM.cpp b/3rdparty/masm/assembler/MacroAssemblerARM.cpp new file mode 100644 index 0000000000..98dc3e9879 --- /dev/null +++ b/3rdparty/masm/assembler/MacroAssemblerARM.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2009 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#include "MacroAssemblerARM.h" + +#if OS(LINUX) +#include +#include +#include +#include +#include +#include +#endif + +namespace JSC { + +static bool isVFPPresent() +{ +#if OS(LINUX) + int fd = open("/proc/self/auxv", O_RDONLY); + if (fd > 0) { + Elf32_auxv_t aux; + while (read(fd, &aux, sizeof(Elf32_auxv_t))) { + if (aux.a_type == AT_HWCAP) { + close(fd); + return aux.a_un.a_val & HWCAP_VFP; + } + } + close(fd); + } +#endif + +#if (COMPILER(RVCT) && defined(__TARGET_FPU_VFP)) || (COMPILER(GCC) && defined(__VFP_FP__)) + return true; +#else + return false; +#endif +} + +const bool MacroAssemblerARM::s_isVFPPresent = isVFPPresent(); + +#if CPU(ARMV5_OR_LOWER) +/* On ARMv5 and below, natural alignment is required. */ +void MacroAssemblerARM::load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) +{ + ARMWord op2; + + ASSERT(address.scale >= 0 && address.scale <= 3); + op2 = m_assembler.lsl(address.index, static_cast(address.scale)); + + if (address.offset >= 0 && address.offset + 0x2 <= 0xff) { + m_assembler.add(ARMRegisters::S0, address.base, op2); + m_assembler.halfDtrUp(ARMAssembler::LoadUint16, dest, ARMRegisters::S0, ARMAssembler::getOp2Half(address.offset)); + m_assembler.halfDtrUp(ARMAssembler::LoadUint16, ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Half(address.offset + 0x2)); + } else if (address.offset < 0 && address.offset >= -0xff) { + m_assembler.add(ARMRegisters::S0, address.base, op2); + m_assembler.halfDtrDown(ARMAssembler::LoadUint16, dest, ARMRegisters::S0, ARMAssembler::getOp2Half(-address.offset)); + m_assembler.halfDtrDown(ARMAssembler::LoadUint16, ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Half(-address.offset - 0x2)); + } else { + m_assembler.moveImm(address.offset, ARMRegisters::S0); + m_assembler.add(ARMRegisters::S0, ARMRegisters::S0, op2); + m_assembler.halfDtrUpRegister(ARMAssembler::LoadUint16, dest, address.base, ARMRegisters::S0); + m_assembler.add(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::Op2Immediate | 0x2); + m_assembler.halfDtrUpRegister(ARMAssembler::LoadUint16, ARMRegisters::S0, address.base, ARMRegisters::S0); + } + m_assembler.orr(dest, dest, m_assembler.lsl(ARMRegisters::S0, 16)); +} +#endif + +} + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) diff --git a/3rdparty/masm/assembler/MacroAssemblerARM.h b/3rdparty/masm/assembler/MacroAssemblerARM.h new file mode 100644 index 0000000000..527126b438 --- /dev/null +++ b/3rdparty/masm/assembler/MacroAssemblerARM.h @@ -0,0 +1,1383 @@ +/* + * Copyright (C) 2008 Apple Inc. + * Copyright (C) 2009, 2010 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerARM_h +#define MacroAssemblerARM_h + +#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#include "ARMAssembler.h" +#include "AbstractMacroAssembler.h" + +namespace JSC { + +class MacroAssemblerARM : public AbstractMacroAssembler { + static const int DoubleConditionMask = 0x0f; + static const int DoubleConditionBitSpecial = 0x10; + COMPILE_ASSERT(!(DoubleConditionBitSpecial & DoubleConditionMask), DoubleConditionBitSpecial_should_not_interfere_with_ARMAssembler_Condition_codes); +public: + typedef ARMRegisters::FPRegisterID FPRegisterID; + + enum RelationalCondition { + Equal = ARMAssembler::EQ, + NotEqual = ARMAssembler::NE, + Above = ARMAssembler::HI, + AboveOrEqual = ARMAssembler::CS, + Below = ARMAssembler::CC, + BelowOrEqual = ARMAssembler::LS, + GreaterThan = ARMAssembler::GT, + GreaterThanOrEqual = ARMAssembler::GE, + LessThan = ARMAssembler::LT, + LessThanOrEqual = ARMAssembler::LE + }; + + enum ResultCondition { + Overflow = ARMAssembler::VS, + Signed = ARMAssembler::MI, + Zero = ARMAssembler::EQ, + NonZero = ARMAssembler::NE + }; + + enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleEqual = ARMAssembler::EQ, + DoubleNotEqual = ARMAssembler::NE | DoubleConditionBitSpecial, + DoubleGreaterThan = ARMAssembler::GT, + DoubleGreaterThanOrEqual = ARMAssembler::GE, + DoubleLessThan = ARMAssembler::CC, + DoubleLessThanOrEqual = ARMAssembler::LS, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = ARMAssembler::EQ | DoubleConditionBitSpecial, + DoubleNotEqualOrUnordered = ARMAssembler::NE, + DoubleGreaterThanOrUnordered = ARMAssembler::HI, + DoubleGreaterThanOrEqualOrUnordered = ARMAssembler::CS, + DoubleLessThanOrUnordered = ARMAssembler::LT, + DoubleLessThanOrEqualOrUnordered = ARMAssembler::LE, + }; + + static const RegisterID stackPointerRegister = ARMRegisters::sp; + static const RegisterID linkRegister = ARMRegisters::lr; + + static const Scale ScalePtr = TimesFour; + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.adds(dest, dest, src); + } + + void add32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.adds(dest, op1, op2); + } + + void add32(TrustedImm32 imm, Address address) + { + load32(address, ARMRegisters::S1); + add32(imm, ARMRegisters::S1); + store32(ARMRegisters::S1, address); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.adds(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void add32(AbsoluteAddress src, RegisterID dest) + { + move(TrustedImmPtr(src.m_ptr), ARMRegisters::S1); + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); + add32(ARMRegisters::S1, dest); + } + + void add32(Address src, RegisterID dest) + { + load32(src, ARMRegisters::S1); + add32(ARMRegisters::S1, dest); + } + + void add32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.adds(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void and32(RegisterID src, RegisterID dest) + { + m_assembler.bitAnds(dest, dest, src); + } + + void and32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.bitAnds(dest, op1, op2); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + ARMWord w = m_assembler.getImm(imm.m_value, ARMRegisters::S0, true); + if (w & ARMAssembler::Op2InvertedImmediate) + m_assembler.bics(dest, dest, w & ~ARMAssembler::Op2InvertedImmediate); + else + m_assembler.bitAnds(dest, dest, w); + } + + void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ARMWord w = m_assembler.getImm(imm.m_value, ARMRegisters::S0, true); + if (w & ARMAssembler::Op2InvertedImmediate) + m_assembler.bics(dest, src, w & ~ARMAssembler::Op2InvertedImmediate); + else + m_assembler.bitAnds(dest, src, w); + } + + void lshift32(RegisterID shiftAmount, RegisterID dest) + { + lshift32(dest, shiftAmount, dest); + } + + void lshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + ARMWord w = ARMAssembler::getOp2Byte(0x1f); + m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); + + m_assembler.movs(dest, m_assembler.lslRegister(src, ARMRegisters::S0)); + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.lsl(dest, imm.m_value & 0x1f)); + } + + void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.lsl(src, imm.m_value & 0x1f)); + } + + void mul32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op2 == dest) { + if (op1 == dest) { + move(op2, ARMRegisters::S0); + op2 = ARMRegisters::S0; + } else { + // Swap the operands. + RegisterID tmp = op1; + op1 = op2; + op2 = tmp; + } + } + m_assembler.muls(dest, op1, op2); + } + + void mul32(RegisterID src, RegisterID dest) + { + mul32(src, dest, dest); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(imm, ARMRegisters::S0); + m_assembler.muls(dest, src, ARMRegisters::S0); + } + + void neg32(RegisterID srcDest) + { + m_assembler.rsbs(srcDest, srcDest, ARMAssembler::getOp2Byte(0)); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orrs(dest, dest, src); + } + + void or32(RegisterID src, AbsoluteAddress dest) + { + move(TrustedImmPtr(dest.m_ptr), ARMRegisters::S0); + load32(Address(ARMRegisters::S0), ARMRegisters::S1); + or32(src, ARMRegisters::S1); + store32(ARMRegisters::S1, ARMRegisters::S0); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.orrs(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.orrs(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.orrs(dest, op1, op2); + } + + void rshift32(RegisterID shiftAmount, RegisterID dest) + { + rshift32(dest, shiftAmount, dest); + } + + void rshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + ARMWord w = ARMAssembler::getOp2Byte(0x1f); + m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); + + m_assembler.movs(dest, m_assembler.asrRegister(src, ARMRegisters::S0)); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + rshift32(dest, imm, dest); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.asr(src, imm.m_value & 0x1f)); + } + + void urshift32(RegisterID shiftAmount, RegisterID dest) + { + urshift32(dest, shiftAmount, dest); + } + + void urshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + ARMWord w = ARMAssembler::getOp2Byte(0x1f); + m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); + + m_assembler.movs(dest, m_assembler.lsrRegister(src, ARMRegisters::S0)); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.lsr(dest, imm.m_value & 0x1f)); + } + + void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.lsr(src, imm.m_value & 0x1f)); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.subs(dest, dest, src); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.subs(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void sub32(TrustedImm32 imm, Address address) + { + load32(address, ARMRegisters::S1); + sub32(imm, ARMRegisters::S1); + store32(ARMRegisters::S1, address); + } + + void sub32(Address src, RegisterID dest) + { + load32(src, ARMRegisters::S1); + sub32(ARMRegisters::S1, dest); + } + + void sub32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.subs(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void xor32(RegisterID src, RegisterID dest) + { + m_assembler.eors(dest, dest, src); + } + + void xor32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.eors(dest, op1, op2); + } + + void xor32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value == -1) + m_assembler.mvns(dest, dest); + else + m_assembler.eors(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (imm.m_value == -1) + m_assembler.mvns(dest, src); + else + m_assembler.eors(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void countLeadingZeros32(RegisterID src, RegisterID dest) + { +#if WTF_ARM_ARCH_AT_LEAST(5) + m_assembler.clz(dest, src); +#else + UNUSED_PARAM(src); + UNUSED_PARAM(dest); + ASSERT_NOT_REACHED(); +#endif + } + + void load8(ImplicitAddress address, RegisterID dest) + { + m_assembler.dataTransfer32(ARMAssembler::LoadUint8, dest, address.base, address.offset); + } + + void load8(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer32(ARMAssembler::LoadUint8, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void load8Signed(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer16(ARMAssembler::LoadInt8, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void load16(ImplicitAddress address, RegisterID dest) + { + m_assembler.dataTransfer16(ARMAssembler::LoadUint16, dest, address.base, address.offset); + } + + void load16(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer16(ARMAssembler::LoadUint16, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void load16Signed(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer16(ARMAssembler::LoadInt16, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void load32(ImplicitAddress address, RegisterID dest) + { + m_assembler.dataTransfer32(ARMAssembler::LoadUint32, dest, address.base, address.offset); + } + + void load32(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer32(ARMAssembler::LoadUint32, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + +#if CPU(ARMV5_OR_LOWER) + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest); +#else + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + load32(address, dest); + } +#endif + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + load16(address, dest); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result(this); + ASSERT(address.offset >= 0 && address.offset <= 255); + m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, address.base, address.offset); + return result; + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabel32 dataLabel(this); + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, 0); + m_assembler.dtrUpRegister(ARMAssembler::LoadUint32, dest, address.base, ARMRegisters::S0); + return dataLabel; + } + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return value >= -4095 && value <= 4095; + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabelCompact dataLabel(this); + ASSERT(isCompactPtrAlignedAddressOffset(address.offset)); + if (address.offset >= 0) + m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, address.base, address.offset); + else + m_assembler.dtrDown(ARMAssembler::LoadUint32, dest, address.base, address.offset); + return dataLabel; + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + DataLabel32 dataLabel(this); + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, 0); + m_assembler.dtrUpRegister(ARMAssembler::StoreUint32, src, address.base, ARMRegisters::S0); + return dataLabel; + } + + void store8(RegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint8, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void store8(TrustedImm32 imm, void* address) + { + move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); + m_assembler.moveImm(imm.m_value, ARMRegisters::S1); + m_assembler.dtrUp(ARMAssembler::StoreUint8, ARMRegisters::S1, ARMRegisters::S0, 0); + } + + void store16(RegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransfer16(ARMAssembler::StoreUint16, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void store32(RegisterID src, ImplicitAddress address) + { + m_assembler.dataTransfer32(ARMAssembler::StoreUint32, src, address.base, address.offset); + } + + void store32(RegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint32, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + move(imm, ARMRegisters::S1); + store32(ARMRegisters::S1, address); + } + + void store32(TrustedImm32 imm, BaseIndex address) + { + move(imm, ARMRegisters::S1); + m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint32, ARMRegisters::S1, address.base, address.index, static_cast(address.scale), address.offset); + } + + void store32(RegisterID src, void* address) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); + m_assembler.dtrUp(ARMAssembler::StoreUint32, src, ARMRegisters::S0, 0); + } + + void store32(TrustedImm32 imm, void* address) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); + m_assembler.moveImm(imm.m_value, ARMRegisters::S1); + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); + } + + void pop(RegisterID dest) + { + m_assembler.pop(dest); + } + + void push(RegisterID src) + { + m_assembler.push(src); + } + + void push(Address address) + { + load32(address, ARMRegisters::S1); + push(ARMRegisters::S1); + } + + void push(TrustedImm32 imm) + { + move(imm, ARMRegisters::S0); + push(ARMRegisters::S0); + } + + void move(TrustedImm32 imm, RegisterID dest) + { + m_assembler.moveImm(imm.m_value, dest); + } + + void move(RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.mov(dest, src); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + move(TrustedImm32(imm), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + m_assembler.mov(ARMRegisters::S0, reg1); + m_assembler.mov(reg1, reg2); + m_assembler.mov(reg2, ARMRegisters::S0); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest) + move(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest) + move(src, dest); + } + + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + load8(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(right.m_value & 0xFFFFFF00)); + load8(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right, int useConstantPool = 0) + { + m_assembler.cmp(left, right); + return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right, int useConstantPool = 0) + { + internalCompare32(left, right); + return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + load32(right, ARMRegisters::S1); + return branch32(cond, left, ARMRegisters::S1); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + load32(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + load32(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + load32(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + load32WithUnalignedHalfWords(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + load8(address, ARMRegisters::S1); + return branchTest32(cond, ARMRegisters::S1, mask); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); + load8(Address(ARMRegisters::S1), ARMRegisters::S1); + return branchTest32(cond, ARMRegisters::S1, mask); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + ASSERT((cond == Zero) || (cond == NonZero)); + m_assembler.tst(reg, mask); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + ARMWord w = m_assembler.getImm(mask.m_value, ARMRegisters::S0, true); + if (w & ARMAssembler::Op2InvertedImmediate) + m_assembler.bics(ARMRegisters::S0, reg, w & ~ARMAssembler::Op2InvertedImmediate); + else + m_assembler.tst(reg, w); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + load32(address, ARMRegisters::S1); + return branchTest32(cond, ARMRegisters::S1, mask); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + load32(address, ARMRegisters::S1); + return branchTest32(cond, ARMRegisters::S1, mask); + } + + Jump jump() + { + return Jump(m_assembler.jmp()); + } + + void jump(RegisterID target) + { + m_assembler.bx(target); + } + + void jump(Address address) + { + load32(address, ARMRegisters::pc); + } + + void jump(AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), ARMRegisters::S0); + load32(Address(ARMRegisters::S0, 0), ARMRegisters::pc); + } + + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + m_assembler.vmov(dest1, dest2, src); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + UNUSED_PARAM(scratch); + m_assembler.vmov(dest, src1, src2); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(src, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchAdd32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(op1, op2, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(src, imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + void mull32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op2 == dest) { + if (op1 == dest) { + move(op2, ARMRegisters::S0); + op2 = ARMRegisters::S0; + } else { + // Swap the operands. + RegisterID tmp = op1; + op1 = op2; + op2 = tmp; + } + } + m_assembler.mull(ARMRegisters::S1, dest, op1, op2); + m_assembler.cmp(ARMRegisters::S1, m_assembler.asr(dest, 31)); + } + + Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + mull32(src1, src2, dest); + cond = NonZero; + } + else + mul32(src1, src2, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchMul32(cond, src, dest, dest); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + move(imm, ARMRegisters::S0); + mull32(ARMRegisters::S0, src, dest); + cond = NonZero; + } + else + mul32(imm, src, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + sub32(src, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + sub32(imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + sub32(src, imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + m_assembler.subs(dest, op1, op2); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchNeg32(ResultCondition cond, RegisterID srcDest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + neg32(srcDest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); + or32(src, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) + { + internalCompare32(reg, imm); + Jump jump(m_assembler.loadBranchTarget(ARMRegisters::S1, ARMCondition(cond), true)); + m_assembler.bx(ARMRegisters::S1, ARMCondition(cond)); + return PatchableJump(jump); + } + + void breakpoint() + { + m_assembler.bkpt(0); + } + + Call nearCall() + { + m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true); + return Call(m_assembler.blx(ARMRegisters::S1), Call::LinkableNear); + } + + Call call(RegisterID target) + { + return Call(m_assembler.blx(target), Call::None); + } + + void call(Address address) + { + call32(address.base, address.offset); + } + + void ret() + { + m_assembler.bx(linkRegister); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmp(left, right); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + m_assembler.cmp(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + load8(left, ARMRegisters::S1); + compare32(cond, ARMRegisters::S1, right, dest); + } + + void test32(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) + { + if (mask.m_value == -1) + m_assembler.cmp(0, reg); + else + m_assembler.tst(reg, m_assembler.getImm(mask.m_value, ARMRegisters::S0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); + } + + void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + load32(address, ARMRegisters::S1); + test32(cond, ARMRegisters::S1, mask, dest); + } + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + load8(address, ARMRegisters::S1); + test32(cond, ARMRegisters::S1, mask, dest); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.add(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S1, reinterpret_cast(address.m_ptr)); + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); + add32(imm, ARMRegisters::S1); + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address.m_ptr)); + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + ARMWord tmp; + + move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S0, ARMRegisters::S1, 0); + + if ((tmp = ARMAssembler::getOp2(imm.m_value)) != ARMAssembler::InvalidImmediate) + m_assembler.adds(ARMRegisters::S0, ARMRegisters::S0, tmp); + else if ((tmp = ARMAssembler::getOp2(-imm.m_value)) != ARMAssembler::InvalidImmediate) + m_assembler.subs(ARMRegisters::S0, ARMRegisters::S0, tmp); + else { + m_assembler.adds(ARMRegisters::S0, ARMRegisters::S0, m_assembler.getImm(imm.m_value, ARMRegisters::S1)); + move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); + } + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S0, ARMRegisters::S1, 0); + + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S0, ARMRegisters::S1, sizeof(ARMWord)); + if (imm.m_value >= 0) + m_assembler.adc(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + else + m_assembler.sbc(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S0, ARMRegisters::S1, sizeof(ARMWord)); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S1, reinterpret_cast(address.m_ptr)); + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); + sub32(imm, ARMRegisters::S1); + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address.m_ptr)); + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); + } + + void load32(const void* address, RegisterID dest) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); + m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, ARMRegisters::S0, 0); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + load32(left.m_ptr, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + load32(left.m_ptr, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + void relativeTableJump(RegisterID index, int scale) + { + ASSERT(scale >= 0 && scale <= 31); + m_assembler.add(ARMRegisters::pc, ARMRegisters::pc, m_assembler.lsl(index, scale)); + + // NOP the default prefetching + m_assembler.mov(ARMRegisters::r0, ARMRegisters::r0); + } + + Call call() + { + ensureSpace(2 * sizeof(ARMWord), sizeof(ARMWord)); + m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true); + return Call(m_assembler.blx(ARMRegisters::S1), Call::Linkable); + } + + Call tailRecursiveCall() + { + return Call::fromTailJump(jump()); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + return Call::fromTailJump(oldJump); + } + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + DataLabelPtr dataLabel(this); + m_assembler.ldrUniqueImmediate(dest, reinterpret_cast(initialValue.m_value)); + return dataLabel; + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S1); + Jump jump = branch32(cond, left, ARMRegisters::S1, true); + return jump; + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + load32(left, ARMRegisters::S1); + dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S0); + Jump jump = branch32(cond, ARMRegisters::S0, ARMRegisters::S1, true); + return jump; + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + DataLabelPtr dataLabel = moveWithPatch(initialValue, ARMRegisters::S1); + store32(ARMRegisters::S1, address); + return dataLabel; + } + + DataLabelPtr storePtrWithPatch(ImplicitAddress address) + { + return storePtrWithPatch(TrustedImmPtr(0), address); + } + + // Floating point operators + static bool supportsFloatingPoint() + { + return s_isVFPPresent; + } + + static bool supportsFloatingPointTruncate() + { + return false; + } + + static bool supportsFloatingPointSqrt() + { + return s_isVFPPresent; + } + static bool supportsFloatingPointAbs() { return false; } + + void loadFloat(BaseIndex address, FPRegisterID dest) + { + m_assembler.baseIndexTransferFloat(ARMAssembler::LoadFloat, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { + m_assembler.dataTransferFloat(ARMAssembler::LoadDouble, dest, address.base, address.offset); + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { + m_assembler.baseIndexTransferFloat(ARMAssembler::LoadDouble, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void loadDouble(const void* address, FPRegisterID dest) + { + move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); + m_assembler.doubleDtrUp(ARMAssembler::LoadDouble, dest, ARMRegisters::S0, 0); + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransferFloat(ARMAssembler::StoreFloat, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { + m_assembler.dataTransferFloat(ARMAssembler::StoreDouble, src, address.base, address.offset); + } + + void storeDouble(FPRegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransferFloat(ARMAssembler::StoreDouble, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void storeDouble(FPRegisterID src, const void* address) + { + move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); + m_assembler.dataTransferFloat(ARMAssembler::StoreDouble, src, ARMRegisters::S0, 0); + } + + void moveDouble(FPRegisterID src, FPRegisterID dest) + { + if (src != dest) + m_assembler.vmov_f64(dest, src); + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vadd_f64(dest, dest, src); + } + + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vadd_f64(dest, op1, op2); + } + + void addDouble(Address src, FPRegisterID dest) + { + loadDouble(src, ARMRegisters::SD0); + addDouble(ARMRegisters::SD0, dest); + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + loadDouble(address.m_ptr, ARMRegisters::SD0); + addDouble(ARMRegisters::SD0, dest); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vdiv_f64(dest, dest, src); + } + + void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vdiv_f64(dest, op1, op2); + } + + void divDouble(Address src, FPRegisterID dest) + { + ASSERT_NOT_REACHED(); // Untested + loadDouble(src, ARMRegisters::SD0); + divDouble(ARMRegisters::SD0, dest); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vsub_f64(dest, dest, src); + } + + void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vsub_f64(dest, op1, op2); + } + + void subDouble(Address src, FPRegisterID dest) + { + loadDouble(src, ARMRegisters::SD0); + subDouble(ARMRegisters::SD0, dest); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vmul_f64(dest, dest, src); + } + + void mulDouble(Address src, FPRegisterID dest) + { + loadDouble(src, ARMRegisters::SD0); + mulDouble(ARMRegisters::SD0, dest); + } + + void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vmul_f64(dest, op1, op2); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vsqrt_f64(dest, src); + } + + void absDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vabs_f64(dest, src); + } + + void negateDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vneg_f64(dest, src); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.vmov_vfp32(dest << 1, src); + m_assembler.vcvt_f64_s32(dest, dest << 1); + } + + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + load32(src, ARMRegisters::S1); + convertInt32ToDouble(ARMRegisters::S1, dest); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + move(TrustedImmPtr(src.m_ptr), ARMRegisters::S1); + load32(Address(ARMRegisters::S1), ARMRegisters::S1); + convertInt32ToDouble(ARMRegisters::S1, dest); + } + + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.vcvt_f64_f32(dst, src); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.vcvt_f32_f64(dst, src); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + m_assembler.vcmp_f64(left, right); + m_assembler.vmrs_apsr(); + if (cond & DoubleConditionBitSpecial) + m_assembler.cmp(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::VS); + return Jump(m_assembler.jmp(static_cast(cond & ~DoubleConditionMask))); + } + + // Truncates 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, INT_MIN). + enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + truncateDoubleToInt32(src, dest); + + m_assembler.add(ARMRegisters::S0, dest, ARMAssembler::getOp2Byte(1)); + m_assembler.bic(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(1)); + + ARMWord w = ARMAssembler::getOp2(0x80000000); + ASSERT(w != ARMAssembler::InvalidImmediate); + m_assembler.cmp(ARMRegisters::S0, w); + return Jump(m_assembler.jmp(branchType == BranchIfTruncateFailed ? ARMAssembler::EQ : ARMAssembler::NE)); + } + + Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + truncateDoubleToUint32(src, dest); + + m_assembler.add(ARMRegisters::S0, dest, ARMAssembler::getOp2Byte(1)); + m_assembler.bic(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(1)); + + m_assembler.cmp(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + return Jump(m_assembler.jmp(branchType == BranchIfTruncateFailed ? ARMAssembler::EQ : ARMAssembler::NE)); + } + + // Result is undefined if the value is outside of the integer range. + void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + m_assembler.vcvt_s32_f64(ARMRegisters::SD0 << 1, src); + m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); + } + + void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) + { + m_assembler.vcvt_u32_f64(ARMRegisters::SD0 << 1, src); + m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); + } + + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + m_assembler.vcvt_s32_f64(ARMRegisters::SD0 << 1, src); + m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + m_assembler.vcvt_f64_s32(ARMRegisters::SD0, ARMRegisters::SD0 << 1); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, ARMRegisters::SD0)); + + // If the result is zero, it might have been -0.0, and 0.0 equals to -0.0 + failureCases.append(branchTest32(Zero, dest)); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) + { + m_assembler.mov(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + convertInt32ToDouble(ARMRegisters::S0, scratch); + return branchDouble(DoubleNotEqual, reg, scratch); + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) + { + m_assembler.mov(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + convertInt32ToDouble(ARMRegisters::S0, scratch); + return branchDouble(DoubleEqualOrUnordered, reg, scratch); + } + + // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. + static RelationalCondition invert(RelationalCondition cond) + { + ASSERT((static_cast(cond & 0x0fffffff)) == 0 && static_cast(cond) < static_cast(ARMAssembler::AL)); + return static_cast(cond ^ 0x10000000); + } + + void nop() + { + m_assembler.nop(); + } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(reinterpret_cast(ARMAssembler::readCallTarget(call.dataLocation()))); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + ARMAssembler::replaceWithJump(instructionStart.dataLocation(), destination.dataLocation()); + } + + static ptrdiff_t maxJumpReplacementSize() + { + ARMAssembler::maxJumpReplacementSize(); + return 0; + } + + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + return label.labelAtOffset(0); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID reg, void* initialValue) + { + ARMAssembler::revertJump(instructionStart.dataLocation(), reg, reinterpret_cast(initialValue) & 0xffff); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + UNREACHABLE_FOR_PLATFORM(); + } + +protected: + ARMAssembler::Condition ARMCondition(RelationalCondition cond) + { + return static_cast(cond); + } + + ARMAssembler::Condition ARMCondition(ResultCondition cond) + { + return static_cast(cond); + } + + void ensureSpace(int insnSpace, int constSpace) + { + m_assembler.ensureSpace(insnSpace, constSpace); + } + + int sizeOfConstantPool() + { + return m_assembler.sizeOfConstantPool(); + } + + void call32(RegisterID base, int32_t offset) + { + load32(Address(base, offset), ARMRegisters::S1); + m_assembler.blx(ARMRegisters::S1); + } + +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + void internalCompare32(RegisterID left, TrustedImm32 right) + { + ARMWord tmp = (static_cast(right.m_value) == 0x80000000) ? ARMAssembler::InvalidImmediate : m_assembler.getOp2(-right.m_value); + if (tmp != ARMAssembler::InvalidImmediate) + m_assembler.cmn(left, tmp); + else + m_assembler.cmp(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); + } + + static void linkCall(void* code, Call call, FunctionPtr function) + { + ARMAssembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + ARMAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + ARMAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static const bool s_isVFPPresent; +}; + +} + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#endif // MacroAssemblerARM_h diff --git a/3rdparty/masm/assembler/MacroAssemblerARMv7.h b/3rdparty/masm/assembler/MacroAssemblerARMv7.h new file mode 100644 index 0000000000..8d7a3a69aa --- /dev/null +++ b/3rdparty/masm/assembler/MacroAssemblerARMv7.h @@ -0,0 +1,1903 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerARMv7_h +#define MacroAssemblerARMv7_h + +#if ENABLE(ASSEMBLER) + +#include "ARMv7Assembler.h" +#include "AbstractMacroAssembler.h" + +namespace JSC { + +class MacroAssemblerARMv7 : public AbstractMacroAssembler { + // FIXME: switch dataTempRegister & addressTempRegister, or possibly use r7? + // - dTR is likely used more than aTR, and we'll get better instruction + // encoding if it's in the low 8 registers. + static const RegisterID dataTempRegister = ARMRegisters::ip; + static const RegisterID addressTempRegister = ARMRegisters::r3; + + static const ARMRegisters::FPDoubleRegisterID fpTempRegister = ARMRegisters::d7; + inline ARMRegisters::FPSingleRegisterID fpTempRegisterAsSingle() { return ARMRegisters::asSingle(fpTempRegister); } + +public: + MacroAssemblerARMv7() + : m_makeJumpPatchable(false) + { + } + + typedef ARMv7Assembler::LinkRecord LinkRecord; + typedef ARMv7Assembler::JumpType JumpType; + typedef ARMv7Assembler::JumpLinkType JumpLinkType; + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return value >= -255 && value <= 255; + } + + Vector& jumpsToLink() { return m_assembler.jumpsToLink(); } + void* unlinkedCode() { return m_assembler.unlinkedCode(); } + bool canCompact(JumpType jumpType) { return m_assembler.canCompact(jumpType); } + JumpLinkType computeJumpType(JumpType jumpType, const uint8_t* from, const uint8_t* to) { return m_assembler.computeJumpType(jumpType, from, to); } + JumpLinkType computeJumpType(LinkRecord& record, const uint8_t* from, const uint8_t* to) { return m_assembler.computeJumpType(record, from, to); } + void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset) {return m_assembler.recordLinkOffsets(regionStart, regionEnd, offset); } + int jumpSizeDelta(JumpType jumpType, JumpLinkType jumpLinkType) { return m_assembler.jumpSizeDelta(jumpType, jumpLinkType); } + void link(LinkRecord& record, uint8_t* from, uint8_t* to) { return m_assembler.link(record, from, to); } + + struct ArmAddress { + enum AddressType { + HasOffset, + HasIndex, + } type; + RegisterID base; + union { + int32_t offset; + struct { + RegisterID index; + Scale scale; + }; + } u; + + explicit ArmAddress(RegisterID base, int32_t offset = 0) + : type(HasOffset) + , base(base) + { + u.offset = offset; + } + + explicit ArmAddress(RegisterID base, RegisterID index, Scale scale = TimesOne) + : type(HasIndex) + , base(base) + { + u.index = index; + u.scale = scale; + } + }; + +public: + typedef ARMRegisters::FPDoubleRegisterID FPRegisterID; + + static const Scale ScalePtr = TimesFour; + + enum RelationalCondition { + Equal = ARMv7Assembler::ConditionEQ, + NotEqual = ARMv7Assembler::ConditionNE, + Above = ARMv7Assembler::ConditionHI, + AboveOrEqual = ARMv7Assembler::ConditionHS, + Below = ARMv7Assembler::ConditionLO, + BelowOrEqual = ARMv7Assembler::ConditionLS, + GreaterThan = ARMv7Assembler::ConditionGT, + GreaterThanOrEqual = ARMv7Assembler::ConditionGE, + LessThan = ARMv7Assembler::ConditionLT, + LessThanOrEqual = ARMv7Assembler::ConditionLE + }; + + enum ResultCondition { + Overflow = ARMv7Assembler::ConditionVS, + Signed = ARMv7Assembler::ConditionMI, + Zero = ARMv7Assembler::ConditionEQ, + NonZero = ARMv7Assembler::ConditionNE + }; + + enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleEqual = ARMv7Assembler::ConditionEQ, + DoubleNotEqual = ARMv7Assembler::ConditionVC, // Not the right flag! check for this & handle differently. + DoubleGreaterThan = ARMv7Assembler::ConditionGT, + DoubleGreaterThanOrEqual = ARMv7Assembler::ConditionGE, + DoubleLessThan = ARMv7Assembler::ConditionLO, + DoubleLessThanOrEqual = ARMv7Assembler::ConditionLS, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = ARMv7Assembler::ConditionVS, // Not the right flag! check for this & handle differently. + DoubleNotEqualOrUnordered = ARMv7Assembler::ConditionNE, + DoubleGreaterThanOrUnordered = ARMv7Assembler::ConditionHI, + DoubleGreaterThanOrEqualOrUnordered = ARMv7Assembler::ConditionHS, + DoubleLessThanOrUnordered = ARMv7Assembler::ConditionLT, + DoubleLessThanOrEqualOrUnordered = ARMv7Assembler::ConditionLE, + }; + + static const RegisterID stackPointerRegister = ARMRegisters::sp; + static const RegisterID linkRegister = ARMRegisters::lr; + + // Integer arithmetic operations: + // + // Operations are typically two operand - operation(source, srcDst) + // For many operations the source may be an TrustedImm32, the srcDst operand + // may often be a memory location (explictly described using an Address + // object). + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.add(dest, dest, src); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + add32(imm, dest, dest); + } + + void add32(AbsoluteAddress src, RegisterID dest) + { + load32(src.m_ptr, dataTempRegister); + add32(dataTempRegister, dest); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add(dest, src, armImm); + else { + move(imm, dataTempRegister); + m_assembler.add(dest, src, dataTempRegister); + } + } + + void add32(TrustedImm32 imm, Address address) + { + load32(address, dataTempRegister); + + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add(dataTempRegister, dataTempRegister, armImm); + else { + // Hrrrm, since dataTempRegister holds the data loaded, + // use addressTempRegister to hold the immediate. + move(imm, addressTempRegister); + m_assembler.add(dataTempRegister, dataTempRegister, addressTempRegister); + } + + store32(dataTempRegister, address); + } + + void add32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + add32(dataTempRegister, dest); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + load32(address.m_ptr, dataTempRegister); + + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add(dataTempRegister, dataTempRegister, armImm); + else { + // Hrrrm, since dataTempRegister holds the data loaded, + // use addressTempRegister to hold the immediate. + move(imm, addressTempRegister); + m_assembler.add(dataTempRegister, dataTempRegister, addressTempRegister); + } + + store32(dataTempRegister, address.m_ptr); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), addressTempRegister); + + m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(0)); + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add_S(dataTempRegister, dataTempRegister, armImm); + else { + move(imm, addressTempRegister); + m_assembler.add_S(dataTempRegister, dataTempRegister, addressTempRegister); + move(TrustedImmPtr(address.m_ptr), addressTempRegister); + } + m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(0)); + + m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(4)); + m_assembler.adc(dataTempRegister, dataTempRegister, ARMThumbImmediate::makeEncodedImm(imm.m_value >> 31)); + m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(4)); + } + + void and32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.ARM_and(dest, op1, op2); + } + + void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.ARM_and(dest, src, armImm); + else { + move(imm, dataTempRegister); + m_assembler.ARM_and(dest, src, dataTempRegister); + } + } + + void and32(RegisterID src, RegisterID dest) + { + and32(dest, src, dest); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + and32(imm, dest, dest); + } + + void countLeadingZeros32(RegisterID src, RegisterID dest) + { + m_assembler.clz(dest, src); + } + + void lshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + // Clamp the shift to the range 0..31 + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); + ASSERT(armImm.isValid()); + m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); + + m_assembler.lsl(dest, src, dataTempRegister); + } + + void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.lsl(dest, src, imm.m_value & 0x1f); + } + + void lshift32(RegisterID shiftAmount, RegisterID dest) + { + lshift32(dest, shiftAmount, dest); + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + lshift32(dest, imm, dest); + } + + void mul32(RegisterID src, RegisterID dest) + { + m_assembler.smull(dest, dataTempRegister, dest, src); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(imm, dataTempRegister); + m_assembler.smull(dest, dataTempRegister, src, dataTempRegister); + } + + void neg32(RegisterID srcDest) + { + m_assembler.neg(srcDest, srcDest); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orr(dest, dest, src); + } + + void or32(RegisterID src, AbsoluteAddress dest) + { + move(TrustedImmPtr(dest.m_ptr), addressTempRegister); + load32(addressTempRegister, dataTempRegister); + or32(src, dataTempRegister); + store32(dataTempRegister, addressTempRegister); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + or32(imm, dest, dest); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.orr(dest, op1, op2); + } + + void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.orr(dest, src, armImm); + else { + move(imm, dataTempRegister); + m_assembler.orr(dest, src, dataTempRegister); + } + } + + void rshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + // Clamp the shift to the range 0..31 + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); + ASSERT(armImm.isValid()); + m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); + + m_assembler.asr(dest, src, dataTempRegister); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.asr(dest, src, imm.m_value & 0x1f); + } + + void rshift32(RegisterID shiftAmount, RegisterID dest) + { + rshift32(dest, shiftAmount, dest); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + rshift32(dest, imm, dest); + } + + void urshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + // Clamp the shift to the range 0..31 + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); + ASSERT(armImm.isValid()); + m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); + + m_assembler.lsr(dest, src, dataTempRegister); + } + + void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.lsr(dest, src, imm.m_value & 0x1f); + } + + void urshift32(RegisterID shiftAmount, RegisterID dest) + { + urshift32(dest, shiftAmount, dest); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + urshift32(dest, imm, dest); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.sub(dest, dest, src); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.sub(dest, dest, armImm); + else { + move(imm, dataTempRegister); + m_assembler.sub(dest, dest, dataTempRegister); + } + } + + void sub32(TrustedImm32 imm, Address address) + { + load32(address, dataTempRegister); + + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.sub(dataTempRegister, dataTempRegister, armImm); + else { + // Hrrrm, since dataTempRegister holds the data loaded, + // use addressTempRegister to hold the immediate. + move(imm, addressTempRegister); + m_assembler.sub(dataTempRegister, dataTempRegister, addressTempRegister); + } + + store32(dataTempRegister, address); + } + + void sub32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + sub32(dataTempRegister, dest); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + load32(address.m_ptr, dataTempRegister); + + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.sub(dataTempRegister, dataTempRegister, armImm); + else { + // Hrrrm, since dataTempRegister holds the data loaded, + // use addressTempRegister to hold the immediate. + move(imm, addressTempRegister); + m_assembler.sub(dataTempRegister, dataTempRegister, addressTempRegister); + } + + store32(dataTempRegister, address.m_ptr); + } + + void xor32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.eor(dest, op1, op2); + } + + void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (imm.m_value == -1) { + m_assembler.mvn(dest, src); + return; + } + + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.eor(dest, src, armImm); + else { + move(imm, dataTempRegister); + m_assembler.eor(dest, src, dataTempRegister); + } + } + + void xor32(RegisterID src, RegisterID dest) + { + xor32(dest, src, dest); + } + + void xor32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value == -1) + m_assembler.mvn(dest, dest); + else + xor32(imm, dest, dest); + } + + + // Memory access operations: + // + // Loads are of the form load(address, destination) and stores of the form + // store(source, address). The source for a store may be an TrustedImm32. Address + // operand objects to loads and store will be implicitly constructed if a + // register is passed. + +private: + void load32(ArmAddress address, RegisterID dest) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.ldr(dest, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.ldr(dest, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.ldr(dest, address.base, address.u.offset, true, false); + } + } + + void load16(ArmAddress address, RegisterID dest) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.ldrh(dest, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.ldrh(dest, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.ldrh(dest, address.base, address.u.offset, true, false); + } + } + + void load16Signed(ArmAddress address, RegisterID dest) + { + ASSERT(address.type == ArmAddress::HasIndex); + m_assembler.ldrsh(dest, address.base, address.u.index, address.u.scale); + } + + void load8(ArmAddress address, RegisterID dest) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.ldrb(dest, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.ldrb(dest, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.ldrb(dest, address.base, address.u.offset, true, false); + } + } + + void load8Signed(ArmAddress address, RegisterID dest) + { + ASSERT(address.type == ArmAddress::HasIndex); + m_assembler.ldrsb(dest, address.base, address.u.index, address.u.scale); + } + +protected: + void store32(RegisterID src, ArmAddress address) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.str(src, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.str(src, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.str(src, address.base, address.u.offset, true, false); + } + } + +private: + void store8(RegisterID src, ArmAddress address) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.strb(src, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.strb(src, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.strb(src, address.base, address.u.offset, true, false); + } + } + + void store16(RegisterID src, ArmAddress address) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.strh(src, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.strh(src, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.strh(src, address.base, address.u.offset, true, false); + } + } + +public: + void load32(ImplicitAddress address, RegisterID dest) + { + load32(setupArmAddress(address), dest); + } + + void load32(BaseIndex address, RegisterID dest) + { + load32(setupArmAddress(address), dest); + } + + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + load32(setupArmAddress(address), dest); + } + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + load16(setupArmAddress(address), dest); + } + + void load32(const void* address, RegisterID dest) + { + move(TrustedImmPtr(address), addressTempRegister); + m_assembler.ldr(dest, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result(this); + ASSERT(address.offset >= 0 && address.offset <= 255); + m_assembler.ldrWide8BitImmediate(dest, address.base, address.offset); + return result; + } + + void load8(ImplicitAddress address, RegisterID dest) + { + load8(setupArmAddress(address), dest); + } + + void load8Signed(ImplicitAddress, RegisterID) + { + UNREACHABLE_FOR_PLATFORM(); + } + + void load8(BaseIndex address, RegisterID dest) + { + load8(setupArmAddress(address), dest); + } + + void load8Signed(BaseIndex address, RegisterID dest) + { + load8Signed(setupArmAddress(address), dest); + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabel32 label = moveWithPatch(TrustedImm32(address.offset), dataTempRegister); + load32(ArmAddress(address.base, dataTempRegister), dest); + return label; + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + + RegisterID base = address.base; + + DataLabelCompact label(this); + ASSERT(isCompactPtrAlignedAddressOffset(address.offset)); + + m_assembler.ldr(dest, base, address.offset, true, false); + return label; + } + + void load16(BaseIndex address, RegisterID dest) + { + m_assembler.ldrh(dest, makeBaseIndexBase(address), address.index, address.scale); + } + + void load16Signed(BaseIndex address, RegisterID dest) + { + load16Signed(setupArmAddress(address), dest); + } + + void load16(ImplicitAddress address, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.offset); + if (armImm.isValid()) + m_assembler.ldrh(dest, address.base, armImm); + else { + move(TrustedImm32(address.offset), dataTempRegister); + m_assembler.ldrh(dest, address.base, dataTempRegister); + } + } + + void load16Signed(ImplicitAddress, RegisterID) + { + UNREACHABLE_FOR_PLATFORM(); + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + DataLabel32 label = moveWithPatch(TrustedImm32(address.offset), dataTempRegister); + store32(src, ArmAddress(address.base, dataTempRegister)); + return label; + } + + void store32(RegisterID src, ImplicitAddress address) + { + store32(src, setupArmAddress(address)); + } + + void store32(RegisterID src, BaseIndex address) + { + store32(src, setupArmAddress(address)); + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + move(imm, dataTempRegister); + store32(dataTempRegister, setupArmAddress(address)); + } + + void store32(TrustedImm32 imm, BaseIndex address) + { + move(imm, dataTempRegister); + store32(dataTempRegister, setupArmAddress(address)); + } + + void store32(RegisterID src, const void* address) + { + move(TrustedImmPtr(address), addressTempRegister); + m_assembler.str(src, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); + } + + void store32(TrustedImm32 imm, const void* address) + { + move(imm, dataTempRegister); + store32(dataTempRegister, address); + } + + void store8(RegisterID src, BaseIndex address) + { + store8(src, setupArmAddress(address)); + } + + void store8(RegisterID src, void* address) + { + move(TrustedImmPtr(address), addressTempRegister); + store8(src, ArmAddress(addressTempRegister, 0)); + } + + void store8(TrustedImm32 imm, void* address) + { + move(imm, dataTempRegister); + store8(dataTempRegister, address); + } + + void store16(RegisterID src, BaseIndex address) + { + store16(src, setupArmAddress(address)); + } + + // Possibly clobbers src, but not on this architecture. + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + m_assembler.vmov(dest1, dest2, src); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + UNUSED_PARAM(scratch); + m_assembler.vmov(dest, src1, src2); + } + +#if ENABLE(JIT_CONSTANT_BLINDING) + static bool shouldBlindForSpecificArch(uint32_t value) + { + ARMThumbImmediate immediate = ARMThumbImmediate::makeEncodedImm(value); + + // Couldn't be encoded as an immediate, so assume it's untrusted. + if (!immediate.isValid()) + return true; + + // If we can encode the immediate, we have less than 16 attacker + // controlled bits. + if (immediate.isEncodedImm()) + return false; + + // Don't let any more than 12 bits of an instruction word + // be controlled by an attacker. + return !immediate.isUInt12(); + } +#endif + + // Floating-point operations: + + static bool supportsFloatingPoint() { return true; } + static bool supportsFloatingPointTruncate() { return true; } + static bool supportsFloatingPointSqrt() { return true; } + static bool supportsFloatingPointAbs() { return true; } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { + RegisterID base = address.base; + int32_t offset = address.offset; + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { + add32(TrustedImm32(offset), base, addressTempRegister); + base = addressTempRegister; + offset = 0; + } + + m_assembler.vldr(dest, base, offset); + } + + void loadFloat(ImplicitAddress address, FPRegisterID dest) + { + RegisterID base = address.base; + int32_t offset = address.offset; + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { + add32(TrustedImm32(offset), base, addressTempRegister); + base = addressTempRegister; + offset = 0; + } + + m_assembler.flds(ARMRegisters::asSingle(dest), base, offset); + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { + move(address.index, addressTempRegister); + lshift32(TrustedImm32(address.scale), addressTempRegister); + add32(address.base, addressTempRegister); + loadDouble(Address(addressTempRegister, address.offset), dest); + } + + void loadFloat(BaseIndex address, FPRegisterID dest) + { + move(address.index, addressTempRegister); + lshift32(TrustedImm32(address.scale), addressTempRegister); + add32(address.base, addressTempRegister); + loadFloat(Address(addressTempRegister, address.offset), dest); + } + + void moveDouble(FPRegisterID src, FPRegisterID dest) + { + if (src != dest) + m_assembler.vmov(dest, src); + } + + void loadDouble(const void* address, FPRegisterID dest) + { + move(TrustedImmPtr(address), addressTempRegister); + m_assembler.vldr(dest, addressTempRegister, 0); + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { + RegisterID base = address.base; + int32_t offset = address.offset; + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { + add32(TrustedImm32(offset), base, addressTempRegister); + base = addressTempRegister; + offset = 0; + } + + m_assembler.vstr(src, base, offset); + } + + void storeFloat(FPRegisterID src, ImplicitAddress address) + { + RegisterID base = address.base; + int32_t offset = address.offset; + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { + add32(TrustedImm32(offset), base, addressTempRegister); + base = addressTempRegister; + offset = 0; + } + + m_assembler.fsts(ARMRegisters::asSingle(src), base, offset); + } + + void storeDouble(FPRegisterID src, const void* address) + { + move(TrustedImmPtr(address), addressTempRegister); + storeDouble(src, addressTempRegister); + } + + void storeDouble(FPRegisterID src, BaseIndex address) + { + move(address.index, addressTempRegister); + lshift32(TrustedImm32(address.scale), addressTempRegister); + add32(address.base, addressTempRegister); + storeDouble(src, Address(addressTempRegister, address.offset)); + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + move(address.index, addressTempRegister); + lshift32(TrustedImm32(address.scale), addressTempRegister); + add32(address.base, addressTempRegister); + storeFloat(src, Address(addressTempRegister, address.offset)); + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vadd(dest, dest, src); + } + + void addDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + addDouble(fpTempRegister, dest); + } + + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vadd(dest, op1, op2); + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + loadDouble(address.m_ptr, fpTempRegister); + m_assembler.vadd(dest, dest, fpTempRegister); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vdiv(dest, dest, src); + } + + void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vdiv(dest, op1, op2); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vsub(dest, dest, src); + } + + void subDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + subDouble(fpTempRegister, dest); + } + + void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vsub(dest, op1, op2); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vmul(dest, dest, src); + } + + void mulDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + mulDouble(fpTempRegister, dest); + } + + void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vmul(dest, op1, op2); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vsqrt(dest, src); + } + + void absDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vabs(dest, src); + } + + void negateDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vneg(dest, src); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.vmov(fpTempRegister, src, src); + m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); + } + + void convertInt32ToDouble(Address address, FPRegisterID dest) + { + // Fixme: load directly into the fpr! + load32(address, dataTempRegister); + m_assembler.vmov(fpTempRegister, dataTempRegister, dataTempRegister); + m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); + } + + void convertInt32ToDouble(AbsoluteAddress address, FPRegisterID dest) + { + // Fixme: load directly into the fpr! + load32(address.m_ptr, dataTempRegister); + m_assembler.vmov(fpTempRegister, dataTempRegister, dataTempRegister); + m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); + } + + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.vcvtds(dst, ARMRegisters::asSingle(src)); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.vcvtsd(ARMRegisters::asSingle(dst), src); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + m_assembler.vcmp(left, right); + m_assembler.vmrs(); + + if (cond == DoubleNotEqual) { + // ConditionNE jumps if NotEqual *or* unordered - force the unordered cases not to jump. + Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); + Jump result = makeBranch(ARMv7Assembler::ConditionNE); + unordered.link(this); + return result; + } + if (cond == DoubleEqualOrUnordered) { + Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); + Jump notEqual = makeBranch(ARMv7Assembler::ConditionNE); + unordered.link(this); + // We get here if either unordered or equal. + Jump result = jump(); + notEqual.link(this); + return result; + } + return makeBranch(cond); + } + + enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + // Convert into dest. + m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + + // Calculate 2x dest. If the value potentially underflowed, it will have + // clamped to 0x80000000, so 2x dest is zero in this case. In the case of + // overflow the result will be equal to -2. + Jump underflow = branchAdd32(Zero, dest, dest, dataTempRegister); + Jump noOverflow = branch32(NotEqual, dataTempRegister, TrustedImm32(-2)); + + // For BranchIfTruncateSuccessful, we branch if 'noOverflow' jumps. + underflow.link(this); + if (branchType == BranchIfTruncateSuccessful) + return noOverflow; + + // We'll reach the current point in the code on failure, so plant a + // jump here & link the success case. + Jump failure = jump(); + noOverflow.link(this); + return failure; + } + + Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + + Jump overflow = branch32(Equal, dest, TrustedImm32(0x7fffffff)); + Jump success = branch32(GreaterThanOrEqual, dest, TrustedImm32(0)); + overflow.link(this); + + if (branchType == BranchIfTruncateSuccessful) + return success; + + Jump failure = jump(); + success.link(this); + return failure; + } + + // Result is undefined if the value is outside of the integer range. + void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + } + + void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) + { + m_assembler.vcvt_floatingPointToUnsigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + } + + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID) + { + m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + m_assembler.vcvt_signedToFloatingPoint(fpTempRegister, fpTempRegisterAsSingle()); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, fpTempRegister)); + + // If the result is zero, it might have been -0.0, and the double comparison won't catch this! + failureCases.append(branchTest32(Zero, dest)); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID) + { + m_assembler.vcmpz(reg); + m_assembler.vmrs(); + Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); + Jump result = makeBranch(ARMv7Assembler::ConditionNE); + unordered.link(this); + return result; + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID) + { + m_assembler.vcmpz(reg); + m_assembler.vmrs(); + Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); + Jump notEqual = makeBranch(ARMv7Assembler::ConditionNE); + unordered.link(this); + // We get here if either unordered or equal. + Jump result = jump(); + notEqual.link(this); + return result; + } + + // Stack manipulation operations: + // + // The ABI is assumed to provide a stack abstraction to memory, + // containing machine word sized units of data. Push and pop + // operations add and remove a single register sized unit of data + // to or from the stack. Peek and poke operations read or write + // values on the stack, without moving the current stack position. + + void pop(RegisterID dest) + { + // store postindexed with writeback + m_assembler.ldr(dest, ARMRegisters::sp, sizeof(void*), false, true); + } + + void push(RegisterID src) + { + // store preindexed with writeback + m_assembler.str(src, ARMRegisters::sp, -sizeof(void*), true, true); + } + + void push(Address address) + { + load32(address, dataTempRegister); + push(dataTempRegister); + } + + void push(TrustedImm32 imm) + { + move(imm, dataTempRegister); + push(dataTempRegister); + } + + // Register move operations: + // + // Move values in registers. + + void move(TrustedImm32 imm, RegisterID dest) + { + uint32_t value = imm.m_value; + + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(value); + + if (armImm.isValid()) + m_assembler.mov(dest, armImm); + else if ((armImm = ARMThumbImmediate::makeEncodedImm(~value)).isValid()) + m_assembler.mvn(dest, armImm); + else { + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(value)); + if (value & 0xffff0000) + m_assembler.movt(dest, ARMThumbImmediate::makeUInt16(value >> 16)); + } + } + + void move(RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.mov(dest, src); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + move(TrustedImm32(imm), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + move(reg1, dataTempRegister); + move(reg2, reg1); + move(dataTempRegister, reg2); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + move(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + move(src, dest); + } + + // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. + static RelationalCondition invert(RelationalCondition cond) + { + return static_cast(cond ^ 1); + } + + void nop() + { + m_assembler.nop(); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + ARMv7Assembler::replaceWithJump(instructionStart.dataLocation(), destination.dataLocation()); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return ARMv7Assembler::maxJumpReplacementSize(); + } + + // Forwards / external control flow operations: + // + // This set of jump and conditional branch operations return a Jump + // object which may linked at a later point, allow forwards jump, + // or jumps that will require external linkage (after the code has been + // relocated). + // + // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge + // respecitvely, for unsigned comparisons the names b, a, be, and ae are + // used (representing the names 'below' and 'above'). + // + // Operands to the comparision are provided in the expected order, e.g. + // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when + // treated as a signed 32bit value, is less than or equal to 5. + // + // jz and jnz test whether the first operand is equal to zero, and take + // an optional second operand of a mask under which to perform the test. +private: + + // Should we be using TEQ for equal/not-equal? + void compare32(RegisterID left, TrustedImm32 right) + { + int32_t imm = right.m_value; + if (!imm) + m_assembler.tst(left, left); + else { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm); + if (armImm.isValid()) + m_assembler.cmp(left, armImm); + else if ((armImm = ARMThumbImmediate::makeEncodedImm(-imm)).isValid()) + m_assembler.cmn(left, armImm); + else { + move(TrustedImm32(imm), dataTempRegister); + m_assembler.cmp(left, dataTempRegister); + } + } + } + + void test32(RegisterID reg, TrustedImm32 mask) + { + int32_t imm = mask.m_value; + + if (imm == -1) + m_assembler.tst(reg, reg); + else { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm); + if (armImm.isValid()) + m_assembler.tst(reg, armImm); + else { + move(mask, dataTempRegister); + m_assembler.tst(reg, dataTempRegister); + } + } + } + +public: + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) + { + m_assembler.cmp(left, right); + return Jump(makeBranch(cond)); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + compare32(left, right); + return Jump(makeBranch(cond)); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + load32(right, dataTempRegister); + return branch32(cond, left, dataTempRegister); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + load32(left, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load32(left, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load32(left, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load32WithUnalignedHalfWords(left, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + load32(left.m_ptr, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load32(left.m_ptr, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branch8(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + compare32(left, right); + return Jump(makeBranch(cond)); + } + + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + ASSERT(!(0xffffff00 & right.m_value)); + // use addressTempRegister incase the branch8 we call uses dataTempRegister. :-/ + load8(left, addressTempRegister); + return branch8(cond, addressTempRegister, right); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(0xffffff00 & right.m_value)); + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load8(left, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + m_assembler.tst(reg, mask); + return Jump(makeBranch(cond)); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + test32(reg, mask); + return Jump(makeBranch(cond)); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + // use addressTempRegister incase the branchTest32 we call uses dataTempRegister. :-/ + load32(address, addressTempRegister); + return branchTest32(cond, addressTempRegister, mask); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + // use addressTempRegister incase the branchTest32 we call uses dataTempRegister. :-/ + load32(address, addressTempRegister); + return branchTest32(cond, addressTempRegister, mask); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + // use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/ + load8(address, addressTempRegister); + return branchTest32(cond, addressTempRegister, mask); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + // use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/ + move(TrustedImmPtr(address.m_ptr), addressTempRegister); + load8(Address(addressTempRegister), addressTempRegister); + return branchTest32(cond, addressTempRegister, mask); + } + + void jump(RegisterID target) + { + m_assembler.bx(target); + } + + // Address is a memory location containing the address to jump to + void jump(Address address) + { + load32(address, dataTempRegister); + m_assembler.bx(dataTempRegister); + } + + void jump(AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), dataTempRegister); + load32(Address(dataTempRegister), dataTempRegister); + m_assembler.bx(dataTempRegister); + } + + + // Arithmetic control flow operations: + // + // This set of conditional branch operations branch based + // on the result of an arithmetic operation. The operation + // is performed as normal, storing the result. + // + // * jz operations branch if the result is zero. + // * jo operations branch if the (signed) arithmetic + // operation caused an overflow to occur. + + Jump branchAdd32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.add_S(dest, op1, op2); + return Jump(makeBranch(cond)); + } + + Jump branchAdd32(ResultCondition cond, RegisterID op1, TrustedImm32 imm, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add_S(dest, op1, armImm); + else { + move(imm, dataTempRegister); + m_assembler.add_S(dest, op1, dataTempRegister); + } + return Jump(makeBranch(cond)); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchAdd32(cond, dest, src, dest); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchAdd32(cond, dest, imm, dest); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) + { + // Move the high bits of the address into addressTempRegister, + // and load the value into dataTempRegister. + move(TrustedImmPtr(dest.m_ptr), addressTempRegister); + m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); + + // Do the add. + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add_S(dataTempRegister, dataTempRegister, armImm); + else { + // If the operand does not fit into an immediate then load it temporarily + // into addressTempRegister; since we're overwriting addressTempRegister + // we'll need to reload it with the high bits of the address afterwards. + move(imm, addressTempRegister); + m_assembler.add_S(dataTempRegister, dataTempRegister, addressTempRegister); + move(TrustedImmPtr(dest.m_ptr), addressTempRegister); + } + + // Store the result. + m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); + + return Jump(makeBranch(cond)); + } + + Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + m_assembler.smull(dest, dataTempRegister, src1, src2); + + if (cond == Overflow) { + m_assembler.asr(addressTempRegister, dest, 31); + return branch32(NotEqual, addressTempRegister, dataTempRegister); + } + + return branchTest32(cond, dest); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchMul32(cond, src, dest, dest); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(imm, dataTempRegister); + return branchMul32(cond, dataTempRegister, src, dest); + } + + Jump branchNeg32(ResultCondition cond, RegisterID srcDest) + { + ARMThumbImmediate zero = ARMThumbImmediate::makeUInt12(0); + m_assembler.sub_S(srcDest, zero, srcDest); + return Jump(makeBranch(cond)); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + m_assembler.orr_S(dest, dest, src); + return Jump(makeBranch(cond)); + } + + Jump branchSub32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.sub_S(dest, op1, op2); + return Jump(makeBranch(cond)); + } + + Jump branchSub32(ResultCondition cond, RegisterID op1, TrustedImm32 imm, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.sub_S(dest, op1, armImm); + else { + move(imm, dataTempRegister); + m_assembler.sub_S(dest, op1, dataTempRegister); + } + return Jump(makeBranch(cond)); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchSub32(cond, dest, src, dest); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchSub32(cond, dest, imm, dest); + } + + void relativeTableJump(RegisterID index, int scale) + { + ASSERT(scale >= 0 && scale <= 31); + + // dataTempRegister will point after the jump if index register contains zero + move(ARMRegisters::pc, dataTempRegister); + m_assembler.add(dataTempRegister, dataTempRegister, ARMThumbImmediate::makeEncodedImm(9)); + + ShiftTypeAndAmount shift(SRType_LSL, scale); + m_assembler.add(dataTempRegister, dataTempRegister, index, shift); + jump(dataTempRegister); + } + + // Miscellaneous operations: + + void breakpoint(uint8_t imm = 0) + { + m_assembler.bkpt(imm); + } + + ALWAYS_INLINE Call nearCall() + { + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Call(m_assembler.blx(dataTempRegister), Call::LinkableNear); + } + + ALWAYS_INLINE Call call() + { + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Call(m_assembler.blx(dataTempRegister), Call::Linkable); + } + + ALWAYS_INLINE Call call(RegisterID target) + { + return Call(m_assembler.blx(target), Call::None); + } + + ALWAYS_INLINE Call call(Address address) + { + load32(address, dataTempRegister); + return Call(m_assembler.blx(dataTempRegister), Call::None); + } + + ALWAYS_INLINE void ret() + { + m_assembler.bx(linkRegister); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmp(left, right); + m_assembler.it(armV7Condition(cond), false); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); + } + + void compare32(RelationalCondition cond, Address left, RegisterID right, RegisterID dest) + { + load32(left, dataTempRegister); + compare32(cond, dataTempRegister, right, dest); + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + load8(left, addressTempRegister); + compare32(cond, addressTempRegister, right, dest); + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + compare32(left, right); + m_assembler.it(armV7Condition(cond), false); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); + } + + // FIXME: + // The mask should be optional... paerhaps the argument order should be + // dest-src, operations always have a dest? ... possibly not true, considering + // asm ops like test, or pseudo ops like pop(). + void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + load32(address, dataTempRegister); + test32(dataTempRegister, mask); + m_assembler.it(armV7Condition(cond), false); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); + } + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + load8(address, dataTempRegister); + test32(dataTempRegister, mask); + m_assembler.it(armV7Condition(cond), false); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); + } + + ALWAYS_INLINE DataLabel32 moveWithPatch(TrustedImm32 imm, RegisterID dst) + { + padBeforePatch(); + moveFixedWidthEncoding(imm, dst); + return DataLabel32(this); + } + + ALWAYS_INLINE DataLabelPtr moveWithPatch(TrustedImmPtr imm, RegisterID dst) + { + padBeforePatch(); + moveFixedWidthEncoding(TrustedImm32(imm), dst); + return DataLabelPtr(this); + } + + ALWAYS_INLINE Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + dataLabel = moveWithPatch(initialRightValue, dataTempRegister); + return branch32(cond, left, dataTempRegister); + } + + ALWAYS_INLINE Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + load32(left, addressTempRegister); + dataLabel = moveWithPatch(initialRightValue, dataTempRegister); + return branch32(cond, addressTempRegister, dataTempRegister); + } + + PatchableJump patchableBranchPtr(RelationalCondition cond, Address left, TrustedImmPtr right = TrustedImmPtr(0)) + { + m_makeJumpPatchable = true; + Jump result = branch32(cond, left, TrustedImm32(right)); + m_makeJumpPatchable = false; + return PatchableJump(result); + } + + PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + m_makeJumpPatchable = true; + Jump result = branchTest32(cond, reg, mask); + m_makeJumpPatchable = false; + return PatchableJump(result); + } + + PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) + { + m_makeJumpPatchable = true; + Jump result = branch32(cond, reg, imm); + m_makeJumpPatchable = false; + return PatchableJump(result); + } + + PatchableJump patchableBranchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + m_makeJumpPatchable = true; + Jump result = branchPtrWithPatch(cond, left, dataLabel, initialRightValue); + m_makeJumpPatchable = false; + return PatchableJump(result); + } + + PatchableJump patchableJump() + { + padBeforePatch(); + m_makeJumpPatchable = true; + Jump result = jump(); + m_makeJumpPatchable = false; + return PatchableJump(result); + } + + ALWAYS_INLINE DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + DataLabelPtr label = moveWithPatch(initialValue, dataTempRegister); + store32(dataTempRegister, address); + return label; + } + ALWAYS_INLINE DataLabelPtr storePtrWithPatch(ImplicitAddress address) { return storePtrWithPatch(TrustedImmPtr(0), address); } + + + ALWAYS_INLINE Call tailRecursiveCall() + { + // Like a normal call, but don't link. + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Call(m_assembler.bx(dataTempRegister), Call::Linkable); + } + + ALWAYS_INLINE Call makeTailRecursiveCall(Jump oldJump) + { + oldJump.link(this); + return tailRecursiveCall(); + } + + + int executableOffsetFor(int location) + { + return m_assembler.executableOffsetFor(location); + } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(reinterpret_cast(ARMv7Assembler::readCallTarget(call.dataLocation()))); + } + + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + const unsigned twoWordOpSize = 4; + return label.labelAtOffset(-twoWordOpSize * 2); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) + { + ARMv7Assembler::revertJumpTo_movT3(instructionStart.dataLocation(), dataTempRegister, ARMThumbImmediate::makeUInt16(reinterpret_cast(initialValue) & 0xffff)); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel, Address, void*) + { + UNREACHABLE_FOR_PLATFORM(); + } + +protected: + ALWAYS_INLINE Jump jump() + { + m_assembler.label(); // Force nop-padding if we're in the middle of a watchpoint. + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Jump(m_assembler.bx(dataTempRegister), m_makeJumpPatchable ? ARMv7Assembler::JumpNoConditionFixedSize : ARMv7Assembler::JumpNoCondition); + } + + ALWAYS_INLINE Jump makeBranch(ARMv7Assembler::Condition cond) + { + m_assembler.label(); // Force nop-padding if we're in the middle of a watchpoint. + m_assembler.it(cond, true, true); + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Jump(m_assembler.bx(dataTempRegister), m_makeJumpPatchable ? ARMv7Assembler::JumpConditionFixedSize : ARMv7Assembler::JumpCondition, cond); + } + ALWAYS_INLINE Jump makeBranch(RelationalCondition cond) { return makeBranch(armV7Condition(cond)); } + ALWAYS_INLINE Jump makeBranch(ResultCondition cond) { return makeBranch(armV7Condition(cond)); } + ALWAYS_INLINE Jump makeBranch(DoubleCondition cond) { return makeBranch(armV7Condition(cond)); } + + ArmAddress setupArmAddress(BaseIndex address) + { + if (address.offset) { + ARMThumbImmediate imm = ARMThumbImmediate::makeUInt12OrEncodedImm(address.offset); + if (imm.isValid()) + m_assembler.add(addressTempRegister, address.base, imm); + else { + move(TrustedImm32(address.offset), addressTempRegister); + m_assembler.add(addressTempRegister, addressTempRegister, address.base); + } + + return ArmAddress(addressTempRegister, address.index, address.scale); + } else + return ArmAddress(address.base, address.index, address.scale); + } + + ArmAddress setupArmAddress(Address address) + { + if ((address.offset >= -0xff) && (address.offset <= 0xfff)) + return ArmAddress(address.base, address.offset); + + move(TrustedImm32(address.offset), addressTempRegister); + return ArmAddress(address.base, addressTempRegister); + } + + ArmAddress setupArmAddress(ImplicitAddress address) + { + if ((address.offset >= -0xff) && (address.offset <= 0xfff)) + return ArmAddress(address.base, address.offset); + + move(TrustedImm32(address.offset), addressTempRegister); + return ArmAddress(address.base, addressTempRegister); + } + + RegisterID makeBaseIndexBase(BaseIndex address) + { + if (!address.offset) + return address.base; + + ARMThumbImmediate imm = ARMThumbImmediate::makeUInt12OrEncodedImm(address.offset); + if (imm.isValid()) + m_assembler.add(addressTempRegister, address.base, imm); + else { + move(TrustedImm32(address.offset), addressTempRegister); + m_assembler.add(addressTempRegister, addressTempRegister, address.base); + } + + return addressTempRegister; + } + + void moveFixedWidthEncoding(TrustedImm32 imm, RegisterID dst) + { + uint32_t value = imm.m_value; + m_assembler.movT3(dst, ARMThumbImmediate::makeUInt16(value & 0xffff)); + m_assembler.movt(dst, ARMThumbImmediate::makeUInt16(value >> 16)); + } + + ARMv7Assembler::Condition armV7Condition(RelationalCondition cond) + { + return static_cast(cond); + } + + ARMv7Assembler::Condition armV7Condition(ResultCondition cond) + { + return static_cast(cond); + } + + ARMv7Assembler::Condition armV7Condition(DoubleCondition cond) + { + return static_cast(cond); + } + +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + ARMv7Assembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + bool m_makeJumpPatchable; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerARMv7_h diff --git a/3rdparty/masm/assembler/MacroAssemblerCodeRef.h b/3rdparty/masm/assembler/MacroAssemblerCodeRef.h new file mode 100644 index 0000000000..c2af24060a --- /dev/null +++ b/3rdparty/masm/assembler/MacroAssemblerCodeRef.h @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2009, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerCodeRef_h +#define MacroAssemblerCodeRef_h + +#include "Disassembler.h" +#include "ExecutableAllocator.h" +#include "LLIntData.h" +#include +#include +#include +#include + +// ASSERT_VALID_CODE_POINTER checks that ptr is a non-null pointer, and that it is a valid +// instruction address on the platform (for example, check any alignment requirements). +#if CPU(ARM_THUMB2) +// ARM/thumb instructions must be 16-bit aligned, but all code pointers to be loaded +// into the processor are decorated with the bottom bit set, indicating that this is +// thumb code (as oposed to 32-bit traditional ARM). The first test checks for both +// decorated and undectorated null, and the second test ensures that the pointer is +// decorated. +#define ASSERT_VALID_CODE_POINTER(ptr) \ + ASSERT(reinterpret_cast(ptr) & ~1); \ + ASSERT(reinterpret_cast(ptr) & 1) +#define ASSERT_VALID_CODE_OFFSET(offset) \ + ASSERT(!(offset & 1)) // Must be multiple of 2. +#else +#define ASSERT_VALID_CODE_POINTER(ptr) \ + ASSERT(ptr) +#define ASSERT_VALID_CODE_OFFSET(offset) // Anything goes! +#endif + +#if CPU(X86) && OS(WINDOWS) +#define CALLING_CONVENTION_IS_STDCALL 1 +#ifndef CDECL +#if COMPILER(MSVC) +#define CDECL __cdecl +#else +#define CDECL __attribute__ ((__cdecl)) +#endif // COMPILER(MSVC) +#endif // CDECL +#else +#define CALLING_CONVENTION_IS_STDCALL 0 +#endif + +#if CPU(X86) +#define HAS_FASTCALL_CALLING_CONVENTION 1 +#ifndef FASTCALL +#if COMPILER(MSVC) +#define FASTCALL __fastcall +#else +#define FASTCALL __attribute__ ((fastcall)) +#endif // COMPILER(MSVC) +#endif // FASTCALL +#else +#define HAS_FASTCALL_CALLING_CONVENTION 0 +#endif // CPU(X86) + +namespace JSC { + +// FunctionPtr: +// +// FunctionPtr should be used to wrap pointers to C/C++ functions in JSC +// (particularly, the stub functions). +class FunctionPtr { +public: + FunctionPtr() + : m_value(0) + { + } + + template + FunctionPtr(returnType(*value)()) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1, argType2)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1, argType2, argType3)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1, argType2, argType3, argType4)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1, argType2, argType3, argType4, argType5)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + +// MSVC doesn't seem to treat functions with different calling conventions as +// different types; these methods already defined for fastcall, below. +#if CALLING_CONVENTION_IS_STDCALL && !OS(WINDOWS) + + template + FunctionPtr(returnType (CDECL *value)()) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (CDECL *value)(argType1)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (CDECL *value)(argType1, argType2)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (CDECL *value)(argType1, argType2, argType3)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (CDECL *value)(argType1, argType2, argType3, argType4)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } +#endif + +#if HAS_FASTCALL_CALLING_CONVENTION + + template + FunctionPtr(returnType (FASTCALL *value)()) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (FASTCALL *value)(argType1)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (FASTCALL *value)(argType1, argType2)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (FASTCALL *value)(argType1, argType2, argType3)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (FASTCALL *value)(argType1, argType2, argType3, argType4)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } +#endif + + template + explicit FunctionPtr(FunctionType* value) + // Using a C-ctyle cast here to avoid compiler error on RVTC: + // Error: #694: reinterpret_cast cannot cast away const or other type qualifiers + // (I guess on RVTC function pointers have a different constness to GCC/MSVC?) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + void* value() const { return m_value; } + void* executableAddress() const { return m_value; } + + +private: + void* m_value; +}; + +// ReturnAddressPtr: +// +// ReturnAddressPtr should be used to wrap return addresses generated by processor +// 'call' instructions exectued in JIT code. We use return addresses to look up +// exception and optimization information, and to repatch the call instruction +// that is the source of the return address. +class ReturnAddressPtr { +public: + ReturnAddressPtr() + : m_value(0) + { + } + + explicit ReturnAddressPtr(void* value) + : m_value(value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + explicit ReturnAddressPtr(FunctionPtr function) + : m_value(function.value()) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + void* value() const { return m_value; } + +private: + void* m_value; +}; + +// MacroAssemblerCodePtr: +// +// MacroAssemblerCodePtr should be used to wrap pointers to JIT generated code. +class MacroAssemblerCodePtr { +public: + MacroAssemblerCodePtr() + : m_value(0) + { + } + + explicit MacroAssemblerCodePtr(void* value) +#if CPU(ARM_THUMB2) + // Decorate the pointer as a thumb code pointer. + : m_value(reinterpret_cast(value) + 1) +#else + : m_value(value) +#endif + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + static MacroAssemblerCodePtr createFromExecutableAddress(void* value) + { + ASSERT_VALID_CODE_POINTER(value); + MacroAssemblerCodePtr result; + result.m_value = value; + return result; + } + +#if ENABLE(LLINT) + static MacroAssemblerCodePtr createLLIntCodePtr(LLIntCode codeId) + { + return createFromExecutableAddress(LLInt::getCodePtr(codeId)); + } +#endif + + explicit MacroAssemblerCodePtr(ReturnAddressPtr ra) + : m_value(ra.value()) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + void* executableAddress() const { return m_value; } +#if CPU(ARM_THUMB2) + // To use this pointer as a data address remove the decoration. + void* dataLocation() const { ASSERT_VALID_CODE_POINTER(m_value); return reinterpret_cast(m_value) - 1; } +#else + void* dataLocation() const { ASSERT_VALID_CODE_POINTER(m_value); return m_value; } +#endif + + bool operator!() const + { + return !m_value; + } + +private: + void* m_value; +}; + +// MacroAssemblerCodeRef: +// +// A reference to a section of JIT generated code. A CodeRef consists of a +// pointer to the code, and a ref pointer to the pool from within which it +// was allocated. +class MacroAssemblerCodeRef { +private: + // This is private because it's dangerous enough that we want uses of it + // to be easy to find - hence the static create method below. + explicit MacroAssemblerCodeRef(MacroAssemblerCodePtr codePtr) + : m_codePtr(codePtr) + { + ASSERT(m_codePtr); + } + +public: + MacroAssemblerCodeRef() + { + } + + MacroAssemblerCodeRef(PassRefPtr executableMemory) + : m_codePtr(executableMemory->start()) + , m_executableMemory(executableMemory) + { + ASSERT(m_executableMemory->isManaged()); + ASSERT(m_executableMemory->start()); + ASSERT(m_codePtr); + } + + // Use this only when you know that the codePtr refers to code that is + // already being kept alive through some other means. Typically this means + // that codePtr is immortal. + static MacroAssemblerCodeRef createSelfManagedCodeRef(MacroAssemblerCodePtr codePtr) + { + return MacroAssemblerCodeRef(codePtr); + } + +#if ENABLE(LLINT) + // Helper for creating self-managed code refs from LLInt. + static MacroAssemblerCodeRef createLLIntCodeRef(LLIntCode codeId) + { + return createSelfManagedCodeRef(MacroAssemblerCodePtr::createFromExecutableAddress(LLInt::getCodePtr(codeId))); + } +#endif + + ExecutableMemoryHandle* executableMemory() const + { + return m_executableMemory.get(); + } + + MacroAssemblerCodePtr code() const + { + return m_codePtr; + } + + size_t size() const + { + if (!m_executableMemory) + return 0; + return m_executableMemory->sizeInBytes(); + } + + bool tryToDisassemble(const char* prefix) const + { + return JSC::tryToDisassemble(m_codePtr, size(), prefix, WTF::dataFile()); + } + + bool operator!() const { return !m_codePtr; } + +private: + MacroAssemblerCodePtr m_codePtr; + RefPtr m_executableMemory; +}; + +} // namespace JSC + +#endif // MacroAssemblerCodeRef_h diff --git a/3rdparty/masm/assembler/MacroAssemblerMIPS.h b/3rdparty/masm/assembler/MacroAssemblerMIPS.h new file mode 100644 index 0000000000..3ab2553001 --- /dev/null +++ b/3rdparty/masm/assembler/MacroAssemblerMIPS.h @@ -0,0 +1,2316 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY MIPS TECHNOLOGIES, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MIPS TECHNOLOGIES, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerMIPS_h +#define MacroAssemblerMIPS_h + +#if ENABLE(ASSEMBLER) && CPU(MIPS) + +#include "AbstractMacroAssembler.h" +#include "MIPSAssembler.h" + +namespace JSC { + +class MacroAssemblerMIPS : public AbstractMacroAssembler { +public: + typedef MIPSRegisters::FPRegisterID FPRegisterID; + + MacroAssemblerMIPS() + : m_fixedWidth(false) + { + } + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return value >= -2147483647 - 1 && value <= 2147483647; + } + + static const Scale ScalePtr = TimesFour; + + // For storing immediate number + static const RegisterID immTempRegister = MIPSRegisters::t0; + // For storing data loaded from the memory + static const RegisterID dataTempRegister = MIPSRegisters::t1; + // For storing address base + static const RegisterID addrTempRegister = MIPSRegisters::t2; + // For storing compare result + static const RegisterID cmpTempRegister = MIPSRegisters::t3; + + // FP temp register + static const FPRegisterID fpTempRegister = MIPSRegisters::f16; + + static const int MaximumCompactPtrAlignedAddressOffset = 0x7FFFFFFF; + + enum RelationalCondition { + Equal, + NotEqual, + Above, + AboveOrEqual, + Below, + BelowOrEqual, + GreaterThan, + GreaterThanOrEqual, + LessThan, + LessThanOrEqual + }; + + enum ResultCondition { + Overflow, + Signed, + Zero, + NonZero + }; + + enum DoubleCondition { + DoubleEqual, + DoubleNotEqual, + DoubleGreaterThan, + DoubleGreaterThanOrEqual, + DoubleLessThan, + DoubleLessThanOrEqual, + DoubleEqualOrUnordered, + DoubleNotEqualOrUnordered, + DoubleGreaterThanOrUnordered, + DoubleGreaterThanOrEqualOrUnordered, + DoubleLessThanOrUnordered, + DoubleLessThanOrEqualOrUnordered + }; + + static const RegisterID stackPointerRegister = MIPSRegisters::sp; + static const RegisterID returnAddressRegister = MIPSRegisters::ra; + + // Integer arithmetic operations: + // + // Operations are typically two operand - operation(source, srcDst) + // For many operations the source may be an TrustedImm32, the srcDst operand + // may often be a memory location (explictly described using an Address + // object). + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.addu(dest, dest, src); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + add32(imm, dest, dest); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (imm.m_value >= -32768 && imm.m_value <= 32767 + && !m_fixedWidth) { + /* + addiu dest, src, imm + */ + m_assembler.addiu(dest, src, imm.m_value); + } else { + /* + li immTemp, imm + addu dest, src, immTemp + */ + move(imm, immTempRegister); + m_assembler.addu(dest, src, immTempRegister); + } + } + + void add32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + add32(imm, src, dest); + } + + void add32(TrustedImm32 imm, Address address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + li immTemp, imm + addu dataTemp, dataTemp, immTemp + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, address.base, address.offset); + if (imm.m_value >= -32768 && imm.m_value <= 32767 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + li immtemp, imm + addu dataTemp, dataTemp, immTemp + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); + + if (imm.m_value >= -32768 && imm.m_value <= 32767 && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); + } + } + + void add32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + add32(dataTempRegister, dest); + } + + void add32(AbsoluteAddress src, RegisterID dest) + { + load32(src.m_ptr, dataTempRegister); + add32(dataTempRegister, dest); + } + + void add32(RegisterID src, Address dest) + { + if (dest.offset >= -32768 && dest.offset <= 32767 && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + addu dataTemp, dataTemp, src + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, dest.base, dest.offset); + m_assembler.addu(dataTempRegister, dataTempRegister, src); + m_assembler.sw(dataTempRegister, dest.base, dest.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + addu dataTemp, dataTemp, src + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (dest.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, dest.base); + m_assembler.lw(dataTempRegister, addrTempRegister, dest.offset); + m_assembler.addu(dataTempRegister, dataTempRegister, src); + m_assembler.sw(dataTempRegister, addrTempRegister, dest.offset); + } + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + /* + li addrTemp, address + li immTemp, imm + lw cmpTemp, 0(addrTemp) + addu dataTemp, cmpTemp, immTemp + sw dataTemp, 0(addrTemp) + */ + move(TrustedImmPtr(address.m_ptr), addrTempRegister); + m_assembler.lw(cmpTempRegister, addrTempRegister, 0); + if (imm.m_value >= -32768 && imm.m_value <= 32767 && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, cmpTempRegister, imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, cmpTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, 0); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + /* + add32(imm, address) + sltu immTemp, dataTemp, cmpTemp # set carry-in bit + lw dataTemp, 4(addrTemp) + addiu dataTemp, imm.m_value >> 31 ? -1 : 0 + addu dataTemp, dataTemp, immTemp + sw dataTemp, 4(addrTemp) + */ + add32(imm, address); + m_assembler.sltu(immTempRegister, dataTempRegister, cmpTempRegister); + m_assembler.lw(dataTempRegister, addrTempRegister, 4); + if (imm.m_value >> 31) + m_assembler.addiu(dataTempRegister, dataTempRegister, -1); + m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); + m_assembler.sw(dataTempRegister, addrTempRegister, 4); + } + + void and32(RegisterID src, RegisterID dest) + { + m_assembler.andInsn(dest, dest, src); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (imm.m_value > 0 && imm.m_value < 65535 && !m_fixedWidth) + m_assembler.andi(dest, dest, imm.m_value); + else { + /* + li immTemp, imm + and dest, dest, immTemp + */ + move(imm, immTempRegister); + m_assembler.andInsn(dest, dest, immTempRegister); + } + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.sll(dest, dest, imm.m_value); + } + + void lshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.sllv(dest, dest, shiftAmount); + } + + void mul32(RegisterID src, RegisterID dest) + { + m_assembler.mul(dest, dest, src); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (!imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (imm.m_value == 1 && !m_fixedWidth) + move(src, dest); + else { + /* + li dataTemp, imm + mul dest, src, dataTemp + */ + move(imm, dataTempRegister); + m_assembler.mul(dest, src, dataTempRegister); + } + } + + void neg32(RegisterID srcDest) + { + m_assembler.subu(srcDest, MIPSRegisters::zero, srcDest); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orInsn(dest, dest, src); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.orInsn(dest, op1, op2); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_value && !m_fixedWidth) + return; + + if (imm.m_value > 0 && imm.m_value < 65535 + && !m_fixedWidth) { + m_assembler.ori(dest, dest, imm.m_value); + return; + } + + /* + li dataTemp, imm + or dest, dest, dataTemp + */ + move(imm, dataTempRegister); + m_assembler.orInsn(dest, dest, dataTempRegister); + } + + void or32(RegisterID src, AbsoluteAddress dest) + { + load32(dest.m_ptr, dataTempRegister); + m_assembler.orInsn(dataTempRegister, dataTempRegister, src); + store32(dataTempRegister, dest.m_ptr); + } + + void rshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.srav(dest, dest, shiftAmount); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.sra(dest, dest, imm.m_value); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.sra(dest, src, imm.m_value); + } + + void urshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.srlv(dest, dest, shiftAmount); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.srl(dest, dest, imm.m_value); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.subu(dest, dest, src); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) { + /* + addiu dest, src, imm + */ + m_assembler.addiu(dest, dest, -imm.m_value); + } else { + /* + li immTemp, imm + subu dest, src, immTemp + */ + move(imm, immTempRegister); + m_assembler.subu(dest, dest, immTempRegister); + } + } + + void sub32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) { + /* + addiu dest, src, imm + */ + m_assembler.addiu(dest, src, -imm.m_value); + } else { + /* + li immTemp, imm + subu dest, src, immTemp + */ + move(imm, immTempRegister); + m_assembler.subu(dest, src, immTempRegister); + } + } + + void sub32(TrustedImm32 imm, Address address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + li immTemp, imm + subu dataTemp, dataTemp, immTemp + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, address.base, address.offset); + if (imm.m_value >= -32767 && imm.m_value <= 32768 && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + li immtemp, imm + subu dataTemp, dataTemp, immTemp + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); + + if (imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); + } + } + + void sub32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + sub32(dataTempRegister, dest); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + /* + li addrTemp, address + li immTemp, imm + lw dataTemp, 0(addrTemp) + subu dataTemp, dataTemp, immTemp + sw dataTemp, 0(addrTemp) + */ + move(TrustedImmPtr(address.m_ptr), addrTempRegister); + m_assembler.lw(dataTempRegister, addrTempRegister, 0); + + if (imm.m_value >= -32767 && imm.m_value <= 32768 && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, 0); + } + + void xor32(RegisterID src, RegisterID dest) + { + m_assembler.xorInsn(dest, dest, src); + } + + void xor32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value == -1) { + m_assembler.nor(dest, dest, MIPSRegisters::zero); + return; + } + + /* + li immTemp, imm + xor dest, dest, immTemp + */ + move(imm, immTempRegister); + m_assembler.xorInsn(dest, dest, immTempRegister); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.sqrtd(dst, src); + } + + void absDouble(FPRegisterID, FPRegisterID) + { + ASSERT_NOT_REACHED(); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result(this); + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, address.offset); + return result; + } + + // Memory access operations: + // + // Loads are of the form load(address, destination) and stores of the form + // store(source, address). The source for a store may be an TrustedImm32. Address + // operand objects to loads and store will be implicitly constructed if a + // register is passed. + + /* Need to use zero-extened load byte for load8. */ + void load8(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lbu(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lbu dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lbu(dest, addrTempRegister, address.offset); + } + } + + void load8(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lbu dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lbu(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lbu dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lbu(dest, addrTempRegister, address.offset); + } + } + + void load8Signed(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lb dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lb(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lb dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lb(dest, addrTempRegister, address.offset); + } + } + + void load32(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lw(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, address.offset); + } + } + + void load32(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lw dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lw dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lw(dest, addrTempRegister, address.offset); + } + } + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + load16(address, dest); + } + + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32764 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + (Big-Endian) + lwl dest, address.offset(addrTemp) + lwr dest, address.offset+3(addrTemp) + (Little-Endian) + lwl dest, address.offset+3(addrTemp) + lwr dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); +#if CPU(BIG_ENDIAN) + m_assembler.lwl(dest, addrTempRegister, address.offset); + m_assembler.lwr(dest, addrTempRegister, address.offset + 3); +#else + m_assembler.lwl(dest, addrTempRegister, address.offset + 3); + m_assembler.lwr(dest, addrTempRegister, address.offset); + +#endif + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, address.offset >> 16 + ori immTemp, immTemp, address.offset & 0xffff + addu addrTemp, addrTemp, immTemp + (Big-Endian) + lw dest, 0(at) + lw dest, 3(at) + (Little-Endian) + lw dest, 3(at) + lw dest, 0(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, address.offset >> 16); + m_assembler.ori(immTempRegister, immTempRegister, address.offset); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); +#if CPU(BIG_ENDIAN) + m_assembler.lwl(dest, addrTempRegister, 0); + m_assembler.lwr(dest, addrTempRegister, 3); +#else + m_assembler.lwl(dest, addrTempRegister, 3); + m_assembler.lwr(dest, addrTempRegister, 0); +#endif + } + } + + void load32(const void* address, RegisterID dest) + { + /* + li addrTemp, address + lw dest, 0(addrTemp) + */ + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.lw(dest, addrTempRegister, 0); + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + lw dest, 0(addrTemp) + */ + DataLabel32 dataLabel(this); + move(TrustedImm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, 0); + m_fixedWidth = false; + return dataLabel; + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabelCompact dataLabel(this); + load32WithAddressOffsetPatch(address, dest); + return dataLabel; + } + + /* Need to use zero-extened load half-word for load16. */ + void load16(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lhu(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lhu dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } + } + + /* Need to use zero-extened load half-word for load16. */ + void load16(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lhu dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lhu dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } + } + + void load16Signed(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lh dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lh(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lh dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lh(dest, addrTempRegister, address.offset); + } + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + sw src, 0(addrTemp) + */ + DataLabel32 dataLabel(this); + move(TrustedImm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, 0); + m_fixedWidth = false; + return dataLabel; + } + + void store8(RegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sb src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sb(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sb src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.sb(src, addrTempRegister, address.offset); + } + } + + void store8(TrustedImm32 imm, void* address) + { + /* + li immTemp, imm + li addrTemp, address + sb src, 0(addrTemp) + */ + if (!imm.m_value && !m_fixedWidth) { + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sb(MIPSRegisters::zero, addrTempRegister, 0); + } else { + move(imm, immTempRegister); + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sb(immTempRegister, addrTempRegister, 0); + } + } + + void store16(RegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sh src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sh(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sh src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.sh(src, addrTempRegister, address.offset); + } + } + + void store32(RegisterID src, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.sw(src, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sw src, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, address.offset); + } + } + + void store32(RegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sw src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sw src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.sw(src, addrTempRegister, address.offset); + } + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + if (!imm.m_value) + m_assembler.sw(MIPSRegisters::zero, address.base, address.offset); + else { + move(imm, immTempRegister); + m_assembler.sw(immTempRegister, address.base, address.offset); + } + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sw immTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + if (!imm.m_value && !m_fixedWidth) + m_assembler.sw(MIPSRegisters::zero, addrTempRegister, address.offset); + else { + move(imm, immTempRegister); + m_assembler.sw(immTempRegister, addrTempRegister, address.offset); + } + } + } + + void store32(RegisterID src, const void* address) + { + /* + li addrTemp, address + sw src, 0(addrTemp) + */ + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sw(src, addrTempRegister, 0); + } + + void store32(TrustedImm32 imm, const void* address) + { + /* + li immTemp, imm + li addrTemp, address + sw src, 0(addrTemp) + */ + if (!imm.m_value && !m_fixedWidth) { + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sw(MIPSRegisters::zero, addrTempRegister, 0); + } else { + move(imm, immTempRegister); + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sw(immTempRegister, addrTempRegister, 0); + } + } + + // Floating-point operations: + + static bool supportsFloatingPoint() + { +#if WTF_MIPS_DOUBLE_FLOAT + return true; +#else + return false; +#endif + } + + static bool supportsFloatingPointTruncate() + { +#if WTF_MIPS_DOUBLE_FLOAT && WTF_MIPS_ISA_AT_LEAST(2) + return true; +#else + return false; +#endif + } + + static bool supportsFloatingPointSqrt() + { +#if WTF_MIPS_DOUBLE_FLOAT && WTF_MIPS_ISA_AT_LEAST(2) + return true; +#else + return false; +#endif + } + static bool supportsFloatingPointAbs() { return false; } + + // Stack manipulation operations: + // + // The ABI is assumed to provide a stack abstraction to memory, + // containing machine word sized units of data. Push and pop + // operations add and remove a single register sized unit of data + // to or from the stack. Peek and poke operations read or write + // values on the stack, without moving the current stack position. + + void pop(RegisterID dest) + { + m_assembler.lw(dest, MIPSRegisters::sp, 0); + m_assembler.addiu(MIPSRegisters::sp, MIPSRegisters::sp, 4); + } + + void push(RegisterID src) + { + m_assembler.addiu(MIPSRegisters::sp, MIPSRegisters::sp, -4); + m_assembler.sw(src, MIPSRegisters::sp, 0); + } + + void push(Address address) + { + load32(address, dataTempRegister); + push(dataTempRegister); + } + + void push(TrustedImm32 imm) + { + move(imm, immTempRegister); + push(immTempRegister); + } + + // Register move operations: + // + // Move values in registers. + + void move(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (m_fixedWidth) { + m_assembler.lui(dest, imm.m_value >> 16); + m_assembler.ori(dest, dest, imm.m_value); + } else + m_assembler.li(dest, imm.m_value); + } + + void move(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + m_assembler.move(dest, src); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + move(TrustedImm32(imm), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + move(reg1, immTempRegister); + move(reg2, reg1); + move(immTempRegister, reg2); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + move(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + move(src, dest); + } + + // Forwards / external control flow operations: + // + // This set of jump and conditional branch operations return a Jump + // object which may linked at a later point, allow forwards jump, + // or jumps that will require external linkage (after the code has been + // relocated). + // + // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge + // respecitvely, for unsigned comparisons the names b, a, be, and ae are + // used (representing the names 'below' and 'above'). + // + // Operands to the comparision are provided in the expected order, e.g. + // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when + // treated as a signed 32bit value, is less than or equal to 5. + // + // jz and jnz test whether the first operand is equal to zero, and take + // an optional second operand of a mask under which to perform the test. + + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + // Make sure the immediate value is unsigned 8 bits. + ASSERT(!(right.m_value & 0xFFFFFF00)); + load8(left, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + // Make sure the immediate value is unsigned 8 bits. + ASSERT(!(right.m_value & 0xFFFFFF00)); + load8(left, dataTempRegister); + move(right, immTempRegister); + compare32(cond, dataTempRegister, immTempRegister, dest); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(right.m_value & 0xFFFFFF00)); + load8(left, dataTempRegister); + // Be careful that the previous load8() uses immTempRegister. + // So, we need to put move() after load8(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) + { + if (cond == Equal) + return branchEqual(left, right); + if (cond == NotEqual) + return branchNotEqual(left, right); + if (cond == Above) { + m_assembler.sltu(cmpTempRegister, right, left); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == AboveOrEqual) { + m_assembler.sltu(cmpTempRegister, left, right); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Below) { + m_assembler.sltu(cmpTempRegister, left, right); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == BelowOrEqual) { + m_assembler.sltu(cmpTempRegister, right, left); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == GreaterThan) { + m_assembler.slt(cmpTempRegister, right, left); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == GreaterThanOrEqual) { + m_assembler.slt(cmpTempRegister, left, right); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == LessThan) { + m_assembler.slt(cmpTempRegister, left, right); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == LessThanOrEqual) { + m_assembler.slt(cmpTempRegister, right, left); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + ASSERT(0); + + return Jump(); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + move(right, immTempRegister); + return branch32(cond, left, immTempRegister); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + load32(right, dataTempRegister); + return branch32(cond, left, dataTempRegister); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + load32(left, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + load32(left, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + load32(left, dataTempRegister); + // Be careful that the previous load32() uses immTempRegister. + // So, we need to put move() after load32(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + load32WithUnalignedHalfWords(left, dataTempRegister); + // Be careful that the previous load32WithUnalignedHalfWords() + // uses immTempRegister. + // So, we need to put move() after load32WithUnalignedHalfWords(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + load32(left.m_ptr, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + load32(left.m_ptr, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + ASSERT((cond == Zero) || (cond == NonZero)); + m_assembler.andInsn(cmpTempRegister, reg, mask); + if (cond == Zero) + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + return branchEqual(reg, MIPSRegisters::zero); + return branchNotEqual(reg, MIPSRegisters::zero); + } + move(mask, immTempRegister); + return branchTest32(cond, reg, immTempRegister); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + load32(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + load32(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + load8(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + move(TrustedImmPtr(address.m_ptr), dataTempRegister); + load8(Address(dataTempRegister), dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump jump() + { + return branchEqual(MIPSRegisters::zero, MIPSRegisters::zero); + } + + void jump(RegisterID target) + { + m_assembler.jr(target); + m_assembler.nop(); + } + + void jump(Address address) + { + m_fixedWidth = true; + load32(address, MIPSRegisters::t9); + m_assembler.jr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + } + + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + m_assembler.vmov(dest1, dest2, src); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + UNUSED_PARAM(scratch); + m_assembler.vmov(dest, src1, src2); + } + + // Arithmetic control flow operations: + // + // This set of conditional branch operations branch based + // on the result of an arithmetic operation. The operation + // is performed as normal, storing the result. + // + // * jz operations branch if the result is zero. + // * jo operations branch if the (signed) arithmetic + // operation caused an overflow to occur. + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + move dest, dataTemp + xor cmpTemp, dataTemp, src + bltz cmpTemp, No_overflow # diff sign bit -> no overflow + addu dest, dataTemp, src + xor cmpTemp, dest, dataTemp + bgez cmpTemp, No_overflow # same sign big -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + move(dest, dataTempRegister); + m_assembler.xorInsn(cmpTempRegister, dataTempRegister, src); + m_assembler.bltz(cmpTempRegister, 10); + m_assembler.addu(dest, dataTempRegister, src); + m_assembler.xorInsn(cmpTempRegister, dest, dataTempRegister); + m_assembler.bgez(cmpTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + add32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + add32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + add32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + return branchAdd32(cond, immTempRegister, dest); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + move(src, dest); + return branchAdd32(cond, immTempRegister, dest); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + mult src, dest + mfhi dataTemp + mflo dest + sra addrTemp, dest, 31 + beq dataTemp, addrTemp, No_overflow # all sign bits (bit 63 to bit 31) are the same -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + m_assembler.mult(src, dest); + m_assembler.mfhi(dataTempRegister); + m_assembler.mflo(dest); + m_assembler.sra(addrTempRegister, dest, 31); + m_assembler.beq(dataTempRegister, addrTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + mul32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + mul32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + mul32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(imm, immTempRegister); + move(src, dest); + return branchMul32(cond, immTempRegister, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + move dest, dataTemp + xor cmpTemp, dataTemp, src + bgez cmpTemp, No_overflow # same sign bit -> no overflow + subu dest, dataTemp, src + xor cmpTemp, dest, dataTemp + bgez cmpTemp, No_overflow # same sign bit -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + move(dest, dataTempRegister); + m_assembler.xorInsn(cmpTempRegister, dataTempRegister, src); + m_assembler.bgez(cmpTempRegister, 10); + m_assembler.subu(dest, dataTempRegister, src); + m_assembler.xorInsn(cmpTempRegister, dest, dataTempRegister); + m_assembler.bgez(cmpTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + sub32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + sub32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + sub32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + return branchSub32(cond, immTempRegister, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + move(src, dest); + return branchSub32(cond, immTempRegister, dest); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Signed) { + or32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + or32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + or32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + // Miscellaneous operations: + + void breakpoint() + { + m_assembler.bkpt(); + } + + Call nearCall() + { + /* We need two words for relaxation. */ + m_assembler.nop(); + m_assembler.nop(); + m_assembler.jal(); + m_assembler.nop(); + return Call(m_assembler.label(), Call::LinkableNear); + } + + Call call() + { + m_assembler.lui(MIPSRegisters::t9, 0); + m_assembler.ori(MIPSRegisters::t9, MIPSRegisters::t9, 0); + m_assembler.jalr(MIPSRegisters::t9); + m_assembler.nop(); + return Call(m_assembler.label(), Call::Linkable); + } + + Call call(RegisterID target) + { + m_assembler.jalr(target); + m_assembler.nop(); + return Call(m_assembler.label(), Call::None); + } + + Call call(Address address) + { + m_fixedWidth = true; + load32(address, MIPSRegisters::t9); + m_assembler.jalr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + return Call(m_assembler.label(), Call::None); + } + + void ret() + { + m_assembler.jr(MIPSRegisters::ra); + m_assembler.nop(); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + if (cond == Equal) { + m_assembler.xorInsn(dest, left, right); + m_assembler.sltiu(dest, dest, 1); + } else if (cond == NotEqual) { + m_assembler.xorInsn(dest, left, right); + m_assembler.sltu(dest, MIPSRegisters::zero, dest); + } else if (cond == Above) + m_assembler.sltu(dest, right, left); + else if (cond == AboveOrEqual) { + m_assembler.sltu(dest, left, right); + m_assembler.xori(dest, dest, 1); + } else if (cond == Below) + m_assembler.sltu(dest, left, right); + else if (cond == BelowOrEqual) { + m_assembler.sltu(dest, right, left); + m_assembler.xori(dest, dest, 1); + } else if (cond == GreaterThan) + m_assembler.slt(dest, right, left); + else if (cond == GreaterThanOrEqual) { + m_assembler.slt(dest, left, right); + m_assembler.xori(dest, dest, 1); + } else if (cond == LessThan) + m_assembler.slt(dest, left, right); + else if (cond == LessThanOrEqual) { + m_assembler.slt(dest, right, left); + m_assembler.xori(dest, dest, 1); + } + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + move(right, immTempRegister); + compare32(cond, left, immTempRegister, dest); + } + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + ASSERT((cond == Zero) || (cond == NonZero)); + load8(address, dataTempRegister); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + m_assembler.sltiu(dest, dataTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); + } else { + move(mask, immTempRegister); + m_assembler.andInsn(cmpTempRegister, dataTempRegister, immTempRegister); + if (cond == Zero) + m_assembler.sltiu(dest, cmpTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, cmpTempRegister); + } + } + + void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + ASSERT((cond == Zero) || (cond == NonZero)); + load32(address, dataTempRegister); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + m_assembler.sltiu(dest, dataTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); + } else { + move(mask, immTempRegister); + m_assembler.andInsn(cmpTempRegister, dataTempRegister, immTempRegister); + if (cond == Zero) + m_assembler.sltiu(dest, cmpTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, cmpTempRegister); + } + } + + DataLabel32 moveWithPatch(TrustedImm32 imm, RegisterID dest) + { + m_fixedWidth = true; + DataLabel32 label(this); + move(imm, dest); + m_fixedWidth = false; + return label; + } + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + m_fixedWidth = true; + DataLabelPtr label(this); + move(initialValue, dest); + m_fixedWidth = false; + return label; + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + m_fixedWidth = true; + dataLabel = moveWithPatch(initialRightValue, immTempRegister); + Jump temp = branch32(cond, left, immTempRegister); + m_fixedWidth = false; + return temp; + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + m_fixedWidth = true; + load32(left, dataTempRegister); + dataLabel = moveWithPatch(initialRightValue, immTempRegister); + Jump temp = branch32(cond, dataTempRegister, immTempRegister); + m_fixedWidth = false; + return temp; + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + m_fixedWidth = true; + DataLabelPtr dataLabel = moveWithPatch(initialValue, dataTempRegister); + store32(dataTempRegister, address); + m_fixedWidth = false; + return dataLabel; + } + + DataLabelPtr storePtrWithPatch(ImplicitAddress address) + { + return storePtrWithPatch(TrustedImmPtr(0), address); + } + + Call tailRecursiveCall() + { + // Like a normal call, but don't update the returned address register + m_fixedWidth = true; + move(TrustedImm32(0), MIPSRegisters::t9); + m_assembler.jr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + return Call(m_assembler.label(), Call::Linkable); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + oldJump.link(this); + return tailRecursiveCall(); + } + + void loadFloat(BaseIndex address, FPRegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lwc1 dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lwc1 dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + } + } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { +#if WTF_MIPS_ISA(1) + /* + li addrTemp, address.offset + addu addrTemp, addrTemp, base + lwc1 dest, 0(addrTemp) + lwc1 dest+1, 4(addrTemp) + */ + move(TrustedImm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lwc1(dest, addrTempRegister, 0); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + m_assembler.ldc1(dest, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + ldc1 dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.ldc1(dest, addrTempRegister, address.offset); + } +#endif + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { +#if WTF_MIPS_ISA(1) + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lwc1 dest, address.offset(addrTemp) + lwc1 dest+1, (address.offset+4)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, address.offset + 4); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lwc1 dest, (address.offset & 0xffff)(at) + lwc1 dest+4, (address.offset & 0xffff + 4)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, address.offset + 4); + } +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + ldc1 dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.ldc1(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + ldc1 dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.ldc1(dest, addrTempRegister, address.offset); + } +#endif + } + + void loadDouble(const void* address, FPRegisterID dest) + { +#if WTF_MIPS_ISA(1) + /* + li addrTemp, address + lwc1 dest, 0(addrTemp) + lwc1 dest+1, 4(addrTemp) + */ + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.lwc1(dest, addrTempRegister, 0); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); +#else + /* + li addrTemp, address + ldc1 dest, 0(addrTemp) + */ + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.ldc1(dest, addrTempRegister, 0); +#endif + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + swc1 src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.swc1(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + swc1 src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.swc1(src, addrTempRegister, address.offset); + } + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { +#if WTF_MIPS_ISA(1) + /* + li addrTemp, address.offset + addu addrTemp, addrTemp, base + swc1 dest, 0(addrTemp) + swc1 dest+1, 4(addrTemp) + */ + move(TrustedImm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.swc1(src, addrTempRegister, 0); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, 4); +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.sdc1(src, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sdc1 src, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sdc1(src, addrTempRegister, address.offset); + } +#endif + } + + void storeDouble(FPRegisterID src, BaseIndex address) + { +#if WTF_MIPS_ISA(1) + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + swc1 src, address.offset(addrTemp) + swc1 src+1, (address.offset + 4)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.swc1(src, addrTempRegister, address.offset); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, address.offset + 4); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + swc1 src, (address.offset & 0xffff)(at) + swc1 src+1, (address.offset & 0xffff + 4)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.swc1(src, addrTempRegister, address.offset); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, address.offset + 4); + } +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sdc1 src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sdc1(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sdc1 src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.sdc1(src, addrTempRegister, address.offset); + } +#endif + } + + void storeDouble(FPRegisterID src, const void* address) + { +#if WTF_MIPS_ISA(1) + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.swc1(src, addrTempRegister, 0); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, 4); +#else + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sdc1(src, addrTempRegister, 0); +#endif + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.addd(dest, dest, src); + } + + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.addd(dest, op1, op2); + } + + void addDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.addd(dest, dest, fpTempRegister); + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + loadDouble(address.m_ptr, fpTempRegister); + m_assembler.addd(dest, dest, fpTempRegister); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.subd(dest, dest, src); + } + + void subDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.subd(dest, dest, fpTempRegister); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.muld(dest, dest, src); + } + + void mulDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.muld(dest, dest, fpTempRegister); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.divd(dest, dest, src); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.mtc1(src, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + load32(src, dataTempRegister); + m_assembler.mtc1(dataTempRegister, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + load32(src.m_ptr, dataTempRegister); + m_assembler.mtc1(dataTempRegister, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.cvtds(dst, src); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.cvtsd(dst, src); + } + + void insertRelaxationWords() + { + /* We need four words for relaxation. */ + m_assembler.beq(MIPSRegisters::zero, MIPSRegisters::zero, 3); // Jump over nops; + m_assembler.nop(); + m_assembler.nop(); + m_assembler.nop(); + } + + Jump branchTrue() + { + m_assembler.appendJump(); + m_assembler.bc1t(); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.label()); + } + + Jump branchFalse() + { + m_assembler.appendJump(); + m_assembler.bc1f(); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.label()); + } + + Jump branchEqual(RegisterID rs, RegisterID rt) + { + m_assembler.appendJump(); + m_assembler.beq(rs, rt, 0); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.label()); + } + + Jump branchNotEqual(RegisterID rs, RegisterID rt) + { + m_assembler.appendJump(); + m_assembler.bne(rs, rt, 0); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.label()); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + if (cond == DoubleEqual) { + m_assembler.ceqd(left, right); + return branchTrue(); + } + if (cond == DoubleNotEqual) { + m_assembler.cueqd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThan) { + m_assembler.cngtd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrEqual) { + m_assembler.cnged(left, right); + return branchFalse(); // false + } + if (cond == DoubleLessThan) { + m_assembler.cltd(left, right); + return branchTrue(); + } + if (cond == DoubleLessThanOrEqual) { + m_assembler.cled(left, right); + return branchTrue(); + } + if (cond == DoubleEqualOrUnordered) { + m_assembler.cueqd(left, right); + return branchTrue(); + } + if (cond == DoubleNotEqualOrUnordered) { + m_assembler.ceqd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrUnordered) { + m_assembler.coled(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrEqualOrUnordered) { + m_assembler.coltd(left, right); + return branchFalse(); // false + } + if (cond == DoubleLessThanOrUnordered) { + m_assembler.cultd(left, right); + return branchTrue(); + } + if (cond == DoubleLessThanOrEqualOrUnordered) { + m_assembler.culed(left, right); + return branchTrue(); + } + ASSERT(0); + + return Jump(); + } + + // Truncates 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, INT_MAX 0x7fffffff). + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + m_assembler.truncwd(fpTempRegister, src); + m_assembler.mfc1(dest, fpTempRegister); + return branch32(Equal, dest, TrustedImm32(0x7fffffff)); + } + + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + m_assembler.cvtwd(fpTempRegister, src); + m_assembler.mfc1(dest, fpTempRegister); + + // If the result is zero, it might have been -0.0, and the double comparison won't catch this! + failureCases.append(branch32(Equal, dest, MIPSRegisters::zero)); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + convertInt32ToDouble(dest, fpTemp); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, fpTemp, src)); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) + { +#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 + m_assembler.mtc1(MIPSRegisters::zero, scratch); + m_assembler.mthc1(MIPSRegisters::zero, scratch); +#else + m_assembler.mtc1(MIPSRegisters::zero, scratch); + m_assembler.mtc1(MIPSRegisters::zero, FPRegisterID(scratch + 1)); +#endif + return branchDouble(DoubleNotEqual, reg, scratch); + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) + { +#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 + m_assembler.mtc1(MIPSRegisters::zero, scratch); + m_assembler.mthc1(MIPSRegisters::zero, scratch); +#else + m_assembler.mtc1(MIPSRegisters::zero, scratch); + m_assembler.mtc1(MIPSRegisters::zero, FPRegisterID(scratch + 1)); +#endif + return branchDouble(DoubleEqualOrUnordered, reg, scratch); + } + + void nop() + { + m_assembler.nop(); + } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(reinterpret_cast(MIPSAssembler::readCallTarget(call.dataLocation()))); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + ASSERT_NOT_REACHED(); + } + + static ptrdiff_t maxJumpReplacementSize() + { + ASSERT_NOT_REACHED(); + return 0; + } + + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + return label.labelAtOffset(0); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) + { + MIPSAssembler::revertJumpToMove(instructionStart.dataLocation(), immTempRegister, reinterpret_cast(initialValue) & 0xffff); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + UNREACHABLE_FOR_PLATFORM(); + } + + +private: + // If m_fixedWidth is true, we will generate a fixed number of instructions. + // Otherwise, we can emit any number of instructions. + bool m_fixedWidth; + + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + MIPSAssembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + +}; + +} + +#endif // ENABLE(ASSEMBLER) && CPU(MIPS) + +#endif // MacroAssemblerMIPS_h diff --git a/3rdparty/masm/assembler/MacroAssemblerSH4.cpp b/3rdparty/masm/assembler/MacroAssemblerSH4.cpp new file mode 100644 index 0000000000..59de3ff48c --- /dev/null +++ b/3rdparty/masm/assembler/MacroAssemblerSH4.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 STMicroelectronics. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "config.h" + +#if ENABLE(ASSEMBLER) && CPU(SH4) + +#include "MacroAssemblerSH4.h" + +namespace JSC { + +void MacroAssemblerSH4::linkCall(void* code, Call call, FunctionPtr function) +{ + SH4Assembler::linkCall(code, call.m_label, function.value()); +} + +void MacroAssemblerSH4::repatchCall(CodeLocationCall call, CodeLocationLabel destination) +{ + SH4Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); +} + +void MacroAssemblerSH4::repatchCall(CodeLocationCall call, FunctionPtr destination) +{ + SH4Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); +} + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) diff --git a/3rdparty/masm/assembler/MacroAssemblerSH4.h b/3rdparty/masm/assembler/MacroAssemblerSH4.h new file mode 100644 index 0000000000..ef210f80cb --- /dev/null +++ b/3rdparty/masm/assembler/MacroAssemblerSH4.h @@ -0,0 +1,2293 @@ +/* + * Copyright (C) 2009-2011 STMicroelectronics. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MacroAssemblerSH4_h +#define MacroAssemblerSH4_h + +#if ENABLE(ASSEMBLER) && CPU(SH4) + +#include "SH4Assembler.h" +#include "AbstractMacroAssembler.h" +#include + +namespace JSC { + +class MacroAssemblerSH4 : public AbstractMacroAssembler { +public: + typedef SH4Assembler::FPRegisterID FPRegisterID; + + static const Scale ScalePtr = TimesFour; + static const FPRegisterID fscratch = SH4Registers::fr10; + static const RegisterID stackPointerRegister = SH4Registers::sp; + static const RegisterID linkRegister = SH4Registers::pr; + static const RegisterID scratchReg3 = SH4Registers::r13; + + static const int MaximumCompactPtrAlignedAddressOffset = 60; + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return (value >= 0) && (value <= MaximumCompactPtrAlignedAddressOffset); + } + + enum RelationalCondition { + Equal = SH4Assembler::EQ, + NotEqual = SH4Assembler::NE, + Above = SH4Assembler::HI, + AboveOrEqual = SH4Assembler::HS, + Below = SH4Assembler::LI, + BelowOrEqual = SH4Assembler::LS, + GreaterThan = SH4Assembler::GT, + GreaterThanOrEqual = SH4Assembler::GE, + LessThan = SH4Assembler::LT, + LessThanOrEqual = SH4Assembler::LE + }; + + enum ResultCondition { + Overflow = SH4Assembler::OF, + Signed = SH4Assembler::SI, + Zero = SH4Assembler::EQ, + NonZero = SH4Assembler::NE + }; + + enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleEqual = SH4Assembler::EQ, + DoubleNotEqual = SH4Assembler::NE, + DoubleGreaterThan = SH4Assembler::GT, + DoubleGreaterThanOrEqual = SH4Assembler::GE, + DoubleLessThan = SH4Assembler::LT, + DoubleLessThanOrEqual = SH4Assembler::LE, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = SH4Assembler::EQU, + DoubleNotEqualOrUnordered = SH4Assembler::NEU, + DoubleGreaterThanOrUnordered = SH4Assembler::GTU, + DoubleGreaterThanOrEqualOrUnordered = SH4Assembler::GEU, + DoubleLessThanOrUnordered = SH4Assembler::LTU, + DoubleLessThanOrEqualOrUnordered = SH4Assembler::LEU, + }; + + RegisterID claimScratch() + { + return m_assembler.claimScratch(); + } + + void releaseScratch(RegisterID reg) + { + m_assembler.releaseScratch(reg); + } + + // Integer arithmetic operations + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.addlRegReg(src, dest); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + if (m_assembler.isImmediate(imm.m_value)) { + m_assembler.addlImm8r(imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm.m_value, scr); + m_assembler.addlRegReg(scr, dest); + releaseScratch(scr); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.movlRegReg(src, dest); + add32(imm, dest); + } + + void add32(TrustedImm32 imm, Address address) + { + RegisterID scr = claimScratch(); + load32(address, scr); + add32(imm, scr); + store32(scr, address); + releaseScratch(scr); + } + + void add32(Address src, RegisterID dest) + { + RegisterID scr = claimScratch(); + load32(src, scr); + m_assembler.addlRegReg(scr, dest); + releaseScratch(scr); + } + + void add32(AbsoluteAddress src, RegisterID dest) + { + RegisterID scr = claimScratch(); + load32(src.m_ptr, scr); + m_assembler.addlRegReg(scr, dest); + releaseScratch(scr); + } + + void and32(RegisterID src, RegisterID dest) + { + m_assembler.andlRegReg(src, dest); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + if ((imm.m_value <= 255) && (imm.m_value >= 0) && (dest == SH4Registers::r0)) { + m_assembler.andlImm8r(imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((imm.m_value), scr); + m_assembler.andlRegReg(scr, dest); + releaseScratch(scr); + } + + void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (src != dest) { + move(imm, dest); + and32(src, dest); + return; + } + + and32(imm, dest); + } + + void lshift32(RegisterID shiftamount, RegisterID dest) + { + if (shiftamount == SH4Registers::r0) + m_assembler.andlImm8r(0x1f, shiftamount); + else { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(0x1f, scr); + m_assembler.andlRegReg(scr, shiftamount); + releaseScratch(scr); + } + m_assembler.shllRegReg(dest, shiftamount); + } + + void rshift32(int imm, RegisterID dest) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(-imm, scr); + m_assembler.shaRegReg(dest, scr); + releaseScratch(scr); + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_value) + return; + + if ((imm.m_value == 1) || (imm.m_value == 2) || (imm.m_value == 8) || (imm.m_value == 16)) { + m_assembler.shllImm8r(imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((imm.m_value & 0x1f) , scr); + m_assembler.shllRegReg(dest, scr); + releaseScratch(scr); + } + + void lshift32(RegisterID src, TrustedImm32 shiftamount, RegisterID dest) + { + if (src != dest) + move(src, dest); + + lshift32(shiftamount, dest); + } + + void mul32(RegisterID src, RegisterID dest) + { + m_assembler.imullRegReg(src, dest); + m_assembler.stsmacl(dest); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + RegisterID scr = claimScratch(); + move(imm, scr); + if (src != dest) + move(src, dest); + mul32(scr, dest); + releaseScratch(scr); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orlRegReg(src, dest); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + if ((imm.m_value <= 255) && (imm.m_value >= 0) && (dest == SH4Registers::r0)) { + m_assembler.orlImm8r(imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm.m_value, scr); + m_assembler.orlRegReg(scr, dest); + releaseScratch(scr); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + move(op1, dest); + else if (op1 == dest) + or32(op2, dest); + else { + move(op2, dest); + or32(op1, dest); + } + } + + +void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (src != dest) { + move(imm, dest); + or32(src, dest); + return; + } + + or32(imm, dest); + } + + void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (src != dest) { + move(imm, dest); + xor32(src, dest); + return; + } + + xor32(imm, dest); + } + + void rshift32(RegisterID shiftamount, RegisterID dest) + { + if (shiftamount == SH4Registers::r0) + m_assembler.andlImm8r(0x1f, shiftamount); + else { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(0x1f, scr); + m_assembler.andlRegReg(scr, shiftamount); + releaseScratch(scr); + } + m_assembler.neg(shiftamount, shiftamount); + m_assembler.shaRegReg(dest, shiftamount); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value & 0x1f) + rshift32(imm.m_value & 0x1f, dest); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (src != dest) + move(src, dest); + rshift32(imm, dest); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.sublRegReg(src, dest); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address, RegisterID scratchReg) + { + RegisterID result = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); + m_assembler.movlMemReg(scratchReg, result); + + if (m_assembler.isImmediate(-imm.m_value)) + m_assembler.addlImm8r(-imm.m_value, result); + else { + m_assembler.loadConstant(imm.m_value, scratchReg3); + m_assembler.sublRegReg(scratchReg3, result); + } + + store32(result, scratchReg); + releaseScratch(result); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + RegisterID result = claimScratch(); + RegisterID scratchReg = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); + m_assembler.movlMemReg(scratchReg, result); + + if (m_assembler.isImmediate(-imm.m_value)) + m_assembler.addlImm8r(-imm.m_value, result); + else { + m_assembler.loadConstant(imm.m_value, scratchReg3); + m_assembler.sublRegReg(scratchReg3, result); + } + + store32(result, scratchReg); + releaseScratch(result); + releaseScratch(scratchReg); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address, RegisterID scratchReg) + { + RegisterID result = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); + m_assembler.movlMemReg(scratchReg, result); + + if (m_assembler.isImmediate(imm.m_value)) + m_assembler.addlImm8r(imm.m_value, result); + else { + m_assembler.loadConstant(imm.m_value, scratchReg3); + m_assembler.addlRegReg(scratchReg3, result); + } + + store32(result, scratchReg); + releaseScratch(result); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + RegisterID result = claimScratch(); + RegisterID scratchReg = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); + m_assembler.movlMemReg(scratchReg, result); + + if (m_assembler.isImmediate(imm.m_value)) + m_assembler.addlImm8r(imm.m_value, result); + else { + m_assembler.loadConstant(imm.m_value, scratchReg3); + m_assembler.addlRegReg(scratchReg3, result); + } + + store32(result, scratchReg); + releaseScratch(result); + releaseScratch(scratchReg); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + RegisterID scr1 = claimScratch(); + RegisterID scr2 = claimScratch(); + + // Add 32-bit LSB first. + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scr1); + m_assembler.movlMemReg(scr1, scr1); // scr1 = 32-bit LSB of int64 @ address + m_assembler.loadConstant(imm.m_value, scr2); + m_assembler.clrt(); + m_assembler.addclRegReg(scr1, scr2); + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scr1); + m_assembler.movlRegMem(scr2, scr1); // Update address with 32-bit LSB result. + + // Then add 32-bit MSB. + m_assembler.addlImm8r(4, scr1); + m_assembler.movlMemReg(scr1, scr1); // scr1 = 32-bit MSB of int64 @ address + m_assembler.movt(scr2); + if (imm.m_value < 0) + m_assembler.addlImm8r(-1, scr2); // Sign extend imm value if needed. + m_assembler.addvlRegReg(scr2, scr1); + m_assembler.loadConstant(reinterpret_cast(address.m_ptr) + 4, scr2); + m_assembler.movlRegMem(scr1, scr2); // Update (address + 4) with 32-bit MSB result. + + releaseScratch(scr2); + releaseScratch(scr1); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + if (m_assembler.isImmediate(-imm.m_value)) { + m_assembler.addlImm8r(-imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm.m_value, scr); + m_assembler.sublRegReg(scr, dest); + releaseScratch(scr); + } + + void sub32(Address src, RegisterID dest) + { + RegisterID scr = claimScratch(); + load32(src, scr); + m_assembler.sublRegReg(scr, dest); + releaseScratch(scr); + } + + void xor32(RegisterID src, RegisterID dest) + { + m_assembler.xorlRegReg(src, dest); + } + + void xor32(TrustedImm32 imm, RegisterID srcDest) + { + if (imm.m_value == -1) { + m_assembler.notlReg(srcDest, srcDest); + return; + } + + if ((srcDest != SH4Registers::r0) || (imm.m_value > 255) || (imm.m_value < 0)) { + RegisterID scr = claimScratch(); + m_assembler.loadConstant((imm.m_value), scr); + m_assembler.xorlRegReg(scr, srcDest); + releaseScratch(scr); + return; + } + + m_assembler.xorlImm8r(imm.m_value, srcDest); + } + + void compare32(int imm, RegisterID dst, RelationalCondition cond) + { + if (((cond == Equal) || (cond == NotEqual)) && (dst == SH4Registers::r0) && m_assembler.isImmediate(imm)) { + m_assembler.cmpEqImmR0(imm, dst); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm, scr); + m_assembler.cmplRegReg(scr, dst, SH4Condition(cond)); + releaseScratch(scr); + } + + void compare32(int offset, RegisterID base, RegisterID left, RelationalCondition cond) + { + RegisterID scr = claimScratch(); + if (!offset) { + m_assembler.movlMemReg(base, scr); + m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); + releaseScratch(scr); + return; + } + + if ((offset < 0) || (offset >= 64)) { + m_assembler.loadConstant(offset, scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, scr); + m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); + releaseScratch(scr); + return; + } + + m_assembler.movlMemReg(offset >> 2, base, scr); + m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); + releaseScratch(scr); + } + + void testImm(int imm, int offset, RegisterID base) + { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + + if ((offset < 0) || (offset >= 64)) { + m_assembler.loadConstant(offset, scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, scr); + } else if (offset) + m_assembler.movlMemReg(offset >> 2, base, scr); + else + m_assembler.movlMemReg(base, scr); + if (m_assembler.isImmediate(imm)) + m_assembler.movImm8(imm, scr1); + else + m_assembler.loadConstant(imm, scr1); + + m_assembler.testlRegReg(scr, scr1); + releaseScratch(scr); + releaseScratch(scr1); + } + + void testlImm(int imm, RegisterID dst) + { + if ((dst == SH4Registers::r0) && (imm <= 255) && (imm >= 0)) { + m_assembler.testlImm8r(imm, dst); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm, scr); + m_assembler.testlRegReg(scr, dst); + releaseScratch(scr); + } + + void compare32(RegisterID right, int offset, RegisterID base, RelationalCondition cond) + { + if (!offset) { + RegisterID scr = claimScratch(); + m_assembler.movlMemReg(base, scr); + m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); + releaseScratch(scr); + return; + } + + if ((offset < 0) || (offset >= 64)) { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(offset, scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, scr); + m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); + releaseScratch(scr); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.movlMemReg(offset >> 2, base, scr); + m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); + releaseScratch(scr); + } + + void compare32(int imm, int offset, RegisterID base, RelationalCondition cond) + { + if (!offset) { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.movlMemReg(base, scr); + m_assembler.loadConstant(imm, scr1); + m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); + releaseScratch(scr1); + releaseScratch(scr); + return; + } + + if ((offset < 0) || (offset >= 64)) { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.loadConstant(offset, scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, scr); + m_assembler.loadConstant(imm, scr1); + m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); + releaseScratch(scr1); + releaseScratch(scr); + return; + } + + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.movlMemReg(offset >> 2, base, scr); + m_assembler.loadConstant(imm, scr1); + m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); + releaseScratch(scr1); + releaseScratch(scr); + } + + // Memory access operation + + void load32(ImplicitAddress address, RegisterID dest) + { + load32(address.base, address.offset, dest); + } + + void load8(ImplicitAddress address, RegisterID dest) + { + load8(address.base, address.offset, dest); + } + + void load8(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + load8(scr, address.offset, dest); + releaseScratch(scr); + } + + void load8Signed(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + load8Signed(scr, address.offset, dest); + releaseScratch(scr); + } + + void load32(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + load32(scr, address.offset, dest); + releaseScratch(scr); + } + + void load32(const void* address, RegisterID dest) + { + m_assembler.loadConstant(reinterpret_cast(const_cast(address)), dest); + m_assembler.movlMemReg(dest, dest); + } + + void load32(RegisterID base, int offset, RegisterID dest) + { + if (!offset) { + m_assembler.movlMemReg(base, dest); + return; + } + + if ((offset >= 0) && (offset < 64)) { + m_assembler.movlMemReg(offset >> 2, base, dest); + return; + } + + if ((dest == SH4Registers::r0) && (dest != base)) { + m_assembler.loadConstant((offset), dest); + m_assembler.movlR0mr(base, dest); + return; + } + + RegisterID scr; + if (dest == base) + scr = claimScratch(); + else + scr = dest; + m_assembler.loadConstant((offset), scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, dest); + + if (dest == base) + releaseScratch(scr); + } + + void load8Signed(RegisterID base, int offset, RegisterID dest) + { + if (!offset) { + m_assembler.movbMemReg(base, dest); + return; + } + + if ((offset > 0) && (offset < 64) && (dest == SH4Registers::r0)) { + m_assembler.movbMemReg(offset, base, dest); + return; + } + + if (base != dest) { + m_assembler.loadConstant((offset), dest); + m_assembler.addlRegReg(base, dest); + m_assembler.movbMemReg(dest, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((offset), scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movbMemReg(scr, dest); + releaseScratch(scr); + } + + void load8(RegisterID base, int offset, RegisterID dest) + { + if (!offset) { + m_assembler.movbMemReg(base, dest); + m_assembler.extub(dest, dest); + return; + } + + if ((offset > 0) && (offset < 64) && (dest == SH4Registers::r0)) { + m_assembler.movbMemReg(offset, base, dest); + m_assembler.extub(dest, dest); + return; + } + + if (base != dest) { + m_assembler.loadConstant((offset), dest); + m_assembler.addlRegReg(base, dest); + m_assembler.movbMemReg(dest, dest); + m_assembler.extub(dest, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((offset), scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movbMemReg(scr, dest); + m_assembler.extub(dest, dest); + releaseScratch(scr); + } + + void load32(RegisterID r0, RegisterID src, RegisterID dst) + { + ASSERT(r0 == SH4Registers::r0); + m_assembler.movlR0mr(src, dst); + } + + void load32(RegisterID src, RegisterID dst) + { + m_assembler.movlMemReg(src, dst); + } + + void load16(ImplicitAddress address, RegisterID dest) + { + if (!address.offset) { + m_assembler.movwMemReg(address.base, dest); + extuw(dest, dest); + return; + } + + if ((address.offset > 0) && (address.offset < 64) && (dest == SH4Registers::r0)) { + m_assembler.movwMemReg(address.offset, address.base, dest); + extuw(dest, dest); + return; + } + + if (address.base != dest) { + m_assembler.loadConstant((address.offset), dest); + m_assembler.addlRegReg(address.base, dest); + m_assembler.movwMemReg(dest, dest); + extuw(dest, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((address.offset), scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movwMemReg(scr, dest); + extuw(dest, dest); + releaseScratch(scr); + } + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + add32(address.base, scr); + load8(scr, scr1); + add32(TrustedImm32(1), scr); + load8(scr, dest); + m_assembler.shllImm8r(8, dest); + or32(scr1, dest); + + releaseScratch(scr); + releaseScratch(scr1); + } + + void load16(RegisterID src, RegisterID dest) + { + m_assembler.movwMemReg(src, dest); + extuw(dest, dest); + } + + void load16Signed(RegisterID src, RegisterID dest) + { + m_assembler.movwMemReg(src, dest); + } + + void load16(RegisterID r0, RegisterID src, RegisterID dest) + { + ASSERT(r0 == SH4Registers::r0); + m_assembler.movwR0mr(src, dest); + extuw(dest, dest); + } + + void load16Signed(RegisterID r0, RegisterID src, RegisterID dest) + { + ASSERT(r0 == SH4Registers::r0); + m_assembler.movwR0mr(src, dest); + } + + void load16(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + + if (address.offset) + add32(TrustedImm32(address.offset), scr); + if (address.base == SH4Registers::r0) + load16(address.base, scr, dest); + else { + add32(address.base, scr); + load16(scr, dest); + } + + releaseScratch(scr); + } + + void load16Signed(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + + if (address.offset) + add32(TrustedImm32(address.offset), scr); + if (address.base == SH4Registers::r0) + load16Signed(address.base, scr, dest); + else { + add32(address.base, scr); + load16Signed(scr, dest); + } + + releaseScratch(scr); + } + + void store8(RegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + + m_assembler.movbRegMem(src, scr); + + releaseScratch(scr); + } + + void store16(RegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + + m_assembler.movwRegMem(src, scr); + + releaseScratch(scr); + } + + void store32(RegisterID src, ImplicitAddress address) + { + RegisterID scr = claimScratch(); + store32(src, address.offset, address.base, scr); + releaseScratch(scr); + } + + void store32(RegisterID src, int offset, RegisterID base, RegisterID scr) + { + if (!offset) { + m_assembler.movlRegMem(src, base); + return; + } + + if ((offset >=0) && (offset < 64)) { + m_assembler.movlRegMem(src, offset >> 2, base); + return; + } + + m_assembler.loadConstant((offset), scr); + if (scr == SH4Registers::r0) { + m_assembler.movlRegMemr0(src, base); + return; + } + + m_assembler.addlRegReg(base, scr); + m_assembler.movlRegMem(src, scr); + } + + void store32(RegisterID src, RegisterID offset, RegisterID base) + { + ASSERT(offset == SH4Registers::r0); + m_assembler.movlRegMemr0(src, base); + } + + void store32(RegisterID src, RegisterID dst) + { + m_assembler.movlRegMem(src, dst); + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.loadConstant((imm.m_value), scr); + store32(scr, address.offset, address.base, scr1); + releaseScratch(scr); + releaseScratch(scr1); + } + + void store32(RegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + store32(src, Address(scr, address.offset)); + + releaseScratch(scr); + } + + void store32(TrustedImm32 imm, void* address) + { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.loadConstant((imm.m_value), scr); + m_assembler.loadConstant(reinterpret_cast(address), scr1); + m_assembler.movlRegMem(scr, scr1); + releaseScratch(scr); + releaseScratch(scr1); + } + + void store32(RegisterID src, void* address) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(reinterpret_cast(address), scr); + m_assembler.movlRegMem(src, scr); + releaseScratch(scr); + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + RegisterID scr = claimScratch(); + DataLabel32 label(this); + m_assembler.loadConstantUnReusable(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlMemReg(scr, dest); + releaseScratch(scr); + return label; + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + RegisterID scr = claimScratch(); + DataLabel32 label(this); + m_assembler.loadConstantUnReusable(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlRegMem(src, scr); + releaseScratch(scr); + return label; + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabelCompact dataLabel(this); + ASSERT(address.offset <= MaximumCompactPtrAlignedAddressOffset); + ASSERT(address.offset >= 0); + m_assembler.movlMemRegCompact(address.offset >> 2, address.base, dest); + return dataLabel; + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result(this); + + RegisterID scr = claimScratch(); + m_assembler.movImm8(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlMemReg(scr, dest); + releaseScratch(scr); + + return result; + } + + // Floating-point operations + + static bool supportsFloatingPoint() { return true; } + static bool supportsFloatingPointTruncate() { return true; } + static bool supportsFloatingPointSqrt() { return true; } + static bool supportsFloatingPointAbs() { return false; } + + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + m_assembler.fldsfpul((FPRegisterID)(src + 1)); + m_assembler.stsfpulReg(dest1); + m_assembler.fldsfpul(src); + m_assembler.stsfpulReg(dest2); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + UNUSED_PARAM(scratch); + m_assembler.ldsrmfpul(src1); + m_assembler.fstsfpul((FPRegisterID)(dest + 1)); + m_assembler.ldsrmfpul(src2); + m_assembler.fstsfpul(dest); + } + + void loadFloat(BaseIndex address, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.fmovsReadrm(scr, dest); + releaseScratch(scr); + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); + m_assembler.fmovsReadrm(scr, dest); + releaseScratch(scr); + } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + + m_assembler.loadConstant(address.offset, scr); + if (address.base == SH4Registers::r0) { + m_assembler.fmovsReadr0r(scr, (FPRegisterID)(dest + 1)); + m_assembler.addlImm8r(4, scr); + m_assembler.fmovsReadr0r(scr, dest); + releaseScratch(scr); + return; + } + + m_assembler.addlRegReg(address.base, scr); + m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); + m_assembler.fmovsReadrm(scr, dest); + releaseScratch(scr); + } + + void loadDouble(const void* address, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(reinterpret_cast(address), scr); + m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); + m_assembler.fmovsReadrm(scr, dest); + releaseScratch(scr); + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.fmovsWriterm(src, scr); + + releaseScratch(scr); + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.fmovsWriterm((FPRegisterID)(src + 1), scr); + m_assembler.addlImm8r(4, scr); + m_assembler.fmovsWriterm(src, scr); + releaseScratch(scr); + } + + void storeDouble(FPRegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.fmovsWriterm((FPRegisterID)(src + 1), scr); + m_assembler.addlImm8r(4, scr); + m_assembler.fmovsWriterm(src, scr); + + releaseScratch(scr); + } + + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + if (op1 == dest) + m_assembler.daddRegReg(op2, dest); + else { + m_assembler.dmovRegReg(op1, dest); + m_assembler.daddRegReg(op2, dest); + } + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.daddRegReg(src, dest); + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + loadDouble(address.m_ptr, fscratch); + addDouble(fscratch, dest); + } + + void addDouble(Address address, FPRegisterID dest) + { + loadDouble(address, fscratch); + addDouble(fscratch, dest); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.dsubRegReg(src, dest); + } + + void subDouble(Address address, FPRegisterID dest) + { + loadDouble(address, fscratch); + subDouble(fscratch, dest); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.dmulRegReg(src, dest); + } + + void mulDouble(Address address, FPRegisterID dest) + { + loadDouble(address, fscratch); + mulDouble(fscratch, dest); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.ddivRegReg(src, dest); + } + + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.fldsfpul(src); + m_assembler.dcnvsd(dst); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.dcnvds(src); + m_assembler.fstsfpul(dst); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.ldsrmfpul(src); + m_assembler.floatfpulDreg(dest); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(reinterpret_cast(src.m_ptr), scr); + convertInt32ToDouble(scr, dest); + releaseScratch(scr); + } + + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + load32(src, scr); + convertInt32ToDouble(scr, dest); + releaseScratch(scr); + } + + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + Jump m_jump; + JumpList end; + + if (dest != SH4Registers::r0) + move(SH4Registers::r0, scr1); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 68, sizeof(uint32_t)); + move(scr, SH4Registers::r0); + m_assembler.andlImm8r(0x3, SH4Registers::r0); + m_assembler.cmpEqImmR0(0x0, SH4Registers::r0); + m_jump = Jump(m_assembler.jne(), SH4Assembler::JumpNear); + if (dest != SH4Registers::r0) + move(scr1, SH4Registers::r0); + + load32(scr, dest); + end.append(Jump(m_assembler.bra(), SH4Assembler::JumpNear)); + m_assembler.nop(); + m_jump.link(this); + m_assembler.andlImm8r(0x1, SH4Registers::r0); + m_assembler.cmpEqImmR0(0x0, SH4Registers::r0); + + if (dest != SH4Registers::r0) + move(scr1, SH4Registers::r0); + + m_jump = Jump(m_assembler.jne(), SH4Assembler::JumpNear); + load16(scr, scr1); + add32(TrustedImm32(2), scr); + load16(scr, dest); + m_assembler.shllImm8r(16, dest); + or32(scr1, dest); + end.append(Jump(m_assembler.bra(), SH4Assembler::JumpNear)); + m_assembler.nop(); + m_jump.link(this); + load8(scr, scr1); + add32(TrustedImm32(1), scr); + load16(scr, dest); + m_assembler.shllImm8r(8, dest); + or32(dest, scr1); + add32(TrustedImm32(2), scr); + load8(scr, dest); + m_assembler.shllImm8r(8, dest); + m_assembler.shllImm8r(16, dest); + or32(scr1, dest); + end.link(this); + + releaseScratch(scr); + releaseScratch(scr1); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + RegisterID scr = scratchReg3; + load32WithUnalignedHalfWords(left, scr); + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testlRegReg(scr, scr); + else + compare32(right.m_value, scr, cond); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) + { + m_assembler.movImm8(0, scratchReg3); + convertInt32ToDouble(scratchReg3, scratch); + return branchDouble(DoubleNotEqual, reg, scratch); + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) + { + m_assembler.movImm8(0, scratchReg3); + convertInt32ToDouble(scratchReg3, scratch); + return branchDouble(DoubleEqualOrUnordered, reg, scratch); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + if (cond == DoubleEqual) { + m_assembler.dcmppeq(right, left); + return branchTrue(); + } + + if (cond == DoubleNotEqual) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppeq(right, left); + releaseScratch(scr); + Jump m_jump = branchFalse(); + end.link(this); + return m_jump; + } + + if (cond == DoubleGreaterThan) { + m_assembler.dcmppgt(right, left); + return branchTrue(); + } + + if (cond == DoubleGreaterThanOrEqual) { + m_assembler.dcmppgt(left, right); + return branchFalse(); + } + + if (cond == DoubleLessThan) { + m_assembler.dcmppgt(left, right); + return branchTrue(); + } + + if (cond == DoubleLessThanOrEqual) { + m_assembler.dcmppgt(right, left); + return branchFalse(); + } + + if (cond == DoubleEqualOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppeq(left, right); + Jump m_jump = Jump(m_assembler.je()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + if (cond == DoubleGreaterThanOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppgt(right, left); + Jump m_jump = Jump(m_assembler.je()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + if (cond == DoubleGreaterThanOrEqualOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppgt(left, right); + Jump m_jump = Jump(m_assembler.jne()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + if (cond == DoubleLessThanOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppgt(left, right); + Jump m_jump = Jump(m_assembler.je()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + if (cond == DoubleLessThanOrEqualOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppgt(right, left); + Jump m_jump = Jump(m_assembler.jne()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + ASSERT(cond == DoubleNotEqualOrUnordered); + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppeq(right, left); + Jump m_jump = Jump(m_assembler.jne()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + Jump branchTrue() + { + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 6, sizeof(uint32_t)); + Jump m_jump = Jump(m_assembler.je()); + m_assembler.extraInstrForBranch(scratchReg3); + return m_jump; + } + + Jump branchFalse() + { + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 6, sizeof(uint32_t)); + Jump m_jump = Jump(m_assembler.jne()); + m_assembler.extraInstrForBranch(scratchReg3); + return m_jump; + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + RegisterID scr = claimScratch(); + move(left.index, scr); + lshift32(TrustedImm32(left.scale), scr); + add32(left.base, scr); + load32(scr, left.offset, scr); + compare32(right.m_value, scr, cond); + releaseScratch(scr); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dest) + { + if (dest != src) + m_assembler.dmovRegReg(src, dest); + m_assembler.dsqrt(dest); + } + + void absDouble(FPRegisterID, FPRegisterID) + { + ASSERT_NOT_REACHED(); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + RegisterID addressTempRegister = claimScratch(); + load8(address, addressTempRegister); + Jump jmp = branchTest32(cond, addressTempRegister, mask); + releaseScratch(addressTempRegister); + return jmp; + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + RegisterID addressTempRegister = claimScratch(); + move(TrustedImmPtr(address.m_ptr), addressTempRegister); + load8(Address(addressTempRegister), addressTempRegister); + Jump jmp = branchTest32(cond, addressTempRegister, mask); + releaseScratch(addressTempRegister); + return jmp; + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest) + move(src, dest); + } + + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + RegisterID addressTempRegister = claimScratch(); + load8(left, addressTempRegister); + Jump jmp = branch32(cond, addressTempRegister, right); + releaseScratch(addressTempRegister); + return jmp; + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + RegisterID addressTempRegister = claimScratch(); + load8(left, addressTempRegister); + compare32(cond, addressTempRegister, right, dest); + releaseScratch(addressTempRegister); + } + + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + m_assembler.ftrcdrmfpul(src); + m_assembler.stsfpulReg(dest); + m_assembler.loadConstant(0x7fffffff, scratchReg3); + m_assembler.cmplRegReg(dest, scratchReg3, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 14, sizeof(uint32_t)); + m_assembler.branch(BT_OPCODE, 2); + m_assembler.addlImm8r(1, scratchReg3); + m_assembler.cmplRegReg(dest, scratchReg3, SH4Condition(Equal)); + return branchTrue(); + } + + // Stack manipulation operations + + void pop(RegisterID dest) + { + m_assembler.popReg(dest); + } + + void push(RegisterID src) + { + m_assembler.pushReg(src); + } + + void push(Address address) + { + if (!address.offset) { + push(address.base); + return; + } + + if ((address.offset < 0) || (address.offset >= 64)) { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlMemReg(scr, SH4Registers::sp); + m_assembler.addlImm8r(-4, SH4Registers::sp); + releaseScratch(scr); + return; + } + + m_assembler.movlMemReg(address.offset >> 2, address.base, SH4Registers::sp); + m_assembler.addlImm8r(-4, SH4Registers::sp); + } + + void push(TrustedImm32 imm) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm.m_value, scr); + push(scr); + releaseScratch(scr); + } + + // Register move operations + + void move(TrustedImm32 imm, RegisterID dest) + { + m_assembler.loadConstant(imm.m_value, dest); + } + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + m_assembler.ensureSpace(m_assembler.maxInstructionSize, sizeof(uint32_t)); + DataLabelPtr dataLabel(this); + m_assembler.loadConstantUnReusable(reinterpret_cast(initialValue.m_value), dest); + return dataLabel; + } + + void move(RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.movlRegReg(src, dest); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + m_assembler.loadConstant(imm.asIntptr(), dest); + } + + void extuw(RegisterID src, RegisterID dst) + { + m_assembler.extuw(src, dst); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmplRegReg(right, left, SH4Condition(cond)); + if (cond != NotEqual) { + m_assembler.movt(dest); + return; + } + + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 4); + m_assembler.movImm8(0, dest); + m_assembler.branch(BT_OPCODE, 0); + m_assembler.movImm8(1, dest); + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + if (left != dest) { + move(right, dest); + compare32(cond, left, dest, dest); + return; + } + + RegisterID scr = claimScratch(); + move(right, scr); + compare32(cond, left, scr, dest); + releaseScratch(scr); + } + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + ASSERT((cond == Zero) || (cond == NonZero)); + + load8(address, dest); + if (mask.m_value == -1) + compare32(0, dest, static_cast(cond)); + else + testlImm(mask.m_value, dest); + if (cond != NonZero) { + m_assembler.movt(dest); + return; + } + + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 4); + m_assembler.movImm8(0, dest); + m_assembler.branch(BT_OPCODE, 0); + m_assembler.movImm8(1, dest); + } + + void loadPtrLinkReg(ImplicitAddress address) + { + RegisterID scr = claimScratch(); + load32(address, scr); + m_assembler.ldspr(scr); + releaseScratch(scr); + } + + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) + { + m_assembler.cmplRegReg(right, left, SH4Condition(cond)); + /* BT label => BF off + nop LDR reg + nop braf @reg + nop nop + */ + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testlRegReg(left, left); + else + compare32(right.m_value, left, cond); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + compare32(right.offset, right.base, left, cond); + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + compare32(right, left.offset, left.base, cond); + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + compare32(right.m_value, left.offset, left.base, cond); + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + RegisterID scr = claimScratch(); + + move(TrustedImm32(reinterpret_cast(left.m_ptr)), scr); + m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); + releaseScratch(scr); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + RegisterID addressTempRegister = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(left.m_ptr), addressTempRegister); + m_assembler.movlMemReg(addressTempRegister, addressTempRegister); + compare32(right.m_value, addressTempRegister, cond); + releaseScratch(addressTempRegister); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(right.m_value & 0xFFFFFF00)); + RegisterID scr = claimScratch(); + + move(left.index, scr); + lshift32(TrustedImm32(left.scale), scr); + + if (left.offset) + add32(TrustedImm32(left.offset), scr); + add32(left.base, scr); + load8(scr, scr); + RegisterID scr1 = claimScratch(); + m_assembler.loadConstant(right.m_value, scr1); + releaseScratch(scr); + releaseScratch(scr1); + + return branch32(cond, scr, scr1); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + ASSERT((cond == Zero) || (cond == NonZero)); + + m_assembler.testlRegReg(reg, mask); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + + if (mask.m_value == -1) + m_assembler.testlRegReg(reg, reg); + else + testlImm(mask.m_value, reg); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + + if (mask.m_value == -1) + compare32(0, address.offset, address.base, static_cast(cond)); + else + testImm(mask.m_value, address.offset, address.base); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + load32(scr, address.offset, scr); + + if (mask.m_value == -1) + m_assembler.testlRegReg(scr, scr); + else + testlImm(mask.m_value, scr); + + releaseScratch(scr); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump jump() + { + return Jump(m_assembler.jmp()); + } + + void jump(RegisterID target) + { + m_assembler.jmpReg(target); + } + + void jump(Address address) + { + RegisterID scr = claimScratch(); + + if ((address.offset < 0) || (address.offset >= 64)) { + m_assembler.loadConstant(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlMemReg(scr, scr); + } else if (address.offset) + m_assembler.movlMemReg(address.offset >> 2, address.base, scr); + else + m_assembler.movlMemReg(address.base, scr); + m_assembler.jmpReg(scr); + + releaseScratch(scr); + } + + // Arithmetic control flow operations + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (cond == Overflow) { + m_assembler.addvlRegReg(src, dest); + return branchTrue(); + } + + if (cond == Signed) { + m_assembler.addlRegReg(src, dest); + // Check if dest is negative + m_assembler.cmppz(dest); + return branchFalse(); + } + + m_assembler.addlRegReg(src, dest); + compare32(0, dest, Equal); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + move(imm, scratchReg3); + return branchAdd32(cond, scratchReg3, dest); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (src != dest) + move(src, dest); + + if (cond == Overflow) { + move(imm, scratchReg3); + m_assembler.addvlRegReg(scratchReg3, dest); + return branchTrue(); + } + + add32(imm, dest); + + if (cond == Signed) { + m_assembler.cmppz(dest); + return branchFalse(); + } + + compare32(0, dest, Equal); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (cond == Overflow) { + RegisterID scr1 = claimScratch(); + RegisterID scr = claimScratch(); + m_assembler.dmullRegReg(src, dest); + m_assembler.stsmacl(dest); + m_assembler.movImm8(-31, scr); + m_assembler.movlRegReg(dest, scr1); + m_assembler.shaRegReg(scr1, scr); + m_assembler.stsmach(scr); + m_assembler.cmplRegReg(scr, scr1, SH4Condition(Equal)); + releaseScratch(scr1); + releaseScratch(scr); + return branchFalse(); + } + + m_assembler.imullRegReg(src, dest); + m_assembler.stsmacl(dest); + if (cond == Signed) { + // Check if dest is negative + m_assembler.cmppz(dest); + return branchFalse(); + } + + compare32(0, dest, static_cast(cond)); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + move(imm, scratchReg3); + if (src != dest) + move(src, dest); + + return branchMul32(cond, scratchReg3, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (cond == Overflow) { + m_assembler.subvlRegReg(src, dest); + return branchTrue(); + } + + if (cond == Signed) { + // Check if dest is negative + m_assembler.sublRegReg(src, dest); + compare32(0, dest, LessThan); + return branchTrue(); + } + + sub32(src, dest); + compare32(0, dest, static_cast(cond)); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + move(imm, scratchReg3); + return branchSub32(cond, scratchReg3, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + move(imm, scratchReg3); + if (src != dest) + move(src, dest); + return branchSub32(cond, scratchReg3, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + if (src1 != dest) + move(src1, dest); + return branchSub32(cond, src2, dest); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (cond == Signed) { + or32(src, dest); + compare32(0, dest, static_cast(LessThan)); + return branchTrue(); + } + + or32(src, dest); + compare32(0, dest, static_cast(cond)); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + m_assembler.ftrcdrmfpul(src); + m_assembler.stsfpulReg(dest); + convertInt32ToDouble(dest, fscratch); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, fscratch, src)); + + if (dest == SH4Registers::r0) + m_assembler.cmpEqImmR0(0, dest); + else { + m_assembler.movImm8(0, scratchReg3); + m_assembler.cmplRegReg(scratchReg3, dest, SH4Condition(Equal)); + } + failureCases.append(branchTrue()); + } + + void neg32(RegisterID dst) + { + m_assembler.neg(dst, dst); + } + + void urshift32(RegisterID shiftamount, RegisterID dest) + { + if (shiftamount == SH4Registers::r0) + m_assembler.andlImm8r(0x1f, shiftamount); + else { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(0x1f, scr); + m_assembler.andlRegReg(scr, shiftamount); + releaseScratch(scr); + } + m_assembler.neg(shiftamount, shiftamount); + m_assembler.shllRegReg(dest, shiftamount); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(-(imm.m_value & 0x1f), scr); + m_assembler.shaRegReg(dest, scr); + releaseScratch(scr); + } + + void urshift32(RegisterID src, TrustedImm32 shiftamount, RegisterID dest) + { + if (src != dest) + move(src, dest); + + urshift32(shiftamount, dest); + } + + Call call() + { + return Call(m_assembler.call(), Call::Linkable); + } + + Call nearCall() + { + return Call(m_assembler.call(), Call::LinkableNear); + } + + Call call(RegisterID target) + { + return Call(m_assembler.call(target), Call::None); + } + + void call(Address address, RegisterID target) + { + load32(address.base, address.offset, target); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 2); + m_assembler.branch(JSR_OPCODE, target); + m_assembler.nop(); + } + + void breakpoint() + { + m_assembler.bkpt(); + m_assembler.nop(); + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + RegisterID dataTempRegister = claimScratch(); + + dataLabel = moveWithPatch(initialRightValue, dataTempRegister); + m_assembler.cmplRegReg(dataTempRegister, left, SH4Condition(cond)); + releaseScratch(dataTempRegister); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + RegisterID scr = claimScratch(); + + m_assembler.loadConstant(left.offset, scr); + m_assembler.addlRegReg(left.base, scr); + m_assembler.movlMemReg(scr, scr); + RegisterID scr1 = claimScratch(); + dataLabel = moveWithPatch(initialRightValue, scr1); + m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); + releaseScratch(scr); + releaseScratch(scr1); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + void ret() + { + m_assembler.ret(); + m_assembler.nop(); + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + RegisterID scr = claimScratch(); + DataLabelPtr label = moveWithPatch(initialValue, scr); + store32(scr, address); + releaseScratch(scr); + return label; + } + + DataLabelPtr storePtrWithPatch(ImplicitAddress address) { return storePtrWithPatch(TrustedImmPtr(0), address); } + + int sizeOfConstantPool() + { + return m_assembler.sizeOfConstantPool(); + } + + Call tailRecursiveCall() + { + RegisterID scr = claimScratch(); + + m_assembler.loadConstantUnReusable(0x0, scr, true); + Jump m_jump = Jump(m_assembler.jmp(scr)); + releaseScratch(scr); + + return Call::fromTailJump(m_jump); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + oldJump.link(this); + return tailRecursiveCall(); + } + + void nop() + { + m_assembler.nop(); + } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(reinterpret_cast(SH4Assembler::readCallTarget(call.dataLocation()))); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + ASSERT_NOT_REACHED(); + } + + static ptrdiff_t maxJumpReplacementSize() + { + ASSERT_NOT_REACHED(); + return 0; + } + + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + return label.labelAtOffset(0); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) + { + SH4Assembler::revertJump(instructionStart.dataLocation(), reinterpret_cast(initialValue) & 0xffff); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + UNREACHABLE_FOR_PLATFORM(); + } + +protected: + SH4Assembler::Condition SH4Condition(RelationalCondition cond) + { + return static_cast(cond); + } + + SH4Assembler::Condition SH4Condition(ResultCondition cond) + { + return static_cast(cond); + } +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void*, Call, FunctionPtr); + static void repatchCall(CodeLocationCall, CodeLocationLabel); + static void repatchCall(CodeLocationCall, FunctionPtr); +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerSH4_h diff --git a/3rdparty/masm/assembler/MacroAssemblerX86.h b/3rdparty/masm/assembler/MacroAssemblerX86.h new file mode 100644 index 0000000000..27a030edfd --- /dev/null +++ b/3rdparty/masm/assembler/MacroAssemblerX86.h @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerX86_h +#define MacroAssemblerX86_h + +#if ENABLE(ASSEMBLER) && CPU(X86) + +#include "MacroAssemblerX86Common.h" + +namespace JSC { + +class MacroAssemblerX86 : public MacroAssemblerX86Common { +public: + static const Scale ScalePtr = TimesFour; + + using MacroAssemblerX86Common::add32; + using MacroAssemblerX86Common::and32; + using MacroAssemblerX86Common::branchAdd32; + using MacroAssemblerX86Common::branchSub32; + using MacroAssemblerX86Common::sub32; + using MacroAssemblerX86Common::or32; + using MacroAssemblerX86Common::load32; + using MacroAssemblerX86Common::store32; + using MacroAssemblerX86Common::store8; + using MacroAssemblerX86Common::branch32; + using MacroAssemblerX86Common::call; + using MacroAssemblerX86Common::jump; + using MacroAssemblerX86Common::addDouble; + using MacroAssemblerX86Common::loadDouble; + using MacroAssemblerX86Common::storeDouble; + using MacroAssemblerX86Common::convertInt32ToDouble; + using MacroAssemblerX86Common::branchTest8; + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.leal_mr(imm.m_value, src, dest); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.addl_im(imm.m_value, address.m_ptr); + } + + void add32(AbsoluteAddress address, RegisterID dest) + { + m_assembler.addl_mr(address.m_ptr, dest); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.addl_im(imm.m_value, address.m_ptr); + m_assembler.adcl_im(imm.m_value >> 31, reinterpret_cast(address.m_ptr) + sizeof(int32_t)); + } + + void and32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.andl_im(imm.m_value, address.m_ptr); + } + + void or32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.orl_im(imm.m_value, address.m_ptr); + } + + void or32(RegisterID reg, AbsoluteAddress address) + { + m_assembler.orl_rm(reg, address.m_ptr); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.subl_im(imm.m_value, address.m_ptr); + } + + void load32(const void* address, RegisterID dest) + { + m_assembler.movl_mr(address, dest); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result = ConvertibleLoadLabel(this); + m_assembler.movl_mr(address.offset, address.base, dest); + return result; + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + m_assembler.addsd_mr(address.m_ptr, dest); + } + + void storeDouble(FPRegisterID src, const void* address) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_rm(src, address); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + m_assembler.cvtsi2sd_mr(src.m_ptr, dest); + } + + void store32(TrustedImm32 imm, void* address) + { + m_assembler.movl_i32m(imm.m_value, address); + } + + void store32(RegisterID src, void* address) + { + m_assembler.movl_rm(src, address); + } + + void store8(TrustedImm32 imm, void* address) + { + ASSERT(-128 <= imm.m_value && imm.m_value < 128); + m_assembler.movb_i8m(imm.m_value, address); + } + + // Possibly clobbers src. + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + movePackedToInt32(src, dest1); + rshiftPacked(TrustedImm32(32), src); + movePackedToInt32(src, dest2); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + moveInt32ToPacked(src1, dest); + moveInt32ToPacked(src2, scratch); + lshiftPacked(TrustedImm32(32), scratch); + orPacked(scratch, dest); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) + { + m_assembler.addl_im(imm.m_value, dest.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) + { + m_assembler.subl_im(imm.m_value, dest.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + m_assembler.cmpl_rm(right, left.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + m_assembler.cmpl_im(right.m_value, left.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Call call() + { + return Call(m_assembler.call(), Call::Linkable); + } + + // Address is a memory location containing the address to jump to + void jump(AbsoluteAddress address) + { + m_assembler.jmp_m(address.m_ptr); + } + + Call tailRecursiveCall() + { + return Call::fromTailJump(jump()); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + return Call::fromTailJump(oldJump); + } + + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + padBeforePatch(); + m_assembler.movl_i32r(initialValue.asIntptr(), dest); + return DataLabelPtr(this); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT(mask.m_value >= -128 && mask.m_value <= 255); + if (mask.m_value == -1) + m_assembler.cmpb_im(0, address.m_ptr); + else + m_assembler.testb_im(mask.m_value, address.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + padBeforePatch(); + m_assembler.cmpl_ir_force32(initialRightValue.asIntptr(), left); + dataLabel = DataLabelPtr(this); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + padBeforePatch(); + m_assembler.cmpl_im_force32(initialRightValue.asIntptr(), left.offset, left.base); + dataLabel = DataLabelPtr(this); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + padBeforePatch(); + m_assembler.movl_i32m(initialValue.asIntptr(), address.offset, address.base); + return DataLabelPtr(this); + } + + static bool supportsFloatingPoint() { return isSSE2Present(); } + // See comment on MacroAssemblerARMv7::supportsFloatingPointTruncate() + static bool supportsFloatingPointTruncate() { return isSSE2Present(); } + static bool supportsFloatingPointSqrt() { return isSSE2Present(); } + static bool supportsFloatingPointAbs() { return isSSE2Present(); } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + intptr_t offset = reinterpret_cast(call.dataLocation())[-1]; + return FunctionPtr(reinterpret_cast(reinterpret_cast(call.dataLocation()) + offset)); + } + + static bool canJumpReplacePatchableBranchPtrWithPatch() { return true; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + const int opcodeBytes = 1; + const int modRMBytes = 1; + const int immediateBytes = 4; + const int totalBytes = opcodeBytes + modRMBytes + immediateBytes; + ASSERT(totalBytes >= maxJumpReplacementSize()); + return label.labelAtOffset(-totalBytes); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) + { + const int opcodeBytes = 1; + const int modRMBytes = 1; + const int offsetBytes = 0; + const int immediateBytes = 4; + const int totalBytes = opcodeBytes + modRMBytes + offsetBytes + immediateBytes; + ASSERT(totalBytes >= maxJumpReplacementSize()); + return label.labelAtOffset(-totalBytes); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID reg, void* initialValue) + { + X86Assembler::revertJumpTo_cmpl_ir_force32(instructionStart.executableAddress(), reinterpret_cast(initialValue), reg); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address address, void* initialValue) + { + ASSERT(!address.offset); + X86Assembler::revertJumpTo_cmpl_im_force32(instructionStart.executableAddress(), reinterpret_cast(initialValue), 0, address.base); + } + +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + X86Assembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + X86Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + X86Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerX86_h diff --git a/3rdparty/masm/assembler/MacroAssemblerX86Common.h b/3rdparty/masm/assembler/MacroAssemblerX86Common.h new file mode 100644 index 0000000000..53cb80c210 --- /dev/null +++ b/3rdparty/masm/assembler/MacroAssemblerX86Common.h @@ -0,0 +1,1541 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerX86Common_h +#define MacroAssemblerX86Common_h + +#if ENABLE(ASSEMBLER) + +#include "X86Assembler.h" +#include "AbstractMacroAssembler.h" + +namespace JSC { + +class MacroAssemblerX86Common : public AbstractMacroAssembler { +protected: +#if CPU(X86_64) + static const X86Registers::RegisterID scratchRegister = X86Registers::r11; +#endif + + static const int DoubleConditionBitInvert = 0x10; + static const int DoubleConditionBitSpecial = 0x20; + static const int DoubleConditionBits = DoubleConditionBitInvert | DoubleConditionBitSpecial; + +public: + typedef X86Assembler::FPRegisterID FPRegisterID; + typedef X86Assembler::XMMRegisterID XMMRegisterID; + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return value >= -128 && value <= 127; + } + + enum RelationalCondition { + Equal = X86Assembler::ConditionE, + NotEqual = X86Assembler::ConditionNE, + Above = X86Assembler::ConditionA, + AboveOrEqual = X86Assembler::ConditionAE, + Below = X86Assembler::ConditionB, + BelowOrEqual = X86Assembler::ConditionBE, + GreaterThan = X86Assembler::ConditionG, + GreaterThanOrEqual = X86Assembler::ConditionGE, + LessThan = X86Assembler::ConditionL, + LessThanOrEqual = X86Assembler::ConditionLE + }; + + enum ResultCondition { + Overflow = X86Assembler::ConditionO, + Signed = X86Assembler::ConditionS, + Zero = X86Assembler::ConditionE, + NonZero = X86Assembler::ConditionNE + }; + + enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleEqual = X86Assembler::ConditionE | DoubleConditionBitSpecial, + DoubleNotEqual = X86Assembler::ConditionNE, + DoubleGreaterThan = X86Assembler::ConditionA, + DoubleGreaterThanOrEqual = X86Assembler::ConditionAE, + DoubleLessThan = X86Assembler::ConditionA | DoubleConditionBitInvert, + DoubleLessThanOrEqual = X86Assembler::ConditionAE | DoubleConditionBitInvert, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = X86Assembler::ConditionE, + DoubleNotEqualOrUnordered = X86Assembler::ConditionNE | DoubleConditionBitSpecial, + DoubleGreaterThanOrUnordered = X86Assembler::ConditionB | DoubleConditionBitInvert, + DoubleGreaterThanOrEqualOrUnordered = X86Assembler::ConditionBE | DoubleConditionBitInvert, + DoubleLessThanOrUnordered = X86Assembler::ConditionB, + DoubleLessThanOrEqualOrUnordered = X86Assembler::ConditionBE, + }; + COMPILE_ASSERT( + !((X86Assembler::ConditionE | X86Assembler::ConditionNE | X86Assembler::ConditionA | X86Assembler::ConditionAE | X86Assembler::ConditionB | X86Assembler::ConditionBE) & DoubleConditionBits), + DoubleConditionBits_should_not_interfere_with_X86Assembler_Condition_codes); + + static const RegisterID stackPointerRegister = X86Registers::esp; + +#if ENABLE(JIT_CONSTANT_BLINDING) + static bool shouldBlindForSpecificArch(uint32_t value) { return value >= 0x00ffffff; } +#if CPU(X86_64) + static bool shouldBlindForSpecificArch(uint64_t value) { return value >= 0x00ffffff; } +#if OS(DARWIN) // On 64-bit systems other than DARWIN uint64_t and uintptr_t are the same type so overload is prohibited. + static bool shouldBlindForSpecificArch(uintptr_t value) { return value >= 0x00ffffff; } +#endif +#endif +#endif + + // Integer arithmetic operations: + // + // Operations are typically two operand - operation(source, srcDst) + // For many operations the source may be an TrustedImm32, the srcDst operand + // may often be a memory location (explictly described using an Address + // object). + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.addl_rr(src, dest); + } + + void add32(TrustedImm32 imm, Address address) + { + m_assembler.addl_im(imm.m_value, address.offset, address.base); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.addl_ir(imm.m_value, dest); + } + + void add32(Address src, RegisterID dest) + { + m_assembler.addl_mr(src.offset, src.base, dest); + } + + void add32(RegisterID src, Address dest) + { + m_assembler.addl_rm(src, dest.offset, dest.base); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.leal_mr(imm.m_value, src, dest); + } + + void and32(RegisterID src, RegisterID dest) + { + m_assembler.andl_rr(src, dest); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.andl_ir(imm.m_value, dest); + } + + void and32(RegisterID src, Address dest) + { + m_assembler.andl_rm(src, dest.offset, dest.base); + } + + void and32(Address src, RegisterID dest) + { + m_assembler.andl_mr(src.offset, src.base, dest); + } + + void and32(TrustedImm32 imm, Address address) + { + m_assembler.andl_im(imm.m_value, address.offset, address.base); + } + + void and32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + zeroExtend32ToPtr(op1, dest); + else if (op1 == dest) + and32(op2, dest); + else { + move(op2, dest); + and32(op1, dest); + } + } + + void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(src, dest); + and32(imm, dest); + } + + void lshift32(RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (shift_amount == X86Registers::ecx) + m_assembler.shll_CLr(dest); + else { + // On x86 we can only shift by ecx; if asked to shift by another register we'll + // need rejig the shift amount into ecx first, and restore the registers afterwards. + // If we dest is ecx, then shift the swapped register! + swap(shift_amount, X86Registers::ecx); + m_assembler.shll_CLr(dest == X86Registers::ecx ? shift_amount : dest); + swap(shift_amount, X86Registers::ecx); + } + } + + void lshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (src != dest) + move(src, dest); + lshift32(shift_amount, dest); + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.shll_i8r(imm.m_value, dest); + } + + void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (src != dest) + move(src, dest); + lshift32(imm, dest); + } + + void mul32(RegisterID src, RegisterID dest) + { + m_assembler.imull_rr(src, dest); + } + + void mul32(Address src, RegisterID dest) + { + m_assembler.imull_mr(src.offset, src.base, dest); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.imull_i32r(src, imm.m_value, dest); + } + + void neg32(RegisterID srcDest) + { + m_assembler.negl_r(srcDest); + } + + void neg32(Address srcDest) + { + m_assembler.negl_m(srcDest.offset, srcDest.base); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orl_rr(src, dest); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.orl_ir(imm.m_value, dest); + } + + void or32(RegisterID src, Address dest) + { + m_assembler.orl_rm(src, dest.offset, dest.base); + } + + void or32(Address src, RegisterID dest) + { + m_assembler.orl_mr(src.offset, src.base, dest); + } + + void or32(TrustedImm32 imm, Address address) + { + m_assembler.orl_im(imm.m_value, address.offset, address.base); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + zeroExtend32ToPtr(op1, dest); + else if (op1 == dest) + or32(op2, dest); + else { + move(op2, dest); + or32(op1, dest); + } + } + + void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(src, dest); + or32(imm, dest); + } + + void rshift32(RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (shift_amount == X86Registers::ecx) + m_assembler.sarl_CLr(dest); + else { + // On x86 we can only shift by ecx; if asked to shift by another register we'll + // need rejig the shift amount into ecx first, and restore the registers afterwards. + // If we dest is ecx, then shift the swapped register! + swap(shift_amount, X86Registers::ecx); + m_assembler.sarl_CLr(dest == X86Registers::ecx ? shift_amount : dest); + swap(shift_amount, X86Registers::ecx); + } + } + + void rshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (src != dest) + move(src, dest); + rshift32(shift_amount, dest); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.sarl_i8r(imm.m_value, dest); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (src != dest) + move(src, dest); + rshift32(imm, dest); + } + + void urshift32(RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (shift_amount == X86Registers::ecx) + m_assembler.shrl_CLr(dest); + else { + // On x86 we can only shift by ecx; if asked to shift by another register we'll + // need rejig the shift amount into ecx first, and restore the registers afterwards. + // If we dest is ecx, then shift the swapped register! + swap(shift_amount, X86Registers::ecx); + m_assembler.shrl_CLr(dest == X86Registers::ecx ? shift_amount : dest); + swap(shift_amount, X86Registers::ecx); + } + } + + void urshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (src != dest) + move(src, dest); + urshift32(shift_amount, dest); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.shrl_i8r(imm.m_value, dest); + } + + void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (src != dest) + move(src, dest); + urshift32(imm, dest); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.subl_rr(src, dest); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.subl_ir(imm.m_value, dest); + } + + void sub32(TrustedImm32 imm, Address address) + { + m_assembler.subl_im(imm.m_value, address.offset, address.base); + } + + void sub32(Address src, RegisterID dest) + { + m_assembler.subl_mr(src.offset, src.base, dest); + } + + void sub32(RegisterID src, Address dest) + { + m_assembler.subl_rm(src, dest.offset, dest.base); + } + + void xor32(RegisterID src, RegisterID dest) + { + m_assembler.xorl_rr(src, dest); + } + + void xor32(TrustedImm32 imm, Address dest) + { + if (imm.m_value == -1) + m_assembler.notl_m(dest.offset, dest.base); + else + m_assembler.xorl_im(imm.m_value, dest.offset, dest.base); + } + + void xor32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value == -1) + m_assembler.notl_r(dest); + else + m_assembler.xorl_ir(imm.m_value, dest); + } + + void xor32(RegisterID src, Address dest) + { + m_assembler.xorl_rm(src, dest.offset, dest.base); + } + + void xor32(Address src, RegisterID dest) + { + m_assembler.xorl_mr(src.offset, src.base, dest); + } + + void xor32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + move(TrustedImm32(0), dest); + else if (op1 == dest) + xor32(op2, dest); + else { + move(op2, dest); + xor32(op1, dest); + } + } + + void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(src, dest); + xor32(imm, dest); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.sqrtsd_rr(src, dst); + } + + void absDouble(FPRegisterID src, FPRegisterID dst) + { + ASSERT(src != dst); + static const double negativeZeroConstant = -0.0; + loadDouble(&negativeZeroConstant, dst); + m_assembler.andnpd_rr(src, dst); + } + + void negateDouble(FPRegisterID src, FPRegisterID dst) + { + ASSERT(src != dst); + static const double negativeZeroConstant = -0.0; + loadDouble(&negativeZeroConstant, dst); + m_assembler.xorpd_rr(src, dst); + } + + + // Memory access operations: + // + // Loads are of the form load(address, destination) and stores of the form + // store(source, address). The source for a store may be an TrustedImm32. Address + // operand objects to loads and store will be implicitly constructed if a + // register is passed. + + void load32(ImplicitAddress address, RegisterID dest) + { + m_assembler.movl_mr(address.offset, address.base, dest); + } + + void load32(BaseIndex address, RegisterID dest) + { + m_assembler.movl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + load32(address, dest); + } + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + load16(address, dest); + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movl_mr_disp32(address.offset, address.base, dest); + return DataLabel32(this); + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movl_mr_disp8(address.offset, address.base, dest); + return DataLabelCompact(this); + } + + static void repatchCompact(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) + { + ASSERT(isCompactPtrAlignedAddressOffset(value)); + AssemblerType_T::repatchCompact(dataLabelCompact.dataLocation(), value); + } + + DataLabelCompact loadCompactWithAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movl_mr_disp8(address.offset, address.base, dest); + return DataLabelCompact(this); + } + + void load8(BaseIndex address, RegisterID dest) + { + m_assembler.movzbl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load8(ImplicitAddress address, RegisterID dest) + { + m_assembler.movzbl_mr(address.offset, address.base, dest); + } + + void load8Signed(BaseIndex address, RegisterID dest) + { + m_assembler.movsbl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load8Signed(ImplicitAddress address, RegisterID dest) + { + m_assembler.movsbl_mr(address.offset, address.base, dest); + } + + void load16(BaseIndex address, RegisterID dest) + { + m_assembler.movzwl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load16(Address address, RegisterID dest) + { + m_assembler.movzwl_mr(address.offset, address.base, dest); + } + + void load16Signed(BaseIndex address, RegisterID dest) + { + m_assembler.movswl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load16Signed(Address address, RegisterID dest) + { + m_assembler.movswl_mr(address.offset, address.base, dest); + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + padBeforePatch(); + m_assembler.movl_rm_disp32(src, address.offset, address.base); + return DataLabel32(this); + } + + void store32(RegisterID src, ImplicitAddress address) + { + m_assembler.movl_rm(src, address.offset, address.base); + } + + void store32(RegisterID src, BaseIndex address) + { + m_assembler.movl_rm(src, address.offset, address.base, address.index, address.scale); + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + m_assembler.movl_i32m(imm.m_value, address.offset, address.base); + } + + void store32(TrustedImm32 imm, BaseIndex address) + { + m_assembler.movl_i32m(imm.m_value, address.offset, address.base, address.index, address.scale); + } + + void store8(TrustedImm32 imm, Address address) + { + ASSERT(-128 <= imm.m_value && imm.m_value < 128); + m_assembler.movb_i8m(imm.m_value, address.offset, address.base); + } + + void store8(TrustedImm32 imm, BaseIndex address) + { + ASSERT(-128 <= imm.m_value && imm.m_value < 128); + m_assembler.movb_i8m(imm.m_value, address.offset, address.base, address.index, address.scale); + } + + void store8(RegisterID src, BaseIndex address) + { +#if CPU(X86) + // On 32-bit x86 we can only store from the first 4 registers; + // esp..edi are mapped to the 'h' registers! + if (src >= 4) { + // Pick a temporary register. + RegisterID temp; + if (address.base != X86Registers::eax && address.index != X86Registers::eax) + temp = X86Registers::eax; + else if (address.base != X86Registers::ebx && address.index != X86Registers::ebx) + temp = X86Registers::ebx; + else { + ASSERT(address.base != X86Registers::ecx && address.index != X86Registers::ecx); + temp = X86Registers::ecx; + } + + // Swap to the temporary register to perform the store. + swap(src, temp); + m_assembler.movb_rm(temp, address.offset, address.base, address.index, address.scale); + swap(src, temp); + return; + } +#endif + m_assembler.movb_rm(src, address.offset, address.base, address.index, address.scale); + } + + void store16(RegisterID src, BaseIndex address) + { +#if CPU(X86) + // On 32-bit x86 we can only store from the first 4 registers; + // esp..edi are mapped to the 'h' registers! + if (src >= 4) { + // Pick a temporary register. + RegisterID temp; + if (address.base != X86Registers::eax && address.index != X86Registers::eax) + temp = X86Registers::eax; + else if (address.base != X86Registers::ebx && address.index != X86Registers::ebx) + temp = X86Registers::ebx; + else { + ASSERT(address.base != X86Registers::ecx && address.index != X86Registers::ecx); + temp = X86Registers::ecx; + } + + // Swap to the temporary register to perform the store. + swap(src, temp); + m_assembler.movw_rm(temp, address.offset, address.base, address.index, address.scale); + swap(src, temp); + return; + } +#endif + m_assembler.movw_rm(src, address.offset, address.base, address.index, address.scale); + } + + + // Floating-point operation: + // + // Presently only supports SSE, not x87 floating point. + + void moveDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (src != dest) + m_assembler.movsd_rr(src, dest); + } + + void loadDouble(const void* address, FPRegisterID dest) + { +#if CPU(X86) + ASSERT(isSSE2Present()); + m_assembler.movsd_mr(address, dest); +#else + move(TrustedImmPtr(address), scratchRegister); + loadDouble(scratchRegister, dest); +#endif + } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_mr(address.offset, address.base, dest); + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_mr(address.offset, address.base, address.index, address.scale, dest); + } + void loadFloat(BaseIndex address, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.movss_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_rm(src, address.offset, address.base); + } + + void storeDouble(FPRegisterID src, BaseIndex address) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_rm(src, address.offset, address.base, address.index, address.scale); + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + ASSERT(isSSE2Present()); + m_assembler.movss_rm(src, address.offset, address.base, address.index, address.scale); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.cvtsd2ss_rr(src, dst); + } + + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.cvtss2sd_rr(src, dst); + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.addsd_rr(src, dest); + } + + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op1 == dest) + addDouble(op2, dest); + else { + moveDouble(op2, dest); + addDouble(op1, dest); + } + } + + void addDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.addsd_mr(src.offset, src.base, dest); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.divsd_rr(src, dest); + } + + void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + // B := A / B is invalid. + ASSERT(op1 == dest || op2 != dest); + + moveDouble(op1, dest); + divDouble(op2, dest); + } + + void divDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.divsd_mr(src.offset, src.base, dest); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.subsd_rr(src, dest); + } + + void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + // B := A - B is invalid. + ASSERT(op1 == dest || op2 != dest); + + moveDouble(op1, dest); + subDouble(op2, dest); + } + + void subDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.subsd_mr(src.offset, src.base, dest); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.mulsd_rr(src, dest); + } + + void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op1 == dest) + mulDouble(op2, dest); + else { + moveDouble(op2, dest); + mulDouble(op1, dest); + } + } + + void mulDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.mulsd_mr(src.offset, src.base, dest); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.cvtsi2sd_rr(src, dest); + } + + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.cvtsi2sd_mr(src.offset, src.base, dest); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + ASSERT(isSSE2Present()); + + if (cond & DoubleConditionBitInvert) + m_assembler.ucomisd_rr(left, right); + else + m_assembler.ucomisd_rr(right, left); + + if (cond == DoubleEqual) { + if (left == right) + return Jump(m_assembler.jnp()); + Jump isUnordered(m_assembler.jp()); + Jump result = Jump(m_assembler.je()); + isUnordered.link(this); + return result; + } else if (cond == DoubleNotEqualOrUnordered) { + if (left == right) + return Jump(m_assembler.jp()); + Jump isUnordered(m_assembler.jp()); + Jump isEqual(m_assembler.je()); + isUnordered.link(this); + Jump result = jump(); + isEqual.link(this); + return result; + } + + ASSERT(!(cond & DoubleConditionBitSpecial)); + return Jump(m_assembler.jCC(static_cast(cond & ~DoubleConditionBits))); + } + + // Truncates 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, INT_MIN). + enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2si_rr(src, dest); + return branch32(branchType ? NotEqual : Equal, dest, TrustedImm32(0x80000000)); + } + + Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2si_rr(src, dest); + return branch32(branchType ? GreaterThanOrEqual : LessThan, dest, TrustedImm32(0)); + } + + void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2si_rr(src, dest); + } + +#if CPU(X86_64) + void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2siq_rr(src, dest); + } +#endif + + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2si_rr(src, dest); + + // If the result is zero, it might have been -0.0, and the double comparison won't catch this! + failureCases.append(branchTest32(Zero, dest)); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + convertInt32ToDouble(dest, fpTemp); + m_assembler.ucomisd_rr(fpTemp, src); + failureCases.append(m_assembler.jp()); + failureCases.append(m_assembler.jne()); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) + { + ASSERT(isSSE2Present()); + m_assembler.xorpd_rr(scratch, scratch); + return branchDouble(DoubleNotEqual, reg, scratch); + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) + { + ASSERT(isSSE2Present()); + m_assembler.xorpd_rr(scratch, scratch); + return branchDouble(DoubleEqualOrUnordered, reg, scratch); + } + + void lshiftPacked(TrustedImm32 imm, XMMRegisterID reg) + { + ASSERT(isSSE2Present()); + m_assembler.psllq_i8r(imm.m_value, reg); + } + + void rshiftPacked(TrustedImm32 imm, XMMRegisterID reg) + { + ASSERT(isSSE2Present()); + m_assembler.psrlq_i8r(imm.m_value, reg); + } + + void orPacked(XMMRegisterID src, XMMRegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.por_rr(src, dst); + } + + void moveInt32ToPacked(RegisterID src, XMMRegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.movd_rr(src, dst); + } + + void movePackedToInt32(XMMRegisterID src, RegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.movd_rr(src, dst); + } + + // Stack manipulation operations: + // + // The ABI is assumed to provide a stack abstraction to memory, + // containing machine word sized units of data. Push and pop + // operations add and remove a single register sized unit of data + // to or from the stack. Peek and poke operations read or write + // values on the stack, without moving the current stack position. + + void pop(RegisterID dest) + { + m_assembler.pop_r(dest); + } + + void push(RegisterID src) + { + m_assembler.push_r(src); + } + + void push(Address address) + { + m_assembler.push_m(address.offset, address.base); + } + + void push(TrustedImm32 imm) + { + m_assembler.push_i32(imm.m_value); + } + + + // Register move operations: + // + // Move values in registers. + + void move(TrustedImm32 imm, RegisterID dest) + { + // Note: on 64-bit the TrustedImm32 value is zero extended into the register, it + // may be useful to have a separate version that sign extends the value? + if (!imm.m_value) + m_assembler.xorl_rr(dest, dest); + else + m_assembler.movl_i32r(imm.m_value, dest); + } + +#if CPU(X86_64) + void move(RegisterID src, RegisterID dest) + { + // Note: on 64-bit this is is a full register move; perhaps it would be + // useful to have separate move32 & movePtr, with move32 zero extending? + if (src != dest) + m_assembler.movq_rr(src, dest); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + m_assembler.movq_i64r(imm.asIntptr(), dest); + } + + void move(TrustedImm64 imm, RegisterID dest) + { + m_assembler.movq_i64r(imm.m_value, dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + if (reg1 != reg2) + m_assembler.xchgq_rr(reg1, reg2); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + m_assembler.movsxd_rr(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + m_assembler.movl_rr(src, dest); + } +#else + void move(RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.movl_rr(src, dest); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + m_assembler.movl_i32r(imm.asIntptr(), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + if (reg1 != reg2) + m_assembler.xchgl_rr(reg1, reg2); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + move(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + move(src, dest); + } +#endif + + + // Forwards / external control flow operations: + // + // This set of jump and conditional branch operations return a Jump + // object which may linked at a later point, allow forwards jump, + // or jumps that will require external linkage (after the code has been + // relocated). + // + // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge + // respecitvely, for unsigned comparisons the names b, a, be, and ae are + // used (representing the names 'below' and 'above'). + // + // Operands to the comparision are provided in the expected order, e.g. + // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when + // treated as a signed 32bit value, is less than or equal to 5. + // + // jz and jnz test whether the first operand is equal to zero, and take + // an optional second operand of a mask under which to perform the test. + +public: + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + m_assembler.cmpb_im(right.m_value, left.offset, left.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) + { + m_assembler.cmpl_rr(right, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testl_rr(left, left); + else + m_assembler.cmpl_ir(right.m_value, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + m_assembler.cmpl_mr(right.offset, right.base, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + m_assembler.cmpl_rm(right, left.offset, left.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + m_assembler.cmpl_im(right.m_value, left.offset, left.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + m_assembler.cmpl_im(right.m_value, left.offset, left.base, left.index, left.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + return branch32(cond, left, right); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + m_assembler.testl_rr(reg, mask); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + // if we are only interested in the low seven bits, this can be tested with a testb + if (mask.m_value == -1) + m_assembler.testl_rr(reg, reg); + else + m_assembler.testl_i32r(mask.m_value, reg); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + if (mask.m_value == -1) + m_assembler.cmpl_im(0, address.offset, address.base); + else + m_assembler.testl_i32m(mask.m_value, address.offset, address.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + if (mask.m_value == -1) + m_assembler.cmpl_im(0, address.offset, address.base, address.index, address.scale); + else + m_assembler.testl_i32m(mask.m_value, address.offset, address.base, address.index, address.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + // Byte in TrustedImm32 is not well defined, so be a little permisive here, but don't accept nonsense values. + ASSERT(mask.m_value >= -128 && mask.m_value <= 255); + if (mask.m_value == -1) + m_assembler.cmpb_im(0, address.offset, address.base); + else + m_assembler.testb_im(mask.m_value, address.offset, address.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest8(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + // Byte in TrustedImm32 is not well defined, so be a little permisive here, but don't accept nonsense values. + ASSERT(mask.m_value >= -128 && mask.m_value <= 255); + if (mask.m_value == -1) + m_assembler.cmpb_im(0, address.offset, address.base, address.index, address.scale); + else + m_assembler.testb_im(mask.m_value, address.offset, address.base, address.index, address.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(right.m_value & 0xFFFFFF00)); + + m_assembler.cmpb_im(right.m_value, left.offset, left.base, left.index, left.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump jump() + { + return Jump(m_assembler.jmp()); + } + + void jump(RegisterID target) + { + m_assembler.jmp_r(target); + } + + // Address is a memory location containing the address to jump to + void jump(Address address) + { + m_assembler.jmp_m(address.offset, address.base); + } + + + // Arithmetic control flow operations: + // + // This set of conditional branch operations branch based + // on the result of an arithmetic operation. The operation + // is performed as normal, storing the result. + // + // * jz operations branch if the result is zero. + // * jo operations branch if the (signed) arithmetic + // operation caused an overflow to occur. + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + add32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + add32(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 src, Address dest) + { + add32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, Address dest) + { + add32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, Address src, RegisterID dest) + { + add32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + if (src1 == dest) + return branchAdd32(cond, src2, dest); + move(src2, dest); + return branchAdd32(cond, src1, dest); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + move(src, dest); + return branchAdd32(cond, imm, dest); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + mul32(src, dest); + if (cond != Overflow) + m_assembler.testl_rr(dest, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchMul32(ResultCondition cond, Address src, RegisterID dest) + { + mul32(src, dest); + if (cond != Overflow) + m_assembler.testl_rr(dest, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + mul32(imm, src, dest); + if (cond != Overflow) + m_assembler.testl_rr(dest, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + if (src1 == dest) + return branchMul32(cond, src2, dest); + move(src2, dest); + return branchMul32(cond, src1, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + sub32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + sub32(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, Address dest) + { + sub32(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, Address dest) + { + sub32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, Address src, RegisterID dest) + { + sub32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + // B := A - B is invalid. + ASSERT(src1 == dest || src2 != dest); + + move(src1, dest); + return branchSub32(cond, src2, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) + { + move(src1, dest); + return branchSub32(cond, src2, dest); + } + + Jump branchNeg32(ResultCondition cond, RegisterID srcDest) + { + neg32(srcDest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + or32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + + // Miscellaneous operations: + + void breakpoint() + { + m_assembler.int3(); + } + + Call nearCall() + { + return Call(m_assembler.call(), Call::LinkableNear); + } + + Call call(RegisterID target) + { + return Call(m_assembler.call(target), Call::None); + } + + void call(Address address) + { + m_assembler.call_m(address.offset, address.base); + } + + void ret() + { + m_assembler.ret(); + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + m_assembler.cmpb_im(right.m_value, left.offset, left.base); + set32(x86Condition(cond), dest); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmpl_rr(right, left); + set32(x86Condition(cond), dest); + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testl_rr(left, left); + else + m_assembler.cmpl_ir(right.m_value, left); + set32(x86Condition(cond), dest); + } + + // FIXME: + // The mask should be optional... perhaps the argument order should be + // dest-src, operations always have a dest? ... possibly not true, considering + // asm ops like test, or pseudo ops like pop(). + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + if (mask.m_value == -1) + m_assembler.cmpb_im(0, address.offset, address.base); + else + m_assembler.testb_im(mask.m_value, address.offset, address.base); + set32(x86Condition(cond), dest); + } + + void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + if (mask.m_value == -1) + m_assembler.cmpl_im(0, address.offset, address.base); + else + m_assembler.testl_i32m(mask.m_value, address.offset, address.base); + set32(x86Condition(cond), dest); + } + + // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. + static RelationalCondition invert(RelationalCondition cond) + { + return static_cast(cond ^ 1); + } + + void nop() + { + m_assembler.nop(); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + X86Assembler::replaceWithJump(instructionStart.executableAddress(), destination.executableAddress()); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return X86Assembler::maxJumpReplacementSize(); + } + +protected: + X86Assembler::Condition x86Condition(RelationalCondition cond) + { + return static_cast(cond); + } + + X86Assembler::Condition x86Condition(ResultCondition cond) + { + return static_cast(cond); + } + + void set32(X86Assembler::Condition cond, RegisterID dest) + { +#if CPU(X86) + // On 32-bit x86 we can only set the first 4 registers; + // esp..edi are mapped to the 'h' registers! + if (dest >= 4) { + m_assembler.xchgl_rr(dest, X86Registers::eax); + m_assembler.setCC_r(cond, X86Registers::eax); + m_assembler.movzbl_rr(X86Registers::eax, X86Registers::eax); + m_assembler.xchgl_rr(dest, X86Registers::eax); + return; + } +#endif + m_assembler.setCC_r(cond, dest); + m_assembler.movzbl_rr(dest, dest); + } + +private: + // Only MacroAssemblerX86 should be using the following method; SSE2 is always available on + // x86_64, and clients & subclasses of MacroAssembler should be using 'supportsFloatingPoint()'. + friend class MacroAssemblerX86; + +#if CPU(X86) +#if OS(MAC_OS_X) + + // All X86 Macs are guaranteed to support at least SSE2, + static bool isSSE2Present() + { + return true; + } + +#else // OS(MAC_OS_X) + + enum SSE2CheckState { + NotCheckedSSE2, + HasSSE2, + NoSSE2 + }; + + static bool isSSE2Present() + { + if (s_sse2CheckState == NotCheckedSSE2) { + // Default the flags value to zero; if the compiler is + // not MSVC or GCC we will read this as SSE2 not present. + int flags = 0; +#if COMPILER(MSVC) + _asm { + mov eax, 1 // cpuid function 1 gives us the standard feature set + cpuid; + mov flags, edx; + } +#elif COMPILER(GCC) + asm ( + "movl $0x1, %%eax;" + "pushl %%ebx;" + "cpuid;" + "popl %%ebx;" + "movl %%edx, %0;" + : "=g" (flags) + : + : "%eax", "%ecx", "%edx" + ); +#endif + static const int SSE2FeatureBit = 1 << 26; + s_sse2CheckState = (flags & SSE2FeatureBit) ? HasSSE2 : NoSSE2; + } + // Only check once. + ASSERT(s_sse2CheckState != NotCheckedSSE2); + + return s_sse2CheckState == HasSSE2; + } + + static SSE2CheckState s_sse2CheckState; + +#endif // OS(MAC_OS_X) +#elif !defined(NDEBUG) // CPU(X86) + + // On x86-64 we should never be checking for SSE2 in a non-debug build, + // but non debug add this method to keep the asserts above happy. + static bool isSSE2Present() + { + return true; + } + +#endif +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerX86Common_h diff --git a/3rdparty/masm/assembler/MacroAssemblerX86_64.h b/3rdparty/masm/assembler/MacroAssemblerX86_64.h new file mode 100644 index 0000000000..c711e6f8da --- /dev/null +++ b/3rdparty/masm/assembler/MacroAssemblerX86_64.h @@ -0,0 +1,643 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerX86_64_h +#define MacroAssemblerX86_64_h + +#if ENABLE(ASSEMBLER) && CPU(X86_64) + +#include "MacroAssemblerX86Common.h" + +#define REPTACH_OFFSET_CALL_R11 3 + +namespace JSC { + +class MacroAssemblerX86_64 : public MacroAssemblerX86Common { +public: + static const Scale ScalePtr = TimesEight; + + using MacroAssemblerX86Common::add32; + using MacroAssemblerX86Common::and32; + using MacroAssemblerX86Common::branchAdd32; + using MacroAssemblerX86Common::or32; + using MacroAssemblerX86Common::sub32; + using MacroAssemblerX86Common::load32; + using MacroAssemblerX86Common::store32; + using MacroAssemblerX86Common::store8; + using MacroAssemblerX86Common::call; + using MacroAssemblerX86Common::jump; + using MacroAssemblerX86Common::addDouble; + using MacroAssemblerX86Common::loadDouble; + using MacroAssemblerX86Common::convertInt32ToDouble; + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + add32(imm, Address(scratchRegister)); + } + + void and32(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + and32(imm, Address(scratchRegister)); + } + + void add32(AbsoluteAddress address, RegisterID dest) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + add32(Address(scratchRegister), dest); + } + + void or32(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + or32(imm, Address(scratchRegister)); + } + + void or32(RegisterID reg, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + or32(reg, Address(scratchRegister)); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + sub32(imm, Address(scratchRegister)); + } + + void load32(const void* address, RegisterID dest) + { + if (dest == X86Registers::eax) + m_assembler.movl_mEAX(address); + else { + move(TrustedImmPtr(address), dest); + load32(dest, dest); + } + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + m_assembler.addsd_mr(0, scratchRegister, dest); + } + + void convertInt32ToDouble(TrustedImm32 imm, FPRegisterID dest) + { + move(imm, scratchRegister); + m_assembler.cvtsi2sd_rr(scratchRegister, dest); + } + + void store32(TrustedImm32 imm, void* address) + { + move(TrustedImmPtr(address), scratchRegister); + store32(imm, scratchRegister); + } + + void store8(TrustedImm32 imm, void* address) + { + move(TrustedImmPtr(address), scratchRegister); + store8(imm, Address(scratchRegister)); + } + + Call call() + { + DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); + Call result = Call(m_assembler.call(scratchRegister), Call::Linkable); + ASSERT_UNUSED(label, differenceBetween(label, result) == REPTACH_OFFSET_CALL_R11); + return result; + } + + // Address is a memory location containing the address to jump to + void jump(AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + jump(Address(scratchRegister)); + } + + Call tailRecursiveCall() + { + DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); + Jump newJump = Jump(m_assembler.jmp_r(scratchRegister)); + ASSERT_UNUSED(label, differenceBetween(label, newJump) == REPTACH_OFFSET_CALL_R11); + return Call::fromTailJump(newJump); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + oldJump.link(this); + DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); + Jump newJump = Jump(m_assembler.jmp_r(scratchRegister)); + ASSERT_UNUSED(label, differenceBetween(label, newJump) == REPTACH_OFFSET_CALL_R11); + return Call::fromTailJump(newJump); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 src, AbsoluteAddress dest) + { + move(TrustedImmPtr(dest.m_ptr), scratchRegister); + add32(src, Address(scratchRegister)); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + void add64(RegisterID src, RegisterID dest) + { + m_assembler.addq_rr(src, dest); + } + + void add64(Address src, RegisterID dest) + { + m_assembler.addq_mr(src.offset, src.base, dest); + } + + void add64(AbsoluteAddress src, RegisterID dest) + { + move(TrustedImmPtr(src.m_ptr), scratchRegister); + add64(Address(scratchRegister), dest); + } + + void add64(TrustedImm32 imm, RegisterID srcDest) + { + m_assembler.addq_ir(imm.m_value, srcDest); + } + + void add64(TrustedImm64 imm, RegisterID dest) + { + move(imm, scratchRegister); + add64(scratchRegister, dest); + } + + void add64(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.leaq_mr(imm.m_value, src, dest); + } + + void add64(TrustedImm32 imm, Address address) + { + m_assembler.addq_im(imm.m_value, address.offset, address.base); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + add64(imm, Address(scratchRegister)); + } + + void and64(RegisterID src, RegisterID dest) + { + m_assembler.andq_rr(src, dest); + } + + void and64(TrustedImm32 imm, RegisterID srcDest) + { + m_assembler.andq_ir(imm.m_value, srcDest); + } + + void neg64(RegisterID dest) + { + m_assembler.negq_r(dest); + } + + void or64(RegisterID src, RegisterID dest) + { + m_assembler.orq_rr(src, dest); + } + + void or64(TrustedImm64 imm, RegisterID dest) + { + move(imm, scratchRegister); + or64(scratchRegister, dest); + } + + void or64(TrustedImm32 imm, RegisterID dest) + { + m_assembler.orq_ir(imm.m_value, dest); + } + + void or64(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + move(op1, dest); + else if (op1 == dest) + or64(op2, dest); + else { + move(op2, dest); + or64(op1, dest); + } + } + + void or64(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(src, dest); + or64(imm, dest); + } + + void rotateRight64(TrustedImm32 imm, RegisterID srcDst) + { + m_assembler.rorq_i8r(imm.m_value, srcDst); + } + + void sub64(RegisterID src, RegisterID dest) + { + m_assembler.subq_rr(src, dest); + } + + void sub64(TrustedImm32 imm, RegisterID dest) + { + m_assembler.subq_ir(imm.m_value, dest); + } + + void sub64(TrustedImm64 imm, RegisterID dest) + { + move(imm, scratchRegister); + sub64(scratchRegister, dest); + } + + void xor64(RegisterID src, RegisterID dest) + { + m_assembler.xorq_rr(src, dest); + } + + void xor64(RegisterID src, Address dest) + { + m_assembler.xorq_rm(src, dest.offset, dest.base); + } + + void xor64(TrustedImm32 imm, RegisterID srcDest) + { + m_assembler.xorq_ir(imm.m_value, srcDest); + } + + void load64(ImplicitAddress address, RegisterID dest) + { + m_assembler.movq_mr(address.offset, address.base, dest); + } + + void load64(BaseIndex address, RegisterID dest) + { + m_assembler.movq_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load64(const void* address, RegisterID dest) + { + if (dest == X86Registers::eax) + m_assembler.movq_mEAX(address); + else { + move(TrustedImmPtr(address), dest); + load64(dest, dest); + } + } + + DataLabel32 load64WithAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movq_mr_disp32(address.offset, address.base, dest); + return DataLabel32(this); + } + + DataLabelCompact load64WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movq_mr_disp8(address.offset, address.base, dest); + return DataLabelCompact(this); + } + + void store64(RegisterID src, ImplicitAddress address) + { + m_assembler.movq_rm(src, address.offset, address.base); + } + + void store64(RegisterID src, BaseIndex address) + { + m_assembler.movq_rm(src, address.offset, address.base, address.index, address.scale); + } + + void store64(RegisterID src, void* address) + { + if (src == X86Registers::eax) + m_assembler.movq_EAXm(address); + else { + move(TrustedImmPtr(address), scratchRegister); + store64(src, scratchRegister); + } + } + + void store64(TrustedImm64 imm, ImplicitAddress address) + { + move(imm, scratchRegister); + store64(scratchRegister, address); + } + + void store64(TrustedImm64 imm, BaseIndex address) + { + move(imm, scratchRegister); + m_assembler.movq_rm(scratchRegister, address.offset, address.base, address.index, address.scale); + } + + DataLabel32 store64WithAddressOffsetPatch(RegisterID src, Address address) + { + padBeforePatch(); + m_assembler.movq_rm_disp32(src, address.offset, address.base); + return DataLabel32(this); + } + + void move64ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.movq_rr(src, dest); + } + + void moveDoubleTo64(FPRegisterID src, RegisterID dest) + { + m_assembler.movq_rr(src, dest); + } + + void compare64(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testq_rr(left, left); + else + m_assembler.cmpq_ir(right.m_value, left); + m_assembler.setCC_r(x86Condition(cond), dest); + m_assembler.movzbl_rr(dest, dest); + } + + void compare64(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmpq_rr(right, left); + m_assembler.setCC_r(x86Condition(cond), dest); + m_assembler.movzbl_rr(dest, dest); + } + + Jump branch64(RelationalCondition cond, RegisterID left, RegisterID right) + { + m_assembler.cmpq_rr(right, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch64(RelationalCondition cond, RegisterID left, TrustedImm64 right) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) { + m_assembler.testq_rr(left, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + move(right, scratchRegister); + return branch64(cond, left, scratchRegister); + } + + Jump branch64(RelationalCondition cond, RegisterID left, Address right) + { + m_assembler.cmpq_mr(right.offset, right.base, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch64(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + move(TrustedImmPtr(left.m_ptr), scratchRegister); + return branch64(cond, Address(scratchRegister), right); + } + + Jump branch64(RelationalCondition cond, Address left, RegisterID right) + { + m_assembler.cmpq_rm(right, left.offset, left.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch64(RelationalCondition cond, Address left, TrustedImm64 right) + { + move(right, scratchRegister); + return branch64(cond, left, scratchRegister); + } + + Jump branchTest64(ResultCondition cond, RegisterID reg, RegisterID mask) + { + m_assembler.testq_rr(reg, mask); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest64(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + // if we are only interested in the low seven bits, this can be tested with a testb + if (mask.m_value == -1) + m_assembler.testq_rr(reg, reg); + else if ((mask.m_value & ~0x7f) == 0) + m_assembler.testb_i8r(mask.m_value, reg); + else + m_assembler.testq_i32r(mask.m_value, reg); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + void test64(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) + { + if (mask.m_value == -1) + m_assembler.testq_rr(reg, reg); + else if ((mask.m_value & ~0x7f) == 0) + m_assembler.testb_i8r(mask.m_value, reg); + else + m_assembler.testq_i32r(mask.m_value, reg); + set32(x86Condition(cond), dest); + } + + void test64(ResultCondition cond, RegisterID reg, RegisterID mask, RegisterID dest) + { + m_assembler.testq_rr(reg, mask); + set32(x86Condition(cond), dest); + } + + Jump branchTest64(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + load64(address.m_ptr, scratchRegister); + return branchTest64(cond, scratchRegister, mask); + } + + Jump branchTest64(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + if (mask.m_value == -1) + m_assembler.cmpq_im(0, address.offset, address.base); + else + m_assembler.testq_i32m(mask.m_value, address.offset, address.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest64(ResultCondition cond, Address address, RegisterID reg) + { + m_assembler.testq_rm(reg, address.offset, address.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest64(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + if (mask.m_value == -1) + m_assembler.cmpq_im(0, address.offset, address.base, address.index, address.scale); + else + m_assembler.testq_i32m(mask.m_value, address.offset, address.base, address.index, address.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + + Jump branchAdd64(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + add64(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd64(ResultCondition cond, RegisterID src, RegisterID dest) + { + add64(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub64(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + sub64(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub64(ResultCondition cond, RegisterID src, RegisterID dest) + { + sub64(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub64(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) + { + move(src1, dest); + return branchSub64(cond, src2, dest); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result = ConvertibleLoadLabel(this); + m_assembler.movq_mr(address.offset, address.base, dest); + return result; + } + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + padBeforePatch(); + m_assembler.movq_i64r(initialValue.asIntptr(), dest); + return DataLabelPtr(this); + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + dataLabel = moveWithPatch(initialRightValue, scratchRegister); + return branch64(cond, left, scratchRegister); + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + dataLabel = moveWithPatch(initialRightValue, scratchRegister); + return branch64(cond, left, scratchRegister); + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + DataLabelPtr label = moveWithPatch(initialValue, scratchRegister); + store64(scratchRegister, address); + return label; + } + + using MacroAssemblerX86Common::branchTest8; + Jump branchTest8(ResultCondition cond, ExtendedAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + TrustedImmPtr addr(reinterpret_cast(address.offset)); + MacroAssemblerX86Common::move(addr, scratchRegister); + return MacroAssemblerX86Common::branchTest8(cond, BaseIndex(scratchRegister, address.base, TimesOne), mask); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + MacroAssemblerX86Common::move(TrustedImmPtr(address.m_ptr), scratchRegister); + return MacroAssemblerX86Common::branchTest8(cond, Address(scratchRegister), mask); + } + + static bool supportsFloatingPoint() { return true; } + // See comment on MacroAssemblerARMv7::supportsFloatingPointTruncate() + static bool supportsFloatingPointTruncate() { return true; } + static bool supportsFloatingPointSqrt() { return true; } + static bool supportsFloatingPointAbs() { return true; } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(X86Assembler::readPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation())); + } + + static RegisterID scratchRegisterForBlinding() { return scratchRegister; } + + static bool canJumpReplacePatchableBranchPtrWithPatch() { return true; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + const int rexBytes = 1; + const int opcodeBytes = 1; + const int immediateBytes = 8; + const int totalBytes = rexBytes + opcodeBytes + immediateBytes; + ASSERT(totalBytes >= maxJumpReplacementSize()); + return label.labelAtOffset(-totalBytes); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) + { + return startOfBranchPtrWithPatchOnRegister(label); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + X86Assembler::revertJumpTo_movq_i64r(instructionStart.executableAddress(), reinterpret_cast(initialValue), scratchRegister); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) + { + X86Assembler::revertJumpTo_movq_i64r(instructionStart.executableAddress(), reinterpret_cast(initialValue), scratchRegister); + } + +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + if (!call.isFlagSet(Call::Near)) + X86Assembler::linkPointer(code, call.m_label.labelAtOffset(-REPTACH_OFFSET_CALL_R11), function.value()); + else + X86Assembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + X86Assembler::repatchPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + X86Assembler::repatchPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation(), destination.executableAddress()); + } + +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerX86_64_h diff --git a/3rdparty/masm/assembler/RepatchBuffer.h b/3rdparty/masm/assembler/RepatchBuffer.h new file mode 100644 index 0000000000..dbb56f9ad5 --- /dev/null +++ b/3rdparty/masm/assembler/RepatchBuffer.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RepatchBuffer_h +#define RepatchBuffer_h + +#if ENABLE(JIT) + +#include "CodeBlock.h" +#include +#include + +namespace JSC { + +// RepatchBuffer: +// +// This class is used to modify code after code generation has been completed, +// and after the code has potentially already been executed. This mechanism is +// used to apply optimizations to the code. +// +class RepatchBuffer { + typedef MacroAssemblerCodePtr CodePtr; + +public: + RepatchBuffer(CodeBlock* codeBlock) + { + JITCode& code = codeBlock->getJITCode(); + m_start = code.start(); + m_size = code.size(); + + ExecutableAllocator::makeWritable(m_start, m_size); + } + + ~RepatchBuffer() + { + ExecutableAllocator::makeExecutable(m_start, m_size); + } + + void relink(CodeLocationJump jump, CodeLocationLabel destination) + { + MacroAssembler::repatchJump(jump, destination); + } + + void relink(CodeLocationCall call, CodeLocationLabel destination) + { + MacroAssembler::repatchCall(call, destination); + } + + void relink(CodeLocationCall call, FunctionPtr destination) + { + MacroAssembler::repatchCall(call, destination); + } + + void relink(CodeLocationNearCall nearCall, CodePtr destination) + { + MacroAssembler::repatchNearCall(nearCall, CodeLocationLabel(destination)); + } + + void relink(CodeLocationNearCall nearCall, CodeLocationLabel destination) + { + MacroAssembler::repatchNearCall(nearCall, destination); + } + + void repatch(CodeLocationDataLabel32 dataLabel32, int32_t value) + { + MacroAssembler::repatchInt32(dataLabel32, value); + } + + void repatch(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) + { + MacroAssembler::repatchCompact(dataLabelCompact, value); + } + + void repatch(CodeLocationDataLabelPtr dataLabelPtr, void* value) + { + MacroAssembler::repatchPointer(dataLabelPtr, value); + } + + void relinkCallerToTrampoline(ReturnAddressPtr returnAddress, CodeLocationLabel label) + { + relink(CodeLocationCall(CodePtr(returnAddress)), label); + } + + void relinkCallerToTrampoline(ReturnAddressPtr returnAddress, CodePtr newCalleeFunction) + { + relinkCallerToTrampoline(returnAddress, CodeLocationLabel(newCalleeFunction)); + } + + void relinkCallerToFunction(ReturnAddressPtr returnAddress, FunctionPtr function) + { + relink(CodeLocationCall(CodePtr(returnAddress)), function); + } + + void relinkNearCallerToTrampoline(ReturnAddressPtr returnAddress, CodeLocationLabel label) + { + relink(CodeLocationNearCall(CodePtr(returnAddress)), label); + } + + void relinkNearCallerToTrampoline(ReturnAddressPtr returnAddress, CodePtr newCalleeFunction) + { + relinkNearCallerToTrampoline(returnAddress, CodeLocationLabel(newCalleeFunction)); + } + + void replaceWithLoad(CodeLocationConvertibleLoad label) + { + MacroAssembler::replaceWithLoad(label); + } + + void replaceWithAddressComputation(CodeLocationConvertibleLoad label) + { + MacroAssembler::replaceWithAddressComputation(label); + } + + void setLoadInstructionIsActive(CodeLocationConvertibleLoad label, bool isActive) + { + if (isActive) + replaceWithLoad(label); + else + replaceWithAddressComputation(label); + } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + return MacroAssembler::startOfBranchPtrWithPatchOnRegister(label); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) + { + return MacroAssembler::startOfPatchableBranchPtrWithPatchOnAddress(label); + } + + void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + MacroAssembler::replaceWithJump(instructionStart, destination); + } + + // This is a *bit* of a silly API, since we currently always also repatch the + // immediate after calling this. But I'm fine with that, since this just feels + // less yucky. + void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, MacroAssembler::RegisterID reg, void* value) + { + MacroAssembler::revertJumpReplacementToBranchPtrWithPatch(instructionStart, reg, value); + } + + void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, MacroAssembler::Address address, void* value) + { + MacroAssembler::revertJumpReplacementToPatchableBranchPtrWithPatch(instructionStart, address, value); + } + +private: + void* m_start; + size_t m_size; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // RepatchBuffer_h diff --git a/3rdparty/masm/assembler/SH4Assembler.h b/3rdparty/masm/assembler/SH4Assembler.h new file mode 100644 index 0000000000..39f5585be1 --- /dev/null +++ b/3rdparty/masm/assembler/SH4Assembler.h @@ -0,0 +1,2152 @@ +/* + * Copyright (C) 2009-2011 STMicroelectronics. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SH4Assembler_h +#define SH4Assembler_h + +#if ENABLE(ASSEMBLER) && CPU(SH4) + +#include "AssemblerBuffer.h" +#include "AssemblerBufferWithConstantPool.h" +#include "JITCompilationEffort.h" +#include +#include +#include +#include +#include +#include + +#ifndef NDEBUG +#define SH4_ASSEMBLER_TRACING +#endif + +namespace JSC { +typedef uint16_t SH4Word; + +enum { + INVALID_OPCODE = 0xffff, + ADD_OPCODE = 0x300c, + ADDIMM_OPCODE = 0x7000, + ADDC_OPCODE = 0x300e, + ADDV_OPCODE = 0x300f, + AND_OPCODE = 0x2009, + ANDIMM_OPCODE = 0xc900, + DIV0_OPCODE = 0x2007, + DIV1_OPCODE = 0x3004, + BF_OPCODE = 0x8b00, + BFS_OPCODE = 0x8f00, + BRA_OPCODE = 0xa000, + BRAF_OPCODE = 0x0023, + NOP_OPCODE = 0x0009, + BSR_OPCODE = 0xb000, + RTS_OPCODE = 0x000b, + BT_OPCODE = 0x8900, + BTS_OPCODE = 0x8d00, + BSRF_OPCODE = 0x0003, + BRK_OPCODE = 0x003b, + FTRC_OPCODE = 0xf03d, + CMPEQ_OPCODE = 0x3000, + CMPEQIMM_OPCODE = 0x8800, + CMPGE_OPCODE = 0x3003, + CMPGT_OPCODE = 0x3007, + CMPHI_OPCODE = 0x3006, + CMPHS_OPCODE = 0x3002, + CMPPL_OPCODE = 0x4015, + CMPPZ_OPCODE = 0x4011, + CMPSTR_OPCODE = 0x200c, + DT_OPCODE = 0x4010, + FCMPEQ_OPCODE = 0xf004, + FCMPGT_OPCODE = 0xf005, + FMOV_OPCODE = 0xf00c, + FADD_OPCODE = 0xf000, + FMUL_OPCODE = 0xf002, + FSUB_OPCODE = 0xf001, + FDIV_OPCODE = 0xf003, + FNEG_OPCODE = 0xf04d, + JMP_OPCODE = 0x402b, + JSR_OPCODE = 0x400b, + LDSPR_OPCODE = 0x402a, + LDSLPR_OPCODE = 0x4026, + MOV_OPCODE = 0x6003, + MOVIMM_OPCODE = 0xe000, + MOVB_WRITE_RN_OPCODE = 0x2000, + MOVB_WRITE_RNDEC_OPCODE = 0x2004, + MOVB_WRITE_R0RN_OPCODE = 0x0004, + MOVB_WRITE_OFFGBR_OPCODE = 0xc000, + MOVB_WRITE_OFFRN_OPCODE = 0x8000, + MOVB_READ_RM_OPCODE = 0x6000, + MOVB_READ_RMINC_OPCODE = 0x6004, + MOVB_READ_R0RM_OPCODE = 0x000c, + MOVB_READ_OFFGBR_OPCODE = 0xc400, + MOVB_READ_OFFRM_OPCODE = 0x8400, + MOVL_WRITE_RN_OPCODE = 0x2002, + MOVL_WRITE_RNDEC_OPCODE = 0x2006, + MOVL_WRITE_R0RN_OPCODE = 0x0006, + MOVL_WRITE_OFFGBR_OPCODE = 0xc200, + MOVL_WRITE_OFFRN_OPCODE = 0x1000, + MOVL_READ_RM_OPCODE = 0x6002, + MOVL_READ_RMINC_OPCODE = 0x6006, + MOVL_READ_R0RM_OPCODE = 0x000e, + MOVL_READ_OFFGBR_OPCODE = 0xc600, + MOVL_READ_OFFPC_OPCODE = 0xd000, + MOVL_READ_OFFRM_OPCODE = 0x5000, + MOVW_WRITE_RN_OPCODE = 0x2001, + MOVW_READ_RM_OPCODE = 0x6001, + MOVW_READ_R0RM_OPCODE = 0x000d, + MOVW_READ_OFFRM_OPCODE = 0x8500, + MOVW_READ_OFFPC_OPCODE = 0x9000, + MOVA_READ_OFFPC_OPCODE = 0xc700, + MOVT_OPCODE = 0x0029, + MULL_OPCODE = 0x0007, + DMULL_L_OPCODE = 0x3005, + STSMACL_OPCODE = 0x001a, + STSMACH_OPCODE = 0x000a, + DMULSL_OPCODE = 0x300d, + NEG_OPCODE = 0x600b, + NEGC_OPCODE = 0x600a, + NOT_OPCODE = 0x6007, + OR_OPCODE = 0x200b, + ORIMM_OPCODE = 0xcb00, + ORBIMM_OPCODE = 0xcf00, + SETS_OPCODE = 0x0058, + SETT_OPCODE = 0x0018, + SHAD_OPCODE = 0x400c, + SHAL_OPCODE = 0x4020, + SHAR_OPCODE = 0x4021, + SHLD_OPCODE = 0x400d, + SHLL_OPCODE = 0x4000, + SHLL2_OPCODE = 0x4008, + SHLL8_OPCODE = 0x4018, + SHLL16_OPCODE = 0x4028, + SHLR_OPCODE = 0x4001, + SHLR2_OPCODE = 0x4009, + SHLR8_OPCODE = 0x4019, + SHLR16_OPCODE = 0x4029, + STSPR_OPCODE = 0x002a, + STSLPR_OPCODE = 0x4022, + FLOAT_OPCODE = 0xf02d, + SUB_OPCODE = 0x3008, + SUBC_OPCODE = 0x300a, + SUBV_OPCODE = 0x300b, + TST_OPCODE = 0x2008, + TSTIMM_OPCODE = 0xc800, + TSTB_OPCODE = 0xcc00, + EXTUB_OPCODE = 0x600c, + EXTUW_OPCODE = 0x600d, + XOR_OPCODE = 0x200a, + XORIMM_OPCODE = 0xca00, + XORB_OPCODE = 0xce00, + FMOVS_READ_RM_INC_OPCODE = 0xf009, + FMOVS_READ_RM_OPCODE = 0xf008, + FMOVS_READ_R0RM_OPCODE = 0xf006, + FMOVS_WRITE_RN_OPCODE = 0xf00a, + FMOVS_WRITE_RN_DEC_OPCODE = 0xf00b, + FMOVS_WRITE_R0RN_OPCODE = 0xf007, + FCNVDS_DRM_FPUL_OPCODE = 0xf0bd, + FCNVSD_FPUL_DRN_OPCODE = 0xf0ad, + LDS_RM_FPUL_OPCODE = 0x405a, + FLDS_FRM_FPUL_OPCODE = 0xf01d, + STS_FPUL_RN_OPCODE = 0x005a, + FSTS_FPUL_FRN_OPCODE = 0xF00d, + LDSFPSCR_OPCODE = 0x406a, + STSFPSCR_OPCODE = 0x006a, + LDSRMFPUL_OPCODE = 0x405a, + FSTSFPULFRN_OPCODE = 0xf00d, + FSQRT_OPCODE = 0xf06d, + FSCHG_OPCODE = 0xf3fd, + CLRT_OPCODE = 8, +}; + +namespace SH4Registers { +typedef enum { + r0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + r13, + r14, fp = r14, + r15, sp = r15, + pc, + pr, +} RegisterID; + +typedef enum { + fr0, dr0 = fr0, + fr1, + fr2, dr2 = fr2, + fr3, + fr4, dr4 = fr4, + fr5, + fr6, dr6 = fr6, + fr7, + fr8, dr8 = fr8, + fr9, + fr10, dr10 = fr10, + fr11, + fr12, dr12 = fr12, + fr13, + fr14, dr14 = fr14, + fr15, +} FPRegisterID; +} + +inline uint16_t getOpcodeGroup1(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0xf) << 8) | ((rn & 0xf) << 4)); +} + +inline uint16_t getOpcodeGroup2(uint16_t opc, int rm) +{ + return (opc | ((rm & 0xf) << 8)); +} + +inline uint16_t getOpcodeGroup3(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0xf) << 8) | (rn & 0xff)); +} + +inline uint16_t getOpcodeGroup4(uint16_t opc, int rm, int rn, int offset) +{ + return (opc | ((rm & 0xf) << 8) | ((rn & 0xf) << 4) | (offset & 0xf)); +} + +inline uint16_t getOpcodeGroup5(uint16_t opc, int rm) +{ + return (opc | (rm & 0xff)); +} + +inline uint16_t getOpcodeGroup6(uint16_t opc, int rm) +{ + return (opc | (rm & 0xfff)); +} + +inline uint16_t getOpcodeGroup7(uint16_t opc, int rm) +{ + return (opc | ((rm & 0x7) << 9)); +} + +inline uint16_t getOpcodeGroup8(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0x7) << 9) | ((rn & 0x7) << 5)); +} + +inline uint16_t getOpcodeGroup9(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0xf) << 8) | ((rn & 0x7) << 5)); +} + +inline uint16_t getOpcodeGroup10(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0x7) << 9) | ((rn & 0xf) << 4)); +} + +inline uint16_t getOpcodeGroup11(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0xf) << 4) | (rn & 0xf)); +} + +inline uint16_t getRn(uint16_t x) +{ + return ((x & 0xf00) >> 8); +} + +inline uint16_t getRm(uint16_t x) +{ + return ((x & 0xf0) >> 4); +} + +inline uint16_t getDisp(uint16_t x) +{ + return (x & 0xf); +} + +inline uint16_t getImm8(uint16_t x) +{ + return (x & 0xff); +} + +inline uint16_t getImm12(uint16_t x) +{ + return (x & 0xfff); +} + +inline uint16_t getDRn(uint16_t x) +{ + return ((x & 0xe00) >> 9); +} + +inline uint16_t getDRm(uint16_t x) +{ + return ((x & 0xe0) >> 5); +} + +class SH4Assembler { +public: + typedef SH4Registers::RegisterID RegisterID; + typedef SH4Registers::FPRegisterID FPRegisterID; + typedef AssemblerBufferWithConstantPool<512, 4, 2, SH4Assembler> SH4Buffer; + static const RegisterID scratchReg1 = SH4Registers::r3; + static const RegisterID scratchReg2 = SH4Registers::r11; + static const uint32_t maxInstructionSize = 16; + + enum { + padForAlign8 = 0x00, + padForAlign16 = 0x0009, + padForAlign32 = 0x00090009, + }; + + enum JumpType { + JumpFar, + JumpNear + }; + + SH4Assembler() + { + m_claimscratchReg = 0x0; + } + + // SH4 condition codes + typedef enum { + EQ = 0x0, // Equal + NE = 0x1, // Not Equal + HS = 0x2, // Unsigend Greater Than equal + HI = 0x3, // Unsigend Greater Than + LS = 0x4, // Unsigend Lower or Same + LI = 0x5, // Unsigend Lower + GE = 0x6, // Greater or Equal + LT = 0x7, // Less Than + GT = 0x8, // Greater Than + LE = 0x9, // Less or Equal + OF = 0xa, // OverFlow + SI = 0xb, // Signed + EQU= 0xc, // Equal or unordered(NaN) + NEU= 0xd, + GTU= 0xe, + GEU= 0xf, + LTU= 0x10, + LEU= 0x11, + } Condition; + + // Opaque label types +public: + bool isImmediate(int constant) + { + return ((constant <= 127) && (constant >= -128)); + } + + RegisterID claimScratch() + { + ASSERT((m_claimscratchReg != 0x3)); + + if (!(m_claimscratchReg & 0x1)) { + m_claimscratchReg = (m_claimscratchReg | 0x1); + return scratchReg1; + } + + m_claimscratchReg = (m_claimscratchReg | 0x2); + return scratchReg2; + } + + void releaseScratch(RegisterID scratchR) + { + if (scratchR == scratchReg1) + m_claimscratchReg = (m_claimscratchReg & 0x2); + else + m_claimscratchReg = (m_claimscratchReg & 0x1); + } + + // Stack operations + + void pushReg(RegisterID reg) + { + if (reg == SH4Registers::pr) { + oneShortOp(getOpcodeGroup2(STSLPR_OPCODE, SH4Registers::sp)); + return; + } + + oneShortOp(getOpcodeGroup1(MOVL_WRITE_RNDEC_OPCODE, SH4Registers::sp, reg)); + } + + void popReg(RegisterID reg) + { + if (reg == SH4Registers::pr) { + oneShortOp(getOpcodeGroup2(LDSLPR_OPCODE, SH4Registers::sp)); + return; + } + + oneShortOp(getOpcodeGroup1(MOVL_READ_RMINC_OPCODE, reg, SH4Registers::sp)); + } + + void movt(RegisterID dst) + { + uint16_t opc = getOpcodeGroup2(MOVT_OPCODE, dst); + oneShortOp(opc); + } + + // Arithmetic operations + + void addlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(ADD_OPCODE, dst, src); + oneShortOp(opc); + } + + void addclRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(ADDC_OPCODE, dst, src); + oneShortOp(opc); + } + + void addvlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(ADDV_OPCODE, dst, src); + oneShortOp(opc); + } + + void addlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 127) && (imm8 >= -128)); + + uint16_t opc = getOpcodeGroup3(ADDIMM_OPCODE, dst, imm8); + oneShortOp(opc); + } + + void andlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(AND_OPCODE, dst, src); + oneShortOp(opc); + } + + void andlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 255) && (imm8 >= 0)); + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup5(ANDIMM_OPCODE, imm8); + oneShortOp(opc); + } + + void div1lRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(DIV1_OPCODE, dst, src); + oneShortOp(opc); + } + + void div0lRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(DIV0_OPCODE, dst, src); + oneShortOp(opc); + } + + void notlReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(NOT_OPCODE, dst, src); + oneShortOp(opc); + } + + void orlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(OR_OPCODE, dst, src); + oneShortOp(opc); + } + + void orlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 255) && (imm8 >= 0)); + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup5(ORIMM_OPCODE, imm8); + oneShortOp(opc); + } + + void sublRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(SUB_OPCODE, dst, src); + oneShortOp(opc); + } + + void subvlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(SUBV_OPCODE, dst, src); + oneShortOp(opc); + } + + void xorlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(XOR_OPCODE, dst, src); + oneShortOp(opc); + } + + void xorlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 255) && (imm8 >= 0)); + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup5(XORIMM_OPCODE, imm8); + oneShortOp(opc); + } + + void shllImm8r(int imm, RegisterID dst) + { + switch (imm) { + case 1: + oneShortOp(getOpcodeGroup2(SHLL_OPCODE, dst)); + break; + case 2: + oneShortOp(getOpcodeGroup2(SHLL2_OPCODE, dst)); + break; + case 8: + oneShortOp(getOpcodeGroup2(SHLL8_OPCODE, dst)); + break; + case 16: + oneShortOp(getOpcodeGroup2(SHLL16_OPCODE, dst)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void neg(RegisterID dst, RegisterID src) + { + uint16_t opc = getOpcodeGroup1(NEG_OPCODE, dst, src); + oneShortOp(opc); + } + + void shllRegReg(RegisterID dst, RegisterID rShift) + { + uint16_t opc = getOpcodeGroup1(SHLD_OPCODE, dst, rShift); + oneShortOp(opc); + } + + void shlrRegReg(RegisterID dst, RegisterID rShift) + { + neg(rShift, rShift); + shllRegReg(dst, rShift); + } + + void sharRegReg(RegisterID dst, RegisterID rShift) + { + neg(rShift, rShift); + shaRegReg(dst, rShift); + } + + void shaRegReg(RegisterID dst, RegisterID rShift) + { + uint16_t opc = getOpcodeGroup1(SHAD_OPCODE, dst, rShift); + oneShortOp(opc); + } + + void shlrImm8r(int imm, RegisterID dst) + { + switch (imm) { + case 1: + oneShortOp(getOpcodeGroup2(SHLR_OPCODE, dst)); + break; + case 2: + oneShortOp(getOpcodeGroup2(SHLR2_OPCODE, dst)); + break; + case 8: + oneShortOp(getOpcodeGroup2(SHLR8_OPCODE, dst)); + break; + case 16: + oneShortOp(getOpcodeGroup2(SHLR16_OPCODE, dst)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void imullRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MULL_OPCODE, dst, src); + oneShortOp(opc); + } + + void dmullRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(DMULL_L_OPCODE, dst, src); + oneShortOp(opc); + } + + void dmulslRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(DMULSL_OPCODE, dst, src); + oneShortOp(opc); + } + + void stsmacl(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(STSMACL_OPCODE, reg); + oneShortOp(opc); + } + + void stsmach(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(STSMACH_OPCODE, reg); + oneShortOp(opc); + } + + // Comparisons + + void cmplRegReg(RegisterID left, RegisterID right, Condition cond) + { + switch (cond) { + case NE: + oneShortOp(getOpcodeGroup1(CMPEQ_OPCODE, right, left)); + break; + case GT: + oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, right, left)); + break; + case EQ: + oneShortOp(getOpcodeGroup1(CMPEQ_OPCODE, right, left)); + break; + case GE: + oneShortOp(getOpcodeGroup1(CMPGE_OPCODE, right, left)); + break; + case HS: + oneShortOp(getOpcodeGroup1(CMPHS_OPCODE, right, left)); + break; + case HI: + oneShortOp(getOpcodeGroup1(CMPHI_OPCODE, right, left)); + break; + case LI: + oneShortOp(getOpcodeGroup1(CMPHI_OPCODE, left, right)); + break; + case LS: + oneShortOp(getOpcodeGroup1(CMPHS_OPCODE, left, right)); + break; + case LE: + oneShortOp(getOpcodeGroup1(CMPGE_OPCODE, left, right)); + break; + case LT: + oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, left, right)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void cmppl(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(CMPPL_OPCODE, reg); + oneShortOp(opc); + } + + void cmppz(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(CMPPZ_OPCODE, reg); + oneShortOp(opc); + } + + void cmpEqImmR0(int imm, RegisterID dst) + { + uint16_t opc = getOpcodeGroup5(CMPEQIMM_OPCODE, imm); + oneShortOp(opc); + } + + void testlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(TST_OPCODE, dst, src); + oneShortOp(opc); + } + + void testlImm8r(int imm, RegisterID dst) + { + ASSERT((dst == SH4Registers::r0) && (imm <= 255) && (imm >= 0)); + + uint16_t opc = getOpcodeGroup5(TSTIMM_OPCODE, imm); + oneShortOp(opc); + } + + void nop() + { + oneShortOp(NOP_OPCODE, false); + } + + void sett() + { + oneShortOp(SETT_OPCODE); + } + + void clrt() + { + oneShortOp(CLRT_OPCODE); + } + + void fschg() + { + oneShortOp(FSCHG_OPCODE); + } + + void bkpt() + { + oneShortOp(BRK_OPCODE, false); + } + + void branch(uint16_t opc, int label) + { + switch (opc) { + case BT_OPCODE: + ASSERT((label <= 127) && (label >= -128)); + oneShortOp(getOpcodeGroup5(BT_OPCODE, label)); + break; + case BRA_OPCODE: + ASSERT((label <= 2047) && (label >= -2048)); + oneShortOp(getOpcodeGroup6(BRA_OPCODE, label)); + break; + case BF_OPCODE: + ASSERT((label <= 127) && (label >= -128)); + oneShortOp(getOpcodeGroup5(BF_OPCODE, label)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void branch(uint16_t opc, RegisterID reg) + { + switch (opc) { + case BRAF_OPCODE: + oneShortOp(getOpcodeGroup2(BRAF_OPCODE, reg)); + break; + case JMP_OPCODE: + oneShortOp(getOpcodeGroup2(JMP_OPCODE, reg)); + break; + case JSR_OPCODE: + oneShortOp(getOpcodeGroup2(JSR_OPCODE, reg)); + break; + case BSRF_OPCODE: + oneShortOp(getOpcodeGroup2(BSRF_OPCODE, reg)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void ldspr(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(LDSPR_OPCODE, reg); + oneShortOp(opc); + } + + void stspr(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(STSPR_OPCODE, reg); + oneShortOp(opc); + } + + void extub(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(EXTUB_OPCODE, dst, src); + oneShortOp(opc); + } + + void extuw(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(EXTUW_OPCODE, dst, src); + oneShortOp(opc); + } + + // float operations + + void ldsrmfpul(RegisterID src) + { + uint16_t opc = getOpcodeGroup2(LDS_RM_FPUL_OPCODE, src); + oneShortOp(opc); + } + + void fneg(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup2(FNEG_OPCODE, dst); + oneShortOp(opc, true, false); + } + + void fsqrt(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup2(FSQRT_OPCODE, dst); + oneShortOp(opc, true, false); + } + + void stsfpulReg(RegisterID src) + { + uint16_t opc = getOpcodeGroup2(STS_FPUL_RN_OPCODE, src); + oneShortOp(opc); + } + + void floatfpulfrn(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup2(FLOAT_OPCODE, src); + oneShortOp(opc, true, false); + } + + void fmull(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMUL_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsReadrm(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsWriterm(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_RN_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsWriter0r(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_R0RN_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsReadr0r(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_READ_R0RM_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsReadrminc(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_INC_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsWriterndec(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_RN_DEC_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void ftrcRegfpul(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup2(FTRC_OPCODE, src); + oneShortOp(opc, true, false); + } + + void fldsfpul(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup2(FLDS_FRM_FPUL_OPCODE, src); + oneShortOp(opc); + } + + void fstsfpul(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup2(FSTS_FPUL_FRN_OPCODE, src); + oneShortOp(opc); + } + + void ldsfpscr(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(LDSFPSCR_OPCODE, reg); + oneShortOp(opc); + } + + void stsfpscr(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(STSFPSCR_OPCODE, reg); + oneShortOp(opc); + } + + // double operations + + void dcnvds(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup7(FCNVDS_DRM_FPUL_OPCODE, src >> 1); + oneShortOp(opc); + } + + void dcnvsd(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup7(FCNVSD_FPUL_DRN_OPCODE, dst >> 1); + oneShortOp(opc); + } + + void dcmppeq(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FCMPEQ_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dcmppgt(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FCMPGT_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dmulRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FMUL_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dsubRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FSUB_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void daddRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FADD_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dmovRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FMOV_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void ddivRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FDIV_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dsqrt(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup7(FSQRT_OPCODE, dst >> 1); + oneShortOp(opc); + } + + void dneg(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup7(FNEG_OPCODE, dst >> 1); + oneShortOp(opc); + } + + void fmovReadrm(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup10(FMOVS_READ_RM_OPCODE, dst >> 1, src); + oneShortOp(opc); + } + + void fmovWriterm(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_RN_OPCODE, dst, src >> 1); + oneShortOp(opc); + } + + void fmovWriter0r(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_R0RN_OPCODE, dst, src >> 1); + oneShortOp(opc); + } + + void fmovReadr0r(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup10(FMOVS_READ_R0RM_OPCODE, dst >> 1, src); + oneShortOp(opc); + } + + void fmovReadrminc(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup10(FMOVS_READ_RM_INC_OPCODE, dst >> 1, src); + oneShortOp(opc); + } + + void fmovWriterndec(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_RN_DEC_OPCODE, dst, src >> 1); + oneShortOp(opc); + } + + void floatfpulDreg(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup7(FLOAT_OPCODE, src >> 1); + oneShortOp(opc); + } + + void ftrcdrmfpul(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup7(FTRC_OPCODE, src >> 1); + oneShortOp(opc); + } + + // Various move ops + + void movImm8(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 127) && (imm8 >= -128)); + + uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, imm8); + oneShortOp(opc); + } + + void movlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOV_OPCODE, dst, src); + oneShortOp(opc); + } + + void movwRegMem(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVW_WRITE_RN_OPCODE, dst, src); + oneShortOp(opc); + } + + void movwMemReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVW_READ_RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movwPCReg(int offset, RegisterID base, RegisterID dst) + { + ASSERT(base == SH4Registers::pc); + ASSERT((offset <= 255) && (offset >= 0)); + + uint16_t opc = getOpcodeGroup3(MOVW_READ_OFFPC_OPCODE, dst, offset); + oneShortOp(opc); + } + + void movwMemReg(int offset, RegisterID base, RegisterID dst) + { + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup11(MOVW_READ_OFFRM_OPCODE, base, offset); + oneShortOp(opc); + } + + void movwR0mr(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVW_READ_R0RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movlRegMem(RegisterID src, int offset, RegisterID base) + { + ASSERT((offset <= 15) && (offset >= 0)); + + if (!offset) { + oneShortOp(getOpcodeGroup1(MOVL_WRITE_RN_OPCODE, base, src)); + return; + } + + oneShortOp(getOpcodeGroup4(MOVL_WRITE_OFFRN_OPCODE, base, src, offset)); + } + + void movlRegMem(RegisterID src, RegisterID base) + { + uint16_t opc = getOpcodeGroup1(MOVL_WRITE_RN_OPCODE, base, src); + oneShortOp(opc); + } + + void movlMemReg(int offset, RegisterID base, RegisterID dst) + { + if (base == SH4Registers::pc) { + ASSERT((offset <= 255) && (offset >= 0)); + oneShortOp(getOpcodeGroup3(MOVL_READ_OFFPC_OPCODE, dst, offset)); + return; + } + + ASSERT((offset <= 15) && (offset >= 0)); + if (!offset) { + oneShortOp(getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base)); + return; + } + + oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset)); + } + + void movlMemRegCompact(int offset, RegisterID base, RegisterID dst) + { + oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset)); + } + + void movbRegMem(RegisterID src, RegisterID base) + { + uint16_t opc = getOpcodeGroup1(MOVB_WRITE_RN_OPCODE, base, src); + oneShortOp(opc); + } + + void movbMemReg(int offset, RegisterID base, RegisterID dst) + { + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup11(MOVB_READ_OFFRM_OPCODE, base, offset); + oneShortOp(opc); + } + + void movbR0mr(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVB_READ_R0RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movbMemReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVB_READ_RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movlMemReg(RegisterID base, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base); + oneShortOp(opc); + } + + void movlMemRegIn(RegisterID base, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVL_READ_RMINC_OPCODE, dst, base); + oneShortOp(opc); + } + + void movlR0mr(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVL_READ_R0RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movlRegMemr0(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVL_WRITE_R0RN_OPCODE, dst, src); + oneShortOp(opc); + } + + void movlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 127) && (imm8 >= -128)); + + uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, imm8); + oneShortOp(opc); + } + + void loadConstant(uint32_t constant, RegisterID dst) + { + if (((int)constant <= 0x7f) && ((int)constant >= -0x80)) { + movImm8(constant, dst); + return; + } + + uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, 0); + + m_buffer.ensureSpace(maxInstructionSize, sizeof(uint32_t)); + printInstr(getOpcodeGroup3(MOVIMM_OPCODE, dst, constant), m_buffer.codeSize()); + m_buffer.putShortWithConstantInt(opc, constant, true); + } + + void loadConstantUnReusable(uint32_t constant, RegisterID dst, bool ensureSpace = false) + { + uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, 0); + + if (ensureSpace) + m_buffer.ensureSpace(maxInstructionSize, sizeof(uint32_t)); + + printInstr(getOpcodeGroup3(MOVIMM_OPCODE, dst, constant), m_buffer.codeSize()); + m_buffer.putShortWithConstantInt(opc, constant); + } + + // Flow control + + AssemblerLabel call() + { + RegisterID scr = claimScratch(); + m_buffer.ensureSpace(maxInstructionSize + 4, sizeof(uint32_t)); + loadConstantUnReusable(0x0, scr); + branch(JSR_OPCODE, scr); + nop(); + releaseScratch(scr); + return m_buffer.label(); + } + + AssemblerLabel call(RegisterID dst) + { + m_buffer.ensureSpace(maxInstructionSize + 2); + branch(JSR_OPCODE, dst); + nop(); + return m_buffer.label(); + } + + AssemblerLabel jmp() + { + RegisterID scr = claimScratch(); + m_buffer.ensureSpace(maxInstructionSize + 4, sizeof(uint32_t)); + AssemblerLabel label = m_buffer.label(); + loadConstantUnReusable(0x0, scr); + branch(BRAF_OPCODE, scr); + nop(); + releaseScratch(scr); + return label; + } + + void extraInstrForBranch(RegisterID dst) + { + loadConstantUnReusable(0x0, dst); + nop(); + nop(); + } + + AssemblerLabel jmp(RegisterID dst) + { + jmpReg(dst); + return m_buffer.label(); + } + + void jmpReg(RegisterID dst) + { + m_buffer.ensureSpace(maxInstructionSize + 2); + branch(JMP_OPCODE, dst); + nop(); + } + + AssemblerLabel jne() + { + AssemblerLabel label = m_buffer.label(); + branch(BF_OPCODE, 0); + return label; + } + + AssemblerLabel je() + { + AssemblerLabel label = m_buffer.label(); + branch(BT_OPCODE, 0); + return label; + } + + AssemblerLabel bra() + { + AssemblerLabel label = m_buffer.label(); + branch(BRA_OPCODE, 0); + return label; + } + + void ret() + { + m_buffer.ensureSpace(maxInstructionSize + 2); + oneShortOp(RTS_OPCODE, false); + } + + AssemblerLabel labelIgnoringWatchpoints() + { + m_buffer.ensureSpaceForAnyInstruction(); + return m_buffer.label(); + } + + AssemblerLabel label() + { + m_buffer.ensureSpaceForAnyInstruction(); + return m_buffer.label(); + } + + int sizeOfConstantPool() + { + return m_buffer.sizeOfConstantPool(); + } + + AssemblerLabel align(int alignment) + { + m_buffer.ensureSpace(maxInstructionSize + 2); + while (!m_buffer.isAligned(alignment)) { + nop(); + m_buffer.ensureSpace(maxInstructionSize + 2); + } + return label(); + } + + static void changePCrelativeAddress(int offset, uint16_t* instructionPtr, uint32_t newAddress) + { + uint32_t address = (offset << 2) + ((reinterpret_cast(instructionPtr) + 4) &(~0x3)); + *reinterpret_cast(address) = newAddress; + } + + static uint32_t readPCrelativeAddress(int offset, uint16_t* instructionPtr) + { + uint32_t address = (offset << 2) + ((reinterpret_cast(instructionPtr) + 4) &(~0x3)); + return *reinterpret_cast(address); + } + + static uint16_t* getInstructionPtr(void* code, int offset) + { + return reinterpret_cast (reinterpret_cast(code) + offset); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + + uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset); + uint16_t instruction = *instructionPtr; + int offsetBits = (reinterpret_cast(to) - reinterpret_cast(code)) - from.m_offset; + + if (((instruction & 0xff00) == BT_OPCODE) || ((instruction & 0xff00) == BF_OPCODE)) { + /* BT label ==> BF 2 + nop LDR reg + nop braf @reg + nop nop + */ + offsetBits -= 8; + instruction ^= 0x0202; + *instructionPtr++ = instruction; + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); + instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); + *instructionPtr = instruction; + printBlockInstr(instructionPtr - 2, from.m_offset, 3); + return; + } + + /* MOV #imm, reg => LDR reg + braf @reg braf @reg + nop nop + */ + ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); + + offsetBits -= 4; + if (offsetBits >= -4096 && offsetBits <= 4094) { + *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); + *(++instructionPtr) = NOP_OPCODE; + printBlockInstr(instructionPtr - 1, from.m_offset, 2); + return; + } + + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); + printInstr(*instructionPtr, from.m_offset + 2); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset); + instructionPtr -= 3; + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(to)); + } + + static void linkPointer(void* code, AssemblerLabel where, void* value) + { + uint16_t* instructionPtr = getInstructionPtr(code, where.m_offset); + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(value)); + } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + ASSERT(call.isSet()); + return call.m_offset; + } + + static uint32_t* getLdrImmAddressOnPool(SH4Word* insn, uint32_t* constPool) + { + return (constPool + (*insn & 0xff)); + } + + static SH4Word patchConstantPoolLoad(SH4Word load, int value) + { + return ((load & ~0xff) | value); + } + + static SH4Buffer::TwoShorts placeConstantPoolBarrier(int offset) + { + ASSERT(((offset >> 1) <=2047) && ((offset >> 1) >= -2048)); + + SH4Buffer::TwoShorts m_barrier; + m_barrier.high = (BRA_OPCODE | (offset >> 1)); + m_barrier.low = NOP_OPCODE; + printInstr(((BRA_OPCODE | (offset >> 1))), 0); + printInstr(NOP_OPCODE, 0); + return m_barrier; + } + + static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr) + { + SH4Word* instructionPtr = reinterpret_cast(loadAddr); + SH4Word instruction = *instructionPtr; + SH4Word index = instruction & 0xff; + + if ((instruction & 0xf000) != MOVIMM_OPCODE) + return; + + ASSERT((((reinterpret_cast(constPoolAddr) - reinterpret_cast(loadAddr)) + index * 4)) < 1024); + + int offset = reinterpret_cast(constPoolAddr) + (index * 4) - ((reinterpret_cast(instructionPtr) & ~0x03) + 4); + instruction &=0xf00; + instruction |= 0xd000; + offset &= 0x03ff; + instruction |= (offset >> 2); + *instructionPtr = instruction; + printInstr(instruction, reinterpret_cast(loadAddr)); + } + + static void repatchPointer(void* where, void* value) + { + patchPointer(where, value); + } + + static void* readPointer(void* code) + { + return reinterpret_cast(readInt32(code)); + } + + static void repatchInt32(void* where, int32_t value) + { + uint16_t* instructionPtr = reinterpret_cast(where); + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, value); + } + + static void repatchCompact(void* where, int32_t value) + { + ASSERT(value >= 0); + ASSERT(value <= 60); + *reinterpret_cast(where) = ((*reinterpret_cast(where) & 0xfff0) | (value >> 2)); + cacheFlush(reinterpret_cast(where), sizeof(uint16_t)); + } + + static void relinkCall(void* from, void* to) + { + uint16_t* instructionPtr = reinterpret_cast(from); + instructionPtr -= 3; + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(to)); + } + + static void relinkJump(void* from, void* to) + { + uint16_t* instructionPtr = reinterpret_cast (from); + uint16_t instruction = *instructionPtr; + int32_t offsetBits = (reinterpret_cast(to) - reinterpret_cast(from)); + + if (((*instructionPtr & 0xff00) == BT_OPCODE) || ((*instructionPtr & 0xff00) == BF_OPCODE)) { + offsetBits -= 8; + instructionPtr++; + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); + instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); + *instructionPtr = instruction; + printBlockInstr(instructionPtr, reinterpret_cast(from) + 1, 3); + return; + } + + ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); + offsetBits -= 4; + if (offsetBits >= -4096 && offsetBits <= 4094) { + *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); + *(++instructionPtr) = NOP_OPCODE; + printBlockInstr(instructionPtr - 2, reinterpret_cast(from), 2); + return; + } + + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); + printInstr(*instructionPtr, reinterpret_cast(from)); + } + + // Linking & patching + + static void revertJump(void* instructionStart, SH4Word imm) + { + SH4Word *insn = reinterpret_cast(instructionStart); + SH4Word disp; + + ASSERT((insn[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); + + disp = insn[0] & 0x00ff; + insn += 2 + (disp << 1); // PC += 4 + (disp*4) + insn = (SH4Word *) ((unsigned) insn & (~3)); + insn[0] = imm; + cacheFlush(insn, sizeof(SH4Word)); + } + + void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type = JumpFar) + { + ASSERT(to.isSet()); + ASSERT(from.isSet()); + + uint16_t* instructionPtr = getInstructionPtr(data(), from.m_offset); + uint16_t instruction = *instructionPtr; + int offsetBits; + + if (type == JumpNear) { + ASSERT((instruction == BT_OPCODE) || (instruction == BF_OPCODE) || (instruction == BRA_OPCODE)); + int offset = (codeSize() - from.m_offset) - 4; + *instructionPtr++ = instruction | (offset >> 1); + printInstr(*instructionPtr, from.m_offset + 2); + return; + } + + if (((instruction & 0xff00) == BT_OPCODE) || ((instruction & 0xff00) == BF_OPCODE)) { + /* BT label => BF 2 + nop LDR reg + nop braf @reg + nop nop + */ + offsetBits = (to.m_offset - from.m_offset) - 8; + instruction ^= 0x0202; + *instructionPtr++ = instruction; + if ((*instructionPtr & 0xf000) == 0xe000) { + uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress()); + *addr = offsetBits; + } else + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); + instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); + *instructionPtr = instruction; + printBlockInstr(instructionPtr - 2, from.m_offset, 3); + return; + } + + /* MOV # imm, reg => LDR reg + braf @reg braf @reg + nop nop + */ + ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); + offsetBits = (to.m_offset - from.m_offset) - 4; + if (offsetBits >= -4096 && offsetBits <= 4094) { + *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); + *(++instructionPtr) = NOP_OPCODE; + printBlockInstr(instructionPtr - 1, from.m_offset, 2); + return; + } + + instruction = *instructionPtr; + if ((instruction & 0xf000) == 0xe000) { + uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress()); + *addr = offsetBits - 2; + printInstr(*instructionPtr, from.m_offset + 2); + return; + } + + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); + printInstr(*instructionPtr, from.m_offset + 2); + } + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + static void patchPointer(void* code, AssemblerLabel where, void* value) + { + patchPointer(reinterpret_cast(code) + where.m_offset, value); + } + + static void patchPointer(void* code, void* value) + { + patchInt32(code, reinterpret_cast(value)); + } + + static void patchInt32(void* code, uint32_t value) + { + changePCrelativeAddress((*(reinterpret_cast(code)) & 0xff), reinterpret_cast(code), value); + } + + static uint32_t readInt32(void* code) + { + return readPCrelativeAddress((*(reinterpret_cast(code)) & 0xff), reinterpret_cast(code)); + } + + static void* readCallTarget(void* from) + { + uint16_t* instructionPtr = static_cast(from); + instructionPtr -= 3; + return reinterpret_cast(readPCrelativeAddress((*instructionPtr & 0xff), instructionPtr)); + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + return m_buffer.executableCopy(globalData, ownerUID, effort); + } + + static void cacheFlush(void* code, size_t size) + { +#if !OS(LINUX) +#error "The cacheFlush support is missing on this platform." +#elif defined CACHEFLUSH_D_L2 + syscall(__NR_cacheflush, reinterpret_cast(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I | CACHEFLUSH_D_L2); +#else + syscall(__NR_cacheflush, reinterpret_cast(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I); +#endif + } + + void prefix(uint16_t pre) + { + m_buffer.putByte(pre); + } + + void oneShortOp(uint16_t opcode, bool checksize = true, bool isDouble = true) + { + printInstr(opcode, m_buffer.codeSize(), isDouble); + if (checksize) + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putShortUnchecked(opcode); + } + + void ensureSpace(int space) + { + m_buffer.ensureSpace(space); + } + + void ensureSpace(int insnSpace, int constSpace) + { + m_buffer.ensureSpace(insnSpace, constSpace); + } + + // Administrative methods + + void* data() const { return m_buffer.data(); } + size_t codeSize() const { return m_buffer.codeSize(); } + +#ifdef SH4_ASSEMBLER_TRACING + static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true) + { + if (!getenv("JavaScriptCoreDumpJIT")) + return; + + const char *format = 0; + printfStdoutInstr("offset: 0x%8.8x\t", size); + switch (opc) { + case BRK_OPCODE: + format = " BRK\n"; + break; + case NOP_OPCODE: + format = " NOP\n"; + break; + case RTS_OPCODE: + format =" *RTS\n"; + break; + case SETS_OPCODE: + format = " SETS\n"; + break; + case SETT_OPCODE: + format = " SETT\n"; + break; + case CLRT_OPCODE: + format = " CLRT\n"; + break; + case FSCHG_OPCODE: + format = " FSCHG\n"; + break; + } + if (format) { + printfStdoutInstr(format); + return; + } + switch (opc & 0xf0ff) { + case BRAF_OPCODE: + format = " *BRAF R%d\n"; + break; + case DT_OPCODE: + format = " DT R%d\n"; + break; + case CMPPL_OPCODE: + format = " CMP/PL R%d\n"; + break; + case CMPPZ_OPCODE: + format = " CMP/PZ R%d\n"; + break; + case JMP_OPCODE: + format = " *JMP @R%d\n"; + break; + case JSR_OPCODE: + format = " *JSR @R%d\n"; + break; + case LDSPR_OPCODE: + format = " LDS R%d, PR\n"; + break; + case LDSLPR_OPCODE: + format = " LDS.L @R%d+, PR\n"; + break; + case MOVT_OPCODE: + format = " MOVT R%d\n"; + break; + case SHAL_OPCODE: + format = " SHAL R%d\n"; + break; + case SHAR_OPCODE: + format = " SHAR R%d\n"; + break; + case SHLL_OPCODE: + format = " SHLL R%d\n"; + break; + case SHLL2_OPCODE: + format = " SHLL2 R%d\n"; + break; + case SHLL8_OPCODE: + format = " SHLL8 R%d\n"; + break; + case SHLL16_OPCODE: + format = " SHLL16 R%d\n"; + break; + case SHLR_OPCODE: + format = " SHLR R%d\n"; + break; + case SHLR2_OPCODE: + format = " SHLR2 R%d\n"; + break; + case SHLR8_OPCODE: + format = " SHLR8 R%d\n"; + break; + case SHLR16_OPCODE: + format = " SHLR16 R%d\n"; + break; + case STSPR_OPCODE: + format = " STS PR, R%d\n"; + break; + case STSLPR_OPCODE: + format = " STS.L PR, @-R%d\n"; + break; + case LDS_RM_FPUL_OPCODE: + format = " LDS R%d, FPUL\n"; + break; + case STS_FPUL_RN_OPCODE: + format = " STS FPUL, R%d \n"; + break; + case FLDS_FRM_FPUL_OPCODE: + format = " FLDS FR%d, FPUL\n"; + break; + case FSTS_FPUL_FRN_OPCODE: + format = " FSTS FPUL, R%d \n"; + break; + case LDSFPSCR_OPCODE: + format = " LDS R%d, FPSCR \n"; + break; + case STSFPSCR_OPCODE: + format = " STS FPSCR, R%d \n"; + break; + case STSMACL_OPCODE: + format = " STS MACL, R%d \n"; + break; + case STSMACH_OPCODE: + format = " STS MACH, R%d \n"; + break; + case BSRF_OPCODE: + format = " *BSRF R%d"; + break; + case FTRC_OPCODE: + format = " FTRC FR%d, FPUL\n"; + break; + } + if (format) { + printfStdoutInstr(format, getRn(opc)); + return; + } + switch (opc & 0xf0ff) { + case FNEG_OPCODE: + format = " FNEG DR%d\n"; + break; + case FLOAT_OPCODE: + format = " FLOAT DR%d\n"; + break; + case FTRC_OPCODE: + format = " FTRC FR%d, FPUL\n"; + break; + case FSQRT_OPCODE: + format = " FSQRT FR%d\n"; + break; + case FCNVDS_DRM_FPUL_OPCODE: + format = " FCNVDS FR%d, FPUL\n"; + break; + case FCNVSD_FPUL_DRN_OPCODE: + format = " FCNVSD FPUL, FR%d\n"; + break; + } + if (format) { + if (isdoubleInst) + printfStdoutInstr(format, getDRn(opc) << 1); + else + printfStdoutInstr(format, getRn(opc)); + return; + } + switch (opc & 0xf00f) { + case ADD_OPCODE: + format = " ADD R%d, R%d\n"; + break; + case ADDC_OPCODE: + format = " ADDC R%d, R%d\n"; + break; + case ADDV_OPCODE: + format = " ADDV R%d, R%d\n"; + break; + case AND_OPCODE: + format = " AND R%d, R%d\n"; + break; + case DIV1_OPCODE: + format = " DIV1 R%d, R%d\n"; + break; + case CMPEQ_OPCODE: + format = " CMP/EQ R%d, R%d\n"; + break; + case CMPGE_OPCODE: + format = " CMP/GE R%d, R%d\n"; + break; + case CMPGT_OPCODE: + format = " CMP/GT R%d, R%d\n"; + break; + case CMPHI_OPCODE: + format = " CMP/HI R%d, R%d\n"; + break; + case CMPHS_OPCODE: + format = " CMP/HS R%d, R%d\n"; + break; + case MOV_OPCODE: + format = " MOV R%d, R%d\n"; + break; + case MOVB_WRITE_RN_OPCODE: + format = " MOV.B R%d, @R%d\n"; + break; + case MOVB_WRITE_RNDEC_OPCODE: + format = " MOV.B R%d, @-R%d\n"; + break; + case MOVB_WRITE_R0RN_OPCODE: + format = " MOV.B R%d, @(R0, R%d)\n"; + break; + case MOVB_READ_RM_OPCODE: + format = " MOV.B @R%d, R%d\n"; + break; + case MOVB_READ_RMINC_OPCODE: + format = " MOV.B @R%d+, R%d\n"; + break; + case MOVB_READ_R0RM_OPCODE: + format = " MOV.B @(R0, R%d), R%d\n"; + break; + case MOVL_WRITE_RN_OPCODE: + format = " MOV.L R%d, @R%d\n"; + break; + case MOVL_WRITE_RNDEC_OPCODE: + format = " MOV.L R%d, @-R%d\n"; + break; + case MOVL_WRITE_R0RN_OPCODE: + format = " MOV.L R%d, @(R0, R%d)\n"; + break; + case MOVL_READ_RM_OPCODE: + format = " MOV.L @R%d, R%d\n"; + break; + case MOVL_READ_RMINC_OPCODE: + format = " MOV.L @R%d+, R%d\n"; + break; + case MOVL_READ_R0RM_OPCODE: + format = " MOV.L @(R0, R%d), R%d\n"; + break; + case MULL_OPCODE: + format = " MUL.L R%d, R%d\n"; + break; + case DMULL_L_OPCODE: + format = " DMULU.L R%d, R%d\n"; + break; + case DMULSL_OPCODE: + format = " DMULS.L R%d, R%d\n"; + break; + case NEG_OPCODE: + format = " NEG R%d, R%d\n"; + break; + case NEGC_OPCODE: + format = " NEGC R%d, R%d\n"; + break; + case NOT_OPCODE: + format = " NOT R%d, R%d\n"; + break; + case OR_OPCODE: + format = " OR R%d, R%d\n"; + break; + case SHAD_OPCODE: + format = " SHAD R%d, R%d\n"; + break; + case SHLD_OPCODE: + format = " SHLD R%d, R%d\n"; + break; + case SUB_OPCODE: + format = " SUB R%d, R%d\n"; + break; + case SUBC_OPCODE: + format = " SUBC R%d, R%d\n"; + break; + case SUBV_OPCODE: + format = " SUBV R%d, R%d\n"; + break; + case TST_OPCODE: + format = " TST R%d, R%d\n"; + break; + case XOR_OPCODE: + format = " XOR R%d, R%d\n";break; + case MOVW_WRITE_RN_OPCODE: + format = " MOV.W R%d, @R%d\n"; + break; + case MOVW_READ_RM_OPCODE: + format = " MOV.W @R%d, R%d\n"; + break; + case MOVW_READ_R0RM_OPCODE: + format = " MOV.W @(R0, R%d), R%d\n"; + break; + case EXTUB_OPCODE: + format = " EXTU.B R%d, R%d\n"; + break; + case EXTUW_OPCODE: + format = " EXTU.W R%d, R%d\n"; + break; + } + if (format) { + printfStdoutInstr(format, getRm(opc), getRn(opc)); + return; + } + switch (opc & 0xf00f) { + case FSUB_OPCODE: + format = " FSUB FR%d, FR%d\n"; + break; + case FADD_OPCODE: + format = " FADD FR%d, FR%d\n"; + break; + case FDIV_OPCODE: + format = " FDIV FR%d, FR%d\n"; + break; + case FMUL_OPCODE: + format = " DMULL FR%d, FR%d\n"; + break; + case FMOV_OPCODE: + format = " FMOV FR%d, FR%d\n"; + break; + case FCMPEQ_OPCODE: + format = " FCMP/EQ FR%d, FR%d\n"; + break; + case FCMPGT_OPCODE: + format = " FCMP/GT FR%d, FR%d\n"; + break; + } + if (format) { + if (isdoubleInst) + printfStdoutInstr(format, getDRm(opc) << 1, getDRn(opc) << 1); + else + printfStdoutInstr(format, getRm(opc), getRn(opc)); + return; + } + switch (opc & 0xf00f) { + case FMOVS_WRITE_RN_DEC_OPCODE: + format = " %s FR%d, @-R%d\n"; + break; + case FMOVS_WRITE_RN_OPCODE: + format = " %s FR%d, @R%d\n"; + break; + case FMOVS_WRITE_R0RN_OPCODE: + format = " %s FR%d, @(R0, R%d)\n"; + break; + } + if (format) { + if (isdoubleInst) + printfStdoutInstr(format, "FMOV", getDRm(opc) << 1, getDRn(opc)); + else + printfStdoutInstr(format, "FMOV.S", getRm(opc), getRn(opc)); + return; + } + switch (opc & 0xf00f) { + case FMOVS_READ_RM_OPCODE: + format = " %s @R%d, FR%d\n"; + break; + case FMOVS_READ_RM_INC_OPCODE: + format = " %s @R%d+, FR%d\n"; + break; + case FMOVS_READ_R0RM_OPCODE: + format = " %s @(R0, R%d), FR%d\n"; + break; + } + if (format) { + if (isdoubleInst) + printfStdoutInstr(format, "FMOV", getDRm(opc), getDRn(opc) << 1); + else + printfStdoutInstr(format, "FMOV.S", getRm(opc), getRn(opc)); + return; + } + switch (opc & 0xff00) { + case BF_OPCODE: + format = " BF %d\n"; + break; + case BFS_OPCODE: + format = " *BF/S %d\n"; + break; + case ANDIMM_OPCODE: + format = " AND #%d, R0\n"; + break; + case BT_OPCODE: + format = " BT %d\n"; + break; + case BTS_OPCODE: + format = " *BT/S %d\n"; + break; + case CMPEQIMM_OPCODE: + format = " CMP/EQ #%d, R0\n"; + break; + case MOVB_WRITE_OFFGBR_OPCODE: + format = " MOV.B R0, @(%d, GBR)\n"; + break; + case MOVB_READ_OFFGBR_OPCODE: + format = " MOV.B @(%d, GBR), R0\n"; + break; + case MOVL_WRITE_OFFGBR_OPCODE: + format = " MOV.L R0, @(%d, GBR)\n"; + break; + case MOVL_READ_OFFGBR_OPCODE: + format = " MOV.L @(%d, GBR), R0\n"; + break; + case MOVA_READ_OFFPC_OPCODE: + format = " MOVA @(%d, PC), R0\n"; + break; + case ORIMM_OPCODE: + format = " OR #%d, R0\n"; + break; + case ORBIMM_OPCODE: + format = " OR.B #%d, @(R0, GBR)\n"; + break; + case TSTIMM_OPCODE: + format = " TST #%d, R0\n"; + break; + case TSTB_OPCODE: + format = " TST.B %d, @(R0, GBR)\n"; + break; + case XORIMM_OPCODE: + format = " XOR #%d, R0\n"; + break; + case XORB_OPCODE: + format = " XOR.B %d, @(R0, GBR)\n"; + break; + } + if (format) { + printfStdoutInstr(format, getImm8(opc)); + return; + } + switch (opc & 0xff00) { + case MOVB_WRITE_OFFRN_OPCODE: + format = " MOV.B R0, @(%d, R%d)\n"; + break; + case MOVB_READ_OFFRM_OPCODE: + format = " MOV.B @(%d, R%d), R0\n"; + break; + } + if (format) { + printfStdoutInstr(format, getDisp(opc), getRm(opc)); + return; + } + switch (opc & 0xf000) { + case BRA_OPCODE: + format = " *BRA %d\n"; + break; + case BSR_OPCODE: + format = " *BSR %d\n"; + break; + } + if (format) { + printfStdoutInstr(format, getImm12(opc)); + return; + } + switch (opc & 0xf000) { + case MOVL_READ_OFFPC_OPCODE: + format = " MOV.L @(%d, PC), R%d\n"; + break; + case ADDIMM_OPCODE: + format = " ADD #%d, R%d\n"; + break; + case MOVIMM_OPCODE: + format = " MOV #%d, R%d\n"; + break; + case MOVW_READ_OFFPC_OPCODE: + format = " MOV.W @(%d, PC), R%d\n"; + break; + } + if (format) { + printfStdoutInstr(format, getImm8(opc), getRn(opc)); + return; + } + switch (opc & 0xf000) { + case MOVL_WRITE_OFFRN_OPCODE: + format = " MOV.L R%d, @(%d, R%d)\n"; + printfStdoutInstr(format, getRm(opc), getDisp(opc), getRn(opc)); + break; + case MOVL_READ_OFFRM_OPCODE: + format = " MOV.L @(%d, R%d), R%d\n"; + printfStdoutInstr(format, getDisp(opc), getRm(opc), getRn(opc)); + break; + } + } + + static void printfStdoutInstr(const char* format, ...) + { + if (getenv("JavaScriptCoreDumpJIT")) { + va_list args; + va_start(args, format); + vprintfStdoutInstr(format, args); + va_end(args); + } + } + + static void vprintfStdoutInstr(const char* format, va_list args) + { + if (getenv("JavaScriptCoreDumpJIT")) + WTF::dataLogFV(format, args); + } + + static void printBlockInstr(uint16_t* first, unsigned offset, int nbInstr) + { + printfStdoutInstr(">> repatch instructions after link\n"); + for (int i = 0; i <= nbInstr; i++) + printInstr(*(first + i), offset + i); + printfStdoutInstr(">> end repatch\n"); + } +#else + static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true) { }; + static void printBlockInstr(uint16_t* first, unsigned offset, int nbInstr) { }; +#endif + + static void replaceWithLoad(void* instructionStart) + { + SH4Word* insPtr = reinterpret_cast(instructionStart); + + insPtr += 2; // skip MOV and ADD opcodes + + if (((*insPtr) & 0xf00f) != MOVL_READ_RM_OPCODE) { + *insPtr = MOVL_READ_RM_OPCODE | (*insPtr & 0x0ff0); + cacheFlush(insPtr, sizeof(SH4Word)); + } + } + + static void replaceWithAddressComputation(void* instructionStart) + { + SH4Word* insPtr = reinterpret_cast(instructionStart); + + insPtr += 2; // skip MOV and ADD opcodes + + if (((*insPtr) & 0xf00f) != MOV_OPCODE) { + *insPtr = MOV_OPCODE | (*insPtr & 0x0ff0); + cacheFlush(insPtr, sizeof(SH4Word)); + } + } + +private: + SH4Buffer m_buffer; + int m_claimscratchReg; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(SH4) + +#endif // SH4Assembler_h diff --git a/3rdparty/masm/assembler/X86Assembler.h b/3rdparty/masm/assembler/X86Assembler.h new file mode 100644 index 0000000000..25ff6f0a50 --- /dev/null +++ b/3rdparty/masm/assembler/X86Assembler.h @@ -0,0 +1,2540 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef X86Assembler_h +#define X86Assembler_h + +#if ENABLE(ASSEMBLER) && (CPU(X86) || CPU(X86_64)) + +#include "AssemblerBuffer.h" +#include "JITCompilationEffort.h" +#include +#include +#include + +namespace JSC { + +inline bool CAN_SIGN_EXTEND_8_32(int32_t value) { return value == (int32_t)(signed char)value; } + +namespace X86Registers { + typedef enum { + eax, + ecx, + edx, + ebx, + esp, + ebp, + esi, + edi, + +#if CPU(X86_64) + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, +#endif + } RegisterID; + + typedef enum { + xmm0, + xmm1, + xmm2, + xmm3, + xmm4, + xmm5, + xmm6, + xmm7, + } XMMRegisterID; +} + +class X86Assembler { +public: + typedef X86Registers::RegisterID RegisterID; + typedef X86Registers::XMMRegisterID XMMRegisterID; + typedef XMMRegisterID FPRegisterID; + + typedef enum { + ConditionO, + ConditionNO, + ConditionB, + ConditionAE, + ConditionE, + ConditionNE, + ConditionBE, + ConditionA, + ConditionS, + ConditionNS, + ConditionP, + ConditionNP, + ConditionL, + ConditionGE, + ConditionLE, + ConditionG, + + ConditionC = ConditionB, + ConditionNC = ConditionAE, + } Condition; + +private: + typedef enum { + OP_ADD_EvGv = 0x01, + OP_ADD_GvEv = 0x03, + OP_OR_EvGv = 0x09, + OP_OR_GvEv = 0x0B, + OP_2BYTE_ESCAPE = 0x0F, + OP_AND_EvGv = 0x21, + OP_AND_GvEv = 0x23, + OP_SUB_EvGv = 0x29, + OP_SUB_GvEv = 0x2B, + PRE_PREDICT_BRANCH_NOT_TAKEN = 0x2E, + OP_XOR_EvGv = 0x31, + OP_XOR_GvEv = 0x33, + OP_CMP_EvGv = 0x39, + OP_CMP_GvEv = 0x3B, +#if CPU(X86_64) + PRE_REX = 0x40, +#endif + OP_PUSH_EAX = 0x50, + OP_POP_EAX = 0x58, +#if CPU(X86_64) + OP_MOVSXD_GvEv = 0x63, +#endif + PRE_OPERAND_SIZE = 0x66, + PRE_SSE_66 = 0x66, + OP_PUSH_Iz = 0x68, + OP_IMUL_GvEvIz = 0x69, + OP_GROUP1_EbIb = 0x80, + OP_GROUP1_EvIz = 0x81, + OP_GROUP1_EvIb = 0x83, + OP_TEST_EbGb = 0x84, + OP_TEST_EvGv = 0x85, + OP_XCHG_EvGv = 0x87, + OP_MOV_EbGb = 0x88, + OP_MOV_EvGv = 0x89, + OP_MOV_GvEv = 0x8B, + OP_LEA = 0x8D, + OP_GROUP1A_Ev = 0x8F, + OP_NOP = 0x90, + OP_CDQ = 0x99, + OP_MOV_EAXOv = 0xA1, + OP_MOV_OvEAX = 0xA3, + OP_MOV_EAXIv = 0xB8, + OP_GROUP2_EvIb = 0xC1, + OP_RET = 0xC3, + OP_GROUP11_EvIb = 0xC6, + OP_GROUP11_EvIz = 0xC7, + OP_INT3 = 0xCC, + OP_GROUP2_Ev1 = 0xD1, + OP_GROUP2_EvCL = 0xD3, + OP_ESCAPE_DD = 0xDD, + OP_CALL_rel32 = 0xE8, + OP_JMP_rel32 = 0xE9, + PRE_SSE_F2 = 0xF2, + PRE_SSE_F3 = 0xF3, + OP_HLT = 0xF4, + OP_GROUP3_EbIb = 0xF6, + OP_GROUP3_Ev = 0xF7, + OP_GROUP3_EvIz = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test. + OP_GROUP5_Ev = 0xFF, + } OneByteOpcodeID; + + typedef enum { + OP2_MOVSD_VsdWsd = 0x10, + OP2_MOVSD_WsdVsd = 0x11, + OP2_MOVSS_VsdWsd = 0x10, + OP2_MOVSS_WsdVsd = 0x11, + OP2_CVTSI2SD_VsdEd = 0x2A, + OP2_CVTTSD2SI_GdWsd = 0x2C, + OP2_UCOMISD_VsdWsd = 0x2E, + OP2_ADDSD_VsdWsd = 0x58, + OP2_MULSD_VsdWsd = 0x59, + OP2_CVTSD2SS_VsdWsd = 0x5A, + OP2_CVTSS2SD_VsdWsd = 0x5A, + OP2_SUBSD_VsdWsd = 0x5C, + OP2_DIVSD_VsdWsd = 0x5E, + OP2_SQRTSD_VsdWsd = 0x51, + OP2_ANDNPD_VpdWpd = 0x55, + OP2_XORPD_VpdWpd = 0x57, + OP2_MOVD_VdEd = 0x6E, + OP2_MOVD_EdVd = 0x7E, + OP2_JCC_rel32 = 0x80, + OP_SETCC = 0x90, + OP2_IMUL_GvEv = 0xAF, + OP2_MOVZX_GvEb = 0xB6, + OP2_MOVSX_GvEb = 0xBE, + OP2_MOVZX_GvEw = 0xB7, + OP2_MOVSX_GvEw = 0xBF, + OP2_PEXTRW_GdUdIb = 0xC5, + OP2_PSLLQ_UdqIb = 0x73, + OP2_PSRLQ_UdqIb = 0x73, + OP2_POR_VdqWdq = 0XEB, + } TwoByteOpcodeID; + + TwoByteOpcodeID jccRel32(Condition cond) + { + return (TwoByteOpcodeID)(OP2_JCC_rel32 + cond); + } + + TwoByteOpcodeID setccOpcode(Condition cond) + { + return (TwoByteOpcodeID)(OP_SETCC + cond); + } + + typedef enum { + GROUP1_OP_ADD = 0, + GROUP1_OP_OR = 1, + GROUP1_OP_ADC = 2, + GROUP1_OP_AND = 4, + GROUP1_OP_SUB = 5, + GROUP1_OP_XOR = 6, + GROUP1_OP_CMP = 7, + + GROUP1A_OP_POP = 0, + + GROUP2_OP_ROL = 0, + GROUP2_OP_ROR = 1, + GROUP2_OP_RCL = 2, + GROUP2_OP_RCR = 3, + + GROUP2_OP_SHL = 4, + GROUP2_OP_SHR = 5, + GROUP2_OP_SAR = 7, + + GROUP3_OP_TEST = 0, + GROUP3_OP_NOT = 2, + GROUP3_OP_NEG = 3, + GROUP3_OP_IDIV = 7, + + GROUP5_OP_CALLN = 2, + GROUP5_OP_JMPN = 4, + GROUP5_OP_PUSH = 6, + + GROUP11_MOV = 0, + + GROUP14_OP_PSLLQ = 6, + GROUP14_OP_PSRLQ = 2, + + ESCAPE_DD_FSTP_doubleReal = 3, + } GroupOpcodeID; + + class X86InstructionFormatter; +public: + + X86Assembler() + : m_indexOfLastWatchpoint(INT_MIN) + , m_indexOfTailOfLastWatchpoint(INT_MIN) + { + } + + // Stack operations: + + void push_r(RegisterID reg) + { + m_formatter.oneByteOp(OP_PUSH_EAX, reg); + } + + void pop_r(RegisterID reg) + { + m_formatter.oneByteOp(OP_POP_EAX, reg); + } + + void push_i32(int imm) + { + m_formatter.oneByteOp(OP_PUSH_Iz); + m_formatter.immediate32(imm); + } + + void push_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_PUSH, base, offset); + } + + void pop_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP1A_Ev, GROUP1A_OP_POP, base, offset); + } + + // Arithmetic operations: + +#if !CPU(X86_64) + void adcl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADC, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADC, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void addl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_ADD_EvGv, src, dst); + } + + void addl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_ADD_GvEv, dst, base, offset); + } + +#if !CPU(X86_64) + void addl_mr(const void* addr, RegisterID dst) + { + m_formatter.oneByteOp(OP_ADD_GvEv, dst, addr); + } +#endif + + void addl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_ADD_EvGv, src, base, offset); + } + + void addl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, dst); + m_formatter.immediate32(imm); + } + } + + void addl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, offset); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void addq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_ADD_EvGv, src, dst); + } + + void addq_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64(OP_ADD_GvEv, dst, base, offset); + } + + void addq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_ADD, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_ADD, dst); + m_formatter.immediate32(imm); + } + } + + void addq_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, offset); + m_formatter.immediate32(imm); + } + } +#else + void addl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void andl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_AND_EvGv, src, dst); + } + + void andl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_AND_GvEv, dst, base, offset); + } + + void andl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_AND_EvGv, src, base, offset); + } + + void andl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, dst); + m_formatter.immediate32(imm); + } + } + + void andl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, base, offset); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void andq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_AND_EvGv, src, dst); + } + + void andq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_AND, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_AND, dst); + m_formatter.immediate32(imm); + } + } +#else + void andl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void negl_r(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, dst); + } + +#if CPU(X86_64) + void negq_r(RegisterID dst) + { + m_formatter.oneByteOp64(OP_GROUP3_Ev, GROUP3_OP_NEG, dst); + } +#endif + + void negl_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, base, offset); + } + + void notl_r(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NOT, dst); + } + + void notl_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NOT, base, offset); + } + + void orl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_OR_EvGv, src, dst); + } + + void orl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_OR_GvEv, dst, base, offset); + } + + void orl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_OR_EvGv, src, base, offset); + } + + void orl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, dst); + m_formatter.immediate32(imm); + } + } + + void orl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, base, offset); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void orq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_OR_EvGv, src, dst); + } + + void orq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_OR, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_OR, dst); + m_formatter.immediate32(imm); + } + } +#else + void orl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, addr); + m_formatter.immediate32(imm); + } + } + + void orl_rm(RegisterID src, const void* addr) + { + m_formatter.oneByteOp(OP_OR_EvGv, src, addr); + } +#endif + + void subl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_SUB_EvGv, src, dst); + } + + void subl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_SUB_GvEv, dst, base, offset); + } + + void subl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_SUB_EvGv, src, base, offset); + } + + void subl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, dst); + m_formatter.immediate32(imm); + } + } + + void subl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, base, offset); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void subq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_SUB_EvGv, src, dst); + } + + void subq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_SUB, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_SUB, dst); + m_formatter.immediate32(imm); + } + } +#else + void subl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void xorl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_XOR_EvGv, src, dst); + } + + void xorl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_XOR_GvEv, dst, base, offset); + } + + void xorl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_XOR_EvGv, src, base, offset); + } + + void xorl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_XOR, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_XOR, base, offset); + m_formatter.immediate32(imm); + } + } + + void xorl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_XOR, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_XOR, dst); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void xorq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_XOR_EvGv, src, dst); + } + + void xorq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_XOR, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_XOR, dst); + m_formatter.immediate32(imm); + } + } + + void xorq_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_XOR_EvGv, src, base, offset); + } + + void rorq_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_ROR, dst); + else { + m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_ROR, dst); + m_formatter.immediate8(imm); + } + } + +#endif + + void sarl_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SAR, dst); + else { + m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SAR, dst); + m_formatter.immediate8(imm); + } + } + + void sarl_CLr(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst); + } + + void shrl_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHR, dst); + else { + m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHR, dst); + m_formatter.immediate8(imm); + } + } + + void shrl_CLr(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHR, dst); + } + + void shll_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHL, dst); + else { + m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHL, dst); + m_formatter.immediate8(imm); + } + } + + void shll_CLr(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHL, dst); + } + +#if CPU(X86_64) + void sarq_CLr(RegisterID dst) + { + m_formatter.oneByteOp64(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst); + } + + void sarq_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_SAR, dst); + else { + m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_SAR, dst); + m_formatter.immediate8(imm); + } + } +#endif + + void imull_rr(RegisterID src, RegisterID dst) + { + m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, src); + } + + void imull_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, base, offset); + } + + void imull_i32r(RegisterID src, int32_t value, RegisterID dst) + { + m_formatter.oneByteOp(OP_IMUL_GvEvIz, dst, src); + m_formatter.immediate32(value); + } + + void idivl_r(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_IDIV, dst); + } + + // Comparisons: + + void cmpl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_CMP_EvGv, src, dst); + } + + void cmpl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_CMP_EvGv, src, base, offset); + } + + void cmpl_mr(int offset, RegisterID base, RegisterID src) + { + m_formatter.oneByteOp(OP_CMP_GvEv, src, base, offset); + } + + void cmpl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); + m_formatter.immediate32(imm); + } + } + + void cmpl_ir_force32(int imm, RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); + m_formatter.immediate32(imm); + } + + void cmpl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); + m_formatter.immediate32(imm); + } + } + + void cmpb_im(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, offset); + m_formatter.immediate8(imm); + } + + void cmpb_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate8(imm); + } + +#if CPU(X86) + void cmpb_im(int imm, const void* addr) + { + m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, addr); + m_formatter.immediate8(imm); + } +#endif + + void cmpl_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate32(imm); + } + } + + void cmpl_im_force32(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); + m_formatter.immediate32(imm); + } + +#if CPU(X86_64) + void cmpq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_CMP_EvGv, src, dst); + } + + void cmpq_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_CMP_EvGv, src, base, offset); + } + + void cmpq_mr(int offset, RegisterID base, RegisterID src) + { + m_formatter.oneByteOp64(OP_CMP_GvEv, src, base, offset); + } + + void cmpq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); + m_formatter.immediate32(imm); + } + } + + void cmpq_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); + m_formatter.immediate32(imm); + } + } + + void cmpq_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate32(imm); + } + } +#else + void cmpl_rm(RegisterID reg, const void* addr) + { + m_formatter.oneByteOp(OP_CMP_EvGv, reg, addr); + } + + void cmpl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void cmpw_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); + m_formatter.immediate16(imm); + } + } + + void cmpw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_CMP_EvGv, src, base, index, scale, offset); + } + + void cmpw_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate16(imm); + } + } + + void testl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_TEST_EvGv, src, dst); + } + + void testl_i32r(int imm, RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, dst); + m_formatter.immediate32(imm); + } + + void testl_i32m(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, offset); + m_formatter.immediate32(imm); + } + + void testb_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp8(OP_TEST_EbGb, src, dst); + } + + void testb_im(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, offset); + m_formatter.immediate8(imm); + } + + void testb_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, index, scale, offset); + m_formatter.immediate8(imm); + } + +#if CPU(X86) + void testb_im(int imm, const void* addr) + { + m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, addr); + m_formatter.immediate8(imm); + } +#endif + + void testl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset); + m_formatter.immediate32(imm); + } + +#if CPU(X86_64) + void testq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_TEST_EvGv, src, dst); + } + + void testq_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_TEST_EvGv, src, base, offset); + } + + void testq_i32r(int imm, RegisterID dst) + { + m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, dst); + m_formatter.immediate32(imm); + } + + void testq_i32m(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, offset); + m_formatter.immediate32(imm); + } + + void testq_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset); + m_formatter.immediate32(imm); + } +#endif + + void testw_rr(RegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_TEST_EvGv, src, dst); + } + + void testb_i8r(int imm, RegisterID dst) + { + m_formatter.oneByteOp8(OP_GROUP3_EbIb, GROUP3_OP_TEST, dst); + m_formatter.immediate8(imm); + } + + void setCC_r(Condition cond, RegisterID dst) + { + m_formatter.twoByteOp8(setccOpcode(cond), (GroupOpcodeID)0, dst); + } + + void sete_r(RegisterID dst) + { + m_formatter.twoByteOp8(setccOpcode(ConditionE), (GroupOpcodeID)0, dst); + } + + void setz_r(RegisterID dst) + { + sete_r(dst); + } + + void setne_r(RegisterID dst) + { + m_formatter.twoByteOp8(setccOpcode(ConditionNE), (GroupOpcodeID)0, dst); + } + + void setnz_r(RegisterID dst) + { + setne_r(dst); + } + + // Various move ops: + + void cdq() + { + m_formatter.oneByteOp(OP_CDQ); + } + + void fstpl(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_ESCAPE_DD, ESCAPE_DD_FSTP_doubleReal, base, offset); + } + + void xchgl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_XCHG_EvGv, src, dst); + } + +#if CPU(X86_64) + void xchgq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_XCHG_EvGv, src, dst); + } +#endif + + void movl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_MOV_EvGv, src, dst); + } + + void movl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_MOV_EvGv, src, base, offset); + } + + void movl_rm_disp32(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset); + } + + void movl_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset); + } + + void movl_mEAX(const void* addr) + { + m_formatter.oneByteOp(OP_MOV_EAXOv); +#if CPU(X86_64) + m_formatter.immediate64(reinterpret_cast(addr)); +#else + m_formatter.immediate32(reinterpret_cast(addr)); +#endif + } + + void movl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, offset); + } + + void movl_mr_disp32(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp_disp32(OP_MOV_GvEv, dst, base, offset); + } + + void movl_mr_disp8(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp_disp8(OP_MOV_GvEv, dst, base, offset); + } + + void movl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, index, scale, offset); + } + + void movl_i32r(int imm, RegisterID dst) + { + m_formatter.oneByteOp(OP_MOV_EAXIv, dst); + m_formatter.immediate32(imm); + } + + void movl_i32m(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, offset); + m_formatter.immediate32(imm); + } + + void movl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, index, scale, offset); + m_formatter.immediate32(imm); + } + +#if !CPU(X86_64) + void movb_i8m(int imm, const void* addr) + { + ASSERT(-128 <= imm && imm < 128); + m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, addr); + m_formatter.immediate8(imm); + } +#endif + + void movb_i8m(int imm, int offset, RegisterID base) + { + ASSERT(-128 <= imm && imm < 128); + m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, offset); + m_formatter.immediate8(imm); + } + + void movb_i8m(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + ASSERT(-128 <= imm && imm < 128); + m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, index, scale, offset); + m_formatter.immediate8(imm); + } + + void movb_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp8(OP_MOV_EbGb, src, base, index, scale, offset); + } + + void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp8(OP_MOV_EvGv, src, base, index, scale, offset); + } + + void movl_EAXm(const void* addr) + { + m_formatter.oneByteOp(OP_MOV_OvEAX); +#if CPU(X86_64) + m_formatter.immediate64(reinterpret_cast(addr)); +#else + m_formatter.immediate32(reinterpret_cast(addr)); +#endif + } + +#if CPU(X86_64) + void movq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOV_EvGv, src, dst); + } + + void movq_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_MOV_EvGv, src, base, offset); + } + + void movq_rm_disp32(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64_disp32(OP_MOV_EvGv, src, base, offset); + } + + void movq_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp64(OP_MOV_EvGv, src, base, index, scale, offset); + } + + void movq_mEAX(const void* addr) + { + m_formatter.oneByteOp64(OP_MOV_EAXOv); + m_formatter.immediate64(reinterpret_cast(addr)); + } + + void movq_EAXm(const void* addr) + { + m_formatter.oneByteOp64(OP_MOV_OvEAX); + m_formatter.immediate64(reinterpret_cast(addr)); + } + + void movq_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOV_GvEv, dst, base, offset); + } + + void movq_mr_disp32(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64_disp32(OP_MOV_GvEv, dst, base, offset); + } + + void movq_mr_disp8(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64_disp8(OP_MOV_GvEv, dst, base, offset); + } + + void movq_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOV_GvEv, dst, base, index, scale, offset); + } + + void movq_i32m(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_GROUP11_EvIz, GROUP11_MOV, base, offset); + m_formatter.immediate32(imm); + } + + void movq_i64r(int64_t imm, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOV_EAXIv, dst); + m_formatter.immediate64(imm); + } + + void movsxd_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOVSXD_GvEv, dst, src); + } + + +#else + void movl_rm(RegisterID src, const void* addr) + { + if (src == X86Registers::eax) + movl_EAXm(addr); + else + m_formatter.oneByteOp(OP_MOV_EvGv, src, addr); + } + + void movl_mr(const void* addr, RegisterID dst) + { + if (dst == X86Registers::eax) + movl_mEAX(addr); + else + m_formatter.oneByteOp(OP_MOV_GvEv, dst, addr); + } + + void movl_i32m(int imm, const void* addr) + { + m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, addr); + m_formatter.immediate32(imm); + } +#endif + + void movzwl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, offset); + } + + void movzwl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, index, scale, offset); + } + + void movswl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, offset); + } + + void movswl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, index, scale, offset); + } + + void movzbl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, offset); + } + + void movzbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, index, scale, offset); + } + + void movsbl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, offset); + } + + void movsbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, index, scale, offset); + } + + void movzbl_rr(RegisterID src, RegisterID dst) + { + // In 64-bit, this may cause an unnecessary REX to be planted (if the dst register + // is in the range ESP-EDI, and the src would not have required a REX). Unneeded + // REX prefixes are defined to be silently ignored by the processor. + m_formatter.twoByteOp8(OP2_MOVZX_GvEb, dst, src); + } + + void leal_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_LEA, dst, base, offset); + } +#if CPU(X86_64) + void leaq_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64(OP_LEA, dst, base, offset); + } +#endif + + // Flow control: + + AssemblerLabel call() + { + m_formatter.oneByteOp(OP_CALL_rel32); + return m_formatter.immediateRel32(); + } + + AssemblerLabel call(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, dst); + return m_formatter.label(); + } + + void call_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, base, offset); + } + + AssemblerLabel jmp() + { + m_formatter.oneByteOp(OP_JMP_rel32); + return m_formatter.immediateRel32(); + } + + // Return a AssemblerLabel so we have a label to the jump, so we can use this + // To make a tail recursive call on x86-64. The MacroAssembler + // really shouldn't wrap this as a Jump, since it can't be linked. :-/ + AssemblerLabel jmp_r(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, dst); + return m_formatter.label(); + } + + void jmp_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, base, offset); + } + +#if !CPU(X86_64) + void jmp_m(const void* address) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, address); + } +#endif + + AssemblerLabel jne() + { + m_formatter.twoByteOp(jccRel32(ConditionNE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jnz() + { + return jne(); + } + + AssemblerLabel je() + { + m_formatter.twoByteOp(jccRel32(ConditionE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jz() + { + return je(); + } + + AssemblerLabel jl() + { + m_formatter.twoByteOp(jccRel32(ConditionL)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jb() + { + m_formatter.twoByteOp(jccRel32(ConditionB)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jle() + { + m_formatter.twoByteOp(jccRel32(ConditionLE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jbe() + { + m_formatter.twoByteOp(jccRel32(ConditionBE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jge() + { + m_formatter.twoByteOp(jccRel32(ConditionGE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jg() + { + m_formatter.twoByteOp(jccRel32(ConditionG)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel ja() + { + m_formatter.twoByteOp(jccRel32(ConditionA)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jae() + { + m_formatter.twoByteOp(jccRel32(ConditionAE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jo() + { + m_formatter.twoByteOp(jccRel32(ConditionO)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jnp() + { + m_formatter.twoByteOp(jccRel32(ConditionNP)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jp() + { + m_formatter.twoByteOp(jccRel32(ConditionP)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel js() + { + m_formatter.twoByteOp(jccRel32(ConditionS)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jCC(Condition cond) + { + m_formatter.twoByteOp(jccRel32(cond)); + return m_formatter.immediateRel32(); + } + + // SSE operations: + + void addsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void addsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, base, offset); + } + +#if !CPU(X86_64) + void addsd_mr(const void* address, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, address); + } +#endif + + void cvtsi2sd_rr(RegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src); + } + + void cvtsi2sd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, offset); + } + +#if !CPU(X86_64) + void cvtsi2sd_mr(const void* address, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, address); + } +#endif + + void cvttsd2si_rr(XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src); + } + + void cvtsd2ss_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTSD2SS_VsdWsd, dst, (RegisterID)src); + } + + void cvtss2sd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F3); + m_formatter.twoByteOp(OP2_CVTSS2SD_VsdWsd, dst, (RegisterID)src); + } + +#if CPU(X86_64) + void cvttsd2siq_rr(XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp64(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src); + } +#endif + + void movd_rr(XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_MOVD_EdVd, (RegisterID)src, dst); + } + + void movd_rr(RegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_MOVD_VdEd, (RegisterID)dst, src); + } + +#if CPU(X86_64) + void movq_rr(XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp64(OP2_MOVD_EdVd, (RegisterID)src, dst); + } + + void movq_rr(RegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp64(OP2_MOVD_VdEd, (RegisterID)dst, src); + } +#endif + + void movsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void movsd_rm(XMMRegisterID src, int offset, RegisterID base) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset); + } + + void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset); + } + + void movss_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.prefix(PRE_SSE_F3); + m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset); + } + + void movsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset); + } + + void movsd_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, dst, base, index, scale, offset); + } + + void movss_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F3); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, dst, base, index, scale, offset); + } + +#if !CPU(X86_64) + void movsd_mr(const void* address, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address); + } + void movsd_rm(XMMRegisterID src, const void* address) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address); + } +#endif + + void mulsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void mulsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, base, offset); + } + + void pextrw_irr(int whichWord, XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_PEXTRW_GdUdIb, (RegisterID)dst, (RegisterID)src); + m_formatter.immediate8(whichWord); + } + + void psllq_i8r(int imm, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp8(OP2_PSLLQ_UdqIb, GROUP14_OP_PSLLQ, (RegisterID)dst); + m_formatter.immediate8(imm); + } + + void psrlq_i8r(int imm, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp8(OP2_PSRLQ_UdqIb, GROUP14_OP_PSRLQ, (RegisterID)dst); + m_formatter.immediate8(imm); + } + + void por_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_POR_VdqWdq, (RegisterID)dst, (RegisterID)src); + } + + void subsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void subsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, base, offset); + } + + void ucomisd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void ucomisd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, base, offset); + } + + void divsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void divsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, base, offset); + } + + void xorpd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src); + } + + void andnpd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_ANDNPD_VpdWpd, (RegisterID)dst, (RegisterID)src); + } + + void sqrtsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_SQRTSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + // Misc instructions: + + void int3() + { + m_formatter.oneByteOp(OP_INT3); + } + + void ret() + { + m_formatter.oneByteOp(OP_RET); + } + + void predictNotTaken() + { + m_formatter.prefix(PRE_PREDICT_BRANCH_NOT_TAKEN); + } + + // Assembler admin methods: + + size_t codeSize() const + { + return m_formatter.codeSize(); + } + + AssemblerLabel labelForWatchpoint() + { + AssemblerLabel result = m_formatter.label(); + if (static_cast(result.m_offset) != m_indexOfLastWatchpoint) + result = label(); + m_indexOfLastWatchpoint = result.m_offset; + m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); + return result; + } + + AssemblerLabel labelIgnoringWatchpoints() + { + return m_formatter.label(); + } + + AssemblerLabel label() + { + AssemblerLabel result = m_formatter.label(); + while (UNLIKELY(static_cast(result.m_offset) < m_indexOfTailOfLastWatchpoint)) { + nop(); + result = m_formatter.label(); + } + return result; + } + + AssemblerLabel align(int alignment) + { + while (!m_formatter.isAligned(alignment)) + m_formatter.oneByteOp(OP_HLT); + + return label(); + } + + // Linking & patching: + // + // 'link' and 'patch' methods are for use on unprotected code - such as the code + // within the AssemblerBuffer, and code being patched by the patch buffer. Once + // code has been finalized it is (platform support permitting) within a non- + // writable region of memory; to modify the code in an execute-only execuable + // pool the 'repatch' and 'relink' methods should be used. + + void linkJump(AssemblerLabel from, AssemblerLabel to) + { + ASSERT(from.isSet()); + ASSERT(to.isSet()); + + char* code = reinterpret_cast(m_formatter.data()); + ASSERT(!reinterpret_cast(code + from.m_offset)[-1]); + setRel32(code + from.m_offset, code + to.m_offset); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + + setRel32(reinterpret_cast(code) + from.m_offset, to); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + + setRel32(reinterpret_cast(code) + from.m_offset, to); + } + + static void linkPointer(void* code, AssemblerLabel where, void* value) + { + ASSERT(where.isSet()); + + setPointer(reinterpret_cast(code) + where.m_offset, value); + } + + static void relinkJump(void* from, void* to) + { + setRel32(from, to); + } + + static void relinkCall(void* from, void* to) + { + setRel32(from, to); + } + + static void repatchCompact(void* where, int32_t value) + { + ASSERT(value >= std::numeric_limits::min()); + ASSERT(value <= std::numeric_limits::max()); + setInt8(where, value); + } + + static void repatchInt32(void* where, int32_t value) + { + setInt32(where, value); + } + + static void repatchPointer(void* where, void* value) + { + setPointer(where, value); + } + + static void* readPointer(void* where) + { + return reinterpret_cast(where)[-1]; + } + + static void replaceWithJump(void* instructionStart, void* to) + { + uint8_t* ptr = reinterpret_cast(instructionStart); + uint8_t* dstPtr = reinterpret_cast(to); + intptr_t distance = (intptr_t)(dstPtr - (ptr + 5)); + ptr[0] = static_cast(OP_JMP_rel32); + *reinterpret_cast(ptr + 1) = static_cast(distance); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return 5; + } + +#if CPU(X86_64) + static void revertJumpTo_movq_i64r(void* instructionStart, int64_t imm, RegisterID dst) + { + const int rexBytes = 1; + const int opcodeBytes = 1; + ASSERT(rexBytes + opcodeBytes <= maxJumpReplacementSize()); + uint8_t* ptr = reinterpret_cast(instructionStart); + ptr[0] = PRE_REX | (1 << 3) | (dst >> 3); + ptr[1] = OP_MOV_EAXIv | (dst & 7); + + union { + uint64_t asWord; + uint8_t asBytes[8]; + } u; + u.asWord = imm; + for (unsigned i = rexBytes + opcodeBytes; i < static_cast(maxJumpReplacementSize()); ++i) + ptr[i] = u.asBytes[i - rexBytes - opcodeBytes]; + } +#endif + + static void revertJumpTo_cmpl_ir_force32(void* instructionStart, int32_t imm, RegisterID dst) + { + const int opcodeBytes = 1; + const int modRMBytes = 1; + ASSERT(opcodeBytes + modRMBytes <= maxJumpReplacementSize()); + uint8_t* ptr = reinterpret_cast(instructionStart); + ptr[0] = OP_GROUP1_EvIz; + ptr[1] = (X86InstructionFormatter::ModRmRegister << 6) | (GROUP1_OP_CMP << 3) | dst; + union { + uint32_t asWord; + uint8_t asBytes[4]; + } u; + u.asWord = imm; + for (unsigned i = opcodeBytes + modRMBytes; i < static_cast(maxJumpReplacementSize()); ++i) + ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes]; + } + + static void revertJumpTo_cmpl_im_force32(void* instructionStart, int32_t imm, int offset, RegisterID dst) + { + ASSERT_UNUSED(offset, !offset); + const int opcodeBytes = 1; + const int modRMBytes = 1; + ASSERT(opcodeBytes + modRMBytes <= maxJumpReplacementSize()); + uint8_t* ptr = reinterpret_cast(instructionStart); + ptr[0] = OP_GROUP1_EvIz; + ptr[1] = (X86InstructionFormatter::ModRmMemoryNoDisp << 6) | (GROUP1_OP_CMP << 3) | dst; + union { + uint32_t asWord; + uint8_t asBytes[4]; + } u; + u.asWord = imm; + for (unsigned i = opcodeBytes + modRMBytes; i < static_cast(maxJumpReplacementSize()); ++i) + ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes]; + } + + static void replaceWithLoad(void* instructionStart) + { + uint8_t* ptr = reinterpret_cast(instructionStart); +#if CPU(X86_64) + if ((*ptr & ~15) == PRE_REX) + ptr++; +#endif + switch (*ptr) { + case OP_MOV_GvEv: + break; + case OP_LEA: + *ptr = OP_MOV_GvEv; + break; + default: + ASSERT_NOT_REACHED(); + } + } + + static void replaceWithAddressComputation(void* instructionStart) + { + uint8_t* ptr = reinterpret_cast(instructionStart); +#if CPU(X86_64) + if ((*ptr & ~15) == PRE_REX) + ptr++; +#endif + switch (*ptr) { + case OP_MOV_GvEv: + *ptr = OP_LEA; + break; + case OP_LEA: + break; + default: + ASSERT_NOT_REACHED(); + } + } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + ASSERT(call.isSet()); + return call.m_offset; + } + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + ASSERT(label.isSet()); + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + return m_formatter.executableCopy(globalData, ownerUID, effort); + } + + unsigned debugOffset() { return m_formatter.debugOffset(); } + + void nop() + { + m_formatter.oneByteOp(OP_NOP); + } + + // This is a no-op on x86 + ALWAYS_INLINE static void cacheFlush(void*, size_t) { } + +private: + + static void setPointer(void* where, void* value) + { + reinterpret_cast(where)[-1] = value; + } + + static void setInt32(void* where, int32_t value) + { + reinterpret_cast(where)[-1] = value; + } + + static void setInt8(void* where, int8_t value) + { + reinterpret_cast(where)[-1] = value; + } + + static void setRel32(void* from, void* to) + { + intptr_t offset = reinterpret_cast(to) - reinterpret_cast(from); + ASSERT(offset == static_cast(offset)); + + setInt32(from, offset); + } + + class X86InstructionFormatter { + + static const int maxInstructionSize = 16; + + public: + + enum ModRmMode { + ModRmMemoryNoDisp, + ModRmMemoryDisp8, + ModRmMemoryDisp32, + ModRmRegister, + }; + + // Legacy prefix bytes: + // + // These are emmitted prior to the instruction. + + void prefix(OneByteOpcodeID pre) + { + m_buffer.putByte(pre); + } + + // Word-sized operands / no operand instruction formatters. + // + // In addition to the opcode, the following operand permutations are supported: + // * None - instruction takes no operands. + // * One register - the low three bits of the RegisterID are added into the opcode. + // * Two registers - encode a register form ModRm (for all ModRm formats, the reg field is passed first, and a GroupOpcodeID may be passed in its place). + // * Three argument ModRM - a register, and a register and an offset describing a memory operand. + // * Five argument ModRM - a register, and a base register, an index, scale, and offset describing a memory operand. + // + // For 32-bit x86 targets, the address operand may also be provided as a void*. + // On 64-bit targets REX prefixes will be planted as necessary, where high numbered registers are used. + // + // The twoByteOp methods plant two-byte Intel instructions sequences (first opcode byte 0x0F). + + void oneByteOp(OneByteOpcodeID opcode) + { + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putByteUnchecked(opcode); + } + + void oneByteOp(OneByteOpcodeID opcode, RegisterID reg) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(0, 0, reg); + m_buffer.putByteUnchecked(opcode + (reg & 7)); + } + + void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, rm); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, offset); + } + + void oneByteOp_disp32(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp32(reg, base, offset); + } + + void oneByteOp_disp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp8(reg, base, offset); + } + + void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, index, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, index, scale, offset); + } + +#if !CPU(X86_64) + void oneByteOp(OneByteOpcodeID opcode, int reg, const void* address) + { + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, address); + } +#endif + + void twoByteOp(TwoByteOpcodeID opcode) + { + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + } + + void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, rm); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, base); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, offset); + } + + void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, index, base); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, index, scale, offset); + } + +#if !CPU(X86_64) + void twoByteOp(TwoByteOpcodeID opcode, int reg, const void* address) + { + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, address); + } +#endif + +#if CPU(X86_64) + // Quad-word-sized operands: + // + // Used to format 64-bit operantions, planting a REX.w prefix. + // When planting d64 or f64 instructions, not requiring a REX.w prefix, + // the normal (non-'64'-postfixed) formatters should be used. + + void oneByteOp64(OneByteOpcodeID opcode) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(0, 0, 0); + m_buffer.putByteUnchecked(opcode); + } + + void oneByteOp64(OneByteOpcodeID opcode, RegisterID reg) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(0, 0, reg); + m_buffer.putByteUnchecked(opcode + (reg & 7)); + } + + void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, rm); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, offset); + } + + void oneByteOp64_disp32(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp32(reg, base, offset); + } + + void oneByteOp64_disp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp8(reg, base, offset); + } + + void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, index, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, index, scale, offset); + } + + void twoByteOp64(TwoByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, rm); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } +#endif + + // Byte-operands: + // + // These methods format byte operations. Byte operations differ from the normal + // formatters in the circumstances under which they will decide to emit REX prefixes. + // These should be used where any register operand signifies a byte register. + // + // The disctinction is due to the handling of register numbers in the range 4..7 on + // x86-64. These register numbers may either represent the second byte of the first + // four registers (ah..bh) or the first byte of the second four registers (spl..dil). + // + // Since ah..bh cannot be used in all permutations of operands (specifically cannot + // be accessed where a REX prefix is present), these are likely best treated as + // deprecated. In order to ensure the correct registers spl..dil are selected a + // REX prefix will be emitted for any byte register operand in the range 4..15. + // + // These formatters may be used in instructions where a mix of operand sizes, in which + // case an unnecessary REX will be emitted, for example: + // movzbl %al, %edi + // In this case a REX will be planted since edi is 7 (and were this a byte operand + // a REX would be required to specify dil instead of bh). Unneeded REX prefixes will + // be silently ignored by the processor. + // + // Address operands should still be checked using regRequiresRex(), while byteRegRequiresRex() + // is provided to check byte register operands. + + void oneByteOp8(OneByteOpcodeID opcode, GroupOpcodeID groupOp, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(rm), 0, 0, rm); + m_buffer.putByteUnchecked(opcode); + registerModRM(groupOp, rm); + } + + void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(reg) || byteRegRequiresRex(rm), reg, 0, rm); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(reg) || regRequiresRex(index) || regRequiresRex(base), reg, index, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, index, scale, offset); + } + + void twoByteOp8(TwoByteOpcodeID opcode, RegisterID reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void twoByteOp8(TwoByteOpcodeID opcode, GroupOpcodeID groupOp, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(rm), 0, 0, rm); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + registerModRM(groupOp, rm); + } + + // Immediates: + // + // An immedaite should be appended where appropriate after an op has been emitted. + // The writes are unchecked since the opcode formatters above will have ensured space. + + void immediate8(int imm) + { + m_buffer.putByteUnchecked(imm); + } + + void immediate16(int imm) + { + m_buffer.putShortUnchecked(imm); + } + + void immediate32(int imm) + { + m_buffer.putIntUnchecked(imm); + } + + void immediate64(int64_t imm) + { + m_buffer.putInt64Unchecked(imm); + } + + AssemblerLabel immediateRel32() + { + m_buffer.putIntUnchecked(0); + return label(); + } + + // Administrative methods: + + size_t codeSize() const { return m_buffer.codeSize(); } + AssemblerLabel label() const { return m_buffer.label(); } + bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); } + void* data() const { return m_buffer.data(); } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + return m_buffer.executableCopy(globalData, ownerUID, effort); + } + + unsigned debugOffset() { return m_buffer.debugOffset(); } + + private: + + // Internals; ModRm and REX formatters. + + static const RegisterID noBase = X86Registers::ebp; + static const RegisterID hasSib = X86Registers::esp; + static const RegisterID noIndex = X86Registers::esp; +#if CPU(X86_64) + static const RegisterID noBase2 = X86Registers::r13; + static const RegisterID hasSib2 = X86Registers::r12; + + // Registers r8 & above require a REX prefixe. + inline bool regRequiresRex(int reg) + { + return (reg >= X86Registers::r8); + } + + // Byte operand register spl & above require a REX prefix (to prevent the 'H' registers be accessed). + inline bool byteRegRequiresRex(int reg) + { + return (reg >= X86Registers::esp); + } + + // Format a REX prefix byte. + inline void emitRex(bool w, int r, int x, int b) + { + ASSERT(r >= 0); + ASSERT(x >= 0); + ASSERT(b >= 0); + m_buffer.putByteUnchecked(PRE_REX | ((int)w << 3) | ((r>>3)<<2) | ((x>>3)<<1) | (b>>3)); + } + + // Used to plant a REX byte with REX.w set (for 64-bit operations). + inline void emitRexW(int r, int x, int b) + { + emitRex(true, r, x, b); + } + + // Used for operations with byte operands - use byteRegRequiresRex() to check register operands, + // regRequiresRex() to check other registers (i.e. address base & index). + inline void emitRexIf(bool condition, int r, int x, int b) + { + if (condition) emitRex(false, r, x, b); + } + + // Used for word sized operations, will plant a REX prefix if necessary (if any register is r8 or above). + inline void emitRexIfNeeded(int r, int x, int b) + { + emitRexIf(regRequiresRex(r) || regRequiresRex(x) || regRequiresRex(b), r, x, b); + } +#else + // No REX prefix bytes on 32-bit x86. + inline bool regRequiresRex(int) { return false; } + inline bool byteRegRequiresRex(int) { return false; } + inline void emitRexIf(bool, int, int, int) {} + inline void emitRexIfNeeded(int, int, int) {} +#endif + + void putModRm(ModRmMode mode, int reg, RegisterID rm) + { + m_buffer.putByteUnchecked((mode << 6) | ((reg & 7) << 3) | (rm & 7)); + } + + void putModRmSib(ModRmMode mode, int reg, RegisterID base, RegisterID index, int scale) + { + ASSERT(mode != ModRmRegister); + + putModRm(mode, reg, hasSib); + m_buffer.putByteUnchecked((scale << 6) | ((index & 7) << 3) | (base & 7)); + } + + void registerModRM(int reg, RegisterID rm) + { + putModRm(ModRmRegister, reg, rm); + } + + void memoryModRM(int reg, RegisterID base, int offset) + { + // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. +#if CPU(X86_64) + if ((base == hasSib) || (base == hasSib2)) { +#else + if (base == hasSib) { +#endif + if (!offset) // No need to check if the base is noBase, since we know it is hasSib! + putModRmSib(ModRmMemoryNoDisp, reg, base, noIndex, 0); + else if (CAN_SIGN_EXTEND_8_32(offset)) { + putModRmSib(ModRmMemoryDisp8, reg, base, noIndex, 0); + m_buffer.putByteUnchecked(offset); + } else { + putModRmSib(ModRmMemoryDisp32, reg, base, noIndex, 0); + m_buffer.putIntUnchecked(offset); + } + } else { +#if CPU(X86_64) + if (!offset && (base != noBase) && (base != noBase2)) +#else + if (!offset && (base != noBase)) +#endif + putModRm(ModRmMemoryNoDisp, reg, base); + else if (CAN_SIGN_EXTEND_8_32(offset)) { + putModRm(ModRmMemoryDisp8, reg, base); + m_buffer.putByteUnchecked(offset); + } else { + putModRm(ModRmMemoryDisp32, reg, base); + m_buffer.putIntUnchecked(offset); + } + } + } + + void memoryModRM_disp8(int reg, RegisterID base, int offset) + { + // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. + ASSERT(CAN_SIGN_EXTEND_8_32(offset)); +#if CPU(X86_64) + if ((base == hasSib) || (base == hasSib2)) { +#else + if (base == hasSib) { +#endif + putModRmSib(ModRmMemoryDisp8, reg, base, noIndex, 0); + m_buffer.putByteUnchecked(offset); + } else { + putModRm(ModRmMemoryDisp8, reg, base); + m_buffer.putByteUnchecked(offset); + } + } + + void memoryModRM_disp32(int reg, RegisterID base, int offset) + { + // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. +#if CPU(X86_64) + if ((base == hasSib) || (base == hasSib2)) { +#else + if (base == hasSib) { +#endif + putModRmSib(ModRmMemoryDisp32, reg, base, noIndex, 0); + m_buffer.putIntUnchecked(offset); + } else { + putModRm(ModRmMemoryDisp32, reg, base); + m_buffer.putIntUnchecked(offset); + } + } + + void memoryModRM(int reg, RegisterID base, RegisterID index, int scale, int offset) + { + ASSERT(index != noIndex); + +#if CPU(X86_64) + if (!offset && (base != noBase) && (base != noBase2)) +#else + if (!offset && (base != noBase)) +#endif + putModRmSib(ModRmMemoryNoDisp, reg, base, index, scale); + else if (CAN_SIGN_EXTEND_8_32(offset)) { + putModRmSib(ModRmMemoryDisp8, reg, base, index, scale); + m_buffer.putByteUnchecked(offset); + } else { + putModRmSib(ModRmMemoryDisp32, reg, base, index, scale); + m_buffer.putIntUnchecked(offset); + } + } + +#if !CPU(X86_64) + void memoryModRM(int reg, const void* address) + { + // noBase + ModRmMemoryNoDisp means noBase + ModRmMemoryDisp32! + putModRm(ModRmMemoryNoDisp, reg, noBase); + m_buffer.putIntUnchecked(reinterpret_cast(address)); + } +#endif + + AssemblerBuffer m_buffer; + } m_formatter; + int m_indexOfLastWatchpoint; + int m_indexOfTailOfLastWatchpoint; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(X86) + +#endif // X86Assembler_h diff --git a/3rdparty/masm/config.h b/3rdparty/masm/config.h new file mode 100644 index 0000000000..5f59f311e3 --- /dev/null +++ b/3rdparty/masm/config.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MASM_CONFIG_H +#define MASM_CONFIG_H + +#include +#ifdef __cplusplus +#include +#include +#include +#include +#else +#include +#endif +#include + +#endif // MASM_CONFIG_H diff --git a/3rdparty/masm/create_regex_tables b/3rdparty/masm/create_regex_tables new file mode 100644 index 0000000000..bd799ba044 --- /dev/null +++ b/3rdparty/masm/create_regex_tables @@ -0,0 +1,121 @@ +# Copyright (C) 2010 Apple Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import sys + +types = { + "wordchar": { "UseTable" : True, "data": ['_', ('0','9'), ('A', 'Z'), ('a','z')]}, + "nonwordchar": { "UseTable" : True, "Inverse": "wordchar", "data": ['`', (0, ord('0') - 1), (ord('9') + 1, ord('A') - 1), (ord('Z') + 1, ord('_') - 1), (ord('z') + 1, 0xffff)]}, + "newline": { "UseTable" : False, "data": ['\n', '\r', 0x2028, 0x2029]}, + "spaces": { "UseTable" : True, "data": [' ', ('\t', '\r'), 0xa0, 0x1680, 0x180e, 0x2028, 0x2029, 0x202f, 0x205f, 0x3000, (0x2000, 0x200a), 0xfeff]}, + "nonspaces": { "UseTable" : True, "Inverse": "spaces", "data": [(0, ord('\t') - 1), (ord('\r') + 1, ord(' ') - 1), (ord(' ') + 1, 0x009f), (0x00a1, 0x167f), (0x1681, 0x180d), (0x180f, 0x1fff), (0x200b, 0x2027), (0x202a, 0x202e), (0x2030, 0x205e), (0x2060, 0x2fff), (0x3001, 0xfefe), (0xff00, 0xffff)]}, + "digits": { "UseTable" : False, "data": [('0', '9')]}, + "nondigits": { "UseTable" : False, "Inverse": "digits", "data": [(0, ord('0') - 1), (ord('9') + 1, 0xffff)] } +} +entriesPerLine = 50 +arrays = ""; +functions = ""; +emitTables = (len(sys.argv) < 2 or sys.argv[1] != "--no-tables") + +for name, classes in types.items(): + ranges = []; + size = 0; + for _class in classes["data"]: + if type(_class) == str: + ranges.append((ord(_class), ord(_class))) + elif type(_class) == int: + ranges.append((_class, _class)) + else: + (min, max) = _class; + if type(min) == str: + min = ord(min) + if type(max) == str: + max = ord(max) + if max > 0x7f and min <= 0x7f: + ranges.append((min, 0x7f)) + min = 0x80 + ranges.append((min,max)) + ranges.sort(); + + if emitTables and classes["UseTable"] and (not "Inverse" in classes): + array = ("static const char _%sData[65536] = {\n" % name); + i = 0 + for (min,max) in ranges: + while i < min: + i = i + 1 + array += ('0,') + if (i % entriesPerLine == 0) and (i != 0): + array += ('\n') + while i <= max: + i = i + 1 + if (i == 65536): + array += ("1") + else: + array += ('1,') + if (i % entriesPerLine == 0) and (i != 0): + array += ('\n') + while i < 0xffff: + array += ("0,") + i = i + 1; + if (i % entriesPerLine == 0) and (i != 0): + array += ('\n') + if i == 0xffff: + array += ("0") + array += ("\n};\n\n"); + arrays += array + + # Generate createFunction: + function = ""; + function += ("CharacterClass* %sCreate()\n" % name) + function += ("{\n") + if emitTables and classes["UseTable"]: + if "Inverse" in classes: + function += (" CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_%sData, true));\n" % (classes["Inverse"])) + else: + function += (" CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_%sData, false));\n" % (name)) + else: + function += (" CharacterClass* characterClass = new CharacterClass(0);\n") + for (min, max) in ranges: + if (min == max): + if (min > 127): + function += (" characterClass->m_matchesUnicode.append(0x%04x);\n" % min) + else: + function += (" characterClass->m_matches.append(0x%02x);\n" % min) + continue + if (min > 127) or (max > 127): + function += (" characterClass->m_rangesUnicode.append(CharacterRange(0x%04x, 0x%04x));\n" % (min, max)) + else: + function += (" characterClass->m_ranges.append(CharacterRange(0x%02x, 0x%02x));\n" % (min, max)) + function += (" return characterClass;\n") + function += ("}\n\n") + functions += function + +if (len(sys.argv) > 1): + f = open(sys.argv[-1], "w") + f.write(arrays) + f.write(functions) + f.close() +else: + print(arrays) + print(functions) + diff --git a/3rdparty/masm/disassembler/Disassembler.cpp b/3rdparty/masm/disassembler/Disassembler.cpp new file mode 100644 index 0000000000..3fed2cdab8 --- /dev/null +++ b/3rdparty/masm/disassembler/Disassembler.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Disassembler.h" + +#include "MacroAssemblerCodeRef.h" +#include + +namespace JSC { + +void disassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const char* prefix, PrintStream& out) +{ + if (tryToDisassemble(codePtr, size, prefix, out)) + return; + + out.printf("%sdisassembly not available for range %p...%p\n", prefix, codePtr.executableAddress(), static_cast(codePtr.executableAddress()) + size); +} + +} // namespace JSC + diff --git a/3rdparty/masm/disassembler/Disassembler.h b/3rdparty/masm/disassembler/Disassembler.h new file mode 100644 index 0000000000..a087a657b3 --- /dev/null +++ b/3rdparty/masm/disassembler/Disassembler.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Disassembler_h +#define Disassembler_h + +#include +#include + +namespace JSC { + +class MacroAssemblerCodePtr; + +#if ENABLE(DISASSEMBLER) +bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, PrintStream&); +#else +inline bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char*, PrintStream&) +{ + return false; +} +#endif + +// Prints either the disassembly, or a line of text indicating that disassembly failed and +// the range of machine code addresses. +void disassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, PrintStream& out); + +} // namespace JSC + +#endif // Disassembler_h + diff --git a/3rdparty/masm/disassembler/UDis86Disassembler.cpp b/3rdparty/masm/disassembler/UDis86Disassembler.cpp new file mode 100644 index 0000000000..63c235b920 --- /dev/null +++ b/3rdparty/masm/disassembler/UDis86Disassembler.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Disassembler.h" + +#if USE(UDIS86) + +#include "MacroAssemblerCodeRef.h" +#include "udis86.h" + +namespace JSC { + +bool tryToDisassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const char* prefix, PrintStream& out) +{ + ud_t disassembler; + ud_init(&disassembler); + ud_set_input_buffer(&disassembler, static_cast(codePtr.executableAddress()), size); +#if CPU(X86_64) + ud_set_mode(&disassembler, 64); +#else + ud_set_mode(&disassembler, 32); +#endif + ud_set_pc(&disassembler, bitwise_cast(codePtr.executableAddress())); + ud_set_syntax(&disassembler, UD_SYN_ATT); + + uint64_t currentPC = disassembler.pc; + while (ud_disassemble(&disassembler)) { + char pcString[20]; + snprintf(pcString, sizeof(pcString), "0x%lx", static_cast(currentPC)); + out.printf("%s%16s: %s\n", prefix, pcString, ud_insn_asm(&disassembler)); + currentPC = disassembler.pc; + } + + return true; +} + +} // namespace JSC + +#endif // USE(UDIS86) + diff --git a/3rdparty/masm/disassembler/udis86/differences.txt b/3rdparty/masm/disassembler/udis86/differences.txt new file mode 100644 index 0000000000..dc225b6ffe --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/differences.txt @@ -0,0 +1,24 @@ +This documents the differences between the stock version of udis86 and the one found +here: + +- All files not named "udis86" were prefixed with "udis86". + +- assert() has been changed to ASSERT() + +- Mass rename of udis86_input.h inp_ prefixed functions and macros to ud_inp_ to + avoid namespace pollution. + +- Removal of KERNEL checks. + +- Added #include of udis86_extern.h in udis86_decode.c. + +- Removed s_ie__pause and s_ie__nop from udis86_decode.c, since they weren't used. + +- Made udis86_syn.h use WTF_ATTRIBUTE_PRINTF. This required making a bunch of little + fixes to make the compiler's format string warnings go away. + +- Made the code in udis86_syn.h use vsnprintf() instead of vsprintf(). + +- Fixed udis86_syn-att.c's jump destination printing to work correctly in 64-bit mode. + +- Add --outputDir option to itab.py. diff --git a/3rdparty/masm/disassembler/udis86/itab.py b/3rdparty/masm/disassembler/udis86/itab.py new file mode 100644 index 0000000000..07e20a6e10 --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/itab.py @@ -0,0 +1,360 @@ +# udis86 - scripts/itab.py +# +# Copyright (c) 2009 Vivek Thampi +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from optparse import OptionParser +import os +import sys + +sys.path.append( '../scripts' ); + +import ud_optable +import ud_opcode + +class UdItabGenerator( ud_opcode.UdOpcodeTables ): + + OperandDict = { + "Ap" : [ "OP_A" , "SZ_P" ], + "E" : [ "OP_E" , "SZ_NA" ], + "Eb" : [ "OP_E" , "SZ_B" ], + "Ew" : [ "OP_E" , "SZ_W" ], + "Ev" : [ "OP_E" , "SZ_V" ], + "Ed" : [ "OP_E" , "SZ_D" ], + "Eq" : [ "OP_E" , "SZ_Q" ], + "Ez" : [ "OP_E" , "SZ_Z" ], + "Ex" : [ "OP_E" , "SZ_MDQ" ], + "Ep" : [ "OP_E" , "SZ_P" ], + "G" : [ "OP_G" , "SZ_NA" ], + "Gb" : [ "OP_G" , "SZ_B" ], + "Gw" : [ "OP_G" , "SZ_W" ], + "Gv" : [ "OP_G" , "SZ_V" ], + "Gy" : [ "OP_G" , "SZ_MDQ" ], + "Gy" : [ "OP_G" , "SZ_MDQ" ], + "Gd" : [ "OP_G" , "SZ_D" ], + "Gq" : [ "OP_G" , "SZ_Q" ], + "Gx" : [ "OP_G" , "SZ_MDQ" ], + "Gz" : [ "OP_G" , "SZ_Z" ], + "M" : [ "OP_M" , "SZ_NA" ], + "Mb" : [ "OP_M" , "SZ_B" ], + "Mw" : [ "OP_M" , "SZ_W" ], + "Ms" : [ "OP_M" , "SZ_W" ], + "Md" : [ "OP_M" , "SZ_D" ], + "Mq" : [ "OP_M" , "SZ_Q" ], + "Mt" : [ "OP_M" , "SZ_T" ], + "Mo" : [ "OP_M" , "SZ_O" ], + "MwRv" : [ "OP_MR" , "SZ_WV" ], + "MdRy" : [ "OP_MR" , "SZ_DY" ], + "MbRv" : [ "OP_MR" , "SZ_BV" ], + "I1" : [ "OP_I1" , "SZ_NA" ], + "I3" : [ "OP_I3" , "SZ_NA" ], + "Ib" : [ "OP_I" , "SZ_B" ], + "Isb" : [ "OP_I" , "SZ_SB" ], + "Iw" : [ "OP_I" , "SZ_W" ], + "Iv" : [ "OP_I" , "SZ_V" ], + "Iz" : [ "OP_I" , "SZ_Z" ], + "Jv" : [ "OP_J" , "SZ_V" ], + "Jz" : [ "OP_J" , "SZ_Z" ], + "Jb" : [ "OP_J" , "SZ_B" ], + "R" : [ "OP_R" , "SZ_RDQ" ], + "C" : [ "OP_C" , "SZ_NA" ], + "D" : [ "OP_D" , "SZ_NA" ], + "S" : [ "OP_S" , "SZ_NA" ], + "Ob" : [ "OP_O" , "SZ_B" ], + "Ow" : [ "OP_O" , "SZ_W" ], + "Ov" : [ "OP_O" , "SZ_V" ], + "V" : [ "OP_V" , "SZ_O" ], + "W" : [ "OP_W" , "SZ_O" ], + "Wsd" : [ "OP_W" , "SZ_O" ], + "Wss" : [ "OP_W" , "SZ_O" ], + "P" : [ "OP_P" , "SZ_Q" ], + "Q" : [ "OP_Q" , "SZ_Q" ], + "VR" : [ "OP_VR" , "SZ_O" ], + "PR" : [ "OP_PR" , "SZ_Q" ], + "AL" : [ "OP_AL" , "SZ_NA" ], + "CL" : [ "OP_CL" , "SZ_NA" ], + "DL" : [ "OP_DL" , "SZ_NA" ], + "BL" : [ "OP_BL" , "SZ_NA" ], + "AH" : [ "OP_AH" , "SZ_NA" ], + "CH" : [ "OP_CH" , "SZ_NA" ], + "DH" : [ "OP_DH" , "SZ_NA" ], + "BH" : [ "OP_BH" , "SZ_NA" ], + "AX" : [ "OP_AX" , "SZ_NA" ], + "CX" : [ "OP_CX" , "SZ_NA" ], + "DX" : [ "OP_DX" , "SZ_NA" ], + "BX" : [ "OP_BX" , "SZ_NA" ], + "SI" : [ "OP_SI" , "SZ_NA" ], + "DI" : [ "OP_DI" , "SZ_NA" ], + "SP" : [ "OP_SP" , "SZ_NA" ], + "BP" : [ "OP_BP" , "SZ_NA" ], + "eAX" : [ "OP_eAX" , "SZ_NA" ], + "eCX" : [ "OP_eCX" , "SZ_NA" ], + "eDX" : [ "OP_eDX" , "SZ_NA" ], + "eBX" : [ "OP_eBX" , "SZ_NA" ], + "eSI" : [ "OP_eSI" , "SZ_NA" ], + "eDI" : [ "OP_eDI" , "SZ_NA" ], + "eSP" : [ "OP_eSP" , "SZ_NA" ], + "eBP" : [ "OP_eBP" , "SZ_NA" ], + "rAX" : [ "OP_rAX" , "SZ_NA" ], + "rCX" : [ "OP_rCX" , "SZ_NA" ], + "rBX" : [ "OP_rBX" , "SZ_NA" ], + "rDX" : [ "OP_rDX" , "SZ_NA" ], + "rSI" : [ "OP_rSI" , "SZ_NA" ], + "rDI" : [ "OP_rDI" , "SZ_NA" ], + "rSP" : [ "OP_rSP" , "SZ_NA" ], + "rBP" : [ "OP_rBP" , "SZ_NA" ], + "ES" : [ "OP_ES" , "SZ_NA" ], + "CS" : [ "OP_CS" , "SZ_NA" ], + "DS" : [ "OP_DS" , "SZ_NA" ], + "SS" : [ "OP_SS" , "SZ_NA" ], + "GS" : [ "OP_GS" , "SZ_NA" ], + "FS" : [ "OP_FS" , "SZ_NA" ], + "ST0" : [ "OP_ST0" , "SZ_NA" ], + "ST1" : [ "OP_ST1" , "SZ_NA" ], + "ST2" : [ "OP_ST2" , "SZ_NA" ], + "ST3" : [ "OP_ST3" , "SZ_NA" ], + "ST4" : [ "OP_ST4" , "SZ_NA" ], + "ST5" : [ "OP_ST5" , "SZ_NA" ], + "ST6" : [ "OP_ST6" , "SZ_NA" ], + "ST7" : [ "OP_ST7" , "SZ_NA" ], + "NONE" : [ "OP_NONE" , "SZ_NA" ], + "ALr8b" : [ "OP_ALr8b" , "SZ_NA" ], + "CLr9b" : [ "OP_CLr9b" , "SZ_NA" ], + "DLr10b" : [ "OP_DLr10b" , "SZ_NA" ], + "BLr11b" : [ "OP_BLr11b" , "SZ_NA" ], + "AHr12b" : [ "OP_AHr12b" , "SZ_NA" ], + "CHr13b" : [ "OP_CHr13b" , "SZ_NA" ], + "DHr14b" : [ "OP_DHr14b" , "SZ_NA" ], + "BHr15b" : [ "OP_BHr15b" , "SZ_NA" ], + "rAXr8" : [ "OP_rAXr8" , "SZ_NA" ], + "rCXr9" : [ "OP_rCXr9" , "SZ_NA" ], + "rDXr10" : [ "OP_rDXr10" , "SZ_NA" ], + "rBXr11" : [ "OP_rBXr11" , "SZ_NA" ], + "rSPr12" : [ "OP_rSPr12" , "SZ_NA" ], + "rBPr13" : [ "OP_rBPr13" , "SZ_NA" ], + "rSIr14" : [ "OP_rSIr14" , "SZ_NA" ], + "rDIr15" : [ "OP_rDIr15" , "SZ_NA" ], + "jWP" : [ "OP_J" , "SZ_WP" ], + "jDP" : [ "OP_J" , "SZ_DP" ], + + } + + # + # opcode prefix dictionary + # + PrefixDict = { + "aso" : "P_aso", + "oso" : "P_oso", + "rexw" : "P_rexw", + "rexb" : "P_rexb", + "rexx" : "P_rexx", + "rexr" : "P_rexr", + "seg" : "P_seg", + "inv64" : "P_inv64", + "def64" : "P_def64", + "depM" : "P_depM", + "cast1" : "P_c1", + "cast2" : "P_c2", + "cast3" : "P_c3", + "cast" : "P_cast", + "sext" : "P_sext" + } + + InvalidEntryIdx = 0 + InvalidEntry = { 'type' : 'invalid', + 'mnemonic' : 'invalid', + 'operands' : '', + 'prefixes' : '', + 'meta' : '' } + + Itab = [] # instruction table + ItabIdx = 1 # instruction table index + GtabIdx = 0 # group table index + GtabMeta = [] + + ItabLookup = {} + + MnemonicAliases = ( "invalid", "3dnow", "none", "db", "pause" ) + + def __init__( self, outputDir ): + # first itab entry (0) is Invalid + self.Itab.append( self.InvalidEntry ) + self.MnemonicsTable.extend( self.MnemonicAliases ) + self.outputDir = outputDir + + def toGroupId( self, id ): + return 0x8000 | id + + def genLookupTable( self, table, scope = '' ): + idxArray = [ ] + ( tabIdx, self.GtabIdx ) = ( self.GtabIdx, self.GtabIdx + 1 ) + self.GtabMeta.append( { 'type' : table[ 'type' ], 'meta' : table[ 'meta' ] } ) + + for _idx in range( self.sizeOfTable( table[ 'type' ] ) ): + idx = "%02x" % _idx + + e = self.InvalidEntry + i = self.InvalidEntryIdx + + if idx in table[ 'entries' ].keys(): + e = table[ 'entries' ][ idx ] + + # leaf node (insn) + if e[ 'type' ] == 'insn': + ( i, self.ItabIdx ) = ( self.ItabIdx, self.ItabIdx + 1 ) + self.Itab.append( e ) + elif e[ 'type' ] != 'invalid': + i = self.genLookupTable( e, 'static' ) + + idxArray.append( i ) + + name = "ud_itab__%s" % tabIdx + self.ItabLookup[ tabIdx ] = name + + self.ItabC.write( "\n" ); + if len( scope ): + self.ItabC.write( scope + ' ' ) + self.ItabC.write( "const uint16_t %s[] = {\n" % name ) + for i in range( len( idxArray ) ): + if i > 0 and i % 4 == 0: + self.ItabC.write( "\n" ) + if ( i%4 == 0 ): + self.ItabC.write( " /* %2x */" % i) + if idxArray[ i ] >= 0x8000: + self.ItabC.write( "%12s," % ("GROUP(%d)" % ( ~0x8000 & idxArray[ i ] ))) + else: + self.ItabC.write( "%12d," % ( idxArray[ i ] )) + self.ItabC.write( "\n" ) + self.ItabC.write( "};\n" ) + + return self.toGroupId( tabIdx ) + + def genLookupTableList( self ): + self.ItabC.write( "\n\n" ); + self.ItabC.write( "struct ud_lookup_table_list_entry ud_lookup_table_list[] = {\n" ) + for i in range( len( self.GtabMeta ) ): + f0 = self.ItabLookup[ i ] + "," + f1 = ( self.nameOfTable( self.GtabMeta[ i ][ 'type' ] ) ) + "," + f2 = "\"%s\"" % self.GtabMeta[ i ][ 'meta' ] + self.ItabC.write( " /* %03d */ { %s %s %s },\n" % ( i, f0, f1, f2 ) ) + self.ItabC.write( "};" ) + + def genInsnTable( self ): + self.ItabC.write( "struct ud_itab_entry ud_itab[] = {\n" ); + idx = 0 + for e in self.Itab: + opr_c = [ "O_NONE", "O_NONE", "O_NONE" ] + pfx_c = [] + opr = e[ 'operands' ] + for i in range(len(opr)): + if not (opr[i] in self.OperandDict.keys()): + print "error: invalid operand declaration: %s\n" % opr[i] + opr_c[i] = "O_" + opr[i] + opr = "%s %s %s" % (opr_c[0] + ",", opr_c[1] + ",", opr_c[2]) + + for p in e['prefixes']: + if not ( p in self.PrefixDict.keys() ): + print "error: invalid prefix specification: %s \n" % pfx + pfx_c.append( self.PrefixDict[p] ) + if len(e['prefixes']) == 0: + pfx_c.append( "P_none" ) + pfx = "|".join( pfx_c ) + + self.ItabC.write( " /* %04d */ { UD_I%s %s, %s },\n" \ + % ( idx, e[ 'mnemonic' ] + ',', opr, pfx ) ) + idx += 1 + self.ItabC.write( "};\n" ) + + self.ItabC.write( "\n\n" ); + self.ItabC.write( "const char * ud_mnemonics_str[] = {\n" ) + self.ItabC.write( ",\n ".join( [ "\"%s\"" % m for m in self.MnemonicsTable ] ) ) + self.ItabC.write( "\n};\n" ) + + + def genItabH( self ): + self.ItabH = open( os.path.join(self.outputDir, "udis86_itab.h"), "w" ) + + # Generate Table Type Enumeration + self.ItabH.write( "#ifndef UD_ITAB_H\n" ) + self.ItabH.write( "#define UD_ITAB_H\n\n" ) + + # table type enumeration + self.ItabH.write( "/* ud_table_type -- lookup table types (see lookup.c) */\n" ) + self.ItabH.write( "enum ud_table_type {\n " ) + enum = [ self.TableInfo[ k ][ 'name' ] for k in self.TableInfo.keys() ] + self.ItabH.write( ",\n ".join( enum ) ) + self.ItabH.write( "\n};\n\n" ); + + # mnemonic enumeration + self.ItabH.write( "/* ud_mnemonic -- mnemonic constants */\n" ) + enum = "enum ud_mnemonic_code {\n " + enum += ",\n ".join( [ "UD_I%s" % m for m in self.MnemonicsTable ] ) + enum += "\n} UD_ATTR_PACKED;\n" + self.ItabH.write( enum ) + self.ItabH.write( "\n" ) + + self.ItabH.write("\n/* itab entry operand definitions */\n"); + operands = self.OperandDict.keys() + operands.sort() + for o in operands: + self.ItabH.write("#define O_%-7s { %-12s %-8s }\n" % + (o, self.OperandDict[o][0] + ",", self.OperandDict[o][1])); + self.ItabH.write("\n\n"); + + self.ItabH.write( "extern const char * ud_mnemonics_str[];\n" ) + + self.ItabH.write( "#define GROUP(n) (0x8000 | (n))" ) + + self.ItabH.write( "\n#endif /* UD_ITAB_H */\n" ) + + self.ItabH.close() + + + def genItabC( self ): + self.ItabC = open( os.path.join(self.outputDir, "udis86_itab.c"), "w" ) + self.ItabC.write( "/* itab.c -- generated by itab.py, do no edit" ) + self.ItabC.write( " */\n" ); + self.ItabC.write( "#include \"udis86_decode.h\"\n\n" ); + + self.genLookupTable( self.OpcodeTable0 ) + self.genLookupTableList() + self.genInsnTable() + + self.ItabC.close() + + def genItab( self ): + self.genItabC() + self.genItabH() + +def main(): + parser = OptionParser() + parser.add_option("--outputDir", dest="outputDir", default="") + options, args = parser.parse_args() + generator = UdItabGenerator(os.path.normpath(options.outputDir)) + optableXmlParser = ud_optable.UdOptableXmlParser() + optableXmlParser.parse( args[ 0 ], generator.addInsnDef ) + + generator.genItab() + +if __name__ == '__main__': + main() diff --git a/3rdparty/masm/disassembler/udis86/optable.xml b/3rdparty/masm/disassembler/udis86/optable.xml new file mode 100644 index 0000000000..14b4ac5935 --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/optable.xml @@ -0,0 +1,8959 @@ + + + + + + aaa + + 37 + inv64 + + + + + aad + + d5 + Ib + inv64 + + + + + aam + + d4 + Ib + inv64 + + + + + aas + + 3f + inv64 + + + + + adc + + aso rexr rexx rexb + 10 + Eb Gb + + + aso oso rexw rexr rexx rexb + 11 + Ev Gv + + + aso rexr rexx rexb + 12 + Gb Eb + + + aso oso rexw rexr rexx rexb + 13 + Gv Ev + + + 14 + AL Ib + + + oso rexw + 15 + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=2 + Eb Ib + + + aso rexr rexx rexb + 82 /reg=2 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 81 /reg=2 + Ev Iz + sext + + + aso oso rexw rexr rexx rexb + 83 /reg=2 + Ev Ib + sext + + + + + add + + aso rexr rexx rexb + 00 + Eb Gb + + + aso oso rexw rexr rexx rexb + 01 + Ev Gv + + + aso rexr rexx rexb + 02 + Gb Eb + + + aso oso rexw rexr rexx rexb + 03 + Gv Ev + + + 04 + AL Ib + + + oso rexw + 05 + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=0 + Eb Ib + + + aso rexr rexx rexb + 82 /reg=0 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 81 /reg=0 + Ev Iz + sext + + + aso oso rexw rexr rexx rexb + 83 /reg=0 + Ev Ib + sext + + + + + + + addpd + + aso rexr rexx rexb + sse66 0f 58 + V W + + + + + addps + + aso rexr rexx rexb + 0f 58 + V W + + + + + addsd + + aso rexr rexx rexb + ssef2 0f 58 + V W + + + + + addss + + aso rexr rexx rexb + ssef3 0f 58 + V W + + + + + and + + aso rexr rexx rexb + 20 + Eb Gb + + + aso oso rexw rexr rexx rexb + 21 + Ev Gv + + + aso rexr rexx rexb + 22 + Gb Eb + + + aso oso rexw rexr rexx rexb + 23 + Gv Ev + + + 24 + AL Ib + + + oso rexw + 25 + rAX Iz + sext + + + aso rexw rexr rexx rexb + 80 /reg=4 + Eb Ib + + + aso rexr rexx rexb + 82 /reg=4 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 81 /reg=4 + Ev Iz + sext + + + aso oso rexw rexr rexx rexb + 83 /reg=4 + Ev Ib + sext + + + + + andpd + + aso rexr rexx rexb + sse66 0f 54 + V W + + + + + andps + + aso rexr rexx rexb + 0f 54 + V W + + + + + andnpd + + aso rexr rexx rexb + sse66 0f 55 + V W + + + + + andnps + + aso rexr rexx rexb + 0f 55 + V W + + + + + arpl + + aso + 63 /m=16 + Ew Gw + inv64 + + + aso + 63 /m=32 + Ew Gw + inv64 + + + + + movsxd + + aso oso rexw rexx rexr rexb + 63 /m=64 + Gv Ed + + + + + bound + + aso oso + 62 + Gv M + inv64 + + + + + bsf + + aso oso rexw rexr rexx rexb + 0f bc + Gv Ev + + + + + bsr + + aso oso rexw rexr rexx rexb + 0f bd + Gv Ev + + + + + bswap + + oso rexw rexb + 0f c8 + rAXr8 + + + oso rexw rexb + 0f c9 + rCXr9 + + + oso rexw rexb + 0f ca + rDXr10 + + + oso rexw rexb + 0f cb + rBXr11 + + + oso rexw rexb + 0f cc + rSPr12 + + + oso rexw rexb + 0f cd + rBPr13 + + + oso rexw rexb + 0f ce + rSIr14 + + + oso rexw rexb + 0f cf + rDIr15 + + + + + bt + + aso oso rexw rexr rexx rexb + 0f ba /reg=4 + Ev Ib + + + aso oso rexw rexr rexx rexb + 0f a3 + Ev Gv + + + + + btc + + aso oso rexw rexr rexx rexb + 0f bb + Ev Gv + + + aso oso rexw rexr rexx rexb + 0f ba /reg=7 + Ev Ib + + + + + btr + + aso oso rexw rexr rexx rexb + 0f b3 + Ev Gv + + + aso oso rexw rexr rexx rexb + 0f ba /reg=6 + Ev Ib + + + + + bts + + aso oso rexw rexr rexx rexb + 0f ab + Ev Gv + + + aso oso rexw rexr rexx rexb + 0f ba /reg=5 + Ev Ib + + + + + call + + aso oso rexw rexr rexx rexb + ff /reg=2 + Ev + def64 + + + aso oso rexw rexr rexx rexb + ff /reg=3 + Ep + + + oso + e8 + Jz + def64 + + + oso + 9a + Ap + inv64 + + + + + cbw + + oso rexw + 98 /o=16 + + + + + cwde + + oso rexw + 98 /o=32 + + + + + cdqe + + oso rexw + 98 /o=64 + + + + + clc + + f8 + + + + + cld + + fc + + + + + clflush + + aso rexw rexr rexx rexb + 0f ae /reg=7 /mod=!11 + M + + + + + clgi + amd + + 0f 01 /reg=3 /mod=11 /rm=5 + + + + + cli + + fa + + + + + clts + + 0f 06 + + + + + cmc + + f5 + + + + + cmovo + + aso oso rexw rexr rexx rexb + 0f 40 + Gv Ev + + + + + cmovno + + aso oso rexw rexr rexx rexb + 0f 41 + Gv Ev + + + + + cmovb + + aso oso rexw rexr rexx rexb + 0f 42 + Gv Ev + + + + + cmovae + + aso oso rexw rexr rexx rexb + 0f 43 + Gv Ev + + + + + cmovz + + aso oso rexw rexr rexx rexb + 0f 44 + Gv Ev + + + + + cmovnz + + aso oso rexw rexr rexx rexb + 0f 45 + Gv Ev + + + + + cmovbe + + aso oso rexw rexr rexx rexb + 0f 46 + Gv Ev + + + + + cmova + + aso oso rexw rexr rexx rexb + 0f 47 + Gv Ev + + + + + cmovs + + aso oso rexw rexr rexx rexb + 0f 48 + Gv Ev + + + + + cmovns + + aso oso rexw rexr rexx rexb + 0f 49 + Gv Ev + + + + + cmovp + + aso oso rexw rexr rexx rexb + 0f 4a + Gv Ev + + + + + cmovnp + + aso oso rexw rexr rexx rexb + 0f 4b + Gv Ev + + + + + cmovl + + aso oso rexw rexr rexx rexb + 0f 4c + Gv Ev + + + + + cmovge + + aso oso rexw rexr rexx rexb + 0f 4d + Gv Ev + + + + + cmovle + + aso oso rexw rexr rexx rexb + 0f 4e + Gv Ev + + + + + cmovg + + aso oso rexw rexr rexx rexb + 0f 4f + Gv Ev + + + + + cmp + + aso rexr rexx rexb + 38 + Eb Gb + + + aso oso rexw rexr rexx rexb + 39 + Ev Gv + + + aso rexr rexx rexb + 3a + Gb Eb + + + aso oso rexw rexr rexx rexb + 3b + Gv Ev + + + 3c + AL Ib + + + oso rexw + 3d + rAX Iz + + + aso rexr rexx rexb + 80 /reg=7 + Eb Ib + + + aso rexr rexx rexb + 82 /reg=7 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 81 /reg=7 + Ev Iz + + + aso oso rexw rexr rexx rexb + 83 /reg=7 + Ev Ib + + + + + cmppd + + aso rexr rexx rexb + sse66 0f c2 + V W Ib + + + + + cmpps + + aso rexr rexx rexb + 0f c2 + V W Ib + + + + + cmpsb + + a6 + + + + + cmpsw + + oso rexw + a7 /o=16 + + + + + cmpsd + + oso rexw + a7 /o=32 + + + aso rexr rexx rexb + ssef2 0f c2 + V W Ib + + + + + cmpsq + + oso rexw + a7 /o=64 + + + + + cmpss + + aso rexr rexx rexb + ssef3 0f c2 + V W Ib + + + + + cmpxchg + + aso rexr rexx rexb + 0f b0 + Eb Gb + + + aso oso rexw rexr rexx rexb + 0f b1 + Ev Gv + + + + + cmpxchg8b + + aso rexr rexx rexb + 0f c7 /reg=1 + M + + + + + comisd + + aso rexr rexx rexb + sse66 0f 2f + V W + + + + + comiss + + aso rexr rexx rexb + 0f 2f + V W + + + + + cpuid + + 0f a2 + + + + + cvtdq2pd + + aso rexr rexx rexb + ssef3 0f e6 + V W + + + + + cvtdq2ps + + aso rexr rexx rexb + 0f 5b + V W + + + + + cvtpd2dq + + aso rexr rexx rexb + ssef2 0f e6 + V W + + + + + cvtpd2pi + + aso rexr rexx rexb + sse66 0f 2d + P W + + + + + cvtpd2ps + + aso rexr rexx rexb + sse66 0f 5a + V W + + + + + cvtpi2ps + + aso rexr rexx rexb + 0f 2a + V Q + + + + + cvtpi2pd + + aso rexr rexx rexb + sse66 0f 2a + V Q + + + + + cvtps2dq + + aso rexr rexx rexb + sse66 0f 5b + V W + + + + + cvtps2pi + + aso rexr rexx rexb + 0f 2d + P W + + + + + cvtps2pd + + aso rexr rexx rexb + 0f 5a + V W + + + + + cvtsd2si + + aso rexw rexr rexx rexb + ssef2 0f 2d + Gy W + + + + + cvtsd2ss + + aso rexr rexx rexb + ssef2 0f 5a + V W + + + + + cvtsi2ss + + aso rexw rexr rexx rexb + ssef3 0f 2a + V Ex + + + + + cvtss2si + + aso rexw rexr rexx rexb + ssef3 0f 2d + Gy W + + + + + cvtss2sd + + aso rexr rexx rexb + ssef3 0f 5a + V W + + + + + cvttpd2pi + + aso rexr rexx rexb + sse66 0f 2c + P W + + + + + cvttpd2dq + + aso rexr rexx rexb + sse66 0f e6 + V W + + + + + cvttps2dq + + aso rexr rexx rexb + ssef3 0f 5b + V W + + + + + cvttps2pi + + aso rexr rexx rexb + 0f 2c + P W + + + + + cvttsd2si + + aso rexw rexr rexx rexb + ssef2 0f 2c + Gy Wsd + + + + + cvtsi2sd + + aso rexw rexr rexx rexb + ssef2 0f 2a + V Ex + + + + + cvttss2si + + aso rexw rexr rexx rexb + ssef3 0f 2c + Gy Wsd + + + + + cwd + + oso rexw + 99 /o=16 + + + + + cdq + + oso rexw + 99 /o=32 + + + + + cqo + + oso rexw + 99 /o=64 + + + + + daa + + 27 + inv64 + + + + + das + + 2f + inv64 + + + + + dec + + oso + 48 + eAX + + + oso + 49 + eCX + + + oso + 4a + eDX + + + oso + 4b + eBX + + + oso + 4c + eSP + + + oso + 4d + eBP + + + oso + 4e + eSI + + + oso + 4f + eDI + + + aso rexw rexr rexx rexb + fe /reg=1 + Eb + + + aso oso rexw rexr rexx rexb + ff /reg=1 + Ev + + + + + div + + aso oso rexw rexr rexx rexb + f7 /reg=6 + Ev + + + aso rexw rexr rexx rexb + f6 /reg=6 + Eb + + + + + divpd + + aso rexr rexx rexb + sse66 0f 5e + V W + + + + + divps + + aso rexr rexx rexb + 0f 5e + V W + + + + + divsd + + aso rexr rexx rexb + ssef2 0f 5e + V W + + + + + divss + + aso rexr rexx rexb + ssef3 0f 5e + V W + + + + + emms + + 0f 77 + + + + + enter + + c8 + Iw Ib + def64 depM + + + + + f2xm1 + X87 + + d9 /mod=11 /x87=30 + + + + + fabs + X87 + + d9 /mod=11 /x87=21 + + + + + fadd + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=0 + Mq + + + aso rexr rexx rexb + d8 /mod=!11 /reg=0 + Md + + + dc /mod=11 /x87=00 + ST0 ST0 + + + dc /mod=11 /x87=01 + ST1 ST0 + + + dc /mod=11 /x87=02 + ST2 ST0 + + + dc /mod=11 /x87=03 + ST3 ST0 + + + dc /mod=11 /x87=04 + ST4 ST0 + + + dc /mod=11 /x87=05 + ST5 ST0 + + + dc /mod=11 /x87=06 + ST6 ST0 + + + dc /mod=11 /x87=07 + ST7 ST0 + + + d8 /mod=11 /x87=00 + ST0 ST0 + + + d8 /mod=11 /x87=01 + ST0 ST1 + + + d8 /mod=11 /x87=02 + ST0 ST2 + + + d8 /mod=11 /x87=03 + ST0 ST3 + + + d8 /mod=11 /x87=04 + ST0 ST4 + + + d8 /mod=11 /x87=05 + ST0 ST5 + + + d8 /mod=11 /x87=06 + ST0 ST6 + + + d8 /mod=11 /x87=07 + ST0 ST7 + + + + + faddp + X87 + + de /mod=11 /x87=00 + ST0 ST0 + + + de /mod=11 /x87=01 + ST1 ST0 + + + de /mod=11 /x87=02 + ST2 ST0 + + + de /mod=11 /x87=03 + ST3 ST0 + + + de /mod=11 /x87=04 + ST4 ST0 + + + de /mod=11 /x87=05 + ST5 ST0 + + + de /mod=11 /x87=06 + ST6 ST0 + + + de /mod=11 /x87=07 + ST7 ST0 + + + + + fbld + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=4 + Mt + + + + + fbstp + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=6 + Mt + + + + + fchs + X87 + + d9 /mod=11 /x87=20 + + + + + fclex + X87 + + db /mod=11 /x87=22 + + + + + fcmovb + X87 + + da /mod=11 /x87=00 + ST0 ST0 + + + da /mod=11 /x87=01 + ST0 ST1 + + + da /mod=11 /x87=02 + ST0 ST2 + + + da /mod=11 /x87=03 + ST0 ST3 + + + da /mod=11 /x87=04 + ST0 ST4 + + + da /mod=11 /x87=05 + ST0 ST5 + + + da /mod=11 /x87=06 + ST0 ST6 + + + da /mod=11 /x87=07 + ST0 ST7 + + + + + fcmove + X87 + + da /mod=11 /x87=08 + ST0 ST0 + + + da /mod=11 /x87=09 + ST0 ST1 + + + da /mod=11 /x87=0a + ST0 ST2 + + + da /mod=11 /x87=0b + ST0 ST3 + + + da /mod=11 /x87=0c + ST0 ST4 + + + da /mod=11 /x87=0d + ST0 ST5 + + + da /mod=11 /x87=0e + ST0 ST6 + + + da /mod=11 /x87=0f + ST0 ST7 + + + + + fcmovbe + X87 + + da /mod=11 /x87=10 + ST0 ST0 + + + da /mod=11 /x87=11 + ST0 ST1 + + + da /mod=11 /x87=12 + ST0 ST2 + + + da /mod=11 /x87=13 + ST0 ST3 + + + da /mod=11 /x87=14 + ST0 ST4 + + + da /mod=11 /x87=15 + ST0 ST5 + + + da /mod=11 /x87=16 + ST0 ST6 + + + da /mod=11 /x87=17 + ST0 ST7 + + + + + fcmovu + X87 + + da /mod=11 /x87=18 + ST0 ST0 + + + da /mod=11 /x87=19 + ST0 ST1 + + + da /mod=11 /x87=1a + ST0 ST2 + + + da /mod=11 /x87=1b + ST0 ST3 + + + da /mod=11 /x87=1c + ST0 ST4 + + + da /mod=11 /x87=1d + ST0 ST5 + + + da /mod=11 /x87=1e + ST0 ST6 + + + da /mod=11 /x87=1f + ST0 ST7 + + + + + fcmovnb + X87 + + db /mod=11 /x87=00 + ST0 ST0 + + + db /mod=11 /x87=01 + ST0 ST1 + + + db /mod=11 /x87=02 + ST0 ST2 + + + db /mod=11 /x87=03 + ST0 ST3 + + + db /mod=11 /x87=04 + ST0 ST4 + + + db /mod=11 /x87=05 + ST0 ST5 + + + db /mod=11 /x87=06 + ST0 ST6 + + + db /mod=11 /x87=07 + ST0 ST7 + + + + + fcmovne + X87 + + db /mod=11 /x87=08 + ST0 ST0 + + + db /mod=11 /x87=09 + ST0 ST1 + + + db /mod=11 /x87=0a + ST0 ST2 + + + db /mod=11 /x87=0b + ST0 ST3 + + + db /mod=11 /x87=0c + ST0 ST4 + + + db /mod=11 /x87=0d + ST0 ST5 + + + db /mod=11 /x87=0e + ST0 ST6 + + + db /mod=11 /x87=0f + ST0 ST7 + + + + + fcmovnbe + X87 + + db /mod=11 /x87=10 + ST0 ST0 + + + db /mod=11 /x87=11 + ST0 ST1 + + + db /mod=11 /x87=12 + ST0 ST2 + + + db /mod=11 /x87=13 + ST0 ST3 + + + db /mod=11 /x87=14 + ST0 ST4 + + + db /mod=11 /x87=15 + ST0 ST5 + + + db /mod=11 /x87=16 + ST0 ST6 + + + db /mod=11 /x87=17 + ST0 ST7 + + + + + fcmovnu + X87 + + db /mod=11 /x87=18 + ST0 ST0 + + + db /mod=11 /x87=19 + ST0 ST1 + + + db /mod=11 /x87=1a + ST0 ST2 + + + db /mod=11 /x87=1b + ST0 ST3 + + + db /mod=11 /x87=1c + ST0 ST4 + + + db /mod=11 /x87=1d + ST0 ST5 + + + db /mod=11 /x87=1e + ST0 ST6 + + + db /mod=11 /x87=1f + ST0 ST7 + + + + + fucomi + X87 + + db /mod=11 /x87=28 + ST0 ST0 + + + db /mod=11 /x87=29 + ST0 ST1 + + + db /mod=11 /x87=2a + ST0 ST2 + + + db /mod=11 /x87=2b + ST0 ST3 + + + db /mod=11 /x87=2c + ST0 ST4 + + + db /mod=11 /x87=2d + ST0 ST5 + + + db /mod=11 /x87=2e + ST0 ST6 + + + db /mod=11 /x87=2f + ST0 ST7 + + + + + fcom + X87 + + aso rexr rexx rexb + d8 /mod=!11 /reg=2 + Md + + + aso rexr rexx rexb + dc /mod=!11 /reg=2 + Mq + + + d8 /mod=11 /x87=10 + ST0 ST0 + + + d8 /mod=11 /x87=11 + ST0 ST1 + + + d8 /mod=11 /x87=12 + ST0 ST2 + + + d8 /mod=11 /x87=13 + ST0 ST3 + + + d8 /mod=11 /x87=14 + ST0 ST4 + + + d8 /mod=11 /x87=15 + ST0 ST5 + + + d8 /mod=11 /x87=16 + ST0 ST6 + + + d8 /mod=11 /x87=17 + ST0 ST7 + + + + + fcom2 + X87 UNDOC + + dc /mod=11 /x87=10 + ST0 + + + dc /mod=11 /x87=11 + ST1 + + + dc /mod=11 /x87=12 + ST2 + + + dc /mod=11 /x87=13 + ST3 + + + dc /mod=11 /x87=14 + ST4 + + + dc /mod=11 /x87=15 + ST5 + + + dc /mod=11 /x87=16 + ST6 + + + dc /mod=11 /x87=17 + ST7 + + + + + fcomp3 + X87 UNDOC + + dc /mod=11 /x87=18 + ST0 + + + dc /mod=11 /x87=19 + ST1 + + + dc /mod=11 /x87=1a + ST2 + + + dc /mod=11 /x87=1b + ST3 + + + dc /mod=11 /x87=1c + ST4 + + + dc /mod=11 /x87=1d + ST5 + + + dc /mod=11 /x87=1e + ST6 + + + dc /mod=11 /x87=1f + ST7 + + + + + fcomi + X87 + + db /mod=11 /x87=30 + ST0 ST0 + + + db /mod=11 /x87=31 + ST0 ST1 + + + db /mod=11 /x87=32 + ST0 ST2 + + + db /mod=11 /x87=33 + ST0 ST3 + + + db /mod=11 /x87=34 + ST0 ST4 + + + db /mod=11 /x87=35 + ST0 ST5 + + + db /mod=11 /x87=36 + ST0 ST6 + + + db /mod=11 /x87=37 + ST0 ST7 + + + + + fucomip + X87 + + df /mod=11 /x87=28 + ST0 ST0 + + + df /mod=11 /x87=29 + ST0 ST1 + + + df /mod=11 /x87=2a + ST0 ST2 + + + df /mod=11 /x87=2b + ST0 ST3 + + + df /mod=11 /x87=2c + ST0 ST4 + + + df /mod=11 /x87=2d + ST0 ST5 + + + df /mod=11 /x87=2e + ST0 ST6 + + + df /mod=11 /x87=2f + ST0 ST7 + + + + + fcomip + X87 + + df /mod=11 /x87=30 + ST0 ST0 + + + df /mod=11 /x87=31 + ST0 ST1 + + + df /mod=11 /x87=32 + ST0 ST2 + + + df /mod=11 /x87=33 + ST0 ST3 + + + df /mod=11 /x87=34 + ST0 ST4 + + + df /mod=11 /x87=35 + ST0 ST5 + + + df /mod=11 /x87=36 + ST0 ST6 + + + df /mod=11 /x87=37 + ST0 ST7 + + + + + fcomp + X87 + + aso rexr rexx rexb + d8 /mod=!11 /reg=3 + Md + + + aso rexr rexx rexb + dc /mod=!11 /reg=3 + Mq + + + d8 /mod=11 /x87=18 + ST0 ST0 + + + d8 /mod=11 /x87=19 + ST0 ST1 + + + d8 /mod=11 /x87=1a + ST0 ST2 + + + d8 /mod=11 /x87=1b + ST0 ST3 + + + d8 /mod=11 /x87=1c + ST0 ST4 + + + d8 /mod=11 /x87=1d + ST0 ST5 + + + d8 /mod=11 /x87=1e + ST0 ST6 + + + d8 /mod=11 /x87=1f + ST0 ST7 + + + + + fcomp5 + X87 UNDOC + + de /mod=11 /x87=10 + ST0 + + + de /mod=11 /x87=11 + ST1 + + + de /mod=11 /x87=12 + ST2 + + + de /mod=11 /x87=13 + ST3 + + + de /mod=11 /x87=14 + ST4 + + + de /mod=11 /x87=15 + ST5 + + + de /mod=11 /x87=16 + ST6 + + + de /mod=11 /x87=17 + ST7 + + + + + fcompp + X87 + + de /mod=11 /x87=19 + + + + + fcos + X87 + + d9 /mod=11 /x87=3f + + + + + fdecstp + X87 + + d9 /mod=11 /x87=36 + + + + + fdiv + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=6 + Mq + + + dc /mod=11 /x87=38 + ST0 ST0 + + + dc /mod=11 /x87=39 + ST1 ST0 + + + dc /mod=11 /x87=3a + ST2 ST0 + + + dc /mod=11 /x87=3b + ST3 ST0 + + + dc /mod=11 /x87=3c + ST4 ST0 + + + dc /mod=11 /x87=3d + ST5 ST0 + + + dc /mod=11 /x87=3e + ST6 ST0 + + + dc /mod=11 /x87=3f + ST7 ST0 + + + aso rexr rexx rexb + d8 /mod=!11 /reg=6 + Md + + + d8 /mod=11 /x87=30 + ST0 ST0 + + + d8 /mod=11 /x87=31 + ST0 ST1 + + + d8 /mod=11 /x87=32 + ST0 ST2 + + + d8 /mod=11 /x87=33 + ST0 ST3 + + + d8 /mod=11 /x87=34 + ST0 ST4 + + + d8 /mod=11 /x87=35 + ST0 ST5 + + + d8 /mod=11 /x87=36 + ST0 ST6 + + + d8 /mod=11 /x87=37 + ST0 ST7 + + + + + fdivp + X87 + + de /mod=11 /x87=38 + ST0 ST0 + + + de /mod=11 /x87=39 + ST1 ST0 + + + de /mod=11 /x87=3a + ST2 ST0 + + + de /mod=11 /x87=3b + ST3 ST0 + + + de /mod=11 /x87=3c + ST4 ST0 + + + de /mod=11 /x87=3d + ST5 ST0 + + + de /mod=11 /x87=3e + ST6 ST0 + + + de /mod=11 /x87=3f + ST7 ST0 + + + + + fdivr + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=7 + Mq + + + dc /mod=11 /x87=30 + ST0 ST0 + + + dc /mod=11 /x87=31 + ST1 ST0 + + + dc /mod=11 /x87=32 + ST2 ST0 + + + dc /mod=11 /x87=33 + ST3 ST0 + + + dc /mod=11 /x87=34 + ST4 ST0 + + + dc /mod=11 /x87=35 + ST5 ST0 + + + dc /mod=11 /x87=36 + ST6 ST0 + + + dc /mod=11 /x87=37 + ST7 ST0 + + + aso rexr rexx rexb + d8 /mod=!11 /reg=7 + Md + + + d8 /mod=11 /x87=38 + ST0 ST0 + + + d8 /mod=11 /x87=39 + ST0 ST1 + + + d8 /mod=11 /x87=3a + ST0 ST2 + + + d8 /mod=11 /x87=3b + ST0 ST3 + + + d8 /mod=11 /x87=3c + ST0 ST4 + + + d8 /mod=11 /x87=3d + ST0 ST5 + + + d8 /mod=11 /x87=3e + ST0 ST6 + + + d8 /mod=11 /x87=3f + ST0 ST7 + + + + + fdivrp + X87 + + de /mod=11 /x87=30 + ST0 ST0 + + + de /mod=11 /x87=31 + ST1 ST0 + + + de /mod=11 /x87=32 + ST2 ST0 + + + de /mod=11 /x87=33 + ST3 ST0 + + + de /mod=11 /x87=34 + ST4 ST0 + + + de /mod=11 /x87=35 + ST5 ST0 + + + de /mod=11 /x87=36 + ST6 ST0 + + + de /mod=11 /x87=37 + ST7 ST0 + + + + + femms + + 0f 0e + + + + + ffree + X87 + + dd /mod=11 /x87=00 + ST0 + + + dd /mod=11 /x87=01 + ST1 + + + dd /mod=11 /x87=02 + ST2 + + + dd /mod=11 /x87=03 + ST3 + + + dd /mod=11 /x87=04 + ST4 + + + dd /mod=11 /x87=05 + ST5 + + + dd /mod=11 /x87=06 + ST6 + + + dd /mod=11 /x87=07 + ST7 + + + + + ffreep + X87 + + df /mod=11 /x87=00 + ST0 + + + df /mod=11 /x87=01 + ST1 + + + df /mod=11 /x87=02 + ST2 + + + df /mod=11 /x87=03 + ST3 + + + df /mod=11 /x87=04 + ST4 + + + df /mod=11 /x87=05 + ST5 + + + df /mod=11 /x87=06 + ST6 + + + df /mod=11 /x87=07 + ST7 + + + + + ficom + X87 + + aso rexr rexx rexb + de /mod=!11 /reg=2 + Mw + + + aso rexr rexx rexb + da /mod=!11 /reg=2 + Md + + + + + ficomp + X87 + + aso rexr rexx rexb + de /mod=!11 /reg=3 + Mw + + + aso rexr rexx rexb + da /mod=!11 /reg=3 + Md + + + + + fild + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=0 + Mw + + + aso rexr rexx rexb + df /mod=!11 /reg=5 + Mq + + + aso rexr rexx rexb + db /mod=!11 /reg=0 + Md + + + + + fncstp + X87 + + d9 /mod=11 /x87=37 + + + + + fninit + X87 + + db /mod=11 /x87=23 + + + + + fiadd + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=0 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=0 + Mw + + + + + fidivr + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=7 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=7 + Mw + + + + + fidiv + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=6 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=6 + Mw + + + + + fisub + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=4 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=4 + Mw + + + + + fisubr + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=5 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=5 + Mw + + + + + fist + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=2 + Mw + + + aso rexr rexx rexb + db /mod=!11 /reg=2 + Md + + + + + fistp + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=3 + Mw + + + aso rexr rexx rexb + df /mod=!11 /reg=7 + Mq + + + aso rexr rexx rexb + db /mod=!11 /reg=3 + Md + + + + + fisttp + X87 + + aso rexr rexx rexb + db /mod=!11 /reg=1 + Md + + + aso rexr rexx rexb + dd /mod=!11 /reg=1 + Mq + + + aso rexr rexx rexb + df /mod=!11 /reg=1 + Mw + + + + + fld + X87 + + aso rexr rexx rexb + db /mod=!11 /reg=5 + Mt + + + aso rexr rexx rexb + dd /mod=!11 /reg=0 + Mq + + + aso rexr rexx rexb + d9 /mod=!11 /reg=0 + Md + + + d9 /mod=11 /x87=00 + ST0 + + + d9 /mod=11 /x87=01 + ST1 + + + d9 /mod=11 /x87=02 + ST2 + + + d9 /mod=11 /x87=03 + ST3 + + + d9 /mod=11 /x87=04 + ST4 + + + d9 /mod=11 /x87=05 + ST5 + + + d9 /mod=11 /x87=06 + ST6 + + + d9 /mod=11 /x87=07 + ST7 + + + + + fld1 + X87 + + d9 /mod=11 /x87=28 + + + + + fldl2t + X87 + + d9 /mod=11 /x87=29 + + + + + fldl2e + X87 + + d9 /mod=11 /x87=2a + + + + + fldlpi + X87 + + d9 /mod=11 /x87=2b + + + + + fldlg2 + X87 + + d9 /mod=11 /x87=2c + + + + + fldln2 + X87 + + d9 /mod=11 /x87=2d + + + + + fldz + X87 + + d9 /mod=11 /x87=2e + + + + + fldcw + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=5 + Mw + + + + + fldenv + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=4 + M + + + + + fmul + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=1 + Mq + + + dc /mod=11 /x87=08 + ST0 ST0 + + + dc /mod=11 /x87=09 + ST1 ST0 + + + dc /mod=11 /x87=0a + ST2 ST0 + + + dc /mod=11 /x87=0b + ST3 ST0 + + + dc /mod=11 /x87=0c + ST4 ST0 + + + dc /mod=11 /x87=0d + ST5 ST0 + + + dc /mod=11 /x87=0e + ST6 ST0 + + + dc /mod=11 /x87=0f + ST7 ST0 + + + aso rexr rexx rexb + d8 /mod=!11 /reg=1 + Md + + + d8 /mod=11 /x87=08 + ST0 ST0 + + + d8 /mod=11 /x87=09 + ST0 ST1 + + + d8 /mod=11 /x87=0a + ST0 ST2 + + + d8 /mod=11 /x87=0b + ST0 ST3 + + + d8 /mod=11 /x87=0c + ST0 ST4 + + + d8 /mod=11 /x87=0d + ST0 ST5 + + + d8 /mod=11 /x87=0e + ST0 ST6 + + + d8 /mod=11 /x87=0f + ST0 ST7 + + + + + fmulp + X87 + + de /mod=11 /x87=08 + ST0 ST0 + + + de /mod=11 /x87=09 + ST1 ST0 + + + de /mod=11 /x87=0a + ST2 ST0 + + + de /mod=11 /x87=0b + ST3 ST0 + + + de /mod=11 /x87=0c + ST4 ST0 + + + de /mod=11 /x87=0d + ST5 ST0 + + + de /mod=11 /x87=0e + ST6 ST0 + + + de /mod=11 /x87=0f + ST7 ST0 + + + + + fimul + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=1 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=1 + Mw + + + + + fnop + X87 + + d9 /mod=11 /x87=10 + + + + + fpatan + X87 + + d9 /mod=11 /x87=33 + + + + + fprem + X87 + + d9 /mod=11 /x87=38 + + + + + fprem1 + X87 + + d9 /mod=11 /x87=35 + + + + + fptan + X87 + + d9 /mod=11 /x87=32 + + + + + frndint + X87 + + d9 /mod=11 /x87=3c + + + + + frstor + X87 + + aso rexr rexx rexb + dd /mod=!11 /reg=4 + M + + + + + fnsave + X87 + + aso rexr rexx rexb + dd /mod=!11 /reg=6 + M + + + + + fscale + X87 + + d9 /mod=11 /x87=3d + + + + + fsin + X87 + + d9 /mod=11 /x87=3e + + + + + fsincos + X87 + + d9 /mod=11 /x87=3b + + + + + fsqrt + X87 + + d9 /mod=11 /x87=3a + + + + + fstp + X87 + + aso rexr rexx rexb + db /mod=!11 /reg=7 + Mt + + + aso rexr rexx rexb + dd /mod=!11 /reg=3 + Mq + + + aso rexr rexx rexb + d9 /mod=!11 /reg=3 + Md + + + dd /mod=11 /x87=18 + ST0 + + + dd /mod=11 /x87=19 + ST1 + + + dd /mod=11 /x87=1a + ST2 + + + dd /mod=11 /x87=1b + ST3 + + + dd /mod=11 /x87=1c + ST4 + + + dd /mod=11 /x87=1d + ST5 + + + dd /mod=11 /x87=1e + ST6 + + + dd /mod=11 /x87=1f + ST7 + + + + + fstp1 + + d9 /mod=11 /x87=18 + ST0 + + + d9 /mod=11 /x87=19 + ST1 + + + d9 /mod=11 /x87=1a + ST2 + + + d9 /mod=11 /x87=1b + ST3 + + + d9 /mod=11 /x87=1c + ST4 + + + d9 /mod=11 /x87=1d + ST5 + + + d9 /mod=11 /x87=1e + ST6 + + + d9 /mod=11 /x87=1f + ST7 + + + + + fstp8 + + df /mod=11 /x87=10 + ST0 + + + df /mod=11 /x87=11 + ST1 + + + df /mod=11 /x87=12 + ST2 + + + df /mod=11 /x87=13 + ST3 + + + df /mod=11 /x87=14 + ST4 + + + df /mod=11 /x87=15 + ST5 + + + df /mod=11 /x87=16 + ST6 + + + df /mod=11 /x87=17 + ST7 + + + + + fstp9 + + df /mod=11 /x87=18 + ST0 + + + df /mod=11 /x87=19 + ST1 + + + df /mod=11 /x87=1a + ST2 + + + df /mod=11 /x87=1b + ST3 + + + df /mod=11 /x87=1c + ST4 + + + df /mod=11 /x87=1d + ST5 + + + df /mod=11 /x87=1e + ST6 + + + df /mod=11 /x87=1f + ST7 + + + + + fst + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=2 + Md + + + aso rexr rexx rexb + dd /mod=!11 /reg=2 + Mq + + + dd /mod=11 /x87=10 + ST0 + + + dd /mod=11 /x87=11 + ST1 + + + dd /mod=11 /x87=12 + ST2 + + + dd /mod=11 /x87=13 + ST3 + + + dd /mod=11 /x87=14 + ST4 + + + dd /mod=11 /x87=15 + ST5 + + + dd /mod=11 /x87=16 + ST6 + + + dd /mod=11 /x87=17 + ST7 + + + + + fnstcw + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=7 + Mw + + + + + fnstenv + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=6 + M + + + + + fnstsw + X87 + + aso rexr rexx rexb + dd /mod=!11 /reg=7 + Mw + + + df /mod=11 /x87=20 + AX + + + + + fsub + X87 + + aso rexr rexx rexb + d8 /mod=!11 /reg=4 + Md + + + aso rexr rexx rexb + dc /mod=!11 /reg=4 + Mq + + + d8 /mod=11 /x87=20 + ST0 ST0 + + + d8 /mod=11 /x87=21 + ST0 ST1 + + + d8 /mod=11 /x87=22 + ST0 ST2 + + + d8 /mod=11 /x87=23 + ST0 ST3 + + + d8 /mod=11 /x87=24 + ST0 ST4 + + + d8 /mod=11 /x87=25 + ST0 ST5 + + + d8 /mod=11 /x87=26 + ST0 ST6 + + + d8 /mod=11 /x87=27 + ST0 ST7 + + + dc /mod=11 /x87=28 + ST0 ST0 + + + dc /mod=11 /x87=29 + ST1 ST0 + + + dc /mod=11 /x87=2a + ST2 ST0 + + + dc /mod=11 /x87=2b + ST3 ST0 + + + dc /mod=11 /x87=2c + ST4 ST0 + + + dc /mod=11 /x87=2d + ST5 ST0 + + + dc /mod=11 /x87=2e + ST6 ST0 + + + dc /mod=11 /x87=2f + ST7 ST0 + + + + + fsubp + X87 + + de /mod=11 /x87=28 + ST0 ST0 + + + de /mod=11 /x87=29 + ST1 ST0 + + + de /mod=11 /x87=2a + ST2 ST0 + + + de /mod=11 /x87=2b + ST3 ST0 + + + de /mod=11 /x87=2c + ST4 ST0 + + + de /mod=11 /x87=2d + ST5 ST0 + + + de /mod=11 /x87=2e + ST6 ST0 + + + de /mod=11 /x87=2f + ST7 ST0 + + + + + fsubr + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=5 + Mq + + + d8 /mod=11 /x87=28 + ST0 ST0 + + + d8 /mod=11 /x87=29 + ST0 ST1 + + + d8 /mod=11 /x87=2a + ST0 ST2 + + + d8 /mod=11 /x87=2b + ST0 ST3 + + + d8 /mod=11 /x87=2c + ST0 ST4 + + + d8 /mod=11 /x87=2d + ST0 ST5 + + + d8 /mod=11 /x87=2e + ST0 ST6 + + + d8 /mod=11 /x87=2f + ST0 ST7 + + + dc /mod=11 /x87=20 + ST0 ST0 + + + dc /mod=11 /x87=21 + ST1 ST0 + + + dc /mod=11 /x87=22 + ST2 ST0 + + + dc /mod=11 /x87=23 + ST3 ST0 + + + dc /mod=11 /x87=24 + ST4 ST0 + + + dc /mod=11 /x87=25 + ST5 ST0 + + + dc /mod=11 /x87=26 + ST6 ST0 + + + dc /mod=11 /x87=27 + ST7 ST0 + + + aso rexr rexx rexb + d8 /mod=!11 /reg=5 + Md + + + + + fsubrp + X87 + + de /mod=11 /x87=20 + ST0 ST0 + + + de /mod=11 /x87=21 + ST1 ST0 + + + de /mod=11 /x87=22 + ST2 ST0 + + + de /mod=11 /x87=23 + ST3 ST0 + + + de /mod=11 /x87=24 + ST4 ST0 + + + de /mod=11 /x87=25 + ST5 ST0 + + + de /mod=11 /x87=26 + ST6 ST0 + + + de /mod=11 /x87=27 + ST7 ST0 + + + + + ftst + X87 + + d9 /mod=11 /x87=24 + + + + + fucom + X87 + + dd /mod=11 /x87=20 + ST0 + + + dd /mod=11 /x87=21 + ST1 + + + dd /mod=11 /x87=22 + ST2 + + + dd /mod=11 /x87=23 + ST3 + + + dd /mod=11 /x87=24 + ST4 + + + dd /mod=11 /x87=25 + ST5 + + + dd /mod=11 /x87=26 + ST6 + + + dd /mod=11 /x87=27 + ST7 + + + + + fucomp + X87 + + dd /mod=11 /x87=28 + ST0 + + + dd /mod=11 /x87=29 + ST1 + + + dd /mod=11 /x87=2a + ST2 + + + dd /mod=11 /x87=2b + ST3 + + + dd /mod=11 /x87=2c + ST4 + + + dd /mod=11 /x87=2d + ST5 + + + dd /mod=11 /x87=2e + ST6 + + + dd /mod=11 /x87=2f + ST7 + + + + + fucompp + X87 + + da /mod=11 /x87=29 + + + + + fxam + X87 + + d9 /mod=11 /x87=25 + + + + + fxch + X87 + + d9 /mod=11 /x87=08 + ST0 ST0 + + + d9 /mod=11 /x87=09 + ST0 ST1 + + + d9 /mod=11 /x87=0a + ST0 ST2 + + + d9 /mod=11 /x87=0b + ST0 ST3 + + + d9 /mod=11 /x87=0c + ST0 ST4 + + + d9 /mod=11 /x87=0d + ST0 ST5 + + + d9 /mod=11 /x87=0e + ST0 ST6 + + + d9 /mod=11 /x87=0f + ST0 ST7 + + + + + fxch4 + X87 + + dd /mod=11 /x87=08 + ST0 + + + dd /mod=11 /x87=09 + ST1 + + + dd /mod=11 /x87=0a + ST2 + + + dd /mod=11 /x87=0b + ST3 + + + dd /mod=11 /x87=0c + ST4 + + + dd /mod=11 /x87=0d + ST5 + + + dd /mod=11 /x87=0e + ST6 + + + dd /mod=11 /x87=0f + ST7 + + + + + fxch7 + X87 + + df /mod=11 /x87=08 + ST0 + + + df /mod=11 /x87=09 + ST1 + + + df /mod=11 /x87=0a + ST2 + + + df /mod=11 /x87=0b + ST3 + + + df /mod=11 /x87=0c + ST4 + + + df /mod=11 /x87=0d + ST5 + + + df /mod=11 /x87=0e + ST6 + + + df /mod=11 /x87=0f + ST7 + + + + + fxrstor + + aso rexw rexr rexx rexb + 0f ae /mod=11 /reg=1 + M + + + + + fxsave + + aso rexw rexr rexx rexb + 0f ae /mod=11 /reg=0 + M + + + + + fpxtract + X87 + + d9 /mod=11 /x87=34 + + + + + fyl2x + X87 + + d9 /mod=11 /x87=31 + + + + + fyl2xp1 + X87 + + d9 /mod=11 /x87=39 + + + + + hlt + + f4 + + + + + idiv + + aso oso rexw rexr rexx rexb + f7 /reg=7 + Ev + + + aso rexw rexr rexx rexb + f6 /reg=7 + Eb + + + + + in + + e4 + AL Ib + + + oso + e5 + eAX Ib + + + ec + AL DX + + + oso + ed + eAX DX + + + + + imul + + aso oso rexw rexr rexx rexb + 0f af + Gv Ev + + + aso rexw rexr rexx rexb + f6 /reg=5 + Eb + + + aso oso rexw rexr rexx rexb + f7 /reg=5 + Ev + + + aso oso rexw rexr rexx rexb + 69 + Gv Ev Iz + sext + + + aso oso rexw rexr rexx rexb + 6b + Gv Ev Ib + sext + + + + + inc + + oso + 40 + eAX + + + oso + 41 + eCX + + + oso + 42 + eDX + + + oso + 43 + eBX + + + oso + 44 + eSP + + + oso + 45 + eBP + + + oso + 46 + eSI + + + oso + 47 + eDI + + + aso oso rexw rexr rexx rexb + ff /reg=0 + Ev + + + aso rexw rexr rexx rexb + fe /reg=0 + Eb + + + + + insb + + 6c + + + + + insw + + oso + 6d /o=16 + + + + + insd + + oso + 6d /o=32 + + + + + int1 + + f1 + + + + + int3 + + cc + + + + + int + + cd + Ib + + + + + into + + ce + inv64 + + + + + invd + + 0f 08 + + + + + invept + intel + + sse66 0f 38 80 /m=32 + Gd Mo + + + sse66 0f 38 80 /m=64 + Gq Mo + + + + + invlpg + + aso rexr rexx rexb + 0f 01 /reg=7 /mod=!11 + M + + + + + invlpga + amd + + 0f 01 /reg=3 /mod=11 /rm=7 + + + + + invvpid + intel + + sse66 0f 38 81 /m=32 + Gd Mo + + + sse66 0f 38 81 /m=64 + Gq Mo + + + + + iretw + + oso rexw + cf /o=16 + + + + + iretd + + oso rexw + cf /o=32 + + + + + iretq + + oso rexw + cf /o=64 + + + + + jo + + 70 + Jb + + + oso + 0f 80 + Jz + def64 depM + + + + + jno + + 71 + Jb + + + oso + 0f 81 + Jz + def64 depM + + + + + jb + + 72 + Jb + + + oso + 0f 82 + Jz + def64 depM + + + + + jae + + 73 + Jb + + + oso + 0f 83 + Jz + def64 depM + + + + + jz + + 74 + Jb + + + oso + 0f 84 + Jz + def64 depM + + + + + jnz + + 75 + Jb + + + oso + 0f 85 + Jz + def64 depM + + + + + jbe + + 76 + Jb + + + oso + 0f 86 + Jz + def64 depM + + + + + ja + + 77 + Jb + + + oso + 0f 87 + Jz + def64 depM + + + + + js + + 78 + Jb + + + oso + 0f 88 + Jz + def64 depM + + + + + jns + + 79 + Jb + + + oso + 0f 89 + Jz + def64 depM + + + + + jp + + 7a + Jb + + + oso + 0f 8a + Jz + def64 depM + + + + + jnp + + 7b + Jb + + + oso + 0f 8b + Jz + def64 depM + + + + + jl + + 7c + Jb + + + oso + 0f 8c + Jz + def64 depM + + + + + jge + + 7d + Jb + + + oso + 0f 8d + Jz + def64 depM + + + + + jle + + 7e + Jb + + + oso + 0f 8e + Jz + def64 depM + + + + + jg + + 7f + Jb + + + oso + 0f 8f + Jz + def64 depM + + + + + jcxz + + aso + e3 /a=16 + Jb + + + + + jecxz + + aso + e3 /a=32 + Jb + + + + + jrcxz + + aso + e3 /a=64 + Jb + + + + + jmp + + aso oso rexw rexr rexx rexb + ff /reg=4 + Ev + def64 depM + + + aso oso rexw rexr rexx rexb + ff /reg=5 + Ep + + + oso + e9 + Jz + def64 depM + cast + + + ea + Ap + inv64 + + + eb + Jb + + + + + lahf + + 9f + + + + + lar + + aso oso rexw rexr rexx rexb + 0f 02 + Gv Ew + + + + + lddqu + + aso rexr rexx rexb + ssef2 0f f0 + V M + + + + + ldmxcsr + + aso rexw rexr rexx rexb + 0f ae /reg=2 /mod=11 + Md + + + + + lds + + aso oso + c5 + Gv M + inv64 + + + + + lea + + aso oso rexw rexr rexx rexb + 8d + Gv M + + + + + les + + aso oso + c4 + Gv M + inv64 + + + + + lfs + + aso oso rexw rexr rexx rexb + 0f b4 + Gz M + + + + + lgs + + aso oso rexw rexr rexx rexb + 0f b5 + Gz M + + + + + lidt + + aso rexr rexx rexb + 0f 01 /reg=3 /mod=!11 + M + + + + + lss + + aso oso rexw rexr rexx rexb + 0f b2 + Gz M + + + + + leave + + c9 + + + + + lfence + + 0f ae /reg=5 /mod=11 /rm=0 + + + 0f ae /reg=5 /mod=11 /rm=1 + + + 0f ae /reg=5 /mod=11 /rm=2 + + + 0f ae /reg=5 /mod=11 /rm=3 + + + 0f ae /reg=5 /mod=11 /rm=4 + + + 0f ae /reg=5 /mod=11 /rm=5 + + + 0f ae /reg=5 /mod=11 /rm=6 + + + 0f ae /reg=5 /mod=11 /rm=7 + + + + + lgdt + + aso rexr rexx rexb + 0f 01 /reg=2 /mod=!11 + M + + + + + lldt + + aso rexr rexx rexb + 0f 00 /reg=2 + Ew + + + + + lmsw + + aso rexr rexx rexb + 0f 01 /reg=6 /mod=!11 + Ew + + + + + lock + + f0 + + + + + lodsb + + seg + ac + + + + + lodsw + + seg oso rexw + ad /o=16 + + + + + lodsd + + seg oso rexw + ad /o=32 + + + + + lodsq + + seg oso rexw + ad /o=64 + + + + + loopnz + + e0 + Jb + + + + + loope + + e1 + Jb + + + + + loop + + e2 + Jb + + + + + lsl + + aso oso rexw rexr rexx rexb + 0f 03 + Gv Ew + + + + + ltr + + aso rexr rexx rexb + 0f 00 /reg=3 + Ew + + + + + maskmovq + + aso rexr rexx rexb + 0f f7 + P PR + + + + + maxpd + + aso rexr rexx rexb + sse66 0f 5f + V W + + + + + maxps + + aso rexr rexx rexb + 0f 5f + V W + + + + + maxsd + + aso rexr rexx rexb + ssef2 0f 5f + V W + + + + + maxss + + aso rexr rexx rexb + ssef3 0f 5f + V W + + + + + mfence + + 0f ae /reg=6 /mod=11 /rm=0 + + + 0f ae /reg=6 /mod=11 /rm=1 + + + 0f ae /reg=6 /mod=11 /rm=2 + + + 0f ae /reg=6 /mod=11 /rm=3 + + + 0f ae /reg=6 /mod=11 /rm=4 + + + 0f ae /reg=6 /mod=11 /rm=5 + + + 0f ae /reg=6 /mod=11 /rm=6 + + + 0f ae /reg=6 /mod=11 /rm=7 + + + + + minpd + + aso rexr rexx rexb + sse66 0f 5d + V W + + + + + minps + + aso rexr rexx rexb + 0f 5d + V W + + + + + minsd + + aso rexr rexx rexb + ssef2 0f 5d + V W + + + + + minss + + aso rexr rexx rexb + ssef3 0f 5d + V W + + + + + monitor + + 0f 01 /reg=1 /mod=11 /rm=0 + + + + + montmul + + 0f a6 /mod=11 /rm=0 /reg=0 + + + + + mov + + aso rexw rexr rexx rexb + c6 /reg=0 + Eb Ib + + + aso oso rexw rexr rexx rexb + c7 /reg=0 + Ev Iz + + + aso rexr rexx rexb + 88 + Eb Gb + + + aso oso rexw rexr rexx rexb + 89 + Ev Gv + + + aso rexr rexx rexb + 8a + Gb Eb + + + aso oso rexw rexr rexx rexb + 8b + Gv Ev + + + aso oso rexr rexx rexb + 8c + Ev S + + + aso oso rexr rexx rexb + 8e + S Ev + + + a0 + AL Ob + + + aso oso rexw + a1 + rAX Ov + + + a2 + Ob AL + + + aso oso rexw + a3 + Ov rAX + + + rexb + b0 + ALr8b Ib + + + rexb + b1 + CLr9b Ib + + + rexb + b2 + DLr10b Ib + + + rexb + b3 + BLr11b Ib + + + rexb + b4 + AHr12b Ib + + + rexb + b5 + CHr13b Ib + + + rexb + b6 + DHr14b Ib + + + rexb + b7 + BHr15b Ib + + + oso rexw rexb + b8 + rAXr8 Iv + + + oso rexw rexb + b9 + rCXr9 Iv + + + oso rexw rexb + ba + rDXr10 Iv + + + oso rexw rexb + bb + rBXr11 Iv + + + oso rexw rexb + bc + rSPr12 Iv + + + oso rexw rexb + bd + rBPr13 Iv + + + oso rexw rexb + be + rSIr14 Iv + + + oso rexw rexb + bf + rDIr15 Iv + + + rexr + 0f 20 + R C + + + rexr + 0f 21 + R D + + + rexr + 0f 22 + C R + + + rexr + 0f 23 + D R + + + + + movapd + + aso rexr rexx rexb + sse66 0f 28 + V W + + + aso rexr rexx rexb + sse66 0f 29 + W V + + + + + movaps + + aso rexr rexx rexb + 0f 28 + V W + + + aso rexr rexx rexb + 0f 29 + W V + + + + + movd + + aso rexw rexr rexx rexb + sse66 0f 6e + V Ex + + + aso rexr rexx rexb + 0f 6e + P Ex + + + aso rexw rexr rexx rexb + sse66 0f 7e + Ex V + + + aso rexr rexx rexb + 0f 7e + Ex P + + + + + movhpd + + aso rexr rexx rexb + sse66 0f 16 /mod=!11 + V M + + + aso rexr rexx rexb + sse66 0f 17 + M V + + + + + movhps + + aso rexr rexx rexb + 0f 16 /mod=!11 + V M + + + aso rexr rexx rexb + 0f 17 + M V + + + + + movlhps + + aso rexr rexx rexb + 0f 16 /mod=11 + V VR + + + + + movlpd + + aso rexr rexx rexb + sse66 0f 12 /mod=!11 + V M + + + aso rexr rexx rexb + sse66 0f 13 + M V + + + + + movlps + + aso rexr rexx rexb + 0f 12 /mod=!11 + V M + + + aso rexr rexx rexb + 0f 13 + M V + + + + + movhlps + + aso rexr rexx rexb + 0f 12 /mod=11 + V VR + + + + + movmskpd + + oso rexr rexb + sse66 0f 50 + Gd VR + + + + + movmskps + + oso rexr rexb + 0f 50 + Gd VR + + + + + movntdq + + aso rexr rexx rexb + sse66 0f e7 + M V + + + + + movnti + + aso rexw rexr rexx rexb + 0f c3 + M Gy + + + + + movntpd + + aso rexr rexx rexb + sse66 0f 2b + M V + + + + + movntps + + aso rexr rexx rexb + 0f 2b + M V + + + + + movntq + + 0f e7 + M P + + + + + movq + + aso rexr rexx rexb + 0f 6f + P Q + + + aso rexr rexx rexb + sse66 0f d6 + W V + + + aso rexr rexx rexb + ssef3 0f 7e + V W + + + aso rexr rexx rexb + 0f 7f + Q P + + + + + movsb + + seg + a4 + + + + + movsw + + seg oso rexw + a5 /o=16 + + + + + movsd + + seg oso rexw + a5 /o=32 + + + aso rexr rexx rexb + ssef2 0f 10 + V W + + + aso rexr rexx rexb + ssef2 0f 11 + W V + + + + + movsq + + seg oso rexw + a5 /o=64 + + + + + movss + + aso rexr rexx rexb + ssef3 0f 10 + V W + + + aso rexr rexx rexb + ssef3 0f 11 + W V + + + + + movsx + + aso oso rexw rexr rexx rexb + 0f be + Gv Eb + + + aso oso rexw rexr rexx rexb + 0f bf + Gv Ew + + + + + movupd + + aso rexr rexx rexb + sse66 0f 10 + V W + + + aso rexr rexx rexb + sse66 0f 11 + W V + + + + + movups + + aso rexr rexx rexb + 0f 10 + V W + + + aso rexr rexx rexb + 0f 11 + W V + + + + + movzx + + aso oso rexw rexr rexx rexb + 0f b6 + Gv Eb + + + aso oso rexw rexr rexx rexb + 0f b7 + Gv Ew + + + + + mul + + aso rexw rexr rexx rexb + f6 /reg=4 + Eb + + + aso oso rexw rexr rexx rexb + f7 /reg=4 + Ev + + + + + mulpd + + aso rexr rexx rexb + sse66 0f 59 + V W + + + + + mulps + + aso rexr rexx rexb + 0f 59 + V W + + + + + mulsd + + aso rexr rexx rexb + ssef2 0f 59 + V W + + + + + mulss + + aso rexr rexx rexb + ssef3 0f 59 + V W + + + + + mwait + + 0f 01 /reg=1 /mod=11 /rm=1 + + + + + neg + + aso rexw rexr rexx rexb + f6 /reg=3 + Eb + + + aso oso rexw rexr rexx rexb + f7 /reg=3 + Ev + + + + + nop + + 90 + + + aso rexr rexx rexb + 0f 19 + M + + + aso rexr rexx rexb + 0f 1a + M + + + aso rexr rexx rexb + 0f 1b + M + + + aso rexr rexx rexb + 0f 1c + M + + + aso rexr rexx rexb + 0f 1d + M + + + aso rexr rexx rexb + 0f 1e + M + + + aso rexr rexx rexb + 0f 1f + M + + + + + not + + aso rexw rexr rexx rexb + f6 /reg=2 + Eb + + + aso oso rexw rexr rexx rexb + f7 /reg=2 + Ev + + + + + or + + aso rexr rexx rexb + 08 + Eb Gb + + + aso oso rexw rexr rexx rexb + 09 + Ev Gv + + + aso rexr rexx rexb + 0a + Gb Eb + + + aso oso rexw rexr rexx rexb + 0b + Gv Ev + + + 0c + AL Ib + + + oso rexw + 0d + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=1 + Eb Ib + + + aso oso rexw rexr rexx rexb + 81 /reg=1 + Ev Iz + sext + + + aso rexr rexx rexb + 82 /reg=1 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 83 /reg=1 + Ev Ib + sext + + + + + orpd + + aso rexr rexx rexb + sse66 0f 56 + V W + + + + + orps + + aso rexr rexx rexb + 0f 56 + V W + + + + + out + + e6 + Ib AL + + + oso + e7 + Ib eAX + + + ee + DX AL + + + oso + ef + DX eAX + + + + + outsb + + 6e + + + + + outsw + + oso + 6f /o=16 + + + + + outsd + + oso + 6f /o=32 + + + + + outsq + + oso + 6f /o=64 + + + + + packsswb + + aso rexr rexx rexb + sse66 0f 63 + V W + + + aso rexr rexx rexb + 0f 63 + P Q + + + + + packssdw + + aso rexr rexx rexb + sse66 0f 6b + V W + + + aso rexr rexx rexb + 0f 6b + P Q + + + + + packuswb + + aso rexr rexx rexb + sse66 0f 67 + V W + + + aso rexr rexx rexb + 0f 67 + P Q + + + + + paddb + + aso rexr rexx rexb + sse66 0f fc + V W + + + aso rexr rexx rexb + 0f fc + P Q + + + + + paddw + + aso rexr rexx rexb + 0f fd + P Q + + + aso rexr rexx rexb + sse66 0f fd + V W + + + + + paddd + + aso rexr rexx rexb + 0f fe + P Q + + + aso rexr rexx rexb + sse66 0f fe + V W + + + + + + paddsb + + aso rexr rexx rexb + 0f ec + P Q + + + aso rexr rexx rexb + sse66 0f ec + V W + + + + + paddsw + + aso rexr rexx rexb + 0f ed + P Q + + + aso rexr rexx rexb + sse66 0f ed + V W + + + + + paddusb + + aso rexr rexx rexb + 0f dc + P Q + + + aso rexr rexx rexb + sse66 0f dc + V W + + + + + paddusw + + aso rexr rexx rexb + 0f dd + P Q + + + aso rexr rexx rexb + sse66 0f dd + V W + + + + + pand + + aso rexr rexx rexb + sse66 0f db + V W + + + aso rexr rexx rexb + 0f db + P Q + + + + + pandn + + aso rexr rexx rexb + sse66 0f df + V W + + + aso rexr rexx rexb + 0f df + P Q + + + + + pavgb + + aso rexr rexx rexb + sse66 0f e0 + V W + + + aso rexr rexx rexb + 0f e0 + P Q + + + + + pavgw + + aso rexr rexx rexb + sse66 0f e3 + V W + + + aso rexr rexx rexb + 0f e3 + P Q + + + + + pcmpeqb + + aso rexr rexx rexb + 0f 74 + P Q + + + aso rexr rexx rexb + sse66 0f 74 + V W + + + + + pcmpeqw + + aso rexr rexx rexb + 0f 75 + P Q + + + aso rexr rexx rexb + sse66 0f 75 + V W + + + + + pcmpeqd + + aso rexr rexx rexb + 0f 76 + P Q + + + aso rexr rexx rexb + sse66 0f 76 + V W + + + + + pcmpgtb + + aso rexr rexx rexb + sse66 0f 64 + V W + + + aso rexr rexx rexb + 0f 64 + P Q + + + + + pcmpgtw + + aso rexr rexx rexb + sse66 0f 65 + V W + + + aso rexr rexx rexb + 0f 65 + P Q + + + + + pcmpgtd + + aso rexr rexx rexb + sse66 0f 66 + V W + + + aso rexr rexx rexb + 0f 66 + P Q + + + + + pextrb + + aso rexr rexb + sse66 0f 3a 14 + MbRv V Ib + def64 + + + + + pextrd + + aso rexr rexw rexb + sse66 0f 3a 16 /o=16 + Ev V Ib + + + aso rexr rexw rexb + sse66 0f 3a 16 /o=32 + Ev V Ib + + + + + pextrq + + aso rexr rexw rexb + sse66 0f 3a 16 /o=64 + Ev V Ib + def64 + + + + + pextrw + + aso rexr rexb + sse66 0f c5 + Gd VR Ib + + + aso oso rexw rexr rexx rexb + 0f c5 + Gd PR Ib + + + + + pinsrw + + aso oso rexw rexr rexx rexb + 0f c4 + P Ew Ib + + + aso rexw rexr rexx rexb + sse66 0f c4 + V Ew Ib + + + + + pmaddwd + + aso rexr rexx rexb + 0f f5 + P Q + + + aso rexr rexx rexb + sse66 0f f5 + V W + + + + + pmaxsw + + aso rexr rexx rexb + sse66 0f ee + V W + + + aso rexr rexx rexb + 0f ee + P Q + + + + + pmaxub + + aso rexr rexx rexb + 0f de + P Q + + + aso rexr rexx rexb + sse66 0f de + V W + + + + + pminsw + + aso rexr rexx rexb + sse66 0f ea + V W + + + aso rexr rexx rexb + 0f ea + P Q + + + + + pminub + + aso rexr rexx rexb + sse66 0f da + V W + + + aso rexr rexx rexb + 0f da + P Q + + + + + pmovmskb + + rexr rexb + sse66 0f d7 + Gd VR + + + oso rexr rexb + 0f d7 + Gd PR + + + + + pmulhuw + + aso rexr rexx rexb + 0f e4 + P Q + + + aso rexr rexx rexb + sse66 0f e4 + V W + + + + + pmulhw + + aso rexr rexx rexb + sse66 0f e5 + V W + + + aso rexr rexx rexb + 0f e5 + P Q + + + + + pmullw + + aso rexr rexx rexb + 0f d5 + P Q + + + aso rexr rexx rexb + sse66 0f d5 + V W + + + + + pop + + 07 + ES + inv64 + + + 17 + SS + inv64 + + + 1f + DS + inv64 + + + 0f a9 + GS + + + 0f a1 + FS + + + oso rexb + 58 + rAXr8 + def64 depM + + + oso rexb + 59 + rCXr9 + def64 depM + + + oso rexb + 5a + rDXr10 + def64 depM + + + oso rexb + 5b + rBXr11 + def64 depM + + + oso rexb + 5c + rSPr12 + def64 depM + + + oso rexb + 5d + rBPr13 + def64 depM + + + oso rexb + 5e + rSIr14 + def64 depM + + + oso rexb + 5f + rDIr15 + def64 depM + + + aso oso rexw rexr rexx rexb + 8f /reg=0 + Ev + def64 depM + + + + + popa + + oso + 61 /o=16 + inv64 + + + + + popad + + oso + 61 /o=32 + inv64 + + + + + popfw + + oso + 9d /m=32 /o=16 + def64 depM + + + oso + 9d /m=16 /o=16 + def64 depM + + + + + popfd + + oso + 9d /m=16 /o=32 + def64 depM + + + oso + 9d /m=32 /o=32 + def64 depM + + + + + popfq + + oso + 9d /m=64 /o=64 + def64 depM + + + + + por + + aso rexr rexx rexb + sse66 0f eb + V W + + + aso rexr rexx rexb + 0f eb + P Q + + + + + prefetch + + aso rexw rexr rexx rexb + 0f 0d /reg=0 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=1 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=2 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=3 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=4 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=5 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=6 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=7 + M + + + + + prefetchnta + + aso rexw rexr rexx rexb + 0f 18 /reg=0 + M + + + + + prefetcht0 + + aso rexw rexr rexx rexb + 0f 18 /reg=1 + M + + + + + prefetcht1 + + aso rexw rexr rexx rexb + 0f 18 /reg=2 + M + + + + + prefetcht2 + + aso rexw rexr rexx rexb + 0f 18 /reg=3 + M + + + + + psadbw + + aso rexr rexx rexb + sse66 0f f6 + V W + + + aso rexr rexx rexb + 0f f6 + P Q + + + + + pshufw + + aso rexr rexx rexb + 0f 70 + P Q Ib + + + + + psllw + + aso rexr rexx rexb + sse66 0f f1 + V W + + + aso rexr rexx rexb + 0f f1 + P Q + + + rexb + sse66 0f 71 /reg=6 + VR Ib + + + 0f 71 /reg=6 + PR Ib + + + + + pslld + + aso rexr rexx rexb + sse66 0f f2 + V W + + + aso rexr rexx rexb + 0f f2 + P Q + + + rexb + sse66 0f 72 /reg=6 + VR Ib + + + 0f 72 /reg=6 + PR Ib + + + + + psllq + + aso rexr rexx rexb + sse66 0f f3 + V W + + + aso rexr rexx rexb + 0f f3 + P Q + + + rexb + sse66 0f 73 /reg=6 + VR Ib + + + 0f 73 /reg=6 + PR Ib + + + + + psraw + + aso rexr rexx rexb + 0f e1 + P Q + + + aso rexr rexx rexb + sse66 0f e1 + V W + + + rexb + sse66 0f 71 /reg=4 + VR Ib + + + 0f 71 /reg=4 + PR Ib + + + + + psrad + + 0f 72 /reg=4 + PR Ib + + + aso rexr rexx rexb + sse66 0f e2 + V W + + + aso rexr rexx rexb + 0f e2 + P Q + + + rexb + sse66 0f 72 /reg=4 + VR Ib + + + + + psrlw + + 0f 71 /reg=2 + PR Ib + + + aso rexr rexx rexb + 0f d1 + P Q + + + aso rexr rexx rexb + sse66 0f d1 + V W + + + rexb + sse66 0f 71 /reg=2 + VR Ib + + + + + psrld + + 0f 72 /reg=2 + PR Ib + + + aso rexr rexx rexb + 0f d2 + P Q + + + aso rexr rexx rexb + sse66 0f d2 + V W + + + rexb + sse66 0f 72 /reg=2 + VR Ib + + + + + psrlq + + 0f 73 /reg=2 + PR Ib + + + aso rexr rexx rexb + 0f d3 + P Q + + + aso rexr rexx rexb + sse66 0f d3 + V W + + + rexb + sse66 0f 73 /reg=2 + VR Ib + + + + + psubb + + aso rexr rexx rexb + sse66 0f f8 + V W + + + aso rexr rexx rexb + 0f f8 + P Q + + + + + psubw + + aso rexr rexx rexb + sse66 0f f9 + V W + + + aso rexr rexx rexb + 0f f9 + P Q + + + + + psubd + + aso rexr rexx rexb + 0f fa + P Q + + + aso rexr rexx rexb + sse66 0f fa + V W + + + + + psubsb + + aso rexr rexx rexb + 0f e8 + P Q + + + aso rexr rexx rexb + sse66 0f e8 + V W + + + + + psubsw + + aso rexr rexx rexb + 0f e9 + P Q + + + aso rexr rexx rexb + sse66 0f e9 + V W + + + + + psubusb + + aso rexr rexx rexb + 0f d8 + P Q + + + aso rexr rexx rexb + sse66 0f d8 + V W + + + + + psubusw + + aso rexr rexx rexb + 0f d9 + P Q + + + aso rexr rexx rexb + sse66 0f d9 + V W + + + + + punpckhbw + + aso rexr rexx rexb + sse66 0f 68 + V W + + + aso rexr rexx rexb + 0f 68 + P Q + + + + + punpckhwd + + aso rexr rexx rexb + sse66 0f 69 + V W + + + aso rexr rexx rexb + 0f 69 + P Q + + + + + punpckhdq + + aso rexr rexx rexb + sse66 0f 6a + V W + + + aso rexr rexx rexb + 0f 6a + P Q + + + + + punpcklbw + + aso rexr rexx rexb + sse66 0f 60 + V W + + + aso rexr rexx rexb + 0f 60 + P Q + + + + + punpcklwd + + aso rexr rexx rexb + sse66 0f 61 + V W + + + aso rexr rexx rexb + 0f 61 + P Q + + + + + punpckldq + + aso rexr rexx rexb + sse66 0f 62 + V W + + + aso rexr rexx rexb + 0f 62 + P Q + + + + + pi2fw + + 0f 0f /3dnow=0c + P Q + + + + + pi2fd + + 0f 0f /3dnow=0d + P Q + + + + + pf2iw + + 0f 0f /3dnow=1c + P Q + + + + + pf2id + + 0f 0f /3dnow=1d + P Q + + + + + pfnacc + + 0f 0f /3dnow=8a + P Q + + + + + pfpnacc + + 0f 0f /3dnow=8e + P Q + + + + + pfcmpge + + 0f 0f /3dnow=90 + P Q + + + + + pfmin + + 0f 0f /3dnow=94 + P Q + + + + + pfrcp + + 0f 0f /3dnow=96 + P Q + + + + + pfrsqrt + + 0f 0f /3dnow=97 + P Q + + + + + pfsub + + 0f 0f /3dnow=9a + P Q + + + + + pfadd + + 0f 0f /3dnow=9e + P Q + + + + + pfcmpgt + + 0f 0f /3dnow=a0 + P Q + + + + + pfmax + + 0f 0f /3dnow=a4 + P Q + + + + + pfrcpit1 + + 0f 0f /3dnow=a6 + P Q + + + + + pfrsqit1 + + 0f 0f /3dnow=a7 + P Q + + + + + pfsubr + + 0f 0f /3dnow=aa + P Q + + + + + pfacc + + 0f 0f /3dnow=ae + P Q + + + + + pfcmpeq + + 0f 0f /3dnow=b0 + P Q + + + + + pfmul + + 0f 0f /3dnow=b4 + P Q + + + + + pfrcpit2 + + 0f 0f /3dnow=b6 + P Q + + + + + pmulhrw + + 0f 0f /3dnow=b7 + P Q + + + + + pswapd + + 0f 0f /3dnow=bb + P Q + + + + + pavgusb + + 0f 0f /3dnow=bf + P Q + + + + + push + + 06 + ES + inv64 + + + 0e + CS + inv64 + + + 16 + SS + inv64 + + + 1e + DS + inv64 + + + 0f a8 + GS + + + 0f a0 + FS + + + oso rexb + 50 + rAXr8 + def64 depM + + + oso rexb + 51 + rCXr9 + def64 depM + + + oso rexb + 52 + rDXr10 + def64 depM + + + oso rexb + 53 + rBXr11 + def64 depM + + + oso rexb + 54 + rSPr12 + def64 depM + + + oso rexb + 55 + rBPr13 + def64 depM + + + oso rexb + 56 + rSIr14 + def64 depM + + + oso rexb + 57 + rDIr15 + def64 depM + + + oso + 68 + Iz + cast + + + aso oso rexw rexr rexx rexb + ff /reg=6 + Ev + def64 + + + 6a + Ib + sext + + + + + pusha + + oso + 60 /o=16 + inv64 + + + + + pushad + + oso + 60 /o=32 + inv64 + + + + + pushfw + + oso + 9c /m=32 /o=16 + def64 + + + oso + 9c /m=16 /o=16 + def64 + + + oso rexw + 9c /m=64 /o=16 + def64 + + + + + pushfd + + oso + 9c /m=16 /o=32 + def64 + + + oso + 9c /m=32 /o=32 + def64 + + + + + pushfq + + oso rexw + 9c /m=64 /o=32 + def64 + + + oso rexw + 9c /m=64 /o=64 + def64 + + + + + pxor + + aso rexr rexx rexb + sse66 0f ef + V W + + + aso rexr rexx rexb + 0f ef + P Q + + + + + rcl + + aso rexw rexr rexx rexb + c0 /reg=2 + Eb Ib + + + aso oso rexw rexr rexx rexb + c1 /reg=2 + Ev Ib + + + aso rexw rexr rexx rexb + d0 /reg=2 + Eb I1 + + + aso rexw rexr rexx rexb + d2 /reg=2 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=2 + Ev CL + cast + + + aso oso rexw rexr rexx rexb + d1 /reg=2 + Ev I1 + + + + + rcr + + aso rexw rexr rexx rexb + d0 /reg=3 + Eb I1 + + + aso oso rexw rexr rexx rexb + c1 /reg=3 + Ev Ib + + + aso rexw rexr rexx rexb + c0 /reg=3 + Eb Ib + + + aso oso rexw rexr rexx rexb + d1 /reg=3 + Ev I1 + + + aso rexw rexr rexx rexb + d2 /reg=3 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=3 + Ev CL + cast + + + + + rol + + aso rexw rexr rexx rexb + c0 /reg=0 + Eb Ib + + + aso rexw rexr rexx rexb + d0 /reg=0 + Eb I1 + + + aso oso rexw rexr rexx rexb + d1 /reg=0 + Ev I1 + + + aso rexw rexr rexx rexb + d2 /reg=0 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=0 + Ev CL + cast + + + aso oso rexw rexr rexx rexb + c1 /reg=0 + Ev Ib + + + + + ror + + aso rexw rexr rexx rexb + d0 /reg=1 + Eb I1 + + + aso rexw rexr rexx rexb + c0 /reg=1 + Eb Ib + + + aso oso rexw rexr rexx rexb + c1 /reg=1 + Ev Ib + + + aso oso rexw rexr rexx rexb + d1 /reg=1 + Ev I1 + + + aso rexw rexr rexx rexb + d2 /reg=1 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=1 + Ev CL + cast + + + + + rcpps + + aso rexr rexx rexb + 0f 53 + V W + + + + + rcpss + + aso rexr rexx rexb + ssef3 0f 53 + V W + + + + + rdmsr + + 0f 32 + + + + + rdpmc + + 0f 33 + + + + + rdtsc + + 0f 31 + + + + + rdtscp + amd + + 0f 01 /reg=7 /mod=11 /rm=1 + + + + + repne + + f2 + + + + + rep + + f3 + + + + + ret + + c2 + Iw + + + c3 + + + + + retf + + ca + Iw + + + cb + + + + + rsm + + 0f aa + + + + + rsqrtps + + aso rexr rexx rexb + 0f 52 + V W + + + + + rsqrtss + + aso rexr rexx rexb + ssef3 0f 52 + V W + + + + + sahf + + 9e + + + + + sal + + + + salc + + d6 + inv64 + + + + + sar + + aso oso rexw rexr rexx rexb + d1 /reg=7 + Ev I1 + + + aso rexw rexr rexx rexb + c0 /reg=7 + Eb Ib + + + aso rexw rexr rexx rexb + d0 /reg=7 + Eb I1 + + + aso oso rexw rexr rexx rexb + c1 /reg=7 + Ev Ib + + + aso rexw rexr rexx rexb + d2 /reg=7 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=7 + Ev CL + cast + + + + + shl + + aso rexw rexr rexx rexb + c0 /reg=6 + Eb Ib + + + aso oso rexw rexr rexx rexb + c1 /reg=6 + Ev Ib + + + aso rexw rexr rexx rexb + d0 /reg=6 + Eb I1 + + + aso rexw rexr rexx rexb + d2 /reg=6 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=6 + Ev CL + cast + + + aso oso rexw rexr rexx rexb + c1 /reg=4 + Ev Ib + + + aso rexr rexx rexb + d2 /reg=4 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d1 /reg=4 + Ev I1 + + + aso rexw rexr rexx rexb + d0 /reg=4 + Eb I1 + + + aso rexw rexr rexx rexb + c0 /reg=4 + Eb Ib + + + aso oso rexw rexr rexx rexb + d3 /reg=4 + Ev CL + + + aso oso rexw rexr rexx rexb + d1 /reg=6 + Ev I1 + + + + + shr + + aso oso rexw rexr rexx rexb + c1 /reg=5 + Ev Ib + + + aso rexw rexr rexx rexb + d2 /reg=5 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d1 /reg=5 + Ev I1 + + + aso rexw rexr rexx rexb + d0 /reg=5 + Eb I1 + + + aso rexw rexr rexx rexb + c0 /reg=5 + Eb Ib + + + aso oso rexw rexr rexx rexb + d3 /reg=5 + Ev CL + cast + + + + + sbb + + aso rexr rexx rexb + 18 + Eb Gb + + + aso oso rexw rexr rexx rexb + 19 + Ev Gv + + + aso rexr rexx rexb + 1a + Gb Eb + + + aso oso rexw rexr rexx rexb + 1b + Gv Ev + + + 1c + AL Ib + + + oso rexw + 1d + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=3 + Eb Ib + + + aso oso rexw rexr rexx rexb + 81 /reg=3 + Ev Iz + sext + + + aso rexr rexx rexb + 82 /reg=3 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 83 /reg=3 + Ev Ib + sext + + + + + scasb + + ae + + + + + scasw + + oso rexw + af /o=16 + + + + + scasd + + oso rexw + af /o=32 + + + + + scasq + + oso rexw + af /o=64 + + + + + seto + + aso rexr rexx rexb + 0f 90 + Eb + + + + + setno + + aso rexr rexx rexb + 0f 91 + Eb + + + + + setb + + aso rexr rexx rexb + 0f 92 + Eb + + + + + setnb + + aso rexr rexx rexb + 0f 93 + Eb + + + + + setz + + aso rexr rexx rexb + 0f 94 + Eb + + + + + setnz + + aso rexr rexx rexb + 0f 95 + Eb + + + + + setbe + + aso rexr rexx rexb + 0f 96 + Eb + + + + + seta + + aso rexr rexx rexb + 0f 97 + Eb + + + + + sets + + aso rexr rexx rexb + 0f 98 + Eb + + + + + setns + + aso rexr rexx rexb + 0f 99 + Eb + + + + + setp + + aso rexr rexx rexb + 0f 9a + Eb + + + + + setnp + + aso rexr rexx rexb + 0f 9b + Eb + + + + + setl + + aso rexr rexx rexb + 0f 9c + Eb + + + + + setge + + aso rexr rexx rexb + 0f 9d + Eb + + + + + setle + + aso rexr rexx rexb + 0f 9e + Eb + + + + + setg + + aso rexr rexx rexb + 0f 9f + Eb + + + + + sfence + + 0f ae /reg=7 /mod=11 /rm=0 + + + 0f ae /reg=7 /mod=11 /rm=1 + + + 0f ae /reg=7 /mod=11 /rm=2 + + + 0f ae /reg=7 /mod=11 /rm=3 + + + 0f ae /reg=7 /mod=11 /rm=4 + + + 0f ae /reg=7 /mod=11 /rm=5 + + + 0f ae /reg=7 /mod=11 /rm=6 + + + 0f ae /reg=7 /mod=11 /rm=7 + + + + + sgdt + + aso rexr rexx rexb + 0f 01 /reg=0 /mod=!11 + M + + + + + shld + + aso oso rexw rexr rexx rexb + 0f a4 + Ev Gv Ib + + + aso oso rexw rexr rexx rexb + 0f a5 + Ev Gv CL + + + + + shrd + + aso oso rexw rexr rexx rexb + 0f ac + Ev Gv Ib + + + aso oso rexw rexr rexx rexb + 0f ad + Ev Gv CL + + + + + shufpd + + aso rexr rexx rexb + sse66 0f c6 + V W Ib + + + + + shufps + + aso rexr rexx rexb + 0f c6 + V W Ib + + + + + sidt + + aso rexr rexx rexb + 0f 01 /reg=1 /mod=!11 + M + + + + + sldt + + aso oso rexr rexx rexb + 0f 00 /reg=0 + MwRv + + + + + smsw + + aso rexr rexx rexb + 0f 01 /reg=4 /mod=!11 + M + + + + + sqrtps + + aso rexr rexx rexb + 0f 51 + V W + + + + + sqrtpd + + aso rexr rexx rexb + sse66 0f 51 + V W + + + + + sqrtsd + + aso rexr rexx rexb + ssef2 0f 51 + V W + + + + + sqrtss + + aso rexr rexx rexb + ssef3 0f 51 + V W + + + + + stc + + f9 + + + + + std + + fd + + + + + stgi + amd + + 0f 01 /reg=3 /mod=11 /rm=4 + + + + + sti + + fb + + + + + skinit + amd + + 0f 01 /reg=3 /mod=11 /rm=6 + + + + + stmxcsr + + aso rexw rexr rexx rexb + 0f ae /mod=11 /reg=3 + Md + + + + + stosb + + seg + aa + + + + + stosw + + seg oso rexw + ab /o=16 + + + + + stosd + + seg oso rexw + ab /o=32 + + + + + stosq + + seg oso rexw + ab /o=64 + + + + + str + + aso oso rexr rexx rexb + 0f 00 /reg=1 + Ev + + + + + sub + + aso rexr rexx rexb + 28 + Eb Gb + + + aso oso rexw rexr rexx rexb + 29 + Ev Gv + + + aso rexr rexx rexb + 2a + Gb Eb + + + aso oso rexw rexr rexx rexb + 2b + Gv Ev + + + 2c + AL Ib + + + oso rexw + 2d + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=5 + Eb Ib + + + aso oso rexw rexr rexx rexb + 81 /reg=5 + Ev Iz + sext + + + aso rexr rexx rexb + 82 /reg=5 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 83 /reg=5 + Ev Ib + sext + + + + + subpd + + aso rexr rexx rexb + sse66 0f 5c + V W + + + + + subps + + aso rexr rexx rexb + 0f 5c + V W + + + + + subsd + + aso rexr rexx rexb + ssef2 0f 5c + V W + + + + + subss + + aso rexr rexx rexb + ssef3 0f 5c + V W + + + + + swapgs + + 0f 01 /reg=7 /mod=11 /rm=0 + + + + + syscall + + 0f 05 + + + + + sysenter + + 0f 34 + inv64 + + + + + sysexit + + 0f 35 + + + + + sysret + + 0f 07 + + + + + test + + aso rexw rexr rexx rexb + f6 /reg=0 + Eb Ib + + + aso rexr rexx rexb + 84 + Eb Gb + + + aso oso rexw rexr rexx rexb + 85 + Ev Gv + + + a8 + AL Ib + + + oso rexw + a9 + rAX Iz + sext + + + aso rexw rexr rexx rexb + f6 /reg=1 + Eb Ib + + + aso oso rexw rexr rexx rexb + f7 /reg=0 + Ev Iz + sext + + + aso oso rexw rexr rexx rexb + f7 /reg=1 + Ev Iz + sext + + + + + ucomisd + + aso rexr rexx rexb + sse66 0f 2e + V W + + + + + ucomiss + + aso rexr rexx rexb + 0f 2e + V W + + + + + ud2 + + 0f 0b + + + + + unpckhpd + + aso rexr rexx rexb + sse66 0f 15 + V W + + + + + unpckhps + + aso rexr rexx rexb + 0f 15 + V W + + + + + unpcklps + + aso rexr rexx rexb + 0f 14 + V W + + + + + unpcklpd + + aso rexr rexx rexb + sse66 0f 14 + V W + + + + + verr + + aso rexr rexx rexb + 0f 00 /reg=4 + Ew + + + + + verw + + aso rexr rexx rexb + 0f 00 /reg=5 + Ew + + + + + vmcall + intel + + 0f 01 /reg=0 /mod=11 /rm=1 + + + + + vmclear + intel + + aso rexr rexx rexb + sse66 0f c7 /reg=6 + Mq + + + + + vmxon + intel + + aso rexr rexx rexb + ssef3 0f c7 /reg=6 + Mq + + + + + vmptrld + intel + + aso rexr rexx rexb + 0f c7 /reg=6 + Mq + + + + + vmptrst + intel + + aso rexr rexx rexb + 0f c7 /reg=7 + Mq + + + + + vmlaunch + intel + + 0f 01 /reg=0 /mod=11 /rm=2 + + + + + vmresume + intel + + 0f 01 /reg=0 /mod=11 /rm=3 + + + + + vmxoff + intel + + 0f 01 /reg=0 /mod=11 /rm=4 + + + + + vmread + intel + + aso rexr rexx rexb + 0f 78 /m=16 + Ed Gd + def64 + + + aso rexr rexx rexb + 0f 78 /m=32 + Ed Gd + def64 + + + aso rexr rexx rexb + 0f 78 /m=64 + Eq Gq + def64 + + + + + vmwrite + intel + + aso rexr rexx rexb + 0f 79 /m=16 + Gd Ed + def64 + + + aso rexr rexx rexb + 0f 79 /m=32 + Gd Ed + def64 + + + aso rexr rexx rexb + 0f 79 /m=64 + Gq Eq + def64 + + + + + vmrun + amd + + 0f 01 /reg=3 /mod=11 /rm=0 + + + + + vmmcall + amd + + 0f 01 /reg=3 /mod=11 /rm=1 + + + + + vmload + amd + + 0f 01 /reg=3 /mod=11 /rm=2 + + + + + vmsave + amd + + 0f 01 /reg=3 /mod=11 /rm=3 + + + + + wait + + 9b + + + + + wbinvd + + 0f 09 + + + + + wrmsr + + 0f 30 + + + + + xadd + + aso oso rexr rexx rexb + 0f c0 + Eb Gb + + + aso oso rexw rexr rexx rexb + 0f c1 + Ev Gv + + + + + xchg + + aso rexr rexx rexb + 86 + Eb Gb + + + aso oso rexw rexr rexx rexb + 87 + Ev Gv + + + oso rexw rexb + 90 + rAXr8 rAX + + + oso rexw rexb + 91 + rCXr9 rAX + + + oso rexw rexb + 92 + rDXr10 rAX + + + oso rexw rexb + 93 + rBXr11 rAX + + + oso rexw rexb + 94 + rSPr12 rAX + + + oso rexw rexb + 95 + rBPr13 rAX + + + oso rexw rexb + 96 + rSIr14 rAX + + + oso rexw rexb + 97 + rDIr15 rAX + + + + + xlatb + + rexw + d7 + + + + + xor + + aso rexr rexx rexb + 30 + Eb Gb + + + aso oso rexw rexr rexx rexb + 31 + Ev Gv + + + aso rexr rexx rexb + 32 + Gb Eb + + + aso oso rexw rexr rexx rexb + 33 + Gv Ev + + + 34 + AL Ib + + + oso rexw + 35 + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=6 + Eb Ib + + + aso oso rexw rexr rexx rexb + 81 /reg=6 + Ev Iz + sext + + + aso rexr rexx rexb + 82 /reg=6 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 83 /reg=6 + Ev Ib + sext + + + + + xorpd + + aso rexr rexx rexb + sse66 0f 57 + V W + + + + + xorps + + aso rexr rexx rexb + 0f 57 + V W + + + + + xcryptecb + + 0f a7 /mod=11 /rm=0 /reg=1 + + + + + xcryptcbc + + 0f a7 /mod=11 /rm=0 /reg=2 + + + + + xcryptctr + + 0f a7 /mod=11 /rm=0 /reg=3 + + + + + xcryptcfb + + 0f a7 /mod=11 /rm=0 /reg=4 + + + + + xcryptofb + + 0f a7 /mod=11 /rm=0 /reg=5 + + + + + xsha1 + + 0f a6 /mod=11 /rm=0 /reg=1 + + + + + xsha256 + + 0f a6 /mod=11 /rm=0 /reg=2 + + + + + xstore + + 0f a7 /mod=11 /rm=0 /reg=0 + + + + + db + + + + + + movdqa + + aso rexr rexx rexb + sse66 0f 7f + W V + + + aso rexr rexx rexb + sse66 0f 6f + V W + + + + + movdq2q + + aso rexb + ssef2 0f d6 + P VR + + + + + movdqu + + aso rexr rexx rexb + ssef3 0f 6f + V W + + + aso rexr rexx rexb + ssef3 0f 7f + W V + + + + + movq2dq + + aso + ssef3 0f d6 + V PR + + + + + paddq + + aso rexr rexx rexb + 0f d4 + P Q + + + aso rexr rexx rexb + sse66 0f d4 + V W + + + + + psubq + + aso rexr rexx rexb + sse66 0f fb + V W + + + aso rexr rexx rexb + 0f fb + P Q + + + + + pmuludq + + aso rexr rexx rexb + 0f f4 + P Q + + + aso rexr rexx rexb + sse66 0f f4 + V W + + + + + pshufhw + + aso rexr rexx rexb + ssef3 0f 70 + V W Ib + + + + + pshuflw + + aso rexr rexx rexb + ssef2 0f 70 + V W Ib + + + + + pshufd + + aso rexr rexx rexb + sse66 0f 70 + V W Ib + + + + + pslldq + + rexb + sse66 0f 73 /reg=7 + VR Ib + + + + + psrldq + + rexb + sse66 0f 73 /reg=3 + VR Ib + + + + + punpckhqdq + + aso rexr rexx rexb + sse66 0f 6d + V W + + + + + punpcklqdq + + aso rexr rexx rexb + sse66 0f 6c + V W + + + + + + + addsubpd + + aso rexr rexx rexb + sse66 0f d0 + V W + + + + + addsubps + + aso rexr rexx rexb + ssef2 0f d0 + V W + + + + + haddpd + + aso rexr rexx rexb + sse66 0f 7c + V W + + + + + haddps + + aso rexr rexx rexb + ssef2 0f 7c + V W + + + + + hsubpd + + aso rexr rexx rexb + sse66 0f 7d + V W + + + + + hsubps + + aso rexr rexx rexb + ssef2 0f 7d + V W + + + + + movddup + + aso rexr rexx rexb + ssef2 0f 12 /mod=11 + V W + + + aso rexr rexx rexb + ssef2 0f 12 /mod=!11 + V W + + + + + movshdup + + aso rexr rexx rexb + ssef3 0f 16 /mod=11 + V W + + + aso rexr rexx rexb + ssef3 0f 16 /mod=!11 + V W + + + + + movsldup + + aso rexr rexx rexb + ssef3 0f 12 /mod=11 + V W + + + aso rexr rexx rexb + ssef3 0f 12 /mod=!11 + V W + + + + + + + pabsb + + aso rexr rexx rexb + 0f 38 1c + P Q + + + aso rexr rexx rexb + sse66 0f 38 1c + V W + + + + + pabsw + + aso rexr rexx rexb + 0f 38 1d + P Q + + + aso rexr rexx rexb + sse66 0f 38 1d + V W + + + + + pabsd + + aso rexr rexx rexb + 0f 38 1e + P Q + + + aso rexr rexx rexb + sse66 0f 38 1e + V W + + + + + psignb + + aso rexr rexx rexb + 0f 38 00 + P Q + + + aso rexr rexx rexb + sse66 0f 38 00 + V W + + + + + phaddw + + aso rexr rexx rexb + 0f 38 01 + P Q + + + aso rexr rexx rexb + sse66 0f 38 01 + V W + + + + + phaddd + + aso rexr rexx rexb + 0f 38 02 + P Q + + + aso rexr rexx rexb + sse66 0f 38 02 + V W + + + + + phaddsw + + aso rexr rexx rexb + 0f 38 03 + P Q + + + aso rexr rexx rexb + sse66 0f 38 03 + V W + + + + + pmaddubsw + + aso rexr rexx rexb + 0f 38 04 + P Q + + + aso rexr rexx rexb + sse66 0f 38 04 + V W + + + + + phsubw + + aso rexr rexx rexb + 0f 38 05 + P Q + + + aso rexr rexx rexb + sse66 0f 38 05 + V W + + + + + phsubd + + aso rexr rexx rexb + 0f 38 06 + P Q + + + aso rexr rexx rexb + sse66 0f 38 06 + V W + + + + + phsubsw + + aso rexr rexx rexb + 0f 38 07 + P Q + + + aso rexr rexx rexb + sse66 0f 38 07 + V W + + + + + psignb + + aso rexr rexx rexb + 0f 38 08 + P Q + + + aso rexr rexx rexb + sse66 0f 38 08 + V W + + + + + psignd + + aso rexr rexx rexb + 0f 38 0a + P Q + + + aso rexr rexx rexb + sse66 0f 38 0a + V W + + + + + psignw + + aso rexr rexx rexb + 0f 38 09 + P Q + + + aso rexr rexx rexb + sse66 0f 38 09 + V W + + + + + pmulhrsw + + aso rexr rexx rexb + 0f 38 0b + P Q + + + aso rexr rexx rexb + sse66 0f 38 0b + V W + + + + + palignr + + aso rexr rexx rexb + 0f 3a 0f + P Q Ib + + + aso rexr rexx rexb + sse66 0f 3a 0f + V W Ib + + + + + + + pblendvb + + aso rexr rexx rexb + sse66 0f 38 10 + V W + + + + + pmuldq + + aso rexr rexx rexb + sse66 0f 38 28 + V W + + + + + pminsb + + aso rexr rexx rexb + sse66 0f 38 38 + V W + + + + + pminsd + + aso rexr rexx rexb + sse66 0f 38 39 + V W + + + + + pminuw + + aso rexr rexx rexb + sse66 0f 38 3a + V W + + + + + pminud + + aso rexr rexx rexb + sse66 0f 38 3b + V W + + + + + pmaxsb + + aso rexr rexx rexb + sse66 0f 38 3c + V W + + + + + pmaxsd + + aso rexr rexx rexb + sse66 0f 38 3d + V W + + + + + pmaxud + + aso rexr rexx rexb + sse66 0f 38 3f + V W + + + + + pmulld + + aso rexr rexx rexb + sse66 0f 38 40 + V W + + + + + phminposuw + + aso rexr rexx rexb + sse66 0f 38 41 + V W + + + + + roundps + + aso rexr rexx rexb + sse66 0f 3a 08 + V W Ib + + + + + roundpd + + aso rexr rexx rexb + sse66 0f 3a 09 + V W Ib + + + + + roundss + + aso rexr rexx rexb + sse66 0f 3a 0a + V W Ib + + + + + roundsd + + aso rexr rexx rexb + sse66 0f 3a 0b + V W Ib + + + + + blendpd + + aso rexr rexx rexb + sse66 0f 3a 0d + V W Ib + + + + + pblendw + + aso rexr rexx rexb + sse66 0f 3a 0e + V W Ib + + + + + blendps + + aso rexr rexx rexb + sse66 0f 3a 0c + V W Ib + + + + + blendvpd + + aso rexr rexx rexb + sse66 0f 38 15 + V W + + + + + blendvps + + aso rexr rexx rexb + sse66 0f 38 14 + V W + + + + + dpps + + aso rexr rexx rexb + sse66 0f 3a 40 + V W Ib + + + + + dppd + + aso rexr rexx rexb + sse66 0f 3a 41 + V W Ib + + + + + mpsadbw + + aso rexr rexx rexb + sse66 0f 3a 42 + V W Ib + + + + + extractps + + aso rexr rexw rexb + sse66 0f 3a 17 + MdRy V Ib + + + + + invalid + + + diff --git a/3rdparty/masm/disassembler/udis86/ud_opcode.py b/3rdparty/masm/disassembler/udis86/ud_opcode.py new file mode 100644 index 0000000000..f301b52461 --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/ud_opcode.py @@ -0,0 +1,235 @@ +# udis86 - scripts/ud_opcode.py +# +# Copyright (c) 2009 Vivek Thampi +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +class UdOpcodeTables: + + TableInfo = { + 'opctbl' : { 'name' : 'UD_TAB__OPC_TABLE', 'size' : 256 }, + '/sse' : { 'name' : 'UD_TAB__OPC_SSE', 'size' : 4 }, + '/reg' : { 'name' : 'UD_TAB__OPC_REG', 'size' : 8 }, + '/rm' : { 'name' : 'UD_TAB__OPC_RM', 'size' : 8 }, + '/mod' : { 'name' : 'UD_TAB__OPC_MOD', 'size' : 2 }, + '/m' : { 'name' : 'UD_TAB__OPC_MODE', 'size' : 3 }, + '/x87' : { 'name' : 'UD_TAB__OPC_X87', 'size' : 64 }, + '/a' : { 'name' : 'UD_TAB__OPC_ASIZE', 'size' : 3 }, + '/o' : { 'name' : 'UD_TAB__OPC_OSIZE', 'size' : 3 }, + '/3dnow' : { 'name' : 'UD_TAB__OPC_3DNOW', 'size' : 256 }, + 'vendor' : { 'name' : 'UD_TAB__OPC_VENDOR', 'size' : 3 }, + } + + OpcodeTable0 = { + 'type' : 'opctbl', + 'entries' : {}, + 'meta' : 'table0' + } + + OpcExtIndex = { + + # ssef2, ssef3, sse66 + 'sse': { + 'none' : '00', + 'f2' : '01', + 'f3' : '02', + '66' : '03' + }, + + # /mod= + 'mod': { + '!11' : '00', + '11' : '01' + }, + + # /m=, /o=, /a= + 'mode': { + '16' : '00', + '32' : '01', + '64' : '02' + }, + + 'vendor' : { + 'amd' : '00', + 'intel' : '01', + 'any' : '02' + } + } + + InsnTable = [] + MnemonicsTable = [] + + ThreeDNowTable = {} + + def sizeOfTable( self, t ): + return self.TableInfo[ t ][ 'size' ] + + def nameOfTable( self, t ): + return self.TableInfo[ t ][ 'name' ] + + # + # Updates a table entry: If the entry doesn't exist + # it will create the entry, otherwise, it will walk + # while validating the path. + # + def updateTable( self, table, index, type, meta ): + if not index in table[ 'entries' ]: + table[ 'entries' ][ index ] = { 'type' : type, 'entries' : {}, 'meta' : meta } + if table[ 'entries' ][ index ][ 'type' ] != type: + raise NameError( "error: violation in opcode mapping (overwrite) %s with %s." % + ( table[ 'entries' ][ index ][ 'type' ], type) ) + return table[ 'entries' ][ index ] + + class Insn: + """An abstract type representing an instruction in the opcode map. + """ + + # A mapping of opcode extensions to their representational + # values used in the opcode map. + OpcExtMap = { + '/rm' : lambda v: "%02x" % int(v, 16), + '/x87' : lambda v: "%02x" % int(v, 16), + '/3dnow' : lambda v: "%02x" % int(v, 16), + '/reg' : lambda v: "%02x" % int(v, 16), + # modrm.mod + # (!11, 11) => (00, 01) + '/mod' : lambda v: '00' if v == '!11' else '01', + # Mode extensions: + # (16, 32, 64) => (00, 01, 02) + '/o' : lambda v: "%02x" % (int(v) / 32), + '/a' : lambda v: "%02x" % (int(v) / 32), + '/m' : lambda v: "%02x" % (int(v) / 32), + '/sse' : lambda v: UdOpcodeTables.OpcExtIndex['sse'][v] + } + + def __init__(self, prefixes, mnemonic, opcodes, operands, vendor): + self.opcodes = opcodes + self.prefixes = prefixes + self.mnemonic = mnemonic + self.operands = operands + self.vendor = vendor + self.opcext = {} + + ssePrefix = None + if self.opcodes[0] in ('ssef2', 'ssef3', 'sse66'): + ssePrefix = self.opcodes[0][3:] + self.opcodes.pop(0) + + # do some preliminary decoding of the instruction type + # 1byte, 2byte or 3byte instruction? + self.nByteInsn = 1 + if self.opcodes[0] == '0f': # 2byte + # 2+ byte opcodes are always disambiguated by an + # sse prefix, unless it is a 3d now instruction + # which is 0f 0f ... + if self.opcodes[1] != '0f' and ssePrefix is None: + ssePrefix = 'none' + if self.opcodes[1] in ('38', '3a'): # 3byte + self.nByteInsn = 3 + else: + self.nByteInsn = 2 + + # The opcode that indexes into the opcode table. + self.opcode = self.opcodes[self.nByteInsn - 1] + + # Record opcode extensions + for opcode in self.opcodes[self.nByteInsn:]: + arg, val = opcode.split('=') + self.opcext[arg] = self.OpcExtMap[arg](val) + + # Record sse extension: the reason sse extension is handled + # separately is that historically sse was handled as a first + # class opcode, not as an extension. Now that sse is handled + # as an extension, we do the manual conversion here, as opposed + # to modifying the opcode xml file. + if ssePrefix is not None: + self.opcext['/sse'] = self.OpcExtMap['/sse'](ssePrefix) + + def parse(self, table, insn): + index = insn.opcodes[0]; + if insn.nByteInsn > 1: + assert index == '0f' + table = self.updateTable(table, index, 'opctbl', '0f') + index = insn.opcodes[1] + + if insn.nByteInsn == 3: + table = self.updateTable(table, index, 'opctbl', index) + index = insn.opcodes[2] + + # Walk down the tree, create levels as needed, for opcode + # extensions. The order is important, and determines how + # well the opcode table is packed. Also note, /sse must be + # before /o, because /sse may consume operand size prefix + # affect the outcome of /o. + for ext in ('/mod', '/x87', '/reg', '/rm', '/sse', + '/o', '/a', '/m', '/3dnow'): + if ext in insn.opcext: + table = self.updateTable(table, index, ext, ext) + index = insn.opcext[ext] + + # additional table for disambiguating vendor + if len(insn.vendor): + table = self.updateTable(table, index, 'vendor', insn.vendor) + index = self.OpcExtIndex['vendor'][insn.vendor] + + # make leaf node entries + leaf = self.updateTable(table, index, 'insn', '') + + leaf['mnemonic'] = insn.mnemonic + leaf['prefixes'] = insn.prefixes + leaf['operands'] = insn.operands + + # add instruction to linear table of instruction forms + self.InsnTable.append({ 'prefixes' : insn.prefixes, + 'mnemonic' : insn.mnemonic, + 'operands' : insn.operands }) + + # add mnemonic to mnemonic table + if not insn.mnemonic in self.MnemonicsTable: + self.MnemonicsTable.append(insn.mnemonic) + + + # Adds an instruction definition to the opcode tables + def addInsnDef( self, prefixes, mnemonic, opcodes, operands, vendor ): + insn = self.Insn(prefixes=prefixes, + mnemonic=mnemonic, + opcodes=opcodes, + operands=operands, + vendor=vendor) + self.parse(self.OpcodeTable0, insn) + + def print_table( self, table, pfxs ): + print "%s |" % pfxs + keys = table[ 'entries' ].keys() + if ( len( keys ) ): + keys.sort() + for idx in keys: + e = table[ 'entries' ][ idx ] + if e[ 'type' ] == 'insn': + print "%s |-<%s>" % ( pfxs, idx ), + print "%s %s" % ( e[ 'mnemonic' ], ' '.join( e[ 'operands'] ) ) + else: + print "%s |-<%s> %s" % ( pfxs, idx, e['type'] ) + self.print_table( e, pfxs + ' |' ) + + def print_tree( self ): + self.print_table( self.OpcodeTable0, '' ) diff --git a/3rdparty/masm/disassembler/udis86/ud_optable.py b/3rdparty/masm/disassembler/udis86/ud_optable.py new file mode 100644 index 0000000000..5b5c55d3b8 --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/ud_optable.py @@ -0,0 +1,103 @@ +# udis86 - scripts/ud_optable.py (optable.xml parser) +# +# Copyright (c) 2009 Vivek Thampi +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import os +import sys +from xml.dom import minidom + +class UdOptableXmlParser: + + def parseDef( self, node ): + ven = '' + pfx = [] + opc = [] + opr = [] + for def_node in node.childNodes: + if not def_node.localName: + continue + if def_node.localName == 'pfx': + pfx = def_node.firstChild.data.split(); + elif def_node.localName == 'opc': + opc = def_node.firstChild.data.split(); + elif def_node.localName == 'opr': + opr = def_node.firstChild.data.split(); + elif def_node.localName == 'mode': + pfx.extend( def_node.firstChild.data.split() ); + elif def_node.localName == 'syn': + pfx.extend( def_node.firstChild.data.split() ); + elif def_node.localName == 'vendor': + ven = ( def_node.firstChild.data ); + else: + print "warning: invalid node - %s" % def_node.localName + continue + return ( pfx, opc, opr, ven ) + + def parse( self, xml, fn ): + xmlDoc = minidom.parse( xml ) + self.TlNode = xmlDoc.firstChild + + while self.TlNode and self.TlNode.localName != "x86optable": + self.TlNode = self.TlNode.nextSibling + + for insnNode in self.TlNode.childNodes: + if not insnNode.localName: + continue + if insnNode.localName != "instruction": + print "warning: invalid insn node - %s" % insnNode.localName + continue + + mnemonic = insnNode.getElementsByTagName( 'mnemonic' )[ 0 ].firstChild.data + vendor = '' + + for node in insnNode.childNodes: + if node.localName == 'vendor': + vendor = node.firstChild.data + elif node.localName == 'def': + ( prefixes, opcodes, operands, local_vendor ) = \ + self.parseDef( node ) + if ( len( local_vendor ) ): + vendor = local_vendor + # callback + fn( prefixes, mnemonic, opcodes, operands, vendor ) + + +def printFn( pfx, mnm, opc, opr, ven ): + print 'def: ', + if len( pfx ): + print ' '.join( pfx ), + print "%s %s %s %s" % \ + ( mnm, ' '.join( opc ), ' '.join( opr ), ven ) + + +def parse( xml, callback ): + parser = UdOptableXmlParser() + parser.parse( xml, callback ) + +def main(): + parser = UdOptableXmlParser() + parser.parse( sys.argv[ 1 ], printFn ) + +if __name__ == "__main__": + main() diff --git a/3rdparty/masm/disassembler/udis86/udis86.c b/3rdparty/masm/disassembler/udis86/udis86.c new file mode 100644 index 0000000000..fbf76707a0 --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/udis86.c @@ -0,0 +1,183 @@ +/* udis86 - libudis86/udis86.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_input.h" +#include "udis86_extern.h" + +#ifndef __UD_STANDALONE__ +# include +# include +#endif /* __UD_STANDALONE__ */ + +/* ============================================================================= + * ud_init() - Initializes ud_t object. + * ============================================================================= + */ +extern void +ud_init(struct ud* u) +{ + memset((void*)u, 0, sizeof(struct ud)); + ud_set_mode(u, 16); + u->mnemonic = UD_Iinvalid; + ud_set_pc(u, 0); +#ifndef __UD_STANDALONE__ + ud_set_input_file(u, stdin); +#endif /* __UD_STANDALONE__ */ +} + +/* ============================================================================= + * ud_disassemble() - disassembles one instruction and returns the number of + * bytes disassembled. A zero means end of disassembly. + * ============================================================================= + */ +extern unsigned int +ud_disassemble(struct ud* u) +{ + if (ud_input_end(u)) + return 0; + + + u->insn_buffer[0] = u->insn_hexcode[0] = 0; + + + if (ud_decode(u) == 0) + return 0; + if (u->translator) + u->translator(u); + return ud_insn_len(u); +} + +/* ============================================================================= + * ud_set_mode() - Set Disassemly Mode. + * ============================================================================= + */ +extern void +ud_set_mode(struct ud* u, uint8_t m) +{ + switch(m) { + case 16: + case 32: + case 64: u->dis_mode = m ; return; + default: u->dis_mode = 16; return; + } +} + +/* ============================================================================= + * ud_set_vendor() - Set vendor. + * ============================================================================= + */ +extern void +ud_set_vendor(struct ud* u, unsigned v) +{ + switch(v) { + case UD_VENDOR_INTEL: + u->vendor = v; + break; + case UD_VENDOR_ANY: + u->vendor = v; + break; + default: + u->vendor = UD_VENDOR_AMD; + } +} + +/* ============================================================================= + * ud_set_pc() - Sets code origin. + * ============================================================================= + */ +extern void +ud_set_pc(struct ud* u, uint64_t o) +{ + u->pc = o; +} + +/* ============================================================================= + * ud_set_syntax() - Sets the output syntax. + * ============================================================================= + */ +extern void +ud_set_syntax(struct ud* u, void (*t)(struct ud*)) +{ + u->translator = t; +} + +/* ============================================================================= + * ud_insn() - returns the disassembled instruction + * ============================================================================= + */ +extern char* +ud_insn_asm(struct ud* u) +{ + return u->insn_buffer; +} + +/* ============================================================================= + * ud_insn_offset() - Returns the offset. + * ============================================================================= + */ +extern uint64_t +ud_insn_off(struct ud* u) +{ + return u->insn_offset; +} + + +/* ============================================================================= + * ud_insn_hex() - Returns hex form of disassembled instruction. + * ============================================================================= + */ +extern char* +ud_insn_hex(struct ud* u) +{ + return u->insn_hexcode; +} + +/* ============================================================================= + * ud_insn_ptr() - Returns code disassembled. + * ============================================================================= + */ +extern uint8_t* +ud_insn_ptr(struct ud* u) +{ + return u->inp_sess; +} + +/* ============================================================================= + * ud_insn_len() - Returns the count of bytes disassembled. + * ============================================================================= + */ +extern unsigned int +ud_insn_len(struct ud* u) +{ + return u->inp_ctr; +} + +#endif // USE(UDIS86) diff --git a/3rdparty/masm/disassembler/udis86/udis86.h b/3rdparty/masm/disassembler/udis86/udis86.h new file mode 100644 index 0000000000..baaf495e04 --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/udis86.h @@ -0,0 +1,33 @@ +/* udis86 - udis86.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UDIS86_H +#define UDIS86_H + +#include "udis86_types.h" +#include "udis86_extern.h" +#include "udis86_itab.h" + +#endif diff --git a/3rdparty/masm/disassembler/udis86/udis86_decode.c b/3rdparty/masm/disassembler/udis86/udis86_decode.c new file mode 100644 index 0000000000..a3fd576655 --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/udis86_decode.c @@ -0,0 +1,1142 @@ +/* udis86 - libudis86/decode.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_extern.h" +#include "udis86_types.h" +#include "udis86_input.h" +#include "udis86_decode.h" +#include + +#define dbg(x, n...) +/* #define dbg printf */ + +#ifndef __UD_STANDALONE__ +# include +#endif /* __UD_STANDALONE__ */ + +/* The max number of prefixes to an instruction */ +#define MAX_PREFIXES 15 + +/* instruction aliases and special cases */ +static struct ud_itab_entry s_ie__invalid = + { UD_Iinvalid, O_NONE, O_NONE, O_NONE, P_none }; + +static int +decode_ext(struct ud *u, uint16_t ptr); + + +static inline int +eff_opr_mode(int dis_mode, int rex_w, int pfx_opr) +{ + if (dis_mode == 64) { + return rex_w ? 64 : (pfx_opr ? 16 : 32); + } else if (dis_mode == 32) { + return pfx_opr ? 16 : 32; + } else { + ASSERT(dis_mode == 16); + return pfx_opr ? 32 : 16; + } +} + + +static inline int +eff_adr_mode(int dis_mode, int pfx_adr) +{ + if (dis_mode == 64) { + return pfx_adr ? 32 : 64; + } else if (dis_mode == 32) { + return pfx_adr ? 16 : 32; + } else { + ASSERT(dis_mode == 16); + return pfx_adr ? 32 : 16; + } +} + + +/* Looks up mnemonic code in the mnemonic string table + * Returns NULL if the mnemonic code is invalid + */ +const char * ud_lookup_mnemonic( enum ud_mnemonic_code c ) +{ + return ud_mnemonics_str[ c ]; +} + + +/* + * decode_prefixes + * + * Extracts instruction prefixes. + */ +static int +decode_prefixes(struct ud *u) +{ + unsigned int have_pfx = 1; + unsigned int i; + uint8_t curr; + + /* if in error state, bail out */ + if ( u->error ) + return -1; + + /* keep going as long as there are prefixes available */ + for ( i = 0; have_pfx ; ++i ) { + + /* Get next byte. */ + ud_inp_next(u); + if ( u->error ) + return -1; + curr = ud_inp_curr( u ); + + /* rex prefixes in 64bit mode */ + if ( u->dis_mode == 64 && ( curr & 0xF0 ) == 0x40 ) { + u->pfx_rex = curr; + } else { + switch ( curr ) + { + case 0x2E : + u->pfx_seg = UD_R_CS; + u->pfx_rex = 0; + break; + case 0x36 : + u->pfx_seg = UD_R_SS; + u->pfx_rex = 0; + break; + case 0x3E : + u->pfx_seg = UD_R_DS; + u->pfx_rex = 0; + break; + case 0x26 : + u->pfx_seg = UD_R_ES; + u->pfx_rex = 0; + break; + case 0x64 : + u->pfx_seg = UD_R_FS; + u->pfx_rex = 0; + break; + case 0x65 : + u->pfx_seg = UD_R_GS; + u->pfx_rex = 0; + break; + case 0x67 : /* adress-size override prefix */ + u->pfx_adr = 0x67; + u->pfx_rex = 0; + break; + case 0xF0 : + u->pfx_lock = 0xF0; + u->pfx_rex = 0; + break; + case 0x66: + /* the 0x66 sse prefix is only effective if no other sse prefix + * has already been specified. + */ + if ( !u->pfx_insn ) u->pfx_insn = 0x66; + u->pfx_opr = 0x66; + u->pfx_rex = 0; + break; + case 0xF2: + u->pfx_insn = 0xF2; + u->pfx_repne = 0xF2; + u->pfx_rex = 0; + break; + case 0xF3: + u->pfx_insn = 0xF3; + u->pfx_rep = 0xF3; + u->pfx_repe = 0xF3; + u->pfx_rex = 0; + break; + default : + /* No more prefixes */ + have_pfx = 0; + break; + } + } + + /* check if we reached max instruction length */ + if ( i + 1 == MAX_INSN_LENGTH ) { + u->error = 1; + break; + } + } + + /* return status */ + if ( u->error ) + return -1; + + /* rewind back one byte in stream, since the above loop + * stops with a non-prefix byte. + */ + ud_inp_back(u); + return 0; +} + + +static inline unsigned int modrm( struct ud * u ) +{ + if ( !u->have_modrm ) { + u->modrm = ud_inp_next( u ); + u->have_modrm = 1; + } + return u->modrm; +} + + +static unsigned int resolve_operand_size( const struct ud * u, unsigned int s ) +{ + switch ( s ) + { + case SZ_V: + return ( u->opr_mode ); + case SZ_Z: + return ( u->opr_mode == 16 ) ? 16 : 32; + case SZ_P: + return ( u->opr_mode == 16 ) ? SZ_WP : SZ_DP; + case SZ_MDQ: + return ( u->opr_mode == 16 ) ? 32 : u->opr_mode; + case SZ_RDQ: + return ( u->dis_mode == 64 ) ? 64 : 32; + default: + return s; + } +} + + +static int resolve_mnemonic( struct ud* u ) +{ + /* far/near flags */ + u->br_far = 0; + u->br_near = 0; + /* readjust operand sizes for call/jmp instrcutions */ + if ( u->mnemonic == UD_Icall || u->mnemonic == UD_Ijmp ) { + /* WP: 16:16 pointer */ + if ( u->operand[ 0 ].size == SZ_WP ) { + u->operand[ 0 ].size = 16; + u->br_far = 1; + u->br_near= 0; + /* DP: 32:32 pointer */ + } else if ( u->operand[ 0 ].size == SZ_DP ) { + u->operand[ 0 ].size = 32; + u->br_far = 1; + u->br_near= 0; + } else { + u->br_far = 0; + u->br_near= 1; + } + /* resolve 3dnow weirdness. */ + } else if ( u->mnemonic == UD_I3dnow ) { + u->mnemonic = ud_itab[ u->le->table[ ud_inp_curr( u ) ] ].mnemonic; + } + /* SWAPGS is only valid in 64bits mode */ + if ( u->mnemonic == UD_Iswapgs && u->dis_mode != 64 ) { + u->error = 1; + return -1; + } + + if (u->mnemonic == UD_Ixchg) { + if ((u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_AX && + u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_AX) || + (u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_EAX && + u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_EAX)) { + u->operand[0].type = UD_NONE; + u->operand[1].type = UD_NONE; + u->mnemonic = UD_Inop; + } + } + + if (u->mnemonic == UD_Inop && u->pfx_rep) { + u->pfx_rep = 0; + u->mnemonic = UD_Ipause; + } + return 0; +} + + +/* ----------------------------------------------------------------------------- + * decode_a()- Decodes operands of the type seg:offset + * ----------------------------------------------------------------------------- + */ +static void +decode_a(struct ud* u, struct ud_operand *op) +{ + if (u->opr_mode == 16) { + /* seg16:off16 */ + op->type = UD_OP_PTR; + op->size = 32; + op->lval.ptr.off = ud_inp_uint16(u); + op->lval.ptr.seg = ud_inp_uint16(u); + } else { + /* seg16:off32 */ + op->type = UD_OP_PTR; + op->size = 48; + op->lval.ptr.off = ud_inp_uint32(u); + op->lval.ptr.seg = ud_inp_uint16(u); + } +} + +/* ----------------------------------------------------------------------------- + * decode_gpr() - Returns decoded General Purpose Register + * ----------------------------------------------------------------------------- + */ +static enum ud_type +decode_gpr(register struct ud* u, unsigned int s, unsigned char rm) +{ + s = resolve_operand_size(u, s); + + switch (s) { + case 64: + return UD_R_RAX + rm; + case SZ_DP: + case 32: + return UD_R_EAX + rm; + case SZ_WP: + case 16: + return UD_R_AX + rm; + case 8: + if (u->dis_mode == 64 && u->pfx_rex) { + if (rm >= 4) + return UD_R_SPL + (rm-4); + return UD_R_AL + rm; + } else return UD_R_AL + rm; + default: + return 0; + } +} + +/* ----------------------------------------------------------------------------- + * resolve_gpr64() - 64bit General Purpose Register-Selection. + * ----------------------------------------------------------------------------- + */ +static enum ud_type +resolve_gpr64(struct ud* u, enum ud_operand_code gpr_op, enum ud_operand_size * size) +{ + if (gpr_op >= OP_rAXr8 && gpr_op <= OP_rDIr15) + gpr_op = (gpr_op - OP_rAXr8) | (REX_B(u->pfx_rex) << 3); + else gpr_op = (gpr_op - OP_rAX); + + if (u->opr_mode == 16) { + *size = 16; + return gpr_op + UD_R_AX; + } + if (u->dis_mode == 32 || + (u->opr_mode == 32 && ! (REX_W(u->pfx_rex) || u->default64))) { + *size = 32; + return gpr_op + UD_R_EAX; + } + + *size = 64; + return gpr_op + UD_R_RAX; +} + +/* ----------------------------------------------------------------------------- + * resolve_gpr32 () - 32bit General Purpose Register-Selection. + * ----------------------------------------------------------------------------- + */ +static enum ud_type +resolve_gpr32(struct ud* u, enum ud_operand_code gpr_op) +{ + gpr_op = gpr_op - OP_eAX; + + if (u->opr_mode == 16) + return gpr_op + UD_R_AX; + + return gpr_op + UD_R_EAX; +} + +/* ----------------------------------------------------------------------------- + * resolve_reg() - Resolves the register type + * ----------------------------------------------------------------------------- + */ +static enum ud_type +resolve_reg(struct ud* u, unsigned int type, unsigned char i) +{ + switch (type) { + case T_MMX : return UD_R_MM0 + (i & 7); + case T_XMM : return UD_R_XMM0 + i; + case T_CRG : return UD_R_CR0 + i; + case T_DBG : return UD_R_DR0 + i; + case T_SEG : { + /* + * Only 6 segment registers, anything else is an error. + */ + if ((i & 7) > 5) { + u->error = 1; + } else { + return UD_R_ES + (i & 7); + } + } + case T_NONE: + default: return UD_NONE; + } +} + +/* ----------------------------------------------------------------------------- + * decode_imm() - Decodes Immediate values. + * ----------------------------------------------------------------------------- + */ +static void +decode_imm(struct ud* u, unsigned int s, struct ud_operand *op) +{ + op->size = resolve_operand_size(u, s); + op->type = UD_OP_IMM; + + switch (op->size) { + case 8: op->lval.sbyte = ud_inp_uint8(u); break; + case 16: op->lval.uword = ud_inp_uint16(u); break; + case 32: op->lval.udword = ud_inp_uint32(u); break; + case 64: op->lval.uqword = ud_inp_uint64(u); break; + default: return; + } +} + + +/* + * decode_modrm_reg + * + * Decodes reg field of mod/rm byte + * + */ +static void +decode_modrm_reg(struct ud *u, + struct ud_operand *operand, + unsigned int type, + unsigned int size) +{ + uint8_t reg = (REX_R(u->pfx_rex) << 3) | MODRM_REG(modrm(u)); + operand->type = UD_OP_REG; + operand->size = resolve_operand_size(u, size); + + if (type == T_GPR) { + operand->base = decode_gpr(u, operand->size, reg); + } else { + operand->base = resolve_reg(u, type, reg); + } +} + + +/* + * decode_modrm_rm + * + * Decodes rm field of mod/rm byte + * + */ +static void +decode_modrm_rm(struct ud *u, + struct ud_operand *op, + unsigned char type, + unsigned int size) + +{ + unsigned char mod, rm, reg; + + /* get mod, r/m and reg fields */ + mod = MODRM_MOD(modrm(u)); + rm = (REX_B(u->pfx_rex) << 3) | MODRM_RM(modrm(u)); + reg = (REX_R(u->pfx_rex) << 3) | MODRM_REG(modrm(u)); + + op->size = resolve_operand_size(u, size); + + /* + * If mod is 11b, then the modrm.rm specifies a register. + * + */ + if (mod == 3) { + op->type = UD_OP_REG; + if (type == T_GPR) { + op->base = decode_gpr(u, op->size, rm); + } else { + op->base = resolve_reg(u, type, (REX_B(u->pfx_rex) << 3) | (rm & 7)); + } + return; + } + + + /* + * !11 => Memory Address + */ + op->type = UD_OP_MEM; + + if (u->adr_mode == 64) { + op->base = UD_R_RAX + rm; + if (mod == 1) { + op->offset = 8; + } else if (mod == 2) { + op->offset = 32; + } else if (mod == 0 && (rm & 7) == 5) { + op->base = UD_R_RIP; + op->offset = 32; + } else { + op->offset = 0; + } + /* + * Scale-Index-Base (SIB) + */ + if ((rm & 7) == 4) { + ud_inp_next(u); + + op->scale = (1 << SIB_S(ud_inp_curr(u))) & ~1; + op->index = UD_R_RAX + (SIB_I(ud_inp_curr(u)) | (REX_X(u->pfx_rex) << 3)); + op->base = UD_R_RAX + (SIB_B(ud_inp_curr(u)) | (REX_B(u->pfx_rex) << 3)); + + /* special conditions for base reference */ + if (op->index == UD_R_RSP) { + op->index = UD_NONE; + op->scale = UD_NONE; + } + + if (op->base == UD_R_RBP || op->base == UD_R_R13) { + if (mod == 0) { + op->base = UD_NONE; + } + if (mod == 1) { + op->offset = 8; + } else { + op->offset = 32; + } + } + } + } else if (u->adr_mode == 32) { + op->base = UD_R_EAX + rm; + if (mod == 1) { + op->offset = 8; + } else if (mod == 2) { + op->offset = 32; + } else if (mod == 0 && rm == 5) { + op->base = UD_NONE; + op->offset = 32; + } else { + op->offset = 0; + } + + /* Scale-Index-Base (SIB) */ + if ((rm & 7) == 4) { + ud_inp_next(u); + + op->scale = (1 << SIB_S(ud_inp_curr(u))) & ~1; + op->index = UD_R_EAX + (SIB_I(ud_inp_curr(u)) | (REX_X(u->pfx_rex) << 3)); + op->base = UD_R_EAX + (SIB_B(ud_inp_curr(u)) | (REX_B(u->pfx_rex) << 3)); + + if (op->index == UD_R_ESP) { + op->index = UD_NONE; + op->scale = UD_NONE; + } + + /* special condition for base reference */ + if (op->base == UD_R_EBP) { + if (mod == 0) { + op->base = UD_NONE; + } + if (mod == 1) { + op->offset = 8; + } else { + op->offset = 32; + } + } + } + } else { + const unsigned int bases[] = { UD_R_BX, UD_R_BX, UD_R_BP, UD_R_BP, + UD_R_SI, UD_R_DI, UD_R_BP, UD_R_BX }; + const unsigned int indices[] = { UD_R_SI, UD_R_DI, UD_R_SI, UD_R_DI, + UD_NONE, UD_NONE, UD_NONE, UD_NONE }; + op->base = bases[rm & 7]; + op->index = indices[rm & 7]; + if (mod == 0 && rm == 6) { + op->offset= 16; + op->base = UD_NONE; + } else if (mod == 1) { + op->offset = 8; + } else if (mod == 2) { + op->offset = 16; + } + } + + /* + * extract offset, if any + */ + switch (op->offset) { + case 8 : op->lval.ubyte = ud_inp_uint8(u); break; + case 16: op->lval.uword = ud_inp_uint16(u); break; + case 32: op->lval.udword = ud_inp_uint32(u); break; + case 64: op->lval.uqword = ud_inp_uint64(u); break; + default: break; + } +} + +/* ----------------------------------------------------------------------------- + * decode_o() - Decodes offset + * ----------------------------------------------------------------------------- + */ +static void +decode_o(struct ud* u, unsigned int s, struct ud_operand *op) +{ + switch (u->adr_mode) { + case 64: + op->offset = 64; + op->lval.uqword = ud_inp_uint64(u); + break; + case 32: + op->offset = 32; + op->lval.udword = ud_inp_uint32(u); + break; + case 16: + op->offset = 16; + op->lval.uword = ud_inp_uint16(u); + break; + default: + return; + } + op->type = UD_OP_MEM; + op->size = resolve_operand_size(u, s); +} + +/* ----------------------------------------------------------------------------- + * decode_operands() - Disassembles Operands. + * ----------------------------------------------------------------------------- + */ +static int +decode_operand(struct ud *u, + struct ud_operand *operand, + enum ud_operand_code type, + unsigned int size) +{ + switch (type) { + case OP_A : + decode_a(u, operand); + break; + case OP_MR: + if (MODRM_MOD(modrm(u)) == 3) { + decode_modrm_rm(u, operand, T_GPR, + size == SZ_DY ? SZ_MDQ : SZ_V); + } else if (size == SZ_WV) { + decode_modrm_rm( u, operand, T_GPR, SZ_W); + } else if (size == SZ_BV) { + decode_modrm_rm( u, operand, T_GPR, SZ_B); + } else if (size == SZ_DY) { + decode_modrm_rm( u, operand, T_GPR, SZ_D); + } else { + ASSERT(!"unexpected size"); + } + break; + case OP_M: + if (MODRM_MOD(modrm(u)) == 3) { + u->error = 1; + } + /* intended fall through */ + case OP_E: + decode_modrm_rm(u, operand, T_GPR, size); + break; + break; + case OP_G: + decode_modrm_reg(u, operand, T_GPR, size); + break; + case OP_I: + decode_imm(u, size, operand); + break; + case OP_I1: + operand->type = UD_OP_CONST; + operand->lval.udword = 1; + break; + case OP_PR: + if (MODRM_MOD(modrm(u)) != 3) { + u->error = 1; + } + decode_modrm_rm(u, operand, T_MMX, size); + break; + case OP_P: + decode_modrm_reg(u, operand, T_MMX, size); + break; + case OP_VR: + if (MODRM_MOD(modrm(u)) != 3) { + u->error = 1; + } + /* intended fall through */ + case OP_W: + decode_modrm_rm(u, operand, T_XMM, size); + break; + case OP_V: + decode_modrm_reg(u, operand, T_XMM, size); + break; + case OP_S: + decode_modrm_reg(u, operand, T_SEG, size); + break; + case OP_AL: + case OP_CL: + case OP_DL: + case OP_BL: + case OP_AH: + case OP_CH: + case OP_DH: + case OP_BH: + operand->type = UD_OP_REG; + operand->base = UD_R_AL + (type - OP_AL); + operand->size = 8; + break; + case OP_DX: + operand->type = UD_OP_REG; + operand->base = UD_R_DX; + operand->size = 16; + break; + case OP_O: + decode_o(u, size, operand); + break; + case OP_rAXr8: + case OP_rCXr9: + case OP_rDXr10: + case OP_rBXr11: + case OP_rSPr12: + case OP_rBPr13: + case OP_rSIr14: + case OP_rDIr15: + case OP_rAX: + case OP_rCX: + case OP_rDX: + case OP_rBX: + case OP_rSP: + case OP_rBP: + case OP_rSI: + case OP_rDI: + operand->type = UD_OP_REG; + operand->base = resolve_gpr64(u, type, &operand->size); + break; + case OP_ALr8b: + case OP_CLr9b: + case OP_DLr10b: + case OP_BLr11b: + case OP_AHr12b: + case OP_CHr13b: + case OP_DHr14b: + case OP_BHr15b: { + ud_type_t gpr = (type - OP_ALr8b) + UD_R_AL + + (REX_B(u->pfx_rex) << 3); + if (UD_R_AH <= gpr && u->pfx_rex) { + gpr = gpr + 4; + } + operand->type = UD_OP_REG; + operand->base = gpr; + break; + } + case OP_eAX: + case OP_eCX: + case OP_eDX: + case OP_eBX: + case OP_eSP: + case OP_eBP: + case OP_eSI: + case OP_eDI: + operand->type = UD_OP_REG; + operand->base = resolve_gpr32(u, type); + operand->size = u->opr_mode == 16 ? 16 : 32; + break; + case OP_ES: + case OP_CS: + case OP_DS: + case OP_SS: + case OP_FS: + case OP_GS: + /* in 64bits mode, only fs and gs are allowed */ + if (u->dis_mode == 64) { + if (type != OP_FS && type != OP_GS) { + u->error= 1; + } + } + operand->type = UD_OP_REG; + operand->base = (type - OP_ES) + UD_R_ES; + operand->size = 16; + break; + case OP_J : + decode_imm(u, size, operand); + operand->type = UD_OP_JIMM; + break ; + case OP_Q: + decode_modrm_rm(u, operand, T_MMX, size); + break; + case OP_R : + decode_modrm_rm(u, operand, T_GPR, size); + break; + case OP_C: + decode_modrm_reg(u, operand, T_CRG, size); + break; + case OP_D: + decode_modrm_reg(u, operand, T_DBG, size); + break; + case OP_I3 : + operand->type = UD_OP_CONST; + operand->lval.sbyte = 3; + break; + case OP_ST0: + case OP_ST1: + case OP_ST2: + case OP_ST3: + case OP_ST4: + case OP_ST5: + case OP_ST6: + case OP_ST7: + operand->type = UD_OP_REG; + operand->base = (type - OP_ST0) + UD_R_ST0; + operand->size = 0; + break; + case OP_AX: + operand->type = UD_OP_REG; + operand->base = UD_R_AX; + operand->size = 16; + break; + default : + operand->type = UD_NONE; + break; + } + return 0; +} + + +/* + * decode_operands + * + * Disassemble upto 3 operands of the current instruction being + * disassembled. By the end of the function, the operand fields + * of the ud structure will have been filled. + */ +static int +decode_operands(struct ud* u) +{ + decode_operand(u, &u->operand[0], + u->itab_entry->operand1.type, + u->itab_entry->operand1.size); + decode_operand(u, &u->operand[1], + u->itab_entry->operand2.type, + u->itab_entry->operand2.size); + decode_operand(u, &u->operand[2], + u->itab_entry->operand3.type, + u->itab_entry->operand3.size); + return 0; +} + +/* ----------------------------------------------------------------------------- + * clear_insn() - clear instruction structure + * ----------------------------------------------------------------------------- + */ +static void +clear_insn(register struct ud* u) +{ + u->error = 0; + u->pfx_seg = 0; + u->pfx_opr = 0; + u->pfx_adr = 0; + u->pfx_lock = 0; + u->pfx_repne = 0; + u->pfx_rep = 0; + u->pfx_repe = 0; + u->pfx_rex = 0; + u->pfx_insn = 0; + u->mnemonic = UD_Inone; + u->itab_entry = NULL; + u->have_modrm = 0; + + memset( &u->operand[ 0 ], 0, sizeof( struct ud_operand ) ); + memset( &u->operand[ 1 ], 0, sizeof( struct ud_operand ) ); + memset( &u->operand[ 2 ], 0, sizeof( struct ud_operand ) ); +} + +static int +resolve_mode( struct ud* u ) +{ + /* if in error state, bail out */ + if ( u->error ) return -1; + + /* propagate prefix effects */ + if ( u->dis_mode == 64 ) { /* set 64bit-mode flags */ + + /* Check validity of instruction m64 */ + if ( P_INV64( u->itab_entry->prefix ) ) { + u->error = 1; + return -1; + } + + /* effective rex prefix is the effective mask for the + * instruction hard-coded in the opcode map. + */ + u->pfx_rex = ( u->pfx_rex & 0x40 ) | + ( u->pfx_rex & REX_PFX_MASK( u->itab_entry->prefix ) ); + + /* whether this instruction has a default operand size of + * 64bit, also hardcoded into the opcode map. + */ + u->default64 = P_DEF64( u->itab_entry->prefix ); + /* calculate effective operand size */ + if ( REX_W( u->pfx_rex ) ) { + u->opr_mode = 64; + } else if ( u->pfx_opr ) { + u->opr_mode = 16; + } else { + /* unless the default opr size of instruction is 64, + * the effective operand size in the absence of rex.w + * prefix is 32. + */ + u->opr_mode = ( u->default64 ) ? 64 : 32; + } + + /* calculate effective address size */ + u->adr_mode = (u->pfx_adr) ? 32 : 64; + } else if ( u->dis_mode == 32 ) { /* set 32bit-mode flags */ + u->opr_mode = ( u->pfx_opr ) ? 16 : 32; + u->adr_mode = ( u->pfx_adr ) ? 16 : 32; + } else if ( u->dis_mode == 16 ) { /* set 16bit-mode flags */ + u->opr_mode = ( u->pfx_opr ) ? 32 : 16; + u->adr_mode = ( u->pfx_adr ) ? 32 : 16; + } + + /* These flags determine which operand to apply the operand size + * cast to. + */ + u->c1 = ( P_C1( u->itab_entry->prefix ) ) ? 1 : 0; + u->c2 = ( P_C2( u->itab_entry->prefix ) ) ? 1 : 0; + u->c3 = ( P_C3( u->itab_entry->prefix ) ) ? 1 : 0; + + /* set flags for implicit addressing */ + u->implicit_addr = P_IMPADDR( u->itab_entry->prefix ); + + return 0; +} + +static int gen_hex( struct ud *u ) +{ + unsigned int i; + unsigned char *src_ptr = ud_inp_sess( u ); + char* src_hex; + + /* bail out if in error stat. */ + if ( u->error ) return -1; + /* output buffer pointe */ + src_hex = ( char* ) u->insn_hexcode; + /* for each byte used to decode instruction */ + for ( i = 0; i < u->inp_ctr; ++i, ++src_ptr) { + sprintf( src_hex, "%02x", *src_ptr & 0xFF ); + src_hex += 2; + } + return 0; +} + + +static inline int +decode_insn(struct ud *u, uint16_t ptr) +{ + ASSERT((ptr & 0x8000) == 0); + u->itab_entry = &ud_itab[ ptr ]; + u->mnemonic = u->itab_entry->mnemonic; + return (resolve_mode(u) == 0 && + decode_operands(u) == 0 && + resolve_mnemonic(u) == 0) ? 0 : -1; +} + + +/* + * decode_3dnow() + * + * Decoding 3dnow is a little tricky because of its strange opcode + * structure. The final opcode disambiguation depends on the last + * byte that comes after the operands have been decoded. Fortunately, + * all 3dnow instructions have the same set of operand types. So we + * go ahead and decode the instruction by picking an arbitrarily chosen + * valid entry in the table, decode the operands, and read the final + * byte to resolve the menmonic. + */ +static inline int +decode_3dnow(struct ud* u) +{ + uint16_t ptr; + ASSERT(u->le->type == UD_TAB__OPC_3DNOW); + ASSERT(u->le->table[0xc] != 0); + decode_insn(u, u->le->table[0xc]); + ud_inp_next(u); + if (u->error) { + return -1; + } + ptr = u->le->table[ud_inp_curr(u)]; + ASSERT((ptr & 0x8000) == 0); + u->mnemonic = ud_itab[ptr].mnemonic; + return 0; +} + + +static int +decode_ssepfx(struct ud *u) +{ + uint8_t idx = ((u->pfx_insn & 0xf) + 1) / 2; + if (u->le->table[idx] == 0) { + idx = 0; + } + if (idx && u->le->table[idx] != 0) { + /* + * "Consume" the prefix as a part of the opcode, so it is no + * longer exported as an instruction prefix. + */ + switch (u->pfx_insn) { + case 0xf2: + u->pfx_repne = 0; + break; + case 0xf3: + u->pfx_rep = 0; + u->pfx_repe = 0; + break; + case 0x66: + u->pfx_opr = 0; + break; + } + } + return decode_ext(u, u->le->table[idx]); +} + + +/* + * decode_ext() + * + * Decode opcode extensions (if any) + */ +static int +decode_ext(struct ud *u, uint16_t ptr) +{ + uint8_t idx = 0; + if ((ptr & 0x8000) == 0) { + return decode_insn(u, ptr); + } + u->le = &ud_lookup_table_list[(~0x8000 & ptr)]; + if (u->le->type == UD_TAB__OPC_3DNOW) { + return decode_3dnow(u); + } + + switch (u->le->type) { + case UD_TAB__OPC_MOD: + /* !11 = 0, 11 = 1 */ + idx = (MODRM_MOD(modrm(u)) + 1) / 4; + break; + /* disassembly mode/operand size/address size based tables. + * 16 = 0,, 32 = 1, 64 = 2 + */ + case UD_TAB__OPC_MODE: + idx = u->dis_mode / 32; + break; + case UD_TAB__OPC_OSIZE: + idx = eff_opr_mode(u->dis_mode, REX_W(u->pfx_rex), u->pfx_opr) / 32; + break; + case UD_TAB__OPC_ASIZE: + idx = eff_adr_mode(u->dis_mode, u->pfx_adr) / 32; + break; + case UD_TAB__OPC_X87: + idx = modrm(u) - 0xC0; + break; + case UD_TAB__OPC_VENDOR: + if (u->vendor == UD_VENDOR_ANY) { + /* choose a valid entry */ + idx = (u->le->table[idx] != 0) ? 0 : 1; + } else if (u->vendor == UD_VENDOR_AMD) { + idx = 0; + } else { + idx = 1; + } + break; + case UD_TAB__OPC_RM: + idx = MODRM_RM(modrm(u)); + break; + case UD_TAB__OPC_REG: + idx = MODRM_REG(modrm(u)); + break; + case UD_TAB__OPC_SSE: + return decode_ssepfx(u); + default: + ASSERT(!"not reached"); + break; + } + + return decode_ext(u, u->le->table[idx]); +} + + +static inline int +decode_opcode(struct ud *u) +{ + uint16_t ptr; + ASSERT(u->le->type == UD_TAB__OPC_TABLE); + ud_inp_next(u); + if (u->error) { + return -1; + } + ptr = u->le->table[ud_inp_curr(u)]; + if (ptr & 0x8000) { + u->le = &ud_lookup_table_list[ptr & ~0x8000]; + if (u->le->type == UD_TAB__OPC_TABLE) { + return decode_opcode(u); + } + } + return decode_ext(u, ptr); +} + + +/* ============================================================================= + * ud_decode() - Instruction decoder. Returns the number of bytes decoded. + * ============================================================================= + */ +unsigned int +ud_decode(struct ud *u) +{ + ud_inp_start(u); + clear_insn(u); + u->le = &ud_lookup_table_list[0]; + u->error = decode_prefixes(u) == -1 || + decode_opcode(u) == -1 || + u->error; + /* Handle decode error. */ + if (u->error) { + /* clear out the decode data. */ + clear_insn(u); + /* mark the sequence of bytes as invalid. */ + u->itab_entry = & s_ie__invalid; + u->mnemonic = u->itab_entry->mnemonic; + } + + /* maybe this stray segment override byte + * should be spewed out? + */ + if ( !P_SEG( u->itab_entry->prefix ) && + u->operand[0].type != UD_OP_MEM && + u->operand[1].type != UD_OP_MEM ) + u->pfx_seg = 0; + + u->insn_offset = u->pc; /* set offset of instruction */ + u->insn_fill = 0; /* set translation buffer index to 0 */ + u->pc += u->inp_ctr; /* move program counter by bytes decoded */ + gen_hex( u ); /* generate hex code */ + + /* return number of bytes disassembled. */ + return u->inp_ctr; +} + +/* +vim: set ts=2 sw=2 expandtab +*/ + +#endif // USE(UDIS86) diff --git a/3rdparty/masm/disassembler/udis86/udis86_decode.h b/3rdparty/masm/disassembler/udis86/udis86_decode.h new file mode 100644 index 0000000000..940ed5ad6f --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/udis86_decode.h @@ -0,0 +1,258 @@ +/* udis86 - libudis86/decode.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_DECODE_H +#define UD_DECODE_H + +#include "udis86_types.h" +#include "udis86_itab.h" + +#define MAX_INSN_LENGTH 15 + +/* register classes */ +#define T_NONE 0 +#define T_GPR 1 +#define T_MMX 2 +#define T_CRG 3 +#define T_DBG 4 +#define T_SEG 5 +#define T_XMM 6 + +/* itab prefix bits */ +#define P_none ( 0 ) +#define P_cast ( 1 << 0 ) +#define P_CAST(n) ( ( n >> 0 ) & 1 ) +#define P_c1 ( 1 << 0 ) +#define P_C1(n) ( ( n >> 0 ) & 1 ) +#define P_rexb ( 1 << 1 ) +#define P_REXB(n) ( ( n >> 1 ) & 1 ) +#define P_depM ( 1 << 2 ) +#define P_DEPM(n) ( ( n >> 2 ) & 1 ) +#define P_c3 ( 1 << 3 ) +#define P_C3(n) ( ( n >> 3 ) & 1 ) +#define P_inv64 ( 1 << 4 ) +#define P_INV64(n) ( ( n >> 4 ) & 1 ) +#define P_rexw ( 1 << 5 ) +#define P_REXW(n) ( ( n >> 5 ) & 1 ) +#define P_c2 ( 1 << 6 ) +#define P_C2(n) ( ( n >> 6 ) & 1 ) +#define P_def64 ( 1 << 7 ) +#define P_DEF64(n) ( ( n >> 7 ) & 1 ) +#define P_rexr ( 1 << 8 ) +#define P_REXR(n) ( ( n >> 8 ) & 1 ) +#define P_oso ( 1 << 9 ) +#define P_OSO(n) ( ( n >> 9 ) & 1 ) +#define P_aso ( 1 << 10 ) +#define P_ASO(n) ( ( n >> 10 ) & 1 ) +#define P_rexx ( 1 << 11 ) +#define P_REXX(n) ( ( n >> 11 ) & 1 ) +#define P_ImpAddr ( 1 << 12 ) +#define P_IMPADDR(n) ( ( n >> 12 ) & 1 ) +#define P_seg ( 1 << 13 ) +#define P_SEG(n) ( ( n >> 13 ) & 1 ) +#define P_sext ( 1 << 14 ) +#define P_SEXT(n) ( ( n >> 14 ) & 1 ) + +/* rex prefix bits */ +#define REX_W(r) ( ( 0xF & ( r ) ) >> 3 ) +#define REX_R(r) ( ( 0x7 & ( r ) ) >> 2 ) +#define REX_X(r) ( ( 0x3 & ( r ) ) >> 1 ) +#define REX_B(r) ( ( 0x1 & ( r ) ) >> 0 ) +#define REX_PFX_MASK(n) ( ( P_REXW(n) << 3 ) | \ + ( P_REXR(n) << 2 ) | \ + ( P_REXX(n) << 1 ) | \ + ( P_REXB(n) << 0 ) ) + +/* scable-index-base bits */ +#define SIB_S(b) ( ( b ) >> 6 ) +#define SIB_I(b) ( ( ( b ) >> 3 ) & 7 ) +#define SIB_B(b) ( ( b ) & 7 ) + +/* modrm bits */ +#define MODRM_REG(b) ( ( ( b ) >> 3 ) & 7 ) +#define MODRM_NNN(b) ( ( ( b ) >> 3 ) & 7 ) +#define MODRM_MOD(b) ( ( ( b ) >> 6 ) & 3 ) +#define MODRM_RM(b) ( ( b ) & 7 ) + +/* operand type constants -- order is important! */ + +enum ud_operand_code { + OP_NONE, + + OP_A, OP_E, OP_M, OP_G, + OP_I, + + OP_AL, OP_CL, OP_DL, OP_BL, + OP_AH, OP_CH, OP_DH, OP_BH, + + OP_ALr8b, OP_CLr9b, OP_DLr10b, OP_BLr11b, + OP_AHr12b, OP_CHr13b, OP_DHr14b, OP_BHr15b, + + OP_AX, OP_CX, OP_DX, OP_BX, + OP_SI, OP_DI, OP_SP, OP_BP, + + OP_rAX, OP_rCX, OP_rDX, OP_rBX, + OP_rSP, OP_rBP, OP_rSI, OP_rDI, + + OP_rAXr8, OP_rCXr9, OP_rDXr10, OP_rBXr11, + OP_rSPr12, OP_rBPr13, OP_rSIr14, OP_rDIr15, + + OP_eAX, OP_eCX, OP_eDX, OP_eBX, + OP_eSP, OP_eBP, OP_eSI, OP_eDI, + + OP_ES, OP_CS, OP_SS, OP_DS, + OP_FS, OP_GS, + + OP_ST0, OP_ST1, OP_ST2, OP_ST3, + OP_ST4, OP_ST5, OP_ST6, OP_ST7, + + OP_J, OP_S, OP_O, + OP_I1, OP_I3, + + OP_V, OP_W, OP_Q, OP_P, + + OP_R, OP_C, OP_D, OP_VR, OP_PR, + + OP_MR +} UD_ATTR_PACKED; + + +/* operand size constants */ + +enum ud_operand_size { + SZ_NA = 0, + SZ_Z = 1, + SZ_V = 2, + SZ_P = 3, + SZ_WP = 4, + SZ_DP = 5, + SZ_MDQ = 6, + SZ_RDQ = 7, + + /* the following values are used as is, + * and thus hard-coded. changing them + * will break internals + */ + SZ_B = 8, + SZ_W = 16, + SZ_D = 32, + SZ_Q = 64, + SZ_T = 80, + SZ_O = 128, + + SZ_WV = 17, + SZ_BV = 18, + SZ_DY = 19 + +} UD_ATTR_PACKED; + + +/* A single operand of an entry in the instruction table. + * (internal use only) + */ +struct ud_itab_entry_operand +{ + enum ud_operand_code type; + enum ud_operand_size size; +}; + + +/* A single entry in an instruction table. + *(internal use only) + */ +struct ud_itab_entry +{ + enum ud_mnemonic_code mnemonic; + struct ud_itab_entry_operand operand1; + struct ud_itab_entry_operand operand2; + struct ud_itab_entry_operand operand3; + uint32_t prefix; +}; + +struct ud_lookup_table_list_entry { + const uint16_t *table; + enum ud_table_type type; + const char *meta; +}; + + +static inline unsigned int sse_pfx_idx( const unsigned int pfx ) +{ + /* 00 = 0 + * f2 = 1 + * f3 = 2 + * 66 = 3 + */ + return ( ( pfx & 0xf ) + 1 ) / 2; +} + +static inline unsigned int mode_idx( const unsigned int mode ) +{ + /* 16 = 0 + * 32 = 1 + * 64 = 2 + */ + return ( mode / 32 ); +} + +static inline unsigned int modrm_mod_idx( const unsigned int mod ) +{ + /* !11 = 0 + * 11 = 1 + */ + return ( mod + 1 ) / 4; +} + +static inline unsigned int vendor_idx( const unsigned int vendor ) +{ + switch ( vendor ) { + case UD_VENDOR_AMD: return 0; + case UD_VENDOR_INTEL: return 1; + case UD_VENDOR_ANY: return 2; + default: return 2; + } +} + +static inline unsigned int is_group_ptr( uint16_t ptr ) +{ + return ( 0x8000 & ptr ); +} + +static inline unsigned int group_idx( uint16_t ptr ) +{ + return ( ~0x8000 & ptr ); +} + + +extern struct ud_itab_entry ud_itab[]; +extern struct ud_lookup_table_list_entry ud_lookup_table_list[]; + +#endif /* UD_DECODE_H */ + +/* vim:cindent + * vim:expandtab + * vim:ts=4 + * vim:sw=4 + */ diff --git a/3rdparty/masm/disassembler/udis86/udis86_extern.h b/3rdparty/masm/disassembler/udis86/udis86_extern.h new file mode 100644 index 0000000000..8e87721e8c --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/udis86_extern.h @@ -0,0 +1,88 @@ +/* udis86 - libudis86/extern.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_EXTERN_H +#define UD_EXTERN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "udis86_types.h" + +/* ============================= PUBLIC API ================================= */ + +extern void ud_init(struct ud*); + +extern void ud_set_mode(struct ud*, uint8_t); + +extern void ud_set_pc(struct ud*, uint64_t); + +extern void ud_set_input_hook(struct ud*, int (*)(struct ud*)); + +extern void ud_set_input_buffer(struct ud*, uint8_t*, size_t); + +#ifndef __UD_STANDALONE__ +extern void ud_set_input_file(struct ud*, FILE*); +#endif /* __UD_STANDALONE__ */ + +extern void ud_set_vendor(struct ud*, unsigned); + +extern void ud_set_syntax(struct ud*, void (*)(struct ud*)); + +extern void ud_input_skip(struct ud*, size_t); + +extern int ud_input_end(struct ud*); + +extern unsigned int ud_decode(struct ud*); + +extern unsigned int ud_disassemble(struct ud*); + +extern void ud_translate_intel(struct ud*); + +extern void ud_translate_att(struct ud*); + +extern char* ud_insn_asm(struct ud* u); + +extern uint8_t* ud_insn_ptr(struct ud* u); + +extern uint64_t ud_insn_off(struct ud*); + +extern char* ud_insn_hex(struct ud*); + +extern unsigned int ud_insn_len(struct ud* u); + +extern const char* ud_lookup_mnemonic(enum ud_mnemonic_code c); + +extern void ud_set_user_opaque_data(struct ud*, void*); + +extern void *ud_get_user_opaque_data(struct ud*); + +/* ========================================================================== */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/3rdparty/masm/disassembler/udis86/udis86_input.c b/3rdparty/masm/disassembler/udis86/udis86_input.c new file mode 100644 index 0000000000..76c6cccf36 --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/udis86_input.c @@ -0,0 +1,263 @@ +/* udis86 - libudis86/input.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_extern.h" +#include "udis86_types.h" +#include "udis86_input.h" + +/* ----------------------------------------------------------------------------- + * inp_buff_hook() - Hook for buffered inputs. + * ----------------------------------------------------------------------------- + */ +static int +inp_buff_hook(struct ud* u) +{ + if (u->inp_buff < u->inp_buff_end) + return *u->inp_buff++; + else return -1; +} + +#ifndef __UD_STANDALONE__ +/* ----------------------------------------------------------------------------- + * inp_file_hook() - Hook for FILE inputs. + * ----------------------------------------------------------------------------- + */ +static int +inp_file_hook(struct ud* u) +{ + return fgetc(u->inp_file); +} +#endif /* __UD_STANDALONE__*/ + +/* ============================================================================= + * ud_inp_set_hook() - Sets input hook. + * ============================================================================= + */ +extern void +ud_set_input_hook(register struct ud* u, int (*hook)(struct ud*)) +{ + u->inp_hook = hook; + ud_inp_init(u); +} + +extern void +ud_set_user_opaque_data( struct ud * u, void * opaque ) +{ + u->user_opaque_data = opaque; +} + +extern void * +ud_get_user_opaque_data( struct ud * u ) +{ + return u->user_opaque_data; +} + +/* ============================================================================= + * ud_inp_set_buffer() - Set buffer as input. + * ============================================================================= + */ +extern void +ud_set_input_buffer(register struct ud* u, uint8_t* buf, size_t len) +{ + u->inp_hook = inp_buff_hook; + u->inp_buff = buf; + u->inp_buff_end = buf + len; + ud_inp_init(u); +} + +#ifndef __UD_STANDALONE__ +/* ============================================================================= + * ud_input_set_file() - Set buffer as input. + * ============================================================================= + */ +extern void +ud_set_input_file(register struct ud* u, FILE* f) +{ + u->inp_hook = inp_file_hook; + u->inp_file = f; + ud_inp_init(u); +} +#endif /* __UD_STANDALONE__ */ + +/* ============================================================================= + * ud_input_skip() - Skip n input bytes. + * ============================================================================= + */ +extern void +ud_input_skip(struct ud* u, size_t n) +{ + while (n--) { + u->inp_hook(u); + } +} + +/* ============================================================================= + * ud_input_end() - Test for end of input. + * ============================================================================= + */ +extern int +ud_input_end(struct ud* u) +{ + return (u->inp_curr == u->inp_fill) && u->inp_end; +} + +/* ----------------------------------------------------------------------------- + * ud_inp_next() - Loads and returns the next byte from input. + * + * inp_curr and inp_fill are pointers to the cache. The program is written based + * on the property that they are 8-bits in size, and will eventually wrap around + * forming a circular buffer. So, the size of the cache is 256 in size, kind of + * unnecessary yet optimized. + * + * A buffer inp_sess stores the bytes disassembled for a single session. + * ----------------------------------------------------------------------------- + */ +extern uint8_t ud_inp_next(struct ud* u) +{ + int c = -1; + /* if current pointer is not upto the fill point in the + * input cache. + */ + if ( u->inp_curr != u->inp_fill ) { + c = u->inp_cache[ ++u->inp_curr ]; + /* if !end-of-input, call the input hook and get a byte */ + } else if ( u->inp_end || ( c = u->inp_hook( u ) ) == -1 ) { + /* end-of-input, mark it as an error, since the decoder, + * expected a byte more. + */ + u->error = 1; + /* flag end of input */ + u->inp_end = 1; + return 0; + } else { + /* increment pointers, we have a new byte. */ + u->inp_curr = ++u->inp_fill; + /* add the byte to the cache */ + u->inp_cache[ u->inp_fill ] = c; + } + /* record bytes input per decode-session. */ + u->inp_sess[ u->inp_ctr++ ] = c; + /* return byte */ + return ( uint8_t ) c; +} + +/* ----------------------------------------------------------------------------- + * ud_inp_back() - Move back a single byte in the stream. + * ----------------------------------------------------------------------------- + */ +extern void +ud_inp_back(struct ud* u) +{ + if ( u->inp_ctr > 0 ) { + --u->inp_curr; + --u->inp_ctr; + } +} + +/* ----------------------------------------------------------------------------- + * ud_inp_peek() - Peek into the next byte in source. + * ----------------------------------------------------------------------------- + */ +extern uint8_t +ud_inp_peek(struct ud* u) +{ + uint8_t r = ud_inp_next(u); + if ( !u->error ) ud_inp_back(u); /* Don't backup if there was an error */ + return r; +} + +/* ----------------------------------------------------------------------------- + * ud_inp_move() - Move ahead n input bytes. + * ----------------------------------------------------------------------------- + */ +extern void +ud_inp_move(struct ud* u, size_t n) +{ + while (n--) + ud_inp_next(u); +} + +/*------------------------------------------------------------------------------ + * ud_inp_uintN() - return uintN from source. + *------------------------------------------------------------------------------ + */ +extern uint8_t +ud_inp_uint8(struct ud* u) +{ + return ud_inp_next(u); +} + +extern uint16_t +ud_inp_uint16(struct ud* u) +{ + uint16_t r, ret; + + ret = ud_inp_next(u); + r = ud_inp_next(u); + return ret | (r << 8); +} + +extern uint32_t +ud_inp_uint32(struct ud* u) +{ + uint32_t r, ret; + + ret = ud_inp_next(u); + r = ud_inp_next(u); + ret = ret | (r << 8); + r = ud_inp_next(u); + ret = ret | (r << 16); + r = ud_inp_next(u); + return ret | (r << 24); +} + +extern uint64_t +ud_inp_uint64(struct ud* u) +{ + uint64_t r, ret; + + ret = ud_inp_next(u); + r = ud_inp_next(u); + ret = ret | (r << 8); + r = ud_inp_next(u); + ret = ret | (r << 16); + r = ud_inp_next(u); + ret = ret | (r << 24); + r = ud_inp_next(u); + ret = ret | (r << 32); + r = ud_inp_next(u); + ret = ret | (r << 40); + r = ud_inp_next(u); + ret = ret | (r << 48); + r = ud_inp_next(u); + return ret | (r << 56); +} + +#endif // USE(UDIS86) diff --git a/3rdparty/masm/disassembler/udis86/udis86_input.h b/3rdparty/masm/disassembler/udis86/udis86_input.h new file mode 100644 index 0000000000..96865a88b5 --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/udis86_input.h @@ -0,0 +1,67 @@ +/* udis86 - libudis86/input.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_INPUT_H +#define UD_INPUT_H + +#include "udis86_types.h" + +uint8_t ud_inp_next(struct ud*); +uint8_t ud_inp_peek(struct ud*); +uint8_t ud_inp_uint8(struct ud*); +uint16_t ud_inp_uint16(struct ud*); +uint32_t ud_inp_uint32(struct ud*); +uint64_t ud_inp_uint64(struct ud*); +void ud_inp_move(struct ud*, size_t); +void ud_inp_back(struct ud*); + +/* ud_inp_init() - Initializes the input system. */ +#define ud_inp_init(u) \ +do { \ + u->inp_curr = 0; \ + u->inp_fill = 0; \ + u->inp_ctr = 0; \ + u->inp_end = 0; \ +} while (0) + +/* ud_inp_start() - Should be called before each de-code operation. */ +#define ud_inp_start(u) u->inp_ctr = 0 + +/* ud_inp_back() - Resets the current pointer to its position before the current + * instruction disassembly was started. + */ +#define ud_inp_reset(u) \ +do { \ + u->inp_curr -= u->inp_ctr; \ + u->inp_ctr = 0; \ +} while (0) + +/* ud_inp_sess() - Returns the pointer to current session. */ +#define ud_inp_sess(u) (u->inp_sess) + +/* inp_cur() - Returns the current input byte. */ +#define ud_inp_curr(u) ((u)->inp_cache[(u)->inp_curr]) + +#endif diff --git a/3rdparty/masm/disassembler/udis86/udis86_itab_holder.c b/3rdparty/masm/disassembler/udis86/udis86_itab_holder.c new file mode 100644 index 0000000000..d5d8726d6a --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/udis86_itab_holder.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_itab.c" + +#endif + diff --git a/3rdparty/masm/disassembler/udis86/udis86_syn-att.c b/3rdparty/masm/disassembler/udis86/udis86_syn-att.c new file mode 100644 index 0000000000..155a34ca29 --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/udis86_syn-att.c @@ -0,0 +1,253 @@ +/* udis86 - libudis86/syn-att.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_types.h" +#include "udis86_extern.h" +#include "udis86_decode.h" +#include "udis86_itab.h" +#include "udis86_syn.h" + +/* ----------------------------------------------------------------------------- + * opr_cast() - Prints an operand cast. + * ----------------------------------------------------------------------------- + */ +static void +opr_cast(struct ud* u, struct ud_operand* op) +{ + switch(op->size) { + case 16 : case 32 : + mkasm(u, "*"); break; + default: break; + } +} + +/* ----------------------------------------------------------------------------- + * gen_operand() - Generates assembly output for each operand. + * ----------------------------------------------------------------------------- + */ +static void +gen_operand(struct ud* u, struct ud_operand* op) +{ + switch(op->type) { + case UD_OP_REG: + mkasm(u, "%%%s", ud_reg_tab[op->base - UD_R_AL]); + break; + + case UD_OP_MEM: + if (u->br_far) opr_cast(u, op); + if (u->pfx_seg) + mkasm(u, "%%%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); + if (op->offset == 8) { + if (op->lval.sbyte < 0) + mkasm(u, "-0x%x", (-op->lval.sbyte) & 0xff); + else mkasm(u, "0x%x", op->lval.sbyte); + } + else if (op->offset == 16) + mkasm(u, "0x%x", op->lval.uword); + else if (op->offset == 32) + mkasm(u, "0x%lx", (unsigned long)op->lval.udword); + else if (op->offset == 64) + mkasm(u, "0x" FMT64 "x", op->lval.uqword); + + if (op->base) + mkasm(u, "(%%%s", ud_reg_tab[op->base - UD_R_AL]); + if (op->index) { + if (op->base) + mkasm(u, ","); + else mkasm(u, "("); + mkasm(u, "%%%s", ud_reg_tab[op->index - UD_R_AL]); + } + if (op->scale) + mkasm(u, ",%d", op->scale); + if (op->base || op->index) + mkasm(u, ")"); + break; + + case UD_OP_IMM: { + int64_t imm = 0; + uint64_t sext_mask = 0xffffffffffffffffull; + unsigned sext_size = op->size; + + switch (op->size) { + case 8: imm = op->lval.sbyte; break; + case 16: imm = op->lval.sword; break; + case 32: imm = op->lval.sdword; break; + case 64: imm = op->lval.sqword; break; + } + if ( P_SEXT( u->itab_entry->prefix ) ) { + sext_size = u->operand[ 0 ].size; + if ( u->mnemonic == UD_Ipush ) + /* push sign-extends to operand size */ + sext_size = u->opr_mode; + } + if ( sext_size < 64 ) + sext_mask = ( 1ull << sext_size ) - 1; + mkasm( u, "$0x" FMT64 "x", imm & sext_mask ); + + break; + } + + case UD_OP_JIMM: + switch (op->size) { + case 8: + mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sbyte); + break; + case 16: + mkasm(u, "0x" FMT64 "x", (u->pc + op->lval.sword) & 0xffff ); + break; + case 32: + if (u->dis_mode == 32) + mkasm(u, "0x" FMT64 "x", (u->pc + op->lval.sdword) & 0xffffffff); + else + mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sdword); + break; + default:break; + } + break; + + case UD_OP_PTR: + switch (op->size) { + case 32: + mkasm(u, "$0x%x, $0x%x", op->lval.ptr.seg, + op->lval.ptr.off & 0xFFFF); + break; + case 48: + mkasm(u, "$0x%x, $0x%lx", op->lval.ptr.seg, + (unsigned long)op->lval.ptr.off); + break; + } + break; + + default: return; + } +} + +/* ============================================================================= + * translates to AT&T syntax + * ============================================================================= + */ +extern void +ud_translate_att(struct ud *u) +{ + int size = 0; + + /* check if P_OSO prefix is used */ + if (! P_OSO(u->itab_entry->prefix) && u->pfx_opr) { + switch (u->dis_mode) { + case 16: + mkasm(u, "o32 "); + break; + case 32: + case 64: + mkasm(u, "o16 "); + break; + } + } + + /* check if P_ASO prefix was used */ + if (! P_ASO(u->itab_entry->prefix) && u->pfx_adr) { + switch (u->dis_mode) { + case 16: + mkasm(u, "a32 "); + break; + case 32: + mkasm(u, "a16 "); + break; + case 64: + mkasm(u, "a32 "); + break; + } + } + + if (u->pfx_lock) + mkasm(u, "lock "); + if (u->pfx_rep) + mkasm(u, "rep "); + if (u->pfx_repne) + mkasm(u, "repne "); + + /* special instructions */ + switch (u->mnemonic) { + case UD_Iretf: + mkasm(u, "lret "); + break; + case UD_Idb: + mkasm(u, ".byte 0x%x", u->operand[0].lval.ubyte); + return; + case UD_Ijmp: + case UD_Icall: + if (u->br_far) mkasm(u, "l"); + mkasm(u, "%s", ud_lookup_mnemonic(u->mnemonic)); + break; + case UD_Ibound: + case UD_Ienter: + if (u->operand[0].type != UD_NONE) + gen_operand(u, &u->operand[0]); + if (u->operand[1].type != UD_NONE) { + mkasm(u, ","); + gen_operand(u, &u->operand[1]); + } + return; + default: + mkasm(u, "%s", ud_lookup_mnemonic(u->mnemonic)); + } + + if (u->c1) + size = u->operand[0].size; + else if (u->c2) + size = u->operand[1].size; + else if (u->c3) + size = u->operand[2].size; + + if (size == 8) + mkasm(u, "b"); + else if (size == 16) + mkasm(u, "w"); + else if (size == 64) + mkasm(u, "q"); + + mkasm(u, " "); + + if (u->operand[2].type != UD_NONE) { + gen_operand(u, &u->operand[2]); + mkasm(u, ", "); + } + + if (u->operand[1].type != UD_NONE) { + gen_operand(u, &u->operand[1]); + mkasm(u, ", "); + } + + if (u->operand[0].type != UD_NONE) + gen_operand(u, &u->operand[0]); +} + +#endif // USE(UDIS86) + diff --git a/3rdparty/masm/disassembler/udis86/udis86_syn-intel.c b/3rdparty/masm/disassembler/udis86/udis86_syn-intel.c new file mode 100644 index 0000000000..d250bd449c --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/udis86_syn-intel.c @@ -0,0 +1,279 @@ +/* udis86 - libudis86/syn-intel.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_types.h" +#include "udis86_extern.h" +#include "udis86_decode.h" +#include "udis86_itab.h" +#include "udis86_syn.h" + +/* ----------------------------------------------------------------------------- + * opr_cast() - Prints an operand cast. + * ----------------------------------------------------------------------------- + */ +static void +opr_cast(struct ud* u, struct ud_operand* op) +{ + switch(op->size) { + case 8: mkasm(u, "byte " ); break; + case 16: mkasm(u, "word " ); break; + case 32: mkasm(u, "dword "); break; + case 64: mkasm(u, "qword "); break; + case 80: mkasm(u, "tword "); break; + default: break; + } + if (u->br_far) + mkasm(u, "far "); +} + +/* ----------------------------------------------------------------------------- + * gen_operand() - Generates assembly output for each operand. + * ----------------------------------------------------------------------------- + */ +static void gen_operand(struct ud* u, struct ud_operand* op, int syn_cast) +{ + switch(op->type) { + case UD_OP_REG: + mkasm(u, "%s", ud_reg_tab[op->base - UD_R_AL]); + break; + + case UD_OP_MEM: { + + int op_f = 0; + + if (syn_cast) + opr_cast(u, op); + + mkasm(u, "["); + + if (u->pfx_seg) + mkasm(u, "%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); + + if (op->base) { + mkasm(u, "%s", ud_reg_tab[op->base - UD_R_AL]); + op_f = 1; + } + + if (op->index) { + if (op_f) + mkasm(u, "+"); + mkasm(u, "%s", ud_reg_tab[op->index - UD_R_AL]); + op_f = 1; + } + + if (op->scale) + mkasm(u, "*%d", op->scale); + + if (op->offset == 8) { + if (op->lval.sbyte < 0) + mkasm(u, "-0x%x", -op->lval.sbyte); + else mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.sbyte); + } + else if (op->offset == 16) + mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.uword); + else if (op->offset == 32) { + if (u->adr_mode == 64) { + if (op->lval.sdword < 0) + mkasm(u, "-0x%x", -op->lval.sdword); + else mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.sdword); + } + else mkasm(u, "%s0x%lx", (op_f) ? "+" : "", (unsigned long)op->lval.udword); + } + else if (op->offset == 64) + mkasm(u, "%s0x" FMT64 "x", (op_f) ? "+" : "", op->lval.uqword); + + mkasm(u, "]"); + break; + } + + case UD_OP_IMM: { + int64_t imm = 0; + uint64_t sext_mask = 0xffffffffffffffffull; + unsigned sext_size = op->size; + + if (syn_cast) + opr_cast(u, op); + switch (op->size) { + case 8: imm = op->lval.sbyte; break; + case 16: imm = op->lval.sword; break; + case 32: imm = op->lval.sdword; break; + case 64: imm = op->lval.sqword; break; + } + if ( P_SEXT( u->itab_entry->prefix ) ) { + sext_size = u->operand[ 0 ].size; + if ( u->mnemonic == UD_Ipush ) + /* push sign-extends to operand size */ + sext_size = u->opr_mode; + } + if ( sext_size < 64 ) + sext_mask = ( 1ull << sext_size ) - 1; + mkasm( u, "0x" FMT64 "x", imm & sext_mask ); + + break; + } + + + case UD_OP_JIMM: + if (syn_cast) opr_cast(u, op); + switch (op->size) { + case 8: + mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sbyte); + break; + case 16: + mkasm(u, "0x" FMT64 "x", ( u->pc + op->lval.sword ) & 0xffff ); + break; + case 32: + mkasm(u, "0x" FMT64 "x", ( u->pc + op->lval.sdword ) & 0xfffffffful ); + break; + default:break; + } + break; + + case UD_OP_PTR: + switch (op->size) { + case 32: + mkasm(u, "word 0x%x:0x%x", op->lval.ptr.seg, + op->lval.ptr.off & 0xFFFF); + break; + case 48: + mkasm(u, "dword 0x%x:0x%lx", op->lval.ptr.seg, + (unsigned long)op->lval.ptr.off); + break; + } + break; + + case UD_OP_CONST: + if (syn_cast) opr_cast(u, op); + mkasm(u, "%d", op->lval.udword); + break; + + default: return; + } +} + +/* ============================================================================= + * translates to intel syntax + * ============================================================================= + */ +extern void ud_translate_intel(struct ud* u) +{ + /* -- prefixes -- */ + + /* check if P_OSO prefix is used */ + if (! P_OSO(u->itab_entry->prefix) && u->pfx_opr) { + switch (u->dis_mode) { + case 16: + mkasm(u, "o32 "); + break; + case 32: + case 64: + mkasm(u, "o16 "); + break; + } + } + + /* check if P_ASO prefix was used */ + if (! P_ASO(u->itab_entry->prefix) && u->pfx_adr) { + switch (u->dis_mode) { + case 16: + mkasm(u, "a32 "); + break; + case 32: + mkasm(u, "a16 "); + break; + case 64: + mkasm(u, "a32 "); + break; + } + } + + if ( u->pfx_seg && + u->operand[0].type != UD_OP_MEM && + u->operand[1].type != UD_OP_MEM ) { + mkasm(u, "%s ", ud_reg_tab[u->pfx_seg - UD_R_AL]); + } + if (u->pfx_lock) + mkasm(u, "lock "); + if (u->pfx_rep) + mkasm(u, "rep "); + if (u->pfx_repne) + mkasm(u, "repne "); + + /* print the instruction mnemonic */ + mkasm(u, "%s ", ud_lookup_mnemonic(u->mnemonic)); + + /* operand 1 */ + if (u->operand[0].type != UD_NONE) { + int cast = 0; + if ( u->operand[0].type == UD_OP_IMM && + u->operand[1].type == UD_NONE ) + cast = u->c1; + if ( u->operand[0].type == UD_OP_MEM ) { + cast = u->c1; + if ( u->operand[1].type == UD_OP_IMM || + u->operand[1].type == UD_OP_CONST ) + cast = 1; + if ( u->operand[1].type == UD_NONE ) + cast = 1; + if ( ( u->operand[0].size != u->operand[1].size ) && u->operand[1].size ) + cast = 1; + } else if ( u->operand[ 0 ].type == UD_OP_JIMM ) { + if ( u->operand[ 0 ].size > 8 ) cast = 1; + } + gen_operand(u, &u->operand[0], cast); + } + /* operand 2 */ + if (u->operand[1].type != UD_NONE) { + int cast = 0; + mkasm(u, ", "); + if ( u->operand[1].type == UD_OP_MEM ) { + cast = u->c1; + + if ( u->operand[0].type != UD_OP_REG ) + cast = 1; + if ( u->operand[0].size != u->operand[1].size && u->operand[1].size ) + cast = 1; + if ( u->operand[0].type == UD_OP_REG && + u->operand[0].base >= UD_R_ES && + u->operand[0].base <= UD_R_GS ) + cast = 0; + } + gen_operand(u, &u->operand[1], cast ); + } + + /* operand 3 */ + if (u->operand[2].type != UD_NONE) { + mkasm(u, ", "); + gen_operand(u, &u->operand[2], u->c3); + } +} + +#endif // USE(UDIS86) + diff --git a/3rdparty/masm/disassembler/udis86/udis86_syn.c b/3rdparty/masm/disassembler/udis86/udis86_syn.c new file mode 100644 index 0000000000..80391b4a08 --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/udis86_syn.c @@ -0,0 +1,87 @@ +/* udis86 - libudis86/syn.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include + +#if USE(UDIS86) + +/* ----------------------------------------------------------------------------- + * Intel Register Table - Order Matters (types.h)! + * ----------------------------------------------------------------------------- + */ +const char* ud_reg_tab[] = +{ + "al", "cl", "dl", "bl", + "ah", "ch", "dh", "bh", + "spl", "bpl", "sil", "dil", + "r8b", "r9b", "r10b", "r11b", + "r12b", "r13b", "r14b", "r15b", + + "ax", "cx", "dx", "bx", + "sp", "bp", "si", "di", + "r8w", "r9w", "r10w", "r11w", + "r12w", "r13W" , "r14w", "r15w", + + "eax", "ecx", "edx", "ebx", + "esp", "ebp", "esi", "edi", + "r8d", "r9d", "r10d", "r11d", + "r12d", "r13d", "r14d", "r15d", + + "rax", "rcx", "rdx", "rbx", + "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", + "r12", "r13", "r14", "r15", + + "es", "cs", "ss", "ds", + "fs", "gs", + + "cr0", "cr1", "cr2", "cr3", + "cr4", "cr5", "cr6", "cr7", + "cr8", "cr9", "cr10", "cr11", + "cr12", "cr13", "cr14", "cr15", + + "dr0", "dr1", "dr2", "dr3", + "dr4", "dr5", "dr6", "dr7", + "dr8", "dr9", "dr10", "dr11", + "dr12", "dr13", "dr14", "dr15", + + "mm0", "mm1", "mm2", "mm3", + "mm4", "mm5", "mm6", "mm7", + + "st0", "st1", "st2", "st3", + "st4", "st5", "st6", "st7", + + "xmm0", "xmm1", "xmm2", "xmm3", + "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10", "xmm11", + "xmm12", "xmm13", "xmm14", "xmm15", + + "rip" +}; + +#endif // USE(UDIS86) + diff --git a/3rdparty/masm/disassembler/udis86/udis86_syn.h b/3rdparty/masm/disassembler/udis86/udis86_syn.h new file mode 100644 index 0000000000..e8636163ef --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/udis86_syn.h @@ -0,0 +1,47 @@ +/* udis86 - libudis86/syn.h + * + * Copyright (c) 2002-2009 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_SYN_H +#define UD_SYN_H + +#include "udis86_types.h" +#include + +#ifndef __UD_STANDALONE__ +# include +#endif /* __UD_STANDALONE__ */ + +extern const char* ud_reg_tab[]; + +static void mkasm(struct ud* u, const char* fmt, ...) WTF_ATTRIBUTE_PRINTF(2, 3); +static void mkasm(struct ud* u, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + u->insn_fill += vsnprintf((char*) u->insn_buffer + u->insn_fill, UD_STRING_BUFFER_SIZE - u->insn_fill, fmt, ap); + va_end(ap); +} + +#endif diff --git a/3rdparty/masm/disassembler/udis86/udis86_types.h b/3rdparty/masm/disassembler/udis86/udis86_types.h new file mode 100644 index 0000000000..320d1ca491 --- /dev/null +++ b/3rdparty/masm/disassembler/udis86/udis86_types.h @@ -0,0 +1,238 @@ +/* udis86 - libudis86/types.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_TYPES_H +#define UD_TYPES_H + +#ifndef __UD_STANDALONE__ +# include +#endif /* __UD_STANDALONE__ */ + +/* gcc specific extensions */ +#ifdef __GNUC__ +# define UD_ATTR_PACKED __attribute__((packed)) +#else +# define UD_ATTR_PACKED +#endif /* UD_ATTR_PACKED */ + +#ifdef _MSC_VER +# define FMT64 "%I64" + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; +#else +# define FMT64 "%ll" +# ifndef __UD_STANDALONE__ +# include +# endif /* __UD_STANDALONE__ */ +#endif + +/* ----------------------------------------------------------------------------- + * All possible "types" of objects in udis86. Order is Important! + * ----------------------------------------------------------------------------- + */ +enum ud_type +{ + UD_NONE, + + /* 8 bit GPRs */ + UD_R_AL, UD_R_CL, UD_R_DL, UD_R_BL, + UD_R_AH, UD_R_CH, UD_R_DH, UD_R_BH, + UD_R_SPL, UD_R_BPL, UD_R_SIL, UD_R_DIL, + UD_R_R8B, UD_R_R9B, UD_R_R10B, UD_R_R11B, + UD_R_R12B, UD_R_R13B, UD_R_R14B, UD_R_R15B, + + /* 16 bit GPRs */ + UD_R_AX, UD_R_CX, UD_R_DX, UD_R_BX, + UD_R_SP, UD_R_BP, UD_R_SI, UD_R_DI, + UD_R_R8W, UD_R_R9W, UD_R_R10W, UD_R_R11W, + UD_R_R12W, UD_R_R13W, UD_R_R14W, UD_R_R15W, + + /* 32 bit GPRs */ + UD_R_EAX, UD_R_ECX, UD_R_EDX, UD_R_EBX, + UD_R_ESP, UD_R_EBP, UD_R_ESI, UD_R_EDI, + UD_R_R8D, UD_R_R9D, UD_R_R10D, UD_R_R11D, + UD_R_R12D, UD_R_R13D, UD_R_R14D, UD_R_R15D, + + /* 64 bit GPRs */ + UD_R_RAX, UD_R_RCX, UD_R_RDX, UD_R_RBX, + UD_R_RSP, UD_R_RBP, UD_R_RSI, UD_R_RDI, + UD_R_R8, UD_R_R9, UD_R_R10, UD_R_R11, + UD_R_R12, UD_R_R13, UD_R_R14, UD_R_R15, + + /* segment registers */ + UD_R_ES, UD_R_CS, UD_R_SS, UD_R_DS, + UD_R_FS, UD_R_GS, + + /* control registers*/ + UD_R_CR0, UD_R_CR1, UD_R_CR2, UD_R_CR3, + UD_R_CR4, UD_R_CR5, UD_R_CR6, UD_R_CR7, + UD_R_CR8, UD_R_CR9, UD_R_CR10, UD_R_CR11, + UD_R_CR12, UD_R_CR13, UD_R_CR14, UD_R_CR15, + + /* debug registers */ + UD_R_DR0, UD_R_DR1, UD_R_DR2, UD_R_DR3, + UD_R_DR4, UD_R_DR5, UD_R_DR6, UD_R_DR7, + UD_R_DR8, UD_R_DR9, UD_R_DR10, UD_R_DR11, + UD_R_DR12, UD_R_DR13, UD_R_DR14, UD_R_DR15, + + /* mmx registers */ + UD_R_MM0, UD_R_MM1, UD_R_MM2, UD_R_MM3, + UD_R_MM4, UD_R_MM5, UD_R_MM6, UD_R_MM7, + + /* x87 registers */ + UD_R_ST0, UD_R_ST1, UD_R_ST2, UD_R_ST3, + UD_R_ST4, UD_R_ST5, UD_R_ST6, UD_R_ST7, + + /* extended multimedia registers */ + UD_R_XMM0, UD_R_XMM1, UD_R_XMM2, UD_R_XMM3, + UD_R_XMM4, UD_R_XMM5, UD_R_XMM6, UD_R_XMM7, + UD_R_XMM8, UD_R_XMM9, UD_R_XMM10, UD_R_XMM11, + UD_R_XMM12, UD_R_XMM13, UD_R_XMM14, UD_R_XMM15, + + UD_R_RIP, + + /* Operand Types */ + UD_OP_REG, UD_OP_MEM, UD_OP_PTR, UD_OP_IMM, + UD_OP_JIMM, UD_OP_CONST +}; + +#include "udis86_itab.h" + +/* ----------------------------------------------------------------------------- + * struct ud_operand - Disassembled instruction Operand. + * ----------------------------------------------------------------------------- + */ +struct ud_operand +{ + enum ud_type type; + uint8_t size; + union { + int8_t sbyte; + uint8_t ubyte; + int16_t sword; + uint16_t uword; + int32_t sdword; + uint32_t udword; + int64_t sqword; + uint64_t uqword; + + struct { + uint16_t seg; + uint32_t off; + } ptr; + } lval; + + enum ud_type base; + enum ud_type index; + uint8_t offset; + uint8_t scale; +}; + +#define UD_STRING_BUFFER_SIZE 64 + +/* ----------------------------------------------------------------------------- + * struct ud - The udis86 object. + * ----------------------------------------------------------------------------- + */ +struct ud +{ + int (*inp_hook) (struct ud*); + uint8_t inp_curr; + uint8_t inp_fill; +#ifndef __UD_STANDALONE__ + FILE* inp_file; +#endif + uint8_t inp_ctr; + uint8_t* inp_buff; + uint8_t* inp_buff_end; + uint8_t inp_end; + void (*translator)(struct ud*); + uint64_t insn_offset; + char insn_hexcode[32]; + char insn_buffer[UD_STRING_BUFFER_SIZE]; + unsigned int insn_fill; + uint8_t dis_mode; + uint64_t pc; + uint8_t vendor; + struct map_entry* mapen; + enum ud_mnemonic_code mnemonic; + struct ud_operand operand[3]; + uint8_t error; + uint8_t pfx_rex; + uint8_t pfx_seg; + uint8_t pfx_opr; + uint8_t pfx_adr; + uint8_t pfx_lock; + uint8_t pfx_rep; + uint8_t pfx_repe; + uint8_t pfx_repne; + uint8_t pfx_insn; + uint8_t default64; + uint8_t opr_mode; + uint8_t adr_mode; + uint8_t br_far; + uint8_t br_near; + uint8_t implicit_addr; + uint8_t c1; + uint8_t c2; + uint8_t c3; + uint8_t inp_cache[256]; + uint8_t inp_sess[64]; + uint8_t have_modrm; + uint8_t modrm; + void * user_opaque_data; + struct ud_itab_entry * itab_entry; + struct ud_lookup_table_list_entry *le; +}; + +/* ----------------------------------------------------------------------------- + * Type-definitions + * ----------------------------------------------------------------------------- + */ +typedef enum ud_type ud_type_t; +typedef enum ud_mnemonic_code ud_mnemonic_code_t; + +typedef struct ud ud_t; +typedef struct ud_operand ud_operand_t; + +#define UD_SYN_INTEL ud_translate_intel +#define UD_SYN_ATT ud_translate_att +#define UD_EOI -1 +#define UD_INP_CACHE_SZ 32 +#define UD_VENDOR_AMD 0 +#define UD_VENDOR_INTEL 1 +#define UD_VENDOR_ANY 2 + +#define bail_out(ud,error_code) longjmp( (ud)->bailout, error_code ) +#define try_decode(ud) if ( setjmp( (ud)->bailout ) == 0 ) +#define catch_error() else + +#endif diff --git a/3rdparty/masm/jit/JITCompilationEffort.h b/3rdparty/masm/jit/JITCompilationEffort.h new file mode 100644 index 0000000000..5eb6801789 --- /dev/null +++ b/3rdparty/masm/jit/JITCompilationEffort.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JITCompilationEffort_h +#define JITCompilationEffort_h + +namespace JSC { + +enum JITCompilationEffort { + JITCompilationCanFail, + JITCompilationMustSucceed +}; + +} // namespace JSC + +#endif // JITCompilationEffort_h + diff --git a/3rdparty/masm/masm.pri b/3rdparty/masm/masm.pri new file mode 100644 index 0000000000..a6d11f633c --- /dev/null +++ b/3rdparty/masm/masm.pri @@ -0,0 +1,107 @@ + +HEADERS += $$PWD/assembler/*.h +SOURCES += $$PWD/assembler/ARMAssembler.cpp +SOURCES += $$PWD/assembler/ARMv7Assembler.cpp +SOURCES += $$PWD/assembler/MacroAssemblerARM.cpp +SOURCES += $$PWD/assembler/MacroAssemblerSH4.cpp +SOURCES += $$PWD/assembler/LinkBuffer.cpp + +SOURCES += $$PWD/wtf/PrintStream.cpp +HEADERS += $$PWD/wtf/PrintStream.h + +SOURCES += $$PWD/wtf/FilePrintStream.cpp +HEADERS += $$PWD/wtf/FilePrintStream.h + +HEADERS += $$PWD/wtf/RawPointer.h + +win32: SOURCES += $$PWD/wtf/OSAllocatorWin.cpp +else: SOURCES += $$PWD/wtf/OSAllocatorPosix.cpp +HEADERS += $$PWD/wtf/OSAllocator.h + +SOURCES += $$PWD/wtf/PageAllocationAligned.cpp +HEADERS += $$PWD/wtf/PageAllocationAligned.h +HEADERS += $$PWD/wtf/PageAllocation.h + +SOURCES += $$PWD/wtf/PageBlock.cpp +HEADERS += $$PWD/wtf/PageBlock.h + +HEADERS += $$PWD/wtf/PageReservation.h + +SOURCES += $$PWD/stubs/WTFStubs.cpp +HEADERS += $$PWD/stubs/WTFStubs.h + +DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" + +DEFINES += ENABLE_LLINT=0 +DEFINES += ENABLE_DFG_JIT=0 +DEFINES += ENABLE_JIT=1 +DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 +DEFINES += ENABLE_ASSEMBLER=1 + +DEFINES += BUILDING_QT__ + +INCLUDEPATH += $$PWD/jit +INCLUDEPATH += $$PWD/assembler +INCLUDEPATH += $$PWD/runtime +INCLUDEPATH += $$PWD/wtf +INCLUDEPATH += $$PWD/stubs +INCLUDEPATH += $$PWD/stubs/wtf +INCLUDEPATH += $$PWD + +DEFINES += WTF_USE_UDIS86=1 +INCLUDEPATH += $$PWD/disassembler +INCLUDEPATH += $$PWD/disassembler/udis86 +INCLUDEPATH += $$_OUT_PWD +SOURCES += $$PWD/disassembler/Disassembler.cpp +SOURCES += $$PWD/disassembler/UDis86Disassembler.cpp +SOURCES += $$PWD/disassembler/udis86/udis86.c +SOURCES += $$PWD/disassembler/udis86/udis86_decode.c +SOURCES += $$PWD/disassembler/udis86/udis86_input.c +SOURCES += $$PWD/disassembler/udis86/udis86_itab_holder.c +SOURCES += $$PWD/disassembler/udis86/udis86_syn-att.c +SOURCES += $$PWD/disassembler/udis86/udis86_syn.c +SOURCES += $$PWD/disassembler/udis86/udis86_syn-intel.c + +DEFINES += ENABLE_YARR_JIT=0 +SOURCES += \ + $$PWD/yarr/YarrCanonicalizeUCS2.cpp \ + $$PWD/yarr/YarrInterpreter.cpp \ + $$PWD/yarr/YarrPattern.cpp \ + $$PWD/yarr/YarrSyntaxChecker.cpp + +HEADERS += $$PWD/yarr/*.h + +retgen.output = RegExpJitTables.h +retgen.script = $$PWD/create_regex_tables +retgen.input = retgen.script +retgen.CONFIG += no_link +retgen.commands = python $$retgen.script > ${QMAKE_FILE_OUT} +QMAKE_EXTRA_COMPILERS += retgen + +ITAB = $$PWD/disassembler/udis86/optable.xml +udis86.output = udis86_itab.h +udis86.input = ITAB +udis86.CONFIG += no_link +udis86.commands = python $$PWD/disassembler/udis86/itab.py ${QMAKE_FILE_IN} +QMAKE_EXTRA_COMPILERS += udis86 + +udis86_tab_cfile.target = $$OUT_PWD/udis86_itab.c +udis86_tab_cfile.depends = udis86_itab.h +QMAKE_EXTRA_TARGETS += udis86_tab_cfile + +# Taken from WebKit/Tools/qmake/mkspecs/features/unix/default_post.prf +linux-g++* { + greaterThan(QT_GCC_MAJOR_VERSION, 3):greaterThan(QT_GCC_MINOR_VERSION, 5) { + !contains(QMAKE_CXXFLAGS, -std=(c|gnu)\\+\\+(0x|11)) { + # We need to deactivate those warnings because some names conflicts with upcoming c++0x types (e.g.nullptr). + QMAKE_CXXFLAGS_WARN_ON += -Wno-c++0x-compat + QMAKE_CXXFLAGS += -Wno-c++0x-compat + } + } +} + +# Don't warn about OVERRIDE and FINAL, since they are feature-checked anyways +*clang:!contains(QMAKE_CXXFLAGS, -std=c++11) { + QMAKE_CXXFLAGS += -Wno-c++11-extensions + QMAKE_OBJECTIVE_CFLAGS += -Wno-c++11-extensions +} diff --git a/3rdparty/masm/runtime/MatchResult.h b/3rdparty/masm/runtime/MatchResult.h new file mode 100644 index 0000000000..d87c8516b0 --- /dev/null +++ b/3rdparty/masm/runtime/MatchResult.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MatchResult_h +#define MatchResult_h + +typedef uint64_t EncodedMatchResult; + +struct MatchResult { + ALWAYS_INLINE MatchResult(size_t start, size_t end) + : start(start) + , end(end) + { + } + + explicit ALWAYS_INLINE MatchResult(EncodedMatchResult encoded) + { + union u { + uint64_t encoded; + struct s { + size_t start; + size_t end; + } split; + } value; + value.encoded = encoded; + start = value.split.start; + end = value.split.end; + } + + ALWAYS_INLINE static MatchResult failed() + { + return MatchResult(WTF::notFound, 0); + } + + ALWAYS_INLINE operator bool() + { + return start != WTF::notFound; + } + + ALWAYS_INLINE bool empty() + { + return start == end; + } + + size_t start; + size_t end; +}; + +#endif diff --git a/3rdparty/masm/stubs/ExecutableAllocator.h b/3rdparty/masm/stubs/ExecutableAllocator.h new file mode 100644 index 0000000000..7b3004aa90 --- /dev/null +++ b/3rdparty/masm/stubs/ExecutableAllocator.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef MASM_EXECUTABLEALLOCATOR_H +#define MASM_EXECUTABLEALLOCATOR_H + +#include +#include + +#include +#include + +namespace JSC { + +class JSGlobalData; + +struct ExecutableMemoryHandle : public RefCounted { + ExecutableMemoryHandle(int size) + : m_size(size) + { + static size_t pageSize = sysconf(_SC_PAGESIZE); + m_size = (m_size + pageSize - 1) & ~(pageSize - 1); +#if OS(DARWIN) +# define MAP_ANONYMOUS MAP_ANON +#endif + m_data = mmap(0, m_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + } + ~ExecutableMemoryHandle() + { + munmap(m_data, m_size); + } + + inline void shrink(size_t) { + // ### TODO. + } + + inline bool isManaged() const { return true; } + + void* start() { return m_data; } + int sizeInBytes() { return m_size; } + + void* m_data; + int m_size; +}; + +struct ExecutableAllocator { + PassRefPtr allocate(JSGlobalData&, int size, void*, int) + { + return adoptRef(new ExecutableMemoryHandle(size)); + } + + static void makeWritable(void*, int) + { + } + + static void makeExecutable(void* addr, int size) + { + static size_t pageSize = sysconf(_SC_PAGESIZE); + size_t iaddr = reinterpret_cast(addr); + size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); + int mode = PROT_READ | PROT_WRITE | PROT_EXEC; + mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode); + } +}; + +} + +#endif // MASM_EXECUTABLEALLOCATOR_H diff --git a/3rdparty/masm/stubs/JSGlobalData.h b/3rdparty/masm/stubs/JSGlobalData.h new file mode 100644 index 0000000000..112bedddd7 --- /dev/null +++ b/3rdparty/masm/stubs/JSGlobalData.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef MASM_JSGLOBALDATA_H +#define MASM_JSGLOBALDATA_H + +#include "ExecutableAllocator.h" +#include "WeakRandom.h" + +namespace JSC { + +class JSGlobalData { +public: + ExecutableAllocator executableAllocator; +}; + +} + +#endif // MASM_JSGLOBALDATA_H diff --git a/3rdparty/masm/stubs/LLIntData.h b/3rdparty/masm/stubs/LLIntData.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/3rdparty/masm/stubs/Options.h b/3rdparty/masm/stubs/Options.h new file mode 100644 index 0000000000..b95e4354e2 --- /dev/null +++ b/3rdparty/masm/stubs/Options.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef OPTIONS_H +#define OPTIONS_H + +namespace JSC { + +struct Options { + static bool showDisassembly() { return true; } + static bool showDFGDisassembly() { return true; } +}; + +} + +#endif // MASM_STUBS/OPTIONS_H diff --git a/3rdparty/masm/stubs/WTFStubs.cpp b/3rdparty/masm/stubs/WTFStubs.cpp new file mode 100644 index 0000000000..530804fe3e --- /dev/null +++ b/3rdparty/masm/stubs/WTFStubs.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include +#include +#include + +namespace WTF { + +void* fastMalloc(size_t size) +{ + return malloc(size); +} + +void* fastRealloc(void* ptr, size_t size) +{ + return realloc(ptr, size); +} + +void fastFree(void* ptr) +{ + free(ptr); +} + +uint32_t cryptographicallyRandomNumber() +{ + return 0; +} + +static FilePrintStream* s_dataFile; + +void setDataFile(FILE* f) +{ + delete s_dataFile; + s_dataFile = new FilePrintStream(f, FilePrintStream::Borrow); +} + +FilePrintStream& dataFile() +{ + if (!s_dataFile) + s_dataFile = new FilePrintStream(stderr, FilePrintStream::Borrow); + return *s_dataFile; +} + +void dataLogFV(const char* format, va_list args) +{ + char buffer[1024]; + vsnprintf(buffer, sizeof(buffer), format, args); + qDebug("%s", buffer); +} + +void dataLogF(const char* format, ...) +{ + char buffer[1024]; + va_list args; + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + qDebug("%s", buffer); +} + +void dataLogFString(const char* str) +{ + qDebug("%s", str); +} + +} + +extern "C" { + +void WTFReportAssertionFailure(const char* /*file*/, int /*line*/, const char* /*function*/, const char* /*assertion*/) +{ +} + +void WTFReportBacktrace() +{ +} + +void WTFInvokeCrashHook() +{ +} + +} + + +#if ENABLE(ASSEMBLER) && CPU(X86) && !OS(MAC_OS_X) +#include + +JSC::MacroAssemblerX86Common::SSE2CheckState JSC::MacroAssemblerX86Common::s_sse2CheckState = JSC::MacroAssemblerX86Common::NotCheckedSSE2; +#endif + diff --git a/3rdparty/masm/stubs/WTFStubs.h b/3rdparty/masm/stubs/WTFStubs.h new file mode 100644 index 0000000000..ec77d25da7 --- /dev/null +++ b/3rdparty/masm/stubs/WTFStubs.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef WTFSTUBS_H +#define WTFSTUBS_H + +namespace WTF { + +void setDataFile(FILE* f); + +} + +#endif // WTFSTUBS_H diff --git a/3rdparty/masm/stubs/wtf/FastAllocBase.h b/3rdparty/masm/stubs/wtf/FastAllocBase.h new file mode 100644 index 0000000000..a062a885af --- /dev/null +++ b/3rdparty/masm/stubs/wtf/FastAllocBase.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef FASTALLOCBASE_H +#define FASTALLOCBASE_H + +/* Dummy empty header file, only needed for #include source compatibility */ + +#define WTF_MAKE_FAST_ALLOCATED + +#endif // FASTALLOCBASE_H diff --git a/3rdparty/masm/stubs/wtf/FastMalloc.h b/3rdparty/masm/stubs/wtf/FastMalloc.h new file mode 100644 index 0000000000..1248c79dec --- /dev/null +++ b/3rdparty/masm/stubs/wtf/FastMalloc.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef FASTMALLOC_H +#define FASTMALLOC_H + +/* Dummy empty header file, only needed for #include source compatibility */ + +#endif // FASTMALLOC_H diff --git a/3rdparty/masm/stubs/wtf/Noncopyable.h b/3rdparty/masm/stubs/wtf/Noncopyable.h new file mode 100644 index 0000000000..d3d1eed6d1 --- /dev/null +++ b/3rdparty/masm/stubs/wtf/Noncopyable.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef NONCOPYABLE_H +#define NONCOPYABLE_H + +#include + +#define WTF_MAKE_NONCOPYABLE(x) Q_DISABLE_COPY(x) + +#endif // NONCOPYABLE_H diff --git a/3rdparty/masm/stubs/wtf/OwnPtr.h b/3rdparty/masm/stubs/wtf/OwnPtr.h new file mode 100644 index 0000000000..31d2f1efa3 --- /dev/null +++ b/3rdparty/masm/stubs/wtf/OwnPtr.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef OWNPTR_H +#define OWNPTR_H + +#include "PassOwnPtr.h" + +#endif // OWNPTR_H diff --git a/3rdparty/masm/stubs/wtf/PassOwnPtr.h b/3rdparty/masm/stubs/wtf/PassOwnPtr.h new file mode 100644 index 0000000000..f9b84e7b57 --- /dev/null +++ b/3rdparty/masm/stubs/wtf/PassOwnPtr.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef PASSOWNPTR_H +#define PASSOWNPTR_H + +#include + +template class PassOwnPtr; +template PassOwnPtr adoptPtr(PtrType*); + +template +struct OwnPtr : public QScopedPointer +{ + OwnPtr() {} + OwnPtr(const PassOwnPtr &ptr) + : QScopedPointer(ptr.leakRef()) + {} + + OwnPtr& operator=(const OwnPtr& other) + { + this->reset(const_cast &>(other).take()); + return *this; + } + + T* get() const { return this->data(); } + + PassOwnPtr release() + { + return adoptPtr(this->take()); + } +}; + +template +class PassOwnPtr { +public: + PassOwnPtr() {} + + PassOwnPtr(T* ptr) + : m_ptr(ptr) + { + } + + PassOwnPtr(const PassOwnPtr& other) + : m_ptr(other.leakRef()) + { + } + + PassOwnPtr(const OwnPtr& other) + : m_ptr(other.take()) + { + } + + ~PassOwnPtr() + { + } + + T* operator->() const { return m_ptr.data(); } + + T* leakRef() const { return m_ptr.take(); } + +private: + template friend PassOwnPtr adoptPtr(PtrType*); + + PassOwnPtr& operator=(const PassOwnPtr&) + {} + mutable QScopedPointer m_ptr; +}; + +template +PassOwnPtr adoptPtr(T* ptr) +{ + PassOwnPtr result; + result.m_ptr.reset(ptr); + return result; +} + + +#endif // PASSOWNPTR_H diff --git a/3rdparty/masm/stubs/wtf/PassRefPtr.h b/3rdparty/masm/stubs/wtf/PassRefPtr.h new file mode 100644 index 0000000000..d97be1c330 --- /dev/null +++ b/3rdparty/masm/stubs/wtf/PassRefPtr.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef PASSREFPTR_H +#define PASSREFPTR_H + +template class RefPtr; + +template +class PassRefPtr { +public: + PassRefPtr() : m_ptr(0) {} + + PassRefPtr(T* ptr) + : m_ptr(ptr) + { + if (m_ptr) + m_ptr->ref(); + } + + PassRefPtr(const PassRefPtr& other) + : m_ptr(other.leakRef()) + { + } + + PassRefPtr(const RefPtr& other) + : m_ptr(other.get()) + { + if (m_ptr) + m_ptr->ref(); + } + + ~PassRefPtr() + { + if (m_ptr) + m_ptr->deref(); + } + + T* operator->() const { return m_ptr; } + + T* leakRef() const + { + T* result = m_ptr; + m_ptr = 0; + return result; + } + +private: + PassRefPtr& operator=(const PassRefPtr&) + {} + + template friend PassRefPtr adoptRef(PtrType*); + mutable T* m_ptr; +}; + +template +PassRefPtr adoptRef(T* ptr) +{ + PassRefPtr result; + result.m_ptr = ptr; + return result; +} + +#endif // PASSREFPTR_H diff --git a/3rdparty/masm/stubs/wtf/RefCounted.h b/3rdparty/masm/stubs/wtf/RefCounted.h new file mode 100644 index 0000000000..4fc9ad9074 --- /dev/null +++ b/3rdparty/masm/stubs/wtf/RefCounted.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef REFCOUNTED_H +#define REFCOUNTED_H + +#include "PassRefPtr.h" + +template +class RefCounted { +public: + RefCounted() : m_refCount(1) {} + ~RefCounted() + { + deref(); + } + + void ref() + { + ++m_refCount; + } + + void deref() + { + if (!--m_refCount) + delete static_cast(this); + } + +protected: + int m_refCount; +}; + +#endif // REFCOUNTED_H diff --git a/3rdparty/masm/stubs/wtf/RefPtr.h b/3rdparty/masm/stubs/wtf/RefPtr.h new file mode 100644 index 0000000000..929b493b4b --- /dev/null +++ b/3rdparty/masm/stubs/wtf/RefPtr.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef REFPTR_H +#define REFPTR_H + +#include "PassRefPtr.h" + +template +class RefPtr { +public: + RefPtr() : m_ptr(0) {} + RefPtr(const RefPtr &other) + : m_ptr(other.m_ptr) + { + if (m_ptr) + m_ptr->ref(); + } + + RefPtr& operator=(const RefPtr& other) + { + if (other.m_ptr) + other.m_ptr->ref(); + if (m_ptr) + m_ptr->deref(); + m_ptr = other.m_ptr; + return *this; + } + + RefPtr(const PassRefPtr& other) + : m_ptr(other.leakRef()) + { + } + + ~RefPtr() + { + if (m_ptr) + m_ptr->deref(); + } + + T* operator->() const { return m_ptr; } + T* get() const { return m_ptr; } + bool operator!() const { return !m_ptr; } + + PassRefPtr release() + { + T* ptr = m_ptr; + m_ptr = 0; + return adoptRef(ptr); + } + +private: + T* m_ptr; +}; + +#endif // REFPTR_H diff --git a/3rdparty/masm/stubs/wtf/TypeTraits.h b/3rdparty/masm/stubs/wtf/TypeTraits.h new file mode 100644 index 0000000000..9b626a7a53 --- /dev/null +++ b/3rdparty/masm/stubs/wtf/TypeTraits.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef TYPETRAITS_H +#define TYPETRAITS_H + +namespace WTF { + +template +struct IsSameType { + static const bool value = false; +}; + +template +struct IsSameType { + static const bool value = true; +}; + +} + +#endif // TYPETRAITS_H diff --git a/3rdparty/masm/stubs/wtf/UnusedParam.h b/3rdparty/masm/stubs/wtf/UnusedParam.h new file mode 100644 index 0000000000..a676bdf303 --- /dev/null +++ b/3rdparty/masm/stubs/wtf/UnusedParam.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef UNUSEDPARAM_H +#define UNUSEDPARAM_H + +#include + +#define UNUSED_PARAM(x) Q_UNUSED(x) + +#endif // UNUSEDPARAM_H diff --git a/3rdparty/masm/stubs/wtf/Vector.h b/3rdparty/masm/stubs/wtf/Vector.h new file mode 100644 index 0000000000..1feea851e1 --- /dev/null +++ b/3rdparty/masm/stubs/wtf/Vector.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef VECTOR_H +#define VECTOR_H + +#include +#include +#include +#include + +namespace WTF { + +template +class Vector : public std::vector { +public: + Vector() {} + Vector(int initialSize) : std::vector(initialSize) {} + + inline void append(const T& value) + { this->push_back(value); } + + inline void append(const Vector& vector) + { + this->insert(this->end(), vector.begin(), vector.end()); + } + + using std::vector::insert; + + inline void insert(size_t position, T value) + { this->insert(this->begin() + position, value); } + + inline void grow(size_t size) + { this->resize(size); } + + inline void shrink(size_t size) + { this->erase(this->begin() + size, this->end()); } + + inline void remove(size_t position) + { this->erase(this->begin() + position); } + + inline bool isEmpty() const { return this->empty(); } + + inline T &last() { return *(this->begin() + this->size() - 1); } +}; + +template +void deleteAllValues(const Vector &vector) +{ + qDeleteAll(vector); +} + +} + +using WTF::Vector; +using WTF::deleteAllValues; + +#endif // VECTOR_H diff --git a/3rdparty/masm/stubs/wtf/text/CString.h b/3rdparty/masm/stubs/wtf/text/CString.h new file mode 100644 index 0000000000..c9a65e5c0b --- /dev/null +++ b/3rdparty/masm/stubs/wtf/text/CString.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CSTRING_H +#define CSTRING_H + +#endif // CSTRING_H diff --git a/3rdparty/masm/stubs/wtf/text/WTFString.h b/3rdparty/masm/stubs/wtf/text/WTFString.h new file mode 100644 index 0000000000..d157dc7adc --- /dev/null +++ b/3rdparty/masm/stubs/wtf/text/WTFString.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef WTFSTRING_H +#define WTFSTRING_H + +#include +#include +#include + +namespace WTF { + +class String : public QString +{ +public: + String(const QString& s) : QString(s) {} + bool is8Bit() const { return false; } + const unsigned char *characters8() const { return 0; } + const UChar *characters16() const { return reinterpret_cast(constData()); } + + template + const T* getCharacters() const; + +}; + +template <> +inline const unsigned char* String::getCharacters() const { return characters8(); } +template <> +inline const UChar* String::getCharacters() const { return characters16(); } + +} + +// Don't import WTF::String into the global namespace to avoid conflicts with QQmlJS::VM::String +namespace JSC { + using WTF::String; +} + +#endif // WTFSTRING_H diff --git a/3rdparty/masm/stubs/wtf/unicode/Unicode.h b/3rdparty/masm/stubs/wtf/unicode/Unicode.h new file mode 100644 index 0000000000..d61bc64c5a --- /dev/null +++ b/3rdparty/masm/stubs/wtf/unicode/Unicode.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef UNICODE_H +#define UNICODE_H + +#include + +typedef unsigned char LChar; +typedef uint16_t UChar; + +namespace Unicode { + inline UChar toLower(UChar ch) { + return QChar::toLower(ch); + } + + inline UChar toUpper(UChar ch) { + return QChar::toUpper(ch); + } +} + +#endif // UNICODE_H diff --git a/3rdparty/masm/wtf/ASCIICType.h b/3rdparty/masm/wtf/ASCIICType.h new file mode 100644 index 0000000000..18e108e1bf --- /dev/null +++ b/3rdparty/masm/wtf/ASCIICType.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_ASCIICType_h +#define WTF_ASCIICType_h + +#include + +// The behavior of many of the functions in the header is dependent +// on the current locale. But in the WebKit project, all uses of those functions +// are in code processing something that's not locale-specific. These equivalents +// for some of the functions are named more explicitly, not dependent +// on the C library locale, and we should also optimize them as needed. + +// All functions return false or leave the character unchanged if passed a character +// that is outside the range 0-7F. So they can be used on Unicode strings or +// characters if the intent is to do processing only if the character is ASCII. + +namespace WTF { + +template inline bool isASCII(CharType c) +{ + return !(c & ~0x7F); +} + +template inline bool isASCIIAlpha(CharType c) +{ + return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; +} + +template inline bool isASCIIDigit(CharType c) +{ + return c >= '0' && c <= '9'; +} + +template inline bool isASCIIAlphanumeric(CharType c) +{ + return isASCIIDigit(c) || isASCIIAlpha(c); +} + +template inline bool isASCIIHexDigit(CharType c) +{ + return isASCIIDigit(c) || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); +} + +template inline bool isASCIILower(CharType c) +{ + return c >= 'a' && c <= 'z'; +} + +template inline bool isASCIIOctalDigit(CharType c) +{ + return (c >= '0') & (c <= '7'); +} + +template inline bool isASCIIPrintable(CharType c) +{ + return c >= ' ' && c <= '~'; +} + +/* + Statistics from a run of Apple's page load test for callers of isASCIISpace: + + character count + --------- ----- + non-spaces 689383 + 20 space 294720 + 0A \n 89059 + 09 \t 28320 + 0D \r 0 + 0C \f 0 + 0B \v 0 + */ +template inline bool isASCIISpace(CharType c) +{ + return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); +} + +template inline bool isASCIIUpper(CharType c) +{ + return c >= 'A' && c <= 'Z'; +} + +template inline CharType toASCIILower(CharType c) +{ + return c | ((c >= 'A' && c <= 'Z') << 5); +} + +template inline CharType toASCIILowerUnchecked(CharType character) +{ + // This function can be used for comparing any input character + // to a lowercase English character. The isASCIIAlphaCaselessEqual + // below should be used for regular comparison of ASCII alpha + // characters, but switch statements in CSS tokenizer require + // direct use of this function. + return character | 0x20; +} + +template inline CharType toASCIIUpper(CharType c) +{ + return c & ~((c >= 'a' && c <= 'z') << 5); +} + +template inline int toASCIIHexValue(CharType c) +{ + ASSERT(isASCIIHexDigit(c)); + return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; +} + +template inline int toASCIIHexValue(CharType upperValue, CharType lowerValue) +{ + ASSERT(isASCIIHexDigit(upperValue) && isASCIIHexDigit(lowerValue)); + return ((toASCIIHexValue(upperValue) << 4) & 0xF0) | toASCIIHexValue(lowerValue); +} + +inline char lowerNibbleToASCIIHexDigit(char c) +{ + char nibble = c & 0xF; + return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; +} + +inline char upperNibbleToASCIIHexDigit(char c) +{ + char nibble = (c >> 4) & 0xF; + return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; +} + +template inline bool isASCIIAlphaCaselessEqual(CharType cssCharacter, char character) +{ + // This function compares a (preferrably) constant ASCII + // lowercase letter to any input character. + ASSERT(character >= 'a' && character <= 'z'); + return LIKELY(toASCIILowerUnchecked(cssCharacter) == character); +} + +} + +using WTF::isASCII; +using WTF::isASCIIAlpha; +using WTF::isASCIIAlphanumeric; +using WTF::isASCIIDigit; +using WTF::isASCIIHexDigit; +using WTF::isASCIILower; +using WTF::isASCIIOctalDigit; +using WTF::isASCIIPrintable; +using WTF::isASCIISpace; +using WTF::isASCIIUpper; +using WTF::toASCIIHexValue; +using WTF::toASCIILower; +using WTF::toASCIILowerUnchecked; +using WTF::toASCIIUpper; +using WTF::lowerNibbleToASCIIHexDigit; +using WTF::upperNibbleToASCIIHexDigit; +using WTF::isASCIIAlphaCaselessEqual; + +#endif diff --git a/3rdparty/masm/wtf/Assertions.h b/3rdparty/masm/wtf/Assertions.h new file mode 100644 index 0000000000..7e079ab187 --- /dev/null +++ b/3rdparty/masm/wtf/Assertions.h @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_Assertions_h +#define WTF_Assertions_h + +/* + no namespaces because this file has to be includable from C and Objective-C + + Note, this file uses many GCC extensions, but it should be compatible with + C, Objective C, C++, and Objective C++. + + For non-debug builds, everything is disabled by default. + Defining any of the symbols explicitly prevents this from having any effect. + + MSVC7 note: variadic macro support was added in MSVC8, so for now we disable + those macros in MSVC7. For more info, see the MSDN document on variadic + macros here: + + http://msdn2.microsoft.com/en-us/library/ms177415(VS.80).aspx +*/ + +#include + +#include + +#if !COMPILER(MSVC) +#include +#endif + +#ifdef NDEBUG +/* Disable ASSERT* macros in release mode. */ +#define ASSERTIONS_DISABLED_DEFAULT 1 +#else +#define ASSERTIONS_DISABLED_DEFAULT 0 +#endif + +#if COMPILER(MSVC7_OR_LOWER) +#define HAVE_VARIADIC_MACRO 0 +#else +#define HAVE_VARIADIC_MACRO 1 +#endif + +#ifndef BACKTRACE_DISABLED +#define BACKTRACE_DISABLED ASSERTIONS_DISABLED_DEFAULT +#endif + +#ifndef ASSERT_DISABLED +#define ASSERT_DISABLED ASSERTIONS_DISABLED_DEFAULT +#endif + +#ifndef ASSERT_MSG_DISABLED +#if HAVE(VARIADIC_MACRO) +#define ASSERT_MSG_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define ASSERT_MSG_DISABLED 1 +#endif +#endif + +#ifndef ASSERT_ARG_DISABLED +#define ASSERT_ARG_DISABLED ASSERTIONS_DISABLED_DEFAULT +#endif + +#ifndef FATAL_DISABLED +#if HAVE(VARIADIC_MACRO) +#define FATAL_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define FATAL_DISABLED 1 +#endif +#endif + +#ifndef ERROR_DISABLED +#if HAVE(VARIADIC_MACRO) +#define ERROR_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define ERROR_DISABLED 1 +#endif +#endif + +#ifndef LOG_DISABLED +#if HAVE(VARIADIC_MACRO) +#define LOG_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define LOG_DISABLED 1 +#endif +#endif + +#if COMPILER(GCC) +#define WTF_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#define WTF_PRETTY_FUNCTION __FUNCTION__ +#endif + +/* WTF logging functions can process %@ in the format string to log a NSObject* but the printf format attribute + emits a warning when %@ is used in the format string. Until is resolved we can't include + the attribute when being used from Objective-C code in case it decides to use %@. */ +#if COMPILER(GCC) && !defined(__OBJC__) +#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) __attribute__((__format__(printf, formatStringArgument, extraArguments))) +#else +#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) +#endif + +/* These helper functions are always declared, but not necessarily always defined if the corresponding function is disabled. */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { WTFLogChannelOff, WTFLogChannelOn } WTFLogChannelState; + +typedef struct { + unsigned mask; + const char *defaultName; + WTFLogChannelState state; +} WTFLogChannel; + +WTF_EXPORT_PRIVATE void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion); +WTF_EXPORT_PRIVATE void WTFReportAssertionFailureWithMessage(const char* file, int line, const char* function, const char* assertion, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6); +WTF_EXPORT_PRIVATE void WTFReportArgumentAssertionFailure(const char* file, int line, const char* function, const char* argName, const char* assertion); +WTF_EXPORT_PRIVATE void WTFReportFatalError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5); +WTF_EXPORT_PRIVATE void WTFReportError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5); +WTF_EXPORT_PRIVATE void WTFLog(WTFLogChannel*, const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); +WTF_EXPORT_PRIVATE void WTFLogVerbose(const char* file, int line, const char* function, WTFLogChannel*, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6); +WTF_EXPORT_PRIVATE void WTFLogAlways(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); + +WTF_EXPORT_PRIVATE void WTFGetBacktrace(void** stack, int* size); +WTF_EXPORT_PRIVATE void WTFReportBacktrace(); +WTF_EXPORT_PRIVATE void WTFPrintBacktrace(void** stack, int size); + +typedef void (*WTFCrashHookFunction)(); +WTF_EXPORT_PRIVATE void WTFSetCrashHook(WTFCrashHookFunction); +WTF_EXPORT_PRIVATE void WTFInvokeCrashHook(); +WTF_EXPORT_PRIVATE void WTFInstallReportBacktraceOnCrashHook(); + +#ifdef __cplusplus +} +#endif + +/* CRASH() - Raises a fatal error resulting in program termination and triggering either the debugger or the crash reporter. + + Use CRASH() in response to known, unrecoverable errors like out-of-memory. + Macro is enabled in both debug and release mode. + To test for unknown errors and verify assumptions, use ASSERT instead, to avoid impacting performance in release builds. + + Signals are ignored by the crash reporter on OS X so we must do better. +*/ +#ifndef CRASH +#if COMPILER(CLANG) +#define CRASH() \ + (WTFReportBacktrace(), \ + WTFInvokeCrashHook(), \ + (*(int *)(uintptr_t)0xbbadbeef = 0), \ + __builtin_trap()) +#else +#define CRASH() \ + (WTFReportBacktrace(), \ + WTFInvokeCrashHook(), \ + (*(int *)(uintptr_t)0xbbadbeef = 0), \ + ((void(*)())0)() /* More reliable, but doesn't say BBADBEEF */ \ + ) +#endif +#endif + +#if COMPILER(CLANG) +#define NO_RETURN_DUE_TO_CRASH NO_RETURN +#else +#define NO_RETURN_DUE_TO_CRASH +#endif + + +/* BACKTRACE + + Print a backtrace to the same location as ASSERT messages. +*/ + +#if BACKTRACE_DISABLED + +#define BACKTRACE() ((void)0) + +#else + +#define BACKTRACE() do { \ + WTFReportBacktrace(); \ +} while(false) + +#endif + +/* ASSERT, ASSERT_NOT_REACHED, ASSERT_UNUSED + + These macros are compiled out of release builds. + Expressions inside them are evaluated in debug builds only. +*/ + +#if OS(WINCE) && !PLATFORM(TORCHMOBILE) +/* FIXME: We include this here only to avoid a conflict with the ASSERT macro. */ +#include +#undef min +#undef max +#undef ERROR +#endif + +#if OS(WINDOWS) +/* FIXME: Change to use something other than ASSERT to avoid this conflict with the underlying platform */ +#undef ASSERT +#endif + +#if ASSERT_DISABLED + +#define ASSERT(assertion) ((void)0) +#define ASSERT_AT(assertion, file, line, function) ((void)0) +#define ASSERT_NOT_REACHED() ((void)0) +#define NO_RETURN_DUE_TO_ASSERT + +#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) +template +inline void assertUnused(T& x) { (void)x; } +#define ASSERT_UNUSED(variable, assertion) (assertUnused(variable)) +#else +#define ASSERT_UNUSED(variable, assertion) ((void)variable) +#endif + +#else + +#define ASSERT(assertion) \ + (!(assertion) ? \ + (WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion), \ + CRASH()) : \ + (void)0) + +#define ASSERT_AT(assertion, file, line, function) \ + (!(assertion) ? \ + (WTFReportAssertionFailure(file, line, function, #assertion), \ + CRASH()) : \ + (void)0) + +#define ASSERT_NOT_REACHED() do { \ + WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, 0); \ + CRASH(); \ +} while (0) + +#define ASSERT_UNUSED(variable, assertion) ASSERT(assertion) + +#define NO_RETURN_DUE_TO_ASSERT NO_RETURN_DUE_TO_CRASH + +#endif + +/* ASSERT_WITH_MESSAGE */ + +#if COMPILER(MSVC7_OR_LOWER) +#define ASSERT_WITH_MESSAGE(assertion) ((void)0) +#elif ASSERT_MSG_DISABLED +#define ASSERT_WITH_MESSAGE(assertion, ...) ((void)0) +#else +#define ASSERT_WITH_MESSAGE(assertion, ...) do \ + if (!(assertion)) { \ + WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ + CRASH(); \ + } \ +while (0) +#endif + +/* ASSERT_WITH_MESSAGE_UNUSED */ + +#if COMPILER(MSVC7_OR_LOWER) +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion) ((void)0) +#elif ASSERT_MSG_DISABLED +#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) +template +inline void assertWithMessageUnused(T& x) { (void)x; } +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) (assertWithMessageUnused(variable)) +#else +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) ((void)variable) +#endif +#else +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) do \ + if (!(assertion)) { \ + WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ + CRASH(); \ + } \ +while (0) +#endif + + +/* ASSERT_ARG */ + +#if ASSERT_ARG_DISABLED + +#define ASSERT_ARG(argName, assertion) ((void)0) + +#else + +#define ASSERT_ARG(argName, assertion) do \ + if (!(assertion)) { \ + WTFReportArgumentAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #argName, #assertion); \ + CRASH(); \ + } \ +while (0) + +#endif + +/* COMPILE_ASSERT */ +#ifndef COMPILE_ASSERT +#if COMPILER_SUPPORTS(C_STATIC_ASSERT) +#define COMPILE_ASSERT(exp, name) _Static_assert((exp), #name) +#else +#define COMPILE_ASSERT(exp, name) typedef int dummy##name [(exp) ? 1 : -1] +#endif +#endif + +/* FATAL */ + +#if COMPILER(MSVC7_OR_LOWER) +#define FATAL() ((void)0) +#elif FATAL_DISABLED +#define FATAL(...) ((void)0) +#else +#define FATAL(...) do { \ + WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__); \ + CRASH(); \ +} while (0) +#endif + +/* LOG_ERROR */ + +#if COMPILER(MSVC7_OR_LOWER) +#define LOG_ERROR() ((void)0) +#elif ERROR_DISABLED +#define LOG_ERROR(...) ((void)0) +#else +#define LOG_ERROR(...) WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__) +#endif + +/* LOG */ + +#if COMPILER(MSVC7_OR_LOWER) +#define LOG() ((void)0) +#elif LOG_DISABLED +#define LOG(channel, ...) ((void)0) +#else +#define LOG(channel, ...) WTFLog(&JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__) +#define JOIN_LOG_CHANNEL_WITH_PREFIX(prefix, channel) JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) +#define JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) prefix ## channel +#endif + +/* LOG_VERBOSE */ + +#if COMPILER(MSVC7_OR_LOWER) +#define LOG_VERBOSE(channel) ((void)0) +#elif LOG_DISABLED +#define LOG_VERBOSE(channel, ...) ((void)0) +#else +#define LOG_VERBOSE(channel, ...) WTFLogVerbose(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, &JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__) +#endif + +/* UNREACHABLE_FOR_PLATFORM */ + +#if COMPILER(CLANG) +// This would be a macro except that its use of #pragma works best around +// a function. Hence it uses macro naming convention. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-noreturn" +static inline void UNREACHABLE_FOR_PLATFORM() +{ + ASSERT_NOT_REACHED(); +} +#pragma clang diagnostic pop +#else +#define UNREACHABLE_FOR_PLATFORM() ASSERT_NOT_REACHED() +#endif + +#endif /* WTF_Assertions_h */ diff --git a/3rdparty/masm/wtf/Atomics.h b/3rdparty/masm/wtf/Atomics.h new file mode 100644 index 0000000000..750e1092ad --- /dev/null +++ b/3rdparty/masm/wtf/Atomics.h @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2007, 2008, 2010, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Note: The implementations of InterlockedIncrement and InterlockedDecrement are based + * on atomic_increment and atomic_exchange_and_add from the Boost C++ Library. The license + * is virtually identical to the Apple license above but is included here for completeness. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef Atomics_h +#define Atomics_h + +#include +#include +#include + +#if OS(WINDOWS) +#include +#elif OS(DARWIN) +#include +#elif OS(QNX) +#include +#elif OS(ANDROID) +#include +#elif COMPILER(GCC) +#if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2))) && !defined(__LSB_VERSION__) +#include +#else +#include +#endif +#endif + +namespace WTF { + +#if OS(WINDOWS) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +#if COMPILER(MINGW) || COMPILER(MSVC7_OR_LOWER) || OS(WINCE) +inline int atomicIncrement(int* addend) { return InterlockedIncrement(reinterpret_cast(addend)); } +inline int atomicDecrement(int* addend) { return InterlockedDecrement(reinterpret_cast(addend)); } +#else +inline int atomicIncrement(int volatile* addend) { return InterlockedIncrement(reinterpret_cast(addend)); } +inline int atomicDecrement(int volatile* addend) { return InterlockedDecrement(reinterpret_cast(addend)); } +#endif + +#elif OS(DARWIN) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +inline int atomicIncrement(int volatile* addend) { return OSAtomicIncrement32Barrier(const_cast(addend)); } +inline int atomicDecrement(int volatile* addend) { return OSAtomicDecrement32Barrier(const_cast(addend)); } + +inline int64_t atomicIncrement(int64_t volatile* addend) { return OSAtomicIncrement64Barrier(const_cast(addend)); } +inline int64_t atomicDecrement(int64_t volatile* addend) { return OSAtomicDecrement64Barrier(const_cast(addend)); } + +#elif OS(QNX) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +// Note, atomic_{add, sub}_value() return the previous value of addend's content. +inline int atomicIncrement(int volatile* addend) { return static_cast(atomic_add_value(reinterpret_cast(addend), 1)) + 1; } +inline int atomicDecrement(int volatile* addend) { return static_cast(atomic_sub_value(reinterpret_cast(addend), 1)) - 1; } + +#elif OS(ANDROID) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +// Note, __atomic_{inc, dec}() return the previous value of addend's content. +inline int atomicIncrement(int volatile* addend) { return __atomic_inc(addend) + 1; } +inline int atomicDecrement(int volatile* addend) { return __atomic_dec(addend) - 1; } + +#elif COMPILER(GCC) && !CPU(SPARC64) // sizeof(_Atomic_word) != sizeof(int) on sparc64 gcc +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +inline int atomicIncrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, 1) + 1; } +inline int atomicDecrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, -1) - 1; } + +#endif + +#if OS(WINDOWS) +inline bool weakCompareAndSwap(volatile unsigned* location, unsigned expected, unsigned newValue) +{ +#if OS(WINCE) + return InterlockedCompareExchange(reinterpret_cast(const_cast(location)), static_cast(newValue), static_cast(expected)) == static_cast(expected); +#else + return InterlockedCompareExchange(reinterpret_cast(location), static_cast(newValue), static_cast(expected)) == static_cast(expected); +#endif +} + +inline bool weakCompareAndSwap(void*volatile* location, void* expected, void* newValue) +{ + return InterlockedCompareExchangePointer(location, newValue, expected) == expected; +} +#else // OS(WINDOWS) --> not windows +#if COMPILER(GCC) && !COMPILER(CLANG) // Work around a gcc bug +inline bool weakCompareAndSwap(volatile unsigned* location, unsigned expected, unsigned newValue) +#else +inline bool weakCompareAndSwap(unsigned* location, unsigned expected, unsigned newValue) +#endif +{ +#if ENABLE(COMPARE_AND_SWAP) +#if CPU(X86) || CPU(X86_64) + unsigned char result; + asm volatile( + "lock; cmpxchgl %3, %2\n\t" + "sete %1" + : "+a"(expected), "=q"(result), "+m"(*location) + : "r"(newValue) + : "memory" + ); +#elif CPU(ARM_THUMB2) + unsigned tmp; + unsigned result; + asm volatile( + "movw %1, #1\n\t" + "ldrex %2, %0\n\t" + "cmp %3, %2\n\t" + "bne.n 0f\n\t" + "strex %1, %4, %0\n\t" + "0:" + : "+Q"(*location), "=&r"(result), "=&r"(tmp) + : "r"(expected), "r"(newValue) + : "memory"); + result = !result; +#else +#error "Bad architecture for compare and swap." +#endif + return result; +#else + UNUSED_PARAM(location); + UNUSED_PARAM(expected); + UNUSED_PARAM(newValue); + CRASH(); + return false; +#endif +} + +inline bool weakCompareAndSwap(void*volatile* location, void* expected, void* newValue) +{ +#if ENABLE(COMPARE_AND_SWAP) +#if CPU(X86_64) + bool result; + asm volatile( + "lock; cmpxchgq %3, %2\n\t" + "sete %1" + : "+a"(expected), "=q"(result), "+m"(*location) + : "r"(newValue) + : "memory" + ); + return result; +#else + return weakCompareAndSwap(bitwise_cast(location), bitwise_cast(expected), bitwise_cast(newValue)); +#endif +#else // ENABLE(COMPARE_AND_SWAP) + UNUSED_PARAM(location); + UNUSED_PARAM(expected); + UNUSED_PARAM(newValue); + CRASH(); + return 0; +#endif // ENABLE(COMPARE_AND_SWAP) +} +#endif // OS(WINDOWS) (end of the not-windows case) + +inline bool weakCompareAndSwapUIntPtr(volatile uintptr_t* location, uintptr_t expected, uintptr_t newValue) +{ + return weakCompareAndSwap(reinterpret_cast(location), reinterpret_cast(expected), reinterpret_cast(newValue)); +} + +#if CPU(ARM_THUMB2) + +inline void memoryBarrierAfterLock() +{ + asm volatile("dmb" ::: "memory"); +} + +inline void memoryBarrierBeforeUnlock() +{ + asm volatile("dmb" ::: "memory"); +} + +#else + +inline void memoryBarrierAfterLock() { } +inline void memoryBarrierBeforeUnlock() { } + +#endif + +} // namespace WTF + +#if USE(LOCKFREE_THREADSAFEREFCOUNTED) +using WTF::atomicDecrement; +using WTF::atomicIncrement; +#endif + +#endif // Atomics_h diff --git a/3rdparty/masm/wtf/BumpPointerAllocator.h b/3rdparty/masm/wtf/BumpPointerAllocator.h new file mode 100644 index 0000000000..3b2cfd974a --- /dev/null +++ b/3rdparty/masm/wtf/BumpPointerAllocator.h @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BumpPointerAllocator_h +#define BumpPointerAllocator_h + +#include +#include +#include + +namespace WTF { + +#define MINIMUM_BUMP_POOL_SIZE 0x1000 + +class BumpPointerPool { +public: + // ensureCapacity will check whether the current pool has capacity to + // allocate 'size' bytes of memory If it does not, it will attempt to + // allocate a new pool (which will be added to this one in a chain). + // + // If allocation fails (out of memory) this method will return null. + // If the return value is non-null, then callers should update any + // references they have to this current (possibly full) BumpPointerPool + // to instead point to the newly returned BumpPointerPool. + BumpPointerPool* ensureCapacity(size_t size) + { + void* allocationEnd = static_cast(m_current) + size; + ASSERT(allocationEnd > m_current); // check for overflow + if (allocationEnd <= static_cast(this)) + return this; + return ensureCapacityCrossPool(this, size); + } + + // alloc should only be called after calling ensureCapacity; as such + // alloc will never fail. + void* alloc(size_t size) + { + void* current = m_current; + void* allocationEnd = static_cast(current) + size; + ASSERT(allocationEnd > current); // check for overflow + ASSERT(allocationEnd <= static_cast(this)); + m_current = allocationEnd; + return current; + } + + // The dealloc method releases memory allocated using alloc. Memory + // must be released in a LIFO fashion, e.g. if the client calls alloc + // four times, returning pointer A, B, C, D, then the only valid order + // in which these may be deallocaed is D, C, B, A. + // + // The client may optionally skip some deallocations. In the example + // above, it would be valid to only explicitly dealloc C, A (D being + // dealloced along with C, B along with A). + // + // If pointer was not allocated from this pool (or pools) then dealloc + // will CRASH(). Callers should update any references they have to + // this current BumpPointerPool to instead point to the returned + // BumpPointerPool. + BumpPointerPool* dealloc(void* position) + { + if ((position >= m_start) && (position <= static_cast(this))) { + ASSERT(position <= m_current); + m_current = position; + return this; + } + return deallocCrossPool(this, position); + } + +private: + // Placement operator new, returns the last 'size' bytes of allocation for use as this. + void* operator new(size_t size, const PageAllocation& allocation) + { + ASSERT(size < allocation.size()); + return reinterpret_cast(reinterpret_cast(allocation.base()) + allocation.size()) - size; + } + + BumpPointerPool(const PageAllocation& allocation) + : m_current(allocation.base()) + , m_start(allocation.base()) + , m_next(0) + , m_previous(0) + , m_allocation(allocation) + { + } + + static BumpPointerPool* create(size_t minimumCapacity = 0) + { + // Add size of BumpPointerPool object, check for overflow. + minimumCapacity += sizeof(BumpPointerPool); + if (minimumCapacity < sizeof(BumpPointerPool)) + return 0; + + size_t poolSize = std::max(static_cast(MINIMUM_BUMP_POOL_SIZE), WTF::pageSize()); + while (poolSize < minimumCapacity) { + poolSize <<= 1; + // The following if check relies on MINIMUM_BUMP_POOL_SIZE being a power of 2! + ASSERT(!(MINIMUM_BUMP_POOL_SIZE & (MINIMUM_BUMP_POOL_SIZE - 1))); + if (!poolSize) + return 0; + } + + PageAllocation allocation = PageAllocation::allocate(poolSize); + if (!!allocation) + return new (allocation) BumpPointerPool(allocation); + return 0; + } + + void shrink() + { + ASSERT(!m_previous); + m_current = m_start; + while (m_next) { + BumpPointerPool* nextNext = m_next->m_next; + m_next->destroy(); + m_next = nextNext; + } + } + + void destroy() + { + m_allocation.deallocate(); + } + + static BumpPointerPool* ensureCapacityCrossPool(BumpPointerPool* previousPool, size_t size) + { + // The pool passed should not have capacity, so we'll start with the next one. + ASSERT(previousPool); + ASSERT((static_cast(previousPool->m_current) + size) > previousPool->m_current); // check for overflow + ASSERT((static_cast(previousPool->m_current) + size) > static_cast(previousPool)); + BumpPointerPool* pool = previousPool->m_next; + + while (true) { + if (!pool) { + // We've run to the end; allocate a new pool. + pool = BumpPointerPool::create(size); + previousPool->m_next = pool; + pool->m_previous = previousPool; + return pool; + } + + // + void* current = pool->m_current; + void* allocationEnd = static_cast(current) + size; + ASSERT(allocationEnd > current); // check for overflow + if (allocationEnd <= static_cast(pool)) + return pool; + } + } + + static BumpPointerPool* deallocCrossPool(BumpPointerPool* pool, void* position) + { + // Should only be called if position is not in the current pool. + ASSERT((position < pool->m_start) || (position > static_cast(pool))); + + while (true) { + // Unwind the current pool to the start, move back in the chain to the previous pool. + pool->m_current = pool->m_start; + pool = pool->m_previous; + + // position was nowhere in the chain! + if (!pool) + CRASH(); + + if ((position >= pool->m_start) && (position <= static_cast(pool))) { + ASSERT(position <= pool->m_current); + pool->m_current = position; + return pool; + } + } + } + + void* m_current; + void* m_start; + BumpPointerPool* m_next; + BumpPointerPool* m_previous; + PageAllocation m_allocation; + + friend class BumpPointerAllocator; +}; + +// A BumpPointerAllocator manages a set of BumpPointerPool objects, which +// can be used for LIFO (stack like) allocation. +// +// To begin allocating using this class call startAllocator(). The result +// of this method will be null if the initial pool allocation fails, or a +// pointer to a BumpPointerPool object that can be used to perform +// allocations. Whilst running no memory will be released until +// stopAllocator() is called. At this point all allocations made through +// this allocator will be reaped, and underlying memory may be freed. +// +// (In practice we will still hold on to the initial pool to allow allocation +// to be quickly restared, but aditional pools will be freed). +// +// This allocator is non-renetrant, it is encumbant on the clients to ensure +// startAllocator() is not called again until stopAllocator() has been called. +class BumpPointerAllocator { +public: + BumpPointerAllocator() + : m_head(0) + { + } + + ~BumpPointerAllocator() + { + if (m_head) + m_head->destroy(); + } + + BumpPointerPool* startAllocator() + { + if (!m_head) + m_head = BumpPointerPool::create(); + return m_head; + } + + void stopAllocator() + { + if (m_head) + m_head->shrink(); + } + +private: + BumpPointerPool* m_head; +}; + +} + +using WTF::BumpPointerAllocator; + +#endif // BumpPointerAllocator_h diff --git a/3rdparty/masm/wtf/CheckedArithmetic.h b/3rdparty/masm/wtf/CheckedArithmetic.h new file mode 100644 index 0000000000..f5d3b75500 --- /dev/null +++ b/3rdparty/masm/wtf/CheckedArithmetic.h @@ -0,0 +1,708 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CheckedArithmetic_h +#define CheckedArithmetic_h + +#include +#include + +#include +#include + +/* Checked + * + * This class provides a mechanism to perform overflow-safe integer arithmetic + * without having to manually ensure that you have all the required bounds checks + * directly in your code. + * + * There are two modes of operation: + * - The default is Checked, and crashes at the point + * and overflow has occurred. + * - The alternative is Checked, which uses an additional + * byte of storage to track whether an overflow has occurred, subsequent + * unchecked operations will crash if an overflow has occured + * + * It is possible to provide a custom overflow handler, in which case you need + * to support these functions: + * - void overflowed(); + * This function is called when an operation has produced an overflow. + * - bool hasOverflowed(); + * This function must return true if overflowed() has been called on an + * instance and false if it has not. + * - void clearOverflow(); + * Used to reset overflow tracking when a value is being overwritten with + * a new value. + * + * Checked works for all integer types, with the following caveats: + * - Mixing signedness of operands is only supported for types narrower than + * 64bits. + * - It does have a performance impact, so tight loops may want to be careful + * when using it. + * + */ + +namespace WTF { + +class CrashOnOverflow { +protected: + NO_RETURN_DUE_TO_CRASH void overflowed() + { + CRASH(); + } + + void clearOverflow() { } + +public: + bool hasOverflowed() const { return false; } +}; + +class RecordOverflow { +protected: + RecordOverflow() + : m_overflowed(false) + { + } + + void overflowed() + { + m_overflowed = true; + } + + void clearOverflow() + { + m_overflowed = false; + } + +public: + bool hasOverflowed() const { return m_overflowed; } + +private: + unsigned char m_overflowed; +}; + +template class Checked; +template struct RemoveChecked; +template struct RemoveChecked >; + +template ::is_signed, bool sourceSigned = std::numeric_limits::is_signed> struct BoundsChecker; +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Same signedness so implicit type conversion will always increase precision + // to widest type + return value <= std::numeric_limits::max(); + } +}; + +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Same signedness so implicit type conversion will always increase precision + // to widest type + return std::numeric_limits::min() <= value && value <= std::numeric_limits::max(); + } +}; + +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Target is unsigned so any value less than zero is clearly unsafe + if (value < 0) + return false; + // If our (unsigned) Target is the same or greater width we can + // convert value to type Target without losing precision + if (sizeof(Target) >= sizeof(Source)) + return static_cast(value) <= std::numeric_limits::max(); + // The signed Source type has greater precision than the target so + // max(Target) -> Source will widen. + return value <= static_cast(std::numeric_limits::max()); + } +}; + +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Signed target with an unsigned source + if (sizeof(Target) <= sizeof(Source)) + return value <= static_cast(std::numeric_limits::max()); + // Target is Wider than Source so we're guaranteed to fit any value in + // unsigned Source + return true; + } +}; + +template ::value || (sizeof(Target) > sizeof(Source)) > struct BoundsCheckElider; +template struct BoundsCheckElider { + static bool inBounds(Source) { return true; } +}; +template struct BoundsCheckElider : public BoundsChecker { +}; + +template static inline bool isInBounds(Source value) +{ + return BoundsCheckElider::inBounds(value); +} + +template struct RemoveChecked { + typedef T CleanType; + static const CleanType DefaultValue = 0; +}; + +template struct RemoveChecked > { + typedef typename RemoveChecked::CleanType CleanType; + static const CleanType DefaultValue = 0; +}; + +template struct RemoveChecked > { + typedef typename RemoveChecked::CleanType CleanType; + static const CleanType DefaultValue = 0; +}; + +// The ResultBase and SignednessSelector are used to workaround typeof not being +// available in MSVC +template sizeof(V)), bool sameSize = (sizeof(U) == sizeof(V))> struct ResultBase; +template struct ResultBase { + typedef U ResultType; +}; + +template struct ResultBase { + typedef V ResultType; +}; + +template struct ResultBase { + typedef U ResultType; +}; + +template ::is_signed, bool vIsSigned = std::numeric_limits::is_signed> struct SignednessSelector; +template struct SignednessSelector { + typedef U ResultType; +}; + +template struct SignednessSelector { + typedef U ResultType; +}; + +template struct SignednessSelector { + typedef V ResultType; +}; + +template struct SignednessSelector { + typedef U ResultType; +}; + +template struct ResultBase { + typedef typename SignednessSelector::ResultType ResultType; +}; + +template struct Result : ResultBase::CleanType, typename RemoveChecked::CleanType> { +}; + +template ::ResultType, + bool lhsSigned = std::numeric_limits::is_signed, bool rhsSigned = std::numeric_limits::is_signed> struct ArithmeticOperations; + +template struct ArithmeticOperations { + // LHS and RHS are signed types + + // Helper function + static inline bool signsMatch(LHS lhs, RHS rhs) + { + return (lhs ^ rhs) >= 0; + } + + static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if ((std::numeric_limits::max() - rhs) < lhs) + return false; + } else { + ResultType temp = lhs - std::numeric_limits::min(); + if (rhs < -temp) + return false; + } + } // if the signs do not match this operation can't overflow + result = lhs + rhs; + return true; + } + + static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (!signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if (lhs > std::numeric_limits::max() + rhs) + return false; + } else { + if (rhs > std::numeric_limits::max() + lhs) + return false; + } + } // if the signs match this operation can't overflow + result = lhs - rhs; + return true; + } + + static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if (lhs && (std::numeric_limits::max() / lhs) < rhs) + return false; + } else { + if (static_cast(lhs) == std::numeric_limits::min() || static_cast(rhs) == std::numeric_limits::min()) + return false; + if ((std::numeric_limits::max() / -lhs) < -rhs) + return false; + } + } else { + if (lhs < 0) { + if (rhs && lhs < (std::numeric_limits::min() / rhs)) + return false; + } else { + if (lhs && rhs < (std::numeric_limits::min() / lhs)) + return false; + } + } + result = lhs * rhs; + return true; + } + + static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } + +}; + +template struct ArithmeticOperations { + // LHS and RHS are unsigned types so bounds checks are nice and easy + static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs + rhs; + if (temp < lhs) + return false; + result = temp; + return true; + } + + static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs - rhs; + if (temp > lhs) + return false; + result = temp; + return true; + } + + static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs * rhs; + if (temp < lhs) + return false; + result = temp; + return true; + } + + static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } + +}; + +template struct ArithmeticOperations { + static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs + rhs; + if (temp < std::numeric_limits::min()) + return false; + if (temp > std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs - rhs; + if (temp < std::numeric_limits::min()) + return false; + if (temp > std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs * rhs; + if (temp < std::numeric_limits::min()) + return false; + if (temp > std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool equals(int lhs, unsigned rhs) + { + return static_cast(lhs) == static_cast(rhs); + } +}; + +template struct ArithmeticOperations { + static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations::add(rhs, lhs, result); + } + + static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations::sub(lhs, rhs, result); + } + + static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations::multiply(rhs, lhs, result); + } + + static inline bool equals(unsigned lhs, int rhs) + { + return ArithmeticOperations::equals(rhs, lhs); + } +}; + +template static inline bool safeAdd(U lhs, V rhs, R& result) +{ + return ArithmeticOperations::add(lhs, rhs, result); +} + +template static inline bool safeSub(U lhs, V rhs, R& result) +{ + return ArithmeticOperations::sub(lhs, rhs, result); +} + +template static inline bool safeMultiply(U lhs, V rhs, R& result) +{ + return ArithmeticOperations::multiply(lhs, rhs, result); +} + +template static inline bool safeEquals(U lhs, V rhs) +{ + return ArithmeticOperations::equals(lhs, rhs); +} + +enum ResultOverflowedTag { ResultOverflowed }; + +// FIXME: Needed to workaround http://llvm.org/bugs/show_bug.cgi?id=10801 +static inline bool workAroundClangBug() { return true; } + +template class Checked : public OverflowHandler { +public: + template friend class Checked; + Checked() + : m_value(0) + { + } + + Checked(ResultOverflowedTag) + : m_value(0) + { + // FIXME: Remove this when clang fixes http://llvm.org/bugs/show_bug.cgi?id=10801 + if (workAroundClangBug()) + this->overflowed(); + } + + template Checked(U value) + { + if (!isInBounds(value)) + this->overflowed(); + m_value = static_cast(value); + } + + template Checked(const Checked& rhs) + : m_value(rhs.m_value) + { + if (rhs.hasOverflowed()) + this->overflowed(); + } + + template Checked(const Checked& rhs) + : OverflowHandler(rhs) + { + if (!isInBounds(rhs.m_value)) + this->overflowed(); + m_value = static_cast(rhs.m_value); + } + + template Checked(const Checked& rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + if (!isInBounds(rhs.m_value)) + this->overflowed(); + m_value = static_cast(rhs.m_value); + } + + const Checked& operator=(Checked rhs) + { + this->clearOverflow(); + if (rhs.hasOverflowed()) + this->overflowed(); + m_value = static_cast(rhs.m_value); + return *this; + } + + template const Checked& operator=(U value) + { + return *this = Checked(value); + } + + template const Checked& operator=(const Checked& rhs) + { + return *this = Checked(rhs); + } + + // prefix + const Checked& operator++() + { + if (m_value == std::numeric_limits::max()) + this->overflowed(); + m_value++; + return *this; + } + + const Checked& operator--() + { + if (m_value == std::numeric_limits::min()) + this->overflowed(); + m_value--; + return *this; + } + + // postfix operators + const Checked operator++(int) + { + if (m_value == std::numeric_limits::max()) + this->overflowed(); + return Checked(m_value++); + } + + const Checked operator--(int) + { + if (m_value == std::numeric_limits::min()) + this->overflowed(); + return Checked(m_value--); + } + + // Boolean operators + bool operator!() const + { + if (this->hasOverflowed()) + CRASH(); + return !m_value; + } + + typedef void* (Checked::*UnspecifiedBoolType); + operator UnspecifiedBoolType*() const + { + if (this->hasOverflowed()) + CRASH(); + return (m_value) ? reinterpret_cast(1) : 0; + } + + // Value accessors. unsafeGet() will crash if there's been an overflow. + T unsafeGet() const + { + if (this->hasOverflowed()) + CRASH(); + return m_value; + } + + bool safeGet(T& value) const WARN_UNUSED_RETURN + { + value = m_value; + return this->hasOverflowed(); + } + + // Mutating assignment + template const Checked operator+=(U rhs) + { + if (!safeAdd(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + template const Checked operator-=(U rhs) + { + if (!safeSub(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + template const Checked operator*=(U rhs) + { + if (!safeMultiply(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + const Checked operator*=(double rhs) + { + double result = rhs * m_value; + // Handle +/- infinity and NaN + if (!(std::numeric_limits::min() <= result && std::numeric_limits::max() >= result)) + this->overflowed(); + m_value = (T)result; + return *this; + } + + const Checked operator*=(float rhs) + { + return *this *= (double)rhs; + } + + template const Checked operator+=(Checked rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this += rhs.m_value; + } + + template const Checked operator-=(Checked rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this -= rhs.m_value; + } + + template const Checked operator*=(Checked rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this *= rhs.m_value; + } + + // Equality comparisons + template bool operator==(Checked rhs) + { + return unsafeGet() == rhs.unsafeGet(); + } + + template bool operator==(U rhs) + { + if (this->hasOverflowed()) + this->overflowed(); + return safeEquals(m_value, rhs); + } + + template const Checked operator==(Checked rhs) + { + return unsafeGet() == Checked(rhs.unsafeGet()); + } + + template bool operator!=(U rhs) + { + return !(*this == rhs); + } + +private: + // Disallow implicit conversion of floating point to integer types + Checked(float); + Checked(double); + void operator=(float); + void operator=(double); + void operator+=(float); + void operator+=(double); + void operator-=(float); + void operator-=(double); + T m_value; +}; + +template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, Checked rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result::ResultType result = 0; + overflowed |= !safeAdd(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, Checked rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result::ResultType result = 0; + overflowed |= !safeSub(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, Checked rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result::ResultType result = 0; + overflowed |= !safeMultiply(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, V rhs) +{ + return lhs + Checked(rhs); +} + +template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, V rhs) +{ + return lhs - Checked(rhs); +} + +template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, V rhs) +{ + return lhs * Checked(rhs); +} + +template static inline Checked::ResultType, OverflowHandler> operator+(U lhs, Checked rhs) +{ + return Checked(lhs) + rhs; +} + +template static inline Checked::ResultType, OverflowHandler> operator-(U lhs, Checked rhs) +{ + return Checked(lhs) - rhs; +} + +template static inline Checked::ResultType, OverflowHandler> operator*(U lhs, Checked rhs) +{ + return Checked(lhs) * rhs; +} + +} + +using WTF::Checked; +using WTF::RecordOverflow; + +#endif diff --git a/3rdparty/masm/wtf/Compiler.h b/3rdparty/masm/wtf/Compiler.h new file mode 100644 index 0000000000..a9ef419c18 --- /dev/null +++ b/3rdparty/masm/wtf/Compiler.h @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_Compiler_h +#define WTF_Compiler_h + +/* COMPILER() - the compiler being used to build the project */ +#define COMPILER(WTF_FEATURE) (defined WTF_COMPILER_##WTF_FEATURE && WTF_COMPILER_##WTF_FEATURE) + +/* COMPILER_SUPPORTS() - whether the compiler being used to build the project supports the given feature. */ +#define COMPILER_SUPPORTS(WTF_COMPILER_FEATURE) (defined WTF_COMPILER_SUPPORTS_##WTF_COMPILER_FEATURE && WTF_COMPILER_SUPPORTS_##WTF_COMPILER_FEATURE) + +/* COMPILER_QUIRK() - whether the compiler being used to build the project requires a given quirk. */ +#define COMPILER_QUIRK(WTF_COMPILER_QUIRK) (defined WTF_COMPILER_QUIRK_##WTF_COMPILER_QUIRK && WTF_COMPILER_QUIRK_##WTF_COMPILER_QUIRK) + +/* ==== COMPILER() - the compiler being used to build the project ==== */ + +/* COMPILER(CLANG) - Clang */ +#if defined(__clang__) +#define WTF_COMPILER_CLANG 1 + +#ifndef __has_extension +#define __has_extension __has_feature /* Compatibility with older versions of clang */ +#endif + +#define CLANG_PRAGMA(PRAGMA) _Pragma(PRAGMA) + +/* Specific compiler features */ +#define WTF_COMPILER_SUPPORTS_CXX_VARIADIC_TEMPLATES __has_extension(cxx_variadic_templates) + +/* There is a bug in clang that comes with Xcode 4.2 where AtomicStrings can't be implicitly converted to Strings + in the presence of move constructors and/or move assignment operators. This bug has been fixed in Xcode 4.3 clang, so we + check for both cxx_rvalue_references as well as the unrelated cxx_nonstatic_member_init feature which we know was added in 4.3 */ +#define WTF_COMPILER_SUPPORTS_CXX_RVALUE_REFERENCES __has_extension(cxx_rvalue_references) && __has_extension(cxx_nonstatic_member_init) + +#define WTF_COMPILER_SUPPORTS_CXX_DELETED_FUNCTIONS __has_extension(cxx_deleted_functions) +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR __has_feature(cxx_nullptr) +#define WTF_COMPILER_SUPPORTS_CXX_EXPLICIT_CONVERSIONS __has_feature(cxx_explicit_conversions) +#define WTF_COMPILER_SUPPORTS_BLOCKS __has_feature(blocks) +#define WTF_COMPILER_SUPPORTS_C_STATIC_ASSERT __has_extension(c_static_assert) +#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL __has_extension(cxx_override_control) +#define WTF_COMPILER_SUPPORTS_HAS_TRIVIAL_DESTRUCTOR __has_extension(has_trivial_destructor) + +#endif + +#ifndef CLANG_PRAGMA +#define CLANG_PRAGMA(PRAGMA) +#endif + +/* COMPILER(MSVC) - Microsoft Visual C++ */ +/* COMPILER(MSVC7_OR_LOWER) - Microsoft Visual C++ 2003 or lower*/ +/* COMPILER(MSVC9_OR_LOWER) - Microsoft Visual C++ 2008 or lower*/ +#if defined(_MSC_VER) +#define WTF_COMPILER_MSVC 1 +#if _MSC_VER < 1400 +#define WTF_COMPILER_MSVC7_OR_LOWER 1 +#elif _MSC_VER < 1600 +#define WTF_COMPILER_MSVC9_OR_LOWER 1 +#endif + +/* Specific compiler features */ +#if !COMPILER(CLANG) && _MSC_VER >= 1600 +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 +#endif + +#if !COMPILER(CLANG) +#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL 1 +#define WTF_COMPILER_QUIRK_FINAL_IS_CALLED_SEALED 1 +#endif + +#endif + +/* COMPILER(RVCT) - ARM RealView Compilation Tools */ +/* COMPILER(RVCT4_OR_GREATER) - ARM RealView Compilation Tools 4.0 or greater */ +#if defined(__CC_ARM) || defined(__ARMCC__) +#define WTF_COMPILER_RVCT 1 +#define RVCT_VERSION_AT_LEAST(major, minor, patch, build) (__ARMCC_VERSION >= (major * 100000 + minor * 10000 + patch * 1000 + build)) +#else +/* Define this for !RVCT compilers, just so we can write things like RVCT_VERSION_AT_LEAST(3, 0, 0, 0). */ +#define RVCT_VERSION_AT_LEAST(major, minor, patch, build) 0 +#endif + +/* COMPILER(GCCE) - GNU Compiler Collection for Embedded */ +#if defined(__GCCE__) +#define WTF_COMPILER_GCCE 1 +#define GCCE_VERSION (__GCCE__ * 10000 + __GCCE_MINOR__ * 100 + __GCCE_PATCHLEVEL__) +#define GCCE_VERSION_AT_LEAST(major, minor, patch) (GCCE_VERSION >= (major * 10000 + minor * 100 + patch)) +#endif + +/* COMPILER(GCC) - GNU Compiler Collection */ +/* --gnu option of the RVCT compiler also defines __GNUC__ */ +#if defined(__GNUC__) && !COMPILER(RVCT) +#define WTF_COMPILER_GCC 1 +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#define GCC_VERSION_AT_LEAST(major, minor, patch) (GCC_VERSION >= (major * 10000 + minor * 100 + patch)) +#else +/* Define this for !GCC compilers, just so we can write things like GCC_VERSION_AT_LEAST(4, 1, 0). */ +#define GCC_VERSION_AT_LEAST(major, minor, patch) 0 +#endif + +/* Specific compiler features */ +#if COMPILER(GCC) && !COMPILER(CLANG) +#if GCC_VERSION_AT_LEAST(4, 7, 0) && defined(__cplusplus) && __cplusplus >= 201103L +#define WTF_COMPILER_SUPPORTS_CXX_RVALUE_REFERENCES 1 +#define WTF_COMPILER_SUPPORTS_CXX_DELETED_FUNCTIONS 1 +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 +#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL 1 +#define WTF_COMPILER_QUIRK_GCC11_GLOBAL_ISINF_ISNAN 1 + +#elif GCC_VERSION_AT_LEAST(4, 6, 0) && defined(__GXX_EXPERIMENTAL_CXX0X__) +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 +#define WTF_COMPILER_QUIRK_GCC11_GLOBAL_ISINF_ISNAN 1 +#endif + +#endif + +/* COMPILER(MINGW) - MinGW GCC */ +/* COMPILER(MINGW64) - mingw-w64 GCC - only used as additional check to exclude mingw.org specific functions */ +#if defined(__MINGW32__) +#define WTF_COMPILER_MINGW 1 +#include <_mingw.h> /* private MinGW header */ + #if defined(__MINGW64_VERSION_MAJOR) /* best way to check for mingw-w64 vs mingw.org */ + #define WTF_COMPILER_MINGW64 1 + #endif /* __MINGW64_VERSION_MAJOR */ +#endif /* __MINGW32__ */ + +/* COMPILER(INTEL) - Intel C++ Compiler */ +#if defined(__INTEL_COMPILER) +#define WTF_COMPILER_INTEL 1 +#endif + +/* COMPILER(SUNCC) */ +#if defined(__SUNPRO_CC) || defined(__SUNPRO_C) +#define WTF_COMPILER_SUNCC 1 +#endif + +/* ==== Compiler features ==== */ + + +/* ALWAYS_INLINE */ + +#ifndef ALWAYS_INLINE +#if COMPILER(GCC) && defined(NDEBUG) && !COMPILER(MINGW) +#define ALWAYS_INLINE inline __attribute__((__always_inline__)) +#elif (COMPILER(MSVC) || COMPILER(RVCT)) && defined(NDEBUG) +#define ALWAYS_INLINE __forceinline +#else +#define ALWAYS_INLINE inline +#endif +#endif + + +/* NEVER_INLINE */ + +#ifndef NEVER_INLINE +#if COMPILER(GCC) +#define NEVER_INLINE __attribute__((__noinline__)) +#elif COMPILER(RVCT) +#define NEVER_INLINE __declspec(noinline) +#else +#define NEVER_INLINE +#endif +#endif + + +/* UNLIKELY */ + +#ifndef UNLIKELY +#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(3, 0, 0, 0) && defined(__GNUC__)) +#define UNLIKELY(x) __builtin_expect((x), 0) +#else +#define UNLIKELY(x) (x) +#endif +#endif + + +/* LIKELY */ + +#ifndef LIKELY +#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(3, 0, 0, 0) && defined(__GNUC__)) +#define LIKELY(x) __builtin_expect((x), 1) +#else +#define LIKELY(x) (x) +#endif +#endif + + +/* NO_RETURN */ + + +#ifndef NO_RETURN +#if COMPILER(GCC) +#define NO_RETURN __attribute((__noreturn__)) +#elif COMPILER(MSVC) || COMPILER(RVCT) +#define NO_RETURN __declspec(noreturn) +#else +#define NO_RETURN +#endif +#endif + + +/* NO_RETURN_WITH_VALUE */ + +#ifndef NO_RETURN_WITH_VALUE +#if !COMPILER(MSVC) +#define NO_RETURN_WITH_VALUE NO_RETURN +#else +#define NO_RETURN_WITH_VALUE +#endif +#endif + + +/* WARN_UNUSED_RETURN */ + +#if COMPILER(GCC) +#define WARN_UNUSED_RETURN __attribute__ ((warn_unused_result)) +#else +#define WARN_UNUSED_RETURN +#endif + +/* OVERRIDE and FINAL */ + +#if COMPILER_SUPPORTS(CXX_OVERRIDE_CONTROL) +#define OVERRIDE override + +#if COMPILER_QUIRK(FINAL_IS_CALLED_SEALED) +#define FINAL sealed +#else +#define FINAL final +#endif + +#else +#define OVERRIDE +#define FINAL +#endif + +/* REFERENCED_FROM_ASM */ + +#ifndef REFERENCED_FROM_ASM +#if COMPILER(GCC) +#define REFERENCED_FROM_ASM __attribute__((used)) +#else +#define REFERENCED_FROM_ASM +#endif +#endif + +/* OBJC_CLASS */ + +#ifndef OBJC_CLASS +#ifdef __OBJC__ +#define OBJC_CLASS @class +#else +#define OBJC_CLASS class +#endif +#endif + +/* ABI */ +#if defined(__ARM_EABI__) || defined(__EABI__) +#define WTF_COMPILER_SUPPORTS_EABI 1 +#endif + +#endif /* WTF_Compiler_h */ diff --git a/3rdparty/masm/wtf/CryptographicallyRandomNumber.h b/3rdparty/masm/wtf/CryptographicallyRandomNumber.h new file mode 100644 index 0000000000..2262b6c3b3 --- /dev/null +++ b/3rdparty/masm/wtf/CryptographicallyRandomNumber.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_CryptographicallyRandomNumber_h +#define WTF_CryptographicallyRandomNumber_h + +#include + +namespace WTF { + +#if USE(OS_RANDOMNESS) +WTF_EXPORT_PRIVATE uint32_t cryptographicallyRandomNumber(); +WTF_EXPORT_PRIVATE void cryptographicallyRandomValues(void* buffer, size_t length); +#endif + +} + +#if USE(OS_RANDOMNESS) +using WTF::cryptographicallyRandomNumber; +using WTF::cryptographicallyRandomValues; +#endif + +#endif diff --git a/3rdparty/masm/wtf/DataLog.h b/3rdparty/masm/wtf/DataLog.h new file mode 100644 index 0000000000..0bd8efe727 --- /dev/null +++ b/3rdparty/masm/wtf/DataLog.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DataLog_h +#define DataLog_h + +#include +#include +#include +#include +#include + +namespace WTF { + +WTF_EXPORT_PRIVATE FilePrintStream& dataFile(); + +WTF_EXPORT_PRIVATE void dataLogFV(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(1, 0); +WTF_EXPORT_PRIVATE void dataLogF(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); +WTF_EXPORT_PRIVATE void dataLogFString(const char*); + +template +void dataLog(const T& value) +{ + dataFile().print(value); +} + +template +void dataLog(const T1& value1, const T2& value2) +{ + dataFile().print(value1, value2); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3) +{ + dataFile().print(value1, value2, value3); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4) +{ + dataFile().print(value1, value2, value3, value4); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5) +{ + dataFile().print(value1, value2, value3, value4, value5); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6) +{ + dataFile().print(value1, value2, value3, value4, value5, value6); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11, value12); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12, const T13& value13) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11, value12, value13); +} + +} // namespace WTF + +using WTF::dataLog; +using WTF::dataLogF; +using WTF::dataLogFString; + +#endif // DataLog_h + diff --git a/3rdparty/masm/wtf/DynamicAnnotations.h b/3rdparty/masm/wtf/DynamicAnnotations.h new file mode 100644 index 0000000000..38acce35e6 --- /dev/null +++ b/3rdparty/masm/wtf/DynamicAnnotations.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_DynamicAnnotations_h +#define WTF_DynamicAnnotations_h + +/* This file defines dynamic annotations for use with dynamic analysis + * tool such as ThreadSanitizer, Valgrind, etc. + * + * Dynamic annotation is a source code annotation that affects + * the generated code (that is, the annotation is not a comment). + * Each such annotation is attached to a particular + * instruction and/or to a particular object (address) in the program. + * + * By using dynamic annotations a developer can give more details to the dynamic + * analysis tool to improve its precision. + * + * In C/C++ program the annotations are represented as C macros. + * With the default build flags, these macros are empty, hence don't affect + * performance of a compiled binary. + * If dynamic annotations are enabled, they just call no-op functions. + * The dynamic analysis tools can intercept these functions and replace them + * with their own implementations. + * + * See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations for more information. + */ + +#if USE(DYNAMIC_ANNOTATIONS) +/* Tell data race detector that we're not interested in reports on the given address range. */ +#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) WTFAnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description) +#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) WTFAnnotateBenignRaceSized(__FILE__, __LINE__, pointer, sizeof(*(pointer)), description) + +/* Annotations for user-defined synchronization mechanisms. + * These annotations can be used to define happens-before arcs in user-defined + * synchronization mechanisms: the race detector will infer an arc from + * the former to the latter when they share the same argument pointer. + * + * The most common case requiring annotations is atomic reference counting: + * bool deref() { + * ANNOTATE_HAPPENS_BEFORE(&m_refCount); + * if (!atomicDecrement(&m_refCount)) { + * // m_refCount is now 0 + * ANNOTATE_HAPPENS_AFTER(&m_refCount); + * // "return true; happens-after each atomicDecrement of m_refCount" + * return true; + * } + * return false; + * } + */ +#define WTF_ANNOTATE_HAPPENS_BEFORE(address) WTFAnnotateHappensBefore(__FILE__, __LINE__, address) +#define WTF_ANNOTATE_HAPPENS_AFTER(address) WTFAnnotateHappensAfter(__FILE__, __LINE__, address) + +#ifdef __cplusplus +extern "C" { +#endif +/* Don't use these directly, use the above macros instead. */ +void WTFAnnotateBenignRaceSized(const char* file, int line, const volatile void* memory, long size, const char* description); +void WTFAnnotateHappensBefore(const char* file, int line, const volatile void* address); +void WTFAnnotateHappensAfter(const char* file, int line, const volatile void* address); +#ifdef __cplusplus +} // extern "C" +#endif + +#else // USE(DYNAMIC_ANNOTATIONS) +/* These macros are empty when dynamic annotations are not enabled so you can + * use them without affecting the performance of release binaries. */ +#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) +#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) +#define WTF_ANNOTATE_HAPPENS_BEFORE(address) +#define WTF_ANNOTATE_HAPPENS_AFTER(address) +#endif // USE(DYNAMIC_ANNOTATIONS) + +#endif // WTF_DynamicAnnotations_h diff --git a/3rdparty/masm/wtf/FilePrintStream.cpp b/3rdparty/masm/wtf/FilePrintStream.cpp new file mode 100644 index 0000000000..b5ab25e0bf --- /dev/null +++ b/3rdparty/masm/wtf/FilePrintStream.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FilePrintStream.h" + +namespace WTF { + +FilePrintStream::FilePrintStream(FILE* file, AdoptionMode adoptionMode) + : m_file(file) + , m_adoptionMode(adoptionMode) +{ +} + +FilePrintStream::~FilePrintStream() +{ + if (m_adoptionMode == Borrow) + return; + fclose(m_file); +} + +PassOwnPtr FilePrintStream::open(const char* filename, const char* mode) +{ + FILE* file = fopen(filename, mode); + if (!file) + return PassOwnPtr(); + + return adoptPtr(new FilePrintStream(file)); +} + +void FilePrintStream::vprintf(const char* format, va_list argList) +{ + vfprintf(m_file, format, argList); +} + +void FilePrintStream::flush() +{ + fflush(m_file); +} + +} // namespace WTF + diff --git a/3rdparty/masm/wtf/FilePrintStream.h b/3rdparty/masm/wtf/FilePrintStream.h new file mode 100644 index 0000000000..bdeab4c479 --- /dev/null +++ b/3rdparty/masm/wtf/FilePrintStream.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FilePrintStream_h +#define FilePrintStream_h + +#include +#include +#include + +namespace WTF { + +class FilePrintStream : public PrintStream { +public: + enum AdoptionMode { + Adopt, + Borrow + }; + + FilePrintStream(FILE*, AdoptionMode = Adopt); + virtual ~FilePrintStream(); + + static PassOwnPtr open(const char* filename, const char* mode); + + FILE* file() { return m_file; } + + void vprintf(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(2, 0); + void flush(); + +private: + FILE* m_file; + AdoptionMode m_adoptionMode; +}; + +} // namespace WTF + +using WTF::FilePrintStream; + +#endif // FilePrintStream_h + diff --git a/3rdparty/masm/wtf/Locker.h b/3rdparty/masm/wtf/Locker.h new file mode 100644 index 0000000000..c465b99ea4 --- /dev/null +++ b/3rdparty/masm/wtf/Locker.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef Locker_h +#define Locker_h + +#include + +namespace WTF { + +template class Locker { + WTF_MAKE_NONCOPYABLE(Locker); +public: + Locker(T& lockable) : m_lockable(lockable) { m_lockable.lock(); } + ~Locker() { m_lockable.unlock(); } +private: + T& m_lockable; +}; + +} + +using WTF::Locker; + +#endif diff --git a/3rdparty/masm/wtf/NotFound.h b/3rdparty/masm/wtf/NotFound.h new file mode 100644 index 0000000000..4263bcecab --- /dev/null +++ b/3rdparty/masm/wtf/NotFound.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NotFound_h +#define NotFound_h + +namespace WTF { + + const size_t notFound = static_cast(-1); + +} // namespace WTF + +using WTF::notFound; + +#endif // NotFound_h diff --git a/3rdparty/masm/wtf/NullPtr.h b/3rdparty/masm/wtf/NullPtr.h new file mode 100644 index 0000000000..98c05140d8 --- /dev/null +++ b/3rdparty/masm/wtf/NullPtr.h @@ -0,0 +1,56 @@ +/* + +Copyright (C) 2010 Apple Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef NullPtr_h +#define NullPtr_h + +// For compilers and standard libraries that do not yet include it, this adds the +// nullptr_t type and nullptr object. They are defined in the same namespaces they +// would be in compiler and library that had the support. + +#include + +#if COMPILER_SUPPORTS(CXX_NULLPTR) || defined(_LIBCPP_VERSION) + +#include + +// libstdc++ supports nullptr_t starting with gcc 4.6. +#if defined(__GLIBCXX__) && __GLIBCXX__ < 20110325 +namespace std { +typedef decltype(nullptr) nullptr_t; +} +#endif + +#else + +namespace std { +class WTF_EXPORT_PRIVATE nullptr_t { }; +} +extern WTF_EXPORT_PRIVATE std::nullptr_t nullptr; + +#endif + +#endif diff --git a/3rdparty/masm/wtf/OSAllocator.h b/3rdparty/masm/wtf/OSAllocator.h new file mode 100644 index 0000000000..a12a467497 --- /dev/null +++ b/3rdparty/masm/wtf/OSAllocator.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OSAllocator_h +#define OSAllocator_h + +#include +#include +#include + +namespace WTF { + +class OSAllocator { +public: + enum Usage { + UnknownUsage = -1, + FastMallocPages = VM_TAG_FOR_TCMALLOC_MEMORY, + JSGCHeapPages = VM_TAG_FOR_COLLECTOR_MEMORY, + JSVMStackPages = VM_TAG_FOR_REGISTERFILE_MEMORY, + JSJITCodePages = VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY, + }; + + // These methods are symmetric; reserveUncommitted allocates VM in an uncommitted state, + // releaseDecommitted should be called on a region of VM allocated by a single reservation, + // the memory must all currently be in a decommitted state. + static void* reserveUncommitted(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool includesGuardPages = false); + WTF_EXPORT_PRIVATE static void releaseDecommitted(void*, size_t); + + // These methods are symmetric; they commit or decommit a region of VM (uncommitted VM should + // never be accessed, since the OS may not have attached physical memory for these regions). + // Clients should only call commit on uncommitted regions and decommit on committed regions. + static void commit(void*, size_t, bool writable, bool executable); + static void decommit(void*, size_t); + + // These methods are symmetric; reserveAndCommit allocates VM in an committed state, + // decommitAndRelease should be called on a region of VM allocated by a single reservation, + // the memory must all currently be in a committed state. + WTF_EXPORT_PRIVATE static void* reserveAndCommit(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool includesGuardPages = false); + static void decommitAndRelease(void* base, size_t size); + + // These methods are akin to reserveAndCommit/decommitAndRelease, above - however rather than + // committing/decommitting the entire region additional parameters allow a subregion to be + // specified. + static void* reserveAndCommit(size_t reserveSize, size_t commitSize, Usage = UnknownUsage, bool writable = true, bool executable = false); + static void decommitAndRelease(void* releaseBase, size_t releaseSize, void* decommitBase, size_t decommitSize); + + // Reallocate an existing, committed allocation. + // The prior allocation must be fully comitted, and the new size will also be fully committed. + // This interface is provided since it may be possible to optimize this operation on some platforms. + template + static T* reallocateCommitted(T*, size_t oldSize, size_t newSize, Usage = UnknownUsage, bool writable = true, bool executable = false); +}; + +inline void* OSAllocator::reserveAndCommit(size_t reserveSize, size_t commitSize, Usage usage, bool writable, bool executable) +{ + void* base = reserveUncommitted(reserveSize, usage, writable, executable); + commit(base, commitSize, writable, executable); + return base; +} + +inline void OSAllocator::decommitAndRelease(void* releaseBase, size_t releaseSize, void* decommitBase, size_t decommitSize) +{ + ASSERT(decommitBase >= releaseBase && (static_cast(decommitBase) + decommitSize) <= (static_cast(releaseBase) + releaseSize)); +#if OS(WINCE) + // On most platforms we can actually skip this final decommit; releasing the VM will + // implicitly decommit any physical memory in the region. This is not true on WINCE. + decommit(decommitBase, decommitSize); +#else + UNUSED_PARAM(decommitBase); + UNUSED_PARAM(decommitSize); +#endif + releaseDecommitted(releaseBase, releaseSize); +} + +inline void OSAllocator::decommitAndRelease(void* base, size_t size) +{ + decommitAndRelease(base, size, base, size); +} + +template +inline T* OSAllocator::reallocateCommitted(T* oldBase, size_t oldSize, size_t newSize, Usage usage, bool writable, bool executable) +{ + void* newBase = reserveAndCommit(newSize, usage, writable, executable); + memcpy(newBase, oldBase, std::min(oldSize, newSize)); + decommitAndRelease(oldBase, oldSize); + return static_cast(newBase); +} + +} // namespace WTF + +using WTF::OSAllocator; + +#endif // OSAllocator_h diff --git a/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/3rdparty/masm/wtf/OSAllocatorPosix.cpp new file mode 100644 index 0000000000..b5b903b8a3 --- /dev/null +++ b/3rdparty/masm/wtf/OSAllocatorPosix.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "OSAllocator.h" + +#include "PageAllocation.h" +#include +#include +#include +#include + +namespace WTF { + +void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) +{ +#if OS(QNX) + // Reserve memory with PROT_NONE and MAP_LAZY so it isn't committed now. + void* result = mmap(0, bytes, PROT_NONE, MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); + if (result == MAP_FAILED) + CRASH(); +#elif OS(LINUX) + void* result = mmap(0, bytes, PROT_NONE, MAP_NORESERVE | MAP_PRIVATE | MAP_ANON, -1, 0); + if (result == MAP_FAILED) + CRASH(); + madvise(result, bytes, MADV_DONTNEED); +#else + void* result = reserveAndCommit(bytes, usage, writable, executable, includesGuardPages); +#if HAVE(MADV_FREE_REUSE) + // To support the "reserve then commit" model, we have to initially decommit. + while (madvise(result, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { } +#endif + +#endif // OS(QNX) + + return result; +} + +void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) +{ + // All POSIX reservations start out logically committed. + int protection = PROT_READ; + if (writable) + protection |= PROT_WRITE; + if (executable) + protection |= PROT_EXEC; + + int flags = MAP_PRIVATE | MAP_ANON; +#if PLATFORM(IOS) + if (executable) + flags |= MAP_JIT; +#endif + +#if OS(DARWIN) + int fd = usage; +#else + UNUSED_PARAM(usage); + int fd = -1; +#endif + + void* result = 0; +#if (OS(DARWIN) && CPU(X86_64)) + if (executable) { + ASSERT(includesGuardPages); + // Cook up an address to allocate at, using the following recipe: + // 17 bits of zero, stay in userspace kids. + // 26 bits of randomness for ASLR. + // 21 bits of zero, at least stay aligned within one level of the pagetables. + // + // But! - as a temporary workaround for some plugin problems (rdar://problem/6812854), + // for now instead of 2^26 bits of ASLR lets stick with 25 bits of randomization plus + // 2^24, which should put up somewhere in the middle of userspace (in the address range + // 0x200000000000 .. 0x5fffffffffff). + intptr_t randomLocation = 0; + randomLocation = arc4random() & ((1 << 25) - 1); + randomLocation += (1 << 24); + randomLocation <<= 21; + result = reinterpret_cast(randomLocation); + } +#endif + + result = mmap(result, bytes, protection, flags, fd, 0); + if (result == MAP_FAILED) { +#if ENABLE(LLINT) + if (executable) + result = 0; + else +#endif + CRASH(); + } + if (result && includesGuardPages) { + // We use mmap to remap the guardpages rather than using mprotect as + // mprotect results in multiple references to the code region. This + // breaks the madvise based mechanism we use to return physical memory + // to the OS. + mmap(result, pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0); + mmap(static_cast(result) + bytes - pageSize(), pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0); + } + return result; +} + +void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable) +{ +#if OS(QNX) + int protection = PROT_READ; + if (writable) + protection |= PROT_WRITE; + if (executable) + protection |= PROT_EXEC; + if (MAP_FAILED == mmap(address, bytes, protection, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0)) + CRASH(); +#elif OS(LINUX) + int protection = PROT_READ; + if (writable) + protection |= PROT_WRITE; + if (executable) + protection |= PROT_EXEC; + if (mprotect(address, bytes, protection)) + CRASH(); + madvise(address, bytes, MADV_WILLNEED); +#elif HAVE(MADV_FREE_REUSE) + UNUSED_PARAM(writable); + UNUSED_PARAM(executable); + while (madvise(address, bytes, MADV_FREE_REUSE) == -1 && errno == EAGAIN) { } +#else + // Non-MADV_FREE_REUSE reservations automatically commit on demand. + UNUSED_PARAM(address); + UNUSED_PARAM(bytes); + UNUSED_PARAM(writable); + UNUSED_PARAM(executable); +#endif +} + +void OSAllocator::decommit(void* address, size_t bytes) +{ +#if OS(QNX) + // Use PROT_NONE and MAP_LAZY to decommit the pages. + mmap(address, bytes, PROT_NONE, MAP_FIXED | MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); +#elif OS(LINUX) + madvise(address, bytes, MADV_DONTNEED); + if (mprotect(address, bytes, PROT_NONE)) + CRASH(); +#elif HAVE(MADV_FREE_REUSE) + while (madvise(address, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { } +#elif HAVE(MADV_FREE) + while (madvise(address, bytes, MADV_FREE) == -1 && errno == EAGAIN) { } +#elif HAVE(MADV_DONTNEED) + while (madvise(address, bytes, MADV_DONTNEED) == -1 && errno == EAGAIN) { } +#else + UNUSED_PARAM(address); + UNUSED_PARAM(bytes); +#endif +} + +void OSAllocator::releaseDecommitted(void* address, size_t bytes) +{ + int result = munmap(address, bytes); + if (result == -1) + CRASH(); +} + +} // namespace WTF diff --git a/3rdparty/masm/wtf/OSAllocatorWin.cpp b/3rdparty/masm/wtf/OSAllocatorWin.cpp new file mode 100644 index 0000000000..7f5d9b8904 --- /dev/null +++ b/3rdparty/masm/wtf/OSAllocatorWin.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "OSAllocator.h" + +#include "windows.h" +#include + +namespace WTF { + +static inline DWORD protection(bool writable, bool executable) +{ + return executable ? + (writable ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ) : + (writable ? PAGE_READWRITE : PAGE_READONLY); +} + +void* OSAllocator::reserveUncommitted(size_t bytes, Usage, bool writable, bool executable, bool) +{ + void* result = VirtualAlloc(0, bytes, MEM_RESERVE, protection(writable, executable)); + if (!result) + CRASH(); + return result; +} + +void* OSAllocator::reserveAndCommit(size_t bytes, Usage, bool writable, bool executable, bool) +{ + void* result = VirtualAlloc(0, bytes, MEM_RESERVE | MEM_COMMIT, protection(writable, executable)); + if (!result) + CRASH(); + return result; +} + +void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable) +{ + void* result = VirtualAlloc(address, bytes, MEM_COMMIT, protection(writable, executable)); + if (!result) + CRASH(); +} + +void OSAllocator::decommit(void* address, size_t bytes) +{ + bool result = VirtualFree(address, bytes, MEM_DECOMMIT); + if (!result) + CRASH(); +} + +void OSAllocator::releaseDecommitted(void* address, size_t bytes) +{ + // According to http://msdn.microsoft.com/en-us/library/aa366892(VS.85).aspx, + // dwSize must be 0 if dwFreeType is MEM_RELEASE. + bool result = VirtualFree(address, 0, MEM_RELEASE); + if (!result) + CRASH(); +} + +} // namespace WTF diff --git a/3rdparty/masm/wtf/PageAllocation.h b/3rdparty/masm/wtf/PageAllocation.h new file mode 100644 index 0000000000..18d31880c0 --- /dev/null +++ b/3rdparty/masm/wtf/PageAllocation.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PageAllocation_h +#define PageAllocation_h + +#include +#include +#include +#include +#include +#include + +#if OS(DARWIN) +#include +#include +#endif + +#if OS(WINDOWS) +#include +#include +#endif + +#if HAVE(ERRNO_H) +#include +#endif + +#if HAVE(MMAP) +#include +#include +#endif + +namespace WTF { + +/* + PageAllocation + + The PageAllocation class provides a cross-platform memory allocation interface + with similar capabilities to posix mmap/munmap. Memory is allocated by calling + PageAllocation::allocate, and deallocated by calling deallocate on the + PageAllocation object. The PageAllocation holds the allocation's base pointer + and size. + + The allocate method is passed the size required (which must be a multiple of + the system page size, which can be accessed using PageAllocation::pageSize). + Callers may also optinally provide a flag indicating the usage (for use by + system memory usage tracking tools, where implemented), and boolean values + specifying the required protection (defaulting to writable, non-executable). +*/ + +class PageAllocation : private PageBlock { +public: + PageAllocation() + { + } + + using PageBlock::size; + using PageBlock::base; + +#ifndef __clang__ + using PageBlock::operator bool; +#else + // FIXME: This is a workaround for , wherein Clang incorrectly emits an access + // control warning when a client tries to use operator bool exposed above via "using PageBlock::operator bool". + operator bool() const { return PageBlock::operator bool(); } +#endif + + static PageAllocation allocate(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) + { + ASSERT(isPageAligned(size)); + return PageAllocation(OSAllocator::reserveAndCommit(size, usage, writable, executable), size); + } + + void deallocate() + { + // Clear base & size before calling release; if this is *inside* allocation + // then we won't be able to clear then after deallocating the memory. + PageAllocation tmp; + std::swap(tmp, *this); + + ASSERT(tmp); + ASSERT(!*this); + + OSAllocator::decommitAndRelease(tmp.base(), tmp.size()); + } + +private: + PageAllocation(void* base, size_t size) + : PageBlock(base, size, false) + { + } +}; + +} // namespace WTF + +using WTF::PageAllocation; + +#endif // PageAllocation_h diff --git a/3rdparty/masm/wtf/PageAllocationAligned.cpp b/3rdparty/masm/wtf/PageAllocationAligned.cpp new file mode 100644 index 0000000000..6f54710d0b --- /dev/null +++ b/3rdparty/masm/wtf/PageAllocationAligned.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PageAllocationAligned.h" + +namespace WTF { + +PageAllocationAligned PageAllocationAligned::allocate(size_t size, size_t alignment, OSAllocator::Usage usage, bool writable, bool executable) +{ + ASSERT(isPageAligned(size)); + ASSERT(isPageAligned(alignment)); + ASSERT(isPowerOfTwo(alignment)); + ASSERT(size >= alignment); + size_t alignmentMask = alignment - 1; + +#if OS(DARWIN) + int flags = VM_FLAGS_ANYWHERE; + if (usage != OSAllocator::UnknownUsage) + flags |= usage; + int protection = PROT_READ; + if (writable) + protection |= PROT_WRITE; + if (executable) + protection |= PROT_EXEC; + + vm_address_t address = 0; + vm_map(current_task(), &address, size, alignmentMask, flags, MEMORY_OBJECT_NULL, 0, FALSE, protection, PROT_READ | PROT_WRITE | PROT_EXEC, VM_INHERIT_DEFAULT); + return PageAllocationAligned(reinterpret_cast(address), size); +#else + size_t alignmentDelta = alignment - pageSize(); + + // Resererve with suffcient additional VM to correctly align. + size_t reservationSize = size + alignmentDelta; + void* reservationBase = OSAllocator::reserveUncommitted(reservationSize, usage, writable, executable); + + // Select an aligned region within the reservation and commit. + void* alignedBase = reinterpret_cast(reservationBase) & alignmentMask + ? reinterpret_cast((reinterpret_cast(reservationBase) & ~alignmentMask) + alignment) + : reservationBase; + OSAllocator::commit(alignedBase, size, writable, executable); + + return PageAllocationAligned(alignedBase, size, reservationBase, reservationSize); +#endif +} + +void PageAllocationAligned::deallocate() +{ + // Clear base & size before calling release; if this is *inside* allocation + // then we won't be able to clear then after deallocating the memory. + PageAllocationAligned tmp; + std::swap(tmp, *this); + + ASSERT(tmp); + ASSERT(!*this); + +#if OS(DARWIN) + vm_deallocate(current_task(), reinterpret_cast(tmp.base()), tmp.size()); +#else + ASSERT(tmp.m_reservation.contains(tmp.base(), tmp.size())); + OSAllocator::decommitAndRelease(tmp.m_reservation.base(), tmp.m_reservation.size(), tmp.base(), tmp.size()); +#endif +} + +} // namespace WTF diff --git a/3rdparty/masm/wtf/PageAllocationAligned.h b/3rdparty/masm/wtf/PageAllocationAligned.h new file mode 100644 index 0000000000..c018dabd8e --- /dev/null +++ b/3rdparty/masm/wtf/PageAllocationAligned.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PageAllocationAligned_h +#define PageAllocationAligned_h + +#include +#include + +namespace WTF { + +class PageAllocationAligned : private PageBlock { +public: + PageAllocationAligned() + { + } + + using PageBlock::operator bool; + using PageBlock::size; + using PageBlock::base; + + static PageAllocationAligned allocate(size_t size, size_t alignment, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false); + + void deallocate(); + +private: +#if OS(DARWIN) + PageAllocationAligned(void* base, size_t size) + : PageBlock(base, size, false) + { + } +#else + PageAllocationAligned(void* base, size_t size, void* reservationBase, size_t reservationSize) + : PageBlock(base, size, false) + , m_reservation(reservationBase, reservationSize, false) + { + } + + PageBlock m_reservation; +#endif +}; + + +} // namespace WTF + +using WTF::PageAllocationAligned; + +#endif // PageAllocationAligned_h diff --git a/3rdparty/masm/wtf/PageBlock.cpp b/3rdparty/masm/wtf/PageBlock.cpp new file mode 100644 index 0000000000..8bbd7eb600 --- /dev/null +++ b/3rdparty/masm/wtf/PageBlock.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PageBlock.h" + +#if OS(UNIX) +#include +#endif + +#if OS(WINDOWS) +#include +#include +#endif + +namespace WTF { + +static size_t s_pageSize; +static size_t s_pageMask; + +#if OS(UNIX) + +inline size_t systemPageSize() +{ + return getpagesize(); +} + +#elif OS(WINDOWS) + +inline size_t systemPageSize() +{ + static size_t size = 0; + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + size = system_info.dwPageSize; + return size; +} + +#endif + +size_t pageSize() +{ + if (!s_pageSize) + s_pageSize = systemPageSize(); + ASSERT(isPowerOfTwo(s_pageSize)); + return s_pageSize; +} + +size_t pageMask() +{ + if (!s_pageMask) + s_pageMask = ~(pageSize() - 1); + return s_pageMask; +} + +} // namespace WTF diff --git a/3rdparty/masm/wtf/PageBlock.h b/3rdparty/masm/wtf/PageBlock.h new file mode 100644 index 0000000000..56e5570178 --- /dev/null +++ b/3rdparty/masm/wtf/PageBlock.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PageBlock_h +#define PageBlock_h + +namespace WTF { + +WTF_EXPORT_PRIVATE size_t pageSize(); +WTF_EXPORT_PRIVATE size_t pageMask(); +inline bool isPageAligned(void* address) { return !(reinterpret_cast(address) & (pageSize() - 1)); } +inline bool isPageAligned(size_t size) { return !(size & (pageSize() - 1)); } +inline bool isPowerOfTwo(size_t size) { return !(size & (size - 1)); } + +class PageBlock { +public: + PageBlock(); + PageBlock(const PageBlock&); + PageBlock(void*, size_t, bool hasGuardPages); + + void* base() const { return m_base; } + size_t size() const { return m_size; } + + operator bool() const { return !!m_realBase; } + + bool contains(void* containedBase, size_t containedSize) + { + return containedBase >= m_base + && (static_cast(containedBase) + containedSize) <= (static_cast(m_base) + m_size); + } + +private: + void* m_realBase; + void* m_base; + size_t m_size; +}; + +inline PageBlock::PageBlock() + : m_realBase(0) + , m_base(0) + , m_size(0) +{ +} + +inline PageBlock::PageBlock(const PageBlock& other) + : m_realBase(other.m_realBase) + , m_base(other.m_base) + , m_size(other.m_size) +{ +} + +inline PageBlock::PageBlock(void* base, size_t size, bool hasGuardPages) + : m_realBase(base) + , m_base(static_cast(base) + ((base && hasGuardPages) ? pageSize() : 0)) + , m_size(size) +{ +} + +} // namespace WTF + +using WTF::pageSize; +using WTF::isPageAligned; +using WTF::isPageAligned; +using WTF::isPowerOfTwo; + +#endif // PageBlock_h diff --git a/3rdparty/masm/wtf/PageReservation.h b/3rdparty/masm/wtf/PageReservation.h new file mode 100644 index 0000000000..77783ebcc4 --- /dev/null +++ b/3rdparty/masm/wtf/PageReservation.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PageReservation_h +#define PageReservation_h + +#include + +namespace WTF { + +/* + PageReservation + + Like PageAllocation, the PageReservation class provides a cross-platform memory + allocation interface, but with a set of capabilities more similar to that of + VirtualAlloc than posix mmap. PageReservation can be used to allocate virtual + memory without committing physical memory pages using PageReservation::reserve. + Following a call to reserve all memory in the region is in a decommited state, + in which the memory should not be used (accessing the memory may cause a fault). + + Before using memory it must be committed by calling commit, which is passed start + and size values (both of which require system page size granularity). One the + committed memory is no longer needed 'decommit' may be called to return the + memory to its devommitted state. Commit should only be called on memory that is + currently decommitted, and decommit should only be called on memory regions that + are currently committed. All memory should be decommited before the reservation + is deallocated. Values in memory may not be retained accross a pair of calls if + the region of memory is decommitted and then committed again. + + Memory protection should not be changed on decommitted memory, and if protection + is changed on memory while it is committed it should be returned to the orignal + protection before decommit is called. +*/ + +class PageReservation : private PageBlock { +public: + PageReservation() + : m_committed(0) + , m_writable(false) + , m_executable(false) + { + } + + using PageBlock::base; + using PageBlock::size; + +#ifndef __clang__ + using PageBlock::operator bool; +#else + // FIXME: This is a workaround for , wherein Clang incorrectly emits an access + // control warning when a client tries to use operator bool exposed above via "using PageBlock::operator bool". + operator bool() const { return PageBlock::operator bool(); } +#endif + + void commit(void* start, size_t size) + { + ASSERT(*this); + ASSERT(isPageAligned(start)); + ASSERT(isPageAligned(size)); + ASSERT(contains(start, size)); + + m_committed += size; + OSAllocator::commit(start, size, m_writable, m_executable); + } + + void decommit(void* start, size_t size) + { + ASSERT(*this); + ASSERT(isPageAligned(start)); + ASSERT(isPageAligned(size)); + ASSERT(contains(start, size)); + + m_committed -= size; + OSAllocator::decommit(start, size); + } + + size_t committed() + { + return m_committed; + } + + static PageReservation reserve(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) + { + ASSERT(isPageAligned(size)); + return PageReservation(OSAllocator::reserveUncommitted(size, usage, writable, executable), size, writable, executable, false); + } + + static PageReservation reserveWithGuardPages(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) + { + ASSERT(isPageAligned(size)); + return PageReservation(OSAllocator::reserveUncommitted(size + pageSize() * 2, usage, writable, executable, true), size, writable, executable, true); + } + + void deallocate() + { + ASSERT(!m_committed); + + // Clear base & size before calling release; if this is *inside* allocation + // then we won't be able to clear then after deallocating the memory. + PageReservation tmp; + std::swap(tmp, *this); + + ASSERT(tmp); + ASSERT(!*this); + + OSAllocator::releaseDecommitted(tmp.base(), tmp.size()); + } + +private: + PageReservation(void* base, size_t size, bool writable, bool executable, bool hasGuardPages) + : PageBlock(base, size, hasGuardPages) + , m_committed(0) + , m_writable(writable) + , m_executable(executable) + { + } + + size_t m_committed; + bool m_writable; + bool m_executable; +}; + +} + +using WTF::PageReservation; + +#endif // PageReservation_h diff --git a/3rdparty/masm/wtf/Platform.h b/3rdparty/masm/wtf/Platform.h new file mode 100644 index 0000000000..f2fd3b0ad5 --- /dev/null +++ b/3rdparty/masm/wtf/Platform.h @@ -0,0 +1,1212 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_Platform_h +#define WTF_Platform_h + +/* Include compiler specific macros */ +#include + +/* ==== PLATFORM handles OS, operating environment, graphics API, and + CPU. This macro will be phased out in favor of platform adaptation + macros, policy decision macros, and top-level port definitions. ==== */ +#define PLATFORM(WTF_FEATURE) (defined WTF_PLATFORM_##WTF_FEATURE && WTF_PLATFORM_##WTF_FEATURE) + + +/* ==== Platform adaptation macros: these describe properties of the target environment. ==== */ + +/* CPU() - the target CPU architecture */ +#define CPU(WTF_FEATURE) (defined WTF_CPU_##WTF_FEATURE && WTF_CPU_##WTF_FEATURE) +/* HAVE() - specific system features (headers, functions or similar) that are present or not */ +#define HAVE(WTF_FEATURE) (defined HAVE_##WTF_FEATURE && HAVE_##WTF_FEATURE) +/* OS() - underlying operating system; only to be used for mandated low-level services like + virtual memory, not to choose a GUI toolkit */ +#define OS(WTF_FEATURE) (defined WTF_OS_##WTF_FEATURE && WTF_OS_##WTF_FEATURE) + + +/* ==== Policy decision macros: these define policy choices for a particular port. ==== */ + +/* USE() - use a particular third-party library or optional OS service */ +#define USE(WTF_FEATURE) (defined WTF_USE_##WTF_FEATURE && WTF_USE_##WTF_FEATURE) +/* ENABLE() - turn on a specific feature of WebKit */ +#define ENABLE(WTF_FEATURE) (defined ENABLE_##WTF_FEATURE && ENABLE_##WTF_FEATURE) + + +/* ==== CPU() - the target CPU architecture ==== */ + +/* This also defines CPU(BIG_ENDIAN) or CPU(MIDDLE_ENDIAN) or neither, as appropriate. */ + +/* CPU(ALPHA) - DEC Alpha */ +#if defined(__alpha__) +#define WTF_CPU_ALPHA 1 +#endif + +/* CPU(IA64) - Itanium / IA-64 */ +#if defined(__ia64__) +#define WTF_CPU_IA64 1 +/* 32-bit mode on Itanium */ +#if !defined(__LP64__) +#define WTF_CPU_IA64_32 1 +#endif +#endif + +/* CPU(MIPS) - MIPS 32-bit */ +/* Note: Only O32 ABI is tested, so we enable it for O32 ABI for now. */ +#if (defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_)) \ + && defined(_ABIO32) +#define WTF_CPU_MIPS 1 +#if defined(__MIPSEB__) +#define WTF_CPU_BIG_ENDIAN 1 +#endif +#define WTF_MIPS_PIC (defined __PIC__) +#define WTF_MIPS_ARCH __mips +#define WTF_MIPS_ISA(v) (defined WTF_MIPS_ARCH && WTF_MIPS_ARCH == v) +#define WTF_MIPS_ISA_AT_LEAST(v) (defined WTF_MIPS_ARCH && WTF_MIPS_ARCH >= v) +#define WTF_MIPS_ARCH_REV __mips_isa_rev +#define WTF_MIPS_ISA_REV(v) (defined WTF_MIPS_ARCH_REV && WTF_MIPS_ARCH_REV == v) +#define WTF_MIPS_DOUBLE_FLOAT (defined __mips_hard_float && !defined __mips_single_float) +#define WTF_MIPS_FP64 (defined __mips_fpr && __mips_fpr == 64) +/* MIPS requires allocators to use aligned memory */ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 +#endif /* MIPS */ + +/* CPU(PPC) - PowerPC 32-bit */ +#if defined(__ppc__) \ + || defined(__PPC__) \ + || defined(__powerpc__) \ + || defined(__powerpc) \ + || defined(__POWERPC__) \ + || defined(_M_PPC) \ + || defined(__PPC) +#define WTF_CPU_PPC 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(PPC64) - PowerPC 64-bit */ +#if defined(__ppc64__) \ + || defined(__PPC64__) +#define WTF_CPU_PPC64 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(SH4) - SuperH SH-4 */ +#if defined(__SH4__) +#define WTF_CPU_SH4 1 +#endif + +/* CPU(SPARC32) - SPARC 32-bit */ +#if defined(__sparc) && !defined(__arch64__) || defined(__sparcv8) +#define WTF_CPU_SPARC32 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(SPARC64) - SPARC 64-bit */ +#if defined(__sparc__) && defined(__arch64__) || defined (__sparcv9) +#define WTF_CPU_SPARC64 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(SPARC) - any SPARC, true for CPU(SPARC32) and CPU(SPARC64) */ +#if CPU(SPARC32) || CPU(SPARC64) +#define WTF_CPU_SPARC 1 +#endif + +/* CPU(S390X) - S390 64-bit */ +#if defined(__s390x__) +#define WTF_CPU_S390X 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(S390) - S390 32-bit */ +#if defined(__s390__) +#define WTF_CPU_S390 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(X86) - i386 / x86 32-bit */ +#if defined(__i386__) \ + || defined(i386) \ + || defined(_M_IX86) \ + || defined(_X86_) \ + || defined(__THW_INTEL) +#define WTF_CPU_X86 1 +#endif + +/* CPU(X86_64) - AMD64 / Intel64 / x86_64 64-bit */ +#if defined(__x86_64__) \ + || defined(_M_X64) +#define WTF_CPU_X86_64 1 +#endif + +/* CPU(ARM) - ARM, any version*/ +#if defined(arm) \ + || defined(__arm__) \ + || defined(ARM) \ + || defined(_ARM_) +#define WTF_CPU_ARM 1 + +#if defined(__ARM_PCS_VFP) +#define WTF_CPU_ARM_HARDFP 1 +#endif + +#if defined(__ARMEB__) || (COMPILER(RVCT) && defined(__BIG_ENDIAN)) +#define WTF_CPU_BIG_ENDIAN 1 + +#elif !defined(__ARM_EABI__) \ + && !defined(__EABI__) \ + && !defined(__VFP_FP__) \ + && !defined(_WIN32_WCE) \ + && !defined(ANDROID) +#define WTF_CPU_MIDDLE_ENDIAN 1 + +#endif + +#define WTF_ARM_ARCH_AT_LEAST(N) (CPU(ARM) && WTF_ARM_ARCH_VERSION >= N) + +/* Set WTF_ARM_ARCH_VERSION */ +#if defined(__ARM_ARCH_4__) \ + || defined(__ARM_ARCH_4T__) \ + || defined(__MARM_ARMV4__) \ + || defined(_ARMV4I_) +#define WTF_ARM_ARCH_VERSION 4 + +#elif defined(__ARM_ARCH_5__) \ + || defined(__ARM_ARCH_5T__) \ + || defined(__MARM_ARMV5__) +#define WTF_ARM_ARCH_VERSION 5 + +#elif defined(__ARM_ARCH_5E__) \ + || defined(__ARM_ARCH_5TE__) \ + || defined(__ARM_ARCH_5TEJ__) +#define WTF_ARM_ARCH_VERSION 5 +/*ARMv5TE requires allocators to use aligned memory*/ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 + +#elif defined(__ARM_ARCH_6__) \ + || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6T2__) \ + || defined(__ARMV6__) +#define WTF_ARM_ARCH_VERSION 6 + +#elif defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) +#define WTF_ARM_ARCH_VERSION 7 + +/* RVCT sets _TARGET_ARCH_ARM */ +#elif defined(__TARGET_ARCH_ARM) +#define WTF_ARM_ARCH_VERSION __TARGET_ARCH_ARM + +#if defined(__TARGET_ARCH_5E) \ + || defined(__TARGET_ARCH_5TE) \ + || defined(__TARGET_ARCH_5TEJ) +/*ARMv5TE requires allocators to use aligned memory*/ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 +#endif + +#else +#define WTF_ARM_ARCH_VERSION 0 + +#endif + +/* Set WTF_THUMB_ARCH_VERSION */ +#if defined(__ARM_ARCH_4T__) +#define WTF_THUMB_ARCH_VERSION 1 + +#elif defined(__ARM_ARCH_5T__) \ + || defined(__ARM_ARCH_5TE__) \ + || defined(__ARM_ARCH_5TEJ__) +#define WTF_THUMB_ARCH_VERSION 2 + +#elif defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6M__) +#define WTF_THUMB_ARCH_VERSION 3 + +#elif defined(__ARM_ARCH_6T2__) \ + || defined(__ARM_ARCH_7__) \ + || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) \ + || defined(__ARM_ARCH_7M__) +#define WTF_THUMB_ARCH_VERSION 4 + +/* RVCT sets __TARGET_ARCH_THUMB */ +#elif defined(__TARGET_ARCH_THUMB) +#define WTF_THUMB_ARCH_VERSION __TARGET_ARCH_THUMB + +#else +#define WTF_THUMB_ARCH_VERSION 0 +#endif + + +/* CPU(ARMV5_OR_LOWER) - ARM instruction set v5 or earlier */ +/* On ARMv5 and below the natural alignment is required. + And there are some other differences for v5 or earlier. */ +#if !defined(ARMV5_OR_LOWER) && !WTF_ARM_ARCH_AT_LEAST(6) +#define WTF_CPU_ARMV5_OR_LOWER 1 +#endif + + +/* CPU(ARM_TRADITIONAL) - Thumb2 is not available, only traditional ARM (v4 or greater) */ +/* CPU(ARM_THUMB2) - Thumb2 instruction set is available */ +/* Only one of these will be defined. */ +#if !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) +# if defined(thumb2) || defined(__thumb2__) \ + || ((defined(__thumb) || defined(__thumb__)) && WTF_THUMB_ARCH_VERSION == 4) +# define WTF_CPU_ARM_TRADITIONAL 0 +# define WTF_CPU_ARM_THUMB2 1 +# elif WTF_ARM_ARCH_AT_LEAST(4) +# define WTF_CPU_ARM_TRADITIONAL 1 +# define WTF_CPU_ARM_THUMB2 0 +# else +# error "Not supported ARM architecture" +# endif +#elif CPU(ARM_TRADITIONAL) && CPU(ARM_THUMB2) /* Sanity Check */ +# error "Cannot use both of WTF_CPU_ARM_TRADITIONAL and WTF_CPU_ARM_THUMB2 platforms" +#endif /* !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) */ + +#if defined(__ARM_NEON__) && !defined(WTF_CPU_ARM_NEON) +#define WTF_CPU_ARM_NEON 1 +#endif + +#if CPU(ARM_NEON) && (!COMPILER(GCC) || GCC_VERSION_AT_LEAST(4, 7, 0)) +// All NEON intrinsics usage can be disabled by this macro. +#define HAVE_ARM_NEON_INTRINSICS 1 +#endif + +#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) +#define WTF_CPU_ARM_VFP 1 +#endif + +#if defined(__ARM_ARCH_7S__) +#define WTF_CPU_APPLE_ARMV7S 1 +#endif + +#endif /* ARM */ + +#if CPU(ARM) || CPU(MIPS) || CPU(SH4) || CPU(SPARC) +#define WTF_CPU_NEEDS_ALIGNED_ACCESS 1 +#endif + +/* ==== OS() - underlying operating system; only to be used for mandated low-level services like + virtual memory, not to choose a GUI toolkit ==== */ + +/* OS(ANDROID) - Android */ +#ifdef ANDROID +#define WTF_OS_ANDROID 1 +#endif + +/* OS(AIX) - AIX */ +#ifdef _AIX +#define WTF_OS_AIX 1 +#endif + +/* OS(DARWIN) - Any Darwin-based OS, including Mac OS X and iPhone OS */ +#ifdef __APPLE__ +#define WTF_OS_DARWIN 1 + +#include +#include +#include +#endif + +/* OS(IOS) - iOS */ +/* OS(MAC_OS_X) - Mac OS X (not including iOS) */ +#if OS(DARWIN) && ((defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED) \ + || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) \ + || (defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR)) +#define WTF_OS_IOS 1 +#elif OS(DARWIN) && defined(TARGET_OS_MAC) && TARGET_OS_MAC +#define WTF_OS_MAC_OS_X 1 + +/* FIXME: These can be removed after sufficient time has passed since the removal of BUILDING_ON / TARGETING macros. */ + +#define ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED 0 / 0 +#define ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED 0 / 0 + +#define BUILDING_ON_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED +#define BUILDING_ON_SNOW_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED +#define BUILDING_ON_LION ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED + +#define TARGETING_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED +#define TARGETING_SNOW_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED +#define TARGETING_LION ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED +#endif + +/* OS(FREEBSD) - FreeBSD */ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +#define WTF_OS_FREEBSD 1 +#endif + +/* OS(HURD) - GNU/Hurd */ +#ifdef __GNU__ +#define WTF_OS_HURD 1 +#endif + +/* OS(LINUX) - Linux */ +#ifdef __linux__ +#define WTF_OS_LINUX 1 +#endif + +/* OS(NETBSD) - NetBSD */ +#if defined(__NetBSD__) +#define WTF_OS_NETBSD 1 +#endif + +/* OS(OPENBSD) - OpenBSD */ +#ifdef __OpenBSD__ +#define WTF_OS_OPENBSD 1 +#endif + +/* OS(QNX) - QNX */ +#if defined(__QNXNTO__) +#define WTF_OS_QNX 1 +#endif + +/* OS(SOLARIS) - Solaris */ +#if defined(sun) || defined(__sun) +#define WTF_OS_SOLARIS 1 +#endif + +/* OS(WINCE) - Windows CE; note that for this platform OS(WINDOWS) is also defined */ +#if defined(_WIN32_WCE) +#define WTF_OS_WINCE 1 +#endif + +/* OS(WINDOWS) - Any version of Windows */ +#if defined(WIN32) || defined(_WIN32) +#define WTF_OS_WINDOWS 1 +#endif + +#define WTF_OS_WIN ERROR "USE WINDOWS WITH OS NOT WIN" +#define WTF_OS_MAC ERROR "USE MAC_OS_X WITH OS NOT MAC" + +/* OS(UNIX) - Any Unix-like system */ +#if OS(AIX) \ + || OS(ANDROID) \ + || OS(DARWIN) \ + || OS(FREEBSD) \ + || OS(HURD) \ + || OS(LINUX) \ + || OS(NETBSD) \ + || OS(OPENBSD) \ + || OS(QNX) \ + || OS(SOLARIS) \ + || defined(unix) \ + || defined(__unix) \ + || defined(__unix__) +#define WTF_OS_UNIX 1 +#endif + +/* Operating environments */ + +/* FIXME: these are all mixes of OS, operating environment and policy choices. */ +/* PLATFORM(CHROMIUM) */ +/* PLATFORM(QT) */ +/* PLATFORM(WX) */ +/* PLATFORM(EFL) */ +/* PLATFORM(GTK) */ +/* PLATFORM(BLACKBERRY) */ +/* PLATFORM(MAC) */ +/* PLATFORM(WIN) */ +#if defined(BUILDING_CHROMIUM__) +#define WTF_PLATFORM_CHROMIUM 1 +#elif defined(BUILDING_QT__) +#define WTF_PLATFORM_QT 1 +#elif defined(BUILDING_WX__) +#define WTF_PLATFORM_WX 1 +#elif defined(BUILDING_EFL__) +#define WTF_PLATFORM_EFL 1 +#elif defined(BUILDING_GTK__) +#define WTF_PLATFORM_GTK 1 +#elif defined(BUILDING_BLACKBERRY__) +#define WTF_PLATFORM_BLACKBERRY 1 +#elif OS(DARWIN) +#define WTF_PLATFORM_MAC 1 +#elif OS(WINDOWS) +#define WTF_PLATFORM_WIN 1 +#endif + +/* PLATFORM(IOS) */ +/* FIXME: this is sometimes used as an OS switch and sometimes for higher-level things */ +#if (defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) +#define WTF_PLATFORM_IOS 1 +#endif + +/* PLATFORM(IOS_SIMULATOR) */ +#if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR +#define WTF_PLATFORM_IOS 1 +#define WTF_PLATFORM_IOS_SIMULATOR 1 +#endif + +/* Graphics engines */ + +/* USE(CG) and PLATFORM(CI) */ +#if PLATFORM(MAC) || PLATFORM(IOS) +#define WTF_USE_CG 1 +#endif +#if PLATFORM(MAC) || PLATFORM(IOS) || (PLATFORM(WIN) && USE(CG)) +#define WTF_USE_CA 1 +#endif + +/* USE(SKIA) for Win/Linux/Mac/Android */ +#if PLATFORM(CHROMIUM) +#if OS(DARWIN) +#define WTF_USE_SKIA 1 +#define WTF_USE_ICCJPEG 1 +#define WTF_USE_QCMSLIB 1 +#elif OS(ANDROID) +#define WTF_USE_SKIA 1 +#define WTF_USE_LOW_QUALITY_IMAGE_INTERPOLATION 1 +#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_DITHERING 1 +#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_FANCY_UPSAMPLING 1 +#else +#define WTF_USE_SKIA 1 +#define WTF_USE_ICCJPEG 1 +#define WTF_USE_QCMSLIB 1 +#endif +#endif + +#if OS(QNX) +#define USE_SYSTEM_MALLOC 1 +#endif + +#if PLATFORM(BLACKBERRY) +#define WTF_USE_MERSENNE_TWISTER_19937 1 +#define WTF_USE_SKIA 1 +#define WTF_USE_LOW_QUALITY_IMAGE_INTERPOLATION 1 +#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_DITHERING 1 +#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_FANCY_UPSAMPLING 1 +#endif + +#if PLATFORM(GTK) +#define WTF_USE_CAIRO 1 +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#endif + + +#if OS(WINCE) +#define WTF_USE_MERSENNE_TWISTER_19937 1 +#endif + +/* On Windows, use QueryPerformanceCounter by default */ +#if OS(WINDOWS) +#define WTF_USE_QUERY_PERFORMANCE_COUNTER 1 +#endif + +#if OS(WINCE) && !PLATFORM(QT) +#define NOSHLWAPI /* shlwapi.h not available on WinCe */ + +/* MSDN documentation says these functions are provided with uspce.lib. But we cannot find this file. */ +#define __usp10__ /* disable "usp10.h" */ + +#define _INC_ASSERT /* disable "assert.h" */ +#define assert(x) + +#endif /* OS(WINCE) && !PLATFORM(QT) */ + +#if OS(WINCE) && !PLATFORM(QT) +#define WTF_USE_WCHAR_UNICODE 1 +#elif PLATFORM(GTK) +/* The GTK+ Unicode backend is configurable */ +#else +#define WTF_USE_ICU_UNICODE 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) +#if CPU(X86_64) +#define WTF_USE_PLUGIN_HOST_PROCESS 1 +#endif +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 +#define ENABLE_GESTURE_EVENTS 1 +#define ENABLE_RUBBER_BANDING 1 +#define WTF_USE_SCROLLBAR_PAINTER 1 +#define HAVE_XPC 1 +#endif +#if !defined(ENABLE_DASHBOARD_SUPPORT) +#define ENABLE_DASHBOARD_SUPPORT 1 +#endif +#define WTF_USE_CF 1 +#define WTF_USE_PTHREADS 1 +#define HAVE_READLINE 1 +#define HAVE_RUNLOOP_TIMER 1 +#define ENABLE_FULLSCREEN_API 1 +#define ENABLE_SMOOTH_SCROLLING 1 +#define ENABLE_WEB_ARCHIVE 1 +#define ENABLE_WEB_AUDIO 1 +#if defined(ENABLE_VIDEO) +#define ENABLE_VIDEO_TRACK 1 +#endif +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 +#define HAVE_LAYER_HOSTING_IN_WINDOW_SERVER 1 +#endif +#define WTF_USE_APPKIT 1 +#define WTF_USE_SECURITY_FRAMEWORK 1 +#endif /* PLATFORM(MAC) && !PLATFORM(IOS) */ + +#if PLATFORM(CHROMIUM) && OS(DARWIN) +#define WTF_USE_CF 1 +#define WTF_USE_PTHREADS 1 +#define WTF_USE_WK_SCROLLBAR_PAINTER 1 +#endif + +#if PLATFORM(CHROMIUM) +#if OS(DARWIN) +/* We can't override the global operator new and delete on OS(DARWIN) because + * some object are allocated by WebKit and deallocated by the embedder. */ +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#else /* !OS(DARWIN) */ +/* On non-OS(DARWIN), the "system malloc" is actually TCMalloc anyway, so there's + * no need to use WebKit's copy of TCMalloc. */ +#define USE_SYSTEM_MALLOC 1 +#endif /* OS(DARWIN) */ +#endif /* PLATFORM(CHROMIUM) */ + +#if PLATFORM(IOS) +#define DONT_FINALIZE_ON_MAIN_THREAD 1 +#endif + +#if PLATFORM(QT) && OS(DARWIN) +#define WTF_USE_CF 1 +#endif + +#if OS(DARWIN) && !PLATFORM(GTK) && !PLATFORM(QT) +#define ENABLE_PURGEABLE_MEMORY 1 +#endif + +#if PLATFORM(IOS) +#define ENABLE_CONTEXT_MENUS 0 +#define ENABLE_DRAG_SUPPORT 0 +#define ENABLE_GEOLOCATION 1 +#define ENABLE_ICONDATABASE 0 +#define ENABLE_INSPECTOR 1 +#define ENABLE_NETSCAPE_PLUGIN_API 0 +#define ENABLE_ORIENTATION_EVENTS 1 +#define ENABLE_REPAINT_THROTTLING 1 +#define ENABLE_WEB_ARCHIVE 1 +#define HAVE_READLINE 1 +#define WTF_USE_CF 1 +#define WTF_USE_CFNETWORK 1 +#define WTF_USE_NETWORK_CFDATA_ARRAY_CALLBACK 1 +#define WTF_USE_PTHREADS 1 + +#if PLATFORM(IOS_SIMULATOR) + #define ENABLE_JIT 0 + #define ENABLE_YARR_JIT 0 +#else + #define ENABLE_JIT 1 + #define ENABLE_LLINT 1 + #define ENABLE_YARR_JIT 1 +#endif + +#define WTF_USE_APPKIT 0 +#define WTF_USE_SECURITY_FRAMEWORK 0 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) +#define WTF_USE_CF 1 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(WIN_CAIRO) +#define WTF_USE_CFNETWORK 1 +#endif + +#if USE(CFNETWORK) || PLATFORM(MAC) || PLATFORM(IOS) +#define WTF_USE_CFURLCACHE 1 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(QT) +#define ENABLE_WEB_ARCHIVE 1 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(WIN_CAIRO) && !PLATFORM(QT) +#define ENABLE_FULLSCREEN_API 1 +#endif + +#if PLATFORM(WX) +#if !CPU(PPC) +#if !defined(ENABLE_ASSEMBLER) +#define ENABLE_ASSEMBLER 1 +#endif +#define ENABLE_JIT 1 +#endif +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#define ENABLE_LLINT 0 +#if OS(DARWIN) +#define WTF_USE_CF 1 +#define ENABLE_WEB_ARCHIVE 1 +#endif +#endif + +#if OS(UNIX) && (PLATFORM(GTK) || PLATFORM(QT)) +#define WTF_USE_PTHREADS 1 +#endif + +#if !defined(HAVE_ACCESSIBILITY) +#if PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && !OS(ANDROID)) || PLATFORM(EFL) +#define HAVE_ACCESSIBILITY 1 +#endif +#endif /* !defined(HAVE_ACCESSIBILITY) */ + +#if OS(UNIX) +#define HAVE_SIGNAL_H 1 +#define WTF_USE_OS_RANDOMNESS 1 +#endif + +#if (OS(FREEBSD) || OS(OPENBSD)) && !defined(__GLIBC__) +#define HAVE_PTHREAD_NP_H 1 +#endif + +#if !defined(HAVE_VASPRINTF) +#if !COMPILER(MSVC) && !COMPILER(RVCT) && !COMPILER(MINGW) && !(COMPILER(GCC) && OS(QNX)) +#define HAVE_VASPRINTF 1 +#endif +#endif + +#if !defined(HAVE_STRNSTR) +#if OS(DARWIN) || (OS(FREEBSD) && !defined(__GLIBC__)) +#define HAVE_STRNSTR 1 +#endif +#endif + +#if !OS(WINDOWS) && !OS(SOLARIS) \ + && !OS(RVCT) \ + && !OS(ANDROID) +#define HAVE_TM_GMTOFF 1 +#define HAVE_TM_ZONE 1 +#define HAVE_TIMEGM 1 +#endif + +#if OS(DARWIN) + +#define HAVE_ERRNO_H 1 +#define HAVE_LANGINFO_H 1 +#define HAVE_MMAP 1 +#define HAVE_MERGESORT 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_SYS_TIMEB_H 1 +#define WTF_USE_ACCELERATE 1 + +#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + +#define HAVE_DISPATCH_H 1 +#define HAVE_HOSTED_CORE_ANIMATION 1 + +#if !PLATFORM(IOS) +#define HAVE_MADV_FREE_REUSE 1 +#define HAVE_MADV_FREE 1 +#define HAVE_PTHREAD_SETNAME_NP 1 +#endif + +#endif + +#if PLATFORM(IOS) +#define HAVE_MADV_FREE 1 +#define HAVE_PTHREAD_SETNAME_NP 1 +#endif + +#elif OS(WINDOWS) + +#if !OS(WINCE) +#define HAVE_SYS_TIMEB_H 1 +#define HAVE_ALIGNED_MALLOC 1 +#define HAVE_ISDEBUGGERPRESENT 1 +#endif +#define HAVE_VIRTUALALLOC 1 +#define WTF_USE_OS_RANDOMNESS 1 + +#elif OS(QNX) + +#define HAVE_ERRNO_H 1 +#define HAVE_MMAP 1 +#define HAVE_MADV_FREE_REUSE 1 +#define HAVE_MADV_FREE 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 +#define WTF_USE_PTHREADS 1 + +#elif OS(ANDROID) + +#define HAVE_ERRNO_H 1 +#define HAVE_NMAP 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 + +#else + +/* FIXME: is this actually used or do other platforms generate their own config.h? */ + +#define HAVE_ERRNO_H 1 +#define HAVE_LANGINFO_H 1 +#define HAVE_MMAP 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 + +#endif + +/* ENABLE macro defaults */ + +#if PLATFORM(QT) +/* We must not customize the global operator new and delete for the Qt port. */ +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#if !OS(UNIX) +#define USE_SYSTEM_MALLOC 1 +#endif +#endif + +#if !defined(ENABLE_ICONDATABASE) +#define ENABLE_ICONDATABASE 1 +#endif + +#if !defined(ENABLE_SQL_DATABASE) +#define ENABLE_SQL_DATABASE 1 +#endif + +#if !defined(ENABLE_JAVASCRIPT_DEBUGGER) +#define ENABLE_JAVASCRIPT_DEBUGGER 1 +#endif + +#if !defined(ENABLE_FTPDIR) +#define ENABLE_FTPDIR 1 +#endif + +#if !defined(ENABLE_CONTEXT_MENUS) +#define ENABLE_CONTEXT_MENUS 1 +#endif + +#if !defined(ENABLE_DRAG_SUPPORT) +#define ENABLE_DRAG_SUPPORT 1 +#endif + +#if !defined(ENABLE_INSPECTOR) +#define ENABLE_INSPECTOR 1 +#endif + +#if !defined(ENABLE_NETSCAPE_PLUGIN_API) +#define ENABLE_NETSCAPE_PLUGIN_API 1 +#endif + +#if !defined(ENABLE_GLOBAL_FASTMALLOC_NEW) +#define ENABLE_GLOBAL_FASTMALLOC_NEW 1 +#endif + +#if !defined(ENABLE_PARSED_STYLE_SHEET_CACHING) +#define ENABLE_PARSED_STYLE_SHEET_CACHING 1 +#endif + +#if !defined(ENABLE_SUBPIXEL_LAYOUT) +#if PLATFORM(CHROMIUM) +#define ENABLE_SUBPIXEL_LAYOUT 1 +#else +#define ENABLE_SUBPIXEL_LAYOUT 0 +#endif +#endif + +#if !defined(ENABLE_SATURATED_LAYOUT_ARITHMETIC) +#define ENABLE_SATURATED_LAYOUT_ARITHMETIC 0 +#endif + +#if ENABLE(ENABLE_SATURATED_LAYOUT_ARITHMETIC) && !ENABLE(ENABLE_SUBPIXEL_LAYOUT) +#error "ENABLE_SATURATED_LAYOUT_ARITHMETIC requires ENABLE_SUBPIXEL_LAYOUT" +#endif + +#if ENABLE(INPUT_TYPE_DATE) || ENABLE(INPUT_TYPE_DATETIME) || ENABLE(INPUT_TYPE_DATETIMELOCAL) || ENABLE(INPUT_TYPE_MONTH) || ENABLE(INPUT_TYPE_TIME) || ENABLE(INPUT_TYPE_WEEK) +#define ENABLE_DATE_AND_TIME_INPUT_TYPES 1 +#endif + +#define ENABLE_DEBUG_WITH_BREAKPOINT 0 +#define ENABLE_SAMPLING_COUNTERS 0 +#define ENABLE_SAMPLING_FLAGS 0 +#define ENABLE_SAMPLING_REGIONS 0 +#define ENABLE_OPCODE_SAMPLING 0 +#define ENABLE_CODEBLOCK_SAMPLING 0 +#if ENABLE(CODEBLOCK_SAMPLING) && !ENABLE(OPCODE_SAMPLING) +#error "CODEBLOCK_SAMPLING requires OPCODE_SAMPLING" +#endif +#if ENABLE(OPCODE_SAMPLING) || ENABLE(SAMPLING_FLAGS) || ENABLE(SAMPLING_REGIONS) +#define ENABLE_SAMPLING_THREAD 1 +#endif + +#if !defined(ENABLE_TEXT_CARET) && !PLATFORM(IOS) +#define ENABLE_TEXT_CARET 1 +#endif + +#if !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32_64) +#if (CPU(X86_64) && (OS(UNIX) || OS(WINDOWS))) \ + || (CPU(IA64) && !CPU(IA64_32)) \ + || CPU(ALPHA) \ + || CPU(SPARC64) \ + || CPU(S390X) \ + || CPU(PPC64) +#define WTF_USE_JSVALUE64 1 +#else +#define WTF_USE_JSVALUE32_64 1 +#endif +#endif /* !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32_64) */ + +/* Disable the JIT on versions of GCC prior to 4.1 */ +#if !defined(ENABLE_JIT) && COMPILER(GCC) && !GCC_VERSION_AT_LEAST(4, 1, 0) +#define ENABLE_JIT 0 +#endif + +/* JIT is not implemented for Windows 64-bit */ +#if !defined(ENABLE_JIT) && OS(WINDOWS) && CPU(X86_64) +#define ENABLE_JIT 0 +#define ENABLE_YARR_JIT 0 +#endif + +#if !defined(ENABLE_JIT) && CPU(SH4) && PLATFORM(QT) +#define ENABLE_JIT 1 +#endif + +/* The JIT is enabled by default on all x86, x86-64, ARM & MIPS platforms. */ +#if !defined(ENABLE_JIT) \ + && (CPU(X86) || CPU(X86_64) || CPU(ARM) || CPU(MIPS)) \ + && (OS(DARWIN) || !COMPILER(GCC) || GCC_VERSION_AT_LEAST(4, 1, 0)) \ + && !OS(WINCE) \ + && !(OS(QNX) && !PLATFORM(QT)) /* We use JIT in QNX Qt */ +#define ENABLE_JIT 1 +#endif + +/* If possible, try to enable a disassembler. This is optional. We proceed in two + steps: first we try to find some disassembler that we can use, and then we + decide if the high-level disassembler API can be enabled. */ +#if !defined(WTF_USE_UDIS86) && ENABLE(JIT) && (PLATFORM(MAC) || (PLATFORM(QT) && OS(LINUX))) \ + && (CPU(X86) || CPU(X86_64)) +#define WTF_USE_UDIS86 1 +#endif + +#if !defined(ENABLE_DISASSEMBLER) && USE(UDIS86) +#define ENABLE_DISASSEMBLER 1 +#endif + +/* On the GTK+ port we take an extra precaution for LLINT support: + * We disable it on x86 builds if the build target doesn't support SSE2 + * instructions (LLINT requires SSE2 on this platform). */ +#if !defined(ENABLE_LLINT) && PLATFORM(GTK) && CPU(X86) && COMPILER(GCC) \ + && !defined(__SSE2__) +#define ENABLE_LLINT 0 +#endif + +/* On some of the platforms where we have a JIT, we want to also have the + low-level interpreter. */ +#if !defined(ENABLE_LLINT) \ + && ENABLE(JIT) \ + && (OS(DARWIN) || OS(LINUX)) \ + && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(QT)) \ + && (CPU(X86) || CPU(X86_64) || CPU(ARM_THUMB2)) +#define ENABLE_LLINT 1 +#endif + +#if !defined(ENABLE_DFG_JIT) && ENABLE(JIT) && !COMPILER(MSVC) +/* Enable the DFG JIT on X86 and X86_64. Only tested on Mac and GNU/Linux. */ +#if (CPU(X86) || CPU(X86_64)) && (PLATFORM(MAC) || OS(LINUX)) +#define ENABLE_DFG_JIT 1 +#endif +/* Enable the DFG JIT on ARMv7. Only tested on iOS and Qt Linux. */ +#if CPU(ARM_THUMB2) && (PLATFORM(IOS) || PLATFORM(BLACKBERRY) || PLATFORM(QT)) +#define ENABLE_DFG_JIT 1 +#endif +/* Enable the DFG JIT on ARM. */ +#if CPU(ARM_TRADITIONAL) +#define ENABLE_DFG_JIT 1 +#endif +#endif + +/* If the jit is not available, enable the LLInt C Loop: */ +#if !ENABLE(JIT) +#undef ENABLE_LLINT /* Undef so that we can redefine it. */ +#undef ENABLE_LLINT_C_LOOP /* Undef so that we can redefine it. */ +#undef ENABLE_DFG_JIT /* Undef so that we can redefine it. */ +#define ENABLE_LLINT 1 +#define ENABLE_LLINT_C_LOOP 1 +#define ENABLE_DFG_JIT 0 +#endif + +/* Do a sanity check to make sure that we at least have one execution engine in + use: */ +#if !(ENABLE(JIT) || ENABLE(LLINT)) +#error You have to have at least one execution model enabled to build JSC +#endif + +/* Profiling of types and values used by JIT code. DFG_JIT depends on it, but you + can enable it manually with DFG turned off if you want to use it as a standalone + profiler. In that case, you probably want to also enable VERBOSE_VALUE_PROFILE + below. */ +#if !defined(ENABLE_VALUE_PROFILER) && ENABLE(DFG_JIT) +#define ENABLE_VALUE_PROFILER 1 +#endif + +#if !defined(ENABLE_VERBOSE_VALUE_PROFILE) && ENABLE(VALUE_PROFILER) +#define ENABLE_VERBOSE_VALUE_PROFILE 0 +#endif + +#if !defined(ENABLE_SIMPLE_HEAP_PROFILING) +#define ENABLE_SIMPLE_HEAP_PROFILING 0 +#endif + +/* Counts uses of write barriers using sampling counters. Be sure to also + set ENABLE_SAMPLING_COUNTERS to 1. */ +#if !defined(ENABLE_WRITE_BARRIER_PROFILING) +#define ENABLE_WRITE_BARRIER_PROFILING 0 +#endif + +/* Configure the JIT */ +#if CPU(X86) && COMPILER(MSVC) +#define JSC_HOST_CALL __fastcall +#elif CPU(X86) && COMPILER(GCC) +#define JSC_HOST_CALL __attribute__ ((fastcall)) +#else +#define JSC_HOST_CALL +#endif + +/* Configure the interpreter */ +#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(4, 0, 0, 0) && defined(__GNUC__)) +#define HAVE_COMPUTED_GOTO 1 +#endif + +/* Determine if we need to enable Computed Goto Opcodes or not: */ +#if HAVE(COMPUTED_GOTO) && ENABLE(LLINT) +#define ENABLE_COMPUTED_GOTO_OPCODES 1 +#endif + +/* Regular Expression Tracing - Set to 1 to trace RegExp's in jsc. Results dumped at exit */ +#define ENABLE_REGEXP_TRACING 0 + +/* Yet Another Regex Runtime - turned on by default for JIT enabled ports. */ +#if !defined(ENABLE_YARR_JIT) && (ENABLE(JIT) || ENABLE(LLINT_C_LOOP)) && !PLATFORM(CHROMIUM) && !(OS(QNX) && PLATFORM(QT)) +#define ENABLE_YARR_JIT 1 + +/* Setting this flag compares JIT results with interpreter results. */ +#define ENABLE_YARR_JIT_DEBUG 0 +#endif + +/* If either the JIT or the RegExp JIT is enabled, then the Assembler must be + enabled as well: */ +#if ENABLE(JIT) || ENABLE(YARR_JIT) +#if defined(ENABLE_ASSEMBLER) && !ENABLE_ASSEMBLER +#error "Cannot enable the JIT or RegExp JIT without enabling the Assembler" +#else +#undef ENABLE_ASSEMBLER +#define ENABLE_ASSEMBLER 1 +#endif +#endif + +/* Pick which allocator to use; we only need an executable allocator if the assembler is compiled in. + On x86-64 we use a single fixed mmap, on other platforms we mmap on demand. */ +#if ENABLE(ASSEMBLER) +#if CPU(X86_64) || PLATFORM(IOS) +#define ENABLE_EXECUTABLE_ALLOCATOR_FIXED 1 +#else +#define ENABLE_EXECUTABLE_ALLOCATOR_DEMAND 1 +#endif +#endif + +#if !defined(ENABLE_PAN_SCROLLING) && OS(WINDOWS) +#define ENABLE_PAN_SCROLLING 1 +#endif + +/*Add other platforms as they update their platfrom specific code to handle TextRun's with 8 bit data. */ +#if PLATFORM(MAC) +#define ENABLE_8BIT_TEXTRUN 1 +#endif + +/* Use the QXmlStreamReader implementation for XMLDocumentParser */ +/* Use the QXmlQuery implementation for XSLTProcessor */ +#if PLATFORM(QT) +#if !USE(LIBXML2) +#define WTF_USE_QXMLSTREAM 1 +#define WTF_USE_QXMLQUERY 1 +#endif +#endif + +/* Accelerated compositing */ +#if PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(QT) || (PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(WIN_CAIRO)) +#define WTF_USE_ACCELERATED_COMPOSITING 1 +#endif + +#if PLATFORM(MAC) || PLATFORM(IOS) +#define ENABLE_CSS_IMAGE_SET 1 +#endif + +#if ENABLE(WEBGL) && !defined(WTF_USE_3D_GRAPHICS) +#define WTF_USE_3D_GRAPHICS 1 +#endif + +/* Qt always uses Texture Mapper */ +#if PLATFORM(QT) +#define WTF_USE_TEXTURE_MAPPER 1 +#endif + +#if USE(TEXTURE_MAPPER) && USE(3D_GRAPHICS) && !defined(WTF_USE_TEXTURE_MAPPER_GL) +#define WTF_USE_TEXTURE_MAPPER_GL 1 +#endif + +/* Compositing on the UI-process in WebKit2 */ +#if PLATFORM(QT) +#define WTF_USE_COORDINATED_GRAPHICS 1 +#endif + +#if PLATFORM(MAC) || PLATFORM(IOS) +#define WTF_USE_PROTECTION_SPACE_AUTH_CALLBACK 1 +#endif + +#if !ENABLE(NETSCAPE_PLUGIN_API) || (ENABLE(NETSCAPE_PLUGIN_API) && ((OS(UNIX) && (PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(WX))) || PLATFORM(EFL))) +#define ENABLE_PLUGIN_PACKAGE_SIMPLE_HASH 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 +#define ENABLE_THREADED_SCROLLING 1 +#endif + +/* Set up a define for a common error that is intended to cause a build error -- thus the space after Error. */ +#define WTF_PLATFORM_CFNETWORK Error USE_macro_should_be_used_with_CFNETWORK + +/* FIXME: Eventually we should enable this for all platforms and get rid of the define. */ +#if PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL) +#define WTF_USE_PLATFORM_STRATEGIES 1 +#endif + +#if PLATFORM(WIN) +#define WTF_USE_CROSS_PLATFORM_CONTEXT_MENUS 1 +#endif + +#if PLATFORM(MAC) && HAVE(ACCESSIBILITY) +#define WTF_USE_ACCESSIBILITY_CONTEXT_MENUS 1 +#endif + +#if CPU(ARM_THUMB2) +#define ENABLE_BRANCH_COMPACTION 1 +#endif + +#if !defined(ENABLE_THREADING_LIBDISPATCH) && HAVE(DISPATCH_H) +#define ENABLE_THREADING_LIBDISPATCH 1 +#elif !defined(ENABLE_THREADING_OPENMP) && defined(_OPENMP) +#define ENABLE_THREADING_OPENMP 1 +#elif !defined(THREADING_GENERIC) +#define ENABLE_THREADING_GENERIC 1 +#endif + +#if ENABLE(GLIB_SUPPORT) +#include +#endif + +/* FIXME: This define won't be needed once #27551 is fully landed. However, + since most ports try to support sub-project independence, adding new headers + to WTF causes many ports to break, and so this way we can address the build + breakages one port at a time. */ +#if !defined(WTF_USE_EXPORT_MACROS) && (PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(WX)) +#define WTF_USE_EXPORT_MACROS 1 +#endif + +#if !defined(WTF_USE_EXPORT_MACROS_FOR_TESTING) && (PLATFORM(GTK) || PLATFORM(WIN)) +#define WTF_USE_EXPORT_MACROS_FOR_TESTING 1 +#endif + +#if (PLATFORM(QT) && !OS(DARWIN) && !OS(WINDOWS)) || PLATFORM(GTK) || PLATFORM(EFL) +#define WTF_USE_UNIX_DOMAIN_SOCKETS 1 +#endif + +#if !defined(ENABLE_COMPARE_AND_SWAP) && (OS(WINDOWS) || (COMPILER(GCC) && (CPU(X86) || CPU(X86_64) || CPU(ARM_THUMB2)))) +#define ENABLE_COMPARE_AND_SWAP 1 +#endif + +#define ENABLE_OBJECT_MARK_LOGGING 0 + +#if !defined(ENABLE_PARALLEL_GC) && !ENABLE(OBJECT_MARK_LOGGING) && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(BLACKBERRY) || PLATFORM(GTK)) && ENABLE(COMPARE_AND_SWAP) +#define ENABLE_PARALLEL_GC 1 +#elif PLATFORM(QT) +// Parallel GC is temporarily disabled on Qt because of regular crashes, see https://bugs.webkit.org/show_bug.cgi?id=90957 for details +#define ENABLE_PARALLEL_GC 0 +#endif + +#if !defined(ENABLE_GC_VALIDATION) && !defined(NDEBUG) +#define ENABLE_GC_VALIDATION 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 +#define WTF_USE_AVFOUNDATION 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 +#define WTF_USE_COREMEDIA 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 +#define HAVE_AVFOUNDATION_TEXT_TRACK_SUPPORT 1 +#endif + +#if PLATFORM(MAC) || PLATFORM(GTK) || PLATFORM(EFL) || (PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(WIN_CAIRO)) || PLATFORM(BLACKBERRY) +#define WTF_USE_REQUEST_ANIMATION_FRAME_TIMER 1 +#endif + +#if PLATFORM(MAC) || PLATFORM(BLACKBERRY) +#define WTF_USE_REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR 1 +#endif + +#if PLATFORM(MAC) && (PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) +#define HAVE_INVERTED_WHEEL_EVENTS 1 +#endif + +#if PLATFORM(MAC) +#define WTF_USE_COREAUDIO 1 +#endif + +#if !defined(WTF_USE_V8) && PLATFORM(CHROMIUM) +#define WTF_USE_V8 1 +#endif + +/* Not using V8 implies using JSC and vice versa */ +#if !USE(V8) +#define WTF_USE_JSC 1 +#endif + +#if ENABLE(NOTIFICATIONS) && PLATFORM(MAC) +#define ENABLE_TEXT_NOTIFICATIONS_ONLY 1 +#endif + +#if !defined(WTF_USE_ZLIB) && !PLATFORM(QT) +#define WTF_USE_ZLIB 1 +#endif + +#if PLATFORM(QT) +#include +#if defined(QT_OPENGL_ES_2) && !defined(WTF_USE_OPENGL_ES_2) +#define WTF_USE_OPENGL_ES_2 1 +#endif +#endif + +#endif /* WTF_Platform_h */ diff --git a/3rdparty/masm/wtf/PossiblyNull.h b/3rdparty/masm/wtf/PossiblyNull.h new file mode 100644 index 0000000000..46a7d713be --- /dev/null +++ b/3rdparty/masm/wtf/PossiblyNull.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PossiblyNull_h +#define PossiblyNull_h + +#include + +namespace WTF { + +template struct PossiblyNull { + PossiblyNull(T data) + : m_data(data) + { + } + PossiblyNull(const PossiblyNull& source) + : m_data(source.m_data) + { + source.m_data = 0; + } + ~PossiblyNull() { ASSERT(!m_data); } + bool getValue(T& out) WARN_UNUSED_RETURN; +private: + mutable T m_data; +}; + +template bool PossiblyNull::getValue(T& out) +{ + out = m_data; + bool result = !!m_data; + m_data = 0; + return result; +} + +} + +#endif diff --git a/3rdparty/masm/wtf/PrintStream.cpp b/3rdparty/masm/wtf/PrintStream.cpp new file mode 100644 index 0000000000..7dd4060971 --- /dev/null +++ b/3rdparty/masm/wtf/PrintStream.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PrintStream.h" + +#include + +namespace WTF { + +PrintStream::PrintStream() { } +PrintStream::~PrintStream() { } // Force the vtable to be in this module + +void PrintStream::printf(const char* format, ...) +{ + va_list argList; + va_start(argList, format); + vprintf(format, argList); + va_end(argList); +} + +void PrintStream::flush() +{ +} + +void printInternal(PrintStream& out, const char* string) +{ + out.printf("%s", string); +} + +void printInternal(PrintStream& out, bool value) +{ + if (value) + out.print("true"); + else + out.print("false"); +} + +void printInternal(PrintStream& out, int value) +{ + out.printf("%d", value); +} + +void printInternal(PrintStream& out, unsigned value) +{ + out.printf("%u", value); +} + +void printInternal(PrintStream& out, long value) +{ + out.printf("%ld", value); +} + +void printInternal(PrintStream& out, unsigned long value) +{ + out.printf("%lu", value); +} + +void printInternal(PrintStream& out, long long value) +{ + out.printf("%lld", value); +} + +void printInternal(PrintStream& out, unsigned long long value) +{ + out.printf("%llu", value); +} + +void printInternal(PrintStream& out, float value) +{ + out.print(static_cast(value)); +} + +void printInternal(PrintStream& out, double value) +{ + out.printf("%lf", value); +} + +void printInternal(PrintStream& out, RawPointer value) +{ + out.printf("%p", value.value()); +} + +void dumpCharacter(PrintStream& out, char value) +{ + out.printf("%c", value); +} + +} // namespace WTF + diff --git a/3rdparty/masm/wtf/PrintStream.h b/3rdparty/masm/wtf/PrintStream.h new file mode 100644 index 0000000000..4134dcf182 --- /dev/null +++ b/3rdparty/masm/wtf/PrintStream.h @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PrintStream_h +#define PrintStream_h + +#include +#include +#include +#include +#include +#include + +namespace WTF { + +class CString; +class String; + +class PrintStream { + WTF_MAKE_FAST_ALLOCATED; WTF_MAKE_NONCOPYABLE(PrintStream); +public: + PrintStream(); + virtual ~PrintStream(); + + void printf(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); + virtual void vprintf(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(2, 0) = 0; + + // Typically a no-op for many subclasses of PrintStream, this is a hint that + // the implementation should flush its buffers if it had not done so already. + virtual void flush(); + + template + void print(const T& value) + { + printInternal(*this, value); + } + + template + void print(const T1& value1, const T2& value2) + { + print(value1); + print(value2); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3) + { + print(value1); + print(value2); + print(value3); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4) + { + print(value1); + print(value2); + print(value3); + print(value4); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + print(value11); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + print(value11); + print(value12); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12, const T13& value13) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + print(value11); + print(value12); + print(value13); + } +}; + +void printInternal(PrintStream&, const char*); +void printInternal(PrintStream&, const CString&); +void printInternal(PrintStream&, const String&); +inline void printInternal(PrintStream& out, char* value) { printInternal(out, static_cast(value)); } +inline void printInternal(PrintStream& out, CString& value) { printInternal(out, static_cast(value)); } +inline void printInternal(PrintStream& out, String& value) { printInternal(out, static_cast(value)); } +void printInternal(PrintStream&, bool); +void printInternal(PrintStream&, int); +void printInternal(PrintStream&, unsigned); +void printInternal(PrintStream&, long); +void printInternal(PrintStream&, unsigned long); +void printInternal(PrintStream&, long long); +void printInternal(PrintStream&, unsigned long long); +void printInternal(PrintStream&, float); +void printInternal(PrintStream&, double); +void printInternal(PrintStream&, RawPointer); + +template +void printInternal(PrintStream& out, const T& value) +{ + value.dump(out); +} + +#define MAKE_PRINT_ADAPTOR(Name, Type, function) \ + class Name { \ + public: \ + Name(const Type& value) \ + : m_value(value) \ + { \ + } \ + void dump(PrintStream& out) const \ + { \ + function(out, m_value); \ + } \ + private: \ + Type m_value; \ + } + +#define MAKE_PRINT_METHOD_ADAPTOR(Name, Type, method) \ + class Name { \ + public: \ + Name(const Type& value) \ + : m_value(value) \ + { \ + } \ + void dump(PrintStream& out) const \ + { \ + m_value.method(out); \ + } \ + private: \ + Type m_value; \ + } + +// Use an adaptor-based dumper for characters to avoid situations where +// you've "compressed" an integer to a character and it ends up printing +// as ASCII when you wanted it to print as a number. +void dumpCharacter(PrintStream&, char); +MAKE_PRINT_ADAPTOR(CharacterDump, char, dumpCharacter); + +template +class PointerDump { +public: + PointerDump(const T* ptr) + : m_ptr(ptr) + { + } + + void dump(PrintStream& out) const + { + if (m_ptr) + printInternal(out, *m_ptr); + else + out.print("(null)"); + } +private: + const T* m_ptr; +}; + +template +PointerDump pointerDump(const T* ptr) { return PointerDump(ptr); } + +} // namespace WTF + +using WTF::CharacterDump; +using WTF::PointerDump; +using WTF::PrintStream; +using WTF::pointerDump; + +#endif // PrintStream_h + diff --git a/3rdparty/masm/wtf/RawPointer.h b/3rdparty/masm/wtf/RawPointer.h new file mode 100644 index 0000000000..6dc7292fb4 --- /dev/null +++ b/3rdparty/masm/wtf/RawPointer.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RawPointer_h +#define RawPointer_h + +namespace WTF { + +class RawPointer { +public: + RawPointer() + : m_value(0) + { + } + + explicit RawPointer(void* value) + : m_value(value) + { + } + + explicit RawPointer(const void* value) + : m_value(value) + { + } + + const void* value() const { return m_value; } + +private: + const void* m_value; +}; + +} // namespace WTF + +using WTF::RawPointer; + +#endif // RawPointer_h diff --git a/3rdparty/masm/wtf/StdLibExtras.h b/3rdparty/masm/wtf/StdLibExtras.h new file mode 100644 index 0000000000..f5e9f78df1 --- /dev/null +++ b/3rdparty/masm/wtf/StdLibExtras.h @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_StdLibExtras_h +#define WTF_StdLibExtras_h + +#include +#include + +// Use these to declare and define a static local variable (static T;) so that +// it is leaked so that its destructors are not called at exit. Using this +// macro also allows workarounds a compiler bug present in Apple's version of GCC 4.0.1. +#ifndef DEFINE_STATIC_LOCAL +#if COMPILER(GCC) && defined(__APPLE_CC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 1 +#define DEFINE_STATIC_LOCAL(type, name, arguments) \ + static type* name##Ptr = new type arguments; \ + type& name = *name##Ptr +#else +#define DEFINE_STATIC_LOCAL(type, name, arguments) \ + static type& name = *new type arguments +#endif +#endif + +// Use this macro to declare and define a debug-only global variable that may have a +// non-trivial constructor and destructor. When building with clang, this will suppress +// warnings about global constructors and exit-time destructors. +#ifndef NDEBUG +#if COMPILER(CLANG) +#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \ + _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \ + static type name arguments; \ + _Pragma("clang diagnostic pop") +#else +#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ + static type name arguments; +#endif // COMPILER(CLANG) +#else +#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) +#endif // NDEBUG + +// OBJECT_OFFSETOF: Like the C++ offsetof macro, but you can use it with classes. +// The magic number 0x4000 is insignificant. We use it to avoid using NULL, since +// NULL can cause compiler problems, especially in cases of multiple inheritance. +#define OBJECT_OFFSETOF(class, field) (reinterpret_cast(&(reinterpret_cast(0x4000)->field)) - 0x4000) + +// STRINGIZE: Can convert any value to quoted string, even expandable macros +#define STRINGIZE(exp) #exp +#define STRINGIZE_VALUE_OF(exp) STRINGIZE(exp) + +/* + * The reinterpret_cast([pointer to Type2]) expressions - where + * sizeof(Type1) > sizeof(Type2) - cause the following warning on ARM with GCC: + * increases required alignment of target type. + * + * An implicit or an extra static_cast bypasses the warning. + * For more info see the following bugzilla entries: + * - https://bugs.webkit.org/show_bug.cgi?id=38045 + * - http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43976 + */ +#if (CPU(ARM) || CPU(MIPS)) && COMPILER(GCC) +template +bool isPointerTypeAlignmentOkay(Type* ptr) +{ + return !(reinterpret_cast(ptr) % __alignof__(Type)); +} + +template +TypePtr reinterpret_cast_ptr(void* ptr) +{ + ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast(ptr))); + return reinterpret_cast(ptr); +} + +template +TypePtr reinterpret_cast_ptr(const void* ptr) +{ + ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast(ptr))); + return reinterpret_cast(ptr); +} +#else +template +bool isPointerTypeAlignmentOkay(Type*) +{ + return true; +} +#define reinterpret_cast_ptr reinterpret_cast +#endif + +namespace WTF { + +static const size_t KB = 1024; +static const size_t MB = 1024 * 1024; + +inline bool isPointerAligned(void* p) +{ + return !((intptr_t)(p) & (sizeof(char*) - 1)); +} + +inline bool is8ByteAligned(void* p) +{ + return !((uintptr_t)(p) & (sizeof(double) - 1)); +} + +/* + * C++'s idea of a reinterpret_cast lacks sufficient cojones. + */ +template +inline TO bitwise_cast(FROM from) +{ + COMPILE_ASSERT(sizeof(TO) == sizeof(FROM), WTF_bitwise_cast_sizeof_casted_types_is_equal); + union { + FROM from; + TO to; + } u; + u.from = from; + return u.to; +} + +template +inline To safeCast(From value) +{ + ASSERT(isInBounds(value)); + return static_cast(value); +} + +// Returns a count of the number of bits set in 'bits'. +inline size_t bitCount(unsigned bits) +{ + bits = bits - ((bits >> 1) & 0x55555555); + bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); + return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; +} + +// Macro that returns a compile time constant with the length of an array, but gives an error if passed a non-array. +template char (&ArrayLengthHelperFunction(T (&)[Size]))[Size]; +// GCC needs some help to deduce a 0 length array. +#if COMPILER(GCC) +template char (&ArrayLengthHelperFunction(T (&)[0]))[0]; +#endif +#define WTF_ARRAY_LENGTH(array) sizeof(::WTF::ArrayLengthHelperFunction(array)) + +// Efficient implementation that takes advantage of powers of two. +inline size_t roundUpToMultipleOf(size_t divisor, size_t x) +{ + ASSERT(divisor && !(divisor & (divisor - 1))); + size_t remainderMask = divisor - 1; + return (x + remainderMask) & ~remainderMask; +} +template inline size_t roundUpToMultipleOf(size_t x) +{ + COMPILE_ASSERT(divisor && !(divisor & (divisor - 1)), divisor_is_a_power_of_two); + return roundUpToMultipleOf(divisor, x); +} + +enum BinarySearchMode { + KeyMustBePresentInArray, + KeyMightNotBePresentInArray, + ReturnAdjacentElementIfKeyIsNotPresent +}; + +template +inline ArrayElementType* binarySearchImpl(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + size_t offset = 0; + while (size > 1) { + int pos = (size - 1) >> 1; + KeyType val = extractKey(&array[offset + pos]); + + if (val == key) + return &array[offset + pos]; + // The item we are looking for is smaller than the item being check; reduce the value of 'size', + // chopping off the right hand half of the array. + if (key < val) + size = pos; + // Discard all values in the left hand half of the array, up to and including the item at pos. + else { + size -= (pos + 1); + offset += (pos + 1); + } + + ASSERT(mode != KeyMustBePresentInArray || size); + } + + if (mode == KeyMightNotBePresentInArray && !size) + return 0; + + ArrayElementType* result = &array[offset]; + + if (mode == KeyMightNotBePresentInArray && key != extractKey(result)) + return 0; + + if (mode == KeyMustBePresentInArray) { + ASSERT(size == 1); + ASSERT(key == extractKey(result)); + } + + return result; +} + +// If the element is not found, crash if asserts are enabled, and behave like approximateBinarySearch in release builds. +template +inline ArrayElementType* binarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(array, size, key, extractKey); +} + +// Return zero if the element is not found. +template +inline ArrayElementType* tryBinarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(array, size, key, extractKey); +} + +// Return the element that is either to the left, or the right, of where the element would have been found. +template +inline ArrayElementType* approximateBinarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(array, size, key, extractKey); +} + +// Variants of the above that use const. +template +inline ArrayElementType* binarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(const_cast(array), size, key, extractKey); +} +template +inline ArrayElementType* tryBinarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(const_cast(array), size, key, extractKey); +} +template +inline ArrayElementType* approximateBinarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(const_cast(array), size, key, extractKey); +} + +} // namespace WTF + +// This version of placement new omits a 0 check. +enum NotNullTag { NotNull }; +inline void* operator new(size_t, NotNullTag, void* location) +{ + ASSERT(location); + return location; +} + +using WTF::KB; +using WTF::MB; +using WTF::isPointerAligned; +using WTF::is8ByteAligned; +using WTF::binarySearch; +using WTF::tryBinarySearch; +using WTF::approximateBinarySearch; +using WTF::bitwise_cast; +using WTF::safeCast; + +#endif // WTF_StdLibExtras_h diff --git a/3rdparty/masm/wtf/VMTags.h b/3rdparty/masm/wtf/VMTags.h new file mode 100644 index 0000000000..117bc3721e --- /dev/null +++ b/3rdparty/masm/wtf/VMTags.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VMTags_h +#define VMTags_h + +// On Mac OS X, the VM subsystem allows tagging memory requested from mmap and vm_map +// in order to aid tools that inspect system memory use. +#if OS(DARWIN) + +#include + +#if defined(VM_MEMORY_TCMALLOC) +#define VM_TAG_FOR_TCMALLOC_MEMORY VM_MAKE_TAG(VM_MEMORY_TCMALLOC) +#else +#define VM_TAG_FOR_TCMALLOC_MEMORY VM_MAKE_TAG(53) +#endif // defined(VM_MEMORY_TCMALLOC) + +#if defined(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) +#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) +#else +#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY VM_MAKE_TAG(64) +#endif // defined(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) + +#if defined(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) +#define VM_TAG_FOR_REGISTERFILE_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) +#else +#define VM_TAG_FOR_REGISTERFILE_MEMORY VM_MAKE_TAG(65) +#endif // defined(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) + +#if defined(VM_MEMORY_JAVASCRIPT_CORE) +#define VM_TAG_FOR_COLLECTOR_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_CORE) +#else +#define VM_TAG_FOR_COLLECTOR_MEMORY VM_MAKE_TAG(63) +#endif // defined(VM_MEMORY_JAVASCRIPT_CORE) + +#if defined(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) +#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY VM_MAKE_TAG(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) +#else +#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY VM_MAKE_TAG(69) +#endif // defined(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) + +#else // OS(DARWIN) + +#define VM_TAG_FOR_TCMALLOC_MEMORY -1 +#define VM_TAG_FOR_COLLECTOR_MEMORY -1 +#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY -1 +#define VM_TAG_FOR_REGISTERFILE_MEMORY -1 +#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY -1 + +#endif // OS(DARWIN) + +#endif // VMTags_h diff --git a/3rdparty/masm/yarr/Yarr.h b/3rdparty/masm/yarr/Yarr.h new file mode 100644 index 0000000000..d393e9fa90 --- /dev/null +++ b/3rdparty/masm/yarr/Yarr.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Yarr_h +#define Yarr_h + +#include "YarrInterpreter.h" +#include "YarrPattern.h" + +namespace JSC { namespace Yarr { + +#define YarrStackSpaceForBackTrackInfoPatternCharacter 1 // Only for !fixed quantifiers. +#define YarrStackSpaceForBackTrackInfoCharacterClass 1 // Only for !fixed quantifiers. +#define YarrStackSpaceForBackTrackInfoBackReference 2 +#define YarrStackSpaceForBackTrackInfoAlternative 1 // One per alternative. +#define YarrStackSpaceForBackTrackInfoParentheticalAssertion 1 +#define YarrStackSpaceForBackTrackInfoParenthesesOnce 1 // Only for !fixed quantifiers. +#define YarrStackSpaceForBackTrackInfoParenthesesTerminal 1 +#define YarrStackSpaceForBackTrackInfoParentheses 2 + +static const unsigned quantifyInfinite = UINT_MAX; +static const unsigned offsetNoMatch = (unsigned)-1; + +// The below limit restricts the number of "recursive" match calls in order to +// avoid spending exponential time on complex regular expressions. +static const unsigned matchLimit = 1000000; + +enum JSRegExpResult { + JSRegExpMatch = 1, + JSRegExpNoMatch = 0, + JSRegExpErrorNoMatch = -1, + JSRegExpErrorHitLimit = -2, + JSRegExpErrorNoMemory = -3, + JSRegExpErrorInternal = -4 +}; + +enum YarrCharSize { + Char8, + Char16 +}; + +} } // namespace JSC::Yarr + +#endif // Yarr_h + diff --git a/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp b/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp new file mode 100644 index 0000000000..7bb3d08eb5 --- /dev/null +++ b/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// DO NOT EDIT! - this file autogenerated by YarrCanonicalizeUCS2.js + +#include "config.h" +#include "YarrCanonicalizeUCS2.h" + +namespace JSC { namespace Yarr { + +#include + +uint16_t ucs2CharacterSet0[] = { 0x01c4u, 0x01c5u, 0x01c6u, 0 }; +uint16_t ucs2CharacterSet1[] = { 0x01c7u, 0x01c8u, 0x01c9u, 0 }; +uint16_t ucs2CharacterSet2[] = { 0x01cau, 0x01cbu, 0x01ccu, 0 }; +uint16_t ucs2CharacterSet3[] = { 0x01f1u, 0x01f2u, 0x01f3u, 0 }; +uint16_t ucs2CharacterSet4[] = { 0x0392u, 0x03b2u, 0x03d0u, 0 }; +uint16_t ucs2CharacterSet5[] = { 0x0395u, 0x03b5u, 0x03f5u, 0 }; +uint16_t ucs2CharacterSet6[] = { 0x0398u, 0x03b8u, 0x03d1u, 0 }; +uint16_t ucs2CharacterSet7[] = { 0x0345u, 0x0399u, 0x03b9u, 0x1fbeu, 0 }; +uint16_t ucs2CharacterSet8[] = { 0x039au, 0x03bau, 0x03f0u, 0 }; +uint16_t ucs2CharacterSet9[] = { 0x00b5u, 0x039cu, 0x03bcu, 0 }; +uint16_t ucs2CharacterSet10[] = { 0x03a0u, 0x03c0u, 0x03d6u, 0 }; +uint16_t ucs2CharacterSet11[] = { 0x03a1u, 0x03c1u, 0x03f1u, 0 }; +uint16_t ucs2CharacterSet12[] = { 0x03a3u, 0x03c2u, 0x03c3u, 0 }; +uint16_t ucs2CharacterSet13[] = { 0x03a6u, 0x03c6u, 0x03d5u, 0 }; +uint16_t ucs2CharacterSet14[] = { 0x1e60u, 0x1e61u, 0x1e9bu, 0 }; + +static const size_t UCS2_CANONICALIZATION_SETS = 15; +uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = { + ucs2CharacterSet0, + ucs2CharacterSet1, + ucs2CharacterSet2, + ucs2CharacterSet3, + ucs2CharacterSet4, + ucs2CharacterSet5, + ucs2CharacterSet6, + ucs2CharacterSet7, + ucs2CharacterSet8, + ucs2CharacterSet9, + ucs2CharacterSet10, + ucs2CharacterSet11, + ucs2CharacterSet12, + ucs2CharacterSet13, + ucs2CharacterSet14, +}; + +const size_t UCS2_CANONICALIZATION_RANGES = 364; +UCS2CanonicalizationRange rangeInfo[UCS2_CANONICALIZATION_RANGES] = { + { 0x0000u, 0x0040u, 0x0000u, CanonicalizeUnique }, + { 0x0041u, 0x005au, 0x0020u, CanonicalizeRangeLo }, + { 0x005bu, 0x0060u, 0x0000u, CanonicalizeUnique }, + { 0x0061u, 0x007au, 0x0020u, CanonicalizeRangeHi }, + { 0x007bu, 0x00b4u, 0x0000u, CanonicalizeUnique }, + { 0x00b5u, 0x00b5u, 0x0009u, CanonicalizeSet }, + { 0x00b6u, 0x00bfu, 0x0000u, CanonicalizeUnique }, + { 0x00c0u, 0x00d6u, 0x0020u, CanonicalizeRangeLo }, + { 0x00d7u, 0x00d7u, 0x0000u, CanonicalizeUnique }, + { 0x00d8u, 0x00deu, 0x0020u, CanonicalizeRangeLo }, + { 0x00dfu, 0x00dfu, 0x0000u, CanonicalizeUnique }, + { 0x00e0u, 0x00f6u, 0x0020u, CanonicalizeRangeHi }, + { 0x00f7u, 0x00f7u, 0x0000u, CanonicalizeUnique }, + { 0x00f8u, 0x00feu, 0x0020u, CanonicalizeRangeHi }, + { 0x00ffu, 0x00ffu, 0x0079u, CanonicalizeRangeLo }, + { 0x0100u, 0x012fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0130u, 0x0131u, 0x0000u, CanonicalizeUnique }, + { 0x0132u, 0x0137u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0138u, 0x0138u, 0x0000u, CanonicalizeUnique }, + { 0x0139u, 0x0148u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0149u, 0x0149u, 0x0000u, CanonicalizeUnique }, + { 0x014au, 0x0177u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0178u, 0x0178u, 0x0079u, CanonicalizeRangeHi }, + { 0x0179u, 0x017eu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x017fu, 0x017fu, 0x0000u, CanonicalizeUnique }, + { 0x0180u, 0x0180u, 0x00c3u, CanonicalizeRangeLo }, + { 0x0181u, 0x0181u, 0x00d2u, CanonicalizeRangeLo }, + { 0x0182u, 0x0185u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0186u, 0x0186u, 0x00ceu, CanonicalizeRangeLo }, + { 0x0187u, 0x0188u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0189u, 0x018au, 0x00cdu, CanonicalizeRangeLo }, + { 0x018bu, 0x018cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x018du, 0x018du, 0x0000u, CanonicalizeUnique }, + { 0x018eu, 0x018eu, 0x004fu, CanonicalizeRangeLo }, + { 0x018fu, 0x018fu, 0x00cau, CanonicalizeRangeLo }, + { 0x0190u, 0x0190u, 0x00cbu, CanonicalizeRangeLo }, + { 0x0191u, 0x0192u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0193u, 0x0193u, 0x00cdu, CanonicalizeRangeLo }, + { 0x0194u, 0x0194u, 0x00cfu, CanonicalizeRangeLo }, + { 0x0195u, 0x0195u, 0x0061u, CanonicalizeRangeLo }, + { 0x0196u, 0x0196u, 0x00d3u, CanonicalizeRangeLo }, + { 0x0197u, 0x0197u, 0x00d1u, CanonicalizeRangeLo }, + { 0x0198u, 0x0199u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x019au, 0x019au, 0x00a3u, CanonicalizeRangeLo }, + { 0x019bu, 0x019bu, 0x0000u, CanonicalizeUnique }, + { 0x019cu, 0x019cu, 0x00d3u, CanonicalizeRangeLo }, + { 0x019du, 0x019du, 0x00d5u, CanonicalizeRangeLo }, + { 0x019eu, 0x019eu, 0x0082u, CanonicalizeRangeLo }, + { 0x019fu, 0x019fu, 0x00d6u, CanonicalizeRangeLo }, + { 0x01a0u, 0x01a5u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01a6u, 0x01a6u, 0x00dau, CanonicalizeRangeLo }, + { 0x01a7u, 0x01a8u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01a9u, 0x01a9u, 0x00dau, CanonicalizeRangeLo }, + { 0x01aau, 0x01abu, 0x0000u, CanonicalizeUnique }, + { 0x01acu, 0x01adu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01aeu, 0x01aeu, 0x00dau, CanonicalizeRangeLo }, + { 0x01afu, 0x01b0u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01b1u, 0x01b2u, 0x00d9u, CanonicalizeRangeLo }, + { 0x01b3u, 0x01b6u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01b7u, 0x01b7u, 0x00dbu, CanonicalizeRangeLo }, + { 0x01b8u, 0x01b9u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01bau, 0x01bbu, 0x0000u, CanonicalizeUnique }, + { 0x01bcu, 0x01bdu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01beu, 0x01beu, 0x0000u, CanonicalizeUnique }, + { 0x01bfu, 0x01bfu, 0x0038u, CanonicalizeRangeLo }, + { 0x01c0u, 0x01c3u, 0x0000u, CanonicalizeUnique }, + { 0x01c4u, 0x01c6u, 0x0000u, CanonicalizeSet }, + { 0x01c7u, 0x01c9u, 0x0001u, CanonicalizeSet }, + { 0x01cau, 0x01ccu, 0x0002u, CanonicalizeSet }, + { 0x01cdu, 0x01dcu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01ddu, 0x01ddu, 0x004fu, CanonicalizeRangeHi }, + { 0x01deu, 0x01efu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01f0u, 0x01f0u, 0x0000u, CanonicalizeUnique }, + { 0x01f1u, 0x01f3u, 0x0003u, CanonicalizeSet }, + { 0x01f4u, 0x01f5u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01f6u, 0x01f6u, 0x0061u, CanonicalizeRangeHi }, + { 0x01f7u, 0x01f7u, 0x0038u, CanonicalizeRangeHi }, + { 0x01f8u, 0x021fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0220u, 0x0220u, 0x0082u, CanonicalizeRangeHi }, + { 0x0221u, 0x0221u, 0x0000u, CanonicalizeUnique }, + { 0x0222u, 0x0233u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0234u, 0x0239u, 0x0000u, CanonicalizeUnique }, + { 0x023au, 0x023au, 0x2a2bu, CanonicalizeRangeLo }, + { 0x023bu, 0x023cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x023du, 0x023du, 0x00a3u, CanonicalizeRangeHi }, + { 0x023eu, 0x023eu, 0x2a28u, CanonicalizeRangeLo }, + { 0x023fu, 0x0240u, 0x2a3fu, CanonicalizeRangeLo }, + { 0x0241u, 0x0242u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0243u, 0x0243u, 0x00c3u, CanonicalizeRangeHi }, + { 0x0244u, 0x0244u, 0x0045u, CanonicalizeRangeLo }, + { 0x0245u, 0x0245u, 0x0047u, CanonicalizeRangeLo }, + { 0x0246u, 0x024fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0250u, 0x0250u, 0x2a1fu, CanonicalizeRangeLo }, + { 0x0251u, 0x0251u, 0x2a1cu, CanonicalizeRangeLo }, + { 0x0252u, 0x0252u, 0x2a1eu, CanonicalizeRangeLo }, + { 0x0253u, 0x0253u, 0x00d2u, CanonicalizeRangeHi }, + { 0x0254u, 0x0254u, 0x00ceu, CanonicalizeRangeHi }, + { 0x0255u, 0x0255u, 0x0000u, CanonicalizeUnique }, + { 0x0256u, 0x0257u, 0x00cdu, CanonicalizeRangeHi }, + { 0x0258u, 0x0258u, 0x0000u, CanonicalizeUnique }, + { 0x0259u, 0x0259u, 0x00cau, CanonicalizeRangeHi }, + { 0x025au, 0x025au, 0x0000u, CanonicalizeUnique }, + { 0x025bu, 0x025bu, 0x00cbu, CanonicalizeRangeHi }, + { 0x025cu, 0x025fu, 0x0000u, CanonicalizeUnique }, + { 0x0260u, 0x0260u, 0x00cdu, CanonicalizeRangeHi }, + { 0x0261u, 0x0262u, 0x0000u, CanonicalizeUnique }, + { 0x0263u, 0x0263u, 0x00cfu, CanonicalizeRangeHi }, + { 0x0264u, 0x0264u, 0x0000u, CanonicalizeUnique }, + { 0x0265u, 0x0265u, 0xa528u, CanonicalizeRangeLo }, + { 0x0266u, 0x0267u, 0x0000u, CanonicalizeUnique }, + { 0x0268u, 0x0268u, 0x00d1u, CanonicalizeRangeHi }, + { 0x0269u, 0x0269u, 0x00d3u, CanonicalizeRangeHi }, + { 0x026au, 0x026au, 0x0000u, CanonicalizeUnique }, + { 0x026bu, 0x026bu, 0x29f7u, CanonicalizeRangeLo }, + { 0x026cu, 0x026eu, 0x0000u, CanonicalizeUnique }, + { 0x026fu, 0x026fu, 0x00d3u, CanonicalizeRangeHi }, + { 0x0270u, 0x0270u, 0x0000u, CanonicalizeUnique }, + { 0x0271u, 0x0271u, 0x29fdu, CanonicalizeRangeLo }, + { 0x0272u, 0x0272u, 0x00d5u, CanonicalizeRangeHi }, + { 0x0273u, 0x0274u, 0x0000u, CanonicalizeUnique }, + { 0x0275u, 0x0275u, 0x00d6u, CanonicalizeRangeHi }, + { 0x0276u, 0x027cu, 0x0000u, CanonicalizeUnique }, + { 0x027du, 0x027du, 0x29e7u, CanonicalizeRangeLo }, + { 0x027eu, 0x027fu, 0x0000u, CanonicalizeUnique }, + { 0x0280u, 0x0280u, 0x00dau, CanonicalizeRangeHi }, + { 0x0281u, 0x0282u, 0x0000u, CanonicalizeUnique }, + { 0x0283u, 0x0283u, 0x00dau, CanonicalizeRangeHi }, + { 0x0284u, 0x0287u, 0x0000u, CanonicalizeUnique }, + { 0x0288u, 0x0288u, 0x00dau, CanonicalizeRangeHi }, + { 0x0289u, 0x0289u, 0x0045u, CanonicalizeRangeHi }, + { 0x028au, 0x028bu, 0x00d9u, CanonicalizeRangeHi }, + { 0x028cu, 0x028cu, 0x0047u, CanonicalizeRangeHi }, + { 0x028du, 0x0291u, 0x0000u, CanonicalizeUnique }, + { 0x0292u, 0x0292u, 0x00dbu, CanonicalizeRangeHi }, + { 0x0293u, 0x0344u, 0x0000u, CanonicalizeUnique }, + { 0x0345u, 0x0345u, 0x0007u, CanonicalizeSet }, + { 0x0346u, 0x036fu, 0x0000u, CanonicalizeUnique }, + { 0x0370u, 0x0373u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0374u, 0x0375u, 0x0000u, CanonicalizeUnique }, + { 0x0376u, 0x0377u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0378u, 0x037au, 0x0000u, CanonicalizeUnique }, + { 0x037bu, 0x037du, 0x0082u, CanonicalizeRangeLo }, + { 0x037eu, 0x0385u, 0x0000u, CanonicalizeUnique }, + { 0x0386u, 0x0386u, 0x0026u, CanonicalizeRangeLo }, + { 0x0387u, 0x0387u, 0x0000u, CanonicalizeUnique }, + { 0x0388u, 0x038au, 0x0025u, CanonicalizeRangeLo }, + { 0x038bu, 0x038bu, 0x0000u, CanonicalizeUnique }, + { 0x038cu, 0x038cu, 0x0040u, CanonicalizeRangeLo }, + { 0x038du, 0x038du, 0x0000u, CanonicalizeUnique }, + { 0x038eu, 0x038fu, 0x003fu, CanonicalizeRangeLo }, + { 0x0390u, 0x0390u, 0x0000u, CanonicalizeUnique }, + { 0x0391u, 0x0391u, 0x0020u, CanonicalizeRangeLo }, + { 0x0392u, 0x0392u, 0x0004u, CanonicalizeSet }, + { 0x0393u, 0x0394u, 0x0020u, CanonicalizeRangeLo }, + { 0x0395u, 0x0395u, 0x0005u, CanonicalizeSet }, + { 0x0396u, 0x0397u, 0x0020u, CanonicalizeRangeLo }, + { 0x0398u, 0x0398u, 0x0006u, CanonicalizeSet }, + { 0x0399u, 0x0399u, 0x0007u, CanonicalizeSet }, + { 0x039au, 0x039au, 0x0008u, CanonicalizeSet }, + { 0x039bu, 0x039bu, 0x0020u, CanonicalizeRangeLo }, + { 0x039cu, 0x039cu, 0x0009u, CanonicalizeSet }, + { 0x039du, 0x039fu, 0x0020u, CanonicalizeRangeLo }, + { 0x03a0u, 0x03a0u, 0x000au, CanonicalizeSet }, + { 0x03a1u, 0x03a1u, 0x000bu, CanonicalizeSet }, + { 0x03a2u, 0x03a2u, 0x0000u, CanonicalizeUnique }, + { 0x03a3u, 0x03a3u, 0x000cu, CanonicalizeSet }, + { 0x03a4u, 0x03a5u, 0x0020u, CanonicalizeRangeLo }, + { 0x03a6u, 0x03a6u, 0x000du, CanonicalizeSet }, + { 0x03a7u, 0x03abu, 0x0020u, CanonicalizeRangeLo }, + { 0x03acu, 0x03acu, 0x0026u, CanonicalizeRangeHi }, + { 0x03adu, 0x03afu, 0x0025u, CanonicalizeRangeHi }, + { 0x03b0u, 0x03b0u, 0x0000u, CanonicalizeUnique }, + { 0x03b1u, 0x03b1u, 0x0020u, CanonicalizeRangeHi }, + { 0x03b2u, 0x03b2u, 0x0004u, CanonicalizeSet }, + { 0x03b3u, 0x03b4u, 0x0020u, CanonicalizeRangeHi }, + { 0x03b5u, 0x03b5u, 0x0005u, CanonicalizeSet }, + { 0x03b6u, 0x03b7u, 0x0020u, CanonicalizeRangeHi }, + { 0x03b8u, 0x03b8u, 0x0006u, CanonicalizeSet }, + { 0x03b9u, 0x03b9u, 0x0007u, CanonicalizeSet }, + { 0x03bau, 0x03bau, 0x0008u, CanonicalizeSet }, + { 0x03bbu, 0x03bbu, 0x0020u, CanonicalizeRangeHi }, + { 0x03bcu, 0x03bcu, 0x0009u, CanonicalizeSet }, + { 0x03bdu, 0x03bfu, 0x0020u, CanonicalizeRangeHi }, + { 0x03c0u, 0x03c0u, 0x000au, CanonicalizeSet }, + { 0x03c1u, 0x03c1u, 0x000bu, CanonicalizeSet }, + { 0x03c2u, 0x03c3u, 0x000cu, CanonicalizeSet }, + { 0x03c4u, 0x03c5u, 0x0020u, CanonicalizeRangeHi }, + { 0x03c6u, 0x03c6u, 0x000du, CanonicalizeSet }, + { 0x03c7u, 0x03cbu, 0x0020u, CanonicalizeRangeHi }, + { 0x03ccu, 0x03ccu, 0x0040u, CanonicalizeRangeHi }, + { 0x03cdu, 0x03ceu, 0x003fu, CanonicalizeRangeHi }, + { 0x03cfu, 0x03cfu, 0x0008u, CanonicalizeRangeLo }, + { 0x03d0u, 0x03d0u, 0x0004u, CanonicalizeSet }, + { 0x03d1u, 0x03d1u, 0x0006u, CanonicalizeSet }, + { 0x03d2u, 0x03d4u, 0x0000u, CanonicalizeUnique }, + { 0x03d5u, 0x03d5u, 0x000du, CanonicalizeSet }, + { 0x03d6u, 0x03d6u, 0x000au, CanonicalizeSet }, + { 0x03d7u, 0x03d7u, 0x0008u, CanonicalizeRangeHi }, + { 0x03d8u, 0x03efu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x03f0u, 0x03f0u, 0x0008u, CanonicalizeSet }, + { 0x03f1u, 0x03f1u, 0x000bu, CanonicalizeSet }, + { 0x03f2u, 0x03f2u, 0x0007u, CanonicalizeRangeLo }, + { 0x03f3u, 0x03f4u, 0x0000u, CanonicalizeUnique }, + { 0x03f5u, 0x03f5u, 0x0005u, CanonicalizeSet }, + { 0x03f6u, 0x03f6u, 0x0000u, CanonicalizeUnique }, + { 0x03f7u, 0x03f8u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x03f9u, 0x03f9u, 0x0007u, CanonicalizeRangeHi }, + { 0x03fau, 0x03fbu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x03fcu, 0x03fcu, 0x0000u, CanonicalizeUnique }, + { 0x03fdu, 0x03ffu, 0x0082u, CanonicalizeRangeHi }, + { 0x0400u, 0x040fu, 0x0050u, CanonicalizeRangeLo }, + { 0x0410u, 0x042fu, 0x0020u, CanonicalizeRangeLo }, + { 0x0430u, 0x044fu, 0x0020u, CanonicalizeRangeHi }, + { 0x0450u, 0x045fu, 0x0050u, CanonicalizeRangeHi }, + { 0x0460u, 0x0481u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0482u, 0x0489u, 0x0000u, CanonicalizeUnique }, + { 0x048au, 0x04bfu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x04c0u, 0x04c0u, 0x000fu, CanonicalizeRangeLo }, + { 0x04c1u, 0x04ceu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x04cfu, 0x04cfu, 0x000fu, CanonicalizeRangeHi }, + { 0x04d0u, 0x0527u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0528u, 0x0530u, 0x0000u, CanonicalizeUnique }, + { 0x0531u, 0x0556u, 0x0030u, CanonicalizeRangeLo }, + { 0x0557u, 0x0560u, 0x0000u, CanonicalizeUnique }, + { 0x0561u, 0x0586u, 0x0030u, CanonicalizeRangeHi }, + { 0x0587u, 0x109fu, 0x0000u, CanonicalizeUnique }, + { 0x10a0u, 0x10c5u, 0x1c60u, CanonicalizeRangeLo }, + { 0x10c6u, 0x1d78u, 0x0000u, CanonicalizeUnique }, + { 0x1d79u, 0x1d79u, 0x8a04u, CanonicalizeRangeLo }, + { 0x1d7au, 0x1d7cu, 0x0000u, CanonicalizeUnique }, + { 0x1d7du, 0x1d7du, 0x0ee6u, CanonicalizeRangeLo }, + { 0x1d7eu, 0x1dffu, 0x0000u, CanonicalizeUnique }, + { 0x1e00u, 0x1e5fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x1e60u, 0x1e61u, 0x000eu, CanonicalizeSet }, + { 0x1e62u, 0x1e95u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x1e96u, 0x1e9au, 0x0000u, CanonicalizeUnique }, + { 0x1e9bu, 0x1e9bu, 0x000eu, CanonicalizeSet }, + { 0x1e9cu, 0x1e9fu, 0x0000u, CanonicalizeUnique }, + { 0x1ea0u, 0x1effu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x1f00u, 0x1f07u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f08u, 0x1f0fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f10u, 0x1f15u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f16u, 0x1f17u, 0x0000u, CanonicalizeUnique }, + { 0x1f18u, 0x1f1du, 0x0008u, CanonicalizeRangeHi }, + { 0x1f1eu, 0x1f1fu, 0x0000u, CanonicalizeUnique }, + { 0x1f20u, 0x1f27u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f28u, 0x1f2fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f30u, 0x1f37u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f38u, 0x1f3fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f40u, 0x1f45u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f46u, 0x1f47u, 0x0000u, CanonicalizeUnique }, + { 0x1f48u, 0x1f4du, 0x0008u, CanonicalizeRangeHi }, + { 0x1f4eu, 0x1f50u, 0x0000u, CanonicalizeUnique }, + { 0x1f51u, 0x1f51u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f52u, 0x1f52u, 0x0000u, CanonicalizeUnique }, + { 0x1f53u, 0x1f53u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f54u, 0x1f54u, 0x0000u, CanonicalizeUnique }, + { 0x1f55u, 0x1f55u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f56u, 0x1f56u, 0x0000u, CanonicalizeUnique }, + { 0x1f57u, 0x1f57u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f58u, 0x1f58u, 0x0000u, CanonicalizeUnique }, + { 0x1f59u, 0x1f59u, 0x0008u, CanonicalizeRangeHi }, + { 0x1f5au, 0x1f5au, 0x0000u, CanonicalizeUnique }, + { 0x1f5bu, 0x1f5bu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f5cu, 0x1f5cu, 0x0000u, CanonicalizeUnique }, + { 0x1f5du, 0x1f5du, 0x0008u, CanonicalizeRangeHi }, + { 0x1f5eu, 0x1f5eu, 0x0000u, CanonicalizeUnique }, + { 0x1f5fu, 0x1f5fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f60u, 0x1f67u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f68u, 0x1f6fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f70u, 0x1f71u, 0x004au, CanonicalizeRangeLo }, + { 0x1f72u, 0x1f75u, 0x0056u, CanonicalizeRangeLo }, + { 0x1f76u, 0x1f77u, 0x0064u, CanonicalizeRangeLo }, + { 0x1f78u, 0x1f79u, 0x0080u, CanonicalizeRangeLo }, + { 0x1f7au, 0x1f7bu, 0x0070u, CanonicalizeRangeLo }, + { 0x1f7cu, 0x1f7du, 0x007eu, CanonicalizeRangeLo }, + { 0x1f7eu, 0x1fafu, 0x0000u, CanonicalizeUnique }, + { 0x1fb0u, 0x1fb1u, 0x0008u, CanonicalizeRangeLo }, + { 0x1fb2u, 0x1fb7u, 0x0000u, CanonicalizeUnique }, + { 0x1fb8u, 0x1fb9u, 0x0008u, CanonicalizeRangeHi }, + { 0x1fbau, 0x1fbbu, 0x004au, CanonicalizeRangeHi }, + { 0x1fbcu, 0x1fbdu, 0x0000u, CanonicalizeUnique }, + { 0x1fbeu, 0x1fbeu, 0x0007u, CanonicalizeSet }, + { 0x1fbfu, 0x1fc7u, 0x0000u, CanonicalizeUnique }, + { 0x1fc8u, 0x1fcbu, 0x0056u, CanonicalizeRangeHi }, + { 0x1fccu, 0x1fcfu, 0x0000u, CanonicalizeUnique }, + { 0x1fd0u, 0x1fd1u, 0x0008u, CanonicalizeRangeLo }, + { 0x1fd2u, 0x1fd7u, 0x0000u, CanonicalizeUnique }, + { 0x1fd8u, 0x1fd9u, 0x0008u, CanonicalizeRangeHi }, + { 0x1fdau, 0x1fdbu, 0x0064u, CanonicalizeRangeHi }, + { 0x1fdcu, 0x1fdfu, 0x0000u, CanonicalizeUnique }, + { 0x1fe0u, 0x1fe1u, 0x0008u, CanonicalizeRangeLo }, + { 0x1fe2u, 0x1fe4u, 0x0000u, CanonicalizeUnique }, + { 0x1fe5u, 0x1fe5u, 0x0007u, CanonicalizeRangeLo }, + { 0x1fe6u, 0x1fe7u, 0x0000u, CanonicalizeUnique }, + { 0x1fe8u, 0x1fe9u, 0x0008u, CanonicalizeRangeHi }, + { 0x1feau, 0x1febu, 0x0070u, CanonicalizeRangeHi }, + { 0x1fecu, 0x1fecu, 0x0007u, CanonicalizeRangeHi }, + { 0x1fedu, 0x1ff7u, 0x0000u, CanonicalizeUnique }, + { 0x1ff8u, 0x1ff9u, 0x0080u, CanonicalizeRangeHi }, + { 0x1ffau, 0x1ffbu, 0x007eu, CanonicalizeRangeHi }, + { 0x1ffcu, 0x2131u, 0x0000u, CanonicalizeUnique }, + { 0x2132u, 0x2132u, 0x001cu, CanonicalizeRangeLo }, + { 0x2133u, 0x214du, 0x0000u, CanonicalizeUnique }, + { 0x214eu, 0x214eu, 0x001cu, CanonicalizeRangeHi }, + { 0x214fu, 0x215fu, 0x0000u, CanonicalizeUnique }, + { 0x2160u, 0x216fu, 0x0010u, CanonicalizeRangeLo }, + { 0x2170u, 0x217fu, 0x0010u, CanonicalizeRangeHi }, + { 0x2180u, 0x2182u, 0x0000u, CanonicalizeUnique }, + { 0x2183u, 0x2184u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2185u, 0x24b5u, 0x0000u, CanonicalizeUnique }, + { 0x24b6u, 0x24cfu, 0x001au, CanonicalizeRangeLo }, + { 0x24d0u, 0x24e9u, 0x001au, CanonicalizeRangeHi }, + { 0x24eau, 0x2bffu, 0x0000u, CanonicalizeUnique }, + { 0x2c00u, 0x2c2eu, 0x0030u, CanonicalizeRangeLo }, + { 0x2c2fu, 0x2c2fu, 0x0000u, CanonicalizeUnique }, + { 0x2c30u, 0x2c5eu, 0x0030u, CanonicalizeRangeHi }, + { 0x2c5fu, 0x2c5fu, 0x0000u, CanonicalizeUnique }, + { 0x2c60u, 0x2c61u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x2c62u, 0x2c62u, 0x29f7u, CanonicalizeRangeHi }, + { 0x2c63u, 0x2c63u, 0x0ee6u, CanonicalizeRangeHi }, + { 0x2c64u, 0x2c64u, 0x29e7u, CanonicalizeRangeHi }, + { 0x2c65u, 0x2c65u, 0x2a2bu, CanonicalizeRangeHi }, + { 0x2c66u, 0x2c66u, 0x2a28u, CanonicalizeRangeHi }, + { 0x2c67u, 0x2c6cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2c6du, 0x2c6du, 0x2a1cu, CanonicalizeRangeHi }, + { 0x2c6eu, 0x2c6eu, 0x29fdu, CanonicalizeRangeHi }, + { 0x2c6fu, 0x2c6fu, 0x2a1fu, CanonicalizeRangeHi }, + { 0x2c70u, 0x2c70u, 0x2a1eu, CanonicalizeRangeHi }, + { 0x2c71u, 0x2c71u, 0x0000u, CanonicalizeUnique }, + { 0x2c72u, 0x2c73u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x2c74u, 0x2c74u, 0x0000u, CanonicalizeUnique }, + { 0x2c75u, 0x2c76u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2c77u, 0x2c7du, 0x0000u, CanonicalizeUnique }, + { 0x2c7eu, 0x2c7fu, 0x2a3fu, CanonicalizeRangeHi }, + { 0x2c80u, 0x2ce3u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x2ce4u, 0x2ceau, 0x0000u, CanonicalizeUnique }, + { 0x2cebu, 0x2ceeu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2cefu, 0x2cffu, 0x0000u, CanonicalizeUnique }, + { 0x2d00u, 0x2d25u, 0x1c60u, CanonicalizeRangeHi }, + { 0x2d26u, 0xa63fu, 0x0000u, CanonicalizeUnique }, + { 0xa640u, 0xa66du, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa66eu, 0xa67fu, 0x0000u, CanonicalizeUnique }, + { 0xa680u, 0xa697u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa698u, 0xa721u, 0x0000u, CanonicalizeUnique }, + { 0xa722u, 0xa72fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa730u, 0xa731u, 0x0000u, CanonicalizeUnique }, + { 0xa732u, 0xa76fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa770u, 0xa778u, 0x0000u, CanonicalizeUnique }, + { 0xa779u, 0xa77cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0xa77du, 0xa77du, 0x8a04u, CanonicalizeRangeHi }, + { 0xa77eu, 0xa787u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa788u, 0xa78au, 0x0000u, CanonicalizeUnique }, + { 0xa78bu, 0xa78cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0xa78du, 0xa78du, 0xa528u, CanonicalizeRangeHi }, + { 0xa78eu, 0xa78fu, 0x0000u, CanonicalizeUnique }, + { 0xa790u, 0xa791u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa792u, 0xa79fu, 0x0000u, CanonicalizeUnique }, + { 0xa7a0u, 0xa7a9u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa7aau, 0xff20u, 0x0000u, CanonicalizeUnique }, + { 0xff21u, 0xff3au, 0x0020u, CanonicalizeRangeLo }, + { 0xff3bu, 0xff40u, 0x0000u, CanonicalizeUnique }, + { 0xff41u, 0xff5au, 0x0020u, CanonicalizeRangeHi }, + { 0xff5bu, 0xffffu, 0x0000u, CanonicalizeUnique }, +}; + +const size_t LATIN_CANONICALIZATION_RANGES = 20; +LatinCanonicalizationRange latinRangeInfo[LATIN_CANONICALIZATION_RANGES] = { + { 0x0000u, 0x0040u, 0x0000u, CanonicalizeLatinSelf }, + { 0x0041u, 0x005au, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x005bu, 0x0060u, 0x0000u, CanonicalizeLatinSelf }, + { 0x0061u, 0x007au, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x007bu, 0x00bfu, 0x0000u, CanonicalizeLatinSelf }, + { 0x00c0u, 0x00d6u, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00d7u, 0x00d7u, 0x0000u, CanonicalizeLatinSelf }, + { 0x00d8u, 0x00deu, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00dfu, 0x00dfu, 0x0000u, CanonicalizeLatinSelf }, + { 0x00e0u, 0x00f6u, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00f7u, 0x00f7u, 0x0000u, CanonicalizeLatinSelf }, + { 0x00f8u, 0x00feu, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00ffu, 0x00ffu, 0x0000u, CanonicalizeLatinSelf }, + { 0x0100u, 0x0177u, 0x0000u, CanonicalizeLatinInvalid }, + { 0x0178u, 0x0178u, 0x00ffu, CanonicalizeLatinOther }, + { 0x0179u, 0x039bu, 0x0000u, CanonicalizeLatinInvalid }, + { 0x039cu, 0x039cu, 0x00b5u, CanonicalizeLatinOther }, + { 0x039du, 0x03bbu, 0x0000u, CanonicalizeLatinInvalid }, + { 0x03bcu, 0x03bcu, 0x00b5u, CanonicalizeLatinOther }, + { 0x03bdu, 0xffffu, 0x0000u, CanonicalizeLatinInvalid }, +}; + +} } // JSC::Yarr + diff --git a/3rdparty/masm/yarr/YarrCanonicalizeUCS2.h b/3rdparty/masm/yarr/YarrCanonicalizeUCS2.h new file mode 100644 index 0000000000..be0ead43d2 --- /dev/null +++ b/3rdparty/masm/yarr/YarrCanonicalizeUCS2.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrCanonicalizeUCS2_H +#define YarrCanonicalizeUCS2_H + +#include +#include + +namespace JSC { namespace Yarr { + +// This set of data (autogenerated using YarrCanonicalizeUCS2.js into YarrCanonicalizeUCS2.cpp) +// provides information for each UCS2 code point as to the set of code points that it should +// match under the ES5.1 case insensitive RegExp matching rules, specified in 15.10.2.8. +enum UCS2CanonicalizationType { + CanonicalizeUnique, // No canonically equal values, e.g. 0x0. + CanonicalizeSet, // Value indicates a set in characterSetInfo. + CanonicalizeRangeLo, // Value is positive delta to pair, E.g. 0x41 has value 0x20, -> 0x61. + CanonicalizeRangeHi, // Value is positive delta to pair, E.g. 0x61 has value 0x20, -> 0x41. + CanonicalizeAlternatingAligned, // Aligned consequtive pair, e.g. 0x1f4,0x1f5. + CanonicalizeAlternatingUnaligned, // Unaligned consequtive pair, e.g. 0x241,0x242. +}; +struct UCS2CanonicalizationRange { uint16_t begin, end, value, type; }; +extern const size_t UCS2_CANONICALIZATION_RANGES; +extern uint16_t* characterSetInfo[]; +extern UCS2CanonicalizationRange rangeInfo[]; + +// This table is similar to the full rangeInfo table, however this maps from UCS2 codepoints to +// the set of Latin1 codepoints that could match. +enum LatinCanonicalizationType { + CanonicalizeLatinSelf, // This character is in the Latin1 range, but has no canonical equivalent in the range. + CanonicalizeLatinMask0x20, // One of a pair of characters, under the mask 0x20. + CanonicalizeLatinOther, // This character is not in the Latin1 range, but canonicalizes to another that is. + CanonicalizeLatinInvalid, // Cannot match against Latin1 input. +}; +struct LatinCanonicalizationRange { uint16_t begin, end, value, type; }; +extern const size_t LATIN_CANONICALIZATION_RANGES; +extern LatinCanonicalizationRange latinRangeInfo[]; + +// This searches in log2 time over ~364 entries, so should typically result in 8 compares. +inline UCS2CanonicalizationRange* rangeInfoFor(UChar ch) +{ + UCS2CanonicalizationRange* info = rangeInfo; + size_t entries = UCS2_CANONICALIZATION_RANGES; + + while (true) { + size_t candidate = entries >> 1; + UCS2CanonicalizationRange* candidateInfo = info + candidate; + if (ch < candidateInfo->begin) + entries = candidate; + else if (ch <= candidateInfo->end) + return candidateInfo; + else { + info = candidateInfo + 1; + entries -= (candidate + 1); + } + } +} + +// Should only be called for characters that have one canonically matching value. +inline UChar getCanonicalPair(UCS2CanonicalizationRange* info, UChar ch) +{ + ASSERT(ch >= info->begin && ch <= info->end); + switch (info->type) { + case CanonicalizeRangeLo: + return ch + info->value; + case CanonicalizeRangeHi: + return ch - info->value; + case CanonicalizeAlternatingAligned: + return ch ^ 1; + case CanonicalizeAlternatingUnaligned: + return ((ch - 1) ^ 1) + 1; + default: + ASSERT_NOT_REACHED(); + } + ASSERT_NOT_REACHED(); + return 0; +} + +// Returns true if no other UCS2 codepoint can match this value. +inline bool isCanonicallyUnique(UChar ch) +{ + return rangeInfoFor(ch)->type == CanonicalizeUnique; +} + +// Returns true if values are equal, under the canonicalization rules. +inline bool areCanonicallyEquivalent(UChar a, UChar b) +{ + UCS2CanonicalizationRange* info = rangeInfoFor(a); + switch (info->type) { + case CanonicalizeUnique: + return a == b; + case CanonicalizeSet: { + for (uint16_t* set = characterSetInfo[info->value]; (a = *set); ++set) { + if (a == b) + return true; + } + return false; + } + case CanonicalizeRangeLo: + return (a == b) || (a + info->value == b); + case CanonicalizeRangeHi: + return (a == b) || (a - info->value == b); + case CanonicalizeAlternatingAligned: + return (a | 1) == (b | 1); + case CanonicalizeAlternatingUnaligned: + return ((a - 1) | 1) == ((b - 1) | 1); + } + + ASSERT_NOT_REACHED(); + return false; +} + +} } // JSC::Yarr + +#endif diff --git a/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js b/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js new file mode 100644 index 0000000000..00361dd46e --- /dev/null +++ b/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// See ES 5.1, 15.10.2.8 +function canonicalize(ch) +{ + var u = String.fromCharCode(ch).toUpperCase(); + if (u.length > 1) + return ch; + var cu = u.charCodeAt(0); + if (ch >= 128 && cu < 128) + return ch; + return cu; +} + +var MAX_UCS2 = 0xFFFF; +var MAX_LATIN = 0xFF; + +var groupedCanonically = []; +// Pass 1: populate groupedCanonically - this is mapping from canonicalized +// values back to the set of character code that canonicalize to them. +for (var i = 0; i <= MAX_UCS2; ++i) { + var ch = canonicalize(i); + if (!groupedCanonically[ch]) + groupedCanonically[ch] = []; + groupedCanonically[ch].push(i); +} + +var typeInfo = []; +var latinTypeInfo = []; +var characterSetInfo = []; +// Pass 2: populate typeInfo & characterSetInfo. For every character calculate +// a typeInfo value, described by the types above, and a value payload. +for (cu in groupedCanonically) { + // The set of characters that canonicalize to cu + var characters = groupedCanonically[cu]; + + // If there is only one, it is unique. + if (characters.length == 1) { + typeInfo[characters[0]] = "CanonicalizeUnique:0"; + latinTypeInfo[characters[0]] = characters[0] <= MAX_LATIN ? "CanonicalizeLatinSelf:0" : "CanonicalizeLatinInvalid:0"; + continue; + } + + // Sort the array. + characters.sort(function(x,y){return x-y;}); + + // If there are more than two characters, create an entry in characterSetInfo. + if (characters.length > 2) { + for (i in characters) + typeInfo[characters[i]] = "CanonicalizeSet:" + characterSetInfo.length; + characterSetInfo.push(characters); + + if (characters[1] <= MAX_LATIN) + throw new Error("sets with more than one latin character not supported!"); + if (characters[0] <= MAX_LATIN) { + for (i in characters) + latinTypeInfo[characters[i]] = "CanonicalizeLatinOther:" + characters[0]; + latinTypeInfo[characters[0]] = "CanonicalizeLatinSelf:0"; + } else { + for (i in characters) + latinTypeInfo[characters[i]] = "CanonicalizeLatinInvalid:0"; + } + + continue; + } + + // We have a pair, mark alternating ranges, otherwise track whether this is the low or high partner. + var lo = characters[0]; + var hi = characters[1]; + var delta = hi - lo; + if (delta == 1) { + var type = lo & 1 ? "CanonicalizeAlternatingUnaligned:0" : "CanonicalizeAlternatingAligned:0"; + typeInfo[lo] = type; + typeInfo[hi] = type; + } else { + typeInfo[lo] = "CanonicalizeRangeLo:" + delta; + typeInfo[hi] = "CanonicalizeRangeHi:" + delta; + } + + if (lo > MAX_LATIN) { + latinTypeInfo[lo] = "CanonicalizeLatinInvalid:0"; + latinTypeInfo[hi] = "CanonicalizeLatinInvalid:0"; + } else if (hi > MAX_LATIN) { + latinTypeInfo[lo] = "CanonicalizeLatinSelf:0"; + latinTypeInfo[hi] = "CanonicalizeLatinOther:" + lo; + } else { + if (delta != 0x20 || lo & 0x20) + throw new Error("pairs of latin characters that don't mask with 0x20 not supported!"); + latinTypeInfo[lo] = "CanonicalizeLatinMask0x20:0"; + latinTypeInfo[hi] = "CanonicalizeLatinMask0x20:0"; + } +} + +var rangeInfo = []; +// Pass 3: coallesce types into ranges. +for (var end = 0; end <= MAX_UCS2; ++end) { + var begin = end; + var type = typeInfo[end]; + while (end < MAX_UCS2 && typeInfo[end + 1] == type) + ++end; + rangeInfo.push({begin:begin, end:end, type:type}); +} + +var latinRangeInfo = []; +// Pass 4: coallesce latin-1 types into ranges. +for (var end = 0; end <= MAX_UCS2; ++end) { + var begin = end; + var type = latinTypeInfo[end]; + while (end < MAX_UCS2 && latinTypeInfo[end + 1] == type) + ++end; + latinRangeInfo.push({begin:begin, end:end, type:type}); +} + + +// Helper function to convert a number to a fixed width hex representation of a C uint16_t. +function hex(x) +{ + var s = Number(x).toString(16); + while (s.length < 4) + s = 0 + s; + return "0x" + s + "u"; +} + +var copyright = ( + "/*" + "\n" + + " * Copyright (C) 2012 Apple Inc. All rights reserved." + "\n" + + " *" + "\n" + + " * Redistribution and use in source and binary forms, with or without" + "\n" + + " * modification, are permitted provided that the following conditions" + "\n" + + " * are met:" + "\n" + + " * 1. Redistributions of source code must retain the above copyright" + "\n" + + " * notice, this list of conditions and the following disclaimer." + "\n" + + " * 2. Redistributions in binary form must reproduce the above copyright" + "\n" + + " * notice, this list of conditions and the following disclaimer in the" + "\n" + + " * documentation and/or other materials provided with the distribution." + "\n" + + " *" + "\n" + + " * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY" + "\n" + + " * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE" + "\n" + + " * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR" + "\n" + + " * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR" + "\n" + + " * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL," + "\n" + + " * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO," + "\n" + + " * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR" + "\n" + + " * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY" + "\n" + + " * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" + "\n" + + " * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" + "\n" + + " * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. " + "\n" + + " */"); + +print(copyright); +print(); +print("// DO NOT EDIT! - this file autogenerated by YarrCanonicalizeUCS2.js"); +print(); +print('#include "config.h"'); +print('#include "YarrCanonicalizeUCS2.h"'); +print(); +print("namespace JSC { namespace Yarr {"); +print(); +print("#include "); +print(); + +for (i in characterSetInfo) { + var characters = "" + var set = characterSetInfo[i]; + for (var j in set) + characters += hex(set[j]) + ", "; + print("uint16_t ucs2CharacterSet" + i + "[] = { " + characters + "0 };"); +} +print(); +print("static const size_t UCS2_CANONICALIZATION_SETS = " + characterSetInfo.length + ";"); +print("uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = {"); +for (i in characterSetInfo) +print(" ucs2CharacterSet" + i + ","); +print("};"); +print(); +print("const size_t UCS2_CANONICALIZATION_RANGES = " + rangeInfo.length + ";"); +print("UCS2CanonicalizationRange rangeInfo[UCS2_CANONICALIZATION_RANGES] = {"); +for (i in rangeInfo) { + var info = rangeInfo[i]; + var typeAndValue = info.type.split(':'); + print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },"); +} +print("};"); +print(); +print("const size_t LATIN_CANONICALIZATION_RANGES = " + latinRangeInfo.length + ";"); +print("LatinCanonicalizationRange latinRangeInfo[LATIN_CANONICALIZATION_RANGES] = {"); +for (i in latinRangeInfo) { + var info = latinRangeInfo[i]; + var typeAndValue = info.type.split(':'); + print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },"); +} +print("};"); +print(); +print("} } // JSC::Yarr"); +print(); + diff --git a/3rdparty/masm/yarr/YarrInterpreter.cpp b/3rdparty/masm/yarr/YarrInterpreter.cpp new file mode 100644 index 0000000000..31603f6d34 --- /dev/null +++ b/3rdparty/masm/yarr/YarrInterpreter.cpp @@ -0,0 +1,1964 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "YarrInterpreter.h" + +#include "Yarr.h" +#include "YarrCanonicalizeUCS2.h" +#include +#include +#include +#include + +#ifndef NDEBUG +#include +#endif + +using namespace WTF; + +namespace JSC { namespace Yarr { + +template +class Interpreter { +public: + struct ParenthesesDisjunctionContext; + + struct BackTrackInfoPatternCharacter { + uintptr_t matchAmount; + }; + struct BackTrackInfoCharacterClass { + uintptr_t matchAmount; + }; + struct BackTrackInfoBackReference { + uintptr_t begin; // Not really needed for greedy quantifiers. + uintptr_t matchAmount; // Not really needed for fixed quantifiers. + }; + struct BackTrackInfoAlternative { + uintptr_t offset; + }; + struct BackTrackInfoParentheticalAssertion { + uintptr_t begin; + }; + struct BackTrackInfoParenthesesOnce { + uintptr_t begin; + }; + struct BackTrackInfoParenthesesTerminal { + uintptr_t begin; + }; + struct BackTrackInfoParentheses { + uintptr_t matchAmount; + ParenthesesDisjunctionContext* lastContext; + }; + + static inline void appendParenthesesDisjunctionContext(BackTrackInfoParentheses* backTrack, ParenthesesDisjunctionContext* context) + { + context->next = backTrack->lastContext; + backTrack->lastContext = context; + ++backTrack->matchAmount; + } + + static inline void popParenthesesDisjunctionContext(BackTrackInfoParentheses* backTrack) + { + ASSERT(backTrack->matchAmount); + ASSERT(backTrack->lastContext); + backTrack->lastContext = backTrack->lastContext->next; + --backTrack->matchAmount; + } + + struct DisjunctionContext + { + DisjunctionContext() + : term(0) + { + } + + void* operator new(size_t, void* where) + { + return where; + } + + int term; + unsigned matchBegin; + unsigned matchEnd; + uintptr_t frame[1]; + }; + + DisjunctionContext* allocDisjunctionContext(ByteDisjunction* disjunction) + { + size_t size = sizeof(DisjunctionContext) - sizeof(uintptr_t) + disjunction->m_frameSize * sizeof(uintptr_t); + allocatorPool = allocatorPool->ensureCapacity(size); + if (!allocatorPool) + CRASH(); + return new (allocatorPool->alloc(size)) DisjunctionContext(); + } + + void freeDisjunctionContext(DisjunctionContext* context) + { + allocatorPool = allocatorPool->dealloc(context); + } + + struct ParenthesesDisjunctionContext + { + ParenthesesDisjunctionContext(unsigned* output, ByteTerm& term) + : next(0) + { + unsigned firstSubpatternId = term.atom.subpatternId; + unsigned numNestedSubpatterns = term.atom.parenthesesDisjunction->m_numSubpatterns; + + for (unsigned i = 0; i < (numNestedSubpatterns << 1); ++i) { + subpatternBackup[i] = output[(firstSubpatternId << 1) + i]; + output[(firstSubpatternId << 1) + i] = offsetNoMatch; + } + + new (getDisjunctionContext(term)) DisjunctionContext(); + } + + void* operator new(size_t, void* where) + { + return where; + } + + void restoreOutput(unsigned* output, unsigned firstSubpatternId, unsigned numNestedSubpatterns) + { + for (unsigned i = 0; i < (numNestedSubpatterns << 1); ++i) + output[(firstSubpatternId << 1) + i] = subpatternBackup[i]; + } + + DisjunctionContext* getDisjunctionContext(ByteTerm& term) + { + return reinterpret_cast(&(subpatternBackup[term.atom.parenthesesDisjunction->m_numSubpatterns << 1])); + } + + ParenthesesDisjunctionContext* next; + unsigned subpatternBackup[1]; + }; + + ParenthesesDisjunctionContext* allocParenthesesDisjunctionContext(ByteDisjunction* disjunction, unsigned* output, ByteTerm& term) + { + size_t size = sizeof(ParenthesesDisjunctionContext) - sizeof(unsigned) + (term.atom.parenthesesDisjunction->m_numSubpatterns << 1) * sizeof(unsigned) + sizeof(DisjunctionContext) - sizeof(uintptr_t) + disjunction->m_frameSize * sizeof(uintptr_t); + allocatorPool = allocatorPool->ensureCapacity(size); + if (!allocatorPool) + CRASH(); + return new (allocatorPool->alloc(size)) ParenthesesDisjunctionContext(output, term); + } + + void freeParenthesesDisjunctionContext(ParenthesesDisjunctionContext* context) + { + allocatorPool = allocatorPool->dealloc(context); + } + + class InputStream { + public: + InputStream(const CharType* input, unsigned start, unsigned length) + : input(input) + , pos(start) + , length(length) + { + } + + void next() + { + ++pos; + } + + void rewind(unsigned amount) + { + ASSERT(pos >= amount); + pos -= amount; + } + + int read() + { + ASSERT(pos < length); + if (pos < length) + return input[pos]; + return -1; + } + + int readPair() + { + ASSERT(pos + 1 < length); + return input[pos] | input[pos + 1] << 16; + } + + int readChecked(unsigned negativePositionOffest) + { + if (pos < negativePositionOffest) + CRASH(); + unsigned p = pos - negativePositionOffest; + ASSERT(p < length); + return input[p]; + } + + int reread(unsigned from) + { + ASSERT(from < length); + return input[from]; + } + + int prev() + { + ASSERT(!(pos > length)); + if (pos && length) + return input[pos - 1]; + return -1; + } + + unsigned getPos() + { + return pos; + } + + void setPos(unsigned p) + { + pos = p; + } + + bool atStart() + { + return pos == 0; + } + + bool atEnd() + { + return pos == length; + } + + unsigned end() + { + return length; + } + + bool checkInput(unsigned count) + { + if (((pos + count) <= length) && ((pos + count) >= pos)) { + pos += count; + return true; + } + return false; + } + + void uncheckInput(unsigned count) + { + if (pos < count) + CRASH(); + pos -= count; + } + + bool atStart(unsigned negativePositionOffest) + { + return pos == negativePositionOffest; + } + + bool atEnd(unsigned negativePositionOffest) + { + if (pos < negativePositionOffest) + CRASH(); + return (pos - negativePositionOffest) == length; + } + + bool isAvailableInput(unsigned offset) + { + return (((pos + offset) <= length) && ((pos + offset) >= pos)); + } + + private: + const CharType* input; + unsigned pos; + unsigned length; + }; + + bool testCharacterClass(CharacterClass* characterClass, int ch) + { + if (ch & 0xFF80) { + for (unsigned i = 0; i < characterClass->m_matchesUnicode.size(); ++i) + if (ch == characterClass->m_matchesUnicode[i]) + return true; + for (unsigned i = 0; i < characterClass->m_rangesUnicode.size(); ++i) + if ((ch >= characterClass->m_rangesUnicode[i].begin) && (ch <= characterClass->m_rangesUnicode[i].end)) + return true; + } else { + for (unsigned i = 0; i < characterClass->m_matches.size(); ++i) + if (ch == characterClass->m_matches[i]) + return true; + for (unsigned i = 0; i < characterClass->m_ranges.size(); ++i) + if ((ch >= characterClass->m_ranges[i].begin) && (ch <= characterClass->m_ranges[i].end)) + return true; + } + + return false; + } + + bool checkCharacter(int testChar, unsigned negativeInputOffset) + { + return testChar == input.readChecked(negativeInputOffset); + } + + bool checkCasedCharacter(int loChar, int hiChar, unsigned negativeInputOffset) + { + int ch = input.readChecked(negativeInputOffset); + return (loChar == ch) || (hiChar == ch); + } + + bool checkCharacterClass(CharacterClass* characterClass, bool invert, unsigned negativeInputOffset) + { + bool match = testCharacterClass(characterClass, input.readChecked(negativeInputOffset)); + return invert ? !match : match; + } + + bool tryConsumeBackReference(int matchBegin, int matchEnd, unsigned negativeInputOffset) + { + unsigned matchSize = (unsigned)(matchEnd - matchBegin); + + if (!input.checkInput(matchSize)) + return false; + + if (pattern->m_ignoreCase) { + for (unsigned i = 0; i < matchSize; ++i) { + int oldCh = input.reread(matchBegin + i); + int ch = input.readChecked(negativeInputOffset + matchSize - i); + + if (oldCh == ch) + continue; + + // The definition for canonicalize (see ES 5.1, 15.10.2.8) means that + // unicode values are never allowed to match against ascii ones. + if (isASCII(oldCh) || isASCII(ch)) { + if (toASCIIUpper(oldCh) == toASCIIUpper(ch)) + continue; + } else if (areCanonicallyEquivalent(oldCh, ch)) + continue; + + input.uncheckInput(matchSize); + return false; + } + } else { + for (unsigned i = 0; i < matchSize; ++i) { + if (!checkCharacter(input.reread(matchBegin + i), negativeInputOffset + matchSize - i)) { + input.uncheckInput(matchSize); + return false; + } + } + } + + return true; + } + + bool matchAssertionBOL(ByteTerm& term) + { + return (input.atStart(term.inputPosition)) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition + 1))); + } + + bool matchAssertionEOL(ByteTerm& term) + { + if (term.inputPosition) + return (input.atEnd(term.inputPosition)) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition))); + + return (input.atEnd()) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.read())); + } + + bool matchAssertionWordBoundary(ByteTerm& term) + { + bool prevIsWordchar = !input.atStart(term.inputPosition) && testCharacterClass(pattern->wordcharCharacterClass, input.readChecked(term.inputPosition + 1)); + bool readIsWordchar; + if (term.inputPosition) + readIsWordchar = !input.atEnd(term.inputPosition) && testCharacterClass(pattern->wordcharCharacterClass, input.readChecked(term.inputPosition)); + else + readIsWordchar = !input.atEnd() && testCharacterClass(pattern->wordcharCharacterClass, input.read()); + + bool wordBoundary = prevIsWordchar != readIsWordchar; + return term.invert() ? !wordBoundary : wordBoundary; + } + + bool backtrackPatternCharacter(ByteTerm& term, DisjunctionContext* context) + { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierFixedCount: + break; + + case QuantifierGreedy: + if (backTrack->matchAmount) { + --backTrack->matchAmount; + input.uncheckInput(1); + return true; + } + break; + + case QuantifierNonGreedy: + if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { + ++backTrack->matchAmount; + if (checkCharacter(term.atom.patternCharacter, term.inputPosition + 1)) + return true; + } + input.uncheckInput(backTrack->matchAmount); + break; + } + + return false; + } + + bool backtrackPatternCasedCharacter(ByteTerm& term, DisjunctionContext* context) + { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierFixedCount: + break; + + case QuantifierGreedy: + if (backTrack->matchAmount) { + --backTrack->matchAmount; + input.uncheckInput(1); + return true; + } + break; + + case QuantifierNonGreedy: + if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { + ++backTrack->matchAmount; + if (checkCasedCharacter(term.atom.casedCharacter.lo, term.atom.casedCharacter.hi, term.inputPosition + 1)) + return true; + } + input.uncheckInput(backTrack->matchAmount); + break; + } + + return false; + } + + bool matchCharacterClass(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeCharacterClass); + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierFixedCount: { + for (unsigned matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) { + if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition - matchAmount)) + return false; + } + return true; + } + + case QuantifierGreedy: { + unsigned matchAmount = 0; + while ((matchAmount < term.atom.quantityCount) && input.checkInput(1)) { + if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) { + input.uncheckInput(1); + break; + } + ++matchAmount; + } + backTrack->matchAmount = matchAmount; + + return true; + } + + case QuantifierNonGreedy: + backTrack->matchAmount = 0; + return true; + } + + ASSERT_NOT_REACHED(); + return false; + } + + bool backtrackCharacterClass(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeCharacterClass); + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierFixedCount: + break; + + case QuantifierGreedy: + if (backTrack->matchAmount) { + --backTrack->matchAmount; + input.uncheckInput(1); + return true; + } + break; + + case QuantifierNonGreedy: + if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { + ++backTrack->matchAmount; + if (checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) + return true; + } + input.uncheckInput(backTrack->matchAmount); + break; + } + + return false; + } + + bool matchBackReference(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeBackReference); + BackTrackInfoBackReference* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + unsigned matchBegin = output[(term.atom.subpatternId << 1)]; + unsigned matchEnd = output[(term.atom.subpatternId << 1) + 1]; + + // If the end position of the referenced match hasn't set yet then the backreference in the same parentheses where it references to that. + // In this case the result of match is empty string like when it references to a parentheses with zero-width match. + // Eg.: /(a\1)/ + if (matchEnd == offsetNoMatch) + return true; + + if (matchBegin == offsetNoMatch) + return true; + + ASSERT(matchBegin <= matchEnd); + + if (matchBegin == matchEnd) + return true; + + switch (term.atom.quantityType) { + case QuantifierFixedCount: { + backTrack->begin = input.getPos(); + for (unsigned matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) { + if (!tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) { + input.setPos(backTrack->begin); + return false; + } + } + return true; + } + + case QuantifierGreedy: { + unsigned matchAmount = 0; + while ((matchAmount < term.atom.quantityCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) + ++matchAmount; + backTrack->matchAmount = matchAmount; + return true; + } + + case QuantifierNonGreedy: + backTrack->begin = input.getPos(); + backTrack->matchAmount = 0; + return true; + } + + ASSERT_NOT_REACHED(); + return false; + } + + bool backtrackBackReference(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeBackReference); + BackTrackInfoBackReference* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + unsigned matchBegin = output[(term.atom.subpatternId << 1)]; + unsigned matchEnd = output[(term.atom.subpatternId << 1) + 1]; + + if (matchBegin == offsetNoMatch) + return false; + + ASSERT(matchBegin <= matchEnd); + + if (matchBegin == matchEnd) + return false; + + switch (term.atom.quantityType) { + case QuantifierFixedCount: + // for quantityCount == 1, could rewind. + input.setPos(backTrack->begin); + break; + + case QuantifierGreedy: + if (backTrack->matchAmount) { + --backTrack->matchAmount; + input.rewind(matchEnd - matchBegin); + return true; + } + break; + + case QuantifierNonGreedy: + if ((backTrack->matchAmount < term.atom.quantityCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) { + ++backTrack->matchAmount; + return true; + } + input.setPos(backTrack->begin); + break; + } + + return false; + } + + void recordParenthesesMatch(ByteTerm& term, ParenthesesDisjunctionContext* context) + { + if (term.capture()) { + unsigned subpatternId = term.atom.subpatternId; + output[(subpatternId << 1)] = context->getDisjunctionContext(term)->matchBegin + term.inputPosition; + output[(subpatternId << 1) + 1] = context->getDisjunctionContext(term)->matchEnd + term.inputPosition; + } + } + void resetMatches(ByteTerm& term, ParenthesesDisjunctionContext* context) + { + unsigned firstSubpatternId = term.atom.subpatternId; + unsigned count = term.atom.parenthesesDisjunction->m_numSubpatterns; + context->restoreOutput(output, firstSubpatternId, count); + } + JSRegExpResult parenthesesDoBacktrack(ByteTerm& term, BackTrackInfoParentheses* backTrack) + { + while (backTrack->matchAmount) { + ParenthesesDisjunctionContext* context = backTrack->lastContext; + + JSRegExpResult result = matchDisjunction(term.atom.parenthesesDisjunction, context->getDisjunctionContext(term), true); + if (result == JSRegExpMatch) + return JSRegExpMatch; + + resetMatches(term, context); + popParenthesesDisjunctionContext(backTrack); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + } + + return JSRegExpNoMatch; + } + + bool matchParenthesesOnceBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierGreedy: { + // set this speculatively; if we get to the parens end this will be true. + backTrack->begin = input.getPos(); + break; + } + case QuantifierNonGreedy: { + backTrack->begin = notFound; + context->term += term.atom.parenthesesWidth; + return true; + } + case QuantifierFixedCount: + break; + } + + if (term.capture()) { + unsigned subpatternId = term.atom.subpatternId; + output[(subpatternId << 1)] = input.getPos() - term.inputPosition; + } + + return true; + } + + bool matchParenthesesOnceEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceEnd); + ASSERT(term.atom.quantityCount == 1); + + if (term.capture()) { + unsigned subpatternId = term.atom.subpatternId; + output[(subpatternId << 1) + 1] = input.getPos() + term.inputPosition; + } + + if (term.atom.quantityType == QuantifierFixedCount) + return true; + + BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); + return backTrack->begin != input.getPos(); + } + + bool backtrackParenthesesOnceBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + if (term.capture()) { + unsigned subpatternId = term.atom.subpatternId; + output[(subpatternId << 1)] = offsetNoMatch; + output[(subpatternId << 1) + 1] = offsetNoMatch; + } + + switch (term.atom.quantityType) { + case QuantifierGreedy: + // if we backtrack to this point, there is another chance - try matching nothing. + ASSERT(backTrack->begin != notFound); + backTrack->begin = notFound; + context->term += term.atom.parenthesesWidth; + return true; + case QuantifierNonGreedy: + ASSERT(backTrack->begin != notFound); + case QuantifierFixedCount: + break; + } + + return false; + } + + bool backtrackParenthesesOnceEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceEnd); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierGreedy: + if (backTrack->begin == notFound) { + context->term -= term.atom.parenthesesWidth; + return false; + } + case QuantifierNonGreedy: + if (backTrack->begin == notFound) { + backTrack->begin = input.getPos(); + if (term.capture()) { + // Technically this access to inputPosition should be accessing the begin term's + // inputPosition, but for repeats other than fixed these values should be + // the same anyway! (We don't pre-check for greedy or non-greedy matches.) + ASSERT((&term - term.atom.parenthesesWidth)->type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + ASSERT((&term - term.atom.parenthesesWidth)->inputPosition == term.inputPosition); + unsigned subpatternId = term.atom.subpatternId; + output[subpatternId << 1] = input.getPos() + term.inputPosition; + } + context->term -= term.atom.parenthesesWidth; + return true; + } + case QuantifierFixedCount: + break; + } + + return false; + } + + bool matchParenthesesTerminalBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); + ASSERT(term.atom.quantityType == QuantifierGreedy); + ASSERT(term.atom.quantityCount == quantifyInfinite); + ASSERT(!term.capture()); + + BackTrackInfoParenthesesTerminal* backTrack = reinterpret_cast(context->frame + term.frameLocation); + backTrack->begin = input.getPos(); + return true; + } + + bool matchParenthesesTerminalEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalEnd); + + BackTrackInfoParenthesesTerminal* backTrack = reinterpret_cast(context->frame + term.frameLocation); + // Empty match is a failed match. + if (backTrack->begin == input.getPos()) + return false; + + // Successful match! Okay, what's next? - loop around and try to match moar! + context->term -= (term.atom.parenthesesWidth + 1); + return true; + } + + bool backtrackParenthesesTerminalBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); + ASSERT(term.atom.quantityType == QuantifierGreedy); + ASSERT(term.atom.quantityCount == quantifyInfinite); + ASSERT(!term.capture()); + + // If we backtrack to this point, we have failed to match this iteration of the parens. + // Since this is greedy / zero minimum a failed is also accepted as a match! + context->term += term.atom.parenthesesWidth; + return true; + } + + bool backtrackParenthesesTerminalEnd(ByteTerm&, DisjunctionContext*) + { + // 'Terminal' parentheses are at the end of the regex, and as such a match past end + // should always be returned as a successful match - we should never backtrack to here. + ASSERT_NOT_REACHED(); + return false; + } + + bool matchParentheticalAssertionBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParentheticalAssertionBegin); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + backTrack->begin = input.getPos(); + return true; + } + + bool matchParentheticalAssertionEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParentheticalAssertionEnd); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + input.setPos(backTrack->begin); + + // We've reached the end of the parens; if they are inverted, this is failure. + if (term.invert()) { + context->term -= term.atom.parenthesesWidth; + return false; + } + + return true; + } + + bool backtrackParentheticalAssertionBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParentheticalAssertionBegin); + ASSERT(term.atom.quantityCount == 1); + + // We've failed to match parens; if they are inverted, this is win! + if (term.invert()) { + context->term += term.atom.parenthesesWidth; + return true; + } + + return false; + } + + bool backtrackParentheticalAssertionEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParentheticalAssertionEnd); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + input.setPos(backTrack->begin); + + context->term -= term.atom.parenthesesWidth; + return false; + } + + JSRegExpResult matchParentheses(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpattern); + + BackTrackInfoParentheses* backTrack = reinterpret_cast(context->frame + term.frameLocation); + ByteDisjunction* disjunctionBody = term.atom.parenthesesDisjunction; + + backTrack->matchAmount = 0; + backTrack->lastContext = 0; + + switch (term.atom.quantityType) { + case QuantifierFixedCount: { + // While we haven't yet reached our fixed limit, + while (backTrack->matchAmount < term.atom.quantityCount) { + // Try to do a match, and it it succeeds, add it to the list. + ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + JSRegExpResult result = matchDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + if (result == JSRegExpMatch) + appendParenthesesDisjunctionContext(backTrack, context); + else { + // The match failed; try to find an alternate point to carry on from. + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack); + if (backtrackResult != JSRegExpMatch) + return backtrackResult; + } + } + + ASSERT(backTrack->matchAmount == term.atom.quantityCount); + ParenthesesDisjunctionContext* context = backTrack->lastContext; + recordParenthesesMatch(term, context); + return JSRegExpMatch; + } + + case QuantifierGreedy: { + while (backTrack->matchAmount < term.atom.quantityCount) { + ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + if (result == JSRegExpMatch) + appendParenthesesDisjunctionContext(backTrack, context); + else { + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + + break; + } + } + + if (backTrack->matchAmount) { + ParenthesesDisjunctionContext* context = backTrack->lastContext; + recordParenthesesMatch(term, context); + } + return JSRegExpMatch; + } + + case QuantifierNonGreedy: + return JSRegExpMatch; + } + + ASSERT_NOT_REACHED(); + return JSRegExpErrorNoMatch; + } + + // Rules for backtracking differ depending on whether this is greedy or non-greedy. + // + // Greedy matches never should try just adding more - you should already have done + // the 'more' cases. Always backtrack, at least a leetle bit. However cases where + // you backtrack an item off the list needs checking, since we'll never have matched + // the one less case. Tracking forwards, still add as much as possible. + // + // Non-greedy, we've already done the one less case, so don't match on popping. + // We haven't done the one more case, so always try to add that. + // + JSRegExpResult backtrackParentheses(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpattern); + + BackTrackInfoParentheses* backTrack = reinterpret_cast(context->frame + term.frameLocation); + ByteDisjunction* disjunctionBody = term.atom.parenthesesDisjunction; + + switch (term.atom.quantityType) { + case QuantifierFixedCount: { + ASSERT(backTrack->matchAmount == term.atom.quantityCount); + + ParenthesesDisjunctionContext* context = 0; + JSRegExpResult result = parenthesesDoBacktrack(term, backTrack); + + if (result != JSRegExpMatch) + return result; + + // While we haven't yet reached our fixed limit, + while (backTrack->matchAmount < term.atom.quantityCount) { + // Try to do a match, and it it succeeds, add it to the list. + context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + result = matchDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + + if (result == JSRegExpMatch) + appendParenthesesDisjunctionContext(backTrack, context); + else { + // The match failed; try to find an alternate point to carry on from. + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack); + if (backtrackResult != JSRegExpMatch) + return backtrackResult; + } + } + + ASSERT(backTrack->matchAmount == term.atom.quantityCount); + context = backTrack->lastContext; + recordParenthesesMatch(term, context); + return JSRegExpMatch; + } + + case QuantifierGreedy: { + if (!backTrack->matchAmount) + return JSRegExpNoMatch; + + ParenthesesDisjunctionContext* context = backTrack->lastContext; + JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term), true); + if (result == JSRegExpMatch) { + while (backTrack->matchAmount < term.atom.quantityCount) { + ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + JSRegExpResult parenthesesResult = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + if (parenthesesResult == JSRegExpMatch) + appendParenthesesDisjunctionContext(backTrack, context); + else { + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (parenthesesResult != JSRegExpNoMatch) + return parenthesesResult; + + break; + } + } + } else { + resetMatches(term, context); + popParenthesesDisjunctionContext(backTrack); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + } + + if (backTrack->matchAmount) { + ParenthesesDisjunctionContext* context = backTrack->lastContext; + recordParenthesesMatch(term, context); + } + return JSRegExpMatch; + } + + case QuantifierNonGreedy: { + // If we've not reached the limit, try to add one more match. + if (backTrack->matchAmount < term.atom.quantityCount) { + ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + if (result == JSRegExpMatch) { + appendParenthesesDisjunctionContext(backTrack, context); + recordParenthesesMatch(term, context); + return JSRegExpMatch; + } + + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + } + + // Nope - okay backtrack looking for an alternative. + while (backTrack->matchAmount) { + ParenthesesDisjunctionContext* context = backTrack->lastContext; + JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term), true); + if (result == JSRegExpMatch) { + // successful backtrack! we're back in the game! + if (backTrack->matchAmount) { + context = backTrack->lastContext; + recordParenthesesMatch(term, context); + } + return JSRegExpMatch; + } + + // pop a match off the stack + resetMatches(term, context); + popParenthesesDisjunctionContext(backTrack); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + } + + return JSRegExpNoMatch; + } + } + + ASSERT_NOT_REACHED(); + return JSRegExpErrorNoMatch; + } + + bool matchDotStarEnclosure(ByteTerm& term, DisjunctionContext* context) + { + UNUSED_PARAM(term); + unsigned matchBegin = context->matchBegin; + + if (matchBegin) { + for (matchBegin--; true; matchBegin--) { + if (testCharacterClass(pattern->newlineCharacterClass, input.reread(matchBegin))) { + ++matchBegin; + break; + } + + if (!matchBegin) + break; + } + } + + unsigned matchEnd = input.getPos(); + + for (; (matchEnd != input.end()) + && (!testCharacterClass(pattern->newlineCharacterClass, input.reread(matchEnd))); matchEnd++) { } + + if (((matchBegin && term.anchors.m_bol) + || ((matchEnd != input.end()) && term.anchors.m_eol)) + && !pattern->m_multiline) + return false; + + context->matchBegin = matchBegin; + context->matchEnd = matchEnd; + return true; + } + +#define MATCH_NEXT() { ++context->term; goto matchAgain; } +#define BACKTRACK() { --context->term; goto backtrack; } +#define currentTerm() (disjunction->terms[context->term]) + JSRegExpResult matchDisjunction(ByteDisjunction* disjunction, DisjunctionContext* context, bool btrack = false) + { + if (!--remainingMatchCount) + return JSRegExpErrorHitLimit; + + if (btrack) + BACKTRACK(); + + context->matchBegin = input.getPos(); + context->term = 0; + + matchAgain: + ASSERT(context->term < static_cast(disjunction->terms.size())); + + switch (currentTerm().type) { + case ByteTerm::TypeSubpatternBegin: + MATCH_NEXT(); + case ByteTerm::TypeSubpatternEnd: + context->matchEnd = input.getPos(); + return JSRegExpMatch; + + case ByteTerm::TypeBodyAlternativeBegin: + MATCH_NEXT(); + case ByteTerm::TypeBodyAlternativeDisjunction: + case ByteTerm::TypeBodyAlternativeEnd: + context->matchEnd = input.getPos(); + return JSRegExpMatch; + + case ByteTerm::TypeAlternativeBegin: + MATCH_NEXT(); + case ByteTerm::TypeAlternativeDisjunction: + case ByteTerm::TypeAlternativeEnd: { + int offset = currentTerm().alternative.end; + BackTrackInfoAlternative* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + backTrack->offset = offset; + context->term += offset; + MATCH_NEXT(); + } + + case ByteTerm::TypeAssertionBOL: + if (matchAssertionBOL(currentTerm())) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeAssertionEOL: + if (matchAssertionEOL(currentTerm())) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeAssertionWordBoundary: + if (matchAssertionWordBoundary(currentTerm())) + MATCH_NEXT(); + BACKTRACK(); + + case ByteTerm::TypePatternCharacterOnce: + case ByteTerm::TypePatternCharacterFixed: { + for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityCount; ++matchAmount) { + if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition - matchAmount)) + BACKTRACK(); + } + MATCH_NEXT(); + } + case ByteTerm::TypePatternCharacterGreedy: { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + unsigned matchAmount = 0; + while ((matchAmount < currentTerm().atom.quantityCount) && input.checkInput(1)) { + if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition + 1)) { + input.uncheckInput(1); + break; + } + ++matchAmount; + } + backTrack->matchAmount = matchAmount; + + MATCH_NEXT(); + } + case ByteTerm::TypePatternCharacterNonGreedy: { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + backTrack->matchAmount = 0; + MATCH_NEXT(); + } + + case ByteTerm::TypePatternCasedCharacterOnce: + case ByteTerm::TypePatternCasedCharacterFixed: { + for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityCount; ++matchAmount) { + if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition - matchAmount)) + BACKTRACK(); + } + MATCH_NEXT(); + } + case ByteTerm::TypePatternCasedCharacterGreedy: { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + unsigned matchAmount = 0; + while ((matchAmount < currentTerm().atom.quantityCount) && input.checkInput(1)) { + if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition + 1)) { + input.uncheckInput(1); + break; + } + ++matchAmount; + } + backTrack->matchAmount = matchAmount; + + MATCH_NEXT(); + } + case ByteTerm::TypePatternCasedCharacterNonGreedy: { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + backTrack->matchAmount = 0; + MATCH_NEXT(); + } + + case ByteTerm::TypeCharacterClass: + if (matchCharacterClass(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeBackReference: + if (matchBackReference(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpattern: { + JSRegExpResult result = matchParentheses(currentTerm(), context); + + if (result == JSRegExpMatch) { + MATCH_NEXT(); + } else if (result != JSRegExpNoMatch) + return result; + + BACKTRACK(); + } + case ByteTerm::TypeParenthesesSubpatternOnceBegin: + if (matchParenthesesOnceBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternOnceEnd: + if (matchParenthesesOnceEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternTerminalBegin: + if (matchParenthesesTerminalBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternTerminalEnd: + if (matchParenthesesTerminalEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParentheticalAssertionBegin: + if (matchParentheticalAssertionBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParentheticalAssertionEnd: + if (matchParentheticalAssertionEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + + case ByteTerm::TypeCheckInput: + if (input.checkInput(currentTerm().checkInputCount)) + MATCH_NEXT(); + BACKTRACK(); + + case ByteTerm::TypeUncheckInput: + input.uncheckInput(currentTerm().checkInputCount); + MATCH_NEXT(); + + case ByteTerm::TypeDotStarEnclosure: + if (matchDotStarEnclosure(currentTerm(), context)) + return JSRegExpMatch; + BACKTRACK(); + } + + // We should never fall-through to here. + ASSERT_NOT_REACHED(); + + backtrack: + ASSERT(context->term < static_cast(disjunction->terms.size())); + + switch (currentTerm().type) { + case ByteTerm::TypeSubpatternBegin: + return JSRegExpNoMatch; + case ByteTerm::TypeSubpatternEnd: + ASSERT_NOT_REACHED(); + + case ByteTerm::TypeBodyAlternativeBegin: + case ByteTerm::TypeBodyAlternativeDisjunction: { + int offset = currentTerm().alternative.next; + context->term += offset; + if (offset > 0) + MATCH_NEXT(); + + if (input.atEnd()) + return JSRegExpNoMatch; + + input.next(); + + context->matchBegin = input.getPos(); + + if (currentTerm().alternative.onceThrough) + context->term += currentTerm().alternative.next; + + MATCH_NEXT(); + } + case ByteTerm::TypeBodyAlternativeEnd: + ASSERT_NOT_REACHED(); + + case ByteTerm::TypeAlternativeBegin: + case ByteTerm::TypeAlternativeDisjunction: { + int offset = currentTerm().alternative.next; + context->term += offset; + if (offset > 0) + MATCH_NEXT(); + BACKTRACK(); + } + case ByteTerm::TypeAlternativeEnd: { + // We should never backtrack back into an alternative of the main body of the regex. + BackTrackInfoAlternative* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + unsigned offset = backTrack->offset; + context->term -= offset; + BACKTRACK(); + } + + case ByteTerm::TypeAssertionBOL: + case ByteTerm::TypeAssertionEOL: + case ByteTerm::TypeAssertionWordBoundary: + BACKTRACK(); + + case ByteTerm::TypePatternCharacterOnce: + case ByteTerm::TypePatternCharacterFixed: + case ByteTerm::TypePatternCharacterGreedy: + case ByteTerm::TypePatternCharacterNonGreedy: + if (backtrackPatternCharacter(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypePatternCasedCharacterOnce: + case ByteTerm::TypePatternCasedCharacterFixed: + case ByteTerm::TypePatternCasedCharacterGreedy: + case ByteTerm::TypePatternCasedCharacterNonGreedy: + if (backtrackPatternCasedCharacter(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeCharacterClass: + if (backtrackCharacterClass(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeBackReference: + if (backtrackBackReference(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpattern: { + JSRegExpResult result = backtrackParentheses(currentTerm(), context); + + if (result == JSRegExpMatch) { + MATCH_NEXT(); + } else if (result != JSRegExpNoMatch) + return result; + + BACKTRACK(); + } + case ByteTerm::TypeParenthesesSubpatternOnceBegin: + if (backtrackParenthesesOnceBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternOnceEnd: + if (backtrackParenthesesOnceEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternTerminalBegin: + if (backtrackParenthesesTerminalBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternTerminalEnd: + if (backtrackParenthesesTerminalEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParentheticalAssertionBegin: + if (backtrackParentheticalAssertionBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParentheticalAssertionEnd: + if (backtrackParentheticalAssertionEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + + case ByteTerm::TypeCheckInput: + input.uncheckInput(currentTerm().checkInputCount); + BACKTRACK(); + + case ByteTerm::TypeUncheckInput: + input.checkInput(currentTerm().checkInputCount); + BACKTRACK(); + + case ByteTerm::TypeDotStarEnclosure: + ASSERT_NOT_REACHED(); + } + + ASSERT_NOT_REACHED(); + return JSRegExpErrorNoMatch; + } + + JSRegExpResult matchNonZeroDisjunction(ByteDisjunction* disjunction, DisjunctionContext* context, bool btrack = false) + { + JSRegExpResult result = matchDisjunction(disjunction, context, btrack); + + if (result == JSRegExpMatch) { + while (context->matchBegin == context->matchEnd) { + result = matchDisjunction(disjunction, context, true); + if (result != JSRegExpMatch) + return result; + } + return JSRegExpMatch; + } + + return result; + } + + unsigned interpret() + { + if (!input.isAvailableInput(0)) + return offsetNoMatch; + + for (unsigned i = 0; i < pattern->m_body->m_numSubpatterns + 1; ++i) + output[i << 1] = offsetNoMatch; + + allocatorPool = pattern->m_allocator->startAllocator(); + if (!allocatorPool) + CRASH(); + + DisjunctionContext* context = allocDisjunctionContext(pattern->m_body.get()); + + JSRegExpResult result = matchDisjunction(pattern->m_body.get(), context, false); + if (result == JSRegExpMatch) { + output[0] = context->matchBegin; + output[1] = context->matchEnd; + } + + freeDisjunctionContext(context); + + pattern->m_allocator->stopAllocator(); + + ASSERT((result == JSRegExpMatch) == (output[0] != offsetNoMatch)); + return output[0]; + } + + Interpreter(BytecodePattern* pattern, unsigned* output, const CharType* input, unsigned length, unsigned start) + : pattern(pattern) + , output(output) + , input(input, start, length) + , allocatorPool(0) + , remainingMatchCount(matchLimit) + { + } + +private: + BytecodePattern* pattern; + unsigned* output; + InputStream input; + BumpPointerPool* allocatorPool; + unsigned remainingMatchCount; +}; + + + +class ByteCompiler { + struct ParenthesesStackEntry { + unsigned beginTerm; + unsigned savedAlternativeIndex; + ParenthesesStackEntry(unsigned beginTerm, unsigned savedAlternativeIndex/*, unsigned subpatternId, bool capture = false*/) + : beginTerm(beginTerm) + , savedAlternativeIndex(savedAlternativeIndex) + { + } + }; + +public: + ByteCompiler(YarrPattern& pattern) + : m_pattern(pattern) + { + m_currentAlternativeIndex = 0; + } + + PassOwnPtr compile(BumpPointerAllocator* allocator) + { + regexBegin(m_pattern.m_numSubpatterns, m_pattern.m_body->m_callFrameSize, m_pattern.m_body->m_alternatives[0]->onceThrough()); + emitDisjunction(m_pattern.m_body); + regexEnd(); + + return adoptPtr(new BytecodePattern(m_bodyDisjunction.release(), m_allParenthesesInfo, m_pattern, allocator)); + } + + void checkInput(unsigned count) + { + m_bodyDisjunction->terms.append(ByteTerm::CheckInput(count)); + } + + void uncheckInput(unsigned count) + { + m_bodyDisjunction->terms.append(ByteTerm::UncheckInput(count)); + } + + void assertionBOL(unsigned inputPosition) + { + m_bodyDisjunction->terms.append(ByteTerm::BOL(inputPosition)); + } + + void assertionEOL(unsigned inputPosition) + { + m_bodyDisjunction->terms.append(ByteTerm::EOL(inputPosition)); + } + + void assertionWordBoundary(bool invert, unsigned inputPosition) + { + m_bodyDisjunction->terms.append(ByteTerm::WordBoundary(invert, inputPosition)); + } + + void atomPatternCharacter(UChar ch, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + if (m_pattern.m_ignoreCase) { + UChar lo = Unicode::toLower(ch); + UChar hi = Unicode::toUpper(ch); + + if (lo != hi) { + m_bodyDisjunction->terms.append(ByteTerm(lo, hi, inputPosition, frameLocation, quantityCount, quantityType)); + return; + } + } + + m_bodyDisjunction->terms.append(ByteTerm(ch, inputPosition, frameLocation, quantityCount, quantityType)); + } + + void atomCharacterClass(CharacterClass* characterClass, bool invert, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + m_bodyDisjunction->terms.append(ByteTerm(characterClass, invert, inputPosition)); + + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType; + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + } + + void atomBackReference(unsigned subpatternId, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + ASSERT(subpatternId); + + m_bodyDisjunction->terms.append(ByteTerm::BackReference(subpatternId, inputPosition)); + + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType; + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + } + + void atomParenthesesOnceBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) + { + int beginTerm = m_bodyDisjunction->terms.size(); + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceBegin, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; + + m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); + m_currentAlternativeIndex = beginTerm + 1; + } + + void atomParenthesesTerminalBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) + { + int beginTerm = m_bodyDisjunction->terms.size(); + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternTerminalBegin, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; + + m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); + m_currentAlternativeIndex = beginTerm + 1; + } + + void atomParenthesesSubpatternBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) + { + // Errrk! - this is a little crazy, we initially generate as a TypeParenthesesSubpatternOnceBegin, + // then fix this up at the end! - simplifying this should make it much clearer. + // https://bugs.webkit.org/show_bug.cgi?id=50136 + + int beginTerm = m_bodyDisjunction->terms.size(); + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceBegin, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; + + m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); + m_currentAlternativeIndex = beginTerm + 1; + } + + void atomParentheticalAssertionBegin(unsigned subpatternId, bool invert, unsigned frameLocation, unsigned alternativeFrameLocation) + { + int beginTerm = m_bodyDisjunction->terms.size(); + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParentheticalAssertionBegin, subpatternId, false, invert, 0)); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; + + m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); + m_currentAlternativeIndex = beginTerm + 1; + } + + void atomParentheticalAssertionEnd(unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + unsigned beginTerm = popParenthesesStack(); + closeAlternative(beginTerm + 1); + unsigned endTerm = m_bodyDisjunction->terms.size(); + + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParentheticalAssertionBegin); + + bool invert = m_bodyDisjunction->terms[beginTerm].invert(); + unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParentheticalAssertionEnd, subpatternId, false, invert, inputPosition)); + m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; + + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; + m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; + } + + void assertionDotStarEnclosure(bool bolAnchored, bool eolAnchored) + { + m_bodyDisjunction->terms.append(ByteTerm::DotStarEnclosure(bolAnchored, eolAnchored)); + } + + unsigned popParenthesesStack() + { + ASSERT(m_parenthesesStack.size()); + int stackEnd = m_parenthesesStack.size() - 1; + unsigned beginTerm = m_parenthesesStack[stackEnd].beginTerm; + m_currentAlternativeIndex = m_parenthesesStack[stackEnd].savedAlternativeIndex; + m_parenthesesStack.shrink(stackEnd); + + ASSERT(beginTerm < m_bodyDisjunction->terms.size()); + ASSERT(m_currentAlternativeIndex < m_bodyDisjunction->terms.size()); + + return beginTerm; + } + +#ifndef NDEBUG + void dumpDisjunction(ByteDisjunction* disjunction) + { + dataLogF("ByteDisjunction(%p):\n\t", disjunction); + for (unsigned i = 0; i < disjunction->terms.size(); ++i) + dataLogF("{ %d } ", disjunction->terms[i].type); + dataLogF("\n"); + } +#endif + + void closeAlternative(int beginTerm) + { + int origBeginTerm = beginTerm; + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeAlternativeBegin); + int endIndex = m_bodyDisjunction->terms.size(); + + unsigned frameLocation = m_bodyDisjunction->terms[beginTerm].frameLocation; + + if (!m_bodyDisjunction->terms[beginTerm].alternative.next) + m_bodyDisjunction->terms.remove(beginTerm); + else { + while (m_bodyDisjunction->terms[beginTerm].alternative.next) { + beginTerm += m_bodyDisjunction->terms[beginTerm].alternative.next; + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeAlternativeDisjunction); + m_bodyDisjunction->terms[beginTerm].alternative.end = endIndex - beginTerm; + m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; + } + + m_bodyDisjunction->terms[beginTerm].alternative.next = origBeginTerm - beginTerm; + + m_bodyDisjunction->terms.append(ByteTerm::AlternativeEnd()); + m_bodyDisjunction->terms[endIndex].frameLocation = frameLocation; + } + } + + void closeBodyAlternative() + { + int beginTerm = 0; + int origBeginTerm = 0; + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeBodyAlternativeBegin); + int endIndex = m_bodyDisjunction->terms.size(); + + unsigned frameLocation = m_bodyDisjunction->terms[beginTerm].frameLocation; + + while (m_bodyDisjunction->terms[beginTerm].alternative.next) { + beginTerm += m_bodyDisjunction->terms[beginTerm].alternative.next; + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeBodyAlternativeDisjunction); + m_bodyDisjunction->terms[beginTerm].alternative.end = endIndex - beginTerm; + m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; + } + + m_bodyDisjunction->terms[beginTerm].alternative.next = origBeginTerm - beginTerm; + + m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeEnd()); + m_bodyDisjunction->terms[endIndex].frameLocation = frameLocation; + } + + void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType, unsigned callFrameSize = 0) + { + unsigned beginTerm = popParenthesesStack(); + closeAlternative(beginTerm + 1); + unsigned endTerm = m_bodyDisjunction->terms.size(); + + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + + ByteTerm& parenthesesBegin = m_bodyDisjunction->terms[beginTerm]; + + bool capture = parenthesesBegin.capture(); + unsigned subpatternId = parenthesesBegin.atom.subpatternId; + + unsigned numSubpatterns = lastSubpatternId - subpatternId + 1; + ByteDisjunction* parenthesesDisjunction = new ByteDisjunction(numSubpatterns, callFrameSize); + + parenthesesDisjunction->terms.append(ByteTerm::SubpatternBegin()); + for (unsigned termInParentheses = beginTerm + 1; termInParentheses < endTerm; ++termInParentheses) + parenthesesDisjunction->terms.append(m_bodyDisjunction->terms[termInParentheses]); + parenthesesDisjunction->terms.append(ByteTerm::SubpatternEnd()); + + m_bodyDisjunction->terms.shrink(beginTerm); + + m_allParenthesesInfo.append(parenthesesDisjunction); + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture, inputPosition)); + + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; + m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; + } + + void atomParenthesesOnceEnd(int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + unsigned beginTerm = popParenthesesStack(); + closeAlternative(beginTerm + 1); + unsigned endTerm = m_bodyDisjunction->terms.size(); + + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + + bool capture = m_bodyDisjunction->terms[beginTerm].capture(); + unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceEnd, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; + + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; + m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; + } + + void atomParenthesesTerminalEnd(int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + unsigned beginTerm = popParenthesesStack(); + closeAlternative(beginTerm + 1); + unsigned endTerm = m_bodyDisjunction->terms.size(); + + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); + + bool capture = m_bodyDisjunction->terms[beginTerm].capture(); + unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternTerminalEnd, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; + + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; + m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; + } + + void regexBegin(unsigned numSubpatterns, unsigned callFrameSize, bool onceThrough) + { + m_bodyDisjunction = adoptPtr(new ByteDisjunction(numSubpatterns, callFrameSize)); + m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeBegin(onceThrough)); + m_bodyDisjunction->terms[0].frameLocation = 0; + m_currentAlternativeIndex = 0; + } + + void regexEnd() + { + closeBodyAlternative(); + } + + void alternativeBodyDisjunction(bool onceThrough) + { + int newAlternativeIndex = m_bodyDisjunction->terms.size(); + m_bodyDisjunction->terms[m_currentAlternativeIndex].alternative.next = newAlternativeIndex - m_currentAlternativeIndex; + m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeDisjunction(onceThrough)); + + m_currentAlternativeIndex = newAlternativeIndex; + } + + void alternativeDisjunction() + { + int newAlternativeIndex = m_bodyDisjunction->terms.size(); + m_bodyDisjunction->terms[m_currentAlternativeIndex].alternative.next = newAlternativeIndex - m_currentAlternativeIndex; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeDisjunction()); + + m_currentAlternativeIndex = newAlternativeIndex; + } + + void emitDisjunction(PatternDisjunction* disjunction, unsigned inputCountAlreadyChecked = 0, unsigned parenthesesInputCountAlreadyChecked = 0) + { + for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { + unsigned currentCountAlreadyChecked = inputCountAlreadyChecked; + + PatternAlternative* alternative = disjunction->m_alternatives[alt]; + + if (alt) { + if (disjunction == m_pattern.m_body) + alternativeBodyDisjunction(alternative->onceThrough()); + else + alternativeDisjunction(); + } + + unsigned minimumSize = alternative->m_minimumSize; + ASSERT(minimumSize >= parenthesesInputCountAlreadyChecked); + unsigned countToCheck = minimumSize - parenthesesInputCountAlreadyChecked; + + if (countToCheck) { + checkInput(countToCheck); + currentCountAlreadyChecked += countToCheck; + } + + for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { + PatternTerm& term = alternative->m_terms[i]; + + switch (term.type) { + case PatternTerm::TypeAssertionBOL: + assertionBOL(currentCountAlreadyChecked - term.inputPosition); + break; + + case PatternTerm::TypeAssertionEOL: + assertionEOL(currentCountAlreadyChecked - term.inputPosition); + break; + + case PatternTerm::TypeAssertionWordBoundary: + assertionWordBoundary(term.invert(), currentCountAlreadyChecked - term.inputPosition); + break; + + case PatternTerm::TypePatternCharacter: + atomPatternCharacter(term.patternCharacter, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); + break; + + case PatternTerm::TypeCharacterClass: + atomCharacterClass(term.characterClass, term.invert(), currentCountAlreadyChecked- term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); + break; + + case PatternTerm::TypeBackReference: + atomBackReference(term.backReferenceSubpatternId, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); + break; + + case PatternTerm::TypeForwardReference: + break; + + case PatternTerm::TypeParenthesesSubpattern: { + unsigned disjunctionAlreadyCheckedCount = 0; + if (term.quantityCount == 1 && !term.parentheses.isCopy) { + unsigned alternativeFrameLocation = term.frameLocation; + // For QuantifierFixedCount we pre-check the minimum size; for greedy/non-greedy we reserve a slot in the frame. + if (term.quantityType == QuantifierFixedCount) + disjunctionAlreadyCheckedCount = term.parentheses.disjunction->m_minimumSize; + else + alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; + unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; + atomParenthesesOnceBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, alternativeFrameLocation); + emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount); + atomParenthesesOnceEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType); + } else if (term.parentheses.isTerminal) { + unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; + atomParenthesesTerminalBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, term.frameLocation + YarrStackSpaceForBackTrackInfoParenthesesOnce); + emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount); + atomParenthesesTerminalEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType); + } else { + unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; + atomParenthesesSubpatternBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, 0); + emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, 0); + atomParenthesesSubpatternEnd(term.parentheses.lastSubpatternId, delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType, term.parentheses.disjunction->m_callFrameSize); + } + break; + } + + case PatternTerm::TypeParentheticalAssertion: { + unsigned alternativeFrameLocation = term.frameLocation + YarrStackSpaceForBackTrackInfoParentheticalAssertion; + + ASSERT(currentCountAlreadyChecked >= static_cast(term.inputPosition)); + unsigned positiveInputOffset = currentCountAlreadyChecked - static_cast(term.inputPosition); + unsigned uncheckAmount = 0; + if (positiveInputOffset > term.parentheses.disjunction->m_minimumSize) { + uncheckAmount = positiveInputOffset - term.parentheses.disjunction->m_minimumSize; + uncheckInput(uncheckAmount); + currentCountAlreadyChecked -= uncheckAmount; + } + + atomParentheticalAssertionBegin(term.parentheses.subpatternId, term.invert(), term.frameLocation, alternativeFrameLocation); + emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, positiveInputOffset - uncheckAmount); + atomParentheticalAssertionEnd(0, term.frameLocation, term.quantityCount, term.quantityType); + if (uncheckAmount) { + checkInput(uncheckAmount); + currentCountAlreadyChecked += uncheckAmount; + } + break; + } + + case PatternTerm::TypeDotStarEnclosure: + assertionDotStarEnclosure(term.anchors.bolAnchor, term.anchors.eolAnchor); + break; + } + } + } + } + +private: + YarrPattern& m_pattern; + OwnPtr m_bodyDisjunction; + unsigned m_currentAlternativeIndex; + Vector m_parenthesesStack; + Vector m_allParenthesesInfo; +}; + +PassOwnPtr byteCompile(YarrPattern& pattern, BumpPointerAllocator* allocator) +{ + return ByteCompiler(pattern).compile(allocator); +} + +unsigned interpret(BytecodePattern* bytecode, const String& input, unsigned start, unsigned* output) +{ + if (input.is8Bit()) + return Interpreter(bytecode, output, input.characters8(), input.length(), start).interpret(); + return Interpreter(bytecode, output, input.characters16(), input.length(), start).interpret(); +} + +unsigned interpret(BytecodePattern* bytecode, const LChar* input, unsigned length, unsigned start, unsigned* output) +{ + return Interpreter(bytecode, output, input, length, start).interpret(); +} + +unsigned interpret(BytecodePattern* bytecode, const UChar* input, unsigned length, unsigned start, unsigned* output) +{ + return Interpreter(bytecode, output, input, length, start).interpret(); +} + +// These should be the same for both UChar & LChar. +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoPatternCharacter) == (YarrStackSpaceForBackTrackInfoPatternCharacter * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoPatternCharacter); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoCharacterClass) == (YarrStackSpaceForBackTrackInfoCharacterClass * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoCharacterClass); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoBackReference) == (YarrStackSpaceForBackTrackInfoBackReference * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoBackReference); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoAlternative) == (YarrStackSpaceForBackTrackInfoAlternative * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoAlternative); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParentheticalAssertion) == (YarrStackSpaceForBackTrackInfoParentheticalAssertion * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheticalAssertion); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParenthesesOnce) == (YarrStackSpaceForBackTrackInfoParenthesesOnce * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParenthesesOnce); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParentheses) == (YarrStackSpaceForBackTrackInfoParentheses * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheses); + + +} } diff --git a/3rdparty/masm/yarr/YarrInterpreter.h b/3rdparty/masm/yarr/YarrInterpreter.h new file mode 100644 index 0000000000..fb60bd979d --- /dev/null +++ b/3rdparty/masm/yarr/YarrInterpreter.h @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrInterpreter_h +#define YarrInterpreter_h + +#include "YarrPattern.h" +#include +#include + +namespace WTF { +class BumpPointerAllocator; +} +using WTF::BumpPointerAllocator; + +namespace JSC { namespace Yarr { + +class ByteDisjunction; + +struct ByteTerm { + enum Type { + TypeBodyAlternativeBegin, + TypeBodyAlternativeDisjunction, + TypeBodyAlternativeEnd, + TypeAlternativeBegin, + TypeAlternativeDisjunction, + TypeAlternativeEnd, + TypeSubpatternBegin, + TypeSubpatternEnd, + TypeAssertionBOL, + TypeAssertionEOL, + TypeAssertionWordBoundary, + TypePatternCharacterOnce, + TypePatternCharacterFixed, + TypePatternCharacterGreedy, + TypePatternCharacterNonGreedy, + TypePatternCasedCharacterOnce, + TypePatternCasedCharacterFixed, + TypePatternCasedCharacterGreedy, + TypePatternCasedCharacterNonGreedy, + TypeCharacterClass, + TypeBackReference, + TypeParenthesesSubpattern, + TypeParenthesesSubpatternOnceBegin, + TypeParenthesesSubpatternOnceEnd, + TypeParenthesesSubpatternTerminalBegin, + TypeParenthesesSubpatternTerminalEnd, + TypeParentheticalAssertionBegin, + TypeParentheticalAssertionEnd, + TypeCheckInput, + TypeUncheckInput, + TypeDotStarEnclosure, + } type; + union { + struct { + union { + UChar patternCharacter; + struct { + UChar lo; + UChar hi; + } casedCharacter; + CharacterClass* characterClass; + unsigned subpatternId; + }; + union { + ByteDisjunction* parenthesesDisjunction; + unsigned parenthesesWidth; + }; + QuantifierType quantityType; + unsigned quantityCount; + } atom; + struct { + int next; + int end; + bool onceThrough; + } alternative; + struct { + bool m_bol : 1; + bool m_eol : 1; + } anchors; + unsigned checkInputCount; + }; + unsigned frameLocation; + bool m_capture : 1; + bool m_invert : 1; + unsigned inputPosition; + + ByteTerm(UChar ch, int inputPos, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + : frameLocation(frameLocation) + , m_capture(false) + , m_invert(false) + { + switch (quantityType) { + case QuantifierFixedCount: + type = (quantityCount == 1) ? ByteTerm::TypePatternCharacterOnce : ByteTerm::TypePatternCharacterFixed; + break; + case QuantifierGreedy: + type = ByteTerm::TypePatternCharacterGreedy; + break; + case QuantifierNonGreedy: + type = ByteTerm::TypePatternCharacterNonGreedy; + break; + } + + atom.patternCharacter = ch; + atom.quantityType = quantityType; + atom.quantityCount = quantityCount.unsafeGet(); + inputPosition = inputPos; + } + + ByteTerm(UChar lo, UChar hi, int inputPos, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + : frameLocation(frameLocation) + , m_capture(false) + , m_invert(false) + { + switch (quantityType) { + case QuantifierFixedCount: + type = (quantityCount == 1) ? ByteTerm::TypePatternCasedCharacterOnce : ByteTerm::TypePatternCasedCharacterFixed; + break; + case QuantifierGreedy: + type = ByteTerm::TypePatternCasedCharacterGreedy; + break; + case QuantifierNonGreedy: + type = ByteTerm::TypePatternCasedCharacterNonGreedy; + break; + } + + atom.casedCharacter.lo = lo; + atom.casedCharacter.hi = hi; + atom.quantityType = quantityType; + atom.quantityCount = quantityCount.unsafeGet(); + inputPosition = inputPos; + } + + ByteTerm(CharacterClass* characterClass, bool invert, int inputPos) + : type(ByteTerm::TypeCharacterClass) + , m_capture(false) + , m_invert(invert) + { + atom.characterClass = characterClass; + atom.quantityType = QuantifierFixedCount; + atom.quantityCount = 1; + inputPosition = inputPos; + } + + ByteTerm(Type type, unsigned subpatternId, ByteDisjunction* parenthesesInfo, bool capture, int inputPos) + : type(type) + , m_capture(capture) + , m_invert(false) + { + atom.subpatternId = subpatternId; + atom.parenthesesDisjunction = parenthesesInfo; + atom.quantityType = QuantifierFixedCount; + atom.quantityCount = 1; + inputPosition = inputPos; + } + + ByteTerm(Type type, bool invert = false) + : type(type) + , m_capture(false) + , m_invert(invert) + { + atom.quantityType = QuantifierFixedCount; + atom.quantityCount = 1; + } + + ByteTerm(Type type, unsigned subpatternId, bool capture, bool invert, int inputPos) + : type(type) + , m_capture(capture) + , m_invert(invert) + { + atom.subpatternId = subpatternId; + atom.quantityType = QuantifierFixedCount; + atom.quantityCount = 1; + inputPosition = inputPos; + } + + static ByteTerm BOL(int inputPos) + { + ByteTerm term(TypeAssertionBOL); + term.inputPosition = inputPos; + return term; + } + + static ByteTerm CheckInput(Checked count) + { + ByteTerm term(TypeCheckInput); + term.checkInputCount = count.unsafeGet(); + return term; + } + + static ByteTerm UncheckInput(Checked count) + { + ByteTerm term(TypeUncheckInput); + term.checkInputCount = count.unsafeGet(); + return term; + } + + static ByteTerm EOL(int inputPos) + { + ByteTerm term(TypeAssertionEOL); + term.inputPosition = inputPos; + return term; + } + + static ByteTerm WordBoundary(bool invert, int inputPos) + { + ByteTerm term(TypeAssertionWordBoundary, invert); + term.inputPosition = inputPos; + return term; + } + + static ByteTerm BackReference(unsigned subpatternId, int inputPos) + { + return ByteTerm(TypeBackReference, subpatternId, false, false, inputPos); + } + + static ByteTerm BodyAlternativeBegin(bool onceThrough) + { + ByteTerm term(TypeBodyAlternativeBegin); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = onceThrough; + return term; + } + + static ByteTerm BodyAlternativeDisjunction(bool onceThrough) + { + ByteTerm term(TypeBodyAlternativeDisjunction); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = onceThrough; + return term; + } + + static ByteTerm BodyAlternativeEnd() + { + ByteTerm term(TypeBodyAlternativeEnd); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = false; + return term; + } + + static ByteTerm AlternativeBegin() + { + ByteTerm term(TypeAlternativeBegin); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = false; + return term; + } + + static ByteTerm AlternativeDisjunction() + { + ByteTerm term(TypeAlternativeDisjunction); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = false; + return term; + } + + static ByteTerm AlternativeEnd() + { + ByteTerm term(TypeAlternativeEnd); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = false; + return term; + } + + static ByteTerm SubpatternBegin() + { + return ByteTerm(TypeSubpatternBegin); + } + + static ByteTerm SubpatternEnd() + { + return ByteTerm(TypeSubpatternEnd); + } + + static ByteTerm DotStarEnclosure(bool bolAnchor, bool eolAnchor) + { + ByteTerm term(TypeDotStarEnclosure); + term.anchors.m_bol = bolAnchor; + term.anchors.m_eol = eolAnchor; + return term; + } + + bool invert() + { + return m_invert; + } + + bool capture() + { + return m_capture; + } +}; + +class ByteDisjunction { + WTF_MAKE_FAST_ALLOCATED; +public: + ByteDisjunction(unsigned numSubpatterns, unsigned frameSize) + : m_numSubpatterns(numSubpatterns) + , m_frameSize(frameSize) + { + } + + Vector terms; + unsigned m_numSubpatterns; + unsigned m_frameSize; +}; + +struct BytecodePattern { + WTF_MAKE_FAST_ALLOCATED; +public: + BytecodePattern(PassOwnPtr body, Vector allParenthesesInfo, YarrPattern& pattern, BumpPointerAllocator* allocator) + : m_body(body) + , m_ignoreCase(pattern.m_ignoreCase) + , m_multiline(pattern.m_multiline) + , m_allocator(allocator) + { + newlineCharacterClass = pattern.newlineCharacterClass(); + wordcharCharacterClass = pattern.wordcharCharacterClass(); + + m_allParenthesesInfo.append(allParenthesesInfo); + m_userCharacterClasses.append(pattern.m_userCharacterClasses); + // 'Steal' the YarrPattern's CharacterClasses! We clear its + // array, so that it won't delete them on destruction. We'll + // take responsibility for that. + pattern.m_userCharacterClasses.clear(); + } + + ~BytecodePattern() + { + deleteAllValues(m_allParenthesesInfo); + deleteAllValues(m_userCharacterClasses); + } + + OwnPtr m_body; + bool m_ignoreCase; + bool m_multiline; + // Each BytecodePattern is associated with a RegExp, each RegExp is associated + // with a JSGlobalData. Cache a pointer to out JSGlobalData's m_regExpAllocator. + BumpPointerAllocator* m_allocator; + + CharacterClass* newlineCharacterClass; + CharacterClass* wordcharCharacterClass; + +private: + Vector m_allParenthesesInfo; + Vector m_userCharacterClasses; +}; + +JS_EXPORT_PRIVATE PassOwnPtr byteCompile(YarrPattern&, BumpPointerAllocator*); +JS_EXPORT_PRIVATE unsigned interpret(BytecodePattern*, const String& input, unsigned start, unsigned* output); +unsigned interpret(BytecodePattern*, const LChar* input, unsigned length, unsigned start, unsigned* output); +unsigned interpret(BytecodePattern*, const UChar* input, unsigned length, unsigned start, unsigned* output); + +} } // namespace JSC::Yarr + +#endif // YarrInterpreter_h diff --git a/3rdparty/masm/yarr/YarrJIT.cpp b/3rdparty/masm/yarr/YarrJIT.cpp new file mode 100644 index 0000000000..ce84e2c74f --- /dev/null +++ b/3rdparty/masm/yarr/YarrJIT.cpp @@ -0,0 +1,2667 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "YarrJIT.h" + +#include +#include "LinkBuffer.h" +#include "Options.h" +#include "Yarr.h" +#include "YarrCanonicalizeUCS2.h" + +#if ENABLE(YARR_JIT) + +using namespace WTF; + +namespace JSC { namespace Yarr { + +template +class YarrGenerator : private MacroAssembler { + friend void jitCompile(JSGlobalData*, YarrCodeBlock& jitObject, const String& pattern, unsigned& numSubpatterns, const char*& error, bool ignoreCase, bool multiline); + +#if CPU(ARM) + static const RegisterID input = ARMRegisters::r0; + static const RegisterID index = ARMRegisters::r1; + static const RegisterID length = ARMRegisters::r2; + static const RegisterID output = ARMRegisters::r4; + + static const RegisterID regT0 = ARMRegisters::r5; + static const RegisterID regT1 = ARMRegisters::r6; + + static const RegisterID returnRegister = ARMRegisters::r0; + static const RegisterID returnRegister2 = ARMRegisters::r1; +#elif CPU(MIPS) + static const RegisterID input = MIPSRegisters::a0; + static const RegisterID index = MIPSRegisters::a1; + static const RegisterID length = MIPSRegisters::a2; + static const RegisterID output = MIPSRegisters::a3; + + static const RegisterID regT0 = MIPSRegisters::t4; + static const RegisterID regT1 = MIPSRegisters::t5; + + static const RegisterID returnRegister = MIPSRegisters::v0; + static const RegisterID returnRegister2 = MIPSRegisters::v1; +#elif CPU(SH4) + static const RegisterID input = SH4Registers::r4; + static const RegisterID index = SH4Registers::r5; + static const RegisterID length = SH4Registers::r6; + static const RegisterID output = SH4Registers::r7; + + static const RegisterID regT0 = SH4Registers::r0; + static const RegisterID regT1 = SH4Registers::r1; + + static const RegisterID returnRegister = SH4Registers::r0; + static const RegisterID returnRegister2 = SH4Registers::r1; +#elif CPU(X86) + static const RegisterID input = X86Registers::eax; + static const RegisterID index = X86Registers::edx; + static const RegisterID length = X86Registers::ecx; + static const RegisterID output = X86Registers::edi; + + static const RegisterID regT0 = X86Registers::ebx; + static const RegisterID regT1 = X86Registers::esi; + + static const RegisterID returnRegister = X86Registers::eax; + static const RegisterID returnRegister2 = X86Registers::edx; +#elif CPU(X86_64) + static const RegisterID input = X86Registers::edi; + static const RegisterID index = X86Registers::esi; + static const RegisterID length = X86Registers::edx; + static const RegisterID output = X86Registers::ecx; + + static const RegisterID regT0 = X86Registers::eax; + static const RegisterID regT1 = X86Registers::ebx; + + static const RegisterID returnRegister = X86Registers::eax; + static const RegisterID returnRegister2 = X86Registers::edx; +#endif + + void optimizeAlternative(PatternAlternative* alternative) + { + if (!alternative->m_terms.size()) + return; + + for (unsigned i = 0; i < alternative->m_terms.size() - 1; ++i) { + PatternTerm& term = alternative->m_terms[i]; + PatternTerm& nextTerm = alternative->m_terms[i + 1]; + + if ((term.type == PatternTerm::TypeCharacterClass) + && (term.quantityType == QuantifierFixedCount) + && (nextTerm.type == PatternTerm::TypePatternCharacter) + && (nextTerm.quantityType == QuantifierFixedCount)) { + PatternTerm termCopy = term; + alternative->m_terms[i] = nextTerm; + alternative->m_terms[i + 1] = termCopy; + } + } + } + + void matchCharacterClassRange(RegisterID character, JumpList& failures, JumpList& matchDest, const CharacterRange* ranges, unsigned count, unsigned* matchIndex, const UChar* matches, unsigned matchCount) + { + do { + // pick which range we're going to generate + int which = count >> 1; + char lo = ranges[which].begin; + char hi = ranges[which].end; + + // check if there are any ranges or matches below lo. If not, just jl to failure - + // if there is anything else to check, check that first, if it falls through jmp to failure. + if ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) { + Jump loOrAbove = branch32(GreaterThanOrEqual, character, Imm32((unsigned short)lo)); + + // generate code for all ranges before this one + if (which) + matchCharacterClassRange(character, failures, matchDest, ranges, which, matchIndex, matches, matchCount); + + while ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) { + matchDest.append(branch32(Equal, character, Imm32((unsigned short)matches[*matchIndex]))); + ++*matchIndex; + } + failures.append(jump()); + + loOrAbove.link(this); + } else if (which) { + Jump loOrAbove = branch32(GreaterThanOrEqual, character, Imm32((unsigned short)lo)); + + matchCharacterClassRange(character, failures, matchDest, ranges, which, matchIndex, matches, matchCount); + failures.append(jump()); + + loOrAbove.link(this); + } else + failures.append(branch32(LessThan, character, Imm32((unsigned short)lo))); + + while ((*matchIndex < matchCount) && (matches[*matchIndex] <= hi)) + ++*matchIndex; + + matchDest.append(branch32(LessThanOrEqual, character, Imm32((unsigned short)hi))); + // fall through to here, the value is above hi. + + // shuffle along & loop around if there are any more matches to handle. + unsigned next = which + 1; + ranges += next; + count -= next; + } while (count); + } + + void matchCharacterClass(RegisterID character, JumpList& matchDest, const CharacterClass* charClass) + { + if (charClass->m_table) { + ExtendedAddress tableEntry(character, reinterpret_cast(charClass->m_table->m_table)); + matchDest.append(branchTest8(charClass->m_table->m_inverted ? Zero : NonZero, tableEntry)); + return; + } + Jump unicodeFail; + if (charClass->m_matchesUnicode.size() || charClass->m_rangesUnicode.size()) { + Jump isAscii = branch32(LessThanOrEqual, character, TrustedImm32(0x7f)); + + if (charClass->m_matchesUnicode.size()) { + for (unsigned i = 0; i < charClass->m_matchesUnicode.size(); ++i) { + UChar ch = charClass->m_matchesUnicode[i]; + matchDest.append(branch32(Equal, character, Imm32(ch))); + } + } + + if (charClass->m_rangesUnicode.size()) { + for (unsigned i = 0; i < charClass->m_rangesUnicode.size(); ++i) { + UChar lo = charClass->m_rangesUnicode[i].begin; + UChar hi = charClass->m_rangesUnicode[i].end; + + Jump below = branch32(LessThan, character, Imm32(lo)); + matchDest.append(branch32(LessThanOrEqual, character, Imm32(hi))); + below.link(this); + } + } + + unicodeFail = jump(); + isAscii.link(this); + } + + if (charClass->m_ranges.size()) { + unsigned matchIndex = 0; + JumpList failures; + matchCharacterClassRange(character, failures, matchDest, charClass->m_ranges.begin(), charClass->m_ranges.size(), &matchIndex, charClass->m_matches.begin(), charClass->m_matches.size()); + while (matchIndex < charClass->m_matches.size()) + matchDest.append(branch32(Equal, character, Imm32((unsigned short)charClass->m_matches[matchIndex++]))); + + failures.link(this); + } else if (charClass->m_matches.size()) { + // optimization: gather 'a','A' etc back together, can mask & test once. + Vector matchesAZaz; + + for (unsigned i = 0; i < charClass->m_matches.size(); ++i) { + char ch = charClass->m_matches[i]; + if (m_pattern.m_ignoreCase) { + if (isASCIILower(ch)) { + matchesAZaz.append(ch); + continue; + } + if (isASCIIUpper(ch)) + continue; + } + matchDest.append(branch32(Equal, character, Imm32((unsigned short)ch))); + } + + if (unsigned countAZaz = matchesAZaz.size()) { + or32(TrustedImm32(32), character); + for (unsigned i = 0; i < countAZaz; ++i) + matchDest.append(branch32(Equal, character, TrustedImm32(matchesAZaz[i]))); + } + } + + if (charClass->m_matchesUnicode.size() || charClass->m_rangesUnicode.size()) + unicodeFail.link(this); + } + + // Jumps if input not available; will have (incorrectly) incremented already! + Jump jumpIfNoAvailableInput(unsigned countToCheck = 0) + { + if (countToCheck) + add32(Imm32(countToCheck), index); + return branch32(Above, index, length); + } + + Jump jumpIfAvailableInput(unsigned countToCheck) + { + add32(Imm32(countToCheck), index); + return branch32(BelowOrEqual, index, length); + } + + Jump checkInput() + { + return branch32(BelowOrEqual, index, length); + } + + Jump atEndOfInput() + { + return branch32(Equal, index, length); + } + + Jump notAtEndOfInput() + { + return branch32(NotEqual, index, length); + } + + Jump jumpIfCharNotEquals(UChar ch, int inputPosition, RegisterID character) + { + readCharacter(inputPosition, character); + + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { + or32(TrustedImm32(0x20), character); + ch |= 0x20; + } + + return branch32(NotEqual, character, Imm32(ch)); + } + + void readCharacter(int inputPosition, RegisterID reg) + { + if (m_charSize == Char8) + load8(BaseIndex(input, index, TimesOne, inputPosition * sizeof(char)), reg); + else + load16(BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), reg); + } + + void storeToFrame(RegisterID reg, unsigned frameLocation) + { + poke(reg, frameLocation); + } + + void storeToFrame(TrustedImm32 imm, unsigned frameLocation) + { + poke(imm, frameLocation); + } + + DataLabelPtr storeToFrameWithPatch(unsigned frameLocation) + { + return storePtrWithPatch(TrustedImmPtr(0), Address(stackPointerRegister, frameLocation * sizeof(void*))); + } + + void loadFromFrame(unsigned frameLocation, RegisterID reg) + { + peek(reg, frameLocation); + } + + void loadFromFrameAndJump(unsigned frameLocation) + { + jump(Address(stackPointerRegister, frameLocation * sizeof(void*))); + } + + void initCallFrame() + { + unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; + if (callFrameSize) + subPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); + } + void removeCallFrame() + { + unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; + if (callFrameSize) + addPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); + } + + // Used to record subpatters, should only be called if compileMode is IncludeSubpatterns. + void setSubpatternStart(RegisterID reg, unsigned subpattern) + { + ASSERT(subpattern); + // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( + store32(reg, Address(output, (subpattern << 1) * sizeof(int))); + } + void setSubpatternEnd(RegisterID reg, unsigned subpattern) + { + ASSERT(subpattern); + // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( + store32(reg, Address(output, ((subpattern << 1) + 1) * sizeof(int))); + } + void clearSubpatternStart(unsigned subpattern) + { + ASSERT(subpattern); + // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( + store32(TrustedImm32(-1), Address(output, (subpattern << 1) * sizeof(int))); + } + + // We use one of three different strategies to track the start of the current match, + // while matching. + // 1) If the pattern has a fixed size, do nothing! - we calculate the value lazily + // at the end of matching. This is irrespective of compileMode, and in this case + // these methods should never be called. + // 2) If we're compiling IncludeSubpatterns, 'output' contains a pointer to an output + // vector, store the match start in the output vector. + // 3) If we're compiling MatchOnly, 'output' is unused, store the match start directly + // in this register. + void setMatchStart(RegisterID reg) + { + ASSERT(!m_pattern.m_body->m_hasFixedSize); + if (compileMode == IncludeSubpatterns) + store32(reg, output); + else + move(reg, output); + } + void getMatchStart(RegisterID reg) + { + ASSERT(!m_pattern.m_body->m_hasFixedSize); + if (compileMode == IncludeSubpatterns) + load32(output, reg); + else + move(output, reg); + } + + enum YarrOpCode { + // These nodes wrap body alternatives - those in the main disjunction, + // rather than subpatterns or assertions. These are chained together in + // a doubly linked list, with a 'begin' node for the first alternative, + // a 'next' node for each subsequent alternative, and an 'end' node at + // the end. In the case of repeating alternatives, the 'end' node also + // has a reference back to 'begin'. + OpBodyAlternativeBegin, + OpBodyAlternativeNext, + OpBodyAlternativeEnd, + // Similar to the body alternatives, but used for subpatterns with two + // or more alternatives. + OpNestedAlternativeBegin, + OpNestedAlternativeNext, + OpNestedAlternativeEnd, + // Used for alternatives in subpatterns where there is only a single + // alternative (backtrackingis easier in these cases), or for alternatives + // which never need to be backtracked (those in parenthetical assertions, + // terminal subpatterns). + OpSimpleNestedAlternativeBegin, + OpSimpleNestedAlternativeNext, + OpSimpleNestedAlternativeEnd, + // Used to wrap 'Once' subpattern matches (quantityCount == 1). + OpParenthesesSubpatternOnceBegin, + OpParenthesesSubpatternOnceEnd, + // Used to wrap 'Terminal' subpattern matches (at the end of the regexp). + OpParenthesesSubpatternTerminalBegin, + OpParenthesesSubpatternTerminalEnd, + // Used to wrap parenthetical assertions. + OpParentheticalAssertionBegin, + OpParentheticalAssertionEnd, + // Wraps all simple terms (pattern characters, character classes). + OpTerm, + // Where an expression contains only 'once through' body alternatives + // and no repeating ones, this op is used to return match failure. + OpMatchFailed + }; + + // This structure is used to hold the compiled opcode information, + // including reference back to the original PatternTerm/PatternAlternatives, + // and JIT compilation data structures. + struct YarrOp { + explicit YarrOp(PatternTerm* term) + : m_op(OpTerm) + , m_term(term) + , m_isDeadCode(false) + { + } + + explicit YarrOp(YarrOpCode op) + : m_op(op) + , m_isDeadCode(false) + { + } + + // The operation, as a YarrOpCode, and also a reference to the PatternTerm. + YarrOpCode m_op; + PatternTerm* m_term; + + // For alternatives, this holds the PatternAlternative and doubly linked + // references to this alternative's siblings. In the case of the + // OpBodyAlternativeEnd node at the end of a section of repeating nodes, + // m_nextOp will reference the OpBodyAlternativeBegin node of the first + // repeating alternative. + PatternAlternative* m_alternative; + size_t m_previousOp; + size_t m_nextOp; + + // Used to record a set of Jumps out of the generated code, typically + // used for jumps out to backtracking code, and a single reentry back + // into the code for a node (likely where a backtrack will trigger + // rematching). + Label m_reentry; + JumpList m_jumps; + + // Used for backtracking when the prior alternative did not consume any + // characters but matched. + Jump m_zeroLengthMatch; + + // This flag is used to null out the second pattern character, when + // two are fused to match a pair together. + bool m_isDeadCode; + + // Currently used in the case of some of the more complex management of + // 'm_checked', to cache the offset used in this alternative, to avoid + // recalculating it. + int m_checkAdjust; + + // Used by OpNestedAlternativeNext/End to hold the pointer to the + // value that will be pushed into the pattern's frame to return to, + // upon backtracking back into the disjunction. + DataLabelPtr m_returnAddress; + }; + + // BacktrackingState + // This class encapsulates information about the state of code generation + // whilst generating the code for backtracking, when a term fails to match. + // Upon entry to code generation of the backtracking code for a given node, + // the Backtracking state will hold references to all control flow sources + // that are outputs in need of further backtracking from the prior node + // generated (which is the subsequent operation in the regular expression, + // and in the m_ops Vector, since we generated backtracking backwards). + // These references to control flow take the form of: + // - A jump list of jumps, to be linked to code that will backtrack them + // further. + // - A set of DataLabelPtr values, to be populated with values to be + // treated effectively as return addresses backtracking into complex + // subpatterns. + // - A flag indicating that the current sequence of generated code up to + // this point requires backtracking. + class BacktrackingState { + public: + BacktrackingState() + : m_pendingFallthrough(false) + { + } + + // Add a jump or jumps, a return address, or set the flag indicating + // that the current 'fallthrough' control flow requires backtracking. + void append(const Jump& jump) + { + m_laterFailures.append(jump); + } + void append(JumpList& jumpList) + { + m_laterFailures.append(jumpList); + } + void append(const DataLabelPtr& returnAddress) + { + m_pendingReturns.append(returnAddress); + } + void fallthrough() + { + ASSERT(!m_pendingFallthrough); + m_pendingFallthrough = true; + } + + // These methods clear the backtracking state, either linking to the + // current location, a provided label, or copying the backtracking out + // to a JumpList. All actions may require code generation to take place, + // and as such are passed a pointer to the assembler. + void link(MacroAssembler* assembler) + { + if (m_pendingReturns.size()) { + Label here(assembler); + for (unsigned i = 0; i < m_pendingReturns.size(); ++i) + m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], here)); + m_pendingReturns.clear(); + } + m_laterFailures.link(assembler); + m_laterFailures.clear(); + m_pendingFallthrough = false; + } + void linkTo(Label label, MacroAssembler* assembler) + { + if (m_pendingReturns.size()) { + for (unsigned i = 0; i < m_pendingReturns.size(); ++i) + m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], label)); + m_pendingReturns.clear(); + } + if (m_pendingFallthrough) + assembler->jump(label); + m_laterFailures.linkTo(label, assembler); + m_laterFailures.clear(); + m_pendingFallthrough = false; + } + void takeBacktracksToJumpList(JumpList& jumpList, MacroAssembler* assembler) + { + if (m_pendingReturns.size()) { + Label here(assembler); + for (unsigned i = 0; i < m_pendingReturns.size(); ++i) + m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], here)); + m_pendingReturns.clear(); + m_pendingFallthrough = true; + } + if (m_pendingFallthrough) + jumpList.append(assembler->jump()); + jumpList.append(m_laterFailures); + m_laterFailures.clear(); + m_pendingFallthrough = false; + } + + bool isEmpty() + { + return m_laterFailures.empty() && m_pendingReturns.isEmpty() && !m_pendingFallthrough; + } + + // Called at the end of code generation to link all return addresses. + void linkDataLabels(LinkBuffer& linkBuffer) + { + ASSERT(isEmpty()); + for (unsigned i = 0; i < m_backtrackRecords.size(); ++i) + linkBuffer.patch(m_backtrackRecords[i].m_dataLabel, linkBuffer.locationOf(m_backtrackRecords[i].m_backtrackLocation)); + } + + private: + struct ReturnAddressRecord { + ReturnAddressRecord(DataLabelPtr dataLabel, Label backtrackLocation) + : m_dataLabel(dataLabel) + , m_backtrackLocation(backtrackLocation) + { + } + + DataLabelPtr m_dataLabel; + Label m_backtrackLocation; + }; + + JumpList m_laterFailures; + bool m_pendingFallthrough; + Vector m_pendingReturns; + Vector m_backtrackRecords; + }; + + // Generation methods: + // =================== + + // This method provides a default implementation of backtracking common + // to many terms; terms commonly jump out of the forwards matching path + // on any failed conditions, and add these jumps to the m_jumps list. If + // no special handling is required we can often just backtrack to m_jumps. + void backtrackTermDefault(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + m_backtrackingState.append(op.m_jumps); + } + + void generateAssertionBOL(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + if (m_pattern.m_multiline) { + const RegisterID character = regT0; + + JumpList matchDest; + if (!term->inputPosition) + matchDest.append(branch32(Equal, index, Imm32(m_checked))); + + readCharacter((term->inputPosition - m_checked) - 1, character); + matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass()); + op.m_jumps.append(jump()); + + matchDest.link(this); + } else { + // Erk, really should poison out these alternatives early. :-/ + if (term->inputPosition) + op.m_jumps.append(jump()); + else + op.m_jumps.append(branch32(NotEqual, index, Imm32(m_checked))); + } + } + void backtrackAssertionBOL(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generateAssertionEOL(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + if (m_pattern.m_multiline) { + const RegisterID character = regT0; + + JumpList matchDest; + if (term->inputPosition == m_checked) + matchDest.append(atEndOfInput()); + + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass()); + op.m_jumps.append(jump()); + + matchDest.link(this); + } else { + if (term->inputPosition == m_checked) + op.m_jumps.append(notAtEndOfInput()); + // Erk, really should poison out these alternatives early. :-/ + else + op.m_jumps.append(jump()); + } + } + void backtrackAssertionEOL(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + // Also falls though on nextIsNotWordChar. + void matchAssertionWordchar(size_t opIndex, JumpList& nextIsWordChar, JumpList& nextIsNotWordChar) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + + if (term->inputPosition == m_checked) + nextIsNotWordChar.append(atEndOfInput()); + + readCharacter((term->inputPosition - m_checked), character); + matchCharacterClass(character, nextIsWordChar, m_pattern.wordcharCharacterClass()); + } + + void generateAssertionWordBoundary(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + + Jump atBegin; + JumpList matchDest; + if (!term->inputPosition) + atBegin = branch32(Equal, index, Imm32(m_checked)); + readCharacter((term->inputPosition - m_checked) - 1, character); + matchCharacterClass(character, matchDest, m_pattern.wordcharCharacterClass()); + if (!term->inputPosition) + atBegin.link(this); + + // We fall through to here if the last character was not a wordchar. + JumpList nonWordCharThenWordChar; + JumpList nonWordCharThenNonWordChar; + if (term->invert()) { + matchAssertionWordchar(opIndex, nonWordCharThenNonWordChar, nonWordCharThenWordChar); + nonWordCharThenWordChar.append(jump()); + } else { + matchAssertionWordchar(opIndex, nonWordCharThenWordChar, nonWordCharThenNonWordChar); + nonWordCharThenNonWordChar.append(jump()); + } + op.m_jumps.append(nonWordCharThenNonWordChar); + + // We jump here if the last character was a wordchar. + matchDest.link(this); + JumpList wordCharThenWordChar; + JumpList wordCharThenNonWordChar; + if (term->invert()) { + matchAssertionWordchar(opIndex, wordCharThenNonWordChar, wordCharThenWordChar); + wordCharThenWordChar.append(jump()); + } else { + matchAssertionWordchar(opIndex, wordCharThenWordChar, wordCharThenNonWordChar); + // This can fall-though! + } + + op.m_jumps.append(wordCharThenWordChar); + + nonWordCharThenWordChar.link(this); + wordCharThenNonWordChar.link(this); + } + void backtrackAssertionWordBoundary(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generatePatternCharacterOnce(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + + if (op.m_isDeadCode) + return; + + // m_ops always ends with a OpBodyAlternativeEnd or OpMatchFailed + // node, so there must always be at least one more node. + ASSERT(opIndex + 1 < m_ops.size()); + YarrOp* nextOp = &m_ops[opIndex + 1]; + + PatternTerm* term = op.m_term; + UChar ch = term->patternCharacter; + + if ((ch > 0xff) && (m_charSize == Char8)) { + // Have a 16 bit pattern character and an 8 bit string - short circuit + op.m_jumps.append(jump()); + return; + } + + const RegisterID character = regT0; + int maxCharactersAtOnce = m_charSize == Char8 ? 4 : 2; + unsigned ignoreCaseMask = 0; + int allCharacters = ch; + int numberCharacters; + int startTermPosition = term->inputPosition; + + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); + + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) + ignoreCaseMask |= 32; + + for (numberCharacters = 1; numberCharacters < maxCharactersAtOnce && nextOp->m_op == OpTerm; ++numberCharacters, nextOp = &m_ops[opIndex + numberCharacters]) { + PatternTerm* nextTerm = nextOp->m_term; + + if (nextTerm->type != PatternTerm::TypePatternCharacter + || nextTerm->quantityType != QuantifierFixedCount + || nextTerm->quantityCount != 1 + || nextTerm->inputPosition != (startTermPosition + numberCharacters)) + break; + + nextOp->m_isDeadCode = true; + + int shiftAmount = (m_charSize == Char8 ? 8 : 16) * numberCharacters; + + UChar currentCharacter = nextTerm->patternCharacter; + + if ((currentCharacter > 0xff) && (m_charSize == Char8)) { + // Have a 16 bit pattern character and an 8 bit string - short circuit + op.m_jumps.append(jump()); + return; + } + + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(currentCharacter) || isCanonicallyUnique(currentCharacter)); + + allCharacters |= (currentCharacter << shiftAmount); + + if ((m_pattern.m_ignoreCase) && (isASCIIAlpha(currentCharacter))) + ignoreCaseMask |= 32 << shiftAmount; + } + + if (m_charSize == Char8) { + switch (numberCharacters) { + case 1: + op.m_jumps.append(jumpIfCharNotEquals(ch, startTermPosition - m_checked, character)); + return; + case 2: { + BaseIndex address(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); + load16Unaligned(address, character); + break; + } + case 3: { + BaseIndex highAddress(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); + load16Unaligned(highAddress, character); + if (ignoreCaseMask) + or32(Imm32(ignoreCaseMask), character); + op.m_jumps.append(branch32(NotEqual, character, Imm32((allCharacters & 0xffff) | ignoreCaseMask))); + op.m_jumps.append(jumpIfCharNotEquals(allCharacters >> 16, startTermPosition + 2 - m_checked, character)); + return; + } + case 4: { + BaseIndex address(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); + load32WithUnalignedHalfWords(address, character); + break; + } + } + } else { + switch (numberCharacters) { + case 1: + op.m_jumps.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); + return; + case 2: + BaseIndex address(input, index, TimesTwo, (term->inputPosition - m_checked) * sizeof(UChar)); + load32WithUnalignedHalfWords(address, character); + break; + } + } + + if (ignoreCaseMask) + or32(Imm32(ignoreCaseMask), character); + op.m_jumps.append(branch32(NotEqual, character, Imm32(allCharacters | ignoreCaseMask))); + return; + } + void backtrackPatternCharacterOnce(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generatePatternCharacterFixed(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + UChar ch = term->patternCharacter; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + move(index, countRegister); + sub32(Imm32(term->quantityCount.unsafeGet()), countRegister); + + Label loop(this); + BaseIndex address(input, countRegister, m_charScale, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(m_charSize == Char8 ? sizeof(char) : sizeof(UChar))).unsafeGet()); + + if (m_charSize == Char8) + load8(address, character); + else + load16(address, character); + + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { + or32(TrustedImm32(0x20), character); + ch |= 0x20; + } + + op.m_jumps.append(branch32(NotEqual, character, Imm32(ch))); + add32(TrustedImm32(1), countRegister); + branch32(NotEqual, countRegister, index).linkTo(loop, this); + } + void backtrackPatternCharacterFixed(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generatePatternCharacterGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + UChar ch = term->patternCharacter; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + move(TrustedImm32(0), countRegister); + + // Unless have a 16 bit pattern character and an 8 bit string - short circuit + if (!((ch > 0xff) && (m_charSize == Char8))) { + JumpList failures; + Label loop(this); + failures.append(atEndOfInput()); + failures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); + + add32(TrustedImm32(1), countRegister); + add32(TrustedImm32(1), index); + if (term->quantityCount == quantifyInfinite) + jump(loop); + else + branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this); + + failures.link(this); + } + op.m_reentry = label(); + + storeToFrame(countRegister, term->frameLocation); + } + void backtrackPatternCharacterGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID countRegister = regT1; + + m_backtrackingState.link(this); + + loadFromFrame(term->frameLocation, countRegister); + m_backtrackingState.append(branchTest32(Zero, countRegister)); + sub32(TrustedImm32(1), countRegister); + sub32(TrustedImm32(1), index); + jump(op.m_reentry); + } + + void generatePatternCharacterNonGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID countRegister = regT1; + + move(TrustedImm32(0), countRegister); + op.m_reentry = label(); + storeToFrame(countRegister, term->frameLocation); + } + void backtrackPatternCharacterNonGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + UChar ch = term->patternCharacter; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + m_backtrackingState.link(this); + + loadFromFrame(term->frameLocation, countRegister); + + // Unless have a 16 bit pattern character and an 8 bit string - short circuit + if (!((ch > 0xff) && (m_charSize == Char8))) { + JumpList nonGreedyFailures; + nonGreedyFailures.append(atEndOfInput()); + if (term->quantityCount != quantifyInfinite) + nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet()))); + nonGreedyFailures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); + + add32(TrustedImm32(1), countRegister); + add32(TrustedImm32(1), index); + + jump(op.m_reentry); + nonGreedyFailures.link(this); + } + + sub32(countRegister, index); + m_backtrackingState.fallthrough(); + } + + void generateCharacterClassOnce(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + + JumpList matchDest; + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, matchDest, term->characterClass); + + if (term->invert()) + op.m_jumps.append(matchDest); + else { + op.m_jumps.append(jump()); + matchDest.link(this); + } + } + void backtrackCharacterClassOnce(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generateCharacterClassFixed(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + move(index, countRegister); + sub32(Imm32(term->quantityCount.unsafeGet()), countRegister); + + Label loop(this); + JumpList matchDest; + if (m_charSize == Char8) + load8(BaseIndex(input, countRegister, TimesOne, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(sizeof(char))).unsafeGet()), character); + else + load16(BaseIndex(input, countRegister, TimesTwo, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(sizeof(UChar))).unsafeGet()), character); + matchCharacterClass(character, matchDest, term->characterClass); + + if (term->invert()) + op.m_jumps.append(matchDest); + else { + op.m_jumps.append(jump()); + matchDest.link(this); + } + + add32(TrustedImm32(1), countRegister); + branch32(NotEqual, countRegister, index).linkTo(loop, this); + } + void backtrackCharacterClassFixed(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generateCharacterClassGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + move(TrustedImm32(0), countRegister); + + JumpList failures; + Label loop(this); + failures.append(atEndOfInput()); + + if (term->invert()) { + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, failures, term->characterClass); + } else { + JumpList matchDest; + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, matchDest, term->characterClass); + failures.append(jump()); + matchDest.link(this); + } + + add32(TrustedImm32(1), countRegister); + add32(TrustedImm32(1), index); + if (term->quantityCount != quantifyInfinite) { + branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this); + failures.append(jump()); + } else + jump(loop); + + failures.link(this); + op.m_reentry = label(); + + storeToFrame(countRegister, term->frameLocation); + } + void backtrackCharacterClassGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID countRegister = regT1; + + m_backtrackingState.link(this); + + loadFromFrame(term->frameLocation, countRegister); + m_backtrackingState.append(branchTest32(Zero, countRegister)); + sub32(TrustedImm32(1), countRegister); + sub32(TrustedImm32(1), index); + jump(op.m_reentry); + } + + void generateCharacterClassNonGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID countRegister = regT1; + + move(TrustedImm32(0), countRegister); + op.m_reentry = label(); + storeToFrame(countRegister, term->frameLocation); + } + void backtrackCharacterClassNonGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + JumpList nonGreedyFailures; + + m_backtrackingState.link(this); + + loadFromFrame(term->frameLocation, countRegister); + + nonGreedyFailures.append(atEndOfInput()); + nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet()))); + + JumpList matchDest; + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, matchDest, term->characterClass); + + if (term->invert()) + nonGreedyFailures.append(matchDest); + else { + nonGreedyFailures.append(jump()); + matchDest.link(this); + } + + add32(TrustedImm32(1), countRegister); + add32(TrustedImm32(1), index); + + jump(op.m_reentry); + + nonGreedyFailures.link(this); + sub32(countRegister, index); + m_backtrackingState.fallthrough(); + } + + void generateDotStarEnclosure(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + const RegisterID matchPos = regT1; + + JumpList foundBeginningNewLine; + JumpList saveStartIndex; + JumpList foundEndingNewLine; + + ASSERT(!m_pattern.m_body->m_hasFixedSize); + getMatchStart(matchPos); + + saveStartIndex.append(branchTest32(Zero, matchPos)); + Label findBOLLoop(this); + sub32(TrustedImm32(1), matchPos); + if (m_charSize == Char8) + load8(BaseIndex(input, matchPos, TimesOne, 0), character); + else + load16(BaseIndex(input, matchPos, TimesTwo, 0), character); + matchCharacterClass(character, foundBeginningNewLine, m_pattern.newlineCharacterClass()); + branchTest32(NonZero, matchPos).linkTo(findBOLLoop, this); + saveStartIndex.append(jump()); + + foundBeginningNewLine.link(this); + add32(TrustedImm32(1), matchPos); // Advance past newline + saveStartIndex.link(this); + + if (!m_pattern.m_multiline && term->anchors.bolAnchor) + op.m_jumps.append(branchTest32(NonZero, matchPos)); + + ASSERT(!m_pattern.m_body->m_hasFixedSize); + setMatchStart(matchPos); + + move(index, matchPos); + + Label findEOLLoop(this); + foundEndingNewLine.append(branch32(Equal, matchPos, length)); + if (m_charSize == Char8) + load8(BaseIndex(input, matchPos, TimesOne, 0), character); + else + load16(BaseIndex(input, matchPos, TimesTwo, 0), character); + matchCharacterClass(character, foundEndingNewLine, m_pattern.newlineCharacterClass()); + add32(TrustedImm32(1), matchPos); + jump(findEOLLoop); + + foundEndingNewLine.link(this); + + if (!m_pattern.m_multiline && term->anchors.eolAnchor) + op.m_jumps.append(branch32(NotEqual, matchPos, length)); + + move(matchPos, index); + } + + void backtrackDotStarEnclosure(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + // Code generation/backtracking for simple terms + // (pattern characters, character classes, and assertions). + // These methods farm out work to the set of functions above. + void generateTerm(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + switch (term->type) { + case PatternTerm::TypePatternCharacter: + switch (term->quantityType) { + case QuantifierFixedCount: + if (term->quantityCount == 1) + generatePatternCharacterOnce(opIndex); + else + generatePatternCharacterFixed(opIndex); + break; + case QuantifierGreedy: + generatePatternCharacterGreedy(opIndex); + break; + case QuantifierNonGreedy: + generatePatternCharacterNonGreedy(opIndex); + break; + } + break; + + case PatternTerm::TypeCharacterClass: + switch (term->quantityType) { + case QuantifierFixedCount: + if (term->quantityCount == 1) + generateCharacterClassOnce(opIndex); + else + generateCharacterClassFixed(opIndex); + break; + case QuantifierGreedy: + generateCharacterClassGreedy(opIndex); + break; + case QuantifierNonGreedy: + generateCharacterClassNonGreedy(opIndex); + break; + } + break; + + case PatternTerm::TypeAssertionBOL: + generateAssertionBOL(opIndex); + break; + + case PatternTerm::TypeAssertionEOL: + generateAssertionEOL(opIndex); + break; + + case PatternTerm::TypeAssertionWordBoundary: + generateAssertionWordBoundary(opIndex); + break; + + case PatternTerm::TypeForwardReference: + break; + + case PatternTerm::TypeParenthesesSubpattern: + case PatternTerm::TypeParentheticalAssertion: + ASSERT_NOT_REACHED(); + case PatternTerm::TypeBackReference: + m_shouldFallBack = true; + break; + case PatternTerm::TypeDotStarEnclosure: + generateDotStarEnclosure(opIndex); + break; + } + } + void backtrackTerm(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + switch (term->type) { + case PatternTerm::TypePatternCharacter: + switch (term->quantityType) { + case QuantifierFixedCount: + if (term->quantityCount == 1) + backtrackPatternCharacterOnce(opIndex); + else + backtrackPatternCharacterFixed(opIndex); + break; + case QuantifierGreedy: + backtrackPatternCharacterGreedy(opIndex); + break; + case QuantifierNonGreedy: + backtrackPatternCharacterNonGreedy(opIndex); + break; + } + break; + + case PatternTerm::TypeCharacterClass: + switch (term->quantityType) { + case QuantifierFixedCount: + if (term->quantityCount == 1) + backtrackCharacterClassOnce(opIndex); + else + backtrackCharacterClassFixed(opIndex); + break; + case QuantifierGreedy: + backtrackCharacterClassGreedy(opIndex); + break; + case QuantifierNonGreedy: + backtrackCharacterClassNonGreedy(opIndex); + break; + } + break; + + case PatternTerm::TypeAssertionBOL: + backtrackAssertionBOL(opIndex); + break; + + case PatternTerm::TypeAssertionEOL: + backtrackAssertionEOL(opIndex); + break; + + case PatternTerm::TypeAssertionWordBoundary: + backtrackAssertionWordBoundary(opIndex); + break; + + case PatternTerm::TypeForwardReference: + break; + + case PatternTerm::TypeParenthesesSubpattern: + case PatternTerm::TypeParentheticalAssertion: + ASSERT_NOT_REACHED(); + + case PatternTerm::TypeDotStarEnclosure: + backtrackDotStarEnclosure(opIndex); + break; + + case PatternTerm::TypeBackReference: + m_shouldFallBack = true; + break; + } + } + + void generate() + { + // Forwards generate the matching code. + ASSERT(m_ops.size()); + size_t opIndex = 0; + + do { + YarrOp& op = m_ops[opIndex]; + switch (op.m_op) { + + case OpTerm: + generateTerm(opIndex); + break; + + // OpBodyAlternativeBegin/Next/End + // + // These nodes wrap the set of alternatives in the body of the regular expression. + // There may be either one or two chains of OpBodyAlternative nodes, one representing + // the 'once through' sequence of alternatives (if any exist), and one representing + // the repeating alternatives (again, if any exist). + // + // Upon normal entry to the Begin alternative, we will check that input is available. + // Reentry to the Begin alternative will take place after the check has taken place, + // and will assume that the input position has already been progressed as appropriate. + // + // Entry to subsequent Next/End alternatives occurs when the prior alternative has + // successfully completed a match - return a success state from JIT code. + // + // Next alternatives allow for reentry optimized to suit backtracking from its + // preceding alternative. It expects the input position to still be set to a position + // appropriate to its predecessor, and it will only perform an input check if the + // predecessor had a minimum size less than its own. + // + // In the case 'once through' expressions, the End node will also have a reentry + // point to jump to when the last alternative fails. Again, this expects the input + // position to still reflect that expected by the prior alternative. + case OpBodyAlternativeBegin: { + PatternAlternative* alternative = op.m_alternative; + + // Upon entry at the head of the set of alternatives, check if input is available + // to run the first alternative. (This progresses the input position). + op.m_jumps.append(jumpIfNoAvailableInput(alternative->m_minimumSize)); + // We will reenter after the check, and assume the input position to have been + // set as appropriate to this alternative. + op.m_reentry = label(); + + m_checked += alternative->m_minimumSize; + break; + } + case OpBodyAlternativeNext: + case OpBodyAlternativeEnd: { + PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; + PatternAlternative* alternative = op.m_alternative; + + // If we get here, the prior alternative matched - return success. + + // Adjust the stack pointer to remove the pattern's frame. + removeCallFrame(); + + // Load appropriate values into the return register and the first output + // slot, and return. In the case of pattern with a fixed size, we will + // not have yet set the value in the first + ASSERT(index != returnRegister); + if (m_pattern.m_body->m_hasFixedSize) { + move(index, returnRegister); + if (priorAlternative->m_minimumSize) + sub32(Imm32(priorAlternative->m_minimumSize), returnRegister); + if (compileMode == IncludeSubpatterns) + store32(returnRegister, output); + } else + getMatchStart(returnRegister); + if (compileMode == IncludeSubpatterns) + store32(index, Address(output, 4)); + move(index, returnRegister2); + + generateReturn(); + + // This is the divide between the tail of the prior alternative, above, and + // the head of the subsequent alternative, below. + + if (op.m_op == OpBodyAlternativeNext) { + // This is the reentry point for the Next alternative. We expect any code + // that jumps here to do so with the input position matching that of the + // PRIOR alteranative, and we will only check input availability if we + // need to progress it forwards. + op.m_reentry = label(); + if (alternative->m_minimumSize > priorAlternative->m_minimumSize) { + add32(Imm32(alternative->m_minimumSize - priorAlternative->m_minimumSize), index); + op.m_jumps.append(jumpIfNoAvailableInput()); + } else if (priorAlternative->m_minimumSize > alternative->m_minimumSize) + sub32(Imm32(priorAlternative->m_minimumSize - alternative->m_minimumSize), index); + } else if (op.m_nextOp == notFound) { + // This is the reentry point for the End of 'once through' alternatives, + // jumped to when the last alternative fails to match. + op.m_reentry = label(); + sub32(Imm32(priorAlternative->m_minimumSize), index); + } + + if (op.m_op == OpBodyAlternativeNext) + m_checked += alternative->m_minimumSize; + m_checked -= priorAlternative->m_minimumSize; + break; + } + + // OpSimpleNestedAlternativeBegin/Next/End + // OpNestedAlternativeBegin/Next/End + // + // These nodes are used to handle sets of alternatives that are nested within + // subpatterns and parenthetical assertions. The 'simple' forms are used where + // we do not need to be able to backtrack back into any alternative other than + // the last, the normal forms allow backtracking into any alternative. + // + // Each Begin/Next node is responsible for planting an input check to ensure + // sufficient input is available on entry. Next nodes additionally need to + // jump to the end - Next nodes use the End node's m_jumps list to hold this + // set of jumps. + // + // In the non-simple forms, successful alternative matches must store a + // 'return address' using a DataLabelPtr, used to store the address to jump + // to when backtracking, to get to the code for the appropriate alternative. + case OpSimpleNestedAlternativeBegin: + case OpNestedAlternativeBegin: { + PatternTerm* term = op.m_term; + PatternAlternative* alternative = op.m_alternative; + PatternDisjunction* disjunction = term->parentheses.disjunction; + + // Calculate how much input we need to check for, and if non-zero check. + op.m_checkAdjust = alternative->m_minimumSize; + if ((term->quantityType == QuantifierFixedCount) && (term->type != PatternTerm::TypeParentheticalAssertion)) + op.m_checkAdjust -= disjunction->m_minimumSize; + if (op.m_checkAdjust) + op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust)); + + m_checked += op.m_checkAdjust; + break; + } + case OpSimpleNestedAlternativeNext: + case OpNestedAlternativeNext: { + PatternTerm* term = op.m_term; + PatternAlternative* alternative = op.m_alternative; + PatternDisjunction* disjunction = term->parentheses.disjunction; + + // In the non-simple case, store a 'return address' so we can backtrack correctly. + if (op.m_op == OpNestedAlternativeNext) { + unsigned parenthesesFrameLocation = term->frameLocation; + unsigned alternativeFrameLocation = parenthesesFrameLocation; + if (term->quantityType != QuantifierFixedCount) + alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; + op.m_returnAddress = storeToFrameWithPatch(alternativeFrameLocation); + } + + if (term->quantityType != QuantifierFixedCount && !m_ops[op.m_previousOp].m_alternative->m_minimumSize) { + // If the previous alternative matched without consuming characters then + // backtrack to try to match while consumming some input. + op.m_zeroLengthMatch = branch32(Equal, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + } + + // If we reach here then the last alternative has matched - jump to the + // End node, to skip over any further alternatives. + // + // FIXME: this is logically O(N^2) (though N can be expected to be very + // small). We could avoid this either by adding an extra jump to the JIT + // data structures, or by making backtracking code that jumps to Next + // alternatives are responsible for checking that input is available (if + // we didn't need to plant the input checks, then m_jumps would be free). + YarrOp* endOp = &m_ops[op.m_nextOp]; + while (endOp->m_nextOp != notFound) { + ASSERT(endOp->m_op == OpSimpleNestedAlternativeNext || endOp->m_op == OpNestedAlternativeNext); + endOp = &m_ops[endOp->m_nextOp]; + } + ASSERT(endOp->m_op == OpSimpleNestedAlternativeEnd || endOp->m_op == OpNestedAlternativeEnd); + endOp->m_jumps.append(jump()); + + // This is the entry point for the next alternative. + op.m_reentry = label(); + + // Calculate how much input we need to check for, and if non-zero check. + op.m_checkAdjust = alternative->m_minimumSize; + if ((term->quantityType == QuantifierFixedCount) && (term->type != PatternTerm::TypeParentheticalAssertion)) + op.m_checkAdjust -= disjunction->m_minimumSize; + if (op.m_checkAdjust) + op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust)); + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked -= lastOp.m_checkAdjust; + m_checked += op.m_checkAdjust; + break; + } + case OpSimpleNestedAlternativeEnd: + case OpNestedAlternativeEnd: { + PatternTerm* term = op.m_term; + + // In the non-simple case, store a 'return address' so we can backtrack correctly. + if (op.m_op == OpNestedAlternativeEnd) { + unsigned parenthesesFrameLocation = term->frameLocation; + unsigned alternativeFrameLocation = parenthesesFrameLocation; + if (term->quantityType != QuantifierFixedCount) + alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; + op.m_returnAddress = storeToFrameWithPatch(alternativeFrameLocation); + } + + if (term->quantityType != QuantifierFixedCount && !m_ops[op.m_previousOp].m_alternative->m_minimumSize) { + // If the previous alternative matched without consuming characters then + // backtrack to try to match while consumming some input. + op.m_zeroLengthMatch = branch32(Equal, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + } + + // If this set of alternatives contains more than one alternative, + // then the Next nodes will have planted jumps to the End, and added + // them to this node's m_jumps list. + op.m_jumps.link(this); + op.m_jumps.clear(); + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked -= lastOp.m_checkAdjust; + break; + } + + // OpParenthesesSubpatternOnceBegin/End + // + // These nodes support (optionally) capturing subpatterns, that have a + // quantity count of 1 (this covers fixed once, and ?/?? quantifiers). + case OpParenthesesSubpatternOnceBegin: { + PatternTerm* term = op.m_term; + unsigned parenthesesFrameLocation = term->frameLocation; + const RegisterID indexTemporary = regT0; + ASSERT(term->quantityCount == 1); + + // Upon entry to a Greedy quantified set of parenthese store the index. + // We'll use this for two purposes: + // - To indicate which iteration we are on of mathing the remainder of + // the expression after the parentheses - the first, including the + // match within the parentheses, or the second having skipped over them. + // - To check for empty matches, which must be rejected. + // + // At the head of a NonGreedy set of parentheses we'll immediately set the + // value on the stack to -1 (indicating a match skipping the subpattern), + // and plant a jump to the end. We'll also plant a label to backtrack to + // to reenter the subpattern later, with a store to set up index on the + // second iteration. + // + // FIXME: for capturing parens, could use the index in the capture array? + if (term->quantityType == QuantifierGreedy) + storeToFrame(index, parenthesesFrameLocation); + else if (term->quantityType == QuantifierNonGreedy) { + storeToFrame(TrustedImm32(-1), parenthesesFrameLocation); + op.m_jumps.append(jump()); + op.m_reentry = label(); + storeToFrame(index, parenthesesFrameLocation); + } + + // If the parenthese are capturing, store the starting index value to the + // captures array, offsetting as necessary. + // + // FIXME: could avoid offsetting this value in JIT code, apply + // offsets only afterwards, at the point the results array is + // being accessed. + if (term->capture() && compileMode == IncludeSubpatterns) { + int inputOffset = term->inputPosition - m_checked; + if (term->quantityType == QuantifierFixedCount) + inputOffset -= term->parentheses.disjunction->m_minimumSize; + if (inputOffset) { + move(index, indexTemporary); + add32(Imm32(inputOffset), indexTemporary); + setSubpatternStart(indexTemporary, term->parentheses.subpatternId); + } else + setSubpatternStart(index, term->parentheses.subpatternId); + } + break; + } + case OpParenthesesSubpatternOnceEnd: { + PatternTerm* term = op.m_term; + const RegisterID indexTemporary = regT0; + ASSERT(term->quantityCount == 1); + +#ifndef NDEBUG + // Runtime ASSERT to make sure that the nested alternative handled the + // "no input consumed" check. + if (term->quantityType != QuantifierFixedCount && !term->parentheses.disjunction->m_minimumSize) { + Jump pastBreakpoint; + pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + breakpoint(); + pastBreakpoint.link(this); + } +#endif + + // If the parenthese are capturing, store the ending index value to the + // captures array, offsetting as necessary. + // + // FIXME: could avoid offsetting this value in JIT code, apply + // offsets only afterwards, at the point the results array is + // being accessed. + if (term->capture() && compileMode == IncludeSubpatterns) { + int inputOffset = term->inputPosition - m_checked; + if (inputOffset) { + move(index, indexTemporary); + add32(Imm32(inputOffset), indexTemporary); + setSubpatternEnd(indexTemporary, term->parentheses.subpatternId); + } else + setSubpatternEnd(index, term->parentheses.subpatternId); + } + + // If the parentheses are quantified Greedy then add a label to jump back + // to if get a failed match from after the parentheses. For NonGreedy + // parentheses, link the jump from before the subpattern to here. + if (term->quantityType == QuantifierGreedy) + op.m_reentry = label(); + else if (term->quantityType == QuantifierNonGreedy) { + YarrOp& beginOp = m_ops[op.m_previousOp]; + beginOp.m_jumps.link(this); + } + break; + } + + // OpParenthesesSubpatternTerminalBegin/End + case OpParenthesesSubpatternTerminalBegin: { + PatternTerm* term = op.m_term; + ASSERT(term->quantityType == QuantifierGreedy); + ASSERT(term->quantityCount == quantifyInfinite); + ASSERT(!term->capture()); + + // Upon entry set a label to loop back to. + op.m_reentry = label(); + + // Store the start index of the current match; we need to reject zero + // length matches. + storeToFrame(index, term->frameLocation); + break; + } + case OpParenthesesSubpatternTerminalEnd: { + YarrOp& beginOp = m_ops[op.m_previousOp]; +#ifndef NDEBUG + PatternTerm* term = op.m_term; + + // Runtime ASSERT to make sure that the nested alternative handled the + // "no input consumed" check. + Jump pastBreakpoint; + pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + breakpoint(); + pastBreakpoint.link(this); +#endif + + // We know that the match is non-zero, we can accept it and + // loop back up to the head of the subpattern. + jump(beginOp.m_reentry); + + // This is the entry point to jump to when we stop matching - we will + // do so once the subpattern cannot match any more. + op.m_reentry = label(); + break; + } + + // OpParentheticalAssertionBegin/End + case OpParentheticalAssertionBegin: { + PatternTerm* term = op.m_term; + + // Store the current index - assertions should not update index, so + // we will need to restore it upon a successful match. + unsigned parenthesesFrameLocation = term->frameLocation; + storeToFrame(index, parenthesesFrameLocation); + + // Check + op.m_checkAdjust = m_checked - term->inputPosition; + if (op.m_checkAdjust) + sub32(Imm32(op.m_checkAdjust), index); + + m_checked -= op.m_checkAdjust; + break; + } + case OpParentheticalAssertionEnd: { + PatternTerm* term = op.m_term; + + // Restore the input index value. + unsigned parenthesesFrameLocation = term->frameLocation; + loadFromFrame(parenthesesFrameLocation, index); + + // If inverted, a successful match of the assertion must be treated + // as a failure, so jump to backtracking. + if (term->invert()) { + op.m_jumps.append(jump()); + op.m_reentry = label(); + } + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked += lastOp.m_checkAdjust; + break; + } + + case OpMatchFailed: + removeCallFrame(); + move(TrustedImmPtr((void*)WTF::notFound), returnRegister); + move(TrustedImm32(0), returnRegister2); + generateReturn(); + break; + } + + ++opIndex; + } while (opIndex < m_ops.size()); + } + + void backtrack() + { + // Backwards generate the backtracking code. + size_t opIndex = m_ops.size(); + ASSERT(opIndex); + + do { + --opIndex; + YarrOp& op = m_ops[opIndex]; + switch (op.m_op) { + + case OpTerm: + backtrackTerm(opIndex); + break; + + // OpBodyAlternativeBegin/Next/End + // + // For each Begin/Next node representing an alternative, we need to decide what to do + // in two circumstances: + // - If we backtrack back into this node, from within the alternative. + // - If the input check at the head of the alternative fails (if this exists). + // + // We treat these two cases differently since in the former case we have slightly + // more information - since we are backtracking out of a prior alternative we know + // that at least enough input was available to run it. For example, given the regular + // expression /a|b/, if we backtrack out of the first alternative (a failed pattern + // character match of 'a'), then we need not perform an additional input availability + // check before running the second alternative. + // + // Backtracking required differs for the last alternative, which in the case of the + // repeating set of alternatives must loop. The code generated for the last alternative + // will also be used to handle all input check failures from any prior alternatives - + // these require similar functionality, in seeking the next available alternative for + // which there is sufficient input. + // + // Since backtracking of all other alternatives simply requires us to link backtracks + // to the reentry point for the subsequent alternative, we will only be generating any + // code when backtracking the last alternative. + case OpBodyAlternativeBegin: + case OpBodyAlternativeNext: { + PatternAlternative* alternative = op.m_alternative; + + if (op.m_op == OpBodyAlternativeNext) { + PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; + m_checked += priorAlternative->m_minimumSize; + } + m_checked -= alternative->m_minimumSize; + + // Is this the last alternative? If not, then if we backtrack to this point we just + // need to jump to try to match the next alternative. + if (m_ops[op.m_nextOp].m_op != OpBodyAlternativeEnd) { + m_backtrackingState.linkTo(m_ops[op.m_nextOp].m_reentry, this); + break; + } + YarrOp& endOp = m_ops[op.m_nextOp]; + + YarrOp* beginOp = &op; + while (beginOp->m_op != OpBodyAlternativeBegin) { + ASSERT(beginOp->m_op == OpBodyAlternativeNext); + beginOp = &m_ops[beginOp->m_previousOp]; + } + + bool onceThrough = endOp.m_nextOp == notFound; + + // First, generate code to handle cases where we backtrack out of an attempted match + // of the last alternative. If this is a 'once through' set of alternatives then we + // have nothing to do - link this straight through to the End. + if (onceThrough) + m_backtrackingState.linkTo(endOp.m_reentry, this); + else { + // If we don't need to move the input poistion, and the pattern has a fixed size + // (in which case we omit the store of the start index until the pattern has matched) + // then we can just link the backtrack out of the last alternative straight to the + // head of the first alternative. + if (m_pattern.m_body->m_hasFixedSize + && (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize) + && (alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize == 1)) + m_backtrackingState.linkTo(beginOp->m_reentry, this); + else { + // We need to generate a trampoline of code to execute before looping back + // around to the first alternative. + m_backtrackingState.link(this); + + // If the pattern size is not fixed, then store the start index, for use if we match. + if (!m_pattern.m_body->m_hasFixedSize) { + if (alternative->m_minimumSize == 1) + setMatchStart(index); + else { + move(index, regT0); + if (alternative->m_minimumSize) + sub32(Imm32(alternative->m_minimumSize - 1), regT0); + else + add32(TrustedImm32(1), regT0); + setMatchStart(regT0); + } + } + + // Generate code to loop. Check whether the last alternative is longer than the + // first (e.g. /a|xy/ or /a|xyz/). + if (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize) { + // We want to loop, and increment input position. If the delta is 1, it is + // already correctly incremented, if more than one then decrement as appropriate. + unsigned delta = alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize; + ASSERT(delta); + if (delta != 1) + sub32(Imm32(delta - 1), index); + jump(beginOp->m_reentry); + } else { + // If the first alternative has minimum size 0xFFFFFFFFu, then there cannot + // be sufficent input available to handle this, so just fall through. + unsigned delta = beginOp->m_alternative->m_minimumSize - alternative->m_minimumSize; + if (delta != 0xFFFFFFFFu) { + // We need to check input because we are incrementing the input. + add32(Imm32(delta + 1), index); + checkInput().linkTo(beginOp->m_reentry, this); + } + } + } + } + + // We can reach this point in the code in two ways: + // - Fallthrough from the code above (a repeating alternative backtracked out of its + // last alternative, and did not have sufficent input to run the first). + // - We will loop back up to the following label when a releating alternative loops, + // following a failed input check. + // + // Either way, we have just failed the input check for the first alternative. + Label firstInputCheckFailed(this); + + // Generate code to handle input check failures from alternatives except the last. + // prevOp is the alternative we're handling a bail out from (initially Begin), and + // nextOp is the alternative we will be attempting to reenter into. + // + // We will link input check failures from the forwards matching path back to the code + // that can handle them. + YarrOp* prevOp = beginOp; + YarrOp* nextOp = &m_ops[beginOp->m_nextOp]; + while (nextOp->m_op != OpBodyAlternativeEnd) { + prevOp->m_jumps.link(this); + + // We only get here if an input check fails, it is only worth checking again + // if the next alternative has a minimum size less than the last. + if (prevOp->m_alternative->m_minimumSize > nextOp->m_alternative->m_minimumSize) { + // FIXME: if we added an extra label to YarrOp, we could avoid needing to + // subtract delta back out, and reduce this code. Should performance test + // the benefit of this. + unsigned delta = prevOp->m_alternative->m_minimumSize - nextOp->m_alternative->m_minimumSize; + sub32(Imm32(delta), index); + Jump fail = jumpIfNoAvailableInput(); + add32(Imm32(delta), index); + jump(nextOp->m_reentry); + fail.link(this); + } else if (prevOp->m_alternative->m_minimumSize < nextOp->m_alternative->m_minimumSize) + add32(Imm32(nextOp->m_alternative->m_minimumSize - prevOp->m_alternative->m_minimumSize), index); + prevOp = nextOp; + nextOp = &m_ops[nextOp->m_nextOp]; + } + + // We fall through to here if there is insufficient input to run the last alternative. + + // If there is insufficient input to run the last alternative, then for 'once through' + // alternatives we are done - just jump back up into the forwards matching path at the End. + if (onceThrough) { + op.m_jumps.linkTo(endOp.m_reentry, this); + jump(endOp.m_reentry); + break; + } + + // For repeating alternatives, link any input check failure from the last alternative to + // this point. + op.m_jumps.link(this); + + bool needsToUpdateMatchStart = !m_pattern.m_body->m_hasFixedSize; + + // Check for cases where input position is already incremented by 1 for the last + // alternative (this is particularly useful where the minimum size of the body + // disjunction is 0, e.g. /a*|b/). + if (needsToUpdateMatchStart && alternative->m_minimumSize == 1) { + // index is already incremented by 1, so just store it now! + setMatchStart(index); + needsToUpdateMatchStart = false; + } + + // Check whether there is sufficient input to loop. Increment the input position by + // one, and check. Also add in the minimum disjunction size before checking - there + // is no point in looping if we're just going to fail all the input checks around + // the next iteration. + ASSERT(alternative->m_minimumSize >= m_pattern.m_body->m_minimumSize); + if (alternative->m_minimumSize == m_pattern.m_body->m_minimumSize) { + // If the last alternative had the same minimum size as the disjunction, + // just simply increment input pos by 1, no adjustment based on minimum size. + add32(TrustedImm32(1), index); + } else { + // If the minumum for the last alternative was one greater than than that + // for the disjunction, we're already progressed by 1, nothing to do! + unsigned delta = (alternative->m_minimumSize - m_pattern.m_body->m_minimumSize) - 1; + if (delta) + sub32(Imm32(delta), index); + } + Jump matchFailed = jumpIfNoAvailableInput(); + + if (needsToUpdateMatchStart) { + if (!m_pattern.m_body->m_minimumSize) + setMatchStart(index); + else { + move(index, regT0); + sub32(Imm32(m_pattern.m_body->m_minimumSize), regT0); + setMatchStart(regT0); + } + } + + // Calculate how much more input the first alternative requires than the minimum + // for the body as a whole. If no more is needed then we dont need an additional + // input check here - jump straight back up to the start of the first alternative. + if (beginOp->m_alternative->m_minimumSize == m_pattern.m_body->m_minimumSize) + jump(beginOp->m_reentry); + else { + if (beginOp->m_alternative->m_minimumSize > m_pattern.m_body->m_minimumSize) + add32(Imm32(beginOp->m_alternative->m_minimumSize - m_pattern.m_body->m_minimumSize), index); + else + sub32(Imm32(m_pattern.m_body->m_minimumSize - beginOp->m_alternative->m_minimumSize), index); + checkInput().linkTo(beginOp->m_reentry, this); + jump(firstInputCheckFailed); + } + + // We jump to here if we iterate to the point that there is insufficient input to + // run any matches, and need to return a failure state from JIT code. + matchFailed.link(this); + + removeCallFrame(); + move(TrustedImmPtr((void*)WTF::notFound), returnRegister); + move(TrustedImm32(0), returnRegister2); + generateReturn(); + break; + } + case OpBodyAlternativeEnd: { + // We should never backtrack back into a body disjunction. + ASSERT(m_backtrackingState.isEmpty()); + + PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; + m_checked += priorAlternative->m_minimumSize; + break; + } + + // OpSimpleNestedAlternativeBegin/Next/End + // OpNestedAlternativeBegin/Next/End + // + // Generate code for when we backtrack back out of an alternative into + // a Begin or Next node, or when the entry input count check fails. If + // there are more alternatives we need to jump to the next alternative, + // if not we backtrack back out of the current set of parentheses. + // + // In the case of non-simple nested assertions we need to also link the + // 'return address' appropriately to backtrack back out into the correct + // alternative. + case OpSimpleNestedAlternativeBegin: + case OpSimpleNestedAlternativeNext: + case OpNestedAlternativeBegin: + case OpNestedAlternativeNext: { + YarrOp& nextOp = m_ops[op.m_nextOp]; + bool isBegin = op.m_previousOp == notFound; + bool isLastAlternative = nextOp.m_nextOp == notFound; + ASSERT(isBegin == (op.m_op == OpSimpleNestedAlternativeBegin || op.m_op == OpNestedAlternativeBegin)); + ASSERT(isLastAlternative == (nextOp.m_op == OpSimpleNestedAlternativeEnd || nextOp.m_op == OpNestedAlternativeEnd)); + + // Treat an input check failure the same as a failed match. + m_backtrackingState.append(op.m_jumps); + + // Set the backtracks to jump to the appropriate place. We may need + // to link the backtracks in one of three different way depending on + // the type of alternative we are dealing with: + // - A single alternative, with no simplings. + // - The last alternative of a set of two or more. + // - An alternative other than the last of a set of two or more. + // + // In the case of a single alternative on its own, we don't need to + // jump anywhere - if the alternative fails to match we can just + // continue to backtrack out of the parentheses without jumping. + // + // In the case of the last alternative in a set of more than one, we + // need to jump to return back out to the beginning. We'll do so by + // adding a jump to the End node's m_jumps list, and linking this + // when we come to generate the Begin node. For alternatives other + // than the last, we need to jump to the next alternative. + // + // If the alternative had adjusted the input position we must link + // backtracking to here, correct, and then jump on. If not we can + // link the backtracks directly to their destination. + if (op.m_checkAdjust) { + // Handle the cases where we need to link the backtracks here. + m_backtrackingState.link(this); + sub32(Imm32(op.m_checkAdjust), index); + if (!isLastAlternative) { + // An alternative that is not the last should jump to its successor. + jump(nextOp.m_reentry); + } else if (!isBegin) { + // The last of more than one alternatives must jump back to the beginning. + nextOp.m_jumps.append(jump()); + } else { + // A single alternative on its own can fall through. + m_backtrackingState.fallthrough(); + } + } else { + // Handle the cases where we can link the backtracks directly to their destinations. + if (!isLastAlternative) { + // An alternative that is not the last should jump to its successor. + m_backtrackingState.linkTo(nextOp.m_reentry, this); + } else if (!isBegin) { + // The last of more than one alternatives must jump back to the beginning. + m_backtrackingState.takeBacktracksToJumpList(nextOp.m_jumps, this); + } + // In the case of a single alternative on its own do nothing - it can fall through. + } + + // If there is a backtrack jump from a zero length match link it here. + if (op.m_zeroLengthMatch.isSet()) + m_backtrackingState.append(op.m_zeroLengthMatch); + + // At this point we've handled the backtracking back into this node. + // Now link any backtracks that need to jump to here. + + // For non-simple alternatives, link the alternative's 'return address' + // so that we backtrack back out into the previous alternative. + if (op.m_op == OpNestedAlternativeNext) + m_backtrackingState.append(op.m_returnAddress); + + // If there is more than one alternative, then the last alternative will + // have planted a jump to be linked to the end. This jump was added to the + // End node's m_jumps list. If we are back at the beginning, link it here. + if (isBegin) { + YarrOp* endOp = &m_ops[op.m_nextOp]; + while (endOp->m_nextOp != notFound) { + ASSERT(endOp->m_op == OpSimpleNestedAlternativeNext || endOp->m_op == OpNestedAlternativeNext); + endOp = &m_ops[endOp->m_nextOp]; + } + ASSERT(endOp->m_op == OpSimpleNestedAlternativeEnd || endOp->m_op == OpNestedAlternativeEnd); + m_backtrackingState.append(endOp->m_jumps); + } + + if (!isBegin) { + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked += lastOp.m_checkAdjust; + } + m_checked -= op.m_checkAdjust; + break; + } + case OpSimpleNestedAlternativeEnd: + case OpNestedAlternativeEnd: { + PatternTerm* term = op.m_term; + + // If there is a backtrack jump from a zero length match link it here. + if (op.m_zeroLengthMatch.isSet()) + m_backtrackingState.append(op.m_zeroLengthMatch); + + // If we backtrack into the end of a simple subpattern do nothing; + // just continue through into the last alternative. If we backtrack + // into the end of a non-simple set of alterntives we need to jump + // to the backtracking return address set up during generation. + if (op.m_op == OpNestedAlternativeEnd) { + m_backtrackingState.link(this); + + // Plant a jump to the return address. + unsigned parenthesesFrameLocation = term->frameLocation; + unsigned alternativeFrameLocation = parenthesesFrameLocation; + if (term->quantityType != QuantifierFixedCount) + alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; + loadFromFrameAndJump(alternativeFrameLocation); + + // Link the DataLabelPtr associated with the end of the last + // alternative to this point. + m_backtrackingState.append(op.m_returnAddress); + } + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked += lastOp.m_checkAdjust; + break; + } + + // OpParenthesesSubpatternOnceBegin/End + // + // When we are backtracking back out of a capturing subpattern we need + // to clear the start index in the matches output array, to record that + // this subpattern has not been captured. + // + // When backtracking back out of a Greedy quantified subpattern we need + // to catch this, and try running the remainder of the alternative after + // the subpattern again, skipping the parentheses. + // + // Upon backtracking back into a quantified set of parentheses we need to + // check whether we were currently skipping the subpattern. If not, we + // can backtrack into them, if we were we need to either backtrack back + // out of the start of the parentheses, or jump back to the forwards + // matching start, depending of whether the match is Greedy or NonGreedy. + case OpParenthesesSubpatternOnceBegin: { + PatternTerm* term = op.m_term; + ASSERT(term->quantityCount == 1); + + // We only need to backtrack to thispoint if capturing or greedy. + if ((term->capture() && compileMode == IncludeSubpatterns) || term->quantityType == QuantifierGreedy) { + m_backtrackingState.link(this); + + // If capturing, clear the capture (we only need to reset start). + if (term->capture() && compileMode == IncludeSubpatterns) + clearSubpatternStart(term->parentheses.subpatternId); + + // If Greedy, jump to the end. + if (term->quantityType == QuantifierGreedy) { + // Clear the flag in the stackframe indicating we ran through the subpattern. + unsigned parenthesesFrameLocation = term->frameLocation; + storeToFrame(TrustedImm32(-1), parenthesesFrameLocation); + // Jump to after the parentheses, skipping the subpattern. + jump(m_ops[op.m_nextOp].m_reentry); + // A backtrack from after the parentheses, when skipping the subpattern, + // will jump back to here. + op.m_jumps.link(this); + } + + m_backtrackingState.fallthrough(); + } + break; + } + case OpParenthesesSubpatternOnceEnd: { + PatternTerm* term = op.m_term; + + if (term->quantityType != QuantifierFixedCount) { + m_backtrackingState.link(this); + + // Check whether we should backtrack back into the parentheses, or if we + // are currently in a state where we had skipped over the subpattern + // (in which case the flag value on the stack will be -1). + unsigned parenthesesFrameLocation = term->frameLocation; + Jump hadSkipped = branch32(Equal, Address(stackPointerRegister, parenthesesFrameLocation * sizeof(void*)), TrustedImm32(-1)); + + if (term->quantityType == QuantifierGreedy) { + // For Greedy parentheses, we skip after having already tried going + // through the subpattern, so if we get here we're done. + YarrOp& beginOp = m_ops[op.m_previousOp]; + beginOp.m_jumps.append(hadSkipped); + } else { + // For NonGreedy parentheses, we try skipping the subpattern first, + // so if we get here we need to try running through the subpattern + // next. Jump back to the start of the parentheses in the forwards + // matching path. + ASSERT(term->quantityType == QuantifierNonGreedy); + YarrOp& beginOp = m_ops[op.m_previousOp]; + hadSkipped.linkTo(beginOp.m_reentry, this); + } + + m_backtrackingState.fallthrough(); + } + + m_backtrackingState.append(op.m_jumps); + break; + } + + // OpParenthesesSubpatternTerminalBegin/End + // + // Terminal subpatterns will always match - there is nothing after them to + // force a backtrack, and they have a minimum count of 0, and as such will + // always produce an acceptable result. + case OpParenthesesSubpatternTerminalBegin: { + // We will backtrack to this point once the subpattern cannot match any + // more. Since no match is accepted as a successful match (we are Greedy + // quantified with a minimum of zero) jump back to the forwards matching + // path at the end. + YarrOp& endOp = m_ops[op.m_nextOp]; + m_backtrackingState.linkTo(endOp.m_reentry, this); + break; + } + case OpParenthesesSubpatternTerminalEnd: + // We should never be backtracking to here (hence the 'terminal' in the name). + ASSERT(m_backtrackingState.isEmpty()); + m_backtrackingState.append(op.m_jumps); + break; + + // OpParentheticalAssertionBegin/End + case OpParentheticalAssertionBegin: { + PatternTerm* term = op.m_term; + YarrOp& endOp = m_ops[op.m_nextOp]; + + // We need to handle the backtracks upon backtracking back out + // of a parenthetical assertion if either we need to correct + // the input index, or the assertion was inverted. + if (op.m_checkAdjust || term->invert()) { + m_backtrackingState.link(this); + + if (op.m_checkAdjust) + add32(Imm32(op.m_checkAdjust), index); + + // In an inverted assertion failure to match the subpattern + // is treated as a successful match - jump to the end of the + // subpattern. We already have adjusted the input position + // back to that before the assertion, which is correct. + if (term->invert()) + jump(endOp.m_reentry); + + m_backtrackingState.fallthrough(); + } + + // The End node's jump list will contain any backtracks into + // the end of the assertion. Also, if inverted, we will have + // added the failure caused by a successful match to this. + m_backtrackingState.append(endOp.m_jumps); + + m_checked += op.m_checkAdjust; + break; + } + case OpParentheticalAssertionEnd: { + // FIXME: We should really be clearing any nested subpattern + // matches on bailing out from after the pattern. Firefox has + // this bug too (presumably because they use YARR!) + + // Never backtrack into an assertion; later failures bail to before the begin. + m_backtrackingState.takeBacktracksToJumpList(op.m_jumps, this); + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked -= lastOp.m_checkAdjust; + break; + } + + case OpMatchFailed: + break; + } + + } while (opIndex); + } + + // Compilation methods: + // ==================== + + // opCompileParenthesesSubpattern + // Emits ops for a subpattern (set of parentheses). These consist + // of a set of alternatives wrapped in an outer set of nodes for + // the parentheses. + // Supported types of parentheses are 'Once' (quantityCount == 1) + // and 'Terminal' (non-capturing parentheses quantified as greedy + // and infinite). + // Alternatives will use the 'Simple' set of ops if either the + // subpattern is terminal (in which case we will never need to + // backtrack), or if the subpattern only contains one alternative. + void opCompileParenthesesSubpattern(PatternTerm* term) + { + YarrOpCode parenthesesBeginOpCode; + YarrOpCode parenthesesEndOpCode; + YarrOpCode alternativeBeginOpCode = OpSimpleNestedAlternativeBegin; + YarrOpCode alternativeNextOpCode = OpSimpleNestedAlternativeNext; + YarrOpCode alternativeEndOpCode = OpSimpleNestedAlternativeEnd; + + // We can currently only compile quantity 1 subpatterns that are + // not copies. We generate a copy in the case of a range quantifier, + // e.g. /(?:x){3,9}/, or /(?:x)+/ (These are effectively expanded to + // /(?:x){3,3}(?:x){0,6}/ and /(?:x)(?:x)*/ repectively). The problem + // comes where the subpattern is capturing, in which case we would + // need to restore the capture from the first subpattern upon a + // failure in the second. + if (term->quantityCount == 1 && !term->parentheses.isCopy) { + // Select the 'Once' nodes. + parenthesesBeginOpCode = OpParenthesesSubpatternOnceBegin; + parenthesesEndOpCode = OpParenthesesSubpatternOnceEnd; + + // If there is more than one alternative we cannot use the 'simple' nodes. + if (term->parentheses.disjunction->m_alternatives.size() != 1) { + alternativeBeginOpCode = OpNestedAlternativeBegin; + alternativeNextOpCode = OpNestedAlternativeNext; + alternativeEndOpCode = OpNestedAlternativeEnd; + } + } else if (term->parentheses.isTerminal) { + // Select the 'Terminal' nodes. + parenthesesBeginOpCode = OpParenthesesSubpatternTerminalBegin; + parenthesesEndOpCode = OpParenthesesSubpatternTerminalEnd; + } else { + // This subpattern is not supported by the JIT. + m_shouldFallBack = true; + return; + } + + size_t parenBegin = m_ops.size(); + m_ops.append(parenthesesBeginOpCode); + + m_ops.append(alternativeBeginOpCode); + m_ops.last().m_previousOp = notFound; + m_ops.last().m_term = term; + Vector& alternatives = term->parentheses.disjunction->m_alternatives; + for (unsigned i = 0; i < alternatives.size(); ++i) { + size_t lastOpIndex = m_ops.size() - 1; + + PatternAlternative* nestedAlternative = alternatives[i]; + opCompileAlternative(nestedAlternative); + + size_t thisOpIndex = m_ops.size(); + m_ops.append(YarrOp(alternativeNextOpCode)); + + YarrOp& lastOp = m_ops[lastOpIndex]; + YarrOp& thisOp = m_ops[thisOpIndex]; + + lastOp.m_alternative = nestedAlternative; + lastOp.m_nextOp = thisOpIndex; + thisOp.m_previousOp = lastOpIndex; + thisOp.m_term = term; + } + YarrOp& lastOp = m_ops.last(); + ASSERT(lastOp.m_op == alternativeNextOpCode); + lastOp.m_op = alternativeEndOpCode; + lastOp.m_alternative = 0; + lastOp.m_nextOp = notFound; + + size_t parenEnd = m_ops.size(); + m_ops.append(parenthesesEndOpCode); + + m_ops[parenBegin].m_term = term; + m_ops[parenBegin].m_previousOp = notFound; + m_ops[parenBegin].m_nextOp = parenEnd; + m_ops[parenEnd].m_term = term; + m_ops[parenEnd].m_previousOp = parenBegin; + m_ops[parenEnd].m_nextOp = notFound; + } + + // opCompileParentheticalAssertion + // Emits ops for a parenthetical assertion. These consist of an + // OpSimpleNestedAlternativeBegin/Next/End set of nodes wrapping + // the alternatives, with these wrapped by an outer pair of + // OpParentheticalAssertionBegin/End nodes. + // We can always use the OpSimpleNestedAlternative nodes in the + // case of parenthetical assertions since these only ever match + // once, and will never backtrack back into the assertion. + void opCompileParentheticalAssertion(PatternTerm* term) + { + size_t parenBegin = m_ops.size(); + m_ops.append(OpParentheticalAssertionBegin); + + m_ops.append(OpSimpleNestedAlternativeBegin); + m_ops.last().m_previousOp = notFound; + m_ops.last().m_term = term; + Vector& alternatives = term->parentheses.disjunction->m_alternatives; + for (unsigned i = 0; i < alternatives.size(); ++i) { + size_t lastOpIndex = m_ops.size() - 1; + + PatternAlternative* nestedAlternative = alternatives[i]; + opCompileAlternative(nestedAlternative); + + size_t thisOpIndex = m_ops.size(); + m_ops.append(YarrOp(OpSimpleNestedAlternativeNext)); + + YarrOp& lastOp = m_ops[lastOpIndex]; + YarrOp& thisOp = m_ops[thisOpIndex]; + + lastOp.m_alternative = nestedAlternative; + lastOp.m_nextOp = thisOpIndex; + thisOp.m_previousOp = lastOpIndex; + thisOp.m_term = term; + } + YarrOp& lastOp = m_ops.last(); + ASSERT(lastOp.m_op == OpSimpleNestedAlternativeNext); + lastOp.m_op = OpSimpleNestedAlternativeEnd; + lastOp.m_alternative = 0; + lastOp.m_nextOp = notFound; + + size_t parenEnd = m_ops.size(); + m_ops.append(OpParentheticalAssertionEnd); + + m_ops[parenBegin].m_term = term; + m_ops[parenBegin].m_previousOp = notFound; + m_ops[parenBegin].m_nextOp = parenEnd; + m_ops[parenEnd].m_term = term; + m_ops[parenEnd].m_previousOp = parenBegin; + m_ops[parenEnd].m_nextOp = notFound; + } + + // opCompileAlternative + // Called to emit nodes for all terms in an alternative. + void opCompileAlternative(PatternAlternative* alternative) + { + optimizeAlternative(alternative); + + for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { + PatternTerm* term = &alternative->m_terms[i]; + + switch (term->type) { + case PatternTerm::TypeParenthesesSubpattern: + opCompileParenthesesSubpattern(term); + break; + + case PatternTerm::TypeParentheticalAssertion: + opCompileParentheticalAssertion(term); + break; + + default: + m_ops.append(term); + } + } + } + + // opCompileBody + // This method compiles the body disjunction of the regular expression. + // The body consists of two sets of alternatives - zero or more 'once + // through' (BOL anchored) alternatives, followed by zero or more + // repeated alternatives. + // For each of these two sets of alteratives, if not empty they will be + // wrapped in a set of OpBodyAlternativeBegin/Next/End nodes (with the + // 'begin' node referencing the first alternative, and 'next' nodes + // referencing any further alternatives. The begin/next/end nodes are + // linked together in a doubly linked list. In the case of repeating + // alternatives, the end node is also linked back to the beginning. + // If no repeating alternatives exist, then a OpMatchFailed node exists + // to return the failing result. + void opCompileBody(PatternDisjunction* disjunction) + { + Vector& alternatives = disjunction->m_alternatives; + size_t currentAlternativeIndex = 0; + + // Emit the 'once through' alternatives. + if (alternatives.size() && alternatives[0]->onceThrough()) { + m_ops.append(YarrOp(OpBodyAlternativeBegin)); + m_ops.last().m_previousOp = notFound; + + do { + size_t lastOpIndex = m_ops.size() - 1; + PatternAlternative* alternative = alternatives[currentAlternativeIndex]; + opCompileAlternative(alternative); + + size_t thisOpIndex = m_ops.size(); + m_ops.append(YarrOp(OpBodyAlternativeNext)); + + YarrOp& lastOp = m_ops[lastOpIndex]; + YarrOp& thisOp = m_ops[thisOpIndex]; + + lastOp.m_alternative = alternative; + lastOp.m_nextOp = thisOpIndex; + thisOp.m_previousOp = lastOpIndex; + + ++currentAlternativeIndex; + } while (currentAlternativeIndex < alternatives.size() && alternatives[currentAlternativeIndex]->onceThrough()); + + YarrOp& lastOp = m_ops.last(); + + ASSERT(lastOp.m_op == OpBodyAlternativeNext); + lastOp.m_op = OpBodyAlternativeEnd; + lastOp.m_alternative = 0; + lastOp.m_nextOp = notFound; + } + + if (currentAlternativeIndex == alternatives.size()) { + m_ops.append(YarrOp(OpMatchFailed)); + return; + } + + // Emit the repeated alternatives. + size_t repeatLoop = m_ops.size(); + m_ops.append(YarrOp(OpBodyAlternativeBegin)); + m_ops.last().m_previousOp = notFound; + do { + size_t lastOpIndex = m_ops.size() - 1; + PatternAlternative* alternative = alternatives[currentAlternativeIndex]; + ASSERT(!alternative->onceThrough()); + opCompileAlternative(alternative); + + size_t thisOpIndex = m_ops.size(); + m_ops.append(YarrOp(OpBodyAlternativeNext)); + + YarrOp& lastOp = m_ops[lastOpIndex]; + YarrOp& thisOp = m_ops[thisOpIndex]; + + lastOp.m_alternative = alternative; + lastOp.m_nextOp = thisOpIndex; + thisOp.m_previousOp = lastOpIndex; + + ++currentAlternativeIndex; + } while (currentAlternativeIndex < alternatives.size()); + YarrOp& lastOp = m_ops.last(); + ASSERT(lastOp.m_op == OpBodyAlternativeNext); + lastOp.m_op = OpBodyAlternativeEnd; + lastOp.m_alternative = 0; + lastOp.m_nextOp = repeatLoop; + } + + void generateEnter() + { +#if CPU(X86_64) + push(X86Registers::ebp); + move(stackPointerRegister, X86Registers::ebp); + push(X86Registers::ebx); +#elif CPU(X86) + push(X86Registers::ebp); + move(stackPointerRegister, X86Registers::ebp); + // TODO: do we need spill registers to fill the output pointer if there are no sub captures? + push(X86Registers::ebx); + push(X86Registers::edi); + push(X86Registers::esi); + // load output into edi (2 = saved ebp + return address). + #if COMPILER(MSVC) + loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), input); + loadPtr(Address(X86Registers::ebp, 3 * sizeof(void*)), index); + loadPtr(Address(X86Registers::ebp, 4 * sizeof(void*)), length); + if (compileMode == IncludeSubpatterns) + loadPtr(Address(X86Registers::ebp, 5 * sizeof(void*)), output); + #else + if (compileMode == IncludeSubpatterns) + loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), output); + #endif +#elif CPU(ARM) + push(ARMRegisters::r4); + push(ARMRegisters::r5); + push(ARMRegisters::r6); +#if CPU(ARM_TRADITIONAL) + push(ARMRegisters::r8); // scratch register +#endif + if (compileMode == IncludeSubpatterns) + move(ARMRegisters::r3, output); +#elif CPU(SH4) + push(SH4Registers::r11); + push(SH4Registers::r13); +#elif CPU(MIPS) + // Do nothing. +#endif + } + + void generateReturn() + { +#if CPU(X86_64) + pop(X86Registers::ebx); + pop(X86Registers::ebp); +#elif CPU(X86) + pop(X86Registers::esi); + pop(X86Registers::edi); + pop(X86Registers::ebx); + pop(X86Registers::ebp); +#elif CPU(ARM) +#if CPU(ARM_TRADITIONAL) + pop(ARMRegisters::r8); // scratch register +#endif + pop(ARMRegisters::r6); + pop(ARMRegisters::r5); + pop(ARMRegisters::r4); +#elif CPU(SH4) + pop(SH4Registers::r13); + pop(SH4Registers::r11); +#elif CPU(MIPS) + // Do nothing +#endif + ret(); + } + +public: + YarrGenerator(YarrPattern& pattern, YarrCharSize charSize) + : m_pattern(pattern) + , m_charSize(charSize) + , m_charScale(m_charSize == Char8 ? TimesOne: TimesTwo) + , m_shouldFallBack(false) + , m_checked(0) + { + } + + void compile(JSGlobalData* globalData, YarrCodeBlock& jitObject) + { + generateEnter(); + + Jump hasInput = checkInput(); + move(TrustedImmPtr((void*)WTF::notFound), returnRegister); + move(TrustedImm32(0), returnRegister2); + generateReturn(); + hasInput.link(this); + + if (compileMode == IncludeSubpatterns) { + for (unsigned i = 0; i < m_pattern.m_numSubpatterns + 1; ++i) + store32(TrustedImm32(-1), Address(output, (i << 1) * sizeof(int))); + } + + if (!m_pattern.m_body->m_hasFixedSize) + setMatchStart(index); + + initCallFrame(); + + // Compile the pattern to the internal 'YarrOp' representation. + opCompileBody(m_pattern.m_body); + + // If we encountered anything we can't handle in the JIT code + // (e.g. backreferences) then return early. + if (m_shouldFallBack) { + jitObject.setFallBack(true); + return; + } + + generate(); + backtrack(); + + // Link & finalize the code. + LinkBuffer linkBuffer(*globalData, this, REGEXP_CODE_ID); + m_backtrackingState.linkDataLabels(linkBuffer); + + if (compileMode == MatchOnly) { + if (m_charSize == Char8) + jitObject.set8BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, ("Match-only 8-bit regular expression"))); + else + jitObject.set16BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, ("Match-only 16-bit regular expression"))); + } else { + if (m_charSize == Char8) + jitObject.set8BitCode(FINALIZE_CODE(linkBuffer, ("8-bit regular expression"))); + else + jitObject.set16BitCode(FINALIZE_CODE(linkBuffer, ("16-bit regular expression"))); + } + jitObject.setFallBack(m_shouldFallBack); + } + +private: + YarrPattern& m_pattern; + + YarrCharSize m_charSize; + + Scale m_charScale; + + // Used to detect regular expression constructs that are not currently + // supported in the JIT; fall back to the interpreter when this is detected. + bool m_shouldFallBack; + + // The regular expression expressed as a linear sequence of operations. + Vector m_ops; + + // This records the current input offset being applied due to the current + // set of alternatives we are nested within. E.g. when matching the + // character 'b' within the regular expression /abc/, we will know that + // the minimum size for the alternative is 3, checked upon entry to the + // alternative, and that 'b' is at offset 1 from the start, and as such + // when matching 'b' we need to apply an offset of -2 to the load. + // + // FIXME: This should go away. Rather than tracking this value throughout + // code generation, we should gather this information up front & store it + // on the YarrOp structure. + int m_checked; + + // This class records state whilst generating the backtracking path of code. + BacktrackingState m_backtrackingState; +}; + +void jitCompile(YarrPattern& pattern, YarrCharSize charSize, JSGlobalData* globalData, YarrCodeBlock& jitObject, YarrJITCompileMode mode) +{ + if (mode == MatchOnly) + YarrGenerator(pattern, charSize).compile(globalData, jitObject); + else + YarrGenerator(pattern, charSize).compile(globalData, jitObject); +} + +}} + +#endif diff --git a/3rdparty/masm/yarr/YarrJIT.h b/3rdparty/masm/yarr/YarrJIT.h new file mode 100644 index 0000000000..bb7033fdea --- /dev/null +++ b/3rdparty/masm/yarr/YarrJIT.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrJIT_h +#define YarrJIT_h + +#if ENABLE(YARR_JIT) + +#include "JSGlobalData.h" +#include "MacroAssemblerCodeRef.h" +#include "MatchResult.h" +#include "Yarr.h" +#include "YarrPattern.h" + +#if CPU(X86) && !COMPILER(MSVC) +#define YARR_CALL __attribute__ ((regparm (3))) +#else +#define YARR_CALL +#endif + +namespace JSC { + +class JSGlobalData; +class ExecutablePool; + +namespace Yarr { + +class YarrCodeBlock { +#if CPU(X86_64) + typedef MatchResult (*YarrJITCode8)(const LChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + typedef MatchResult (*YarrJITCode16)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + typedef MatchResult (*YarrJITCodeMatchOnly8)(const LChar* input, unsigned start, unsigned length) YARR_CALL; + typedef MatchResult (*YarrJITCodeMatchOnly16)(const UChar* input, unsigned start, unsigned length) YARR_CALL; +#else + typedef EncodedMatchResult (*YarrJITCode8)(const LChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + typedef EncodedMatchResult (*YarrJITCode16)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + typedef EncodedMatchResult (*YarrJITCodeMatchOnly8)(const LChar* input, unsigned start, unsigned length) YARR_CALL; + typedef EncodedMatchResult (*YarrJITCodeMatchOnly16)(const UChar* input, unsigned start, unsigned length) YARR_CALL; +#endif + +public: + YarrCodeBlock() + : m_needFallBack(false) + { + } + + ~YarrCodeBlock() + { + } + + void setFallBack(bool fallback) { m_needFallBack = fallback; } + bool isFallBack() { return m_needFallBack; } + + bool has8BitCode() { return m_ref8.size(); } + bool has16BitCode() { return m_ref16.size(); } + void set8BitCode(MacroAssemblerCodeRef ref) { m_ref8 = ref; } + void set16BitCode(MacroAssemblerCodeRef ref) { m_ref16 = ref; } + + bool has8BitCodeMatchOnly() { return m_matchOnly8.size(); } + bool has16BitCodeMatchOnly() { return m_matchOnly16.size(); } + void set8BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly8 = matchOnly; } + void set16BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly16 = matchOnly; } + + MatchResult execute(const LChar* input, unsigned start, unsigned length, int* output) + { + ASSERT(has8BitCode()); + return MatchResult(reinterpret_cast(m_ref8.code().executableAddress())(input, start, length, output)); + } + + MatchResult execute(const UChar* input, unsigned start, unsigned length, int* output) + { + ASSERT(has16BitCode()); + return MatchResult(reinterpret_cast(m_ref16.code().executableAddress())(input, start, length, output)); + } + + MatchResult execute(const LChar* input, unsigned start, unsigned length) + { + ASSERT(has8BitCodeMatchOnly()); + return MatchResult(reinterpret_cast(m_matchOnly8.code().executableAddress())(input, start, length)); + } + + MatchResult execute(const UChar* input, unsigned start, unsigned length) + { + ASSERT(has16BitCodeMatchOnly()); + return MatchResult(reinterpret_cast(m_matchOnly16.code().executableAddress())(input, start, length)); + } + +#if ENABLE(REGEXP_TRACING) + void *getAddr() { return m_ref.code().executableAddress(); } +#endif + + void clear() + { + m_ref8 = MacroAssemblerCodeRef(); + m_ref16 = MacroAssemblerCodeRef(); + m_matchOnly8 = MacroAssemblerCodeRef(); + m_matchOnly16 = MacroAssemblerCodeRef(); + m_needFallBack = false; + } + +private: + MacroAssemblerCodeRef m_ref8; + MacroAssemblerCodeRef m_ref16; + MacroAssemblerCodeRef m_matchOnly8; + MacroAssemblerCodeRef m_matchOnly16; + bool m_needFallBack; +}; + +enum YarrJITCompileMode { + MatchOnly, + IncludeSubpatterns +}; +void jitCompile(YarrPattern&, YarrCharSize, JSGlobalData*, YarrCodeBlock& jitObject, YarrJITCompileMode = IncludeSubpatterns); + +} } // namespace JSC::Yarr + +#endif + +#endif // YarrJIT_h diff --git a/3rdparty/masm/yarr/YarrParser.h b/3rdparty/masm/yarr/YarrParser.h new file mode 100644 index 0000000000..4bab1a0903 --- /dev/null +++ b/3rdparty/masm/yarr/YarrParser.h @@ -0,0 +1,880 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrParser_h +#define YarrParser_h + +#include "Yarr.h" +#include +#include +#include + +namespace JSC { namespace Yarr { + +#define REGEXP_ERROR_PREFIX "Invalid regular expression: " + +enum BuiltInCharacterClassID { + DigitClassID, + SpaceClassID, + WordClassID, + NewlineClassID, +}; + +// The Parser class should not be used directly - only via the Yarr::parse() method. +template +class Parser { +private: + template + friend const char* parse(FriendDelegate&, const String& pattern, unsigned backReferenceLimit); + + enum ErrorCode { + NoError, + PatternTooLarge, + QuantifierOutOfOrder, + QuantifierWithoutAtom, + QuantifierTooLarge, + MissingParentheses, + ParenthesesUnmatched, + ParenthesesTypeInvalid, + CharacterClassUnmatched, + CharacterClassOutOfOrder, + EscapeUnterminated, + NumberOfErrorCodes + }; + + /* + * CharacterClassParserDelegate: + * + * The class CharacterClassParserDelegate is used in the parsing of character + * classes. This class handles detection of character ranges. This class + * implements enough of the delegate interface such that it can be passed to + * parseEscape() as an EscapeDelegate. This allows parseEscape() to be reused + * to perform the parsing of escape characters in character sets. + */ + class CharacterClassParserDelegate { + public: + CharacterClassParserDelegate(Delegate& delegate, ErrorCode& err) + : m_delegate(delegate) + , m_err(err) + , m_state(Empty) + , m_character(0) + { + } + + /* + * begin(): + * + * Called at beginning of construction. + */ + void begin(bool invert) + { + m_delegate.atomCharacterClassBegin(invert); + } + + /* + * atomPatternCharacter(): + * + * This method is called either from parseCharacterClass() (for an unescaped + * character in a character class), or from parseEscape(). In the former case + * the value true will be passed for the argument 'hyphenIsRange', and in this + * mode we will allow a hypen to be treated as indicating a range (i.e. /[a-z]/ + * is different to /[a\-z]/). + */ + void atomPatternCharacter(UChar ch, bool hyphenIsRange = false) + { + switch (m_state) { + case AfterCharacterClass: + // Following a builtin character class we need look out for a hyphen. + // We're looking for invalid ranges, such as /[\d-x]/ or /[\d-\d]/. + // If we see a hyphen following a charater class then unlike usual + // we'll report it to the delegate immediately, and put ourself into + // a poisoned state. Any following calls to add another character or + // character class will result in an error. (A hypen following a + // character-class is itself valid, but only at the end of a regex). + if (hyphenIsRange && ch == '-') { + m_delegate.atomCharacterClassAtom('-'); + m_state = AfterCharacterClassHyphen; + return; + } + // Otherwise just fall through - cached character so treat this as Empty. + + case Empty: + m_character = ch; + m_state = CachedCharacter; + return; + + case CachedCharacter: + if (hyphenIsRange && ch == '-') + m_state = CachedCharacterHyphen; + else { + m_delegate.atomCharacterClassAtom(m_character); + m_character = ch; + } + return; + + case CachedCharacterHyphen: + if (ch < m_character) { + m_err = CharacterClassOutOfOrder; + return; + } + m_delegate.atomCharacterClassRange(m_character, ch); + m_state = Empty; + return; + + // See coment in atomBuiltInCharacterClass below. + // This too is technically an error, per ECMA-262, and again we + // we chose to allow this. Note a subtlely here that while we + // diverge from the spec's definition of CharacterRange we do + // remain in compliance with the grammar. For example, consider + // the expression /[\d-a-z]/. We comply with the grammar in + // this case by not allowing a-z to be matched as a range. + case AfterCharacterClassHyphen: + m_delegate.atomCharacterClassAtom(ch); + m_state = Empty; + return; + } + } + + /* + * atomBuiltInCharacterClass(): + * + * Adds a built-in character class, called by parseEscape(). + */ + void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert) + { + switch (m_state) { + case CachedCharacter: + // Flush the currently cached character, then fall through. + m_delegate.atomCharacterClassAtom(m_character); + + case Empty: + case AfterCharacterClass: + m_state = AfterCharacterClass; + m_delegate.atomCharacterClassBuiltIn(classID, invert); + return; + + // If we hit either of these cases, we have an invalid range that + // looks something like /[x-\d]/ or /[\d-\d]/. + // According to ECMA-262 this should be a syntax error, but + // empirical testing shows this to break teh webz. Instead we + // comply with to the ECMA-262 grammar, and assume the grammar to + // have matched the range correctly, but tweak our interpretation + // of CharacterRange. Effectively we implicitly handle the hyphen + // as if it were escaped, e.g. /[\w-_]/ is treated as /[\w\-_]/. + case CachedCharacterHyphen: + m_delegate.atomCharacterClassAtom(m_character); + m_delegate.atomCharacterClassAtom('-'); + // fall through + case AfterCharacterClassHyphen: + m_delegate.atomCharacterClassBuiltIn(classID, invert); + m_state = Empty; + return; + } + } + + /* + * end(): + * + * Called at end of construction. + */ + void end() + { + if (m_state == CachedCharacter) + m_delegate.atomCharacterClassAtom(m_character); + else if (m_state == CachedCharacterHyphen) { + m_delegate.atomCharacterClassAtom(m_character); + m_delegate.atomCharacterClassAtom('-'); + } + m_delegate.atomCharacterClassEnd(); + } + + // parseEscape() should never call these delegate methods when + // invoked with inCharacterClass set. + NO_RETURN_DUE_TO_ASSERT void assertionWordBoundary(bool) { ASSERT_NOT_REACHED(); } + NO_RETURN_DUE_TO_ASSERT void atomBackReference(unsigned) { ASSERT_NOT_REACHED(); } + + private: + Delegate& m_delegate; + ErrorCode& m_err; + enum CharacterClassConstructionState { + Empty, + CachedCharacter, + CachedCharacterHyphen, + AfterCharacterClass, + AfterCharacterClassHyphen, + } m_state; + UChar m_character; + }; + + Parser(Delegate& delegate, const String& pattern, unsigned backReferenceLimit) + : m_delegate(delegate) + , m_backReferenceLimit(backReferenceLimit) + , m_err(NoError) + , m_data(pattern.getCharacters()) + , m_size(pattern.length()) + , m_index(0) + , m_parenthesesNestingDepth(0) + { + } + + /* + * parseEscape(): + * + * Helper for parseTokens() AND parseCharacterClass(). + * Unlike the other parser methods, this function does not report tokens + * directly to the member delegate (m_delegate), instead tokens are + * emitted to the delegate provided as an argument. In the case of atom + * escapes, parseTokens() will call parseEscape() passing m_delegate as + * an argument, and as such the escape will be reported to the delegate. + * + * However this method may also be used by parseCharacterClass(), in which + * case a CharacterClassParserDelegate will be passed as the delegate that + * tokens should be added to. A boolean flag is also provided to indicate + * whether that an escape in a CharacterClass is being parsed (some parsing + * rules change in this context). + * + * The boolean value returned by this method indicates whether the token + * parsed was an atom (outside of a characted class \b and \B will be + * interpreted as assertions). + */ + template + bool parseEscape(EscapeDelegate& delegate) + { + ASSERT(!m_err); + ASSERT(peek() == '\\'); + consume(); + + if (atEndOfPattern()) { + m_err = EscapeUnterminated; + return false; + } + + switch (peek()) { + // Assertions + case 'b': + consume(); + if (inCharacterClass) + delegate.atomPatternCharacter('\b'); + else { + delegate.assertionWordBoundary(false); + return false; + } + break; + case 'B': + consume(); + if (inCharacterClass) + delegate.atomPatternCharacter('B'); + else { + delegate.assertionWordBoundary(true); + return false; + } + break; + + // CharacterClassEscape + case 'd': + consume(); + delegate.atomBuiltInCharacterClass(DigitClassID, false); + break; + case 's': + consume(); + delegate.atomBuiltInCharacterClass(SpaceClassID, false); + break; + case 'w': + consume(); + delegate.atomBuiltInCharacterClass(WordClassID, false); + break; + case 'D': + consume(); + delegate.atomBuiltInCharacterClass(DigitClassID, true); + break; + case 'S': + consume(); + delegate.atomBuiltInCharacterClass(SpaceClassID, true); + break; + case 'W': + consume(); + delegate.atomBuiltInCharacterClass(WordClassID, true); + break; + + // DecimalEscape + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + // To match Firefox, we parse an invalid backreference in the range [1-7] as an octal escape. + // First, try to parse this as backreference. + if (!inCharacterClass) { + ParseState state = saveState(); + + unsigned backReference = consumeNumber(); + if (backReference <= m_backReferenceLimit) { + delegate.atomBackReference(backReference); + break; + } + + restoreState(state); + } + + // Not a backreference, and not octal. + if (peek() >= '8') { + delegate.atomPatternCharacter('\\'); + break; + } + + // Fall-through to handle this as an octal escape. + } + + // Octal escape + case '0': + delegate.atomPatternCharacter(consumeOctal()); + break; + + // ControlEscape + case 'f': + consume(); + delegate.atomPatternCharacter('\f'); + break; + case 'n': + consume(); + delegate.atomPatternCharacter('\n'); + break; + case 'r': + consume(); + delegate.atomPatternCharacter('\r'); + break; + case 't': + consume(); + delegate.atomPatternCharacter('\t'); + break; + case 'v': + consume(); + delegate.atomPatternCharacter('\v'); + break; + + // ControlLetter + case 'c': { + ParseState state = saveState(); + consume(); + if (!atEndOfPattern()) { + int control = consume(); + + // To match Firefox, inside a character class, we also accept numbers and '_' as control characters. + if (inCharacterClass ? WTF::isASCIIAlphanumeric(control) || (control == '_') : WTF::isASCIIAlpha(control)) { + delegate.atomPatternCharacter(control & 0x1f); + break; + } + } + restoreState(state); + delegate.atomPatternCharacter('\\'); + break; + } + + // HexEscape + case 'x': { + consume(); + int x = tryConsumeHex(2); + if (x == -1) + delegate.atomPatternCharacter('x'); + else + delegate.atomPatternCharacter(x); + break; + } + + // UnicodeEscape + case 'u': { + consume(); + int u = tryConsumeHex(4); + if (u == -1) + delegate.atomPatternCharacter('u'); + else + delegate.atomPatternCharacter(u); + break; + } + + // IdentityEscape + default: + delegate.atomPatternCharacter(consume()); + } + + return true; + } + + /* + * parseAtomEscape(), parseCharacterClassEscape(): + * + * These methods alias to parseEscape(). + */ + bool parseAtomEscape() + { + return parseEscape(m_delegate); + } + void parseCharacterClassEscape(CharacterClassParserDelegate& delegate) + { + parseEscape(delegate); + } + + /* + * parseCharacterClass(): + * + * Helper for parseTokens(); calls dirctly and indirectly (via parseCharacterClassEscape) + * to an instance of CharacterClassParserDelegate, to describe the character class to the + * delegate. + */ + void parseCharacterClass() + { + ASSERT(!m_err); + ASSERT(peek() == '['); + consume(); + + CharacterClassParserDelegate characterClassConstructor(m_delegate, m_err); + + characterClassConstructor.begin(tryConsume('^')); + + while (!atEndOfPattern()) { + switch (peek()) { + case ']': + consume(); + characterClassConstructor.end(); + return; + + case '\\': + parseCharacterClassEscape(characterClassConstructor); + break; + + default: + characterClassConstructor.atomPatternCharacter(consume(), true); + } + + if (m_err) + return; + } + + m_err = CharacterClassUnmatched; + } + + /* + * parseParenthesesBegin(): + * + * Helper for parseTokens(); checks for parentheses types other than regular capturing subpatterns. + */ + void parseParenthesesBegin() + { + ASSERT(!m_err); + ASSERT(peek() == '('); + consume(); + + if (tryConsume('?')) { + if (atEndOfPattern()) { + m_err = ParenthesesTypeInvalid; + return; + } + + switch (consume()) { + case ':': + m_delegate.atomParenthesesSubpatternBegin(false); + break; + + case '=': + m_delegate.atomParentheticalAssertionBegin(); + break; + + case '!': + m_delegate.atomParentheticalAssertionBegin(true); + break; + + default: + m_err = ParenthesesTypeInvalid; + } + } else + m_delegate.atomParenthesesSubpatternBegin(); + + ++m_parenthesesNestingDepth; + } + + /* + * parseParenthesesEnd(): + * + * Helper for parseTokens(); checks for parse errors (due to unmatched parentheses). + */ + void parseParenthesesEnd() + { + ASSERT(!m_err); + ASSERT(peek() == ')'); + consume(); + + if (m_parenthesesNestingDepth > 0) + m_delegate.atomParenthesesEnd(); + else + m_err = ParenthesesUnmatched; + + --m_parenthesesNestingDepth; + } + + /* + * parseQuantifier(): + * + * Helper for parseTokens(); checks for parse errors and non-greedy quantifiers. + */ + void parseQuantifier(bool lastTokenWasAnAtom, unsigned min, unsigned max) + { + ASSERT(!m_err); + ASSERT(min <= max); + + if (min == UINT_MAX) { + m_err = QuantifierTooLarge; + return; + } + + if (lastTokenWasAnAtom) + m_delegate.quantifyAtom(min, max, !tryConsume('?')); + else + m_err = QuantifierWithoutAtom; + } + + /* + * parseTokens(): + * + * This method loops over the input pattern reporting tokens to the delegate. + * The method returns when a parse error is detected, or the end of the pattern + * is reached. One piece of state is tracked around the loop, which is whether + * the last token passed to the delegate was an atom (this is necessary to detect + * a parse error when a quantifier provided without an atom to quantify). + */ + void parseTokens() + { + bool lastTokenWasAnAtom = false; + + while (!atEndOfPattern()) { + switch (peek()) { + case '|': + consume(); + m_delegate.disjunction(); + lastTokenWasAnAtom = false; + break; + + case '(': + parseParenthesesBegin(); + lastTokenWasAnAtom = false; + break; + + case ')': + parseParenthesesEnd(); + lastTokenWasAnAtom = true; + break; + + case '^': + consume(); + m_delegate.assertionBOL(); + lastTokenWasAnAtom = false; + break; + + case '$': + consume(); + m_delegate.assertionEOL(); + lastTokenWasAnAtom = false; + break; + + case '.': + consume(); + m_delegate.atomBuiltInCharacterClass(NewlineClassID, true); + lastTokenWasAnAtom = true; + break; + + case '[': + parseCharacterClass(); + lastTokenWasAnAtom = true; + break; + + case '\\': + lastTokenWasAnAtom = parseAtomEscape(); + break; + + case '*': + consume(); + parseQuantifier(lastTokenWasAnAtom, 0, quantifyInfinite); + lastTokenWasAnAtom = false; + break; + + case '+': + consume(); + parseQuantifier(lastTokenWasAnAtom, 1, quantifyInfinite); + lastTokenWasAnAtom = false; + break; + + case '?': + consume(); + parseQuantifier(lastTokenWasAnAtom, 0, 1); + lastTokenWasAnAtom = false; + break; + + case '{': { + ParseState state = saveState(); + + consume(); + if (peekIsDigit()) { + unsigned min = consumeNumber(); + unsigned max = min; + + if (tryConsume(',')) + max = peekIsDigit() ? consumeNumber() : quantifyInfinite; + + if (tryConsume('}')) { + if (min <= max) + parseQuantifier(lastTokenWasAnAtom, min, max); + else + m_err = QuantifierOutOfOrder; + lastTokenWasAnAtom = false; + break; + } + } + + restoreState(state); + } // if we did not find a complete quantifer, fall through to the default case. + + default: + m_delegate.atomPatternCharacter(consume()); + lastTokenWasAnAtom = true; + } + + if (m_err) + return; + } + + if (m_parenthesesNestingDepth > 0) + m_err = MissingParentheses; + } + + /* + * parse(): + * + * This method calls parseTokens() to parse over the input and converts any + * error code to a const char* for a result. + */ + const char* parse() + { + if (m_size > MAX_PATTERN_SIZE) + m_err = PatternTooLarge; + else + parseTokens(); + ASSERT(atEndOfPattern() || m_err); + + // The order of this array must match the ErrorCode enum. + static const char* errorMessages[NumberOfErrorCodes] = { + 0, // NoError + REGEXP_ERROR_PREFIX "regular expression too large", + REGEXP_ERROR_PREFIX "numbers out of order in {} quantifier", + REGEXP_ERROR_PREFIX "nothing to repeat", + REGEXP_ERROR_PREFIX "number too large in {} quantifier", + REGEXP_ERROR_PREFIX "missing )", + REGEXP_ERROR_PREFIX "unmatched parentheses", + REGEXP_ERROR_PREFIX "unrecognized character after (?", + REGEXP_ERROR_PREFIX "missing terminating ] for character class", + REGEXP_ERROR_PREFIX "range out of order in character class", + REGEXP_ERROR_PREFIX "\\ at end of pattern" + }; + + return errorMessages[m_err]; + } + + // Misc helper functions: + + typedef unsigned ParseState; + + ParseState saveState() + { + return m_index; + } + + void restoreState(ParseState state) + { + m_index = state; + } + + bool atEndOfPattern() + { + ASSERT(m_index <= m_size); + return m_index == m_size; + } + + int peek() + { + ASSERT(m_index < m_size); + return m_data[m_index]; + } + + bool peekIsDigit() + { + return !atEndOfPattern() && WTF::isASCIIDigit(peek()); + } + + unsigned peekDigit() + { + ASSERT(peekIsDigit()); + return peek() - '0'; + } + + int consume() + { + ASSERT(m_index < m_size); + return m_data[m_index++]; + } + + unsigned consumeDigit() + { + ASSERT(peekIsDigit()); + return consume() - '0'; + } + + unsigned consumeNumber() + { + unsigned n = consumeDigit(); + // check for overflow. + for (unsigned newValue; peekIsDigit() && ((newValue = n * 10 + peekDigit()) >= n); ) { + n = newValue; + consume(); + } + return n; + } + + unsigned consumeOctal() + { + ASSERT(WTF::isASCIIOctalDigit(peek())); + + unsigned n = consumeDigit(); + while (n < 32 && !atEndOfPattern() && WTF::isASCIIOctalDigit(peek())) + n = n * 8 + consumeDigit(); + return n; + } + + bool tryConsume(UChar ch) + { + if (atEndOfPattern() || (m_data[m_index] != ch)) + return false; + ++m_index; + return true; + } + + int tryConsumeHex(int count) + { + ParseState state = saveState(); + + int n = 0; + while (count--) { + if (atEndOfPattern() || !WTF::isASCIIHexDigit(peek())) { + restoreState(state); + return -1; + } + n = (n << 4) | WTF::toASCIIHexValue(consume()); + } + return n; + } + + Delegate& m_delegate; + unsigned m_backReferenceLimit; + ErrorCode m_err; + const CharType* m_data; + unsigned m_size; + unsigned m_index; + unsigned m_parenthesesNestingDepth; + + // Derived by empirical testing of compile time in PCRE and WREC. + static const unsigned MAX_PATTERN_SIZE = 1024 * 1024; +}; + +/* + * Yarr::parse(): + * + * The parse method is passed a pattern to be parsed and a delegate upon which + * callbacks will be made to record the parsed tokens forming the regex. + * Yarr::parse() returns null on success, or a const C string providing an error + * message where a parse error occurs. + * + * The Delegate must implement the following interface: + * + * void assertionBOL(); + * void assertionEOL(); + * void assertionWordBoundary(bool invert); + * + * void atomPatternCharacter(UChar ch); + * void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert); + * void atomCharacterClassBegin(bool invert) + * void atomCharacterClassAtom(UChar ch) + * void atomCharacterClassRange(UChar begin, UChar end) + * void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert) + * void atomCharacterClassEnd() + * void atomParenthesesSubpatternBegin(bool capture = true); + * void atomParentheticalAssertionBegin(bool invert = false); + * void atomParenthesesEnd(); + * void atomBackReference(unsigned subpatternId); + * + * void quantifyAtom(unsigned min, unsigned max, bool greedy); + * + * void disjunction(); + * + * The regular expression is described by a sequence of assertion*() and atom*() + * callbacks to the delegate, describing the terms in the regular expression. + * Following an atom a quantifyAtom() call may occur to indicate that the previous + * atom should be quantified. In the case of atoms described across multiple + * calls (parentheses and character classes) the call to quantifyAtom() will come + * after the call to the atom*End() method, never after atom*Begin(). + * + * Character classes may either be described by a single call to + * atomBuiltInCharacterClass(), or by a sequence of atomCharacterClass*() calls. + * In the latter case, ...Begin() will be called, followed by a sequence of + * calls to ...Atom(), ...Range(), and ...BuiltIn(), followed by a call to ...End(). + * + * Sequences of atoms and assertions are broken into alternatives via calls to + * disjunction(). Assertions, atoms, and disjunctions emitted between calls to + * atomParenthesesBegin() and atomParenthesesEnd() form the body of a subpattern. + * atomParenthesesBegin() is passed a subpatternId. In the case of a regular + * capturing subpattern, this will be the subpatternId associated with these + * parentheses, and will also by definition be the lowest subpatternId of these + * parentheses and of any nested paretheses. The atomParenthesesEnd() method + * is passed the subpatternId of the last capturing subexpression nested within + * these paretheses. In the case of a capturing subpattern with no nested + * capturing subpatterns, the same subpatternId will be passed to the begin and + * end functions. In the case of non-capturing subpatterns the subpatternId + * passed to the begin method is also the first possible subpatternId that might + * be nested within these paretheses. If a set of non-capturing parentheses does + * not contain any capturing subpatterns, then the subpatternId passed to begin + * will be greater than the subpatternId passed to end. + */ + +template +const char* parse(Delegate& delegate, const String& pattern, unsigned backReferenceLimit = quantifyInfinite) +{ + if (pattern.is8Bit()) + return Parser(delegate, pattern, backReferenceLimit).parse(); + return Parser(delegate, pattern, backReferenceLimit).parse(); +} + +} } // namespace JSC::Yarr + +#endif // YarrParser_h diff --git a/3rdparty/masm/yarr/YarrPattern.cpp b/3rdparty/masm/yarr/YarrPattern.cpp new file mode 100644 index 0000000000..c953a38d2f --- /dev/null +++ b/3rdparty/masm/yarr/YarrPattern.cpp @@ -0,0 +1,874 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "YarrPattern.h" + +#include "Yarr.h" +#include "YarrCanonicalizeUCS2.h" +#include "YarrParser.h" +#include + +using namespace WTF; + +namespace JSC { namespace Yarr { + +#include "RegExpJitTables.h" + +class CharacterClassConstructor { +public: + CharacterClassConstructor(bool isCaseInsensitive = false) + : m_isCaseInsensitive(isCaseInsensitive) + { + } + + void reset() + { + m_matches.clear(); + m_ranges.clear(); + m_matchesUnicode.clear(); + m_rangesUnicode.clear(); + } + + void append(const CharacterClass* other) + { + for (size_t i = 0; i < other->m_matches.size(); ++i) + addSorted(m_matches, other->m_matches[i]); + for (size_t i = 0; i < other->m_ranges.size(); ++i) + addSortedRange(m_ranges, other->m_ranges[i].begin, other->m_ranges[i].end); + for (size_t i = 0; i < other->m_matchesUnicode.size(); ++i) + addSorted(m_matchesUnicode, other->m_matchesUnicode[i]); + for (size_t i = 0; i < other->m_rangesUnicode.size(); ++i) + addSortedRange(m_rangesUnicode, other->m_rangesUnicode[i].begin, other->m_rangesUnicode[i].end); + } + + void putChar(UChar ch) + { + // Handle ascii cases. + if (ch <= 0x7f) { + if (m_isCaseInsensitive && isASCIIAlpha(ch)) { + addSorted(m_matches, toASCIIUpper(ch)); + addSorted(m_matches, toASCIILower(ch)); + } else + addSorted(m_matches, ch); + return; + } + + // Simple case, not a case-insensitive match. + if (!m_isCaseInsensitive) { + addSorted(m_matchesUnicode, ch); + return; + } + + // Add multiple matches, if necessary. + UCS2CanonicalizationRange* info = rangeInfoFor(ch); + if (info->type == CanonicalizeUnique) + addSorted(m_matchesUnicode, ch); + else + putUnicodeIgnoreCase(ch, info); + } + + void putUnicodeIgnoreCase(UChar ch, UCS2CanonicalizationRange* info) + { + ASSERT(m_isCaseInsensitive); + ASSERT(ch > 0x7f); + ASSERT(ch >= info->begin && ch <= info->end); + ASSERT(info->type != CanonicalizeUnique); + if (info->type == CanonicalizeSet) { + for (uint16_t* set = characterSetInfo[info->value]; (ch = *set); ++set) + addSorted(m_matchesUnicode, ch); + } else { + addSorted(m_matchesUnicode, ch); + addSorted(m_matchesUnicode, getCanonicalPair(info, ch)); + } + } + + void putRange(UChar lo, UChar hi) + { + if (lo <= 0x7f) { + char asciiLo = lo; + char asciiHi = std::min(hi, (UChar)0x7f); + addSortedRange(m_ranges, lo, asciiHi); + + if (m_isCaseInsensitive) { + if ((asciiLo <= 'Z') && (asciiHi >= 'A')) + addSortedRange(m_ranges, std::max(asciiLo, 'A')+('a'-'A'), std::min(asciiHi, 'Z')+('a'-'A')); + if ((asciiLo <= 'z') && (asciiHi >= 'a')) + addSortedRange(m_ranges, std::max(asciiLo, 'a')+('A'-'a'), std::min(asciiHi, 'z')+('A'-'a')); + } + } + if (hi <= 0x7f) + return; + + lo = std::max(lo, (UChar)0x80); + addSortedRange(m_rangesUnicode, lo, hi); + + if (!m_isCaseInsensitive) + return; + + UCS2CanonicalizationRange* info = rangeInfoFor(lo); + while (true) { + // Handle the range [lo .. end] + UChar end = std::min(info->end, hi); + + switch (info->type) { + case CanonicalizeUnique: + // Nothing to do - no canonical equivalents. + break; + case CanonicalizeSet: { + UChar ch; + for (uint16_t* set = characterSetInfo[info->value]; (ch = *set); ++set) + addSorted(m_matchesUnicode, ch); + break; + } + case CanonicalizeRangeLo: + addSortedRange(m_rangesUnicode, lo + info->value, end + info->value); + break; + case CanonicalizeRangeHi: + addSortedRange(m_rangesUnicode, lo - info->value, end - info->value); + break; + case CanonicalizeAlternatingAligned: + // Use addSortedRange since there is likely an abutting range to combine with. + if (lo & 1) + addSortedRange(m_rangesUnicode, lo - 1, lo - 1); + if (!(end & 1)) + addSortedRange(m_rangesUnicode, end + 1, end + 1); + break; + case CanonicalizeAlternatingUnaligned: + // Use addSortedRange since there is likely an abutting range to combine with. + if (!(lo & 1)) + addSortedRange(m_rangesUnicode, lo - 1, lo - 1); + if (end & 1) + addSortedRange(m_rangesUnicode, end + 1, end + 1); + break; + } + + if (hi == end) + return; + + ++info; + lo = info->begin; + }; + + } + + CharacterClass* charClass() + { + CharacterClass* characterClass = new CharacterClass(0); + + characterClass->m_matches.swap(m_matches); + characterClass->m_ranges.swap(m_ranges); + characterClass->m_matchesUnicode.swap(m_matchesUnicode); + characterClass->m_rangesUnicode.swap(m_rangesUnicode); + + return characterClass; + } + +private: + void addSorted(Vector& matches, UChar ch) + { + unsigned pos = 0; + unsigned range = matches.size(); + + // binary chop, find position to insert char. + while (range) { + unsigned index = range >> 1; + + int val = matches[pos+index] - ch; + if (!val) + return; + else if (val > 0) + range = index; + else { + pos += (index+1); + range -= (index+1); + } + } + + if (pos == matches.size()) + matches.append(ch); + else + matches.insert(pos, ch); + } + + void addSortedRange(Vector& ranges, UChar lo, UChar hi) + { + unsigned end = ranges.size(); + + // Simple linear scan - I doubt there are that many ranges anyway... + // feel free to fix this with something faster (eg binary chop). + for (unsigned i = 0; i < end; ++i) { + // does the new range fall before the current position in the array + if (hi < ranges[i].begin) { + // optional optimization: concatenate appending ranges? - may not be worthwhile. + if (hi == (ranges[i].begin - 1)) { + ranges[i].begin = lo; + return; + } + ranges.insert(i, CharacterRange(lo, hi)); + return; + } + // Okay, since we didn't hit the last case, the end of the new range is definitely at or after the begining + // If the new range start at or before the end of the last range, then the overlap (if it starts one after the + // end of the last range they concatenate, which is just as good. + if (lo <= (ranges[i].end + 1)) { + // found an intersect! we'll replace this entry in the array. + ranges[i].begin = std::min(ranges[i].begin, lo); + ranges[i].end = std::max(ranges[i].end, hi); + + // now check if the new range can subsume any subsequent ranges. + unsigned next = i+1; + // each iteration of the loop we will either remove something from the list, or break the loop. + while (next < ranges.size()) { + if (ranges[next].begin <= (ranges[i].end + 1)) { + // the next entry now overlaps / concatenates this one. + ranges[i].end = std::max(ranges[i].end, ranges[next].end); + ranges.remove(next); + } else + break; + } + + return; + } + } + + // CharacterRange comes after all existing ranges. + ranges.append(CharacterRange(lo, hi)); + } + + bool m_isCaseInsensitive; + + Vector m_matches; + Vector m_ranges; + Vector m_matchesUnicode; + Vector m_rangesUnicode; +}; + +class YarrPatternConstructor { +public: + YarrPatternConstructor(YarrPattern& pattern) + : m_pattern(pattern) + , m_characterClassConstructor(pattern.m_ignoreCase) + , m_invertParentheticalAssertion(false) + { + m_pattern.m_body = new PatternDisjunction(); + m_alternative = m_pattern.m_body->addNewAlternative(); + m_pattern.m_disjunctions.append(m_pattern.m_body); + } + + ~YarrPatternConstructor() + { + } + + void reset() + { + m_pattern.reset(); + m_characterClassConstructor.reset(); + + m_pattern.m_body = new PatternDisjunction(); + m_alternative = m_pattern.m_body->addNewAlternative(); + m_pattern.m_disjunctions.append(m_pattern.m_body); + } + + void assertionBOL() + { + if (!m_alternative->m_terms.size() & !m_invertParentheticalAssertion) { + m_alternative->m_startsWithBOL = true; + m_alternative->m_containsBOL = true; + m_pattern.m_containsBOL = true; + } + m_alternative->m_terms.append(PatternTerm::BOL()); + } + void assertionEOL() + { + m_alternative->m_terms.append(PatternTerm::EOL()); + } + void assertionWordBoundary(bool invert) + { + m_alternative->m_terms.append(PatternTerm::WordBoundary(invert)); + } + + void atomPatternCharacter(UChar ch) + { + // We handle case-insensitive checking of unicode characters which do have both + // cases by handling them as if they were defined using a CharacterClass. + if (!m_pattern.m_ignoreCase || isASCII(ch)) { + m_alternative->m_terms.append(PatternTerm(ch)); + return; + } + + UCS2CanonicalizationRange* info = rangeInfoFor(ch); + if (info->type == CanonicalizeUnique) { + m_alternative->m_terms.append(PatternTerm(ch)); + return; + } + + m_characterClassConstructor.putUnicodeIgnoreCase(ch, info); + CharacterClass* newCharacterClass = m_characterClassConstructor.charClass(); + m_pattern.m_userCharacterClasses.append(newCharacterClass); + m_alternative->m_terms.append(PatternTerm(newCharacterClass, false)); + } + + void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert) + { + switch (classID) { + case DigitClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.digitsCharacterClass(), invert)); + break; + case SpaceClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.spacesCharacterClass(), invert)); + break; + case WordClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.wordcharCharacterClass(), invert)); + break; + case NewlineClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.newlineCharacterClass(), invert)); + break; + } + } + + void atomCharacterClassBegin(bool invert = false) + { + m_invertCharacterClass = invert; + } + + void atomCharacterClassAtom(UChar ch) + { + m_characterClassConstructor.putChar(ch); + } + + void atomCharacterClassRange(UChar begin, UChar end) + { + m_characterClassConstructor.putRange(begin, end); + } + + void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert) + { + ASSERT(classID != NewlineClassID); + + switch (classID) { + case DigitClassID: + m_characterClassConstructor.append(invert ? m_pattern.nondigitsCharacterClass() : m_pattern.digitsCharacterClass()); + break; + + case SpaceClassID: + m_characterClassConstructor.append(invert ? m_pattern.nonspacesCharacterClass() : m_pattern.spacesCharacterClass()); + break; + + case WordClassID: + m_characterClassConstructor.append(invert ? m_pattern.nonwordcharCharacterClass() : m_pattern.wordcharCharacterClass()); + break; + + default: + ASSERT_NOT_REACHED(); + } + } + + void atomCharacterClassEnd() + { + CharacterClass* newCharacterClass = m_characterClassConstructor.charClass(); + m_pattern.m_userCharacterClasses.append(newCharacterClass); + m_alternative->m_terms.append(PatternTerm(newCharacterClass, m_invertCharacterClass)); + } + + void atomParenthesesSubpatternBegin(bool capture = true) + { + unsigned subpatternId = m_pattern.m_numSubpatterns + 1; + if (capture) + m_pattern.m_numSubpatterns++; + + PatternDisjunction* parenthesesDisjunction = new PatternDisjunction(m_alternative); + m_pattern.m_disjunctions.append(parenthesesDisjunction); + m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture, false)); + m_alternative = parenthesesDisjunction->addNewAlternative(); + } + + void atomParentheticalAssertionBegin(bool invert = false) + { + PatternDisjunction* parenthesesDisjunction = new PatternDisjunction(m_alternative); + m_pattern.m_disjunctions.append(parenthesesDisjunction); + m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParentheticalAssertion, m_pattern.m_numSubpatterns + 1, parenthesesDisjunction, false, invert)); + m_alternative = parenthesesDisjunction->addNewAlternative(); + m_invertParentheticalAssertion = invert; + } + + void atomParenthesesEnd() + { + ASSERT(m_alternative->m_parent); + ASSERT(m_alternative->m_parent->m_parent); + + PatternDisjunction* parenthesesDisjunction = m_alternative->m_parent; + m_alternative = m_alternative->m_parent->m_parent; + + PatternTerm& lastTerm = m_alternative->lastTerm(); + + unsigned numParenAlternatives = parenthesesDisjunction->m_alternatives.size(); + unsigned numBOLAnchoredAlts = 0; + + for (unsigned i = 0; i < numParenAlternatives; i++) { + // Bubble up BOL flags + if (parenthesesDisjunction->m_alternatives[i]->m_startsWithBOL) + numBOLAnchoredAlts++; + } + + if (numBOLAnchoredAlts) { + m_alternative->m_containsBOL = true; + // If all the alternatives in parens start with BOL, then so does this one + if (numBOLAnchoredAlts == numParenAlternatives) + m_alternative->m_startsWithBOL = true; + } + + lastTerm.parentheses.lastSubpatternId = m_pattern.m_numSubpatterns; + m_invertParentheticalAssertion = false; + } + + void atomBackReference(unsigned subpatternId) + { + ASSERT(subpatternId); + m_pattern.m_containsBackreferences = true; + m_pattern.m_maxBackReference = std::max(m_pattern.m_maxBackReference, subpatternId); + + if (subpatternId > m_pattern.m_numSubpatterns) { + m_alternative->m_terms.append(PatternTerm::ForwardReference()); + return; + } + + PatternAlternative* currentAlternative = m_alternative; + ASSERT(currentAlternative); + + // Note to self: if we waited until the AST was baked, we could also remove forwards refs + while ((currentAlternative = currentAlternative->m_parent->m_parent)) { + PatternTerm& term = currentAlternative->lastTerm(); + ASSERT((term.type == PatternTerm::TypeParenthesesSubpattern) || (term.type == PatternTerm::TypeParentheticalAssertion)); + + if ((term.type == PatternTerm::TypeParenthesesSubpattern) && term.capture() && (subpatternId == term.parentheses.subpatternId)) { + m_alternative->m_terms.append(PatternTerm::ForwardReference()); + return; + } + } + + m_alternative->m_terms.append(PatternTerm(subpatternId)); + } + + // deep copy the argument disjunction. If filterStartsWithBOL is true, + // skip alternatives with m_startsWithBOL set true. + PatternDisjunction* copyDisjunction(PatternDisjunction* disjunction, bool filterStartsWithBOL = false) + { + PatternDisjunction* newDisjunction = 0; + for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { + PatternAlternative* alternative = disjunction->m_alternatives[alt]; + if (!filterStartsWithBOL || !alternative->m_startsWithBOL) { + if (!newDisjunction) { + newDisjunction = new PatternDisjunction(); + newDisjunction->m_parent = disjunction->m_parent; + } + PatternAlternative* newAlternative = newDisjunction->addNewAlternative(); + for (unsigned i = 0; i < alternative->m_terms.size(); ++i) + newAlternative->m_terms.append(copyTerm(alternative->m_terms[i], filterStartsWithBOL)); + } + } + + if (newDisjunction) + m_pattern.m_disjunctions.append(newDisjunction); + return newDisjunction; + } + + PatternTerm copyTerm(PatternTerm& term, bool filterStartsWithBOL = false) + { + if ((term.type != PatternTerm::TypeParenthesesSubpattern) && (term.type != PatternTerm::TypeParentheticalAssertion)) + return PatternTerm(term); + + PatternTerm termCopy = term; + termCopy.parentheses.disjunction = copyDisjunction(termCopy.parentheses.disjunction, filterStartsWithBOL); + return termCopy; + } + + void quantifyAtom(unsigned min, unsigned max, bool greedy) + { + ASSERT(min <= max); + ASSERT(m_alternative->m_terms.size()); + + if (!max) { + m_alternative->removeLastTerm(); + return; + } + + PatternTerm& term = m_alternative->lastTerm(); + ASSERT(term.type > PatternTerm::TypeAssertionWordBoundary); + ASSERT((term.quantityCount == 1) && (term.quantityType == QuantifierFixedCount)); + + if (term.type == PatternTerm::TypeParentheticalAssertion) { + // If an assertion is quantified with a minimum count of zero, it can simply be removed. + // This arises from the RepeatMatcher behaviour in the spec. Matching an assertion never + // results in any input being consumed, however the continuation passed to the assertion + // (called in steps, 8c and 9 of the RepeatMatcher definition, ES5.1 15.10.2.5) will + // reject all zero length matches (see step 2.1). A match from the continuation of the + // expression will still be accepted regardless (via steps 8a and 11) - the upshot of all + // this is that matches from the assertion are not required, and won't be accepted anyway, + // so no need to ever run it. + if (!min) + m_alternative->removeLastTerm(); + // We never need to run an assertion more than once. Subsequent interations will be run + // with the same start index (since assertions are non-capturing) and the same captures + // (per step 4 of RepeatMatcher in ES5.1 15.10.2.5), and as such will always produce the + // same result and captures. If the first match succeeds then the subsequent (min - 1) + // matches will too. Any additional optional matches will fail (on the same basis as the + // minimum zero quantified assertions, above), but this will still result in a match. + return; + } + + if (min == 0) + term.quantify(max, greedy ? QuantifierGreedy : QuantifierNonGreedy); + else if (min == max) + term.quantify(min, QuantifierFixedCount); + else { + term.quantify(min, QuantifierFixedCount); + m_alternative->m_terms.append(copyTerm(term)); + // NOTE: this term is interesting from an analysis perspective, in that it can be ignored..... + m_alternative->lastTerm().quantify((max == quantifyInfinite) ? max : max - min, greedy ? QuantifierGreedy : QuantifierNonGreedy); + if (m_alternative->lastTerm().type == PatternTerm::TypeParenthesesSubpattern) + m_alternative->lastTerm().parentheses.isCopy = true; + } + } + + void disjunction() + { + m_alternative = m_alternative->m_parent->addNewAlternative(); + } + + unsigned setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition) + { + alternative->m_hasFixedSize = true; + Checked currentInputPosition = initialInputPosition; + + for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { + PatternTerm& term = alternative->m_terms[i]; + + switch (term.type) { + case PatternTerm::TypeAssertionBOL: + case PatternTerm::TypeAssertionEOL: + case PatternTerm::TypeAssertionWordBoundary: + term.inputPosition = currentInputPosition.unsafeGet(); + break; + + case PatternTerm::TypeBackReference: + term.inputPosition = currentInputPosition.unsafeGet(); + term.frameLocation = currentCallFrameSize; + currentCallFrameSize += YarrStackSpaceForBackTrackInfoBackReference; + alternative->m_hasFixedSize = false; + break; + + case PatternTerm::TypeForwardReference: + break; + + case PatternTerm::TypePatternCharacter: + term.inputPosition = currentInputPosition.unsafeGet(); + if (term.quantityType != QuantifierFixedCount) { + term.frameLocation = currentCallFrameSize; + currentCallFrameSize += YarrStackSpaceForBackTrackInfoPatternCharacter; + alternative->m_hasFixedSize = false; + } else + currentInputPosition += term.quantityCount; + break; + + case PatternTerm::TypeCharacterClass: + term.inputPosition = currentInputPosition.unsafeGet(); + if (term.quantityType != QuantifierFixedCount) { + term.frameLocation = currentCallFrameSize; + currentCallFrameSize += YarrStackSpaceForBackTrackInfoCharacterClass; + alternative->m_hasFixedSize = false; + } else + currentInputPosition += term.quantityCount; + break; + + case PatternTerm::TypeParenthesesSubpattern: + // Note: for fixed once parentheses we will ensure at least the minimum is available; others are on their own. + term.frameLocation = currentCallFrameSize; + if (term.quantityCount == 1 && !term.parentheses.isCopy) { + if (term.quantityType != QuantifierFixedCount) + currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesOnce; + currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet()); + // If quantity is fixed, then pre-check its minimum size. + if (term.quantityType == QuantifierFixedCount) + currentInputPosition += term.parentheses.disjunction->m_minimumSize; + term.inputPosition = currentInputPosition.unsafeGet(); + } else if (term.parentheses.isTerminal) { + currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesTerminal; + currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet()); + term.inputPosition = currentInputPosition.unsafeGet(); + } else { + term.inputPosition = currentInputPosition.unsafeGet(); + setupDisjunctionOffsets(term.parentheses.disjunction, 0, currentInputPosition.unsafeGet()); + currentCallFrameSize += YarrStackSpaceForBackTrackInfoParentheses; + } + // Fixed count of 1 could be accepted, if they have a fixed size *AND* if all alternatives are of the same length. + alternative->m_hasFixedSize = false; + break; + + case PatternTerm::TypeParentheticalAssertion: + term.inputPosition = currentInputPosition.unsafeGet(); + term.frameLocation = currentCallFrameSize; + currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition.unsafeGet()); + break; + + case PatternTerm::TypeDotStarEnclosure: + alternative->m_hasFixedSize = false; + term.inputPosition = initialInputPosition; + break; + } + } + + alternative->m_minimumSize = (currentInputPosition - initialInputPosition).unsafeGet(); + return currentCallFrameSize; + } + + unsigned setupDisjunctionOffsets(PatternDisjunction* disjunction, unsigned initialCallFrameSize, unsigned initialInputPosition) + { + if ((disjunction != m_pattern.m_body) && (disjunction->m_alternatives.size() > 1)) + initialCallFrameSize += YarrStackSpaceForBackTrackInfoAlternative; + + unsigned minimumInputSize = UINT_MAX; + unsigned maximumCallFrameSize = 0; + bool hasFixedSize = true; + + for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { + PatternAlternative* alternative = disjunction->m_alternatives[alt]; + unsigned currentAlternativeCallFrameSize = setupAlternativeOffsets(alternative, initialCallFrameSize, initialInputPosition); + minimumInputSize = std::min(minimumInputSize, alternative->m_minimumSize); + maximumCallFrameSize = std::max(maximumCallFrameSize, currentAlternativeCallFrameSize); + hasFixedSize &= alternative->m_hasFixedSize; + } + + ASSERT(minimumInputSize != UINT_MAX); + ASSERT(maximumCallFrameSize >= initialCallFrameSize); + + disjunction->m_hasFixedSize = hasFixedSize; + disjunction->m_minimumSize = minimumInputSize; + disjunction->m_callFrameSize = maximumCallFrameSize; + return maximumCallFrameSize; + } + + void setupOffsets() + { + setupDisjunctionOffsets(m_pattern.m_body, 0, 0); + } + + // This optimization identifies sets of parentheses that we will never need to backtrack. + // In these cases we do not need to store state from prior iterations. + // We can presently avoid backtracking for: + // * where the parens are at the end of the regular expression (last term in any of the + // alternatives of the main body disjunction). + // * where the parens are non-capturing, and quantified unbounded greedy (*). + // * where the parens do not contain any capturing subpatterns. + void checkForTerminalParentheses() + { + // This check is much too crude; should be just checking whether the candidate + // node contains nested capturing subpatterns, not the whole expression! + if (m_pattern.m_numSubpatterns) + return; + + Vector& alternatives = m_pattern.m_body->m_alternatives; + for (size_t i = 0; i < alternatives.size(); ++i) { + Vector& terms = alternatives[i]->m_terms; + if (terms.size()) { + PatternTerm& term = terms.last(); + if (term.type == PatternTerm::TypeParenthesesSubpattern + && term.quantityType == QuantifierGreedy + && term.quantityCount == quantifyInfinite + && !term.capture()) + term.parentheses.isTerminal = true; + } + } + } + + void optimizeBOL() + { + // Look for expressions containing beginning of line (^) anchoring and unroll them. + // e.g. /^a|^b|c/ becomes /^a|^b|c/ which is executed once followed by /c/ which loops + // This code relies on the parsing code tagging alternatives with m_containsBOL and + // m_startsWithBOL and rolling those up to containing alternatives. + // At this point, this is only valid for non-multiline expressions. + PatternDisjunction* disjunction = m_pattern.m_body; + + if (!m_pattern.m_containsBOL || m_pattern.m_multiline) + return; + + PatternDisjunction* loopDisjunction = copyDisjunction(disjunction, true); + + // Set alternatives in disjunction to "onceThrough" + for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) + disjunction->m_alternatives[alt]->setOnceThrough(); + + if (loopDisjunction) { + // Move alternatives from loopDisjunction to disjunction + for (unsigned alt = 0; alt < loopDisjunction->m_alternatives.size(); ++alt) + disjunction->m_alternatives.append(loopDisjunction->m_alternatives[alt]); + + loopDisjunction->m_alternatives.clear(); + } + } + + bool containsCapturingTerms(PatternAlternative* alternative, size_t firstTermIndex, size_t lastTermIndex) + { + Vector& terms = alternative->m_terms; + + for (size_t termIndex = firstTermIndex; termIndex <= lastTermIndex; ++termIndex) { + PatternTerm& term = terms[termIndex]; + + if (term.m_capture) + return true; + + if (term.type == PatternTerm::TypeParenthesesSubpattern) { + PatternDisjunction* nestedDisjunction = term.parentheses.disjunction; + for (unsigned alt = 0; alt < nestedDisjunction->m_alternatives.size(); ++alt) { + if (containsCapturingTerms(nestedDisjunction->m_alternatives[alt], 0, nestedDisjunction->m_alternatives[alt]->m_terms.size() - 1)) + return true; + } + } + } + + return false; + } + + // This optimization identifies alternatives in the form of + // [^].*[?].*[$] for expressions that don't have any + // capturing terms. The alternative is changed to + // followed by processing of the dot stars to find and adjust the + // beginning and the end of the match. + void optimizeDotStarWrappedExpressions() + { + Vector& alternatives = m_pattern.m_body->m_alternatives; + if (alternatives.size() != 1) + return; + + PatternAlternative* alternative = alternatives[0]; + Vector& terms = alternative->m_terms; + if (terms.size() >= 3) { + bool startsWithBOL = false; + bool endsWithEOL = false; + size_t termIndex, firstExpressionTerm, lastExpressionTerm; + + termIndex = 0; + if (terms[termIndex].type == PatternTerm::TypeAssertionBOL) { + startsWithBOL = true; + ++termIndex; + } + + PatternTerm& firstNonAnchorTerm = terms[termIndex]; + if ((firstNonAnchorTerm.type != PatternTerm::TypeCharacterClass) || (firstNonAnchorTerm.characterClass != m_pattern.newlineCharacterClass()) || !((firstNonAnchorTerm.quantityType == QuantifierGreedy) || (firstNonAnchorTerm.quantityType == QuantifierNonGreedy))) + return; + + firstExpressionTerm = termIndex + 1; + + termIndex = terms.size() - 1; + if (terms[termIndex].type == PatternTerm::TypeAssertionEOL) { + endsWithEOL = true; + --termIndex; + } + + PatternTerm& lastNonAnchorTerm = terms[termIndex]; + if ((lastNonAnchorTerm.type != PatternTerm::TypeCharacterClass) || (lastNonAnchorTerm.characterClass != m_pattern.newlineCharacterClass()) || (lastNonAnchorTerm.quantityType != QuantifierGreedy)) + return; + + lastExpressionTerm = termIndex - 1; + + if (firstExpressionTerm > lastExpressionTerm) + return; + + if (!containsCapturingTerms(alternative, firstExpressionTerm, lastExpressionTerm)) { + for (termIndex = terms.size() - 1; termIndex > lastExpressionTerm; --termIndex) + terms.remove(termIndex); + + for (termIndex = firstExpressionTerm; termIndex > 0; --termIndex) + terms.remove(termIndex - 1); + + terms.append(PatternTerm(startsWithBOL, endsWithEOL)); + + m_pattern.m_containsBOL = false; + } + } + } + +private: + YarrPattern& m_pattern; + PatternAlternative* m_alternative; + CharacterClassConstructor m_characterClassConstructor; + bool m_invertCharacterClass; + bool m_invertParentheticalAssertion; +}; + +const char* YarrPattern::compile(const String& patternString) +{ + YarrPatternConstructor constructor(*this); + + if (const char* error = parse(constructor, patternString)) + return error; + + // If the pattern contains illegal backreferences reset & reparse. + // Quoting Netscape's "What's new in JavaScript 1.2", + // "Note: if the number of left parentheses is less than the number specified + // in \#, the \# is taken as an octal escape as described in the next row." + if (containsIllegalBackReference()) { + unsigned numSubpatterns = m_numSubpatterns; + + constructor.reset(); +#if !ASSERT_DISABLED + const char* error = +#endif + parse(constructor, patternString, numSubpatterns); + + ASSERT(!error); + ASSERT(numSubpatterns == m_numSubpatterns); + } + + constructor.checkForTerminalParentheses(); + constructor.optimizeDotStarWrappedExpressions(); + constructor.optimizeBOL(); + + constructor.setupOffsets(); + + return 0; +} + +YarrPattern::YarrPattern(const String& pattern, bool ignoreCase, bool multiline, const char** error) + : m_ignoreCase(ignoreCase) + , m_multiline(multiline) + , m_containsBackreferences(false) + , m_containsBOL(false) + , m_numSubpatterns(0) + , m_maxBackReference(0) + , newlineCached(0) + , digitsCached(0) + , spacesCached(0) + , wordcharCached(0) + , nondigitsCached(0) + , nonspacesCached(0) + , nonwordcharCached(0) +{ + *error = compile(pattern); +} + +} } diff --git a/3rdparty/masm/yarr/YarrPattern.h b/3rdparty/masm/yarr/YarrPattern.h new file mode 100644 index 0000000000..14e89b8e09 --- /dev/null +++ b/3rdparty/masm/yarr/YarrPattern.h @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrPattern_h +#define YarrPattern_h + +#include +#include +#include +#include +#include + +namespace JSC { namespace Yarr { + +struct PatternDisjunction; + +struct CharacterRange { + UChar begin; + UChar end; + + CharacterRange(UChar begin, UChar end) + : begin(begin) + , end(end) + { + } +}; + +struct CharacterClassTable : RefCounted { + const char* m_table; + bool m_inverted; + static PassRefPtr create(const char* table, bool inverted) + { + return adoptRef(new CharacterClassTable(table, inverted)); + } + +private: + CharacterClassTable(const char* table, bool inverted) + : m_table(table) + , m_inverted(inverted) + { + } +}; + +struct CharacterClass { + WTF_MAKE_FAST_ALLOCATED; +public: + // All CharacterClass instances have to have the full set of matches and ranges, + // they may have an optional table for faster lookups (which must match the + // specified matches and ranges) + CharacterClass(PassRefPtr table) + : m_table(table) + { + } + Vector m_matches; + Vector m_ranges; + Vector m_matchesUnicode; + Vector m_rangesUnicode; + RefPtr m_table; +}; + +enum QuantifierType { + QuantifierFixedCount, + QuantifierGreedy, + QuantifierNonGreedy, +}; + +struct PatternTerm { + enum Type { + TypeAssertionBOL, + TypeAssertionEOL, + TypeAssertionWordBoundary, + TypePatternCharacter, + TypeCharacterClass, + TypeBackReference, + TypeForwardReference, + TypeParenthesesSubpattern, + TypeParentheticalAssertion, + TypeDotStarEnclosure, + } type; + bool m_capture :1; + bool m_invert :1; + union { + UChar patternCharacter; + CharacterClass* characterClass; + unsigned backReferenceSubpatternId; + struct { + PatternDisjunction* disjunction; + unsigned subpatternId; + unsigned lastSubpatternId; + bool isCopy; + bool isTerminal; + } parentheses; + struct { + bool bolAnchor : 1; + bool eolAnchor : 1; + } anchors; + }; + QuantifierType quantityType; + Checked quantityCount; + int inputPosition; + unsigned frameLocation; + + PatternTerm(UChar ch) + : type(PatternTerm::TypePatternCharacter) + , m_capture(false) + , m_invert(false) + { + patternCharacter = ch; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(CharacterClass* charClass, bool invert) + : type(PatternTerm::TypeCharacterClass) + , m_capture(false) + , m_invert(invert) + { + characterClass = charClass; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(Type type, unsigned subpatternId, PatternDisjunction* disjunction, bool capture = false, bool invert = false) + : type(type) + , m_capture(capture) + , m_invert(invert) + { + parentheses.disjunction = disjunction; + parentheses.subpatternId = subpatternId; + parentheses.isCopy = false; + parentheses.isTerminal = false; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(Type type, bool invert = false) + : type(type) + , m_capture(false) + , m_invert(invert) + { + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(unsigned spatternId) + : type(TypeBackReference) + , m_capture(false) + , m_invert(false) + { + backReferenceSubpatternId = spatternId; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(bool bolAnchor, bool eolAnchor) + : type(TypeDotStarEnclosure) + , m_capture(false) + , m_invert(false) + { + anchors.bolAnchor = bolAnchor; + anchors.eolAnchor = eolAnchor; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + static PatternTerm ForwardReference() + { + return PatternTerm(TypeForwardReference); + } + + static PatternTerm BOL() + { + return PatternTerm(TypeAssertionBOL); + } + + static PatternTerm EOL() + { + return PatternTerm(TypeAssertionEOL); + } + + static PatternTerm WordBoundary(bool invert) + { + return PatternTerm(TypeAssertionWordBoundary, invert); + } + + bool invert() + { + return m_invert; + } + + bool capture() + { + return m_capture; + } + + void quantify(unsigned count, QuantifierType type) + { + quantityCount = count; + quantityType = type; + } +}; + +struct PatternAlternative { + WTF_MAKE_FAST_ALLOCATED; +public: + PatternAlternative(PatternDisjunction* disjunction) + : m_parent(disjunction) + , m_onceThrough(false) + , m_hasFixedSize(false) + , m_startsWithBOL(false) + , m_containsBOL(false) + { + } + + PatternTerm& lastTerm() + { + ASSERT(m_terms.size()); + return m_terms[m_terms.size() - 1]; + } + + void removeLastTerm() + { + ASSERT(m_terms.size()); + m_terms.shrink(m_terms.size() - 1); + } + + void setOnceThrough() + { + m_onceThrough = true; + } + + bool onceThrough() + { + return m_onceThrough; + } + + Vector m_terms; + PatternDisjunction* m_parent; + unsigned m_minimumSize; + bool m_onceThrough : 1; + bool m_hasFixedSize : 1; + bool m_startsWithBOL : 1; + bool m_containsBOL : 1; +}; + +struct PatternDisjunction { + WTF_MAKE_FAST_ALLOCATED; +public: + PatternDisjunction(PatternAlternative* parent = 0) + : m_parent(parent) + , m_hasFixedSize(false) + { + } + + ~PatternDisjunction() + { + deleteAllValues(m_alternatives); + } + + PatternAlternative* addNewAlternative() + { + PatternAlternative* alternative = new PatternAlternative(this); + m_alternatives.append(alternative); + return alternative; + } + + Vector m_alternatives; + PatternAlternative* m_parent; + unsigned m_minimumSize; + unsigned m_callFrameSize; + bool m_hasFixedSize; +}; + +// You probably don't want to be calling these functions directly +// (please to be calling newlineCharacterClass() et al on your +// friendly neighborhood YarrPattern instance to get nicely +// cached copies). +CharacterClass* newlineCreate(); +CharacterClass* digitsCreate(); +CharacterClass* spacesCreate(); +CharacterClass* wordcharCreate(); +CharacterClass* nondigitsCreate(); +CharacterClass* nonspacesCreate(); +CharacterClass* nonwordcharCreate(); + +struct TermChain { + TermChain(PatternTerm term) + : term(term) + {} + + PatternTerm term; + Vector hotTerms; +}; + +struct YarrPattern { + JS_EXPORT_PRIVATE YarrPattern(const String& pattern, bool ignoreCase, bool multiline, const char** error); + + ~YarrPattern() + { + deleteAllValues(m_disjunctions); + deleteAllValues(m_userCharacterClasses); + } + + void reset() + { + m_numSubpatterns = 0; + m_maxBackReference = 0; + + m_containsBackreferences = false; + m_containsBOL = false; + + newlineCached = 0; + digitsCached = 0; + spacesCached = 0; + wordcharCached = 0; + nondigitsCached = 0; + nonspacesCached = 0; + nonwordcharCached = 0; + + deleteAllValues(m_disjunctions); + m_disjunctions.clear(); + deleteAllValues(m_userCharacterClasses); + m_userCharacterClasses.clear(); + } + + bool containsIllegalBackReference() + { + return m_maxBackReference > m_numSubpatterns; + } + + CharacterClass* newlineCharacterClass() + { + if (!newlineCached) + m_userCharacterClasses.append(newlineCached = newlineCreate()); + return newlineCached; + } + CharacterClass* digitsCharacterClass() + { + if (!digitsCached) + m_userCharacterClasses.append(digitsCached = digitsCreate()); + return digitsCached; + } + CharacterClass* spacesCharacterClass() + { + if (!spacesCached) + m_userCharacterClasses.append(spacesCached = spacesCreate()); + return spacesCached; + } + CharacterClass* wordcharCharacterClass() + { + if (!wordcharCached) + m_userCharacterClasses.append(wordcharCached = wordcharCreate()); + return wordcharCached; + } + CharacterClass* nondigitsCharacterClass() + { + if (!nondigitsCached) + m_userCharacterClasses.append(nondigitsCached = nondigitsCreate()); + return nondigitsCached; + } + CharacterClass* nonspacesCharacterClass() + { + if (!nonspacesCached) + m_userCharacterClasses.append(nonspacesCached = nonspacesCreate()); + return nonspacesCached; + } + CharacterClass* nonwordcharCharacterClass() + { + if (!nonwordcharCached) + m_userCharacterClasses.append(nonwordcharCached = nonwordcharCreate()); + return nonwordcharCached; + } + + bool m_ignoreCase : 1; + bool m_multiline : 1; + bool m_containsBackreferences : 1; + bool m_containsBOL : 1; + unsigned m_numSubpatterns; + unsigned m_maxBackReference; + PatternDisjunction* m_body; + Vector m_disjunctions; + Vector m_userCharacterClasses; + +private: + const char* compile(const String& patternString); + + CharacterClass* newlineCached; + CharacterClass* digitsCached; + CharacterClass* spacesCached; + CharacterClass* wordcharCached; + CharacterClass* nondigitsCached; + CharacterClass* nonspacesCached; + CharacterClass* nonwordcharCached; +}; + +} } // namespace JSC::Yarr + +#endif // YarrPattern_h diff --git a/3rdparty/masm/yarr/YarrSyntaxChecker.cpp b/3rdparty/masm/yarr/YarrSyntaxChecker.cpp new file mode 100644 index 0000000000..aa98c4a354 --- /dev/null +++ b/3rdparty/masm/yarr/YarrSyntaxChecker.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "YarrSyntaxChecker.h" + +#include "YarrParser.h" + +namespace JSC { namespace Yarr { + +class SyntaxChecker { +public: + void assertionBOL() {} + void assertionEOL() {} + void assertionWordBoundary(bool) {} + void atomPatternCharacter(UChar) {} + void atomBuiltInCharacterClass(BuiltInCharacterClassID, bool) {} + void atomCharacterClassBegin(bool = false) {} + void atomCharacterClassAtom(UChar) {} + void atomCharacterClassRange(UChar, UChar) {} + void atomCharacterClassBuiltIn(BuiltInCharacterClassID, bool) {} + void atomCharacterClassEnd() {} + void atomParenthesesSubpatternBegin(bool = true) {} + void atomParentheticalAssertionBegin(bool = false) {} + void atomParenthesesEnd() {} + void atomBackReference(unsigned) {} + void quantifyAtom(unsigned, unsigned, bool) {} + void disjunction() {} +}; + +const char* checkSyntax(const String& pattern) +{ + SyntaxChecker syntaxChecker; + return parse(syntaxChecker, pattern); +} + +}} // JSC::YARR diff --git a/3rdparty/masm/yarr/YarrSyntaxChecker.h b/3rdparty/masm/yarr/YarrSyntaxChecker.h new file mode 100644 index 0000000000..104ced3ab4 --- /dev/null +++ b/3rdparty/masm/yarr/YarrSyntaxChecker.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrSyntaxChecker_h +#define YarrSyntaxChecker_h + +#include + +namespace JSC { namespace Yarr { + +const char* checkSyntax(const String& pattern); + +}} // JSC::YARR + +#endif // YarrSyntaxChecker_h + diff --git a/3rdparty/masm/yarr/yarr.pri b/3rdparty/masm/yarr/yarr.pri new file mode 100644 index 0000000000..7e9b4d3f3b --- /dev/null +++ b/3rdparty/masm/yarr/yarr.pri @@ -0,0 +1,12 @@ +# ------------------------------------------------------------------- +# Project file for YARR +# +# See 'Tools/qmake/README' for an overview of the build system +# ------------------------------------------------------------------- + +SOURCES += \ + $$PWD/YarrInterpreter.cpp \ + $$PWD/YarrPattern.cpp \ + $$PWD/YarrSyntaxChecker.cpp \ + $$PWD/YarrCanonicalizeUCS2.cpp + diff --git a/masm/WeakRandom.h b/masm/WeakRandom.h deleted file mode 100644 index 325d1f6ac6..0000000000 --- a/masm/WeakRandom.h +++ /dev/null @@ -1,52 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef MASM_WEAKRANDOM_H -#define MASM_WEAKRANDOM_H - -#include - -struct WeakRandom { - WeakRandom(int) {} - uint32_t getUint32() { return 0; } -}; - -#endif // MASM_WEAKRANDOM_H diff --git a/masm/assembler/ARMAssembler.cpp b/masm/assembler/ARMAssembler.cpp deleted file mode 100644 index 9655557a5d..0000000000 --- a/masm/assembler/ARMAssembler.cpp +++ /dev/null @@ -1,428 +0,0 @@ -/* - * Copyright (C) 2009 University of Szeged - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" - -#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) - -#include "ARMAssembler.h" - -namespace JSC { - -// Patching helpers - -void ARMAssembler::patchConstantPoolLoad(void* loadAddr, void* constPoolAddr) -{ - ARMWord *ldr = reinterpret_cast(loadAddr); - ARMWord diff = reinterpret_cast(constPoolAddr) - ldr; - ARMWord index = (*ldr & 0xfff) >> 1; - - ASSERT(diff >= 1); - if (diff >= 2 || index > 0) { - diff = (diff + index - 2) * sizeof(ARMWord); - ASSERT(diff <= 0xfff); - *ldr = (*ldr & ~0xfff) | diff; - } else - *ldr = (*ldr & ~(0xfff | ARMAssembler::DataTransferUp)) | sizeof(ARMWord); -} - -// Handle immediates - -ARMWord ARMAssembler::getOp2(ARMWord imm) -{ - int rol; - - if (imm <= 0xff) - return Op2Immediate | imm; - - if ((imm & 0xff000000) == 0) { - imm <<= 8; - rol = 8; - } - else { - imm = (imm << 24) | (imm >> 8); - rol = 0; - } - - if ((imm & 0xff000000) == 0) { - imm <<= 8; - rol += 4; - } - - if ((imm & 0xf0000000) == 0) { - imm <<= 4; - rol += 2; - } - - if ((imm & 0xc0000000) == 0) { - imm <<= 2; - rol += 1; - } - - if ((imm & 0x00ffffff) == 0) - return Op2Immediate | (imm >> 24) | (rol << 8); - - return InvalidImmediate; -} - -int ARMAssembler::genInt(int reg, ARMWord imm, bool positive) -{ - // Step1: Search a non-immediate part - ARMWord mask; - ARMWord imm1; - ARMWord imm2; - int rol; - - mask = 0xff000000; - rol = 8; - while(1) { - if ((imm & mask) == 0) { - imm = (imm << rol) | (imm >> (32 - rol)); - rol = 4 + (rol >> 1); - break; - } - rol += 2; - mask >>= 2; - if (mask & 0x3) { - // rol 8 - imm = (imm << 8) | (imm >> 24); - mask = 0xff00; - rol = 24; - while (1) { - if ((imm & mask) == 0) { - imm = (imm << rol) | (imm >> (32 - rol)); - rol = (rol >> 1) - 8; - break; - } - rol += 2; - mask >>= 2; - if (mask & 0x3) - return 0; - } - break; - } - } - - ASSERT((imm & 0xff) == 0); - - if ((imm & 0xff000000) == 0) { - imm1 = Op2Immediate | ((imm >> 16) & 0xff) | (((rol + 4) & 0xf) << 8); - imm2 = Op2Immediate | ((imm >> 8) & 0xff) | (((rol + 8) & 0xf) << 8); - } else if (imm & 0xc0000000) { - imm1 = Op2Immediate | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8); - imm <<= 8; - rol += 4; - - if ((imm & 0xff000000) == 0) { - imm <<= 8; - rol += 4; - } - - if ((imm & 0xf0000000) == 0) { - imm <<= 4; - rol += 2; - } - - if ((imm & 0xc0000000) == 0) { - imm <<= 2; - rol += 1; - } - - if ((imm & 0x00ffffff) == 0) - imm2 = Op2Immediate | (imm >> 24) | ((rol & 0xf) << 8); - else - return 0; - } else { - if ((imm & 0xf0000000) == 0) { - imm <<= 4; - rol += 2; - } - - if ((imm & 0xc0000000) == 0) { - imm <<= 2; - rol += 1; - } - - imm1 = Op2Immediate | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8); - imm <<= 8; - rol += 4; - - if ((imm & 0xf0000000) == 0) { - imm <<= 4; - rol += 2; - } - - if ((imm & 0xc0000000) == 0) { - imm <<= 2; - rol += 1; - } - - if ((imm & 0x00ffffff) == 0) - imm2 = Op2Immediate | (imm >> 24) | ((rol & 0xf) << 8); - else - return 0; - } - - if (positive) { - mov(reg, imm1); - orr(reg, reg, imm2); - } else { - mvn(reg, imm1); - bic(reg, reg, imm2); - } - - return 1; -} - -ARMWord ARMAssembler::getImm(ARMWord imm, int tmpReg, bool invert) -{ - ARMWord tmp; - - // Do it by 1 instruction - tmp = getOp2(imm); - if (tmp != InvalidImmediate) - return tmp; - - tmp = getOp2(~imm); - if (tmp != InvalidImmediate) { - if (invert) - return tmp | Op2InvertedImmediate; - mvn(tmpReg, tmp); - return tmpReg; - } - - return encodeComplexImm(imm, tmpReg); -} - -void ARMAssembler::moveImm(ARMWord imm, int dest) -{ - ARMWord tmp; - - // Do it by 1 instruction - tmp = getOp2(imm); - if (tmp != InvalidImmediate) { - mov(dest, tmp); - return; - } - - tmp = getOp2(~imm); - if (tmp != InvalidImmediate) { - mvn(dest, tmp); - return; - } - - encodeComplexImm(imm, dest); -} - -ARMWord ARMAssembler::encodeComplexImm(ARMWord imm, int dest) -{ -#if WTF_ARM_ARCH_AT_LEAST(7) - ARMWord tmp = getImm16Op2(imm); - if (tmp != InvalidImmediate) { - movw(dest, tmp); - return dest; - } - movw(dest, getImm16Op2(imm & 0xffff)); - movt(dest, getImm16Op2(imm >> 16)); - return dest; -#else - // Do it by 2 instruction - if (genInt(dest, imm, true)) - return dest; - if (genInt(dest, ~imm, false)) - return dest; - - ldrImmediate(dest, imm); - return dest; -#endif -} - -// Memory load/store helpers - -void ARMAssembler::dataTransfer32(DataTransferTypeA transferType, RegisterID srcDst, RegisterID base, int32_t offset) -{ - if (offset >= 0) { - if (offset <= 0xfff) - dtrUp(transferType, srcDst, base, offset); - else if (offset <= 0xfffff) { - add(ARMRegisters::S0, base, Op2Immediate | (offset >> 12) | (10 << 8)); - dtrUp(transferType, srcDst, ARMRegisters::S0, (offset & 0xfff)); - } else { - moveImm(offset, ARMRegisters::S0); - dtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); - } - } else { - if (offset >= -0xfff) - dtrDown(transferType, srcDst, base, -offset); - else if (offset >= -0xfffff) { - sub(ARMRegisters::S0, base, Op2Immediate | (-offset >> 12) | (10 << 8)); - dtrDown(transferType, srcDst, ARMRegisters::S0, (-offset & 0xfff)); - } else { - moveImm(offset, ARMRegisters::S0); - dtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); - } - } -} - -void ARMAssembler::baseIndexTransfer32(DataTransferTypeA transferType, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) -{ - ASSERT(scale >= 0 && scale <= 3); - ARMWord op2 = lsl(index, scale); - - if (!offset) { - dtrUpRegister(transferType, srcDst, base, op2); - return; - } - - add(ARMRegisters::S1, base, op2); - dataTransfer32(transferType, srcDst, ARMRegisters::S1, offset); -} - -void ARMAssembler::dataTransfer16(DataTransferTypeB transferType, RegisterID srcDst, RegisterID base, int32_t offset) -{ - if (offset >= 0) { - if (offset <= 0xff) - halfDtrUp(transferType, srcDst, base, getOp2Half(offset)); - else if (offset <= 0xffff) { - add(ARMRegisters::S0, base, Op2Immediate | (offset >> 8) | (12 << 8)); - halfDtrUp(transferType, srcDst, ARMRegisters::S0, getOp2Half(offset & 0xff)); - } else { - moveImm(offset, ARMRegisters::S0); - halfDtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); - } - } else { - if (offset >= -0xff) - halfDtrDown(transferType, srcDst, base, getOp2Half(-offset)); - else if (offset >= -0xffff) { - sub(ARMRegisters::S0, base, Op2Immediate | (-offset >> 8) | (12 << 8)); - halfDtrDown(transferType, srcDst, ARMRegisters::S0, getOp2Half(-offset & 0xff)); - } else { - moveImm(offset, ARMRegisters::S0); - halfDtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); - } - } -} - -void ARMAssembler::baseIndexTransfer16(DataTransferTypeB transferType, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) -{ - if (!scale && !offset) { - halfDtrUpRegister(transferType, srcDst, base, index); - return; - } - - add(ARMRegisters::S1, base, lsl(index, scale)); - dataTransfer16(transferType, srcDst, ARMRegisters::S1, offset); -} - -void ARMAssembler::dataTransferFloat(DataTransferTypeFloat transferType, FPRegisterID srcDst, RegisterID base, int32_t offset) -{ - // VFP cannot directly access memory that is not four-byte-aligned - if (!(offset & 0x3)) { - if (offset <= 0x3ff && offset >= 0) { - doubleDtrUp(transferType, srcDst, base, offset >> 2); - return; - } - if (offset <= 0x3ffff && offset >= 0) { - add(ARMRegisters::S0, base, Op2Immediate | (offset >> 10) | (11 << 8)); - doubleDtrUp(transferType, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff); - return; - } - offset = -offset; - - if (offset <= 0x3ff && offset >= 0) { - doubleDtrDown(transferType, srcDst, base, offset >> 2); - return; - } - if (offset <= 0x3ffff && offset >= 0) { - sub(ARMRegisters::S0, base, Op2Immediate | (offset >> 10) | (11 << 8)); - doubleDtrDown(transferType, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff); - return; - } - offset = -offset; - } - - moveImm(offset, ARMRegisters::S0); - add(ARMRegisters::S0, ARMRegisters::S0, base); - doubleDtrUp(transferType, srcDst, ARMRegisters::S0, 0); -} - -void ARMAssembler::baseIndexTransferFloat(DataTransferTypeFloat transferType, FPRegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) -{ - add(ARMRegisters::S1, base, lsl(index, scale)); - dataTransferFloat(transferType, srcDst, ARMRegisters::S1, offset); -} - -PassRefPtr ARMAssembler::executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) -{ - // 64-bit alignment is required for next constant pool and JIT code as well - m_buffer.flushWithoutBarrier(true); - if (!m_buffer.isAligned(8)) - bkpt(0); - - RefPtr result = m_buffer.executableCopy(globalData, ownerUID, effort); - char* data = reinterpret_cast(result->start()); - - for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) { - // The last bit is set if the constant must be placed on constant pool. - int pos = (iter->m_offset) & (~0x1); - ARMWord* ldrAddr = reinterpret_cast_ptr(data + pos); - ARMWord* addr = getLdrImmAddress(ldrAddr); - if (*addr != InvalidBranchTarget) { - if (!(iter->m_offset & 1)) { - intptr_t difference = reinterpret_cast_ptr(data + *addr) - (ldrAddr + DefaultPrefetchOffset); - - if ((difference <= MaximumBranchOffsetDistance && difference >= MinimumBranchOffsetDistance)) { - *ldrAddr = B | getConditionalField(*ldrAddr) | (difference & BranchOffsetMask); - continue; - } - } - *addr = reinterpret_cast(data + *addr); - } - } - - return result; -} - -#if OS(LINUX) && COMPILER(RVCT) - -__asm void ARMAssembler::cacheFlush(void* code, size_t size) -{ - ARM - push {r7} - add r1, r1, r0 - mov r7, #0xf0000 - add r7, r7, #0x2 - mov r2, #0x0 - svc #0x0 - pop {r7} - bx lr -} - -#endif - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) diff --git a/masm/assembler/ARMAssembler.h b/masm/assembler/ARMAssembler.h deleted file mode 100644 index ebab46d98a..0000000000 --- a/masm/assembler/ARMAssembler.h +++ /dev/null @@ -1,1108 +0,0 @@ -/* - * Copyright (C) 2009, 2010 University of Szeged - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ARMAssembler_h -#define ARMAssembler_h - -#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) - -#include "AssemblerBufferWithConstantPool.h" -#include "JITCompilationEffort.h" -#include -namespace JSC { - - typedef uint32_t ARMWord; - - namespace ARMRegisters { - typedef enum { - r0 = 0, - r1, - r2, - r3, S0 = r3, /* Same as thumb assembler. */ - r4, - r5, - r6, - r7, - r8, - r9, - r10, - r11, - r12, S1 = r12, - r13, sp = r13, - r14, lr = r14, - r15, pc = r15 - } RegisterID; - - typedef enum { - d0, - d1, - d2, - d3, - d4, - d5, - d6, - d7, SD0 = d7, /* Same as thumb assembler. */ - d8, - d9, - d10, - d11, - d12, - d13, - d14, - d15, - d16, - d17, - d18, - d19, - d20, - d21, - d22, - d23, - d24, - d25, - d26, - d27, - d28, - d29, - d30, - d31 - } FPRegisterID; - - } // namespace ARMRegisters - - class ARMAssembler { - public: - typedef ARMRegisters::RegisterID RegisterID; - typedef ARMRegisters::FPRegisterID FPRegisterID; - typedef AssemblerBufferWithConstantPool<2048, 4, 4, ARMAssembler> ARMBuffer; - typedef SegmentedVector Jumps; - - ARMAssembler() - : m_indexOfTailOfLastWatchpoint(1) - { - } - - // ARM conditional constants - typedef enum { - EQ = 0x00000000, // Zero - NE = 0x10000000, // Non-zero - CS = 0x20000000, - CC = 0x30000000, - MI = 0x40000000, - PL = 0x50000000, - VS = 0x60000000, - VC = 0x70000000, - HI = 0x80000000, - LS = 0x90000000, - GE = 0xa0000000, - LT = 0xb0000000, - GT = 0xc0000000, - LE = 0xd0000000, - AL = 0xe0000000 - } Condition; - - // ARM instruction constants - enum { - AND = (0x0 << 21), - EOR = (0x1 << 21), - SUB = (0x2 << 21), - RSB = (0x3 << 21), - ADD = (0x4 << 21), - ADC = (0x5 << 21), - SBC = (0x6 << 21), - RSC = (0x7 << 21), - TST = (0x8 << 21), - TEQ = (0x9 << 21), - CMP = (0xa << 21), - CMN = (0xb << 21), - ORR = (0xc << 21), - MOV = (0xd << 21), - BIC = (0xe << 21), - MVN = (0xf << 21), - MUL = 0x00000090, - MULL = 0x00c00090, - VMOV_F64 = 0x0eb00b40, - VADD_F64 = 0x0e300b00, - VDIV_F64 = 0x0e800b00, - VSUB_F64 = 0x0e300b40, - VMUL_F64 = 0x0e200b00, - VCMP_F64 = 0x0eb40b40, - VSQRT_F64 = 0x0eb10bc0, - VABS_F64 = 0x0eb00bc0, - VNEG_F64 = 0x0eb10b40, - STMDB = 0x09200000, - LDMIA = 0x08b00000, - B = 0x0a000000, - BL = 0x0b000000, - BX = 0x012fff10, - VMOV_VFP64 = 0x0c400a10, - VMOV_ARM64 = 0x0c500a10, - VMOV_VFP32 = 0x0e000a10, - VMOV_ARM32 = 0x0e100a10, - VCVT_F64_S32 = 0x0eb80bc0, - VCVT_S32_F64 = 0x0ebd0b40, - VCVT_U32_F64 = 0x0ebc0b40, - VCVT_F32_F64 = 0x0eb70bc0, - VCVT_F64_F32 = 0x0eb70ac0, - VMRS_APSR = 0x0ef1fa10, - CLZ = 0x016f0f10, - BKPT = 0xe1200070, - BLX = 0x012fff30, -#if WTF_ARM_ARCH_AT_LEAST(7) - MOVW = 0x03000000, - MOVT = 0x03400000, -#endif - NOP = 0xe1a00000, - }; - - enum { - Op2Immediate = (1 << 25), - ImmediateForHalfWordTransfer = (1 << 22), - Op2InvertedImmediate = (1 << 26), - SetConditionalCodes = (1 << 20), - Op2IsRegisterArgument = (1 << 25), - // Data transfer flags. - DataTransferUp = (1 << 23), - DataTransferWriteBack = (1 << 21), - DataTransferPostUpdate = (1 << 24), - DataTransferLoad = (1 << 20), - ByteDataTransfer = (1 << 22), - }; - - enum DataTransferTypeA { - LoadUint32 = 0x05000000 | DataTransferLoad, - LoadUint8 = 0x05400000 | DataTransferLoad, - StoreUint32 = 0x05000000, - StoreUint8 = 0x05400000, - }; - - enum DataTransferTypeB { - LoadUint16 = 0x010000b0 | DataTransferLoad, - LoadInt16 = 0x010000f0 | DataTransferLoad, - LoadInt8 = 0x010000d0 | DataTransferLoad, - StoreUint16 = 0x010000b0, - }; - - enum DataTransferTypeFloat { - LoadFloat = 0x0d000a00 | DataTransferLoad, - LoadDouble = 0x0d000b00 | DataTransferLoad, - StoreFloat = 0x0d000a00, - StoreDouble = 0x0d000b00, - }; - - // Masks of ARM instructions - enum { - BranchOffsetMask = 0x00ffffff, - ConditionalFieldMask = 0xf0000000, - DataTransferOffsetMask = 0xfff, - }; - - enum { - MinimumBranchOffsetDistance = -0x00800000, - MaximumBranchOffsetDistance = 0x007fffff, - }; - - enum { - padForAlign8 = 0x00, - padForAlign16 = 0x0000, - padForAlign32 = 0xe12fff7f // 'bkpt 0xffff' instruction. - }; - - static const ARMWord InvalidImmediate = 0xf0000000; - static const ARMWord InvalidBranchTarget = 0xffffffff; - static const int DefaultPrefetchOffset = 2; - - static const ARMWord BlxInstructionMask = 0x012fff30; - static const ARMWord LdrOrAddInstructionMask = 0x0ff00000; - static const ARMWord LdrPcImmediateInstructionMask = 0x0f7f0000; - - static const ARMWord AddImmediateInstruction = 0x02800000; - static const ARMWord BlxInstruction = 0x012fff30; - static const ARMWord LdrImmediateInstruction = 0x05900000; - static const ARMWord LdrPcImmediateInstruction = 0x051f0000; - - // Instruction formating - - void emitInstruction(ARMWord op, int rd, int rn, ARMWord op2) - { - ASSERT(((op2 & ~Op2Immediate) <= 0xfff) || (((op2 & ~ImmediateForHalfWordTransfer) <= 0xfff))); - m_buffer.putInt(op | RN(rn) | RD(rd) | op2); - } - - void emitDoublePrecisionInstruction(ARMWord op, int dd, int dn, int dm) - { - ASSERT((dd >= 0 && dd <= 31) && (dn >= 0 && dn <= 31) && (dm >= 0 && dm <= 31)); - m_buffer.putInt(op | ((dd & 0xf) << 12) | ((dd & 0x10) << (22 - 4)) - | ((dn & 0xf) << 16) | ((dn & 0x10) << (7 - 4)) - | (dm & 0xf) | ((dm & 0x10) << (5 - 4))); - } - - void emitSinglePrecisionInstruction(ARMWord op, int sd, int sn, int sm) - { - ASSERT((sd >= 0 && sd <= 31) && (sn >= 0 && sn <= 31) && (sm >= 0 && sm <= 31)); - m_buffer.putInt(op | ((sd >> 1) << 12) | ((sd & 0x1) << 22) - | ((sn >> 1) << 16) | ((sn & 0x1) << 7) - | (sm >> 1) | ((sm & 0x1) << 5)); - } - - void bitAnd(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | AND, rd, rn, op2); - } - - void bitAnds(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | AND | SetConditionalCodes, rd, rn, op2); - } - - void eor(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | EOR, rd, rn, op2); - } - - void eors(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | EOR | SetConditionalCodes, rd, rn, op2); - } - - void sub(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | SUB, rd, rn, op2); - } - - void subs(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | SUB | SetConditionalCodes, rd, rn, op2); - } - - void rsb(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | RSB, rd, rn, op2); - } - - void rsbs(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | RSB | SetConditionalCodes, rd, rn, op2); - } - - void add(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | ADD, rd, rn, op2); - } - - void adds(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | ADD | SetConditionalCodes, rd, rn, op2); - } - - void adc(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | ADC, rd, rn, op2); - } - - void adcs(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | ADC | SetConditionalCodes, rd, rn, op2); - } - - void sbc(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | SBC, rd, rn, op2); - } - - void sbcs(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | SBC | SetConditionalCodes, rd, rn, op2); - } - - void rsc(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | RSC, rd, rn, op2); - } - - void rscs(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | RSC | SetConditionalCodes, rd, rn, op2); - } - - void tst(int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | TST | SetConditionalCodes, 0, rn, op2); - } - - void teq(int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | TEQ | SetConditionalCodes, 0, rn, op2); - } - - void cmp(int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | CMP | SetConditionalCodes, 0, rn, op2); - } - - void cmn(int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | CMN | SetConditionalCodes, 0, rn, op2); - } - - void orr(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | ORR, rd, rn, op2); - } - - void orrs(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | ORR | SetConditionalCodes, rd, rn, op2); - } - - void mov(int rd, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | MOV, rd, ARMRegisters::r0, op2); - } - -#if WTF_ARM_ARCH_AT_LEAST(7) - void movw(int rd, ARMWord op2, Condition cc = AL) - { - ASSERT((op2 | 0xf0fff) == 0xf0fff); - m_buffer.putInt(toARMWord(cc) | MOVW | RD(rd) | op2); - } - - void movt(int rd, ARMWord op2, Condition cc = AL) - { - ASSERT((op2 | 0xf0fff) == 0xf0fff); - m_buffer.putInt(toARMWord(cc) | MOVT | RD(rd) | op2); - } -#endif - - void movs(int rd, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | MOV | SetConditionalCodes, rd, ARMRegisters::r0, op2); - } - - static void revertJump(void* instructionStart, RegisterID rd, ARMWord imm) - { - ARMWord* insn = reinterpret_cast(instructionStart); - ARMWord* address = getLdrImmAddress(insn); - *address = imm; - } - - void bic(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | BIC, rd, rn, op2); - } - - void bics(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | BIC | SetConditionalCodes, rd, rn, op2); - } - - void mvn(int rd, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | MVN, rd, ARMRegisters::r0, op2); - } - - void mvns(int rd, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | MVN | SetConditionalCodes, rd, ARMRegisters::r0, op2); - } - - void mul(int rd, int rn, int rm, Condition cc = AL) - { - m_buffer.putInt(toARMWord(cc) | MUL | RN(rd) | RS(rn) | RM(rm)); - } - - void muls(int rd, int rn, int rm, Condition cc = AL) - { - m_buffer.putInt(toARMWord(cc) | MUL | SetConditionalCodes | RN(rd) | RS(rn) | RM(rm)); - } - - void mull(int rdhi, int rdlo, int rn, int rm, Condition cc = AL) - { - m_buffer.putInt(toARMWord(cc) | MULL | RN(rdhi) | RD(rdlo) | RS(rn) | RM(rm)); - } - - void vmov_f64(int dd, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VMOV_F64, dd, 0, dm); - } - - void vadd_f64(int dd, int dn, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VADD_F64, dd, dn, dm); - } - - void vdiv_f64(int dd, int dn, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VDIV_F64, dd, dn, dm); - } - - void vsub_f64(int dd, int dn, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VSUB_F64, dd, dn, dm); - } - - void vmul_f64(int dd, int dn, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VMUL_F64, dd, dn, dm); - } - - void vcmp_f64(int dd, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VCMP_F64, dd, 0, dm); - } - - void vsqrt_f64(int dd, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VSQRT_F64, dd, 0, dm); - } - - void vabs_f64(int dd, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VABS_F64, dd, 0, dm); - } - - void vneg_f64(int dd, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VNEG_F64, dd, 0, dm); - } - - void ldrImmediate(int rd, ARMWord imm, Condition cc = AL) - { - m_buffer.putIntWithConstantInt(toARMWord(cc) | LoadUint32 | DataTransferUp | RN(ARMRegisters::pc) | RD(rd), imm, true); - } - - void ldrUniqueImmediate(int rd, ARMWord imm, Condition cc = AL) - { - m_buffer.putIntWithConstantInt(toARMWord(cc) | LoadUint32 | DataTransferUp | RN(ARMRegisters::pc) | RD(rd), imm); - } - - void dtrUp(DataTransferTypeA transferType, int rd, int rb, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rb, op2); - } - - void dtrUpRegister(DataTransferTypeA transferType, int rd, int rb, int rm, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType | DataTransferUp | Op2IsRegisterArgument, rd, rb, rm); - } - - void dtrDown(DataTransferTypeA transferType, int rd, int rb, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType, rd, rb, op2); - } - - void dtrDownRegister(DataTransferTypeA transferType, int rd, int rb, int rm, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType | Op2IsRegisterArgument, rd, rb, rm); - } - - void halfDtrUp(DataTransferTypeB transferType, int rd, int rb, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rb, op2); - } - - void halfDtrUpRegister(DataTransferTypeB transferType, int rd, int rn, int rm, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rn, rm); - } - - void halfDtrDown(DataTransferTypeB transferType, int rd, int rb, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType, rd, rb, op2); - } - - void halfDtrDownRegister(DataTransferTypeB transferType, int rd, int rn, int rm, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType, rd, rn, rm); - } - - void doubleDtrUp(DataTransferTypeFloat type, int rd, int rb, ARMWord op2, Condition cc = AL) - { - ASSERT(op2 <= 0xff && rd <= 15); - /* Only d0-d15 and s0, s2, s4 ... s30 are supported. */ - m_buffer.putInt(toARMWord(cc) | DataTransferUp | type | (rd << 12) | RN(rb) | op2); - } - - void doubleDtrDown(DataTransferTypeFloat type, int rd, int rb, ARMWord op2, Condition cc = AL) - { - ASSERT(op2 <= 0xff && rd <= 15); - /* Only d0-d15 and s0, s2, s4 ... s30 are supported. */ - m_buffer.putInt(toARMWord(cc) | type | (rd << 12) | RN(rb) | op2); - } - - void push(int reg, Condition cc = AL) - { - ASSERT(ARMWord(reg) <= 0xf); - m_buffer.putInt(toARMWord(cc) | StoreUint32 | DataTransferWriteBack | RN(ARMRegisters::sp) | RD(reg) | 0x4); - } - - void pop(int reg, Condition cc = AL) - { - ASSERT(ARMWord(reg) <= 0xf); - m_buffer.putInt(toARMWord(cc) | (LoadUint32 ^ DataTransferPostUpdate) | DataTransferUp | RN(ARMRegisters::sp) | RD(reg) | 0x4); - } - - inline void poke(int reg, Condition cc = AL) - { - dtrDown(StoreUint32, ARMRegisters::sp, 0, reg, cc); - } - - inline void peek(int reg, Condition cc = AL) - { - dtrUp(LoadUint32, reg, ARMRegisters::sp, 0, cc); - } - - void vmov_vfp64(int sm, int rt, int rt2, Condition cc = AL) - { - ASSERT(rt != rt2); - m_buffer.putInt(toARMWord(cc) | VMOV_VFP64 | RN(rt2) | RD(rt) | (sm & 0xf) | ((sm & 0x10) << (5 - 4))); - } - - void vmov_arm64(int rt, int rt2, int sm, Condition cc = AL) - { - ASSERT(rt != rt2); - m_buffer.putInt(toARMWord(cc) | VMOV_ARM64 | RN(rt2) | RD(rt) | (sm & 0xf) | ((sm & 0x10) << (5 - 4))); - } - - void vmov_vfp32(int sn, int rt, Condition cc = AL) - { - ASSERT(rt <= 15); - emitSinglePrecisionInstruction(toARMWord(cc) | VMOV_VFP32, rt << 1, sn, 0); - } - - void vmov_arm32(int rt, int sn, Condition cc = AL) - { - ASSERT(rt <= 15); - emitSinglePrecisionInstruction(toARMWord(cc) | VMOV_ARM32, rt << 1, sn, 0); - } - - void vcvt_f64_s32(int dd, int sm, Condition cc = AL) - { - ASSERT(!(sm & 0x1)); // sm must be divisible by 2 - emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F64_S32, dd, 0, (sm >> 1)); - } - - void vcvt_s32_f64(int sd, int dm, Condition cc = AL) - { - ASSERT(!(sd & 0x1)); // sd must be divisible by 2 - emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_S32_F64, (sd >> 1), 0, dm); - } - - void vcvt_u32_f64(int sd, int dm, Condition cc = AL) - { - ASSERT(!(sd & 0x1)); // sd must be divisible by 2 - emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_U32_F64, (sd >> 1), 0, dm); - } - - void vcvt_f64_f32(int dd, int sm, Condition cc = AL) - { - ASSERT(dd <= 15 && sm <= 15); - emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F64_F32, dd, 0, sm); - } - - void vcvt_f32_f64(int dd, int sm, Condition cc = AL) - { - ASSERT(dd <= 15 && sm <= 15); - emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F32_F64, dd, 0, sm); - } - - void vmrs_apsr(Condition cc = AL) - { - m_buffer.putInt(toARMWord(cc) | VMRS_APSR); - } - - void clz(int rd, int rm, Condition cc = AL) - { - m_buffer.putInt(toARMWord(cc) | CLZ | RD(rd) | RM(rm)); - } - - void bkpt(ARMWord value) - { - m_buffer.putInt(BKPT | ((value & 0xff0) << 4) | (value & 0xf)); - } - - void nop() - { - m_buffer.putInt(NOP); - } - - void bx(int rm, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | BX, 0, 0, RM(rm)); - } - - AssemblerLabel blx(int rm, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | BLX, 0, 0, RM(rm)); - return m_buffer.label(); - } - - static ARMWord lsl(int reg, ARMWord value) - { - ASSERT(reg <= ARMRegisters::pc); - ASSERT(value <= 0x1f); - return reg | (value << 7) | 0x00; - } - - static ARMWord lsr(int reg, ARMWord value) - { - ASSERT(reg <= ARMRegisters::pc); - ASSERT(value <= 0x1f); - return reg | (value << 7) | 0x20; - } - - static ARMWord asr(int reg, ARMWord value) - { - ASSERT(reg <= ARMRegisters::pc); - ASSERT(value <= 0x1f); - return reg | (value << 7) | 0x40; - } - - static ARMWord lslRegister(int reg, int shiftReg) - { - ASSERT(reg <= ARMRegisters::pc); - ASSERT(shiftReg <= ARMRegisters::pc); - return reg | (shiftReg << 8) | 0x10; - } - - static ARMWord lsrRegister(int reg, int shiftReg) - { - ASSERT(reg <= ARMRegisters::pc); - ASSERT(shiftReg <= ARMRegisters::pc); - return reg | (shiftReg << 8) | 0x30; - } - - static ARMWord asrRegister(int reg, int shiftReg) - { - ASSERT(reg <= ARMRegisters::pc); - ASSERT(shiftReg <= ARMRegisters::pc); - return reg | (shiftReg << 8) | 0x50; - } - - // General helpers - - size_t codeSize() const - { - return m_buffer.codeSize(); - } - - void ensureSpace(int insnSpace, int constSpace) - { - m_buffer.ensureSpace(insnSpace, constSpace); - } - - int sizeOfConstantPool() - { - return m_buffer.sizeOfConstantPool(); - } - - AssemblerLabel labelIgnoringWatchpoints() - { - m_buffer.ensureSpaceForAnyInstruction(); - return m_buffer.label(); - } - - AssemblerLabel labelForWatchpoint() - { - m_buffer.ensureSpaceForAnyInstruction(maxJumpReplacementSize() / sizeof(ARMWord)); - AssemblerLabel result = m_buffer.label(); - if (result.m_offset != (m_indexOfTailOfLastWatchpoint - maxJumpReplacementSize())) - result = label(); - m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); - return label(); - } - - AssemblerLabel label() - { - AssemblerLabel result = labelIgnoringWatchpoints(); - while (result.m_offset + 1 < m_indexOfTailOfLastWatchpoint) { - nop(); - // The available number of instructions are ensured by labelForWatchpoint. - result = m_buffer.label(); - } - return result; - } - - AssemblerLabel align(int alignment) - { - while (!m_buffer.isAligned(alignment)) - mov(ARMRegisters::r0, ARMRegisters::r0); - - return label(); - } - - AssemblerLabel loadBranchTarget(int rd, Condition cc = AL, int useConstantPool = 0) - { - ensureSpace(sizeof(ARMWord), sizeof(ARMWord)); - m_jumps.append(m_buffer.codeSize() | (useConstantPool & 0x1)); - ldrUniqueImmediate(rd, InvalidBranchTarget, cc); - return m_buffer.label(); - } - - AssemblerLabel jmp(Condition cc = AL, int useConstantPool = 0) - { - return loadBranchTarget(ARMRegisters::pc, cc, useConstantPool); - } - - PassRefPtr executableCopy(JSGlobalData&, void* ownerUID, JITCompilationEffort); - - unsigned debugOffset() { return m_buffer.debugOffset(); } - - // DFG assembly helpers for moving data between fp and registers. - void vmov(RegisterID rd1, RegisterID rd2, FPRegisterID rn) - { - vmov_arm64(rd1, rd2, rn); - } - - void vmov(FPRegisterID rd, RegisterID rn1, RegisterID rn2) - { - vmov_vfp64(rd, rn1, rn2); - } - - // Patching helpers - - static ARMWord* getLdrImmAddress(ARMWord* insn) - { - // Check for call - if ((*insn & LdrPcImmediateInstructionMask) != LdrPcImmediateInstruction) { - // Must be BLX - ASSERT((*insn & BlxInstructionMask) == BlxInstruction); - insn--; - } - - // Must be an ldr ..., [pc +/- imm] - ASSERT((*insn & LdrPcImmediateInstructionMask) == LdrPcImmediateInstruction); - - ARMWord addr = reinterpret_cast(insn) + DefaultPrefetchOffset * sizeof(ARMWord); - if (*insn & DataTransferUp) - return reinterpret_cast(addr + (*insn & DataTransferOffsetMask)); - return reinterpret_cast(addr - (*insn & DataTransferOffsetMask)); - } - - static ARMWord* getLdrImmAddressOnPool(ARMWord* insn, uint32_t* constPool) - { - // Must be an ldr ..., [pc +/- imm] - ASSERT((*insn & LdrPcImmediateInstructionMask) == LdrPcImmediateInstruction); - - if (*insn & 0x1) - return reinterpret_cast(constPool + ((*insn & DataTransferOffsetMask) >> 1)); - return getLdrImmAddress(insn); - } - - static void patchPointerInternal(intptr_t from, void* to) - { - ARMWord* insn = reinterpret_cast(from); - ARMWord* addr = getLdrImmAddress(insn); - *addr = reinterpret_cast(to); - } - - static ARMWord patchConstantPoolLoad(ARMWord load, ARMWord value) - { - value = (value << 1) + 1; - ASSERT(!(value & ~DataTransferOffsetMask)); - return (load & ~DataTransferOffsetMask) | value; - } - - static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr); - - // Read pointers - static void* readPointer(void* from) - { - ARMWord* instruction = reinterpret_cast(from); - ARMWord* address = getLdrImmAddress(instruction); - return *reinterpret_cast(address); - } - - // Patch pointers - - static void linkPointer(void* code, AssemblerLabel from, void* to) - { - patchPointerInternal(reinterpret_cast(code) + from.m_offset, to); - } - - static void repatchInt32(void* where, int32_t to) - { - patchPointerInternal(reinterpret_cast(where), reinterpret_cast(to)); - } - - static void repatchCompact(void* where, int32_t value) - { - ARMWord* instruction = reinterpret_cast(where); - ASSERT((*instruction & 0x0f700000) == LoadUint32); - if (value >= 0) - *instruction = (*instruction & 0xff7ff000) | DataTransferUp | value; - else - *instruction = (*instruction & 0xff7ff000) | -value; - cacheFlush(instruction, sizeof(ARMWord)); - } - - static void repatchPointer(void* from, void* to) - { - patchPointerInternal(reinterpret_cast(from), to); - } - - // Linkers - static intptr_t getAbsoluteJumpAddress(void* base, int offset = 0) - { - return reinterpret_cast(base) + offset - sizeof(ARMWord); - } - - void linkJump(AssemblerLabel from, AssemblerLabel to) - { - ARMWord* insn = reinterpret_cast(getAbsoluteJumpAddress(m_buffer.data(), from.m_offset)); - ARMWord* addr = getLdrImmAddressOnPool(insn, m_buffer.poolAddress()); - *addr = toARMWord(to.m_offset); - } - - static void linkJump(void* code, AssemblerLabel from, void* to) - { - patchPointerInternal(getAbsoluteJumpAddress(code, from.m_offset), to); - } - - static void relinkJump(void* from, void* to) - { - patchPointerInternal(getAbsoluteJumpAddress(from), to); - } - - static void linkCall(void* code, AssemblerLabel from, void* to) - { - patchPointerInternal(getAbsoluteJumpAddress(code, from.m_offset), to); - } - - static void relinkCall(void* from, void* to) - { - patchPointerInternal(getAbsoluteJumpAddress(from), to); - } - - static void* readCallTarget(void* from) - { - return reinterpret_cast(readPointer(reinterpret_cast(getAbsoluteJumpAddress(from)))); - } - - static void replaceWithJump(void* instructionStart, void* to) - { - ARMWord* instruction = reinterpret_cast(instructionStart) - 1; - intptr_t difference = reinterpret_cast(to) - (reinterpret_cast(instruction) + DefaultPrefetchOffset * sizeof(ARMWord)); - - if (!(difference & 1)) { - difference >>= 2; - if ((difference <= MaximumBranchOffsetDistance && difference >= MinimumBranchOffsetDistance)) { - // Direct branch. - instruction[0] = B | AL | (difference & BranchOffsetMask); - cacheFlush(instruction, sizeof(ARMWord)); - return; - } - } - - // Load target. - instruction[0] = LoadUint32 | AL | RN(ARMRegisters::pc) | RD(ARMRegisters::pc) | 4; - instruction[1] = reinterpret_cast(to); - cacheFlush(instruction, sizeof(ARMWord) * 2); - } - - static ptrdiff_t maxJumpReplacementSize() - { - return sizeof(ARMWord) * 2; - } - - static void replaceWithLoad(void* instructionStart) - { - ARMWord* instruction = reinterpret_cast(instructionStart); - cacheFlush(instruction, sizeof(ARMWord)); - - ASSERT((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction || (*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction); - if ((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction) { - *instruction = (*instruction & ~LdrOrAddInstructionMask) | LdrImmediateInstruction; - cacheFlush(instruction, sizeof(ARMWord)); - } - } - - static void replaceWithAddressComputation(void* instructionStart) - { - ARMWord* instruction = reinterpret_cast(instructionStart); - cacheFlush(instruction, sizeof(ARMWord)); - - ASSERT((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction || (*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction); - if ((*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction) { - *instruction = (*instruction & ~LdrOrAddInstructionMask) | AddImmediateInstruction; - cacheFlush(instruction, sizeof(ARMWord)); - } - } - - // Address operations - - static void* getRelocatedAddress(void* code, AssemblerLabel label) - { - return reinterpret_cast(reinterpret_cast(code) + label.m_offset); - } - - // Address differences - - static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) - { - return b.m_offset - a.m_offset; - } - - static unsigned getCallReturnOffset(AssemblerLabel call) - { - return call.m_offset; - } - - // Handle immediates - - static ARMWord getOp2(ARMWord imm); - - // Fast case if imm is known to be between 0 and 0xff - static ARMWord getOp2Byte(ARMWord imm) - { - ASSERT(imm <= 0xff); - return Op2Immediate | imm; - } - - static ARMWord getOp2Half(ARMWord imm) - { - ASSERT(imm <= 0xff); - return ImmediateForHalfWordTransfer | (imm & 0x0f) | ((imm & 0xf0) << 4); - } - -#if WTF_ARM_ARCH_AT_LEAST(7) - static ARMWord getImm16Op2(ARMWord imm) - { - if (imm <= 0xffff) - return (imm & 0xf000) << 4 | (imm & 0xfff); - return InvalidImmediate; - } -#endif - ARMWord getImm(ARMWord imm, int tmpReg, bool invert = false); - void moveImm(ARMWord imm, int dest); - ARMWord encodeComplexImm(ARMWord imm, int dest); - - // Memory load/store helpers - - void dataTransfer32(DataTransferTypeA, RegisterID srcDst, RegisterID base, int32_t offset); - void baseIndexTransfer32(DataTransferTypeA, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); - void dataTransfer16(DataTransferTypeB, RegisterID srcDst, RegisterID base, int32_t offset); - void baseIndexTransfer16(DataTransferTypeB, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); - void dataTransferFloat(DataTransferTypeFloat, FPRegisterID srcDst, RegisterID base, int32_t offset); - void baseIndexTransferFloat(DataTransferTypeFloat, FPRegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); - - // Constant pool hnadlers - - static ARMWord placeConstantPoolBarrier(int offset) - { - offset = (offset - sizeof(ARMWord)) >> 2; - ASSERT((offset <= MaximumBranchOffsetDistance && offset >= MinimumBranchOffsetDistance)); - return AL | B | (offset & BranchOffsetMask); - } - -#if OS(LINUX) && COMPILER(RVCT) - static __asm void cacheFlush(void* code, size_t); -#else - static void cacheFlush(void* code, size_t size) - { -#if OS(LINUX) && COMPILER(GCC) - uintptr_t currentPage = reinterpret_cast(code) & ~(pageSize() - 1); - uintptr_t lastPage = (reinterpret_cast(code) + size) & ~(pageSize() - 1); - do { - asm volatile( - "push {r7}\n" - "mov r0, %0\n" - "mov r1, %1\n" - "mov r7, #0xf0000\n" - "add r7, r7, #0x2\n" - "mov r2, #0x0\n" - "svc 0x0\n" - "pop {r7}\n" - : - : "r" (currentPage), "r" (currentPage + pageSize()) - : "r0", "r1", "r2"); - currentPage += pageSize(); - } while (lastPage >= currentPage); -#elif OS(WINCE) - CacheRangeFlush(code, size, CACHE_SYNC_ALL); -#elif OS(QNX) && ENABLE(ASSEMBLER_WX_EXCLUSIVE) - UNUSED_PARAM(code); - UNUSED_PARAM(size); -#elif OS(QNX) - msync(code, size, MS_INVALIDATE_ICACHE); -#else -#error "The cacheFlush support is missing on this platform." -#endif - } -#endif - - private: - static ARMWord RM(int reg) - { - ASSERT(reg <= ARMRegisters::pc); - return reg; - } - - static ARMWord RS(int reg) - { - ASSERT(reg <= ARMRegisters::pc); - return reg << 8; - } - - static ARMWord RD(int reg) - { - ASSERT(reg <= ARMRegisters::pc); - return reg << 12; - } - - static ARMWord RN(int reg) - { - ASSERT(reg <= ARMRegisters::pc); - return reg << 16; - } - - static ARMWord getConditionalField(ARMWord i) - { - return i & ConditionalFieldMask; - } - - static ARMWord toARMWord(Condition cc) - { - return static_cast(cc); - } - - static ARMWord toARMWord(uint32_t u) - { - return static_cast(u); - } - - int genInt(int reg, ARMWord imm, bool positive); - - ARMBuffer m_buffer; - Jumps m_jumps; - uint32_t m_indexOfTailOfLastWatchpoint; - }; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) - -#endif // ARMAssembler_h diff --git a/masm/assembler/ARMv7Assembler.cpp b/masm/assembler/ARMv7Assembler.cpp deleted file mode 100644 index faca66421b..0000000000 --- a/masm/assembler/ARMv7Assembler.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" - -#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) - -#include "ARMv7Assembler.h" - -namespace JSC { - -} - -#endif diff --git a/masm/assembler/ARMv7Assembler.h b/masm/assembler/ARMv7Assembler.h deleted file mode 100644 index b93ec6e63f..0000000000 --- a/masm/assembler/ARMv7Assembler.h +++ /dev/null @@ -1,2706 +0,0 @@ -/* - * Copyright (C) 2009, 2010, 2012 Apple Inc. All rights reserved. - * Copyright (C) 2010 University of Szeged - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ARMAssembler_h -#define ARMAssembler_h - -#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) - -#include "AssemblerBuffer.h" -#include -#include -#include - -namespace JSC { - -namespace ARMRegisters { - typedef enum { - r0, - r1, - r2, - r3, - r4, - r5, - r6, - r7, wr = r7, // thumb work register - r8, - r9, sb = r9, // static base - r10, sl = r10, // stack limit - r11, fp = r11, // frame pointer - r12, ip = r12, - r13, sp = r13, - r14, lr = r14, - r15, pc = r15, - } RegisterID; - - typedef enum { - s0, - s1, - s2, - s3, - s4, - s5, - s6, - s7, - s8, - s9, - s10, - s11, - s12, - s13, - s14, - s15, - s16, - s17, - s18, - s19, - s20, - s21, - s22, - s23, - s24, - s25, - s26, - s27, - s28, - s29, - s30, - s31, - } FPSingleRegisterID; - - typedef enum { - d0, - d1, - d2, - d3, - d4, - d5, - d6, - d7, - d8, - d9, - d10, - d11, - d12, - d13, - d14, - d15, - d16, - d17, - d18, - d19, - d20, - d21, - d22, - d23, - d24, - d25, - d26, - d27, - d28, - d29, - d30, - d31, - } FPDoubleRegisterID; - - typedef enum { - q0, - q1, - q2, - q3, - q4, - q5, - q6, - q7, - q8, - q9, - q10, - q11, - q12, - q13, - q14, - q15, - q16, - q17, - q18, - q19, - q20, - q21, - q22, - q23, - q24, - q25, - q26, - q27, - q28, - q29, - q30, - q31, - } FPQuadRegisterID; - - inline FPSingleRegisterID asSingle(FPDoubleRegisterID reg) - { - ASSERT(reg < d16); - return (FPSingleRegisterID)(reg << 1); - } - - inline FPDoubleRegisterID asDouble(FPSingleRegisterID reg) - { - ASSERT(!(reg & 1)); - return (FPDoubleRegisterID)(reg >> 1); - } -} - -class ARMv7Assembler; -class ARMThumbImmediate { - friend class ARMv7Assembler; - - typedef uint8_t ThumbImmediateType; - static const ThumbImmediateType TypeInvalid = 0; - static const ThumbImmediateType TypeEncoded = 1; - static const ThumbImmediateType TypeUInt16 = 2; - - typedef union { - int16_t asInt; - struct { - unsigned imm8 : 8; - unsigned imm3 : 3; - unsigned i : 1; - unsigned imm4 : 4; - }; - // If this is an encoded immediate, then it may describe a shift, or a pattern. - struct { - unsigned shiftValue7 : 7; - unsigned shiftAmount : 5; - }; - struct { - unsigned immediate : 8; - unsigned pattern : 4; - }; - } ThumbImmediateValue; - - // byte0 contains least significant bit; not using an array to make client code endian agnostic. - typedef union { - int32_t asInt; - struct { - uint8_t byte0; - uint8_t byte1; - uint8_t byte2; - uint8_t byte3; - }; - } PatternBytes; - - ALWAYS_INLINE static void countLeadingZerosPartial(uint32_t& value, int32_t& zeros, const int N) - { - if (value & ~((1 << N) - 1)) /* check for any of the top N bits (of 2N bits) are set */ - value >>= N; /* if any were set, lose the bottom N */ - else /* if none of the top N bits are set, */ - zeros += N; /* then we have identified N leading zeros */ - } - - static int32_t countLeadingZeros(uint32_t value) - { - if (!value) - return 32; - - int32_t zeros = 0; - countLeadingZerosPartial(value, zeros, 16); - countLeadingZerosPartial(value, zeros, 8); - countLeadingZerosPartial(value, zeros, 4); - countLeadingZerosPartial(value, zeros, 2); - countLeadingZerosPartial(value, zeros, 1); - return zeros; - } - - ARMThumbImmediate() - : m_type(TypeInvalid) - { - m_value.asInt = 0; - } - - ARMThumbImmediate(ThumbImmediateType type, ThumbImmediateValue value) - : m_type(type) - , m_value(value) - { - } - - ARMThumbImmediate(ThumbImmediateType type, uint16_t value) - : m_type(TypeUInt16) - { - // Make sure this constructor is only reached with type TypeUInt16; - // this extra parameter makes the code a little clearer by making it - // explicit at call sites which type is being constructed - ASSERT_UNUSED(type, type == TypeUInt16); - - m_value.asInt = value; - } - -public: - static ARMThumbImmediate makeEncodedImm(uint32_t value) - { - ThumbImmediateValue encoding; - encoding.asInt = 0; - - // okay, these are easy. - if (value < 256) { - encoding.immediate = value; - encoding.pattern = 0; - return ARMThumbImmediate(TypeEncoded, encoding); - } - - int32_t leadingZeros = countLeadingZeros(value); - // if there were 24 or more leading zeros, then we'd have hit the (value < 256) case. - ASSERT(leadingZeros < 24); - - // Given a number with bit fields Z:B:C, where count(Z)+count(B)+count(C) == 32, - // Z are the bits known zero, B is the 8-bit immediate, C are the bits to check for - // zero. count(B) == 8, so the count of bits to be checked is 24 - count(Z). - int32_t rightShiftAmount = 24 - leadingZeros; - if (value == ((value >> rightShiftAmount) << rightShiftAmount)) { - // Shift the value down to the low byte position. The assign to - // shiftValue7 drops the implicit top bit. - encoding.shiftValue7 = value >> rightShiftAmount; - // The endoded shift amount is the magnitude of a right rotate. - encoding.shiftAmount = 8 + leadingZeros; - return ARMThumbImmediate(TypeEncoded, encoding); - } - - PatternBytes bytes; - bytes.asInt = value; - - if ((bytes.byte0 == bytes.byte1) && (bytes.byte0 == bytes.byte2) && (bytes.byte0 == bytes.byte3)) { - encoding.immediate = bytes.byte0; - encoding.pattern = 3; - return ARMThumbImmediate(TypeEncoded, encoding); - } - - if ((bytes.byte0 == bytes.byte2) && !(bytes.byte1 | bytes.byte3)) { - encoding.immediate = bytes.byte0; - encoding.pattern = 1; - return ARMThumbImmediate(TypeEncoded, encoding); - } - - if ((bytes.byte1 == bytes.byte3) && !(bytes.byte0 | bytes.byte2)) { - encoding.immediate = bytes.byte1; - encoding.pattern = 2; - return ARMThumbImmediate(TypeEncoded, encoding); - } - - return ARMThumbImmediate(); - } - - static ARMThumbImmediate makeUInt12(int32_t value) - { - return (!(value & 0xfffff000)) - ? ARMThumbImmediate(TypeUInt16, (uint16_t)value) - : ARMThumbImmediate(); - } - - static ARMThumbImmediate makeUInt12OrEncodedImm(int32_t value) - { - // If this is not a 12-bit unsigned it, try making an encoded immediate. - return (!(value & 0xfffff000)) - ? ARMThumbImmediate(TypeUInt16, (uint16_t)value) - : makeEncodedImm(value); - } - - // The 'make' methods, above, return a !isValid() value if the argument - // cannot be represented as the requested type. This methods is called - // 'get' since the argument can always be represented. - static ARMThumbImmediate makeUInt16(uint16_t value) - { - return ARMThumbImmediate(TypeUInt16, value); - } - - bool isValid() - { - return m_type != TypeInvalid; - } - - uint16_t asUInt16() const { return m_value.asInt; } - - // These methods rely on the format of encoded byte values. - bool isUInt3() { return !(m_value.asInt & 0xfff8); } - bool isUInt4() { return !(m_value.asInt & 0xfff0); } - bool isUInt5() { return !(m_value.asInt & 0xffe0); } - bool isUInt6() { return !(m_value.asInt & 0xffc0); } - bool isUInt7() { return !(m_value.asInt & 0xff80); } - bool isUInt8() { return !(m_value.asInt & 0xff00); } - bool isUInt9() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xfe00); } - bool isUInt10() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xfc00); } - bool isUInt12() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xf000); } - bool isUInt16() { return m_type == TypeUInt16; } - uint8_t getUInt3() { ASSERT(isUInt3()); return m_value.asInt; } - uint8_t getUInt4() { ASSERT(isUInt4()); return m_value.asInt; } - uint8_t getUInt5() { ASSERT(isUInt5()); return m_value.asInt; } - uint8_t getUInt6() { ASSERT(isUInt6()); return m_value.asInt; } - uint8_t getUInt7() { ASSERT(isUInt7()); return m_value.asInt; } - uint8_t getUInt8() { ASSERT(isUInt8()); return m_value.asInt; } - uint16_t getUInt9() { ASSERT(isUInt9()); return m_value.asInt; } - uint16_t getUInt10() { ASSERT(isUInt10()); return m_value.asInt; } - uint16_t getUInt12() { ASSERT(isUInt12()); return m_value.asInt; } - uint16_t getUInt16() { ASSERT(isUInt16()); return m_value.asInt; } - - bool isEncodedImm() { return m_type == TypeEncoded; } - -private: - ThumbImmediateType m_type; - ThumbImmediateValue m_value; -}; - -typedef enum { - SRType_LSL, - SRType_LSR, - SRType_ASR, - SRType_ROR, - - SRType_RRX = SRType_ROR -} ARMShiftType; - -class ShiftTypeAndAmount { - friend class ARMv7Assembler; - -public: - ShiftTypeAndAmount() - { - m_u.type = (ARMShiftType)0; - m_u.amount = 0; - } - - ShiftTypeAndAmount(ARMShiftType type, unsigned amount) - { - m_u.type = type; - m_u.amount = amount & 31; - } - - unsigned lo4() { return m_u.lo4; } - unsigned hi4() { return m_u.hi4; } - -private: - union { - struct { - unsigned lo4 : 4; - unsigned hi4 : 4; - }; - struct { - unsigned type : 2; - unsigned amount : 6; - }; - } m_u; -}; - -class ARMv7Assembler { -public: - typedef ARMRegisters::RegisterID RegisterID; - typedef ARMRegisters::FPSingleRegisterID FPSingleRegisterID; - typedef ARMRegisters::FPDoubleRegisterID FPDoubleRegisterID; - typedef ARMRegisters::FPQuadRegisterID FPQuadRegisterID; - - // (HS, LO, HI, LS) -> (AE, B, A, BE) - // (VS, VC) -> (O, NO) - typedef enum { - ConditionEQ, - ConditionNE, - ConditionHS, ConditionCS = ConditionHS, - ConditionLO, ConditionCC = ConditionLO, - ConditionMI, - ConditionPL, - ConditionVS, - ConditionVC, - ConditionHI, - ConditionLS, - ConditionGE, - ConditionLT, - ConditionGT, - ConditionLE, - ConditionAL, - ConditionInvalid - } Condition; - -#define JUMP_ENUM_WITH_SIZE(index, value) (((value) << 3) | (index)) -#define JUMP_ENUM_SIZE(jump) ((jump) >> 3) - enum JumpType { JumpFixed = JUMP_ENUM_WITH_SIZE(0, 0), - JumpNoCondition = JUMP_ENUM_WITH_SIZE(1, 5 * sizeof(uint16_t)), - JumpCondition = JUMP_ENUM_WITH_SIZE(2, 6 * sizeof(uint16_t)), - JumpNoConditionFixedSize = JUMP_ENUM_WITH_SIZE(3, 5 * sizeof(uint16_t)), - JumpConditionFixedSize = JUMP_ENUM_WITH_SIZE(4, 6 * sizeof(uint16_t)) - }; - enum JumpLinkType { - LinkInvalid = JUMP_ENUM_WITH_SIZE(0, 0), - LinkJumpT1 = JUMP_ENUM_WITH_SIZE(1, sizeof(uint16_t)), - LinkJumpT2 = JUMP_ENUM_WITH_SIZE(2, sizeof(uint16_t)), - LinkJumpT3 = JUMP_ENUM_WITH_SIZE(3, 2 * sizeof(uint16_t)), - LinkJumpT4 = JUMP_ENUM_WITH_SIZE(4, 2 * sizeof(uint16_t)), - LinkConditionalJumpT4 = JUMP_ENUM_WITH_SIZE(5, 3 * sizeof(uint16_t)), - LinkBX = JUMP_ENUM_WITH_SIZE(6, 5 * sizeof(uint16_t)), - LinkConditionalBX = JUMP_ENUM_WITH_SIZE(7, 6 * sizeof(uint16_t)) - }; - - class LinkRecord { - public: - LinkRecord(intptr_t from, intptr_t to, JumpType type, Condition condition) - { - data.realTypes.m_from = from; - data.realTypes.m_to = to; - data.realTypes.m_type = type; - data.realTypes.m_linkType = LinkInvalid; - data.realTypes.m_condition = condition; - } - void operator=(const LinkRecord& other) - { - data.copyTypes.content[0] = other.data.copyTypes.content[0]; - data.copyTypes.content[1] = other.data.copyTypes.content[1]; - data.copyTypes.content[2] = other.data.copyTypes.content[2]; - } - intptr_t from() const { return data.realTypes.m_from; } - void setFrom(intptr_t from) { data.realTypes.m_from = from; } - intptr_t to() const { return data.realTypes.m_to; } - JumpType type() const { return data.realTypes.m_type; } - JumpLinkType linkType() const { return data.realTypes.m_linkType; } - void setLinkType(JumpLinkType linkType) { ASSERT(data.realTypes.m_linkType == LinkInvalid); data.realTypes.m_linkType = linkType; } - Condition condition() const { return data.realTypes.m_condition; } - private: - union { - struct RealTypes { - intptr_t m_from : 31; - intptr_t m_to : 31; - JumpType m_type : 8; - JumpLinkType m_linkType : 8; - Condition m_condition : 16; - } realTypes; - struct CopyTypes { - uint32_t content[3]; - } copyTypes; - COMPILE_ASSERT(sizeof(RealTypes) == sizeof(CopyTypes), LinkRecordCopyStructSizeEqualsRealStruct); - } data; - }; - - ARMv7Assembler() - : m_indexOfLastWatchpoint(INT_MIN) - , m_indexOfTailOfLastWatchpoint(INT_MIN) - { - } - -private: - - // ARMv7, Appx-A.6.3 - static bool BadReg(RegisterID reg) - { - return (reg == ARMRegisters::sp) || (reg == ARMRegisters::pc); - } - - uint32_t singleRegisterMask(FPSingleRegisterID rdNum, int highBitsShift, int lowBitShift) - { - uint32_t rdMask = (rdNum >> 1) << highBitsShift; - if (rdNum & 1) - rdMask |= 1 << lowBitShift; - return rdMask; - } - - uint32_t doubleRegisterMask(FPDoubleRegisterID rdNum, int highBitShift, int lowBitsShift) - { - uint32_t rdMask = (rdNum & 0xf) << lowBitsShift; - if (rdNum & 16) - rdMask |= 1 << highBitShift; - return rdMask; - } - - typedef enum { - OP_ADD_reg_T1 = 0x1800, - OP_SUB_reg_T1 = 0x1A00, - OP_ADD_imm_T1 = 0x1C00, - OP_SUB_imm_T1 = 0x1E00, - OP_MOV_imm_T1 = 0x2000, - OP_CMP_imm_T1 = 0x2800, - OP_ADD_imm_T2 = 0x3000, - OP_SUB_imm_T2 = 0x3800, - OP_AND_reg_T1 = 0x4000, - OP_EOR_reg_T1 = 0x4040, - OP_TST_reg_T1 = 0x4200, - OP_RSB_imm_T1 = 0x4240, - OP_CMP_reg_T1 = 0x4280, - OP_ORR_reg_T1 = 0x4300, - OP_MVN_reg_T1 = 0x43C0, - OP_ADD_reg_T2 = 0x4400, - OP_MOV_reg_T1 = 0x4600, - OP_BLX = 0x4700, - OP_BX = 0x4700, - OP_STR_reg_T1 = 0x5000, - OP_STRH_reg_T1 = 0x5200, - OP_STRB_reg_T1 = 0x5400, - OP_LDRSB_reg_T1 = 0x5600, - OP_LDR_reg_T1 = 0x5800, - OP_LDRH_reg_T1 = 0x5A00, - OP_LDRB_reg_T1 = 0x5C00, - OP_LDRSH_reg_T1 = 0x5E00, - OP_STR_imm_T1 = 0x6000, - OP_LDR_imm_T1 = 0x6800, - OP_STRB_imm_T1 = 0x7000, - OP_LDRB_imm_T1 = 0x7800, - OP_STRH_imm_T1 = 0x8000, - OP_LDRH_imm_T1 = 0x8800, - OP_STR_imm_T2 = 0x9000, - OP_LDR_imm_T2 = 0x9800, - OP_ADD_SP_imm_T1 = 0xA800, - OP_ADD_SP_imm_T2 = 0xB000, - OP_SUB_SP_imm_T1 = 0xB080, - OP_BKPT = 0xBE00, - OP_IT = 0xBF00, - OP_NOP_T1 = 0xBF00, - } OpcodeID; - - typedef enum { - OP_B_T1 = 0xD000, - OP_B_T2 = 0xE000, - OP_AND_reg_T2 = 0xEA00, - OP_TST_reg_T2 = 0xEA10, - OP_ORR_reg_T2 = 0xEA40, - OP_ORR_S_reg_T2 = 0xEA50, - OP_ASR_imm_T1 = 0xEA4F, - OP_LSL_imm_T1 = 0xEA4F, - OP_LSR_imm_T1 = 0xEA4F, - OP_ROR_imm_T1 = 0xEA4F, - OP_MVN_reg_T2 = 0xEA6F, - OP_EOR_reg_T2 = 0xEA80, - OP_ADD_reg_T3 = 0xEB00, - OP_ADD_S_reg_T3 = 0xEB10, - OP_SUB_reg_T2 = 0xEBA0, - OP_SUB_S_reg_T2 = 0xEBB0, - OP_CMP_reg_T2 = 0xEBB0, - OP_VMOV_CtoD = 0xEC00, - OP_VMOV_DtoC = 0xEC10, - OP_FSTS = 0xED00, - OP_VSTR = 0xED00, - OP_FLDS = 0xED10, - OP_VLDR = 0xED10, - OP_VMOV_CtoS = 0xEE00, - OP_VMOV_StoC = 0xEE10, - OP_VMUL_T2 = 0xEE20, - OP_VADD_T2 = 0xEE30, - OP_VSUB_T2 = 0xEE30, - OP_VDIV = 0xEE80, - OP_VABS_T2 = 0xEEB0, - OP_VCMP = 0xEEB0, - OP_VCVT_FPIVFP = 0xEEB0, - OP_VMOV_T2 = 0xEEB0, - OP_VMOV_IMM_T2 = 0xEEB0, - OP_VMRS = 0xEEB0, - OP_VNEG_T2 = 0xEEB0, - OP_VSQRT_T1 = 0xEEB0, - OP_VCVTSD_T1 = 0xEEB0, - OP_VCVTDS_T1 = 0xEEB0, - OP_B_T3a = 0xF000, - OP_B_T4a = 0xF000, - OP_AND_imm_T1 = 0xF000, - OP_TST_imm = 0xF010, - OP_ORR_imm_T1 = 0xF040, - OP_MOV_imm_T2 = 0xF040, - OP_MVN_imm = 0xF060, - OP_EOR_imm_T1 = 0xF080, - OP_ADD_imm_T3 = 0xF100, - OP_ADD_S_imm_T3 = 0xF110, - OP_CMN_imm = 0xF110, - OP_ADC_imm = 0xF140, - OP_SUB_imm_T3 = 0xF1A0, - OP_SUB_S_imm_T3 = 0xF1B0, - OP_CMP_imm_T2 = 0xF1B0, - OP_RSB_imm_T2 = 0xF1C0, - OP_RSB_S_imm_T2 = 0xF1D0, - OP_ADD_imm_T4 = 0xF200, - OP_MOV_imm_T3 = 0xF240, - OP_SUB_imm_T4 = 0xF2A0, - OP_MOVT = 0xF2C0, - OP_UBFX_T1 = 0xF3C0, - OP_NOP_T2a = 0xF3AF, - OP_STRB_imm_T3 = 0xF800, - OP_STRB_reg_T2 = 0xF800, - OP_LDRB_imm_T3 = 0xF810, - OP_LDRB_reg_T2 = 0xF810, - OP_STRH_imm_T3 = 0xF820, - OP_STRH_reg_T2 = 0xF820, - OP_LDRH_reg_T2 = 0xF830, - OP_LDRH_imm_T3 = 0xF830, - OP_STR_imm_T4 = 0xF840, - OP_STR_reg_T2 = 0xF840, - OP_LDR_imm_T4 = 0xF850, - OP_LDR_reg_T2 = 0xF850, - OP_STRB_imm_T2 = 0xF880, - OP_LDRB_imm_T2 = 0xF890, - OP_STRH_imm_T2 = 0xF8A0, - OP_LDRH_imm_T2 = 0xF8B0, - OP_STR_imm_T3 = 0xF8C0, - OP_LDR_imm_T3 = 0xF8D0, - OP_LDRSB_reg_T2 = 0xF910, - OP_LDRSH_reg_T2 = 0xF930, - OP_LSL_reg_T2 = 0xFA00, - OP_LSR_reg_T2 = 0xFA20, - OP_ASR_reg_T2 = 0xFA40, - OP_ROR_reg_T2 = 0xFA60, - OP_CLZ = 0xFAB0, - OP_SMULL_T1 = 0xFB80, - } OpcodeID1; - - typedef enum { - OP_VADD_T2b = 0x0A00, - OP_VDIVb = 0x0A00, - OP_FLDSb = 0x0A00, - OP_VLDRb = 0x0A00, - OP_VMOV_IMM_T2b = 0x0A00, - OP_VMOV_T2b = 0x0A40, - OP_VMUL_T2b = 0x0A00, - OP_FSTSb = 0x0A00, - OP_VSTRb = 0x0A00, - OP_VMOV_StoCb = 0x0A10, - OP_VMOV_CtoSb = 0x0A10, - OP_VMOV_DtoCb = 0x0A10, - OP_VMOV_CtoDb = 0x0A10, - OP_VMRSb = 0x0A10, - OP_VABS_T2b = 0x0A40, - OP_VCMPb = 0x0A40, - OP_VCVT_FPIVFPb = 0x0A40, - OP_VNEG_T2b = 0x0A40, - OP_VSUB_T2b = 0x0A40, - OP_VSQRT_T1b = 0x0A40, - OP_VCVTSD_T1b = 0x0A40, - OP_VCVTDS_T1b = 0x0A40, - OP_NOP_T2b = 0x8000, - OP_B_T3b = 0x8000, - OP_B_T4b = 0x9000, - } OpcodeID2; - - struct FourFours { - FourFours(unsigned f3, unsigned f2, unsigned f1, unsigned f0) - { - m_u.f0 = f0; - m_u.f1 = f1; - m_u.f2 = f2; - m_u.f3 = f3; - } - - union { - unsigned value; - struct { - unsigned f0 : 4; - unsigned f1 : 4; - unsigned f2 : 4; - unsigned f3 : 4; - }; - } m_u; - }; - - class ARMInstructionFormatter; - - // false means else! - bool ifThenElseConditionBit(Condition condition, bool isIf) - { - return isIf ? (condition & 1) : !(condition & 1); - } - uint8_t ifThenElse(Condition condition, bool inst2if, bool inst3if, bool inst4if) - { - int mask = (ifThenElseConditionBit(condition, inst2if) << 3) - | (ifThenElseConditionBit(condition, inst3if) << 2) - | (ifThenElseConditionBit(condition, inst4if) << 1) - | 1; - ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); - return (condition << 4) | mask; - } - uint8_t ifThenElse(Condition condition, bool inst2if, bool inst3if) - { - int mask = (ifThenElseConditionBit(condition, inst2if) << 3) - | (ifThenElseConditionBit(condition, inst3if) << 2) - | 2; - ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); - return (condition << 4) | mask; - } - uint8_t ifThenElse(Condition condition, bool inst2if) - { - int mask = (ifThenElseConditionBit(condition, inst2if) << 3) - | 4; - ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); - return (condition << 4) | mask; - } - - uint8_t ifThenElse(Condition condition) - { - int mask = 8; - return (condition << 4) | mask; - } - -public: - - void adc(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - // Rd can only be SP if Rn is also SP. - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isEncodedImm()); - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADC_imm, rn, rd, imm); - } - - void add(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - // Rd can only be SP if Rn is also SP. - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isValid()); - - if (rn == ARMRegisters::sp) { - ASSERT(!(imm.getUInt16() & 3)); - if (!(rd & 8) && imm.isUInt10()) { - m_formatter.oneWordOp5Reg3Imm8(OP_ADD_SP_imm_T1, rd, static_cast(imm.getUInt10() >> 2)); - return; - } else if ((rd == ARMRegisters::sp) && imm.isUInt9()) { - m_formatter.oneWordOp9Imm7(OP_ADD_SP_imm_T2, static_cast(imm.getUInt9() >> 2)); - return; - } - } else if (!((rd | rn) & 8)) { - if (imm.isUInt3()) { - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); - return; - } else if ((rd == rn) && imm.isUInt8()) { - m_formatter.oneWordOp5Reg3Imm8(OP_ADD_imm_T2, rd, imm.getUInt8()); - return; - } - } - - if (imm.isEncodedImm()) - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_imm_T3, rn, rd, imm); - else { - ASSERT(imm.isUInt12()); - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_imm_T4, rn, rd, imm); - } - } - - ALWAYS_INLINE void add(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_ADD_reg_T3, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - // NOTE: In an IT block, add doesn't modify the flags register. - ALWAYS_INLINE void add(RegisterID rd, RegisterID rn, RegisterID rm) - { - if (rd == rn) - m_formatter.oneWordOp8RegReg143(OP_ADD_reg_T2, rm, rd); - else if (rd == rm) - m_formatter.oneWordOp8RegReg143(OP_ADD_reg_T2, rn, rd); - else if (!((rd | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_reg_T1, rm, rn, rd); - else - add(rd, rn, rm, ShiftTypeAndAmount()); - } - - // Not allowed in an IT (if then) block. - ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - // Rd can only be SP if Rn is also SP. - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isEncodedImm()); - - if (!((rd | rn) & 8)) { - if (imm.isUInt3()) { - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); - return; - } else if ((rd == rn) && imm.isUInt8()) { - m_formatter.oneWordOp5Reg3Imm8(OP_ADD_imm_T2, rd, imm.getUInt8()); - return; - } - } - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_S_imm_T3, rn, rd, imm); - } - - // Not allowed in an IT (if then) block? - ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_ADD_S_reg_T3, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - // Not allowed in an IT (if then) block. - ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, RegisterID rm) - { - if (!((rd | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_reg_T1, rm, rn, rd); - else - add_S(rd, rn, rm, ShiftTypeAndAmount()); - } - - ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(imm.isEncodedImm()); - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_AND_imm_T1, rn, rd, imm); - } - - ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_AND_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, RegisterID rm) - { - if ((rd == rn) && !((rd | rm) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_AND_reg_T1, rm, rd); - else if ((rd == rm) && !((rd | rn) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_AND_reg_T1, rn, rd); - else - ARM_and(rd, rn, rm, ShiftTypeAndAmount()); - } - - ALWAYS_INLINE void asr(RegisterID rd, RegisterID rm, int32_t shiftAmount) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rm)); - ShiftTypeAndAmount shift(SRType_ASR, shiftAmount); - m_formatter.twoWordOp16FourFours(OP_ASR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - ALWAYS_INLINE void asr(RegisterID rd, RegisterID rn, RegisterID rm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_ASR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); - } - - // Only allowed in IT (if then) block if last instruction. - ALWAYS_INLINE AssemblerLabel b() - { - m_formatter.twoWordOp16Op16(OP_B_T4a, OP_B_T4b); - return m_formatter.label(); - } - - // Only allowed in IT (if then) block if last instruction. - ALWAYS_INLINE AssemblerLabel blx(RegisterID rm) - { - ASSERT(rm != ARMRegisters::pc); - m_formatter.oneWordOp8RegReg143(OP_BLX, rm, (RegisterID)8); - return m_formatter.label(); - } - - // Only allowed in IT (if then) block if last instruction. - ALWAYS_INLINE AssemblerLabel bx(RegisterID rm) - { - m_formatter.oneWordOp8RegReg143(OP_BX, rm, (RegisterID)0); - return m_formatter.label(); - } - - void bkpt(uint8_t imm = 0) - { - m_formatter.oneWordOp8Imm8(OP_BKPT, imm); - } - - ALWAYS_INLINE void clz(RegisterID rd, RegisterID rm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_CLZ, rm, FourFours(0xf, rd, 8, rm)); - } - - ALWAYS_INLINE void cmn(RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isEncodedImm()); - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_CMN_imm, rn, (RegisterID)0xf, imm); - } - - ALWAYS_INLINE void cmp(RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isEncodedImm()); - - if (!(rn & 8) && imm.isUInt8()) - m_formatter.oneWordOp5Reg3Imm8(OP_CMP_imm_T1, rn, imm.getUInt8()); - else - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_CMP_imm_T2, rn, (RegisterID)0xf, imm); - } - - ALWAYS_INLINE void cmp(RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_CMP_reg_T2, rn, FourFours(shift.hi4(), 0xf, shift.lo4(), rm)); - } - - ALWAYS_INLINE void cmp(RegisterID rn, RegisterID rm) - { - if ((rn | rm) & 8) - cmp(rn, rm, ShiftTypeAndAmount()); - else - m_formatter.oneWordOp10Reg3Reg3(OP_CMP_reg_T1, rm, rn); - } - - // xor is not spelled with an 'e'. :-( - ALWAYS_INLINE void eor(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(imm.isEncodedImm()); - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_EOR_imm_T1, rn, rd, imm); - } - - // xor is not spelled with an 'e'. :-( - ALWAYS_INLINE void eor(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_EOR_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - // xor is not spelled with an 'e'. :-( - void eor(RegisterID rd, RegisterID rn, RegisterID rm) - { - if ((rd == rn) && !((rd | rm) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_EOR_reg_T1, rm, rd); - else if ((rd == rm) && !((rd | rn) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_EOR_reg_T1, rn, rd); - else - eor(rd, rn, rm, ShiftTypeAndAmount()); - } - - ALWAYS_INLINE void it(Condition cond) - { - m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond)); - } - - ALWAYS_INLINE void it(Condition cond, bool inst2if) - { - m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if)); - } - - ALWAYS_INLINE void it(Condition cond, bool inst2if, bool inst3if) - { - m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if, inst3if)); - } - - ALWAYS_INLINE void it(Condition cond, bool inst2if, bool inst3if, bool inst4if) - { - m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if, inst3if, inst4if)); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rn != ARMRegisters::pc); // LDR (literal) - ASSERT(imm.isUInt12()); - - if (!((rt | rn) & 8) && imm.isUInt7()) - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDR_imm_T1, imm.getUInt7() >> 2, rn, rt); - else if ((rn == ARMRegisters::sp) && !(rt & 8) && imm.isUInt10()) - m_formatter.oneWordOp5Reg3Imm8(OP_LDR_imm_T2, rt, static_cast(imm.getUInt10() >> 2)); - else - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T3, rn, rt, imm.getUInt12()); - } - - ALWAYS_INLINE void ldrWide8BitImmediate(RegisterID rt, RegisterID rn, uint8_t immediate) - { - ASSERT(rn != ARMRegisters::pc); - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T3, rn, rt, immediate); - } - - ALWAYS_INLINE void ldrCompact(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rn != ARMRegisters::pc); // LDR (literal) - ASSERT(imm.isUInt7()); - ASSERT(!((rt | rn) & 8)); - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDR_imm_T1, imm.getUInt7() >> 2, rn, rt); - } - - // If index is set, this is a regular offset or a pre-indexed load; - // if index is not set then is is a post-index load. - // - // If wback is set rn is updated - this is a pre or post index load, - // if wback is not set this is a regular offset memory access. - // - // (-255 <= offset <= 255) - // _reg = REG[rn] - // _tmp = _reg + offset - // MEM[index ? _tmp : _reg] = REG[rt] - // if (wback) REG[rn] = _tmp - ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(index || wback); - ASSERT(!wback | (rt != rn)); - - bool add = true; - if (offset < 0) { - add = false; - offset = -offset; - } - ASSERT((offset & ~0xff) == 0); - - offset |= (wback << 8); - offset |= (add << 9); - offset |= (index << 10); - offset |= (1 << 11); - - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T4, rn, rt, offset); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(rn != ARMRegisters::pc); // LDR (literal) - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDR_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_LDR_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rn != ARMRegisters::pc); // LDR (literal) - ASSERT(imm.isUInt12()); - - if (!((rt | rn) & 8) && imm.isUInt6()) - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDRH_imm_T1, imm.getUInt6() >> 2, rn, rt); - else - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRH_imm_T2, rn, rt, imm.getUInt12()); - } - - // If index is set, this is a regular offset or a pre-indexed load; - // if index is not set then is is a post-index load. - // - // If wback is set rn is updated - this is a pre or post index load, - // if wback is not set this is a regular offset memory access. - // - // (-255 <= offset <= 255) - // _reg = REG[rn] - // _tmp = _reg + offset - // MEM[index ? _tmp : _reg] = REG[rt] - // if (wback) REG[rn] = _tmp - ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(index || wback); - ASSERT(!wback | (rt != rn)); - - bool add = true; - if (offset < 0) { - add = false; - offset = -offset; - } - ASSERT((offset & ~0xff) == 0); - - offset |= (wback << 8); - offset |= (add << 9); - offset |= (index << 10); - offset |= (1 << 11); - - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRH_imm_T3, rn, rt, offset); - } - - ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(!BadReg(rt)); // Memory hint - ASSERT(rn != ARMRegisters::pc); // LDRH (literal) - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRH_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_LDRH_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - void ldrb(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rn != ARMRegisters::pc); // LDR (literal) - ASSERT(imm.isUInt12()); - - if (!((rt | rn) & 8) && imm.isUInt5()) - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDRB_imm_T1, imm.getUInt5(), rn, rt); - else - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRB_imm_T2, rn, rt, imm.getUInt12()); - } - - void ldrb(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(index || wback); - ASSERT(!wback | (rt != rn)); - - bool add = true; - if (offset < 0) { - add = false; - offset = -offset; - } - - ASSERT(!(offset & ~0xff)); - - offset |= (wback << 8); - offset |= (add << 9); - offset |= (index << 10); - offset |= (1 << 11); - - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRB_imm_T3, rn, rt, offset); - } - - ALWAYS_INLINE void ldrb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(rn != ARMRegisters::pc); // LDR (literal) - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRB_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_LDRB_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - void ldrsb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRSB_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_LDRSB_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - void ldrsh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRSH_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_LDRSH_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - void lsl(RegisterID rd, RegisterID rm, int32_t shiftAmount) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rm)); - ShiftTypeAndAmount shift(SRType_LSL, shiftAmount); - m_formatter.twoWordOp16FourFours(OP_LSL_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - ALWAYS_INLINE void lsl(RegisterID rd, RegisterID rn, RegisterID rm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_LSL_reg_T2, rn, FourFours(0xf, rd, 0, rm)); - } - - ALWAYS_INLINE void lsr(RegisterID rd, RegisterID rm, int32_t shiftAmount) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rm)); - ShiftTypeAndAmount shift(SRType_LSR, shiftAmount); - m_formatter.twoWordOp16FourFours(OP_LSR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - ALWAYS_INLINE void lsr(RegisterID rd, RegisterID rn, RegisterID rm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_LSR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); - } - - ALWAYS_INLINE void movT3(RegisterID rd, ARMThumbImmediate imm) - { - ASSERT(imm.isValid()); - ASSERT(!imm.isEncodedImm()); - ASSERT(!BadReg(rd)); - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOV_imm_T3, imm.m_value.imm4, rd, imm); - } - - static void revertJumpTo_movT3(void* instructionStart, RegisterID rd, ARMThumbImmediate imm) - { - ASSERT(imm.isValid()); - ASSERT(!imm.isEncodedImm()); - ASSERT(!BadReg(rd)); - - uint16_t* address = static_cast(instructionStart); - address[0] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, imm); - address[1] = twoWordOp5i6Imm4Reg4EncodedImmSecond(rd, imm); - cacheFlush(address, sizeof(uint16_t) * 2); - } - - ALWAYS_INLINE void mov(RegisterID rd, ARMThumbImmediate imm) - { - ASSERT(imm.isValid()); - ASSERT(!BadReg(rd)); - - if ((rd < 8) && imm.isUInt8()) - m_formatter.oneWordOp5Reg3Imm8(OP_MOV_imm_T1, rd, imm.getUInt8()); - else if (imm.isEncodedImm()) - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOV_imm_T2, 0xf, rd, imm); - else - movT3(rd, imm); - } - - ALWAYS_INLINE void mov(RegisterID rd, RegisterID rm) - { - m_formatter.oneWordOp8RegReg143(OP_MOV_reg_T1, rm, rd); - } - - ALWAYS_INLINE void movt(RegisterID rd, ARMThumbImmediate imm) - { - ASSERT(imm.isUInt16()); - ASSERT(!BadReg(rd)); - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOVT, imm.m_value.imm4, rd, imm); - } - - ALWAYS_INLINE void mvn(RegisterID rd, ARMThumbImmediate imm) - { - ASSERT(imm.isEncodedImm()); - ASSERT(!BadReg(rd)); - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MVN_imm, 0xf, rd, imm); - } - - ALWAYS_INLINE void mvn(RegisterID rd, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp16FourFours(OP_MVN_reg_T2, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - ALWAYS_INLINE void mvn(RegisterID rd, RegisterID rm) - { - if (!((rd | rm) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_MVN_reg_T1, rm, rd); - else - mvn(rd, rm, ShiftTypeAndAmount()); - } - - ALWAYS_INLINE void neg(RegisterID rd, RegisterID rm) - { - ARMThumbImmediate zero = ARMThumbImmediate::makeUInt12(0); - sub(rd, zero, rm); - } - - ALWAYS_INLINE void orr(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(imm.isEncodedImm()); - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ORR_imm_T1, rn, rd, imm); - } - - ALWAYS_INLINE void orr(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_ORR_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - void orr(RegisterID rd, RegisterID rn, RegisterID rm) - { - if ((rd == rn) && !((rd | rm) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rm, rd); - else if ((rd == rm) && !((rd | rn) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rn, rd); - else - orr(rd, rn, rm, ShiftTypeAndAmount()); - } - - ALWAYS_INLINE void orr_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_ORR_S_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - void orr_S(RegisterID rd, RegisterID rn, RegisterID rm) - { - if ((rd == rn) && !((rd | rm) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rm, rd); - else if ((rd == rm) && !((rd | rn) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rn, rd); - else - orr_S(rd, rn, rm, ShiftTypeAndAmount()); - } - - ALWAYS_INLINE void ror(RegisterID rd, RegisterID rm, int32_t shiftAmount) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rm)); - ShiftTypeAndAmount shift(SRType_ROR, shiftAmount); - m_formatter.twoWordOp16FourFours(OP_ROR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - ALWAYS_INLINE void ror(RegisterID rd, RegisterID rn, RegisterID rm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_ROR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); - } - - ALWAYS_INLINE void smull(RegisterID rdLo, RegisterID rdHi, RegisterID rn, RegisterID rm) - { - ASSERT(!BadReg(rdLo)); - ASSERT(!BadReg(rdHi)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - ASSERT(rdLo != rdHi); - m_formatter.twoWordOp12Reg4FourFours(OP_SMULL_T1, rn, FourFours(rdLo, rdHi, 0, rm)); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isUInt12()); - - if (!((rt | rn) & 8) && imm.isUInt7()) - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STR_imm_T1, imm.getUInt7() >> 2, rn, rt); - else if ((rn == ARMRegisters::sp) && !(rt & 8) && imm.isUInt10()) - m_formatter.oneWordOp5Reg3Imm8(OP_STR_imm_T2, rt, static_cast(imm.getUInt10() >> 2)); - else - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STR_imm_T3, rn, rt, imm.getUInt12()); - } - - // If index is set, this is a regular offset or a pre-indexed store; - // if index is not set then is is a post-index store. - // - // If wback is set rn is updated - this is a pre or post index store, - // if wback is not set this is a regular offset memory access. - // - // (-255 <= offset <= 255) - // _reg = REG[rn] - // _tmp = _reg + offset - // MEM[index ? _tmp : _reg] = REG[rt] - // if (wback) REG[rn] = _tmp - ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(index || wback); - ASSERT(!wback | (rt != rn)); - - bool add = true; - if (offset < 0) { - add = false; - offset = -offset; - } - ASSERT((offset & ~0xff) == 0); - - offset |= (wback << 8); - offset |= (add << 9); - offset |= (index << 10); - offset |= (1 << 11); - - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STR_imm_T4, rn, rt, offset); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STR_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_STR_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isUInt12()); - - if (!((rt | rn) & 8) && imm.isUInt7()) - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STRB_imm_T1, imm.getUInt7() >> 2, rn, rt); - else - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRB_imm_T2, rn, rt, imm.getUInt12()); - } - - // If index is set, this is a regular offset or a pre-indexed store; - // if index is not set then is is a post-index store. - // - // If wback is set rn is updated - this is a pre or post index store, - // if wback is not set this is a regular offset memory access. - // - // (-255 <= offset <= 255) - // _reg = REG[rn] - // _tmp = _reg + offset - // MEM[index ? _tmp : _reg] = REG[rt] - // if (wback) REG[rn] = _tmp - ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(index || wback); - ASSERT(!wback | (rt != rn)); - - bool add = true; - if (offset < 0) { - add = false; - offset = -offset; - } - ASSERT((offset & ~0xff) == 0); - - offset |= (wback << 8); - offset |= (add << 9); - offset |= (index << 10); - offset |= (1 << 11); - - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRB_imm_T3, rn, rt, offset); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STRB_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_STRB_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isUInt12()); - - if (!((rt | rn) & 8) && imm.isUInt7()) - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STRH_imm_T1, imm.getUInt7() >> 2, rn, rt); - else - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRH_imm_T2, rn, rt, imm.getUInt12()); - } - - // If index is set, this is a regular offset or a pre-indexed store; - // if index is not set then is is a post-index store. - // - // If wback is set rn is updated - this is a pre or post index store, - // if wback is not set this is a regular offset memory access. - // - // (-255 <= offset <= 255) - // _reg = REG[rn] - // _tmp = _reg + offset - // MEM[index ? _tmp : _reg] = REG[rt] - // if (wback) REG[rn] = _tmp - ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(index || wback); - ASSERT(!wback | (rt != rn)); - - bool add = true; - if (offset < 0) { - add = false; - offset = -offset; - } - ASSERT(!(offset & ~0xff)); - - offset |= (wback << 8); - offset |= (add << 9); - offset |= (index << 10); - offset |= (1 << 11); - - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRH_imm_T3, rn, rt, offset); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STRH_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_STRH_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - // Rd can only be SP if Rn is also SP. - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isValid()); - - if ((rn == ARMRegisters::sp) && (rd == ARMRegisters::sp) && imm.isUInt9()) { - ASSERT(!(imm.getUInt16() & 3)); - m_formatter.oneWordOp9Imm7(OP_SUB_SP_imm_T1, static_cast(imm.getUInt9() >> 2)); - return; - } else if (!((rd | rn) & 8)) { - if (imm.isUInt3()) { - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); - return; - } else if ((rd == rn) && imm.isUInt8()) { - m_formatter.oneWordOp5Reg3Imm8(OP_SUB_imm_T2, rd, imm.getUInt8()); - return; - } - } - - if (imm.isEncodedImm()) - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_imm_T3, rn, rd, imm); - else { - ASSERT(imm.isUInt12()); - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_imm_T4, rn, rd, imm); - } - } - - ALWAYS_INLINE void sub(RegisterID rd, ARMThumbImmediate imm, RegisterID rn) - { - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isValid()); - ASSERT(imm.isUInt12()); - - if (!((rd | rn) & 8) && !imm.getUInt12()) - m_formatter.oneWordOp10Reg3Reg3(OP_RSB_imm_T1, rn, rd); - else - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_RSB_imm_T2, rn, rd, imm); - } - - ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_SUB_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - // NOTE: In an IT block, add doesn't modify the flags register. - ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, RegisterID rm) - { - if (!((rd | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_reg_T1, rm, rn, rd); - else - sub(rd, rn, rm, ShiftTypeAndAmount()); - } - - // Not allowed in an IT (if then) block. - void sub_S(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - // Rd can only be SP if Rn is also SP. - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isValid()); - - if ((rn == ARMRegisters::sp) && (rd == ARMRegisters::sp) && imm.isUInt9()) { - ASSERT(!(imm.getUInt16() & 3)); - m_formatter.oneWordOp9Imm7(OP_SUB_SP_imm_T1, static_cast(imm.getUInt9() >> 2)); - return; - } else if (!((rd | rn) & 8)) { - if (imm.isUInt3()) { - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); - return; - } else if ((rd == rn) && imm.isUInt8()) { - m_formatter.oneWordOp5Reg3Imm8(OP_SUB_imm_T2, rd, imm.getUInt8()); - return; - } - } - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_S_imm_T3, rn, rd, imm); - } - - ALWAYS_INLINE void sub_S(RegisterID rd, ARMThumbImmediate imm, RegisterID rn) - { - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isValid()); - ASSERT(imm.isUInt12()); - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_RSB_S_imm_T2, rn, rd, imm); - } - - // Not allowed in an IT (if then) block? - ALWAYS_INLINE void sub_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_SUB_S_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - // Not allowed in an IT (if then) block. - ALWAYS_INLINE void sub_S(RegisterID rd, RegisterID rn, RegisterID rm) - { - if (!((rd | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_reg_T1, rm, rn, rd); - else - sub_S(rd, rn, rm, ShiftTypeAndAmount()); - } - - ALWAYS_INLINE void tst(RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(!BadReg(rn)); - ASSERT(imm.isEncodedImm()); - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_TST_imm, rn, (RegisterID)0xf, imm); - } - - ALWAYS_INLINE void tst(RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_TST_reg_T2, rn, FourFours(shift.hi4(), 0xf, shift.lo4(), rm)); - } - - ALWAYS_INLINE void tst(RegisterID rn, RegisterID rm) - { - if ((rn | rm) & 8) - tst(rn, rm, ShiftTypeAndAmount()); - else - m_formatter.oneWordOp10Reg3Reg3(OP_TST_reg_T1, rm, rn); - } - - ALWAYS_INLINE void ubfx(RegisterID rd, RegisterID rn, unsigned lsb, unsigned width) - { - ASSERT(lsb < 32); - ASSERT((width >= 1) && (width <= 32)); - ASSERT((lsb + width) <= 32); - m_formatter.twoWordOp12Reg40Imm3Reg4Imm20Imm5(OP_UBFX_T1, rd, rn, (lsb & 0x1c) << 10, (lsb & 0x3) << 6, (width - 1) & 0x1f); - } - - void vadd(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VADD_T2, OP_VADD_T2b, true, rn, rd, rm); - } - - void vcmp(FPDoubleRegisterID rd, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VCMP, OP_VCMPb, true, VFPOperand(4), rd, rm); - } - - void vcmpz(FPDoubleRegisterID rd) - { - m_formatter.vfpOp(OP_VCMP, OP_VCMPb, true, VFPOperand(5), rd, VFPOperand(0)); - } - - void vcvt_signedToFloatingPoint(FPDoubleRegisterID rd, FPSingleRegisterID rm) - { - // boolean values are 64bit (toInt, unsigned, roundZero) - m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(false, false, false), rd, rm); - } - - void vcvt_floatingPointToSigned(FPSingleRegisterID rd, FPDoubleRegisterID rm) - { - // boolean values are 64bit (toInt, unsigned, roundZero) - m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(true, false, true), rd, rm); - } - - void vcvt_floatingPointToUnsigned(FPSingleRegisterID rd, FPDoubleRegisterID rm) - { - // boolean values are 64bit (toInt, unsigned, roundZero) - m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(true, true, true), rd, rm); - } - - void vdiv(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VDIV, OP_VDIVb, true, rn, rd, rm); - } - - void vldr(FPDoubleRegisterID rd, RegisterID rn, int32_t imm) - { - m_formatter.vfpMemOp(OP_VLDR, OP_VLDRb, true, rn, rd, imm); - } - - void flds(FPSingleRegisterID rd, RegisterID rn, int32_t imm) - { - m_formatter.vfpMemOp(OP_FLDS, OP_FLDSb, false, rn, rd, imm); - } - - void vmov(RegisterID rd, FPSingleRegisterID rn) - { - ASSERT(!BadReg(rd)); - m_formatter.vfpOp(OP_VMOV_StoC, OP_VMOV_StoCb, false, rn, rd, VFPOperand(0)); - } - - void vmov(FPSingleRegisterID rd, RegisterID rn) - { - ASSERT(!BadReg(rn)); - m_formatter.vfpOp(OP_VMOV_CtoS, OP_VMOV_CtoSb, false, rd, rn, VFPOperand(0)); - } - - void vmov(RegisterID rd1, RegisterID rd2, FPDoubleRegisterID rn) - { - ASSERT(!BadReg(rd1)); - ASSERT(!BadReg(rd2)); - m_formatter.vfpOp(OP_VMOV_DtoC, OP_VMOV_DtoCb, true, rd2, VFPOperand(rd1 | 16), rn); - } - - void vmov(FPDoubleRegisterID rd, RegisterID rn1, RegisterID rn2) - { - ASSERT(!BadReg(rn1)); - ASSERT(!BadReg(rn2)); - m_formatter.vfpOp(OP_VMOV_CtoD, OP_VMOV_CtoDb, true, rn2, VFPOperand(rn1 | 16), rd); - } - - void vmov(FPDoubleRegisterID rd, FPDoubleRegisterID rn) - { - m_formatter.vfpOp(OP_VMOV_T2, OP_VMOV_T2b, true, VFPOperand(0), rd, rn); - } - - void vmrs(RegisterID reg = ARMRegisters::pc) - { - ASSERT(reg != ARMRegisters::sp); - m_formatter.vfpOp(OP_VMRS, OP_VMRSb, false, VFPOperand(1), VFPOperand(0x10 | reg), VFPOperand(0)); - } - - void vmul(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VMUL_T2, OP_VMUL_T2b, true, rn, rd, rm); - } - - void vstr(FPDoubleRegisterID rd, RegisterID rn, int32_t imm) - { - m_formatter.vfpMemOp(OP_VSTR, OP_VSTRb, true, rn, rd, imm); - } - - void fsts(FPSingleRegisterID rd, RegisterID rn, int32_t imm) - { - m_formatter.vfpMemOp(OP_FSTS, OP_FSTSb, false, rn, rd, imm); - } - - void vsub(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VSUB_T2, OP_VSUB_T2b, true, rn, rd, rm); - } - - void vabs(FPDoubleRegisterID rd, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VABS_T2, OP_VABS_T2b, true, VFPOperand(16), rd, rm); - } - - void vneg(FPDoubleRegisterID rd, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VNEG_T2, OP_VNEG_T2b, true, VFPOperand(1), rd, rm); - } - - void vsqrt(FPDoubleRegisterID rd, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VSQRT_T1, OP_VSQRT_T1b, true, VFPOperand(17), rd, rm); - } - - void vcvtds(FPDoubleRegisterID rd, FPSingleRegisterID rm) - { - m_formatter.vfpOp(OP_VCVTDS_T1, OP_VCVTDS_T1b, false, VFPOperand(23), rd, rm); - } - - void vcvtsd(FPSingleRegisterID rd, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VCVTSD_T1, OP_VCVTSD_T1b, true, VFPOperand(23), rd, rm); - } - - void nop() - { - m_formatter.oneWordOp8Imm8(OP_NOP_T1, 0); - } - - AssemblerLabel labelIgnoringWatchpoints() - { - return m_formatter.label(); - } - - AssemblerLabel labelForWatchpoint() - { - AssemblerLabel result = m_formatter.label(); - if (static_cast(result.m_offset) != m_indexOfLastWatchpoint) - result = label(); - m_indexOfLastWatchpoint = result.m_offset; - m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); - return result; - } - - AssemblerLabel label() - { - AssemblerLabel result = m_formatter.label(); - while (UNLIKELY(static_cast(result.m_offset) < m_indexOfTailOfLastWatchpoint)) { - nop(); - result = m_formatter.label(); - } - return result; - } - - AssemblerLabel align(int alignment) - { - while (!m_formatter.isAligned(alignment)) - bkpt(); - - return label(); - } - - static void* getRelocatedAddress(void* code, AssemblerLabel label) - { - ASSERT(label.isSet()); - return reinterpret_cast(reinterpret_cast(code) + label.m_offset); - } - - static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) - { - return b.m_offset - a.m_offset; - } - - int executableOffsetFor(int location) - { - if (!location) - return 0; - return static_cast(m_formatter.data())[location / sizeof(int32_t) - 1]; - } - - int jumpSizeDelta(JumpType jumpType, JumpLinkType jumpLinkType) { return JUMP_ENUM_SIZE(jumpType) - JUMP_ENUM_SIZE(jumpLinkType); } - - // Assembler admin methods: - - static ALWAYS_INLINE bool linkRecordSourceComparator(const LinkRecord& a, const LinkRecord& b) - { - return a.from() < b.from(); - } - - bool canCompact(JumpType jumpType) - { - // The following cannot be compacted: - // JumpFixed: represents custom jump sequence - // JumpNoConditionFixedSize: represents unconditional jump that must remain a fixed size - // JumpConditionFixedSize: represents conditional jump that must remain a fixed size - return (jumpType == JumpNoCondition) || (jumpType == JumpCondition); - } - - JumpLinkType computeJumpType(JumpType jumpType, const uint8_t* from, const uint8_t* to) - { - if (jumpType == JumpFixed) - return LinkInvalid; - - // for patchable jump we must leave space for the longest code sequence - if (jumpType == JumpNoConditionFixedSize) - return LinkBX; - if (jumpType == JumpConditionFixedSize) - return LinkConditionalBX; - - const int paddingSize = JUMP_ENUM_SIZE(jumpType); - - if (jumpType == JumpCondition) { - // 2-byte conditional T1 - const uint16_t* jumpT1Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT1))); - if (canBeJumpT1(jumpT1Location, to)) - return LinkJumpT1; - // 4-byte conditional T3 - const uint16_t* jumpT3Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT3))); - if (canBeJumpT3(jumpT3Location, to)) - return LinkJumpT3; - // 4-byte conditional T4 with IT - const uint16_t* conditionalJumpT4Location = - reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkConditionalJumpT4))); - if (canBeJumpT4(conditionalJumpT4Location, to)) - return LinkConditionalJumpT4; - } else { - // 2-byte unconditional T2 - const uint16_t* jumpT2Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT2))); - if (canBeJumpT2(jumpT2Location, to)) - return LinkJumpT2; - // 4-byte unconditional T4 - const uint16_t* jumpT4Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT4))); - if (canBeJumpT4(jumpT4Location, to)) - return LinkJumpT4; - // use long jump sequence - return LinkBX; - } - - ASSERT(jumpType == JumpCondition); - return LinkConditionalBX; - } - - JumpLinkType computeJumpType(LinkRecord& record, const uint8_t* from, const uint8_t* to) - { - JumpLinkType linkType = computeJumpType(record.type(), from, to); - record.setLinkType(linkType); - return linkType; - } - - void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset) - { - int32_t ptr = regionStart / sizeof(int32_t); - const int32_t end = regionEnd / sizeof(int32_t); - int32_t* offsets = static_cast(m_formatter.data()); - while (ptr < end) - offsets[ptr++] = offset; - } - - Vector& jumpsToLink() - { - std::sort(m_jumpsToLink.begin(), m_jumpsToLink.end(), linkRecordSourceComparator); - return m_jumpsToLink; - } - - void ALWAYS_INLINE link(LinkRecord& record, uint8_t* from, uint8_t* to) - { - switch (record.linkType()) { - case LinkJumpT1: - linkJumpT1(record.condition(), reinterpret_cast_ptr(from), to); - break; - case LinkJumpT2: - linkJumpT2(reinterpret_cast_ptr(from), to); - break; - case LinkJumpT3: - linkJumpT3(record.condition(), reinterpret_cast_ptr(from), to); - break; - case LinkJumpT4: - linkJumpT4(reinterpret_cast_ptr(from), to); - break; - case LinkConditionalJumpT4: - linkConditionalJumpT4(record.condition(), reinterpret_cast_ptr(from), to); - break; - case LinkConditionalBX: - linkConditionalBX(record.condition(), reinterpret_cast_ptr(from), to); - break; - case LinkBX: - linkBX(reinterpret_cast_ptr(from), to); - break; - default: - ASSERT_NOT_REACHED(); - break; - } - } - - void* unlinkedCode() { return m_formatter.data(); } - size_t codeSize() const { return m_formatter.codeSize(); } - - static unsigned getCallReturnOffset(AssemblerLabel call) - { - ASSERT(call.isSet()); - return call.m_offset; - } - - // Linking & patching: - // - // 'link' and 'patch' methods are for use on unprotected code - such as the code - // within the AssemblerBuffer, and code being patched by the patch buffer. Once - // code has been finalized it is (platform support permitting) within a non- - // writable region of memory; to modify the code in an execute-only execuable - // pool the 'repatch' and 'relink' methods should be used. - - void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type, Condition condition) - { - ASSERT(to.isSet()); - ASSERT(from.isSet()); - m_jumpsToLink.append(LinkRecord(from.m_offset, to.m_offset, type, condition)); - } - - static void linkJump(void* code, AssemblerLabel from, void* to) - { - ASSERT(from.isSet()); - - uint16_t* location = reinterpret_cast(reinterpret_cast(code) + from.m_offset); - linkJumpAbsolute(location, to); - } - - static void linkCall(void* code, AssemblerLabel from, void* to) - { - ASSERT(!(reinterpret_cast(code) & 1)); - ASSERT(from.isSet()); - ASSERT(reinterpret_cast(to) & 1); - - setPointer(reinterpret_cast(reinterpret_cast(code) + from.m_offset) - 1, to, false); - } - - static void linkPointer(void* code, AssemblerLabel where, void* value) - { - setPointer(reinterpret_cast(code) + where.m_offset, value, false); - } - - static void relinkJump(void* from, void* to) - { - ASSERT(!(reinterpret_cast(from) & 1)); - ASSERT(!(reinterpret_cast(to) & 1)); - - linkJumpAbsolute(reinterpret_cast(from), to); - - cacheFlush(reinterpret_cast(from) - 5, 5 * sizeof(uint16_t)); - } - - static void relinkCall(void* from, void* to) - { - ASSERT(!(reinterpret_cast(from) & 1)); - ASSERT(reinterpret_cast(to) & 1); - - setPointer(reinterpret_cast(from) - 1, to, true); - } - - static void* readCallTarget(void* from) - { - return readPointer(reinterpret_cast(from) - 1); - } - - static void repatchInt32(void* where, int32_t value) - { - ASSERT(!(reinterpret_cast(where) & 1)); - - setInt32(where, value, true); - } - - static void repatchCompact(void* where, int32_t offset) - { - ASSERT(offset >= -255 && offset <= 255); - - bool add = true; - if (offset < 0) { - add = false; - offset = -offset; - } - - offset |= (add << 9); - offset |= (1 << 10); - offset |= (1 << 11); - - uint16_t* location = reinterpret_cast(where); - location[1] &= ~((1 << 12) - 1); - location[1] |= offset; - cacheFlush(location, sizeof(uint16_t) * 2); - } - - static void repatchPointer(void* where, void* value) - { - ASSERT(!(reinterpret_cast(where) & 1)); - - setPointer(where, value, true); - } - - static void* readPointer(void* where) - { - return reinterpret_cast(readInt32(where)); - } - - static void replaceWithJump(void* instructionStart, void* to) - { - ASSERT(!(bitwise_cast(instructionStart) & 1)); - ASSERT(!(bitwise_cast(to) & 1)); - uint16_t* ptr = reinterpret_cast(instructionStart) + 2; - - linkJumpT4(ptr, to); - cacheFlush(ptr - 2, sizeof(uint16_t) * 2); - } - - static ptrdiff_t maxJumpReplacementSize() - { - return 4; - } - - static void replaceWithLoad(void* instructionStart) - { - ASSERT(!(bitwise_cast(instructionStart) & 1)); - uint16_t* ptr = reinterpret_cast(instructionStart); - switch (ptr[0] & 0xFFF0) { - case OP_LDR_imm_T3: - break; - case OP_ADD_imm_T3: - ASSERT(!(ptr[1] & 0xF000)); - ptr[0] &= 0x000F; - ptr[0] |= OP_LDR_imm_T3; - ptr[1] |= (ptr[1] & 0x0F00) << 4; - ptr[1] &= 0xF0FF; - cacheFlush(ptr, sizeof(uint16_t) * 2); - break; - default: - ASSERT_NOT_REACHED(); - } - } - - static void replaceWithAddressComputation(void* instructionStart) - { - ASSERT(!(bitwise_cast(instructionStart) & 1)); - uint16_t* ptr = reinterpret_cast(instructionStart); - switch (ptr[0] & 0xFFF0) { - case OP_LDR_imm_T3: - ASSERT(!(ptr[1] & 0x0F00)); - ptr[0] &= 0x000F; - ptr[0] |= OP_ADD_imm_T3; - ptr[1] |= (ptr[1] & 0xF000) >> 4; - ptr[1] &= 0x0FFF; - cacheFlush(ptr, sizeof(uint16_t) * 2); - break; - case OP_ADD_imm_T3: - break; - default: - ASSERT_NOT_REACHED(); - } - } - - unsigned debugOffset() { return m_formatter.debugOffset(); } - - static void cacheFlush(void* code, size_t size) - { -#if OS(IOS) - sys_cache_control(kCacheFunctionPrepareForExecution, code, size); -#elif OS(LINUX) - asm volatile( - "push {r7}\n" - "mov r0, %0\n" - "mov r1, %1\n" - "movw r7, #0x2\n" - "movt r7, #0xf\n" - "movs r2, #0x0\n" - "svc 0x0\n" - "pop {r7}\n" - : - : "r" (code), "r" (reinterpret_cast(code) + size) - : "r0", "r1", "r2"); -#elif OS(WINCE) - CacheRangeFlush(code, size, CACHE_SYNC_ALL); -#elif OS(QNX) -#if !ENABLE(ASSEMBLER_WX_EXCLUSIVE) - msync(code, size, MS_INVALIDATE_ICACHE); -#else - UNUSED_PARAM(code); - UNUSED_PARAM(size); -#endif -#else -#error "The cacheFlush support is missing on this platform." -#endif - } - -private: - // VFP operations commonly take one or more 5-bit operands, typically representing a - // floating point register number. This will commonly be encoded in the instruction - // in two parts, with one single bit field, and one 4-bit field. In the case of - // double precision operands the high bit of the register number will be encoded - // separately, and for single precision operands the high bit of the register number - // will be encoded individually. - // VFPOperand encapsulates a 5-bit VFP operand, with bits 0..3 containing the 4-bit - // field to be encoded together in the instruction (the low 4-bits of a double - // register number, or the high 4-bits of a single register number), and bit 4 - // contains the bit value to be encoded individually. - struct VFPOperand { - explicit VFPOperand(uint32_t value) - : m_value(value) - { - ASSERT(!(m_value & ~0x1f)); - } - - VFPOperand(FPDoubleRegisterID reg) - : m_value(reg) - { - } - - VFPOperand(RegisterID reg) - : m_value(reg) - { - } - - VFPOperand(FPSingleRegisterID reg) - : m_value(((reg & 1) << 4) | (reg >> 1)) // rotate the lowest bit of 'reg' to the top. - { - } - - uint32_t bits1() - { - return m_value >> 4; - } - - uint32_t bits4() - { - return m_value & 0xf; - } - - uint32_t m_value; - }; - - VFPOperand vcvtOp(bool toInteger, bool isUnsigned, bool isRoundZero) - { - // Cannot specify rounding when converting to float. - ASSERT(toInteger || !isRoundZero); - - uint32_t op = 0x8; - if (toInteger) { - // opc2 indicates both toInteger & isUnsigned. - op |= isUnsigned ? 0x4 : 0x5; - // 'op' field in instruction is isRoundZero - if (isRoundZero) - op |= 0x10; - } else { - ASSERT(!isRoundZero); - // 'op' field in instruction is isUnsigned - if (!isUnsigned) - op |= 0x10; - } - return VFPOperand(op); - } - - static void setInt32(void* code, uint32_t value, bool flush) - { - uint16_t* location = reinterpret_cast(code); - ASSERT(isMOV_imm_T3(location - 4) && isMOVT(location - 2)); - - ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(value)); - ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(value >> 16)); - location[-4] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); - location[-3] = twoWordOp5i6Imm4Reg4EncodedImmSecond((location[-3] >> 8) & 0xf, lo16); - location[-2] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); - location[-1] = twoWordOp5i6Imm4Reg4EncodedImmSecond((location[-1] >> 8) & 0xf, hi16); - - if (flush) - cacheFlush(location - 4, 4 * sizeof(uint16_t)); - } - - static int32_t readInt32(void* code) - { - uint16_t* location = reinterpret_cast(code); - ASSERT(isMOV_imm_T3(location - 4) && isMOVT(location - 2)); - - ARMThumbImmediate lo16; - ARMThumbImmediate hi16; - decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(lo16, location[-4]); - decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(lo16, location[-3]); - decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(hi16, location[-2]); - decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(hi16, location[-1]); - uint32_t result = hi16.asUInt16(); - result <<= 16; - result |= lo16.asUInt16(); - return static_cast(result); - } - - static void setUInt7ForLoad(void* code, ARMThumbImmediate imm) - { - // Requires us to have planted a LDR_imm_T1 - ASSERT(imm.isValid()); - ASSERT(imm.isUInt7()); - uint16_t* location = reinterpret_cast(code); - location[0] &= ~((static_cast(0x7f) >> 2) << 6); - location[0] |= (imm.getUInt7() >> 2) << 6; - cacheFlush(location, sizeof(uint16_t)); - } - - static void setPointer(void* code, void* value, bool flush) - { - setInt32(code, reinterpret_cast(value), flush); - } - - static bool isB(void* address) - { - uint16_t* instruction = static_cast(address); - return ((instruction[0] & 0xf800) == OP_B_T4a) && ((instruction[1] & 0xd000) == OP_B_T4b); - } - - static bool isBX(void* address) - { - uint16_t* instruction = static_cast(address); - return (instruction[0] & 0xff87) == OP_BX; - } - - static bool isMOV_imm_T3(void* address) - { - uint16_t* instruction = static_cast(address); - return ((instruction[0] & 0xFBF0) == OP_MOV_imm_T3) && ((instruction[1] & 0x8000) == 0); - } - - static bool isMOVT(void* address) - { - uint16_t* instruction = static_cast(address); - return ((instruction[0] & 0xFBF0) == OP_MOVT) && ((instruction[1] & 0x8000) == 0); - } - - static bool isNOP_T1(void* address) - { - uint16_t* instruction = static_cast(address); - return instruction[0] == OP_NOP_T1; - } - - static bool isNOP_T2(void* address) - { - uint16_t* instruction = static_cast(address); - return (instruction[0] == OP_NOP_T2a) && (instruction[1] == OP_NOP_T2b); - } - - static bool canBeJumpT1(const uint16_t* instruction, const void* target) - { - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - // It does not appear to be documented in the ARM ARM (big surprise), but - // for OP_B_T1 the branch displacement encoded in the instruction is 2 - // less than the actual displacement. - relative -= 2; - return ((relative << 23) >> 23) == relative; - } - - static bool canBeJumpT2(const uint16_t* instruction, const void* target) - { - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - // It does not appear to be documented in the ARM ARM (big surprise), but - // for OP_B_T2 the branch displacement encoded in the instruction is 2 - // less than the actual displacement. - relative -= 2; - return ((relative << 20) >> 20) == relative; - } - - static bool canBeJumpT3(const uint16_t* instruction, const void* target) - { - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - return ((relative << 11) >> 11) == relative; - } - - static bool canBeJumpT4(const uint16_t* instruction, const void* target) - { - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - return ((relative << 7) >> 7) == relative; - } - - void linkJumpT1(Condition cond, uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - ASSERT(canBeJumpT1(instruction, target)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - // It does not appear to be documented in the ARM ARM (big surprise), but - // for OP_B_T1 the branch displacement encoded in the instruction is 2 - // less than the actual displacement. - relative -= 2; - - // All branch offsets should be an even distance. - ASSERT(!(relative & 1)); - instruction[-1] = OP_B_T1 | ((cond & 0xf) << 8) | ((relative & 0x1fe) >> 1); - } - - static void linkJumpT2(uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - ASSERT(canBeJumpT2(instruction, target)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - // It does not appear to be documented in the ARM ARM (big surprise), but - // for OP_B_T2 the branch displacement encoded in the instruction is 2 - // less than the actual displacement. - relative -= 2; - - // All branch offsets should be an even distance. - ASSERT(!(relative & 1)); - instruction[-1] = OP_B_T2 | ((relative & 0xffe) >> 1); - } - - void linkJumpT3(Condition cond, uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - ASSERT(canBeJumpT3(instruction, target)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - - // All branch offsets should be an even distance. - ASSERT(!(relative & 1)); - instruction[-2] = OP_B_T3a | ((relative & 0x100000) >> 10) | ((cond & 0xf) << 6) | ((relative & 0x3f000) >> 12); - instruction[-1] = OP_B_T3b | ((relative & 0x80000) >> 8) | ((relative & 0x40000) >> 5) | ((relative & 0xffe) >> 1); - } - - static void linkJumpT4(uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - ASSERT(canBeJumpT4(instruction, target)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - // ARM encoding for the top two bits below the sign bit is 'peculiar'. - if (relative >= 0) - relative ^= 0xC00000; - - // All branch offsets should be an even distance. - ASSERT(!(relative & 1)); - instruction[-2] = OP_B_T4a | ((relative & 0x1000000) >> 14) | ((relative & 0x3ff000) >> 12); - instruction[-1] = OP_B_T4b | ((relative & 0x800000) >> 10) | ((relative & 0x400000) >> 11) | ((relative & 0xffe) >> 1); - } - - void linkConditionalJumpT4(Condition cond, uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - instruction[-3] = ifThenElse(cond) | OP_IT; - linkJumpT4(instruction, target); - } - - static void linkBX(uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; - ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) + 1)); - ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) >> 16)); - instruction[-5] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); - instruction[-4] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, lo16); - instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); - instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); - instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); - } - - void linkConditionalBX(Condition cond, uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - linkBX(instruction, target); - instruction[-6] = ifThenElse(cond, true, true) | OP_IT; - } - - static void linkJumpAbsolute(uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - ASSERT((isMOV_imm_T3(instruction - 5) && isMOVT(instruction - 3) && isBX(instruction - 1)) - || (isNOP_T1(instruction - 5) && isNOP_T2(instruction - 4) && isB(instruction - 2))); - - if (canBeJumpT4(instruction, target)) { - // There may be a better way to fix this, but right now put the NOPs first, since in the - // case of an conditional branch this will be coming after an ITTT predicating *three* - // instructions! Looking backwards to modify the ITTT to an IT is not easy, due to - // variable wdith encoding - the previous instruction might *look* like an ITTT but - // actually be the second half of a 2-word op. - instruction[-5] = OP_NOP_T1; - instruction[-4] = OP_NOP_T2a; - instruction[-3] = OP_NOP_T2b; - linkJumpT4(instruction, target); - } else { - const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; - ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) + 1)); - ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) >> 16)); - instruction[-5] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); - instruction[-4] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, lo16); - instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); - instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); - instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); - } - } - - static uint16_t twoWordOp5i6Imm4Reg4EncodedImmFirst(uint16_t op, ARMThumbImmediate imm) - { - return op | (imm.m_value.i << 10) | imm.m_value.imm4; - } - - static void decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(ARMThumbImmediate& result, uint16_t value) - { - result.m_value.i = (value >> 10) & 1; - result.m_value.imm4 = value & 15; - } - - static uint16_t twoWordOp5i6Imm4Reg4EncodedImmSecond(uint16_t rd, ARMThumbImmediate imm) - { - return (imm.m_value.imm3 << 12) | (rd << 8) | imm.m_value.imm8; - } - - static void decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(ARMThumbImmediate& result, uint16_t value) - { - result.m_value.imm3 = (value >> 12) & 7; - result.m_value.imm8 = value & 255; - } - - class ARMInstructionFormatter { - public: - ALWAYS_INLINE void oneWordOp5Reg3Imm8(OpcodeID op, RegisterID rd, uint8_t imm) - { - m_buffer.putShort(op | (rd << 8) | imm); - } - - ALWAYS_INLINE void oneWordOp5Imm5Reg3Reg3(OpcodeID op, uint8_t imm, RegisterID reg1, RegisterID reg2) - { - m_buffer.putShort(op | (imm << 6) | (reg1 << 3) | reg2); - } - - ALWAYS_INLINE void oneWordOp7Reg3Reg3Reg3(OpcodeID op, RegisterID reg1, RegisterID reg2, RegisterID reg3) - { - m_buffer.putShort(op | (reg1 << 6) | (reg2 << 3) | reg3); - } - - ALWAYS_INLINE void oneWordOp8Imm8(OpcodeID op, uint8_t imm) - { - m_buffer.putShort(op | imm); - } - - ALWAYS_INLINE void oneWordOp8RegReg143(OpcodeID op, RegisterID reg1, RegisterID reg2) - { - m_buffer.putShort(op | ((reg2 & 8) << 4) | (reg1 << 3) | (reg2 & 7)); - } - - ALWAYS_INLINE void oneWordOp9Imm7(OpcodeID op, uint8_t imm) - { - m_buffer.putShort(op | imm); - } - - ALWAYS_INLINE void oneWordOp10Reg3Reg3(OpcodeID op, RegisterID reg1, RegisterID reg2) - { - m_buffer.putShort(op | (reg1 << 3) | reg2); - } - - ALWAYS_INLINE void twoWordOp12Reg4FourFours(OpcodeID1 op, RegisterID reg, FourFours ff) - { - m_buffer.putShort(op | reg); - m_buffer.putShort(ff.m_u.value); - } - - ALWAYS_INLINE void twoWordOp16FourFours(OpcodeID1 op, FourFours ff) - { - m_buffer.putShort(op); - m_buffer.putShort(ff.m_u.value); - } - - ALWAYS_INLINE void twoWordOp16Op16(OpcodeID1 op1, OpcodeID2 op2) - { - m_buffer.putShort(op1); - m_buffer.putShort(op2); - } - - ALWAYS_INLINE void twoWordOp5i6Imm4Reg4EncodedImm(OpcodeID1 op, int imm4, RegisterID rd, ARMThumbImmediate imm) - { - ARMThumbImmediate newImm = imm; - newImm.m_value.imm4 = imm4; - - m_buffer.putShort(ARMv7Assembler::twoWordOp5i6Imm4Reg4EncodedImmFirst(op, newImm)); - m_buffer.putShort(ARMv7Assembler::twoWordOp5i6Imm4Reg4EncodedImmSecond(rd, newImm)); - } - - ALWAYS_INLINE void twoWordOp12Reg4Reg4Imm12(OpcodeID1 op, RegisterID reg1, RegisterID reg2, uint16_t imm) - { - m_buffer.putShort(op | reg1); - m_buffer.putShort((reg2 << 12) | imm); - } - - ALWAYS_INLINE void twoWordOp12Reg40Imm3Reg4Imm20Imm5(OpcodeID1 op, RegisterID reg1, RegisterID reg2, uint16_t imm1, uint16_t imm2, uint16_t imm3) - { - m_buffer.putShort(op | reg1); - m_buffer.putShort((imm1 << 12) | (reg2 << 8) | (imm2 << 6) | imm3); - } - - // Formats up instructions of the pattern: - // 111111111B11aaaa:bbbb222SA2C2cccc - // Where 1s in the pattern come from op1, 2s in the pattern come from op2, S is the provided size bit. - // Operands provide 5 bit values of the form Aaaaa, Bbbbb, Ccccc. - ALWAYS_INLINE void vfpOp(OpcodeID1 op1, OpcodeID2 op2, bool size, VFPOperand a, VFPOperand b, VFPOperand c) - { - ASSERT(!(op1 & 0x004f)); - ASSERT(!(op2 & 0xf1af)); - m_buffer.putShort(op1 | b.bits1() << 6 | a.bits4()); - m_buffer.putShort(op2 | b.bits4() << 12 | size << 8 | a.bits1() << 7 | c.bits1() << 5 | c.bits4()); - } - - // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. - // (i.e. +/-(0..255) 32-bit words) - ALWAYS_INLINE void vfpMemOp(OpcodeID1 op1, OpcodeID2 op2, bool size, RegisterID rn, VFPOperand rd, int32_t imm) - { - bool up = true; - if (imm < 0) { - imm = -imm; - up = false; - } - - uint32_t offset = imm; - ASSERT(!(offset & ~0x3fc)); - offset >>= 2; - - m_buffer.putShort(op1 | (up << 7) | rd.bits1() << 6 | rn); - m_buffer.putShort(op2 | rd.bits4() << 12 | size << 8 | offset); - } - - // Administrative methods: - - size_t codeSize() const { return m_buffer.codeSize(); } - AssemblerLabel label() const { return m_buffer.label(); } - bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); } - void* data() const { return m_buffer.data(); } - - unsigned debugOffset() { return m_buffer.debugOffset(); } - - private: - AssemblerBuffer m_buffer; - } m_formatter; - - Vector m_jumpsToLink; - Vector m_offsets; - int m_indexOfLastWatchpoint; - int m_indexOfTailOfLastWatchpoint; -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) - -#endif // ARMAssembler_h diff --git a/masm/assembler/AbstractMacroAssembler.h b/masm/assembler/AbstractMacroAssembler.h deleted file mode 100644 index ee78ef84eb..0000000000 --- a/masm/assembler/AbstractMacroAssembler.h +++ /dev/null @@ -1,792 +0,0 @@ -/* - * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef AbstractMacroAssembler_h -#define AbstractMacroAssembler_h - -#include "AssemblerBuffer.h" -#include "CodeLocation.h" -#include "MacroAssemblerCodeRef.h" -#include -#include -#include - -#if ENABLE(ASSEMBLER) - - -#if PLATFORM(QT) -#define ENABLE_JIT_CONSTANT_BLINDING 0 -#endif - -#ifndef ENABLE_JIT_CONSTANT_BLINDING -#define ENABLE_JIT_CONSTANT_BLINDING 1 -#endif - -namespace JSC { - -class JumpReplacementWatchpoint; -class LinkBuffer; -class RepatchBuffer; -class Watchpoint; -namespace DFG { -struct OSRExit; -} - -template -class AbstractMacroAssembler { -public: - friend class JITWriteBarrierBase; - typedef AssemblerType AssemblerType_T; - - typedef MacroAssemblerCodePtr CodePtr; - typedef MacroAssemblerCodeRef CodeRef; - - class Jump; - - typedef typename AssemblerType::RegisterID RegisterID; - - // Section 1: MacroAssembler operand types - // - // The following types are used as operands to MacroAssembler operations, - // describing immediate and memory operands to the instructions to be planted. - - enum Scale { - TimesOne, - TimesTwo, - TimesFour, - TimesEight, - }; - - // Address: - // - // Describes a simple base-offset address. - struct Address { - explicit Address(RegisterID base, int32_t offset = 0) - : base(base) - , offset(offset) - { - } - - RegisterID base; - int32_t offset; - }; - - struct ExtendedAddress { - explicit ExtendedAddress(RegisterID base, intptr_t offset = 0) - : base(base) - , offset(offset) - { - } - - RegisterID base; - intptr_t offset; - }; - - // ImplicitAddress: - // - // This class is used for explicit 'load' and 'store' operations - // (as opposed to situations in which a memory operand is provided - // to a generic operation, such as an integer arithmetic instruction). - // - // In the case of a load (or store) operation we want to permit - // addresses to be implicitly constructed, e.g. the two calls: - // - // load32(Address(addrReg), destReg); - // load32(addrReg, destReg); - // - // Are equivalent, and the explicit wrapping of the Address in the former - // is unnecessary. - struct ImplicitAddress { - ImplicitAddress(RegisterID base) - : base(base) - , offset(0) - { - } - - ImplicitAddress(Address address) - : base(address.base) - , offset(address.offset) - { - } - - RegisterID base; - int32_t offset; - }; - - // BaseIndex: - // - // Describes a complex addressing mode. - struct BaseIndex { - BaseIndex(RegisterID base, RegisterID index, Scale scale, int32_t offset = 0) - : base(base) - , index(index) - , scale(scale) - , offset(offset) - { - } - - RegisterID base; - RegisterID index; - Scale scale; - int32_t offset; - }; - - // AbsoluteAddress: - // - // Describes an memory operand given by a pointer. For regular load & store - // operations an unwrapped void* will be used, rather than using this. - struct AbsoluteAddress { - explicit AbsoluteAddress(const void* ptr) - : m_ptr(ptr) - { - } - - const void* m_ptr; - }; - - // TrustedImmPtr: - // - // A pointer sized immediate operand to an instruction - this is wrapped - // in a class requiring explicit construction in order to differentiate - // from pointers used as absolute addresses to memory operations - struct TrustedImmPtr { - TrustedImmPtr() { } - - explicit TrustedImmPtr(const void* value) - : m_value(value) - { - } - - // This is only here so that TrustedImmPtr(0) does not confuse the C++ - // overload handling rules. - explicit TrustedImmPtr(int value) - : m_value(0) - { - ASSERT_UNUSED(value, !value); - } - - explicit TrustedImmPtr(size_t value) - : m_value(reinterpret_cast(value)) - { - } - - intptr_t asIntptr() - { - return reinterpret_cast(m_value); - } - - const void* m_value; - }; - - struct ImmPtr : -#if ENABLE(JIT_CONSTANT_BLINDING) - private TrustedImmPtr -#else - public TrustedImmPtr -#endif - { - explicit ImmPtr(const void* value) - : TrustedImmPtr(value) - { - } - - TrustedImmPtr asTrustedImmPtr() { return *this; } - }; - - // TrustedImm32: - // - // A 32bit immediate operand to an instruction - this is wrapped in a - // class requiring explicit construction in order to prevent RegisterIDs - // (which are implemented as an enum) from accidentally being passed as - // immediate values. - struct TrustedImm32 { - TrustedImm32() { } - - explicit TrustedImm32(int32_t value) - : m_value(value) - { - } - -#if !CPU(X86_64) - explicit TrustedImm32(TrustedImmPtr ptr) - : m_value(ptr.asIntptr()) - { - } -#endif - - int32_t m_value; - }; - - - struct Imm32 : -#if ENABLE(JIT_CONSTANT_BLINDING) - private TrustedImm32 -#else - public TrustedImm32 -#endif - { - explicit Imm32(int32_t value) - : TrustedImm32(value) - { - } -#if !CPU(X86_64) - explicit Imm32(TrustedImmPtr ptr) - : TrustedImm32(ptr) - { - } -#endif - const TrustedImm32& asTrustedImm32() const { return *this; } - - }; - - // TrustedImm64: - // - // A 64bit immediate operand to an instruction - this is wrapped in a - // class requiring explicit construction in order to prevent RegisterIDs - // (which are implemented as an enum) from accidentally being passed as - // immediate values. - struct TrustedImm64 { - TrustedImm64() { } - - explicit TrustedImm64(int64_t value) - : m_value(value) - { - } - -#if CPU(X86_64) - explicit TrustedImm64(TrustedImmPtr ptr) - : m_value(ptr.asIntptr()) - { - } -#endif - - int64_t m_value; - }; - - struct Imm64 : -#if ENABLE(JIT_CONSTANT_BLINDING) - private TrustedImm64 -#else - public TrustedImm64 -#endif - { - explicit Imm64(int64_t value) - : TrustedImm64(value) - { - } -#if CPU(X86_64) - explicit Imm64(TrustedImmPtr ptr) - : TrustedImm64(ptr) - { - } -#endif - const TrustedImm64& asTrustedImm64() const { return *this; } - }; - - // Section 2: MacroAssembler code buffer handles - // - // The following types are used to reference items in the code buffer - // during JIT code generation. For example, the type Jump is used to - // track the location of a jump instruction so that it may later be - // linked to a label marking its destination. - - - // Label: - // - // A Label records a point in the generated instruction stream, typically such that - // it may be used as a destination for a jump. - class Label { - template - friend class AbstractMacroAssembler; - friend struct DFG::OSRExit; - friend class Jump; - friend class JumpReplacementWatchpoint; - friend class MacroAssemblerCodeRef; - friend class LinkBuffer; - friend class Watchpoint; - - public: - Label() - { - } - - Label(AbstractMacroAssembler* masm) - : m_label(masm->m_assembler.label()) - { - } - - bool isSet() const { return m_label.isSet(); } - private: - AssemblerLabel m_label; - }; - - // ConvertibleLoadLabel: - // - // A ConvertibleLoadLabel records a loadPtr instruction that can be patched to an addPtr - // so that: - // - // loadPtr(Address(a, i), b) - // - // becomes: - // - // addPtr(TrustedImmPtr(i), a, b) - class ConvertibleLoadLabel { - template - friend class AbstractMacroAssembler; - friend class LinkBuffer; - - public: - ConvertibleLoadLabel() - { - } - - ConvertibleLoadLabel(AbstractMacroAssembler* masm) - : m_label(masm->m_assembler.labelIgnoringWatchpoints()) - { - } - - bool isSet() const { return m_label.isSet(); } - private: - AssemblerLabel m_label; - }; - - // DataLabelPtr: - // - // A DataLabelPtr is used to refer to a location in the code containing a pointer to be - // patched after the code has been generated. - class DataLabelPtr { - template - friend class AbstractMacroAssembler; - friend class LinkBuffer; - public: - DataLabelPtr() - { - } - - DataLabelPtr(AbstractMacroAssembler* masm) - : m_label(masm->m_assembler.label()) - { - } - - bool isSet() const { return m_label.isSet(); } - - private: - AssemblerLabel m_label; - }; - - // DataLabel32: - // - // A DataLabelPtr is used to refer to a location in the code containing a pointer to be - // patched after the code has been generated. - class DataLabel32 { - template - friend class AbstractMacroAssembler; - friend class LinkBuffer; - public: - DataLabel32() - { - } - - DataLabel32(AbstractMacroAssembler* masm) - : m_label(masm->m_assembler.label()) - { - } - - AssemblerLabel label() const { return m_label; } - - private: - AssemblerLabel m_label; - }; - - // DataLabelCompact: - // - // A DataLabelCompact is used to refer to a location in the code containing a - // compact immediate to be patched after the code has been generated. - class DataLabelCompact { - template - friend class AbstractMacroAssembler; - friend class LinkBuffer; - public: - DataLabelCompact() - { - } - - DataLabelCompact(AbstractMacroAssembler* masm) - : m_label(masm->m_assembler.label()) - { - } - - DataLabelCompact(AssemblerLabel label) - : m_label(label) - { - } - - private: - AssemblerLabel m_label; - }; - - // Call: - // - // A Call object is a reference to a call instruction that has been planted - // into the code buffer - it is typically used to link the call, setting the - // relative offset such that when executed it will call to the desired - // destination. - class Call { - template - friend class AbstractMacroAssembler; - - public: - enum Flags { - None = 0x0, - Linkable = 0x1, - Near = 0x2, - LinkableNear = 0x3, - }; - - Call() - : m_flags(None) - { - } - - Call(AssemblerLabel jmp, Flags flags) - : m_label(jmp) - , m_flags(flags) - { - } - - bool isFlagSet(Flags flag) - { - return m_flags & flag; - } - - static Call fromTailJump(Jump jump) - { - return Call(jump.m_label, Linkable); - } - - AssemblerLabel m_label; - private: - Flags m_flags; - }; - - // Jump: - // - // A jump object is a reference to a jump instruction that has been planted - // into the code buffer - it is typically used to link the jump, setting the - // relative offset such that when executed it will jump to the desired - // destination. - class Jump { - template - friend class AbstractMacroAssembler; - friend class Call; - friend struct DFG::OSRExit; - friend class LinkBuffer; - public: - Jump() - { - } - -#if CPU(ARM_THUMB2) - // Fixme: this information should be stored in the instruction stream, not in the Jump object. - Jump(AssemblerLabel jmp, ARMv7Assembler::JumpType type, ARMv7Assembler::Condition condition = ARMv7Assembler::ConditionInvalid) - : m_label(jmp) - , m_type(type) - , m_condition(condition) - { - } -#elif CPU(SH4) - Jump(AssemblerLabel jmp, SH4Assembler::JumpType type = SH4Assembler::JumpFar) - : m_label(jmp) - , m_type(type) - { - } -#else - Jump(AssemblerLabel jmp) - : m_label(jmp) - { - } -#endif - - Label label() const - { - Label result; - result.m_label = m_label; - return result; - } - - void link(AbstractMacroAssembler* masm) const - { -#if CPU(ARM_THUMB2) - masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type, m_condition); -#elif CPU(SH4) - masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type); -#else - masm->m_assembler.linkJump(m_label, masm->m_assembler.label()); -#endif - } - - void linkTo(Label label, AbstractMacroAssembler* masm) const - { -#if CPU(ARM_THUMB2) - masm->m_assembler.linkJump(m_label, label.m_label, m_type, m_condition); -#else - masm->m_assembler.linkJump(m_label, label.m_label); -#endif - } - - bool isSet() const { return m_label.isSet(); } - - private: - AssemblerLabel m_label; -#if CPU(ARM_THUMB2) - ARMv7Assembler::JumpType m_type; - ARMv7Assembler::Condition m_condition; -#endif -#if CPU(SH4) - SH4Assembler::JumpType m_type; -#endif - }; - - struct PatchableJump { - PatchableJump() - { - } - - explicit PatchableJump(Jump jump) - : m_jump(jump) - { - } - - operator Jump&() { return m_jump; } - - Jump m_jump; - }; - - // JumpList: - // - // A JumpList is a set of Jump objects. - // All jumps in the set will be linked to the same destination. - class JumpList { - friend class LinkBuffer; - - public: - typedef Vector JumpVector; - - JumpList() { } - - JumpList(Jump jump) - { - append(jump); - } - - void link(AbstractMacroAssembler* masm) - { - size_t size = m_jumps.size(); - for (size_t i = 0; i < size; ++i) - m_jumps[i].link(masm); - m_jumps.clear(); - } - - void linkTo(Label label, AbstractMacroAssembler* masm) - { - size_t size = m_jumps.size(); - for (size_t i = 0; i < size; ++i) - m_jumps[i].linkTo(label, masm); - m_jumps.clear(); - } - - void append(Jump jump) - { - m_jumps.append(jump); - } - - void append(const JumpList& other) - { - m_jumps.append(other.m_jumps.begin(), other.m_jumps.size()); - } - - bool empty() - { - return !m_jumps.size(); - } - - void clear() - { - m_jumps.clear(); - } - - const JumpVector& jumps() const { return m_jumps; } - - private: - JumpVector m_jumps; - }; - - - // Section 3: Misc admin methods -#if ENABLE(DFG_JIT) - Label labelIgnoringWatchpoints() - { - Label result; - result.m_label = m_assembler.labelIgnoringWatchpoints(); - return result; - } -#else - Label labelIgnoringWatchpoints() - { - return label(); - } -#endif - - Label label() - { - return Label(this); - } - - void padBeforePatch() - { - // Rely on the fact that asking for a label already does the padding. - (void)label(); - } - - Label watchpointLabel() - { - Label result; - result.m_label = m_assembler.labelForWatchpoint(); - return result; - } - - Label align() - { - m_assembler.align(16); - return Label(this); - } - - template - static ptrdiff_t differenceBetween(T from, U to) - { - return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_label); - } - - static ptrdiff_t differenceBetweenCodePtr(const MacroAssemblerCodePtr& a, const MacroAssemblerCodePtr& b) - { - return reinterpret_cast(b.executableAddress()) - reinterpret_cast(a.executableAddress()); - } - - unsigned debugOffset() { return m_assembler.debugOffset(); } - - ALWAYS_INLINE static void cacheFlush(void* code, size_t size) - { - AssemblerType::cacheFlush(code, size); - } -protected: - AbstractMacroAssembler() - : m_randomSource(cryptographicallyRandomNumber()) - { - } - - AssemblerType m_assembler; - - uint32_t random() - { - return m_randomSource.getUint32(); - } - - WeakRandom m_randomSource; - -#if ENABLE(JIT_CONSTANT_BLINDING) - static bool scratchRegisterForBlinding() { return false; } - static bool shouldBlindForSpecificArch(uint32_t) { return true; } - static bool shouldBlindForSpecificArch(uint64_t) { return true; } -#endif - - friend class LinkBuffer; - friend class RepatchBuffer; - - static void linkJump(void* code, Jump jump, CodeLocationLabel target) - { - AssemblerType::linkJump(code, jump.m_label, target.dataLocation()); - } - - static void linkPointer(void* code, AssemblerLabel label, void* value) - { - AssemblerType::linkPointer(code, label, value); - } - - static void* getLinkerAddress(void* code, AssemblerLabel label) - { - return AssemblerType::getRelocatedAddress(code, label); - } - - static unsigned getLinkerCallReturnOffset(Call call) - { - return AssemblerType::getCallReturnOffset(call.m_label); - } - - static void repatchJump(CodeLocationJump jump, CodeLocationLabel destination) - { - AssemblerType::relinkJump(jump.dataLocation(), destination.dataLocation()); - } - - static void repatchNearCall(CodeLocationNearCall nearCall, CodeLocationLabel destination) - { - AssemblerType::relinkCall(nearCall.dataLocation(), destination.executableAddress()); - } - - static void repatchCompact(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) - { - AssemblerType::repatchCompact(dataLabelCompact.dataLocation(), value); - } - - static void repatchInt32(CodeLocationDataLabel32 dataLabel32, int32_t value) - { - AssemblerType::repatchInt32(dataLabel32.dataLocation(), value); - } - - static void repatchPointer(CodeLocationDataLabelPtr dataLabelPtr, void* value) - { - AssemblerType::repatchPointer(dataLabelPtr.dataLocation(), value); - } - - static void* readPointer(CodeLocationDataLabelPtr dataLabelPtr) - { - return AssemblerType::readPointer(dataLabelPtr.dataLocation()); - } - - static void replaceWithLoad(CodeLocationConvertibleLoad label) - { - AssemblerType::replaceWithLoad(label.dataLocation()); - } - - static void replaceWithAddressComputation(CodeLocationConvertibleLoad label) - { - AssemblerType::replaceWithAddressComputation(label.dataLocation()); - } -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // AbstractMacroAssembler_h diff --git a/masm/assembler/AssemblerBuffer.h b/masm/assembler/AssemblerBuffer.h deleted file mode 100644 index bc52801ba7..0000000000 --- a/masm/assembler/AssemblerBuffer.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef AssemblerBuffer_h -#define AssemblerBuffer_h - -#if ENABLE(ASSEMBLER) - -#include "ExecutableAllocator.h" -#include "JITCompilationEffort.h" -#include "JSGlobalData.h" -#include "stdint.h" -#include -#include -#include -#include - -namespace JSC { - - struct AssemblerLabel { - AssemblerLabel() - : m_offset(std::numeric_limits::max()) - { - } - - explicit AssemblerLabel(uint32_t offset) - : m_offset(offset) - { - } - - bool isSet() const { return (m_offset != std::numeric_limits::max()); } - - AssemblerLabel labelAtOffset(int offset) const - { - return AssemblerLabel(m_offset + offset); - } - - uint32_t m_offset; - }; - - class AssemblerBuffer { - static const int inlineCapacity = 128; - public: - AssemblerBuffer() - : m_storage(inlineCapacity) - , m_buffer(&(*m_storage.begin())) - , m_capacity(inlineCapacity) - , m_index(0) - { - } - - ~AssemblerBuffer() - { - } - - bool isAvailable(int space) - { - return m_index + space <= m_capacity; - } - - void ensureSpace(int space) - { - if (!isAvailable(space)) - grow(); - } - - bool isAligned(int alignment) const - { - return !(m_index & (alignment - 1)); - } - - template - void putIntegral(IntegralType value) - { - ensureSpace(sizeof(IntegralType)); - putIntegralUnchecked(value); - } - - template - void putIntegralUnchecked(IntegralType value) - { - ASSERT(isAvailable(sizeof(IntegralType))); - *reinterpret_cast_ptr(m_buffer + m_index) = value; - m_index += sizeof(IntegralType); - } - - void putByteUnchecked(int8_t value) { putIntegralUnchecked(value); } - void putByte(int8_t value) { putIntegral(value); } - void putShortUnchecked(int16_t value) { putIntegralUnchecked(value); } - void putShort(int16_t value) { putIntegral(value); } - void putIntUnchecked(int32_t value) { putIntegralUnchecked(value); } - void putInt(int32_t value) { putIntegral(value); } - void putInt64Unchecked(int64_t value) { putIntegralUnchecked(value); } - void putInt64(int64_t value) { putIntegral(value); } - - void* data() const - { - return m_buffer; - } - - size_t codeSize() const - { - return m_index; - } - - AssemblerLabel label() const - { - return AssemblerLabel(m_index); - } - - PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) - { - if (!m_index) - return 0; - - RefPtr result = globalData.executableAllocator.allocate(globalData, m_index, ownerUID, effort); - - if (!result) - return 0; - - ExecutableAllocator::makeWritable(result->start(), result->sizeInBytes()); - - memcpy(result->start(), m_buffer, m_index); - - return result.release(); - } - - unsigned debugOffset() { return m_index; } - - protected: - void append(const char* data, int size) - { - if (!isAvailable(size)) - grow(size); - - memcpy(m_buffer + m_index, data, size); - m_index += size; - } - - void grow(int extraCapacity = 0) - { - m_capacity += m_capacity / 2 + extraCapacity; - - m_storage.grow(m_capacity); - m_buffer = &(*m_storage.begin()); - } - - private: - Vector m_storage; - char* m_buffer; - int m_capacity; - int m_index; - }; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // AssemblerBuffer_h diff --git a/masm/assembler/AssemblerBufferWithConstantPool.h b/masm/assembler/AssemblerBufferWithConstantPool.h deleted file mode 100644 index 5377ef0c7a..0000000000 --- a/masm/assembler/AssemblerBufferWithConstantPool.h +++ /dev/null @@ -1,342 +0,0 @@ -/* - * Copyright (C) 2009 University of Szeged - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef AssemblerBufferWithConstantPool_h -#define AssemblerBufferWithConstantPool_h - -#if ENABLE(ASSEMBLER) - -#include "AssemblerBuffer.h" -#include - -#define ASSEMBLER_HAS_CONSTANT_POOL 1 - -namespace JSC { - -/* - On a constant pool 4 or 8 bytes data can be stored. The values can be - constants or addresses. The addresses should be 32 or 64 bits. The constants - should be double-precisions float or integer numbers which are hard to be - encoded as few machine instructions. - - TODO: The pool is desinged to handle both 32 and 64 bits values, but - currently only the 4 bytes constants are implemented and tested. - - The AssemblerBuffer can contain multiple constant pools. Each pool is inserted - into the instruction stream - protected by a jump instruction from the - execution flow. - - The flush mechanism is called when no space remain to insert the next instruction - into the pool. Three values are used to determine when the constant pool itself - have to be inserted into the instruction stream (Assembler Buffer): - - - maxPoolSize: size of the constant pool in bytes, this value cannot be - larger than the maximum offset of a PC relative memory load - - - barrierSize: size of jump instruction in bytes which protects the - constant pool from execution - - - maxInstructionSize: maximum length of a machine instruction in bytes - - There are some callbacks which solve the target architecture specific - address handling: - - - TYPE patchConstantPoolLoad(TYPE load, int value): - patch the 'load' instruction with the index of the constant in the - constant pool and return the patched instruction. - - - void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr): - patch the a PC relative load instruction at 'loadAddr' address with the - final relative offset. The offset can be computed with help of - 'constPoolAddr' (the address of the constant pool) and index of the - constant (which is stored previously in the load instruction itself). - - - TYPE placeConstantPoolBarrier(int size): - return with a constant pool barrier instruction which jumps over the - constant pool. - - The 'put*WithConstant*' functions should be used to place a data into the - constant pool. -*/ - -template -class AssemblerBufferWithConstantPool : public AssemblerBuffer { - typedef SegmentedVector LoadOffsets; - using AssemblerBuffer::putIntegral; - using AssemblerBuffer::putIntegralUnchecked; -public: - typedef struct { - short high; - short low; - } TwoShorts; - - enum { - UniqueConst, - ReusableConst, - UnusedEntry, - }; - - AssemblerBufferWithConstantPool() - : AssemblerBuffer() - , m_numConsts(0) - , m_maxDistance(maxPoolSize) - , m_lastConstDelta(0) - { - m_pool = static_cast(fastMalloc(maxPoolSize)); - m_mask = static_cast(fastMalloc(maxPoolSize / sizeof(uint32_t))); - } - - ~AssemblerBufferWithConstantPool() - { - fastFree(m_mask); - fastFree(m_pool); - } - - void ensureSpace(int space) - { - flushIfNoSpaceFor(space); - AssemblerBuffer::ensureSpace(space); - } - - void ensureSpace(int insnSpace, int constSpace) - { - flushIfNoSpaceFor(insnSpace, constSpace); - AssemblerBuffer::ensureSpace(insnSpace); - } - - void ensureSpaceForAnyInstruction(int amount = 1) - { - flushIfNoSpaceFor(amount * maxInstructionSize, amount * sizeof(uint64_t)); - } - - bool isAligned(int alignment) - { - flushIfNoSpaceFor(alignment); - return AssemblerBuffer::isAligned(alignment); - } - - void putByteUnchecked(int value) - { - AssemblerBuffer::putByteUnchecked(value); - correctDeltas(1); - } - - void putByte(int value) - { - flushIfNoSpaceFor(1); - AssemblerBuffer::putByte(value); - correctDeltas(1); - } - - void putShortUnchecked(int value) - { - AssemblerBuffer::putShortUnchecked(value); - correctDeltas(2); - } - - void putShort(int value) - { - flushIfNoSpaceFor(2); - AssemblerBuffer::putShort(value); - correctDeltas(2); - } - - void putIntUnchecked(int value) - { - AssemblerBuffer::putIntUnchecked(value); - correctDeltas(4); - } - - void putInt(int value) - { - flushIfNoSpaceFor(4); - AssemblerBuffer::putInt(value); - correctDeltas(4); - } - - void putInt64Unchecked(int64_t value) - { - AssemblerBuffer::putInt64Unchecked(value); - correctDeltas(8); - } - - void putIntegral(TwoShorts value) - { - putIntegral(value.high); - putIntegral(value.low); - } - - void putIntegralUnchecked(TwoShorts value) - { - putIntegralUnchecked(value.high); - putIntegralUnchecked(value.low); - } - - PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) - { - flushConstantPool(false); - return AssemblerBuffer::executableCopy(globalData, ownerUID, effort); - } - - void putShortWithConstantInt(uint16_t insn, uint32_t constant, bool isReusable = false) - { - putIntegralWithConstantInt(insn, constant, isReusable); - } - - void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false) - { - putIntegralWithConstantInt(insn, constant, isReusable); - } - - // This flushing mechanism can be called after any unconditional jumps. - void flushWithoutBarrier(bool isForced = false) - { - // Flush if constant pool is more than 60% full to avoid overuse of this function. - if (isForced || 5 * static_cast(m_numConsts) > 3 * maxPoolSize / sizeof(uint32_t)) - flushConstantPool(false); - } - - uint32_t* poolAddress() - { - return m_pool; - } - - int sizeOfConstantPool() - { - return m_numConsts; - } - -private: - void correctDeltas(int insnSize) - { - m_maxDistance -= insnSize; - m_lastConstDelta -= insnSize; - if (m_lastConstDelta < 0) - m_lastConstDelta = 0; - } - - void correctDeltas(int insnSize, int constSize) - { - correctDeltas(insnSize); - - m_maxDistance -= m_lastConstDelta; - m_lastConstDelta = constSize; - } - - template - void putIntegralWithConstantInt(IntegralType insn, uint32_t constant, bool isReusable) - { - if (!m_numConsts) - m_maxDistance = maxPoolSize; - flushIfNoSpaceFor(sizeof(IntegralType), 4); - - m_loadOffsets.append(codeSize()); - if (isReusable) { - for (int i = 0; i < m_numConsts; ++i) { - if (m_mask[i] == ReusableConst && m_pool[i] == constant) { - putIntegral(static_cast(AssemblerType::patchConstantPoolLoad(insn, i))); - correctDeltas(sizeof(IntegralType)); - return; - } - } - } - - m_pool[m_numConsts] = constant; - m_mask[m_numConsts] = static_cast(isReusable ? ReusableConst : UniqueConst); - - putIntegral(static_cast(AssemblerType::patchConstantPoolLoad(insn, m_numConsts))); - ++m_numConsts; - - correctDeltas(sizeof(IntegralType), 4); - } - - void flushConstantPool(bool useBarrier = true) - { - if (m_numConsts == 0) - return; - int alignPool = (codeSize() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1); - - if (alignPool) - alignPool = sizeof(uint64_t) - alignPool; - - // Callback to protect the constant pool from execution - if (useBarrier) - putIntegral(AssemblerType::placeConstantPoolBarrier(m_numConsts * sizeof(uint32_t) + alignPool)); - - if (alignPool) { - if (alignPool & 1) - AssemblerBuffer::putByte(AssemblerType::padForAlign8); - if (alignPool & 2) - AssemblerBuffer::putShort(AssemblerType::padForAlign16); - if (alignPool & 4) - AssemblerBuffer::putInt(AssemblerType::padForAlign32); - } - - int constPoolOffset = codeSize(); - append(reinterpret_cast(m_pool), m_numConsts * sizeof(uint32_t)); - - // Patch each PC relative load - for (LoadOffsets::Iterator iter = m_loadOffsets.begin(); iter != m_loadOffsets.end(); ++iter) { - void* loadAddr = reinterpret_cast(data()) + *iter; - AssemblerType::patchConstantPoolLoad(loadAddr, reinterpret_cast(data()) + constPoolOffset); - } - - m_loadOffsets.clear(); - m_numConsts = 0; - } - - void flushIfNoSpaceFor(int nextInsnSize) - { - if (m_numConsts == 0) - return; - int lastConstDelta = m_lastConstDelta > nextInsnSize ? m_lastConstDelta - nextInsnSize : 0; - if ((m_maxDistance < nextInsnSize + lastConstDelta + barrierSize + (int)sizeof(uint32_t))) - flushConstantPool(); - } - - void flushIfNoSpaceFor(int nextInsnSize, int nextConstSize) - { - if (m_numConsts == 0) - return; - if ((m_maxDistance < nextInsnSize + m_lastConstDelta + nextConstSize + barrierSize + (int)sizeof(uint32_t)) || - (m_numConsts * sizeof(uint32_t) + nextConstSize >= maxPoolSize)) - flushConstantPool(); - } - - uint32_t* m_pool; - char* m_mask; - LoadOffsets m_loadOffsets; - - int m_numConsts; - int m_maxDistance; - int m_lastConstDelta; -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // AssemblerBufferWithConstantPool_h diff --git a/masm/assembler/CodeLocation.h b/masm/assembler/CodeLocation.h deleted file mode 100644 index 86d1f2b755..0000000000 --- a/masm/assembler/CodeLocation.h +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef CodeLocation_h -#define CodeLocation_h - -#include "MacroAssemblerCodeRef.h" - -#if ENABLE(ASSEMBLER) - -namespace JSC { - -class CodeLocationInstruction; -class CodeLocationLabel; -class CodeLocationJump; -class CodeLocationCall; -class CodeLocationNearCall; -class CodeLocationDataLabelCompact; -class CodeLocationDataLabel32; -class CodeLocationDataLabelPtr; -class CodeLocationConvertibleLoad; - -// The CodeLocation* types are all pretty much do-nothing wrappers around -// CodePtr (or MacroAssemblerCodePtr, to give it its full name). These -// classes only exist to provide type-safety when linking and patching code. -// -// The one new piece of functionallity introduced by these classes is the -// ability to create (or put another way, to re-discover) another CodeLocation -// at an offset from one you already know. When patching code to optimize it -// we often want to patch a number of instructions that are short, fixed -// offsets apart. To reduce memory overhead we will only retain a pointer to -// one of the instructions, and we will use the *AtOffset methods provided by -// CodeLocationCommon to find the other points in the code to modify. -class CodeLocationCommon : public MacroAssemblerCodePtr { -public: - CodeLocationInstruction instructionAtOffset(int offset); - CodeLocationLabel labelAtOffset(int offset); - CodeLocationJump jumpAtOffset(int offset); - CodeLocationCall callAtOffset(int offset); - CodeLocationNearCall nearCallAtOffset(int offset); - CodeLocationDataLabelPtr dataLabelPtrAtOffset(int offset); - CodeLocationDataLabel32 dataLabel32AtOffset(int offset); - CodeLocationDataLabelCompact dataLabelCompactAtOffset(int offset); - CodeLocationConvertibleLoad convertibleLoadAtOffset(int offset); - -protected: - CodeLocationCommon() - { - } - - CodeLocationCommon(MacroAssemblerCodePtr location) - : MacroAssemblerCodePtr(location) - { - } -}; - -class CodeLocationInstruction : public CodeLocationCommon { -public: - CodeLocationInstruction() {} - explicit CodeLocationInstruction(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) {} - explicit CodeLocationInstruction(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} -}; - -class CodeLocationLabel : public CodeLocationCommon { -public: - CodeLocationLabel() {} - explicit CodeLocationLabel(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) {} - explicit CodeLocationLabel(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} -}; - -class CodeLocationJump : public CodeLocationCommon { -public: - CodeLocationJump() {} - explicit CodeLocationJump(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) {} - explicit CodeLocationJump(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} -}; - -class CodeLocationCall : public CodeLocationCommon { -public: - CodeLocationCall() {} - explicit CodeLocationCall(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) {} - explicit CodeLocationCall(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} -}; - -class CodeLocationNearCall : public CodeLocationCommon { -public: - CodeLocationNearCall() {} - explicit CodeLocationNearCall(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) {} - explicit CodeLocationNearCall(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} -}; - -class CodeLocationDataLabel32 : public CodeLocationCommon { -public: - CodeLocationDataLabel32() {} - explicit CodeLocationDataLabel32(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) {} - explicit CodeLocationDataLabel32(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} -}; - -class CodeLocationDataLabelCompact : public CodeLocationCommon { -public: - CodeLocationDataLabelCompact() { } - explicit CodeLocationDataLabelCompact(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) { } - explicit CodeLocationDataLabelCompact(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) { } -}; - -class CodeLocationDataLabelPtr : public CodeLocationCommon { -public: - CodeLocationDataLabelPtr() {} - explicit CodeLocationDataLabelPtr(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) {} - explicit CodeLocationDataLabelPtr(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} -}; - -class CodeLocationConvertibleLoad : public CodeLocationCommon { -public: - CodeLocationConvertibleLoad() { } - explicit CodeLocationConvertibleLoad(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) { } - explicit CodeLocationConvertibleLoad(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) { } -}; - -inline CodeLocationInstruction CodeLocationCommon::instructionAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationInstruction(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationLabel CodeLocationCommon::labelAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationLabel(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationJump CodeLocationCommon::jumpAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationJump(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationCall CodeLocationCommon::callAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationCall(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationNearCall CodeLocationCommon::nearCallAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationNearCall(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationDataLabelPtr CodeLocationCommon::dataLabelPtrAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationDataLabelPtr(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationDataLabel32 CodeLocationCommon::dataLabel32AtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationDataLabel32(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationDataLabelCompact CodeLocationCommon::dataLabelCompactAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationDataLabelCompact(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationConvertibleLoad CodeLocationCommon::convertibleLoadAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationConvertibleLoad(reinterpret_cast(dataLocation()) + offset); -} - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // CodeLocation_h diff --git a/masm/assembler/LinkBuffer.cpp b/masm/assembler/LinkBuffer.cpp deleted file mode 100644 index c269157ba5..0000000000 --- a/masm/assembler/LinkBuffer.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "LinkBuffer.h" - -#if ENABLE(ASSEMBLER) - -#include "Options.h" - -namespace JSC { - -LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithoutDisassembly() -{ - performFinalization(); - - return CodeRef(m_executableMemory); -} - -LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithDisassembly(const char* format, ...) -{ - ASSERT(Options::showDisassembly() || Options::showDFGDisassembly()); - - CodeRef result = finalizeCodeWithoutDisassembly(); - - dataLogF("Generated JIT code for "); - va_list argList; - va_start(argList, format); - WTF::dataLogFV(format, argList); - va_end(argList); - dataLogF(":\n"); - - dataLogF(" Code at [%p, %p):\n", result.code().executableAddress(), static_cast(result.code().executableAddress()) + result.size()); - disassemble(result.code(), m_size, " ", WTF::dataFile()); - - return result; -} - -void LinkBuffer::linkCode(void* ownerUID, JITCompilationEffort effort) -{ - ASSERT(!m_code); -#if !ENABLE(BRANCH_COMPACTION) - m_executableMemory = m_assembler->m_assembler.executableCopy(*m_globalData, ownerUID, effort); - if (!m_executableMemory) - return; - m_code = m_executableMemory->start(); - m_size = m_assembler->m_assembler.codeSize(); - ASSERT(m_code); -#else - m_initialSize = m_assembler->m_assembler.codeSize(); - m_executableMemory = m_globalData->executableAllocator.allocate(*m_globalData, m_initialSize, ownerUID, effort); - if (!m_executableMemory) - return; - m_code = (uint8_t*)m_executableMemory->start(); - ASSERT(m_code); - ExecutableAllocator::makeWritable(m_code, m_initialSize); - uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode(); - uint8_t* outData = reinterpret_cast(m_code); - int readPtr = 0; - int writePtr = 0; - Vector& jumpsToLink = m_assembler->jumpsToLink(); - unsigned jumpCount = jumpsToLink.size(); - for (unsigned i = 0; i < jumpCount; ++i) { - int offset = readPtr - writePtr; - ASSERT(!(offset & 1)); - - // Copy the instructions from the last jump to the current one. - size_t regionSize = jumpsToLink[i].from() - readPtr; - uint16_t* copySource = reinterpret_cast_ptr(inData + readPtr); - uint16_t* copyEnd = reinterpret_cast_ptr(inData + readPtr + regionSize); - uint16_t* copyDst = reinterpret_cast_ptr(outData + writePtr); - ASSERT(!(regionSize % 2)); - ASSERT(!(readPtr % 2)); - ASSERT(!(writePtr % 2)); - while (copySource != copyEnd) - *copyDst++ = *copySource++; - m_assembler->recordLinkOffsets(readPtr, jumpsToLink[i].from(), offset); - readPtr += regionSize; - writePtr += regionSize; - - // Calculate absolute address of the jump target, in the case of backwards - // branches we need to be precise, forward branches we are pessimistic - const uint8_t* target; - if (jumpsToLink[i].to() >= jumpsToLink[i].from()) - target = outData + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far - else - target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); - - JumpLinkType jumpLinkType = m_assembler->computeJumpType(jumpsToLink[i], outData + writePtr, target); - // Compact branch if we can... - if (m_assembler->canCompact(jumpsToLink[i].type())) { - // Step back in the write stream - int32_t delta = m_assembler->jumpSizeDelta(jumpsToLink[i].type(), jumpLinkType); - if (delta) { - writePtr -= delta; - m_assembler->recordLinkOffsets(jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr); - } - } - jumpsToLink[i].setFrom(writePtr); - } - // Copy everything after the last jump - memcpy(outData + writePtr, inData + readPtr, m_initialSize - readPtr); - m_assembler->recordLinkOffsets(readPtr, m_initialSize, readPtr - writePtr); - - for (unsigned i = 0; i < jumpCount; ++i) { - uint8_t* location = outData + jumpsToLink[i].from(); - uint8_t* target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); - m_assembler->link(jumpsToLink[i], location, target); - } - - jumpsToLink.clear(); - m_size = writePtr + m_initialSize - readPtr; - m_executableMemory->shrink(m_size); - -#if DUMP_LINK_STATISTICS - dumpLinkStatistics(m_code, m_initialSize, m_size); -#endif -#if DUMP_CODE - dumpCode(m_code, m_size); -#endif -#endif -} - -void LinkBuffer::performFinalization() -{ -#ifndef NDEBUG - ASSERT(!m_completed); - ASSERT(isValid()); - m_completed = true; -#endif - -#if ENABLE(BRANCH_COMPACTION) - ExecutableAllocator::makeExecutable(code(), m_initialSize); -#else - ExecutableAllocator::makeExecutable(code(), m_size); -#endif - MacroAssembler::cacheFlush(code(), m_size); -} - -#if DUMP_LINK_STATISTICS -void LinkBuffer::dumpLinkStatistics(void* code, size_t initializeSize, size_t finalSize) -{ - static unsigned linkCount = 0; - static unsigned totalInitialSize = 0; - static unsigned totalFinalSize = 0; - linkCount++; - totalInitialSize += initialSize; - totalFinalSize += finalSize; - dataLogF("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", - code, static_cast(initialSize), static_cast(finalSize), - static_cast(initialSize - finalSize), - 100.0 * (initialSize - finalSize) / initialSize); - dataLogF("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", - linkCount, totalInitialSize, totalFinalSize, totalInitialSize - totalFinalSize, - 100.0 * (totalInitialSize - totalFinalSize) / totalInitialSize); -} -#endif - -#if DUMP_CODE -void LinkBuffer::dumpCode(void* code, size_t size) -{ -#if CPU(ARM_THUMB2) - // Dump the generated code in an asm file format that can be assembled and then disassembled - // for debugging purposes. For example, save this output as jit.s: - // gcc -arch armv7 -c jit.s - // otool -tv jit.o - static unsigned codeCount = 0; - unsigned short* tcode = static_cast(code); - size_t tsize = size / sizeof(short); - char nameBuf[128]; - snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); - dataLogF("\t.syntax unified\n" - "\t.section\t__TEXT,__text,regular,pure_instructions\n" - "\t.globl\t%s\n" - "\t.align 2\n" - "\t.code 16\n" - "\t.thumb_func\t%s\n" - "# %p\n" - "%s:\n", nameBuf, nameBuf, code, nameBuf); - - for (unsigned i = 0; i < tsize; i++) - dataLogF("\t.short\t0x%x\n", tcode[i]); -#elif CPU(ARM_TRADITIONAL) - // gcc -c jit.s - // objdump -D jit.o - static unsigned codeCount = 0; - unsigned int* tcode = static_cast(code); - size_t tsize = size / sizeof(unsigned int); - char nameBuf[128]; - snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); - dataLogF("\t.globl\t%s\n" - "\t.align 4\n" - "\t.code 32\n" - "\t.text\n" - "# %p\n" - "%s:\n", nameBuf, code, nameBuf); - - for (unsigned i = 0; i < tsize; i++) - dataLogF("\t.long\t0x%x\n", tcode[i]); -#endif -} -#endif - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - - diff --git a/masm/assembler/LinkBuffer.h b/masm/assembler/LinkBuffer.h deleted file mode 100644 index e1882433c1..0000000000 --- a/masm/assembler/LinkBuffer.h +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright (C) 2009, 2010, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef LinkBuffer_h -#define LinkBuffer_h - -#if ENABLE(ASSEMBLER) - -#define DUMP_LINK_STATISTICS 0 -#define DUMP_CODE 0 - -#define GLOBAL_THUNK_ID reinterpret_cast(static_cast(-1)) -#define REGEXP_CODE_ID reinterpret_cast(static_cast(-2)) - -#include "JITCompilationEffort.h" -#include "MacroAssembler.h" -#include -#include - -namespace JSC { - -class JSGlobalData; - -// LinkBuffer: -// -// This class assists in linking code generated by the macro assembler, once code generation -// has been completed, and the code has been copied to is final location in memory. At this -// time pointers to labels within the code may be resolved, and relative offsets to external -// addresses may be fixed. -// -// Specifically: -// * Jump objects may be linked to external targets, -// * The address of Jump objects may taken, such that it can later be relinked. -// * The return address of a Call may be acquired. -// * The address of a Label pointing into the code may be resolved. -// * The value referenced by a DataLabel may be set. -// -class LinkBuffer { - WTF_MAKE_NONCOPYABLE(LinkBuffer); - typedef MacroAssemblerCodeRef CodeRef; - typedef MacroAssemblerCodePtr CodePtr; - typedef MacroAssembler::Label Label; - typedef MacroAssembler::Jump Jump; - typedef MacroAssembler::PatchableJump PatchableJump; - typedef MacroAssembler::JumpList JumpList; - typedef MacroAssembler::Call Call; - typedef MacroAssembler::DataLabelCompact DataLabelCompact; - typedef MacroAssembler::DataLabel32 DataLabel32; - typedef MacroAssembler::DataLabelPtr DataLabelPtr; - typedef MacroAssembler::ConvertibleLoadLabel ConvertibleLoadLabel; -#if ENABLE(BRANCH_COMPACTION) - typedef MacroAssembler::LinkRecord LinkRecord; - typedef MacroAssembler::JumpLinkType JumpLinkType; -#endif - -public: - LinkBuffer(JSGlobalData& globalData, MacroAssembler* masm, void* ownerUID, JITCompilationEffort effort = JITCompilationMustSucceed) - : m_size(0) -#if ENABLE(BRANCH_COMPACTION) - , m_initialSize(0) -#endif - , m_code(0) - , m_assembler(masm) - , m_globalData(&globalData) -#ifndef NDEBUG - , m_completed(false) - , m_effort(effort) -#endif - { - linkCode(ownerUID, effort); - } - - ~LinkBuffer() - { - ASSERT(m_completed || (!m_executableMemory && m_effort == JITCompilationCanFail)); - } - - bool didFailToAllocate() const - { - return !m_executableMemory; - } - - bool isValid() const - { - return !didFailToAllocate(); - } - - // These methods are used to link or set values at code generation time. - - void link(Call call, FunctionPtr function) - { - ASSERT(call.isFlagSet(Call::Linkable)); - call.m_label = applyOffset(call.m_label); - MacroAssembler::linkCall(code(), call, function); - } - - void link(Jump jump, CodeLocationLabel label) - { - jump.m_label = applyOffset(jump.m_label); - MacroAssembler::linkJump(code(), jump, label); - } - - void link(JumpList list, CodeLocationLabel label) - { - for (unsigned i = 0; i < list.m_jumps.size(); ++i) - link(list.m_jumps[i], label); - } - - void patch(DataLabelPtr label, void* value) - { - AssemblerLabel target = applyOffset(label.m_label); - MacroAssembler::linkPointer(code(), target, value); - } - - void patch(DataLabelPtr label, CodeLocationLabel value) - { - AssemblerLabel target = applyOffset(label.m_label); - MacroAssembler::linkPointer(code(), target, value.executableAddress()); - } - - // These methods are used to obtain handles to allow the code to be relinked / repatched later. - - CodeLocationCall locationOf(Call call) - { - ASSERT(call.isFlagSet(Call::Linkable)); - ASSERT(!call.isFlagSet(Call::Near)); - return CodeLocationCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_label))); - } - - CodeLocationNearCall locationOfNearCall(Call call) - { - ASSERT(call.isFlagSet(Call::Linkable)); - ASSERT(call.isFlagSet(Call::Near)); - return CodeLocationNearCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_label))); - } - - CodeLocationLabel locationOf(PatchableJump jump) - { - return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(jump.m_jump.m_label))); - } - - CodeLocationLabel locationOf(Label label) - { - return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); - } - - CodeLocationDataLabelPtr locationOf(DataLabelPtr label) - { - return CodeLocationDataLabelPtr(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); - } - - CodeLocationDataLabel32 locationOf(DataLabel32 label) - { - return CodeLocationDataLabel32(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); - } - - CodeLocationDataLabelCompact locationOf(DataLabelCompact label) - { - return CodeLocationDataLabelCompact(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); - } - - CodeLocationConvertibleLoad locationOf(ConvertibleLoadLabel label) - { - return CodeLocationConvertibleLoad(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); - } - - // This method obtains the return address of the call, given as an offset from - // the start of the code. - unsigned returnAddressOffset(Call call) - { - call.m_label = applyOffset(call.m_label); - return MacroAssembler::getLinkerCallReturnOffset(call); - } - - uint32_t offsetOf(Label label) - { - return applyOffset(label.m_label).m_offset; - } - - // Upon completion of all patching 'FINALIZE_CODE()' should be called once to - // complete generation of the code. Alternatively, call - // finalizeCodeWithoutDisassembly() directly if you have your own way of - // displaying disassembly. - - CodeRef finalizeCodeWithoutDisassembly(); - CodeRef finalizeCodeWithDisassembly(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); - - CodePtr trampolineAt(Label label) - { - return CodePtr(MacroAssembler::AssemblerType_T::getRelocatedAddress(code(), applyOffset(label.m_label))); - } - - void* debugAddress() - { - return m_code; - } - - size_t debugSize() - { - return m_size; - } - -private: - template T applyOffset(T src) - { -#if ENABLE(BRANCH_COMPACTION) - src.m_offset -= m_assembler->executableOffsetFor(src.m_offset); -#endif - return src; - } - - // Keep this private! - the underlying code should only be obtained externally via finalizeCode(). - void* code() - { - return m_code; - } - - void linkCode(void* ownerUID, JITCompilationEffort); - - void performFinalization(); - -#if DUMP_LINK_STATISTICS - static void dumpLinkStatistics(void* code, size_t initialSize, size_t finalSize); -#endif - -#if DUMP_CODE - static void dumpCode(void* code, size_t); -#endif - - RefPtr m_executableMemory; - size_t m_size; -#if ENABLE(BRANCH_COMPACTION) - size_t m_initialSize; -#endif - void* m_code; - MacroAssembler* m_assembler; - JSGlobalData* m_globalData; -#ifndef NDEBUG - bool m_completed; - JITCompilationEffort m_effort; -#endif -}; - -#define FINALIZE_CODE_IF(condition, linkBufferReference, dataLogFArgumentsForHeading) \ - (UNLIKELY((condition)) \ - ? ((linkBufferReference).finalizeCodeWithDisassembly dataLogFArgumentsForHeading) \ - : (linkBufferReference).finalizeCodeWithoutDisassembly()) - -// Use this to finalize code, like so: -// -// CodeRef code = FINALIZE_CODE(linkBuffer, ("my super thingy number %d", number)); -// -// Which, in disassembly mode, will print: -// -// Generated JIT code for my super thingy number 42: -// Code at [0x123456, 0x234567]: -// 0x123456: mov $0, 0 -// 0x12345a: ret -// -// ... and so on. -// -// Note that the dataLogFArgumentsForHeading are only evaluated when showDisassembly -// is true, so you can hide expensive disassembly-only computations inside there. - -#define FINALIZE_CODE(linkBufferReference, dataLogFArgumentsForHeading) \ - FINALIZE_CODE_IF(Options::showDisassembly(), linkBufferReference, dataLogFArgumentsForHeading) - -#define FINALIZE_DFG_CODE(linkBufferReference, dataLogFArgumentsForHeading) \ - FINALIZE_CODE_IF((Options::showDisassembly() || Options::showDFGDisassembly()), linkBufferReference, dataLogFArgumentsForHeading) - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // LinkBuffer_h diff --git a/masm/assembler/MIPSAssembler.h b/masm/assembler/MIPSAssembler.h deleted file mode 100644 index 026f87e52a..0000000000 --- a/masm/assembler/MIPSAssembler.h +++ /dev/null @@ -1,1032 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * Copyright (C) 2009 University of Szeged - * All rights reserved. - * Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY MIPS TECHNOLOGIES, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MIPS TECHNOLOGIES, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MIPSAssembler_h -#define MIPSAssembler_h - -#if ENABLE(ASSEMBLER) && CPU(MIPS) - -#include "AssemblerBuffer.h" -#include "JITCompilationEffort.h" -#include -#include - -namespace JSC { - -typedef uint32_t MIPSWord; - -namespace MIPSRegisters { -typedef enum { - r0 = 0, - r1, - r2, - r3, - r4, - r5, - r6, - r7, - r8, - r9, - r10, - r11, - r12, - r13, - r14, - r15, - r16, - r17, - r18, - r19, - r20, - r21, - r22, - r23, - r24, - r25, - r26, - r27, - r28, - r29, - r30, - r31, - zero = r0, - at = r1, - v0 = r2, - v1 = r3, - a0 = r4, - a1 = r5, - a2 = r6, - a3 = r7, - t0 = r8, - t1 = r9, - t2 = r10, - t3 = r11, - t4 = r12, - t5 = r13, - t6 = r14, - t7 = r15, - s0 = r16, - s1 = r17, - s2 = r18, - s3 = r19, - s4 = r20, - s5 = r21, - s6 = r22, - s7 = r23, - t8 = r24, - t9 = r25, - k0 = r26, - k1 = r27, - gp = r28, - sp = r29, - fp = r30, - ra = r31 -} RegisterID; - -typedef enum { - f0, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - f18, - f19, - f20, - f21, - f22, - f23, - f24, - f25, - f26, - f27, - f28, - f29, - f30, - f31 -} FPRegisterID; - -} // namespace MIPSRegisters - -class MIPSAssembler { -public: - typedef MIPSRegisters::RegisterID RegisterID; - typedef MIPSRegisters::FPRegisterID FPRegisterID; - typedef SegmentedVector Jumps; - - MIPSAssembler() - { - } - - // MIPS instruction opcode field position - enum { - OP_SH_RD = 11, - OP_SH_RT = 16, - OP_SH_RS = 21, - OP_SH_SHAMT = 6, - OP_SH_CODE = 16, - OP_SH_FD = 6, - OP_SH_FS = 11, - OP_SH_FT = 16 - }; - - void emitInst(MIPSWord op) - { - void* oldBase = m_buffer.data(); - - m_buffer.putInt(op); - - void* newBase = m_buffer.data(); - if (oldBase != newBase) - relocateJumps(oldBase, newBase); - } - - void nop() - { - emitInst(0x00000000); - } - - /* Need to insert one load data delay nop for mips1. */ - void loadDelayNop() - { -#if WTF_MIPS_ISA(1) - nop(); -#endif - } - - /* Need to insert one coprocessor access delay nop for mips1. */ - void copDelayNop() - { -#if WTF_MIPS_ISA(1) - nop(); -#endif - } - - void move(RegisterID rd, RegisterID rs) - { - /* addu */ - emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS)); - } - - /* Set an immediate value to a register. This may generate 1 or 2 - instructions. */ - void li(RegisterID dest, int imm) - { - if (imm >= -32768 && imm <= 32767) - addiu(dest, MIPSRegisters::zero, imm); - else if (imm >= 0 && imm < 65536) - ori(dest, MIPSRegisters::zero, imm); - else { - lui(dest, imm >> 16); - if (imm & 0xffff) - ori(dest, dest, imm); - } - } - - void lui(RegisterID rt, int imm) - { - emitInst(0x3c000000 | (rt << OP_SH_RT) | (imm & 0xffff)); - } - - void addiu(RegisterID rt, RegisterID rs, int imm) - { - emitInst(0x24000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); - } - - void addu(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void subu(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x00000023 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void mult(RegisterID rs, RegisterID rt) - { - emitInst(0x00000018 | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void div(RegisterID rs, RegisterID rt) - { - emitInst(0x0000001a | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void mfhi(RegisterID rd) - { - emitInst(0x00000010 | (rd << OP_SH_RD)); - } - - void mflo(RegisterID rd) - { - emitInst(0x00000012 | (rd << OP_SH_RD)); - } - - void mul(RegisterID rd, RegisterID rs, RegisterID rt) - { -#if WTF_MIPS_ISA_AT_LEAST(32) - emitInst(0x70000002 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); -#else - mult(rs, rt); - mflo(rd); -#endif - } - - void andInsn(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x00000024 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void andi(RegisterID rt, RegisterID rs, int imm) - { - emitInst(0x30000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); - } - - void nor(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x00000027 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void orInsn(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x00000025 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void ori(RegisterID rt, RegisterID rs, int imm) - { - emitInst(0x34000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); - } - - void xorInsn(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x00000026 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void xori(RegisterID rt, RegisterID rs, int imm) - { - emitInst(0x38000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); - } - - void slt(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x0000002a | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void sltu(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x0000002b | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void sltiu(RegisterID rt, RegisterID rs, int imm) - { - emitInst(0x2c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); - } - - void sll(RegisterID rd, RegisterID rt, int shamt) - { - emitInst(0x00000000 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); - } - - void sllv(RegisterID rd, RegisterID rt, int rs) - { - emitInst(0x00000004 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); - } - - void sra(RegisterID rd, RegisterID rt, int shamt) - { - emitInst(0x00000003 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); - } - - void srav(RegisterID rd, RegisterID rt, RegisterID rs) - { - emitInst(0x00000007 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); - } - - void srl(RegisterID rd, RegisterID rt, int shamt) - { - emitInst(0x00000002 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); - } - - void srlv(RegisterID rd, RegisterID rt, RegisterID rs) - { - emitInst(0x00000006 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); - } - - void lb(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0x80000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - loadDelayNop(); - } - - void lbu(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0x90000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - loadDelayNop(); - } - - void lw(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0x8c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - loadDelayNop(); - } - - void lwl(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0x88000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - loadDelayNop(); - } - - void lwr(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0x98000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - loadDelayNop(); - } - - void lh(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0x84000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - loadDelayNop(); - } - - void lhu(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0x94000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - loadDelayNop(); - } - - void sb(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0xa0000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - } - - void sh(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0xa4000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - } - - void sw(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0xac000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - } - - void jr(RegisterID rs) - { - emitInst(0x00000008 | (rs << OP_SH_RS)); - } - - void jalr(RegisterID rs) - { - emitInst(0x0000f809 | (rs << OP_SH_RS)); - } - - void jal() - { - emitInst(0x0c000000); - } - - void bkpt() - { - int value = 512; /* BRK_BUG */ - emitInst(0x0000000d | ((value & 0x3ff) << OP_SH_CODE)); - } - - void bgez(RegisterID rs, int imm) - { - emitInst(0x04010000 | (rs << OP_SH_RS) | (imm & 0xffff)); - } - - void bltz(RegisterID rs, int imm) - { - emitInst(0x04000000 | (rs << OP_SH_RS) | (imm & 0xffff)); - } - - void beq(RegisterID rs, RegisterID rt, int imm) - { - emitInst(0x10000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff)); - } - - void bne(RegisterID rs, RegisterID rt, int imm) - { - emitInst(0x14000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff)); - } - - void bc1t() - { - emitInst(0x45010000); - } - - void bc1f() - { - emitInst(0x45000000); - } - - void appendJump() - { - m_jumps.append(m_buffer.label()); - } - - void addd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200000 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - } - - void subd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200001 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - } - - void muld(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200002 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - } - - void divd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200003 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - } - - void lwc1(FPRegisterID ft, RegisterID rs, int offset) - { - emitInst(0xc4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); - copDelayNop(); - } - - void ldc1(FPRegisterID ft, RegisterID rs, int offset) - { - emitInst(0xd4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); - } - - void swc1(FPRegisterID ft, RegisterID rs, int offset) - { - emitInst(0xe4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); - } - - void sdc1(FPRegisterID ft, RegisterID rs, int offset) - { - emitInst(0xf4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); - } - - void mtc1(RegisterID rt, FPRegisterID fs) - { - emitInst(0x44800000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); - copDelayNop(); - } - - void mthc1(RegisterID rt, FPRegisterID fs) - { - emitInst(0x44e00000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); - copDelayNop(); - } - - void mfc1(RegisterID rt, FPRegisterID fs) - { - emitInst(0x44000000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); - copDelayNop(); - } - - void sqrtd(FPRegisterID fd, FPRegisterID fs) - { - emitInst(0x46200004 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); - } - - void truncwd(FPRegisterID fd, FPRegisterID fs) - { - emitInst(0x4620000d | (fd << OP_SH_FD) | (fs << OP_SH_FS)); - } - - void cvtdw(FPRegisterID fd, FPRegisterID fs) - { - emitInst(0x46800021 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); - } - - void cvtds(FPRegisterID fd, FPRegisterID fs) - { - emitInst(0x46000021 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); - } - - void cvtwd(FPRegisterID fd, FPRegisterID fs) - { - emitInst(0x46200024 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); - } - - void cvtsd(FPRegisterID fd, FPRegisterID fs) - { - emitInst(0x46200020 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); - } - - void ceqd(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200032 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void cngtd(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x4620003f | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void cnged(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x4620003d | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void cltd(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x4620003c | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void cled(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x4620003e | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void cueqd(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200033 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void coled(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200036 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void coltd(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200034 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void culed(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200037 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void cultd(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200035 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - // General helpers - - AssemblerLabel labelIgnoringWatchpoints() - { - return m_buffer.label(); - } - - AssemblerLabel label() - { - return m_buffer.label(); - } - - AssemblerLabel align(int alignment) - { - while (!m_buffer.isAligned(alignment)) - bkpt(); - - return label(); - } - - static void* getRelocatedAddress(void* code, AssemblerLabel label) - { - return reinterpret_cast(reinterpret_cast(code) + label.m_offset); - } - - static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) - { - return b.m_offset - a.m_offset; - } - - // Assembler admin methods: - - size_t codeSize() const - { - return m_buffer.codeSize(); - } - - PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) - { - RefPtr result = m_buffer.executableCopy(globalData, ownerUID, effort); - if (!result) - return 0; - - relocateJumps(m_buffer.data(), result->start()); - return result.release(); - } - - unsigned debugOffset() { return m_buffer.debugOffset(); } - - // Assembly helpers for moving data between fp and registers. - void vmov(RegisterID rd1, RegisterID rd2, FPRegisterID rn) - { - mfc1(rd1, rn); - mfc1(rd2, FPRegisterID(rn + 1)); - } - - void vmov(FPRegisterID rd, RegisterID rn1, RegisterID rn2) - { - mtc1(rn1, rd); - mtc1(rn2, FPRegisterID(rd + 1)); - } - - static unsigned getCallReturnOffset(AssemblerLabel call) - { - // The return address is after a call and a delay slot instruction - return call.m_offset; - } - - // Linking & patching: - // - // 'link' and 'patch' methods are for use on unprotected code - such as the code - // within the AssemblerBuffer, and code being patched by the patch buffer. Once - // code has been finalized it is (platform support permitting) within a non- - // writable region of memory; to modify the code in an execute-only execuable - // pool the 'repatch' and 'relink' methods should be used. - - void linkJump(AssemblerLabel from, AssemblerLabel to) - { - ASSERT(to.isSet()); - ASSERT(from.isSet()); - MIPSWord* insn = reinterpret_cast(reinterpret_cast(m_buffer.data()) + from.m_offset); - MIPSWord* toPos = reinterpret_cast(reinterpret_cast(m_buffer.data()) + to.m_offset); - - ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5))); - insn = insn - 6; - linkWithOffset(insn, toPos); - } - - static void linkJump(void* code, AssemblerLabel from, void* to) - { - ASSERT(from.isSet()); - MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); - - ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5))); - insn = insn - 6; - linkWithOffset(insn, to); - } - - static void linkCall(void* code, AssemblerLabel from, void* to) - { - MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); - linkCallInternal(insn, to); - } - - static void linkPointer(void* code, AssemblerLabel from, void* to) - { - MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); - ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui - *insn = (*insn & 0xffff0000) | ((reinterpret_cast(to) >> 16) & 0xffff); - insn++; - ASSERT((*insn & 0xfc000000) == 0x34000000); // ori - *insn = (*insn & 0xffff0000) | (reinterpret_cast(to) & 0xffff); - } - - static void relinkJump(void* from, void* to) - { - MIPSWord* insn = reinterpret_cast(from); - - ASSERT(!(*(insn - 1)) && !(*(insn - 5))); - insn = insn - 6; - int flushSize = linkWithOffset(insn, to); - - cacheFlush(insn, flushSize); - } - - static void relinkCall(void* from, void* to) - { - void* start; - int size = linkCallInternal(from, to); - if (size == sizeof(MIPSWord)) - start = reinterpret_cast(reinterpret_cast(from) - 2 * sizeof(MIPSWord)); - else - start = reinterpret_cast(reinterpret_cast(from) - 4 * sizeof(MIPSWord)); - - cacheFlush(start, size); - } - - static void repatchInt32(void* from, int32_t to) - { - MIPSWord* insn = reinterpret_cast(from); - ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui - *insn = (*insn & 0xffff0000) | ((to >> 16) & 0xffff); - insn++; - ASSERT((*insn & 0xfc000000) == 0x34000000); // ori - *insn = (*insn & 0xffff0000) | (to & 0xffff); - insn--; - cacheFlush(insn, 2 * sizeof(MIPSWord)); - } - - static int32_t readInt32(void* from) - { - MIPSWord* insn = reinterpret_cast(from); - ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui - int32_t result = (*insn & 0x0000ffff) << 16; - insn++; - ASSERT((*insn & 0xfc000000) == 0x34000000); // ori - result |= *insn & 0x0000ffff; - return result; - } - - static void repatchCompact(void* where, int32_t value) - { - repatchInt32(where, value); - } - - static void repatchPointer(void* from, void* to) - { - repatchInt32(from, reinterpret_cast(to)); - } - - static void* readPointer(void* from) - { - return reinterpret_cast(readInt32(from)); - } - - static void* readCallTarget(void* from) - { - MIPSWord* insn = reinterpret_cast(from); - insn -= 4; - ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui - int32_t result = (*insn & 0x0000ffff) << 16; - insn++; - ASSERT((*insn & 0xfc000000) == 0x34000000); // ori - result |= *insn & 0x0000ffff; - return reinterpret_cast(result); - } - - static void cacheFlush(void* code, size_t size) - { -#if GCC_VERSION_AT_LEAST(4, 3, 0) -#if WTF_MIPS_ISA_REV(2) && !GCC_VERSION_AT_LEAST(4, 4, 3) - int lineSize; - asm("rdhwr %0, $1" : "=r" (lineSize)); - // - // Modify "start" and "end" to avoid GCC 4.3.0-4.4.2 bug in - // mips_expand_synci_loop that may execute synci one more time. - // "start" points to the fisrt byte of the cache line. - // "end" points to the last byte of the line before the last cache line. - // Because size is always a multiple of 4, this is safe to set - // "end" to the last byte. - // - intptr_t start = reinterpret_cast(code) & (-lineSize); - intptr_t end = ((reinterpret_cast(code) + size - 1) & (-lineSize)) - 1; - __builtin___clear_cache(reinterpret_cast(start), reinterpret_cast(end)); -#else - intptr_t end = reinterpret_cast(code) + size; - __builtin___clear_cache(reinterpret_cast(code), reinterpret_cast(end)); -#endif -#else - _flush_cache(reinterpret_cast(code), size, BCACHE); -#endif - } - - static void revertJumpToMove(void* instructionStart, RegisterID rt, int imm) - { - MIPSWord* insn = static_cast(instructionStart) + 1; - ASSERT((*insn & 0xfc000000) == 0x34000000); - *insn = (*insn & 0xfc1f0000) | (imm & 0xffff); - cacheFlush(insn, sizeof(MIPSWord)); - } - - static void replaceWithJump(void* instructionStart, void* to) - { - MIPSWord* instruction = reinterpret_cast(instructionStart); - intptr_t jumpTo = reinterpret_cast(to); - - // lui - instruction[0] = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((jumpTo >> 16) & 0xffff); - // ori - instruction[1] = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (jumpTo & 0xffff); - // jr - instruction[2] = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); - // nop - instruction[3] = 0x0; - - cacheFlush(instruction, sizeof(MIPSWord) * 4); - } - - static void replaceWithLoad(void* instructionStart) - { - MIPSWord* insn = reinterpret_cast(instructionStart); - ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui - insn++; - ASSERT((*insn & 0xfc0007ff) == 0x00000021); // addu - insn++; - *insn = 0x8c000000 | ((*insn) & 0x3ffffff); // lw - cacheFlush(insn, 4); - } - - static void replaceWithAddressComputation(void* instructionStart) - { - MIPSWord* insn = reinterpret_cast(instructionStart); - ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui - insn++; - ASSERT((*insn & 0xfc0007ff) == 0x00000021); // addu - insn++; - *insn = 0x24000000 | ((*insn) & 0x3ffffff); // addiu - cacheFlush(insn, 4); - } - -private: - /* Update each jump in the buffer of newBase. */ - void relocateJumps(void* oldBase, void* newBase) - { - // Check each jump - for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) { - int pos = iter->m_offset; - MIPSWord* insn = reinterpret_cast(reinterpret_cast(newBase) + pos); - insn = insn + 2; - // Need to make sure we have 5 valid instructions after pos - if ((unsigned)pos >= m_buffer.codeSize() - 5 * sizeof(MIPSWord)) - continue; - - if ((*insn & 0xfc000000) == 0x08000000) { // j - int offset = *insn & 0x03ffffff; - int oldInsnAddress = (int)insn - (int)newBase + (int)oldBase; - int topFourBits = (oldInsnAddress + 4) >> 28; - int oldTargetAddress = (topFourBits << 28) | (offset << 2); - int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase; - int newInsnAddress = (int)insn; - if (((newInsnAddress + 4) >> 28) == (newTargetAddress >> 28)) - *insn = 0x08000000 | ((newTargetAddress >> 2) & 0x3ffffff); - else { - /* lui */ - *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); - /* ori */ - *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); - /* jr */ - *(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); - } - } else if ((*insn & 0xffe00000) == 0x3c000000) { // lui - int high = (*insn & 0xffff) << 16; - int low = *(insn + 1) & 0xffff; - int oldTargetAddress = high | low; - int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase; - /* lui */ - *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); - /* ori */ - *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); - } - } - } - - static int linkWithOffset(MIPSWord* insn, void* to) - { - ASSERT((*insn & 0xfc000000) == 0x10000000 // beq - || (*insn & 0xfc000000) == 0x14000000 // bne - || (*insn & 0xffff0000) == 0x45010000 // bc1t - || (*insn & 0xffff0000) == 0x45000000); // bc1f - intptr_t diff = (reinterpret_cast(to) - reinterpret_cast(insn) - 4) >> 2; - - if (diff < -32768 || diff > 32767 || *(insn + 2) != 0x10000003) { - /* - Convert the sequence: - beq $2, $3, target - nop - b 1f - nop - nop - nop - 1: - - to the new sequence if possible: - bne $2, $3, 1f - nop - j target - nop - nop - nop - 1: - - OR to the new sequence: - bne $2, $3, 1f - nop - lui $25, target >> 16 - ori $25, $25, target & 0xffff - jr $25 - nop - 1: - - Note: beq/bne/bc1t/bc1f are converted to bne/beq/bc1f/bc1t. - */ - - if (*(insn + 2) == 0x10000003) { - if ((*insn & 0xfc000000) == 0x10000000) // beq - *insn = (*insn & 0x03ff0000) | 0x14000005; // bne - else if ((*insn & 0xfc000000) == 0x14000000) // bne - *insn = (*insn & 0x03ff0000) | 0x10000005; // beq - else if ((*insn & 0xffff0000) == 0x45010000) // bc1t - *insn = 0x45000005; // bc1f - else if ((*insn & 0xffff0000) == 0x45000000) // bc1f - *insn = 0x45010005; // bc1t - else - ASSERT(0); - } - - insn = insn + 2; - if ((reinterpret_cast(insn) + 4) >> 28 - == reinterpret_cast(to) >> 28) { - *insn = 0x08000000 | ((reinterpret_cast(to) >> 2) & 0x3ffffff); - *(insn + 1) = 0; - return 4 * sizeof(MIPSWord); - } - - intptr_t newTargetAddress = reinterpret_cast(to); - /* lui */ - *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); - /* ori */ - *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); - /* jr */ - *(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); - return 5 * sizeof(MIPSWord); - } - - *insn = (*insn & 0xffff0000) | (diff & 0xffff); - return sizeof(MIPSWord); - } - - static int linkCallInternal(void* from, void* to) - { - MIPSWord* insn = reinterpret_cast(from); - insn = insn - 4; - - if ((*(insn + 2) & 0xfc000000) == 0x0c000000) { // jal - if ((reinterpret_cast(from) - 4) >> 28 - == reinterpret_cast(to) >> 28) { - *(insn + 2) = 0x0c000000 | ((reinterpret_cast(to) >> 2) & 0x3ffffff); - return sizeof(MIPSWord); - } - - /* lui $25, (to >> 16) & 0xffff */ - *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((reinterpret_cast(to) >> 16) & 0xffff); - /* ori $25, $25, to & 0xffff */ - *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (reinterpret_cast(to) & 0xffff); - /* jalr $25 */ - *(insn + 2) = 0x0000f809 | (MIPSRegisters::t9 << OP_SH_RS); - return 3 * sizeof(MIPSWord); - } - - ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui - ASSERT((*(insn + 1) & 0xfc000000) == 0x34000000); // ori - - /* lui */ - *insn = (*insn & 0xffff0000) | ((reinterpret_cast(to) >> 16) & 0xffff); - /* ori */ - *(insn + 1) = (*(insn + 1) & 0xffff0000) | (reinterpret_cast(to) & 0xffff); - return 2 * sizeof(MIPSWord); - } - - AssemblerBuffer m_buffer; - Jumps m_jumps; -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) && CPU(MIPS) - -#endif // MIPSAssembler_h diff --git a/masm/assembler/MacroAssembler.h b/masm/assembler/MacroAssembler.h deleted file mode 100644 index 3d57340f93..0000000000 --- a/masm/assembler/MacroAssembler.h +++ /dev/null @@ -1,1464 +0,0 @@ -/* - * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssembler_h -#define MacroAssembler_h - -#include - -#if ENABLE(ASSEMBLER) - -#if CPU(ARM_THUMB2) -#include "MacroAssemblerARMv7.h" -namespace JSC { typedef MacroAssemblerARMv7 MacroAssemblerBase; }; - -#elif CPU(ARM_TRADITIONAL) -#include "MacroAssemblerARM.h" -namespace JSC { typedef MacroAssemblerARM MacroAssemblerBase; }; - -#elif CPU(MIPS) -#include "MacroAssemblerMIPS.h" -namespace JSC { -typedef MacroAssemblerMIPS MacroAssemblerBase; -}; - -#elif CPU(X86) -#include "MacroAssemblerX86.h" -namespace JSC { typedef MacroAssemblerX86 MacroAssemblerBase; }; - -#elif CPU(X86_64) -#include "MacroAssemblerX86_64.h" -namespace JSC { typedef MacroAssemblerX86_64 MacroAssemblerBase; }; - -#elif CPU(SH4) -#include "MacroAssemblerSH4.h" -namespace JSC { -typedef MacroAssemblerSH4 MacroAssemblerBase; -}; - -#else -#error "The MacroAssembler is not supported on this platform." -#endif - -namespace JSC { - -class MacroAssembler : public MacroAssemblerBase { -public: - - using MacroAssemblerBase::pop; - using MacroAssemblerBase::jump; - using MacroAssemblerBase::branch32; - using MacroAssemblerBase::move; - -#if ENABLE(JIT_CONSTANT_BLINDING) - using MacroAssemblerBase::add32; - using MacroAssemblerBase::and32; - using MacroAssemblerBase::branchAdd32; - using MacroAssemblerBase::branchMul32; - using MacroAssemblerBase::branchSub32; - using MacroAssemblerBase::lshift32; - using MacroAssemblerBase::or32; - using MacroAssemblerBase::rshift32; - using MacroAssemblerBase::store32; - using MacroAssemblerBase::sub32; - using MacroAssemblerBase::urshift32; - using MacroAssemblerBase::xor32; -#endif - - static const double twoToThe32; // This is super useful for some double code. - - // Utilities used by the DFG JIT. -#if ENABLE(DFG_JIT) - using MacroAssemblerBase::invert; - - static DoubleCondition invert(DoubleCondition cond) - { - switch (cond) { - case DoubleEqual: - return DoubleNotEqualOrUnordered; - case DoubleNotEqual: - return DoubleEqualOrUnordered; - case DoubleGreaterThan: - return DoubleLessThanOrEqualOrUnordered; - case DoubleGreaterThanOrEqual: - return DoubleLessThanOrUnordered; - case DoubleLessThan: - return DoubleGreaterThanOrEqualOrUnordered; - case DoubleLessThanOrEqual: - return DoubleGreaterThanOrUnordered; - case DoubleEqualOrUnordered: - return DoubleNotEqual; - case DoubleNotEqualOrUnordered: - return DoubleEqual; - case DoubleGreaterThanOrUnordered: - return DoubleLessThanOrEqual; - case DoubleGreaterThanOrEqualOrUnordered: - return DoubleLessThan; - case DoubleLessThanOrUnordered: - return DoubleGreaterThanOrEqual; - case DoubleLessThanOrEqualOrUnordered: - return DoubleGreaterThan; - default: - ASSERT_NOT_REACHED(); - return DoubleEqual; // make compiler happy - } - } - - static bool isInvertible(ResultCondition cond) - { - switch (cond) { - case Zero: - case NonZero: - return true; - default: - return false; - } - } - - static ResultCondition invert(ResultCondition cond) - { - switch (cond) { - case Zero: - return NonZero; - case NonZero: - return Zero; - default: - ASSERT_NOT_REACHED(); - return Zero; // Make compiler happy for release builds. - } - } -#endif - - // Platform agnostic onvenience functions, - // described in terms of other macro assembly methods. - void pop() - { - addPtr(TrustedImm32(sizeof(void*)), stackPointerRegister); - } - - void peek(RegisterID dest, int index = 0) - { - loadPtr(Address(stackPointerRegister, (index * sizeof(void*))), dest); - } - - Address addressForPoke(int index) - { - return Address(stackPointerRegister, (index * sizeof(void*))); - } - - void poke(RegisterID src, int index = 0) - { - storePtr(src, addressForPoke(index)); - } - - void poke(TrustedImm32 value, int index = 0) - { - store32(value, addressForPoke(index)); - } - - void poke(TrustedImmPtr imm, int index = 0) - { - storePtr(imm, addressForPoke(index)); - } - -#if CPU(X86_64) - void peek64(RegisterID dest, int index = 0) - { - load64(Address(stackPointerRegister, (index * sizeof(void*))), dest); - } - - void poke(TrustedImm64 value, int index = 0) - { - store64(value, addressForPoke(index)); - } - - void poke64(RegisterID src, int index = 0) - { - store64(src, addressForPoke(index)); - } -#endif - - - // Backwards banches, these are currently all implemented using existing forwards branch mechanisms. - void branchPtr(RelationalCondition cond, RegisterID op1, TrustedImmPtr imm, Label target) - { - branchPtr(cond, op1, imm).linkTo(target, this); - } - void branchPtr(RelationalCondition cond, RegisterID op1, ImmPtr imm, Label target) - { - branchPtr(cond, op1, imm).linkTo(target, this); - } - - void branch32(RelationalCondition cond, RegisterID op1, RegisterID op2, Label target) - { - branch32(cond, op1, op2).linkTo(target, this); - } - - void branch32(RelationalCondition cond, RegisterID op1, TrustedImm32 imm, Label target) - { - branch32(cond, op1, imm).linkTo(target, this); - } - - void branch32(RelationalCondition cond, RegisterID op1, Imm32 imm, Label target) - { - branch32(cond, op1, imm).linkTo(target, this); - } - - void branch32(RelationalCondition cond, RegisterID left, Address right, Label target) - { - branch32(cond, left, right).linkTo(target, this); - } - - Jump branch32(RelationalCondition cond, TrustedImm32 left, RegisterID right) - { - return branch32(commute(cond), right, left); - } - - Jump branch32(RelationalCondition cond, Imm32 left, RegisterID right) - { - return branch32(commute(cond), right, left); - } - - void branchTestPtr(ResultCondition cond, RegisterID reg, Label target) - { - branchTestPtr(cond, reg).linkTo(target, this); - } - -#if !CPU(ARM_THUMB2) - PatchableJump patchableBranchPtr(RelationalCondition cond, Address left, TrustedImmPtr right = TrustedImmPtr(0)) - { - return PatchableJump(branchPtr(cond, left, right)); - } - - PatchableJump patchableBranchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - return PatchableJump(branchPtrWithPatch(cond, left, dataLabel, initialRightValue)); - } - - PatchableJump patchableJump() - { - return PatchableJump(jump()); - } - - PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - return PatchableJump(branchTest32(cond, reg, mask)); - } -#endif // !CPU(ARM_THUMB2) - -#if !CPU(ARM) - PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) - { - return PatchableJump(branch32(cond, reg, imm)); - } -#endif // !(CPU(ARM) - - void jump(Label target) - { - jump().linkTo(target, this); - } - - // Commute a relational condition, returns a new condition that will produce - // the same results given the same inputs but with their positions exchanged. - static RelationalCondition commute(RelationalCondition condition) - { - switch (condition) { - case Above: - return Below; - case AboveOrEqual: - return BelowOrEqual; - case Below: - return Above; - case BelowOrEqual: - return AboveOrEqual; - case GreaterThan: - return LessThan; - case GreaterThanOrEqual: - return LessThanOrEqual; - case LessThan: - return GreaterThan; - case LessThanOrEqual: - return GreaterThanOrEqual; - default: - break; - } - - ASSERT(condition == Equal || condition == NotEqual); - return condition; - } - - static const unsigned BlindingModulus = 64; - bool shouldConsiderBlinding() - { - return !(random() & (BlindingModulus - 1)); - } - - // Ptr methods - // On 32-bit platforms (i.e. x86), these methods directly map onto their 32-bit equivalents. - // FIXME: should this use a test for 32-bitness instead of this specific exception? -#if !CPU(X86_64) - void addPtr(Address src, RegisterID dest) - { - add32(src, dest); - } - - void addPtr(AbsoluteAddress src, RegisterID dest) - { - add32(src, dest); - } - - void addPtr(RegisterID src, RegisterID dest) - { - add32(src, dest); - } - - void addPtr(TrustedImm32 imm, RegisterID srcDest) - { - add32(imm, srcDest); - } - - void addPtr(TrustedImmPtr imm, RegisterID dest) - { - add32(TrustedImm32(imm), dest); - } - - void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - add32(imm, src, dest); - } - - void addPtr(TrustedImm32 imm, AbsoluteAddress address) - { - add32(imm, address); - } - - void andPtr(RegisterID src, RegisterID dest) - { - and32(src, dest); - } - - void andPtr(TrustedImm32 imm, RegisterID srcDest) - { - and32(imm, srcDest); - } - - void negPtr(RegisterID dest) - { - neg32(dest); - } - - void orPtr(RegisterID src, RegisterID dest) - { - or32(src, dest); - } - - void orPtr(RegisterID op1, RegisterID op2, RegisterID dest) - { - or32(op1, op2, dest); - } - - void orPtr(TrustedImmPtr imm, RegisterID dest) - { - or32(TrustedImm32(imm), dest); - } - - void orPtr(TrustedImm32 imm, RegisterID dest) - { - or32(imm, dest); - } - - void subPtr(RegisterID src, RegisterID dest) - { - sub32(src, dest); - } - - void subPtr(TrustedImm32 imm, RegisterID dest) - { - sub32(imm, dest); - } - - void subPtr(TrustedImmPtr imm, RegisterID dest) - { - sub32(TrustedImm32(imm), dest); - } - - void xorPtr(RegisterID src, RegisterID dest) - { - xor32(src, dest); - } - - void xorPtr(TrustedImm32 imm, RegisterID srcDest) - { - xor32(imm, srcDest); - } - - - void loadPtr(ImplicitAddress address, RegisterID dest) - { - load32(address, dest); - } - - void loadPtr(BaseIndex address, RegisterID dest) - { - load32(address, dest); - } - - void loadPtr(const void* address, RegisterID dest) - { - load32(address, dest); - } - - DataLabel32 loadPtrWithAddressOffsetPatch(Address address, RegisterID dest) - { - return load32WithAddressOffsetPatch(address, dest); - } - - DataLabelCompact loadPtrWithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - return load32WithCompactAddressOffsetPatch(address, dest); - } - - void move(ImmPtr imm, RegisterID dest) - { - move(Imm32(imm.asTrustedImmPtr()), dest); - } - - void comparePtr(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - compare32(cond, left, right, dest); - } - - void storePtr(RegisterID src, ImplicitAddress address) - { - store32(src, address); - } - - void storePtr(RegisterID src, BaseIndex address) - { - store32(src, address); - } - - void storePtr(RegisterID src, void* address) - { - store32(src, address); - } - - void storePtr(TrustedImmPtr imm, ImplicitAddress address) - { - store32(TrustedImm32(imm), address); - } - - void storePtr(ImmPtr imm, Address address) - { - store32(Imm32(imm.asTrustedImmPtr()), address); - } - - void storePtr(TrustedImmPtr imm, void* address) - { - store32(TrustedImm32(imm), address); - } - - DataLabel32 storePtrWithAddressOffsetPatch(RegisterID src, Address address) - { - return store32WithAddressOffsetPatch(src, address); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, RegisterID right) - { - return branch32(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, TrustedImmPtr right) - { - return branch32(cond, left, TrustedImm32(right)); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, ImmPtr right) - { - return branch32(cond, left, Imm32(right.asTrustedImmPtr())); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, Address right) - { - return branch32(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, Address left, RegisterID right) - { - return branch32(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - return branch32(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, Address left, TrustedImmPtr right) - { - return branch32(cond, left, TrustedImm32(right)); - } - - Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, TrustedImmPtr right) - { - return branch32(cond, left, TrustedImm32(right)); - } - - Jump branchSubPtr(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchSub32(cond, src, dest); - } - - Jump branchTestPtr(ResultCondition cond, RegisterID reg, RegisterID mask) - { - return branchTest32(cond, reg, mask); - } - - Jump branchTestPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - return branchTest32(cond, reg, mask); - } - - Jump branchTestPtr(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - return branchTest32(cond, address, mask); - } - - Jump branchTestPtr(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - return branchTest32(cond, address, mask); - } - - Jump branchAddPtr(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchAdd32(cond, src, dest); - } - - Jump branchSubPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - return branchSub32(cond, imm, dest); - } - using MacroAssemblerBase::branchTest8; - Jump branchTest8(ResultCondition cond, ExtendedAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - return MacroAssemblerBase::branchTest8(cond, Address(address.base, address.offset), mask); - } -#else - void addPtr(RegisterID src, RegisterID dest) - { - add64(src, dest); - } - - void addPtr(Address src, RegisterID dest) - { - add64(src, dest); - } - - void addPtr(TrustedImm32 imm, RegisterID srcDest) - { - add64(imm, srcDest); - } - - void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - add64(imm, src, dest); - } - - void addPtr(TrustedImm32 imm, Address address) - { - add64(imm, address); - } - - void addPtr(AbsoluteAddress src, RegisterID dest) - { - add64(src, dest); - } - - void addPtr(TrustedImmPtr imm, RegisterID dest) - { - add64(TrustedImm64(imm), dest); - } - - void addPtr(TrustedImm32 imm, AbsoluteAddress address) - { - add64(imm, address); - } - - void andPtr(RegisterID src, RegisterID dest) - { - and64(src, dest); - } - - void andPtr(TrustedImm32 imm, RegisterID srcDest) - { - and64(imm, srcDest); - } - - void negPtr(RegisterID dest) - { - neg64(dest); - } - - void orPtr(RegisterID src, RegisterID dest) - { - or64(src, dest); - } - - void orPtr(TrustedImm32 imm, RegisterID dest) - { - or64(imm, dest); - } - - void orPtr(TrustedImmPtr imm, RegisterID dest) - { - or64(TrustedImm64(imm), dest); - } - - void orPtr(RegisterID op1, RegisterID op2, RegisterID dest) - { - or64(op1, op2, dest); - } - - void orPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - or64(imm, src, dest); - } - - void rotateRightPtr(TrustedImm32 imm, RegisterID srcDst) - { - rotateRight64(imm, srcDst); - } - - void subPtr(RegisterID src, RegisterID dest) - { - sub64(src, dest); - } - - void subPtr(TrustedImm32 imm, RegisterID dest) - { - sub64(imm, dest); - } - - void subPtr(TrustedImmPtr imm, RegisterID dest) - { - sub64(TrustedImm64(imm), dest); - } - - void xorPtr(RegisterID src, RegisterID dest) - { - xor64(src, dest); - } - - void xorPtr(RegisterID src, Address dest) - { - xor64(src, dest); - } - - void xorPtr(TrustedImm32 imm, RegisterID srcDest) - { - xor64(imm, srcDest); - } - - void loadPtr(ImplicitAddress address, RegisterID dest) - { - load64(address, dest); - } - - void loadPtr(BaseIndex address, RegisterID dest) - { - load64(address, dest); - } - - void loadPtr(const void* address, RegisterID dest) - { - load64(address, dest); - } - - DataLabel32 loadPtrWithAddressOffsetPatch(Address address, RegisterID dest) - { - return load64WithAddressOffsetPatch(address, dest); - } - - DataLabelCompact loadPtrWithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - return load64WithCompactAddressOffsetPatch(address, dest); - } - - void storePtr(RegisterID src, ImplicitAddress address) - { - store64(src, address); - } - - void storePtr(RegisterID src, BaseIndex address) - { - store64(src, address); - } - - void storePtr(RegisterID src, void* address) - { - store64(src, address); - } - - void storePtr(TrustedImmPtr imm, ImplicitAddress address) - { - store64(TrustedImm64(imm), address); - } - - void storePtr(TrustedImmPtr imm, BaseIndex address) - { - store64(TrustedImm64(imm), address); - } - - DataLabel32 storePtrWithAddressOffsetPatch(RegisterID src, Address address) - { - return store64WithAddressOffsetPatch(src, address); - } - - void comparePtr(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - compare64(cond, left, right, dest); - } - - void comparePtr(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) - { - compare64(cond, left, right, dest); - } - - void testPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) - { - test64(cond, reg, mask, dest); - } - - void testPtr(ResultCondition cond, RegisterID reg, RegisterID mask, RegisterID dest) - { - test64(cond, reg, mask, dest); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, RegisterID right) - { - return branch64(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, TrustedImmPtr right) - { - return branch64(cond, left, TrustedImm64(right)); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, Address right) - { - return branch64(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, Address left, RegisterID right) - { - return branch64(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - return branch64(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, Address left, TrustedImmPtr right) - { - return branch64(cond, left, TrustedImm64(right)); - } - - Jump branchTestPtr(ResultCondition cond, RegisterID reg, RegisterID mask) - { - return branchTest64(cond, reg, mask); - } - - Jump branchTestPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - return branchTest64(cond, reg, mask); - } - - Jump branchTestPtr(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - return branchTest64(cond, address, mask); - } - - Jump branchTestPtr(ResultCondition cond, Address address, RegisterID reg) - { - return branchTest64(cond, address, reg); - } - - Jump branchTestPtr(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - return branchTest64(cond, address, mask); - } - - Jump branchTestPtr(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - return branchTest64(cond, address, mask); - } - - Jump branchAddPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - return branchAdd64(cond, imm, dest); - } - - Jump branchAddPtr(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchAdd64(cond, src, dest); - } - - Jump branchSubPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - return branchSub64(cond, imm, dest); - } - - Jump branchSubPtr(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchSub64(cond, src, dest); - } - - Jump branchSubPtr(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) - { - return branchSub64(cond, src1, src2, dest); - } - -#if ENABLE(JIT_CONSTANT_BLINDING) - using MacroAssemblerBase::and64; - using MacroAssemblerBase::convertInt32ToDouble; - using MacroAssemblerBase::store64; - bool shouldBlindDouble(double value) - { - // Don't trust NaN or +/-Infinity - if (!isfinite(value)) - return shouldConsiderBlinding(); - - // Try to force normalisation, and check that there's no change - // in the bit pattern - if (bitwise_cast(value * 1.0) != bitwise_cast(value)) - return shouldConsiderBlinding(); - - value = abs(value); - // Only allow a limited set of fractional components - double scaledValue = value * 8; - if (scaledValue / 8 != value) - return shouldConsiderBlinding(); - double frac = scaledValue - floor(scaledValue); - if (frac != 0.0) - return shouldConsiderBlinding(); - - return value > 0xff; - } - - bool shouldBlind(ImmPtr imm) - { -#if !defined(NDEBUG) - UNUSED_PARAM(imm); - // Debug always blind all constants, if only so we know - // if we've broken blinding during patch development. - return true; -#endif - - // First off we'll special case common, "safe" values to avoid hurting - // performance too much - uintptr_t value = imm.asTrustedImmPtr().asIntptr(); - switch (value) { - case 0xffff: - case 0xffffff: - case 0xffffffffL: - case 0xffffffffffL: - case 0xffffffffffffL: - case 0xffffffffffffffL: - case 0xffffffffffffffffL: - return false; - default: { - if (value <= 0xff) - return false; - if (~value <= 0xff) - return false; - } - } - - if (!shouldConsiderBlinding()) - return false; - - return shouldBlindForSpecificArch(value); - } - - struct RotatedImmPtr { - RotatedImmPtr(uintptr_t v1, uint8_t v2) - : value(v1) - , rotation(v2) - { - } - TrustedImmPtr value; - TrustedImm32 rotation; - }; - - RotatedImmPtr rotationBlindConstant(ImmPtr imm) - { - uint8_t rotation = random() % (sizeof(void*) * 8); - uintptr_t value = imm.asTrustedImmPtr().asIntptr(); - value = (value << rotation) | (value >> (sizeof(void*) * 8 - rotation)); - return RotatedImmPtr(value, rotation); - } - - void loadRotationBlindedConstant(RotatedImmPtr constant, RegisterID dest) - { - move(constant.value, dest); - rotateRightPtr(constant.rotation, dest); - } - - bool shouldBlind(Imm64 imm) - { -#if !defined(NDEBUG) - UNUSED_PARAM(imm); - // Debug always blind all constants, if only so we know - // if we've broken blinding during patch development. - return true; -#endif - - // First off we'll special case common, "safe" values to avoid hurting - // performance too much - uint64_t value = imm.asTrustedImm64().m_value; - switch (value) { - case 0xffff: - case 0xffffff: - case 0xffffffffL: - case 0xffffffffffL: - case 0xffffffffffffL: - case 0xffffffffffffffL: - case 0xffffffffffffffffL: - return false; - default: { - if (value <= 0xff) - return false; - if (~value <= 0xff) - return false; - - JSValue jsValue = JSValue::decode(value); - if (jsValue.isInt32()) - return shouldBlind(Imm32(jsValue.asInt32())); - if (jsValue.isDouble() && !shouldBlindDouble(jsValue.asDouble())) - return false; - - if (!shouldBlindDouble(bitwise_cast(value))) - return false; - } - } - - if (!shouldConsiderBlinding()) - return false; - - return shouldBlindForSpecificArch(value); - } - - struct RotatedImm64 { - RotatedImm64(uint64_t v1, uint8_t v2) - : value(v1) - , rotation(v2) - { - } - TrustedImm64 value; - TrustedImm32 rotation; - }; - - RotatedImm64 rotationBlindConstant(Imm64 imm) - { - uint8_t rotation = random() % (sizeof(int64_t) * 8); - uint64_t value = imm.asTrustedImm64().m_value; - value = (value << rotation) | (value >> (sizeof(int64_t) * 8 - rotation)); - return RotatedImm64(value, rotation); - } - - void loadRotationBlindedConstant(RotatedImm64 constant, RegisterID dest) - { - move(constant.value, dest); - rotateRight64(constant.rotation, dest); - } - - void convertInt32ToDouble(Imm32 imm, FPRegisterID dest) - { - if (shouldBlind(imm)) { - RegisterID scratchRegister = scratchRegisterForBlinding(); - loadXorBlindedConstant(xorBlindConstant(imm), scratchRegister); - convertInt32ToDouble(scratchRegister, dest); - } else - convertInt32ToDouble(imm.asTrustedImm32(), dest); - } - - void move(ImmPtr imm, RegisterID dest) - { - if (shouldBlind(imm)) - loadRotationBlindedConstant(rotationBlindConstant(imm), dest); - else - move(imm.asTrustedImmPtr(), dest); - } - - void move(Imm64 imm, RegisterID dest) - { - if (shouldBlind(imm)) - loadRotationBlindedConstant(rotationBlindConstant(imm), dest); - else - move(imm.asTrustedImm64(), dest); - } - - void and64(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = andBlindedConstant(imm); - and64(key.value1, dest); - and64(key.value2, dest); - } else - and64(imm.asTrustedImm32(), dest); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, ImmPtr right) - { - if (shouldBlind(right)) { - RegisterID scratchRegister = scratchRegisterForBlinding(); - loadRotationBlindedConstant(rotationBlindConstant(right), scratchRegister); - return branchPtr(cond, left, scratchRegister); - } - return branchPtr(cond, left, right.asTrustedImmPtr()); - } - - void storePtr(ImmPtr imm, Address dest) - { - if (shouldBlind(imm)) { - RegisterID scratchRegister = scratchRegisterForBlinding(); - loadRotationBlindedConstant(rotationBlindConstant(imm), scratchRegister); - storePtr(scratchRegister, dest); - } else - storePtr(imm.asTrustedImmPtr(), dest); - } - - void store64(Imm64 imm, Address dest) - { - if (shouldBlind(imm)) { - RegisterID scratchRegister = scratchRegisterForBlinding(); - loadRotationBlindedConstant(rotationBlindConstant(imm), scratchRegister); - store64(scratchRegister, dest); - } else - store64(imm.asTrustedImm64(), dest); - } - -#endif - -#endif // !CPU(X86_64) - -#if ENABLE(JIT_CONSTANT_BLINDING) - bool shouldBlind(Imm32 imm) - { -#if !defined(NDEBUG) - UNUSED_PARAM(imm); - // Debug always blind all constants, if only so we know - // if we've broken blinding during patch development. - return true; -#else - - // First off we'll special case common, "safe" values to avoid hurting - // performance too much - uint32_t value = imm.asTrustedImm32().m_value; - switch (value) { - case 0xffff: - case 0xffffff: - case 0xffffffff: - return false; - default: - if (value <= 0xff) - return false; - if (~value <= 0xff) - return false; - } - - if (!shouldConsiderBlinding()) - return false; - - return shouldBlindForSpecificArch(value); -#endif - } - - struct BlindedImm32 { - BlindedImm32(int32_t v1, int32_t v2) - : value1(v1) - , value2(v2) - { - } - TrustedImm32 value1; - TrustedImm32 value2; - }; - - uint32_t keyForConstant(uint32_t value, uint32_t& mask) - { - uint32_t key = random(); - if (value <= 0xff) - mask = 0xff; - else if (value <= 0xffff) - mask = 0xffff; - else if (value <= 0xffffff) - mask = 0xffffff; - else - mask = 0xffffffff; - return key & mask; - } - - uint32_t keyForConstant(uint32_t value) - { - uint32_t mask = 0; - return keyForConstant(value, mask); - } - - BlindedImm32 xorBlindConstant(Imm32 imm) - { - uint32_t baseValue = imm.asTrustedImm32().m_value; - uint32_t key = keyForConstant(baseValue); - return BlindedImm32(baseValue ^ key, key); - } - - BlindedImm32 additionBlindedConstant(Imm32 imm) - { - // The addition immediate may be used as a pointer offset. Keep aligned based on "imm". - static uint32_t maskTable[4] = { 0xfffffffc, 0xffffffff, 0xfffffffe, 0xffffffff }; - - uint32_t baseValue = imm.asTrustedImm32().m_value; - uint32_t key = keyForConstant(baseValue) & maskTable[baseValue & 3]; - if (key > baseValue) - key = key - baseValue; - return BlindedImm32(baseValue - key, key); - } - - BlindedImm32 andBlindedConstant(Imm32 imm) - { - uint32_t baseValue = imm.asTrustedImm32().m_value; - uint32_t mask = 0; - uint32_t key = keyForConstant(baseValue, mask); - ASSERT((baseValue & mask) == baseValue); - return BlindedImm32(((baseValue & key) | ~key) & mask, ((baseValue & ~key) | key) & mask); - } - - BlindedImm32 orBlindedConstant(Imm32 imm) - { - uint32_t baseValue = imm.asTrustedImm32().m_value; - uint32_t mask = 0; - uint32_t key = keyForConstant(baseValue, mask); - ASSERT((baseValue & mask) == baseValue); - return BlindedImm32((baseValue & key) & mask, (baseValue & ~key) & mask); - } - - void loadXorBlindedConstant(BlindedImm32 constant, RegisterID dest) - { - move(constant.value1, dest); - xor32(constant.value2, dest); - } - - void add32(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = additionBlindedConstant(imm); - add32(key.value1, dest); - add32(key.value2, dest); - } else - add32(imm.asTrustedImm32(), dest); - } - - void addPtr(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = additionBlindedConstant(imm); - addPtr(key.value1, dest); - addPtr(key.value2, dest); - } else - addPtr(imm.asTrustedImm32(), dest); - } - - void and32(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = andBlindedConstant(imm); - and32(key.value1, dest); - and32(key.value2, dest); - } else - and32(imm.asTrustedImm32(), dest); - } - - void andPtr(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = andBlindedConstant(imm); - andPtr(key.value1, dest); - andPtr(key.value2, dest); - } else - andPtr(imm.asTrustedImm32(), dest); - } - - void and32(Imm32 imm, RegisterID src, RegisterID dest) - { - if (shouldBlind(imm)) { - if (src == dest) - return and32(imm.asTrustedImm32(), dest); - loadXorBlindedConstant(xorBlindConstant(imm), dest); - and32(src, dest); - } else - and32(imm.asTrustedImm32(), src, dest); - } - - void move(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) - loadXorBlindedConstant(xorBlindConstant(imm), dest); - else - move(imm.asTrustedImm32(), dest); - } - - void or32(Imm32 imm, RegisterID src, RegisterID dest) - { - if (shouldBlind(imm)) { - if (src == dest) - return or32(imm, dest); - loadXorBlindedConstant(xorBlindConstant(imm), dest); - or32(src, dest); - } else - or32(imm.asTrustedImm32(), src, dest); - } - - void or32(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = orBlindedConstant(imm); - or32(key.value1, dest); - or32(key.value2, dest); - } else - or32(imm.asTrustedImm32(), dest); - } - - void poke(Imm32 value, int index = 0) - { - store32(value, addressForPoke(index)); - } - - void poke(ImmPtr value, int index = 0) - { - storePtr(value, addressForPoke(index)); - } - -#if CPU(X86_64) - void poke(Imm64 value, int index = 0) - { - store64(value, addressForPoke(index)); - } -#endif - - void store32(Imm32 imm, Address dest) - { - if (shouldBlind(imm)) { -#if CPU(X86) || CPU(X86_64) - BlindedImm32 blind = xorBlindConstant(imm); - store32(blind.value1, dest); - xor32(blind.value2, dest); -#else - if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { - loadXorBlindedConstant(xorBlindConstant(imm), scratchRegister); - store32(scratchRegister, dest); - } else { - // If we don't have a scratch register available for use, we'll just - // place a random number of nops. - uint32_t nopCount = random() & 3; - while (nopCount--) - nop(); - store32(imm.asTrustedImm32(), dest); - } -#endif - } else - store32(imm.asTrustedImm32(), dest); - } - - void sub32(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = additionBlindedConstant(imm); - sub32(key.value1, dest); - sub32(key.value2, dest); - } else - sub32(imm.asTrustedImm32(), dest); - } - - void subPtr(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = additionBlindedConstant(imm); - subPtr(key.value1, dest); - subPtr(key.value2, dest); - } else - subPtr(imm.asTrustedImm32(), dest); - } - - void xor32(Imm32 imm, RegisterID src, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 blind = xorBlindConstant(imm); - xor32(blind.value1, src, dest); - xor32(blind.value2, dest); - } else - xor32(imm.asTrustedImm32(), src, dest); - } - - void xor32(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 blind = xorBlindConstant(imm); - xor32(blind.value1, dest); - xor32(blind.value2, dest); - } else - xor32(imm.asTrustedImm32(), dest); - } - - Jump branch32(RelationalCondition cond, RegisterID left, Imm32 right) - { - if (shouldBlind(right)) { - if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { - loadXorBlindedConstant(xorBlindConstant(right), scratchRegister); - return branch32(cond, left, scratchRegister); - } - // If we don't have a scratch register available for use, we'll just - // place a random number of nops. - uint32_t nopCount = random() & 3; - while (nopCount--) - nop(); - return branch32(cond, left, right.asTrustedImm32()); - } - - return branch32(cond, left, right.asTrustedImm32()); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, Imm32 imm, RegisterID dest) - { - if (src == dest) { - if (!scratchRegisterForBlinding()) { - // Release mode ASSERT, if this fails we will perform incorrect codegen. - CRASH(); - } - } - if (shouldBlind(imm)) { - if (src == dest) { - if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { - move(src, scratchRegister); - src = scratchRegister; - } - } - loadXorBlindedConstant(xorBlindConstant(imm), dest); - return branchAdd32(cond, src, dest); - } - return branchAdd32(cond, src, imm.asTrustedImm32(), dest); - } - - Jump branchMul32(ResultCondition cond, Imm32 imm, RegisterID src, RegisterID dest) - { - if (src == dest) { - if (!scratchRegisterForBlinding()) { - // Release mode ASSERT, if this fails we will perform incorrect codegen. - CRASH(); - } - } - if (shouldBlind(imm)) { - if (src == dest) { - if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { - move(src, scratchRegister); - src = scratchRegister; - } - } - loadXorBlindedConstant(xorBlindConstant(imm), dest); - return branchMul32(cond, src, dest); - } - return branchMul32(cond, imm.asTrustedImm32(), src, dest); - } - - // branchSub32 takes a scratch register as 32 bit platforms make use of this, - // with src == dst, and on x86-32 we don't have a platform scratch register. - Jump branchSub32(ResultCondition cond, RegisterID src, Imm32 imm, RegisterID dest, RegisterID scratch) - { - if (shouldBlind(imm)) { - ASSERT(scratch != dest); - ASSERT(scratch != src); - loadXorBlindedConstant(xorBlindConstant(imm), scratch); - return branchSub32(cond, src, scratch, dest); - } - return branchSub32(cond, src, imm.asTrustedImm32(), dest); - } - - // Immediate shifts only have 5 controllable bits - // so we'll consider them safe for now. - TrustedImm32 trustedImm32ForShift(Imm32 imm) - { - return TrustedImm32(imm.asTrustedImm32().m_value & 31); - } - - void lshift32(Imm32 imm, RegisterID dest) - { - lshift32(trustedImm32ForShift(imm), dest); - } - - void lshift32(RegisterID src, Imm32 amount, RegisterID dest) - { - lshift32(src, trustedImm32ForShift(amount), dest); - } - - void rshift32(Imm32 imm, RegisterID dest) - { - rshift32(trustedImm32ForShift(imm), dest); - } - - void rshift32(RegisterID src, Imm32 amount, RegisterID dest) - { - rshift32(src, trustedImm32ForShift(amount), dest); - } - - void urshift32(Imm32 imm, RegisterID dest) - { - urshift32(trustedImm32ForShift(imm), dest); - } - - void urshift32(RegisterID src, Imm32 amount, RegisterID dest) - { - urshift32(src, trustedImm32ForShift(amount), dest); - } -#endif -}; - -} // namespace JSC - -#else // ENABLE(ASSEMBLER) - -// If there is no assembler for this platform, at least allow code to make references to -// some of the things it would otherwise define, albeit without giving that code any way -// of doing anything useful. -class MacroAssembler { -private: - MacroAssembler() { } - -public: - - enum RegisterID { NoRegister }; - enum FPRegisterID { NoFPRegister }; -}; - -#endif // ENABLE(ASSEMBLER) - -#endif // MacroAssembler_h diff --git a/masm/assembler/MacroAssemblerARM.cpp b/masm/assembler/MacroAssemblerARM.cpp deleted file mode 100644 index 98dc3e9879..0000000000 --- a/masm/assembler/MacroAssemblerARM.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2009 University of Szeged - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" - -#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) - -#include "MacroAssemblerARM.h" - -#if OS(LINUX) -#include -#include -#include -#include -#include -#include -#endif - -namespace JSC { - -static bool isVFPPresent() -{ -#if OS(LINUX) - int fd = open("/proc/self/auxv", O_RDONLY); - if (fd > 0) { - Elf32_auxv_t aux; - while (read(fd, &aux, sizeof(Elf32_auxv_t))) { - if (aux.a_type == AT_HWCAP) { - close(fd); - return aux.a_un.a_val & HWCAP_VFP; - } - } - close(fd); - } -#endif - -#if (COMPILER(RVCT) && defined(__TARGET_FPU_VFP)) || (COMPILER(GCC) && defined(__VFP_FP__)) - return true; -#else - return false; -#endif -} - -const bool MacroAssemblerARM::s_isVFPPresent = isVFPPresent(); - -#if CPU(ARMV5_OR_LOWER) -/* On ARMv5 and below, natural alignment is required. */ -void MacroAssemblerARM::load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) -{ - ARMWord op2; - - ASSERT(address.scale >= 0 && address.scale <= 3); - op2 = m_assembler.lsl(address.index, static_cast(address.scale)); - - if (address.offset >= 0 && address.offset + 0x2 <= 0xff) { - m_assembler.add(ARMRegisters::S0, address.base, op2); - m_assembler.halfDtrUp(ARMAssembler::LoadUint16, dest, ARMRegisters::S0, ARMAssembler::getOp2Half(address.offset)); - m_assembler.halfDtrUp(ARMAssembler::LoadUint16, ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Half(address.offset + 0x2)); - } else if (address.offset < 0 && address.offset >= -0xff) { - m_assembler.add(ARMRegisters::S0, address.base, op2); - m_assembler.halfDtrDown(ARMAssembler::LoadUint16, dest, ARMRegisters::S0, ARMAssembler::getOp2Half(-address.offset)); - m_assembler.halfDtrDown(ARMAssembler::LoadUint16, ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Half(-address.offset - 0x2)); - } else { - m_assembler.moveImm(address.offset, ARMRegisters::S0); - m_assembler.add(ARMRegisters::S0, ARMRegisters::S0, op2); - m_assembler.halfDtrUpRegister(ARMAssembler::LoadUint16, dest, address.base, ARMRegisters::S0); - m_assembler.add(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::Op2Immediate | 0x2); - m_assembler.halfDtrUpRegister(ARMAssembler::LoadUint16, ARMRegisters::S0, address.base, ARMRegisters::S0); - } - m_assembler.orr(dest, dest, m_assembler.lsl(ARMRegisters::S0, 16)); -} -#endif - -} - -#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) diff --git a/masm/assembler/MacroAssemblerARM.h b/masm/assembler/MacroAssemblerARM.h deleted file mode 100644 index 527126b438..0000000000 --- a/masm/assembler/MacroAssemblerARM.h +++ /dev/null @@ -1,1383 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. - * Copyright (C) 2009, 2010 University of Szeged - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssemblerARM_h -#define MacroAssemblerARM_h - -#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) - -#include "ARMAssembler.h" -#include "AbstractMacroAssembler.h" - -namespace JSC { - -class MacroAssemblerARM : public AbstractMacroAssembler { - static const int DoubleConditionMask = 0x0f; - static const int DoubleConditionBitSpecial = 0x10; - COMPILE_ASSERT(!(DoubleConditionBitSpecial & DoubleConditionMask), DoubleConditionBitSpecial_should_not_interfere_with_ARMAssembler_Condition_codes); -public: - typedef ARMRegisters::FPRegisterID FPRegisterID; - - enum RelationalCondition { - Equal = ARMAssembler::EQ, - NotEqual = ARMAssembler::NE, - Above = ARMAssembler::HI, - AboveOrEqual = ARMAssembler::CS, - Below = ARMAssembler::CC, - BelowOrEqual = ARMAssembler::LS, - GreaterThan = ARMAssembler::GT, - GreaterThanOrEqual = ARMAssembler::GE, - LessThan = ARMAssembler::LT, - LessThanOrEqual = ARMAssembler::LE - }; - - enum ResultCondition { - Overflow = ARMAssembler::VS, - Signed = ARMAssembler::MI, - Zero = ARMAssembler::EQ, - NonZero = ARMAssembler::NE - }; - - enum DoubleCondition { - // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. - DoubleEqual = ARMAssembler::EQ, - DoubleNotEqual = ARMAssembler::NE | DoubleConditionBitSpecial, - DoubleGreaterThan = ARMAssembler::GT, - DoubleGreaterThanOrEqual = ARMAssembler::GE, - DoubleLessThan = ARMAssembler::CC, - DoubleLessThanOrEqual = ARMAssembler::LS, - // If either operand is NaN, these conditions always evaluate to true. - DoubleEqualOrUnordered = ARMAssembler::EQ | DoubleConditionBitSpecial, - DoubleNotEqualOrUnordered = ARMAssembler::NE, - DoubleGreaterThanOrUnordered = ARMAssembler::HI, - DoubleGreaterThanOrEqualOrUnordered = ARMAssembler::CS, - DoubleLessThanOrUnordered = ARMAssembler::LT, - DoubleLessThanOrEqualOrUnordered = ARMAssembler::LE, - }; - - static const RegisterID stackPointerRegister = ARMRegisters::sp; - static const RegisterID linkRegister = ARMRegisters::lr; - - static const Scale ScalePtr = TimesFour; - - void add32(RegisterID src, RegisterID dest) - { - m_assembler.adds(dest, dest, src); - } - - void add32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.adds(dest, op1, op2); - } - - void add32(TrustedImm32 imm, Address address) - { - load32(address, ARMRegisters::S1); - add32(imm, ARMRegisters::S1); - store32(ARMRegisters::S1, address); - } - - void add32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.adds(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void add32(AbsoluteAddress src, RegisterID dest) - { - move(TrustedImmPtr(src.m_ptr), ARMRegisters::S1); - m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); - add32(ARMRegisters::S1, dest); - } - - void add32(Address src, RegisterID dest) - { - load32(src, ARMRegisters::S1); - add32(ARMRegisters::S1, dest); - } - - void add32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.adds(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void and32(RegisterID src, RegisterID dest) - { - m_assembler.bitAnds(dest, dest, src); - } - - void and32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.bitAnds(dest, op1, op2); - } - - void and32(TrustedImm32 imm, RegisterID dest) - { - ARMWord w = m_assembler.getImm(imm.m_value, ARMRegisters::S0, true); - if (w & ARMAssembler::Op2InvertedImmediate) - m_assembler.bics(dest, dest, w & ~ARMAssembler::Op2InvertedImmediate); - else - m_assembler.bitAnds(dest, dest, w); - } - - void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - ARMWord w = m_assembler.getImm(imm.m_value, ARMRegisters::S0, true); - if (w & ARMAssembler::Op2InvertedImmediate) - m_assembler.bics(dest, src, w & ~ARMAssembler::Op2InvertedImmediate); - else - m_assembler.bitAnds(dest, src, w); - } - - void lshift32(RegisterID shiftAmount, RegisterID dest) - { - lshift32(dest, shiftAmount, dest); - } - - void lshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) - { - ARMWord w = ARMAssembler::getOp2Byte(0x1f); - m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); - - m_assembler.movs(dest, m_assembler.lslRegister(src, ARMRegisters::S0)); - } - - void lshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.movs(dest, m_assembler.lsl(dest, imm.m_value & 0x1f)); - } - - void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.movs(dest, m_assembler.lsl(src, imm.m_value & 0x1f)); - } - - void mul32(RegisterID op1, RegisterID op2, RegisterID dest) - { - if (op2 == dest) { - if (op1 == dest) { - move(op2, ARMRegisters::S0); - op2 = ARMRegisters::S0; - } else { - // Swap the operands. - RegisterID tmp = op1; - op1 = op2; - op2 = tmp; - } - } - m_assembler.muls(dest, op1, op2); - } - - void mul32(RegisterID src, RegisterID dest) - { - mul32(src, dest, dest); - } - - void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(imm, ARMRegisters::S0); - m_assembler.muls(dest, src, ARMRegisters::S0); - } - - void neg32(RegisterID srcDest) - { - m_assembler.rsbs(srcDest, srcDest, ARMAssembler::getOp2Byte(0)); - } - - void or32(RegisterID src, RegisterID dest) - { - m_assembler.orrs(dest, dest, src); - } - - void or32(RegisterID src, AbsoluteAddress dest) - { - move(TrustedImmPtr(dest.m_ptr), ARMRegisters::S0); - load32(Address(ARMRegisters::S0), ARMRegisters::S1); - or32(src, ARMRegisters::S1); - store32(ARMRegisters::S1, ARMRegisters::S0); - } - - void or32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.orrs(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - m_assembler.orrs(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void or32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.orrs(dest, op1, op2); - } - - void rshift32(RegisterID shiftAmount, RegisterID dest) - { - rshift32(dest, shiftAmount, dest); - } - - void rshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) - { - ARMWord w = ARMAssembler::getOp2Byte(0x1f); - m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); - - m_assembler.movs(dest, m_assembler.asrRegister(src, ARMRegisters::S0)); - } - - void rshift32(TrustedImm32 imm, RegisterID dest) - { - rshift32(dest, imm, dest); - } - - void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.movs(dest, m_assembler.asr(src, imm.m_value & 0x1f)); - } - - void urshift32(RegisterID shiftAmount, RegisterID dest) - { - urshift32(dest, shiftAmount, dest); - } - - void urshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) - { - ARMWord w = ARMAssembler::getOp2Byte(0x1f); - m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); - - m_assembler.movs(dest, m_assembler.lsrRegister(src, ARMRegisters::S0)); - } - - void urshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.movs(dest, m_assembler.lsr(dest, imm.m_value & 0x1f)); - } - - void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.movs(dest, m_assembler.lsr(src, imm.m_value & 0x1f)); - } - - void sub32(RegisterID src, RegisterID dest) - { - m_assembler.subs(dest, dest, src); - } - - void sub32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.subs(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void sub32(TrustedImm32 imm, Address address) - { - load32(address, ARMRegisters::S1); - sub32(imm, ARMRegisters::S1); - store32(ARMRegisters::S1, address); - } - - void sub32(Address src, RegisterID dest) - { - load32(src, ARMRegisters::S1); - sub32(ARMRegisters::S1, dest); - } - - void sub32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.subs(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void xor32(RegisterID src, RegisterID dest) - { - m_assembler.eors(dest, dest, src); - } - - void xor32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.eors(dest, op1, op2); - } - - void xor32(TrustedImm32 imm, RegisterID dest) - { - if (imm.m_value == -1) - m_assembler.mvns(dest, dest); - else - m_assembler.eors(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (imm.m_value == -1) - m_assembler.mvns(dest, src); - else - m_assembler.eors(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void countLeadingZeros32(RegisterID src, RegisterID dest) - { -#if WTF_ARM_ARCH_AT_LEAST(5) - m_assembler.clz(dest, src); -#else - UNUSED_PARAM(src); - UNUSED_PARAM(dest); - ASSERT_NOT_REACHED(); -#endif - } - - void load8(ImplicitAddress address, RegisterID dest) - { - m_assembler.dataTransfer32(ARMAssembler::LoadUint8, dest, address.base, address.offset); - } - - void load8(BaseIndex address, RegisterID dest) - { - m_assembler.baseIndexTransfer32(ARMAssembler::LoadUint8, dest, address.base, address.index, static_cast(address.scale), address.offset); - } - - void load8Signed(BaseIndex address, RegisterID dest) - { - m_assembler.baseIndexTransfer16(ARMAssembler::LoadInt8, dest, address.base, address.index, static_cast(address.scale), address.offset); - } - - void load16(ImplicitAddress address, RegisterID dest) - { - m_assembler.dataTransfer16(ARMAssembler::LoadUint16, dest, address.base, address.offset); - } - - void load16(BaseIndex address, RegisterID dest) - { - m_assembler.baseIndexTransfer16(ARMAssembler::LoadUint16, dest, address.base, address.index, static_cast(address.scale), address.offset); - } - - void load16Signed(BaseIndex address, RegisterID dest) - { - m_assembler.baseIndexTransfer16(ARMAssembler::LoadInt16, dest, address.base, address.index, static_cast(address.scale), address.offset); - } - - void load32(ImplicitAddress address, RegisterID dest) - { - m_assembler.dataTransfer32(ARMAssembler::LoadUint32, dest, address.base, address.offset); - } - - void load32(BaseIndex address, RegisterID dest) - { - m_assembler.baseIndexTransfer32(ARMAssembler::LoadUint32, dest, address.base, address.index, static_cast(address.scale), address.offset); - } - -#if CPU(ARMV5_OR_LOWER) - void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest); -#else - void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) - { - load32(address, dest); - } -#endif - - void load16Unaligned(BaseIndex address, RegisterID dest) - { - load16(address, dest); - } - - ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) - { - ConvertibleLoadLabel result(this); - ASSERT(address.offset >= 0 && address.offset <= 255); - m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, address.base, address.offset); - return result; - } - - DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) - { - DataLabel32 dataLabel(this); - m_assembler.ldrUniqueImmediate(ARMRegisters::S0, 0); - m_assembler.dtrUpRegister(ARMAssembler::LoadUint32, dest, address.base, ARMRegisters::S0); - return dataLabel; - } - - static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) - { - return value >= -4095 && value <= 4095; - } - - DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - DataLabelCompact dataLabel(this); - ASSERT(isCompactPtrAlignedAddressOffset(address.offset)); - if (address.offset >= 0) - m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, address.base, address.offset); - else - m_assembler.dtrDown(ARMAssembler::LoadUint32, dest, address.base, address.offset); - return dataLabel; - } - - DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) - { - DataLabel32 dataLabel(this); - m_assembler.ldrUniqueImmediate(ARMRegisters::S0, 0); - m_assembler.dtrUpRegister(ARMAssembler::StoreUint32, src, address.base, ARMRegisters::S0); - return dataLabel; - } - - void store8(RegisterID src, BaseIndex address) - { - m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint8, src, address.base, address.index, static_cast(address.scale), address.offset); - } - - void store8(TrustedImm32 imm, void* address) - { - move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); - m_assembler.moveImm(imm.m_value, ARMRegisters::S1); - m_assembler.dtrUp(ARMAssembler::StoreUint8, ARMRegisters::S1, ARMRegisters::S0, 0); - } - - void store16(RegisterID src, BaseIndex address) - { - m_assembler.baseIndexTransfer16(ARMAssembler::StoreUint16, src, address.base, address.index, static_cast(address.scale), address.offset); - } - - void store32(RegisterID src, ImplicitAddress address) - { - m_assembler.dataTransfer32(ARMAssembler::StoreUint32, src, address.base, address.offset); - } - - void store32(RegisterID src, BaseIndex address) - { - m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint32, src, address.base, address.index, static_cast(address.scale), address.offset); - } - - void store32(TrustedImm32 imm, ImplicitAddress address) - { - move(imm, ARMRegisters::S1); - store32(ARMRegisters::S1, address); - } - - void store32(TrustedImm32 imm, BaseIndex address) - { - move(imm, ARMRegisters::S1); - m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint32, ARMRegisters::S1, address.base, address.index, static_cast(address.scale), address.offset); - } - - void store32(RegisterID src, void* address) - { - m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); - m_assembler.dtrUp(ARMAssembler::StoreUint32, src, ARMRegisters::S0, 0); - } - - void store32(TrustedImm32 imm, void* address) - { - m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); - m_assembler.moveImm(imm.m_value, ARMRegisters::S1); - m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); - } - - void pop(RegisterID dest) - { - m_assembler.pop(dest); - } - - void push(RegisterID src) - { - m_assembler.push(src); - } - - void push(Address address) - { - load32(address, ARMRegisters::S1); - push(ARMRegisters::S1); - } - - void push(TrustedImm32 imm) - { - move(imm, ARMRegisters::S0); - push(ARMRegisters::S0); - } - - void move(TrustedImm32 imm, RegisterID dest) - { - m_assembler.moveImm(imm.m_value, dest); - } - - void move(RegisterID src, RegisterID dest) - { - if (src != dest) - m_assembler.mov(dest, src); - } - - void move(TrustedImmPtr imm, RegisterID dest) - { - move(TrustedImm32(imm), dest); - } - - void swap(RegisterID reg1, RegisterID reg2) - { - m_assembler.mov(ARMRegisters::S0, reg1); - m_assembler.mov(reg1, reg2); - m_assembler.mov(reg2, ARMRegisters::S0); - } - - void signExtend32ToPtr(RegisterID src, RegisterID dest) - { - if (src != dest) - move(src, dest); - } - - void zeroExtend32ToPtr(RegisterID src, RegisterID dest) - { - if (src != dest) - move(src, dest); - } - - Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) - { - load8(left, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - ASSERT(!(right.m_value & 0xFFFFFF00)); - load8(left, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right, int useConstantPool = 0) - { - m_assembler.cmp(left, right); - return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); - } - - Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right, int useConstantPool = 0) - { - internalCompare32(left, right); - return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); - } - - Jump branch32(RelationalCondition cond, RegisterID left, Address right) - { - load32(right, ARMRegisters::S1); - return branch32(cond, left, ARMRegisters::S1); - } - - Jump branch32(RelationalCondition cond, Address left, RegisterID right) - { - load32(left, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) - { - load32(left, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - load32(left, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - load32WithUnalignedHalfWords(left, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - load8(address, ARMRegisters::S1); - return branchTest32(cond, ARMRegisters::S1, mask); - } - - Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); - load8(Address(ARMRegisters::S1), ARMRegisters::S1); - return branchTest32(cond, ARMRegisters::S1, mask); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) - { - ASSERT((cond == Zero) || (cond == NonZero)); - m_assembler.tst(reg, mask); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - ASSERT((cond == Zero) || (cond == NonZero)); - ARMWord w = m_assembler.getImm(mask.m_value, ARMRegisters::S0, true); - if (w & ARMAssembler::Op2InvertedImmediate) - m_assembler.bics(ARMRegisters::S0, reg, w & ~ARMAssembler::Op2InvertedImmediate); - else - m_assembler.tst(reg, w); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - load32(address, ARMRegisters::S1); - return branchTest32(cond, ARMRegisters::S1, mask); - } - - Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - load32(address, ARMRegisters::S1); - return branchTest32(cond, ARMRegisters::S1, mask); - } - - Jump jump() - { - return Jump(m_assembler.jmp()); - } - - void jump(RegisterID target) - { - m_assembler.bx(target); - } - - void jump(Address address) - { - load32(address, ARMRegisters::pc); - } - - void jump(AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), ARMRegisters::S0); - load32(Address(ARMRegisters::S0, 0), ARMRegisters::pc); - } - - void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) - { - m_assembler.vmov(dest1, dest2, src); - } - - void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) - { - UNUSED_PARAM(scratch); - m_assembler.vmov(dest, src1, src2); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - add32(src, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchAdd32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - add32(op1, op2, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - add32(imm, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - add32(src, imm, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - add32(imm, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - void mull32(RegisterID op1, RegisterID op2, RegisterID dest) - { - if (op2 == dest) { - if (op1 == dest) { - move(op2, ARMRegisters::S0); - op2 = ARMRegisters::S0; - } else { - // Swap the operands. - RegisterID tmp = op1; - op1 = op2; - op2 = tmp; - } - } - m_assembler.mull(ARMRegisters::S1, dest, op1, op2); - m_assembler.cmp(ARMRegisters::S1, m_assembler.asr(dest, 31)); - } - - Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - if (cond == Overflow) { - mull32(src1, src2, dest); - cond = NonZero; - } - else - mul32(src1, src2, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchMul32(cond, src, dest, dest); - } - - Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - if (cond == Overflow) { - move(imm, ARMRegisters::S0); - mull32(ARMRegisters::S0, src, dest); - cond = NonZero; - } - else - mul32(imm, src, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - sub32(src, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - sub32(imm, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - sub32(src, imm, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchSub32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - m_assembler.subs(dest, op1, op2); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchNeg32(ResultCondition cond, RegisterID srcDest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - neg32(srcDest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); - or32(src, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) - { - internalCompare32(reg, imm); - Jump jump(m_assembler.loadBranchTarget(ARMRegisters::S1, ARMCondition(cond), true)); - m_assembler.bx(ARMRegisters::S1, ARMCondition(cond)); - return PatchableJump(jump); - } - - void breakpoint() - { - m_assembler.bkpt(0); - } - - Call nearCall() - { - m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true); - return Call(m_assembler.blx(ARMRegisters::S1), Call::LinkableNear); - } - - Call call(RegisterID target) - { - return Call(m_assembler.blx(target), Call::None); - } - - void call(Address address) - { - call32(address.base, address.offset); - } - - void ret() - { - m_assembler.bx(linkRegister); - } - - void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) - { - m_assembler.cmp(left, right); - m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); - m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); - } - - void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - m_assembler.cmp(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); - m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); - m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); - } - - void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) - { - load8(left, ARMRegisters::S1); - compare32(cond, ARMRegisters::S1, right, dest); - } - - void test32(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) - { - if (mask.m_value == -1) - m_assembler.cmp(0, reg); - else - m_assembler.tst(reg, m_assembler.getImm(mask.m_value, ARMRegisters::S0)); - m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); - m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); - } - - void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - load32(address, ARMRegisters::S1); - test32(cond, ARMRegisters::S1, mask, dest); - } - - void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - load8(address, ARMRegisters::S1); - test32(cond, ARMRegisters::S1, mask, dest); - } - - void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - m_assembler.add(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void add32(TrustedImm32 imm, AbsoluteAddress address) - { - m_assembler.ldrUniqueImmediate(ARMRegisters::S1, reinterpret_cast(address.m_ptr)); - m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); - add32(imm, ARMRegisters::S1); - m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address.m_ptr)); - m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); - } - - void add64(TrustedImm32 imm, AbsoluteAddress address) - { - ARMWord tmp; - - move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); - m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S0, ARMRegisters::S1, 0); - - if ((tmp = ARMAssembler::getOp2(imm.m_value)) != ARMAssembler::InvalidImmediate) - m_assembler.adds(ARMRegisters::S0, ARMRegisters::S0, tmp); - else if ((tmp = ARMAssembler::getOp2(-imm.m_value)) != ARMAssembler::InvalidImmediate) - m_assembler.subs(ARMRegisters::S0, ARMRegisters::S0, tmp); - else { - m_assembler.adds(ARMRegisters::S0, ARMRegisters::S0, m_assembler.getImm(imm.m_value, ARMRegisters::S1)); - move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); - } - m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S0, ARMRegisters::S1, 0); - - m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S0, ARMRegisters::S1, sizeof(ARMWord)); - if (imm.m_value >= 0) - m_assembler.adc(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); - else - m_assembler.sbc(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); - m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S0, ARMRegisters::S1, sizeof(ARMWord)); - } - - void sub32(TrustedImm32 imm, AbsoluteAddress address) - { - m_assembler.ldrUniqueImmediate(ARMRegisters::S1, reinterpret_cast(address.m_ptr)); - m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); - sub32(imm, ARMRegisters::S1); - m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address.m_ptr)); - m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); - } - - void load32(const void* address, RegisterID dest) - { - m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); - m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, ARMRegisters::S0, 0); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - load32(left.m_ptr, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) - { - load32(left.m_ptr, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - void relativeTableJump(RegisterID index, int scale) - { - ASSERT(scale >= 0 && scale <= 31); - m_assembler.add(ARMRegisters::pc, ARMRegisters::pc, m_assembler.lsl(index, scale)); - - // NOP the default prefetching - m_assembler.mov(ARMRegisters::r0, ARMRegisters::r0); - } - - Call call() - { - ensureSpace(2 * sizeof(ARMWord), sizeof(ARMWord)); - m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true); - return Call(m_assembler.blx(ARMRegisters::S1), Call::Linkable); - } - - Call tailRecursiveCall() - { - return Call::fromTailJump(jump()); - } - - Call makeTailRecursiveCall(Jump oldJump) - { - return Call::fromTailJump(oldJump); - } - - DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) - { - DataLabelPtr dataLabel(this); - m_assembler.ldrUniqueImmediate(dest, reinterpret_cast(initialValue.m_value)); - return dataLabel; - } - - Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S1); - Jump jump = branch32(cond, left, ARMRegisters::S1, true); - return jump; - } - - Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - load32(left, ARMRegisters::S1); - dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S0); - Jump jump = branch32(cond, ARMRegisters::S0, ARMRegisters::S1, true); - return jump; - } - - DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) - { - DataLabelPtr dataLabel = moveWithPatch(initialValue, ARMRegisters::S1); - store32(ARMRegisters::S1, address); - return dataLabel; - } - - DataLabelPtr storePtrWithPatch(ImplicitAddress address) - { - return storePtrWithPatch(TrustedImmPtr(0), address); - } - - // Floating point operators - static bool supportsFloatingPoint() - { - return s_isVFPPresent; - } - - static bool supportsFloatingPointTruncate() - { - return false; - } - - static bool supportsFloatingPointSqrt() - { - return s_isVFPPresent; - } - static bool supportsFloatingPointAbs() { return false; } - - void loadFloat(BaseIndex address, FPRegisterID dest) - { - m_assembler.baseIndexTransferFloat(ARMAssembler::LoadFloat, dest, address.base, address.index, static_cast(address.scale), address.offset); - } - - void loadDouble(ImplicitAddress address, FPRegisterID dest) - { - m_assembler.dataTransferFloat(ARMAssembler::LoadDouble, dest, address.base, address.offset); - } - - void loadDouble(BaseIndex address, FPRegisterID dest) - { - m_assembler.baseIndexTransferFloat(ARMAssembler::LoadDouble, dest, address.base, address.index, static_cast(address.scale), address.offset); - } - - void loadDouble(const void* address, FPRegisterID dest) - { - move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); - m_assembler.doubleDtrUp(ARMAssembler::LoadDouble, dest, ARMRegisters::S0, 0); - } - - void storeFloat(FPRegisterID src, BaseIndex address) - { - m_assembler.baseIndexTransferFloat(ARMAssembler::StoreFloat, src, address.base, address.index, static_cast(address.scale), address.offset); - } - - void storeDouble(FPRegisterID src, ImplicitAddress address) - { - m_assembler.dataTransferFloat(ARMAssembler::StoreDouble, src, address.base, address.offset); - } - - void storeDouble(FPRegisterID src, BaseIndex address) - { - m_assembler.baseIndexTransferFloat(ARMAssembler::StoreDouble, src, address.base, address.index, static_cast(address.scale), address.offset); - } - - void storeDouble(FPRegisterID src, const void* address) - { - move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); - m_assembler.dataTransferFloat(ARMAssembler::StoreDouble, src, ARMRegisters::S0, 0); - } - - void moveDouble(FPRegisterID src, FPRegisterID dest) - { - if (src != dest) - m_assembler.vmov_f64(dest, src); - } - - void addDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vadd_f64(dest, dest, src); - } - - void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vadd_f64(dest, op1, op2); - } - - void addDouble(Address src, FPRegisterID dest) - { - loadDouble(src, ARMRegisters::SD0); - addDouble(ARMRegisters::SD0, dest); - } - - void addDouble(AbsoluteAddress address, FPRegisterID dest) - { - loadDouble(address.m_ptr, ARMRegisters::SD0); - addDouble(ARMRegisters::SD0, dest); - } - - void divDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vdiv_f64(dest, dest, src); - } - - void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vdiv_f64(dest, op1, op2); - } - - void divDouble(Address src, FPRegisterID dest) - { - ASSERT_NOT_REACHED(); // Untested - loadDouble(src, ARMRegisters::SD0); - divDouble(ARMRegisters::SD0, dest); - } - - void subDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vsub_f64(dest, dest, src); - } - - void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vsub_f64(dest, op1, op2); - } - - void subDouble(Address src, FPRegisterID dest) - { - loadDouble(src, ARMRegisters::SD0); - subDouble(ARMRegisters::SD0, dest); - } - - void mulDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vmul_f64(dest, dest, src); - } - - void mulDouble(Address src, FPRegisterID dest) - { - loadDouble(src, ARMRegisters::SD0); - mulDouble(ARMRegisters::SD0, dest); - } - - void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vmul_f64(dest, op1, op2); - } - - void sqrtDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vsqrt_f64(dest, src); - } - - void absDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vabs_f64(dest, src); - } - - void negateDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vneg_f64(dest, src); - } - - void convertInt32ToDouble(RegisterID src, FPRegisterID dest) - { - m_assembler.vmov_vfp32(dest << 1, src); - m_assembler.vcvt_f64_s32(dest, dest << 1); - } - - void convertInt32ToDouble(Address src, FPRegisterID dest) - { - load32(src, ARMRegisters::S1); - convertInt32ToDouble(ARMRegisters::S1, dest); - } - - void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) - { - move(TrustedImmPtr(src.m_ptr), ARMRegisters::S1); - load32(Address(ARMRegisters::S1), ARMRegisters::S1); - convertInt32ToDouble(ARMRegisters::S1, dest); - } - - void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) - { - m_assembler.vcvt_f64_f32(dst, src); - } - - void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) - { - m_assembler.vcvt_f32_f64(dst, src); - } - - Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) - { - m_assembler.vcmp_f64(left, right); - m_assembler.vmrs_apsr(); - if (cond & DoubleConditionBitSpecial) - m_assembler.cmp(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::VS); - return Jump(m_assembler.jmp(static_cast(cond & ~DoubleConditionMask))); - } - - // Truncates 'src' to an integer, and places the resulting 'dest'. - // If the result is not representable as a 32 bit value, branch. - // May also branch for some values that are representable in 32 bits - // (specifically, in this case, INT_MIN). - enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; - Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) - { - truncateDoubleToInt32(src, dest); - - m_assembler.add(ARMRegisters::S0, dest, ARMAssembler::getOp2Byte(1)); - m_assembler.bic(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(1)); - - ARMWord w = ARMAssembler::getOp2(0x80000000); - ASSERT(w != ARMAssembler::InvalidImmediate); - m_assembler.cmp(ARMRegisters::S0, w); - return Jump(m_assembler.jmp(branchType == BranchIfTruncateFailed ? ARMAssembler::EQ : ARMAssembler::NE)); - } - - Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) - { - truncateDoubleToUint32(src, dest); - - m_assembler.add(ARMRegisters::S0, dest, ARMAssembler::getOp2Byte(1)); - m_assembler.bic(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(1)); - - m_assembler.cmp(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); - return Jump(m_assembler.jmp(branchType == BranchIfTruncateFailed ? ARMAssembler::EQ : ARMAssembler::NE)); - } - - // Result is undefined if the value is outside of the integer range. - void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) - { - m_assembler.vcvt_s32_f64(ARMRegisters::SD0 << 1, src); - m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); - } - - void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) - { - m_assembler.vcvt_u32_f64(ARMRegisters::SD0 << 1, src); - m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); - } - - // Convert 'src' to an integer, and places the resulting 'dest'. - // If the result is not representable as a 32 bit value, branch. - // May also branch for some values that are representable in 32 bits - // (specifically, in this case, 0). - void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) - { - m_assembler.vcvt_s32_f64(ARMRegisters::SD0 << 1, src); - m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); - - // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. - m_assembler.vcvt_f64_s32(ARMRegisters::SD0, ARMRegisters::SD0 << 1); - failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, ARMRegisters::SD0)); - - // If the result is zero, it might have been -0.0, and 0.0 equals to -0.0 - failureCases.append(branchTest32(Zero, dest)); - } - - Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) - { - m_assembler.mov(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); - convertInt32ToDouble(ARMRegisters::S0, scratch); - return branchDouble(DoubleNotEqual, reg, scratch); - } - - Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) - { - m_assembler.mov(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); - convertInt32ToDouble(ARMRegisters::S0, scratch); - return branchDouble(DoubleEqualOrUnordered, reg, scratch); - } - - // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. - static RelationalCondition invert(RelationalCondition cond) - { - ASSERT((static_cast(cond & 0x0fffffff)) == 0 && static_cast(cond) < static_cast(ARMAssembler::AL)); - return static_cast(cond ^ 0x10000000); - } - - void nop() - { - m_assembler.nop(); - } - - static FunctionPtr readCallTarget(CodeLocationCall call) - { - return FunctionPtr(reinterpret_cast(ARMAssembler::readCallTarget(call.dataLocation()))); - } - - static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) - { - ARMAssembler::replaceWithJump(instructionStart.dataLocation(), destination.dataLocation()); - } - - static ptrdiff_t maxJumpReplacementSize() - { - ARMAssembler::maxJumpReplacementSize(); - return 0; - } - - static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } - - static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) - { - UNREACHABLE_FOR_PLATFORM(); - return CodeLocationLabel(); - } - - static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) - { - return label.labelAtOffset(0); - } - - static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID reg, void* initialValue) - { - ARMAssembler::revertJump(instructionStart.dataLocation(), reg, reinterpret_cast(initialValue) & 0xffff); - } - - static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) - { - UNREACHABLE_FOR_PLATFORM(); - } - -protected: - ARMAssembler::Condition ARMCondition(RelationalCondition cond) - { - return static_cast(cond); - } - - ARMAssembler::Condition ARMCondition(ResultCondition cond) - { - return static_cast(cond); - } - - void ensureSpace(int insnSpace, int constSpace) - { - m_assembler.ensureSpace(insnSpace, constSpace); - } - - int sizeOfConstantPool() - { - return m_assembler.sizeOfConstantPool(); - } - - void call32(RegisterID base, int32_t offset) - { - load32(Address(base, offset), ARMRegisters::S1); - m_assembler.blx(ARMRegisters::S1); - } - -private: - friend class LinkBuffer; - friend class RepatchBuffer; - - void internalCompare32(RegisterID left, TrustedImm32 right) - { - ARMWord tmp = (static_cast(right.m_value) == 0x80000000) ? ARMAssembler::InvalidImmediate : m_assembler.getOp2(-right.m_value); - if (tmp != ARMAssembler::InvalidImmediate) - m_assembler.cmn(left, tmp); - else - m_assembler.cmp(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); - } - - static void linkCall(void* code, Call call, FunctionPtr function) - { - ARMAssembler::linkCall(code, call.m_label, function.value()); - } - - static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) - { - ARMAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } - - static void repatchCall(CodeLocationCall call, FunctionPtr destination) - { - ARMAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } - - static const bool s_isVFPPresent; -}; - -} - -#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) - -#endif // MacroAssemblerARM_h diff --git a/masm/assembler/MacroAssemblerARMv7.h b/masm/assembler/MacroAssemblerARMv7.h deleted file mode 100644 index 8d7a3a69aa..0000000000 --- a/masm/assembler/MacroAssemblerARMv7.h +++ /dev/null @@ -1,1903 +0,0 @@ -/* - * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. - * Copyright (C) 2010 University of Szeged - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssemblerARMv7_h -#define MacroAssemblerARMv7_h - -#if ENABLE(ASSEMBLER) - -#include "ARMv7Assembler.h" -#include "AbstractMacroAssembler.h" - -namespace JSC { - -class MacroAssemblerARMv7 : public AbstractMacroAssembler { - // FIXME: switch dataTempRegister & addressTempRegister, or possibly use r7? - // - dTR is likely used more than aTR, and we'll get better instruction - // encoding if it's in the low 8 registers. - static const RegisterID dataTempRegister = ARMRegisters::ip; - static const RegisterID addressTempRegister = ARMRegisters::r3; - - static const ARMRegisters::FPDoubleRegisterID fpTempRegister = ARMRegisters::d7; - inline ARMRegisters::FPSingleRegisterID fpTempRegisterAsSingle() { return ARMRegisters::asSingle(fpTempRegister); } - -public: - MacroAssemblerARMv7() - : m_makeJumpPatchable(false) - { - } - - typedef ARMv7Assembler::LinkRecord LinkRecord; - typedef ARMv7Assembler::JumpType JumpType; - typedef ARMv7Assembler::JumpLinkType JumpLinkType; - - static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) - { - return value >= -255 && value <= 255; - } - - Vector& jumpsToLink() { return m_assembler.jumpsToLink(); } - void* unlinkedCode() { return m_assembler.unlinkedCode(); } - bool canCompact(JumpType jumpType) { return m_assembler.canCompact(jumpType); } - JumpLinkType computeJumpType(JumpType jumpType, const uint8_t* from, const uint8_t* to) { return m_assembler.computeJumpType(jumpType, from, to); } - JumpLinkType computeJumpType(LinkRecord& record, const uint8_t* from, const uint8_t* to) { return m_assembler.computeJumpType(record, from, to); } - void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset) {return m_assembler.recordLinkOffsets(regionStart, regionEnd, offset); } - int jumpSizeDelta(JumpType jumpType, JumpLinkType jumpLinkType) { return m_assembler.jumpSizeDelta(jumpType, jumpLinkType); } - void link(LinkRecord& record, uint8_t* from, uint8_t* to) { return m_assembler.link(record, from, to); } - - struct ArmAddress { - enum AddressType { - HasOffset, - HasIndex, - } type; - RegisterID base; - union { - int32_t offset; - struct { - RegisterID index; - Scale scale; - }; - } u; - - explicit ArmAddress(RegisterID base, int32_t offset = 0) - : type(HasOffset) - , base(base) - { - u.offset = offset; - } - - explicit ArmAddress(RegisterID base, RegisterID index, Scale scale = TimesOne) - : type(HasIndex) - , base(base) - { - u.index = index; - u.scale = scale; - } - }; - -public: - typedef ARMRegisters::FPDoubleRegisterID FPRegisterID; - - static const Scale ScalePtr = TimesFour; - - enum RelationalCondition { - Equal = ARMv7Assembler::ConditionEQ, - NotEqual = ARMv7Assembler::ConditionNE, - Above = ARMv7Assembler::ConditionHI, - AboveOrEqual = ARMv7Assembler::ConditionHS, - Below = ARMv7Assembler::ConditionLO, - BelowOrEqual = ARMv7Assembler::ConditionLS, - GreaterThan = ARMv7Assembler::ConditionGT, - GreaterThanOrEqual = ARMv7Assembler::ConditionGE, - LessThan = ARMv7Assembler::ConditionLT, - LessThanOrEqual = ARMv7Assembler::ConditionLE - }; - - enum ResultCondition { - Overflow = ARMv7Assembler::ConditionVS, - Signed = ARMv7Assembler::ConditionMI, - Zero = ARMv7Assembler::ConditionEQ, - NonZero = ARMv7Assembler::ConditionNE - }; - - enum DoubleCondition { - // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. - DoubleEqual = ARMv7Assembler::ConditionEQ, - DoubleNotEqual = ARMv7Assembler::ConditionVC, // Not the right flag! check for this & handle differently. - DoubleGreaterThan = ARMv7Assembler::ConditionGT, - DoubleGreaterThanOrEqual = ARMv7Assembler::ConditionGE, - DoubleLessThan = ARMv7Assembler::ConditionLO, - DoubleLessThanOrEqual = ARMv7Assembler::ConditionLS, - // If either operand is NaN, these conditions always evaluate to true. - DoubleEqualOrUnordered = ARMv7Assembler::ConditionVS, // Not the right flag! check for this & handle differently. - DoubleNotEqualOrUnordered = ARMv7Assembler::ConditionNE, - DoubleGreaterThanOrUnordered = ARMv7Assembler::ConditionHI, - DoubleGreaterThanOrEqualOrUnordered = ARMv7Assembler::ConditionHS, - DoubleLessThanOrUnordered = ARMv7Assembler::ConditionLT, - DoubleLessThanOrEqualOrUnordered = ARMv7Assembler::ConditionLE, - }; - - static const RegisterID stackPointerRegister = ARMRegisters::sp; - static const RegisterID linkRegister = ARMRegisters::lr; - - // Integer arithmetic operations: - // - // Operations are typically two operand - operation(source, srcDst) - // For many operations the source may be an TrustedImm32, the srcDst operand - // may often be a memory location (explictly described using an Address - // object). - - void add32(RegisterID src, RegisterID dest) - { - m_assembler.add(dest, dest, src); - } - - void add32(TrustedImm32 imm, RegisterID dest) - { - add32(imm, dest, dest); - } - - void add32(AbsoluteAddress src, RegisterID dest) - { - load32(src.m_ptr, dataTempRegister); - add32(dataTempRegister, dest); - } - - void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.add(dest, src, armImm); - else { - move(imm, dataTempRegister); - m_assembler.add(dest, src, dataTempRegister); - } - } - - void add32(TrustedImm32 imm, Address address) - { - load32(address, dataTempRegister); - - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.add(dataTempRegister, dataTempRegister, armImm); - else { - // Hrrrm, since dataTempRegister holds the data loaded, - // use addressTempRegister to hold the immediate. - move(imm, addressTempRegister); - m_assembler.add(dataTempRegister, dataTempRegister, addressTempRegister); - } - - store32(dataTempRegister, address); - } - - void add32(Address src, RegisterID dest) - { - load32(src, dataTempRegister); - add32(dataTempRegister, dest); - } - - void add32(TrustedImm32 imm, AbsoluteAddress address) - { - load32(address.m_ptr, dataTempRegister); - - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.add(dataTempRegister, dataTempRegister, armImm); - else { - // Hrrrm, since dataTempRegister holds the data loaded, - // use addressTempRegister to hold the immediate. - move(imm, addressTempRegister); - m_assembler.add(dataTempRegister, dataTempRegister, addressTempRegister); - } - - store32(dataTempRegister, address.m_ptr); - } - - void add64(TrustedImm32 imm, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), addressTempRegister); - - m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(0)); - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.add_S(dataTempRegister, dataTempRegister, armImm); - else { - move(imm, addressTempRegister); - m_assembler.add_S(dataTempRegister, dataTempRegister, addressTempRegister); - move(TrustedImmPtr(address.m_ptr), addressTempRegister); - } - m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(0)); - - m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(4)); - m_assembler.adc(dataTempRegister, dataTempRegister, ARMThumbImmediate::makeEncodedImm(imm.m_value >> 31)); - m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(4)); - } - - void and32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.ARM_and(dest, op1, op2); - } - - void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.ARM_and(dest, src, armImm); - else { - move(imm, dataTempRegister); - m_assembler.ARM_and(dest, src, dataTempRegister); - } - } - - void and32(RegisterID src, RegisterID dest) - { - and32(dest, src, dest); - } - - void and32(TrustedImm32 imm, RegisterID dest) - { - and32(imm, dest, dest); - } - - void countLeadingZeros32(RegisterID src, RegisterID dest) - { - m_assembler.clz(dest, src); - } - - void lshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) - { - // Clamp the shift to the range 0..31 - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); - ASSERT(armImm.isValid()); - m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); - - m_assembler.lsl(dest, src, dataTempRegister); - } - - void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.lsl(dest, src, imm.m_value & 0x1f); - } - - void lshift32(RegisterID shiftAmount, RegisterID dest) - { - lshift32(dest, shiftAmount, dest); - } - - void lshift32(TrustedImm32 imm, RegisterID dest) - { - lshift32(dest, imm, dest); - } - - void mul32(RegisterID src, RegisterID dest) - { - m_assembler.smull(dest, dataTempRegister, dest, src); - } - - void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(imm, dataTempRegister); - m_assembler.smull(dest, dataTempRegister, src, dataTempRegister); - } - - void neg32(RegisterID srcDest) - { - m_assembler.neg(srcDest, srcDest); - } - - void or32(RegisterID src, RegisterID dest) - { - m_assembler.orr(dest, dest, src); - } - - void or32(RegisterID src, AbsoluteAddress dest) - { - move(TrustedImmPtr(dest.m_ptr), addressTempRegister); - load32(addressTempRegister, dataTempRegister); - or32(src, dataTempRegister); - store32(dataTempRegister, addressTempRegister); - } - - void or32(TrustedImm32 imm, RegisterID dest) - { - or32(imm, dest, dest); - } - - void or32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.orr(dest, op1, op2); - } - - void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.orr(dest, src, armImm); - else { - move(imm, dataTempRegister); - m_assembler.orr(dest, src, dataTempRegister); - } - } - - void rshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) - { - // Clamp the shift to the range 0..31 - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); - ASSERT(armImm.isValid()); - m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); - - m_assembler.asr(dest, src, dataTempRegister); - } - - void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.asr(dest, src, imm.m_value & 0x1f); - } - - void rshift32(RegisterID shiftAmount, RegisterID dest) - { - rshift32(dest, shiftAmount, dest); - } - - void rshift32(TrustedImm32 imm, RegisterID dest) - { - rshift32(dest, imm, dest); - } - - void urshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) - { - // Clamp the shift to the range 0..31 - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); - ASSERT(armImm.isValid()); - m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); - - m_assembler.lsr(dest, src, dataTempRegister); - } - - void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.lsr(dest, src, imm.m_value & 0x1f); - } - - void urshift32(RegisterID shiftAmount, RegisterID dest) - { - urshift32(dest, shiftAmount, dest); - } - - void urshift32(TrustedImm32 imm, RegisterID dest) - { - urshift32(dest, imm, dest); - } - - void sub32(RegisterID src, RegisterID dest) - { - m_assembler.sub(dest, dest, src); - } - - void sub32(TrustedImm32 imm, RegisterID dest) - { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.sub(dest, dest, armImm); - else { - move(imm, dataTempRegister); - m_assembler.sub(dest, dest, dataTempRegister); - } - } - - void sub32(TrustedImm32 imm, Address address) - { - load32(address, dataTempRegister); - - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.sub(dataTempRegister, dataTempRegister, armImm); - else { - // Hrrrm, since dataTempRegister holds the data loaded, - // use addressTempRegister to hold the immediate. - move(imm, addressTempRegister); - m_assembler.sub(dataTempRegister, dataTempRegister, addressTempRegister); - } - - store32(dataTempRegister, address); - } - - void sub32(Address src, RegisterID dest) - { - load32(src, dataTempRegister); - sub32(dataTempRegister, dest); - } - - void sub32(TrustedImm32 imm, AbsoluteAddress address) - { - load32(address.m_ptr, dataTempRegister); - - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.sub(dataTempRegister, dataTempRegister, armImm); - else { - // Hrrrm, since dataTempRegister holds the data loaded, - // use addressTempRegister to hold the immediate. - move(imm, addressTempRegister); - m_assembler.sub(dataTempRegister, dataTempRegister, addressTempRegister); - } - - store32(dataTempRegister, address.m_ptr); - } - - void xor32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.eor(dest, op1, op2); - } - - void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (imm.m_value == -1) { - m_assembler.mvn(dest, src); - return; - } - - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.eor(dest, src, armImm); - else { - move(imm, dataTempRegister); - m_assembler.eor(dest, src, dataTempRegister); - } - } - - void xor32(RegisterID src, RegisterID dest) - { - xor32(dest, src, dest); - } - - void xor32(TrustedImm32 imm, RegisterID dest) - { - if (imm.m_value == -1) - m_assembler.mvn(dest, dest); - else - xor32(imm, dest, dest); - } - - - // Memory access operations: - // - // Loads are of the form load(address, destination) and stores of the form - // store(source, address). The source for a store may be an TrustedImm32. Address - // operand objects to loads and store will be implicitly constructed if a - // register is passed. - -private: - void load32(ArmAddress address, RegisterID dest) - { - if (address.type == ArmAddress::HasIndex) - m_assembler.ldr(dest, address.base, address.u.index, address.u.scale); - else if (address.u.offset >= 0) { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); - ASSERT(armImm.isValid()); - m_assembler.ldr(dest, address.base, armImm); - } else { - ASSERT(address.u.offset >= -255); - m_assembler.ldr(dest, address.base, address.u.offset, true, false); - } - } - - void load16(ArmAddress address, RegisterID dest) - { - if (address.type == ArmAddress::HasIndex) - m_assembler.ldrh(dest, address.base, address.u.index, address.u.scale); - else if (address.u.offset >= 0) { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); - ASSERT(armImm.isValid()); - m_assembler.ldrh(dest, address.base, armImm); - } else { - ASSERT(address.u.offset >= -255); - m_assembler.ldrh(dest, address.base, address.u.offset, true, false); - } - } - - void load16Signed(ArmAddress address, RegisterID dest) - { - ASSERT(address.type == ArmAddress::HasIndex); - m_assembler.ldrsh(dest, address.base, address.u.index, address.u.scale); - } - - void load8(ArmAddress address, RegisterID dest) - { - if (address.type == ArmAddress::HasIndex) - m_assembler.ldrb(dest, address.base, address.u.index, address.u.scale); - else if (address.u.offset >= 0) { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); - ASSERT(armImm.isValid()); - m_assembler.ldrb(dest, address.base, armImm); - } else { - ASSERT(address.u.offset >= -255); - m_assembler.ldrb(dest, address.base, address.u.offset, true, false); - } - } - - void load8Signed(ArmAddress address, RegisterID dest) - { - ASSERT(address.type == ArmAddress::HasIndex); - m_assembler.ldrsb(dest, address.base, address.u.index, address.u.scale); - } - -protected: - void store32(RegisterID src, ArmAddress address) - { - if (address.type == ArmAddress::HasIndex) - m_assembler.str(src, address.base, address.u.index, address.u.scale); - else if (address.u.offset >= 0) { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); - ASSERT(armImm.isValid()); - m_assembler.str(src, address.base, armImm); - } else { - ASSERT(address.u.offset >= -255); - m_assembler.str(src, address.base, address.u.offset, true, false); - } - } - -private: - void store8(RegisterID src, ArmAddress address) - { - if (address.type == ArmAddress::HasIndex) - m_assembler.strb(src, address.base, address.u.index, address.u.scale); - else if (address.u.offset >= 0) { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); - ASSERT(armImm.isValid()); - m_assembler.strb(src, address.base, armImm); - } else { - ASSERT(address.u.offset >= -255); - m_assembler.strb(src, address.base, address.u.offset, true, false); - } - } - - void store16(RegisterID src, ArmAddress address) - { - if (address.type == ArmAddress::HasIndex) - m_assembler.strh(src, address.base, address.u.index, address.u.scale); - else if (address.u.offset >= 0) { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); - ASSERT(armImm.isValid()); - m_assembler.strh(src, address.base, armImm); - } else { - ASSERT(address.u.offset >= -255); - m_assembler.strh(src, address.base, address.u.offset, true, false); - } - } - -public: - void load32(ImplicitAddress address, RegisterID dest) - { - load32(setupArmAddress(address), dest); - } - - void load32(BaseIndex address, RegisterID dest) - { - load32(setupArmAddress(address), dest); - } - - void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) - { - load32(setupArmAddress(address), dest); - } - - void load16Unaligned(BaseIndex address, RegisterID dest) - { - load16(setupArmAddress(address), dest); - } - - void load32(const void* address, RegisterID dest) - { - move(TrustedImmPtr(address), addressTempRegister); - m_assembler.ldr(dest, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); - } - - ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) - { - ConvertibleLoadLabel result(this); - ASSERT(address.offset >= 0 && address.offset <= 255); - m_assembler.ldrWide8BitImmediate(dest, address.base, address.offset); - return result; - } - - void load8(ImplicitAddress address, RegisterID dest) - { - load8(setupArmAddress(address), dest); - } - - void load8Signed(ImplicitAddress, RegisterID) - { - UNREACHABLE_FOR_PLATFORM(); - } - - void load8(BaseIndex address, RegisterID dest) - { - load8(setupArmAddress(address), dest); - } - - void load8Signed(BaseIndex address, RegisterID dest) - { - load8Signed(setupArmAddress(address), dest); - } - - DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) - { - DataLabel32 label = moveWithPatch(TrustedImm32(address.offset), dataTempRegister); - load32(ArmAddress(address.base, dataTempRegister), dest); - return label; - } - - DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - padBeforePatch(); - - RegisterID base = address.base; - - DataLabelCompact label(this); - ASSERT(isCompactPtrAlignedAddressOffset(address.offset)); - - m_assembler.ldr(dest, base, address.offset, true, false); - return label; - } - - void load16(BaseIndex address, RegisterID dest) - { - m_assembler.ldrh(dest, makeBaseIndexBase(address), address.index, address.scale); - } - - void load16Signed(BaseIndex address, RegisterID dest) - { - load16Signed(setupArmAddress(address), dest); - } - - void load16(ImplicitAddress address, RegisterID dest) - { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.offset); - if (armImm.isValid()) - m_assembler.ldrh(dest, address.base, armImm); - else { - move(TrustedImm32(address.offset), dataTempRegister); - m_assembler.ldrh(dest, address.base, dataTempRegister); - } - } - - void load16Signed(ImplicitAddress, RegisterID) - { - UNREACHABLE_FOR_PLATFORM(); - } - - DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) - { - DataLabel32 label = moveWithPatch(TrustedImm32(address.offset), dataTempRegister); - store32(src, ArmAddress(address.base, dataTempRegister)); - return label; - } - - void store32(RegisterID src, ImplicitAddress address) - { - store32(src, setupArmAddress(address)); - } - - void store32(RegisterID src, BaseIndex address) - { - store32(src, setupArmAddress(address)); - } - - void store32(TrustedImm32 imm, ImplicitAddress address) - { - move(imm, dataTempRegister); - store32(dataTempRegister, setupArmAddress(address)); - } - - void store32(TrustedImm32 imm, BaseIndex address) - { - move(imm, dataTempRegister); - store32(dataTempRegister, setupArmAddress(address)); - } - - void store32(RegisterID src, const void* address) - { - move(TrustedImmPtr(address), addressTempRegister); - m_assembler.str(src, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); - } - - void store32(TrustedImm32 imm, const void* address) - { - move(imm, dataTempRegister); - store32(dataTempRegister, address); - } - - void store8(RegisterID src, BaseIndex address) - { - store8(src, setupArmAddress(address)); - } - - void store8(RegisterID src, void* address) - { - move(TrustedImmPtr(address), addressTempRegister); - store8(src, ArmAddress(addressTempRegister, 0)); - } - - void store8(TrustedImm32 imm, void* address) - { - move(imm, dataTempRegister); - store8(dataTempRegister, address); - } - - void store16(RegisterID src, BaseIndex address) - { - store16(src, setupArmAddress(address)); - } - - // Possibly clobbers src, but not on this architecture. - void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) - { - m_assembler.vmov(dest1, dest2, src); - } - - void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) - { - UNUSED_PARAM(scratch); - m_assembler.vmov(dest, src1, src2); - } - -#if ENABLE(JIT_CONSTANT_BLINDING) - static bool shouldBlindForSpecificArch(uint32_t value) - { - ARMThumbImmediate immediate = ARMThumbImmediate::makeEncodedImm(value); - - // Couldn't be encoded as an immediate, so assume it's untrusted. - if (!immediate.isValid()) - return true; - - // If we can encode the immediate, we have less than 16 attacker - // controlled bits. - if (immediate.isEncodedImm()) - return false; - - // Don't let any more than 12 bits of an instruction word - // be controlled by an attacker. - return !immediate.isUInt12(); - } -#endif - - // Floating-point operations: - - static bool supportsFloatingPoint() { return true; } - static bool supportsFloatingPointTruncate() { return true; } - static bool supportsFloatingPointSqrt() { return true; } - static bool supportsFloatingPointAbs() { return true; } - - void loadDouble(ImplicitAddress address, FPRegisterID dest) - { - RegisterID base = address.base; - int32_t offset = address.offset; - - // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. - if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { - add32(TrustedImm32(offset), base, addressTempRegister); - base = addressTempRegister; - offset = 0; - } - - m_assembler.vldr(dest, base, offset); - } - - void loadFloat(ImplicitAddress address, FPRegisterID dest) - { - RegisterID base = address.base; - int32_t offset = address.offset; - - // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. - if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { - add32(TrustedImm32(offset), base, addressTempRegister); - base = addressTempRegister; - offset = 0; - } - - m_assembler.flds(ARMRegisters::asSingle(dest), base, offset); - } - - void loadDouble(BaseIndex address, FPRegisterID dest) - { - move(address.index, addressTempRegister); - lshift32(TrustedImm32(address.scale), addressTempRegister); - add32(address.base, addressTempRegister); - loadDouble(Address(addressTempRegister, address.offset), dest); - } - - void loadFloat(BaseIndex address, FPRegisterID dest) - { - move(address.index, addressTempRegister); - lshift32(TrustedImm32(address.scale), addressTempRegister); - add32(address.base, addressTempRegister); - loadFloat(Address(addressTempRegister, address.offset), dest); - } - - void moveDouble(FPRegisterID src, FPRegisterID dest) - { - if (src != dest) - m_assembler.vmov(dest, src); - } - - void loadDouble(const void* address, FPRegisterID dest) - { - move(TrustedImmPtr(address), addressTempRegister); - m_assembler.vldr(dest, addressTempRegister, 0); - } - - void storeDouble(FPRegisterID src, ImplicitAddress address) - { - RegisterID base = address.base; - int32_t offset = address.offset; - - // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. - if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { - add32(TrustedImm32(offset), base, addressTempRegister); - base = addressTempRegister; - offset = 0; - } - - m_assembler.vstr(src, base, offset); - } - - void storeFloat(FPRegisterID src, ImplicitAddress address) - { - RegisterID base = address.base; - int32_t offset = address.offset; - - // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. - if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { - add32(TrustedImm32(offset), base, addressTempRegister); - base = addressTempRegister; - offset = 0; - } - - m_assembler.fsts(ARMRegisters::asSingle(src), base, offset); - } - - void storeDouble(FPRegisterID src, const void* address) - { - move(TrustedImmPtr(address), addressTempRegister); - storeDouble(src, addressTempRegister); - } - - void storeDouble(FPRegisterID src, BaseIndex address) - { - move(address.index, addressTempRegister); - lshift32(TrustedImm32(address.scale), addressTempRegister); - add32(address.base, addressTempRegister); - storeDouble(src, Address(addressTempRegister, address.offset)); - } - - void storeFloat(FPRegisterID src, BaseIndex address) - { - move(address.index, addressTempRegister); - lshift32(TrustedImm32(address.scale), addressTempRegister); - add32(address.base, addressTempRegister); - storeFloat(src, Address(addressTempRegister, address.offset)); - } - - void addDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vadd(dest, dest, src); - } - - void addDouble(Address src, FPRegisterID dest) - { - loadDouble(src, fpTempRegister); - addDouble(fpTempRegister, dest); - } - - void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vadd(dest, op1, op2); - } - - void addDouble(AbsoluteAddress address, FPRegisterID dest) - { - loadDouble(address.m_ptr, fpTempRegister); - m_assembler.vadd(dest, dest, fpTempRegister); - } - - void divDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vdiv(dest, dest, src); - } - - void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vdiv(dest, op1, op2); - } - - void subDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vsub(dest, dest, src); - } - - void subDouble(Address src, FPRegisterID dest) - { - loadDouble(src, fpTempRegister); - subDouble(fpTempRegister, dest); - } - - void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vsub(dest, op1, op2); - } - - void mulDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vmul(dest, dest, src); - } - - void mulDouble(Address src, FPRegisterID dest) - { - loadDouble(src, fpTempRegister); - mulDouble(fpTempRegister, dest); - } - - void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vmul(dest, op1, op2); - } - - void sqrtDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vsqrt(dest, src); - } - - void absDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vabs(dest, src); - } - - void negateDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vneg(dest, src); - } - - void convertInt32ToDouble(RegisterID src, FPRegisterID dest) - { - m_assembler.vmov(fpTempRegister, src, src); - m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); - } - - void convertInt32ToDouble(Address address, FPRegisterID dest) - { - // Fixme: load directly into the fpr! - load32(address, dataTempRegister); - m_assembler.vmov(fpTempRegister, dataTempRegister, dataTempRegister); - m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); - } - - void convertInt32ToDouble(AbsoluteAddress address, FPRegisterID dest) - { - // Fixme: load directly into the fpr! - load32(address.m_ptr, dataTempRegister); - m_assembler.vmov(fpTempRegister, dataTempRegister, dataTempRegister); - m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); - } - - void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) - { - m_assembler.vcvtds(dst, ARMRegisters::asSingle(src)); - } - - void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) - { - m_assembler.vcvtsd(ARMRegisters::asSingle(dst), src); - } - - Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) - { - m_assembler.vcmp(left, right); - m_assembler.vmrs(); - - if (cond == DoubleNotEqual) { - // ConditionNE jumps if NotEqual *or* unordered - force the unordered cases not to jump. - Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); - Jump result = makeBranch(ARMv7Assembler::ConditionNE); - unordered.link(this); - return result; - } - if (cond == DoubleEqualOrUnordered) { - Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); - Jump notEqual = makeBranch(ARMv7Assembler::ConditionNE); - unordered.link(this); - // We get here if either unordered or equal. - Jump result = jump(); - notEqual.link(this); - return result; - } - return makeBranch(cond); - } - - enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; - Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) - { - // Convert into dest. - m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); - m_assembler.vmov(dest, fpTempRegisterAsSingle()); - - // Calculate 2x dest. If the value potentially underflowed, it will have - // clamped to 0x80000000, so 2x dest is zero in this case. In the case of - // overflow the result will be equal to -2. - Jump underflow = branchAdd32(Zero, dest, dest, dataTempRegister); - Jump noOverflow = branch32(NotEqual, dataTempRegister, TrustedImm32(-2)); - - // For BranchIfTruncateSuccessful, we branch if 'noOverflow' jumps. - underflow.link(this); - if (branchType == BranchIfTruncateSuccessful) - return noOverflow; - - // We'll reach the current point in the code on failure, so plant a - // jump here & link the success case. - Jump failure = jump(); - noOverflow.link(this); - return failure; - } - - Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) - { - m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); - m_assembler.vmov(dest, fpTempRegisterAsSingle()); - - Jump overflow = branch32(Equal, dest, TrustedImm32(0x7fffffff)); - Jump success = branch32(GreaterThanOrEqual, dest, TrustedImm32(0)); - overflow.link(this); - - if (branchType == BranchIfTruncateSuccessful) - return success; - - Jump failure = jump(); - success.link(this); - return failure; - } - - // Result is undefined if the value is outside of the integer range. - void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) - { - m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); - m_assembler.vmov(dest, fpTempRegisterAsSingle()); - } - - void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) - { - m_assembler.vcvt_floatingPointToUnsigned(fpTempRegisterAsSingle(), src); - m_assembler.vmov(dest, fpTempRegisterAsSingle()); - } - - // Convert 'src' to an integer, and places the resulting 'dest'. - // If the result is not representable as a 32 bit value, branch. - // May also branch for some values that are representable in 32 bits - // (specifically, in this case, 0). - void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID) - { - m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); - m_assembler.vmov(dest, fpTempRegisterAsSingle()); - - // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. - m_assembler.vcvt_signedToFloatingPoint(fpTempRegister, fpTempRegisterAsSingle()); - failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, fpTempRegister)); - - // If the result is zero, it might have been -0.0, and the double comparison won't catch this! - failureCases.append(branchTest32(Zero, dest)); - } - - Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID) - { - m_assembler.vcmpz(reg); - m_assembler.vmrs(); - Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); - Jump result = makeBranch(ARMv7Assembler::ConditionNE); - unordered.link(this); - return result; - } - - Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID) - { - m_assembler.vcmpz(reg); - m_assembler.vmrs(); - Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); - Jump notEqual = makeBranch(ARMv7Assembler::ConditionNE); - unordered.link(this); - // We get here if either unordered or equal. - Jump result = jump(); - notEqual.link(this); - return result; - } - - // Stack manipulation operations: - // - // The ABI is assumed to provide a stack abstraction to memory, - // containing machine word sized units of data. Push and pop - // operations add and remove a single register sized unit of data - // to or from the stack. Peek and poke operations read or write - // values on the stack, without moving the current stack position. - - void pop(RegisterID dest) - { - // store postindexed with writeback - m_assembler.ldr(dest, ARMRegisters::sp, sizeof(void*), false, true); - } - - void push(RegisterID src) - { - // store preindexed with writeback - m_assembler.str(src, ARMRegisters::sp, -sizeof(void*), true, true); - } - - void push(Address address) - { - load32(address, dataTempRegister); - push(dataTempRegister); - } - - void push(TrustedImm32 imm) - { - move(imm, dataTempRegister); - push(dataTempRegister); - } - - // Register move operations: - // - // Move values in registers. - - void move(TrustedImm32 imm, RegisterID dest) - { - uint32_t value = imm.m_value; - - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(value); - - if (armImm.isValid()) - m_assembler.mov(dest, armImm); - else if ((armImm = ARMThumbImmediate::makeEncodedImm(~value)).isValid()) - m_assembler.mvn(dest, armImm); - else { - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(value)); - if (value & 0xffff0000) - m_assembler.movt(dest, ARMThumbImmediate::makeUInt16(value >> 16)); - } - } - - void move(RegisterID src, RegisterID dest) - { - if (src != dest) - m_assembler.mov(dest, src); - } - - void move(TrustedImmPtr imm, RegisterID dest) - { - move(TrustedImm32(imm), dest); - } - - void swap(RegisterID reg1, RegisterID reg2) - { - move(reg1, dataTempRegister); - move(reg2, reg1); - move(dataTempRegister, reg2); - } - - void signExtend32ToPtr(RegisterID src, RegisterID dest) - { - move(src, dest); - } - - void zeroExtend32ToPtr(RegisterID src, RegisterID dest) - { - move(src, dest); - } - - // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. - static RelationalCondition invert(RelationalCondition cond) - { - return static_cast(cond ^ 1); - } - - void nop() - { - m_assembler.nop(); - } - - static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) - { - ARMv7Assembler::replaceWithJump(instructionStart.dataLocation(), destination.dataLocation()); - } - - static ptrdiff_t maxJumpReplacementSize() - { - return ARMv7Assembler::maxJumpReplacementSize(); - } - - // Forwards / external control flow operations: - // - // This set of jump and conditional branch operations return a Jump - // object which may linked at a later point, allow forwards jump, - // or jumps that will require external linkage (after the code has been - // relocated). - // - // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge - // respecitvely, for unsigned comparisons the names b, a, be, and ae are - // used (representing the names 'below' and 'above'). - // - // Operands to the comparision are provided in the expected order, e.g. - // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when - // treated as a signed 32bit value, is less than or equal to 5. - // - // jz and jnz test whether the first operand is equal to zero, and take - // an optional second operand of a mask under which to perform the test. -private: - - // Should we be using TEQ for equal/not-equal? - void compare32(RegisterID left, TrustedImm32 right) - { - int32_t imm = right.m_value; - if (!imm) - m_assembler.tst(left, left); - else { - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm); - if (armImm.isValid()) - m_assembler.cmp(left, armImm); - else if ((armImm = ARMThumbImmediate::makeEncodedImm(-imm)).isValid()) - m_assembler.cmn(left, armImm); - else { - move(TrustedImm32(imm), dataTempRegister); - m_assembler.cmp(left, dataTempRegister); - } - } - } - - void test32(RegisterID reg, TrustedImm32 mask) - { - int32_t imm = mask.m_value; - - if (imm == -1) - m_assembler.tst(reg, reg); - else { - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm); - if (armImm.isValid()) - m_assembler.tst(reg, armImm); - else { - move(mask, dataTempRegister); - m_assembler.tst(reg, dataTempRegister); - } - } - } - -public: - Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) - { - m_assembler.cmp(left, right); - return Jump(makeBranch(cond)); - } - - Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) - { - compare32(left, right); - return Jump(makeBranch(cond)); - } - - Jump branch32(RelationalCondition cond, RegisterID left, Address right) - { - load32(right, dataTempRegister); - return branch32(cond, left, dataTempRegister); - } - - Jump branch32(RelationalCondition cond, Address left, RegisterID right) - { - load32(left, dataTempRegister); - return branch32(cond, dataTempRegister, right); - } - - Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) - { - // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ - load32(left, addressTempRegister); - return branch32(cond, addressTempRegister, right); - } - - Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ - load32(left, addressTempRegister); - return branch32(cond, addressTempRegister, right); - } - - Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ - load32WithUnalignedHalfWords(left, addressTempRegister); - return branch32(cond, addressTempRegister, right); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - load32(left.m_ptr, dataTempRegister); - return branch32(cond, dataTempRegister, right); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) - { - // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ - load32(left.m_ptr, addressTempRegister); - return branch32(cond, addressTempRegister, right); - } - - Jump branch8(RelationalCondition cond, RegisterID left, TrustedImm32 right) - { - compare32(left, right); - return Jump(makeBranch(cond)); - } - - Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) - { - ASSERT(!(0xffffff00 & right.m_value)); - // use addressTempRegister incase the branch8 we call uses dataTempRegister. :-/ - load8(left, addressTempRegister); - return branch8(cond, addressTempRegister, right); - } - - Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - ASSERT(!(0xffffff00 & right.m_value)); - // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ - load8(left, addressTempRegister); - return branch32(cond, addressTempRegister, right); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) - { - m_assembler.tst(reg, mask); - return Jump(makeBranch(cond)); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - test32(reg, mask); - return Jump(makeBranch(cond)); - } - - Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - // use addressTempRegister incase the branchTest32 we call uses dataTempRegister. :-/ - load32(address, addressTempRegister); - return branchTest32(cond, addressTempRegister, mask); - } - - Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - // use addressTempRegister incase the branchTest32 we call uses dataTempRegister. :-/ - load32(address, addressTempRegister); - return branchTest32(cond, addressTempRegister, mask); - } - - Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - // use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/ - load8(address, addressTempRegister); - return branchTest32(cond, addressTempRegister, mask); - } - - Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - // use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/ - move(TrustedImmPtr(address.m_ptr), addressTempRegister); - load8(Address(addressTempRegister), addressTempRegister); - return branchTest32(cond, addressTempRegister, mask); - } - - void jump(RegisterID target) - { - m_assembler.bx(target); - } - - // Address is a memory location containing the address to jump to - void jump(Address address) - { - load32(address, dataTempRegister); - m_assembler.bx(dataTempRegister); - } - - void jump(AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), dataTempRegister); - load32(Address(dataTempRegister), dataTempRegister); - m_assembler.bx(dataTempRegister); - } - - - // Arithmetic control flow operations: - // - // This set of conditional branch operations branch based - // on the result of an arithmetic operation. The operation - // is performed as normal, storing the result. - // - // * jz operations branch if the result is zero. - // * jo operations branch if the (signed) arithmetic - // operation caused an overflow to occur. - - Jump branchAdd32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.add_S(dest, op1, op2); - return Jump(makeBranch(cond)); - } - - Jump branchAdd32(ResultCondition cond, RegisterID op1, TrustedImm32 imm, RegisterID dest) - { - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.add_S(dest, op1, armImm); - else { - move(imm, dataTempRegister); - m_assembler.add_S(dest, op1, dataTempRegister); - } - return Jump(makeBranch(cond)); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchAdd32(cond, dest, src, dest); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - return branchAdd32(cond, dest, imm, dest); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) - { - // Move the high bits of the address into addressTempRegister, - // and load the value into dataTempRegister. - move(TrustedImmPtr(dest.m_ptr), addressTempRegister); - m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); - - // Do the add. - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.add_S(dataTempRegister, dataTempRegister, armImm); - else { - // If the operand does not fit into an immediate then load it temporarily - // into addressTempRegister; since we're overwriting addressTempRegister - // we'll need to reload it with the high bits of the address afterwards. - move(imm, addressTempRegister); - m_assembler.add_S(dataTempRegister, dataTempRegister, addressTempRegister); - move(TrustedImmPtr(dest.m_ptr), addressTempRegister); - } - - // Store the result. - m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); - - return Jump(makeBranch(cond)); - } - - Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) - { - m_assembler.smull(dest, dataTempRegister, src1, src2); - - if (cond == Overflow) { - m_assembler.asr(addressTempRegister, dest, 31); - return branch32(NotEqual, addressTempRegister, dataTempRegister); - } - - return branchTest32(cond, dest); - } - - Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchMul32(cond, src, dest, dest); - } - - Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(imm, dataTempRegister); - return branchMul32(cond, dataTempRegister, src, dest); - } - - Jump branchNeg32(ResultCondition cond, RegisterID srcDest) - { - ARMThumbImmediate zero = ARMThumbImmediate::makeUInt12(0); - m_assembler.sub_S(srcDest, zero, srcDest); - return Jump(makeBranch(cond)); - } - - Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) - { - m_assembler.orr_S(dest, dest, src); - return Jump(makeBranch(cond)); - } - - Jump branchSub32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.sub_S(dest, op1, op2); - return Jump(makeBranch(cond)); - } - - Jump branchSub32(ResultCondition cond, RegisterID op1, TrustedImm32 imm, RegisterID dest) - { - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.sub_S(dest, op1, armImm); - else { - move(imm, dataTempRegister); - m_assembler.sub_S(dest, op1, dataTempRegister); - } - return Jump(makeBranch(cond)); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchSub32(cond, dest, src, dest); - } - - Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - return branchSub32(cond, dest, imm, dest); - } - - void relativeTableJump(RegisterID index, int scale) - { - ASSERT(scale >= 0 && scale <= 31); - - // dataTempRegister will point after the jump if index register contains zero - move(ARMRegisters::pc, dataTempRegister); - m_assembler.add(dataTempRegister, dataTempRegister, ARMThumbImmediate::makeEncodedImm(9)); - - ShiftTypeAndAmount shift(SRType_LSL, scale); - m_assembler.add(dataTempRegister, dataTempRegister, index, shift); - jump(dataTempRegister); - } - - // Miscellaneous operations: - - void breakpoint(uint8_t imm = 0) - { - m_assembler.bkpt(imm); - } - - ALWAYS_INLINE Call nearCall() - { - moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); - return Call(m_assembler.blx(dataTempRegister), Call::LinkableNear); - } - - ALWAYS_INLINE Call call() - { - moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); - return Call(m_assembler.blx(dataTempRegister), Call::Linkable); - } - - ALWAYS_INLINE Call call(RegisterID target) - { - return Call(m_assembler.blx(target), Call::None); - } - - ALWAYS_INLINE Call call(Address address) - { - load32(address, dataTempRegister); - return Call(m_assembler.blx(dataTempRegister), Call::None); - } - - ALWAYS_INLINE void ret() - { - m_assembler.bx(linkRegister); - } - - void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) - { - m_assembler.cmp(left, right); - m_assembler.it(armV7Condition(cond), false); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); - } - - void compare32(RelationalCondition cond, Address left, RegisterID right, RegisterID dest) - { - load32(left, dataTempRegister); - compare32(cond, dataTempRegister, right, dest); - } - - void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) - { - load8(left, addressTempRegister); - compare32(cond, addressTempRegister, right, dest); - } - - void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - compare32(left, right); - m_assembler.it(armV7Condition(cond), false); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); - } - - // FIXME: - // The mask should be optional... paerhaps the argument order should be - // dest-src, operations always have a dest? ... possibly not true, considering - // asm ops like test, or pseudo ops like pop(). - void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - load32(address, dataTempRegister); - test32(dataTempRegister, mask); - m_assembler.it(armV7Condition(cond), false); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); - } - - void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - load8(address, dataTempRegister); - test32(dataTempRegister, mask); - m_assembler.it(armV7Condition(cond), false); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); - } - - ALWAYS_INLINE DataLabel32 moveWithPatch(TrustedImm32 imm, RegisterID dst) - { - padBeforePatch(); - moveFixedWidthEncoding(imm, dst); - return DataLabel32(this); - } - - ALWAYS_INLINE DataLabelPtr moveWithPatch(TrustedImmPtr imm, RegisterID dst) - { - padBeforePatch(); - moveFixedWidthEncoding(TrustedImm32(imm), dst); - return DataLabelPtr(this); - } - - ALWAYS_INLINE Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - dataLabel = moveWithPatch(initialRightValue, dataTempRegister); - return branch32(cond, left, dataTempRegister); - } - - ALWAYS_INLINE Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - load32(left, addressTempRegister); - dataLabel = moveWithPatch(initialRightValue, dataTempRegister); - return branch32(cond, addressTempRegister, dataTempRegister); - } - - PatchableJump patchableBranchPtr(RelationalCondition cond, Address left, TrustedImmPtr right = TrustedImmPtr(0)) - { - m_makeJumpPatchable = true; - Jump result = branch32(cond, left, TrustedImm32(right)); - m_makeJumpPatchable = false; - return PatchableJump(result); - } - - PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - m_makeJumpPatchable = true; - Jump result = branchTest32(cond, reg, mask); - m_makeJumpPatchable = false; - return PatchableJump(result); - } - - PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) - { - m_makeJumpPatchable = true; - Jump result = branch32(cond, reg, imm); - m_makeJumpPatchable = false; - return PatchableJump(result); - } - - PatchableJump patchableBranchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - m_makeJumpPatchable = true; - Jump result = branchPtrWithPatch(cond, left, dataLabel, initialRightValue); - m_makeJumpPatchable = false; - return PatchableJump(result); - } - - PatchableJump patchableJump() - { - padBeforePatch(); - m_makeJumpPatchable = true; - Jump result = jump(); - m_makeJumpPatchable = false; - return PatchableJump(result); - } - - ALWAYS_INLINE DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) - { - DataLabelPtr label = moveWithPatch(initialValue, dataTempRegister); - store32(dataTempRegister, address); - return label; - } - ALWAYS_INLINE DataLabelPtr storePtrWithPatch(ImplicitAddress address) { return storePtrWithPatch(TrustedImmPtr(0), address); } - - - ALWAYS_INLINE Call tailRecursiveCall() - { - // Like a normal call, but don't link. - moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); - return Call(m_assembler.bx(dataTempRegister), Call::Linkable); - } - - ALWAYS_INLINE Call makeTailRecursiveCall(Jump oldJump) - { - oldJump.link(this); - return tailRecursiveCall(); - } - - - int executableOffsetFor(int location) - { - return m_assembler.executableOffsetFor(location); - } - - static FunctionPtr readCallTarget(CodeLocationCall call) - { - return FunctionPtr(reinterpret_cast(ARMv7Assembler::readCallTarget(call.dataLocation()))); - } - - static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } - - static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) - { - const unsigned twoWordOpSize = 4; - return label.labelAtOffset(-twoWordOpSize * 2); - } - - static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) - { - ARMv7Assembler::revertJumpTo_movT3(instructionStart.dataLocation(), dataTempRegister, ARMThumbImmediate::makeUInt16(reinterpret_cast(initialValue) & 0xffff)); - } - - static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) - { - UNREACHABLE_FOR_PLATFORM(); - return CodeLocationLabel(); - } - - static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel, Address, void*) - { - UNREACHABLE_FOR_PLATFORM(); - } - -protected: - ALWAYS_INLINE Jump jump() - { - m_assembler.label(); // Force nop-padding if we're in the middle of a watchpoint. - moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); - return Jump(m_assembler.bx(dataTempRegister), m_makeJumpPatchable ? ARMv7Assembler::JumpNoConditionFixedSize : ARMv7Assembler::JumpNoCondition); - } - - ALWAYS_INLINE Jump makeBranch(ARMv7Assembler::Condition cond) - { - m_assembler.label(); // Force nop-padding if we're in the middle of a watchpoint. - m_assembler.it(cond, true, true); - moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); - return Jump(m_assembler.bx(dataTempRegister), m_makeJumpPatchable ? ARMv7Assembler::JumpConditionFixedSize : ARMv7Assembler::JumpCondition, cond); - } - ALWAYS_INLINE Jump makeBranch(RelationalCondition cond) { return makeBranch(armV7Condition(cond)); } - ALWAYS_INLINE Jump makeBranch(ResultCondition cond) { return makeBranch(armV7Condition(cond)); } - ALWAYS_INLINE Jump makeBranch(DoubleCondition cond) { return makeBranch(armV7Condition(cond)); } - - ArmAddress setupArmAddress(BaseIndex address) - { - if (address.offset) { - ARMThumbImmediate imm = ARMThumbImmediate::makeUInt12OrEncodedImm(address.offset); - if (imm.isValid()) - m_assembler.add(addressTempRegister, address.base, imm); - else { - move(TrustedImm32(address.offset), addressTempRegister); - m_assembler.add(addressTempRegister, addressTempRegister, address.base); - } - - return ArmAddress(addressTempRegister, address.index, address.scale); - } else - return ArmAddress(address.base, address.index, address.scale); - } - - ArmAddress setupArmAddress(Address address) - { - if ((address.offset >= -0xff) && (address.offset <= 0xfff)) - return ArmAddress(address.base, address.offset); - - move(TrustedImm32(address.offset), addressTempRegister); - return ArmAddress(address.base, addressTempRegister); - } - - ArmAddress setupArmAddress(ImplicitAddress address) - { - if ((address.offset >= -0xff) && (address.offset <= 0xfff)) - return ArmAddress(address.base, address.offset); - - move(TrustedImm32(address.offset), addressTempRegister); - return ArmAddress(address.base, addressTempRegister); - } - - RegisterID makeBaseIndexBase(BaseIndex address) - { - if (!address.offset) - return address.base; - - ARMThumbImmediate imm = ARMThumbImmediate::makeUInt12OrEncodedImm(address.offset); - if (imm.isValid()) - m_assembler.add(addressTempRegister, address.base, imm); - else { - move(TrustedImm32(address.offset), addressTempRegister); - m_assembler.add(addressTempRegister, addressTempRegister, address.base); - } - - return addressTempRegister; - } - - void moveFixedWidthEncoding(TrustedImm32 imm, RegisterID dst) - { - uint32_t value = imm.m_value; - m_assembler.movT3(dst, ARMThumbImmediate::makeUInt16(value & 0xffff)); - m_assembler.movt(dst, ARMThumbImmediate::makeUInt16(value >> 16)); - } - - ARMv7Assembler::Condition armV7Condition(RelationalCondition cond) - { - return static_cast(cond); - } - - ARMv7Assembler::Condition armV7Condition(ResultCondition cond) - { - return static_cast(cond); - } - - ARMv7Assembler::Condition armV7Condition(DoubleCondition cond) - { - return static_cast(cond); - } - -private: - friend class LinkBuffer; - friend class RepatchBuffer; - - static void linkCall(void* code, Call call, FunctionPtr function) - { - ARMv7Assembler::linkCall(code, call.m_label, function.value()); - } - - static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) - { - ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } - - static void repatchCall(CodeLocationCall call, FunctionPtr destination) - { - ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } - - bool m_makeJumpPatchable; -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // MacroAssemblerARMv7_h diff --git a/masm/assembler/MacroAssemblerCodeRef.h b/masm/assembler/MacroAssemblerCodeRef.h deleted file mode 100644 index c2af24060a..0000000000 --- a/masm/assembler/MacroAssemblerCodeRef.h +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright (C) 2009, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssemblerCodeRef_h -#define MacroAssemblerCodeRef_h - -#include "Disassembler.h" -#include "ExecutableAllocator.h" -#include "LLIntData.h" -#include -#include -#include -#include - -// ASSERT_VALID_CODE_POINTER checks that ptr is a non-null pointer, and that it is a valid -// instruction address on the platform (for example, check any alignment requirements). -#if CPU(ARM_THUMB2) -// ARM/thumb instructions must be 16-bit aligned, but all code pointers to be loaded -// into the processor are decorated with the bottom bit set, indicating that this is -// thumb code (as oposed to 32-bit traditional ARM). The first test checks for both -// decorated and undectorated null, and the second test ensures that the pointer is -// decorated. -#define ASSERT_VALID_CODE_POINTER(ptr) \ - ASSERT(reinterpret_cast(ptr) & ~1); \ - ASSERT(reinterpret_cast(ptr) & 1) -#define ASSERT_VALID_CODE_OFFSET(offset) \ - ASSERT(!(offset & 1)) // Must be multiple of 2. -#else -#define ASSERT_VALID_CODE_POINTER(ptr) \ - ASSERT(ptr) -#define ASSERT_VALID_CODE_OFFSET(offset) // Anything goes! -#endif - -#if CPU(X86) && OS(WINDOWS) -#define CALLING_CONVENTION_IS_STDCALL 1 -#ifndef CDECL -#if COMPILER(MSVC) -#define CDECL __cdecl -#else -#define CDECL __attribute__ ((__cdecl)) -#endif // COMPILER(MSVC) -#endif // CDECL -#else -#define CALLING_CONVENTION_IS_STDCALL 0 -#endif - -#if CPU(X86) -#define HAS_FASTCALL_CALLING_CONVENTION 1 -#ifndef FASTCALL -#if COMPILER(MSVC) -#define FASTCALL __fastcall -#else -#define FASTCALL __attribute__ ((fastcall)) -#endif // COMPILER(MSVC) -#endif // FASTCALL -#else -#define HAS_FASTCALL_CALLING_CONVENTION 0 -#endif // CPU(X86) - -namespace JSC { - -// FunctionPtr: -// -// FunctionPtr should be used to wrap pointers to C/C++ functions in JSC -// (particularly, the stub functions). -class FunctionPtr { -public: - FunctionPtr() - : m_value(0) - { - } - - template - FunctionPtr(returnType(*value)()) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType(*value)(argType1)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType(*value)(argType1, argType2)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType(*value)(argType1, argType2, argType3)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType(*value)(argType1, argType2, argType3, argType4)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType(*value)(argType1, argType2, argType3, argType4, argType5)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - -// MSVC doesn't seem to treat functions with different calling conventions as -// different types; these methods already defined for fastcall, below. -#if CALLING_CONVENTION_IS_STDCALL && !OS(WINDOWS) - - template - FunctionPtr(returnType (CDECL *value)()) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (CDECL *value)(argType1)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (CDECL *value)(argType1, argType2)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (CDECL *value)(argType1, argType2, argType3)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (CDECL *value)(argType1, argType2, argType3, argType4)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } -#endif - -#if HAS_FASTCALL_CALLING_CONVENTION - - template - FunctionPtr(returnType (FASTCALL *value)()) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (FASTCALL *value)(argType1)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (FASTCALL *value)(argType1, argType2)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (FASTCALL *value)(argType1, argType2, argType3)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (FASTCALL *value)(argType1, argType2, argType3, argType4)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } -#endif - - template - explicit FunctionPtr(FunctionType* value) - // Using a C-ctyle cast here to avoid compiler error on RVTC: - // Error: #694: reinterpret_cast cannot cast away const or other type qualifiers - // (I guess on RVTC function pointers have a different constness to GCC/MSVC?) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - void* value() const { return m_value; } - void* executableAddress() const { return m_value; } - - -private: - void* m_value; -}; - -// ReturnAddressPtr: -// -// ReturnAddressPtr should be used to wrap return addresses generated by processor -// 'call' instructions exectued in JIT code. We use return addresses to look up -// exception and optimization information, and to repatch the call instruction -// that is the source of the return address. -class ReturnAddressPtr { -public: - ReturnAddressPtr() - : m_value(0) - { - } - - explicit ReturnAddressPtr(void* value) - : m_value(value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - explicit ReturnAddressPtr(FunctionPtr function) - : m_value(function.value()) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - void* value() const { return m_value; } - -private: - void* m_value; -}; - -// MacroAssemblerCodePtr: -// -// MacroAssemblerCodePtr should be used to wrap pointers to JIT generated code. -class MacroAssemblerCodePtr { -public: - MacroAssemblerCodePtr() - : m_value(0) - { - } - - explicit MacroAssemblerCodePtr(void* value) -#if CPU(ARM_THUMB2) - // Decorate the pointer as a thumb code pointer. - : m_value(reinterpret_cast(value) + 1) -#else - : m_value(value) -#endif - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - static MacroAssemblerCodePtr createFromExecutableAddress(void* value) - { - ASSERT_VALID_CODE_POINTER(value); - MacroAssemblerCodePtr result; - result.m_value = value; - return result; - } - -#if ENABLE(LLINT) - static MacroAssemblerCodePtr createLLIntCodePtr(LLIntCode codeId) - { - return createFromExecutableAddress(LLInt::getCodePtr(codeId)); - } -#endif - - explicit MacroAssemblerCodePtr(ReturnAddressPtr ra) - : m_value(ra.value()) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - void* executableAddress() const { return m_value; } -#if CPU(ARM_THUMB2) - // To use this pointer as a data address remove the decoration. - void* dataLocation() const { ASSERT_VALID_CODE_POINTER(m_value); return reinterpret_cast(m_value) - 1; } -#else - void* dataLocation() const { ASSERT_VALID_CODE_POINTER(m_value); return m_value; } -#endif - - bool operator!() const - { - return !m_value; - } - -private: - void* m_value; -}; - -// MacroAssemblerCodeRef: -// -// A reference to a section of JIT generated code. A CodeRef consists of a -// pointer to the code, and a ref pointer to the pool from within which it -// was allocated. -class MacroAssemblerCodeRef { -private: - // This is private because it's dangerous enough that we want uses of it - // to be easy to find - hence the static create method below. - explicit MacroAssemblerCodeRef(MacroAssemblerCodePtr codePtr) - : m_codePtr(codePtr) - { - ASSERT(m_codePtr); - } - -public: - MacroAssemblerCodeRef() - { - } - - MacroAssemblerCodeRef(PassRefPtr executableMemory) - : m_codePtr(executableMemory->start()) - , m_executableMemory(executableMemory) - { - ASSERT(m_executableMemory->isManaged()); - ASSERT(m_executableMemory->start()); - ASSERT(m_codePtr); - } - - // Use this only when you know that the codePtr refers to code that is - // already being kept alive through some other means. Typically this means - // that codePtr is immortal. - static MacroAssemblerCodeRef createSelfManagedCodeRef(MacroAssemblerCodePtr codePtr) - { - return MacroAssemblerCodeRef(codePtr); - } - -#if ENABLE(LLINT) - // Helper for creating self-managed code refs from LLInt. - static MacroAssemblerCodeRef createLLIntCodeRef(LLIntCode codeId) - { - return createSelfManagedCodeRef(MacroAssemblerCodePtr::createFromExecutableAddress(LLInt::getCodePtr(codeId))); - } -#endif - - ExecutableMemoryHandle* executableMemory() const - { - return m_executableMemory.get(); - } - - MacroAssemblerCodePtr code() const - { - return m_codePtr; - } - - size_t size() const - { - if (!m_executableMemory) - return 0; - return m_executableMemory->sizeInBytes(); - } - - bool tryToDisassemble(const char* prefix) const - { - return JSC::tryToDisassemble(m_codePtr, size(), prefix, WTF::dataFile()); - } - - bool operator!() const { return !m_codePtr; } - -private: - MacroAssemblerCodePtr m_codePtr; - RefPtr m_executableMemory; -}; - -} // namespace JSC - -#endif // MacroAssemblerCodeRef_h diff --git a/masm/assembler/MacroAssemblerMIPS.h b/masm/assembler/MacroAssemblerMIPS.h deleted file mode 100644 index 3ab2553001..0000000000 --- a/masm/assembler/MacroAssemblerMIPS.h +++ /dev/null @@ -1,2316 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All rights reserved. - * Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY MIPS TECHNOLOGIES, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MIPS TECHNOLOGIES, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssemblerMIPS_h -#define MacroAssemblerMIPS_h - -#if ENABLE(ASSEMBLER) && CPU(MIPS) - -#include "AbstractMacroAssembler.h" -#include "MIPSAssembler.h" - -namespace JSC { - -class MacroAssemblerMIPS : public AbstractMacroAssembler { -public: - typedef MIPSRegisters::FPRegisterID FPRegisterID; - - MacroAssemblerMIPS() - : m_fixedWidth(false) - { - } - - static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) - { - return value >= -2147483647 - 1 && value <= 2147483647; - } - - static const Scale ScalePtr = TimesFour; - - // For storing immediate number - static const RegisterID immTempRegister = MIPSRegisters::t0; - // For storing data loaded from the memory - static const RegisterID dataTempRegister = MIPSRegisters::t1; - // For storing address base - static const RegisterID addrTempRegister = MIPSRegisters::t2; - // For storing compare result - static const RegisterID cmpTempRegister = MIPSRegisters::t3; - - // FP temp register - static const FPRegisterID fpTempRegister = MIPSRegisters::f16; - - static const int MaximumCompactPtrAlignedAddressOffset = 0x7FFFFFFF; - - enum RelationalCondition { - Equal, - NotEqual, - Above, - AboveOrEqual, - Below, - BelowOrEqual, - GreaterThan, - GreaterThanOrEqual, - LessThan, - LessThanOrEqual - }; - - enum ResultCondition { - Overflow, - Signed, - Zero, - NonZero - }; - - enum DoubleCondition { - DoubleEqual, - DoubleNotEqual, - DoubleGreaterThan, - DoubleGreaterThanOrEqual, - DoubleLessThan, - DoubleLessThanOrEqual, - DoubleEqualOrUnordered, - DoubleNotEqualOrUnordered, - DoubleGreaterThanOrUnordered, - DoubleGreaterThanOrEqualOrUnordered, - DoubleLessThanOrUnordered, - DoubleLessThanOrEqualOrUnordered - }; - - static const RegisterID stackPointerRegister = MIPSRegisters::sp; - static const RegisterID returnAddressRegister = MIPSRegisters::ra; - - // Integer arithmetic operations: - // - // Operations are typically two operand - operation(source, srcDst) - // For many operations the source may be an TrustedImm32, the srcDst operand - // may often be a memory location (explictly described using an Address - // object). - - void add32(RegisterID src, RegisterID dest) - { - m_assembler.addu(dest, dest, src); - } - - void add32(TrustedImm32 imm, RegisterID dest) - { - add32(imm, dest, dest); - } - - void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (imm.m_value >= -32768 && imm.m_value <= 32767 - && !m_fixedWidth) { - /* - addiu dest, src, imm - */ - m_assembler.addiu(dest, src, imm.m_value); - } else { - /* - li immTemp, imm - addu dest, src, immTemp - */ - move(imm, immTempRegister); - m_assembler.addu(dest, src, immTempRegister); - } - } - - void add32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - add32(imm, src, dest); - } - - void add32(TrustedImm32 imm, Address address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - lw dataTemp, offset(base) - li immTemp, imm - addu dataTemp, dataTemp, immTemp - sw dataTemp, offset(base) - */ - m_assembler.lw(dataTempRegister, address.base, address.offset); - if (imm.m_value >= -32768 && imm.m_value <= 32767 - && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, imm.m_value); - else { - move(imm, immTempRegister); - m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); - } - m_assembler.sw(dataTempRegister, address.base, address.offset); - } else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - lw dataTemp, (offset & 0xffff)(addrTemp) - li immtemp, imm - addu dataTemp, dataTemp, immTemp - sw dataTemp, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); - - if (imm.m_value >= -32768 && imm.m_value <= 32767 && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, imm.m_value); - else { - move(imm, immTempRegister); - m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); - } - m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); - } - } - - void add32(Address src, RegisterID dest) - { - load32(src, dataTempRegister); - add32(dataTempRegister, dest); - } - - void add32(AbsoluteAddress src, RegisterID dest) - { - load32(src.m_ptr, dataTempRegister); - add32(dataTempRegister, dest); - } - - void add32(RegisterID src, Address dest) - { - if (dest.offset >= -32768 && dest.offset <= 32767 && !m_fixedWidth) { - /* - lw dataTemp, offset(base) - addu dataTemp, dataTemp, src - sw dataTemp, offset(base) - */ - m_assembler.lw(dataTempRegister, dest.base, dest.offset); - m_assembler.addu(dataTempRegister, dataTempRegister, src); - m_assembler.sw(dataTempRegister, dest.base, dest.offset); - } else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - lw dataTemp, (offset & 0xffff)(addrTemp) - addu dataTemp, dataTemp, src - sw dataTemp, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (dest.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, dest.base); - m_assembler.lw(dataTempRegister, addrTempRegister, dest.offset); - m_assembler.addu(dataTempRegister, dataTempRegister, src); - m_assembler.sw(dataTempRegister, addrTempRegister, dest.offset); - } - } - - void add32(TrustedImm32 imm, AbsoluteAddress address) - { - /* - li addrTemp, address - li immTemp, imm - lw cmpTemp, 0(addrTemp) - addu dataTemp, cmpTemp, immTemp - sw dataTemp, 0(addrTemp) - */ - move(TrustedImmPtr(address.m_ptr), addrTempRegister); - m_assembler.lw(cmpTempRegister, addrTempRegister, 0); - if (imm.m_value >= -32768 && imm.m_value <= 32767 && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, cmpTempRegister, imm.m_value); - else { - move(imm, immTempRegister); - m_assembler.addu(dataTempRegister, cmpTempRegister, immTempRegister); - } - m_assembler.sw(dataTempRegister, addrTempRegister, 0); - } - - void add64(TrustedImm32 imm, AbsoluteAddress address) - { - /* - add32(imm, address) - sltu immTemp, dataTemp, cmpTemp # set carry-in bit - lw dataTemp, 4(addrTemp) - addiu dataTemp, imm.m_value >> 31 ? -1 : 0 - addu dataTemp, dataTemp, immTemp - sw dataTemp, 4(addrTemp) - */ - add32(imm, address); - m_assembler.sltu(immTempRegister, dataTempRegister, cmpTempRegister); - m_assembler.lw(dataTempRegister, addrTempRegister, 4); - if (imm.m_value >> 31) - m_assembler.addiu(dataTempRegister, dataTempRegister, -1); - m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); - m_assembler.sw(dataTempRegister, addrTempRegister, 4); - } - - void and32(RegisterID src, RegisterID dest) - { - m_assembler.andInsn(dest, dest, src); - } - - void and32(TrustedImm32 imm, RegisterID dest) - { - if (!imm.m_value && !m_fixedWidth) - move(MIPSRegisters::zero, dest); - else if (imm.m_value > 0 && imm.m_value < 65535 && !m_fixedWidth) - m_assembler.andi(dest, dest, imm.m_value); - else { - /* - li immTemp, imm - and dest, dest, immTemp - */ - move(imm, immTempRegister); - m_assembler.andInsn(dest, dest, immTempRegister); - } - } - - void lshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.sll(dest, dest, imm.m_value); - } - - void lshift32(RegisterID shiftAmount, RegisterID dest) - { - m_assembler.sllv(dest, dest, shiftAmount); - } - - void mul32(RegisterID src, RegisterID dest) - { - m_assembler.mul(dest, dest, src); - } - - void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (!imm.m_value && !m_fixedWidth) - move(MIPSRegisters::zero, dest); - else if (imm.m_value == 1 && !m_fixedWidth) - move(src, dest); - else { - /* - li dataTemp, imm - mul dest, src, dataTemp - */ - move(imm, dataTempRegister); - m_assembler.mul(dest, src, dataTempRegister); - } - } - - void neg32(RegisterID srcDest) - { - m_assembler.subu(srcDest, MIPSRegisters::zero, srcDest); - } - - void or32(RegisterID src, RegisterID dest) - { - m_assembler.orInsn(dest, dest, src); - } - - void or32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.orInsn(dest, op1, op2); - } - - void or32(TrustedImm32 imm, RegisterID dest) - { - if (!imm.m_value && !m_fixedWidth) - return; - - if (imm.m_value > 0 && imm.m_value < 65535 - && !m_fixedWidth) { - m_assembler.ori(dest, dest, imm.m_value); - return; - } - - /* - li dataTemp, imm - or dest, dest, dataTemp - */ - move(imm, dataTempRegister); - m_assembler.orInsn(dest, dest, dataTempRegister); - } - - void or32(RegisterID src, AbsoluteAddress dest) - { - load32(dest.m_ptr, dataTempRegister); - m_assembler.orInsn(dataTempRegister, dataTempRegister, src); - store32(dataTempRegister, dest.m_ptr); - } - - void rshift32(RegisterID shiftAmount, RegisterID dest) - { - m_assembler.srav(dest, dest, shiftAmount); - } - - void rshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.sra(dest, dest, imm.m_value); - } - - void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.sra(dest, src, imm.m_value); - } - - void urshift32(RegisterID shiftAmount, RegisterID dest) - { - m_assembler.srlv(dest, dest, shiftAmount); - } - - void urshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.srl(dest, dest, imm.m_value); - } - - void sub32(RegisterID src, RegisterID dest) - { - m_assembler.subu(dest, dest, src); - } - - void sub32(TrustedImm32 imm, RegisterID dest) - { - if (imm.m_value >= -32767 && imm.m_value <= 32768 - && !m_fixedWidth) { - /* - addiu dest, src, imm - */ - m_assembler.addiu(dest, dest, -imm.m_value); - } else { - /* - li immTemp, imm - subu dest, src, immTemp - */ - move(imm, immTempRegister); - m_assembler.subu(dest, dest, immTempRegister); - } - } - - void sub32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - if (imm.m_value >= -32767 && imm.m_value <= 32768 - && !m_fixedWidth) { - /* - addiu dest, src, imm - */ - m_assembler.addiu(dest, src, -imm.m_value); - } else { - /* - li immTemp, imm - subu dest, src, immTemp - */ - move(imm, immTempRegister); - m_assembler.subu(dest, src, immTempRegister); - } - } - - void sub32(TrustedImm32 imm, Address address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - lw dataTemp, offset(base) - li immTemp, imm - subu dataTemp, dataTemp, immTemp - sw dataTemp, offset(base) - */ - m_assembler.lw(dataTempRegister, address.base, address.offset); - if (imm.m_value >= -32767 && imm.m_value <= 32768 && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); - else { - move(imm, immTempRegister); - m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); - } - m_assembler.sw(dataTempRegister, address.base, address.offset); - } else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - lw dataTemp, (offset & 0xffff)(addrTemp) - li immtemp, imm - subu dataTemp, dataTemp, immTemp - sw dataTemp, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); - - if (imm.m_value >= -32767 && imm.m_value <= 32768 - && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); - else { - move(imm, immTempRegister); - m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); - } - m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); - } - } - - void sub32(Address src, RegisterID dest) - { - load32(src, dataTempRegister); - sub32(dataTempRegister, dest); - } - - void sub32(TrustedImm32 imm, AbsoluteAddress address) - { - /* - li addrTemp, address - li immTemp, imm - lw dataTemp, 0(addrTemp) - subu dataTemp, dataTemp, immTemp - sw dataTemp, 0(addrTemp) - */ - move(TrustedImmPtr(address.m_ptr), addrTempRegister); - m_assembler.lw(dataTempRegister, addrTempRegister, 0); - - if (imm.m_value >= -32767 && imm.m_value <= 32768 && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); - else { - move(imm, immTempRegister); - m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); - } - m_assembler.sw(dataTempRegister, addrTempRegister, 0); - } - - void xor32(RegisterID src, RegisterID dest) - { - m_assembler.xorInsn(dest, dest, src); - } - - void xor32(TrustedImm32 imm, RegisterID dest) - { - if (imm.m_value == -1) { - m_assembler.nor(dest, dest, MIPSRegisters::zero); - return; - } - - /* - li immTemp, imm - xor dest, dest, immTemp - */ - move(imm, immTempRegister); - m_assembler.xorInsn(dest, dest, immTempRegister); - } - - void sqrtDouble(FPRegisterID src, FPRegisterID dst) - { - m_assembler.sqrtd(dst, src); - } - - void absDouble(FPRegisterID, FPRegisterID) - { - ASSERT_NOT_REACHED(); - } - - ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) - { - ConvertibleLoadLabel result(this); - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - lw dest, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lw(dest, addrTempRegister, address.offset); - return result; - } - - // Memory access operations: - // - // Loads are of the form load(address, destination) and stores of the form - // store(source, address). The source for a store may be an TrustedImm32. Address - // operand objects to loads and store will be implicitly constructed if a - // register is passed. - - /* Need to use zero-extened load byte for load8. */ - void load8(ImplicitAddress address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) - m_assembler.lbu(dest, address.base, address.offset); - else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - lbu dest, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lbu(dest, addrTempRegister, address.offset); - } - } - - void load8(BaseIndex address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lbu dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lbu(dest, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - lbu dest, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.lbu(dest, addrTempRegister, address.offset); - } - } - - void load8Signed(BaseIndex address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lb dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lb(dest, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - lb dest, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.lb(dest, addrTempRegister, address.offset); - } - } - - void load32(ImplicitAddress address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) - m_assembler.lw(dest, address.base, address.offset); - else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - lw dest, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lw(dest, addrTempRegister, address.offset); - } - } - - void load32(BaseIndex address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lw dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lw(dest, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - lw dest, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.lw(dest, addrTempRegister, address.offset); - } - } - - void load16Unaligned(BaseIndex address, RegisterID dest) - { - load16(address, dest); - } - - void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32764 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - (Big-Endian) - lwl dest, address.offset(addrTemp) - lwr dest, address.offset+3(addrTemp) - (Little-Endian) - lwl dest, address.offset+3(addrTemp) - lwr dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); -#if CPU(BIG_ENDIAN) - m_assembler.lwl(dest, addrTempRegister, address.offset); - m_assembler.lwr(dest, addrTempRegister, address.offset + 3); -#else - m_assembler.lwl(dest, addrTempRegister, address.offset + 3); - m_assembler.lwr(dest, addrTempRegister, address.offset); - -#endif - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, address.offset >> 16 - ori immTemp, immTemp, address.offset & 0xffff - addu addrTemp, addrTemp, immTemp - (Big-Endian) - lw dest, 0(at) - lw dest, 3(at) - (Little-Endian) - lw dest, 3(at) - lw dest, 0(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, address.offset >> 16); - m_assembler.ori(immTempRegister, immTempRegister, address.offset); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); -#if CPU(BIG_ENDIAN) - m_assembler.lwl(dest, addrTempRegister, 0); - m_assembler.lwr(dest, addrTempRegister, 3); -#else - m_assembler.lwl(dest, addrTempRegister, 3); - m_assembler.lwr(dest, addrTempRegister, 0); -#endif - } - } - - void load32(const void* address, RegisterID dest) - { - /* - li addrTemp, address - lw dest, 0(addrTemp) - */ - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.lw(dest, addrTempRegister, 0); - } - - DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) - { - m_fixedWidth = true; - /* - lui addrTemp, address.offset >> 16 - ori addrTemp, addrTemp, address.offset & 0xffff - addu addrTemp, addrTemp, address.base - lw dest, 0(addrTemp) - */ - DataLabel32 dataLabel(this); - move(TrustedImm32(address.offset), addrTempRegister); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lw(dest, addrTempRegister, 0); - m_fixedWidth = false; - return dataLabel; - } - - DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - DataLabelCompact dataLabel(this); - load32WithAddressOffsetPatch(address, dest); - return dataLabel; - } - - /* Need to use zero-extened load half-word for load16. */ - void load16(ImplicitAddress address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) - m_assembler.lhu(dest, address.base, address.offset); - else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - lhu dest, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lhu(dest, addrTempRegister, address.offset); - } - } - - /* Need to use zero-extened load half-word for load16. */ - void load16(BaseIndex address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lhu dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lhu(dest, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - lhu dest, (address.offset & 0xffff)(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.lhu(dest, addrTempRegister, address.offset); - } - } - - void load16Signed(BaseIndex address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lh dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lh(dest, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - lh dest, (address.offset & 0xffff)(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.lh(dest, addrTempRegister, address.offset); - } - } - - DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) - { - m_fixedWidth = true; - /* - lui addrTemp, address.offset >> 16 - ori addrTemp, addrTemp, address.offset & 0xffff - addu addrTemp, addrTemp, address.base - sw src, 0(addrTemp) - */ - DataLabel32 dataLabel(this); - move(TrustedImm32(address.offset), addrTempRegister); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.sw(src, addrTempRegister, 0); - m_fixedWidth = false; - return dataLabel; - } - - void store8(RegisterID src, BaseIndex address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - sb src, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.sb(src, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - sb src, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.sb(src, addrTempRegister, address.offset); - } - } - - void store8(TrustedImm32 imm, void* address) - { - /* - li immTemp, imm - li addrTemp, address - sb src, 0(addrTemp) - */ - if (!imm.m_value && !m_fixedWidth) { - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.sb(MIPSRegisters::zero, addrTempRegister, 0); - } else { - move(imm, immTempRegister); - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.sb(immTempRegister, addrTempRegister, 0); - } - } - - void store16(RegisterID src, BaseIndex address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - sh src, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.sh(src, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - sh src, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.sh(src, addrTempRegister, address.offset); - } - } - - void store32(RegisterID src, ImplicitAddress address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) - m_assembler.sw(src, address.base, address.offset); - else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - sw src, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.sw(src, addrTempRegister, address.offset); - } - } - - void store32(RegisterID src, BaseIndex address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - sw src, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.sw(src, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - sw src, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.sw(src, addrTempRegister, address.offset); - } - } - - void store32(TrustedImm32 imm, ImplicitAddress address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - if (!imm.m_value) - m_assembler.sw(MIPSRegisters::zero, address.base, address.offset); - else { - move(imm, immTempRegister); - m_assembler.sw(immTempRegister, address.base, address.offset); - } - } else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - sw immTemp, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - if (!imm.m_value && !m_fixedWidth) - m_assembler.sw(MIPSRegisters::zero, addrTempRegister, address.offset); - else { - move(imm, immTempRegister); - m_assembler.sw(immTempRegister, addrTempRegister, address.offset); - } - } - } - - void store32(RegisterID src, const void* address) - { - /* - li addrTemp, address - sw src, 0(addrTemp) - */ - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.sw(src, addrTempRegister, 0); - } - - void store32(TrustedImm32 imm, const void* address) - { - /* - li immTemp, imm - li addrTemp, address - sw src, 0(addrTemp) - */ - if (!imm.m_value && !m_fixedWidth) { - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.sw(MIPSRegisters::zero, addrTempRegister, 0); - } else { - move(imm, immTempRegister); - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.sw(immTempRegister, addrTempRegister, 0); - } - } - - // Floating-point operations: - - static bool supportsFloatingPoint() - { -#if WTF_MIPS_DOUBLE_FLOAT - return true; -#else - return false; -#endif - } - - static bool supportsFloatingPointTruncate() - { -#if WTF_MIPS_DOUBLE_FLOAT && WTF_MIPS_ISA_AT_LEAST(2) - return true; -#else - return false; -#endif - } - - static bool supportsFloatingPointSqrt() - { -#if WTF_MIPS_DOUBLE_FLOAT && WTF_MIPS_ISA_AT_LEAST(2) - return true; -#else - return false; -#endif - } - static bool supportsFloatingPointAbs() { return false; } - - // Stack manipulation operations: - // - // The ABI is assumed to provide a stack abstraction to memory, - // containing machine word sized units of data. Push and pop - // operations add and remove a single register sized unit of data - // to or from the stack. Peek and poke operations read or write - // values on the stack, without moving the current stack position. - - void pop(RegisterID dest) - { - m_assembler.lw(dest, MIPSRegisters::sp, 0); - m_assembler.addiu(MIPSRegisters::sp, MIPSRegisters::sp, 4); - } - - void push(RegisterID src) - { - m_assembler.addiu(MIPSRegisters::sp, MIPSRegisters::sp, -4); - m_assembler.sw(src, MIPSRegisters::sp, 0); - } - - void push(Address address) - { - load32(address, dataTempRegister); - push(dataTempRegister); - } - - void push(TrustedImm32 imm) - { - move(imm, immTempRegister); - push(immTempRegister); - } - - // Register move operations: - // - // Move values in registers. - - void move(TrustedImm32 imm, RegisterID dest) - { - if (!imm.m_value && !m_fixedWidth) - move(MIPSRegisters::zero, dest); - else if (m_fixedWidth) { - m_assembler.lui(dest, imm.m_value >> 16); - m_assembler.ori(dest, dest, imm.m_value); - } else - m_assembler.li(dest, imm.m_value); - } - - void move(RegisterID src, RegisterID dest) - { - if (src != dest || m_fixedWidth) - m_assembler.move(dest, src); - } - - void move(TrustedImmPtr imm, RegisterID dest) - { - move(TrustedImm32(imm), dest); - } - - void swap(RegisterID reg1, RegisterID reg2) - { - move(reg1, immTempRegister); - move(reg2, reg1); - move(immTempRegister, reg2); - } - - void signExtend32ToPtr(RegisterID src, RegisterID dest) - { - if (src != dest || m_fixedWidth) - move(src, dest); - } - - void zeroExtend32ToPtr(RegisterID src, RegisterID dest) - { - if (src != dest || m_fixedWidth) - move(src, dest); - } - - // Forwards / external control flow operations: - // - // This set of jump and conditional branch operations return a Jump - // object which may linked at a later point, allow forwards jump, - // or jumps that will require external linkage (after the code has been - // relocated). - // - // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge - // respecitvely, for unsigned comparisons the names b, a, be, and ae are - // used (representing the names 'below' and 'above'). - // - // Operands to the comparision are provided in the expected order, e.g. - // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when - // treated as a signed 32bit value, is less than or equal to 5. - // - // jz and jnz test whether the first operand is equal to zero, and take - // an optional second operand of a mask under which to perform the test. - - Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) - { - // Make sure the immediate value is unsigned 8 bits. - ASSERT(!(right.m_value & 0xFFFFFF00)); - load8(left, dataTempRegister); - move(right, immTempRegister); - return branch32(cond, dataTempRegister, immTempRegister); - } - - void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) - { - // Make sure the immediate value is unsigned 8 bits. - ASSERT(!(right.m_value & 0xFFFFFF00)); - load8(left, dataTempRegister); - move(right, immTempRegister); - compare32(cond, dataTempRegister, immTempRegister, dest); - } - - Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - ASSERT(!(right.m_value & 0xFFFFFF00)); - load8(left, dataTempRegister); - // Be careful that the previous load8() uses immTempRegister. - // So, we need to put move() after load8(). - move(right, immTempRegister); - return branch32(cond, dataTempRegister, immTempRegister); - } - - Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) - { - if (cond == Equal) - return branchEqual(left, right); - if (cond == NotEqual) - return branchNotEqual(left, right); - if (cond == Above) { - m_assembler.sltu(cmpTempRegister, right, left); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == AboveOrEqual) { - m_assembler.sltu(cmpTempRegister, left, right); - return branchEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == Below) { - m_assembler.sltu(cmpTempRegister, left, right); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == BelowOrEqual) { - m_assembler.sltu(cmpTempRegister, right, left); - return branchEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == GreaterThan) { - m_assembler.slt(cmpTempRegister, right, left); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == GreaterThanOrEqual) { - m_assembler.slt(cmpTempRegister, left, right); - return branchEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == LessThan) { - m_assembler.slt(cmpTempRegister, left, right); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == LessThanOrEqual) { - m_assembler.slt(cmpTempRegister, right, left); - return branchEqual(cmpTempRegister, MIPSRegisters::zero); - } - ASSERT(0); - - return Jump(); - } - - Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) - { - move(right, immTempRegister); - return branch32(cond, left, immTempRegister); - } - - Jump branch32(RelationalCondition cond, RegisterID left, Address right) - { - load32(right, dataTempRegister); - return branch32(cond, left, dataTempRegister); - } - - Jump branch32(RelationalCondition cond, Address left, RegisterID right) - { - load32(left, dataTempRegister); - return branch32(cond, dataTempRegister, right); - } - - Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) - { - load32(left, dataTempRegister); - move(right, immTempRegister); - return branch32(cond, dataTempRegister, immTempRegister); - } - - Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - load32(left, dataTempRegister); - // Be careful that the previous load32() uses immTempRegister. - // So, we need to put move() after load32(). - move(right, immTempRegister); - return branch32(cond, dataTempRegister, immTempRegister); - } - - Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - load32WithUnalignedHalfWords(left, dataTempRegister); - // Be careful that the previous load32WithUnalignedHalfWords() - // uses immTempRegister. - // So, we need to put move() after load32WithUnalignedHalfWords(). - move(right, immTempRegister); - return branch32(cond, dataTempRegister, immTempRegister); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - load32(left.m_ptr, dataTempRegister); - return branch32(cond, dataTempRegister, right); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) - { - load32(left.m_ptr, dataTempRegister); - move(right, immTempRegister); - return branch32(cond, dataTempRegister, immTempRegister); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) - { - ASSERT((cond == Zero) || (cond == NonZero)); - m_assembler.andInsn(cmpTempRegister, reg, mask); - if (cond == Zero) - return branchEqual(cmpTempRegister, MIPSRegisters::zero); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - ASSERT((cond == Zero) || (cond == NonZero)); - if (mask.m_value == -1 && !m_fixedWidth) { - if (cond == Zero) - return branchEqual(reg, MIPSRegisters::zero); - return branchNotEqual(reg, MIPSRegisters::zero); - } - move(mask, immTempRegister); - return branchTest32(cond, reg, immTempRegister); - } - - Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - load32(address, dataTempRegister); - return branchTest32(cond, dataTempRegister, mask); - } - - Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - load32(address, dataTempRegister); - return branchTest32(cond, dataTempRegister, mask); - } - - Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - load8(address, dataTempRegister); - return branchTest32(cond, dataTempRegister, mask); - } - - Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - move(TrustedImmPtr(address.m_ptr), dataTempRegister); - load8(Address(dataTempRegister), dataTempRegister); - return branchTest32(cond, dataTempRegister, mask); - } - - Jump jump() - { - return branchEqual(MIPSRegisters::zero, MIPSRegisters::zero); - } - - void jump(RegisterID target) - { - m_assembler.jr(target); - m_assembler.nop(); - } - - void jump(Address address) - { - m_fixedWidth = true; - load32(address, MIPSRegisters::t9); - m_assembler.jr(MIPSRegisters::t9); - m_assembler.nop(); - m_fixedWidth = false; - } - - void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) - { - m_assembler.vmov(dest1, dest2, src); - } - - void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) - { - UNUSED_PARAM(scratch); - m_assembler.vmov(dest, src1, src2); - } - - // Arithmetic control flow operations: - // - // This set of conditional branch operations branch based - // on the result of an arithmetic operation. The operation - // is performed as normal, storing the result. - // - // * jz operations branch if the result is zero. - // * jo operations branch if the (signed) arithmetic - // operation caused an overflow to occur. - - Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - if (cond == Overflow) { - /* - move dest, dataTemp - xor cmpTemp, dataTemp, src - bltz cmpTemp, No_overflow # diff sign bit -> no overflow - addu dest, dataTemp, src - xor cmpTemp, dest, dataTemp - bgez cmpTemp, No_overflow # same sign big -> no overflow - nop - b Overflow - nop - nop - nop - nop - nop - No_overflow: - */ - move(dest, dataTempRegister); - m_assembler.xorInsn(cmpTempRegister, dataTempRegister, src); - m_assembler.bltz(cmpTempRegister, 10); - m_assembler.addu(dest, dataTempRegister, src); - m_assembler.xorInsn(cmpTempRegister, dest, dataTempRegister); - m_assembler.bgez(cmpTempRegister, 7); - m_assembler.nop(); - return jump(); - } - if (cond == Signed) { - add32(src, dest); - // Check if dest is negative. - m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == Zero) { - add32(src, dest); - return branchEqual(dest, MIPSRegisters::zero); - } - if (cond == NonZero) { - add32(src, dest); - return branchNotEqual(dest, MIPSRegisters::zero); - } - ASSERT(0); - return Jump(); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - move(imm, immTempRegister); - return branchAdd32(cond, immTempRegister, dest); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) - { - move(imm, immTempRegister); - move(src, dest); - return branchAdd32(cond, immTempRegister, dest); - } - - Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - if (cond == Overflow) { - /* - mult src, dest - mfhi dataTemp - mflo dest - sra addrTemp, dest, 31 - beq dataTemp, addrTemp, No_overflow # all sign bits (bit 63 to bit 31) are the same -> no overflow - nop - b Overflow - nop - nop - nop - nop - nop - No_overflow: - */ - m_assembler.mult(src, dest); - m_assembler.mfhi(dataTempRegister); - m_assembler.mflo(dest); - m_assembler.sra(addrTempRegister, dest, 31); - m_assembler.beq(dataTempRegister, addrTempRegister, 7); - m_assembler.nop(); - return jump(); - } - if (cond == Signed) { - mul32(src, dest); - // Check if dest is negative. - m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == Zero) { - mul32(src, dest); - return branchEqual(dest, MIPSRegisters::zero); - } - if (cond == NonZero) { - mul32(src, dest); - return branchNotEqual(dest, MIPSRegisters::zero); - } - ASSERT(0); - return Jump(); - } - - Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(imm, immTempRegister); - move(src, dest); - return branchMul32(cond, immTempRegister, dest); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - if (cond == Overflow) { - /* - move dest, dataTemp - xor cmpTemp, dataTemp, src - bgez cmpTemp, No_overflow # same sign bit -> no overflow - subu dest, dataTemp, src - xor cmpTemp, dest, dataTemp - bgez cmpTemp, No_overflow # same sign bit -> no overflow - nop - b Overflow - nop - nop - nop - nop - nop - No_overflow: - */ - move(dest, dataTempRegister); - m_assembler.xorInsn(cmpTempRegister, dataTempRegister, src); - m_assembler.bgez(cmpTempRegister, 10); - m_assembler.subu(dest, dataTempRegister, src); - m_assembler.xorInsn(cmpTempRegister, dest, dataTempRegister); - m_assembler.bgez(cmpTempRegister, 7); - m_assembler.nop(); - return jump(); - } - if (cond == Signed) { - sub32(src, dest); - // Check if dest is negative. - m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == Zero) { - sub32(src, dest); - return branchEqual(dest, MIPSRegisters::zero); - } - if (cond == NonZero) { - sub32(src, dest); - return branchNotEqual(dest, MIPSRegisters::zero); - } - ASSERT(0); - return Jump(); - } - - Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - move(imm, immTempRegister); - return branchSub32(cond, immTempRegister, dest); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) - { - move(imm, immTempRegister); - move(src, dest); - return branchSub32(cond, immTempRegister, dest); - } - - Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); - if (cond == Signed) { - or32(src, dest); - // Check if dest is negative. - m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == Zero) { - or32(src, dest); - return branchEqual(dest, MIPSRegisters::zero); - } - if (cond == NonZero) { - or32(src, dest); - return branchNotEqual(dest, MIPSRegisters::zero); - } - ASSERT(0); - return Jump(); - } - - // Miscellaneous operations: - - void breakpoint() - { - m_assembler.bkpt(); - } - - Call nearCall() - { - /* We need two words for relaxation. */ - m_assembler.nop(); - m_assembler.nop(); - m_assembler.jal(); - m_assembler.nop(); - return Call(m_assembler.label(), Call::LinkableNear); - } - - Call call() - { - m_assembler.lui(MIPSRegisters::t9, 0); - m_assembler.ori(MIPSRegisters::t9, MIPSRegisters::t9, 0); - m_assembler.jalr(MIPSRegisters::t9); - m_assembler.nop(); - return Call(m_assembler.label(), Call::Linkable); - } - - Call call(RegisterID target) - { - m_assembler.jalr(target); - m_assembler.nop(); - return Call(m_assembler.label(), Call::None); - } - - Call call(Address address) - { - m_fixedWidth = true; - load32(address, MIPSRegisters::t9); - m_assembler.jalr(MIPSRegisters::t9); - m_assembler.nop(); - m_fixedWidth = false; - return Call(m_assembler.label(), Call::None); - } - - void ret() - { - m_assembler.jr(MIPSRegisters::ra); - m_assembler.nop(); - } - - void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) - { - if (cond == Equal) { - m_assembler.xorInsn(dest, left, right); - m_assembler.sltiu(dest, dest, 1); - } else if (cond == NotEqual) { - m_assembler.xorInsn(dest, left, right); - m_assembler.sltu(dest, MIPSRegisters::zero, dest); - } else if (cond == Above) - m_assembler.sltu(dest, right, left); - else if (cond == AboveOrEqual) { - m_assembler.sltu(dest, left, right); - m_assembler.xori(dest, dest, 1); - } else if (cond == Below) - m_assembler.sltu(dest, left, right); - else if (cond == BelowOrEqual) { - m_assembler.sltu(dest, right, left); - m_assembler.xori(dest, dest, 1); - } else if (cond == GreaterThan) - m_assembler.slt(dest, right, left); - else if (cond == GreaterThanOrEqual) { - m_assembler.slt(dest, left, right); - m_assembler.xori(dest, dest, 1); - } else if (cond == LessThan) - m_assembler.slt(dest, left, right); - else if (cond == LessThanOrEqual) { - m_assembler.slt(dest, right, left); - m_assembler.xori(dest, dest, 1); - } - } - - void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - move(right, immTempRegister); - compare32(cond, left, immTempRegister, dest); - } - - void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - ASSERT((cond == Zero) || (cond == NonZero)); - load8(address, dataTempRegister); - if (mask.m_value == -1 && !m_fixedWidth) { - if (cond == Zero) - m_assembler.sltiu(dest, dataTempRegister, 1); - else - m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); - } else { - move(mask, immTempRegister); - m_assembler.andInsn(cmpTempRegister, dataTempRegister, immTempRegister); - if (cond == Zero) - m_assembler.sltiu(dest, cmpTempRegister, 1); - else - m_assembler.sltu(dest, MIPSRegisters::zero, cmpTempRegister); - } - } - - void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - ASSERT((cond == Zero) || (cond == NonZero)); - load32(address, dataTempRegister); - if (mask.m_value == -1 && !m_fixedWidth) { - if (cond == Zero) - m_assembler.sltiu(dest, dataTempRegister, 1); - else - m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); - } else { - move(mask, immTempRegister); - m_assembler.andInsn(cmpTempRegister, dataTempRegister, immTempRegister); - if (cond == Zero) - m_assembler.sltiu(dest, cmpTempRegister, 1); - else - m_assembler.sltu(dest, MIPSRegisters::zero, cmpTempRegister); - } - } - - DataLabel32 moveWithPatch(TrustedImm32 imm, RegisterID dest) - { - m_fixedWidth = true; - DataLabel32 label(this); - move(imm, dest); - m_fixedWidth = false; - return label; - } - - DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) - { - m_fixedWidth = true; - DataLabelPtr label(this); - move(initialValue, dest); - m_fixedWidth = false; - return label; - } - - Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - m_fixedWidth = true; - dataLabel = moveWithPatch(initialRightValue, immTempRegister); - Jump temp = branch32(cond, left, immTempRegister); - m_fixedWidth = false; - return temp; - } - - Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - m_fixedWidth = true; - load32(left, dataTempRegister); - dataLabel = moveWithPatch(initialRightValue, immTempRegister); - Jump temp = branch32(cond, dataTempRegister, immTempRegister); - m_fixedWidth = false; - return temp; - } - - DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) - { - m_fixedWidth = true; - DataLabelPtr dataLabel = moveWithPatch(initialValue, dataTempRegister); - store32(dataTempRegister, address); - m_fixedWidth = false; - return dataLabel; - } - - DataLabelPtr storePtrWithPatch(ImplicitAddress address) - { - return storePtrWithPatch(TrustedImmPtr(0), address); - } - - Call tailRecursiveCall() - { - // Like a normal call, but don't update the returned address register - m_fixedWidth = true; - move(TrustedImm32(0), MIPSRegisters::t9); - m_assembler.jr(MIPSRegisters::t9); - m_assembler.nop(); - m_fixedWidth = false; - return Call(m_assembler.label(), Call::Linkable); - } - - Call makeTailRecursiveCall(Jump oldJump) - { - oldJump.link(this); - return tailRecursiveCall(); - } - - void loadFloat(BaseIndex address, FPRegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lwc1 dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lwc1(dest, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - lwc1 dest, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.lwc1(dest, addrTempRegister, address.offset); - } - } - - void loadDouble(ImplicitAddress address, FPRegisterID dest) - { -#if WTF_MIPS_ISA(1) - /* - li addrTemp, address.offset - addu addrTemp, addrTemp, base - lwc1 dest, 0(addrTemp) - lwc1 dest+1, 4(addrTemp) - */ - move(TrustedImm32(address.offset), addrTempRegister); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lwc1(dest, addrTempRegister, 0); - m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); -#else - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - m_assembler.ldc1(dest, address.base, address.offset); - } else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - ldc1 dest, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.ldc1(dest, addrTempRegister, address.offset); - } -#endif - } - - void loadDouble(BaseIndex address, FPRegisterID dest) - { -#if WTF_MIPS_ISA(1) - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lwc1 dest, address.offset(addrTemp) - lwc1 dest+1, (address.offset+4)(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lwc1(dest, addrTempRegister, address.offset); - m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, address.offset + 4); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - lwc1 dest, (address.offset & 0xffff)(at) - lwc1 dest+4, (address.offset & 0xffff + 4)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.lwc1(dest, addrTempRegister, address.offset); - m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, address.offset + 4); - } -#else - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - ldc1 dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.ldc1(dest, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - ldc1 dest, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.ldc1(dest, addrTempRegister, address.offset); - } -#endif - } - - void loadDouble(const void* address, FPRegisterID dest) - { -#if WTF_MIPS_ISA(1) - /* - li addrTemp, address - lwc1 dest, 0(addrTemp) - lwc1 dest+1, 4(addrTemp) - */ - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.lwc1(dest, addrTempRegister, 0); - m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); -#else - /* - li addrTemp, address - ldc1 dest, 0(addrTemp) - */ - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.ldc1(dest, addrTempRegister, 0); -#endif - } - - void storeFloat(FPRegisterID src, BaseIndex address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - swc1 src, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.swc1(src, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - swc1 src, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.swc1(src, addrTempRegister, address.offset); - } - } - - void storeDouble(FPRegisterID src, ImplicitAddress address) - { -#if WTF_MIPS_ISA(1) - /* - li addrTemp, address.offset - addu addrTemp, addrTemp, base - swc1 dest, 0(addrTemp) - swc1 dest+1, 4(addrTemp) - */ - move(TrustedImm32(address.offset), addrTempRegister); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.swc1(src, addrTempRegister, 0); - m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, 4); -#else - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) - m_assembler.sdc1(src, address.base, address.offset); - else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - sdc1 src, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.sdc1(src, addrTempRegister, address.offset); - } -#endif - } - - void storeDouble(FPRegisterID src, BaseIndex address) - { -#if WTF_MIPS_ISA(1) - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - swc1 src, address.offset(addrTemp) - swc1 src+1, (address.offset + 4)(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.swc1(src, addrTempRegister, address.offset); - m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, address.offset + 4); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - swc1 src, (address.offset & 0xffff)(at) - swc1 src+1, (address.offset & 0xffff + 4)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.swc1(src, addrTempRegister, address.offset); - m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, address.offset + 4); - } -#else - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - sdc1 src, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.sdc1(src, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - sdc1 src, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.sdc1(src, addrTempRegister, address.offset); - } -#endif - } - - void storeDouble(FPRegisterID src, const void* address) - { -#if WTF_MIPS_ISA(1) - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.swc1(src, addrTempRegister, 0); - m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, 4); -#else - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.sdc1(src, addrTempRegister, 0); -#endif - } - - void addDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.addd(dest, dest, src); - } - - void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.addd(dest, op1, op2); - } - - void addDouble(Address src, FPRegisterID dest) - { - loadDouble(src, fpTempRegister); - m_assembler.addd(dest, dest, fpTempRegister); - } - - void addDouble(AbsoluteAddress address, FPRegisterID dest) - { - loadDouble(address.m_ptr, fpTempRegister); - m_assembler.addd(dest, dest, fpTempRegister); - } - - void subDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.subd(dest, dest, src); - } - - void subDouble(Address src, FPRegisterID dest) - { - loadDouble(src, fpTempRegister); - m_assembler.subd(dest, dest, fpTempRegister); - } - - void mulDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.muld(dest, dest, src); - } - - void mulDouble(Address src, FPRegisterID dest) - { - loadDouble(src, fpTempRegister); - m_assembler.muld(dest, dest, fpTempRegister); - } - - void divDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.divd(dest, dest, src); - } - - void convertInt32ToDouble(RegisterID src, FPRegisterID dest) - { - m_assembler.mtc1(src, fpTempRegister); - m_assembler.cvtdw(dest, fpTempRegister); - } - - void convertInt32ToDouble(Address src, FPRegisterID dest) - { - load32(src, dataTempRegister); - m_assembler.mtc1(dataTempRegister, fpTempRegister); - m_assembler.cvtdw(dest, fpTempRegister); - } - - void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) - { - load32(src.m_ptr, dataTempRegister); - m_assembler.mtc1(dataTempRegister, fpTempRegister); - m_assembler.cvtdw(dest, fpTempRegister); - } - - void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) - { - m_assembler.cvtds(dst, src); - } - - void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) - { - m_assembler.cvtsd(dst, src); - } - - void insertRelaxationWords() - { - /* We need four words for relaxation. */ - m_assembler.beq(MIPSRegisters::zero, MIPSRegisters::zero, 3); // Jump over nops; - m_assembler.nop(); - m_assembler.nop(); - m_assembler.nop(); - } - - Jump branchTrue() - { - m_assembler.appendJump(); - m_assembler.bc1t(); - m_assembler.nop(); - insertRelaxationWords(); - return Jump(m_assembler.label()); - } - - Jump branchFalse() - { - m_assembler.appendJump(); - m_assembler.bc1f(); - m_assembler.nop(); - insertRelaxationWords(); - return Jump(m_assembler.label()); - } - - Jump branchEqual(RegisterID rs, RegisterID rt) - { - m_assembler.appendJump(); - m_assembler.beq(rs, rt, 0); - m_assembler.nop(); - insertRelaxationWords(); - return Jump(m_assembler.label()); - } - - Jump branchNotEqual(RegisterID rs, RegisterID rt) - { - m_assembler.appendJump(); - m_assembler.bne(rs, rt, 0); - m_assembler.nop(); - insertRelaxationWords(); - return Jump(m_assembler.label()); - } - - Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) - { - if (cond == DoubleEqual) { - m_assembler.ceqd(left, right); - return branchTrue(); - } - if (cond == DoubleNotEqual) { - m_assembler.cueqd(left, right); - return branchFalse(); // false - } - if (cond == DoubleGreaterThan) { - m_assembler.cngtd(left, right); - return branchFalse(); // false - } - if (cond == DoubleGreaterThanOrEqual) { - m_assembler.cnged(left, right); - return branchFalse(); // false - } - if (cond == DoubleLessThan) { - m_assembler.cltd(left, right); - return branchTrue(); - } - if (cond == DoubleLessThanOrEqual) { - m_assembler.cled(left, right); - return branchTrue(); - } - if (cond == DoubleEqualOrUnordered) { - m_assembler.cueqd(left, right); - return branchTrue(); - } - if (cond == DoubleNotEqualOrUnordered) { - m_assembler.ceqd(left, right); - return branchFalse(); // false - } - if (cond == DoubleGreaterThanOrUnordered) { - m_assembler.coled(left, right); - return branchFalse(); // false - } - if (cond == DoubleGreaterThanOrEqualOrUnordered) { - m_assembler.coltd(left, right); - return branchFalse(); // false - } - if (cond == DoubleLessThanOrUnordered) { - m_assembler.cultd(left, right); - return branchTrue(); - } - if (cond == DoubleLessThanOrEqualOrUnordered) { - m_assembler.culed(left, right); - return branchTrue(); - } - ASSERT(0); - - return Jump(); - } - - // Truncates 'src' to an integer, and places the resulting 'dest'. - // If the result is not representable as a 32 bit value, branch. - // May also branch for some values that are representable in 32 bits - // (specifically, in this case, INT_MAX 0x7fffffff). - Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) - { - m_assembler.truncwd(fpTempRegister, src); - m_assembler.mfc1(dest, fpTempRegister); - return branch32(Equal, dest, TrustedImm32(0x7fffffff)); - } - - // Convert 'src' to an integer, and places the resulting 'dest'. - // If the result is not representable as a 32 bit value, branch. - // May also branch for some values that are representable in 32 bits - // (specifically, in this case, 0). - void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) - { - m_assembler.cvtwd(fpTempRegister, src); - m_assembler.mfc1(dest, fpTempRegister); - - // If the result is zero, it might have been -0.0, and the double comparison won't catch this! - failureCases.append(branch32(Equal, dest, MIPSRegisters::zero)); - - // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. - convertInt32ToDouble(dest, fpTemp); - failureCases.append(branchDouble(DoubleNotEqualOrUnordered, fpTemp, src)); - } - - Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) - { -#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 - m_assembler.mtc1(MIPSRegisters::zero, scratch); - m_assembler.mthc1(MIPSRegisters::zero, scratch); -#else - m_assembler.mtc1(MIPSRegisters::zero, scratch); - m_assembler.mtc1(MIPSRegisters::zero, FPRegisterID(scratch + 1)); -#endif - return branchDouble(DoubleNotEqual, reg, scratch); - } - - Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) - { -#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 - m_assembler.mtc1(MIPSRegisters::zero, scratch); - m_assembler.mthc1(MIPSRegisters::zero, scratch); -#else - m_assembler.mtc1(MIPSRegisters::zero, scratch); - m_assembler.mtc1(MIPSRegisters::zero, FPRegisterID(scratch + 1)); -#endif - return branchDouble(DoubleEqualOrUnordered, reg, scratch); - } - - void nop() - { - m_assembler.nop(); - } - - static FunctionPtr readCallTarget(CodeLocationCall call) - { - return FunctionPtr(reinterpret_cast(MIPSAssembler::readCallTarget(call.dataLocation()))); - } - - static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) - { - ASSERT_NOT_REACHED(); - } - - static ptrdiff_t maxJumpReplacementSize() - { - ASSERT_NOT_REACHED(); - return 0; - } - - static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } - - static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) - { - return label.labelAtOffset(0); - } - - static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) - { - MIPSAssembler::revertJumpToMove(instructionStart.dataLocation(), immTempRegister, reinterpret_cast(initialValue) & 0xffff); - } - - static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) - { - UNREACHABLE_FOR_PLATFORM(); - return CodeLocationLabel(); - } - - static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) - { - UNREACHABLE_FOR_PLATFORM(); - } - - -private: - // If m_fixedWidth is true, we will generate a fixed number of instructions. - // Otherwise, we can emit any number of instructions. - bool m_fixedWidth; - - friend class LinkBuffer; - friend class RepatchBuffer; - - static void linkCall(void* code, Call call, FunctionPtr function) - { - MIPSAssembler::linkCall(code, call.m_label, function.value()); - } - - static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) - { - MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } - - static void repatchCall(CodeLocationCall call, FunctionPtr destination) - { - MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } - -}; - -} - -#endif // ENABLE(ASSEMBLER) && CPU(MIPS) - -#endif // MacroAssemblerMIPS_h diff --git a/masm/assembler/MacroAssemblerSH4.cpp b/masm/assembler/MacroAssemblerSH4.cpp deleted file mode 100644 index 59de3ff48c..0000000000 --- a/masm/assembler/MacroAssemblerSH4.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2011 STMicroelectronics. All rights reserved. - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "config.h" - -#if ENABLE(ASSEMBLER) && CPU(SH4) - -#include "MacroAssemblerSH4.h" - -namespace JSC { - -void MacroAssemblerSH4::linkCall(void* code, Call call, FunctionPtr function) -{ - SH4Assembler::linkCall(code, call.m_label, function.value()); -} - -void MacroAssemblerSH4::repatchCall(CodeLocationCall call, CodeLocationLabel destination) -{ - SH4Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); -} - -void MacroAssemblerSH4::repatchCall(CodeLocationCall call, FunctionPtr destination) -{ - SH4Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); -} - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) diff --git a/masm/assembler/MacroAssemblerSH4.h b/masm/assembler/MacroAssemblerSH4.h deleted file mode 100644 index ef210f80cb..0000000000 --- a/masm/assembler/MacroAssemblerSH4.h +++ /dev/null @@ -1,2293 +0,0 @@ -/* - * Copyright (C) 2009-2011 STMicroelectronics. All rights reserved. - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef MacroAssemblerSH4_h -#define MacroAssemblerSH4_h - -#if ENABLE(ASSEMBLER) && CPU(SH4) - -#include "SH4Assembler.h" -#include "AbstractMacroAssembler.h" -#include - -namespace JSC { - -class MacroAssemblerSH4 : public AbstractMacroAssembler { -public: - typedef SH4Assembler::FPRegisterID FPRegisterID; - - static const Scale ScalePtr = TimesFour; - static const FPRegisterID fscratch = SH4Registers::fr10; - static const RegisterID stackPointerRegister = SH4Registers::sp; - static const RegisterID linkRegister = SH4Registers::pr; - static const RegisterID scratchReg3 = SH4Registers::r13; - - static const int MaximumCompactPtrAlignedAddressOffset = 60; - - static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) - { - return (value >= 0) && (value <= MaximumCompactPtrAlignedAddressOffset); - } - - enum RelationalCondition { - Equal = SH4Assembler::EQ, - NotEqual = SH4Assembler::NE, - Above = SH4Assembler::HI, - AboveOrEqual = SH4Assembler::HS, - Below = SH4Assembler::LI, - BelowOrEqual = SH4Assembler::LS, - GreaterThan = SH4Assembler::GT, - GreaterThanOrEqual = SH4Assembler::GE, - LessThan = SH4Assembler::LT, - LessThanOrEqual = SH4Assembler::LE - }; - - enum ResultCondition { - Overflow = SH4Assembler::OF, - Signed = SH4Assembler::SI, - Zero = SH4Assembler::EQ, - NonZero = SH4Assembler::NE - }; - - enum DoubleCondition { - // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. - DoubleEqual = SH4Assembler::EQ, - DoubleNotEqual = SH4Assembler::NE, - DoubleGreaterThan = SH4Assembler::GT, - DoubleGreaterThanOrEqual = SH4Assembler::GE, - DoubleLessThan = SH4Assembler::LT, - DoubleLessThanOrEqual = SH4Assembler::LE, - // If either operand is NaN, these conditions always evaluate to true. - DoubleEqualOrUnordered = SH4Assembler::EQU, - DoubleNotEqualOrUnordered = SH4Assembler::NEU, - DoubleGreaterThanOrUnordered = SH4Assembler::GTU, - DoubleGreaterThanOrEqualOrUnordered = SH4Assembler::GEU, - DoubleLessThanOrUnordered = SH4Assembler::LTU, - DoubleLessThanOrEqualOrUnordered = SH4Assembler::LEU, - }; - - RegisterID claimScratch() - { - return m_assembler.claimScratch(); - } - - void releaseScratch(RegisterID reg) - { - m_assembler.releaseScratch(reg); - } - - // Integer arithmetic operations - - void add32(RegisterID src, RegisterID dest) - { - m_assembler.addlRegReg(src, dest); - } - - void add32(TrustedImm32 imm, RegisterID dest) - { - if (m_assembler.isImmediate(imm.m_value)) { - m_assembler.addlImm8r(imm.m_value, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant(imm.m_value, scr); - m_assembler.addlRegReg(scr, dest); - releaseScratch(scr); - } - - void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (src != dest) - m_assembler.movlRegReg(src, dest); - add32(imm, dest); - } - - void add32(TrustedImm32 imm, Address address) - { - RegisterID scr = claimScratch(); - load32(address, scr); - add32(imm, scr); - store32(scr, address); - releaseScratch(scr); - } - - void add32(Address src, RegisterID dest) - { - RegisterID scr = claimScratch(); - load32(src, scr); - m_assembler.addlRegReg(scr, dest); - releaseScratch(scr); - } - - void add32(AbsoluteAddress src, RegisterID dest) - { - RegisterID scr = claimScratch(); - load32(src.m_ptr, scr); - m_assembler.addlRegReg(scr, dest); - releaseScratch(scr); - } - - void and32(RegisterID src, RegisterID dest) - { - m_assembler.andlRegReg(src, dest); - } - - void and32(TrustedImm32 imm, RegisterID dest) - { - if ((imm.m_value <= 255) && (imm.m_value >= 0) && (dest == SH4Registers::r0)) { - m_assembler.andlImm8r(imm.m_value, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant((imm.m_value), scr); - m_assembler.andlRegReg(scr, dest); - releaseScratch(scr); - } - - void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (src != dest) { - move(imm, dest); - and32(src, dest); - return; - } - - and32(imm, dest); - } - - void lshift32(RegisterID shiftamount, RegisterID dest) - { - if (shiftamount == SH4Registers::r0) - m_assembler.andlImm8r(0x1f, shiftamount); - else { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(0x1f, scr); - m_assembler.andlRegReg(scr, shiftamount); - releaseScratch(scr); - } - m_assembler.shllRegReg(dest, shiftamount); - } - - void rshift32(int imm, RegisterID dest) - { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(-imm, scr); - m_assembler.shaRegReg(dest, scr); - releaseScratch(scr); - } - - void lshift32(TrustedImm32 imm, RegisterID dest) - { - if (!imm.m_value) - return; - - if ((imm.m_value == 1) || (imm.m_value == 2) || (imm.m_value == 8) || (imm.m_value == 16)) { - m_assembler.shllImm8r(imm.m_value, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant((imm.m_value & 0x1f) , scr); - m_assembler.shllRegReg(dest, scr); - releaseScratch(scr); - } - - void lshift32(RegisterID src, TrustedImm32 shiftamount, RegisterID dest) - { - if (src != dest) - move(src, dest); - - lshift32(shiftamount, dest); - } - - void mul32(RegisterID src, RegisterID dest) - { - m_assembler.imullRegReg(src, dest); - m_assembler.stsmacl(dest); - } - - void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - RegisterID scr = claimScratch(); - move(imm, scr); - if (src != dest) - move(src, dest); - mul32(scr, dest); - releaseScratch(scr); - } - - void or32(RegisterID src, RegisterID dest) - { - m_assembler.orlRegReg(src, dest); - } - - void or32(TrustedImm32 imm, RegisterID dest) - { - if ((imm.m_value <= 255) && (imm.m_value >= 0) && (dest == SH4Registers::r0)) { - m_assembler.orlImm8r(imm.m_value, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant(imm.m_value, scr); - m_assembler.orlRegReg(scr, dest); - releaseScratch(scr); - } - - void or32(RegisterID op1, RegisterID op2, RegisterID dest) - { - if (op1 == op2) - move(op1, dest); - else if (op1 == dest) - or32(op2, dest); - else { - move(op2, dest); - or32(op1, dest); - } - } - - -void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (src != dest) { - move(imm, dest); - or32(src, dest); - return; - } - - or32(imm, dest); - } - - void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (src != dest) { - move(imm, dest); - xor32(src, dest); - return; - } - - xor32(imm, dest); - } - - void rshift32(RegisterID shiftamount, RegisterID dest) - { - if (shiftamount == SH4Registers::r0) - m_assembler.andlImm8r(0x1f, shiftamount); - else { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(0x1f, scr); - m_assembler.andlRegReg(scr, shiftamount); - releaseScratch(scr); - } - m_assembler.neg(shiftamount, shiftamount); - m_assembler.shaRegReg(dest, shiftamount); - } - - void rshift32(TrustedImm32 imm, RegisterID dest) - { - if (imm.m_value & 0x1f) - rshift32(imm.m_value & 0x1f, dest); - } - - void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - if (src != dest) - move(src, dest); - rshift32(imm, dest); - } - - void sub32(RegisterID src, RegisterID dest) - { - m_assembler.sublRegReg(src, dest); - } - - void sub32(TrustedImm32 imm, AbsoluteAddress address, RegisterID scratchReg) - { - RegisterID result = claimScratch(); - - m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); - m_assembler.movlMemReg(scratchReg, result); - - if (m_assembler.isImmediate(-imm.m_value)) - m_assembler.addlImm8r(-imm.m_value, result); - else { - m_assembler.loadConstant(imm.m_value, scratchReg3); - m_assembler.sublRegReg(scratchReg3, result); - } - - store32(result, scratchReg); - releaseScratch(result); - } - - void sub32(TrustedImm32 imm, AbsoluteAddress address) - { - RegisterID result = claimScratch(); - RegisterID scratchReg = claimScratch(); - - m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); - m_assembler.movlMemReg(scratchReg, result); - - if (m_assembler.isImmediate(-imm.m_value)) - m_assembler.addlImm8r(-imm.m_value, result); - else { - m_assembler.loadConstant(imm.m_value, scratchReg3); - m_assembler.sublRegReg(scratchReg3, result); - } - - store32(result, scratchReg); - releaseScratch(result); - releaseScratch(scratchReg); - } - - void add32(TrustedImm32 imm, AbsoluteAddress address, RegisterID scratchReg) - { - RegisterID result = claimScratch(); - - m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); - m_assembler.movlMemReg(scratchReg, result); - - if (m_assembler.isImmediate(imm.m_value)) - m_assembler.addlImm8r(imm.m_value, result); - else { - m_assembler.loadConstant(imm.m_value, scratchReg3); - m_assembler.addlRegReg(scratchReg3, result); - } - - store32(result, scratchReg); - releaseScratch(result); - } - - void add32(TrustedImm32 imm, AbsoluteAddress address) - { - RegisterID result = claimScratch(); - RegisterID scratchReg = claimScratch(); - - m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); - m_assembler.movlMemReg(scratchReg, result); - - if (m_assembler.isImmediate(imm.m_value)) - m_assembler.addlImm8r(imm.m_value, result); - else { - m_assembler.loadConstant(imm.m_value, scratchReg3); - m_assembler.addlRegReg(scratchReg3, result); - } - - store32(result, scratchReg); - releaseScratch(result); - releaseScratch(scratchReg); - } - - void add64(TrustedImm32 imm, AbsoluteAddress address) - { - RegisterID scr1 = claimScratch(); - RegisterID scr2 = claimScratch(); - - // Add 32-bit LSB first. - m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scr1); - m_assembler.movlMemReg(scr1, scr1); // scr1 = 32-bit LSB of int64 @ address - m_assembler.loadConstant(imm.m_value, scr2); - m_assembler.clrt(); - m_assembler.addclRegReg(scr1, scr2); - m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scr1); - m_assembler.movlRegMem(scr2, scr1); // Update address with 32-bit LSB result. - - // Then add 32-bit MSB. - m_assembler.addlImm8r(4, scr1); - m_assembler.movlMemReg(scr1, scr1); // scr1 = 32-bit MSB of int64 @ address - m_assembler.movt(scr2); - if (imm.m_value < 0) - m_assembler.addlImm8r(-1, scr2); // Sign extend imm value if needed. - m_assembler.addvlRegReg(scr2, scr1); - m_assembler.loadConstant(reinterpret_cast(address.m_ptr) + 4, scr2); - m_assembler.movlRegMem(scr1, scr2); // Update (address + 4) with 32-bit MSB result. - - releaseScratch(scr2); - releaseScratch(scr1); - } - - void sub32(TrustedImm32 imm, RegisterID dest) - { - if (m_assembler.isImmediate(-imm.m_value)) { - m_assembler.addlImm8r(-imm.m_value, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant(imm.m_value, scr); - m_assembler.sublRegReg(scr, dest); - releaseScratch(scr); - } - - void sub32(Address src, RegisterID dest) - { - RegisterID scr = claimScratch(); - load32(src, scr); - m_assembler.sublRegReg(scr, dest); - releaseScratch(scr); - } - - void xor32(RegisterID src, RegisterID dest) - { - m_assembler.xorlRegReg(src, dest); - } - - void xor32(TrustedImm32 imm, RegisterID srcDest) - { - if (imm.m_value == -1) { - m_assembler.notlReg(srcDest, srcDest); - return; - } - - if ((srcDest != SH4Registers::r0) || (imm.m_value > 255) || (imm.m_value < 0)) { - RegisterID scr = claimScratch(); - m_assembler.loadConstant((imm.m_value), scr); - m_assembler.xorlRegReg(scr, srcDest); - releaseScratch(scr); - return; - } - - m_assembler.xorlImm8r(imm.m_value, srcDest); - } - - void compare32(int imm, RegisterID dst, RelationalCondition cond) - { - if (((cond == Equal) || (cond == NotEqual)) && (dst == SH4Registers::r0) && m_assembler.isImmediate(imm)) { - m_assembler.cmpEqImmR0(imm, dst); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant(imm, scr); - m_assembler.cmplRegReg(scr, dst, SH4Condition(cond)); - releaseScratch(scr); - } - - void compare32(int offset, RegisterID base, RegisterID left, RelationalCondition cond) - { - RegisterID scr = claimScratch(); - if (!offset) { - m_assembler.movlMemReg(base, scr); - m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); - releaseScratch(scr); - return; - } - - if ((offset < 0) || (offset >= 64)) { - m_assembler.loadConstant(offset, scr); - m_assembler.addlRegReg(base, scr); - m_assembler.movlMemReg(scr, scr); - m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); - releaseScratch(scr); - return; - } - - m_assembler.movlMemReg(offset >> 2, base, scr); - m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); - releaseScratch(scr); - } - - void testImm(int imm, int offset, RegisterID base) - { - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - - if ((offset < 0) || (offset >= 64)) { - m_assembler.loadConstant(offset, scr); - m_assembler.addlRegReg(base, scr); - m_assembler.movlMemReg(scr, scr); - } else if (offset) - m_assembler.movlMemReg(offset >> 2, base, scr); - else - m_assembler.movlMemReg(base, scr); - if (m_assembler.isImmediate(imm)) - m_assembler.movImm8(imm, scr1); - else - m_assembler.loadConstant(imm, scr1); - - m_assembler.testlRegReg(scr, scr1); - releaseScratch(scr); - releaseScratch(scr1); - } - - void testlImm(int imm, RegisterID dst) - { - if ((dst == SH4Registers::r0) && (imm <= 255) && (imm >= 0)) { - m_assembler.testlImm8r(imm, dst); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant(imm, scr); - m_assembler.testlRegReg(scr, dst); - releaseScratch(scr); - } - - void compare32(RegisterID right, int offset, RegisterID base, RelationalCondition cond) - { - if (!offset) { - RegisterID scr = claimScratch(); - m_assembler.movlMemReg(base, scr); - m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); - releaseScratch(scr); - return; - } - - if ((offset < 0) || (offset >= 64)) { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(offset, scr); - m_assembler.addlRegReg(base, scr); - m_assembler.movlMemReg(scr, scr); - m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); - releaseScratch(scr); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.movlMemReg(offset >> 2, base, scr); - m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); - releaseScratch(scr); - } - - void compare32(int imm, int offset, RegisterID base, RelationalCondition cond) - { - if (!offset) { - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - m_assembler.movlMemReg(base, scr); - m_assembler.loadConstant(imm, scr1); - m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); - releaseScratch(scr1); - releaseScratch(scr); - return; - } - - if ((offset < 0) || (offset >= 64)) { - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - m_assembler.loadConstant(offset, scr); - m_assembler.addlRegReg(base, scr); - m_assembler.movlMemReg(scr, scr); - m_assembler.loadConstant(imm, scr1); - m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); - releaseScratch(scr1); - releaseScratch(scr); - return; - } - - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - m_assembler.movlMemReg(offset >> 2, base, scr); - m_assembler.loadConstant(imm, scr1); - m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); - releaseScratch(scr1); - releaseScratch(scr); - } - - // Memory access operation - - void load32(ImplicitAddress address, RegisterID dest) - { - load32(address.base, address.offset, dest); - } - - void load8(ImplicitAddress address, RegisterID dest) - { - load8(address.base, address.offset, dest); - } - - void load8(BaseIndex address, RegisterID dest) - { - RegisterID scr = claimScratch(); - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - load8(scr, address.offset, dest); - releaseScratch(scr); - } - - void load8Signed(BaseIndex address, RegisterID dest) - { - RegisterID scr = claimScratch(); - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - load8Signed(scr, address.offset, dest); - releaseScratch(scr); - } - - void load32(BaseIndex address, RegisterID dest) - { - RegisterID scr = claimScratch(); - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - load32(scr, address.offset, dest); - releaseScratch(scr); - } - - void load32(const void* address, RegisterID dest) - { - m_assembler.loadConstant(reinterpret_cast(const_cast(address)), dest); - m_assembler.movlMemReg(dest, dest); - } - - void load32(RegisterID base, int offset, RegisterID dest) - { - if (!offset) { - m_assembler.movlMemReg(base, dest); - return; - } - - if ((offset >= 0) && (offset < 64)) { - m_assembler.movlMemReg(offset >> 2, base, dest); - return; - } - - if ((dest == SH4Registers::r0) && (dest != base)) { - m_assembler.loadConstant((offset), dest); - m_assembler.movlR0mr(base, dest); - return; - } - - RegisterID scr; - if (dest == base) - scr = claimScratch(); - else - scr = dest; - m_assembler.loadConstant((offset), scr); - m_assembler.addlRegReg(base, scr); - m_assembler.movlMemReg(scr, dest); - - if (dest == base) - releaseScratch(scr); - } - - void load8Signed(RegisterID base, int offset, RegisterID dest) - { - if (!offset) { - m_assembler.movbMemReg(base, dest); - return; - } - - if ((offset > 0) && (offset < 64) && (dest == SH4Registers::r0)) { - m_assembler.movbMemReg(offset, base, dest); - return; - } - - if (base != dest) { - m_assembler.loadConstant((offset), dest); - m_assembler.addlRegReg(base, dest); - m_assembler.movbMemReg(dest, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant((offset), scr); - m_assembler.addlRegReg(base, scr); - m_assembler.movbMemReg(scr, dest); - releaseScratch(scr); - } - - void load8(RegisterID base, int offset, RegisterID dest) - { - if (!offset) { - m_assembler.movbMemReg(base, dest); - m_assembler.extub(dest, dest); - return; - } - - if ((offset > 0) && (offset < 64) && (dest == SH4Registers::r0)) { - m_assembler.movbMemReg(offset, base, dest); - m_assembler.extub(dest, dest); - return; - } - - if (base != dest) { - m_assembler.loadConstant((offset), dest); - m_assembler.addlRegReg(base, dest); - m_assembler.movbMemReg(dest, dest); - m_assembler.extub(dest, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant((offset), scr); - m_assembler.addlRegReg(base, scr); - m_assembler.movbMemReg(scr, dest); - m_assembler.extub(dest, dest); - releaseScratch(scr); - } - - void load32(RegisterID r0, RegisterID src, RegisterID dst) - { - ASSERT(r0 == SH4Registers::r0); - m_assembler.movlR0mr(src, dst); - } - - void load32(RegisterID src, RegisterID dst) - { - m_assembler.movlMemReg(src, dst); - } - - void load16(ImplicitAddress address, RegisterID dest) - { - if (!address.offset) { - m_assembler.movwMemReg(address.base, dest); - extuw(dest, dest); - return; - } - - if ((address.offset > 0) && (address.offset < 64) && (dest == SH4Registers::r0)) { - m_assembler.movwMemReg(address.offset, address.base, dest); - extuw(dest, dest); - return; - } - - if (address.base != dest) { - m_assembler.loadConstant((address.offset), dest); - m_assembler.addlRegReg(address.base, dest); - m_assembler.movwMemReg(dest, dest); - extuw(dest, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant((address.offset), scr); - m_assembler.addlRegReg(address.base, scr); - m_assembler.movwMemReg(scr, dest); - extuw(dest, dest); - releaseScratch(scr); - } - - void load16Unaligned(BaseIndex address, RegisterID dest) - { - - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - - if (address.offset) - add32(TrustedImm32(address.offset), scr); - - add32(address.base, scr); - load8(scr, scr1); - add32(TrustedImm32(1), scr); - load8(scr, dest); - m_assembler.shllImm8r(8, dest); - or32(scr1, dest); - - releaseScratch(scr); - releaseScratch(scr1); - } - - void load16(RegisterID src, RegisterID dest) - { - m_assembler.movwMemReg(src, dest); - extuw(dest, dest); - } - - void load16Signed(RegisterID src, RegisterID dest) - { - m_assembler.movwMemReg(src, dest); - } - - void load16(RegisterID r0, RegisterID src, RegisterID dest) - { - ASSERT(r0 == SH4Registers::r0); - m_assembler.movwR0mr(src, dest); - extuw(dest, dest); - } - - void load16Signed(RegisterID r0, RegisterID src, RegisterID dest) - { - ASSERT(r0 == SH4Registers::r0); - m_assembler.movwR0mr(src, dest); - } - - void load16(BaseIndex address, RegisterID dest) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - - if (address.offset) - add32(TrustedImm32(address.offset), scr); - if (address.base == SH4Registers::r0) - load16(address.base, scr, dest); - else { - add32(address.base, scr); - load16(scr, dest); - } - - releaseScratch(scr); - } - - void load16Signed(BaseIndex address, RegisterID dest) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - - if (address.offset) - add32(TrustedImm32(address.offset), scr); - if (address.base == SH4Registers::r0) - load16Signed(address.base, scr, dest); - else { - add32(address.base, scr); - load16Signed(scr, dest); - } - - releaseScratch(scr); - } - - void store8(RegisterID src, BaseIndex address) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - - m_assembler.movbRegMem(src, scr); - - releaseScratch(scr); - } - - void store16(RegisterID src, BaseIndex address) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - - m_assembler.movwRegMem(src, scr); - - releaseScratch(scr); - } - - void store32(RegisterID src, ImplicitAddress address) - { - RegisterID scr = claimScratch(); - store32(src, address.offset, address.base, scr); - releaseScratch(scr); - } - - void store32(RegisterID src, int offset, RegisterID base, RegisterID scr) - { - if (!offset) { - m_assembler.movlRegMem(src, base); - return; - } - - if ((offset >=0) && (offset < 64)) { - m_assembler.movlRegMem(src, offset >> 2, base); - return; - } - - m_assembler.loadConstant((offset), scr); - if (scr == SH4Registers::r0) { - m_assembler.movlRegMemr0(src, base); - return; - } - - m_assembler.addlRegReg(base, scr); - m_assembler.movlRegMem(src, scr); - } - - void store32(RegisterID src, RegisterID offset, RegisterID base) - { - ASSERT(offset == SH4Registers::r0); - m_assembler.movlRegMemr0(src, base); - } - - void store32(RegisterID src, RegisterID dst) - { - m_assembler.movlRegMem(src, dst); - } - - void store32(TrustedImm32 imm, ImplicitAddress address) - { - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - m_assembler.loadConstant((imm.m_value), scr); - store32(scr, address.offset, address.base, scr1); - releaseScratch(scr); - releaseScratch(scr1); - } - - void store32(RegisterID src, BaseIndex address) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - store32(src, Address(scr, address.offset)); - - releaseScratch(scr); - } - - void store32(TrustedImm32 imm, void* address) - { - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - m_assembler.loadConstant((imm.m_value), scr); - m_assembler.loadConstant(reinterpret_cast(address), scr1); - m_assembler.movlRegMem(scr, scr1); - releaseScratch(scr); - releaseScratch(scr1); - } - - void store32(RegisterID src, void* address) - { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(reinterpret_cast(address), scr); - m_assembler.movlRegMem(src, scr); - releaseScratch(scr); - } - - DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) - { - RegisterID scr = claimScratch(); - DataLabel32 label(this); - m_assembler.loadConstantUnReusable(address.offset, scr); - m_assembler.addlRegReg(address.base, scr); - m_assembler.movlMemReg(scr, dest); - releaseScratch(scr); - return label; - } - - DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) - { - RegisterID scr = claimScratch(); - DataLabel32 label(this); - m_assembler.loadConstantUnReusable(address.offset, scr); - m_assembler.addlRegReg(address.base, scr); - m_assembler.movlRegMem(src, scr); - releaseScratch(scr); - return label; - } - - DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - DataLabelCompact dataLabel(this); - ASSERT(address.offset <= MaximumCompactPtrAlignedAddressOffset); - ASSERT(address.offset >= 0); - m_assembler.movlMemRegCompact(address.offset >> 2, address.base, dest); - return dataLabel; - } - - ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) - { - ConvertibleLoadLabel result(this); - - RegisterID scr = claimScratch(); - m_assembler.movImm8(address.offset, scr); - m_assembler.addlRegReg(address.base, scr); - m_assembler.movlMemReg(scr, dest); - releaseScratch(scr); - - return result; - } - - // Floating-point operations - - static bool supportsFloatingPoint() { return true; } - static bool supportsFloatingPointTruncate() { return true; } - static bool supportsFloatingPointSqrt() { return true; } - static bool supportsFloatingPointAbs() { return false; } - - void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) - { - m_assembler.fldsfpul((FPRegisterID)(src + 1)); - m_assembler.stsfpulReg(dest1); - m_assembler.fldsfpul(src); - m_assembler.stsfpulReg(dest2); - } - - void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) - { - UNUSED_PARAM(scratch); - m_assembler.ldsrmfpul(src1); - m_assembler.fstsfpul((FPRegisterID)(dest + 1)); - m_assembler.ldsrmfpul(src2); - m_assembler.fstsfpul(dest); - } - - void loadFloat(BaseIndex address, FPRegisterID dest) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - if (address.offset) - add32(TrustedImm32(address.offset), scr); - - m_assembler.fmovsReadrm(scr, dest); - releaseScratch(scr); - } - - void loadDouble(BaseIndex address, FPRegisterID dest) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - if (address.offset) - add32(TrustedImm32(address.offset), scr); - - m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); - m_assembler.fmovsReadrm(scr, dest); - releaseScratch(scr); - } - - void loadDouble(ImplicitAddress address, FPRegisterID dest) - { - RegisterID scr = claimScratch(); - - m_assembler.loadConstant(address.offset, scr); - if (address.base == SH4Registers::r0) { - m_assembler.fmovsReadr0r(scr, (FPRegisterID)(dest + 1)); - m_assembler.addlImm8r(4, scr); - m_assembler.fmovsReadr0r(scr, dest); - releaseScratch(scr); - return; - } - - m_assembler.addlRegReg(address.base, scr); - m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); - m_assembler.fmovsReadrm(scr, dest); - releaseScratch(scr); - } - - void loadDouble(const void* address, FPRegisterID dest) - { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(reinterpret_cast(address), scr); - m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); - m_assembler.fmovsReadrm(scr, dest); - releaseScratch(scr); - } - - void storeFloat(FPRegisterID src, BaseIndex address) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - if (address.offset) - add32(TrustedImm32(address.offset), scr); - - m_assembler.fmovsWriterm(src, scr); - - releaseScratch(scr); - } - - void storeDouble(FPRegisterID src, ImplicitAddress address) - { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(address.offset, scr); - m_assembler.addlRegReg(address.base, scr); - m_assembler.fmovsWriterm((FPRegisterID)(src + 1), scr); - m_assembler.addlImm8r(4, scr); - m_assembler.fmovsWriterm(src, scr); - releaseScratch(scr); - } - - void storeDouble(FPRegisterID src, BaseIndex address) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - if (address.offset) - add32(TrustedImm32(address.offset), scr); - - m_assembler.fmovsWriterm((FPRegisterID)(src + 1), scr); - m_assembler.addlImm8r(4, scr); - m_assembler.fmovsWriterm(src, scr); - - releaseScratch(scr); - } - - void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - if (op1 == dest) - m_assembler.daddRegReg(op2, dest); - else { - m_assembler.dmovRegReg(op1, dest); - m_assembler.daddRegReg(op2, dest); - } - } - - void addDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.daddRegReg(src, dest); - } - - void addDouble(AbsoluteAddress address, FPRegisterID dest) - { - loadDouble(address.m_ptr, fscratch); - addDouble(fscratch, dest); - } - - void addDouble(Address address, FPRegisterID dest) - { - loadDouble(address, fscratch); - addDouble(fscratch, dest); - } - - void subDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.dsubRegReg(src, dest); - } - - void subDouble(Address address, FPRegisterID dest) - { - loadDouble(address, fscratch); - subDouble(fscratch, dest); - } - - void mulDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.dmulRegReg(src, dest); - } - - void mulDouble(Address address, FPRegisterID dest) - { - loadDouble(address, fscratch); - mulDouble(fscratch, dest); - } - - void divDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.ddivRegReg(src, dest); - } - - void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) - { - m_assembler.fldsfpul(src); - m_assembler.dcnvsd(dst); - } - - void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) - { - m_assembler.dcnvds(src); - m_assembler.fstsfpul(dst); - } - - void convertInt32ToDouble(RegisterID src, FPRegisterID dest) - { - m_assembler.ldsrmfpul(src); - m_assembler.floatfpulDreg(dest); - } - - void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) - { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(reinterpret_cast(src.m_ptr), scr); - convertInt32ToDouble(scr, dest); - releaseScratch(scr); - } - - void convertInt32ToDouble(Address src, FPRegisterID dest) - { - RegisterID scr = claimScratch(); - load32(src, scr); - convertInt32ToDouble(scr, dest); - releaseScratch(scr); - } - - void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) - { - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - Jump m_jump; - JumpList end; - - if (dest != SH4Registers::r0) - move(SH4Registers::r0, scr1); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - - if (address.offset) - add32(TrustedImm32(address.offset), scr); - - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 68, sizeof(uint32_t)); - move(scr, SH4Registers::r0); - m_assembler.andlImm8r(0x3, SH4Registers::r0); - m_assembler.cmpEqImmR0(0x0, SH4Registers::r0); - m_jump = Jump(m_assembler.jne(), SH4Assembler::JumpNear); - if (dest != SH4Registers::r0) - move(scr1, SH4Registers::r0); - - load32(scr, dest); - end.append(Jump(m_assembler.bra(), SH4Assembler::JumpNear)); - m_assembler.nop(); - m_jump.link(this); - m_assembler.andlImm8r(0x1, SH4Registers::r0); - m_assembler.cmpEqImmR0(0x0, SH4Registers::r0); - - if (dest != SH4Registers::r0) - move(scr1, SH4Registers::r0); - - m_jump = Jump(m_assembler.jne(), SH4Assembler::JumpNear); - load16(scr, scr1); - add32(TrustedImm32(2), scr); - load16(scr, dest); - m_assembler.shllImm8r(16, dest); - or32(scr1, dest); - end.append(Jump(m_assembler.bra(), SH4Assembler::JumpNear)); - m_assembler.nop(); - m_jump.link(this); - load8(scr, scr1); - add32(TrustedImm32(1), scr); - load16(scr, dest); - m_assembler.shllImm8r(8, dest); - or32(dest, scr1); - add32(TrustedImm32(2), scr); - load8(scr, dest); - m_assembler.shllImm8r(8, dest); - m_assembler.shllImm8r(16, dest); - or32(scr1, dest); - end.link(this); - - releaseScratch(scr); - releaseScratch(scr1); - } - - Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - RegisterID scr = scratchReg3; - load32WithUnalignedHalfWords(left, scr); - if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) - m_assembler.testlRegReg(scr, scr); - else - compare32(right.m_value, scr, cond); - - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) - { - m_assembler.movImm8(0, scratchReg3); - convertInt32ToDouble(scratchReg3, scratch); - return branchDouble(DoubleNotEqual, reg, scratch); - } - - Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) - { - m_assembler.movImm8(0, scratchReg3); - convertInt32ToDouble(scratchReg3, scratch); - return branchDouble(DoubleEqualOrUnordered, reg, scratch); - } - - Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) - { - if (cond == DoubleEqual) { - m_assembler.dcmppeq(right, left); - return branchTrue(); - } - - if (cond == DoubleNotEqual) { - RegisterID scr = claimScratch(); - JumpList end; - m_assembler.loadConstant(0x7fbfffff, scratchReg3); - m_assembler.dcnvds(right); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcnvds(left); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcmppeq(right, left); - releaseScratch(scr); - Jump m_jump = branchFalse(); - end.link(this); - return m_jump; - } - - if (cond == DoubleGreaterThan) { - m_assembler.dcmppgt(right, left); - return branchTrue(); - } - - if (cond == DoubleGreaterThanOrEqual) { - m_assembler.dcmppgt(left, right); - return branchFalse(); - } - - if (cond == DoubleLessThan) { - m_assembler.dcmppgt(left, right); - return branchTrue(); - } - - if (cond == DoubleLessThanOrEqual) { - m_assembler.dcmppgt(right, left); - return branchFalse(); - } - - if (cond == DoubleEqualOrUnordered) { - RegisterID scr = claimScratch(); - JumpList end; - m_assembler.loadConstant(0x7fbfffff, scratchReg3); - m_assembler.dcnvds(right); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcnvds(left); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcmppeq(left, right); - Jump m_jump = Jump(m_assembler.je()); - end.link(this); - m_assembler.extraInstrForBranch(scr); - releaseScratch(scr); - return m_jump; - } - - if (cond == DoubleGreaterThanOrUnordered) { - RegisterID scr = claimScratch(); - JumpList end; - m_assembler.loadConstant(0x7fbfffff, scratchReg3); - m_assembler.dcnvds(right); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcnvds(left); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcmppgt(right, left); - Jump m_jump = Jump(m_assembler.je()); - end.link(this); - m_assembler.extraInstrForBranch(scr); - releaseScratch(scr); - return m_jump; - } - - if (cond == DoubleGreaterThanOrEqualOrUnordered) { - RegisterID scr = claimScratch(); - JumpList end; - m_assembler.loadConstant(0x7fbfffff, scratchReg3); - m_assembler.dcnvds(right); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcnvds(left); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcmppgt(left, right); - Jump m_jump = Jump(m_assembler.jne()); - end.link(this); - m_assembler.extraInstrForBranch(scr); - releaseScratch(scr); - return m_jump; - } - - if (cond == DoubleLessThanOrUnordered) { - RegisterID scr = claimScratch(); - JumpList end; - m_assembler.loadConstant(0x7fbfffff, scratchReg3); - m_assembler.dcnvds(right); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcnvds(left); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcmppgt(left, right); - Jump m_jump = Jump(m_assembler.je()); - end.link(this); - m_assembler.extraInstrForBranch(scr); - releaseScratch(scr); - return m_jump; - } - - if (cond == DoubleLessThanOrEqualOrUnordered) { - RegisterID scr = claimScratch(); - JumpList end; - m_assembler.loadConstant(0x7fbfffff, scratchReg3); - m_assembler.dcnvds(right); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcnvds(left); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcmppgt(right, left); - Jump m_jump = Jump(m_assembler.jne()); - end.link(this); - m_assembler.extraInstrForBranch(scr); - releaseScratch(scr); - return m_jump; - } - - ASSERT(cond == DoubleNotEqualOrUnordered); - RegisterID scr = claimScratch(); - JumpList end; - m_assembler.loadConstant(0x7fbfffff, scratchReg3); - m_assembler.dcnvds(right); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcnvds(left); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcmppeq(right, left); - Jump m_jump = Jump(m_assembler.jne()); - end.link(this); - m_assembler.extraInstrForBranch(scr); - releaseScratch(scr); - return m_jump; - } - - Jump branchTrue() - { - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 6, sizeof(uint32_t)); - Jump m_jump = Jump(m_assembler.je()); - m_assembler.extraInstrForBranch(scratchReg3); - return m_jump; - } - - Jump branchFalse() - { - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 6, sizeof(uint32_t)); - Jump m_jump = Jump(m_assembler.jne()); - m_assembler.extraInstrForBranch(scratchReg3); - return m_jump; - } - - Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - RegisterID scr = claimScratch(); - move(left.index, scr); - lshift32(TrustedImm32(left.scale), scr); - add32(left.base, scr); - load32(scr, left.offset, scr); - compare32(right.m_value, scr, cond); - releaseScratch(scr); - - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - void sqrtDouble(FPRegisterID src, FPRegisterID dest) - { - if (dest != src) - m_assembler.dmovRegReg(src, dest); - m_assembler.dsqrt(dest); - } - - void absDouble(FPRegisterID, FPRegisterID) - { - ASSERT_NOT_REACHED(); - } - - Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - RegisterID addressTempRegister = claimScratch(); - load8(address, addressTempRegister); - Jump jmp = branchTest32(cond, addressTempRegister, mask); - releaseScratch(addressTempRegister); - return jmp; - } - - Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - RegisterID addressTempRegister = claimScratch(); - move(TrustedImmPtr(address.m_ptr), addressTempRegister); - load8(Address(addressTempRegister), addressTempRegister); - Jump jmp = branchTest32(cond, addressTempRegister, mask); - releaseScratch(addressTempRegister); - return jmp; - } - - void signExtend32ToPtr(RegisterID src, RegisterID dest) - { - if (src != dest) - move(src, dest); - } - - Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) - { - RegisterID addressTempRegister = claimScratch(); - load8(left, addressTempRegister); - Jump jmp = branch32(cond, addressTempRegister, right); - releaseScratch(addressTempRegister); - return jmp; - } - - void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) - { - RegisterID addressTempRegister = claimScratch(); - load8(left, addressTempRegister); - compare32(cond, addressTempRegister, right, dest); - releaseScratch(addressTempRegister); - } - - Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) - { - m_assembler.ftrcdrmfpul(src); - m_assembler.stsfpulReg(dest); - m_assembler.loadConstant(0x7fffffff, scratchReg3); - m_assembler.cmplRegReg(dest, scratchReg3, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 14, sizeof(uint32_t)); - m_assembler.branch(BT_OPCODE, 2); - m_assembler.addlImm8r(1, scratchReg3); - m_assembler.cmplRegReg(dest, scratchReg3, SH4Condition(Equal)); - return branchTrue(); - } - - // Stack manipulation operations - - void pop(RegisterID dest) - { - m_assembler.popReg(dest); - } - - void push(RegisterID src) - { - m_assembler.pushReg(src); - } - - void push(Address address) - { - if (!address.offset) { - push(address.base); - return; - } - - if ((address.offset < 0) || (address.offset >= 64)) { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(address.offset, scr); - m_assembler.addlRegReg(address.base, scr); - m_assembler.movlMemReg(scr, SH4Registers::sp); - m_assembler.addlImm8r(-4, SH4Registers::sp); - releaseScratch(scr); - return; - } - - m_assembler.movlMemReg(address.offset >> 2, address.base, SH4Registers::sp); - m_assembler.addlImm8r(-4, SH4Registers::sp); - } - - void push(TrustedImm32 imm) - { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(imm.m_value, scr); - push(scr); - releaseScratch(scr); - } - - // Register move operations - - void move(TrustedImm32 imm, RegisterID dest) - { - m_assembler.loadConstant(imm.m_value, dest); - } - - DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) - { - m_assembler.ensureSpace(m_assembler.maxInstructionSize, sizeof(uint32_t)); - DataLabelPtr dataLabel(this); - m_assembler.loadConstantUnReusable(reinterpret_cast(initialValue.m_value), dest); - return dataLabel; - } - - void move(RegisterID src, RegisterID dest) - { - if (src != dest) - m_assembler.movlRegReg(src, dest); - } - - void move(TrustedImmPtr imm, RegisterID dest) - { - m_assembler.loadConstant(imm.asIntptr(), dest); - } - - void extuw(RegisterID src, RegisterID dst) - { - m_assembler.extuw(src, dst); - } - - void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) - { - m_assembler.cmplRegReg(right, left, SH4Condition(cond)); - if (cond != NotEqual) { - m_assembler.movt(dest); - return; - } - - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 4); - m_assembler.movImm8(0, dest); - m_assembler.branch(BT_OPCODE, 0); - m_assembler.movImm8(1, dest); - } - - void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - if (left != dest) { - move(right, dest); - compare32(cond, left, dest, dest); - return; - } - - RegisterID scr = claimScratch(); - move(right, scr); - compare32(cond, left, scr, dest); - releaseScratch(scr); - } - - void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - ASSERT((cond == Zero) || (cond == NonZero)); - - load8(address, dest); - if (mask.m_value == -1) - compare32(0, dest, static_cast(cond)); - else - testlImm(mask.m_value, dest); - if (cond != NonZero) { - m_assembler.movt(dest); - return; - } - - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 4); - m_assembler.movImm8(0, dest); - m_assembler.branch(BT_OPCODE, 0); - m_assembler.movImm8(1, dest); - } - - void loadPtrLinkReg(ImplicitAddress address) - { - RegisterID scr = claimScratch(); - load32(address, scr); - m_assembler.ldspr(scr); - releaseScratch(scr); - } - - Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) - { - m_assembler.cmplRegReg(right, left, SH4Condition(cond)); - /* BT label => BF off - nop LDR reg - nop braf @reg - nop nop - */ - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) - { - if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) - m_assembler.testlRegReg(left, left); - else - compare32(right.m_value, left, cond); - - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branch32(RelationalCondition cond, RegisterID left, Address right) - { - compare32(right.offset, right.base, left, cond); - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branch32(RelationalCondition cond, Address left, RegisterID right) - { - compare32(right, left.offset, left.base, cond); - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) - { - compare32(right.m_value, left.offset, left.base, cond); - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - RegisterID scr = claimScratch(); - - move(TrustedImm32(reinterpret_cast(left.m_ptr)), scr); - m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); - releaseScratch(scr); - - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) - { - RegisterID addressTempRegister = claimScratch(); - - m_assembler.loadConstant(reinterpret_cast(left.m_ptr), addressTempRegister); - m_assembler.movlMemReg(addressTempRegister, addressTempRegister); - compare32(right.m_value, addressTempRegister, cond); - releaseScratch(addressTempRegister); - - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - ASSERT(!(right.m_value & 0xFFFFFF00)); - RegisterID scr = claimScratch(); - - move(left.index, scr); - lshift32(TrustedImm32(left.scale), scr); - - if (left.offset) - add32(TrustedImm32(left.offset), scr); - add32(left.base, scr); - load8(scr, scr); - RegisterID scr1 = claimScratch(); - m_assembler.loadConstant(right.m_value, scr1); - releaseScratch(scr); - releaseScratch(scr1); - - return branch32(cond, scr, scr1); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) - { - ASSERT((cond == Zero) || (cond == NonZero)); - - m_assembler.testlRegReg(reg, mask); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - ASSERT((cond == Zero) || (cond == NonZero)); - - if (mask.m_value == -1) - m_assembler.testlRegReg(reg, reg); - else - testlImm(mask.m_value, reg); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - ASSERT((cond == Zero) || (cond == NonZero)); - - if (mask.m_value == -1) - compare32(0, address.offset, address.base, static_cast(cond)); - else - testImm(mask.m_value, address.offset, address.base); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - load32(scr, address.offset, scr); - - if (mask.m_value == -1) - m_assembler.testlRegReg(scr, scr); - else - testlImm(mask.m_value, scr); - - releaseScratch(scr); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump jump() - { - return Jump(m_assembler.jmp()); - } - - void jump(RegisterID target) - { - m_assembler.jmpReg(target); - } - - void jump(Address address) - { - RegisterID scr = claimScratch(); - - if ((address.offset < 0) || (address.offset >= 64)) { - m_assembler.loadConstant(address.offset, scr); - m_assembler.addlRegReg(address.base, scr); - m_assembler.movlMemReg(scr, scr); - } else if (address.offset) - m_assembler.movlMemReg(address.offset >> 2, address.base, scr); - else - m_assembler.movlMemReg(address.base, scr); - m_assembler.jmpReg(scr); - - releaseScratch(scr); - } - - // Arithmetic control flow operations - - Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - - if (cond == Overflow) { - m_assembler.addvlRegReg(src, dest); - return branchTrue(); - } - - if (cond == Signed) { - m_assembler.addlRegReg(src, dest); - // Check if dest is negative - m_assembler.cmppz(dest); - return branchFalse(); - } - - m_assembler.addlRegReg(src, dest); - compare32(0, dest, Equal); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - - move(imm, scratchReg3); - return branchAdd32(cond, scratchReg3, dest); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - - if (src != dest) - move(src, dest); - - if (cond == Overflow) { - move(imm, scratchReg3); - m_assembler.addvlRegReg(scratchReg3, dest); - return branchTrue(); - } - - add32(imm, dest); - - if (cond == Signed) { - m_assembler.cmppz(dest); - return branchFalse(); - } - - compare32(0, dest, Equal); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - - if (cond == Overflow) { - RegisterID scr1 = claimScratch(); - RegisterID scr = claimScratch(); - m_assembler.dmullRegReg(src, dest); - m_assembler.stsmacl(dest); - m_assembler.movImm8(-31, scr); - m_assembler.movlRegReg(dest, scr1); - m_assembler.shaRegReg(scr1, scr); - m_assembler.stsmach(scr); - m_assembler.cmplRegReg(scr, scr1, SH4Condition(Equal)); - releaseScratch(scr1); - releaseScratch(scr); - return branchFalse(); - } - - m_assembler.imullRegReg(src, dest); - m_assembler.stsmacl(dest); - if (cond == Signed) { - // Check if dest is negative - m_assembler.cmppz(dest); - return branchFalse(); - } - - compare32(0, dest, static_cast(cond)); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - - move(imm, scratchReg3); - if (src != dest) - move(src, dest); - - return branchMul32(cond, scratchReg3, dest); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - - if (cond == Overflow) { - m_assembler.subvlRegReg(src, dest); - return branchTrue(); - } - - if (cond == Signed) { - // Check if dest is negative - m_assembler.sublRegReg(src, dest); - compare32(0, dest, LessThan); - return branchTrue(); - } - - sub32(src, dest); - compare32(0, dest, static_cast(cond)); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - - move(imm, scratchReg3); - return branchSub32(cond, scratchReg3, dest); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) - { - move(imm, scratchReg3); - if (src != dest) - move(src, dest); - return branchSub32(cond, scratchReg3, dest); - } - - Jump branchSub32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) - { - if (src1 != dest) - move(src1, dest); - return branchSub32(cond, src2, dest); - } - - Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); - - if (cond == Signed) { - or32(src, dest); - compare32(0, dest, static_cast(LessThan)); - return branchTrue(); - } - - or32(src, dest); - compare32(0, dest, static_cast(cond)); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) - { - m_assembler.ftrcdrmfpul(src); - m_assembler.stsfpulReg(dest); - convertInt32ToDouble(dest, fscratch); - failureCases.append(branchDouble(DoubleNotEqualOrUnordered, fscratch, src)); - - if (dest == SH4Registers::r0) - m_assembler.cmpEqImmR0(0, dest); - else { - m_assembler.movImm8(0, scratchReg3); - m_assembler.cmplRegReg(scratchReg3, dest, SH4Condition(Equal)); - } - failureCases.append(branchTrue()); - } - - void neg32(RegisterID dst) - { - m_assembler.neg(dst, dst); - } - - void urshift32(RegisterID shiftamount, RegisterID dest) - { - if (shiftamount == SH4Registers::r0) - m_assembler.andlImm8r(0x1f, shiftamount); - else { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(0x1f, scr); - m_assembler.andlRegReg(scr, shiftamount); - releaseScratch(scr); - } - m_assembler.neg(shiftamount, shiftamount); - m_assembler.shllRegReg(dest, shiftamount); - } - - void urshift32(TrustedImm32 imm, RegisterID dest) - { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(-(imm.m_value & 0x1f), scr); - m_assembler.shaRegReg(dest, scr); - releaseScratch(scr); - } - - void urshift32(RegisterID src, TrustedImm32 shiftamount, RegisterID dest) - { - if (src != dest) - move(src, dest); - - urshift32(shiftamount, dest); - } - - Call call() - { - return Call(m_assembler.call(), Call::Linkable); - } - - Call nearCall() - { - return Call(m_assembler.call(), Call::LinkableNear); - } - - Call call(RegisterID target) - { - return Call(m_assembler.call(target), Call::None); - } - - void call(Address address, RegisterID target) - { - load32(address.base, address.offset, target); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 2); - m_assembler.branch(JSR_OPCODE, target); - m_assembler.nop(); - } - - void breakpoint() - { - m_assembler.bkpt(); - m_assembler.nop(); - } - - Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - RegisterID dataTempRegister = claimScratch(); - - dataLabel = moveWithPatch(initialRightValue, dataTempRegister); - m_assembler.cmplRegReg(dataTempRegister, left, SH4Condition(cond)); - releaseScratch(dataTempRegister); - - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - RegisterID scr = claimScratch(); - - m_assembler.loadConstant(left.offset, scr); - m_assembler.addlRegReg(left.base, scr); - m_assembler.movlMemReg(scr, scr); - RegisterID scr1 = claimScratch(); - dataLabel = moveWithPatch(initialRightValue, scr1); - m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); - releaseScratch(scr); - releaseScratch(scr1); - - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - void ret() - { - m_assembler.ret(); - m_assembler.nop(); - } - - DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) - { - RegisterID scr = claimScratch(); - DataLabelPtr label = moveWithPatch(initialValue, scr); - store32(scr, address); - releaseScratch(scr); - return label; - } - - DataLabelPtr storePtrWithPatch(ImplicitAddress address) { return storePtrWithPatch(TrustedImmPtr(0), address); } - - int sizeOfConstantPool() - { - return m_assembler.sizeOfConstantPool(); - } - - Call tailRecursiveCall() - { - RegisterID scr = claimScratch(); - - m_assembler.loadConstantUnReusable(0x0, scr, true); - Jump m_jump = Jump(m_assembler.jmp(scr)); - releaseScratch(scr); - - return Call::fromTailJump(m_jump); - } - - Call makeTailRecursiveCall(Jump oldJump) - { - oldJump.link(this); - return tailRecursiveCall(); - } - - void nop() - { - m_assembler.nop(); - } - - static FunctionPtr readCallTarget(CodeLocationCall call) - { - return FunctionPtr(reinterpret_cast(SH4Assembler::readCallTarget(call.dataLocation()))); - } - - static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) - { - ASSERT_NOT_REACHED(); - } - - static ptrdiff_t maxJumpReplacementSize() - { - ASSERT_NOT_REACHED(); - return 0; - } - - static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } - - static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) - { - return label.labelAtOffset(0); - } - - static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) - { - SH4Assembler::revertJump(instructionStart.dataLocation(), reinterpret_cast(initialValue) & 0xffff); - } - - static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) - { - UNREACHABLE_FOR_PLATFORM(); - return CodeLocationLabel(); - } - - static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) - { - UNREACHABLE_FOR_PLATFORM(); - } - -protected: - SH4Assembler::Condition SH4Condition(RelationalCondition cond) - { - return static_cast(cond); - } - - SH4Assembler::Condition SH4Condition(ResultCondition cond) - { - return static_cast(cond); - } -private: - friend class LinkBuffer; - friend class RepatchBuffer; - - static void linkCall(void*, Call, FunctionPtr); - static void repatchCall(CodeLocationCall, CodeLocationLabel); - static void repatchCall(CodeLocationCall, FunctionPtr); -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // MacroAssemblerSH4_h diff --git a/masm/assembler/MacroAssemblerX86.h b/masm/assembler/MacroAssemblerX86.h deleted file mode 100644 index 27a030edfd..0000000000 --- a/masm/assembler/MacroAssemblerX86.h +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssemblerX86_h -#define MacroAssemblerX86_h - -#if ENABLE(ASSEMBLER) && CPU(X86) - -#include "MacroAssemblerX86Common.h" - -namespace JSC { - -class MacroAssemblerX86 : public MacroAssemblerX86Common { -public: - static const Scale ScalePtr = TimesFour; - - using MacroAssemblerX86Common::add32; - using MacroAssemblerX86Common::and32; - using MacroAssemblerX86Common::branchAdd32; - using MacroAssemblerX86Common::branchSub32; - using MacroAssemblerX86Common::sub32; - using MacroAssemblerX86Common::or32; - using MacroAssemblerX86Common::load32; - using MacroAssemblerX86Common::store32; - using MacroAssemblerX86Common::store8; - using MacroAssemblerX86Common::branch32; - using MacroAssemblerX86Common::call; - using MacroAssemblerX86Common::jump; - using MacroAssemblerX86Common::addDouble; - using MacroAssemblerX86Common::loadDouble; - using MacroAssemblerX86Common::storeDouble; - using MacroAssemblerX86Common::convertInt32ToDouble; - using MacroAssemblerX86Common::branchTest8; - - void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - m_assembler.leal_mr(imm.m_value, src, dest); - } - - void add32(TrustedImm32 imm, AbsoluteAddress address) - { - m_assembler.addl_im(imm.m_value, address.m_ptr); - } - - void add32(AbsoluteAddress address, RegisterID dest) - { - m_assembler.addl_mr(address.m_ptr, dest); - } - - void add64(TrustedImm32 imm, AbsoluteAddress address) - { - m_assembler.addl_im(imm.m_value, address.m_ptr); - m_assembler.adcl_im(imm.m_value >> 31, reinterpret_cast(address.m_ptr) + sizeof(int32_t)); - } - - void and32(TrustedImm32 imm, AbsoluteAddress address) - { - m_assembler.andl_im(imm.m_value, address.m_ptr); - } - - void or32(TrustedImm32 imm, AbsoluteAddress address) - { - m_assembler.orl_im(imm.m_value, address.m_ptr); - } - - void or32(RegisterID reg, AbsoluteAddress address) - { - m_assembler.orl_rm(reg, address.m_ptr); - } - - void sub32(TrustedImm32 imm, AbsoluteAddress address) - { - m_assembler.subl_im(imm.m_value, address.m_ptr); - } - - void load32(const void* address, RegisterID dest) - { - m_assembler.movl_mr(address, dest); - } - - ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) - { - ConvertibleLoadLabel result = ConvertibleLoadLabel(this); - m_assembler.movl_mr(address.offset, address.base, dest); - return result; - } - - void addDouble(AbsoluteAddress address, FPRegisterID dest) - { - m_assembler.addsd_mr(address.m_ptr, dest); - } - - void storeDouble(FPRegisterID src, const void* address) - { - ASSERT(isSSE2Present()); - m_assembler.movsd_rm(src, address); - } - - void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) - { - m_assembler.cvtsi2sd_mr(src.m_ptr, dest); - } - - void store32(TrustedImm32 imm, void* address) - { - m_assembler.movl_i32m(imm.m_value, address); - } - - void store32(RegisterID src, void* address) - { - m_assembler.movl_rm(src, address); - } - - void store8(TrustedImm32 imm, void* address) - { - ASSERT(-128 <= imm.m_value && imm.m_value < 128); - m_assembler.movb_i8m(imm.m_value, address); - } - - // Possibly clobbers src. - void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) - { - movePackedToInt32(src, dest1); - rshiftPacked(TrustedImm32(32), src); - movePackedToInt32(src, dest2); - } - - void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) - { - moveInt32ToPacked(src1, dest); - moveInt32ToPacked(src2, scratch); - lshiftPacked(TrustedImm32(32), scratch); - orPacked(scratch, dest); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) - { - m_assembler.addl_im(imm.m_value, dest.m_ptr); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) - { - m_assembler.subl_im(imm.m_value, dest.m_ptr); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - m_assembler.cmpl_rm(right, left.m_ptr); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) - { - m_assembler.cmpl_im(right.m_value, left.m_ptr); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Call call() - { - return Call(m_assembler.call(), Call::Linkable); - } - - // Address is a memory location containing the address to jump to - void jump(AbsoluteAddress address) - { - m_assembler.jmp_m(address.m_ptr); - } - - Call tailRecursiveCall() - { - return Call::fromTailJump(jump()); - } - - Call makeTailRecursiveCall(Jump oldJump) - { - return Call::fromTailJump(oldJump); - } - - - DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) - { - padBeforePatch(); - m_assembler.movl_i32r(initialValue.asIntptr(), dest); - return DataLabelPtr(this); - } - - Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - ASSERT(mask.m_value >= -128 && mask.m_value <= 255); - if (mask.m_value == -1) - m_assembler.cmpb_im(0, address.m_ptr); - else - m_assembler.testb_im(mask.m_value, address.m_ptr); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - padBeforePatch(); - m_assembler.cmpl_ir_force32(initialRightValue.asIntptr(), left); - dataLabel = DataLabelPtr(this); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - padBeforePatch(); - m_assembler.cmpl_im_force32(initialRightValue.asIntptr(), left.offset, left.base); - dataLabel = DataLabelPtr(this); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) - { - padBeforePatch(); - m_assembler.movl_i32m(initialValue.asIntptr(), address.offset, address.base); - return DataLabelPtr(this); - } - - static bool supportsFloatingPoint() { return isSSE2Present(); } - // See comment on MacroAssemblerARMv7::supportsFloatingPointTruncate() - static bool supportsFloatingPointTruncate() { return isSSE2Present(); } - static bool supportsFloatingPointSqrt() { return isSSE2Present(); } - static bool supportsFloatingPointAbs() { return isSSE2Present(); } - - static FunctionPtr readCallTarget(CodeLocationCall call) - { - intptr_t offset = reinterpret_cast(call.dataLocation())[-1]; - return FunctionPtr(reinterpret_cast(reinterpret_cast(call.dataLocation()) + offset)); - } - - static bool canJumpReplacePatchableBranchPtrWithPatch() { return true; } - - static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) - { - const int opcodeBytes = 1; - const int modRMBytes = 1; - const int immediateBytes = 4; - const int totalBytes = opcodeBytes + modRMBytes + immediateBytes; - ASSERT(totalBytes >= maxJumpReplacementSize()); - return label.labelAtOffset(-totalBytes); - } - - static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) - { - const int opcodeBytes = 1; - const int modRMBytes = 1; - const int offsetBytes = 0; - const int immediateBytes = 4; - const int totalBytes = opcodeBytes + modRMBytes + offsetBytes + immediateBytes; - ASSERT(totalBytes >= maxJumpReplacementSize()); - return label.labelAtOffset(-totalBytes); - } - - static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID reg, void* initialValue) - { - X86Assembler::revertJumpTo_cmpl_ir_force32(instructionStart.executableAddress(), reinterpret_cast(initialValue), reg); - } - - static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address address, void* initialValue) - { - ASSERT(!address.offset); - X86Assembler::revertJumpTo_cmpl_im_force32(instructionStart.executableAddress(), reinterpret_cast(initialValue), 0, address.base); - } - -private: - friend class LinkBuffer; - friend class RepatchBuffer; - - static void linkCall(void* code, Call call, FunctionPtr function) - { - X86Assembler::linkCall(code, call.m_label, function.value()); - } - - static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) - { - X86Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } - - static void repatchCall(CodeLocationCall call, FunctionPtr destination) - { - X86Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // MacroAssemblerX86_h diff --git a/masm/assembler/MacroAssemblerX86Common.h b/masm/assembler/MacroAssemblerX86Common.h deleted file mode 100644 index 53cb80c210..0000000000 --- a/masm/assembler/MacroAssemblerX86Common.h +++ /dev/null @@ -1,1541 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssemblerX86Common_h -#define MacroAssemblerX86Common_h - -#if ENABLE(ASSEMBLER) - -#include "X86Assembler.h" -#include "AbstractMacroAssembler.h" - -namespace JSC { - -class MacroAssemblerX86Common : public AbstractMacroAssembler { -protected: -#if CPU(X86_64) - static const X86Registers::RegisterID scratchRegister = X86Registers::r11; -#endif - - static const int DoubleConditionBitInvert = 0x10; - static const int DoubleConditionBitSpecial = 0x20; - static const int DoubleConditionBits = DoubleConditionBitInvert | DoubleConditionBitSpecial; - -public: - typedef X86Assembler::FPRegisterID FPRegisterID; - typedef X86Assembler::XMMRegisterID XMMRegisterID; - - static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) - { - return value >= -128 && value <= 127; - } - - enum RelationalCondition { - Equal = X86Assembler::ConditionE, - NotEqual = X86Assembler::ConditionNE, - Above = X86Assembler::ConditionA, - AboveOrEqual = X86Assembler::ConditionAE, - Below = X86Assembler::ConditionB, - BelowOrEqual = X86Assembler::ConditionBE, - GreaterThan = X86Assembler::ConditionG, - GreaterThanOrEqual = X86Assembler::ConditionGE, - LessThan = X86Assembler::ConditionL, - LessThanOrEqual = X86Assembler::ConditionLE - }; - - enum ResultCondition { - Overflow = X86Assembler::ConditionO, - Signed = X86Assembler::ConditionS, - Zero = X86Assembler::ConditionE, - NonZero = X86Assembler::ConditionNE - }; - - enum DoubleCondition { - // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. - DoubleEqual = X86Assembler::ConditionE | DoubleConditionBitSpecial, - DoubleNotEqual = X86Assembler::ConditionNE, - DoubleGreaterThan = X86Assembler::ConditionA, - DoubleGreaterThanOrEqual = X86Assembler::ConditionAE, - DoubleLessThan = X86Assembler::ConditionA | DoubleConditionBitInvert, - DoubleLessThanOrEqual = X86Assembler::ConditionAE | DoubleConditionBitInvert, - // If either operand is NaN, these conditions always evaluate to true. - DoubleEqualOrUnordered = X86Assembler::ConditionE, - DoubleNotEqualOrUnordered = X86Assembler::ConditionNE | DoubleConditionBitSpecial, - DoubleGreaterThanOrUnordered = X86Assembler::ConditionB | DoubleConditionBitInvert, - DoubleGreaterThanOrEqualOrUnordered = X86Assembler::ConditionBE | DoubleConditionBitInvert, - DoubleLessThanOrUnordered = X86Assembler::ConditionB, - DoubleLessThanOrEqualOrUnordered = X86Assembler::ConditionBE, - }; - COMPILE_ASSERT( - !((X86Assembler::ConditionE | X86Assembler::ConditionNE | X86Assembler::ConditionA | X86Assembler::ConditionAE | X86Assembler::ConditionB | X86Assembler::ConditionBE) & DoubleConditionBits), - DoubleConditionBits_should_not_interfere_with_X86Assembler_Condition_codes); - - static const RegisterID stackPointerRegister = X86Registers::esp; - -#if ENABLE(JIT_CONSTANT_BLINDING) - static bool shouldBlindForSpecificArch(uint32_t value) { return value >= 0x00ffffff; } -#if CPU(X86_64) - static bool shouldBlindForSpecificArch(uint64_t value) { return value >= 0x00ffffff; } -#if OS(DARWIN) // On 64-bit systems other than DARWIN uint64_t and uintptr_t are the same type so overload is prohibited. - static bool shouldBlindForSpecificArch(uintptr_t value) { return value >= 0x00ffffff; } -#endif -#endif -#endif - - // Integer arithmetic operations: - // - // Operations are typically two operand - operation(source, srcDst) - // For many operations the source may be an TrustedImm32, the srcDst operand - // may often be a memory location (explictly described using an Address - // object). - - void add32(RegisterID src, RegisterID dest) - { - m_assembler.addl_rr(src, dest); - } - - void add32(TrustedImm32 imm, Address address) - { - m_assembler.addl_im(imm.m_value, address.offset, address.base); - } - - void add32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.addl_ir(imm.m_value, dest); - } - - void add32(Address src, RegisterID dest) - { - m_assembler.addl_mr(src.offset, src.base, dest); - } - - void add32(RegisterID src, Address dest) - { - m_assembler.addl_rm(src, dest.offset, dest.base); - } - - void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - m_assembler.leal_mr(imm.m_value, src, dest); - } - - void and32(RegisterID src, RegisterID dest) - { - m_assembler.andl_rr(src, dest); - } - - void and32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.andl_ir(imm.m_value, dest); - } - - void and32(RegisterID src, Address dest) - { - m_assembler.andl_rm(src, dest.offset, dest.base); - } - - void and32(Address src, RegisterID dest) - { - m_assembler.andl_mr(src.offset, src.base, dest); - } - - void and32(TrustedImm32 imm, Address address) - { - m_assembler.andl_im(imm.m_value, address.offset, address.base); - } - - void and32(RegisterID op1, RegisterID op2, RegisterID dest) - { - if (op1 == op2) - zeroExtend32ToPtr(op1, dest); - else if (op1 == dest) - and32(op2, dest); - else { - move(op2, dest); - and32(op1, dest); - } - } - - void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(src, dest); - and32(imm, dest); - } - - void lshift32(RegisterID shift_amount, RegisterID dest) - { - ASSERT(shift_amount != dest); - - if (shift_amount == X86Registers::ecx) - m_assembler.shll_CLr(dest); - else { - // On x86 we can only shift by ecx; if asked to shift by another register we'll - // need rejig the shift amount into ecx first, and restore the registers afterwards. - // If we dest is ecx, then shift the swapped register! - swap(shift_amount, X86Registers::ecx); - m_assembler.shll_CLr(dest == X86Registers::ecx ? shift_amount : dest); - swap(shift_amount, X86Registers::ecx); - } - } - - void lshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) - { - ASSERT(shift_amount != dest); - - if (src != dest) - move(src, dest); - lshift32(shift_amount, dest); - } - - void lshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.shll_i8r(imm.m_value, dest); - } - - void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - if (src != dest) - move(src, dest); - lshift32(imm, dest); - } - - void mul32(RegisterID src, RegisterID dest) - { - m_assembler.imull_rr(src, dest); - } - - void mul32(Address src, RegisterID dest) - { - m_assembler.imull_mr(src.offset, src.base, dest); - } - - void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - m_assembler.imull_i32r(src, imm.m_value, dest); - } - - void neg32(RegisterID srcDest) - { - m_assembler.negl_r(srcDest); - } - - void neg32(Address srcDest) - { - m_assembler.negl_m(srcDest.offset, srcDest.base); - } - - void or32(RegisterID src, RegisterID dest) - { - m_assembler.orl_rr(src, dest); - } - - void or32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.orl_ir(imm.m_value, dest); - } - - void or32(RegisterID src, Address dest) - { - m_assembler.orl_rm(src, dest.offset, dest.base); - } - - void or32(Address src, RegisterID dest) - { - m_assembler.orl_mr(src.offset, src.base, dest); - } - - void or32(TrustedImm32 imm, Address address) - { - m_assembler.orl_im(imm.m_value, address.offset, address.base); - } - - void or32(RegisterID op1, RegisterID op2, RegisterID dest) - { - if (op1 == op2) - zeroExtend32ToPtr(op1, dest); - else if (op1 == dest) - or32(op2, dest); - else { - move(op2, dest); - or32(op1, dest); - } - } - - void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(src, dest); - or32(imm, dest); - } - - void rshift32(RegisterID shift_amount, RegisterID dest) - { - ASSERT(shift_amount != dest); - - if (shift_amount == X86Registers::ecx) - m_assembler.sarl_CLr(dest); - else { - // On x86 we can only shift by ecx; if asked to shift by another register we'll - // need rejig the shift amount into ecx first, and restore the registers afterwards. - // If we dest is ecx, then shift the swapped register! - swap(shift_amount, X86Registers::ecx); - m_assembler.sarl_CLr(dest == X86Registers::ecx ? shift_amount : dest); - swap(shift_amount, X86Registers::ecx); - } - } - - void rshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) - { - ASSERT(shift_amount != dest); - - if (src != dest) - move(src, dest); - rshift32(shift_amount, dest); - } - - void rshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.sarl_i8r(imm.m_value, dest); - } - - void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - if (src != dest) - move(src, dest); - rshift32(imm, dest); - } - - void urshift32(RegisterID shift_amount, RegisterID dest) - { - ASSERT(shift_amount != dest); - - if (shift_amount == X86Registers::ecx) - m_assembler.shrl_CLr(dest); - else { - // On x86 we can only shift by ecx; if asked to shift by another register we'll - // need rejig the shift amount into ecx first, and restore the registers afterwards. - // If we dest is ecx, then shift the swapped register! - swap(shift_amount, X86Registers::ecx); - m_assembler.shrl_CLr(dest == X86Registers::ecx ? shift_amount : dest); - swap(shift_amount, X86Registers::ecx); - } - } - - void urshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) - { - ASSERT(shift_amount != dest); - - if (src != dest) - move(src, dest); - urshift32(shift_amount, dest); - } - - void urshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.shrl_i8r(imm.m_value, dest); - } - - void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - if (src != dest) - move(src, dest); - urshift32(imm, dest); - } - - void sub32(RegisterID src, RegisterID dest) - { - m_assembler.subl_rr(src, dest); - } - - void sub32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.subl_ir(imm.m_value, dest); - } - - void sub32(TrustedImm32 imm, Address address) - { - m_assembler.subl_im(imm.m_value, address.offset, address.base); - } - - void sub32(Address src, RegisterID dest) - { - m_assembler.subl_mr(src.offset, src.base, dest); - } - - void sub32(RegisterID src, Address dest) - { - m_assembler.subl_rm(src, dest.offset, dest.base); - } - - void xor32(RegisterID src, RegisterID dest) - { - m_assembler.xorl_rr(src, dest); - } - - void xor32(TrustedImm32 imm, Address dest) - { - if (imm.m_value == -1) - m_assembler.notl_m(dest.offset, dest.base); - else - m_assembler.xorl_im(imm.m_value, dest.offset, dest.base); - } - - void xor32(TrustedImm32 imm, RegisterID dest) - { - if (imm.m_value == -1) - m_assembler.notl_r(dest); - else - m_assembler.xorl_ir(imm.m_value, dest); - } - - void xor32(RegisterID src, Address dest) - { - m_assembler.xorl_rm(src, dest.offset, dest.base); - } - - void xor32(Address src, RegisterID dest) - { - m_assembler.xorl_mr(src.offset, src.base, dest); - } - - void xor32(RegisterID op1, RegisterID op2, RegisterID dest) - { - if (op1 == op2) - move(TrustedImm32(0), dest); - else if (op1 == dest) - xor32(op2, dest); - else { - move(op2, dest); - xor32(op1, dest); - } - } - - void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(src, dest); - xor32(imm, dest); - } - - void sqrtDouble(FPRegisterID src, FPRegisterID dst) - { - m_assembler.sqrtsd_rr(src, dst); - } - - void absDouble(FPRegisterID src, FPRegisterID dst) - { - ASSERT(src != dst); - static const double negativeZeroConstant = -0.0; - loadDouble(&negativeZeroConstant, dst); - m_assembler.andnpd_rr(src, dst); - } - - void negateDouble(FPRegisterID src, FPRegisterID dst) - { - ASSERT(src != dst); - static const double negativeZeroConstant = -0.0; - loadDouble(&negativeZeroConstant, dst); - m_assembler.xorpd_rr(src, dst); - } - - - // Memory access operations: - // - // Loads are of the form load(address, destination) and stores of the form - // store(source, address). The source for a store may be an TrustedImm32. Address - // operand objects to loads and store will be implicitly constructed if a - // register is passed. - - void load32(ImplicitAddress address, RegisterID dest) - { - m_assembler.movl_mr(address.offset, address.base, dest); - } - - void load32(BaseIndex address, RegisterID dest) - { - m_assembler.movl_mr(address.offset, address.base, address.index, address.scale, dest); - } - - void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) - { - load32(address, dest); - } - - void load16Unaligned(BaseIndex address, RegisterID dest) - { - load16(address, dest); - } - - DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) - { - padBeforePatch(); - m_assembler.movl_mr_disp32(address.offset, address.base, dest); - return DataLabel32(this); - } - - DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - padBeforePatch(); - m_assembler.movl_mr_disp8(address.offset, address.base, dest); - return DataLabelCompact(this); - } - - static void repatchCompact(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) - { - ASSERT(isCompactPtrAlignedAddressOffset(value)); - AssemblerType_T::repatchCompact(dataLabelCompact.dataLocation(), value); - } - - DataLabelCompact loadCompactWithAddressOffsetPatch(Address address, RegisterID dest) - { - padBeforePatch(); - m_assembler.movl_mr_disp8(address.offset, address.base, dest); - return DataLabelCompact(this); - } - - void load8(BaseIndex address, RegisterID dest) - { - m_assembler.movzbl_mr(address.offset, address.base, address.index, address.scale, dest); - } - - void load8(ImplicitAddress address, RegisterID dest) - { - m_assembler.movzbl_mr(address.offset, address.base, dest); - } - - void load8Signed(BaseIndex address, RegisterID dest) - { - m_assembler.movsbl_mr(address.offset, address.base, address.index, address.scale, dest); - } - - void load8Signed(ImplicitAddress address, RegisterID dest) - { - m_assembler.movsbl_mr(address.offset, address.base, dest); - } - - void load16(BaseIndex address, RegisterID dest) - { - m_assembler.movzwl_mr(address.offset, address.base, address.index, address.scale, dest); - } - - void load16(Address address, RegisterID dest) - { - m_assembler.movzwl_mr(address.offset, address.base, dest); - } - - void load16Signed(BaseIndex address, RegisterID dest) - { - m_assembler.movswl_mr(address.offset, address.base, address.index, address.scale, dest); - } - - void load16Signed(Address address, RegisterID dest) - { - m_assembler.movswl_mr(address.offset, address.base, dest); - } - - DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) - { - padBeforePatch(); - m_assembler.movl_rm_disp32(src, address.offset, address.base); - return DataLabel32(this); - } - - void store32(RegisterID src, ImplicitAddress address) - { - m_assembler.movl_rm(src, address.offset, address.base); - } - - void store32(RegisterID src, BaseIndex address) - { - m_assembler.movl_rm(src, address.offset, address.base, address.index, address.scale); - } - - void store32(TrustedImm32 imm, ImplicitAddress address) - { - m_assembler.movl_i32m(imm.m_value, address.offset, address.base); - } - - void store32(TrustedImm32 imm, BaseIndex address) - { - m_assembler.movl_i32m(imm.m_value, address.offset, address.base, address.index, address.scale); - } - - void store8(TrustedImm32 imm, Address address) - { - ASSERT(-128 <= imm.m_value && imm.m_value < 128); - m_assembler.movb_i8m(imm.m_value, address.offset, address.base); - } - - void store8(TrustedImm32 imm, BaseIndex address) - { - ASSERT(-128 <= imm.m_value && imm.m_value < 128); - m_assembler.movb_i8m(imm.m_value, address.offset, address.base, address.index, address.scale); - } - - void store8(RegisterID src, BaseIndex address) - { -#if CPU(X86) - // On 32-bit x86 we can only store from the first 4 registers; - // esp..edi are mapped to the 'h' registers! - if (src >= 4) { - // Pick a temporary register. - RegisterID temp; - if (address.base != X86Registers::eax && address.index != X86Registers::eax) - temp = X86Registers::eax; - else if (address.base != X86Registers::ebx && address.index != X86Registers::ebx) - temp = X86Registers::ebx; - else { - ASSERT(address.base != X86Registers::ecx && address.index != X86Registers::ecx); - temp = X86Registers::ecx; - } - - // Swap to the temporary register to perform the store. - swap(src, temp); - m_assembler.movb_rm(temp, address.offset, address.base, address.index, address.scale); - swap(src, temp); - return; - } -#endif - m_assembler.movb_rm(src, address.offset, address.base, address.index, address.scale); - } - - void store16(RegisterID src, BaseIndex address) - { -#if CPU(X86) - // On 32-bit x86 we can only store from the first 4 registers; - // esp..edi are mapped to the 'h' registers! - if (src >= 4) { - // Pick a temporary register. - RegisterID temp; - if (address.base != X86Registers::eax && address.index != X86Registers::eax) - temp = X86Registers::eax; - else if (address.base != X86Registers::ebx && address.index != X86Registers::ebx) - temp = X86Registers::ebx; - else { - ASSERT(address.base != X86Registers::ecx && address.index != X86Registers::ecx); - temp = X86Registers::ecx; - } - - // Swap to the temporary register to perform the store. - swap(src, temp); - m_assembler.movw_rm(temp, address.offset, address.base, address.index, address.scale); - swap(src, temp); - return; - } -#endif - m_assembler.movw_rm(src, address.offset, address.base, address.index, address.scale); - } - - - // Floating-point operation: - // - // Presently only supports SSE, not x87 floating point. - - void moveDouble(FPRegisterID src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - if (src != dest) - m_assembler.movsd_rr(src, dest); - } - - void loadDouble(const void* address, FPRegisterID dest) - { -#if CPU(X86) - ASSERT(isSSE2Present()); - m_assembler.movsd_mr(address, dest); -#else - move(TrustedImmPtr(address), scratchRegister); - loadDouble(scratchRegister, dest); -#endif - } - - void loadDouble(ImplicitAddress address, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.movsd_mr(address.offset, address.base, dest); - } - - void loadDouble(BaseIndex address, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.movsd_mr(address.offset, address.base, address.index, address.scale, dest); - } - void loadFloat(BaseIndex address, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.movss_mr(address.offset, address.base, address.index, address.scale, dest); - } - - void storeDouble(FPRegisterID src, ImplicitAddress address) - { - ASSERT(isSSE2Present()); - m_assembler.movsd_rm(src, address.offset, address.base); - } - - void storeDouble(FPRegisterID src, BaseIndex address) - { - ASSERT(isSSE2Present()); - m_assembler.movsd_rm(src, address.offset, address.base, address.index, address.scale); - } - - void storeFloat(FPRegisterID src, BaseIndex address) - { - ASSERT(isSSE2Present()); - m_assembler.movss_rm(src, address.offset, address.base, address.index, address.scale); - } - - void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) - { - ASSERT(isSSE2Present()); - m_assembler.cvtsd2ss_rr(src, dst); - } - - void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) - { - ASSERT(isSSE2Present()); - m_assembler.cvtss2sd_rr(src, dst); - } - - void addDouble(FPRegisterID src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.addsd_rr(src, dest); - } - - void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - if (op1 == dest) - addDouble(op2, dest); - else { - moveDouble(op2, dest); - addDouble(op1, dest); - } - } - - void addDouble(Address src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.addsd_mr(src.offset, src.base, dest); - } - - void divDouble(FPRegisterID src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.divsd_rr(src, dest); - } - - void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - // B := A / B is invalid. - ASSERT(op1 == dest || op2 != dest); - - moveDouble(op1, dest); - divDouble(op2, dest); - } - - void divDouble(Address src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.divsd_mr(src.offset, src.base, dest); - } - - void subDouble(FPRegisterID src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.subsd_rr(src, dest); - } - - void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - // B := A - B is invalid. - ASSERT(op1 == dest || op2 != dest); - - moveDouble(op1, dest); - subDouble(op2, dest); - } - - void subDouble(Address src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.subsd_mr(src.offset, src.base, dest); - } - - void mulDouble(FPRegisterID src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.mulsd_rr(src, dest); - } - - void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - if (op1 == dest) - mulDouble(op2, dest); - else { - moveDouble(op2, dest); - mulDouble(op1, dest); - } - } - - void mulDouble(Address src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.mulsd_mr(src.offset, src.base, dest); - } - - void convertInt32ToDouble(RegisterID src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.cvtsi2sd_rr(src, dest); - } - - void convertInt32ToDouble(Address src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.cvtsi2sd_mr(src.offset, src.base, dest); - } - - Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) - { - ASSERT(isSSE2Present()); - - if (cond & DoubleConditionBitInvert) - m_assembler.ucomisd_rr(left, right); - else - m_assembler.ucomisd_rr(right, left); - - if (cond == DoubleEqual) { - if (left == right) - return Jump(m_assembler.jnp()); - Jump isUnordered(m_assembler.jp()); - Jump result = Jump(m_assembler.je()); - isUnordered.link(this); - return result; - } else if (cond == DoubleNotEqualOrUnordered) { - if (left == right) - return Jump(m_assembler.jp()); - Jump isUnordered(m_assembler.jp()); - Jump isEqual(m_assembler.je()); - isUnordered.link(this); - Jump result = jump(); - isEqual.link(this); - return result; - } - - ASSERT(!(cond & DoubleConditionBitSpecial)); - return Jump(m_assembler.jCC(static_cast(cond & ~DoubleConditionBits))); - } - - // Truncates 'src' to an integer, and places the resulting 'dest'. - // If the result is not representable as a 32 bit value, branch. - // May also branch for some values that are representable in 32 bits - // (specifically, in this case, INT_MIN). - enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; - Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) - { - ASSERT(isSSE2Present()); - m_assembler.cvttsd2si_rr(src, dest); - return branch32(branchType ? NotEqual : Equal, dest, TrustedImm32(0x80000000)); - } - - Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) - { - ASSERT(isSSE2Present()); - m_assembler.cvttsd2si_rr(src, dest); - return branch32(branchType ? GreaterThanOrEqual : LessThan, dest, TrustedImm32(0)); - } - - void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.cvttsd2si_rr(src, dest); - } - -#if CPU(X86_64) - void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.cvttsd2siq_rr(src, dest); - } -#endif - - // Convert 'src' to an integer, and places the resulting 'dest'. - // If the result is not representable as a 32 bit value, branch. - // May also branch for some values that are representable in 32 bits - // (specifically, in this case, 0). - void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) - { - ASSERT(isSSE2Present()); - m_assembler.cvttsd2si_rr(src, dest); - - // If the result is zero, it might have been -0.0, and the double comparison won't catch this! - failureCases.append(branchTest32(Zero, dest)); - - // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. - convertInt32ToDouble(dest, fpTemp); - m_assembler.ucomisd_rr(fpTemp, src); - failureCases.append(m_assembler.jp()); - failureCases.append(m_assembler.jne()); - } - - Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) - { - ASSERT(isSSE2Present()); - m_assembler.xorpd_rr(scratch, scratch); - return branchDouble(DoubleNotEqual, reg, scratch); - } - - Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) - { - ASSERT(isSSE2Present()); - m_assembler.xorpd_rr(scratch, scratch); - return branchDouble(DoubleEqualOrUnordered, reg, scratch); - } - - void lshiftPacked(TrustedImm32 imm, XMMRegisterID reg) - { - ASSERT(isSSE2Present()); - m_assembler.psllq_i8r(imm.m_value, reg); - } - - void rshiftPacked(TrustedImm32 imm, XMMRegisterID reg) - { - ASSERT(isSSE2Present()); - m_assembler.psrlq_i8r(imm.m_value, reg); - } - - void orPacked(XMMRegisterID src, XMMRegisterID dst) - { - ASSERT(isSSE2Present()); - m_assembler.por_rr(src, dst); - } - - void moveInt32ToPacked(RegisterID src, XMMRegisterID dst) - { - ASSERT(isSSE2Present()); - m_assembler.movd_rr(src, dst); - } - - void movePackedToInt32(XMMRegisterID src, RegisterID dst) - { - ASSERT(isSSE2Present()); - m_assembler.movd_rr(src, dst); - } - - // Stack manipulation operations: - // - // The ABI is assumed to provide a stack abstraction to memory, - // containing machine word sized units of data. Push and pop - // operations add and remove a single register sized unit of data - // to or from the stack. Peek and poke operations read or write - // values on the stack, without moving the current stack position. - - void pop(RegisterID dest) - { - m_assembler.pop_r(dest); - } - - void push(RegisterID src) - { - m_assembler.push_r(src); - } - - void push(Address address) - { - m_assembler.push_m(address.offset, address.base); - } - - void push(TrustedImm32 imm) - { - m_assembler.push_i32(imm.m_value); - } - - - // Register move operations: - // - // Move values in registers. - - void move(TrustedImm32 imm, RegisterID dest) - { - // Note: on 64-bit the TrustedImm32 value is zero extended into the register, it - // may be useful to have a separate version that sign extends the value? - if (!imm.m_value) - m_assembler.xorl_rr(dest, dest); - else - m_assembler.movl_i32r(imm.m_value, dest); - } - -#if CPU(X86_64) - void move(RegisterID src, RegisterID dest) - { - // Note: on 64-bit this is is a full register move; perhaps it would be - // useful to have separate move32 & movePtr, with move32 zero extending? - if (src != dest) - m_assembler.movq_rr(src, dest); - } - - void move(TrustedImmPtr imm, RegisterID dest) - { - m_assembler.movq_i64r(imm.asIntptr(), dest); - } - - void move(TrustedImm64 imm, RegisterID dest) - { - m_assembler.movq_i64r(imm.m_value, dest); - } - - void swap(RegisterID reg1, RegisterID reg2) - { - if (reg1 != reg2) - m_assembler.xchgq_rr(reg1, reg2); - } - - void signExtend32ToPtr(RegisterID src, RegisterID dest) - { - m_assembler.movsxd_rr(src, dest); - } - - void zeroExtend32ToPtr(RegisterID src, RegisterID dest) - { - m_assembler.movl_rr(src, dest); - } -#else - void move(RegisterID src, RegisterID dest) - { - if (src != dest) - m_assembler.movl_rr(src, dest); - } - - void move(TrustedImmPtr imm, RegisterID dest) - { - m_assembler.movl_i32r(imm.asIntptr(), dest); - } - - void swap(RegisterID reg1, RegisterID reg2) - { - if (reg1 != reg2) - m_assembler.xchgl_rr(reg1, reg2); - } - - void signExtend32ToPtr(RegisterID src, RegisterID dest) - { - move(src, dest); - } - - void zeroExtend32ToPtr(RegisterID src, RegisterID dest) - { - move(src, dest); - } -#endif - - - // Forwards / external control flow operations: - // - // This set of jump and conditional branch operations return a Jump - // object which may linked at a later point, allow forwards jump, - // or jumps that will require external linkage (after the code has been - // relocated). - // - // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge - // respecitvely, for unsigned comparisons the names b, a, be, and ae are - // used (representing the names 'below' and 'above'). - // - // Operands to the comparision are provided in the expected order, e.g. - // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when - // treated as a signed 32bit value, is less than or equal to 5. - // - // jz and jnz test whether the first operand is equal to zero, and take - // an optional second operand of a mask under which to perform the test. - -public: - Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) - { - m_assembler.cmpb_im(right.m_value, left.offset, left.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) - { - m_assembler.cmpl_rr(right, left); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) - { - if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) - m_assembler.testl_rr(left, left); - else - m_assembler.cmpl_ir(right.m_value, left); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, RegisterID left, Address right) - { - m_assembler.cmpl_mr(right.offset, right.base, left); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, Address left, RegisterID right) - { - m_assembler.cmpl_rm(right, left.offset, left.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) - { - m_assembler.cmpl_im(right.m_value, left.offset, left.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - m_assembler.cmpl_im(right.m_value, left.offset, left.base, left.index, left.scale); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - return branch32(cond, left, right); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) - { - m_assembler.testl_rr(reg, mask); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - // if we are only interested in the low seven bits, this can be tested with a testb - if (mask.m_value == -1) - m_assembler.testl_rr(reg, reg); - else - m_assembler.testl_i32r(mask.m_value, reg); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - if (mask.m_value == -1) - m_assembler.cmpl_im(0, address.offset, address.base); - else - m_assembler.testl_i32m(mask.m_value, address.offset, address.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - if (mask.m_value == -1) - m_assembler.cmpl_im(0, address.offset, address.base, address.index, address.scale); - else - m_assembler.testl_i32m(mask.m_value, address.offset, address.base, address.index, address.scale); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - // Byte in TrustedImm32 is not well defined, so be a little permisive here, but don't accept nonsense values. - ASSERT(mask.m_value >= -128 && mask.m_value <= 255); - if (mask.m_value == -1) - m_assembler.cmpb_im(0, address.offset, address.base); - else - m_assembler.testb_im(mask.m_value, address.offset, address.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest8(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - // Byte in TrustedImm32 is not well defined, so be a little permisive here, but don't accept nonsense values. - ASSERT(mask.m_value >= -128 && mask.m_value <= 255); - if (mask.m_value == -1) - m_assembler.cmpb_im(0, address.offset, address.base, address.index, address.scale); - else - m_assembler.testb_im(mask.m_value, address.offset, address.base, address.index, address.scale); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - ASSERT(!(right.m_value & 0xFFFFFF00)); - - m_assembler.cmpb_im(right.m_value, left.offset, left.base, left.index, left.scale); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump jump() - { - return Jump(m_assembler.jmp()); - } - - void jump(RegisterID target) - { - m_assembler.jmp_r(target); - } - - // Address is a memory location containing the address to jump to - void jump(Address address) - { - m_assembler.jmp_m(address.offset, address.base); - } - - - // Arithmetic control flow operations: - // - // This set of conditional branch operations branch based - // on the result of an arithmetic operation. The operation - // is performed as normal, storing the result. - // - // * jz operations branch if the result is zero. - // * jo operations branch if the (signed) arithmetic - // operation caused an overflow to occur. - - Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) - { - add32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - add32(imm, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 src, Address dest) - { - add32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, Address dest) - { - add32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchAdd32(ResultCondition cond, Address src, RegisterID dest) - { - add32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) - { - if (src1 == dest) - return branchAdd32(cond, src2, dest); - move(src2, dest); - return branchAdd32(cond, src1, dest); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) - { - move(src, dest); - return branchAdd32(cond, imm, dest); - } - - Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) - { - mul32(src, dest); - if (cond != Overflow) - m_assembler.testl_rr(dest, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchMul32(ResultCondition cond, Address src, RegisterID dest) - { - mul32(src, dest); - if (cond != Overflow) - m_assembler.testl_rr(dest, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) - { - mul32(imm, src, dest); - if (cond != Overflow) - m_assembler.testl_rr(dest, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) - { - if (src1 == dest) - return branchMul32(cond, src2, dest); - move(src2, dest); - return branchMul32(cond, src1, dest); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) - { - sub32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - sub32(imm, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub32(ResultCondition cond, TrustedImm32 imm, Address dest) - { - sub32(imm, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, Address dest) - { - sub32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub32(ResultCondition cond, Address src, RegisterID dest) - { - sub32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) - { - // B := A - B is invalid. - ASSERT(src1 == dest || src2 != dest); - - move(src1, dest); - return branchSub32(cond, src2, dest); - } - - Jump branchSub32(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) - { - move(src1, dest); - return branchSub32(cond, src2, dest); - } - - Jump branchNeg32(ResultCondition cond, RegisterID srcDest) - { - neg32(srcDest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) - { - or32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - - // Miscellaneous operations: - - void breakpoint() - { - m_assembler.int3(); - } - - Call nearCall() - { - return Call(m_assembler.call(), Call::LinkableNear); - } - - Call call(RegisterID target) - { - return Call(m_assembler.call(target), Call::None); - } - - void call(Address address) - { - m_assembler.call_m(address.offset, address.base); - } - - void ret() - { - m_assembler.ret(); - } - - void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) - { - m_assembler.cmpb_im(right.m_value, left.offset, left.base); - set32(x86Condition(cond), dest); - } - - void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) - { - m_assembler.cmpl_rr(right, left); - set32(x86Condition(cond), dest); - } - - void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) - m_assembler.testl_rr(left, left); - else - m_assembler.cmpl_ir(right.m_value, left); - set32(x86Condition(cond), dest); - } - - // FIXME: - // The mask should be optional... perhaps the argument order should be - // dest-src, operations always have a dest? ... possibly not true, considering - // asm ops like test, or pseudo ops like pop(). - - void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - if (mask.m_value == -1) - m_assembler.cmpb_im(0, address.offset, address.base); - else - m_assembler.testb_im(mask.m_value, address.offset, address.base); - set32(x86Condition(cond), dest); - } - - void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - if (mask.m_value == -1) - m_assembler.cmpl_im(0, address.offset, address.base); - else - m_assembler.testl_i32m(mask.m_value, address.offset, address.base); - set32(x86Condition(cond), dest); - } - - // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. - static RelationalCondition invert(RelationalCondition cond) - { - return static_cast(cond ^ 1); - } - - void nop() - { - m_assembler.nop(); - } - - static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) - { - X86Assembler::replaceWithJump(instructionStart.executableAddress(), destination.executableAddress()); - } - - static ptrdiff_t maxJumpReplacementSize() - { - return X86Assembler::maxJumpReplacementSize(); - } - -protected: - X86Assembler::Condition x86Condition(RelationalCondition cond) - { - return static_cast(cond); - } - - X86Assembler::Condition x86Condition(ResultCondition cond) - { - return static_cast(cond); - } - - void set32(X86Assembler::Condition cond, RegisterID dest) - { -#if CPU(X86) - // On 32-bit x86 we can only set the first 4 registers; - // esp..edi are mapped to the 'h' registers! - if (dest >= 4) { - m_assembler.xchgl_rr(dest, X86Registers::eax); - m_assembler.setCC_r(cond, X86Registers::eax); - m_assembler.movzbl_rr(X86Registers::eax, X86Registers::eax); - m_assembler.xchgl_rr(dest, X86Registers::eax); - return; - } -#endif - m_assembler.setCC_r(cond, dest); - m_assembler.movzbl_rr(dest, dest); - } - -private: - // Only MacroAssemblerX86 should be using the following method; SSE2 is always available on - // x86_64, and clients & subclasses of MacroAssembler should be using 'supportsFloatingPoint()'. - friend class MacroAssemblerX86; - -#if CPU(X86) -#if OS(MAC_OS_X) - - // All X86 Macs are guaranteed to support at least SSE2, - static bool isSSE2Present() - { - return true; - } - -#else // OS(MAC_OS_X) - - enum SSE2CheckState { - NotCheckedSSE2, - HasSSE2, - NoSSE2 - }; - - static bool isSSE2Present() - { - if (s_sse2CheckState == NotCheckedSSE2) { - // Default the flags value to zero; if the compiler is - // not MSVC or GCC we will read this as SSE2 not present. - int flags = 0; -#if COMPILER(MSVC) - _asm { - mov eax, 1 // cpuid function 1 gives us the standard feature set - cpuid; - mov flags, edx; - } -#elif COMPILER(GCC) - asm ( - "movl $0x1, %%eax;" - "pushl %%ebx;" - "cpuid;" - "popl %%ebx;" - "movl %%edx, %0;" - : "=g" (flags) - : - : "%eax", "%ecx", "%edx" - ); -#endif - static const int SSE2FeatureBit = 1 << 26; - s_sse2CheckState = (flags & SSE2FeatureBit) ? HasSSE2 : NoSSE2; - } - // Only check once. - ASSERT(s_sse2CheckState != NotCheckedSSE2); - - return s_sse2CheckState == HasSSE2; - } - - static SSE2CheckState s_sse2CheckState; - -#endif // OS(MAC_OS_X) -#elif !defined(NDEBUG) // CPU(X86) - - // On x86-64 we should never be checking for SSE2 in a non-debug build, - // but non debug add this method to keep the asserts above happy. - static bool isSSE2Present() - { - return true; - } - -#endif -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // MacroAssemblerX86Common_h diff --git a/masm/assembler/MacroAssemblerX86_64.h b/masm/assembler/MacroAssemblerX86_64.h deleted file mode 100644 index c711e6f8da..0000000000 --- a/masm/assembler/MacroAssemblerX86_64.h +++ /dev/null @@ -1,643 +0,0 @@ -/* - * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssemblerX86_64_h -#define MacroAssemblerX86_64_h - -#if ENABLE(ASSEMBLER) && CPU(X86_64) - -#include "MacroAssemblerX86Common.h" - -#define REPTACH_OFFSET_CALL_R11 3 - -namespace JSC { - -class MacroAssemblerX86_64 : public MacroAssemblerX86Common { -public: - static const Scale ScalePtr = TimesEight; - - using MacroAssemblerX86Common::add32; - using MacroAssemblerX86Common::and32; - using MacroAssemblerX86Common::branchAdd32; - using MacroAssemblerX86Common::or32; - using MacroAssemblerX86Common::sub32; - using MacroAssemblerX86Common::load32; - using MacroAssemblerX86Common::store32; - using MacroAssemblerX86Common::store8; - using MacroAssemblerX86Common::call; - using MacroAssemblerX86Common::jump; - using MacroAssemblerX86Common::addDouble; - using MacroAssemblerX86Common::loadDouble; - using MacroAssemblerX86Common::convertInt32ToDouble; - - void add32(TrustedImm32 imm, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - add32(imm, Address(scratchRegister)); - } - - void and32(TrustedImm32 imm, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - and32(imm, Address(scratchRegister)); - } - - void add32(AbsoluteAddress address, RegisterID dest) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - add32(Address(scratchRegister), dest); - } - - void or32(TrustedImm32 imm, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - or32(imm, Address(scratchRegister)); - } - - void or32(RegisterID reg, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - or32(reg, Address(scratchRegister)); - } - - void sub32(TrustedImm32 imm, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - sub32(imm, Address(scratchRegister)); - } - - void load32(const void* address, RegisterID dest) - { - if (dest == X86Registers::eax) - m_assembler.movl_mEAX(address); - else { - move(TrustedImmPtr(address), dest); - load32(dest, dest); - } - } - - void addDouble(AbsoluteAddress address, FPRegisterID dest) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - m_assembler.addsd_mr(0, scratchRegister, dest); - } - - void convertInt32ToDouble(TrustedImm32 imm, FPRegisterID dest) - { - move(imm, scratchRegister); - m_assembler.cvtsi2sd_rr(scratchRegister, dest); - } - - void store32(TrustedImm32 imm, void* address) - { - move(TrustedImmPtr(address), scratchRegister); - store32(imm, scratchRegister); - } - - void store8(TrustedImm32 imm, void* address) - { - move(TrustedImmPtr(address), scratchRegister); - store8(imm, Address(scratchRegister)); - } - - Call call() - { - DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); - Call result = Call(m_assembler.call(scratchRegister), Call::Linkable); - ASSERT_UNUSED(label, differenceBetween(label, result) == REPTACH_OFFSET_CALL_R11); - return result; - } - - // Address is a memory location containing the address to jump to - void jump(AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - jump(Address(scratchRegister)); - } - - Call tailRecursiveCall() - { - DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); - Jump newJump = Jump(m_assembler.jmp_r(scratchRegister)); - ASSERT_UNUSED(label, differenceBetween(label, newJump) == REPTACH_OFFSET_CALL_R11); - return Call::fromTailJump(newJump); - } - - Call makeTailRecursiveCall(Jump oldJump) - { - oldJump.link(this); - DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); - Jump newJump = Jump(m_assembler.jmp_r(scratchRegister)); - ASSERT_UNUSED(label, differenceBetween(label, newJump) == REPTACH_OFFSET_CALL_R11); - return Call::fromTailJump(newJump); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 src, AbsoluteAddress dest) - { - move(TrustedImmPtr(dest.m_ptr), scratchRegister); - add32(src, Address(scratchRegister)); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - void add64(RegisterID src, RegisterID dest) - { - m_assembler.addq_rr(src, dest); - } - - void add64(Address src, RegisterID dest) - { - m_assembler.addq_mr(src.offset, src.base, dest); - } - - void add64(AbsoluteAddress src, RegisterID dest) - { - move(TrustedImmPtr(src.m_ptr), scratchRegister); - add64(Address(scratchRegister), dest); - } - - void add64(TrustedImm32 imm, RegisterID srcDest) - { - m_assembler.addq_ir(imm.m_value, srcDest); - } - - void add64(TrustedImm64 imm, RegisterID dest) - { - move(imm, scratchRegister); - add64(scratchRegister, dest); - } - - void add64(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - m_assembler.leaq_mr(imm.m_value, src, dest); - } - - void add64(TrustedImm32 imm, Address address) - { - m_assembler.addq_im(imm.m_value, address.offset, address.base); - } - - void add64(TrustedImm32 imm, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - add64(imm, Address(scratchRegister)); - } - - void and64(RegisterID src, RegisterID dest) - { - m_assembler.andq_rr(src, dest); - } - - void and64(TrustedImm32 imm, RegisterID srcDest) - { - m_assembler.andq_ir(imm.m_value, srcDest); - } - - void neg64(RegisterID dest) - { - m_assembler.negq_r(dest); - } - - void or64(RegisterID src, RegisterID dest) - { - m_assembler.orq_rr(src, dest); - } - - void or64(TrustedImm64 imm, RegisterID dest) - { - move(imm, scratchRegister); - or64(scratchRegister, dest); - } - - void or64(TrustedImm32 imm, RegisterID dest) - { - m_assembler.orq_ir(imm.m_value, dest); - } - - void or64(RegisterID op1, RegisterID op2, RegisterID dest) - { - if (op1 == op2) - move(op1, dest); - else if (op1 == dest) - or64(op2, dest); - else { - move(op2, dest); - or64(op1, dest); - } - } - - void or64(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(src, dest); - or64(imm, dest); - } - - void rotateRight64(TrustedImm32 imm, RegisterID srcDst) - { - m_assembler.rorq_i8r(imm.m_value, srcDst); - } - - void sub64(RegisterID src, RegisterID dest) - { - m_assembler.subq_rr(src, dest); - } - - void sub64(TrustedImm32 imm, RegisterID dest) - { - m_assembler.subq_ir(imm.m_value, dest); - } - - void sub64(TrustedImm64 imm, RegisterID dest) - { - move(imm, scratchRegister); - sub64(scratchRegister, dest); - } - - void xor64(RegisterID src, RegisterID dest) - { - m_assembler.xorq_rr(src, dest); - } - - void xor64(RegisterID src, Address dest) - { - m_assembler.xorq_rm(src, dest.offset, dest.base); - } - - void xor64(TrustedImm32 imm, RegisterID srcDest) - { - m_assembler.xorq_ir(imm.m_value, srcDest); - } - - void load64(ImplicitAddress address, RegisterID dest) - { - m_assembler.movq_mr(address.offset, address.base, dest); - } - - void load64(BaseIndex address, RegisterID dest) - { - m_assembler.movq_mr(address.offset, address.base, address.index, address.scale, dest); - } - - void load64(const void* address, RegisterID dest) - { - if (dest == X86Registers::eax) - m_assembler.movq_mEAX(address); - else { - move(TrustedImmPtr(address), dest); - load64(dest, dest); - } - } - - DataLabel32 load64WithAddressOffsetPatch(Address address, RegisterID dest) - { - padBeforePatch(); - m_assembler.movq_mr_disp32(address.offset, address.base, dest); - return DataLabel32(this); - } - - DataLabelCompact load64WithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - padBeforePatch(); - m_assembler.movq_mr_disp8(address.offset, address.base, dest); - return DataLabelCompact(this); - } - - void store64(RegisterID src, ImplicitAddress address) - { - m_assembler.movq_rm(src, address.offset, address.base); - } - - void store64(RegisterID src, BaseIndex address) - { - m_assembler.movq_rm(src, address.offset, address.base, address.index, address.scale); - } - - void store64(RegisterID src, void* address) - { - if (src == X86Registers::eax) - m_assembler.movq_EAXm(address); - else { - move(TrustedImmPtr(address), scratchRegister); - store64(src, scratchRegister); - } - } - - void store64(TrustedImm64 imm, ImplicitAddress address) - { - move(imm, scratchRegister); - store64(scratchRegister, address); - } - - void store64(TrustedImm64 imm, BaseIndex address) - { - move(imm, scratchRegister); - m_assembler.movq_rm(scratchRegister, address.offset, address.base, address.index, address.scale); - } - - DataLabel32 store64WithAddressOffsetPatch(RegisterID src, Address address) - { - padBeforePatch(); - m_assembler.movq_rm_disp32(src, address.offset, address.base); - return DataLabel32(this); - } - - void move64ToDouble(RegisterID src, FPRegisterID dest) - { - m_assembler.movq_rr(src, dest); - } - - void moveDoubleTo64(FPRegisterID src, RegisterID dest) - { - m_assembler.movq_rr(src, dest); - } - - void compare64(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) - m_assembler.testq_rr(left, left); - else - m_assembler.cmpq_ir(right.m_value, left); - m_assembler.setCC_r(x86Condition(cond), dest); - m_assembler.movzbl_rr(dest, dest); - } - - void compare64(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) - { - m_assembler.cmpq_rr(right, left); - m_assembler.setCC_r(x86Condition(cond), dest); - m_assembler.movzbl_rr(dest, dest); - } - - Jump branch64(RelationalCondition cond, RegisterID left, RegisterID right) - { - m_assembler.cmpq_rr(right, left); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch64(RelationalCondition cond, RegisterID left, TrustedImm64 right) - { - if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) { - m_assembler.testq_rr(left, left); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - move(right, scratchRegister); - return branch64(cond, left, scratchRegister); - } - - Jump branch64(RelationalCondition cond, RegisterID left, Address right) - { - m_assembler.cmpq_mr(right.offset, right.base, left); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch64(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - move(TrustedImmPtr(left.m_ptr), scratchRegister); - return branch64(cond, Address(scratchRegister), right); - } - - Jump branch64(RelationalCondition cond, Address left, RegisterID right) - { - m_assembler.cmpq_rm(right, left.offset, left.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch64(RelationalCondition cond, Address left, TrustedImm64 right) - { - move(right, scratchRegister); - return branch64(cond, left, scratchRegister); - } - - Jump branchTest64(ResultCondition cond, RegisterID reg, RegisterID mask) - { - m_assembler.testq_rr(reg, mask); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest64(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - // if we are only interested in the low seven bits, this can be tested with a testb - if (mask.m_value == -1) - m_assembler.testq_rr(reg, reg); - else if ((mask.m_value & ~0x7f) == 0) - m_assembler.testb_i8r(mask.m_value, reg); - else - m_assembler.testq_i32r(mask.m_value, reg); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - void test64(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) - { - if (mask.m_value == -1) - m_assembler.testq_rr(reg, reg); - else if ((mask.m_value & ~0x7f) == 0) - m_assembler.testb_i8r(mask.m_value, reg); - else - m_assembler.testq_i32r(mask.m_value, reg); - set32(x86Condition(cond), dest); - } - - void test64(ResultCondition cond, RegisterID reg, RegisterID mask, RegisterID dest) - { - m_assembler.testq_rr(reg, mask); - set32(x86Condition(cond), dest); - } - - Jump branchTest64(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - load64(address.m_ptr, scratchRegister); - return branchTest64(cond, scratchRegister, mask); - } - - Jump branchTest64(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - if (mask.m_value == -1) - m_assembler.cmpq_im(0, address.offset, address.base); - else - m_assembler.testq_i32m(mask.m_value, address.offset, address.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest64(ResultCondition cond, Address address, RegisterID reg) - { - m_assembler.testq_rm(reg, address.offset, address.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest64(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - if (mask.m_value == -1) - m_assembler.cmpq_im(0, address.offset, address.base, address.index, address.scale); - else - m_assembler.testq_i32m(mask.m_value, address.offset, address.base, address.index, address.scale); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - - Jump branchAdd64(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - add64(imm, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchAdd64(ResultCondition cond, RegisterID src, RegisterID dest) - { - add64(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub64(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - sub64(imm, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub64(ResultCondition cond, RegisterID src, RegisterID dest) - { - sub64(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub64(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) - { - move(src1, dest); - return branchSub64(cond, src2, dest); - } - - ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) - { - ConvertibleLoadLabel result = ConvertibleLoadLabel(this); - m_assembler.movq_mr(address.offset, address.base, dest); - return result; - } - - DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) - { - padBeforePatch(); - m_assembler.movq_i64r(initialValue.asIntptr(), dest); - return DataLabelPtr(this); - } - - Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - dataLabel = moveWithPatch(initialRightValue, scratchRegister); - return branch64(cond, left, scratchRegister); - } - - Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - dataLabel = moveWithPatch(initialRightValue, scratchRegister); - return branch64(cond, left, scratchRegister); - } - - DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) - { - DataLabelPtr label = moveWithPatch(initialValue, scratchRegister); - store64(scratchRegister, address); - return label; - } - - using MacroAssemblerX86Common::branchTest8; - Jump branchTest8(ResultCondition cond, ExtendedAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - TrustedImmPtr addr(reinterpret_cast(address.offset)); - MacroAssemblerX86Common::move(addr, scratchRegister); - return MacroAssemblerX86Common::branchTest8(cond, BaseIndex(scratchRegister, address.base, TimesOne), mask); - } - - Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - MacroAssemblerX86Common::move(TrustedImmPtr(address.m_ptr), scratchRegister); - return MacroAssemblerX86Common::branchTest8(cond, Address(scratchRegister), mask); - } - - static bool supportsFloatingPoint() { return true; } - // See comment on MacroAssemblerARMv7::supportsFloatingPointTruncate() - static bool supportsFloatingPointTruncate() { return true; } - static bool supportsFloatingPointSqrt() { return true; } - static bool supportsFloatingPointAbs() { return true; } - - static FunctionPtr readCallTarget(CodeLocationCall call) - { - return FunctionPtr(X86Assembler::readPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation())); - } - - static RegisterID scratchRegisterForBlinding() { return scratchRegister; } - - static bool canJumpReplacePatchableBranchPtrWithPatch() { return true; } - - static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) - { - const int rexBytes = 1; - const int opcodeBytes = 1; - const int immediateBytes = 8; - const int totalBytes = rexBytes + opcodeBytes + immediateBytes; - ASSERT(totalBytes >= maxJumpReplacementSize()); - return label.labelAtOffset(-totalBytes); - } - - static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) - { - return startOfBranchPtrWithPatchOnRegister(label); - } - - static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) - { - X86Assembler::revertJumpTo_movq_i64r(instructionStart.executableAddress(), reinterpret_cast(initialValue), scratchRegister); - } - - static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) - { - X86Assembler::revertJumpTo_movq_i64r(instructionStart.executableAddress(), reinterpret_cast(initialValue), scratchRegister); - } - -private: - friend class LinkBuffer; - friend class RepatchBuffer; - - static void linkCall(void* code, Call call, FunctionPtr function) - { - if (!call.isFlagSet(Call::Near)) - X86Assembler::linkPointer(code, call.m_label.labelAtOffset(-REPTACH_OFFSET_CALL_R11), function.value()); - else - X86Assembler::linkCall(code, call.m_label, function.value()); - } - - static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) - { - X86Assembler::repatchPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation(), destination.executableAddress()); - } - - static void repatchCall(CodeLocationCall call, FunctionPtr destination) - { - X86Assembler::repatchPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation(), destination.executableAddress()); - } - -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // MacroAssemblerX86_64_h diff --git a/masm/assembler/RepatchBuffer.h b/masm/assembler/RepatchBuffer.h deleted file mode 100644 index dbb56f9ad5..0000000000 --- a/masm/assembler/RepatchBuffer.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef RepatchBuffer_h -#define RepatchBuffer_h - -#if ENABLE(JIT) - -#include "CodeBlock.h" -#include -#include - -namespace JSC { - -// RepatchBuffer: -// -// This class is used to modify code after code generation has been completed, -// and after the code has potentially already been executed. This mechanism is -// used to apply optimizations to the code. -// -class RepatchBuffer { - typedef MacroAssemblerCodePtr CodePtr; - -public: - RepatchBuffer(CodeBlock* codeBlock) - { - JITCode& code = codeBlock->getJITCode(); - m_start = code.start(); - m_size = code.size(); - - ExecutableAllocator::makeWritable(m_start, m_size); - } - - ~RepatchBuffer() - { - ExecutableAllocator::makeExecutable(m_start, m_size); - } - - void relink(CodeLocationJump jump, CodeLocationLabel destination) - { - MacroAssembler::repatchJump(jump, destination); - } - - void relink(CodeLocationCall call, CodeLocationLabel destination) - { - MacroAssembler::repatchCall(call, destination); - } - - void relink(CodeLocationCall call, FunctionPtr destination) - { - MacroAssembler::repatchCall(call, destination); - } - - void relink(CodeLocationNearCall nearCall, CodePtr destination) - { - MacroAssembler::repatchNearCall(nearCall, CodeLocationLabel(destination)); - } - - void relink(CodeLocationNearCall nearCall, CodeLocationLabel destination) - { - MacroAssembler::repatchNearCall(nearCall, destination); - } - - void repatch(CodeLocationDataLabel32 dataLabel32, int32_t value) - { - MacroAssembler::repatchInt32(dataLabel32, value); - } - - void repatch(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) - { - MacroAssembler::repatchCompact(dataLabelCompact, value); - } - - void repatch(CodeLocationDataLabelPtr dataLabelPtr, void* value) - { - MacroAssembler::repatchPointer(dataLabelPtr, value); - } - - void relinkCallerToTrampoline(ReturnAddressPtr returnAddress, CodeLocationLabel label) - { - relink(CodeLocationCall(CodePtr(returnAddress)), label); - } - - void relinkCallerToTrampoline(ReturnAddressPtr returnAddress, CodePtr newCalleeFunction) - { - relinkCallerToTrampoline(returnAddress, CodeLocationLabel(newCalleeFunction)); - } - - void relinkCallerToFunction(ReturnAddressPtr returnAddress, FunctionPtr function) - { - relink(CodeLocationCall(CodePtr(returnAddress)), function); - } - - void relinkNearCallerToTrampoline(ReturnAddressPtr returnAddress, CodeLocationLabel label) - { - relink(CodeLocationNearCall(CodePtr(returnAddress)), label); - } - - void relinkNearCallerToTrampoline(ReturnAddressPtr returnAddress, CodePtr newCalleeFunction) - { - relinkNearCallerToTrampoline(returnAddress, CodeLocationLabel(newCalleeFunction)); - } - - void replaceWithLoad(CodeLocationConvertibleLoad label) - { - MacroAssembler::replaceWithLoad(label); - } - - void replaceWithAddressComputation(CodeLocationConvertibleLoad label) - { - MacroAssembler::replaceWithAddressComputation(label); - } - - void setLoadInstructionIsActive(CodeLocationConvertibleLoad label, bool isActive) - { - if (isActive) - replaceWithLoad(label); - else - replaceWithAddressComputation(label); - } - - static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) - { - return MacroAssembler::startOfBranchPtrWithPatchOnRegister(label); - } - - static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) - { - return MacroAssembler::startOfPatchableBranchPtrWithPatchOnAddress(label); - } - - void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) - { - MacroAssembler::replaceWithJump(instructionStart, destination); - } - - // This is a *bit* of a silly API, since we currently always also repatch the - // immediate after calling this. But I'm fine with that, since this just feels - // less yucky. - void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, MacroAssembler::RegisterID reg, void* value) - { - MacroAssembler::revertJumpReplacementToBranchPtrWithPatch(instructionStart, reg, value); - } - - void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, MacroAssembler::Address address, void* value) - { - MacroAssembler::revertJumpReplacementToPatchableBranchPtrWithPatch(instructionStart, address, value); - } - -private: - void* m_start; - size_t m_size; -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // RepatchBuffer_h diff --git a/masm/assembler/SH4Assembler.h b/masm/assembler/SH4Assembler.h deleted file mode 100644 index 39f5585be1..0000000000 --- a/masm/assembler/SH4Assembler.h +++ /dev/null @@ -1,2152 +0,0 @@ -/* - * Copyright (C) 2009-2011 STMicroelectronics. All rights reserved. - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SH4Assembler_h -#define SH4Assembler_h - -#if ENABLE(ASSEMBLER) && CPU(SH4) - -#include "AssemblerBuffer.h" -#include "AssemblerBufferWithConstantPool.h" -#include "JITCompilationEffort.h" -#include -#include -#include -#include -#include -#include - -#ifndef NDEBUG -#define SH4_ASSEMBLER_TRACING -#endif - -namespace JSC { -typedef uint16_t SH4Word; - -enum { - INVALID_OPCODE = 0xffff, - ADD_OPCODE = 0x300c, - ADDIMM_OPCODE = 0x7000, - ADDC_OPCODE = 0x300e, - ADDV_OPCODE = 0x300f, - AND_OPCODE = 0x2009, - ANDIMM_OPCODE = 0xc900, - DIV0_OPCODE = 0x2007, - DIV1_OPCODE = 0x3004, - BF_OPCODE = 0x8b00, - BFS_OPCODE = 0x8f00, - BRA_OPCODE = 0xa000, - BRAF_OPCODE = 0x0023, - NOP_OPCODE = 0x0009, - BSR_OPCODE = 0xb000, - RTS_OPCODE = 0x000b, - BT_OPCODE = 0x8900, - BTS_OPCODE = 0x8d00, - BSRF_OPCODE = 0x0003, - BRK_OPCODE = 0x003b, - FTRC_OPCODE = 0xf03d, - CMPEQ_OPCODE = 0x3000, - CMPEQIMM_OPCODE = 0x8800, - CMPGE_OPCODE = 0x3003, - CMPGT_OPCODE = 0x3007, - CMPHI_OPCODE = 0x3006, - CMPHS_OPCODE = 0x3002, - CMPPL_OPCODE = 0x4015, - CMPPZ_OPCODE = 0x4011, - CMPSTR_OPCODE = 0x200c, - DT_OPCODE = 0x4010, - FCMPEQ_OPCODE = 0xf004, - FCMPGT_OPCODE = 0xf005, - FMOV_OPCODE = 0xf00c, - FADD_OPCODE = 0xf000, - FMUL_OPCODE = 0xf002, - FSUB_OPCODE = 0xf001, - FDIV_OPCODE = 0xf003, - FNEG_OPCODE = 0xf04d, - JMP_OPCODE = 0x402b, - JSR_OPCODE = 0x400b, - LDSPR_OPCODE = 0x402a, - LDSLPR_OPCODE = 0x4026, - MOV_OPCODE = 0x6003, - MOVIMM_OPCODE = 0xe000, - MOVB_WRITE_RN_OPCODE = 0x2000, - MOVB_WRITE_RNDEC_OPCODE = 0x2004, - MOVB_WRITE_R0RN_OPCODE = 0x0004, - MOVB_WRITE_OFFGBR_OPCODE = 0xc000, - MOVB_WRITE_OFFRN_OPCODE = 0x8000, - MOVB_READ_RM_OPCODE = 0x6000, - MOVB_READ_RMINC_OPCODE = 0x6004, - MOVB_READ_R0RM_OPCODE = 0x000c, - MOVB_READ_OFFGBR_OPCODE = 0xc400, - MOVB_READ_OFFRM_OPCODE = 0x8400, - MOVL_WRITE_RN_OPCODE = 0x2002, - MOVL_WRITE_RNDEC_OPCODE = 0x2006, - MOVL_WRITE_R0RN_OPCODE = 0x0006, - MOVL_WRITE_OFFGBR_OPCODE = 0xc200, - MOVL_WRITE_OFFRN_OPCODE = 0x1000, - MOVL_READ_RM_OPCODE = 0x6002, - MOVL_READ_RMINC_OPCODE = 0x6006, - MOVL_READ_R0RM_OPCODE = 0x000e, - MOVL_READ_OFFGBR_OPCODE = 0xc600, - MOVL_READ_OFFPC_OPCODE = 0xd000, - MOVL_READ_OFFRM_OPCODE = 0x5000, - MOVW_WRITE_RN_OPCODE = 0x2001, - MOVW_READ_RM_OPCODE = 0x6001, - MOVW_READ_R0RM_OPCODE = 0x000d, - MOVW_READ_OFFRM_OPCODE = 0x8500, - MOVW_READ_OFFPC_OPCODE = 0x9000, - MOVA_READ_OFFPC_OPCODE = 0xc700, - MOVT_OPCODE = 0x0029, - MULL_OPCODE = 0x0007, - DMULL_L_OPCODE = 0x3005, - STSMACL_OPCODE = 0x001a, - STSMACH_OPCODE = 0x000a, - DMULSL_OPCODE = 0x300d, - NEG_OPCODE = 0x600b, - NEGC_OPCODE = 0x600a, - NOT_OPCODE = 0x6007, - OR_OPCODE = 0x200b, - ORIMM_OPCODE = 0xcb00, - ORBIMM_OPCODE = 0xcf00, - SETS_OPCODE = 0x0058, - SETT_OPCODE = 0x0018, - SHAD_OPCODE = 0x400c, - SHAL_OPCODE = 0x4020, - SHAR_OPCODE = 0x4021, - SHLD_OPCODE = 0x400d, - SHLL_OPCODE = 0x4000, - SHLL2_OPCODE = 0x4008, - SHLL8_OPCODE = 0x4018, - SHLL16_OPCODE = 0x4028, - SHLR_OPCODE = 0x4001, - SHLR2_OPCODE = 0x4009, - SHLR8_OPCODE = 0x4019, - SHLR16_OPCODE = 0x4029, - STSPR_OPCODE = 0x002a, - STSLPR_OPCODE = 0x4022, - FLOAT_OPCODE = 0xf02d, - SUB_OPCODE = 0x3008, - SUBC_OPCODE = 0x300a, - SUBV_OPCODE = 0x300b, - TST_OPCODE = 0x2008, - TSTIMM_OPCODE = 0xc800, - TSTB_OPCODE = 0xcc00, - EXTUB_OPCODE = 0x600c, - EXTUW_OPCODE = 0x600d, - XOR_OPCODE = 0x200a, - XORIMM_OPCODE = 0xca00, - XORB_OPCODE = 0xce00, - FMOVS_READ_RM_INC_OPCODE = 0xf009, - FMOVS_READ_RM_OPCODE = 0xf008, - FMOVS_READ_R0RM_OPCODE = 0xf006, - FMOVS_WRITE_RN_OPCODE = 0xf00a, - FMOVS_WRITE_RN_DEC_OPCODE = 0xf00b, - FMOVS_WRITE_R0RN_OPCODE = 0xf007, - FCNVDS_DRM_FPUL_OPCODE = 0xf0bd, - FCNVSD_FPUL_DRN_OPCODE = 0xf0ad, - LDS_RM_FPUL_OPCODE = 0x405a, - FLDS_FRM_FPUL_OPCODE = 0xf01d, - STS_FPUL_RN_OPCODE = 0x005a, - FSTS_FPUL_FRN_OPCODE = 0xF00d, - LDSFPSCR_OPCODE = 0x406a, - STSFPSCR_OPCODE = 0x006a, - LDSRMFPUL_OPCODE = 0x405a, - FSTSFPULFRN_OPCODE = 0xf00d, - FSQRT_OPCODE = 0xf06d, - FSCHG_OPCODE = 0xf3fd, - CLRT_OPCODE = 8, -}; - -namespace SH4Registers { -typedef enum { - r0, - r1, - r2, - r3, - r4, - r5, - r6, - r7, - r8, - r9, - r10, - r11, - r12, - r13, - r14, fp = r14, - r15, sp = r15, - pc, - pr, -} RegisterID; - -typedef enum { - fr0, dr0 = fr0, - fr1, - fr2, dr2 = fr2, - fr3, - fr4, dr4 = fr4, - fr5, - fr6, dr6 = fr6, - fr7, - fr8, dr8 = fr8, - fr9, - fr10, dr10 = fr10, - fr11, - fr12, dr12 = fr12, - fr13, - fr14, dr14 = fr14, - fr15, -} FPRegisterID; -} - -inline uint16_t getOpcodeGroup1(uint16_t opc, int rm, int rn) -{ - return (opc | ((rm & 0xf) << 8) | ((rn & 0xf) << 4)); -} - -inline uint16_t getOpcodeGroup2(uint16_t opc, int rm) -{ - return (opc | ((rm & 0xf) << 8)); -} - -inline uint16_t getOpcodeGroup3(uint16_t opc, int rm, int rn) -{ - return (opc | ((rm & 0xf) << 8) | (rn & 0xff)); -} - -inline uint16_t getOpcodeGroup4(uint16_t opc, int rm, int rn, int offset) -{ - return (opc | ((rm & 0xf) << 8) | ((rn & 0xf) << 4) | (offset & 0xf)); -} - -inline uint16_t getOpcodeGroup5(uint16_t opc, int rm) -{ - return (opc | (rm & 0xff)); -} - -inline uint16_t getOpcodeGroup6(uint16_t opc, int rm) -{ - return (opc | (rm & 0xfff)); -} - -inline uint16_t getOpcodeGroup7(uint16_t opc, int rm) -{ - return (opc | ((rm & 0x7) << 9)); -} - -inline uint16_t getOpcodeGroup8(uint16_t opc, int rm, int rn) -{ - return (opc | ((rm & 0x7) << 9) | ((rn & 0x7) << 5)); -} - -inline uint16_t getOpcodeGroup9(uint16_t opc, int rm, int rn) -{ - return (opc | ((rm & 0xf) << 8) | ((rn & 0x7) << 5)); -} - -inline uint16_t getOpcodeGroup10(uint16_t opc, int rm, int rn) -{ - return (opc | ((rm & 0x7) << 9) | ((rn & 0xf) << 4)); -} - -inline uint16_t getOpcodeGroup11(uint16_t opc, int rm, int rn) -{ - return (opc | ((rm & 0xf) << 4) | (rn & 0xf)); -} - -inline uint16_t getRn(uint16_t x) -{ - return ((x & 0xf00) >> 8); -} - -inline uint16_t getRm(uint16_t x) -{ - return ((x & 0xf0) >> 4); -} - -inline uint16_t getDisp(uint16_t x) -{ - return (x & 0xf); -} - -inline uint16_t getImm8(uint16_t x) -{ - return (x & 0xff); -} - -inline uint16_t getImm12(uint16_t x) -{ - return (x & 0xfff); -} - -inline uint16_t getDRn(uint16_t x) -{ - return ((x & 0xe00) >> 9); -} - -inline uint16_t getDRm(uint16_t x) -{ - return ((x & 0xe0) >> 5); -} - -class SH4Assembler { -public: - typedef SH4Registers::RegisterID RegisterID; - typedef SH4Registers::FPRegisterID FPRegisterID; - typedef AssemblerBufferWithConstantPool<512, 4, 2, SH4Assembler> SH4Buffer; - static const RegisterID scratchReg1 = SH4Registers::r3; - static const RegisterID scratchReg2 = SH4Registers::r11; - static const uint32_t maxInstructionSize = 16; - - enum { - padForAlign8 = 0x00, - padForAlign16 = 0x0009, - padForAlign32 = 0x00090009, - }; - - enum JumpType { - JumpFar, - JumpNear - }; - - SH4Assembler() - { - m_claimscratchReg = 0x0; - } - - // SH4 condition codes - typedef enum { - EQ = 0x0, // Equal - NE = 0x1, // Not Equal - HS = 0x2, // Unsigend Greater Than equal - HI = 0x3, // Unsigend Greater Than - LS = 0x4, // Unsigend Lower or Same - LI = 0x5, // Unsigend Lower - GE = 0x6, // Greater or Equal - LT = 0x7, // Less Than - GT = 0x8, // Greater Than - LE = 0x9, // Less or Equal - OF = 0xa, // OverFlow - SI = 0xb, // Signed - EQU= 0xc, // Equal or unordered(NaN) - NEU= 0xd, - GTU= 0xe, - GEU= 0xf, - LTU= 0x10, - LEU= 0x11, - } Condition; - - // Opaque label types -public: - bool isImmediate(int constant) - { - return ((constant <= 127) && (constant >= -128)); - } - - RegisterID claimScratch() - { - ASSERT((m_claimscratchReg != 0x3)); - - if (!(m_claimscratchReg & 0x1)) { - m_claimscratchReg = (m_claimscratchReg | 0x1); - return scratchReg1; - } - - m_claimscratchReg = (m_claimscratchReg | 0x2); - return scratchReg2; - } - - void releaseScratch(RegisterID scratchR) - { - if (scratchR == scratchReg1) - m_claimscratchReg = (m_claimscratchReg & 0x2); - else - m_claimscratchReg = (m_claimscratchReg & 0x1); - } - - // Stack operations - - void pushReg(RegisterID reg) - { - if (reg == SH4Registers::pr) { - oneShortOp(getOpcodeGroup2(STSLPR_OPCODE, SH4Registers::sp)); - return; - } - - oneShortOp(getOpcodeGroup1(MOVL_WRITE_RNDEC_OPCODE, SH4Registers::sp, reg)); - } - - void popReg(RegisterID reg) - { - if (reg == SH4Registers::pr) { - oneShortOp(getOpcodeGroup2(LDSLPR_OPCODE, SH4Registers::sp)); - return; - } - - oneShortOp(getOpcodeGroup1(MOVL_READ_RMINC_OPCODE, reg, SH4Registers::sp)); - } - - void movt(RegisterID dst) - { - uint16_t opc = getOpcodeGroup2(MOVT_OPCODE, dst); - oneShortOp(opc); - } - - // Arithmetic operations - - void addlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(ADD_OPCODE, dst, src); - oneShortOp(opc); - } - - void addclRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(ADDC_OPCODE, dst, src); - oneShortOp(opc); - } - - void addvlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(ADDV_OPCODE, dst, src); - oneShortOp(opc); - } - - void addlImm8r(int imm8, RegisterID dst) - { - ASSERT((imm8 <= 127) && (imm8 >= -128)); - - uint16_t opc = getOpcodeGroup3(ADDIMM_OPCODE, dst, imm8); - oneShortOp(opc); - } - - void andlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(AND_OPCODE, dst, src); - oneShortOp(opc); - } - - void andlImm8r(int imm8, RegisterID dst) - { - ASSERT((imm8 <= 255) && (imm8 >= 0)); - ASSERT(dst == SH4Registers::r0); - - uint16_t opc = getOpcodeGroup5(ANDIMM_OPCODE, imm8); - oneShortOp(opc); - } - - void div1lRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(DIV1_OPCODE, dst, src); - oneShortOp(opc); - } - - void div0lRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(DIV0_OPCODE, dst, src); - oneShortOp(opc); - } - - void notlReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(NOT_OPCODE, dst, src); - oneShortOp(opc); - } - - void orlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(OR_OPCODE, dst, src); - oneShortOp(opc); - } - - void orlImm8r(int imm8, RegisterID dst) - { - ASSERT((imm8 <= 255) && (imm8 >= 0)); - ASSERT(dst == SH4Registers::r0); - - uint16_t opc = getOpcodeGroup5(ORIMM_OPCODE, imm8); - oneShortOp(opc); - } - - void sublRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(SUB_OPCODE, dst, src); - oneShortOp(opc); - } - - void subvlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(SUBV_OPCODE, dst, src); - oneShortOp(opc); - } - - void xorlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(XOR_OPCODE, dst, src); - oneShortOp(opc); - } - - void xorlImm8r(int imm8, RegisterID dst) - { - ASSERT((imm8 <= 255) && (imm8 >= 0)); - ASSERT(dst == SH4Registers::r0); - - uint16_t opc = getOpcodeGroup5(XORIMM_OPCODE, imm8); - oneShortOp(opc); - } - - void shllImm8r(int imm, RegisterID dst) - { - switch (imm) { - case 1: - oneShortOp(getOpcodeGroup2(SHLL_OPCODE, dst)); - break; - case 2: - oneShortOp(getOpcodeGroup2(SHLL2_OPCODE, dst)); - break; - case 8: - oneShortOp(getOpcodeGroup2(SHLL8_OPCODE, dst)); - break; - case 16: - oneShortOp(getOpcodeGroup2(SHLL16_OPCODE, dst)); - break; - default: - ASSERT_NOT_REACHED(); - } - } - - void neg(RegisterID dst, RegisterID src) - { - uint16_t opc = getOpcodeGroup1(NEG_OPCODE, dst, src); - oneShortOp(opc); - } - - void shllRegReg(RegisterID dst, RegisterID rShift) - { - uint16_t opc = getOpcodeGroup1(SHLD_OPCODE, dst, rShift); - oneShortOp(opc); - } - - void shlrRegReg(RegisterID dst, RegisterID rShift) - { - neg(rShift, rShift); - shllRegReg(dst, rShift); - } - - void sharRegReg(RegisterID dst, RegisterID rShift) - { - neg(rShift, rShift); - shaRegReg(dst, rShift); - } - - void shaRegReg(RegisterID dst, RegisterID rShift) - { - uint16_t opc = getOpcodeGroup1(SHAD_OPCODE, dst, rShift); - oneShortOp(opc); - } - - void shlrImm8r(int imm, RegisterID dst) - { - switch (imm) { - case 1: - oneShortOp(getOpcodeGroup2(SHLR_OPCODE, dst)); - break; - case 2: - oneShortOp(getOpcodeGroup2(SHLR2_OPCODE, dst)); - break; - case 8: - oneShortOp(getOpcodeGroup2(SHLR8_OPCODE, dst)); - break; - case 16: - oneShortOp(getOpcodeGroup2(SHLR16_OPCODE, dst)); - break; - default: - ASSERT_NOT_REACHED(); - } - } - - void imullRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MULL_OPCODE, dst, src); - oneShortOp(opc); - } - - void dmullRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(DMULL_L_OPCODE, dst, src); - oneShortOp(opc); - } - - void dmulslRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(DMULSL_OPCODE, dst, src); - oneShortOp(opc); - } - - void stsmacl(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(STSMACL_OPCODE, reg); - oneShortOp(opc); - } - - void stsmach(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(STSMACH_OPCODE, reg); - oneShortOp(opc); - } - - // Comparisons - - void cmplRegReg(RegisterID left, RegisterID right, Condition cond) - { - switch (cond) { - case NE: - oneShortOp(getOpcodeGroup1(CMPEQ_OPCODE, right, left)); - break; - case GT: - oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, right, left)); - break; - case EQ: - oneShortOp(getOpcodeGroup1(CMPEQ_OPCODE, right, left)); - break; - case GE: - oneShortOp(getOpcodeGroup1(CMPGE_OPCODE, right, left)); - break; - case HS: - oneShortOp(getOpcodeGroup1(CMPHS_OPCODE, right, left)); - break; - case HI: - oneShortOp(getOpcodeGroup1(CMPHI_OPCODE, right, left)); - break; - case LI: - oneShortOp(getOpcodeGroup1(CMPHI_OPCODE, left, right)); - break; - case LS: - oneShortOp(getOpcodeGroup1(CMPHS_OPCODE, left, right)); - break; - case LE: - oneShortOp(getOpcodeGroup1(CMPGE_OPCODE, left, right)); - break; - case LT: - oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, left, right)); - break; - default: - ASSERT_NOT_REACHED(); - } - } - - void cmppl(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(CMPPL_OPCODE, reg); - oneShortOp(opc); - } - - void cmppz(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(CMPPZ_OPCODE, reg); - oneShortOp(opc); - } - - void cmpEqImmR0(int imm, RegisterID dst) - { - uint16_t opc = getOpcodeGroup5(CMPEQIMM_OPCODE, imm); - oneShortOp(opc); - } - - void testlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(TST_OPCODE, dst, src); - oneShortOp(opc); - } - - void testlImm8r(int imm, RegisterID dst) - { - ASSERT((dst == SH4Registers::r0) && (imm <= 255) && (imm >= 0)); - - uint16_t opc = getOpcodeGroup5(TSTIMM_OPCODE, imm); - oneShortOp(opc); - } - - void nop() - { - oneShortOp(NOP_OPCODE, false); - } - - void sett() - { - oneShortOp(SETT_OPCODE); - } - - void clrt() - { - oneShortOp(CLRT_OPCODE); - } - - void fschg() - { - oneShortOp(FSCHG_OPCODE); - } - - void bkpt() - { - oneShortOp(BRK_OPCODE, false); - } - - void branch(uint16_t opc, int label) - { - switch (opc) { - case BT_OPCODE: - ASSERT((label <= 127) && (label >= -128)); - oneShortOp(getOpcodeGroup5(BT_OPCODE, label)); - break; - case BRA_OPCODE: - ASSERT((label <= 2047) && (label >= -2048)); - oneShortOp(getOpcodeGroup6(BRA_OPCODE, label)); - break; - case BF_OPCODE: - ASSERT((label <= 127) && (label >= -128)); - oneShortOp(getOpcodeGroup5(BF_OPCODE, label)); - break; - default: - ASSERT_NOT_REACHED(); - } - } - - void branch(uint16_t opc, RegisterID reg) - { - switch (opc) { - case BRAF_OPCODE: - oneShortOp(getOpcodeGroup2(BRAF_OPCODE, reg)); - break; - case JMP_OPCODE: - oneShortOp(getOpcodeGroup2(JMP_OPCODE, reg)); - break; - case JSR_OPCODE: - oneShortOp(getOpcodeGroup2(JSR_OPCODE, reg)); - break; - case BSRF_OPCODE: - oneShortOp(getOpcodeGroup2(BSRF_OPCODE, reg)); - break; - default: - ASSERT_NOT_REACHED(); - } - } - - void ldspr(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(LDSPR_OPCODE, reg); - oneShortOp(opc); - } - - void stspr(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(STSPR_OPCODE, reg); - oneShortOp(opc); - } - - void extub(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(EXTUB_OPCODE, dst, src); - oneShortOp(opc); - } - - void extuw(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(EXTUW_OPCODE, dst, src); - oneShortOp(opc); - } - - // float operations - - void ldsrmfpul(RegisterID src) - { - uint16_t opc = getOpcodeGroup2(LDS_RM_FPUL_OPCODE, src); - oneShortOp(opc); - } - - void fneg(FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup2(FNEG_OPCODE, dst); - oneShortOp(opc, true, false); - } - - void fsqrt(FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup2(FSQRT_OPCODE, dst); - oneShortOp(opc, true, false); - } - - void stsfpulReg(RegisterID src) - { - uint16_t opc = getOpcodeGroup2(STS_FPUL_RN_OPCODE, src); - oneShortOp(opc); - } - - void floatfpulfrn(FPRegisterID src) - { - uint16_t opc = getOpcodeGroup2(FLOAT_OPCODE, src); - oneShortOp(opc, true, false); - } - - void fmull(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup1(FMUL_OPCODE, dst, src); - oneShortOp(opc, true, false); - } - - void fmovsReadrm(RegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_OPCODE, dst, src); - oneShortOp(opc, true, false); - } - - void fmovsWriterm(FPRegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_RN_OPCODE, dst, src); - oneShortOp(opc, true, false); - } - - void fmovsWriter0r(FPRegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_R0RN_OPCODE, dst, src); - oneShortOp(opc, true, false); - } - - void fmovsReadr0r(RegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup1(FMOVS_READ_R0RM_OPCODE, dst, src); - oneShortOp(opc, true, false); - } - - void fmovsReadrminc(RegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_INC_OPCODE, dst, src); - oneShortOp(opc, true, false); - } - - void fmovsWriterndec(FPRegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_RN_DEC_OPCODE, dst, src); - oneShortOp(opc, true, false); - } - - void ftrcRegfpul(FPRegisterID src) - { - uint16_t opc = getOpcodeGroup2(FTRC_OPCODE, src); - oneShortOp(opc, true, false); - } - - void fldsfpul(FPRegisterID src) - { - uint16_t opc = getOpcodeGroup2(FLDS_FRM_FPUL_OPCODE, src); - oneShortOp(opc); - } - - void fstsfpul(FPRegisterID src) - { - uint16_t opc = getOpcodeGroup2(FSTS_FPUL_FRN_OPCODE, src); - oneShortOp(opc); - } - - void ldsfpscr(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(LDSFPSCR_OPCODE, reg); - oneShortOp(opc); - } - - void stsfpscr(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(STSFPSCR_OPCODE, reg); - oneShortOp(opc); - } - - // double operations - - void dcnvds(FPRegisterID src) - { - uint16_t opc = getOpcodeGroup7(FCNVDS_DRM_FPUL_OPCODE, src >> 1); - oneShortOp(opc); - } - - void dcnvsd(FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup7(FCNVSD_FPUL_DRN_OPCODE, dst >> 1); - oneShortOp(opc); - } - - void dcmppeq(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup8(FCMPEQ_OPCODE, dst >> 1, src >> 1); - oneShortOp(opc); - } - - void dcmppgt(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup8(FCMPGT_OPCODE, dst >> 1, src >> 1); - oneShortOp(opc); - } - - void dmulRegReg(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup8(FMUL_OPCODE, dst >> 1, src >> 1); - oneShortOp(opc); - } - - void dsubRegReg(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup8(FSUB_OPCODE, dst >> 1, src >> 1); - oneShortOp(opc); - } - - void daddRegReg(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup8(FADD_OPCODE, dst >> 1, src >> 1); - oneShortOp(opc); - } - - void dmovRegReg(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup8(FMOV_OPCODE, dst >> 1, src >> 1); - oneShortOp(opc); - } - - void ddivRegReg(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup8(FDIV_OPCODE, dst >> 1, src >> 1); - oneShortOp(opc); - } - - void dsqrt(FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup7(FSQRT_OPCODE, dst >> 1); - oneShortOp(opc); - } - - void dneg(FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup7(FNEG_OPCODE, dst >> 1); - oneShortOp(opc); - } - - void fmovReadrm(RegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup10(FMOVS_READ_RM_OPCODE, dst >> 1, src); - oneShortOp(opc); - } - - void fmovWriterm(FPRegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_RN_OPCODE, dst, src >> 1); - oneShortOp(opc); - } - - void fmovWriter0r(FPRegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_R0RN_OPCODE, dst, src >> 1); - oneShortOp(opc); - } - - void fmovReadr0r(RegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup10(FMOVS_READ_R0RM_OPCODE, dst >> 1, src); - oneShortOp(opc); - } - - void fmovReadrminc(RegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup10(FMOVS_READ_RM_INC_OPCODE, dst >> 1, src); - oneShortOp(opc); - } - - void fmovWriterndec(FPRegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_RN_DEC_OPCODE, dst, src >> 1); - oneShortOp(opc); - } - - void floatfpulDreg(FPRegisterID src) - { - uint16_t opc = getOpcodeGroup7(FLOAT_OPCODE, src >> 1); - oneShortOp(opc); - } - - void ftrcdrmfpul(FPRegisterID src) - { - uint16_t opc = getOpcodeGroup7(FTRC_OPCODE, src >> 1); - oneShortOp(opc); - } - - // Various move ops - - void movImm8(int imm8, RegisterID dst) - { - ASSERT((imm8 <= 127) && (imm8 >= -128)); - - uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, imm8); - oneShortOp(opc); - } - - void movlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOV_OPCODE, dst, src); - oneShortOp(opc); - } - - void movwRegMem(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVW_WRITE_RN_OPCODE, dst, src); - oneShortOp(opc); - } - - void movwMemReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVW_READ_RM_OPCODE, dst, src); - oneShortOp(opc); - } - - void movwPCReg(int offset, RegisterID base, RegisterID dst) - { - ASSERT(base == SH4Registers::pc); - ASSERT((offset <= 255) && (offset >= 0)); - - uint16_t opc = getOpcodeGroup3(MOVW_READ_OFFPC_OPCODE, dst, offset); - oneShortOp(opc); - } - - void movwMemReg(int offset, RegisterID base, RegisterID dst) - { - ASSERT(dst == SH4Registers::r0); - - uint16_t opc = getOpcodeGroup11(MOVW_READ_OFFRM_OPCODE, base, offset); - oneShortOp(opc); - } - - void movwR0mr(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVW_READ_R0RM_OPCODE, dst, src); - oneShortOp(opc); - } - - void movlRegMem(RegisterID src, int offset, RegisterID base) - { - ASSERT((offset <= 15) && (offset >= 0)); - - if (!offset) { - oneShortOp(getOpcodeGroup1(MOVL_WRITE_RN_OPCODE, base, src)); - return; - } - - oneShortOp(getOpcodeGroup4(MOVL_WRITE_OFFRN_OPCODE, base, src, offset)); - } - - void movlRegMem(RegisterID src, RegisterID base) - { - uint16_t opc = getOpcodeGroup1(MOVL_WRITE_RN_OPCODE, base, src); - oneShortOp(opc); - } - - void movlMemReg(int offset, RegisterID base, RegisterID dst) - { - if (base == SH4Registers::pc) { - ASSERT((offset <= 255) && (offset >= 0)); - oneShortOp(getOpcodeGroup3(MOVL_READ_OFFPC_OPCODE, dst, offset)); - return; - } - - ASSERT((offset <= 15) && (offset >= 0)); - if (!offset) { - oneShortOp(getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base)); - return; - } - - oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset)); - } - - void movlMemRegCompact(int offset, RegisterID base, RegisterID dst) - { - oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset)); - } - - void movbRegMem(RegisterID src, RegisterID base) - { - uint16_t opc = getOpcodeGroup1(MOVB_WRITE_RN_OPCODE, base, src); - oneShortOp(opc); - } - - void movbMemReg(int offset, RegisterID base, RegisterID dst) - { - ASSERT(dst == SH4Registers::r0); - - uint16_t opc = getOpcodeGroup11(MOVB_READ_OFFRM_OPCODE, base, offset); - oneShortOp(opc); - } - - void movbR0mr(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVB_READ_R0RM_OPCODE, dst, src); - oneShortOp(opc); - } - - void movbMemReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVB_READ_RM_OPCODE, dst, src); - oneShortOp(opc); - } - - void movlMemReg(RegisterID base, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base); - oneShortOp(opc); - } - - void movlMemRegIn(RegisterID base, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVL_READ_RMINC_OPCODE, dst, base); - oneShortOp(opc); - } - - void movlR0mr(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVL_READ_R0RM_OPCODE, dst, src); - oneShortOp(opc); - } - - void movlRegMemr0(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVL_WRITE_R0RN_OPCODE, dst, src); - oneShortOp(opc); - } - - void movlImm8r(int imm8, RegisterID dst) - { - ASSERT((imm8 <= 127) && (imm8 >= -128)); - - uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, imm8); - oneShortOp(opc); - } - - void loadConstant(uint32_t constant, RegisterID dst) - { - if (((int)constant <= 0x7f) && ((int)constant >= -0x80)) { - movImm8(constant, dst); - return; - } - - uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, 0); - - m_buffer.ensureSpace(maxInstructionSize, sizeof(uint32_t)); - printInstr(getOpcodeGroup3(MOVIMM_OPCODE, dst, constant), m_buffer.codeSize()); - m_buffer.putShortWithConstantInt(opc, constant, true); - } - - void loadConstantUnReusable(uint32_t constant, RegisterID dst, bool ensureSpace = false) - { - uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, 0); - - if (ensureSpace) - m_buffer.ensureSpace(maxInstructionSize, sizeof(uint32_t)); - - printInstr(getOpcodeGroup3(MOVIMM_OPCODE, dst, constant), m_buffer.codeSize()); - m_buffer.putShortWithConstantInt(opc, constant); - } - - // Flow control - - AssemblerLabel call() - { - RegisterID scr = claimScratch(); - m_buffer.ensureSpace(maxInstructionSize + 4, sizeof(uint32_t)); - loadConstantUnReusable(0x0, scr); - branch(JSR_OPCODE, scr); - nop(); - releaseScratch(scr); - return m_buffer.label(); - } - - AssemblerLabel call(RegisterID dst) - { - m_buffer.ensureSpace(maxInstructionSize + 2); - branch(JSR_OPCODE, dst); - nop(); - return m_buffer.label(); - } - - AssemblerLabel jmp() - { - RegisterID scr = claimScratch(); - m_buffer.ensureSpace(maxInstructionSize + 4, sizeof(uint32_t)); - AssemblerLabel label = m_buffer.label(); - loadConstantUnReusable(0x0, scr); - branch(BRAF_OPCODE, scr); - nop(); - releaseScratch(scr); - return label; - } - - void extraInstrForBranch(RegisterID dst) - { - loadConstantUnReusable(0x0, dst); - nop(); - nop(); - } - - AssemblerLabel jmp(RegisterID dst) - { - jmpReg(dst); - return m_buffer.label(); - } - - void jmpReg(RegisterID dst) - { - m_buffer.ensureSpace(maxInstructionSize + 2); - branch(JMP_OPCODE, dst); - nop(); - } - - AssemblerLabel jne() - { - AssemblerLabel label = m_buffer.label(); - branch(BF_OPCODE, 0); - return label; - } - - AssemblerLabel je() - { - AssemblerLabel label = m_buffer.label(); - branch(BT_OPCODE, 0); - return label; - } - - AssemblerLabel bra() - { - AssemblerLabel label = m_buffer.label(); - branch(BRA_OPCODE, 0); - return label; - } - - void ret() - { - m_buffer.ensureSpace(maxInstructionSize + 2); - oneShortOp(RTS_OPCODE, false); - } - - AssemblerLabel labelIgnoringWatchpoints() - { - m_buffer.ensureSpaceForAnyInstruction(); - return m_buffer.label(); - } - - AssemblerLabel label() - { - m_buffer.ensureSpaceForAnyInstruction(); - return m_buffer.label(); - } - - int sizeOfConstantPool() - { - return m_buffer.sizeOfConstantPool(); - } - - AssemblerLabel align(int alignment) - { - m_buffer.ensureSpace(maxInstructionSize + 2); - while (!m_buffer.isAligned(alignment)) { - nop(); - m_buffer.ensureSpace(maxInstructionSize + 2); - } - return label(); - } - - static void changePCrelativeAddress(int offset, uint16_t* instructionPtr, uint32_t newAddress) - { - uint32_t address = (offset << 2) + ((reinterpret_cast(instructionPtr) + 4) &(~0x3)); - *reinterpret_cast(address) = newAddress; - } - - static uint32_t readPCrelativeAddress(int offset, uint16_t* instructionPtr) - { - uint32_t address = (offset << 2) + ((reinterpret_cast(instructionPtr) + 4) &(~0x3)); - return *reinterpret_cast(address); - } - - static uint16_t* getInstructionPtr(void* code, int offset) - { - return reinterpret_cast (reinterpret_cast(code) + offset); - } - - static void linkJump(void* code, AssemblerLabel from, void* to) - { - ASSERT(from.isSet()); - - uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset); - uint16_t instruction = *instructionPtr; - int offsetBits = (reinterpret_cast(to) - reinterpret_cast(code)) - from.m_offset; - - if (((instruction & 0xff00) == BT_OPCODE) || ((instruction & 0xff00) == BF_OPCODE)) { - /* BT label ==> BF 2 - nop LDR reg - nop braf @reg - nop nop - */ - offsetBits -= 8; - instruction ^= 0x0202; - *instructionPtr++ = instruction; - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); - instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); - *instructionPtr = instruction; - printBlockInstr(instructionPtr - 2, from.m_offset, 3); - return; - } - - /* MOV #imm, reg => LDR reg - braf @reg braf @reg - nop nop - */ - ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); - - offsetBits -= 4; - if (offsetBits >= -4096 && offsetBits <= 4094) { - *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); - *(++instructionPtr) = NOP_OPCODE; - printBlockInstr(instructionPtr - 1, from.m_offset, 2); - return; - } - - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); - printInstr(*instructionPtr, from.m_offset + 2); - } - - static void linkCall(void* code, AssemblerLabel from, void* to) - { - uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset); - instructionPtr -= 3; - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(to)); - } - - static void linkPointer(void* code, AssemblerLabel where, void* value) - { - uint16_t* instructionPtr = getInstructionPtr(code, where.m_offset); - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(value)); - } - - static unsigned getCallReturnOffset(AssemblerLabel call) - { - ASSERT(call.isSet()); - return call.m_offset; - } - - static uint32_t* getLdrImmAddressOnPool(SH4Word* insn, uint32_t* constPool) - { - return (constPool + (*insn & 0xff)); - } - - static SH4Word patchConstantPoolLoad(SH4Word load, int value) - { - return ((load & ~0xff) | value); - } - - static SH4Buffer::TwoShorts placeConstantPoolBarrier(int offset) - { - ASSERT(((offset >> 1) <=2047) && ((offset >> 1) >= -2048)); - - SH4Buffer::TwoShorts m_barrier; - m_barrier.high = (BRA_OPCODE | (offset >> 1)); - m_barrier.low = NOP_OPCODE; - printInstr(((BRA_OPCODE | (offset >> 1))), 0); - printInstr(NOP_OPCODE, 0); - return m_barrier; - } - - static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr) - { - SH4Word* instructionPtr = reinterpret_cast(loadAddr); - SH4Word instruction = *instructionPtr; - SH4Word index = instruction & 0xff; - - if ((instruction & 0xf000) != MOVIMM_OPCODE) - return; - - ASSERT((((reinterpret_cast(constPoolAddr) - reinterpret_cast(loadAddr)) + index * 4)) < 1024); - - int offset = reinterpret_cast(constPoolAddr) + (index * 4) - ((reinterpret_cast(instructionPtr) & ~0x03) + 4); - instruction &=0xf00; - instruction |= 0xd000; - offset &= 0x03ff; - instruction |= (offset >> 2); - *instructionPtr = instruction; - printInstr(instruction, reinterpret_cast(loadAddr)); - } - - static void repatchPointer(void* where, void* value) - { - patchPointer(where, value); - } - - static void* readPointer(void* code) - { - return reinterpret_cast(readInt32(code)); - } - - static void repatchInt32(void* where, int32_t value) - { - uint16_t* instructionPtr = reinterpret_cast(where); - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, value); - } - - static void repatchCompact(void* where, int32_t value) - { - ASSERT(value >= 0); - ASSERT(value <= 60); - *reinterpret_cast(where) = ((*reinterpret_cast(where) & 0xfff0) | (value >> 2)); - cacheFlush(reinterpret_cast(where), sizeof(uint16_t)); - } - - static void relinkCall(void* from, void* to) - { - uint16_t* instructionPtr = reinterpret_cast(from); - instructionPtr -= 3; - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(to)); - } - - static void relinkJump(void* from, void* to) - { - uint16_t* instructionPtr = reinterpret_cast (from); - uint16_t instruction = *instructionPtr; - int32_t offsetBits = (reinterpret_cast(to) - reinterpret_cast(from)); - - if (((*instructionPtr & 0xff00) == BT_OPCODE) || ((*instructionPtr & 0xff00) == BF_OPCODE)) { - offsetBits -= 8; - instructionPtr++; - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); - instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); - *instructionPtr = instruction; - printBlockInstr(instructionPtr, reinterpret_cast(from) + 1, 3); - return; - } - - ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); - offsetBits -= 4; - if (offsetBits >= -4096 && offsetBits <= 4094) { - *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); - *(++instructionPtr) = NOP_OPCODE; - printBlockInstr(instructionPtr - 2, reinterpret_cast(from), 2); - return; - } - - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); - printInstr(*instructionPtr, reinterpret_cast(from)); - } - - // Linking & patching - - static void revertJump(void* instructionStart, SH4Word imm) - { - SH4Word *insn = reinterpret_cast(instructionStart); - SH4Word disp; - - ASSERT((insn[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); - - disp = insn[0] & 0x00ff; - insn += 2 + (disp << 1); // PC += 4 + (disp*4) - insn = (SH4Word *) ((unsigned) insn & (~3)); - insn[0] = imm; - cacheFlush(insn, sizeof(SH4Word)); - } - - void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type = JumpFar) - { - ASSERT(to.isSet()); - ASSERT(from.isSet()); - - uint16_t* instructionPtr = getInstructionPtr(data(), from.m_offset); - uint16_t instruction = *instructionPtr; - int offsetBits; - - if (type == JumpNear) { - ASSERT((instruction == BT_OPCODE) || (instruction == BF_OPCODE) || (instruction == BRA_OPCODE)); - int offset = (codeSize() - from.m_offset) - 4; - *instructionPtr++ = instruction | (offset >> 1); - printInstr(*instructionPtr, from.m_offset + 2); - return; - } - - if (((instruction & 0xff00) == BT_OPCODE) || ((instruction & 0xff00) == BF_OPCODE)) { - /* BT label => BF 2 - nop LDR reg - nop braf @reg - nop nop - */ - offsetBits = (to.m_offset - from.m_offset) - 8; - instruction ^= 0x0202; - *instructionPtr++ = instruction; - if ((*instructionPtr & 0xf000) == 0xe000) { - uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress()); - *addr = offsetBits; - } else - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); - instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); - *instructionPtr = instruction; - printBlockInstr(instructionPtr - 2, from.m_offset, 3); - return; - } - - /* MOV # imm, reg => LDR reg - braf @reg braf @reg - nop nop - */ - ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); - offsetBits = (to.m_offset - from.m_offset) - 4; - if (offsetBits >= -4096 && offsetBits <= 4094) { - *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); - *(++instructionPtr) = NOP_OPCODE; - printBlockInstr(instructionPtr - 1, from.m_offset, 2); - return; - } - - instruction = *instructionPtr; - if ((instruction & 0xf000) == 0xe000) { - uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress()); - *addr = offsetBits - 2; - printInstr(*instructionPtr, from.m_offset + 2); - return; - } - - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); - printInstr(*instructionPtr, from.m_offset + 2); - } - - static void* getRelocatedAddress(void* code, AssemblerLabel label) - { - return reinterpret_cast(reinterpret_cast(code) + label.m_offset); - } - - static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) - { - return b.m_offset - a.m_offset; - } - - static void patchPointer(void* code, AssemblerLabel where, void* value) - { - patchPointer(reinterpret_cast(code) + where.m_offset, value); - } - - static void patchPointer(void* code, void* value) - { - patchInt32(code, reinterpret_cast(value)); - } - - static void patchInt32(void* code, uint32_t value) - { - changePCrelativeAddress((*(reinterpret_cast(code)) & 0xff), reinterpret_cast(code), value); - } - - static uint32_t readInt32(void* code) - { - return readPCrelativeAddress((*(reinterpret_cast(code)) & 0xff), reinterpret_cast(code)); - } - - static void* readCallTarget(void* from) - { - uint16_t* instructionPtr = static_cast(from); - instructionPtr -= 3; - return reinterpret_cast(readPCrelativeAddress((*instructionPtr & 0xff), instructionPtr)); - } - - PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) - { - return m_buffer.executableCopy(globalData, ownerUID, effort); - } - - static void cacheFlush(void* code, size_t size) - { -#if !OS(LINUX) -#error "The cacheFlush support is missing on this platform." -#elif defined CACHEFLUSH_D_L2 - syscall(__NR_cacheflush, reinterpret_cast(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I | CACHEFLUSH_D_L2); -#else - syscall(__NR_cacheflush, reinterpret_cast(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I); -#endif - } - - void prefix(uint16_t pre) - { - m_buffer.putByte(pre); - } - - void oneShortOp(uint16_t opcode, bool checksize = true, bool isDouble = true) - { - printInstr(opcode, m_buffer.codeSize(), isDouble); - if (checksize) - m_buffer.ensureSpace(maxInstructionSize); - m_buffer.putShortUnchecked(opcode); - } - - void ensureSpace(int space) - { - m_buffer.ensureSpace(space); - } - - void ensureSpace(int insnSpace, int constSpace) - { - m_buffer.ensureSpace(insnSpace, constSpace); - } - - // Administrative methods - - void* data() const { return m_buffer.data(); } - size_t codeSize() const { return m_buffer.codeSize(); } - -#ifdef SH4_ASSEMBLER_TRACING - static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true) - { - if (!getenv("JavaScriptCoreDumpJIT")) - return; - - const char *format = 0; - printfStdoutInstr("offset: 0x%8.8x\t", size); - switch (opc) { - case BRK_OPCODE: - format = " BRK\n"; - break; - case NOP_OPCODE: - format = " NOP\n"; - break; - case RTS_OPCODE: - format =" *RTS\n"; - break; - case SETS_OPCODE: - format = " SETS\n"; - break; - case SETT_OPCODE: - format = " SETT\n"; - break; - case CLRT_OPCODE: - format = " CLRT\n"; - break; - case FSCHG_OPCODE: - format = " FSCHG\n"; - break; - } - if (format) { - printfStdoutInstr(format); - return; - } - switch (opc & 0xf0ff) { - case BRAF_OPCODE: - format = " *BRAF R%d\n"; - break; - case DT_OPCODE: - format = " DT R%d\n"; - break; - case CMPPL_OPCODE: - format = " CMP/PL R%d\n"; - break; - case CMPPZ_OPCODE: - format = " CMP/PZ R%d\n"; - break; - case JMP_OPCODE: - format = " *JMP @R%d\n"; - break; - case JSR_OPCODE: - format = " *JSR @R%d\n"; - break; - case LDSPR_OPCODE: - format = " LDS R%d, PR\n"; - break; - case LDSLPR_OPCODE: - format = " LDS.L @R%d+, PR\n"; - break; - case MOVT_OPCODE: - format = " MOVT R%d\n"; - break; - case SHAL_OPCODE: - format = " SHAL R%d\n"; - break; - case SHAR_OPCODE: - format = " SHAR R%d\n"; - break; - case SHLL_OPCODE: - format = " SHLL R%d\n"; - break; - case SHLL2_OPCODE: - format = " SHLL2 R%d\n"; - break; - case SHLL8_OPCODE: - format = " SHLL8 R%d\n"; - break; - case SHLL16_OPCODE: - format = " SHLL16 R%d\n"; - break; - case SHLR_OPCODE: - format = " SHLR R%d\n"; - break; - case SHLR2_OPCODE: - format = " SHLR2 R%d\n"; - break; - case SHLR8_OPCODE: - format = " SHLR8 R%d\n"; - break; - case SHLR16_OPCODE: - format = " SHLR16 R%d\n"; - break; - case STSPR_OPCODE: - format = " STS PR, R%d\n"; - break; - case STSLPR_OPCODE: - format = " STS.L PR, @-R%d\n"; - break; - case LDS_RM_FPUL_OPCODE: - format = " LDS R%d, FPUL\n"; - break; - case STS_FPUL_RN_OPCODE: - format = " STS FPUL, R%d \n"; - break; - case FLDS_FRM_FPUL_OPCODE: - format = " FLDS FR%d, FPUL\n"; - break; - case FSTS_FPUL_FRN_OPCODE: - format = " FSTS FPUL, R%d \n"; - break; - case LDSFPSCR_OPCODE: - format = " LDS R%d, FPSCR \n"; - break; - case STSFPSCR_OPCODE: - format = " STS FPSCR, R%d \n"; - break; - case STSMACL_OPCODE: - format = " STS MACL, R%d \n"; - break; - case STSMACH_OPCODE: - format = " STS MACH, R%d \n"; - break; - case BSRF_OPCODE: - format = " *BSRF R%d"; - break; - case FTRC_OPCODE: - format = " FTRC FR%d, FPUL\n"; - break; - } - if (format) { - printfStdoutInstr(format, getRn(opc)); - return; - } - switch (opc & 0xf0ff) { - case FNEG_OPCODE: - format = " FNEG DR%d\n"; - break; - case FLOAT_OPCODE: - format = " FLOAT DR%d\n"; - break; - case FTRC_OPCODE: - format = " FTRC FR%d, FPUL\n"; - break; - case FSQRT_OPCODE: - format = " FSQRT FR%d\n"; - break; - case FCNVDS_DRM_FPUL_OPCODE: - format = " FCNVDS FR%d, FPUL\n"; - break; - case FCNVSD_FPUL_DRN_OPCODE: - format = " FCNVSD FPUL, FR%d\n"; - break; - } - if (format) { - if (isdoubleInst) - printfStdoutInstr(format, getDRn(opc) << 1); - else - printfStdoutInstr(format, getRn(opc)); - return; - } - switch (opc & 0xf00f) { - case ADD_OPCODE: - format = " ADD R%d, R%d\n"; - break; - case ADDC_OPCODE: - format = " ADDC R%d, R%d\n"; - break; - case ADDV_OPCODE: - format = " ADDV R%d, R%d\n"; - break; - case AND_OPCODE: - format = " AND R%d, R%d\n"; - break; - case DIV1_OPCODE: - format = " DIV1 R%d, R%d\n"; - break; - case CMPEQ_OPCODE: - format = " CMP/EQ R%d, R%d\n"; - break; - case CMPGE_OPCODE: - format = " CMP/GE R%d, R%d\n"; - break; - case CMPGT_OPCODE: - format = " CMP/GT R%d, R%d\n"; - break; - case CMPHI_OPCODE: - format = " CMP/HI R%d, R%d\n"; - break; - case CMPHS_OPCODE: - format = " CMP/HS R%d, R%d\n"; - break; - case MOV_OPCODE: - format = " MOV R%d, R%d\n"; - break; - case MOVB_WRITE_RN_OPCODE: - format = " MOV.B R%d, @R%d\n"; - break; - case MOVB_WRITE_RNDEC_OPCODE: - format = " MOV.B R%d, @-R%d\n"; - break; - case MOVB_WRITE_R0RN_OPCODE: - format = " MOV.B R%d, @(R0, R%d)\n"; - break; - case MOVB_READ_RM_OPCODE: - format = " MOV.B @R%d, R%d\n"; - break; - case MOVB_READ_RMINC_OPCODE: - format = " MOV.B @R%d+, R%d\n"; - break; - case MOVB_READ_R0RM_OPCODE: - format = " MOV.B @(R0, R%d), R%d\n"; - break; - case MOVL_WRITE_RN_OPCODE: - format = " MOV.L R%d, @R%d\n"; - break; - case MOVL_WRITE_RNDEC_OPCODE: - format = " MOV.L R%d, @-R%d\n"; - break; - case MOVL_WRITE_R0RN_OPCODE: - format = " MOV.L R%d, @(R0, R%d)\n"; - break; - case MOVL_READ_RM_OPCODE: - format = " MOV.L @R%d, R%d\n"; - break; - case MOVL_READ_RMINC_OPCODE: - format = " MOV.L @R%d+, R%d\n"; - break; - case MOVL_READ_R0RM_OPCODE: - format = " MOV.L @(R0, R%d), R%d\n"; - break; - case MULL_OPCODE: - format = " MUL.L R%d, R%d\n"; - break; - case DMULL_L_OPCODE: - format = " DMULU.L R%d, R%d\n"; - break; - case DMULSL_OPCODE: - format = " DMULS.L R%d, R%d\n"; - break; - case NEG_OPCODE: - format = " NEG R%d, R%d\n"; - break; - case NEGC_OPCODE: - format = " NEGC R%d, R%d\n"; - break; - case NOT_OPCODE: - format = " NOT R%d, R%d\n"; - break; - case OR_OPCODE: - format = " OR R%d, R%d\n"; - break; - case SHAD_OPCODE: - format = " SHAD R%d, R%d\n"; - break; - case SHLD_OPCODE: - format = " SHLD R%d, R%d\n"; - break; - case SUB_OPCODE: - format = " SUB R%d, R%d\n"; - break; - case SUBC_OPCODE: - format = " SUBC R%d, R%d\n"; - break; - case SUBV_OPCODE: - format = " SUBV R%d, R%d\n"; - break; - case TST_OPCODE: - format = " TST R%d, R%d\n"; - break; - case XOR_OPCODE: - format = " XOR R%d, R%d\n";break; - case MOVW_WRITE_RN_OPCODE: - format = " MOV.W R%d, @R%d\n"; - break; - case MOVW_READ_RM_OPCODE: - format = " MOV.W @R%d, R%d\n"; - break; - case MOVW_READ_R0RM_OPCODE: - format = " MOV.W @(R0, R%d), R%d\n"; - break; - case EXTUB_OPCODE: - format = " EXTU.B R%d, R%d\n"; - break; - case EXTUW_OPCODE: - format = " EXTU.W R%d, R%d\n"; - break; - } - if (format) { - printfStdoutInstr(format, getRm(opc), getRn(opc)); - return; - } - switch (opc & 0xf00f) { - case FSUB_OPCODE: - format = " FSUB FR%d, FR%d\n"; - break; - case FADD_OPCODE: - format = " FADD FR%d, FR%d\n"; - break; - case FDIV_OPCODE: - format = " FDIV FR%d, FR%d\n"; - break; - case FMUL_OPCODE: - format = " DMULL FR%d, FR%d\n"; - break; - case FMOV_OPCODE: - format = " FMOV FR%d, FR%d\n"; - break; - case FCMPEQ_OPCODE: - format = " FCMP/EQ FR%d, FR%d\n"; - break; - case FCMPGT_OPCODE: - format = " FCMP/GT FR%d, FR%d\n"; - break; - } - if (format) { - if (isdoubleInst) - printfStdoutInstr(format, getDRm(opc) << 1, getDRn(opc) << 1); - else - printfStdoutInstr(format, getRm(opc), getRn(opc)); - return; - } - switch (opc & 0xf00f) { - case FMOVS_WRITE_RN_DEC_OPCODE: - format = " %s FR%d, @-R%d\n"; - break; - case FMOVS_WRITE_RN_OPCODE: - format = " %s FR%d, @R%d\n"; - break; - case FMOVS_WRITE_R0RN_OPCODE: - format = " %s FR%d, @(R0, R%d)\n"; - break; - } - if (format) { - if (isdoubleInst) - printfStdoutInstr(format, "FMOV", getDRm(opc) << 1, getDRn(opc)); - else - printfStdoutInstr(format, "FMOV.S", getRm(opc), getRn(opc)); - return; - } - switch (opc & 0xf00f) { - case FMOVS_READ_RM_OPCODE: - format = " %s @R%d, FR%d\n"; - break; - case FMOVS_READ_RM_INC_OPCODE: - format = " %s @R%d+, FR%d\n"; - break; - case FMOVS_READ_R0RM_OPCODE: - format = " %s @(R0, R%d), FR%d\n"; - break; - } - if (format) { - if (isdoubleInst) - printfStdoutInstr(format, "FMOV", getDRm(opc), getDRn(opc) << 1); - else - printfStdoutInstr(format, "FMOV.S", getRm(opc), getRn(opc)); - return; - } - switch (opc & 0xff00) { - case BF_OPCODE: - format = " BF %d\n"; - break; - case BFS_OPCODE: - format = " *BF/S %d\n"; - break; - case ANDIMM_OPCODE: - format = " AND #%d, R0\n"; - break; - case BT_OPCODE: - format = " BT %d\n"; - break; - case BTS_OPCODE: - format = " *BT/S %d\n"; - break; - case CMPEQIMM_OPCODE: - format = " CMP/EQ #%d, R0\n"; - break; - case MOVB_WRITE_OFFGBR_OPCODE: - format = " MOV.B R0, @(%d, GBR)\n"; - break; - case MOVB_READ_OFFGBR_OPCODE: - format = " MOV.B @(%d, GBR), R0\n"; - break; - case MOVL_WRITE_OFFGBR_OPCODE: - format = " MOV.L R0, @(%d, GBR)\n"; - break; - case MOVL_READ_OFFGBR_OPCODE: - format = " MOV.L @(%d, GBR), R0\n"; - break; - case MOVA_READ_OFFPC_OPCODE: - format = " MOVA @(%d, PC), R0\n"; - break; - case ORIMM_OPCODE: - format = " OR #%d, R0\n"; - break; - case ORBIMM_OPCODE: - format = " OR.B #%d, @(R0, GBR)\n"; - break; - case TSTIMM_OPCODE: - format = " TST #%d, R0\n"; - break; - case TSTB_OPCODE: - format = " TST.B %d, @(R0, GBR)\n"; - break; - case XORIMM_OPCODE: - format = " XOR #%d, R0\n"; - break; - case XORB_OPCODE: - format = " XOR.B %d, @(R0, GBR)\n"; - break; - } - if (format) { - printfStdoutInstr(format, getImm8(opc)); - return; - } - switch (opc & 0xff00) { - case MOVB_WRITE_OFFRN_OPCODE: - format = " MOV.B R0, @(%d, R%d)\n"; - break; - case MOVB_READ_OFFRM_OPCODE: - format = " MOV.B @(%d, R%d), R0\n"; - break; - } - if (format) { - printfStdoutInstr(format, getDisp(opc), getRm(opc)); - return; - } - switch (opc & 0xf000) { - case BRA_OPCODE: - format = " *BRA %d\n"; - break; - case BSR_OPCODE: - format = " *BSR %d\n"; - break; - } - if (format) { - printfStdoutInstr(format, getImm12(opc)); - return; - } - switch (opc & 0xf000) { - case MOVL_READ_OFFPC_OPCODE: - format = " MOV.L @(%d, PC), R%d\n"; - break; - case ADDIMM_OPCODE: - format = " ADD #%d, R%d\n"; - break; - case MOVIMM_OPCODE: - format = " MOV #%d, R%d\n"; - break; - case MOVW_READ_OFFPC_OPCODE: - format = " MOV.W @(%d, PC), R%d\n"; - break; - } - if (format) { - printfStdoutInstr(format, getImm8(opc), getRn(opc)); - return; - } - switch (opc & 0xf000) { - case MOVL_WRITE_OFFRN_OPCODE: - format = " MOV.L R%d, @(%d, R%d)\n"; - printfStdoutInstr(format, getRm(opc), getDisp(opc), getRn(opc)); - break; - case MOVL_READ_OFFRM_OPCODE: - format = " MOV.L @(%d, R%d), R%d\n"; - printfStdoutInstr(format, getDisp(opc), getRm(opc), getRn(opc)); - break; - } - } - - static void printfStdoutInstr(const char* format, ...) - { - if (getenv("JavaScriptCoreDumpJIT")) { - va_list args; - va_start(args, format); - vprintfStdoutInstr(format, args); - va_end(args); - } - } - - static void vprintfStdoutInstr(const char* format, va_list args) - { - if (getenv("JavaScriptCoreDumpJIT")) - WTF::dataLogFV(format, args); - } - - static void printBlockInstr(uint16_t* first, unsigned offset, int nbInstr) - { - printfStdoutInstr(">> repatch instructions after link\n"); - for (int i = 0; i <= nbInstr; i++) - printInstr(*(first + i), offset + i); - printfStdoutInstr(">> end repatch\n"); - } -#else - static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true) { }; - static void printBlockInstr(uint16_t* first, unsigned offset, int nbInstr) { }; -#endif - - static void replaceWithLoad(void* instructionStart) - { - SH4Word* insPtr = reinterpret_cast(instructionStart); - - insPtr += 2; // skip MOV and ADD opcodes - - if (((*insPtr) & 0xf00f) != MOVL_READ_RM_OPCODE) { - *insPtr = MOVL_READ_RM_OPCODE | (*insPtr & 0x0ff0); - cacheFlush(insPtr, sizeof(SH4Word)); - } - } - - static void replaceWithAddressComputation(void* instructionStart) - { - SH4Word* insPtr = reinterpret_cast(instructionStart); - - insPtr += 2; // skip MOV and ADD opcodes - - if (((*insPtr) & 0xf00f) != MOV_OPCODE) { - *insPtr = MOV_OPCODE | (*insPtr & 0x0ff0); - cacheFlush(insPtr, sizeof(SH4Word)); - } - } - -private: - SH4Buffer m_buffer; - int m_claimscratchReg; -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) && CPU(SH4) - -#endif // SH4Assembler_h diff --git a/masm/assembler/X86Assembler.h b/masm/assembler/X86Assembler.h deleted file mode 100644 index 25ff6f0a50..0000000000 --- a/masm/assembler/X86Assembler.h +++ /dev/null @@ -1,2540 +0,0 @@ -/* - * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef X86Assembler_h -#define X86Assembler_h - -#if ENABLE(ASSEMBLER) && (CPU(X86) || CPU(X86_64)) - -#include "AssemblerBuffer.h" -#include "JITCompilationEffort.h" -#include -#include -#include - -namespace JSC { - -inline bool CAN_SIGN_EXTEND_8_32(int32_t value) { return value == (int32_t)(signed char)value; } - -namespace X86Registers { - typedef enum { - eax, - ecx, - edx, - ebx, - esp, - ebp, - esi, - edi, - -#if CPU(X86_64) - r8, - r9, - r10, - r11, - r12, - r13, - r14, - r15, -#endif - } RegisterID; - - typedef enum { - xmm0, - xmm1, - xmm2, - xmm3, - xmm4, - xmm5, - xmm6, - xmm7, - } XMMRegisterID; -} - -class X86Assembler { -public: - typedef X86Registers::RegisterID RegisterID; - typedef X86Registers::XMMRegisterID XMMRegisterID; - typedef XMMRegisterID FPRegisterID; - - typedef enum { - ConditionO, - ConditionNO, - ConditionB, - ConditionAE, - ConditionE, - ConditionNE, - ConditionBE, - ConditionA, - ConditionS, - ConditionNS, - ConditionP, - ConditionNP, - ConditionL, - ConditionGE, - ConditionLE, - ConditionG, - - ConditionC = ConditionB, - ConditionNC = ConditionAE, - } Condition; - -private: - typedef enum { - OP_ADD_EvGv = 0x01, - OP_ADD_GvEv = 0x03, - OP_OR_EvGv = 0x09, - OP_OR_GvEv = 0x0B, - OP_2BYTE_ESCAPE = 0x0F, - OP_AND_EvGv = 0x21, - OP_AND_GvEv = 0x23, - OP_SUB_EvGv = 0x29, - OP_SUB_GvEv = 0x2B, - PRE_PREDICT_BRANCH_NOT_TAKEN = 0x2E, - OP_XOR_EvGv = 0x31, - OP_XOR_GvEv = 0x33, - OP_CMP_EvGv = 0x39, - OP_CMP_GvEv = 0x3B, -#if CPU(X86_64) - PRE_REX = 0x40, -#endif - OP_PUSH_EAX = 0x50, - OP_POP_EAX = 0x58, -#if CPU(X86_64) - OP_MOVSXD_GvEv = 0x63, -#endif - PRE_OPERAND_SIZE = 0x66, - PRE_SSE_66 = 0x66, - OP_PUSH_Iz = 0x68, - OP_IMUL_GvEvIz = 0x69, - OP_GROUP1_EbIb = 0x80, - OP_GROUP1_EvIz = 0x81, - OP_GROUP1_EvIb = 0x83, - OP_TEST_EbGb = 0x84, - OP_TEST_EvGv = 0x85, - OP_XCHG_EvGv = 0x87, - OP_MOV_EbGb = 0x88, - OP_MOV_EvGv = 0x89, - OP_MOV_GvEv = 0x8B, - OP_LEA = 0x8D, - OP_GROUP1A_Ev = 0x8F, - OP_NOP = 0x90, - OP_CDQ = 0x99, - OP_MOV_EAXOv = 0xA1, - OP_MOV_OvEAX = 0xA3, - OP_MOV_EAXIv = 0xB8, - OP_GROUP2_EvIb = 0xC1, - OP_RET = 0xC3, - OP_GROUP11_EvIb = 0xC6, - OP_GROUP11_EvIz = 0xC7, - OP_INT3 = 0xCC, - OP_GROUP2_Ev1 = 0xD1, - OP_GROUP2_EvCL = 0xD3, - OP_ESCAPE_DD = 0xDD, - OP_CALL_rel32 = 0xE8, - OP_JMP_rel32 = 0xE9, - PRE_SSE_F2 = 0xF2, - PRE_SSE_F3 = 0xF3, - OP_HLT = 0xF4, - OP_GROUP3_EbIb = 0xF6, - OP_GROUP3_Ev = 0xF7, - OP_GROUP3_EvIz = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test. - OP_GROUP5_Ev = 0xFF, - } OneByteOpcodeID; - - typedef enum { - OP2_MOVSD_VsdWsd = 0x10, - OP2_MOVSD_WsdVsd = 0x11, - OP2_MOVSS_VsdWsd = 0x10, - OP2_MOVSS_WsdVsd = 0x11, - OP2_CVTSI2SD_VsdEd = 0x2A, - OP2_CVTTSD2SI_GdWsd = 0x2C, - OP2_UCOMISD_VsdWsd = 0x2E, - OP2_ADDSD_VsdWsd = 0x58, - OP2_MULSD_VsdWsd = 0x59, - OP2_CVTSD2SS_VsdWsd = 0x5A, - OP2_CVTSS2SD_VsdWsd = 0x5A, - OP2_SUBSD_VsdWsd = 0x5C, - OP2_DIVSD_VsdWsd = 0x5E, - OP2_SQRTSD_VsdWsd = 0x51, - OP2_ANDNPD_VpdWpd = 0x55, - OP2_XORPD_VpdWpd = 0x57, - OP2_MOVD_VdEd = 0x6E, - OP2_MOVD_EdVd = 0x7E, - OP2_JCC_rel32 = 0x80, - OP_SETCC = 0x90, - OP2_IMUL_GvEv = 0xAF, - OP2_MOVZX_GvEb = 0xB6, - OP2_MOVSX_GvEb = 0xBE, - OP2_MOVZX_GvEw = 0xB7, - OP2_MOVSX_GvEw = 0xBF, - OP2_PEXTRW_GdUdIb = 0xC5, - OP2_PSLLQ_UdqIb = 0x73, - OP2_PSRLQ_UdqIb = 0x73, - OP2_POR_VdqWdq = 0XEB, - } TwoByteOpcodeID; - - TwoByteOpcodeID jccRel32(Condition cond) - { - return (TwoByteOpcodeID)(OP2_JCC_rel32 + cond); - } - - TwoByteOpcodeID setccOpcode(Condition cond) - { - return (TwoByteOpcodeID)(OP_SETCC + cond); - } - - typedef enum { - GROUP1_OP_ADD = 0, - GROUP1_OP_OR = 1, - GROUP1_OP_ADC = 2, - GROUP1_OP_AND = 4, - GROUP1_OP_SUB = 5, - GROUP1_OP_XOR = 6, - GROUP1_OP_CMP = 7, - - GROUP1A_OP_POP = 0, - - GROUP2_OP_ROL = 0, - GROUP2_OP_ROR = 1, - GROUP2_OP_RCL = 2, - GROUP2_OP_RCR = 3, - - GROUP2_OP_SHL = 4, - GROUP2_OP_SHR = 5, - GROUP2_OP_SAR = 7, - - GROUP3_OP_TEST = 0, - GROUP3_OP_NOT = 2, - GROUP3_OP_NEG = 3, - GROUP3_OP_IDIV = 7, - - GROUP5_OP_CALLN = 2, - GROUP5_OP_JMPN = 4, - GROUP5_OP_PUSH = 6, - - GROUP11_MOV = 0, - - GROUP14_OP_PSLLQ = 6, - GROUP14_OP_PSRLQ = 2, - - ESCAPE_DD_FSTP_doubleReal = 3, - } GroupOpcodeID; - - class X86InstructionFormatter; -public: - - X86Assembler() - : m_indexOfLastWatchpoint(INT_MIN) - , m_indexOfTailOfLastWatchpoint(INT_MIN) - { - } - - // Stack operations: - - void push_r(RegisterID reg) - { - m_formatter.oneByteOp(OP_PUSH_EAX, reg); - } - - void pop_r(RegisterID reg) - { - m_formatter.oneByteOp(OP_POP_EAX, reg); - } - - void push_i32(int imm) - { - m_formatter.oneByteOp(OP_PUSH_Iz); - m_formatter.immediate32(imm); - } - - void push_m(int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_PUSH, base, offset); - } - - void pop_m(int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP1A_Ev, GROUP1A_OP_POP, base, offset); - } - - // Arithmetic operations: - -#if !CPU(X86_64) - void adcl_im(int imm, const void* addr) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADC, addr); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADC, addr); - m_formatter.immediate32(imm); - } - } -#endif - - void addl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_ADD_EvGv, src, dst); - } - - void addl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp(OP_ADD_GvEv, dst, base, offset); - } - -#if !CPU(X86_64) - void addl_mr(const void* addr, RegisterID dst) - { - m_formatter.oneByteOp(OP_ADD_GvEv, dst, addr); - } -#endif - - void addl_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_ADD_EvGv, src, base, offset); - } - - void addl_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, dst); - m_formatter.immediate32(imm); - } - } - - void addl_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, offset); - m_formatter.immediate32(imm); - } - } - -#if CPU(X86_64) - void addq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_ADD_EvGv, src, dst); - } - - void addq_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp64(OP_ADD_GvEv, dst, base, offset); - } - - void addq_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_ADD, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_ADD, dst); - m_formatter.immediate32(imm); - } - } - - void addq_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, offset); - m_formatter.immediate32(imm); - } - } -#else - void addl_im(int imm, const void* addr) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, addr); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, addr); - m_formatter.immediate32(imm); - } - } -#endif - - void andl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_AND_EvGv, src, dst); - } - - void andl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp(OP_AND_GvEv, dst, base, offset); - } - - void andl_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_AND_EvGv, src, base, offset); - } - - void andl_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, dst); - m_formatter.immediate32(imm); - } - } - - void andl_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, base, offset); - m_formatter.immediate32(imm); - } - } - -#if CPU(X86_64) - void andq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_AND_EvGv, src, dst); - } - - void andq_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_AND, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_AND, dst); - m_formatter.immediate32(imm); - } - } -#else - void andl_im(int imm, const void* addr) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, addr); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, addr); - m_formatter.immediate32(imm); - } - } -#endif - - void negl_r(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, dst); - } - -#if CPU(X86_64) - void negq_r(RegisterID dst) - { - m_formatter.oneByteOp64(OP_GROUP3_Ev, GROUP3_OP_NEG, dst); - } -#endif - - void negl_m(int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, base, offset); - } - - void notl_r(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NOT, dst); - } - - void notl_m(int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NOT, base, offset); - } - - void orl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_OR_EvGv, src, dst); - } - - void orl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp(OP_OR_GvEv, dst, base, offset); - } - - void orl_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_OR_EvGv, src, base, offset); - } - - void orl_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, dst); - m_formatter.immediate32(imm); - } - } - - void orl_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, base, offset); - m_formatter.immediate32(imm); - } - } - -#if CPU(X86_64) - void orq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_OR_EvGv, src, dst); - } - - void orq_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_OR, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_OR, dst); - m_formatter.immediate32(imm); - } - } -#else - void orl_im(int imm, const void* addr) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, addr); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, addr); - m_formatter.immediate32(imm); - } - } - - void orl_rm(RegisterID src, const void* addr) - { - m_formatter.oneByteOp(OP_OR_EvGv, src, addr); - } -#endif - - void subl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_SUB_EvGv, src, dst); - } - - void subl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp(OP_SUB_GvEv, dst, base, offset); - } - - void subl_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_SUB_EvGv, src, base, offset); - } - - void subl_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, dst); - m_formatter.immediate32(imm); - } - } - - void subl_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, base, offset); - m_formatter.immediate32(imm); - } - } - -#if CPU(X86_64) - void subq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_SUB_EvGv, src, dst); - } - - void subq_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_SUB, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_SUB, dst); - m_formatter.immediate32(imm); - } - } -#else - void subl_im(int imm, const void* addr) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, addr); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, addr); - m_formatter.immediate32(imm); - } - } -#endif - - void xorl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_XOR_EvGv, src, dst); - } - - void xorl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp(OP_XOR_GvEv, dst, base, offset); - } - - void xorl_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_XOR_EvGv, src, base, offset); - } - - void xorl_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_XOR, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_XOR, base, offset); - m_formatter.immediate32(imm); - } - } - - void xorl_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_XOR, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_XOR, dst); - m_formatter.immediate32(imm); - } - } - -#if CPU(X86_64) - void xorq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_XOR_EvGv, src, dst); - } - - void xorq_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_XOR, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_XOR, dst); - m_formatter.immediate32(imm); - } - } - - void xorq_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp64(OP_XOR_EvGv, src, base, offset); - } - - void rorq_i8r(int imm, RegisterID dst) - { - if (imm == 1) - m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_ROR, dst); - else { - m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_ROR, dst); - m_formatter.immediate8(imm); - } - } - -#endif - - void sarl_i8r(int imm, RegisterID dst) - { - if (imm == 1) - m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SAR, dst); - else { - m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SAR, dst); - m_formatter.immediate8(imm); - } - } - - void sarl_CLr(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst); - } - - void shrl_i8r(int imm, RegisterID dst) - { - if (imm == 1) - m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHR, dst); - else { - m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHR, dst); - m_formatter.immediate8(imm); - } - } - - void shrl_CLr(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHR, dst); - } - - void shll_i8r(int imm, RegisterID dst) - { - if (imm == 1) - m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHL, dst); - else { - m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHL, dst); - m_formatter.immediate8(imm); - } - } - - void shll_CLr(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHL, dst); - } - -#if CPU(X86_64) - void sarq_CLr(RegisterID dst) - { - m_formatter.oneByteOp64(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst); - } - - void sarq_i8r(int imm, RegisterID dst) - { - if (imm == 1) - m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_SAR, dst); - else { - m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_SAR, dst); - m_formatter.immediate8(imm); - } - } -#endif - - void imull_rr(RegisterID src, RegisterID dst) - { - m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, src); - } - - void imull_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, base, offset); - } - - void imull_i32r(RegisterID src, int32_t value, RegisterID dst) - { - m_formatter.oneByteOp(OP_IMUL_GvEvIz, dst, src); - m_formatter.immediate32(value); - } - - void idivl_r(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_IDIV, dst); - } - - // Comparisons: - - void cmpl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_CMP_EvGv, src, dst); - } - - void cmpl_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_CMP_EvGv, src, base, offset); - } - - void cmpl_mr(int offset, RegisterID base, RegisterID src) - { - m_formatter.oneByteOp(OP_CMP_GvEv, src, base, offset); - } - - void cmpl_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); - m_formatter.immediate32(imm); - } - } - - void cmpl_ir_force32(int imm, RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); - m_formatter.immediate32(imm); - } - - void cmpl_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); - m_formatter.immediate32(imm); - } - } - - void cmpb_im(int imm, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, offset); - m_formatter.immediate8(imm); - } - - void cmpb_im(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, index, scale, offset); - m_formatter.immediate8(imm); - } - -#if CPU(X86) - void cmpb_im(int imm, const void* addr) - { - m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, addr); - m_formatter.immediate8(imm); - } -#endif - - void cmpl_im(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); - m_formatter.immediate32(imm); - } - } - - void cmpl_im_force32(int imm, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); - m_formatter.immediate32(imm); - } - -#if CPU(X86_64) - void cmpq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_CMP_EvGv, src, dst); - } - - void cmpq_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp64(OP_CMP_EvGv, src, base, offset); - } - - void cmpq_mr(int offset, RegisterID base, RegisterID src) - { - m_formatter.oneByteOp64(OP_CMP_GvEv, src, base, offset); - } - - void cmpq_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); - m_formatter.immediate32(imm); - } - } - - void cmpq_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); - m_formatter.immediate32(imm); - } - } - - void cmpq_im(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); - m_formatter.immediate32(imm); - } - } -#else - void cmpl_rm(RegisterID reg, const void* addr) - { - m_formatter.oneByteOp(OP_CMP_EvGv, reg, addr); - } - - void cmpl_im(int imm, const void* addr) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, addr); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, addr); - m_formatter.immediate32(imm); - } - } -#endif - - void cmpw_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); - m_formatter.immediate16(imm); - } - } - - void cmpw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp(OP_CMP_EvGv, src, base, index, scale, offset); - } - - void cmpw_im(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); - m_formatter.immediate16(imm); - } - } - - void testl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_TEST_EvGv, src, dst); - } - - void testl_i32r(int imm, RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, dst); - m_formatter.immediate32(imm); - } - - void testl_i32m(int imm, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, offset); - m_formatter.immediate32(imm); - } - - void testb_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp8(OP_TEST_EbGb, src, dst); - } - - void testb_im(int imm, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, offset); - m_formatter.immediate8(imm); - } - - void testb_im(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, index, scale, offset); - m_formatter.immediate8(imm); - } - -#if CPU(X86) - void testb_im(int imm, const void* addr) - { - m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, addr); - m_formatter.immediate8(imm); - } -#endif - - void testl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset); - m_formatter.immediate32(imm); - } - -#if CPU(X86_64) - void testq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_TEST_EvGv, src, dst); - } - - void testq_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp64(OP_TEST_EvGv, src, base, offset); - } - - void testq_i32r(int imm, RegisterID dst) - { - m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, dst); - m_formatter.immediate32(imm); - } - - void testq_i32m(int imm, int offset, RegisterID base) - { - m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, offset); - m_formatter.immediate32(imm); - } - - void testq_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset); - m_formatter.immediate32(imm); - } -#endif - - void testw_rr(RegisterID src, RegisterID dst) - { - m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp(OP_TEST_EvGv, src, dst); - } - - void testb_i8r(int imm, RegisterID dst) - { - m_formatter.oneByteOp8(OP_GROUP3_EbIb, GROUP3_OP_TEST, dst); - m_formatter.immediate8(imm); - } - - void setCC_r(Condition cond, RegisterID dst) - { - m_formatter.twoByteOp8(setccOpcode(cond), (GroupOpcodeID)0, dst); - } - - void sete_r(RegisterID dst) - { - m_formatter.twoByteOp8(setccOpcode(ConditionE), (GroupOpcodeID)0, dst); - } - - void setz_r(RegisterID dst) - { - sete_r(dst); - } - - void setne_r(RegisterID dst) - { - m_formatter.twoByteOp8(setccOpcode(ConditionNE), (GroupOpcodeID)0, dst); - } - - void setnz_r(RegisterID dst) - { - setne_r(dst); - } - - // Various move ops: - - void cdq() - { - m_formatter.oneByteOp(OP_CDQ); - } - - void fstpl(int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_ESCAPE_DD, ESCAPE_DD_FSTP_doubleReal, base, offset); - } - - void xchgl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_XCHG_EvGv, src, dst); - } - -#if CPU(X86_64) - void xchgq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_XCHG_EvGv, src, dst); - } -#endif - - void movl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_MOV_EvGv, src, dst); - } - - void movl_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_MOV_EvGv, src, base, offset); - } - - void movl_rm_disp32(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset); - } - - void movl_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset); - } - - void movl_mEAX(const void* addr) - { - m_formatter.oneByteOp(OP_MOV_EAXOv); -#if CPU(X86_64) - m_formatter.immediate64(reinterpret_cast(addr)); -#else - m_formatter.immediate32(reinterpret_cast(addr)); -#endif - } - - void movl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, offset); - } - - void movl_mr_disp32(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp_disp32(OP_MOV_GvEv, dst, base, offset); - } - - void movl_mr_disp8(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp_disp8(OP_MOV_GvEv, dst, base, offset); - } - - void movl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) - { - m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, index, scale, offset); - } - - void movl_i32r(int imm, RegisterID dst) - { - m_formatter.oneByteOp(OP_MOV_EAXIv, dst); - m_formatter.immediate32(imm); - } - - void movl_i32m(int imm, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, offset); - m_formatter.immediate32(imm); - } - - void movl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, index, scale, offset); - m_formatter.immediate32(imm); - } - -#if !CPU(X86_64) - void movb_i8m(int imm, const void* addr) - { - ASSERT(-128 <= imm && imm < 128); - m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, addr); - m_formatter.immediate8(imm); - } -#endif - - void movb_i8m(int imm, int offset, RegisterID base) - { - ASSERT(-128 <= imm && imm < 128); - m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, offset); - m_formatter.immediate8(imm); - } - - void movb_i8m(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - ASSERT(-128 <= imm && imm < 128); - m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, index, scale, offset); - m_formatter.immediate8(imm); - } - - void movb_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp8(OP_MOV_EbGb, src, base, index, scale, offset); - } - - void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp8(OP_MOV_EvGv, src, base, index, scale, offset); - } - - void movl_EAXm(const void* addr) - { - m_formatter.oneByteOp(OP_MOV_OvEAX); -#if CPU(X86_64) - m_formatter.immediate64(reinterpret_cast(addr)); -#else - m_formatter.immediate32(reinterpret_cast(addr)); -#endif - } - -#if CPU(X86_64) - void movq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_MOV_EvGv, src, dst); - } - - void movq_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp64(OP_MOV_EvGv, src, base, offset); - } - - void movq_rm_disp32(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp64_disp32(OP_MOV_EvGv, src, base, offset); - } - - void movq_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp64(OP_MOV_EvGv, src, base, index, scale, offset); - } - - void movq_mEAX(const void* addr) - { - m_formatter.oneByteOp64(OP_MOV_EAXOv); - m_formatter.immediate64(reinterpret_cast(addr)); - } - - void movq_EAXm(const void* addr) - { - m_formatter.oneByteOp64(OP_MOV_OvEAX); - m_formatter.immediate64(reinterpret_cast(addr)); - } - - void movq_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp64(OP_MOV_GvEv, dst, base, offset); - } - - void movq_mr_disp32(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp64_disp32(OP_MOV_GvEv, dst, base, offset); - } - - void movq_mr_disp8(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp64_disp8(OP_MOV_GvEv, dst, base, offset); - } - - void movq_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) - { - m_formatter.oneByteOp64(OP_MOV_GvEv, dst, base, index, scale, offset); - } - - void movq_i32m(int imm, int offset, RegisterID base) - { - m_formatter.oneByteOp64(OP_GROUP11_EvIz, GROUP11_MOV, base, offset); - m_formatter.immediate32(imm); - } - - void movq_i64r(int64_t imm, RegisterID dst) - { - m_formatter.oneByteOp64(OP_MOV_EAXIv, dst); - m_formatter.immediate64(imm); - } - - void movsxd_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_MOVSXD_GvEv, dst, src); - } - - -#else - void movl_rm(RegisterID src, const void* addr) - { - if (src == X86Registers::eax) - movl_EAXm(addr); - else - m_formatter.oneByteOp(OP_MOV_EvGv, src, addr); - } - - void movl_mr(const void* addr, RegisterID dst) - { - if (dst == X86Registers::eax) - movl_mEAX(addr); - else - m_formatter.oneByteOp(OP_MOV_GvEv, dst, addr); - } - - void movl_i32m(int imm, const void* addr) - { - m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, addr); - m_formatter.immediate32(imm); - } -#endif - - void movzwl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, offset); - } - - void movzwl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, index, scale, offset); - } - - void movswl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, offset); - } - - void movswl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, index, scale, offset); - } - - void movzbl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, offset); - } - - void movzbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, index, scale, offset); - } - - void movsbl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, offset); - } - - void movsbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, index, scale, offset); - } - - void movzbl_rr(RegisterID src, RegisterID dst) - { - // In 64-bit, this may cause an unnecessary REX to be planted (if the dst register - // is in the range ESP-EDI, and the src would not have required a REX). Unneeded - // REX prefixes are defined to be silently ignored by the processor. - m_formatter.twoByteOp8(OP2_MOVZX_GvEb, dst, src); - } - - void leal_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp(OP_LEA, dst, base, offset); - } -#if CPU(X86_64) - void leaq_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp64(OP_LEA, dst, base, offset); - } -#endif - - // Flow control: - - AssemblerLabel call() - { - m_formatter.oneByteOp(OP_CALL_rel32); - return m_formatter.immediateRel32(); - } - - AssemblerLabel call(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, dst); - return m_formatter.label(); - } - - void call_m(int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, base, offset); - } - - AssemblerLabel jmp() - { - m_formatter.oneByteOp(OP_JMP_rel32); - return m_formatter.immediateRel32(); - } - - // Return a AssemblerLabel so we have a label to the jump, so we can use this - // To make a tail recursive call on x86-64. The MacroAssembler - // really shouldn't wrap this as a Jump, since it can't be linked. :-/ - AssemblerLabel jmp_r(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, dst); - return m_formatter.label(); - } - - void jmp_m(int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, base, offset); - } - -#if !CPU(X86_64) - void jmp_m(const void* address) - { - m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, address); - } -#endif - - AssemblerLabel jne() - { - m_formatter.twoByteOp(jccRel32(ConditionNE)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jnz() - { - return jne(); - } - - AssemblerLabel je() - { - m_formatter.twoByteOp(jccRel32(ConditionE)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jz() - { - return je(); - } - - AssemblerLabel jl() - { - m_formatter.twoByteOp(jccRel32(ConditionL)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jb() - { - m_formatter.twoByteOp(jccRel32(ConditionB)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jle() - { - m_formatter.twoByteOp(jccRel32(ConditionLE)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jbe() - { - m_formatter.twoByteOp(jccRel32(ConditionBE)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jge() - { - m_formatter.twoByteOp(jccRel32(ConditionGE)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jg() - { - m_formatter.twoByteOp(jccRel32(ConditionG)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel ja() - { - m_formatter.twoByteOp(jccRel32(ConditionA)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jae() - { - m_formatter.twoByteOp(jccRel32(ConditionAE)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jo() - { - m_formatter.twoByteOp(jccRel32(ConditionO)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jnp() - { - m_formatter.twoByteOp(jccRel32(ConditionNP)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jp() - { - m_formatter.twoByteOp(jccRel32(ConditionP)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel js() - { - m_formatter.twoByteOp(jccRel32(ConditionS)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jCC(Condition cond) - { - m_formatter.twoByteOp(jccRel32(cond)); - return m_formatter.immediateRel32(); - } - - // SSE operations: - - void addsd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, (RegisterID)src); - } - - void addsd_mr(int offset, RegisterID base, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, base, offset); - } - -#if !CPU(X86_64) - void addsd_mr(const void* address, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, address); - } -#endif - - void cvtsi2sd_rr(RegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src); - } - - void cvtsi2sd_mr(int offset, RegisterID base, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, offset); - } - -#if !CPU(X86_64) - void cvtsi2sd_mr(const void* address, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, address); - } -#endif - - void cvttsd2si_rr(XMMRegisterID src, RegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src); - } - - void cvtsd2ss_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_CVTSD2SS_VsdWsd, dst, (RegisterID)src); - } - - void cvtss2sd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F3); - m_formatter.twoByteOp(OP2_CVTSS2SD_VsdWsd, dst, (RegisterID)src); - } - -#if CPU(X86_64) - void cvttsd2siq_rr(XMMRegisterID src, RegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp64(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src); - } -#endif - - void movd_rr(XMMRegisterID src, RegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_MOVD_EdVd, (RegisterID)src, dst); - } - - void movd_rr(RegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_MOVD_VdEd, (RegisterID)dst, src); - } - -#if CPU(X86_64) - void movq_rr(XMMRegisterID src, RegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp64(OP2_MOVD_EdVd, (RegisterID)src, dst); - } - - void movq_rr(RegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp64(OP2_MOVD_VdEd, (RegisterID)dst, src); - } -#endif - - void movsd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, (RegisterID)src); - } - - void movsd_rm(XMMRegisterID src, int offset, RegisterID base) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset); - } - - void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset); - } - - void movss_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.prefix(PRE_SSE_F3); - m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset); - } - - void movsd_mr(int offset, RegisterID base, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset); - } - - void movsd_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, dst, base, index, scale, offset); - } - - void movss_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F3); - m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, dst, base, index, scale, offset); - } - -#if !CPU(X86_64) - void movsd_mr(const void* address, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address); - } - void movsd_rm(XMMRegisterID src, const void* address) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address); - } -#endif - - void mulsd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, (RegisterID)src); - } - - void mulsd_mr(int offset, RegisterID base, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, base, offset); - } - - void pextrw_irr(int whichWord, XMMRegisterID src, RegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_PEXTRW_GdUdIb, (RegisterID)dst, (RegisterID)src); - m_formatter.immediate8(whichWord); - } - - void psllq_i8r(int imm, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp8(OP2_PSLLQ_UdqIb, GROUP14_OP_PSLLQ, (RegisterID)dst); - m_formatter.immediate8(imm); - } - - void psrlq_i8r(int imm, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp8(OP2_PSRLQ_UdqIb, GROUP14_OP_PSRLQ, (RegisterID)dst); - m_formatter.immediate8(imm); - } - - void por_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_POR_VdqWdq, (RegisterID)dst, (RegisterID)src); - } - - void subsd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, (RegisterID)src); - } - - void subsd_mr(int offset, RegisterID base, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, base, offset); - } - - void ucomisd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, (RegisterID)src); - } - - void ucomisd_mr(int offset, RegisterID base, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, base, offset); - } - - void divsd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, (RegisterID)src); - } - - void divsd_mr(int offset, RegisterID base, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, base, offset); - } - - void xorpd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src); - } - - void andnpd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_ANDNPD_VpdWpd, (RegisterID)dst, (RegisterID)src); - } - - void sqrtsd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_SQRTSD_VsdWsd, (RegisterID)dst, (RegisterID)src); - } - - // Misc instructions: - - void int3() - { - m_formatter.oneByteOp(OP_INT3); - } - - void ret() - { - m_formatter.oneByteOp(OP_RET); - } - - void predictNotTaken() - { - m_formatter.prefix(PRE_PREDICT_BRANCH_NOT_TAKEN); - } - - // Assembler admin methods: - - size_t codeSize() const - { - return m_formatter.codeSize(); - } - - AssemblerLabel labelForWatchpoint() - { - AssemblerLabel result = m_formatter.label(); - if (static_cast(result.m_offset) != m_indexOfLastWatchpoint) - result = label(); - m_indexOfLastWatchpoint = result.m_offset; - m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); - return result; - } - - AssemblerLabel labelIgnoringWatchpoints() - { - return m_formatter.label(); - } - - AssemblerLabel label() - { - AssemblerLabel result = m_formatter.label(); - while (UNLIKELY(static_cast(result.m_offset) < m_indexOfTailOfLastWatchpoint)) { - nop(); - result = m_formatter.label(); - } - return result; - } - - AssemblerLabel align(int alignment) - { - while (!m_formatter.isAligned(alignment)) - m_formatter.oneByteOp(OP_HLT); - - return label(); - } - - // Linking & patching: - // - // 'link' and 'patch' methods are for use on unprotected code - such as the code - // within the AssemblerBuffer, and code being patched by the patch buffer. Once - // code has been finalized it is (platform support permitting) within a non- - // writable region of memory; to modify the code in an execute-only execuable - // pool the 'repatch' and 'relink' methods should be used. - - void linkJump(AssemblerLabel from, AssemblerLabel to) - { - ASSERT(from.isSet()); - ASSERT(to.isSet()); - - char* code = reinterpret_cast(m_formatter.data()); - ASSERT(!reinterpret_cast(code + from.m_offset)[-1]); - setRel32(code + from.m_offset, code + to.m_offset); - } - - static void linkJump(void* code, AssemblerLabel from, void* to) - { - ASSERT(from.isSet()); - - setRel32(reinterpret_cast(code) + from.m_offset, to); - } - - static void linkCall(void* code, AssemblerLabel from, void* to) - { - ASSERT(from.isSet()); - - setRel32(reinterpret_cast(code) + from.m_offset, to); - } - - static void linkPointer(void* code, AssemblerLabel where, void* value) - { - ASSERT(where.isSet()); - - setPointer(reinterpret_cast(code) + where.m_offset, value); - } - - static void relinkJump(void* from, void* to) - { - setRel32(from, to); - } - - static void relinkCall(void* from, void* to) - { - setRel32(from, to); - } - - static void repatchCompact(void* where, int32_t value) - { - ASSERT(value >= std::numeric_limits::min()); - ASSERT(value <= std::numeric_limits::max()); - setInt8(where, value); - } - - static void repatchInt32(void* where, int32_t value) - { - setInt32(where, value); - } - - static void repatchPointer(void* where, void* value) - { - setPointer(where, value); - } - - static void* readPointer(void* where) - { - return reinterpret_cast(where)[-1]; - } - - static void replaceWithJump(void* instructionStart, void* to) - { - uint8_t* ptr = reinterpret_cast(instructionStart); - uint8_t* dstPtr = reinterpret_cast(to); - intptr_t distance = (intptr_t)(dstPtr - (ptr + 5)); - ptr[0] = static_cast(OP_JMP_rel32); - *reinterpret_cast(ptr + 1) = static_cast(distance); - } - - static ptrdiff_t maxJumpReplacementSize() - { - return 5; - } - -#if CPU(X86_64) - static void revertJumpTo_movq_i64r(void* instructionStart, int64_t imm, RegisterID dst) - { - const int rexBytes = 1; - const int opcodeBytes = 1; - ASSERT(rexBytes + opcodeBytes <= maxJumpReplacementSize()); - uint8_t* ptr = reinterpret_cast(instructionStart); - ptr[0] = PRE_REX | (1 << 3) | (dst >> 3); - ptr[1] = OP_MOV_EAXIv | (dst & 7); - - union { - uint64_t asWord; - uint8_t asBytes[8]; - } u; - u.asWord = imm; - for (unsigned i = rexBytes + opcodeBytes; i < static_cast(maxJumpReplacementSize()); ++i) - ptr[i] = u.asBytes[i - rexBytes - opcodeBytes]; - } -#endif - - static void revertJumpTo_cmpl_ir_force32(void* instructionStart, int32_t imm, RegisterID dst) - { - const int opcodeBytes = 1; - const int modRMBytes = 1; - ASSERT(opcodeBytes + modRMBytes <= maxJumpReplacementSize()); - uint8_t* ptr = reinterpret_cast(instructionStart); - ptr[0] = OP_GROUP1_EvIz; - ptr[1] = (X86InstructionFormatter::ModRmRegister << 6) | (GROUP1_OP_CMP << 3) | dst; - union { - uint32_t asWord; - uint8_t asBytes[4]; - } u; - u.asWord = imm; - for (unsigned i = opcodeBytes + modRMBytes; i < static_cast(maxJumpReplacementSize()); ++i) - ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes]; - } - - static void revertJumpTo_cmpl_im_force32(void* instructionStart, int32_t imm, int offset, RegisterID dst) - { - ASSERT_UNUSED(offset, !offset); - const int opcodeBytes = 1; - const int modRMBytes = 1; - ASSERT(opcodeBytes + modRMBytes <= maxJumpReplacementSize()); - uint8_t* ptr = reinterpret_cast(instructionStart); - ptr[0] = OP_GROUP1_EvIz; - ptr[1] = (X86InstructionFormatter::ModRmMemoryNoDisp << 6) | (GROUP1_OP_CMP << 3) | dst; - union { - uint32_t asWord; - uint8_t asBytes[4]; - } u; - u.asWord = imm; - for (unsigned i = opcodeBytes + modRMBytes; i < static_cast(maxJumpReplacementSize()); ++i) - ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes]; - } - - static void replaceWithLoad(void* instructionStart) - { - uint8_t* ptr = reinterpret_cast(instructionStart); -#if CPU(X86_64) - if ((*ptr & ~15) == PRE_REX) - ptr++; -#endif - switch (*ptr) { - case OP_MOV_GvEv: - break; - case OP_LEA: - *ptr = OP_MOV_GvEv; - break; - default: - ASSERT_NOT_REACHED(); - } - } - - static void replaceWithAddressComputation(void* instructionStart) - { - uint8_t* ptr = reinterpret_cast(instructionStart); -#if CPU(X86_64) - if ((*ptr & ~15) == PRE_REX) - ptr++; -#endif - switch (*ptr) { - case OP_MOV_GvEv: - *ptr = OP_LEA; - break; - case OP_LEA: - break; - default: - ASSERT_NOT_REACHED(); - } - } - - static unsigned getCallReturnOffset(AssemblerLabel call) - { - ASSERT(call.isSet()); - return call.m_offset; - } - - static void* getRelocatedAddress(void* code, AssemblerLabel label) - { - ASSERT(label.isSet()); - return reinterpret_cast(reinterpret_cast(code) + label.m_offset); - } - - static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) - { - return b.m_offset - a.m_offset; - } - - PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) - { - return m_formatter.executableCopy(globalData, ownerUID, effort); - } - - unsigned debugOffset() { return m_formatter.debugOffset(); } - - void nop() - { - m_formatter.oneByteOp(OP_NOP); - } - - // This is a no-op on x86 - ALWAYS_INLINE static void cacheFlush(void*, size_t) { } - -private: - - static void setPointer(void* where, void* value) - { - reinterpret_cast(where)[-1] = value; - } - - static void setInt32(void* where, int32_t value) - { - reinterpret_cast(where)[-1] = value; - } - - static void setInt8(void* where, int8_t value) - { - reinterpret_cast(where)[-1] = value; - } - - static void setRel32(void* from, void* to) - { - intptr_t offset = reinterpret_cast(to) - reinterpret_cast(from); - ASSERT(offset == static_cast(offset)); - - setInt32(from, offset); - } - - class X86InstructionFormatter { - - static const int maxInstructionSize = 16; - - public: - - enum ModRmMode { - ModRmMemoryNoDisp, - ModRmMemoryDisp8, - ModRmMemoryDisp32, - ModRmRegister, - }; - - // Legacy prefix bytes: - // - // These are emmitted prior to the instruction. - - void prefix(OneByteOpcodeID pre) - { - m_buffer.putByte(pre); - } - - // Word-sized operands / no operand instruction formatters. - // - // In addition to the opcode, the following operand permutations are supported: - // * None - instruction takes no operands. - // * One register - the low three bits of the RegisterID are added into the opcode. - // * Two registers - encode a register form ModRm (for all ModRm formats, the reg field is passed first, and a GroupOpcodeID may be passed in its place). - // * Three argument ModRM - a register, and a register and an offset describing a memory operand. - // * Five argument ModRM - a register, and a base register, an index, scale, and offset describing a memory operand. - // - // For 32-bit x86 targets, the address operand may also be provided as a void*. - // On 64-bit targets REX prefixes will be planted as necessary, where high numbered registers are used. - // - // The twoByteOp methods plant two-byte Intel instructions sequences (first opcode byte 0x0F). - - void oneByteOp(OneByteOpcodeID opcode) - { - m_buffer.ensureSpace(maxInstructionSize); - m_buffer.putByteUnchecked(opcode); - } - - void oneByteOp(OneByteOpcodeID opcode, RegisterID reg) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(0, 0, reg); - m_buffer.putByteUnchecked(opcode + (reg & 7)); - } - - void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, 0, rm); - m_buffer.putByteUnchecked(opcode); - registerModRM(reg, rm); - } - - void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, 0, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, base, offset); - } - - void oneByteOp_disp32(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, 0, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM_disp32(reg, base, offset); - } - - void oneByteOp_disp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, 0, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM_disp8(reg, base, offset); - } - - void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, index, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, base, index, scale, offset); - } - -#if !CPU(X86_64) - void oneByteOp(OneByteOpcodeID opcode, int reg, const void* address) - { - m_buffer.ensureSpace(maxInstructionSize); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, address); - } -#endif - - void twoByteOp(TwoByteOpcodeID opcode) - { - m_buffer.ensureSpace(maxInstructionSize); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - } - - void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, 0, rm); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - registerModRM(reg, rm); - } - - void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID base, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, 0, base); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, base, offset); - } - - void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, index, base); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, base, index, scale, offset); - } - -#if !CPU(X86_64) - void twoByteOp(TwoByteOpcodeID opcode, int reg, const void* address) - { - m_buffer.ensureSpace(maxInstructionSize); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, address); - } -#endif - -#if CPU(X86_64) - // Quad-word-sized operands: - // - // Used to format 64-bit operantions, planting a REX.w prefix. - // When planting d64 or f64 instructions, not requiring a REX.w prefix, - // the normal (non-'64'-postfixed) formatters should be used. - - void oneByteOp64(OneByteOpcodeID opcode) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(0, 0, 0); - m_buffer.putByteUnchecked(opcode); - } - - void oneByteOp64(OneByteOpcodeID opcode, RegisterID reg) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(0, 0, reg); - m_buffer.putByteUnchecked(opcode + (reg & 7)); - } - - void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(reg, 0, rm); - m_buffer.putByteUnchecked(opcode); - registerModRM(reg, rm); - } - - void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(reg, 0, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, base, offset); - } - - void oneByteOp64_disp32(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(reg, 0, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM_disp32(reg, base, offset); - } - - void oneByteOp64_disp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(reg, 0, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM_disp8(reg, base, offset); - } - - void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(reg, index, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, base, index, scale, offset); - } - - void twoByteOp64(TwoByteOpcodeID opcode, int reg, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(reg, 0, rm); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - registerModRM(reg, rm); - } -#endif - - // Byte-operands: - // - // These methods format byte operations. Byte operations differ from the normal - // formatters in the circumstances under which they will decide to emit REX prefixes. - // These should be used where any register operand signifies a byte register. - // - // The disctinction is due to the handling of register numbers in the range 4..7 on - // x86-64. These register numbers may either represent the second byte of the first - // four registers (ah..bh) or the first byte of the second four registers (spl..dil). - // - // Since ah..bh cannot be used in all permutations of operands (specifically cannot - // be accessed where a REX prefix is present), these are likely best treated as - // deprecated. In order to ensure the correct registers spl..dil are selected a - // REX prefix will be emitted for any byte register operand in the range 4..15. - // - // These formatters may be used in instructions where a mix of operand sizes, in which - // case an unnecessary REX will be emitted, for example: - // movzbl %al, %edi - // In this case a REX will be planted since edi is 7 (and were this a byte operand - // a REX would be required to specify dil instead of bh). Unneeded REX prefixes will - // be silently ignored by the processor. - // - // Address operands should still be checked using regRequiresRex(), while byteRegRequiresRex() - // is provided to check byte register operands. - - void oneByteOp8(OneByteOpcodeID opcode, GroupOpcodeID groupOp, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(byteRegRequiresRex(rm), 0, 0, rm); - m_buffer.putByteUnchecked(opcode); - registerModRM(groupOp, rm); - } - - void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(byteRegRequiresRex(reg) || byteRegRequiresRex(rm), reg, 0, rm); - m_buffer.putByteUnchecked(opcode); - registerModRM(reg, rm); - } - - void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(byteRegRequiresRex(reg) || regRequiresRex(index) || regRequiresRex(base), reg, index, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, base, index, scale, offset); - } - - void twoByteOp8(TwoByteOpcodeID opcode, RegisterID reg, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(byteRegRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - registerModRM(reg, rm); - } - - void twoByteOp8(TwoByteOpcodeID opcode, GroupOpcodeID groupOp, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(byteRegRequiresRex(rm), 0, 0, rm); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - registerModRM(groupOp, rm); - } - - // Immediates: - // - // An immedaite should be appended where appropriate after an op has been emitted. - // The writes are unchecked since the opcode formatters above will have ensured space. - - void immediate8(int imm) - { - m_buffer.putByteUnchecked(imm); - } - - void immediate16(int imm) - { - m_buffer.putShortUnchecked(imm); - } - - void immediate32(int imm) - { - m_buffer.putIntUnchecked(imm); - } - - void immediate64(int64_t imm) - { - m_buffer.putInt64Unchecked(imm); - } - - AssemblerLabel immediateRel32() - { - m_buffer.putIntUnchecked(0); - return label(); - } - - // Administrative methods: - - size_t codeSize() const { return m_buffer.codeSize(); } - AssemblerLabel label() const { return m_buffer.label(); } - bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); } - void* data() const { return m_buffer.data(); } - - PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) - { - return m_buffer.executableCopy(globalData, ownerUID, effort); - } - - unsigned debugOffset() { return m_buffer.debugOffset(); } - - private: - - // Internals; ModRm and REX formatters. - - static const RegisterID noBase = X86Registers::ebp; - static const RegisterID hasSib = X86Registers::esp; - static const RegisterID noIndex = X86Registers::esp; -#if CPU(X86_64) - static const RegisterID noBase2 = X86Registers::r13; - static const RegisterID hasSib2 = X86Registers::r12; - - // Registers r8 & above require a REX prefixe. - inline bool regRequiresRex(int reg) - { - return (reg >= X86Registers::r8); - } - - // Byte operand register spl & above require a REX prefix (to prevent the 'H' registers be accessed). - inline bool byteRegRequiresRex(int reg) - { - return (reg >= X86Registers::esp); - } - - // Format a REX prefix byte. - inline void emitRex(bool w, int r, int x, int b) - { - ASSERT(r >= 0); - ASSERT(x >= 0); - ASSERT(b >= 0); - m_buffer.putByteUnchecked(PRE_REX | ((int)w << 3) | ((r>>3)<<2) | ((x>>3)<<1) | (b>>3)); - } - - // Used to plant a REX byte with REX.w set (for 64-bit operations). - inline void emitRexW(int r, int x, int b) - { - emitRex(true, r, x, b); - } - - // Used for operations with byte operands - use byteRegRequiresRex() to check register operands, - // regRequiresRex() to check other registers (i.e. address base & index). - inline void emitRexIf(bool condition, int r, int x, int b) - { - if (condition) emitRex(false, r, x, b); - } - - // Used for word sized operations, will plant a REX prefix if necessary (if any register is r8 or above). - inline void emitRexIfNeeded(int r, int x, int b) - { - emitRexIf(regRequiresRex(r) || regRequiresRex(x) || regRequiresRex(b), r, x, b); - } -#else - // No REX prefix bytes on 32-bit x86. - inline bool regRequiresRex(int) { return false; } - inline bool byteRegRequiresRex(int) { return false; } - inline void emitRexIf(bool, int, int, int) {} - inline void emitRexIfNeeded(int, int, int) {} -#endif - - void putModRm(ModRmMode mode, int reg, RegisterID rm) - { - m_buffer.putByteUnchecked((mode << 6) | ((reg & 7) << 3) | (rm & 7)); - } - - void putModRmSib(ModRmMode mode, int reg, RegisterID base, RegisterID index, int scale) - { - ASSERT(mode != ModRmRegister); - - putModRm(mode, reg, hasSib); - m_buffer.putByteUnchecked((scale << 6) | ((index & 7) << 3) | (base & 7)); - } - - void registerModRM(int reg, RegisterID rm) - { - putModRm(ModRmRegister, reg, rm); - } - - void memoryModRM(int reg, RegisterID base, int offset) - { - // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. -#if CPU(X86_64) - if ((base == hasSib) || (base == hasSib2)) { -#else - if (base == hasSib) { -#endif - if (!offset) // No need to check if the base is noBase, since we know it is hasSib! - putModRmSib(ModRmMemoryNoDisp, reg, base, noIndex, 0); - else if (CAN_SIGN_EXTEND_8_32(offset)) { - putModRmSib(ModRmMemoryDisp8, reg, base, noIndex, 0); - m_buffer.putByteUnchecked(offset); - } else { - putModRmSib(ModRmMemoryDisp32, reg, base, noIndex, 0); - m_buffer.putIntUnchecked(offset); - } - } else { -#if CPU(X86_64) - if (!offset && (base != noBase) && (base != noBase2)) -#else - if (!offset && (base != noBase)) -#endif - putModRm(ModRmMemoryNoDisp, reg, base); - else if (CAN_SIGN_EXTEND_8_32(offset)) { - putModRm(ModRmMemoryDisp8, reg, base); - m_buffer.putByteUnchecked(offset); - } else { - putModRm(ModRmMemoryDisp32, reg, base); - m_buffer.putIntUnchecked(offset); - } - } - } - - void memoryModRM_disp8(int reg, RegisterID base, int offset) - { - // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. - ASSERT(CAN_SIGN_EXTEND_8_32(offset)); -#if CPU(X86_64) - if ((base == hasSib) || (base == hasSib2)) { -#else - if (base == hasSib) { -#endif - putModRmSib(ModRmMemoryDisp8, reg, base, noIndex, 0); - m_buffer.putByteUnchecked(offset); - } else { - putModRm(ModRmMemoryDisp8, reg, base); - m_buffer.putByteUnchecked(offset); - } - } - - void memoryModRM_disp32(int reg, RegisterID base, int offset) - { - // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. -#if CPU(X86_64) - if ((base == hasSib) || (base == hasSib2)) { -#else - if (base == hasSib) { -#endif - putModRmSib(ModRmMemoryDisp32, reg, base, noIndex, 0); - m_buffer.putIntUnchecked(offset); - } else { - putModRm(ModRmMemoryDisp32, reg, base); - m_buffer.putIntUnchecked(offset); - } - } - - void memoryModRM(int reg, RegisterID base, RegisterID index, int scale, int offset) - { - ASSERT(index != noIndex); - -#if CPU(X86_64) - if (!offset && (base != noBase) && (base != noBase2)) -#else - if (!offset && (base != noBase)) -#endif - putModRmSib(ModRmMemoryNoDisp, reg, base, index, scale); - else if (CAN_SIGN_EXTEND_8_32(offset)) { - putModRmSib(ModRmMemoryDisp8, reg, base, index, scale); - m_buffer.putByteUnchecked(offset); - } else { - putModRmSib(ModRmMemoryDisp32, reg, base, index, scale); - m_buffer.putIntUnchecked(offset); - } - } - -#if !CPU(X86_64) - void memoryModRM(int reg, const void* address) - { - // noBase + ModRmMemoryNoDisp means noBase + ModRmMemoryDisp32! - putModRm(ModRmMemoryNoDisp, reg, noBase); - m_buffer.putIntUnchecked(reinterpret_cast(address)); - } -#endif - - AssemblerBuffer m_buffer; - } m_formatter; - int m_indexOfLastWatchpoint; - int m_indexOfTailOfLastWatchpoint; -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) && CPU(X86) - -#endif // X86Assembler_h diff --git a/masm/config.h b/masm/config.h deleted file mode 100644 index 5f59f311e3..0000000000 --- a/masm/config.h +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef MASM_CONFIG_H -#define MASM_CONFIG_H - -#include -#ifdef __cplusplus -#include -#include -#include -#include -#else -#include -#endif -#include - -#endif // MASM_CONFIG_H diff --git a/masm/create_regex_tables b/masm/create_regex_tables deleted file mode 100644 index bd799ba044..0000000000 --- a/masm/create_regex_tables +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright (C) 2010 Apple Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import sys - -types = { - "wordchar": { "UseTable" : True, "data": ['_', ('0','9'), ('A', 'Z'), ('a','z')]}, - "nonwordchar": { "UseTable" : True, "Inverse": "wordchar", "data": ['`', (0, ord('0') - 1), (ord('9') + 1, ord('A') - 1), (ord('Z') + 1, ord('_') - 1), (ord('z') + 1, 0xffff)]}, - "newline": { "UseTable" : False, "data": ['\n', '\r', 0x2028, 0x2029]}, - "spaces": { "UseTable" : True, "data": [' ', ('\t', '\r'), 0xa0, 0x1680, 0x180e, 0x2028, 0x2029, 0x202f, 0x205f, 0x3000, (0x2000, 0x200a), 0xfeff]}, - "nonspaces": { "UseTable" : True, "Inverse": "spaces", "data": [(0, ord('\t') - 1), (ord('\r') + 1, ord(' ') - 1), (ord(' ') + 1, 0x009f), (0x00a1, 0x167f), (0x1681, 0x180d), (0x180f, 0x1fff), (0x200b, 0x2027), (0x202a, 0x202e), (0x2030, 0x205e), (0x2060, 0x2fff), (0x3001, 0xfefe), (0xff00, 0xffff)]}, - "digits": { "UseTable" : False, "data": [('0', '9')]}, - "nondigits": { "UseTable" : False, "Inverse": "digits", "data": [(0, ord('0') - 1), (ord('9') + 1, 0xffff)] } -} -entriesPerLine = 50 -arrays = ""; -functions = ""; -emitTables = (len(sys.argv) < 2 or sys.argv[1] != "--no-tables") - -for name, classes in types.items(): - ranges = []; - size = 0; - for _class in classes["data"]: - if type(_class) == str: - ranges.append((ord(_class), ord(_class))) - elif type(_class) == int: - ranges.append((_class, _class)) - else: - (min, max) = _class; - if type(min) == str: - min = ord(min) - if type(max) == str: - max = ord(max) - if max > 0x7f and min <= 0x7f: - ranges.append((min, 0x7f)) - min = 0x80 - ranges.append((min,max)) - ranges.sort(); - - if emitTables and classes["UseTable"] and (not "Inverse" in classes): - array = ("static const char _%sData[65536] = {\n" % name); - i = 0 - for (min,max) in ranges: - while i < min: - i = i + 1 - array += ('0,') - if (i % entriesPerLine == 0) and (i != 0): - array += ('\n') - while i <= max: - i = i + 1 - if (i == 65536): - array += ("1") - else: - array += ('1,') - if (i % entriesPerLine == 0) and (i != 0): - array += ('\n') - while i < 0xffff: - array += ("0,") - i = i + 1; - if (i % entriesPerLine == 0) and (i != 0): - array += ('\n') - if i == 0xffff: - array += ("0") - array += ("\n};\n\n"); - arrays += array - - # Generate createFunction: - function = ""; - function += ("CharacterClass* %sCreate()\n" % name) - function += ("{\n") - if emitTables and classes["UseTable"]: - if "Inverse" in classes: - function += (" CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_%sData, true));\n" % (classes["Inverse"])) - else: - function += (" CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_%sData, false));\n" % (name)) - else: - function += (" CharacterClass* characterClass = new CharacterClass(0);\n") - for (min, max) in ranges: - if (min == max): - if (min > 127): - function += (" characterClass->m_matchesUnicode.append(0x%04x);\n" % min) - else: - function += (" characterClass->m_matches.append(0x%02x);\n" % min) - continue - if (min > 127) or (max > 127): - function += (" characterClass->m_rangesUnicode.append(CharacterRange(0x%04x, 0x%04x));\n" % (min, max)) - else: - function += (" characterClass->m_ranges.append(CharacterRange(0x%02x, 0x%02x));\n" % (min, max)) - function += (" return characterClass;\n") - function += ("}\n\n") - functions += function - -if (len(sys.argv) > 1): - f = open(sys.argv[-1], "w") - f.write(arrays) - f.write(functions) - f.close() -else: - print(arrays) - print(functions) - diff --git a/masm/disassembler/Disassembler.cpp b/masm/disassembler/Disassembler.cpp deleted file mode 100644 index 3fed2cdab8..0000000000 --- a/masm/disassembler/Disassembler.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "Disassembler.h" - -#include "MacroAssemblerCodeRef.h" -#include - -namespace JSC { - -void disassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const char* prefix, PrintStream& out) -{ - if (tryToDisassemble(codePtr, size, prefix, out)) - return; - - out.printf("%sdisassembly not available for range %p...%p\n", prefix, codePtr.executableAddress(), static_cast(codePtr.executableAddress()) + size); -} - -} // namespace JSC - diff --git a/masm/disassembler/Disassembler.h b/masm/disassembler/Disassembler.h deleted file mode 100644 index a087a657b3..0000000000 --- a/masm/disassembler/Disassembler.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef Disassembler_h -#define Disassembler_h - -#include -#include - -namespace JSC { - -class MacroAssemblerCodePtr; - -#if ENABLE(DISASSEMBLER) -bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, PrintStream&); -#else -inline bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char*, PrintStream&) -{ - return false; -} -#endif - -// Prints either the disassembly, or a line of text indicating that disassembly failed and -// the range of machine code addresses. -void disassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, PrintStream& out); - -} // namespace JSC - -#endif // Disassembler_h - diff --git a/masm/disassembler/UDis86Disassembler.cpp b/masm/disassembler/UDis86Disassembler.cpp deleted file mode 100644 index 63c235b920..0000000000 --- a/masm/disassembler/UDis86Disassembler.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "Disassembler.h" - -#if USE(UDIS86) - -#include "MacroAssemblerCodeRef.h" -#include "udis86.h" - -namespace JSC { - -bool tryToDisassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const char* prefix, PrintStream& out) -{ - ud_t disassembler; - ud_init(&disassembler); - ud_set_input_buffer(&disassembler, static_cast(codePtr.executableAddress()), size); -#if CPU(X86_64) - ud_set_mode(&disassembler, 64); -#else - ud_set_mode(&disassembler, 32); -#endif - ud_set_pc(&disassembler, bitwise_cast(codePtr.executableAddress())); - ud_set_syntax(&disassembler, UD_SYN_ATT); - - uint64_t currentPC = disassembler.pc; - while (ud_disassemble(&disassembler)) { - char pcString[20]; - snprintf(pcString, sizeof(pcString), "0x%lx", static_cast(currentPC)); - out.printf("%s%16s: %s\n", prefix, pcString, ud_insn_asm(&disassembler)); - currentPC = disassembler.pc; - } - - return true; -} - -} // namespace JSC - -#endif // USE(UDIS86) - diff --git a/masm/disassembler/udis86/differences.txt b/masm/disassembler/udis86/differences.txt deleted file mode 100644 index dc225b6ffe..0000000000 --- a/masm/disassembler/udis86/differences.txt +++ /dev/null @@ -1,24 +0,0 @@ -This documents the differences between the stock version of udis86 and the one found -here: - -- All files not named "udis86" were prefixed with "udis86". - -- assert() has been changed to ASSERT() - -- Mass rename of udis86_input.h inp_ prefixed functions and macros to ud_inp_ to - avoid namespace pollution. - -- Removal of KERNEL checks. - -- Added #include of udis86_extern.h in udis86_decode.c. - -- Removed s_ie__pause and s_ie__nop from udis86_decode.c, since they weren't used. - -- Made udis86_syn.h use WTF_ATTRIBUTE_PRINTF. This required making a bunch of little - fixes to make the compiler's format string warnings go away. - -- Made the code in udis86_syn.h use vsnprintf() instead of vsprintf(). - -- Fixed udis86_syn-att.c's jump destination printing to work correctly in 64-bit mode. - -- Add --outputDir option to itab.py. diff --git a/masm/disassembler/udis86/itab.py b/masm/disassembler/udis86/itab.py deleted file mode 100644 index 07e20a6e10..0000000000 --- a/masm/disassembler/udis86/itab.py +++ /dev/null @@ -1,360 +0,0 @@ -# udis86 - scripts/itab.py -# -# Copyright (c) 2009 Vivek Thampi -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from optparse import OptionParser -import os -import sys - -sys.path.append( '../scripts' ); - -import ud_optable -import ud_opcode - -class UdItabGenerator( ud_opcode.UdOpcodeTables ): - - OperandDict = { - "Ap" : [ "OP_A" , "SZ_P" ], - "E" : [ "OP_E" , "SZ_NA" ], - "Eb" : [ "OP_E" , "SZ_B" ], - "Ew" : [ "OP_E" , "SZ_W" ], - "Ev" : [ "OP_E" , "SZ_V" ], - "Ed" : [ "OP_E" , "SZ_D" ], - "Eq" : [ "OP_E" , "SZ_Q" ], - "Ez" : [ "OP_E" , "SZ_Z" ], - "Ex" : [ "OP_E" , "SZ_MDQ" ], - "Ep" : [ "OP_E" , "SZ_P" ], - "G" : [ "OP_G" , "SZ_NA" ], - "Gb" : [ "OP_G" , "SZ_B" ], - "Gw" : [ "OP_G" , "SZ_W" ], - "Gv" : [ "OP_G" , "SZ_V" ], - "Gy" : [ "OP_G" , "SZ_MDQ" ], - "Gy" : [ "OP_G" , "SZ_MDQ" ], - "Gd" : [ "OP_G" , "SZ_D" ], - "Gq" : [ "OP_G" , "SZ_Q" ], - "Gx" : [ "OP_G" , "SZ_MDQ" ], - "Gz" : [ "OP_G" , "SZ_Z" ], - "M" : [ "OP_M" , "SZ_NA" ], - "Mb" : [ "OP_M" , "SZ_B" ], - "Mw" : [ "OP_M" , "SZ_W" ], - "Ms" : [ "OP_M" , "SZ_W" ], - "Md" : [ "OP_M" , "SZ_D" ], - "Mq" : [ "OP_M" , "SZ_Q" ], - "Mt" : [ "OP_M" , "SZ_T" ], - "Mo" : [ "OP_M" , "SZ_O" ], - "MwRv" : [ "OP_MR" , "SZ_WV" ], - "MdRy" : [ "OP_MR" , "SZ_DY" ], - "MbRv" : [ "OP_MR" , "SZ_BV" ], - "I1" : [ "OP_I1" , "SZ_NA" ], - "I3" : [ "OP_I3" , "SZ_NA" ], - "Ib" : [ "OP_I" , "SZ_B" ], - "Isb" : [ "OP_I" , "SZ_SB" ], - "Iw" : [ "OP_I" , "SZ_W" ], - "Iv" : [ "OP_I" , "SZ_V" ], - "Iz" : [ "OP_I" , "SZ_Z" ], - "Jv" : [ "OP_J" , "SZ_V" ], - "Jz" : [ "OP_J" , "SZ_Z" ], - "Jb" : [ "OP_J" , "SZ_B" ], - "R" : [ "OP_R" , "SZ_RDQ" ], - "C" : [ "OP_C" , "SZ_NA" ], - "D" : [ "OP_D" , "SZ_NA" ], - "S" : [ "OP_S" , "SZ_NA" ], - "Ob" : [ "OP_O" , "SZ_B" ], - "Ow" : [ "OP_O" , "SZ_W" ], - "Ov" : [ "OP_O" , "SZ_V" ], - "V" : [ "OP_V" , "SZ_O" ], - "W" : [ "OP_W" , "SZ_O" ], - "Wsd" : [ "OP_W" , "SZ_O" ], - "Wss" : [ "OP_W" , "SZ_O" ], - "P" : [ "OP_P" , "SZ_Q" ], - "Q" : [ "OP_Q" , "SZ_Q" ], - "VR" : [ "OP_VR" , "SZ_O" ], - "PR" : [ "OP_PR" , "SZ_Q" ], - "AL" : [ "OP_AL" , "SZ_NA" ], - "CL" : [ "OP_CL" , "SZ_NA" ], - "DL" : [ "OP_DL" , "SZ_NA" ], - "BL" : [ "OP_BL" , "SZ_NA" ], - "AH" : [ "OP_AH" , "SZ_NA" ], - "CH" : [ "OP_CH" , "SZ_NA" ], - "DH" : [ "OP_DH" , "SZ_NA" ], - "BH" : [ "OP_BH" , "SZ_NA" ], - "AX" : [ "OP_AX" , "SZ_NA" ], - "CX" : [ "OP_CX" , "SZ_NA" ], - "DX" : [ "OP_DX" , "SZ_NA" ], - "BX" : [ "OP_BX" , "SZ_NA" ], - "SI" : [ "OP_SI" , "SZ_NA" ], - "DI" : [ "OP_DI" , "SZ_NA" ], - "SP" : [ "OP_SP" , "SZ_NA" ], - "BP" : [ "OP_BP" , "SZ_NA" ], - "eAX" : [ "OP_eAX" , "SZ_NA" ], - "eCX" : [ "OP_eCX" , "SZ_NA" ], - "eDX" : [ "OP_eDX" , "SZ_NA" ], - "eBX" : [ "OP_eBX" , "SZ_NA" ], - "eSI" : [ "OP_eSI" , "SZ_NA" ], - "eDI" : [ "OP_eDI" , "SZ_NA" ], - "eSP" : [ "OP_eSP" , "SZ_NA" ], - "eBP" : [ "OP_eBP" , "SZ_NA" ], - "rAX" : [ "OP_rAX" , "SZ_NA" ], - "rCX" : [ "OP_rCX" , "SZ_NA" ], - "rBX" : [ "OP_rBX" , "SZ_NA" ], - "rDX" : [ "OP_rDX" , "SZ_NA" ], - "rSI" : [ "OP_rSI" , "SZ_NA" ], - "rDI" : [ "OP_rDI" , "SZ_NA" ], - "rSP" : [ "OP_rSP" , "SZ_NA" ], - "rBP" : [ "OP_rBP" , "SZ_NA" ], - "ES" : [ "OP_ES" , "SZ_NA" ], - "CS" : [ "OP_CS" , "SZ_NA" ], - "DS" : [ "OP_DS" , "SZ_NA" ], - "SS" : [ "OP_SS" , "SZ_NA" ], - "GS" : [ "OP_GS" , "SZ_NA" ], - "FS" : [ "OP_FS" , "SZ_NA" ], - "ST0" : [ "OP_ST0" , "SZ_NA" ], - "ST1" : [ "OP_ST1" , "SZ_NA" ], - "ST2" : [ "OP_ST2" , "SZ_NA" ], - "ST3" : [ "OP_ST3" , "SZ_NA" ], - "ST4" : [ "OP_ST4" , "SZ_NA" ], - "ST5" : [ "OP_ST5" , "SZ_NA" ], - "ST6" : [ "OP_ST6" , "SZ_NA" ], - "ST7" : [ "OP_ST7" , "SZ_NA" ], - "NONE" : [ "OP_NONE" , "SZ_NA" ], - "ALr8b" : [ "OP_ALr8b" , "SZ_NA" ], - "CLr9b" : [ "OP_CLr9b" , "SZ_NA" ], - "DLr10b" : [ "OP_DLr10b" , "SZ_NA" ], - "BLr11b" : [ "OP_BLr11b" , "SZ_NA" ], - "AHr12b" : [ "OP_AHr12b" , "SZ_NA" ], - "CHr13b" : [ "OP_CHr13b" , "SZ_NA" ], - "DHr14b" : [ "OP_DHr14b" , "SZ_NA" ], - "BHr15b" : [ "OP_BHr15b" , "SZ_NA" ], - "rAXr8" : [ "OP_rAXr8" , "SZ_NA" ], - "rCXr9" : [ "OP_rCXr9" , "SZ_NA" ], - "rDXr10" : [ "OP_rDXr10" , "SZ_NA" ], - "rBXr11" : [ "OP_rBXr11" , "SZ_NA" ], - "rSPr12" : [ "OP_rSPr12" , "SZ_NA" ], - "rBPr13" : [ "OP_rBPr13" , "SZ_NA" ], - "rSIr14" : [ "OP_rSIr14" , "SZ_NA" ], - "rDIr15" : [ "OP_rDIr15" , "SZ_NA" ], - "jWP" : [ "OP_J" , "SZ_WP" ], - "jDP" : [ "OP_J" , "SZ_DP" ], - - } - - # - # opcode prefix dictionary - # - PrefixDict = { - "aso" : "P_aso", - "oso" : "P_oso", - "rexw" : "P_rexw", - "rexb" : "P_rexb", - "rexx" : "P_rexx", - "rexr" : "P_rexr", - "seg" : "P_seg", - "inv64" : "P_inv64", - "def64" : "P_def64", - "depM" : "P_depM", - "cast1" : "P_c1", - "cast2" : "P_c2", - "cast3" : "P_c3", - "cast" : "P_cast", - "sext" : "P_sext" - } - - InvalidEntryIdx = 0 - InvalidEntry = { 'type' : 'invalid', - 'mnemonic' : 'invalid', - 'operands' : '', - 'prefixes' : '', - 'meta' : '' } - - Itab = [] # instruction table - ItabIdx = 1 # instruction table index - GtabIdx = 0 # group table index - GtabMeta = [] - - ItabLookup = {} - - MnemonicAliases = ( "invalid", "3dnow", "none", "db", "pause" ) - - def __init__( self, outputDir ): - # first itab entry (0) is Invalid - self.Itab.append( self.InvalidEntry ) - self.MnemonicsTable.extend( self.MnemonicAliases ) - self.outputDir = outputDir - - def toGroupId( self, id ): - return 0x8000 | id - - def genLookupTable( self, table, scope = '' ): - idxArray = [ ] - ( tabIdx, self.GtabIdx ) = ( self.GtabIdx, self.GtabIdx + 1 ) - self.GtabMeta.append( { 'type' : table[ 'type' ], 'meta' : table[ 'meta' ] } ) - - for _idx in range( self.sizeOfTable( table[ 'type' ] ) ): - idx = "%02x" % _idx - - e = self.InvalidEntry - i = self.InvalidEntryIdx - - if idx in table[ 'entries' ].keys(): - e = table[ 'entries' ][ idx ] - - # leaf node (insn) - if e[ 'type' ] == 'insn': - ( i, self.ItabIdx ) = ( self.ItabIdx, self.ItabIdx + 1 ) - self.Itab.append( e ) - elif e[ 'type' ] != 'invalid': - i = self.genLookupTable( e, 'static' ) - - idxArray.append( i ) - - name = "ud_itab__%s" % tabIdx - self.ItabLookup[ tabIdx ] = name - - self.ItabC.write( "\n" ); - if len( scope ): - self.ItabC.write( scope + ' ' ) - self.ItabC.write( "const uint16_t %s[] = {\n" % name ) - for i in range( len( idxArray ) ): - if i > 0 and i % 4 == 0: - self.ItabC.write( "\n" ) - if ( i%4 == 0 ): - self.ItabC.write( " /* %2x */" % i) - if idxArray[ i ] >= 0x8000: - self.ItabC.write( "%12s," % ("GROUP(%d)" % ( ~0x8000 & idxArray[ i ] ))) - else: - self.ItabC.write( "%12d," % ( idxArray[ i ] )) - self.ItabC.write( "\n" ) - self.ItabC.write( "};\n" ) - - return self.toGroupId( tabIdx ) - - def genLookupTableList( self ): - self.ItabC.write( "\n\n" ); - self.ItabC.write( "struct ud_lookup_table_list_entry ud_lookup_table_list[] = {\n" ) - for i in range( len( self.GtabMeta ) ): - f0 = self.ItabLookup[ i ] + "," - f1 = ( self.nameOfTable( self.GtabMeta[ i ][ 'type' ] ) ) + "," - f2 = "\"%s\"" % self.GtabMeta[ i ][ 'meta' ] - self.ItabC.write( " /* %03d */ { %s %s %s },\n" % ( i, f0, f1, f2 ) ) - self.ItabC.write( "};" ) - - def genInsnTable( self ): - self.ItabC.write( "struct ud_itab_entry ud_itab[] = {\n" ); - idx = 0 - for e in self.Itab: - opr_c = [ "O_NONE", "O_NONE", "O_NONE" ] - pfx_c = [] - opr = e[ 'operands' ] - for i in range(len(opr)): - if not (opr[i] in self.OperandDict.keys()): - print "error: invalid operand declaration: %s\n" % opr[i] - opr_c[i] = "O_" + opr[i] - opr = "%s %s %s" % (opr_c[0] + ",", opr_c[1] + ",", opr_c[2]) - - for p in e['prefixes']: - if not ( p in self.PrefixDict.keys() ): - print "error: invalid prefix specification: %s \n" % pfx - pfx_c.append( self.PrefixDict[p] ) - if len(e['prefixes']) == 0: - pfx_c.append( "P_none" ) - pfx = "|".join( pfx_c ) - - self.ItabC.write( " /* %04d */ { UD_I%s %s, %s },\n" \ - % ( idx, e[ 'mnemonic' ] + ',', opr, pfx ) ) - idx += 1 - self.ItabC.write( "};\n" ) - - self.ItabC.write( "\n\n" ); - self.ItabC.write( "const char * ud_mnemonics_str[] = {\n" ) - self.ItabC.write( ",\n ".join( [ "\"%s\"" % m for m in self.MnemonicsTable ] ) ) - self.ItabC.write( "\n};\n" ) - - - def genItabH( self ): - self.ItabH = open( os.path.join(self.outputDir, "udis86_itab.h"), "w" ) - - # Generate Table Type Enumeration - self.ItabH.write( "#ifndef UD_ITAB_H\n" ) - self.ItabH.write( "#define UD_ITAB_H\n\n" ) - - # table type enumeration - self.ItabH.write( "/* ud_table_type -- lookup table types (see lookup.c) */\n" ) - self.ItabH.write( "enum ud_table_type {\n " ) - enum = [ self.TableInfo[ k ][ 'name' ] for k in self.TableInfo.keys() ] - self.ItabH.write( ",\n ".join( enum ) ) - self.ItabH.write( "\n};\n\n" ); - - # mnemonic enumeration - self.ItabH.write( "/* ud_mnemonic -- mnemonic constants */\n" ) - enum = "enum ud_mnemonic_code {\n " - enum += ",\n ".join( [ "UD_I%s" % m for m in self.MnemonicsTable ] ) - enum += "\n} UD_ATTR_PACKED;\n" - self.ItabH.write( enum ) - self.ItabH.write( "\n" ) - - self.ItabH.write("\n/* itab entry operand definitions */\n"); - operands = self.OperandDict.keys() - operands.sort() - for o in operands: - self.ItabH.write("#define O_%-7s { %-12s %-8s }\n" % - (o, self.OperandDict[o][0] + ",", self.OperandDict[o][1])); - self.ItabH.write("\n\n"); - - self.ItabH.write( "extern const char * ud_mnemonics_str[];\n" ) - - self.ItabH.write( "#define GROUP(n) (0x8000 | (n))" ) - - self.ItabH.write( "\n#endif /* UD_ITAB_H */\n" ) - - self.ItabH.close() - - - def genItabC( self ): - self.ItabC = open( os.path.join(self.outputDir, "udis86_itab.c"), "w" ) - self.ItabC.write( "/* itab.c -- generated by itab.py, do no edit" ) - self.ItabC.write( " */\n" ); - self.ItabC.write( "#include \"udis86_decode.h\"\n\n" ); - - self.genLookupTable( self.OpcodeTable0 ) - self.genLookupTableList() - self.genInsnTable() - - self.ItabC.close() - - def genItab( self ): - self.genItabC() - self.genItabH() - -def main(): - parser = OptionParser() - parser.add_option("--outputDir", dest="outputDir", default="") - options, args = parser.parse_args() - generator = UdItabGenerator(os.path.normpath(options.outputDir)) - optableXmlParser = ud_optable.UdOptableXmlParser() - optableXmlParser.parse( args[ 0 ], generator.addInsnDef ) - - generator.genItab() - -if __name__ == '__main__': - main() diff --git a/masm/disassembler/udis86/optable.xml b/masm/disassembler/udis86/optable.xml deleted file mode 100644 index 14b4ac5935..0000000000 --- a/masm/disassembler/udis86/optable.xml +++ /dev/null @@ -1,8959 +0,0 @@ - - - - - - aaa - - 37 - inv64 - - - - - aad - - d5 - Ib - inv64 - - - - - aam - - d4 - Ib - inv64 - - - - - aas - - 3f - inv64 - - - - - adc - - aso rexr rexx rexb - 10 - Eb Gb - - - aso oso rexw rexr rexx rexb - 11 - Ev Gv - - - aso rexr rexx rexb - 12 - Gb Eb - - - aso oso rexw rexr rexx rexb - 13 - Gv Ev - - - 14 - AL Ib - - - oso rexw - 15 - rAX Iz - sext - - - aso rexr rexx rexb - 80 /reg=2 - Eb Ib - - - aso rexr rexx rexb - 82 /reg=2 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 81 /reg=2 - Ev Iz - sext - - - aso oso rexw rexr rexx rexb - 83 /reg=2 - Ev Ib - sext - - - - - add - - aso rexr rexx rexb - 00 - Eb Gb - - - aso oso rexw rexr rexx rexb - 01 - Ev Gv - - - aso rexr rexx rexb - 02 - Gb Eb - - - aso oso rexw rexr rexx rexb - 03 - Gv Ev - - - 04 - AL Ib - - - oso rexw - 05 - rAX Iz - sext - - - aso rexr rexx rexb - 80 /reg=0 - Eb Ib - - - aso rexr rexx rexb - 82 /reg=0 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 81 /reg=0 - Ev Iz - sext - - - aso oso rexw rexr rexx rexb - 83 /reg=0 - Ev Ib - sext - - - - - - - addpd - - aso rexr rexx rexb - sse66 0f 58 - V W - - - - - addps - - aso rexr rexx rexb - 0f 58 - V W - - - - - addsd - - aso rexr rexx rexb - ssef2 0f 58 - V W - - - - - addss - - aso rexr rexx rexb - ssef3 0f 58 - V W - - - - - and - - aso rexr rexx rexb - 20 - Eb Gb - - - aso oso rexw rexr rexx rexb - 21 - Ev Gv - - - aso rexr rexx rexb - 22 - Gb Eb - - - aso oso rexw rexr rexx rexb - 23 - Gv Ev - - - 24 - AL Ib - - - oso rexw - 25 - rAX Iz - sext - - - aso rexw rexr rexx rexb - 80 /reg=4 - Eb Ib - - - aso rexr rexx rexb - 82 /reg=4 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 81 /reg=4 - Ev Iz - sext - - - aso oso rexw rexr rexx rexb - 83 /reg=4 - Ev Ib - sext - - - - - andpd - - aso rexr rexx rexb - sse66 0f 54 - V W - - - - - andps - - aso rexr rexx rexb - 0f 54 - V W - - - - - andnpd - - aso rexr rexx rexb - sse66 0f 55 - V W - - - - - andnps - - aso rexr rexx rexb - 0f 55 - V W - - - - - arpl - - aso - 63 /m=16 - Ew Gw - inv64 - - - aso - 63 /m=32 - Ew Gw - inv64 - - - - - movsxd - - aso oso rexw rexx rexr rexb - 63 /m=64 - Gv Ed - - - - - bound - - aso oso - 62 - Gv M - inv64 - - - - - bsf - - aso oso rexw rexr rexx rexb - 0f bc - Gv Ev - - - - - bsr - - aso oso rexw rexr rexx rexb - 0f bd - Gv Ev - - - - - bswap - - oso rexw rexb - 0f c8 - rAXr8 - - - oso rexw rexb - 0f c9 - rCXr9 - - - oso rexw rexb - 0f ca - rDXr10 - - - oso rexw rexb - 0f cb - rBXr11 - - - oso rexw rexb - 0f cc - rSPr12 - - - oso rexw rexb - 0f cd - rBPr13 - - - oso rexw rexb - 0f ce - rSIr14 - - - oso rexw rexb - 0f cf - rDIr15 - - - - - bt - - aso oso rexw rexr rexx rexb - 0f ba /reg=4 - Ev Ib - - - aso oso rexw rexr rexx rexb - 0f a3 - Ev Gv - - - - - btc - - aso oso rexw rexr rexx rexb - 0f bb - Ev Gv - - - aso oso rexw rexr rexx rexb - 0f ba /reg=7 - Ev Ib - - - - - btr - - aso oso rexw rexr rexx rexb - 0f b3 - Ev Gv - - - aso oso rexw rexr rexx rexb - 0f ba /reg=6 - Ev Ib - - - - - bts - - aso oso rexw rexr rexx rexb - 0f ab - Ev Gv - - - aso oso rexw rexr rexx rexb - 0f ba /reg=5 - Ev Ib - - - - - call - - aso oso rexw rexr rexx rexb - ff /reg=2 - Ev - def64 - - - aso oso rexw rexr rexx rexb - ff /reg=3 - Ep - - - oso - e8 - Jz - def64 - - - oso - 9a - Ap - inv64 - - - - - cbw - - oso rexw - 98 /o=16 - - - - - cwde - - oso rexw - 98 /o=32 - - - - - cdqe - - oso rexw - 98 /o=64 - - - - - clc - - f8 - - - - - cld - - fc - - - - - clflush - - aso rexw rexr rexx rexb - 0f ae /reg=7 /mod=!11 - M - - - - - clgi - amd - - 0f 01 /reg=3 /mod=11 /rm=5 - - - - - cli - - fa - - - - - clts - - 0f 06 - - - - - cmc - - f5 - - - - - cmovo - - aso oso rexw rexr rexx rexb - 0f 40 - Gv Ev - - - - - cmovno - - aso oso rexw rexr rexx rexb - 0f 41 - Gv Ev - - - - - cmovb - - aso oso rexw rexr rexx rexb - 0f 42 - Gv Ev - - - - - cmovae - - aso oso rexw rexr rexx rexb - 0f 43 - Gv Ev - - - - - cmovz - - aso oso rexw rexr rexx rexb - 0f 44 - Gv Ev - - - - - cmovnz - - aso oso rexw rexr rexx rexb - 0f 45 - Gv Ev - - - - - cmovbe - - aso oso rexw rexr rexx rexb - 0f 46 - Gv Ev - - - - - cmova - - aso oso rexw rexr rexx rexb - 0f 47 - Gv Ev - - - - - cmovs - - aso oso rexw rexr rexx rexb - 0f 48 - Gv Ev - - - - - cmovns - - aso oso rexw rexr rexx rexb - 0f 49 - Gv Ev - - - - - cmovp - - aso oso rexw rexr rexx rexb - 0f 4a - Gv Ev - - - - - cmovnp - - aso oso rexw rexr rexx rexb - 0f 4b - Gv Ev - - - - - cmovl - - aso oso rexw rexr rexx rexb - 0f 4c - Gv Ev - - - - - cmovge - - aso oso rexw rexr rexx rexb - 0f 4d - Gv Ev - - - - - cmovle - - aso oso rexw rexr rexx rexb - 0f 4e - Gv Ev - - - - - cmovg - - aso oso rexw rexr rexx rexb - 0f 4f - Gv Ev - - - - - cmp - - aso rexr rexx rexb - 38 - Eb Gb - - - aso oso rexw rexr rexx rexb - 39 - Ev Gv - - - aso rexr rexx rexb - 3a - Gb Eb - - - aso oso rexw rexr rexx rexb - 3b - Gv Ev - - - 3c - AL Ib - - - oso rexw - 3d - rAX Iz - - - aso rexr rexx rexb - 80 /reg=7 - Eb Ib - - - aso rexr rexx rexb - 82 /reg=7 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 81 /reg=7 - Ev Iz - - - aso oso rexw rexr rexx rexb - 83 /reg=7 - Ev Ib - - - - - cmppd - - aso rexr rexx rexb - sse66 0f c2 - V W Ib - - - - - cmpps - - aso rexr rexx rexb - 0f c2 - V W Ib - - - - - cmpsb - - a6 - - - - - cmpsw - - oso rexw - a7 /o=16 - - - - - cmpsd - - oso rexw - a7 /o=32 - - - aso rexr rexx rexb - ssef2 0f c2 - V W Ib - - - - - cmpsq - - oso rexw - a7 /o=64 - - - - - cmpss - - aso rexr rexx rexb - ssef3 0f c2 - V W Ib - - - - - cmpxchg - - aso rexr rexx rexb - 0f b0 - Eb Gb - - - aso oso rexw rexr rexx rexb - 0f b1 - Ev Gv - - - - - cmpxchg8b - - aso rexr rexx rexb - 0f c7 /reg=1 - M - - - - - comisd - - aso rexr rexx rexb - sse66 0f 2f - V W - - - - - comiss - - aso rexr rexx rexb - 0f 2f - V W - - - - - cpuid - - 0f a2 - - - - - cvtdq2pd - - aso rexr rexx rexb - ssef3 0f e6 - V W - - - - - cvtdq2ps - - aso rexr rexx rexb - 0f 5b - V W - - - - - cvtpd2dq - - aso rexr rexx rexb - ssef2 0f e6 - V W - - - - - cvtpd2pi - - aso rexr rexx rexb - sse66 0f 2d - P W - - - - - cvtpd2ps - - aso rexr rexx rexb - sse66 0f 5a - V W - - - - - cvtpi2ps - - aso rexr rexx rexb - 0f 2a - V Q - - - - - cvtpi2pd - - aso rexr rexx rexb - sse66 0f 2a - V Q - - - - - cvtps2dq - - aso rexr rexx rexb - sse66 0f 5b - V W - - - - - cvtps2pi - - aso rexr rexx rexb - 0f 2d - P W - - - - - cvtps2pd - - aso rexr rexx rexb - 0f 5a - V W - - - - - cvtsd2si - - aso rexw rexr rexx rexb - ssef2 0f 2d - Gy W - - - - - cvtsd2ss - - aso rexr rexx rexb - ssef2 0f 5a - V W - - - - - cvtsi2ss - - aso rexw rexr rexx rexb - ssef3 0f 2a - V Ex - - - - - cvtss2si - - aso rexw rexr rexx rexb - ssef3 0f 2d - Gy W - - - - - cvtss2sd - - aso rexr rexx rexb - ssef3 0f 5a - V W - - - - - cvttpd2pi - - aso rexr rexx rexb - sse66 0f 2c - P W - - - - - cvttpd2dq - - aso rexr rexx rexb - sse66 0f e6 - V W - - - - - cvttps2dq - - aso rexr rexx rexb - ssef3 0f 5b - V W - - - - - cvttps2pi - - aso rexr rexx rexb - 0f 2c - P W - - - - - cvttsd2si - - aso rexw rexr rexx rexb - ssef2 0f 2c - Gy Wsd - - - - - cvtsi2sd - - aso rexw rexr rexx rexb - ssef2 0f 2a - V Ex - - - - - cvttss2si - - aso rexw rexr rexx rexb - ssef3 0f 2c - Gy Wsd - - - - - cwd - - oso rexw - 99 /o=16 - - - - - cdq - - oso rexw - 99 /o=32 - - - - - cqo - - oso rexw - 99 /o=64 - - - - - daa - - 27 - inv64 - - - - - das - - 2f - inv64 - - - - - dec - - oso - 48 - eAX - - - oso - 49 - eCX - - - oso - 4a - eDX - - - oso - 4b - eBX - - - oso - 4c - eSP - - - oso - 4d - eBP - - - oso - 4e - eSI - - - oso - 4f - eDI - - - aso rexw rexr rexx rexb - fe /reg=1 - Eb - - - aso oso rexw rexr rexx rexb - ff /reg=1 - Ev - - - - - div - - aso oso rexw rexr rexx rexb - f7 /reg=6 - Ev - - - aso rexw rexr rexx rexb - f6 /reg=6 - Eb - - - - - divpd - - aso rexr rexx rexb - sse66 0f 5e - V W - - - - - divps - - aso rexr rexx rexb - 0f 5e - V W - - - - - divsd - - aso rexr rexx rexb - ssef2 0f 5e - V W - - - - - divss - - aso rexr rexx rexb - ssef3 0f 5e - V W - - - - - emms - - 0f 77 - - - - - enter - - c8 - Iw Ib - def64 depM - - - - - f2xm1 - X87 - - d9 /mod=11 /x87=30 - - - - - fabs - X87 - - d9 /mod=11 /x87=21 - - - - - fadd - X87 - - aso rexr rexx rexb - dc /mod=!11 /reg=0 - Mq - - - aso rexr rexx rexb - d8 /mod=!11 /reg=0 - Md - - - dc /mod=11 /x87=00 - ST0 ST0 - - - dc /mod=11 /x87=01 - ST1 ST0 - - - dc /mod=11 /x87=02 - ST2 ST0 - - - dc /mod=11 /x87=03 - ST3 ST0 - - - dc /mod=11 /x87=04 - ST4 ST0 - - - dc /mod=11 /x87=05 - ST5 ST0 - - - dc /mod=11 /x87=06 - ST6 ST0 - - - dc /mod=11 /x87=07 - ST7 ST0 - - - d8 /mod=11 /x87=00 - ST0 ST0 - - - d8 /mod=11 /x87=01 - ST0 ST1 - - - d8 /mod=11 /x87=02 - ST0 ST2 - - - d8 /mod=11 /x87=03 - ST0 ST3 - - - d8 /mod=11 /x87=04 - ST0 ST4 - - - d8 /mod=11 /x87=05 - ST0 ST5 - - - d8 /mod=11 /x87=06 - ST0 ST6 - - - d8 /mod=11 /x87=07 - ST0 ST7 - - - - - faddp - X87 - - de /mod=11 /x87=00 - ST0 ST0 - - - de /mod=11 /x87=01 - ST1 ST0 - - - de /mod=11 /x87=02 - ST2 ST0 - - - de /mod=11 /x87=03 - ST3 ST0 - - - de /mod=11 /x87=04 - ST4 ST0 - - - de /mod=11 /x87=05 - ST5 ST0 - - - de /mod=11 /x87=06 - ST6 ST0 - - - de /mod=11 /x87=07 - ST7 ST0 - - - - - fbld - X87 - - aso rexr rexx rexb - df /mod=!11 /reg=4 - Mt - - - - - fbstp - X87 - - aso rexr rexx rexb - df /mod=!11 /reg=6 - Mt - - - - - fchs - X87 - - d9 /mod=11 /x87=20 - - - - - fclex - X87 - - db /mod=11 /x87=22 - - - - - fcmovb - X87 - - da /mod=11 /x87=00 - ST0 ST0 - - - da /mod=11 /x87=01 - ST0 ST1 - - - da /mod=11 /x87=02 - ST0 ST2 - - - da /mod=11 /x87=03 - ST0 ST3 - - - da /mod=11 /x87=04 - ST0 ST4 - - - da /mod=11 /x87=05 - ST0 ST5 - - - da /mod=11 /x87=06 - ST0 ST6 - - - da /mod=11 /x87=07 - ST0 ST7 - - - - - fcmove - X87 - - da /mod=11 /x87=08 - ST0 ST0 - - - da /mod=11 /x87=09 - ST0 ST1 - - - da /mod=11 /x87=0a - ST0 ST2 - - - da /mod=11 /x87=0b - ST0 ST3 - - - da /mod=11 /x87=0c - ST0 ST4 - - - da /mod=11 /x87=0d - ST0 ST5 - - - da /mod=11 /x87=0e - ST0 ST6 - - - da /mod=11 /x87=0f - ST0 ST7 - - - - - fcmovbe - X87 - - da /mod=11 /x87=10 - ST0 ST0 - - - da /mod=11 /x87=11 - ST0 ST1 - - - da /mod=11 /x87=12 - ST0 ST2 - - - da /mod=11 /x87=13 - ST0 ST3 - - - da /mod=11 /x87=14 - ST0 ST4 - - - da /mod=11 /x87=15 - ST0 ST5 - - - da /mod=11 /x87=16 - ST0 ST6 - - - da /mod=11 /x87=17 - ST0 ST7 - - - - - fcmovu - X87 - - da /mod=11 /x87=18 - ST0 ST0 - - - da /mod=11 /x87=19 - ST0 ST1 - - - da /mod=11 /x87=1a - ST0 ST2 - - - da /mod=11 /x87=1b - ST0 ST3 - - - da /mod=11 /x87=1c - ST0 ST4 - - - da /mod=11 /x87=1d - ST0 ST5 - - - da /mod=11 /x87=1e - ST0 ST6 - - - da /mod=11 /x87=1f - ST0 ST7 - - - - - fcmovnb - X87 - - db /mod=11 /x87=00 - ST0 ST0 - - - db /mod=11 /x87=01 - ST0 ST1 - - - db /mod=11 /x87=02 - ST0 ST2 - - - db /mod=11 /x87=03 - ST0 ST3 - - - db /mod=11 /x87=04 - ST0 ST4 - - - db /mod=11 /x87=05 - ST0 ST5 - - - db /mod=11 /x87=06 - ST0 ST6 - - - db /mod=11 /x87=07 - ST0 ST7 - - - - - fcmovne - X87 - - db /mod=11 /x87=08 - ST0 ST0 - - - db /mod=11 /x87=09 - ST0 ST1 - - - db /mod=11 /x87=0a - ST0 ST2 - - - db /mod=11 /x87=0b - ST0 ST3 - - - db /mod=11 /x87=0c - ST0 ST4 - - - db /mod=11 /x87=0d - ST0 ST5 - - - db /mod=11 /x87=0e - ST0 ST6 - - - db /mod=11 /x87=0f - ST0 ST7 - - - - - fcmovnbe - X87 - - db /mod=11 /x87=10 - ST0 ST0 - - - db /mod=11 /x87=11 - ST0 ST1 - - - db /mod=11 /x87=12 - ST0 ST2 - - - db /mod=11 /x87=13 - ST0 ST3 - - - db /mod=11 /x87=14 - ST0 ST4 - - - db /mod=11 /x87=15 - ST0 ST5 - - - db /mod=11 /x87=16 - ST0 ST6 - - - db /mod=11 /x87=17 - ST0 ST7 - - - - - fcmovnu - X87 - - db /mod=11 /x87=18 - ST0 ST0 - - - db /mod=11 /x87=19 - ST0 ST1 - - - db /mod=11 /x87=1a - ST0 ST2 - - - db /mod=11 /x87=1b - ST0 ST3 - - - db /mod=11 /x87=1c - ST0 ST4 - - - db /mod=11 /x87=1d - ST0 ST5 - - - db /mod=11 /x87=1e - ST0 ST6 - - - db /mod=11 /x87=1f - ST0 ST7 - - - - - fucomi - X87 - - db /mod=11 /x87=28 - ST0 ST0 - - - db /mod=11 /x87=29 - ST0 ST1 - - - db /mod=11 /x87=2a - ST0 ST2 - - - db /mod=11 /x87=2b - ST0 ST3 - - - db /mod=11 /x87=2c - ST0 ST4 - - - db /mod=11 /x87=2d - ST0 ST5 - - - db /mod=11 /x87=2e - ST0 ST6 - - - db /mod=11 /x87=2f - ST0 ST7 - - - - - fcom - X87 - - aso rexr rexx rexb - d8 /mod=!11 /reg=2 - Md - - - aso rexr rexx rexb - dc /mod=!11 /reg=2 - Mq - - - d8 /mod=11 /x87=10 - ST0 ST0 - - - d8 /mod=11 /x87=11 - ST0 ST1 - - - d8 /mod=11 /x87=12 - ST0 ST2 - - - d8 /mod=11 /x87=13 - ST0 ST3 - - - d8 /mod=11 /x87=14 - ST0 ST4 - - - d8 /mod=11 /x87=15 - ST0 ST5 - - - d8 /mod=11 /x87=16 - ST0 ST6 - - - d8 /mod=11 /x87=17 - ST0 ST7 - - - - - fcom2 - X87 UNDOC - - dc /mod=11 /x87=10 - ST0 - - - dc /mod=11 /x87=11 - ST1 - - - dc /mod=11 /x87=12 - ST2 - - - dc /mod=11 /x87=13 - ST3 - - - dc /mod=11 /x87=14 - ST4 - - - dc /mod=11 /x87=15 - ST5 - - - dc /mod=11 /x87=16 - ST6 - - - dc /mod=11 /x87=17 - ST7 - - - - - fcomp3 - X87 UNDOC - - dc /mod=11 /x87=18 - ST0 - - - dc /mod=11 /x87=19 - ST1 - - - dc /mod=11 /x87=1a - ST2 - - - dc /mod=11 /x87=1b - ST3 - - - dc /mod=11 /x87=1c - ST4 - - - dc /mod=11 /x87=1d - ST5 - - - dc /mod=11 /x87=1e - ST6 - - - dc /mod=11 /x87=1f - ST7 - - - - - fcomi - X87 - - db /mod=11 /x87=30 - ST0 ST0 - - - db /mod=11 /x87=31 - ST0 ST1 - - - db /mod=11 /x87=32 - ST0 ST2 - - - db /mod=11 /x87=33 - ST0 ST3 - - - db /mod=11 /x87=34 - ST0 ST4 - - - db /mod=11 /x87=35 - ST0 ST5 - - - db /mod=11 /x87=36 - ST0 ST6 - - - db /mod=11 /x87=37 - ST0 ST7 - - - - - fucomip - X87 - - df /mod=11 /x87=28 - ST0 ST0 - - - df /mod=11 /x87=29 - ST0 ST1 - - - df /mod=11 /x87=2a - ST0 ST2 - - - df /mod=11 /x87=2b - ST0 ST3 - - - df /mod=11 /x87=2c - ST0 ST4 - - - df /mod=11 /x87=2d - ST0 ST5 - - - df /mod=11 /x87=2e - ST0 ST6 - - - df /mod=11 /x87=2f - ST0 ST7 - - - - - fcomip - X87 - - df /mod=11 /x87=30 - ST0 ST0 - - - df /mod=11 /x87=31 - ST0 ST1 - - - df /mod=11 /x87=32 - ST0 ST2 - - - df /mod=11 /x87=33 - ST0 ST3 - - - df /mod=11 /x87=34 - ST0 ST4 - - - df /mod=11 /x87=35 - ST0 ST5 - - - df /mod=11 /x87=36 - ST0 ST6 - - - df /mod=11 /x87=37 - ST0 ST7 - - - - - fcomp - X87 - - aso rexr rexx rexb - d8 /mod=!11 /reg=3 - Md - - - aso rexr rexx rexb - dc /mod=!11 /reg=3 - Mq - - - d8 /mod=11 /x87=18 - ST0 ST0 - - - d8 /mod=11 /x87=19 - ST0 ST1 - - - d8 /mod=11 /x87=1a - ST0 ST2 - - - d8 /mod=11 /x87=1b - ST0 ST3 - - - d8 /mod=11 /x87=1c - ST0 ST4 - - - d8 /mod=11 /x87=1d - ST0 ST5 - - - d8 /mod=11 /x87=1e - ST0 ST6 - - - d8 /mod=11 /x87=1f - ST0 ST7 - - - - - fcomp5 - X87 UNDOC - - de /mod=11 /x87=10 - ST0 - - - de /mod=11 /x87=11 - ST1 - - - de /mod=11 /x87=12 - ST2 - - - de /mod=11 /x87=13 - ST3 - - - de /mod=11 /x87=14 - ST4 - - - de /mod=11 /x87=15 - ST5 - - - de /mod=11 /x87=16 - ST6 - - - de /mod=11 /x87=17 - ST7 - - - - - fcompp - X87 - - de /mod=11 /x87=19 - - - - - fcos - X87 - - d9 /mod=11 /x87=3f - - - - - fdecstp - X87 - - d9 /mod=11 /x87=36 - - - - - fdiv - X87 - - aso rexr rexx rexb - dc /mod=!11 /reg=6 - Mq - - - dc /mod=11 /x87=38 - ST0 ST0 - - - dc /mod=11 /x87=39 - ST1 ST0 - - - dc /mod=11 /x87=3a - ST2 ST0 - - - dc /mod=11 /x87=3b - ST3 ST0 - - - dc /mod=11 /x87=3c - ST4 ST0 - - - dc /mod=11 /x87=3d - ST5 ST0 - - - dc /mod=11 /x87=3e - ST6 ST0 - - - dc /mod=11 /x87=3f - ST7 ST0 - - - aso rexr rexx rexb - d8 /mod=!11 /reg=6 - Md - - - d8 /mod=11 /x87=30 - ST0 ST0 - - - d8 /mod=11 /x87=31 - ST0 ST1 - - - d8 /mod=11 /x87=32 - ST0 ST2 - - - d8 /mod=11 /x87=33 - ST0 ST3 - - - d8 /mod=11 /x87=34 - ST0 ST4 - - - d8 /mod=11 /x87=35 - ST0 ST5 - - - d8 /mod=11 /x87=36 - ST0 ST6 - - - d8 /mod=11 /x87=37 - ST0 ST7 - - - - - fdivp - X87 - - de /mod=11 /x87=38 - ST0 ST0 - - - de /mod=11 /x87=39 - ST1 ST0 - - - de /mod=11 /x87=3a - ST2 ST0 - - - de /mod=11 /x87=3b - ST3 ST0 - - - de /mod=11 /x87=3c - ST4 ST0 - - - de /mod=11 /x87=3d - ST5 ST0 - - - de /mod=11 /x87=3e - ST6 ST0 - - - de /mod=11 /x87=3f - ST7 ST0 - - - - - fdivr - X87 - - aso rexr rexx rexb - dc /mod=!11 /reg=7 - Mq - - - dc /mod=11 /x87=30 - ST0 ST0 - - - dc /mod=11 /x87=31 - ST1 ST0 - - - dc /mod=11 /x87=32 - ST2 ST0 - - - dc /mod=11 /x87=33 - ST3 ST0 - - - dc /mod=11 /x87=34 - ST4 ST0 - - - dc /mod=11 /x87=35 - ST5 ST0 - - - dc /mod=11 /x87=36 - ST6 ST0 - - - dc /mod=11 /x87=37 - ST7 ST0 - - - aso rexr rexx rexb - d8 /mod=!11 /reg=7 - Md - - - d8 /mod=11 /x87=38 - ST0 ST0 - - - d8 /mod=11 /x87=39 - ST0 ST1 - - - d8 /mod=11 /x87=3a - ST0 ST2 - - - d8 /mod=11 /x87=3b - ST0 ST3 - - - d8 /mod=11 /x87=3c - ST0 ST4 - - - d8 /mod=11 /x87=3d - ST0 ST5 - - - d8 /mod=11 /x87=3e - ST0 ST6 - - - d8 /mod=11 /x87=3f - ST0 ST7 - - - - - fdivrp - X87 - - de /mod=11 /x87=30 - ST0 ST0 - - - de /mod=11 /x87=31 - ST1 ST0 - - - de /mod=11 /x87=32 - ST2 ST0 - - - de /mod=11 /x87=33 - ST3 ST0 - - - de /mod=11 /x87=34 - ST4 ST0 - - - de /mod=11 /x87=35 - ST5 ST0 - - - de /mod=11 /x87=36 - ST6 ST0 - - - de /mod=11 /x87=37 - ST7 ST0 - - - - - femms - - 0f 0e - - - - - ffree - X87 - - dd /mod=11 /x87=00 - ST0 - - - dd /mod=11 /x87=01 - ST1 - - - dd /mod=11 /x87=02 - ST2 - - - dd /mod=11 /x87=03 - ST3 - - - dd /mod=11 /x87=04 - ST4 - - - dd /mod=11 /x87=05 - ST5 - - - dd /mod=11 /x87=06 - ST6 - - - dd /mod=11 /x87=07 - ST7 - - - - - ffreep - X87 - - df /mod=11 /x87=00 - ST0 - - - df /mod=11 /x87=01 - ST1 - - - df /mod=11 /x87=02 - ST2 - - - df /mod=11 /x87=03 - ST3 - - - df /mod=11 /x87=04 - ST4 - - - df /mod=11 /x87=05 - ST5 - - - df /mod=11 /x87=06 - ST6 - - - df /mod=11 /x87=07 - ST7 - - - - - ficom - X87 - - aso rexr rexx rexb - de /mod=!11 /reg=2 - Mw - - - aso rexr rexx rexb - da /mod=!11 /reg=2 - Md - - - - - ficomp - X87 - - aso rexr rexx rexb - de /mod=!11 /reg=3 - Mw - - - aso rexr rexx rexb - da /mod=!11 /reg=3 - Md - - - - - fild - X87 - - aso rexr rexx rexb - df /mod=!11 /reg=0 - Mw - - - aso rexr rexx rexb - df /mod=!11 /reg=5 - Mq - - - aso rexr rexx rexb - db /mod=!11 /reg=0 - Md - - - - - fncstp - X87 - - d9 /mod=11 /x87=37 - - - - - fninit - X87 - - db /mod=11 /x87=23 - - - - - fiadd - X87 - - aso rexr rexx rexb - da /mod=!11 /reg=0 - Md - - - aso rexr rexx rexb - de /mod=!11 /reg=0 - Mw - - - - - fidivr - X87 - - aso rexr rexx rexb - da /mod=!11 /reg=7 - Md - - - aso rexr rexx rexb - de /mod=!11 /reg=7 - Mw - - - - - fidiv - X87 - - aso rexr rexx rexb - da /mod=!11 /reg=6 - Md - - - aso rexr rexx rexb - de /mod=!11 /reg=6 - Mw - - - - - fisub - X87 - - aso rexr rexx rexb - da /mod=!11 /reg=4 - Md - - - aso rexr rexx rexb - de /mod=!11 /reg=4 - Mw - - - - - fisubr - X87 - - aso rexr rexx rexb - da /mod=!11 /reg=5 - Md - - - aso rexr rexx rexb - de /mod=!11 /reg=5 - Mw - - - - - fist - X87 - - aso rexr rexx rexb - df /mod=!11 /reg=2 - Mw - - - aso rexr rexx rexb - db /mod=!11 /reg=2 - Md - - - - - fistp - X87 - - aso rexr rexx rexb - df /mod=!11 /reg=3 - Mw - - - aso rexr rexx rexb - df /mod=!11 /reg=7 - Mq - - - aso rexr rexx rexb - db /mod=!11 /reg=3 - Md - - - - - fisttp - X87 - - aso rexr rexx rexb - db /mod=!11 /reg=1 - Md - - - aso rexr rexx rexb - dd /mod=!11 /reg=1 - Mq - - - aso rexr rexx rexb - df /mod=!11 /reg=1 - Mw - - - - - fld - X87 - - aso rexr rexx rexb - db /mod=!11 /reg=5 - Mt - - - aso rexr rexx rexb - dd /mod=!11 /reg=0 - Mq - - - aso rexr rexx rexb - d9 /mod=!11 /reg=0 - Md - - - d9 /mod=11 /x87=00 - ST0 - - - d9 /mod=11 /x87=01 - ST1 - - - d9 /mod=11 /x87=02 - ST2 - - - d9 /mod=11 /x87=03 - ST3 - - - d9 /mod=11 /x87=04 - ST4 - - - d9 /mod=11 /x87=05 - ST5 - - - d9 /mod=11 /x87=06 - ST6 - - - d9 /mod=11 /x87=07 - ST7 - - - - - fld1 - X87 - - d9 /mod=11 /x87=28 - - - - - fldl2t - X87 - - d9 /mod=11 /x87=29 - - - - - fldl2e - X87 - - d9 /mod=11 /x87=2a - - - - - fldlpi - X87 - - d9 /mod=11 /x87=2b - - - - - fldlg2 - X87 - - d9 /mod=11 /x87=2c - - - - - fldln2 - X87 - - d9 /mod=11 /x87=2d - - - - - fldz - X87 - - d9 /mod=11 /x87=2e - - - - - fldcw - X87 - - aso rexr rexx rexb - d9 /mod=!11 /reg=5 - Mw - - - - - fldenv - X87 - - aso rexr rexx rexb - d9 /mod=!11 /reg=4 - M - - - - - fmul - X87 - - aso rexr rexx rexb - dc /mod=!11 /reg=1 - Mq - - - dc /mod=11 /x87=08 - ST0 ST0 - - - dc /mod=11 /x87=09 - ST1 ST0 - - - dc /mod=11 /x87=0a - ST2 ST0 - - - dc /mod=11 /x87=0b - ST3 ST0 - - - dc /mod=11 /x87=0c - ST4 ST0 - - - dc /mod=11 /x87=0d - ST5 ST0 - - - dc /mod=11 /x87=0e - ST6 ST0 - - - dc /mod=11 /x87=0f - ST7 ST0 - - - aso rexr rexx rexb - d8 /mod=!11 /reg=1 - Md - - - d8 /mod=11 /x87=08 - ST0 ST0 - - - d8 /mod=11 /x87=09 - ST0 ST1 - - - d8 /mod=11 /x87=0a - ST0 ST2 - - - d8 /mod=11 /x87=0b - ST0 ST3 - - - d8 /mod=11 /x87=0c - ST0 ST4 - - - d8 /mod=11 /x87=0d - ST0 ST5 - - - d8 /mod=11 /x87=0e - ST0 ST6 - - - d8 /mod=11 /x87=0f - ST0 ST7 - - - - - fmulp - X87 - - de /mod=11 /x87=08 - ST0 ST0 - - - de /mod=11 /x87=09 - ST1 ST0 - - - de /mod=11 /x87=0a - ST2 ST0 - - - de /mod=11 /x87=0b - ST3 ST0 - - - de /mod=11 /x87=0c - ST4 ST0 - - - de /mod=11 /x87=0d - ST5 ST0 - - - de /mod=11 /x87=0e - ST6 ST0 - - - de /mod=11 /x87=0f - ST7 ST0 - - - - - fimul - X87 - - aso rexr rexx rexb - da /mod=!11 /reg=1 - Md - - - aso rexr rexx rexb - de /mod=!11 /reg=1 - Mw - - - - - fnop - X87 - - d9 /mod=11 /x87=10 - - - - - fpatan - X87 - - d9 /mod=11 /x87=33 - - - - - fprem - X87 - - d9 /mod=11 /x87=38 - - - - - fprem1 - X87 - - d9 /mod=11 /x87=35 - - - - - fptan - X87 - - d9 /mod=11 /x87=32 - - - - - frndint - X87 - - d9 /mod=11 /x87=3c - - - - - frstor - X87 - - aso rexr rexx rexb - dd /mod=!11 /reg=4 - M - - - - - fnsave - X87 - - aso rexr rexx rexb - dd /mod=!11 /reg=6 - M - - - - - fscale - X87 - - d9 /mod=11 /x87=3d - - - - - fsin - X87 - - d9 /mod=11 /x87=3e - - - - - fsincos - X87 - - d9 /mod=11 /x87=3b - - - - - fsqrt - X87 - - d9 /mod=11 /x87=3a - - - - - fstp - X87 - - aso rexr rexx rexb - db /mod=!11 /reg=7 - Mt - - - aso rexr rexx rexb - dd /mod=!11 /reg=3 - Mq - - - aso rexr rexx rexb - d9 /mod=!11 /reg=3 - Md - - - dd /mod=11 /x87=18 - ST0 - - - dd /mod=11 /x87=19 - ST1 - - - dd /mod=11 /x87=1a - ST2 - - - dd /mod=11 /x87=1b - ST3 - - - dd /mod=11 /x87=1c - ST4 - - - dd /mod=11 /x87=1d - ST5 - - - dd /mod=11 /x87=1e - ST6 - - - dd /mod=11 /x87=1f - ST7 - - - - - fstp1 - - d9 /mod=11 /x87=18 - ST0 - - - d9 /mod=11 /x87=19 - ST1 - - - d9 /mod=11 /x87=1a - ST2 - - - d9 /mod=11 /x87=1b - ST3 - - - d9 /mod=11 /x87=1c - ST4 - - - d9 /mod=11 /x87=1d - ST5 - - - d9 /mod=11 /x87=1e - ST6 - - - d9 /mod=11 /x87=1f - ST7 - - - - - fstp8 - - df /mod=11 /x87=10 - ST0 - - - df /mod=11 /x87=11 - ST1 - - - df /mod=11 /x87=12 - ST2 - - - df /mod=11 /x87=13 - ST3 - - - df /mod=11 /x87=14 - ST4 - - - df /mod=11 /x87=15 - ST5 - - - df /mod=11 /x87=16 - ST6 - - - df /mod=11 /x87=17 - ST7 - - - - - fstp9 - - df /mod=11 /x87=18 - ST0 - - - df /mod=11 /x87=19 - ST1 - - - df /mod=11 /x87=1a - ST2 - - - df /mod=11 /x87=1b - ST3 - - - df /mod=11 /x87=1c - ST4 - - - df /mod=11 /x87=1d - ST5 - - - df /mod=11 /x87=1e - ST6 - - - df /mod=11 /x87=1f - ST7 - - - - - fst - X87 - - aso rexr rexx rexb - d9 /mod=!11 /reg=2 - Md - - - aso rexr rexx rexb - dd /mod=!11 /reg=2 - Mq - - - dd /mod=11 /x87=10 - ST0 - - - dd /mod=11 /x87=11 - ST1 - - - dd /mod=11 /x87=12 - ST2 - - - dd /mod=11 /x87=13 - ST3 - - - dd /mod=11 /x87=14 - ST4 - - - dd /mod=11 /x87=15 - ST5 - - - dd /mod=11 /x87=16 - ST6 - - - dd /mod=11 /x87=17 - ST7 - - - - - fnstcw - X87 - - aso rexr rexx rexb - d9 /mod=!11 /reg=7 - Mw - - - - - fnstenv - X87 - - aso rexr rexx rexb - d9 /mod=!11 /reg=6 - M - - - - - fnstsw - X87 - - aso rexr rexx rexb - dd /mod=!11 /reg=7 - Mw - - - df /mod=11 /x87=20 - AX - - - - - fsub - X87 - - aso rexr rexx rexb - d8 /mod=!11 /reg=4 - Md - - - aso rexr rexx rexb - dc /mod=!11 /reg=4 - Mq - - - d8 /mod=11 /x87=20 - ST0 ST0 - - - d8 /mod=11 /x87=21 - ST0 ST1 - - - d8 /mod=11 /x87=22 - ST0 ST2 - - - d8 /mod=11 /x87=23 - ST0 ST3 - - - d8 /mod=11 /x87=24 - ST0 ST4 - - - d8 /mod=11 /x87=25 - ST0 ST5 - - - d8 /mod=11 /x87=26 - ST0 ST6 - - - d8 /mod=11 /x87=27 - ST0 ST7 - - - dc /mod=11 /x87=28 - ST0 ST0 - - - dc /mod=11 /x87=29 - ST1 ST0 - - - dc /mod=11 /x87=2a - ST2 ST0 - - - dc /mod=11 /x87=2b - ST3 ST0 - - - dc /mod=11 /x87=2c - ST4 ST0 - - - dc /mod=11 /x87=2d - ST5 ST0 - - - dc /mod=11 /x87=2e - ST6 ST0 - - - dc /mod=11 /x87=2f - ST7 ST0 - - - - - fsubp - X87 - - de /mod=11 /x87=28 - ST0 ST0 - - - de /mod=11 /x87=29 - ST1 ST0 - - - de /mod=11 /x87=2a - ST2 ST0 - - - de /mod=11 /x87=2b - ST3 ST0 - - - de /mod=11 /x87=2c - ST4 ST0 - - - de /mod=11 /x87=2d - ST5 ST0 - - - de /mod=11 /x87=2e - ST6 ST0 - - - de /mod=11 /x87=2f - ST7 ST0 - - - - - fsubr - X87 - - aso rexr rexx rexb - dc /mod=!11 /reg=5 - Mq - - - d8 /mod=11 /x87=28 - ST0 ST0 - - - d8 /mod=11 /x87=29 - ST0 ST1 - - - d8 /mod=11 /x87=2a - ST0 ST2 - - - d8 /mod=11 /x87=2b - ST0 ST3 - - - d8 /mod=11 /x87=2c - ST0 ST4 - - - d8 /mod=11 /x87=2d - ST0 ST5 - - - d8 /mod=11 /x87=2e - ST0 ST6 - - - d8 /mod=11 /x87=2f - ST0 ST7 - - - dc /mod=11 /x87=20 - ST0 ST0 - - - dc /mod=11 /x87=21 - ST1 ST0 - - - dc /mod=11 /x87=22 - ST2 ST0 - - - dc /mod=11 /x87=23 - ST3 ST0 - - - dc /mod=11 /x87=24 - ST4 ST0 - - - dc /mod=11 /x87=25 - ST5 ST0 - - - dc /mod=11 /x87=26 - ST6 ST0 - - - dc /mod=11 /x87=27 - ST7 ST0 - - - aso rexr rexx rexb - d8 /mod=!11 /reg=5 - Md - - - - - fsubrp - X87 - - de /mod=11 /x87=20 - ST0 ST0 - - - de /mod=11 /x87=21 - ST1 ST0 - - - de /mod=11 /x87=22 - ST2 ST0 - - - de /mod=11 /x87=23 - ST3 ST0 - - - de /mod=11 /x87=24 - ST4 ST0 - - - de /mod=11 /x87=25 - ST5 ST0 - - - de /mod=11 /x87=26 - ST6 ST0 - - - de /mod=11 /x87=27 - ST7 ST0 - - - - - ftst - X87 - - d9 /mod=11 /x87=24 - - - - - fucom - X87 - - dd /mod=11 /x87=20 - ST0 - - - dd /mod=11 /x87=21 - ST1 - - - dd /mod=11 /x87=22 - ST2 - - - dd /mod=11 /x87=23 - ST3 - - - dd /mod=11 /x87=24 - ST4 - - - dd /mod=11 /x87=25 - ST5 - - - dd /mod=11 /x87=26 - ST6 - - - dd /mod=11 /x87=27 - ST7 - - - - - fucomp - X87 - - dd /mod=11 /x87=28 - ST0 - - - dd /mod=11 /x87=29 - ST1 - - - dd /mod=11 /x87=2a - ST2 - - - dd /mod=11 /x87=2b - ST3 - - - dd /mod=11 /x87=2c - ST4 - - - dd /mod=11 /x87=2d - ST5 - - - dd /mod=11 /x87=2e - ST6 - - - dd /mod=11 /x87=2f - ST7 - - - - - fucompp - X87 - - da /mod=11 /x87=29 - - - - - fxam - X87 - - d9 /mod=11 /x87=25 - - - - - fxch - X87 - - d9 /mod=11 /x87=08 - ST0 ST0 - - - d9 /mod=11 /x87=09 - ST0 ST1 - - - d9 /mod=11 /x87=0a - ST0 ST2 - - - d9 /mod=11 /x87=0b - ST0 ST3 - - - d9 /mod=11 /x87=0c - ST0 ST4 - - - d9 /mod=11 /x87=0d - ST0 ST5 - - - d9 /mod=11 /x87=0e - ST0 ST6 - - - d9 /mod=11 /x87=0f - ST0 ST7 - - - - - fxch4 - X87 - - dd /mod=11 /x87=08 - ST0 - - - dd /mod=11 /x87=09 - ST1 - - - dd /mod=11 /x87=0a - ST2 - - - dd /mod=11 /x87=0b - ST3 - - - dd /mod=11 /x87=0c - ST4 - - - dd /mod=11 /x87=0d - ST5 - - - dd /mod=11 /x87=0e - ST6 - - - dd /mod=11 /x87=0f - ST7 - - - - - fxch7 - X87 - - df /mod=11 /x87=08 - ST0 - - - df /mod=11 /x87=09 - ST1 - - - df /mod=11 /x87=0a - ST2 - - - df /mod=11 /x87=0b - ST3 - - - df /mod=11 /x87=0c - ST4 - - - df /mod=11 /x87=0d - ST5 - - - df /mod=11 /x87=0e - ST6 - - - df /mod=11 /x87=0f - ST7 - - - - - fxrstor - - aso rexw rexr rexx rexb - 0f ae /mod=11 /reg=1 - M - - - - - fxsave - - aso rexw rexr rexx rexb - 0f ae /mod=11 /reg=0 - M - - - - - fpxtract - X87 - - d9 /mod=11 /x87=34 - - - - - fyl2x - X87 - - d9 /mod=11 /x87=31 - - - - - fyl2xp1 - X87 - - d9 /mod=11 /x87=39 - - - - - hlt - - f4 - - - - - idiv - - aso oso rexw rexr rexx rexb - f7 /reg=7 - Ev - - - aso rexw rexr rexx rexb - f6 /reg=7 - Eb - - - - - in - - e4 - AL Ib - - - oso - e5 - eAX Ib - - - ec - AL DX - - - oso - ed - eAX DX - - - - - imul - - aso oso rexw rexr rexx rexb - 0f af - Gv Ev - - - aso rexw rexr rexx rexb - f6 /reg=5 - Eb - - - aso oso rexw rexr rexx rexb - f7 /reg=5 - Ev - - - aso oso rexw rexr rexx rexb - 69 - Gv Ev Iz - sext - - - aso oso rexw rexr rexx rexb - 6b - Gv Ev Ib - sext - - - - - inc - - oso - 40 - eAX - - - oso - 41 - eCX - - - oso - 42 - eDX - - - oso - 43 - eBX - - - oso - 44 - eSP - - - oso - 45 - eBP - - - oso - 46 - eSI - - - oso - 47 - eDI - - - aso oso rexw rexr rexx rexb - ff /reg=0 - Ev - - - aso rexw rexr rexx rexb - fe /reg=0 - Eb - - - - - insb - - 6c - - - - - insw - - oso - 6d /o=16 - - - - - insd - - oso - 6d /o=32 - - - - - int1 - - f1 - - - - - int3 - - cc - - - - - int - - cd - Ib - - - - - into - - ce - inv64 - - - - - invd - - 0f 08 - - - - - invept - intel - - sse66 0f 38 80 /m=32 - Gd Mo - - - sse66 0f 38 80 /m=64 - Gq Mo - - - - - invlpg - - aso rexr rexx rexb - 0f 01 /reg=7 /mod=!11 - M - - - - - invlpga - amd - - 0f 01 /reg=3 /mod=11 /rm=7 - - - - - invvpid - intel - - sse66 0f 38 81 /m=32 - Gd Mo - - - sse66 0f 38 81 /m=64 - Gq Mo - - - - - iretw - - oso rexw - cf /o=16 - - - - - iretd - - oso rexw - cf /o=32 - - - - - iretq - - oso rexw - cf /o=64 - - - - - jo - - 70 - Jb - - - oso - 0f 80 - Jz - def64 depM - - - - - jno - - 71 - Jb - - - oso - 0f 81 - Jz - def64 depM - - - - - jb - - 72 - Jb - - - oso - 0f 82 - Jz - def64 depM - - - - - jae - - 73 - Jb - - - oso - 0f 83 - Jz - def64 depM - - - - - jz - - 74 - Jb - - - oso - 0f 84 - Jz - def64 depM - - - - - jnz - - 75 - Jb - - - oso - 0f 85 - Jz - def64 depM - - - - - jbe - - 76 - Jb - - - oso - 0f 86 - Jz - def64 depM - - - - - ja - - 77 - Jb - - - oso - 0f 87 - Jz - def64 depM - - - - - js - - 78 - Jb - - - oso - 0f 88 - Jz - def64 depM - - - - - jns - - 79 - Jb - - - oso - 0f 89 - Jz - def64 depM - - - - - jp - - 7a - Jb - - - oso - 0f 8a - Jz - def64 depM - - - - - jnp - - 7b - Jb - - - oso - 0f 8b - Jz - def64 depM - - - - - jl - - 7c - Jb - - - oso - 0f 8c - Jz - def64 depM - - - - - jge - - 7d - Jb - - - oso - 0f 8d - Jz - def64 depM - - - - - jle - - 7e - Jb - - - oso - 0f 8e - Jz - def64 depM - - - - - jg - - 7f - Jb - - - oso - 0f 8f - Jz - def64 depM - - - - - jcxz - - aso - e3 /a=16 - Jb - - - - - jecxz - - aso - e3 /a=32 - Jb - - - - - jrcxz - - aso - e3 /a=64 - Jb - - - - - jmp - - aso oso rexw rexr rexx rexb - ff /reg=4 - Ev - def64 depM - - - aso oso rexw rexr rexx rexb - ff /reg=5 - Ep - - - oso - e9 - Jz - def64 depM - cast - - - ea - Ap - inv64 - - - eb - Jb - - - - - lahf - - 9f - - - - - lar - - aso oso rexw rexr rexx rexb - 0f 02 - Gv Ew - - - - - lddqu - - aso rexr rexx rexb - ssef2 0f f0 - V M - - - - - ldmxcsr - - aso rexw rexr rexx rexb - 0f ae /reg=2 /mod=11 - Md - - - - - lds - - aso oso - c5 - Gv M - inv64 - - - - - lea - - aso oso rexw rexr rexx rexb - 8d - Gv M - - - - - les - - aso oso - c4 - Gv M - inv64 - - - - - lfs - - aso oso rexw rexr rexx rexb - 0f b4 - Gz M - - - - - lgs - - aso oso rexw rexr rexx rexb - 0f b5 - Gz M - - - - - lidt - - aso rexr rexx rexb - 0f 01 /reg=3 /mod=!11 - M - - - - - lss - - aso oso rexw rexr rexx rexb - 0f b2 - Gz M - - - - - leave - - c9 - - - - - lfence - - 0f ae /reg=5 /mod=11 /rm=0 - - - 0f ae /reg=5 /mod=11 /rm=1 - - - 0f ae /reg=5 /mod=11 /rm=2 - - - 0f ae /reg=5 /mod=11 /rm=3 - - - 0f ae /reg=5 /mod=11 /rm=4 - - - 0f ae /reg=5 /mod=11 /rm=5 - - - 0f ae /reg=5 /mod=11 /rm=6 - - - 0f ae /reg=5 /mod=11 /rm=7 - - - - - lgdt - - aso rexr rexx rexb - 0f 01 /reg=2 /mod=!11 - M - - - - - lldt - - aso rexr rexx rexb - 0f 00 /reg=2 - Ew - - - - - lmsw - - aso rexr rexx rexb - 0f 01 /reg=6 /mod=!11 - Ew - - - - - lock - - f0 - - - - - lodsb - - seg - ac - - - - - lodsw - - seg oso rexw - ad /o=16 - - - - - lodsd - - seg oso rexw - ad /o=32 - - - - - lodsq - - seg oso rexw - ad /o=64 - - - - - loopnz - - e0 - Jb - - - - - loope - - e1 - Jb - - - - - loop - - e2 - Jb - - - - - lsl - - aso oso rexw rexr rexx rexb - 0f 03 - Gv Ew - - - - - ltr - - aso rexr rexx rexb - 0f 00 /reg=3 - Ew - - - - - maskmovq - - aso rexr rexx rexb - 0f f7 - P PR - - - - - maxpd - - aso rexr rexx rexb - sse66 0f 5f - V W - - - - - maxps - - aso rexr rexx rexb - 0f 5f - V W - - - - - maxsd - - aso rexr rexx rexb - ssef2 0f 5f - V W - - - - - maxss - - aso rexr rexx rexb - ssef3 0f 5f - V W - - - - - mfence - - 0f ae /reg=6 /mod=11 /rm=0 - - - 0f ae /reg=6 /mod=11 /rm=1 - - - 0f ae /reg=6 /mod=11 /rm=2 - - - 0f ae /reg=6 /mod=11 /rm=3 - - - 0f ae /reg=6 /mod=11 /rm=4 - - - 0f ae /reg=6 /mod=11 /rm=5 - - - 0f ae /reg=6 /mod=11 /rm=6 - - - 0f ae /reg=6 /mod=11 /rm=7 - - - - - minpd - - aso rexr rexx rexb - sse66 0f 5d - V W - - - - - minps - - aso rexr rexx rexb - 0f 5d - V W - - - - - minsd - - aso rexr rexx rexb - ssef2 0f 5d - V W - - - - - minss - - aso rexr rexx rexb - ssef3 0f 5d - V W - - - - - monitor - - 0f 01 /reg=1 /mod=11 /rm=0 - - - - - montmul - - 0f a6 /mod=11 /rm=0 /reg=0 - - - - - mov - - aso rexw rexr rexx rexb - c6 /reg=0 - Eb Ib - - - aso oso rexw rexr rexx rexb - c7 /reg=0 - Ev Iz - - - aso rexr rexx rexb - 88 - Eb Gb - - - aso oso rexw rexr rexx rexb - 89 - Ev Gv - - - aso rexr rexx rexb - 8a - Gb Eb - - - aso oso rexw rexr rexx rexb - 8b - Gv Ev - - - aso oso rexr rexx rexb - 8c - Ev S - - - aso oso rexr rexx rexb - 8e - S Ev - - - a0 - AL Ob - - - aso oso rexw - a1 - rAX Ov - - - a2 - Ob AL - - - aso oso rexw - a3 - Ov rAX - - - rexb - b0 - ALr8b Ib - - - rexb - b1 - CLr9b Ib - - - rexb - b2 - DLr10b Ib - - - rexb - b3 - BLr11b Ib - - - rexb - b4 - AHr12b Ib - - - rexb - b5 - CHr13b Ib - - - rexb - b6 - DHr14b Ib - - - rexb - b7 - BHr15b Ib - - - oso rexw rexb - b8 - rAXr8 Iv - - - oso rexw rexb - b9 - rCXr9 Iv - - - oso rexw rexb - ba - rDXr10 Iv - - - oso rexw rexb - bb - rBXr11 Iv - - - oso rexw rexb - bc - rSPr12 Iv - - - oso rexw rexb - bd - rBPr13 Iv - - - oso rexw rexb - be - rSIr14 Iv - - - oso rexw rexb - bf - rDIr15 Iv - - - rexr - 0f 20 - R C - - - rexr - 0f 21 - R D - - - rexr - 0f 22 - C R - - - rexr - 0f 23 - D R - - - - - movapd - - aso rexr rexx rexb - sse66 0f 28 - V W - - - aso rexr rexx rexb - sse66 0f 29 - W V - - - - - movaps - - aso rexr rexx rexb - 0f 28 - V W - - - aso rexr rexx rexb - 0f 29 - W V - - - - - movd - - aso rexw rexr rexx rexb - sse66 0f 6e - V Ex - - - aso rexr rexx rexb - 0f 6e - P Ex - - - aso rexw rexr rexx rexb - sse66 0f 7e - Ex V - - - aso rexr rexx rexb - 0f 7e - Ex P - - - - - movhpd - - aso rexr rexx rexb - sse66 0f 16 /mod=!11 - V M - - - aso rexr rexx rexb - sse66 0f 17 - M V - - - - - movhps - - aso rexr rexx rexb - 0f 16 /mod=!11 - V M - - - aso rexr rexx rexb - 0f 17 - M V - - - - - movlhps - - aso rexr rexx rexb - 0f 16 /mod=11 - V VR - - - - - movlpd - - aso rexr rexx rexb - sse66 0f 12 /mod=!11 - V M - - - aso rexr rexx rexb - sse66 0f 13 - M V - - - - - movlps - - aso rexr rexx rexb - 0f 12 /mod=!11 - V M - - - aso rexr rexx rexb - 0f 13 - M V - - - - - movhlps - - aso rexr rexx rexb - 0f 12 /mod=11 - V VR - - - - - movmskpd - - oso rexr rexb - sse66 0f 50 - Gd VR - - - - - movmskps - - oso rexr rexb - 0f 50 - Gd VR - - - - - movntdq - - aso rexr rexx rexb - sse66 0f e7 - M V - - - - - movnti - - aso rexw rexr rexx rexb - 0f c3 - M Gy - - - - - movntpd - - aso rexr rexx rexb - sse66 0f 2b - M V - - - - - movntps - - aso rexr rexx rexb - 0f 2b - M V - - - - - movntq - - 0f e7 - M P - - - - - movq - - aso rexr rexx rexb - 0f 6f - P Q - - - aso rexr rexx rexb - sse66 0f d6 - W V - - - aso rexr rexx rexb - ssef3 0f 7e - V W - - - aso rexr rexx rexb - 0f 7f - Q P - - - - - movsb - - seg - a4 - - - - - movsw - - seg oso rexw - a5 /o=16 - - - - - movsd - - seg oso rexw - a5 /o=32 - - - aso rexr rexx rexb - ssef2 0f 10 - V W - - - aso rexr rexx rexb - ssef2 0f 11 - W V - - - - - movsq - - seg oso rexw - a5 /o=64 - - - - - movss - - aso rexr rexx rexb - ssef3 0f 10 - V W - - - aso rexr rexx rexb - ssef3 0f 11 - W V - - - - - movsx - - aso oso rexw rexr rexx rexb - 0f be - Gv Eb - - - aso oso rexw rexr rexx rexb - 0f bf - Gv Ew - - - - - movupd - - aso rexr rexx rexb - sse66 0f 10 - V W - - - aso rexr rexx rexb - sse66 0f 11 - W V - - - - - movups - - aso rexr rexx rexb - 0f 10 - V W - - - aso rexr rexx rexb - 0f 11 - W V - - - - - movzx - - aso oso rexw rexr rexx rexb - 0f b6 - Gv Eb - - - aso oso rexw rexr rexx rexb - 0f b7 - Gv Ew - - - - - mul - - aso rexw rexr rexx rexb - f6 /reg=4 - Eb - - - aso oso rexw rexr rexx rexb - f7 /reg=4 - Ev - - - - - mulpd - - aso rexr rexx rexb - sse66 0f 59 - V W - - - - - mulps - - aso rexr rexx rexb - 0f 59 - V W - - - - - mulsd - - aso rexr rexx rexb - ssef2 0f 59 - V W - - - - - mulss - - aso rexr rexx rexb - ssef3 0f 59 - V W - - - - - mwait - - 0f 01 /reg=1 /mod=11 /rm=1 - - - - - neg - - aso rexw rexr rexx rexb - f6 /reg=3 - Eb - - - aso oso rexw rexr rexx rexb - f7 /reg=3 - Ev - - - - - nop - - 90 - - - aso rexr rexx rexb - 0f 19 - M - - - aso rexr rexx rexb - 0f 1a - M - - - aso rexr rexx rexb - 0f 1b - M - - - aso rexr rexx rexb - 0f 1c - M - - - aso rexr rexx rexb - 0f 1d - M - - - aso rexr rexx rexb - 0f 1e - M - - - aso rexr rexx rexb - 0f 1f - M - - - - - not - - aso rexw rexr rexx rexb - f6 /reg=2 - Eb - - - aso oso rexw rexr rexx rexb - f7 /reg=2 - Ev - - - - - or - - aso rexr rexx rexb - 08 - Eb Gb - - - aso oso rexw rexr rexx rexb - 09 - Ev Gv - - - aso rexr rexx rexb - 0a - Gb Eb - - - aso oso rexw rexr rexx rexb - 0b - Gv Ev - - - 0c - AL Ib - - - oso rexw - 0d - rAX Iz - sext - - - aso rexr rexx rexb - 80 /reg=1 - Eb Ib - - - aso oso rexw rexr rexx rexb - 81 /reg=1 - Ev Iz - sext - - - aso rexr rexx rexb - 82 /reg=1 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 83 /reg=1 - Ev Ib - sext - - - - - orpd - - aso rexr rexx rexb - sse66 0f 56 - V W - - - - - orps - - aso rexr rexx rexb - 0f 56 - V W - - - - - out - - e6 - Ib AL - - - oso - e7 - Ib eAX - - - ee - DX AL - - - oso - ef - DX eAX - - - - - outsb - - 6e - - - - - outsw - - oso - 6f /o=16 - - - - - outsd - - oso - 6f /o=32 - - - - - outsq - - oso - 6f /o=64 - - - - - packsswb - - aso rexr rexx rexb - sse66 0f 63 - V W - - - aso rexr rexx rexb - 0f 63 - P Q - - - - - packssdw - - aso rexr rexx rexb - sse66 0f 6b - V W - - - aso rexr rexx rexb - 0f 6b - P Q - - - - - packuswb - - aso rexr rexx rexb - sse66 0f 67 - V W - - - aso rexr rexx rexb - 0f 67 - P Q - - - - - paddb - - aso rexr rexx rexb - sse66 0f fc - V W - - - aso rexr rexx rexb - 0f fc - P Q - - - - - paddw - - aso rexr rexx rexb - 0f fd - P Q - - - aso rexr rexx rexb - sse66 0f fd - V W - - - - - paddd - - aso rexr rexx rexb - 0f fe - P Q - - - aso rexr rexx rexb - sse66 0f fe - V W - - - - - - paddsb - - aso rexr rexx rexb - 0f ec - P Q - - - aso rexr rexx rexb - sse66 0f ec - V W - - - - - paddsw - - aso rexr rexx rexb - 0f ed - P Q - - - aso rexr rexx rexb - sse66 0f ed - V W - - - - - paddusb - - aso rexr rexx rexb - 0f dc - P Q - - - aso rexr rexx rexb - sse66 0f dc - V W - - - - - paddusw - - aso rexr rexx rexb - 0f dd - P Q - - - aso rexr rexx rexb - sse66 0f dd - V W - - - - - pand - - aso rexr rexx rexb - sse66 0f db - V W - - - aso rexr rexx rexb - 0f db - P Q - - - - - pandn - - aso rexr rexx rexb - sse66 0f df - V W - - - aso rexr rexx rexb - 0f df - P Q - - - - - pavgb - - aso rexr rexx rexb - sse66 0f e0 - V W - - - aso rexr rexx rexb - 0f e0 - P Q - - - - - pavgw - - aso rexr rexx rexb - sse66 0f e3 - V W - - - aso rexr rexx rexb - 0f e3 - P Q - - - - - pcmpeqb - - aso rexr rexx rexb - 0f 74 - P Q - - - aso rexr rexx rexb - sse66 0f 74 - V W - - - - - pcmpeqw - - aso rexr rexx rexb - 0f 75 - P Q - - - aso rexr rexx rexb - sse66 0f 75 - V W - - - - - pcmpeqd - - aso rexr rexx rexb - 0f 76 - P Q - - - aso rexr rexx rexb - sse66 0f 76 - V W - - - - - pcmpgtb - - aso rexr rexx rexb - sse66 0f 64 - V W - - - aso rexr rexx rexb - 0f 64 - P Q - - - - - pcmpgtw - - aso rexr rexx rexb - sse66 0f 65 - V W - - - aso rexr rexx rexb - 0f 65 - P Q - - - - - pcmpgtd - - aso rexr rexx rexb - sse66 0f 66 - V W - - - aso rexr rexx rexb - 0f 66 - P Q - - - - - pextrb - - aso rexr rexb - sse66 0f 3a 14 - MbRv V Ib - def64 - - - - - pextrd - - aso rexr rexw rexb - sse66 0f 3a 16 /o=16 - Ev V Ib - - - aso rexr rexw rexb - sse66 0f 3a 16 /o=32 - Ev V Ib - - - - - pextrq - - aso rexr rexw rexb - sse66 0f 3a 16 /o=64 - Ev V Ib - def64 - - - - - pextrw - - aso rexr rexb - sse66 0f c5 - Gd VR Ib - - - aso oso rexw rexr rexx rexb - 0f c5 - Gd PR Ib - - - - - pinsrw - - aso oso rexw rexr rexx rexb - 0f c4 - P Ew Ib - - - aso rexw rexr rexx rexb - sse66 0f c4 - V Ew Ib - - - - - pmaddwd - - aso rexr rexx rexb - 0f f5 - P Q - - - aso rexr rexx rexb - sse66 0f f5 - V W - - - - - pmaxsw - - aso rexr rexx rexb - sse66 0f ee - V W - - - aso rexr rexx rexb - 0f ee - P Q - - - - - pmaxub - - aso rexr rexx rexb - 0f de - P Q - - - aso rexr rexx rexb - sse66 0f de - V W - - - - - pminsw - - aso rexr rexx rexb - sse66 0f ea - V W - - - aso rexr rexx rexb - 0f ea - P Q - - - - - pminub - - aso rexr rexx rexb - sse66 0f da - V W - - - aso rexr rexx rexb - 0f da - P Q - - - - - pmovmskb - - rexr rexb - sse66 0f d7 - Gd VR - - - oso rexr rexb - 0f d7 - Gd PR - - - - - pmulhuw - - aso rexr rexx rexb - 0f e4 - P Q - - - aso rexr rexx rexb - sse66 0f e4 - V W - - - - - pmulhw - - aso rexr rexx rexb - sse66 0f e5 - V W - - - aso rexr rexx rexb - 0f e5 - P Q - - - - - pmullw - - aso rexr rexx rexb - 0f d5 - P Q - - - aso rexr rexx rexb - sse66 0f d5 - V W - - - - - pop - - 07 - ES - inv64 - - - 17 - SS - inv64 - - - 1f - DS - inv64 - - - 0f a9 - GS - - - 0f a1 - FS - - - oso rexb - 58 - rAXr8 - def64 depM - - - oso rexb - 59 - rCXr9 - def64 depM - - - oso rexb - 5a - rDXr10 - def64 depM - - - oso rexb - 5b - rBXr11 - def64 depM - - - oso rexb - 5c - rSPr12 - def64 depM - - - oso rexb - 5d - rBPr13 - def64 depM - - - oso rexb - 5e - rSIr14 - def64 depM - - - oso rexb - 5f - rDIr15 - def64 depM - - - aso oso rexw rexr rexx rexb - 8f /reg=0 - Ev - def64 depM - - - - - popa - - oso - 61 /o=16 - inv64 - - - - - popad - - oso - 61 /o=32 - inv64 - - - - - popfw - - oso - 9d /m=32 /o=16 - def64 depM - - - oso - 9d /m=16 /o=16 - def64 depM - - - - - popfd - - oso - 9d /m=16 /o=32 - def64 depM - - - oso - 9d /m=32 /o=32 - def64 depM - - - - - popfq - - oso - 9d /m=64 /o=64 - def64 depM - - - - - por - - aso rexr rexx rexb - sse66 0f eb - V W - - - aso rexr rexx rexb - 0f eb - P Q - - - - - prefetch - - aso rexw rexr rexx rexb - 0f 0d /reg=0 - M - - - aso rexw rexr rexx rexb - 0f 0d /reg=1 - M - - - aso rexw rexr rexx rexb - 0f 0d /reg=2 - M - - - aso rexw rexr rexx rexb - 0f 0d /reg=3 - M - - - aso rexw rexr rexx rexb - 0f 0d /reg=4 - M - - - aso rexw rexr rexx rexb - 0f 0d /reg=5 - M - - - aso rexw rexr rexx rexb - 0f 0d /reg=6 - M - - - aso rexw rexr rexx rexb - 0f 0d /reg=7 - M - - - - - prefetchnta - - aso rexw rexr rexx rexb - 0f 18 /reg=0 - M - - - - - prefetcht0 - - aso rexw rexr rexx rexb - 0f 18 /reg=1 - M - - - - - prefetcht1 - - aso rexw rexr rexx rexb - 0f 18 /reg=2 - M - - - - - prefetcht2 - - aso rexw rexr rexx rexb - 0f 18 /reg=3 - M - - - - - psadbw - - aso rexr rexx rexb - sse66 0f f6 - V W - - - aso rexr rexx rexb - 0f f6 - P Q - - - - - pshufw - - aso rexr rexx rexb - 0f 70 - P Q Ib - - - - - psllw - - aso rexr rexx rexb - sse66 0f f1 - V W - - - aso rexr rexx rexb - 0f f1 - P Q - - - rexb - sse66 0f 71 /reg=6 - VR Ib - - - 0f 71 /reg=6 - PR Ib - - - - - pslld - - aso rexr rexx rexb - sse66 0f f2 - V W - - - aso rexr rexx rexb - 0f f2 - P Q - - - rexb - sse66 0f 72 /reg=6 - VR Ib - - - 0f 72 /reg=6 - PR Ib - - - - - psllq - - aso rexr rexx rexb - sse66 0f f3 - V W - - - aso rexr rexx rexb - 0f f3 - P Q - - - rexb - sse66 0f 73 /reg=6 - VR Ib - - - 0f 73 /reg=6 - PR Ib - - - - - psraw - - aso rexr rexx rexb - 0f e1 - P Q - - - aso rexr rexx rexb - sse66 0f e1 - V W - - - rexb - sse66 0f 71 /reg=4 - VR Ib - - - 0f 71 /reg=4 - PR Ib - - - - - psrad - - 0f 72 /reg=4 - PR Ib - - - aso rexr rexx rexb - sse66 0f e2 - V W - - - aso rexr rexx rexb - 0f e2 - P Q - - - rexb - sse66 0f 72 /reg=4 - VR Ib - - - - - psrlw - - 0f 71 /reg=2 - PR Ib - - - aso rexr rexx rexb - 0f d1 - P Q - - - aso rexr rexx rexb - sse66 0f d1 - V W - - - rexb - sse66 0f 71 /reg=2 - VR Ib - - - - - psrld - - 0f 72 /reg=2 - PR Ib - - - aso rexr rexx rexb - 0f d2 - P Q - - - aso rexr rexx rexb - sse66 0f d2 - V W - - - rexb - sse66 0f 72 /reg=2 - VR Ib - - - - - psrlq - - 0f 73 /reg=2 - PR Ib - - - aso rexr rexx rexb - 0f d3 - P Q - - - aso rexr rexx rexb - sse66 0f d3 - V W - - - rexb - sse66 0f 73 /reg=2 - VR Ib - - - - - psubb - - aso rexr rexx rexb - sse66 0f f8 - V W - - - aso rexr rexx rexb - 0f f8 - P Q - - - - - psubw - - aso rexr rexx rexb - sse66 0f f9 - V W - - - aso rexr rexx rexb - 0f f9 - P Q - - - - - psubd - - aso rexr rexx rexb - 0f fa - P Q - - - aso rexr rexx rexb - sse66 0f fa - V W - - - - - psubsb - - aso rexr rexx rexb - 0f e8 - P Q - - - aso rexr rexx rexb - sse66 0f e8 - V W - - - - - psubsw - - aso rexr rexx rexb - 0f e9 - P Q - - - aso rexr rexx rexb - sse66 0f e9 - V W - - - - - psubusb - - aso rexr rexx rexb - 0f d8 - P Q - - - aso rexr rexx rexb - sse66 0f d8 - V W - - - - - psubusw - - aso rexr rexx rexb - 0f d9 - P Q - - - aso rexr rexx rexb - sse66 0f d9 - V W - - - - - punpckhbw - - aso rexr rexx rexb - sse66 0f 68 - V W - - - aso rexr rexx rexb - 0f 68 - P Q - - - - - punpckhwd - - aso rexr rexx rexb - sse66 0f 69 - V W - - - aso rexr rexx rexb - 0f 69 - P Q - - - - - punpckhdq - - aso rexr rexx rexb - sse66 0f 6a - V W - - - aso rexr rexx rexb - 0f 6a - P Q - - - - - punpcklbw - - aso rexr rexx rexb - sse66 0f 60 - V W - - - aso rexr rexx rexb - 0f 60 - P Q - - - - - punpcklwd - - aso rexr rexx rexb - sse66 0f 61 - V W - - - aso rexr rexx rexb - 0f 61 - P Q - - - - - punpckldq - - aso rexr rexx rexb - sse66 0f 62 - V W - - - aso rexr rexx rexb - 0f 62 - P Q - - - - - pi2fw - - 0f 0f /3dnow=0c - P Q - - - - - pi2fd - - 0f 0f /3dnow=0d - P Q - - - - - pf2iw - - 0f 0f /3dnow=1c - P Q - - - - - pf2id - - 0f 0f /3dnow=1d - P Q - - - - - pfnacc - - 0f 0f /3dnow=8a - P Q - - - - - pfpnacc - - 0f 0f /3dnow=8e - P Q - - - - - pfcmpge - - 0f 0f /3dnow=90 - P Q - - - - - pfmin - - 0f 0f /3dnow=94 - P Q - - - - - pfrcp - - 0f 0f /3dnow=96 - P Q - - - - - pfrsqrt - - 0f 0f /3dnow=97 - P Q - - - - - pfsub - - 0f 0f /3dnow=9a - P Q - - - - - pfadd - - 0f 0f /3dnow=9e - P Q - - - - - pfcmpgt - - 0f 0f /3dnow=a0 - P Q - - - - - pfmax - - 0f 0f /3dnow=a4 - P Q - - - - - pfrcpit1 - - 0f 0f /3dnow=a6 - P Q - - - - - pfrsqit1 - - 0f 0f /3dnow=a7 - P Q - - - - - pfsubr - - 0f 0f /3dnow=aa - P Q - - - - - pfacc - - 0f 0f /3dnow=ae - P Q - - - - - pfcmpeq - - 0f 0f /3dnow=b0 - P Q - - - - - pfmul - - 0f 0f /3dnow=b4 - P Q - - - - - pfrcpit2 - - 0f 0f /3dnow=b6 - P Q - - - - - pmulhrw - - 0f 0f /3dnow=b7 - P Q - - - - - pswapd - - 0f 0f /3dnow=bb - P Q - - - - - pavgusb - - 0f 0f /3dnow=bf - P Q - - - - - push - - 06 - ES - inv64 - - - 0e - CS - inv64 - - - 16 - SS - inv64 - - - 1e - DS - inv64 - - - 0f a8 - GS - - - 0f a0 - FS - - - oso rexb - 50 - rAXr8 - def64 depM - - - oso rexb - 51 - rCXr9 - def64 depM - - - oso rexb - 52 - rDXr10 - def64 depM - - - oso rexb - 53 - rBXr11 - def64 depM - - - oso rexb - 54 - rSPr12 - def64 depM - - - oso rexb - 55 - rBPr13 - def64 depM - - - oso rexb - 56 - rSIr14 - def64 depM - - - oso rexb - 57 - rDIr15 - def64 depM - - - oso - 68 - Iz - cast - - - aso oso rexw rexr rexx rexb - ff /reg=6 - Ev - def64 - - - 6a - Ib - sext - - - - - pusha - - oso - 60 /o=16 - inv64 - - - - - pushad - - oso - 60 /o=32 - inv64 - - - - - pushfw - - oso - 9c /m=32 /o=16 - def64 - - - oso - 9c /m=16 /o=16 - def64 - - - oso rexw - 9c /m=64 /o=16 - def64 - - - - - pushfd - - oso - 9c /m=16 /o=32 - def64 - - - oso - 9c /m=32 /o=32 - def64 - - - - - pushfq - - oso rexw - 9c /m=64 /o=32 - def64 - - - oso rexw - 9c /m=64 /o=64 - def64 - - - - - pxor - - aso rexr rexx rexb - sse66 0f ef - V W - - - aso rexr rexx rexb - 0f ef - P Q - - - - - rcl - - aso rexw rexr rexx rexb - c0 /reg=2 - Eb Ib - - - aso oso rexw rexr rexx rexb - c1 /reg=2 - Ev Ib - - - aso rexw rexr rexx rexb - d0 /reg=2 - Eb I1 - - - aso rexw rexr rexx rexb - d2 /reg=2 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d3 /reg=2 - Ev CL - cast - - - aso oso rexw rexr rexx rexb - d1 /reg=2 - Ev I1 - - - - - rcr - - aso rexw rexr rexx rexb - d0 /reg=3 - Eb I1 - - - aso oso rexw rexr rexx rexb - c1 /reg=3 - Ev Ib - - - aso rexw rexr rexx rexb - c0 /reg=3 - Eb Ib - - - aso oso rexw rexr rexx rexb - d1 /reg=3 - Ev I1 - - - aso rexw rexr rexx rexb - d2 /reg=3 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d3 /reg=3 - Ev CL - cast - - - - - rol - - aso rexw rexr rexx rexb - c0 /reg=0 - Eb Ib - - - aso rexw rexr rexx rexb - d0 /reg=0 - Eb I1 - - - aso oso rexw rexr rexx rexb - d1 /reg=0 - Ev I1 - - - aso rexw rexr rexx rexb - d2 /reg=0 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d3 /reg=0 - Ev CL - cast - - - aso oso rexw rexr rexx rexb - c1 /reg=0 - Ev Ib - - - - - ror - - aso rexw rexr rexx rexb - d0 /reg=1 - Eb I1 - - - aso rexw rexr rexx rexb - c0 /reg=1 - Eb Ib - - - aso oso rexw rexr rexx rexb - c1 /reg=1 - Ev Ib - - - aso oso rexw rexr rexx rexb - d1 /reg=1 - Ev I1 - - - aso rexw rexr rexx rexb - d2 /reg=1 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d3 /reg=1 - Ev CL - cast - - - - - rcpps - - aso rexr rexx rexb - 0f 53 - V W - - - - - rcpss - - aso rexr rexx rexb - ssef3 0f 53 - V W - - - - - rdmsr - - 0f 32 - - - - - rdpmc - - 0f 33 - - - - - rdtsc - - 0f 31 - - - - - rdtscp - amd - - 0f 01 /reg=7 /mod=11 /rm=1 - - - - - repne - - f2 - - - - - rep - - f3 - - - - - ret - - c2 - Iw - - - c3 - - - - - retf - - ca - Iw - - - cb - - - - - rsm - - 0f aa - - - - - rsqrtps - - aso rexr rexx rexb - 0f 52 - V W - - - - - rsqrtss - - aso rexr rexx rexb - ssef3 0f 52 - V W - - - - - sahf - - 9e - - - - - sal - - - - salc - - d6 - inv64 - - - - - sar - - aso oso rexw rexr rexx rexb - d1 /reg=7 - Ev I1 - - - aso rexw rexr rexx rexb - c0 /reg=7 - Eb Ib - - - aso rexw rexr rexx rexb - d0 /reg=7 - Eb I1 - - - aso oso rexw rexr rexx rexb - c1 /reg=7 - Ev Ib - - - aso rexw rexr rexx rexb - d2 /reg=7 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d3 /reg=7 - Ev CL - cast - - - - - shl - - aso rexw rexr rexx rexb - c0 /reg=6 - Eb Ib - - - aso oso rexw rexr rexx rexb - c1 /reg=6 - Ev Ib - - - aso rexw rexr rexx rexb - d0 /reg=6 - Eb I1 - - - aso rexw rexr rexx rexb - d2 /reg=6 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d3 /reg=6 - Ev CL - cast - - - aso oso rexw rexr rexx rexb - c1 /reg=4 - Ev Ib - - - aso rexr rexx rexb - d2 /reg=4 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d1 /reg=4 - Ev I1 - - - aso rexw rexr rexx rexb - d0 /reg=4 - Eb I1 - - - aso rexw rexr rexx rexb - c0 /reg=4 - Eb Ib - - - aso oso rexw rexr rexx rexb - d3 /reg=4 - Ev CL - - - aso oso rexw rexr rexx rexb - d1 /reg=6 - Ev I1 - - - - - shr - - aso oso rexw rexr rexx rexb - c1 /reg=5 - Ev Ib - - - aso rexw rexr rexx rexb - d2 /reg=5 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d1 /reg=5 - Ev I1 - - - aso rexw rexr rexx rexb - d0 /reg=5 - Eb I1 - - - aso rexw rexr rexx rexb - c0 /reg=5 - Eb Ib - - - aso oso rexw rexr rexx rexb - d3 /reg=5 - Ev CL - cast - - - - - sbb - - aso rexr rexx rexb - 18 - Eb Gb - - - aso oso rexw rexr rexx rexb - 19 - Ev Gv - - - aso rexr rexx rexb - 1a - Gb Eb - - - aso oso rexw rexr rexx rexb - 1b - Gv Ev - - - 1c - AL Ib - - - oso rexw - 1d - rAX Iz - sext - - - aso rexr rexx rexb - 80 /reg=3 - Eb Ib - - - aso oso rexw rexr rexx rexb - 81 /reg=3 - Ev Iz - sext - - - aso rexr rexx rexb - 82 /reg=3 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 83 /reg=3 - Ev Ib - sext - - - - - scasb - - ae - - - - - scasw - - oso rexw - af /o=16 - - - - - scasd - - oso rexw - af /o=32 - - - - - scasq - - oso rexw - af /o=64 - - - - - seto - - aso rexr rexx rexb - 0f 90 - Eb - - - - - setno - - aso rexr rexx rexb - 0f 91 - Eb - - - - - setb - - aso rexr rexx rexb - 0f 92 - Eb - - - - - setnb - - aso rexr rexx rexb - 0f 93 - Eb - - - - - setz - - aso rexr rexx rexb - 0f 94 - Eb - - - - - setnz - - aso rexr rexx rexb - 0f 95 - Eb - - - - - setbe - - aso rexr rexx rexb - 0f 96 - Eb - - - - - seta - - aso rexr rexx rexb - 0f 97 - Eb - - - - - sets - - aso rexr rexx rexb - 0f 98 - Eb - - - - - setns - - aso rexr rexx rexb - 0f 99 - Eb - - - - - setp - - aso rexr rexx rexb - 0f 9a - Eb - - - - - setnp - - aso rexr rexx rexb - 0f 9b - Eb - - - - - setl - - aso rexr rexx rexb - 0f 9c - Eb - - - - - setge - - aso rexr rexx rexb - 0f 9d - Eb - - - - - setle - - aso rexr rexx rexb - 0f 9e - Eb - - - - - setg - - aso rexr rexx rexb - 0f 9f - Eb - - - - - sfence - - 0f ae /reg=7 /mod=11 /rm=0 - - - 0f ae /reg=7 /mod=11 /rm=1 - - - 0f ae /reg=7 /mod=11 /rm=2 - - - 0f ae /reg=7 /mod=11 /rm=3 - - - 0f ae /reg=7 /mod=11 /rm=4 - - - 0f ae /reg=7 /mod=11 /rm=5 - - - 0f ae /reg=7 /mod=11 /rm=6 - - - 0f ae /reg=7 /mod=11 /rm=7 - - - - - sgdt - - aso rexr rexx rexb - 0f 01 /reg=0 /mod=!11 - M - - - - - shld - - aso oso rexw rexr rexx rexb - 0f a4 - Ev Gv Ib - - - aso oso rexw rexr rexx rexb - 0f a5 - Ev Gv CL - - - - - shrd - - aso oso rexw rexr rexx rexb - 0f ac - Ev Gv Ib - - - aso oso rexw rexr rexx rexb - 0f ad - Ev Gv CL - - - - - shufpd - - aso rexr rexx rexb - sse66 0f c6 - V W Ib - - - - - shufps - - aso rexr rexx rexb - 0f c6 - V W Ib - - - - - sidt - - aso rexr rexx rexb - 0f 01 /reg=1 /mod=!11 - M - - - - - sldt - - aso oso rexr rexx rexb - 0f 00 /reg=0 - MwRv - - - - - smsw - - aso rexr rexx rexb - 0f 01 /reg=4 /mod=!11 - M - - - - - sqrtps - - aso rexr rexx rexb - 0f 51 - V W - - - - - sqrtpd - - aso rexr rexx rexb - sse66 0f 51 - V W - - - - - sqrtsd - - aso rexr rexx rexb - ssef2 0f 51 - V W - - - - - sqrtss - - aso rexr rexx rexb - ssef3 0f 51 - V W - - - - - stc - - f9 - - - - - std - - fd - - - - - stgi - amd - - 0f 01 /reg=3 /mod=11 /rm=4 - - - - - sti - - fb - - - - - skinit - amd - - 0f 01 /reg=3 /mod=11 /rm=6 - - - - - stmxcsr - - aso rexw rexr rexx rexb - 0f ae /mod=11 /reg=3 - Md - - - - - stosb - - seg - aa - - - - - stosw - - seg oso rexw - ab /o=16 - - - - - stosd - - seg oso rexw - ab /o=32 - - - - - stosq - - seg oso rexw - ab /o=64 - - - - - str - - aso oso rexr rexx rexb - 0f 00 /reg=1 - Ev - - - - - sub - - aso rexr rexx rexb - 28 - Eb Gb - - - aso oso rexw rexr rexx rexb - 29 - Ev Gv - - - aso rexr rexx rexb - 2a - Gb Eb - - - aso oso rexw rexr rexx rexb - 2b - Gv Ev - - - 2c - AL Ib - - - oso rexw - 2d - rAX Iz - sext - - - aso rexr rexx rexb - 80 /reg=5 - Eb Ib - - - aso oso rexw rexr rexx rexb - 81 /reg=5 - Ev Iz - sext - - - aso rexr rexx rexb - 82 /reg=5 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 83 /reg=5 - Ev Ib - sext - - - - - subpd - - aso rexr rexx rexb - sse66 0f 5c - V W - - - - - subps - - aso rexr rexx rexb - 0f 5c - V W - - - - - subsd - - aso rexr rexx rexb - ssef2 0f 5c - V W - - - - - subss - - aso rexr rexx rexb - ssef3 0f 5c - V W - - - - - swapgs - - 0f 01 /reg=7 /mod=11 /rm=0 - - - - - syscall - - 0f 05 - - - - - sysenter - - 0f 34 - inv64 - - - - - sysexit - - 0f 35 - - - - - sysret - - 0f 07 - - - - - test - - aso rexw rexr rexx rexb - f6 /reg=0 - Eb Ib - - - aso rexr rexx rexb - 84 - Eb Gb - - - aso oso rexw rexr rexx rexb - 85 - Ev Gv - - - a8 - AL Ib - - - oso rexw - a9 - rAX Iz - sext - - - aso rexw rexr rexx rexb - f6 /reg=1 - Eb Ib - - - aso oso rexw rexr rexx rexb - f7 /reg=0 - Ev Iz - sext - - - aso oso rexw rexr rexx rexb - f7 /reg=1 - Ev Iz - sext - - - - - ucomisd - - aso rexr rexx rexb - sse66 0f 2e - V W - - - - - ucomiss - - aso rexr rexx rexb - 0f 2e - V W - - - - - ud2 - - 0f 0b - - - - - unpckhpd - - aso rexr rexx rexb - sse66 0f 15 - V W - - - - - unpckhps - - aso rexr rexx rexb - 0f 15 - V W - - - - - unpcklps - - aso rexr rexx rexb - 0f 14 - V W - - - - - unpcklpd - - aso rexr rexx rexb - sse66 0f 14 - V W - - - - - verr - - aso rexr rexx rexb - 0f 00 /reg=4 - Ew - - - - - verw - - aso rexr rexx rexb - 0f 00 /reg=5 - Ew - - - - - vmcall - intel - - 0f 01 /reg=0 /mod=11 /rm=1 - - - - - vmclear - intel - - aso rexr rexx rexb - sse66 0f c7 /reg=6 - Mq - - - - - vmxon - intel - - aso rexr rexx rexb - ssef3 0f c7 /reg=6 - Mq - - - - - vmptrld - intel - - aso rexr rexx rexb - 0f c7 /reg=6 - Mq - - - - - vmptrst - intel - - aso rexr rexx rexb - 0f c7 /reg=7 - Mq - - - - - vmlaunch - intel - - 0f 01 /reg=0 /mod=11 /rm=2 - - - - - vmresume - intel - - 0f 01 /reg=0 /mod=11 /rm=3 - - - - - vmxoff - intel - - 0f 01 /reg=0 /mod=11 /rm=4 - - - - - vmread - intel - - aso rexr rexx rexb - 0f 78 /m=16 - Ed Gd - def64 - - - aso rexr rexx rexb - 0f 78 /m=32 - Ed Gd - def64 - - - aso rexr rexx rexb - 0f 78 /m=64 - Eq Gq - def64 - - - - - vmwrite - intel - - aso rexr rexx rexb - 0f 79 /m=16 - Gd Ed - def64 - - - aso rexr rexx rexb - 0f 79 /m=32 - Gd Ed - def64 - - - aso rexr rexx rexb - 0f 79 /m=64 - Gq Eq - def64 - - - - - vmrun - amd - - 0f 01 /reg=3 /mod=11 /rm=0 - - - - - vmmcall - amd - - 0f 01 /reg=3 /mod=11 /rm=1 - - - - - vmload - amd - - 0f 01 /reg=3 /mod=11 /rm=2 - - - - - vmsave - amd - - 0f 01 /reg=3 /mod=11 /rm=3 - - - - - wait - - 9b - - - - - wbinvd - - 0f 09 - - - - - wrmsr - - 0f 30 - - - - - xadd - - aso oso rexr rexx rexb - 0f c0 - Eb Gb - - - aso oso rexw rexr rexx rexb - 0f c1 - Ev Gv - - - - - xchg - - aso rexr rexx rexb - 86 - Eb Gb - - - aso oso rexw rexr rexx rexb - 87 - Ev Gv - - - oso rexw rexb - 90 - rAXr8 rAX - - - oso rexw rexb - 91 - rCXr9 rAX - - - oso rexw rexb - 92 - rDXr10 rAX - - - oso rexw rexb - 93 - rBXr11 rAX - - - oso rexw rexb - 94 - rSPr12 rAX - - - oso rexw rexb - 95 - rBPr13 rAX - - - oso rexw rexb - 96 - rSIr14 rAX - - - oso rexw rexb - 97 - rDIr15 rAX - - - - - xlatb - - rexw - d7 - - - - - xor - - aso rexr rexx rexb - 30 - Eb Gb - - - aso oso rexw rexr rexx rexb - 31 - Ev Gv - - - aso rexr rexx rexb - 32 - Gb Eb - - - aso oso rexw rexr rexx rexb - 33 - Gv Ev - - - 34 - AL Ib - - - oso rexw - 35 - rAX Iz - sext - - - aso rexr rexx rexb - 80 /reg=6 - Eb Ib - - - aso oso rexw rexr rexx rexb - 81 /reg=6 - Ev Iz - sext - - - aso rexr rexx rexb - 82 /reg=6 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 83 /reg=6 - Ev Ib - sext - - - - - xorpd - - aso rexr rexx rexb - sse66 0f 57 - V W - - - - - xorps - - aso rexr rexx rexb - 0f 57 - V W - - - - - xcryptecb - - 0f a7 /mod=11 /rm=0 /reg=1 - - - - - xcryptcbc - - 0f a7 /mod=11 /rm=0 /reg=2 - - - - - xcryptctr - - 0f a7 /mod=11 /rm=0 /reg=3 - - - - - xcryptcfb - - 0f a7 /mod=11 /rm=0 /reg=4 - - - - - xcryptofb - - 0f a7 /mod=11 /rm=0 /reg=5 - - - - - xsha1 - - 0f a6 /mod=11 /rm=0 /reg=1 - - - - - xsha256 - - 0f a6 /mod=11 /rm=0 /reg=2 - - - - - xstore - - 0f a7 /mod=11 /rm=0 /reg=0 - - - - - db - - - - - - movdqa - - aso rexr rexx rexb - sse66 0f 7f - W V - - - aso rexr rexx rexb - sse66 0f 6f - V W - - - - - movdq2q - - aso rexb - ssef2 0f d6 - P VR - - - - - movdqu - - aso rexr rexx rexb - ssef3 0f 6f - V W - - - aso rexr rexx rexb - ssef3 0f 7f - W V - - - - - movq2dq - - aso - ssef3 0f d6 - V PR - - - - - paddq - - aso rexr rexx rexb - 0f d4 - P Q - - - aso rexr rexx rexb - sse66 0f d4 - V W - - - - - psubq - - aso rexr rexx rexb - sse66 0f fb - V W - - - aso rexr rexx rexb - 0f fb - P Q - - - - - pmuludq - - aso rexr rexx rexb - 0f f4 - P Q - - - aso rexr rexx rexb - sse66 0f f4 - V W - - - - - pshufhw - - aso rexr rexx rexb - ssef3 0f 70 - V W Ib - - - - - pshuflw - - aso rexr rexx rexb - ssef2 0f 70 - V W Ib - - - - - pshufd - - aso rexr rexx rexb - sse66 0f 70 - V W Ib - - - - - pslldq - - rexb - sse66 0f 73 /reg=7 - VR Ib - - - - - psrldq - - rexb - sse66 0f 73 /reg=3 - VR Ib - - - - - punpckhqdq - - aso rexr rexx rexb - sse66 0f 6d - V W - - - - - punpcklqdq - - aso rexr rexx rexb - sse66 0f 6c - V W - - - - - - - addsubpd - - aso rexr rexx rexb - sse66 0f d0 - V W - - - - - addsubps - - aso rexr rexx rexb - ssef2 0f d0 - V W - - - - - haddpd - - aso rexr rexx rexb - sse66 0f 7c - V W - - - - - haddps - - aso rexr rexx rexb - ssef2 0f 7c - V W - - - - - hsubpd - - aso rexr rexx rexb - sse66 0f 7d - V W - - - - - hsubps - - aso rexr rexx rexb - ssef2 0f 7d - V W - - - - - movddup - - aso rexr rexx rexb - ssef2 0f 12 /mod=11 - V W - - - aso rexr rexx rexb - ssef2 0f 12 /mod=!11 - V W - - - - - movshdup - - aso rexr rexx rexb - ssef3 0f 16 /mod=11 - V W - - - aso rexr rexx rexb - ssef3 0f 16 /mod=!11 - V W - - - - - movsldup - - aso rexr rexx rexb - ssef3 0f 12 /mod=11 - V W - - - aso rexr rexx rexb - ssef3 0f 12 /mod=!11 - V W - - - - - - - pabsb - - aso rexr rexx rexb - 0f 38 1c - P Q - - - aso rexr rexx rexb - sse66 0f 38 1c - V W - - - - - pabsw - - aso rexr rexx rexb - 0f 38 1d - P Q - - - aso rexr rexx rexb - sse66 0f 38 1d - V W - - - - - pabsd - - aso rexr rexx rexb - 0f 38 1e - P Q - - - aso rexr rexx rexb - sse66 0f 38 1e - V W - - - - - psignb - - aso rexr rexx rexb - 0f 38 00 - P Q - - - aso rexr rexx rexb - sse66 0f 38 00 - V W - - - - - phaddw - - aso rexr rexx rexb - 0f 38 01 - P Q - - - aso rexr rexx rexb - sse66 0f 38 01 - V W - - - - - phaddd - - aso rexr rexx rexb - 0f 38 02 - P Q - - - aso rexr rexx rexb - sse66 0f 38 02 - V W - - - - - phaddsw - - aso rexr rexx rexb - 0f 38 03 - P Q - - - aso rexr rexx rexb - sse66 0f 38 03 - V W - - - - - pmaddubsw - - aso rexr rexx rexb - 0f 38 04 - P Q - - - aso rexr rexx rexb - sse66 0f 38 04 - V W - - - - - phsubw - - aso rexr rexx rexb - 0f 38 05 - P Q - - - aso rexr rexx rexb - sse66 0f 38 05 - V W - - - - - phsubd - - aso rexr rexx rexb - 0f 38 06 - P Q - - - aso rexr rexx rexb - sse66 0f 38 06 - V W - - - - - phsubsw - - aso rexr rexx rexb - 0f 38 07 - P Q - - - aso rexr rexx rexb - sse66 0f 38 07 - V W - - - - - psignb - - aso rexr rexx rexb - 0f 38 08 - P Q - - - aso rexr rexx rexb - sse66 0f 38 08 - V W - - - - - psignd - - aso rexr rexx rexb - 0f 38 0a - P Q - - - aso rexr rexx rexb - sse66 0f 38 0a - V W - - - - - psignw - - aso rexr rexx rexb - 0f 38 09 - P Q - - - aso rexr rexx rexb - sse66 0f 38 09 - V W - - - - - pmulhrsw - - aso rexr rexx rexb - 0f 38 0b - P Q - - - aso rexr rexx rexb - sse66 0f 38 0b - V W - - - - - palignr - - aso rexr rexx rexb - 0f 3a 0f - P Q Ib - - - aso rexr rexx rexb - sse66 0f 3a 0f - V W Ib - - - - - - - pblendvb - - aso rexr rexx rexb - sse66 0f 38 10 - V W - - - - - pmuldq - - aso rexr rexx rexb - sse66 0f 38 28 - V W - - - - - pminsb - - aso rexr rexx rexb - sse66 0f 38 38 - V W - - - - - pminsd - - aso rexr rexx rexb - sse66 0f 38 39 - V W - - - - - pminuw - - aso rexr rexx rexb - sse66 0f 38 3a - V W - - - - - pminud - - aso rexr rexx rexb - sse66 0f 38 3b - V W - - - - - pmaxsb - - aso rexr rexx rexb - sse66 0f 38 3c - V W - - - - - pmaxsd - - aso rexr rexx rexb - sse66 0f 38 3d - V W - - - - - pmaxud - - aso rexr rexx rexb - sse66 0f 38 3f - V W - - - - - pmulld - - aso rexr rexx rexb - sse66 0f 38 40 - V W - - - - - phminposuw - - aso rexr rexx rexb - sse66 0f 38 41 - V W - - - - - roundps - - aso rexr rexx rexb - sse66 0f 3a 08 - V W Ib - - - - - roundpd - - aso rexr rexx rexb - sse66 0f 3a 09 - V W Ib - - - - - roundss - - aso rexr rexx rexb - sse66 0f 3a 0a - V W Ib - - - - - roundsd - - aso rexr rexx rexb - sse66 0f 3a 0b - V W Ib - - - - - blendpd - - aso rexr rexx rexb - sse66 0f 3a 0d - V W Ib - - - - - pblendw - - aso rexr rexx rexb - sse66 0f 3a 0e - V W Ib - - - - - blendps - - aso rexr rexx rexb - sse66 0f 3a 0c - V W Ib - - - - - blendvpd - - aso rexr rexx rexb - sse66 0f 38 15 - V W - - - - - blendvps - - aso rexr rexx rexb - sse66 0f 38 14 - V W - - - - - dpps - - aso rexr rexx rexb - sse66 0f 3a 40 - V W Ib - - - - - dppd - - aso rexr rexx rexb - sse66 0f 3a 41 - V W Ib - - - - - mpsadbw - - aso rexr rexx rexb - sse66 0f 3a 42 - V W Ib - - - - - extractps - - aso rexr rexw rexb - sse66 0f 3a 17 - MdRy V Ib - - - - - invalid - - - diff --git a/masm/disassembler/udis86/ud_opcode.py b/masm/disassembler/udis86/ud_opcode.py deleted file mode 100644 index f301b52461..0000000000 --- a/masm/disassembler/udis86/ud_opcode.py +++ /dev/null @@ -1,235 +0,0 @@ -# udis86 - scripts/ud_opcode.py -# -# Copyright (c) 2009 Vivek Thampi -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -class UdOpcodeTables: - - TableInfo = { - 'opctbl' : { 'name' : 'UD_TAB__OPC_TABLE', 'size' : 256 }, - '/sse' : { 'name' : 'UD_TAB__OPC_SSE', 'size' : 4 }, - '/reg' : { 'name' : 'UD_TAB__OPC_REG', 'size' : 8 }, - '/rm' : { 'name' : 'UD_TAB__OPC_RM', 'size' : 8 }, - '/mod' : { 'name' : 'UD_TAB__OPC_MOD', 'size' : 2 }, - '/m' : { 'name' : 'UD_TAB__OPC_MODE', 'size' : 3 }, - '/x87' : { 'name' : 'UD_TAB__OPC_X87', 'size' : 64 }, - '/a' : { 'name' : 'UD_TAB__OPC_ASIZE', 'size' : 3 }, - '/o' : { 'name' : 'UD_TAB__OPC_OSIZE', 'size' : 3 }, - '/3dnow' : { 'name' : 'UD_TAB__OPC_3DNOW', 'size' : 256 }, - 'vendor' : { 'name' : 'UD_TAB__OPC_VENDOR', 'size' : 3 }, - } - - OpcodeTable0 = { - 'type' : 'opctbl', - 'entries' : {}, - 'meta' : 'table0' - } - - OpcExtIndex = { - - # ssef2, ssef3, sse66 - 'sse': { - 'none' : '00', - 'f2' : '01', - 'f3' : '02', - '66' : '03' - }, - - # /mod= - 'mod': { - '!11' : '00', - '11' : '01' - }, - - # /m=, /o=, /a= - 'mode': { - '16' : '00', - '32' : '01', - '64' : '02' - }, - - 'vendor' : { - 'amd' : '00', - 'intel' : '01', - 'any' : '02' - } - } - - InsnTable = [] - MnemonicsTable = [] - - ThreeDNowTable = {} - - def sizeOfTable( self, t ): - return self.TableInfo[ t ][ 'size' ] - - def nameOfTable( self, t ): - return self.TableInfo[ t ][ 'name' ] - - # - # Updates a table entry: If the entry doesn't exist - # it will create the entry, otherwise, it will walk - # while validating the path. - # - def updateTable( self, table, index, type, meta ): - if not index in table[ 'entries' ]: - table[ 'entries' ][ index ] = { 'type' : type, 'entries' : {}, 'meta' : meta } - if table[ 'entries' ][ index ][ 'type' ] != type: - raise NameError( "error: violation in opcode mapping (overwrite) %s with %s." % - ( table[ 'entries' ][ index ][ 'type' ], type) ) - return table[ 'entries' ][ index ] - - class Insn: - """An abstract type representing an instruction in the opcode map. - """ - - # A mapping of opcode extensions to their representational - # values used in the opcode map. - OpcExtMap = { - '/rm' : lambda v: "%02x" % int(v, 16), - '/x87' : lambda v: "%02x" % int(v, 16), - '/3dnow' : lambda v: "%02x" % int(v, 16), - '/reg' : lambda v: "%02x" % int(v, 16), - # modrm.mod - # (!11, 11) => (00, 01) - '/mod' : lambda v: '00' if v == '!11' else '01', - # Mode extensions: - # (16, 32, 64) => (00, 01, 02) - '/o' : lambda v: "%02x" % (int(v) / 32), - '/a' : lambda v: "%02x" % (int(v) / 32), - '/m' : lambda v: "%02x" % (int(v) / 32), - '/sse' : lambda v: UdOpcodeTables.OpcExtIndex['sse'][v] - } - - def __init__(self, prefixes, mnemonic, opcodes, operands, vendor): - self.opcodes = opcodes - self.prefixes = prefixes - self.mnemonic = mnemonic - self.operands = operands - self.vendor = vendor - self.opcext = {} - - ssePrefix = None - if self.opcodes[0] in ('ssef2', 'ssef3', 'sse66'): - ssePrefix = self.opcodes[0][3:] - self.opcodes.pop(0) - - # do some preliminary decoding of the instruction type - # 1byte, 2byte or 3byte instruction? - self.nByteInsn = 1 - if self.opcodes[0] == '0f': # 2byte - # 2+ byte opcodes are always disambiguated by an - # sse prefix, unless it is a 3d now instruction - # which is 0f 0f ... - if self.opcodes[1] != '0f' and ssePrefix is None: - ssePrefix = 'none' - if self.opcodes[1] in ('38', '3a'): # 3byte - self.nByteInsn = 3 - else: - self.nByteInsn = 2 - - # The opcode that indexes into the opcode table. - self.opcode = self.opcodes[self.nByteInsn - 1] - - # Record opcode extensions - for opcode in self.opcodes[self.nByteInsn:]: - arg, val = opcode.split('=') - self.opcext[arg] = self.OpcExtMap[arg](val) - - # Record sse extension: the reason sse extension is handled - # separately is that historically sse was handled as a first - # class opcode, not as an extension. Now that sse is handled - # as an extension, we do the manual conversion here, as opposed - # to modifying the opcode xml file. - if ssePrefix is not None: - self.opcext['/sse'] = self.OpcExtMap['/sse'](ssePrefix) - - def parse(self, table, insn): - index = insn.opcodes[0]; - if insn.nByteInsn > 1: - assert index == '0f' - table = self.updateTable(table, index, 'opctbl', '0f') - index = insn.opcodes[1] - - if insn.nByteInsn == 3: - table = self.updateTable(table, index, 'opctbl', index) - index = insn.opcodes[2] - - # Walk down the tree, create levels as needed, for opcode - # extensions. The order is important, and determines how - # well the opcode table is packed. Also note, /sse must be - # before /o, because /sse may consume operand size prefix - # affect the outcome of /o. - for ext in ('/mod', '/x87', '/reg', '/rm', '/sse', - '/o', '/a', '/m', '/3dnow'): - if ext in insn.opcext: - table = self.updateTable(table, index, ext, ext) - index = insn.opcext[ext] - - # additional table for disambiguating vendor - if len(insn.vendor): - table = self.updateTable(table, index, 'vendor', insn.vendor) - index = self.OpcExtIndex['vendor'][insn.vendor] - - # make leaf node entries - leaf = self.updateTable(table, index, 'insn', '') - - leaf['mnemonic'] = insn.mnemonic - leaf['prefixes'] = insn.prefixes - leaf['operands'] = insn.operands - - # add instruction to linear table of instruction forms - self.InsnTable.append({ 'prefixes' : insn.prefixes, - 'mnemonic' : insn.mnemonic, - 'operands' : insn.operands }) - - # add mnemonic to mnemonic table - if not insn.mnemonic in self.MnemonicsTable: - self.MnemonicsTable.append(insn.mnemonic) - - - # Adds an instruction definition to the opcode tables - def addInsnDef( self, prefixes, mnemonic, opcodes, operands, vendor ): - insn = self.Insn(prefixes=prefixes, - mnemonic=mnemonic, - opcodes=opcodes, - operands=operands, - vendor=vendor) - self.parse(self.OpcodeTable0, insn) - - def print_table( self, table, pfxs ): - print "%s |" % pfxs - keys = table[ 'entries' ].keys() - if ( len( keys ) ): - keys.sort() - for idx in keys: - e = table[ 'entries' ][ idx ] - if e[ 'type' ] == 'insn': - print "%s |-<%s>" % ( pfxs, idx ), - print "%s %s" % ( e[ 'mnemonic' ], ' '.join( e[ 'operands'] ) ) - else: - print "%s |-<%s> %s" % ( pfxs, idx, e['type'] ) - self.print_table( e, pfxs + ' |' ) - - def print_tree( self ): - self.print_table( self.OpcodeTable0, '' ) diff --git a/masm/disassembler/udis86/ud_optable.py b/masm/disassembler/udis86/ud_optable.py deleted file mode 100644 index 5b5c55d3b8..0000000000 --- a/masm/disassembler/udis86/ud_optable.py +++ /dev/null @@ -1,103 +0,0 @@ -# udis86 - scripts/ud_optable.py (optable.xml parser) -# -# Copyright (c) 2009 Vivek Thampi -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import os -import sys -from xml.dom import minidom - -class UdOptableXmlParser: - - def parseDef( self, node ): - ven = '' - pfx = [] - opc = [] - opr = [] - for def_node in node.childNodes: - if not def_node.localName: - continue - if def_node.localName == 'pfx': - pfx = def_node.firstChild.data.split(); - elif def_node.localName == 'opc': - opc = def_node.firstChild.data.split(); - elif def_node.localName == 'opr': - opr = def_node.firstChild.data.split(); - elif def_node.localName == 'mode': - pfx.extend( def_node.firstChild.data.split() ); - elif def_node.localName == 'syn': - pfx.extend( def_node.firstChild.data.split() ); - elif def_node.localName == 'vendor': - ven = ( def_node.firstChild.data ); - else: - print "warning: invalid node - %s" % def_node.localName - continue - return ( pfx, opc, opr, ven ) - - def parse( self, xml, fn ): - xmlDoc = minidom.parse( xml ) - self.TlNode = xmlDoc.firstChild - - while self.TlNode and self.TlNode.localName != "x86optable": - self.TlNode = self.TlNode.nextSibling - - for insnNode in self.TlNode.childNodes: - if not insnNode.localName: - continue - if insnNode.localName != "instruction": - print "warning: invalid insn node - %s" % insnNode.localName - continue - - mnemonic = insnNode.getElementsByTagName( 'mnemonic' )[ 0 ].firstChild.data - vendor = '' - - for node in insnNode.childNodes: - if node.localName == 'vendor': - vendor = node.firstChild.data - elif node.localName == 'def': - ( prefixes, opcodes, operands, local_vendor ) = \ - self.parseDef( node ) - if ( len( local_vendor ) ): - vendor = local_vendor - # callback - fn( prefixes, mnemonic, opcodes, operands, vendor ) - - -def printFn( pfx, mnm, opc, opr, ven ): - print 'def: ', - if len( pfx ): - print ' '.join( pfx ), - print "%s %s %s %s" % \ - ( mnm, ' '.join( opc ), ' '.join( opr ), ven ) - - -def parse( xml, callback ): - parser = UdOptableXmlParser() - parser.parse( xml, callback ) - -def main(): - parser = UdOptableXmlParser() - parser.parse( sys.argv[ 1 ], printFn ) - -if __name__ == "__main__": - main() diff --git a/masm/disassembler/udis86/udis86.c b/masm/disassembler/udis86/udis86.c deleted file mode 100644 index fbf76707a0..0000000000 --- a/masm/disassembler/udis86/udis86.c +++ /dev/null @@ -1,183 +0,0 @@ -/* udis86 - libudis86/udis86.c - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include - -#if USE(UDIS86) - -#include "udis86_input.h" -#include "udis86_extern.h" - -#ifndef __UD_STANDALONE__ -# include -# include -#endif /* __UD_STANDALONE__ */ - -/* ============================================================================= - * ud_init() - Initializes ud_t object. - * ============================================================================= - */ -extern void -ud_init(struct ud* u) -{ - memset((void*)u, 0, sizeof(struct ud)); - ud_set_mode(u, 16); - u->mnemonic = UD_Iinvalid; - ud_set_pc(u, 0); -#ifndef __UD_STANDALONE__ - ud_set_input_file(u, stdin); -#endif /* __UD_STANDALONE__ */ -} - -/* ============================================================================= - * ud_disassemble() - disassembles one instruction and returns the number of - * bytes disassembled. A zero means end of disassembly. - * ============================================================================= - */ -extern unsigned int -ud_disassemble(struct ud* u) -{ - if (ud_input_end(u)) - return 0; - - - u->insn_buffer[0] = u->insn_hexcode[0] = 0; - - - if (ud_decode(u) == 0) - return 0; - if (u->translator) - u->translator(u); - return ud_insn_len(u); -} - -/* ============================================================================= - * ud_set_mode() - Set Disassemly Mode. - * ============================================================================= - */ -extern void -ud_set_mode(struct ud* u, uint8_t m) -{ - switch(m) { - case 16: - case 32: - case 64: u->dis_mode = m ; return; - default: u->dis_mode = 16; return; - } -} - -/* ============================================================================= - * ud_set_vendor() - Set vendor. - * ============================================================================= - */ -extern void -ud_set_vendor(struct ud* u, unsigned v) -{ - switch(v) { - case UD_VENDOR_INTEL: - u->vendor = v; - break; - case UD_VENDOR_ANY: - u->vendor = v; - break; - default: - u->vendor = UD_VENDOR_AMD; - } -} - -/* ============================================================================= - * ud_set_pc() - Sets code origin. - * ============================================================================= - */ -extern void -ud_set_pc(struct ud* u, uint64_t o) -{ - u->pc = o; -} - -/* ============================================================================= - * ud_set_syntax() - Sets the output syntax. - * ============================================================================= - */ -extern void -ud_set_syntax(struct ud* u, void (*t)(struct ud*)) -{ - u->translator = t; -} - -/* ============================================================================= - * ud_insn() - returns the disassembled instruction - * ============================================================================= - */ -extern char* -ud_insn_asm(struct ud* u) -{ - return u->insn_buffer; -} - -/* ============================================================================= - * ud_insn_offset() - Returns the offset. - * ============================================================================= - */ -extern uint64_t -ud_insn_off(struct ud* u) -{ - return u->insn_offset; -} - - -/* ============================================================================= - * ud_insn_hex() - Returns hex form of disassembled instruction. - * ============================================================================= - */ -extern char* -ud_insn_hex(struct ud* u) -{ - return u->insn_hexcode; -} - -/* ============================================================================= - * ud_insn_ptr() - Returns code disassembled. - * ============================================================================= - */ -extern uint8_t* -ud_insn_ptr(struct ud* u) -{ - return u->inp_sess; -} - -/* ============================================================================= - * ud_insn_len() - Returns the count of bytes disassembled. - * ============================================================================= - */ -extern unsigned int -ud_insn_len(struct ud* u) -{ - return u->inp_ctr; -} - -#endif // USE(UDIS86) diff --git a/masm/disassembler/udis86/udis86.h b/masm/disassembler/udis86/udis86.h deleted file mode 100644 index baaf495e04..0000000000 --- a/masm/disassembler/udis86/udis86.h +++ /dev/null @@ -1,33 +0,0 @@ -/* udis86 - udis86.h - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef UDIS86_H -#define UDIS86_H - -#include "udis86_types.h" -#include "udis86_extern.h" -#include "udis86_itab.h" - -#endif diff --git a/masm/disassembler/udis86/udis86_decode.c b/masm/disassembler/udis86/udis86_decode.c deleted file mode 100644 index a3fd576655..0000000000 --- a/masm/disassembler/udis86/udis86_decode.c +++ /dev/null @@ -1,1142 +0,0 @@ -/* udis86 - libudis86/decode.c - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include - -#if USE(UDIS86) - -#include "udis86_extern.h" -#include "udis86_types.h" -#include "udis86_input.h" -#include "udis86_decode.h" -#include - -#define dbg(x, n...) -/* #define dbg printf */ - -#ifndef __UD_STANDALONE__ -# include -#endif /* __UD_STANDALONE__ */ - -/* The max number of prefixes to an instruction */ -#define MAX_PREFIXES 15 - -/* instruction aliases and special cases */ -static struct ud_itab_entry s_ie__invalid = - { UD_Iinvalid, O_NONE, O_NONE, O_NONE, P_none }; - -static int -decode_ext(struct ud *u, uint16_t ptr); - - -static inline int -eff_opr_mode(int dis_mode, int rex_w, int pfx_opr) -{ - if (dis_mode == 64) { - return rex_w ? 64 : (pfx_opr ? 16 : 32); - } else if (dis_mode == 32) { - return pfx_opr ? 16 : 32; - } else { - ASSERT(dis_mode == 16); - return pfx_opr ? 32 : 16; - } -} - - -static inline int -eff_adr_mode(int dis_mode, int pfx_adr) -{ - if (dis_mode == 64) { - return pfx_adr ? 32 : 64; - } else if (dis_mode == 32) { - return pfx_adr ? 16 : 32; - } else { - ASSERT(dis_mode == 16); - return pfx_adr ? 32 : 16; - } -} - - -/* Looks up mnemonic code in the mnemonic string table - * Returns NULL if the mnemonic code is invalid - */ -const char * ud_lookup_mnemonic( enum ud_mnemonic_code c ) -{ - return ud_mnemonics_str[ c ]; -} - - -/* - * decode_prefixes - * - * Extracts instruction prefixes. - */ -static int -decode_prefixes(struct ud *u) -{ - unsigned int have_pfx = 1; - unsigned int i; - uint8_t curr; - - /* if in error state, bail out */ - if ( u->error ) - return -1; - - /* keep going as long as there are prefixes available */ - for ( i = 0; have_pfx ; ++i ) { - - /* Get next byte. */ - ud_inp_next(u); - if ( u->error ) - return -1; - curr = ud_inp_curr( u ); - - /* rex prefixes in 64bit mode */ - if ( u->dis_mode == 64 && ( curr & 0xF0 ) == 0x40 ) { - u->pfx_rex = curr; - } else { - switch ( curr ) - { - case 0x2E : - u->pfx_seg = UD_R_CS; - u->pfx_rex = 0; - break; - case 0x36 : - u->pfx_seg = UD_R_SS; - u->pfx_rex = 0; - break; - case 0x3E : - u->pfx_seg = UD_R_DS; - u->pfx_rex = 0; - break; - case 0x26 : - u->pfx_seg = UD_R_ES; - u->pfx_rex = 0; - break; - case 0x64 : - u->pfx_seg = UD_R_FS; - u->pfx_rex = 0; - break; - case 0x65 : - u->pfx_seg = UD_R_GS; - u->pfx_rex = 0; - break; - case 0x67 : /* adress-size override prefix */ - u->pfx_adr = 0x67; - u->pfx_rex = 0; - break; - case 0xF0 : - u->pfx_lock = 0xF0; - u->pfx_rex = 0; - break; - case 0x66: - /* the 0x66 sse prefix is only effective if no other sse prefix - * has already been specified. - */ - if ( !u->pfx_insn ) u->pfx_insn = 0x66; - u->pfx_opr = 0x66; - u->pfx_rex = 0; - break; - case 0xF2: - u->pfx_insn = 0xF2; - u->pfx_repne = 0xF2; - u->pfx_rex = 0; - break; - case 0xF3: - u->pfx_insn = 0xF3; - u->pfx_rep = 0xF3; - u->pfx_repe = 0xF3; - u->pfx_rex = 0; - break; - default : - /* No more prefixes */ - have_pfx = 0; - break; - } - } - - /* check if we reached max instruction length */ - if ( i + 1 == MAX_INSN_LENGTH ) { - u->error = 1; - break; - } - } - - /* return status */ - if ( u->error ) - return -1; - - /* rewind back one byte in stream, since the above loop - * stops with a non-prefix byte. - */ - ud_inp_back(u); - return 0; -} - - -static inline unsigned int modrm( struct ud * u ) -{ - if ( !u->have_modrm ) { - u->modrm = ud_inp_next( u ); - u->have_modrm = 1; - } - return u->modrm; -} - - -static unsigned int resolve_operand_size( const struct ud * u, unsigned int s ) -{ - switch ( s ) - { - case SZ_V: - return ( u->opr_mode ); - case SZ_Z: - return ( u->opr_mode == 16 ) ? 16 : 32; - case SZ_P: - return ( u->opr_mode == 16 ) ? SZ_WP : SZ_DP; - case SZ_MDQ: - return ( u->opr_mode == 16 ) ? 32 : u->opr_mode; - case SZ_RDQ: - return ( u->dis_mode == 64 ) ? 64 : 32; - default: - return s; - } -} - - -static int resolve_mnemonic( struct ud* u ) -{ - /* far/near flags */ - u->br_far = 0; - u->br_near = 0; - /* readjust operand sizes for call/jmp instrcutions */ - if ( u->mnemonic == UD_Icall || u->mnemonic == UD_Ijmp ) { - /* WP: 16:16 pointer */ - if ( u->operand[ 0 ].size == SZ_WP ) { - u->operand[ 0 ].size = 16; - u->br_far = 1; - u->br_near= 0; - /* DP: 32:32 pointer */ - } else if ( u->operand[ 0 ].size == SZ_DP ) { - u->operand[ 0 ].size = 32; - u->br_far = 1; - u->br_near= 0; - } else { - u->br_far = 0; - u->br_near= 1; - } - /* resolve 3dnow weirdness. */ - } else if ( u->mnemonic == UD_I3dnow ) { - u->mnemonic = ud_itab[ u->le->table[ ud_inp_curr( u ) ] ].mnemonic; - } - /* SWAPGS is only valid in 64bits mode */ - if ( u->mnemonic == UD_Iswapgs && u->dis_mode != 64 ) { - u->error = 1; - return -1; - } - - if (u->mnemonic == UD_Ixchg) { - if ((u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_AX && - u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_AX) || - (u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_EAX && - u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_EAX)) { - u->operand[0].type = UD_NONE; - u->operand[1].type = UD_NONE; - u->mnemonic = UD_Inop; - } - } - - if (u->mnemonic == UD_Inop && u->pfx_rep) { - u->pfx_rep = 0; - u->mnemonic = UD_Ipause; - } - return 0; -} - - -/* ----------------------------------------------------------------------------- - * decode_a()- Decodes operands of the type seg:offset - * ----------------------------------------------------------------------------- - */ -static void -decode_a(struct ud* u, struct ud_operand *op) -{ - if (u->opr_mode == 16) { - /* seg16:off16 */ - op->type = UD_OP_PTR; - op->size = 32; - op->lval.ptr.off = ud_inp_uint16(u); - op->lval.ptr.seg = ud_inp_uint16(u); - } else { - /* seg16:off32 */ - op->type = UD_OP_PTR; - op->size = 48; - op->lval.ptr.off = ud_inp_uint32(u); - op->lval.ptr.seg = ud_inp_uint16(u); - } -} - -/* ----------------------------------------------------------------------------- - * decode_gpr() - Returns decoded General Purpose Register - * ----------------------------------------------------------------------------- - */ -static enum ud_type -decode_gpr(register struct ud* u, unsigned int s, unsigned char rm) -{ - s = resolve_operand_size(u, s); - - switch (s) { - case 64: - return UD_R_RAX + rm; - case SZ_DP: - case 32: - return UD_R_EAX + rm; - case SZ_WP: - case 16: - return UD_R_AX + rm; - case 8: - if (u->dis_mode == 64 && u->pfx_rex) { - if (rm >= 4) - return UD_R_SPL + (rm-4); - return UD_R_AL + rm; - } else return UD_R_AL + rm; - default: - return 0; - } -} - -/* ----------------------------------------------------------------------------- - * resolve_gpr64() - 64bit General Purpose Register-Selection. - * ----------------------------------------------------------------------------- - */ -static enum ud_type -resolve_gpr64(struct ud* u, enum ud_operand_code gpr_op, enum ud_operand_size * size) -{ - if (gpr_op >= OP_rAXr8 && gpr_op <= OP_rDIr15) - gpr_op = (gpr_op - OP_rAXr8) | (REX_B(u->pfx_rex) << 3); - else gpr_op = (gpr_op - OP_rAX); - - if (u->opr_mode == 16) { - *size = 16; - return gpr_op + UD_R_AX; - } - if (u->dis_mode == 32 || - (u->opr_mode == 32 && ! (REX_W(u->pfx_rex) || u->default64))) { - *size = 32; - return gpr_op + UD_R_EAX; - } - - *size = 64; - return gpr_op + UD_R_RAX; -} - -/* ----------------------------------------------------------------------------- - * resolve_gpr32 () - 32bit General Purpose Register-Selection. - * ----------------------------------------------------------------------------- - */ -static enum ud_type -resolve_gpr32(struct ud* u, enum ud_operand_code gpr_op) -{ - gpr_op = gpr_op - OP_eAX; - - if (u->opr_mode == 16) - return gpr_op + UD_R_AX; - - return gpr_op + UD_R_EAX; -} - -/* ----------------------------------------------------------------------------- - * resolve_reg() - Resolves the register type - * ----------------------------------------------------------------------------- - */ -static enum ud_type -resolve_reg(struct ud* u, unsigned int type, unsigned char i) -{ - switch (type) { - case T_MMX : return UD_R_MM0 + (i & 7); - case T_XMM : return UD_R_XMM0 + i; - case T_CRG : return UD_R_CR0 + i; - case T_DBG : return UD_R_DR0 + i; - case T_SEG : { - /* - * Only 6 segment registers, anything else is an error. - */ - if ((i & 7) > 5) { - u->error = 1; - } else { - return UD_R_ES + (i & 7); - } - } - case T_NONE: - default: return UD_NONE; - } -} - -/* ----------------------------------------------------------------------------- - * decode_imm() - Decodes Immediate values. - * ----------------------------------------------------------------------------- - */ -static void -decode_imm(struct ud* u, unsigned int s, struct ud_operand *op) -{ - op->size = resolve_operand_size(u, s); - op->type = UD_OP_IMM; - - switch (op->size) { - case 8: op->lval.sbyte = ud_inp_uint8(u); break; - case 16: op->lval.uword = ud_inp_uint16(u); break; - case 32: op->lval.udword = ud_inp_uint32(u); break; - case 64: op->lval.uqword = ud_inp_uint64(u); break; - default: return; - } -} - - -/* - * decode_modrm_reg - * - * Decodes reg field of mod/rm byte - * - */ -static void -decode_modrm_reg(struct ud *u, - struct ud_operand *operand, - unsigned int type, - unsigned int size) -{ - uint8_t reg = (REX_R(u->pfx_rex) << 3) | MODRM_REG(modrm(u)); - operand->type = UD_OP_REG; - operand->size = resolve_operand_size(u, size); - - if (type == T_GPR) { - operand->base = decode_gpr(u, operand->size, reg); - } else { - operand->base = resolve_reg(u, type, reg); - } -} - - -/* - * decode_modrm_rm - * - * Decodes rm field of mod/rm byte - * - */ -static void -decode_modrm_rm(struct ud *u, - struct ud_operand *op, - unsigned char type, - unsigned int size) - -{ - unsigned char mod, rm, reg; - - /* get mod, r/m and reg fields */ - mod = MODRM_MOD(modrm(u)); - rm = (REX_B(u->pfx_rex) << 3) | MODRM_RM(modrm(u)); - reg = (REX_R(u->pfx_rex) << 3) | MODRM_REG(modrm(u)); - - op->size = resolve_operand_size(u, size); - - /* - * If mod is 11b, then the modrm.rm specifies a register. - * - */ - if (mod == 3) { - op->type = UD_OP_REG; - if (type == T_GPR) { - op->base = decode_gpr(u, op->size, rm); - } else { - op->base = resolve_reg(u, type, (REX_B(u->pfx_rex) << 3) | (rm & 7)); - } - return; - } - - - /* - * !11 => Memory Address - */ - op->type = UD_OP_MEM; - - if (u->adr_mode == 64) { - op->base = UD_R_RAX + rm; - if (mod == 1) { - op->offset = 8; - } else if (mod == 2) { - op->offset = 32; - } else if (mod == 0 && (rm & 7) == 5) { - op->base = UD_R_RIP; - op->offset = 32; - } else { - op->offset = 0; - } - /* - * Scale-Index-Base (SIB) - */ - if ((rm & 7) == 4) { - ud_inp_next(u); - - op->scale = (1 << SIB_S(ud_inp_curr(u))) & ~1; - op->index = UD_R_RAX + (SIB_I(ud_inp_curr(u)) | (REX_X(u->pfx_rex) << 3)); - op->base = UD_R_RAX + (SIB_B(ud_inp_curr(u)) | (REX_B(u->pfx_rex) << 3)); - - /* special conditions for base reference */ - if (op->index == UD_R_RSP) { - op->index = UD_NONE; - op->scale = UD_NONE; - } - - if (op->base == UD_R_RBP || op->base == UD_R_R13) { - if (mod == 0) { - op->base = UD_NONE; - } - if (mod == 1) { - op->offset = 8; - } else { - op->offset = 32; - } - } - } - } else if (u->adr_mode == 32) { - op->base = UD_R_EAX + rm; - if (mod == 1) { - op->offset = 8; - } else if (mod == 2) { - op->offset = 32; - } else if (mod == 0 && rm == 5) { - op->base = UD_NONE; - op->offset = 32; - } else { - op->offset = 0; - } - - /* Scale-Index-Base (SIB) */ - if ((rm & 7) == 4) { - ud_inp_next(u); - - op->scale = (1 << SIB_S(ud_inp_curr(u))) & ~1; - op->index = UD_R_EAX + (SIB_I(ud_inp_curr(u)) | (REX_X(u->pfx_rex) << 3)); - op->base = UD_R_EAX + (SIB_B(ud_inp_curr(u)) | (REX_B(u->pfx_rex) << 3)); - - if (op->index == UD_R_ESP) { - op->index = UD_NONE; - op->scale = UD_NONE; - } - - /* special condition for base reference */ - if (op->base == UD_R_EBP) { - if (mod == 0) { - op->base = UD_NONE; - } - if (mod == 1) { - op->offset = 8; - } else { - op->offset = 32; - } - } - } - } else { - const unsigned int bases[] = { UD_R_BX, UD_R_BX, UD_R_BP, UD_R_BP, - UD_R_SI, UD_R_DI, UD_R_BP, UD_R_BX }; - const unsigned int indices[] = { UD_R_SI, UD_R_DI, UD_R_SI, UD_R_DI, - UD_NONE, UD_NONE, UD_NONE, UD_NONE }; - op->base = bases[rm & 7]; - op->index = indices[rm & 7]; - if (mod == 0 && rm == 6) { - op->offset= 16; - op->base = UD_NONE; - } else if (mod == 1) { - op->offset = 8; - } else if (mod == 2) { - op->offset = 16; - } - } - - /* - * extract offset, if any - */ - switch (op->offset) { - case 8 : op->lval.ubyte = ud_inp_uint8(u); break; - case 16: op->lval.uword = ud_inp_uint16(u); break; - case 32: op->lval.udword = ud_inp_uint32(u); break; - case 64: op->lval.uqword = ud_inp_uint64(u); break; - default: break; - } -} - -/* ----------------------------------------------------------------------------- - * decode_o() - Decodes offset - * ----------------------------------------------------------------------------- - */ -static void -decode_o(struct ud* u, unsigned int s, struct ud_operand *op) -{ - switch (u->adr_mode) { - case 64: - op->offset = 64; - op->lval.uqword = ud_inp_uint64(u); - break; - case 32: - op->offset = 32; - op->lval.udword = ud_inp_uint32(u); - break; - case 16: - op->offset = 16; - op->lval.uword = ud_inp_uint16(u); - break; - default: - return; - } - op->type = UD_OP_MEM; - op->size = resolve_operand_size(u, s); -} - -/* ----------------------------------------------------------------------------- - * decode_operands() - Disassembles Operands. - * ----------------------------------------------------------------------------- - */ -static int -decode_operand(struct ud *u, - struct ud_operand *operand, - enum ud_operand_code type, - unsigned int size) -{ - switch (type) { - case OP_A : - decode_a(u, operand); - break; - case OP_MR: - if (MODRM_MOD(modrm(u)) == 3) { - decode_modrm_rm(u, operand, T_GPR, - size == SZ_DY ? SZ_MDQ : SZ_V); - } else if (size == SZ_WV) { - decode_modrm_rm( u, operand, T_GPR, SZ_W); - } else if (size == SZ_BV) { - decode_modrm_rm( u, operand, T_GPR, SZ_B); - } else if (size == SZ_DY) { - decode_modrm_rm( u, operand, T_GPR, SZ_D); - } else { - ASSERT(!"unexpected size"); - } - break; - case OP_M: - if (MODRM_MOD(modrm(u)) == 3) { - u->error = 1; - } - /* intended fall through */ - case OP_E: - decode_modrm_rm(u, operand, T_GPR, size); - break; - break; - case OP_G: - decode_modrm_reg(u, operand, T_GPR, size); - break; - case OP_I: - decode_imm(u, size, operand); - break; - case OP_I1: - operand->type = UD_OP_CONST; - operand->lval.udword = 1; - break; - case OP_PR: - if (MODRM_MOD(modrm(u)) != 3) { - u->error = 1; - } - decode_modrm_rm(u, operand, T_MMX, size); - break; - case OP_P: - decode_modrm_reg(u, operand, T_MMX, size); - break; - case OP_VR: - if (MODRM_MOD(modrm(u)) != 3) { - u->error = 1; - } - /* intended fall through */ - case OP_W: - decode_modrm_rm(u, operand, T_XMM, size); - break; - case OP_V: - decode_modrm_reg(u, operand, T_XMM, size); - break; - case OP_S: - decode_modrm_reg(u, operand, T_SEG, size); - break; - case OP_AL: - case OP_CL: - case OP_DL: - case OP_BL: - case OP_AH: - case OP_CH: - case OP_DH: - case OP_BH: - operand->type = UD_OP_REG; - operand->base = UD_R_AL + (type - OP_AL); - operand->size = 8; - break; - case OP_DX: - operand->type = UD_OP_REG; - operand->base = UD_R_DX; - operand->size = 16; - break; - case OP_O: - decode_o(u, size, operand); - break; - case OP_rAXr8: - case OP_rCXr9: - case OP_rDXr10: - case OP_rBXr11: - case OP_rSPr12: - case OP_rBPr13: - case OP_rSIr14: - case OP_rDIr15: - case OP_rAX: - case OP_rCX: - case OP_rDX: - case OP_rBX: - case OP_rSP: - case OP_rBP: - case OP_rSI: - case OP_rDI: - operand->type = UD_OP_REG; - operand->base = resolve_gpr64(u, type, &operand->size); - break; - case OP_ALr8b: - case OP_CLr9b: - case OP_DLr10b: - case OP_BLr11b: - case OP_AHr12b: - case OP_CHr13b: - case OP_DHr14b: - case OP_BHr15b: { - ud_type_t gpr = (type - OP_ALr8b) + UD_R_AL - + (REX_B(u->pfx_rex) << 3); - if (UD_R_AH <= gpr && u->pfx_rex) { - gpr = gpr + 4; - } - operand->type = UD_OP_REG; - operand->base = gpr; - break; - } - case OP_eAX: - case OP_eCX: - case OP_eDX: - case OP_eBX: - case OP_eSP: - case OP_eBP: - case OP_eSI: - case OP_eDI: - operand->type = UD_OP_REG; - operand->base = resolve_gpr32(u, type); - operand->size = u->opr_mode == 16 ? 16 : 32; - break; - case OP_ES: - case OP_CS: - case OP_DS: - case OP_SS: - case OP_FS: - case OP_GS: - /* in 64bits mode, only fs and gs are allowed */ - if (u->dis_mode == 64) { - if (type != OP_FS && type != OP_GS) { - u->error= 1; - } - } - operand->type = UD_OP_REG; - operand->base = (type - OP_ES) + UD_R_ES; - operand->size = 16; - break; - case OP_J : - decode_imm(u, size, operand); - operand->type = UD_OP_JIMM; - break ; - case OP_Q: - decode_modrm_rm(u, operand, T_MMX, size); - break; - case OP_R : - decode_modrm_rm(u, operand, T_GPR, size); - break; - case OP_C: - decode_modrm_reg(u, operand, T_CRG, size); - break; - case OP_D: - decode_modrm_reg(u, operand, T_DBG, size); - break; - case OP_I3 : - operand->type = UD_OP_CONST; - operand->lval.sbyte = 3; - break; - case OP_ST0: - case OP_ST1: - case OP_ST2: - case OP_ST3: - case OP_ST4: - case OP_ST5: - case OP_ST6: - case OP_ST7: - operand->type = UD_OP_REG; - operand->base = (type - OP_ST0) + UD_R_ST0; - operand->size = 0; - break; - case OP_AX: - operand->type = UD_OP_REG; - operand->base = UD_R_AX; - operand->size = 16; - break; - default : - operand->type = UD_NONE; - break; - } - return 0; -} - - -/* - * decode_operands - * - * Disassemble upto 3 operands of the current instruction being - * disassembled. By the end of the function, the operand fields - * of the ud structure will have been filled. - */ -static int -decode_operands(struct ud* u) -{ - decode_operand(u, &u->operand[0], - u->itab_entry->operand1.type, - u->itab_entry->operand1.size); - decode_operand(u, &u->operand[1], - u->itab_entry->operand2.type, - u->itab_entry->operand2.size); - decode_operand(u, &u->operand[2], - u->itab_entry->operand3.type, - u->itab_entry->operand3.size); - return 0; -} - -/* ----------------------------------------------------------------------------- - * clear_insn() - clear instruction structure - * ----------------------------------------------------------------------------- - */ -static void -clear_insn(register struct ud* u) -{ - u->error = 0; - u->pfx_seg = 0; - u->pfx_opr = 0; - u->pfx_adr = 0; - u->pfx_lock = 0; - u->pfx_repne = 0; - u->pfx_rep = 0; - u->pfx_repe = 0; - u->pfx_rex = 0; - u->pfx_insn = 0; - u->mnemonic = UD_Inone; - u->itab_entry = NULL; - u->have_modrm = 0; - - memset( &u->operand[ 0 ], 0, sizeof( struct ud_operand ) ); - memset( &u->operand[ 1 ], 0, sizeof( struct ud_operand ) ); - memset( &u->operand[ 2 ], 0, sizeof( struct ud_operand ) ); -} - -static int -resolve_mode( struct ud* u ) -{ - /* if in error state, bail out */ - if ( u->error ) return -1; - - /* propagate prefix effects */ - if ( u->dis_mode == 64 ) { /* set 64bit-mode flags */ - - /* Check validity of instruction m64 */ - if ( P_INV64( u->itab_entry->prefix ) ) { - u->error = 1; - return -1; - } - - /* effective rex prefix is the effective mask for the - * instruction hard-coded in the opcode map. - */ - u->pfx_rex = ( u->pfx_rex & 0x40 ) | - ( u->pfx_rex & REX_PFX_MASK( u->itab_entry->prefix ) ); - - /* whether this instruction has a default operand size of - * 64bit, also hardcoded into the opcode map. - */ - u->default64 = P_DEF64( u->itab_entry->prefix ); - /* calculate effective operand size */ - if ( REX_W( u->pfx_rex ) ) { - u->opr_mode = 64; - } else if ( u->pfx_opr ) { - u->opr_mode = 16; - } else { - /* unless the default opr size of instruction is 64, - * the effective operand size in the absence of rex.w - * prefix is 32. - */ - u->opr_mode = ( u->default64 ) ? 64 : 32; - } - - /* calculate effective address size */ - u->adr_mode = (u->pfx_adr) ? 32 : 64; - } else if ( u->dis_mode == 32 ) { /* set 32bit-mode flags */ - u->opr_mode = ( u->pfx_opr ) ? 16 : 32; - u->adr_mode = ( u->pfx_adr ) ? 16 : 32; - } else if ( u->dis_mode == 16 ) { /* set 16bit-mode flags */ - u->opr_mode = ( u->pfx_opr ) ? 32 : 16; - u->adr_mode = ( u->pfx_adr ) ? 32 : 16; - } - - /* These flags determine which operand to apply the operand size - * cast to. - */ - u->c1 = ( P_C1( u->itab_entry->prefix ) ) ? 1 : 0; - u->c2 = ( P_C2( u->itab_entry->prefix ) ) ? 1 : 0; - u->c3 = ( P_C3( u->itab_entry->prefix ) ) ? 1 : 0; - - /* set flags for implicit addressing */ - u->implicit_addr = P_IMPADDR( u->itab_entry->prefix ); - - return 0; -} - -static int gen_hex( struct ud *u ) -{ - unsigned int i; - unsigned char *src_ptr = ud_inp_sess( u ); - char* src_hex; - - /* bail out if in error stat. */ - if ( u->error ) return -1; - /* output buffer pointe */ - src_hex = ( char* ) u->insn_hexcode; - /* for each byte used to decode instruction */ - for ( i = 0; i < u->inp_ctr; ++i, ++src_ptr) { - sprintf( src_hex, "%02x", *src_ptr & 0xFF ); - src_hex += 2; - } - return 0; -} - - -static inline int -decode_insn(struct ud *u, uint16_t ptr) -{ - ASSERT((ptr & 0x8000) == 0); - u->itab_entry = &ud_itab[ ptr ]; - u->mnemonic = u->itab_entry->mnemonic; - return (resolve_mode(u) == 0 && - decode_operands(u) == 0 && - resolve_mnemonic(u) == 0) ? 0 : -1; -} - - -/* - * decode_3dnow() - * - * Decoding 3dnow is a little tricky because of its strange opcode - * structure. The final opcode disambiguation depends on the last - * byte that comes after the operands have been decoded. Fortunately, - * all 3dnow instructions have the same set of operand types. So we - * go ahead and decode the instruction by picking an arbitrarily chosen - * valid entry in the table, decode the operands, and read the final - * byte to resolve the menmonic. - */ -static inline int -decode_3dnow(struct ud* u) -{ - uint16_t ptr; - ASSERT(u->le->type == UD_TAB__OPC_3DNOW); - ASSERT(u->le->table[0xc] != 0); - decode_insn(u, u->le->table[0xc]); - ud_inp_next(u); - if (u->error) { - return -1; - } - ptr = u->le->table[ud_inp_curr(u)]; - ASSERT((ptr & 0x8000) == 0); - u->mnemonic = ud_itab[ptr].mnemonic; - return 0; -} - - -static int -decode_ssepfx(struct ud *u) -{ - uint8_t idx = ((u->pfx_insn & 0xf) + 1) / 2; - if (u->le->table[idx] == 0) { - idx = 0; - } - if (idx && u->le->table[idx] != 0) { - /* - * "Consume" the prefix as a part of the opcode, so it is no - * longer exported as an instruction prefix. - */ - switch (u->pfx_insn) { - case 0xf2: - u->pfx_repne = 0; - break; - case 0xf3: - u->pfx_rep = 0; - u->pfx_repe = 0; - break; - case 0x66: - u->pfx_opr = 0; - break; - } - } - return decode_ext(u, u->le->table[idx]); -} - - -/* - * decode_ext() - * - * Decode opcode extensions (if any) - */ -static int -decode_ext(struct ud *u, uint16_t ptr) -{ - uint8_t idx = 0; - if ((ptr & 0x8000) == 0) { - return decode_insn(u, ptr); - } - u->le = &ud_lookup_table_list[(~0x8000 & ptr)]; - if (u->le->type == UD_TAB__OPC_3DNOW) { - return decode_3dnow(u); - } - - switch (u->le->type) { - case UD_TAB__OPC_MOD: - /* !11 = 0, 11 = 1 */ - idx = (MODRM_MOD(modrm(u)) + 1) / 4; - break; - /* disassembly mode/operand size/address size based tables. - * 16 = 0,, 32 = 1, 64 = 2 - */ - case UD_TAB__OPC_MODE: - idx = u->dis_mode / 32; - break; - case UD_TAB__OPC_OSIZE: - idx = eff_opr_mode(u->dis_mode, REX_W(u->pfx_rex), u->pfx_opr) / 32; - break; - case UD_TAB__OPC_ASIZE: - idx = eff_adr_mode(u->dis_mode, u->pfx_adr) / 32; - break; - case UD_TAB__OPC_X87: - idx = modrm(u) - 0xC0; - break; - case UD_TAB__OPC_VENDOR: - if (u->vendor == UD_VENDOR_ANY) { - /* choose a valid entry */ - idx = (u->le->table[idx] != 0) ? 0 : 1; - } else if (u->vendor == UD_VENDOR_AMD) { - idx = 0; - } else { - idx = 1; - } - break; - case UD_TAB__OPC_RM: - idx = MODRM_RM(modrm(u)); - break; - case UD_TAB__OPC_REG: - idx = MODRM_REG(modrm(u)); - break; - case UD_TAB__OPC_SSE: - return decode_ssepfx(u); - default: - ASSERT(!"not reached"); - break; - } - - return decode_ext(u, u->le->table[idx]); -} - - -static inline int -decode_opcode(struct ud *u) -{ - uint16_t ptr; - ASSERT(u->le->type == UD_TAB__OPC_TABLE); - ud_inp_next(u); - if (u->error) { - return -1; - } - ptr = u->le->table[ud_inp_curr(u)]; - if (ptr & 0x8000) { - u->le = &ud_lookup_table_list[ptr & ~0x8000]; - if (u->le->type == UD_TAB__OPC_TABLE) { - return decode_opcode(u); - } - } - return decode_ext(u, ptr); -} - - -/* ============================================================================= - * ud_decode() - Instruction decoder. Returns the number of bytes decoded. - * ============================================================================= - */ -unsigned int -ud_decode(struct ud *u) -{ - ud_inp_start(u); - clear_insn(u); - u->le = &ud_lookup_table_list[0]; - u->error = decode_prefixes(u) == -1 || - decode_opcode(u) == -1 || - u->error; - /* Handle decode error. */ - if (u->error) { - /* clear out the decode data. */ - clear_insn(u); - /* mark the sequence of bytes as invalid. */ - u->itab_entry = & s_ie__invalid; - u->mnemonic = u->itab_entry->mnemonic; - } - - /* maybe this stray segment override byte - * should be spewed out? - */ - if ( !P_SEG( u->itab_entry->prefix ) && - u->operand[0].type != UD_OP_MEM && - u->operand[1].type != UD_OP_MEM ) - u->pfx_seg = 0; - - u->insn_offset = u->pc; /* set offset of instruction */ - u->insn_fill = 0; /* set translation buffer index to 0 */ - u->pc += u->inp_ctr; /* move program counter by bytes decoded */ - gen_hex( u ); /* generate hex code */ - - /* return number of bytes disassembled. */ - return u->inp_ctr; -} - -/* -vim: set ts=2 sw=2 expandtab -*/ - -#endif // USE(UDIS86) diff --git a/masm/disassembler/udis86/udis86_decode.h b/masm/disassembler/udis86/udis86_decode.h deleted file mode 100644 index 940ed5ad6f..0000000000 --- a/masm/disassembler/udis86/udis86_decode.h +++ /dev/null @@ -1,258 +0,0 @@ -/* udis86 - libudis86/decode.h - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef UD_DECODE_H -#define UD_DECODE_H - -#include "udis86_types.h" -#include "udis86_itab.h" - -#define MAX_INSN_LENGTH 15 - -/* register classes */ -#define T_NONE 0 -#define T_GPR 1 -#define T_MMX 2 -#define T_CRG 3 -#define T_DBG 4 -#define T_SEG 5 -#define T_XMM 6 - -/* itab prefix bits */ -#define P_none ( 0 ) -#define P_cast ( 1 << 0 ) -#define P_CAST(n) ( ( n >> 0 ) & 1 ) -#define P_c1 ( 1 << 0 ) -#define P_C1(n) ( ( n >> 0 ) & 1 ) -#define P_rexb ( 1 << 1 ) -#define P_REXB(n) ( ( n >> 1 ) & 1 ) -#define P_depM ( 1 << 2 ) -#define P_DEPM(n) ( ( n >> 2 ) & 1 ) -#define P_c3 ( 1 << 3 ) -#define P_C3(n) ( ( n >> 3 ) & 1 ) -#define P_inv64 ( 1 << 4 ) -#define P_INV64(n) ( ( n >> 4 ) & 1 ) -#define P_rexw ( 1 << 5 ) -#define P_REXW(n) ( ( n >> 5 ) & 1 ) -#define P_c2 ( 1 << 6 ) -#define P_C2(n) ( ( n >> 6 ) & 1 ) -#define P_def64 ( 1 << 7 ) -#define P_DEF64(n) ( ( n >> 7 ) & 1 ) -#define P_rexr ( 1 << 8 ) -#define P_REXR(n) ( ( n >> 8 ) & 1 ) -#define P_oso ( 1 << 9 ) -#define P_OSO(n) ( ( n >> 9 ) & 1 ) -#define P_aso ( 1 << 10 ) -#define P_ASO(n) ( ( n >> 10 ) & 1 ) -#define P_rexx ( 1 << 11 ) -#define P_REXX(n) ( ( n >> 11 ) & 1 ) -#define P_ImpAddr ( 1 << 12 ) -#define P_IMPADDR(n) ( ( n >> 12 ) & 1 ) -#define P_seg ( 1 << 13 ) -#define P_SEG(n) ( ( n >> 13 ) & 1 ) -#define P_sext ( 1 << 14 ) -#define P_SEXT(n) ( ( n >> 14 ) & 1 ) - -/* rex prefix bits */ -#define REX_W(r) ( ( 0xF & ( r ) ) >> 3 ) -#define REX_R(r) ( ( 0x7 & ( r ) ) >> 2 ) -#define REX_X(r) ( ( 0x3 & ( r ) ) >> 1 ) -#define REX_B(r) ( ( 0x1 & ( r ) ) >> 0 ) -#define REX_PFX_MASK(n) ( ( P_REXW(n) << 3 ) | \ - ( P_REXR(n) << 2 ) | \ - ( P_REXX(n) << 1 ) | \ - ( P_REXB(n) << 0 ) ) - -/* scable-index-base bits */ -#define SIB_S(b) ( ( b ) >> 6 ) -#define SIB_I(b) ( ( ( b ) >> 3 ) & 7 ) -#define SIB_B(b) ( ( b ) & 7 ) - -/* modrm bits */ -#define MODRM_REG(b) ( ( ( b ) >> 3 ) & 7 ) -#define MODRM_NNN(b) ( ( ( b ) >> 3 ) & 7 ) -#define MODRM_MOD(b) ( ( ( b ) >> 6 ) & 3 ) -#define MODRM_RM(b) ( ( b ) & 7 ) - -/* operand type constants -- order is important! */ - -enum ud_operand_code { - OP_NONE, - - OP_A, OP_E, OP_M, OP_G, - OP_I, - - OP_AL, OP_CL, OP_DL, OP_BL, - OP_AH, OP_CH, OP_DH, OP_BH, - - OP_ALr8b, OP_CLr9b, OP_DLr10b, OP_BLr11b, - OP_AHr12b, OP_CHr13b, OP_DHr14b, OP_BHr15b, - - OP_AX, OP_CX, OP_DX, OP_BX, - OP_SI, OP_DI, OP_SP, OP_BP, - - OP_rAX, OP_rCX, OP_rDX, OP_rBX, - OP_rSP, OP_rBP, OP_rSI, OP_rDI, - - OP_rAXr8, OP_rCXr9, OP_rDXr10, OP_rBXr11, - OP_rSPr12, OP_rBPr13, OP_rSIr14, OP_rDIr15, - - OP_eAX, OP_eCX, OP_eDX, OP_eBX, - OP_eSP, OP_eBP, OP_eSI, OP_eDI, - - OP_ES, OP_CS, OP_SS, OP_DS, - OP_FS, OP_GS, - - OP_ST0, OP_ST1, OP_ST2, OP_ST3, - OP_ST4, OP_ST5, OP_ST6, OP_ST7, - - OP_J, OP_S, OP_O, - OP_I1, OP_I3, - - OP_V, OP_W, OP_Q, OP_P, - - OP_R, OP_C, OP_D, OP_VR, OP_PR, - - OP_MR -} UD_ATTR_PACKED; - - -/* operand size constants */ - -enum ud_operand_size { - SZ_NA = 0, - SZ_Z = 1, - SZ_V = 2, - SZ_P = 3, - SZ_WP = 4, - SZ_DP = 5, - SZ_MDQ = 6, - SZ_RDQ = 7, - - /* the following values are used as is, - * and thus hard-coded. changing them - * will break internals - */ - SZ_B = 8, - SZ_W = 16, - SZ_D = 32, - SZ_Q = 64, - SZ_T = 80, - SZ_O = 128, - - SZ_WV = 17, - SZ_BV = 18, - SZ_DY = 19 - -} UD_ATTR_PACKED; - - -/* A single operand of an entry in the instruction table. - * (internal use only) - */ -struct ud_itab_entry_operand -{ - enum ud_operand_code type; - enum ud_operand_size size; -}; - - -/* A single entry in an instruction table. - *(internal use only) - */ -struct ud_itab_entry -{ - enum ud_mnemonic_code mnemonic; - struct ud_itab_entry_operand operand1; - struct ud_itab_entry_operand operand2; - struct ud_itab_entry_operand operand3; - uint32_t prefix; -}; - -struct ud_lookup_table_list_entry { - const uint16_t *table; - enum ud_table_type type; - const char *meta; -}; - - -static inline unsigned int sse_pfx_idx( const unsigned int pfx ) -{ - /* 00 = 0 - * f2 = 1 - * f3 = 2 - * 66 = 3 - */ - return ( ( pfx & 0xf ) + 1 ) / 2; -} - -static inline unsigned int mode_idx( const unsigned int mode ) -{ - /* 16 = 0 - * 32 = 1 - * 64 = 2 - */ - return ( mode / 32 ); -} - -static inline unsigned int modrm_mod_idx( const unsigned int mod ) -{ - /* !11 = 0 - * 11 = 1 - */ - return ( mod + 1 ) / 4; -} - -static inline unsigned int vendor_idx( const unsigned int vendor ) -{ - switch ( vendor ) { - case UD_VENDOR_AMD: return 0; - case UD_VENDOR_INTEL: return 1; - case UD_VENDOR_ANY: return 2; - default: return 2; - } -} - -static inline unsigned int is_group_ptr( uint16_t ptr ) -{ - return ( 0x8000 & ptr ); -} - -static inline unsigned int group_idx( uint16_t ptr ) -{ - return ( ~0x8000 & ptr ); -} - - -extern struct ud_itab_entry ud_itab[]; -extern struct ud_lookup_table_list_entry ud_lookup_table_list[]; - -#endif /* UD_DECODE_H */ - -/* vim:cindent - * vim:expandtab - * vim:ts=4 - * vim:sw=4 - */ diff --git a/masm/disassembler/udis86/udis86_extern.h b/masm/disassembler/udis86/udis86_extern.h deleted file mode 100644 index 8e87721e8c..0000000000 --- a/masm/disassembler/udis86/udis86_extern.h +++ /dev/null @@ -1,88 +0,0 @@ -/* udis86 - libudis86/extern.h - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef UD_EXTERN_H -#define UD_EXTERN_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "udis86_types.h" - -/* ============================= PUBLIC API ================================= */ - -extern void ud_init(struct ud*); - -extern void ud_set_mode(struct ud*, uint8_t); - -extern void ud_set_pc(struct ud*, uint64_t); - -extern void ud_set_input_hook(struct ud*, int (*)(struct ud*)); - -extern void ud_set_input_buffer(struct ud*, uint8_t*, size_t); - -#ifndef __UD_STANDALONE__ -extern void ud_set_input_file(struct ud*, FILE*); -#endif /* __UD_STANDALONE__ */ - -extern void ud_set_vendor(struct ud*, unsigned); - -extern void ud_set_syntax(struct ud*, void (*)(struct ud*)); - -extern void ud_input_skip(struct ud*, size_t); - -extern int ud_input_end(struct ud*); - -extern unsigned int ud_decode(struct ud*); - -extern unsigned int ud_disassemble(struct ud*); - -extern void ud_translate_intel(struct ud*); - -extern void ud_translate_att(struct ud*); - -extern char* ud_insn_asm(struct ud* u); - -extern uint8_t* ud_insn_ptr(struct ud* u); - -extern uint64_t ud_insn_off(struct ud*); - -extern char* ud_insn_hex(struct ud*); - -extern unsigned int ud_insn_len(struct ud* u); - -extern const char* ud_lookup_mnemonic(enum ud_mnemonic_code c); - -extern void ud_set_user_opaque_data(struct ud*, void*); - -extern void *ud_get_user_opaque_data(struct ud*); - -/* ========================================================================== */ - -#ifdef __cplusplus -} -#endif -#endif diff --git a/masm/disassembler/udis86/udis86_input.c b/masm/disassembler/udis86/udis86_input.c deleted file mode 100644 index 76c6cccf36..0000000000 --- a/masm/disassembler/udis86/udis86_input.c +++ /dev/null @@ -1,263 +0,0 @@ -/* udis86 - libudis86/input.c - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "config.h" -#include - -#if USE(UDIS86) - -#include "udis86_extern.h" -#include "udis86_types.h" -#include "udis86_input.h" - -/* ----------------------------------------------------------------------------- - * inp_buff_hook() - Hook for buffered inputs. - * ----------------------------------------------------------------------------- - */ -static int -inp_buff_hook(struct ud* u) -{ - if (u->inp_buff < u->inp_buff_end) - return *u->inp_buff++; - else return -1; -} - -#ifndef __UD_STANDALONE__ -/* ----------------------------------------------------------------------------- - * inp_file_hook() - Hook for FILE inputs. - * ----------------------------------------------------------------------------- - */ -static int -inp_file_hook(struct ud* u) -{ - return fgetc(u->inp_file); -} -#endif /* __UD_STANDALONE__*/ - -/* ============================================================================= - * ud_inp_set_hook() - Sets input hook. - * ============================================================================= - */ -extern void -ud_set_input_hook(register struct ud* u, int (*hook)(struct ud*)) -{ - u->inp_hook = hook; - ud_inp_init(u); -} - -extern void -ud_set_user_opaque_data( struct ud * u, void * opaque ) -{ - u->user_opaque_data = opaque; -} - -extern void * -ud_get_user_opaque_data( struct ud * u ) -{ - return u->user_opaque_data; -} - -/* ============================================================================= - * ud_inp_set_buffer() - Set buffer as input. - * ============================================================================= - */ -extern void -ud_set_input_buffer(register struct ud* u, uint8_t* buf, size_t len) -{ - u->inp_hook = inp_buff_hook; - u->inp_buff = buf; - u->inp_buff_end = buf + len; - ud_inp_init(u); -} - -#ifndef __UD_STANDALONE__ -/* ============================================================================= - * ud_input_set_file() - Set buffer as input. - * ============================================================================= - */ -extern void -ud_set_input_file(register struct ud* u, FILE* f) -{ - u->inp_hook = inp_file_hook; - u->inp_file = f; - ud_inp_init(u); -} -#endif /* __UD_STANDALONE__ */ - -/* ============================================================================= - * ud_input_skip() - Skip n input bytes. - * ============================================================================= - */ -extern void -ud_input_skip(struct ud* u, size_t n) -{ - while (n--) { - u->inp_hook(u); - } -} - -/* ============================================================================= - * ud_input_end() - Test for end of input. - * ============================================================================= - */ -extern int -ud_input_end(struct ud* u) -{ - return (u->inp_curr == u->inp_fill) && u->inp_end; -} - -/* ----------------------------------------------------------------------------- - * ud_inp_next() - Loads and returns the next byte from input. - * - * inp_curr and inp_fill are pointers to the cache. The program is written based - * on the property that they are 8-bits in size, and will eventually wrap around - * forming a circular buffer. So, the size of the cache is 256 in size, kind of - * unnecessary yet optimized. - * - * A buffer inp_sess stores the bytes disassembled for a single session. - * ----------------------------------------------------------------------------- - */ -extern uint8_t ud_inp_next(struct ud* u) -{ - int c = -1; - /* if current pointer is not upto the fill point in the - * input cache. - */ - if ( u->inp_curr != u->inp_fill ) { - c = u->inp_cache[ ++u->inp_curr ]; - /* if !end-of-input, call the input hook and get a byte */ - } else if ( u->inp_end || ( c = u->inp_hook( u ) ) == -1 ) { - /* end-of-input, mark it as an error, since the decoder, - * expected a byte more. - */ - u->error = 1; - /* flag end of input */ - u->inp_end = 1; - return 0; - } else { - /* increment pointers, we have a new byte. */ - u->inp_curr = ++u->inp_fill; - /* add the byte to the cache */ - u->inp_cache[ u->inp_fill ] = c; - } - /* record bytes input per decode-session. */ - u->inp_sess[ u->inp_ctr++ ] = c; - /* return byte */ - return ( uint8_t ) c; -} - -/* ----------------------------------------------------------------------------- - * ud_inp_back() - Move back a single byte in the stream. - * ----------------------------------------------------------------------------- - */ -extern void -ud_inp_back(struct ud* u) -{ - if ( u->inp_ctr > 0 ) { - --u->inp_curr; - --u->inp_ctr; - } -} - -/* ----------------------------------------------------------------------------- - * ud_inp_peek() - Peek into the next byte in source. - * ----------------------------------------------------------------------------- - */ -extern uint8_t -ud_inp_peek(struct ud* u) -{ - uint8_t r = ud_inp_next(u); - if ( !u->error ) ud_inp_back(u); /* Don't backup if there was an error */ - return r; -} - -/* ----------------------------------------------------------------------------- - * ud_inp_move() - Move ahead n input bytes. - * ----------------------------------------------------------------------------- - */ -extern void -ud_inp_move(struct ud* u, size_t n) -{ - while (n--) - ud_inp_next(u); -} - -/*------------------------------------------------------------------------------ - * ud_inp_uintN() - return uintN from source. - *------------------------------------------------------------------------------ - */ -extern uint8_t -ud_inp_uint8(struct ud* u) -{ - return ud_inp_next(u); -} - -extern uint16_t -ud_inp_uint16(struct ud* u) -{ - uint16_t r, ret; - - ret = ud_inp_next(u); - r = ud_inp_next(u); - return ret | (r << 8); -} - -extern uint32_t -ud_inp_uint32(struct ud* u) -{ - uint32_t r, ret; - - ret = ud_inp_next(u); - r = ud_inp_next(u); - ret = ret | (r << 8); - r = ud_inp_next(u); - ret = ret | (r << 16); - r = ud_inp_next(u); - return ret | (r << 24); -} - -extern uint64_t -ud_inp_uint64(struct ud* u) -{ - uint64_t r, ret; - - ret = ud_inp_next(u); - r = ud_inp_next(u); - ret = ret | (r << 8); - r = ud_inp_next(u); - ret = ret | (r << 16); - r = ud_inp_next(u); - ret = ret | (r << 24); - r = ud_inp_next(u); - ret = ret | (r << 32); - r = ud_inp_next(u); - ret = ret | (r << 40); - r = ud_inp_next(u); - ret = ret | (r << 48); - r = ud_inp_next(u); - return ret | (r << 56); -} - -#endif // USE(UDIS86) diff --git a/masm/disassembler/udis86/udis86_input.h b/masm/disassembler/udis86/udis86_input.h deleted file mode 100644 index 96865a88b5..0000000000 --- a/masm/disassembler/udis86/udis86_input.h +++ /dev/null @@ -1,67 +0,0 @@ -/* udis86 - libudis86/input.h - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef UD_INPUT_H -#define UD_INPUT_H - -#include "udis86_types.h" - -uint8_t ud_inp_next(struct ud*); -uint8_t ud_inp_peek(struct ud*); -uint8_t ud_inp_uint8(struct ud*); -uint16_t ud_inp_uint16(struct ud*); -uint32_t ud_inp_uint32(struct ud*); -uint64_t ud_inp_uint64(struct ud*); -void ud_inp_move(struct ud*, size_t); -void ud_inp_back(struct ud*); - -/* ud_inp_init() - Initializes the input system. */ -#define ud_inp_init(u) \ -do { \ - u->inp_curr = 0; \ - u->inp_fill = 0; \ - u->inp_ctr = 0; \ - u->inp_end = 0; \ -} while (0) - -/* ud_inp_start() - Should be called before each de-code operation. */ -#define ud_inp_start(u) u->inp_ctr = 0 - -/* ud_inp_back() - Resets the current pointer to its position before the current - * instruction disassembly was started. - */ -#define ud_inp_reset(u) \ -do { \ - u->inp_curr -= u->inp_ctr; \ - u->inp_ctr = 0; \ -} while (0) - -/* ud_inp_sess() - Returns the pointer to current session. */ -#define ud_inp_sess(u) (u->inp_sess) - -/* inp_cur() - Returns the current input byte. */ -#define ud_inp_curr(u) ((u)->inp_cache[(u)->inp_curr]) - -#endif diff --git a/masm/disassembler/udis86/udis86_itab_holder.c b/masm/disassembler/udis86/udis86_itab_holder.c deleted file mode 100644 index d5d8726d6a..0000000000 --- a/masm/disassembler/udis86/udis86_itab_holder.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include - -#if USE(UDIS86) - -#include "udis86_itab.c" - -#endif - diff --git a/masm/disassembler/udis86/udis86_syn-att.c b/masm/disassembler/udis86/udis86_syn-att.c deleted file mode 100644 index 155a34ca29..0000000000 --- a/masm/disassembler/udis86/udis86_syn-att.c +++ /dev/null @@ -1,253 +0,0 @@ -/* udis86 - libudis86/syn-att.c - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "config.h" -#include - -#if USE(UDIS86) - -#include "udis86_types.h" -#include "udis86_extern.h" -#include "udis86_decode.h" -#include "udis86_itab.h" -#include "udis86_syn.h" - -/* ----------------------------------------------------------------------------- - * opr_cast() - Prints an operand cast. - * ----------------------------------------------------------------------------- - */ -static void -opr_cast(struct ud* u, struct ud_operand* op) -{ - switch(op->size) { - case 16 : case 32 : - mkasm(u, "*"); break; - default: break; - } -} - -/* ----------------------------------------------------------------------------- - * gen_operand() - Generates assembly output for each operand. - * ----------------------------------------------------------------------------- - */ -static void -gen_operand(struct ud* u, struct ud_operand* op) -{ - switch(op->type) { - case UD_OP_REG: - mkasm(u, "%%%s", ud_reg_tab[op->base - UD_R_AL]); - break; - - case UD_OP_MEM: - if (u->br_far) opr_cast(u, op); - if (u->pfx_seg) - mkasm(u, "%%%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); - if (op->offset == 8) { - if (op->lval.sbyte < 0) - mkasm(u, "-0x%x", (-op->lval.sbyte) & 0xff); - else mkasm(u, "0x%x", op->lval.sbyte); - } - else if (op->offset == 16) - mkasm(u, "0x%x", op->lval.uword); - else if (op->offset == 32) - mkasm(u, "0x%lx", (unsigned long)op->lval.udword); - else if (op->offset == 64) - mkasm(u, "0x" FMT64 "x", op->lval.uqword); - - if (op->base) - mkasm(u, "(%%%s", ud_reg_tab[op->base - UD_R_AL]); - if (op->index) { - if (op->base) - mkasm(u, ","); - else mkasm(u, "("); - mkasm(u, "%%%s", ud_reg_tab[op->index - UD_R_AL]); - } - if (op->scale) - mkasm(u, ",%d", op->scale); - if (op->base || op->index) - mkasm(u, ")"); - break; - - case UD_OP_IMM: { - int64_t imm = 0; - uint64_t sext_mask = 0xffffffffffffffffull; - unsigned sext_size = op->size; - - switch (op->size) { - case 8: imm = op->lval.sbyte; break; - case 16: imm = op->lval.sword; break; - case 32: imm = op->lval.sdword; break; - case 64: imm = op->lval.sqword; break; - } - if ( P_SEXT( u->itab_entry->prefix ) ) { - sext_size = u->operand[ 0 ].size; - if ( u->mnemonic == UD_Ipush ) - /* push sign-extends to operand size */ - sext_size = u->opr_mode; - } - if ( sext_size < 64 ) - sext_mask = ( 1ull << sext_size ) - 1; - mkasm( u, "$0x" FMT64 "x", imm & sext_mask ); - - break; - } - - case UD_OP_JIMM: - switch (op->size) { - case 8: - mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sbyte); - break; - case 16: - mkasm(u, "0x" FMT64 "x", (u->pc + op->lval.sword) & 0xffff ); - break; - case 32: - if (u->dis_mode == 32) - mkasm(u, "0x" FMT64 "x", (u->pc + op->lval.sdword) & 0xffffffff); - else - mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sdword); - break; - default:break; - } - break; - - case UD_OP_PTR: - switch (op->size) { - case 32: - mkasm(u, "$0x%x, $0x%x", op->lval.ptr.seg, - op->lval.ptr.off & 0xFFFF); - break; - case 48: - mkasm(u, "$0x%x, $0x%lx", op->lval.ptr.seg, - (unsigned long)op->lval.ptr.off); - break; - } - break; - - default: return; - } -} - -/* ============================================================================= - * translates to AT&T syntax - * ============================================================================= - */ -extern void -ud_translate_att(struct ud *u) -{ - int size = 0; - - /* check if P_OSO prefix is used */ - if (! P_OSO(u->itab_entry->prefix) && u->pfx_opr) { - switch (u->dis_mode) { - case 16: - mkasm(u, "o32 "); - break; - case 32: - case 64: - mkasm(u, "o16 "); - break; - } - } - - /* check if P_ASO prefix was used */ - if (! P_ASO(u->itab_entry->prefix) && u->pfx_adr) { - switch (u->dis_mode) { - case 16: - mkasm(u, "a32 "); - break; - case 32: - mkasm(u, "a16 "); - break; - case 64: - mkasm(u, "a32 "); - break; - } - } - - if (u->pfx_lock) - mkasm(u, "lock "); - if (u->pfx_rep) - mkasm(u, "rep "); - if (u->pfx_repne) - mkasm(u, "repne "); - - /* special instructions */ - switch (u->mnemonic) { - case UD_Iretf: - mkasm(u, "lret "); - break; - case UD_Idb: - mkasm(u, ".byte 0x%x", u->operand[0].lval.ubyte); - return; - case UD_Ijmp: - case UD_Icall: - if (u->br_far) mkasm(u, "l"); - mkasm(u, "%s", ud_lookup_mnemonic(u->mnemonic)); - break; - case UD_Ibound: - case UD_Ienter: - if (u->operand[0].type != UD_NONE) - gen_operand(u, &u->operand[0]); - if (u->operand[1].type != UD_NONE) { - mkasm(u, ","); - gen_operand(u, &u->operand[1]); - } - return; - default: - mkasm(u, "%s", ud_lookup_mnemonic(u->mnemonic)); - } - - if (u->c1) - size = u->operand[0].size; - else if (u->c2) - size = u->operand[1].size; - else if (u->c3) - size = u->operand[2].size; - - if (size == 8) - mkasm(u, "b"); - else if (size == 16) - mkasm(u, "w"); - else if (size == 64) - mkasm(u, "q"); - - mkasm(u, " "); - - if (u->operand[2].type != UD_NONE) { - gen_operand(u, &u->operand[2]); - mkasm(u, ", "); - } - - if (u->operand[1].type != UD_NONE) { - gen_operand(u, &u->operand[1]); - mkasm(u, ", "); - } - - if (u->operand[0].type != UD_NONE) - gen_operand(u, &u->operand[0]); -} - -#endif // USE(UDIS86) - diff --git a/masm/disassembler/udis86/udis86_syn-intel.c b/masm/disassembler/udis86/udis86_syn-intel.c deleted file mode 100644 index d250bd449c..0000000000 --- a/masm/disassembler/udis86/udis86_syn-intel.c +++ /dev/null @@ -1,279 +0,0 @@ -/* udis86 - libudis86/syn-intel.c - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "config.h" -#include - -#if USE(UDIS86) - -#include "udis86_types.h" -#include "udis86_extern.h" -#include "udis86_decode.h" -#include "udis86_itab.h" -#include "udis86_syn.h" - -/* ----------------------------------------------------------------------------- - * opr_cast() - Prints an operand cast. - * ----------------------------------------------------------------------------- - */ -static void -opr_cast(struct ud* u, struct ud_operand* op) -{ - switch(op->size) { - case 8: mkasm(u, "byte " ); break; - case 16: mkasm(u, "word " ); break; - case 32: mkasm(u, "dword "); break; - case 64: mkasm(u, "qword "); break; - case 80: mkasm(u, "tword "); break; - default: break; - } - if (u->br_far) - mkasm(u, "far "); -} - -/* ----------------------------------------------------------------------------- - * gen_operand() - Generates assembly output for each operand. - * ----------------------------------------------------------------------------- - */ -static void gen_operand(struct ud* u, struct ud_operand* op, int syn_cast) -{ - switch(op->type) { - case UD_OP_REG: - mkasm(u, "%s", ud_reg_tab[op->base - UD_R_AL]); - break; - - case UD_OP_MEM: { - - int op_f = 0; - - if (syn_cast) - opr_cast(u, op); - - mkasm(u, "["); - - if (u->pfx_seg) - mkasm(u, "%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); - - if (op->base) { - mkasm(u, "%s", ud_reg_tab[op->base - UD_R_AL]); - op_f = 1; - } - - if (op->index) { - if (op_f) - mkasm(u, "+"); - mkasm(u, "%s", ud_reg_tab[op->index - UD_R_AL]); - op_f = 1; - } - - if (op->scale) - mkasm(u, "*%d", op->scale); - - if (op->offset == 8) { - if (op->lval.sbyte < 0) - mkasm(u, "-0x%x", -op->lval.sbyte); - else mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.sbyte); - } - else if (op->offset == 16) - mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.uword); - else if (op->offset == 32) { - if (u->adr_mode == 64) { - if (op->lval.sdword < 0) - mkasm(u, "-0x%x", -op->lval.sdword); - else mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.sdword); - } - else mkasm(u, "%s0x%lx", (op_f) ? "+" : "", (unsigned long)op->lval.udword); - } - else if (op->offset == 64) - mkasm(u, "%s0x" FMT64 "x", (op_f) ? "+" : "", op->lval.uqword); - - mkasm(u, "]"); - break; - } - - case UD_OP_IMM: { - int64_t imm = 0; - uint64_t sext_mask = 0xffffffffffffffffull; - unsigned sext_size = op->size; - - if (syn_cast) - opr_cast(u, op); - switch (op->size) { - case 8: imm = op->lval.sbyte; break; - case 16: imm = op->lval.sword; break; - case 32: imm = op->lval.sdword; break; - case 64: imm = op->lval.sqword; break; - } - if ( P_SEXT( u->itab_entry->prefix ) ) { - sext_size = u->operand[ 0 ].size; - if ( u->mnemonic == UD_Ipush ) - /* push sign-extends to operand size */ - sext_size = u->opr_mode; - } - if ( sext_size < 64 ) - sext_mask = ( 1ull << sext_size ) - 1; - mkasm( u, "0x" FMT64 "x", imm & sext_mask ); - - break; - } - - - case UD_OP_JIMM: - if (syn_cast) opr_cast(u, op); - switch (op->size) { - case 8: - mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sbyte); - break; - case 16: - mkasm(u, "0x" FMT64 "x", ( u->pc + op->lval.sword ) & 0xffff ); - break; - case 32: - mkasm(u, "0x" FMT64 "x", ( u->pc + op->lval.sdword ) & 0xfffffffful ); - break; - default:break; - } - break; - - case UD_OP_PTR: - switch (op->size) { - case 32: - mkasm(u, "word 0x%x:0x%x", op->lval.ptr.seg, - op->lval.ptr.off & 0xFFFF); - break; - case 48: - mkasm(u, "dword 0x%x:0x%lx", op->lval.ptr.seg, - (unsigned long)op->lval.ptr.off); - break; - } - break; - - case UD_OP_CONST: - if (syn_cast) opr_cast(u, op); - mkasm(u, "%d", op->lval.udword); - break; - - default: return; - } -} - -/* ============================================================================= - * translates to intel syntax - * ============================================================================= - */ -extern void ud_translate_intel(struct ud* u) -{ - /* -- prefixes -- */ - - /* check if P_OSO prefix is used */ - if (! P_OSO(u->itab_entry->prefix) && u->pfx_opr) { - switch (u->dis_mode) { - case 16: - mkasm(u, "o32 "); - break; - case 32: - case 64: - mkasm(u, "o16 "); - break; - } - } - - /* check if P_ASO prefix was used */ - if (! P_ASO(u->itab_entry->prefix) && u->pfx_adr) { - switch (u->dis_mode) { - case 16: - mkasm(u, "a32 "); - break; - case 32: - mkasm(u, "a16 "); - break; - case 64: - mkasm(u, "a32 "); - break; - } - } - - if ( u->pfx_seg && - u->operand[0].type != UD_OP_MEM && - u->operand[1].type != UD_OP_MEM ) { - mkasm(u, "%s ", ud_reg_tab[u->pfx_seg - UD_R_AL]); - } - if (u->pfx_lock) - mkasm(u, "lock "); - if (u->pfx_rep) - mkasm(u, "rep "); - if (u->pfx_repne) - mkasm(u, "repne "); - - /* print the instruction mnemonic */ - mkasm(u, "%s ", ud_lookup_mnemonic(u->mnemonic)); - - /* operand 1 */ - if (u->operand[0].type != UD_NONE) { - int cast = 0; - if ( u->operand[0].type == UD_OP_IMM && - u->operand[1].type == UD_NONE ) - cast = u->c1; - if ( u->operand[0].type == UD_OP_MEM ) { - cast = u->c1; - if ( u->operand[1].type == UD_OP_IMM || - u->operand[1].type == UD_OP_CONST ) - cast = 1; - if ( u->operand[1].type == UD_NONE ) - cast = 1; - if ( ( u->operand[0].size != u->operand[1].size ) && u->operand[1].size ) - cast = 1; - } else if ( u->operand[ 0 ].type == UD_OP_JIMM ) { - if ( u->operand[ 0 ].size > 8 ) cast = 1; - } - gen_operand(u, &u->operand[0], cast); - } - /* operand 2 */ - if (u->operand[1].type != UD_NONE) { - int cast = 0; - mkasm(u, ", "); - if ( u->operand[1].type == UD_OP_MEM ) { - cast = u->c1; - - if ( u->operand[0].type != UD_OP_REG ) - cast = 1; - if ( u->operand[0].size != u->operand[1].size && u->operand[1].size ) - cast = 1; - if ( u->operand[0].type == UD_OP_REG && - u->operand[0].base >= UD_R_ES && - u->operand[0].base <= UD_R_GS ) - cast = 0; - } - gen_operand(u, &u->operand[1], cast ); - } - - /* operand 3 */ - if (u->operand[2].type != UD_NONE) { - mkasm(u, ", "); - gen_operand(u, &u->operand[2], u->c3); - } -} - -#endif // USE(UDIS86) - diff --git a/masm/disassembler/udis86/udis86_syn.c b/masm/disassembler/udis86/udis86_syn.c deleted file mode 100644 index 80391b4a08..0000000000 --- a/masm/disassembler/udis86/udis86_syn.c +++ /dev/null @@ -1,87 +0,0 @@ -/* udis86 - libudis86/syn.c - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include - -#if USE(UDIS86) - -/* ----------------------------------------------------------------------------- - * Intel Register Table - Order Matters (types.h)! - * ----------------------------------------------------------------------------- - */ -const char* ud_reg_tab[] = -{ - "al", "cl", "dl", "bl", - "ah", "ch", "dh", "bh", - "spl", "bpl", "sil", "dil", - "r8b", "r9b", "r10b", "r11b", - "r12b", "r13b", "r14b", "r15b", - - "ax", "cx", "dx", "bx", - "sp", "bp", "si", "di", - "r8w", "r9w", "r10w", "r11w", - "r12w", "r13W" , "r14w", "r15w", - - "eax", "ecx", "edx", "ebx", - "esp", "ebp", "esi", "edi", - "r8d", "r9d", "r10d", "r11d", - "r12d", "r13d", "r14d", "r15d", - - "rax", "rcx", "rdx", "rbx", - "rsp", "rbp", "rsi", "rdi", - "r8", "r9", "r10", "r11", - "r12", "r13", "r14", "r15", - - "es", "cs", "ss", "ds", - "fs", "gs", - - "cr0", "cr1", "cr2", "cr3", - "cr4", "cr5", "cr6", "cr7", - "cr8", "cr9", "cr10", "cr11", - "cr12", "cr13", "cr14", "cr15", - - "dr0", "dr1", "dr2", "dr3", - "dr4", "dr5", "dr6", "dr7", - "dr8", "dr9", "dr10", "dr11", - "dr12", "dr13", "dr14", "dr15", - - "mm0", "mm1", "mm2", "mm3", - "mm4", "mm5", "mm6", "mm7", - - "st0", "st1", "st2", "st3", - "st4", "st5", "st6", "st7", - - "xmm0", "xmm1", "xmm2", "xmm3", - "xmm4", "xmm5", "xmm6", "xmm7", - "xmm8", "xmm9", "xmm10", "xmm11", - "xmm12", "xmm13", "xmm14", "xmm15", - - "rip" -}; - -#endif // USE(UDIS86) - diff --git a/masm/disassembler/udis86/udis86_syn.h b/masm/disassembler/udis86/udis86_syn.h deleted file mode 100644 index e8636163ef..0000000000 --- a/masm/disassembler/udis86/udis86_syn.h +++ /dev/null @@ -1,47 +0,0 @@ -/* udis86 - libudis86/syn.h - * - * Copyright (c) 2002-2009 - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef UD_SYN_H -#define UD_SYN_H - -#include "udis86_types.h" -#include - -#ifndef __UD_STANDALONE__ -# include -#endif /* __UD_STANDALONE__ */ - -extern const char* ud_reg_tab[]; - -static void mkasm(struct ud* u, const char* fmt, ...) WTF_ATTRIBUTE_PRINTF(2, 3); -static void mkasm(struct ud* u, const char* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - u->insn_fill += vsnprintf((char*) u->insn_buffer + u->insn_fill, UD_STRING_BUFFER_SIZE - u->insn_fill, fmt, ap); - va_end(ap); -} - -#endif diff --git a/masm/disassembler/udis86/udis86_types.h b/masm/disassembler/udis86/udis86_types.h deleted file mode 100644 index 320d1ca491..0000000000 --- a/masm/disassembler/udis86/udis86_types.h +++ /dev/null @@ -1,238 +0,0 @@ -/* udis86 - libudis86/types.h - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef UD_TYPES_H -#define UD_TYPES_H - -#ifndef __UD_STANDALONE__ -# include -#endif /* __UD_STANDALONE__ */ - -/* gcc specific extensions */ -#ifdef __GNUC__ -# define UD_ATTR_PACKED __attribute__((packed)) -#else -# define UD_ATTR_PACKED -#endif /* UD_ATTR_PACKED */ - -#ifdef _MSC_VER -# define FMT64 "%I64" - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; -#else -# define FMT64 "%ll" -# ifndef __UD_STANDALONE__ -# include -# endif /* __UD_STANDALONE__ */ -#endif - -/* ----------------------------------------------------------------------------- - * All possible "types" of objects in udis86. Order is Important! - * ----------------------------------------------------------------------------- - */ -enum ud_type -{ - UD_NONE, - - /* 8 bit GPRs */ - UD_R_AL, UD_R_CL, UD_R_DL, UD_R_BL, - UD_R_AH, UD_R_CH, UD_R_DH, UD_R_BH, - UD_R_SPL, UD_R_BPL, UD_R_SIL, UD_R_DIL, - UD_R_R8B, UD_R_R9B, UD_R_R10B, UD_R_R11B, - UD_R_R12B, UD_R_R13B, UD_R_R14B, UD_R_R15B, - - /* 16 bit GPRs */ - UD_R_AX, UD_R_CX, UD_R_DX, UD_R_BX, - UD_R_SP, UD_R_BP, UD_R_SI, UD_R_DI, - UD_R_R8W, UD_R_R9W, UD_R_R10W, UD_R_R11W, - UD_R_R12W, UD_R_R13W, UD_R_R14W, UD_R_R15W, - - /* 32 bit GPRs */ - UD_R_EAX, UD_R_ECX, UD_R_EDX, UD_R_EBX, - UD_R_ESP, UD_R_EBP, UD_R_ESI, UD_R_EDI, - UD_R_R8D, UD_R_R9D, UD_R_R10D, UD_R_R11D, - UD_R_R12D, UD_R_R13D, UD_R_R14D, UD_R_R15D, - - /* 64 bit GPRs */ - UD_R_RAX, UD_R_RCX, UD_R_RDX, UD_R_RBX, - UD_R_RSP, UD_R_RBP, UD_R_RSI, UD_R_RDI, - UD_R_R8, UD_R_R9, UD_R_R10, UD_R_R11, - UD_R_R12, UD_R_R13, UD_R_R14, UD_R_R15, - - /* segment registers */ - UD_R_ES, UD_R_CS, UD_R_SS, UD_R_DS, - UD_R_FS, UD_R_GS, - - /* control registers*/ - UD_R_CR0, UD_R_CR1, UD_R_CR2, UD_R_CR3, - UD_R_CR4, UD_R_CR5, UD_R_CR6, UD_R_CR7, - UD_R_CR8, UD_R_CR9, UD_R_CR10, UD_R_CR11, - UD_R_CR12, UD_R_CR13, UD_R_CR14, UD_R_CR15, - - /* debug registers */ - UD_R_DR0, UD_R_DR1, UD_R_DR2, UD_R_DR3, - UD_R_DR4, UD_R_DR5, UD_R_DR6, UD_R_DR7, - UD_R_DR8, UD_R_DR9, UD_R_DR10, UD_R_DR11, - UD_R_DR12, UD_R_DR13, UD_R_DR14, UD_R_DR15, - - /* mmx registers */ - UD_R_MM0, UD_R_MM1, UD_R_MM2, UD_R_MM3, - UD_R_MM4, UD_R_MM5, UD_R_MM6, UD_R_MM7, - - /* x87 registers */ - UD_R_ST0, UD_R_ST1, UD_R_ST2, UD_R_ST3, - UD_R_ST4, UD_R_ST5, UD_R_ST6, UD_R_ST7, - - /* extended multimedia registers */ - UD_R_XMM0, UD_R_XMM1, UD_R_XMM2, UD_R_XMM3, - UD_R_XMM4, UD_R_XMM5, UD_R_XMM6, UD_R_XMM7, - UD_R_XMM8, UD_R_XMM9, UD_R_XMM10, UD_R_XMM11, - UD_R_XMM12, UD_R_XMM13, UD_R_XMM14, UD_R_XMM15, - - UD_R_RIP, - - /* Operand Types */ - UD_OP_REG, UD_OP_MEM, UD_OP_PTR, UD_OP_IMM, - UD_OP_JIMM, UD_OP_CONST -}; - -#include "udis86_itab.h" - -/* ----------------------------------------------------------------------------- - * struct ud_operand - Disassembled instruction Operand. - * ----------------------------------------------------------------------------- - */ -struct ud_operand -{ - enum ud_type type; - uint8_t size; - union { - int8_t sbyte; - uint8_t ubyte; - int16_t sword; - uint16_t uword; - int32_t sdword; - uint32_t udword; - int64_t sqword; - uint64_t uqword; - - struct { - uint16_t seg; - uint32_t off; - } ptr; - } lval; - - enum ud_type base; - enum ud_type index; - uint8_t offset; - uint8_t scale; -}; - -#define UD_STRING_BUFFER_SIZE 64 - -/* ----------------------------------------------------------------------------- - * struct ud - The udis86 object. - * ----------------------------------------------------------------------------- - */ -struct ud -{ - int (*inp_hook) (struct ud*); - uint8_t inp_curr; - uint8_t inp_fill; -#ifndef __UD_STANDALONE__ - FILE* inp_file; -#endif - uint8_t inp_ctr; - uint8_t* inp_buff; - uint8_t* inp_buff_end; - uint8_t inp_end; - void (*translator)(struct ud*); - uint64_t insn_offset; - char insn_hexcode[32]; - char insn_buffer[UD_STRING_BUFFER_SIZE]; - unsigned int insn_fill; - uint8_t dis_mode; - uint64_t pc; - uint8_t vendor; - struct map_entry* mapen; - enum ud_mnemonic_code mnemonic; - struct ud_operand operand[3]; - uint8_t error; - uint8_t pfx_rex; - uint8_t pfx_seg; - uint8_t pfx_opr; - uint8_t pfx_adr; - uint8_t pfx_lock; - uint8_t pfx_rep; - uint8_t pfx_repe; - uint8_t pfx_repne; - uint8_t pfx_insn; - uint8_t default64; - uint8_t opr_mode; - uint8_t adr_mode; - uint8_t br_far; - uint8_t br_near; - uint8_t implicit_addr; - uint8_t c1; - uint8_t c2; - uint8_t c3; - uint8_t inp_cache[256]; - uint8_t inp_sess[64]; - uint8_t have_modrm; - uint8_t modrm; - void * user_opaque_data; - struct ud_itab_entry * itab_entry; - struct ud_lookup_table_list_entry *le; -}; - -/* ----------------------------------------------------------------------------- - * Type-definitions - * ----------------------------------------------------------------------------- - */ -typedef enum ud_type ud_type_t; -typedef enum ud_mnemonic_code ud_mnemonic_code_t; - -typedef struct ud ud_t; -typedef struct ud_operand ud_operand_t; - -#define UD_SYN_INTEL ud_translate_intel -#define UD_SYN_ATT ud_translate_att -#define UD_EOI -1 -#define UD_INP_CACHE_SZ 32 -#define UD_VENDOR_AMD 0 -#define UD_VENDOR_INTEL 1 -#define UD_VENDOR_ANY 2 - -#define bail_out(ud,error_code) longjmp( (ud)->bailout, error_code ) -#define try_decode(ud) if ( setjmp( (ud)->bailout ) == 0 ) -#define catch_error() else - -#endif diff --git a/masm/jit/JITCompilationEffort.h b/masm/jit/JITCompilationEffort.h deleted file mode 100644 index 5eb6801789..0000000000 --- a/masm/jit/JITCompilationEffort.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef JITCompilationEffort_h -#define JITCompilationEffort_h - -namespace JSC { - -enum JITCompilationEffort { - JITCompilationCanFail, - JITCompilationMustSucceed -}; - -} // namespace JSC - -#endif // JITCompilationEffort_h - diff --git a/masm/masm.pri b/masm/masm.pri deleted file mode 100644 index a6d11f633c..0000000000 --- a/masm/masm.pri +++ /dev/null @@ -1,107 +0,0 @@ - -HEADERS += $$PWD/assembler/*.h -SOURCES += $$PWD/assembler/ARMAssembler.cpp -SOURCES += $$PWD/assembler/ARMv7Assembler.cpp -SOURCES += $$PWD/assembler/MacroAssemblerARM.cpp -SOURCES += $$PWD/assembler/MacroAssemblerSH4.cpp -SOURCES += $$PWD/assembler/LinkBuffer.cpp - -SOURCES += $$PWD/wtf/PrintStream.cpp -HEADERS += $$PWD/wtf/PrintStream.h - -SOURCES += $$PWD/wtf/FilePrintStream.cpp -HEADERS += $$PWD/wtf/FilePrintStream.h - -HEADERS += $$PWD/wtf/RawPointer.h - -win32: SOURCES += $$PWD/wtf/OSAllocatorWin.cpp -else: SOURCES += $$PWD/wtf/OSAllocatorPosix.cpp -HEADERS += $$PWD/wtf/OSAllocator.h - -SOURCES += $$PWD/wtf/PageAllocationAligned.cpp -HEADERS += $$PWD/wtf/PageAllocationAligned.h -HEADERS += $$PWD/wtf/PageAllocation.h - -SOURCES += $$PWD/wtf/PageBlock.cpp -HEADERS += $$PWD/wtf/PageBlock.h - -HEADERS += $$PWD/wtf/PageReservation.h - -SOURCES += $$PWD/stubs/WTFStubs.cpp -HEADERS += $$PWD/stubs/WTFStubs.h - -DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" - -DEFINES += ENABLE_LLINT=0 -DEFINES += ENABLE_DFG_JIT=0 -DEFINES += ENABLE_JIT=1 -DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 -DEFINES += ENABLE_ASSEMBLER=1 - -DEFINES += BUILDING_QT__ - -INCLUDEPATH += $$PWD/jit -INCLUDEPATH += $$PWD/assembler -INCLUDEPATH += $$PWD/runtime -INCLUDEPATH += $$PWD/wtf -INCLUDEPATH += $$PWD/stubs -INCLUDEPATH += $$PWD/stubs/wtf -INCLUDEPATH += $$PWD - -DEFINES += WTF_USE_UDIS86=1 -INCLUDEPATH += $$PWD/disassembler -INCLUDEPATH += $$PWD/disassembler/udis86 -INCLUDEPATH += $$_OUT_PWD -SOURCES += $$PWD/disassembler/Disassembler.cpp -SOURCES += $$PWD/disassembler/UDis86Disassembler.cpp -SOURCES += $$PWD/disassembler/udis86/udis86.c -SOURCES += $$PWD/disassembler/udis86/udis86_decode.c -SOURCES += $$PWD/disassembler/udis86/udis86_input.c -SOURCES += $$PWD/disassembler/udis86/udis86_itab_holder.c -SOURCES += $$PWD/disassembler/udis86/udis86_syn-att.c -SOURCES += $$PWD/disassembler/udis86/udis86_syn.c -SOURCES += $$PWD/disassembler/udis86/udis86_syn-intel.c - -DEFINES += ENABLE_YARR_JIT=0 -SOURCES += \ - $$PWD/yarr/YarrCanonicalizeUCS2.cpp \ - $$PWD/yarr/YarrInterpreter.cpp \ - $$PWD/yarr/YarrPattern.cpp \ - $$PWD/yarr/YarrSyntaxChecker.cpp - -HEADERS += $$PWD/yarr/*.h - -retgen.output = RegExpJitTables.h -retgen.script = $$PWD/create_regex_tables -retgen.input = retgen.script -retgen.CONFIG += no_link -retgen.commands = python $$retgen.script > ${QMAKE_FILE_OUT} -QMAKE_EXTRA_COMPILERS += retgen - -ITAB = $$PWD/disassembler/udis86/optable.xml -udis86.output = udis86_itab.h -udis86.input = ITAB -udis86.CONFIG += no_link -udis86.commands = python $$PWD/disassembler/udis86/itab.py ${QMAKE_FILE_IN} -QMAKE_EXTRA_COMPILERS += udis86 - -udis86_tab_cfile.target = $$OUT_PWD/udis86_itab.c -udis86_tab_cfile.depends = udis86_itab.h -QMAKE_EXTRA_TARGETS += udis86_tab_cfile - -# Taken from WebKit/Tools/qmake/mkspecs/features/unix/default_post.prf -linux-g++* { - greaterThan(QT_GCC_MAJOR_VERSION, 3):greaterThan(QT_GCC_MINOR_VERSION, 5) { - !contains(QMAKE_CXXFLAGS, -std=(c|gnu)\\+\\+(0x|11)) { - # We need to deactivate those warnings because some names conflicts with upcoming c++0x types (e.g.nullptr). - QMAKE_CXXFLAGS_WARN_ON += -Wno-c++0x-compat - QMAKE_CXXFLAGS += -Wno-c++0x-compat - } - } -} - -# Don't warn about OVERRIDE and FINAL, since they are feature-checked anyways -*clang:!contains(QMAKE_CXXFLAGS, -std=c++11) { - QMAKE_CXXFLAGS += -Wno-c++11-extensions - QMAKE_OBJECTIVE_CFLAGS += -Wno-c++11-extensions -} diff --git a/masm/runtime/MatchResult.h b/masm/runtime/MatchResult.h deleted file mode 100644 index d87c8516b0..0000000000 --- a/masm/runtime/MatchResult.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MatchResult_h -#define MatchResult_h - -typedef uint64_t EncodedMatchResult; - -struct MatchResult { - ALWAYS_INLINE MatchResult(size_t start, size_t end) - : start(start) - , end(end) - { - } - - explicit ALWAYS_INLINE MatchResult(EncodedMatchResult encoded) - { - union u { - uint64_t encoded; - struct s { - size_t start; - size_t end; - } split; - } value; - value.encoded = encoded; - start = value.split.start; - end = value.split.end; - } - - ALWAYS_INLINE static MatchResult failed() - { - return MatchResult(WTF::notFound, 0); - } - - ALWAYS_INLINE operator bool() - { - return start != WTF::notFound; - } - - ALWAYS_INLINE bool empty() - { - return start == end; - } - - size_t start; - size_t end; -}; - -#endif diff --git a/masm/stubs/ExecutableAllocator.h b/masm/stubs/ExecutableAllocator.h deleted file mode 100644 index 7b3004aa90..0000000000 --- a/masm/stubs/ExecutableAllocator.h +++ /dev/null @@ -1,105 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef MASM_EXECUTABLEALLOCATOR_H -#define MASM_EXECUTABLEALLOCATOR_H - -#include -#include - -#include -#include - -namespace JSC { - -class JSGlobalData; - -struct ExecutableMemoryHandle : public RefCounted { - ExecutableMemoryHandle(int size) - : m_size(size) - { - static size_t pageSize = sysconf(_SC_PAGESIZE); - m_size = (m_size + pageSize - 1) & ~(pageSize - 1); -#if OS(DARWIN) -# define MAP_ANONYMOUS MAP_ANON -#endif - m_data = mmap(0, m_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - } - ~ExecutableMemoryHandle() - { - munmap(m_data, m_size); - } - - inline void shrink(size_t) { - // ### TODO. - } - - inline bool isManaged() const { return true; } - - void* start() { return m_data; } - int sizeInBytes() { return m_size; } - - void* m_data; - int m_size; -}; - -struct ExecutableAllocator { - PassRefPtr allocate(JSGlobalData&, int size, void*, int) - { - return adoptRef(new ExecutableMemoryHandle(size)); - } - - static void makeWritable(void*, int) - { - } - - static void makeExecutable(void* addr, int size) - { - static size_t pageSize = sysconf(_SC_PAGESIZE); - size_t iaddr = reinterpret_cast(addr); - size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); - int mode = PROT_READ | PROT_WRITE | PROT_EXEC; - mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode); - } -}; - -} - -#endif // MASM_EXECUTABLEALLOCATOR_H diff --git a/masm/stubs/JSGlobalData.h b/masm/stubs/JSGlobalData.h deleted file mode 100644 index 112bedddd7..0000000000 --- a/masm/stubs/JSGlobalData.h +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef MASM_JSGLOBALDATA_H -#define MASM_JSGLOBALDATA_H - -#include "ExecutableAllocator.h" -#include "WeakRandom.h" - -namespace JSC { - -class JSGlobalData { -public: - ExecutableAllocator executableAllocator; -}; - -} - -#endif // MASM_JSGLOBALDATA_H diff --git a/masm/stubs/LLIntData.h b/masm/stubs/LLIntData.h deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/masm/stubs/Options.h b/masm/stubs/Options.h deleted file mode 100644 index b95e4354e2..0000000000 --- a/masm/stubs/Options.h +++ /dev/null @@ -1,53 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef OPTIONS_H -#define OPTIONS_H - -namespace JSC { - -struct Options { - static bool showDisassembly() { return true; } - static bool showDFGDisassembly() { return true; } -}; - -} - -#endif // MASM_STUBS/OPTIONS_H diff --git a/masm/stubs/WTFStubs.cpp b/masm/stubs/WTFStubs.cpp deleted file mode 100644 index 530804fe3e..0000000000 --- a/masm/stubs/WTFStubs.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include -#include -#include -#include -#include -#include - -namespace WTF { - -void* fastMalloc(size_t size) -{ - return malloc(size); -} - -void* fastRealloc(void* ptr, size_t size) -{ - return realloc(ptr, size); -} - -void fastFree(void* ptr) -{ - free(ptr); -} - -uint32_t cryptographicallyRandomNumber() -{ - return 0; -} - -static FilePrintStream* s_dataFile; - -void setDataFile(FILE* f) -{ - delete s_dataFile; - s_dataFile = new FilePrintStream(f, FilePrintStream::Borrow); -} - -FilePrintStream& dataFile() -{ - if (!s_dataFile) - s_dataFile = new FilePrintStream(stderr, FilePrintStream::Borrow); - return *s_dataFile; -} - -void dataLogFV(const char* format, va_list args) -{ - char buffer[1024]; - vsnprintf(buffer, sizeof(buffer), format, args); - qDebug("%s", buffer); -} - -void dataLogF(const char* format, ...) -{ - char buffer[1024]; - va_list args; - va_start(args, format); - vsnprintf(buffer, sizeof(buffer), format, args); - va_end(args); - qDebug("%s", buffer); -} - -void dataLogFString(const char* str) -{ - qDebug("%s", str); -} - -} - -extern "C" { - -void WTFReportAssertionFailure(const char* /*file*/, int /*line*/, const char* /*function*/, const char* /*assertion*/) -{ -} - -void WTFReportBacktrace() -{ -} - -void WTFInvokeCrashHook() -{ -} - -} - - -#if ENABLE(ASSEMBLER) && CPU(X86) && !OS(MAC_OS_X) -#include - -JSC::MacroAssemblerX86Common::SSE2CheckState JSC::MacroAssemblerX86Common::s_sse2CheckState = JSC::MacroAssemblerX86Common::NotCheckedSSE2; -#endif - diff --git a/masm/stubs/WTFStubs.h b/masm/stubs/WTFStubs.h deleted file mode 100644 index ec77d25da7..0000000000 --- a/masm/stubs/WTFStubs.h +++ /dev/null @@ -1,50 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef WTFSTUBS_H -#define WTFSTUBS_H - -namespace WTF { - -void setDataFile(FILE* f); - -} - -#endif // WTFSTUBS_H diff --git a/masm/stubs/wtf/FastAllocBase.h b/masm/stubs/wtf/FastAllocBase.h deleted file mode 100644 index a062a885af..0000000000 --- a/masm/stubs/wtf/FastAllocBase.h +++ /dev/null @@ -1,48 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef FASTALLOCBASE_H -#define FASTALLOCBASE_H - -/* Dummy empty header file, only needed for #include source compatibility */ - -#define WTF_MAKE_FAST_ALLOCATED - -#endif // FASTALLOCBASE_H diff --git a/masm/stubs/wtf/FastMalloc.h b/masm/stubs/wtf/FastMalloc.h deleted file mode 100644 index 1248c79dec..0000000000 --- a/masm/stubs/wtf/FastMalloc.h +++ /dev/null @@ -1,46 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef FASTMALLOC_H -#define FASTMALLOC_H - -/* Dummy empty header file, only needed for #include source compatibility */ - -#endif // FASTMALLOC_H diff --git a/masm/stubs/wtf/Noncopyable.h b/masm/stubs/wtf/Noncopyable.h deleted file mode 100644 index d3d1eed6d1..0000000000 --- a/masm/stubs/wtf/Noncopyable.h +++ /dev/null @@ -1,48 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef NONCOPYABLE_H -#define NONCOPYABLE_H - -#include - -#define WTF_MAKE_NONCOPYABLE(x) Q_DISABLE_COPY(x) - -#endif // NONCOPYABLE_H diff --git a/masm/stubs/wtf/OwnPtr.h b/masm/stubs/wtf/OwnPtr.h deleted file mode 100644 index 31d2f1efa3..0000000000 --- a/masm/stubs/wtf/OwnPtr.h +++ /dev/null @@ -1,46 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef OWNPTR_H -#define OWNPTR_H - -#include "PassOwnPtr.h" - -#endif // OWNPTR_H diff --git a/masm/stubs/wtf/PassOwnPtr.h b/masm/stubs/wtf/PassOwnPtr.h deleted file mode 100644 index f9b84e7b57..0000000000 --- a/masm/stubs/wtf/PassOwnPtr.h +++ /dev/null @@ -1,116 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef PASSOWNPTR_H -#define PASSOWNPTR_H - -#include - -template class PassOwnPtr; -template PassOwnPtr adoptPtr(PtrType*); - -template -struct OwnPtr : public QScopedPointer -{ - OwnPtr() {} - OwnPtr(const PassOwnPtr &ptr) - : QScopedPointer(ptr.leakRef()) - {} - - OwnPtr& operator=(const OwnPtr& other) - { - this->reset(const_cast &>(other).take()); - return *this; - } - - T* get() const { return this->data(); } - - PassOwnPtr release() - { - return adoptPtr(this->take()); - } -}; - -template -class PassOwnPtr { -public: - PassOwnPtr() {} - - PassOwnPtr(T* ptr) - : m_ptr(ptr) - { - } - - PassOwnPtr(const PassOwnPtr& other) - : m_ptr(other.leakRef()) - { - } - - PassOwnPtr(const OwnPtr& other) - : m_ptr(other.take()) - { - } - - ~PassOwnPtr() - { - } - - T* operator->() const { return m_ptr.data(); } - - T* leakRef() const { return m_ptr.take(); } - -private: - template friend PassOwnPtr adoptPtr(PtrType*); - - PassOwnPtr& operator=(const PassOwnPtr&) - {} - mutable QScopedPointer m_ptr; -}; - -template -PassOwnPtr adoptPtr(T* ptr) -{ - PassOwnPtr result; - result.m_ptr.reset(ptr); - return result; -} - - -#endif // PASSOWNPTR_H diff --git a/masm/stubs/wtf/PassRefPtr.h b/masm/stubs/wtf/PassRefPtr.h deleted file mode 100644 index d97be1c330..0000000000 --- a/masm/stubs/wtf/PassRefPtr.h +++ /dev/null @@ -1,101 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef PASSREFPTR_H -#define PASSREFPTR_H - -template class RefPtr; - -template -class PassRefPtr { -public: - PassRefPtr() : m_ptr(0) {} - - PassRefPtr(T* ptr) - : m_ptr(ptr) - { - if (m_ptr) - m_ptr->ref(); - } - - PassRefPtr(const PassRefPtr& other) - : m_ptr(other.leakRef()) - { - } - - PassRefPtr(const RefPtr& other) - : m_ptr(other.get()) - { - if (m_ptr) - m_ptr->ref(); - } - - ~PassRefPtr() - { - if (m_ptr) - m_ptr->deref(); - } - - T* operator->() const { return m_ptr; } - - T* leakRef() const - { - T* result = m_ptr; - m_ptr = 0; - return result; - } - -private: - PassRefPtr& operator=(const PassRefPtr&) - {} - - template friend PassRefPtr adoptRef(PtrType*); - mutable T* m_ptr; -}; - -template -PassRefPtr adoptRef(T* ptr) -{ - PassRefPtr result; - result.m_ptr = ptr; - return result; -} - -#endif // PASSREFPTR_H diff --git a/masm/stubs/wtf/RefCounted.h b/masm/stubs/wtf/RefCounted.h deleted file mode 100644 index 4fc9ad9074..0000000000 --- a/masm/stubs/wtf/RefCounted.h +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef REFCOUNTED_H -#define REFCOUNTED_H - -#include "PassRefPtr.h" - -template -class RefCounted { -public: - RefCounted() : m_refCount(1) {} - ~RefCounted() - { - deref(); - } - - void ref() - { - ++m_refCount; - } - - void deref() - { - if (!--m_refCount) - delete static_cast(this); - } - -protected: - int m_refCount; -}; - -#endif // REFCOUNTED_H diff --git a/masm/stubs/wtf/RefPtr.h b/masm/stubs/wtf/RefPtr.h deleted file mode 100644 index 929b493b4b..0000000000 --- a/masm/stubs/wtf/RefPtr.h +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef REFPTR_H -#define REFPTR_H - -#include "PassRefPtr.h" - -template -class RefPtr { -public: - RefPtr() : m_ptr(0) {} - RefPtr(const RefPtr &other) - : m_ptr(other.m_ptr) - { - if (m_ptr) - m_ptr->ref(); - } - - RefPtr& operator=(const RefPtr& other) - { - if (other.m_ptr) - other.m_ptr->ref(); - if (m_ptr) - m_ptr->deref(); - m_ptr = other.m_ptr; - return *this; - } - - RefPtr(const PassRefPtr& other) - : m_ptr(other.leakRef()) - { - } - - ~RefPtr() - { - if (m_ptr) - m_ptr->deref(); - } - - T* operator->() const { return m_ptr; } - T* get() const { return m_ptr; } - bool operator!() const { return !m_ptr; } - - PassRefPtr release() - { - T* ptr = m_ptr; - m_ptr = 0; - return adoptRef(ptr); - } - -private: - T* m_ptr; -}; - -#endif // REFPTR_H diff --git a/masm/stubs/wtf/TypeTraits.h b/masm/stubs/wtf/TypeTraits.h deleted file mode 100644 index 9b626a7a53..0000000000 --- a/masm/stubs/wtf/TypeTraits.h +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef TYPETRAITS_H -#define TYPETRAITS_H - -namespace WTF { - -template -struct IsSameType { - static const bool value = false; -}; - -template -struct IsSameType { - static const bool value = true; -}; - -} - -#endif // TYPETRAITS_H diff --git a/masm/stubs/wtf/UnusedParam.h b/masm/stubs/wtf/UnusedParam.h deleted file mode 100644 index a676bdf303..0000000000 --- a/masm/stubs/wtf/UnusedParam.h +++ /dev/null @@ -1,48 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef UNUSEDPARAM_H -#define UNUSEDPARAM_H - -#include - -#define UNUSED_PARAM(x) Q_UNUSED(x) - -#endif // UNUSEDPARAM_H diff --git a/masm/stubs/wtf/Vector.h b/masm/stubs/wtf/Vector.h deleted file mode 100644 index 1feea851e1..0000000000 --- a/masm/stubs/wtf/Vector.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef VECTOR_H -#define VECTOR_H - -#include -#include -#include -#include - -namespace WTF { - -template -class Vector : public std::vector { -public: - Vector() {} - Vector(int initialSize) : std::vector(initialSize) {} - - inline void append(const T& value) - { this->push_back(value); } - - inline void append(const Vector& vector) - { - this->insert(this->end(), vector.begin(), vector.end()); - } - - using std::vector::insert; - - inline void insert(size_t position, T value) - { this->insert(this->begin() + position, value); } - - inline void grow(size_t size) - { this->resize(size); } - - inline void shrink(size_t size) - { this->erase(this->begin() + size, this->end()); } - - inline void remove(size_t position) - { this->erase(this->begin() + position); } - - inline bool isEmpty() const { return this->empty(); } - - inline T &last() { return *(this->begin() + this->size() - 1); } -}; - -template -void deleteAllValues(const Vector &vector) -{ - qDeleteAll(vector); -} - -} - -using WTF::Vector; -using WTF::deleteAllValues; - -#endif // VECTOR_H diff --git a/masm/stubs/wtf/text/CString.h b/masm/stubs/wtf/text/CString.h deleted file mode 100644 index c9a65e5c0b..0000000000 --- a/masm/stubs/wtf/text/CString.h +++ /dev/null @@ -1,44 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef CSTRING_H -#define CSTRING_H - -#endif // CSTRING_H diff --git a/masm/stubs/wtf/text/WTFString.h b/masm/stubs/wtf/text/WTFString.h deleted file mode 100644 index d157dc7adc..0000000000 --- a/masm/stubs/wtf/text/WTFString.h +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef WTFSTRING_H -#define WTFSTRING_H - -#include -#include -#include - -namespace WTF { - -class String : public QString -{ -public: - String(const QString& s) : QString(s) {} - bool is8Bit() const { return false; } - const unsigned char *characters8() const { return 0; } - const UChar *characters16() const { return reinterpret_cast(constData()); } - - template - const T* getCharacters() const; - -}; - -template <> -inline const unsigned char* String::getCharacters() const { return characters8(); } -template <> -inline const UChar* String::getCharacters() const { return characters16(); } - -} - -// Don't import WTF::String into the global namespace to avoid conflicts with QQmlJS::VM::String -namespace JSC { - using WTF::String; -} - -#endif // WTFSTRING_H diff --git a/masm/stubs/wtf/unicode/Unicode.h b/masm/stubs/wtf/unicode/Unicode.h deleted file mode 100644 index d61bc64c5a..0000000000 --- a/masm/stubs/wtf/unicode/Unicode.h +++ /dev/null @@ -1,59 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef UNICODE_H -#define UNICODE_H - -#include - -typedef unsigned char LChar; -typedef uint16_t UChar; - -namespace Unicode { - inline UChar toLower(UChar ch) { - return QChar::toLower(ch); - } - - inline UChar toUpper(UChar ch) { - return QChar::toUpper(ch); - } -} - -#endif // UNICODE_H diff --git a/masm/wtf/ASCIICType.h b/masm/wtf/ASCIICType.h deleted file mode 100644 index 18e108e1bf..0000000000 --- a/masm/wtf/ASCIICType.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_ASCIICType_h -#define WTF_ASCIICType_h - -#include - -// The behavior of many of the functions in the header is dependent -// on the current locale. But in the WebKit project, all uses of those functions -// are in code processing something that's not locale-specific. These equivalents -// for some of the functions are named more explicitly, not dependent -// on the C library locale, and we should also optimize them as needed. - -// All functions return false or leave the character unchanged if passed a character -// that is outside the range 0-7F. So they can be used on Unicode strings or -// characters if the intent is to do processing only if the character is ASCII. - -namespace WTF { - -template inline bool isASCII(CharType c) -{ - return !(c & ~0x7F); -} - -template inline bool isASCIIAlpha(CharType c) -{ - return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; -} - -template inline bool isASCIIDigit(CharType c) -{ - return c >= '0' && c <= '9'; -} - -template inline bool isASCIIAlphanumeric(CharType c) -{ - return isASCIIDigit(c) || isASCIIAlpha(c); -} - -template inline bool isASCIIHexDigit(CharType c) -{ - return isASCIIDigit(c) || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); -} - -template inline bool isASCIILower(CharType c) -{ - return c >= 'a' && c <= 'z'; -} - -template inline bool isASCIIOctalDigit(CharType c) -{ - return (c >= '0') & (c <= '7'); -} - -template inline bool isASCIIPrintable(CharType c) -{ - return c >= ' ' && c <= '~'; -} - -/* - Statistics from a run of Apple's page load test for callers of isASCIISpace: - - character count - --------- ----- - non-spaces 689383 - 20 space 294720 - 0A \n 89059 - 09 \t 28320 - 0D \r 0 - 0C \f 0 - 0B \v 0 - */ -template inline bool isASCIISpace(CharType c) -{ - return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); -} - -template inline bool isASCIIUpper(CharType c) -{ - return c >= 'A' && c <= 'Z'; -} - -template inline CharType toASCIILower(CharType c) -{ - return c | ((c >= 'A' && c <= 'Z') << 5); -} - -template inline CharType toASCIILowerUnchecked(CharType character) -{ - // This function can be used for comparing any input character - // to a lowercase English character. The isASCIIAlphaCaselessEqual - // below should be used for regular comparison of ASCII alpha - // characters, but switch statements in CSS tokenizer require - // direct use of this function. - return character | 0x20; -} - -template inline CharType toASCIIUpper(CharType c) -{ - return c & ~((c >= 'a' && c <= 'z') << 5); -} - -template inline int toASCIIHexValue(CharType c) -{ - ASSERT(isASCIIHexDigit(c)); - return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; -} - -template inline int toASCIIHexValue(CharType upperValue, CharType lowerValue) -{ - ASSERT(isASCIIHexDigit(upperValue) && isASCIIHexDigit(lowerValue)); - return ((toASCIIHexValue(upperValue) << 4) & 0xF0) | toASCIIHexValue(lowerValue); -} - -inline char lowerNibbleToASCIIHexDigit(char c) -{ - char nibble = c & 0xF; - return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; -} - -inline char upperNibbleToASCIIHexDigit(char c) -{ - char nibble = (c >> 4) & 0xF; - return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; -} - -template inline bool isASCIIAlphaCaselessEqual(CharType cssCharacter, char character) -{ - // This function compares a (preferrably) constant ASCII - // lowercase letter to any input character. - ASSERT(character >= 'a' && character <= 'z'); - return LIKELY(toASCIILowerUnchecked(cssCharacter) == character); -} - -} - -using WTF::isASCII; -using WTF::isASCIIAlpha; -using WTF::isASCIIAlphanumeric; -using WTF::isASCIIDigit; -using WTF::isASCIIHexDigit; -using WTF::isASCIILower; -using WTF::isASCIIOctalDigit; -using WTF::isASCIIPrintable; -using WTF::isASCIISpace; -using WTF::isASCIIUpper; -using WTF::toASCIIHexValue; -using WTF::toASCIILower; -using WTF::toASCIILowerUnchecked; -using WTF::toASCIIUpper; -using WTF::lowerNibbleToASCIIHexDigit; -using WTF::upperNibbleToASCIIHexDigit; -using WTF::isASCIIAlphaCaselessEqual; - -#endif diff --git a/masm/wtf/Assertions.h b/masm/wtf/Assertions.h deleted file mode 100644 index 7e079ab187..0000000000 --- a/masm/wtf/Assertions.h +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_Assertions_h -#define WTF_Assertions_h - -/* - no namespaces because this file has to be includable from C and Objective-C - - Note, this file uses many GCC extensions, but it should be compatible with - C, Objective C, C++, and Objective C++. - - For non-debug builds, everything is disabled by default. - Defining any of the symbols explicitly prevents this from having any effect. - - MSVC7 note: variadic macro support was added in MSVC8, so for now we disable - those macros in MSVC7. For more info, see the MSDN document on variadic - macros here: - - http://msdn2.microsoft.com/en-us/library/ms177415(VS.80).aspx -*/ - -#include - -#include - -#if !COMPILER(MSVC) -#include -#endif - -#ifdef NDEBUG -/* Disable ASSERT* macros in release mode. */ -#define ASSERTIONS_DISABLED_DEFAULT 1 -#else -#define ASSERTIONS_DISABLED_DEFAULT 0 -#endif - -#if COMPILER(MSVC7_OR_LOWER) -#define HAVE_VARIADIC_MACRO 0 -#else -#define HAVE_VARIADIC_MACRO 1 -#endif - -#ifndef BACKTRACE_DISABLED -#define BACKTRACE_DISABLED ASSERTIONS_DISABLED_DEFAULT -#endif - -#ifndef ASSERT_DISABLED -#define ASSERT_DISABLED ASSERTIONS_DISABLED_DEFAULT -#endif - -#ifndef ASSERT_MSG_DISABLED -#if HAVE(VARIADIC_MACRO) -#define ASSERT_MSG_DISABLED ASSERTIONS_DISABLED_DEFAULT -#else -#define ASSERT_MSG_DISABLED 1 -#endif -#endif - -#ifndef ASSERT_ARG_DISABLED -#define ASSERT_ARG_DISABLED ASSERTIONS_DISABLED_DEFAULT -#endif - -#ifndef FATAL_DISABLED -#if HAVE(VARIADIC_MACRO) -#define FATAL_DISABLED ASSERTIONS_DISABLED_DEFAULT -#else -#define FATAL_DISABLED 1 -#endif -#endif - -#ifndef ERROR_DISABLED -#if HAVE(VARIADIC_MACRO) -#define ERROR_DISABLED ASSERTIONS_DISABLED_DEFAULT -#else -#define ERROR_DISABLED 1 -#endif -#endif - -#ifndef LOG_DISABLED -#if HAVE(VARIADIC_MACRO) -#define LOG_DISABLED ASSERTIONS_DISABLED_DEFAULT -#else -#define LOG_DISABLED 1 -#endif -#endif - -#if COMPILER(GCC) -#define WTF_PRETTY_FUNCTION __PRETTY_FUNCTION__ -#else -#define WTF_PRETTY_FUNCTION __FUNCTION__ -#endif - -/* WTF logging functions can process %@ in the format string to log a NSObject* but the printf format attribute - emits a warning when %@ is used in the format string. Until is resolved we can't include - the attribute when being used from Objective-C code in case it decides to use %@. */ -#if COMPILER(GCC) && !defined(__OBJC__) -#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) __attribute__((__format__(printf, formatStringArgument, extraArguments))) -#else -#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) -#endif - -/* These helper functions are always declared, but not necessarily always defined if the corresponding function is disabled. */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { WTFLogChannelOff, WTFLogChannelOn } WTFLogChannelState; - -typedef struct { - unsigned mask; - const char *defaultName; - WTFLogChannelState state; -} WTFLogChannel; - -WTF_EXPORT_PRIVATE void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion); -WTF_EXPORT_PRIVATE void WTFReportAssertionFailureWithMessage(const char* file, int line, const char* function, const char* assertion, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6); -WTF_EXPORT_PRIVATE void WTFReportArgumentAssertionFailure(const char* file, int line, const char* function, const char* argName, const char* assertion); -WTF_EXPORT_PRIVATE void WTFReportFatalError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5); -WTF_EXPORT_PRIVATE void WTFReportError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5); -WTF_EXPORT_PRIVATE void WTFLog(WTFLogChannel*, const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); -WTF_EXPORT_PRIVATE void WTFLogVerbose(const char* file, int line, const char* function, WTFLogChannel*, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6); -WTF_EXPORT_PRIVATE void WTFLogAlways(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); - -WTF_EXPORT_PRIVATE void WTFGetBacktrace(void** stack, int* size); -WTF_EXPORT_PRIVATE void WTFReportBacktrace(); -WTF_EXPORT_PRIVATE void WTFPrintBacktrace(void** stack, int size); - -typedef void (*WTFCrashHookFunction)(); -WTF_EXPORT_PRIVATE void WTFSetCrashHook(WTFCrashHookFunction); -WTF_EXPORT_PRIVATE void WTFInvokeCrashHook(); -WTF_EXPORT_PRIVATE void WTFInstallReportBacktraceOnCrashHook(); - -#ifdef __cplusplus -} -#endif - -/* CRASH() - Raises a fatal error resulting in program termination and triggering either the debugger or the crash reporter. - - Use CRASH() in response to known, unrecoverable errors like out-of-memory. - Macro is enabled in both debug and release mode. - To test for unknown errors and verify assumptions, use ASSERT instead, to avoid impacting performance in release builds. - - Signals are ignored by the crash reporter on OS X so we must do better. -*/ -#ifndef CRASH -#if COMPILER(CLANG) -#define CRASH() \ - (WTFReportBacktrace(), \ - WTFInvokeCrashHook(), \ - (*(int *)(uintptr_t)0xbbadbeef = 0), \ - __builtin_trap()) -#else -#define CRASH() \ - (WTFReportBacktrace(), \ - WTFInvokeCrashHook(), \ - (*(int *)(uintptr_t)0xbbadbeef = 0), \ - ((void(*)())0)() /* More reliable, but doesn't say BBADBEEF */ \ - ) -#endif -#endif - -#if COMPILER(CLANG) -#define NO_RETURN_DUE_TO_CRASH NO_RETURN -#else -#define NO_RETURN_DUE_TO_CRASH -#endif - - -/* BACKTRACE - - Print a backtrace to the same location as ASSERT messages. -*/ - -#if BACKTRACE_DISABLED - -#define BACKTRACE() ((void)0) - -#else - -#define BACKTRACE() do { \ - WTFReportBacktrace(); \ -} while(false) - -#endif - -/* ASSERT, ASSERT_NOT_REACHED, ASSERT_UNUSED - - These macros are compiled out of release builds. - Expressions inside them are evaluated in debug builds only. -*/ - -#if OS(WINCE) && !PLATFORM(TORCHMOBILE) -/* FIXME: We include this here only to avoid a conflict with the ASSERT macro. */ -#include -#undef min -#undef max -#undef ERROR -#endif - -#if OS(WINDOWS) -/* FIXME: Change to use something other than ASSERT to avoid this conflict with the underlying platform */ -#undef ASSERT -#endif - -#if ASSERT_DISABLED - -#define ASSERT(assertion) ((void)0) -#define ASSERT_AT(assertion, file, line, function) ((void)0) -#define ASSERT_NOT_REACHED() ((void)0) -#define NO_RETURN_DUE_TO_ASSERT - -#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) -template -inline void assertUnused(T& x) { (void)x; } -#define ASSERT_UNUSED(variable, assertion) (assertUnused(variable)) -#else -#define ASSERT_UNUSED(variable, assertion) ((void)variable) -#endif - -#else - -#define ASSERT(assertion) \ - (!(assertion) ? \ - (WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion), \ - CRASH()) : \ - (void)0) - -#define ASSERT_AT(assertion, file, line, function) \ - (!(assertion) ? \ - (WTFReportAssertionFailure(file, line, function, #assertion), \ - CRASH()) : \ - (void)0) - -#define ASSERT_NOT_REACHED() do { \ - WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, 0); \ - CRASH(); \ -} while (0) - -#define ASSERT_UNUSED(variable, assertion) ASSERT(assertion) - -#define NO_RETURN_DUE_TO_ASSERT NO_RETURN_DUE_TO_CRASH - -#endif - -/* ASSERT_WITH_MESSAGE */ - -#if COMPILER(MSVC7_OR_LOWER) -#define ASSERT_WITH_MESSAGE(assertion) ((void)0) -#elif ASSERT_MSG_DISABLED -#define ASSERT_WITH_MESSAGE(assertion, ...) ((void)0) -#else -#define ASSERT_WITH_MESSAGE(assertion, ...) do \ - if (!(assertion)) { \ - WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ - CRASH(); \ - } \ -while (0) -#endif - -/* ASSERT_WITH_MESSAGE_UNUSED */ - -#if COMPILER(MSVC7_OR_LOWER) -#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion) ((void)0) -#elif ASSERT_MSG_DISABLED -#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) -template -inline void assertWithMessageUnused(T& x) { (void)x; } -#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) (assertWithMessageUnused(variable)) -#else -#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) ((void)variable) -#endif -#else -#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) do \ - if (!(assertion)) { \ - WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ - CRASH(); \ - } \ -while (0) -#endif - - -/* ASSERT_ARG */ - -#if ASSERT_ARG_DISABLED - -#define ASSERT_ARG(argName, assertion) ((void)0) - -#else - -#define ASSERT_ARG(argName, assertion) do \ - if (!(assertion)) { \ - WTFReportArgumentAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #argName, #assertion); \ - CRASH(); \ - } \ -while (0) - -#endif - -/* COMPILE_ASSERT */ -#ifndef COMPILE_ASSERT -#if COMPILER_SUPPORTS(C_STATIC_ASSERT) -#define COMPILE_ASSERT(exp, name) _Static_assert((exp), #name) -#else -#define COMPILE_ASSERT(exp, name) typedef int dummy##name [(exp) ? 1 : -1] -#endif -#endif - -/* FATAL */ - -#if COMPILER(MSVC7_OR_LOWER) -#define FATAL() ((void)0) -#elif FATAL_DISABLED -#define FATAL(...) ((void)0) -#else -#define FATAL(...) do { \ - WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__); \ - CRASH(); \ -} while (0) -#endif - -/* LOG_ERROR */ - -#if COMPILER(MSVC7_OR_LOWER) -#define LOG_ERROR() ((void)0) -#elif ERROR_DISABLED -#define LOG_ERROR(...) ((void)0) -#else -#define LOG_ERROR(...) WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__) -#endif - -/* LOG */ - -#if COMPILER(MSVC7_OR_LOWER) -#define LOG() ((void)0) -#elif LOG_DISABLED -#define LOG(channel, ...) ((void)0) -#else -#define LOG(channel, ...) WTFLog(&JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__) -#define JOIN_LOG_CHANNEL_WITH_PREFIX(prefix, channel) JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) -#define JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) prefix ## channel -#endif - -/* LOG_VERBOSE */ - -#if COMPILER(MSVC7_OR_LOWER) -#define LOG_VERBOSE(channel) ((void)0) -#elif LOG_DISABLED -#define LOG_VERBOSE(channel, ...) ((void)0) -#else -#define LOG_VERBOSE(channel, ...) WTFLogVerbose(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, &JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__) -#endif - -/* UNREACHABLE_FOR_PLATFORM */ - -#if COMPILER(CLANG) -// This would be a macro except that its use of #pragma works best around -// a function. Hence it uses macro naming convention. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmissing-noreturn" -static inline void UNREACHABLE_FOR_PLATFORM() -{ - ASSERT_NOT_REACHED(); -} -#pragma clang diagnostic pop -#else -#define UNREACHABLE_FOR_PLATFORM() ASSERT_NOT_REACHED() -#endif - -#endif /* WTF_Assertions_h */ diff --git a/masm/wtf/Atomics.h b/masm/wtf/Atomics.h deleted file mode 100644 index 750e1092ad..0000000000 --- a/masm/wtf/Atomics.h +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (C) 2007, 2008, 2010, 2012 Apple Inc. All rights reserved. - * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * Note: The implementations of InterlockedIncrement and InterlockedDecrement are based - * on atomic_increment and atomic_exchange_and_add from the Boost C++ Library. The license - * is virtually identical to the Apple license above but is included here for completeness. - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, - * must be included in all copies of the Software, in whole or in part, and - * all derivative works of the Software, unless such copies or derivative - * works are solely in the form of machine-executable object code generated by - * a source language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef Atomics_h -#define Atomics_h - -#include -#include -#include - -#if OS(WINDOWS) -#include -#elif OS(DARWIN) -#include -#elif OS(QNX) -#include -#elif OS(ANDROID) -#include -#elif COMPILER(GCC) -#if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2))) && !defined(__LSB_VERSION__) -#include -#else -#include -#endif -#endif - -namespace WTF { - -#if OS(WINDOWS) -#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 - -#if COMPILER(MINGW) || COMPILER(MSVC7_OR_LOWER) || OS(WINCE) -inline int atomicIncrement(int* addend) { return InterlockedIncrement(reinterpret_cast(addend)); } -inline int atomicDecrement(int* addend) { return InterlockedDecrement(reinterpret_cast(addend)); } -#else -inline int atomicIncrement(int volatile* addend) { return InterlockedIncrement(reinterpret_cast(addend)); } -inline int atomicDecrement(int volatile* addend) { return InterlockedDecrement(reinterpret_cast(addend)); } -#endif - -#elif OS(DARWIN) -#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 - -inline int atomicIncrement(int volatile* addend) { return OSAtomicIncrement32Barrier(const_cast(addend)); } -inline int atomicDecrement(int volatile* addend) { return OSAtomicDecrement32Barrier(const_cast(addend)); } - -inline int64_t atomicIncrement(int64_t volatile* addend) { return OSAtomicIncrement64Barrier(const_cast(addend)); } -inline int64_t atomicDecrement(int64_t volatile* addend) { return OSAtomicDecrement64Barrier(const_cast(addend)); } - -#elif OS(QNX) -#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 - -// Note, atomic_{add, sub}_value() return the previous value of addend's content. -inline int atomicIncrement(int volatile* addend) { return static_cast(atomic_add_value(reinterpret_cast(addend), 1)) + 1; } -inline int atomicDecrement(int volatile* addend) { return static_cast(atomic_sub_value(reinterpret_cast(addend), 1)) - 1; } - -#elif OS(ANDROID) -#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 - -// Note, __atomic_{inc, dec}() return the previous value of addend's content. -inline int atomicIncrement(int volatile* addend) { return __atomic_inc(addend) + 1; } -inline int atomicDecrement(int volatile* addend) { return __atomic_dec(addend) - 1; } - -#elif COMPILER(GCC) && !CPU(SPARC64) // sizeof(_Atomic_word) != sizeof(int) on sparc64 gcc -#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 - -inline int atomicIncrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, 1) + 1; } -inline int atomicDecrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, -1) - 1; } - -#endif - -#if OS(WINDOWS) -inline bool weakCompareAndSwap(volatile unsigned* location, unsigned expected, unsigned newValue) -{ -#if OS(WINCE) - return InterlockedCompareExchange(reinterpret_cast(const_cast(location)), static_cast(newValue), static_cast(expected)) == static_cast(expected); -#else - return InterlockedCompareExchange(reinterpret_cast(location), static_cast(newValue), static_cast(expected)) == static_cast(expected); -#endif -} - -inline bool weakCompareAndSwap(void*volatile* location, void* expected, void* newValue) -{ - return InterlockedCompareExchangePointer(location, newValue, expected) == expected; -} -#else // OS(WINDOWS) --> not windows -#if COMPILER(GCC) && !COMPILER(CLANG) // Work around a gcc bug -inline bool weakCompareAndSwap(volatile unsigned* location, unsigned expected, unsigned newValue) -#else -inline bool weakCompareAndSwap(unsigned* location, unsigned expected, unsigned newValue) -#endif -{ -#if ENABLE(COMPARE_AND_SWAP) -#if CPU(X86) || CPU(X86_64) - unsigned char result; - asm volatile( - "lock; cmpxchgl %3, %2\n\t" - "sete %1" - : "+a"(expected), "=q"(result), "+m"(*location) - : "r"(newValue) - : "memory" - ); -#elif CPU(ARM_THUMB2) - unsigned tmp; - unsigned result; - asm volatile( - "movw %1, #1\n\t" - "ldrex %2, %0\n\t" - "cmp %3, %2\n\t" - "bne.n 0f\n\t" - "strex %1, %4, %0\n\t" - "0:" - : "+Q"(*location), "=&r"(result), "=&r"(tmp) - : "r"(expected), "r"(newValue) - : "memory"); - result = !result; -#else -#error "Bad architecture for compare and swap." -#endif - return result; -#else - UNUSED_PARAM(location); - UNUSED_PARAM(expected); - UNUSED_PARAM(newValue); - CRASH(); - return false; -#endif -} - -inline bool weakCompareAndSwap(void*volatile* location, void* expected, void* newValue) -{ -#if ENABLE(COMPARE_AND_SWAP) -#if CPU(X86_64) - bool result; - asm volatile( - "lock; cmpxchgq %3, %2\n\t" - "sete %1" - : "+a"(expected), "=q"(result), "+m"(*location) - : "r"(newValue) - : "memory" - ); - return result; -#else - return weakCompareAndSwap(bitwise_cast(location), bitwise_cast(expected), bitwise_cast(newValue)); -#endif -#else // ENABLE(COMPARE_AND_SWAP) - UNUSED_PARAM(location); - UNUSED_PARAM(expected); - UNUSED_PARAM(newValue); - CRASH(); - return 0; -#endif // ENABLE(COMPARE_AND_SWAP) -} -#endif // OS(WINDOWS) (end of the not-windows case) - -inline bool weakCompareAndSwapUIntPtr(volatile uintptr_t* location, uintptr_t expected, uintptr_t newValue) -{ - return weakCompareAndSwap(reinterpret_cast(location), reinterpret_cast(expected), reinterpret_cast(newValue)); -} - -#if CPU(ARM_THUMB2) - -inline void memoryBarrierAfterLock() -{ - asm volatile("dmb" ::: "memory"); -} - -inline void memoryBarrierBeforeUnlock() -{ - asm volatile("dmb" ::: "memory"); -} - -#else - -inline void memoryBarrierAfterLock() { } -inline void memoryBarrierBeforeUnlock() { } - -#endif - -} // namespace WTF - -#if USE(LOCKFREE_THREADSAFEREFCOUNTED) -using WTF::atomicDecrement; -using WTF::atomicIncrement; -#endif - -#endif // Atomics_h diff --git a/masm/wtf/BumpPointerAllocator.h b/masm/wtf/BumpPointerAllocator.h deleted file mode 100644 index 3b2cfd974a..0000000000 --- a/masm/wtf/BumpPointerAllocator.h +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef BumpPointerAllocator_h -#define BumpPointerAllocator_h - -#include -#include -#include - -namespace WTF { - -#define MINIMUM_BUMP_POOL_SIZE 0x1000 - -class BumpPointerPool { -public: - // ensureCapacity will check whether the current pool has capacity to - // allocate 'size' bytes of memory If it does not, it will attempt to - // allocate a new pool (which will be added to this one in a chain). - // - // If allocation fails (out of memory) this method will return null. - // If the return value is non-null, then callers should update any - // references they have to this current (possibly full) BumpPointerPool - // to instead point to the newly returned BumpPointerPool. - BumpPointerPool* ensureCapacity(size_t size) - { - void* allocationEnd = static_cast(m_current) + size; - ASSERT(allocationEnd > m_current); // check for overflow - if (allocationEnd <= static_cast(this)) - return this; - return ensureCapacityCrossPool(this, size); - } - - // alloc should only be called after calling ensureCapacity; as such - // alloc will never fail. - void* alloc(size_t size) - { - void* current = m_current; - void* allocationEnd = static_cast(current) + size; - ASSERT(allocationEnd > current); // check for overflow - ASSERT(allocationEnd <= static_cast(this)); - m_current = allocationEnd; - return current; - } - - // The dealloc method releases memory allocated using alloc. Memory - // must be released in a LIFO fashion, e.g. if the client calls alloc - // four times, returning pointer A, B, C, D, then the only valid order - // in which these may be deallocaed is D, C, B, A. - // - // The client may optionally skip some deallocations. In the example - // above, it would be valid to only explicitly dealloc C, A (D being - // dealloced along with C, B along with A). - // - // If pointer was not allocated from this pool (or pools) then dealloc - // will CRASH(). Callers should update any references they have to - // this current BumpPointerPool to instead point to the returned - // BumpPointerPool. - BumpPointerPool* dealloc(void* position) - { - if ((position >= m_start) && (position <= static_cast(this))) { - ASSERT(position <= m_current); - m_current = position; - return this; - } - return deallocCrossPool(this, position); - } - -private: - // Placement operator new, returns the last 'size' bytes of allocation for use as this. - void* operator new(size_t size, const PageAllocation& allocation) - { - ASSERT(size < allocation.size()); - return reinterpret_cast(reinterpret_cast(allocation.base()) + allocation.size()) - size; - } - - BumpPointerPool(const PageAllocation& allocation) - : m_current(allocation.base()) - , m_start(allocation.base()) - , m_next(0) - , m_previous(0) - , m_allocation(allocation) - { - } - - static BumpPointerPool* create(size_t minimumCapacity = 0) - { - // Add size of BumpPointerPool object, check for overflow. - minimumCapacity += sizeof(BumpPointerPool); - if (minimumCapacity < sizeof(BumpPointerPool)) - return 0; - - size_t poolSize = std::max(static_cast(MINIMUM_BUMP_POOL_SIZE), WTF::pageSize()); - while (poolSize < minimumCapacity) { - poolSize <<= 1; - // The following if check relies on MINIMUM_BUMP_POOL_SIZE being a power of 2! - ASSERT(!(MINIMUM_BUMP_POOL_SIZE & (MINIMUM_BUMP_POOL_SIZE - 1))); - if (!poolSize) - return 0; - } - - PageAllocation allocation = PageAllocation::allocate(poolSize); - if (!!allocation) - return new (allocation) BumpPointerPool(allocation); - return 0; - } - - void shrink() - { - ASSERT(!m_previous); - m_current = m_start; - while (m_next) { - BumpPointerPool* nextNext = m_next->m_next; - m_next->destroy(); - m_next = nextNext; - } - } - - void destroy() - { - m_allocation.deallocate(); - } - - static BumpPointerPool* ensureCapacityCrossPool(BumpPointerPool* previousPool, size_t size) - { - // The pool passed should not have capacity, so we'll start with the next one. - ASSERT(previousPool); - ASSERT((static_cast(previousPool->m_current) + size) > previousPool->m_current); // check for overflow - ASSERT((static_cast(previousPool->m_current) + size) > static_cast(previousPool)); - BumpPointerPool* pool = previousPool->m_next; - - while (true) { - if (!pool) { - // We've run to the end; allocate a new pool. - pool = BumpPointerPool::create(size); - previousPool->m_next = pool; - pool->m_previous = previousPool; - return pool; - } - - // - void* current = pool->m_current; - void* allocationEnd = static_cast(current) + size; - ASSERT(allocationEnd > current); // check for overflow - if (allocationEnd <= static_cast(pool)) - return pool; - } - } - - static BumpPointerPool* deallocCrossPool(BumpPointerPool* pool, void* position) - { - // Should only be called if position is not in the current pool. - ASSERT((position < pool->m_start) || (position > static_cast(pool))); - - while (true) { - // Unwind the current pool to the start, move back in the chain to the previous pool. - pool->m_current = pool->m_start; - pool = pool->m_previous; - - // position was nowhere in the chain! - if (!pool) - CRASH(); - - if ((position >= pool->m_start) && (position <= static_cast(pool))) { - ASSERT(position <= pool->m_current); - pool->m_current = position; - return pool; - } - } - } - - void* m_current; - void* m_start; - BumpPointerPool* m_next; - BumpPointerPool* m_previous; - PageAllocation m_allocation; - - friend class BumpPointerAllocator; -}; - -// A BumpPointerAllocator manages a set of BumpPointerPool objects, which -// can be used for LIFO (stack like) allocation. -// -// To begin allocating using this class call startAllocator(). The result -// of this method will be null if the initial pool allocation fails, or a -// pointer to a BumpPointerPool object that can be used to perform -// allocations. Whilst running no memory will be released until -// stopAllocator() is called. At this point all allocations made through -// this allocator will be reaped, and underlying memory may be freed. -// -// (In practice we will still hold on to the initial pool to allow allocation -// to be quickly restared, but aditional pools will be freed). -// -// This allocator is non-renetrant, it is encumbant on the clients to ensure -// startAllocator() is not called again until stopAllocator() has been called. -class BumpPointerAllocator { -public: - BumpPointerAllocator() - : m_head(0) - { - } - - ~BumpPointerAllocator() - { - if (m_head) - m_head->destroy(); - } - - BumpPointerPool* startAllocator() - { - if (!m_head) - m_head = BumpPointerPool::create(); - return m_head; - } - - void stopAllocator() - { - if (m_head) - m_head->shrink(); - } - -private: - BumpPointerPool* m_head; -}; - -} - -using WTF::BumpPointerAllocator; - -#endif // BumpPointerAllocator_h diff --git a/masm/wtf/CheckedArithmetic.h b/masm/wtf/CheckedArithmetic.h deleted file mode 100644 index f5d3b75500..0000000000 --- a/masm/wtf/CheckedArithmetic.h +++ /dev/null @@ -1,708 +0,0 @@ -/* - * Copyright (C) 2011 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef CheckedArithmetic_h -#define CheckedArithmetic_h - -#include -#include - -#include -#include - -/* Checked - * - * This class provides a mechanism to perform overflow-safe integer arithmetic - * without having to manually ensure that you have all the required bounds checks - * directly in your code. - * - * There are two modes of operation: - * - The default is Checked, and crashes at the point - * and overflow has occurred. - * - The alternative is Checked, which uses an additional - * byte of storage to track whether an overflow has occurred, subsequent - * unchecked operations will crash if an overflow has occured - * - * It is possible to provide a custom overflow handler, in which case you need - * to support these functions: - * - void overflowed(); - * This function is called when an operation has produced an overflow. - * - bool hasOverflowed(); - * This function must return true if overflowed() has been called on an - * instance and false if it has not. - * - void clearOverflow(); - * Used to reset overflow tracking when a value is being overwritten with - * a new value. - * - * Checked works for all integer types, with the following caveats: - * - Mixing signedness of operands is only supported for types narrower than - * 64bits. - * - It does have a performance impact, so tight loops may want to be careful - * when using it. - * - */ - -namespace WTF { - -class CrashOnOverflow { -protected: - NO_RETURN_DUE_TO_CRASH void overflowed() - { - CRASH(); - } - - void clearOverflow() { } - -public: - bool hasOverflowed() const { return false; } -}; - -class RecordOverflow { -protected: - RecordOverflow() - : m_overflowed(false) - { - } - - void overflowed() - { - m_overflowed = true; - } - - void clearOverflow() - { - m_overflowed = false; - } - -public: - bool hasOverflowed() const { return m_overflowed; } - -private: - unsigned char m_overflowed; -}; - -template class Checked; -template struct RemoveChecked; -template struct RemoveChecked >; - -template ::is_signed, bool sourceSigned = std::numeric_limits::is_signed> struct BoundsChecker; -template struct BoundsChecker { - static bool inBounds(Source value) - { - // Same signedness so implicit type conversion will always increase precision - // to widest type - return value <= std::numeric_limits::max(); - } -}; - -template struct BoundsChecker { - static bool inBounds(Source value) - { - // Same signedness so implicit type conversion will always increase precision - // to widest type - return std::numeric_limits::min() <= value && value <= std::numeric_limits::max(); - } -}; - -template struct BoundsChecker { - static bool inBounds(Source value) - { - // Target is unsigned so any value less than zero is clearly unsafe - if (value < 0) - return false; - // If our (unsigned) Target is the same or greater width we can - // convert value to type Target without losing precision - if (sizeof(Target) >= sizeof(Source)) - return static_cast(value) <= std::numeric_limits::max(); - // The signed Source type has greater precision than the target so - // max(Target) -> Source will widen. - return value <= static_cast(std::numeric_limits::max()); - } -}; - -template struct BoundsChecker { - static bool inBounds(Source value) - { - // Signed target with an unsigned source - if (sizeof(Target) <= sizeof(Source)) - return value <= static_cast(std::numeric_limits::max()); - // Target is Wider than Source so we're guaranteed to fit any value in - // unsigned Source - return true; - } -}; - -template ::value || (sizeof(Target) > sizeof(Source)) > struct BoundsCheckElider; -template struct BoundsCheckElider { - static bool inBounds(Source) { return true; } -}; -template struct BoundsCheckElider : public BoundsChecker { -}; - -template static inline bool isInBounds(Source value) -{ - return BoundsCheckElider::inBounds(value); -} - -template struct RemoveChecked { - typedef T CleanType; - static const CleanType DefaultValue = 0; -}; - -template struct RemoveChecked > { - typedef typename RemoveChecked::CleanType CleanType; - static const CleanType DefaultValue = 0; -}; - -template struct RemoveChecked > { - typedef typename RemoveChecked::CleanType CleanType; - static const CleanType DefaultValue = 0; -}; - -// The ResultBase and SignednessSelector are used to workaround typeof not being -// available in MSVC -template sizeof(V)), bool sameSize = (sizeof(U) == sizeof(V))> struct ResultBase; -template struct ResultBase { - typedef U ResultType; -}; - -template struct ResultBase { - typedef V ResultType; -}; - -template struct ResultBase { - typedef U ResultType; -}; - -template ::is_signed, bool vIsSigned = std::numeric_limits::is_signed> struct SignednessSelector; -template struct SignednessSelector { - typedef U ResultType; -}; - -template struct SignednessSelector { - typedef U ResultType; -}; - -template struct SignednessSelector { - typedef V ResultType; -}; - -template struct SignednessSelector { - typedef U ResultType; -}; - -template struct ResultBase { - typedef typename SignednessSelector::ResultType ResultType; -}; - -template struct Result : ResultBase::CleanType, typename RemoveChecked::CleanType> { -}; - -template ::ResultType, - bool lhsSigned = std::numeric_limits::is_signed, bool rhsSigned = std::numeric_limits::is_signed> struct ArithmeticOperations; - -template struct ArithmeticOperations { - // LHS and RHS are signed types - - // Helper function - static inline bool signsMatch(LHS lhs, RHS rhs) - { - return (lhs ^ rhs) >= 0; - } - - static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - if (signsMatch(lhs, rhs)) { - if (lhs >= 0) { - if ((std::numeric_limits::max() - rhs) < lhs) - return false; - } else { - ResultType temp = lhs - std::numeric_limits::min(); - if (rhs < -temp) - return false; - } - } // if the signs do not match this operation can't overflow - result = lhs + rhs; - return true; - } - - static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - if (!signsMatch(lhs, rhs)) { - if (lhs >= 0) { - if (lhs > std::numeric_limits::max() + rhs) - return false; - } else { - if (rhs > std::numeric_limits::max() + lhs) - return false; - } - } // if the signs match this operation can't overflow - result = lhs - rhs; - return true; - } - - static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - if (signsMatch(lhs, rhs)) { - if (lhs >= 0) { - if (lhs && (std::numeric_limits::max() / lhs) < rhs) - return false; - } else { - if (static_cast(lhs) == std::numeric_limits::min() || static_cast(rhs) == std::numeric_limits::min()) - return false; - if ((std::numeric_limits::max() / -lhs) < -rhs) - return false; - } - } else { - if (lhs < 0) { - if (rhs && lhs < (std::numeric_limits::min() / rhs)) - return false; - } else { - if (lhs && rhs < (std::numeric_limits::min() / lhs)) - return false; - } - } - result = lhs * rhs; - return true; - } - - static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } - -}; - -template struct ArithmeticOperations { - // LHS and RHS are unsigned types so bounds checks are nice and easy - static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - ResultType temp = lhs + rhs; - if (temp < lhs) - return false; - result = temp; - return true; - } - - static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - ResultType temp = lhs - rhs; - if (temp > lhs) - return false; - result = temp; - return true; - } - - static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - ResultType temp = lhs * rhs; - if (temp < lhs) - return false; - result = temp; - return true; - } - - static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } - -}; - -template struct ArithmeticOperations { - static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) - { - int64_t temp = lhs + rhs; - if (temp < std::numeric_limits::min()) - return false; - if (temp > std::numeric_limits::max()) - return false; - result = static_cast(temp); - return true; - } - - static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) - { - int64_t temp = lhs - rhs; - if (temp < std::numeric_limits::min()) - return false; - if (temp > std::numeric_limits::max()) - return false; - result = static_cast(temp); - return true; - } - - static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) - { - int64_t temp = lhs * rhs; - if (temp < std::numeric_limits::min()) - return false; - if (temp > std::numeric_limits::max()) - return false; - result = static_cast(temp); - return true; - } - - static inline bool equals(int lhs, unsigned rhs) - { - return static_cast(lhs) == static_cast(rhs); - } -}; - -template struct ArithmeticOperations { - static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) - { - return ArithmeticOperations::add(rhs, lhs, result); - } - - static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) - { - return ArithmeticOperations::sub(lhs, rhs, result); - } - - static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) - { - return ArithmeticOperations::multiply(rhs, lhs, result); - } - - static inline bool equals(unsigned lhs, int rhs) - { - return ArithmeticOperations::equals(rhs, lhs); - } -}; - -template static inline bool safeAdd(U lhs, V rhs, R& result) -{ - return ArithmeticOperations::add(lhs, rhs, result); -} - -template static inline bool safeSub(U lhs, V rhs, R& result) -{ - return ArithmeticOperations::sub(lhs, rhs, result); -} - -template static inline bool safeMultiply(U lhs, V rhs, R& result) -{ - return ArithmeticOperations::multiply(lhs, rhs, result); -} - -template static inline bool safeEquals(U lhs, V rhs) -{ - return ArithmeticOperations::equals(lhs, rhs); -} - -enum ResultOverflowedTag { ResultOverflowed }; - -// FIXME: Needed to workaround http://llvm.org/bugs/show_bug.cgi?id=10801 -static inline bool workAroundClangBug() { return true; } - -template class Checked : public OverflowHandler { -public: - template friend class Checked; - Checked() - : m_value(0) - { - } - - Checked(ResultOverflowedTag) - : m_value(0) - { - // FIXME: Remove this when clang fixes http://llvm.org/bugs/show_bug.cgi?id=10801 - if (workAroundClangBug()) - this->overflowed(); - } - - template Checked(U value) - { - if (!isInBounds(value)) - this->overflowed(); - m_value = static_cast(value); - } - - template Checked(const Checked& rhs) - : m_value(rhs.m_value) - { - if (rhs.hasOverflowed()) - this->overflowed(); - } - - template Checked(const Checked& rhs) - : OverflowHandler(rhs) - { - if (!isInBounds(rhs.m_value)) - this->overflowed(); - m_value = static_cast(rhs.m_value); - } - - template Checked(const Checked& rhs) - { - if (rhs.hasOverflowed()) - this->overflowed(); - if (!isInBounds(rhs.m_value)) - this->overflowed(); - m_value = static_cast(rhs.m_value); - } - - const Checked& operator=(Checked rhs) - { - this->clearOverflow(); - if (rhs.hasOverflowed()) - this->overflowed(); - m_value = static_cast(rhs.m_value); - return *this; - } - - template const Checked& operator=(U value) - { - return *this = Checked(value); - } - - template const Checked& operator=(const Checked& rhs) - { - return *this = Checked(rhs); - } - - // prefix - const Checked& operator++() - { - if (m_value == std::numeric_limits::max()) - this->overflowed(); - m_value++; - return *this; - } - - const Checked& operator--() - { - if (m_value == std::numeric_limits::min()) - this->overflowed(); - m_value--; - return *this; - } - - // postfix operators - const Checked operator++(int) - { - if (m_value == std::numeric_limits::max()) - this->overflowed(); - return Checked(m_value++); - } - - const Checked operator--(int) - { - if (m_value == std::numeric_limits::min()) - this->overflowed(); - return Checked(m_value--); - } - - // Boolean operators - bool operator!() const - { - if (this->hasOverflowed()) - CRASH(); - return !m_value; - } - - typedef void* (Checked::*UnspecifiedBoolType); - operator UnspecifiedBoolType*() const - { - if (this->hasOverflowed()) - CRASH(); - return (m_value) ? reinterpret_cast(1) : 0; - } - - // Value accessors. unsafeGet() will crash if there's been an overflow. - T unsafeGet() const - { - if (this->hasOverflowed()) - CRASH(); - return m_value; - } - - bool safeGet(T& value) const WARN_UNUSED_RETURN - { - value = m_value; - return this->hasOverflowed(); - } - - // Mutating assignment - template const Checked operator+=(U rhs) - { - if (!safeAdd(m_value, rhs, m_value)) - this->overflowed(); - return *this; - } - - template const Checked operator-=(U rhs) - { - if (!safeSub(m_value, rhs, m_value)) - this->overflowed(); - return *this; - } - - template const Checked operator*=(U rhs) - { - if (!safeMultiply(m_value, rhs, m_value)) - this->overflowed(); - return *this; - } - - const Checked operator*=(double rhs) - { - double result = rhs * m_value; - // Handle +/- infinity and NaN - if (!(std::numeric_limits::min() <= result && std::numeric_limits::max() >= result)) - this->overflowed(); - m_value = (T)result; - return *this; - } - - const Checked operator*=(float rhs) - { - return *this *= (double)rhs; - } - - template const Checked operator+=(Checked rhs) - { - if (rhs.hasOverflowed()) - this->overflowed(); - return *this += rhs.m_value; - } - - template const Checked operator-=(Checked rhs) - { - if (rhs.hasOverflowed()) - this->overflowed(); - return *this -= rhs.m_value; - } - - template const Checked operator*=(Checked rhs) - { - if (rhs.hasOverflowed()) - this->overflowed(); - return *this *= rhs.m_value; - } - - // Equality comparisons - template bool operator==(Checked rhs) - { - return unsafeGet() == rhs.unsafeGet(); - } - - template bool operator==(U rhs) - { - if (this->hasOverflowed()) - this->overflowed(); - return safeEquals(m_value, rhs); - } - - template const Checked operator==(Checked rhs) - { - return unsafeGet() == Checked(rhs.unsafeGet()); - } - - template bool operator!=(U rhs) - { - return !(*this == rhs); - } - -private: - // Disallow implicit conversion of floating point to integer types - Checked(float); - Checked(double); - void operator=(float); - void operator=(double); - void operator+=(float); - void operator+=(double); - void operator-=(float); - void operator-=(double); - T m_value; -}; - -template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, Checked rhs) -{ - U x = 0; - V y = 0; - bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); - typename Result::ResultType result = 0; - overflowed |= !safeAdd(x, y, result); - if (overflowed) - return ResultOverflowed; - return result; -} - -template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, Checked rhs) -{ - U x = 0; - V y = 0; - bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); - typename Result::ResultType result = 0; - overflowed |= !safeSub(x, y, result); - if (overflowed) - return ResultOverflowed; - return result; -} - -template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, Checked rhs) -{ - U x = 0; - V y = 0; - bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); - typename Result::ResultType result = 0; - overflowed |= !safeMultiply(x, y, result); - if (overflowed) - return ResultOverflowed; - return result; -} - -template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, V rhs) -{ - return lhs + Checked(rhs); -} - -template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, V rhs) -{ - return lhs - Checked(rhs); -} - -template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, V rhs) -{ - return lhs * Checked(rhs); -} - -template static inline Checked::ResultType, OverflowHandler> operator+(U lhs, Checked rhs) -{ - return Checked(lhs) + rhs; -} - -template static inline Checked::ResultType, OverflowHandler> operator-(U lhs, Checked rhs) -{ - return Checked(lhs) - rhs; -} - -template static inline Checked::ResultType, OverflowHandler> operator*(U lhs, Checked rhs) -{ - return Checked(lhs) * rhs; -} - -} - -using WTF::Checked; -using WTF::RecordOverflow; - -#endif diff --git a/masm/wtf/Compiler.h b/masm/wtf/Compiler.h deleted file mode 100644 index a9ef419c18..0000000000 --- a/masm/wtf/Compiler.h +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_Compiler_h -#define WTF_Compiler_h - -/* COMPILER() - the compiler being used to build the project */ -#define COMPILER(WTF_FEATURE) (defined WTF_COMPILER_##WTF_FEATURE && WTF_COMPILER_##WTF_FEATURE) - -/* COMPILER_SUPPORTS() - whether the compiler being used to build the project supports the given feature. */ -#define COMPILER_SUPPORTS(WTF_COMPILER_FEATURE) (defined WTF_COMPILER_SUPPORTS_##WTF_COMPILER_FEATURE && WTF_COMPILER_SUPPORTS_##WTF_COMPILER_FEATURE) - -/* COMPILER_QUIRK() - whether the compiler being used to build the project requires a given quirk. */ -#define COMPILER_QUIRK(WTF_COMPILER_QUIRK) (defined WTF_COMPILER_QUIRK_##WTF_COMPILER_QUIRK && WTF_COMPILER_QUIRK_##WTF_COMPILER_QUIRK) - -/* ==== COMPILER() - the compiler being used to build the project ==== */ - -/* COMPILER(CLANG) - Clang */ -#if defined(__clang__) -#define WTF_COMPILER_CLANG 1 - -#ifndef __has_extension -#define __has_extension __has_feature /* Compatibility with older versions of clang */ -#endif - -#define CLANG_PRAGMA(PRAGMA) _Pragma(PRAGMA) - -/* Specific compiler features */ -#define WTF_COMPILER_SUPPORTS_CXX_VARIADIC_TEMPLATES __has_extension(cxx_variadic_templates) - -/* There is a bug in clang that comes with Xcode 4.2 where AtomicStrings can't be implicitly converted to Strings - in the presence of move constructors and/or move assignment operators. This bug has been fixed in Xcode 4.3 clang, so we - check for both cxx_rvalue_references as well as the unrelated cxx_nonstatic_member_init feature which we know was added in 4.3 */ -#define WTF_COMPILER_SUPPORTS_CXX_RVALUE_REFERENCES __has_extension(cxx_rvalue_references) && __has_extension(cxx_nonstatic_member_init) - -#define WTF_COMPILER_SUPPORTS_CXX_DELETED_FUNCTIONS __has_extension(cxx_deleted_functions) -#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR __has_feature(cxx_nullptr) -#define WTF_COMPILER_SUPPORTS_CXX_EXPLICIT_CONVERSIONS __has_feature(cxx_explicit_conversions) -#define WTF_COMPILER_SUPPORTS_BLOCKS __has_feature(blocks) -#define WTF_COMPILER_SUPPORTS_C_STATIC_ASSERT __has_extension(c_static_assert) -#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL __has_extension(cxx_override_control) -#define WTF_COMPILER_SUPPORTS_HAS_TRIVIAL_DESTRUCTOR __has_extension(has_trivial_destructor) - -#endif - -#ifndef CLANG_PRAGMA -#define CLANG_PRAGMA(PRAGMA) -#endif - -/* COMPILER(MSVC) - Microsoft Visual C++ */ -/* COMPILER(MSVC7_OR_LOWER) - Microsoft Visual C++ 2003 or lower*/ -/* COMPILER(MSVC9_OR_LOWER) - Microsoft Visual C++ 2008 or lower*/ -#if defined(_MSC_VER) -#define WTF_COMPILER_MSVC 1 -#if _MSC_VER < 1400 -#define WTF_COMPILER_MSVC7_OR_LOWER 1 -#elif _MSC_VER < 1600 -#define WTF_COMPILER_MSVC9_OR_LOWER 1 -#endif - -/* Specific compiler features */ -#if !COMPILER(CLANG) && _MSC_VER >= 1600 -#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 -#endif - -#if !COMPILER(CLANG) -#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL 1 -#define WTF_COMPILER_QUIRK_FINAL_IS_CALLED_SEALED 1 -#endif - -#endif - -/* COMPILER(RVCT) - ARM RealView Compilation Tools */ -/* COMPILER(RVCT4_OR_GREATER) - ARM RealView Compilation Tools 4.0 or greater */ -#if defined(__CC_ARM) || defined(__ARMCC__) -#define WTF_COMPILER_RVCT 1 -#define RVCT_VERSION_AT_LEAST(major, minor, patch, build) (__ARMCC_VERSION >= (major * 100000 + minor * 10000 + patch * 1000 + build)) -#else -/* Define this for !RVCT compilers, just so we can write things like RVCT_VERSION_AT_LEAST(3, 0, 0, 0). */ -#define RVCT_VERSION_AT_LEAST(major, minor, patch, build) 0 -#endif - -/* COMPILER(GCCE) - GNU Compiler Collection for Embedded */ -#if defined(__GCCE__) -#define WTF_COMPILER_GCCE 1 -#define GCCE_VERSION (__GCCE__ * 10000 + __GCCE_MINOR__ * 100 + __GCCE_PATCHLEVEL__) -#define GCCE_VERSION_AT_LEAST(major, minor, patch) (GCCE_VERSION >= (major * 10000 + minor * 100 + patch)) -#endif - -/* COMPILER(GCC) - GNU Compiler Collection */ -/* --gnu option of the RVCT compiler also defines __GNUC__ */ -#if defined(__GNUC__) && !COMPILER(RVCT) -#define WTF_COMPILER_GCC 1 -#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) -#define GCC_VERSION_AT_LEAST(major, minor, patch) (GCC_VERSION >= (major * 10000 + minor * 100 + patch)) -#else -/* Define this for !GCC compilers, just so we can write things like GCC_VERSION_AT_LEAST(4, 1, 0). */ -#define GCC_VERSION_AT_LEAST(major, minor, patch) 0 -#endif - -/* Specific compiler features */ -#if COMPILER(GCC) && !COMPILER(CLANG) -#if GCC_VERSION_AT_LEAST(4, 7, 0) && defined(__cplusplus) && __cplusplus >= 201103L -#define WTF_COMPILER_SUPPORTS_CXX_RVALUE_REFERENCES 1 -#define WTF_COMPILER_SUPPORTS_CXX_DELETED_FUNCTIONS 1 -#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 -#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL 1 -#define WTF_COMPILER_QUIRK_GCC11_GLOBAL_ISINF_ISNAN 1 - -#elif GCC_VERSION_AT_LEAST(4, 6, 0) && defined(__GXX_EXPERIMENTAL_CXX0X__) -#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 -#define WTF_COMPILER_QUIRK_GCC11_GLOBAL_ISINF_ISNAN 1 -#endif - -#endif - -/* COMPILER(MINGW) - MinGW GCC */ -/* COMPILER(MINGW64) - mingw-w64 GCC - only used as additional check to exclude mingw.org specific functions */ -#if defined(__MINGW32__) -#define WTF_COMPILER_MINGW 1 -#include <_mingw.h> /* private MinGW header */ - #if defined(__MINGW64_VERSION_MAJOR) /* best way to check for mingw-w64 vs mingw.org */ - #define WTF_COMPILER_MINGW64 1 - #endif /* __MINGW64_VERSION_MAJOR */ -#endif /* __MINGW32__ */ - -/* COMPILER(INTEL) - Intel C++ Compiler */ -#if defined(__INTEL_COMPILER) -#define WTF_COMPILER_INTEL 1 -#endif - -/* COMPILER(SUNCC) */ -#if defined(__SUNPRO_CC) || defined(__SUNPRO_C) -#define WTF_COMPILER_SUNCC 1 -#endif - -/* ==== Compiler features ==== */ - - -/* ALWAYS_INLINE */ - -#ifndef ALWAYS_INLINE -#if COMPILER(GCC) && defined(NDEBUG) && !COMPILER(MINGW) -#define ALWAYS_INLINE inline __attribute__((__always_inline__)) -#elif (COMPILER(MSVC) || COMPILER(RVCT)) && defined(NDEBUG) -#define ALWAYS_INLINE __forceinline -#else -#define ALWAYS_INLINE inline -#endif -#endif - - -/* NEVER_INLINE */ - -#ifndef NEVER_INLINE -#if COMPILER(GCC) -#define NEVER_INLINE __attribute__((__noinline__)) -#elif COMPILER(RVCT) -#define NEVER_INLINE __declspec(noinline) -#else -#define NEVER_INLINE -#endif -#endif - - -/* UNLIKELY */ - -#ifndef UNLIKELY -#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(3, 0, 0, 0) && defined(__GNUC__)) -#define UNLIKELY(x) __builtin_expect((x), 0) -#else -#define UNLIKELY(x) (x) -#endif -#endif - - -/* LIKELY */ - -#ifndef LIKELY -#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(3, 0, 0, 0) && defined(__GNUC__)) -#define LIKELY(x) __builtin_expect((x), 1) -#else -#define LIKELY(x) (x) -#endif -#endif - - -/* NO_RETURN */ - - -#ifndef NO_RETURN -#if COMPILER(GCC) -#define NO_RETURN __attribute((__noreturn__)) -#elif COMPILER(MSVC) || COMPILER(RVCT) -#define NO_RETURN __declspec(noreturn) -#else -#define NO_RETURN -#endif -#endif - - -/* NO_RETURN_WITH_VALUE */ - -#ifndef NO_RETURN_WITH_VALUE -#if !COMPILER(MSVC) -#define NO_RETURN_WITH_VALUE NO_RETURN -#else -#define NO_RETURN_WITH_VALUE -#endif -#endif - - -/* WARN_UNUSED_RETURN */ - -#if COMPILER(GCC) -#define WARN_UNUSED_RETURN __attribute__ ((warn_unused_result)) -#else -#define WARN_UNUSED_RETURN -#endif - -/* OVERRIDE and FINAL */ - -#if COMPILER_SUPPORTS(CXX_OVERRIDE_CONTROL) -#define OVERRIDE override - -#if COMPILER_QUIRK(FINAL_IS_CALLED_SEALED) -#define FINAL sealed -#else -#define FINAL final -#endif - -#else -#define OVERRIDE -#define FINAL -#endif - -/* REFERENCED_FROM_ASM */ - -#ifndef REFERENCED_FROM_ASM -#if COMPILER(GCC) -#define REFERENCED_FROM_ASM __attribute__((used)) -#else -#define REFERENCED_FROM_ASM -#endif -#endif - -/* OBJC_CLASS */ - -#ifndef OBJC_CLASS -#ifdef __OBJC__ -#define OBJC_CLASS @class -#else -#define OBJC_CLASS class -#endif -#endif - -/* ABI */ -#if defined(__ARM_EABI__) || defined(__EABI__) -#define WTF_COMPILER_SUPPORTS_EABI 1 -#endif - -#endif /* WTF_Compiler_h */ diff --git a/masm/wtf/CryptographicallyRandomNumber.h b/masm/wtf/CryptographicallyRandomNumber.h deleted file mode 100644 index 2262b6c3b3..0000000000 --- a/masm/wtf/CryptographicallyRandomNumber.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_CryptographicallyRandomNumber_h -#define WTF_CryptographicallyRandomNumber_h - -#include - -namespace WTF { - -#if USE(OS_RANDOMNESS) -WTF_EXPORT_PRIVATE uint32_t cryptographicallyRandomNumber(); -WTF_EXPORT_PRIVATE void cryptographicallyRandomValues(void* buffer, size_t length); -#endif - -} - -#if USE(OS_RANDOMNESS) -using WTF::cryptographicallyRandomNumber; -using WTF::cryptographicallyRandomValues; -#endif - -#endif diff --git a/masm/wtf/DataLog.h b/masm/wtf/DataLog.h deleted file mode 100644 index 0bd8efe727..0000000000 --- a/masm/wtf/DataLog.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef DataLog_h -#define DataLog_h - -#include -#include -#include -#include -#include - -namespace WTF { - -WTF_EXPORT_PRIVATE FilePrintStream& dataFile(); - -WTF_EXPORT_PRIVATE void dataLogFV(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(1, 0); -WTF_EXPORT_PRIVATE void dataLogF(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); -WTF_EXPORT_PRIVATE void dataLogFString(const char*); - -template -void dataLog(const T& value) -{ - dataFile().print(value); -} - -template -void dataLog(const T1& value1, const T2& value2) -{ - dataFile().print(value1, value2); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3) -{ - dataFile().print(value1, value2, value3); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4) -{ - dataFile().print(value1, value2, value3, value4); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5) -{ - dataFile().print(value1, value2, value3, value4, value5); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6) -{ - dataFile().print(value1, value2, value3, value4, value5, value6); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11, value12); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12, const T13& value13) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11, value12, value13); -} - -} // namespace WTF - -using WTF::dataLog; -using WTF::dataLogF; -using WTF::dataLogFString; - -#endif // DataLog_h - diff --git a/masm/wtf/DynamicAnnotations.h b/masm/wtf/DynamicAnnotations.h deleted file mode 100644 index 38acce35e6..0000000000 --- a/masm/wtf/DynamicAnnotations.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_DynamicAnnotations_h -#define WTF_DynamicAnnotations_h - -/* This file defines dynamic annotations for use with dynamic analysis - * tool such as ThreadSanitizer, Valgrind, etc. - * - * Dynamic annotation is a source code annotation that affects - * the generated code (that is, the annotation is not a comment). - * Each such annotation is attached to a particular - * instruction and/or to a particular object (address) in the program. - * - * By using dynamic annotations a developer can give more details to the dynamic - * analysis tool to improve its precision. - * - * In C/C++ program the annotations are represented as C macros. - * With the default build flags, these macros are empty, hence don't affect - * performance of a compiled binary. - * If dynamic annotations are enabled, they just call no-op functions. - * The dynamic analysis tools can intercept these functions and replace them - * with their own implementations. - * - * See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations for more information. - */ - -#if USE(DYNAMIC_ANNOTATIONS) -/* Tell data race detector that we're not interested in reports on the given address range. */ -#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) WTFAnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description) -#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) WTFAnnotateBenignRaceSized(__FILE__, __LINE__, pointer, sizeof(*(pointer)), description) - -/* Annotations for user-defined synchronization mechanisms. - * These annotations can be used to define happens-before arcs in user-defined - * synchronization mechanisms: the race detector will infer an arc from - * the former to the latter when they share the same argument pointer. - * - * The most common case requiring annotations is atomic reference counting: - * bool deref() { - * ANNOTATE_HAPPENS_BEFORE(&m_refCount); - * if (!atomicDecrement(&m_refCount)) { - * // m_refCount is now 0 - * ANNOTATE_HAPPENS_AFTER(&m_refCount); - * // "return true; happens-after each atomicDecrement of m_refCount" - * return true; - * } - * return false; - * } - */ -#define WTF_ANNOTATE_HAPPENS_BEFORE(address) WTFAnnotateHappensBefore(__FILE__, __LINE__, address) -#define WTF_ANNOTATE_HAPPENS_AFTER(address) WTFAnnotateHappensAfter(__FILE__, __LINE__, address) - -#ifdef __cplusplus -extern "C" { -#endif -/* Don't use these directly, use the above macros instead. */ -void WTFAnnotateBenignRaceSized(const char* file, int line, const volatile void* memory, long size, const char* description); -void WTFAnnotateHappensBefore(const char* file, int line, const volatile void* address); -void WTFAnnotateHappensAfter(const char* file, int line, const volatile void* address); -#ifdef __cplusplus -} // extern "C" -#endif - -#else // USE(DYNAMIC_ANNOTATIONS) -/* These macros are empty when dynamic annotations are not enabled so you can - * use them without affecting the performance of release binaries. */ -#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) -#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) -#define WTF_ANNOTATE_HAPPENS_BEFORE(address) -#define WTF_ANNOTATE_HAPPENS_AFTER(address) -#endif // USE(DYNAMIC_ANNOTATIONS) - -#endif // WTF_DynamicAnnotations_h diff --git a/masm/wtf/FilePrintStream.cpp b/masm/wtf/FilePrintStream.cpp deleted file mode 100644 index b5ab25e0bf..0000000000 --- a/masm/wtf/FilePrintStream.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "FilePrintStream.h" - -namespace WTF { - -FilePrintStream::FilePrintStream(FILE* file, AdoptionMode adoptionMode) - : m_file(file) - , m_adoptionMode(adoptionMode) -{ -} - -FilePrintStream::~FilePrintStream() -{ - if (m_adoptionMode == Borrow) - return; - fclose(m_file); -} - -PassOwnPtr FilePrintStream::open(const char* filename, const char* mode) -{ - FILE* file = fopen(filename, mode); - if (!file) - return PassOwnPtr(); - - return adoptPtr(new FilePrintStream(file)); -} - -void FilePrintStream::vprintf(const char* format, va_list argList) -{ - vfprintf(m_file, format, argList); -} - -void FilePrintStream::flush() -{ - fflush(m_file); -} - -} // namespace WTF - diff --git a/masm/wtf/FilePrintStream.h b/masm/wtf/FilePrintStream.h deleted file mode 100644 index bdeab4c479..0000000000 --- a/masm/wtf/FilePrintStream.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef FilePrintStream_h -#define FilePrintStream_h - -#include -#include -#include - -namespace WTF { - -class FilePrintStream : public PrintStream { -public: - enum AdoptionMode { - Adopt, - Borrow - }; - - FilePrintStream(FILE*, AdoptionMode = Adopt); - virtual ~FilePrintStream(); - - static PassOwnPtr open(const char* filename, const char* mode); - - FILE* file() { return m_file; } - - void vprintf(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(2, 0); - void flush(); - -private: - FILE* m_file; - AdoptionMode m_adoptionMode; -}; - -} // namespace WTF - -using WTF::FilePrintStream; - -#endif // FilePrintStream_h - diff --git a/masm/wtf/Locker.h b/masm/wtf/Locker.h deleted file mode 100644 index c465b99ea4..0000000000 --- a/masm/wtf/Locker.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef Locker_h -#define Locker_h - -#include - -namespace WTF { - -template class Locker { - WTF_MAKE_NONCOPYABLE(Locker); -public: - Locker(T& lockable) : m_lockable(lockable) { m_lockable.lock(); } - ~Locker() { m_lockable.unlock(); } -private: - T& m_lockable; -}; - -} - -using WTF::Locker; - -#endif diff --git a/masm/wtf/NotFound.h b/masm/wtf/NotFound.h deleted file mode 100644 index 4263bcecab..0000000000 --- a/masm/wtf/NotFound.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef NotFound_h -#define NotFound_h - -namespace WTF { - - const size_t notFound = static_cast(-1); - -} // namespace WTF - -using WTF::notFound; - -#endif // NotFound_h diff --git a/masm/wtf/NullPtr.h b/masm/wtf/NullPtr.h deleted file mode 100644 index 98c05140d8..0000000000 --- a/masm/wtf/NullPtr.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - -Copyright (C) 2010 Apple Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef NullPtr_h -#define NullPtr_h - -// For compilers and standard libraries that do not yet include it, this adds the -// nullptr_t type and nullptr object. They are defined in the same namespaces they -// would be in compiler and library that had the support. - -#include - -#if COMPILER_SUPPORTS(CXX_NULLPTR) || defined(_LIBCPP_VERSION) - -#include - -// libstdc++ supports nullptr_t starting with gcc 4.6. -#if defined(__GLIBCXX__) && __GLIBCXX__ < 20110325 -namespace std { -typedef decltype(nullptr) nullptr_t; -} -#endif - -#else - -namespace std { -class WTF_EXPORT_PRIVATE nullptr_t { }; -} -extern WTF_EXPORT_PRIVATE std::nullptr_t nullptr; - -#endif - -#endif diff --git a/masm/wtf/OSAllocator.h b/masm/wtf/OSAllocator.h deleted file mode 100644 index a12a467497..0000000000 --- a/masm/wtf/OSAllocator.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef OSAllocator_h -#define OSAllocator_h - -#include -#include -#include - -namespace WTF { - -class OSAllocator { -public: - enum Usage { - UnknownUsage = -1, - FastMallocPages = VM_TAG_FOR_TCMALLOC_MEMORY, - JSGCHeapPages = VM_TAG_FOR_COLLECTOR_MEMORY, - JSVMStackPages = VM_TAG_FOR_REGISTERFILE_MEMORY, - JSJITCodePages = VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY, - }; - - // These methods are symmetric; reserveUncommitted allocates VM in an uncommitted state, - // releaseDecommitted should be called on a region of VM allocated by a single reservation, - // the memory must all currently be in a decommitted state. - static void* reserveUncommitted(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool includesGuardPages = false); - WTF_EXPORT_PRIVATE static void releaseDecommitted(void*, size_t); - - // These methods are symmetric; they commit or decommit a region of VM (uncommitted VM should - // never be accessed, since the OS may not have attached physical memory for these regions). - // Clients should only call commit on uncommitted regions and decommit on committed regions. - static void commit(void*, size_t, bool writable, bool executable); - static void decommit(void*, size_t); - - // These methods are symmetric; reserveAndCommit allocates VM in an committed state, - // decommitAndRelease should be called on a region of VM allocated by a single reservation, - // the memory must all currently be in a committed state. - WTF_EXPORT_PRIVATE static void* reserveAndCommit(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool includesGuardPages = false); - static void decommitAndRelease(void* base, size_t size); - - // These methods are akin to reserveAndCommit/decommitAndRelease, above - however rather than - // committing/decommitting the entire region additional parameters allow a subregion to be - // specified. - static void* reserveAndCommit(size_t reserveSize, size_t commitSize, Usage = UnknownUsage, bool writable = true, bool executable = false); - static void decommitAndRelease(void* releaseBase, size_t releaseSize, void* decommitBase, size_t decommitSize); - - // Reallocate an existing, committed allocation. - // The prior allocation must be fully comitted, and the new size will also be fully committed. - // This interface is provided since it may be possible to optimize this operation on some platforms. - template - static T* reallocateCommitted(T*, size_t oldSize, size_t newSize, Usage = UnknownUsage, bool writable = true, bool executable = false); -}; - -inline void* OSAllocator::reserveAndCommit(size_t reserveSize, size_t commitSize, Usage usage, bool writable, bool executable) -{ - void* base = reserveUncommitted(reserveSize, usage, writable, executable); - commit(base, commitSize, writable, executable); - return base; -} - -inline void OSAllocator::decommitAndRelease(void* releaseBase, size_t releaseSize, void* decommitBase, size_t decommitSize) -{ - ASSERT(decommitBase >= releaseBase && (static_cast(decommitBase) + decommitSize) <= (static_cast(releaseBase) + releaseSize)); -#if OS(WINCE) - // On most platforms we can actually skip this final decommit; releasing the VM will - // implicitly decommit any physical memory in the region. This is not true on WINCE. - decommit(decommitBase, decommitSize); -#else - UNUSED_PARAM(decommitBase); - UNUSED_PARAM(decommitSize); -#endif - releaseDecommitted(releaseBase, releaseSize); -} - -inline void OSAllocator::decommitAndRelease(void* base, size_t size) -{ - decommitAndRelease(base, size, base, size); -} - -template -inline T* OSAllocator::reallocateCommitted(T* oldBase, size_t oldSize, size_t newSize, Usage usage, bool writable, bool executable) -{ - void* newBase = reserveAndCommit(newSize, usage, writable, executable); - memcpy(newBase, oldBase, std::min(oldSize, newSize)); - decommitAndRelease(oldBase, oldSize); - return static_cast(newBase); -} - -} // namespace WTF - -using WTF::OSAllocator; - -#endif // OSAllocator_h diff --git a/masm/wtf/OSAllocatorPosix.cpp b/masm/wtf/OSAllocatorPosix.cpp deleted file mode 100644 index b5b903b8a3..0000000000 --- a/masm/wtf/OSAllocatorPosix.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "OSAllocator.h" - -#include "PageAllocation.h" -#include -#include -#include -#include - -namespace WTF { - -void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) -{ -#if OS(QNX) - // Reserve memory with PROT_NONE and MAP_LAZY so it isn't committed now. - void* result = mmap(0, bytes, PROT_NONE, MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); - if (result == MAP_FAILED) - CRASH(); -#elif OS(LINUX) - void* result = mmap(0, bytes, PROT_NONE, MAP_NORESERVE | MAP_PRIVATE | MAP_ANON, -1, 0); - if (result == MAP_FAILED) - CRASH(); - madvise(result, bytes, MADV_DONTNEED); -#else - void* result = reserveAndCommit(bytes, usage, writable, executable, includesGuardPages); -#if HAVE(MADV_FREE_REUSE) - // To support the "reserve then commit" model, we have to initially decommit. - while (madvise(result, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { } -#endif - -#endif // OS(QNX) - - return result; -} - -void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) -{ - // All POSIX reservations start out logically committed. - int protection = PROT_READ; - if (writable) - protection |= PROT_WRITE; - if (executable) - protection |= PROT_EXEC; - - int flags = MAP_PRIVATE | MAP_ANON; -#if PLATFORM(IOS) - if (executable) - flags |= MAP_JIT; -#endif - -#if OS(DARWIN) - int fd = usage; -#else - UNUSED_PARAM(usage); - int fd = -1; -#endif - - void* result = 0; -#if (OS(DARWIN) && CPU(X86_64)) - if (executable) { - ASSERT(includesGuardPages); - // Cook up an address to allocate at, using the following recipe: - // 17 bits of zero, stay in userspace kids. - // 26 bits of randomness for ASLR. - // 21 bits of zero, at least stay aligned within one level of the pagetables. - // - // But! - as a temporary workaround for some plugin problems (rdar://problem/6812854), - // for now instead of 2^26 bits of ASLR lets stick with 25 bits of randomization plus - // 2^24, which should put up somewhere in the middle of userspace (in the address range - // 0x200000000000 .. 0x5fffffffffff). - intptr_t randomLocation = 0; - randomLocation = arc4random() & ((1 << 25) - 1); - randomLocation += (1 << 24); - randomLocation <<= 21; - result = reinterpret_cast(randomLocation); - } -#endif - - result = mmap(result, bytes, protection, flags, fd, 0); - if (result == MAP_FAILED) { -#if ENABLE(LLINT) - if (executable) - result = 0; - else -#endif - CRASH(); - } - if (result && includesGuardPages) { - // We use mmap to remap the guardpages rather than using mprotect as - // mprotect results in multiple references to the code region. This - // breaks the madvise based mechanism we use to return physical memory - // to the OS. - mmap(result, pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0); - mmap(static_cast(result) + bytes - pageSize(), pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0); - } - return result; -} - -void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable) -{ -#if OS(QNX) - int protection = PROT_READ; - if (writable) - protection |= PROT_WRITE; - if (executable) - protection |= PROT_EXEC; - if (MAP_FAILED == mmap(address, bytes, protection, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0)) - CRASH(); -#elif OS(LINUX) - int protection = PROT_READ; - if (writable) - protection |= PROT_WRITE; - if (executable) - protection |= PROT_EXEC; - if (mprotect(address, bytes, protection)) - CRASH(); - madvise(address, bytes, MADV_WILLNEED); -#elif HAVE(MADV_FREE_REUSE) - UNUSED_PARAM(writable); - UNUSED_PARAM(executable); - while (madvise(address, bytes, MADV_FREE_REUSE) == -1 && errno == EAGAIN) { } -#else - // Non-MADV_FREE_REUSE reservations automatically commit on demand. - UNUSED_PARAM(address); - UNUSED_PARAM(bytes); - UNUSED_PARAM(writable); - UNUSED_PARAM(executable); -#endif -} - -void OSAllocator::decommit(void* address, size_t bytes) -{ -#if OS(QNX) - // Use PROT_NONE and MAP_LAZY to decommit the pages. - mmap(address, bytes, PROT_NONE, MAP_FIXED | MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); -#elif OS(LINUX) - madvise(address, bytes, MADV_DONTNEED); - if (mprotect(address, bytes, PROT_NONE)) - CRASH(); -#elif HAVE(MADV_FREE_REUSE) - while (madvise(address, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { } -#elif HAVE(MADV_FREE) - while (madvise(address, bytes, MADV_FREE) == -1 && errno == EAGAIN) { } -#elif HAVE(MADV_DONTNEED) - while (madvise(address, bytes, MADV_DONTNEED) == -1 && errno == EAGAIN) { } -#else - UNUSED_PARAM(address); - UNUSED_PARAM(bytes); -#endif -} - -void OSAllocator::releaseDecommitted(void* address, size_t bytes) -{ - int result = munmap(address, bytes); - if (result == -1) - CRASH(); -} - -} // namespace WTF diff --git a/masm/wtf/OSAllocatorWin.cpp b/masm/wtf/OSAllocatorWin.cpp deleted file mode 100644 index 7f5d9b8904..0000000000 --- a/masm/wtf/OSAllocatorWin.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "OSAllocator.h" - -#include "windows.h" -#include - -namespace WTF { - -static inline DWORD protection(bool writable, bool executable) -{ - return executable ? - (writable ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ) : - (writable ? PAGE_READWRITE : PAGE_READONLY); -} - -void* OSAllocator::reserveUncommitted(size_t bytes, Usage, bool writable, bool executable, bool) -{ - void* result = VirtualAlloc(0, bytes, MEM_RESERVE, protection(writable, executable)); - if (!result) - CRASH(); - return result; -} - -void* OSAllocator::reserveAndCommit(size_t bytes, Usage, bool writable, bool executable, bool) -{ - void* result = VirtualAlloc(0, bytes, MEM_RESERVE | MEM_COMMIT, protection(writable, executable)); - if (!result) - CRASH(); - return result; -} - -void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable) -{ - void* result = VirtualAlloc(address, bytes, MEM_COMMIT, protection(writable, executable)); - if (!result) - CRASH(); -} - -void OSAllocator::decommit(void* address, size_t bytes) -{ - bool result = VirtualFree(address, bytes, MEM_DECOMMIT); - if (!result) - CRASH(); -} - -void OSAllocator::releaseDecommitted(void* address, size_t bytes) -{ - // According to http://msdn.microsoft.com/en-us/library/aa366892(VS.85).aspx, - // dwSize must be 0 if dwFreeType is MEM_RELEASE. - bool result = VirtualFree(address, 0, MEM_RELEASE); - if (!result) - CRASH(); -} - -} // namespace WTF diff --git a/masm/wtf/PageAllocation.h b/masm/wtf/PageAllocation.h deleted file mode 100644 index 18d31880c0..0000000000 --- a/masm/wtf/PageAllocation.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef PageAllocation_h -#define PageAllocation_h - -#include -#include -#include -#include -#include -#include - -#if OS(DARWIN) -#include -#include -#endif - -#if OS(WINDOWS) -#include -#include -#endif - -#if HAVE(ERRNO_H) -#include -#endif - -#if HAVE(MMAP) -#include -#include -#endif - -namespace WTF { - -/* - PageAllocation - - The PageAllocation class provides a cross-platform memory allocation interface - with similar capabilities to posix mmap/munmap. Memory is allocated by calling - PageAllocation::allocate, and deallocated by calling deallocate on the - PageAllocation object. The PageAllocation holds the allocation's base pointer - and size. - - The allocate method is passed the size required (which must be a multiple of - the system page size, which can be accessed using PageAllocation::pageSize). - Callers may also optinally provide a flag indicating the usage (for use by - system memory usage tracking tools, where implemented), and boolean values - specifying the required protection (defaulting to writable, non-executable). -*/ - -class PageAllocation : private PageBlock { -public: - PageAllocation() - { - } - - using PageBlock::size; - using PageBlock::base; - -#ifndef __clang__ - using PageBlock::operator bool; -#else - // FIXME: This is a workaround for , wherein Clang incorrectly emits an access - // control warning when a client tries to use operator bool exposed above via "using PageBlock::operator bool". - operator bool() const { return PageBlock::operator bool(); } -#endif - - static PageAllocation allocate(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) - { - ASSERT(isPageAligned(size)); - return PageAllocation(OSAllocator::reserveAndCommit(size, usage, writable, executable), size); - } - - void deallocate() - { - // Clear base & size before calling release; if this is *inside* allocation - // then we won't be able to clear then after deallocating the memory. - PageAllocation tmp; - std::swap(tmp, *this); - - ASSERT(tmp); - ASSERT(!*this); - - OSAllocator::decommitAndRelease(tmp.base(), tmp.size()); - } - -private: - PageAllocation(void* base, size_t size) - : PageBlock(base, size, false) - { - } -}; - -} // namespace WTF - -using WTF::PageAllocation; - -#endif // PageAllocation_h diff --git a/masm/wtf/PageAllocationAligned.cpp b/masm/wtf/PageAllocationAligned.cpp deleted file mode 100644 index 6f54710d0b..0000000000 --- a/masm/wtf/PageAllocationAligned.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "PageAllocationAligned.h" - -namespace WTF { - -PageAllocationAligned PageAllocationAligned::allocate(size_t size, size_t alignment, OSAllocator::Usage usage, bool writable, bool executable) -{ - ASSERT(isPageAligned(size)); - ASSERT(isPageAligned(alignment)); - ASSERT(isPowerOfTwo(alignment)); - ASSERT(size >= alignment); - size_t alignmentMask = alignment - 1; - -#if OS(DARWIN) - int flags = VM_FLAGS_ANYWHERE; - if (usage != OSAllocator::UnknownUsage) - flags |= usage; - int protection = PROT_READ; - if (writable) - protection |= PROT_WRITE; - if (executable) - protection |= PROT_EXEC; - - vm_address_t address = 0; - vm_map(current_task(), &address, size, alignmentMask, flags, MEMORY_OBJECT_NULL, 0, FALSE, protection, PROT_READ | PROT_WRITE | PROT_EXEC, VM_INHERIT_DEFAULT); - return PageAllocationAligned(reinterpret_cast(address), size); -#else - size_t alignmentDelta = alignment - pageSize(); - - // Resererve with suffcient additional VM to correctly align. - size_t reservationSize = size + alignmentDelta; - void* reservationBase = OSAllocator::reserveUncommitted(reservationSize, usage, writable, executable); - - // Select an aligned region within the reservation and commit. - void* alignedBase = reinterpret_cast(reservationBase) & alignmentMask - ? reinterpret_cast((reinterpret_cast(reservationBase) & ~alignmentMask) + alignment) - : reservationBase; - OSAllocator::commit(alignedBase, size, writable, executable); - - return PageAllocationAligned(alignedBase, size, reservationBase, reservationSize); -#endif -} - -void PageAllocationAligned::deallocate() -{ - // Clear base & size before calling release; if this is *inside* allocation - // then we won't be able to clear then after deallocating the memory. - PageAllocationAligned tmp; - std::swap(tmp, *this); - - ASSERT(tmp); - ASSERT(!*this); - -#if OS(DARWIN) - vm_deallocate(current_task(), reinterpret_cast(tmp.base()), tmp.size()); -#else - ASSERT(tmp.m_reservation.contains(tmp.base(), tmp.size())); - OSAllocator::decommitAndRelease(tmp.m_reservation.base(), tmp.m_reservation.size(), tmp.base(), tmp.size()); -#endif -} - -} // namespace WTF diff --git a/masm/wtf/PageAllocationAligned.h b/masm/wtf/PageAllocationAligned.h deleted file mode 100644 index c018dabd8e..0000000000 --- a/masm/wtf/PageAllocationAligned.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef PageAllocationAligned_h -#define PageAllocationAligned_h - -#include -#include - -namespace WTF { - -class PageAllocationAligned : private PageBlock { -public: - PageAllocationAligned() - { - } - - using PageBlock::operator bool; - using PageBlock::size; - using PageBlock::base; - - static PageAllocationAligned allocate(size_t size, size_t alignment, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false); - - void deallocate(); - -private: -#if OS(DARWIN) - PageAllocationAligned(void* base, size_t size) - : PageBlock(base, size, false) - { - } -#else - PageAllocationAligned(void* base, size_t size, void* reservationBase, size_t reservationSize) - : PageBlock(base, size, false) - , m_reservation(reservationBase, reservationSize, false) - { - } - - PageBlock m_reservation; -#endif -}; - - -} // namespace WTF - -using WTF::PageAllocationAligned; - -#endif // PageAllocationAligned_h diff --git a/masm/wtf/PageBlock.cpp b/masm/wtf/PageBlock.cpp deleted file mode 100644 index 8bbd7eb600..0000000000 --- a/masm/wtf/PageBlock.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "PageBlock.h" - -#if OS(UNIX) -#include -#endif - -#if OS(WINDOWS) -#include -#include -#endif - -namespace WTF { - -static size_t s_pageSize; -static size_t s_pageMask; - -#if OS(UNIX) - -inline size_t systemPageSize() -{ - return getpagesize(); -} - -#elif OS(WINDOWS) - -inline size_t systemPageSize() -{ - static size_t size = 0; - SYSTEM_INFO system_info; - GetSystemInfo(&system_info); - size = system_info.dwPageSize; - return size; -} - -#endif - -size_t pageSize() -{ - if (!s_pageSize) - s_pageSize = systemPageSize(); - ASSERT(isPowerOfTwo(s_pageSize)); - return s_pageSize; -} - -size_t pageMask() -{ - if (!s_pageMask) - s_pageMask = ~(pageSize() - 1); - return s_pageMask; -} - -} // namespace WTF diff --git a/masm/wtf/PageBlock.h b/masm/wtf/PageBlock.h deleted file mode 100644 index 56e5570178..0000000000 --- a/masm/wtf/PageBlock.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef PageBlock_h -#define PageBlock_h - -namespace WTF { - -WTF_EXPORT_PRIVATE size_t pageSize(); -WTF_EXPORT_PRIVATE size_t pageMask(); -inline bool isPageAligned(void* address) { return !(reinterpret_cast(address) & (pageSize() - 1)); } -inline bool isPageAligned(size_t size) { return !(size & (pageSize() - 1)); } -inline bool isPowerOfTwo(size_t size) { return !(size & (size - 1)); } - -class PageBlock { -public: - PageBlock(); - PageBlock(const PageBlock&); - PageBlock(void*, size_t, bool hasGuardPages); - - void* base() const { return m_base; } - size_t size() const { return m_size; } - - operator bool() const { return !!m_realBase; } - - bool contains(void* containedBase, size_t containedSize) - { - return containedBase >= m_base - && (static_cast(containedBase) + containedSize) <= (static_cast(m_base) + m_size); - } - -private: - void* m_realBase; - void* m_base; - size_t m_size; -}; - -inline PageBlock::PageBlock() - : m_realBase(0) - , m_base(0) - , m_size(0) -{ -} - -inline PageBlock::PageBlock(const PageBlock& other) - : m_realBase(other.m_realBase) - , m_base(other.m_base) - , m_size(other.m_size) -{ -} - -inline PageBlock::PageBlock(void* base, size_t size, bool hasGuardPages) - : m_realBase(base) - , m_base(static_cast(base) + ((base && hasGuardPages) ? pageSize() : 0)) - , m_size(size) -{ -} - -} // namespace WTF - -using WTF::pageSize; -using WTF::isPageAligned; -using WTF::isPageAligned; -using WTF::isPowerOfTwo; - -#endif // PageBlock_h diff --git a/masm/wtf/PageReservation.h b/masm/wtf/PageReservation.h deleted file mode 100644 index 77783ebcc4..0000000000 --- a/masm/wtf/PageReservation.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef PageReservation_h -#define PageReservation_h - -#include - -namespace WTF { - -/* - PageReservation - - Like PageAllocation, the PageReservation class provides a cross-platform memory - allocation interface, but with a set of capabilities more similar to that of - VirtualAlloc than posix mmap. PageReservation can be used to allocate virtual - memory without committing physical memory pages using PageReservation::reserve. - Following a call to reserve all memory in the region is in a decommited state, - in which the memory should not be used (accessing the memory may cause a fault). - - Before using memory it must be committed by calling commit, which is passed start - and size values (both of which require system page size granularity). One the - committed memory is no longer needed 'decommit' may be called to return the - memory to its devommitted state. Commit should only be called on memory that is - currently decommitted, and decommit should only be called on memory regions that - are currently committed. All memory should be decommited before the reservation - is deallocated. Values in memory may not be retained accross a pair of calls if - the region of memory is decommitted and then committed again. - - Memory protection should not be changed on decommitted memory, and if protection - is changed on memory while it is committed it should be returned to the orignal - protection before decommit is called. -*/ - -class PageReservation : private PageBlock { -public: - PageReservation() - : m_committed(0) - , m_writable(false) - , m_executable(false) - { - } - - using PageBlock::base; - using PageBlock::size; - -#ifndef __clang__ - using PageBlock::operator bool; -#else - // FIXME: This is a workaround for , wherein Clang incorrectly emits an access - // control warning when a client tries to use operator bool exposed above via "using PageBlock::operator bool". - operator bool() const { return PageBlock::operator bool(); } -#endif - - void commit(void* start, size_t size) - { - ASSERT(*this); - ASSERT(isPageAligned(start)); - ASSERT(isPageAligned(size)); - ASSERT(contains(start, size)); - - m_committed += size; - OSAllocator::commit(start, size, m_writable, m_executable); - } - - void decommit(void* start, size_t size) - { - ASSERT(*this); - ASSERT(isPageAligned(start)); - ASSERT(isPageAligned(size)); - ASSERT(contains(start, size)); - - m_committed -= size; - OSAllocator::decommit(start, size); - } - - size_t committed() - { - return m_committed; - } - - static PageReservation reserve(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) - { - ASSERT(isPageAligned(size)); - return PageReservation(OSAllocator::reserveUncommitted(size, usage, writable, executable), size, writable, executable, false); - } - - static PageReservation reserveWithGuardPages(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) - { - ASSERT(isPageAligned(size)); - return PageReservation(OSAllocator::reserveUncommitted(size + pageSize() * 2, usage, writable, executable, true), size, writable, executable, true); - } - - void deallocate() - { - ASSERT(!m_committed); - - // Clear base & size before calling release; if this is *inside* allocation - // then we won't be able to clear then after deallocating the memory. - PageReservation tmp; - std::swap(tmp, *this); - - ASSERT(tmp); - ASSERT(!*this); - - OSAllocator::releaseDecommitted(tmp.base(), tmp.size()); - } - -private: - PageReservation(void* base, size_t size, bool writable, bool executable, bool hasGuardPages) - : PageBlock(base, size, hasGuardPages) - , m_committed(0) - , m_writable(writable) - , m_executable(executable) - { - } - - size_t m_committed; - bool m_writable; - bool m_executable; -}; - -} - -using WTF::PageReservation; - -#endif // PageReservation_h diff --git a/masm/wtf/Platform.h b/masm/wtf/Platform.h deleted file mode 100644 index f2fd3b0ad5..0000000000 --- a/masm/wtf/Platform.h +++ /dev/null @@ -1,1212 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. - * Copyright (C) 2007-2009 Torch Mobile, Inc. - * Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_Platform_h -#define WTF_Platform_h - -/* Include compiler specific macros */ -#include - -/* ==== PLATFORM handles OS, operating environment, graphics API, and - CPU. This macro will be phased out in favor of platform adaptation - macros, policy decision macros, and top-level port definitions. ==== */ -#define PLATFORM(WTF_FEATURE) (defined WTF_PLATFORM_##WTF_FEATURE && WTF_PLATFORM_##WTF_FEATURE) - - -/* ==== Platform adaptation macros: these describe properties of the target environment. ==== */ - -/* CPU() - the target CPU architecture */ -#define CPU(WTF_FEATURE) (defined WTF_CPU_##WTF_FEATURE && WTF_CPU_##WTF_FEATURE) -/* HAVE() - specific system features (headers, functions or similar) that are present or not */ -#define HAVE(WTF_FEATURE) (defined HAVE_##WTF_FEATURE && HAVE_##WTF_FEATURE) -/* OS() - underlying operating system; only to be used for mandated low-level services like - virtual memory, not to choose a GUI toolkit */ -#define OS(WTF_FEATURE) (defined WTF_OS_##WTF_FEATURE && WTF_OS_##WTF_FEATURE) - - -/* ==== Policy decision macros: these define policy choices for a particular port. ==== */ - -/* USE() - use a particular third-party library or optional OS service */ -#define USE(WTF_FEATURE) (defined WTF_USE_##WTF_FEATURE && WTF_USE_##WTF_FEATURE) -/* ENABLE() - turn on a specific feature of WebKit */ -#define ENABLE(WTF_FEATURE) (defined ENABLE_##WTF_FEATURE && ENABLE_##WTF_FEATURE) - - -/* ==== CPU() - the target CPU architecture ==== */ - -/* This also defines CPU(BIG_ENDIAN) or CPU(MIDDLE_ENDIAN) or neither, as appropriate. */ - -/* CPU(ALPHA) - DEC Alpha */ -#if defined(__alpha__) -#define WTF_CPU_ALPHA 1 -#endif - -/* CPU(IA64) - Itanium / IA-64 */ -#if defined(__ia64__) -#define WTF_CPU_IA64 1 -/* 32-bit mode on Itanium */ -#if !defined(__LP64__) -#define WTF_CPU_IA64_32 1 -#endif -#endif - -/* CPU(MIPS) - MIPS 32-bit */ -/* Note: Only O32 ABI is tested, so we enable it for O32 ABI for now. */ -#if (defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_)) \ - && defined(_ABIO32) -#define WTF_CPU_MIPS 1 -#if defined(__MIPSEB__) -#define WTF_CPU_BIG_ENDIAN 1 -#endif -#define WTF_MIPS_PIC (defined __PIC__) -#define WTF_MIPS_ARCH __mips -#define WTF_MIPS_ISA(v) (defined WTF_MIPS_ARCH && WTF_MIPS_ARCH == v) -#define WTF_MIPS_ISA_AT_LEAST(v) (defined WTF_MIPS_ARCH && WTF_MIPS_ARCH >= v) -#define WTF_MIPS_ARCH_REV __mips_isa_rev -#define WTF_MIPS_ISA_REV(v) (defined WTF_MIPS_ARCH_REV && WTF_MIPS_ARCH_REV == v) -#define WTF_MIPS_DOUBLE_FLOAT (defined __mips_hard_float && !defined __mips_single_float) -#define WTF_MIPS_FP64 (defined __mips_fpr && __mips_fpr == 64) -/* MIPS requires allocators to use aligned memory */ -#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 -#endif /* MIPS */ - -/* CPU(PPC) - PowerPC 32-bit */ -#if defined(__ppc__) \ - || defined(__PPC__) \ - || defined(__powerpc__) \ - || defined(__powerpc) \ - || defined(__POWERPC__) \ - || defined(_M_PPC) \ - || defined(__PPC) -#define WTF_CPU_PPC 1 -#define WTF_CPU_BIG_ENDIAN 1 -#endif - -/* CPU(PPC64) - PowerPC 64-bit */ -#if defined(__ppc64__) \ - || defined(__PPC64__) -#define WTF_CPU_PPC64 1 -#define WTF_CPU_BIG_ENDIAN 1 -#endif - -/* CPU(SH4) - SuperH SH-4 */ -#if defined(__SH4__) -#define WTF_CPU_SH4 1 -#endif - -/* CPU(SPARC32) - SPARC 32-bit */ -#if defined(__sparc) && !defined(__arch64__) || defined(__sparcv8) -#define WTF_CPU_SPARC32 1 -#define WTF_CPU_BIG_ENDIAN 1 -#endif - -/* CPU(SPARC64) - SPARC 64-bit */ -#if defined(__sparc__) && defined(__arch64__) || defined (__sparcv9) -#define WTF_CPU_SPARC64 1 -#define WTF_CPU_BIG_ENDIAN 1 -#endif - -/* CPU(SPARC) - any SPARC, true for CPU(SPARC32) and CPU(SPARC64) */ -#if CPU(SPARC32) || CPU(SPARC64) -#define WTF_CPU_SPARC 1 -#endif - -/* CPU(S390X) - S390 64-bit */ -#if defined(__s390x__) -#define WTF_CPU_S390X 1 -#define WTF_CPU_BIG_ENDIAN 1 -#endif - -/* CPU(S390) - S390 32-bit */ -#if defined(__s390__) -#define WTF_CPU_S390 1 -#define WTF_CPU_BIG_ENDIAN 1 -#endif - -/* CPU(X86) - i386 / x86 32-bit */ -#if defined(__i386__) \ - || defined(i386) \ - || defined(_M_IX86) \ - || defined(_X86_) \ - || defined(__THW_INTEL) -#define WTF_CPU_X86 1 -#endif - -/* CPU(X86_64) - AMD64 / Intel64 / x86_64 64-bit */ -#if defined(__x86_64__) \ - || defined(_M_X64) -#define WTF_CPU_X86_64 1 -#endif - -/* CPU(ARM) - ARM, any version*/ -#if defined(arm) \ - || defined(__arm__) \ - || defined(ARM) \ - || defined(_ARM_) -#define WTF_CPU_ARM 1 - -#if defined(__ARM_PCS_VFP) -#define WTF_CPU_ARM_HARDFP 1 -#endif - -#if defined(__ARMEB__) || (COMPILER(RVCT) && defined(__BIG_ENDIAN)) -#define WTF_CPU_BIG_ENDIAN 1 - -#elif !defined(__ARM_EABI__) \ - && !defined(__EABI__) \ - && !defined(__VFP_FP__) \ - && !defined(_WIN32_WCE) \ - && !defined(ANDROID) -#define WTF_CPU_MIDDLE_ENDIAN 1 - -#endif - -#define WTF_ARM_ARCH_AT_LEAST(N) (CPU(ARM) && WTF_ARM_ARCH_VERSION >= N) - -/* Set WTF_ARM_ARCH_VERSION */ -#if defined(__ARM_ARCH_4__) \ - || defined(__ARM_ARCH_4T__) \ - || defined(__MARM_ARMV4__) \ - || defined(_ARMV4I_) -#define WTF_ARM_ARCH_VERSION 4 - -#elif defined(__ARM_ARCH_5__) \ - || defined(__ARM_ARCH_5T__) \ - || defined(__MARM_ARMV5__) -#define WTF_ARM_ARCH_VERSION 5 - -#elif defined(__ARM_ARCH_5E__) \ - || defined(__ARM_ARCH_5TE__) \ - || defined(__ARM_ARCH_5TEJ__) -#define WTF_ARM_ARCH_VERSION 5 -/*ARMv5TE requires allocators to use aligned memory*/ -#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 - -#elif defined(__ARM_ARCH_6__) \ - || defined(__ARM_ARCH_6J__) \ - || defined(__ARM_ARCH_6K__) \ - || defined(__ARM_ARCH_6Z__) \ - || defined(__ARM_ARCH_6ZK__) \ - || defined(__ARM_ARCH_6T2__) \ - || defined(__ARMV6__) -#define WTF_ARM_ARCH_VERSION 6 - -#elif defined(__ARM_ARCH_7A__) \ - || defined(__ARM_ARCH_7R__) -#define WTF_ARM_ARCH_VERSION 7 - -/* RVCT sets _TARGET_ARCH_ARM */ -#elif defined(__TARGET_ARCH_ARM) -#define WTF_ARM_ARCH_VERSION __TARGET_ARCH_ARM - -#if defined(__TARGET_ARCH_5E) \ - || defined(__TARGET_ARCH_5TE) \ - || defined(__TARGET_ARCH_5TEJ) -/*ARMv5TE requires allocators to use aligned memory*/ -#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 -#endif - -#else -#define WTF_ARM_ARCH_VERSION 0 - -#endif - -/* Set WTF_THUMB_ARCH_VERSION */ -#if defined(__ARM_ARCH_4T__) -#define WTF_THUMB_ARCH_VERSION 1 - -#elif defined(__ARM_ARCH_5T__) \ - || defined(__ARM_ARCH_5TE__) \ - || defined(__ARM_ARCH_5TEJ__) -#define WTF_THUMB_ARCH_VERSION 2 - -#elif defined(__ARM_ARCH_6J__) \ - || defined(__ARM_ARCH_6K__) \ - || defined(__ARM_ARCH_6Z__) \ - || defined(__ARM_ARCH_6ZK__) \ - || defined(__ARM_ARCH_6M__) -#define WTF_THUMB_ARCH_VERSION 3 - -#elif defined(__ARM_ARCH_6T2__) \ - || defined(__ARM_ARCH_7__) \ - || defined(__ARM_ARCH_7A__) \ - || defined(__ARM_ARCH_7R__) \ - || defined(__ARM_ARCH_7M__) -#define WTF_THUMB_ARCH_VERSION 4 - -/* RVCT sets __TARGET_ARCH_THUMB */ -#elif defined(__TARGET_ARCH_THUMB) -#define WTF_THUMB_ARCH_VERSION __TARGET_ARCH_THUMB - -#else -#define WTF_THUMB_ARCH_VERSION 0 -#endif - - -/* CPU(ARMV5_OR_LOWER) - ARM instruction set v5 or earlier */ -/* On ARMv5 and below the natural alignment is required. - And there are some other differences for v5 or earlier. */ -#if !defined(ARMV5_OR_LOWER) && !WTF_ARM_ARCH_AT_LEAST(6) -#define WTF_CPU_ARMV5_OR_LOWER 1 -#endif - - -/* CPU(ARM_TRADITIONAL) - Thumb2 is not available, only traditional ARM (v4 or greater) */ -/* CPU(ARM_THUMB2) - Thumb2 instruction set is available */ -/* Only one of these will be defined. */ -#if !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) -# if defined(thumb2) || defined(__thumb2__) \ - || ((defined(__thumb) || defined(__thumb__)) && WTF_THUMB_ARCH_VERSION == 4) -# define WTF_CPU_ARM_TRADITIONAL 0 -# define WTF_CPU_ARM_THUMB2 1 -# elif WTF_ARM_ARCH_AT_LEAST(4) -# define WTF_CPU_ARM_TRADITIONAL 1 -# define WTF_CPU_ARM_THUMB2 0 -# else -# error "Not supported ARM architecture" -# endif -#elif CPU(ARM_TRADITIONAL) && CPU(ARM_THUMB2) /* Sanity Check */ -# error "Cannot use both of WTF_CPU_ARM_TRADITIONAL and WTF_CPU_ARM_THUMB2 platforms" -#endif /* !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) */ - -#if defined(__ARM_NEON__) && !defined(WTF_CPU_ARM_NEON) -#define WTF_CPU_ARM_NEON 1 -#endif - -#if CPU(ARM_NEON) && (!COMPILER(GCC) || GCC_VERSION_AT_LEAST(4, 7, 0)) -// All NEON intrinsics usage can be disabled by this macro. -#define HAVE_ARM_NEON_INTRINSICS 1 -#endif - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) -#define WTF_CPU_ARM_VFP 1 -#endif - -#if defined(__ARM_ARCH_7S__) -#define WTF_CPU_APPLE_ARMV7S 1 -#endif - -#endif /* ARM */ - -#if CPU(ARM) || CPU(MIPS) || CPU(SH4) || CPU(SPARC) -#define WTF_CPU_NEEDS_ALIGNED_ACCESS 1 -#endif - -/* ==== OS() - underlying operating system; only to be used for mandated low-level services like - virtual memory, not to choose a GUI toolkit ==== */ - -/* OS(ANDROID) - Android */ -#ifdef ANDROID -#define WTF_OS_ANDROID 1 -#endif - -/* OS(AIX) - AIX */ -#ifdef _AIX -#define WTF_OS_AIX 1 -#endif - -/* OS(DARWIN) - Any Darwin-based OS, including Mac OS X and iPhone OS */ -#ifdef __APPLE__ -#define WTF_OS_DARWIN 1 - -#include -#include -#include -#endif - -/* OS(IOS) - iOS */ -/* OS(MAC_OS_X) - Mac OS X (not including iOS) */ -#if OS(DARWIN) && ((defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED) \ - || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) \ - || (defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR)) -#define WTF_OS_IOS 1 -#elif OS(DARWIN) && defined(TARGET_OS_MAC) && TARGET_OS_MAC -#define WTF_OS_MAC_OS_X 1 - -/* FIXME: These can be removed after sufficient time has passed since the removal of BUILDING_ON / TARGETING macros. */ - -#define ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED 0 / 0 -#define ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED 0 / 0 - -#define BUILDING_ON_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED -#define BUILDING_ON_SNOW_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED -#define BUILDING_ON_LION ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED - -#define TARGETING_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED -#define TARGETING_SNOW_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED -#define TARGETING_LION ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED -#endif - -/* OS(FREEBSD) - FreeBSD */ -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) -#define WTF_OS_FREEBSD 1 -#endif - -/* OS(HURD) - GNU/Hurd */ -#ifdef __GNU__ -#define WTF_OS_HURD 1 -#endif - -/* OS(LINUX) - Linux */ -#ifdef __linux__ -#define WTF_OS_LINUX 1 -#endif - -/* OS(NETBSD) - NetBSD */ -#if defined(__NetBSD__) -#define WTF_OS_NETBSD 1 -#endif - -/* OS(OPENBSD) - OpenBSD */ -#ifdef __OpenBSD__ -#define WTF_OS_OPENBSD 1 -#endif - -/* OS(QNX) - QNX */ -#if defined(__QNXNTO__) -#define WTF_OS_QNX 1 -#endif - -/* OS(SOLARIS) - Solaris */ -#if defined(sun) || defined(__sun) -#define WTF_OS_SOLARIS 1 -#endif - -/* OS(WINCE) - Windows CE; note that for this platform OS(WINDOWS) is also defined */ -#if defined(_WIN32_WCE) -#define WTF_OS_WINCE 1 -#endif - -/* OS(WINDOWS) - Any version of Windows */ -#if defined(WIN32) || defined(_WIN32) -#define WTF_OS_WINDOWS 1 -#endif - -#define WTF_OS_WIN ERROR "USE WINDOWS WITH OS NOT WIN" -#define WTF_OS_MAC ERROR "USE MAC_OS_X WITH OS NOT MAC" - -/* OS(UNIX) - Any Unix-like system */ -#if OS(AIX) \ - || OS(ANDROID) \ - || OS(DARWIN) \ - || OS(FREEBSD) \ - || OS(HURD) \ - || OS(LINUX) \ - || OS(NETBSD) \ - || OS(OPENBSD) \ - || OS(QNX) \ - || OS(SOLARIS) \ - || defined(unix) \ - || defined(__unix) \ - || defined(__unix__) -#define WTF_OS_UNIX 1 -#endif - -/* Operating environments */ - -/* FIXME: these are all mixes of OS, operating environment and policy choices. */ -/* PLATFORM(CHROMIUM) */ -/* PLATFORM(QT) */ -/* PLATFORM(WX) */ -/* PLATFORM(EFL) */ -/* PLATFORM(GTK) */ -/* PLATFORM(BLACKBERRY) */ -/* PLATFORM(MAC) */ -/* PLATFORM(WIN) */ -#if defined(BUILDING_CHROMIUM__) -#define WTF_PLATFORM_CHROMIUM 1 -#elif defined(BUILDING_QT__) -#define WTF_PLATFORM_QT 1 -#elif defined(BUILDING_WX__) -#define WTF_PLATFORM_WX 1 -#elif defined(BUILDING_EFL__) -#define WTF_PLATFORM_EFL 1 -#elif defined(BUILDING_GTK__) -#define WTF_PLATFORM_GTK 1 -#elif defined(BUILDING_BLACKBERRY__) -#define WTF_PLATFORM_BLACKBERRY 1 -#elif OS(DARWIN) -#define WTF_PLATFORM_MAC 1 -#elif OS(WINDOWS) -#define WTF_PLATFORM_WIN 1 -#endif - -/* PLATFORM(IOS) */ -/* FIXME: this is sometimes used as an OS switch and sometimes for higher-level things */ -#if (defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) -#define WTF_PLATFORM_IOS 1 -#endif - -/* PLATFORM(IOS_SIMULATOR) */ -#if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR -#define WTF_PLATFORM_IOS 1 -#define WTF_PLATFORM_IOS_SIMULATOR 1 -#endif - -/* Graphics engines */ - -/* USE(CG) and PLATFORM(CI) */ -#if PLATFORM(MAC) || PLATFORM(IOS) -#define WTF_USE_CG 1 -#endif -#if PLATFORM(MAC) || PLATFORM(IOS) || (PLATFORM(WIN) && USE(CG)) -#define WTF_USE_CA 1 -#endif - -/* USE(SKIA) for Win/Linux/Mac/Android */ -#if PLATFORM(CHROMIUM) -#if OS(DARWIN) -#define WTF_USE_SKIA 1 -#define WTF_USE_ICCJPEG 1 -#define WTF_USE_QCMSLIB 1 -#elif OS(ANDROID) -#define WTF_USE_SKIA 1 -#define WTF_USE_LOW_QUALITY_IMAGE_INTERPOLATION 1 -#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_DITHERING 1 -#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_FANCY_UPSAMPLING 1 -#else -#define WTF_USE_SKIA 1 -#define WTF_USE_ICCJPEG 1 -#define WTF_USE_QCMSLIB 1 -#endif -#endif - -#if OS(QNX) -#define USE_SYSTEM_MALLOC 1 -#endif - -#if PLATFORM(BLACKBERRY) -#define WTF_USE_MERSENNE_TWISTER_19937 1 -#define WTF_USE_SKIA 1 -#define WTF_USE_LOW_QUALITY_IMAGE_INTERPOLATION 1 -#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_DITHERING 1 -#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_FANCY_UPSAMPLING 1 -#endif - -#if PLATFORM(GTK) -#define WTF_USE_CAIRO 1 -#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 -#endif - - -#if OS(WINCE) -#define WTF_USE_MERSENNE_TWISTER_19937 1 -#endif - -/* On Windows, use QueryPerformanceCounter by default */ -#if OS(WINDOWS) -#define WTF_USE_QUERY_PERFORMANCE_COUNTER 1 -#endif - -#if OS(WINCE) && !PLATFORM(QT) -#define NOSHLWAPI /* shlwapi.h not available on WinCe */ - -/* MSDN documentation says these functions are provided with uspce.lib. But we cannot find this file. */ -#define __usp10__ /* disable "usp10.h" */ - -#define _INC_ASSERT /* disable "assert.h" */ -#define assert(x) - -#endif /* OS(WINCE) && !PLATFORM(QT) */ - -#if OS(WINCE) && !PLATFORM(QT) -#define WTF_USE_WCHAR_UNICODE 1 -#elif PLATFORM(GTK) -/* The GTK+ Unicode backend is configurable */ -#else -#define WTF_USE_ICU_UNICODE 1 -#endif - -#if PLATFORM(MAC) && !PLATFORM(IOS) -#if CPU(X86_64) -#define WTF_USE_PLUGIN_HOST_PROCESS 1 -#endif -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 -#define ENABLE_GESTURE_EVENTS 1 -#define ENABLE_RUBBER_BANDING 1 -#define WTF_USE_SCROLLBAR_PAINTER 1 -#define HAVE_XPC 1 -#endif -#if !defined(ENABLE_DASHBOARD_SUPPORT) -#define ENABLE_DASHBOARD_SUPPORT 1 -#endif -#define WTF_USE_CF 1 -#define WTF_USE_PTHREADS 1 -#define HAVE_READLINE 1 -#define HAVE_RUNLOOP_TIMER 1 -#define ENABLE_FULLSCREEN_API 1 -#define ENABLE_SMOOTH_SCROLLING 1 -#define ENABLE_WEB_ARCHIVE 1 -#define ENABLE_WEB_AUDIO 1 -#if defined(ENABLE_VIDEO) -#define ENABLE_VIDEO_TRACK 1 -#endif -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 -#define HAVE_LAYER_HOSTING_IN_WINDOW_SERVER 1 -#endif -#define WTF_USE_APPKIT 1 -#define WTF_USE_SECURITY_FRAMEWORK 1 -#endif /* PLATFORM(MAC) && !PLATFORM(IOS) */ - -#if PLATFORM(CHROMIUM) && OS(DARWIN) -#define WTF_USE_CF 1 -#define WTF_USE_PTHREADS 1 -#define WTF_USE_WK_SCROLLBAR_PAINTER 1 -#endif - -#if PLATFORM(CHROMIUM) -#if OS(DARWIN) -/* We can't override the global operator new and delete on OS(DARWIN) because - * some object are allocated by WebKit and deallocated by the embedder. */ -#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 -#else /* !OS(DARWIN) */ -/* On non-OS(DARWIN), the "system malloc" is actually TCMalloc anyway, so there's - * no need to use WebKit's copy of TCMalloc. */ -#define USE_SYSTEM_MALLOC 1 -#endif /* OS(DARWIN) */ -#endif /* PLATFORM(CHROMIUM) */ - -#if PLATFORM(IOS) -#define DONT_FINALIZE_ON_MAIN_THREAD 1 -#endif - -#if PLATFORM(QT) && OS(DARWIN) -#define WTF_USE_CF 1 -#endif - -#if OS(DARWIN) && !PLATFORM(GTK) && !PLATFORM(QT) -#define ENABLE_PURGEABLE_MEMORY 1 -#endif - -#if PLATFORM(IOS) -#define ENABLE_CONTEXT_MENUS 0 -#define ENABLE_DRAG_SUPPORT 0 -#define ENABLE_GEOLOCATION 1 -#define ENABLE_ICONDATABASE 0 -#define ENABLE_INSPECTOR 1 -#define ENABLE_NETSCAPE_PLUGIN_API 0 -#define ENABLE_ORIENTATION_EVENTS 1 -#define ENABLE_REPAINT_THROTTLING 1 -#define ENABLE_WEB_ARCHIVE 1 -#define HAVE_READLINE 1 -#define WTF_USE_CF 1 -#define WTF_USE_CFNETWORK 1 -#define WTF_USE_NETWORK_CFDATA_ARRAY_CALLBACK 1 -#define WTF_USE_PTHREADS 1 - -#if PLATFORM(IOS_SIMULATOR) - #define ENABLE_JIT 0 - #define ENABLE_YARR_JIT 0 -#else - #define ENABLE_JIT 1 - #define ENABLE_LLINT 1 - #define ENABLE_YARR_JIT 1 -#endif - -#define WTF_USE_APPKIT 0 -#define WTF_USE_SECURITY_FRAMEWORK 0 -#endif - -#if PLATFORM(WIN) && !OS(WINCE) -#define WTF_USE_CF 1 -#endif - -#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(WIN_CAIRO) -#define WTF_USE_CFNETWORK 1 -#endif - -#if USE(CFNETWORK) || PLATFORM(MAC) || PLATFORM(IOS) -#define WTF_USE_CFURLCACHE 1 -#endif - -#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(QT) -#define ENABLE_WEB_ARCHIVE 1 -#endif - -#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(WIN_CAIRO) && !PLATFORM(QT) -#define ENABLE_FULLSCREEN_API 1 -#endif - -#if PLATFORM(WX) -#if !CPU(PPC) -#if !defined(ENABLE_ASSEMBLER) -#define ENABLE_ASSEMBLER 1 -#endif -#define ENABLE_JIT 1 -#endif -#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 -#define ENABLE_LLINT 0 -#if OS(DARWIN) -#define WTF_USE_CF 1 -#define ENABLE_WEB_ARCHIVE 1 -#endif -#endif - -#if OS(UNIX) && (PLATFORM(GTK) || PLATFORM(QT)) -#define WTF_USE_PTHREADS 1 -#endif - -#if !defined(HAVE_ACCESSIBILITY) -#if PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && !OS(ANDROID)) || PLATFORM(EFL) -#define HAVE_ACCESSIBILITY 1 -#endif -#endif /* !defined(HAVE_ACCESSIBILITY) */ - -#if OS(UNIX) -#define HAVE_SIGNAL_H 1 -#define WTF_USE_OS_RANDOMNESS 1 -#endif - -#if (OS(FREEBSD) || OS(OPENBSD)) && !defined(__GLIBC__) -#define HAVE_PTHREAD_NP_H 1 -#endif - -#if !defined(HAVE_VASPRINTF) -#if !COMPILER(MSVC) && !COMPILER(RVCT) && !COMPILER(MINGW) && !(COMPILER(GCC) && OS(QNX)) -#define HAVE_VASPRINTF 1 -#endif -#endif - -#if !defined(HAVE_STRNSTR) -#if OS(DARWIN) || (OS(FREEBSD) && !defined(__GLIBC__)) -#define HAVE_STRNSTR 1 -#endif -#endif - -#if !OS(WINDOWS) && !OS(SOLARIS) \ - && !OS(RVCT) \ - && !OS(ANDROID) -#define HAVE_TM_GMTOFF 1 -#define HAVE_TM_ZONE 1 -#define HAVE_TIMEGM 1 -#endif - -#if OS(DARWIN) - -#define HAVE_ERRNO_H 1 -#define HAVE_LANGINFO_H 1 -#define HAVE_MMAP 1 -#define HAVE_MERGESORT 1 -#define HAVE_STRINGS_H 1 -#define HAVE_SYS_PARAM_H 1 -#define HAVE_SYS_TIME_H 1 -#define HAVE_SYS_TIMEB_H 1 -#define WTF_USE_ACCELERATE 1 - -#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 - -#define HAVE_DISPATCH_H 1 -#define HAVE_HOSTED_CORE_ANIMATION 1 - -#if !PLATFORM(IOS) -#define HAVE_MADV_FREE_REUSE 1 -#define HAVE_MADV_FREE 1 -#define HAVE_PTHREAD_SETNAME_NP 1 -#endif - -#endif - -#if PLATFORM(IOS) -#define HAVE_MADV_FREE 1 -#define HAVE_PTHREAD_SETNAME_NP 1 -#endif - -#elif OS(WINDOWS) - -#if !OS(WINCE) -#define HAVE_SYS_TIMEB_H 1 -#define HAVE_ALIGNED_MALLOC 1 -#define HAVE_ISDEBUGGERPRESENT 1 -#endif -#define HAVE_VIRTUALALLOC 1 -#define WTF_USE_OS_RANDOMNESS 1 - -#elif OS(QNX) - -#define HAVE_ERRNO_H 1 -#define HAVE_MMAP 1 -#define HAVE_MADV_FREE_REUSE 1 -#define HAVE_MADV_FREE 1 -#define HAVE_STRINGS_H 1 -#define HAVE_SYS_PARAM_H 1 -#define HAVE_SYS_TIME_H 1 -#define WTF_USE_PTHREADS 1 - -#elif OS(ANDROID) - -#define HAVE_ERRNO_H 1 -#define HAVE_NMAP 1 -#define HAVE_STRINGS_H 1 -#define HAVE_SYS_PARAM_H 1 -#define HAVE_SYS_TIME_H 1 - -#else - -/* FIXME: is this actually used or do other platforms generate their own config.h? */ - -#define HAVE_ERRNO_H 1 -#define HAVE_LANGINFO_H 1 -#define HAVE_MMAP 1 -#define HAVE_STRINGS_H 1 -#define HAVE_SYS_PARAM_H 1 -#define HAVE_SYS_TIME_H 1 - -#endif - -/* ENABLE macro defaults */ - -#if PLATFORM(QT) -/* We must not customize the global operator new and delete for the Qt port. */ -#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 -#if !OS(UNIX) -#define USE_SYSTEM_MALLOC 1 -#endif -#endif - -#if !defined(ENABLE_ICONDATABASE) -#define ENABLE_ICONDATABASE 1 -#endif - -#if !defined(ENABLE_SQL_DATABASE) -#define ENABLE_SQL_DATABASE 1 -#endif - -#if !defined(ENABLE_JAVASCRIPT_DEBUGGER) -#define ENABLE_JAVASCRIPT_DEBUGGER 1 -#endif - -#if !defined(ENABLE_FTPDIR) -#define ENABLE_FTPDIR 1 -#endif - -#if !defined(ENABLE_CONTEXT_MENUS) -#define ENABLE_CONTEXT_MENUS 1 -#endif - -#if !defined(ENABLE_DRAG_SUPPORT) -#define ENABLE_DRAG_SUPPORT 1 -#endif - -#if !defined(ENABLE_INSPECTOR) -#define ENABLE_INSPECTOR 1 -#endif - -#if !defined(ENABLE_NETSCAPE_PLUGIN_API) -#define ENABLE_NETSCAPE_PLUGIN_API 1 -#endif - -#if !defined(ENABLE_GLOBAL_FASTMALLOC_NEW) -#define ENABLE_GLOBAL_FASTMALLOC_NEW 1 -#endif - -#if !defined(ENABLE_PARSED_STYLE_SHEET_CACHING) -#define ENABLE_PARSED_STYLE_SHEET_CACHING 1 -#endif - -#if !defined(ENABLE_SUBPIXEL_LAYOUT) -#if PLATFORM(CHROMIUM) -#define ENABLE_SUBPIXEL_LAYOUT 1 -#else -#define ENABLE_SUBPIXEL_LAYOUT 0 -#endif -#endif - -#if !defined(ENABLE_SATURATED_LAYOUT_ARITHMETIC) -#define ENABLE_SATURATED_LAYOUT_ARITHMETIC 0 -#endif - -#if ENABLE(ENABLE_SATURATED_LAYOUT_ARITHMETIC) && !ENABLE(ENABLE_SUBPIXEL_LAYOUT) -#error "ENABLE_SATURATED_LAYOUT_ARITHMETIC requires ENABLE_SUBPIXEL_LAYOUT" -#endif - -#if ENABLE(INPUT_TYPE_DATE) || ENABLE(INPUT_TYPE_DATETIME) || ENABLE(INPUT_TYPE_DATETIMELOCAL) || ENABLE(INPUT_TYPE_MONTH) || ENABLE(INPUT_TYPE_TIME) || ENABLE(INPUT_TYPE_WEEK) -#define ENABLE_DATE_AND_TIME_INPUT_TYPES 1 -#endif - -#define ENABLE_DEBUG_WITH_BREAKPOINT 0 -#define ENABLE_SAMPLING_COUNTERS 0 -#define ENABLE_SAMPLING_FLAGS 0 -#define ENABLE_SAMPLING_REGIONS 0 -#define ENABLE_OPCODE_SAMPLING 0 -#define ENABLE_CODEBLOCK_SAMPLING 0 -#if ENABLE(CODEBLOCK_SAMPLING) && !ENABLE(OPCODE_SAMPLING) -#error "CODEBLOCK_SAMPLING requires OPCODE_SAMPLING" -#endif -#if ENABLE(OPCODE_SAMPLING) || ENABLE(SAMPLING_FLAGS) || ENABLE(SAMPLING_REGIONS) -#define ENABLE_SAMPLING_THREAD 1 -#endif - -#if !defined(ENABLE_TEXT_CARET) && !PLATFORM(IOS) -#define ENABLE_TEXT_CARET 1 -#endif - -#if !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32_64) -#if (CPU(X86_64) && (OS(UNIX) || OS(WINDOWS))) \ - || (CPU(IA64) && !CPU(IA64_32)) \ - || CPU(ALPHA) \ - || CPU(SPARC64) \ - || CPU(S390X) \ - || CPU(PPC64) -#define WTF_USE_JSVALUE64 1 -#else -#define WTF_USE_JSVALUE32_64 1 -#endif -#endif /* !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32_64) */ - -/* Disable the JIT on versions of GCC prior to 4.1 */ -#if !defined(ENABLE_JIT) && COMPILER(GCC) && !GCC_VERSION_AT_LEAST(4, 1, 0) -#define ENABLE_JIT 0 -#endif - -/* JIT is not implemented for Windows 64-bit */ -#if !defined(ENABLE_JIT) && OS(WINDOWS) && CPU(X86_64) -#define ENABLE_JIT 0 -#define ENABLE_YARR_JIT 0 -#endif - -#if !defined(ENABLE_JIT) && CPU(SH4) && PLATFORM(QT) -#define ENABLE_JIT 1 -#endif - -/* The JIT is enabled by default on all x86, x86-64, ARM & MIPS platforms. */ -#if !defined(ENABLE_JIT) \ - && (CPU(X86) || CPU(X86_64) || CPU(ARM) || CPU(MIPS)) \ - && (OS(DARWIN) || !COMPILER(GCC) || GCC_VERSION_AT_LEAST(4, 1, 0)) \ - && !OS(WINCE) \ - && !(OS(QNX) && !PLATFORM(QT)) /* We use JIT in QNX Qt */ -#define ENABLE_JIT 1 -#endif - -/* If possible, try to enable a disassembler. This is optional. We proceed in two - steps: first we try to find some disassembler that we can use, and then we - decide if the high-level disassembler API can be enabled. */ -#if !defined(WTF_USE_UDIS86) && ENABLE(JIT) && (PLATFORM(MAC) || (PLATFORM(QT) && OS(LINUX))) \ - && (CPU(X86) || CPU(X86_64)) -#define WTF_USE_UDIS86 1 -#endif - -#if !defined(ENABLE_DISASSEMBLER) && USE(UDIS86) -#define ENABLE_DISASSEMBLER 1 -#endif - -/* On the GTK+ port we take an extra precaution for LLINT support: - * We disable it on x86 builds if the build target doesn't support SSE2 - * instructions (LLINT requires SSE2 on this platform). */ -#if !defined(ENABLE_LLINT) && PLATFORM(GTK) && CPU(X86) && COMPILER(GCC) \ - && !defined(__SSE2__) -#define ENABLE_LLINT 0 -#endif - -/* On some of the platforms where we have a JIT, we want to also have the - low-level interpreter. */ -#if !defined(ENABLE_LLINT) \ - && ENABLE(JIT) \ - && (OS(DARWIN) || OS(LINUX)) \ - && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(QT)) \ - && (CPU(X86) || CPU(X86_64) || CPU(ARM_THUMB2)) -#define ENABLE_LLINT 1 -#endif - -#if !defined(ENABLE_DFG_JIT) && ENABLE(JIT) && !COMPILER(MSVC) -/* Enable the DFG JIT on X86 and X86_64. Only tested on Mac and GNU/Linux. */ -#if (CPU(X86) || CPU(X86_64)) && (PLATFORM(MAC) || OS(LINUX)) -#define ENABLE_DFG_JIT 1 -#endif -/* Enable the DFG JIT on ARMv7. Only tested on iOS and Qt Linux. */ -#if CPU(ARM_THUMB2) && (PLATFORM(IOS) || PLATFORM(BLACKBERRY) || PLATFORM(QT)) -#define ENABLE_DFG_JIT 1 -#endif -/* Enable the DFG JIT on ARM. */ -#if CPU(ARM_TRADITIONAL) -#define ENABLE_DFG_JIT 1 -#endif -#endif - -/* If the jit is not available, enable the LLInt C Loop: */ -#if !ENABLE(JIT) -#undef ENABLE_LLINT /* Undef so that we can redefine it. */ -#undef ENABLE_LLINT_C_LOOP /* Undef so that we can redefine it. */ -#undef ENABLE_DFG_JIT /* Undef so that we can redefine it. */ -#define ENABLE_LLINT 1 -#define ENABLE_LLINT_C_LOOP 1 -#define ENABLE_DFG_JIT 0 -#endif - -/* Do a sanity check to make sure that we at least have one execution engine in - use: */ -#if !(ENABLE(JIT) || ENABLE(LLINT)) -#error You have to have at least one execution model enabled to build JSC -#endif - -/* Profiling of types and values used by JIT code. DFG_JIT depends on it, but you - can enable it manually with DFG turned off if you want to use it as a standalone - profiler. In that case, you probably want to also enable VERBOSE_VALUE_PROFILE - below. */ -#if !defined(ENABLE_VALUE_PROFILER) && ENABLE(DFG_JIT) -#define ENABLE_VALUE_PROFILER 1 -#endif - -#if !defined(ENABLE_VERBOSE_VALUE_PROFILE) && ENABLE(VALUE_PROFILER) -#define ENABLE_VERBOSE_VALUE_PROFILE 0 -#endif - -#if !defined(ENABLE_SIMPLE_HEAP_PROFILING) -#define ENABLE_SIMPLE_HEAP_PROFILING 0 -#endif - -/* Counts uses of write barriers using sampling counters. Be sure to also - set ENABLE_SAMPLING_COUNTERS to 1. */ -#if !defined(ENABLE_WRITE_BARRIER_PROFILING) -#define ENABLE_WRITE_BARRIER_PROFILING 0 -#endif - -/* Configure the JIT */ -#if CPU(X86) && COMPILER(MSVC) -#define JSC_HOST_CALL __fastcall -#elif CPU(X86) && COMPILER(GCC) -#define JSC_HOST_CALL __attribute__ ((fastcall)) -#else -#define JSC_HOST_CALL -#endif - -/* Configure the interpreter */ -#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(4, 0, 0, 0) && defined(__GNUC__)) -#define HAVE_COMPUTED_GOTO 1 -#endif - -/* Determine if we need to enable Computed Goto Opcodes or not: */ -#if HAVE(COMPUTED_GOTO) && ENABLE(LLINT) -#define ENABLE_COMPUTED_GOTO_OPCODES 1 -#endif - -/* Regular Expression Tracing - Set to 1 to trace RegExp's in jsc. Results dumped at exit */ -#define ENABLE_REGEXP_TRACING 0 - -/* Yet Another Regex Runtime - turned on by default for JIT enabled ports. */ -#if !defined(ENABLE_YARR_JIT) && (ENABLE(JIT) || ENABLE(LLINT_C_LOOP)) && !PLATFORM(CHROMIUM) && !(OS(QNX) && PLATFORM(QT)) -#define ENABLE_YARR_JIT 1 - -/* Setting this flag compares JIT results with interpreter results. */ -#define ENABLE_YARR_JIT_DEBUG 0 -#endif - -/* If either the JIT or the RegExp JIT is enabled, then the Assembler must be - enabled as well: */ -#if ENABLE(JIT) || ENABLE(YARR_JIT) -#if defined(ENABLE_ASSEMBLER) && !ENABLE_ASSEMBLER -#error "Cannot enable the JIT or RegExp JIT without enabling the Assembler" -#else -#undef ENABLE_ASSEMBLER -#define ENABLE_ASSEMBLER 1 -#endif -#endif - -/* Pick which allocator to use; we only need an executable allocator if the assembler is compiled in. - On x86-64 we use a single fixed mmap, on other platforms we mmap on demand. */ -#if ENABLE(ASSEMBLER) -#if CPU(X86_64) || PLATFORM(IOS) -#define ENABLE_EXECUTABLE_ALLOCATOR_FIXED 1 -#else -#define ENABLE_EXECUTABLE_ALLOCATOR_DEMAND 1 -#endif -#endif - -#if !defined(ENABLE_PAN_SCROLLING) && OS(WINDOWS) -#define ENABLE_PAN_SCROLLING 1 -#endif - -/*Add other platforms as they update their platfrom specific code to handle TextRun's with 8 bit data. */ -#if PLATFORM(MAC) -#define ENABLE_8BIT_TEXTRUN 1 -#endif - -/* Use the QXmlStreamReader implementation for XMLDocumentParser */ -/* Use the QXmlQuery implementation for XSLTProcessor */ -#if PLATFORM(QT) -#if !USE(LIBXML2) -#define WTF_USE_QXMLSTREAM 1 -#define WTF_USE_QXMLQUERY 1 -#endif -#endif - -/* Accelerated compositing */ -#if PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(QT) || (PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(WIN_CAIRO)) -#define WTF_USE_ACCELERATED_COMPOSITING 1 -#endif - -#if PLATFORM(MAC) || PLATFORM(IOS) -#define ENABLE_CSS_IMAGE_SET 1 -#endif - -#if ENABLE(WEBGL) && !defined(WTF_USE_3D_GRAPHICS) -#define WTF_USE_3D_GRAPHICS 1 -#endif - -/* Qt always uses Texture Mapper */ -#if PLATFORM(QT) -#define WTF_USE_TEXTURE_MAPPER 1 -#endif - -#if USE(TEXTURE_MAPPER) && USE(3D_GRAPHICS) && !defined(WTF_USE_TEXTURE_MAPPER_GL) -#define WTF_USE_TEXTURE_MAPPER_GL 1 -#endif - -/* Compositing on the UI-process in WebKit2 */ -#if PLATFORM(QT) -#define WTF_USE_COORDINATED_GRAPHICS 1 -#endif - -#if PLATFORM(MAC) || PLATFORM(IOS) -#define WTF_USE_PROTECTION_SPACE_AUTH_CALLBACK 1 -#endif - -#if !ENABLE(NETSCAPE_PLUGIN_API) || (ENABLE(NETSCAPE_PLUGIN_API) && ((OS(UNIX) && (PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(WX))) || PLATFORM(EFL))) -#define ENABLE_PLUGIN_PACKAGE_SIMPLE_HASH 1 -#endif - -#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 -#define ENABLE_THREADED_SCROLLING 1 -#endif - -/* Set up a define for a common error that is intended to cause a build error -- thus the space after Error. */ -#define WTF_PLATFORM_CFNETWORK Error USE_macro_should_be_used_with_CFNETWORK - -/* FIXME: Eventually we should enable this for all platforms and get rid of the define. */ -#if PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL) -#define WTF_USE_PLATFORM_STRATEGIES 1 -#endif - -#if PLATFORM(WIN) -#define WTF_USE_CROSS_PLATFORM_CONTEXT_MENUS 1 -#endif - -#if PLATFORM(MAC) && HAVE(ACCESSIBILITY) -#define WTF_USE_ACCESSIBILITY_CONTEXT_MENUS 1 -#endif - -#if CPU(ARM_THUMB2) -#define ENABLE_BRANCH_COMPACTION 1 -#endif - -#if !defined(ENABLE_THREADING_LIBDISPATCH) && HAVE(DISPATCH_H) -#define ENABLE_THREADING_LIBDISPATCH 1 -#elif !defined(ENABLE_THREADING_OPENMP) && defined(_OPENMP) -#define ENABLE_THREADING_OPENMP 1 -#elif !defined(THREADING_GENERIC) -#define ENABLE_THREADING_GENERIC 1 -#endif - -#if ENABLE(GLIB_SUPPORT) -#include -#endif - -/* FIXME: This define won't be needed once #27551 is fully landed. However, - since most ports try to support sub-project independence, adding new headers - to WTF causes many ports to break, and so this way we can address the build - breakages one port at a time. */ -#if !defined(WTF_USE_EXPORT_MACROS) && (PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(WX)) -#define WTF_USE_EXPORT_MACROS 1 -#endif - -#if !defined(WTF_USE_EXPORT_MACROS_FOR_TESTING) && (PLATFORM(GTK) || PLATFORM(WIN)) -#define WTF_USE_EXPORT_MACROS_FOR_TESTING 1 -#endif - -#if (PLATFORM(QT) && !OS(DARWIN) && !OS(WINDOWS)) || PLATFORM(GTK) || PLATFORM(EFL) -#define WTF_USE_UNIX_DOMAIN_SOCKETS 1 -#endif - -#if !defined(ENABLE_COMPARE_AND_SWAP) && (OS(WINDOWS) || (COMPILER(GCC) && (CPU(X86) || CPU(X86_64) || CPU(ARM_THUMB2)))) -#define ENABLE_COMPARE_AND_SWAP 1 -#endif - -#define ENABLE_OBJECT_MARK_LOGGING 0 - -#if !defined(ENABLE_PARALLEL_GC) && !ENABLE(OBJECT_MARK_LOGGING) && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(BLACKBERRY) || PLATFORM(GTK)) && ENABLE(COMPARE_AND_SWAP) -#define ENABLE_PARALLEL_GC 1 -#elif PLATFORM(QT) -// Parallel GC is temporarily disabled on Qt because of regular crashes, see https://bugs.webkit.org/show_bug.cgi?id=90957 for details -#define ENABLE_PARALLEL_GC 0 -#endif - -#if !defined(ENABLE_GC_VALIDATION) && !defined(NDEBUG) -#define ENABLE_GC_VALIDATION 1 -#endif - -#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 -#define WTF_USE_AVFOUNDATION 1 -#endif - -#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 -#define WTF_USE_COREMEDIA 1 -#endif - -#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 -#define HAVE_AVFOUNDATION_TEXT_TRACK_SUPPORT 1 -#endif - -#if PLATFORM(MAC) || PLATFORM(GTK) || PLATFORM(EFL) || (PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(WIN_CAIRO)) || PLATFORM(BLACKBERRY) -#define WTF_USE_REQUEST_ANIMATION_FRAME_TIMER 1 -#endif - -#if PLATFORM(MAC) || PLATFORM(BLACKBERRY) -#define WTF_USE_REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR 1 -#endif - -#if PLATFORM(MAC) && (PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) -#define HAVE_INVERTED_WHEEL_EVENTS 1 -#endif - -#if PLATFORM(MAC) -#define WTF_USE_COREAUDIO 1 -#endif - -#if !defined(WTF_USE_V8) && PLATFORM(CHROMIUM) -#define WTF_USE_V8 1 -#endif - -/* Not using V8 implies using JSC and vice versa */ -#if !USE(V8) -#define WTF_USE_JSC 1 -#endif - -#if ENABLE(NOTIFICATIONS) && PLATFORM(MAC) -#define ENABLE_TEXT_NOTIFICATIONS_ONLY 1 -#endif - -#if !defined(WTF_USE_ZLIB) && !PLATFORM(QT) -#define WTF_USE_ZLIB 1 -#endif - -#if PLATFORM(QT) -#include -#if defined(QT_OPENGL_ES_2) && !defined(WTF_USE_OPENGL_ES_2) -#define WTF_USE_OPENGL_ES_2 1 -#endif -#endif - -#endif /* WTF_Platform_h */ diff --git a/masm/wtf/PossiblyNull.h b/masm/wtf/PossiblyNull.h deleted file mode 100644 index 46a7d713be..0000000000 --- a/masm/wtf/PossiblyNull.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef PossiblyNull_h -#define PossiblyNull_h - -#include - -namespace WTF { - -template struct PossiblyNull { - PossiblyNull(T data) - : m_data(data) - { - } - PossiblyNull(const PossiblyNull& source) - : m_data(source.m_data) - { - source.m_data = 0; - } - ~PossiblyNull() { ASSERT(!m_data); } - bool getValue(T& out) WARN_UNUSED_RETURN; -private: - mutable T m_data; -}; - -template bool PossiblyNull::getValue(T& out) -{ - out = m_data; - bool result = !!m_data; - m_data = 0; - return result; -} - -} - -#endif diff --git a/masm/wtf/PrintStream.cpp b/masm/wtf/PrintStream.cpp deleted file mode 100644 index 7dd4060971..0000000000 --- a/masm/wtf/PrintStream.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "PrintStream.h" - -#include - -namespace WTF { - -PrintStream::PrintStream() { } -PrintStream::~PrintStream() { } // Force the vtable to be in this module - -void PrintStream::printf(const char* format, ...) -{ - va_list argList; - va_start(argList, format); - vprintf(format, argList); - va_end(argList); -} - -void PrintStream::flush() -{ -} - -void printInternal(PrintStream& out, const char* string) -{ - out.printf("%s", string); -} - -void printInternal(PrintStream& out, bool value) -{ - if (value) - out.print("true"); - else - out.print("false"); -} - -void printInternal(PrintStream& out, int value) -{ - out.printf("%d", value); -} - -void printInternal(PrintStream& out, unsigned value) -{ - out.printf("%u", value); -} - -void printInternal(PrintStream& out, long value) -{ - out.printf("%ld", value); -} - -void printInternal(PrintStream& out, unsigned long value) -{ - out.printf("%lu", value); -} - -void printInternal(PrintStream& out, long long value) -{ - out.printf("%lld", value); -} - -void printInternal(PrintStream& out, unsigned long long value) -{ - out.printf("%llu", value); -} - -void printInternal(PrintStream& out, float value) -{ - out.print(static_cast(value)); -} - -void printInternal(PrintStream& out, double value) -{ - out.printf("%lf", value); -} - -void printInternal(PrintStream& out, RawPointer value) -{ - out.printf("%p", value.value()); -} - -void dumpCharacter(PrintStream& out, char value) -{ - out.printf("%c", value); -} - -} // namespace WTF - diff --git a/masm/wtf/PrintStream.h b/masm/wtf/PrintStream.h deleted file mode 100644 index 4134dcf182..0000000000 --- a/masm/wtf/PrintStream.h +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef PrintStream_h -#define PrintStream_h - -#include -#include -#include -#include -#include -#include - -namespace WTF { - -class CString; -class String; - -class PrintStream { - WTF_MAKE_FAST_ALLOCATED; WTF_MAKE_NONCOPYABLE(PrintStream); -public: - PrintStream(); - virtual ~PrintStream(); - - void printf(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); - virtual void vprintf(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(2, 0) = 0; - - // Typically a no-op for many subclasses of PrintStream, this is a hint that - // the implementation should flush its buffers if it had not done so already. - virtual void flush(); - - template - void print(const T& value) - { - printInternal(*this, value); - } - - template - void print(const T1& value1, const T2& value2) - { - print(value1); - print(value2); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3) - { - print(value1); - print(value2); - print(value3); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4) - { - print(value1); - print(value2); - print(value3); - print(value4); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - print(value10); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - print(value10); - print(value11); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - print(value10); - print(value11); - print(value12); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12, const T13& value13) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - print(value10); - print(value11); - print(value12); - print(value13); - } -}; - -void printInternal(PrintStream&, const char*); -void printInternal(PrintStream&, const CString&); -void printInternal(PrintStream&, const String&); -inline void printInternal(PrintStream& out, char* value) { printInternal(out, static_cast(value)); } -inline void printInternal(PrintStream& out, CString& value) { printInternal(out, static_cast(value)); } -inline void printInternal(PrintStream& out, String& value) { printInternal(out, static_cast(value)); } -void printInternal(PrintStream&, bool); -void printInternal(PrintStream&, int); -void printInternal(PrintStream&, unsigned); -void printInternal(PrintStream&, long); -void printInternal(PrintStream&, unsigned long); -void printInternal(PrintStream&, long long); -void printInternal(PrintStream&, unsigned long long); -void printInternal(PrintStream&, float); -void printInternal(PrintStream&, double); -void printInternal(PrintStream&, RawPointer); - -template -void printInternal(PrintStream& out, const T& value) -{ - value.dump(out); -} - -#define MAKE_PRINT_ADAPTOR(Name, Type, function) \ - class Name { \ - public: \ - Name(const Type& value) \ - : m_value(value) \ - { \ - } \ - void dump(PrintStream& out) const \ - { \ - function(out, m_value); \ - } \ - private: \ - Type m_value; \ - } - -#define MAKE_PRINT_METHOD_ADAPTOR(Name, Type, method) \ - class Name { \ - public: \ - Name(const Type& value) \ - : m_value(value) \ - { \ - } \ - void dump(PrintStream& out) const \ - { \ - m_value.method(out); \ - } \ - private: \ - Type m_value; \ - } - -// Use an adaptor-based dumper for characters to avoid situations where -// you've "compressed" an integer to a character and it ends up printing -// as ASCII when you wanted it to print as a number. -void dumpCharacter(PrintStream&, char); -MAKE_PRINT_ADAPTOR(CharacterDump, char, dumpCharacter); - -template -class PointerDump { -public: - PointerDump(const T* ptr) - : m_ptr(ptr) - { - } - - void dump(PrintStream& out) const - { - if (m_ptr) - printInternal(out, *m_ptr); - else - out.print("(null)"); - } -private: - const T* m_ptr; -}; - -template -PointerDump pointerDump(const T* ptr) { return PointerDump(ptr); } - -} // namespace WTF - -using WTF::CharacterDump; -using WTF::PointerDump; -using WTF::PrintStream; -using WTF::pointerDump; - -#endif // PrintStream_h - diff --git a/masm/wtf/RawPointer.h b/masm/wtf/RawPointer.h deleted file mode 100644 index 6dc7292fb4..0000000000 --- a/masm/wtf/RawPointer.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef RawPointer_h -#define RawPointer_h - -namespace WTF { - -class RawPointer { -public: - RawPointer() - : m_value(0) - { - } - - explicit RawPointer(void* value) - : m_value(value) - { - } - - explicit RawPointer(const void* value) - : m_value(value) - { - } - - const void* value() const { return m_value; } - -private: - const void* m_value; -}; - -} // namespace WTF - -using WTF::RawPointer; - -#endif // RawPointer_h diff --git a/masm/wtf/StdLibExtras.h b/masm/wtf/StdLibExtras.h deleted file mode 100644 index f5e9f78df1..0000000000 --- a/masm/wtf/StdLibExtras.h +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_StdLibExtras_h -#define WTF_StdLibExtras_h - -#include -#include - -// Use these to declare and define a static local variable (static T;) so that -// it is leaked so that its destructors are not called at exit. Using this -// macro also allows workarounds a compiler bug present in Apple's version of GCC 4.0.1. -#ifndef DEFINE_STATIC_LOCAL -#if COMPILER(GCC) && defined(__APPLE_CC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 1 -#define DEFINE_STATIC_LOCAL(type, name, arguments) \ - static type* name##Ptr = new type arguments; \ - type& name = *name##Ptr -#else -#define DEFINE_STATIC_LOCAL(type, name, arguments) \ - static type& name = *new type arguments -#endif -#endif - -// Use this macro to declare and define a debug-only global variable that may have a -// non-trivial constructor and destructor. When building with clang, this will suppress -// warnings about global constructors and exit-time destructors. -#ifndef NDEBUG -#if COMPILER(CLANG) -#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \ - _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \ - static type name arguments; \ - _Pragma("clang diagnostic pop") -#else -#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ - static type name arguments; -#endif // COMPILER(CLANG) -#else -#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) -#endif // NDEBUG - -// OBJECT_OFFSETOF: Like the C++ offsetof macro, but you can use it with classes. -// The magic number 0x4000 is insignificant. We use it to avoid using NULL, since -// NULL can cause compiler problems, especially in cases of multiple inheritance. -#define OBJECT_OFFSETOF(class, field) (reinterpret_cast(&(reinterpret_cast(0x4000)->field)) - 0x4000) - -// STRINGIZE: Can convert any value to quoted string, even expandable macros -#define STRINGIZE(exp) #exp -#define STRINGIZE_VALUE_OF(exp) STRINGIZE(exp) - -/* - * The reinterpret_cast([pointer to Type2]) expressions - where - * sizeof(Type1) > sizeof(Type2) - cause the following warning on ARM with GCC: - * increases required alignment of target type. - * - * An implicit or an extra static_cast bypasses the warning. - * For more info see the following bugzilla entries: - * - https://bugs.webkit.org/show_bug.cgi?id=38045 - * - http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43976 - */ -#if (CPU(ARM) || CPU(MIPS)) && COMPILER(GCC) -template -bool isPointerTypeAlignmentOkay(Type* ptr) -{ - return !(reinterpret_cast(ptr) % __alignof__(Type)); -} - -template -TypePtr reinterpret_cast_ptr(void* ptr) -{ - ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast(ptr))); - return reinterpret_cast(ptr); -} - -template -TypePtr reinterpret_cast_ptr(const void* ptr) -{ - ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast(ptr))); - return reinterpret_cast(ptr); -} -#else -template -bool isPointerTypeAlignmentOkay(Type*) -{ - return true; -} -#define reinterpret_cast_ptr reinterpret_cast -#endif - -namespace WTF { - -static const size_t KB = 1024; -static const size_t MB = 1024 * 1024; - -inline bool isPointerAligned(void* p) -{ - return !((intptr_t)(p) & (sizeof(char*) - 1)); -} - -inline bool is8ByteAligned(void* p) -{ - return !((uintptr_t)(p) & (sizeof(double) - 1)); -} - -/* - * C++'s idea of a reinterpret_cast lacks sufficient cojones. - */ -template -inline TO bitwise_cast(FROM from) -{ - COMPILE_ASSERT(sizeof(TO) == sizeof(FROM), WTF_bitwise_cast_sizeof_casted_types_is_equal); - union { - FROM from; - TO to; - } u; - u.from = from; - return u.to; -} - -template -inline To safeCast(From value) -{ - ASSERT(isInBounds(value)); - return static_cast(value); -} - -// Returns a count of the number of bits set in 'bits'. -inline size_t bitCount(unsigned bits) -{ - bits = bits - ((bits >> 1) & 0x55555555); - bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); - return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; -} - -// Macro that returns a compile time constant with the length of an array, but gives an error if passed a non-array. -template char (&ArrayLengthHelperFunction(T (&)[Size]))[Size]; -// GCC needs some help to deduce a 0 length array. -#if COMPILER(GCC) -template char (&ArrayLengthHelperFunction(T (&)[0]))[0]; -#endif -#define WTF_ARRAY_LENGTH(array) sizeof(::WTF::ArrayLengthHelperFunction(array)) - -// Efficient implementation that takes advantage of powers of two. -inline size_t roundUpToMultipleOf(size_t divisor, size_t x) -{ - ASSERT(divisor && !(divisor & (divisor - 1))); - size_t remainderMask = divisor - 1; - return (x + remainderMask) & ~remainderMask; -} -template inline size_t roundUpToMultipleOf(size_t x) -{ - COMPILE_ASSERT(divisor && !(divisor & (divisor - 1)), divisor_is_a_power_of_two); - return roundUpToMultipleOf(divisor, x); -} - -enum BinarySearchMode { - KeyMustBePresentInArray, - KeyMightNotBePresentInArray, - ReturnAdjacentElementIfKeyIsNotPresent -}; - -template -inline ArrayElementType* binarySearchImpl(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) -{ - size_t offset = 0; - while (size > 1) { - int pos = (size - 1) >> 1; - KeyType val = extractKey(&array[offset + pos]); - - if (val == key) - return &array[offset + pos]; - // The item we are looking for is smaller than the item being check; reduce the value of 'size', - // chopping off the right hand half of the array. - if (key < val) - size = pos; - // Discard all values in the left hand half of the array, up to and including the item at pos. - else { - size -= (pos + 1); - offset += (pos + 1); - } - - ASSERT(mode != KeyMustBePresentInArray || size); - } - - if (mode == KeyMightNotBePresentInArray && !size) - return 0; - - ArrayElementType* result = &array[offset]; - - if (mode == KeyMightNotBePresentInArray && key != extractKey(result)) - return 0; - - if (mode == KeyMustBePresentInArray) { - ASSERT(size == 1); - ASSERT(key == extractKey(result)); - } - - return result; -} - -// If the element is not found, crash if asserts are enabled, and behave like approximateBinarySearch in release builds. -template -inline ArrayElementType* binarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) -{ - return binarySearchImpl(array, size, key, extractKey); -} - -// Return zero if the element is not found. -template -inline ArrayElementType* tryBinarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) -{ - return binarySearchImpl(array, size, key, extractKey); -} - -// Return the element that is either to the left, or the right, of where the element would have been found. -template -inline ArrayElementType* approximateBinarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) -{ - return binarySearchImpl(array, size, key, extractKey); -} - -// Variants of the above that use const. -template -inline ArrayElementType* binarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) -{ - return binarySearchImpl(const_cast(array), size, key, extractKey); -} -template -inline ArrayElementType* tryBinarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) -{ - return binarySearchImpl(const_cast(array), size, key, extractKey); -} -template -inline ArrayElementType* approximateBinarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) -{ - return binarySearchImpl(const_cast(array), size, key, extractKey); -} - -} // namespace WTF - -// This version of placement new omits a 0 check. -enum NotNullTag { NotNull }; -inline void* operator new(size_t, NotNullTag, void* location) -{ - ASSERT(location); - return location; -} - -using WTF::KB; -using WTF::MB; -using WTF::isPointerAligned; -using WTF::is8ByteAligned; -using WTF::binarySearch; -using WTF::tryBinarySearch; -using WTF::approximateBinarySearch; -using WTF::bitwise_cast; -using WTF::safeCast; - -#endif // WTF_StdLibExtras_h diff --git a/masm/wtf/VMTags.h b/masm/wtf/VMTags.h deleted file mode 100644 index 117bc3721e..0000000000 --- a/masm/wtf/VMTags.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef VMTags_h -#define VMTags_h - -// On Mac OS X, the VM subsystem allows tagging memory requested from mmap and vm_map -// in order to aid tools that inspect system memory use. -#if OS(DARWIN) - -#include - -#if defined(VM_MEMORY_TCMALLOC) -#define VM_TAG_FOR_TCMALLOC_MEMORY VM_MAKE_TAG(VM_MEMORY_TCMALLOC) -#else -#define VM_TAG_FOR_TCMALLOC_MEMORY VM_MAKE_TAG(53) -#endif // defined(VM_MEMORY_TCMALLOC) - -#if defined(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) -#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) -#else -#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY VM_MAKE_TAG(64) -#endif // defined(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) - -#if defined(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) -#define VM_TAG_FOR_REGISTERFILE_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) -#else -#define VM_TAG_FOR_REGISTERFILE_MEMORY VM_MAKE_TAG(65) -#endif // defined(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) - -#if defined(VM_MEMORY_JAVASCRIPT_CORE) -#define VM_TAG_FOR_COLLECTOR_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_CORE) -#else -#define VM_TAG_FOR_COLLECTOR_MEMORY VM_MAKE_TAG(63) -#endif // defined(VM_MEMORY_JAVASCRIPT_CORE) - -#if defined(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) -#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY VM_MAKE_TAG(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) -#else -#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY VM_MAKE_TAG(69) -#endif // defined(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) - -#else // OS(DARWIN) - -#define VM_TAG_FOR_TCMALLOC_MEMORY -1 -#define VM_TAG_FOR_COLLECTOR_MEMORY -1 -#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY -1 -#define VM_TAG_FOR_REGISTERFILE_MEMORY -1 -#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY -1 - -#endif // OS(DARWIN) - -#endif // VMTags_h diff --git a/masm/yarr/Yarr.h b/masm/yarr/Yarr.h deleted file mode 100644 index d393e9fa90..0000000000 --- a/masm/yarr/Yarr.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef Yarr_h -#define Yarr_h - -#include "YarrInterpreter.h" -#include "YarrPattern.h" - -namespace JSC { namespace Yarr { - -#define YarrStackSpaceForBackTrackInfoPatternCharacter 1 // Only for !fixed quantifiers. -#define YarrStackSpaceForBackTrackInfoCharacterClass 1 // Only for !fixed quantifiers. -#define YarrStackSpaceForBackTrackInfoBackReference 2 -#define YarrStackSpaceForBackTrackInfoAlternative 1 // One per alternative. -#define YarrStackSpaceForBackTrackInfoParentheticalAssertion 1 -#define YarrStackSpaceForBackTrackInfoParenthesesOnce 1 // Only for !fixed quantifiers. -#define YarrStackSpaceForBackTrackInfoParenthesesTerminal 1 -#define YarrStackSpaceForBackTrackInfoParentheses 2 - -static const unsigned quantifyInfinite = UINT_MAX; -static const unsigned offsetNoMatch = (unsigned)-1; - -// The below limit restricts the number of "recursive" match calls in order to -// avoid spending exponential time on complex regular expressions. -static const unsigned matchLimit = 1000000; - -enum JSRegExpResult { - JSRegExpMatch = 1, - JSRegExpNoMatch = 0, - JSRegExpErrorNoMatch = -1, - JSRegExpErrorHitLimit = -2, - JSRegExpErrorNoMemory = -3, - JSRegExpErrorInternal = -4 -}; - -enum YarrCharSize { - Char8, - Char16 -}; - -} } // namespace JSC::Yarr - -#endif // Yarr_h - diff --git a/masm/yarr/YarrCanonicalizeUCS2.cpp b/masm/yarr/YarrCanonicalizeUCS2.cpp deleted file mode 100644 index 7bb3d08eb5..0000000000 --- a/masm/yarr/YarrCanonicalizeUCS2.cpp +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// DO NOT EDIT! - this file autogenerated by YarrCanonicalizeUCS2.js - -#include "config.h" -#include "YarrCanonicalizeUCS2.h" - -namespace JSC { namespace Yarr { - -#include - -uint16_t ucs2CharacterSet0[] = { 0x01c4u, 0x01c5u, 0x01c6u, 0 }; -uint16_t ucs2CharacterSet1[] = { 0x01c7u, 0x01c8u, 0x01c9u, 0 }; -uint16_t ucs2CharacterSet2[] = { 0x01cau, 0x01cbu, 0x01ccu, 0 }; -uint16_t ucs2CharacterSet3[] = { 0x01f1u, 0x01f2u, 0x01f3u, 0 }; -uint16_t ucs2CharacterSet4[] = { 0x0392u, 0x03b2u, 0x03d0u, 0 }; -uint16_t ucs2CharacterSet5[] = { 0x0395u, 0x03b5u, 0x03f5u, 0 }; -uint16_t ucs2CharacterSet6[] = { 0x0398u, 0x03b8u, 0x03d1u, 0 }; -uint16_t ucs2CharacterSet7[] = { 0x0345u, 0x0399u, 0x03b9u, 0x1fbeu, 0 }; -uint16_t ucs2CharacterSet8[] = { 0x039au, 0x03bau, 0x03f0u, 0 }; -uint16_t ucs2CharacterSet9[] = { 0x00b5u, 0x039cu, 0x03bcu, 0 }; -uint16_t ucs2CharacterSet10[] = { 0x03a0u, 0x03c0u, 0x03d6u, 0 }; -uint16_t ucs2CharacterSet11[] = { 0x03a1u, 0x03c1u, 0x03f1u, 0 }; -uint16_t ucs2CharacterSet12[] = { 0x03a3u, 0x03c2u, 0x03c3u, 0 }; -uint16_t ucs2CharacterSet13[] = { 0x03a6u, 0x03c6u, 0x03d5u, 0 }; -uint16_t ucs2CharacterSet14[] = { 0x1e60u, 0x1e61u, 0x1e9bu, 0 }; - -static const size_t UCS2_CANONICALIZATION_SETS = 15; -uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = { - ucs2CharacterSet0, - ucs2CharacterSet1, - ucs2CharacterSet2, - ucs2CharacterSet3, - ucs2CharacterSet4, - ucs2CharacterSet5, - ucs2CharacterSet6, - ucs2CharacterSet7, - ucs2CharacterSet8, - ucs2CharacterSet9, - ucs2CharacterSet10, - ucs2CharacterSet11, - ucs2CharacterSet12, - ucs2CharacterSet13, - ucs2CharacterSet14, -}; - -const size_t UCS2_CANONICALIZATION_RANGES = 364; -UCS2CanonicalizationRange rangeInfo[UCS2_CANONICALIZATION_RANGES] = { - { 0x0000u, 0x0040u, 0x0000u, CanonicalizeUnique }, - { 0x0041u, 0x005au, 0x0020u, CanonicalizeRangeLo }, - { 0x005bu, 0x0060u, 0x0000u, CanonicalizeUnique }, - { 0x0061u, 0x007au, 0x0020u, CanonicalizeRangeHi }, - { 0x007bu, 0x00b4u, 0x0000u, CanonicalizeUnique }, - { 0x00b5u, 0x00b5u, 0x0009u, CanonicalizeSet }, - { 0x00b6u, 0x00bfu, 0x0000u, CanonicalizeUnique }, - { 0x00c0u, 0x00d6u, 0x0020u, CanonicalizeRangeLo }, - { 0x00d7u, 0x00d7u, 0x0000u, CanonicalizeUnique }, - { 0x00d8u, 0x00deu, 0x0020u, CanonicalizeRangeLo }, - { 0x00dfu, 0x00dfu, 0x0000u, CanonicalizeUnique }, - { 0x00e0u, 0x00f6u, 0x0020u, CanonicalizeRangeHi }, - { 0x00f7u, 0x00f7u, 0x0000u, CanonicalizeUnique }, - { 0x00f8u, 0x00feu, 0x0020u, CanonicalizeRangeHi }, - { 0x00ffu, 0x00ffu, 0x0079u, CanonicalizeRangeLo }, - { 0x0100u, 0x012fu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0130u, 0x0131u, 0x0000u, CanonicalizeUnique }, - { 0x0132u, 0x0137u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0138u, 0x0138u, 0x0000u, CanonicalizeUnique }, - { 0x0139u, 0x0148u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x0149u, 0x0149u, 0x0000u, CanonicalizeUnique }, - { 0x014au, 0x0177u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0178u, 0x0178u, 0x0079u, CanonicalizeRangeHi }, - { 0x0179u, 0x017eu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x017fu, 0x017fu, 0x0000u, CanonicalizeUnique }, - { 0x0180u, 0x0180u, 0x00c3u, CanonicalizeRangeLo }, - { 0x0181u, 0x0181u, 0x00d2u, CanonicalizeRangeLo }, - { 0x0182u, 0x0185u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0186u, 0x0186u, 0x00ceu, CanonicalizeRangeLo }, - { 0x0187u, 0x0188u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x0189u, 0x018au, 0x00cdu, CanonicalizeRangeLo }, - { 0x018bu, 0x018cu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x018du, 0x018du, 0x0000u, CanonicalizeUnique }, - { 0x018eu, 0x018eu, 0x004fu, CanonicalizeRangeLo }, - { 0x018fu, 0x018fu, 0x00cau, CanonicalizeRangeLo }, - { 0x0190u, 0x0190u, 0x00cbu, CanonicalizeRangeLo }, - { 0x0191u, 0x0192u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x0193u, 0x0193u, 0x00cdu, CanonicalizeRangeLo }, - { 0x0194u, 0x0194u, 0x00cfu, CanonicalizeRangeLo }, - { 0x0195u, 0x0195u, 0x0061u, CanonicalizeRangeLo }, - { 0x0196u, 0x0196u, 0x00d3u, CanonicalizeRangeLo }, - { 0x0197u, 0x0197u, 0x00d1u, CanonicalizeRangeLo }, - { 0x0198u, 0x0199u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x019au, 0x019au, 0x00a3u, CanonicalizeRangeLo }, - { 0x019bu, 0x019bu, 0x0000u, CanonicalizeUnique }, - { 0x019cu, 0x019cu, 0x00d3u, CanonicalizeRangeLo }, - { 0x019du, 0x019du, 0x00d5u, CanonicalizeRangeLo }, - { 0x019eu, 0x019eu, 0x0082u, CanonicalizeRangeLo }, - { 0x019fu, 0x019fu, 0x00d6u, CanonicalizeRangeLo }, - { 0x01a0u, 0x01a5u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x01a6u, 0x01a6u, 0x00dau, CanonicalizeRangeLo }, - { 0x01a7u, 0x01a8u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x01a9u, 0x01a9u, 0x00dau, CanonicalizeRangeLo }, - { 0x01aau, 0x01abu, 0x0000u, CanonicalizeUnique }, - { 0x01acu, 0x01adu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x01aeu, 0x01aeu, 0x00dau, CanonicalizeRangeLo }, - { 0x01afu, 0x01b0u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x01b1u, 0x01b2u, 0x00d9u, CanonicalizeRangeLo }, - { 0x01b3u, 0x01b6u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x01b7u, 0x01b7u, 0x00dbu, CanonicalizeRangeLo }, - { 0x01b8u, 0x01b9u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x01bau, 0x01bbu, 0x0000u, CanonicalizeUnique }, - { 0x01bcu, 0x01bdu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x01beu, 0x01beu, 0x0000u, CanonicalizeUnique }, - { 0x01bfu, 0x01bfu, 0x0038u, CanonicalizeRangeLo }, - { 0x01c0u, 0x01c3u, 0x0000u, CanonicalizeUnique }, - { 0x01c4u, 0x01c6u, 0x0000u, CanonicalizeSet }, - { 0x01c7u, 0x01c9u, 0x0001u, CanonicalizeSet }, - { 0x01cau, 0x01ccu, 0x0002u, CanonicalizeSet }, - { 0x01cdu, 0x01dcu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x01ddu, 0x01ddu, 0x004fu, CanonicalizeRangeHi }, - { 0x01deu, 0x01efu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x01f0u, 0x01f0u, 0x0000u, CanonicalizeUnique }, - { 0x01f1u, 0x01f3u, 0x0003u, CanonicalizeSet }, - { 0x01f4u, 0x01f5u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x01f6u, 0x01f6u, 0x0061u, CanonicalizeRangeHi }, - { 0x01f7u, 0x01f7u, 0x0038u, CanonicalizeRangeHi }, - { 0x01f8u, 0x021fu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0220u, 0x0220u, 0x0082u, CanonicalizeRangeHi }, - { 0x0221u, 0x0221u, 0x0000u, CanonicalizeUnique }, - { 0x0222u, 0x0233u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0234u, 0x0239u, 0x0000u, CanonicalizeUnique }, - { 0x023au, 0x023au, 0x2a2bu, CanonicalizeRangeLo }, - { 0x023bu, 0x023cu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x023du, 0x023du, 0x00a3u, CanonicalizeRangeHi }, - { 0x023eu, 0x023eu, 0x2a28u, CanonicalizeRangeLo }, - { 0x023fu, 0x0240u, 0x2a3fu, CanonicalizeRangeLo }, - { 0x0241u, 0x0242u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x0243u, 0x0243u, 0x00c3u, CanonicalizeRangeHi }, - { 0x0244u, 0x0244u, 0x0045u, CanonicalizeRangeLo }, - { 0x0245u, 0x0245u, 0x0047u, CanonicalizeRangeLo }, - { 0x0246u, 0x024fu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0250u, 0x0250u, 0x2a1fu, CanonicalizeRangeLo }, - { 0x0251u, 0x0251u, 0x2a1cu, CanonicalizeRangeLo }, - { 0x0252u, 0x0252u, 0x2a1eu, CanonicalizeRangeLo }, - { 0x0253u, 0x0253u, 0x00d2u, CanonicalizeRangeHi }, - { 0x0254u, 0x0254u, 0x00ceu, CanonicalizeRangeHi }, - { 0x0255u, 0x0255u, 0x0000u, CanonicalizeUnique }, - { 0x0256u, 0x0257u, 0x00cdu, CanonicalizeRangeHi }, - { 0x0258u, 0x0258u, 0x0000u, CanonicalizeUnique }, - { 0x0259u, 0x0259u, 0x00cau, CanonicalizeRangeHi }, - { 0x025au, 0x025au, 0x0000u, CanonicalizeUnique }, - { 0x025bu, 0x025bu, 0x00cbu, CanonicalizeRangeHi }, - { 0x025cu, 0x025fu, 0x0000u, CanonicalizeUnique }, - { 0x0260u, 0x0260u, 0x00cdu, CanonicalizeRangeHi }, - { 0x0261u, 0x0262u, 0x0000u, CanonicalizeUnique }, - { 0x0263u, 0x0263u, 0x00cfu, CanonicalizeRangeHi }, - { 0x0264u, 0x0264u, 0x0000u, CanonicalizeUnique }, - { 0x0265u, 0x0265u, 0xa528u, CanonicalizeRangeLo }, - { 0x0266u, 0x0267u, 0x0000u, CanonicalizeUnique }, - { 0x0268u, 0x0268u, 0x00d1u, CanonicalizeRangeHi }, - { 0x0269u, 0x0269u, 0x00d3u, CanonicalizeRangeHi }, - { 0x026au, 0x026au, 0x0000u, CanonicalizeUnique }, - { 0x026bu, 0x026bu, 0x29f7u, CanonicalizeRangeLo }, - { 0x026cu, 0x026eu, 0x0000u, CanonicalizeUnique }, - { 0x026fu, 0x026fu, 0x00d3u, CanonicalizeRangeHi }, - { 0x0270u, 0x0270u, 0x0000u, CanonicalizeUnique }, - { 0x0271u, 0x0271u, 0x29fdu, CanonicalizeRangeLo }, - { 0x0272u, 0x0272u, 0x00d5u, CanonicalizeRangeHi }, - { 0x0273u, 0x0274u, 0x0000u, CanonicalizeUnique }, - { 0x0275u, 0x0275u, 0x00d6u, CanonicalizeRangeHi }, - { 0x0276u, 0x027cu, 0x0000u, CanonicalizeUnique }, - { 0x027du, 0x027du, 0x29e7u, CanonicalizeRangeLo }, - { 0x027eu, 0x027fu, 0x0000u, CanonicalizeUnique }, - { 0x0280u, 0x0280u, 0x00dau, CanonicalizeRangeHi }, - { 0x0281u, 0x0282u, 0x0000u, CanonicalizeUnique }, - { 0x0283u, 0x0283u, 0x00dau, CanonicalizeRangeHi }, - { 0x0284u, 0x0287u, 0x0000u, CanonicalizeUnique }, - { 0x0288u, 0x0288u, 0x00dau, CanonicalizeRangeHi }, - { 0x0289u, 0x0289u, 0x0045u, CanonicalizeRangeHi }, - { 0x028au, 0x028bu, 0x00d9u, CanonicalizeRangeHi }, - { 0x028cu, 0x028cu, 0x0047u, CanonicalizeRangeHi }, - { 0x028du, 0x0291u, 0x0000u, CanonicalizeUnique }, - { 0x0292u, 0x0292u, 0x00dbu, CanonicalizeRangeHi }, - { 0x0293u, 0x0344u, 0x0000u, CanonicalizeUnique }, - { 0x0345u, 0x0345u, 0x0007u, CanonicalizeSet }, - { 0x0346u, 0x036fu, 0x0000u, CanonicalizeUnique }, - { 0x0370u, 0x0373u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0374u, 0x0375u, 0x0000u, CanonicalizeUnique }, - { 0x0376u, 0x0377u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0378u, 0x037au, 0x0000u, CanonicalizeUnique }, - { 0x037bu, 0x037du, 0x0082u, CanonicalizeRangeLo }, - { 0x037eu, 0x0385u, 0x0000u, CanonicalizeUnique }, - { 0x0386u, 0x0386u, 0x0026u, CanonicalizeRangeLo }, - { 0x0387u, 0x0387u, 0x0000u, CanonicalizeUnique }, - { 0x0388u, 0x038au, 0x0025u, CanonicalizeRangeLo }, - { 0x038bu, 0x038bu, 0x0000u, CanonicalizeUnique }, - { 0x038cu, 0x038cu, 0x0040u, CanonicalizeRangeLo }, - { 0x038du, 0x038du, 0x0000u, CanonicalizeUnique }, - { 0x038eu, 0x038fu, 0x003fu, CanonicalizeRangeLo }, - { 0x0390u, 0x0390u, 0x0000u, CanonicalizeUnique }, - { 0x0391u, 0x0391u, 0x0020u, CanonicalizeRangeLo }, - { 0x0392u, 0x0392u, 0x0004u, CanonicalizeSet }, - { 0x0393u, 0x0394u, 0x0020u, CanonicalizeRangeLo }, - { 0x0395u, 0x0395u, 0x0005u, CanonicalizeSet }, - { 0x0396u, 0x0397u, 0x0020u, CanonicalizeRangeLo }, - { 0x0398u, 0x0398u, 0x0006u, CanonicalizeSet }, - { 0x0399u, 0x0399u, 0x0007u, CanonicalizeSet }, - { 0x039au, 0x039au, 0x0008u, CanonicalizeSet }, - { 0x039bu, 0x039bu, 0x0020u, CanonicalizeRangeLo }, - { 0x039cu, 0x039cu, 0x0009u, CanonicalizeSet }, - { 0x039du, 0x039fu, 0x0020u, CanonicalizeRangeLo }, - { 0x03a0u, 0x03a0u, 0x000au, CanonicalizeSet }, - { 0x03a1u, 0x03a1u, 0x000bu, CanonicalizeSet }, - { 0x03a2u, 0x03a2u, 0x0000u, CanonicalizeUnique }, - { 0x03a3u, 0x03a3u, 0x000cu, CanonicalizeSet }, - { 0x03a4u, 0x03a5u, 0x0020u, CanonicalizeRangeLo }, - { 0x03a6u, 0x03a6u, 0x000du, CanonicalizeSet }, - { 0x03a7u, 0x03abu, 0x0020u, CanonicalizeRangeLo }, - { 0x03acu, 0x03acu, 0x0026u, CanonicalizeRangeHi }, - { 0x03adu, 0x03afu, 0x0025u, CanonicalizeRangeHi }, - { 0x03b0u, 0x03b0u, 0x0000u, CanonicalizeUnique }, - { 0x03b1u, 0x03b1u, 0x0020u, CanonicalizeRangeHi }, - { 0x03b2u, 0x03b2u, 0x0004u, CanonicalizeSet }, - { 0x03b3u, 0x03b4u, 0x0020u, CanonicalizeRangeHi }, - { 0x03b5u, 0x03b5u, 0x0005u, CanonicalizeSet }, - { 0x03b6u, 0x03b7u, 0x0020u, CanonicalizeRangeHi }, - { 0x03b8u, 0x03b8u, 0x0006u, CanonicalizeSet }, - { 0x03b9u, 0x03b9u, 0x0007u, CanonicalizeSet }, - { 0x03bau, 0x03bau, 0x0008u, CanonicalizeSet }, - { 0x03bbu, 0x03bbu, 0x0020u, CanonicalizeRangeHi }, - { 0x03bcu, 0x03bcu, 0x0009u, CanonicalizeSet }, - { 0x03bdu, 0x03bfu, 0x0020u, CanonicalizeRangeHi }, - { 0x03c0u, 0x03c0u, 0x000au, CanonicalizeSet }, - { 0x03c1u, 0x03c1u, 0x000bu, CanonicalizeSet }, - { 0x03c2u, 0x03c3u, 0x000cu, CanonicalizeSet }, - { 0x03c4u, 0x03c5u, 0x0020u, CanonicalizeRangeHi }, - { 0x03c6u, 0x03c6u, 0x000du, CanonicalizeSet }, - { 0x03c7u, 0x03cbu, 0x0020u, CanonicalizeRangeHi }, - { 0x03ccu, 0x03ccu, 0x0040u, CanonicalizeRangeHi }, - { 0x03cdu, 0x03ceu, 0x003fu, CanonicalizeRangeHi }, - { 0x03cfu, 0x03cfu, 0x0008u, CanonicalizeRangeLo }, - { 0x03d0u, 0x03d0u, 0x0004u, CanonicalizeSet }, - { 0x03d1u, 0x03d1u, 0x0006u, CanonicalizeSet }, - { 0x03d2u, 0x03d4u, 0x0000u, CanonicalizeUnique }, - { 0x03d5u, 0x03d5u, 0x000du, CanonicalizeSet }, - { 0x03d6u, 0x03d6u, 0x000au, CanonicalizeSet }, - { 0x03d7u, 0x03d7u, 0x0008u, CanonicalizeRangeHi }, - { 0x03d8u, 0x03efu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x03f0u, 0x03f0u, 0x0008u, CanonicalizeSet }, - { 0x03f1u, 0x03f1u, 0x000bu, CanonicalizeSet }, - { 0x03f2u, 0x03f2u, 0x0007u, CanonicalizeRangeLo }, - { 0x03f3u, 0x03f4u, 0x0000u, CanonicalizeUnique }, - { 0x03f5u, 0x03f5u, 0x0005u, CanonicalizeSet }, - { 0x03f6u, 0x03f6u, 0x0000u, CanonicalizeUnique }, - { 0x03f7u, 0x03f8u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x03f9u, 0x03f9u, 0x0007u, CanonicalizeRangeHi }, - { 0x03fau, 0x03fbu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x03fcu, 0x03fcu, 0x0000u, CanonicalizeUnique }, - { 0x03fdu, 0x03ffu, 0x0082u, CanonicalizeRangeHi }, - { 0x0400u, 0x040fu, 0x0050u, CanonicalizeRangeLo }, - { 0x0410u, 0x042fu, 0x0020u, CanonicalizeRangeLo }, - { 0x0430u, 0x044fu, 0x0020u, CanonicalizeRangeHi }, - { 0x0450u, 0x045fu, 0x0050u, CanonicalizeRangeHi }, - { 0x0460u, 0x0481u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0482u, 0x0489u, 0x0000u, CanonicalizeUnique }, - { 0x048au, 0x04bfu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x04c0u, 0x04c0u, 0x000fu, CanonicalizeRangeLo }, - { 0x04c1u, 0x04ceu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x04cfu, 0x04cfu, 0x000fu, CanonicalizeRangeHi }, - { 0x04d0u, 0x0527u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0528u, 0x0530u, 0x0000u, CanonicalizeUnique }, - { 0x0531u, 0x0556u, 0x0030u, CanonicalizeRangeLo }, - { 0x0557u, 0x0560u, 0x0000u, CanonicalizeUnique }, - { 0x0561u, 0x0586u, 0x0030u, CanonicalizeRangeHi }, - { 0x0587u, 0x109fu, 0x0000u, CanonicalizeUnique }, - { 0x10a0u, 0x10c5u, 0x1c60u, CanonicalizeRangeLo }, - { 0x10c6u, 0x1d78u, 0x0000u, CanonicalizeUnique }, - { 0x1d79u, 0x1d79u, 0x8a04u, CanonicalizeRangeLo }, - { 0x1d7au, 0x1d7cu, 0x0000u, CanonicalizeUnique }, - { 0x1d7du, 0x1d7du, 0x0ee6u, CanonicalizeRangeLo }, - { 0x1d7eu, 0x1dffu, 0x0000u, CanonicalizeUnique }, - { 0x1e00u, 0x1e5fu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x1e60u, 0x1e61u, 0x000eu, CanonicalizeSet }, - { 0x1e62u, 0x1e95u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x1e96u, 0x1e9au, 0x0000u, CanonicalizeUnique }, - { 0x1e9bu, 0x1e9bu, 0x000eu, CanonicalizeSet }, - { 0x1e9cu, 0x1e9fu, 0x0000u, CanonicalizeUnique }, - { 0x1ea0u, 0x1effu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x1f00u, 0x1f07u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f08u, 0x1f0fu, 0x0008u, CanonicalizeRangeHi }, - { 0x1f10u, 0x1f15u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f16u, 0x1f17u, 0x0000u, CanonicalizeUnique }, - { 0x1f18u, 0x1f1du, 0x0008u, CanonicalizeRangeHi }, - { 0x1f1eu, 0x1f1fu, 0x0000u, CanonicalizeUnique }, - { 0x1f20u, 0x1f27u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f28u, 0x1f2fu, 0x0008u, CanonicalizeRangeHi }, - { 0x1f30u, 0x1f37u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f38u, 0x1f3fu, 0x0008u, CanonicalizeRangeHi }, - { 0x1f40u, 0x1f45u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f46u, 0x1f47u, 0x0000u, CanonicalizeUnique }, - { 0x1f48u, 0x1f4du, 0x0008u, CanonicalizeRangeHi }, - { 0x1f4eu, 0x1f50u, 0x0000u, CanonicalizeUnique }, - { 0x1f51u, 0x1f51u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f52u, 0x1f52u, 0x0000u, CanonicalizeUnique }, - { 0x1f53u, 0x1f53u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f54u, 0x1f54u, 0x0000u, CanonicalizeUnique }, - { 0x1f55u, 0x1f55u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f56u, 0x1f56u, 0x0000u, CanonicalizeUnique }, - { 0x1f57u, 0x1f57u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f58u, 0x1f58u, 0x0000u, CanonicalizeUnique }, - { 0x1f59u, 0x1f59u, 0x0008u, CanonicalizeRangeHi }, - { 0x1f5au, 0x1f5au, 0x0000u, CanonicalizeUnique }, - { 0x1f5bu, 0x1f5bu, 0x0008u, CanonicalizeRangeHi }, - { 0x1f5cu, 0x1f5cu, 0x0000u, CanonicalizeUnique }, - { 0x1f5du, 0x1f5du, 0x0008u, CanonicalizeRangeHi }, - { 0x1f5eu, 0x1f5eu, 0x0000u, CanonicalizeUnique }, - { 0x1f5fu, 0x1f5fu, 0x0008u, CanonicalizeRangeHi }, - { 0x1f60u, 0x1f67u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f68u, 0x1f6fu, 0x0008u, CanonicalizeRangeHi }, - { 0x1f70u, 0x1f71u, 0x004au, CanonicalizeRangeLo }, - { 0x1f72u, 0x1f75u, 0x0056u, CanonicalizeRangeLo }, - { 0x1f76u, 0x1f77u, 0x0064u, CanonicalizeRangeLo }, - { 0x1f78u, 0x1f79u, 0x0080u, CanonicalizeRangeLo }, - { 0x1f7au, 0x1f7bu, 0x0070u, CanonicalizeRangeLo }, - { 0x1f7cu, 0x1f7du, 0x007eu, CanonicalizeRangeLo }, - { 0x1f7eu, 0x1fafu, 0x0000u, CanonicalizeUnique }, - { 0x1fb0u, 0x1fb1u, 0x0008u, CanonicalizeRangeLo }, - { 0x1fb2u, 0x1fb7u, 0x0000u, CanonicalizeUnique }, - { 0x1fb8u, 0x1fb9u, 0x0008u, CanonicalizeRangeHi }, - { 0x1fbau, 0x1fbbu, 0x004au, CanonicalizeRangeHi }, - { 0x1fbcu, 0x1fbdu, 0x0000u, CanonicalizeUnique }, - { 0x1fbeu, 0x1fbeu, 0x0007u, CanonicalizeSet }, - { 0x1fbfu, 0x1fc7u, 0x0000u, CanonicalizeUnique }, - { 0x1fc8u, 0x1fcbu, 0x0056u, CanonicalizeRangeHi }, - { 0x1fccu, 0x1fcfu, 0x0000u, CanonicalizeUnique }, - { 0x1fd0u, 0x1fd1u, 0x0008u, CanonicalizeRangeLo }, - { 0x1fd2u, 0x1fd7u, 0x0000u, CanonicalizeUnique }, - { 0x1fd8u, 0x1fd9u, 0x0008u, CanonicalizeRangeHi }, - { 0x1fdau, 0x1fdbu, 0x0064u, CanonicalizeRangeHi }, - { 0x1fdcu, 0x1fdfu, 0x0000u, CanonicalizeUnique }, - { 0x1fe0u, 0x1fe1u, 0x0008u, CanonicalizeRangeLo }, - { 0x1fe2u, 0x1fe4u, 0x0000u, CanonicalizeUnique }, - { 0x1fe5u, 0x1fe5u, 0x0007u, CanonicalizeRangeLo }, - { 0x1fe6u, 0x1fe7u, 0x0000u, CanonicalizeUnique }, - { 0x1fe8u, 0x1fe9u, 0x0008u, CanonicalizeRangeHi }, - { 0x1feau, 0x1febu, 0x0070u, CanonicalizeRangeHi }, - { 0x1fecu, 0x1fecu, 0x0007u, CanonicalizeRangeHi }, - { 0x1fedu, 0x1ff7u, 0x0000u, CanonicalizeUnique }, - { 0x1ff8u, 0x1ff9u, 0x0080u, CanonicalizeRangeHi }, - { 0x1ffau, 0x1ffbu, 0x007eu, CanonicalizeRangeHi }, - { 0x1ffcu, 0x2131u, 0x0000u, CanonicalizeUnique }, - { 0x2132u, 0x2132u, 0x001cu, CanonicalizeRangeLo }, - { 0x2133u, 0x214du, 0x0000u, CanonicalizeUnique }, - { 0x214eu, 0x214eu, 0x001cu, CanonicalizeRangeHi }, - { 0x214fu, 0x215fu, 0x0000u, CanonicalizeUnique }, - { 0x2160u, 0x216fu, 0x0010u, CanonicalizeRangeLo }, - { 0x2170u, 0x217fu, 0x0010u, CanonicalizeRangeHi }, - { 0x2180u, 0x2182u, 0x0000u, CanonicalizeUnique }, - { 0x2183u, 0x2184u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x2185u, 0x24b5u, 0x0000u, CanonicalizeUnique }, - { 0x24b6u, 0x24cfu, 0x001au, CanonicalizeRangeLo }, - { 0x24d0u, 0x24e9u, 0x001au, CanonicalizeRangeHi }, - { 0x24eau, 0x2bffu, 0x0000u, CanonicalizeUnique }, - { 0x2c00u, 0x2c2eu, 0x0030u, CanonicalizeRangeLo }, - { 0x2c2fu, 0x2c2fu, 0x0000u, CanonicalizeUnique }, - { 0x2c30u, 0x2c5eu, 0x0030u, CanonicalizeRangeHi }, - { 0x2c5fu, 0x2c5fu, 0x0000u, CanonicalizeUnique }, - { 0x2c60u, 0x2c61u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x2c62u, 0x2c62u, 0x29f7u, CanonicalizeRangeHi }, - { 0x2c63u, 0x2c63u, 0x0ee6u, CanonicalizeRangeHi }, - { 0x2c64u, 0x2c64u, 0x29e7u, CanonicalizeRangeHi }, - { 0x2c65u, 0x2c65u, 0x2a2bu, CanonicalizeRangeHi }, - { 0x2c66u, 0x2c66u, 0x2a28u, CanonicalizeRangeHi }, - { 0x2c67u, 0x2c6cu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x2c6du, 0x2c6du, 0x2a1cu, CanonicalizeRangeHi }, - { 0x2c6eu, 0x2c6eu, 0x29fdu, CanonicalizeRangeHi }, - { 0x2c6fu, 0x2c6fu, 0x2a1fu, CanonicalizeRangeHi }, - { 0x2c70u, 0x2c70u, 0x2a1eu, CanonicalizeRangeHi }, - { 0x2c71u, 0x2c71u, 0x0000u, CanonicalizeUnique }, - { 0x2c72u, 0x2c73u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x2c74u, 0x2c74u, 0x0000u, CanonicalizeUnique }, - { 0x2c75u, 0x2c76u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x2c77u, 0x2c7du, 0x0000u, CanonicalizeUnique }, - { 0x2c7eu, 0x2c7fu, 0x2a3fu, CanonicalizeRangeHi }, - { 0x2c80u, 0x2ce3u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x2ce4u, 0x2ceau, 0x0000u, CanonicalizeUnique }, - { 0x2cebu, 0x2ceeu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x2cefu, 0x2cffu, 0x0000u, CanonicalizeUnique }, - { 0x2d00u, 0x2d25u, 0x1c60u, CanonicalizeRangeHi }, - { 0x2d26u, 0xa63fu, 0x0000u, CanonicalizeUnique }, - { 0xa640u, 0xa66du, 0x0000u, CanonicalizeAlternatingAligned }, - { 0xa66eu, 0xa67fu, 0x0000u, CanonicalizeUnique }, - { 0xa680u, 0xa697u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0xa698u, 0xa721u, 0x0000u, CanonicalizeUnique }, - { 0xa722u, 0xa72fu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0xa730u, 0xa731u, 0x0000u, CanonicalizeUnique }, - { 0xa732u, 0xa76fu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0xa770u, 0xa778u, 0x0000u, CanonicalizeUnique }, - { 0xa779u, 0xa77cu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0xa77du, 0xa77du, 0x8a04u, CanonicalizeRangeHi }, - { 0xa77eu, 0xa787u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0xa788u, 0xa78au, 0x0000u, CanonicalizeUnique }, - { 0xa78bu, 0xa78cu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0xa78du, 0xa78du, 0xa528u, CanonicalizeRangeHi }, - { 0xa78eu, 0xa78fu, 0x0000u, CanonicalizeUnique }, - { 0xa790u, 0xa791u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0xa792u, 0xa79fu, 0x0000u, CanonicalizeUnique }, - { 0xa7a0u, 0xa7a9u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0xa7aau, 0xff20u, 0x0000u, CanonicalizeUnique }, - { 0xff21u, 0xff3au, 0x0020u, CanonicalizeRangeLo }, - { 0xff3bu, 0xff40u, 0x0000u, CanonicalizeUnique }, - { 0xff41u, 0xff5au, 0x0020u, CanonicalizeRangeHi }, - { 0xff5bu, 0xffffu, 0x0000u, CanonicalizeUnique }, -}; - -const size_t LATIN_CANONICALIZATION_RANGES = 20; -LatinCanonicalizationRange latinRangeInfo[LATIN_CANONICALIZATION_RANGES] = { - { 0x0000u, 0x0040u, 0x0000u, CanonicalizeLatinSelf }, - { 0x0041u, 0x005au, 0x0000u, CanonicalizeLatinMask0x20 }, - { 0x005bu, 0x0060u, 0x0000u, CanonicalizeLatinSelf }, - { 0x0061u, 0x007au, 0x0000u, CanonicalizeLatinMask0x20 }, - { 0x007bu, 0x00bfu, 0x0000u, CanonicalizeLatinSelf }, - { 0x00c0u, 0x00d6u, 0x0000u, CanonicalizeLatinMask0x20 }, - { 0x00d7u, 0x00d7u, 0x0000u, CanonicalizeLatinSelf }, - { 0x00d8u, 0x00deu, 0x0000u, CanonicalizeLatinMask0x20 }, - { 0x00dfu, 0x00dfu, 0x0000u, CanonicalizeLatinSelf }, - { 0x00e0u, 0x00f6u, 0x0000u, CanonicalizeLatinMask0x20 }, - { 0x00f7u, 0x00f7u, 0x0000u, CanonicalizeLatinSelf }, - { 0x00f8u, 0x00feu, 0x0000u, CanonicalizeLatinMask0x20 }, - { 0x00ffu, 0x00ffu, 0x0000u, CanonicalizeLatinSelf }, - { 0x0100u, 0x0177u, 0x0000u, CanonicalizeLatinInvalid }, - { 0x0178u, 0x0178u, 0x00ffu, CanonicalizeLatinOther }, - { 0x0179u, 0x039bu, 0x0000u, CanonicalizeLatinInvalid }, - { 0x039cu, 0x039cu, 0x00b5u, CanonicalizeLatinOther }, - { 0x039du, 0x03bbu, 0x0000u, CanonicalizeLatinInvalid }, - { 0x03bcu, 0x03bcu, 0x00b5u, CanonicalizeLatinOther }, - { 0x03bdu, 0xffffu, 0x0000u, CanonicalizeLatinInvalid }, -}; - -} } // JSC::Yarr - diff --git a/masm/yarr/YarrCanonicalizeUCS2.h b/masm/yarr/YarrCanonicalizeUCS2.h deleted file mode 100644 index be0ead43d2..0000000000 --- a/masm/yarr/YarrCanonicalizeUCS2.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef YarrCanonicalizeUCS2_H -#define YarrCanonicalizeUCS2_H - -#include -#include - -namespace JSC { namespace Yarr { - -// This set of data (autogenerated using YarrCanonicalizeUCS2.js into YarrCanonicalizeUCS2.cpp) -// provides information for each UCS2 code point as to the set of code points that it should -// match under the ES5.1 case insensitive RegExp matching rules, specified in 15.10.2.8. -enum UCS2CanonicalizationType { - CanonicalizeUnique, // No canonically equal values, e.g. 0x0. - CanonicalizeSet, // Value indicates a set in characterSetInfo. - CanonicalizeRangeLo, // Value is positive delta to pair, E.g. 0x41 has value 0x20, -> 0x61. - CanonicalizeRangeHi, // Value is positive delta to pair, E.g. 0x61 has value 0x20, -> 0x41. - CanonicalizeAlternatingAligned, // Aligned consequtive pair, e.g. 0x1f4,0x1f5. - CanonicalizeAlternatingUnaligned, // Unaligned consequtive pair, e.g. 0x241,0x242. -}; -struct UCS2CanonicalizationRange { uint16_t begin, end, value, type; }; -extern const size_t UCS2_CANONICALIZATION_RANGES; -extern uint16_t* characterSetInfo[]; -extern UCS2CanonicalizationRange rangeInfo[]; - -// This table is similar to the full rangeInfo table, however this maps from UCS2 codepoints to -// the set of Latin1 codepoints that could match. -enum LatinCanonicalizationType { - CanonicalizeLatinSelf, // This character is in the Latin1 range, but has no canonical equivalent in the range. - CanonicalizeLatinMask0x20, // One of a pair of characters, under the mask 0x20. - CanonicalizeLatinOther, // This character is not in the Latin1 range, but canonicalizes to another that is. - CanonicalizeLatinInvalid, // Cannot match against Latin1 input. -}; -struct LatinCanonicalizationRange { uint16_t begin, end, value, type; }; -extern const size_t LATIN_CANONICALIZATION_RANGES; -extern LatinCanonicalizationRange latinRangeInfo[]; - -// This searches in log2 time over ~364 entries, so should typically result in 8 compares. -inline UCS2CanonicalizationRange* rangeInfoFor(UChar ch) -{ - UCS2CanonicalizationRange* info = rangeInfo; - size_t entries = UCS2_CANONICALIZATION_RANGES; - - while (true) { - size_t candidate = entries >> 1; - UCS2CanonicalizationRange* candidateInfo = info + candidate; - if (ch < candidateInfo->begin) - entries = candidate; - else if (ch <= candidateInfo->end) - return candidateInfo; - else { - info = candidateInfo + 1; - entries -= (candidate + 1); - } - } -} - -// Should only be called for characters that have one canonically matching value. -inline UChar getCanonicalPair(UCS2CanonicalizationRange* info, UChar ch) -{ - ASSERT(ch >= info->begin && ch <= info->end); - switch (info->type) { - case CanonicalizeRangeLo: - return ch + info->value; - case CanonicalizeRangeHi: - return ch - info->value; - case CanonicalizeAlternatingAligned: - return ch ^ 1; - case CanonicalizeAlternatingUnaligned: - return ((ch - 1) ^ 1) + 1; - default: - ASSERT_NOT_REACHED(); - } - ASSERT_NOT_REACHED(); - return 0; -} - -// Returns true if no other UCS2 codepoint can match this value. -inline bool isCanonicallyUnique(UChar ch) -{ - return rangeInfoFor(ch)->type == CanonicalizeUnique; -} - -// Returns true if values are equal, under the canonicalization rules. -inline bool areCanonicallyEquivalent(UChar a, UChar b) -{ - UCS2CanonicalizationRange* info = rangeInfoFor(a); - switch (info->type) { - case CanonicalizeUnique: - return a == b; - case CanonicalizeSet: { - for (uint16_t* set = characterSetInfo[info->value]; (a = *set); ++set) { - if (a == b) - return true; - } - return false; - } - case CanonicalizeRangeLo: - return (a == b) || (a + info->value == b); - case CanonicalizeRangeHi: - return (a == b) || (a - info->value == b); - case CanonicalizeAlternatingAligned: - return (a | 1) == (b | 1); - case CanonicalizeAlternatingUnaligned: - return ((a - 1) | 1) == ((b - 1) | 1); - } - - ASSERT_NOT_REACHED(); - return false; -} - -} } // JSC::Yarr - -#endif diff --git a/masm/yarr/YarrCanonicalizeUCS2.js b/masm/yarr/YarrCanonicalizeUCS2.js deleted file mode 100644 index 00361dd46e..0000000000 --- a/masm/yarr/YarrCanonicalizeUCS2.js +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// See ES 5.1, 15.10.2.8 -function canonicalize(ch) -{ - var u = String.fromCharCode(ch).toUpperCase(); - if (u.length > 1) - return ch; - var cu = u.charCodeAt(0); - if (ch >= 128 && cu < 128) - return ch; - return cu; -} - -var MAX_UCS2 = 0xFFFF; -var MAX_LATIN = 0xFF; - -var groupedCanonically = []; -// Pass 1: populate groupedCanonically - this is mapping from canonicalized -// values back to the set of character code that canonicalize to them. -for (var i = 0; i <= MAX_UCS2; ++i) { - var ch = canonicalize(i); - if (!groupedCanonically[ch]) - groupedCanonically[ch] = []; - groupedCanonically[ch].push(i); -} - -var typeInfo = []; -var latinTypeInfo = []; -var characterSetInfo = []; -// Pass 2: populate typeInfo & characterSetInfo. For every character calculate -// a typeInfo value, described by the types above, and a value payload. -for (cu in groupedCanonically) { - // The set of characters that canonicalize to cu - var characters = groupedCanonically[cu]; - - // If there is only one, it is unique. - if (characters.length == 1) { - typeInfo[characters[0]] = "CanonicalizeUnique:0"; - latinTypeInfo[characters[0]] = characters[0] <= MAX_LATIN ? "CanonicalizeLatinSelf:0" : "CanonicalizeLatinInvalid:0"; - continue; - } - - // Sort the array. - characters.sort(function(x,y){return x-y;}); - - // If there are more than two characters, create an entry in characterSetInfo. - if (characters.length > 2) { - for (i in characters) - typeInfo[characters[i]] = "CanonicalizeSet:" + characterSetInfo.length; - characterSetInfo.push(characters); - - if (characters[1] <= MAX_LATIN) - throw new Error("sets with more than one latin character not supported!"); - if (characters[0] <= MAX_LATIN) { - for (i in characters) - latinTypeInfo[characters[i]] = "CanonicalizeLatinOther:" + characters[0]; - latinTypeInfo[characters[0]] = "CanonicalizeLatinSelf:0"; - } else { - for (i in characters) - latinTypeInfo[characters[i]] = "CanonicalizeLatinInvalid:0"; - } - - continue; - } - - // We have a pair, mark alternating ranges, otherwise track whether this is the low or high partner. - var lo = characters[0]; - var hi = characters[1]; - var delta = hi - lo; - if (delta == 1) { - var type = lo & 1 ? "CanonicalizeAlternatingUnaligned:0" : "CanonicalizeAlternatingAligned:0"; - typeInfo[lo] = type; - typeInfo[hi] = type; - } else { - typeInfo[lo] = "CanonicalizeRangeLo:" + delta; - typeInfo[hi] = "CanonicalizeRangeHi:" + delta; - } - - if (lo > MAX_LATIN) { - latinTypeInfo[lo] = "CanonicalizeLatinInvalid:0"; - latinTypeInfo[hi] = "CanonicalizeLatinInvalid:0"; - } else if (hi > MAX_LATIN) { - latinTypeInfo[lo] = "CanonicalizeLatinSelf:0"; - latinTypeInfo[hi] = "CanonicalizeLatinOther:" + lo; - } else { - if (delta != 0x20 || lo & 0x20) - throw new Error("pairs of latin characters that don't mask with 0x20 not supported!"); - latinTypeInfo[lo] = "CanonicalizeLatinMask0x20:0"; - latinTypeInfo[hi] = "CanonicalizeLatinMask0x20:0"; - } -} - -var rangeInfo = []; -// Pass 3: coallesce types into ranges. -for (var end = 0; end <= MAX_UCS2; ++end) { - var begin = end; - var type = typeInfo[end]; - while (end < MAX_UCS2 && typeInfo[end + 1] == type) - ++end; - rangeInfo.push({begin:begin, end:end, type:type}); -} - -var latinRangeInfo = []; -// Pass 4: coallesce latin-1 types into ranges. -for (var end = 0; end <= MAX_UCS2; ++end) { - var begin = end; - var type = latinTypeInfo[end]; - while (end < MAX_UCS2 && latinTypeInfo[end + 1] == type) - ++end; - latinRangeInfo.push({begin:begin, end:end, type:type}); -} - - -// Helper function to convert a number to a fixed width hex representation of a C uint16_t. -function hex(x) -{ - var s = Number(x).toString(16); - while (s.length < 4) - s = 0 + s; - return "0x" + s + "u"; -} - -var copyright = ( - "/*" + "\n" + - " * Copyright (C) 2012 Apple Inc. All rights reserved." + "\n" + - " *" + "\n" + - " * Redistribution and use in source and binary forms, with or without" + "\n" + - " * modification, are permitted provided that the following conditions" + "\n" + - " * are met:" + "\n" + - " * 1. Redistributions of source code must retain the above copyright" + "\n" + - " * notice, this list of conditions and the following disclaimer." + "\n" + - " * 2. Redistributions in binary form must reproduce the above copyright" + "\n" + - " * notice, this list of conditions and the following disclaimer in the" + "\n" + - " * documentation and/or other materials provided with the distribution." + "\n" + - " *" + "\n" + - " * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY" + "\n" + - " * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE" + "\n" + - " * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR" + "\n" + - " * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR" + "\n" + - " * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL," + "\n" + - " * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO," + "\n" + - " * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR" + "\n" + - " * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY" + "\n" + - " * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" + "\n" + - " * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" + "\n" + - " * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. " + "\n" + - " */"); - -print(copyright); -print(); -print("// DO NOT EDIT! - this file autogenerated by YarrCanonicalizeUCS2.js"); -print(); -print('#include "config.h"'); -print('#include "YarrCanonicalizeUCS2.h"'); -print(); -print("namespace JSC { namespace Yarr {"); -print(); -print("#include "); -print(); - -for (i in characterSetInfo) { - var characters = "" - var set = characterSetInfo[i]; - for (var j in set) - characters += hex(set[j]) + ", "; - print("uint16_t ucs2CharacterSet" + i + "[] = { " + characters + "0 };"); -} -print(); -print("static const size_t UCS2_CANONICALIZATION_SETS = " + characterSetInfo.length + ";"); -print("uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = {"); -for (i in characterSetInfo) -print(" ucs2CharacterSet" + i + ","); -print("};"); -print(); -print("const size_t UCS2_CANONICALIZATION_RANGES = " + rangeInfo.length + ";"); -print("UCS2CanonicalizationRange rangeInfo[UCS2_CANONICALIZATION_RANGES] = {"); -for (i in rangeInfo) { - var info = rangeInfo[i]; - var typeAndValue = info.type.split(':'); - print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },"); -} -print("};"); -print(); -print("const size_t LATIN_CANONICALIZATION_RANGES = " + latinRangeInfo.length + ";"); -print("LatinCanonicalizationRange latinRangeInfo[LATIN_CANONICALIZATION_RANGES] = {"); -for (i in latinRangeInfo) { - var info = latinRangeInfo[i]; - var typeAndValue = info.type.split(':'); - print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },"); -} -print("};"); -print(); -print("} } // JSC::Yarr"); -print(); - diff --git a/masm/yarr/YarrInterpreter.cpp b/masm/yarr/YarrInterpreter.cpp deleted file mode 100644 index 31603f6d34..0000000000 --- a/masm/yarr/YarrInterpreter.cpp +++ /dev/null @@ -1,1964 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "YarrInterpreter.h" - -#include "Yarr.h" -#include "YarrCanonicalizeUCS2.h" -#include -#include -#include -#include - -#ifndef NDEBUG -#include -#endif - -using namespace WTF; - -namespace JSC { namespace Yarr { - -template -class Interpreter { -public: - struct ParenthesesDisjunctionContext; - - struct BackTrackInfoPatternCharacter { - uintptr_t matchAmount; - }; - struct BackTrackInfoCharacterClass { - uintptr_t matchAmount; - }; - struct BackTrackInfoBackReference { - uintptr_t begin; // Not really needed for greedy quantifiers. - uintptr_t matchAmount; // Not really needed for fixed quantifiers. - }; - struct BackTrackInfoAlternative { - uintptr_t offset; - }; - struct BackTrackInfoParentheticalAssertion { - uintptr_t begin; - }; - struct BackTrackInfoParenthesesOnce { - uintptr_t begin; - }; - struct BackTrackInfoParenthesesTerminal { - uintptr_t begin; - }; - struct BackTrackInfoParentheses { - uintptr_t matchAmount; - ParenthesesDisjunctionContext* lastContext; - }; - - static inline void appendParenthesesDisjunctionContext(BackTrackInfoParentheses* backTrack, ParenthesesDisjunctionContext* context) - { - context->next = backTrack->lastContext; - backTrack->lastContext = context; - ++backTrack->matchAmount; - } - - static inline void popParenthesesDisjunctionContext(BackTrackInfoParentheses* backTrack) - { - ASSERT(backTrack->matchAmount); - ASSERT(backTrack->lastContext); - backTrack->lastContext = backTrack->lastContext->next; - --backTrack->matchAmount; - } - - struct DisjunctionContext - { - DisjunctionContext() - : term(0) - { - } - - void* operator new(size_t, void* where) - { - return where; - } - - int term; - unsigned matchBegin; - unsigned matchEnd; - uintptr_t frame[1]; - }; - - DisjunctionContext* allocDisjunctionContext(ByteDisjunction* disjunction) - { - size_t size = sizeof(DisjunctionContext) - sizeof(uintptr_t) + disjunction->m_frameSize * sizeof(uintptr_t); - allocatorPool = allocatorPool->ensureCapacity(size); - if (!allocatorPool) - CRASH(); - return new (allocatorPool->alloc(size)) DisjunctionContext(); - } - - void freeDisjunctionContext(DisjunctionContext* context) - { - allocatorPool = allocatorPool->dealloc(context); - } - - struct ParenthesesDisjunctionContext - { - ParenthesesDisjunctionContext(unsigned* output, ByteTerm& term) - : next(0) - { - unsigned firstSubpatternId = term.atom.subpatternId; - unsigned numNestedSubpatterns = term.atom.parenthesesDisjunction->m_numSubpatterns; - - for (unsigned i = 0; i < (numNestedSubpatterns << 1); ++i) { - subpatternBackup[i] = output[(firstSubpatternId << 1) + i]; - output[(firstSubpatternId << 1) + i] = offsetNoMatch; - } - - new (getDisjunctionContext(term)) DisjunctionContext(); - } - - void* operator new(size_t, void* where) - { - return where; - } - - void restoreOutput(unsigned* output, unsigned firstSubpatternId, unsigned numNestedSubpatterns) - { - for (unsigned i = 0; i < (numNestedSubpatterns << 1); ++i) - output[(firstSubpatternId << 1) + i] = subpatternBackup[i]; - } - - DisjunctionContext* getDisjunctionContext(ByteTerm& term) - { - return reinterpret_cast(&(subpatternBackup[term.atom.parenthesesDisjunction->m_numSubpatterns << 1])); - } - - ParenthesesDisjunctionContext* next; - unsigned subpatternBackup[1]; - }; - - ParenthesesDisjunctionContext* allocParenthesesDisjunctionContext(ByteDisjunction* disjunction, unsigned* output, ByteTerm& term) - { - size_t size = sizeof(ParenthesesDisjunctionContext) - sizeof(unsigned) + (term.atom.parenthesesDisjunction->m_numSubpatterns << 1) * sizeof(unsigned) + sizeof(DisjunctionContext) - sizeof(uintptr_t) + disjunction->m_frameSize * sizeof(uintptr_t); - allocatorPool = allocatorPool->ensureCapacity(size); - if (!allocatorPool) - CRASH(); - return new (allocatorPool->alloc(size)) ParenthesesDisjunctionContext(output, term); - } - - void freeParenthesesDisjunctionContext(ParenthesesDisjunctionContext* context) - { - allocatorPool = allocatorPool->dealloc(context); - } - - class InputStream { - public: - InputStream(const CharType* input, unsigned start, unsigned length) - : input(input) - , pos(start) - , length(length) - { - } - - void next() - { - ++pos; - } - - void rewind(unsigned amount) - { - ASSERT(pos >= amount); - pos -= amount; - } - - int read() - { - ASSERT(pos < length); - if (pos < length) - return input[pos]; - return -1; - } - - int readPair() - { - ASSERT(pos + 1 < length); - return input[pos] | input[pos + 1] << 16; - } - - int readChecked(unsigned negativePositionOffest) - { - if (pos < negativePositionOffest) - CRASH(); - unsigned p = pos - negativePositionOffest; - ASSERT(p < length); - return input[p]; - } - - int reread(unsigned from) - { - ASSERT(from < length); - return input[from]; - } - - int prev() - { - ASSERT(!(pos > length)); - if (pos && length) - return input[pos - 1]; - return -1; - } - - unsigned getPos() - { - return pos; - } - - void setPos(unsigned p) - { - pos = p; - } - - bool atStart() - { - return pos == 0; - } - - bool atEnd() - { - return pos == length; - } - - unsigned end() - { - return length; - } - - bool checkInput(unsigned count) - { - if (((pos + count) <= length) && ((pos + count) >= pos)) { - pos += count; - return true; - } - return false; - } - - void uncheckInput(unsigned count) - { - if (pos < count) - CRASH(); - pos -= count; - } - - bool atStart(unsigned negativePositionOffest) - { - return pos == negativePositionOffest; - } - - bool atEnd(unsigned negativePositionOffest) - { - if (pos < negativePositionOffest) - CRASH(); - return (pos - negativePositionOffest) == length; - } - - bool isAvailableInput(unsigned offset) - { - return (((pos + offset) <= length) && ((pos + offset) >= pos)); - } - - private: - const CharType* input; - unsigned pos; - unsigned length; - }; - - bool testCharacterClass(CharacterClass* characterClass, int ch) - { - if (ch & 0xFF80) { - for (unsigned i = 0; i < characterClass->m_matchesUnicode.size(); ++i) - if (ch == characterClass->m_matchesUnicode[i]) - return true; - for (unsigned i = 0; i < characterClass->m_rangesUnicode.size(); ++i) - if ((ch >= characterClass->m_rangesUnicode[i].begin) && (ch <= characterClass->m_rangesUnicode[i].end)) - return true; - } else { - for (unsigned i = 0; i < characterClass->m_matches.size(); ++i) - if (ch == characterClass->m_matches[i]) - return true; - for (unsigned i = 0; i < characterClass->m_ranges.size(); ++i) - if ((ch >= characterClass->m_ranges[i].begin) && (ch <= characterClass->m_ranges[i].end)) - return true; - } - - return false; - } - - bool checkCharacter(int testChar, unsigned negativeInputOffset) - { - return testChar == input.readChecked(negativeInputOffset); - } - - bool checkCasedCharacter(int loChar, int hiChar, unsigned negativeInputOffset) - { - int ch = input.readChecked(negativeInputOffset); - return (loChar == ch) || (hiChar == ch); - } - - bool checkCharacterClass(CharacterClass* characterClass, bool invert, unsigned negativeInputOffset) - { - bool match = testCharacterClass(characterClass, input.readChecked(negativeInputOffset)); - return invert ? !match : match; - } - - bool tryConsumeBackReference(int matchBegin, int matchEnd, unsigned negativeInputOffset) - { - unsigned matchSize = (unsigned)(matchEnd - matchBegin); - - if (!input.checkInput(matchSize)) - return false; - - if (pattern->m_ignoreCase) { - for (unsigned i = 0; i < matchSize; ++i) { - int oldCh = input.reread(matchBegin + i); - int ch = input.readChecked(negativeInputOffset + matchSize - i); - - if (oldCh == ch) - continue; - - // The definition for canonicalize (see ES 5.1, 15.10.2.8) means that - // unicode values are never allowed to match against ascii ones. - if (isASCII(oldCh) || isASCII(ch)) { - if (toASCIIUpper(oldCh) == toASCIIUpper(ch)) - continue; - } else if (areCanonicallyEquivalent(oldCh, ch)) - continue; - - input.uncheckInput(matchSize); - return false; - } - } else { - for (unsigned i = 0; i < matchSize; ++i) { - if (!checkCharacter(input.reread(matchBegin + i), negativeInputOffset + matchSize - i)) { - input.uncheckInput(matchSize); - return false; - } - } - } - - return true; - } - - bool matchAssertionBOL(ByteTerm& term) - { - return (input.atStart(term.inputPosition)) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition + 1))); - } - - bool matchAssertionEOL(ByteTerm& term) - { - if (term.inputPosition) - return (input.atEnd(term.inputPosition)) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition))); - - return (input.atEnd()) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.read())); - } - - bool matchAssertionWordBoundary(ByteTerm& term) - { - bool prevIsWordchar = !input.atStart(term.inputPosition) && testCharacterClass(pattern->wordcharCharacterClass, input.readChecked(term.inputPosition + 1)); - bool readIsWordchar; - if (term.inputPosition) - readIsWordchar = !input.atEnd(term.inputPosition) && testCharacterClass(pattern->wordcharCharacterClass, input.readChecked(term.inputPosition)); - else - readIsWordchar = !input.atEnd() && testCharacterClass(pattern->wordcharCharacterClass, input.read()); - - bool wordBoundary = prevIsWordchar != readIsWordchar; - return term.invert() ? !wordBoundary : wordBoundary; - } - - bool backtrackPatternCharacter(ByteTerm& term, DisjunctionContext* context) - { - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - switch (term.atom.quantityType) { - case QuantifierFixedCount: - break; - - case QuantifierGreedy: - if (backTrack->matchAmount) { - --backTrack->matchAmount; - input.uncheckInput(1); - return true; - } - break; - - case QuantifierNonGreedy: - if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { - ++backTrack->matchAmount; - if (checkCharacter(term.atom.patternCharacter, term.inputPosition + 1)) - return true; - } - input.uncheckInput(backTrack->matchAmount); - break; - } - - return false; - } - - bool backtrackPatternCasedCharacter(ByteTerm& term, DisjunctionContext* context) - { - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - switch (term.atom.quantityType) { - case QuantifierFixedCount: - break; - - case QuantifierGreedy: - if (backTrack->matchAmount) { - --backTrack->matchAmount; - input.uncheckInput(1); - return true; - } - break; - - case QuantifierNonGreedy: - if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { - ++backTrack->matchAmount; - if (checkCasedCharacter(term.atom.casedCharacter.lo, term.atom.casedCharacter.hi, term.inputPosition + 1)) - return true; - } - input.uncheckInput(backTrack->matchAmount); - break; - } - - return false; - } - - bool matchCharacterClass(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeCharacterClass); - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - switch (term.atom.quantityType) { - case QuantifierFixedCount: { - for (unsigned matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) { - if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition - matchAmount)) - return false; - } - return true; - } - - case QuantifierGreedy: { - unsigned matchAmount = 0; - while ((matchAmount < term.atom.quantityCount) && input.checkInput(1)) { - if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) { - input.uncheckInput(1); - break; - } - ++matchAmount; - } - backTrack->matchAmount = matchAmount; - - return true; - } - - case QuantifierNonGreedy: - backTrack->matchAmount = 0; - return true; - } - - ASSERT_NOT_REACHED(); - return false; - } - - bool backtrackCharacterClass(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeCharacterClass); - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - switch (term.atom.quantityType) { - case QuantifierFixedCount: - break; - - case QuantifierGreedy: - if (backTrack->matchAmount) { - --backTrack->matchAmount; - input.uncheckInput(1); - return true; - } - break; - - case QuantifierNonGreedy: - if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { - ++backTrack->matchAmount; - if (checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) - return true; - } - input.uncheckInput(backTrack->matchAmount); - break; - } - - return false; - } - - bool matchBackReference(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeBackReference); - BackTrackInfoBackReference* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - unsigned matchBegin = output[(term.atom.subpatternId << 1)]; - unsigned matchEnd = output[(term.atom.subpatternId << 1) + 1]; - - // If the end position of the referenced match hasn't set yet then the backreference in the same parentheses where it references to that. - // In this case the result of match is empty string like when it references to a parentheses with zero-width match. - // Eg.: /(a\1)/ - if (matchEnd == offsetNoMatch) - return true; - - if (matchBegin == offsetNoMatch) - return true; - - ASSERT(matchBegin <= matchEnd); - - if (matchBegin == matchEnd) - return true; - - switch (term.atom.quantityType) { - case QuantifierFixedCount: { - backTrack->begin = input.getPos(); - for (unsigned matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) { - if (!tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) { - input.setPos(backTrack->begin); - return false; - } - } - return true; - } - - case QuantifierGreedy: { - unsigned matchAmount = 0; - while ((matchAmount < term.atom.quantityCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) - ++matchAmount; - backTrack->matchAmount = matchAmount; - return true; - } - - case QuantifierNonGreedy: - backTrack->begin = input.getPos(); - backTrack->matchAmount = 0; - return true; - } - - ASSERT_NOT_REACHED(); - return false; - } - - bool backtrackBackReference(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeBackReference); - BackTrackInfoBackReference* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - unsigned matchBegin = output[(term.atom.subpatternId << 1)]; - unsigned matchEnd = output[(term.atom.subpatternId << 1) + 1]; - - if (matchBegin == offsetNoMatch) - return false; - - ASSERT(matchBegin <= matchEnd); - - if (matchBegin == matchEnd) - return false; - - switch (term.atom.quantityType) { - case QuantifierFixedCount: - // for quantityCount == 1, could rewind. - input.setPos(backTrack->begin); - break; - - case QuantifierGreedy: - if (backTrack->matchAmount) { - --backTrack->matchAmount; - input.rewind(matchEnd - matchBegin); - return true; - } - break; - - case QuantifierNonGreedy: - if ((backTrack->matchAmount < term.atom.quantityCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) { - ++backTrack->matchAmount; - return true; - } - input.setPos(backTrack->begin); - break; - } - - return false; - } - - void recordParenthesesMatch(ByteTerm& term, ParenthesesDisjunctionContext* context) - { - if (term.capture()) { - unsigned subpatternId = term.atom.subpatternId; - output[(subpatternId << 1)] = context->getDisjunctionContext(term)->matchBegin + term.inputPosition; - output[(subpatternId << 1) + 1] = context->getDisjunctionContext(term)->matchEnd + term.inputPosition; - } - } - void resetMatches(ByteTerm& term, ParenthesesDisjunctionContext* context) - { - unsigned firstSubpatternId = term.atom.subpatternId; - unsigned count = term.atom.parenthesesDisjunction->m_numSubpatterns; - context->restoreOutput(output, firstSubpatternId, count); - } - JSRegExpResult parenthesesDoBacktrack(ByteTerm& term, BackTrackInfoParentheses* backTrack) - { - while (backTrack->matchAmount) { - ParenthesesDisjunctionContext* context = backTrack->lastContext; - - JSRegExpResult result = matchDisjunction(term.atom.parenthesesDisjunction, context->getDisjunctionContext(term), true); - if (result == JSRegExpMatch) - return JSRegExpMatch; - - resetMatches(term, context); - popParenthesesDisjunctionContext(backTrack); - freeParenthesesDisjunctionContext(context); - - if (result != JSRegExpNoMatch) - return result; - } - - return JSRegExpNoMatch; - } - - bool matchParenthesesOnceBegin(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceBegin); - ASSERT(term.atom.quantityCount == 1); - - BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - switch (term.atom.quantityType) { - case QuantifierGreedy: { - // set this speculatively; if we get to the parens end this will be true. - backTrack->begin = input.getPos(); - break; - } - case QuantifierNonGreedy: { - backTrack->begin = notFound; - context->term += term.atom.parenthesesWidth; - return true; - } - case QuantifierFixedCount: - break; - } - - if (term.capture()) { - unsigned subpatternId = term.atom.subpatternId; - output[(subpatternId << 1)] = input.getPos() - term.inputPosition; - } - - return true; - } - - bool matchParenthesesOnceEnd(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceEnd); - ASSERT(term.atom.quantityCount == 1); - - if (term.capture()) { - unsigned subpatternId = term.atom.subpatternId; - output[(subpatternId << 1) + 1] = input.getPos() + term.inputPosition; - } - - if (term.atom.quantityType == QuantifierFixedCount) - return true; - - BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); - return backTrack->begin != input.getPos(); - } - - bool backtrackParenthesesOnceBegin(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceBegin); - ASSERT(term.atom.quantityCount == 1); - - BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - if (term.capture()) { - unsigned subpatternId = term.atom.subpatternId; - output[(subpatternId << 1)] = offsetNoMatch; - output[(subpatternId << 1) + 1] = offsetNoMatch; - } - - switch (term.atom.quantityType) { - case QuantifierGreedy: - // if we backtrack to this point, there is another chance - try matching nothing. - ASSERT(backTrack->begin != notFound); - backTrack->begin = notFound; - context->term += term.atom.parenthesesWidth; - return true; - case QuantifierNonGreedy: - ASSERT(backTrack->begin != notFound); - case QuantifierFixedCount: - break; - } - - return false; - } - - bool backtrackParenthesesOnceEnd(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceEnd); - ASSERT(term.atom.quantityCount == 1); - - BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - switch (term.atom.quantityType) { - case QuantifierGreedy: - if (backTrack->begin == notFound) { - context->term -= term.atom.parenthesesWidth; - return false; - } - case QuantifierNonGreedy: - if (backTrack->begin == notFound) { - backTrack->begin = input.getPos(); - if (term.capture()) { - // Technically this access to inputPosition should be accessing the begin term's - // inputPosition, but for repeats other than fixed these values should be - // the same anyway! (We don't pre-check for greedy or non-greedy matches.) - ASSERT((&term - term.atom.parenthesesWidth)->type == ByteTerm::TypeParenthesesSubpatternOnceBegin); - ASSERT((&term - term.atom.parenthesesWidth)->inputPosition == term.inputPosition); - unsigned subpatternId = term.atom.subpatternId; - output[subpatternId << 1] = input.getPos() + term.inputPosition; - } - context->term -= term.atom.parenthesesWidth; - return true; - } - case QuantifierFixedCount: - break; - } - - return false; - } - - bool matchParenthesesTerminalBegin(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); - ASSERT(term.atom.quantityType == QuantifierGreedy); - ASSERT(term.atom.quantityCount == quantifyInfinite); - ASSERT(!term.capture()); - - BackTrackInfoParenthesesTerminal* backTrack = reinterpret_cast(context->frame + term.frameLocation); - backTrack->begin = input.getPos(); - return true; - } - - bool matchParenthesesTerminalEnd(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalEnd); - - BackTrackInfoParenthesesTerminal* backTrack = reinterpret_cast(context->frame + term.frameLocation); - // Empty match is a failed match. - if (backTrack->begin == input.getPos()) - return false; - - // Successful match! Okay, what's next? - loop around and try to match moar! - context->term -= (term.atom.parenthesesWidth + 1); - return true; - } - - bool backtrackParenthesesTerminalBegin(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); - ASSERT(term.atom.quantityType == QuantifierGreedy); - ASSERT(term.atom.quantityCount == quantifyInfinite); - ASSERT(!term.capture()); - - // If we backtrack to this point, we have failed to match this iteration of the parens. - // Since this is greedy / zero minimum a failed is also accepted as a match! - context->term += term.atom.parenthesesWidth; - return true; - } - - bool backtrackParenthesesTerminalEnd(ByteTerm&, DisjunctionContext*) - { - // 'Terminal' parentheses are at the end of the regex, and as such a match past end - // should always be returned as a successful match - we should never backtrack to here. - ASSERT_NOT_REACHED(); - return false; - } - - bool matchParentheticalAssertionBegin(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParentheticalAssertionBegin); - ASSERT(term.atom.quantityCount == 1); - - BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - backTrack->begin = input.getPos(); - return true; - } - - bool matchParentheticalAssertionEnd(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParentheticalAssertionEnd); - ASSERT(term.atom.quantityCount == 1); - - BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - input.setPos(backTrack->begin); - - // We've reached the end of the parens; if they are inverted, this is failure. - if (term.invert()) { - context->term -= term.atom.parenthesesWidth; - return false; - } - - return true; - } - - bool backtrackParentheticalAssertionBegin(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParentheticalAssertionBegin); - ASSERT(term.atom.quantityCount == 1); - - // We've failed to match parens; if they are inverted, this is win! - if (term.invert()) { - context->term += term.atom.parenthesesWidth; - return true; - } - - return false; - } - - bool backtrackParentheticalAssertionEnd(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParentheticalAssertionEnd); - ASSERT(term.atom.quantityCount == 1); - - BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - input.setPos(backTrack->begin); - - context->term -= term.atom.parenthesesWidth; - return false; - } - - JSRegExpResult matchParentheses(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpattern); - - BackTrackInfoParentheses* backTrack = reinterpret_cast(context->frame + term.frameLocation); - ByteDisjunction* disjunctionBody = term.atom.parenthesesDisjunction; - - backTrack->matchAmount = 0; - backTrack->lastContext = 0; - - switch (term.atom.quantityType) { - case QuantifierFixedCount: { - // While we haven't yet reached our fixed limit, - while (backTrack->matchAmount < term.atom.quantityCount) { - // Try to do a match, and it it succeeds, add it to the list. - ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); - JSRegExpResult result = matchDisjunction(disjunctionBody, context->getDisjunctionContext(term)); - if (result == JSRegExpMatch) - appendParenthesesDisjunctionContext(backTrack, context); - else { - // The match failed; try to find an alternate point to carry on from. - resetMatches(term, context); - freeParenthesesDisjunctionContext(context); - - if (result != JSRegExpNoMatch) - return result; - JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack); - if (backtrackResult != JSRegExpMatch) - return backtrackResult; - } - } - - ASSERT(backTrack->matchAmount == term.atom.quantityCount); - ParenthesesDisjunctionContext* context = backTrack->lastContext; - recordParenthesesMatch(term, context); - return JSRegExpMatch; - } - - case QuantifierGreedy: { - while (backTrack->matchAmount < term.atom.quantityCount) { - ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); - JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); - if (result == JSRegExpMatch) - appendParenthesesDisjunctionContext(backTrack, context); - else { - resetMatches(term, context); - freeParenthesesDisjunctionContext(context); - - if (result != JSRegExpNoMatch) - return result; - - break; - } - } - - if (backTrack->matchAmount) { - ParenthesesDisjunctionContext* context = backTrack->lastContext; - recordParenthesesMatch(term, context); - } - return JSRegExpMatch; - } - - case QuantifierNonGreedy: - return JSRegExpMatch; - } - - ASSERT_NOT_REACHED(); - return JSRegExpErrorNoMatch; - } - - // Rules for backtracking differ depending on whether this is greedy or non-greedy. - // - // Greedy matches never should try just adding more - you should already have done - // the 'more' cases. Always backtrack, at least a leetle bit. However cases where - // you backtrack an item off the list needs checking, since we'll never have matched - // the one less case. Tracking forwards, still add as much as possible. - // - // Non-greedy, we've already done the one less case, so don't match on popping. - // We haven't done the one more case, so always try to add that. - // - JSRegExpResult backtrackParentheses(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpattern); - - BackTrackInfoParentheses* backTrack = reinterpret_cast(context->frame + term.frameLocation); - ByteDisjunction* disjunctionBody = term.atom.parenthesesDisjunction; - - switch (term.atom.quantityType) { - case QuantifierFixedCount: { - ASSERT(backTrack->matchAmount == term.atom.quantityCount); - - ParenthesesDisjunctionContext* context = 0; - JSRegExpResult result = parenthesesDoBacktrack(term, backTrack); - - if (result != JSRegExpMatch) - return result; - - // While we haven't yet reached our fixed limit, - while (backTrack->matchAmount < term.atom.quantityCount) { - // Try to do a match, and it it succeeds, add it to the list. - context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); - result = matchDisjunction(disjunctionBody, context->getDisjunctionContext(term)); - - if (result == JSRegExpMatch) - appendParenthesesDisjunctionContext(backTrack, context); - else { - // The match failed; try to find an alternate point to carry on from. - resetMatches(term, context); - freeParenthesesDisjunctionContext(context); - - if (result != JSRegExpNoMatch) - return result; - JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack); - if (backtrackResult != JSRegExpMatch) - return backtrackResult; - } - } - - ASSERT(backTrack->matchAmount == term.atom.quantityCount); - context = backTrack->lastContext; - recordParenthesesMatch(term, context); - return JSRegExpMatch; - } - - case QuantifierGreedy: { - if (!backTrack->matchAmount) - return JSRegExpNoMatch; - - ParenthesesDisjunctionContext* context = backTrack->lastContext; - JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term), true); - if (result == JSRegExpMatch) { - while (backTrack->matchAmount < term.atom.quantityCount) { - ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); - JSRegExpResult parenthesesResult = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); - if (parenthesesResult == JSRegExpMatch) - appendParenthesesDisjunctionContext(backTrack, context); - else { - resetMatches(term, context); - freeParenthesesDisjunctionContext(context); - - if (parenthesesResult != JSRegExpNoMatch) - return parenthesesResult; - - break; - } - } - } else { - resetMatches(term, context); - popParenthesesDisjunctionContext(backTrack); - freeParenthesesDisjunctionContext(context); - - if (result != JSRegExpNoMatch) - return result; - } - - if (backTrack->matchAmount) { - ParenthesesDisjunctionContext* context = backTrack->lastContext; - recordParenthesesMatch(term, context); - } - return JSRegExpMatch; - } - - case QuantifierNonGreedy: { - // If we've not reached the limit, try to add one more match. - if (backTrack->matchAmount < term.atom.quantityCount) { - ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); - JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); - if (result == JSRegExpMatch) { - appendParenthesesDisjunctionContext(backTrack, context); - recordParenthesesMatch(term, context); - return JSRegExpMatch; - } - - resetMatches(term, context); - freeParenthesesDisjunctionContext(context); - - if (result != JSRegExpNoMatch) - return result; - } - - // Nope - okay backtrack looking for an alternative. - while (backTrack->matchAmount) { - ParenthesesDisjunctionContext* context = backTrack->lastContext; - JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term), true); - if (result == JSRegExpMatch) { - // successful backtrack! we're back in the game! - if (backTrack->matchAmount) { - context = backTrack->lastContext; - recordParenthesesMatch(term, context); - } - return JSRegExpMatch; - } - - // pop a match off the stack - resetMatches(term, context); - popParenthesesDisjunctionContext(backTrack); - freeParenthesesDisjunctionContext(context); - - if (result != JSRegExpNoMatch) - return result; - } - - return JSRegExpNoMatch; - } - } - - ASSERT_NOT_REACHED(); - return JSRegExpErrorNoMatch; - } - - bool matchDotStarEnclosure(ByteTerm& term, DisjunctionContext* context) - { - UNUSED_PARAM(term); - unsigned matchBegin = context->matchBegin; - - if (matchBegin) { - for (matchBegin--; true; matchBegin--) { - if (testCharacterClass(pattern->newlineCharacterClass, input.reread(matchBegin))) { - ++matchBegin; - break; - } - - if (!matchBegin) - break; - } - } - - unsigned matchEnd = input.getPos(); - - for (; (matchEnd != input.end()) - && (!testCharacterClass(pattern->newlineCharacterClass, input.reread(matchEnd))); matchEnd++) { } - - if (((matchBegin && term.anchors.m_bol) - || ((matchEnd != input.end()) && term.anchors.m_eol)) - && !pattern->m_multiline) - return false; - - context->matchBegin = matchBegin; - context->matchEnd = matchEnd; - return true; - } - -#define MATCH_NEXT() { ++context->term; goto matchAgain; } -#define BACKTRACK() { --context->term; goto backtrack; } -#define currentTerm() (disjunction->terms[context->term]) - JSRegExpResult matchDisjunction(ByteDisjunction* disjunction, DisjunctionContext* context, bool btrack = false) - { - if (!--remainingMatchCount) - return JSRegExpErrorHitLimit; - - if (btrack) - BACKTRACK(); - - context->matchBegin = input.getPos(); - context->term = 0; - - matchAgain: - ASSERT(context->term < static_cast(disjunction->terms.size())); - - switch (currentTerm().type) { - case ByteTerm::TypeSubpatternBegin: - MATCH_NEXT(); - case ByteTerm::TypeSubpatternEnd: - context->matchEnd = input.getPos(); - return JSRegExpMatch; - - case ByteTerm::TypeBodyAlternativeBegin: - MATCH_NEXT(); - case ByteTerm::TypeBodyAlternativeDisjunction: - case ByteTerm::TypeBodyAlternativeEnd: - context->matchEnd = input.getPos(); - return JSRegExpMatch; - - case ByteTerm::TypeAlternativeBegin: - MATCH_NEXT(); - case ByteTerm::TypeAlternativeDisjunction: - case ByteTerm::TypeAlternativeEnd: { - int offset = currentTerm().alternative.end; - BackTrackInfoAlternative* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); - backTrack->offset = offset; - context->term += offset; - MATCH_NEXT(); - } - - case ByteTerm::TypeAssertionBOL: - if (matchAssertionBOL(currentTerm())) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeAssertionEOL: - if (matchAssertionEOL(currentTerm())) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeAssertionWordBoundary: - if (matchAssertionWordBoundary(currentTerm())) - MATCH_NEXT(); - BACKTRACK(); - - case ByteTerm::TypePatternCharacterOnce: - case ByteTerm::TypePatternCharacterFixed: { - for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityCount; ++matchAmount) { - if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition - matchAmount)) - BACKTRACK(); - } - MATCH_NEXT(); - } - case ByteTerm::TypePatternCharacterGreedy: { - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); - unsigned matchAmount = 0; - while ((matchAmount < currentTerm().atom.quantityCount) && input.checkInput(1)) { - if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition + 1)) { - input.uncheckInput(1); - break; - } - ++matchAmount; - } - backTrack->matchAmount = matchAmount; - - MATCH_NEXT(); - } - case ByteTerm::TypePatternCharacterNonGreedy: { - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); - backTrack->matchAmount = 0; - MATCH_NEXT(); - } - - case ByteTerm::TypePatternCasedCharacterOnce: - case ByteTerm::TypePatternCasedCharacterFixed: { - for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityCount; ++matchAmount) { - if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition - matchAmount)) - BACKTRACK(); - } - MATCH_NEXT(); - } - case ByteTerm::TypePatternCasedCharacterGreedy: { - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); - unsigned matchAmount = 0; - while ((matchAmount < currentTerm().atom.quantityCount) && input.checkInput(1)) { - if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition + 1)) { - input.uncheckInput(1); - break; - } - ++matchAmount; - } - backTrack->matchAmount = matchAmount; - - MATCH_NEXT(); - } - case ByteTerm::TypePatternCasedCharacterNonGreedy: { - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); - backTrack->matchAmount = 0; - MATCH_NEXT(); - } - - case ByteTerm::TypeCharacterClass: - if (matchCharacterClass(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeBackReference: - if (matchBackReference(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpattern: { - JSRegExpResult result = matchParentheses(currentTerm(), context); - - if (result == JSRegExpMatch) { - MATCH_NEXT(); - } else if (result != JSRegExpNoMatch) - return result; - - BACKTRACK(); - } - case ByteTerm::TypeParenthesesSubpatternOnceBegin: - if (matchParenthesesOnceBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternOnceEnd: - if (matchParenthesesOnceEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternTerminalBegin: - if (matchParenthesesTerminalBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternTerminalEnd: - if (matchParenthesesTerminalEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParentheticalAssertionBegin: - if (matchParentheticalAssertionBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParentheticalAssertionEnd: - if (matchParentheticalAssertionEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - - case ByteTerm::TypeCheckInput: - if (input.checkInput(currentTerm().checkInputCount)) - MATCH_NEXT(); - BACKTRACK(); - - case ByteTerm::TypeUncheckInput: - input.uncheckInput(currentTerm().checkInputCount); - MATCH_NEXT(); - - case ByteTerm::TypeDotStarEnclosure: - if (matchDotStarEnclosure(currentTerm(), context)) - return JSRegExpMatch; - BACKTRACK(); - } - - // We should never fall-through to here. - ASSERT_NOT_REACHED(); - - backtrack: - ASSERT(context->term < static_cast(disjunction->terms.size())); - - switch (currentTerm().type) { - case ByteTerm::TypeSubpatternBegin: - return JSRegExpNoMatch; - case ByteTerm::TypeSubpatternEnd: - ASSERT_NOT_REACHED(); - - case ByteTerm::TypeBodyAlternativeBegin: - case ByteTerm::TypeBodyAlternativeDisjunction: { - int offset = currentTerm().alternative.next; - context->term += offset; - if (offset > 0) - MATCH_NEXT(); - - if (input.atEnd()) - return JSRegExpNoMatch; - - input.next(); - - context->matchBegin = input.getPos(); - - if (currentTerm().alternative.onceThrough) - context->term += currentTerm().alternative.next; - - MATCH_NEXT(); - } - case ByteTerm::TypeBodyAlternativeEnd: - ASSERT_NOT_REACHED(); - - case ByteTerm::TypeAlternativeBegin: - case ByteTerm::TypeAlternativeDisjunction: { - int offset = currentTerm().alternative.next; - context->term += offset; - if (offset > 0) - MATCH_NEXT(); - BACKTRACK(); - } - case ByteTerm::TypeAlternativeEnd: { - // We should never backtrack back into an alternative of the main body of the regex. - BackTrackInfoAlternative* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); - unsigned offset = backTrack->offset; - context->term -= offset; - BACKTRACK(); - } - - case ByteTerm::TypeAssertionBOL: - case ByteTerm::TypeAssertionEOL: - case ByteTerm::TypeAssertionWordBoundary: - BACKTRACK(); - - case ByteTerm::TypePatternCharacterOnce: - case ByteTerm::TypePatternCharacterFixed: - case ByteTerm::TypePatternCharacterGreedy: - case ByteTerm::TypePatternCharacterNonGreedy: - if (backtrackPatternCharacter(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypePatternCasedCharacterOnce: - case ByteTerm::TypePatternCasedCharacterFixed: - case ByteTerm::TypePatternCasedCharacterGreedy: - case ByteTerm::TypePatternCasedCharacterNonGreedy: - if (backtrackPatternCasedCharacter(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeCharacterClass: - if (backtrackCharacterClass(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeBackReference: - if (backtrackBackReference(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpattern: { - JSRegExpResult result = backtrackParentheses(currentTerm(), context); - - if (result == JSRegExpMatch) { - MATCH_NEXT(); - } else if (result != JSRegExpNoMatch) - return result; - - BACKTRACK(); - } - case ByteTerm::TypeParenthesesSubpatternOnceBegin: - if (backtrackParenthesesOnceBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternOnceEnd: - if (backtrackParenthesesOnceEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternTerminalBegin: - if (backtrackParenthesesTerminalBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternTerminalEnd: - if (backtrackParenthesesTerminalEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParentheticalAssertionBegin: - if (backtrackParentheticalAssertionBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParentheticalAssertionEnd: - if (backtrackParentheticalAssertionEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - - case ByteTerm::TypeCheckInput: - input.uncheckInput(currentTerm().checkInputCount); - BACKTRACK(); - - case ByteTerm::TypeUncheckInput: - input.checkInput(currentTerm().checkInputCount); - BACKTRACK(); - - case ByteTerm::TypeDotStarEnclosure: - ASSERT_NOT_REACHED(); - } - - ASSERT_NOT_REACHED(); - return JSRegExpErrorNoMatch; - } - - JSRegExpResult matchNonZeroDisjunction(ByteDisjunction* disjunction, DisjunctionContext* context, bool btrack = false) - { - JSRegExpResult result = matchDisjunction(disjunction, context, btrack); - - if (result == JSRegExpMatch) { - while (context->matchBegin == context->matchEnd) { - result = matchDisjunction(disjunction, context, true); - if (result != JSRegExpMatch) - return result; - } - return JSRegExpMatch; - } - - return result; - } - - unsigned interpret() - { - if (!input.isAvailableInput(0)) - return offsetNoMatch; - - for (unsigned i = 0; i < pattern->m_body->m_numSubpatterns + 1; ++i) - output[i << 1] = offsetNoMatch; - - allocatorPool = pattern->m_allocator->startAllocator(); - if (!allocatorPool) - CRASH(); - - DisjunctionContext* context = allocDisjunctionContext(pattern->m_body.get()); - - JSRegExpResult result = matchDisjunction(pattern->m_body.get(), context, false); - if (result == JSRegExpMatch) { - output[0] = context->matchBegin; - output[1] = context->matchEnd; - } - - freeDisjunctionContext(context); - - pattern->m_allocator->stopAllocator(); - - ASSERT((result == JSRegExpMatch) == (output[0] != offsetNoMatch)); - return output[0]; - } - - Interpreter(BytecodePattern* pattern, unsigned* output, const CharType* input, unsigned length, unsigned start) - : pattern(pattern) - , output(output) - , input(input, start, length) - , allocatorPool(0) - , remainingMatchCount(matchLimit) - { - } - -private: - BytecodePattern* pattern; - unsigned* output; - InputStream input; - BumpPointerPool* allocatorPool; - unsigned remainingMatchCount; -}; - - - -class ByteCompiler { - struct ParenthesesStackEntry { - unsigned beginTerm; - unsigned savedAlternativeIndex; - ParenthesesStackEntry(unsigned beginTerm, unsigned savedAlternativeIndex/*, unsigned subpatternId, bool capture = false*/) - : beginTerm(beginTerm) - , savedAlternativeIndex(savedAlternativeIndex) - { - } - }; - -public: - ByteCompiler(YarrPattern& pattern) - : m_pattern(pattern) - { - m_currentAlternativeIndex = 0; - } - - PassOwnPtr compile(BumpPointerAllocator* allocator) - { - regexBegin(m_pattern.m_numSubpatterns, m_pattern.m_body->m_callFrameSize, m_pattern.m_body->m_alternatives[0]->onceThrough()); - emitDisjunction(m_pattern.m_body); - regexEnd(); - - return adoptPtr(new BytecodePattern(m_bodyDisjunction.release(), m_allParenthesesInfo, m_pattern, allocator)); - } - - void checkInput(unsigned count) - { - m_bodyDisjunction->terms.append(ByteTerm::CheckInput(count)); - } - - void uncheckInput(unsigned count) - { - m_bodyDisjunction->terms.append(ByteTerm::UncheckInput(count)); - } - - void assertionBOL(unsigned inputPosition) - { - m_bodyDisjunction->terms.append(ByteTerm::BOL(inputPosition)); - } - - void assertionEOL(unsigned inputPosition) - { - m_bodyDisjunction->terms.append(ByteTerm::EOL(inputPosition)); - } - - void assertionWordBoundary(bool invert, unsigned inputPosition) - { - m_bodyDisjunction->terms.append(ByteTerm::WordBoundary(invert, inputPosition)); - } - - void atomPatternCharacter(UChar ch, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - { - if (m_pattern.m_ignoreCase) { - UChar lo = Unicode::toLower(ch); - UChar hi = Unicode::toUpper(ch); - - if (lo != hi) { - m_bodyDisjunction->terms.append(ByteTerm(lo, hi, inputPosition, frameLocation, quantityCount, quantityType)); - return; - } - } - - m_bodyDisjunction->terms.append(ByteTerm(ch, inputPosition, frameLocation, quantityCount, quantityType)); - } - - void atomCharacterClass(CharacterClass* characterClass, bool invert, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - { - m_bodyDisjunction->terms.append(ByteTerm(characterClass, invert, inputPosition)); - - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType; - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; - } - - void atomBackReference(unsigned subpatternId, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - { - ASSERT(subpatternId); - - m_bodyDisjunction->terms.append(ByteTerm::BackReference(subpatternId, inputPosition)); - - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType; - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; - } - - void atomParenthesesOnceBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) - { - int beginTerm = m_bodyDisjunction->terms.size(); - - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceBegin, subpatternId, capture, false, inputPosition)); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; - m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; - - m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); - m_currentAlternativeIndex = beginTerm + 1; - } - - void atomParenthesesTerminalBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) - { - int beginTerm = m_bodyDisjunction->terms.size(); - - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternTerminalBegin, subpatternId, capture, false, inputPosition)); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; - m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; - - m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); - m_currentAlternativeIndex = beginTerm + 1; - } - - void atomParenthesesSubpatternBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) - { - // Errrk! - this is a little crazy, we initially generate as a TypeParenthesesSubpatternOnceBegin, - // then fix this up at the end! - simplifying this should make it much clearer. - // https://bugs.webkit.org/show_bug.cgi?id=50136 - - int beginTerm = m_bodyDisjunction->terms.size(); - - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceBegin, subpatternId, capture, false, inputPosition)); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; - m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; - - m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); - m_currentAlternativeIndex = beginTerm + 1; - } - - void atomParentheticalAssertionBegin(unsigned subpatternId, bool invert, unsigned frameLocation, unsigned alternativeFrameLocation) - { - int beginTerm = m_bodyDisjunction->terms.size(); - - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParentheticalAssertionBegin, subpatternId, false, invert, 0)); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; - m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; - - m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); - m_currentAlternativeIndex = beginTerm + 1; - } - - void atomParentheticalAssertionEnd(unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - { - unsigned beginTerm = popParenthesesStack(); - closeAlternative(beginTerm + 1); - unsigned endTerm = m_bodyDisjunction->terms.size(); - - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParentheticalAssertionBegin); - - bool invert = m_bodyDisjunction->terms[beginTerm].invert(); - unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; - - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParentheticalAssertionEnd, subpatternId, false, invert, inputPosition)); - m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; - m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; - m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; - - m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; - m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; - } - - void assertionDotStarEnclosure(bool bolAnchored, bool eolAnchored) - { - m_bodyDisjunction->terms.append(ByteTerm::DotStarEnclosure(bolAnchored, eolAnchored)); - } - - unsigned popParenthesesStack() - { - ASSERT(m_parenthesesStack.size()); - int stackEnd = m_parenthesesStack.size() - 1; - unsigned beginTerm = m_parenthesesStack[stackEnd].beginTerm; - m_currentAlternativeIndex = m_parenthesesStack[stackEnd].savedAlternativeIndex; - m_parenthesesStack.shrink(stackEnd); - - ASSERT(beginTerm < m_bodyDisjunction->terms.size()); - ASSERT(m_currentAlternativeIndex < m_bodyDisjunction->terms.size()); - - return beginTerm; - } - -#ifndef NDEBUG - void dumpDisjunction(ByteDisjunction* disjunction) - { - dataLogF("ByteDisjunction(%p):\n\t", disjunction); - for (unsigned i = 0; i < disjunction->terms.size(); ++i) - dataLogF("{ %d } ", disjunction->terms[i].type); - dataLogF("\n"); - } -#endif - - void closeAlternative(int beginTerm) - { - int origBeginTerm = beginTerm; - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeAlternativeBegin); - int endIndex = m_bodyDisjunction->terms.size(); - - unsigned frameLocation = m_bodyDisjunction->terms[beginTerm].frameLocation; - - if (!m_bodyDisjunction->terms[beginTerm].alternative.next) - m_bodyDisjunction->terms.remove(beginTerm); - else { - while (m_bodyDisjunction->terms[beginTerm].alternative.next) { - beginTerm += m_bodyDisjunction->terms[beginTerm].alternative.next; - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeAlternativeDisjunction); - m_bodyDisjunction->terms[beginTerm].alternative.end = endIndex - beginTerm; - m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; - } - - m_bodyDisjunction->terms[beginTerm].alternative.next = origBeginTerm - beginTerm; - - m_bodyDisjunction->terms.append(ByteTerm::AlternativeEnd()); - m_bodyDisjunction->terms[endIndex].frameLocation = frameLocation; - } - } - - void closeBodyAlternative() - { - int beginTerm = 0; - int origBeginTerm = 0; - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeBodyAlternativeBegin); - int endIndex = m_bodyDisjunction->terms.size(); - - unsigned frameLocation = m_bodyDisjunction->terms[beginTerm].frameLocation; - - while (m_bodyDisjunction->terms[beginTerm].alternative.next) { - beginTerm += m_bodyDisjunction->terms[beginTerm].alternative.next; - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeBodyAlternativeDisjunction); - m_bodyDisjunction->terms[beginTerm].alternative.end = endIndex - beginTerm; - m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; - } - - m_bodyDisjunction->terms[beginTerm].alternative.next = origBeginTerm - beginTerm; - - m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeEnd()); - m_bodyDisjunction->terms[endIndex].frameLocation = frameLocation; - } - - void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType, unsigned callFrameSize = 0) - { - unsigned beginTerm = popParenthesesStack(); - closeAlternative(beginTerm + 1); - unsigned endTerm = m_bodyDisjunction->terms.size(); - - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternOnceBegin); - - ByteTerm& parenthesesBegin = m_bodyDisjunction->terms[beginTerm]; - - bool capture = parenthesesBegin.capture(); - unsigned subpatternId = parenthesesBegin.atom.subpatternId; - - unsigned numSubpatterns = lastSubpatternId - subpatternId + 1; - ByteDisjunction* parenthesesDisjunction = new ByteDisjunction(numSubpatterns, callFrameSize); - - parenthesesDisjunction->terms.append(ByteTerm::SubpatternBegin()); - for (unsigned termInParentheses = beginTerm + 1; termInParentheses < endTerm; ++termInParentheses) - parenthesesDisjunction->terms.append(m_bodyDisjunction->terms[termInParentheses]); - parenthesesDisjunction->terms.append(ByteTerm::SubpatternEnd()); - - m_bodyDisjunction->terms.shrink(beginTerm); - - m_allParenthesesInfo.append(parenthesesDisjunction); - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture, inputPosition)); - - m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; - m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; - } - - void atomParenthesesOnceEnd(int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - { - unsigned beginTerm = popParenthesesStack(); - closeAlternative(beginTerm + 1); - unsigned endTerm = m_bodyDisjunction->terms.size(); - - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternOnceBegin); - - bool capture = m_bodyDisjunction->terms[beginTerm].capture(); - unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; - - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceEnd, subpatternId, capture, false, inputPosition)); - m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; - m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; - m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; - - m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; - m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; - } - - void atomParenthesesTerminalEnd(int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - { - unsigned beginTerm = popParenthesesStack(); - closeAlternative(beginTerm + 1); - unsigned endTerm = m_bodyDisjunction->terms.size(); - - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); - - bool capture = m_bodyDisjunction->terms[beginTerm].capture(); - unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; - - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternTerminalEnd, subpatternId, capture, false, inputPosition)); - m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; - m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; - m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; - - m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; - m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; - } - - void regexBegin(unsigned numSubpatterns, unsigned callFrameSize, bool onceThrough) - { - m_bodyDisjunction = adoptPtr(new ByteDisjunction(numSubpatterns, callFrameSize)); - m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeBegin(onceThrough)); - m_bodyDisjunction->terms[0].frameLocation = 0; - m_currentAlternativeIndex = 0; - } - - void regexEnd() - { - closeBodyAlternative(); - } - - void alternativeBodyDisjunction(bool onceThrough) - { - int newAlternativeIndex = m_bodyDisjunction->terms.size(); - m_bodyDisjunction->terms[m_currentAlternativeIndex].alternative.next = newAlternativeIndex - m_currentAlternativeIndex; - m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeDisjunction(onceThrough)); - - m_currentAlternativeIndex = newAlternativeIndex; - } - - void alternativeDisjunction() - { - int newAlternativeIndex = m_bodyDisjunction->terms.size(); - m_bodyDisjunction->terms[m_currentAlternativeIndex].alternative.next = newAlternativeIndex - m_currentAlternativeIndex; - m_bodyDisjunction->terms.append(ByteTerm::AlternativeDisjunction()); - - m_currentAlternativeIndex = newAlternativeIndex; - } - - void emitDisjunction(PatternDisjunction* disjunction, unsigned inputCountAlreadyChecked = 0, unsigned parenthesesInputCountAlreadyChecked = 0) - { - for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { - unsigned currentCountAlreadyChecked = inputCountAlreadyChecked; - - PatternAlternative* alternative = disjunction->m_alternatives[alt]; - - if (alt) { - if (disjunction == m_pattern.m_body) - alternativeBodyDisjunction(alternative->onceThrough()); - else - alternativeDisjunction(); - } - - unsigned minimumSize = alternative->m_minimumSize; - ASSERT(minimumSize >= parenthesesInputCountAlreadyChecked); - unsigned countToCheck = minimumSize - parenthesesInputCountAlreadyChecked; - - if (countToCheck) { - checkInput(countToCheck); - currentCountAlreadyChecked += countToCheck; - } - - for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { - PatternTerm& term = alternative->m_terms[i]; - - switch (term.type) { - case PatternTerm::TypeAssertionBOL: - assertionBOL(currentCountAlreadyChecked - term.inputPosition); - break; - - case PatternTerm::TypeAssertionEOL: - assertionEOL(currentCountAlreadyChecked - term.inputPosition); - break; - - case PatternTerm::TypeAssertionWordBoundary: - assertionWordBoundary(term.invert(), currentCountAlreadyChecked - term.inputPosition); - break; - - case PatternTerm::TypePatternCharacter: - atomPatternCharacter(term.patternCharacter, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); - break; - - case PatternTerm::TypeCharacterClass: - atomCharacterClass(term.characterClass, term.invert(), currentCountAlreadyChecked- term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); - break; - - case PatternTerm::TypeBackReference: - atomBackReference(term.backReferenceSubpatternId, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); - break; - - case PatternTerm::TypeForwardReference: - break; - - case PatternTerm::TypeParenthesesSubpattern: { - unsigned disjunctionAlreadyCheckedCount = 0; - if (term.quantityCount == 1 && !term.parentheses.isCopy) { - unsigned alternativeFrameLocation = term.frameLocation; - // For QuantifierFixedCount we pre-check the minimum size; for greedy/non-greedy we reserve a slot in the frame. - if (term.quantityType == QuantifierFixedCount) - disjunctionAlreadyCheckedCount = term.parentheses.disjunction->m_minimumSize; - else - alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; - unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; - atomParenthesesOnceBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, alternativeFrameLocation); - emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount); - atomParenthesesOnceEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType); - } else if (term.parentheses.isTerminal) { - unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; - atomParenthesesTerminalBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, term.frameLocation + YarrStackSpaceForBackTrackInfoParenthesesOnce); - emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount); - atomParenthesesTerminalEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType); - } else { - unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; - atomParenthesesSubpatternBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, 0); - emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, 0); - atomParenthesesSubpatternEnd(term.parentheses.lastSubpatternId, delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType, term.parentheses.disjunction->m_callFrameSize); - } - break; - } - - case PatternTerm::TypeParentheticalAssertion: { - unsigned alternativeFrameLocation = term.frameLocation + YarrStackSpaceForBackTrackInfoParentheticalAssertion; - - ASSERT(currentCountAlreadyChecked >= static_cast(term.inputPosition)); - unsigned positiveInputOffset = currentCountAlreadyChecked - static_cast(term.inputPosition); - unsigned uncheckAmount = 0; - if (positiveInputOffset > term.parentheses.disjunction->m_minimumSize) { - uncheckAmount = positiveInputOffset - term.parentheses.disjunction->m_minimumSize; - uncheckInput(uncheckAmount); - currentCountAlreadyChecked -= uncheckAmount; - } - - atomParentheticalAssertionBegin(term.parentheses.subpatternId, term.invert(), term.frameLocation, alternativeFrameLocation); - emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, positiveInputOffset - uncheckAmount); - atomParentheticalAssertionEnd(0, term.frameLocation, term.quantityCount, term.quantityType); - if (uncheckAmount) { - checkInput(uncheckAmount); - currentCountAlreadyChecked += uncheckAmount; - } - break; - } - - case PatternTerm::TypeDotStarEnclosure: - assertionDotStarEnclosure(term.anchors.bolAnchor, term.anchors.eolAnchor); - break; - } - } - } - } - -private: - YarrPattern& m_pattern; - OwnPtr m_bodyDisjunction; - unsigned m_currentAlternativeIndex; - Vector m_parenthesesStack; - Vector m_allParenthesesInfo; -}; - -PassOwnPtr byteCompile(YarrPattern& pattern, BumpPointerAllocator* allocator) -{ - return ByteCompiler(pattern).compile(allocator); -} - -unsigned interpret(BytecodePattern* bytecode, const String& input, unsigned start, unsigned* output) -{ - if (input.is8Bit()) - return Interpreter(bytecode, output, input.characters8(), input.length(), start).interpret(); - return Interpreter(bytecode, output, input.characters16(), input.length(), start).interpret(); -} - -unsigned interpret(BytecodePattern* bytecode, const LChar* input, unsigned length, unsigned start, unsigned* output) -{ - return Interpreter(bytecode, output, input, length, start).interpret(); -} - -unsigned interpret(BytecodePattern* bytecode, const UChar* input, unsigned length, unsigned start, unsigned* output) -{ - return Interpreter(bytecode, output, input, length, start).interpret(); -} - -// These should be the same for both UChar & LChar. -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoPatternCharacter) == (YarrStackSpaceForBackTrackInfoPatternCharacter * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoPatternCharacter); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoCharacterClass) == (YarrStackSpaceForBackTrackInfoCharacterClass * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoCharacterClass); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoBackReference) == (YarrStackSpaceForBackTrackInfoBackReference * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoBackReference); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoAlternative) == (YarrStackSpaceForBackTrackInfoAlternative * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoAlternative); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParentheticalAssertion) == (YarrStackSpaceForBackTrackInfoParentheticalAssertion * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheticalAssertion); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParenthesesOnce) == (YarrStackSpaceForBackTrackInfoParenthesesOnce * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParenthesesOnce); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParentheses) == (YarrStackSpaceForBackTrackInfoParentheses * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheses); - - -} } diff --git a/masm/yarr/YarrInterpreter.h b/masm/yarr/YarrInterpreter.h deleted file mode 100644 index fb60bd979d..0000000000 --- a/masm/yarr/YarrInterpreter.h +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef YarrInterpreter_h -#define YarrInterpreter_h - -#include "YarrPattern.h" -#include -#include - -namespace WTF { -class BumpPointerAllocator; -} -using WTF::BumpPointerAllocator; - -namespace JSC { namespace Yarr { - -class ByteDisjunction; - -struct ByteTerm { - enum Type { - TypeBodyAlternativeBegin, - TypeBodyAlternativeDisjunction, - TypeBodyAlternativeEnd, - TypeAlternativeBegin, - TypeAlternativeDisjunction, - TypeAlternativeEnd, - TypeSubpatternBegin, - TypeSubpatternEnd, - TypeAssertionBOL, - TypeAssertionEOL, - TypeAssertionWordBoundary, - TypePatternCharacterOnce, - TypePatternCharacterFixed, - TypePatternCharacterGreedy, - TypePatternCharacterNonGreedy, - TypePatternCasedCharacterOnce, - TypePatternCasedCharacterFixed, - TypePatternCasedCharacterGreedy, - TypePatternCasedCharacterNonGreedy, - TypeCharacterClass, - TypeBackReference, - TypeParenthesesSubpattern, - TypeParenthesesSubpatternOnceBegin, - TypeParenthesesSubpatternOnceEnd, - TypeParenthesesSubpatternTerminalBegin, - TypeParenthesesSubpatternTerminalEnd, - TypeParentheticalAssertionBegin, - TypeParentheticalAssertionEnd, - TypeCheckInput, - TypeUncheckInput, - TypeDotStarEnclosure, - } type; - union { - struct { - union { - UChar patternCharacter; - struct { - UChar lo; - UChar hi; - } casedCharacter; - CharacterClass* characterClass; - unsigned subpatternId; - }; - union { - ByteDisjunction* parenthesesDisjunction; - unsigned parenthesesWidth; - }; - QuantifierType quantityType; - unsigned quantityCount; - } atom; - struct { - int next; - int end; - bool onceThrough; - } alternative; - struct { - bool m_bol : 1; - bool m_eol : 1; - } anchors; - unsigned checkInputCount; - }; - unsigned frameLocation; - bool m_capture : 1; - bool m_invert : 1; - unsigned inputPosition; - - ByteTerm(UChar ch, int inputPos, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - : frameLocation(frameLocation) - , m_capture(false) - , m_invert(false) - { - switch (quantityType) { - case QuantifierFixedCount: - type = (quantityCount == 1) ? ByteTerm::TypePatternCharacterOnce : ByteTerm::TypePatternCharacterFixed; - break; - case QuantifierGreedy: - type = ByteTerm::TypePatternCharacterGreedy; - break; - case QuantifierNonGreedy: - type = ByteTerm::TypePatternCharacterNonGreedy; - break; - } - - atom.patternCharacter = ch; - atom.quantityType = quantityType; - atom.quantityCount = quantityCount.unsafeGet(); - inputPosition = inputPos; - } - - ByteTerm(UChar lo, UChar hi, int inputPos, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - : frameLocation(frameLocation) - , m_capture(false) - , m_invert(false) - { - switch (quantityType) { - case QuantifierFixedCount: - type = (quantityCount == 1) ? ByteTerm::TypePatternCasedCharacterOnce : ByteTerm::TypePatternCasedCharacterFixed; - break; - case QuantifierGreedy: - type = ByteTerm::TypePatternCasedCharacterGreedy; - break; - case QuantifierNonGreedy: - type = ByteTerm::TypePatternCasedCharacterNonGreedy; - break; - } - - atom.casedCharacter.lo = lo; - atom.casedCharacter.hi = hi; - atom.quantityType = quantityType; - atom.quantityCount = quantityCount.unsafeGet(); - inputPosition = inputPos; - } - - ByteTerm(CharacterClass* characterClass, bool invert, int inputPos) - : type(ByteTerm::TypeCharacterClass) - , m_capture(false) - , m_invert(invert) - { - atom.characterClass = characterClass; - atom.quantityType = QuantifierFixedCount; - atom.quantityCount = 1; - inputPosition = inputPos; - } - - ByteTerm(Type type, unsigned subpatternId, ByteDisjunction* parenthesesInfo, bool capture, int inputPos) - : type(type) - , m_capture(capture) - , m_invert(false) - { - atom.subpatternId = subpatternId; - atom.parenthesesDisjunction = parenthesesInfo; - atom.quantityType = QuantifierFixedCount; - atom.quantityCount = 1; - inputPosition = inputPos; - } - - ByteTerm(Type type, bool invert = false) - : type(type) - , m_capture(false) - , m_invert(invert) - { - atom.quantityType = QuantifierFixedCount; - atom.quantityCount = 1; - } - - ByteTerm(Type type, unsigned subpatternId, bool capture, bool invert, int inputPos) - : type(type) - , m_capture(capture) - , m_invert(invert) - { - atom.subpatternId = subpatternId; - atom.quantityType = QuantifierFixedCount; - atom.quantityCount = 1; - inputPosition = inputPos; - } - - static ByteTerm BOL(int inputPos) - { - ByteTerm term(TypeAssertionBOL); - term.inputPosition = inputPos; - return term; - } - - static ByteTerm CheckInput(Checked count) - { - ByteTerm term(TypeCheckInput); - term.checkInputCount = count.unsafeGet(); - return term; - } - - static ByteTerm UncheckInput(Checked count) - { - ByteTerm term(TypeUncheckInput); - term.checkInputCount = count.unsafeGet(); - return term; - } - - static ByteTerm EOL(int inputPos) - { - ByteTerm term(TypeAssertionEOL); - term.inputPosition = inputPos; - return term; - } - - static ByteTerm WordBoundary(bool invert, int inputPos) - { - ByteTerm term(TypeAssertionWordBoundary, invert); - term.inputPosition = inputPos; - return term; - } - - static ByteTerm BackReference(unsigned subpatternId, int inputPos) - { - return ByteTerm(TypeBackReference, subpatternId, false, false, inputPos); - } - - static ByteTerm BodyAlternativeBegin(bool onceThrough) - { - ByteTerm term(TypeBodyAlternativeBegin); - term.alternative.next = 0; - term.alternative.end = 0; - term.alternative.onceThrough = onceThrough; - return term; - } - - static ByteTerm BodyAlternativeDisjunction(bool onceThrough) - { - ByteTerm term(TypeBodyAlternativeDisjunction); - term.alternative.next = 0; - term.alternative.end = 0; - term.alternative.onceThrough = onceThrough; - return term; - } - - static ByteTerm BodyAlternativeEnd() - { - ByteTerm term(TypeBodyAlternativeEnd); - term.alternative.next = 0; - term.alternative.end = 0; - term.alternative.onceThrough = false; - return term; - } - - static ByteTerm AlternativeBegin() - { - ByteTerm term(TypeAlternativeBegin); - term.alternative.next = 0; - term.alternative.end = 0; - term.alternative.onceThrough = false; - return term; - } - - static ByteTerm AlternativeDisjunction() - { - ByteTerm term(TypeAlternativeDisjunction); - term.alternative.next = 0; - term.alternative.end = 0; - term.alternative.onceThrough = false; - return term; - } - - static ByteTerm AlternativeEnd() - { - ByteTerm term(TypeAlternativeEnd); - term.alternative.next = 0; - term.alternative.end = 0; - term.alternative.onceThrough = false; - return term; - } - - static ByteTerm SubpatternBegin() - { - return ByteTerm(TypeSubpatternBegin); - } - - static ByteTerm SubpatternEnd() - { - return ByteTerm(TypeSubpatternEnd); - } - - static ByteTerm DotStarEnclosure(bool bolAnchor, bool eolAnchor) - { - ByteTerm term(TypeDotStarEnclosure); - term.anchors.m_bol = bolAnchor; - term.anchors.m_eol = eolAnchor; - return term; - } - - bool invert() - { - return m_invert; - } - - bool capture() - { - return m_capture; - } -}; - -class ByteDisjunction { - WTF_MAKE_FAST_ALLOCATED; -public: - ByteDisjunction(unsigned numSubpatterns, unsigned frameSize) - : m_numSubpatterns(numSubpatterns) - , m_frameSize(frameSize) - { - } - - Vector terms; - unsigned m_numSubpatterns; - unsigned m_frameSize; -}; - -struct BytecodePattern { - WTF_MAKE_FAST_ALLOCATED; -public: - BytecodePattern(PassOwnPtr body, Vector allParenthesesInfo, YarrPattern& pattern, BumpPointerAllocator* allocator) - : m_body(body) - , m_ignoreCase(pattern.m_ignoreCase) - , m_multiline(pattern.m_multiline) - , m_allocator(allocator) - { - newlineCharacterClass = pattern.newlineCharacterClass(); - wordcharCharacterClass = pattern.wordcharCharacterClass(); - - m_allParenthesesInfo.append(allParenthesesInfo); - m_userCharacterClasses.append(pattern.m_userCharacterClasses); - // 'Steal' the YarrPattern's CharacterClasses! We clear its - // array, so that it won't delete them on destruction. We'll - // take responsibility for that. - pattern.m_userCharacterClasses.clear(); - } - - ~BytecodePattern() - { - deleteAllValues(m_allParenthesesInfo); - deleteAllValues(m_userCharacterClasses); - } - - OwnPtr m_body; - bool m_ignoreCase; - bool m_multiline; - // Each BytecodePattern is associated with a RegExp, each RegExp is associated - // with a JSGlobalData. Cache a pointer to out JSGlobalData's m_regExpAllocator. - BumpPointerAllocator* m_allocator; - - CharacterClass* newlineCharacterClass; - CharacterClass* wordcharCharacterClass; - -private: - Vector m_allParenthesesInfo; - Vector m_userCharacterClasses; -}; - -JS_EXPORT_PRIVATE PassOwnPtr byteCompile(YarrPattern&, BumpPointerAllocator*); -JS_EXPORT_PRIVATE unsigned interpret(BytecodePattern*, const String& input, unsigned start, unsigned* output); -unsigned interpret(BytecodePattern*, const LChar* input, unsigned length, unsigned start, unsigned* output); -unsigned interpret(BytecodePattern*, const UChar* input, unsigned length, unsigned start, unsigned* output); - -} } // namespace JSC::Yarr - -#endif // YarrInterpreter_h diff --git a/masm/yarr/YarrJIT.cpp b/masm/yarr/YarrJIT.cpp deleted file mode 100644 index ce84e2c74f..0000000000 --- a/masm/yarr/YarrJIT.cpp +++ /dev/null @@ -1,2667 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "YarrJIT.h" - -#include -#include "LinkBuffer.h" -#include "Options.h" -#include "Yarr.h" -#include "YarrCanonicalizeUCS2.h" - -#if ENABLE(YARR_JIT) - -using namespace WTF; - -namespace JSC { namespace Yarr { - -template -class YarrGenerator : private MacroAssembler { - friend void jitCompile(JSGlobalData*, YarrCodeBlock& jitObject, const String& pattern, unsigned& numSubpatterns, const char*& error, bool ignoreCase, bool multiline); - -#if CPU(ARM) - static const RegisterID input = ARMRegisters::r0; - static const RegisterID index = ARMRegisters::r1; - static const RegisterID length = ARMRegisters::r2; - static const RegisterID output = ARMRegisters::r4; - - static const RegisterID regT0 = ARMRegisters::r5; - static const RegisterID regT1 = ARMRegisters::r6; - - static const RegisterID returnRegister = ARMRegisters::r0; - static const RegisterID returnRegister2 = ARMRegisters::r1; -#elif CPU(MIPS) - static const RegisterID input = MIPSRegisters::a0; - static const RegisterID index = MIPSRegisters::a1; - static const RegisterID length = MIPSRegisters::a2; - static const RegisterID output = MIPSRegisters::a3; - - static const RegisterID regT0 = MIPSRegisters::t4; - static const RegisterID regT1 = MIPSRegisters::t5; - - static const RegisterID returnRegister = MIPSRegisters::v0; - static const RegisterID returnRegister2 = MIPSRegisters::v1; -#elif CPU(SH4) - static const RegisterID input = SH4Registers::r4; - static const RegisterID index = SH4Registers::r5; - static const RegisterID length = SH4Registers::r6; - static const RegisterID output = SH4Registers::r7; - - static const RegisterID regT0 = SH4Registers::r0; - static const RegisterID regT1 = SH4Registers::r1; - - static const RegisterID returnRegister = SH4Registers::r0; - static const RegisterID returnRegister2 = SH4Registers::r1; -#elif CPU(X86) - static const RegisterID input = X86Registers::eax; - static const RegisterID index = X86Registers::edx; - static const RegisterID length = X86Registers::ecx; - static const RegisterID output = X86Registers::edi; - - static const RegisterID regT0 = X86Registers::ebx; - static const RegisterID regT1 = X86Registers::esi; - - static const RegisterID returnRegister = X86Registers::eax; - static const RegisterID returnRegister2 = X86Registers::edx; -#elif CPU(X86_64) - static const RegisterID input = X86Registers::edi; - static const RegisterID index = X86Registers::esi; - static const RegisterID length = X86Registers::edx; - static const RegisterID output = X86Registers::ecx; - - static const RegisterID regT0 = X86Registers::eax; - static const RegisterID regT1 = X86Registers::ebx; - - static const RegisterID returnRegister = X86Registers::eax; - static const RegisterID returnRegister2 = X86Registers::edx; -#endif - - void optimizeAlternative(PatternAlternative* alternative) - { - if (!alternative->m_terms.size()) - return; - - for (unsigned i = 0; i < alternative->m_terms.size() - 1; ++i) { - PatternTerm& term = alternative->m_terms[i]; - PatternTerm& nextTerm = alternative->m_terms[i + 1]; - - if ((term.type == PatternTerm::TypeCharacterClass) - && (term.quantityType == QuantifierFixedCount) - && (nextTerm.type == PatternTerm::TypePatternCharacter) - && (nextTerm.quantityType == QuantifierFixedCount)) { - PatternTerm termCopy = term; - alternative->m_terms[i] = nextTerm; - alternative->m_terms[i + 1] = termCopy; - } - } - } - - void matchCharacterClassRange(RegisterID character, JumpList& failures, JumpList& matchDest, const CharacterRange* ranges, unsigned count, unsigned* matchIndex, const UChar* matches, unsigned matchCount) - { - do { - // pick which range we're going to generate - int which = count >> 1; - char lo = ranges[which].begin; - char hi = ranges[which].end; - - // check if there are any ranges or matches below lo. If not, just jl to failure - - // if there is anything else to check, check that first, if it falls through jmp to failure. - if ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) { - Jump loOrAbove = branch32(GreaterThanOrEqual, character, Imm32((unsigned short)lo)); - - // generate code for all ranges before this one - if (which) - matchCharacterClassRange(character, failures, matchDest, ranges, which, matchIndex, matches, matchCount); - - while ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) { - matchDest.append(branch32(Equal, character, Imm32((unsigned short)matches[*matchIndex]))); - ++*matchIndex; - } - failures.append(jump()); - - loOrAbove.link(this); - } else if (which) { - Jump loOrAbove = branch32(GreaterThanOrEqual, character, Imm32((unsigned short)lo)); - - matchCharacterClassRange(character, failures, matchDest, ranges, which, matchIndex, matches, matchCount); - failures.append(jump()); - - loOrAbove.link(this); - } else - failures.append(branch32(LessThan, character, Imm32((unsigned short)lo))); - - while ((*matchIndex < matchCount) && (matches[*matchIndex] <= hi)) - ++*matchIndex; - - matchDest.append(branch32(LessThanOrEqual, character, Imm32((unsigned short)hi))); - // fall through to here, the value is above hi. - - // shuffle along & loop around if there are any more matches to handle. - unsigned next = which + 1; - ranges += next; - count -= next; - } while (count); - } - - void matchCharacterClass(RegisterID character, JumpList& matchDest, const CharacterClass* charClass) - { - if (charClass->m_table) { - ExtendedAddress tableEntry(character, reinterpret_cast(charClass->m_table->m_table)); - matchDest.append(branchTest8(charClass->m_table->m_inverted ? Zero : NonZero, tableEntry)); - return; - } - Jump unicodeFail; - if (charClass->m_matchesUnicode.size() || charClass->m_rangesUnicode.size()) { - Jump isAscii = branch32(LessThanOrEqual, character, TrustedImm32(0x7f)); - - if (charClass->m_matchesUnicode.size()) { - for (unsigned i = 0; i < charClass->m_matchesUnicode.size(); ++i) { - UChar ch = charClass->m_matchesUnicode[i]; - matchDest.append(branch32(Equal, character, Imm32(ch))); - } - } - - if (charClass->m_rangesUnicode.size()) { - for (unsigned i = 0; i < charClass->m_rangesUnicode.size(); ++i) { - UChar lo = charClass->m_rangesUnicode[i].begin; - UChar hi = charClass->m_rangesUnicode[i].end; - - Jump below = branch32(LessThan, character, Imm32(lo)); - matchDest.append(branch32(LessThanOrEqual, character, Imm32(hi))); - below.link(this); - } - } - - unicodeFail = jump(); - isAscii.link(this); - } - - if (charClass->m_ranges.size()) { - unsigned matchIndex = 0; - JumpList failures; - matchCharacterClassRange(character, failures, matchDest, charClass->m_ranges.begin(), charClass->m_ranges.size(), &matchIndex, charClass->m_matches.begin(), charClass->m_matches.size()); - while (matchIndex < charClass->m_matches.size()) - matchDest.append(branch32(Equal, character, Imm32((unsigned short)charClass->m_matches[matchIndex++]))); - - failures.link(this); - } else if (charClass->m_matches.size()) { - // optimization: gather 'a','A' etc back together, can mask & test once. - Vector matchesAZaz; - - for (unsigned i = 0; i < charClass->m_matches.size(); ++i) { - char ch = charClass->m_matches[i]; - if (m_pattern.m_ignoreCase) { - if (isASCIILower(ch)) { - matchesAZaz.append(ch); - continue; - } - if (isASCIIUpper(ch)) - continue; - } - matchDest.append(branch32(Equal, character, Imm32((unsigned short)ch))); - } - - if (unsigned countAZaz = matchesAZaz.size()) { - or32(TrustedImm32(32), character); - for (unsigned i = 0; i < countAZaz; ++i) - matchDest.append(branch32(Equal, character, TrustedImm32(matchesAZaz[i]))); - } - } - - if (charClass->m_matchesUnicode.size() || charClass->m_rangesUnicode.size()) - unicodeFail.link(this); - } - - // Jumps if input not available; will have (incorrectly) incremented already! - Jump jumpIfNoAvailableInput(unsigned countToCheck = 0) - { - if (countToCheck) - add32(Imm32(countToCheck), index); - return branch32(Above, index, length); - } - - Jump jumpIfAvailableInput(unsigned countToCheck) - { - add32(Imm32(countToCheck), index); - return branch32(BelowOrEqual, index, length); - } - - Jump checkInput() - { - return branch32(BelowOrEqual, index, length); - } - - Jump atEndOfInput() - { - return branch32(Equal, index, length); - } - - Jump notAtEndOfInput() - { - return branch32(NotEqual, index, length); - } - - Jump jumpIfCharNotEquals(UChar ch, int inputPosition, RegisterID character) - { - readCharacter(inputPosition, character); - - // For case-insesitive compares, non-ascii characters that have different - // upper & lower case representations are converted to a character class. - ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); - if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { - or32(TrustedImm32(0x20), character); - ch |= 0x20; - } - - return branch32(NotEqual, character, Imm32(ch)); - } - - void readCharacter(int inputPosition, RegisterID reg) - { - if (m_charSize == Char8) - load8(BaseIndex(input, index, TimesOne, inputPosition * sizeof(char)), reg); - else - load16(BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), reg); - } - - void storeToFrame(RegisterID reg, unsigned frameLocation) - { - poke(reg, frameLocation); - } - - void storeToFrame(TrustedImm32 imm, unsigned frameLocation) - { - poke(imm, frameLocation); - } - - DataLabelPtr storeToFrameWithPatch(unsigned frameLocation) - { - return storePtrWithPatch(TrustedImmPtr(0), Address(stackPointerRegister, frameLocation * sizeof(void*))); - } - - void loadFromFrame(unsigned frameLocation, RegisterID reg) - { - peek(reg, frameLocation); - } - - void loadFromFrameAndJump(unsigned frameLocation) - { - jump(Address(stackPointerRegister, frameLocation * sizeof(void*))); - } - - void initCallFrame() - { - unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; - if (callFrameSize) - subPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); - } - void removeCallFrame() - { - unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; - if (callFrameSize) - addPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); - } - - // Used to record subpatters, should only be called if compileMode is IncludeSubpatterns. - void setSubpatternStart(RegisterID reg, unsigned subpattern) - { - ASSERT(subpattern); - // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( - store32(reg, Address(output, (subpattern << 1) * sizeof(int))); - } - void setSubpatternEnd(RegisterID reg, unsigned subpattern) - { - ASSERT(subpattern); - // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( - store32(reg, Address(output, ((subpattern << 1) + 1) * sizeof(int))); - } - void clearSubpatternStart(unsigned subpattern) - { - ASSERT(subpattern); - // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( - store32(TrustedImm32(-1), Address(output, (subpattern << 1) * sizeof(int))); - } - - // We use one of three different strategies to track the start of the current match, - // while matching. - // 1) If the pattern has a fixed size, do nothing! - we calculate the value lazily - // at the end of matching. This is irrespective of compileMode, and in this case - // these methods should never be called. - // 2) If we're compiling IncludeSubpatterns, 'output' contains a pointer to an output - // vector, store the match start in the output vector. - // 3) If we're compiling MatchOnly, 'output' is unused, store the match start directly - // in this register. - void setMatchStart(RegisterID reg) - { - ASSERT(!m_pattern.m_body->m_hasFixedSize); - if (compileMode == IncludeSubpatterns) - store32(reg, output); - else - move(reg, output); - } - void getMatchStart(RegisterID reg) - { - ASSERT(!m_pattern.m_body->m_hasFixedSize); - if (compileMode == IncludeSubpatterns) - load32(output, reg); - else - move(output, reg); - } - - enum YarrOpCode { - // These nodes wrap body alternatives - those in the main disjunction, - // rather than subpatterns or assertions. These are chained together in - // a doubly linked list, with a 'begin' node for the first alternative, - // a 'next' node for each subsequent alternative, and an 'end' node at - // the end. In the case of repeating alternatives, the 'end' node also - // has a reference back to 'begin'. - OpBodyAlternativeBegin, - OpBodyAlternativeNext, - OpBodyAlternativeEnd, - // Similar to the body alternatives, but used for subpatterns with two - // or more alternatives. - OpNestedAlternativeBegin, - OpNestedAlternativeNext, - OpNestedAlternativeEnd, - // Used for alternatives in subpatterns where there is only a single - // alternative (backtrackingis easier in these cases), or for alternatives - // which never need to be backtracked (those in parenthetical assertions, - // terminal subpatterns). - OpSimpleNestedAlternativeBegin, - OpSimpleNestedAlternativeNext, - OpSimpleNestedAlternativeEnd, - // Used to wrap 'Once' subpattern matches (quantityCount == 1). - OpParenthesesSubpatternOnceBegin, - OpParenthesesSubpatternOnceEnd, - // Used to wrap 'Terminal' subpattern matches (at the end of the regexp). - OpParenthesesSubpatternTerminalBegin, - OpParenthesesSubpatternTerminalEnd, - // Used to wrap parenthetical assertions. - OpParentheticalAssertionBegin, - OpParentheticalAssertionEnd, - // Wraps all simple terms (pattern characters, character classes). - OpTerm, - // Where an expression contains only 'once through' body alternatives - // and no repeating ones, this op is used to return match failure. - OpMatchFailed - }; - - // This structure is used to hold the compiled opcode information, - // including reference back to the original PatternTerm/PatternAlternatives, - // and JIT compilation data structures. - struct YarrOp { - explicit YarrOp(PatternTerm* term) - : m_op(OpTerm) - , m_term(term) - , m_isDeadCode(false) - { - } - - explicit YarrOp(YarrOpCode op) - : m_op(op) - , m_isDeadCode(false) - { - } - - // The operation, as a YarrOpCode, and also a reference to the PatternTerm. - YarrOpCode m_op; - PatternTerm* m_term; - - // For alternatives, this holds the PatternAlternative and doubly linked - // references to this alternative's siblings. In the case of the - // OpBodyAlternativeEnd node at the end of a section of repeating nodes, - // m_nextOp will reference the OpBodyAlternativeBegin node of the first - // repeating alternative. - PatternAlternative* m_alternative; - size_t m_previousOp; - size_t m_nextOp; - - // Used to record a set of Jumps out of the generated code, typically - // used for jumps out to backtracking code, and a single reentry back - // into the code for a node (likely where a backtrack will trigger - // rematching). - Label m_reentry; - JumpList m_jumps; - - // Used for backtracking when the prior alternative did not consume any - // characters but matched. - Jump m_zeroLengthMatch; - - // This flag is used to null out the second pattern character, when - // two are fused to match a pair together. - bool m_isDeadCode; - - // Currently used in the case of some of the more complex management of - // 'm_checked', to cache the offset used in this alternative, to avoid - // recalculating it. - int m_checkAdjust; - - // Used by OpNestedAlternativeNext/End to hold the pointer to the - // value that will be pushed into the pattern's frame to return to, - // upon backtracking back into the disjunction. - DataLabelPtr m_returnAddress; - }; - - // BacktrackingState - // This class encapsulates information about the state of code generation - // whilst generating the code for backtracking, when a term fails to match. - // Upon entry to code generation of the backtracking code for a given node, - // the Backtracking state will hold references to all control flow sources - // that are outputs in need of further backtracking from the prior node - // generated (which is the subsequent operation in the regular expression, - // and in the m_ops Vector, since we generated backtracking backwards). - // These references to control flow take the form of: - // - A jump list of jumps, to be linked to code that will backtrack them - // further. - // - A set of DataLabelPtr values, to be populated with values to be - // treated effectively as return addresses backtracking into complex - // subpatterns. - // - A flag indicating that the current sequence of generated code up to - // this point requires backtracking. - class BacktrackingState { - public: - BacktrackingState() - : m_pendingFallthrough(false) - { - } - - // Add a jump or jumps, a return address, or set the flag indicating - // that the current 'fallthrough' control flow requires backtracking. - void append(const Jump& jump) - { - m_laterFailures.append(jump); - } - void append(JumpList& jumpList) - { - m_laterFailures.append(jumpList); - } - void append(const DataLabelPtr& returnAddress) - { - m_pendingReturns.append(returnAddress); - } - void fallthrough() - { - ASSERT(!m_pendingFallthrough); - m_pendingFallthrough = true; - } - - // These methods clear the backtracking state, either linking to the - // current location, a provided label, or copying the backtracking out - // to a JumpList. All actions may require code generation to take place, - // and as such are passed a pointer to the assembler. - void link(MacroAssembler* assembler) - { - if (m_pendingReturns.size()) { - Label here(assembler); - for (unsigned i = 0; i < m_pendingReturns.size(); ++i) - m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], here)); - m_pendingReturns.clear(); - } - m_laterFailures.link(assembler); - m_laterFailures.clear(); - m_pendingFallthrough = false; - } - void linkTo(Label label, MacroAssembler* assembler) - { - if (m_pendingReturns.size()) { - for (unsigned i = 0; i < m_pendingReturns.size(); ++i) - m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], label)); - m_pendingReturns.clear(); - } - if (m_pendingFallthrough) - assembler->jump(label); - m_laterFailures.linkTo(label, assembler); - m_laterFailures.clear(); - m_pendingFallthrough = false; - } - void takeBacktracksToJumpList(JumpList& jumpList, MacroAssembler* assembler) - { - if (m_pendingReturns.size()) { - Label here(assembler); - for (unsigned i = 0; i < m_pendingReturns.size(); ++i) - m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], here)); - m_pendingReturns.clear(); - m_pendingFallthrough = true; - } - if (m_pendingFallthrough) - jumpList.append(assembler->jump()); - jumpList.append(m_laterFailures); - m_laterFailures.clear(); - m_pendingFallthrough = false; - } - - bool isEmpty() - { - return m_laterFailures.empty() && m_pendingReturns.isEmpty() && !m_pendingFallthrough; - } - - // Called at the end of code generation to link all return addresses. - void linkDataLabels(LinkBuffer& linkBuffer) - { - ASSERT(isEmpty()); - for (unsigned i = 0; i < m_backtrackRecords.size(); ++i) - linkBuffer.patch(m_backtrackRecords[i].m_dataLabel, linkBuffer.locationOf(m_backtrackRecords[i].m_backtrackLocation)); - } - - private: - struct ReturnAddressRecord { - ReturnAddressRecord(DataLabelPtr dataLabel, Label backtrackLocation) - : m_dataLabel(dataLabel) - , m_backtrackLocation(backtrackLocation) - { - } - - DataLabelPtr m_dataLabel; - Label m_backtrackLocation; - }; - - JumpList m_laterFailures; - bool m_pendingFallthrough; - Vector m_pendingReturns; - Vector m_backtrackRecords; - }; - - // Generation methods: - // =================== - - // This method provides a default implementation of backtracking common - // to many terms; terms commonly jump out of the forwards matching path - // on any failed conditions, and add these jumps to the m_jumps list. If - // no special handling is required we can often just backtrack to m_jumps. - void backtrackTermDefault(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - m_backtrackingState.append(op.m_jumps); - } - - void generateAssertionBOL(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - if (m_pattern.m_multiline) { - const RegisterID character = regT0; - - JumpList matchDest; - if (!term->inputPosition) - matchDest.append(branch32(Equal, index, Imm32(m_checked))); - - readCharacter((term->inputPosition - m_checked) - 1, character); - matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass()); - op.m_jumps.append(jump()); - - matchDest.link(this); - } else { - // Erk, really should poison out these alternatives early. :-/ - if (term->inputPosition) - op.m_jumps.append(jump()); - else - op.m_jumps.append(branch32(NotEqual, index, Imm32(m_checked))); - } - } - void backtrackAssertionBOL(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - void generateAssertionEOL(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - if (m_pattern.m_multiline) { - const RegisterID character = regT0; - - JumpList matchDest; - if (term->inputPosition == m_checked) - matchDest.append(atEndOfInput()); - - readCharacter(term->inputPosition - m_checked, character); - matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass()); - op.m_jumps.append(jump()); - - matchDest.link(this); - } else { - if (term->inputPosition == m_checked) - op.m_jumps.append(notAtEndOfInput()); - // Erk, really should poison out these alternatives early. :-/ - else - op.m_jumps.append(jump()); - } - } - void backtrackAssertionEOL(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - // Also falls though on nextIsNotWordChar. - void matchAssertionWordchar(size_t opIndex, JumpList& nextIsWordChar, JumpList& nextIsNotWordChar) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID character = regT0; - - if (term->inputPosition == m_checked) - nextIsNotWordChar.append(atEndOfInput()); - - readCharacter((term->inputPosition - m_checked), character); - matchCharacterClass(character, nextIsWordChar, m_pattern.wordcharCharacterClass()); - } - - void generateAssertionWordBoundary(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID character = regT0; - - Jump atBegin; - JumpList matchDest; - if (!term->inputPosition) - atBegin = branch32(Equal, index, Imm32(m_checked)); - readCharacter((term->inputPosition - m_checked) - 1, character); - matchCharacterClass(character, matchDest, m_pattern.wordcharCharacterClass()); - if (!term->inputPosition) - atBegin.link(this); - - // We fall through to here if the last character was not a wordchar. - JumpList nonWordCharThenWordChar; - JumpList nonWordCharThenNonWordChar; - if (term->invert()) { - matchAssertionWordchar(opIndex, nonWordCharThenNonWordChar, nonWordCharThenWordChar); - nonWordCharThenWordChar.append(jump()); - } else { - matchAssertionWordchar(opIndex, nonWordCharThenWordChar, nonWordCharThenNonWordChar); - nonWordCharThenNonWordChar.append(jump()); - } - op.m_jumps.append(nonWordCharThenNonWordChar); - - // We jump here if the last character was a wordchar. - matchDest.link(this); - JumpList wordCharThenWordChar; - JumpList wordCharThenNonWordChar; - if (term->invert()) { - matchAssertionWordchar(opIndex, wordCharThenNonWordChar, wordCharThenWordChar); - wordCharThenWordChar.append(jump()); - } else { - matchAssertionWordchar(opIndex, wordCharThenWordChar, wordCharThenNonWordChar); - // This can fall-though! - } - - op.m_jumps.append(wordCharThenWordChar); - - nonWordCharThenWordChar.link(this); - wordCharThenNonWordChar.link(this); - } - void backtrackAssertionWordBoundary(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - void generatePatternCharacterOnce(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - - if (op.m_isDeadCode) - return; - - // m_ops always ends with a OpBodyAlternativeEnd or OpMatchFailed - // node, so there must always be at least one more node. - ASSERT(opIndex + 1 < m_ops.size()); - YarrOp* nextOp = &m_ops[opIndex + 1]; - - PatternTerm* term = op.m_term; - UChar ch = term->patternCharacter; - - if ((ch > 0xff) && (m_charSize == Char8)) { - // Have a 16 bit pattern character and an 8 bit string - short circuit - op.m_jumps.append(jump()); - return; - } - - const RegisterID character = regT0; - int maxCharactersAtOnce = m_charSize == Char8 ? 4 : 2; - unsigned ignoreCaseMask = 0; - int allCharacters = ch; - int numberCharacters; - int startTermPosition = term->inputPosition; - - // For case-insesitive compares, non-ascii characters that have different - // upper & lower case representations are converted to a character class. - ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); - - if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) - ignoreCaseMask |= 32; - - for (numberCharacters = 1; numberCharacters < maxCharactersAtOnce && nextOp->m_op == OpTerm; ++numberCharacters, nextOp = &m_ops[opIndex + numberCharacters]) { - PatternTerm* nextTerm = nextOp->m_term; - - if (nextTerm->type != PatternTerm::TypePatternCharacter - || nextTerm->quantityType != QuantifierFixedCount - || nextTerm->quantityCount != 1 - || nextTerm->inputPosition != (startTermPosition + numberCharacters)) - break; - - nextOp->m_isDeadCode = true; - - int shiftAmount = (m_charSize == Char8 ? 8 : 16) * numberCharacters; - - UChar currentCharacter = nextTerm->patternCharacter; - - if ((currentCharacter > 0xff) && (m_charSize == Char8)) { - // Have a 16 bit pattern character and an 8 bit string - short circuit - op.m_jumps.append(jump()); - return; - } - - // For case-insesitive compares, non-ascii characters that have different - // upper & lower case representations are converted to a character class. - ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(currentCharacter) || isCanonicallyUnique(currentCharacter)); - - allCharacters |= (currentCharacter << shiftAmount); - - if ((m_pattern.m_ignoreCase) && (isASCIIAlpha(currentCharacter))) - ignoreCaseMask |= 32 << shiftAmount; - } - - if (m_charSize == Char8) { - switch (numberCharacters) { - case 1: - op.m_jumps.append(jumpIfCharNotEquals(ch, startTermPosition - m_checked, character)); - return; - case 2: { - BaseIndex address(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); - load16Unaligned(address, character); - break; - } - case 3: { - BaseIndex highAddress(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); - load16Unaligned(highAddress, character); - if (ignoreCaseMask) - or32(Imm32(ignoreCaseMask), character); - op.m_jumps.append(branch32(NotEqual, character, Imm32((allCharacters & 0xffff) | ignoreCaseMask))); - op.m_jumps.append(jumpIfCharNotEquals(allCharacters >> 16, startTermPosition + 2 - m_checked, character)); - return; - } - case 4: { - BaseIndex address(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); - load32WithUnalignedHalfWords(address, character); - break; - } - } - } else { - switch (numberCharacters) { - case 1: - op.m_jumps.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); - return; - case 2: - BaseIndex address(input, index, TimesTwo, (term->inputPosition - m_checked) * sizeof(UChar)); - load32WithUnalignedHalfWords(address, character); - break; - } - } - - if (ignoreCaseMask) - or32(Imm32(ignoreCaseMask), character); - op.m_jumps.append(branch32(NotEqual, character, Imm32(allCharacters | ignoreCaseMask))); - return; - } - void backtrackPatternCharacterOnce(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - void generatePatternCharacterFixed(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - UChar ch = term->patternCharacter; - - const RegisterID character = regT0; - const RegisterID countRegister = regT1; - - move(index, countRegister); - sub32(Imm32(term->quantityCount.unsafeGet()), countRegister); - - Label loop(this); - BaseIndex address(input, countRegister, m_charScale, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(m_charSize == Char8 ? sizeof(char) : sizeof(UChar))).unsafeGet()); - - if (m_charSize == Char8) - load8(address, character); - else - load16(address, character); - - // For case-insesitive compares, non-ascii characters that have different - // upper & lower case representations are converted to a character class. - ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); - if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { - or32(TrustedImm32(0x20), character); - ch |= 0x20; - } - - op.m_jumps.append(branch32(NotEqual, character, Imm32(ch))); - add32(TrustedImm32(1), countRegister); - branch32(NotEqual, countRegister, index).linkTo(loop, this); - } - void backtrackPatternCharacterFixed(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - void generatePatternCharacterGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - UChar ch = term->patternCharacter; - - const RegisterID character = regT0; - const RegisterID countRegister = regT1; - - move(TrustedImm32(0), countRegister); - - // Unless have a 16 bit pattern character and an 8 bit string - short circuit - if (!((ch > 0xff) && (m_charSize == Char8))) { - JumpList failures; - Label loop(this); - failures.append(atEndOfInput()); - failures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); - - add32(TrustedImm32(1), countRegister); - add32(TrustedImm32(1), index); - if (term->quantityCount == quantifyInfinite) - jump(loop); - else - branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this); - - failures.link(this); - } - op.m_reentry = label(); - - storeToFrame(countRegister, term->frameLocation); - } - void backtrackPatternCharacterGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID countRegister = regT1; - - m_backtrackingState.link(this); - - loadFromFrame(term->frameLocation, countRegister); - m_backtrackingState.append(branchTest32(Zero, countRegister)); - sub32(TrustedImm32(1), countRegister); - sub32(TrustedImm32(1), index); - jump(op.m_reentry); - } - - void generatePatternCharacterNonGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID countRegister = regT1; - - move(TrustedImm32(0), countRegister); - op.m_reentry = label(); - storeToFrame(countRegister, term->frameLocation); - } - void backtrackPatternCharacterNonGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - UChar ch = term->patternCharacter; - - const RegisterID character = regT0; - const RegisterID countRegister = regT1; - - m_backtrackingState.link(this); - - loadFromFrame(term->frameLocation, countRegister); - - // Unless have a 16 bit pattern character and an 8 bit string - short circuit - if (!((ch > 0xff) && (m_charSize == Char8))) { - JumpList nonGreedyFailures; - nonGreedyFailures.append(atEndOfInput()); - if (term->quantityCount != quantifyInfinite) - nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet()))); - nonGreedyFailures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); - - add32(TrustedImm32(1), countRegister); - add32(TrustedImm32(1), index); - - jump(op.m_reentry); - nonGreedyFailures.link(this); - } - - sub32(countRegister, index); - m_backtrackingState.fallthrough(); - } - - void generateCharacterClassOnce(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID character = regT0; - - JumpList matchDest; - readCharacter(term->inputPosition - m_checked, character); - matchCharacterClass(character, matchDest, term->characterClass); - - if (term->invert()) - op.m_jumps.append(matchDest); - else { - op.m_jumps.append(jump()); - matchDest.link(this); - } - } - void backtrackCharacterClassOnce(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - void generateCharacterClassFixed(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID character = regT0; - const RegisterID countRegister = regT1; - - move(index, countRegister); - sub32(Imm32(term->quantityCount.unsafeGet()), countRegister); - - Label loop(this); - JumpList matchDest; - if (m_charSize == Char8) - load8(BaseIndex(input, countRegister, TimesOne, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(sizeof(char))).unsafeGet()), character); - else - load16(BaseIndex(input, countRegister, TimesTwo, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(sizeof(UChar))).unsafeGet()), character); - matchCharacterClass(character, matchDest, term->characterClass); - - if (term->invert()) - op.m_jumps.append(matchDest); - else { - op.m_jumps.append(jump()); - matchDest.link(this); - } - - add32(TrustedImm32(1), countRegister); - branch32(NotEqual, countRegister, index).linkTo(loop, this); - } - void backtrackCharacterClassFixed(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - void generateCharacterClassGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID character = regT0; - const RegisterID countRegister = regT1; - - move(TrustedImm32(0), countRegister); - - JumpList failures; - Label loop(this); - failures.append(atEndOfInput()); - - if (term->invert()) { - readCharacter(term->inputPosition - m_checked, character); - matchCharacterClass(character, failures, term->characterClass); - } else { - JumpList matchDest; - readCharacter(term->inputPosition - m_checked, character); - matchCharacterClass(character, matchDest, term->characterClass); - failures.append(jump()); - matchDest.link(this); - } - - add32(TrustedImm32(1), countRegister); - add32(TrustedImm32(1), index); - if (term->quantityCount != quantifyInfinite) { - branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this); - failures.append(jump()); - } else - jump(loop); - - failures.link(this); - op.m_reentry = label(); - - storeToFrame(countRegister, term->frameLocation); - } - void backtrackCharacterClassGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID countRegister = regT1; - - m_backtrackingState.link(this); - - loadFromFrame(term->frameLocation, countRegister); - m_backtrackingState.append(branchTest32(Zero, countRegister)); - sub32(TrustedImm32(1), countRegister); - sub32(TrustedImm32(1), index); - jump(op.m_reentry); - } - - void generateCharacterClassNonGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID countRegister = regT1; - - move(TrustedImm32(0), countRegister); - op.m_reentry = label(); - storeToFrame(countRegister, term->frameLocation); - } - void backtrackCharacterClassNonGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID character = regT0; - const RegisterID countRegister = regT1; - - JumpList nonGreedyFailures; - - m_backtrackingState.link(this); - - loadFromFrame(term->frameLocation, countRegister); - - nonGreedyFailures.append(atEndOfInput()); - nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet()))); - - JumpList matchDest; - readCharacter(term->inputPosition - m_checked, character); - matchCharacterClass(character, matchDest, term->characterClass); - - if (term->invert()) - nonGreedyFailures.append(matchDest); - else { - nonGreedyFailures.append(jump()); - matchDest.link(this); - } - - add32(TrustedImm32(1), countRegister); - add32(TrustedImm32(1), index); - - jump(op.m_reentry); - - nonGreedyFailures.link(this); - sub32(countRegister, index); - m_backtrackingState.fallthrough(); - } - - void generateDotStarEnclosure(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID character = regT0; - const RegisterID matchPos = regT1; - - JumpList foundBeginningNewLine; - JumpList saveStartIndex; - JumpList foundEndingNewLine; - - ASSERT(!m_pattern.m_body->m_hasFixedSize); - getMatchStart(matchPos); - - saveStartIndex.append(branchTest32(Zero, matchPos)); - Label findBOLLoop(this); - sub32(TrustedImm32(1), matchPos); - if (m_charSize == Char8) - load8(BaseIndex(input, matchPos, TimesOne, 0), character); - else - load16(BaseIndex(input, matchPos, TimesTwo, 0), character); - matchCharacterClass(character, foundBeginningNewLine, m_pattern.newlineCharacterClass()); - branchTest32(NonZero, matchPos).linkTo(findBOLLoop, this); - saveStartIndex.append(jump()); - - foundBeginningNewLine.link(this); - add32(TrustedImm32(1), matchPos); // Advance past newline - saveStartIndex.link(this); - - if (!m_pattern.m_multiline && term->anchors.bolAnchor) - op.m_jumps.append(branchTest32(NonZero, matchPos)); - - ASSERT(!m_pattern.m_body->m_hasFixedSize); - setMatchStart(matchPos); - - move(index, matchPos); - - Label findEOLLoop(this); - foundEndingNewLine.append(branch32(Equal, matchPos, length)); - if (m_charSize == Char8) - load8(BaseIndex(input, matchPos, TimesOne, 0), character); - else - load16(BaseIndex(input, matchPos, TimesTwo, 0), character); - matchCharacterClass(character, foundEndingNewLine, m_pattern.newlineCharacterClass()); - add32(TrustedImm32(1), matchPos); - jump(findEOLLoop); - - foundEndingNewLine.link(this); - - if (!m_pattern.m_multiline && term->anchors.eolAnchor) - op.m_jumps.append(branch32(NotEqual, matchPos, length)); - - move(matchPos, index); - } - - void backtrackDotStarEnclosure(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - // Code generation/backtracking for simple terms - // (pattern characters, character classes, and assertions). - // These methods farm out work to the set of functions above. - void generateTerm(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - switch (term->type) { - case PatternTerm::TypePatternCharacter: - switch (term->quantityType) { - case QuantifierFixedCount: - if (term->quantityCount == 1) - generatePatternCharacterOnce(opIndex); - else - generatePatternCharacterFixed(opIndex); - break; - case QuantifierGreedy: - generatePatternCharacterGreedy(opIndex); - break; - case QuantifierNonGreedy: - generatePatternCharacterNonGreedy(opIndex); - break; - } - break; - - case PatternTerm::TypeCharacterClass: - switch (term->quantityType) { - case QuantifierFixedCount: - if (term->quantityCount == 1) - generateCharacterClassOnce(opIndex); - else - generateCharacterClassFixed(opIndex); - break; - case QuantifierGreedy: - generateCharacterClassGreedy(opIndex); - break; - case QuantifierNonGreedy: - generateCharacterClassNonGreedy(opIndex); - break; - } - break; - - case PatternTerm::TypeAssertionBOL: - generateAssertionBOL(opIndex); - break; - - case PatternTerm::TypeAssertionEOL: - generateAssertionEOL(opIndex); - break; - - case PatternTerm::TypeAssertionWordBoundary: - generateAssertionWordBoundary(opIndex); - break; - - case PatternTerm::TypeForwardReference: - break; - - case PatternTerm::TypeParenthesesSubpattern: - case PatternTerm::TypeParentheticalAssertion: - ASSERT_NOT_REACHED(); - case PatternTerm::TypeBackReference: - m_shouldFallBack = true; - break; - case PatternTerm::TypeDotStarEnclosure: - generateDotStarEnclosure(opIndex); - break; - } - } - void backtrackTerm(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - switch (term->type) { - case PatternTerm::TypePatternCharacter: - switch (term->quantityType) { - case QuantifierFixedCount: - if (term->quantityCount == 1) - backtrackPatternCharacterOnce(opIndex); - else - backtrackPatternCharacterFixed(opIndex); - break; - case QuantifierGreedy: - backtrackPatternCharacterGreedy(opIndex); - break; - case QuantifierNonGreedy: - backtrackPatternCharacterNonGreedy(opIndex); - break; - } - break; - - case PatternTerm::TypeCharacterClass: - switch (term->quantityType) { - case QuantifierFixedCount: - if (term->quantityCount == 1) - backtrackCharacterClassOnce(opIndex); - else - backtrackCharacterClassFixed(opIndex); - break; - case QuantifierGreedy: - backtrackCharacterClassGreedy(opIndex); - break; - case QuantifierNonGreedy: - backtrackCharacterClassNonGreedy(opIndex); - break; - } - break; - - case PatternTerm::TypeAssertionBOL: - backtrackAssertionBOL(opIndex); - break; - - case PatternTerm::TypeAssertionEOL: - backtrackAssertionEOL(opIndex); - break; - - case PatternTerm::TypeAssertionWordBoundary: - backtrackAssertionWordBoundary(opIndex); - break; - - case PatternTerm::TypeForwardReference: - break; - - case PatternTerm::TypeParenthesesSubpattern: - case PatternTerm::TypeParentheticalAssertion: - ASSERT_NOT_REACHED(); - - case PatternTerm::TypeDotStarEnclosure: - backtrackDotStarEnclosure(opIndex); - break; - - case PatternTerm::TypeBackReference: - m_shouldFallBack = true; - break; - } - } - - void generate() - { - // Forwards generate the matching code. - ASSERT(m_ops.size()); - size_t opIndex = 0; - - do { - YarrOp& op = m_ops[opIndex]; - switch (op.m_op) { - - case OpTerm: - generateTerm(opIndex); - break; - - // OpBodyAlternativeBegin/Next/End - // - // These nodes wrap the set of alternatives in the body of the regular expression. - // There may be either one or two chains of OpBodyAlternative nodes, one representing - // the 'once through' sequence of alternatives (if any exist), and one representing - // the repeating alternatives (again, if any exist). - // - // Upon normal entry to the Begin alternative, we will check that input is available. - // Reentry to the Begin alternative will take place after the check has taken place, - // and will assume that the input position has already been progressed as appropriate. - // - // Entry to subsequent Next/End alternatives occurs when the prior alternative has - // successfully completed a match - return a success state from JIT code. - // - // Next alternatives allow for reentry optimized to suit backtracking from its - // preceding alternative. It expects the input position to still be set to a position - // appropriate to its predecessor, and it will only perform an input check if the - // predecessor had a minimum size less than its own. - // - // In the case 'once through' expressions, the End node will also have a reentry - // point to jump to when the last alternative fails. Again, this expects the input - // position to still reflect that expected by the prior alternative. - case OpBodyAlternativeBegin: { - PatternAlternative* alternative = op.m_alternative; - - // Upon entry at the head of the set of alternatives, check if input is available - // to run the first alternative. (This progresses the input position). - op.m_jumps.append(jumpIfNoAvailableInput(alternative->m_minimumSize)); - // We will reenter after the check, and assume the input position to have been - // set as appropriate to this alternative. - op.m_reentry = label(); - - m_checked += alternative->m_minimumSize; - break; - } - case OpBodyAlternativeNext: - case OpBodyAlternativeEnd: { - PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; - PatternAlternative* alternative = op.m_alternative; - - // If we get here, the prior alternative matched - return success. - - // Adjust the stack pointer to remove the pattern's frame. - removeCallFrame(); - - // Load appropriate values into the return register and the first output - // slot, and return. In the case of pattern with a fixed size, we will - // not have yet set the value in the first - ASSERT(index != returnRegister); - if (m_pattern.m_body->m_hasFixedSize) { - move(index, returnRegister); - if (priorAlternative->m_minimumSize) - sub32(Imm32(priorAlternative->m_minimumSize), returnRegister); - if (compileMode == IncludeSubpatterns) - store32(returnRegister, output); - } else - getMatchStart(returnRegister); - if (compileMode == IncludeSubpatterns) - store32(index, Address(output, 4)); - move(index, returnRegister2); - - generateReturn(); - - // This is the divide between the tail of the prior alternative, above, and - // the head of the subsequent alternative, below. - - if (op.m_op == OpBodyAlternativeNext) { - // This is the reentry point for the Next alternative. We expect any code - // that jumps here to do so with the input position matching that of the - // PRIOR alteranative, and we will only check input availability if we - // need to progress it forwards. - op.m_reentry = label(); - if (alternative->m_minimumSize > priorAlternative->m_minimumSize) { - add32(Imm32(alternative->m_minimumSize - priorAlternative->m_minimumSize), index); - op.m_jumps.append(jumpIfNoAvailableInput()); - } else if (priorAlternative->m_minimumSize > alternative->m_minimumSize) - sub32(Imm32(priorAlternative->m_minimumSize - alternative->m_minimumSize), index); - } else if (op.m_nextOp == notFound) { - // This is the reentry point for the End of 'once through' alternatives, - // jumped to when the last alternative fails to match. - op.m_reentry = label(); - sub32(Imm32(priorAlternative->m_minimumSize), index); - } - - if (op.m_op == OpBodyAlternativeNext) - m_checked += alternative->m_minimumSize; - m_checked -= priorAlternative->m_minimumSize; - break; - } - - // OpSimpleNestedAlternativeBegin/Next/End - // OpNestedAlternativeBegin/Next/End - // - // These nodes are used to handle sets of alternatives that are nested within - // subpatterns and parenthetical assertions. The 'simple' forms are used where - // we do not need to be able to backtrack back into any alternative other than - // the last, the normal forms allow backtracking into any alternative. - // - // Each Begin/Next node is responsible for planting an input check to ensure - // sufficient input is available on entry. Next nodes additionally need to - // jump to the end - Next nodes use the End node's m_jumps list to hold this - // set of jumps. - // - // In the non-simple forms, successful alternative matches must store a - // 'return address' using a DataLabelPtr, used to store the address to jump - // to when backtracking, to get to the code for the appropriate alternative. - case OpSimpleNestedAlternativeBegin: - case OpNestedAlternativeBegin: { - PatternTerm* term = op.m_term; - PatternAlternative* alternative = op.m_alternative; - PatternDisjunction* disjunction = term->parentheses.disjunction; - - // Calculate how much input we need to check for, and if non-zero check. - op.m_checkAdjust = alternative->m_minimumSize; - if ((term->quantityType == QuantifierFixedCount) && (term->type != PatternTerm::TypeParentheticalAssertion)) - op.m_checkAdjust -= disjunction->m_minimumSize; - if (op.m_checkAdjust) - op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust)); - - m_checked += op.m_checkAdjust; - break; - } - case OpSimpleNestedAlternativeNext: - case OpNestedAlternativeNext: { - PatternTerm* term = op.m_term; - PatternAlternative* alternative = op.m_alternative; - PatternDisjunction* disjunction = term->parentheses.disjunction; - - // In the non-simple case, store a 'return address' so we can backtrack correctly. - if (op.m_op == OpNestedAlternativeNext) { - unsigned parenthesesFrameLocation = term->frameLocation; - unsigned alternativeFrameLocation = parenthesesFrameLocation; - if (term->quantityType != QuantifierFixedCount) - alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; - op.m_returnAddress = storeToFrameWithPatch(alternativeFrameLocation); - } - - if (term->quantityType != QuantifierFixedCount && !m_ops[op.m_previousOp].m_alternative->m_minimumSize) { - // If the previous alternative matched without consuming characters then - // backtrack to try to match while consumming some input. - op.m_zeroLengthMatch = branch32(Equal, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); - } - - // If we reach here then the last alternative has matched - jump to the - // End node, to skip over any further alternatives. - // - // FIXME: this is logically O(N^2) (though N can be expected to be very - // small). We could avoid this either by adding an extra jump to the JIT - // data structures, or by making backtracking code that jumps to Next - // alternatives are responsible for checking that input is available (if - // we didn't need to plant the input checks, then m_jumps would be free). - YarrOp* endOp = &m_ops[op.m_nextOp]; - while (endOp->m_nextOp != notFound) { - ASSERT(endOp->m_op == OpSimpleNestedAlternativeNext || endOp->m_op == OpNestedAlternativeNext); - endOp = &m_ops[endOp->m_nextOp]; - } - ASSERT(endOp->m_op == OpSimpleNestedAlternativeEnd || endOp->m_op == OpNestedAlternativeEnd); - endOp->m_jumps.append(jump()); - - // This is the entry point for the next alternative. - op.m_reentry = label(); - - // Calculate how much input we need to check for, and if non-zero check. - op.m_checkAdjust = alternative->m_minimumSize; - if ((term->quantityType == QuantifierFixedCount) && (term->type != PatternTerm::TypeParentheticalAssertion)) - op.m_checkAdjust -= disjunction->m_minimumSize; - if (op.m_checkAdjust) - op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust)); - - YarrOp& lastOp = m_ops[op.m_previousOp]; - m_checked -= lastOp.m_checkAdjust; - m_checked += op.m_checkAdjust; - break; - } - case OpSimpleNestedAlternativeEnd: - case OpNestedAlternativeEnd: { - PatternTerm* term = op.m_term; - - // In the non-simple case, store a 'return address' so we can backtrack correctly. - if (op.m_op == OpNestedAlternativeEnd) { - unsigned parenthesesFrameLocation = term->frameLocation; - unsigned alternativeFrameLocation = parenthesesFrameLocation; - if (term->quantityType != QuantifierFixedCount) - alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; - op.m_returnAddress = storeToFrameWithPatch(alternativeFrameLocation); - } - - if (term->quantityType != QuantifierFixedCount && !m_ops[op.m_previousOp].m_alternative->m_minimumSize) { - // If the previous alternative matched without consuming characters then - // backtrack to try to match while consumming some input. - op.m_zeroLengthMatch = branch32(Equal, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); - } - - // If this set of alternatives contains more than one alternative, - // then the Next nodes will have planted jumps to the End, and added - // them to this node's m_jumps list. - op.m_jumps.link(this); - op.m_jumps.clear(); - - YarrOp& lastOp = m_ops[op.m_previousOp]; - m_checked -= lastOp.m_checkAdjust; - break; - } - - // OpParenthesesSubpatternOnceBegin/End - // - // These nodes support (optionally) capturing subpatterns, that have a - // quantity count of 1 (this covers fixed once, and ?/?? quantifiers). - case OpParenthesesSubpatternOnceBegin: { - PatternTerm* term = op.m_term; - unsigned parenthesesFrameLocation = term->frameLocation; - const RegisterID indexTemporary = regT0; - ASSERT(term->quantityCount == 1); - - // Upon entry to a Greedy quantified set of parenthese store the index. - // We'll use this for two purposes: - // - To indicate which iteration we are on of mathing the remainder of - // the expression after the parentheses - the first, including the - // match within the parentheses, or the second having skipped over them. - // - To check for empty matches, which must be rejected. - // - // At the head of a NonGreedy set of parentheses we'll immediately set the - // value on the stack to -1 (indicating a match skipping the subpattern), - // and plant a jump to the end. We'll also plant a label to backtrack to - // to reenter the subpattern later, with a store to set up index on the - // second iteration. - // - // FIXME: for capturing parens, could use the index in the capture array? - if (term->quantityType == QuantifierGreedy) - storeToFrame(index, parenthesesFrameLocation); - else if (term->quantityType == QuantifierNonGreedy) { - storeToFrame(TrustedImm32(-1), parenthesesFrameLocation); - op.m_jumps.append(jump()); - op.m_reentry = label(); - storeToFrame(index, parenthesesFrameLocation); - } - - // If the parenthese are capturing, store the starting index value to the - // captures array, offsetting as necessary. - // - // FIXME: could avoid offsetting this value in JIT code, apply - // offsets only afterwards, at the point the results array is - // being accessed. - if (term->capture() && compileMode == IncludeSubpatterns) { - int inputOffset = term->inputPosition - m_checked; - if (term->quantityType == QuantifierFixedCount) - inputOffset -= term->parentheses.disjunction->m_minimumSize; - if (inputOffset) { - move(index, indexTemporary); - add32(Imm32(inputOffset), indexTemporary); - setSubpatternStart(indexTemporary, term->parentheses.subpatternId); - } else - setSubpatternStart(index, term->parentheses.subpatternId); - } - break; - } - case OpParenthesesSubpatternOnceEnd: { - PatternTerm* term = op.m_term; - const RegisterID indexTemporary = regT0; - ASSERT(term->quantityCount == 1); - -#ifndef NDEBUG - // Runtime ASSERT to make sure that the nested alternative handled the - // "no input consumed" check. - if (term->quantityType != QuantifierFixedCount && !term->parentheses.disjunction->m_minimumSize) { - Jump pastBreakpoint; - pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); - breakpoint(); - pastBreakpoint.link(this); - } -#endif - - // If the parenthese are capturing, store the ending index value to the - // captures array, offsetting as necessary. - // - // FIXME: could avoid offsetting this value in JIT code, apply - // offsets only afterwards, at the point the results array is - // being accessed. - if (term->capture() && compileMode == IncludeSubpatterns) { - int inputOffset = term->inputPosition - m_checked; - if (inputOffset) { - move(index, indexTemporary); - add32(Imm32(inputOffset), indexTemporary); - setSubpatternEnd(indexTemporary, term->parentheses.subpatternId); - } else - setSubpatternEnd(index, term->parentheses.subpatternId); - } - - // If the parentheses are quantified Greedy then add a label to jump back - // to if get a failed match from after the parentheses. For NonGreedy - // parentheses, link the jump from before the subpattern to here. - if (term->quantityType == QuantifierGreedy) - op.m_reentry = label(); - else if (term->quantityType == QuantifierNonGreedy) { - YarrOp& beginOp = m_ops[op.m_previousOp]; - beginOp.m_jumps.link(this); - } - break; - } - - // OpParenthesesSubpatternTerminalBegin/End - case OpParenthesesSubpatternTerminalBegin: { - PatternTerm* term = op.m_term; - ASSERT(term->quantityType == QuantifierGreedy); - ASSERT(term->quantityCount == quantifyInfinite); - ASSERT(!term->capture()); - - // Upon entry set a label to loop back to. - op.m_reentry = label(); - - // Store the start index of the current match; we need to reject zero - // length matches. - storeToFrame(index, term->frameLocation); - break; - } - case OpParenthesesSubpatternTerminalEnd: { - YarrOp& beginOp = m_ops[op.m_previousOp]; -#ifndef NDEBUG - PatternTerm* term = op.m_term; - - // Runtime ASSERT to make sure that the nested alternative handled the - // "no input consumed" check. - Jump pastBreakpoint; - pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); - breakpoint(); - pastBreakpoint.link(this); -#endif - - // We know that the match is non-zero, we can accept it and - // loop back up to the head of the subpattern. - jump(beginOp.m_reentry); - - // This is the entry point to jump to when we stop matching - we will - // do so once the subpattern cannot match any more. - op.m_reentry = label(); - break; - } - - // OpParentheticalAssertionBegin/End - case OpParentheticalAssertionBegin: { - PatternTerm* term = op.m_term; - - // Store the current index - assertions should not update index, so - // we will need to restore it upon a successful match. - unsigned parenthesesFrameLocation = term->frameLocation; - storeToFrame(index, parenthesesFrameLocation); - - // Check - op.m_checkAdjust = m_checked - term->inputPosition; - if (op.m_checkAdjust) - sub32(Imm32(op.m_checkAdjust), index); - - m_checked -= op.m_checkAdjust; - break; - } - case OpParentheticalAssertionEnd: { - PatternTerm* term = op.m_term; - - // Restore the input index value. - unsigned parenthesesFrameLocation = term->frameLocation; - loadFromFrame(parenthesesFrameLocation, index); - - // If inverted, a successful match of the assertion must be treated - // as a failure, so jump to backtracking. - if (term->invert()) { - op.m_jumps.append(jump()); - op.m_reentry = label(); - } - - YarrOp& lastOp = m_ops[op.m_previousOp]; - m_checked += lastOp.m_checkAdjust; - break; - } - - case OpMatchFailed: - removeCallFrame(); - move(TrustedImmPtr((void*)WTF::notFound), returnRegister); - move(TrustedImm32(0), returnRegister2); - generateReturn(); - break; - } - - ++opIndex; - } while (opIndex < m_ops.size()); - } - - void backtrack() - { - // Backwards generate the backtracking code. - size_t opIndex = m_ops.size(); - ASSERT(opIndex); - - do { - --opIndex; - YarrOp& op = m_ops[opIndex]; - switch (op.m_op) { - - case OpTerm: - backtrackTerm(opIndex); - break; - - // OpBodyAlternativeBegin/Next/End - // - // For each Begin/Next node representing an alternative, we need to decide what to do - // in two circumstances: - // - If we backtrack back into this node, from within the alternative. - // - If the input check at the head of the alternative fails (if this exists). - // - // We treat these two cases differently since in the former case we have slightly - // more information - since we are backtracking out of a prior alternative we know - // that at least enough input was available to run it. For example, given the regular - // expression /a|b/, if we backtrack out of the first alternative (a failed pattern - // character match of 'a'), then we need not perform an additional input availability - // check before running the second alternative. - // - // Backtracking required differs for the last alternative, which in the case of the - // repeating set of alternatives must loop. The code generated for the last alternative - // will also be used to handle all input check failures from any prior alternatives - - // these require similar functionality, in seeking the next available alternative for - // which there is sufficient input. - // - // Since backtracking of all other alternatives simply requires us to link backtracks - // to the reentry point for the subsequent alternative, we will only be generating any - // code when backtracking the last alternative. - case OpBodyAlternativeBegin: - case OpBodyAlternativeNext: { - PatternAlternative* alternative = op.m_alternative; - - if (op.m_op == OpBodyAlternativeNext) { - PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; - m_checked += priorAlternative->m_minimumSize; - } - m_checked -= alternative->m_minimumSize; - - // Is this the last alternative? If not, then if we backtrack to this point we just - // need to jump to try to match the next alternative. - if (m_ops[op.m_nextOp].m_op != OpBodyAlternativeEnd) { - m_backtrackingState.linkTo(m_ops[op.m_nextOp].m_reentry, this); - break; - } - YarrOp& endOp = m_ops[op.m_nextOp]; - - YarrOp* beginOp = &op; - while (beginOp->m_op != OpBodyAlternativeBegin) { - ASSERT(beginOp->m_op == OpBodyAlternativeNext); - beginOp = &m_ops[beginOp->m_previousOp]; - } - - bool onceThrough = endOp.m_nextOp == notFound; - - // First, generate code to handle cases where we backtrack out of an attempted match - // of the last alternative. If this is a 'once through' set of alternatives then we - // have nothing to do - link this straight through to the End. - if (onceThrough) - m_backtrackingState.linkTo(endOp.m_reentry, this); - else { - // If we don't need to move the input poistion, and the pattern has a fixed size - // (in which case we omit the store of the start index until the pattern has matched) - // then we can just link the backtrack out of the last alternative straight to the - // head of the first alternative. - if (m_pattern.m_body->m_hasFixedSize - && (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize) - && (alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize == 1)) - m_backtrackingState.linkTo(beginOp->m_reentry, this); - else { - // We need to generate a trampoline of code to execute before looping back - // around to the first alternative. - m_backtrackingState.link(this); - - // If the pattern size is not fixed, then store the start index, for use if we match. - if (!m_pattern.m_body->m_hasFixedSize) { - if (alternative->m_minimumSize == 1) - setMatchStart(index); - else { - move(index, regT0); - if (alternative->m_minimumSize) - sub32(Imm32(alternative->m_minimumSize - 1), regT0); - else - add32(TrustedImm32(1), regT0); - setMatchStart(regT0); - } - } - - // Generate code to loop. Check whether the last alternative is longer than the - // first (e.g. /a|xy/ or /a|xyz/). - if (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize) { - // We want to loop, and increment input position. If the delta is 1, it is - // already correctly incremented, if more than one then decrement as appropriate. - unsigned delta = alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize; - ASSERT(delta); - if (delta != 1) - sub32(Imm32(delta - 1), index); - jump(beginOp->m_reentry); - } else { - // If the first alternative has minimum size 0xFFFFFFFFu, then there cannot - // be sufficent input available to handle this, so just fall through. - unsigned delta = beginOp->m_alternative->m_minimumSize - alternative->m_minimumSize; - if (delta != 0xFFFFFFFFu) { - // We need to check input because we are incrementing the input. - add32(Imm32(delta + 1), index); - checkInput().linkTo(beginOp->m_reentry, this); - } - } - } - } - - // We can reach this point in the code in two ways: - // - Fallthrough from the code above (a repeating alternative backtracked out of its - // last alternative, and did not have sufficent input to run the first). - // - We will loop back up to the following label when a releating alternative loops, - // following a failed input check. - // - // Either way, we have just failed the input check for the first alternative. - Label firstInputCheckFailed(this); - - // Generate code to handle input check failures from alternatives except the last. - // prevOp is the alternative we're handling a bail out from (initially Begin), and - // nextOp is the alternative we will be attempting to reenter into. - // - // We will link input check failures from the forwards matching path back to the code - // that can handle them. - YarrOp* prevOp = beginOp; - YarrOp* nextOp = &m_ops[beginOp->m_nextOp]; - while (nextOp->m_op != OpBodyAlternativeEnd) { - prevOp->m_jumps.link(this); - - // We only get here if an input check fails, it is only worth checking again - // if the next alternative has a minimum size less than the last. - if (prevOp->m_alternative->m_minimumSize > nextOp->m_alternative->m_minimumSize) { - // FIXME: if we added an extra label to YarrOp, we could avoid needing to - // subtract delta back out, and reduce this code. Should performance test - // the benefit of this. - unsigned delta = prevOp->m_alternative->m_minimumSize - nextOp->m_alternative->m_minimumSize; - sub32(Imm32(delta), index); - Jump fail = jumpIfNoAvailableInput(); - add32(Imm32(delta), index); - jump(nextOp->m_reentry); - fail.link(this); - } else if (prevOp->m_alternative->m_minimumSize < nextOp->m_alternative->m_minimumSize) - add32(Imm32(nextOp->m_alternative->m_minimumSize - prevOp->m_alternative->m_minimumSize), index); - prevOp = nextOp; - nextOp = &m_ops[nextOp->m_nextOp]; - } - - // We fall through to here if there is insufficient input to run the last alternative. - - // If there is insufficient input to run the last alternative, then for 'once through' - // alternatives we are done - just jump back up into the forwards matching path at the End. - if (onceThrough) { - op.m_jumps.linkTo(endOp.m_reentry, this); - jump(endOp.m_reentry); - break; - } - - // For repeating alternatives, link any input check failure from the last alternative to - // this point. - op.m_jumps.link(this); - - bool needsToUpdateMatchStart = !m_pattern.m_body->m_hasFixedSize; - - // Check for cases where input position is already incremented by 1 for the last - // alternative (this is particularly useful where the minimum size of the body - // disjunction is 0, e.g. /a*|b/). - if (needsToUpdateMatchStart && alternative->m_minimumSize == 1) { - // index is already incremented by 1, so just store it now! - setMatchStart(index); - needsToUpdateMatchStart = false; - } - - // Check whether there is sufficient input to loop. Increment the input position by - // one, and check. Also add in the minimum disjunction size before checking - there - // is no point in looping if we're just going to fail all the input checks around - // the next iteration. - ASSERT(alternative->m_minimumSize >= m_pattern.m_body->m_minimumSize); - if (alternative->m_minimumSize == m_pattern.m_body->m_minimumSize) { - // If the last alternative had the same minimum size as the disjunction, - // just simply increment input pos by 1, no adjustment based on minimum size. - add32(TrustedImm32(1), index); - } else { - // If the minumum for the last alternative was one greater than than that - // for the disjunction, we're already progressed by 1, nothing to do! - unsigned delta = (alternative->m_minimumSize - m_pattern.m_body->m_minimumSize) - 1; - if (delta) - sub32(Imm32(delta), index); - } - Jump matchFailed = jumpIfNoAvailableInput(); - - if (needsToUpdateMatchStart) { - if (!m_pattern.m_body->m_minimumSize) - setMatchStart(index); - else { - move(index, regT0); - sub32(Imm32(m_pattern.m_body->m_minimumSize), regT0); - setMatchStart(regT0); - } - } - - // Calculate how much more input the first alternative requires than the minimum - // for the body as a whole. If no more is needed then we dont need an additional - // input check here - jump straight back up to the start of the first alternative. - if (beginOp->m_alternative->m_minimumSize == m_pattern.m_body->m_minimumSize) - jump(beginOp->m_reentry); - else { - if (beginOp->m_alternative->m_minimumSize > m_pattern.m_body->m_minimumSize) - add32(Imm32(beginOp->m_alternative->m_minimumSize - m_pattern.m_body->m_minimumSize), index); - else - sub32(Imm32(m_pattern.m_body->m_minimumSize - beginOp->m_alternative->m_minimumSize), index); - checkInput().linkTo(beginOp->m_reentry, this); - jump(firstInputCheckFailed); - } - - // We jump to here if we iterate to the point that there is insufficient input to - // run any matches, and need to return a failure state from JIT code. - matchFailed.link(this); - - removeCallFrame(); - move(TrustedImmPtr((void*)WTF::notFound), returnRegister); - move(TrustedImm32(0), returnRegister2); - generateReturn(); - break; - } - case OpBodyAlternativeEnd: { - // We should never backtrack back into a body disjunction. - ASSERT(m_backtrackingState.isEmpty()); - - PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; - m_checked += priorAlternative->m_minimumSize; - break; - } - - // OpSimpleNestedAlternativeBegin/Next/End - // OpNestedAlternativeBegin/Next/End - // - // Generate code for when we backtrack back out of an alternative into - // a Begin or Next node, or when the entry input count check fails. If - // there are more alternatives we need to jump to the next alternative, - // if not we backtrack back out of the current set of parentheses. - // - // In the case of non-simple nested assertions we need to also link the - // 'return address' appropriately to backtrack back out into the correct - // alternative. - case OpSimpleNestedAlternativeBegin: - case OpSimpleNestedAlternativeNext: - case OpNestedAlternativeBegin: - case OpNestedAlternativeNext: { - YarrOp& nextOp = m_ops[op.m_nextOp]; - bool isBegin = op.m_previousOp == notFound; - bool isLastAlternative = nextOp.m_nextOp == notFound; - ASSERT(isBegin == (op.m_op == OpSimpleNestedAlternativeBegin || op.m_op == OpNestedAlternativeBegin)); - ASSERT(isLastAlternative == (nextOp.m_op == OpSimpleNestedAlternativeEnd || nextOp.m_op == OpNestedAlternativeEnd)); - - // Treat an input check failure the same as a failed match. - m_backtrackingState.append(op.m_jumps); - - // Set the backtracks to jump to the appropriate place. We may need - // to link the backtracks in one of three different way depending on - // the type of alternative we are dealing with: - // - A single alternative, with no simplings. - // - The last alternative of a set of two or more. - // - An alternative other than the last of a set of two or more. - // - // In the case of a single alternative on its own, we don't need to - // jump anywhere - if the alternative fails to match we can just - // continue to backtrack out of the parentheses without jumping. - // - // In the case of the last alternative in a set of more than one, we - // need to jump to return back out to the beginning. We'll do so by - // adding a jump to the End node's m_jumps list, and linking this - // when we come to generate the Begin node. For alternatives other - // than the last, we need to jump to the next alternative. - // - // If the alternative had adjusted the input position we must link - // backtracking to here, correct, and then jump on. If not we can - // link the backtracks directly to their destination. - if (op.m_checkAdjust) { - // Handle the cases where we need to link the backtracks here. - m_backtrackingState.link(this); - sub32(Imm32(op.m_checkAdjust), index); - if (!isLastAlternative) { - // An alternative that is not the last should jump to its successor. - jump(nextOp.m_reentry); - } else if (!isBegin) { - // The last of more than one alternatives must jump back to the beginning. - nextOp.m_jumps.append(jump()); - } else { - // A single alternative on its own can fall through. - m_backtrackingState.fallthrough(); - } - } else { - // Handle the cases where we can link the backtracks directly to their destinations. - if (!isLastAlternative) { - // An alternative that is not the last should jump to its successor. - m_backtrackingState.linkTo(nextOp.m_reentry, this); - } else if (!isBegin) { - // The last of more than one alternatives must jump back to the beginning. - m_backtrackingState.takeBacktracksToJumpList(nextOp.m_jumps, this); - } - // In the case of a single alternative on its own do nothing - it can fall through. - } - - // If there is a backtrack jump from a zero length match link it here. - if (op.m_zeroLengthMatch.isSet()) - m_backtrackingState.append(op.m_zeroLengthMatch); - - // At this point we've handled the backtracking back into this node. - // Now link any backtracks that need to jump to here. - - // For non-simple alternatives, link the alternative's 'return address' - // so that we backtrack back out into the previous alternative. - if (op.m_op == OpNestedAlternativeNext) - m_backtrackingState.append(op.m_returnAddress); - - // If there is more than one alternative, then the last alternative will - // have planted a jump to be linked to the end. This jump was added to the - // End node's m_jumps list. If we are back at the beginning, link it here. - if (isBegin) { - YarrOp* endOp = &m_ops[op.m_nextOp]; - while (endOp->m_nextOp != notFound) { - ASSERT(endOp->m_op == OpSimpleNestedAlternativeNext || endOp->m_op == OpNestedAlternativeNext); - endOp = &m_ops[endOp->m_nextOp]; - } - ASSERT(endOp->m_op == OpSimpleNestedAlternativeEnd || endOp->m_op == OpNestedAlternativeEnd); - m_backtrackingState.append(endOp->m_jumps); - } - - if (!isBegin) { - YarrOp& lastOp = m_ops[op.m_previousOp]; - m_checked += lastOp.m_checkAdjust; - } - m_checked -= op.m_checkAdjust; - break; - } - case OpSimpleNestedAlternativeEnd: - case OpNestedAlternativeEnd: { - PatternTerm* term = op.m_term; - - // If there is a backtrack jump from a zero length match link it here. - if (op.m_zeroLengthMatch.isSet()) - m_backtrackingState.append(op.m_zeroLengthMatch); - - // If we backtrack into the end of a simple subpattern do nothing; - // just continue through into the last alternative. If we backtrack - // into the end of a non-simple set of alterntives we need to jump - // to the backtracking return address set up during generation. - if (op.m_op == OpNestedAlternativeEnd) { - m_backtrackingState.link(this); - - // Plant a jump to the return address. - unsigned parenthesesFrameLocation = term->frameLocation; - unsigned alternativeFrameLocation = parenthesesFrameLocation; - if (term->quantityType != QuantifierFixedCount) - alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; - loadFromFrameAndJump(alternativeFrameLocation); - - // Link the DataLabelPtr associated with the end of the last - // alternative to this point. - m_backtrackingState.append(op.m_returnAddress); - } - - YarrOp& lastOp = m_ops[op.m_previousOp]; - m_checked += lastOp.m_checkAdjust; - break; - } - - // OpParenthesesSubpatternOnceBegin/End - // - // When we are backtracking back out of a capturing subpattern we need - // to clear the start index in the matches output array, to record that - // this subpattern has not been captured. - // - // When backtracking back out of a Greedy quantified subpattern we need - // to catch this, and try running the remainder of the alternative after - // the subpattern again, skipping the parentheses. - // - // Upon backtracking back into a quantified set of parentheses we need to - // check whether we were currently skipping the subpattern. If not, we - // can backtrack into them, if we were we need to either backtrack back - // out of the start of the parentheses, or jump back to the forwards - // matching start, depending of whether the match is Greedy or NonGreedy. - case OpParenthesesSubpatternOnceBegin: { - PatternTerm* term = op.m_term; - ASSERT(term->quantityCount == 1); - - // We only need to backtrack to thispoint if capturing or greedy. - if ((term->capture() && compileMode == IncludeSubpatterns) || term->quantityType == QuantifierGreedy) { - m_backtrackingState.link(this); - - // If capturing, clear the capture (we only need to reset start). - if (term->capture() && compileMode == IncludeSubpatterns) - clearSubpatternStart(term->parentheses.subpatternId); - - // If Greedy, jump to the end. - if (term->quantityType == QuantifierGreedy) { - // Clear the flag in the stackframe indicating we ran through the subpattern. - unsigned parenthesesFrameLocation = term->frameLocation; - storeToFrame(TrustedImm32(-1), parenthesesFrameLocation); - // Jump to after the parentheses, skipping the subpattern. - jump(m_ops[op.m_nextOp].m_reentry); - // A backtrack from after the parentheses, when skipping the subpattern, - // will jump back to here. - op.m_jumps.link(this); - } - - m_backtrackingState.fallthrough(); - } - break; - } - case OpParenthesesSubpatternOnceEnd: { - PatternTerm* term = op.m_term; - - if (term->quantityType != QuantifierFixedCount) { - m_backtrackingState.link(this); - - // Check whether we should backtrack back into the parentheses, or if we - // are currently in a state where we had skipped over the subpattern - // (in which case the flag value on the stack will be -1). - unsigned parenthesesFrameLocation = term->frameLocation; - Jump hadSkipped = branch32(Equal, Address(stackPointerRegister, parenthesesFrameLocation * sizeof(void*)), TrustedImm32(-1)); - - if (term->quantityType == QuantifierGreedy) { - // For Greedy parentheses, we skip after having already tried going - // through the subpattern, so if we get here we're done. - YarrOp& beginOp = m_ops[op.m_previousOp]; - beginOp.m_jumps.append(hadSkipped); - } else { - // For NonGreedy parentheses, we try skipping the subpattern first, - // so if we get here we need to try running through the subpattern - // next. Jump back to the start of the parentheses in the forwards - // matching path. - ASSERT(term->quantityType == QuantifierNonGreedy); - YarrOp& beginOp = m_ops[op.m_previousOp]; - hadSkipped.linkTo(beginOp.m_reentry, this); - } - - m_backtrackingState.fallthrough(); - } - - m_backtrackingState.append(op.m_jumps); - break; - } - - // OpParenthesesSubpatternTerminalBegin/End - // - // Terminal subpatterns will always match - there is nothing after them to - // force a backtrack, and they have a minimum count of 0, and as such will - // always produce an acceptable result. - case OpParenthesesSubpatternTerminalBegin: { - // We will backtrack to this point once the subpattern cannot match any - // more. Since no match is accepted as a successful match (we are Greedy - // quantified with a minimum of zero) jump back to the forwards matching - // path at the end. - YarrOp& endOp = m_ops[op.m_nextOp]; - m_backtrackingState.linkTo(endOp.m_reentry, this); - break; - } - case OpParenthesesSubpatternTerminalEnd: - // We should never be backtracking to here (hence the 'terminal' in the name). - ASSERT(m_backtrackingState.isEmpty()); - m_backtrackingState.append(op.m_jumps); - break; - - // OpParentheticalAssertionBegin/End - case OpParentheticalAssertionBegin: { - PatternTerm* term = op.m_term; - YarrOp& endOp = m_ops[op.m_nextOp]; - - // We need to handle the backtracks upon backtracking back out - // of a parenthetical assertion if either we need to correct - // the input index, or the assertion was inverted. - if (op.m_checkAdjust || term->invert()) { - m_backtrackingState.link(this); - - if (op.m_checkAdjust) - add32(Imm32(op.m_checkAdjust), index); - - // In an inverted assertion failure to match the subpattern - // is treated as a successful match - jump to the end of the - // subpattern. We already have adjusted the input position - // back to that before the assertion, which is correct. - if (term->invert()) - jump(endOp.m_reentry); - - m_backtrackingState.fallthrough(); - } - - // The End node's jump list will contain any backtracks into - // the end of the assertion. Also, if inverted, we will have - // added the failure caused by a successful match to this. - m_backtrackingState.append(endOp.m_jumps); - - m_checked += op.m_checkAdjust; - break; - } - case OpParentheticalAssertionEnd: { - // FIXME: We should really be clearing any nested subpattern - // matches on bailing out from after the pattern. Firefox has - // this bug too (presumably because they use YARR!) - - // Never backtrack into an assertion; later failures bail to before the begin. - m_backtrackingState.takeBacktracksToJumpList(op.m_jumps, this); - - YarrOp& lastOp = m_ops[op.m_previousOp]; - m_checked -= lastOp.m_checkAdjust; - break; - } - - case OpMatchFailed: - break; - } - - } while (opIndex); - } - - // Compilation methods: - // ==================== - - // opCompileParenthesesSubpattern - // Emits ops for a subpattern (set of parentheses). These consist - // of a set of alternatives wrapped in an outer set of nodes for - // the parentheses. - // Supported types of parentheses are 'Once' (quantityCount == 1) - // and 'Terminal' (non-capturing parentheses quantified as greedy - // and infinite). - // Alternatives will use the 'Simple' set of ops if either the - // subpattern is terminal (in which case we will never need to - // backtrack), or if the subpattern only contains one alternative. - void opCompileParenthesesSubpattern(PatternTerm* term) - { - YarrOpCode parenthesesBeginOpCode; - YarrOpCode parenthesesEndOpCode; - YarrOpCode alternativeBeginOpCode = OpSimpleNestedAlternativeBegin; - YarrOpCode alternativeNextOpCode = OpSimpleNestedAlternativeNext; - YarrOpCode alternativeEndOpCode = OpSimpleNestedAlternativeEnd; - - // We can currently only compile quantity 1 subpatterns that are - // not copies. We generate a copy in the case of a range quantifier, - // e.g. /(?:x){3,9}/, or /(?:x)+/ (These are effectively expanded to - // /(?:x){3,3}(?:x){0,6}/ and /(?:x)(?:x)*/ repectively). The problem - // comes where the subpattern is capturing, in which case we would - // need to restore the capture from the first subpattern upon a - // failure in the second. - if (term->quantityCount == 1 && !term->parentheses.isCopy) { - // Select the 'Once' nodes. - parenthesesBeginOpCode = OpParenthesesSubpatternOnceBegin; - parenthesesEndOpCode = OpParenthesesSubpatternOnceEnd; - - // If there is more than one alternative we cannot use the 'simple' nodes. - if (term->parentheses.disjunction->m_alternatives.size() != 1) { - alternativeBeginOpCode = OpNestedAlternativeBegin; - alternativeNextOpCode = OpNestedAlternativeNext; - alternativeEndOpCode = OpNestedAlternativeEnd; - } - } else if (term->parentheses.isTerminal) { - // Select the 'Terminal' nodes. - parenthesesBeginOpCode = OpParenthesesSubpatternTerminalBegin; - parenthesesEndOpCode = OpParenthesesSubpatternTerminalEnd; - } else { - // This subpattern is not supported by the JIT. - m_shouldFallBack = true; - return; - } - - size_t parenBegin = m_ops.size(); - m_ops.append(parenthesesBeginOpCode); - - m_ops.append(alternativeBeginOpCode); - m_ops.last().m_previousOp = notFound; - m_ops.last().m_term = term; - Vector& alternatives = term->parentheses.disjunction->m_alternatives; - for (unsigned i = 0; i < alternatives.size(); ++i) { - size_t lastOpIndex = m_ops.size() - 1; - - PatternAlternative* nestedAlternative = alternatives[i]; - opCompileAlternative(nestedAlternative); - - size_t thisOpIndex = m_ops.size(); - m_ops.append(YarrOp(alternativeNextOpCode)); - - YarrOp& lastOp = m_ops[lastOpIndex]; - YarrOp& thisOp = m_ops[thisOpIndex]; - - lastOp.m_alternative = nestedAlternative; - lastOp.m_nextOp = thisOpIndex; - thisOp.m_previousOp = lastOpIndex; - thisOp.m_term = term; - } - YarrOp& lastOp = m_ops.last(); - ASSERT(lastOp.m_op == alternativeNextOpCode); - lastOp.m_op = alternativeEndOpCode; - lastOp.m_alternative = 0; - lastOp.m_nextOp = notFound; - - size_t parenEnd = m_ops.size(); - m_ops.append(parenthesesEndOpCode); - - m_ops[parenBegin].m_term = term; - m_ops[parenBegin].m_previousOp = notFound; - m_ops[parenBegin].m_nextOp = parenEnd; - m_ops[parenEnd].m_term = term; - m_ops[parenEnd].m_previousOp = parenBegin; - m_ops[parenEnd].m_nextOp = notFound; - } - - // opCompileParentheticalAssertion - // Emits ops for a parenthetical assertion. These consist of an - // OpSimpleNestedAlternativeBegin/Next/End set of nodes wrapping - // the alternatives, with these wrapped by an outer pair of - // OpParentheticalAssertionBegin/End nodes. - // We can always use the OpSimpleNestedAlternative nodes in the - // case of parenthetical assertions since these only ever match - // once, and will never backtrack back into the assertion. - void opCompileParentheticalAssertion(PatternTerm* term) - { - size_t parenBegin = m_ops.size(); - m_ops.append(OpParentheticalAssertionBegin); - - m_ops.append(OpSimpleNestedAlternativeBegin); - m_ops.last().m_previousOp = notFound; - m_ops.last().m_term = term; - Vector& alternatives = term->parentheses.disjunction->m_alternatives; - for (unsigned i = 0; i < alternatives.size(); ++i) { - size_t lastOpIndex = m_ops.size() - 1; - - PatternAlternative* nestedAlternative = alternatives[i]; - opCompileAlternative(nestedAlternative); - - size_t thisOpIndex = m_ops.size(); - m_ops.append(YarrOp(OpSimpleNestedAlternativeNext)); - - YarrOp& lastOp = m_ops[lastOpIndex]; - YarrOp& thisOp = m_ops[thisOpIndex]; - - lastOp.m_alternative = nestedAlternative; - lastOp.m_nextOp = thisOpIndex; - thisOp.m_previousOp = lastOpIndex; - thisOp.m_term = term; - } - YarrOp& lastOp = m_ops.last(); - ASSERT(lastOp.m_op == OpSimpleNestedAlternativeNext); - lastOp.m_op = OpSimpleNestedAlternativeEnd; - lastOp.m_alternative = 0; - lastOp.m_nextOp = notFound; - - size_t parenEnd = m_ops.size(); - m_ops.append(OpParentheticalAssertionEnd); - - m_ops[parenBegin].m_term = term; - m_ops[parenBegin].m_previousOp = notFound; - m_ops[parenBegin].m_nextOp = parenEnd; - m_ops[parenEnd].m_term = term; - m_ops[parenEnd].m_previousOp = parenBegin; - m_ops[parenEnd].m_nextOp = notFound; - } - - // opCompileAlternative - // Called to emit nodes for all terms in an alternative. - void opCompileAlternative(PatternAlternative* alternative) - { - optimizeAlternative(alternative); - - for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { - PatternTerm* term = &alternative->m_terms[i]; - - switch (term->type) { - case PatternTerm::TypeParenthesesSubpattern: - opCompileParenthesesSubpattern(term); - break; - - case PatternTerm::TypeParentheticalAssertion: - opCompileParentheticalAssertion(term); - break; - - default: - m_ops.append(term); - } - } - } - - // opCompileBody - // This method compiles the body disjunction of the regular expression. - // The body consists of two sets of alternatives - zero or more 'once - // through' (BOL anchored) alternatives, followed by zero or more - // repeated alternatives. - // For each of these two sets of alteratives, if not empty they will be - // wrapped in a set of OpBodyAlternativeBegin/Next/End nodes (with the - // 'begin' node referencing the first alternative, and 'next' nodes - // referencing any further alternatives. The begin/next/end nodes are - // linked together in a doubly linked list. In the case of repeating - // alternatives, the end node is also linked back to the beginning. - // If no repeating alternatives exist, then a OpMatchFailed node exists - // to return the failing result. - void opCompileBody(PatternDisjunction* disjunction) - { - Vector& alternatives = disjunction->m_alternatives; - size_t currentAlternativeIndex = 0; - - // Emit the 'once through' alternatives. - if (alternatives.size() && alternatives[0]->onceThrough()) { - m_ops.append(YarrOp(OpBodyAlternativeBegin)); - m_ops.last().m_previousOp = notFound; - - do { - size_t lastOpIndex = m_ops.size() - 1; - PatternAlternative* alternative = alternatives[currentAlternativeIndex]; - opCompileAlternative(alternative); - - size_t thisOpIndex = m_ops.size(); - m_ops.append(YarrOp(OpBodyAlternativeNext)); - - YarrOp& lastOp = m_ops[lastOpIndex]; - YarrOp& thisOp = m_ops[thisOpIndex]; - - lastOp.m_alternative = alternative; - lastOp.m_nextOp = thisOpIndex; - thisOp.m_previousOp = lastOpIndex; - - ++currentAlternativeIndex; - } while (currentAlternativeIndex < alternatives.size() && alternatives[currentAlternativeIndex]->onceThrough()); - - YarrOp& lastOp = m_ops.last(); - - ASSERT(lastOp.m_op == OpBodyAlternativeNext); - lastOp.m_op = OpBodyAlternativeEnd; - lastOp.m_alternative = 0; - lastOp.m_nextOp = notFound; - } - - if (currentAlternativeIndex == alternatives.size()) { - m_ops.append(YarrOp(OpMatchFailed)); - return; - } - - // Emit the repeated alternatives. - size_t repeatLoop = m_ops.size(); - m_ops.append(YarrOp(OpBodyAlternativeBegin)); - m_ops.last().m_previousOp = notFound; - do { - size_t lastOpIndex = m_ops.size() - 1; - PatternAlternative* alternative = alternatives[currentAlternativeIndex]; - ASSERT(!alternative->onceThrough()); - opCompileAlternative(alternative); - - size_t thisOpIndex = m_ops.size(); - m_ops.append(YarrOp(OpBodyAlternativeNext)); - - YarrOp& lastOp = m_ops[lastOpIndex]; - YarrOp& thisOp = m_ops[thisOpIndex]; - - lastOp.m_alternative = alternative; - lastOp.m_nextOp = thisOpIndex; - thisOp.m_previousOp = lastOpIndex; - - ++currentAlternativeIndex; - } while (currentAlternativeIndex < alternatives.size()); - YarrOp& lastOp = m_ops.last(); - ASSERT(lastOp.m_op == OpBodyAlternativeNext); - lastOp.m_op = OpBodyAlternativeEnd; - lastOp.m_alternative = 0; - lastOp.m_nextOp = repeatLoop; - } - - void generateEnter() - { -#if CPU(X86_64) - push(X86Registers::ebp); - move(stackPointerRegister, X86Registers::ebp); - push(X86Registers::ebx); -#elif CPU(X86) - push(X86Registers::ebp); - move(stackPointerRegister, X86Registers::ebp); - // TODO: do we need spill registers to fill the output pointer if there are no sub captures? - push(X86Registers::ebx); - push(X86Registers::edi); - push(X86Registers::esi); - // load output into edi (2 = saved ebp + return address). - #if COMPILER(MSVC) - loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), input); - loadPtr(Address(X86Registers::ebp, 3 * sizeof(void*)), index); - loadPtr(Address(X86Registers::ebp, 4 * sizeof(void*)), length); - if (compileMode == IncludeSubpatterns) - loadPtr(Address(X86Registers::ebp, 5 * sizeof(void*)), output); - #else - if (compileMode == IncludeSubpatterns) - loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), output); - #endif -#elif CPU(ARM) - push(ARMRegisters::r4); - push(ARMRegisters::r5); - push(ARMRegisters::r6); -#if CPU(ARM_TRADITIONAL) - push(ARMRegisters::r8); // scratch register -#endif - if (compileMode == IncludeSubpatterns) - move(ARMRegisters::r3, output); -#elif CPU(SH4) - push(SH4Registers::r11); - push(SH4Registers::r13); -#elif CPU(MIPS) - // Do nothing. -#endif - } - - void generateReturn() - { -#if CPU(X86_64) - pop(X86Registers::ebx); - pop(X86Registers::ebp); -#elif CPU(X86) - pop(X86Registers::esi); - pop(X86Registers::edi); - pop(X86Registers::ebx); - pop(X86Registers::ebp); -#elif CPU(ARM) -#if CPU(ARM_TRADITIONAL) - pop(ARMRegisters::r8); // scratch register -#endif - pop(ARMRegisters::r6); - pop(ARMRegisters::r5); - pop(ARMRegisters::r4); -#elif CPU(SH4) - pop(SH4Registers::r13); - pop(SH4Registers::r11); -#elif CPU(MIPS) - // Do nothing -#endif - ret(); - } - -public: - YarrGenerator(YarrPattern& pattern, YarrCharSize charSize) - : m_pattern(pattern) - , m_charSize(charSize) - , m_charScale(m_charSize == Char8 ? TimesOne: TimesTwo) - , m_shouldFallBack(false) - , m_checked(0) - { - } - - void compile(JSGlobalData* globalData, YarrCodeBlock& jitObject) - { - generateEnter(); - - Jump hasInput = checkInput(); - move(TrustedImmPtr((void*)WTF::notFound), returnRegister); - move(TrustedImm32(0), returnRegister2); - generateReturn(); - hasInput.link(this); - - if (compileMode == IncludeSubpatterns) { - for (unsigned i = 0; i < m_pattern.m_numSubpatterns + 1; ++i) - store32(TrustedImm32(-1), Address(output, (i << 1) * sizeof(int))); - } - - if (!m_pattern.m_body->m_hasFixedSize) - setMatchStart(index); - - initCallFrame(); - - // Compile the pattern to the internal 'YarrOp' representation. - opCompileBody(m_pattern.m_body); - - // If we encountered anything we can't handle in the JIT code - // (e.g. backreferences) then return early. - if (m_shouldFallBack) { - jitObject.setFallBack(true); - return; - } - - generate(); - backtrack(); - - // Link & finalize the code. - LinkBuffer linkBuffer(*globalData, this, REGEXP_CODE_ID); - m_backtrackingState.linkDataLabels(linkBuffer); - - if (compileMode == MatchOnly) { - if (m_charSize == Char8) - jitObject.set8BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, ("Match-only 8-bit regular expression"))); - else - jitObject.set16BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, ("Match-only 16-bit regular expression"))); - } else { - if (m_charSize == Char8) - jitObject.set8BitCode(FINALIZE_CODE(linkBuffer, ("8-bit regular expression"))); - else - jitObject.set16BitCode(FINALIZE_CODE(linkBuffer, ("16-bit regular expression"))); - } - jitObject.setFallBack(m_shouldFallBack); - } - -private: - YarrPattern& m_pattern; - - YarrCharSize m_charSize; - - Scale m_charScale; - - // Used to detect regular expression constructs that are not currently - // supported in the JIT; fall back to the interpreter when this is detected. - bool m_shouldFallBack; - - // The regular expression expressed as a linear sequence of operations. - Vector m_ops; - - // This records the current input offset being applied due to the current - // set of alternatives we are nested within. E.g. when matching the - // character 'b' within the regular expression /abc/, we will know that - // the minimum size for the alternative is 3, checked upon entry to the - // alternative, and that 'b' is at offset 1 from the start, and as such - // when matching 'b' we need to apply an offset of -2 to the load. - // - // FIXME: This should go away. Rather than tracking this value throughout - // code generation, we should gather this information up front & store it - // on the YarrOp structure. - int m_checked; - - // This class records state whilst generating the backtracking path of code. - BacktrackingState m_backtrackingState; -}; - -void jitCompile(YarrPattern& pattern, YarrCharSize charSize, JSGlobalData* globalData, YarrCodeBlock& jitObject, YarrJITCompileMode mode) -{ - if (mode == MatchOnly) - YarrGenerator(pattern, charSize).compile(globalData, jitObject); - else - YarrGenerator(pattern, charSize).compile(globalData, jitObject); -} - -}} - -#endif diff --git a/masm/yarr/YarrJIT.h b/masm/yarr/YarrJIT.h deleted file mode 100644 index bb7033fdea..0000000000 --- a/masm/yarr/YarrJIT.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef YarrJIT_h -#define YarrJIT_h - -#if ENABLE(YARR_JIT) - -#include "JSGlobalData.h" -#include "MacroAssemblerCodeRef.h" -#include "MatchResult.h" -#include "Yarr.h" -#include "YarrPattern.h" - -#if CPU(X86) && !COMPILER(MSVC) -#define YARR_CALL __attribute__ ((regparm (3))) -#else -#define YARR_CALL -#endif - -namespace JSC { - -class JSGlobalData; -class ExecutablePool; - -namespace Yarr { - -class YarrCodeBlock { -#if CPU(X86_64) - typedef MatchResult (*YarrJITCode8)(const LChar* input, unsigned start, unsigned length, int* output) YARR_CALL; - typedef MatchResult (*YarrJITCode16)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL; - typedef MatchResult (*YarrJITCodeMatchOnly8)(const LChar* input, unsigned start, unsigned length) YARR_CALL; - typedef MatchResult (*YarrJITCodeMatchOnly16)(const UChar* input, unsigned start, unsigned length) YARR_CALL; -#else - typedef EncodedMatchResult (*YarrJITCode8)(const LChar* input, unsigned start, unsigned length, int* output) YARR_CALL; - typedef EncodedMatchResult (*YarrJITCode16)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL; - typedef EncodedMatchResult (*YarrJITCodeMatchOnly8)(const LChar* input, unsigned start, unsigned length) YARR_CALL; - typedef EncodedMatchResult (*YarrJITCodeMatchOnly16)(const UChar* input, unsigned start, unsigned length) YARR_CALL; -#endif - -public: - YarrCodeBlock() - : m_needFallBack(false) - { - } - - ~YarrCodeBlock() - { - } - - void setFallBack(bool fallback) { m_needFallBack = fallback; } - bool isFallBack() { return m_needFallBack; } - - bool has8BitCode() { return m_ref8.size(); } - bool has16BitCode() { return m_ref16.size(); } - void set8BitCode(MacroAssemblerCodeRef ref) { m_ref8 = ref; } - void set16BitCode(MacroAssemblerCodeRef ref) { m_ref16 = ref; } - - bool has8BitCodeMatchOnly() { return m_matchOnly8.size(); } - bool has16BitCodeMatchOnly() { return m_matchOnly16.size(); } - void set8BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly8 = matchOnly; } - void set16BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly16 = matchOnly; } - - MatchResult execute(const LChar* input, unsigned start, unsigned length, int* output) - { - ASSERT(has8BitCode()); - return MatchResult(reinterpret_cast(m_ref8.code().executableAddress())(input, start, length, output)); - } - - MatchResult execute(const UChar* input, unsigned start, unsigned length, int* output) - { - ASSERT(has16BitCode()); - return MatchResult(reinterpret_cast(m_ref16.code().executableAddress())(input, start, length, output)); - } - - MatchResult execute(const LChar* input, unsigned start, unsigned length) - { - ASSERT(has8BitCodeMatchOnly()); - return MatchResult(reinterpret_cast(m_matchOnly8.code().executableAddress())(input, start, length)); - } - - MatchResult execute(const UChar* input, unsigned start, unsigned length) - { - ASSERT(has16BitCodeMatchOnly()); - return MatchResult(reinterpret_cast(m_matchOnly16.code().executableAddress())(input, start, length)); - } - -#if ENABLE(REGEXP_TRACING) - void *getAddr() { return m_ref.code().executableAddress(); } -#endif - - void clear() - { - m_ref8 = MacroAssemblerCodeRef(); - m_ref16 = MacroAssemblerCodeRef(); - m_matchOnly8 = MacroAssemblerCodeRef(); - m_matchOnly16 = MacroAssemblerCodeRef(); - m_needFallBack = false; - } - -private: - MacroAssemblerCodeRef m_ref8; - MacroAssemblerCodeRef m_ref16; - MacroAssemblerCodeRef m_matchOnly8; - MacroAssemblerCodeRef m_matchOnly16; - bool m_needFallBack; -}; - -enum YarrJITCompileMode { - MatchOnly, - IncludeSubpatterns -}; -void jitCompile(YarrPattern&, YarrCharSize, JSGlobalData*, YarrCodeBlock& jitObject, YarrJITCompileMode = IncludeSubpatterns); - -} } // namespace JSC::Yarr - -#endif - -#endif // YarrJIT_h diff --git a/masm/yarr/YarrParser.h b/masm/yarr/YarrParser.h deleted file mode 100644 index 4bab1a0903..0000000000 --- a/masm/yarr/YarrParser.h +++ /dev/null @@ -1,880 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef YarrParser_h -#define YarrParser_h - -#include "Yarr.h" -#include -#include -#include - -namespace JSC { namespace Yarr { - -#define REGEXP_ERROR_PREFIX "Invalid regular expression: " - -enum BuiltInCharacterClassID { - DigitClassID, - SpaceClassID, - WordClassID, - NewlineClassID, -}; - -// The Parser class should not be used directly - only via the Yarr::parse() method. -template -class Parser { -private: - template - friend const char* parse(FriendDelegate&, const String& pattern, unsigned backReferenceLimit); - - enum ErrorCode { - NoError, - PatternTooLarge, - QuantifierOutOfOrder, - QuantifierWithoutAtom, - QuantifierTooLarge, - MissingParentheses, - ParenthesesUnmatched, - ParenthesesTypeInvalid, - CharacterClassUnmatched, - CharacterClassOutOfOrder, - EscapeUnterminated, - NumberOfErrorCodes - }; - - /* - * CharacterClassParserDelegate: - * - * The class CharacterClassParserDelegate is used in the parsing of character - * classes. This class handles detection of character ranges. This class - * implements enough of the delegate interface such that it can be passed to - * parseEscape() as an EscapeDelegate. This allows parseEscape() to be reused - * to perform the parsing of escape characters in character sets. - */ - class CharacterClassParserDelegate { - public: - CharacterClassParserDelegate(Delegate& delegate, ErrorCode& err) - : m_delegate(delegate) - , m_err(err) - , m_state(Empty) - , m_character(0) - { - } - - /* - * begin(): - * - * Called at beginning of construction. - */ - void begin(bool invert) - { - m_delegate.atomCharacterClassBegin(invert); - } - - /* - * atomPatternCharacter(): - * - * This method is called either from parseCharacterClass() (for an unescaped - * character in a character class), or from parseEscape(). In the former case - * the value true will be passed for the argument 'hyphenIsRange', and in this - * mode we will allow a hypen to be treated as indicating a range (i.e. /[a-z]/ - * is different to /[a\-z]/). - */ - void atomPatternCharacter(UChar ch, bool hyphenIsRange = false) - { - switch (m_state) { - case AfterCharacterClass: - // Following a builtin character class we need look out for a hyphen. - // We're looking for invalid ranges, such as /[\d-x]/ or /[\d-\d]/. - // If we see a hyphen following a charater class then unlike usual - // we'll report it to the delegate immediately, and put ourself into - // a poisoned state. Any following calls to add another character or - // character class will result in an error. (A hypen following a - // character-class is itself valid, but only at the end of a regex). - if (hyphenIsRange && ch == '-') { - m_delegate.atomCharacterClassAtom('-'); - m_state = AfterCharacterClassHyphen; - return; - } - // Otherwise just fall through - cached character so treat this as Empty. - - case Empty: - m_character = ch; - m_state = CachedCharacter; - return; - - case CachedCharacter: - if (hyphenIsRange && ch == '-') - m_state = CachedCharacterHyphen; - else { - m_delegate.atomCharacterClassAtom(m_character); - m_character = ch; - } - return; - - case CachedCharacterHyphen: - if (ch < m_character) { - m_err = CharacterClassOutOfOrder; - return; - } - m_delegate.atomCharacterClassRange(m_character, ch); - m_state = Empty; - return; - - // See coment in atomBuiltInCharacterClass below. - // This too is technically an error, per ECMA-262, and again we - // we chose to allow this. Note a subtlely here that while we - // diverge from the spec's definition of CharacterRange we do - // remain in compliance with the grammar. For example, consider - // the expression /[\d-a-z]/. We comply with the grammar in - // this case by not allowing a-z to be matched as a range. - case AfterCharacterClassHyphen: - m_delegate.atomCharacterClassAtom(ch); - m_state = Empty; - return; - } - } - - /* - * atomBuiltInCharacterClass(): - * - * Adds a built-in character class, called by parseEscape(). - */ - void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert) - { - switch (m_state) { - case CachedCharacter: - // Flush the currently cached character, then fall through. - m_delegate.atomCharacterClassAtom(m_character); - - case Empty: - case AfterCharacterClass: - m_state = AfterCharacterClass; - m_delegate.atomCharacterClassBuiltIn(classID, invert); - return; - - // If we hit either of these cases, we have an invalid range that - // looks something like /[x-\d]/ or /[\d-\d]/. - // According to ECMA-262 this should be a syntax error, but - // empirical testing shows this to break teh webz. Instead we - // comply with to the ECMA-262 grammar, and assume the grammar to - // have matched the range correctly, but tweak our interpretation - // of CharacterRange. Effectively we implicitly handle the hyphen - // as if it were escaped, e.g. /[\w-_]/ is treated as /[\w\-_]/. - case CachedCharacterHyphen: - m_delegate.atomCharacterClassAtom(m_character); - m_delegate.atomCharacterClassAtom('-'); - // fall through - case AfterCharacterClassHyphen: - m_delegate.atomCharacterClassBuiltIn(classID, invert); - m_state = Empty; - return; - } - } - - /* - * end(): - * - * Called at end of construction. - */ - void end() - { - if (m_state == CachedCharacter) - m_delegate.atomCharacterClassAtom(m_character); - else if (m_state == CachedCharacterHyphen) { - m_delegate.atomCharacterClassAtom(m_character); - m_delegate.atomCharacterClassAtom('-'); - } - m_delegate.atomCharacterClassEnd(); - } - - // parseEscape() should never call these delegate methods when - // invoked with inCharacterClass set. - NO_RETURN_DUE_TO_ASSERT void assertionWordBoundary(bool) { ASSERT_NOT_REACHED(); } - NO_RETURN_DUE_TO_ASSERT void atomBackReference(unsigned) { ASSERT_NOT_REACHED(); } - - private: - Delegate& m_delegate; - ErrorCode& m_err; - enum CharacterClassConstructionState { - Empty, - CachedCharacter, - CachedCharacterHyphen, - AfterCharacterClass, - AfterCharacterClassHyphen, - } m_state; - UChar m_character; - }; - - Parser(Delegate& delegate, const String& pattern, unsigned backReferenceLimit) - : m_delegate(delegate) - , m_backReferenceLimit(backReferenceLimit) - , m_err(NoError) - , m_data(pattern.getCharacters()) - , m_size(pattern.length()) - , m_index(0) - , m_parenthesesNestingDepth(0) - { - } - - /* - * parseEscape(): - * - * Helper for parseTokens() AND parseCharacterClass(). - * Unlike the other parser methods, this function does not report tokens - * directly to the member delegate (m_delegate), instead tokens are - * emitted to the delegate provided as an argument. In the case of atom - * escapes, parseTokens() will call parseEscape() passing m_delegate as - * an argument, and as such the escape will be reported to the delegate. - * - * However this method may also be used by parseCharacterClass(), in which - * case a CharacterClassParserDelegate will be passed as the delegate that - * tokens should be added to. A boolean flag is also provided to indicate - * whether that an escape in a CharacterClass is being parsed (some parsing - * rules change in this context). - * - * The boolean value returned by this method indicates whether the token - * parsed was an atom (outside of a characted class \b and \B will be - * interpreted as assertions). - */ - template - bool parseEscape(EscapeDelegate& delegate) - { - ASSERT(!m_err); - ASSERT(peek() == '\\'); - consume(); - - if (atEndOfPattern()) { - m_err = EscapeUnterminated; - return false; - } - - switch (peek()) { - // Assertions - case 'b': - consume(); - if (inCharacterClass) - delegate.atomPatternCharacter('\b'); - else { - delegate.assertionWordBoundary(false); - return false; - } - break; - case 'B': - consume(); - if (inCharacterClass) - delegate.atomPatternCharacter('B'); - else { - delegate.assertionWordBoundary(true); - return false; - } - break; - - // CharacterClassEscape - case 'd': - consume(); - delegate.atomBuiltInCharacterClass(DigitClassID, false); - break; - case 's': - consume(); - delegate.atomBuiltInCharacterClass(SpaceClassID, false); - break; - case 'w': - consume(); - delegate.atomBuiltInCharacterClass(WordClassID, false); - break; - case 'D': - consume(); - delegate.atomBuiltInCharacterClass(DigitClassID, true); - break; - case 'S': - consume(); - delegate.atomBuiltInCharacterClass(SpaceClassID, true); - break; - case 'W': - consume(); - delegate.atomBuiltInCharacterClass(WordClassID, true); - break; - - // DecimalEscape - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - // To match Firefox, we parse an invalid backreference in the range [1-7] as an octal escape. - // First, try to parse this as backreference. - if (!inCharacterClass) { - ParseState state = saveState(); - - unsigned backReference = consumeNumber(); - if (backReference <= m_backReferenceLimit) { - delegate.atomBackReference(backReference); - break; - } - - restoreState(state); - } - - // Not a backreference, and not octal. - if (peek() >= '8') { - delegate.atomPatternCharacter('\\'); - break; - } - - // Fall-through to handle this as an octal escape. - } - - // Octal escape - case '0': - delegate.atomPatternCharacter(consumeOctal()); - break; - - // ControlEscape - case 'f': - consume(); - delegate.atomPatternCharacter('\f'); - break; - case 'n': - consume(); - delegate.atomPatternCharacter('\n'); - break; - case 'r': - consume(); - delegate.atomPatternCharacter('\r'); - break; - case 't': - consume(); - delegate.atomPatternCharacter('\t'); - break; - case 'v': - consume(); - delegate.atomPatternCharacter('\v'); - break; - - // ControlLetter - case 'c': { - ParseState state = saveState(); - consume(); - if (!atEndOfPattern()) { - int control = consume(); - - // To match Firefox, inside a character class, we also accept numbers and '_' as control characters. - if (inCharacterClass ? WTF::isASCIIAlphanumeric(control) || (control == '_') : WTF::isASCIIAlpha(control)) { - delegate.atomPatternCharacter(control & 0x1f); - break; - } - } - restoreState(state); - delegate.atomPatternCharacter('\\'); - break; - } - - // HexEscape - case 'x': { - consume(); - int x = tryConsumeHex(2); - if (x == -1) - delegate.atomPatternCharacter('x'); - else - delegate.atomPatternCharacter(x); - break; - } - - // UnicodeEscape - case 'u': { - consume(); - int u = tryConsumeHex(4); - if (u == -1) - delegate.atomPatternCharacter('u'); - else - delegate.atomPatternCharacter(u); - break; - } - - // IdentityEscape - default: - delegate.atomPatternCharacter(consume()); - } - - return true; - } - - /* - * parseAtomEscape(), parseCharacterClassEscape(): - * - * These methods alias to parseEscape(). - */ - bool parseAtomEscape() - { - return parseEscape(m_delegate); - } - void parseCharacterClassEscape(CharacterClassParserDelegate& delegate) - { - parseEscape(delegate); - } - - /* - * parseCharacterClass(): - * - * Helper for parseTokens(); calls dirctly and indirectly (via parseCharacterClassEscape) - * to an instance of CharacterClassParserDelegate, to describe the character class to the - * delegate. - */ - void parseCharacterClass() - { - ASSERT(!m_err); - ASSERT(peek() == '['); - consume(); - - CharacterClassParserDelegate characterClassConstructor(m_delegate, m_err); - - characterClassConstructor.begin(tryConsume('^')); - - while (!atEndOfPattern()) { - switch (peek()) { - case ']': - consume(); - characterClassConstructor.end(); - return; - - case '\\': - parseCharacterClassEscape(characterClassConstructor); - break; - - default: - characterClassConstructor.atomPatternCharacter(consume(), true); - } - - if (m_err) - return; - } - - m_err = CharacterClassUnmatched; - } - - /* - * parseParenthesesBegin(): - * - * Helper for parseTokens(); checks for parentheses types other than regular capturing subpatterns. - */ - void parseParenthesesBegin() - { - ASSERT(!m_err); - ASSERT(peek() == '('); - consume(); - - if (tryConsume('?')) { - if (atEndOfPattern()) { - m_err = ParenthesesTypeInvalid; - return; - } - - switch (consume()) { - case ':': - m_delegate.atomParenthesesSubpatternBegin(false); - break; - - case '=': - m_delegate.atomParentheticalAssertionBegin(); - break; - - case '!': - m_delegate.atomParentheticalAssertionBegin(true); - break; - - default: - m_err = ParenthesesTypeInvalid; - } - } else - m_delegate.atomParenthesesSubpatternBegin(); - - ++m_parenthesesNestingDepth; - } - - /* - * parseParenthesesEnd(): - * - * Helper for parseTokens(); checks for parse errors (due to unmatched parentheses). - */ - void parseParenthesesEnd() - { - ASSERT(!m_err); - ASSERT(peek() == ')'); - consume(); - - if (m_parenthesesNestingDepth > 0) - m_delegate.atomParenthesesEnd(); - else - m_err = ParenthesesUnmatched; - - --m_parenthesesNestingDepth; - } - - /* - * parseQuantifier(): - * - * Helper for parseTokens(); checks for parse errors and non-greedy quantifiers. - */ - void parseQuantifier(bool lastTokenWasAnAtom, unsigned min, unsigned max) - { - ASSERT(!m_err); - ASSERT(min <= max); - - if (min == UINT_MAX) { - m_err = QuantifierTooLarge; - return; - } - - if (lastTokenWasAnAtom) - m_delegate.quantifyAtom(min, max, !tryConsume('?')); - else - m_err = QuantifierWithoutAtom; - } - - /* - * parseTokens(): - * - * This method loops over the input pattern reporting tokens to the delegate. - * The method returns when a parse error is detected, or the end of the pattern - * is reached. One piece of state is tracked around the loop, which is whether - * the last token passed to the delegate was an atom (this is necessary to detect - * a parse error when a quantifier provided without an atom to quantify). - */ - void parseTokens() - { - bool lastTokenWasAnAtom = false; - - while (!atEndOfPattern()) { - switch (peek()) { - case '|': - consume(); - m_delegate.disjunction(); - lastTokenWasAnAtom = false; - break; - - case '(': - parseParenthesesBegin(); - lastTokenWasAnAtom = false; - break; - - case ')': - parseParenthesesEnd(); - lastTokenWasAnAtom = true; - break; - - case '^': - consume(); - m_delegate.assertionBOL(); - lastTokenWasAnAtom = false; - break; - - case '$': - consume(); - m_delegate.assertionEOL(); - lastTokenWasAnAtom = false; - break; - - case '.': - consume(); - m_delegate.atomBuiltInCharacterClass(NewlineClassID, true); - lastTokenWasAnAtom = true; - break; - - case '[': - parseCharacterClass(); - lastTokenWasAnAtom = true; - break; - - case '\\': - lastTokenWasAnAtom = parseAtomEscape(); - break; - - case '*': - consume(); - parseQuantifier(lastTokenWasAnAtom, 0, quantifyInfinite); - lastTokenWasAnAtom = false; - break; - - case '+': - consume(); - parseQuantifier(lastTokenWasAnAtom, 1, quantifyInfinite); - lastTokenWasAnAtom = false; - break; - - case '?': - consume(); - parseQuantifier(lastTokenWasAnAtom, 0, 1); - lastTokenWasAnAtom = false; - break; - - case '{': { - ParseState state = saveState(); - - consume(); - if (peekIsDigit()) { - unsigned min = consumeNumber(); - unsigned max = min; - - if (tryConsume(',')) - max = peekIsDigit() ? consumeNumber() : quantifyInfinite; - - if (tryConsume('}')) { - if (min <= max) - parseQuantifier(lastTokenWasAnAtom, min, max); - else - m_err = QuantifierOutOfOrder; - lastTokenWasAnAtom = false; - break; - } - } - - restoreState(state); - } // if we did not find a complete quantifer, fall through to the default case. - - default: - m_delegate.atomPatternCharacter(consume()); - lastTokenWasAnAtom = true; - } - - if (m_err) - return; - } - - if (m_parenthesesNestingDepth > 0) - m_err = MissingParentheses; - } - - /* - * parse(): - * - * This method calls parseTokens() to parse over the input and converts any - * error code to a const char* for a result. - */ - const char* parse() - { - if (m_size > MAX_PATTERN_SIZE) - m_err = PatternTooLarge; - else - parseTokens(); - ASSERT(atEndOfPattern() || m_err); - - // The order of this array must match the ErrorCode enum. - static const char* errorMessages[NumberOfErrorCodes] = { - 0, // NoError - REGEXP_ERROR_PREFIX "regular expression too large", - REGEXP_ERROR_PREFIX "numbers out of order in {} quantifier", - REGEXP_ERROR_PREFIX "nothing to repeat", - REGEXP_ERROR_PREFIX "number too large in {} quantifier", - REGEXP_ERROR_PREFIX "missing )", - REGEXP_ERROR_PREFIX "unmatched parentheses", - REGEXP_ERROR_PREFIX "unrecognized character after (?", - REGEXP_ERROR_PREFIX "missing terminating ] for character class", - REGEXP_ERROR_PREFIX "range out of order in character class", - REGEXP_ERROR_PREFIX "\\ at end of pattern" - }; - - return errorMessages[m_err]; - } - - // Misc helper functions: - - typedef unsigned ParseState; - - ParseState saveState() - { - return m_index; - } - - void restoreState(ParseState state) - { - m_index = state; - } - - bool atEndOfPattern() - { - ASSERT(m_index <= m_size); - return m_index == m_size; - } - - int peek() - { - ASSERT(m_index < m_size); - return m_data[m_index]; - } - - bool peekIsDigit() - { - return !atEndOfPattern() && WTF::isASCIIDigit(peek()); - } - - unsigned peekDigit() - { - ASSERT(peekIsDigit()); - return peek() - '0'; - } - - int consume() - { - ASSERT(m_index < m_size); - return m_data[m_index++]; - } - - unsigned consumeDigit() - { - ASSERT(peekIsDigit()); - return consume() - '0'; - } - - unsigned consumeNumber() - { - unsigned n = consumeDigit(); - // check for overflow. - for (unsigned newValue; peekIsDigit() && ((newValue = n * 10 + peekDigit()) >= n); ) { - n = newValue; - consume(); - } - return n; - } - - unsigned consumeOctal() - { - ASSERT(WTF::isASCIIOctalDigit(peek())); - - unsigned n = consumeDigit(); - while (n < 32 && !atEndOfPattern() && WTF::isASCIIOctalDigit(peek())) - n = n * 8 + consumeDigit(); - return n; - } - - bool tryConsume(UChar ch) - { - if (atEndOfPattern() || (m_data[m_index] != ch)) - return false; - ++m_index; - return true; - } - - int tryConsumeHex(int count) - { - ParseState state = saveState(); - - int n = 0; - while (count--) { - if (atEndOfPattern() || !WTF::isASCIIHexDigit(peek())) { - restoreState(state); - return -1; - } - n = (n << 4) | WTF::toASCIIHexValue(consume()); - } - return n; - } - - Delegate& m_delegate; - unsigned m_backReferenceLimit; - ErrorCode m_err; - const CharType* m_data; - unsigned m_size; - unsigned m_index; - unsigned m_parenthesesNestingDepth; - - // Derived by empirical testing of compile time in PCRE and WREC. - static const unsigned MAX_PATTERN_SIZE = 1024 * 1024; -}; - -/* - * Yarr::parse(): - * - * The parse method is passed a pattern to be parsed and a delegate upon which - * callbacks will be made to record the parsed tokens forming the regex. - * Yarr::parse() returns null on success, or a const C string providing an error - * message where a parse error occurs. - * - * The Delegate must implement the following interface: - * - * void assertionBOL(); - * void assertionEOL(); - * void assertionWordBoundary(bool invert); - * - * void atomPatternCharacter(UChar ch); - * void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert); - * void atomCharacterClassBegin(bool invert) - * void atomCharacterClassAtom(UChar ch) - * void atomCharacterClassRange(UChar begin, UChar end) - * void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert) - * void atomCharacterClassEnd() - * void atomParenthesesSubpatternBegin(bool capture = true); - * void atomParentheticalAssertionBegin(bool invert = false); - * void atomParenthesesEnd(); - * void atomBackReference(unsigned subpatternId); - * - * void quantifyAtom(unsigned min, unsigned max, bool greedy); - * - * void disjunction(); - * - * The regular expression is described by a sequence of assertion*() and atom*() - * callbacks to the delegate, describing the terms in the regular expression. - * Following an atom a quantifyAtom() call may occur to indicate that the previous - * atom should be quantified. In the case of atoms described across multiple - * calls (parentheses and character classes) the call to quantifyAtom() will come - * after the call to the atom*End() method, never after atom*Begin(). - * - * Character classes may either be described by a single call to - * atomBuiltInCharacterClass(), or by a sequence of atomCharacterClass*() calls. - * In the latter case, ...Begin() will be called, followed by a sequence of - * calls to ...Atom(), ...Range(), and ...BuiltIn(), followed by a call to ...End(). - * - * Sequences of atoms and assertions are broken into alternatives via calls to - * disjunction(). Assertions, atoms, and disjunctions emitted between calls to - * atomParenthesesBegin() and atomParenthesesEnd() form the body of a subpattern. - * atomParenthesesBegin() is passed a subpatternId. In the case of a regular - * capturing subpattern, this will be the subpatternId associated with these - * parentheses, and will also by definition be the lowest subpatternId of these - * parentheses and of any nested paretheses. The atomParenthesesEnd() method - * is passed the subpatternId of the last capturing subexpression nested within - * these paretheses. In the case of a capturing subpattern with no nested - * capturing subpatterns, the same subpatternId will be passed to the begin and - * end functions. In the case of non-capturing subpatterns the subpatternId - * passed to the begin method is also the first possible subpatternId that might - * be nested within these paretheses. If a set of non-capturing parentheses does - * not contain any capturing subpatterns, then the subpatternId passed to begin - * will be greater than the subpatternId passed to end. - */ - -template -const char* parse(Delegate& delegate, const String& pattern, unsigned backReferenceLimit = quantifyInfinite) -{ - if (pattern.is8Bit()) - return Parser(delegate, pattern, backReferenceLimit).parse(); - return Parser(delegate, pattern, backReferenceLimit).parse(); -} - -} } // namespace JSC::Yarr - -#endif // YarrParser_h diff --git a/masm/yarr/YarrPattern.cpp b/masm/yarr/YarrPattern.cpp deleted file mode 100644 index c953a38d2f..0000000000 --- a/masm/yarr/YarrPattern.cpp +++ /dev/null @@ -1,874 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "YarrPattern.h" - -#include "Yarr.h" -#include "YarrCanonicalizeUCS2.h" -#include "YarrParser.h" -#include - -using namespace WTF; - -namespace JSC { namespace Yarr { - -#include "RegExpJitTables.h" - -class CharacterClassConstructor { -public: - CharacterClassConstructor(bool isCaseInsensitive = false) - : m_isCaseInsensitive(isCaseInsensitive) - { - } - - void reset() - { - m_matches.clear(); - m_ranges.clear(); - m_matchesUnicode.clear(); - m_rangesUnicode.clear(); - } - - void append(const CharacterClass* other) - { - for (size_t i = 0; i < other->m_matches.size(); ++i) - addSorted(m_matches, other->m_matches[i]); - for (size_t i = 0; i < other->m_ranges.size(); ++i) - addSortedRange(m_ranges, other->m_ranges[i].begin, other->m_ranges[i].end); - for (size_t i = 0; i < other->m_matchesUnicode.size(); ++i) - addSorted(m_matchesUnicode, other->m_matchesUnicode[i]); - for (size_t i = 0; i < other->m_rangesUnicode.size(); ++i) - addSortedRange(m_rangesUnicode, other->m_rangesUnicode[i].begin, other->m_rangesUnicode[i].end); - } - - void putChar(UChar ch) - { - // Handle ascii cases. - if (ch <= 0x7f) { - if (m_isCaseInsensitive && isASCIIAlpha(ch)) { - addSorted(m_matches, toASCIIUpper(ch)); - addSorted(m_matches, toASCIILower(ch)); - } else - addSorted(m_matches, ch); - return; - } - - // Simple case, not a case-insensitive match. - if (!m_isCaseInsensitive) { - addSorted(m_matchesUnicode, ch); - return; - } - - // Add multiple matches, if necessary. - UCS2CanonicalizationRange* info = rangeInfoFor(ch); - if (info->type == CanonicalizeUnique) - addSorted(m_matchesUnicode, ch); - else - putUnicodeIgnoreCase(ch, info); - } - - void putUnicodeIgnoreCase(UChar ch, UCS2CanonicalizationRange* info) - { - ASSERT(m_isCaseInsensitive); - ASSERT(ch > 0x7f); - ASSERT(ch >= info->begin && ch <= info->end); - ASSERT(info->type != CanonicalizeUnique); - if (info->type == CanonicalizeSet) { - for (uint16_t* set = characterSetInfo[info->value]; (ch = *set); ++set) - addSorted(m_matchesUnicode, ch); - } else { - addSorted(m_matchesUnicode, ch); - addSorted(m_matchesUnicode, getCanonicalPair(info, ch)); - } - } - - void putRange(UChar lo, UChar hi) - { - if (lo <= 0x7f) { - char asciiLo = lo; - char asciiHi = std::min(hi, (UChar)0x7f); - addSortedRange(m_ranges, lo, asciiHi); - - if (m_isCaseInsensitive) { - if ((asciiLo <= 'Z') && (asciiHi >= 'A')) - addSortedRange(m_ranges, std::max(asciiLo, 'A')+('a'-'A'), std::min(asciiHi, 'Z')+('a'-'A')); - if ((asciiLo <= 'z') && (asciiHi >= 'a')) - addSortedRange(m_ranges, std::max(asciiLo, 'a')+('A'-'a'), std::min(asciiHi, 'z')+('A'-'a')); - } - } - if (hi <= 0x7f) - return; - - lo = std::max(lo, (UChar)0x80); - addSortedRange(m_rangesUnicode, lo, hi); - - if (!m_isCaseInsensitive) - return; - - UCS2CanonicalizationRange* info = rangeInfoFor(lo); - while (true) { - // Handle the range [lo .. end] - UChar end = std::min(info->end, hi); - - switch (info->type) { - case CanonicalizeUnique: - // Nothing to do - no canonical equivalents. - break; - case CanonicalizeSet: { - UChar ch; - for (uint16_t* set = characterSetInfo[info->value]; (ch = *set); ++set) - addSorted(m_matchesUnicode, ch); - break; - } - case CanonicalizeRangeLo: - addSortedRange(m_rangesUnicode, lo + info->value, end + info->value); - break; - case CanonicalizeRangeHi: - addSortedRange(m_rangesUnicode, lo - info->value, end - info->value); - break; - case CanonicalizeAlternatingAligned: - // Use addSortedRange since there is likely an abutting range to combine with. - if (lo & 1) - addSortedRange(m_rangesUnicode, lo - 1, lo - 1); - if (!(end & 1)) - addSortedRange(m_rangesUnicode, end + 1, end + 1); - break; - case CanonicalizeAlternatingUnaligned: - // Use addSortedRange since there is likely an abutting range to combine with. - if (!(lo & 1)) - addSortedRange(m_rangesUnicode, lo - 1, lo - 1); - if (end & 1) - addSortedRange(m_rangesUnicode, end + 1, end + 1); - break; - } - - if (hi == end) - return; - - ++info; - lo = info->begin; - }; - - } - - CharacterClass* charClass() - { - CharacterClass* characterClass = new CharacterClass(0); - - characterClass->m_matches.swap(m_matches); - characterClass->m_ranges.swap(m_ranges); - characterClass->m_matchesUnicode.swap(m_matchesUnicode); - characterClass->m_rangesUnicode.swap(m_rangesUnicode); - - return characterClass; - } - -private: - void addSorted(Vector& matches, UChar ch) - { - unsigned pos = 0; - unsigned range = matches.size(); - - // binary chop, find position to insert char. - while (range) { - unsigned index = range >> 1; - - int val = matches[pos+index] - ch; - if (!val) - return; - else if (val > 0) - range = index; - else { - pos += (index+1); - range -= (index+1); - } - } - - if (pos == matches.size()) - matches.append(ch); - else - matches.insert(pos, ch); - } - - void addSortedRange(Vector& ranges, UChar lo, UChar hi) - { - unsigned end = ranges.size(); - - // Simple linear scan - I doubt there are that many ranges anyway... - // feel free to fix this with something faster (eg binary chop). - for (unsigned i = 0; i < end; ++i) { - // does the new range fall before the current position in the array - if (hi < ranges[i].begin) { - // optional optimization: concatenate appending ranges? - may not be worthwhile. - if (hi == (ranges[i].begin - 1)) { - ranges[i].begin = lo; - return; - } - ranges.insert(i, CharacterRange(lo, hi)); - return; - } - // Okay, since we didn't hit the last case, the end of the new range is definitely at or after the begining - // If the new range start at or before the end of the last range, then the overlap (if it starts one after the - // end of the last range they concatenate, which is just as good. - if (lo <= (ranges[i].end + 1)) { - // found an intersect! we'll replace this entry in the array. - ranges[i].begin = std::min(ranges[i].begin, lo); - ranges[i].end = std::max(ranges[i].end, hi); - - // now check if the new range can subsume any subsequent ranges. - unsigned next = i+1; - // each iteration of the loop we will either remove something from the list, or break the loop. - while (next < ranges.size()) { - if (ranges[next].begin <= (ranges[i].end + 1)) { - // the next entry now overlaps / concatenates this one. - ranges[i].end = std::max(ranges[i].end, ranges[next].end); - ranges.remove(next); - } else - break; - } - - return; - } - } - - // CharacterRange comes after all existing ranges. - ranges.append(CharacterRange(lo, hi)); - } - - bool m_isCaseInsensitive; - - Vector m_matches; - Vector m_ranges; - Vector m_matchesUnicode; - Vector m_rangesUnicode; -}; - -class YarrPatternConstructor { -public: - YarrPatternConstructor(YarrPattern& pattern) - : m_pattern(pattern) - , m_characterClassConstructor(pattern.m_ignoreCase) - , m_invertParentheticalAssertion(false) - { - m_pattern.m_body = new PatternDisjunction(); - m_alternative = m_pattern.m_body->addNewAlternative(); - m_pattern.m_disjunctions.append(m_pattern.m_body); - } - - ~YarrPatternConstructor() - { - } - - void reset() - { - m_pattern.reset(); - m_characterClassConstructor.reset(); - - m_pattern.m_body = new PatternDisjunction(); - m_alternative = m_pattern.m_body->addNewAlternative(); - m_pattern.m_disjunctions.append(m_pattern.m_body); - } - - void assertionBOL() - { - if (!m_alternative->m_terms.size() & !m_invertParentheticalAssertion) { - m_alternative->m_startsWithBOL = true; - m_alternative->m_containsBOL = true; - m_pattern.m_containsBOL = true; - } - m_alternative->m_terms.append(PatternTerm::BOL()); - } - void assertionEOL() - { - m_alternative->m_terms.append(PatternTerm::EOL()); - } - void assertionWordBoundary(bool invert) - { - m_alternative->m_terms.append(PatternTerm::WordBoundary(invert)); - } - - void atomPatternCharacter(UChar ch) - { - // We handle case-insensitive checking of unicode characters which do have both - // cases by handling them as if they were defined using a CharacterClass. - if (!m_pattern.m_ignoreCase || isASCII(ch)) { - m_alternative->m_terms.append(PatternTerm(ch)); - return; - } - - UCS2CanonicalizationRange* info = rangeInfoFor(ch); - if (info->type == CanonicalizeUnique) { - m_alternative->m_terms.append(PatternTerm(ch)); - return; - } - - m_characterClassConstructor.putUnicodeIgnoreCase(ch, info); - CharacterClass* newCharacterClass = m_characterClassConstructor.charClass(); - m_pattern.m_userCharacterClasses.append(newCharacterClass); - m_alternative->m_terms.append(PatternTerm(newCharacterClass, false)); - } - - void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert) - { - switch (classID) { - case DigitClassID: - m_alternative->m_terms.append(PatternTerm(m_pattern.digitsCharacterClass(), invert)); - break; - case SpaceClassID: - m_alternative->m_terms.append(PatternTerm(m_pattern.spacesCharacterClass(), invert)); - break; - case WordClassID: - m_alternative->m_terms.append(PatternTerm(m_pattern.wordcharCharacterClass(), invert)); - break; - case NewlineClassID: - m_alternative->m_terms.append(PatternTerm(m_pattern.newlineCharacterClass(), invert)); - break; - } - } - - void atomCharacterClassBegin(bool invert = false) - { - m_invertCharacterClass = invert; - } - - void atomCharacterClassAtom(UChar ch) - { - m_characterClassConstructor.putChar(ch); - } - - void atomCharacterClassRange(UChar begin, UChar end) - { - m_characterClassConstructor.putRange(begin, end); - } - - void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert) - { - ASSERT(classID != NewlineClassID); - - switch (classID) { - case DigitClassID: - m_characterClassConstructor.append(invert ? m_pattern.nondigitsCharacterClass() : m_pattern.digitsCharacterClass()); - break; - - case SpaceClassID: - m_characterClassConstructor.append(invert ? m_pattern.nonspacesCharacterClass() : m_pattern.spacesCharacterClass()); - break; - - case WordClassID: - m_characterClassConstructor.append(invert ? m_pattern.nonwordcharCharacterClass() : m_pattern.wordcharCharacterClass()); - break; - - default: - ASSERT_NOT_REACHED(); - } - } - - void atomCharacterClassEnd() - { - CharacterClass* newCharacterClass = m_characterClassConstructor.charClass(); - m_pattern.m_userCharacterClasses.append(newCharacterClass); - m_alternative->m_terms.append(PatternTerm(newCharacterClass, m_invertCharacterClass)); - } - - void atomParenthesesSubpatternBegin(bool capture = true) - { - unsigned subpatternId = m_pattern.m_numSubpatterns + 1; - if (capture) - m_pattern.m_numSubpatterns++; - - PatternDisjunction* parenthesesDisjunction = new PatternDisjunction(m_alternative); - m_pattern.m_disjunctions.append(parenthesesDisjunction); - m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture, false)); - m_alternative = parenthesesDisjunction->addNewAlternative(); - } - - void atomParentheticalAssertionBegin(bool invert = false) - { - PatternDisjunction* parenthesesDisjunction = new PatternDisjunction(m_alternative); - m_pattern.m_disjunctions.append(parenthesesDisjunction); - m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParentheticalAssertion, m_pattern.m_numSubpatterns + 1, parenthesesDisjunction, false, invert)); - m_alternative = parenthesesDisjunction->addNewAlternative(); - m_invertParentheticalAssertion = invert; - } - - void atomParenthesesEnd() - { - ASSERT(m_alternative->m_parent); - ASSERT(m_alternative->m_parent->m_parent); - - PatternDisjunction* parenthesesDisjunction = m_alternative->m_parent; - m_alternative = m_alternative->m_parent->m_parent; - - PatternTerm& lastTerm = m_alternative->lastTerm(); - - unsigned numParenAlternatives = parenthesesDisjunction->m_alternatives.size(); - unsigned numBOLAnchoredAlts = 0; - - for (unsigned i = 0; i < numParenAlternatives; i++) { - // Bubble up BOL flags - if (parenthesesDisjunction->m_alternatives[i]->m_startsWithBOL) - numBOLAnchoredAlts++; - } - - if (numBOLAnchoredAlts) { - m_alternative->m_containsBOL = true; - // If all the alternatives in parens start with BOL, then so does this one - if (numBOLAnchoredAlts == numParenAlternatives) - m_alternative->m_startsWithBOL = true; - } - - lastTerm.parentheses.lastSubpatternId = m_pattern.m_numSubpatterns; - m_invertParentheticalAssertion = false; - } - - void atomBackReference(unsigned subpatternId) - { - ASSERT(subpatternId); - m_pattern.m_containsBackreferences = true; - m_pattern.m_maxBackReference = std::max(m_pattern.m_maxBackReference, subpatternId); - - if (subpatternId > m_pattern.m_numSubpatterns) { - m_alternative->m_terms.append(PatternTerm::ForwardReference()); - return; - } - - PatternAlternative* currentAlternative = m_alternative; - ASSERT(currentAlternative); - - // Note to self: if we waited until the AST was baked, we could also remove forwards refs - while ((currentAlternative = currentAlternative->m_parent->m_parent)) { - PatternTerm& term = currentAlternative->lastTerm(); - ASSERT((term.type == PatternTerm::TypeParenthesesSubpattern) || (term.type == PatternTerm::TypeParentheticalAssertion)); - - if ((term.type == PatternTerm::TypeParenthesesSubpattern) && term.capture() && (subpatternId == term.parentheses.subpatternId)) { - m_alternative->m_terms.append(PatternTerm::ForwardReference()); - return; - } - } - - m_alternative->m_terms.append(PatternTerm(subpatternId)); - } - - // deep copy the argument disjunction. If filterStartsWithBOL is true, - // skip alternatives with m_startsWithBOL set true. - PatternDisjunction* copyDisjunction(PatternDisjunction* disjunction, bool filterStartsWithBOL = false) - { - PatternDisjunction* newDisjunction = 0; - for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { - PatternAlternative* alternative = disjunction->m_alternatives[alt]; - if (!filterStartsWithBOL || !alternative->m_startsWithBOL) { - if (!newDisjunction) { - newDisjunction = new PatternDisjunction(); - newDisjunction->m_parent = disjunction->m_parent; - } - PatternAlternative* newAlternative = newDisjunction->addNewAlternative(); - for (unsigned i = 0; i < alternative->m_terms.size(); ++i) - newAlternative->m_terms.append(copyTerm(alternative->m_terms[i], filterStartsWithBOL)); - } - } - - if (newDisjunction) - m_pattern.m_disjunctions.append(newDisjunction); - return newDisjunction; - } - - PatternTerm copyTerm(PatternTerm& term, bool filterStartsWithBOL = false) - { - if ((term.type != PatternTerm::TypeParenthesesSubpattern) && (term.type != PatternTerm::TypeParentheticalAssertion)) - return PatternTerm(term); - - PatternTerm termCopy = term; - termCopy.parentheses.disjunction = copyDisjunction(termCopy.parentheses.disjunction, filterStartsWithBOL); - return termCopy; - } - - void quantifyAtom(unsigned min, unsigned max, bool greedy) - { - ASSERT(min <= max); - ASSERT(m_alternative->m_terms.size()); - - if (!max) { - m_alternative->removeLastTerm(); - return; - } - - PatternTerm& term = m_alternative->lastTerm(); - ASSERT(term.type > PatternTerm::TypeAssertionWordBoundary); - ASSERT((term.quantityCount == 1) && (term.quantityType == QuantifierFixedCount)); - - if (term.type == PatternTerm::TypeParentheticalAssertion) { - // If an assertion is quantified with a minimum count of zero, it can simply be removed. - // This arises from the RepeatMatcher behaviour in the spec. Matching an assertion never - // results in any input being consumed, however the continuation passed to the assertion - // (called in steps, 8c and 9 of the RepeatMatcher definition, ES5.1 15.10.2.5) will - // reject all zero length matches (see step 2.1). A match from the continuation of the - // expression will still be accepted regardless (via steps 8a and 11) - the upshot of all - // this is that matches from the assertion are not required, and won't be accepted anyway, - // so no need to ever run it. - if (!min) - m_alternative->removeLastTerm(); - // We never need to run an assertion more than once. Subsequent interations will be run - // with the same start index (since assertions are non-capturing) and the same captures - // (per step 4 of RepeatMatcher in ES5.1 15.10.2.5), and as such will always produce the - // same result and captures. If the first match succeeds then the subsequent (min - 1) - // matches will too. Any additional optional matches will fail (on the same basis as the - // minimum zero quantified assertions, above), but this will still result in a match. - return; - } - - if (min == 0) - term.quantify(max, greedy ? QuantifierGreedy : QuantifierNonGreedy); - else if (min == max) - term.quantify(min, QuantifierFixedCount); - else { - term.quantify(min, QuantifierFixedCount); - m_alternative->m_terms.append(copyTerm(term)); - // NOTE: this term is interesting from an analysis perspective, in that it can be ignored..... - m_alternative->lastTerm().quantify((max == quantifyInfinite) ? max : max - min, greedy ? QuantifierGreedy : QuantifierNonGreedy); - if (m_alternative->lastTerm().type == PatternTerm::TypeParenthesesSubpattern) - m_alternative->lastTerm().parentheses.isCopy = true; - } - } - - void disjunction() - { - m_alternative = m_alternative->m_parent->addNewAlternative(); - } - - unsigned setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition) - { - alternative->m_hasFixedSize = true; - Checked currentInputPosition = initialInputPosition; - - for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { - PatternTerm& term = alternative->m_terms[i]; - - switch (term.type) { - case PatternTerm::TypeAssertionBOL: - case PatternTerm::TypeAssertionEOL: - case PatternTerm::TypeAssertionWordBoundary: - term.inputPosition = currentInputPosition.unsafeGet(); - break; - - case PatternTerm::TypeBackReference: - term.inputPosition = currentInputPosition.unsafeGet(); - term.frameLocation = currentCallFrameSize; - currentCallFrameSize += YarrStackSpaceForBackTrackInfoBackReference; - alternative->m_hasFixedSize = false; - break; - - case PatternTerm::TypeForwardReference: - break; - - case PatternTerm::TypePatternCharacter: - term.inputPosition = currentInputPosition.unsafeGet(); - if (term.quantityType != QuantifierFixedCount) { - term.frameLocation = currentCallFrameSize; - currentCallFrameSize += YarrStackSpaceForBackTrackInfoPatternCharacter; - alternative->m_hasFixedSize = false; - } else - currentInputPosition += term.quantityCount; - break; - - case PatternTerm::TypeCharacterClass: - term.inputPosition = currentInputPosition.unsafeGet(); - if (term.quantityType != QuantifierFixedCount) { - term.frameLocation = currentCallFrameSize; - currentCallFrameSize += YarrStackSpaceForBackTrackInfoCharacterClass; - alternative->m_hasFixedSize = false; - } else - currentInputPosition += term.quantityCount; - break; - - case PatternTerm::TypeParenthesesSubpattern: - // Note: for fixed once parentheses we will ensure at least the minimum is available; others are on their own. - term.frameLocation = currentCallFrameSize; - if (term.quantityCount == 1 && !term.parentheses.isCopy) { - if (term.quantityType != QuantifierFixedCount) - currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesOnce; - currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet()); - // If quantity is fixed, then pre-check its minimum size. - if (term.quantityType == QuantifierFixedCount) - currentInputPosition += term.parentheses.disjunction->m_minimumSize; - term.inputPosition = currentInputPosition.unsafeGet(); - } else if (term.parentheses.isTerminal) { - currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesTerminal; - currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet()); - term.inputPosition = currentInputPosition.unsafeGet(); - } else { - term.inputPosition = currentInputPosition.unsafeGet(); - setupDisjunctionOffsets(term.parentheses.disjunction, 0, currentInputPosition.unsafeGet()); - currentCallFrameSize += YarrStackSpaceForBackTrackInfoParentheses; - } - // Fixed count of 1 could be accepted, if they have a fixed size *AND* if all alternatives are of the same length. - alternative->m_hasFixedSize = false; - break; - - case PatternTerm::TypeParentheticalAssertion: - term.inputPosition = currentInputPosition.unsafeGet(); - term.frameLocation = currentCallFrameSize; - currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition.unsafeGet()); - break; - - case PatternTerm::TypeDotStarEnclosure: - alternative->m_hasFixedSize = false; - term.inputPosition = initialInputPosition; - break; - } - } - - alternative->m_minimumSize = (currentInputPosition - initialInputPosition).unsafeGet(); - return currentCallFrameSize; - } - - unsigned setupDisjunctionOffsets(PatternDisjunction* disjunction, unsigned initialCallFrameSize, unsigned initialInputPosition) - { - if ((disjunction != m_pattern.m_body) && (disjunction->m_alternatives.size() > 1)) - initialCallFrameSize += YarrStackSpaceForBackTrackInfoAlternative; - - unsigned minimumInputSize = UINT_MAX; - unsigned maximumCallFrameSize = 0; - bool hasFixedSize = true; - - for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { - PatternAlternative* alternative = disjunction->m_alternatives[alt]; - unsigned currentAlternativeCallFrameSize = setupAlternativeOffsets(alternative, initialCallFrameSize, initialInputPosition); - minimumInputSize = std::min(minimumInputSize, alternative->m_minimumSize); - maximumCallFrameSize = std::max(maximumCallFrameSize, currentAlternativeCallFrameSize); - hasFixedSize &= alternative->m_hasFixedSize; - } - - ASSERT(minimumInputSize != UINT_MAX); - ASSERT(maximumCallFrameSize >= initialCallFrameSize); - - disjunction->m_hasFixedSize = hasFixedSize; - disjunction->m_minimumSize = minimumInputSize; - disjunction->m_callFrameSize = maximumCallFrameSize; - return maximumCallFrameSize; - } - - void setupOffsets() - { - setupDisjunctionOffsets(m_pattern.m_body, 0, 0); - } - - // This optimization identifies sets of parentheses that we will never need to backtrack. - // In these cases we do not need to store state from prior iterations. - // We can presently avoid backtracking for: - // * where the parens are at the end of the regular expression (last term in any of the - // alternatives of the main body disjunction). - // * where the parens are non-capturing, and quantified unbounded greedy (*). - // * where the parens do not contain any capturing subpatterns. - void checkForTerminalParentheses() - { - // This check is much too crude; should be just checking whether the candidate - // node contains nested capturing subpatterns, not the whole expression! - if (m_pattern.m_numSubpatterns) - return; - - Vector& alternatives = m_pattern.m_body->m_alternatives; - for (size_t i = 0; i < alternatives.size(); ++i) { - Vector& terms = alternatives[i]->m_terms; - if (terms.size()) { - PatternTerm& term = terms.last(); - if (term.type == PatternTerm::TypeParenthesesSubpattern - && term.quantityType == QuantifierGreedy - && term.quantityCount == quantifyInfinite - && !term.capture()) - term.parentheses.isTerminal = true; - } - } - } - - void optimizeBOL() - { - // Look for expressions containing beginning of line (^) anchoring and unroll them. - // e.g. /^a|^b|c/ becomes /^a|^b|c/ which is executed once followed by /c/ which loops - // This code relies on the parsing code tagging alternatives with m_containsBOL and - // m_startsWithBOL and rolling those up to containing alternatives. - // At this point, this is only valid for non-multiline expressions. - PatternDisjunction* disjunction = m_pattern.m_body; - - if (!m_pattern.m_containsBOL || m_pattern.m_multiline) - return; - - PatternDisjunction* loopDisjunction = copyDisjunction(disjunction, true); - - // Set alternatives in disjunction to "onceThrough" - for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) - disjunction->m_alternatives[alt]->setOnceThrough(); - - if (loopDisjunction) { - // Move alternatives from loopDisjunction to disjunction - for (unsigned alt = 0; alt < loopDisjunction->m_alternatives.size(); ++alt) - disjunction->m_alternatives.append(loopDisjunction->m_alternatives[alt]); - - loopDisjunction->m_alternatives.clear(); - } - } - - bool containsCapturingTerms(PatternAlternative* alternative, size_t firstTermIndex, size_t lastTermIndex) - { - Vector& terms = alternative->m_terms; - - for (size_t termIndex = firstTermIndex; termIndex <= lastTermIndex; ++termIndex) { - PatternTerm& term = terms[termIndex]; - - if (term.m_capture) - return true; - - if (term.type == PatternTerm::TypeParenthesesSubpattern) { - PatternDisjunction* nestedDisjunction = term.parentheses.disjunction; - for (unsigned alt = 0; alt < nestedDisjunction->m_alternatives.size(); ++alt) { - if (containsCapturingTerms(nestedDisjunction->m_alternatives[alt], 0, nestedDisjunction->m_alternatives[alt]->m_terms.size() - 1)) - return true; - } - } - } - - return false; - } - - // This optimization identifies alternatives in the form of - // [^].*[?].*[$] for expressions that don't have any - // capturing terms. The alternative is changed to - // followed by processing of the dot stars to find and adjust the - // beginning and the end of the match. - void optimizeDotStarWrappedExpressions() - { - Vector& alternatives = m_pattern.m_body->m_alternatives; - if (alternatives.size() != 1) - return; - - PatternAlternative* alternative = alternatives[0]; - Vector& terms = alternative->m_terms; - if (terms.size() >= 3) { - bool startsWithBOL = false; - bool endsWithEOL = false; - size_t termIndex, firstExpressionTerm, lastExpressionTerm; - - termIndex = 0; - if (terms[termIndex].type == PatternTerm::TypeAssertionBOL) { - startsWithBOL = true; - ++termIndex; - } - - PatternTerm& firstNonAnchorTerm = terms[termIndex]; - if ((firstNonAnchorTerm.type != PatternTerm::TypeCharacterClass) || (firstNonAnchorTerm.characterClass != m_pattern.newlineCharacterClass()) || !((firstNonAnchorTerm.quantityType == QuantifierGreedy) || (firstNonAnchorTerm.quantityType == QuantifierNonGreedy))) - return; - - firstExpressionTerm = termIndex + 1; - - termIndex = terms.size() - 1; - if (terms[termIndex].type == PatternTerm::TypeAssertionEOL) { - endsWithEOL = true; - --termIndex; - } - - PatternTerm& lastNonAnchorTerm = terms[termIndex]; - if ((lastNonAnchorTerm.type != PatternTerm::TypeCharacterClass) || (lastNonAnchorTerm.characterClass != m_pattern.newlineCharacterClass()) || (lastNonAnchorTerm.quantityType != QuantifierGreedy)) - return; - - lastExpressionTerm = termIndex - 1; - - if (firstExpressionTerm > lastExpressionTerm) - return; - - if (!containsCapturingTerms(alternative, firstExpressionTerm, lastExpressionTerm)) { - for (termIndex = terms.size() - 1; termIndex > lastExpressionTerm; --termIndex) - terms.remove(termIndex); - - for (termIndex = firstExpressionTerm; termIndex > 0; --termIndex) - terms.remove(termIndex - 1); - - terms.append(PatternTerm(startsWithBOL, endsWithEOL)); - - m_pattern.m_containsBOL = false; - } - } - } - -private: - YarrPattern& m_pattern; - PatternAlternative* m_alternative; - CharacterClassConstructor m_characterClassConstructor; - bool m_invertCharacterClass; - bool m_invertParentheticalAssertion; -}; - -const char* YarrPattern::compile(const String& patternString) -{ - YarrPatternConstructor constructor(*this); - - if (const char* error = parse(constructor, patternString)) - return error; - - // If the pattern contains illegal backreferences reset & reparse. - // Quoting Netscape's "What's new in JavaScript 1.2", - // "Note: if the number of left parentheses is less than the number specified - // in \#, the \# is taken as an octal escape as described in the next row." - if (containsIllegalBackReference()) { - unsigned numSubpatterns = m_numSubpatterns; - - constructor.reset(); -#if !ASSERT_DISABLED - const char* error = -#endif - parse(constructor, patternString, numSubpatterns); - - ASSERT(!error); - ASSERT(numSubpatterns == m_numSubpatterns); - } - - constructor.checkForTerminalParentheses(); - constructor.optimizeDotStarWrappedExpressions(); - constructor.optimizeBOL(); - - constructor.setupOffsets(); - - return 0; -} - -YarrPattern::YarrPattern(const String& pattern, bool ignoreCase, bool multiline, const char** error) - : m_ignoreCase(ignoreCase) - , m_multiline(multiline) - , m_containsBackreferences(false) - , m_containsBOL(false) - , m_numSubpatterns(0) - , m_maxBackReference(0) - , newlineCached(0) - , digitsCached(0) - , spacesCached(0) - , wordcharCached(0) - , nondigitsCached(0) - , nonspacesCached(0) - , nonwordcharCached(0) -{ - *error = compile(pattern); -} - -} } diff --git a/masm/yarr/YarrPattern.h b/masm/yarr/YarrPattern.h deleted file mode 100644 index 14e89b8e09..0000000000 --- a/masm/yarr/YarrPattern.h +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef YarrPattern_h -#define YarrPattern_h - -#include -#include -#include -#include -#include - -namespace JSC { namespace Yarr { - -struct PatternDisjunction; - -struct CharacterRange { - UChar begin; - UChar end; - - CharacterRange(UChar begin, UChar end) - : begin(begin) - , end(end) - { - } -}; - -struct CharacterClassTable : RefCounted { - const char* m_table; - bool m_inverted; - static PassRefPtr create(const char* table, bool inverted) - { - return adoptRef(new CharacterClassTable(table, inverted)); - } - -private: - CharacterClassTable(const char* table, bool inverted) - : m_table(table) - , m_inverted(inverted) - { - } -}; - -struct CharacterClass { - WTF_MAKE_FAST_ALLOCATED; -public: - // All CharacterClass instances have to have the full set of matches and ranges, - // they may have an optional table for faster lookups (which must match the - // specified matches and ranges) - CharacterClass(PassRefPtr table) - : m_table(table) - { - } - Vector m_matches; - Vector m_ranges; - Vector m_matchesUnicode; - Vector m_rangesUnicode; - RefPtr m_table; -}; - -enum QuantifierType { - QuantifierFixedCount, - QuantifierGreedy, - QuantifierNonGreedy, -}; - -struct PatternTerm { - enum Type { - TypeAssertionBOL, - TypeAssertionEOL, - TypeAssertionWordBoundary, - TypePatternCharacter, - TypeCharacterClass, - TypeBackReference, - TypeForwardReference, - TypeParenthesesSubpattern, - TypeParentheticalAssertion, - TypeDotStarEnclosure, - } type; - bool m_capture :1; - bool m_invert :1; - union { - UChar patternCharacter; - CharacterClass* characterClass; - unsigned backReferenceSubpatternId; - struct { - PatternDisjunction* disjunction; - unsigned subpatternId; - unsigned lastSubpatternId; - bool isCopy; - bool isTerminal; - } parentheses; - struct { - bool bolAnchor : 1; - bool eolAnchor : 1; - } anchors; - }; - QuantifierType quantityType; - Checked quantityCount; - int inputPosition; - unsigned frameLocation; - - PatternTerm(UChar ch) - : type(PatternTerm::TypePatternCharacter) - , m_capture(false) - , m_invert(false) - { - patternCharacter = ch; - quantityType = QuantifierFixedCount; - quantityCount = 1; - } - - PatternTerm(CharacterClass* charClass, bool invert) - : type(PatternTerm::TypeCharacterClass) - , m_capture(false) - , m_invert(invert) - { - characterClass = charClass; - quantityType = QuantifierFixedCount; - quantityCount = 1; - } - - PatternTerm(Type type, unsigned subpatternId, PatternDisjunction* disjunction, bool capture = false, bool invert = false) - : type(type) - , m_capture(capture) - , m_invert(invert) - { - parentheses.disjunction = disjunction; - parentheses.subpatternId = subpatternId; - parentheses.isCopy = false; - parentheses.isTerminal = false; - quantityType = QuantifierFixedCount; - quantityCount = 1; - } - - PatternTerm(Type type, bool invert = false) - : type(type) - , m_capture(false) - , m_invert(invert) - { - quantityType = QuantifierFixedCount; - quantityCount = 1; - } - - PatternTerm(unsigned spatternId) - : type(TypeBackReference) - , m_capture(false) - , m_invert(false) - { - backReferenceSubpatternId = spatternId; - quantityType = QuantifierFixedCount; - quantityCount = 1; - } - - PatternTerm(bool bolAnchor, bool eolAnchor) - : type(TypeDotStarEnclosure) - , m_capture(false) - , m_invert(false) - { - anchors.bolAnchor = bolAnchor; - anchors.eolAnchor = eolAnchor; - quantityType = QuantifierFixedCount; - quantityCount = 1; - } - - static PatternTerm ForwardReference() - { - return PatternTerm(TypeForwardReference); - } - - static PatternTerm BOL() - { - return PatternTerm(TypeAssertionBOL); - } - - static PatternTerm EOL() - { - return PatternTerm(TypeAssertionEOL); - } - - static PatternTerm WordBoundary(bool invert) - { - return PatternTerm(TypeAssertionWordBoundary, invert); - } - - bool invert() - { - return m_invert; - } - - bool capture() - { - return m_capture; - } - - void quantify(unsigned count, QuantifierType type) - { - quantityCount = count; - quantityType = type; - } -}; - -struct PatternAlternative { - WTF_MAKE_FAST_ALLOCATED; -public: - PatternAlternative(PatternDisjunction* disjunction) - : m_parent(disjunction) - , m_onceThrough(false) - , m_hasFixedSize(false) - , m_startsWithBOL(false) - , m_containsBOL(false) - { - } - - PatternTerm& lastTerm() - { - ASSERT(m_terms.size()); - return m_terms[m_terms.size() - 1]; - } - - void removeLastTerm() - { - ASSERT(m_terms.size()); - m_terms.shrink(m_terms.size() - 1); - } - - void setOnceThrough() - { - m_onceThrough = true; - } - - bool onceThrough() - { - return m_onceThrough; - } - - Vector m_terms; - PatternDisjunction* m_parent; - unsigned m_minimumSize; - bool m_onceThrough : 1; - bool m_hasFixedSize : 1; - bool m_startsWithBOL : 1; - bool m_containsBOL : 1; -}; - -struct PatternDisjunction { - WTF_MAKE_FAST_ALLOCATED; -public: - PatternDisjunction(PatternAlternative* parent = 0) - : m_parent(parent) - , m_hasFixedSize(false) - { - } - - ~PatternDisjunction() - { - deleteAllValues(m_alternatives); - } - - PatternAlternative* addNewAlternative() - { - PatternAlternative* alternative = new PatternAlternative(this); - m_alternatives.append(alternative); - return alternative; - } - - Vector m_alternatives; - PatternAlternative* m_parent; - unsigned m_minimumSize; - unsigned m_callFrameSize; - bool m_hasFixedSize; -}; - -// You probably don't want to be calling these functions directly -// (please to be calling newlineCharacterClass() et al on your -// friendly neighborhood YarrPattern instance to get nicely -// cached copies). -CharacterClass* newlineCreate(); -CharacterClass* digitsCreate(); -CharacterClass* spacesCreate(); -CharacterClass* wordcharCreate(); -CharacterClass* nondigitsCreate(); -CharacterClass* nonspacesCreate(); -CharacterClass* nonwordcharCreate(); - -struct TermChain { - TermChain(PatternTerm term) - : term(term) - {} - - PatternTerm term; - Vector hotTerms; -}; - -struct YarrPattern { - JS_EXPORT_PRIVATE YarrPattern(const String& pattern, bool ignoreCase, bool multiline, const char** error); - - ~YarrPattern() - { - deleteAllValues(m_disjunctions); - deleteAllValues(m_userCharacterClasses); - } - - void reset() - { - m_numSubpatterns = 0; - m_maxBackReference = 0; - - m_containsBackreferences = false; - m_containsBOL = false; - - newlineCached = 0; - digitsCached = 0; - spacesCached = 0; - wordcharCached = 0; - nondigitsCached = 0; - nonspacesCached = 0; - nonwordcharCached = 0; - - deleteAllValues(m_disjunctions); - m_disjunctions.clear(); - deleteAllValues(m_userCharacterClasses); - m_userCharacterClasses.clear(); - } - - bool containsIllegalBackReference() - { - return m_maxBackReference > m_numSubpatterns; - } - - CharacterClass* newlineCharacterClass() - { - if (!newlineCached) - m_userCharacterClasses.append(newlineCached = newlineCreate()); - return newlineCached; - } - CharacterClass* digitsCharacterClass() - { - if (!digitsCached) - m_userCharacterClasses.append(digitsCached = digitsCreate()); - return digitsCached; - } - CharacterClass* spacesCharacterClass() - { - if (!spacesCached) - m_userCharacterClasses.append(spacesCached = spacesCreate()); - return spacesCached; - } - CharacterClass* wordcharCharacterClass() - { - if (!wordcharCached) - m_userCharacterClasses.append(wordcharCached = wordcharCreate()); - return wordcharCached; - } - CharacterClass* nondigitsCharacterClass() - { - if (!nondigitsCached) - m_userCharacterClasses.append(nondigitsCached = nondigitsCreate()); - return nondigitsCached; - } - CharacterClass* nonspacesCharacterClass() - { - if (!nonspacesCached) - m_userCharacterClasses.append(nonspacesCached = nonspacesCreate()); - return nonspacesCached; - } - CharacterClass* nonwordcharCharacterClass() - { - if (!nonwordcharCached) - m_userCharacterClasses.append(nonwordcharCached = nonwordcharCreate()); - return nonwordcharCached; - } - - bool m_ignoreCase : 1; - bool m_multiline : 1; - bool m_containsBackreferences : 1; - bool m_containsBOL : 1; - unsigned m_numSubpatterns; - unsigned m_maxBackReference; - PatternDisjunction* m_body; - Vector m_disjunctions; - Vector m_userCharacterClasses; - -private: - const char* compile(const String& patternString); - - CharacterClass* newlineCached; - CharacterClass* digitsCached; - CharacterClass* spacesCached; - CharacterClass* wordcharCached; - CharacterClass* nondigitsCached; - CharacterClass* nonspacesCached; - CharacterClass* nonwordcharCached; -}; - -} } // namespace JSC::Yarr - -#endif // YarrPattern_h diff --git a/masm/yarr/YarrSyntaxChecker.cpp b/masm/yarr/YarrSyntaxChecker.cpp deleted file mode 100644 index aa98c4a354..0000000000 --- a/masm/yarr/YarrSyntaxChecker.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2011 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "YarrSyntaxChecker.h" - -#include "YarrParser.h" - -namespace JSC { namespace Yarr { - -class SyntaxChecker { -public: - void assertionBOL() {} - void assertionEOL() {} - void assertionWordBoundary(bool) {} - void atomPatternCharacter(UChar) {} - void atomBuiltInCharacterClass(BuiltInCharacterClassID, bool) {} - void atomCharacterClassBegin(bool = false) {} - void atomCharacterClassAtom(UChar) {} - void atomCharacterClassRange(UChar, UChar) {} - void atomCharacterClassBuiltIn(BuiltInCharacterClassID, bool) {} - void atomCharacterClassEnd() {} - void atomParenthesesSubpatternBegin(bool = true) {} - void atomParentheticalAssertionBegin(bool = false) {} - void atomParenthesesEnd() {} - void atomBackReference(unsigned) {} - void quantifyAtom(unsigned, unsigned, bool) {} - void disjunction() {} -}; - -const char* checkSyntax(const String& pattern) -{ - SyntaxChecker syntaxChecker; - return parse(syntaxChecker, pattern); -} - -}} // JSC::YARR diff --git a/masm/yarr/YarrSyntaxChecker.h b/masm/yarr/YarrSyntaxChecker.h deleted file mode 100644 index 104ced3ab4..0000000000 --- a/masm/yarr/YarrSyntaxChecker.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2011 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef YarrSyntaxChecker_h -#define YarrSyntaxChecker_h - -#include - -namespace JSC { namespace Yarr { - -const char* checkSyntax(const String& pattern); - -}} // JSC::YARR - -#endif // YarrSyntaxChecker_h - diff --git a/masm/yarr/yarr.pri b/masm/yarr/yarr.pri deleted file mode 100644 index 7e9b4d3f3b..0000000000 --- a/masm/yarr/yarr.pri +++ /dev/null @@ -1,12 +0,0 @@ -# ------------------------------------------------------------------- -# Project file for YARR -# -# See 'Tools/qmake/README' for an overview of the build system -# ------------------------------------------------------------------- - -SOURCES += \ - $$PWD/YarrInterpreter.cpp \ - $$PWD/YarrPattern.cpp \ - $$PWD/YarrSyntaxChecker.cpp \ - $$PWD/YarrCanonicalizeUCS2.cpp - diff --git a/v4.pro b/v4.pro index d85e283ef0..d181c025f7 100644 --- a/v4.pro +++ b/v4.pro @@ -126,5 +126,5 @@ QMAKE_EXTRA_TARGETS += checkmothtarget include(moth/moth.pri) -include(masm/masm.pri) +include(3rdparty/masm/masm.pri) include(3rdparty/double-conversion/double-conversion.pri) -- cgit v1.2.3 From 7c8266360b23964c46d246abb01c1f9cd762acf9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 22:03:14 +0100 Subject: Move ArrayCtor and ArrayProto into their own file Change-Id: I304f50704dbad396abc7c6c8ae73d454ebd73e01 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 1 + qv4arrayobject.cpp | 720 +++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4arrayobject.h | 95 +++++++ qv4ecmaobjects.cpp | 678 ------------------------------------------------- qv4ecmaobjects_p.h | 39 --- v4.pro | 2 + 6 files changed, 818 insertions(+), 717 deletions(-) create mode 100644 qv4arrayobject.cpp create mode 100644 qv4arrayobject.h diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 231c6b843e..0e9abb5507 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include diff --git a/qv4arrayobject.cpp b/qv4arrayobject.cpp new file mode 100644 index 0000000000..302122936a --- /dev/null +++ b/qv4arrayobject.cpp @@ -0,0 +1,720 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4arrayobject.h" +#include "qv4array.h" + +using namespace QQmlJS::VM; + +ArrayCtor::ArrayCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value ArrayCtor::call(ExecutionContext *ctx) +{ + ArrayObject *a = ctx->engine->newArrayObject(ctx); + Array &value = a->array; + if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { + bool ok; + uint len = ctx->argument(0).asArrayLength(ctx, &ok); + + if (!ok) { + ctx->throwRangeError(ctx->argument(0)); + return Value::undefinedValue(); + } + + value.setLengthUnchecked(len); + } else { + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { + value.set(i, ctx->argument(i)); + } + } + + return Value::fromObject(a); +} + +void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isArray"), method_isArray, 1); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(ctx, QStringLiteral("join"), method_join, 1); + defineDefaultProperty(ctx, QStringLiteral("pop"), method_pop, 0); + defineDefaultProperty(ctx, QStringLiteral("push"), method_push, 1); + defineDefaultProperty(ctx, QStringLiteral("reverse"), method_reverse, 0); + defineDefaultProperty(ctx, QStringLiteral("shift"), method_shift, 0); + defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); + defineDefaultProperty(ctx, QStringLiteral("sort"), method_sort, 1); + defineDefaultProperty(ctx, QStringLiteral("splice"), method_splice, 2); + defineDefaultProperty(ctx, QStringLiteral("unshift"), method_unshift, 1); + defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("every"), method_every, 1); + defineDefaultProperty(ctx, QStringLiteral("some"), method_some, 1); + defineDefaultProperty(ctx, QStringLiteral("forEach"), method_forEach, 1); + defineDefaultProperty(ctx, QStringLiteral("map"), method_map, 1); + defineDefaultProperty(ctx, QStringLiteral("filter"), method_filter, 1); + defineDefaultProperty(ctx, QStringLiteral("reduce"), method_reduce, 1); + defineDefaultProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 1); +} + +uint ArrayPrototype::getLength(ExecutionContext *ctx, Object *o) +{ + if (o->isArray) + return o->array.length(); + return o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); +} + +Value ArrayPrototype::method_isArray(ExecutionContext *ctx) +{ + Value arg = ctx->argument(0); + bool isArray = arg.asArrayObject(); + return Value::fromBoolean(isArray); +} + +Value ArrayPrototype::method_toString(ExecutionContext *ctx) +{ + return method_join(ctx); +} + +Value ArrayPrototype::method_toLocaleString(ExecutionContext *ctx) +{ + return method_toString(ctx); +} + +Value ArrayPrototype::method_concat(ExecutionContext *ctx) +{ + Array result; + + if (ArrayObject *instance = ctx->thisObject.asArrayObject()) + result = instance->array; + else { + QString v = ctx->thisObject.toString(ctx)->toQString(); + result.set(0, Value::fromString(ctx, v)); + } + + for (uint i = 0; i < ctx->argumentCount; ++i) { + quint32 k = result.length(); + Value arg = ctx->argument(i); + + if (ArrayObject *elt = arg.asArrayObject()) + result.concat(elt->array); + + else + result.set(k, arg); + } + + return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); +} + +Value ArrayPrototype::method_join(ExecutionContext *ctx) +{ + Value arg = ctx->argument(0); + + QString r4; + if (arg.isUndefined()) + r4 = QStringLiteral(","); + else + r4 = arg.toString(ctx)->toQString(); + + Value self = ctx->thisObject; + const Value length = self.property(ctx, ctx->engine->id_length); + const quint32 r2 = Value::toUInt32(length.isUndefined() ? 0 : length.toNumber(ctx)); + + static QSet visitedArrayElements; + + if (! r2 || visitedArrayElements.contains(self.objectValue())) + return Value::fromString(ctx, QString()); + + // avoid infinite recursion + visitedArrayElements.insert(self.objectValue()); + + QString R; + + // ### FIXME + if (ArrayObject *a = self.asArrayObject()) { + for (uint i = 0; i < a->array.length(); ++i) { + if (i) + R += r4; + + Value e = a->__get__(ctx, i); + if (! (e.isUndefined() || e.isNull())) + R += e.toString(ctx)->toQString(); + } + } else { + // + // crazy! + // + Value r6 = self.property(ctx, ctx->engine->identifier(QStringLiteral("0"))); + if (!(r6.isUndefined() || r6.isNull())) + R = r6.toString(ctx)->toQString(); + + for (quint32 k = 1; k < r2; ++k) { + R += r4; + + String *name = Value::fromDouble(k).toString(ctx); + Value r12 = self.property(ctx, name); + + if (! (r12.isUndefined() || r12.isNull())) + R += r12.toString(ctx)->toQString(); + } + } + + visitedArrayElements.remove(self.objectValue()); + return Value::fromString(ctx, R); +} + +Value ArrayPrototype::method_pop(ExecutionContext *ctx) +{ + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + Value v = instance->getValueChecked(ctx, instance->array.back()); + instance->array.pop_back(); + return v; + } + + Value r1 = self.property(ctx, ctx->engine->id_length); + quint32 r2 = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; + if (r2) { + String *r6 = Value::fromDouble(r2 - 1).toString(ctx); + Value r7 = self.property(ctx, r6); + self.objectValue()->__delete__(ctx, r6); + self.objectValue()->__put__(ctx, ctx->engine->id_length, Value::fromDouble(2 - 1)); + return r7; + } + + self.objectValue()->__put__(ctx, ctx->engine->id_length, Value::fromDouble(0)); + return Value::undefinedValue(); +} + +Value ArrayPrototype::method_push(ExecutionContext *ctx) +{ + Value self = ctx->thisObject; + if (ArrayObject *instance = self.asArrayObject()) { + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { + Value val = ctx->argument(i); + instance->array.push_back(val); + } + return Value::fromDouble(instance->array.length()); + } + + Value r1 = self.property(ctx, ctx->engine->id_length); + quint32 n = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; + for (unsigned int index = 0; index < ctx->argumentCount; ++index, ++n) { + Value r3 = ctx->argument(index); + String *name = Value::fromDouble(n).toString(ctx); + self.objectValue()->__put__(ctx, name, r3); + } + Value r = Value::fromDouble(n); + self.objectValue()->__put__(ctx, ctx->engine->id_length, r); + return r; +} + +Value ArrayPrototype::method_reverse(ExecutionContext *ctx) +{ + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) + ctx->throwUnimplemented(QStringLiteral("Array.prototype.reverse")); + + int lo = 0, hi = instance->array.length() - 1; + + // ### + for (; lo < hi; ++lo, --hi) { + Value tmp = instance->__get__(ctx, lo); + instance->array.set(lo, instance->__get__(ctx, hi)); + instance->array.set(hi, tmp); + } + return Value::undefinedValue(); +} + +Value ArrayPrototype::method_shift(ExecutionContext *ctx) +{ + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) + ctx->throwUnimplemented(QStringLiteral("Array.prototype.shift")); + + Value v = instance->getValueChecked(ctx, instance->array.front()); + instance->array.pop_front(); + return v; +} + +Value ArrayPrototype::method_slice(ExecutionContext *ctx) +{ + Object *o = ctx->thisObject.toObject(ctx).objectValue(); + + Array result; + uint len = o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); + double s = ctx->argument(0).toInteger(ctx); + uint start; + if (s < 0) + start = (uint)qMax(len + s, 0.); + else if (s > len) + start = len; + else + start = (uint) s; + uint end = len; + if (!ctx->argument(1).isUndefined()) { + double e = ctx->argument(1).toInteger(ctx); + if (e < 0) + end = (uint)qMax(len + e, 0.); + else if (e > len) + end = len; + else + end = (uint) e; + } + + uint n = 0; + for (uint i = start; i < end; ++i) { + bool exists; + Value v = o->__get__(ctx, i, &exists); + if (exists) + result.set(n, v); + ++n; + } + return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); +} + +Value ArrayPrototype::method_sort(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + Value comparefn = ctx->argument(0); + instance->array.sort(ctx, instance, comparefn, len); + return ctx->thisObject; +} + +Value ArrayPrototype::method_splice(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); + + ArrayObject *newArray = ctx->engine->newArrayObject(ctx); + + double rs = ctx->argument(0).toInteger(ctx); + uint start; + if (rs < 0) + start = (uint) qMax(0., len + rs); + else + start = (uint) qMin(rs, (double)len); + + uint deleteCount = (uint)qMin(qMax(ctx->argument(1).toInteger(ctx), 0.), (double)(len - start)); + + newArray->array.values.resize(deleteCount); + PropertyDescriptor *pd = newArray->array.values.data(); + for (uint i = 0; i < deleteCount; ++i) { + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Enabled; + pd->configurable = PropertyDescriptor::Enabled; + pd->value = instance->__get__(ctx, start + i); + ++pd; + } + newArray->array.setLengthUnchecked(deleteCount); + + uint itemCount = ctx->argumentCount < 2 ? 0 : ctx->argumentCount - 2; + + if (itemCount < deleteCount) { + for (uint k = start; k < len - deleteCount; ++k) { + bool exists; + Value v = instance->__get__(ctx, k + deleteCount, &exists); + if (exists) + instance->array.set(k + itemCount, v); + else + instance->__delete__(ctx, k + itemCount); + } + for (uint k = len; k > len - deleteCount + itemCount; --k) + instance->__delete__(ctx, k - 1); + } else if (itemCount > deleteCount) { + uint k = len - deleteCount; + while (k > start) { + bool exists; + Value v = instance->__get__(ctx, k + deleteCount - 1, &exists); + if (exists) + instance->array.set(k + itemCount - 1, v); + else + instance->__delete__(ctx, k + itemCount - 1); + --k; + } + } + + for (uint i = 0; i < itemCount; ++i) + instance->array.set(start + i, ctx->argument(i + 2)); + + ctx->strictMode = true; + instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - deleteCount + itemCount)); + + return Value::fromObject(newArray); +} + +Value ArrayPrototype::method_unshift(ExecutionContext *ctx) +{ + ArrayObject *instance = ctx->thisObject.asArrayObject(); + if (!instance) + ctx->throwUnimplemented(QStringLiteral("Array.prototype.shift")); + + for (int i = ctx->argumentCount - 1; i >= 0; --i) { + Value v = ctx->argument(i); + instance->array.push_front(v); + } + + uint l = instance->array.length(); + if (l < INT_MAX) + return Value::fromInt32(l); + return Value::fromDouble((double)l); +} + +Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); + if (!len) + return Value::fromInt32(-1); + + Value searchValue; + uint fromIndex = 0; + + if (ctx->argumentCount >= 1) + searchValue = ctx->argument(0); + else + searchValue = Value::undefinedValue(); + + if (ctx->argumentCount >= 2) { + double f = ctx->argument(1).toInteger(ctx); + if (f >= len) + return Value::fromInt32(-1); + if (f < 0) + f = qMax(len + f, 0.); + fromIndex = (uint) f; + } + + if (instance->isString) { + for (uint k = fromIndex; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (exists && __qmljs_strict_equal(v, searchValue)) + return Value::fromDouble(k); + } + return Value::fromInt32(-1); + } + + return instance->array.indexOf(searchValue, fromIndex, len, ctx, instance); +} + +Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); + if (!len) + return Value::fromInt32(-1); + + Value searchValue; + uint fromIndex = len - 1; + + if (ctx->argumentCount >= 1) + searchValue = ctx->argument(0); + else + searchValue = Value::undefinedValue(); + + if (ctx->argumentCount >= 2) { + double f = ctx->argument(1).toInteger(ctx); + if (f >= len) + return Value::fromInt32(-1); + if (f < 0) + f = qMax(len + f, 0.); + fromIndex = (uint) f; + } + + for (uint k = fromIndex; k > 0; --k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (exists && __qmljs_strict_equal(v, searchValue)) + return Value::fromDouble(k); + } + return Value::fromInt32(-1); +} + +Value ArrayPrototype::method_every(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + Value thisArg = ctx->argument(1); + + bool ok = true; + for (uint k = 0; ok && k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = callback->call(ctx, thisArg, args, 3); + ok = __qmljs_to_boolean(r, ctx); + } + return Value::fromBoolean(ok); +} + +Value ArrayPrototype::method_some(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + Value thisArg = ctx->argument(1); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = callback->call(ctx, thisArg, args, 3); + if (__qmljs_to_boolean(r, ctx)) + return Value::fromBoolean(true); + } + return Value::fromBoolean(false); +} + +Value ArrayPrototype::method_forEach(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + Value thisArg = ctx->argument(1); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + callback->call(ctx, thisArg, args, 3); + } + return Value::undefinedValue(); +} + +Value ArrayPrototype::method_map(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + Value thisArg = ctx->argument(1); + + ArrayObject *a = ctx->engine->newArrayObject(ctx); + a->array.setLength(len); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value mapped = callback->call(ctx, thisArg, args, 3); + a->array.set(k, mapped); + } + return Value::fromObject(a); +} + +Value ArrayPrototype::method_filter(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + Value thisArg = ctx->argument(1); + + ArrayObject *a = ctx->engine->newArrayObject(ctx); + + uint to = 0; + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value selected = callback->call(ctx, thisArg, args, 3); + if (__qmljs_to_boolean(selected, ctx)) { + a->array.set(to, v); + ++to; + } + } + return Value::fromObject(a); +} + +Value ArrayPrototype::method_reduce(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + uint k = 0; + Value acc; + if (ctx->argumentCount > 1) { + acc = ctx->argument(1); + } else { + bool kPresent = false; + while (k < len && !kPresent) { + Value v = instance->__get__(ctx, k, &kPresent); + if (kPresent) + acc = v; + ++k; + } + if (!kPresent) + __qmljs_throw_type_error(ctx); + } + + while (k < len) { + bool kPresent; + Value v = instance->__get__(ctx, k, &kPresent); + if (kPresent) { + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k); + args[3] = ctx->thisObject; + acc = callback->call(ctx, Value::undefinedValue(), args, 4); + } + ++k; + } + return acc; +} + +Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + if (len == 0) { + if (ctx->argumentCount == 1) + __qmljs_throw_type_error(ctx); + return ctx->argument(1); + } + + uint k = len; + Value acc; + if (ctx->argumentCount > 1) { + acc = ctx->argument(1); + } else { + bool kPresent = false; + while (k > 0 && !kPresent) { + Value v = instance->__get__(ctx, k - 1, &kPresent); + if (kPresent) + acc = v; + --k; + } + if (!kPresent) + __qmljs_throw_type_error(ctx); + } + + while (k > 0) { + bool kPresent; + Value v = instance->__get__(ctx, k - 1, &kPresent); + if (kPresent) { + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k - 1); + args[3] = ctx->thisObject; + acc = callback->call(ctx, Value::undefinedValue(), args, 4); + } + --k; + } + return acc; +} + diff --git a/qv4arrayobject.h b/qv4arrayobject.h new file mode 100644 index 0000000000..8a15546c1c --- /dev/null +++ b/qv4arrayobject.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ARRAYOBJECT_H +#define QV4ARRAYOBJECT_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include + +namespace QQmlJS { +namespace VM { + + +struct ArrayCtor: FunctionObject +{ + ArrayCtor(ExecutionContext *scope); + + virtual Value call(ExecutionContext *ctx); +}; + +struct ArrayPrototype: ArrayObject +{ + ArrayPrototype(ExecutionContext *context) : ArrayObject(context) {} + + void init(ExecutionContext *ctx, const Value &ctor); + + static uint getLength(ExecutionContext *ctx, Object *o); + + static Value method_isArray(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *ctx); + static Value method_toLocaleString(ExecutionContext *ctx); + static Value method_concat(ExecutionContext *ctx); + static Value method_join(ExecutionContext *ctx); + static Value method_pop(ExecutionContext *ctx); + static Value method_push(ExecutionContext *ctx); + static Value method_reverse(ExecutionContext *ctx); + static Value method_shift(ExecutionContext *ctx); + static Value method_slice(ExecutionContext *ctx); + static Value method_sort(ExecutionContext *ctx); + static Value method_splice(ExecutionContext *ctx); + static Value method_unshift(ExecutionContext *ctx); + static Value method_indexOf(ExecutionContext *ctx); + static Value method_lastIndexOf(ExecutionContext *ctx); + static Value method_every(ExecutionContext *ctx); + static Value method_some(ExecutionContext *ctx); + static Value method_forEach(ExecutionContext *ctx); + static Value method_map(ExecutionContext *ctx); + static Value method_filter(ExecutionContext *ctx); + static Value method_reduce(ExecutionContext *ctx); + static Value method_reduceRight(ExecutionContext *ctx); +}; + + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 206d349b9d..e64852ec81 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -783,681 +783,3 @@ Value BooleanPrototype::method_valueOf(ExecutionContext *ctx) return thisObject->value; } - -// -// Array object -// -ArrayCtor::ArrayCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value ArrayCtor::call(ExecutionContext *ctx) -{ - ArrayObject *a = ctx->engine->newArrayObject(ctx); - Array &value = a->array; - if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { - bool ok; - uint len = ctx->argument(0).asArrayLength(ctx, &ok); - - if (!ok) { - ctx->throwRangeError(ctx->argument(0)); - return Value::undefinedValue(); - } - - value.setLengthUnchecked(len); - } else { - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { - value.set(i, ctx->argument(i)); - } - } - - return Value::fromObject(a); -} - -void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isArray"), method_isArray, 1); - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString, 0); - defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); - defineDefaultProperty(ctx, QStringLiteral("join"), method_join, 1); - defineDefaultProperty(ctx, QStringLiteral("pop"), method_pop, 0); - defineDefaultProperty(ctx, QStringLiteral("push"), method_push, 1); - defineDefaultProperty(ctx, QStringLiteral("reverse"), method_reverse, 0); - defineDefaultProperty(ctx, QStringLiteral("shift"), method_shift, 0); - defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); - defineDefaultProperty(ctx, QStringLiteral("sort"), method_sort, 1); - defineDefaultProperty(ctx, QStringLiteral("splice"), method_splice, 2); - defineDefaultProperty(ctx, QStringLiteral("unshift"), method_unshift, 1); - defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); - defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); - defineDefaultProperty(ctx, QStringLiteral("every"), method_every, 1); - defineDefaultProperty(ctx, QStringLiteral("some"), method_some, 1); - defineDefaultProperty(ctx, QStringLiteral("forEach"), method_forEach, 1); - defineDefaultProperty(ctx, QStringLiteral("map"), method_map, 1); - defineDefaultProperty(ctx, QStringLiteral("filter"), method_filter, 1); - defineDefaultProperty(ctx, QStringLiteral("reduce"), method_reduce, 1); - defineDefaultProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 1); -} - -uint ArrayPrototype::getLength(ExecutionContext *ctx, Object *o) -{ - if (o->isArray) - return o->array.length(); - return o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); -} - -Value ArrayPrototype::method_isArray(ExecutionContext *ctx) -{ - Value arg = ctx->argument(0); - bool isArray = arg.asArrayObject(); - return Value::fromBoolean(isArray); -} - -Value ArrayPrototype::method_toString(ExecutionContext *ctx) -{ - return method_join(ctx); -} - -Value ArrayPrototype::method_toLocaleString(ExecutionContext *ctx) -{ - return method_toString(ctx); -} - -Value ArrayPrototype::method_concat(ExecutionContext *ctx) -{ - Array result; - - if (ArrayObject *instance = ctx->thisObject.asArrayObject()) - result = instance->array; - else { - QString v = ctx->thisObject.toString(ctx)->toQString(); - result.set(0, Value::fromString(ctx, v)); - } - - for (uint i = 0; i < ctx->argumentCount; ++i) { - quint32 k = result.length(); - Value arg = ctx->argument(i); - - if (ArrayObject *elt = arg.asArrayObject()) - result.concat(elt->array); - - else - result.set(k, arg); - } - - return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); -} - -Value ArrayPrototype::method_join(ExecutionContext *ctx) -{ - Value arg = ctx->argument(0); - - QString r4; - if (arg.isUndefined()) - r4 = QStringLiteral(","); - else - r4 = arg.toString(ctx)->toQString(); - - Value self = ctx->thisObject; - const Value length = self.property(ctx, ctx->engine->id_length); - const quint32 r2 = Value::toUInt32(length.isUndefined() ? 0 : length.toNumber(ctx)); - - static QSet visitedArrayElements; - - if (! r2 || visitedArrayElements.contains(self.objectValue())) - return Value::fromString(ctx, QString()); - - // avoid infinite recursion - visitedArrayElements.insert(self.objectValue()); - - QString R; - - // ### FIXME - if (ArrayObject *a = self.asArrayObject()) { - for (uint i = 0; i < a->array.length(); ++i) { - if (i) - R += r4; - - Value e = a->__get__(ctx, i); - if (! (e.isUndefined() || e.isNull())) - R += e.toString(ctx)->toQString(); - } - } else { - // - // crazy! - // - Value r6 = self.property(ctx, ctx->engine->identifier(QStringLiteral("0"))); - if (!(r6.isUndefined() || r6.isNull())) - R = r6.toString(ctx)->toQString(); - - for (quint32 k = 1; k < r2; ++k) { - R += r4; - - String *name = Value::fromDouble(k).toString(ctx); - Value r12 = self.property(ctx, name); - - if (! (r12.isUndefined() || r12.isNull())) - R += r12.toString(ctx)->toQString(); - } - } - - visitedArrayElements.remove(self.objectValue()); - return Value::fromString(ctx, R); -} - -Value ArrayPrototype::method_pop(ExecutionContext *ctx) -{ - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - Value v = instance->getValueChecked(ctx, instance->array.back()); - instance->array.pop_back(); - return v; - } - - Value r1 = self.property(ctx, ctx->engine->id_length); - quint32 r2 = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; - if (r2) { - String *r6 = Value::fromDouble(r2 - 1).toString(ctx); - Value r7 = self.property(ctx, r6); - self.objectValue()->__delete__(ctx, r6); - self.objectValue()->__put__(ctx, ctx->engine->id_length, Value::fromDouble(2 - 1)); - return r7; - } - - self.objectValue()->__put__(ctx, ctx->engine->id_length, Value::fromDouble(0)); - return Value::undefinedValue(); -} - -Value ArrayPrototype::method_push(ExecutionContext *ctx) -{ - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { - Value val = ctx->argument(i); - instance->array.push_back(val); - } - return Value::fromDouble(instance->array.length()); - } - - Value r1 = self.property(ctx, ctx->engine->id_length); - quint32 n = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; - for (unsigned int index = 0; index < ctx->argumentCount; ++index, ++n) { - Value r3 = ctx->argument(index); - String *name = Value::fromDouble(n).toString(ctx); - self.objectValue()->__put__(ctx, name, r3); - } - Value r = Value::fromDouble(n); - self.objectValue()->__put__(ctx, ctx->engine->id_length, r); - return r; -} - -Value ArrayPrototype::method_reverse(ExecutionContext *ctx) -{ - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.reverse")); - - int lo = 0, hi = instance->array.length() - 1; - - // ### - for (; lo < hi; ++lo, --hi) { - Value tmp = instance->__get__(ctx, lo); - instance->array.set(lo, instance->__get__(ctx, hi)); - instance->array.set(hi, tmp); - } - return Value::undefinedValue(); -} - -Value ArrayPrototype::method_shift(ExecutionContext *ctx) -{ - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.shift")); - - Value v = instance->getValueChecked(ctx, instance->array.front()); - instance->array.pop_front(); - return v; -} - -Value ArrayPrototype::method_slice(ExecutionContext *ctx) -{ - Object *o = ctx->thisObject.toObject(ctx).objectValue(); - - Array result; - uint len = o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); - double s = ctx->argument(0).toInteger(ctx); - uint start; - if (s < 0) - start = (uint)qMax(len + s, 0.); - else if (s > len) - start = len; - else - start = (uint) s; - uint end = len; - if (!ctx->argument(1).isUndefined()) { - double e = ctx->argument(1).toInteger(ctx); - if (e < 0) - end = (uint)qMax(len + e, 0.); - else if (e > len) - end = len; - else - end = (uint) e; - } - - uint n = 0; - for (uint i = start; i < end; ++i) { - bool exists; - Value v = o->__get__(ctx, i, &exists); - if (exists) - result.set(n, v); - ++n; - } - return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); -} - -Value ArrayPrototype::method_sort(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - Value comparefn = ctx->argument(0); - instance->array.sort(ctx, instance, comparefn, len); - return ctx->thisObject; -} - -Value ArrayPrototype::method_splice(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - uint len = getLength(ctx, instance); - - ArrayObject *newArray = ctx->engine->newArrayObject(ctx); - - double rs = ctx->argument(0).toInteger(ctx); - uint start; - if (rs < 0) - start = (uint) qMax(0., len + rs); - else - start = (uint) qMin(rs, (double)len); - - uint deleteCount = (uint)qMin(qMax(ctx->argument(1).toInteger(ctx), 0.), (double)(len - start)); - - newArray->array.values.resize(deleteCount); - PropertyDescriptor *pd = newArray->array.values.data(); - for (uint i = 0; i < deleteCount; ++i) { - pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Enabled; - pd->enumberable = PropertyDescriptor::Enabled; - pd->configurable = PropertyDescriptor::Enabled; - pd->value = instance->__get__(ctx, start + i); - ++pd; - } - newArray->array.setLengthUnchecked(deleteCount); - - uint itemCount = ctx->argumentCount < 2 ? 0 : ctx->argumentCount - 2; - - if (itemCount < deleteCount) { - for (uint k = start; k < len - deleteCount; ++k) { - bool exists; - Value v = instance->__get__(ctx, k + deleteCount, &exists); - if (exists) - instance->array.set(k + itemCount, v); - else - instance->__delete__(ctx, k + itemCount); - } - for (uint k = len; k > len - deleteCount + itemCount; --k) - instance->__delete__(ctx, k - 1); - } else if (itemCount > deleteCount) { - uint k = len - deleteCount; - while (k > start) { - bool exists; - Value v = instance->__get__(ctx, k + deleteCount - 1, &exists); - if (exists) - instance->array.set(k + itemCount - 1, v); - else - instance->__delete__(ctx, k + itemCount - 1); - --k; - } - } - - for (uint i = 0; i < itemCount; ++i) - instance->array.set(start + i, ctx->argument(i + 2)); - - ctx->strictMode = true; - instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - deleteCount + itemCount)); - - return Value::fromObject(newArray); -} - -Value ArrayPrototype::method_unshift(ExecutionContext *ctx) -{ - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.shift")); - - for (int i = ctx->argumentCount - 1; i >= 0; --i) { - Value v = ctx->argument(i); - instance->array.push_front(v); - } - - uint l = instance->array.length(); - if (l < INT_MAX) - return Value::fromInt32(l); - return Value::fromDouble((double)l); -} - -Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - uint len = getLength(ctx, instance); - if (!len) - return Value::fromInt32(-1); - - Value searchValue; - uint fromIndex = 0; - - if (ctx->argumentCount >= 1) - searchValue = ctx->argument(0); - else - searchValue = Value::undefinedValue(); - - if (ctx->argumentCount >= 2) { - double f = ctx->argument(1).toInteger(ctx); - if (f >= len) - return Value::fromInt32(-1); - if (f < 0) - f = qMax(len + f, 0.); - fromIndex = (uint) f; - } - - if (instance->isString) { - for (uint k = fromIndex; k < len; ++k) { - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (exists && __qmljs_strict_equal(v, searchValue)) - return Value::fromDouble(k); - } - return Value::fromInt32(-1); - } - - return instance->array.indexOf(searchValue, fromIndex, len, ctx, instance); -} - -Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - uint len = getLength(ctx, instance); - if (!len) - return Value::fromInt32(-1); - - Value searchValue; - uint fromIndex = len - 1; - - if (ctx->argumentCount >= 1) - searchValue = ctx->argument(0); - else - searchValue = Value::undefinedValue(); - - if (ctx->argumentCount >= 2) { - double f = ctx->argument(1).toInteger(ctx); - if (f >= len) - return Value::fromInt32(-1); - if (f < 0) - f = qMax(len + f, 0.); - fromIndex = (uint) f; - } - - for (uint k = fromIndex; k > 0; --k) { - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (exists && __qmljs_strict_equal(v, searchValue)) - return Value::fromDouble(k); - } - return Value::fromInt32(-1); -} - -Value ArrayPrototype::method_every(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - __qmljs_throw_type_error(ctx); - - Value thisArg = ctx->argument(1); - - bool ok = true; - for (uint k = 0; ok && k < len; ++k) { - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value r = callback->call(ctx, thisArg, args, 3); - ok = __qmljs_to_boolean(r, ctx); - } - return Value::fromBoolean(ok); -} - -Value ArrayPrototype::method_some(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - __qmljs_throw_type_error(ctx); - - Value thisArg = ctx->argument(1); - - for (uint k = 0; k < len; ++k) { - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value r = callback->call(ctx, thisArg, args, 3); - if (__qmljs_to_boolean(r, ctx)) - return Value::fromBoolean(true); - } - return Value::fromBoolean(false); -} - -Value ArrayPrototype::method_forEach(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - __qmljs_throw_type_error(ctx); - - Value thisArg = ctx->argument(1); - - for (uint k = 0; k < len; ++k) { - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - callback->call(ctx, thisArg, args, 3); - } - return Value::undefinedValue(); -} - -Value ArrayPrototype::method_map(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - __qmljs_throw_type_error(ctx); - - Value thisArg = ctx->argument(1); - - ArrayObject *a = ctx->engine->newArrayObject(ctx); - a->array.setLength(len); - - for (uint k = 0; k < len; ++k) { - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value mapped = callback->call(ctx, thisArg, args, 3); - a->array.set(k, mapped); - } - return Value::fromObject(a); -} - -Value ArrayPrototype::method_filter(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - __qmljs_throw_type_error(ctx); - - Value thisArg = ctx->argument(1); - - ArrayObject *a = ctx->engine->newArrayObject(ctx); - - uint to = 0; - for (uint k = 0; k < len; ++k) { - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value selected = callback->call(ctx, thisArg, args, 3); - if (__qmljs_to_boolean(selected, ctx)) { - a->array.set(to, v); - ++to; - } - } - return Value::fromObject(a); -} - -Value ArrayPrototype::method_reduce(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - __qmljs_throw_type_error(ctx); - - uint k = 0; - Value acc; - if (ctx->argumentCount > 1) { - acc = ctx->argument(1); - } else { - bool kPresent = false; - while (k < len && !kPresent) { - Value v = instance->__get__(ctx, k, &kPresent); - if (kPresent) - acc = v; - ++k; - } - if (!kPresent) - __qmljs_throw_type_error(ctx); - } - - while (k < len) { - bool kPresent; - Value v = instance->__get__(ctx, k, &kPresent); - if (kPresent) { - Value args[4]; - args[0] = acc; - args[1] = v; - args[2] = Value::fromDouble(k); - args[3] = ctx->thisObject; - acc = callback->call(ctx, Value::undefinedValue(), args, 4); - } - ++k; - } - return acc; -} - -Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - __qmljs_throw_type_error(ctx); - - if (len == 0) { - if (ctx->argumentCount == 1) - __qmljs_throw_type_error(ctx); - return ctx->argument(1); - } - - uint k = len; - Value acc; - if (ctx->argumentCount > 1) { - acc = ctx->argument(1); - } else { - bool kPresent = false; - while (k > 0 && !kPresent) { - Value v = instance->__get__(ctx, k - 1, &kPresent); - if (kPresent) - acc = v; - --k; - } - if (!kPresent) - __qmljs_throw_type_error(ctx); - } - - while (k > 0) { - bool kPresent; - Value v = instance->__get__(ctx, k - 1, &kPresent); - if (kPresent) { - Value args[4]; - args[0] = acc; - args[1] = v; - args[2] = Value::fromDouble(k - 1); - args[3] = ctx->thisObject; - acc = callback->call(ctx, Value::undefinedValue(), args, 4); - } - --k; - } - return acc; -} - diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 687601cce1..12cf99b62e 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -126,45 +126,6 @@ struct BooleanPrototype: BooleanObject static Value method_valueOf(ExecutionContext *ctx); }; -struct ArrayCtor: FunctionObject -{ - ArrayCtor(ExecutionContext *scope); - - virtual Value call(ExecutionContext *ctx); -}; - -struct ArrayPrototype: ArrayObject -{ - ArrayPrototype(ExecutionContext *context) : ArrayObject(context) {} - - void init(ExecutionContext *ctx, const Value &ctor); - - static uint getLength(ExecutionContext *ctx, Object *o); - - static Value method_isArray(ExecutionContext *ctx); - static Value method_toString(ExecutionContext *ctx); - static Value method_toLocaleString(ExecutionContext *ctx); - static Value method_concat(ExecutionContext *ctx); - static Value method_join(ExecutionContext *ctx); - static Value method_pop(ExecutionContext *ctx); - static Value method_push(ExecutionContext *ctx); - static Value method_reverse(ExecutionContext *ctx); - static Value method_shift(ExecutionContext *ctx); - static Value method_slice(ExecutionContext *ctx); - static Value method_sort(ExecutionContext *ctx); - static Value method_splice(ExecutionContext *ctx); - static Value method_unshift(ExecutionContext *ctx); - static Value method_indexOf(ExecutionContext *ctx); - static Value method_lastIndexOf(ExecutionContext *ctx); - static Value method_every(ExecutionContext *ctx); - static Value method_some(ExecutionContext *ctx); - static Value method_forEach(ExecutionContext *ctx); - static Value method_map(ExecutionContext *ctx); - static Value method_filter(ExecutionContext *ctx); - static Value method_reduce(ExecutionContext *ctx); - static Value method_reduceRight(ExecutionContext *ctx); -}; - } // end of namespace VM } // end of namespace QQmlJS diff --git a/v4.pro b/v4.pro index d181c025f7..bc54c45fba 100644 --- a/v4.pro +++ b/v4.pro @@ -26,6 +26,7 @@ SOURCES += main.cpp \ qv4mm.cpp \ qv4managed.cpp \ qv4array.cpp \ + qv4arrayobject.cpp \ qv4argumentsobject.cpp \ qv4dateobject.cpp \ qv4errorobject.cpp \ @@ -57,6 +58,7 @@ HEADERS += \ qv4mm.h \ qv4managed.h \ qv4array.h \ + qv4arrayobject.h \ qv4argumentsobject.h \ qv4dateobject.h \ qv4errorobject.h \ -- cgit v1.2.3 From 3332397292f53c1b968a411b035007253ba55895 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 22:12:05 +0100 Subject: Move Number object into it's own file Change-Id: Ie431e653956efa35dc92523e236af1097d57bab9 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 1 + qv4ecmaobjects.cpp | 179 ----------------------------------------- qv4ecmaobjects_p.h | 21 ----- qv4numberobject.cpp | 226 ++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4numberobject.h | 76 ++++++++++++++++++ v4.pro | 2 + 6 files changed, 305 insertions(+), 200 deletions(-) create mode 100644 qv4numberobject.cpp create mode 100644 qv4numberobject.h diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 0e9abb5507..982b7e5aaa 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include "qv4mm.h" diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index e64852ec81..3db62973ca 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -558,185 +558,6 @@ Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Prope } -// -// Number object -// -NumberCtor::NumberCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value NumberCtor::construct(ExecutionContext *ctx) -{ - double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; - return Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(d))); -} - -Value NumberCtor::call(ExecutionContext *ctx) -{ - double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; - return Value::fromDouble(d); -} - -void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf())); - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308)); - -#ifdef __INTEL_COMPILER -# pragma warning( push ) -# pragma warning(disable: 239) -#endif - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324)); -#ifdef __INTEL_COMPILER -# pragma warning( pop ) -#endif - - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); - defineDefaultProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); - defineDefaultProperty(ctx, QStringLiteral("toFixed"), method_toFixed, 1); - defineDefaultProperty(ctx, QStringLiteral("toExponential"), method_toExponential); - defineDefaultProperty(ctx, QStringLiteral("toPrecision"), method_toPrecision); -} - -Value NumberPrototype::method_toString(ExecutionContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - Value arg = ctx->argument(0); - if (!arg.isUndefined()) { - int radix = arg.toInt32(ctx); - if (radix < 2 || radix > 36) { - ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") - .arg(radix)); - return Value::undefinedValue(); - } - - double num = thisObject->value.asDouble(); - if (std::isnan(num)) { - return Value::fromString(ctx, QStringLiteral("NaN")); - } else if (qIsInf(num)) { - return Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); - } - - if (radix != 10) { - QString str; - bool negative = false; - if (num < 0) { - negative = true; - num = -num; - } - double frac = num - ::floor(num); - num = Value::toInteger(num); - do { - char c = (char)::fmod(num, radix); - c = (c < 10) ? (c + '0') : (c - 10 + 'a'); - str.prepend(QLatin1Char(c)); - num = ::floor(num / radix); - } while (num != 0); - if (frac != 0) { - str.append(QLatin1Char('.')); - do { - frac = frac * radix; - char c = (char)::floor(frac); - c = (c < 10) ? (c + '0') : (c - 10 + 'a'); - str.append(QLatin1Char(c)); - frac = frac - ::floor(frac); - } while (frac != 0); - } - if (negative) - str.prepend(QLatin1Char('-')); - return Value::fromString(ctx, str); - } - } - - Value internalValue = thisObject->value; - String *str = internalValue.toString(ctx); - return Value::fromString(str); -} - -Value NumberPrototype::method_toLocaleString(ExecutionContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - String *str = thisObject->value.toString(ctx); - return Value::fromString(str); -} - -Value NumberPrototype::method_valueOf(ExecutionContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - return thisObject->value; -} - -Value NumberPrototype::method_toFixed(ExecutionContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - double fdigits = 0; - - if (ctx->argumentCount > 0) - fdigits = ctx->argument(0).toInteger(ctx); - - if (std::isnan(fdigits)) - fdigits = 0; - - double v = thisObject->value.asDouble(); - QString str; - if (std::isnan(v)) - str = QString::fromLatin1("NaN"); - else if (qIsInf(v)) - str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); - else - str = QString::number(v, 'f', int (fdigits)); - return Value::fromString(ctx, str); -} - -Value NumberPrototype::method_toExponential(ExecutionContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - double fdigits = 0; - - if (ctx->argumentCount > 0) - fdigits = ctx->argument(0).toInteger(ctx); - - QString z = QString::number(thisObject->value.asDouble(), 'e', int (fdigits)); - return Value::fromString(ctx, z); -} - -Value NumberPrototype::method_toPrecision(ExecutionContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - double fdigits = 0; - - if (ctx->argumentCount > 0) - fdigits = ctx->argument(0).toInteger(ctx); - - return Value::fromString(ctx, QString::number(thisObject->value.asDouble(), 'g', int (fdigits))); -} - // // Boolean object // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 12cf99b62e..1dcf82b0e3 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -88,27 +88,6 @@ struct ObjectPrototype: Object static Value fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc); }; -struct NumberCtor: FunctionObject -{ - NumberCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct NumberPrototype: NumberObject -{ - NumberPrototype(): NumberObject(Value::fromDouble(0)) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_toString(ExecutionContext *ctx); - static Value method_toLocaleString(ExecutionContext *ctx); - static Value method_valueOf(ExecutionContext *ctx); - static Value method_toFixed(ExecutionContext *ctx); - static Value method_toExponential(ExecutionContext *ctx); - static Value method_toPrecision(ExecutionContext *ctx); -}; - struct BooleanCtor: FunctionObject { BooleanCtor(ExecutionContext *scope); diff --git a/qv4numberobject.cpp b/qv4numberobject.cpp new file mode 100644 index 0000000000..1926e5ab7f --- /dev/null +++ b/qv4numberobject.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4numberobject.h" +#include +#include +#include +#include + + +using namespace QQmlJS::VM; + + +NumberCtor::NumberCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value NumberCtor::construct(ExecutionContext *ctx) +{ + double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; + return Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(d))); +} + +Value NumberCtor::call(ExecutionContext *ctx) +{ + double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; + return Value::fromDouble(d); +} + +void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308)); + +#ifdef __INTEL_COMPILER +# pragma warning( push ) +# pragma warning(disable: 239) +#endif + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324)); +#ifdef __INTEL_COMPILER +# pragma warning( pop ) +#endif + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); + defineDefaultProperty(ctx, QStringLiteral("toFixed"), method_toFixed, 1); + defineDefaultProperty(ctx, QStringLiteral("toExponential"), method_toExponential); + defineDefaultProperty(ctx, QStringLiteral("toPrecision"), method_toPrecision); +} + +Value NumberPrototype::method_toString(ExecutionContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + Value arg = ctx->argument(0); + if (!arg.isUndefined()) { + int radix = arg.toInt32(ctx); + if (radix < 2 || radix > 36) { + ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") + .arg(radix)); + return Value::undefinedValue(); + } + + double num = thisObject->value.asDouble(); + if (std::isnan(num)) { + return Value::fromString(ctx, QStringLiteral("NaN")); + } else if (qIsInf(num)) { + return Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); + } + + if (radix != 10) { + QString str; + bool negative = false; + if (num < 0) { + negative = true; + num = -num; + } + double frac = num - ::floor(num); + num = Value::toInteger(num); + do { + char c = (char)::fmod(num, radix); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.prepend(QLatin1Char(c)); + num = ::floor(num / radix); + } while (num != 0); + if (frac != 0) { + str.append(QLatin1Char('.')); + do { + frac = frac * radix; + char c = (char)::floor(frac); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.append(QLatin1Char(c)); + frac = frac - ::floor(frac); + } while (frac != 0); + } + if (negative) + str.prepend(QLatin1Char('-')); + return Value::fromString(ctx, str); + } + } + + Value internalValue = thisObject->value; + String *str = internalValue.toString(ctx); + return Value::fromString(str); +} + +Value NumberPrototype::method_toLocaleString(ExecutionContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + String *str = thisObject->value.toString(ctx); + return Value::fromString(str); +} + +Value NumberPrototype::method_valueOf(ExecutionContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + return thisObject->value; +} + +Value NumberPrototype::method_toFixed(ExecutionContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + double fdigits = 0; + + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(ctx); + + if (std::isnan(fdigits)) + fdigits = 0; + + double v = thisObject->value.asDouble(); + QString str; + if (std::isnan(v)) + str = QString::fromLatin1("NaN"); + else if (qIsInf(v)) + str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); + else + str = QString::number(v, 'f', int (fdigits)); + return Value::fromString(ctx, str); +} + +Value NumberPrototype::method_toExponential(ExecutionContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + double fdigits = 0; + + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(ctx); + + QString z = QString::number(thisObject->value.asDouble(), 'e', int (fdigits)); + return Value::fromString(ctx, z); +} + +Value NumberPrototype::method_toPrecision(ExecutionContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + double fdigits = 0; + + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(ctx); + + return Value::fromString(ctx, QString::number(thisObject->value.asDouble(), 'g', int (fdigits))); +} diff --git a/qv4numberobject.h b/qv4numberobject.h new file mode 100644 index 0000000000..85f9e1ff2b --- /dev/null +++ b/qv4numberobject.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4NUMBEROBJECT_H +#define QV4NUMBEROBJECT_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include + +namespace QQmlJS { +namespace VM { + +struct NumberCtor: FunctionObject +{ + NumberCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct NumberPrototype: NumberObject +{ + NumberPrototype(): NumberObject(Value::fromDouble(0)) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(ExecutionContext *ctx); + static Value method_toLocaleString(ExecutionContext *ctx); + static Value method_valueOf(ExecutionContext *ctx); + static Value method_toFixed(ExecutionContext *ctx); + static Value method_toExponential(ExecutionContext *ctx); + static Value method_toPrecision(ExecutionContext *ctx); +}; + + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/v4.pro b/v4.pro index bc54c45fba..ac77693274 100644 --- a/v4.pro +++ b/v4.pro @@ -34,6 +34,7 @@ SOURCES += main.cpp \ qv4globalobject.cpp \ qv4jsonobject.cpp \ qv4mathobject.cpp \ + qv4numberobject.cpp \ qv4object.cpp \ qv4regexpobject.cpp \ qv4stringobject.cpp \ @@ -66,6 +67,7 @@ HEADERS += \ qv4globalobject.h \ qv4jsonobject.h \ qv4mathobject.h \ + qv4numberobject.h \ qv4object.h \ qv4regexpobject.h \ qv4stringobject.h \ -- cgit v1.2.3 From 64fda7f7900249f2b94ec5b5a6c185bb99f274c0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 22:14:55 +0100 Subject: Move Boolean object into it's own file Change-Id: Ia62b223111fa41877bc24b6320f0078a3546c55b Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 1 + qv4booleanobject.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4booleanobject.h | 72 ++++++++++++++++++++++++++++++++++++++++++ qv4ecmaobjects.cpp | 48 ---------------------------- qv4ecmaobjects_p.h | 17 ---------- v4.pro | 2 ++ 6 files changed, 163 insertions(+), 65 deletions(-) create mode 100644 qv4booleanobject.cpp create mode 100644 qv4booleanobject.h diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 982b7e5aaa..e90af10805 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include diff --git a/qv4booleanobject.cpp b/qv4booleanobject.cpp new file mode 100644 index 0000000000..2a49435a10 --- /dev/null +++ b/qv4booleanobject.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4booleanobject.h" + +using namespace QQmlJS::VM; + +BooleanCtor::BooleanCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value BooleanCtor::construct(ExecutionContext *ctx) +{ + const double n = ctx->argument(0).toBoolean(ctx); + return Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n))); +} + +Value BooleanCtor::call(ExecutionContext *ctx) +{ + bool value = ctx->argumentCount ? ctx->argument(0).toBoolean(ctx) : 0; + return Value::fromBoolean(value); +} + +void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); +} + +Value BooleanPrototype::method_toString(ExecutionContext *ctx) +{ + BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); + if (!thisObject) + ctx->throwTypeError(); + + return Value::fromString(ctx, QLatin1String(thisObject->value.booleanValue() ? "true" : "false")); +} + +Value BooleanPrototype::method_valueOf(ExecutionContext *ctx) +{ + BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); + if (!thisObject) + ctx->throwTypeError(); + + return thisObject->value; +} diff --git a/qv4booleanobject.h b/qv4booleanobject.h new file mode 100644 index 0000000000..44d87b1d50 --- /dev/null +++ b/qv4booleanobject.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4BOOLEANOBJECT_H +#define QBOOLEANOBJECT_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include + +namespace QQmlJS { +namespace VM { + +struct BooleanCtor: FunctionObject +{ + BooleanCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct BooleanPrototype: BooleanObject +{ + BooleanPrototype(): BooleanObject(Value::fromBoolean(false)) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(ExecutionContext *ctx); + static Value method_valueOf(ExecutionContext *ctx); +}; + + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 3db62973ca..9037758b03 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -556,51 +556,3 @@ Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Prope return Value::fromObject(o); } - - -// -// Boolean object -// -BooleanCtor::BooleanCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value BooleanCtor::construct(ExecutionContext *ctx) -{ - const double n = ctx->argument(0).toBoolean(ctx); - return Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n))); -} - -Value BooleanCtor::call(ExecutionContext *ctx) -{ - bool value = ctx->argumentCount ? ctx->argument(0).toBoolean(ctx) : 0; - return Value::fromBoolean(value); -} - -void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); -} - -Value BooleanPrototype::method_toString(ExecutionContext *ctx) -{ - BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); - if (!thisObject) - ctx->throwTypeError(); - - return Value::fromString(ctx, QLatin1String(thisObject->value.booleanValue() ? "true" : "false")); -} - -Value BooleanPrototype::method_valueOf(ExecutionContext *ctx) -{ - BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); - if (!thisObject) - ctx->throwTypeError(); - - return thisObject->value; -} diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 1dcf82b0e3..93527a28ef 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -88,23 +88,6 @@ struct ObjectPrototype: Object static Value fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc); }; -struct BooleanCtor: FunctionObject -{ - BooleanCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct BooleanPrototype: BooleanObject -{ - BooleanPrototype(): BooleanObject(Value::fromBoolean(false)) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_toString(ExecutionContext *ctx); - static Value method_valueOf(ExecutionContext *ctx); -}; - } // end of namespace VM } // end of namespace QQmlJS diff --git a/v4.pro b/v4.pro index ac77693274..50bed794d1 100644 --- a/v4.pro +++ b/v4.pro @@ -28,6 +28,7 @@ SOURCES += main.cpp \ qv4array.cpp \ qv4arrayobject.cpp \ qv4argumentsobject.cpp \ + qv4booleanobject.cpp \ qv4dateobject.cpp \ qv4errorobject.cpp \ qv4functionobject.cpp \ @@ -61,6 +62,7 @@ HEADERS += \ qv4array.h \ qv4arrayobject.h \ qv4argumentsobject.h \ + qv4booleanobject.h \ qv4dateobject.h \ qv4errorobject.h \ qv4functionobject.h \ -- cgit v1.2.3 From bd6630925103cf77b70ec4b53252395efe29281e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 22:17:55 +0100 Subject: rename qv4ecmaobjects.* to qv4objectproto.* Change-Id: I4bf60a424542cb7e53d54011ac5d0c503453913f Reviewed-by: Simon Hausmann --- main.cpp | 2 +- qmljs_engine.cpp | 2 +- qmljs_environment.cpp | 2 +- qmljs_runtime.cpp | 2 +- qmljs_value.cpp | 2 +- qv4dateobject.cpp | 2 +- qv4ecmaobjects.cpp | 558 -------------------------------------------------- qv4ecmaobjects_p.h | 95 --------- qv4functionobject.cpp | 2 +- qv4globalobject.cpp | 2 +- qv4jsonobject.cpp | 2 +- qv4mm.cpp | 2 +- qv4object.cpp | 2 +- qv4objectproto.cpp | 558 ++++++++++++++++++++++++++++++++++++++++++++++++++ qv4objectproto.h | 95 +++++++++ qv4regexpobject.cpp | 2 +- qv4stringobject.cpp | 2 +- v4.pro | 4 +- 18 files changed, 668 insertions(+), 668 deletions(-) delete mode 100644 qv4ecmaobjects.cpp delete mode 100644 qv4ecmaobjects_p.h create mode 100644 qv4objectproto.cpp create mode 100644 qv4objectproto.h diff --git a/main.cpp b/main.cpp index 3f3e2df695..3a7911e997 100644 --- a/main.cpp +++ b/main.cpp @@ -54,7 +54,7 @@ #include "qv4isel_moth_p.h" #include "qv4vme_moth_p.h" #include "qv4syntaxchecker_p.h" -#include "qv4ecmaobjects_p.h" +#include "qv4objectproto.h" #include "qv4isel_p.h" #include "qv4mm.h" #include "qmljs_environment.h" diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index e90af10805..e3ce0b41aa 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -40,7 +40,7 @@ ****************************************************************************/ #include #include -#include +#include #include #include #include diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index b756f9796d..89ba600c62 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -43,7 +43,7 @@ #include "debugging.h" #include #include -#include +#include #include "qv4mm.h" #include diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 2c03f47523..38b5ac643d 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -43,7 +43,7 @@ #include "qmljs_runtime.h" #include "qv4object.h" #include "qv4ir_p.h" -#include "qv4ecmaobjects_p.h" +#include "qv4objectproto.h" #include "private/qlocale_tools_p.h" #include diff --git a/qmljs_value.cpp b/qmljs_value.cpp index ebea41e0ff..72f92a1701 100644 --- a/qmljs_value.cpp +++ b/qmljs_value.cpp @@ -40,7 +40,7 @@ ****************************************************************************/ #include #include -#include +#include namespace QQmlJS { namespace VM { diff --git a/qv4dateobject.cpp b/qv4dateobject.cpp index d9a40c9173..a6e5119f67 100644 --- a/qv4dateobject.cpp +++ b/qv4dateobject.cpp @@ -41,7 +41,7 @@ #include "qv4dateobject.h" -#include "qv4ecmaobjects_p.h" +#include "qv4objectproto.h" #include "qv4mm.h" #include #include diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp deleted file mode 100644 index 9037758b03..0000000000 --- a/qv4ecmaobjects.cpp +++ /dev/null @@ -1,558 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qv4ecmaobjects_p.h" -#include "qv4mm.h" -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifndef Q_WS_WIN -# include -# ifndef Q_OS_VXWORKS -# include -# else -# include "qplatformdefs.h" -# endif -#else -# include -#endif - -using namespace QQmlJS::VM; - - -// -// Object -// -ObjectCtor::ObjectCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value ObjectCtor::construct(ExecutionContext *ctx) -{ - if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull()) - return ctx->thisObject; - return __qmljs_to_object(ctx->argument(0), ctx); -} - -Value ObjectCtor::call(ExecutionContext *ctx) -{ - if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull()) - return Value::fromObject(ctx->engine->newObject()); - return __qmljs_to_object(ctx->argument(0), ctx); -} - -void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("create"), method_create, 2); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 3); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 2); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("seal"), method_seal, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("freeze"), method_freeze, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("keys"), method_keys, 1); - - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); - defineDefaultProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1); - defineDefaultProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1); - defineDefaultProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1); - defineDefaultProperty(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 0); - defineDefaultProperty(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 0); -} - -Value ObjectPrototype::method_getPrototypeOf(ExecutionContext *ctx) -{ - Value o = ctx->argument(0); - if (! o.isObject()) - ctx->throwTypeError(); - - Object *p = o.objectValue()->prototype; - return p ? Value::fromObject(p) : Value::nullValue(); -} - -Value ObjectPrototype::method_getOwnPropertyDescriptor(ExecutionContext *ctx) -{ - Value O = ctx->argument(0); - if (!O.isObject()) - ctx->throwTypeError(); - - String *name = ctx->argument(1).toString(ctx); - PropertyDescriptor *desc = O.objectValue()->__getOwnProperty__(ctx, name); - return fromPropertyDescriptor(ctx, desc); -} - -Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) -{ - Object *O = ctx->argument(0).asObject(); - if (!O) - ctx->throwTypeError(); - - ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); - Array &a = array->array; - ObjectIterator it(ctx, O, ObjectIterator::NoFlags); - while (1) { - Value v = it.nextPropertyNameAsString(); - if (v.isNull()) - break; - a.push_back(v); - } - return Value::fromObject(array); -} - -Value ObjectPrototype::method_create(ExecutionContext *ctx) -{ - Value O = ctx->argument(0); - if (!O.isObject() && !O.isNull()) - ctx->throwTypeError(); - - Object *newObject = ctx->engine->newObject(); - newObject->prototype = O.objectValue(); - - Value objValue = Value::fromObject(newObject); - if (ctx->argumentCount > 1 && !ctx->argument(1).isUndefined()) { - ctx->arguments[0] = objValue; - method_defineProperties(ctx); - } - - return objValue; -} - -Value ObjectPrototype::method_defineProperty(ExecutionContext *ctx) -{ - Value O = ctx->argument(0); - if (!O.isObject()) - ctx->throwTypeError(); - - String *name = ctx->argument(1).toString(ctx); - - Value attributes = ctx->argument(2); - PropertyDescriptor pd; - toPropertyDescriptor(ctx, attributes, &pd); - - if (!O.objectValue()->__defineOwnProperty__(ctx, name, &pd)) - __qmljs_throw_type_error(ctx); - - return O; -} - -Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) -{ - Value O = ctx->argument(0); - if (!O.isObject()) - ctx->throwTypeError(); - - Object *o = ctx->argument(1).toObject(ctx).objectValue(); - - ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - PropertyDescriptor n; - toPropertyDescriptor(ctx, o->getValue(ctx, pd), &n); - bool ok; - if (name) - ok = O.objectValue()->__defineOwnProperty__(ctx, name, &n); - else - ok = O.objectValue()->__defineOwnProperty__(ctx, index, &n); - if (!ok) - __qmljs_throw_type_error(ctx); - } - - return O; -} - -Value ObjectPrototype::method_seal(ExecutionContext *ctx) -{ - if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->argument(0).objectValue(); - o->extensible = false; - - ObjectIterator it(ctx, o, ObjectIterator::NoFlags); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - pd->configurable = PropertyDescriptor::Disabled; - } - return ctx->argument(0); -} - -Value ObjectPrototype::method_freeze(ExecutionContext *ctx) -{ - if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->argument(0).objectValue(); - o->extensible = false; - - ObjectIterator it(ctx, o, ObjectIterator::NoFlags); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - if (pd->type == PropertyDescriptor::Data) - pd->writable = PropertyDescriptor::Disabled; - pd->configurable = PropertyDescriptor::Disabled; - } - return ctx->argument(0); -} - -Value ObjectPrototype::method_preventExtensions(ExecutionContext *ctx) -{ - if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->argument(0).objectValue(); - o->extensible = false; - return ctx->argument(0); -} - -Value ObjectPrototype::method_isSealed(ExecutionContext *ctx) -{ - if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->argument(0).objectValue(); - if (o->extensible) - return Value::fromBoolean(false); - - ObjectIterator it(ctx, o, ObjectIterator::NoFlags); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - if (pd->configurable != PropertyDescriptor::Disabled) - return Value::fromBoolean(false); - } - return Value::fromBoolean(true); -} - -Value ObjectPrototype::method_isFrozen(ExecutionContext *ctx) -{ - if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->argument(0).objectValue(); - if (o->extensible) - return Value::fromBoolean(false); - - ObjectIterator it(ctx, o, ObjectIterator::NoFlags); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - if (pd->isWritable() || pd->isConfigurable()) - return Value::fromBoolean(false); - } - return Value::fromBoolean(true); -} - -Value ObjectPrototype::method_isExtensible(ExecutionContext *ctx) -{ - if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->argument(0).objectValue(); - return Value::fromBoolean(o->extensible); -} - -Value ObjectPrototype::method_keys(ExecutionContext *ctx) -{ - if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->argument(0).objectValue(); - - ArrayObject *a = ctx->engine->newArrayObject(ctx); - - ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - Value key; - if (name) { - key = Value::fromString(name); - } else { - key = Value::fromDouble(index); - key = __qmljs_to_string(key, ctx); - } - a->array.push_back(key); - } - - return Value::fromObject(a); -} - -Value ObjectPrototype::method_toString(ExecutionContext *ctx) -{ - if (ctx->thisObject.isUndefined()) { - return Value::fromString(ctx, QStringLiteral("[object Undefined]")); - } else if (ctx->thisObject.isNull()) { - return Value::fromString(ctx, QStringLiteral("[object Null]")); - } else { - Value obj = __qmljs_to_object(ctx->thisObject, ctx); - QString className = obj.objectValue()->className(); - return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(className)); - } -} - -Value ObjectPrototype::method_toLocaleString(ExecutionContext *ctx) -{ - Object *o = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - Value ts = o->__get__(ctx, ctx->engine->identifier(QStringLiteral("toString"))); - FunctionObject *f = ts.asFunctionObject(); - if (!f) - __qmljs_throw_type_error(ctx); - return f->call(ctx, Value::fromObject(o), 0, 0); -} - -Value ObjectPrototype::method_valueOf(ExecutionContext *ctx) -{ - return ctx->thisObject.toObject(ctx); -} - -Value ObjectPrototype::method_hasOwnProperty(ExecutionContext *ctx) -{ - String *P = ctx->argument(0).toString(ctx); - Value O = ctx->thisObject.toObject(ctx); - bool r = O.objectValue()->__getOwnProperty__(ctx, P) != 0; - return Value::fromBoolean(r); -} - -Value ObjectPrototype::method_isPrototypeOf(ExecutionContext *ctx) -{ - Value V = ctx->argument(0); - if (! V.isObject()) - return Value::fromBoolean(false); - - Object *O = ctx->thisObject.toObject(ctx).objectValue(); - Object *proto = V.objectValue()->prototype; - while (proto) { - if (O == proto) - return Value::fromBoolean(true); - proto = proto->prototype; - } - return Value::fromBoolean(false); -} - -Value ObjectPrototype::method_propertyIsEnumerable(ExecutionContext *ctx) -{ - String *p = ctx->argument(0).toString(ctx); - - Object *o = ctx->thisObject.toObject(ctx).objectValue(); - PropertyDescriptor *pd = o->__getOwnProperty__(ctx, p); - return Value::fromBoolean(pd && pd->isEnumerable()); -} - -Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx) -{ - if (ctx->argumentCount < 2) - __qmljs_throw_type_error(ctx); - String *prop = ctx->argument(0).toString(ctx); - - FunctionObject *f = ctx->argument(1).asFunctionObject(); - if (!f) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->thisObject.toObject(ctx).objectValue(); - - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(f, 0); - pd.configurable = PropertyDescriptor::Enabled; - pd.enumberable = PropertyDescriptor::Enabled; - o->__defineOwnProperty__(ctx, prop, &pd); - return Value::undefinedValue(); -} - -Value ObjectPrototype::method_defineSetter(ExecutionContext *ctx) -{ - if (ctx->argumentCount < 2) - __qmljs_throw_type_error(ctx); - String *prop = ctx->argument(0).toString(ctx); - - FunctionObject *f = ctx->argument(1).asFunctionObject(); - if (!f) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->thisObject.toObject(ctx).objectValue(); - - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(0, f); - pd.configurable = PropertyDescriptor::Enabled; - pd.enumberable = PropertyDescriptor::Enabled; - o->__defineOwnProperty__(ctx, prop, &pd); - return Value::undefinedValue(); -} - -void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc) -{ - if (!v.isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = v.objectValue(); - - desc->type = PropertyDescriptor::Generic; - - desc->enumberable = PropertyDescriptor::Undefined; - if (o->__hasProperty__(ctx, ctx->engine->id_enumerable)) - desc->enumberable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_enumerable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; - - desc->configurable = PropertyDescriptor::Undefined; - if (o->__hasProperty__(ctx, ctx->engine->id_configurable)) - desc->configurable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_configurable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; - - desc->get = 0; - if (o->__hasProperty__(ctx, ctx->engine->id_get)) { - Value get = o->__get__(ctx, ctx->engine->id_get); - FunctionObject *f = get.asFunctionObject(); - if (f) { - desc->get = f; - } else if (get.isUndefined()) { - desc->get = (FunctionObject *)0x1; - } else { - __qmljs_throw_type_error(ctx); - } - desc->type = PropertyDescriptor::Accessor; - } - - desc->set = 0; - if (o->__hasProperty__(ctx, ctx->engine->id_set)) { - Value set = o->__get__(ctx, ctx->engine->id_set); - FunctionObject *f = set.asFunctionObject(); - if (f) { - desc->set = f; - } else if (set.isUndefined()) { - desc->set = (FunctionObject *)0x1; - } else { - __qmljs_throw_type_error(ctx); - } - desc->type = PropertyDescriptor::Accessor; - } - - desc->writable = PropertyDescriptor::Undefined; - if (o->__hasProperty__(ctx, ctx->engine->id_writable)) { - if (desc->isAccessor()) - __qmljs_throw_type_error(ctx); - desc->writable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_writable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; - // writable forces it to be a data descriptor - desc->value = Value::undefinedValue(); - } - - if (o->__hasProperty__(ctx, ctx->engine->id_value)) { - if (desc->isAccessor()) - __qmljs_throw_type_error(ctx); - desc->value = o->__get__(ctx, ctx->engine->id_value); - desc->type = PropertyDescriptor::Data; - } - -} - - -Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc) -{ - if (!desc) - return Value::undefinedValue(); - - ExecutionEngine *engine = ctx->engine; -// Let obj be the result of creating a new object as if by the expression new Object() where Object is the standard built-in constructor with that name. - Object *o = engine->newObject(); - - PropertyDescriptor pd; - pd.type = PropertyDescriptor::Data; - pd.writable = PropertyDescriptor::Enabled; - pd.enumberable = PropertyDescriptor::Enabled; - pd.configurable = PropertyDescriptor::Enabled; - - if (desc->isData()) { - pd.value = desc->value; - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("value")), &pd); - pd.value = Value::fromBoolean(desc->writable == PropertyDescriptor::Enabled ? true : false); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("writable")), &pd); - } else { - pd.value = desc->get ? Value::fromObject(desc->get) : Value::undefinedValue(); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("get")), &pd); - pd.value = desc->set ? Value::fromObject(desc->set) : Value::undefinedValue(); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("set")), &pd); - } - pd.value = Value::fromBoolean(desc->enumberable == PropertyDescriptor::Enabled ? true : false); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("enumerable")), &pd); - pd.value = Value::fromBoolean(desc->configurable == PropertyDescriptor::Enabled ? true : false); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("configurable")), &pd); - - return Value::fromObject(o); -} diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h deleted file mode 100644 index 93527a28ef..0000000000 --- a/qv4ecmaobjects_p.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4ECMAOBJECTS_P_H -#define QV4ECMAOBJECTS_P_H - -#include "qv4object.h" -#include "qv4functionobject.h" -#include - -namespace QQmlJS { -namespace VM { - -struct ObjectCtor: FunctionObject -{ - ObjectCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct ObjectPrototype: Object -{ - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_getPrototypeOf(ExecutionContext *ctx); - static Value method_getOwnPropertyDescriptor(ExecutionContext *ctx); - static Value method_getOwnPropertyNames(ExecutionContext *ctx); - static Value method_create(ExecutionContext *ctx); - static Value method_defineProperty(ExecutionContext *ctx); - static Value method_defineProperties(ExecutionContext *ctx); - static Value method_seal(ExecutionContext *ctx); - static Value method_freeze(ExecutionContext *ctx); - static Value method_preventExtensions(ExecutionContext *ctx); - static Value method_isSealed(ExecutionContext *ctx); - static Value method_isFrozen(ExecutionContext *ctx); - static Value method_isExtensible(ExecutionContext *ctx); - static Value method_keys(ExecutionContext *ctx); - - static Value method_toString(ExecutionContext *ctx); - static Value method_toLocaleString(ExecutionContext *ctx); - static Value method_valueOf(ExecutionContext *ctx); - static Value method_hasOwnProperty(ExecutionContext *ctx); - static Value method_isPrototypeOf(ExecutionContext *ctx); - static Value method_propertyIsEnumerable(ExecutionContext *ctx); - - static Value method_defineGetter(ExecutionContext *ctx); - static Value method_defineSetter(ExecutionContext *ctx); - - static void toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc); - static Value fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc); -}; - - -} // end of namespace VM -} // end of namespace QQmlJS - -#endif // QV4ECMAOBJECTS_P_H diff --git a/qv4functionobject.cpp b/qv4functionobject.cpp index 01be72624e..78948889cf 100644 --- a/qv4functionobject.cpp +++ b/qv4functionobject.cpp @@ -42,7 +42,7 @@ #include "qv4object.h" #include "qv4ir_p.h" #include "qv4isel_p.h" -#include "qv4ecmaobjects_p.h" +#include "qv4objectproto.h" #include "qv4stringobject.h" #include "qv4mm.h" diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index 4b2c1911f9..f87ad707dc 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -42,7 +42,7 @@ #include "qv4globalobject.h" #include "qv4ir_p.h" #include "qv4isel_p.h" -#include "qv4ecmaobjects_p.h" +#include "qv4objectproto.h" #include "qv4stringobject.h" #include "qv4mm.h" diff --git a/qv4jsonobject.cpp b/qv4jsonobject.cpp index 91013e9e24..77645d20e8 100644 --- a/qv4jsonobject.cpp +++ b/qv4jsonobject.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ #include -#include +#include #include #include #include diff --git a/qv4mm.cpp b/qv4mm.cpp index daac6e8a24..c3e527059f 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -29,7 +29,7 @@ #include "qmljs_engine.h" #include "qv4object.h" -#include "qv4ecmaobjects_p.h" +#include "qv4objectproto.h" #include "qv4mm.h" #include "PageAllocation.h" #include "StdLibExtras.h" diff --git a/qv4object.cpp b/qv4object.cpp index a8814b368c..6c723f1609 100644 --- a/qv4object.cpp +++ b/qv4object.cpp @@ -42,7 +42,7 @@ #include "qv4object.h" #include "qv4ir_p.h" #include "qv4isel_p.h" -#include "qv4ecmaobjects_p.h" +#include "qv4objectproto.h" #include "qv4stringobject.h" #include "qv4mm.h" diff --git a/qv4objectproto.cpp b/qv4objectproto.cpp new file mode 100644 index 0000000000..98f205d1d8 --- /dev/null +++ b/qv4objectproto.cpp @@ -0,0 +1,558 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4objectproto.h" +#include "qv4mm.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef Q_WS_WIN +# include +# ifndef Q_OS_VXWORKS +# include +# else +# include "qplatformdefs.h" +# endif +#else +# include +#endif + +using namespace QQmlJS::VM; + + +// +// Object +// +ObjectCtor::ObjectCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value ObjectCtor::construct(ExecutionContext *ctx) +{ + if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull()) + return ctx->thisObject; + return __qmljs_to_object(ctx->argument(0), ctx); +} + +Value ObjectCtor::call(ExecutionContext *ctx) +{ + if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull()) + return Value::fromObject(ctx->engine->newObject()); + return __qmljs_to_object(ctx->argument(0), ctx); +} + +void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("create"), method_create, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 3); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("seal"), method_seal, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("freeze"), method_freeze, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("keys"), method_keys, 1); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + defineDefaultProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1); + defineDefaultProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1); + defineDefaultProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1); + defineDefaultProperty(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 0); + defineDefaultProperty(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 0); +} + +Value ObjectPrototype::method_getPrototypeOf(ExecutionContext *ctx) +{ + Value o = ctx->argument(0); + if (! o.isObject()) + ctx->throwTypeError(); + + Object *p = o.objectValue()->prototype; + return p ? Value::fromObject(p) : Value::nullValue(); +} + +Value ObjectPrototype::method_getOwnPropertyDescriptor(ExecutionContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + String *name = ctx->argument(1).toString(ctx); + PropertyDescriptor *desc = O.objectValue()->__getOwnProperty__(ctx, name); + return fromPropertyDescriptor(ctx, desc); +} + +Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) +{ + Object *O = ctx->argument(0).asObject(); + if (!O) + ctx->throwTypeError(); + + ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); + Array &a = array->array; + ObjectIterator it(ctx, O, ObjectIterator::NoFlags); + while (1) { + Value v = it.nextPropertyNameAsString(); + if (v.isNull()) + break; + a.push_back(v); + } + return Value::fromObject(array); +} + +Value ObjectPrototype::method_create(ExecutionContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject() && !O.isNull()) + ctx->throwTypeError(); + + Object *newObject = ctx->engine->newObject(); + newObject->prototype = O.objectValue(); + + Value objValue = Value::fromObject(newObject); + if (ctx->argumentCount > 1 && !ctx->argument(1).isUndefined()) { + ctx->arguments[0] = objValue; + method_defineProperties(ctx); + } + + return objValue; +} + +Value ObjectPrototype::method_defineProperty(ExecutionContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + String *name = ctx->argument(1).toString(ctx); + + Value attributes = ctx->argument(2); + PropertyDescriptor pd; + toPropertyDescriptor(ctx, attributes, &pd); + + if (!O.objectValue()->__defineOwnProperty__(ctx, name, &pd)) + __qmljs_throw_type_error(ctx); + + return O; +} + +Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(1).toObject(ctx).objectValue(); + + ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + PropertyDescriptor n; + toPropertyDescriptor(ctx, o->getValue(ctx, pd), &n); + bool ok; + if (name) + ok = O.objectValue()->__defineOwnProperty__(ctx, name, &n); + else + ok = O.objectValue()->__defineOwnProperty__(ctx, index, &n); + if (!ok) + __qmljs_throw_type_error(ctx); + } + + return O; +} + +Value ObjectPrototype::method_seal(ExecutionContext *ctx) +{ + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + + ObjectIterator it(ctx, o, ObjectIterator::NoFlags); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + pd->configurable = PropertyDescriptor::Disabled; + } + return ctx->argument(0); +} + +Value ObjectPrototype::method_freeze(ExecutionContext *ctx) +{ + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + + ObjectIterator it(ctx, o, ObjectIterator::NoFlags); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + if (pd->type == PropertyDescriptor::Data) + pd->writable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Disabled; + } + return ctx->argument(0); +} + +Value ObjectPrototype::method_preventExtensions(ExecutionContext *ctx) +{ + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + return ctx->argument(0); +} + +Value ObjectPrototype::method_isSealed(ExecutionContext *ctx) +{ + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + if (o->extensible) + return Value::fromBoolean(false); + + ObjectIterator it(ctx, o, ObjectIterator::NoFlags); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + if (pd->configurable != PropertyDescriptor::Disabled) + return Value::fromBoolean(false); + } + return Value::fromBoolean(true); +} + +Value ObjectPrototype::method_isFrozen(ExecutionContext *ctx) +{ + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + if (o->extensible) + return Value::fromBoolean(false); + + ObjectIterator it(ctx, o, ObjectIterator::NoFlags); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + if (pd->isWritable() || pd->isConfigurable()) + return Value::fromBoolean(false); + } + return Value::fromBoolean(true); +} + +Value ObjectPrototype::method_isExtensible(ExecutionContext *ctx) +{ + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + return Value::fromBoolean(o->extensible); +} + +Value ObjectPrototype::method_keys(ExecutionContext *ctx) +{ + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + + ArrayObject *a = ctx->engine->newArrayObject(ctx); + + ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + Value key; + if (name) { + key = Value::fromString(name); + } else { + key = Value::fromDouble(index); + key = __qmljs_to_string(key, ctx); + } + a->array.push_back(key); + } + + return Value::fromObject(a); +} + +Value ObjectPrototype::method_toString(ExecutionContext *ctx) +{ + if (ctx->thisObject.isUndefined()) { + return Value::fromString(ctx, QStringLiteral("[object Undefined]")); + } else if (ctx->thisObject.isNull()) { + return Value::fromString(ctx, QStringLiteral("[object Null]")); + } else { + Value obj = __qmljs_to_object(ctx->thisObject, ctx); + QString className = obj.objectValue()->className(); + return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(className)); + } +} + +Value ObjectPrototype::method_toLocaleString(ExecutionContext *ctx) +{ + Object *o = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Value ts = o->__get__(ctx, ctx->engine->identifier(QStringLiteral("toString"))); + FunctionObject *f = ts.asFunctionObject(); + if (!f) + __qmljs_throw_type_error(ctx); + return f->call(ctx, Value::fromObject(o), 0, 0); +} + +Value ObjectPrototype::method_valueOf(ExecutionContext *ctx) +{ + return ctx->thisObject.toObject(ctx); +} + +Value ObjectPrototype::method_hasOwnProperty(ExecutionContext *ctx) +{ + String *P = ctx->argument(0).toString(ctx); + Value O = ctx->thisObject.toObject(ctx); + bool r = O.objectValue()->__getOwnProperty__(ctx, P) != 0; + return Value::fromBoolean(r); +} + +Value ObjectPrototype::method_isPrototypeOf(ExecutionContext *ctx) +{ + Value V = ctx->argument(0); + if (! V.isObject()) + return Value::fromBoolean(false); + + Object *O = ctx->thisObject.toObject(ctx).objectValue(); + Object *proto = V.objectValue()->prototype; + while (proto) { + if (O == proto) + return Value::fromBoolean(true); + proto = proto->prototype; + } + return Value::fromBoolean(false); +} + +Value ObjectPrototype::method_propertyIsEnumerable(ExecutionContext *ctx) +{ + String *p = ctx->argument(0).toString(ctx); + + Object *o = ctx->thisObject.toObject(ctx).objectValue(); + PropertyDescriptor *pd = o->__getOwnProperty__(ctx, p); + return Value::fromBoolean(pd && pd->isEnumerable()); +} + +Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx) +{ + if (ctx->argumentCount < 2) + __qmljs_throw_type_error(ctx); + String *prop = ctx->argument(0).toString(ctx); + + FunctionObject *f = ctx->argument(1).asFunctionObject(); + if (!f) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->thisObject.toObject(ctx).objectValue(); + + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(f, 0); + pd.configurable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + o->__defineOwnProperty__(ctx, prop, &pd); + return Value::undefinedValue(); +} + +Value ObjectPrototype::method_defineSetter(ExecutionContext *ctx) +{ + if (ctx->argumentCount < 2) + __qmljs_throw_type_error(ctx); + String *prop = ctx->argument(0).toString(ctx); + + FunctionObject *f = ctx->argument(1).asFunctionObject(); + if (!f) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->thisObject.toObject(ctx).objectValue(); + + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(0, f); + pd.configurable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + o->__defineOwnProperty__(ctx, prop, &pd); + return Value::undefinedValue(); +} + +void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc) +{ + if (!v.isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = v.objectValue(); + + desc->type = PropertyDescriptor::Generic; + + desc->enumberable = PropertyDescriptor::Undefined; + if (o->__hasProperty__(ctx, ctx->engine->id_enumerable)) + desc->enumberable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_enumerable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; + + desc->configurable = PropertyDescriptor::Undefined; + if (o->__hasProperty__(ctx, ctx->engine->id_configurable)) + desc->configurable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_configurable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; + + desc->get = 0; + if (o->__hasProperty__(ctx, ctx->engine->id_get)) { + Value get = o->__get__(ctx, ctx->engine->id_get); + FunctionObject *f = get.asFunctionObject(); + if (f) { + desc->get = f; + } else if (get.isUndefined()) { + desc->get = (FunctionObject *)0x1; + } else { + __qmljs_throw_type_error(ctx); + } + desc->type = PropertyDescriptor::Accessor; + } + + desc->set = 0; + if (o->__hasProperty__(ctx, ctx->engine->id_set)) { + Value set = o->__get__(ctx, ctx->engine->id_set); + FunctionObject *f = set.asFunctionObject(); + if (f) { + desc->set = f; + } else if (set.isUndefined()) { + desc->set = (FunctionObject *)0x1; + } else { + __qmljs_throw_type_error(ctx); + } + desc->type = PropertyDescriptor::Accessor; + } + + desc->writable = PropertyDescriptor::Undefined; + if (o->__hasProperty__(ctx, ctx->engine->id_writable)) { + if (desc->isAccessor()) + __qmljs_throw_type_error(ctx); + desc->writable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_writable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; + // writable forces it to be a data descriptor + desc->value = Value::undefinedValue(); + } + + if (o->__hasProperty__(ctx, ctx->engine->id_value)) { + if (desc->isAccessor()) + __qmljs_throw_type_error(ctx); + desc->value = o->__get__(ctx, ctx->engine->id_value); + desc->type = PropertyDescriptor::Data; + } + +} + + +Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc) +{ + if (!desc) + return Value::undefinedValue(); + + ExecutionEngine *engine = ctx->engine; +// Let obj be the result of creating a new object as if by the expression new Object() where Object is the standard built-in constructor with that name. + Object *o = engine->newObject(); + + PropertyDescriptor pd; + pd.type = PropertyDescriptor::Data; + pd.writable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + pd.configurable = PropertyDescriptor::Enabled; + + if (desc->isData()) { + pd.value = desc->value; + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("value")), &pd); + pd.value = Value::fromBoolean(desc->writable == PropertyDescriptor::Enabled ? true : false); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("writable")), &pd); + } else { + pd.value = desc->get ? Value::fromObject(desc->get) : Value::undefinedValue(); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("get")), &pd); + pd.value = desc->set ? Value::fromObject(desc->set) : Value::undefinedValue(); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("set")), &pd); + } + pd.value = Value::fromBoolean(desc->enumberable == PropertyDescriptor::Enabled ? true : false); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("enumerable")), &pd); + pd.value = Value::fromBoolean(desc->configurable == PropertyDescriptor::Enabled ? true : false); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("configurable")), &pd); + + return Value::fromObject(o); +} diff --git a/qv4objectproto.h b/qv4objectproto.h new file mode 100644 index 0000000000..93527a28ef --- /dev/null +++ b/qv4objectproto.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ECMAOBJECTS_P_H +#define QV4ECMAOBJECTS_P_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include + +namespace QQmlJS { +namespace VM { + +struct ObjectCtor: FunctionObject +{ + ObjectCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct ObjectPrototype: Object +{ + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_getPrototypeOf(ExecutionContext *ctx); + static Value method_getOwnPropertyDescriptor(ExecutionContext *ctx); + static Value method_getOwnPropertyNames(ExecutionContext *ctx); + static Value method_create(ExecutionContext *ctx); + static Value method_defineProperty(ExecutionContext *ctx); + static Value method_defineProperties(ExecutionContext *ctx); + static Value method_seal(ExecutionContext *ctx); + static Value method_freeze(ExecutionContext *ctx); + static Value method_preventExtensions(ExecutionContext *ctx); + static Value method_isSealed(ExecutionContext *ctx); + static Value method_isFrozen(ExecutionContext *ctx); + static Value method_isExtensible(ExecutionContext *ctx); + static Value method_keys(ExecutionContext *ctx); + + static Value method_toString(ExecutionContext *ctx); + static Value method_toLocaleString(ExecutionContext *ctx); + static Value method_valueOf(ExecutionContext *ctx); + static Value method_hasOwnProperty(ExecutionContext *ctx); + static Value method_isPrototypeOf(ExecutionContext *ctx); + static Value method_propertyIsEnumerable(ExecutionContext *ctx); + + static Value method_defineGetter(ExecutionContext *ctx); + static Value method_defineSetter(ExecutionContext *ctx); + + static void toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc); + static Value fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc); +}; + + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/qv4regexpobject.cpp b/qv4regexpobject.cpp index 20d0088185..d393593b96 100644 --- a/qv4regexpobject.cpp +++ b/qv4regexpobject.cpp @@ -42,7 +42,7 @@ #include "qv4regexpobject.h" #include "qv4ir_p.h" #include "qv4isel_p.h" -#include "qv4ecmaobjects_p.h" +#include "qv4objectproto.h" #include "qv4stringobject.h" #include "qv4mm.h" diff --git a/qv4stringobject.cpp b/qv4stringobject.cpp index 36030fbd46..af5ac9430f 100644 --- a/qv4stringobject.cpp +++ b/qv4stringobject.cpp @@ -42,7 +42,7 @@ #include "qv4stringobject.h" #include "qv4regexpobject.h" -#include "qv4ecmaobjects_p.h" +#include "qv4objectproto.h" #include "qv4mm.h" #include #include diff --git a/v4.pro b/v4.pro index 50bed794d1..006f3d43ec 100644 --- a/v4.pro +++ b/v4.pro @@ -18,7 +18,6 @@ SOURCES += main.cpp \ qmljs_runtime.cpp \ qmljs_value.cpp \ qv4syntaxchecker.cpp \ - qv4ecmaobjects.cpp \ qv4isel_masm.cpp \ llvm_runtime.cpp \ qv4isel_p.cpp \ @@ -37,6 +36,7 @@ SOURCES += main.cpp \ qv4mathobject.cpp \ qv4numberobject.cpp \ qv4object.cpp \ + qv4objectproto.cpp \ qv4regexpobject.cpp \ qv4stringobject.cpp \ qv4string.cpp \ @@ -52,7 +52,6 @@ HEADERS += \ qmljs_math.h \ qmljs_value.h \ qv4syntaxchecker_p.h \ - qv4ecmaobjects_p.h \ qv4isel_masm_p.h \ qv4isel_p.h \ qv4isel_util_p.h \ @@ -71,6 +70,7 @@ HEADERS += \ qv4mathobject.h \ qv4numberobject.h \ qv4object.h \ + qv4objectproto.h \ qv4regexpobject.h \ qv4stringobject.h \ qv4string.h \ -- cgit v1.2.3 From 7632431c4ce18bd8d8d32fc2273d890e0247f39e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 22:34:02 +0100 Subject: Fix a few bugs in Number.prototype Change-Id: I1ab95c019b628c3e298f88ea8d5b7f8b4d560aee Reviewed-by: Simon Hausmann --- qv4numberobject.cpp | 5 ++++- tests/TestExpectations | 5 ----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/qv4numberobject.cpp b/qv4numberobject.cpp index 1926e5ab7f..10ab037f68 100644 --- a/qv4numberobject.cpp +++ b/qv4numberobject.cpp @@ -87,7 +87,7 @@ void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); - defineDefaultProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString); defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); defineDefaultProperty(ctx, QStringLiteral("toFixed"), method_toFixed, 1); defineDefaultProperty(ctx, QStringLiteral("toExponential"), method_toExponential); @@ -185,6 +185,9 @@ Value NumberPrototype::method_toFixed(ExecutionContext *ctx) if (std::isnan(fdigits)) fdigits = 0; + if (fdigits < 0 || fdigits > 20) + ctx->throwRangeError(ctx->thisObject); + double v = thisObject->value.asDouble(); QString str; if (std::isnan(v)) diff --git a/tests/TestExpectations b/tests/TestExpectations index 8143b46464..e7c2e666e1 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -516,7 +516,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-13 failing 15.2.3.3-4-51 failing 15.2.3.3-4-9 failing -15.2.3.3-4-90 failing 15.2.3.6-2-17-1 failing 15.2.3.5-4-315 failing 15.2.3.6-4-291-1 failing @@ -745,9 +744,6 @@ S15.5.4.15_A1_T9 failing S15.5.4.7_A1_T11 failing S15.5.4.8_A1_T12 failing S15.5.4.8_A1_T4 failing -S15.7.4_A3.3 failing -S15.7.4.5_A1.3_T01 failing -S15.7.4.5_A1.3_T02 failing S15.7.4.5_A1.4_T01 failing 6.4_c failing 8.0_L15 failing @@ -862,7 +858,6 @@ S15.7.4.5_A1.4_T01 failing 12.4_a failing 13.1.1_6 failing 13.1.1_7 failing -13.2.1_1 failing 13.2.1_4 failing 13.2.1_5 failing 13.3.0_2 failing -- cgit v1.2.3 From eeed42daead7b3372ea80b49a700fd7067e18c9e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 21 Jan 2013 23:07:22 +0100 Subject: Fix remaining issues in lastIndexOf Change-Id: Ice23d8af32e26909757cadf5d4a0257d7926ca31 Reviewed-by: Simon Hausmann --- qv4arrayobject.cpp | 18 +++++++++++------- tests/TestExpectations | 45 --------------------------------------------- 2 files changed, 11 insertions(+), 52 deletions(-) diff --git a/qv4arrayobject.cpp b/qv4arrayobject.cpp index 302122936a..3cad1f7b29 100644 --- a/qv4arrayobject.cpp +++ b/qv4arrayobject.cpp @@ -453,7 +453,7 @@ Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) return Value::fromInt32(-1); Value searchValue; - uint fromIndex = len - 1; + uint fromIndex = len; if (ctx->argumentCount >= 1) searchValue = ctx->argument(0); @@ -462,14 +462,18 @@ Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) if (ctx->argumentCount >= 2) { double f = ctx->argument(1).toInteger(ctx); - if (f >= len) - return Value::fromInt32(-1); - if (f < 0) - f = qMax(len + f, 0.); - fromIndex = (uint) f; + if (f > 0) + f = qMin(f, (double)(len - 1)); + else if (f < 0) { + f = len + f; + if (f < 0) + return Value::fromInt32(-1); + } + fromIndex = (uint) f + 1; } - for (uint k = fromIndex; k > 0; --k) { + for (uint k = fromIndex; k > 0;) { + --k; bool exists; Value v = instance->__get__(ctx, k, &exists); if (exists && __qmljs_strict_equal(v, searchValue)) diff --git a/tests/TestExpectations b/tests/TestExpectations index e7c2e666e1..b7d8d003f5 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -13,8 +13,6 @@ S15.9.3.1_A5_T6 failing # Tests failing that are supposed to pass. -15.4.4.15-5-16 failing -15.4.4.15-5-12 failing 15.4.4.14-9-9 failing S10.2.3_A1.1_T2 failing S10.2.3_A1.2_T2 failing @@ -547,49 +545,7 @@ S15.4.4.13_A4_T2 failing 15.4.4.14-9-a-17 failing 15.4.4.14-9-a-7 failing 15.4.4.14-9-a-9 failing -15.4.4.15-3-2 failing -15.4.4.15-3-29 failing -15.4.4.15-5-14 failing -15.4.4.15-5-2 failing -15.4.4.15-5-33 failing -15.4.4.15-5-4 failing -15.4.4.15-5-5 failing -15.4.4.15-5-7 failing -15.4.4.15-5-8 failing -15.4.4.15-5-9 failing -15.4.4.15-6-1 failing -15.4.4.15-8-b-i-1 failing -15.4.4.15-8-b-i-10 failing -15.4.4.15-8-b-i-11 failing -15.4.4.15-8-b-i-12 failing -15.4.4.15-8-b-i-13 failing -15.4.4.15-8-b-i-14 failing -15.4.4.15-8-b-i-15 failing -15.4.4.15-8-b-i-16 failing -15.4.4.15-8-b-i-17 failing -15.4.4.15-8-b-i-18 failing -15.4.4.15-8-b-i-19 failing -15.4.4.15-8-b-i-2 failing -15.4.4.15-8-b-i-20 failing -15.4.4.15-8-b-i-21 failing -15.4.4.15-8-b-i-22 failing -15.4.4.15-8-b-i-23 failing -15.4.4.15-8-b-i-25 failing -15.4.4.15-8-b-i-26 failing -15.4.4.15-8-b-i-3 failing -15.4.4.15-8-b-i-4 failing 15.4.4.15-8-b-i-5 failing -15.4.4.15-8-b-i-6 failing -15.4.4.15-8-b-i-7 failing -15.4.4.15-8-b-i-8 failing -15.4.4.15-8-b-i-9 failing -15.4.4.15-8-b-ii-11 failing -15.4.4.15-8-b-ii-2 failing -15.4.4.15-8-b-ii-3 failing -15.4.4.15-8-b-ii-6 failing -15.4.4.15-8-b-ii-7 failing -15.4.4.15-8-b-ii-8 failing -15.4.4.15-8-b-ii-9 failing 15.4.4.16-1-10 failing 15.4.4.17-1-10 failing 15.4.4.18-1-10 failing @@ -742,7 +698,6 @@ S15.5.4.15_A1_T8 failing S15.5.4.15_A1_T9 failing 15.5.4.20-4-1 failing S15.5.4.7_A1_T11 failing -S15.5.4.8_A1_T12 failing S15.5.4.8_A1_T4 failing S15.7.4.5_A1.4_T01 failing 6.4_c failing -- cgit v1.2.3 From ed7aeb40deb68a3e300cb3bd784a0c012b1afcab Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 22 Jan 2013 09:31:56 +0100 Subject: Don't clutter the source dir with .o files Change-Id: If265752cb7ce3e55321e0dc0db7fbaa4369d9af4 Reviewed-by: Simon Hausmann --- v4.pro | 2 ++ 1 file changed, 2 insertions(+) diff --git a/v4.pro b/v4.pro index 006f3d43ec..c379a977bc 100644 --- a/v4.pro +++ b/v4.pro @@ -4,6 +4,8 @@ CONFIG += console LLVM_CONFIG=llvm-config +OBJECTS_DIR=.obj + # Pick up the qmake variable or environment variable for LLVM_INSTALL_DIR. If either was set, change the LLVM_CONFIG to use that. isEmpty(LLVM_INSTALL_DIR):LLVM_INSTALL_DIR=$$(LLVM_INSTALL_DIR) !isEmpty(LLVM_INSTALL_DIR):LLVM_CONFIG=$$LLVM_INSTALL_DIR/bin/llvm-config -- cgit v1.2.3 From 4dd4b77c6a1309d1c4c088a8fe84166876f685ea Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 22 Jan 2013 10:24:58 +0100 Subject: Fix remaining issues in Array.prototype.(un)shift Change-Id: I872abc79656511806955337a15d0fc04b8b286f8 Reviewed-by: Simon Hausmann --- qv4array.cpp | 4 +-- qv4arrayobject.cpp | 80 ++++++++++++++++++++++++++++++++++++++++---------- tests/TestExpectations | 19 ------------ 3 files changed, 66 insertions(+), 37 deletions(-) diff --git a/qv4array.cpp b/qv4array.cpp index 15150cc9e3..049402c763 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -609,7 +609,7 @@ bool Array::setLength(uint newLen) { it = prev; } } else { - PropertyDescriptor *it = values.data() + offset + values.size(); + PropertyDescriptor *it = values.data() + values.size(); const PropertyDescriptor *begin = values.constData() + offset + newLen; while (--it >= begin) { if (it->type != PropertyDescriptor::Generic && !it->isConfigurable()) { @@ -618,7 +618,7 @@ bool Array::setLength(uint newLen) { break; } } - values.resize(newLen); + values.resize(newLen + offset); } } else { if (newLen >= 0x100000) diff --git a/qv4arrayobject.cpp b/qv4arrayobject.cpp index 3cad1f7b29..f7c2069c6c 100644 --- a/qv4arrayobject.cpp +++ b/qv4arrayobject.cpp @@ -272,13 +272,41 @@ Value ArrayPrototype::method_reverse(ExecutionContext *ctx) Value ArrayPrototype::method_shift(ExecutionContext *ctx) { - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.shift")); + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); + + if (!len) { + if (!instance->isArray) + instance->__put__(ctx, ctx->engine->id_length, Value::fromInt32(0)); + return Value::undefinedValue(); + } + + Value result = instance->getValueChecked(ctx, instance->array.front()); - Value v = instance->getValueChecked(ctx, instance->array.front()); - instance->array.pop_front(); - return v; + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->array.length()) + protoHasArray = true; + + if (!protoHasArray && len >= instance->array.length()) { + instance->array.pop_front(); + } else { + // do it the slow way + for (uint k = 1; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (exists) + instance->__put__(ctx, k - 1, v); + else + instance->__delete__(ctx, k - 1); + } + instance->__delete__(ctx, len - 1); + } + + if (!instance->isArray) + instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - 1)); + return result; } Value ArrayPrototype::method_slice(ExecutionContext *ctx) @@ -393,19 +421,39 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) Value ArrayPrototype::method_unshift(ExecutionContext *ctx) { - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.shift")); + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); + + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->array.length()) + protoHasArray = true; - for (int i = ctx->argumentCount - 1; i >= 0; --i) { - Value v = ctx->argument(i); - instance->array.push_front(v); + if (!protoHasArray && len >= instance->array.length()) { + for (int i = ctx->argumentCount - 1; i >= 0; --i) { + Value v = ctx->argument(i); + instance->array.push_front(v); + } + } else { + for (uint k = len; k > 0; --k) { + bool exists; + Value v = instance->__get__(ctx, k - 1, &exists); + if (exists) + instance->__put__(ctx, k + ctx->argumentCount - 1, v); + else + instance->__delete__(ctx, k + ctx->argumentCount - 1); + } + for (uint i = 0; i < ctx->argumentCount; ++i) + instance->__put__(ctx, i, ctx->argument(i)); } + uint newLen = len + ctx->argumentCount; + if (!instance->isArray) + instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); - uint l = instance->array.length(); - if (l < INT_MAX) - return Value::fromInt32(l); - return Value::fromDouble((double)l); + if (newLen < INT_MAX) + return Value::fromInt32(newLen); + return Value::fromDouble((double)newLen); } Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index b7d8d003f5..70c1529728 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -533,14 +533,6 @@ S15.1.3.2_A5.3 failing 15.2.3.7-6-a-286 failing 15.2.3.7-6-a-288 failing 15.2.3.7-6-a-289 failing -S15.4.4.13_A2_T1 failing -S15.4.4.13_A2_T2 failing -S15.4.4.13_A2_T3 failing -S15.4.4.13_A3_T1 failing -S15.4.4.13_A3_T2 failing -S15.4.4.13_A3_T3 failing -S15.4.4.13_A4_T1 failing -S15.4.4.13_A4_T2 failing 15.4.4.14-9-a-10 failing 15.4.4.14-9-a-17 failing 15.4.4.14-9-a-7 failing @@ -582,17 +574,6 @@ S15.4.4.8_A3_T2 failing S15.4.4.8_A3_T3 failing S15.4.4.8_A4_T1 failing S15.4.4.8_A4_T2 failing -S15.4.4.9_A1.2_T1 failing -S15.4.4.9_A2_T1 failing -S15.4.4.9_A2_T2 failing -S15.4.4.9_A2_T3 failing -S15.4.4.9_A2_T4 failing -S15.4.4.9_A2_T5 failing -S15.4.4.9_A3_T1 failing -S15.4.4.9_A3_T2 failing -S15.4.4.9_A3_T3 failing -S15.4.4.9_A4_T1 failing -S15.4.4.9_A4_T2 failing S15.5.4.13_A2_T2 failing S15.5.4.13_A3_T3 failing S15.5.4.14_A1_T1 failing -- cgit v1.2.3 From 70045ffccccb186f8bb8cdd39cdde34654670860 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 22 Jan 2013 10:57:38 +0100 Subject: Fix remaining test failures in Array.prototype.push/pop Change-Id: If645e8d4628f252ffc3e96037cdc3749d6cf2ddd Reviewed-by: Simon Hausmann --- qv4arrayobject.cpp | 82 +++++++++++++++++++++++++++++++------------------- tests/TestExpectations | 8 ----- 2 files changed, 51 insertions(+), 39 deletions(-) diff --git a/qv4arrayobject.cpp b/qv4arrayobject.cpp index f7c2069c6c..8d3757bfd5 100644 --- a/qv4arrayobject.cpp +++ b/qv4arrayobject.cpp @@ -209,48 +209,68 @@ Value ArrayPrototype::method_join(ExecutionContext *ctx) Value ArrayPrototype::method_pop(ExecutionContext *ctx) { - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - Value v = instance->getValueChecked(ctx, instance->array.back()); - instance->array.pop_back(); - return v; - } + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); - Value r1 = self.property(ctx, ctx->engine->id_length); - quint32 r2 = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; - if (r2) { - String *r6 = Value::fromDouble(r2 - 1).toString(ctx); - Value r7 = self.property(ctx, r6); - self.objectValue()->__delete__(ctx, r6); - self.objectValue()->__put__(ctx, ctx->engine->id_length, Value::fromDouble(2 - 1)); - return r7; + if (!len) { + if (!instance->isArray) + instance->__put__(ctx, ctx->engine->id_length, Value::fromInt32(0)); + return Value::undefinedValue(); } - self.objectValue()->__put__(ctx, ctx->engine->id_length, Value::fromDouble(0)); - return Value::undefinedValue(); + Value result = instance->__get__(ctx, len - 1); + + instance->__delete__(ctx, len - 1); + if (instance->isArray) + instance->array.setLengthUnchecked(len - 1); + else + instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - 1)); + return result; } Value ArrayPrototype::method_push(ExecutionContext *ctx) { - Value self = ctx->thisObject; - if (ArrayObject *instance = self.asArrayObject()) { - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { - Value val = ctx->argument(i); - instance->array.push_back(val); + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); + + if (len + ctx->argumentCount < len) { + // ughh... + double l = len; + for (double i = 0; i < ctx->argumentCount; ++i) { + Value idx = Value::fromDouble(l + i); + instance->__put__(ctx, idx.toString(ctx), ctx->argument(i)); } - return Value::fromDouble(instance->array.length()); + double newLen = l + ctx->argumentCount; + if (!instance->isArray) + instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); + else + ctx->throwRangeError(Value::fromString(ctx, QStringLiteral("Array.prototype.push: Overflow"))); + return Value::fromDouble(newLen); } - Value r1 = self.property(ctx, ctx->engine->id_length); - quint32 n = !r1.isUndefined() ? r1.toUInt32(ctx) : 0; - for (unsigned int index = 0; index < ctx->argumentCount; ++index, ++n) { - Value r3 = ctx->argument(index); - String *name = Value::fromDouble(n).toString(ctx); - self.objectValue()->__put__(ctx, name, r3); + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->array.length()) + protoHasArray = true; + + if (!protoHasArray && len == instance->array.length()) { + for (uint i = 0; i < ctx->argumentCount; ++i) { + Value v = ctx->argument(i); + instance->array.push_back(v); + } + } else { + for (uint i = 0; i < ctx->argumentCount; ++i) + instance->__put__(ctx, len + i, ctx->argument(i)); } - Value r = Value::fromDouble(n); - self.objectValue()->__put__(ctx, ctx->engine->id_length, r); - return r; + uint newLen = len + ctx->argumentCount; + if (!instance->isArray) + instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); + + if (newLen < INT_MAX) + return Value::fromInt32(newLen); + return Value::fromDouble((double)newLen); + } Value ArrayPrototype::method_reverse(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index 70c1529728..4694aa2abc 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -555,14 +555,6 @@ S15.4.4.3_A3_T1 failing S15.4.4.4_A2_T1 failing S15.4.4.4_A2_T2 failing S15.4.4.4_A3_T1 failing -S15.4.4.6_A1.2_T1 failing -S15.4.4.6_A3_T2 failing -S15.4.4.6_A3_T3 failing -S15.4.4.6_A4_T1 failing -S15.4.4.6_A4_T2 failing -S15.4.4.7_A3 failing -S15.4.4.7_A4_T2 failing -S15.4.4.7_A4_T3 failing S15.5.4.11_A5_T1 failing S15.4.4.8_A1_T1 failing S15.4.4.8_A1_T2 failing -- cgit v1.2.3 From 37d61fef02c5ac82edd60f1f7544cde6dab53ae6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 22 Jan 2013 11:53:08 +0100 Subject: Fix a bug in Array.proto.indexOf for sparse arrays Change-Id: I113c242b057ee4c6f2d06edf17e8bf8b453ba58c Reviewed-by: Simon Hausmann --- qv4array.cpp | 2 +- tests/TestExpectations | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/qv4array.cpp b/qv4array.cpp index 049402c763..8cf745e1b4 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -491,7 +491,7 @@ Value Array::indexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *c return Value::fromDouble(i); } } else if (sparse) { - for (SparseArrayNode *n = sparse->findNode(fromIndex); n && n->key() < endIndex; n = n->nextNode()) { + for (SparseArrayNode *n = sparse->lowerBound(fromIndex); n && n->key() < endIndex; n = n->nextNode()) { bool exists; Value value = o->getValueChecked(ctx, descriptor(n->value), &exists); if (exists && __qmljs_strict_equal(value, v)) diff --git a/tests/TestExpectations b/tests/TestExpectations index 4694aa2abc..ca67cd97b0 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -13,7 +13,6 @@ S15.9.3.1_A5_T6 failing # Tests failing that are supposed to pass. -15.4.4.14-9-9 failing S10.2.3_A1.1_T2 failing S10.2.3_A1.2_T2 failing S10.2.3_A1.3_T2 failing -- cgit v1.2.3 From 1f67558f680f9ce70425e04010efadfacc946ba4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 Jan 2013 17:21:36 +0100 Subject: Implement String.prototype.split Change-Id: I676bf6b9338ac6ce3aebadc6007858983d45f02e Reviewed-by: Lars Knoll --- qv4stringobject.cpp | 78 ++++++++++++++++++++++++++++++++++++++-- tests/TestExpectations | 98 -------------------------------------------------- 2 files changed, 76 insertions(+), 100 deletions(-) diff --git a/qv4stringobject.cpp b/qv4stringobject.cpp index af5ac9430f..c2063fc724 100644 --- a/qv4stringobject.cpp +++ b/qv4stringobject.cpp @@ -510,8 +510,82 @@ Value StringPrototype::method_slice(ExecutionContext *ctx) Value StringPrototype::method_split(ExecutionContext *ctx) { - ctx->throwUnimplemented(QStringLiteral("String.prototype.splt")); - return Value::undefinedValue(); + QString text; + if (StringObject *thisObject = ctx->thisObject.asStringObject()) + text = thisObject->value.stringValue()->toQString(); + else + text = ctx->thisObject.toString(ctx)->toQString(); + + Value separatorValue = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); + Value limitValue = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); + + ArrayObject* array = ctx->engine->newArrayObject(ctx); + Value result = Value::fromObject(array); + + if (separatorValue.isUndefined()) { + if (limitValue.isUndefined()) { + array->array.push_back(Value::fromString(ctx, text)); + return result; + } + return Value::fromString(ctx, text.left(limitValue.toInteger(ctx))); + } + + uint limit = limitValue.isUndefined() ? UINT_MAX : limitValue.toUInt32(ctx); + + if (limit == 0) + return result; + + if (RegExpObject* re = separatorValue.asRegExpObject()) { + if (re->value->pattern().isEmpty()) { + re = 0; + separatorValue = Value::fromString(ctx, QString()); + } + } + + if (RegExpObject* re = separatorValue.asRegExpObject()) { + uint offset = 0; + uint* matchOffsets = (uint*)alloca(re->value->captureCount() * 2 * sizeof(uint)); + while (true) { + uint result = re->value->match(text, offset, matchOffsets); + if (result == JSC::Yarr::offsetNoMatch) + break; + + array->array.push_back(Value::fromString(ctx, text.mid(offset, matchOffsets[0] - offset))); + offset = qMax(offset + 1, matchOffsets[1]); + + if (array->array.length() >= limit) + break; + + for (int i = 1; i < re->value->captureCount(); ++i) { + uint start = matchOffsets[i * 2]; + uint end = matchOffsets[i * 2 + 1]; + array->array.push_back(Value::fromString(ctx, text.mid(start, end - start))); + if (array->array.length() >= limit) + break; + } + } + if (array->array.length() < limit) + array->array.push_back(Value::fromString(ctx, text.mid(offset))); + } else { + QString separator = separatorValue.toString(ctx)->toQString(); + if (separator.isEmpty()) { + for (uint i = 0; i < qMin(limit, uint(text.length())); ++i) + array->array.push_back(Value::fromString(ctx, text.mid(i, 1))); + return result; + } + + int start = 0; + int end; + while ((end = text.indexOf(separator, start)) != -1) { + array->array.push_back(Value::fromString(ctx, text.mid(start, end - start))); + start = end + separator.size(); + if (array->array.length() >= limit) + break; + } + if (array->array.length() < limit && start != -1) + array->array.push_back(Value::fromString(ctx, text.mid(start))); + } + return result; } Value StringPrototype::method_substr(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index ca67cd97b0..7efa62f76a 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -209,7 +209,6 @@ S12.10_A1.4_T4 failing S12.10_A1.4_T5 failing S12.10_A1.5_T4 failing S12.10_A1.5_T5 failing -S12.6.1_A8 failing 12.2.1-1-s failing 12.2.1-12-s failing 12.2.1-13-s failing @@ -239,7 +238,6 @@ S12.6.1_A8 failing 12.2.1-4-s failing 12.2.1-7-s failing 12.2.1-8-s failing -S12.6.2_A8 failing 13.0-10-s failing 13.0-11-s failing 13.0-13-s failing @@ -567,103 +565,7 @@ S15.4.4.8_A4_T1 failing S15.4.4.8_A4_T2 failing S15.5.4.13_A2_T2 failing S15.5.4.13_A3_T3 failing -S15.5.4.14_A1_T1 failing -S15.5.4.14_A1_T10 failing -S15.5.4.14_A1_T11 failing -S15.5.4.14_A1_T12 failing -S15.5.4.14_A1_T13 failing -S15.5.4.14_A1_T14 failing -S15.5.4.14_A1_T15 failing -S15.5.4.14_A1_T16 failing -S15.5.4.14_A1_T17 failing -S15.5.4.14_A1_T18 failing -S15.5.4.14_A1_T2 failing -S15.5.4.14_A1_T3 failing -S15.5.4.14_A1_T4 failing -S15.5.4.14_A1_T5 failing -S15.5.4.14_A1_T6 failing -S15.5.4.14_A1_T7 failing -S15.5.4.14_A1_T8 failing -S15.5.4.14_A1_T9 failing -S15.5.4.14_A2_T1 failing -S15.5.4.14_A2_T10 failing -S15.5.4.14_A2_T11 failing -S15.5.4.14_A2_T12 failing -S15.5.4.14_A2_T13 failing -S15.5.4.14_A2_T14 failing -S15.5.4.14_A2_T15 failing -S15.5.4.14_A2_T16 failing -S15.5.4.14_A2_T17 failing -S15.5.4.14_A2_T18 failing -S15.5.4.14_A2_T19 failing -S15.5.4.14_A2_T2 failing -S15.5.4.14_A2_T20 failing -S15.5.4.14_A2_T21 failing -S15.5.4.14_A2_T22 failing -S15.5.4.14_A2_T23 failing -S15.5.4.14_A2_T24 failing -S15.5.4.14_A2_T25 failing -S15.5.4.14_A2_T26 failing -S15.5.4.14_A2_T27 failing -S15.5.4.14_A2_T28 failing -S15.5.4.14_A2_T29 failing -S15.5.4.14_A2_T3 failing -S15.5.4.14_A2_T30 failing -S15.5.4.14_A2_T31 failing -S15.5.4.14_A2_T32 failing -S15.5.4.14_A2_T33 failing -S15.5.4.14_A2_T34 failing -S15.5.4.14_A2_T35 failing -S15.5.4.14_A2_T36 failing -S15.5.4.14_A2_T37 failing -S15.5.4.14_A2_T38 failing -S15.5.4.14_A2_T39 failing -S15.5.4.14_A2_T4 failing -S15.5.4.14_A2_T40 failing -S15.5.4.14_A2_T41 failing -S15.5.4.14_A2_T42 failing -S15.5.4.14_A2_T43 failing -S15.5.4.14_A2_T5 failing -S15.5.4.14_A2_T6 failing -S15.5.4.14_A2_T7 failing -S15.5.4.14_A2_T8 failing -S15.5.4.14_A2_T9 failing -S15.5.4.14_A3_T1 failing -S15.5.4.14_A3_T10 failing -S15.5.4.14_A3_T11 failing -S15.5.4.14_A3_T2 failing -S15.5.4.14_A3_T3 failing -S15.5.4.14_A3_T4 failing -S15.5.4.14_A3_T5 failing -S15.5.4.14_A3_T6 failing S15.5.4.14_A3_T7 failing -S15.5.4.14_A3_T8 failing -S15.5.4.14_A3_T9 failing -S15.5.4.14_A4_T1 failing -S15.5.4.14_A4_T10 failing -S15.5.4.14_A4_T11 failing -S15.5.4.14_A4_T12 failing -S15.5.4.14_A4_T13 failing -S15.5.4.14_A4_T14 failing -S15.5.4.14_A4_T15 failing -S15.5.4.14_A4_T16 failing -S15.5.4.14_A4_T17 failing -S15.5.4.14_A4_T18 failing -S15.5.4.14_A4_T19 failing -S15.5.4.14_A4_T2 failing -S15.5.4.14_A4_T20 failing -S15.5.4.14_A4_T21 failing -S15.5.4.14_A4_T22 failing -S15.5.4.14_A4_T23 failing -S15.5.4.14_A4_T24 failing -S15.5.4.14_A4_T25 failing -S15.5.4.14_A4_T3 failing -S15.5.4.14_A4_T4 failing -S15.5.4.14_A4_T5 failing -S15.5.4.14_A4_T6 failing -S15.5.4.14_A4_T7 failing -S15.5.4.14_A4_T8 failing -S15.5.4.14_A4_T9 failing S15.5.4.15_A1_T2 failing S15.5.4.15_A1_T7 failing S15.5.4.15_A1_T8 failing -- cgit v1.2.3 From aad19bb51fb7ca0cddcc5d9a772c0f80c042e37b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 22 Jan 2013 11:59:38 +0100 Subject: Fix Math.toString() Change-Id: I6fc901c7b9c1ca625a13ae8417f931c2fc948fe9 Reviewed-by: Lars Knoll --- qv4mathobject.h | 1 + tests/TestExpectations | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/qv4mathobject.h b/qv4mathobject.h index 68014f1f61..c2b26efce1 100644 --- a/qv4mathobject.h +++ b/qv4mathobject.h @@ -50,6 +50,7 @@ namespace VM { struct MathObject: Object { MathObject(ExecutionContext *ctx); + virtual QString className() { return QStringLiteral("Math"); } static Value method_abs(ExecutionContext *ctx); static Value method_acos(ExecutionContext *ctx); diff --git a/tests/TestExpectations b/tests/TestExpectations index 7efa62f76a..7a75cc28f9 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -535,15 +535,8 @@ S15.1.3.2_A5.3 failing 15.4.4.14-9-a-7 failing 15.4.4.14-9-a-9 failing 15.4.4.15-8-b-i-5 failing -15.4.4.16-1-10 failing -15.4.4.17-1-10 failing -15.4.4.18-1-10 failing -15.4.4.19-1-10 failing 15.4.4.19-5-1 failing -15.4.4.20-1-10 failing -15.4.4.21-1-10 failing 15.4.4.21-9-c-i-6 failing -15.4.4.22-1-10 failing 15.4.4.22-9-c-i-6 failing S15.4.4.3_A1_T1 failing S15.4.4.3_A3_T1 failing @@ -565,7 +558,6 @@ S15.4.4.8_A4_T1 failing S15.4.4.8_A4_T2 failing S15.5.4.13_A2_T2 failing S15.5.4.13_A3_T3 failing -S15.5.4.14_A3_T7 failing S15.5.4.15_A1_T2 failing S15.5.4.15_A1_T7 failing S15.5.4.15_A1_T8 failing -- cgit v1.2.3 From 28f368c04411ac5f2a162bcf0e2a10de1d14b4a4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 22 Jan 2013 12:15:51 +0100 Subject: Fix name property of Error prototype Change-Id: I21445a63a807d6a54bf8d8523fb901ae83f674a4 Reviewed-by: Lars Knoll --- qv4errorobject.cpp | 1 + tests/TestExpectations | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/qv4errorobject.cpp b/qv4errorobject.cpp index d59fa23563..b5005cb12c 100644 --- a/qv4errorobject.cpp +++ b/qv4errorobject.cpp @@ -203,6 +203,7 @@ void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) obj->defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); obj->defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); obj->defineDefaultProperty(ctx, QStringLiteral("message"), Value::fromString(ctx, QString())); + obj->defineDefaultProperty(ctx, QStringLiteral("name"), Value::fromString(ctx, QStringLiteral("Error"))); } Value ErrorPrototype::method_toString(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index 7a75cc28f9..7faeaa7e63 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -289,8 +289,6 @@ S15.1.2.2_A9.2 failing S15.1.2.2_A9.3 failing S15.1.2.2_A9.4 failing S15.1.2.2_A9.7 failing -S15.11.4.2_A1 failing -S15.11.4.2_A2 failing 15.12.1.1-g6-3 failing 15.12.1.1-g6-4 failing 15.12.1.1-g6-5 failing -- cgit v1.2.3 From 65aa5a7739a7de30f39758a5fa0634fcc32d5e2b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 22 Jan 2013 12:20:31 +0100 Subject: Run the tests in a simulated pacific time zone Some date related tests rely on this. Change-Id: I764392d8bfdc1f738bb218d43bc234b901384c78 Reviewed-by: Lars Knoll --- tests/TestExpectations | 13 +------------ tests/test262.py | 2 ++ 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index 7faeaa7e63..d22c616d84 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -2,16 +2,6 @@ S15.1.3.1_A2.5_T1 S15.1.3.2_A2.5_T1 -# broken test cases -# The test data in the 6 tests below is wrong. V8 agrees with our results and the test suite is 0.375 days off -S15.9.3.1_A5_T1 failing -S15.9.3.1_A5_T2 failing -S15.9.3.1_A5_T3 failing -S15.9.3.1_A5_T4 failing -S15.9.3.1_A5_T5 failing -S15.9.3.1_A5_T6 failing - - # Tests failing that are supposed to pass. S10.2.3_A1.1_T2 failing S10.2.3_A1.2_T2 failing @@ -561,7 +551,6 @@ S15.5.4.15_A1_T7 failing S15.5.4.15_A1_T8 failing S15.5.4.15_A1_T9 failing 15.5.4.20-4-1 failing -S15.5.4.7_A1_T11 failing S15.5.4.8_A1_T4 failing S15.7.4.5_A1.4_T01 failing 6.4_c failing @@ -745,4 +734,4 @@ S15.4.4.4_A1_T2 failing 15.4.4.21-8-b-iii-1-6 failing 15.12.3_4-1-1 -15.12.3_4-1-3 \ No newline at end of file +15.12.3_4-1-3 diff --git a/tests/test262.py b/tests/test262.py index 7f958086f7..09a3931ad5 100755 --- a/tests/test262.py +++ b/tests/test262.py @@ -523,6 +523,8 @@ class TestSuite(object): def Main(): + # Some date tests rely on being run in pacific time. + os.environ["TZ"] = "PST8PDT" parser = BuildOptions() (options, args) = parser.parse_args() ValidateOptions(options) -- cgit v1.2.3 From f9312114789d7e943e5ab6d855b947c06f06f2cb Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 22 Jan 2013 14:06:22 +0100 Subject: Fix name of Array.prototype.toLocaleString method Change-Id: I7347f25e07ee99a332b8e5f4b6cc50aac1c2bb70 Reviewed-by: Lars Knoll --- qv4arrayobject.cpp | 2 +- tests/TestExpectations | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/qv4arrayobject.cpp b/qv4arrayobject.cpp index 8d3757bfd5..b740d6d5cd 100644 --- a/qv4arrayobject.cpp +++ b/qv4arrayobject.cpp @@ -79,7 +79,7 @@ void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isArray"), method_isArray, 1); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocalString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); defineDefaultProperty(ctx, QStringLiteral("join"), method_join, 1); defineDefaultProperty(ctx, QStringLiteral("pop"), method_pop, 0); diff --git a/tests/TestExpectations b/tests/TestExpectations index d22c616d84..dfbf08b5d4 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -497,7 +497,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-11 failing 15.2.3.3-4-12 failing 15.2.3.3-4-13 failing -15.2.3.3-4-51 failing 15.2.3.3-4-9 failing 15.2.3.6-2-17-1 failing 15.2.3.5-4-315 failing -- cgit v1.2.3 From 2af92f495df4c6634022959f6b03b1cd208145d4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 22 Jan 2013 14:53:46 +0100 Subject: Fix bug in property table When inserting a new entry in the property table and we're reusing an entry from the free-list, make sure to assign the name correctly to ensure we calculate the correct bucket according to the _new_ name and not the name from the old re-used property. Also removed unused isEmpty() method that doesn't return the correct thing anyway (_propertyCount is never decreased). Change-Id: Ifc4f2f6b1c5fe975e30bdc81386061d6215ad3e3 Reviewed-by: Lars Knoll --- qv4propertytable.h | 3 +-- tests/TestExpectations | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/qv4propertytable.h b/qv4propertytable.h index 6ee7f92233..3bd47679b5 100644 --- a/qv4propertytable.h +++ b/qv4propertytable.h @@ -87,8 +87,6 @@ public: delete[] _buckets; } - inline bool isEmpty() const { return _propertyCount == 0; } - typedef PropertyTableEntry **iterator; inline iterator begin() const { return _properties; } inline iterator end() const { return _properties + _propertyCount; } @@ -156,6 +154,7 @@ public: PropertyTableEntry *prop; if (_freeList) { prop = _freeList; + prop->name = name; _freeList = _freeList->next; } else { prop = new PropertyTableEntry(name); diff --git a/tests/TestExpectations b/tests/TestExpectations index dfbf08b5d4..fe7f7b8725 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -106,7 +106,6 @@ S11.3.2_A4_T4 failing 11.4.1-5-2 failing S11.4.1_A1 failing S11.4.1_A2.1 failing -S11.4.1_A3.3 failing 11.4.4-2-1-s failing 11.4.4-2-2-s failing 11.4.5-2-1-s failing @@ -499,7 +498,6 @@ S15.1.3.2_A5.3 failing 15.2.3.3-4-13 failing 15.2.3.3-4-9 failing 15.2.3.6-2-17-1 failing -15.2.3.5-4-315 failing 15.2.3.6-4-291-1 failing 15.2.3.6-4-292-1 failing 15.2.3.6-4-293-2 failing @@ -512,7 +510,6 @@ S15.1.3.2_A5.3 failing 15.2.3.6-4-300-1 failing 15.2.3.6-4-360-3 failing 15.2.3.6-4-360-7 failing -15.2.3.7-6-a-110 failing 15.2.3.7-6-a-280 failing 15.2.3.7-6-a-286 failing 15.2.3.7-6-a-288 failing -- cgit v1.2.3 From 2d229fc60ea417f2857366556bdf823fc908b2e8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 22 Jan 2013 12:28:43 +0100 Subject: Fix String.prototype.substring when called with undefined end value Change-Id: Id5c9563d492c5a15881cb4190dc9e8cd20284d31 Reviewed-by: Lars Knoll --- qv4stringobject.cpp | 5 +++-- tests/TestExpectations | 6 +----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/qv4stringobject.cpp b/qv4stringobject.cpp index c2063fc724..178c625596 100644 --- a/qv4stringobject.cpp +++ b/qv4stringobject.cpp @@ -622,8 +622,9 @@ Value StringPrototype::method_substring(ExecutionContext *ctx) if (ctx->argumentCount > 0) start = ctx->argument(0).toInteger(ctx); - if (ctx->argumentCount > 1) - end = ctx->argument(1).toInteger(ctx); + Value endValue = ctx->argument(1); + if (!endValue.isUndefined()) + end = endValue.toInteger(ctx); if (std::isnan(start) || start < 0) start = 0; diff --git a/tests/TestExpectations b/tests/TestExpectations index fe7f7b8725..a5a9f02616 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -542,10 +542,6 @@ S15.4.4.8_A4_T1 failing S15.4.4.8_A4_T2 failing S15.5.4.13_A2_T2 failing S15.5.4.13_A3_T3 failing -S15.5.4.15_A1_T2 failing -S15.5.4.15_A1_T7 failing -S15.5.4.15_A1_T8 failing -S15.5.4.15_A1_T9 failing 15.5.4.20-4-1 failing S15.5.4.8_A1_T4 failing S15.7.4.5_A1.4_T01 failing @@ -730,4 +726,4 @@ S15.4.4.4_A1_T2 failing 15.4.4.21-8-b-iii-1-6 failing 15.12.3_4-1-1 -15.12.3_4-1-3 +15.12.3_4-1-3 \ No newline at end of file -- cgit v1.2.3 From 0ae152e7f909835b825b2bbc9380109a58aeab88 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 22 Jan 2013 13:52:13 +0100 Subject: Fix String.prototype.slice with boundary values The bounding must be done on doubles to be spec compliant. Change-Id: Ic4a0311893680ca3855fe83e5075f65b05a26abf Reviewed-by: Lars Knoll --- qv4stringobject.cpp | 19 +++++++++++-------- tests/TestExpectations | 2 -- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/qv4stringobject.cpp b/qv4stringobject.cpp index 178c625596..7766b10713 100644 --- a/qv4stringobject.cpp +++ b/qv4stringobject.cpp @@ -488,24 +488,27 @@ Value StringPrototype::method_search(ExecutionContext *ctx) Value StringPrototype::method_slice(ExecutionContext *ctx) { const QString text = getThisString(ctx); - const int length = text.length(); + const double length = text.length(); - int start = int (ctx->argument(0).toInteger(ctx)); - int end = ctx->argument(1).isUndefined() - ? length : int (ctx->argument(1).toInteger(ctx)); + double start = ctx->argument(0).toInteger(ctx); + double end = ctx->argument(1).isUndefined() + ? length : ctx->argument(1).toInteger(ctx); if (start < 0) - start = qMax(length + start, 0); + start = qMax(length + start, 0.); else start = qMin(start, length); if (end < 0) - end = qMax(length + end, 0); + end = qMax(length + end, 0.); else end = qMin(end, length); - int count = qMax(0, end - start); - return Value::fromString(ctx, text.mid(start, count)); + const int intStart = int(start); + const int intEnd = int(end); + + int count = qMax(0, intEnd - intStart); + return Value::fromString(ctx, text.mid(intStart, count)); } Value StringPrototype::method_split(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index a5a9f02616..2fd3077bd4 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -540,8 +540,6 @@ S15.4.4.8_A3_T2 failing S15.4.4.8_A3_T3 failing S15.4.4.8_A4_T1 failing S15.4.4.8_A4_T2 failing -S15.5.4.13_A2_T2 failing -S15.5.4.13_A3_T3 failing 15.5.4.20-4-1 failing S15.5.4.8_A1_T4 failing S15.7.4.5_A1.4_T01 failing -- cgit v1.2.3 From 55d54a125e61dd31b7035c492ca8223a04e565f4 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 22 Jan 2013 15:13:15 +0100 Subject: Add URI handling methods Change-Id: Ieee66aa1f95e6676316c4504b4874d307f5b3564 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 9 ++ qmljs_engine.h | 1 + qmljs_environment.cpp | 5 + qmljs_environment.h | 1 + qv4errorobject.cpp | 7 ++ qv4errorobject.h | 1 + qv4globalobject.cpp | 330 +++++++++++++++++++++++++++++++++++++++++++++++-- qv4globalobject.h | 80 +++++------- tests/TestExpectations | 99 +-------------- 9 files changed, 375 insertions(+), 158 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index e3ce0b41aa..0f6823daac 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -224,6 +224,10 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) glo->defineDefaultProperty(rootContext, QStringLiteral("parseFloat"), Value::fromObject(new (memoryManager) ParseFloatFunction(rootContext))); glo->defineDefaultProperty(rootContext, QStringLiteral("isNaN"), Value::fromObject(new (memoryManager) IsNaNFunction(rootContext))); glo->defineDefaultProperty(rootContext, QStringLiteral("isFinite"), Value::fromObject(new (memoryManager) IsFiniteFunction(rootContext))); + glo->defineDefaultProperty(rootContext, QStringLiteral("decodeURI"), Value::fromObject(new (memoryManager) DecodeUriFunction(rootContext))); + glo->defineDefaultProperty(rootContext, QStringLiteral("decodeURIComponent"), Value::fromObject(new (memoryManager) DecodeUriComponentFunction(rootContext))); + glo->defineDefaultProperty(rootContext, QStringLiteral("encodeURI"), Value::fromObject(new (memoryManager) EncodeUriFunction(rootContext))); + glo->defineDefaultProperty(rootContext, QStringLiteral("encodeURIComponent"), Value::fromObject(new (memoryManager) EncodeUriComponentFunction(rootContext))); } ExecutionEngine::~ExecutionEngine() @@ -419,6 +423,11 @@ Object *ExecutionEngine::newRangeErrorObject(ExecutionContext *ctx, const QStrin return new (memoryManager) RangeErrorObject(ctx, message); } +Object *ExecutionEngine::newURIErrorObject(ExecutionContext *ctx, Value message) +{ + return new (memoryManager) URIErrorObject(ctx, message); +} + Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) { MathObject *object = new (memoryManager) MathObject(ctx); diff --git a/qmljs_engine.h b/qmljs_engine.h index fbb6e48134..afc0b3b086 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -211,6 +211,7 @@ struct ExecutionEngine Object *newReferenceErrorObject(ExecutionContext *ctx, const QString &message); Object *newTypeErrorObject(ExecutionContext *ctx, const QString &message); Object *newRangeErrorObject(ExecutionContext *ctx, const QString &message); + Object *newURIErrorObject(ExecutionContext *ctx, Value message); Object *newMathObject(ExecutionContext *ctx); Object *newActivationObject(); diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 89ba600c62..d01b03865d 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -440,6 +440,11 @@ void ExecutionContext::throwRangeError(Value value) throwError(Value::fromObject(engine->newRangeErrorObject(this, msg))); } +void ExecutionContext::throwURIError(Value msg) +{ + throwError(Value::fromObject(engine->newURIErrorObject(this, msg))); +} + void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc) { MemoryManager::GCBlocker blockGC(parent->engine->memoryManager); diff --git a/qmljs_environment.h b/qmljs_environment.h index f043b06da6..b5f6802bf9 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -119,6 +119,7 @@ struct ExecutionContext void throwTypeError(); void throwReferenceError(Value value); void throwRangeError(Value value); + void throwURIError(Value msg); void throwUnimplemented(const QString &message); void setProperty(String *name, Value value); diff --git a/qv4errorobject.cpp b/qv4errorobject.cpp index b5005cb12c..8e10a905ed 100644 --- a/qv4errorobject.cpp +++ b/qv4errorobject.cpp @@ -150,6 +150,13 @@ URIErrorObject::URIErrorObject(ExecutionContext *ctx) prototype = ctx->engine->uRIErrorPrototype; } +URIErrorObject::URIErrorObject(ExecutionContext *ctx, Value msg) + : ErrorObject(ctx->engine, msg) +{ + setNameProperty(ctx); + prototype = ctx->engine->uRIErrorPrototype; +} + ErrorCtor::ErrorCtor(ExecutionContext *scope) : FunctionObject(scope) diff --git a/qv4errorobject.h b/qv4errorobject.h index 66f2676a2a..5c31f3365e 100644 --- a/qv4errorobject.h +++ b/qv4errorobject.h @@ -95,6 +95,7 @@ struct TypeErrorObject: ErrorObject { struct URIErrorObject: ErrorObject { URIErrorObject(ExecutionContext *ctx); + URIErrorObject(ExecutionContext *ctx, Value); virtual QString className() { return QStringLiteral("URIError"); } }; diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index f87ad707dc..f928b0da62 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -40,11 +40,9 @@ ****************************************************************************/ #include "qv4globalobject.h" -#include "qv4ir_p.h" -#include "qv4isel_p.h" -#include "qv4objectproto.h" -#include "qv4stringobject.h" #include "qv4mm.h" +#include "qmljs_value.h" +#include "qmljs_environment.h" #include #include @@ -54,15 +52,244 @@ #include #include "private/qlocale_tools_p.h" -#include #include -#include -#include +#include #include -#include using namespace QQmlJS::VM; +static inline char toHex(char c) +{ + static const char hexnumbers[] = "0123456789ABCDEF"; + return hexnumbers[c & 0xf]; +} + +static int fromHex(QChar ch) +{ + ushort c = ch.unicode(); + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'A') && (c <= 'F')) + return c - 'A' + 10; + if ((c >= 'a') && (c <= 'f')) + return c - 'a' + 10; + return -1; +} + +static QString escape(const QString &input) +{ + QString output; + output.reserve(input.size() * 3); + const int length = input.length(); + for (int i = 0; i < length; ++i) { + ushort uc = input.at(i).unicode(); + if (uc < 0x100) { + if ( (uc > 0x60 && uc < 0x7B) + || (uc > 0x3F && uc < 0x5B) + || (uc > 0x2C && uc < 0x3A) + || (uc == 0x2A) + || (uc == 0x2B) + || (uc == 0x5F)) { + output.append(QChar(uc)); + } else { + output.append('%'); + output.append(QChar(toHex(uc >> 4))); + output.append(QChar(toHex(uc))); + } + } else { + output.append('%'); + output.append('u'); + output.append(QChar(toHex(uc >> 12))); + output.append(QChar(toHex(uc >> 8))); + output.append(QChar(toHex(uc >> 4))); + output.append(QChar(toHex(uc))); + } + } + return output; +} + +static QString unescape(const QString &input) +{ + QString result; + result.reserve(input.length()); + int i = 0; + const int length = input.length(); + while (i < length) { + QChar c = input.at(i++); + if ((c == '%') && (i + 1 < length)) { + QChar a = input.at(i); + if ((a == 'u') && (i + 4 < length)) { + int d3 = fromHex(input.at(i+1)); + int d2 = fromHex(input.at(i+2)); + int d1 = fromHex(input.at(i+3)); + int d0 = fromHex(input.at(i+4)); + if ((d3 != -1) && (d2 != -1) && (d1 != -1) && (d0 != -1)) { + ushort uc = ushort((d3 << 12) | (d2 << 8) | (d1 << 4) | d0); + result.append(QChar(uc)); + i += 5; + } else { + result.append(c); + } + } else { + int d1 = fromHex(a); + int d0 = fromHex(input.at(i+1)); + if ((d1 != -1) && (d0 != -1)) { + c = (d1 << 4) | d0; + i += 2; + } + result.append(c); + } + } else { + result.append(c); + } + } + return result; +} + +static const char uriReserved[] = ";/?:@&=+$,"; +static const char uriUnescaped[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()"; + +static QString encode(const QString &input, const QString &unescapedSet, bool *ok) +{ + *ok = true; + QString output; + const int length = input.length(); + int i = 0; + while (i < length) { + const QChar c = input.at(i); + if (!unescapedSet.contains(c)) { + ushort uc = c.unicode(); + if ((uc >= 0xDC00) && (uc <= 0xDFFF)) { + *ok = false; + break; + } + if (!((uc < 0xD800) || (uc > 0xDBFF))) { + ++i; + if (i == length) { + *ok = false; + break; + } + const ushort uc2 = input.at(i).unicode(); + if ((uc < 0xDC00) || (uc > 0xDFFF)) { + *ok = false; + break; + } + uc = ((uc - 0xD800) * 0x400) + (uc2 - 0xDC00) + 0x10000; + } + QString tmp(1, QChar(uc)); + QByteArray octets = tmp.toUtf8(); + for (int j = 0; j < octets.length(); ++j) { + output.append(QLatin1Char('%')); + output.append(QLatin1Char(toHex(octets.at(j) >> 4))); + output.append(QLatin1Char(toHex(octets.at(j)))); + } + } else { + output.append(c); + } + ++i; + } + if (i != length) + *ok = false; + return output; +} + +static QString decode(const QString &input, const QString &reservedSet, bool *ok) +{ + *ok = true; + QString output; + const int length = input.length(); + int i = 0; + const QChar percent = QLatin1Char('%'); + while (i < length) { + const QChar ch = input.at(i); + if (ch == percent) { + int start = i; + if (i + 2 >= length) + goto error; + + int d1 = fromHex(input.at(i+1)); + int d0 = fromHex(input.at(i+2)); + if ((d1 == -1) || (d0 == -1)) + goto error; + + int b = (d1 << 4) | d0; + i += 2; + if (b & 0x80) { + int uc; + int min_uc; + int need; + if ((b & 0xe0) == 0xc0) { + uc = b & 0x1f; + need = 1; + min_uc = 0x80; + } else if ((b & 0xf0) == 0xe0) { + uc = b & 0x0f; + need = 2; + min_uc = 0x800; + } else if ((b & 0xf8) == 0xf0) { + uc = b & 0x07; + need = 3; + min_uc = 0x10000; + } else { + goto error; + } + + if (i + (3 * need) >= length) + goto error; + + for (int j = 0; j < need; ++j) { + ++i; + if (input.at(i) != percent) + goto error; + + d1 = fromHex(input.at(i+1)); + d0 = fromHex(input.at(i+2)); + if ((d1 == -1) || (d0 == -1)) + goto error; + + b = (d1 << 4) | d0; + if ((b & 0xC0) != 0x80) + goto error; + + i += 2; + uc = (uc << 6) + b; + } + if (uc < min_uc) + goto error; + + if (uc < 0x10000) { + output.append(QChar(uc)); + } else { + if (uc > 0x10FFFF) + goto error; + + ushort l = ushort(((uc - 0x10000) & 0x3FF) + 0xDC00); + ushort h = ushort((((uc - 0x10000) >> 10) & 0x3FF) + 0xD800); + output.append(QChar(l)); + output.append(QChar(h)); + } + } else { + QChar z(b); + if (!reservedSet.contains(z)) { + output.append(z); + } else { + output.append(input.mid(start, i - start + 1)); + } + } + } else { + output.append(ch); + } + ++i; + } + if (i != length) + *ok = false; + return output; + error: + *ok = false; + return QString(); +} + + Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) { @@ -349,3 +576,90 @@ Value IsFiniteFunction::call(ExecutionContext *context, Value /*thisObject*/, Va double d = v.toNumber(context); return Value::fromBoolean(std::isfinite(d)); } + + +/// decodeURI [15.1.3.1] +DecodeUriFunction::DecodeUriFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("decodeURI")); +} + +Value DecodeUriFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +{ + if (argc == 0) + return Value::undefinedValue(); + + QString uriString = args[0].toString(context)->toQString(); + bool ok; + QString out = decode(uriString, QString::fromUtf8(uriReserved) + QString::fromUtf8("#"), &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + +/// decodeURIComponent [15.1.3.2] +DecodeUriComponentFunction::DecodeUriComponentFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("decodeURIComponent")); +} + +Value DecodeUriComponentFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +{ + if (argc == 0) + return Value::undefinedValue(); + + QString uriString = args[0].toString(context)->toQString(); + bool ok; + QString out = decode(uriString, QString(), &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + +/// encodeURI [15.1.3.3] +EncodeUriFunction::EncodeUriFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("encodeURI")); +} + +Value EncodeUriFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +{ + if (argc == 0) + return Value::undefinedValue(); + + QString uriString = args[0].toString(context)->toQString(); + bool ok; + QString out = encode(uriString, QLatin1String(uriReserved) + QLatin1String(uriUnescaped) + QString::fromUtf8("#"), &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + + +/// encodeURIComponent [15.1.3.4] +EncodeUriComponentFunction::EncodeUriComponentFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("encodeURIComponent")); +} + +Value EncodeUriComponentFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +{ + if (argc == 0) + return Value::undefinedValue(); + + QString uriString = args[0].toString(context)->toQString(); + bool ok; + QString out = encode(uriString, QString(), &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + diff --git a/qv4globalobject.h b/qv4globalobject.h index 822ec97326..fbbeac6b6b 100644 --- a/qv4globalobject.h +++ b/qv4globalobject.h @@ -41,64 +41,12 @@ #ifndef QV4GLOBALOBJECT_H #define QV4GLOBALOBJECT_H -#include "qmljs_runtime.h" -#include "qmljs_engine.h" -#include "qmljs_environment.h" #include "qv4functionobject.h" -#include "qv4array.h" -#include "qv4string.h" -#include "qv4codegen_p.h" -#include "qv4isel_p.h" -#include "qv4managed.h" -#include "qv4propertydescriptor.h" -#include "qv4propertytable.h" -#include "qv4objectiterator.h" -#include "qv4regexp.h" - -#include -#include -#include -#include -#include namespace QQmlJS { namespace VM { -struct Value; -struct Function; -struct Object; -struct ObjectIterator; -struct BooleanObject; -struct NumberObject; -struct StringObject; -struct ArrayObject; -struct DateObject; -struct FunctionObject; -struct RegExpObject; -struct ErrorObject; -struct ArgumentsObject; -struct ExecutionContext; -struct ExecutionEngine; -class MemoryManager; - -struct ObjectPrototype; -struct StringPrototype; -struct NumberPrototype; -struct BooleanPrototype; -struct ArrayPrototype; -struct FunctionPrototype; -struct DatePrototype; -struct RegExpPrototype; -struct ErrorPrototype; -struct EvalErrorPrototype; -struct RangeErrorPrototype; -struct ReferenceErrorPrototype; -struct SyntaxErrorPrototype; -struct TypeErrorPrototype; -struct URIErrorPrototype; - - struct EvalFunction : FunctionObject { EvalFunction(ExecutionContext *scope); @@ -139,6 +87,34 @@ struct IsFiniteFunction: FunctionObject virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); }; +struct DecodeUriFunction: FunctionObject +{ + DecodeUriFunction(ExecutionContext *scope); + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); +}; + +struct DecodeUriComponentFunction: FunctionObject +{ + DecodeUriComponentFunction(ExecutionContext *scope); + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); +}; + +struct EncodeUriFunction: FunctionObject +{ + EncodeUriFunction(ExecutionContext *scope); + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); +}; + +struct EncodeUriComponentFunction: FunctionObject +{ + EncodeUriComponentFunction(ExecutionContext *scope); + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); +}; + } // namespace VM } // namespace QQmlJS diff --git a/tests/TestExpectations b/tests/TestExpectations index 2fd3077bd4..5da48051e6 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -3,9 +3,6 @@ S15.1.3.1_A2.5_T1 S15.1.3.2_A2.5_T1 # Tests failing that are supposed to pass. -S10.2.3_A1.1_T2 failing -S10.2.3_A1.2_T2 failing -S10.2.3_A1.3_T2 failing 10.4.2-1-1 failing 10.4.2-1-2 failing 10.4.2-1-3 failing @@ -307,12 +304,6 @@ S15.1.2.5_A2.2 failing S15.1.2.5_A2.3 failing S15.1.2.5_A2.4 failing S15.1.2.5_A2.7 failing -S15.1.3.1_A1.10_T1 failing -S15.1.3.1_A1.11_T1 failing -S15.1.3.1_A1.11_T2 failing -S15.1.3.1_A1.12_T1 failing -S15.1.3.1_A1.12_T2 failing -S15.1.3.1_A1.12_T3 failing S15.1.3.1_A1.13_T1 failing S15.1.3.1_A1.13_T2 failing S15.1.3.1_A1.14_T1 failing @@ -325,21 +316,6 @@ S15.1.3.1_A1.15_T3 failing S15.1.3.1_A1.15_T4 failing S15.1.3.1_A1.15_T5 failing S15.1.3.1_A1.15_T6 failing -S15.1.3.1_A1.1_T1 failing -S15.1.3.1_A1.2_T1 failing -S15.1.3.1_A1.2_T2 failing -S15.1.3.1_A1.3_T1 failing -S15.1.3.1_A1.3_T2 failing -S15.1.3.1_A1.4_T1 failing -S15.1.3.1_A1.5_T1 failing -S15.1.3.1_A1.6_T1 failing -S15.1.3.1_A1.7_T1 failing -S15.1.3.1_A1.8_T1 failing -S15.1.3.1_A1.8_T2 failing -S15.1.3.1_A1.9_T1 failing -S15.1.3.1_A1.9_T2 failing -S15.1.3.1_A1.9_T3 failing -S15.1.3.1_A2.1_T1 failing 15.12.3-11-2 failing 15.12.3-11-26 failing 15.12.3-11-3 failing @@ -372,52 +348,21 @@ S15.1.3.1_A2.1_T1 failing 15.12.3_2-3-a-3 failing 15.12.3_4-1-2 failing S15.1.3.2_A5.4 failing -S15.1.3.2_A5.6 failing S15.1.3.2_A5.7 failing -S15.1.3.2_A6_T1 failing -S15.1.3.3_A1.1_T1 failing -S15.1.3.3_A1.1_T2 failing -S15.1.3.3_A1.2_T1 failing -S15.1.3.3_A1.2_T2 failing -S15.1.3.3_A1.3_T1 failing -S15.1.3.3_A2.1_T1 failing -S15.1.3.3_A2.2_T1 failing -S15.1.3.3_A2.3_T1 failing S15.1.3.3_A2.4_T1 failing S15.1.3.3_A2.4_T2 failing S15.1.3.3_A2.5_T1 failing -S15.1.3.3_A3.1_T1 failing -S15.1.3.3_A3.2_T1 failing -S15.1.3.3_A3.2_T2 failing -S15.1.3.3_A3.2_T3 failing -S15.1.3.3_A3.3_T1 failing -S15.1.3.3_A4_T1 failing -S15.1.3.3_A4_T2 failing -S15.1.3.3_A4_T3 failing -S15.1.3.3_A4_T4 failing S15.1.3.3_A5.1 failing S15.1.3.3_A5.2 failing S15.1.3.3_A5.3 failing S15.1.3.3_A5.4 failing -S15.1.3.3_A5.6 failing S15.1.3.3_A5.7 failing -S15.1.3.3_A6_T1 failing -S15.1.3.4_A1.1_T1 failing -S15.1.3.4_A1.1_T2 failing -S15.1.3.4_A1.2_T1 failing -S15.1.3.4_A1.2_T2 failing -S15.1.3.4_A1.3_T1 failing -S15.1.3.4_A2.1_T1 failing -S15.1.3.4_A2.2_T1 failing -S15.1.3.4_A2.3_T1 failing S15.1.3.4_A2.4_T1 failing S15.1.3.4_A2.4_T2 failing S15.1.3.4_A2.5_T1 failing -S15.1.3.4_A3.1_T1 failing S15.1.3.4_A3.2_T1 failing S15.1.3.4_A3.2_T2 failing S15.1.3.4_A3.2_T3 failing -S15.1.3.4_A3.3_T1 failing S15.1.3.4_A4_T1 failing S15.1.3.4_A4_T2 failing S15.1.3.4_A4_T3 failing @@ -426,32 +371,16 @@ S15.1.3.4_A5.1 failing S15.1.3.4_A5.2 failing S15.1.3.4_A5.3 failing S15.1.3.4_A5.4 failing -S15.1.3.4_A5.6 failing S15.1.3.4_A5.7 failing S15.1.3.4_A6_T1 failing -S15.1.3.1_A2.2_T1 failing S15.1.3.1_A2.3_T1 failing S15.1.3.1_A2.4_T1 failing -S15.1.3.1_A3_T1 failing -S15.1.3.1_A3_T2 failing -S15.1.3.1_A3_T3 failing -S15.1.3.1_A4_T1 failing S15.1.3.1_A4_T2 failing -S15.1.3.1_A4_T3 failing -S15.1.3.1_A4_T4 failing S15.1.3.1_A5.1 failing S15.1.3.1_A5.2 failing S15.1.3.1_A5.3 failing S15.1.3.1_A5.4 failing -S15.1.3.1_A5.6 failing S15.1.3.1_A5.7 failing -S15.1.3.1_A6_T1 failing -S15.1.3.2_A1.10_T1 failing -S15.1.3.2_A1.11_T1 failing -S15.1.3.2_A1.11_T2 failing -S15.1.3.2_A1.12_T1 failing -S15.1.3.2_A1.12_T2 failing -S15.1.3.2_A1.12_T3 failing S15.1.3.2_A1.13_T1 failing S15.1.3.2_A1.13_T2 failing S15.1.3.2_A1.14_T1 failing @@ -464,39 +393,14 @@ S15.1.3.2_A1.15_T3 failing S15.1.3.2_A1.15_T4 failing S15.1.3.2_A1.15_T5 failing S15.1.3.2_A1.15_T6 failing -S15.1.3.2_A1.1_T1 failing -S15.1.3.2_A1.2_T1 failing -S15.1.3.2_A1.2_T2 failing -S15.1.3.2_A1.3_T1 failing -S15.1.3.2_A1.3_T2 failing -S15.1.3.2_A1.4_T1 failing -S15.1.3.2_A1.5_T1 failing -S15.1.3.2_A1.6_T1 failing -S15.1.3.2_A1.7_T1 failing -S15.1.3.2_A1.8_T1 failing -S15.1.3.2_A1.8_T2 failing -S15.1.3.2_A1.9_T1 failing -S15.1.3.2_A1.9_T2 failing -S15.1.3.2_A1.9_T3 failing -S15.1.3.2_A2.1_T1 failing -S15.1.3.2_A2.2_T1 failing S15.1.3.2_A2.3_T1 failing S15.1.3.2_A2.4_T1 failing -S15.1.3.2_A3_T1 failing -S15.1.3.2_A3_T2 failing -S15.1.3.2_A3_T3 failing -S15.1.3.2_A4_T1 failing S15.1.3.2_A4_T2 failing -S15.1.3.2_A4_T3 failing -S15.1.3.2_A4_T4 failing S15.1.3.2_A5.1 failing S15.1.3.2_A5.2 failing S15.1.3.2_A5.3 failing -15.2.3.3-4-10 failing -15.2.3.3-4-11 failing 15.2.3.3-4-12 failing 15.2.3.3-4-13 failing -15.2.3.3-4-9 failing 15.2.3.6-2-17-1 failing 15.2.3.6-4-291-1 failing 15.2.3.6-4-292-1 failing @@ -711,7 +615,6 @@ S15.4.4.4_A1_T2 failing 15.12.3-11-23 failing 15.12.3-11-24 failing 15.12.3-11-25 failing -15.2.3.4-4-1 failing 15.4.4.18-7-c-i-6 failing 15.4.4.19-8-c-i-6 failing 15.4.4.20-9-c-i-6 failing @@ -724,4 +627,4 @@ S15.4.4.4_A1_T2 failing 15.4.4.21-8-b-iii-1-6 failing 15.12.3_4-1-1 -15.12.3_4-1-3 \ No newline at end of file +15.12.3_4-1-3 -- cgit v1.2.3 From 6fb0f5c1b8b4aa36699549d193ff2b5c07033833 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 22 Jan 2013 15:26:32 +0100 Subject: Add escape and unescape to the global object Change-Id: I46353c10a44856c0358e811f5356238721d4a1ec Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 2 ++ qv4globalobject.cpp | 31 +++++++++++++++++++++++++++++++ qv4globalobject.h | 13 +++++++++++++ tests/TestExpectations | 3 --- 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 0f6823daac..23697fb41e 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -228,6 +228,8 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) glo->defineDefaultProperty(rootContext, QStringLiteral("decodeURIComponent"), Value::fromObject(new (memoryManager) DecodeUriComponentFunction(rootContext))); glo->defineDefaultProperty(rootContext, QStringLiteral("encodeURI"), Value::fromObject(new (memoryManager) EncodeUriFunction(rootContext))); glo->defineDefaultProperty(rootContext, QStringLiteral("encodeURIComponent"), Value::fromObject(new (memoryManager) EncodeUriComponentFunction(rootContext))); + glo->defineDefaultProperty(rootContext, QStringLiteral("escape"), Value::fromObject(new (memoryManager) EscapeFunction(rootContext))); + glo->defineDefaultProperty(rootContext, QStringLiteral("unescape"), Value::fromObject(new (memoryManager) UnescapeFunction(rootContext))); } ExecutionEngine::~ExecutionEngine() diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index f928b0da62..a4603ae0e2 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -663,3 +663,34 @@ Value EncodeUriComponentFunction::call(ExecutionContext *context, Value /*thisOb return Value::fromString(context, out); } + +EscapeFunction::EscapeFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("escape")); +} + +Value EscapeFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +{ + if (!argc) + return Value::fromString(context, QStringLiteral("undefined")); + + QString str = args->toString(context)->toQString(); + return Value::fromString(context, escape(str)); +} + + +UnescapeFunction::UnescapeFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->newString(QLatin1String("unescape")); +} + +Value UnescapeFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +{ + if (!argc) + return Value::fromString(context, QStringLiteral("undefined")); + + QString str = args->toString(context)->toQString(); + return Value::fromString(context, unescape(str)); +} diff --git a/qv4globalobject.h b/qv4globalobject.h index fbbeac6b6b..152f4d3764 100644 --- a/qv4globalobject.h +++ b/qv4globalobject.h @@ -115,6 +115,19 @@ struct EncodeUriComponentFunction: FunctionObject virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); }; +struct EscapeFunction: FunctionObject +{ + EscapeFunction(ExecutionContext *scope); + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); +}; + +struct UnescapeFunction: FunctionObject +{ + UnescapeFunction(ExecutionContext *scope); + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); +}; } // namespace VM } // namespace QQmlJS diff --git a/tests/TestExpectations b/tests/TestExpectations index 5da48051e6..dfdd99a445 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -74,7 +74,6 @@ S11.13.2_A4.3_T2.9 failing S11.2.1_A3_T1 failing S11.2.1_A3_T2 failing S11.2.1_A3_T3 failing -S11.2.1_A4_T1 failing S11.2.1_A4_T3 failing 11.2.3-3_3 failing 11.3.1-2-1-s failing @@ -399,8 +398,6 @@ S15.1.3.2_A4_T2 failing S15.1.3.2_A5.1 failing S15.1.3.2_A5.2 failing S15.1.3.2_A5.3 failing -15.2.3.3-4-12 failing -15.2.3.3-4-13 failing 15.2.3.6-2-17-1 failing 15.2.3.6-4-291-1 failing 15.2.3.6-4-292-1 failing -- cgit v1.2.3 From 461c1ef24ea71a3994c931bda8c75c35c66968f5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 22 Jan 2013 15:20:35 +0100 Subject: Fix thisObject for all builtin function calls Even when doing var v = Object.prototype.valueOf; v(); the thisObject must not be converted to the global object automatically, i.e. remain null. We previously implemented this lack of conversion for apply() and call(), but it does in fact apply to all built-in functions. Consequently we can get rid of callDirect and the virtual maybeAdjustThisObject function and instead just do the "tweak" with the help of a little boolean. Change-Id: I93bc37f4c6e896d6dcf169aa74953b0e460312ce Reviewed-by: Lars Knoll --- qv4functionobject.cpp | 34 ++++++++++------------------------ qv4functionobject.h | 4 ---- qv4managed.h | 7 ++++--- tests/TestExpectations | 1 - 4 files changed, 14 insertions(+), 32 deletions(-) diff --git a/qv4functionobject.cpp b/qv4functionobject.cpp index 78948889cf..e5501d8320 100644 --- a/qv4functionobject.cpp +++ b/qv4functionobject.cpp @@ -118,18 +118,13 @@ Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *a ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; ctx->initCallContext(context, thisObject, this, args, argc); - Value result = call(ctx); - ctx->leaveCallContext(); - return result; -} - -Value FunctionObject::callDirect(ExecutionContext *context, Value thisObject, Value *args, int argc) -{ - ExecutionContext k; - ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; - - ctx->initCallContext(context, thisObject, this, args, argc); - maybeAdjustThisObjectForDirectCall(ctx, thisObject); + if (isBuiltinFunction) { + // Built-in functions allow for the this object to be null or undefined. This overrides + // the behaviour of changing thisObject to the global object if null/undefined and allows + // the built-in functions for example to throw a type error if null is passed. + if (thisObject.isNull() || thisObject.isUndefined()) + ctx->thisObject = thisObject; + } Value result = call(ctx); ctx->leaveCallContext(); return result; @@ -244,7 +239,7 @@ Value FunctionPrototype::method_apply(ExecutionContext *ctx) if (!o) ctx->throwTypeError(); - return o->callDirect(ctx, thisArg, args.data(), args.size()); + return o->call(ctx, thisArg, args.data(), args.size()); } Value FunctionPrototype::method_call(ExecutionContext *ctx) @@ -260,7 +255,7 @@ Value FunctionPrototype::method_call(ExecutionContext *ctx) if (!o) ctx->throwTypeError(); - return o->callDirect(ctx, thisArg, args.data(), args.size()); + return o->call(ctx, thisArg, args.data(), args.size()); } Value FunctionPrototype::method_bind(ExecutionContext *ctx) @@ -353,6 +348,7 @@ BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (* , code(code) { this->name = name; + isBuiltinFunction = true; } Value BuiltinFunction::construct(ExecutionContext *ctx) @@ -361,16 +357,6 @@ Value BuiltinFunction::construct(ExecutionContext *ctx) return Value::undefinedValue(); } -void BuiltinFunction::maybeAdjustThisObjectForDirectCall(ExecutionContext *context, Value thisArg) -{ - // Built-in functions allow for the this object to be null or undefined. This overrides - // the behaviour of changing thisObject to the global object if null/undefined and allows - // the built-in functions for example to throw a type error if null is passed. - if (thisArg.isNull() || thisArg.isUndefined()) - context->thisObject = thisArg; -} - - BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) : FunctionObject(scope) , target(target) diff --git a/qv4functionobject.h b/qv4functionobject.h index 727361ff69..912c1038a4 100644 --- a/qv4functionobject.h +++ b/qv4functionobject.h @@ -152,9 +152,6 @@ struct FunctionObject: Object { virtual Value construct(ExecutionContext *context, Value *args, int argc); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); - // Nothing to do in the default implementation, only _native_ functions might change context->thisObject. - virtual void maybeAdjustThisObjectForDirectCall(ExecutionContext* /*context*/, Value /*thisArg*/) { } - Value callDirect(ExecutionContext *context, Value thisObject, Value *args, int argc); virtual struct ScriptFunction *asScriptFunction() { return 0; } @@ -188,7 +185,6 @@ struct BuiltinFunction: FunctionObject { BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); virtual Value call(ExecutionContext *ctx) { return code(ctx); } virtual Value construct(ExecutionContext *ctx); - virtual void maybeAdjustThisObjectForDirectCall(ExecutionContext *context, Value thisArg); }; struct ScriptFunction: FunctionObject { diff --git a/qv4managed.h b/qv4managed.h index a0472cff3f..c7a5959087 100644 --- a/qv4managed.h +++ b/qv4managed.h @@ -64,7 +64,7 @@ private: void operator = (const Managed &other); protected: - Managed() : markBit(0), inUse(1), extensible(true), isArray(false), isString(false), unused(0) { } + Managed() : markBit(0), inUse(1), extensible(true), isArray(false), isString(false), isBuiltinFunction(false), unused(0) { } virtual ~Managed(); public: @@ -82,13 +82,14 @@ protected: quintptr extensible : 1; // used by Object quintptr isArray : 1; // used by Object & Array quintptr isString : 1; // used by Object & StringObject + quintptr isBuiltinFunction : 1; // used by FunctionObject quintptr needsActivation : 1; // used by FunctionObject quintptr usesArgumentsObject : 1; // used by FunctionObject quintptr strictMode : 1; // used by FunctionObject #if CPU(X86_64) - quintptr unused : 56; + quintptr unused : 55; #elif CPU(X86) - quintptr unused : 24; + quintptr unused : 23; #else #error "implement me" #endif diff --git a/tests/TestExpectations b/tests/TestExpectations index dfdd99a445..029387cd27 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -596,7 +596,6 @@ S12.9_A1_T7 failing S12.9_A1_T8 failing S12.9_A1_T9 failing S15.2.4.4_A14 failing -S15.2.4.4_A15 failing # Array regressions S15.4.4.4_A1_T2 failing -- cgit v1.2.3 From 46c2ff414ad69657f5f61fd58ee8546ff2bdd9a9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 22 Jan 2013 16:12:12 +0100 Subject: Fix length property of global utility functions Functions such as parseInt must have a correct length property that is read-only. Change-Id: I91d9a8709c9a2ee23c72388a8737b8761357a285 Reviewed-by: Lars Knoll --- qmljs_engine.cpp | 20 ++++---- qv4globalobject.cpp | 125 +++++++++++-------------------------------------- qv4globalobject.h | 78 +++++------------------------- tests/TestExpectations | 42 +---------------- 4 files changed, 50 insertions(+), 215 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 23697fb41e..c2fe43cb13 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -220,16 +220,16 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) glo->defineDefaultProperty(rootContext, QStringLiteral("eval"), Value::fromObject(new (memoryManager) EvalFunction(rootContext))); - glo->defineDefaultProperty(rootContext, QStringLiteral("parseInt"), Value::fromObject(new (memoryManager) ParseIntFunction(rootContext))); - glo->defineDefaultProperty(rootContext, QStringLiteral("parseFloat"), Value::fromObject(new (memoryManager) ParseFloatFunction(rootContext))); - glo->defineDefaultProperty(rootContext, QStringLiteral("isNaN"), Value::fromObject(new (memoryManager) IsNaNFunction(rootContext))); - glo->defineDefaultProperty(rootContext, QStringLiteral("isFinite"), Value::fromObject(new (memoryManager) IsFiniteFunction(rootContext))); - glo->defineDefaultProperty(rootContext, QStringLiteral("decodeURI"), Value::fromObject(new (memoryManager) DecodeUriFunction(rootContext))); - glo->defineDefaultProperty(rootContext, QStringLiteral("decodeURIComponent"), Value::fromObject(new (memoryManager) DecodeUriComponentFunction(rootContext))); - glo->defineDefaultProperty(rootContext, QStringLiteral("encodeURI"), Value::fromObject(new (memoryManager) EncodeUriFunction(rootContext))); - glo->defineDefaultProperty(rootContext, QStringLiteral("encodeURIComponent"), Value::fromObject(new (memoryManager) EncodeUriComponentFunction(rootContext))); - glo->defineDefaultProperty(rootContext, QStringLiteral("escape"), Value::fromObject(new (memoryManager) EscapeFunction(rootContext))); - glo->defineDefaultProperty(rootContext, QStringLiteral("unescape"), Value::fromObject(new (memoryManager) UnescapeFunction(rootContext))); + glo->defineDefaultProperty(rootContext, QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); + glo->defineDefaultProperty(rootContext, QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("escape"), GlobalFunctions::method_escape, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1); } ExecutionEngine::~ExecutionEngine() diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index a4603ae0e2..5d01d86e01 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -405,13 +405,6 @@ QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct return globalCode; } -// parseInt [15.1.2.2] -ParseIntFunction::ParseIntFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->newString(QLatin1String("parseInt")); -} - static inline int toInt(const QChar &qc, int R) { ushort c = qc.unicode(); @@ -428,12 +421,11 @@ static inline int toInt(const QChar &qc, int R) return -1; } -Value ParseIntFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +// parseInt [15.1.2.2] +Value GlobalFunctions::method_parseInt(ExecutionContext *context) { - Q_UNUSED(thisObject); - - Value string = (argc > 0) ? args[0] : Value::undefinedValue(); - Value radix = (argc > 1) ? args[1] : Value::undefinedValue(); + Value string = context->argument(0); + Value radix = context->argument(1); int R = radix.isUndefined() ? 0 : radix.toInt32(context); // [15.1.2.2] step by step: @@ -509,18 +501,9 @@ Value ParseIntFunction::call(ExecutionContext *context, Value thisObject, Value } // parseFloat [15.1.2.3] -ParseFloatFunction::ParseFloatFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->newString(QLatin1String("parseFloat")); -} - -Value ParseFloatFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +Value GlobalFunctions::method_parseFloat(ExecutionContext *context) { - Q_UNUSED(context); - Q_UNUSED(thisObject); - - Value string = (argc > 0) ? args[0] : Value::undefinedValue(); + Value string = context->argument(0); // [15.1.2.3] step by step: String *inputString = string.toString(context); // 1 @@ -544,15 +527,9 @@ Value ParseFloatFunction::call(ExecutionContext *context, Value thisObject, Valu } /// isNaN [15.1.2.4] -IsNaNFunction::IsNaNFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->newString(QLatin1String("isNaN")); -} - -Value IsNaNFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +Value GlobalFunctions::method_isNaN(ExecutionContext *context) { - const Value &v = (argc > 0) ? args[0] : Value::undefinedValue(); + const Value &v = context->argument(0); if (v.integerCompatible()) return Value::fromBoolean(false); @@ -561,15 +538,9 @@ Value IsNaNFunction::call(ExecutionContext *context, Value /*thisObject*/, Value } /// isFinite [15.1.2.5] -IsFiniteFunction::IsFiniteFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->newString(QLatin1String("isFinite")); -} - -Value IsFiniteFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +Value GlobalFunctions::method_isFinite(ExecutionContext *context) { - const Value &v = (argc > 0) ? args[0] : Value::undefinedValue(); + const Value &v = context->argument(0); if (v.integerCompatible()) return Value::fromBoolean(true); @@ -577,20 +548,13 @@ Value IsFiniteFunction::call(ExecutionContext *context, Value /*thisObject*/, Va return Value::fromBoolean(std::isfinite(d)); } - /// decodeURI [15.1.3.1] -DecodeUriFunction::DecodeUriFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->newString(QLatin1String("decodeURI")); -} - -Value DecodeUriFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +Value GlobalFunctions::method_decodeURI(ExecutionContext *context) { - if (argc == 0) + if (context->argumentCount == 0) return Value::undefinedValue(); - QString uriString = args[0].toString(context)->toQString(); + QString uriString = context->argument(0).toString(context)->toQString(); bool ok; QString out = decode(uriString, QString::fromUtf8(uriReserved) + QString::fromUtf8("#"), &ok); if (!ok) @@ -600,18 +564,12 @@ Value DecodeUriFunction::call(ExecutionContext *context, Value /*thisObject*/, V } /// decodeURIComponent [15.1.3.2] -DecodeUriComponentFunction::DecodeUriComponentFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->newString(QLatin1String("decodeURIComponent")); -} - -Value DecodeUriComponentFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +Value GlobalFunctions::method_decodeURIComponent(ExecutionContext *context) { - if (argc == 0) + if (context->argumentCount == 0) return Value::undefinedValue(); - QString uriString = args[0].toString(context)->toQString(); + QString uriString = context->argument(0).toString(context)->toQString(); bool ok; QString out = decode(uriString, QString(), &ok); if (!ok) @@ -621,18 +579,12 @@ Value DecodeUriComponentFunction::call(ExecutionContext *context, Value /*thisOb } /// encodeURI [15.1.3.3] -EncodeUriFunction::EncodeUriFunction(ExecutionContext *scope) - : FunctionObject(scope) +Value GlobalFunctions::method_encodeURI(ExecutionContext *context) { - name = scope->engine->newString(QLatin1String("encodeURI")); -} - -Value EncodeUriFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) -{ - if (argc == 0) + if (context->argumentCount == 0) return Value::undefinedValue(); - QString uriString = args[0].toString(context)->toQString(); + QString uriString = context->argument(0).toString(context)->toQString(); bool ok; QString out = encode(uriString, QLatin1String(uriReserved) + QLatin1String(uriUnescaped) + QString::fromUtf8("#"), &ok); if (!ok) @@ -641,20 +593,13 @@ Value EncodeUriFunction::call(ExecutionContext *context, Value /*thisObject*/, V return Value::fromString(context, out); } - /// encodeURIComponent [15.1.3.4] -EncodeUriComponentFunction::EncodeUriComponentFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->newString(QLatin1String("encodeURIComponent")); -} - -Value EncodeUriComponentFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +Value GlobalFunctions::method_encodeURIComponent(ExecutionContext *context) { - if (argc == 0) + if (context->argumentCount == 0) return Value::undefinedValue(); - QString uriString = args[0].toString(context)->toQString(); + QString uriString = context->argument(0).toString(context)->toQString(); bool ok; QString out = encode(uriString, QString(), &ok); if (!ok) @@ -663,34 +608,20 @@ Value EncodeUriComponentFunction::call(ExecutionContext *context, Value /*thisOb return Value::fromString(context, out); } - -EscapeFunction::EscapeFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->newString(QLatin1String("escape")); -} - -Value EscapeFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +Value GlobalFunctions::method_escape(ExecutionContext *context) { - if (!argc) + if (!context->argumentCount) return Value::fromString(context, QStringLiteral("undefined")); - QString str = args->toString(context)->toQString(); + QString str = context->argument(0).toString(context)->toQString(); return Value::fromString(context, escape(str)); } - -UnescapeFunction::UnescapeFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->newString(QLatin1String("unescape")); -} - -Value UnescapeFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +Value GlobalFunctions::method_unescape(ExecutionContext *context) { - if (!argc) + if (!context->argumentCount) return Value::fromString(context, QStringLiteral("undefined")); - QString str = args->toString(context)->toQString(); + QString str = context->argument(0).toString(context)->toQString(); return Value::fromString(context, unescape(str)); } diff --git a/qv4globalobject.h b/qv4globalobject.h index 152f4d3764..3ea7b38c56 100644 --- a/qv4globalobject.h +++ b/qv4globalobject.h @@ -59,74 +59,18 @@ struct EvalFunction : FunctionObject virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); }; -struct ParseIntFunction: FunctionObject +struct GlobalFunctions { - ParseIntFunction(ExecutionContext *scope); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); -}; - -struct ParseFloatFunction: FunctionObject -{ - ParseFloatFunction(ExecutionContext *scope); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); -}; - -struct IsNaNFunction: FunctionObject -{ - IsNaNFunction(ExecutionContext *scope); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); -}; - -struct IsFiniteFunction: FunctionObject -{ - IsFiniteFunction(ExecutionContext *scope); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); -}; - -struct DecodeUriFunction: FunctionObject -{ - DecodeUriFunction(ExecutionContext *scope); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); -}; - -struct DecodeUriComponentFunction: FunctionObject -{ - DecodeUriComponentFunction(ExecutionContext *scope); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); -}; - -struct EncodeUriFunction: FunctionObject -{ - EncodeUriFunction(ExecutionContext *scope); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); -}; - -struct EncodeUriComponentFunction: FunctionObject -{ - EncodeUriComponentFunction(ExecutionContext *scope); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); -}; - -struct EscapeFunction: FunctionObject -{ - EscapeFunction(ExecutionContext *scope); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); -}; - -struct UnescapeFunction: FunctionObject -{ - UnescapeFunction(ExecutionContext *scope); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); + static Value method_parseInt(ExecutionContext *context); + static Value method_parseFloat(ExecutionContext *context); + static Value method_isNaN(ExecutionContext *context); + static Value method_isFinite(ExecutionContext *context); + static Value method_decodeURI(ExecutionContext *context); + static Value method_decodeURIComponent(ExecutionContext *context); + static Value method_encodeURI(ExecutionContext *context); + static Value method_encodeURIComponent(ExecutionContext *context); + static Value method_escape(ExecutionContext *context); + static Value method_unescape(ExecutionContext *context); }; } // namespace VM diff --git a/tests/TestExpectations b/tests/TestExpectations index 029387cd27..82e49a5d60 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -269,11 +269,6 @@ S12.7_A7 failing S12.8_A4_T1 failing S12.8_A4_T2 failing S12.8_A4_T3 failing -S15.1.2.2_A9.1 failing -S15.1.2.2_A9.2 failing -S15.1.2.2_A9.3 failing -S15.1.2.2_A9.4 failing -S15.1.2.2_A9.7 failing 15.12.1.1-g6-3 failing 15.12.1.1-g6-4 failing 15.12.1.1-g6-5 failing @@ -288,21 +283,6 @@ S15.12.2_A1 failing 15.12.3-11-13 failing 15.12.3-11-14 failing 15.12.3-11-15 failing -S15.1.2.3_A7.1 failing -S15.1.2.3_A7.2 failing -S15.1.2.3_A7.3 failing -S15.1.2.3_A7.4 failing -S15.1.2.3_A7.7 failing -S15.1.2.4_A2.1 failing -S15.1.2.4_A2.2 failing -S15.1.2.4_A2.3 failing -S15.1.2.4_A2.4 failing -S15.1.2.4_A2.7 failing -S15.1.2.5_A2.1 failing -S15.1.2.5_A2.2 failing -S15.1.2.5_A2.3 failing -S15.1.2.5_A2.4 failing -S15.1.2.5_A2.7 failing S15.1.3.1_A1.13_T1 failing S15.1.3.1_A1.13_T2 failing S15.1.3.1_A1.14_T1 failing @@ -346,16 +326,9 @@ S15.1.3.1_A1.15_T6 failing 15.12.3_2-3-a-2 failing 15.12.3_2-3-a-3 failing 15.12.3_4-1-2 failing -S15.1.3.2_A5.4 failing -S15.1.3.2_A5.7 failing S15.1.3.3_A2.4_T1 failing S15.1.3.3_A2.4_T2 failing S15.1.3.3_A2.5_T1 failing -S15.1.3.3_A5.1 failing -S15.1.3.3_A5.2 failing -S15.1.3.3_A5.3 failing -S15.1.3.3_A5.4 failing -S15.1.3.3_A5.7 failing S15.1.3.4_A2.4_T1 failing S15.1.3.4_A2.4_T2 failing S15.1.3.4_A2.5_T1 failing @@ -366,20 +339,10 @@ S15.1.3.4_A4_T1 failing S15.1.3.4_A4_T2 failing S15.1.3.4_A4_T3 failing S15.1.3.4_A4_T4 failing -S15.1.3.4_A5.1 failing -S15.1.3.4_A5.2 failing -S15.1.3.4_A5.3 failing -S15.1.3.4_A5.4 failing -S15.1.3.4_A5.7 failing S15.1.3.4_A6_T1 failing S15.1.3.1_A2.3_T1 failing S15.1.3.1_A2.4_T1 failing S15.1.3.1_A4_T2 failing -S15.1.3.1_A5.1 failing -S15.1.3.1_A5.2 failing -S15.1.3.1_A5.3 failing -S15.1.3.1_A5.4 failing -S15.1.3.1_A5.7 failing S15.1.3.2_A1.13_T1 failing S15.1.3.2_A1.13_T2 failing S15.1.3.2_A1.14_T1 failing @@ -395,9 +358,6 @@ S15.1.3.2_A1.15_T6 failing S15.1.3.2_A2.3_T1 failing S15.1.3.2_A2.4_T1 failing S15.1.3.2_A4_T2 failing -S15.1.3.2_A5.1 failing -S15.1.3.2_A5.2 failing -S15.1.3.2_A5.3 failing 15.2.3.6-2-17-1 failing 15.2.3.6-4-291-1 failing 15.2.3.6-4-292-1 failing @@ -623,4 +583,4 @@ S15.4.4.4_A1_T2 failing 15.4.4.21-8-b-iii-1-6 failing 15.12.3_4-1-1 -15.12.3_4-1-3 +15.12.3_4-1-3 \ No newline at end of file -- cgit v1.2.3 From 9c4e3acb7c95ec894cd50e7ccd7cada510279fee Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 22 Jan 2013 16:39:16 +0100 Subject: Don't convert the this object twice Pass the converted this object to call, instead of having call maybe convert it once again. Change-Id: I64496548fe62cf42ee582f4eaa0a666945fd57f9 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 38b5ac643d..c0202d9bf3 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -757,7 +757,7 @@ Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, if (!o) context->throwTypeError(); - return o->call(context, that, args, argc); + return o->call(context, thisObject, args, argc); } Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc) -- cgit v1.2.3 From f89a0b12b02bf6d74993e1b750a9fc210c5fa30c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 22 Jan 2013 17:24:25 +0100 Subject: Fix memory corruption in the MM We never free objects ourselves anymore, and the code here would only lead us appending the last object in the free list a second time. Change-Id: I2aa7bd10fbb0990c990d6948124443d222cf82f5 Reviewed-by: Simon Hausmann --- qv4mm.cpp | 8 ++------ tests/TestExpectations | 24 ------------------------ 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/qv4mm.cpp b/qv4mm.cpp index c3e527059f..6521ff55ed 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -202,7 +202,7 @@ std::size_t MemoryManager::sweep() std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t size) { -// qDebug("chunkStart @ %p, size=%x", chunkStart, size); +// qDebug("chunkStart @ %p, size=%x, pos=%x (%x)", chunkStart, size, size>>4, m_d->smallItems[size >> 4]); std::size_t freedCount = 0; Managed **f = &m_d->smallItems[size >> 4]; @@ -219,7 +219,7 @@ std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t if (m->markBit) { m->markBit = 0; } else { -// qDebug() << "-- collecting it." << m << reinterpret_cast(&m->data); +// qDebug() << "-- collecting it." << m << *f << &m->nextFree; m->~Managed(); m->nextFree = *f; @@ -227,10 +227,6 @@ std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t //scribble(m, 0x99, size); ++freedCount; } - } else if (!m->nextFree) { - m->nextFree = *f; - f = &m->nextFree; - ++freedCount; } } diff --git a/tests/TestExpectations b/tests/TestExpectations index 82e49a5d60..d719b5b1f2 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -283,18 +283,6 @@ S15.12.2_A1 failing 15.12.3-11-13 failing 15.12.3-11-14 failing 15.12.3-11-15 failing -S15.1.3.1_A1.13_T1 failing -S15.1.3.1_A1.13_T2 failing -S15.1.3.1_A1.14_T1 failing -S15.1.3.1_A1.14_T2 failing -S15.1.3.1_A1.14_T3 failing -S15.1.3.1_A1.14_T4 failing -S15.1.3.1_A1.15_T1 failing -S15.1.3.1_A1.15_T2 failing -S15.1.3.1_A1.15_T3 failing -S15.1.3.1_A1.15_T4 failing -S15.1.3.1_A1.15_T5 failing -S15.1.3.1_A1.15_T6 failing 15.12.3-11-2 failing 15.12.3-11-26 failing 15.12.3-11-3 failing @@ -343,18 +331,6 @@ S15.1.3.4_A6_T1 failing S15.1.3.1_A2.3_T1 failing S15.1.3.1_A2.4_T1 failing S15.1.3.1_A4_T2 failing -S15.1.3.2_A1.13_T1 failing -S15.1.3.2_A1.13_T2 failing -S15.1.3.2_A1.14_T1 failing -S15.1.3.2_A1.14_T2 failing -S15.1.3.2_A1.14_T3 failing -S15.1.3.2_A1.14_T4 failing -S15.1.3.2_A1.15_T1 failing -S15.1.3.2_A1.15_T2 failing -S15.1.3.2_A1.15_T3 failing -S15.1.3.2_A1.15_T4 failing -S15.1.3.2_A1.15_T5 failing -S15.1.3.2_A1.15_T6 failing S15.1.3.2_A2.3_T1 failing S15.1.3.2_A2.4_T1 failing S15.1.3.2_A4_T2 failing -- cgit v1.2.3 From 656376e789d48b07f545d7bbf8bcd6f980cc278d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 22 Jan 2013 19:25:02 +0100 Subject: Fix remaining issues in the URI methods Change-Id: I0a402fb38f72d067b9bccae1b0c85cfca2cc2812 Reviewed-by: Simon Hausmann --- qv4globalobject.cpp | 39 ++++++++++++++++++++++++++++----------- tests/TestExpectations | 20 -------------------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index 5d01d86e01..059dfe7d60 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -149,6 +149,13 @@ static QString unescape(const QString &input) static const char uriReserved[] = ";/?:@&=+$,"; static const char uriUnescaped[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()"; +static void addEscapeSequence(QString &output, uchar ch) +{ + output.append(QLatin1Char('%')); + output.append(QLatin1Char(toHex(ch >> 4))); + output.append(QLatin1Char(toHex(ch & 0xf))); +} + static QString encode(const QString &input, const QString &unescapedSet, bool *ok) { *ok = true; @@ -158,7 +165,7 @@ static QString encode(const QString &input, const QString &unescapedSet, bool *o while (i < length) { const QChar c = input.at(i); if (!unescapedSet.contains(c)) { - ushort uc = c.unicode(); + uint uc = c.unicode(); if ((uc >= 0xDC00) && (uc <= 0xDFFF)) { *ok = false; break; @@ -169,19 +176,29 @@ static QString encode(const QString &input, const QString &unescapedSet, bool *o *ok = false; break; } - const ushort uc2 = input.at(i).unicode(); - if ((uc < 0xDC00) || (uc > 0xDFFF)) { + const uint uc2 = input.at(i).unicode(); + if ((uc2 < 0xDC00) || (uc2 > 0xDFFF)) { *ok = false; break; } uc = ((uc - 0xD800) * 0x400) + (uc2 - 0xDC00) + 0x10000; } - QString tmp(1, QChar(uc)); - QByteArray octets = tmp.toUtf8(); - for (int j = 0; j < octets.length(); ++j) { - output.append(QLatin1Char('%')); - output.append(QLatin1Char(toHex(octets.at(j) >> 4))); - output.append(QLatin1Char(toHex(octets.at(j)))); + if (uc < 0x80) { + addEscapeSequence(output, (uchar)uc); + } else { + if (uc < 0x0800) { + addEscapeSequence(output, 0xc0 | ((uchar) (uc >> 6))); + } else { + + if (QChar::requiresSurrogates(uc)) { + addEscapeSequence(output, 0xf0 | ((uchar) (uc >> 18))); + addEscapeSequence(output, 0x80 | (((uchar) (uc >> 12)) & 0x3f)); + } else { + addEscapeSequence(output, 0xe0 | (((uchar) (uc >> 12)) & 0x3f)); + } + addEscapeSequence(output, 0x80 | (((uchar) (uc >> 6)) & 0x3f)); + } + addEscapeSequence(output, 0x80 | ((uchar) (uc&0x3f))); } } else { output.append(c); @@ -252,7 +269,7 @@ static QString decode(const QString &input, const QString &reservedSet, bool *ok goto error; i += 2; - uc = (uc << 6) + b; + uc = (uc << 6) + (b & 0x3f); } if (uc < min_uc) goto error; @@ -601,7 +618,7 @@ Value GlobalFunctions::method_encodeURIComponent(ExecutionContext *context) QString uriString = context->argument(0).toString(context)->toQString(); bool ok; - QString out = encode(uriString, QString(), &ok); + QString out = encode(uriString, QString(uriUnescaped), &ok); if (!ok) context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); diff --git a/tests/TestExpectations b/tests/TestExpectations index d719b5b1f2..ab76a420c6 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -314,26 +314,6 @@ S15.12.2_A1 failing 15.12.3_2-3-a-2 failing 15.12.3_2-3-a-3 failing 15.12.3_4-1-2 failing -S15.1.3.3_A2.4_T1 failing -S15.1.3.3_A2.4_T2 failing -S15.1.3.3_A2.5_T1 failing -S15.1.3.4_A2.4_T1 failing -S15.1.3.4_A2.4_T2 failing -S15.1.3.4_A2.5_T1 failing -S15.1.3.4_A3.2_T1 failing -S15.1.3.4_A3.2_T2 failing -S15.1.3.4_A3.2_T3 failing -S15.1.3.4_A4_T1 failing -S15.1.3.4_A4_T2 failing -S15.1.3.4_A4_T3 failing -S15.1.3.4_A4_T4 failing -S15.1.3.4_A6_T1 failing -S15.1.3.1_A2.3_T1 failing -S15.1.3.1_A2.4_T1 failing -S15.1.3.1_A4_T2 failing -S15.1.3.2_A2.3_T1 failing -S15.1.3.2_A2.4_T1 failing -S15.1.3.2_A4_T2 failing 15.2.3.6-2-17-1 failing 15.2.3.6-4-291-1 failing 15.2.3.6-4-292-1 failing -- cgit v1.2.3 From f4b512b84ec3c97222b431de337ac8a1ad272150 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 22 Jan 2013 22:23:59 +0100 Subject: Fix remaining test failures related to Arguments object Change-Id: I294da46457858c14c6d438cfc022d3144d4385d0 Reviewed-by: Simon Hausmann --- qv4argumentsobject.cpp | 61 ++++++++++++++++++++++++++++++++++++++++++++++---- qv4argumentsobject.h | 5 +++++ qv4managed.h | 3 ++- qv4object.cpp | 4 ++++ qv4object.h | 2 +- tests/TestExpectations | 20 +++++------------ 6 files changed, 74 insertions(+), 21 deletions(-) diff --git a/qv4argumentsobject.cpp b/qv4argumentsobject.cpp index 7453b7a817..b803644b18 100644 --- a/qv4argumentsobject.cpp +++ b/qv4argumentsobject.cpp @@ -58,23 +58,75 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterC __defineOwnProperty__(context, QStringLiteral("callee"), &pd); __defineOwnProperty__(context, QStringLiteral("caller"), &pd); } else { - uint enumerableParams = qMin(formalParameterCount, actualParameterCount); - context->engine->requireArgumentsAccessors(enumerableParams); - for (uint i = 0; i < (uint)enumerableParams; ++i) + uint numAccessors = qMin(formalParameterCount, actualParameterCount); + context->engine->requireArgumentsAccessors(numAccessors); + for (uint i = 0; i < (uint)numAccessors; ++i) { + mappedArguments.append(context->argument(i)); __defineOwnProperty__(context, i, &context->engine->argumentsAccessors.at(i)); + } PropertyDescriptor pd; pd.type = PropertyDescriptor::Data; pd.writable = PropertyDescriptor::Enabled; pd.configurable = PropertyDescriptor::Enabled; pd.enumberable = PropertyDescriptor::Enabled; - for (uint i = enumerableParams; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) { + for (uint i = numAccessors; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) { pd.value = context->argument(i); __defineOwnProperty__(context, i, &pd); } defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); + isArgumentsObject = true; + } +} + +bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc) +{ + PropertyDescriptor *pd = array.at(index); + PropertyDescriptor map; + bool isMapped = false; + if (pd && index < (uint)mappedArguments.size()) + isMapped = pd->isAccessor() && pd->get == context->engine->argumentsAccessors.at(index).get; + + if (isMapped) { + map = *pd; + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->configurable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Enabled; + pd->value = mappedArguments.at(index); + } + + isArgumentsObject = false; + bool strict = ctx->strictMode; + ctx->strictMode = false; + bool result = Object::__defineOwnProperty__(ctx, index, desc); + ctx->strictMode = strict; + isArgumentsObject = true; + + if (isMapped && desc->isData()) { + if (desc->type != PropertyDescriptor::Generic) { + Value arg = desc->value; + map.set->call(ctx, Value::fromObject(this), &arg, 1); + } + if (desc->writable != PropertyDescriptor::Disabled) + *pd = map; } + + if (ctx->strictMode && !result) + __qmljs_throw_type_error(ctx); + return result; } +void ArgumentsObject::getCollectables(QVector &objects) +{ + for (int i = 0; i < mappedArguments.size(); ++i) { + Object *o = mappedArguments.at(i).asObject(); + if (o) + objects.append(o); + } + Object::getCollectables(objects); +} + + Value ArgumentsGetterFunction::call(ExecutionContext *ctx, Value thisObject, Value *, int) { Object *that = thisObject.asObject(); @@ -102,5 +154,6 @@ Value ArgumentsSetterFunction::call(ExecutionContext *ctx, Value thisObject, Val return Value::undefinedValue(); } + } } diff --git a/qv4argumentsobject.h b/qv4argumentsobject.h index 90af02f1b8..398f5de147 100644 --- a/qv4argumentsobject.h +++ b/qv4argumentsobject.h @@ -70,9 +70,14 @@ struct ArgumentsSetterFunction: FunctionObject struct ArgumentsObject: Object { ExecutionContext *context; + QVector mappedArguments; ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount); virtual QString className() { return QStringLiteral("Arguments"); } virtual ArgumentsObject *asArgumentsObject() { return this; } + + bool defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); + + virtual void getCollectables(QVector &objects); }; } diff --git a/qv4managed.h b/qv4managed.h index c7a5959087..51aa68ab0c 100644 --- a/qv4managed.h +++ b/qv4managed.h @@ -64,7 +64,7 @@ private: void operator = (const Managed &other); protected: - Managed() : markBit(0), inUse(1), extensible(true), isArray(false), isString(false), isBuiltinFunction(false), unused(0) { } + Managed() : markBit(0), inUse(1), extensible(true), isArray(false), isArgumentsObject(false), isString(false), isBuiltinFunction(false), unused(0) { } virtual ~Managed(); public: @@ -81,6 +81,7 @@ protected: quintptr inUse : 1; quintptr extensible : 1; // used by Object quintptr isArray : 1; // used by Object & Array + quintptr isArgumentsObject : 1; quintptr isString : 1; // used by Object & StringObject quintptr isBuiltinFunction : 1; // used by FunctionObject quintptr needsActivation : 1; // used by FunctionObject diff --git a/qv4object.cpp b/qv4object.cpp index 6c723f1609..5fcb7fca44 100644 --- a/qv4object.cpp +++ b/qv4object.cpp @@ -44,6 +44,7 @@ #include "qv4isel_p.h" #include "qv4objectproto.h" #include "qv4stringobject.h" +#include "qv4argumentsobject.h" #include "qv4mm.h" #include @@ -548,6 +549,9 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Prop if (isArray && index >= array.length() && !array.getLengthProperty()->isWritable()) goto reject; + if (isArgumentsObject) + return static_cast(this)->defineOwnProperty(ctx, index, desc); + // Clause 1 current = __getOwnProperty__(ctx, index); if (!current) { diff --git a/qv4object.h b/qv4object.h index 819b5d7f2a..2747dffc73 100644 --- a/qv4object.h +++ b/qv4object.h @@ -137,7 +137,7 @@ struct Object: Managed { virtual bool __delete__(ExecutionContext *ctx, String *name); virtual bool __delete__(ExecutionContext *ctx, uint index); bool __defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc); - virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc); bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc); diff --git a/tests/TestExpectations b/tests/TestExpectations index ab76a420c6..914d19d4b1 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -2,6 +2,11 @@ S15.1.3.1_A2.5_T1 S15.1.3.2_A2.5_T1 +# wrong tests +# uses octal number +15.2.3.6-2-17-1 failing + + # Tests failing that are supposed to pass. 10.4.2-1-1 failing 10.4.2-1-2 failing @@ -314,23 +319,8 @@ S15.12.2_A1 failing 15.12.3_2-3-a-2 failing 15.12.3_2-3-a-3 failing 15.12.3_4-1-2 failing -15.2.3.6-2-17-1 failing -15.2.3.6-4-291-1 failing -15.2.3.6-4-292-1 failing -15.2.3.6-4-293-2 failing -15.2.3.6-4-293-3 failing -15.2.3.6-4-294-1 failing -15.2.3.6-4-295-1 failing -15.2.3.6-4-296-1 failing -15.2.3.6-4-297-1 failing -15.2.3.6-4-299-1 failing -15.2.3.6-4-300-1 failing 15.2.3.6-4-360-3 failing 15.2.3.6-4-360-7 failing -15.2.3.7-6-a-280 failing -15.2.3.7-6-a-286 failing -15.2.3.7-6-a-288 failing -15.2.3.7-6-a-289 failing 15.4.4.14-9-a-10 failing 15.4.4.14-9-a-17 failing 15.4.4.14-9-a-7 failing -- cgit v1.2.3 From 94d6186445a94fb54e2bac56a600117c790064e5 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 22 Jan 2013 22:43:07 +0100 Subject: Fixed remaining failures for Array.prototype.reverse Change-Id: I688a35f8873012efa3202b18d050542a6ddde196 Reviewed-by: Simon Hausmann --- qv4arrayobject.cpp | 24 +++++++++++++++--------- tests/TestExpectations | 10 ---------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/qv4arrayobject.cpp b/qv4arrayobject.cpp index b740d6d5cd..04a70909d8 100644 --- a/qv4arrayobject.cpp +++ b/qv4arrayobject.cpp @@ -275,19 +275,25 @@ Value ArrayPrototype::method_push(ExecutionContext *ctx) Value ArrayPrototype::method_reverse(ExecutionContext *ctx) { - ArrayObject *instance = ctx->thisObject.asArrayObject(); - if (!instance) - ctx->throwUnimplemented(QStringLiteral("Array.prototype.reverse")); + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint length = getLength(ctx, instance); - int lo = 0, hi = instance->array.length() - 1; + int lo = 0, hi = length - 1; - // ### for (; lo < hi; ++lo, --hi) { - Value tmp = instance->__get__(ctx, lo); - instance->array.set(lo, instance->__get__(ctx, hi)); - instance->array.set(hi, tmp); + bool loExists, hiExists; + Value lval = instance->__get__(ctx, lo, &loExists); + Value hval = instance->__get__(ctx, hi, &hiExists); + if (hiExists) + instance->__put__(ctx, lo, hval); + else + instance->__delete__(ctx, lo); + if (loExists) + instance->__put__(ctx, hi, lval); + else + instance->__delete__(ctx, hi); } - return Value::undefinedValue(); + return Value::fromObject(instance); } Value ArrayPrototype::method_shift(ExecutionContext *ctx) diff --git a/tests/TestExpectations b/tests/TestExpectations index 914d19d4b1..1618263762 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -337,16 +337,6 @@ S15.4.4.4_A2_T1 failing S15.4.4.4_A2_T2 failing S15.4.4.4_A3_T1 failing S15.5.4.11_A5_T1 failing -S15.4.4.8_A1_T1 failing -S15.4.4.8_A1_T2 failing -S15.4.4.8_A2_T1 failing -S15.4.4.8_A2_T2 failing -S15.4.4.8_A2_T3 failing -S15.4.4.8_A3_T1 failing -S15.4.4.8_A3_T2 failing -S15.4.4.8_A3_T3 failing -S15.4.4.8_A4_T1 failing -S15.4.4.8_A4_T2 failing 15.5.4.20-4-1 failing S15.5.4.8_A1_T4 failing S15.7.4.5_A1.4_T01 failing -- cgit v1.2.3 From b9d68476efc9a52384a5ae73d166badc4d88e44f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 Jan 2013 09:37:13 +0100 Subject: Fix test results to match exceptations on i386 Use SSE math instead of i387 to get the expected results for ch08/8.5/S8.5_A2.1 and ch08/8.5/S8.5_A2.2 Change-Id: Ide1a29340c5ea7a786a679ceb4712c033c6e7fd8 Reviewed-by: Lars Knoll --- v4.pro | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/v4.pro b/v4.pro index c379a977bc..b76868e7fe 100644 --- a/v4.pro +++ b/v4.pro @@ -119,6 +119,13 @@ DEFINES += QMLJS_NO_LLVM } +# Use SSE2 floating point math on 32 bit instead of the default +# 387 to make test results pass on 32 and on 64 bit builds. +linux-g++*:isEqual(QT_ARCH,i386) { + QMAKE_CFLAGS += -march=pentium4 -msse2 -mfpmath=sse + QMAKE_CXXFLAGS += -march=pentium4 -msse2 -mfpmath=sse +} + TESTSCRIPT=$$PWD/tests/test262.py V4CMD = $$OUT_PWD/v4 -- cgit v1.2.3 From 224b569aedf1cfb1a53cf29008d67bcc64339b56 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 Jan 2013 09:42:46 +0100 Subject: Fix crash when using continue in switch inside loop Statements like switch() call enterLoop() with a null continue block. However it is permitted to use continue inside a case in switch, and that should then continue in the next outter loop that supports it. Change-Id: I723d2dad3123a2a658964d541522c7961f578dbf Reviewed-by: Lars Knoll --- qv4codegen.cpp | 9 +++++++-- tests/switch.2.js | 11 +++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 tests/switch.2.js diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 41d1d6b1e4..7948ff123f 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1988,7 +1988,12 @@ bool Codegen::visit(ContinueStatement *ast) unwindException(_loop->tryCleanup); if (ast->label.isEmpty()) { - _block->JUMP(_loop->continueBlock); + for (Loop *loop = _loop; loop; loop = loop->parent) { + if (loop->continueBlock) { + _block->JUMP(loop->continueBlock); + return false; + } + } } else { for (Loop *loop = _loop; loop; loop = loop->parent) { if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { @@ -1999,8 +2004,8 @@ bool Codegen::visit(ContinueStatement *ast) return false; } } - throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); } + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); return false; } diff --git a/tests/switch.2.js b/tests/switch.2.js new file mode 100644 index 0000000000..60d59c161b --- /dev/null +++ b/tests/switch.2.js @@ -0,0 +1,11 @@ +var i; +for (i = 0; i < 2; ++i) { + switch ("a") { + case "a": + continue; + default: + break; + } +} +if (i != 2) + print("loop did not run often enough"); -- cgit v1.2.3 From 0095efa64a678e4802097b69eb54937080064c52 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 23 Jan 2013 10:07:18 +0100 Subject: Fix direct vs indirect calls to eval (15.1.2.11) Change-Id: I6f5dc4a1cc538f255c614838821dd2b379ba2832 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 4 +++- qmljs_engine.h | 4 ++++ qmljs_runtime.cpp | 4 ++++ qv4globalobject.cpp | 33 +++++++++++++++++++-------------- qv4globalobject.h | 1 + tests/TestExpectations | 4 ---- 6 files changed, 31 insertions(+), 19 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index c2fe43cb13..2008dbddb4 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -108,6 +108,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) id_value = identifier(QStringLiteral("value")); id_get = identifier(QStringLiteral("get")); id_set = identifier(QStringLiteral("set")); + id_eval = identifier(QStringLiteral("eval")); objectPrototype = new (memoryManager) ObjectPrototype(); stringPrototype = new (memoryManager) StringPrototype(rootContext); @@ -218,7 +219,8 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) glo->defineReadonlyProperty(this, QStringLiteral("NaN"), Value::fromDouble(nan(""))); glo->defineReadonlyProperty(this, QStringLiteral("Infinity"), Value::fromDouble(INFINITY)); - glo->defineDefaultProperty(rootContext, QStringLiteral("eval"), Value::fromObject(new (memoryManager) EvalFunction(rootContext))); + evalFunction = new (memoryManager) EvalFunction(rootContext); + glo->defineDefaultProperty(rootContext, QStringLiteral("eval"), Value::fromObject(evalFunction)); glo->defineDefaultProperty(rootContext, QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); glo->defineDefaultProperty(rootContext, QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); diff --git a/qmljs_engine.h b/qmljs_engine.h index afc0b3b086..2b8b8b5cff 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -90,6 +90,7 @@ struct ReferenceErrorPrototype; struct SyntaxErrorPrototype; struct TypeErrorPrototype; struct URIErrorPrototype; +struct EvalFunction; class RegExp; @@ -137,6 +138,8 @@ struct ExecutionEngine TypeErrorPrototype *typeErrorPrototype; URIErrorPrototype *uRIErrorPrototype; + EvalFunction *evalFunction; + QVector argumentsAccessors; String *id_length; @@ -152,6 +155,7 @@ struct ExecutionEngine String *id_value; String *id_get; String *id_set; + String *id_eval; struct ExceptionHandler { ExecutionContext *context; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index c0202d9bf3..b6750ff5fd 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -44,6 +44,7 @@ #include "qv4object.h" #include "qv4ir_p.h" #include "qv4objectproto.h" +#include "qv4globalobject.h" #include "private/qlocale_tools_p.h" #include @@ -740,6 +741,9 @@ Value __qmljs_call_activation_property(ExecutionContext *context, String *name, Value thisObject = base ? Value::fromObject(base) : Value::undefinedValue(); + if (o == context->engine->evalFunction && name == context->engine->id_eval) + return static_cast(o)->call(context, thisObject, args, argc, true); + return o->call(context, thisObject, args, argc); } diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index 059dfe7d60..3a6b34d3d9 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -307,8 +307,13 @@ static QString decode(const QString &input, const QString &reservedSet, bool *ok } +EvalFunction::EvalFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->id_eval; +} -Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc) +Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc, bool directCall) { if (argc < 1) return Value::undefinedValue(); @@ -316,9 +321,6 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value if (!args[0].isString()) return args[0]; - // ### how to determine this correctly? - bool directCall = true; - const QString code = args[0].stringValue()->toQString(); QQmlJS::VM::Function *f = parseSource(context, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode); if (!f) @@ -326,16 +328,18 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value bool strict = f->isStrict || context->strictMode; - ExecutionContext k, *ctx; + ExecutionContext k; + + ExecutionContext *ctx = context; if (!directCall) { - qDebug() << "!direct"; - // ### - } else if (strict) { + // the context for eval should be the global scope + while (ctx->parent) + ctx = ctx->parent; + } + + if (strict) { ctx = &k; ctx->initCallContext(context, context->thisObject, this, args, argc); - } else { - // use the surrounding context - ctx = context; } // set the correct strict mode flag on the context @@ -352,10 +356,11 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value return result; } -EvalFunction::EvalFunction(ExecutionContext *scope) - : FunctionObject(scope) + +Value EvalFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) { - name = scope->engine->newString(QLatin1String("eval")); + // indirect call + return call(context, thisObject, args, argc, false); } QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, diff --git a/qv4globalobject.h b/qv4globalobject.h index 3ea7b38c56..c335029a6b 100644 --- a/qv4globalobject.h +++ b/qv4globalobject.h @@ -57,6 +57,7 @@ struct EvalFunction : FunctionObject QQmlJS::Codegen::Mode mode); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); + Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool directCall); }; struct GlobalFunctions diff --git a/tests/TestExpectations b/tests/TestExpectations index 1618263762..0eb51ee9b4 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -8,11 +8,7 @@ S15.1.3.2_A2.5_T1 # Tests failing that are supposed to pass. -10.4.2-1-1 failing 10.4.2-1-2 failing -10.4.2-1-3 failing -10.4.2-1-4 failing -10.4.2-1-5 failing 10.4.3-1-104 failing 10.4.3-1-106 failing 10.4.3-1-17-s failing -- cgit v1.2.3 From 5305ec5ac0daa2d41ec308d109b9e55aa26b987b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 23 Jan 2013 10:11:05 +0100 Subject: Split off 2 builtins into their own instruction. The foreach-iterator-object and foreach-next-property-name now have their own interpreter instructions. Also cleaned up the parameter passing helper methods. Change-Id: I3f375ba29e1ae914e707039b157fa011878b88b6 Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 19 +++++++++++++++---- moth/qv4isel_moth.cpp | 33 +++++++++++--------------------- moth/qv4isel_moth_p.h | 1 - moth/qv4vme_moth.cpp | 51 ++++++++++++++++++++++++++++---------------------- 4 files changed, 55 insertions(+), 49 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index e3cc8430e5..bafa9aa80f 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -19,6 +19,8 @@ F(CallValue, callValue) \ F(CallProperty, callProperty) \ F(CallBuiltin, callBuiltin) \ + F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ + F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \ F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ F(CallBuiltinDeleteName, callBuiltinDeleteName) \ @@ -158,13 +160,20 @@ union Instr builtin_create_exception_handler, builtin_delete_exception_handler, builtin_get_exception, - builtin_foreach_iterator_object, - builtin_foreach_next_property_name, builtin_push_with, builtin_pop_with } builtin; - quint32 argc; - quint32 args; + int argTemp; + int targetTempIndex; + }; + struct instr_callBuiltinForeachIteratorObject { + MOTH_INSTR_HEADER + int argTemp; + int targetTempIndex; + }; + struct instr_callBuiltinForeachNextPropertyName { + MOTH_INSTR_HEADER + int argTemp; int targetTempIndex; }; struct instr_callBuiltinDeleteMember { @@ -313,6 +322,8 @@ union Instr instr_callValue callValue; instr_callProperty callProperty; instr_callBuiltin callBuiltin; + instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; + instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName; instr_callBuiltinDeleteMember callBuiltinDeleteMember; instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; instr_callBuiltinDeleteName callBuiltinDeleteName; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index ce890e08fc..1a7c8d1e29 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -608,24 +608,15 @@ void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR: addInstruction(imo); } -void InstructionSelection::prepareCallArg(IR::Expr *e, quint32 &argc, quint32 &args) -{ - IR::ExprList exprs; - exprs.init(e); - prepareCallArgs(&exprs, argc, args); -} - void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 &args) { bool singleArgIsTemp = false; if (e && e->next == 0) { - // ok, only 1 argument in the cal... + // ok, only 1 argument in the call... const int idx = e->expr->asTemp()->index; - if (idx >= 0) { - // not an argument to this function... - // so if it's not a local, we're in: - singleArgIsTemp = idx >= _function->locals.size(); - } + // We can only pass a reference into the stack, which holds temps that + // are not arguments (idx >= 0) nor locals (idx >= localCound). + singleArgIsTemp = idx >= _function->locals.size(); } if (singleArgIsTemp) { @@ -790,7 +781,7 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_throw; - prepareCallArg(arg, call.argc, call.args); + call.argTemp = arg->index; addInstruction(call); } @@ -826,18 +817,17 @@ void InstructionSelection::callBuiltinGetException(IR::Temp *result) void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_foreach_iterator_object; - prepareCallArg(arg, call.argc, call.args); + Instruction::CallBuiltinForeachIteratorObject call; + call.argTemp = arg->index; call.targetTempIndex = result ? result->index : scratchTempIndex(); addInstruction(call); } void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_foreach_next_property_name; - prepareCallArg(arg, call.argc, call.args); + Instruction::CallBuiltinForeachNextPropertyName call; + call.argTemp = arg->index; + qDebug("result index: %d", result ? result->index : -1); call.targetTempIndex = result ? result->index : scratchTempIndex(); addInstruction(call); } @@ -846,8 +836,7 @@ void InstructionSelection::callBuiltinPushWith(IR::Temp *arg) { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_push_with; - prepareCallArg(arg, call.argc, call.args); - assert(call.argc == 1); + call.argTemp = arg->index; addInstruction(call); } diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index e1090645ba..035003336f 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -78,7 +78,6 @@ private: }; void simpleMove(IR::Move *); - void prepareCallArg(IR::Expr *e, quint32 &argc, quint32 &args); void prepareCallArgs(IR::ExprList *, quint32 &, quint32 &); int outgoingArgumentTempStart() const { return _function->tempCount; } diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 27b92a4ceb..f861996a41 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -123,6 +123,9 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co #endif ) { +#ifdef DO_TRACE_INSTR + qDebug("Starting VME with context=%p and code=%p", context, code); +#endif // DO_TRACE_INSTR #ifdef MOTH_THREADED_INTERPRETER if (storeJumpTable) { @@ -165,7 +168,8 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(LoadName) TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); - TEMP(instr.targetTempIndex) = __qmljs_get_activation_property(context, instr.name); + VM::Value val = __qmljs_get_activation_property(context, instr.name); + TEMP(instr.targetTempIndex) = val; MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(StoreName) @@ -214,35 +218,34 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co } } #endif // DO_TRACE_INSTR - quint32 argStart = instr.args - context->variableCount(); + int argStart = instr.args - context->variableCount(); TRACE(Call, "value index = %d, argStart = %d, argc = %d, result temp index = %d", instr.destIndex, argStart, instr.argc, instr.targetTempIndex); VM::Value *args = stack + argStart; TEMP(instr.targetTempIndex) = __qmljs_call_value(context, VM::Value::undefinedValue(), TEMP(instr.destIndex), args, instr.argc); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) - quint32 argStart = instr.args - context->variableCount(); + int argStart = instr.args - context->variableCount(); + // TODO: change this assert everywhere to include a minimum + // TODO: the args calculation is duplicate code, fix that VM::Value *args = stack + argStart; VM::Value base = TEMP(instr.baseTemp); TEMP(instr.targetTempIndex) = __qmljs_call_property(context, base, instr.name, args, instr.argc); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallBuiltin) - quint32 argStart = instr.args - context->variableCount(); - VM::Value *args = stack + argStart; - void *buf; + // TODO: split this into separate instructions switch (instr.builtin) { case Instr::instr_callBuiltin::builtin_throw: TRACE(builtin_throw, "Throwing now...%s", ""); - Q_ASSERT(instr.argc == 1); - __qmljs_builtin_throw(args[0], context); + __qmljs_builtin_throw(TEMP(instr.argTemp), context); break; case Instr::instr_callBuiltin::builtin_rethrow: __qmljs_builtin_rethrow(context); break; case Instr::instr_callBuiltin::builtin_create_exception_handler: { TRACE(builtin_create_exception_handler, "%s", ""); - buf = __qmljs_create_exception_handler(context); + void *buf = __qmljs_create_exception_handler(context); // The targetTempIndex is the only value we need from the instr to // continue execution when an exception is caught. int targetTempIndex = instr.targetTempIndex; @@ -266,17 +269,8 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co case Instr::instr_callBuiltin::builtin_get_exception: TEMP(instr.targetTempIndex) = __qmljs_get_exception(context); break; - case Instr::instr_callBuiltin::builtin_foreach_iterator_object: - Q_ASSERT(instr.argc == 1); - TEMP(instr.targetTempIndex) = __qmljs_foreach_iterator_object(args[0], context); - break; - case Instr::instr_callBuiltin::builtin_foreach_next_property_name: - Q_ASSERT(instr.argc == 1); - TEMP(instr.targetTempIndex) = __qmljs_foreach_next_property_name(args[0]); - break; case Instr::instr_callBuiltin::builtin_push_with: - Q_ASSERT(instr.argc == 1); - __qmljs_builtin_push_with(args[0], context); + __qmljs_builtin_push_with(TEMP(instr.argTemp), context); break; case Instr::instr_callBuiltin::builtin_pop_with: __qmljs_builtin_pop_with(context); @@ -287,6 +281,18 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co } MOTH_END_INSTR(CallBuiltin) + MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) + VM::Value &obj = TEMP(instr.argTemp); + VM::Value it = __qmljs_foreach_iterator_object(obj, context); + TEMP(instr.targetTempIndex) = it; + MOTH_END_INSTR(CallBuiltinForeachIteratorObject) + + MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) + VM::Value &iter = TEMP(instr.argTemp); + VM::Value val = __qmljs_foreach_next_property_name(iter); + TEMP(instr.targetTempIndex) = val; + MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) + MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) TEMP(instr.targetTempIndex) = __qmljs_delete_member(context, TEMP(instr.base), instr.member); MOTH_END_INSTR(CallBuiltinDeleteMember) @@ -328,20 +334,20 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinDefineProperty) MOTH_BEGIN_INSTR(CreateValue) - quint32 argStart = instr.args - context->variableCount(); + int argStart = instr.args - context->variableCount(); VM::Value *args = stack + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_value(context, TEMP(instr.func), args, instr.argc); MOTH_END_INSTR(CreateValue) MOTH_BEGIN_INSTR(CreateProperty) - quint32 argStart = instr.args - context->variableCount(); + int argStart = instr.args - context->variableCount(); VM::Value *args = stack + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_property(context, TEMP(instr.base), instr.name, args, instr.argc); MOTH_END_INSTR(CreateProperty) MOTH_BEGIN_INSTR(CreateActivationProperty) TRACE(inline, "property name = %s, argc = %d", instr.name->toQString().toUtf8().constData(), instr.argc); - quint32 argStart = instr.args - context->variableCount(); + int argStart = instr.args - context->variableCount(); VM::Value *args = stack + argStart; TEMP(instr.targetTempIndex) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); MOTH_END_INSTR(CreateActivationProperty) @@ -392,6 +398,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(InplaceMemberOp) MOTH_BEGIN_INSTR(InplaceNameOp) + TRACE(name, "%s", instr.targetName->toQString().toUtf8().constData()); VM::Value source = instr.sourceIsTemp ? TEMP(instr.source.tempIndex) : instr.source.value; instr.alu(source, instr.targetName, -- cgit v1.2.3 From 9c2327d25cb6b28ddf9c0e83f57cab129b9a446b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 23 Jan 2013 10:12:51 +0100 Subject: Fix eval when a context gets inherited. When a context gets inherited, the locals have to be passed into the CodeGen class, so the indexes in the caller and callee match up. Change-Id: I21f496e8355a5dd7f13c06e011ede60fc04ccba0 Reviewed-by: Lars Knoll --- main.cpp | 4 ++-- qmljs_environment.cpp | 2 +- qv4codegen.cpp | 19 ++++++++++++++++--- qv4codegen_p.h | 10 +++++++--- qv4globalobject.cpp | 29 +++++++++++++++++++---------- qv4globalobject.h | 3 ++- tests/TestExpectations | 1 - 7 files changed, 47 insertions(+), 21 deletions(-) diff --git a/main.cpp b/main.cpp index 3a7911e997..af14fb741b 100644 --- a/main.cpp +++ b/main.cpp @@ -395,7 +395,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode); + QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode, /*inheritContext =*/ false); if (!f) continue; @@ -420,5 +420,5 @@ int main(int argc, char *argv[]) vm.memoryManager->dumpStats(); } return EXIT_SUCCESS; - } + } // switch (mode) } diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index d01b03865d..9d81bb7ce9 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -476,7 +476,7 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha } locals = function->varCount ? new Value[function->varCount] : 0; - if (function->varCount) + if (locals) std::fill(locals, locals + function->varCount, Value::undefinedValue()); activation = 0; diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 7948ff123f..7d530ffdfe 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -438,7 +438,9 @@ Codegen::Codegen(ErrorHandler *errorHandler, bool strictMode) { } -IR::Function *Codegen::operator()(const QString &fileName, Program *node, IR::Module *module, Mode mode) +IR::Function *Codegen::operator()(const QString &fileName, Program *node, + IR::Module *module, Mode mode, + const QStringList &inheritedLocals) { assert(node); @@ -449,7 +451,8 @@ IR::Function *Codegen::operator()(const QString &fileName, Program *node, IR::Mo ScanFunctions scan(this); scan(node); - IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, node->elements, mode); + IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, + node->elements, mode, inheritedLocals); if (_debugger) { if (node->elements->element) { SourceLocation loc = node->elements->element->firstSourceLocation(); @@ -1815,7 +1818,8 @@ void Codegen::linearize(IR::Function *function) IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, - AST::SourceElements *body, Mode mode) + AST::SourceElements *body, Mode mode, + const QStringList &inheritedLocals) { qSwap(_mode, mode); // enter function code. @@ -1843,6 +1847,15 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, (*it).index = t; } } else { + if (!_env->isStrict) { + foreach (const QString &inheritedLocal, inheritedLocals) { + function->LOCAL(inheritedLocal); + unsigned tempIndex = entryBlock->newTemp(); + Environment::Member member = { Environment::UndefinedMember, tempIndex, 0 }; + _env->members.insert(inheritedLocal, member); + } + } + IR::ExprList *args = 0; for (Environment::MemberMap::const_iterator it = _env->members.constBegin(); it != _env->members.constEnd(); ++it) { const QString &local = it.key(); diff --git a/qv4codegen_p.h b/qv4codegen_p.h index f84c542f76..8a83908dd1 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -43,6 +43,7 @@ #include "qv4ir_p.h" #include +#include #include namespace QQmlJS { @@ -78,7 +79,7 @@ public: FunctionCode }; - IR::Function *operator()(const QString &fileName, AST::Program *ast, IR::Module *module, Mode mode = GlobalCode); + IR::Function *operator()(const QString &fileName, AST::Program *ast, IR::Module *module, Mode mode = GlobalCode, const QStringList &inheritedLocals = QStringList()); IR::Function *operator()(const QString &fileName, AST::FunctionExpression *ast, IR::Module *module); protected: @@ -252,8 +253,11 @@ protected: void cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse); void linearize(IR::Function *function); - IR::Function *defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, - AST::SourceElements *body, Mode mode = FunctionCode); + IR::Function *defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body, + Mode mode = FunctionCode, + const QStringList &inheritedLocals = QStringList()); int indexOfArgument(const QStringRef &string) const; void unwindException(TryCleanup *outest); diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index 3a6b34d3d9..1857eee40b 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -318,11 +318,21 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value if (argc < 1) return Value::undefinedValue(); + ExecutionContext *ctx = context; + if (!directCall) { + // the context for eval should be the global scope + while (ctx->parent) + ctx = ctx->parent; + } + if (!args[0].isString()) return args[0]; const QString code = args[0].stringValue()->toQString(); - QQmlJS::VM::Function *f = parseSource(context, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode); + bool inheritContext = !context->strictMode; + QQmlJS::VM::Function *f = parseSource(context, QStringLiteral("eval code"), + code, QQmlJS::Codegen::EvalCode, + inheritContext); if (!f) return Value::undefinedValue(); @@ -330,13 +340,6 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value ExecutionContext k; - ExecutionContext *ctx = context; - if (!directCall) { - // the context for eval should be the global scope - while (ctx->parent) - ctx = ctx->parent; - } - if (strict) { ctx = &k; ctx->initCallContext(context, context->thisObject, this, args, argc); @@ -365,7 +368,8 @@ Value EvalFunction::call(ExecutionContext *context, Value thisObject, Value *arg QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, const QString &source, - QQmlJS::Codegen::Mode mode) + QQmlJS::Codegen::Mode mode, + bool inheritContext) { using namespace QQmlJS; @@ -412,8 +416,13 @@ QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct return 0; } + QStringList inheritedLocals; + if (inheritContext) + for (String **i = ctx->variables(), **ei = i + ctx->variableCount(); i < ei; ++i) + inheritedLocals.append(*i ? (*i)->toQString() : QString()); + Codegen cg(ctx); - IR::Function *globalIRCode = cg(fileName, program, &module, mode); + IR::Function *globalIRCode = cg(fileName, program, &module, mode, inheritedLocals); QScopedPointer isel(ctx->engine->iselFactory->create(vm, &module)); if (globalIRCode) globalCode = isel->vmFunction(globalIRCode); diff --git a/qv4globalobject.h b/qv4globalobject.h index c335029a6b..84668cf719 100644 --- a/qv4globalobject.h +++ b/qv4globalobject.h @@ -54,7 +54,8 @@ struct EvalFunction : FunctionObject static QQmlJS::VM::Function *parseSource(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, const QString &source, - QQmlJS::Codegen::Mode mode); + QQmlJS::Codegen::Mode mode, + bool inheritContext); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool directCall); diff --git a/tests/TestExpectations b/tests/TestExpectations index 0eb51ee9b4..b0a00e6518 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -99,7 +99,6 @@ S11.3.2_A4_T4 failing 11.4.1-2-5 failing 11.4.1-2-6 failing 11.4.1-4.a-5 failing -11.4.1-4.a-7 failing 11.4.1-5-2 failing S11.4.1_A1 failing S11.4.1_A2.1 failing -- cgit v1.2.3 From cdbfd5e9303cd84342a08333e19137fe814bc851 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 23 Jan 2013 10:22:52 +0100 Subject: Method signature changes to remove duplicate logic. Change-Id: Ib4a17dd90629a71f1ee2daa442e49d2237b1073d Reviewed-by: Lars Knoll --- moth/qv4isel_moth.cpp | 38 ++++++++++++++------------------------ moth/qv4isel_moth_p.h | 8 ++++---- qv4isel_llvm.cpp | 8 ++++---- qv4isel_llvm_p.h | 8 ++++---- qv4isel_masm.cpp | 42 ++++++++++++++++++------------------------ qv4isel_masm_p.h | 8 ++++---- qv4isel_p.cpp | 24 ++++++++++++------------ qv4isel_p.h | 8 ++++---- 8 files changed, 64 insertions(+), 80 deletions(-) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 1a7c8d1e29..aef7d41da4 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -329,28 +329,22 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) delete[] codeStart; } -void InstructionSelection::callValue(IR::Call *c, IR::Temp *result) +void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) { - IR::Temp *t = c->base->asTemp(); - Q_ASSERT(t); - Instruction::CallValue call; - prepareCallArgs(c->args, call.argc, call.args); - call.destIndex = t->index; + prepareCallArgs(args, call.argc, call.args); + call.destIndex = value->index; call.targetTempIndex = result ? result->index : scratchTempIndex(); addInstruction(call); } -void InstructionSelection::callProperty(IR::Call *c, IR::Temp *result) +void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) { - IR::Member *m = c->base->asMember(); - Q_ASSERT(m); - // call the property on the loaded base Instruction::CallProperty call; - call.baseTemp = m->base->asTemp()->index; - call.name = engine()->newString(*m->name); - prepareCallArgs(c->args, call.argc, call.args); + call.baseTemp = base->index; + call.name = engine()->newString(name); + prepareCallArgs(args, call.argc, call.args); call.targetTempIndex = result ? result->index : scratchTempIndex(); addInstruction(call); } @@ -366,25 +360,21 @@ void InstructionSelection::constructActivationProperty(IR::Name *func, addInstruction(create); } -void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) +void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) { - IR::Member *member = call->base->asMember(); - assert(member != 0); - assert(member->base->asTemp() != 0); - Instruction::CreateProperty create; - create.base = member->base->asTemp()->index; - create.name = engine()->newString(*member->name); - prepareCallArgs(call->args, create.argc, create.args); + create.base = base->index; + create.name = engine()->newString(name); + prepareCallArgs(args, create.argc, create.args); create.targetTempIndex = result->index; addInstruction(create); } -void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) +void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) { Instruction::CreateValue create; - create.func = call->base->asTemp()->index; - prepareCallArgs(call->args, create.argc, create.args); + create.func = value->index; + prepareCallArgs(args, create.argc, create.args); create.targetTempIndex = result->index; addInstruction(create); } diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 035003336f..6cac936534 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -45,11 +45,11 @@ protected: virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); - virtual void callValue(IR::Call *c, IR::Temp *result); - virtual void callProperty(IR::Call *c, IR::Temp *result); + virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); + virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); - virtual void constructProperty(IR::New *call, IR::Temp *result); - virtual void constructValue(IR::New *call, IR::Temp *result); + virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); virtual void loadThisObject(IR::Temp *temp); virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp); virtual void loadString(const QString &str, IR::Temp *targetTemp); diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index a4a20a5cd8..79458be365 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -448,14 +448,14 @@ void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QSt Q_UNREACHABLE(); } -void InstructionSelection::callValue(IR::Call *c, IR::Temp *temp) +void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) { // TODO assert(!"TODO!"); Q_UNREACHABLE(); } -void InstructionSelection::callProperty(IR::Call *c, IR::Temp *temp) +void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) { // TODO assert(!"TODO!"); @@ -471,14 +471,14 @@ void InstructionSelection::constructActivationProperty(IR::Name *func, Q_UNREACHABLE(); } -void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) +void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) { // TODO assert(!"TODO!"); Q_UNREACHABLE(); } -void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) +void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) { // TODO assert(!"TODO!"); diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 777bfcd460..78e54b680b 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -91,11 +91,11 @@ public: // methods from InstructionSelection: virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); - virtual void callValue(IR::Call *c, IR::Temp *temp); - virtual void callProperty(IR::Call *c, IR::Temp *temp); + virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); + virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); - virtual void constructProperty(IR::New *call, IR::Temp *result); - virtual void constructValue(IR::New *call, IR::Temp *result); + virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); virtual void loadThisObject(IR::Temp *temp); virtual void loadConst(IR::Const *con, IR::Temp *temp); virtual void loadString(const QString &str, IR::Temp *targetTemp); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 085706b57e..3aead74731 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -543,14 +543,11 @@ void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QSt object, identifier(name), value, Assembler::ContextRegister); } -void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) +void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) { - IR::Temp *baseTemp = call->base->asTemp(); - assert(baseTemp != 0); - - int argc = prepareVariableArguments(call->args); + int argc = prepareVariableArguments(args); IR::Temp* thisObject = 0; - generateFunctionCall(result, __qmljs_call_value, Assembler::ContextRegister, thisObject, baseTemp, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); + generateFunctionCall(result, __qmljs_call_value, Assembler::ContextRegister, thisObject, value, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } void InstructionSelection::loadThisObject(IR::Temp *temp) @@ -726,14 +723,16 @@ void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR: } } -void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) +void InstructionSelection::callProperty(IR::Temp *base, const QString &name, + IR::ExprList *args, IR::Temp *result) { - IR::Member *member = call->base->asMember(); - assert(member != 0); - assert(member->base->asTemp() != 0); + assert(base != 0); - int argc = prepareVariableArguments(call->args); - generateFunctionCall(result, __qmljs_call_property, Assembler::ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); + int argc = prepareVariableArguments(args); + generateFunctionCall(result, __qmljs_call_property, + Assembler::ContextRegister, base, identifier(name), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); } String *InstructionSelection::identifier(const QString &s) @@ -748,23 +747,18 @@ void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprL callRuntimeMethod(result, __qmljs_construct_activation_property, func, args); } -void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) +void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) { - IR::Member *member = call->base->asMember(); - assert(member != 0); - assert(member->base->asTemp() != 0); - - int argc = prepareVariableArguments(call->args); - generateFunctionCall(result, __qmljs_construct_property, Assembler::ContextRegister, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); + int argc = prepareVariableArguments(args); + generateFunctionCall(result, __qmljs_construct_property, Assembler::ContextRegister, base, identifier(name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } -void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) +void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) { - IR::Temp *baseTemp = call->base->asTemp(); - assert(baseTemp != 0); + assert(value != 0); - int argc = prepareVariableArguments(call->args); - generateFunctionCall(result, __qmljs_construct_value, Assembler::ContextRegister, baseTemp, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); + int argc = prepareVariableArguments(args); + generateFunctionCall(result, __qmljs_construct_value, Assembler::ContextRegister, value, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } void InstructionSelection::visitJump(IR::Jump *s) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 192439bea6..3d18036d19 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -673,8 +673,8 @@ protected: virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); - virtual void callProperty(IR::Call *call, IR::Temp *result); - virtual void callValue(IR::Call *call, IR::Temp *result); + virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); virtual void loadThisObject(IR::Temp *temp); virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp); virtual void loadString(const QString &str, IR::Temp *targetTemp); @@ -723,8 +723,8 @@ protected: VM::String *identifier(const QString &s); virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); - virtual void constructProperty(IR::New *ctor, IR::Temp *result); - virtual void constructValue(IR::New *call, IR::Temp *result); + virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); virtual void visitJump(IR::Jump *); virtual void visitCJump(IR::CJump *); diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 28c35b0109..9a694ae9a1 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -102,11 +102,11 @@ void InstructionSelection::visitMove(IR::Move *s) if (Name *func = ctor->base->asName()) { constructActivationProperty(func, ctor->args, t); return; - } else if (ctor->base->asMember()) { - constructProperty(ctor, t); + } else if (IR::Member *member = ctor->base->asMember()) { + constructProperty(member->base->asTemp(), *member->name, ctor->args, t); return; - } else if (ctor->base->asTemp()) { - constructValue(ctor, t); + } else if (IR::Temp *value = ctor->base->asTemp()) { + constructValue(value, ctor->args, t); return; } } else if (IR::Member *m = s->source->asMember()) { @@ -132,11 +132,11 @@ void InstructionSelection::visitMove(IR::Move *s) if (c->base->asName()) { callBuiltin(c, t); return; - } else if (c->base->asMember()) { - callProperty(c, t); + } else if (Member *member = c->base->asMember()) { + callProperty(member->base, *member->name, c->args, t); return; - } else if (c->base->asTemp()) { - callValue(c, t); + } else if (IR::Temp *value = c->base->asTemp()) { + callValue(value, c->args, t); return; } } @@ -206,10 +206,10 @@ void InstructionSelection::visitExp(IR::Exp *s) // These are calls where the result is ignored. if (c->base->asName()) { callBuiltin(c, 0); - } else if (c->base->asTemp()) { - callValue(c, 0); - } else if (c->base->asMember()) { - callProperty(c, 0); + } else if (Temp *value = c->base->asTemp()) { + callValue(value, c->args, 0); + } else if (Member *member = c->base->asMember()) { + callProperty(member->base, *member->name, c->args, 0); } else { Q_UNIMPLEMENTED(); } diff --git a/qv4isel_p.h b/qv4isel_p.h index ca5eb69d66..8181853bd6 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -101,11 +101,11 @@ public: // to implement by subclasses: virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0; virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) = 0; virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) = 0; - virtual void callValue(IR::Call *c, IR::Temp *temp) = 0; - virtual void callProperty(IR::Call *c, IR::Temp *temp) = 0; + virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) = 0; + virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) = 0; virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result) = 0; - virtual void constructProperty(IR::New *ctor, IR::Temp *result) = 0; - virtual void constructValue(IR::New *call, IR::Temp *result) = 0; + virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) = 0; + virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) = 0; virtual void loadThisObject(IR::Temp *temp) = 0; virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) = 0; virtual void loadString(const QString &str, IR::Temp *targetTemp) = 0; -- cgit v1.2.3 From 7b6d356d07facd3bbd16b3a95aaef3b2e52860c9 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 23 Jan 2013 11:04:48 +0100 Subject: Fix compiler warnings. The second call method in EvalFunction was hiding an overloaded virtual function, hence the name change. Change-Id: Ic333d1e7359df97ff25dace87c80b40a939a4cea Reviewed-by: Lars Knoll --- qmljs_runtime.cpp | 2 +- qv4dateobject.cpp | 1 + qv4globalobject.cpp | 4 ++-- qv4globalobject.h | 2 +- qv4jsonobject.cpp | 1 + 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index b6750ff5fd..dddf89a0ca 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -742,7 +742,7 @@ Value __qmljs_call_activation_property(ExecutionContext *context, String *name, Value thisObject = base ? Value::fromObject(base) : Value::undefinedValue(); if (o == context->engine->evalFunction && name == context->engine->id_eval) - return static_cast(o)->call(context, thisObject, args, argc, true); + return static_cast(o)->evalCall(context, thisObject, args, argc, true); return o->call(context, thisObject, args, argc); } diff --git a/qv4dateobject.cpp b/qv4dateobject.cpp index a6e5119f67..3038e7205f 100644 --- a/qv4dateobject.cpp +++ b/qv4dateobject.cpp @@ -802,6 +802,7 @@ Value DatePrototype::method_UTC(ExecutionContext *ctx) Value DatePrototype::method_now(ExecutionContext *ctx) { + Q_UNUSED(ctx); double t = currentTime(); return Value::fromDouble(t); } diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index 1857eee40b..67c1759cd4 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -313,7 +313,7 @@ EvalFunction::EvalFunction(ExecutionContext *scope) name = scope->engine->id_eval; } -Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc, bool directCall) +Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc, bool directCall) { if (argc < 1) return Value::undefinedValue(); @@ -363,7 +363,7 @@ Value EvalFunction::call(ExecutionContext *context, Value /*thisObject*/, Value Value EvalFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) { // indirect call - return call(context, thisObject, args, argc, false); + return evalCall(context, thisObject, args, argc, false); } QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, diff --git a/qv4globalobject.h b/qv4globalobject.h index 84668cf719..20722274d7 100644 --- a/qv4globalobject.h +++ b/qv4globalobject.h @@ -58,7 +58,7 @@ struct EvalFunction : FunctionObject bool inheritContext); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); - Value call(ExecutionContext *context, Value thisObject, Value *args, int argc, bool directCall); + Value evalCall(ExecutionContext *context, Value thisObject, Value *args, int argc, bool directCall); }; struct GlobalFunctions diff --git a/qv4jsonobject.cpp b/qv4jsonobject.cpp index 77645d20e8..d8e793e830 100644 --- a/qv4jsonobject.cpp +++ b/qv4jsonobject.cpp @@ -103,6 +103,7 @@ Value JsonObject::method_parse(ExecutionContext *ctx) Value JsonObject::method_stringify(ExecutionContext *ctx) { + Q_UNUSED(ctx); assert(!"Not implemented"); } -- cgit v1.2.3 From 10b68524f674df1c8f447f41beae35edafb8fed9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 23 Jan 2013 10:39:58 +0100 Subject: Refactor JSON.parse Use a modified copy of the parser in QtCore to get all test cases to pass and to be a lot faster. In addition, this will later on to add support for the second argument to JSON.parse Change-Id: Iaa4cbda29db9c53c3dd5ab2b2ded71efdaa6f8ee Reviewed-by: Simon Hausmann --- qv4jsonobject.cpp | 658 +++++++++++++++++++++++++++++++++++++++++++------ qv4jsonobject.h | 5 - tests/TestExpectations | 7 +- 3 files changed, 581 insertions(+), 89 deletions(-) diff --git a/qv4jsonobject.cpp b/qv4jsonobject.cpp index d8e793e830..6c0b02ff88 100644 --- a/qv4jsonobject.cpp +++ b/qv4jsonobject.cpp @@ -48,123 +48,625 @@ namespace QQmlJS { namespace VM { -JsonObject::JsonObject(ExecutionContext *context) - : Object() +//#define PARSER_DEBUG +#ifdef PARSER_DEBUG +static int indent = 0; +#define BEGIN qDebug() << QByteArray(4*indent++, ' ').constData() +#define END --indent +#define DEBUG qDebug() << QByteArray(4*indent, ' ').constData() +#else +#define BEGIN if (1) ; else qDebug() +#define END do {} while (0) +#define DEBUG if (1) ; else qDebug() +#endif + + +class Parser { - prototype = context->engine->objectPrototype; +public: + Parser(ExecutionContext *context, const QChar *json, int length); - defineDefaultProperty(context, QStringLiteral("parse"), method_parse, 2); - defineDefaultProperty(context, QStringLiteral("stringify"), method_stringify); -} + Value parse(QJsonParseError *error); +private: + inline bool eatSpace(); + inline QChar nextToken(); -Value JsonObject::method_parse(ExecutionContext *ctx) + Value parseObject(); + Value parseArray(); + bool parseMember(Object *o); + bool parseString(QString *string); + bool parseValue(Value *val); + bool parseNumber(Value *val); + + ExecutionContext *context; + const QChar *head; + const QChar *json; + const QChar *end; + + int nestingLevel; + QJsonParseError::ParseError lastError; +}; + +static const int nestingLimit = 1024; + + +Parser::Parser(ExecutionContext *context, const QChar *json, int length) + : context(context), head(json), json(json), nestingLevel(0), lastError(QJsonParseError::NoError) { - QString jtext = ctx->argument(0).toString(ctx)->toQString(); + end = json + length; +} + + + +/* + +begin-array = ws %x5B ws ; [ left square bracket + +begin-object = ws %x7B ws ; { left curly bracket - const QChar *ch = jtext.constData(); - const QChar *end = ch + jtext.size(); +end-array = ws %x5D ws ; ] right square bracket - bool simple = false; - while (ch < end) { - if (*ch == ' ' || *ch == '\t' || *ch == '\n' || *ch == '\r') { - ++ch; - } else if (*ch == '[' || *ch == '{') { +end-object = ws %x7D ws ; } right curly bracket + +name-separator = ws %x3A ws ; : colon + +value-separator = ws %x2C ws ; , comma + +Insignificant whitespace is allowed before or after any of the six +structural characters. + +ws = *( + %x20 / ; Space + %x09 / ; Horizontal tab + %x0A / ; Line feed or New line + %x0D ; Carriage return + ) + +*/ + +enum { + Space = 0x20, + Tab = 0x09, + LineFeed = 0x0a, + Return = 0x0d, + BeginArray = 0x5b, + BeginObject = 0x7b, + EndArray = 0x5d, + EndObject = 0x7d, + NameSeparator = 0x3a, + ValueSeparator = 0x2c, + Quote = 0x22 +}; + +bool Parser::eatSpace() +{ + while (json < end) { + if (*json > Space) break; - } else { - // simple type - jtext.prepend('['); - jtext.append(']'); - simple = true; + if (*json != Space && + *json != Tab && + *json != LineFeed && + *json != Return) + break; + ++json; + } + return (json < end); +} + +QChar Parser::nextToken() +{ + if (!eatSpace()) + return 0; + QChar token = *json++; + switch (token.unicode()) { + case BeginArray: + case BeginObject: + case NameSeparator: + case ValueSeparator: + case EndArray: + case EndObject: + eatSpace(); + case Quote: + break; + default: + token = 0; + break; + } + return token; +} + +/* + JSON-text = object / array +*/ +Value Parser::parse(QJsonParseError *error) +{ +#ifdef PARSER_DEBUG + indent = 0; + qDebug() << ">>>>> parser begin"; +#endif + + eatSpace(); + + Value v; + if (!parseValue(&v)) { +#ifdef PARSER_DEBUG + qDebug() << ">>>>> parser error"; +#endif + if (lastError == QJsonParseError::NoError) + lastError = QJsonParseError::IllegalValue; + error->offset = json - head; + error->error = lastError; + return Value::undefinedValue(); + } + + // some input left... + if (eatSpace()) { + lastError = QJsonParseError::IllegalValue; + error->offset = json - head; + error->error = lastError; + return Value::undefinedValue(); + } + + END; + error->offset = 0; + error->error = QJsonParseError::NoError; + return v; +} + +/* + object = begin-object [ member *( value-separator member ) ] + end-object +*/ + +Value Parser::parseObject() +{ + if (++nestingLevel > nestingLimit) { + lastError = QJsonParseError::DeepNesting; + return Value::undefinedValue(); + } + + BEGIN << "parseObject pos=" << json; + + Object *o = context->engine->newObject(); + Value objectVal = Value::fromObject(o); + + QChar token = nextToken(); + while (token == Quote) { + if (!parseMember(o)) + return Value::undefinedValue(); + token = nextToken(); + if (token != ValueSeparator) break; + token = nextToken(); + if (token == EndObject) { + lastError = QJsonParseError::MissingObject; + return Value::undefinedValue(); } } - QJsonParseError e; - QJsonDocument doc = QJsonDocument::fromJson(jtext.toUtf8(), &e); - if (e.error != QJsonParseError::NoError) - ctx->throwSyntaxError(0); + DEBUG << "end token=" << token; + if (token != EndObject) { + lastError = QJsonParseError::UnterminatedObject; + return Value::undefinedValue(); + } - // iterate over the doc and convert to V4 types - Value result; - if (doc.isArray()) - result = convertArray(ctx, doc.array()); - else if (doc.isObject()) - result = convertObject(ctx, doc.object()); - else - result = Value::undefinedValue(); + END; + + --nestingLevel; + return objectVal; +} + +/* + member = string name-separator value +*/ +bool Parser::parseMember(Object *o) +{ + BEGIN << "parseMember"; + if (!o->members) + o->members.reset(new PropertyTable()); - if (simple) { - result = result.objectValue()->__get__(ctx, (uint)0); + QString key; + if (!parseString(&key)) + return false; + QChar token = nextToken(); + if (token != NameSeparator) { + lastError = QJsonParseError::MissingNameSeparator; + return false; } + Value val; + if (!parseValue(&val)) + return false; - return result; + PropertyDescriptor *p = o->members->insert(context->engine->identifier(key)); + p->value = val; + + END; + return true; } -Value JsonObject::method_stringify(ExecutionContext *ctx) +/* + array = begin-array [ value *( value-separator value ) ] end-array +*/ +Value Parser::parseArray() { - Q_UNUSED(ctx); - assert(!"Not implemented"); + BEGIN << "parseArray"; + Array array; + + if (++nestingLevel > nestingLimit) { + lastError = QJsonParseError::DeepNesting; + return Value::undefinedValue(); + } + + if (!eatSpace()) { + lastError = QJsonParseError::UnterminatedArray; + return Value::undefinedValue(); + } + if (*json == EndArray) { + nextToken(); + } else { + uint index = 0; + while (1) { + Value val; + if (!parseValue(&val)) + return Value::undefinedValue(); + array.set(index, val); + QChar token = nextToken(); + if (token == EndArray) + break; + else if (token != ValueSeparator) { + if (!eatSpace()) + lastError = QJsonParseError::UnterminatedArray; + else + lastError = QJsonParseError::MissingValueSeparator; + return Value::undefinedValue(); + } + ++index; + } + } + + DEBUG << "size =" << array.length(); + END; + + --nestingLevel; + return Value::fromObject(context->engine->newArrayObject(context, array)); } -static void checkString(ExecutionContext *context, const QString &s) +/* +value = false / null / true / object / array / number / string + +*/ + +bool Parser::parseValue(Value *val) { - const QChar *ch = s.constData(); - const QChar *end = ch + s.length(); - while (ch < end) { - if (ch->unicode() <= 0x1f) - context->throwSyntaxError(0); - ++ch; + BEGIN << "parse Value" << *json; + + switch ((json++)->unicode()) { + case 'n': + if (end - json < 4) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'u' && + *json++ == 'l' && + *json++ == 'l') { + *val = Value::nullValue(); + DEBUG << "value: null"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case 't': + if (end - json < 4) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'r' && + *json++ == 'u' && + *json++ == 'e') { + *val = Value::fromBoolean(true); + DEBUG << "value: true"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case 'f': + if (end - json < 5) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'a' && + *json++ == 'l' && + *json++ == 's' && + *json++ == 'e') { + *val = Value::fromBoolean(false); + DEBUG << "value: false"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case Quote: { + QString value; + if (!parseString(&value)) + return false; + DEBUG << "value: string"; + END; + *val = Value::fromString(context, value); + return true; } + case BeginArray: { + *val = parseArray(); + if (val->isUndefined()) + return false; + DEBUG << "value: array"; + END; + return true; + } + case BeginObject: { + *val = parseObject(); + if (val->isUndefined()) + return false; + DEBUG << "value: object"; + END; + return true; + } + case EndArray: + lastError = QJsonParseError::MissingObject; + return false; + default: + --json; + if (!parseNumber(val)) + return false; + DEBUG << "value: number"; + END; + } + + return true; } -Value JsonObject::convertValue(ExecutionContext *context, const QJsonValue &value) + + + + +/* + number = [ minus ] int [ frac ] [ exp ] + decimal-point = %x2E ; . + digit1-9 = %x31-39 ; 1-9 + e = %x65 / %x45 ; e E + exp = e [ minus / plus ] 1*DIGIT + frac = decimal-point 1*DIGIT + int = zero / ( digit1-9 *DIGIT ) + minus = %x2D ; - + plus = %x2B ; + + zero = %x30 ; 0 + +*/ + +bool Parser::parseNumber(Value *val) { - switch (value.type()) { - case QJsonValue::Null: - return Value::nullValue(); - case QJsonValue::Bool: - return Value::fromBoolean(value.toBool()); - case QJsonValue::Double: - return Value::fromDouble(value.toDouble()); - case QJsonValue::String: { - Value v = Value::fromString(context, value.toString()); - checkString(context, v.stringValue()->toQString()); - return v; + BEGIN << "parseNumber" << *json; + + const QChar *start = json; + bool isInt = true; + + // minus + if (json < end && *json == '-') + ++json; + + // int = zero / ( digit1-9 *DIGIT ) + if (json < end && *json == '0') { + ++json; + } else { + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // frac = decimal-point 1*DIGIT + if (json < end && *json == '.') { + isInt = false; + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // exp = e [ minus / plus ] 1*DIGIT + if (json < end && (*json == 'e' || *json == 'E')) { + isInt = false; + ++json; + if (json < end && (*json == '-' || *json == '+')) + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + QString number(start, json - start); + DEBUG << "numberstring" << number; + + if (isInt) { + bool ok; + int n = number.toInt(&ok); + if (ok && n < (1<<25) && n > -(1<<25)) { + *val = Value::fromInt32(n); + END; + return true; + } + } + + bool ok; + double d; + d = number.toDouble(&ok); + + if (!ok) { + lastError = QJsonParseError::IllegalNumber; + return false; + } + + * val = Value::fromDouble(d); + + END; + return true; +} + +/* + + string = quotation-mark *char quotation-mark + + char = unescaped / + escape ( + %x22 / ; " quotation mark U+0022 + %x5C / ; \ reverse solidus U+005C + %x2F / ; / solidus U+002F + %x62 / ; b backspace U+0008 + %x66 / ; f form feed U+000C + %x6E / ; n line feed U+000A + %x72 / ; r carriage return U+000D + %x74 / ; t tab U+0009 + %x75 4HEXDIG ) ; uXXXX U+XXXX + + escape = %x5C ; \ + + quotation-mark = %x22 ; " + + unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + */ +static inline bool addHexDigit(QChar digit, uint *result) +{ + ushort d = digit.unicode(); + *result <<= 4; + if (d >= '0' && d <= '9') + *result |= (d - '0'); + else if (d >= 'a' && d <= 'f') + *result |= (d - 'a') + 10; + else if (d >= 'A' && d <= 'F') + *result |= (d - 'A') + 10; + else + return false; + return true; +} + +static inline bool scanEscapeSequence(const QChar *&json, const QChar *end, uint *ch) +{ + ++json; + if (json >= end) + return false; + + DEBUG << "scan escape"; + uint escaped = (json++)->unicode(); + switch (escaped) { + case '"': + *ch = '"'; break; + case '\\': + *ch = '\\'; break; + case '/': + *ch = '/'; break; + case 'b': + *ch = 0x8; break; + case 'f': + *ch = 0xc; break; + case 'n': + *ch = 0xa; break; + case 'r': + *ch = 0xd; break; + case 't': + *ch = 0x9; break; + case 'u': { + *ch = 0; + if (json > end - 4) + return false; + for (int i = 0; i < 4; ++i) { + if (!addHexDigit(*json, ch)) + return false; + ++json; + } + if (*ch <= 0x1f) + return false; + return true; } - case QJsonValue::Array: - return convertArray(context, value.toArray()); - case QJsonValue::Object: - return convertObject(context, value.toObject()); default: - assert(!"internal error in JSON conversion"); - return Value::undefinedValue(); + return false; } + return true; } -Value JsonObject::convertArray(ExecutionContext *context, const QJsonArray &array) + +bool Parser::parseString(QString *string) { - ArrayObject *o = context->engine->newArrayObject(context); - for (int i = 0; i < array.size(); ++i) { - QJsonValue v = array.at(i); - o->array.set(i, convertValue(context, v)); + BEGIN << "parse string stringPos=" << json; + + while (json < end) { + if (*json == '"') + break; + else if (*json == '\\') { + uint ch = 0; + if (!scanEscapeSequence(json, end, &ch)) { + lastError = QJsonParseError::IllegalEscapeSequence; + return false; + } + qDebug() << "scanEscape" << hex << ch; + if (QChar::requiresSurrogates(ch)) { + *string += QChar::highSurrogate(ch); + *string += QChar::lowSurrogate(ch); + } else { + *string += QChar(ch); + } + } else { + if (json->unicode() <= 0x1f) { + lastError = QJsonParseError::IllegalEscapeSequence; + return false; + } + *string += *json; + ++json; + } + } + ++json; + + if (json > end) { + lastError = QJsonParseError::UnterminatedString; + return false; } - o->array.setLengthUnchecked(array.size()); - return Value::fromObject(o); + + END; + return true; } -Value JsonObject::convertObject(ExecutionContext *context, const QJsonObject &object) + + +JsonObject::JsonObject(ExecutionContext *context) + : Object() { - Object *o = context->engine->newObject(); - for (QJsonObject::const_iterator it = object.constBegin(); it != object.constEnd(); ++it) { - QString key = it.key(); - checkString(context, key); - QJsonValue v = it.value(); - o->__put__(context, key, convertValue(context, v)); + prototype = context->engine->objectPrototype; + + defineDefaultProperty(context, QStringLiteral("parse"), method_parse, 2); + defineDefaultProperty(context, QStringLiteral("stringify"), method_stringify); +} + + +Value JsonObject::method_parse(ExecutionContext *ctx) +{ + QString jtext = ctx->argument(0).toString(ctx)->toQString(); + + DEBUG << "parsing source = " << jtext; + Parser parser(ctx, jtext.constData(), jtext.length()); + QJsonParseError error; + Value result = parser.parse(&error); + if (error.error != QJsonParseError::NoError) { + DEBUG << "parse error" << error.errorString(); + ctx->throwSyntaxError(0); } - return Value::fromObject(o); + + return result; +} + +Value JsonObject::method_stringify(ExecutionContext *ctx) +{ + Q_UNUSED(ctx); + assert(!"Not implemented"); } + } } diff --git a/qv4jsonobject.h b/qv4jsonobject.h index 193d4900a5..6626471606 100644 --- a/qv4jsonobject.h +++ b/qv4jsonobject.h @@ -53,11 +53,6 @@ struct JsonObject : Object { static Value method_parse(ExecutionContext *ctx); static Value method_stringify(ExecutionContext *ctx); -private: - static Value convertArray(ExecutionContext *context, const QJsonArray &array); - static Value convertObject(ExecutionContext *context, const QJsonObject &object); - static Value convertValue(ExecutionContext *context, const QJsonValue &value); - }; } diff --git a/tests/TestExpectations b/tests/TestExpectations index b0a00e6518..47409284e5 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -269,11 +269,6 @@ S12.7_A7 failing S12.8_A4_T1 failing S12.8_A4_T2 failing S12.8_A4_T3 failing -15.12.1.1-g6-3 failing -15.12.1.1-g6-4 failing -15.12.1.1-g6-5 failing -15.12.1.1-g6-6 failing -15.12.1.1-g6-7 failing S15.12.2_A1 failing 15.12.3-0-2 failing 15.12.3-11-1 failing @@ -514,4 +509,4 @@ S15.4.4.4_A1_T2 failing 15.4.4.21-8-b-iii-1-6 failing 15.12.3_4-1-1 -15.12.3_4-1-3 \ No newline at end of file +15.12.3_4-1-3 -- cgit v1.2.3 From f71bf94fc9b78309ca237c33f9b0a7a726a6afea Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 Jan 2013 11:50:01 +0100 Subject: Fix crash when using the built-in regexp syntax When /someregexp/ appears in the source, we generate a new regexp object in the MASM back-end at run-time. That object is memory managed but it's never marked, because we only store its address in the generated code. This patch adds a way for the code generator to collect values that are generated like this and encoded only in the code directly, and therefore require extra book-keeping when collecting the roots. Change-Id: Ia4cc3f4578e2e7e4378ea7ab1a32c66f0e6ab4bf Reviewed-by: Lars Knoll --- qv4functionobject.h | 1 + qv4isel_masm.cpp | 6 +++++- qv4isel_masm_p.h | 1 + qv4mm.cpp | 6 ++++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/qv4functionobject.h b/qv4functionobject.h index 912c1038a4..95e06e11fc 100644 --- a/qv4functionobject.h +++ b/qv4functionobject.h @@ -107,6 +107,7 @@ struct Function { QList formals; QList locals; + QVector generatedValues; bool hasNestedFunctions : 1; bool hasDirectEval : 1; diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 3aead74731..2e60f720aa 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -370,6 +370,7 @@ InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Modu : EvalInstructionSelection(engine, module) , _block(0) , _function(0) + , _vmFunction(0) , _asm(0) { } @@ -382,6 +383,7 @@ InstructionSelection::~InstructionSelection() void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) { qSwap(_function, function); + qSwap(_vmFunction, vmFunction); Assembler* oldAssembler = _asm; _asm = new Assembler(_function); @@ -423,8 +425,9 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) #endif _asm->ret(); - _asm->link(vmFunction); + _asm->link(_vmFunction); + qSwap(_vmFunction, vmFunction); qSwap(_function, function); delete _asm; _asm = oldAssembler; @@ -570,6 +573,7 @@ void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *target { Value v = Value::fromObject(engine()->newRegExpObject(*sourceRegexp->value, sourceRegexp->flags)); + _vmFunction->generatedValues.append(v); _asm->storeValue(v, targetTemp); } diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 3d18036d19..2300377c65 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -748,6 +748,7 @@ private: IR::BasicBlock *_block; IR::Function* _function; + VM::Function* _vmFunction; Assembler* _asm; }; diff --git a/qv4mm.cpp b/qv4mm.cpp index 6521ff55ed..f624cec7fd 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -350,6 +350,12 @@ void MemoryManager::collectRoots(QVector &roots) const roots.append(it->object); } + for (int i = 0; i < m_d->engine->functions.size(); ++i) { + Function* f = m_d->engine->functions.at(i); + for (int k = 0; k < f->generatedValues.count(); ++k) + add(roots, f->generatedValues.at(k)); + } + collectRootsOnStack(roots); } -- cgit v1.2.3 From 62b9e0f2cad80c48c87b703d6bcef9988fd5c1a0 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 23 Jan 2013 13:21:09 +0100 Subject: Fix callBuiltinInvalid for the interpreter. Change-Id: Ifde12261a7c66a7b268129d5fae4ae626e16a00f Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 9 +++++++++ moth/qv4isel_moth.cpp | 16 +++------------- moth/qv4vme_moth.cpp | 11 ++++++++++- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index bafa9aa80f..d20b84e5a8 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -18,6 +18,7 @@ F(Push, push) \ F(CallValue, callValue) \ F(CallProperty, callProperty) \ + F(CallActivationProperty, callActivationProperty) \ F(CallBuiltin, callBuiltin) \ F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \ @@ -152,6 +153,13 @@ union Instr quint32 args; int targetTempIndex; }; + struct instr_callActivationProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + int targetTempIndex; + }; struct instr_callBuiltin { MOTH_INSTR_HEADER enum { @@ -321,6 +329,7 @@ union Instr instr_push push; instr_callValue callValue; instr_callProperty callProperty; + instr_callActivationProperty callActivationProperty; instr_callBuiltin callBuiltin; instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index aef7d41da4..2acdb88709 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -683,19 +683,10 @@ void InstructionSelection::visitRet(IR::Ret *s) void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) { - const int scratchIndex = scratchTempIndex(); - - Instruction::LoadName load; - load.name = engine()->newString(*func->id); - load.targetTempIndex = scratchIndex; - addInstruction(load); - - const int targetTempIndex = result ? result->index : scratchTempIndex(); - - Instruction::CallValue call; + Instruction::CallActivationProperty call; + call.name = engine()->newString(*func->id); prepareCallArgs(args, call.argc, call.args); - call.destIndex = scratchIndex; - call.targetTempIndex = targetTempIndex; + call.targetTempIndex = result ? result->index : scratchTempIndex(); addInstruction(call); } @@ -817,7 +808,6 @@ void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR: { Instruction::CallBuiltinForeachNextPropertyName call; call.argTemp = arg->index; - qDebug("result index: %d", result ? result->index : -1); call.targetTempIndex = result ? result->index : scratchTempIndex(); addInstruction(call); } diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index f861996a41..dff4db68a8 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -221,7 +221,8 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co int argStart = instr.args - context->variableCount(); TRACE(Call, "value index = %d, argStart = %d, argc = %d, result temp index = %d", instr.destIndex, argStart, instr.argc, instr.targetTempIndex); VM::Value *args = stack + argStart; - TEMP(instr.targetTempIndex) = __qmljs_call_value(context, VM::Value::undefinedValue(), TEMP(instr.destIndex), args, instr.argc); + VM::Value result = __qmljs_call_value(context, VM::Value::undefinedValue(), TEMP(instr.destIndex), args, instr.argc); + TEMP(instr.targetTempIndex) = result; MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) @@ -233,6 +234,14 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co TEMP(instr.targetTempIndex) = __qmljs_call_property(context, base, instr.name, args, instr.argc); MOTH_END_INSTR(CallProperty) + MOTH_BEGIN_INSTR(CallActivationProperty) + int argStart = instr.args - context->variableCount(); + // TODO: change this assert everywhere to include a minimum + // TODO: the args calculation is duplicate code, fix that + VM::Value *args = stack + argStart; + TEMP(instr.targetTempIndex) = __qmljs_call_activation_property(context, instr.name, args, instr.argc); + MOTH_END_INSTR(CallActivationProperty) + MOTH_BEGIN_INSTR(CallBuiltin) // TODO: split this into separate instructions switch (instr.builtin) { -- cgit v1.2.3 From 8762d4fdbb311c908d8d5d2b69afb3a58fa5dc89 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 23 Jan 2013 14:31:05 +0100 Subject: Implement JSON.stringify Change-Id: If1d170767c61c2435fe82f7e26e1ebf8de6327bb Reviewed-by: Simon Hausmann --- qv4jsonobject.cpp | 274 +++++++++++++++++++++++++++++++++++++++++++++++-- tests/TestExpectations | 49 --------- 2 files changed, 268 insertions(+), 55 deletions(-) diff --git a/qv4jsonobject.cpp b/qv4jsonobject.cpp index 6c0b02ff88..719cb56d8a 100644 --- a/qv4jsonobject.cpp +++ b/qv4jsonobject.cpp @@ -40,10 +40,14 @@ ****************************************************************************/ #include #include +#include +#include +#include +#include #include -#include -#include -#include +#include +#include + namespace QQmlJS { namespace VM { @@ -633,6 +637,230 @@ bool Parser::parseString(QString *string) } +struct Stringify +{ + ExecutionContext *ctx; + FunctionObject *replacerFunction; + QVector propertyList; + QString gap; + QString indent; + + QStack stack; + + Stringify(ExecutionContext *ctx) : ctx(ctx), replacerFunction(0) {} + + QString Str(const QString &key, Value value); + QString JA(ArrayObject *a); + QString JO(Object *o); + + QString makeMember(const QString &key, Value v); +}; + +static QString quote(const QString &str) +{ + QString product = "\""; + for (int i = 0; i < str.length(); ++i) { + QChar c = str.at(i); + switch (c.unicode()) { + case '"': + product += "\\\""; + break; + case '\\': + product += "\\\\"; + break; + case '\b': + product += "\\b"; + break; + case '\f': + product += "\\f"; + break; + case '\n': + product += "\\n"; + break; + case '\r': + product += "\\r"; + break; + case '\t': + product += "\\t"; + break; + default: + if (c.unicode() <= 0x1f) { + product += "\\u00"; + product += c.unicode() > 0xf ? '1' : '0'; + product += "0123456789abcdef"[c.unicode() & 0xf]; + } else { + product += c; + } + } + } + product += '"'; + return product; +} + +QString Stringify::Str(const QString &key, Value value) +{ + QString result; + + if (Object *o = value.asObject()) { + FunctionObject *toJSON = o->__get__(ctx, ctx->engine->identifier(QStringLiteral("toJSON"))).asFunctionObject(); + if (toJSON) { + Value arg = Value::fromString(ctx, key); + value = toJSON->call(ctx, value, &arg, 1); + } + } + + if (replacerFunction) { + Object *holder = ctx->engine->newObject(); + Value holderValue = Value::fromObject(holder); + holder->__put__(ctx, QString(), value); + Value args[2]; + args[0] = Value::fromString(ctx, key); + args[1] = value; + value = replacerFunction->call(ctx, holderValue, args, 2); + } + + if (Object *o = value.asObject()) { + if (NumberObject *n = o->asNumberObject()) + value = n->value; + else if (StringObject *so = o->asStringObject()) + value = so->value; + else if (BooleanObject *b =o->asBooleanObject()) + value = b->value; + } + + if (value.isNull()) + return QStringLiteral("null"); + if (value.isBoolean()) + return value.booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); + if (value.isString()) + return quote(value.stringValue()->toQString()); + + if (value.isNumber()) { + double d = value.toNumber(ctx); + return std::isfinite(d) ? value.toString(ctx)->toQString() : QStringLiteral("null"); + } + + if (Object *o = value.asObject()) { + if (!o->asFunctionObject()) { + if (o->asArrayObject()) + return JA(static_cast(o)); + else + return JO(o); + } + } + + return QString(); +} + +QString Stringify::makeMember(const QString &key, Value v) +{ + QString strP = Str(key, v); + if (!strP.isEmpty()) { + QString member = quote(key) + ':'; + if (!gap.isEmpty()) + member += ' '; + member += strP; + return member; + } + return QString(); +} + +QString Stringify::JO(Object *o) +{ + if (stack.contains(o)) + ctx->throwTypeError(); + + QString result; + stack.push(o); + QString stepback = indent; + indent += gap; + + QStringList partial; + if (propertyList.isEmpty()) { + ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); + + while (1) { + String *name; + uint index; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + Value v = o->getValueChecked(ctx, pd); + QString key; + if (name) + key = name->toQString(); + else + key = QString::number(index); + QString member = makeMember(key, v); + if (!member.isEmpty()) + partial += member; + } + } else { + for (int i = 0; i < propertyList.size(); ++i) { + bool exists; + Value v = o->__get__(ctx, propertyList.at(i), &exists); + if (!exists) + continue; + QString member = makeMember(propertyList.at(i)->toQString(), v); + if (!member.isEmpty()) + partial += member; + } + } + + if (partial.isEmpty()) { + result = QStringLiteral("{}"); + } else if (gap.isEmpty()) { + result = "{" + partial.join(",") + "}"; + } else { + QString separator = ",\n" + indent; + result = "{\n" + indent + partial.join(separator) + "\n" + stepback + "}"; + } + + indent = stepback; + stack.pop(); + return result; +} + +QString Stringify::JA(ArrayObject *a) +{ + if (stack.contains(a)) + ctx->throwTypeError(); + + QString result; + stack.push(a); + QString stepback = indent; + indent += gap; + + QStringList partial; + uint len = a->array.length(); + for (uint i = 0; i < len; ++i) { + bool exists; + Value v = a->__get__(ctx, i, &exists); + if (!exists) { + partial += QStringLiteral("null"); + continue; + } + QString strP = Str(QString::number(i), v); + if (!strP.isEmpty()) + partial += strP; + else + partial += QStringLiteral("null"); + } + + if (partial.isEmpty()) { + result = QStringLiteral("[]"); + } else if (gap.isEmpty()) { + result = "[" + partial.join(",") + "]"; + } else { + QString separator = ",\n" + indent; + result = "[\n" + indent + partial.join(separator) + "\n" + stepback + "]"; + } + + indent = stepback; + stack.pop(); + return result; +} + JsonObject::JsonObject(ExecutionContext *context) : Object() @@ -640,7 +868,7 @@ JsonObject::JsonObject(ExecutionContext *context) prototype = context->engine->objectPrototype; defineDefaultProperty(context, QStringLiteral("parse"), method_parse, 2); - defineDefaultProperty(context, QStringLiteral("stringify"), method_stringify); + defineDefaultProperty(context, QStringLiteral("stringify"), method_stringify, 3); } @@ -662,8 +890,42 @@ Value JsonObject::method_parse(ExecutionContext *ctx) Value JsonObject::method_stringify(ExecutionContext *ctx) { - Q_UNUSED(ctx); - assert(!"Not implemented"); + Stringify stringify(ctx); + + Object *o = ctx->argument(1).asObject(); + if (o) { + stringify.replacerFunction = o->asFunctionObject(); + if (o->isArray) { + for (uint i = 0; i < o->array.length(); ++i) { + Value v = o->__get__(ctx, i); + if (v.asNumberObject() || v.asStringObject() || v.isNumber()) + v = __qmljs_to_string(v, ctx); + if (v.isString()) { + String *s = v.stringValue(); + if (!stringify.propertyList.contains(s)) + stringify.propertyList.append(s); + } + } + } + } + + Value s = ctx->argument(2); + if (NumberObject *n = s.asNumberObject()) + s = n->value; + else if (StringObject *so = s.asStringObject()) + s = so->value; + + if (s.isNumber()) { + stringify.gap = QString(qMin(10, (int)s.toInteger(ctx)), ' '); + } else if (s.isString()) { + stringify.gap = s.stringValue()->toQString().left(10); + } + + + QString result = stringify.Str(QString(), ctx->argument(0)); + if (result.isEmpty()) + return Value::undefinedValue(); + return Value::fromString(ctx, result); } diff --git a/tests/TestExpectations b/tests/TestExpectations index 47409284e5..ad05f24cb8 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -270,45 +270,6 @@ S12.8_A4_T1 failing S12.8_A4_T2 failing S12.8_A4_T3 failing S15.12.2_A1 failing -15.12.3-0-2 failing -15.12.3-11-1 failing -15.12.3-11-10 failing -15.12.3-11-11 failing -15.12.3-11-12 failing -15.12.3-11-13 failing -15.12.3-11-14 failing -15.12.3-11-15 failing -15.12.3-11-2 failing -15.12.3-11-26 failing -15.12.3-11-3 failing -15.12.3-11-4 failing -15.12.3-11-5 failing -15.12.3-11-6 failing -15.12.3-11-7 failing -15.12.3-11-8 failing -15.12.3-11-9 failing -15.12.3-4-1 failing -15.12.3-5-a-i-1 failing -15.12.3-5-b-i-1 failing -15.12.3-6-a-1 failing -15.12.3-6-a-2 failing -15.12.3-6-b-1 failing -15.12.3-6-b-2 failing -15.12.3-6-b-3 failing -15.12.3-6-b-4 failing -15.12.3-7-a-1 failing -15.12.3-8-a-1 failing -15.12.3-8-a-2 failing -15.12.3-8-a-3 failing -15.12.3-8-a-4 failing -15.12.3-8-a-5 failing -15.12.3_2-2-b-i-1 failing -15.12.3_2-2-b-i-2 failing -15.12.3_2-2-b-i-3 failing -15.12.3_2-3-a-1 failing -15.12.3_2-3-a-2 failing -15.12.3_2-3-a-3 failing -15.12.3_4-1-2 failing 15.2.3.6-4-360-3 failing 15.2.3.6-4-360-7 failing 15.4.4.14-9-a-10 failing @@ -487,16 +448,6 @@ S15.2.4.4_A14 failing S15.4.4.4_A1_T2 failing # Regressions due to Object/property refactoring -15.12.3-11-16 failing -15.12.3-11-17 failing -15.12.3-11-18 failing -15.12.3-11-19 failing -15.12.3-11-20 failing -15.12.3-11-21 failing -15.12.3-11-22 failing -15.12.3-11-23 failing -15.12.3-11-24 failing -15.12.3-11-25 failing 15.4.4.18-7-c-i-6 failing 15.4.4.19-8-c-i-6 failing 15.4.4.20-9-c-i-6 failing -- cgit v1.2.3 From 1f8c5a5b371ceffc8c2a8455e182a8f6fb786d5c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 23 Jan 2013 15:20:44 +0100 Subject: Fix some errors related to strict mode eval and arguments aren't allowed in variable declarations or as lvalues. More correct propogation of the strict mode flag from eval into Codegen. Change-Id: Ic519900463aa2f57311a64787e33bc6924838f16 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 7 ++++++ qv4globalobject.cpp | 15 ++++++++--- tests/TestExpectations | 67 +------------------------------------------------- 3 files changed, 19 insertions(+), 70 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 7d530ffdfe..bc45257126 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -305,6 +305,8 @@ protected: virtual bool visit(VariableDeclaration *ast) { + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == "arguments")) + _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); checkName(ast->name, ast->identifierToken); if (ast->name == QLatin1String("arguments")) _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; @@ -1119,6 +1121,11 @@ bool Codegen::visit(BinaryExpression *ast) } Result left = expression(ast->left); + if (_function->isStrict) { + if (IR::Name *n = left->asName()) + if (*n->id == QLatin1String("eval") || *n->id == QLatin1String("arguments")) + throwSyntaxError(ast->left->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); + } Result right = expression(ast->right); switch (ast->op) { diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index 67c1759cd4..df666df1ad 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -319,6 +319,7 @@ Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Va return Value::undefinedValue(); ExecutionContext *ctx = context; + if (!directCall) { // the context for eval should be the global scope while (ctx->parent) @@ -329,14 +330,20 @@ Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Va return args[0]; const QString code = args[0].stringValue()->toQString(); - bool inheritContext = !context->strictMode; - QQmlJS::VM::Function *f = parseSource(context, QStringLiteral("eval code"), + bool inheritContext = !ctx->strictMode; + + bool cstrict = ctx->strictMode; + if (!directCall) + ctx->strictMode = false; + QQmlJS::VM::Function *f = parseSource(ctx, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode, inheritContext); + ctx->strictMode = cstrict; + if (!f) return Value::undefinedValue(); - bool strict = f->isStrict || context->strictMode; + bool strict = f->isStrict || (directCall && context->strictMode); ExecutionContext k; @@ -346,7 +353,7 @@ Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Va } // set the correct strict mode flag on the context - bool cstrict = ctx->strictMode; + cstrict = ctx->strictMode; ctx->strictMode = strict; Value result = f->code(ctx, f->codeData); diff --git a/tests/TestExpectations b/tests/TestExpectations index ad05f24cb8..6c3905357b 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -14,8 +14,6 @@ S15.1.3.2_A2.5_T1 10.4.3-1-17-s failing 10.4.3-1-63-s failing 10.4.3-1-82-s failing -10.5-1-s failing -10.5-7-b-1-s failing 11.1.4_4-5-1 failing 11.1.4_5-6-1 failing S11.10.1_A2.4_T1 failing @@ -32,10 +30,6 @@ S11.11.1_A3_T4 failing 11.13.1-1-2 failing 11.13.1-1-3 failing 11.13.1-1-4 failing -11.13.1-4-28-s failing -11.13.1-4-29-s failing -11.13.1-4-30-s failing -11.13.1-4-31-s failing 11.13.2-45-s failing 11.13.2-46-s failing 11.13.2-47-s failing @@ -47,28 +41,6 @@ S11.11.1_A3_T4 failing 11.13.2-53-s failing 11.13.2-54-s failing 11.13.2-55-s failing -11.13.2-6-1-s failing -11.13.2-6-10-s failing -11.13.2-6-11-s failing -11.13.2-6-12-s failing -11.13.2-6-13-s failing -11.13.2-6-14-s failing -11.13.2-6-15-s failing -11.13.2-6-16-s failing -11.13.2-6-17-s failing -11.13.2-6-18-s failing -11.13.2-6-19-s failing -11.13.2-6-2-s failing -11.13.2-6-20-s failing -11.13.2-6-21-s failing -11.13.2-6-22-s failing -11.13.2-6-3-s failing -11.13.2-6-4-s failing -11.13.2-6-5-s failing -11.13.2-6-6-s failing -11.13.2-6-7-s failing -11.13.2-6-8-s failing -11.13.2-6-9-s failing S11.13.2_A4.3_T1.4 failing S11.13.2_A4.3_T2.3 failing S11.13.2_A4.3_T2.9 failing @@ -194,44 +166,7 @@ S12.10_A1.4_T4 failing S12.10_A1.4_T5 failing S12.10_A1.5_T4 failing S12.10_A1.5_T5 failing -12.2.1-1-s failing -12.2.1-12-s failing -12.2.1-13-s failing -12.2.1-14-s failing -12.2.1-15-s failing -12.2.1-18-s failing -12.2.1-19-s failing -12.2.1-2-s failing -12.2.1-21-s failing 12.2.1-22-s failing -12.2.1-23-s failing -12.2.1-24-s failing -12.2.1-25-s failing -12.2.1-26-s failing -12.2.1-27-s failing -12.2.1-28-s failing -12.2.1-29-s failing -12.2.1-3-s failing -12.2.1-30-s failing -12.2.1-31-s failing -12.2.1-32-s failing -12.2.1-33-s failing -12.2.1-34-s failing -12.2.1-35-s failing -12.2.1-36-s failing -12.2.1-37-s failing -12.2.1-4-s failing -12.2.1-7-s failing -12.2.1-8-s failing -13.0-10-s failing -13.0-11-s failing -13.0-13-s failing -13.0-14-s failing -13.0-15-s failing -13.0-16-s failing -13.0-7-s failing -13.0-8-s failing -13.0-9-s failing S13_A15_T4 failing S13_A3_T1 failing 13.1-11-s failing @@ -460,4 +395,4 @@ S15.4.4.4_A1_T2 failing 15.4.4.21-8-b-iii-1-6 failing 15.12.3_4-1-1 -15.12.3_4-1-3 +15.12.3_4-1-3 \ No newline at end of file -- cgit v1.2.3 From d9a15cb8ec77eb370e806f391214f2e3ded272b5 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 23 Jan 2013 20:24:12 +0100 Subject: Fix a crash when a non direct call to eval caused a syntax error Change-Id: I89d1eabd248fd844f7cd2caa29667e0c7850958d Reviewed-by: Simon Hausmann --- main.cpp | 3 ++- qv4codegen.cpp | 4 ++-- qv4codegen_p.h | 2 +- qv4functionobject.cpp | 2 +- qv4globalobject.cpp | 14 +++++--------- qv4globalobject.h | 2 +- tests/TestExpectations | 1 - 7 files changed, 12 insertions(+), 16 deletions(-) diff --git a/main.cpp b/main.cpp index af14fb741b..741171ea1d 100644 --- a/main.cpp +++ b/main.cpp @@ -395,7 +395,8 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode, /*inheritContext =*/ false); + QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode, + /*strictMode =*/ false, /*inheritContext =*/ false); if (!f) continue; diff --git a/qv4codegen.cpp b/qv4codegen.cpp index bc45257126..4e5def9a7b 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -402,7 +402,7 @@ private: QStack _envStack; }; -Codegen::Codegen(VM::ExecutionContext *context) +Codegen::Codegen(VM::ExecutionContext *context, bool strict) : _module(0) , _function(0) , _block(0) @@ -415,7 +415,7 @@ Codegen::Codegen(VM::ExecutionContext *context) , _labelledStatement(0) , _tryCleanup(0) , _context(context) - , _strictMode(context->strictMode) + , _strictMode(strict) , _debugger(context->engine->debugger) , _errorHandler(0) { diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 8a83908dd1..9806198ecb 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -70,7 +70,7 @@ public: class Codegen: protected AST::Visitor { public: - Codegen(VM::ExecutionContext *ctx); + Codegen(VM::ExecutionContext *ctx, bool strict); Codegen(ErrorHandler *errorHandler, bool strictMode); enum Mode { diff --git a/qv4functionobject.cpp b/qv4functionobject.cpp index e5501d8320..798549116c 100644 --- a/qv4functionobject.cpp +++ b/qv4functionobject.cpp @@ -182,7 +182,7 @@ Value FunctionCtor::construct(ExecutionContext *ctx) IR::Module module; - Codegen cg(ctx); + Codegen cg(ctx, ctx->strictMode); IR::Function *irf = cg(QString(), fe, &module); QScopedPointer isel(ctx->engine->iselFactory->create(ctx->engine, &module)); diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index df666df1ad..f75d313482 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -332,13 +332,9 @@ Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Va const QString code = args[0].stringValue()->toQString(); bool inheritContext = !ctx->strictMode; - bool cstrict = ctx->strictMode; - if (!directCall) - ctx->strictMode = false; - QQmlJS::VM::Function *f = parseSource(ctx, QStringLiteral("eval code"), + QQmlJS::VM::Function *f = parseSource(context, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode, - inheritContext); - ctx->strictMode = cstrict; + (directCall && context->strictMode), inheritContext); if (!f) return Value::undefinedValue(); @@ -353,7 +349,7 @@ Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Va } // set the correct strict mode flag on the context - cstrict = ctx->strictMode; + bool cstrict = ctx->strictMode; ctx->strictMode = strict; Value result = f->code(ctx, f->codeData); @@ -376,7 +372,7 @@ Value EvalFunction::call(ExecutionContext *context, Value thisObject, Value *arg QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, const QString &source, QQmlJS::Codegen::Mode mode, - bool inheritContext) + bool strictMode, bool inheritContext) { using namespace QQmlJS; @@ -428,7 +424,7 @@ QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct for (String **i = ctx->variables(), **ei = i + ctx->variableCount(); i < ei; ++i) inheritedLocals.append(*i ? (*i)->toQString() : QString()); - Codegen cg(ctx); + Codegen cg(ctx, strictMode); IR::Function *globalIRCode = cg(fileName, program, &module, mode, inheritedLocals); QScopedPointer isel(ctx->engine->iselFactory->create(vm, &module)); if (globalIRCode) diff --git a/qv4globalobject.h b/qv4globalobject.h index 20722274d7..2292e380d6 100644 --- a/qv4globalobject.h +++ b/qv4globalobject.h @@ -54,7 +54,7 @@ struct EvalFunction : FunctionObject static QQmlJS::VM::Function *parseSource(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, const QString &source, - QQmlJS::Codegen::Mode mode, + QQmlJS::Codegen::Mode mode, bool strictMode, bool inheritContext); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); diff --git a/tests/TestExpectations b/tests/TestExpectations index 6c3905357b..0aa424d7db 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -166,7 +166,6 @@ S12.10_A1.4_T4 failing S12.10_A1.4_T5 failing S12.10_A1.5_T4 failing S12.10_A1.5_T5 failing -12.2.1-22-s failing S13_A15_T4 failing S13_A3_T1 failing 13.1-11-s failing -- cgit v1.2.3 From de0d7f657cdd82ed2ffd1b8d5f23b17b544b0144 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 23 Jan 2013 20:44:47 +0100 Subject: Don't crash on for(x in null) Change-Id: I3bd5610fa9bffcc195bb1c96a5a595061931a20e Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 2 +- tests/TestExpectations | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index dddf89a0ca..f23a93e73a 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -631,7 +631,7 @@ Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx) { if (!in.isNull() && !in.isUndefined()) in = __qmljs_to_object(in, ctx); - Object *it = ctx->engine->newForEachIteratorObject(ctx, in.objectValue()); + Object *it = ctx->engine->newForEachIteratorObject(ctx, in.asObject()); return Value::fromObject(it); } diff --git a/tests/TestExpectations b/tests/TestExpectations index 0aa424d7db..a6d9ca64fa 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -198,7 +198,6 @@ S15.1.2.1_A4.2 failing S15.1.2.1_A4.3 failing S15.1.2.1_A4.4 failing S15.1.2.1_A4.7 failing -S12.6.4_A2 failing S12.7_A7 failing S12.8_A4_T1 failing S12.8_A4_T2 failing -- cgit v1.2.3 From 3a8d7f6123589adbb3170f5bf61ed5f0eab3bb9e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 23 Jan 2013 21:00:38 +0100 Subject: Loops are local to functions Loops need to be reset when parsing an inner function, as labels can't cross these boundaries. Also throw a syntax error when a continue statement outside a loop is encountered. Change-Id: I69484a2f7a550590194e82667bc4c66aba89ae43 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 49 ++++++++++++++++++++++++++----------------------- tests/TestExpectations | 2 -- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 4e5def9a7b..da2a17cc7b 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1888,6 +1888,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, IR::ExprList *throwArgs = function->New(); throwArgs->expr = throwBlock->TEMP(returnAddress); throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); + Loop *loop = 0; qSwap(_function, function); qSwap(_block, entryBlock); @@ -1895,6 +1896,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, qSwap(_throwBlock, throwBlock); qSwap(_returnAddress, returnAddress); qSwap(_tryCleanup, tryCleanup); + qSwap(_loop, loop); for (FormalParameterList *it = formals; it; it = it->next) { _function->RECEIVE(it->name.toString()); @@ -1928,6 +1930,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, qSwap(_throwBlock, throwBlock); qSwap(_returnAddress, returnAddress); qSwap(_tryCleanup, tryCleanup); + qSwap(_loop, loop); leaveEnvironment(); @@ -1987,45 +1990,45 @@ bool Codegen::visit(BreakStatement *ast) { if (!_loop) throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Break outside of loop")); - unwindException(_loop->tryCleanup); + Loop *loop = 0; if (ast->label.isEmpty()) - _block->JUMP(_loop->breakBlock); + loop = _loop; else { - for (Loop *loop = _loop; loop; loop = loop->parent) { - if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { - _block->JUMP(loop->breakBlock); - return false; - } + for (loop = _loop; loop; loop = loop->parent) { + if (loop->labelledStatement && loop->labelledStatement->label == ast->label) + break; } - throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); } - + unwindException(loop->tryCleanup); + _block->JUMP(loop->breakBlock); return false; } bool Codegen::visit(ContinueStatement *ast) { - unwindException(_loop->tryCleanup); - + Loop *loop = 0; if (ast->label.isEmpty()) { - for (Loop *loop = _loop; loop; loop = loop->parent) { - if (loop->continueBlock) { - _block->JUMP(loop->continueBlock); - return false; - } + for (loop = _loop; loop; loop = loop->parent) { + if (loop->continueBlock) + break; } } else { - for (Loop *loop = _loop; loop; loop = loop->parent) { + for (loop = _loop; loop; loop = loop->parent) { if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { - if (! loop->continueBlock) - break; - - _block->JUMP(loop->continueBlock); - return false; + if (!loop->continueBlock) + loop = 0; + break; } } + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); } - throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "continue outside of loop")); + unwindException(loop->tryCleanup); + _block->JUMP(loop->continueBlock); return false; } diff --git a/tests/TestExpectations b/tests/TestExpectations index a6d9ca64fa..8891965c93 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -191,14 +191,12 @@ S14_A2 failing 14.1-5-s failing 15.1.1.3-3 failing S15.1.2.1_A3.2_T8 failing -S15.1.2.1_A3.3_T1 failing S15.1.2.1_A3.3_T3 failing S15.1.2.1_A4.1 failing S15.1.2.1_A4.2 failing S15.1.2.1_A4.3 failing S15.1.2.1_A4.4 failing S15.1.2.1_A4.7 failing -S12.7_A7 failing S12.8_A4_T1 failing S12.8_A4_T2 failing S12.8_A4_T3 failing -- cgit v1.2.3 From 0b886a07b0d766f39f90c91193faf438b462597a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 23 Jan 2013 21:47:44 +0100 Subject: Labelled statements can also appear outside of loops Change-Id: I121b9341dd102964f52dec2a0278c2a3d74b9a8a Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 2 +- tests/TestExpectations | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index da2a17cc7b..46a228e1f9 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -2172,7 +2172,7 @@ bool Codegen::visit(LabelledStatement *ast) AST::cast(ast->statement) || AST::cast(ast->statement)) { statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. - } else if (_loop) { + } else { IR::BasicBlock *breakBlock = _function->newBasicBlock(); enterLoop(ast->statement, breakBlock, /*continueBlock*/ 0); statement(ast->statement); diff --git a/tests/TestExpectations b/tests/TestExpectations index 8891965c93..0db14a416d 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -197,9 +197,6 @@ S15.1.2.1_A4.2 failing S15.1.2.1_A4.3 failing S15.1.2.1_A4.4 failing S15.1.2.1_A4.7 failing -S12.8_A4_T1 failing -S12.8_A4_T2 failing -S12.8_A4_T3 failing S15.12.2_A1 failing 15.2.3.6-4-360-3 failing 15.2.3.6-4-360-7 failing -- cgit v1.2.3 From 4633832960e0b6b58666b7870778a1623d60f62a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 23 Jan 2013 21:53:54 +0100 Subject: Fix wrong constant folding Change-Id: I8c48ae0597c41c69c77d56cba4c6be738d5d282b Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 4 ++-- tests/TestExpectations | 11 ----------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 46a228e1f9..1f6825521a 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -631,9 +631,9 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) case IR::OpMod: return _block->CONST(IR::NumberType, ::fmod(c1->value, c2->value)); case IR::OpMul: return _block->CONST(IR::NumberType, c1->value * c2->value); case IR::OpOr: return _block->CONST(IR::NumberType, c1->value ? c1->value : c2->value); - case IR::OpRShift: return _block->CONST(IR::NumberType, VM::Value::toInt32(c1->value) << (VM::Value::toUInt32(c2->value) & 0x1f)); + case IR::OpRShift: return _block->CONST(IR::NumberType, VM::Value::toInt32(c1->value) >> (VM::Value::toUInt32(c2->value) & 0x1f)); case IR::OpSub: return _block->CONST(IR::NumberType, c1->value - c2->value); - case IR::OpURShift: return _block->CONST(IR::NumberType,VM::Value::toUInt32(c1->value) << (VM::Value::toUInt32(c2->value) & 0x1f)); + case IR::OpURShift: return _block->CONST(IR::NumberType,VM::Value::toUInt32(c1->value) >> (VM::Value::toUInt32(c2->value) & 0x1f)); case IR::OpInstanceof: case IR::OpIn: diff --git a/tests/TestExpectations b/tests/TestExpectations index 0db14a416d..6cdedd225f 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -92,10 +92,6 @@ S11.7.1_A2.4_T1 failing S11.7.1_A2.4_T3 failing S11.7.2_A2.4_T1 failing S11.7.2_A2.4_T3 failing -S11.7.2_A3_T1.2 failing -S11.7.2_A4_T3 failing -S11.7.2_A4_T4 failing -S11.7.2_A5.2_T1 failing S11.6.1_A2.4_T1 failing S11.6.1_A2.4_T3 failing S11.6.2_A2.4_T1 failing @@ -106,12 +102,6 @@ S11.8.3_A2.4_T1 failing S11.8.3_A2.4_T3 failing S11.7.3_A2.4_T1 failing S11.7.3_A2.4_T3 failing -S11.7.3_A3_T1.2 failing -S11.7.3_A4_T1 failing -S11.7.3_A4_T2 failing -S11.7.3_A4_T3 failing -S11.7.3_A4_T4 failing -S11.7.3_A5.2_T1 failing S11.8.1_A2.4_T1 failing S11.8.1_A2.4_T3 failing S11.8.4_A2.4_T1 failing @@ -153,7 +143,6 @@ S12.11_A1_T1 failing S12.11_A1_T2 failing S12.11_A1_T3 failing S12.11_A1_T4 failing -S12.13_A3_T3 failing 12.14-13 failing 12.10-0-3 failing S12.10_A1.11_T1 failing -- cgit v1.2.3 From 91a91d1054bafa8df1208556c4cefc6afbd2a25e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 23 Jan 2013 23:11:54 +0100 Subject: Fix bug in switch statement Change-Id: I42f509f825d8c095c3762a86e5da881de1038d7d Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 4 ++-- tests/TestExpectations | 12 +----------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 1f6825521a..3bc545d994 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -2313,7 +2313,7 @@ bool Codegen::visit(SwitchStatement *ast) Result rhs = expression(clause->expression); IR::BasicBlock *iftrue = blockMap[clause]; IR::BasicBlock *iffalse = _function->newBasicBlock(); - cjump(binop(IR::OpEqual, *lhs, *rhs), iftrue, iffalse); + cjump(binop(IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); _block = iffalse; } @@ -2322,7 +2322,7 @@ bool Codegen::visit(SwitchStatement *ast) Result rhs = expression(clause->expression); IR::BasicBlock *iftrue = blockMap[clause]; IR::BasicBlock *iffalse = _function->newBasicBlock(); - cjump(binop(IR::OpEqual, *lhs, *rhs), iftrue, iffalse); + cjump(binop(IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); _block = iffalse; } diff --git a/tests/TestExpectations b/tests/TestExpectations index 6cdedd225f..fc90235157 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -6,7 +6,6 @@ S15.1.3.2_A2.5_T1 # uses octal number 15.2.3.6-2-17-1 failing - # Tests failing that are supposed to pass. 10.4.2-1-2 failing 10.4.3-1-104 failing @@ -139,10 +138,6 @@ S12.10_A3.4_T4 failing S12.10_A3.4_T5 failing S12.10_A3.5_T4 failing S12.10_A3.5_T5 failing -S12.11_A1_T1 failing -S12.11_A1_T2 failing -S12.11_A1_T3 failing -S12.11_A1_T4 failing 12.14-13 failing 12.10-0-3 failing S12.10_A1.11_T1 failing @@ -363,18 +358,13 @@ S15.2.4.4_A14 failing # Array regressions S15.4.4.4_A1_T2 failing - -# Regressions due to Object/property refactoring 15.4.4.18-7-c-i-6 failing 15.4.4.19-8-c-i-6 failing 15.4.4.20-9-c-i-6 failing 15.4.4.22-8-b-iii-1-6 failing - -# Bugs in Array.prototype 15.4.4.14-9-b-i-5 failing 15.4.4.16-7-c-i-6 failing 15.4.4.17-7-c-i-6 failing - 15.4.4.21-8-b-iii-1-6 failing 15.12.3_4-1-1 -15.12.3_4-1-3 \ No newline at end of file +15.12.3_4-1-3 -- cgit v1.2.3 From 22a09c8dd09cb3aa4b67568bec47fb500ddeef42 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 23 Jan 2013 22:49:31 +0100 Subject: Fix all but one remaining test case for exceptions Change-Id: I03d42eeeb28a0f75d3cad81cb7af815c9784c43e Reviewed-by: Simon Hausmann --- moth/qv4instr_moth_p.h | 1 - moth/qv4isel_moth.cpp | 7 ------- moth/qv4isel_moth_p.h | 1 - moth/qv4vme_moth.cpp | 3 --- qmljs_runtime.cpp | 6 ------ qv4codegen.cpp | 21 +++++++++++++-------- qv4ir.cpp | 2 -- qv4ir_p.h | 1 - qv4isel_llvm_p.h | 1 - qv4isel_masm.cpp | 5 ----- qv4isel_masm_p.h | 1 - qv4isel_p.cpp | 4 ---- qv4isel_p.h | 1 - tests/TestExpectations | 5 ----- 14 files changed, 13 insertions(+), 46 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index d20b84e5a8..07016cca00 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -164,7 +164,6 @@ union Instr MOTH_INSTR_HEADER enum { builtin_throw, - builtin_rethrow, builtin_create_exception_handler, builtin_delete_exception_handler, builtin_get_exception, diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 2acdb88709..2626955cfc 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -766,13 +766,6 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) addInstruction(call); } -void InstructionSelection::callBuiltinRethrow() -{ - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_rethrow; - addInstruction(call); -} - void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) { Instruction::CallBuiltin call; diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 6cac936534..8225721190 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -34,7 +34,6 @@ protected: virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); virtual void callBuiltinDeleteValue(IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinRethrow(); virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); virtual void callBuiltinDeleteExceptionHandler(); virtual void callBuiltinGetException(IR::Temp *result); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index dff4db68a8..fab02c4dc8 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -249,9 +249,6 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co TRACE(builtin_throw, "Throwing now...%s", ""); __qmljs_builtin_throw(TEMP(instr.argTemp), context); break; - case Instr::instr_callBuiltin::builtin_rethrow: - __qmljs_builtin_rethrow(context); - break; case Instr::instr_callBuiltin::builtin_create_exception_handler: { TRACE(builtin_create_exception_handler, "%s", ""); void *buf = __qmljs_create_exception_handler(context); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index f23a93e73a..1accdea488 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -900,12 +900,6 @@ void __qmljs_builtin_throw(Value val, ExecutionContext *context) __qmljs_throw(val, context); } -void __qmljs_builtin_rethrow(ExecutionContext *context) -{ - __qmljs_throw(context->engine->exception, context); -} - - void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx) { Object *obj = __qmljs_to_object(o, ctx).asObject(); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 3bc545d994..07d874f490 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -2347,6 +2347,10 @@ bool Codegen::visit(ThrowStatement *ast) bool Codegen::visit(TryStatement *ast) { + if (_function->isStrict && ast->catchExpression && + (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) + throwSyntaxError(ast->catchExpression->identifierToken, QCoreApplication::translate("qv4codegen", "Catch variable name may not be eval or arguments in strict mode")); + IR::BasicBlock *tryBody = _function->newBasicBlock(); IR::BasicBlock *catchBody = ast->catchExpression ? _function->newBasicBlock() : 0; // We always need a finally body to clean up the exception handler @@ -2421,17 +2425,18 @@ bool Codegen::visit(TryStatement *ast) _block = finallyBody; _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), deleteExceptionArgs)); + int exception_to_rethrow = _block->newTemp(); + move(_block->TEMP(exception_to_rethrow), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0)); + if (ast->finallyExpression && ast->finallyExpression->statement) statement(ast->finallyExpression->statement); - if (!catchBody) { - IR::BasicBlock *rethrowBlock = _function->newBasicBlock(); - _block->CJUMP(_block->TEMP(hasException), rethrowBlock, after); - _block = rethrowBlock; - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_rethrow, 0, 0), 0)); - } else { - _block->CJUMP(_block->TEMP(hasException), _throwBlock, after); - } + IR::BasicBlock *rethrowBlock = _function->newBasicBlock(); + _block->CJUMP(_block->TEMP(hasException), rethrowBlock, after); + _block = rethrowBlock; + move(_block->TEMP(_returnAddress), _block->TEMP(exception_to_rethrow)); + _block->JUMP(_throwBlock); + _block = after; return false; diff --git a/qv4ir.cpp b/qv4ir.cpp index 68976fb39d..30d2dcb634 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -226,8 +226,6 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_delete"; case Name::builtin_throw: return "builtin_throw"; - case Name::builtin_rethrow: - return "builtin_rethrow"; case Name::builtin_create_exception_handler: return "builtin_create_exception_handler"; case Name::builtin_delete_exception_handler: diff --git a/qv4ir_p.h b/qv4ir_p.h index 03ac2ad29a..f3db3bd66d 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -276,7 +276,6 @@ struct Name: Expr { builtin_typeof, builtin_delete, builtin_throw, - builtin_rethrow, builtin_create_exception_handler, builtin_delete_exception_handler, builtin_get_exception, diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 78e54b680b..dbd6084033 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -80,7 +80,6 @@ public: // methods from InstructionSelection: virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); virtual void callBuiltinDeleteValue(IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinRethrow(); virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); virtual void callBuiltinDeleteExceptionHandler(); virtual void callBuiltinGetException(IR::Temp *result); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 2e60f720aa..844f7864ae 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -483,11 +483,6 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, arg, Assembler::ContextRegister); } -void InstructionSelection::callBuiltinRethrow() -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_rethrow, Assembler::ContextRegister); -} - void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) { generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_create_exception_handler, Assembler::ContextRegister); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 2300377c65..c0fdabf61c 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -662,7 +662,6 @@ protected: virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); virtual void callBuiltinDeleteValue(IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinRethrow(); virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); virtual void callBuiltinDeleteExceptionHandler(); virtual void callBuiltinGetException(IR::Temp *result); diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 9a694ae9a1..c4df10a2ef 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -268,10 +268,6 @@ void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result) callBuiltinThrow(arg); } return; - case IR::Name::builtin_rethrow: - callBuiltinRethrow(); - return; - case IR::Name::builtin_create_exception_handler: callBuiltinCreateExceptionHandler(result); return; diff --git a/qv4isel_p.h b/qv4isel_p.h index 8181853bd6..1d71055136 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -90,7 +90,6 @@ public: // to implement by subclasses: virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result) = 0; virtual void callBuiltinDeleteValue(IR::Temp *result) = 0; virtual void callBuiltinThrow(IR::Temp *arg) = 0; - virtual void callBuiltinRethrow() = 0; virtual void callBuiltinCreateExceptionHandler(IR::Temp *result) = 0; virtual void callBuiltinDeleteExceptionHandler() = 0; virtual void callBuiltinGetException(IR::Temp *result) = 0; diff --git a/tests/TestExpectations b/tests/TestExpectations index fc90235157..427e158a2d 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -119,11 +119,6 @@ S11.9.4_A2.4_T1 failing S11.9.4_A2.4_T3 failing S11.9.5_A2.4_T1 failing S11.9.5_A2.4_T3 failing -S12.14_A7_T1 failing -S12.14_A7_T3 failing -12.14.1-1-s failing -12.14.1-2-s failing -12.14.1-3-s failing S12.10_A3.11_T1 failing S12.10_A3.11_T2 failing S12.10_A3.11_T3 failing -- cgit v1.2.3 From cb2ee6807758291d2834ee0c4e04a58c712f1b37 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 Jan 2013 10:20:12 +0100 Subject: Skip Intl tests for now and re-generate TestExpectations Change-Id: I178222506bf697451a3b83c8595bfb0e88b71eae Reviewed-by: Lars Knoll --- tests/TestExpectations | 189 ++++++++----------------------------------------- tests/test262.py | 2 +- 2 files changed, 29 insertions(+), 162 deletions(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index 427e158a2d..9efb8a81f6 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -6,7 +6,7 @@ S15.1.3.2_A2.5_T1 # uses octal number 15.2.3.6-2-17-1 failing -# Tests failing that are supposed to pass. + 10.4.2-1-2 failing 10.4.3-1-104 failing 10.4.3-1-106 failing @@ -73,12 +73,12 @@ S11.3.2_A4_T4 failing 11.4.1-5-2 failing S11.4.1_A1 failing S11.4.1_A2.1 failing +S11.5.1_A2.4_T1 failing +S11.5.1_A2.4_T3 failing 11.4.4-2-1-s failing 11.4.4-2-2-s failing 11.4.5-2-1-s failing 11.4.5-2-2-s failing -S11.5.1_A2.4_T1 failing -S11.5.1_A2.4_T3 failing S11.5.2_A2.4_T1 failing S11.5.2_A2.4_T3 failing S11.5.3_A2.4_T1 failing @@ -87,14 +87,14 @@ S11.5.3_A3_T1.4 failing S11.5.3_A3_T2.3 failing S11.5.3_A3_T2.9 failing S11.5.3_A4_T2 failing -S11.7.1_A2.4_T1 failing -S11.7.1_A2.4_T3 failing -S11.7.2_A2.4_T1 failing -S11.7.2_A2.4_T3 failing S11.6.1_A2.4_T1 failing S11.6.1_A2.4_T3 failing S11.6.2_A2.4_T1 failing S11.6.2_A2.4_T3 failing +S11.7.1_A2.4_T1 failing +S11.7.1_A2.4_T3 failing +S11.7.2_A2.4_T1 failing +S11.7.2_A2.4_T3 failing S11.8.2_A2.4_T1 failing S11.8.2_A2.4_T3 failing S11.8.3_A2.4_T1 failing @@ -119,6 +119,18 @@ S11.9.4_A2.4_T1 failing S11.9.4_A2.4_T3 failing S11.9.5_A2.4_T1 failing S11.9.5_A2.4_T3 failing +12.10-0-3 failing +S12.10_A1.11_T1 failing +S12.10_A1.11_T2 failing +S12.10_A1.11_T4 failing +S12.10_A1.12_T1 failing +S12.10_A1.12_T2 failing +S12.10_A1.12_T4 failing +S12.10_A1.4_T4 failing +S12.10_A1.4_T5 failing +S12.10_A1.5_T4 failing +S12.10_A1.5_T5 failing +12.14-13 failing S12.10_A3.11_T1 failing S12.10_A3.11_T2 failing S12.10_A3.11_T3 failing @@ -133,18 +145,6 @@ S12.10_A3.4_T4 failing S12.10_A3.4_T5 failing S12.10_A3.5_T4 failing S12.10_A3.5_T5 failing -12.14-13 failing -12.10-0-3 failing -S12.10_A1.11_T1 failing -S12.10_A1.11_T2 failing -S12.10_A1.11_T4 failing -S12.10_A1.12_T1 failing -S12.10_A1.12_T2 failing -S12.10_A1.12_T4 failing -S12.10_A1.4_T4 failing -S12.10_A1.4_T5 failing -S12.10_A1.5_T4 failing -S12.10_A1.5_T5 failing S13_A15_T4 failing S13_A3_T1 failing 13.1-11-s failing @@ -183,14 +183,23 @@ S15.12.2_A1 failing 15.4.4.14-9-a-17 failing 15.4.4.14-9-a-7 failing 15.4.4.14-9-a-9 failing +15.4.4.14-9-b-i-5 failing 15.4.4.15-8-b-i-5 failing +15.4.4.16-7-c-i-6 failing +15.4.4.17-7-c-i-6 failing +15.4.4.18-7-c-i-6 failing 15.4.4.19-5-1 failing +15.4.4.19-8-c-i-6 failing +15.4.4.20-9-c-i-6 failing +15.4.4.21-8-b-iii-1-6 failing 15.4.4.21-9-c-i-6 failing +15.4.4.22-8-b-iii-1-6 failing 15.4.4.22-9-c-i-6 failing S15.4.4.3_A1_T1 failing S15.4.4.3_A3_T1 failing 15.4.4.4-5-b-iii-3-b-1 failing 15.4.4.4-5-c-i-1 failing +S15.4.4.4_A1_T2 failing S15.4.4.4_A2_T1 failing S15.4.4.4_A2_T2 failing S15.4.4.4_A3_T1 failing @@ -198,135 +207,6 @@ S15.5.4.11_A5_T1 failing 15.5.4.20-4-1 failing S15.5.4.8_A1_T4 failing S15.7.4.5_A1.4_T01 failing -6.4_c failing -8.0_L15 failing -9.1_a failing -9.1_b failing -9.2.1_1 failing -9.2.1_2 failing -9.2.1_3 failing -9.2.1_4 failing -9.2.1_8_c_ii failing -9.2.1_8_c_vi failing -9.2.2 failing -9.2.3_5 failing -9.2.5_11_g_ii_2 failing -9.2.5_6 failing -9.2.6_2 failing -9.2.6_4 failing -9.2.6_4_b failing -9.2.6_4_c failing -9.2.8_1_c failing -9.2.8_4 failing -10.1.1_1 failing -10.1.1_10 failing -10.1.1_11 failing -10.1.1_13 failing -10.1.1_19_b failing -10.1.1_19_c failing -10.1.1_20 failing -10.1.1_23 failing -10.1.1_6 failing -10.1.2_a failing -10.1.3 failing -10.1_L15 failing -10.2.1 failing -10.2.2 failing -10.2.2_L15 failing -10.2.3_b failing -10.3.1 failing -10.3.2_1_a_L15 failing -10.3.2_1_c failing -10.3.2_CS_a failing -10.3.2_CS_b_NN failing -10.3.2_CS_c_NN failing -10.3.2_CS_d_NN failing -10.3.2_L15 failing -10.3.3 failing -10.3.3_L15 failing -10.3_L15 failing -10.3_a failing -10.3_b failing -10.4_a failing -11.1.1_1 failing -11.1.1_15 failing -11.1.1_17 failing -11.1.1_19 failing -11.1.1_20_c failing -11.1.1_21 failing -11.1.1_34 failing -11.1.1_6 failing -11.1.1_7 failing -11.1.2 failing -11.1.3 failing -11.1_L15 failing -11.2.1 failing -11.2.2 failing -11.2.2_L15 failing -11.2.3_b failing -11.3.1 failing -11.3.2_1_a_L15 failing -11.3.2_1_a_ii failing -11.3.2_1_c failing -11.3.2_FN_1 failing -11.3.2_FN_2 failing -11.3.2_FN_3_b failing -11.3.2_L15 failing -11.3.2_TRF failing -11.3.2_TRP failing -11.3.3 failing -11.3.3_L15 failing -11.3_L15 failing -11.3_a failing -11.3_b failing -11.4_a failing -12.1.1_1 failing -12.1.1_18 failing -12.1.1_22 failing -12.1.1_23 failing -12.1.1_25 failing -12.1.1_5 failing -12.1.1_6 failing -12.1.1_TDTO failing -12.1.2 failing -12.1.3 failing -12.1_L15 failing -12.2.1 failing -12.2.2 failing -12.2.2_L15 failing -12.2.3_b failing -12.2.3_c failing -12.3.1 failing -12.3.2_1_a_L15 failing -12.3.2_1_c failing -12.3.2_FDT_1 failing -12.3.2_FDT_7_a_iv failing -12.3.2_L15 failing -12.3.2_TLT_2 failing -12.3.3 failing -12.3.3_L15 failing -12.3_L15 failing -12.3_a failing -12.3_b failing -12.4_a failing -13.1.1_6 failing -13.1.1_7 failing -13.2.1_4 failing -13.2.1_5 failing -13.3.0_2 failing -13.3.0_6 failing -13.3.0_7 failing -6.2.2_a failing -6.2.2_b failing -6.2.2_c failing -6.2.3 failing -6.2.4 failing -6.3.1_a failing -6.3.1_b failing -6.4_a failing -6.4_b failing - -# Tests that are passing but are supposed to fail (@negative) Sbp_12.5_A9_T3 failing Sbp_12.6.2_A13_T3 failing Sbp_7.8.4_A6.1_T4 failing @@ -350,16 +230,3 @@ S12.9_A1_T7 failing S12.9_A1_T8 failing S12.9_A1_T9 failing S15.2.4.4_A14 failing - -# Array regressions -S15.4.4.4_A1_T2 failing -15.4.4.18-7-c-i-6 failing -15.4.4.19-8-c-i-6 failing -15.4.4.20-9-c-i-6 failing -15.4.4.22-8-b-iii-1-6 failing -15.4.4.14-9-b-i-5 failing -15.4.4.16-7-c-i-6 failing -15.4.4.17-7-c-i-6 failing -15.4.4.21-8-b-iii-1-6 failing -15.12.3_4-1-1 -15.12.3_4-1-3 diff --git a/tests/test262.py b/tests/test262.py index 09a3931ad5..607fca7266 100755 --- a/tests/test262.py +++ b/tests/test262.py @@ -431,7 +431,7 @@ class TestSuite(object): else: logging.warning("Unexpected path %s", full_path) rel_path = full_path - if self.ShouldRun(rel_path, tests): + if self.ShouldRun(rel_path, tests) and not rel_path.startswith("intl402/"): basename = path.basename(full_path)[:-3] name = rel_path.split(path.sep)[:-1] + [basename] if EXCLUDE_LIST.count(basename) >= 1 or self.expectations.testsToSkip.count(basename) >= 1: -- cgit v1.2.3 From 8f77d5d35e9e876896357bba162aeba57f659fbe Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 24 Jan 2013 11:56:26 +0100 Subject: Fix compilation of (incomplete) llvm backend. Change-Id: Ib87c3427cfd937516a58849540e0e668a28cb8dc Reviewed-by: Lars Knoll --- qv4isel_llvm.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 79458be365..e915abda8e 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -370,13 +370,6 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) Q_UNREACHABLE(); } -void InstructionSelection::callBuiltinRethrow() -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) { // TODO -- cgit v1.2.3 From 7afb2e22466821f1e292c8b39edb79f1fa7c4819 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 Jan 2013 11:58:58 +0100 Subject: Fix evaluation of lhs in binary expressions The entire left hand side of a binary expression must be evaluated before the right hand side. In case of for example x !== (x = 1) it is important to generate the code for looking up "x" from the lhs before dealing with the rhs, because this overall expression is supposed to throw a reference error. Change-Id: I03aee3257ff7b7a60aa789dba9f0445c387a1ad5 Reviewed-by: Lars Knoll --- qv4codegen.cpp | 49 ++++++++++++++++++++++++++++++++++--------------- tests/TestExpectations | 44 +------------------------------------------- 2 files changed, 35 insertions(+), 58 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 07d874f490..b93e740b24 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1120,32 +1120,33 @@ bool Codegen::visit(BinaryExpression *ast) return false; } - Result left = expression(ast->left); + IR::Expr* left = *expression(ast->left); if (_function->isStrict) { if (IR::Name *n = left->asName()) if (*n->id == QLatin1String("eval") || *n->id == QLatin1String("arguments")) throwSyntaxError(ast->left->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); } - Result right = expression(ast->right); switch (ast->op) { case QSOperator::Or: case QSOperator::And: break; - case QSOperator::Assign: + case QSOperator::Assign: { + IR::Expr* right = *expression(ast->right); if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) throwSyntaxError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of assignment operator is not an lvalue")); if (_expr.accept(nx)) { - move(*left, *right); + move(left, right); } else { const unsigned t = _block->newTemp(); - move(_block->TEMP(t), *right); - move(*left, _block->TEMP(t)); - _expr.code = *left; + move(_block->TEMP(t), right); + move(left, _block->TEMP(t)); + _expr.code = left; } break; + } case QSOperator::InplaceAnd: case QSOperator::InplaceSub: @@ -1158,16 +1159,17 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::InplaceRightShift: case QSOperator::InplaceURightShift: case QSOperator::InplaceXor: { + IR::Expr* right = *expression(ast->right); if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) throwSyntaxError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of inplace operator is not an lvalue")); if (_expr.accept(nx)) { - move(*left, *right, baseOp(ast->op)); + move(left, right, baseOp(ast->op)); } else { const unsigned t = _block->newTemp(); - move(_block->TEMP(t), *right); - move(*left, _block->TEMP(t), baseOp(ast->op)); - _expr.code = *left; + move(_block->TEMP(t), right); + move(left, _block->TEMP(t), baseOp(ast->op)); + _expr.code = left; } break; } @@ -1181,11 +1183,19 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::Le: case QSOperator::Lt: case QSOperator::StrictEqual: - case QSOperator::StrictNotEqual: + case QSOperator::StrictNotEqual: { + if (!left->asTemp() && !left->asConst()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + IR::Expr* right = *expression(ast->right); + if (_expr.accept(cx)) { - cjump(binop(IR::binaryOperator(ast->op), *left, *right), _expr.iftrue, _expr.iffalse); + cjump(binop(IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse); } else { - IR::Expr *e = binop(IR::binaryOperator(ast->op), *left, *right); + IR::Expr *e = binop(IR::binaryOperator(ast->op), left, right); if (e->asConst() || e->asString()) _expr.code = e; else { @@ -1195,6 +1205,7 @@ bool Codegen::visit(BinaryExpression *ast) } } break; + } case QSOperator::Add: case QSOperator::BitAnd: @@ -1207,7 +1218,15 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::RShift: case QSOperator::Sub: case QSOperator::URShift: { - IR::Expr *e = binop(IR::binaryOperator(ast->op), *left, *right); + if (!left->asTemp() && !left->asConst()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + IR::Expr* right = *expression(ast->right); + + IR::Expr *e = binop(IR::binaryOperator(ast->op), left, right); if (e->asConst() || e->asString()) _expr.code = e; else { diff --git a/tests/TestExpectations b/tests/TestExpectations index 9efb8a81f6..61ab662bf7 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -15,12 +15,6 @@ S15.1.3.2_A2.5_T1 10.4.3-1-82-s failing 11.1.4_4-5-1 failing 11.1.4_5-6-1 failing -S11.10.1_A2.4_T1 failing -S11.10.1_A2.4_T3 failing -S11.10.2_A2.4_T1 failing -S11.10.2_A2.4_T3 failing -S11.10.3_A2.4_T1 failing -S11.10.3_A2.4_T3 failing S11.11.1_A2.1_T1 failing S11.11.1_A3_T2 failing S11.11.1_A3_T3 failing @@ -73,52 +67,16 @@ S11.3.2_A4_T4 failing 11.4.1-5-2 failing S11.4.1_A1 failing S11.4.1_A2.1 failing -S11.5.1_A2.4_T1 failing -S11.5.1_A2.4_T3 failing 11.4.4-2-1-s failing 11.4.4-2-2-s failing 11.4.5-2-1-s failing 11.4.5-2-2-s failing -S11.5.2_A2.4_T1 failing -S11.5.2_A2.4_T3 failing -S11.5.3_A2.4_T1 failing -S11.5.3_A2.4_T3 failing S11.5.3_A3_T1.4 failing S11.5.3_A3_T2.3 failing S11.5.3_A3_T2.9 failing S11.5.3_A4_T2 failing -S11.6.1_A2.4_T1 failing -S11.6.1_A2.4_T3 failing -S11.6.2_A2.4_T1 failing -S11.6.2_A2.4_T3 failing -S11.7.1_A2.4_T1 failing -S11.7.1_A2.4_T3 failing -S11.7.2_A2.4_T1 failing -S11.7.2_A2.4_T3 failing -S11.8.2_A2.4_T1 failing -S11.8.2_A2.4_T3 failing -S11.8.3_A2.4_T1 failing -S11.8.3_A2.4_T3 failing -S11.7.3_A2.4_T1 failing -S11.7.3_A2.4_T3 failing -S11.8.1_A2.4_T1 failing -S11.8.1_A2.4_T3 failing -S11.8.4_A2.4_T1 failing -S11.8.4_A2.4_T3 failing -S11.8.6_A2.4_T1 failing -S11.8.6_A2.4_T3 failing S11.8.6_A3 failing S11.8.6_A5_T2 failing -S11.8.7_A2.4_T1 failing -S11.8.7_A2.4_T3 failing -S11.9.1_A2.4_T1 failing -S11.9.1_A2.4_T3 failing -S11.9.2_A2.4_T1 failing -S11.9.2_A2.4_T3 failing -S11.9.4_A2.4_T1 failing -S11.9.4_A2.4_T3 failing -S11.9.5_A2.4_T1 failing -S11.9.5_A2.4_T3 failing 12.10-0-3 failing S12.10_A1.11_T1 failing S12.10_A1.11_T2 failing @@ -229,4 +187,4 @@ S12.9_A1_T6 failing S12.9_A1_T7 failing S12.9_A1_T8 failing S12.9_A1_T9 failing -S15.2.4.4_A14 failing +S15.2.4.4_A14 failing \ No newline at end of file -- cgit v1.2.3 From bea4a7d8c5ed308d15d6a55cbd7e0ff18689b694 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 Jan 2013 12:26:44 +0100 Subject: Fix build on Mac OS X Use std::isnan instead of isnan, like everywhere else in the file. Change-Id: Ie58288b0a8d2043dc88054babe3beee78007cd15 Reviewed-by: Simon Hausmann --- qv4dateobject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4dateobject.cpp b/qv4dateobject.cpp index 3038e7205f..b9a4446087 100644 --- a/qv4dateobject.cpp +++ b/qv4dateobject.cpp @@ -1225,7 +1225,7 @@ Value DatePrototype::method_setFullYear(ExecutionContext *ctx) ctx->throwTypeError(); double t = LocalTime(self->value.asDouble()); - if (isnan(t)) + if (std::isnan(t)) t = 0; double year = ctx->argument(0).toNumber(ctx); double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); -- cgit v1.2.3 From 6b566d815f6fbe281233286fd3d4f8138aca088f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 24 Jan 2013 11:58:46 +0100 Subject: Create a real execution context for with() statements This is required for full spec compliance. Change-Id: I0c1a3ed249a458032d29ba80650a5fdc2eac5c01 Reviewed-by: Simon Hausmann --- moth/qv4vme_moth.cpp | 4 +- qmljs_engine.h | 1 - qmljs_environment.cpp | 146 +++++++++++++++++++++++++++---------------------- qmljs_environment.h | 12 ++-- qmljs_runtime.cpp | 18 ++---- qmljs_runtime.h | 4 +- qv4isel_masm.cpp | 4 +- qv4mm.cpp | 5 +- tests/TestExpectations | 25 +-------- 9 files changed, 101 insertions(+), 118 deletions(-) diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index fab02c4dc8..0a1b3a2594 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -276,10 +276,10 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co TEMP(instr.targetTempIndex) = __qmljs_get_exception(context); break; case Instr::instr_callBuiltin::builtin_push_with: - __qmljs_builtin_push_with(TEMP(instr.argTemp), context); + context = __qmljs_builtin_push_with(TEMP(instr.argTemp), context); break; case Instr::instr_callBuiltin::builtin_pop_with: - __qmljs_builtin_pop_with(context); + context = __qmljs_builtin_pop_with(context); break; default: assert(!"TODO!"); diff --git a/qmljs_engine.h b/qmljs_engine.h index 2b8b8b5cff..7bf3957bae 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -159,7 +159,6 @@ struct ExecutionEngine struct ExceptionHandler { ExecutionContext *context; - ExecutionContext::With *with; const uchar *code; // Interpreter state int targetTempIndex; // Interpreter state jmp_buf stackFrame; diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 9d81bb7ce9..627a988b70 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -170,26 +170,22 @@ bool ExecutionContext::deleteBinding(ExecutionContext *scope, String *name) return false; } -void ExecutionContext::pushWithObject(Object *with) +ExecutionContext *ExecutionContext::pushWithObject(Object *with) { - With *w = new With; - w->next = withObject; - w->object = with; - withObject = w; + ExecutionContext *withCtx = engine->newContext(); + withCtx->init(this, with); + engine->current = withCtx; + return withCtx; } -void ExecutionContext::popWithObject() +ExecutionContext *ExecutionContext::popWithObject() { - assert(withObject); + assert(engine->current == this); + assert(withObject != 0); - With *w = withObject; - withObject = w->next; - delete w; -} - -ExecutionContext *ExecutionContext::outer() const -{ - return function ? function->scope : 0; + engine->current = parent; + parent = 0; + return engine->current; } String **ExecutionContext::formals() const @@ -217,6 +213,7 @@ void ExecutionContext::init(ExecutionEngine *eng) { engine = eng; parent = 0; + outer = 0; thisObject = eng->globalObject; function = 0; @@ -230,6 +227,22 @@ void ExecutionContext::init(ExecutionEngine *eng) eng->exception = Value::undefinedValue(); } +void ExecutionContext::init(ExecutionContext *p, Object *with) +{ + engine = p->engine; + parent = p; + outer = p; + thisObject = p->thisObject; + + function = 0; + arguments = 0; + argumentCount = 0; + locals = 0; + strictMode = false; + activation = 0; + withObject = with; +} + void ExecutionContext::destroy() { delete[] arguments; @@ -238,17 +251,14 @@ void ExecutionContext::destroy() bool ExecutionContext::deleteProperty(String *name) { - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { if (ctx->withObject) { - ExecutionContext::With *w = ctx->withObject; - while (w) { - if (w->object->__hasProperty__(this, name)) - return w->object->__delete__(this, name); - w = w->next; - } + if (ctx->withObject->__hasProperty__(this, name)) + return ctx->withObject->__delete__(this, name); + } else { + if (ctx->activation && ctx->activation->__hasProperty__(this, name)) + return ctx->activation->__delete__(this, name); } - if (ctx->activation && ctx->activation->__hasProperty__(this, name)) - return ctx->activation->__delete__(this, name); } if (strictMode) throwSyntaxError(0); @@ -257,19 +267,20 @@ bool ExecutionContext::deleteProperty(String *name) void ExecutionContext::setProperty(String *name, Value value) { - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { - if (ctx->withObject) { - With *w = ctx->withObject; - while (w) { - if (w->object->__hasProperty__(ctx, name)) { - w->object->__put__(ctx, name, value); - return; - } - w = w->next; +// qDebug() << "=== SetProperty" << value.toString(this)->toQString(); + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (Object *w = ctx->withObject) { +// qDebug() << ctx << "hasWith"; + if (w->__hasProperty__(ctx, name)) { +// qDebug() << " withHasProp"; + w->__put__(ctx, name, value); + return; } + } else { +// qDebug() << ctx << "setting mutable binding"; + if (ctx->setMutableBinding(this, name, value)) + return; } - if (ctx->setMutableBinding(this, name, value)) - return; } if (strictMode || name == engine->id_this) throwReferenceError(Value::fromString(name)); @@ -281,19 +292,23 @@ Value ExecutionContext::getProperty(String *name) if (name == engine->id_this) return thisObject; - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { - if (With *w = ctx->withObject) { - while (w) { - bool hasProperty = false; - Value v = w->object->__get__(ctx, name, &hasProperty); - if (hasProperty) - return v; - w = w->next; + bool hasWith = false; +// qDebug() << "=== getProperty" << name->toQString(); + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (Object *w = ctx->withObject) { + hasWith = true; +// qDebug() << ctx << "hasWith"; + bool hasProperty = false; + Value v = w->__get__(ctx, name, &hasProperty); + if (hasProperty) { +// qDebug() << " withHasProp"; + return v; } + continue; } if (FunctionObject *f = ctx->function) { - if (f->needsActivation || ctx->withObject) { + if (f->needsActivation || hasWith) { for (unsigned int i = 0; i < f->varCount; ++i) if (f->varList[i]->isEqualTo(name)) return ctx->locals[i]; @@ -318,19 +333,19 @@ Value ExecutionContext::getPropertyNoThrow(String *name) if (name == engine->id_this) return thisObject; - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { - if (With *w = ctx->withObject) { - while (w) { - bool hasProperty = false; - Value v = w->object->__get__(ctx, name, &hasProperty); - if (hasProperty) - return v; - w = w->next; - } + bool hasWith = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (Object *w = ctx->withObject) { + hasWith = true; + bool hasProperty = false; + Value v = w->__get__(ctx, name, &hasProperty); + if (hasProperty) + return v; + continue; } if (FunctionObject *f = ctx->function) { - if (f->needsActivation || ctx->withObject) { + if (f->needsActivation || hasWith) { for (unsigned int i = 0; i < f->varCount; ++i) if (f->varList[i]->isEqualTo(name)) return ctx->locals[i]; @@ -356,21 +371,21 @@ Value ExecutionContext::getPropertyAndBase(String *name, Object **base) if (name == engine->id_this) return thisObject; - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { - if (With *w = ctx->withObject) { - while (w) { - bool hasProperty = false; - Value v = w->object->__get__(ctx, name, &hasProperty); - if (hasProperty) { - *base = w->object; - return v; - } - w = w->next; + bool hasWith = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (Object *w = ctx->withObject) { + hasWith = true; + bool hasProperty = false; + Value v = w->__get__(ctx, name, &hasProperty); + if (hasProperty) { + *base = w; + return v; } + continue; } if (FunctionObject *f = ctx->function) { - if (f->needsActivation || ctx->withObject) { + if (f->needsActivation || hasWith) { for (unsigned int i = 0; i < f->varCount; ++i) if (f->varList[i]->isEqualTo(name)) return ctx->locals[i]; @@ -451,6 +466,7 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha engine = parent->engine; this->parent = parent; + outer = f->scope; engine->current = this; function = f; diff --git a/qmljs_environment.h b/qmljs_environment.h index b5f6802bf9..16b883270b 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -74,6 +74,7 @@ struct ExecutionContext { ExecutionEngine *engine; ExecutionContext *parent; + ExecutionContext *outer; Value thisObject; FunctionObject *function; @@ -86,17 +87,14 @@ struct ExecutionContext unsigned int formalCount() const; String **variables() const; unsigned int variableCount() const; - ExecutionContext *outer() const; bool strictMode; Object *activation; - struct With { - Object *object; - With *next; - } *withObject; + Object *withObject; void init(ExecutionEngine *e); + void init(ExecutionContext *p, Object *with); void destroy(); bool hasBinding(String *name) const; @@ -105,8 +103,8 @@ struct ExecutionContext Value getBindingValue(ExecutionContext *scope, String *name, bool strict) const; bool deleteBinding(ExecutionContext *ctx, String *name); - void pushWithObject(Object *with); - void popWithObject(); + ExecutionContext *pushWithObject(Object *with); + ExecutionContext *popWithObject(); void initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc); void leaveCallContext(); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 1accdea488..a8610b2bac 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -813,16 +813,11 @@ void __qmljs_throw(Value value, ExecutionContext *context) // clean up call contexts while (context != handler.context) { ExecutionContext *parent = context->parent; - context->leaveCallContext(); + if (!context->withObject) + context->leaveCallContext(); context = parent; } - while (context->withObject != handler.with) { - ExecutionContext::With *w = context->withObject; - context->withObject = w->next; - delete w; - } - context->engine->exception = value; longjmp(handler.stackFrame, 1); @@ -834,7 +829,6 @@ void *__qmljs_create_exception_handler(ExecutionContext *context) context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler()); ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); handler.context = context; - handler.with = context->withObject; return handler.stackFrame; } @@ -900,15 +894,15 @@ void __qmljs_builtin_throw(Value val, ExecutionContext *context) __qmljs_throw(val, context); } -void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx) +ExecutionContext *__qmljs_builtin_push_with(Value o, ExecutionContext *ctx) { Object *obj = __qmljs_to_object(o, ctx).asObject(); - ctx->pushWithObject(obj); + return ctx->pushWithObject(obj); } -void __qmljs_builtin_pop_with(ExecutionContext *ctx) +ExecutionContext *__qmljs_builtin_pop_with(ExecutionContext *ctx) { - ctx->popWithObject(); + return ctx->popWithObject(); } void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 1b638ef184..2d280d7c04 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -101,8 +101,8 @@ Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext * void __qmljs_builtin_throw(Value val, ExecutionContext *context); void __qmljs_builtin_rethrow(ExecutionContext *context); -void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx); -void __qmljs_builtin_pop_with(ExecutionContext *ctx); +ExecutionContext *__qmljs_builtin_push_with(Value o, ExecutionContext *ctx); +ExecutionContext *__qmljs_builtin_pop_with(ExecutionContext *ctx); void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name); void __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx); void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 844f7864ae..d202475825 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -515,12 +515,12 @@ void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR: void InstructionSelection::callBuiltinPushWith(IR::Temp *arg) { - generateFunctionCall(Assembler::Void, __qmljs_builtin_push_with, arg, Assembler::ContextRegister); + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with, arg, Assembler::ContextRegister); } void InstructionSelection::callBuiltinPopWith() { - generateFunctionCall(Assembler::Void, __qmljs_builtin_pop_with, Assembler::ContextRegister); + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_pop_with, Assembler::ContextRegister); } void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) diff --git a/qv4mm.cpp b/qv4mm.cpp index f624cec7fd..895026da57 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -345,9 +345,8 @@ void MemoryManager::collectRoots(QVector &roots) const add(roots, ctxt->locals[local]); if (ctxt->activation) roots.append(ctxt->activation); - for (ExecutionContext::With *it = ctxt->withObject; it; it = it->next) - if (it->object) - roots.append(it->object); + if (ctxt->withObject) + roots.append(ctxt->withObject); } for (int i = 0; i < m_d->engine->functions.size(); ++i) { diff --git a/tests/TestExpectations b/tests/TestExpectations index 61ab662bf7..faec6299d5 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -77,28 +77,11 @@ S11.5.3_A3_T2.9 failing S11.5.3_A4_T2 failing S11.8.6_A3 failing S11.8.6_A5_T2 failing -12.10-0-3 failing -S12.10_A1.11_T1 failing -S12.10_A1.11_T2 failing -S12.10_A1.11_T4 failing -S12.10_A1.12_T1 failing -S12.10_A1.12_T2 failing -S12.10_A1.12_T4 failing S12.10_A1.4_T4 failing S12.10_A1.4_T5 failing S12.10_A1.5_T4 failing S12.10_A1.5_T5 failing 12.14-13 failing -S12.10_A3.11_T1 failing -S12.10_A3.11_T2 failing -S12.10_A3.11_T3 failing -S12.10_A3.11_T4 failing -S12.10_A3.11_T5 failing -S12.10_A3.12_T1 failing -S12.10_A3.12_T2 failing -S12.10_A3.12_T3 failing -S12.10_A3.12_T4 failing -S12.10_A3.12_T5 failing S12.10_A3.4_T4 failing S12.10_A3.4_T5 failing S12.10_A3.5_T4 failing @@ -117,12 +100,6 @@ S13_A3_T1 failing 13.1-40-s failing 13.1-41-s failing 13.1-42-s failing -S13.2.2_A19_T1 failing -S13.2.2_A19_T3 failing -S13.2.2_A19_T4 failing -S13.2.2_A19_T5 failing -S13.2.2_A19_T6 failing -S13.2.2_A19_T8 failing S13.2.3_A1 failing S14_A2 failing 14.1-5-s failing @@ -187,4 +164,4 @@ S12.9_A1_T6 failing S12.9_A1_T7 failing S12.9_A1_T8 failing S12.9_A1_T9 failing -S15.2.4.4_A14 failing \ No newline at end of file +S15.2.4.4_A14 failing -- cgit v1.2.3 From 2a877ea0dbeb3e5f7d9d9d2e354da7bd86d962b4 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 24 Jan 2013 12:13:47 +0100 Subject: Rename some builtin methods rename push_with to push_with_scope and pop_with to pop_scope. This is to prepare for adding a push_catch_scope. Change-Id: I73d30d097fc50f4a00d94f5927d154292bcd4482 Reviewed-by: Simon Hausmann --- moth/qv4instr_moth_p.h | 4 ++-- moth/qv4isel_moth.cpp | 8 ++++---- moth/qv4isel_moth_p.h | 4 ++-- moth/qv4vme_moth.cpp | 8 ++++---- qmljs_environment.cpp | 4 ++-- qmljs_environment.h | 4 ++-- qmljs_runtime.cpp | 8 ++++---- qmljs_runtime.h | 4 ++-- qv4codegen.cpp | 4 ++-- qv4ir.cpp | 8 ++++---- qv4ir_p.h | 4 ++-- qv4isel_llvm.cpp | 4 ++-- qv4isel_llvm_p.h | 4 ++-- qv4isel_masm.cpp | 8 ++++---- qv4isel_masm_p.h | 4 ++-- qv4isel_p.cpp | 8 ++++---- qv4isel_p.h | 4 ++-- 17 files changed, 46 insertions(+), 46 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 07016cca00..a2863ef389 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -167,8 +167,8 @@ union Instr builtin_create_exception_handler, builtin_delete_exception_handler, builtin_get_exception, - builtin_push_with, - builtin_pop_with + builtin_push_with_scope, + builtin_pop_scope } builtin; int argTemp; int targetTempIndex; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 2626955cfc..2246442cf2 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -805,18 +805,18 @@ void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR: addInstruction(call); } -void InstructionSelection::callBuiltinPushWith(IR::Temp *arg) +void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) { Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_push_with; + call.builtin = Instruction::CallBuiltin::builtin_push_with_scope; call.argTemp = arg->index; addInstruction(call); } -void InstructionSelection::callBuiltinPopWith() +void InstructionSelection::callBuiltinPopScope() { Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_pop_with; + call.builtin = Instruction::CallBuiltin::builtin_pop_scope; addInstruction(call); } diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 8225721190..10cb33ac0e 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -39,8 +39,8 @@ protected: virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); - virtual void callBuiltinPushWith(IR::Temp *arg); - virtual void callBuiltinPopWith(); + virtual void callBuiltinPushWithScope(IR::Temp *arg); + virtual void callBuiltinPopScope(); virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 0a1b3a2594..fbfa47d401 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -275,11 +275,11 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co case Instr::instr_callBuiltin::builtin_get_exception: TEMP(instr.targetTempIndex) = __qmljs_get_exception(context); break; - case Instr::instr_callBuiltin::builtin_push_with: - context = __qmljs_builtin_push_with(TEMP(instr.argTemp), context); + case Instr::instr_callBuiltin::builtin_push_with_scope: + context = __qmljs_builtin_push_with_scope(TEMP(instr.argTemp), context); break; - case Instr::instr_callBuiltin::builtin_pop_with: - context = __qmljs_builtin_pop_with(context); + case Instr::instr_callBuiltin::builtin_pop_scope: + context = __qmljs_builtin_pop_scope(context); break; default: assert(!"TODO!"); diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 627a988b70..a4db06a358 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -170,7 +170,7 @@ bool ExecutionContext::deleteBinding(ExecutionContext *scope, String *name) return false; } -ExecutionContext *ExecutionContext::pushWithObject(Object *with) +ExecutionContext *ExecutionContext::createWithScope(Object *with) { ExecutionContext *withCtx = engine->newContext(); withCtx->init(this, with); @@ -178,7 +178,7 @@ ExecutionContext *ExecutionContext::pushWithObject(Object *with) return withCtx; } -ExecutionContext *ExecutionContext::popWithObject() +ExecutionContext *ExecutionContext::popScope() { assert(engine->current == this); assert(withObject != 0); diff --git a/qmljs_environment.h b/qmljs_environment.h index 16b883270b..0bdbdd5715 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -103,8 +103,8 @@ struct ExecutionContext Value getBindingValue(ExecutionContext *scope, String *name, bool strict) const; bool deleteBinding(ExecutionContext *ctx, String *name); - ExecutionContext *pushWithObject(Object *with); - ExecutionContext *popWithObject(); + ExecutionContext *createWithScope(Object *with); + ExecutionContext *popScope(); void initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc); void leaveCallContext(); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index a8610b2bac..114a7b9936 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -894,15 +894,15 @@ void __qmljs_builtin_throw(Value val, ExecutionContext *context) __qmljs_throw(val, context); } -ExecutionContext *__qmljs_builtin_push_with(Value o, ExecutionContext *ctx) +ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx) { Object *obj = __qmljs_to_object(o, ctx).asObject(); - return ctx->pushWithObject(obj); + return ctx->createWithScope(obj); } -ExecutionContext *__qmljs_builtin_pop_with(ExecutionContext *ctx) +ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx) { - return ctx->popWithObject(); + return ctx->popScope(); } void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 2d280d7c04..e1c6d5a335 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -101,8 +101,8 @@ Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext * void __qmljs_builtin_throw(Value val, ExecutionContext *context); void __qmljs_builtin_rethrow(ExecutionContext *context); -ExecutionContext *__qmljs_builtin_push_with(Value o, ExecutionContext *ctx); -ExecutionContext *__qmljs_builtin_pop_with(ExecutionContext *ctx); +ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx); +ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx); void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name); void __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx); void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index b93e740b24..f16ce68f56 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -2513,11 +2513,11 @@ bool Codegen::visit(WithStatement *ast) _block->MOVE(_block->TEMP(withObject), *expression(ast->expression)); IR::ExprList *args = _function->New(); args->init(_block->TEMP(withObject)); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with, 0, 0), args)); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with_scope, 0, 0), args)); ++_function->insideWith; statement(ast->statement); --_function->insideWith; - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_with, 0, 0), 0)); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); IR::BasicBlock *next = _function->newBasicBlock(); _block->JUMP(next); diff --git a/qv4ir.cpp b/qv4ir.cpp index 30d2dcb634..969d07a843 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -236,10 +236,10 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_foreach_iterator_object"; case IR::Name::builtin_foreach_next_property_name: return "builtin_foreach_next_property_name"; - case IR::Name::builtin_push_with: - return "builtin_push_with"; - case IR::Name::builtin_pop_with: - return "builtin_pop_with"; + case IR::Name::builtin_push_with_scope: + return "builtin_push_with_scope"; + case IR::Name::builtin_pop_scope: + return "builtin_pop_scope"; case IR::Name::builtin_declare_vars: return "builtin_declare_vars"; case IR::Name::builtin_define_property: diff --git a/qv4ir_p.h b/qv4ir_p.h index f3db3bd66d..69059780c9 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -281,8 +281,8 @@ struct Name: Expr { builtin_get_exception, builtin_foreach_iterator_object, builtin_foreach_next_property_name, - builtin_push_with, - builtin_pop_with, + builtin_push_with_scope, + builtin_pop_scope, builtin_declare_vars, builtin_define_property, builtin_define_getter_setter diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index e915abda8e..6c74dc6d0d 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -405,14 +405,14 @@ void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR: Q_UNREACHABLE(); } -void InstructionSelection::callBuiltinPushWith(IR::Temp *arg) +void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) { // TODO assert(!"TODO!"); Q_UNREACHABLE(); } -void InstructionSelection::callBuiltinPopWith() +void InstructionSelection::callBuiltinPopScope() { // TODO assert(!"TODO!"); diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index dbd6084033..9dcf961171 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -85,8 +85,8 @@ public: // methods from InstructionSelection: virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); - virtual void callBuiltinPushWith(IR::Temp *arg); - virtual void callBuiltinPopWith(); + virtual void callBuiltinPushWithScope(IR::Temp *arg); + virtual void callBuiltinPopScope(); virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index d202475825..5f34c02fba 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -513,14 +513,14 @@ void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR: generateFunctionCall(result, __qmljs_foreach_next_property_name, arg); } -void InstructionSelection::callBuiltinPushWith(IR::Temp *arg) +void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) { - generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with, arg, Assembler::ContextRegister); + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with_scope, arg, Assembler::ContextRegister); } -void InstructionSelection::callBuiltinPopWith() +void InstructionSelection::callBuiltinPopScope() { - generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_pop_with, Assembler::ContextRegister); + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_pop_scope, Assembler::ContextRegister); } void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index c0fdabf61c..e45ed51592 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -667,8 +667,8 @@ protected: virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); - virtual void callBuiltinPushWith(IR::Temp *arg); - virtual void callBuiltinPopWith(); + virtual void callBuiltinPushWithScope(IR::Temp *arg); + virtual void callBuiltinPopScope(); virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index c4df10a2ef..17f846675f 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -291,14 +291,14 @@ void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result) assert(arg != 0); callBuiltinForeachNextPropertyname(arg, result); } return; - case IR::Name::builtin_push_with: { + case IR::Name::builtin_push_with_scope: { IR::Temp *arg = call->args->expr->asTemp(); assert(arg != 0); - callBuiltinPushWith(arg); + callBuiltinPushWithScope(arg); } return; - case IR::Name::builtin_pop_with: - callBuiltinPopWith(); + case IR::Name::builtin_pop_scope: + callBuiltinPopScope(); return; case IR::Name::builtin_declare_vars: { diff --git a/qv4isel_p.h b/qv4isel_p.h index 1d71055136..fc145523ac 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -95,8 +95,8 @@ public: // to implement by subclasses: virtual void callBuiltinGetException(IR::Temp *result) = 0; virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) = 0; virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) = 0; - virtual void callBuiltinPushWith(IR::Temp *arg) = 0; - virtual void callBuiltinPopWith() = 0; + virtual void callBuiltinPushWithScope(IR::Temp *arg) = 0; + virtual void callBuiltinPopScope() = 0; virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0; virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) = 0; virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) = 0; -- cgit v1.2.3 From 865a5bf47fb077b654930a84852f189676a7cff3 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 24 Jan 2013 13:12:26 +0100 Subject: Fix the remaining test failures for the with() statement Properly pop the pushed scopes for break, continue and return statements inside with() blocks. Change-Id: I7439cd7b7c70819cd5de903395e1b67c394a38c6 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 55 ++++++++++++++++++++++++++++++-------------------- qv4codegen_p.h | 16 ++++++++------- tests/TestExpectations | 10 +-------- 3 files changed, 43 insertions(+), 38 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index f16ce68f56..5ee7159220 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -413,7 +413,7 @@ Codegen::Codegen(VM::ExecutionContext *context, bool strict) , _env(0) , _loop(0) , _labelledStatement(0) - , _tryCleanup(0) + , _scopeAndFinally(0) , _context(context) , _strictMode(strict) , _debugger(context->engine->debugger) @@ -432,7 +432,7 @@ Codegen::Codegen(ErrorHandler *errorHandler, bool strictMode) , _env(0) , _loop(0) , _labelledStatement(0) - , _tryCleanup(0) + , _scopeAndFinally(0) , _context(0) , _strictMode(strictMode) , _debugger(0) @@ -515,7 +515,7 @@ void Codegen::enterLoop(Statement *node, IR::BasicBlock *breakBlock, IR::BasicBl { _loop = new Loop(node, breakBlock, continueBlock, _loop); _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement - _loop->tryCleanup = _tryCleanup; + _loop->scopeAndFinally = _scopeAndFinally; _labelledStatement = 0; } @@ -1849,7 +1849,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, { qSwap(_mode, mode); // enter function code. - TryCleanup *tryCleanup = 0; + ScopeAndFinally *scopeAndFinally = 0; enterEnvironment(ast); IR::Function *function = _module->newFunction(name, _function); @@ -1914,7 +1914,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, qSwap(_exitBlock, exitBlock); qSwap(_throwBlock, throwBlock); qSwap(_returnAddress, returnAddress); - qSwap(_tryCleanup, tryCleanup); + qSwap(_scopeAndFinally, scopeAndFinally); qSwap(_loop, loop); for (FormalParameterList *it = formals; it; it = it->next) { @@ -1948,7 +1948,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, qSwap(_exitBlock, exitBlock); qSwap(_throwBlock, throwBlock); qSwap(_returnAddress, returnAddress); - qSwap(_tryCleanup, tryCleanup); + qSwap(_scopeAndFinally, scopeAndFinally); qSwap(_loop, loop); leaveEnvironment(); @@ -2020,7 +2020,7 @@ bool Codegen::visit(BreakStatement *ast) if (!loop) throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); } - unwindException(loop->tryCleanup); + unwindException(loop->scopeAndFinally); _block->JUMP(loop->breakBlock); return false; } @@ -2046,7 +2046,7 @@ bool Codegen::visit(ContinueStatement *ast) } if (!loop) throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "continue outside of loop")); - unwindException(loop->tryCleanup); + unwindException(loop->scopeAndFinally); _block->JUMP(loop->continueBlock); return false; } @@ -2394,8 +2394,8 @@ bool Codegen::visit(TryStatement *ast) deleteExceptionArgs->next->init(_block->TEMP(inCatch)); } - TryCleanup tcf(_tryCleanup, ast->finallyExpression, deleteExceptionArgs); - _tryCleanup = &tcf; + ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression, deleteExceptionArgs); + _scopeAndFinally = &tcf; _block->CJUMP(_block->TEMP(hasException), catchBody ? catchBody : finallyBody, tryBody); @@ -2438,7 +2438,7 @@ bool Codegen::visit(TryStatement *ast) _block->JUMP(finallyBody); } - _tryCleanup = tcf.parent; + _scopeAndFinally = tcf.parent; IR::BasicBlock *after = _function->newBasicBlock(); _block = finallyBody; @@ -2461,18 +2461,23 @@ bool Codegen::visit(TryStatement *ast) return false; } -void Codegen::unwindException(Codegen::TryCleanup *outest) +void Codegen::unwindException(Codegen::ScopeAndFinally *outest) { - TryCleanup *tryCleanup = _tryCleanup; - qSwap(_tryCleanup, tryCleanup); - while (_tryCleanup != outest) { - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), _tryCleanup->deleteExceptionArgs)); - TryCleanup *tc = _tryCleanup; - _tryCleanup = tc->parent; - if (tc->finally && tc->finally->statement) - statement(tc->finally->statement); + ScopeAndFinally *scopeAndFinally = _scopeAndFinally; + qSwap(_scopeAndFinally, scopeAndFinally); + while (_scopeAndFinally != outest) { + if (_scopeAndFinally->popScope) { + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0))); + _scopeAndFinally = _scopeAndFinally->parent; + } else { + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), _scopeAndFinally->deleteExceptionArgs)); + ScopeAndFinally *tc = _scopeAndFinally; + _scopeAndFinally = tc->parent; + if (tc->finally && tc->finally->statement) + statement(tc->finally->statement); + } } - qSwap(_tryCleanup, tryCleanup); + qSwap(_scopeAndFinally, scopeAndFinally); } bool Codegen::visit(VariableStatement *ast) @@ -2514,8 +2519,14 @@ bool Codegen::visit(WithStatement *ast) IR::ExprList *args = _function->New(); args->init(_block->TEMP(withObject)); _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with_scope, 0, 0), args)); + ++_function->insideWith; - statement(ast->statement); + { + ScopeAndFinally scope(_scopeAndFinally); + _scopeAndFinally = &scope; + statement(ast->statement); + _scopeAndFinally = scope.parent; + } --_function->insideWith; _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); diff --git a/qv4codegen_p.h b/qv4codegen_p.h index 9806198ecb..a8e0fcc4ad 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -213,13 +213,15 @@ protected: struct UiMember { }; - struct TryCleanup { - TryCleanup *parent; + struct ScopeAndFinally { + ScopeAndFinally *parent; AST::Finally *finally; IR::ExprList *deleteExceptionArgs; + bool popScope; - TryCleanup(TryCleanup *parent, AST::Finally *finally, IR::ExprList *deleteExceptionArgs) - : parent(parent), finally(finally), deleteExceptionArgs(deleteExceptionArgs) + ScopeAndFinally(ScopeAndFinally *parent) : parent(parent), finally(0), deleteExceptionArgs(0), popScope(true) {} + ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally, IR::ExprList *deleteExceptionArgs) + : parent(parent), finally(finally), deleteExceptionArgs(deleteExceptionArgs), popScope(false) {} }; @@ -229,7 +231,7 @@ protected: IR::BasicBlock *breakBlock; IR::BasicBlock *continueBlock; Loop *parent; - TryCleanup *tryCleanup; + ScopeAndFinally *scopeAndFinally; Loop(AST::Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock, Loop *parent) : labelledStatement(0), node(node), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {} @@ -260,7 +262,7 @@ protected: const QStringList &inheritedLocals = QStringList()); int indexOfArgument(const QStringRef &string) const; - void unwindException(TryCleanup *outest); + void unwindException(ScopeAndFinally *outest); void statement(AST::Statement *ast); void statement(AST::ExpressionNode *ast); @@ -399,7 +401,7 @@ private: Environment *_env; Loop *_loop; AST::LabelledStatement *_labelledStatement; - TryCleanup *_tryCleanup; + ScopeAndFinally *_scopeAndFinally; QHash _envMap; QHash _functionMap; VM::ExecutionContext *_context; diff --git a/tests/TestExpectations b/tests/TestExpectations index faec6299d5..4193992a42 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -77,15 +77,7 @@ S11.5.3_A3_T2.9 failing S11.5.3_A4_T2 failing S11.8.6_A3 failing S11.8.6_A5_T2 failing -S12.10_A1.4_T4 failing -S12.10_A1.4_T5 failing -S12.10_A1.5_T4 failing -S12.10_A1.5_T5 failing 12.14-13 failing -S12.10_A3.4_T4 failing -S12.10_A3.4_T5 failing -S12.10_A3.5_T4 failing -S12.10_A3.5_T5 failing S13_A15_T4 failing S13_A3_T1 failing 13.1-11-s failing @@ -164,4 +156,4 @@ S12.9_A1_T6 failing S12.9_A1_T7 failing S12.9_A1_T8 failing S12.9_A1_T9 failing -S15.2.4.4_A14 failing +S15.2.4.4_A14 failing \ No newline at end of file -- cgit v1.2.3 From 4e4fb0ced23391c4ffb1b59845d94e6c658d3deb Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 Jan 2013 12:53:47 +0100 Subject: Fix properties of literal array initialisers Array entries defined in such a way need to be own properties. To avoid calling builtin_define_property with the integer index converted to a string for each property, this patch also introduces a builtin_define_array_property that takes an integer index instead. Change-Id: Ib21f99a555b3237753a7930a7edc70bfcd49ac18 Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 8 ++++++++ moth/qv4isel_moth.cpp | 9 +++++++++ moth/qv4isel_moth_p.h | 1 + moth/qv4vme_moth.cpp | 4 ++++ qmljs_runtime.cpp | 14 ++++++++++++++ qmljs_runtime.h | 1 + qv4codegen.cpp | 19 ++++++++++++++++++- qv4ir.cpp | 2 ++ qv4ir_p.h | 1 + qv4isel_masm.cpp | 6 ++++++ qv4isel_masm_p.h | 1 + qv4isel_p.cpp | 16 ++++++++++++++++ qv4isel_p.h | 1 + tests/TestExpectations | 16 +--------------- 14 files changed, 83 insertions(+), 16 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index a2863ef389..28d72cb10d 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -32,6 +32,7 @@ F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ F(CallBuiltinDefineGetterSetter, callBuiltinDefineGetterSetter) \ F(CallBuiltinDefineProperty, callBuiltinDefineProperty) \ + F(CallBuiltinDefineArrayProperty, callBuiltinDefineArrayProperty) \ F(CreateValue, createValue) \ F(CreateProperty, createProperty) \ F(CreateActivationProperty, createActivationProperty) \ @@ -240,6 +241,12 @@ union Instr VM::String *name; int valueTemp; }; + struct instr_callBuiltinDefineArrayProperty { + MOTH_INSTR_HEADER + int objectTemp; + int index; + int valueTemp; + }; struct instr_createValue { MOTH_INSTR_HEADER int func; @@ -342,6 +349,7 @@ union Instr instr_callBuiltinDeclareVar callBuiltinDeclareVar; instr_callBuiltinDefineGetterSetter callBuiltinDefineGetterSetter; instr_callBuiltinDefineProperty callBuiltinDefineProperty; + instr_callBuiltinDefineArrayProperty callBuiltinDefineArrayProperty; instr_createValue createValue; instr_createProperty createProperty; instr_createActivationProperty createActivationProperty; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 2246442cf2..eab1e63519 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -847,6 +847,15 @@ void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QSt addInstruction(call); } +void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) +{ + Instruction::CallBuiltinDefineArrayProperty call; + call.objectTemp = object->index; + call.index = index; + call.valueTemp = value->index; + addInstruction(call); +} + ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) { #ifdef MOTH_THREADED_INTERPRETER diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 10cb33ac0e..3874adf527 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -44,6 +44,7 @@ protected: virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); + virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index fbfa47d401..5052d82c46 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -339,6 +339,10 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co __qmljs_builtin_define_property(TEMP(instr.objectTemp), instr.name, TEMP(instr.valueTemp), context); MOTH_END_INSTR(CallBuiltinDefineProperty) + MOTH_BEGIN_INSTR(CallBuiltinDefineArrayProperty) + __qmljs_builtin_define_array_property(TEMP(instr.objectTemp), instr.index, TEMP(instr.valueTemp), context); + MOTH_END_INSTR(CallBuiltinDefineArrayProperty) + MOTH_BEGIN_INSTR(CreateValue) int argStart = instr.args - context->variableCount(); VM::Value *args = stack + argStart; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 114a7b9936..e297dabd01 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -924,6 +924,20 @@ void __qmljs_builtin_define_property(Value object, String *name, Value val, Exec o->__defineOwnProperty__(ctx, name, &pd); } +void __qmljs_builtin_define_array_property(Value object, int index, Value val, ExecutionContext *ctx) +{ + Object *o = object.asObject(); + assert(o); + + PropertyDescriptor pd; + pd.value = val; + pd.type = PropertyDescriptor::Data; + pd.writable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + pd.configurable = PropertyDescriptor::Enabled; + o->__defineOwnProperty__(ctx, index, &pd); +} + void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx) { Object *o = object.asObject(); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index e1c6d5a335..08ba85acb3 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -105,6 +105,7 @@ ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx); void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name); void __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx); +void __qmljs_builtin_define_array_property(Value object, int index, Value val, ExecutionContext *ctx); void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); // constructors diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 5ee7159220..56783738b8 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1025,11 +1025,28 @@ bool Codegen::visit(ArrayLiteral *ast) const unsigned t = _block->newTemp(); move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Array"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); int index = 0; + unsigned value = 0; for (ElementList *it = ast->elements; it; it = it->next) { for (Elision *elision = it->elision; elision; elision = elision->next) ++index; Result expr = expression(it->expression); - move(subscript(_block->TEMP(t), _block->CONST(IR::NumberType, index)), *expr); + + IR::ExprList *args = _function->New(); + IR::ExprList *current = args; + current->expr = _block->TEMP(t); + current->next = _function->New(); + current = current->next; + current->expr = _block->CONST(IR::NumberType, index); + current->next = _function->New(); + current = current->next; + + if (!value) + value = _block->newTemp(); + move(_block->TEMP(value), *expr); + // __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) + current->expr = _block->TEMP(value); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_define_array_property, 0, 0), args)); + ++index; } if (ast->elision) { diff --git a/qv4ir.cpp b/qv4ir.cpp index 969d07a843..a34a2943a5 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -244,6 +244,8 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_declare_vars"; case IR::Name::builtin_define_property: return "builtin_define_property"; + case IR::Name::builtin_define_array_property: + return "builtin_define_array_property"; case IR::Name::builtin_define_getter_setter: return "builtin_define_getter_setter"; } diff --git a/qv4ir_p.h b/qv4ir_p.h index 69059780c9..f77cb58597 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -285,6 +285,7 @@ struct Name: Expr { builtin_pop_scope, builtin_declare_vars, builtin_define_property, + builtin_define_array_property, builtin_define_getter_setter }; diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 5f34c02fba..15cf5fc535 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -541,6 +541,12 @@ void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QSt object, identifier(name), value, Assembler::ContextRegister); } +void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_array_property, + object, Assembler::TrustedImm32(index), value, Assembler::ContextRegister); +} + void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) { int argc = prepareVariableArguments(args); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index e45ed51592..35f48522ec 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -672,6 +672,7 @@ protected: virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); + virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); virtual void loadThisObject(IR::Temp *temp); diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 17f846675f..03600fdcd4 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -348,6 +348,22 @@ void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result) callBuiltinDefineProperty(object, *name->id, value); } return; + case IR::Name::builtin_define_array_property: { + if (!call->args) + return; + IR::ExprList *args = call->args; + IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + IR::Const *index = args->expr->asConst(); + args = args->next; + assert(args); + IR::Temp *value = args->expr->asTemp(); + + callBuiltinDefineArrayProperty(object, int(index->value), value); + } return; + default: break; } diff --git a/qv4isel_p.h b/qv4isel_p.h index fc145523ac..8b932ae693 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -100,6 +100,7 @@ public: // to implement by subclasses: virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0; virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) = 0; virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) = 0; + virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) = 0; virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) = 0; virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) = 0; virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result) = 0; diff --git a/tests/TestExpectations b/tests/TestExpectations index 4193992a42..e6a473f207 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -13,8 +13,6 @@ S15.1.3.2_A2.5_T1 10.4.3-1-17-s failing 10.4.3-1-63-s failing 10.4.3-1-82-s failing -11.1.4_4-5-1 failing -11.1.4_5-6-1 failing S11.11.1_A2.1_T1 failing S11.11.1_A3_T2 failing S11.11.1_A3_T3 failing @@ -110,21 +108,9 @@ S15.12.2_A1 failing 15.4.4.14-9-a-17 failing 15.4.4.14-9-a-7 failing 15.4.4.14-9-a-9 failing -15.4.4.14-9-b-i-5 failing -15.4.4.15-8-b-i-5 failing -15.4.4.16-7-c-i-6 failing -15.4.4.17-7-c-i-6 failing -15.4.4.18-7-c-i-6 failing 15.4.4.19-5-1 failing -15.4.4.19-8-c-i-6 failing -15.4.4.20-9-c-i-6 failing -15.4.4.21-8-b-iii-1-6 failing -15.4.4.21-9-c-i-6 failing -15.4.4.22-8-b-iii-1-6 failing -15.4.4.22-9-c-i-6 failing S15.4.4.3_A1_T1 failing S15.4.4.3_A3_T1 failing -15.4.4.4-5-b-iii-3-b-1 failing 15.4.4.4-5-c-i-1 failing S15.4.4.4_A1_T2 failing S15.4.4.4_A2_T1 failing @@ -156,4 +142,4 @@ S12.9_A1_T6 failing S12.9_A1_T7 failing S12.9_A1_T8 failing S12.9_A1_T9 failing -S15.2.4.4_A14 failing \ No newline at end of file +S15.2.4.4_A14 failing -- cgit v1.2.3 From 08897e64c866fbf283e7cf434a4db0a5ae1f13a7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 Jan 2013 14:27:36 +0100 Subject: Fix return statement outside of function body Such a program is syntactically incorrect Change-Id: Ica23c67aae55890a36064987f6019d9d7f50585e Reviewed-by: Lars Knoll --- qv4codegen.cpp | 2 ++ tests/TestExpectations | 12 +----------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 56783738b8..dfc855ba2c 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -2289,6 +2289,8 @@ bool Codegen::visit(LocalForStatement *ast) bool Codegen::visit(ReturnStatement *ast) { + if (_mode == GlobalCode) + throwSyntaxError(ast->returnToken, QCoreApplication::translate("qv4codegen", "Return statement outside of function")); if (ast->expression) { Result expr = expression(ast->expression); move(_block->TEMP(_returnAddress), *expr); diff --git a/tests/TestExpectations b/tests/TestExpectations index e6a473f207..311f3c9e40 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -132,14 +132,4 @@ Sbp_A3_T1 failing Sbp_A4_T1 failing Sbp_A4_T2 failing S12.4_A1 failing -S12.9_A1_T1 failing -S12.9_A1_T10 failing -S12.9_A1_T2 failing -S12.9_A1_T3 failing -S12.9_A1_T4 failing -S12.9_A1_T5 failing -S12.9_A1_T6 failing -S12.9_A1_T7 failing -S12.9_A1_T8 failing -S12.9_A1_T9 failing -S15.2.4.4_A14 failing +S15.2.4.4_A14 failing \ No newline at end of file -- cgit v1.2.3 From 77fc11421216aebcab20aaf8f6aa158c1349407e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 24 Jan 2013 15:03:00 +0100 Subject: Throw a reference error when trying to use an invalid lvalue Change-Id: I08c19710eaf58da100852fa9c2b07a4d8bb8b0c4 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 12 +++++++++++- qv4codegen_p.h | 1 + tests/TestExpectations | 4 ---- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index dfc855ba2c..655a7079f3 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1152,7 +1152,7 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::Assign: { IR::Expr* right = *expression(ast->right); if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) - throwSyntaxError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of assignment operator is not an lvalue")); + throwReferenceError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of assignment operator is not an lvalue")); if (_expr.accept(nx)) { move(left, right); @@ -2607,3 +2607,13 @@ void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) else Q_ASSERT(!"No error handler available."); } + +void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail) +{ + if (_context) + _context->throwReferenceError(VM::Value::fromString(_context, detail)); + else if (_errorHandler) + throwSyntaxError(loc, detail); + else + Q_ASSERT(!"No error handler available."); +} diff --git a/qv4codegen_p.h b/qv4codegen_p.h index a8e0fcc4ad..ffb9f9010b 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -385,6 +385,7 @@ protected: virtual bool visit(AST::UiSourceElement *ast); void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); + void throwReferenceError(const AST::SourceLocation &loc, const QString &detail); private: QString _fileName; diff --git a/tests/TestExpectations b/tests/TestExpectations index 311f3c9e40..8f2ed81a3b 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -17,10 +17,6 @@ S11.11.1_A2.1_T1 failing S11.11.1_A3_T2 failing S11.11.1_A3_T3 failing S11.11.1_A3_T4 failing -11.13.1-1-1 failing -11.13.1-1-2 failing -11.13.1-1-3 failing -11.13.1-1-4 failing 11.13.2-45-s failing 11.13.2-46-s failing 11.13.2-47-s failing -- cgit v1.2.3 From d059d3ab72c4483eb9754f0585f7d5892889b7d4 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 24 Jan 2013 15:31:54 +0100 Subject: Avoid integer division by 0 Change-Id: I5c50878192e3f4dd6e7297e51a450840c4c1037e Reviewed-by: Simon Hausmann --- qmljs_runtime.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 08ba85acb3..870e1d315a 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -589,7 +589,7 @@ inline Value __qmljs_mod(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); - if (Value::integerCompatible(left, right)) + if (Value::integerCompatible(left, right) && right.integerValue() != 0) return Value::fromInt32(left.integerValue() % right.integerValue()); double lval = __qmljs_to_number(left, ctx); -- cgit v1.2.3 From 04a87449934c35037464b8b244b79817ec17c714 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 24 Jan 2013 15:32:12 +0100 Subject: Fix inplace operators We actually need to call __put__ on the result of the operation even if the lhs didn't exist before. Change-Id: I54acaab203fd34fd88a36685211e382a8399b11b Reviewed-by: Simon Hausmann --- qv4object.cpp | 12 +++--------- qv4object.h | 4 ++-- tests/TestExpectations | 17 ----------------- 3 files changed, 5 insertions(+), 28 deletions(-) diff --git a/qv4object.cpp b/qv4object.cpp index 5fcb7fca44..5b4aace113 100644 --- a/qv4object.cpp +++ b/qv4object.cpp @@ -102,32 +102,26 @@ Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p return getValue(ctx, p); } -bool Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx) +void Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx) { bool hasProperty = false; Value v = __get__(ctx, name, &hasProperty); - if (!hasProperty) - return false; Value result = op(v, rhs, ctx); __put__(ctx, name, result); - return true; } -bool Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) +void Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) { uint idx = index.asArrayIndex(); if (idx < UINT_MAX) { bool hasProperty = false; Value v = __get__(ctx, idx, &hasProperty); - if (!hasProperty) - return false; v = op(v, rhs, ctx); __put__(ctx, idx, v); - return true; } String *name = index.toString(ctx); assert(name); - return inplaceBinOp(rhs, name, op, ctx); + inplaceBinOp(rhs, name, op, ctx); } void Object::defineDefaultProperty(String *name, Value value) diff --git a/qv4object.h b/qv4object.h index 2747dffc73..25bf94c3fb 100644 --- a/qv4object.h +++ b/qv4object.h @@ -152,8 +152,8 @@ struct Object: Managed { Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const; Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const; - bool inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); - bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); + void inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); + void inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); /* The spec default: Writable: true, Enumerable: false, Configurable: true */ void defineDefaultProperty(String *name, Value value); diff --git a/tests/TestExpectations b/tests/TestExpectations index 8f2ed81a3b..47572a6e02 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -17,20 +17,6 @@ S11.11.1_A2.1_T1 failing S11.11.1_A3_T2 failing S11.11.1_A3_T3 failing S11.11.1_A3_T4 failing -11.13.2-45-s failing -11.13.2-46-s failing -11.13.2-47-s failing -11.13.2-48-s failing -11.13.2-49-s failing -11.13.2-50-s failing -11.13.2-51-s failing -11.13.2-52-s failing -11.13.2-53-s failing -11.13.2-54-s failing -11.13.2-55-s failing -S11.13.2_A4.3_T1.4 failing -S11.13.2_A4.3_T2.3 failing -S11.13.2_A4.3_T2.9 failing S11.2.1_A3_T1 failing S11.2.1_A3_T2 failing S11.2.1_A3_T3 failing @@ -65,9 +51,6 @@ S11.4.1_A2.1 failing 11.4.4-2-2-s failing 11.4.5-2-1-s failing 11.4.5-2-2-s failing -S11.5.3_A3_T1.4 failing -S11.5.3_A3_T2.3 failing -S11.5.3_A3_T2.9 failing S11.5.3_A4_T2 failing S11.8.6_A3 failing S11.8.6_A5_T2 failing -- cgit v1.2.3 From 4faaff19d698a8857d6fa5f870ccedc6f7b89299 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 Jan 2013 15:36:33 +0100 Subject: Throw a syntax error if eval or arguments is used in increment/decrement expressions Change-Id: Icd966b626a172302eb4b22f5cadba0085d31c320 Reviewed-by: Lars Knoll Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 56 +++++++++++++++++--------------------------------- qv4codegen_p.h | 2 ++ tests/TestExpectations | 8 -------- 3 files changed, 21 insertions(+), 45 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 655a7079f3..055261694e 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -305,7 +305,7 @@ protected: virtual bool visit(VariableDeclaration *ast) { - if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == "arguments")) + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); checkName(ast->name, ast->identifierToken); if (ast->name == QLatin1String("arguments")) @@ -1138,11 +1138,7 @@ bool Codegen::visit(BinaryExpression *ast) } IR::Expr* left = *expression(ast->left); - if (_function->isStrict) { - if (IR::Name *n = left->asName()) - if (*n->id == QLatin1String("eval") || *n->id == QLatin1String("arguments")) - throwSyntaxError(ast->left->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); - } + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()); switch (ast->op) { case QSOperator::Or: @@ -1531,15 +1527,8 @@ bool Codegen::visit(ObjectLiteral *ast) bool Codegen::visit(PostDecrementExpression *ast) { - // ### - // Throw a SyntaxError exception if the following conditions are all true: - // Type(lhs) is Reference is true - // IsStrictReference(lhs) is true - // Type(GetBase(lhs)) is Environment Record - // GetReferencedName(lhs) is either "eval" or "arguments" - - Result expr = expression(ast->base); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); if (_expr.accept(nx)) { move(*expr, unop(IR::OpDecrement, *expr)); } else { @@ -1553,14 +1542,8 @@ bool Codegen::visit(PostDecrementExpression *ast) bool Codegen::visit(PostIncrementExpression *ast) { - // ### - // Throw a SyntaxError exception if the following conditions are all true: - // Type(lhs) is Reference is true - // IsStrictReference(lhs) is true - // Type(GetBase(lhs)) is Environment Record - // GetReferencedName(lhs) is either "eval" or "arguments" - Result expr = expression(ast->base); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); if (_expr.accept(nx)) { move(*expr, unop(IR::OpIncrement, *expr)); } else { @@ -1574,14 +1557,8 @@ bool Codegen::visit(PostIncrementExpression *ast) bool Codegen::visit(PreDecrementExpression *ast) { - // ### - // Throw a SyntaxError exception if the following conditions are all true: - // Type(lhs) is Reference is true - // IsStrictReference(lhs) is true - // Type(GetBase(lhs)) is Environment Record - // GetReferencedName(lhs) is either "eval" or "arguments" - Result expr = expression(ast->expression); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); move(*expr, unop(IR::OpDecrement, *expr)); if (_expr.accept(nx)) { // nothing to do @@ -1593,14 +1570,8 @@ bool Codegen::visit(PreDecrementExpression *ast) bool Codegen::visit(PreIncrementExpression *ast) { - // ### - // Throw a SyntaxError exception if the following conditions are all true: - // Type(lhs) is Reference is true - // IsStrictReference(lhs) is true - // Type(GetBase(lhs)) is Environment Record - // GetReferencedName(lhs) is either "eval" or "arguments" - Result expr = expression(ast->expression); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); move(*expr, unop(IR::OpIncrement, *expr)); if (_expr.accept(nx)) { // nothing to do @@ -2386,8 +2357,8 @@ bool Codegen::visit(ThrowStatement *ast) bool Codegen::visit(TryStatement *ast) { if (_function->isStrict && ast->catchExpression && - (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) - throwSyntaxError(ast->catchExpression->identifierToken, QCoreApplication::translate("qv4codegen", "Catch variable name may not be eval or arguments in strict mode")); + (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) + throwSyntaxError(ast->catchExpression->identifierToken, QCoreApplication::translate("qv4codegen", "Catch variable name may not be eval or arguments in strict mode")); IR::BasicBlock *tryBody = _function->newBasicBlock(); IR::BasicBlock *catchBody = ast->catchExpression ? _function->newBasicBlock() : 0; @@ -2592,6 +2563,17 @@ bool Codegen::visit(UiSourceElement *) return false; } +void Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(IR::Expr *expr, const SourceLocation& loc) +{ + if (!_env->isStrict) + return; + IR::Name *n = expr->asName(); + if (!n) + return; + if (*n->id == QLatin1String("eval") || *n->id == QLatin1String("arguments")) + throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); +} + void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) { VM::DiagnosticMessage *msg = new VM::DiagnosticMessage; diff --git a/qv4codegen_p.h b/qv4codegen_p.h index ffb9f9010b..89953c8ff9 100644 --- a/qv4codegen_p.h +++ b/qv4codegen_p.h @@ -384,6 +384,8 @@ protected: virtual bool visit(AST::UiScriptBinding *ast); virtual bool visit(AST::UiSourceElement *ast); + void throwSyntaxErrorOnEvalOrArgumentsInStrictMode(IR::Expr* expr, const AST::SourceLocation &loc); + void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); void throwReferenceError(const AST::SourceLocation &loc, const QString &detail); diff --git a/tests/TestExpectations b/tests/TestExpectations index 47572a6e02..b90a9f156e 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -22,15 +22,11 @@ S11.2.1_A3_T2 failing S11.2.1_A3_T3 failing S11.2.1_A4_T3 failing 11.2.3-3_3 failing -11.3.1-2-1-s failing -11.3.1-2-2-s failing S11.3.1_A2.2_T1 failing S11.3.1_A4_T1 failing S11.3.1_A4_T2 failing S11.3.1_A4_T3 failing S11.3.1_A4_T4 failing -11.3.2-2-1-s failing -11.3.2-2-2-s failing S11.3.2_A2.2_T1 failing S11.3.2_A4_T1 failing S11.3.2_A4_T2 failing @@ -47,10 +43,6 @@ S11.3.2_A4_T4 failing 11.4.1-5-2 failing S11.4.1_A1 failing S11.4.1_A2.1 failing -11.4.4-2-1-s failing -11.4.4-2-2-s failing -11.4.5-2-1-s failing -11.4.5-2-2-s failing S11.5.3_A4_T2 failing S11.8.6_A3 failing S11.8.6_A5_T2 failing -- cgit v1.2.3 From 2eb06595a244e31dc192e679b32a8474c9926fe8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 Jan 2013 16:07:18 +0100 Subject: Delete on a function argument should return false Change-Id: I36aafcfa43bced2b8315af39432cf7731daaf458 Reviewed-by: Lars Knoll --- qv4codegen.cpp | 2 +- tests/TestExpectations | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 055261694e..60247ac4aa 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1303,7 +1303,7 @@ bool Codegen::visit(DeleteExpression *ast) if (_function->isStrict) throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); // can't delete an argument, just evaluate expr for side effects - _expr.accept(nx); + _expr.code = _block->CONST(IR::BoolType, 0); return false; } if (_function->isStrict && (*expr)->asName()) diff --git a/tests/TestExpectations b/tests/TestExpectations index b90a9f156e..3732536751 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -40,7 +40,6 @@ S11.3.2_A4_T4 failing 11.4.1-2-5 failing 11.4.1-2-6 failing 11.4.1-4.a-5 failing -11.4.1-5-2 failing S11.4.1_A1 failing S11.4.1_A2.1 failing S11.5.3_A4_T2 failing -- cgit v1.2.3 From dffe5c8baf4c2530bfd71853c077ce823f20327b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 24 Jan 2013 15:49:05 +0100 Subject: Fix compilation of llvm backend. Change-Id: I714977f7e5e7040a8fd252525cd9f84fbfe40862 Reviewed-by: Lars Knoll --- qv4isel_llvm.cpp | 7 +++++++ qv4isel_llvm_p.h | 1 + 2 files changed, 8 insertions(+) diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 6c74dc6d0d..6bd3412ab8 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -441,6 +441,13 @@ void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QSt Q_UNREACHABLE(); } +void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) { // TODO diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index 9dcf961171..c2c20d66f7 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -90,6 +90,7 @@ public: // methods from InstructionSelection: virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); + virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); -- cgit v1.2.3 From 49e201fbf9914e5fc31d85239ecb2c19fc0d7623 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 25 Jan 2013 10:38:52 +0100 Subject: Remove a few virtual keywords that aren't required anymore Change-Id: Ic7d46d78cd1efeac16a25586c5b8fb3cb761512d Reviewed-by: Simon Hausmann --- qv4object.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qv4object.h b/qv4object.h index 25bf94c3fb..ddf0d05c8e 100644 --- a/qv4object.h +++ b/qv4object.h @@ -126,16 +126,16 @@ struct Object: Managed { PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name); PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, uint index); - virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); + Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); void __put__(ExecutionContext *ctx, String *name, Value value); void __put__(ExecutionContext *ctx, uint index, Value value); - virtual bool __hasProperty__(const ExecutionContext *ctx, String *name) const; - virtual bool __hasProperty__(const ExecutionContext *ctx, uint index) const; - virtual bool __delete__(ExecutionContext *ctx, String *name); - virtual bool __delete__(ExecutionContext *ctx, uint index); + bool __hasProperty__(const ExecutionContext *ctx, String *name) const; + bool __hasProperty__(const ExecutionContext *ctx, uint index) const; + bool __delete__(ExecutionContext *ctx, String *name); + bool __delete__(ExecutionContext *ctx, uint index); bool __defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc); bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc); bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); -- cgit v1.2.3 From 77a4268150de881ab0b302d6cfc82d6b9b8107eb Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 25 Jan 2013 12:16:45 +0100 Subject: Add missing return statement Fixes crypto.js Change-Id: If3b2d182b8e6a93acd2c32904e5b9a9fd49181cf Reviewed-by: Simon Hausmann --- qv4object.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/qv4object.cpp b/qv4object.cpp index 5b4aace113..108ad4884e 100644 --- a/qv4object.cpp +++ b/qv4object.cpp @@ -118,6 +118,7 @@ void Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ct Value v = __get__(ctx, idx, &hasProperty); v = op(v, rhs, ctx); __put__(ctx, idx, v); + return; } String *name = index.toString(ctx); assert(name); -- cgit v1.2.3 From 4134f8b50d0dd3958332bbd49e872b3799269535 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 25 Jan 2013 12:43:44 +0100 Subject: Faster marking of objects in the GC Change-Id: I4ccbf7fc50758fc176bfdb2f0382a7ec1f18d6ba Reviewed-by: Simon Hausmann --- qv4argumentsobject.cpp | 6 +++--- qv4argumentsobject.h | 2 +- qv4array.cpp | 9 +++++---- qv4array.h | 2 +- qv4functionobject.cpp | 10 +++++----- qv4functionobject.h | 2 +- qv4managed.h | 9 ++++++++- qv4mm.cpp | 20 +++----------------- qv4mm.h | 2 +- qv4object.cpp | 18 +++++++++--------- qv4object.h | 4 ++-- 11 files changed, 39 insertions(+), 45 deletions(-) diff --git a/qv4argumentsobject.cpp b/qv4argumentsobject.cpp index b803644b18..320b3e09e3 100644 --- a/qv4argumentsobject.cpp +++ b/qv4argumentsobject.cpp @@ -116,14 +116,14 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const return result; } -void ArgumentsObject::getCollectables(QVector &objects) +void ArgumentsObject::markObjects() { for (int i = 0; i < mappedArguments.size(); ++i) { Object *o = mappedArguments.at(i).asObject(); if (o) - objects.append(o); + o->mark(); } - Object::getCollectables(objects); + Object::markObjects(); } diff --git a/qv4argumentsobject.h b/qv4argumentsobject.h index 398f5de147..c3f5de7d84 100644 --- a/qv4argumentsobject.h +++ b/qv4argumentsobject.h @@ -77,7 +77,7 @@ struct ArgumentsObject: Object { bool defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); - virtual void getCollectables(QVector &objects); + virtual void markObjects(); }; } diff --git a/qv4array.cpp b/qv4array.cpp index 8cf745e1b4..70bbd9d467 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -628,18 +628,19 @@ bool Array::setLength(uint newLen) { return ok; } -void Array::getCollectables(QVector &objects) const { +void Array::markObjects() const +{ uint i = sparse ? 0 : offset; for (; i < (uint)values.size(); ++i) { const PropertyDescriptor &pd = values.at(i); if (pd.isData()) { if (Object *o = pd.value.asObject()) - objects.append(o); + o->mark(); } else if (pd.isAccessor()) { if (pd.get) - objects.append(pd.get); + pd.get->mark(); if (pd.set) - objects.append(pd.set); + pd.set->mark(); } } } diff --git a/qv4array.h b/qv4array.h index ead2f34ce1..19aa99b4bb 100644 --- a/qv4array.h +++ b/qv4array.h @@ -543,7 +543,7 @@ public: } } - void getCollectables(QVector &objects) const; + void markObjects() const; void push_front(Value v) { if (!sparse) { diff --git a/qv4functionobject.cpp b/qv4functionobject.cpp index 798549116c..b580f840e5 100644 --- a/qv4functionobject.cpp +++ b/qv4functionobject.cpp @@ -402,13 +402,13 @@ bool BoundFunction::hasInstance(ExecutionContext *ctx, const Value &value) return target->hasInstance(ctx, value); } -void BoundFunction::getCollectables(QVector &objects) +void BoundFunction::markObjects() { - FunctionObject::getCollectables(objects); - objects.append(target); + target->mark(); if (Object *o = boundThis.asObject()) - objects.append(o); + o->mark(); for (int i = 0; i < boundArgs.size(); ++i) if (Object *o = boundArgs.at(i).asObject()) - objects.append(o); + o->mark(); + FunctionObject::markObjects(); } diff --git a/qv4functionobject.h b/qv4functionobject.h index 95e06e11fc..f913744550 100644 --- a/qv4functionobject.h +++ b/qv4functionobject.h @@ -210,7 +210,7 @@ struct BoundFunction: FunctionObject { virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); virtual Value construct(ExecutionContext *context, Value *args, int argc); virtual bool hasInstance(ExecutionContext *ctx, const Value &value); - virtual void getCollectables(QVector &objects); + virtual void markObjects(); }; } // namespace VM diff --git a/qv4managed.h b/qv4managed.h index 51aa68ab0c..855200dc65 100644 --- a/qv4managed.h +++ b/qv4managed.h @@ -71,8 +71,15 @@ public: void *operator new(size_t size, MemoryManager *mm); void operator delete(void *ptr); + inline void mark() { + if (markBit) + return; + markBit = 1; + markObjects(); + } + protected: - virtual void getCollectables(QVector &objects) = 0; + virtual void markObjects() = 0; union { Managed *nextFree; diff --git a/qv4mm.cpp b/qv4mm.cpp index 895026da57..e4f1fbe3dc 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -165,29 +165,15 @@ void MemoryManager::scribble(Managed *obj, int c, int size) const ::memset((void *)(obj + 1), c, size - sizeof(Managed)); } -std::size_t MemoryManager::mark(const QVector &objects) +void MemoryManager::mark(const QVector &objects) { - std::size_t marks = 0; - - QVector kids; - kids.reserve(32); - foreach (Object *o, objects) { if (!o) continue; - - Managed *obj = o; - assert(obj->inUse); - if (obj->markBit == 0) { - obj->markBit = 1; - ++marks; - static_cast(o)->getCollectables(kids); - marks += mark(kids); - kids.resize(0); - } + o->mark(); } - return marks; + return; } std::size_t MemoryManager::sweep() diff --git a/qv4mm.h b/qv4mm.h index 5790ba4b61..0dd3e61f09 100644 --- a/qv4mm.h +++ b/qv4mm.h @@ -111,7 +111,7 @@ protected: private: void collectRoots(QVector &roots) const; - static std::size_t mark(const QVector &objects); + static void mark(const QVector &objects); std::size_t sweep(); std::size_t sweep(char *chunkStart, std::size_t chunkSize, size_t size); diff --git a/qv4object.cpp b/qv4object.cpp index 108ad4884e..5f8a6933b3 100644 --- a/qv4object.cpp +++ b/qv4object.cpp @@ -168,10 +168,10 @@ void Object::defineReadonlyProperty(String *name, Value value) pd->value = value; } -void Object::getCollectables(QVector &objects) +void Object::markObjects() { if (prototype) - objects.append(prototype); + prototype->mark(); if (members) { for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) { @@ -180,16 +180,16 @@ void Object::getCollectables(QVector &objects) PropertyDescriptor &pd = (*it)->descriptor; if (pd.isData()) { if (Object *o = pd.value.asObject()) - objects.append(o); + o->mark(); } else if (pd.isAccessor()) { if (pd.get) - objects.append(pd.get); + pd.get->mark(); if (pd.set) - objects.append(pd.set); + pd.set->mark(); } } } - array.getCollectables(objects); + array.markObjects(); } // Section 8.12.1 @@ -660,10 +660,10 @@ void ArrayObject::init(ExecutionContext *context) -void ForEachIteratorObject::getCollectables(QVector &objects) +void ForEachIteratorObject::markObjects() { - Object::getCollectables(objects); + Object::markObjects(); if (it.object) - objects.append(it.object); + it.object->mark(); } diff --git a/qv4object.h b/qv4object.h index ddf0d05c8e..df7cf1c456 100644 --- a/qv4object.h +++ b/qv4object.h @@ -164,7 +164,7 @@ struct Object: Managed { void defineReadonlyProperty(String *name, Value value); protected: - virtual void getCollectables(QVector &objects); + virtual void markObjects(); friend struct ObjectIterator; }; @@ -178,7 +178,7 @@ struct ForEachIteratorObject: Object { Value nextPropertyName() { return it.nextPropertyNameAsString(); } protected: - virtual void getCollectables(QVector &objects); + virtual void markObjects(); }; struct BooleanObject: Object { -- cgit v1.2.3 From 8917dbcb6e1cc3572bc12a458239df80d10cbc8a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 25 Jan 2013 13:13:06 +0100 Subject: Remove a bunch of virtuals in Object This should speed up casting to the derived types, and simplify things in the longer term. Change-Id: I7362c86e5e2724d5b7e245695f9fe29144999715 Reviewed-by: Simon Hausmann --- qv4argumentsobject.cpp | 2 ++ qv4argumentsobject.h | 1 - qv4dateobject.h | 3 +-- qv4errorobject.cpp | 2 ++ qv4errorobject.h | 1 - qv4functionobject.h | 10 ++++++---- qv4managed.h | 44 +++++++++++++++++++++++++++++++++++++++++--- qv4object.cpp | 2 ++ qv4object.h | 18 +++--------------- qv4regexpobject.cpp | 2 ++ qv4regexpobject.h | 1 - qv4stringobject.cpp | 1 + qv4stringobject.h | 1 - 13 files changed, 60 insertions(+), 28 deletions(-) diff --git a/qv4argumentsobject.cpp b/qv4argumentsobject.cpp index 320b3e09e3..b8e64855bc 100644 --- a/qv4argumentsobject.cpp +++ b/qv4argumentsobject.cpp @@ -47,6 +47,8 @@ namespace VM { ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount) : context(context) { + type = Type_ArgumentsObject; + defineDefaultProperty(context->engine->id_length, Value::fromInt32(actualParameterCount)); if (context->strictMode) { for (uint i = 0; i < context->argumentCount; ++i) diff --git a/qv4argumentsobject.h b/qv4argumentsobject.h index c3f5de7d84..2785ccb72d 100644 --- a/qv4argumentsobject.h +++ b/qv4argumentsobject.h @@ -73,7 +73,6 @@ struct ArgumentsObject: Object { QVector mappedArguments; ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount); virtual QString className() { return QStringLiteral("Arguments"); } - virtual ArgumentsObject *asArgumentsObject() { return this; } bool defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); diff --git a/qv4dateobject.h b/qv4dateobject.h index 204b8379b8..0cc29289b2 100644 --- a/qv4dateobject.h +++ b/qv4dateobject.h @@ -50,9 +50,8 @@ namespace VM { struct DateObject: Object { Value value; - DateObject(const Value &value): value(value) {} + DateObject(const Value &value): value(value) { type = Type_DateObject; } virtual QString className() { return QStringLiteral("Date"); } - virtual DateObject *asDateObject() { return this; } }; struct DateCtor: FunctionObject diff --git a/qv4errorobject.cpp b/qv4errorobject.cpp index 8e10a905ed..f10ee40fb8 100644 --- a/qv4errorobject.cpp +++ b/qv4errorobject.cpp @@ -75,6 +75,8 @@ using namespace QQmlJS::VM; ErrorObject::ErrorObject(ExecutionEngine* engine, const Value &message) { + type = Type_ErrorObject; + if (message.type() != Value::Undefined_Type) defineDefaultProperty(engine->identifier(QStringLiteral("message")), message); } diff --git a/qv4errorobject.h b/qv4errorobject.h index 5c31f3365e..74dc62bdba 100644 --- a/qv4errorobject.h +++ b/qv4errorobject.h @@ -50,7 +50,6 @@ namespace VM { struct ErrorObject: Object { ErrorObject(ExecutionEngine* engine, const Value &message); virtual QString className() { return QStringLiteral("Error"); } - virtual ErrorObject *asErrorObject() { return this; } virtual struct SyntaxErrorObject *asSyntaxError() { return 0; } diff --git a/qv4functionobject.h b/qv4functionobject.h index f913744550..a62863c430 100644 --- a/qv4functionobject.h +++ b/qv4functionobject.h @@ -143,12 +143,14 @@ struct FunctionObject: Object { , varList(0) , formalParameterCount(0) , varCount(0) - { needsActivation = false; - usesArgumentsObject = false; - strictMode = false; } + { + type = Type_FunctionObject; + needsActivation = false; + usesArgumentsObject = false; + strictMode = false; + } virtual QString className() { return QStringLiteral("Function"); } - virtual FunctionObject *asFunctionObject() { return this; } virtual bool hasInstance(ExecutionContext *ctx, const Value &value); virtual Value construct(ExecutionContext *context, Value *args, int argc); diff --git a/qv4managed.h b/qv4managed.h index 855200dc65..8513228701 100644 --- a/qv4managed.h +++ b/qv4managed.h @@ -56,6 +56,17 @@ struct ObjectPrototype; struct ExecutionContext; struct ScriptFunction; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct ForeachIteratorObject; + struct Managed { private: @@ -64,7 +75,8 @@ private: void operator = (const Managed &other); protected: - Managed() : markBit(0), inUse(1), extensible(true), isArray(false), isArgumentsObject(false), isString(false), isBuiltinFunction(false), unused(0) { } + Managed() : markBit(0), inUse(1), extensible(true), + isArray(false), isArgumentsObject(false), isString(false), isBuiltinFunction(false), type(Type_Object), unused(0) { } virtual ~Managed(); public: @@ -78,6 +90,31 @@ public: markObjects(); } + enum Type { + Type_Object = 0, + Type_ArrayObject, + Type_FunctionObject, + Type_BooleanObject, + Type_NumberObject, + Type_StringObject, + Type_DateObject, + Type_RegExpObject, + Type_ErrorObject, + Type_ArgumentsObject, + Type_ForeachIteratorObject + }; + + ArrayObject *asArrayObject() { return type == Type_ArrayObject ? reinterpret_cast(this) : 0; } + FunctionObject *asFunctionObject() { return type == Type_FunctionObject ? reinterpret_cast(this) : 0; } + BooleanObject *asBooleanObject() { return type == Type_BooleanObject ? reinterpret_cast(this) : 0; } + NumberObject *asNumberObject() { return type == Type_NumberObject ? reinterpret_cast(this) : 0; } + StringObject *asStringObject() { return type == Type_StringObject ? reinterpret_cast(this) : 0; } + DateObject *asDateObject() { return type == Type_DateObject ? reinterpret_cast(this) : 0; } + RegExpObject *asRegExpObject() { return type == Type_RegExpObject ? reinterpret_cast(this) : 0; } + ErrorObject *asErrorObject() { return type == Type_ErrorObject ? reinterpret_cast(this) : 0; } + ArgumentsObject *asArgumentsObject() { return type == Type_ArgumentsObject ? reinterpret_cast(this) : 0; } + ForeachIteratorObject *asForeachIteratorObject() { return type == Type_ForeachIteratorObject ? reinterpret_cast(this) : 0; } + protected: virtual void markObjects() = 0; @@ -94,10 +131,11 @@ protected: quintptr needsActivation : 1; // used by FunctionObject quintptr usesArgumentsObject : 1; // used by FunctionObject quintptr strictMode : 1; // used by FunctionObject + quintptr type : 4; #if CPU(X86_64) - quintptr unused : 55; + quintptr unused : 50; #elif CPU(X86) - quintptr unused : 23; + quintptr unused : 18; #else #error "implement me" #endif diff --git a/qv4object.cpp b/qv4object.cpp index 5f8a6933b3..b564698f31 100644 --- a/qv4object.cpp +++ b/qv4object.cpp @@ -646,7 +646,9 @@ Value Object::call(ExecutionContext *context, Value , Value *, int) void ArrayObject::init(ExecutionContext *context) { + type = Type_ArrayObject; isArray = true; + if (!members) members.reset(new PropertyTable()); PropertyDescriptor *pd = members->insert(context->engine->id_length); diff --git a/qv4object.h b/qv4object.h index df7cf1c456..63a3ba623d 100644 --- a/qv4object.h +++ b/qv4object.h @@ -111,15 +111,6 @@ struct Object: Managed { virtual ~Object(); virtual QString className() { return QStringLiteral("Object"); } - virtual BooleanObject *asBooleanObject() { return 0; } - virtual NumberObject *asNumberObject() { return 0; } - virtual StringObject *asStringObject() { return 0; } - virtual DateObject *asDateObject() { return 0; } - virtual ArrayObject *asArrayObject() { return 0; } - virtual FunctionObject *asFunctionObject() { return 0; } - virtual RegExpObject *asRegExpObject() { return 0; } - virtual ErrorObject *asErrorObject() { return 0; } - virtual ArgumentsObject *asArgumentsObject() { return 0; } PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); @@ -172,7 +163,7 @@ protected: struct ForEachIteratorObject: Object { ObjectIterator it; ForEachIteratorObject(ExecutionContext *ctx, Object *o) - : it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) {} + : it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) { type = Type_ForeachIteratorObject; } virtual QString className() { return QStringLiteral("__ForEachIteratorObject"); } Value nextPropertyName() { return it.nextPropertyNameAsString(); } @@ -183,16 +174,14 @@ protected: struct BooleanObject: Object { Value value; - BooleanObject(const Value &value): value(value) {} + BooleanObject(const Value &value): value(value) { type = Type_BooleanObject; } virtual QString className() { return QStringLiteral("Boolean"); } - virtual BooleanObject *asBooleanObject() { return this; } }; struct NumberObject: Object { Value value; - NumberObject(const Value &value): value(value) {} + NumberObject(const Value &value): value(value) { type = Type_NumberObject; } virtual QString className() { return QStringLiteral("Number"); } - virtual NumberObject *asNumberObject() { return this; } }; struct ArrayObject: Object { @@ -200,7 +189,6 @@ struct ArrayObject: Object { ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setLengthUnchecked(array.length()); } void init(ExecutionContext *context); virtual QString className() { return QStringLiteral("Array"); } - virtual ArrayObject *asArrayObject() { return this; } }; diff --git a/qv4regexpobject.cpp b/qv4regexpobject.cpp index d393593b96..1f78c56782 100644 --- a/qv4regexpobject.cpp +++ b/qv4regexpobject.cpp @@ -68,6 +68,8 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bo : value(value) , global(global) { + type = Type_RegExpObject; + if (!members) members.reset(new PropertyTable()); lastIndexProperty = members->insert(engine->identifier(QStringLiteral("lastIndex"))); diff --git a/qv4regexpobject.h b/qv4regexpobject.h index e5a7ca9e37..9655374572 100644 --- a/qv4regexpobject.h +++ b/qv4regexpobject.h @@ -71,7 +71,6 @@ struct RegExpObject: Object { bool global; RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global); virtual QString className() { return QStringLiteral("RegExp"); } - virtual RegExpObject *asRegExpObject() { return this; } }; diff --git a/qv4stringobject.cpp b/qv4stringobject.cpp index 7766b10713..5829e8c455 100644 --- a/qv4stringobject.cpp +++ b/qv4stringobject.cpp @@ -78,6 +78,7 @@ using namespace QQmlJS::VM; StringObject::StringObject(ExecutionContext *ctx, const Value &value) : value(value) { + type = Type_StringObject; isString = true; tmpProperty.type = PropertyDescriptor::Data; diff --git a/qv4stringobject.h b/qv4stringobject.h index 7e2fc84dbe..97574d951a 100644 --- a/qv4stringobject.h +++ b/qv4stringobject.h @@ -53,7 +53,6 @@ struct StringObject: Object { PropertyDescriptor tmpProperty; StringObject(ExecutionContext *ctx, const Value &value); virtual QString className() { return QStringLiteral("String"); } - virtual StringObject *asStringObject() { return this; } PropertyDescriptor *getIndex(ExecutionContext *ctx, uint index); }; -- cgit v1.2.3 From e813c253f350bad7a1ad16ddd2d7e6601830609d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 25 Jan 2013 13:23:58 +0100 Subject: Inline casting of Value to objects Change-Id: Ic5538b8a0a1f430a265399bced0ce9fc0a79696e Reviewed-by: Simon Hausmann --- qmljs_value.cpp | 45 ---------------------------------------- qmljs_value.h | 64 ++++++++++++++++++++++++++++++++++++++++++++++++--------- qv4managed.h | 1 + 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/qmljs_value.cpp b/qmljs_value.cpp index 72f92a1701..0e4e4f2d82 100644 --- a/qmljs_value.cpp +++ b/qmljs_value.cpp @@ -153,51 +153,6 @@ double Value::toInteger(double number) return std::signbit(number) ? -v : v; } -Object *Value::asObject() const -{ - return isObject() ? objectValue() : 0; -} - -FunctionObject *Value::asFunctionObject() const -{ - return isObject() ? objectValue()->asFunctionObject() : 0; -} - -BooleanObject *Value::asBooleanObject() const -{ - return isObject() ? objectValue()->asBooleanObject() : 0; -} - -NumberObject *Value::asNumberObject() const -{ - return isObject() ? objectValue()->asNumberObject() : 0; -} - -StringObject *Value::asStringObject() const -{ - return isObject() ? objectValue()->asStringObject() : 0; -} - -DateObject *Value::asDateObject() const -{ - return isObject() ? objectValue()->asDateObject() : 0; -} - -RegExpObject *Value::asRegExpObject() const -{ - return isObject() ? objectValue()->asRegExpObject() : 0; -} - -ArrayObject *Value::asArrayObject() const -{ - return isObject() ? objectValue()->asArrayObject() : 0; -} - -ErrorObject *Value::asErrorObject() const -{ - return isObject() ? objectValue()->asErrorObject() : 0; -} - Value Value::property(ExecutionContext *ctx, String *name) const { return isObject() ? objectValue()->__get__(ctx, name) : undefinedValue(); diff --git a/qmljs_value.h b/qmljs_value.h index 9e19788c41..b6124c0a97 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -47,21 +47,12 @@ #include #include #include +#include namespace QQmlJS { namespace VM { struct String; -struct Object; -struct BooleanObject; -struct NumberObject; -struct StringObject; -struct ArrayObject; -struct DateObject; -struct FunctionObject; -struct RegExpObject; -struct ErrorObject; -struct ArgumentsObject; struct ExecutionContext; struct ExecutionEngine; struct Value; @@ -87,6 +78,7 @@ struct Value int int_32; #if CPU(X86_64) #else + Managed *m; Object *o; String *s; #endif @@ -170,6 +162,9 @@ struct Value Object *objectValue() const { return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift)); } + Managed *managed() const { + return (Managed *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } #else String *stringValue() const { return s; @@ -177,6 +172,9 @@ struct Value Object *objectValue() const { return o; } + Managed *managed() const { + return m; + } #endif quint64 rawValue() const { @@ -417,6 +415,52 @@ inline uint Value::asArrayLength(ExecutionContext *ctx, bool *ok) const } +inline Object *Value::asObject() const +{ + return isObject() ? objectValue() : 0; +} + +inline FunctionObject *Value::asFunctionObject() const +{ + return isObject() ? managed()->asFunctionObject() : 0; +} + +inline BooleanObject *Value::asBooleanObject() const +{ + return isObject() ? managed()->asBooleanObject() : 0; +} + +inline NumberObject *Value::asNumberObject() const +{ + return isObject() ? managed()->asNumberObject() : 0; +} + +inline StringObject *Value::asStringObject() const +{ + return isObject() ? managed()->asStringObject() : 0; +} + +inline DateObject *Value::asDateObject() const +{ + return isObject() ? managed()->asDateObject() : 0; +} + +inline RegExpObject *Value::asRegExpObject() const +{ + return isObject() ? managed()->asRegExpObject() : 0; +} + +inline ArrayObject *Value::asArrayObject() const +{ + return isObject() ? managed()->asArrayObject() : 0; +} + +inline ErrorObject *Value::asErrorObject() const +{ + return isObject() ? managed()->asErrorObject() : 0; +} + + } // namespace VM } // namespace QQmlJS diff --git a/qv4managed.h b/qv4managed.h index 8513228701..ff1963d1ef 100644 --- a/qv4managed.h +++ b/qv4managed.h @@ -104,6 +104,7 @@ public: Type_ForeachIteratorObject }; + Object *asObject() { return reinterpret_cast(this); } ArrayObject *asArrayObject() { return type == Type_ArrayObject ? reinterpret_cast(this) : 0; } FunctionObject *asFunctionObject() { return type == Type_FunctionObject ? reinterpret_cast(this) : 0; } BooleanObject *asBooleanObject() { return type == Type_BooleanObject ? reinterpret_cast(this) : 0; } -- cgit v1.2.3 From 6370d75d822152029ed60caba8bdaae9975008fb Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 25 Jan 2013 13:41:37 +0100 Subject: Remove the isString and isArray flags in Managed These are not needed anymore, as we have the type stored in the object. Change-Id: I86594f4c3b3669ee0576e1ca141a64494e82a176 Reviewed-by: Simon Hausmann --- qv4argumentsobject.cpp | 6 +++--- qv4arrayobject.cpp | 18 +++++++++--------- qv4jsonobject.cpp | 2 +- qv4managed.h | 13 +++++++------ qv4object.cpp | 13 ++++++------- qv4stringobject.cpp | 1 - 6 files changed, 26 insertions(+), 27 deletions(-) diff --git a/qv4argumentsobject.cpp b/qv4argumentsobject.cpp index b8e64855bc..b2dc3d4656 100644 --- a/qv4argumentsobject.cpp +++ b/qv4argumentsobject.cpp @@ -76,7 +76,7 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterC __defineOwnProperty__(context, i, &pd); } defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); - isArgumentsObject = true; + isNonStrictArgumentsObject = true; } } @@ -97,12 +97,12 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const pd->value = mappedArguments.at(index); } - isArgumentsObject = false; + isNonStrictArgumentsObject = false; bool strict = ctx->strictMode; ctx->strictMode = false; bool result = Object::__defineOwnProperty__(ctx, index, desc); ctx->strictMode = strict; - isArgumentsObject = true; + isNonStrictArgumentsObject = true; if (isMapped && desc->isData()) { if (desc->type != PropertyDescriptor::Generic) { diff --git a/qv4arrayobject.cpp b/qv4arrayobject.cpp index 04a70909d8..cbf34d3077 100644 --- a/qv4arrayobject.cpp +++ b/qv4arrayobject.cpp @@ -103,7 +103,7 @@ void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) uint ArrayPrototype::getLength(ExecutionContext *ctx, Object *o) { - if (o->isArray) + if (o->isArrayObject()) return o->array.length(); return o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); } @@ -213,7 +213,7 @@ Value ArrayPrototype::method_pop(ExecutionContext *ctx) uint len = getLength(ctx, instance); if (!len) { - if (!instance->isArray) + if (!instance->isArrayObject()) instance->__put__(ctx, ctx->engine->id_length, Value::fromInt32(0)); return Value::undefinedValue(); } @@ -221,7 +221,7 @@ Value ArrayPrototype::method_pop(ExecutionContext *ctx) Value result = instance->__get__(ctx, len - 1); instance->__delete__(ctx, len - 1); - if (instance->isArray) + if (instance->isArrayObject()) instance->array.setLengthUnchecked(len - 1); else instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - 1)); @@ -241,7 +241,7 @@ Value ArrayPrototype::method_push(ExecutionContext *ctx) instance->__put__(ctx, idx.toString(ctx), ctx->argument(i)); } double newLen = l + ctx->argumentCount; - if (!instance->isArray) + if (!instance->isArrayObject()) instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); else ctx->throwRangeError(Value::fromString(ctx, QStringLiteral("Array.prototype.push: Overflow"))); @@ -264,7 +264,7 @@ Value ArrayPrototype::method_push(ExecutionContext *ctx) instance->__put__(ctx, len + i, ctx->argument(i)); } uint newLen = len + ctx->argumentCount; - if (!instance->isArray) + if (!instance->isArrayObject()) instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); if (newLen < INT_MAX) @@ -302,7 +302,7 @@ Value ArrayPrototype::method_shift(ExecutionContext *ctx) uint len = getLength(ctx, instance); if (!len) { - if (!instance->isArray) + if (!instance->isArrayObject()) instance->__put__(ctx, ctx->engine->id_length, Value::fromInt32(0)); return Value::undefinedValue(); } @@ -330,7 +330,7 @@ Value ArrayPrototype::method_shift(ExecutionContext *ctx) instance->__delete__(ctx, len - 1); } - if (!instance->isArray) + if (!instance->isArrayObject()) instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - 1)); return result; } @@ -474,7 +474,7 @@ Value ArrayPrototype::method_unshift(ExecutionContext *ctx) instance->__put__(ctx, i, ctx->argument(i)); } uint newLen = len + ctx->argumentCount; - if (!instance->isArray) + if (!instance->isArrayObject()) instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); if (newLen < INT_MAX) @@ -506,7 +506,7 @@ Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) fromIndex = (uint) f; } - if (instance->isString) { + if (instance->isStringObject()) { for (uint k = fromIndex; k < len; ++k) { bool exists; Value v = instance->__get__(ctx, k, &exists); diff --git a/qv4jsonobject.cpp b/qv4jsonobject.cpp index 719cb56d8a..a12c4a586a 100644 --- a/qv4jsonobject.cpp +++ b/qv4jsonobject.cpp @@ -895,7 +895,7 @@ Value JsonObject::method_stringify(ExecutionContext *ctx) Object *o = ctx->argument(1).asObject(); if (o) { stringify.replacerFunction = o->asFunctionObject(); - if (o->isArray) { + if (o->isArrayObject()) { for (uint i = 0; i < o->array.length(); ++i) { Value v = o->__get__(ctx, i); if (v.asNumberObject() || v.asStringObject() || v.isNumber()) diff --git a/qv4managed.h b/qv4managed.h index ff1963d1ef..60e8d3be8a 100644 --- a/qv4managed.h +++ b/qv4managed.h @@ -76,7 +76,7 @@ private: protected: Managed() : markBit(0), inUse(1), extensible(true), - isArray(false), isArgumentsObject(false), isString(false), isBuiltinFunction(false), type(Type_Object), unused(0) { } + isNonStrictArgumentsObject(false), isBuiltinFunction(false), type(Type_Object), unused(0) { } virtual ~Managed(); public: @@ -116,6 +116,9 @@ public: ArgumentsObject *asArgumentsObject() { return type == Type_ArgumentsObject ? reinterpret_cast(this) : 0; } ForeachIteratorObject *asForeachIteratorObject() { return type == Type_ForeachIteratorObject ? reinterpret_cast(this) : 0; } + bool isArrayObject() const { return type == Type_ArrayObject; } + bool isStringObject() const { return type == Type_StringObject; } + protected: virtual void markObjects() = 0; @@ -125,18 +128,16 @@ protected: quintptr markBit : 1; quintptr inUse : 1; quintptr extensible : 1; // used by Object - quintptr isArray : 1; // used by Object & Array - quintptr isArgumentsObject : 1; - quintptr isString : 1; // used by Object & StringObject + quintptr isNonStrictArgumentsObject : 1; quintptr isBuiltinFunction : 1; // used by FunctionObject quintptr needsActivation : 1; // used by FunctionObject quintptr usesArgumentsObject : 1; // used by FunctionObject quintptr strictMode : 1; // used by FunctionObject quintptr type : 4; #if CPU(X86_64) - quintptr unused : 50; + quintptr unused : 51; #elif CPU(X86) - quintptr unused : 18; + quintptr unused : 19; #else #error "implement me" #endif diff --git a/qv4object.cpp b/qv4object.cpp index b564698f31..79aee81923 100644 --- a/qv4object.cpp +++ b/qv4object.cpp @@ -209,7 +209,7 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, uint index PropertyDescriptor *p = array.at(index); if(p && p->type != PropertyDescriptor::Generic) return p; - if (isString) + if (isStringObject()) return static_cast(this)->getIndex(ctx, index); return 0; @@ -241,7 +241,7 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, uin PropertyDescriptor *p = o->array.at(index); if(p && p->type != PropertyDescriptor::Generic) return p; - if (o->isString) { + if (o->isStringObject()) { p = static_cast(o)->getIndex(ctx, index); if (p) return p; @@ -306,7 +306,7 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) goto reject; } else if (!pd->isWritable()) goto reject; - else if (isArray && name->isEqualTo(ctx->engine->id_length)) { + else if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { bool ok; uint l = value.asArrayLength(ctx, &ok); if (!ok) @@ -492,7 +492,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr PropertyDescriptor *current; - if (isArray && name->isEqualTo(ctx->engine->id_length)) { + if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { PropertyDescriptor *lp = array.getLengthProperty(); if (desc->isEmpty() || desc->isSubset(lp)) return true; @@ -541,10 +541,10 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Prop PropertyDescriptor *current; // 15.4.5.1, 4b - if (isArray && index >= array.length() && !array.getLengthProperty()->isWritable()) + if (isArrayObject() && index >= array.length() && !array.getLengthProperty()->isWritable()) goto reject; - if (isArgumentsObject) + if (isNonStrictArgumentsObject) return static_cast(this)->defineOwnProperty(ctx, index, desc); // Clause 1 @@ -647,7 +647,6 @@ Value Object::call(ExecutionContext *context, Value , Value *, int) void ArrayObject::init(ExecutionContext *context) { type = Type_ArrayObject; - isArray = true; if (!members) members.reset(new PropertyTable()); diff --git a/qv4stringobject.cpp b/qv4stringobject.cpp index 5829e8c455..420fdb2bf9 100644 --- a/qv4stringobject.cpp +++ b/qv4stringobject.cpp @@ -79,7 +79,6 @@ StringObject::StringObject(ExecutionContext *ctx, const Value &value) : value(value) { type = Type_StringObject; - isString = true; tmpProperty.type = PropertyDescriptor::Data; tmpProperty.enumberable = PropertyDescriptor::Enabled; -- cgit v1.2.3 From fc16cfed8a3ff4eea6b7ff590787c220e8d86f6c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 25 Jan 2013 09:34:54 +0100 Subject: Suppress a linker warning for unused flag. Change-Id: I53aaf5f7bc7ec63e92e02d18010b2f1d53c40a00 Reviewed-by: Simon Hausmann --- v4.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v4.pro b/v4.pro index b76868e7fe..980d0d6155 100644 --- a/v4.pro +++ b/v4.pro @@ -10,7 +10,7 @@ OBJECTS_DIR=.obj isEmpty(LLVM_INSTALL_DIR):LLVM_INSTALL_DIR=$$(LLVM_INSTALL_DIR) !isEmpty(LLVM_INSTALL_DIR):LLVM_CONFIG=$$LLVM_INSTALL_DIR/bin/llvm-config -LIBS += -rdynamic +!macx-clang*:LIBS += -rdynamic SOURCES += main.cpp \ qv4codegen.cpp \ -- cgit v1.2.3 From d39e4ba8d68cc65f75cf2e9ae7d390db553b85d9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 Jan 2013 14:00:48 +0100 Subject: Fix more tests with the delete operator Delete on non-reference types should return true. Delete on our temporaries should evaluate to false. Change-Id: Ic6b39a0cf5833900f2a492ac8b37386fc2963ab3 Reviewed-by: Lars Knoll --- qv4codegen.cpp | 29 ++++++++++++++++++++++------- tests/TestExpectations | 7 ------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 60247ac4aa..9575bc1f63 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1297,19 +1297,34 @@ bool Codegen::visit(ConditionalExpression *ast) bool Codegen::visit(DeleteExpression *ast) { - Result expr = expression(ast->expression); - if ((*expr)->asTemp() && (*expr)->asTemp()->index < 0) { - // expr points to a function argument - if (_function->isStrict) + IR::Expr* expr = *expression(ast->expression); + // Temporaries cannot be deleted + if (expr->asTemp() && expr->asTemp()->index < _env->members.size()) { + // Trying to delete a function argument might throw. + if (_function->isStrict && expr->asTemp()->index < 0) throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); - // can't delete an argument, just evaluate expr for side effects _expr.code = _block->CONST(IR::BoolType, 0); return false; } - if (_function->isStrict && (*expr)->asName()) + if (_function->isStrict && expr->asName()) throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); + + // [[11.4.1]] Return true if it's not a reference + if (expr->asConst() || expr->asString()) { + _expr.code = _block->CONST(IR::BoolType, 1); + return false; + } + + // Return values from calls are also not a reference, but we have to + // perform the call to allow for side effects. + if (expr->asCall()) { + _block->EXP(expr); + _expr.code = _block->CONST(IR::BoolType, 1); + return false; + } + IR::ExprList *args = _function->New(); - args->init(reference(*expr)); + args->init(reference(expr)); _expr.code = call(_block->NAME(IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args); return false; } diff --git a/tests/TestExpectations b/tests/TestExpectations index 3732536751..b04ad1dbbf 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -32,15 +32,8 @@ S11.3.2_A4_T1 failing S11.3.2_A4_T2 failing S11.3.2_A4_T3 failing S11.3.2_A4_T4 failing -11.4.1-0-1 failing -11.4.1-2-1 failing -11.4.1-2-2 failing -11.4.1-2-3 failing -11.4.1-2-4 failing 11.4.1-2-5 failing -11.4.1-2-6 failing 11.4.1-4.a-5 failing -S11.4.1_A1 failing S11.4.1_A2.1 failing S11.5.3_A4_T2 failing S11.8.6_A3 failing -- cgit v1.2.3 From 3935a84333f9e367b6f23fff4135f3cd5652d0e5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 Jan 2013 15:52:42 +0100 Subject: Fix remaining issues with the delete operator Delete on known temps should return true and false on known local variables. Change-Id: I71be8361306eb825b975a3aee294665eb8561366 Reviewed-by: Lars Knoll --- qmljs_environment.cpp | 12 ++++++++++++ qv4codegen.cpp | 4 ++++ tests/TestExpectations | 7 +++---- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index a4db06a358..8b2be30042 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -251,14 +251,26 @@ void ExecutionContext::destroy() bool ExecutionContext::deleteProperty(String *name) { + bool hasWith = false; for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { if (ctx->withObject) { + hasWith = true; if (ctx->withObject->__hasProperty__(this, name)) return ctx->withObject->__delete__(this, name); } else { if (ctx->activation && ctx->activation->__hasProperty__(this, name)) return ctx->activation->__delete__(this, name); } + if (FunctionObject *f = ctx->function) { + if (f->needsActivation || hasWith) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return false; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return false; + } + } } if (strictMode) throwSyntaxError(0); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 9575bc1f63..e5d9994b79 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1322,6 +1322,10 @@ bool Codegen::visit(DeleteExpression *ast) _expr.code = _block->CONST(IR::BoolType, 1); return false; } + if (expr->asTemp() && expr->asTemp()->index >= _env->members.size()) { + _expr.code = _block->CONST(IR::BoolType, 1); + return false; + } IR::ExprList *args = _function->New(); args->init(reference(expr)); diff --git a/tests/TestExpectations b/tests/TestExpectations index b04ad1dbbf..8fffc997ea 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -32,9 +32,6 @@ S11.3.2_A4_T1 failing S11.3.2_A4_T2 failing S11.3.2_A4_T3 failing S11.3.2_A4_T4 failing -11.4.1-2-5 failing -11.4.1-4.a-5 failing -S11.4.1_A2.1 failing S11.5.3_A4_T2 failing S11.8.6_A3 failing S11.8.6_A5_T2 failing @@ -95,4 +92,6 @@ Sbp_A3_T1 failing Sbp_A4_T1 failing Sbp_A4_T2 failing S12.4_A1 failing -S15.2.4.4_A14 failing \ No newline at end of file +S15.2.4.4_A14 failing +# Try/catch scoping issue +S12.14_A4 failing \ No newline at end of file -- cgit v1.2.3 From 57619f11fce2316066ad893d27b125db7e23257b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 Jan 2013 16:03:06 +0100 Subject: Fix empty return statement in eval It should throw a syntax error even inside eval it seems. Change-Id: I4de6d96f9ba61569d7d7e723ecb5f2d946c8f4ad Reviewed-by: Lars Knoll --- qv4codegen.cpp | 2 +- tests/TestExpectations | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index e5d9994b79..00610cb1e4 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -2279,7 +2279,7 @@ bool Codegen::visit(LocalForStatement *ast) bool Codegen::visit(ReturnStatement *ast) { - if (_mode == GlobalCode) + if (_mode != FunctionCode) throwSyntaxError(ast->returnToken, QCoreApplication::translate("qv4codegen", "Return statement outside of function")); if (ast->expression) { Result expr = expression(ast->expression); diff --git a/tests/TestExpectations b/tests/TestExpectations index 8fffc997ea..3f18771fed 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -55,7 +55,6 @@ S14_A2 failing 14.1-5-s failing 15.1.1.3-3 failing S15.1.2.1_A3.2_T8 failing -S15.1.2.1_A3.3_T3 failing S15.1.2.1_A4.1 failing S15.1.2.1_A4.2 failing S15.1.2.1_A4.3 failing -- cgit v1.2.3 From a65d99fb752d02ca7d10a2d604940f7fece50373 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 27 Jan 2013 17:30:40 +0100 Subject: Fix a bug in the evaluation of && Change-Id: Ia446bd05dcca3fc146719284e5ac96d4c7d61c06 Reviewed-by: Erik Verbruggen --- qv4codegen.cpp | 9 ++------- tests/TestExpectations | 4 ---- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 00610cb1e4..0730febfe2 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1095,17 +1095,12 @@ bool Codegen::visit(BinaryExpression *ast) condition(ast->right, _expr.iftrue, _expr.iffalse); } else { IR::BasicBlock *iftrue = _function->newBasicBlock(); - IR::BasicBlock *iffalse = _function->newBasicBlock(); IR::BasicBlock *endif = _function->newBasicBlock(); const unsigned r = _block->newTemp(); - condition(ast->left, iftrue, iffalse); - _block = iffalse; - - move(_block->TEMP(r), _block->CONST(IR::BoolType, 0)); - _block->JUMP(endif); - + move(_block->TEMP(r), *expression(ast->left)); + cjump(_block->TEMP(r), iftrue, endif); _block = iftrue; move(_block->TEMP(r), *expression(ast->right)); _block->JUMP(endif); diff --git a/tests/TestExpectations b/tests/TestExpectations index 3f18771fed..3b3d390c6c 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -13,10 +13,6 @@ S15.1.3.2_A2.5_T1 10.4.3-1-17-s failing 10.4.3-1-63-s failing 10.4.3-1-82-s failing -S11.11.1_A2.1_T1 failing -S11.11.1_A3_T2 failing -S11.11.1_A3_T3 failing -S11.11.1_A3_T4 failing S11.2.1_A3_T1 failing S11.2.1_A3_T2 failing S11.2.1_A3_T3 failing -- cgit v1.2.3 From dac9968b79669e1650f6b771257d79b9d5c3bd5a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 27 Jan 2013 21:27:44 +0100 Subject: test function names for eval or arguments in strict mode using these as function names should trigger a syntax error in strict mode. Change-Id: I8d83fda72db856b692c8f6f1454c762255bcaef1 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 4 ++++ tests/TestExpectations | 12 ------------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 0730febfe2..1490dad8c4 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -324,6 +324,8 @@ protected: virtual bool visit(FunctionExpression *ast) { + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Function name may not be eval or arguments in strict mode")); enterFunction(ast, ast->name.toString(), ast->formals, ast->body); return true; } @@ -346,6 +348,8 @@ protected: virtual bool visit(FunctionDeclaration *ast) { + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Function name may not be eval or arguments in strict mode")); enterFunction(ast, ast->name.toString(), ast->formals, ast->body, ast); return true; } diff --git a/tests/TestExpectations b/tests/TestExpectations index 3b3d390c6c..800340ac13 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -34,18 +34,6 @@ S11.8.6_A5_T2 failing 12.14-13 failing S13_A15_T4 failing S13_A3_T1 failing -13.1-11-s failing -13.1-12-s failing -13.1-13-s failing -13.1-14-s failing -13.1-35-s failing -13.1-36-s failing -13.1-37-s failing -13.1-38-s failing -13.1-39-s failing -13.1-40-s failing -13.1-41-s failing -13.1-42-s failing S13.2.3_A1 failing S14_A2 failing 14.1-5-s failing -- cgit v1.2.3 From db10aa3906b9740cdf64854945842ed3c9e978d0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 27 Jan 2013 22:13:28 +0100 Subject: The function prototype has a length property Change-Id: I1ef4d7c86ac5c5a8e7ed08081a86c4f826527c0d Reviewed-by: Simon Hausmann --- qv4functionobject.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qv4functionobject.cpp b/qv4functionobject.cpp index b580f840e5..c857e23730 100644 --- a/qv4functionobject.cpp +++ b/qv4functionobject.cpp @@ -206,6 +206,8 @@ void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2); defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 1); defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 1); + + defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(0)); } Value FunctionPrototype::method_toString(ExecutionContext *ctx) -- cgit v1.2.3 From 4af0acd57febb0c07a912268f24218d5ca2ee7ec Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 27 Jan 2013 22:31:28 +0100 Subject: Number/Boolean.prototype.toString() also accept matching primitive values These methods also accept matching (ie. booleans or numbers) primitives as this argument. Change-Id: Ib31cadd46d327381abf02847ae35e7be05715f84 Reviewed-by: Simon Hausmann --- qv4booleanobject.cpp | 14 ++++++++++---- qv4numberobject.cpp | 16 ++++++++++------ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/qv4booleanobject.cpp b/qv4booleanobject.cpp index 2a49435a10..9df0f23a6a 100644 --- a/qv4booleanobject.cpp +++ b/qv4booleanobject.cpp @@ -71,11 +71,17 @@ void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) Value BooleanPrototype::method_toString(ExecutionContext *ctx) { - BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); - if (!thisObject) - ctx->throwTypeError(); + bool result; + if (ctx->thisObject.isBoolean()) { + result = ctx->thisObject.booleanValue(); + } else { + BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); + if (!thisObject) + ctx->throwTypeError(); + result = thisObject->value.booleanValue(); + } - return Value::fromString(ctx, QLatin1String(thisObject->value.booleanValue() ? "true" : "false")); + return Value::fromString(ctx, QLatin1String(result ? "true" : "false")); } Value BooleanPrototype::method_valueOf(ExecutionContext *ctx) diff --git a/qv4numberobject.cpp b/qv4numberobject.cpp index 10ab037f68..08711a8187 100644 --- a/qv4numberobject.cpp +++ b/qv4numberobject.cpp @@ -96,9 +96,15 @@ void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) Value NumberPrototype::method_toString(ExecutionContext *ctx) { - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); + double num; + if (ctx->thisObject.isNumber()) { + num = ctx->thisObject.asDouble(); + } else { + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + num = thisObject->value.asDouble(); + } Value arg = ctx->argument(0); if (!arg.isUndefined()) { @@ -109,7 +115,6 @@ Value NumberPrototype::method_toString(ExecutionContext *ctx) return Value::undefinedValue(); } - double num = thisObject->value.asDouble(); if (std::isnan(num)) { return Value::fromString(ctx, QStringLiteral("NaN")); } else if (qIsInf(num)) { @@ -147,8 +152,7 @@ Value NumberPrototype::method_toString(ExecutionContext *ctx) } } - Value internalValue = thisObject->value; - String *str = internalValue.toString(ctx); + String *str = Value::fromDouble(num).toString(ctx); return Value::fromString(str); } -- cgit v1.2.3 From 63a0776c0ee73e7957208b96573a8275504879ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Mon, 28 Jan 2013 12:54:07 +0100 Subject: Fix constness of Value::toXXX functions. Change-Id: I66d622555b2cca4e6aa546745fd0e46373dee919 Reviewed-by: Lars Knoll --- qmljs_value.cpp | 2 +- qmljs_value.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qmljs_value.cpp b/qmljs_value.cpp index 0e4e4f2d82..ec3ff21f9c 100644 --- a/qmljs_value.cpp +++ b/qmljs_value.cpp @@ -46,7 +46,7 @@ namespace QQmlJS { namespace VM { -int Value::toUInt16(ExecutionContext *ctx) +int Value::toUInt16(ExecutionContext *ctx) const { return __qmljs_to_uint16(*this, ctx); } diff --git a/qmljs_value.h b/qmljs_value.h index b6124c0a97..5d0e4956d9 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -198,8 +198,8 @@ struct Value static int toInt32(double value); static unsigned int toUInt32(double value); - int toUInt16(ExecutionContext *ctx); - int toInt32(ExecutionContext *ctx); + int toUInt16(ExecutionContext *ctx) const; + int toInt32(ExecutionContext *ctx) const; unsigned int toUInt32(ExecutionContext *ctx) const; Bool toBoolean(ExecutionContext *ctx) const; @@ -343,7 +343,7 @@ inline Value Value::fromObject(Object *o) return v; } -inline int Value::toInt32(ExecutionContext *ctx) +inline int Value::toInt32(ExecutionContext *ctx) const { if (isConvertibleToInt()) return int_32; -- cgit v1.2.3 From 1c1f1a3903a5980b90c8850818eb87cc490fa47f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 27 Jan 2013 22:39:01 +0100 Subject: Fix call expressions with a subscript Call expressions such as foo[bar]() where wrongly converted to a temporary and then called by value. This made the call have the wrong this argument, as the reference was resolved in the assignment to the temporary. Change-Id: Id6044f833dfcf9ee8fa4f9ec0602a929abdc2c48 Reviewed-by: Simon Hausmann --- moth/qv4instr_moth_p.h | 10 ++++++++++ moth/qv4isel_moth.cpp | 11 +++++++++++ moth/qv4isel_moth_p.h | 1 + moth/qv4vme_moth.cpp | 9 +++++++++ qmljs_runtime.cpp | 19 ++++++++++++++++++- qmljs_runtime.h | 1 + qv4codegen.cpp | 7 ++----- qv4isel_masm.cpp | 11 +++++++++++ qv4isel_masm_p.h | 1 + qv4isel_p.cpp | 5 +++++ qv4isel_p.h | 1 + tests/TestExpectations | 4 ---- 12 files changed, 70 insertions(+), 10 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 28d72cb10d..eb48a0bfd3 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -18,6 +18,7 @@ F(Push, push) \ F(CallValue, callValue) \ F(CallProperty, callProperty) \ + F(CallElement, callElement) \ F(CallActivationProperty, callActivationProperty) \ F(CallBuiltin, callBuiltin) \ F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ @@ -154,6 +155,14 @@ union Instr quint32 args; int targetTempIndex; }; + struct instr_callElement { + MOTH_INSTR_HEADER + int index; + int baseTemp; + quint32 argc; + quint32 args; + int targetTempIndex; + }; struct instr_callActivationProperty { MOTH_INSTR_HEADER VM::String *name; @@ -335,6 +344,7 @@ union Instr instr_push push; instr_callValue callValue; instr_callProperty callProperty; + instr_callElement callElement; instr_callActivationProperty callActivationProperty; instr_callBuiltin callBuiltin; instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index eab1e63519..b2e67816d4 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -349,6 +349,17 @@ void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR: addInstruction(call); } +void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) +{ + // call the property on the loaded base + Instruction::CallElement call; + call.baseTemp = base->index; + call.index = index->index; + prepareCallArgs(args, call.argc, call.args); + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result) diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 3874adf527..1f67fef18e 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -47,6 +47,7 @@ protected: virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result); virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 5052d82c46..6358e21922 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -234,6 +234,15 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co TEMP(instr.targetTempIndex) = __qmljs_call_property(context, base, instr.name, args, instr.argc); MOTH_END_INSTR(CallProperty) + MOTH_BEGIN_INSTR(CallElement) + int argStart = instr.args - context->variableCount(); + // TODO: change this assert everywhere to include a minimum + // TODO: the args calculation is duplicate code, fix that + VM::Value *args = stack + argStart; + VM::Value base = TEMP(instr.baseTemp); + TEMP(instr.targetTempIndex) = __qmljs_call_element(context, base, TEMP(instr.index), args, instr.argc); + MOTH_END_INSTR(CallProperty) + MOTH_BEGIN_INSTR(CallActivationProperty) int argStart = instr.args - context->variableCount(); // TODO: change this assert everywhere to include a minimum diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index e297dabd01..fca7e2e250 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -580,7 +580,7 @@ Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) uint idx = index.asArrayIndex(); if (type != Value::Object_Type) { - if (type == Value::String_Type) { + if (type == Value::String_Type && idx < UINT_MAX) { String *str = object.stringValue(); if (idx >= (uint)str->toQString().length()) return Value::undefinedValue(); @@ -764,6 +764,23 @@ Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, return o->call(context, thisObject, args, argc); } +Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc) +{ + Value thisObject = that; + if (!thisObject.isObject()) + thisObject = __qmljs_to_object(thisObject, context); + + assert(thisObject.isObject()); + Object *baseObject = thisObject.objectValue(); + + Value func = baseObject->__get__(context, index.toString(context)); + Object *o = func.asObject(); + if (!o) + context->throwTypeError(); + + return o->call(context, thisObject, args, argc); +} + Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc) { Object *o = func.asObject(); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 870e1d315a..11437cda36 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -88,6 +88,7 @@ extern "C" { // context Value __qmljs_call_activation_property(ExecutionContext *, String *name, Value *args, int argc); Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, Value *args, int argc); +Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc); Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc); Value __qmljs_construct_activation_property(ExecutionContext *, String *name, Value *args, int argc); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 1490dad8c4..462aeea879 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -684,11 +684,8 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args) { - if (base->asMember() || base->asName() || base->asTemp()) - return _block->CALL(base, args); - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), base); - return _block->CALL(_block->TEMP(t), args); + base = reference(base); + return _block->CALL(base, args); } void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 15cf5fc535..88c6417dba 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -740,6 +740,17 @@ void InstructionSelection::callProperty(IR::Temp *base, const QString &name, Assembler::TrustedImm32(argc)); } +void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) +{ + assert(base != 0); + + int argc = prepareVariableArguments(args); + generateFunctionCall(result, __qmljs_call_element, + Assembler::ContextRegister, base, index, + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); +} + String *InstructionSelection::identifier(const QString &s) { return engine()->identifier(s); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 35f48522ec..64b6311640 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -674,6 +674,7 @@ protected: virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result); virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); virtual void loadThisObject(IR::Temp *temp); virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp); diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 03600fdcd4..c7ed89ea1f 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -135,6 +135,9 @@ void InstructionSelection::visitMove(IR::Move *s) } else if (Member *member = c->base->asMember()) { callProperty(member->base, *member->name, c->args, t); return; + } else if (Subscript *s = c->base->asSubscript()) { + callSubscript(s->base, s->index, c->args, t); + return; } else if (IR::Temp *value = c->base->asTemp()) { callValue(value, c->args, t); return; @@ -210,6 +213,8 @@ void InstructionSelection::visitExp(IR::Exp *s) callValue(value, c->args, 0); } else if (Member *member = c->base->asMember()) { callProperty(member->base, *member->name, c->args, 0); + } else if (Subscript *s = c->base->asSubscript()) { + callSubscript(s->base, s->index, c->args, 0); } else { Q_UNIMPLEMENTED(); } diff --git a/qv4isel_p.h b/qv4isel_p.h index 8b932ae693..8861484f26 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -103,6 +103,7 @@ public: // to implement by subclasses: virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) = 0; virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) = 0; virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) = 0; + virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) = 0; virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result) = 0; virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) = 0; virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) = 0; diff --git a/tests/TestExpectations b/tests/TestExpectations index 800340ac13..c35ba29baf 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -13,10 +13,6 @@ S15.1.3.2_A2.5_T1 10.4.3-1-17-s failing 10.4.3-1-63-s failing 10.4.3-1-82-s failing -S11.2.1_A3_T1 failing -S11.2.1_A3_T2 failing -S11.2.1_A3_T3 failing -S11.2.1_A4_T3 failing 11.2.3-3_3 failing S11.3.1_A2.2_T1 failing S11.3.1_A4_T1 failing -- cgit v1.2.3 From 9ad7428fe683b2ccc729f77dc541e6ac20d50829 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 28 Jan 2013 10:39:40 +0100 Subject: Properly set the length of the eval function Change-Id: I88a483f5c9a758c4983d5d3b2c684f6bc7e8cf55 Reviewed-by: Simon Hausmann --- qv4globalobject.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index f75d313482..337196c6a2 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -311,6 +311,7 @@ EvalFunction::EvalFunction(ExecutionContext *scope) : FunctionObject(scope) { name = scope->engine->id_eval; + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); } Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc, bool directCall) -- cgit v1.2.3 From 3bee6a7b2b18916e90d37e2e26b81a1047ed147f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 28 Jan 2013 10:40:30 +0100 Subject: Most statements don't return a value In eval mode, we need to write the result of statements into the return value. But most statements don't return a value, so the statement shouldn't write a return value. Change-Id: I9ce24fe6689bd2bb2aee6241ca84a25f9a266f5c Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 462aeea879..b5c0e9d765 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -726,10 +726,6 @@ void Codegen::statement(ExpressionNode *ast) { if (! ast) { return; - } else if (_mode == EvalCode) { - Result e = expression(ast); - if (*e) - move(_block->TEMP(_returnAddress), *e); } else { Result r(nx); qSwap(_expr, r); @@ -2091,7 +2087,13 @@ bool Codegen::visit(EmptyStatement *) bool Codegen::visit(ExpressionStatement *ast) { - statement(ast->expression); + if (_mode == EvalCode) { + Result e = expression(ast->expression); + if (*e) + move(_block->TEMP(_returnAddress), *e); + } else { + statement(ast->expression); + } return false; } -- cgit v1.2.3 From 0e51ac4d87488e77cf78179181d8ee0ff5d9f101 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 28 Jan 2013 10:41:42 +0100 Subject: Set the prototype for FunctionObject in it's constructor This avoids a few test failures where the prototype for eval wasn't set up correctly. Change-Id: I5a5c1dafe4a6328bf1bbc95901ff22df10083a86 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 1 - qv4functionobject.cpp | 20 ++++++++++++++++---- qv4functionobject.h | 14 +------------- tests/TestExpectations | 5 ----- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 2008dbddb4..4b53d06b72 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -263,7 +263,6 @@ Function *ExecutionEngine::newFunction(const QString &name) FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) { BuiltinFunction *f = new (memoryManager) BuiltinFunction(scope, name, code); - f->prototype = scope->engine->functionPrototype; return f; } diff --git a/qv4functionobject.cpp b/qv4functionobject.cpp index c857e23730..a55f96dd26 100644 --- a/qv4functionobject.cpp +++ b/qv4functionobject.cpp @@ -69,6 +69,22 @@ Function::~Function() delete[] codeData; } +FunctionObject::FunctionObject(ExecutionContext *scope) + : scope(scope) + , name(0) + , formalParameterList(0) + , varList(0) + , formalParameterCount(0) + , varCount(0) +{ + prototype = scope->engine->functionPrototype; + + type = Type_FunctionObject; + needsActivation = false; + usesArgumentsObject = false; + strictMode = false; +} + bool FunctionObject::hasInstance(ExecutionContext *ctx, const Value &value) { if (! value.isObject()) @@ -320,8 +336,6 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) pd->configurable = PropertyDescriptor::Disabled; pd->value = Value::fromObject(proto); - prototype = scope->engine->functionPrototype; - if (scope->strictMode) { FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); @@ -365,8 +379,6 @@ BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Va , boundThis(boundThis) , boundArgs(boundArgs) { - prototype = scope->engine->functionPrototype; - int len = target->__get__(scope, scope->engine->id_length).toUInt32(scope); len -= boundArgs.size(); if (len < 0) diff --git a/qv4functionobject.h b/qv4functionobject.h index a62863c430..ad49cdccd8 100644 --- a/qv4functionobject.h +++ b/qv4functionobject.h @@ -136,19 +136,7 @@ struct FunctionObject: Object { unsigned int formalParameterCount; unsigned int varCount; - FunctionObject(ExecutionContext *scope) - : scope(scope) - , name(0) - , formalParameterList(0) - , varList(0) - , formalParameterCount(0) - , varCount(0) - { - type = Type_FunctionObject; - needsActivation = false; - usesArgumentsObject = false; - strictMode = false; - } + FunctionObject(ExecutionContext *scope); virtual QString className() { return QStringLiteral("Function"); } virtual bool hasInstance(ExecutionContext *ctx, const Value &value); diff --git a/tests/TestExpectations b/tests/TestExpectations index c35ba29baf..bc08dd4921 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -34,11 +34,6 @@ S13.2.3_A1 failing S14_A2 failing 14.1-5-s failing 15.1.1.3-3 failing -S15.1.2.1_A3.2_T8 failing -S15.1.2.1_A4.1 failing -S15.1.2.1_A4.2 failing -S15.1.2.1_A4.3 failing -S15.1.2.1_A4.4 failing S15.1.2.1_A4.7 failing S15.12.2_A1 failing 15.2.3.6-4-360-3 failing -- cgit v1.2.3 From fb11343a072ddc67518df0946ff58a6c4d9e8041 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 28 Jan 2013 13:32:08 +0100 Subject: Add a mechanism to protect managed objects from deletion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I6700dc94ad481e12ee7f00c04c0c37261e22a715 Reviewed-by: JÄ™drzej Nowacki --- qv4mm.cpp | 31 +++++++++++++++++++++++-------- qv4mm.h | 9 ++++++--- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/qv4mm.cpp b/qv4mm.cpp index e4f1fbe3dc..71e6def7db 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -64,6 +64,7 @@ struct MemoryManager::Data }; QVector heapChunks; + QHash protectedObject; // statistics: #ifdef DETAILED_MM_STATS @@ -165,12 +166,12 @@ void MemoryManager::scribble(Managed *obj, int c, int size) const ::memset((void *)(obj + 1), c, size - sizeof(Managed)); } -void MemoryManager::mark(const QVector &objects) +void MemoryManager::mark(const QVector &objects) { - foreach (Object *o, objects) { - if (!o) + foreach (Managed *m, objects) { + if (!m) continue; - o->mark(); + m->mark(); } return; @@ -239,7 +240,7 @@ void MemoryManager::runGC() // QTime t; t.start(); // qDebug() << ">>>>>>>>runGC"; - QVector roots; + QVector roots; collectRoots(roots); // std::cerr << "GC: found " << roots.size() // << " roots in " << t.elapsed() @@ -268,7 +269,18 @@ MemoryManager::~MemoryManager() sweep(); } -static inline void add(QVector &values, const Value &v) +void MemoryManager::protect(Managed *m) +{ + ++m_d->protectedObject[m]; +} + +void MemoryManager::unprotect(Managed *m) +{ + if (!--m_d->protectedObject[m]) + m_d->protectedObject.remove(m); +} + +static inline void add(QVector &values, const Value &v) { if (Object *o = v.asObject()) values.append(o); @@ -310,7 +322,7 @@ void MemoryManager::willAllocate(std::size_t size) #endif // DETAILED_MM_STATS -void MemoryManager::collectRoots(QVector &roots) const +void MemoryManager::collectRoots(QVector &roots) const { add(roots, m_d->engine->globalObject); add(roots, m_d->engine->exception); @@ -342,9 +354,12 @@ void MemoryManager::collectRoots(QVector &roots) const } collectRootsOnStack(roots); + + for (QHash::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it) + roots.append(it.key()); } -void MemoryManager::collectRootsOnStack(QVector &roots) const +void MemoryManager::collectRootsOnStack(QVector &roots) const { if (!m_d->heapChunks.count()) return; diff --git a/qv4mm.h b/qv4mm.h index 0dd3e61f09..07c6e959ad 100644 --- a/qv4mm.h +++ b/qv4mm.h @@ -73,6 +73,9 @@ public: MemoryManager(); ~MemoryManager(); + void protect(Managed *m); + void unprotect(Managed *m); + // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries). // Note: all occurances of "16" in alloc/dealloc are also due to the alignment. static inline std::size_t align(std::size_t size) @@ -101,7 +104,7 @@ protected: void scribble(Managed *obj, int c, int size) const; - void collectRootsOnStack(QVector &roots) const; + void collectRootsOnStack(QVector &roots) const; ExecutionEngine *engine() const; @@ -110,8 +113,8 @@ protected: #endif // DETAILED_MM_STATS private: - void collectRoots(QVector &roots) const; - static void mark(const QVector &objects); + void collectRoots(QVector &roots) const; + static void mark(const QVector &objects); std::size_t sweep(); std::size_t sweep(char *chunkStart, std::size_t chunkSize, size_t size); -- cgit v1.2.3 From 4db1a52e313e0534bee024699d83317ba0b6ad1b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 28 Jan 2013 10:50:07 +0100 Subject: new eval() should throw a type error Change-Id: I487a11a060657ac1eeae4c51085ce907711b51c8 Reviewed-by: Simon Hausmann --- qv4globalobject.cpp | 6 ++++++ qv4globalobject.h | 2 ++ tests/TestExpectations | 1 - 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index 337196c6a2..9adffce1da 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -370,6 +370,12 @@ Value EvalFunction::call(ExecutionContext *context, Value thisObject, Value *arg return evalCall(context, thisObject, args, argc, false); } +Value EvalFunction::construct(ExecutionContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, const QString &source, QQmlJS::Codegen::Mode mode, diff --git a/qv4globalobject.h b/qv4globalobject.h index 2292e380d6..1ad76cf0a4 100644 --- a/qv4globalobject.h +++ b/qv4globalobject.h @@ -59,6 +59,8 @@ struct EvalFunction : FunctionObject virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); Value evalCall(ExecutionContext *context, Value thisObject, Value *args, int argc, bool directCall); + + Value construct(ExecutionContext *ctx); }; struct GlobalFunctions diff --git a/tests/TestExpectations b/tests/TestExpectations index bc08dd4921..7469238342 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -34,7 +34,6 @@ S13.2.3_A1 failing S14_A2 failing 14.1-5-s failing 15.1.1.3-3 failing -S15.1.2.1_A4.7 failing S15.12.2_A1 failing 15.2.3.6-4-360-3 failing 15.2.3.6-4-360-7 failing -- cgit v1.2.3 From ff643446f595c028f158ef95f3ca0ef23e072a17 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 28 Jan 2013 11:42:01 +0100 Subject: Smaller cleanup Change-Id: I42a1de385e286bf1e4568769886034161f7fe500 Reviewed-by: Simon Hausmann --- qv4codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4codegen.cpp b/qv4codegen.cpp index b5c0e9d765..9b1b7cd4f1 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1165,7 +1165,7 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::InplaceURightShift: case QSOperator::InplaceXor: { IR::Expr* right = *expression(ast->right); - if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) + if (!left->isLValue()) throwSyntaxError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of inplace operator is not an lvalue")); if (_expr.accept(nx)) { -- cgit v1.2.3 From d8e0c2e0e5c8f80d8a6d929fdb9153136bf03b7d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 27 Jan 2013 21:16:09 +0100 Subject: Properly working post increment/decrement operators This required some larger changes in our infrastructure. Unfortunately the post increment/decrement operators don't return the old value unmodified, but return toNumber(oldValue). At the same time they need to properly store the new value into the referenced expression. The only way to solve this (as we can't have two return values) is to pass a proper reference into runtime methods. Change-Id: I0e0c2cc011ab22d5d4b27924abc8c18372c104a5 Reviewed-by: Simon Hausmann --- moth/qv4instr_moth_p.h | 60 +++++++++++++++++++ moth/qv4isel_moth.cpp | 68 +++++++++++++++++++++ moth/qv4isel_moth_p.h | 8 +++ moth/qv4vme_moth.cpp | 33 +++++++++++ qmljs_runtime.cpp | 156 +++++++++++++++++++++++++++++++++++++++++++++++++ qmljs_runtime.h | 10 ++++ qv4codegen.cpp | 28 ++++----- qv4ir.cpp | 4 ++ qv4ir_p.h | 2 + qv4isel_masm.cpp | 40 +++++++++++++ qv4isel_masm_p.h | 30 ++++++++++ qv4isel_p.cpp | 34 +++++++++++ qv4isel_p.h | 8 +++ tests/TestExpectations | 10 ---- 14 files changed, 465 insertions(+), 26 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index eb48a0bfd3..5f7d649636 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -30,6 +30,14 @@ F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \ F(CallBuiltinTypeofName, callBuiltinTypeofName) \ F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \ + F(CallBuiltinPostIncMember, callBuiltinPostIncMember) \ + F(CallBuiltinPostIncSubscript, callBuiltinPostIncSubscript) \ + F(CallBuiltinPostIncName, callBuiltinPostIncName) \ + F(CallBuiltinPostIncValue, callBuiltinPostIncValue) \ + F(CallBuiltinPostDecMember, callBuiltinPostDecMember) \ + F(CallBuiltinPostDecSubscript, callBuiltinPostDecSubscript) \ + F(CallBuiltinPostDecName, callBuiltinPostDecName) \ + F(CallBuiltinPostDecValue, callBuiltinPostDecValue) \ F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ F(CallBuiltinDefineGetterSetter, callBuiltinDefineGetterSetter) \ F(CallBuiltinDefineProperty, callBuiltinDefineProperty) \ @@ -232,6 +240,50 @@ union Instr int tempIndex; int targetTempIndex; }; + struct instr_callBuiltinPostIncMember { + MOTH_INSTR_HEADER + int base; + VM::String *member; + int targetTempIndex; + }; + struct instr_callBuiltinPostIncSubscript { + MOTH_INSTR_HEADER + int base; + int index; + int targetTempIndex; + }; + struct instr_callBuiltinPostIncName { + MOTH_INSTR_HEADER + VM::String *name; + int targetTempIndex; + }; + struct instr_callBuiltinPostIncValue { + MOTH_INSTR_HEADER + int tempIndex; + int targetTempIndex; + }; + struct instr_callBuiltinPostDecMember { + MOTH_INSTR_HEADER + int base; + VM::String *member; + int targetTempIndex; + }; + struct instr_callBuiltinPostDecSubscript { + MOTH_INSTR_HEADER + int base; + int index; + int targetTempIndex; + }; + struct instr_callBuiltinPostDecName { + MOTH_INSTR_HEADER + VM::String *name; + int targetTempIndex; + }; + struct instr_callBuiltinPostDecValue { + MOTH_INSTR_HEADER + int tempIndex; + int targetTempIndex; + }; struct instr_callBuiltinDeclareVar { MOTH_INSTR_HEADER bool isDeletable; @@ -356,6 +408,14 @@ union Instr instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript; instr_callBuiltinTypeofName callBuiltinTypeofName; instr_callBuiltinTypeofValue callBuiltinTypeofValue; + instr_callBuiltinPostIncMember callBuiltinPostIncMember; + instr_callBuiltinPostIncSubscript callBuiltinPostIncSubscript; + instr_callBuiltinPostIncName callBuiltinPostIncName; + instr_callBuiltinPostIncValue callBuiltinPostIncValue; + instr_callBuiltinPostDecMember callBuiltinPostDecMember; + instr_callBuiltinPostDecSubscript callBuiltinPostDecSubscript; + instr_callBuiltinPostDecName callBuiltinPostDecName; + instr_callBuiltinPostDecValue callBuiltinPostDecValue; instr_callBuiltinDeclareVar callBuiltinDeclareVar; instr_callBuiltinDefineGetterSetter callBuiltinDefineGetterSetter; instr_callBuiltinDefineProperty callBuiltinDefineProperty; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index b2e67816d4..46cff05b69 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -769,6 +769,74 @@ void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) addInstruction(load); } +void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinPostDecMember call; + call.base = base->index; + call.member = engine()->identifier(name); + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + Instruction::CallBuiltinPostDecSubscript call; + call.base = base->index; + call.index = index->index; + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinPostDecName call; + call.name = engine()->identifier(name); + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) +{ + Instruction::CallBuiltinPostDecValue call; + call.tempIndex = value->index; + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinPostIncMember call; + call.base = base->index; + call.member = engine()->identifier(name); + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + Instruction::CallBuiltinPostIncSubscript call; + call.base = base->index; + call.index = index->index; + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinPostIncName call; + call.name = engine()->identifier(name); + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) +{ + Instruction::CallBuiltinPostIncValue call; + call.tempIndex = value->index; + call.targetTempIndex = result ? result->index : scratchTempIndex(); + addInstruction(call); +} + void InstructionSelection::callBuiltinThrow(IR::Temp *arg) { Instruction::CallBuiltin call; diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index 1f67fef18e..c9b32d688f 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -33,6 +33,14 @@ protected: virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); virtual void callBuiltinDeleteValue(IR::Temp *result); + virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); virtual void callBuiltinDeleteExceptionHandler(); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 6358e21922..5786a546b7 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -116,6 +116,7 @@ private: }; #define TEMP(index) *tempValue(context, stack, index) +#define TEMPPTR(index) tempValue(context, stack, index) VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code #ifdef MOTH_THREADED_INTERPRETER @@ -336,6 +337,38 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof(TEMP(instr.tempIndex), context); MOTH_END_INSTR(CallBuiltinTypeofValue) + MOTH_BEGIN_INSTR(CallBuiltinPostIncMember) + TEMP(instr.targetTempIndex) = __qmljs_builtin_post_increment_member(TEMP(instr.base), instr.member, context); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncSubscript) + TEMP(instr.targetTempIndex) = __qmljs_builtin_post_increment_element(TEMP(instr.base), TEMP(instr.index), context); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncName) + TEMP(instr.targetTempIndex) = __qmljs_builtin_post_increment_name(instr.name, context); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncValue) + TEMP(instr.targetTempIndex) = __qmljs_builtin_post_increment(TEMPPTR(instr.tempIndex), context); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecMember) + TEMP(instr.targetTempIndex) = __qmljs_builtin_post_decrement_member(TEMP(instr.base), instr.member, context); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecSubscript) + TEMP(instr.targetTempIndex) = __qmljs_builtin_post_decrement_element(TEMP(instr.base), TEMP(instr.index), context); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecName) + TEMP(instr.targetTempIndex) = __qmljs_builtin_post_decrement_name(instr.name, context); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecValue) + TEMP(instr.targetTempIndex) = __qmljs_builtin_post_decrement(TEMPPTR(instr.tempIndex), context); + MOTH_END_INSTR(CallBuiltinTypeofValue) + MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) __qmljs_builtin_declare_var(context, instr.isDeletable, instr.varName); MOTH_END_INSTR(CallBuiltinDeclareVar) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index fca7e2e250..4fdf2d31fe 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -906,6 +906,162 @@ Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext * return __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context); } +Value __qmljs_builtin_post_increment(Value *val, ExecutionContext *ctx) +{ + if (val->isInteger() && val->integerValue() < INT_MAX) { + Value retval = *val; + val->int_32 += 1; + return retval; + } + + double d = __qmljs_to_number(*val, ctx); + *val = Value::fromDouble(d + 1); + return Value::fromDouble(d); +} + +Value __qmljs_builtin_post_increment_name(String *name, ExecutionContext *context) +{ + Value v = context->getProperty(name); + Value retval; + + if (v.isInteger() && v.integerValue() < INT_MAX) { + retval = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v, context); + retval = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + context->setProperty(name, v); + return retval; +} + +Value __qmljs_builtin_post_increment_member(Value base, String *name, ExecutionContext *context) +{ + Object *o = __qmljs_to_object(base, context).objectValue(); + + Value v = o->__get__(context, name); + Value retval; + + if (v.isInteger() && v.integerValue() < INT_MAX) { + retval = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v, context); + retval = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + o->__put__(context, name, v); + return retval; +} + +Value __qmljs_builtin_post_increment_element(Value base, Value index, ExecutionContext *context) +{ + Object *o = __qmljs_to_object(base, context).objectValue(); + + uint idx = index.asArrayIndex(); + + if (idx == UINT_MAX) { + String *s = index.toString(context); + return __qmljs_builtin_post_increment_member(base, s, context); + } + + Value v = o->__get__(context, idx); + Value retval; + + if (v.isInteger() && v.integerValue() < INT_MAX) { + retval = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v, context); + retval = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + o->__put__(context, idx, v); + return retval; +} + +Value __qmljs_builtin_post_decrement(Value *val, ExecutionContext *ctx) +{ + if (val->isInteger() && val->integerValue() > INT_MIN) { + Value retval = *val; + val->int_32 -= 1; + return retval; + } + + double d = __qmljs_to_number(*val, ctx); + *val = Value::fromDouble(d - 1); + return Value::fromDouble(d); +} + +Value __qmljs_builtin_post_decrement_name(String *name, ExecutionContext *context) +{ + Value v = context->getProperty(name); + Value retval; + + if (v.isInteger() && v.integerValue() > INT_MIN) { + retval = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v, context); + retval = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + context->setProperty(name, v); + return retval; +} + +Value __qmljs_builtin_post_decrement_member(Value base, String *name, ExecutionContext *context) +{ + Object *o = __qmljs_to_object(base, context).objectValue(); + + Value v = o->__get__(context, name); + Value retval; + + if (v.isInteger() && v.integerValue() > INT_MIN) { + retval = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v, context); + retval = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + o->__put__(context, name, v); + return retval; +} + +Value __qmljs_builtin_post_decrement_element(Value base, Value index, ExecutionContext *context) +{ + Object *o = __qmljs_to_object(base, context).objectValue(); + + uint idx = index.asArrayIndex(); + + if (idx == UINT_MAX) { + String *s = index.toString(context); + return __qmljs_builtin_post_decrement_member(base, s, context); + } + + Value v = o->__get__(context, idx); + Value retval; + + if (v.isInteger() && v.integerValue() > INT_MIN) { + retval = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v, context); + retval = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + o->__put__(context, idx, v); + return retval; +} + void __qmljs_builtin_throw(Value val, ExecutionContext *context) { __qmljs_throw(val, context); diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 11437cda36..830dc9a628 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -100,6 +100,16 @@ Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context); Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext *context); Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context); +Value __qmljs_builtin_post_increment(Value *val, ExecutionContext *ctx); +Value __qmljs_builtin_post_increment_name(String *name, ExecutionContext *context); +Value __qmljs_builtin_post_increment_member(Value base, String *name, ExecutionContext *context); +Value __qmljs_builtin_post_increment_element(Value base, Value index, ExecutionContext *context); + +Value __qmljs_builtin_post_decrement(Value *val, ExecutionContext *ctx); +Value __qmljs_builtin_post_decrement_name(String *name, ExecutionContext *context); +Value __qmljs_builtin_post_decrement_member(Value base, String *name, ExecutionContext *context); +Value __qmljs_builtin_post_decrement_element(Value base, Value index, ExecutionContext *context); + void __qmljs_builtin_throw(Value val, ExecutionContext *context); void __qmljs_builtin_rethrow(ExecutionContext *context); ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx); diff --git a/qv4codegen.cpp b/qv4codegen.cpp index 9b1b7cd4f1..4c4f7dd1d6 100644 --- a/qv4codegen.cpp +++ b/qv4codegen.cpp @@ -1539,30 +1539,26 @@ bool Codegen::visit(ObjectLiteral *ast) bool Codegen::visit(PostDecrementExpression *ast) { Result expr = expression(ast->base); + if (!expr->isLValue()) + throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); - if (_expr.accept(nx)) { - move(*expr, unop(IR::OpDecrement, *expr)); - } else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), *expr); - move(*expr, unop(IR::OpDecrement, _block->TEMP(t))); - _expr.code = _block->TEMP(t); - } + + IR::ExprList *args = _function->New(); + args->init(*expr); + _expr.code = call(_block->NAME(IR::Name::builtin_postdecrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); return false; } bool Codegen::visit(PostIncrementExpression *ast) { Result expr = expression(ast->base); + if (!expr->isLValue()) + throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); - if (_expr.accept(nx)) { - move(*expr, unop(IR::OpIncrement, *expr)); - } else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), *expr); - move(*expr, unop(IR::OpIncrement, _block->TEMP(t))); - _expr.code = _block->TEMP(t); - } + + IR::ExprList *args = _function->New(); + args->init(*expr); + _expr.code = call(_block->NAME(IR::Name::builtin_postincrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); return false; } diff --git a/qv4ir.cpp b/qv4ir.cpp index a34a2943a5..1c2a9d9e62 100644 --- a/qv4ir.cpp +++ b/qv4ir.cpp @@ -224,6 +224,10 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_typeof"; case Name::builtin_delete: return "builtin_delete"; + case Name::builtin_postincrement: + return "builtin_postincrement"; + case Name::builtin_postdecrement: + return "builtin_postdecrement"; case Name::builtin_throw: return "builtin_throw"; case Name::builtin_create_exception_handler: diff --git a/qv4ir_p.h b/qv4ir_p.h index f77cb58597..64f59f41d4 100644 --- a/qv4ir_p.h +++ b/qv4ir_p.h @@ -275,6 +275,8 @@ struct Name: Expr { builtin_invalid, builtin_typeof, builtin_delete, + builtin_postincrement, + builtin_postdecrement, builtin_throw, builtin_create_exception_handler, builtin_delete_exception_handler, diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 88c6417dba..e6bd218c62 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -478,6 +478,46 @@ void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) _asm->storeValue(Value::fromBoolean(false), result); } +void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_increment_member, base, identifier(name), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_increment_element, base, index, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_increment_name, identifier(name), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_increment, Assembler::PointerToValue(value), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_decrement_member, base, identifier(name), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_decrement_element, base, index, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_decrement_name, identifier(name), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_decrement, Assembler::PointerToValue(value), Assembler::ContextRegister); +} + void InstructionSelection::callBuiltinThrow(IR::Temp *arg) { generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, arg, Assembler::ContextRegister); diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 64b6311640..347db94b35 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -165,6 +165,10 @@ public: FunctionPtr externalFunction; const char* functionName; }; + struct PointerToValue { + PointerToValue(IR::Temp *value) : value(value) {} + IR::Temp *value; + }; void callAbsolute(const char* functionName, FunctionPtr function) { CallToLink ctl; @@ -196,6 +200,14 @@ public: } #ifdef VALUE_FITS_IN_REGISTER + void loadArgument(PointerToValue temp, RegisterID dest) + { + assert(temp.value); + + Pointer addr = loadTempAddress(dest, temp.value); + loadArgument(addr, dest); + } + void loadArgument(IR::Temp* temp, RegisterID dest) { if (!temp) { @@ -297,6 +309,14 @@ public: #endif } + void push(PointerToValue temp) + { + assert (temp.value); + + Pointer ptr = loadTempAddress(ScratchRegister, temp.value); + push(ptr); + } + void push(IR::Temp* temp) { if (temp) { @@ -401,6 +421,8 @@ public: { return sizeof(void*); } static inline int sizeOfArgument(VM::String* string) { return sizeof(string); } + static inline int sizeOfArgument(const PointerToValue &) + { return sizeof(void *); } static inline int sizeOfArgument(TrustedImmPtr) { return sizeof(void*); } static inline int sizeOfArgument(TrustedImm32) @@ -661,6 +683,14 @@ protected: virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); virtual void callBuiltinDeleteValue(IR::Temp *result); + virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); virtual void callBuiltinDeleteExceptionHandler(); diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index c7ed89ea1f..8cc7febdd9 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -267,6 +267,40 @@ void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result) } } break; + case IR::Name::builtin_postincrement: { + if (IR::Member *m = call->args->expr->asMember()) { + callBuiltinPostIncrementMember(m->base->asTemp(), *m->name, result); + return; + } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinPostIncrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (IR::Name *n = call->args->expr->asName()) { + callBuiltinPostIncrementName(*n->id, result); + return; + } else if (IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinPostIncrementValue(arg, result); + return; + } + } break; + + case IR::Name::builtin_postdecrement: { + if (IR::Member *m = call->args->expr->asMember()) { + callBuiltinPostDecrementMember(m->base->asTemp(), *m->name, result); + return; + } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinPostDecrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (IR::Name *n = call->args->expr->asName()) { + callBuiltinPostDecrementName(*n->id, result); + return; + } else if (IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinPostDecrementValue(arg, result); + return; + } + } break; + case IR::Name::builtin_throw: { IR::Temp *arg = call->args->expr->asTemp(); assert(arg != 0); diff --git a/qv4isel_p.h b/qv4isel_p.h index 8861484f26..0fb3e8de39 100644 --- a/qv4isel_p.h +++ b/qv4isel_p.h @@ -89,6 +89,14 @@ public: // to implement by subclasses: virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0; virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result) = 0; virtual void callBuiltinDeleteValue(IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) = 0; virtual void callBuiltinThrow(IR::Temp *arg) = 0; virtual void callBuiltinCreateExceptionHandler(IR::Temp *result) = 0; virtual void callBuiltinDeleteExceptionHandler() = 0; diff --git a/tests/TestExpectations b/tests/TestExpectations index 7469238342..265d083435 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -14,16 +14,6 @@ S15.1.3.2_A2.5_T1 10.4.3-1-63-s failing 10.4.3-1-82-s failing 11.2.3-3_3 failing -S11.3.1_A2.2_T1 failing -S11.3.1_A4_T1 failing -S11.3.1_A4_T2 failing -S11.3.1_A4_T3 failing -S11.3.1_A4_T4 failing -S11.3.2_A2.2_T1 failing -S11.3.2_A4_T1 failing -S11.3.2_A4_T2 failing -S11.3.2_A4_T3 failing -S11.3.2_A4_T4 failing S11.5.3_A4_T2 failing S11.8.6_A3 failing S11.8.6_A5_T2 failing -- cgit v1.2.3 From aee036304a3e44050a9ef493d59d97cb3ff42da4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 28 Jan 2013 13:49:03 +0100 Subject: Fix build on ia32 loadArgument(PointToValue) is architecture independent :) Change-Id: I6ce8b7900088bc7052002a9ae60cbebdd5357d8d Reviewed-by: Lars Knoll --- qv4isel_masm_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h index 347db94b35..5a4a29b154 100644 --- a/qv4isel_masm_p.h +++ b/qv4isel_masm_p.h @@ -199,7 +199,6 @@ public: addPtr(TrustedImm32(ptr.offset), ptr.base, dest); } -#ifdef VALUE_FITS_IN_REGISTER void loadArgument(PointerToValue temp, RegisterID dest) { assert(temp.value); @@ -208,6 +207,7 @@ public: loadArgument(addr, dest); } +#ifdef VALUE_FITS_IN_REGISTER void loadArgument(IR::Temp* temp, RegisterID dest) { if (!temp) { -- cgit v1.2.3 From 16640a180988f5f76f726c9badd86de272f36f37 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 25 Jan 2013 19:32:30 +0100 Subject: Change TEMP handling in the interpreter. By introducing the Param struct, and moving the ValueOrTemp into it too, the parameter decoding for instructions is cleaner and less error-prone: it de-couples the HIR/MIR representation from the interpreter representation, and allows for proper handling of the with-context. Change-Id: I0bc8f72bc7ce62b8efa298cbea5711adac4a4d34 Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 237 ++++++++++++++++++++++----------------- moth/qv4isel_moth.cpp | 271 +++++++++++++++++++++----------------------- moth/qv4isel_moth_p.h | 36 +++++- moth/qv4vme_moth.cpp | 299 +++++++++++++++++++++++-------------------------- moth/qv4vme_moth_p.h | 4 +- qmljs_engine.h | 2 +- qv4mm.cpp | 2 +- v4.pro | 2 +- 8 files changed, 444 insertions(+), 409 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index 5f7d649636..b5695b76e1 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -75,9 +75,53 @@ namespace Moth { union Instr { - union ValueOrTemp { + struct Param { + enum { + ValueType = 0, + ArgumentType = 1, + LocalType = 2, + TempType = 3 + }; VM::Value value; - int tempIndex; + unsigned type : 2; + unsigned index : 30; + + bool isValue() const { return type == ValueType; } + bool isArgument() const { return type == ArgumentType; } + bool isLocal() const { return type == LocalType; } + bool isTemp() const { return type == TempType; } + + static Param createValue(const VM::Value &v) + { + Param p; + p.type = ValueType; + p.value = v; + return p; + } + + static Param createArgument(unsigned idx) + { + Param p; + p.type = ArgumentType; + p.index = idx; + return p; + } + + static Param createLocal(unsigned idx) + { + Param p; + p.type = LocalType; + p.index = idx; + return p; + } + + static Param createTemp(unsigned idx) + { + Param p; + p.type = TempType; + p.index = idx; + return p; + } }; enum Type { @@ -86,63 +130,59 @@ union Instr struct instr_common { MOTH_INSTR_HEADER - int tempIndex; }; struct instr_ret { MOTH_INSTR_HEADER - int tempIndex; + Param result; }; struct instr_loadValue { MOTH_INSTR_HEADER - int targetTempIndex; - VM::Value value; + Param value; + Param result; }; struct instr_moveTemp { MOTH_INSTR_HEADER - int fromTempIndex; - int toTempIndex; + Param source; + Param result; }; struct instr_loadClosure { MOTH_INSTR_HEADER VM::Function *value; - int targetTempIndex; + Param result; }; struct instr_loadName { MOTH_INSTR_HEADER VM::String *name; - int targetTempIndex; + Param result; }; struct instr_storeName { MOTH_INSTR_HEADER VM::String *name; - ValueOrTemp source; - unsigned sourceIsTemp:1; + Param source; }; struct instr_loadProperty { MOTH_INSTR_HEADER - int baseTemp; - int targetTempIndex; VM::String *name; + Param base; + Param result; }; struct instr_storeProperty { MOTH_INSTR_HEADER - int baseTemp; VM::String *name; - ValueOrTemp source; - unsigned sourceIsTemp:1; + Param base; + Param source; }; struct instr_loadElement { MOTH_INSTR_HEADER - int base; - int index; - int targetTempIndex; + Param base; + Param index; + Param result; }; struct instr_storeElement { MOTH_INSTR_HEADER - int base; - int index; - ValueOrTemp source; - unsigned sourceIsTemp:1; + Param base; + Param index; + Param source; }; struct instr_push { MOTH_INSTR_HEADER @@ -152,31 +192,31 @@ union Instr MOTH_INSTR_HEADER quint32 argc; quint32 args; - int destIndex; - int targetTempIndex; + Param dest; + Param result; }; struct instr_callProperty { MOTH_INSTR_HEADER VM::String *name; - int baseTemp; quint32 argc; quint32 args; - int targetTempIndex; + Param base; + Param result; }; struct instr_callElement { MOTH_INSTR_HEADER - int index; - int baseTemp; + Param base; + Param index; quint32 argc; quint32 args; - int targetTempIndex; + Param result; }; struct instr_callActivationProperty { MOTH_INSTR_HEADER VM::String *name; quint32 argc; quint32 args; - int targetTempIndex; + Param result; }; struct instr_callBuiltin { MOTH_INSTR_HEADER @@ -188,147 +228,147 @@ union Instr builtin_push_with_scope, builtin_pop_scope } builtin; - int argTemp; - int targetTempIndex; + Param arg; + Param result; }; struct instr_callBuiltinForeachIteratorObject { MOTH_INSTR_HEADER - int argTemp; - int targetTempIndex; + Param arg; + Param result; }; struct instr_callBuiltinForeachNextPropertyName { MOTH_INSTR_HEADER - int argTemp; - int targetTempIndex; + Param arg; + Param result; }; struct instr_callBuiltinDeleteMember { MOTH_INSTR_HEADER - int base; VM::String *member; - int targetTempIndex; + Param base; + Param result; }; struct instr_callBuiltinDeleteSubscript { MOTH_INSTR_HEADER - int base; - int index; - int targetTempIndex; + Param base; + Param index; + Param result; }; struct instr_callBuiltinDeleteName { MOTH_INSTR_HEADER VM::String *name; - int targetTempIndex; + Param result; }; struct instr_callBuiltinTypeofMember { MOTH_INSTR_HEADER - int base; VM::String *member; - int targetTempIndex; + Param base; + Param result; }; struct instr_callBuiltinTypeofSubscript { MOTH_INSTR_HEADER - int base; - int index; - int targetTempIndex; + Param base; + Param index; + Param result; }; struct instr_callBuiltinTypeofName { MOTH_INSTR_HEADER VM::String *name; - int targetTempIndex; + Param result; }; struct instr_callBuiltinTypeofValue { MOTH_INSTR_HEADER - int tempIndex; - int targetTempIndex; + Param value; + Param result; }; struct instr_callBuiltinPostIncMember { MOTH_INSTR_HEADER - int base; + Param base; VM::String *member; - int targetTempIndex; + Param result; }; struct instr_callBuiltinPostIncSubscript { MOTH_INSTR_HEADER - int base; - int index; - int targetTempIndex; + Param base; + Param index; + Param result; }; struct instr_callBuiltinPostIncName { MOTH_INSTR_HEADER VM::String *name; - int targetTempIndex; + Param result; }; struct instr_callBuiltinPostIncValue { MOTH_INSTR_HEADER - int tempIndex; - int targetTempIndex; + Param value; + Param result; }; struct instr_callBuiltinPostDecMember { MOTH_INSTR_HEADER - int base; + Param base; VM::String *member; - int targetTempIndex; + Param result; }; struct instr_callBuiltinPostDecSubscript { MOTH_INSTR_HEADER - int base; - int index; - int targetTempIndex; + Param base; + Param index; + Param result; }; struct instr_callBuiltinPostDecName { MOTH_INSTR_HEADER VM::String *name; - int targetTempIndex; + Param result; }; struct instr_callBuiltinPostDecValue { MOTH_INSTR_HEADER - int tempIndex; - int targetTempIndex; + Param value; + Param result; }; struct instr_callBuiltinDeclareVar { MOTH_INSTR_HEADER - bool isDeletable; VM::String *varName; + bool isDeletable; }; struct instr_callBuiltinDefineGetterSetter { MOTH_INSTR_HEADER - int objectTemp; VM::String *name; - int getterTemp; - int setterTemp; + Param object; + Param getter; + Param setter; }; struct instr_callBuiltinDefineProperty { MOTH_INSTR_HEADER - int objectTemp; VM::String *name; - int valueTemp; + Param object; + Param value; }; struct instr_callBuiltinDefineArrayProperty { MOTH_INSTR_HEADER - int objectTemp; + Param object; + Param value; int index; - int valueTemp; }; struct instr_createValue { MOTH_INSTR_HEADER - int func; quint32 argc; quint32 args; - int targetTempIndex; + Param func; + Param result; }; struct instr_createProperty { MOTH_INSTR_HEADER - int base; VM::String *name; quint32 argc; quint32 args; - int targetTempIndex; + Param base; + Param result; }; struct instr_createActivationProperty { MOTH_INSTR_HEADER VM::String *name; quint32 argc; quint32 args; - int targetTempIndex; + Param result; }; struct instr_jump { MOTH_INSTR_HEADER @@ -337,49 +377,44 @@ union Instr struct instr_cjump { MOTH_INSTR_HEADER ptrdiff_t offset; - int tempIndex; + Param condition; }; struct instr_unop { MOTH_INSTR_HEADER VM::Value (*alu)(const VM::Value value, VM::ExecutionContext *ctx); - int e; - int targetTempIndex; + Param source; + Param result; }; struct instr_binop { MOTH_INSTR_HEADER VM::Value (*alu)(const VM::Value , const VM::Value, VM::ExecutionContext *); - int targetTempIndex; - ValueOrTemp lhs; - ValueOrTemp rhs; - unsigned lhsIsTemp:1; - unsigned rhsIsTemp:1; + Param lhs; + Param rhs; + Param result; }; struct instr_loadThis { MOTH_INSTR_HEADER - int targetTempIndex; + Param result; }; struct instr_inplaceElementOp { MOTH_INSTR_HEADER void (*alu)(VM::Value, VM::Value, VM::Value, VM::ExecutionContext *); - int targetBase; - int targetIndex; - ValueOrTemp source; - unsigned sourceIsTemp:1; + Param base; + Param index; + Param source; }; struct instr_inplaceMemberOp { MOTH_INSTR_HEADER void (*alu)(VM::Value, VM::Value, VM::String *, VM::ExecutionContext *); - int targetBase; - VM::String *targetMember; - ValueOrTemp source; - unsigned sourceIsTemp:1; + VM::String *member; + Param base; + Param source; }; struct instr_inplaceNameOp { MOTH_INSTR_HEADER void (*alu)(VM::Value, VM::String *, VM::ExecutionContext *); - VM::String *targetName; - ValueOrTemp source; - unsigned sourceIsTemp:1; + VM::String *name; + Param source; }; instr_common common; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 46cff05b69..1e58314a49 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -12,28 +12,18 @@ namespace { QTextStream qout(stderr, QIODevice::WriteOnly); -static unsigned toValueOrTemp(IR::Expr *e, Instr::ValueOrTemp &vot) -{ - if (IR::Const *c = e->asConst()) { - vot.value = convertToValue(c); - return 0; - } else if (IR::Temp *t = e->asTemp()) { - vot.tempIndex = t->index; - return 1; - } else { - Q_UNREACHABLE(); - } -} - -#undef DEBUG_TEMP_COMPRESSION +//#define DEBUG_TEMP_COMPRESSION +#ifdef DEBUG_TEMP_COMPRESSION +# define DBTC(x) x +#else // !DEBUG_TEMP_COMPRESSION +# define DBTC(x) +#endif // DEBUG_TEMP_COMPRESSION class CompressTemps: public IR::StmtVisitor, IR::ExprVisitor { public: void run(IR::Function *function) { -#ifdef DEBUG_TEMP_COMPRESSION - qDebug() << "starting on function" << (*function->name) << "with" << function->tempCount << "temps."; -#endif // DEBUG_TEMP_COMPRESSION + DBTC(qDebug() << "starting on function" << (*function->name) << "with" << function->tempCount << "temps.";) _seenTemps.clear(); _nextFree = 0; @@ -57,27 +47,21 @@ public: int maxUsed = _nextFree; foreach (IR::BasicBlock *block, function->basicBlocks) { -#ifdef DEBUG_TEMP_COMPRESSION - qDebug("L%d:", block->index); -#endif // DEBUG_TEMP_COMPRESSION + DBTC(qDebug("L%d:", block->index)); for (int i = 0, ei = block->statements.size(); i < ei; ++i ) { _currentStatement = block->statements[i]; if (i == 0) expireOld(); -#ifdef DEBUG_TEMP_COMPRESSION - _currentStatement->dump(qout);qout<dump(qout);qout<d) _currentStatement->accept(this); } maxUsed = std::max(maxUsed, _nextFree); } -#ifdef DEBUG_TEMP_COMPRESSION - qDebug() << "function" << (*function->name) << "uses" << maxUsed << "temps."; -#endif // DEBUG_TEMP_COMPRESSION + DBTC(qDebug() << "function" << (*function->name) << "uses" << maxUsed << "temps.";) function->tempCount = maxUsed + _localCount; } @@ -131,9 +115,7 @@ private: int remap(int tempIndex) { for (ActiveTemps::const_iterator i = _active.begin(), ei = _active.end(); i < ei; ++i) { if (i->first == tempIndex) { -#ifdef DEBUG_TEMP_COMPRESSION - qDebug() << " lookup" << (tempIndex + _localCount) << "->" << (i->second + _localCount); -#endif // DEBUG_TEMP_COMPRESSION + DBTC(qDebug() << " lookup" << (tempIndex + _localCount) << "->" << (i->second + _localCount);) return i->second; } } @@ -147,9 +129,7 @@ private: if (_nextFree <= firstFree) _nextFree = firstFree + 1; _active.prepend(qMakePair(tempIndex, firstFree)); -#ifdef DEBUG_TEMP_COMPRESSION - qDebug() << " add" << (tempIndex + _localCount) << "->" << (firstFree+ _localCount); -#endif // DEBUG_TEMP_COMPRESSION + DBTC(qDebug() << " add" << (tempIndex + _localCount) << "->" << (firstFree+ _localCount);) } int expireOld() { @@ -171,9 +151,7 @@ private: inUse[p.second] = true; ++i; } else { -#ifdef DEBUG_TEMP_COMPRESSION - qDebug() << " remove" << (p.first + _localCount) << "->" << (p.second + _localCount); -#endif // DEBUG_TEMP_COMPRESSION + DBTC(qDebug() << " remove" << (p.first + _localCount) << "->" << (p.second + _localCount);) _active.remove(i); } } @@ -192,6 +170,7 @@ private: int _nextFree; int _pinnedCount; }; +#undef DBTC typedef VM::Value (*ALUFunction)(const VM::Value, const VM::Value, VM::ExecutionContext*); inline ALUFunction aluOpFunction(IR::AluOp op) @@ -297,7 +276,8 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) qSwap(codeNext, _codeNext); qSwap(codeEnd, _codeEnd); - CompressTemps().run(_function); + // TODO: FIXME: fix the temp compression with the new temp index layout. +// CompressTemps().run(_function); int locals = frameSize(); assert(locals >= 0); @@ -333,8 +313,8 @@ void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Te { Instruction::CallValue call; prepareCallArgs(args, call.argc, call.args); - call.destIndex = value->index; - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.dest = getParam(value); + call.result = getResultParam(result); addInstruction(call); } @@ -342,10 +322,10 @@ void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR: { // call the property on the loaded base Instruction::CallProperty call; - call.baseTemp = base->index; + call.base = getParam(base); call.name = engine()->newString(name); prepareCallArgs(args, call.argc, call.args); - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.result = getResultParam(result); addInstruction(call); } @@ -353,10 +333,10 @@ void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::Ex { // call the property on the loaded base Instruction::CallElement call; - call.baseTemp = base->index; - call.index = index->index; + call.base = getParam(base); + call.index = getParam(index); prepareCallArgs(args, call.argc, call.args); - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.result = getResultParam(result); addInstruction(call); } @@ -367,33 +347,33 @@ void InstructionSelection::constructActivationProperty(IR::Name *func, Instruction::CreateActivationProperty create; create.name = engine()->newString(*func->id); prepareCallArgs(args, create.argc, create.args); - create.targetTempIndex = result->index; + create.result = getResultParam(result); addInstruction(create); } void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) { Instruction::CreateProperty create; - create.base = base->index; + create.base = getParam(base); create.name = engine()->newString(name); prepareCallArgs(args, create.argc, create.args); - create.targetTempIndex = result->index; + create.result = getResultParam(result); addInstruction(create); } void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) { Instruction::CreateValue create; - create.func = value->index; + create.func = getParam(value); prepareCallArgs(args, create.argc, create.args); - create.targetTempIndex = result->index; + create.result = getResultParam(result); addInstruction(create); } void InstructionSelection::loadThisObject(IR::Temp *temp) { Instruction::LoadThis load; - load.targetTempIndex = temp->index; + load.result = getResultParam(temp); addInstruction(load); } @@ -402,26 +382,27 @@ void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTem assert(sourceConst); Instruction::LoadValue load; - load.targetTempIndex = targetTemp->index; - load.value = convertToValue(sourceConst); + load.value = getParam(sourceConst); + load.result = getResultParam(targetTemp); addInstruction(load); } void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) { Instruction::LoadValue load; - load.value = VM::Value::fromString(engine()->newString(str)); - load.targetTempIndex = targetTemp->index; + load.value = Instr::Param::createValue(VM::Value::fromString(engine()->newString(str))); + load.result = getResultParam(targetTemp); addInstruction(load); } void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) { Instruction::LoadValue load; - load.value = VM::Value::fromObject(engine()->newRegExpObject( - *sourceRegexp->value, - sourceRegexp->flags)); - load.targetTempIndex = targetTemp->index; + load.value = Instr::Param::createValue( + VM::Value::fromObject(engine()->newRegExpObject( + *sourceRegexp->value, + sourceRegexp->flags))); + load.result = getResultParam(targetTemp); addInstruction(load); } @@ -429,14 +410,14 @@ void InstructionSelection::getActivationProperty(const QString &name, IR::Temp * { Instruction::LoadName load; load.name = engine()->newString(name); - load.targetTempIndex = temp->index; + load.result = getResultParam(temp); addInstruction(load); } void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) { Instruction::StoreName store; - store.sourceIsTemp = toValueOrTemp(source, store.source); + store.source = getParam(source); store.name = engine()->newString(targetName); addInstruction(store); } @@ -447,51 +428,51 @@ void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) assert(vmFunc); Instruction::LoadClosure load; load.value = vmFunc; - load.targetTempIndex = target->index; + load.result = getResultParam(target); addInstruction(load); } void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::Temp *target) { Instruction::LoadProperty load; - load.baseTemp = base->index; + load.base = getParam(base); load.name = engine()->newString(name); - load.targetTempIndex = target->index; + load.result = getResultParam(target); addInstruction(load); } void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) { Instruction::StoreProperty store; - store.baseTemp = targetBase->index; + store.base = getParam(targetBase); store.name = engine()->newString(targetName); - store.sourceIsTemp = toValueOrTemp(source, store.source); + store.source = getParam(source); addInstruction(store); } void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) { Instruction::LoadElement load; - load.base = base->index; - load.index = index->index; - load.targetTempIndex = target->index; + load.base = getParam(base); + load.index = getParam(index); + load.result = getResultParam(target); addInstruction(load); } void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) { Instruction::StoreElement store; - store.base = targetBase->index; - store.index = targetIndex->index; - store.sourceIsTemp = toValueOrTemp(source, store.source); + store.base = getParam(targetBase); + store.index = getParam(targetIndex); + store.source = getParam(source); addInstruction(store); } void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) { Instruction::MoveTemp move; - move.fromTempIndex = sourceTemp->index; - move.toTempIndex = targetTemp->index; + move.source = getParam(sourceTemp); + move.result = getResultParam(targetTemp); addInstruction(move); } @@ -512,8 +493,8 @@ void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp * if (op) { Instruction::Unop unop; unop.alu = op; - unop.e = sourceTemp->index; - unop.targetTempIndex = targetTemp->index; + unop.source = getParam(sourceTemp); + unop.result = getResultParam(targetTemp); addInstruction(unop); } else { qWarning(" UNOP1"); @@ -524,9 +505,9 @@ void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr { Instruction::Binop binop; binop.alu = aluOpFunction(oper); - binop.lhsIsTemp = toValueOrTemp(leftSource, binop.lhs); - binop.rhsIsTemp = toValueOrTemp(rightSource, binop.rhs); - binop.targetTempIndex = target->index; + binop.lhs = getParam(leftSource); + binop.rhs = getParam(rightSource); + binop.result = getResultParam(target); addInstruction(binop); } @@ -551,8 +532,8 @@ void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, c if (op) { Instruction::InplaceNameOp ieo; ieo.alu = op; - ieo.targetName = engine()->newString(targetName); - ieo.sourceIsTemp = toValueOrTemp(sourceExpr, ieo.source); + ieo.name = engine()->newString(targetName); + ieo.source = getParam(sourceExpr); addInstruction(ieo); } } @@ -577,9 +558,9 @@ void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr Instruction::InplaceElementOp ieo; ieo.alu = op; - ieo.targetBase = targetBaseTemp->index; - ieo.targetIndex = targetIndexTemp->index; - ieo.sourceIsTemp = toValueOrTemp(sourceExpr, ieo.source); + ieo.base = getParam(targetBaseTemp); + ieo.index = getParam(targetIndexTemp); + ieo.source = getParam(sourceExpr); addInstruction(ieo); } @@ -603,9 +584,9 @@ void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR: Instruction::InplaceMemberOp imo; imo.alu = op; - imo.targetBase = targetBase->index; - imo.targetMember = engine()->newString(targetName); - imo.sourceIsTemp = toValueOrTemp(source, imo.source); + imo.base = getParam(targetBase); + imo.member = engine()->newString(targetName); + imo.source = getParam(source); addInstruction(imo); } @@ -623,7 +604,7 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint if (singleArgIsTemp) { // We pass single arguments as references to the stack, but only if it's not a local or an argument. argc = 1; - args = e->expr->asTemp()->index; + args = e->expr->asTemp()->index - _function->locals.size(); } else if (e) { // We need to move all the temps into the function arg array int argLocation = outgoingArgumentTempStart(); @@ -632,8 +613,8 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint args = argLocation; while (e) { Instruction::MoveTemp move; - move.fromTempIndex = e->expr->asTemp()->index; - move.toTempIndex = argLocation; + move.source = getParam(e->expr); + move.result = Instr::Param::createTemp(argLocation); addInstruction(move); ++argLocation; ++argc; @@ -656,24 +637,24 @@ void InstructionSelection::visitJump(IR::Jump *s) void InstructionSelection::visitCJump(IR::CJump *s) { - int tempIndex; + Instr::Param condition; if (IR::Temp *t = s->cond->asTemp()) { - tempIndex = t->index; + condition = getResultParam(t); } else if (IR::Binop *b = s->cond->asBinop()) { - tempIndex = scratchTempIndex(); + condition = getResultParam(0); Instruction::Binop binop; binop.alu = aluOpFunction(b->op); - binop.lhsIsTemp = toValueOrTemp(b->left, binop.lhs); - binop.rhsIsTemp = toValueOrTemp(b->right, binop.rhs); - binop.targetTempIndex = tempIndex; + binop.lhs = getParam(b->left); + binop.rhs = getParam(b->right); + binop.result = condition; addInstruction(binop); } else { - Q_UNREACHABLE(); + Q_UNIMPLEMENTED(); } Instruction::CJump jump; jump.offset = 0; - jump.tempIndex = tempIndex; + jump.condition = condition; ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); _patches[s->iftrue].append(trueLoc); @@ -688,7 +669,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { Instruction::Ret ret; - ret.tempIndex = s->expr->index; + ret.result = getParam(s->expr); addInstruction(ret); } @@ -697,25 +678,25 @@ void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args Instruction::CallActivationProperty call; call.name = engine()->newString(*func->id); prepareCallArgs(args, call.argc, call.args); - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.result = getResultParam(result); addInstruction(call); } void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) { Instruction::CallBuiltinTypeofMember call; - call.base = base->index; + call.base = getParam(base); call.member = engine()->identifier(name); - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.result = getResultParam(result); addInstruction(call); } void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) { Instruction::CallBuiltinTypeofSubscript call; - call.base = base->index; - call.index = index->index; - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); addInstruction(call); } @@ -723,33 +704,33 @@ void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp * { Instruction::CallBuiltinTypeofName call; call.name = engine()->identifier(name); - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.result = getResultParam(result); addInstruction(call); } void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) { Instruction::CallBuiltinTypeofValue call; - call.tempIndex = value->index; - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.value = getParam(value); + call.result = getResultParam(result); addInstruction(call); } void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) { Instruction::CallBuiltinDeleteMember call; - call.base = base->index; + call.base = getParam(base); call.member = engine()->newString(name); - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.result = getResultParam(result); addInstruction(call); } void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) { Instruction::CallBuiltinDeleteSubscript call; - call.base = base->index; - call.index = index->index; - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); addInstruction(call); } @@ -757,33 +738,33 @@ void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp * { Instruction::CallBuiltinDeleteName call; call.name = engine()->newString(name); - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.result = getResultParam(result); addInstruction(call); } void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) { Instruction::LoadValue load; - load.value = VM::Value::fromBoolean(false); - load.targetTempIndex = result ? result->index : scratchTempIndex(); + load.value = Instr::Param::createValue(VM::Value::fromBoolean(false)); + load.result = getResultParam(result); addInstruction(load); } void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) { Instruction::CallBuiltinPostDecMember call; - call.base = base->index; + call.base = getParam(base); call.member = engine()->identifier(name); - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.result = getResultParam(result); addInstruction(call); } void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) { Instruction::CallBuiltinPostDecSubscript call; - call.base = base->index; - call.index = index->index; - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); addInstruction(call); } @@ -791,33 +772,33 @@ void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR: { Instruction::CallBuiltinPostDecName call; call.name = engine()->identifier(name); - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.result = getResultParam(result); addInstruction(call); } void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) { Instruction::CallBuiltinPostDecValue call; - call.tempIndex = value->index; - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.value = getParam(value); + call.result = getResultParam(result); addInstruction(call); } void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) { Instruction::CallBuiltinPostIncMember call; - call.base = base->index; + call.base = getParam(base); call.member = engine()->identifier(name); - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.result = getResultParam(result); addInstruction(call); } void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) { Instruction::CallBuiltinPostIncSubscript call; - call.base = base->index; - call.index = index->index; - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); addInstruction(call); } @@ -825,15 +806,15 @@ void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR: { Instruction::CallBuiltinPostIncName call; call.name = engine()->identifier(name); - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.result = getResultParam(result); addInstruction(call); } void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) { Instruction::CallBuiltinPostIncValue call; - call.tempIndex = value->index; - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.value = getParam(value); + call.result = getResultParam(result); addInstruction(call); } @@ -841,7 +822,7 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_throw; - call.argTemp = arg->index; + call.arg = getParam(arg); addInstruction(call); } @@ -849,7 +830,7 @@ void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_create_exception_handler; - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.result = getResultParam(result); addInstruction(call); } @@ -864,23 +845,23 @@ void InstructionSelection::callBuiltinGetException(IR::Temp *result) { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_get_exception; - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.result = getResultParam(result); addInstruction(call); } void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) { Instruction::CallBuiltinForeachIteratorObject call; - call.argTemp = arg->index; - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.arg = getParam(arg); + call.result = getResultParam(result); addInstruction(call); } void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) { Instruction::CallBuiltinForeachNextPropertyName call; - call.argTemp = arg->index; - call.targetTempIndex = result ? result->index : scratchTempIndex(); + call.arg = getParam(arg); + call.result = getResultParam(result); addInstruction(call); } @@ -888,7 +869,7 @@ void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_push_with_scope; - call.argTemp = arg->index; + call.arg = getParam(arg); addInstruction(call); } @@ -910,28 +891,28 @@ void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString & void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) { Instruction::CallBuiltinDefineGetterSetter call; - call.objectTemp = object->index; + call.object = getParam(object); call.name = engine()->newString(name); - call.getterTemp = getter->index; - call.setterTemp = setter->index; + call.getter = getParam(getter); + call.setter = getParam(setter); addInstruction(call); } void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) { Instruction::CallBuiltinDefineProperty call; - call.objectTemp = object->index; + call.object = getParam(object); call.name = engine()->newString(name); - call.valueTemp = value->index; + call.value = getParam(value); addInstruction(call); } void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) { Instruction::CallBuiltinDefineArrayProperty call; - call.objectTemp = object->index; + call.object = getParam(object); call.index = index; - call.valueTemp = value->index; + call.value = getParam(value); addInstruction(call); } diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index c9b32d688f..ad07dcb0bc 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -86,12 +86,44 @@ private: Instruction(); }; + Instr::Param getParam(IR::Expr *e) + { + Q_ASSERT(e); + + typedef Instr::Param Param; + if (IR::Const *c = e->asConst()) { + return Param::createValue(convertToValue(c)); + } else if (IR::Temp *t = e->asTemp()) { + const int index = t->index; + if (index < 0) { + return Param::createArgument(-index - 1); + } else { + const int localCount = _function->locals.size(); + if (index < localCount) + return Param::createLocal(index); + else + return Param::createTemp(index - localCount); + } + } else { + Q_UNIMPLEMENTED(); + return Param(); + } + } + + Instr::Param getResultParam(IR::Temp *result) + { + if (result) + return getParam(result); + else + return Instr::Param::createTemp(scratchTempIndex()); + } + void simpleMove(IR::Move *); void prepareCallArgs(IR::ExprList *, quint32 &, quint32 &); - int outgoingArgumentTempStart() const { return _function->tempCount; } + int outgoingArgumentTempStart() const { return _function->tempCount - _function->locals.size(); } int scratchTempIndex() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; } - int frameSize() const { return scratchTempIndex() + 1 - _function->locals.size(); } + int frameSize() const { return scratchTempIndex() + 1; } template inline ptrdiff_t addInstruction(const InstrData &data); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 5786a546b7..1578b2c242 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -18,6 +18,27 @@ using namespace QQmlJS; using namespace QQmlJS::Moth; +class FunctionState: public Debugging::FunctionState +{ +public: + FunctionState(QQmlJS::VM::ExecutionContext *context, const uchar **code) + : Debugging::FunctionState(context) + , stack(0) + , stackSize(0) + , code(code) + {} + + virtual VM::Value *temp(unsigned idx) { return stack + idx; } + + void setStack(VM::Value *stack, unsigned stackSize) + { this->stack = stack; this->stackSize = stackSize; } + +private: + VM::Value *stack; + unsigned stackSize; + const uchar **code; +}; + #define MOTH_BEGIN_INSTR_COMMON(I) { \ const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ code += InstrMeta<(int)Instr::I>::Size; \ @@ -53,70 +74,58 @@ using namespace QQmlJS::Moth; #endif -static inline VM::Value *tempValue(QQmlJS::VM::ExecutionContext *context, VM::Value* stack, int index) +static inline VM::Value *getValueRef(QQmlJS::VM::ExecutionContext *context, + VM::Value* stack, + const Instr::Param ¶m +#if !defined(QT_NO_DEBUG) + , unsigned stackSize +#endif + ) { #ifdef DO_TRACE_INSTR - const char *kind; - int pos; - if (index < 0) { - kind = "arg"; - pos = -index - 1; - } else if (index < (int) context->variableCount()) { - kind = "local"; - pos = index; + if (param.isValue()) { + fprintf(stderr, " value\n"); + } else if (param.isArgument()) { + fprintf(stderr, " argument %d\n", param.index); + } else if (param.isLocal()) { + fprintf(stderr, " local %d\n", param.index); + } else if (param.isTemp()) { + fprintf(stderr, " temp %d\n", param.index); } else { - kind = "temp"; - pos = index - context->variableCount(); + Q_ASSERT(!"INVALID"); } - fprintf(stderr, " tempValue: index = %d : %s = %d\n", - index, kind, pos); #endif // DO_TRACE_INSTR - if (index < 0) { - const int arg = -index - 1; - + if (param.isValue()) { + return const_cast(¶m.value); + } else if (param.isArgument()) { + const unsigned arg = param.index; Q_ASSERT(arg >= 0); Q_ASSERT((unsigned) arg < context->argumentCount); Q_ASSERT(context->arguments); - return context->arguments + arg; - } else if (index < (int) context->variableCount()) { + } else if (param.isLocal()) { + const unsigned index = param.index; Q_ASSERT(index >= 0); + Q_ASSERT(index < context->variableCount()); Q_ASSERT(context->locals); - return context->locals + index; + } else if (param.isTemp()) { + Q_ASSERT(param.index < stackSize); + return stack + param.index; } else { - int off = index - context->variableCount(); - - Q_ASSERT(off >= 0); - - return stack + off; + Q_UNIMPLEMENTED(); + return 0; } } -class FunctionState: public Debugging::FunctionState -{ -public: - FunctionState(QQmlJS::VM::ExecutionContext *context, const uchar **code) - : Debugging::FunctionState(context) - , stack(0) - , stackSize(0) - , code(code) - {} - - virtual VM::Value *temp(unsigned idx) { return stack + idx; } - - void setStack(VM::Value *stack, unsigned stackSize) - { this->stack = stack; this->stackSize = stackSize; } - -private: - VM::Value *stack; - unsigned stackSize; - const uchar **code; -}; - -#define TEMP(index) *tempValue(context, stack, index) -#define TEMPPTR(index) tempValue(context, stack, index) +#if defined(QT_NO_DEBUG) +# define VALUE(param) *getValueRef(context, stack, param) +# define VALUEPTR(param) getValueRef(context, stack, param) +#else +# define VALUE(param) *getValueRef(context, stack, param, stackSize) +# define VALUEPTR(param) getValueRef(context, stack, param, stackSize) +#endif VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code #ifdef MOTH_THREADED_INTERPRETER @@ -154,51 +163,42 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co #endif MOTH_BEGIN_INSTR(MoveTemp) - VM::Value tmp = TEMP(instr.fromTempIndex); - TEMP(instr.toTempIndex) = tmp; + VALUE(instr.result) = VALUE(instr.source); MOTH_END_INSTR(MoveTemp) MOTH_BEGIN_INSTR(LoadValue) - TEMP(instr.targetTempIndex) = instr.value; +// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); + VALUE(instr.result) = VALUE(instr.value); MOTH_END_INSTR(LoadValue) MOTH_BEGIN_INSTR(LoadClosure) - VM::Value c = __qmljs_init_closure(instr.value, context); - TEMP(instr.targetTempIndex) = c; + VALUE(instr.result) = __qmljs_init_closure(instr.value, context); MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); - VM::Value val = __qmljs_get_activation_property(context, instr.name); - TEMP(instr.targetTempIndex) = val; + VALUE(instr.result) = __qmljs_get_activation_property(context, instr.name); MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(StoreName) TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); - VM::Value source = instr.sourceIsTemp ? TEMP(instr.source.tempIndex) : instr.source.value; - __qmljs_set_activation_property(context, instr.name, source); + __qmljs_set_activation_property(context, instr.name, VALUE(instr.source)); MOTH_END_INSTR(StoreName) MOTH_BEGIN_INSTR(LoadElement) - TEMP(instr.targetTempIndex) = __qmljs_get_element(context, TEMP(instr.base), TEMP(instr.index)); + VALUE(instr.result) = __qmljs_get_element(context, VALUE(instr.base), VALUE(instr.index)); MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) - VM::Value source = instr.sourceIsTemp ? TEMP(instr.source.tempIndex) : instr.source.value; - __qmljs_set_element(context, TEMP(instr.base), TEMP(instr.index), source); + __qmljs_set_element(context, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) - TRACE(inline, "base temp = %d, property name = %s", instr.baseTemp, instr.name->toQString().toUtf8().constData()); - VM::Value base = TEMP(instr.baseTemp); - TEMP(instr.targetTempIndex) = __qmljs_get_property(context, base, instr.name); + VALUE(instr.result) = __qmljs_get_property(context, VALUE(instr.base), instr.name); MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(StoreProperty) - TRACE(inline, "base temp = %d, property name = %s", instr.baseTemp, instr.name->toQString().toUtf8().constData()); - VM::Value base = TEMP(instr.baseTemp); - VM::Value source = instr.sourceIsTemp ? TEMP(instr.source.tempIndex) : instr.source.value; - __qmljs_set_property(context, base, instr.name, source); + __qmljs_set_property(context, VALUE(instr.base), instr.name, VALUE(instr.source)); MOTH_END_INSTR(StoreProperty) MOTH_BEGIN_INSTR(Push) @@ -211,7 +211,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(CallValue) #ifdef DO_TRACE_INSTR if (Debugging::Debugger *debugger = context->engine->debugger) { - if (VM::FunctionObject *o = (TEMP(instr.destIndex)).asFunctionObject()) { + if (VM::FunctionObject *o = (VALUE(instr.dest)).asFunctionObject()) { if (Debugging::FunctionDebugInfo *info = debugger->debugInfo(o)) { QString n = debugger->name(o); std::cerr << "*** Call to \"" << (n.isNull() ? "" : qPrintable(n)) << "\" defined @" << info->startLine << ":" << info->startColumn << std::endl; @@ -219,37 +219,28 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co } } #endif // DO_TRACE_INSTR - int argStart = instr.args - context->variableCount(); - TRACE(Call, "value index = %d, argStart = %d, argc = %d, result temp index = %d", instr.destIndex, argStart, instr.argc, instr.targetTempIndex); - VM::Value *args = stack + argStart; - VM::Value result = __qmljs_call_value(context, VM::Value::undefinedValue(), TEMP(instr.destIndex), args, instr.argc); - TEMP(instr.targetTempIndex) = result; + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_call_value(context, VM::Value::undefinedValue(), VALUE(instr.dest), args, instr.argc); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) - int argStart = instr.args - context->variableCount(); - // TODO: change this assert everywhere to include a minimum - // TODO: the args calculation is duplicate code, fix that - VM::Value *args = stack + argStart; - VM::Value base = TEMP(instr.baseTemp); - TEMP(instr.targetTempIndex) = __qmljs_call_property(context, base, instr.name, args, instr.argc); + TRACE(property name, "%s", qPrintable(instr.name->toQString())); + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_call_property(context, VALUE(instr.base), instr.name, args, instr.argc); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallElement) - int argStart = instr.args - context->variableCount(); - // TODO: change this assert everywhere to include a minimum - // TODO: the args calculation is duplicate code, fix that - VM::Value *args = stack + argStart; - VM::Value base = TEMP(instr.baseTemp); - TEMP(instr.targetTempIndex) = __qmljs_call_element(context, base, TEMP(instr.index), args, instr.argc); + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_call_element(context, VALUE(instr.base), VALUE(instr.index), args, instr.argc); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallActivationProperty) - int argStart = instr.args - context->variableCount(); - // TODO: change this assert everywhere to include a minimum - // TODO: the args calculation is duplicate code, fix that - VM::Value *args = stack + argStart; - TEMP(instr.targetTempIndex) = __qmljs_call_activation_property(context, instr.name, args, instr.argc); + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_call_activation_property(context, instr.name, args, instr.argc); MOTH_END_INSTR(CallActivationProperty) MOTH_BEGIN_INSTR(CallBuiltin) @@ -257,116 +248,115 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co switch (instr.builtin) { case Instr::instr_callBuiltin::builtin_throw: TRACE(builtin_throw, "Throwing now...%s", ""); - __qmljs_builtin_throw(TEMP(instr.argTemp), context); + __qmljs_builtin_throw(VALUE(instr.arg), context); break; case Instr::instr_callBuiltin::builtin_create_exception_handler: { TRACE(builtin_create_exception_handler, "%s", ""); void *buf = __qmljs_create_exception_handler(context); - // The targetTempIndex is the only value we need from the instr to + // The resultIndex is the only value we need from the instr to // continue execution when an exception is caught. - int targetTempIndex = instr.targetTempIndex; + VM::Value *result = getValueRef(context, stack, instr.result, stackSize); int didThrow = setjmp(* static_cast(buf)); // Two ways to come here: after a create, or after a throw. if (didThrow) // At this point, the interpreter state can be anything but // valid, so first restore the state. This includes all relevant // locals. - restoreState(context, targetTempIndex, code); + restoreState(context, result, code); else // Save the state and any variables we need when catching an // exception, so we can restore the state at that point. - saveState(context, targetTempIndex, code); - TEMP(targetTempIndex) = VM::Value::fromInt32(didThrow); + saveState(context, result, code); + *result = VM::Value::fromInt32(didThrow); } break; case Instr::instr_callBuiltin::builtin_delete_exception_handler: TRACE(builtin_delete_exception_handler, "%s", ""); __qmljs_delete_exception_handler(context); break; case Instr::instr_callBuiltin::builtin_get_exception: - TEMP(instr.targetTempIndex) = __qmljs_get_exception(context); + TRACE(builtin_get_exception, "%s", ""); + VALUE(instr.result) = __qmljs_get_exception(context); break; case Instr::instr_callBuiltin::builtin_push_with_scope: - context = __qmljs_builtin_push_with_scope(TEMP(instr.argTemp), context); + TRACE(builtin_push_with_scope, "%s", ""); + context = __qmljs_builtin_push_with_scope(VALUE(instr.arg), context); break; case Instr::instr_callBuiltin::builtin_pop_scope: + TRACE(builtin_pop_scope, "%s", ""); context = __qmljs_builtin_pop_scope(context); break; default: - assert(!"TODO!"); - Q_UNREACHABLE(); + Q_UNIMPLEMENTED(); + return VM::Value(); } MOTH_END_INSTR(CallBuiltin) MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) - VM::Value &obj = TEMP(instr.argTemp); - VM::Value it = __qmljs_foreach_iterator_object(obj, context); - TEMP(instr.targetTempIndex) = it; + VALUE(instr.result) = __qmljs_foreach_iterator_object(VALUE(instr.arg), context); MOTH_END_INSTR(CallBuiltinForeachIteratorObject) MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) - VM::Value &iter = TEMP(instr.argTemp); - VM::Value val = __qmljs_foreach_next_property_name(iter); - TEMP(instr.targetTempIndex) = val; + VALUE(instr.result) = __qmljs_foreach_next_property_name(VALUE(instr.arg)); MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) - TEMP(instr.targetTempIndex) = __qmljs_delete_member(context, TEMP(instr.base), instr.member); + VALUE(instr.result) = __qmljs_delete_member(context, VALUE(instr.base), instr.member); MOTH_END_INSTR(CallBuiltinDeleteMember) MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) - TEMP(instr.targetTempIndex) = __qmljs_delete_subscript(context, TEMP(instr.base), TEMP(instr.index)); + VALUE(instr.result) = __qmljs_delete_subscript(context, VALUE(instr.base), VALUE(instr.index)); MOTH_END_INSTR(CallBuiltinDeleteSubscript) MOTH_BEGIN_INSTR(CallBuiltinDeleteName) - TEMP(instr.targetTempIndex) = __qmljs_delete_name(context, instr.name); + VALUE(instr.result) = __qmljs_delete_name(context, instr.name); MOTH_END_INSTR(CallBuiltinDeleteName) MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) - TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof_member(TEMP(instr.base), instr.member, context); + VALUE(instr.result) = __qmljs_builtin_typeof_member(VALUE(instr.base), instr.member, context); MOTH_END_INSTR(CallBuiltinTypeofMember) MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) - TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof_element(TEMP(instr.base), TEMP(instr.index), context); + VALUE(instr.result) = __qmljs_builtin_typeof_element(VALUE(instr.base), VALUE(instr.index), context); MOTH_END_INSTR(CallBuiltinTypeofSubscript) MOTH_BEGIN_INSTR(CallBuiltinTypeofName) - TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof_name(instr.name, context); + VALUE(instr.result) = __qmljs_builtin_typeof_name(instr.name, context); MOTH_END_INSTR(CallBuiltinTypeofName) MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) - TEMP(instr.targetTempIndex) = __qmljs_builtin_typeof(TEMP(instr.tempIndex), context); + VALUE(instr.result) = __qmljs_builtin_typeof(VALUE(instr.value), context); MOTH_END_INSTR(CallBuiltinTypeofValue) MOTH_BEGIN_INSTR(CallBuiltinPostIncMember) - TEMP(instr.targetTempIndex) = __qmljs_builtin_post_increment_member(TEMP(instr.base), instr.member, context); + VALUE(instr.result) = __qmljs_builtin_post_increment_member(VALUE(instr.base), instr.member, context); MOTH_END_INSTR(CallBuiltinTypeofMember) MOTH_BEGIN_INSTR(CallBuiltinPostIncSubscript) - TEMP(instr.targetTempIndex) = __qmljs_builtin_post_increment_element(TEMP(instr.base), TEMP(instr.index), context); + VALUE(instr.result) = __qmljs_builtin_post_increment_element(VALUE(instr.base), VALUE(instr.index), context); MOTH_END_INSTR(CallBuiltinTypeofSubscript) MOTH_BEGIN_INSTR(CallBuiltinPostIncName) - TEMP(instr.targetTempIndex) = __qmljs_builtin_post_increment_name(instr.name, context); + VALUE(instr.result) = __qmljs_builtin_post_increment_name(instr.name, context); MOTH_END_INSTR(CallBuiltinTypeofName) MOTH_BEGIN_INSTR(CallBuiltinPostIncValue) - TEMP(instr.targetTempIndex) = __qmljs_builtin_post_increment(TEMPPTR(instr.tempIndex), context); + VALUE(instr.result) = __qmljs_builtin_post_increment(VALUEPTR(instr.value), context); MOTH_END_INSTR(CallBuiltinTypeofValue) MOTH_BEGIN_INSTR(CallBuiltinPostDecMember) - TEMP(instr.targetTempIndex) = __qmljs_builtin_post_decrement_member(TEMP(instr.base), instr.member, context); + VALUE(instr.result) = __qmljs_builtin_post_decrement_member(VALUE(instr.base), instr.member, context); MOTH_END_INSTR(CallBuiltinTypeofMember) MOTH_BEGIN_INSTR(CallBuiltinPostDecSubscript) - TEMP(instr.targetTempIndex) = __qmljs_builtin_post_decrement_element(TEMP(instr.base), TEMP(instr.index), context); + VALUE(instr.result) = __qmljs_builtin_post_decrement_element(VALUE(instr.base), VALUE(instr.index), context); MOTH_END_INSTR(CallBuiltinTypeofSubscript) MOTH_BEGIN_INSTR(CallBuiltinPostDecName) - TEMP(instr.targetTempIndex) = __qmljs_builtin_post_decrement_name(instr.name, context); + VALUE(instr.result) = __qmljs_builtin_post_decrement_name(instr.name, context); MOTH_END_INSTR(CallBuiltinTypeofName) MOTH_BEGIN_INSTR(CallBuiltinPostDecValue) - TEMP(instr.targetTempIndex) = __qmljs_builtin_post_decrement(TEMPPTR(instr.tempIndex), context); + VALUE(instr.result) = __qmljs_builtin_post_decrement(VALUEPTR(instr.value), context); MOTH_END_INSTR(CallBuiltinTypeofValue) MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) @@ -374,34 +364,34 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinDeclareVar) MOTH_BEGIN_INSTR(CallBuiltinDefineGetterSetter) - __qmljs_builtin_define_getter_setter(TEMP(instr.objectTemp), instr.name, TEMP(instr.getterTemp), TEMP(instr.setterTemp), context); + __qmljs_builtin_define_getter_setter(VALUE(instr.object), instr.name, VALUE(instr.getter), VALUE(instr.setter), context); MOTH_END_INSTR(CallBuiltinDefineGetterSetter) MOTH_BEGIN_INSTR(CallBuiltinDefineProperty) - __qmljs_builtin_define_property(TEMP(instr.objectTemp), instr.name, TEMP(instr.valueTemp), context); + __qmljs_builtin_define_property(VALUE(instr.object), instr.name, VALUE(instr.value), context); MOTH_END_INSTR(CallBuiltinDefineProperty) MOTH_BEGIN_INSTR(CallBuiltinDefineArrayProperty) - __qmljs_builtin_define_array_property(TEMP(instr.objectTemp), instr.index, TEMP(instr.valueTemp), context); + __qmljs_builtin_define_array_property(VALUE(instr.object), instr.index, VALUE(instr.value), context); MOTH_END_INSTR(CallBuiltinDefineArrayProperty) MOTH_BEGIN_INSTR(CreateValue) - int argStart = instr.args - context->variableCount(); - VM::Value *args = stack + argStart; - TEMP(instr.targetTempIndex) = __qmljs_construct_value(context, TEMP(instr.func), args, instr.argc); + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_construct_value(context, VALUE(instr.func), args, instr.argc); MOTH_END_INSTR(CreateValue) MOTH_BEGIN_INSTR(CreateProperty) - int argStart = instr.args - context->variableCount(); - VM::Value *args = stack + argStart; - TEMP(instr.targetTempIndex) = __qmljs_construct_property(context, TEMP(instr.base), instr.name, args, instr.argc); + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_construct_property(context, VALUE(instr.base), instr.name, args, instr.argc); MOTH_END_INSTR(CreateProperty) MOTH_BEGIN_INSTR(CreateActivationProperty) - TRACE(inline, "property name = %s, argc = %d", instr.name->toQString().toUtf8().constData(), instr.argc); - int argStart = instr.args - context->variableCount(); - VM::Value *args = stack + argStart; - TEMP(instr.targetTempIndex) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); + TRACE(inline, "property name = %s, args = %d, argc = %d", instr.name->toQString().toUtf8().constData(), instr.args, instr.argc); + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); MOTH_END_INSTR(CreateActivationProperty) MOTH_BEGIN_INSTR(Jump) @@ -409,51 +399,48 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(Jump) MOTH_BEGIN_INSTR(CJump) - if (__qmljs_to_boolean(TEMP(instr.tempIndex), context)) + uint cond = __qmljs_to_boolean(VALUE(instr.condition), context); + TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); + if (cond) code = ((uchar *)&instr.offset) + instr.offset; MOTH_END_INSTR(CJump) MOTH_BEGIN_INSTR(Unop) - TEMP(instr.targetTempIndex) = instr.alu(TEMP(instr.e), context); + VALUE(instr.result) = instr.alu(VALUE(instr.source), context); MOTH_END_INSTR(Unop) MOTH_BEGIN_INSTR(Binop) - VM::Value lhs = instr.lhsIsTemp ? TEMP(instr.lhs.tempIndex) : instr.lhs.value; - VM::Value rhs = instr.rhsIsTemp ? TEMP(instr.rhs.tempIndex) : instr.rhs.value; - TEMP(instr.targetTempIndex) = instr.alu(lhs, rhs, context); + VALUE(instr.result) = instr.alu(VALUE(instr.lhs), VALUE(instr.rhs), context); MOTH_END_INSTR(Binop) MOTH_BEGIN_INSTR(Ret) - VM::Value result = TEMP(instr.tempIndex); + VM::Value &result = VALUE(instr.result); // TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); return result; MOTH_END_INSTR(Ret) MOTH_BEGIN_INSTR(LoadThis) - TEMP(instr.targetTempIndex) = __qmljs_get_thisObject(context); + VALUE(instr.result) = __qmljs_get_thisObject(context); MOTH_END_INSTR(LoadThis) MOTH_BEGIN_INSTR(InplaceElementOp) - VM::Value source = instr.sourceIsTemp ? TEMP(instr.source.tempIndex) : instr.source.value; - instr.alu(TEMP(instr.targetBase), - TEMP(instr.targetIndex), - source, + instr.alu(VALUE(instr.base), + VALUE(instr.index), + VALUE(instr.source), context); MOTH_END_INSTR(InplaceElementOp) MOTH_BEGIN_INSTR(InplaceMemberOp) - VM::Value source = instr.sourceIsTemp ? TEMP(instr.source.tempIndex) : instr.source.value; - instr.alu(source, - TEMP(instr.targetBase), - instr.targetMember, + instr.alu(VALUE(instr.source), + VALUE(instr.base), + instr.member, context); MOTH_END_INSTR(InplaceMemberOp) MOTH_BEGIN_INSTR(InplaceNameOp) - TRACE(name, "%s", instr.targetName->toQString().toUtf8().constData()); - VM::Value source = instr.sourceIsTemp ? TEMP(instr.source.tempIndex) : instr.source.value; - instr.alu(source, - instr.targetName, + TRACE(name, "%s", instr.name->toQString().toUtf8().constData()); + instr.alu(VALUE(instr.source), + instr.name, context); MOTH_END_INSTR(InplaceNameOp) @@ -487,16 +474,16 @@ VM::Value VME::exec(VM::ExecutionContext *ctxt, const uchar *code) return vme(ctxt, code); } -void VME::restoreState(VM::ExecutionContext *context, int &targetTempIndex, const uchar *&code) +void VME::restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code) { VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); - targetTempIndex = handler.targetTempIndex; + target = handler.target; code = handler.code; } -void VME::saveState(VM::ExecutionContext *context, int targetTempIndex, const uchar *code) +void VME::saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code) { VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); - handler.targetTempIndex = targetTempIndex; + handler.target = target; handler.code = code; } diff --git a/moth/qv4vme_moth_p.h b/moth/qv4vme_moth_p.h index c3ccccd59a..2fd877f7b9 100644 --- a/moth/qv4vme_moth_p.h +++ b/moth/qv4vme_moth_p.h @@ -27,8 +27,8 @@ public: #endif private: - static void restoreState(VM::ExecutionContext *context, int &targetTempIndex, const uchar *&code); - static void saveState(VM::ExecutionContext *context, int targetTempIndex, const uchar *code); + static void restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code); + static void saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code); }; } // namespace Moth diff --git a/qmljs_engine.h b/qmljs_engine.h index 7bf3957bae..247e0b1bd9 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -160,7 +160,7 @@ struct ExecutionEngine struct ExceptionHandler { ExecutionContext *context; const uchar *code; // Interpreter state - int targetTempIndex; // Interpreter state + Value *target; // Interpreter state jmp_buf stackFrame; }; diff --git a/qv4mm.cpp b/qv4mm.cpp index 71e6def7db..ce7ad2e331 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -293,9 +293,9 @@ void MemoryManager::setExecutionEngine(ExecutionEngine *engine) void MemoryManager::dumpStats() const { +#ifdef DETAILED_MM_STATS std::cerr << "=================" << std::endl; std::cerr << "Allocation stats:" << std::endl; -#ifdef DETAILED_MM_STATS std::cerr << "Requests for each chunk size:" << std::endl; for (int i = 0; i < m_d->allocSizeCounters.size(); ++i) { if (unsigned count = m_d->allocSizeCounters[i]) { diff --git a/v4.pro b/v4.pro index 980d0d6155..12e9013c6c 100644 --- a/v4.pro +++ b/v4.pro @@ -135,7 +135,7 @@ checktarget.depends = all QMAKE_EXTRA_TARGETS += checktarget checkmothtarget.target = check-interpreter -checkmothtarget.commands = python $$TESTSCRIPT --command=\"$$V4CMD --interpret\" --parallel --with-test-expectations --update-expectations +checkmothtarget.commands = python $$TESTSCRIPT --command=\"$$V4CMD --interpret\" --parallel --with-test-expectations checkmothtarget.depends = all QMAKE_EXTRA_TARGETS += checkmothtarget -- cgit v1.2.3 From db75237fff1cf6a657acd3eae2e79f2dc166979c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 29 Jan 2013 09:17:22 +0100 Subject: "Fix" llvm build by adding empty definitions for missing methods. Change-Id: I172ad23bc56bda9b3a8276cb102599d6243026c4 Reviewed-by: Lars Knoll --- qv4isel_llvm.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4isel_llvm_p.h | 9 ++++++++ 2 files changed, 72 insertions(+) diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp index 6bd3412ab8..e575fa0bd1 100644 --- a/qv4isel_llvm.cpp +++ b/qv4isel_llvm.cpp @@ -363,6 +363,62 @@ void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) Q_UNREACHABLE(); } +void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + void InstructionSelection::callBuiltinThrow(IR::Temp *arg) { // TODO @@ -462,6 +518,13 @@ void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR: Q_UNREACHABLE(); } +void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result) diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h index c2c20d66f7..46fd2fefa4 100644 --- a/qv4isel_llvm_p.h +++ b/qv4isel_llvm_p.h @@ -79,6 +79,14 @@ public: // methods from InstructionSelection: virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); virtual void callBuiltinDeleteValue(IR::Temp *result); + virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); virtual void callBuiltinDeleteExceptionHandler(); @@ -93,6 +101,7 @@ public: // methods from InstructionSelection: virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result); virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); -- cgit v1.2.3 From 8caa74dce24963fb56d64ce631588ae31f9658a6 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Sun, 27 Jan 2013 09:59:37 +0100 Subject: Split CallBuitin variants into separate instructions. This saves space by removing (sometimes) unused parameters, and the need to store and switch over another enum to determine which of the various built-in instructions is called. Change-Id: Ie59c49608bf445a07d406b45fd48cfb94bbfc919 Reviewed-by: Lars Knoll --- moth/qv4instr_moth_p.h | 41 ++++++++++++++++------- moth/qv4isel_moth.cpp | 18 ++++------- moth/qv4vme_moth.cpp | 88 +++++++++++++++++++++++--------------------------- 3 files changed, 77 insertions(+), 70 deletions(-) diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index b5695b76e1..741ddba79d 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -20,7 +20,12 @@ F(CallProperty, callProperty) \ F(CallElement, callElement) \ F(CallActivationProperty, callActivationProperty) \ - F(CallBuiltin, callBuiltin) \ + F(CallBuiltinThrow, callBuiltinThrow) \ + F(CallBuiltinCreateExceptionHandler, callBuiltinCreateExceptionHandler) \ + F(CallBuiltinDeleteExceptionHandler, callBuiltinDeleteExceptionHandler) \ + F(CallBuiltinGetException, callBuiltinGetException) \ + F(CallBuiltinPushScope, callBuiltinPushScope) \ + F(CallBuiltinPopScope, callBuiltinPopScope) \ F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \ F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ @@ -218,19 +223,28 @@ union Instr quint32 args; Param result; }; - struct instr_callBuiltin { + struct instr_callBuiltinThrow { MOTH_INSTR_HEADER - enum { - builtin_throw, - builtin_create_exception_handler, - builtin_delete_exception_handler, - builtin_get_exception, - builtin_push_with_scope, - builtin_pop_scope - } builtin; Param arg; + }; + struct instr_callBuiltinCreateExceptionHandler { + MOTH_INSTR_HEADER Param result; }; + struct instr_callBuiltinDeleteExceptionHandler { + MOTH_INSTR_HEADER + }; + struct instr_callBuiltinGetException { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_callBuiltinPushScope { + MOTH_INSTR_HEADER + Param arg; + }; + struct instr_callBuiltinPopScope { + MOTH_INSTR_HEADER + }; struct instr_callBuiltinForeachIteratorObject { MOTH_INSTR_HEADER Param arg; @@ -433,7 +447,12 @@ union Instr instr_callProperty callProperty; instr_callElement callElement; instr_callActivationProperty callActivationProperty; - instr_callBuiltin callBuiltin; + instr_callBuiltinThrow callBuiltinThrow; + instr_callBuiltinCreateExceptionHandler callBuiltinCreateExceptionHandler; + instr_callBuiltinDeleteExceptionHandler callBuiltinDeleteExceptionHandler; + instr_callBuiltinGetException callBuiltinGetException; + instr_callBuiltinPushScope callBuiltinPushScope; + instr_callBuiltinPopScope callBuiltinPopScope; instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName; instr_callBuiltinDeleteMember callBuiltinDeleteMember; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 1e58314a49..2eab5f212e 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -820,31 +820,27 @@ void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Te void InstructionSelection::callBuiltinThrow(IR::Temp *arg) { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_throw; + Instruction::CallBuiltinThrow call; call.arg = getParam(arg); addInstruction(call); } void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_create_exception_handler; + Instruction::CallBuiltinCreateExceptionHandler call; call.result = getResultParam(result); addInstruction(call); } void InstructionSelection::callBuiltinDeleteExceptionHandler() { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_delete_exception_handler; + Instruction::CallBuiltinDeleteExceptionHandler call; addInstruction(call); } void InstructionSelection::callBuiltinGetException(IR::Temp *result) { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_get_exception; + Instruction::CallBuiltinGetException call; call.result = getResultParam(result); addInstruction(call); } @@ -867,16 +863,14 @@ void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR: void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_push_with_scope; + Instruction::CallBuiltinPushScope call; call.arg = getParam(arg); addInstruction(call); } void InstructionSelection::callBuiltinPopScope() { - Instruction::CallBuiltin call; - call.builtin = Instruction::CallBuiltin::builtin_pop_scope; + Instruction::CallBuiltinPopScope call; addInstruction(call); } diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index 1578b2c242..a3ab1de46a 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -243,53 +243,47 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co VALUE(instr.result) = __qmljs_call_activation_property(context, instr.name, args, instr.argc); MOTH_END_INSTR(CallActivationProperty) - MOTH_BEGIN_INSTR(CallBuiltin) - // TODO: split this into separate instructions - switch (instr.builtin) { - case Instr::instr_callBuiltin::builtin_throw: - TRACE(builtin_throw, "Throwing now...%s", ""); - __qmljs_builtin_throw(VALUE(instr.arg), context); - break; - case Instr::instr_callBuiltin::builtin_create_exception_handler: { - TRACE(builtin_create_exception_handler, "%s", ""); - void *buf = __qmljs_create_exception_handler(context); - // The resultIndex is the only value we need from the instr to - // continue execution when an exception is caught. - VM::Value *result = getValueRef(context, stack, instr.result, stackSize); - int didThrow = setjmp(* static_cast(buf)); - // Two ways to come here: after a create, or after a throw. - if (didThrow) - // At this point, the interpreter state can be anything but - // valid, so first restore the state. This includes all relevant - // locals. - restoreState(context, result, code); - else - // Save the state and any variables we need when catching an - // exception, so we can restore the state at that point. - saveState(context, result, code); - *result = VM::Value::fromInt32(didThrow); - } break; - case Instr::instr_callBuiltin::builtin_delete_exception_handler: - TRACE(builtin_delete_exception_handler, "%s", ""); - __qmljs_delete_exception_handler(context); - break; - case Instr::instr_callBuiltin::builtin_get_exception: - TRACE(builtin_get_exception, "%s", ""); - VALUE(instr.result) = __qmljs_get_exception(context); - break; - case Instr::instr_callBuiltin::builtin_push_with_scope: - TRACE(builtin_push_with_scope, "%s", ""); - context = __qmljs_builtin_push_with_scope(VALUE(instr.arg), context); - break; - case Instr::instr_callBuiltin::builtin_pop_scope: - TRACE(builtin_pop_scope, "%s", ""); - context = __qmljs_builtin_pop_scope(context); - break; - default: - Q_UNIMPLEMENTED(); - return VM::Value(); - } - MOTH_END_INSTR(CallBuiltin) + MOTH_BEGIN_INSTR(CallBuiltinThrow) + __qmljs_builtin_throw(VALUE(instr.arg), context); + MOTH_END_INSTR(CallBuiltinThrow) + + MOTH_BEGIN_INSTR(CallBuiltinCreateExceptionHandler) + void *buf = __qmljs_create_exception_handler(context); + // The result is the only value we need from the instr to + // continue execution when an exception is caught. + VM::Value *result = getValueRef(context, stack, instr.result +#if !defined(QT_NO_DEBUG) + , stackSize +#endif + ); + int didThrow = setjmp(* static_cast(buf)); + // Two ways to come here: after a create, or after a throw. + if (didThrow) + // At this point, the interpreter state can be anything but + // valid, so first restore the state. + restoreState(context, result, code); + else + // Save the state and any variables we need when catching an + // exception, so we can restore the state at that point. + saveState(context, result, code); + *result = VM::Value::fromInt32(didThrow); + MOTH_END_INSTR(CallBuiltinCreateExceptionHandler) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteExceptionHandler) + __qmljs_delete_exception_handler(context); + MOTH_END_INSTR(CallBuiltinDeleteExceptionHandler) + + MOTH_BEGIN_INSTR(CallBuiltinGetException) + VALUE(instr.result) = __qmljs_get_exception(context); + MOTH_END_INSTR(CallBuiltinGetException) + + MOTH_BEGIN_INSTR(CallBuiltinPushScope) + context = __qmljs_builtin_push_with_scope(VALUE(instr.arg), context); + MOTH_END_INSTR(CallBuiltinPushScope) + + MOTH_BEGIN_INSTR(CallBuiltinPopScope) + context = __qmljs_builtin_pop_scope(context); + MOTH_END_INSTR(CallBuiltinPopScope) MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) VALUE(instr.result) = __qmljs_foreach_iterator_object(VALUE(instr.arg), context); -- cgit v1.2.3 From 0e0a0bbb00695be39241a937c4f7eb263cc1ed40 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 28 Jan 2013 16:46:09 +0100 Subject: Garbage collection for Strings Let String inherit from Managed, and manage them through the same garbage collector as for regular objects. Change-Id: Iae1e047daeff683519d52beddcaca7824a01bb27 Reviewed-by: Lars Knoll Reviewed-by: Simon Hausmann --- main.cpp | 1 + moth/qv4isel_moth.cpp | 58 +++++++++++++++++------------- moth/qv4isel_moth_p.h | 3 ++ qmljs_engine.cpp | 64 ++++++++++++++++++++------------- qmljs_engine.h | 5 ++- qmljs_environment.cpp | 23 +++++++++--- qmljs_environment.h | 2 ++ qmljs_runtime.cpp | 6 ++-- qmljs_value.cpp | 1 - qmljs_value.h | 14 ++++++++ qv4argumentsobject.cpp | 6 ++-- qv4array.cpp | 4 +-- qv4functionobject.cpp | 50 +++++++++++++++++++++----- qv4functionobject.h | 15 +++++--- qv4isel_masm.cpp | 6 ++-- qv4isel_p.cpp | 4 +-- qv4managed.h | 41 +++++++++++---------- qv4mm.cpp | 96 ++++++++++++++++++-------------------------------- qv4mm.h | 6 ++-- qv4object.cpp | 5 +-- qv4object.h | 2 +- qv4string.cpp | 21 +++++------ qv4string.h | 26 +++++++------- qv4stringobject.cpp | 6 ++++ qv4stringobject.h | 3 ++ 25 files changed, 279 insertions(+), 189 deletions(-) diff --git a/main.cpp b/main.cpp index 741171ea1d..07e8b3cc24 100644 --- a/main.cpp +++ b/main.cpp @@ -399,6 +399,7 @@ int main(int argc, char *argv[]) /*strictMode =*/ false, /*inheritContext =*/ false); if (!f) continue; + vm.globalCode = f; ctx->strictMode = f->isStrict; if (debugger) diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 2eab5f212e..5522be4c32 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -245,6 +245,7 @@ inline ALUFunction aluOpFunction(IR::AluOp op) InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module) : EvalInstructionSelection(engine, module) , _function(0) + , _vmFunction(0) , _block(0) , _codeStart(0) , _codeNext(0) @@ -269,6 +270,7 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) uchar *codeEnd = codeStart + codeSize; qSwap(_function, function); + qSwap(_vmFunction, vmFunction); qSwap(block, _block); qSwap(patches, _patches); qSwap(addrs, _addrs); @@ -295,10 +297,11 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) patchJumpAddresses(); - vmFunction->code = VME::exec; - vmFunction->codeData = squeezeCode(); + _vmFunction->code = VME::exec; + _vmFunction->codeData = squeezeCode(); qSwap(_function, function); + qSwap(_vmFunction, vmFunction); qSwap(block, _block); qSwap(patches, _patches); qSwap(addrs, _addrs); @@ -323,7 +326,7 @@ void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR: // call the property on the loaded base Instruction::CallProperty call; call.base = getParam(base); - call.name = engine()->newString(name); + call.name = identifier(name); prepareCallArgs(args, call.argc, call.args); call.result = getResultParam(result); addInstruction(call); @@ -345,7 +348,7 @@ void InstructionSelection::constructActivationProperty(IR::Name *func, IR::Temp *result) { Instruction::CreateActivationProperty create; - create.name = engine()->newString(*func->id); + create.name = identifier(*func->id); prepareCallArgs(args, create.argc, create.args); create.result = getResultParam(result); addInstruction(create); @@ -355,7 +358,7 @@ void InstructionSelection::constructProperty(IR::Temp *base, const QString &name { Instruction::CreateProperty create; create.base = getParam(base); - create.name = engine()->newString(name); + create.name = identifier(name); prepareCallArgs(args, create.argc, create.args); create.result = getResultParam(result); addInstruction(create); @@ -390,7 +393,7 @@ void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTem void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) { Instruction::LoadValue load; - load.value = Instr::Param::createValue(VM::Value::fromString(engine()->newString(str))); + load.value = Instr::Param::createValue(VM::Value::fromString(identifier(str))); load.result = getResultParam(targetTemp); addInstruction(load); } @@ -409,7 +412,7 @@ void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *target void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) { Instruction::LoadName load; - load.name = engine()->newString(name); + load.name = identifier(name); load.result = getResultParam(temp); addInstruction(load); } @@ -418,7 +421,7 @@ void InstructionSelection::setActivationProperty(IR::Expr *source, const QString { Instruction::StoreName store; store.source = getParam(source); - store.name = engine()->newString(targetName); + store.name = identifier(targetName); addInstruction(store); } @@ -436,7 +439,7 @@ void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR:: { Instruction::LoadProperty load; load.base = getParam(base); - load.name = engine()->newString(name); + load.name = identifier(name); load.result = getResultParam(target); addInstruction(load); } @@ -445,7 +448,7 @@ void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, c { Instruction::StoreProperty store; store.base = getParam(targetBase); - store.name = engine()->newString(targetName); + store.name = identifier(targetName); store.source = getParam(source); addInstruction(store); } @@ -532,7 +535,7 @@ void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, c if (op) { Instruction::InplaceNameOp ieo; ieo.alu = op; - ieo.name = engine()->newString(targetName); + ieo.name = identifier(targetName); ieo.source = getParam(sourceExpr); addInstruction(ieo); } @@ -585,7 +588,7 @@ void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR: Instruction::InplaceMemberOp imo; imo.alu = op; imo.base = getParam(targetBase); - imo.member = engine()->newString(targetName); + imo.member = identifier(targetName); imo.source = getParam(source); addInstruction(imo); } @@ -676,7 +679,7 @@ void InstructionSelection::visitRet(IR::Ret *s) void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) { Instruction::CallActivationProperty call; - call.name = engine()->newString(*func->id); + call.name = identifier(*func->id); prepareCallArgs(args, call.argc, call.args); call.result = getResultParam(result); addInstruction(call); @@ -686,7 +689,7 @@ void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString { Instruction::CallBuiltinTypeofMember call; call.base = getParam(base); - call.member = engine()->identifier(name); + call.member = identifier(name); call.result = getResultParam(result); addInstruction(call); } @@ -703,7 +706,7 @@ void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp * void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result) { Instruction::CallBuiltinTypeofName call; - call.name = engine()->identifier(name); + call.name = identifier(name); call.result = getResultParam(result); addInstruction(call); } @@ -720,7 +723,7 @@ void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString { Instruction::CallBuiltinDeleteMember call; call.base = getParam(base); - call.member = engine()->newString(name); + call.member = identifier(name); call.result = getResultParam(result); addInstruction(call); } @@ -737,7 +740,7 @@ void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp * void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) { Instruction::CallBuiltinDeleteName call; - call.name = engine()->newString(name); + call.name = identifier(name); call.result = getResultParam(result); addInstruction(call); } @@ -754,7 +757,7 @@ void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const { Instruction::CallBuiltinPostDecMember call; call.base = getParam(base); - call.member = engine()->identifier(name); + call.member = identifier(name); call.result = getResultParam(result); addInstruction(call); } @@ -771,7 +774,7 @@ void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR: void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result) { Instruction::CallBuiltinPostDecName call; - call.name = engine()->identifier(name); + call.name = identifier(name); call.result = getResultParam(result); addInstruction(call); } @@ -788,7 +791,7 @@ void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const { Instruction::CallBuiltinPostIncMember call; call.base = getParam(base); - call.member = engine()->identifier(name); + call.member = identifier(name); call.result = getResultParam(result); addInstruction(call); } @@ -805,7 +808,7 @@ void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR: void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result) { Instruction::CallBuiltinPostIncName call; - call.name = engine()->identifier(name); + call.name = identifier(name); call.result = getResultParam(result); addInstruction(call); } @@ -878,7 +881,7 @@ void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString & { Instruction::CallBuiltinDeclareVar call; call.isDeletable = deletable; - call.varName = engine()->newString(name); + call.varName = identifier(name); addInstruction(call); } @@ -886,7 +889,7 @@ void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const { Instruction::CallBuiltinDefineGetterSetter call; call.object = getParam(object); - call.name = engine()->newString(name); + call.name = identifier(name); call.getter = getParam(getter); call.setter = getParam(setter); addInstruction(call); @@ -896,7 +899,7 @@ void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QSt { Instruction::CallBuiltinDefineProperty call; call.object = getParam(object); - call.name = engine()->newString(name); + call.name = identifier(name); call.value = getParam(value); addInstruction(call); } @@ -963,3 +966,10 @@ uchar *InstructionSelection::squeezeCode() const ::memcpy(squeezed, _codeStart, codeSize); return squeezed; } + +VM::String *InstructionSelection::identifier(const QString &s) +{ + VM::String *str = engine()->identifier(s); + _vmFunction->identifiers.append(str); + return str; +} diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index ad07dcb0bc..90b191b627 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -131,7 +131,10 @@ private: void patchJumpAddresses(); uchar *squeezeCode() const; + VM::String *identifier(const QString &s); + IR::Function *_function; + VM::Function *_vmFunction; IR::BasicBlock *_block; QHash > _patches; diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 4b53d06b72..e688233677 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -39,6 +39,7 @@ ** ****************************************************************************/ #include +#include #include #include #include @@ -59,36 +60,16 @@ namespace QQmlJS { namespace VM { -class StringPool -{ - QHash strings; -public: - ~StringPool() - { - qDeleteAll(strings); - } - - String *newString(const QString &s) - { - QHash::const_iterator it = strings.find(s); - if (it != strings.end()) - return it.value(); - String *str = new String(s); - strings.insert(s, str); - return str; - } -}; - ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) : memoryManager(new QQmlJS::VM::MemoryManager) , iselFactory(factory) , debugger(0) , globalObject(Value::nullValue()) + , globalCode(0) , exception(Value::nullValue()) { MemoryManager::GCBlocker gcBlocker(memoryManager); - stringPool.reset(new StringPool); memoryManager->setExecutionEngine(this); rootContext = newContext(); @@ -250,12 +231,12 @@ ExecutionContext *ExecutionEngine::newContext() String *ExecutionEngine::identifier(const QString &s) { - return stringPool->newString(s); + return new (memoryManager) String(s); } Function *ExecutionEngine::newFunction(const QString &name) { - VM::Function *f = new VM::Function(name); + VM::Function *f = new VM::Function(identifier(name)); functions.append(f); return f; } @@ -297,7 +278,7 @@ FunctionObject *ExecutionEngine::newObjectCtor(ExecutionContext *ctx) String *ExecutionEngine::newString(const QString &s) { - return stringPool->newString(s); + return new (memoryManager) String(s); } Object *ExecutionEngine::newStringObject(ExecutionContext *ctx, const Value &value) @@ -467,5 +448,40 @@ void ExecutionEngine::requireArgumentsAccessors(int n) } } +void ExecutionEngine::markObjects() +{ + globalObject.mark(); + + if (globalCode) + globalCode->mark(); + + exception.mark(); + + for (int i = 0; i < argumentsAccessors.size(); ++i) { + const PropertyDescriptor &pd = argumentsAccessors.at(i); + pd.get->mark(); + pd.set->mark(); + } + + + for (int i = 0; i < functions.size(); ++i) + functions.at(i)->mark(); + + id_length->mark(); + id_prototype->mark(); + id_constructor->mark(); + id_arguments->mark(); + id_caller->mark(); + id_this->mark(); + id___proto__->mark(); + id_enumerable->mark(); + id_configurable->mark(); + id_writable->mark(); + id_value->mark(); + id_get->mark(); + id_set->mark(); + id_eval->mark(); +} + } // namespace VM } // namespace QQmlJS diff --git a/qmljs_engine.h b/qmljs_engine.h index 247e0b1bd9..8945bd3844 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -106,6 +106,8 @@ struct ExecutionEngine Value globalObject; + VM::Function *globalCode; + Value objectCtor; Value stringCtor; Value numberCtor; @@ -167,7 +169,6 @@ struct ExecutionEngine QVector unwindStack; Value exception; - QScopedPointer stringPool; QVector functions; ExecutionEngine(EvalISelFactory *iselFactory); @@ -222,6 +223,8 @@ struct ExecutionEngine Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o); void requireArgumentsAccessors(int n); + + void markObjects(); }; } // namespace VM diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 8b2be30042..d76bbb91bd 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -277,6 +277,21 @@ bool ExecutionContext::deleteProperty(String *name) return true; } +void ExecutionContext::mark() +{ + thisObject.mark(); + if (function) + function->mark(); + for (unsigned arg = 0, lastArg = formalCount(); arg < lastArg; ++arg) + arguments[arg].mark(); + for (unsigned local = 0, lastLocal = variableCount(); local < lastLocal; ++local) + locals[local].mark(); + if (activation) + activation->mark(); + if (withObject) + withObject->mark(); +} + void ExecutionContext::setProperty(String *name, Value value) { // qDebug() << "=== SetProperty" << value.toString(this)->toQString(); @@ -294,14 +309,14 @@ void ExecutionContext::setProperty(String *name, Value value) return; } } - if (strictMode || name == engine->id_this) + if (strictMode || name->isEqualTo(engine->id_this)) throwReferenceError(Value::fromString(name)); engine->globalObject.objectValue()->__put__(this, name, value); } Value ExecutionContext::getProperty(String *name) { - if (name == engine->id_this) + if (name->isEqualTo(engine->id_this)) return thisObject; bool hasWith = false; @@ -342,7 +357,7 @@ Value ExecutionContext::getProperty(String *name) Value ExecutionContext::getPropertyNoThrow(String *name) { - if (name == engine->id_this) + if (name->isEqualTo(engine->id_this)) return thisObject; bool hasWith = false; @@ -380,7 +395,7 @@ Value ExecutionContext::getPropertyAndBase(String *name, Object **base) { *base = 0; - if (name == engine->id_this) + if (name->isEqualTo(engine->id_this)) return thisObject; bool hasWith = false; diff --git a/qmljs_environment.h b/qmljs_environment.h index 0bdbdd5715..97c85612ed 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -133,6 +133,8 @@ struct ExecutionContext return arguments[index]; return Value::undefinedValue(); } + + void mark(); }; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 4fdf2d31fe..469281c89d 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -134,10 +134,10 @@ Function *__qmljs_register_function(ExecutionContext *ctx, String *name, for (unsigned i = 0; i < formalCount; ++i) if (formals[i]) - f->formals.append(formals[i]->toQString()); + f->formals.append(formals[i]); for (unsigned i = 0; i < localCount; ++i) if (locals[i]) - f->locals.append(locals[i]->toQString()); + f->locals.append(locals[i]); return f; } @@ -741,7 +741,7 @@ Value __qmljs_call_activation_property(ExecutionContext *context, String *name, Value thisObject = base ? Value::fromObject(base) : Value::undefinedValue(); - if (o == context->engine->evalFunction && name == context->engine->id_eval) + if (o == context->engine->evalFunction && name->isEqualTo(context->engine->id_eval)) return static_cast(o)->evalCall(context, thisObject, args, argc, true); return o->call(context, thisObject, args, argc); diff --git a/qmljs_value.cpp b/qmljs_value.cpp index ec3ff21f9c..8dba13a9c9 100644 --- a/qmljs_value.cpp +++ b/qmljs_value.cpp @@ -78,7 +78,6 @@ Value Value::toObject(ExecutionContext *ctx) const return __qmljs_to_object(*this, ctx); } - bool Value::sameValue(Value other) const { if (val == other.val) return true; diff --git a/qmljs_value.h b/qmljs_value.h index 5d0e4956d9..401b61fc8f 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -240,6 +240,7 @@ struct Value return b; } + Managed *asManaged() const; Object *asObject() const; FunctionObject *asFunctionObject() const; BooleanObject *asBooleanObject() const; @@ -256,6 +257,12 @@ struct Value // Section 9.12 bool sameValue(Value other) const; + + void mark() const { + Managed *m = asManaged(); + if (m) + m->mark(); + } }; inline Value Value::undefinedValue() @@ -415,6 +422,13 @@ inline uint Value::asArrayLength(ExecutionContext *ctx, bool *ok) const } +inline Managed *Value::asManaged() const +{ + if (isObject() || isString()) + return managed(); + return 0; +} + inline Object *Value::asObject() const { return isObject() ? objectValue() : 0; diff --git a/qv4argumentsobject.cpp b/qv4argumentsobject.cpp index b2dc3d4656..f7327506e9 100644 --- a/qv4argumentsobject.cpp +++ b/qv4argumentsobject.cpp @@ -121,9 +121,9 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const void ArgumentsObject::markObjects() { for (int i = 0; i < mappedArguments.size(); ++i) { - Object *o = mappedArguments.at(i).asObject(); - if (o) - o->mark(); + Managed *m = mappedArguments.at(i).asManaged(); + if (m) + m->mark(); } Object::markObjects(); } diff --git a/qv4array.cpp b/qv4array.cpp index 70bbd9d467..9879b2f372 100644 --- a/qv4array.cpp +++ b/qv4array.cpp @@ -634,8 +634,8 @@ void Array::markObjects() const for (; i < (uint)values.size(); ++i) { const PropertyDescriptor &pd = values.at(i); if (pd.isData()) { - if (Object *o = pd.value.asObject()) - o->mark(); + if (Managed *m = pd.value.asManaged()) + m->mark(); } else if (pd.isAccessor()) { if (pd.get) pd.get->mark(); diff --git a/qv4functionobject.cpp b/qv4functionobject.cpp index a55f96dd26..dfc22a3d8a 100644 --- a/qv4functionobject.cpp +++ b/qv4functionobject.cpp @@ -69,6 +69,21 @@ Function::~Function() delete[] codeData; } +void Function::mark() +{ + if (name) + name->mark(); + for (int i = 0; i < formals.size(); ++i) + formals.at(i)->mark(); + for (int i = 0; i < locals.size(); ++i) + locals.at(i)->mark(); + for (int i = 0; i < generatedValues.size(); ++i) + if (Managed *m = generatedValues.at(i).asManaged()) + m->mark(); + for (int i = 0; i < identifiers.size(); ++i) + identifiers.at(i)->mark(); +} + FunctionObject::FunctionObject(ExecutionContext *scope) : scope(scope) , name(0) @@ -146,6 +161,18 @@ Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *a return result; } +void FunctionObject::markObjects() +{ + if (name) + name->mark(); + for (uint i = 0; i < formalParameterCount; ++i) + formalParameterList[i]->mark(); + for (uint i = 0; i < varCount; ++i) + varList[i]->mark(); + scope->mark(); + Object::markObjects(); +} + Value FunctionObject::call(ExecutionContext *ctx) { Q_UNUSED(ctx); @@ -305,25 +332,26 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) MemoryManager::GCBlocker gcBlocker(scope->engine->memoryManager); - if (!function->name.isEmpty()) - name = scope->engine->identifier(function->name); + name = function->name; needsActivation = function->needsActivation(); usesArgumentsObject = function->usesArgumentsObject; strictMode = function->isStrict; formalParameterCount = function->formals.size(); + // ### no need to copy if (formalParameterCount) { formalParameterList = new String*[formalParameterCount]; for (unsigned int i = 0; i < formalParameterCount; ++i) { - formalParameterList[i] = scope->engine->identifier(function->formals.at(i)); + formalParameterList[i] = function->formals.at(i); } } defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(formalParameterCount)); varCount = function->locals.size(); + // ### no need to copy if (varCount) { varList = new String*[varCount]; for (unsigned int i = 0; i < varCount; ++i) { - varList[i] = scope->engine->identifier(function->locals.at(i)); + varList[i] = function->locals.at(i); } } @@ -358,6 +386,12 @@ Value ScriptFunction::call(VM::ExecutionContext *ctx) return function->code(ctx, function->codeData); } +void ScriptFunction::markObjects() +{ + function->mark(); + FunctionObject::markObjects(); +} + BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) : FunctionObject(scope) @@ -419,10 +453,10 @@ bool BoundFunction::hasInstance(ExecutionContext *ctx, const Value &value) void BoundFunction::markObjects() { target->mark(); - if (Object *o = boundThis.asObject()) - o->mark(); + if (Managed *m = boundThis.asManaged()) + m->mark(); for (int i = 0; i < boundArgs.size(); ++i) - if (Object *o = boundArgs.at(i).asObject()) - o->mark(); + if (Managed *m = boundArgs.at(i).asManaged()) + m->mark(); FunctionObject::markObjects(); } diff --git a/qv4functionobject.h b/qv4functionobject.h index ad49cdccd8..dfdcf74dfc 100644 --- a/qv4functionobject.h +++ b/qv4functionobject.h @@ -99,22 +99,23 @@ struct TypeErrorPrototype; struct URIErrorPrototype; struct Function { - QString name; + String *name; VM::Value (*code)(VM::ExecutionContext *, const uchar *); const uchar *codeData; JSC::MacroAssemblerCodeRef codeRef; - QList formals; - QList locals; + QList formals; + QList locals; QVector generatedValues; + QVector identifiers; bool hasNestedFunctions : 1; bool hasDirectEval : 1; bool usesArgumentsObject : 1; bool isStrict : 1; - Function(const QString &name) + Function(String *name) : name(name) , code(0) , codeData(0) @@ -126,6 +127,8 @@ struct Function { ~Function(); inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; } + + void mark(); }; struct FunctionObject: Object { @@ -146,6 +149,8 @@ struct FunctionObject: Object { virtual struct ScriptFunction *asScriptFunction() { return 0; } + virtual void markObjects(); + protected: virtual Value call(ExecutionContext *ctx); virtual Value construct(ExecutionContext *ctx); @@ -187,6 +192,8 @@ struct ScriptFunction: FunctionObject { virtual Value call(ExecutionContext *ctx); virtual ScriptFunction *asScriptFunction() { return this; } + + virtual void markObjects(); }; struct BoundFunction: FunctionObject { diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index e6bd218c62..a4a136c656 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -606,7 +606,7 @@ void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTem void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) { - Value v = Value::fromString(engine()->newString(str)); + Value v = Value::fromString(identifier(str)); _asm->storeValue(v, targetTemp); } @@ -793,7 +793,9 @@ void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::Ex String *InstructionSelection::identifier(const QString &s) { - return engine()->identifier(s); + String *str = engine()->identifier(s); + _vmFunction->identifiers.append(str); + return str; } void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result) diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp index 8cc7febdd9..b408be3673 100644 --- a/qv4isel_p.cpp +++ b/qv4isel_p.cpp @@ -46,10 +46,10 @@ VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngin foreach (const QString *formal, irFunction->formals) if (formal) - vmFunction->formals.append(*formal); + vmFunction->formals.append(engine->identifier(*formal)); foreach (const QString *local, irFunction->locals) if (local) - vmFunction->locals.append(*local); + vmFunction->locals.append(engine->identifier(*local)); foreach (IR::Function *function, irFunction->nestedFunctions) createFunctionMapping(engine, function); diff --git a/qv4managed.h b/qv4managed.h index 60e8d3be8a..ab75826af6 100644 --- a/qv4managed.h +++ b/qv4managed.h @@ -51,6 +51,7 @@ namespace QQmlJS { namespace VM { class MemoryManager; +struct String; struct Object; struct ObjectPrototype; struct ExecutionContext; @@ -75,8 +76,19 @@ private: void operator = (const Managed &other); protected: - Managed() : markBit(0), inUse(1), extensible(true), - isNonStrictArgumentsObject(false), isBuiltinFunction(false), type(Type_Object), unused(0) { } + Managed() + : markBit(0) + , inUse(1) + , extensible(1) + , isNonStrictArgumentsObject(0) + , isBuiltinFunction(0) + , needsActivation(0) + , usesArgumentsObject(0) + , strictMode(0) + , type(Type_Invalid) + , unused(0) + , stringHash(0) + {} virtual ~Managed(); public: @@ -87,11 +99,14 @@ public: if (markBit) return; markBit = 1; - markObjects(); + if (type != Type_String) + markObjects(); } enum Type { - Type_Object = 0, + Type_Invalid, + Type_String, + Type_Object, Type_ArrayObject, Type_FunctionObject, Type_BooleanObject, @@ -104,6 +119,7 @@ public: Type_ForeachIteratorObject }; + String *asString() { return reinterpret_cast(this); } Object *asObject() { return reinterpret_cast(this); } ArrayObject *asArrayObject() { return type == Type_ArrayObject ? reinterpret_cast(this) : 0; } FunctionObject *asFunctionObject() { return type == Type_FunctionObject ? reinterpret_cast(this) : 0; } @@ -120,7 +136,7 @@ public: bool isStringObject() const { return type == Type_StringObject; } protected: - virtual void markObjects() = 0; + virtual void markObjects() {} union { Managed *nextFree; @@ -134,25 +150,14 @@ protected: quintptr usesArgumentsObject : 1; // used by FunctionObject quintptr strictMode : 1; // used by FunctionObject quintptr type : 4; -#if CPU(X86_64) - quintptr unused : 51; -#elif CPU(X86) - quintptr unused : 19; -#else -#error "implement me" -#endif + quintptr unused : 20; + mutable quintptr stringHash : 32; }; }; private: friend class MemoryManager; - friend struct Object; - friend struct ObjectPrototype; - friend class Array; - friend struct ArrayPrototype; - friend struct FunctionObject; friend struct ExecutionContext; - friend struct ScriptFunction; }; } diff --git a/qv4mm.cpp b/qv4mm.cpp index ce7ad2e331..3e5f7c8d64 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -142,6 +142,7 @@ Managed *MemoryManager::alloc(std::size_t size) qSort(m_d->heapChunks); char *chunk = (char *)allocation.memory.base(); char *end = chunk + allocation.memory.size() - size; + memset(chunk, 0, allocation.memory.size()); Managed **last = &m_d->smallItems[pos]; while (chunk <= end) { Managed *o = reinterpret_cast(chunk); @@ -166,13 +167,17 @@ void MemoryManager::scribble(Managed *obj, int c, int size) const ::memset((void *)(obj + 1), c, size - sizeof(Managed)); } -void MemoryManager::mark(const QVector &objects) +void MemoryManager::mark() { - foreach (Managed *m, objects) { - if (!m) - continue; - m->mark(); - } + m_d->engine->markObjects(); + + for (ExecutionContext *ctxt = engine()->current; ctxt; ctxt = ctxt->parent) + ctxt->mark(); + + for (QHash::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it) + it.key()->mark(); + + collectFromStack(); return; } @@ -240,14 +245,8 @@ void MemoryManager::runGC() // QTime t; t.start(); // qDebug() << ">>>>>>>>runGC"; - QVector roots; - collectRoots(roots); -// std::cerr << "GC: found " << roots.size() -// << " roots in " << t.elapsed() -// << "ms" << std::endl; -// t.restart(); - /*std::size_t marks =*/ mark(roots); + mark(); // std::cerr << "GC: marked " << marks // << " objects in " << t.elapsed() // << "ms" << std::endl; @@ -322,64 +321,33 @@ void MemoryManager::willAllocate(std::size_t size) #endif // DETAILED_MM_STATS -void MemoryManager::collectRoots(QVector &roots) const -{ - add(roots, m_d->engine->globalObject); - add(roots, m_d->engine->exception); - - for (int i = 0; i < m_d->engine->argumentsAccessors.size(); ++i) { - const PropertyDescriptor &pd = m_d->engine->argumentsAccessors.at(i); - add(roots, Value::fromObject(pd.get)); - add(roots, Value::fromObject(pd.set)); - } - - for (ExecutionContext *ctxt = engine()->current; ctxt; ctxt = ctxt->parent) { - add(roots, ctxt->thisObject); - if (ctxt->function) - roots.append(ctxt->function); - for (unsigned arg = 0, lastArg = ctxt->formalCount(); arg < lastArg; ++arg) - add(roots, ctxt->arguments[arg]); - for (unsigned local = 0, lastLocal = ctxt->variableCount(); local < lastLocal; ++local) - add(roots, ctxt->locals[local]); - if (ctxt->activation) - roots.append(ctxt->activation); - if (ctxt->withObject) - roots.append(ctxt->withObject); - } - - for (int i = 0; i < m_d->engine->functions.size(); ++i) { - Function* f = m_d->engine->functions.at(i); - for (int k = 0; k < f->generatedValues.count(); ++k) - add(roots, f->generatedValues.at(k)); - } - - collectRootsOnStack(roots); - - for (QHash::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it) - roots.append(it.key()); -} - -void MemoryManager::collectRootsOnStack(QVector &roots) const +void MemoryManager::collectFromStack() const { if (!m_d->heapChunks.count()) return; - Value valueOnStack = Value::undefinedValue(); + quintptr valueOnStack = 0; - void* stackTop = 0; #if USE(PTHREADS) #if OS(DARWIN) + void* stackTop = 0; stackTop = pthread_get_stackaddr_np(pthread_self()); + quintptr *top = static_cast(stackTop); #else + void* stackBottom = 0; pthread_attr_t attr; pthread_getattr_np(pthread_self(), &attr); size_t stackSize = 0; - pthread_attr_getstack(&attr, &stackTop, &stackSize); + pthread_attr_getstack(&attr, &stackBottom, &stackSize); + pthread_attr_destroy(&attr); + + quintptr *top = static_cast(stackBottom) + stackSize/sizeof(quintptr); #endif #endif +// qDebug() << "stack:" << hex << stackTop << stackSize << (stackTop + stackSize); - Value* top = reinterpret_cast(stackTop) - 1; - Value* current = (&valueOnStack) + 1; + quintptr *current = (&valueOnStack) + 1; +// qDebug() << "collectFromStack" << top << current << &valueOnStack; char** heapChunkBoundaries = (char**)alloca(m_d->heapChunks.count() * 2 * sizeof(char*)); char** heapChunkBoundariesEnd = heapChunkBoundaries + 2 * m_d->heapChunks.count(); @@ -391,18 +359,21 @@ void MemoryManager::collectRootsOnStack(QVector &roots) const } for (; current < top; ++current) { - Object* possibleObject = current->asObject(); - if (!possibleObject) - continue; + char* genericPtr = +#if CPU(X86_64) + reinterpret_cast((*current) & ~(quint64(Value::Type_Mask) << Value::Tag_Shift)); +#else + reinterpret_cast(*current); +#endif - char* genericPtr = reinterpret_cast(possibleObject); if (genericPtr < *heapChunkBoundaries || genericPtr >= *(heapChunkBoundariesEnd - 1)) continue; int index = qLowerBound(heapChunkBoundaries, heapChunkBoundariesEnd, genericPtr) - heapChunkBoundaries; // An odd index means the pointer is _before_ the end of a heap chunk and therefore valid. if (index & 1) { int size = m_d->heapChunks.at(index >> 1).chunkSize; - Managed *m = possibleObject; + Managed *m = reinterpret_cast(genericPtr); +// qDebug() << " inside" << size << m; if (((quintptr)m - (quintptr)heapChunkBoundaries[index-1]) % size) // wrongly aligned value, skip it @@ -412,7 +383,8 @@ void MemoryManager::collectRootsOnStack(QVector &roots) const // Skip pointers to already freed objects, they are bogus as well continue; - roots.append(possibleObject); + m->mark(); +// qDebug() << " marking"; } } } diff --git a/qv4mm.h b/qv4mm.h index 07c6e959ad..48d0c3d12e 100644 --- a/qv4mm.h +++ b/qv4mm.h @@ -104,8 +104,6 @@ protected: void scribble(Managed *obj, int c, int size) const; - void collectRootsOnStack(QVector &roots) const; - ExecutionEngine *engine() const; #ifdef DETAILED_MM_STATS @@ -113,8 +111,8 @@ protected: #endif // DETAILED_MM_STATS private: - void collectRoots(QVector &roots) const; - static void mark(const QVector &objects); + void collectFromStack() const; + void mark(); std::size_t sweep(); std::size_t sweep(char *chunkStart, std::size_t chunkSize, size_t size); diff --git a/qv4object.cpp b/qv4object.cpp index 79aee81923..93475fbf7e 100644 --- a/qv4object.cpp +++ b/qv4object.cpp @@ -177,10 +177,11 @@ void Object::markObjects() for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) { if (!(*it)) continue; + (*it)->name->mark(); PropertyDescriptor &pd = (*it)->descriptor; if (pd.isData()) { - if (Object *o = pd.value.asObject()) - o->mark(); + if (Managed *m = pd.value.asManaged()) + m->mark(); } else if (pd.isAccessor()) { if (pd.get) pd.get->mark(); diff --git a/qv4object.h b/qv4object.h index 63a3ba623d..816cbd7d33 100644 --- a/qv4object.h +++ b/qv4object.h @@ -104,7 +104,7 @@ struct Object: Managed { Array array; Object() - : prototype(0) {} + : prototype(0) { type = Type_Object; } Object(const Array &a) : prototype(0), array(a) {} diff --git a/qv4string.cpp b/qv4string.cpp index 0f34fc0114..6e0c14d108 100644 --- a/qv4string.cpp +++ b/qv4string.cpp @@ -41,6 +41,7 @@ #include "qv4string.h" #include "qmljs_runtime.h" +#include namespace QQmlJS { namespace VM { @@ -71,8 +72,8 @@ static uint toArrayIndex(const QChar *ch, const QChar *end) uint String::asArrayIndexSlow() const { - if (_hashValue < LargestHashedArrayIndex) - return _hashValue; + if (stringHash < LargestHashedArrayIndex) + return stringHash; const QChar *ch = _text.constData(); const QChar *end = ch + _text.length(); @@ -83,10 +84,10 @@ uint String::toUInt(bool *ok) const { *ok = true; - if (_hashValue == InvalidHashValue) + if (stringHash == InvalidHashValue) createHashValue(); - if (_hashValue < LargestHashedArrayIndex) - return _hashValue; + if (stringHash < LargestHashedArrayIndex) + return stringHash; double d = __qmljs_string_to_number(this); uint l = (uint)d; @@ -102,10 +103,10 @@ void String::createHashValue() const const QChar *end = ch + _text.length(); // array indices get their number as hash value, for large numbers we set to INT_MAX - _hashValue = toArrayIndex(ch, end); - if (_hashValue < UINT_MAX) { - if (_hashValue > INT_MAX) - _hashValue = INT_MAX; + stringHash = toArrayIndex(ch, end); + if (stringHash < UINT_MAX) { + if (stringHash > INT_MAX) + stringHash = INT_MAX; return; } @@ -116,7 +117,7 @@ void String::createHashValue() const } // set highest bit to mark it as a non number - _hashValue = h | 0xf0000000; + stringHash = h | 0xf0000000; } } diff --git a/qv4string.h b/qv4string.h index 0b35525304..34e241218c 100644 --- a/qv4string.h +++ b/qv4string.h @@ -42,12 +42,15 @@ #define QV4STRING_H #include -#include +#include namespace QQmlJS { namespace VM { -struct String { +struct String : public Managed { + String(const QString &text) + : _text(text) { type = Type_String; stringHash = InvalidHashValue; } + inline bool isEqualTo(const String *other) const { if (this == other) return true; @@ -61,10 +64,10 @@ struct String { } inline unsigned hashValue() const { - if (_hashValue == InvalidHashValue) + if (stringHash == InvalidHashValue) createHashValue(); - return _hashValue; + return stringHash; } enum { InvalidArrayIndex = 0xffffffff, @@ -72,26 +75,21 @@ struct String { InvalidHashValue = 0xffffffff }; uint asArrayIndex() const { - if (_hashValue == InvalidHashValue) + if (stringHash == InvalidHashValue) createHashValue(); - if (_hashValue > LargestHashedArrayIndex) + if (stringHash > LargestHashedArrayIndex) return InvalidArrayIndex; - if (_hashValue < LargestHashedArrayIndex) - return _hashValue; + if (stringHash < LargestHashedArrayIndex) + return stringHash; return asArrayIndexSlow(); } uint asArrayIndexSlow() const; uint toUInt(bool *ok) const; -private: - friend class StringPool; - String(const QString &text) - : _text(text), _hashValue(InvalidHashValue) {} - private: void createHashValue() const; + QString _text; - mutable unsigned _hashValue; }; } diff --git a/qv4stringobject.cpp b/qv4stringobject.cpp index 420fdb2bf9..63f374b013 100644 --- a/qv4stringobject.cpp +++ b/qv4stringobject.cpp @@ -100,6 +100,12 @@ PropertyDescriptor *StringObject::getIndex(ExecutionContext *ctx, uint index) return &tmpProperty; } +void StringObject::markObjects() +{ + value.stringValue()->mark(); + Object::markObjects(); +} + StringCtor::StringCtor(ExecutionContext *scope) : FunctionObject(scope) diff --git a/qv4stringobject.h b/qv4stringobject.h index 97574d951a..d131e942ab 100644 --- a/qv4stringobject.h +++ b/qv4stringobject.h @@ -55,6 +55,9 @@ struct StringObject: Object { virtual QString className() { return QStringLiteral("String"); } PropertyDescriptor *getIndex(ExecutionContext *ctx, uint index); + +protected: + virtual void markObjects(); }; struct StringCtor: FunctionObject -- cgit v1.2.3 From ff99f697be0f2caa7675d79aee8627b01f4b5f4a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 29 Jan 2013 11:24:43 +0100 Subject: Don't copy formals and locals from VM::Function to FunctionObject This should make creation of a FunctionObject quite a bit cheaper Change-Id: I0937f9f2354b7abc47c3673f4957bd70ff9a97b8 Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 4 ++-- qmljs_environment.h | 4 ++-- qv4functionobject.cpp | 27 +++++++-------------------- qv4functionobject.h | 8 ++++---- qv4globalobject.cpp | 2 +- 5 files changed, 16 insertions(+), 29 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index d76bbb91bd..007928cd41 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -188,7 +188,7 @@ ExecutionContext *ExecutionContext::popScope() return engine->current; } -String **ExecutionContext::formals() const +String * const *ExecutionContext::formals() const { return function ? function->formalParameterList : 0; } @@ -198,7 +198,7 @@ unsigned int ExecutionContext::formalCount() const return function ? function->formalParameterCount : 0; } -String **ExecutionContext::variables() const +String * const *ExecutionContext::variables() const { return function ? function->varList : 0; } diff --git a/qmljs_environment.h b/qmljs_environment.h index 97c85612ed..ec135a46cd 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -83,9 +83,9 @@ struct ExecutionContext unsigned int argumentCount; Value *locals; - String **formals() const; + String * const *formals() const; unsigned int formalCount() const; - String **variables() const; + String * const *variables() const; unsigned int variableCount() const; bool strictMode; diff --git a/qv4functionobject.cpp b/qv4functionobject.cpp index dfc22a3d8a..e4635505f3 100644 --- a/qv4functionobject.cpp +++ b/qv4functionobject.cpp @@ -165,10 +165,11 @@ void FunctionObject::markObjects() { if (name) name->mark(); - for (uint i = 0; i < formalParameterCount; ++i) - formalParameterList[i]->mark(); - for (uint i = 0; i < varCount; ++i) - varList[i]->mark(); + // these are marked in VM::Function: +// for (uint i = 0; i < formalParameterCount; ++i) +// formalParameterList[i]->mark(); +// for (uint i = 0; i < varCount; ++i) +// varList[i]->mark(); scope->mark(); Object::markObjects(); } @@ -337,23 +338,11 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) usesArgumentsObject = function->usesArgumentsObject; strictMode = function->isStrict; formalParameterCount = function->formals.size(); - // ### no need to copy - if (formalParameterCount) { - formalParameterList = new String*[formalParameterCount]; - for (unsigned int i = 0; i < formalParameterCount; ++i) { - formalParameterList[i] = function->formals.at(i); - } - } + formalParameterList = function->formals.constData(); defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(formalParameterCount)); varCount = function->locals.size(); - // ### no need to copy - if (varCount) { - varList = new String*[varCount]; - for (unsigned int i = 0; i < varCount; ++i) { - varList[i] = function->locals.at(i); - } - } + varList = function->locals.constData(); Object *proto = scope->engine->newObject(); proto->defineDefaultProperty(scope->engine->id_constructor, Value::fromObject(this)); @@ -376,8 +365,6 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) ScriptFunction::~ScriptFunction() { - delete[] formalParameterList; - delete[] varList; } Value ScriptFunction::call(VM::ExecutionContext *ctx) diff --git a/qv4functionobject.h b/qv4functionobject.h index dfdcf74dfc..f554456ae0 100644 --- a/qv4functionobject.h +++ b/qv4functionobject.h @@ -105,8 +105,8 @@ struct Function { const uchar *codeData; JSC::MacroAssemblerCodeRef codeRef; - QList formals; - QList locals; + QVector formals; + QVector locals; QVector generatedValues; QVector identifiers; @@ -134,8 +134,8 @@ struct Function { struct FunctionObject: Object { ExecutionContext *scope; String *name; - String **formalParameterList; - String **varList; + String * const *formalParameterList; + String * const *varList; unsigned int formalParameterCount; unsigned int varCount; diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index 9adffce1da..a446f6f522 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -428,7 +428,7 @@ QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct QStringList inheritedLocals; if (inheritContext) - for (String **i = ctx->variables(), **ei = i + ctx->variableCount(); i < ei; ++i) + for (String * const *i = ctx->variables(), * const *ei = i + ctx->variableCount(); i < ei; ++i) inheritedLocals.append(*i ? (*i)->toQString() : QString()); Codegen cg(ctx, strictMode); -- cgit v1.2.3 From 87b61295d3cfc629bb01b516ba6d86b313424d9d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 29 Jan 2013 13:23:32 +0100 Subject: Allocate execution context and local variables in one chunk This avoids a few mallocs and in addition memory leaks where the local variables or arguments array wasn't destroyed before. crypto.js seems to run without a mem leak now :) Change-Id: Icca74c5dba764fadabd7a77f233bdf5883046c86 Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 13 ++++++------- qmljs_environment.h | 4 ++++ qv4functionobject.cpp | 9 +++++---- qv4globalobject.cpp | 6 ++++-- tests/fact.2.js | 2 +- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 007928cd41..9042a435a0 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -507,20 +507,21 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha thisObject = thisObject.toObject(this); } + locals = function->varCount ? reinterpret_cast(this + 1) : 0; + if (locals) + std::fill(locals, locals + function->varCount, Value::undefinedValue()); + arguments = args; argumentCount = argc; if (function->needsActivation || argc < function->formalParameterCount){ argumentCount = qMax(argc, function->formalParameterCount); - arguments = new Value[argumentCount]; + arguments = reinterpret_cast(this + 1) + function->varCount; if (argc) std::copy(args, args + argc, arguments); if (argc < function->formalParameterCount) std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); } - locals = function->varCount ? new Value[function->varCount] : 0; - if (locals) - std::fill(locals, locals + function->varCount, Value::undefinedValue()); activation = 0; withObject = 0; @@ -539,10 +540,8 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha void ExecutionContext::leaveCallContext() { - if (!function->needsActivation) { - delete[] locals; + if (!function->needsActivation) locals = 0; - } engine->current = parent; parent = 0; diff --git a/qmljs_environment.h b/qmljs_environment.h index ec135a46cd..c21cab55ac 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -135,8 +135,12 @@ struct ExecutionContext } void mark(); + }; +/* Function *f, int argc */ +#define requiredMemoryForExecutionContect(f, argc) \ + sizeof(ExecutionContext) + sizeof(Value) * (f->varCount + qMax((uint)argc, f->formalParameterCount)) } // namespace VM diff --git a/qv4functionobject.cpp b/qv4functionobject.cpp index e4635505f3..34cdbb503d 100644 --- a/qv4functionobject.cpp +++ b/qv4functionobject.cpp @@ -131,8 +131,8 @@ Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc if (proto.isObject()) obj->prototype = proto.objectValue(); - ExecutionContext k; - ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; + uint size = requiredMemoryForExecutionContect(this, argc); + ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); ctx->initCallContext(context, Value::fromObject(obj), this, args, argc); Value result = construct(ctx); @@ -145,8 +145,9 @@ Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *args, int argc) { - ExecutionContext k; - ExecutionContext *ctx = needsActivation ? context->engine->newContext() : &k; + uint size = requiredMemoryForExecutionContect(this, argc); + ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); + ctx->initCallContext(context, thisObject, this, args, argc); if (isBuiltinFunction) { diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index a446f6f522..bdf010f7b2 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -55,6 +55,7 @@ #include #include #include +#include using namespace QQmlJS::VM; @@ -342,10 +343,11 @@ Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Va bool strict = f->isStrict || (directCall && context->strictMode); - ExecutionContext k; + uint size = requiredMemoryForExecutionContect(this, argc); + ExecutionContext *k = static_cast(alloca(size)); if (strict) { - ctx = &k; + ctx = k; ctx->initCallContext(context, context->thisObject, this, args, argc); } diff --git a/tests/fact.2.js b/tests/fact.2.js index d8f750b5a1..c0f087e4c9 100644 --- a/tests/fact.2.js +++ b/tests/fact.2.js @@ -3,6 +3,6 @@ function fact(n) { return n > 1 ? n * fact(n - 1) : 1 } -for (var i = 0; i < 1000000; i = i + 1) +for (var i = 0; i < 10000; i = i + 1) fact(12) -- cgit v1.2.3 From cac94a5a9fe203726b4232badc8602b3c8280fe2 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 29 Jan 2013 13:45:32 +0100 Subject: Remove Object::call() The method didn't make any sense. It's easier to directly cast to a FunctionObject and check whether that succeded. Change-Id: I8d76e2b93dc32d6a8fa82e66173d1d2c1e5da514 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 12 ++++++------ qv4object.cpp | 6 ------ qv4object.h | 2 -- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 469281c89d..3caf61be72 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -521,14 +521,14 @@ Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int type Object *oo = object.objectValue(); Value conv = oo->__get__(ctx, meth1); - if (Object *o = conv.asObject()) { + if (FunctionObject *o = conv.asFunctionObject()) { Value r = o->call(ctx, object, 0, 0); if (r.isPrimitive()) return r; } conv = oo->__get__(ctx, meth2); - if (Object *o = conv.asObject()) { + if (FunctionObject *o = conv.asFunctionObject()) { Value r = o->call(ctx, object, 0, 0); if (r.isPrimitive()) return r; @@ -735,7 +735,7 @@ Value __qmljs_call_activation_property(ExecutionContext *context, String *name, { Object *base; Value func = context->getPropertyAndBase(name, &base); - Object *o = func.asObject(); + FunctionObject *o = func.asFunctionObject(); if (!o) context->throwTypeError(); @@ -757,7 +757,7 @@ Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, Object *baseObject = thisObject.objectValue(); Value func = baseObject->__get__(context, name); - Object *o = func.asObject(); + FunctionObject *o = func.asFunctionObject(); if (!o) context->throwTypeError(); @@ -774,7 +774,7 @@ Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, V Object *baseObject = thisObject.objectValue(); Value func = baseObject->__get__(context, index.toString(context)); - Object *o = func.asObject(); + FunctionObject *o = func.asFunctionObject(); if (!o) context->throwTypeError(); @@ -783,7 +783,7 @@ Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, V Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc) { - Object *o = func.asObject(); + FunctionObject *o = func.asFunctionObject(); if (!o) context->throwTypeError(); return o->call(context, thisObject, args, argc); diff --git a/qv4object.cpp b/qv4object.cpp index 93475fbf7e..e848935896 100644 --- a/qv4object.cpp +++ b/qv4object.cpp @@ -639,12 +639,6 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, c } -Value Object::call(ExecutionContext *context, Value , Value *, int) -{ - context->throwTypeError(); - return Value::undefinedValue(); -} - void ArrayObject::init(ExecutionContext *context) { type = Type_ArrayObject; diff --git a/qv4object.h b/qv4object.h index 816cbd7d33..0a2ac60797 100644 --- a/qv4object.h +++ b/qv4object.h @@ -132,8 +132,6 @@ struct Object: Managed { bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc); - virtual Value call(ExecutionContext *context, Value, Value *, int); - // // helpers // -- cgit v1.2.3 From 75f2b5b706fc919a4186dc8d9571488841025793 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 29 Jan 2013 14:31:42 +0100 Subject: Remove the virtual Object::className() Change-Id: I70ecf244792c1c22b57ba7bdfed64b4b5b055e8c Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 2 +- qv4argumentsobject.h | 1 - qv4dateobject.h | 1 - qv4errorobject.cpp | 11 ++++++++ qv4errorobject.h | 18 ++++++++----- qv4functionobject.h | 1 - qv4jsonobject.cpp | 1 + qv4jsonobject.h | 1 - qv4managed.cpp | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++ qv4managed.h | 10 +++++-- qv4mathobject.cpp | 2 ++ qv4object.h | 6 ----- qv4regexpobject.h | 1 - qv4stringobject.h | 1 - 14 files changed, 108 insertions(+), 22 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 3caf61be72..682f025707 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -640,7 +640,7 @@ Value __qmljs_foreach_next_property_name(Value foreach_iterator) assert(foreach_iterator.isObject()); ForEachIteratorObject *it = static_cast(foreach_iterator.objectValue()); - assert(it->className() == QLatin1String("__ForEachIteratorObject")); + assert(it->asForeachIteratorObject()); return it->nextPropertyName(); } diff --git a/qv4argumentsobject.h b/qv4argumentsobject.h index 2785ccb72d..adbaf5db1a 100644 --- a/qv4argumentsobject.h +++ b/qv4argumentsobject.h @@ -72,7 +72,6 @@ struct ArgumentsObject: Object { ExecutionContext *context; QVector mappedArguments; ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount); - virtual QString className() { return QStringLiteral("Arguments"); } bool defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); diff --git a/qv4dateobject.h b/qv4dateobject.h index 0cc29289b2..ede4473217 100644 --- a/qv4dateobject.h +++ b/qv4dateobject.h @@ -51,7 +51,6 @@ namespace VM { struct DateObject: Object { Value value; DateObject(const Value &value): value(value) { type = Type_DateObject; } - virtual QString className() { return QStringLiteral("Date"); } }; struct DateCtor: FunctionObject diff --git a/qv4errorobject.cpp b/qv4errorobject.cpp index f10ee40fb8..9d4a067f35 100644 --- a/qv4errorobject.cpp +++ b/qv4errorobject.cpp @@ -74,6 +74,7 @@ using namespace QQmlJS::VM; ErrorObject::ErrorObject(ExecutionEngine* engine, const Value &message) + : errorType(Error) { type = Type_ErrorObject; @@ -90,6 +91,7 @@ SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *m : ErrorObject(ctx->engine, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0)) , msg(message) { + errorType = SyntaxError; prototype = ctx->engine->syntaxErrorPrototype; setNameProperty(ctx); } @@ -99,6 +101,7 @@ SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *m EvalErrorObject::EvalErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->engine, ctx->argument(0)) { + errorType = EvalError; setNameProperty(ctx); prototype = ctx->engine->evalErrorPrototype; } @@ -106,6 +109,7 @@ EvalErrorObject::EvalErrorObject(ExecutionContext *ctx) RangeErrorObject::RangeErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->engine, ctx->argument(0)) { + errorType = RangeError; setNameProperty(ctx); prototype = ctx->engine->rangeErrorPrototype; } @@ -113,6 +117,7 @@ RangeErrorObject::RangeErrorObject(ExecutionContext *ctx) RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &msg) : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) { + errorType = RangeError; setNameProperty(ctx); prototype = ctx->engine->rangeErrorPrototype; } @@ -120,6 +125,7 @@ RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &msg) ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->engine, ctx->argument(0)) { + errorType = ReferenceError; setNameProperty(ctx); prototype = ctx->engine->referenceErrorPrototype; } @@ -127,6 +133,7 @@ ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx) ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString &msg) : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) { + errorType = ReferenceError; setNameProperty(ctx); prototype = ctx->engine->referenceErrorPrototype; } @@ -134,6 +141,7 @@ ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString TypeErrorObject::TypeErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->engine, ctx->argument(0)) { + errorType = TypeError; setNameProperty(ctx); prototype = ctx->engine->typeErrorPrototype; } @@ -141,6 +149,7 @@ TypeErrorObject::TypeErrorObject(ExecutionContext *ctx) TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &msg) : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) { + errorType = TypeError; setNameProperty(ctx); prototype = ctx->engine->typeErrorPrototype; } @@ -148,6 +157,7 @@ TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &msg) URIErrorObject::URIErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->engine, ctx->argument(0)) { + errorType = URIError; setNameProperty(ctx); prototype = ctx->engine->uRIErrorPrototype; } @@ -155,6 +165,7 @@ URIErrorObject::URIErrorObject(ExecutionContext *ctx) URIErrorObject::URIErrorObject(ExecutionContext *ctx, Value msg) : ErrorObject(ctx->engine, msg) { + errorType = URIError; setNameProperty(ctx); prototype = ctx->engine->uRIErrorPrototype; } diff --git a/qv4errorobject.h b/qv4errorobject.h index 74dc62bdba..d2d2340fae 100644 --- a/qv4errorobject.h +++ b/qv4errorobject.h @@ -48,8 +48,18 @@ namespace QQmlJS { namespace VM { struct ErrorObject: Object { + enum ErrorType { + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError + }; + ErrorType errorType; + ErrorObject(ExecutionEngine* engine, const Value &message); - virtual QString className() { return QStringLiteral("Error"); } virtual struct SyntaxErrorObject *asSyntaxError() { return 0; } @@ -59,25 +69,21 @@ protected: struct EvalErrorObject: ErrorObject { EvalErrorObject(ExecutionContext *ctx); - virtual QString className() { return QStringLiteral("EvalError"); } }; struct RangeErrorObject: ErrorObject { RangeErrorObject(ExecutionContext *ctx); RangeErrorObject(ExecutionContext *ctx, const QString &msg); - virtual QString className() { return QStringLiteral("RangeError"); } }; struct ReferenceErrorObject: ErrorObject { ReferenceErrorObject(ExecutionContext *ctx); ReferenceErrorObject(ExecutionContext *ctx, const QString &msg); - virtual QString className() { return QStringLiteral("ReferenceError"); } }; struct SyntaxErrorObject: ErrorObject { SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg); ~SyntaxErrorObject() { delete msg; } - virtual QString className() { return QStringLiteral("SyntaxError"); } virtual SyntaxErrorObject *asSyntaxError() { return this; } DiagnosticMessage *message() { return msg; } @@ -89,13 +95,11 @@ private: struct TypeErrorObject: ErrorObject { TypeErrorObject(ExecutionContext *ctx); TypeErrorObject(ExecutionContext *ctx, const QString &msg); - virtual QString className() { return QStringLiteral("TypeError"); } }; struct URIErrorObject: ErrorObject { URIErrorObject(ExecutionContext *ctx); URIErrorObject(ExecutionContext *ctx, Value); - virtual QString className() { return QStringLiteral("URIError"); } }; struct ErrorCtor: FunctionObject diff --git a/qv4functionobject.h b/qv4functionobject.h index f554456ae0..b70ff1afa4 100644 --- a/qv4functionobject.h +++ b/qv4functionobject.h @@ -141,7 +141,6 @@ struct FunctionObject: Object { FunctionObject(ExecutionContext *scope); - virtual QString className() { return QStringLiteral("Function"); } virtual bool hasInstance(ExecutionContext *ctx, const Value &value); virtual Value construct(ExecutionContext *context, Value *args, int argc); diff --git a/qv4jsonobject.cpp b/qv4jsonobject.cpp index a12c4a586a..fa39476f4e 100644 --- a/qv4jsonobject.cpp +++ b/qv4jsonobject.cpp @@ -865,6 +865,7 @@ QString Stringify::JA(ArrayObject *a) JsonObject::JsonObject(ExecutionContext *context) : Object() { + type = Type_JSONObject; prototype = context->engine->objectPrototype; defineDefaultProperty(context, QStringLiteral("parse"), method_parse, 2); diff --git a/qv4jsonobject.h b/qv4jsonobject.h index 6626471606..7c1df7c40b 100644 --- a/qv4jsonobject.h +++ b/qv4jsonobject.h @@ -48,7 +48,6 @@ namespace VM { struct JsonObject : Object { JsonObject(ExecutionContext *context); - virtual QString className() { return QStringLiteral("JSON"); } static Value method_parse(ExecutionContext *ctx); static Value method_stringify(ExecutionContext *ctx); diff --git a/qv4managed.cpp b/qv4managed.cpp index a48b9896eb..8156878508 100644 --- a/qv4managed.cpp +++ b/qv4managed.cpp @@ -41,6 +41,7 @@ #include "qv4managed.h" #include "qv4mm.h" +#include "qv4errorobject.h" using namespace QQmlJS::VM; @@ -65,3 +66,76 @@ void Managed::operator delete(void *ptr) Managed *m = static_cast(ptr); m->~Managed(); } + + +QString Managed::className() const +{ + const char *s = 0; + switch (Type(type)) { + case Type_Invalid: + case Type_String: + return QString(); + case Type_Object: + s = "Object"; + break; + case Type_ArrayObject: + s = "Array"; + break; + case Type_FunctionObject: + s = "Function"; + break; + case Type_BooleanObject: + s = "Boolean"; + break; + case Type_NumberObject: + s = "Number"; + break; + case Type_StringObject: + s = "String"; + break; + case Type_DateObject: + s = "Date"; + break; + case Type_RegExpObject: + s = "RegExp"; + break; + case Type_ErrorObject: + switch (static_cast(this)->errorType) { + case ErrorObject::Error: + s = "Error"; + break; + case ErrorObject::EvalError: + s = "EvalError"; + break; + case ErrorObject::RangeError: + s = "RangeError"; + break; + case ErrorObject::ReferenceError: + s = "ReferenceError"; + break; + case ErrorObject::SyntaxError: + s = "SyntaxError"; + break; + case ErrorObject::TypeError: + s = "TypeError"; + break; + case ErrorObject::URIError: + s = "URIError"; + break; + } + break; + case Type_ArgumentsObject: + s = "Arguments"; + break; + case Type_JSONObject: + s = "JSON"; + break; + case Type_MathObject: + s = "Math"; + break; + case Type_ForeachIteratorObject: + s = "__ForeachIterator"; + break; + } + return QString::fromLatin1(s); +} diff --git a/qv4managed.h b/qv4managed.h index ab75826af6..7b6768d166 100644 --- a/qv4managed.h +++ b/qv4managed.h @@ -66,6 +66,7 @@ struct FunctionObject; struct RegExpObject; struct ErrorObject; struct ArgumentsObject; +struct JSONObject; struct ForeachIteratorObject; struct Managed @@ -116,6 +117,8 @@ public: Type_RegExpObject, Type_ErrorObject, Type_ArgumentsObject, + Type_JSONObject, + Type_MathObject, Type_ForeachIteratorObject }; @@ -130,11 +133,14 @@ public: RegExpObject *asRegExpObject() { return type == Type_RegExpObject ? reinterpret_cast(this) : 0; } ErrorObject *asErrorObject() { return type == Type_ErrorObject ? reinterpret_cast(this) : 0; } ArgumentsObject *asArgumentsObject() { return type == Type_ArgumentsObject ? reinterpret_cast(this) : 0; } + JSONObject *asJSONObject() { return type == Type_JSONObject ? reinterpret_cast(this) : 0; } ForeachIteratorObject *asForeachIteratorObject() { return type == Type_ForeachIteratorObject ? reinterpret_cast(this) : 0; } bool isArrayObject() const { return type == Type_ArrayObject; } bool isStringObject() const { return type == Type_StringObject; } + QString className() const; + protected: virtual void markObjects() {} @@ -149,8 +155,8 @@ protected: quintptr needsActivation : 1; // used by FunctionObject quintptr usesArgumentsObject : 1; // used by FunctionObject quintptr strictMode : 1; // used by FunctionObject - quintptr type : 4; - quintptr unused : 20; + quintptr type : 8; + quintptr unused : 16; mutable quintptr stringHash : 32; }; }; diff --git a/qv4mathobject.cpp b/qv4mathobject.cpp index 2eefb51616..8076e1623b 100644 --- a/qv4mathobject.cpp +++ b/qv4mathobject.cpp @@ -51,6 +51,8 @@ static const double qt_PI = 2.0 * ::asin(1.0); MathObject::MathObject(ExecutionContext *ctx) { + type = Type_MathObject; + defineReadonlyProperty(ctx->engine, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); defineReadonlyProperty(ctx->engine, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); defineReadonlyProperty(ctx->engine, QStringLiteral("LN10"), Value::fromDouble(::log(10.0))); diff --git a/qv4object.h b/qv4object.h index 0a2ac60797..362cd15653 100644 --- a/qv4object.h +++ b/qv4object.h @@ -110,8 +110,6 @@ struct Object: Managed { virtual ~Object(); - virtual QString className() { return QStringLiteral("Object"); } - PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name); @@ -162,7 +160,6 @@ struct ForEachIteratorObject: Object { ObjectIterator it; ForEachIteratorObject(ExecutionContext *ctx, Object *o) : it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) { type = Type_ForeachIteratorObject; } - virtual QString className() { return QStringLiteral("__ForEachIteratorObject"); } Value nextPropertyName() { return it.nextPropertyNameAsString(); } @@ -173,20 +170,17 @@ protected: struct BooleanObject: Object { Value value; BooleanObject(const Value &value): value(value) { type = Type_BooleanObject; } - virtual QString className() { return QStringLiteral("Boolean"); } }; struct NumberObject: Object { Value value; NumberObject(const Value &value): value(value) { type = Type_NumberObject; } - virtual QString className() { return QStringLiteral("Number"); } }; struct ArrayObject: Object { ArrayObject(ExecutionContext *ctx) { init(ctx); } ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setLengthUnchecked(array.length()); } void init(ExecutionContext *context); - virtual QString className() { return QStringLiteral("Array"); } }; diff --git a/qv4regexpobject.h b/qv4regexpobject.h index 9655374572..50241f2daf 100644 --- a/qv4regexpobject.h +++ b/qv4regexpobject.h @@ -70,7 +70,6 @@ struct RegExpObject: Object { PropertyDescriptor *lastIndexProperty; bool global; RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global); - virtual QString className() { return QStringLiteral("RegExp"); } }; diff --git a/qv4stringobject.h b/qv4stringobject.h index d131e942ab..7e62e299b9 100644 --- a/qv4stringobject.h +++ b/qv4stringobject.h @@ -52,7 +52,6 @@ struct StringObject: Object { Value value; PropertyDescriptor tmpProperty; StringObject(ExecutionContext *ctx, const Value &value); - virtual QString className() { return QStringLiteral("String"); } PropertyDescriptor *getIndex(ExecutionContext *ctx, uint index); -- cgit v1.2.3 From 475731534f3a54815c1cd2d1d0462e913c35e06b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 29 Jan 2013 21:23:19 +0100 Subject: Add a faster code path for calling builtin methods Builtin methods usually don't need a Context of their own, so we can fully avoid the overhead of creating and destroying it. Implemented the support for making builtin methods context less and verified it with some of the methods in String.prototype. Change-Id: I187b46ba684183b4fba49ae70e3139d32ef585e0 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 6 +++ qmljs_engine.h | 1 + qv4functionobject.cpp | 17 ++++++- qv4functionobject.h | 14 +++++- qv4object.cpp | 9 ++++ qv4object.h | 1 + qv4stringobject.cpp | 126 +++++++++++++++++++++++++++----------------------- qv4stringobject.h | 19 ++++---- 8 files changed, 121 insertions(+), 72 deletions(-) diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index e688233677..0b1b14b13e 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -242,6 +242,12 @@ Function *ExecutionEngine::newFunction(const QString &name) } FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) +{ + BuiltinFunctionOld *f = new (memoryManager) BuiltinFunctionOld(scope, name, code); + return f; +} + +FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)) { BuiltinFunction *f = new (memoryManager) BuiltinFunction(scope, name, code); return f; diff --git a/qmljs_engine.h b/qmljs_engine.h index 8945bd3844..01b1c511f6 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -181,6 +181,7 @@ struct ExecutionEngine VM::Function *newFunction(const QString &name); FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); + FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)); FunctionObject *newScriptFunction(ExecutionContext *scope, VM::Function *function); BoundFunction *newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); diff --git a/qv4functionobject.cpp b/qv4functionobject.cpp index 34cdbb503d..f5e37def0c 100644 --- a/qv4functionobject.cpp +++ b/qv4functionobject.cpp @@ -381,7 +381,21 @@ void ScriptFunction::markObjects() } -BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) +BuiltinFunctionOld::BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) + : FunctionObject(scope) + , code(code) +{ + this->name = name; + isBuiltinFunction = true; +} + +Value BuiltinFunctionOld::construct(ExecutionContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)) : FunctionObject(scope) , code(code) { @@ -395,6 +409,7 @@ Value BuiltinFunction::construct(ExecutionContext *ctx) return Value::undefinedValue(); } + BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) : FunctionObject(scope) , target(target) diff --git a/qv4functionobject.h b/qv4functionobject.h index b70ff1afa4..822cee9491 100644 --- a/qv4functionobject.h +++ b/qv4functionobject.h @@ -174,14 +174,24 @@ struct FunctionPrototype: FunctionObject static Value method_bind(ExecutionContext *ctx); }; -struct BuiltinFunction: FunctionObject { +struct BuiltinFunctionOld: FunctionObject { Value (*code)(ExecutionContext *); - BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); + BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); virtual Value call(ExecutionContext *ctx) { return code(ctx); } virtual Value construct(ExecutionContext *ctx); }; +struct BuiltinFunction: FunctionObject { + Value (*code)(ExecutionContext *parentContext, Value thisObject, Value *args, int argc); + + BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)); + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc) { + return code(context, thisObject, args, argc); + } + virtual Value construct(ExecutionContext *ctx); +}; + struct ScriptFunction: FunctionObject { VM::Function *function; diff --git a/qv4object.cpp b/qv4object.cpp index e848935896..dadf8479c2 100644 --- a/qv4object.cpp +++ b/qv4object.cpp @@ -151,6 +151,15 @@ void Object::defineDefaultProperty(ExecutionContext *context, const QString &nam defineDefaultProperty(s, Value::fromObject(function)); } +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *, Value, Value *, int), int argumentCount) +{ + Q_UNUSED(argumentCount); + String *s = context->engine->identifier(name); + FunctionObject* function = context->engine->newBuiltinFunction(context, s, code); + function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); + defineDefaultProperty(s, Value::fromObject(function)); +} + void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value) { defineReadonlyProperty(engine->identifier(name), value); diff --git a/qv4object.h b/qv4object.h index 362cd15653..c50ffe2755 100644 --- a/qv4object.h +++ b/qv4object.h @@ -146,6 +146,7 @@ struct Object: Managed { void defineDefaultProperty(String *name, Value value); void defineDefaultProperty(ExecutionContext *context, const QString &name, Value value); void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int count = 0); + void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *, Value, Value *, int), int argumentCount = 0); /* Fixed: Writable: false, Enumerable: false, Configurable: false */ void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value); void defineReadonlyProperty(String *name, Value value); diff --git a/qv4stringobject.cpp b/qv4stringobject.cpp index 63f374b013..ced905d564 100644 --- a/qv4stringobject.cpp +++ b/qv4stringobject.cpp @@ -140,7 +140,7 @@ void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_toString); // valueOf and toString are identical defineDefaultProperty(ctx, QStringLiteral("charAt"), method_charAt, 1); defineDefaultProperty(ctx, QStringLiteral("charCodeAt"), method_charCodeAt, 1); defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); @@ -161,7 +161,7 @@ void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) defineDefaultProperty(ctx, QStringLiteral("trim"), method_trim); } -QString StringPrototype::getThisString(ExecutionContext *ctx) +static QString getThisString(ExecutionContext *ctx) { String* str = 0; Value thisObject = ctx->thisObject; @@ -174,77 +174,86 @@ QString StringPrototype::getThisString(ExecutionContext *ctx) return str->toQString(); } -Value StringPrototype::method_toString(ExecutionContext *ctx) +static QString getThisString(ExecutionContext *parentCtx, Value thisObject) { - StringObject *o = ctx->thisObject.asStringObject(); - if (!o) - ctx->throwTypeError(); - return o->value; + if (thisObject.isString()) + return thisObject.stringValue()->toQString(); + + String* str = 0; + if (StringObject *thisString = thisObject.asStringObject()) + str = thisString->value.stringValue(); + else if (thisObject.isUndefined() || thisObject.isNull()) + parentCtx->throwTypeError(); + else + str = thisObject.toString(parentCtx); + return str->toQString(); } -Value StringPrototype::method_valueOf(ExecutionContext *ctx) +Value StringPrototype::method_toString(ExecutionContext *parentCtx, Value thisObject, Value *, int) { - StringObject *o = ctx->thisObject.asStringObject(); + if (thisObject.isString()) + return thisObject; + + StringObject *o = thisObject.asStringObject(); if (!o) - ctx->throwTypeError(); + parentCtx->throwTypeError(); return o->value; } -Value StringPrototype::method_charAt(ExecutionContext *ctx) +Value StringPrototype::method_charAt(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - const QString str = getThisString(ctx); + const QString str = getThisString(parentCtx, thisObject); int pos = 0; - if (ctx->argumentCount > 0) - pos = (int) ctx->argument(0).toInteger(ctx); + if (argc > 0) + pos = (int) argv[0].toInteger(parentCtx); QString result; if (pos >= 0 && pos < str.length()) result += str.at(pos); - return Value::fromString(ctx, result); + return Value::fromString(parentCtx, result); } -Value StringPrototype::method_charCodeAt(ExecutionContext *ctx) +Value StringPrototype::method_charCodeAt(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - const QString str = getThisString(ctx); + const QString str = getThisString(parentCtx, thisObject); int pos = 0; - if (ctx->argumentCount > 0) - pos = (int) ctx->argument(0).toInteger(ctx); + if (argc > 0) + pos = (int) argv[0].toInteger(parentCtx); - double result = qSNaN(); if (pos >= 0 && pos < str.length()) - result = str.at(pos).unicode(); + return Value::fromInt32(str.at(pos).unicode()); - return Value::fromDouble(result); + return Value::fromDouble(qSNaN()); } -Value StringPrototype::method_concat(ExecutionContext *ctx) +Value StringPrototype::method_concat(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - QString value = getThisString(ctx); + QString value = getThisString(parentCtx, thisObject); - for (unsigned i = 0; i < ctx->argumentCount; ++i) { - Value v = __qmljs_to_string(ctx->argument(i), ctx); + for (unsigned i = 0; i < argc; ++i) { + Value v = __qmljs_to_string(argv[i], parentCtx); assert(v.isString()); value += v.stringValue()->toQString(); } - return Value::fromString(ctx, value); + return Value::fromString(parentCtx, value); } -Value StringPrototype::method_indexOf(ExecutionContext *ctx) +Value StringPrototype::method_indexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - QString value = getThisString(ctx); + QString value = getThisString(parentCtx, thisObject); QString searchString; - if (ctx->argumentCount) - searchString = ctx->argument(0).toString(ctx)->toQString(); + if (argc) + searchString = argv[0].toString(parentCtx)->toQString(); int pos = 0; - if (ctx->argumentCount > 1) - pos = (int) ctx->argument(1).toInteger(ctx); + if (argc > 1) + pos = (int) argv[1].toInteger(parentCtx); int index = -1; if (! value.isEmpty()) @@ -253,18 +262,18 @@ Value StringPrototype::method_indexOf(ExecutionContext *ctx) return Value::fromDouble(index); } -Value StringPrototype::method_lastIndexOf(ExecutionContext *ctx) +Value StringPrototype::method_lastIndexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - const QString value = getThisString(ctx); + const QString value = getThisString(parentCtx, thisObject); QString searchString; - if (ctx->argumentCount) { - Value v = __qmljs_to_string(ctx->argument(0), ctx); + if (argc) { + Value v = __qmljs_to_string(argv[0], parentCtx); searchString = v.stringValue()->toQString(); } - Value posArg = ctx->argument(1); - double position = __qmljs_to_number(posArg, ctx); + Value posArg = argc > 1 ? argv[1] : Value::undefinedValue(); + double position = __qmljs_to_number(posArg, parentCtx); if (std::isnan(position)) position = +qInf(); else @@ -277,56 +286,57 @@ Value StringPrototype::method_lastIndexOf(ExecutionContext *ctx) return Value::fromDouble(index); } -Value StringPrototype::method_localeCompare(ExecutionContext *ctx) +Value StringPrototype::method_localeCompare(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - const QString value = getThisString(ctx); - const QString that = ctx->argument(0).toString(ctx)->toQString(); + const QString value = getThisString(parentCtx, thisObject); + const QString that = (argc ? argv[0] : Value::undefinedValue()).toString(parentCtx)->toQString(); return Value::fromDouble(QString::localeAwareCompare(value, that)); } -Value StringPrototype::method_match(ExecutionContext *ctx) +Value StringPrototype::method_match(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - if (ctx->thisObject.isUndefined() || ctx->thisObject.isNull()) - __qmljs_throw_type_error(ctx); + if (thisObject.isUndefined() || thisObject.isNull()) + __qmljs_throw_type_error(parentCtx); - String *s = ctx->thisObject.toString(ctx); + String *s = thisObject.toString(parentCtx); - Value regexp = ctx->argument(0); + Value regexp = argc ? argv[0] : Value::undefinedValue(); RegExpObject *rx = regexp.asRegExpObject(); if (!rx) - rx = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ®exp, 1).asRegExpObject(); + rx = parentCtx->engine->regExpCtor.asFunctionObject()->construct(parentCtx, ®exp, 1).asRegExpObject(); if (!rx) // ### CHECK - __qmljs_throw_type_error(ctx); + __qmljs_throw_type_error(parentCtx); bool global = rx->global; - FunctionObject *exec = ctx->engine->regExpPrototype->__get__(ctx, ctx->engine->identifier(QStringLiteral("exec")), 0).asFunctionObject(); + // ### use the standard builtin function, not the one that might be redefined in the proto + FunctionObject *exec = parentCtx->engine->regExpPrototype->__get__(parentCtx, parentCtx->engine->identifier(QStringLiteral("exec")), 0).asFunctionObject(); Value arg = Value::fromString(s); if (!global) - return exec->call(ctx, Value::fromObject(rx), &arg, 1); + return exec->call(parentCtx, Value::fromObject(rx), &arg, 1); - String *lastIndex = ctx->engine->identifier(QStringLiteral("lastIndex")); - rx->__put__(ctx, lastIndex, Value::fromDouble(0.)); - ArrayObject *a = ctx->engine->newArrayObject(ctx); + String *lastIndex = parentCtx->engine->identifier(QStringLiteral("lastIndex")); + rx->__put__(parentCtx, lastIndex, Value::fromInt32(0)); + ArrayObject *a = parentCtx->engine->newArrayObject(parentCtx); double previousLastIndex = 0; uint n = 0; while (1) { - Value result = exec->call(ctx, Value::fromObject(rx), &arg, 1); + Value result = exec->call(parentCtx, Value::fromObject(rx), &arg, 1); if (result.isNull()) break; assert(result.isObject()); - double thisIndex = rx->__get__(ctx, lastIndex, 0).toInteger(ctx); + double thisIndex = rx->__get__(parentCtx, lastIndex, 0).toInteger(parentCtx); if (previousLastIndex == thisIndex) { previousLastIndex = thisIndex + 1; - rx->__put__(ctx, lastIndex, Value::fromDouble(previousLastIndex)); + rx->__put__(parentCtx, lastIndex, Value::fromDouble(previousLastIndex)); } else { previousLastIndex = thisIndex; } - Value matchStr = result.objectValue()->__get__(ctx, (uint)0, (bool *)0); + Value matchStr = result.objectValue()->__get__(parentCtx, (uint)0, (bool *)0); a->array.set(n, matchStr); ++n; } diff --git a/qv4stringobject.h b/qv4stringobject.h index 7e62e299b9..f799c765dc 100644 --- a/qv4stringobject.h +++ b/qv4stringobject.h @@ -72,17 +72,14 @@ struct StringPrototype: StringObject StringPrototype(ExecutionContext *ctx): StringObject(ctx, Value::fromString(ctx, QString())) {} void init(ExecutionContext *ctx, const Value &ctor); - static QString getThisString(ExecutionContext *ctx); - - static Value method_toString(ExecutionContext *ctx); - static Value method_valueOf(ExecutionContext *ctx); - static Value method_charAt(ExecutionContext *ctx); - static Value method_charCodeAt(ExecutionContext *ctx); - static Value method_concat(ExecutionContext *ctx); - static Value method_indexOf(ExecutionContext *ctx); - static Value method_lastIndexOf(ExecutionContext *ctx); - static Value method_localeCompare(ExecutionContext *ctx); - static Value method_match(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_charAt(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_charCodeAt(ExecutionContext *, Value thisObject, Value *argv, int argc); + static Value method_concat(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_indexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_lastIndexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_localeCompare(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_match(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); static Value method_replace(ExecutionContext *ctx); static Value method_search(ExecutionContext *ctx); static Value method_slice(ExecutionContext *ctx); -- cgit v1.2.3 From 27d9ded1bed732514fdf607ab0d422dd69aade99 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 29 Jan 2013 22:07:48 +0100 Subject: Optimise callProperty for Strings. Don't convert the String to a StringOject, just to call a method on it's prototype. The result would in any case be the same. Change-Id: I74cea392b20b6c5642d010287ebf6e27c91eea83 Reviewed-by: Simon Hausmann --- qmljs_runtime.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 682f025707..9e50217554 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -45,6 +45,7 @@ #include "qv4ir_p.h" #include "qv4objectproto.h" #include "qv4globalobject.h" +#include "qv4stringobject.h" #include "private/qlocale_tools_p.h" #include @@ -747,14 +748,18 @@ Value __qmljs_call_activation_property(ExecutionContext *context, String *name, return o->call(context, thisObject, args, argc); } -Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, Value *args, int argc) +Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String *name, Value *args, int argc) { - Value thisObject = that; - if (!thisObject.isObject()) - thisObject = __qmljs_to_object(thisObject, context); + Object *baseObject; + if (thisObject.isString()) { + baseObject = context->engine->stringPrototype; + } else { + if (!thisObject.isObject()) + thisObject = __qmljs_to_object(thisObject, context); - assert(thisObject.isObject()); - Object *baseObject = thisObject.objectValue(); + assert(thisObject.isObject()); + baseObject = thisObject.objectValue(); + } Value func = baseObject->__get__(context, name); FunctionObject *o = func.asFunctionObject(); -- cgit v1.2.3 From 0cc9cb0545cc06f54bf608faa7e2d020f7fa8ad6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 29 Jan 2013 22:08:35 +0100 Subject: Use faster calling path for more builtin methods This speeds up some test cases I have by a factor of 2. Change-Id: Ief9e2845c974d701fa032937cafced323279e9ec Reviewed-by: Simon Hausmann --- qv4globalobject.cpp | 40 ++++++++++++++++++++-------------------- qv4globalobject.h | 8 ++++---- qv4mathobject.cpp | 14 +++++++------- qv4mathobject.h | 2 +- qv4stringobject.cpp | 43 ++++++++++++++++++++++--------------------- qv4stringobject.h | 6 +++--- 6 files changed, 57 insertions(+), 56 deletions(-) diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index bdf010f7b2..d538cfd9c3 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -592,63 +592,63 @@ Value GlobalFunctions::method_isFinite(ExecutionContext *context) } /// decodeURI [15.1.3.1] -Value GlobalFunctions::method_decodeURI(ExecutionContext *context) +Value GlobalFunctions::method_decodeURI(ExecutionContext *parentCtx, Value, Value *argv, int argc) { - if (context->argumentCount == 0) + if (argc == 0) return Value::undefinedValue(); - QString uriString = context->argument(0).toString(context)->toQString(); + QString uriString = argv[0].toString(parentCtx)->toQString(); bool ok; QString out = decode(uriString, QString::fromUtf8(uriReserved) + QString::fromUtf8("#"), &ok); if (!ok) - context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); - return Value::fromString(context, out); + return Value::fromString(parentCtx, out); } /// decodeURIComponent [15.1.3.2] -Value GlobalFunctions::method_decodeURIComponent(ExecutionContext *context) +Value GlobalFunctions::method_decodeURIComponent(ExecutionContext *parentCtx, Value, Value *argv, int argc) { - if (context->argumentCount == 0) + if (argc == 0) return Value::undefinedValue(); - QString uriString = context->argument(0).toString(context)->toQString(); + QString uriString = argv[0].toString(parentCtx)->toQString(); bool ok; QString out = decode(uriString, QString(), &ok); if (!ok) - context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); - return Value::fromString(context, out); + return Value::fromString(parentCtx, out); } /// encodeURI [15.1.3.3] -Value GlobalFunctions::method_encodeURI(ExecutionContext *context) +Value GlobalFunctions::method_encodeURI(ExecutionContext *parentCtx, Value, Value *argv, int argc) { - if (context->argumentCount == 0) + if (argc == 0) return Value::undefinedValue(); - QString uriString = context->argument(0).toString(context)->toQString(); + QString uriString = argv[0].toString(parentCtx)->toQString(); bool ok; QString out = encode(uriString, QLatin1String(uriReserved) + QLatin1String(uriUnescaped) + QString::fromUtf8("#"), &ok); if (!ok) - context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); - return Value::fromString(context, out); + return Value::fromString(parentCtx, out); } /// encodeURIComponent [15.1.3.4] -Value GlobalFunctions::method_encodeURIComponent(ExecutionContext *context) +Value GlobalFunctions::method_encodeURIComponent(ExecutionContext *parentCtx, Value, Value *argv, int argc) { - if (context->argumentCount == 0) + if (argc == 0) return Value::undefinedValue(); - QString uriString = context->argument(0).toString(context)->toQString(); + QString uriString = argv[0].toString(parentCtx)->toQString(); bool ok; QString out = encode(uriString, QString(uriUnescaped), &ok); if (!ok) - context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); - return Value::fromString(context, out); + return Value::fromString(parentCtx, out); } Value GlobalFunctions::method_escape(ExecutionContext *context) diff --git a/qv4globalobject.h b/qv4globalobject.h index 1ad76cf0a4..fecad46daa 100644 --- a/qv4globalobject.h +++ b/qv4globalobject.h @@ -69,10 +69,10 @@ struct GlobalFunctions static Value method_parseFloat(ExecutionContext *context); static Value method_isNaN(ExecutionContext *context); static Value method_isFinite(ExecutionContext *context); - static Value method_decodeURI(ExecutionContext *context); - static Value method_decodeURIComponent(ExecutionContext *context); - static Value method_encodeURI(ExecutionContext *context); - static Value method_encodeURIComponent(ExecutionContext *context); + static Value method_decodeURI(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_decodeURIComponent(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_encodeURI(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_encodeURIComponent(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); static Value method_escape(ExecutionContext *context); static Value method_unescape(ExecutionContext *context); }; diff --git a/qv4mathobject.cpp b/qv4mathobject.cpp index 8076e1623b..e40faba861 100644 --- a/qv4mathobject.cpp +++ b/qv4mathobject.cpp @@ -214,10 +214,10 @@ Value MathObject::method_min(ExecutionContext *ctx) return Value::fromDouble(mx); } -Value MathObject::method_pow(ExecutionContext *ctx) +Value MathObject::method_pow(ExecutionContext *parentCtx, Value, Value *argv, int argc) { - double x = ctx->argument(0).toNumber(ctx); - double y = ctx->argument(1).toNumber(ctx); + double x = argc > 0 ? argv[0].toNumber(parentCtx) : qSNaN(); + double y = argc > 1 ? argv[1].toNumber(parentCtx) : qSNaN(); if (std::isnan(y)) return Value::fromDouble(qSNaN()); @@ -246,14 +246,14 @@ Value MathObject::method_pow(ExecutionContext *ctx) else if (qIsInf(x) && copySign(1.0, x) == -1.0) { if (y > 0) { if (::fmod(y, 2.0) == 1.0) - return Value::number(ctx, -qInf()); + return Value::fromDouble(-qInf()); else - return Value::number(ctx, qInf()); + return Value::fromDouble(qInf()); } else if (y < 0) { if (::fmod(-y, 2.0) == 1.0) - return Value::number(ctx, copySign(0, -1.0)); + return Value::fromDouble(copySign(0, -1.0)); else - return Value::number(ctx, 0); + return Value::fromDouble(0); } } #endif diff --git a/qv4mathobject.h b/qv4mathobject.h index c2b26efce1..c8428d2942 100644 --- a/qv4mathobject.h +++ b/qv4mathobject.h @@ -64,7 +64,7 @@ struct MathObject: Object static Value method_log(ExecutionContext *ctx); static Value method_max(ExecutionContext *ctx); static Value method_min(ExecutionContext *ctx); - static Value method_pow(ExecutionContext *ctx); + static Value method_pow(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); static Value method_random(ExecutionContext *ctx); static Value method_round(ExecutionContext *ctx); static Value method_sin(ExecutionContext *ctx); diff --git a/qv4stringobject.cpp b/qv4stringobject.cpp index ced905d564..81968cc55a 100644 --- a/qv4stringobject.cpp +++ b/qv4stringobject.cpp @@ -234,7 +234,7 @@ Value StringPrototype::method_concat(ExecutionContext *parentCtx, Value thisObje { QString value = getThisString(parentCtx, thisObject); - for (unsigned i = 0; i < argc; ++i) { + for (int i = 0; i < argc; ++i) { Value v = __qmljs_to_string(argv[i], parentCtx); assert(v.isString()); value += v.stringValue()->toQString(); @@ -607,17 +607,17 @@ Value StringPrototype::method_split(ExecutionContext *ctx) return result; } -Value StringPrototype::method_substr(ExecutionContext *ctx) +Value StringPrototype::method_substr(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - const QString value = getThisString(ctx); + const QString value = getThisString(parentCtx, thisObject); double start = 0; - if (ctx->argumentCount > 0) - start = ctx->argument(0).toInteger(ctx); + if (argc > 0) + start = argv[0].toInteger(parentCtx); double length = +qInf(); - if (ctx->argumentCount > 1) - length = ctx->argument(1).toInteger(ctx); + if (argc > 1) + length = argv[1].toInteger(parentCtx); double count = value.length(); if (start < 0) @@ -627,23 +627,23 @@ Value StringPrototype::method_substr(ExecutionContext *ctx) qint32 x = Value::toInt32(start); qint32 y = Value::toInt32(length); - return Value::fromString(ctx, value.mid(x, y)); + return Value::fromString(parentCtx, value.mid(x, y)); } -Value StringPrototype::method_substring(ExecutionContext *ctx) +Value StringPrototype::method_substring(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - QString value = getThisString(ctx); + QString value = getThisString(parentCtx, thisObject); int length = value.length(); double start = 0; double end = length; - if (ctx->argumentCount > 0) - start = ctx->argument(0).toInteger(ctx); + if (argc > 0) + start = argv[0].toInteger(parentCtx); - Value endValue = ctx->argument(1); + Value endValue = argc > 1 ? argv[1] : Value::undefinedValue(); if (!endValue.isUndefined()) - end = endValue.toInteger(ctx); + end = endValue.toInteger(parentCtx); if (std::isnan(start) || start < 0) start = 0; @@ -665,7 +665,7 @@ Value StringPrototype::method_substring(ExecutionContext *ctx) qint32 x = (int)start; qint32 y = (int)(end - start); - return Value::fromString(ctx, value.mid(x, y)); + return Value::fromString(parentCtx, value.mid(x, y)); } Value StringPrototype::method_toLowerCase(ExecutionContext *ctx) @@ -690,14 +690,15 @@ Value StringPrototype::method_toLocaleUpperCase(ExecutionContext *ctx) return method_toUpperCase(ctx); } -Value StringPrototype::method_fromCharCode(ExecutionContext *ctx) +Value StringPrototype::method_fromCharCode(ExecutionContext *parentCtx, Value, Value *argv, int argc) { - QString str; - for (unsigned i = 0; i < ctx->argumentCount; ++i) { - QChar c(ctx->argument(i).toUInt16(ctx)); - str += c; + QString str(argc, Qt::Uninitialized); + QChar *ch = str.data(); + for (int i = 0; i < argc; ++i) { + *ch = QChar(argv[i].toUInt16(parentCtx)); + ++ch; } - return Value::fromString(ctx, str); + return Value::fromString(parentCtx, str); } Value StringPrototype::method_trim(ExecutionContext *ctx) diff --git a/qv4stringobject.h b/qv4stringobject.h index f799c765dc..1becd97a46 100644 --- a/qv4stringobject.h +++ b/qv4stringobject.h @@ -84,13 +84,13 @@ struct StringPrototype: StringObject static Value method_search(ExecutionContext *ctx); static Value method_slice(ExecutionContext *ctx); static Value method_split(ExecutionContext *ctx); - static Value method_substr(ExecutionContext *ctx); - static Value method_substring(ExecutionContext *ctx); + static Value method_substr(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_substring(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); static Value method_toLowerCase(ExecutionContext *ctx); static Value method_toLocaleLowerCase(ExecutionContext *ctx); static Value method_toUpperCase(ExecutionContext *ctx); static Value method_toLocaleUpperCase(ExecutionContext *ctx); - static Value method_fromCharCode(ExecutionContext *ctx); + static Value method_fromCharCode(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); static Value method_trim(ExecutionContext *ctx); }; -- cgit v1.2.3 From 4f239d8fd1b5c1155b0f40e3e10abf915e3d9058 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 08:58:27 +0100 Subject: Allocate larger chunks if we use lots of memory This avoids excessive GC activity on test cases that allocate lots of memory. v4 now runs some of the test cases that were too slow to run before. Change-Id: Id668b03799b086445ec0fb48d577a5844f962de3 Reviewed-by: Erik Verbruggen --- qv4mm.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/qv4mm.cpp b/qv4mm.cpp index 3e5f7c8d64..c77ec1eaba 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -58,6 +58,7 @@ struct MemoryManager::Data enum { MaxItemSize = 256 }; Managed *smallItems[MaxItemSize/16]; + uint nChunks[MaxItemSize/16]; struct Chunk { PageAllocation memory; int chunkSize; @@ -77,6 +78,7 @@ struct MemoryManager::Data , engine(0) { memset(smallItems, 0, sizeof(smallItems)); + memset(nChunks, 0, sizeof(nChunks)); scribble = qgetenv("MM_NO_SCRIBBLE").isEmpty(); aggressiveGC = !qgetenv("MM_AGGRESSIVE_GC").isEmpty(); } @@ -133,7 +135,11 @@ Managed *MemoryManager::alloc(std::size_t size) // no free item available, allocate a new chunk { - std::size_t allocSize = std::max(size, CHUNK_SIZE); + // allocate larger chunks at a time to avoid excessive GC, but cap at 64M chunks + uint shift = ++m_d->nChunks[pos]; + if (shift > 10) + shift = 10; + std::size_t allocSize = std::max(size, CHUNK_SIZE*(1 << shift)); allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); Data::Chunk allocation; allocation.memory = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); -- cgit v1.2.3 From 0781ecb087b027cccc1c44de1a1c7520cc89e2d2 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 09:11:18 +0100 Subject: Fix a bug with Unicode surrogates in decodeURI Change-Id: Id8348c77ad4d5429c4b4116b8ef0dc94e130c4e3 Reviewed-by: Simon Hausmann --- qv4globalobject.cpp | 2 +- tests/TestExpectations | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp index d538cfd9c3..74cd32d88a 100644 --- a/qv4globalobject.cpp +++ b/qv4globalobject.cpp @@ -283,8 +283,8 @@ static QString decode(const QString &input, const QString &reservedSet, bool *ok ushort l = ushort(((uc - 0x10000) & 0x3FF) + 0xDC00); ushort h = ushort((((uc - 0x10000) >> 10) & 0x3FF) + 0xD800); - output.append(QChar(l)); output.append(QChar(h)); + output.append(QChar(l)); } } else { QChar z(b); diff --git a/tests/TestExpectations b/tests/TestExpectations index 265d083435..39ace8bd60 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -1,7 +1,3 @@ -# Skipped because of slow string concatenation, test takes a looong time to execute. -S15.1.3.1_A2.5_T1 -S15.1.3.2_A2.5_T1 - # wrong tests # uses octal number 15.2.3.6-2-17-1 failing @@ -57,4 +53,4 @@ Sbp_A4_T2 failing S12.4_A1 failing S15.2.4.4_A14 failing # Try/catch scoping issue -S12.14_A4 failing \ No newline at end of file +S12.14_A4 failing -- cgit v1.2.3 From 74fba4d8069c946c1ba12b9ac4d4026aaf14118b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Tue, 29 Jan 2013 14:20:50 +0100 Subject: Say hello to QtV4 module. Change-Id: I507cd5707b7d7223a0d901cf939896fb2649b684 Reviewed-by: Lars Knoll --- .qmake.conf | 1 + 3rdparty/double-conversion/README | 6 - 3rdparty/double-conversion/bignum-dtoa.cc | 640 -- 3rdparty/double-conversion/bignum-dtoa.h | 84 - 3rdparty/double-conversion/bignum.cc | 764 -- 3rdparty/double-conversion/bignum.h | 145 - 3rdparty/double-conversion/cached-powers.cc | 175 - 3rdparty/double-conversion/cached-powers.h | 64 - 3rdparty/double-conversion/diy-fp.cc | 57 - 3rdparty/double-conversion/diy-fp.h | 118 - 3rdparty/double-conversion/double-conversion.cc | 889 -- 3rdparty/double-conversion/double-conversion.h | 536 -- 3rdparty/double-conversion/double-conversion.pri | 3 - 3rdparty/double-conversion/fast-dtoa.cc | 664 -- 3rdparty/double-conversion/fast-dtoa.h | 88 - 3rdparty/double-conversion/fixed-dtoa.cc | 402 - 3rdparty/double-conversion/fixed-dtoa.h | 56 - 3rdparty/double-conversion/ieee.h | 398 - 3rdparty/double-conversion/strtod.cc | 554 -- 3rdparty/double-conversion/strtod.h | 45 - 3rdparty/double-conversion/utils.h | 313 - 3rdparty/masm/WeakRandom.h | 52 - 3rdparty/masm/assembler/ARMAssembler.cpp | 428 - 3rdparty/masm/assembler/ARMAssembler.h | 1108 --- 3rdparty/masm/assembler/ARMv7Assembler.cpp | 36 - 3rdparty/masm/assembler/ARMv7Assembler.h | 2706 ------ 3rdparty/masm/assembler/AbstractMacroAssembler.h | 792 -- 3rdparty/masm/assembler/AssemblerBuffer.h | 181 - .../assembler/AssemblerBufferWithConstantPool.h | 342 - 3rdparty/masm/assembler/CodeLocation.h | 218 - 3rdparty/masm/assembler/LinkBuffer.cpp | 230 - 3rdparty/masm/assembler/LinkBuffer.h | 297 - 3rdparty/masm/assembler/MIPSAssembler.h | 1032 --- 3rdparty/masm/assembler/MacroAssembler.h | 1464 ---- 3rdparty/masm/assembler/MacroAssemblerARM.cpp | 99 - 3rdparty/masm/assembler/MacroAssemblerARM.h | 1383 --- 3rdparty/masm/assembler/MacroAssemblerARMv7.h | 1903 ----- 3rdparty/masm/assembler/MacroAssemblerCodeRef.h | 399 - 3rdparty/masm/assembler/MacroAssemblerMIPS.h | 2316 ----- 3rdparty/masm/assembler/MacroAssemblerSH4.cpp | 52 - 3rdparty/masm/assembler/MacroAssemblerSH4.h | 2293 ----- 3rdparty/masm/assembler/MacroAssemblerX86.h | 314 - 3rdparty/masm/assembler/MacroAssemblerX86Common.h | 1541 ---- 3rdparty/masm/assembler/MacroAssemblerX86_64.h | 643 -- 3rdparty/masm/assembler/RepatchBuffer.h | 181 - 3rdparty/masm/assembler/SH4Assembler.h | 2152 ----- 3rdparty/masm/assembler/X86Assembler.h | 2540 ------ 3rdparty/masm/config.h | 56 - 3rdparty/masm/create_regex_tables | 121 - 3rdparty/masm/disassembler/Disassembler.cpp | 43 - 3rdparty/masm/disassembler/Disassembler.h | 52 - 3rdparty/masm/disassembler/UDis86Disassembler.cpp | 63 - 3rdparty/masm/disassembler/udis86/differences.txt | 24 - 3rdparty/masm/disassembler/udis86/itab.py | 360 - 3rdparty/masm/disassembler/udis86/optable.xml | 8959 -------------------- 3rdparty/masm/disassembler/udis86/ud_opcode.py | 235 - 3rdparty/masm/disassembler/udis86/ud_optable.py | 103 - 3rdparty/masm/disassembler/udis86/udis86.c | 183 - 3rdparty/masm/disassembler/udis86/udis86.h | 33 - 3rdparty/masm/disassembler/udis86/udis86_decode.c | 1142 --- 3rdparty/masm/disassembler/udis86/udis86_decode.h | 258 - 3rdparty/masm/disassembler/udis86/udis86_extern.h | 88 - 3rdparty/masm/disassembler/udis86/udis86_input.c | 263 - 3rdparty/masm/disassembler/udis86/udis86_input.h | 67 - .../masm/disassembler/udis86/udis86_itab_holder.c | 34 - 3rdparty/masm/disassembler/udis86/udis86_syn-att.c | 253 - .../masm/disassembler/udis86/udis86_syn-intel.c | 279 - 3rdparty/masm/disassembler/udis86/udis86_syn.c | 87 - 3rdparty/masm/disassembler/udis86/udis86_syn.h | 47 - 3rdparty/masm/disassembler/udis86/udis86_types.h | 238 - 3rdparty/masm/jit/JITCompilationEffort.h | 39 - 3rdparty/masm/masm.pri | 107 - 3rdparty/masm/runtime/MatchResult.h | 71 - 3rdparty/masm/stubs/ExecutableAllocator.h | 105 - 3rdparty/masm/stubs/JSGlobalData.h | 56 - 3rdparty/masm/stubs/LLIntData.h | 0 3rdparty/masm/stubs/Options.h | 53 - 3rdparty/masm/stubs/WTFStubs.cpp | 131 - 3rdparty/masm/stubs/WTFStubs.h | 50 - 3rdparty/masm/stubs/wtf/FastAllocBase.h | 48 - 3rdparty/masm/stubs/wtf/FastMalloc.h | 46 - 3rdparty/masm/stubs/wtf/Noncopyable.h | 48 - 3rdparty/masm/stubs/wtf/OwnPtr.h | 46 - 3rdparty/masm/stubs/wtf/PassOwnPtr.h | 116 - 3rdparty/masm/stubs/wtf/PassRefPtr.h | 101 - 3rdparty/masm/stubs/wtf/RefCounted.h | 70 - 3rdparty/masm/stubs/wtf/RefPtr.h | 93 - 3rdparty/masm/stubs/wtf/TypeTraits.h | 58 - 3rdparty/masm/stubs/wtf/UnusedParam.h | 48 - 3rdparty/masm/stubs/wtf/Vector.h | 95 - 3rdparty/masm/stubs/wtf/text/CString.h | 44 - 3rdparty/masm/stubs/wtf/text/WTFString.h | 75 - 3rdparty/masm/stubs/wtf/unicode/Unicode.h | 59 - 3rdparty/masm/wtf/ASCIICType.h | 181 - 3rdparty/masm/wtf/Assertions.h | 393 - 3rdparty/masm/wtf/Atomics.h | 241 - 3rdparty/masm/wtf/BumpPointerAllocator.h | 252 - 3rdparty/masm/wtf/CheckedArithmetic.h | 708 -- 3rdparty/masm/wtf/Compiler.h | 284 - 3rdparty/masm/wtf/CryptographicallyRandomNumber.h | 45 - 3rdparty/masm/wtf/DataLog.h | 128 - 3rdparty/masm/wtf/DynamicAnnotations.h | 96 - 3rdparty/masm/wtf/FilePrintStream.cpp | 64 - 3rdparty/masm/wtf/FilePrintStream.h | 62 - 3rdparty/masm/wtf/Locker.h | 48 - 3rdparty/masm/wtf/NotFound.h | 37 - 3rdparty/masm/wtf/NullPtr.h | 56 - 3rdparty/masm/wtf/OSAllocator.h | 115 - 3rdparty/masm/wtf/OSAllocatorPosix.cpp | 184 - 3rdparty/masm/wtf/OSAllocatorWin.cpp | 80 - 3rdparty/masm/wtf/PageAllocation.h | 120 - 3rdparty/masm/wtf/PageAllocationAligned.cpp | 87 - 3rdparty/masm/wtf/PageAllocationAligned.h | 70 - 3rdparty/masm/wtf/PageBlock.cpp | 78 - 3rdparty/masm/wtf/PageBlock.h | 88 - 3rdparty/masm/wtf/PageReservation.h | 149 - 3rdparty/masm/wtf/Platform.h | 1212 --- 3rdparty/masm/wtf/PossiblyNull.h | 59 - 3rdparty/masm/wtf/PrintStream.cpp | 112 - 3rdparty/masm/wtf/PrintStream.h | 300 - 3rdparty/masm/wtf/RawPointer.h | 58 - 3rdparty/masm/wtf/StdLibExtras.h | 282 - 3rdparty/masm/wtf/VMTags.h | 75 - 3rdparty/masm/yarr/Yarr.h | 69 - 3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp | 463 - 3rdparty/masm/yarr/YarrCanonicalizeUCS2.h | 138 - 3rdparty/masm/yarr/YarrCanonicalizeUCS2.js | 219 - 3rdparty/masm/yarr/YarrInterpreter.cpp | 1964 ----- 3rdparty/masm/yarr/YarrInterpreter.h | 385 - 3rdparty/masm/yarr/YarrJIT.cpp | 2667 ------ 3rdparty/masm/yarr/YarrJIT.h | 141 - 3rdparty/masm/yarr/YarrParser.h | 880 -- 3rdparty/masm/yarr/YarrPattern.cpp | 874 -- 3rdparty/masm/yarr/YarrPattern.h | 421 - 3rdparty/masm/yarr/YarrSyntaxChecker.cpp | 59 - 3rdparty/masm/yarr/YarrSyntaxChecker.h | 38 - 3rdparty/masm/yarr/yarr.pri | 12 - debugging.cpp | 214 - debugging.h | 140 - llvm_runtime.cpp | 523 -- main.cpp | 426 - moth/moth.pri | 13 - moth/qv4instr_moth.cpp | 15 - moth/qv4instr_moth_p.h | 514 -- moth/qv4isel_moth.cpp | 975 --- moth/qv4isel_moth_p.h | 167 - moth/qv4vme_moth.cpp | 483 -- moth/qv4vme_moth_p.h | 37 - qmljs_engine.cpp | 493 -- qmljs_engine.h | 234 - qmljs_environment.cpp | 564 -- qmljs_environment.h | 149 - qmljs_math.h | 106 - qmljs_runtime.cpp | 1160 --- qmljs_runtime.h | 861 -- qmljs_value.cpp | 163 - qmljs_value.h | 481 -- qv4_llvm_p.h | 52 - qv4argumentsobject.cpp | 161 - qv4argumentsobject.h | 85 - qv4array.cpp | 649 -- qv4array.h | 638 -- qv4arrayobject.cpp | 798 -- qv4arrayobject.h | 95 - qv4booleanobject.cpp | 94 - qv4booleanobject.h | 72 - qv4codegen.cpp | 2614 ------ qv4codegen_p.h | 420 - qv4dateobject.cpp | 1313 --- qv4dateobject.h | 125 - qv4errorobject.cpp | 257 - qv4errorobject.h | 206 - qv4functionobject.cpp | 465 - qv4functionobject.h | 225 - qv4globalobject.cpp | 670 -- qv4globalobject.h | 83 - qv4ir.cpp | 670 -- qv4ir_p.h | 726 -- qv4isel_llvm.cpp | 1387 --- qv4isel_llvm_p.h | 177 - qv4isel_masm.cpp | 933 -- qv4isel_masm_p.h | 797 -- qv4isel_p.cpp | 414 - qv4isel_p.h | 143 - qv4isel_util_p.h | 63 - qv4jsonobject.cpp | 935 -- qv4jsonobject.h | 61 - qv4managed.cpp | 141 - qv4managed.h | 172 - qv4mathobject.cpp | 299 - qv4mathobject.h | 78 - qv4mm.cpp | 396 - qv4mm.h | 127 - qv4numberobject.cpp | 233 - qv4numberobject.h | 76 - qv4object.cpp | 674 -- qv4object.h | 191 - qv4objectiterator.cpp | 166 - qv4objectiterator.h | 79 - qv4objectproto.cpp | 558 -- qv4objectproto.h | 95 - qv4propertydescriptor.h | 169 - qv4propertytable.h | 224 - qv4regexp.cpp | 76 - qv4regexp.h | 92 - qv4regexpobject.cpp | 237 - qv4regexpobject.h | 100 - qv4string.cpp | 124 - qv4string.h | 98 - qv4stringobject.cpp | 722 -- qv4stringobject.h | 100 - qv4syntaxchecker.cpp | 119 - qv4syntaxchecker_p.h | 73 - src/3rdparty/double-conversion/README | 6 + src/3rdparty/double-conversion/bignum-dtoa.cc | 640 ++ src/3rdparty/double-conversion/bignum-dtoa.h | 84 + src/3rdparty/double-conversion/bignum.cc | 764 ++ src/3rdparty/double-conversion/bignum.h | 145 + src/3rdparty/double-conversion/cached-powers.cc | 175 + src/3rdparty/double-conversion/cached-powers.h | 64 + src/3rdparty/double-conversion/diy-fp.cc | 57 + src/3rdparty/double-conversion/diy-fp.h | 118 + .../double-conversion/double-conversion.cc | 889 ++ src/3rdparty/double-conversion/double-conversion.h | 536 ++ .../double-conversion/double-conversion.pri | 4 + src/3rdparty/double-conversion/fast-dtoa.cc | 664 ++ src/3rdparty/double-conversion/fast-dtoa.h | 88 + src/3rdparty/double-conversion/fixed-dtoa.cc | 402 + src/3rdparty/double-conversion/fixed-dtoa.h | 56 + src/3rdparty/double-conversion/ieee.h | 398 + src/3rdparty/double-conversion/strtod.cc | 554 ++ src/3rdparty/double-conversion/strtod.h | 45 + src/3rdparty/double-conversion/utils.h | 313 + src/3rdparty/masm/WeakRandom.h | 52 + src/3rdparty/masm/assembler/ARMAssembler.cpp | 428 + src/3rdparty/masm/assembler/ARMAssembler.h | 1108 +++ src/3rdparty/masm/assembler/ARMv7Assembler.cpp | 36 + src/3rdparty/masm/assembler/ARMv7Assembler.h | 2706 ++++++ .../masm/assembler/AbstractMacroAssembler.h | 792 ++ src/3rdparty/masm/assembler/AssemblerBuffer.h | 181 + .../assembler/AssemblerBufferWithConstantPool.h | 342 + src/3rdparty/masm/assembler/CodeLocation.h | 218 + src/3rdparty/masm/assembler/LinkBuffer.cpp | 230 + src/3rdparty/masm/assembler/LinkBuffer.h | 297 + src/3rdparty/masm/assembler/MIPSAssembler.h | 1032 +++ src/3rdparty/masm/assembler/MacroAssembler.h | 1464 ++++ src/3rdparty/masm/assembler/MacroAssemblerARM.cpp | 99 + src/3rdparty/masm/assembler/MacroAssemblerARM.h | 1383 +++ src/3rdparty/masm/assembler/MacroAssemblerARMv7.h | 1903 +++++ .../masm/assembler/MacroAssemblerCodeRef.h | 399 + src/3rdparty/masm/assembler/MacroAssemblerMIPS.h | 2316 +++++ src/3rdparty/masm/assembler/MacroAssemblerSH4.cpp | 52 + src/3rdparty/masm/assembler/MacroAssemblerSH4.h | 2293 +++++ src/3rdparty/masm/assembler/MacroAssemblerX86.h | 314 + .../masm/assembler/MacroAssemblerX86Common.h | 1541 ++++ src/3rdparty/masm/assembler/MacroAssemblerX86_64.h | 643 ++ src/3rdparty/masm/assembler/RepatchBuffer.h | 181 + src/3rdparty/masm/assembler/SH4Assembler.h | 2152 +++++ src/3rdparty/masm/assembler/X86Assembler.h | 2540 ++++++ src/3rdparty/masm/config.h | 56 + src/3rdparty/masm/create_regex_tables | 121 + src/3rdparty/masm/disassembler/Disassembler.cpp | 43 + src/3rdparty/masm/disassembler/Disassembler.h | 52 + .../masm/disassembler/UDis86Disassembler.cpp | 63 + .../masm/disassembler/udis86/differences.txt | 24 + src/3rdparty/masm/disassembler/udis86/itab.py | 360 + src/3rdparty/masm/disassembler/udis86/optable.xml | 8959 ++++++++++++++++++++ src/3rdparty/masm/disassembler/udis86/ud_opcode.py | 235 + .../masm/disassembler/udis86/ud_optable.py | 103 + src/3rdparty/masm/disassembler/udis86/udis86.c | 183 + src/3rdparty/masm/disassembler/udis86/udis86.h | 33 + .../masm/disassembler/udis86/udis86_decode.c | 1142 +++ .../masm/disassembler/udis86/udis86_decode.h | 258 + .../masm/disassembler/udis86/udis86_extern.h | 88 + .../masm/disassembler/udis86/udis86_input.c | 263 + .../masm/disassembler/udis86/udis86_input.h | 67 + .../masm/disassembler/udis86/udis86_itab_holder.c | 34 + .../masm/disassembler/udis86/udis86_syn-att.c | 253 + .../masm/disassembler/udis86/udis86_syn-intel.c | 279 + src/3rdparty/masm/disassembler/udis86/udis86_syn.c | 87 + src/3rdparty/masm/disassembler/udis86/udis86_syn.h | 47 + .../masm/disassembler/udis86/udis86_types.h | 238 + src/3rdparty/masm/jit/JITCompilationEffort.h | 39 + src/3rdparty/masm/masm.pri | 108 + src/3rdparty/masm/runtime/MatchResult.h | 71 + src/3rdparty/masm/stubs/ExecutableAllocator.h | 105 + src/3rdparty/masm/stubs/JSGlobalData.h | 56 + src/3rdparty/masm/stubs/LLIntData.h | 0 src/3rdparty/masm/stubs/Options.h | 53 + src/3rdparty/masm/stubs/WTFStubs.cpp | 131 + src/3rdparty/masm/stubs/WTFStubs.h | 50 + src/3rdparty/masm/stubs/wtf/FastAllocBase.h | 48 + src/3rdparty/masm/stubs/wtf/FastMalloc.h | 46 + src/3rdparty/masm/stubs/wtf/Noncopyable.h | 48 + src/3rdparty/masm/stubs/wtf/OwnPtr.h | 46 + src/3rdparty/masm/stubs/wtf/PassOwnPtr.h | 116 + src/3rdparty/masm/stubs/wtf/PassRefPtr.h | 101 + src/3rdparty/masm/stubs/wtf/RefCounted.h | 70 + src/3rdparty/masm/stubs/wtf/RefPtr.h | 93 + src/3rdparty/masm/stubs/wtf/TypeTraits.h | 58 + src/3rdparty/masm/stubs/wtf/UnusedParam.h | 48 + src/3rdparty/masm/stubs/wtf/Vector.h | 95 + src/3rdparty/masm/stubs/wtf/text/CString.h | 44 + src/3rdparty/masm/stubs/wtf/text/WTFString.h | 75 + src/3rdparty/masm/stubs/wtf/unicode/Unicode.h | 59 + src/3rdparty/masm/wtf/ASCIICType.h | 181 + src/3rdparty/masm/wtf/Assertions.h | 393 + src/3rdparty/masm/wtf/Atomics.h | 241 + src/3rdparty/masm/wtf/BumpPointerAllocator.h | 252 + src/3rdparty/masm/wtf/CheckedArithmetic.h | 708 ++ src/3rdparty/masm/wtf/Compiler.h | 284 + .../masm/wtf/CryptographicallyRandomNumber.h | 45 + src/3rdparty/masm/wtf/DataLog.h | 128 + src/3rdparty/masm/wtf/DynamicAnnotations.h | 96 + src/3rdparty/masm/wtf/FilePrintStream.cpp | 64 + src/3rdparty/masm/wtf/FilePrintStream.h | 62 + src/3rdparty/masm/wtf/Locker.h | 48 + src/3rdparty/masm/wtf/NotFound.h | 37 + src/3rdparty/masm/wtf/NullPtr.h | 56 + src/3rdparty/masm/wtf/OSAllocator.h | 115 + src/3rdparty/masm/wtf/OSAllocatorPosix.cpp | 184 + src/3rdparty/masm/wtf/OSAllocatorWin.cpp | 80 + src/3rdparty/masm/wtf/PageAllocation.h | 120 + src/3rdparty/masm/wtf/PageAllocationAligned.cpp | 87 + src/3rdparty/masm/wtf/PageAllocationAligned.h | 70 + src/3rdparty/masm/wtf/PageBlock.cpp | 78 + src/3rdparty/masm/wtf/PageBlock.h | 88 + src/3rdparty/masm/wtf/PageReservation.h | 149 + src/3rdparty/masm/wtf/Platform.h | 1212 +++ src/3rdparty/masm/wtf/PossiblyNull.h | 59 + src/3rdparty/masm/wtf/PrintStream.cpp | 112 + src/3rdparty/masm/wtf/PrintStream.h | 300 + src/3rdparty/masm/wtf/RawPointer.h | 58 + src/3rdparty/masm/wtf/StdLibExtras.h | 282 + src/3rdparty/masm/wtf/VMTags.h | 75 + src/3rdparty/masm/yarr/Yarr.h | 69 + src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp | 463 + src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.h | 138 + src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js | 219 + src/3rdparty/masm/yarr/YarrInterpreter.cpp | 1964 +++++ src/3rdparty/masm/yarr/YarrInterpreter.h | 385 + src/3rdparty/masm/yarr/YarrJIT.cpp | 2667 ++++++ src/3rdparty/masm/yarr/YarrJIT.h | 141 + src/3rdparty/masm/yarr/YarrParser.h | 880 ++ src/3rdparty/masm/yarr/YarrPattern.cpp | 874 ++ src/3rdparty/masm/yarr/YarrPattern.h | 421 + src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp | 59 + src/3rdparty/masm/yarr/YarrSyntaxChecker.h | 38 + src/3rdparty/masm/yarr/yarr.pri | 12 + src/src.pro | 3 + src/tools/main.cpp | 426 + src/tools/tools.pro | 17 + src/v4/debugging.cpp | 214 + src/v4/debugging.h | 141 + src/v4/llvm_runtime.cpp | 523 ++ src/v4/moth/moth.pri | 13 + src/v4/moth/qv4instr_moth.cpp | 15 + src/v4/moth/qv4instr_moth_p.h | 514 ++ src/v4/moth/qv4isel_moth.cpp | 975 +++ src/v4/moth/qv4isel_moth_p.h | 168 + src/v4/moth/qv4vme_moth.cpp | 483 ++ src/v4/moth/qv4vme_moth_p.h | 37 + src/v4/qmljs_engine.cpp | 493 ++ src/v4/qmljs_engine.h | 235 + src/v4/qmljs_environment.cpp | 564 ++ src/v4/qmljs_environment.h | 150 + src/v4/qmljs_math.h | 106 + src/v4/qmljs_runtime.cpp | 1161 +++ src/v4/qmljs_runtime.h | 862 ++ src/v4/qmljs_value.cpp | 163 + src/v4/qmljs_value.h | 480 ++ src/v4/qv4_llvm_p.h | 53 + src/v4/qv4argumentsobject.cpp | 161 + src/v4/qv4argumentsobject.h | 85 + src/v4/qv4array.cpp | 649 ++ src/v4/qv4array.h | 639 ++ src/v4/qv4arrayobject.cpp | 798 ++ src/v4/qv4arrayobject.h | 95 + src/v4/qv4booleanobject.cpp | 94 + src/v4/qv4booleanobject.h | 72 + src/v4/qv4codegen.cpp | 2614 ++++++ src/v4/qv4codegen_p.h | 421 + src/v4/qv4dateobject.cpp | 1313 +++ src/v4/qv4dateobject.h | 125 + src/v4/qv4errorobject.cpp | 257 + src/v4/qv4errorobject.h | 206 + src/v4/qv4functionobject.cpp | 465 + src/v4/qv4functionobject.h | 229 + src/v4/qv4global.h | 49 + src/v4/qv4globalobject.cpp | 670 ++ src/v4/qv4globalobject.h | 84 + src/v4/qv4ir.cpp | 670 ++ src/v4/qv4ir_p.h | 724 ++ src/v4/qv4isel_llvm.cpp | 1388 +++ src/v4/qv4isel_llvm_p.h | 177 + src/v4/qv4isel_masm.cpp | 933 ++ src/v4/qv4isel_masm_p.h | 798 ++ src/v4/qv4isel_p.cpp | 414 + src/v4/qv4isel_p.h | 144 + src/v4/qv4isel_util_p.h | 63 + src/v4/qv4jsonobject.cpp | 935 ++ src/v4/qv4jsonobject.h | 61 + src/v4/qv4managed.cpp | 141 + src/v4/qv4managed.h | 173 + src/v4/qv4mathobject.cpp | 299 + src/v4/qv4mathobject.h | 78 + src/v4/qv4mm.cpp | 396 + src/v4/qv4mm.h | 128 + src/v4/qv4numberobject.cpp | 233 + src/v4/qv4numberobject.h | 76 + src/v4/qv4object.cpp | 674 ++ src/v4/qv4object.h | 192 + src/v4/qv4objectiterator.cpp | 166 + src/v4/qv4objectiterator.h | 79 + src/v4/qv4objectproto.cpp | 558 ++ src/v4/qv4objectproto.h | 95 + src/v4/qv4propertydescriptor.h | 169 + src/v4/qv4propertytable.h | 224 + src/v4/qv4regexp.cpp | 76 + src/v4/qv4regexp.h | 92 + src/v4/qv4regexpobject.cpp | 237 + src/v4/qv4regexpobject.h | 100 + src/v4/qv4string.cpp | 124 + src/v4/qv4string.h | 98 + src/v4/qv4stringobject.cpp | 722 ++ src/v4/qv4stringobject.h | 100 + src/v4/qv4syntaxchecker.cpp | 119 + src/v4/qv4syntaxchecker_p.h | 73 + src/v4/v4.pro | 155 + sync.profile | 20 + v4.pro | 145 - v4vm.pro | 2 + 432 files changed, 87975 insertions(+), 87854 deletions(-) create mode 100644 .qmake.conf delete mode 100644 3rdparty/double-conversion/README delete mode 100644 3rdparty/double-conversion/bignum-dtoa.cc delete mode 100644 3rdparty/double-conversion/bignum-dtoa.h delete mode 100644 3rdparty/double-conversion/bignum.cc delete mode 100644 3rdparty/double-conversion/bignum.h delete mode 100644 3rdparty/double-conversion/cached-powers.cc delete mode 100644 3rdparty/double-conversion/cached-powers.h delete mode 100644 3rdparty/double-conversion/diy-fp.cc delete mode 100644 3rdparty/double-conversion/diy-fp.h delete mode 100644 3rdparty/double-conversion/double-conversion.cc delete mode 100644 3rdparty/double-conversion/double-conversion.h delete mode 100644 3rdparty/double-conversion/double-conversion.pri delete mode 100644 3rdparty/double-conversion/fast-dtoa.cc delete mode 100644 3rdparty/double-conversion/fast-dtoa.h delete mode 100644 3rdparty/double-conversion/fixed-dtoa.cc delete mode 100644 3rdparty/double-conversion/fixed-dtoa.h delete mode 100644 3rdparty/double-conversion/ieee.h delete mode 100644 3rdparty/double-conversion/strtod.cc delete mode 100644 3rdparty/double-conversion/strtod.h delete mode 100644 3rdparty/double-conversion/utils.h delete mode 100644 3rdparty/masm/WeakRandom.h delete mode 100644 3rdparty/masm/assembler/ARMAssembler.cpp delete mode 100644 3rdparty/masm/assembler/ARMAssembler.h delete mode 100644 3rdparty/masm/assembler/ARMv7Assembler.cpp delete mode 100644 3rdparty/masm/assembler/ARMv7Assembler.h delete mode 100644 3rdparty/masm/assembler/AbstractMacroAssembler.h delete mode 100644 3rdparty/masm/assembler/AssemblerBuffer.h delete mode 100644 3rdparty/masm/assembler/AssemblerBufferWithConstantPool.h delete mode 100644 3rdparty/masm/assembler/CodeLocation.h delete mode 100644 3rdparty/masm/assembler/LinkBuffer.cpp delete mode 100644 3rdparty/masm/assembler/LinkBuffer.h delete mode 100644 3rdparty/masm/assembler/MIPSAssembler.h delete mode 100644 3rdparty/masm/assembler/MacroAssembler.h delete mode 100644 3rdparty/masm/assembler/MacroAssemblerARM.cpp delete mode 100644 3rdparty/masm/assembler/MacroAssemblerARM.h delete mode 100644 3rdparty/masm/assembler/MacroAssemblerARMv7.h delete mode 100644 3rdparty/masm/assembler/MacroAssemblerCodeRef.h delete mode 100644 3rdparty/masm/assembler/MacroAssemblerMIPS.h delete mode 100644 3rdparty/masm/assembler/MacroAssemblerSH4.cpp delete mode 100644 3rdparty/masm/assembler/MacroAssemblerSH4.h delete mode 100644 3rdparty/masm/assembler/MacroAssemblerX86.h delete mode 100644 3rdparty/masm/assembler/MacroAssemblerX86Common.h delete mode 100644 3rdparty/masm/assembler/MacroAssemblerX86_64.h delete mode 100644 3rdparty/masm/assembler/RepatchBuffer.h delete mode 100644 3rdparty/masm/assembler/SH4Assembler.h delete mode 100644 3rdparty/masm/assembler/X86Assembler.h delete mode 100644 3rdparty/masm/config.h delete mode 100644 3rdparty/masm/create_regex_tables delete mode 100644 3rdparty/masm/disassembler/Disassembler.cpp delete mode 100644 3rdparty/masm/disassembler/Disassembler.h delete mode 100644 3rdparty/masm/disassembler/UDis86Disassembler.cpp delete mode 100644 3rdparty/masm/disassembler/udis86/differences.txt delete mode 100644 3rdparty/masm/disassembler/udis86/itab.py delete mode 100644 3rdparty/masm/disassembler/udis86/optable.xml delete mode 100644 3rdparty/masm/disassembler/udis86/ud_opcode.py delete mode 100644 3rdparty/masm/disassembler/udis86/ud_optable.py delete mode 100644 3rdparty/masm/disassembler/udis86/udis86.c delete mode 100644 3rdparty/masm/disassembler/udis86/udis86.h delete mode 100644 3rdparty/masm/disassembler/udis86/udis86_decode.c delete mode 100644 3rdparty/masm/disassembler/udis86/udis86_decode.h delete mode 100644 3rdparty/masm/disassembler/udis86/udis86_extern.h delete mode 100644 3rdparty/masm/disassembler/udis86/udis86_input.c delete mode 100644 3rdparty/masm/disassembler/udis86/udis86_input.h delete mode 100644 3rdparty/masm/disassembler/udis86/udis86_itab_holder.c delete mode 100644 3rdparty/masm/disassembler/udis86/udis86_syn-att.c delete mode 100644 3rdparty/masm/disassembler/udis86/udis86_syn-intel.c delete mode 100644 3rdparty/masm/disassembler/udis86/udis86_syn.c delete mode 100644 3rdparty/masm/disassembler/udis86/udis86_syn.h delete mode 100644 3rdparty/masm/disassembler/udis86/udis86_types.h delete mode 100644 3rdparty/masm/jit/JITCompilationEffort.h delete mode 100644 3rdparty/masm/masm.pri delete mode 100644 3rdparty/masm/runtime/MatchResult.h delete mode 100644 3rdparty/masm/stubs/ExecutableAllocator.h delete mode 100644 3rdparty/masm/stubs/JSGlobalData.h delete mode 100644 3rdparty/masm/stubs/LLIntData.h delete mode 100644 3rdparty/masm/stubs/Options.h delete mode 100644 3rdparty/masm/stubs/WTFStubs.cpp delete mode 100644 3rdparty/masm/stubs/WTFStubs.h delete mode 100644 3rdparty/masm/stubs/wtf/FastAllocBase.h delete mode 100644 3rdparty/masm/stubs/wtf/FastMalloc.h delete mode 100644 3rdparty/masm/stubs/wtf/Noncopyable.h delete mode 100644 3rdparty/masm/stubs/wtf/OwnPtr.h delete mode 100644 3rdparty/masm/stubs/wtf/PassOwnPtr.h delete mode 100644 3rdparty/masm/stubs/wtf/PassRefPtr.h delete mode 100644 3rdparty/masm/stubs/wtf/RefCounted.h delete mode 100644 3rdparty/masm/stubs/wtf/RefPtr.h delete mode 100644 3rdparty/masm/stubs/wtf/TypeTraits.h delete mode 100644 3rdparty/masm/stubs/wtf/UnusedParam.h delete mode 100644 3rdparty/masm/stubs/wtf/Vector.h delete mode 100644 3rdparty/masm/stubs/wtf/text/CString.h delete mode 100644 3rdparty/masm/stubs/wtf/text/WTFString.h delete mode 100644 3rdparty/masm/stubs/wtf/unicode/Unicode.h delete mode 100644 3rdparty/masm/wtf/ASCIICType.h delete mode 100644 3rdparty/masm/wtf/Assertions.h delete mode 100644 3rdparty/masm/wtf/Atomics.h delete mode 100644 3rdparty/masm/wtf/BumpPointerAllocator.h delete mode 100644 3rdparty/masm/wtf/CheckedArithmetic.h delete mode 100644 3rdparty/masm/wtf/Compiler.h delete mode 100644 3rdparty/masm/wtf/CryptographicallyRandomNumber.h delete mode 100644 3rdparty/masm/wtf/DataLog.h delete mode 100644 3rdparty/masm/wtf/DynamicAnnotations.h delete mode 100644 3rdparty/masm/wtf/FilePrintStream.cpp delete mode 100644 3rdparty/masm/wtf/FilePrintStream.h delete mode 100644 3rdparty/masm/wtf/Locker.h delete mode 100644 3rdparty/masm/wtf/NotFound.h delete mode 100644 3rdparty/masm/wtf/NullPtr.h delete mode 100644 3rdparty/masm/wtf/OSAllocator.h delete mode 100644 3rdparty/masm/wtf/OSAllocatorPosix.cpp delete mode 100644 3rdparty/masm/wtf/OSAllocatorWin.cpp delete mode 100644 3rdparty/masm/wtf/PageAllocation.h delete mode 100644 3rdparty/masm/wtf/PageAllocationAligned.cpp delete mode 100644 3rdparty/masm/wtf/PageAllocationAligned.h delete mode 100644 3rdparty/masm/wtf/PageBlock.cpp delete mode 100644 3rdparty/masm/wtf/PageBlock.h delete mode 100644 3rdparty/masm/wtf/PageReservation.h delete mode 100644 3rdparty/masm/wtf/Platform.h delete mode 100644 3rdparty/masm/wtf/PossiblyNull.h delete mode 100644 3rdparty/masm/wtf/PrintStream.cpp delete mode 100644 3rdparty/masm/wtf/PrintStream.h delete mode 100644 3rdparty/masm/wtf/RawPointer.h delete mode 100644 3rdparty/masm/wtf/StdLibExtras.h delete mode 100644 3rdparty/masm/wtf/VMTags.h delete mode 100644 3rdparty/masm/yarr/Yarr.h delete mode 100644 3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp delete mode 100644 3rdparty/masm/yarr/YarrCanonicalizeUCS2.h delete mode 100644 3rdparty/masm/yarr/YarrCanonicalizeUCS2.js delete mode 100644 3rdparty/masm/yarr/YarrInterpreter.cpp delete mode 100644 3rdparty/masm/yarr/YarrInterpreter.h delete mode 100644 3rdparty/masm/yarr/YarrJIT.cpp delete mode 100644 3rdparty/masm/yarr/YarrJIT.h delete mode 100644 3rdparty/masm/yarr/YarrParser.h delete mode 100644 3rdparty/masm/yarr/YarrPattern.cpp delete mode 100644 3rdparty/masm/yarr/YarrPattern.h delete mode 100644 3rdparty/masm/yarr/YarrSyntaxChecker.cpp delete mode 100644 3rdparty/masm/yarr/YarrSyntaxChecker.h delete mode 100644 3rdparty/masm/yarr/yarr.pri delete mode 100644 debugging.cpp delete mode 100644 debugging.h delete mode 100644 llvm_runtime.cpp delete mode 100644 main.cpp delete mode 100644 moth/moth.pri delete mode 100644 moth/qv4instr_moth.cpp delete mode 100644 moth/qv4instr_moth_p.h delete mode 100644 moth/qv4isel_moth.cpp delete mode 100644 moth/qv4isel_moth_p.h delete mode 100644 moth/qv4vme_moth.cpp delete mode 100644 moth/qv4vme_moth_p.h delete mode 100644 qmljs_engine.cpp delete mode 100644 qmljs_engine.h delete mode 100644 qmljs_environment.cpp delete mode 100644 qmljs_environment.h delete mode 100644 qmljs_math.h delete mode 100644 qmljs_runtime.cpp delete mode 100644 qmljs_runtime.h delete mode 100644 qmljs_value.cpp delete mode 100644 qmljs_value.h delete mode 100644 qv4_llvm_p.h delete mode 100644 qv4argumentsobject.cpp delete mode 100644 qv4argumentsobject.h delete mode 100644 qv4array.cpp delete mode 100644 qv4array.h delete mode 100644 qv4arrayobject.cpp delete mode 100644 qv4arrayobject.h delete mode 100644 qv4booleanobject.cpp delete mode 100644 qv4booleanobject.h delete mode 100644 qv4codegen.cpp delete mode 100644 qv4codegen_p.h delete mode 100644 qv4dateobject.cpp delete mode 100644 qv4dateobject.h delete mode 100644 qv4errorobject.cpp delete mode 100644 qv4errorobject.h delete mode 100644 qv4functionobject.cpp delete mode 100644 qv4functionobject.h delete mode 100644 qv4globalobject.cpp delete mode 100644 qv4globalobject.h delete mode 100644 qv4ir.cpp delete mode 100644 qv4ir_p.h delete mode 100644 qv4isel_llvm.cpp delete mode 100644 qv4isel_llvm_p.h delete mode 100644 qv4isel_masm.cpp delete mode 100644 qv4isel_masm_p.h delete mode 100644 qv4isel_p.cpp delete mode 100644 qv4isel_p.h delete mode 100644 qv4isel_util_p.h delete mode 100644 qv4jsonobject.cpp delete mode 100644 qv4jsonobject.h delete mode 100644 qv4managed.cpp delete mode 100644 qv4managed.h delete mode 100644 qv4mathobject.cpp delete mode 100644 qv4mathobject.h delete mode 100644 qv4mm.cpp delete mode 100644 qv4mm.h delete mode 100644 qv4numberobject.cpp delete mode 100644 qv4numberobject.h delete mode 100644 qv4object.cpp delete mode 100644 qv4object.h delete mode 100644 qv4objectiterator.cpp delete mode 100644 qv4objectiterator.h delete mode 100644 qv4objectproto.cpp delete mode 100644 qv4objectproto.h delete mode 100644 qv4propertydescriptor.h delete mode 100644 qv4propertytable.h delete mode 100644 qv4regexp.cpp delete mode 100644 qv4regexp.h delete mode 100644 qv4regexpobject.cpp delete mode 100644 qv4regexpobject.h delete mode 100644 qv4string.cpp delete mode 100644 qv4string.h delete mode 100644 qv4stringobject.cpp delete mode 100644 qv4stringobject.h delete mode 100644 qv4syntaxchecker.cpp delete mode 100644 qv4syntaxchecker_p.h create mode 100644 src/3rdparty/double-conversion/README create mode 100644 src/3rdparty/double-conversion/bignum-dtoa.cc create mode 100644 src/3rdparty/double-conversion/bignum-dtoa.h create mode 100644 src/3rdparty/double-conversion/bignum.cc create mode 100644 src/3rdparty/double-conversion/bignum.h create mode 100644 src/3rdparty/double-conversion/cached-powers.cc create mode 100644 src/3rdparty/double-conversion/cached-powers.h create mode 100644 src/3rdparty/double-conversion/diy-fp.cc create mode 100644 src/3rdparty/double-conversion/diy-fp.h create mode 100644 src/3rdparty/double-conversion/double-conversion.cc create mode 100644 src/3rdparty/double-conversion/double-conversion.h create mode 100644 src/3rdparty/double-conversion/double-conversion.pri create mode 100644 src/3rdparty/double-conversion/fast-dtoa.cc create mode 100644 src/3rdparty/double-conversion/fast-dtoa.h create mode 100644 src/3rdparty/double-conversion/fixed-dtoa.cc create mode 100644 src/3rdparty/double-conversion/fixed-dtoa.h create mode 100644 src/3rdparty/double-conversion/ieee.h create mode 100644 src/3rdparty/double-conversion/strtod.cc create mode 100644 src/3rdparty/double-conversion/strtod.h create mode 100644 src/3rdparty/double-conversion/utils.h create mode 100644 src/3rdparty/masm/WeakRandom.h create mode 100644 src/3rdparty/masm/assembler/ARMAssembler.cpp create mode 100644 src/3rdparty/masm/assembler/ARMAssembler.h create mode 100644 src/3rdparty/masm/assembler/ARMv7Assembler.cpp create mode 100644 src/3rdparty/masm/assembler/ARMv7Assembler.h create mode 100644 src/3rdparty/masm/assembler/AbstractMacroAssembler.h create mode 100644 src/3rdparty/masm/assembler/AssemblerBuffer.h create mode 100644 src/3rdparty/masm/assembler/AssemblerBufferWithConstantPool.h create mode 100644 src/3rdparty/masm/assembler/CodeLocation.h create mode 100644 src/3rdparty/masm/assembler/LinkBuffer.cpp create mode 100644 src/3rdparty/masm/assembler/LinkBuffer.h create mode 100644 src/3rdparty/masm/assembler/MIPSAssembler.h create mode 100644 src/3rdparty/masm/assembler/MacroAssembler.h create mode 100644 src/3rdparty/masm/assembler/MacroAssemblerARM.cpp create mode 100644 src/3rdparty/masm/assembler/MacroAssemblerARM.h create mode 100644 src/3rdparty/masm/assembler/MacroAssemblerARMv7.h create mode 100644 src/3rdparty/masm/assembler/MacroAssemblerCodeRef.h create mode 100644 src/3rdparty/masm/assembler/MacroAssemblerMIPS.h create mode 100644 src/3rdparty/masm/assembler/MacroAssemblerSH4.cpp create mode 100644 src/3rdparty/masm/assembler/MacroAssemblerSH4.h create mode 100644 src/3rdparty/masm/assembler/MacroAssemblerX86.h create mode 100644 src/3rdparty/masm/assembler/MacroAssemblerX86Common.h create mode 100644 src/3rdparty/masm/assembler/MacroAssemblerX86_64.h create mode 100644 src/3rdparty/masm/assembler/RepatchBuffer.h create mode 100644 src/3rdparty/masm/assembler/SH4Assembler.h create mode 100644 src/3rdparty/masm/assembler/X86Assembler.h create mode 100644 src/3rdparty/masm/config.h create mode 100644 src/3rdparty/masm/create_regex_tables create mode 100644 src/3rdparty/masm/disassembler/Disassembler.cpp create mode 100644 src/3rdparty/masm/disassembler/Disassembler.h create mode 100644 src/3rdparty/masm/disassembler/UDis86Disassembler.cpp create mode 100644 src/3rdparty/masm/disassembler/udis86/differences.txt create mode 100644 src/3rdparty/masm/disassembler/udis86/itab.py create mode 100644 src/3rdparty/masm/disassembler/udis86/optable.xml create mode 100644 src/3rdparty/masm/disassembler/udis86/ud_opcode.py create mode 100644 src/3rdparty/masm/disassembler/udis86/ud_optable.py create mode 100644 src/3rdparty/masm/disassembler/udis86/udis86.c create mode 100644 src/3rdparty/masm/disassembler/udis86/udis86.h create mode 100644 src/3rdparty/masm/disassembler/udis86/udis86_decode.c create mode 100644 src/3rdparty/masm/disassembler/udis86/udis86_decode.h create mode 100644 src/3rdparty/masm/disassembler/udis86/udis86_extern.h create mode 100644 src/3rdparty/masm/disassembler/udis86/udis86_input.c create mode 100644 src/3rdparty/masm/disassembler/udis86/udis86_input.h create mode 100644 src/3rdparty/masm/disassembler/udis86/udis86_itab_holder.c create mode 100644 src/3rdparty/masm/disassembler/udis86/udis86_syn-att.c create mode 100644 src/3rdparty/masm/disassembler/udis86/udis86_syn-intel.c create mode 100644 src/3rdparty/masm/disassembler/udis86/udis86_syn.c create mode 100644 src/3rdparty/masm/disassembler/udis86/udis86_syn.h create mode 100644 src/3rdparty/masm/disassembler/udis86/udis86_types.h create mode 100644 src/3rdparty/masm/jit/JITCompilationEffort.h create mode 100644 src/3rdparty/masm/masm.pri create mode 100644 src/3rdparty/masm/runtime/MatchResult.h create mode 100644 src/3rdparty/masm/stubs/ExecutableAllocator.h create mode 100644 src/3rdparty/masm/stubs/JSGlobalData.h create mode 100644 src/3rdparty/masm/stubs/LLIntData.h create mode 100644 src/3rdparty/masm/stubs/Options.h create mode 100644 src/3rdparty/masm/stubs/WTFStubs.cpp create mode 100644 src/3rdparty/masm/stubs/WTFStubs.h create mode 100644 src/3rdparty/masm/stubs/wtf/FastAllocBase.h create mode 100644 src/3rdparty/masm/stubs/wtf/FastMalloc.h create mode 100644 src/3rdparty/masm/stubs/wtf/Noncopyable.h create mode 100644 src/3rdparty/masm/stubs/wtf/OwnPtr.h create mode 100644 src/3rdparty/masm/stubs/wtf/PassOwnPtr.h create mode 100644 src/3rdparty/masm/stubs/wtf/PassRefPtr.h create mode 100644 src/3rdparty/masm/stubs/wtf/RefCounted.h create mode 100644 src/3rdparty/masm/stubs/wtf/RefPtr.h create mode 100644 src/3rdparty/masm/stubs/wtf/TypeTraits.h create mode 100644 src/3rdparty/masm/stubs/wtf/UnusedParam.h create mode 100644 src/3rdparty/masm/stubs/wtf/Vector.h create mode 100644 src/3rdparty/masm/stubs/wtf/text/CString.h create mode 100644 src/3rdparty/masm/stubs/wtf/text/WTFString.h create mode 100644 src/3rdparty/masm/stubs/wtf/unicode/Unicode.h create mode 100644 src/3rdparty/masm/wtf/ASCIICType.h create mode 100644 src/3rdparty/masm/wtf/Assertions.h create mode 100644 src/3rdparty/masm/wtf/Atomics.h create mode 100644 src/3rdparty/masm/wtf/BumpPointerAllocator.h create mode 100644 src/3rdparty/masm/wtf/CheckedArithmetic.h create mode 100644 src/3rdparty/masm/wtf/Compiler.h create mode 100644 src/3rdparty/masm/wtf/CryptographicallyRandomNumber.h create mode 100644 src/3rdparty/masm/wtf/DataLog.h create mode 100644 src/3rdparty/masm/wtf/DynamicAnnotations.h create mode 100644 src/3rdparty/masm/wtf/FilePrintStream.cpp create mode 100644 src/3rdparty/masm/wtf/FilePrintStream.h create mode 100644 src/3rdparty/masm/wtf/Locker.h create mode 100644 src/3rdparty/masm/wtf/NotFound.h create mode 100644 src/3rdparty/masm/wtf/NullPtr.h create mode 100644 src/3rdparty/masm/wtf/OSAllocator.h create mode 100644 src/3rdparty/masm/wtf/OSAllocatorPosix.cpp create mode 100644 src/3rdparty/masm/wtf/OSAllocatorWin.cpp create mode 100644 src/3rdparty/masm/wtf/PageAllocation.h create mode 100644 src/3rdparty/masm/wtf/PageAllocationAligned.cpp create mode 100644 src/3rdparty/masm/wtf/PageAllocationAligned.h create mode 100644 src/3rdparty/masm/wtf/PageBlock.cpp create mode 100644 src/3rdparty/masm/wtf/PageBlock.h create mode 100644 src/3rdparty/masm/wtf/PageReservation.h create mode 100644 src/3rdparty/masm/wtf/Platform.h create mode 100644 src/3rdparty/masm/wtf/PossiblyNull.h create mode 100644 src/3rdparty/masm/wtf/PrintStream.cpp create mode 100644 src/3rdparty/masm/wtf/PrintStream.h create mode 100644 src/3rdparty/masm/wtf/RawPointer.h create mode 100644 src/3rdparty/masm/wtf/StdLibExtras.h create mode 100644 src/3rdparty/masm/wtf/VMTags.h create mode 100644 src/3rdparty/masm/yarr/Yarr.h create mode 100644 src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp create mode 100644 src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.h create mode 100644 src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js create mode 100644 src/3rdparty/masm/yarr/YarrInterpreter.cpp create mode 100644 src/3rdparty/masm/yarr/YarrInterpreter.h create mode 100644 src/3rdparty/masm/yarr/YarrJIT.cpp create mode 100644 src/3rdparty/masm/yarr/YarrJIT.h create mode 100644 src/3rdparty/masm/yarr/YarrParser.h create mode 100644 src/3rdparty/masm/yarr/YarrPattern.cpp create mode 100644 src/3rdparty/masm/yarr/YarrPattern.h create mode 100644 src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp create mode 100644 src/3rdparty/masm/yarr/YarrSyntaxChecker.h create mode 100644 src/3rdparty/masm/yarr/yarr.pri create mode 100644 src/src.pro create mode 100644 src/tools/main.cpp create mode 100644 src/tools/tools.pro create mode 100644 src/v4/debugging.cpp create mode 100644 src/v4/debugging.h create mode 100644 src/v4/llvm_runtime.cpp create mode 100644 src/v4/moth/moth.pri create mode 100644 src/v4/moth/qv4instr_moth.cpp create mode 100644 src/v4/moth/qv4instr_moth_p.h create mode 100644 src/v4/moth/qv4isel_moth.cpp create mode 100644 src/v4/moth/qv4isel_moth_p.h create mode 100644 src/v4/moth/qv4vme_moth.cpp create mode 100644 src/v4/moth/qv4vme_moth_p.h create mode 100644 src/v4/qmljs_engine.cpp create mode 100644 src/v4/qmljs_engine.h create mode 100644 src/v4/qmljs_environment.cpp create mode 100644 src/v4/qmljs_environment.h create mode 100644 src/v4/qmljs_math.h create mode 100644 src/v4/qmljs_runtime.cpp create mode 100644 src/v4/qmljs_runtime.h create mode 100644 src/v4/qmljs_value.cpp create mode 100644 src/v4/qmljs_value.h create mode 100644 src/v4/qv4_llvm_p.h create mode 100644 src/v4/qv4argumentsobject.cpp create mode 100644 src/v4/qv4argumentsobject.h create mode 100644 src/v4/qv4array.cpp create mode 100644 src/v4/qv4array.h create mode 100644 src/v4/qv4arrayobject.cpp create mode 100644 src/v4/qv4arrayobject.h create mode 100644 src/v4/qv4booleanobject.cpp create mode 100644 src/v4/qv4booleanobject.h create mode 100644 src/v4/qv4codegen.cpp create mode 100644 src/v4/qv4codegen_p.h create mode 100644 src/v4/qv4dateobject.cpp create mode 100644 src/v4/qv4dateobject.h create mode 100644 src/v4/qv4errorobject.cpp create mode 100644 src/v4/qv4errorobject.h create mode 100644 src/v4/qv4functionobject.cpp create mode 100644 src/v4/qv4functionobject.h create mode 100644 src/v4/qv4global.h create mode 100644 src/v4/qv4globalobject.cpp create mode 100644 src/v4/qv4globalobject.h create mode 100644 src/v4/qv4ir.cpp create mode 100644 src/v4/qv4ir_p.h create mode 100644 src/v4/qv4isel_llvm.cpp create mode 100644 src/v4/qv4isel_llvm_p.h create mode 100644 src/v4/qv4isel_masm.cpp create mode 100644 src/v4/qv4isel_masm_p.h create mode 100644 src/v4/qv4isel_p.cpp create mode 100644 src/v4/qv4isel_p.h create mode 100644 src/v4/qv4isel_util_p.h create mode 100644 src/v4/qv4jsonobject.cpp create mode 100644 src/v4/qv4jsonobject.h create mode 100644 src/v4/qv4managed.cpp create mode 100644 src/v4/qv4managed.h create mode 100644 src/v4/qv4mathobject.cpp create mode 100644 src/v4/qv4mathobject.h create mode 100644 src/v4/qv4mm.cpp create mode 100644 src/v4/qv4mm.h create mode 100644 src/v4/qv4numberobject.cpp create mode 100644 src/v4/qv4numberobject.h create mode 100644 src/v4/qv4object.cpp create mode 100644 src/v4/qv4object.h create mode 100644 src/v4/qv4objectiterator.cpp create mode 100644 src/v4/qv4objectiterator.h create mode 100644 src/v4/qv4objectproto.cpp create mode 100644 src/v4/qv4objectproto.h create mode 100644 src/v4/qv4propertydescriptor.h create mode 100644 src/v4/qv4propertytable.h create mode 100644 src/v4/qv4regexp.cpp create mode 100644 src/v4/qv4regexp.h create mode 100644 src/v4/qv4regexpobject.cpp create mode 100644 src/v4/qv4regexpobject.h create mode 100644 src/v4/qv4string.cpp create mode 100644 src/v4/qv4string.h create mode 100644 src/v4/qv4stringobject.cpp create mode 100644 src/v4/qv4stringobject.h create mode 100644 src/v4/qv4syntaxchecker.cpp create mode 100644 src/v4/qv4syntaxchecker_p.h create mode 100644 src/v4/v4.pro create mode 100644 sync.profile delete mode 100644 v4.pro create mode 100644 v4vm.pro diff --git a/.qmake.conf b/.qmake.conf new file mode 100644 index 0000000000..42ba8e45fb --- /dev/null +++ b/.qmake.conf @@ -0,0 +1 @@ +load(qt_build_config) diff --git a/3rdparty/double-conversion/README b/3rdparty/double-conversion/README deleted file mode 100644 index 40ed4a7efd..0000000000 --- a/3rdparty/double-conversion/README +++ /dev/null @@ -1,6 +0,0 @@ -This is a copy of the library for binary-decimal and decimal-binary conversion routines for IEEE doubles, taken -from - - http://code.google.com/p/double-conversion/ - -commit e5b34421b763f7bf7e4f9081403db417d5a55a36 diff --git a/3rdparty/double-conversion/bignum-dtoa.cc b/3rdparty/double-conversion/bignum-dtoa.cc deleted file mode 100644 index b6c2e85d17..0000000000 --- a/3rdparty/double-conversion/bignum-dtoa.cc +++ /dev/null @@ -1,640 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include - -#include "bignum-dtoa.h" - -#include "bignum.h" -#include "ieee.h" - -namespace double_conversion { - -static int NormalizedExponent(uint64_t significand, int exponent) { - ASSERT(significand != 0); - while ((significand & Double::kHiddenBit) == 0) { - significand = significand << 1; - exponent = exponent - 1; - } - return exponent; -} - - -// Forward declarations: -// Returns an estimation of k such that 10^(k-1) <= v < 10^k. -static int EstimatePower(int exponent); -// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator -// and denominator. -static void InitialScaledStartValues(uint64_t significand, - int exponent, - bool lower_boundary_is_closer, - int estimated_power, - bool need_boundary_deltas, - Bignum* numerator, - Bignum* denominator, - Bignum* delta_minus, - Bignum* delta_plus); -// Multiplies numerator/denominator so that its values lies in the range 1-10. -// Returns decimal_point s.t. -// v = numerator'/denominator' * 10^(decimal_point-1) -// where numerator' and denominator' are the values of numerator and -// denominator after the call to this function. -static void FixupMultiply10(int estimated_power, bool is_even, - int* decimal_point, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus); -// Generates digits from the left to the right and stops when the generated -// digits yield the shortest decimal representation of v. -static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus, - bool is_even, - Vector buffer, int* length); -// Generates 'requested_digits' after the decimal point. -static void BignumToFixed(int requested_digits, int* decimal_point, - Bignum* numerator, Bignum* denominator, - Vector(buffer), int* length); -// Generates 'count' digits of numerator/denominator. -// Once 'count' digits have been produced rounds the result depending on the -// remainder (remainders of exactly .5 round upwards). Might update the -// decimal_point when rounding up (for example for 0.9999). -static void GenerateCountedDigits(int count, int* decimal_point, - Bignum* numerator, Bignum* denominator, - Vector(buffer), int* length); - - -void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, - Vector buffer, int* length, int* decimal_point) { - ASSERT(v > 0); - ASSERT(!Double(v).IsSpecial()); - uint64_t significand; - int exponent; - bool lower_boundary_is_closer; - if (mode == BIGNUM_DTOA_SHORTEST_SINGLE) { - float f = static_cast(v); - ASSERT(f == v); - significand = Single(f).Significand(); - exponent = Single(f).Exponent(); - lower_boundary_is_closer = Single(f).LowerBoundaryIsCloser(); - } else { - significand = Double(v).Significand(); - exponent = Double(v).Exponent(); - lower_boundary_is_closer = Double(v).LowerBoundaryIsCloser(); - } - bool need_boundary_deltas = - (mode == BIGNUM_DTOA_SHORTEST || mode == BIGNUM_DTOA_SHORTEST_SINGLE); - - bool is_even = (significand & 1) == 0; - int normalized_exponent = NormalizedExponent(significand, exponent); - // estimated_power might be too low by 1. - int estimated_power = EstimatePower(normalized_exponent); - - // Shortcut for Fixed. - // The requested digits correspond to the digits after the point. If the - // number is much too small, then there is no need in trying to get any - // digits. - if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) { - buffer[0] = '\0'; - *length = 0; - // Set decimal-point to -requested_digits. This is what Gay does. - // Note that it should not have any effect anyways since the string is - // empty. - *decimal_point = -requested_digits; - return; - } - - Bignum numerator; - Bignum denominator; - Bignum delta_minus; - Bignum delta_plus; - // Make sure the bignum can grow large enough. The smallest double equals - // 4e-324. In this case the denominator needs fewer than 324*4 binary digits. - // The maximum double is 1.7976931348623157e308 which needs fewer than - // 308*4 binary digits. - ASSERT(Bignum::kMaxSignificantBits >= 324*4); - InitialScaledStartValues(significand, exponent, lower_boundary_is_closer, - estimated_power, need_boundary_deltas, - &numerator, &denominator, - &delta_minus, &delta_plus); - // We now have v = (numerator / denominator) * 10^estimated_power. - FixupMultiply10(estimated_power, is_even, decimal_point, - &numerator, &denominator, - &delta_minus, &delta_plus); - // We now have v = (numerator / denominator) * 10^(decimal_point-1), and - // 1 <= (numerator + delta_plus) / denominator < 10 - switch (mode) { - case BIGNUM_DTOA_SHORTEST: - case BIGNUM_DTOA_SHORTEST_SINGLE: - GenerateShortestDigits(&numerator, &denominator, - &delta_minus, &delta_plus, - is_even, buffer, length); - break; - case BIGNUM_DTOA_FIXED: - BignumToFixed(requested_digits, decimal_point, - &numerator, &denominator, - buffer, length); - break; - case BIGNUM_DTOA_PRECISION: - GenerateCountedDigits(requested_digits, decimal_point, - &numerator, &denominator, - buffer, length); - break; - default: - UNREACHABLE(); - } - buffer[*length] = '\0'; -} - - -// The procedure starts generating digits from the left to the right and stops -// when the generated digits yield the shortest decimal representation of v. A -// decimal representation of v is a number lying closer to v than to any other -// double, so it converts to v when read. -// -// This is true if d, the decimal representation, is between m- and m+, the -// upper and lower boundaries. d must be strictly between them if !is_even. -// m- := (numerator - delta_minus) / denominator -// m+ := (numerator + delta_plus) / denominator -// -// Precondition: 0 <= (numerator+delta_plus) / denominator < 10. -// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit -// will be produced. This should be the standard precondition. -static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus, - bool is_even, - Vector buffer, int* length) { - // Small optimization: if delta_minus and delta_plus are the same just reuse - // one of the two bignums. - if (Bignum::Equal(*delta_minus, *delta_plus)) { - delta_plus = delta_minus; - } - *length = 0; - while (true) { - uint16_t digit; - digit = numerator->DivideModuloIntBignum(*denominator); - ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. - // digit = numerator / denominator (integer division). - // numerator = numerator % denominator. - buffer[(*length)++] = digit + '0'; - - // Can we stop already? - // If the remainder of the division is less than the distance to the lower - // boundary we can stop. In this case we simply round down (discarding the - // remainder). - // Similarly we test if we can round up (using the upper boundary). - bool in_delta_room_minus; - bool in_delta_room_plus; - if (is_even) { - in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus); - } else { - in_delta_room_minus = Bignum::Less(*numerator, *delta_minus); - } - if (is_even) { - in_delta_room_plus = - Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; - } else { - in_delta_room_plus = - Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; - } - if (!in_delta_room_minus && !in_delta_room_plus) { - // Prepare for next iteration. - numerator->Times10(); - delta_minus->Times10(); - // We optimized delta_plus to be equal to delta_minus (if they share the - // same value). So don't multiply delta_plus if they point to the same - // object. - if (delta_minus != delta_plus) { - delta_plus->Times10(); - } - } else if (in_delta_room_minus && in_delta_room_plus) { - // Let's see if 2*numerator < denominator. - // If yes, then the next digit would be < 5 and we can round down. - int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator); - if (compare < 0) { - // Remaining digits are less than .5. -> Round down (== do nothing). - } else if (compare > 0) { - // Remaining digits are more than .5 of denominator. -> Round up. - // Note that the last digit could not be a '9' as otherwise the whole - // loop would have stopped earlier. - // We still have an assert here in case the preconditions were not - // satisfied. - ASSERT(buffer[(*length) - 1] != '9'); - buffer[(*length) - 1]++; - } else { - // Halfway case. - // TODO(floitsch): need a way to solve half-way cases. - // For now let's round towards even (since this is what Gay seems to - // do). - - if ((buffer[(*length) - 1] - '0') % 2 == 0) { - // Round down => Do nothing. - } else { - ASSERT(buffer[(*length) - 1] != '9'); - buffer[(*length) - 1]++; - } - } - return; - } else if (in_delta_room_minus) { - // Round down (== do nothing). - return; - } else { // in_delta_room_plus - // Round up. - // Note again that the last digit could not be '9' since this would have - // stopped the loop earlier. - // We still have an ASSERT here, in case the preconditions were not - // satisfied. - ASSERT(buffer[(*length) -1] != '9'); - buffer[(*length) - 1]++; - return; - } - } -} - - -// Let v = numerator / denominator < 10. -// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point) -// from left to right. Once 'count' digits have been produced we decide wether -// to round up or down. Remainders of exactly .5 round upwards. Numbers such -// as 9.999999 propagate a carry all the way, and change the -// exponent (decimal_point), when rounding upwards. -static void GenerateCountedDigits(int count, int* decimal_point, - Bignum* numerator, Bignum* denominator, - Vector(buffer), int* length) { - ASSERT(count >= 0); - for (int i = 0; i < count - 1; ++i) { - uint16_t digit; - digit = numerator->DivideModuloIntBignum(*denominator); - ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. - // digit = numerator / denominator (integer division). - // numerator = numerator % denominator. - buffer[i] = digit + '0'; - // Prepare for next iteration. - numerator->Times10(); - } - // Generate the last digit. - uint16_t digit; - digit = numerator->DivideModuloIntBignum(*denominator); - if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { - digit++; - } - buffer[count - 1] = digit + '0'; - // Correct bad digits (in case we had a sequence of '9's). Propagate the - // carry until we hat a non-'9' or til we reach the first digit. - for (int i = count - 1; i > 0; --i) { - if (buffer[i] != '0' + 10) break; - buffer[i] = '0'; - buffer[i - 1]++; - } - if (buffer[0] == '0' + 10) { - // Propagate a carry past the top place. - buffer[0] = '1'; - (*decimal_point)++; - } - *length = count; -} - - -// Generates 'requested_digits' after the decimal point. It might omit -// trailing '0's. If the input number is too small then no digits at all are -// generated (ex.: 2 fixed digits for 0.00001). -// -// Input verifies: 1 <= (numerator + delta) / denominator < 10. -static void BignumToFixed(int requested_digits, int* decimal_point, - Bignum* numerator, Bignum* denominator, - Vector(buffer), int* length) { - // Note that we have to look at more than just the requested_digits, since - // a number could be rounded up. Example: v=0.5 with requested_digits=0. - // Even though the power of v equals 0 we can't just stop here. - if (-(*decimal_point) > requested_digits) { - // The number is definitively too small. - // Ex: 0.001 with requested_digits == 1. - // Set decimal-point to -requested_digits. This is what Gay does. - // Note that it should not have any effect anyways since the string is - // empty. - *decimal_point = -requested_digits; - *length = 0; - return; - } else if (-(*decimal_point) == requested_digits) { - // We only need to verify if the number rounds down or up. - // Ex: 0.04 and 0.06 with requested_digits == 1. - ASSERT(*decimal_point == -requested_digits); - // Initially the fraction lies in range (1, 10]. Multiply the denominator - // by 10 so that we can compare more easily. - denominator->Times10(); - if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { - // If the fraction is >= 0.5 then we have to include the rounded - // digit. - buffer[0] = '1'; - *length = 1; - (*decimal_point)++; - } else { - // Note that we caught most of similar cases earlier. - *length = 0; - } - return; - } else { - // The requested digits correspond to the digits after the point. - // The variable 'needed_digits' includes the digits before the point. - int needed_digits = (*decimal_point) + requested_digits; - GenerateCountedDigits(needed_digits, decimal_point, - numerator, denominator, - buffer, length); - } -} - - -// Returns an estimation of k such that 10^(k-1) <= v < 10^k where -// v = f * 2^exponent and 2^52 <= f < 2^53. -// v is hence a normalized double with the given exponent. The output is an -// approximation for the exponent of the decimal approimation .digits * 10^k. -// -// The result might undershoot by 1 in which case 10^k <= v < 10^k+1. -// Note: this property holds for v's upper boundary m+ too. -// 10^k <= m+ < 10^k+1. -// (see explanation below). -// -// Examples: -// EstimatePower(0) => 16 -// EstimatePower(-52) => 0 -// -// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0. -static int EstimatePower(int exponent) { - // This function estimates log10 of v where v = f*2^e (with e == exponent). - // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)). - // Note that f is bounded by its container size. Let p = 53 (the double's - // significand size). Then 2^(p-1) <= f < 2^p. - // - // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close - // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)). - // The computed number undershoots by less than 0.631 (when we compute log3 - // and not log10). - // - // Optimization: since we only need an approximated result this computation - // can be performed on 64 bit integers. On x86/x64 architecture the speedup is - // not really measurable, though. - // - // Since we want to avoid overshooting we decrement by 1e10 so that - // floating-point imprecisions don't affect us. - // - // Explanation for v's boundary m+: the computation takes advantage of - // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement - // (even for denormals where the delta can be much more important). - - const double k1Log10 = 0.30102999566398114; // 1/lg(10) - - // For doubles len(f) == 53 (don't forget the hidden bit). - const int kSignificandSize = Double::kSignificandSize; - double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10); - return static_cast(estimate); -} - - -// See comments for InitialScaledStartValues. -static void InitialScaledStartValuesPositiveExponent( - uint64_t significand, int exponent, - int estimated_power, bool need_boundary_deltas, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus) { - // A positive exponent implies a positive power. - ASSERT(estimated_power >= 0); - // Since the estimated_power is positive we simply multiply the denominator - // by 10^estimated_power. - - // numerator = v. - numerator->AssignUInt64(significand); - numerator->ShiftLeft(exponent); - // denominator = 10^estimated_power. - denominator->AssignPowerUInt16(10, estimated_power); - - if (need_boundary_deltas) { - // Introduce a common denominator so that the deltas to the boundaries are - // integers. - denominator->ShiftLeft(1); - numerator->ShiftLeft(1); - // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common - // denominator (of 2) delta_plus equals 2^e. - delta_plus->AssignUInt16(1); - delta_plus->ShiftLeft(exponent); - // Same for delta_minus. The adjustments if f == 2^p-1 are done later. - delta_minus->AssignUInt16(1); - delta_minus->ShiftLeft(exponent); - } -} - - -// See comments for InitialScaledStartValues -static void InitialScaledStartValuesNegativeExponentPositivePower( - uint64_t significand, int exponent, - int estimated_power, bool need_boundary_deltas, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus) { - // v = f * 2^e with e < 0, and with estimated_power >= 0. - // This means that e is close to 0 (have a look at how estimated_power is - // computed). - - // numerator = significand - // since v = significand * 2^exponent this is equivalent to - // numerator = v * / 2^-exponent - numerator->AssignUInt64(significand); - // denominator = 10^estimated_power * 2^-exponent (with exponent < 0) - denominator->AssignPowerUInt16(10, estimated_power); - denominator->ShiftLeft(-exponent); - - if (need_boundary_deltas) { - // Introduce a common denominator so that the deltas to the boundaries are - // integers. - denominator->ShiftLeft(1); - numerator->ShiftLeft(1); - // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common - // denominator (of 2) delta_plus equals 2^e. - // Given that the denominator already includes v's exponent the distance - // to the boundaries is simply 1. - delta_plus->AssignUInt16(1); - // Same for delta_minus. The adjustments if f == 2^p-1 are done later. - delta_minus->AssignUInt16(1); - } -} - - -// See comments for InitialScaledStartValues -static void InitialScaledStartValuesNegativeExponentNegativePower( - uint64_t significand, int exponent, - int estimated_power, bool need_boundary_deltas, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus) { - // Instead of multiplying the denominator with 10^estimated_power we - // multiply all values (numerator and deltas) by 10^-estimated_power. - - // Use numerator as temporary container for power_ten. - Bignum* power_ten = numerator; - power_ten->AssignPowerUInt16(10, -estimated_power); - - if (need_boundary_deltas) { - // Since power_ten == numerator we must make a copy of 10^estimated_power - // before we complete the computation of the numerator. - // delta_plus = delta_minus = 10^estimated_power - delta_plus->AssignBignum(*power_ten); - delta_minus->AssignBignum(*power_ten); - } - - // numerator = significand * 2 * 10^-estimated_power - // since v = significand * 2^exponent this is equivalent to - // numerator = v * 10^-estimated_power * 2 * 2^-exponent. - // Remember: numerator has been abused as power_ten. So no need to assign it - // to itself. - ASSERT(numerator == power_ten); - numerator->MultiplyByUInt64(significand); - - // denominator = 2 * 2^-exponent with exponent < 0. - denominator->AssignUInt16(1); - denominator->ShiftLeft(-exponent); - - if (need_boundary_deltas) { - // Introduce a common denominator so that the deltas to the boundaries are - // integers. - numerator->ShiftLeft(1); - denominator->ShiftLeft(1); - // With this shift the boundaries have their correct value, since - // delta_plus = 10^-estimated_power, and - // delta_minus = 10^-estimated_power. - // These assignments have been done earlier. - // The adjustments if f == 2^p-1 (lower boundary is closer) are done later. - } -} - - -// Let v = significand * 2^exponent. -// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator -// and denominator. The functions GenerateShortestDigits and -// GenerateCountedDigits will then convert this ratio to its decimal -// representation d, with the required accuracy. -// Then d * 10^estimated_power is the representation of v. -// (Note: the fraction and the estimated_power might get adjusted before -// generating the decimal representation.) -// -// The initial start values consist of: -// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power. -// - a scaled (common) denominator. -// optionally (used by GenerateShortestDigits to decide if it has the shortest -// decimal converting back to v): -// - v - m-: the distance to the lower boundary. -// - m+ - v: the distance to the upper boundary. -// -// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator. -// -// Let ep == estimated_power, then the returned values will satisfy: -// v / 10^ep = numerator / denominator. -// v's boundarys m- and m+: -// m- / 10^ep == v / 10^ep - delta_minus / denominator -// m+ / 10^ep == v / 10^ep + delta_plus / denominator -// Or in other words: -// m- == v - delta_minus * 10^ep / denominator; -// m+ == v + delta_plus * 10^ep / denominator; -// -// Since 10^(k-1) <= v < 10^k (with k == estimated_power) -// or 10^k <= v < 10^(k+1) -// we then have 0.1 <= numerator/denominator < 1 -// or 1 <= numerator/denominator < 10 -// -// It is then easy to kickstart the digit-generation routine. -// -// The boundary-deltas are only filled if the mode equals BIGNUM_DTOA_SHORTEST -// or BIGNUM_DTOA_SHORTEST_SINGLE. - -static void InitialScaledStartValues(uint64_t significand, - int exponent, - bool lower_boundary_is_closer, - int estimated_power, - bool need_boundary_deltas, - Bignum* numerator, - Bignum* denominator, - Bignum* delta_minus, - Bignum* delta_plus) { - if (exponent >= 0) { - InitialScaledStartValuesPositiveExponent( - significand, exponent, estimated_power, need_boundary_deltas, - numerator, denominator, delta_minus, delta_plus); - } else if (estimated_power >= 0) { - InitialScaledStartValuesNegativeExponentPositivePower( - significand, exponent, estimated_power, need_boundary_deltas, - numerator, denominator, delta_minus, delta_plus); - } else { - InitialScaledStartValuesNegativeExponentNegativePower( - significand, exponent, estimated_power, need_boundary_deltas, - numerator, denominator, delta_minus, delta_plus); - } - - if (need_boundary_deltas && lower_boundary_is_closer) { - // The lower boundary is closer at half the distance of "normal" numbers. - // Increase the common denominator and adapt all but the delta_minus. - denominator->ShiftLeft(1); // *2 - numerator->ShiftLeft(1); // *2 - delta_plus->ShiftLeft(1); // *2 - } -} - - -// This routine multiplies numerator/denominator so that its values lies in the -// range 1-10. That is after a call to this function we have: -// 1 <= (numerator + delta_plus) /denominator < 10. -// Let numerator the input before modification and numerator' the argument -// after modification, then the output-parameter decimal_point is such that -// numerator / denominator * 10^estimated_power == -// numerator' / denominator' * 10^(decimal_point - 1) -// In some cases estimated_power was too low, and this is already the case. We -// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k == -// estimated_power) but do not touch the numerator or denominator. -// Otherwise the routine multiplies the numerator and the deltas by 10. -static void FixupMultiply10(int estimated_power, bool is_even, - int* decimal_point, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus) { - bool in_range; - if (is_even) { - // For IEEE doubles half-way cases (in decimal system numbers ending with 5) - // are rounded to the closest floating-point number with even significand. - in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; - } else { - in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; - } - if (in_range) { - // Since numerator + delta_plus >= denominator we already have - // 1 <= numerator/denominator < 10. Simply update the estimated_power. - *decimal_point = estimated_power + 1; - } else { - *decimal_point = estimated_power; - numerator->Times10(); - if (Bignum::Equal(*delta_minus, *delta_plus)) { - delta_minus->Times10(); - delta_plus->AssignBignum(*delta_minus); - } else { - delta_minus->Times10(); - delta_plus->Times10(); - } - } -} - -} // namespace double_conversion diff --git a/3rdparty/double-conversion/bignum-dtoa.h b/3rdparty/double-conversion/bignum-dtoa.h deleted file mode 100644 index 34b961992d..0000000000 --- a/3rdparty/double-conversion/bignum-dtoa.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DOUBLE_CONVERSION_BIGNUM_DTOA_H_ -#define DOUBLE_CONVERSION_BIGNUM_DTOA_H_ - -#include "utils.h" - -namespace double_conversion { - -enum BignumDtoaMode { - // Return the shortest correct representation. - // For example the output of 0.299999999999999988897 is (the less accurate but - // correct) 0.3. - BIGNUM_DTOA_SHORTEST, - // Same as BIGNUM_DTOA_SHORTEST but for single-precision floats. - BIGNUM_DTOA_SHORTEST_SINGLE, - // Return a fixed number of digits after the decimal point. - // For instance fixed(0.1, 4) becomes 0.1000 - // If the input number is big, the output will be big. - BIGNUM_DTOA_FIXED, - // Return a fixed number of digits, no matter what the exponent is. - BIGNUM_DTOA_PRECISION -}; - -// Converts the given double 'v' to ascii. -// The result should be interpreted as buffer * 10^(point-length). -// The buffer will be null-terminated. -// -// The input v must be > 0 and different from NaN, and Infinity. -// -// The output depends on the given mode: -// - SHORTEST: produce the least amount of digits for which the internal -// identity requirement is still satisfied. If the digits are printed -// (together with the correct exponent) then reading this number will give -// 'v' again. The buffer will choose the representation that is closest to -// 'v'. If there are two at the same distance, than the number is round up. -// In this mode the 'requested_digits' parameter is ignored. -// - FIXED: produces digits necessary to print a given number with -// 'requested_digits' digits after the decimal point. The produced digits -// might be too short in which case the caller has to fill the gaps with '0's. -// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. -// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns -// buffer="2", point=0. -// Note: the length of the returned buffer has no meaning wrt the significance -// of its digits. That is, just because it contains '0's does not mean that -// any other digit would not satisfy the internal identity requirement. -// - PRECISION: produces 'requested_digits' where the first digit is not '0'. -// Even though the length of produced digits usually equals -// 'requested_digits', the function is allowed to return fewer digits, in -// which case the caller has to fill the missing digits with '0's. -// Halfway cases are again rounded up. -// 'BignumDtoa' expects the given buffer to be big enough to hold all digits -// and a terminating null-character. -void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, - Vector buffer, int* length, int* point); - -} // namespace double_conversion - -#endif // DOUBLE_CONVERSION_BIGNUM_DTOA_H_ diff --git a/3rdparty/double-conversion/bignum.cc b/3rdparty/double-conversion/bignum.cc deleted file mode 100644 index 747491a089..0000000000 --- a/3rdparty/double-conversion/bignum.cc +++ /dev/null @@ -1,764 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "bignum.h" -#include "utils.h" - -namespace double_conversion { - -Bignum::Bignum() - : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) { - for (int i = 0; i < kBigitCapacity; ++i) { - bigits_[i] = 0; - } -} - - -template -static int BitSize(S value) { - return 8 * sizeof(value); -} - -// Guaranteed to lie in one Bigit. -void Bignum::AssignUInt16(uint16_t value) { - ASSERT(kBigitSize >= BitSize(value)); - Zero(); - if (value == 0) return; - - EnsureCapacity(1); - bigits_[0] = value; - used_digits_ = 1; -} - - -void Bignum::AssignUInt64(uint64_t value) { - const int kUInt64Size = 64; - - Zero(); - if (value == 0) return; - - int needed_bigits = kUInt64Size / kBigitSize + 1; - EnsureCapacity(needed_bigits); - for (int i = 0; i < needed_bigits; ++i) { - bigits_[i] = value & kBigitMask; - value = value >> kBigitSize; - } - used_digits_ = needed_bigits; - Clamp(); -} - - -void Bignum::AssignBignum(const Bignum& other) { - exponent_ = other.exponent_; - for (int i = 0; i < other.used_digits_; ++i) { - bigits_[i] = other.bigits_[i]; - } - // Clear the excess digits (if there were any). - for (int i = other.used_digits_; i < used_digits_; ++i) { - bigits_[i] = 0; - } - used_digits_ = other.used_digits_; -} - - -static uint64_t ReadUInt64(Vector buffer, - int from, - int digits_to_read) { - uint64_t result = 0; - for (int i = from; i < from + digits_to_read; ++i) { - int digit = buffer[i] - '0'; - ASSERT(0 <= digit && digit <= 9); - result = result * 10 + digit; - } - return result; -} - - -void Bignum::AssignDecimalString(Vector value) { - // 2^64 = 18446744073709551616 > 10^19 - const int kMaxUint64DecimalDigits = 19; - Zero(); - int length = value.length(); - int pos = 0; - // Let's just say that each digit needs 4 bits. - while (length >= kMaxUint64DecimalDigits) { - uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits); - pos += kMaxUint64DecimalDigits; - length -= kMaxUint64DecimalDigits; - MultiplyByPowerOfTen(kMaxUint64DecimalDigits); - AddUInt64(digits); - } - uint64_t digits = ReadUInt64(value, pos, length); - MultiplyByPowerOfTen(length); - AddUInt64(digits); - Clamp(); -} - - -static int HexCharValue(char c) { - if ('0' <= c && c <= '9') return c - '0'; - if ('a' <= c && c <= 'f') return 10 + c - 'a'; - if ('A' <= c && c <= 'F') return 10 + c - 'A'; - UNREACHABLE(); - return 0; // To make compiler happy. -} - - -void Bignum::AssignHexString(Vector value) { - Zero(); - int length = value.length(); - - int needed_bigits = length * 4 / kBigitSize + 1; - EnsureCapacity(needed_bigits); - int string_index = length - 1; - for (int i = 0; i < needed_bigits - 1; ++i) { - // These bigits are guaranteed to be "full". - Chunk current_bigit = 0; - for (int j = 0; j < kBigitSize / 4; j++) { - current_bigit += HexCharValue(value[string_index--]) << (j * 4); - } - bigits_[i] = current_bigit; - } - used_digits_ = needed_bigits - 1; - - Chunk most_significant_bigit = 0; // Could be = 0; - for (int j = 0; j <= string_index; ++j) { - most_significant_bigit <<= 4; - most_significant_bigit += HexCharValue(value[j]); - } - if (most_significant_bigit != 0) { - bigits_[used_digits_] = most_significant_bigit; - used_digits_++; - } - Clamp(); -} - - -void Bignum::AddUInt64(uint64_t operand) { - if (operand == 0) return; - Bignum other; - other.AssignUInt64(operand); - AddBignum(other); -} - - -void Bignum::AddBignum(const Bignum& other) { - ASSERT(IsClamped()); - ASSERT(other.IsClamped()); - - // If this has a greater exponent than other append zero-bigits to this. - // After this call exponent_ <= other.exponent_. - Align(other); - - // There are two possibilities: - // aaaaaaaaaaa 0000 (where the 0s represent a's exponent) - // bbbbb 00000000 - // ---------------- - // ccccccccccc 0000 - // or - // aaaaaaaaaa 0000 - // bbbbbbbbb 0000000 - // ----------------- - // cccccccccccc 0000 - // In both cases we might need a carry bigit. - - EnsureCapacity(1 + Max(BigitLength(), other.BigitLength()) - exponent_); - Chunk carry = 0; - int bigit_pos = other.exponent_ - exponent_; - ASSERT(bigit_pos >= 0); - for (int i = 0; i < other.used_digits_; ++i) { - Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry; - bigits_[bigit_pos] = sum & kBigitMask; - carry = sum >> kBigitSize; - bigit_pos++; - } - - while (carry != 0) { - Chunk sum = bigits_[bigit_pos] + carry; - bigits_[bigit_pos] = sum & kBigitMask; - carry = sum >> kBigitSize; - bigit_pos++; - } - used_digits_ = Max(bigit_pos, used_digits_); - ASSERT(IsClamped()); -} - - -void Bignum::SubtractBignum(const Bignum& other) { - ASSERT(IsClamped()); - ASSERT(other.IsClamped()); - // We require this to be bigger than other. - ASSERT(LessEqual(other, *this)); - - Align(other); - - int offset = other.exponent_ - exponent_; - Chunk borrow = 0; - int i; - for (i = 0; i < other.used_digits_; ++i) { - ASSERT((borrow == 0) || (borrow == 1)); - Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow; - bigits_[i + offset] = difference & kBigitMask; - borrow = difference >> (kChunkSize - 1); - } - while (borrow != 0) { - Chunk difference = bigits_[i + offset] - borrow; - bigits_[i + offset] = difference & kBigitMask; - borrow = difference >> (kChunkSize - 1); - ++i; - } - Clamp(); -} - - -void Bignum::ShiftLeft(int shift_amount) { - if (used_digits_ == 0) return; - exponent_ += shift_amount / kBigitSize; - int local_shift = shift_amount % kBigitSize; - EnsureCapacity(used_digits_ + 1); - BigitsShiftLeft(local_shift); -} - - -void Bignum::MultiplyByUInt32(uint32_t factor) { - if (factor == 1) return; - if (factor == 0) { - Zero(); - return; - } - if (used_digits_ == 0) return; - - // The product of a bigit with the factor is of size kBigitSize + 32. - // Assert that this number + 1 (for the carry) fits into double chunk. - ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1); - DoubleChunk carry = 0; - for (int i = 0; i < used_digits_; ++i) { - DoubleChunk product = static_cast(factor) * bigits_[i] + carry; - bigits_[i] = static_cast(product & kBigitMask); - carry = (product >> kBigitSize); - } - while (carry != 0) { - EnsureCapacity(used_digits_ + 1); - bigits_[used_digits_] = carry & kBigitMask; - used_digits_++; - carry >>= kBigitSize; - } -} - - -void Bignum::MultiplyByUInt64(uint64_t factor) { - if (factor == 1) return; - if (factor == 0) { - Zero(); - return; - } - ASSERT(kBigitSize < 32); - uint64_t carry = 0; - uint64_t low = factor & 0xFFFFFFFF; - uint64_t high = factor >> 32; - for (int i = 0; i < used_digits_; ++i) { - uint64_t product_low = low * bigits_[i]; - uint64_t product_high = high * bigits_[i]; - uint64_t tmp = (carry & kBigitMask) + product_low; - bigits_[i] = tmp & kBigitMask; - carry = (carry >> kBigitSize) + (tmp >> kBigitSize) + - (product_high << (32 - kBigitSize)); - } - while (carry != 0) { - EnsureCapacity(used_digits_ + 1); - bigits_[used_digits_] = carry & kBigitMask; - used_digits_++; - carry >>= kBigitSize; - } -} - - -void Bignum::MultiplyByPowerOfTen(int exponent) { - const uint64_t kFive27 = UINT64_2PART_C(0x6765c793, fa10079d); - const uint16_t kFive1 = 5; - const uint16_t kFive2 = kFive1 * 5; - const uint16_t kFive3 = kFive2 * 5; - const uint16_t kFive4 = kFive3 * 5; - const uint16_t kFive5 = kFive4 * 5; - const uint16_t kFive6 = kFive5 * 5; - const uint32_t kFive7 = kFive6 * 5; - const uint32_t kFive8 = kFive7 * 5; - const uint32_t kFive9 = kFive8 * 5; - const uint32_t kFive10 = kFive9 * 5; - const uint32_t kFive11 = kFive10 * 5; - const uint32_t kFive12 = kFive11 * 5; - const uint32_t kFive13 = kFive12 * 5; - const uint32_t kFive1_to_12[] = - { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6, - kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 }; - - ASSERT(exponent >= 0); - if (exponent == 0) return; - if (used_digits_ == 0) return; - - // We shift by exponent at the end just before returning. - int remaining_exponent = exponent; - while (remaining_exponent >= 27) { - MultiplyByUInt64(kFive27); - remaining_exponent -= 27; - } - while (remaining_exponent >= 13) { - MultiplyByUInt32(kFive13); - remaining_exponent -= 13; - } - if (remaining_exponent > 0) { - MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]); - } - ShiftLeft(exponent); -} - - -void Bignum::Square() { - ASSERT(IsClamped()); - int product_length = 2 * used_digits_; - EnsureCapacity(product_length); - - // Comba multiplication: compute each column separately. - // Example: r = a2a1a0 * b2b1b0. - // r = 1 * a0b0 + - // 10 * (a1b0 + a0b1) + - // 100 * (a2b0 + a1b1 + a0b2) + - // 1000 * (a2b1 + a1b2) + - // 10000 * a2b2 - // - // In the worst case we have to accumulate nb-digits products of digit*digit. - // - // Assert that the additional number of bits in a DoubleChunk are enough to - // sum up used_digits of Bigit*Bigit. - if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) { - UNIMPLEMENTED(); - } - DoubleChunk accumulator = 0; - // First shift the digits so we don't overwrite them. - int copy_offset = used_digits_; - for (int i = 0; i < used_digits_; ++i) { - bigits_[copy_offset + i] = bigits_[i]; - } - // We have two loops to avoid some 'if's in the loop. - for (int i = 0; i < used_digits_; ++i) { - // Process temporary digit i with power i. - // The sum of the two indices must be equal to i. - int bigit_index1 = i; - int bigit_index2 = 0; - // Sum all of the sub-products. - while (bigit_index1 >= 0) { - Chunk chunk1 = bigits_[copy_offset + bigit_index1]; - Chunk chunk2 = bigits_[copy_offset + bigit_index2]; - accumulator += static_cast(chunk1) * chunk2; - bigit_index1--; - bigit_index2++; - } - bigits_[i] = static_cast(accumulator) & kBigitMask; - accumulator >>= kBigitSize; - } - for (int i = used_digits_; i < product_length; ++i) { - int bigit_index1 = used_digits_ - 1; - int bigit_index2 = i - bigit_index1; - // Invariant: sum of both indices is again equal to i. - // Inner loop runs 0 times on last iteration, emptying accumulator. - while (bigit_index2 < used_digits_) { - Chunk chunk1 = bigits_[copy_offset + bigit_index1]; - Chunk chunk2 = bigits_[copy_offset + bigit_index2]; - accumulator += static_cast(chunk1) * chunk2; - bigit_index1--; - bigit_index2++; - } - // The overwritten bigits_[i] will never be read in further loop iterations, - // because bigit_index1 and bigit_index2 are always greater - // than i - used_digits_. - bigits_[i] = static_cast(accumulator) & kBigitMask; - accumulator >>= kBigitSize; - } - // Since the result was guaranteed to lie inside the number the - // accumulator must be 0 now. - ASSERT(accumulator == 0); - - // Don't forget to update the used_digits and the exponent. - used_digits_ = product_length; - exponent_ *= 2; - Clamp(); -} - - -void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { - ASSERT(base != 0); - ASSERT(power_exponent >= 0); - if (power_exponent == 0) { - AssignUInt16(1); - return; - } - Zero(); - int shifts = 0; - // We expect base to be in range 2-32, and most often to be 10. - // It does not make much sense to implement different algorithms for counting - // the bits. - while ((base & 1) == 0) { - base >>= 1; - shifts++; - } - int bit_size = 0; - int tmp_base = base; - while (tmp_base != 0) { - tmp_base >>= 1; - bit_size++; - } - int final_size = bit_size * power_exponent; - // 1 extra bigit for the shifting, and one for rounded final_size. - EnsureCapacity(final_size / kBigitSize + 2); - - // Left to Right exponentiation. - int mask = 1; - while (power_exponent >= mask) mask <<= 1; - - // The mask is now pointing to the bit above the most significant 1-bit of - // power_exponent. - // Get rid of first 1-bit; - mask >>= 2; - uint64_t this_value = base; - - bool delayed_multipliciation = false; - const uint64_t max_32bits = 0xFFFFFFFF; - while (mask != 0 && this_value <= max_32bits) { - this_value = this_value * this_value; - // Verify that there is enough space in this_value to perform the - // multiplication. The first bit_size bits must be 0. - if ((power_exponent & mask) != 0) { - uint64_t base_bits_mask = - ~((static_cast(1) << (64 - bit_size)) - 1); - bool high_bits_zero = (this_value & base_bits_mask) == 0; - if (high_bits_zero) { - this_value *= base; - } else { - delayed_multipliciation = true; - } - } - mask >>= 1; - } - AssignUInt64(this_value); - if (delayed_multipliciation) { - MultiplyByUInt32(base); - } - - // Now do the same thing as a bignum. - while (mask != 0) { - Square(); - if ((power_exponent & mask) != 0) { - MultiplyByUInt32(base); - } - mask >>= 1; - } - - // And finally add the saved shifts. - ShiftLeft(shifts * power_exponent); -} - - -// Precondition: this/other < 16bit. -uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { - ASSERT(IsClamped()); - ASSERT(other.IsClamped()); - ASSERT(other.used_digits_ > 0); - - // Easy case: if we have less digits than the divisor than the result is 0. - // Note: this handles the case where this == 0, too. - if (BigitLength() < other.BigitLength()) { - return 0; - } - - Align(other); - - uint16_t result = 0; - - // Start by removing multiples of 'other' until both numbers have the same - // number of digits. - while (BigitLength() > other.BigitLength()) { - // This naive approach is extremely inefficient if the this divided other - // might be big. This function is implemented for doubleToString where - // the result should be small (less than 10). - ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16)); - // Remove the multiples of the first digit. - // Example this = 23 and other equals 9. -> Remove 2 multiples. - result += bigits_[used_digits_ - 1]; - SubtractTimes(other, bigits_[used_digits_ - 1]); - } - - ASSERT(BigitLength() == other.BigitLength()); - - // Both bignums are at the same length now. - // Since other has more than 0 digits we know that the access to - // bigits_[used_digits_ - 1] is safe. - Chunk this_bigit = bigits_[used_digits_ - 1]; - Chunk other_bigit = other.bigits_[other.used_digits_ - 1]; - - if (other.used_digits_ == 1) { - // Shortcut for easy (and common) case. - int quotient = this_bigit / other_bigit; - bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient; - result += quotient; - Clamp(); - return result; - } - - int division_estimate = this_bigit / (other_bigit + 1); - result += division_estimate; - SubtractTimes(other, division_estimate); - - if (other_bigit * (division_estimate + 1) > this_bigit) { - // No need to even try to subtract. Even if other's remaining digits were 0 - // another subtraction would be too much. - return result; - } - - while (LessEqual(other, *this)) { - SubtractBignum(other); - result++; - } - return result; -} - - -template -static int SizeInHexChars(S number) { - ASSERT(number > 0); - int result = 0; - while (number != 0) { - number >>= 4; - result++; - } - return result; -} - - -static char HexCharOfValue(int value) { - ASSERT(0 <= value && value <= 16); - if (value < 10) return value + '0'; - return value - 10 + 'A'; -} - - -bool Bignum::ToHexString(char* buffer, int buffer_size) const { - ASSERT(IsClamped()); - // Each bigit must be printable as separate hex-character. - ASSERT(kBigitSize % 4 == 0); - const int kHexCharsPerBigit = kBigitSize / 4; - - if (used_digits_ == 0) { - if (buffer_size < 2) return false; - buffer[0] = '0'; - buffer[1] = '\0'; - return true; - } - // We add 1 for the terminating '\0' character. - int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit + - SizeInHexChars(bigits_[used_digits_ - 1]) + 1; - if (needed_chars > buffer_size) return false; - int string_index = needed_chars - 1; - buffer[string_index--] = '\0'; - for (int i = 0; i < exponent_; ++i) { - for (int j = 0; j < kHexCharsPerBigit; ++j) { - buffer[string_index--] = '0'; - } - } - for (int i = 0; i < used_digits_ - 1; ++i) { - Chunk current_bigit = bigits_[i]; - for (int j = 0; j < kHexCharsPerBigit; ++j) { - buffer[string_index--] = HexCharOfValue(current_bigit & 0xF); - current_bigit >>= 4; - } - } - // And finally the last bigit. - Chunk most_significant_bigit = bigits_[used_digits_ - 1]; - while (most_significant_bigit != 0) { - buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF); - most_significant_bigit >>= 4; - } - return true; -} - - -Bignum::Chunk Bignum::BigitAt(int index) const { - if (index >= BigitLength()) return 0; - if (index < exponent_) return 0; - return bigits_[index - exponent_]; -} - - -int Bignum::Compare(const Bignum& a, const Bignum& b) { - ASSERT(a.IsClamped()); - ASSERT(b.IsClamped()); - int bigit_length_a = a.BigitLength(); - int bigit_length_b = b.BigitLength(); - if (bigit_length_a < bigit_length_b) return -1; - if (bigit_length_a > bigit_length_b) return +1; - for (int i = bigit_length_a - 1; i >= Min(a.exponent_, b.exponent_); --i) { - Chunk bigit_a = a.BigitAt(i); - Chunk bigit_b = b.BigitAt(i); - if (bigit_a < bigit_b) return -1; - if (bigit_a > bigit_b) return +1; - // Otherwise they are equal up to this digit. Try the next digit. - } - return 0; -} - - -int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) { - ASSERT(a.IsClamped()); - ASSERT(b.IsClamped()); - ASSERT(c.IsClamped()); - if (a.BigitLength() < b.BigitLength()) { - return PlusCompare(b, a, c); - } - if (a.BigitLength() + 1 < c.BigitLength()) return -1; - if (a.BigitLength() > c.BigitLength()) return +1; - // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than - // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one - // of 'a'. - if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) { - return -1; - } - - Chunk borrow = 0; - // Starting at min_exponent all digits are == 0. So no need to compare them. - int min_exponent = Min(Min(a.exponent_, b.exponent_), c.exponent_); - for (int i = c.BigitLength() - 1; i >= min_exponent; --i) { - Chunk chunk_a = a.BigitAt(i); - Chunk chunk_b = b.BigitAt(i); - Chunk chunk_c = c.BigitAt(i); - Chunk sum = chunk_a + chunk_b; - if (sum > chunk_c + borrow) { - return +1; - } else { - borrow = chunk_c + borrow - sum; - if (borrow > 1) return -1; - borrow <<= kBigitSize; - } - } - if (borrow == 0) return 0; - return -1; -} - - -void Bignum::Clamp() { - while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) { - used_digits_--; - } - if (used_digits_ == 0) { - // Zero. - exponent_ = 0; - } -} - - -bool Bignum::IsClamped() const { - return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0; -} - - -void Bignum::Zero() { - for (int i = 0; i < used_digits_; ++i) { - bigits_[i] = 0; - } - used_digits_ = 0; - exponent_ = 0; -} - - -void Bignum::Align(const Bignum& other) { - if (exponent_ > other.exponent_) { - // If "X" represents a "hidden" digit (by the exponent) then we are in the - // following case (a == this, b == other): - // a: aaaaaaXXXX or a: aaaaaXXX - // b: bbbbbbX b: bbbbbbbbXX - // We replace some of the hidden digits (X) of a with 0 digits. - // a: aaaaaa000X or a: aaaaa0XX - int zero_digits = exponent_ - other.exponent_; - EnsureCapacity(used_digits_ + zero_digits); - for (int i = used_digits_ - 1; i >= 0; --i) { - bigits_[i + zero_digits] = bigits_[i]; - } - for (int i = 0; i < zero_digits; ++i) { - bigits_[i] = 0; - } - used_digits_ += zero_digits; - exponent_ -= zero_digits; - ASSERT(used_digits_ >= 0); - ASSERT(exponent_ >= 0); - } -} - - -void Bignum::BigitsShiftLeft(int shift_amount) { - ASSERT(shift_amount < kBigitSize); - ASSERT(shift_amount >= 0); - Chunk carry = 0; - for (int i = 0; i < used_digits_; ++i) { - Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount); - bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask; - carry = new_carry; - } - if (carry != 0) { - bigits_[used_digits_] = carry; - used_digits_++; - } -} - - -void Bignum::SubtractTimes(const Bignum& other, int factor) { - ASSERT(exponent_ <= other.exponent_); - if (factor < 3) { - for (int i = 0; i < factor; ++i) { - SubtractBignum(other); - } - return; - } - Chunk borrow = 0; - int exponent_diff = other.exponent_ - exponent_; - for (int i = 0; i < other.used_digits_; ++i) { - DoubleChunk product = static_cast(factor) * other.bigits_[i]; - DoubleChunk remove = borrow + product; - Chunk difference = bigits_[i + exponent_diff] - (remove & kBigitMask); - bigits_[i + exponent_diff] = difference & kBigitMask; - borrow = static_cast((difference >> (kChunkSize - 1)) + - (remove >> kBigitSize)); - } - for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) { - if (borrow == 0) return; - Chunk difference = bigits_[i] - borrow; - bigits_[i] = difference & kBigitMask; - borrow = difference >> (kChunkSize - 1); - ++i; - } - Clamp(); -} - - -} // namespace double_conversion diff --git a/3rdparty/double-conversion/bignum.h b/3rdparty/double-conversion/bignum.h deleted file mode 100644 index 5ec3544f57..0000000000 --- a/3rdparty/double-conversion/bignum.h +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DOUBLE_CONVERSION_BIGNUM_H_ -#define DOUBLE_CONVERSION_BIGNUM_H_ - -#include "utils.h" - -namespace double_conversion { - -class Bignum { - public: - // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately. - // This bignum can encode much bigger numbers, since it contains an - // exponent. - static const int kMaxSignificantBits = 3584; - - Bignum(); - void AssignUInt16(uint16_t value); - void AssignUInt64(uint64_t value); - void AssignBignum(const Bignum& other); - - void AssignDecimalString(Vector value); - void AssignHexString(Vector value); - - void AssignPowerUInt16(uint16_t base, int exponent); - - void AddUInt16(uint16_t operand); - void AddUInt64(uint64_t operand); - void AddBignum(const Bignum& other); - // Precondition: this >= other. - void SubtractBignum(const Bignum& other); - - void Square(); - void ShiftLeft(int shift_amount); - void MultiplyByUInt32(uint32_t factor); - void MultiplyByUInt64(uint64_t factor); - void MultiplyByPowerOfTen(int exponent); - void Times10() { return MultiplyByUInt32(10); } - // Pseudocode: - // int result = this / other; - // this = this % other; - // In the worst case this function is in O(this/other). - uint16_t DivideModuloIntBignum(const Bignum& other); - - bool ToHexString(char* buffer, int buffer_size) const; - - // Returns - // -1 if a < b, - // 0 if a == b, and - // +1 if a > b. - static int Compare(const Bignum& a, const Bignum& b); - static bool Equal(const Bignum& a, const Bignum& b) { - return Compare(a, b) == 0; - } - static bool LessEqual(const Bignum& a, const Bignum& b) { - return Compare(a, b) <= 0; - } - static bool Less(const Bignum& a, const Bignum& b) { - return Compare(a, b) < 0; - } - // Returns Compare(a + b, c); - static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c); - // Returns a + b == c - static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) { - return PlusCompare(a, b, c) == 0; - } - // Returns a + b <= c - static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) { - return PlusCompare(a, b, c) <= 0; - } - // Returns a + b < c - static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) { - return PlusCompare(a, b, c) < 0; - } - private: - typedef uint32_t Chunk; - typedef uint64_t DoubleChunk; - - static const int kChunkSize = sizeof(Chunk) * 8; - static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8; - // With bigit size of 28 we loose some bits, but a double still fits easily - // into two chunks, and more importantly we can use the Comba multiplication. - static const int kBigitSize = 28; - static const Chunk kBigitMask = (1 << kBigitSize) - 1; - // Every instance allocates kBigitLength chunks on the stack. Bignums cannot - // grow. There are no checks if the stack-allocated space is sufficient. - static const int kBigitCapacity = kMaxSignificantBits / kBigitSize; - - void EnsureCapacity(int size) { - if (size > kBigitCapacity) { - UNREACHABLE(); - } - } - void Align(const Bignum& other); - void Clamp(); - bool IsClamped() const; - void Zero(); - // Requires this to have enough capacity (no tests done). - // Updates used_digits_ if necessary. - // shift_amount must be < kBigitSize. - void BigitsShiftLeft(int shift_amount); - // BigitLength includes the "hidden" digits encoded in the exponent. - int BigitLength() const { return used_digits_ + exponent_; } - Chunk BigitAt(int index) const; - void SubtractTimes(const Bignum& other, int factor); - - Chunk bigits_buffer_[kBigitCapacity]; - // A vector backed by bigits_buffer_. This way accesses to the array are - // checked for out-of-bounds errors. - Vector bigits_; - int used_digits_; - // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize). - int exponent_; - - DISALLOW_COPY_AND_ASSIGN(Bignum); -}; - -} // namespace double_conversion - -#endif // DOUBLE_CONVERSION_BIGNUM_H_ diff --git a/3rdparty/double-conversion/cached-powers.cc b/3rdparty/double-conversion/cached-powers.cc deleted file mode 100644 index c676429194..0000000000 --- a/3rdparty/double-conversion/cached-powers.cc +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include -#include - -#include "utils.h" - -#include "cached-powers.h" - -namespace double_conversion { - -struct CachedPower { - uint64_t significand; - int16_t binary_exponent; - int16_t decimal_exponent; -}; - -static const CachedPower kCachedPowers[] = { - {UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348}, - {UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340}, - {UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332}, - {UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324}, - {UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316}, - {UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308}, - {UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300}, - {UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292}, - {UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284}, - {UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276}, - {UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268}, - {UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260}, - {UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252}, - {UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244}, - {UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236}, - {UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228}, - {UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220}, - {UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212}, - {UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204}, - {UINT64_2PART_C(0xef340a98, 172aace5), -715, -196}, - {UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188}, - {UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180}, - {UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172}, - {UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164}, - {UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156}, - {UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148}, - {UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140}, - {UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132}, - {UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124}, - {UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116}, - {UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108}, - {UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100}, - {UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92}, - {UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84}, - {UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76}, - {UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68}, - {UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60}, - {UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52}, - {UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44}, - {UINT64_2PART_C(0xaa242499, 697392d3), -183, -36}, - {UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28}, - {UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20}, - {UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12}, - {UINT64_2PART_C(0xd1b71758, e219652c), -77, -4}, - {UINT64_2PART_C(0x9c400000, 00000000), -50, 4}, - {UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12}, - {UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20}, - {UINT64_2PART_C(0x813f3978, f8940984), 30, 28}, - {UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36}, - {UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44}, - {UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52}, - {UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60}, - {UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68}, - {UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76}, - {UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84}, - {UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92}, - {UINT64_2PART_C(0x924d692c, a61be758), 269, 100}, - {UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108}, - {UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116}, - {UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124}, - {UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132}, - {UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140}, - {UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148}, - {UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156}, - {UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164}, - {UINT64_2PART_C(0xa59bc234, db398c25), 508, 172}, - {UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180}, - {UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188}, - {UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196}, - {UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204}, - {UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212}, - {UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220}, - {UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228}, - {UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236}, - {UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244}, - {UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252}, - {UINT64_2PART_C(0xd01fef10, a657842c), 800, 260}, - {UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268}, - {UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276}, - {UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284}, - {UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292}, - {UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300}, - {UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308}, - {UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316}, - {UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324}, - {UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332}, - {UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340}, -}; - -static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers); -static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent. -static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) -// Difference between the decimal exponents in the table above. -const int PowersOfTenCache::kDecimalExponentDistance = 8; -const int PowersOfTenCache::kMinDecimalExponent = -348; -const int PowersOfTenCache::kMaxDecimalExponent = 340; - -void PowersOfTenCache::GetCachedPowerForBinaryExponentRange( - int min_exponent, - int max_exponent, - DiyFp* power, - int* decimal_exponent) { - int kQ = DiyFp::kSignificandSize; - double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10); - int foo = kCachedPowersOffset; - int index = - (foo + static_cast(k) - 1) / kDecimalExponentDistance + 1; - ASSERT(0 <= index && index < kCachedPowersLength); - CachedPower cached_power = kCachedPowers[index]; - ASSERT(min_exponent <= cached_power.binary_exponent); - ASSERT(cached_power.binary_exponent <= max_exponent); - *decimal_exponent = cached_power.decimal_exponent; - *power = DiyFp(cached_power.significand, cached_power.binary_exponent); -} - - -void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent, - DiyFp* power, - int* found_exponent) { - ASSERT(kMinDecimalExponent <= requested_exponent); - ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance); - int index = - (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance; - CachedPower cached_power = kCachedPowers[index]; - *power = DiyFp(cached_power.significand, cached_power.binary_exponent); - *found_exponent = cached_power.decimal_exponent; - ASSERT(*found_exponent <= requested_exponent); - ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance); -} - -} // namespace double_conversion diff --git a/3rdparty/double-conversion/cached-powers.h b/3rdparty/double-conversion/cached-powers.h deleted file mode 100644 index 61a50614cf..0000000000 --- a/3rdparty/double-conversion/cached-powers.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DOUBLE_CONVERSION_CACHED_POWERS_H_ -#define DOUBLE_CONVERSION_CACHED_POWERS_H_ - -#include "diy-fp.h" - -namespace double_conversion { - -class PowersOfTenCache { - public: - - // Not all powers of ten are cached. The decimal exponent of two neighboring - // cached numbers will differ by kDecimalExponentDistance. - static const int kDecimalExponentDistance; - - static const int kMinDecimalExponent; - static const int kMaxDecimalExponent; - - // Returns a cached power-of-ten with a binary exponent in the range - // [min_exponent; max_exponent] (boundaries included). - static void GetCachedPowerForBinaryExponentRange(int min_exponent, - int max_exponent, - DiyFp* power, - int* decimal_exponent); - - // Returns a cached power of ten x ~= 10^k such that - // k <= decimal_exponent < k + kCachedPowersDecimalDistance. - // The given decimal_exponent must satisfy - // kMinDecimalExponent <= requested_exponent, and - // requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance. - static void GetCachedPowerForDecimalExponent(int requested_exponent, - DiyFp* power, - int* found_exponent); -}; - -} // namespace double_conversion - -#endif // DOUBLE_CONVERSION_CACHED_POWERS_H_ diff --git a/3rdparty/double-conversion/diy-fp.cc b/3rdparty/double-conversion/diy-fp.cc deleted file mode 100644 index ddd1891b16..0000000000 --- a/3rdparty/double-conversion/diy-fp.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -#include "diy-fp.h" -#include "utils.h" - -namespace double_conversion { - -void DiyFp::Multiply(const DiyFp& other) { - // Simply "emulates" a 128 bit multiplication. - // However: the resulting number only contains 64 bits. The least - // significant 64 bits are only used for rounding the most significant 64 - // bits. - const uint64_t kM32 = 0xFFFFFFFFU; - uint64_t a = f_ >> 32; - uint64_t b = f_ & kM32; - uint64_t c = other.f_ >> 32; - uint64_t d = other.f_ & kM32; - uint64_t ac = a * c; - uint64_t bc = b * c; - uint64_t ad = a * d; - uint64_t bd = b * d; - uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32); - // By adding 1U << 31 to tmp we round the final result. - // Halfway cases will be round up. - tmp += 1U << 31; - uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); - e_ += other.e_ + 64; - f_ = result_f; -} - -} // namespace double_conversion diff --git a/3rdparty/double-conversion/diy-fp.h b/3rdparty/double-conversion/diy-fp.h deleted file mode 100644 index 9dcf8fbdba..0000000000 --- a/3rdparty/double-conversion/diy-fp.h +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DOUBLE_CONVERSION_DIY_FP_H_ -#define DOUBLE_CONVERSION_DIY_FP_H_ - -#include "utils.h" - -namespace double_conversion { - -// This "Do It Yourself Floating Point" class implements a floating-point number -// with a uint64 significand and an int exponent. Normalized DiyFp numbers will -// have the most significant bit of the significand set. -// Multiplication and Subtraction do not normalize their results. -// DiyFp are not designed to contain special doubles (NaN and Infinity). -class DiyFp { - public: - static const int kSignificandSize = 64; - - DiyFp() : f_(0), e_(0) {} - DiyFp(uint64_t f, int e) : f_(f), e_(e) {} - - // this = this - other. - // The exponents of both numbers must be the same and the significand of this - // must be bigger than the significand of other. - // The result will not be normalized. - void Subtract(const DiyFp& other) { - ASSERT(e_ == other.e_); - ASSERT(f_ >= other.f_); - f_ -= other.f_; - } - - // Returns a - b. - // The exponents of both numbers must be the same and this must be bigger - // than other. The result will not be normalized. - static DiyFp Minus(const DiyFp& a, const DiyFp& b) { - DiyFp result = a; - result.Subtract(b); - return result; - } - - - // this = this * other. - void Multiply(const DiyFp& other); - - // returns a * b; - static DiyFp Times(const DiyFp& a, const DiyFp& b) { - DiyFp result = a; - result.Multiply(b); - return result; - } - - void Normalize() { - ASSERT(f_ != 0); - uint64_t f = f_; - int e = e_; - - // This method is mainly called for normalizing boundaries. In general - // boundaries need to be shifted by 10 bits. We thus optimize for this case. - const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000); - while ((f & k10MSBits) == 0) { - f <<= 10; - e -= 10; - } - while ((f & kUint64MSB) == 0) { - f <<= 1; - e--; - } - f_ = f; - e_ = e; - } - - static DiyFp Normalize(const DiyFp& a) { - DiyFp result = a; - result.Normalize(); - return result; - } - - uint64_t f() const { return f_; } - int e() const { return e_; } - - void set_f(uint64_t new_value) { f_ = new_value; } - void set_e(int new_value) { e_ = new_value; } - - private: - static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000); - - uint64_t f_; - int e_; -}; - -} // namespace double_conversion - -#endif // DOUBLE_CONVERSION_DIY_FP_H_ diff --git a/3rdparty/double-conversion/double-conversion.cc b/3rdparty/double-conversion/double-conversion.cc deleted file mode 100644 index a79fe92d22..0000000000 --- a/3rdparty/double-conversion/double-conversion.cc +++ /dev/null @@ -1,889 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include - -#include "double-conversion.h" - -#include "bignum-dtoa.h" -#include "fast-dtoa.h" -#include "fixed-dtoa.h" -#include "ieee.h" -#include "strtod.h" -#include "utils.h" - -namespace double_conversion { - -const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() { - int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN; - static DoubleToStringConverter converter(flags, - "Infinity", - "NaN", - 'e', - -6, 21, - 6, 0); - return converter; -} - - -bool DoubleToStringConverter::HandleSpecialValues( - double value, - StringBuilder* result_builder) const { - Double double_inspect(value); - if (double_inspect.IsInfinite()) { - if (infinity_symbol_ == NULL) return false; - if (value < 0) { - result_builder->AddCharacter('-'); - } - result_builder->AddString(infinity_symbol_); - return true; - } - if (double_inspect.IsNan()) { - if (nan_symbol_ == NULL) return false; - result_builder->AddString(nan_symbol_); - return true; - } - return false; -} - - -void DoubleToStringConverter::CreateExponentialRepresentation( - const char* decimal_digits, - int length, - int exponent, - StringBuilder* result_builder) const { - ASSERT(length != 0); - result_builder->AddCharacter(decimal_digits[0]); - if (length != 1) { - result_builder->AddCharacter('.'); - result_builder->AddSubstring(&decimal_digits[1], length-1); - } - result_builder->AddCharacter(exponent_character_); - if (exponent < 0) { - result_builder->AddCharacter('-'); - exponent = -exponent; - } else { - if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) { - result_builder->AddCharacter('+'); - } - } - if (exponent == 0) { - result_builder->AddCharacter('0'); - return; - } - ASSERT(exponent < 1e4); - const int kMaxExponentLength = 5; - char buffer[kMaxExponentLength + 1]; - buffer[kMaxExponentLength] = '\0'; - int first_char_pos = kMaxExponentLength; - while (exponent > 0) { - buffer[--first_char_pos] = '0' + (exponent % 10); - exponent /= 10; - } - result_builder->AddSubstring(&buffer[first_char_pos], - kMaxExponentLength - first_char_pos); -} - - -void DoubleToStringConverter::CreateDecimalRepresentation( - const char* decimal_digits, - int length, - int decimal_point, - int digits_after_point, - StringBuilder* result_builder) const { - // Create a representation that is padded with zeros if needed. - if (decimal_point <= 0) { - // "0.00000decimal_rep". - result_builder->AddCharacter('0'); - if (digits_after_point > 0) { - result_builder->AddCharacter('.'); - result_builder->AddPadding('0', -decimal_point); - ASSERT(length <= digits_after_point - (-decimal_point)); - result_builder->AddSubstring(decimal_digits, length); - int remaining_digits = digits_after_point - (-decimal_point) - length; - result_builder->AddPadding('0', remaining_digits); - } - } else if (decimal_point >= length) { - // "decimal_rep0000.00000" or "decimal_rep.0000" - result_builder->AddSubstring(decimal_digits, length); - result_builder->AddPadding('0', decimal_point - length); - if (digits_after_point > 0) { - result_builder->AddCharacter('.'); - result_builder->AddPadding('0', digits_after_point); - } - } else { - // "decima.l_rep000" - ASSERT(digits_after_point > 0); - result_builder->AddSubstring(decimal_digits, decimal_point); - result_builder->AddCharacter('.'); - ASSERT(length - decimal_point <= digits_after_point); - result_builder->AddSubstring(&decimal_digits[decimal_point], - length - decimal_point); - int remaining_digits = digits_after_point - (length - decimal_point); - result_builder->AddPadding('0', remaining_digits); - } - if (digits_after_point == 0) { - if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) { - result_builder->AddCharacter('.'); - } - if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) { - result_builder->AddCharacter('0'); - } - } -} - - -bool DoubleToStringConverter::ToShortestIeeeNumber( - double value, - StringBuilder* result_builder, - DoubleToStringConverter::DtoaMode mode) const { - ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE); - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - int decimal_point; - bool sign; - const int kDecimalRepCapacity = kBase10MaximalLength + 1; - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - - DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - - bool unique_zero = (flags_ & UNIQUE_ZERO) != 0; - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - int exponent = decimal_point - 1; - if ((decimal_in_shortest_low_ <= exponent) && - (exponent < decimal_in_shortest_high_)) { - CreateDecimalRepresentation(decimal_rep, decimal_rep_length, - decimal_point, - Max(0, decimal_rep_length - decimal_point), - result_builder); - } else { - CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, - result_builder); - } - return true; -} - - -bool DoubleToStringConverter::ToFixed(double value, - int requested_digits, - StringBuilder* result_builder) const { - ASSERT(kMaxFixedDigitsBeforePoint == 60); - const double kFirstNonFixed = 1e60; - - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - if (requested_digits > kMaxFixedDigitsAfterPoint) return false; - if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false; - - // Find a sufficiently precise decimal representation of n. - int decimal_point; - bool sign; - // Add space for the '\0' byte. - const int kDecimalRepCapacity = - kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1; - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - DoubleToAscii(value, FIXED, requested_digits, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - - bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, - requested_digits, result_builder); - return true; -} - - -bool DoubleToStringConverter::ToExponential( - double value, - int requested_digits, - StringBuilder* result_builder) const { - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - if (requested_digits < -1) return false; - if (requested_digits > kMaxExponentialDigits) return false; - - int decimal_point; - bool sign; - // Add space for digit before the decimal point and the '\0' character. - const int kDecimalRepCapacity = kMaxExponentialDigits + 2; - ASSERT(kDecimalRepCapacity > kBase10MaximalLength); - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - - if (requested_digits == -1) { - DoubleToAscii(value, SHORTEST, 0, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - } else { - DoubleToAscii(value, PRECISION, requested_digits + 1, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - ASSERT(decimal_rep_length <= requested_digits + 1); - - for (int i = decimal_rep_length; i < requested_digits + 1; ++i) { - decimal_rep[i] = '0'; - } - decimal_rep_length = requested_digits + 1; - } - - bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - int exponent = decimal_point - 1; - CreateExponentialRepresentation(decimal_rep, - decimal_rep_length, - exponent, - result_builder); - return true; -} - - -bool DoubleToStringConverter::ToPrecision(double value, - int precision, - StringBuilder* result_builder) const { - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { - return false; - } - - // Find a sufficiently precise decimal representation of n. - int decimal_point; - bool sign; - // Add one for the terminating null character. - const int kDecimalRepCapacity = kMaxPrecisionDigits + 1; - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - - DoubleToAscii(value, PRECISION, precision, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - ASSERT(decimal_rep_length <= precision); - - bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - // The exponent if we print the number as x.xxeyyy. That is with the - // decimal point after the first digit. - int exponent = decimal_point - 1; - - int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0; - if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) || - (decimal_point - precision + extra_zero > - max_trailing_padding_zeroes_in_precision_mode_)) { - // Fill buffer to contain 'precision' digits. - // Usually the buffer is already at the correct length, but 'DoubleToAscii' - // is allowed to return less characters. - for (int i = decimal_rep_length; i < precision; ++i) { - decimal_rep[i] = '0'; - } - - CreateExponentialRepresentation(decimal_rep, - precision, - exponent, - result_builder); - } else { - CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, - Max(0, precision - decimal_point), - result_builder); - } - return true; -} - - -static BignumDtoaMode DtoaToBignumDtoaMode( - DoubleToStringConverter::DtoaMode dtoa_mode) { - switch (dtoa_mode) { - case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST; - case DoubleToStringConverter::SHORTEST_SINGLE: - return BIGNUM_DTOA_SHORTEST_SINGLE; - case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED; - case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION; - default: - UNREACHABLE(); - return BIGNUM_DTOA_SHORTEST; // To silence compiler. - } -} - - -void DoubleToStringConverter::DoubleToAscii(double v, - DtoaMode mode, - int requested_digits, - char* buffer, - int buffer_length, - bool* sign, - int* length, - int* point) { - Vector vector(buffer, buffer_length); - ASSERT(!Double(v).IsSpecial()); - ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0); - - if (Double(v).Sign() < 0) { - *sign = true; - v = -v; - } else { - *sign = false; - } - - if (mode == PRECISION && requested_digits == 0) { - vector[0] = '\0'; - *length = 0; - return; - } - - if (v == 0) { - vector[0] = '0'; - vector[1] = '\0'; - *length = 1; - *point = 1; - return; - } - - bool fast_worked; - switch (mode) { - case SHORTEST: - fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point); - break; - case SHORTEST_SINGLE: - fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0, - vector, length, point); - break; - case FIXED: - fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point); - break; - case PRECISION: - fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, - vector, length, point); - break; - default: - UNREACHABLE(); - fast_worked = false; - } - if (fast_worked) return; - - // If the fast dtoa didn't succeed use the slower bignum version. - BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); - BignumDtoa(v, bignum_mode, requested_digits, vector, length, point); - vector[*length] = '\0'; -} - - -// Consumes the given substring from the iterator. -// Returns false, if the substring does not match. -static bool ConsumeSubString(const char** current, - const char* end, - const char* substring) { - ASSERT(**current == *substring); - for (substring++; *substring != '\0'; substring++) { - ++*current; - if (*current == end || **current != *substring) return false; - } - ++*current; - return true; -} - - -// Maximum number of significant digits in decimal representation. -// The longest possible double in decimal representation is -// (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074 -// (768 digits). If we parse a number whose first digits are equal to a -// mean of 2 adjacent doubles (that could have up to 769 digits) the result -// must be rounded to the bigger one unless the tail consists of zeros, so -// we don't need to preserve all the digits. -const int kMaxSignificantDigits = 772; - - -// Returns true if a nonspace found and false if the end has reached. -static inline bool AdvanceToNonspace(const char** current, const char* end) { - while (*current != end) { - if (**current != ' ') return true; - ++*current; - } - return false; -} - - -static bool isDigit(int x, int radix) { - return (x >= '0' && x <= '9' && x < '0' + radix) - || (radix > 10 && x >= 'a' && x < 'a' + radix - 10) - || (radix > 10 && x >= 'A' && x < 'A' + radix - 10); -} - - -static double SignedZero(bool sign) { - return sign ? -0.0 : 0.0; -} - - -// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end. -template -static double RadixStringToIeee(const char* current, - const char* end, - bool sign, - bool allow_trailing_junk, - double junk_string_value, - bool read_as_double, - const char** trailing_pointer) { - ASSERT(current != end); - - const int kDoubleSize = Double::kSignificandSize; - const int kSingleSize = Single::kSignificandSize; - const int kSignificandSize = read_as_double? kDoubleSize: kSingleSize; - - // Skip leading 0s. - while (*current == '0') { - ++current; - if (current == end) { - *trailing_pointer = end; - return SignedZero(sign); - } - } - - int64_t number = 0; - int exponent = 0; - const int radix = (1 << radix_log_2); - - do { - int digit; - if (*current >= '0' && *current <= '9' && *current < '0' + radix) { - digit = static_cast(*current) - '0'; - } else if (radix > 10 && *current >= 'a' && *current < 'a' + radix - 10) { - digit = static_cast(*current) - 'a' + 10; - } else if (radix > 10 && *current >= 'A' && *current < 'A' + radix - 10) { - digit = static_cast(*current) - 'A' + 10; - } else { - if (allow_trailing_junk || !AdvanceToNonspace(¤t, end)) { - break; - } else { - return junk_string_value; - } - } - - number = number * radix + digit; - int overflow = static_cast(number >> kSignificandSize); - if (overflow != 0) { - // Overflow occurred. Need to determine which direction to round the - // result. - int overflow_bits_count = 1; - while (overflow > 1) { - overflow_bits_count++; - overflow >>= 1; - } - - int dropped_bits_mask = ((1 << overflow_bits_count) - 1); - int dropped_bits = static_cast(number) & dropped_bits_mask; - number >>= overflow_bits_count; - exponent = overflow_bits_count; - - bool zero_tail = true; - while (true) { - ++current; - if (current == end || !isDigit(*current, radix)) break; - zero_tail = zero_tail && *current == '0'; - exponent += radix_log_2; - } - - if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { - return junk_string_value; - } - - int middle_value = (1 << (overflow_bits_count - 1)); - if (dropped_bits > middle_value) { - number++; // Rounding up. - } else if (dropped_bits == middle_value) { - // Rounding to even to consistency with decimals: half-way case rounds - // up if significant part is odd and down otherwise. - if ((number & 1) != 0 || !zero_tail) { - number++; // Rounding up. - } - } - - // Rounding up may cause overflow. - if ((number & ((int64_t)1 << kSignificandSize)) != 0) { - exponent++; - number >>= 1; - } - break; - } - ++current; - } while (current != end); - - ASSERT(number < ((int64_t)1 << kSignificandSize)); - ASSERT(static_cast(static_cast(number)) == number); - - *trailing_pointer = current; - - if (exponent == 0) { - if (sign) { - if (number == 0) return -0.0; - number = -number; - } - return static_cast(number); - } - - ASSERT(number != 0); - return Double(DiyFp(number, exponent)).value(); -} - - -double StringToDoubleConverter::StringToIeee( - const char* input, - int length, - int* processed_characters_count, - bool read_as_double) { - const char* current = input; - const char* end = input + length; - - *processed_characters_count = 0; - - const bool allow_trailing_junk = (flags_ & ALLOW_TRAILING_JUNK) != 0; - const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0; - const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0; - const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0; - - // To make sure that iterator dereferencing is valid the following - // convention is used: - // 1. Each '++current' statement is followed by check for equality to 'end'. - // 2. If AdvanceToNonspace returned false then current == end. - // 3. If 'current' becomes equal to 'end' the function returns or goes to - // 'parsing_done'. - // 4. 'current' is not dereferenced after the 'parsing_done' label. - // 5. Code before 'parsing_done' may rely on 'current != end'. - if (current == end) return empty_string_value_; - - if (allow_leading_spaces || allow_trailing_spaces) { - if (!AdvanceToNonspace(¤t, end)) { - *processed_characters_count = current - input; - return empty_string_value_; - } - if (!allow_leading_spaces && (input != current)) { - // No leading spaces allowed, but AdvanceToNonspace moved forward. - return junk_string_value_; - } - } - - // The longest form of simplified number is: "-.1eXXX\0". - const int kBufferSize = kMaxSignificantDigits + 10; - char buffer[kBufferSize]; // NOLINT: size is known at compile time. - int buffer_pos = 0; - - // Exponent will be adjusted if insignificant digits of the integer part - // or insignificant leading zeros of the fractional part are dropped. - int exponent = 0; - int significant_digits = 0; - int insignificant_digits = 0; - bool nonzero_digit_dropped = false; - - bool sign = false; - - if (*current == '+' || *current == '-') { - sign = (*current == '-'); - ++current; - const char* next_non_space = current; - // Skip following spaces (if allowed). - if (!AdvanceToNonspace(&next_non_space, end)) return junk_string_value_; - if (!allow_spaces_after_sign && (current != next_non_space)) { - return junk_string_value_; - } - current = next_non_space; - } - - if (infinity_symbol_ != NULL) { - if (*current == infinity_symbol_[0]) { - if (!ConsumeSubString(¤t, end, infinity_symbol_)) { - return junk_string_value_; - } - - if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { - return junk_string_value_; - } - if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { - return junk_string_value_; - } - - ASSERT(buffer_pos == 0); - *processed_characters_count = current - input; - return sign ? -Double::Infinity() : Double::Infinity(); - } - } - - if (nan_symbol_ != NULL) { - if (*current == nan_symbol_[0]) { - if (!ConsumeSubString(¤t, end, nan_symbol_)) { - return junk_string_value_; - } - - if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { - return junk_string_value_; - } - if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { - return junk_string_value_; - } - - ASSERT(buffer_pos == 0); - *processed_characters_count = current - input; - return sign ? -Double::NaN() : Double::NaN(); - } - } - - bool leading_zero = false; - if (*current == '0') { - ++current; - if (current == end) { - *processed_characters_count = current - input; - return SignedZero(sign); - } - - leading_zero = true; - - // It could be hexadecimal value. - if ((flags_ & ALLOW_HEX) && (*current == 'x' || *current == 'X')) { - ++current; - if (current == end || !isDigit(*current, 16)) { - return junk_string_value_; // "0x". - } - - const char* tail_pointer = NULL; - double result = RadixStringToIeee<4>(current, - end, - sign, - allow_trailing_junk, - junk_string_value_, - read_as_double, - &tail_pointer); - if (tail_pointer != NULL) { - if (allow_trailing_spaces) AdvanceToNonspace(&tail_pointer, end); - *processed_characters_count = tail_pointer - input; - } - return result; - } - - // Ignore leading zeros in the integer part. - while (*current == '0') { - ++current; - if (current == end) { - *processed_characters_count = current - input; - return SignedZero(sign); - } - } - } - - bool octal = leading_zero && (flags_ & ALLOW_OCTALS) != 0; - - // Copy significant digits of the integer part (if any) to the buffer. - while (*current >= '0' && *current <= '9') { - if (significant_digits < kMaxSignificantDigits) { - ASSERT(buffer_pos < kBufferSize); - buffer[buffer_pos++] = static_cast(*current); - significant_digits++; - // Will later check if it's an octal in the buffer. - } else { - insignificant_digits++; // Move the digit into the exponential part. - nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; - } - octal = octal && *current < '8'; - ++current; - if (current == end) goto parsing_done; - } - - if (significant_digits == 0) { - octal = false; - } - - if (*current == '.') { - if (octal && !allow_trailing_junk) return junk_string_value_; - if (octal) goto parsing_done; - - ++current; - if (current == end) { - if (significant_digits == 0 && !leading_zero) { - return junk_string_value_; - } else { - goto parsing_done; - } - } - - if (significant_digits == 0) { - // octal = false; - // Integer part consists of 0 or is absent. Significant digits start after - // leading zeros (if any). - while (*current == '0') { - ++current; - if (current == end) { - *processed_characters_count = current - input; - return SignedZero(sign); - } - exponent--; // Move this 0 into the exponent. - } - } - - // There is a fractional part. - // We don't emit a '.', but adjust the exponent instead. - while (*current >= '0' && *current <= '9') { - if (significant_digits < kMaxSignificantDigits) { - ASSERT(buffer_pos < kBufferSize); - buffer[buffer_pos++] = static_cast(*current); - significant_digits++; - exponent--; - } else { - // Ignore insignificant digits in the fractional part. - nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; - } - ++current; - if (current == end) goto parsing_done; - } - } - - if (!leading_zero && exponent == 0 && significant_digits == 0) { - // If leading_zeros is true then the string contains zeros. - // If exponent < 0 then string was [+-]\.0*... - // If significant_digits != 0 the string is not equal to 0. - // Otherwise there are no digits in the string. - return junk_string_value_; - } - - // Parse exponential part. - if (*current == 'e' || *current == 'E') { - if (octal && !allow_trailing_junk) return junk_string_value_; - if (octal) goto parsing_done; - ++current; - if (current == end) { - if (allow_trailing_junk) { - goto parsing_done; - } else { - return junk_string_value_; - } - } - char sign = '+'; - if (*current == '+' || *current == '-') { - sign = static_cast(*current); - ++current; - if (current == end) { - if (allow_trailing_junk) { - goto parsing_done; - } else { - return junk_string_value_; - } - } - } - - if (current == end || *current < '0' || *current > '9') { - if (allow_trailing_junk) { - goto parsing_done; - } else { - return junk_string_value_; - } - } - - const int max_exponent = INT_MAX / 2; - ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); - int num = 0; - do { - // Check overflow. - int digit = *current - '0'; - if (num >= max_exponent / 10 - && !(num == max_exponent / 10 && digit <= max_exponent % 10)) { - num = max_exponent; - } else { - num = num * 10 + digit; - } - ++current; - } while (current != end && *current >= '0' && *current <= '9'); - - exponent += (sign == '-' ? -num : num); - } - - if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { - return junk_string_value_; - } - if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { - return junk_string_value_; - } - if (allow_trailing_spaces) { - AdvanceToNonspace(¤t, end); - } - - parsing_done: - exponent += insignificant_digits; - - if (octal) { - double result; - const char* tail_pointer = NULL; - result = RadixStringToIeee<3>(buffer, - buffer + buffer_pos, - sign, - allow_trailing_junk, - junk_string_value_, - read_as_double, - &tail_pointer); - ASSERT(tail_pointer != NULL); - *processed_characters_count = current - input; - return result; - } - - if (nonzero_digit_dropped) { - buffer[buffer_pos++] = '1'; - exponent--; - } - - ASSERT(buffer_pos < kBufferSize); - buffer[buffer_pos] = '\0'; - - double converted; - if (read_as_double) { - converted = Strtod(Vector(buffer, buffer_pos), exponent); - } else { - converted = Strtof(Vector(buffer, buffer_pos), exponent); - } - *processed_characters_count = current - input; - return sign? -converted: converted; -} - -} // namespace double_conversion diff --git a/3rdparty/double-conversion/double-conversion.h b/3rdparty/double-conversion/double-conversion.h deleted file mode 100644 index f98edae75a..0000000000 --- a/3rdparty/double-conversion/double-conversion.h +++ /dev/null @@ -1,536 +0,0 @@ -// Copyright 2012 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ -#define DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ - -#include "utils.h" - -namespace double_conversion { - -class DoubleToStringConverter { - public: - // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint - // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the - // function returns false. - static const int kMaxFixedDigitsBeforePoint = 60; - static const int kMaxFixedDigitsAfterPoint = 60; - - // When calling ToExponential with a requested_digits - // parameter > kMaxExponentialDigits then the function returns false. - static const int kMaxExponentialDigits = 120; - - // When calling ToPrecision with a requested_digits - // parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits - // then the function returns false. - static const int kMinPrecisionDigits = 1; - static const int kMaxPrecisionDigits = 120; - - enum Flags { - NO_FLAGS = 0, - EMIT_POSITIVE_EXPONENT_SIGN = 1, - EMIT_TRAILING_DECIMAL_POINT = 2, - EMIT_TRAILING_ZERO_AFTER_POINT = 4, - UNIQUE_ZERO = 8 - }; - - // Flags should be a bit-or combination of the possible Flags-enum. - // - NO_FLAGS: no special flags. - // - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent - // form, emits a '+' for positive exponents. Example: 1.2e+2. - // - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is - // converted into decimal format then a trailing decimal point is appended. - // Example: 2345.0 is converted to "2345.". - // - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point - // emits a trailing '0'-character. This flag requires the - // EXMIT_TRAILING_DECIMAL_POINT flag. - // Example: 2345.0 is converted to "2345.0". - // - UNIQUE_ZERO: "-0.0" is converted to "0.0". - // - // Infinity symbol and nan_symbol provide the string representation for these - // special values. If the string is NULL and the special value is encountered - // then the conversion functions return false. - // - // The exponent_character is used in exponential representations. It is - // usually 'e' or 'E'. - // - // When converting to the shortest representation the converter will - // represent input numbers in decimal format if they are in the interval - // [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[ - // (lower boundary included, greater boundary excluded). - // Example: with decimal_in_shortest_low = -6 and - // decimal_in_shortest_high = 21: - // ToShortest(0.000001) -> "0.000001" - // ToShortest(0.0000001) -> "1e-7" - // ToShortest(111111111111111111111.0) -> "111111111111111110000" - // ToShortest(100000000000000000000.0) -> "100000000000000000000" - // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" - // - // When converting to precision mode the converter may add - // max_leading_padding_zeroes before returning the number in exponential - // format. - // Example with max_leading_padding_zeroes_in_precision_mode = 6. - // ToPrecision(0.0000012345, 2) -> "0.0000012" - // ToPrecision(0.00000012345, 2) -> "1.2e-7" - // Similarily the converter may add up to - // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid - // returning an exponential representation. A zero added by the - // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. - // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: - // ToPrecision(230.0, 2) -> "230" - // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. - // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. - DoubleToStringConverter(int flags, - const char* infinity_symbol, - const char* nan_symbol, - char exponent_character, - int decimal_in_shortest_low, - int decimal_in_shortest_high, - int max_leading_padding_zeroes_in_precision_mode, - int max_trailing_padding_zeroes_in_precision_mode) - : flags_(flags), - infinity_symbol_(infinity_symbol), - nan_symbol_(nan_symbol), - exponent_character_(exponent_character), - decimal_in_shortest_low_(decimal_in_shortest_low), - decimal_in_shortest_high_(decimal_in_shortest_high), - max_leading_padding_zeroes_in_precision_mode_( - max_leading_padding_zeroes_in_precision_mode), - max_trailing_padding_zeroes_in_precision_mode_( - max_trailing_padding_zeroes_in_precision_mode) { - // When 'trailing zero after the point' is set, then 'trailing point' - // must be set too. - ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) || - !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0)); - } - - // Returns a converter following the EcmaScript specification. - static const DoubleToStringConverter& EcmaScriptConverter(); - - // Computes the shortest string of digits that correctly represent the input - // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high - // (see constructor) it then either returns a decimal representation, or an - // exponential representation. - // Example with decimal_in_shortest_low = -6, - // decimal_in_shortest_high = 21, - // EMIT_POSITIVE_EXPONENT_SIGN activated, and - // EMIT_TRAILING_DECIMAL_POINT deactived: - // ToShortest(0.000001) -> "0.000001" - // ToShortest(0.0000001) -> "1e-7" - // ToShortest(111111111111111111111.0) -> "111111111111111110000" - // ToShortest(100000000000000000000.0) -> "100000000000000000000" - // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" - // - // Note: the conversion may round the output if the returned string - // is accurate enough to uniquely identify the input-number. - // For example the most precise representation of the double 9e59 equals - // "899999999999999918767229449717619953810131273674690656206848", but - // the converter will return the shorter (but still correct) "9e59". - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except when the input value is special and no infinity_symbol or - // nan_symbol has been given to the constructor. - bool ToShortest(double value, StringBuilder* result_builder) const { - return ToShortestIeeeNumber(value, result_builder, SHORTEST); - } - - // Same as ToShortest, but for single-precision floats. - bool ToShortestSingle(float value, StringBuilder* result_builder) const { - return ToShortestIeeeNumber(value, result_builder, SHORTEST_SINGLE); - } - - - // Computes a decimal representation with a fixed number of digits after the - // decimal point. The last emitted digit is rounded. - // - // Examples: - // ToFixed(3.12, 1) -> "3.1" - // ToFixed(3.1415, 3) -> "3.142" - // ToFixed(1234.56789, 4) -> "1234.5679" - // ToFixed(1.23, 5) -> "1.23000" - // ToFixed(0.1, 4) -> "0.1000" - // ToFixed(1e30, 2) -> "1000000000000000019884624838656.00" - // ToFixed(0.1, 30) -> "0.100000000000000005551115123126" - // ToFixed(0.1, 17) -> "0.10000000000000001" - // - // If requested_digits equals 0, then the tail of the result depends on - // the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT. - // Examples, for requested_digits == 0, - // let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be - // - false and false: then 123.45 -> 123 - // 0.678 -> 1 - // - true and false: then 123.45 -> 123. - // 0.678 -> 1. - // - true and true: then 123.45 -> 123.0 - // 0.678 -> 1.0 - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except for the following cases: - // - the input value is special and no infinity_symbol or nan_symbol has - // been provided to the constructor, - // - 'value' > 10^kMaxFixedDigitsBeforePoint, or - // - 'requested_digits' > kMaxFixedDigitsAfterPoint. - // The last two conditions imply that the result will never contain more than - // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters - // (one additional character for the sign, and one for the decimal point). - bool ToFixed(double value, - int requested_digits, - StringBuilder* result_builder) const; - - // Computes a representation in exponential format with requested_digits - // after the decimal point. The last emitted digit is rounded. - // If requested_digits equals -1, then the shortest exponential representation - // is computed. - // - // Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and - // exponent_character set to 'e'. - // ToExponential(3.12, 1) -> "3.1e0" - // ToExponential(5.0, 3) -> "5.000e0" - // ToExponential(0.001, 2) -> "1.00e-3" - // ToExponential(3.1415, -1) -> "3.1415e0" - // ToExponential(3.1415, 4) -> "3.1415e0" - // ToExponential(3.1415, 3) -> "3.142e0" - // ToExponential(123456789000000, 3) -> "1.235e14" - // ToExponential(1000000000000000019884624838656.0, -1) -> "1e30" - // ToExponential(1000000000000000019884624838656.0, 32) -> - // "1.00000000000000001988462483865600e30" - // ToExponential(1234, 0) -> "1e3" - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except for the following cases: - // - the input value is special and no infinity_symbol or nan_symbol has - // been provided to the constructor, - // - 'requested_digits' > kMaxExponentialDigits. - // The last condition implies that the result will never contain more than - // kMaxExponentialDigits + 8 characters (the sign, the digit before the - // decimal point, the decimal point, the exponent character, the - // exponent's sign, and at most 3 exponent digits). - bool ToExponential(double value, - int requested_digits, - StringBuilder* result_builder) const; - - // Computes 'precision' leading digits of the given 'value' and returns them - // either in exponential or decimal format, depending on - // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the - // constructor). - // The last computed digit is rounded. - // - // Example with max_leading_padding_zeroes_in_precision_mode = 6. - // ToPrecision(0.0000012345, 2) -> "0.0000012" - // ToPrecision(0.00000012345, 2) -> "1.2e-7" - // Similarily the converter may add up to - // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid - // returning an exponential representation. A zero added by the - // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. - // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: - // ToPrecision(230.0, 2) -> "230" - // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. - // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. - // Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no - // EMIT_TRAILING_ZERO_AFTER_POINT: - // ToPrecision(123450.0, 6) -> "123450" - // ToPrecision(123450.0, 5) -> "123450" - // ToPrecision(123450.0, 4) -> "123500" - // ToPrecision(123450.0, 3) -> "123000" - // ToPrecision(123450.0, 2) -> "1.2e5" - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except for the following cases: - // - the input value is special and no infinity_symbol or nan_symbol has - // been provided to the constructor, - // - precision < kMinPericisionDigits - // - precision > kMaxPrecisionDigits - // The last condition implies that the result will never contain more than - // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the - // exponent character, the exponent's sign, and at most 3 exponent digits). - bool ToPrecision(double value, - int precision, - StringBuilder* result_builder) const; - - enum DtoaMode { - // Produce the shortest correct representation. - // For example the output of 0.299999999999999988897 is (the less accurate - // but correct) 0.3. - SHORTEST, - // Same as SHORTEST, but for single-precision floats. - SHORTEST_SINGLE, - // Produce a fixed number of digits after the decimal point. - // For instance fixed(0.1, 4) becomes 0.1000 - // If the input number is big, the output will be big. - FIXED, - // Fixed number of digits (independent of the decimal point). - PRECISION - }; - - // The maximal number of digits that are needed to emit a double in base 10. - // A higher precision can be achieved by using more digits, but the shortest - // accurate representation of any double will never use more digits than - // kBase10MaximalLength. - // Note that DoubleToAscii null-terminates its input. So the given buffer - // should be at least kBase10MaximalLength + 1 characters long. - static const int kBase10MaximalLength = 17; - - // Converts the given double 'v' to ascii. 'v' must not be NaN, +Infinity, or - // -Infinity. In SHORTEST_SINGLE-mode this restriction also applies to 'v' - // after it has been casted to a single-precision float. That is, in this - // mode static_cast(v) must not be NaN, +Infinity or -Infinity. - // - // The result should be interpreted as buffer * 10^(point-length). - // - // The output depends on the given mode: - // - SHORTEST: produce the least amount of digits for which the internal - // identity requirement is still satisfied. If the digits are printed - // (together with the correct exponent) then reading this number will give - // 'v' again. The buffer will choose the representation that is closest to - // 'v'. If there are two at the same distance, than the one farther away - // from 0 is chosen (halfway cases - ending with 5 - are rounded up). - // In this mode the 'requested_digits' parameter is ignored. - // - SHORTEST_SINGLE: same as SHORTEST but with single-precision. - // - FIXED: produces digits necessary to print a given number with - // 'requested_digits' digits after the decimal point. The produced digits - // might be too short in which case the caller has to fill the remainder - // with '0's. - // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. - // Halfway cases are rounded towards +/-Infinity (away from 0). The call - // toFixed(0.15, 2) thus returns buffer="2", point=0. - // The returned buffer may contain digits that would be truncated from the - // shortest representation of the input. - // - PRECISION: produces 'requested_digits' where the first digit is not '0'. - // Even though the length of produced digits usually equals - // 'requested_digits', the function is allowed to return fewer digits, in - // which case the caller has to fill the missing digits with '0's. - // Halfway cases are again rounded away from 0. - // DoubleToAscii expects the given buffer to be big enough to hold all - // digits and a terminating null-character. In SHORTEST-mode it expects a - // buffer of at least kBase10MaximalLength + 1. In all other modes the - // requested_digits parameter and the padding-zeroes limit the size of the - // output. Don't forget the decimal point, the exponent character and the - // terminating null-character when computing the maximal output size. - // The given length is only used in debug mode to ensure the buffer is big - // enough. - static void DoubleToAscii(double v, - DtoaMode mode, - int requested_digits, - char* buffer, - int buffer_length, - bool* sign, - int* length, - int* point); - - private: - // Implementation for ToShortest and ToShortestSingle. - bool ToShortestIeeeNumber(double value, - StringBuilder* result_builder, - DtoaMode mode) const; - - // If the value is a special value (NaN or Infinity) constructs the - // corresponding string using the configured infinity/nan-symbol. - // If either of them is NULL or the value is not special then the - // function returns false. - bool HandleSpecialValues(double value, StringBuilder* result_builder) const; - // Constructs an exponential representation (i.e. 1.234e56). - // The given exponent assumes a decimal point after the first decimal digit. - void CreateExponentialRepresentation(const char* decimal_digits, - int length, - int exponent, - StringBuilder* result_builder) const; - // Creates a decimal representation (i.e 1234.5678). - void CreateDecimalRepresentation(const char* decimal_digits, - int length, - int decimal_point, - int digits_after_point, - StringBuilder* result_builder) const; - - const int flags_; - const char* const infinity_symbol_; - const char* const nan_symbol_; - const char exponent_character_; - const int decimal_in_shortest_low_; - const int decimal_in_shortest_high_; - const int max_leading_padding_zeroes_in_precision_mode_; - const int max_trailing_padding_zeroes_in_precision_mode_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter); -}; - - -class StringToDoubleConverter { - public: - // Enumeration for allowing octals and ignoring junk when converting - // strings to numbers. - enum Flags { - NO_FLAGS = 0, - ALLOW_HEX = 1, - ALLOW_OCTALS = 2, - ALLOW_TRAILING_JUNK = 4, - ALLOW_LEADING_SPACES = 8, - ALLOW_TRAILING_SPACES = 16, - ALLOW_SPACES_AFTER_SIGN = 32 - }; - - // Flags should be a bit-or combination of the possible Flags-enum. - // - NO_FLAGS: no special flags. - // - ALLOW_HEX: recognizes the prefix "0x". Hex numbers may only be integers. - // Ex: StringToDouble("0x1234") -> 4660.0 - // In StringToDouble("0x1234.56") the characters ".56" are trailing - // junk. The result of the call is hence dependent on - // the ALLOW_TRAILING_JUNK flag and/or the junk value. - // With this flag "0x" is a junk-string. Even with ALLOW_TRAILING_JUNK, - // the string will not be parsed as "0" followed by junk. - // - // - ALLOW_OCTALS: recognizes the prefix "0" for octals: - // If a sequence of octal digits starts with '0', then the number is - // read as octal integer. Octal numbers may only be integers. - // Ex: StringToDouble("01234") -> 668.0 - // StringToDouble("012349") -> 12349.0 // Not a sequence of octal - // // digits. - // In StringToDouble("01234.56") the characters ".56" are trailing - // junk. The result of the call is hence dependent on - // the ALLOW_TRAILING_JUNK flag and/or the junk value. - // In StringToDouble("01234e56") the characters "e56" are trailing - // junk, too. - // - ALLOW_TRAILING_JUNK: ignore trailing characters that are not part of - // a double literal. - // - ALLOW_LEADING_SPACES: skip over leading spaces. - // - ALLOW_TRAILING_SPACES: ignore trailing spaces. - // - ALLOW_SPACES_AFTER_SIGN: ignore spaces after the sign. - // Ex: StringToDouble("- 123.2") -> -123.2. - // StringToDouble("+ 123.2") -> 123.2 - // - // empty_string_value is returned when an empty string is given as input. - // If ALLOW_LEADING_SPACES or ALLOW_TRAILING_SPACES are set, then a string - // containing only spaces is converted to the 'empty_string_value', too. - // - // junk_string_value is returned when - // a) ALLOW_TRAILING_JUNK is not set, and a junk character (a character not - // part of a double-literal) is found. - // b) ALLOW_TRAILING_JUNK is set, but the string does not start with a - // double literal. - // - // infinity_symbol and nan_symbol are strings that are used to detect - // inputs that represent infinity and NaN. They can be null, in which case - // they are ignored. - // The conversion routine first reads any possible signs. Then it compares the - // following character of the input-string with the first character of - // the infinity, and nan-symbol. If either matches, the function assumes, that - // a match has been found, and expects the following input characters to match - // the remaining characters of the special-value symbol. - // This means that the following restrictions apply to special-value symbols: - // - they must not start with signs ('+', or '-'), - // - they must not have the same first character. - // - they must not start with digits. - // - // Examples: - // flags = ALLOW_HEX | ALLOW_TRAILING_JUNK, - // empty_string_value = 0.0, - // junk_string_value = NaN, - // infinity_symbol = "infinity", - // nan_symbol = "nan": - // StringToDouble("0x1234") -> 4660.0. - // StringToDouble("0x1234K") -> 4660.0. - // StringToDouble("") -> 0.0 // empty_string_value. - // StringToDouble(" ") -> NaN // junk_string_value. - // StringToDouble(" 1") -> NaN // junk_string_value. - // StringToDouble("0x") -> NaN // junk_string_value. - // StringToDouble("-123.45") -> -123.45. - // StringToDouble("--123.45") -> NaN // junk_string_value. - // StringToDouble("123e45") -> 123e45. - // StringToDouble("123E45") -> 123e45. - // StringToDouble("123e+45") -> 123e45. - // StringToDouble("123E-45") -> 123e-45. - // StringToDouble("123e") -> 123.0 // trailing junk ignored. - // StringToDouble("123e-") -> 123.0 // trailing junk ignored. - // StringToDouble("+NaN") -> NaN // NaN string literal. - // StringToDouble("-infinity") -> -inf. // infinity literal. - // StringToDouble("Infinity") -> NaN // junk_string_value. - // - // flags = ALLOW_OCTAL | ALLOW_LEADING_SPACES, - // empty_string_value = 0.0, - // junk_string_value = NaN, - // infinity_symbol = NULL, - // nan_symbol = NULL: - // StringToDouble("0x1234") -> NaN // junk_string_value. - // StringToDouble("01234") -> 668.0. - // StringToDouble("") -> 0.0 // empty_string_value. - // StringToDouble(" ") -> 0.0 // empty_string_value. - // StringToDouble(" 1") -> 1.0 - // StringToDouble("0x") -> NaN // junk_string_value. - // StringToDouble("0123e45") -> NaN // junk_string_value. - // StringToDouble("01239E45") -> 1239e45. - // StringToDouble("-infinity") -> NaN // junk_string_value. - // StringToDouble("NaN") -> NaN // junk_string_value. - StringToDoubleConverter(int flags, - double empty_string_value, - double junk_string_value, - const char* infinity_symbol, - const char* nan_symbol) - : flags_(flags), - empty_string_value_(empty_string_value), - junk_string_value_(junk_string_value), - infinity_symbol_(infinity_symbol), - nan_symbol_(nan_symbol) { - } - - // Performs the conversion. - // The output parameter 'processed_characters_count' is set to the number - // of characters that have been processed to read the number. - // Spaces than are processed with ALLOW_{LEADING|TRAILING}_SPACES are included - // in the 'processed_characters_count'. Trailing junk is never included. - double StringToDouble(const char* buffer, - int length, - int* processed_characters_count) { - return StringToIeee(buffer, length, processed_characters_count, true); - } - - // Same as StringToDouble but reads a float. - // Note that this is not equivalent to static_cast(StringToDouble(...)) - // due to potential double-rounding. - float StringToFloat(const char* buffer, - int length, - int* processed_characters_count) { - return static_cast(StringToIeee(buffer, length, - processed_characters_count, false)); - } - - private: - const int flags_; - const double empty_string_value_; - const double junk_string_value_; - const char* const infinity_symbol_; - const char* const nan_symbol_; - - double StringToIeee(const char* buffer, - int length, - int* processed_characters_count, - bool read_as_double); - - DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter); -}; - -} // namespace double_conversion - -#endif // DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ diff --git a/3rdparty/double-conversion/double-conversion.pri b/3rdparty/double-conversion/double-conversion.pri deleted file mode 100644 index 9a9a2b89d8..0000000000 --- a/3rdparty/double-conversion/double-conversion.pri +++ /dev/null @@ -1,3 +0,0 @@ -VPATH += $$PWD -SOURCES += $$PWD/*.cc -HEADERS += $$PWD/*.h diff --git a/3rdparty/double-conversion/fast-dtoa.cc b/3rdparty/double-conversion/fast-dtoa.cc deleted file mode 100644 index 1a0f823509..0000000000 --- a/3rdparty/double-conversion/fast-dtoa.cc +++ /dev/null @@ -1,664 +0,0 @@ -// Copyright 2012 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "fast-dtoa.h" - -#include "cached-powers.h" -#include "diy-fp.h" -#include "ieee.h" - -namespace double_conversion { - -// The minimal and maximal target exponent define the range of w's binary -// exponent, where 'w' is the result of multiplying the input by a cached power -// of ten. -// -// A different range might be chosen on a different platform, to optimize digit -// generation, but a smaller range requires more powers of ten to be cached. -static const int kMinimalTargetExponent = -60; -static const int kMaximalTargetExponent = -32; - - -// Adjusts the last digit of the generated number, and screens out generated -// solutions that may be inaccurate. A solution may be inaccurate if it is -// outside the safe interval, or if we cannot prove that it is closer to the -// input than a neighboring representation of the same length. -// -// Input: * buffer containing the digits of too_high / 10^kappa -// * the buffer's length -// * distance_too_high_w == (too_high - w).f() * unit -// * unsafe_interval == (too_high - too_low).f() * unit -// * rest = (too_high - buffer * 10^kappa).f() * unit -// * ten_kappa = 10^kappa * unit -// * unit = the common multiplier -// Output: returns true if the buffer is guaranteed to contain the closest -// representable number to the input. -// Modifies the generated digits in the buffer to approach (round towards) w. -static bool RoundWeed(Vector buffer, - int length, - uint64_t distance_too_high_w, - uint64_t unsafe_interval, - uint64_t rest, - uint64_t ten_kappa, - uint64_t unit) { - uint64_t small_distance = distance_too_high_w - unit; - uint64_t big_distance = distance_too_high_w + unit; - // Let w_low = too_high - big_distance, and - // w_high = too_high - small_distance. - // Note: w_low < w < w_high - // - // The real w (* unit) must lie somewhere inside the interval - // ]w_low; w_high[ (often written as "(w_low; w_high)") - - // Basically the buffer currently contains a number in the unsafe interval - // ]too_low; too_high[ with too_low < w < too_high - // - // too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ^v 1 unit ^ ^ ^ ^ - // boundary_high --------------------- . . . . - // ^v 1 unit . . . . - // - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . . - // . . ^ . . - // . big_distance . . . - // . . . . rest - // small_distance . . . . - // v . . . . - // w_high - - - - - - - - - - - - - - - - - - . . . . - // ^v 1 unit . . . . - // w ---------------------------------------- . . . . - // ^v 1 unit v . . . - // w_low - - - - - - - - - - - - - - - - - - - - - . . . - // . . v - // buffer --------------------------------------------------+-------+-------- - // . . - // safe_interval . - // v . - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . - // ^v 1 unit . - // boundary_low ------------------------- unsafe_interval - // ^v 1 unit v - // too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - // - // Note that the value of buffer could lie anywhere inside the range too_low - // to too_high. - // - // boundary_low, boundary_high and w are approximations of the real boundaries - // and v (the input number). They are guaranteed to be precise up to one unit. - // In fact the error is guaranteed to be strictly less than one unit. - // - // Anything that lies outside the unsafe interval is guaranteed not to round - // to v when read again. - // Anything that lies inside the safe interval is guaranteed to round to v - // when read again. - // If the number inside the buffer lies inside the unsafe interval but not - // inside the safe interval then we simply do not know and bail out (returning - // false). - // - // Similarly we have to take into account the imprecision of 'w' when finding - // the closest representation of 'w'. If we have two potential - // representations, and one is closer to both w_low and w_high, then we know - // it is closer to the actual value v. - // - // By generating the digits of too_high we got the largest (closest to - // too_high) buffer that is still in the unsafe interval. In the case where - // w_high < buffer < too_high we try to decrement the buffer. - // This way the buffer approaches (rounds towards) w. - // There are 3 conditions that stop the decrementation process: - // 1) the buffer is already below w_high - // 2) decrementing the buffer would make it leave the unsafe interval - // 3) decrementing the buffer would yield a number below w_high and farther - // away than the current number. In other words: - // (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high - // Instead of using the buffer directly we use its distance to too_high. - // Conceptually rest ~= too_high - buffer - // We need to do the following tests in this order to avoid over- and - // underflows. - ASSERT(rest <= unsafe_interval); - while (rest < small_distance && // Negated condition 1 - unsafe_interval - rest >= ten_kappa && // Negated condition 2 - (rest + ten_kappa < small_distance || // buffer{-1} > w_high - small_distance - rest >= rest + ten_kappa - small_distance)) { - buffer[length - 1]--; - rest += ten_kappa; - } - - // We have approached w+ as much as possible. We now test if approaching w- - // would require changing the buffer. If yes, then we have two possible - // representations close to w, but we cannot decide which one is closer. - if (rest < big_distance && - unsafe_interval - rest >= ten_kappa && - (rest + ten_kappa < big_distance || - big_distance - rest > rest + ten_kappa - big_distance)) { - return false; - } - - // Weeding test. - // The safe interval is [too_low + 2 ulp; too_high - 2 ulp] - // Since too_low = too_high - unsafe_interval this is equivalent to - // [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp] - // Conceptually we have: rest ~= too_high - buffer - return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit); -} - - -// Rounds the buffer upwards if the result is closer to v by possibly adding -// 1 to the buffer. If the precision of the calculation is not sufficient to -// round correctly, return false. -// The rounding might shift the whole buffer in which case the kappa is -// adjusted. For example "99", kappa = 3 might become "10", kappa = 4. -// -// If 2*rest > ten_kappa then the buffer needs to be round up. -// rest can have an error of +/- 1 unit. This function accounts for the -// imprecision and returns false, if the rounding direction cannot be -// unambiguously determined. -// -// Precondition: rest < ten_kappa. -static bool RoundWeedCounted(Vector buffer, - int length, - uint64_t rest, - uint64_t ten_kappa, - uint64_t unit, - int* kappa) { - ASSERT(rest < ten_kappa); - // The following tests are done in a specific order to avoid overflows. They - // will work correctly with any uint64 values of rest < ten_kappa and unit. - // - // If the unit is too big, then we don't know which way to round. For example - // a unit of 50 means that the real number lies within rest +/- 50. If - // 10^kappa == 40 then there is no way to tell which way to round. - if (unit >= ten_kappa) return false; - // Even if unit is just half the size of 10^kappa we are already completely - // lost. (And after the previous test we know that the expression will not - // over/underflow.) - if (ten_kappa - unit <= unit) return false; - // If 2 * (rest + unit) <= 10^kappa we can safely round down. - if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) { - return true; - } - // If 2 * (rest - unit) >= 10^kappa, then we can safely round up. - if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) { - // Increment the last digit recursively until we find a non '9' digit. - buffer[length - 1]++; - for (int i = length - 1; i > 0; --i) { - if (buffer[i] != '0' + 10) break; - buffer[i] = '0'; - buffer[i - 1]++; - } - // If the first digit is now '0'+ 10 we had a buffer with all '9's. With the - // exception of the first digit all digits are now '0'. Simply switch the - // first digit to '1' and adjust the kappa. Example: "99" becomes "10" and - // the power (the kappa) is increased. - if (buffer[0] == '0' + 10) { - buffer[0] = '1'; - (*kappa) += 1; - } - return true; - } - return false; -} - -// Returns the biggest power of ten that is less than or equal to the given -// number. We furthermore receive the maximum number of bits 'number' has. -// -// Returns power == 10^(exponent_plus_one-1) such that -// power <= number < power * 10. -// If number_bits == 0 then 0^(0-1) is returned. -// The number of bits must be <= 32. -// Precondition: number < (1 << (number_bits + 1)). - -// Inspired by the method for finding an integer log base 10 from here: -// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 -static unsigned int const kSmallPowersOfTen[] = - {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, - 1000000000}; - -static void BiggestPowerTen(uint32_t number, - int number_bits, - uint32_t* power, - int* exponent_plus_one) { - ASSERT(number < (1u << (number_bits + 1))); - // 1233/4096 is approximately 1/lg(10). - int exponent_plus_one_guess = ((number_bits + 1) * 1233 >> 12); - // We increment to skip over the first entry in the kPowersOf10 table. - // Note: kPowersOf10[i] == 10^(i-1). - exponent_plus_one_guess++; - // We don't have any guarantees that 2^number_bits <= number. - // TODO(floitsch): can we change the 'while' into an 'if'? We definitely see - // number < (2^number_bits - 1), but I haven't encountered - // number < (2^number_bits - 2) yet. - while (number < kSmallPowersOfTen[exponent_plus_one_guess]) { - exponent_plus_one_guess--; - } - *power = kSmallPowersOfTen[exponent_plus_one_guess]; - *exponent_plus_one = exponent_plus_one_guess; -} - -// Generates the digits of input number w. -// w is a floating-point number (DiyFp), consisting of a significand and an -// exponent. Its exponent is bounded by kMinimalTargetExponent and -// kMaximalTargetExponent. -// Hence -60 <= w.e() <= -32. -// -// Returns false if it fails, in which case the generated digits in the buffer -// should not be used. -// Preconditions: -// * low, w and high are correct up to 1 ulp (unit in the last place). That -// is, their error must be less than a unit of their last digits. -// * low.e() == w.e() == high.e() -// * low < w < high, and taking into account their error: low~ <= high~ -// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent -// Postconditions: returns false if procedure fails. -// otherwise: -// * buffer is not null-terminated, but len contains the number of digits. -// * buffer contains the shortest possible decimal digit-sequence -// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the -// correct values of low and high (without their error). -// * if more than one decimal representation gives the minimal number of -// decimal digits then the one closest to W (where W is the correct value -// of w) is chosen. -// Remark: this procedure takes into account the imprecision of its input -// numbers. If the precision is not enough to guarantee all the postconditions -// then false is returned. This usually happens rarely (~0.5%). -// -// Say, for the sake of example, that -// w.e() == -48, and w.f() == 0x1234567890abcdef -// w's value can be computed by w.f() * 2^w.e() -// We can obtain w's integral digits by simply shifting w.f() by -w.e(). -// -> w's integral part is 0x1234 -// w's fractional part is therefore 0x567890abcdef. -// Printing w's integral part is easy (simply print 0x1234 in decimal). -// In order to print its fraction we repeatedly multiply the fraction by 10 and -// get each digit. Example the first digit after the point would be computed by -// (0x567890abcdef * 10) >> 48. -> 3 -// The whole thing becomes slightly more complicated because we want to stop -// once we have enough digits. That is, once the digits inside the buffer -// represent 'w' we can stop. Everything inside the interval low - high -// represents w. However we have to pay attention to low, high and w's -// imprecision. -static bool DigitGen(DiyFp low, - DiyFp w, - DiyFp high, - Vector buffer, - int* length, - int* kappa) { - ASSERT(low.e() == w.e() && w.e() == high.e()); - ASSERT(low.f() + 1 <= high.f() - 1); - ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); - // low, w and high are imprecise, but by less than one ulp (unit in the last - // place). - // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that - // the new numbers are outside of the interval we want the final - // representation to lie in. - // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield - // numbers that are certain to lie in the interval. We will use this fact - // later on. - // We will now start by generating the digits within the uncertain - // interval. Later we will weed out representations that lie outside the safe - // interval and thus _might_ lie outside the correct interval. - uint64_t unit = 1; - DiyFp too_low = DiyFp(low.f() - unit, low.e()); - DiyFp too_high = DiyFp(high.f() + unit, high.e()); - // too_low and too_high are guaranteed to lie outside the interval we want the - // generated number in. - DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low); - // We now cut the input number into two parts: the integral digits and the - // fractionals. We will not write any decimal separator though, but adapt - // kappa instead. - // Reminder: we are currently computing the digits (stored inside the buffer) - // such that: too_low < buffer * 10^kappa < too_high - // We use too_high for the digit_generation and stop as soon as possible. - // If we stop early we effectively round down. - DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); - // Division by one is a shift. - uint32_t integrals = static_cast(too_high.f() >> -one.e()); - // Modulo by one is an and. - uint64_t fractionals = too_high.f() & (one.f() - 1); - uint32_t divisor; - int divisor_exponent_plus_one; - BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), - &divisor, &divisor_exponent_plus_one); - *kappa = divisor_exponent_plus_one; - *length = 0; - // Loop invariant: buffer = too_high / 10^kappa (integer division) - // The invariant holds for the first iteration: kappa has been initialized - // with the divisor exponent + 1. And the divisor is the biggest power of ten - // that is smaller than integrals. - while (*kappa > 0) { - int digit = integrals / divisor; - buffer[*length] = '0' + digit; - (*length)++; - integrals %= divisor; - (*kappa)--; - // Note that kappa now equals the exponent of the divisor and that the - // invariant thus holds again. - uint64_t rest = - (static_cast(integrals) << -one.e()) + fractionals; - // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e()) - // Reminder: unsafe_interval.e() == one.e() - if (rest < unsafe_interval.f()) { - // Rounding down (by not emitting the remaining digits) yields a number - // that lies within the unsafe interval. - return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(), - unsafe_interval.f(), rest, - static_cast(divisor) << -one.e(), unit); - } - divisor /= 10; - } - - // The integrals have been generated. We are at the point of the decimal - // separator. In the following loop we simply multiply the remaining digits by - // 10 and divide by one. We just need to pay attention to multiply associated - // data (like the interval or 'unit'), too. - // Note that the multiplication by 10 does not overflow, because w.e >= -60 - // and thus one.e >= -60. - ASSERT(one.e() >= -60); - ASSERT(fractionals < one.f()); - ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); - while (true) { - fractionals *= 10; - unit *= 10; - unsafe_interval.set_f(unsafe_interval.f() * 10); - // Integer division by one. - int digit = static_cast(fractionals >> -one.e()); - buffer[*length] = '0' + digit; - (*length)++; - fractionals &= one.f() - 1; // Modulo by one. - (*kappa)--; - if (fractionals < unsafe_interval.f()) { - return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit, - unsafe_interval.f(), fractionals, one.f(), unit); - } - } -} - - - -// Generates (at most) requested_digits digits of input number w. -// w is a floating-point number (DiyFp), consisting of a significand and an -// exponent. Its exponent is bounded by kMinimalTargetExponent and -// kMaximalTargetExponent. -// Hence -60 <= w.e() <= -32. -// -// Returns false if it fails, in which case the generated digits in the buffer -// should not be used. -// Preconditions: -// * w is correct up to 1 ulp (unit in the last place). That -// is, its error must be strictly less than a unit of its last digit. -// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent -// -// Postconditions: returns false if procedure fails. -// otherwise: -// * buffer is not null-terminated, but length contains the number of -// digits. -// * the representation in buffer is the most precise representation of -// requested_digits digits. -// * buffer contains at most requested_digits digits of w. If there are less -// than requested_digits digits then some trailing '0's have been removed. -// * kappa is such that -// w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2. -// -// Remark: This procedure takes into account the imprecision of its input -// numbers. If the precision is not enough to guarantee all the postconditions -// then false is returned. This usually happens rarely, but the failure-rate -// increases with higher requested_digits. -static bool DigitGenCounted(DiyFp w, - int requested_digits, - Vector buffer, - int* length, - int* kappa) { - ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); - ASSERT(kMinimalTargetExponent >= -60); - ASSERT(kMaximalTargetExponent <= -32); - // w is assumed to have an error less than 1 unit. Whenever w is scaled we - // also scale its error. - uint64_t w_error = 1; - // We cut the input number into two parts: the integral digits and the - // fractional digits. We don't emit any decimal separator, but adapt kappa - // instead. Example: instead of writing "1.2" we put "12" into the buffer and - // increase kappa by 1. - DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); - // Division by one is a shift. - uint32_t integrals = static_cast(w.f() >> -one.e()); - // Modulo by one is an and. - uint64_t fractionals = w.f() & (one.f() - 1); - uint32_t divisor; - int divisor_exponent_plus_one; - BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), - &divisor, &divisor_exponent_plus_one); - *kappa = divisor_exponent_plus_one; - *length = 0; - - // Loop invariant: buffer = w / 10^kappa (integer division) - // The invariant holds for the first iteration: kappa has been initialized - // with the divisor exponent + 1. And the divisor is the biggest power of ten - // that is smaller than 'integrals'. - while (*kappa > 0) { - int digit = integrals / divisor; - buffer[*length] = '0' + digit; - (*length)++; - requested_digits--; - integrals %= divisor; - (*kappa)--; - // Note that kappa now equals the exponent of the divisor and that the - // invariant thus holds again. - if (requested_digits == 0) break; - divisor /= 10; - } - - if (requested_digits == 0) { - uint64_t rest = - (static_cast(integrals) << -one.e()) + fractionals; - return RoundWeedCounted(buffer, *length, rest, - static_cast(divisor) << -one.e(), w_error, - kappa); - } - - // The integrals have been generated. We are at the point of the decimal - // separator. In the following loop we simply multiply the remaining digits by - // 10 and divide by one. We just need to pay attention to multiply associated - // data (the 'unit'), too. - // Note that the multiplication by 10 does not overflow, because w.e >= -60 - // and thus one.e >= -60. - ASSERT(one.e() >= -60); - ASSERT(fractionals < one.f()); - ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); - while (requested_digits > 0 && fractionals > w_error) { - fractionals *= 10; - w_error *= 10; - // Integer division by one. - int digit = static_cast(fractionals >> -one.e()); - buffer[*length] = '0' + digit; - (*length)++; - requested_digits--; - fractionals &= one.f() - 1; // Modulo by one. - (*kappa)--; - } - if (requested_digits != 0) return false; - return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error, - kappa); -} - - -// Provides a decimal representation of v. -// Returns true if it succeeds, otherwise the result cannot be trusted. -// There will be *length digits inside the buffer (not null-terminated). -// If the function returns true then -// v == (double) (buffer * 10^decimal_exponent). -// The digits in the buffer are the shortest representation possible: no -// 0.09999999999999999 instead of 0.1. The shorter representation will even be -// chosen even if the longer one would be closer to v. -// The last digit will be closest to the actual v. That is, even if several -// digits might correctly yield 'v' when read again, the closest will be -// computed. -static bool Grisu3(double v, - FastDtoaMode mode, - Vector buffer, - int* length, - int* decimal_exponent) { - DiyFp w = Double(v).AsNormalizedDiyFp(); - // boundary_minus and boundary_plus are the boundaries between v and its - // closest floating-point neighbors. Any number strictly between - // boundary_minus and boundary_plus will round to v when convert to a double. - // Grisu3 will never output representations that lie exactly on a boundary. - DiyFp boundary_minus, boundary_plus; - if (mode == FAST_DTOA_SHORTEST) { - Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus); - } else { - ASSERT(mode == FAST_DTOA_SHORTEST_SINGLE); - float single_v = static_cast(v); - Single(single_v).NormalizedBoundaries(&boundary_minus, &boundary_plus); - } - ASSERT(boundary_plus.e() == w.e()); - DiyFp ten_mk; // Cached power of ten: 10^-k - int mk; // -k - int ten_mk_minimal_binary_exponent = - kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); - int ten_mk_maximal_binary_exponent = - kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); - PowersOfTenCache::GetCachedPowerForBinaryExponentRange( - ten_mk_minimal_binary_exponent, - ten_mk_maximal_binary_exponent, - &ten_mk, &mk); - ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + - DiyFp::kSignificandSize) && - (kMaximalTargetExponent >= w.e() + ten_mk.e() + - DiyFp::kSignificandSize)); - // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a - // 64 bit significand and ten_mk is thus only precise up to 64 bits. - - // The DiyFp::Times procedure rounds its result, and ten_mk is approximated - // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now - // off by a small amount. - // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. - // In other words: let f = scaled_w.f() and e = scaled_w.e(), then - // (f-1) * 2^e < w*10^k < (f+1) * 2^e - DiyFp scaled_w = DiyFp::Times(w, ten_mk); - ASSERT(scaled_w.e() == - boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize); - // In theory it would be possible to avoid some recomputations by computing - // the difference between w and boundary_minus/plus (a power of 2) and to - // compute scaled_boundary_minus/plus by subtracting/adding from - // scaled_w. However the code becomes much less readable and the speed - // enhancements are not terriffic. - DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk); - DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk); - - // DigitGen will generate the digits of scaled_w. Therefore we have - // v == (double) (scaled_w * 10^-mk). - // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an - // integer than it will be updated. For instance if scaled_w == 1.23 then - // the buffer will be filled with "123" und the decimal_exponent will be - // decreased by 2. - int kappa; - bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus, - buffer, length, &kappa); - *decimal_exponent = -mk + kappa; - return result; -} - - -// The "counted" version of grisu3 (see above) only generates requested_digits -// number of digits. This version does not generate the shortest representation, -// and with enough requested digits 0.1 will at some point print as 0.9999999... -// Grisu3 is too imprecise for real halfway cases (1.5 will not work) and -// therefore the rounding strategy for halfway cases is irrelevant. -static bool Grisu3Counted(double v, - int requested_digits, - Vector buffer, - int* length, - int* decimal_exponent) { - DiyFp w = Double(v).AsNormalizedDiyFp(); - DiyFp ten_mk; // Cached power of ten: 10^-k - int mk; // -k - int ten_mk_minimal_binary_exponent = - kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); - int ten_mk_maximal_binary_exponent = - kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); - PowersOfTenCache::GetCachedPowerForBinaryExponentRange( - ten_mk_minimal_binary_exponent, - ten_mk_maximal_binary_exponent, - &ten_mk, &mk); - ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + - DiyFp::kSignificandSize) && - (kMaximalTargetExponent >= w.e() + ten_mk.e() + - DiyFp::kSignificandSize)); - // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a - // 64 bit significand and ten_mk is thus only precise up to 64 bits. - - // The DiyFp::Times procedure rounds its result, and ten_mk is approximated - // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now - // off by a small amount. - // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. - // In other words: let f = scaled_w.f() and e = scaled_w.e(), then - // (f-1) * 2^e < w*10^k < (f+1) * 2^e - DiyFp scaled_w = DiyFp::Times(w, ten_mk); - - // We now have (double) (scaled_w * 10^-mk). - // DigitGen will generate the first requested_digits digits of scaled_w and - // return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It - // will not always be exactly the same since DigitGenCounted only produces a - // limited number of digits.) - int kappa; - bool result = DigitGenCounted(scaled_w, requested_digits, - buffer, length, &kappa); - *decimal_exponent = -mk + kappa; - return result; -} - - -bool FastDtoa(double v, - FastDtoaMode mode, - int requested_digits, - Vector buffer, - int* length, - int* decimal_point) { - ASSERT(v > 0); - ASSERT(!Double(v).IsSpecial()); - - bool result = false; - int decimal_exponent = 0; - switch (mode) { - case FAST_DTOA_SHORTEST: - case FAST_DTOA_SHORTEST_SINGLE: - result = Grisu3(v, mode, buffer, length, &decimal_exponent); - break; - case FAST_DTOA_PRECISION: - result = Grisu3Counted(v, requested_digits, - buffer, length, &decimal_exponent); - break; - default: - UNREACHABLE(); - } - if (result) { - *decimal_point = *length + decimal_exponent; - buffer[*length] = '\0'; - } - return result; -} - -} // namespace double_conversion diff --git a/3rdparty/double-conversion/fast-dtoa.h b/3rdparty/double-conversion/fast-dtoa.h deleted file mode 100644 index 5f1e8eee5e..0000000000 --- a/3rdparty/double-conversion/fast-dtoa.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DOUBLE_CONVERSION_FAST_DTOA_H_ -#define DOUBLE_CONVERSION_FAST_DTOA_H_ - -#include "utils.h" - -namespace double_conversion { - -enum FastDtoaMode { - // Computes the shortest representation of the given input. The returned - // result will be the most accurate number of this length. Longer - // representations might be more accurate. - FAST_DTOA_SHORTEST, - // Same as FAST_DTOA_SHORTEST but for single-precision floats. - FAST_DTOA_SHORTEST_SINGLE, - // Computes a representation where the precision (number of digits) is - // given as input. The precision is independent of the decimal point. - FAST_DTOA_PRECISION -}; - -// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not -// include the terminating '\0' character. -static const int kFastDtoaMaximalLength = 17; -// Same for single-precision numbers. -static const int kFastDtoaMaximalSingleLength = 9; - -// Provides a decimal representation of v. -// The result should be interpreted as buffer * 10^(point - length). -// -// Precondition: -// * v must be a strictly positive finite double. -// -// Returns true if it succeeds, otherwise the result can not be trusted. -// There will be *length digits inside the buffer followed by a null terminator. -// If the function returns true and mode equals -// - FAST_DTOA_SHORTEST, then -// the parameter requested_digits is ignored. -// The result satisfies -// v == (double) (buffer * 10^(point - length)). -// The digits in the buffer are the shortest representation possible. E.g. -// if 0.099999999999 and 0.1 represent the same double then "1" is returned -// with point = 0. -// The last digit will be closest to the actual v. That is, even if several -// digits might correctly yield 'v' when read again, the buffer will contain -// the one closest to v. -// - FAST_DTOA_PRECISION, then -// the buffer contains requested_digits digits. -// the difference v - (buffer * 10^(point-length)) is closest to zero for -// all possible representations of requested_digits digits. -// If there are two values that are equally close, then FastDtoa returns -// false. -// For both modes the buffer must be large enough to hold the result. -bool FastDtoa(double d, - FastDtoaMode mode, - int requested_digits, - Vector buffer, - int* length, - int* decimal_point); - -} // namespace double_conversion - -#endif // DOUBLE_CONVERSION_FAST_DTOA_H_ diff --git a/3rdparty/double-conversion/fixed-dtoa.cc b/3rdparty/double-conversion/fixed-dtoa.cc deleted file mode 100644 index d56b1449b2..0000000000 --- a/3rdparty/double-conversion/fixed-dtoa.cc +++ /dev/null @@ -1,402 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include - -#include "fixed-dtoa.h" -#include "ieee.h" - -namespace double_conversion { - -// Represents a 128bit type. This class should be replaced by a native type on -// platforms that support 128bit integers. -class UInt128 { - public: - UInt128() : high_bits_(0), low_bits_(0) { } - UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) { } - - void Multiply(uint32_t multiplicand) { - uint64_t accumulator; - - accumulator = (low_bits_ & kMask32) * multiplicand; - uint32_t part = static_cast(accumulator & kMask32); - accumulator >>= 32; - accumulator = accumulator + (low_bits_ >> 32) * multiplicand; - low_bits_ = (accumulator << 32) + part; - accumulator >>= 32; - accumulator = accumulator + (high_bits_ & kMask32) * multiplicand; - part = static_cast(accumulator & kMask32); - accumulator >>= 32; - accumulator = accumulator + (high_bits_ >> 32) * multiplicand; - high_bits_ = (accumulator << 32) + part; - ASSERT((accumulator >> 32) == 0); - } - - void Shift(int shift_amount) { - ASSERT(-64 <= shift_amount && shift_amount <= 64); - if (shift_amount == 0) { - return; - } else if (shift_amount == -64) { - high_bits_ = low_bits_; - low_bits_ = 0; - } else if (shift_amount == 64) { - low_bits_ = high_bits_; - high_bits_ = 0; - } else if (shift_amount <= 0) { - high_bits_ <<= -shift_amount; - high_bits_ += low_bits_ >> (64 + shift_amount); - low_bits_ <<= -shift_amount; - } else { - low_bits_ >>= shift_amount; - low_bits_ += high_bits_ << (64 - shift_amount); - high_bits_ >>= shift_amount; - } - } - - // Modifies *this to *this MOD (2^power). - // Returns *this DIV (2^power). - int DivModPowerOf2(int power) { - if (power >= 64) { - int result = static_cast(high_bits_ >> (power - 64)); - high_bits_ -= static_cast(result) << (power - 64); - return result; - } else { - uint64_t part_low = low_bits_ >> power; - uint64_t part_high = high_bits_ << (64 - power); - int result = static_cast(part_low + part_high); - high_bits_ = 0; - low_bits_ -= part_low << power; - return result; - } - } - - bool IsZero() const { - return high_bits_ == 0 && low_bits_ == 0; - } - - int BitAt(int position) { - if (position >= 64) { - return static_cast(high_bits_ >> (position - 64)) & 1; - } else { - return static_cast(low_bits_ >> position) & 1; - } - } - - private: - static const uint64_t kMask32 = 0xFFFFFFFF; - // Value == (high_bits_ << 64) + low_bits_ - uint64_t high_bits_; - uint64_t low_bits_; -}; - - -static const int kDoubleSignificandSize = 53; // Includes the hidden bit. - - -static void FillDigits32FixedLength(uint32_t number, int requested_length, - Vector buffer, int* length) { - for (int i = requested_length - 1; i >= 0; --i) { - buffer[(*length) + i] = '0' + number % 10; - number /= 10; - } - *length += requested_length; -} - - -static void FillDigits32(uint32_t number, Vector buffer, int* length) { - int number_length = 0; - // We fill the digits in reverse order and exchange them afterwards. - while (number != 0) { - int digit = number % 10; - number /= 10; - buffer[(*length) + number_length] = '0' + digit; - number_length++; - } - // Exchange the digits. - int i = *length; - int j = *length + number_length - 1; - while (i < j) { - char tmp = buffer[i]; - buffer[i] = buffer[j]; - buffer[j] = tmp; - i++; - j--; - } - *length += number_length; -} - - -static void FillDigits64FixedLength(uint64_t number, int requested_length, - Vector buffer, int* length) { - const uint32_t kTen7 = 10000000; - // For efficiency cut the number into 3 uint32_t parts, and print those. - uint32_t part2 = static_cast(number % kTen7); - number /= kTen7; - uint32_t part1 = static_cast(number % kTen7); - uint32_t part0 = static_cast(number / kTen7); - - FillDigits32FixedLength(part0, 3, buffer, length); - FillDigits32FixedLength(part1, 7, buffer, length); - FillDigits32FixedLength(part2, 7, buffer, length); -} - - -static void FillDigits64(uint64_t number, Vector buffer, int* length) { - const uint32_t kTen7 = 10000000; - // For efficiency cut the number into 3 uint32_t parts, and print those. - uint32_t part2 = static_cast(number % kTen7); - number /= kTen7; - uint32_t part1 = static_cast(number % kTen7); - uint32_t part0 = static_cast(number / kTen7); - - if (part0 != 0) { - FillDigits32(part0, buffer, length); - FillDigits32FixedLength(part1, 7, buffer, length); - FillDigits32FixedLength(part2, 7, buffer, length); - } else if (part1 != 0) { - FillDigits32(part1, buffer, length); - FillDigits32FixedLength(part2, 7, buffer, length); - } else { - FillDigits32(part2, buffer, length); - } -} - - -static void RoundUp(Vector buffer, int* length, int* decimal_point) { - // An empty buffer represents 0. - if (*length == 0) { - buffer[0] = '1'; - *decimal_point = 1; - *length = 1; - return; - } - // Round the last digit until we either have a digit that was not '9' or until - // we reached the first digit. - buffer[(*length) - 1]++; - for (int i = (*length) - 1; i > 0; --i) { - if (buffer[i] != '0' + 10) { - return; - } - buffer[i] = '0'; - buffer[i - 1]++; - } - // If the first digit is now '0' + 10, we would need to set it to '0' and add - // a '1' in front. However we reach the first digit only if all following - // digits had been '9' before rounding up. Now all trailing digits are '0' and - // we simply switch the first digit to '1' and update the decimal-point - // (indicating that the point is now one digit to the right). - if (buffer[0] == '0' + 10) { - buffer[0] = '1'; - (*decimal_point)++; - } -} - - -// The given fractionals number represents a fixed-point number with binary -// point at bit (-exponent). -// Preconditions: -// -128 <= exponent <= 0. -// 0 <= fractionals * 2^exponent < 1 -// The buffer holds the result. -// The function will round its result. During the rounding-process digits not -// generated by this function might be updated, and the decimal-point variable -// might be updated. If this function generates the digits 99 and the buffer -// already contained "199" (thus yielding a buffer of "19999") then a -// rounding-up will change the contents of the buffer to "20000". -static void FillFractionals(uint64_t fractionals, int exponent, - int fractional_count, Vector buffer, - int* length, int* decimal_point) { - ASSERT(-128 <= exponent && exponent <= 0); - // 'fractionals' is a fixed-point number, with binary point at bit - // (-exponent). Inside the function the non-converted remainder of fractionals - // is a fixed-point number, with binary point at bit 'point'. - if (-exponent <= 64) { - // One 64 bit number is sufficient. - ASSERT(fractionals >> 56 == 0); - int point = -exponent; - for (int i = 0; i < fractional_count; ++i) { - if (fractionals == 0) break; - // Instead of multiplying by 10 we multiply by 5 and adjust the point - // location. This way the fractionals variable will not overflow. - // Invariant at the beginning of the loop: fractionals < 2^point. - // Initially we have: point <= 64 and fractionals < 2^56 - // After each iteration the point is decremented by one. - // Note that 5^3 = 125 < 128 = 2^7. - // Therefore three iterations of this loop will not overflow fractionals - // (even without the subtraction at the end of the loop body). At this - // time point will satisfy point <= 61 and therefore fractionals < 2^point - // and any further multiplication of fractionals by 5 will not overflow. - fractionals *= 5; - point--; - int digit = static_cast(fractionals >> point); - buffer[*length] = '0' + digit; - (*length)++; - fractionals -= static_cast(digit) << point; - } - // If the first bit after the point is set we have to round up. - if (((fractionals >> (point - 1)) & 1) == 1) { - RoundUp(buffer, length, decimal_point); - } - } else { // We need 128 bits. - ASSERT(64 < -exponent && -exponent <= 128); - UInt128 fractionals128 = UInt128(fractionals, 0); - fractionals128.Shift(-exponent - 64); - int point = 128; - for (int i = 0; i < fractional_count; ++i) { - if (fractionals128.IsZero()) break; - // As before: instead of multiplying by 10 we multiply by 5 and adjust the - // point location. - // This multiplication will not overflow for the same reasons as before. - fractionals128.Multiply(5); - point--; - int digit = fractionals128.DivModPowerOf2(point); - buffer[*length] = '0' + digit; - (*length)++; - } - if (fractionals128.BitAt(point - 1) == 1) { - RoundUp(buffer, length, decimal_point); - } - } -} - - -// Removes leading and trailing zeros. -// If leading zeros are removed then the decimal point position is adjusted. -static void TrimZeros(Vector buffer, int* length, int* decimal_point) { - while (*length > 0 && buffer[(*length) - 1] == '0') { - (*length)--; - } - int first_non_zero = 0; - while (first_non_zero < *length && buffer[first_non_zero] == '0') { - first_non_zero++; - } - if (first_non_zero != 0) { - for (int i = first_non_zero; i < *length; ++i) { - buffer[i - first_non_zero] = buffer[i]; - } - *length -= first_non_zero; - *decimal_point -= first_non_zero; - } -} - - -bool FastFixedDtoa(double v, - int fractional_count, - Vector buffer, - int* length, - int* decimal_point) { - const uint32_t kMaxUInt32 = 0xFFFFFFFF; - uint64_t significand = Double(v).Significand(); - int exponent = Double(v).Exponent(); - // v = significand * 2^exponent (with significand a 53bit integer). - // If the exponent is larger than 20 (i.e. we may have a 73bit number) then we - // don't know how to compute the representation. 2^73 ~= 9.5*10^21. - // If necessary this limit could probably be increased, but we don't need - // more. - if (exponent > 20) return false; - if (fractional_count > 20) return false; - *length = 0; - // At most kDoubleSignificandSize bits of the significand are non-zero. - // Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero - // bits: 0..11*..0xxx..53*..xx - if (exponent + kDoubleSignificandSize > 64) { - // The exponent must be > 11. - // - // We know that v = significand * 2^exponent. - // And the exponent > 11. - // We simplify the task by dividing v by 10^17. - // The quotient delivers the first digits, and the remainder fits into a 64 - // bit number. - // Dividing by 10^17 is equivalent to dividing by 5^17*2^17. - const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17 - uint64_t divisor = kFive17; - int divisor_power = 17; - uint64_t dividend = significand; - uint32_t quotient; - uint64_t remainder; - // Let v = f * 2^e with f == significand and e == exponent. - // Then need q (quotient) and r (remainder) as follows: - // v = q * 10^17 + r - // f * 2^e = q * 10^17 + r - // f * 2^e = q * 5^17 * 2^17 + r - // If e > 17 then - // f * 2^(e-17) = q * 5^17 + r/2^17 - // else - // f = q * 5^17 * 2^(17-e) + r/2^e - if (exponent > divisor_power) { - // We only allow exponents of up to 20 and therefore (17 - e) <= 3 - dividend <<= exponent - divisor_power; - quotient = static_cast(dividend / divisor); - remainder = (dividend % divisor) << divisor_power; - } else { - divisor <<= divisor_power - exponent; - quotient = static_cast(dividend / divisor); - remainder = (dividend % divisor) << exponent; - } - FillDigits32(quotient, buffer, length); - FillDigits64FixedLength(remainder, divisor_power, buffer, length); - *decimal_point = *length; - } else if (exponent >= 0) { - // 0 <= exponent <= 11 - significand <<= exponent; - FillDigits64(significand, buffer, length); - *decimal_point = *length; - } else if (exponent > -kDoubleSignificandSize) { - // We have to cut the number. - uint64_t integrals = significand >> -exponent; - uint64_t fractionals = significand - (integrals << -exponent); - if (integrals > kMaxUInt32) { - FillDigits64(integrals, buffer, length); - } else { - FillDigits32(static_cast(integrals), buffer, length); - } - *decimal_point = *length; - FillFractionals(fractionals, exponent, fractional_count, - buffer, length, decimal_point); - } else if (exponent < -128) { - // This configuration (with at most 20 digits) means that all digits must be - // 0. - ASSERT(fractional_count <= 20); - buffer[0] = '\0'; - *length = 0; - *decimal_point = -fractional_count; - } else { - *decimal_point = 0; - FillFractionals(significand, exponent, fractional_count, - buffer, length, decimal_point); - } - TrimZeros(buffer, length, decimal_point); - buffer[*length] = '\0'; - if ((*length) == 0) { - // The string is empty and the decimal_point thus has no importance. Mimick - // Gay's dtoa and and set it to -fractional_count. - *decimal_point = -fractional_count; - } - return true; -} - -} // namespace double_conversion diff --git a/3rdparty/double-conversion/fixed-dtoa.h b/3rdparty/double-conversion/fixed-dtoa.h deleted file mode 100644 index 3bdd08e21f..0000000000 --- a/3rdparty/double-conversion/fixed-dtoa.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DOUBLE_CONVERSION_FIXED_DTOA_H_ -#define DOUBLE_CONVERSION_FIXED_DTOA_H_ - -#include "utils.h" - -namespace double_conversion { - -// Produces digits necessary to print a given number with -// 'fractional_count' digits after the decimal point. -// The buffer must be big enough to hold the result plus one terminating null -// character. -// -// The produced digits might be too short in which case the caller has to fill -// the gaps with '0's. -// Example: FastFixedDtoa(0.001, 5, ...) is allowed to return buffer = "1", and -// decimal_point = -2. -// Halfway cases are rounded towards +/-Infinity (away from 0). The call -// FastFixedDtoa(0.15, 2, ...) thus returns buffer = "2", decimal_point = 0. -// The returned buffer may contain digits that would be truncated from the -// shortest representation of the input. -// -// This method only works for some parameters. If it can't handle the input it -// returns false. The output is null-terminated when the function succeeds. -bool FastFixedDtoa(double v, int fractional_count, - Vector buffer, int* length, int* decimal_point); - -} // namespace double_conversion - -#endif // DOUBLE_CONVERSION_FIXED_DTOA_H_ diff --git a/3rdparty/double-conversion/ieee.h b/3rdparty/double-conversion/ieee.h deleted file mode 100644 index 839dc47d45..0000000000 --- a/3rdparty/double-conversion/ieee.h +++ /dev/null @@ -1,398 +0,0 @@ -// Copyright 2012 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DOUBLE_CONVERSION_DOUBLE_H_ -#define DOUBLE_CONVERSION_DOUBLE_H_ - -#include "diy-fp.h" - -namespace double_conversion { - -// We assume that doubles and uint64_t have the same endianness. -static uint64_t double_to_uint64(double d) { return BitCast(d); } -static double uint64_to_double(uint64_t d64) { return BitCast(d64); } -static uint32_t float_to_uint32(float f) { return BitCast(f); } -static float uint32_to_float(uint32_t d32) { return BitCast(d32); } - -// Helper functions for doubles. -class Double { - public: - static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000); - static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000); - static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); - static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000); - static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit. - static const int kSignificandSize = 53; - - Double() : d64_(0) {} - explicit Double(double d) : d64_(double_to_uint64(d)) {} - explicit Double(uint64_t d64) : d64_(d64) {} - explicit Double(DiyFp diy_fp) - : d64_(DiyFpToUint64(diy_fp)) {} - - // The value encoded by this Double must be greater or equal to +0.0. - // It must not be special (infinity, or NaN). - DiyFp AsDiyFp() const { - ASSERT(Sign() > 0); - ASSERT(!IsSpecial()); - return DiyFp(Significand(), Exponent()); - } - - // The value encoded by this Double must be strictly greater than 0. - DiyFp AsNormalizedDiyFp() const { - ASSERT(value() > 0.0); - uint64_t f = Significand(); - int e = Exponent(); - - // The current double could be a denormal. - while ((f & kHiddenBit) == 0) { - f <<= 1; - e--; - } - // Do the final shifts in one go. - f <<= DiyFp::kSignificandSize - kSignificandSize; - e -= DiyFp::kSignificandSize - kSignificandSize; - return DiyFp(f, e); - } - - // Returns the double's bit as uint64. - uint64_t AsUint64() const { - return d64_; - } - - // Returns the next greater double. Returns +infinity on input +infinity. - double NextDouble() const { - if (d64_ == kInfinity) return Double(kInfinity).value(); - if (Sign() < 0 && Significand() == 0) { - // -0.0 - return 0.0; - } - if (Sign() < 0) { - return Double(d64_ - 1).value(); - } else { - return Double(d64_ + 1).value(); - } - } - - double PreviousDouble() const { - if (d64_ == (kInfinity | kSignMask)) return -Double::Infinity(); - if (Sign() < 0) { - return Double(d64_ + 1).value(); - } else { - if (Significand() == 0) return -0.0; - return Double(d64_ - 1).value(); - } - } - - int Exponent() const { - if (IsDenormal()) return kDenormalExponent; - - uint64_t d64 = AsUint64(); - int biased_e = - static_cast((d64 & kExponentMask) >> kPhysicalSignificandSize); - return biased_e - kExponentBias; - } - - uint64_t Significand() const { - uint64_t d64 = AsUint64(); - uint64_t significand = d64 & kSignificandMask; - if (!IsDenormal()) { - return significand + kHiddenBit; - } else { - return significand; - } - } - - // Returns true if the double is a denormal. - bool IsDenormal() const { - uint64_t d64 = AsUint64(); - return (d64 & kExponentMask) == 0; - } - - // We consider denormals not to be special. - // Hence only Infinity and NaN are special. - bool IsSpecial() const { - uint64_t d64 = AsUint64(); - return (d64 & kExponentMask) == kExponentMask; - } - - bool IsNan() const { - uint64_t d64 = AsUint64(); - return ((d64 & kExponentMask) == kExponentMask) && - ((d64 & kSignificandMask) != 0); - } - - bool IsInfinite() const { - uint64_t d64 = AsUint64(); - return ((d64 & kExponentMask) == kExponentMask) && - ((d64 & kSignificandMask) == 0); - } - - int Sign() const { - uint64_t d64 = AsUint64(); - return (d64 & kSignMask) == 0? 1: -1; - } - - // Precondition: the value encoded by this Double must be greater or equal - // than +0.0. - DiyFp UpperBoundary() const { - ASSERT(Sign() > 0); - return DiyFp(Significand() * 2 + 1, Exponent() - 1); - } - - // Computes the two boundaries of this. - // The bigger boundary (m_plus) is normalized. The lower boundary has the same - // exponent as m_plus. - // Precondition: the value encoded by this Double must be greater than 0. - void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { - ASSERT(value() > 0.0); - DiyFp v = this->AsDiyFp(); - DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); - DiyFp m_minus; - if (LowerBoundaryIsCloser()) { - m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); - } else { - m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); - } - m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); - m_minus.set_e(m_plus.e()); - *out_m_plus = m_plus; - *out_m_minus = m_minus; - } - - bool LowerBoundaryIsCloser() const { - // The boundary is closer if the significand is of the form f == 2^p-1 then - // the lower boundary is closer. - // Think of v = 1000e10 and v- = 9999e9. - // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but - // at a distance of 1e8. - // The only exception is for the smallest normal: the largest denormal is - // at the same distance as its successor. - // Note: denormals have the same exponent as the smallest normals. - bool physical_significand_is_zero = ((AsUint64() & kSignificandMask) == 0); - return physical_significand_is_zero && (Exponent() != kDenormalExponent); - } - - double value() const { return uint64_to_double(d64_); } - - // Returns the significand size for a given order of magnitude. - // If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude. - // This function returns the number of significant binary digits v will have - // once it's encoded into a double. In almost all cases this is equal to - // kSignificandSize. The only exceptions are denormals. They start with - // leading zeroes and their effective significand-size is hence smaller. - static int SignificandSizeForOrderOfMagnitude(int order) { - if (order >= (kDenormalExponent + kSignificandSize)) { - return kSignificandSize; - } - if (order <= kDenormalExponent) return 0; - return order - kDenormalExponent; - } - - static double Infinity() { - return Double(kInfinity).value(); - } - - static double NaN() { - return Double(kNaN).value(); - } - - private: - static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; - static const int kDenormalExponent = -kExponentBias + 1; - static const int kMaxExponent = 0x7FF - kExponentBias; - static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000); - static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000); - - const uint64_t d64_; - - static uint64_t DiyFpToUint64(DiyFp diy_fp) { - uint64_t significand = diy_fp.f(); - int exponent = diy_fp.e(); - while (significand > kHiddenBit + kSignificandMask) { - significand >>= 1; - exponent++; - } - if (exponent >= kMaxExponent) { - return kInfinity; - } - if (exponent < kDenormalExponent) { - return 0; - } - while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) { - significand <<= 1; - exponent--; - } - uint64_t biased_exponent; - if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) { - biased_exponent = 0; - } else { - biased_exponent = static_cast(exponent + kExponentBias); - } - return (significand & kSignificandMask) | - (biased_exponent << kPhysicalSignificandSize); - } -}; - -class Single { - public: - static const uint32_t kSignMask = 0x80000000; - static const uint32_t kExponentMask = 0x7F800000; - static const uint32_t kSignificandMask = 0x007FFFFF; - static const uint32_t kHiddenBit = 0x00800000; - static const int kPhysicalSignificandSize = 23; // Excludes the hidden bit. - static const int kSignificandSize = 24; - - Single() : d32_(0) {} - explicit Single(float f) : d32_(float_to_uint32(f)) {} - explicit Single(uint32_t d32) : d32_(d32) {} - - // The value encoded by this Single must be greater or equal to +0.0. - // It must not be special (infinity, or NaN). - DiyFp AsDiyFp() const { - ASSERT(Sign() > 0); - ASSERT(!IsSpecial()); - return DiyFp(Significand(), Exponent()); - } - - // Returns the single's bit as uint64. - uint32_t AsUint32() const { - return d32_; - } - - int Exponent() const { - if (IsDenormal()) return kDenormalExponent; - - uint32_t d32 = AsUint32(); - int biased_e = - static_cast((d32 & kExponentMask) >> kPhysicalSignificandSize); - return biased_e - kExponentBias; - } - - uint32_t Significand() const { - uint32_t d32 = AsUint32(); - uint32_t significand = d32 & kSignificandMask; - if (!IsDenormal()) { - return significand + kHiddenBit; - } else { - return significand; - } - } - - // Returns true if the single is a denormal. - bool IsDenormal() const { - uint32_t d32 = AsUint32(); - return (d32 & kExponentMask) == 0; - } - - // We consider denormals not to be special. - // Hence only Infinity and NaN are special. - bool IsSpecial() const { - uint32_t d32 = AsUint32(); - return (d32 & kExponentMask) == kExponentMask; - } - - bool IsNan() const { - uint32_t d32 = AsUint32(); - return ((d32 & kExponentMask) == kExponentMask) && - ((d32 & kSignificandMask) != 0); - } - - bool IsInfinite() const { - uint32_t d32 = AsUint32(); - return ((d32 & kExponentMask) == kExponentMask) && - ((d32 & kSignificandMask) == 0); - } - - int Sign() const { - uint32_t d32 = AsUint32(); - return (d32 & kSignMask) == 0? 1: -1; - } - - // Computes the two boundaries of this. - // The bigger boundary (m_plus) is normalized. The lower boundary has the same - // exponent as m_plus. - // Precondition: the value encoded by this Single must be greater than 0. - void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { - ASSERT(value() > 0.0); - DiyFp v = this->AsDiyFp(); - DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); - DiyFp m_minus; - if (LowerBoundaryIsCloser()) { - m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); - } else { - m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); - } - m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); - m_minus.set_e(m_plus.e()); - *out_m_plus = m_plus; - *out_m_minus = m_minus; - } - - // Precondition: the value encoded by this Single must be greater or equal - // than +0.0. - DiyFp UpperBoundary() const { - ASSERT(Sign() > 0); - return DiyFp(Significand() * 2 + 1, Exponent() - 1); - } - - bool LowerBoundaryIsCloser() const { - // The boundary is closer if the significand is of the form f == 2^p-1 then - // the lower boundary is closer. - // Think of v = 1000e10 and v- = 9999e9. - // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but - // at a distance of 1e8. - // The only exception is for the smallest normal: the largest denormal is - // at the same distance as its successor. - // Note: denormals have the same exponent as the smallest normals. - bool physical_significand_is_zero = ((AsUint32() & kSignificandMask) == 0); - return physical_significand_is_zero && (Exponent() != kDenormalExponent); - } - - float value() const { return uint32_to_float(d32_); } - - static float Infinity() { - return Single(kInfinity).value(); - } - - static float NaN() { - return Single(kNaN).value(); - } - - private: - static const int kExponentBias = 0x7F + kPhysicalSignificandSize; - static const int kDenormalExponent = -kExponentBias + 1; - static const int kMaxExponent = 0xFF - kExponentBias; - static const uint32_t kInfinity = 0x7F800000; - static const uint32_t kNaN = 0x7FC00000; - - const uint32_t d32_; -}; - -} // namespace double_conversion - -#endif // DOUBLE_CONVERSION_DOUBLE_H_ diff --git a/3rdparty/double-conversion/strtod.cc b/3rdparty/double-conversion/strtod.cc deleted file mode 100644 index 9758989f71..0000000000 --- a/3rdparty/double-conversion/strtod.cc +++ /dev/null @@ -1,554 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include - -#include "strtod.h" -#include "bignum.h" -#include "cached-powers.h" -#include "ieee.h" - -namespace double_conversion { - -// 2^53 = 9007199254740992. -// Any integer with at most 15 decimal digits will hence fit into a double -// (which has a 53bit significand) without loss of precision. -static const int kMaxExactDoubleIntegerDecimalDigits = 15; -// 2^64 = 18446744073709551616 > 10^19 -static const int kMaxUint64DecimalDigits = 19; - -// Max double: 1.7976931348623157 x 10^308 -// Min non-zero double: 4.9406564584124654 x 10^-324 -// Any x >= 10^309 is interpreted as +infinity. -// Any x <= 10^-324 is interpreted as 0. -// Note that 2.5e-324 (despite being smaller than the min double) will be read -// as non-zero (equal to the min non-zero double). -static const int kMaxDecimalPower = 309; -static const int kMinDecimalPower = -324; - -// 2^64 = 18446744073709551616 -static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); - - -static const double exact_powers_of_ten[] = { - 1.0, // 10^0 - 10.0, - 100.0, - 1000.0, - 10000.0, - 100000.0, - 1000000.0, - 10000000.0, - 100000000.0, - 1000000000.0, - 10000000000.0, // 10^10 - 100000000000.0, - 1000000000000.0, - 10000000000000.0, - 100000000000000.0, - 1000000000000000.0, - 10000000000000000.0, - 100000000000000000.0, - 1000000000000000000.0, - 10000000000000000000.0, - 100000000000000000000.0, // 10^20 - 1000000000000000000000.0, - // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22 - 10000000000000000000000.0 -}; -static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten); - -// Maximum number of significant digits in the decimal representation. -// In fact the value is 772 (see conversions.cc), but to give us some margin -// we round up to 780. -static const int kMaxSignificantDecimalDigits = 780; - -static Vector TrimLeadingZeros(Vector buffer) { - for (int i = 0; i < buffer.length(); i++) { - if (buffer[i] != '0') { - return buffer.SubVector(i, buffer.length()); - } - } - return Vector(buffer.start(), 0); -} - - -static Vector TrimTrailingZeros(Vector buffer) { - for (int i = buffer.length() - 1; i >= 0; --i) { - if (buffer[i] != '0') { - return buffer.SubVector(0, i + 1); - } - } - return Vector(buffer.start(), 0); -} - - -static void CutToMaxSignificantDigits(Vector buffer, - int exponent, - char* significant_buffer, - int* significant_exponent) { - for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) { - significant_buffer[i] = buffer[i]; - } - // The input buffer has been trimmed. Therefore the last digit must be - // different from '0'. - ASSERT(buffer[buffer.length() - 1] != '0'); - // Set the last digit to be non-zero. This is sufficient to guarantee - // correct rounding. - significant_buffer[kMaxSignificantDecimalDigits - 1] = '1'; - *significant_exponent = - exponent + (buffer.length() - kMaxSignificantDecimalDigits); -} - - -// Trims the buffer and cuts it to at most kMaxSignificantDecimalDigits. -// If possible the input-buffer is reused, but if the buffer needs to be -// modified (due to cutting), then the input needs to be copied into the -// buffer_copy_space. -static void TrimAndCut(Vector buffer, int exponent, - char* buffer_copy_space, int space_size, - Vector* trimmed, int* updated_exponent) { - Vector left_trimmed = TrimLeadingZeros(buffer); - Vector right_trimmed = TrimTrailingZeros(left_trimmed); - exponent += left_trimmed.length() - right_trimmed.length(); - if (right_trimmed.length() > kMaxSignificantDecimalDigits) { - ASSERT(space_size >= kMaxSignificantDecimalDigits); - CutToMaxSignificantDigits(right_trimmed, exponent, - buffer_copy_space, updated_exponent); - *trimmed = Vector(buffer_copy_space, - kMaxSignificantDecimalDigits); - } else { - *trimmed = right_trimmed; - *updated_exponent = exponent; - } -} - - -// Reads digits from the buffer and converts them to a uint64. -// Reads in as many digits as fit into a uint64. -// When the string starts with "1844674407370955161" no further digit is read. -// Since 2^64 = 18446744073709551616 it would still be possible read another -// digit if it was less or equal than 6, but this would complicate the code. -static uint64_t ReadUint64(Vector buffer, - int* number_of_read_digits) { - uint64_t result = 0; - int i = 0; - while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) { - int digit = buffer[i++] - '0'; - ASSERT(0 <= digit && digit <= 9); - result = 10 * result + digit; - } - *number_of_read_digits = i; - return result; -} - - -// Reads a DiyFp from the buffer. -// The returned DiyFp is not necessarily normalized. -// If remaining_decimals is zero then the returned DiyFp is accurate. -// Otherwise it has been rounded and has error of at most 1/2 ulp. -static void ReadDiyFp(Vector buffer, - DiyFp* result, - int* remaining_decimals) { - int read_digits; - uint64_t significand = ReadUint64(buffer, &read_digits); - if (buffer.length() == read_digits) { - *result = DiyFp(significand, 0); - *remaining_decimals = 0; - } else { - // Round the significand. - if (buffer[read_digits] >= '5') { - significand++; - } - // Compute the binary exponent. - int exponent = 0; - *result = DiyFp(significand, exponent); - *remaining_decimals = buffer.length() - read_digits; - } -} - - -static bool DoubleStrtod(Vector trimmed, - int exponent, - double* result) { -#if !defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) - // On x86 the floating-point stack can be 64 or 80 bits wide. If it is - // 80 bits wide (as is the case on Linux) then double-rounding occurs and the - // result is not accurate. - // We know that Windows32 uses 64 bits and is therefore accurate. - // Note that the ARM simulator is compiled for 32bits. It therefore exhibits - // the same problem. - return false; -#endif - if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) { - int read_digits; - // The trimmed input fits into a double. - // If the 10^exponent (resp. 10^-exponent) fits into a double too then we - // can compute the result-double simply by multiplying (resp. dividing) the - // two numbers. - // This is possible because IEEE guarantees that floating-point operations - // return the best possible approximation. - if (exponent < 0 && -exponent < kExactPowersOfTenSize) { - // 10^-exponent fits into a double. - *result = static_cast(ReadUint64(trimmed, &read_digits)); - ASSERT(read_digits == trimmed.length()); - *result /= exact_powers_of_ten[-exponent]; - return true; - } - if (0 <= exponent && exponent < kExactPowersOfTenSize) { - // 10^exponent fits into a double. - *result = static_cast(ReadUint64(trimmed, &read_digits)); - ASSERT(read_digits == trimmed.length()); - *result *= exact_powers_of_ten[exponent]; - return true; - } - int remaining_digits = - kMaxExactDoubleIntegerDecimalDigits - trimmed.length(); - if ((0 <= exponent) && - (exponent - remaining_digits < kExactPowersOfTenSize)) { - // The trimmed string was short and we can multiply it with - // 10^remaining_digits. As a result the remaining exponent now fits - // into a double too. - *result = static_cast(ReadUint64(trimmed, &read_digits)); - ASSERT(read_digits == trimmed.length()); - *result *= exact_powers_of_ten[remaining_digits]; - *result *= exact_powers_of_ten[exponent - remaining_digits]; - return true; - } - } - return false; -} - - -// Returns 10^exponent as an exact DiyFp. -// The given exponent must be in the range [1; kDecimalExponentDistance[. -static DiyFp AdjustmentPowerOfTen(int exponent) { - ASSERT(0 < exponent); - ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance); - // Simply hardcode the remaining powers for the given decimal exponent - // distance. - ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8); - switch (exponent) { - case 1: return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60); - case 2: return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57); - case 3: return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54); - case 4: return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50); - case 5: return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47); - case 6: return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44); - case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40); - default: - UNREACHABLE(); - return DiyFp(0, 0); - } -} - - -// If the function returns true then the result is the correct double. -// Otherwise it is either the correct double or the double that is just below -// the correct double. -static bool DiyFpStrtod(Vector buffer, - int exponent, - double* result) { - DiyFp input; - int remaining_decimals; - ReadDiyFp(buffer, &input, &remaining_decimals); - // Since we may have dropped some digits the input is not accurate. - // If remaining_decimals is different than 0 than the error is at most - // .5 ulp (unit in the last place). - // We don't want to deal with fractions and therefore keep a common - // denominator. - const int kDenominatorLog = 3; - const int kDenominator = 1 << kDenominatorLog; - // Move the remaining decimals into the exponent. - exponent += remaining_decimals; - int error = (remaining_decimals == 0 ? 0 : kDenominator / 2); - - int old_e = input.e(); - input.Normalize(); - error <<= old_e - input.e(); - - ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent); - if (exponent < PowersOfTenCache::kMinDecimalExponent) { - *result = 0.0; - return true; - } - DiyFp cached_power; - int cached_decimal_exponent; - PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent, - &cached_power, - &cached_decimal_exponent); - - if (cached_decimal_exponent != exponent) { - int adjustment_exponent = exponent - cached_decimal_exponent; - DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent); - input.Multiply(adjustment_power); - if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) { - // The product of input with the adjustment power fits into a 64 bit - // integer. - ASSERT(DiyFp::kSignificandSize == 64); - } else { - // The adjustment power is exact. There is hence only an error of 0.5. - error += kDenominator / 2; - } - } - - input.Multiply(cached_power); - // The error introduced by a multiplication of a*b equals - // error_a + error_b + error_a*error_b/2^64 + 0.5 - // Substituting a with 'input' and b with 'cached_power' we have - // error_b = 0.5 (all cached powers have an error of less than 0.5 ulp), - // error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64 - int error_b = kDenominator / 2; - int error_ab = (error == 0 ? 0 : 1); // We round up to 1. - int fixed_error = kDenominator / 2; - error += error_b + error_ab + fixed_error; - - old_e = input.e(); - input.Normalize(); - error <<= old_e - input.e(); - - // See if the double's significand changes if we add/subtract the error. - int order_of_magnitude = DiyFp::kSignificandSize + input.e(); - int effective_significand_size = - Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude); - int precision_digits_count = - DiyFp::kSignificandSize - effective_significand_size; - if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) { - // This can only happen for very small denormals. In this case the - // half-way multiplied by the denominator exceeds the range of an uint64. - // Simply shift everything to the right. - int shift_amount = (precision_digits_count + kDenominatorLog) - - DiyFp::kSignificandSize + 1; - input.set_f(input.f() >> shift_amount); - input.set_e(input.e() + shift_amount); - // We add 1 for the lost precision of error, and kDenominator for - // the lost precision of input.f(). - error = (error >> shift_amount) + 1 + kDenominator; - precision_digits_count -= shift_amount; - } - // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too. - ASSERT(DiyFp::kSignificandSize == 64); - ASSERT(precision_digits_count < 64); - uint64_t one64 = 1; - uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1; - uint64_t precision_bits = input.f() & precision_bits_mask; - uint64_t half_way = one64 << (precision_digits_count - 1); - precision_bits *= kDenominator; - half_way *= kDenominator; - DiyFp rounded_input(input.f() >> precision_digits_count, - input.e() + precision_digits_count); - if (precision_bits >= half_way + error) { - rounded_input.set_f(rounded_input.f() + 1); - } - // If the last_bits are too close to the half-way case than we are too - // inaccurate and round down. In this case we return false so that we can - // fall back to a more precise algorithm. - - *result = Double(rounded_input).value(); - if (half_way - error < precision_bits && precision_bits < half_way + error) { - // Too imprecise. The caller will have to fall back to a slower version. - // However the returned number is guaranteed to be either the correct - // double, or the next-lower double. - return false; - } else { - return true; - } -} - - -// Returns -// - -1 if buffer*10^exponent < diy_fp. -// - 0 if buffer*10^exponent == diy_fp. -// - +1 if buffer*10^exponent > diy_fp. -// Preconditions: -// buffer.length() + exponent <= kMaxDecimalPower + 1 -// buffer.length() + exponent > kMinDecimalPower -// buffer.length() <= kMaxDecimalSignificantDigits -static int CompareBufferWithDiyFp(Vector buffer, - int exponent, - DiyFp diy_fp) { - ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); - ASSERT(buffer.length() + exponent > kMinDecimalPower); - ASSERT(buffer.length() <= kMaxSignificantDecimalDigits); - // Make sure that the Bignum will be able to hold all our numbers. - // Our Bignum implementation has a separate field for exponents. Shifts will - // consume at most one bigit (< 64 bits). - // ln(10) == 3.3219... - ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits); - Bignum buffer_bignum; - Bignum diy_fp_bignum; - buffer_bignum.AssignDecimalString(buffer); - diy_fp_bignum.AssignUInt64(diy_fp.f()); - if (exponent >= 0) { - buffer_bignum.MultiplyByPowerOfTen(exponent); - } else { - diy_fp_bignum.MultiplyByPowerOfTen(-exponent); - } - if (diy_fp.e() > 0) { - diy_fp_bignum.ShiftLeft(diy_fp.e()); - } else { - buffer_bignum.ShiftLeft(-diy_fp.e()); - } - return Bignum::Compare(buffer_bignum, diy_fp_bignum); -} - - -// Returns true if the guess is the correct double. -// Returns false, when guess is either correct or the next-lower double. -static bool ComputeGuess(Vector trimmed, int exponent, - double* guess) { - if (trimmed.length() == 0) { - *guess = 0.0; - return true; - } - if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) { - *guess = Double::Infinity(); - return true; - } - if (exponent + trimmed.length() <= kMinDecimalPower) { - *guess = 0.0; - return true; - } - - if (DoubleStrtod(trimmed, exponent, guess) || - DiyFpStrtod(trimmed, exponent, guess)) { - return true; - } - if (*guess == Double::Infinity()) { - return true; - } - return false; -} - -double Strtod(Vector buffer, int exponent) { - char copy_buffer[kMaxSignificantDecimalDigits]; - Vector trimmed; - int updated_exponent; - TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, - &trimmed, &updated_exponent); - exponent = updated_exponent; - - double guess; - bool is_correct = ComputeGuess(trimmed, exponent, &guess); - if (is_correct) return guess; - - DiyFp upper_boundary = Double(guess).UpperBoundary(); - int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); - if (comparison < 0) { - return guess; - } else if (comparison > 0) { - return Double(guess).NextDouble(); - } else if ((Double(guess).Significand() & 1) == 0) { - // Round towards even. - return guess; - } else { - return Double(guess).NextDouble(); - } -} - -float Strtof(Vector buffer, int exponent) { - char copy_buffer[kMaxSignificantDecimalDigits]; - Vector trimmed; - int updated_exponent; - TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, - &trimmed, &updated_exponent); - exponent = updated_exponent; - - double double_guess; - bool is_correct = ComputeGuess(trimmed, exponent, &double_guess); - - float float_guess = static_cast(double_guess); - if (float_guess == double_guess) { - // This shortcut triggers for integer values. - return float_guess; - } - - // We must catch double-rounding. Say the double has been rounded up, and is - // now a boundary of a float, and rounds up again. This is why we have to - // look at previous too. - // Example (in decimal numbers): - // input: 12349 - // high-precision (4 digits): 1235 - // low-precision (3 digits): - // when read from input: 123 - // when rounded from high precision: 124. - // To do this we simply look at the neigbors of the correct result and see - // if they would round to the same float. If the guess is not correct we have - // to look at four values (since two different doubles could be the correct - // double). - - double double_next = Double(double_guess).NextDouble(); - double double_previous = Double(double_guess).PreviousDouble(); - - float f1 = static_cast(double_previous); - float f2 = float_guess; - float f3 = static_cast(double_next); - float f4; - if (is_correct) { - f4 = f3; - } else { - double double_next2 = Double(double_next).NextDouble(); - f4 = static_cast(double_next2); - } - ASSERT(f1 <= f2 && f2 <= f3 && f3 <= f4); - - // If the guess doesn't lie near a single-precision boundary we can simply - // return its float-value. - if (f1 == f4) { - return float_guess; - } - - ASSERT((f1 != f2 && f2 == f3 && f3 == f4) || - (f1 == f2 && f2 != f3 && f3 == f4) || - (f1 == f2 && f2 == f3 && f3 != f4)); - - // guess and next are the two possible canditates (in the same way that - // double_guess was the lower candidate for a double-precision guess). - float guess = f1; - float next = f4; - DiyFp upper_boundary; - if (guess == 0.0f) { - float min_float = 1e-45f; - upper_boundary = Double(static_cast(min_float) / 2).AsDiyFp(); - } else { - upper_boundary = Single(guess).UpperBoundary(); - } - int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); - if (comparison < 0) { - return guess; - } else if (comparison > 0) { - return next; - } else if ((Single(guess).Significand() & 1) == 0) { - // Round towards even. - return guess; - } else { - return next; - } -} - -} // namespace double_conversion diff --git a/3rdparty/double-conversion/strtod.h b/3rdparty/double-conversion/strtod.h deleted file mode 100644 index ed0293b8f5..0000000000 --- a/3rdparty/double-conversion/strtod.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DOUBLE_CONVERSION_STRTOD_H_ -#define DOUBLE_CONVERSION_STRTOD_H_ - -#include "utils.h" - -namespace double_conversion { - -// The buffer must only contain digits in the range [0-9]. It must not -// contain a dot or a sign. It must not start with '0', and must not be empty. -double Strtod(Vector buffer, int exponent); - -// The buffer must only contain digits in the range [0-9]. It must not -// contain a dot or a sign. It must not start with '0', and must not be empty. -float Strtof(Vector buffer, int exponent); - -} // namespace double_conversion - -#endif // DOUBLE_CONVERSION_STRTOD_H_ diff --git a/3rdparty/double-conversion/utils.h b/3rdparty/double-conversion/utils.h deleted file mode 100644 index 767094b8b7..0000000000 --- a/3rdparty/double-conversion/utils.h +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright 2010 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef DOUBLE_CONVERSION_UTILS_H_ -#define DOUBLE_CONVERSION_UTILS_H_ - -#include -#include - -#include -#ifndef ASSERT -#define ASSERT(condition) (assert(condition)) -#endif -#ifndef UNIMPLEMENTED -#define UNIMPLEMENTED() (abort()) -#endif -#ifndef UNREACHABLE -#define UNREACHABLE() (abort()) -#endif - -// Double operations detection based on target architecture. -// Linux uses a 80bit wide floating point stack on x86. This induces double -// rounding, which in turn leads to wrong results. -// An easy way to test if the floating-point operations are correct is to -// evaluate: 89255.0/1e22. If the floating-point stack is 64 bits wide then -// the result is equal to 89255e-22. -// The best way to test this, is to create a division-function and to compare -// the output of the division with the expected result. (Inlining must be -// disabled.) -// On Linux,x86 89255e-22 != Div_double(89255.0/1e22) -#if defined(_M_X64) || defined(__x86_64__) || \ - defined(__ARMEL__) || defined(__avr32__) || \ - defined(__hppa__) || defined(__ia64__) || \ - defined(__mips__) || defined(__powerpc__) || \ - defined(__sparc__) || defined(__sparc) || defined(__s390__) || \ - defined(__SH4__) || defined(__alpha__) || \ - defined(_MIPS_ARCH_MIPS32R2) -#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 -#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) -#if defined(_WIN32) -// Windows uses a 64bit wide floating point stack. -#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 -#else -#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS -#endif // _WIN32 -#else -#error Target architecture was not detected as supported by Double-Conversion. -#endif - - -#if defined(_WIN32) && !defined(__MINGW32__) - -typedef signed char int8_t; -typedef unsigned char uint8_t; -typedef short int16_t; // NOLINT -typedef unsigned short uint16_t; // NOLINT -typedef int int32_t; -typedef unsigned int uint32_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -// intptr_t and friends are defined in crtdefs.h through stdio.h. - -#else - -#include - -#endif - -// The following macro works on both 32 and 64-bit platforms. -// Usage: instead of writing 0x1234567890123456 -// write UINT64_2PART_C(0x12345678,90123456); -#define UINT64_2PART_C(a, b) (((static_cast(a) << 32) + 0x##b##u)) - - -// The expression ARRAY_SIZE(a) is a compile-time constant of type -// size_t which represents the number of elements of the given -// array. You should only use ARRAY_SIZE on statically allocated -// arrays. -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(a) \ - ((sizeof(a) / sizeof(*(a))) / \ - static_cast(!(sizeof(a) % sizeof(*(a))))) -#endif - -// A macro to disallow the evil copy constructor and operator= functions -// This should be used in the private: declarations for a class -#ifndef DISALLOW_COPY_AND_ASSIGN -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) -#endif - -// A macro to disallow all the implicit constructors, namely the -// default constructor, copy constructor and operator= functions. -// -// This should be used in the private: declarations for a class -// that wants to prevent anyone from instantiating it. This is -// especially useful for classes containing only static methods. -#ifndef DISALLOW_IMPLICIT_CONSTRUCTORS -#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ - TypeName(); \ - DISALLOW_COPY_AND_ASSIGN(TypeName) -#endif - -namespace double_conversion { - -static const int kCharSize = sizeof(char); - -// Returns the maximum of the two parameters. -template -static T Max(T a, T b) { - return a < b ? b : a; -} - - -// Returns the minimum of the two parameters. -template -static T Min(T a, T b) { - return a < b ? a : b; -} - - -inline int StrLength(const char* string) { - size_t length = strlen(string); - ASSERT(length == static_cast(static_cast(length))); - return static_cast(length); -} - -// This is a simplified version of V8's Vector class. -template -class Vector { - public: - Vector() : start_(NULL), length_(0) {} - Vector(T* data, int length) : start_(data), length_(length) { - ASSERT(length == 0 || (length > 0 && data != NULL)); - } - - // Returns a vector using the same backing storage as this one, - // spanning from and including 'from', to but not including 'to'. - Vector SubVector(int from, int to) { - ASSERT(to <= length_); - ASSERT(from < to); - ASSERT(0 <= from); - return Vector(start() + from, to - from); - } - - // Returns the length of the vector. - int length() const { return length_; } - - // Returns whether or not the vector is empty. - bool is_empty() const { return length_ == 0; } - - // Returns the pointer to the start of the data in the vector. - T* start() const { return start_; } - - // Access individual vector elements - checks bounds in debug mode. - T& operator[](int index) const { - ASSERT(0 <= index && index < length_); - return start_[index]; - } - - T& first() { return start_[0]; } - - T& last() { return start_[length_ - 1]; } - - private: - T* start_; - int length_; -}; - - -// Helper class for building result strings in a character buffer. The -// purpose of the class is to use safe operations that checks the -// buffer bounds on all operations in debug mode. -class StringBuilder { - public: - StringBuilder(char* buffer, int size) - : buffer_(buffer, size), position_(0) { } - - ~StringBuilder() { if (!is_finalized()) Finalize(); } - - int size() const { return buffer_.length(); } - - // Get the current position in the builder. - int position() const { - ASSERT(!is_finalized()); - return position_; - } - - // Reset the position. - void Reset() { position_ = 0; } - - // Add a single character to the builder. It is not allowed to add - // 0-characters; use the Finalize() method to terminate the string - // instead. - void AddCharacter(char c) { - ASSERT(c != '\0'); - ASSERT(!is_finalized() && position_ < buffer_.length()); - buffer_[position_++] = c; - } - - // Add an entire string to the builder. Uses strlen() internally to - // compute the length of the input string. - void AddString(const char* s) { - AddSubstring(s, StrLength(s)); - } - - // Add the first 'n' characters of the given string 's' to the - // builder. The input string must have enough characters. - void AddSubstring(const char* s, int n) { - ASSERT(!is_finalized() && position_ + n < buffer_.length()); - ASSERT(static_cast(n) <= strlen(s)); - memmove(&buffer_[position_], s, n * kCharSize); - position_ += n; - } - - - // Add character padding to the builder. If count is non-positive, - // nothing is added to the builder. - void AddPadding(char c, int count) { - for (int i = 0; i < count; i++) { - AddCharacter(c); - } - } - - // Finalize the string by 0-terminating it and returning the buffer. - char* Finalize() { - ASSERT(!is_finalized() && position_ < buffer_.length()); - buffer_[position_] = '\0'; - // Make sure nobody managed to add a 0-character to the - // buffer while building the string. - ASSERT(strlen(buffer_.start()) == static_cast(position_)); - position_ = -1; - ASSERT(is_finalized()); - return buffer_.start(); - } - - private: - Vector buffer_; - int position_; - - bool is_finalized() const { return position_ < 0; } - - DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder); -}; - -// The type-based aliasing rule allows the compiler to assume that pointers of -// different types (for some definition of different) never alias each other. -// Thus the following code does not work: -// -// float f = foo(); -// int fbits = *(int*)(&f); -// -// The compiler 'knows' that the int pointer can't refer to f since the types -// don't match, so the compiler may cache f in a register, leaving random data -// in fbits. Using C++ style casts makes no difference, however a pointer to -// char data is assumed to alias any other pointer. This is the 'memcpy -// exception'. -// -// Bit_cast uses the memcpy exception to move the bits from a variable of one -// type of a variable of another type. Of course the end result is likely to -// be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005) -// will completely optimize BitCast away. -// -// There is an additional use for BitCast. -// Recent gccs will warn when they see casts that may result in breakage due to -// the type-based aliasing rule. If you have checked that there is no breakage -// you can use BitCast to cast one pointer type to another. This confuses gcc -// enough that it can no longer see that you have cast one pointer type to -// another thus avoiding the warning. -template -inline Dest BitCast(const Source& source) { - // Compile time assertion: sizeof(Dest) == sizeof(Source) - // A compile error here means your Dest and Source have different sizes. - typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1]; - - Dest dest; - memmove(&dest, &source, sizeof(dest)); - return dest; -} - -template -inline Dest BitCast(Source* source) { - return BitCast(reinterpret_cast(source)); -} - -} // namespace double_conversion - -#endif // DOUBLE_CONVERSION_UTILS_H_ diff --git a/3rdparty/masm/WeakRandom.h b/3rdparty/masm/WeakRandom.h deleted file mode 100644 index 325d1f6ac6..0000000000 --- a/3rdparty/masm/WeakRandom.h +++ /dev/null @@ -1,52 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef MASM_WEAKRANDOM_H -#define MASM_WEAKRANDOM_H - -#include - -struct WeakRandom { - WeakRandom(int) {} - uint32_t getUint32() { return 0; } -}; - -#endif // MASM_WEAKRANDOM_H diff --git a/3rdparty/masm/assembler/ARMAssembler.cpp b/3rdparty/masm/assembler/ARMAssembler.cpp deleted file mode 100644 index 9655557a5d..0000000000 --- a/3rdparty/masm/assembler/ARMAssembler.cpp +++ /dev/null @@ -1,428 +0,0 @@ -/* - * Copyright (C) 2009 University of Szeged - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" - -#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) - -#include "ARMAssembler.h" - -namespace JSC { - -// Patching helpers - -void ARMAssembler::patchConstantPoolLoad(void* loadAddr, void* constPoolAddr) -{ - ARMWord *ldr = reinterpret_cast(loadAddr); - ARMWord diff = reinterpret_cast(constPoolAddr) - ldr; - ARMWord index = (*ldr & 0xfff) >> 1; - - ASSERT(diff >= 1); - if (diff >= 2 || index > 0) { - diff = (diff + index - 2) * sizeof(ARMWord); - ASSERT(diff <= 0xfff); - *ldr = (*ldr & ~0xfff) | diff; - } else - *ldr = (*ldr & ~(0xfff | ARMAssembler::DataTransferUp)) | sizeof(ARMWord); -} - -// Handle immediates - -ARMWord ARMAssembler::getOp2(ARMWord imm) -{ - int rol; - - if (imm <= 0xff) - return Op2Immediate | imm; - - if ((imm & 0xff000000) == 0) { - imm <<= 8; - rol = 8; - } - else { - imm = (imm << 24) | (imm >> 8); - rol = 0; - } - - if ((imm & 0xff000000) == 0) { - imm <<= 8; - rol += 4; - } - - if ((imm & 0xf0000000) == 0) { - imm <<= 4; - rol += 2; - } - - if ((imm & 0xc0000000) == 0) { - imm <<= 2; - rol += 1; - } - - if ((imm & 0x00ffffff) == 0) - return Op2Immediate | (imm >> 24) | (rol << 8); - - return InvalidImmediate; -} - -int ARMAssembler::genInt(int reg, ARMWord imm, bool positive) -{ - // Step1: Search a non-immediate part - ARMWord mask; - ARMWord imm1; - ARMWord imm2; - int rol; - - mask = 0xff000000; - rol = 8; - while(1) { - if ((imm & mask) == 0) { - imm = (imm << rol) | (imm >> (32 - rol)); - rol = 4 + (rol >> 1); - break; - } - rol += 2; - mask >>= 2; - if (mask & 0x3) { - // rol 8 - imm = (imm << 8) | (imm >> 24); - mask = 0xff00; - rol = 24; - while (1) { - if ((imm & mask) == 0) { - imm = (imm << rol) | (imm >> (32 - rol)); - rol = (rol >> 1) - 8; - break; - } - rol += 2; - mask >>= 2; - if (mask & 0x3) - return 0; - } - break; - } - } - - ASSERT((imm & 0xff) == 0); - - if ((imm & 0xff000000) == 0) { - imm1 = Op2Immediate | ((imm >> 16) & 0xff) | (((rol + 4) & 0xf) << 8); - imm2 = Op2Immediate | ((imm >> 8) & 0xff) | (((rol + 8) & 0xf) << 8); - } else if (imm & 0xc0000000) { - imm1 = Op2Immediate | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8); - imm <<= 8; - rol += 4; - - if ((imm & 0xff000000) == 0) { - imm <<= 8; - rol += 4; - } - - if ((imm & 0xf0000000) == 0) { - imm <<= 4; - rol += 2; - } - - if ((imm & 0xc0000000) == 0) { - imm <<= 2; - rol += 1; - } - - if ((imm & 0x00ffffff) == 0) - imm2 = Op2Immediate | (imm >> 24) | ((rol & 0xf) << 8); - else - return 0; - } else { - if ((imm & 0xf0000000) == 0) { - imm <<= 4; - rol += 2; - } - - if ((imm & 0xc0000000) == 0) { - imm <<= 2; - rol += 1; - } - - imm1 = Op2Immediate | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8); - imm <<= 8; - rol += 4; - - if ((imm & 0xf0000000) == 0) { - imm <<= 4; - rol += 2; - } - - if ((imm & 0xc0000000) == 0) { - imm <<= 2; - rol += 1; - } - - if ((imm & 0x00ffffff) == 0) - imm2 = Op2Immediate | (imm >> 24) | ((rol & 0xf) << 8); - else - return 0; - } - - if (positive) { - mov(reg, imm1); - orr(reg, reg, imm2); - } else { - mvn(reg, imm1); - bic(reg, reg, imm2); - } - - return 1; -} - -ARMWord ARMAssembler::getImm(ARMWord imm, int tmpReg, bool invert) -{ - ARMWord tmp; - - // Do it by 1 instruction - tmp = getOp2(imm); - if (tmp != InvalidImmediate) - return tmp; - - tmp = getOp2(~imm); - if (tmp != InvalidImmediate) { - if (invert) - return tmp | Op2InvertedImmediate; - mvn(tmpReg, tmp); - return tmpReg; - } - - return encodeComplexImm(imm, tmpReg); -} - -void ARMAssembler::moveImm(ARMWord imm, int dest) -{ - ARMWord tmp; - - // Do it by 1 instruction - tmp = getOp2(imm); - if (tmp != InvalidImmediate) { - mov(dest, tmp); - return; - } - - tmp = getOp2(~imm); - if (tmp != InvalidImmediate) { - mvn(dest, tmp); - return; - } - - encodeComplexImm(imm, dest); -} - -ARMWord ARMAssembler::encodeComplexImm(ARMWord imm, int dest) -{ -#if WTF_ARM_ARCH_AT_LEAST(7) - ARMWord tmp = getImm16Op2(imm); - if (tmp != InvalidImmediate) { - movw(dest, tmp); - return dest; - } - movw(dest, getImm16Op2(imm & 0xffff)); - movt(dest, getImm16Op2(imm >> 16)); - return dest; -#else - // Do it by 2 instruction - if (genInt(dest, imm, true)) - return dest; - if (genInt(dest, ~imm, false)) - return dest; - - ldrImmediate(dest, imm); - return dest; -#endif -} - -// Memory load/store helpers - -void ARMAssembler::dataTransfer32(DataTransferTypeA transferType, RegisterID srcDst, RegisterID base, int32_t offset) -{ - if (offset >= 0) { - if (offset <= 0xfff) - dtrUp(transferType, srcDst, base, offset); - else if (offset <= 0xfffff) { - add(ARMRegisters::S0, base, Op2Immediate | (offset >> 12) | (10 << 8)); - dtrUp(transferType, srcDst, ARMRegisters::S0, (offset & 0xfff)); - } else { - moveImm(offset, ARMRegisters::S0); - dtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); - } - } else { - if (offset >= -0xfff) - dtrDown(transferType, srcDst, base, -offset); - else if (offset >= -0xfffff) { - sub(ARMRegisters::S0, base, Op2Immediate | (-offset >> 12) | (10 << 8)); - dtrDown(transferType, srcDst, ARMRegisters::S0, (-offset & 0xfff)); - } else { - moveImm(offset, ARMRegisters::S0); - dtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); - } - } -} - -void ARMAssembler::baseIndexTransfer32(DataTransferTypeA transferType, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) -{ - ASSERT(scale >= 0 && scale <= 3); - ARMWord op2 = lsl(index, scale); - - if (!offset) { - dtrUpRegister(transferType, srcDst, base, op2); - return; - } - - add(ARMRegisters::S1, base, op2); - dataTransfer32(transferType, srcDst, ARMRegisters::S1, offset); -} - -void ARMAssembler::dataTransfer16(DataTransferTypeB transferType, RegisterID srcDst, RegisterID base, int32_t offset) -{ - if (offset >= 0) { - if (offset <= 0xff) - halfDtrUp(transferType, srcDst, base, getOp2Half(offset)); - else if (offset <= 0xffff) { - add(ARMRegisters::S0, base, Op2Immediate | (offset >> 8) | (12 << 8)); - halfDtrUp(transferType, srcDst, ARMRegisters::S0, getOp2Half(offset & 0xff)); - } else { - moveImm(offset, ARMRegisters::S0); - halfDtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); - } - } else { - if (offset >= -0xff) - halfDtrDown(transferType, srcDst, base, getOp2Half(-offset)); - else if (offset >= -0xffff) { - sub(ARMRegisters::S0, base, Op2Immediate | (-offset >> 8) | (12 << 8)); - halfDtrDown(transferType, srcDst, ARMRegisters::S0, getOp2Half(-offset & 0xff)); - } else { - moveImm(offset, ARMRegisters::S0); - halfDtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); - } - } -} - -void ARMAssembler::baseIndexTransfer16(DataTransferTypeB transferType, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) -{ - if (!scale && !offset) { - halfDtrUpRegister(transferType, srcDst, base, index); - return; - } - - add(ARMRegisters::S1, base, lsl(index, scale)); - dataTransfer16(transferType, srcDst, ARMRegisters::S1, offset); -} - -void ARMAssembler::dataTransferFloat(DataTransferTypeFloat transferType, FPRegisterID srcDst, RegisterID base, int32_t offset) -{ - // VFP cannot directly access memory that is not four-byte-aligned - if (!(offset & 0x3)) { - if (offset <= 0x3ff && offset >= 0) { - doubleDtrUp(transferType, srcDst, base, offset >> 2); - return; - } - if (offset <= 0x3ffff && offset >= 0) { - add(ARMRegisters::S0, base, Op2Immediate | (offset >> 10) | (11 << 8)); - doubleDtrUp(transferType, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff); - return; - } - offset = -offset; - - if (offset <= 0x3ff && offset >= 0) { - doubleDtrDown(transferType, srcDst, base, offset >> 2); - return; - } - if (offset <= 0x3ffff && offset >= 0) { - sub(ARMRegisters::S0, base, Op2Immediate | (offset >> 10) | (11 << 8)); - doubleDtrDown(transferType, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff); - return; - } - offset = -offset; - } - - moveImm(offset, ARMRegisters::S0); - add(ARMRegisters::S0, ARMRegisters::S0, base); - doubleDtrUp(transferType, srcDst, ARMRegisters::S0, 0); -} - -void ARMAssembler::baseIndexTransferFloat(DataTransferTypeFloat transferType, FPRegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) -{ - add(ARMRegisters::S1, base, lsl(index, scale)); - dataTransferFloat(transferType, srcDst, ARMRegisters::S1, offset); -} - -PassRefPtr ARMAssembler::executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) -{ - // 64-bit alignment is required for next constant pool and JIT code as well - m_buffer.flushWithoutBarrier(true); - if (!m_buffer.isAligned(8)) - bkpt(0); - - RefPtr result = m_buffer.executableCopy(globalData, ownerUID, effort); - char* data = reinterpret_cast(result->start()); - - for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) { - // The last bit is set if the constant must be placed on constant pool. - int pos = (iter->m_offset) & (~0x1); - ARMWord* ldrAddr = reinterpret_cast_ptr(data + pos); - ARMWord* addr = getLdrImmAddress(ldrAddr); - if (*addr != InvalidBranchTarget) { - if (!(iter->m_offset & 1)) { - intptr_t difference = reinterpret_cast_ptr(data + *addr) - (ldrAddr + DefaultPrefetchOffset); - - if ((difference <= MaximumBranchOffsetDistance && difference >= MinimumBranchOffsetDistance)) { - *ldrAddr = B | getConditionalField(*ldrAddr) | (difference & BranchOffsetMask); - continue; - } - } - *addr = reinterpret_cast(data + *addr); - } - } - - return result; -} - -#if OS(LINUX) && COMPILER(RVCT) - -__asm void ARMAssembler::cacheFlush(void* code, size_t size) -{ - ARM - push {r7} - add r1, r1, r0 - mov r7, #0xf0000 - add r7, r7, #0x2 - mov r2, #0x0 - svc #0x0 - pop {r7} - bx lr -} - -#endif - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) diff --git a/3rdparty/masm/assembler/ARMAssembler.h b/3rdparty/masm/assembler/ARMAssembler.h deleted file mode 100644 index ebab46d98a..0000000000 --- a/3rdparty/masm/assembler/ARMAssembler.h +++ /dev/null @@ -1,1108 +0,0 @@ -/* - * Copyright (C) 2009, 2010 University of Szeged - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ARMAssembler_h -#define ARMAssembler_h - -#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) - -#include "AssemblerBufferWithConstantPool.h" -#include "JITCompilationEffort.h" -#include -namespace JSC { - - typedef uint32_t ARMWord; - - namespace ARMRegisters { - typedef enum { - r0 = 0, - r1, - r2, - r3, S0 = r3, /* Same as thumb assembler. */ - r4, - r5, - r6, - r7, - r8, - r9, - r10, - r11, - r12, S1 = r12, - r13, sp = r13, - r14, lr = r14, - r15, pc = r15 - } RegisterID; - - typedef enum { - d0, - d1, - d2, - d3, - d4, - d5, - d6, - d7, SD0 = d7, /* Same as thumb assembler. */ - d8, - d9, - d10, - d11, - d12, - d13, - d14, - d15, - d16, - d17, - d18, - d19, - d20, - d21, - d22, - d23, - d24, - d25, - d26, - d27, - d28, - d29, - d30, - d31 - } FPRegisterID; - - } // namespace ARMRegisters - - class ARMAssembler { - public: - typedef ARMRegisters::RegisterID RegisterID; - typedef ARMRegisters::FPRegisterID FPRegisterID; - typedef AssemblerBufferWithConstantPool<2048, 4, 4, ARMAssembler> ARMBuffer; - typedef SegmentedVector Jumps; - - ARMAssembler() - : m_indexOfTailOfLastWatchpoint(1) - { - } - - // ARM conditional constants - typedef enum { - EQ = 0x00000000, // Zero - NE = 0x10000000, // Non-zero - CS = 0x20000000, - CC = 0x30000000, - MI = 0x40000000, - PL = 0x50000000, - VS = 0x60000000, - VC = 0x70000000, - HI = 0x80000000, - LS = 0x90000000, - GE = 0xa0000000, - LT = 0xb0000000, - GT = 0xc0000000, - LE = 0xd0000000, - AL = 0xe0000000 - } Condition; - - // ARM instruction constants - enum { - AND = (0x0 << 21), - EOR = (0x1 << 21), - SUB = (0x2 << 21), - RSB = (0x3 << 21), - ADD = (0x4 << 21), - ADC = (0x5 << 21), - SBC = (0x6 << 21), - RSC = (0x7 << 21), - TST = (0x8 << 21), - TEQ = (0x9 << 21), - CMP = (0xa << 21), - CMN = (0xb << 21), - ORR = (0xc << 21), - MOV = (0xd << 21), - BIC = (0xe << 21), - MVN = (0xf << 21), - MUL = 0x00000090, - MULL = 0x00c00090, - VMOV_F64 = 0x0eb00b40, - VADD_F64 = 0x0e300b00, - VDIV_F64 = 0x0e800b00, - VSUB_F64 = 0x0e300b40, - VMUL_F64 = 0x0e200b00, - VCMP_F64 = 0x0eb40b40, - VSQRT_F64 = 0x0eb10bc0, - VABS_F64 = 0x0eb00bc0, - VNEG_F64 = 0x0eb10b40, - STMDB = 0x09200000, - LDMIA = 0x08b00000, - B = 0x0a000000, - BL = 0x0b000000, - BX = 0x012fff10, - VMOV_VFP64 = 0x0c400a10, - VMOV_ARM64 = 0x0c500a10, - VMOV_VFP32 = 0x0e000a10, - VMOV_ARM32 = 0x0e100a10, - VCVT_F64_S32 = 0x0eb80bc0, - VCVT_S32_F64 = 0x0ebd0b40, - VCVT_U32_F64 = 0x0ebc0b40, - VCVT_F32_F64 = 0x0eb70bc0, - VCVT_F64_F32 = 0x0eb70ac0, - VMRS_APSR = 0x0ef1fa10, - CLZ = 0x016f0f10, - BKPT = 0xe1200070, - BLX = 0x012fff30, -#if WTF_ARM_ARCH_AT_LEAST(7) - MOVW = 0x03000000, - MOVT = 0x03400000, -#endif - NOP = 0xe1a00000, - }; - - enum { - Op2Immediate = (1 << 25), - ImmediateForHalfWordTransfer = (1 << 22), - Op2InvertedImmediate = (1 << 26), - SetConditionalCodes = (1 << 20), - Op2IsRegisterArgument = (1 << 25), - // Data transfer flags. - DataTransferUp = (1 << 23), - DataTransferWriteBack = (1 << 21), - DataTransferPostUpdate = (1 << 24), - DataTransferLoad = (1 << 20), - ByteDataTransfer = (1 << 22), - }; - - enum DataTransferTypeA { - LoadUint32 = 0x05000000 | DataTransferLoad, - LoadUint8 = 0x05400000 | DataTransferLoad, - StoreUint32 = 0x05000000, - StoreUint8 = 0x05400000, - }; - - enum DataTransferTypeB { - LoadUint16 = 0x010000b0 | DataTransferLoad, - LoadInt16 = 0x010000f0 | DataTransferLoad, - LoadInt8 = 0x010000d0 | DataTransferLoad, - StoreUint16 = 0x010000b0, - }; - - enum DataTransferTypeFloat { - LoadFloat = 0x0d000a00 | DataTransferLoad, - LoadDouble = 0x0d000b00 | DataTransferLoad, - StoreFloat = 0x0d000a00, - StoreDouble = 0x0d000b00, - }; - - // Masks of ARM instructions - enum { - BranchOffsetMask = 0x00ffffff, - ConditionalFieldMask = 0xf0000000, - DataTransferOffsetMask = 0xfff, - }; - - enum { - MinimumBranchOffsetDistance = -0x00800000, - MaximumBranchOffsetDistance = 0x007fffff, - }; - - enum { - padForAlign8 = 0x00, - padForAlign16 = 0x0000, - padForAlign32 = 0xe12fff7f // 'bkpt 0xffff' instruction. - }; - - static const ARMWord InvalidImmediate = 0xf0000000; - static const ARMWord InvalidBranchTarget = 0xffffffff; - static const int DefaultPrefetchOffset = 2; - - static const ARMWord BlxInstructionMask = 0x012fff30; - static const ARMWord LdrOrAddInstructionMask = 0x0ff00000; - static const ARMWord LdrPcImmediateInstructionMask = 0x0f7f0000; - - static const ARMWord AddImmediateInstruction = 0x02800000; - static const ARMWord BlxInstruction = 0x012fff30; - static const ARMWord LdrImmediateInstruction = 0x05900000; - static const ARMWord LdrPcImmediateInstruction = 0x051f0000; - - // Instruction formating - - void emitInstruction(ARMWord op, int rd, int rn, ARMWord op2) - { - ASSERT(((op2 & ~Op2Immediate) <= 0xfff) || (((op2 & ~ImmediateForHalfWordTransfer) <= 0xfff))); - m_buffer.putInt(op | RN(rn) | RD(rd) | op2); - } - - void emitDoublePrecisionInstruction(ARMWord op, int dd, int dn, int dm) - { - ASSERT((dd >= 0 && dd <= 31) && (dn >= 0 && dn <= 31) && (dm >= 0 && dm <= 31)); - m_buffer.putInt(op | ((dd & 0xf) << 12) | ((dd & 0x10) << (22 - 4)) - | ((dn & 0xf) << 16) | ((dn & 0x10) << (7 - 4)) - | (dm & 0xf) | ((dm & 0x10) << (5 - 4))); - } - - void emitSinglePrecisionInstruction(ARMWord op, int sd, int sn, int sm) - { - ASSERT((sd >= 0 && sd <= 31) && (sn >= 0 && sn <= 31) && (sm >= 0 && sm <= 31)); - m_buffer.putInt(op | ((sd >> 1) << 12) | ((sd & 0x1) << 22) - | ((sn >> 1) << 16) | ((sn & 0x1) << 7) - | (sm >> 1) | ((sm & 0x1) << 5)); - } - - void bitAnd(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | AND, rd, rn, op2); - } - - void bitAnds(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | AND | SetConditionalCodes, rd, rn, op2); - } - - void eor(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | EOR, rd, rn, op2); - } - - void eors(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | EOR | SetConditionalCodes, rd, rn, op2); - } - - void sub(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | SUB, rd, rn, op2); - } - - void subs(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | SUB | SetConditionalCodes, rd, rn, op2); - } - - void rsb(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | RSB, rd, rn, op2); - } - - void rsbs(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | RSB | SetConditionalCodes, rd, rn, op2); - } - - void add(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | ADD, rd, rn, op2); - } - - void adds(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | ADD | SetConditionalCodes, rd, rn, op2); - } - - void adc(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | ADC, rd, rn, op2); - } - - void adcs(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | ADC | SetConditionalCodes, rd, rn, op2); - } - - void sbc(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | SBC, rd, rn, op2); - } - - void sbcs(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | SBC | SetConditionalCodes, rd, rn, op2); - } - - void rsc(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | RSC, rd, rn, op2); - } - - void rscs(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | RSC | SetConditionalCodes, rd, rn, op2); - } - - void tst(int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | TST | SetConditionalCodes, 0, rn, op2); - } - - void teq(int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | TEQ | SetConditionalCodes, 0, rn, op2); - } - - void cmp(int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | CMP | SetConditionalCodes, 0, rn, op2); - } - - void cmn(int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | CMN | SetConditionalCodes, 0, rn, op2); - } - - void orr(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | ORR, rd, rn, op2); - } - - void orrs(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | ORR | SetConditionalCodes, rd, rn, op2); - } - - void mov(int rd, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | MOV, rd, ARMRegisters::r0, op2); - } - -#if WTF_ARM_ARCH_AT_LEAST(7) - void movw(int rd, ARMWord op2, Condition cc = AL) - { - ASSERT((op2 | 0xf0fff) == 0xf0fff); - m_buffer.putInt(toARMWord(cc) | MOVW | RD(rd) | op2); - } - - void movt(int rd, ARMWord op2, Condition cc = AL) - { - ASSERT((op2 | 0xf0fff) == 0xf0fff); - m_buffer.putInt(toARMWord(cc) | MOVT | RD(rd) | op2); - } -#endif - - void movs(int rd, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | MOV | SetConditionalCodes, rd, ARMRegisters::r0, op2); - } - - static void revertJump(void* instructionStart, RegisterID rd, ARMWord imm) - { - ARMWord* insn = reinterpret_cast(instructionStart); - ARMWord* address = getLdrImmAddress(insn); - *address = imm; - } - - void bic(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | BIC, rd, rn, op2); - } - - void bics(int rd, int rn, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | BIC | SetConditionalCodes, rd, rn, op2); - } - - void mvn(int rd, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | MVN, rd, ARMRegisters::r0, op2); - } - - void mvns(int rd, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | MVN | SetConditionalCodes, rd, ARMRegisters::r0, op2); - } - - void mul(int rd, int rn, int rm, Condition cc = AL) - { - m_buffer.putInt(toARMWord(cc) | MUL | RN(rd) | RS(rn) | RM(rm)); - } - - void muls(int rd, int rn, int rm, Condition cc = AL) - { - m_buffer.putInt(toARMWord(cc) | MUL | SetConditionalCodes | RN(rd) | RS(rn) | RM(rm)); - } - - void mull(int rdhi, int rdlo, int rn, int rm, Condition cc = AL) - { - m_buffer.putInt(toARMWord(cc) | MULL | RN(rdhi) | RD(rdlo) | RS(rn) | RM(rm)); - } - - void vmov_f64(int dd, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VMOV_F64, dd, 0, dm); - } - - void vadd_f64(int dd, int dn, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VADD_F64, dd, dn, dm); - } - - void vdiv_f64(int dd, int dn, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VDIV_F64, dd, dn, dm); - } - - void vsub_f64(int dd, int dn, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VSUB_F64, dd, dn, dm); - } - - void vmul_f64(int dd, int dn, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VMUL_F64, dd, dn, dm); - } - - void vcmp_f64(int dd, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VCMP_F64, dd, 0, dm); - } - - void vsqrt_f64(int dd, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VSQRT_F64, dd, 0, dm); - } - - void vabs_f64(int dd, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VABS_F64, dd, 0, dm); - } - - void vneg_f64(int dd, int dm, Condition cc = AL) - { - emitDoublePrecisionInstruction(toARMWord(cc) | VNEG_F64, dd, 0, dm); - } - - void ldrImmediate(int rd, ARMWord imm, Condition cc = AL) - { - m_buffer.putIntWithConstantInt(toARMWord(cc) | LoadUint32 | DataTransferUp | RN(ARMRegisters::pc) | RD(rd), imm, true); - } - - void ldrUniqueImmediate(int rd, ARMWord imm, Condition cc = AL) - { - m_buffer.putIntWithConstantInt(toARMWord(cc) | LoadUint32 | DataTransferUp | RN(ARMRegisters::pc) | RD(rd), imm); - } - - void dtrUp(DataTransferTypeA transferType, int rd, int rb, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rb, op2); - } - - void dtrUpRegister(DataTransferTypeA transferType, int rd, int rb, int rm, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType | DataTransferUp | Op2IsRegisterArgument, rd, rb, rm); - } - - void dtrDown(DataTransferTypeA transferType, int rd, int rb, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType, rd, rb, op2); - } - - void dtrDownRegister(DataTransferTypeA transferType, int rd, int rb, int rm, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType | Op2IsRegisterArgument, rd, rb, rm); - } - - void halfDtrUp(DataTransferTypeB transferType, int rd, int rb, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rb, op2); - } - - void halfDtrUpRegister(DataTransferTypeB transferType, int rd, int rn, int rm, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rn, rm); - } - - void halfDtrDown(DataTransferTypeB transferType, int rd, int rb, ARMWord op2, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType, rd, rb, op2); - } - - void halfDtrDownRegister(DataTransferTypeB transferType, int rd, int rn, int rm, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | transferType, rd, rn, rm); - } - - void doubleDtrUp(DataTransferTypeFloat type, int rd, int rb, ARMWord op2, Condition cc = AL) - { - ASSERT(op2 <= 0xff && rd <= 15); - /* Only d0-d15 and s0, s2, s4 ... s30 are supported. */ - m_buffer.putInt(toARMWord(cc) | DataTransferUp | type | (rd << 12) | RN(rb) | op2); - } - - void doubleDtrDown(DataTransferTypeFloat type, int rd, int rb, ARMWord op2, Condition cc = AL) - { - ASSERT(op2 <= 0xff && rd <= 15); - /* Only d0-d15 and s0, s2, s4 ... s30 are supported. */ - m_buffer.putInt(toARMWord(cc) | type | (rd << 12) | RN(rb) | op2); - } - - void push(int reg, Condition cc = AL) - { - ASSERT(ARMWord(reg) <= 0xf); - m_buffer.putInt(toARMWord(cc) | StoreUint32 | DataTransferWriteBack | RN(ARMRegisters::sp) | RD(reg) | 0x4); - } - - void pop(int reg, Condition cc = AL) - { - ASSERT(ARMWord(reg) <= 0xf); - m_buffer.putInt(toARMWord(cc) | (LoadUint32 ^ DataTransferPostUpdate) | DataTransferUp | RN(ARMRegisters::sp) | RD(reg) | 0x4); - } - - inline void poke(int reg, Condition cc = AL) - { - dtrDown(StoreUint32, ARMRegisters::sp, 0, reg, cc); - } - - inline void peek(int reg, Condition cc = AL) - { - dtrUp(LoadUint32, reg, ARMRegisters::sp, 0, cc); - } - - void vmov_vfp64(int sm, int rt, int rt2, Condition cc = AL) - { - ASSERT(rt != rt2); - m_buffer.putInt(toARMWord(cc) | VMOV_VFP64 | RN(rt2) | RD(rt) | (sm & 0xf) | ((sm & 0x10) << (5 - 4))); - } - - void vmov_arm64(int rt, int rt2, int sm, Condition cc = AL) - { - ASSERT(rt != rt2); - m_buffer.putInt(toARMWord(cc) | VMOV_ARM64 | RN(rt2) | RD(rt) | (sm & 0xf) | ((sm & 0x10) << (5 - 4))); - } - - void vmov_vfp32(int sn, int rt, Condition cc = AL) - { - ASSERT(rt <= 15); - emitSinglePrecisionInstruction(toARMWord(cc) | VMOV_VFP32, rt << 1, sn, 0); - } - - void vmov_arm32(int rt, int sn, Condition cc = AL) - { - ASSERT(rt <= 15); - emitSinglePrecisionInstruction(toARMWord(cc) | VMOV_ARM32, rt << 1, sn, 0); - } - - void vcvt_f64_s32(int dd, int sm, Condition cc = AL) - { - ASSERT(!(sm & 0x1)); // sm must be divisible by 2 - emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F64_S32, dd, 0, (sm >> 1)); - } - - void vcvt_s32_f64(int sd, int dm, Condition cc = AL) - { - ASSERT(!(sd & 0x1)); // sd must be divisible by 2 - emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_S32_F64, (sd >> 1), 0, dm); - } - - void vcvt_u32_f64(int sd, int dm, Condition cc = AL) - { - ASSERT(!(sd & 0x1)); // sd must be divisible by 2 - emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_U32_F64, (sd >> 1), 0, dm); - } - - void vcvt_f64_f32(int dd, int sm, Condition cc = AL) - { - ASSERT(dd <= 15 && sm <= 15); - emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F64_F32, dd, 0, sm); - } - - void vcvt_f32_f64(int dd, int sm, Condition cc = AL) - { - ASSERT(dd <= 15 && sm <= 15); - emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F32_F64, dd, 0, sm); - } - - void vmrs_apsr(Condition cc = AL) - { - m_buffer.putInt(toARMWord(cc) | VMRS_APSR); - } - - void clz(int rd, int rm, Condition cc = AL) - { - m_buffer.putInt(toARMWord(cc) | CLZ | RD(rd) | RM(rm)); - } - - void bkpt(ARMWord value) - { - m_buffer.putInt(BKPT | ((value & 0xff0) << 4) | (value & 0xf)); - } - - void nop() - { - m_buffer.putInt(NOP); - } - - void bx(int rm, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | BX, 0, 0, RM(rm)); - } - - AssemblerLabel blx(int rm, Condition cc = AL) - { - emitInstruction(toARMWord(cc) | BLX, 0, 0, RM(rm)); - return m_buffer.label(); - } - - static ARMWord lsl(int reg, ARMWord value) - { - ASSERT(reg <= ARMRegisters::pc); - ASSERT(value <= 0x1f); - return reg | (value << 7) | 0x00; - } - - static ARMWord lsr(int reg, ARMWord value) - { - ASSERT(reg <= ARMRegisters::pc); - ASSERT(value <= 0x1f); - return reg | (value << 7) | 0x20; - } - - static ARMWord asr(int reg, ARMWord value) - { - ASSERT(reg <= ARMRegisters::pc); - ASSERT(value <= 0x1f); - return reg | (value << 7) | 0x40; - } - - static ARMWord lslRegister(int reg, int shiftReg) - { - ASSERT(reg <= ARMRegisters::pc); - ASSERT(shiftReg <= ARMRegisters::pc); - return reg | (shiftReg << 8) | 0x10; - } - - static ARMWord lsrRegister(int reg, int shiftReg) - { - ASSERT(reg <= ARMRegisters::pc); - ASSERT(shiftReg <= ARMRegisters::pc); - return reg | (shiftReg << 8) | 0x30; - } - - static ARMWord asrRegister(int reg, int shiftReg) - { - ASSERT(reg <= ARMRegisters::pc); - ASSERT(shiftReg <= ARMRegisters::pc); - return reg | (shiftReg << 8) | 0x50; - } - - // General helpers - - size_t codeSize() const - { - return m_buffer.codeSize(); - } - - void ensureSpace(int insnSpace, int constSpace) - { - m_buffer.ensureSpace(insnSpace, constSpace); - } - - int sizeOfConstantPool() - { - return m_buffer.sizeOfConstantPool(); - } - - AssemblerLabel labelIgnoringWatchpoints() - { - m_buffer.ensureSpaceForAnyInstruction(); - return m_buffer.label(); - } - - AssemblerLabel labelForWatchpoint() - { - m_buffer.ensureSpaceForAnyInstruction(maxJumpReplacementSize() / sizeof(ARMWord)); - AssemblerLabel result = m_buffer.label(); - if (result.m_offset != (m_indexOfTailOfLastWatchpoint - maxJumpReplacementSize())) - result = label(); - m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); - return label(); - } - - AssemblerLabel label() - { - AssemblerLabel result = labelIgnoringWatchpoints(); - while (result.m_offset + 1 < m_indexOfTailOfLastWatchpoint) { - nop(); - // The available number of instructions are ensured by labelForWatchpoint. - result = m_buffer.label(); - } - return result; - } - - AssemblerLabel align(int alignment) - { - while (!m_buffer.isAligned(alignment)) - mov(ARMRegisters::r0, ARMRegisters::r0); - - return label(); - } - - AssemblerLabel loadBranchTarget(int rd, Condition cc = AL, int useConstantPool = 0) - { - ensureSpace(sizeof(ARMWord), sizeof(ARMWord)); - m_jumps.append(m_buffer.codeSize() | (useConstantPool & 0x1)); - ldrUniqueImmediate(rd, InvalidBranchTarget, cc); - return m_buffer.label(); - } - - AssemblerLabel jmp(Condition cc = AL, int useConstantPool = 0) - { - return loadBranchTarget(ARMRegisters::pc, cc, useConstantPool); - } - - PassRefPtr executableCopy(JSGlobalData&, void* ownerUID, JITCompilationEffort); - - unsigned debugOffset() { return m_buffer.debugOffset(); } - - // DFG assembly helpers for moving data between fp and registers. - void vmov(RegisterID rd1, RegisterID rd2, FPRegisterID rn) - { - vmov_arm64(rd1, rd2, rn); - } - - void vmov(FPRegisterID rd, RegisterID rn1, RegisterID rn2) - { - vmov_vfp64(rd, rn1, rn2); - } - - // Patching helpers - - static ARMWord* getLdrImmAddress(ARMWord* insn) - { - // Check for call - if ((*insn & LdrPcImmediateInstructionMask) != LdrPcImmediateInstruction) { - // Must be BLX - ASSERT((*insn & BlxInstructionMask) == BlxInstruction); - insn--; - } - - // Must be an ldr ..., [pc +/- imm] - ASSERT((*insn & LdrPcImmediateInstructionMask) == LdrPcImmediateInstruction); - - ARMWord addr = reinterpret_cast(insn) + DefaultPrefetchOffset * sizeof(ARMWord); - if (*insn & DataTransferUp) - return reinterpret_cast(addr + (*insn & DataTransferOffsetMask)); - return reinterpret_cast(addr - (*insn & DataTransferOffsetMask)); - } - - static ARMWord* getLdrImmAddressOnPool(ARMWord* insn, uint32_t* constPool) - { - // Must be an ldr ..., [pc +/- imm] - ASSERT((*insn & LdrPcImmediateInstructionMask) == LdrPcImmediateInstruction); - - if (*insn & 0x1) - return reinterpret_cast(constPool + ((*insn & DataTransferOffsetMask) >> 1)); - return getLdrImmAddress(insn); - } - - static void patchPointerInternal(intptr_t from, void* to) - { - ARMWord* insn = reinterpret_cast(from); - ARMWord* addr = getLdrImmAddress(insn); - *addr = reinterpret_cast(to); - } - - static ARMWord patchConstantPoolLoad(ARMWord load, ARMWord value) - { - value = (value << 1) + 1; - ASSERT(!(value & ~DataTransferOffsetMask)); - return (load & ~DataTransferOffsetMask) | value; - } - - static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr); - - // Read pointers - static void* readPointer(void* from) - { - ARMWord* instruction = reinterpret_cast(from); - ARMWord* address = getLdrImmAddress(instruction); - return *reinterpret_cast(address); - } - - // Patch pointers - - static void linkPointer(void* code, AssemblerLabel from, void* to) - { - patchPointerInternal(reinterpret_cast(code) + from.m_offset, to); - } - - static void repatchInt32(void* where, int32_t to) - { - patchPointerInternal(reinterpret_cast(where), reinterpret_cast(to)); - } - - static void repatchCompact(void* where, int32_t value) - { - ARMWord* instruction = reinterpret_cast(where); - ASSERT((*instruction & 0x0f700000) == LoadUint32); - if (value >= 0) - *instruction = (*instruction & 0xff7ff000) | DataTransferUp | value; - else - *instruction = (*instruction & 0xff7ff000) | -value; - cacheFlush(instruction, sizeof(ARMWord)); - } - - static void repatchPointer(void* from, void* to) - { - patchPointerInternal(reinterpret_cast(from), to); - } - - // Linkers - static intptr_t getAbsoluteJumpAddress(void* base, int offset = 0) - { - return reinterpret_cast(base) + offset - sizeof(ARMWord); - } - - void linkJump(AssemblerLabel from, AssemblerLabel to) - { - ARMWord* insn = reinterpret_cast(getAbsoluteJumpAddress(m_buffer.data(), from.m_offset)); - ARMWord* addr = getLdrImmAddressOnPool(insn, m_buffer.poolAddress()); - *addr = toARMWord(to.m_offset); - } - - static void linkJump(void* code, AssemblerLabel from, void* to) - { - patchPointerInternal(getAbsoluteJumpAddress(code, from.m_offset), to); - } - - static void relinkJump(void* from, void* to) - { - patchPointerInternal(getAbsoluteJumpAddress(from), to); - } - - static void linkCall(void* code, AssemblerLabel from, void* to) - { - patchPointerInternal(getAbsoluteJumpAddress(code, from.m_offset), to); - } - - static void relinkCall(void* from, void* to) - { - patchPointerInternal(getAbsoluteJumpAddress(from), to); - } - - static void* readCallTarget(void* from) - { - return reinterpret_cast(readPointer(reinterpret_cast(getAbsoluteJumpAddress(from)))); - } - - static void replaceWithJump(void* instructionStart, void* to) - { - ARMWord* instruction = reinterpret_cast(instructionStart) - 1; - intptr_t difference = reinterpret_cast(to) - (reinterpret_cast(instruction) + DefaultPrefetchOffset * sizeof(ARMWord)); - - if (!(difference & 1)) { - difference >>= 2; - if ((difference <= MaximumBranchOffsetDistance && difference >= MinimumBranchOffsetDistance)) { - // Direct branch. - instruction[0] = B | AL | (difference & BranchOffsetMask); - cacheFlush(instruction, sizeof(ARMWord)); - return; - } - } - - // Load target. - instruction[0] = LoadUint32 | AL | RN(ARMRegisters::pc) | RD(ARMRegisters::pc) | 4; - instruction[1] = reinterpret_cast(to); - cacheFlush(instruction, sizeof(ARMWord) * 2); - } - - static ptrdiff_t maxJumpReplacementSize() - { - return sizeof(ARMWord) * 2; - } - - static void replaceWithLoad(void* instructionStart) - { - ARMWord* instruction = reinterpret_cast(instructionStart); - cacheFlush(instruction, sizeof(ARMWord)); - - ASSERT((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction || (*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction); - if ((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction) { - *instruction = (*instruction & ~LdrOrAddInstructionMask) | LdrImmediateInstruction; - cacheFlush(instruction, sizeof(ARMWord)); - } - } - - static void replaceWithAddressComputation(void* instructionStart) - { - ARMWord* instruction = reinterpret_cast(instructionStart); - cacheFlush(instruction, sizeof(ARMWord)); - - ASSERT((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction || (*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction); - if ((*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction) { - *instruction = (*instruction & ~LdrOrAddInstructionMask) | AddImmediateInstruction; - cacheFlush(instruction, sizeof(ARMWord)); - } - } - - // Address operations - - static void* getRelocatedAddress(void* code, AssemblerLabel label) - { - return reinterpret_cast(reinterpret_cast(code) + label.m_offset); - } - - // Address differences - - static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) - { - return b.m_offset - a.m_offset; - } - - static unsigned getCallReturnOffset(AssemblerLabel call) - { - return call.m_offset; - } - - // Handle immediates - - static ARMWord getOp2(ARMWord imm); - - // Fast case if imm is known to be between 0 and 0xff - static ARMWord getOp2Byte(ARMWord imm) - { - ASSERT(imm <= 0xff); - return Op2Immediate | imm; - } - - static ARMWord getOp2Half(ARMWord imm) - { - ASSERT(imm <= 0xff); - return ImmediateForHalfWordTransfer | (imm & 0x0f) | ((imm & 0xf0) << 4); - } - -#if WTF_ARM_ARCH_AT_LEAST(7) - static ARMWord getImm16Op2(ARMWord imm) - { - if (imm <= 0xffff) - return (imm & 0xf000) << 4 | (imm & 0xfff); - return InvalidImmediate; - } -#endif - ARMWord getImm(ARMWord imm, int tmpReg, bool invert = false); - void moveImm(ARMWord imm, int dest); - ARMWord encodeComplexImm(ARMWord imm, int dest); - - // Memory load/store helpers - - void dataTransfer32(DataTransferTypeA, RegisterID srcDst, RegisterID base, int32_t offset); - void baseIndexTransfer32(DataTransferTypeA, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); - void dataTransfer16(DataTransferTypeB, RegisterID srcDst, RegisterID base, int32_t offset); - void baseIndexTransfer16(DataTransferTypeB, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); - void dataTransferFloat(DataTransferTypeFloat, FPRegisterID srcDst, RegisterID base, int32_t offset); - void baseIndexTransferFloat(DataTransferTypeFloat, FPRegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); - - // Constant pool hnadlers - - static ARMWord placeConstantPoolBarrier(int offset) - { - offset = (offset - sizeof(ARMWord)) >> 2; - ASSERT((offset <= MaximumBranchOffsetDistance && offset >= MinimumBranchOffsetDistance)); - return AL | B | (offset & BranchOffsetMask); - } - -#if OS(LINUX) && COMPILER(RVCT) - static __asm void cacheFlush(void* code, size_t); -#else - static void cacheFlush(void* code, size_t size) - { -#if OS(LINUX) && COMPILER(GCC) - uintptr_t currentPage = reinterpret_cast(code) & ~(pageSize() - 1); - uintptr_t lastPage = (reinterpret_cast(code) + size) & ~(pageSize() - 1); - do { - asm volatile( - "push {r7}\n" - "mov r0, %0\n" - "mov r1, %1\n" - "mov r7, #0xf0000\n" - "add r7, r7, #0x2\n" - "mov r2, #0x0\n" - "svc 0x0\n" - "pop {r7}\n" - : - : "r" (currentPage), "r" (currentPage + pageSize()) - : "r0", "r1", "r2"); - currentPage += pageSize(); - } while (lastPage >= currentPage); -#elif OS(WINCE) - CacheRangeFlush(code, size, CACHE_SYNC_ALL); -#elif OS(QNX) && ENABLE(ASSEMBLER_WX_EXCLUSIVE) - UNUSED_PARAM(code); - UNUSED_PARAM(size); -#elif OS(QNX) - msync(code, size, MS_INVALIDATE_ICACHE); -#else -#error "The cacheFlush support is missing on this platform." -#endif - } -#endif - - private: - static ARMWord RM(int reg) - { - ASSERT(reg <= ARMRegisters::pc); - return reg; - } - - static ARMWord RS(int reg) - { - ASSERT(reg <= ARMRegisters::pc); - return reg << 8; - } - - static ARMWord RD(int reg) - { - ASSERT(reg <= ARMRegisters::pc); - return reg << 12; - } - - static ARMWord RN(int reg) - { - ASSERT(reg <= ARMRegisters::pc); - return reg << 16; - } - - static ARMWord getConditionalField(ARMWord i) - { - return i & ConditionalFieldMask; - } - - static ARMWord toARMWord(Condition cc) - { - return static_cast(cc); - } - - static ARMWord toARMWord(uint32_t u) - { - return static_cast(u); - } - - int genInt(int reg, ARMWord imm, bool positive); - - ARMBuffer m_buffer; - Jumps m_jumps; - uint32_t m_indexOfTailOfLastWatchpoint; - }; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) - -#endif // ARMAssembler_h diff --git a/3rdparty/masm/assembler/ARMv7Assembler.cpp b/3rdparty/masm/assembler/ARMv7Assembler.cpp deleted file mode 100644 index faca66421b..0000000000 --- a/3rdparty/masm/assembler/ARMv7Assembler.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" - -#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) - -#include "ARMv7Assembler.h" - -namespace JSC { - -} - -#endif diff --git a/3rdparty/masm/assembler/ARMv7Assembler.h b/3rdparty/masm/assembler/ARMv7Assembler.h deleted file mode 100644 index b93ec6e63f..0000000000 --- a/3rdparty/masm/assembler/ARMv7Assembler.h +++ /dev/null @@ -1,2706 +0,0 @@ -/* - * Copyright (C) 2009, 2010, 2012 Apple Inc. All rights reserved. - * Copyright (C) 2010 University of Szeged - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ARMAssembler_h -#define ARMAssembler_h - -#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) - -#include "AssemblerBuffer.h" -#include -#include -#include - -namespace JSC { - -namespace ARMRegisters { - typedef enum { - r0, - r1, - r2, - r3, - r4, - r5, - r6, - r7, wr = r7, // thumb work register - r8, - r9, sb = r9, // static base - r10, sl = r10, // stack limit - r11, fp = r11, // frame pointer - r12, ip = r12, - r13, sp = r13, - r14, lr = r14, - r15, pc = r15, - } RegisterID; - - typedef enum { - s0, - s1, - s2, - s3, - s4, - s5, - s6, - s7, - s8, - s9, - s10, - s11, - s12, - s13, - s14, - s15, - s16, - s17, - s18, - s19, - s20, - s21, - s22, - s23, - s24, - s25, - s26, - s27, - s28, - s29, - s30, - s31, - } FPSingleRegisterID; - - typedef enum { - d0, - d1, - d2, - d3, - d4, - d5, - d6, - d7, - d8, - d9, - d10, - d11, - d12, - d13, - d14, - d15, - d16, - d17, - d18, - d19, - d20, - d21, - d22, - d23, - d24, - d25, - d26, - d27, - d28, - d29, - d30, - d31, - } FPDoubleRegisterID; - - typedef enum { - q0, - q1, - q2, - q3, - q4, - q5, - q6, - q7, - q8, - q9, - q10, - q11, - q12, - q13, - q14, - q15, - q16, - q17, - q18, - q19, - q20, - q21, - q22, - q23, - q24, - q25, - q26, - q27, - q28, - q29, - q30, - q31, - } FPQuadRegisterID; - - inline FPSingleRegisterID asSingle(FPDoubleRegisterID reg) - { - ASSERT(reg < d16); - return (FPSingleRegisterID)(reg << 1); - } - - inline FPDoubleRegisterID asDouble(FPSingleRegisterID reg) - { - ASSERT(!(reg & 1)); - return (FPDoubleRegisterID)(reg >> 1); - } -} - -class ARMv7Assembler; -class ARMThumbImmediate { - friend class ARMv7Assembler; - - typedef uint8_t ThumbImmediateType; - static const ThumbImmediateType TypeInvalid = 0; - static const ThumbImmediateType TypeEncoded = 1; - static const ThumbImmediateType TypeUInt16 = 2; - - typedef union { - int16_t asInt; - struct { - unsigned imm8 : 8; - unsigned imm3 : 3; - unsigned i : 1; - unsigned imm4 : 4; - }; - // If this is an encoded immediate, then it may describe a shift, or a pattern. - struct { - unsigned shiftValue7 : 7; - unsigned shiftAmount : 5; - }; - struct { - unsigned immediate : 8; - unsigned pattern : 4; - }; - } ThumbImmediateValue; - - // byte0 contains least significant bit; not using an array to make client code endian agnostic. - typedef union { - int32_t asInt; - struct { - uint8_t byte0; - uint8_t byte1; - uint8_t byte2; - uint8_t byte3; - }; - } PatternBytes; - - ALWAYS_INLINE static void countLeadingZerosPartial(uint32_t& value, int32_t& zeros, const int N) - { - if (value & ~((1 << N) - 1)) /* check for any of the top N bits (of 2N bits) are set */ - value >>= N; /* if any were set, lose the bottom N */ - else /* if none of the top N bits are set, */ - zeros += N; /* then we have identified N leading zeros */ - } - - static int32_t countLeadingZeros(uint32_t value) - { - if (!value) - return 32; - - int32_t zeros = 0; - countLeadingZerosPartial(value, zeros, 16); - countLeadingZerosPartial(value, zeros, 8); - countLeadingZerosPartial(value, zeros, 4); - countLeadingZerosPartial(value, zeros, 2); - countLeadingZerosPartial(value, zeros, 1); - return zeros; - } - - ARMThumbImmediate() - : m_type(TypeInvalid) - { - m_value.asInt = 0; - } - - ARMThumbImmediate(ThumbImmediateType type, ThumbImmediateValue value) - : m_type(type) - , m_value(value) - { - } - - ARMThumbImmediate(ThumbImmediateType type, uint16_t value) - : m_type(TypeUInt16) - { - // Make sure this constructor is only reached with type TypeUInt16; - // this extra parameter makes the code a little clearer by making it - // explicit at call sites which type is being constructed - ASSERT_UNUSED(type, type == TypeUInt16); - - m_value.asInt = value; - } - -public: - static ARMThumbImmediate makeEncodedImm(uint32_t value) - { - ThumbImmediateValue encoding; - encoding.asInt = 0; - - // okay, these are easy. - if (value < 256) { - encoding.immediate = value; - encoding.pattern = 0; - return ARMThumbImmediate(TypeEncoded, encoding); - } - - int32_t leadingZeros = countLeadingZeros(value); - // if there were 24 or more leading zeros, then we'd have hit the (value < 256) case. - ASSERT(leadingZeros < 24); - - // Given a number with bit fields Z:B:C, where count(Z)+count(B)+count(C) == 32, - // Z are the bits known zero, B is the 8-bit immediate, C are the bits to check for - // zero. count(B) == 8, so the count of bits to be checked is 24 - count(Z). - int32_t rightShiftAmount = 24 - leadingZeros; - if (value == ((value >> rightShiftAmount) << rightShiftAmount)) { - // Shift the value down to the low byte position. The assign to - // shiftValue7 drops the implicit top bit. - encoding.shiftValue7 = value >> rightShiftAmount; - // The endoded shift amount is the magnitude of a right rotate. - encoding.shiftAmount = 8 + leadingZeros; - return ARMThumbImmediate(TypeEncoded, encoding); - } - - PatternBytes bytes; - bytes.asInt = value; - - if ((bytes.byte0 == bytes.byte1) && (bytes.byte0 == bytes.byte2) && (bytes.byte0 == bytes.byte3)) { - encoding.immediate = bytes.byte0; - encoding.pattern = 3; - return ARMThumbImmediate(TypeEncoded, encoding); - } - - if ((bytes.byte0 == bytes.byte2) && !(bytes.byte1 | bytes.byte3)) { - encoding.immediate = bytes.byte0; - encoding.pattern = 1; - return ARMThumbImmediate(TypeEncoded, encoding); - } - - if ((bytes.byte1 == bytes.byte3) && !(bytes.byte0 | bytes.byte2)) { - encoding.immediate = bytes.byte1; - encoding.pattern = 2; - return ARMThumbImmediate(TypeEncoded, encoding); - } - - return ARMThumbImmediate(); - } - - static ARMThumbImmediate makeUInt12(int32_t value) - { - return (!(value & 0xfffff000)) - ? ARMThumbImmediate(TypeUInt16, (uint16_t)value) - : ARMThumbImmediate(); - } - - static ARMThumbImmediate makeUInt12OrEncodedImm(int32_t value) - { - // If this is not a 12-bit unsigned it, try making an encoded immediate. - return (!(value & 0xfffff000)) - ? ARMThumbImmediate(TypeUInt16, (uint16_t)value) - : makeEncodedImm(value); - } - - // The 'make' methods, above, return a !isValid() value if the argument - // cannot be represented as the requested type. This methods is called - // 'get' since the argument can always be represented. - static ARMThumbImmediate makeUInt16(uint16_t value) - { - return ARMThumbImmediate(TypeUInt16, value); - } - - bool isValid() - { - return m_type != TypeInvalid; - } - - uint16_t asUInt16() const { return m_value.asInt; } - - // These methods rely on the format of encoded byte values. - bool isUInt3() { return !(m_value.asInt & 0xfff8); } - bool isUInt4() { return !(m_value.asInt & 0xfff0); } - bool isUInt5() { return !(m_value.asInt & 0xffe0); } - bool isUInt6() { return !(m_value.asInt & 0xffc0); } - bool isUInt7() { return !(m_value.asInt & 0xff80); } - bool isUInt8() { return !(m_value.asInt & 0xff00); } - bool isUInt9() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xfe00); } - bool isUInt10() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xfc00); } - bool isUInt12() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xf000); } - bool isUInt16() { return m_type == TypeUInt16; } - uint8_t getUInt3() { ASSERT(isUInt3()); return m_value.asInt; } - uint8_t getUInt4() { ASSERT(isUInt4()); return m_value.asInt; } - uint8_t getUInt5() { ASSERT(isUInt5()); return m_value.asInt; } - uint8_t getUInt6() { ASSERT(isUInt6()); return m_value.asInt; } - uint8_t getUInt7() { ASSERT(isUInt7()); return m_value.asInt; } - uint8_t getUInt8() { ASSERT(isUInt8()); return m_value.asInt; } - uint16_t getUInt9() { ASSERT(isUInt9()); return m_value.asInt; } - uint16_t getUInt10() { ASSERT(isUInt10()); return m_value.asInt; } - uint16_t getUInt12() { ASSERT(isUInt12()); return m_value.asInt; } - uint16_t getUInt16() { ASSERT(isUInt16()); return m_value.asInt; } - - bool isEncodedImm() { return m_type == TypeEncoded; } - -private: - ThumbImmediateType m_type; - ThumbImmediateValue m_value; -}; - -typedef enum { - SRType_LSL, - SRType_LSR, - SRType_ASR, - SRType_ROR, - - SRType_RRX = SRType_ROR -} ARMShiftType; - -class ShiftTypeAndAmount { - friend class ARMv7Assembler; - -public: - ShiftTypeAndAmount() - { - m_u.type = (ARMShiftType)0; - m_u.amount = 0; - } - - ShiftTypeAndAmount(ARMShiftType type, unsigned amount) - { - m_u.type = type; - m_u.amount = amount & 31; - } - - unsigned lo4() { return m_u.lo4; } - unsigned hi4() { return m_u.hi4; } - -private: - union { - struct { - unsigned lo4 : 4; - unsigned hi4 : 4; - }; - struct { - unsigned type : 2; - unsigned amount : 6; - }; - } m_u; -}; - -class ARMv7Assembler { -public: - typedef ARMRegisters::RegisterID RegisterID; - typedef ARMRegisters::FPSingleRegisterID FPSingleRegisterID; - typedef ARMRegisters::FPDoubleRegisterID FPDoubleRegisterID; - typedef ARMRegisters::FPQuadRegisterID FPQuadRegisterID; - - // (HS, LO, HI, LS) -> (AE, B, A, BE) - // (VS, VC) -> (O, NO) - typedef enum { - ConditionEQ, - ConditionNE, - ConditionHS, ConditionCS = ConditionHS, - ConditionLO, ConditionCC = ConditionLO, - ConditionMI, - ConditionPL, - ConditionVS, - ConditionVC, - ConditionHI, - ConditionLS, - ConditionGE, - ConditionLT, - ConditionGT, - ConditionLE, - ConditionAL, - ConditionInvalid - } Condition; - -#define JUMP_ENUM_WITH_SIZE(index, value) (((value) << 3) | (index)) -#define JUMP_ENUM_SIZE(jump) ((jump) >> 3) - enum JumpType { JumpFixed = JUMP_ENUM_WITH_SIZE(0, 0), - JumpNoCondition = JUMP_ENUM_WITH_SIZE(1, 5 * sizeof(uint16_t)), - JumpCondition = JUMP_ENUM_WITH_SIZE(2, 6 * sizeof(uint16_t)), - JumpNoConditionFixedSize = JUMP_ENUM_WITH_SIZE(3, 5 * sizeof(uint16_t)), - JumpConditionFixedSize = JUMP_ENUM_WITH_SIZE(4, 6 * sizeof(uint16_t)) - }; - enum JumpLinkType { - LinkInvalid = JUMP_ENUM_WITH_SIZE(0, 0), - LinkJumpT1 = JUMP_ENUM_WITH_SIZE(1, sizeof(uint16_t)), - LinkJumpT2 = JUMP_ENUM_WITH_SIZE(2, sizeof(uint16_t)), - LinkJumpT3 = JUMP_ENUM_WITH_SIZE(3, 2 * sizeof(uint16_t)), - LinkJumpT4 = JUMP_ENUM_WITH_SIZE(4, 2 * sizeof(uint16_t)), - LinkConditionalJumpT4 = JUMP_ENUM_WITH_SIZE(5, 3 * sizeof(uint16_t)), - LinkBX = JUMP_ENUM_WITH_SIZE(6, 5 * sizeof(uint16_t)), - LinkConditionalBX = JUMP_ENUM_WITH_SIZE(7, 6 * sizeof(uint16_t)) - }; - - class LinkRecord { - public: - LinkRecord(intptr_t from, intptr_t to, JumpType type, Condition condition) - { - data.realTypes.m_from = from; - data.realTypes.m_to = to; - data.realTypes.m_type = type; - data.realTypes.m_linkType = LinkInvalid; - data.realTypes.m_condition = condition; - } - void operator=(const LinkRecord& other) - { - data.copyTypes.content[0] = other.data.copyTypes.content[0]; - data.copyTypes.content[1] = other.data.copyTypes.content[1]; - data.copyTypes.content[2] = other.data.copyTypes.content[2]; - } - intptr_t from() const { return data.realTypes.m_from; } - void setFrom(intptr_t from) { data.realTypes.m_from = from; } - intptr_t to() const { return data.realTypes.m_to; } - JumpType type() const { return data.realTypes.m_type; } - JumpLinkType linkType() const { return data.realTypes.m_linkType; } - void setLinkType(JumpLinkType linkType) { ASSERT(data.realTypes.m_linkType == LinkInvalid); data.realTypes.m_linkType = linkType; } - Condition condition() const { return data.realTypes.m_condition; } - private: - union { - struct RealTypes { - intptr_t m_from : 31; - intptr_t m_to : 31; - JumpType m_type : 8; - JumpLinkType m_linkType : 8; - Condition m_condition : 16; - } realTypes; - struct CopyTypes { - uint32_t content[3]; - } copyTypes; - COMPILE_ASSERT(sizeof(RealTypes) == sizeof(CopyTypes), LinkRecordCopyStructSizeEqualsRealStruct); - } data; - }; - - ARMv7Assembler() - : m_indexOfLastWatchpoint(INT_MIN) - , m_indexOfTailOfLastWatchpoint(INT_MIN) - { - } - -private: - - // ARMv7, Appx-A.6.3 - static bool BadReg(RegisterID reg) - { - return (reg == ARMRegisters::sp) || (reg == ARMRegisters::pc); - } - - uint32_t singleRegisterMask(FPSingleRegisterID rdNum, int highBitsShift, int lowBitShift) - { - uint32_t rdMask = (rdNum >> 1) << highBitsShift; - if (rdNum & 1) - rdMask |= 1 << lowBitShift; - return rdMask; - } - - uint32_t doubleRegisterMask(FPDoubleRegisterID rdNum, int highBitShift, int lowBitsShift) - { - uint32_t rdMask = (rdNum & 0xf) << lowBitsShift; - if (rdNum & 16) - rdMask |= 1 << highBitShift; - return rdMask; - } - - typedef enum { - OP_ADD_reg_T1 = 0x1800, - OP_SUB_reg_T1 = 0x1A00, - OP_ADD_imm_T1 = 0x1C00, - OP_SUB_imm_T1 = 0x1E00, - OP_MOV_imm_T1 = 0x2000, - OP_CMP_imm_T1 = 0x2800, - OP_ADD_imm_T2 = 0x3000, - OP_SUB_imm_T2 = 0x3800, - OP_AND_reg_T1 = 0x4000, - OP_EOR_reg_T1 = 0x4040, - OP_TST_reg_T1 = 0x4200, - OP_RSB_imm_T1 = 0x4240, - OP_CMP_reg_T1 = 0x4280, - OP_ORR_reg_T1 = 0x4300, - OP_MVN_reg_T1 = 0x43C0, - OP_ADD_reg_T2 = 0x4400, - OP_MOV_reg_T1 = 0x4600, - OP_BLX = 0x4700, - OP_BX = 0x4700, - OP_STR_reg_T1 = 0x5000, - OP_STRH_reg_T1 = 0x5200, - OP_STRB_reg_T1 = 0x5400, - OP_LDRSB_reg_T1 = 0x5600, - OP_LDR_reg_T1 = 0x5800, - OP_LDRH_reg_T1 = 0x5A00, - OP_LDRB_reg_T1 = 0x5C00, - OP_LDRSH_reg_T1 = 0x5E00, - OP_STR_imm_T1 = 0x6000, - OP_LDR_imm_T1 = 0x6800, - OP_STRB_imm_T1 = 0x7000, - OP_LDRB_imm_T1 = 0x7800, - OP_STRH_imm_T1 = 0x8000, - OP_LDRH_imm_T1 = 0x8800, - OP_STR_imm_T2 = 0x9000, - OP_LDR_imm_T2 = 0x9800, - OP_ADD_SP_imm_T1 = 0xA800, - OP_ADD_SP_imm_T2 = 0xB000, - OP_SUB_SP_imm_T1 = 0xB080, - OP_BKPT = 0xBE00, - OP_IT = 0xBF00, - OP_NOP_T1 = 0xBF00, - } OpcodeID; - - typedef enum { - OP_B_T1 = 0xD000, - OP_B_T2 = 0xE000, - OP_AND_reg_T2 = 0xEA00, - OP_TST_reg_T2 = 0xEA10, - OP_ORR_reg_T2 = 0xEA40, - OP_ORR_S_reg_T2 = 0xEA50, - OP_ASR_imm_T1 = 0xEA4F, - OP_LSL_imm_T1 = 0xEA4F, - OP_LSR_imm_T1 = 0xEA4F, - OP_ROR_imm_T1 = 0xEA4F, - OP_MVN_reg_T2 = 0xEA6F, - OP_EOR_reg_T2 = 0xEA80, - OP_ADD_reg_T3 = 0xEB00, - OP_ADD_S_reg_T3 = 0xEB10, - OP_SUB_reg_T2 = 0xEBA0, - OP_SUB_S_reg_T2 = 0xEBB0, - OP_CMP_reg_T2 = 0xEBB0, - OP_VMOV_CtoD = 0xEC00, - OP_VMOV_DtoC = 0xEC10, - OP_FSTS = 0xED00, - OP_VSTR = 0xED00, - OP_FLDS = 0xED10, - OP_VLDR = 0xED10, - OP_VMOV_CtoS = 0xEE00, - OP_VMOV_StoC = 0xEE10, - OP_VMUL_T2 = 0xEE20, - OP_VADD_T2 = 0xEE30, - OP_VSUB_T2 = 0xEE30, - OP_VDIV = 0xEE80, - OP_VABS_T2 = 0xEEB0, - OP_VCMP = 0xEEB0, - OP_VCVT_FPIVFP = 0xEEB0, - OP_VMOV_T2 = 0xEEB0, - OP_VMOV_IMM_T2 = 0xEEB0, - OP_VMRS = 0xEEB0, - OP_VNEG_T2 = 0xEEB0, - OP_VSQRT_T1 = 0xEEB0, - OP_VCVTSD_T1 = 0xEEB0, - OP_VCVTDS_T1 = 0xEEB0, - OP_B_T3a = 0xF000, - OP_B_T4a = 0xF000, - OP_AND_imm_T1 = 0xF000, - OP_TST_imm = 0xF010, - OP_ORR_imm_T1 = 0xF040, - OP_MOV_imm_T2 = 0xF040, - OP_MVN_imm = 0xF060, - OP_EOR_imm_T1 = 0xF080, - OP_ADD_imm_T3 = 0xF100, - OP_ADD_S_imm_T3 = 0xF110, - OP_CMN_imm = 0xF110, - OP_ADC_imm = 0xF140, - OP_SUB_imm_T3 = 0xF1A0, - OP_SUB_S_imm_T3 = 0xF1B0, - OP_CMP_imm_T2 = 0xF1B0, - OP_RSB_imm_T2 = 0xF1C0, - OP_RSB_S_imm_T2 = 0xF1D0, - OP_ADD_imm_T4 = 0xF200, - OP_MOV_imm_T3 = 0xF240, - OP_SUB_imm_T4 = 0xF2A0, - OP_MOVT = 0xF2C0, - OP_UBFX_T1 = 0xF3C0, - OP_NOP_T2a = 0xF3AF, - OP_STRB_imm_T3 = 0xF800, - OP_STRB_reg_T2 = 0xF800, - OP_LDRB_imm_T3 = 0xF810, - OP_LDRB_reg_T2 = 0xF810, - OP_STRH_imm_T3 = 0xF820, - OP_STRH_reg_T2 = 0xF820, - OP_LDRH_reg_T2 = 0xF830, - OP_LDRH_imm_T3 = 0xF830, - OP_STR_imm_T4 = 0xF840, - OP_STR_reg_T2 = 0xF840, - OP_LDR_imm_T4 = 0xF850, - OP_LDR_reg_T2 = 0xF850, - OP_STRB_imm_T2 = 0xF880, - OP_LDRB_imm_T2 = 0xF890, - OP_STRH_imm_T2 = 0xF8A0, - OP_LDRH_imm_T2 = 0xF8B0, - OP_STR_imm_T3 = 0xF8C0, - OP_LDR_imm_T3 = 0xF8D0, - OP_LDRSB_reg_T2 = 0xF910, - OP_LDRSH_reg_T2 = 0xF930, - OP_LSL_reg_T2 = 0xFA00, - OP_LSR_reg_T2 = 0xFA20, - OP_ASR_reg_T2 = 0xFA40, - OP_ROR_reg_T2 = 0xFA60, - OP_CLZ = 0xFAB0, - OP_SMULL_T1 = 0xFB80, - } OpcodeID1; - - typedef enum { - OP_VADD_T2b = 0x0A00, - OP_VDIVb = 0x0A00, - OP_FLDSb = 0x0A00, - OP_VLDRb = 0x0A00, - OP_VMOV_IMM_T2b = 0x0A00, - OP_VMOV_T2b = 0x0A40, - OP_VMUL_T2b = 0x0A00, - OP_FSTSb = 0x0A00, - OP_VSTRb = 0x0A00, - OP_VMOV_StoCb = 0x0A10, - OP_VMOV_CtoSb = 0x0A10, - OP_VMOV_DtoCb = 0x0A10, - OP_VMOV_CtoDb = 0x0A10, - OP_VMRSb = 0x0A10, - OP_VABS_T2b = 0x0A40, - OP_VCMPb = 0x0A40, - OP_VCVT_FPIVFPb = 0x0A40, - OP_VNEG_T2b = 0x0A40, - OP_VSUB_T2b = 0x0A40, - OP_VSQRT_T1b = 0x0A40, - OP_VCVTSD_T1b = 0x0A40, - OP_VCVTDS_T1b = 0x0A40, - OP_NOP_T2b = 0x8000, - OP_B_T3b = 0x8000, - OP_B_T4b = 0x9000, - } OpcodeID2; - - struct FourFours { - FourFours(unsigned f3, unsigned f2, unsigned f1, unsigned f0) - { - m_u.f0 = f0; - m_u.f1 = f1; - m_u.f2 = f2; - m_u.f3 = f3; - } - - union { - unsigned value; - struct { - unsigned f0 : 4; - unsigned f1 : 4; - unsigned f2 : 4; - unsigned f3 : 4; - }; - } m_u; - }; - - class ARMInstructionFormatter; - - // false means else! - bool ifThenElseConditionBit(Condition condition, bool isIf) - { - return isIf ? (condition & 1) : !(condition & 1); - } - uint8_t ifThenElse(Condition condition, bool inst2if, bool inst3if, bool inst4if) - { - int mask = (ifThenElseConditionBit(condition, inst2if) << 3) - | (ifThenElseConditionBit(condition, inst3if) << 2) - | (ifThenElseConditionBit(condition, inst4if) << 1) - | 1; - ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); - return (condition << 4) | mask; - } - uint8_t ifThenElse(Condition condition, bool inst2if, bool inst3if) - { - int mask = (ifThenElseConditionBit(condition, inst2if) << 3) - | (ifThenElseConditionBit(condition, inst3if) << 2) - | 2; - ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); - return (condition << 4) | mask; - } - uint8_t ifThenElse(Condition condition, bool inst2if) - { - int mask = (ifThenElseConditionBit(condition, inst2if) << 3) - | 4; - ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); - return (condition << 4) | mask; - } - - uint8_t ifThenElse(Condition condition) - { - int mask = 8; - return (condition << 4) | mask; - } - -public: - - void adc(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - // Rd can only be SP if Rn is also SP. - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isEncodedImm()); - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADC_imm, rn, rd, imm); - } - - void add(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - // Rd can only be SP if Rn is also SP. - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isValid()); - - if (rn == ARMRegisters::sp) { - ASSERT(!(imm.getUInt16() & 3)); - if (!(rd & 8) && imm.isUInt10()) { - m_formatter.oneWordOp5Reg3Imm8(OP_ADD_SP_imm_T1, rd, static_cast(imm.getUInt10() >> 2)); - return; - } else if ((rd == ARMRegisters::sp) && imm.isUInt9()) { - m_formatter.oneWordOp9Imm7(OP_ADD_SP_imm_T2, static_cast(imm.getUInt9() >> 2)); - return; - } - } else if (!((rd | rn) & 8)) { - if (imm.isUInt3()) { - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); - return; - } else if ((rd == rn) && imm.isUInt8()) { - m_formatter.oneWordOp5Reg3Imm8(OP_ADD_imm_T2, rd, imm.getUInt8()); - return; - } - } - - if (imm.isEncodedImm()) - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_imm_T3, rn, rd, imm); - else { - ASSERT(imm.isUInt12()); - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_imm_T4, rn, rd, imm); - } - } - - ALWAYS_INLINE void add(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_ADD_reg_T3, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - // NOTE: In an IT block, add doesn't modify the flags register. - ALWAYS_INLINE void add(RegisterID rd, RegisterID rn, RegisterID rm) - { - if (rd == rn) - m_formatter.oneWordOp8RegReg143(OP_ADD_reg_T2, rm, rd); - else if (rd == rm) - m_formatter.oneWordOp8RegReg143(OP_ADD_reg_T2, rn, rd); - else if (!((rd | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_reg_T1, rm, rn, rd); - else - add(rd, rn, rm, ShiftTypeAndAmount()); - } - - // Not allowed in an IT (if then) block. - ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - // Rd can only be SP if Rn is also SP. - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isEncodedImm()); - - if (!((rd | rn) & 8)) { - if (imm.isUInt3()) { - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); - return; - } else if ((rd == rn) && imm.isUInt8()) { - m_formatter.oneWordOp5Reg3Imm8(OP_ADD_imm_T2, rd, imm.getUInt8()); - return; - } - } - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_S_imm_T3, rn, rd, imm); - } - - // Not allowed in an IT (if then) block? - ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_ADD_S_reg_T3, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - // Not allowed in an IT (if then) block. - ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, RegisterID rm) - { - if (!((rd | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_reg_T1, rm, rn, rd); - else - add_S(rd, rn, rm, ShiftTypeAndAmount()); - } - - ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(imm.isEncodedImm()); - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_AND_imm_T1, rn, rd, imm); - } - - ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_AND_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, RegisterID rm) - { - if ((rd == rn) && !((rd | rm) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_AND_reg_T1, rm, rd); - else if ((rd == rm) && !((rd | rn) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_AND_reg_T1, rn, rd); - else - ARM_and(rd, rn, rm, ShiftTypeAndAmount()); - } - - ALWAYS_INLINE void asr(RegisterID rd, RegisterID rm, int32_t shiftAmount) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rm)); - ShiftTypeAndAmount shift(SRType_ASR, shiftAmount); - m_formatter.twoWordOp16FourFours(OP_ASR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - ALWAYS_INLINE void asr(RegisterID rd, RegisterID rn, RegisterID rm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_ASR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); - } - - // Only allowed in IT (if then) block if last instruction. - ALWAYS_INLINE AssemblerLabel b() - { - m_formatter.twoWordOp16Op16(OP_B_T4a, OP_B_T4b); - return m_formatter.label(); - } - - // Only allowed in IT (if then) block if last instruction. - ALWAYS_INLINE AssemblerLabel blx(RegisterID rm) - { - ASSERT(rm != ARMRegisters::pc); - m_formatter.oneWordOp8RegReg143(OP_BLX, rm, (RegisterID)8); - return m_formatter.label(); - } - - // Only allowed in IT (if then) block if last instruction. - ALWAYS_INLINE AssemblerLabel bx(RegisterID rm) - { - m_formatter.oneWordOp8RegReg143(OP_BX, rm, (RegisterID)0); - return m_formatter.label(); - } - - void bkpt(uint8_t imm = 0) - { - m_formatter.oneWordOp8Imm8(OP_BKPT, imm); - } - - ALWAYS_INLINE void clz(RegisterID rd, RegisterID rm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_CLZ, rm, FourFours(0xf, rd, 8, rm)); - } - - ALWAYS_INLINE void cmn(RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isEncodedImm()); - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_CMN_imm, rn, (RegisterID)0xf, imm); - } - - ALWAYS_INLINE void cmp(RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isEncodedImm()); - - if (!(rn & 8) && imm.isUInt8()) - m_formatter.oneWordOp5Reg3Imm8(OP_CMP_imm_T1, rn, imm.getUInt8()); - else - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_CMP_imm_T2, rn, (RegisterID)0xf, imm); - } - - ALWAYS_INLINE void cmp(RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_CMP_reg_T2, rn, FourFours(shift.hi4(), 0xf, shift.lo4(), rm)); - } - - ALWAYS_INLINE void cmp(RegisterID rn, RegisterID rm) - { - if ((rn | rm) & 8) - cmp(rn, rm, ShiftTypeAndAmount()); - else - m_formatter.oneWordOp10Reg3Reg3(OP_CMP_reg_T1, rm, rn); - } - - // xor is not spelled with an 'e'. :-( - ALWAYS_INLINE void eor(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(imm.isEncodedImm()); - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_EOR_imm_T1, rn, rd, imm); - } - - // xor is not spelled with an 'e'. :-( - ALWAYS_INLINE void eor(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_EOR_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - // xor is not spelled with an 'e'. :-( - void eor(RegisterID rd, RegisterID rn, RegisterID rm) - { - if ((rd == rn) && !((rd | rm) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_EOR_reg_T1, rm, rd); - else if ((rd == rm) && !((rd | rn) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_EOR_reg_T1, rn, rd); - else - eor(rd, rn, rm, ShiftTypeAndAmount()); - } - - ALWAYS_INLINE void it(Condition cond) - { - m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond)); - } - - ALWAYS_INLINE void it(Condition cond, bool inst2if) - { - m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if)); - } - - ALWAYS_INLINE void it(Condition cond, bool inst2if, bool inst3if) - { - m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if, inst3if)); - } - - ALWAYS_INLINE void it(Condition cond, bool inst2if, bool inst3if, bool inst4if) - { - m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if, inst3if, inst4if)); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rn != ARMRegisters::pc); // LDR (literal) - ASSERT(imm.isUInt12()); - - if (!((rt | rn) & 8) && imm.isUInt7()) - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDR_imm_T1, imm.getUInt7() >> 2, rn, rt); - else if ((rn == ARMRegisters::sp) && !(rt & 8) && imm.isUInt10()) - m_formatter.oneWordOp5Reg3Imm8(OP_LDR_imm_T2, rt, static_cast(imm.getUInt10() >> 2)); - else - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T3, rn, rt, imm.getUInt12()); - } - - ALWAYS_INLINE void ldrWide8BitImmediate(RegisterID rt, RegisterID rn, uint8_t immediate) - { - ASSERT(rn != ARMRegisters::pc); - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T3, rn, rt, immediate); - } - - ALWAYS_INLINE void ldrCompact(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rn != ARMRegisters::pc); // LDR (literal) - ASSERT(imm.isUInt7()); - ASSERT(!((rt | rn) & 8)); - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDR_imm_T1, imm.getUInt7() >> 2, rn, rt); - } - - // If index is set, this is a regular offset or a pre-indexed load; - // if index is not set then is is a post-index load. - // - // If wback is set rn is updated - this is a pre or post index load, - // if wback is not set this is a regular offset memory access. - // - // (-255 <= offset <= 255) - // _reg = REG[rn] - // _tmp = _reg + offset - // MEM[index ? _tmp : _reg] = REG[rt] - // if (wback) REG[rn] = _tmp - ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(index || wback); - ASSERT(!wback | (rt != rn)); - - bool add = true; - if (offset < 0) { - add = false; - offset = -offset; - } - ASSERT((offset & ~0xff) == 0); - - offset |= (wback << 8); - offset |= (add << 9); - offset |= (index << 10); - offset |= (1 << 11); - - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T4, rn, rt, offset); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(rn != ARMRegisters::pc); // LDR (literal) - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDR_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_LDR_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rn != ARMRegisters::pc); // LDR (literal) - ASSERT(imm.isUInt12()); - - if (!((rt | rn) & 8) && imm.isUInt6()) - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDRH_imm_T1, imm.getUInt6() >> 2, rn, rt); - else - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRH_imm_T2, rn, rt, imm.getUInt12()); - } - - // If index is set, this is a regular offset or a pre-indexed load; - // if index is not set then is is a post-index load. - // - // If wback is set rn is updated - this is a pre or post index load, - // if wback is not set this is a regular offset memory access. - // - // (-255 <= offset <= 255) - // _reg = REG[rn] - // _tmp = _reg + offset - // MEM[index ? _tmp : _reg] = REG[rt] - // if (wback) REG[rn] = _tmp - ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(index || wback); - ASSERT(!wback | (rt != rn)); - - bool add = true; - if (offset < 0) { - add = false; - offset = -offset; - } - ASSERT((offset & ~0xff) == 0); - - offset |= (wback << 8); - offset |= (add << 9); - offset |= (index << 10); - offset |= (1 << 11); - - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRH_imm_T3, rn, rt, offset); - } - - ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(!BadReg(rt)); // Memory hint - ASSERT(rn != ARMRegisters::pc); // LDRH (literal) - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRH_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_LDRH_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - void ldrb(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rn != ARMRegisters::pc); // LDR (literal) - ASSERT(imm.isUInt12()); - - if (!((rt | rn) & 8) && imm.isUInt5()) - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDRB_imm_T1, imm.getUInt5(), rn, rt); - else - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRB_imm_T2, rn, rt, imm.getUInt12()); - } - - void ldrb(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(index || wback); - ASSERT(!wback | (rt != rn)); - - bool add = true; - if (offset < 0) { - add = false; - offset = -offset; - } - - ASSERT(!(offset & ~0xff)); - - offset |= (wback << 8); - offset |= (add << 9); - offset |= (index << 10); - offset |= (1 << 11); - - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRB_imm_T3, rn, rt, offset); - } - - ALWAYS_INLINE void ldrb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(rn != ARMRegisters::pc); // LDR (literal) - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRB_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_LDRB_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - void ldrsb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRSB_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_LDRSB_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - void ldrsh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRSH_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_LDRSH_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - void lsl(RegisterID rd, RegisterID rm, int32_t shiftAmount) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rm)); - ShiftTypeAndAmount shift(SRType_LSL, shiftAmount); - m_formatter.twoWordOp16FourFours(OP_LSL_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - ALWAYS_INLINE void lsl(RegisterID rd, RegisterID rn, RegisterID rm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_LSL_reg_T2, rn, FourFours(0xf, rd, 0, rm)); - } - - ALWAYS_INLINE void lsr(RegisterID rd, RegisterID rm, int32_t shiftAmount) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rm)); - ShiftTypeAndAmount shift(SRType_LSR, shiftAmount); - m_formatter.twoWordOp16FourFours(OP_LSR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - ALWAYS_INLINE void lsr(RegisterID rd, RegisterID rn, RegisterID rm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_LSR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); - } - - ALWAYS_INLINE void movT3(RegisterID rd, ARMThumbImmediate imm) - { - ASSERT(imm.isValid()); - ASSERT(!imm.isEncodedImm()); - ASSERT(!BadReg(rd)); - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOV_imm_T3, imm.m_value.imm4, rd, imm); - } - - static void revertJumpTo_movT3(void* instructionStart, RegisterID rd, ARMThumbImmediate imm) - { - ASSERT(imm.isValid()); - ASSERT(!imm.isEncodedImm()); - ASSERT(!BadReg(rd)); - - uint16_t* address = static_cast(instructionStart); - address[0] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, imm); - address[1] = twoWordOp5i6Imm4Reg4EncodedImmSecond(rd, imm); - cacheFlush(address, sizeof(uint16_t) * 2); - } - - ALWAYS_INLINE void mov(RegisterID rd, ARMThumbImmediate imm) - { - ASSERT(imm.isValid()); - ASSERT(!BadReg(rd)); - - if ((rd < 8) && imm.isUInt8()) - m_formatter.oneWordOp5Reg3Imm8(OP_MOV_imm_T1, rd, imm.getUInt8()); - else if (imm.isEncodedImm()) - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOV_imm_T2, 0xf, rd, imm); - else - movT3(rd, imm); - } - - ALWAYS_INLINE void mov(RegisterID rd, RegisterID rm) - { - m_formatter.oneWordOp8RegReg143(OP_MOV_reg_T1, rm, rd); - } - - ALWAYS_INLINE void movt(RegisterID rd, ARMThumbImmediate imm) - { - ASSERT(imm.isUInt16()); - ASSERT(!BadReg(rd)); - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOVT, imm.m_value.imm4, rd, imm); - } - - ALWAYS_INLINE void mvn(RegisterID rd, ARMThumbImmediate imm) - { - ASSERT(imm.isEncodedImm()); - ASSERT(!BadReg(rd)); - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MVN_imm, 0xf, rd, imm); - } - - ALWAYS_INLINE void mvn(RegisterID rd, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp16FourFours(OP_MVN_reg_T2, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - ALWAYS_INLINE void mvn(RegisterID rd, RegisterID rm) - { - if (!((rd | rm) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_MVN_reg_T1, rm, rd); - else - mvn(rd, rm, ShiftTypeAndAmount()); - } - - ALWAYS_INLINE void neg(RegisterID rd, RegisterID rm) - { - ARMThumbImmediate zero = ARMThumbImmediate::makeUInt12(0); - sub(rd, zero, rm); - } - - ALWAYS_INLINE void orr(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(imm.isEncodedImm()); - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ORR_imm_T1, rn, rd, imm); - } - - ALWAYS_INLINE void orr(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_ORR_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - void orr(RegisterID rd, RegisterID rn, RegisterID rm) - { - if ((rd == rn) && !((rd | rm) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rm, rd); - else if ((rd == rm) && !((rd | rn) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rn, rd); - else - orr(rd, rn, rm, ShiftTypeAndAmount()); - } - - ALWAYS_INLINE void orr_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_ORR_S_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - void orr_S(RegisterID rd, RegisterID rn, RegisterID rm) - { - if ((rd == rn) && !((rd | rm) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rm, rd); - else if ((rd == rm) && !((rd | rn) & 8)) - m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rn, rd); - else - orr_S(rd, rn, rm, ShiftTypeAndAmount()); - } - - ALWAYS_INLINE void ror(RegisterID rd, RegisterID rm, int32_t shiftAmount) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rm)); - ShiftTypeAndAmount shift(SRType_ROR, shiftAmount); - m_formatter.twoWordOp16FourFours(OP_ROR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - ALWAYS_INLINE void ror(RegisterID rd, RegisterID rn, RegisterID rm) - { - ASSERT(!BadReg(rd)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_ROR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); - } - - ALWAYS_INLINE void smull(RegisterID rdLo, RegisterID rdHi, RegisterID rn, RegisterID rm) - { - ASSERT(!BadReg(rdLo)); - ASSERT(!BadReg(rdHi)); - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - ASSERT(rdLo != rdHi); - m_formatter.twoWordOp12Reg4FourFours(OP_SMULL_T1, rn, FourFours(rdLo, rdHi, 0, rm)); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isUInt12()); - - if (!((rt | rn) & 8) && imm.isUInt7()) - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STR_imm_T1, imm.getUInt7() >> 2, rn, rt); - else if ((rn == ARMRegisters::sp) && !(rt & 8) && imm.isUInt10()) - m_formatter.oneWordOp5Reg3Imm8(OP_STR_imm_T2, rt, static_cast(imm.getUInt10() >> 2)); - else - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STR_imm_T3, rn, rt, imm.getUInt12()); - } - - // If index is set, this is a regular offset or a pre-indexed store; - // if index is not set then is is a post-index store. - // - // If wback is set rn is updated - this is a pre or post index store, - // if wback is not set this is a regular offset memory access. - // - // (-255 <= offset <= 255) - // _reg = REG[rn] - // _tmp = _reg + offset - // MEM[index ? _tmp : _reg] = REG[rt] - // if (wback) REG[rn] = _tmp - ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(index || wback); - ASSERT(!wback | (rt != rn)); - - bool add = true; - if (offset < 0) { - add = false; - offset = -offset; - } - ASSERT((offset & ~0xff) == 0); - - offset |= (wback << 8); - offset |= (add << 9); - offset |= (index << 10); - offset |= (1 << 11); - - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STR_imm_T4, rn, rt, offset); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STR_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_STR_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isUInt12()); - - if (!((rt | rn) & 8) && imm.isUInt7()) - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STRB_imm_T1, imm.getUInt7() >> 2, rn, rt); - else - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRB_imm_T2, rn, rt, imm.getUInt12()); - } - - // If index is set, this is a regular offset or a pre-indexed store; - // if index is not set then is is a post-index store. - // - // If wback is set rn is updated - this is a pre or post index store, - // if wback is not set this is a regular offset memory access. - // - // (-255 <= offset <= 255) - // _reg = REG[rn] - // _tmp = _reg + offset - // MEM[index ? _tmp : _reg] = REG[rt] - // if (wback) REG[rn] = _tmp - ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(index || wback); - ASSERT(!wback | (rt != rn)); - - bool add = true; - if (offset < 0) { - add = false; - offset = -offset; - } - ASSERT((offset & ~0xff) == 0); - - offset |= (wback << 8); - offset |= (add << 9); - offset |= (index << 10); - offset |= (1 << 11); - - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRB_imm_T3, rn, rt, offset); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STRB_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_STRB_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isUInt12()); - - if (!((rt | rn) & 8) && imm.isUInt7()) - m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STRH_imm_T1, imm.getUInt7() >> 2, rn, rt); - else - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRH_imm_T2, rn, rt, imm.getUInt12()); - } - - // If index is set, this is a regular offset or a pre-indexed store; - // if index is not set then is is a post-index store. - // - // If wback is set rn is updated - this is a pre or post index store, - // if wback is not set this is a regular offset memory access. - // - // (-255 <= offset <= 255) - // _reg = REG[rn] - // _tmp = _reg + offset - // MEM[index ? _tmp : _reg] = REG[rt] - // if (wback) REG[rn] = _tmp - ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) - { - ASSERT(rt != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(index || wback); - ASSERT(!wback | (rt != rn)); - - bool add = true; - if (offset < 0) { - add = false; - offset = -offset; - } - ASSERT(!(offset & ~0xff)); - - offset |= (wback << 8); - offset |= (add << 9); - offset |= (index << 10); - offset |= (1 << 11); - - m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRH_imm_T3, rn, rt, offset); - } - - // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. - ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) - { - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - ASSERT(shift <= 3); - - if (!shift && !((rt | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STRH_reg_T1, rm, rn, rt); - else - m_formatter.twoWordOp12Reg4FourFours(OP_STRH_reg_T2, rn, FourFours(rt, 0, shift, rm)); - } - - ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - // Rd can only be SP if Rn is also SP. - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isValid()); - - if ((rn == ARMRegisters::sp) && (rd == ARMRegisters::sp) && imm.isUInt9()) { - ASSERT(!(imm.getUInt16() & 3)); - m_formatter.oneWordOp9Imm7(OP_SUB_SP_imm_T1, static_cast(imm.getUInt9() >> 2)); - return; - } else if (!((rd | rn) & 8)) { - if (imm.isUInt3()) { - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); - return; - } else if ((rd == rn) && imm.isUInt8()) { - m_formatter.oneWordOp5Reg3Imm8(OP_SUB_imm_T2, rd, imm.getUInt8()); - return; - } - } - - if (imm.isEncodedImm()) - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_imm_T3, rn, rd, imm); - else { - ASSERT(imm.isUInt12()); - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_imm_T4, rn, rd, imm); - } - } - - ALWAYS_INLINE void sub(RegisterID rd, ARMThumbImmediate imm, RegisterID rn) - { - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isValid()); - ASSERT(imm.isUInt12()); - - if (!((rd | rn) & 8) && !imm.getUInt12()) - m_formatter.oneWordOp10Reg3Reg3(OP_RSB_imm_T1, rn, rd); - else - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_RSB_imm_T2, rn, rd, imm); - } - - ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_SUB_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - // NOTE: In an IT block, add doesn't modify the flags register. - ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, RegisterID rm) - { - if (!((rd | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_reg_T1, rm, rn, rd); - else - sub(rd, rn, rm, ShiftTypeAndAmount()); - } - - // Not allowed in an IT (if then) block. - void sub_S(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) - { - // Rd can only be SP if Rn is also SP. - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isValid()); - - if ((rn == ARMRegisters::sp) && (rd == ARMRegisters::sp) && imm.isUInt9()) { - ASSERT(!(imm.getUInt16() & 3)); - m_formatter.oneWordOp9Imm7(OP_SUB_SP_imm_T1, static_cast(imm.getUInt9() >> 2)); - return; - } else if (!((rd | rn) & 8)) { - if (imm.isUInt3()) { - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); - return; - } else if ((rd == rn) && imm.isUInt8()) { - m_formatter.oneWordOp5Reg3Imm8(OP_SUB_imm_T2, rd, imm.getUInt8()); - return; - } - } - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_S_imm_T3, rn, rd, imm); - } - - ALWAYS_INLINE void sub_S(RegisterID rd, ARMThumbImmediate imm, RegisterID rn) - { - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(imm.isValid()); - ASSERT(imm.isUInt12()); - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_RSB_S_imm_T2, rn, rd, imm); - } - - // Not allowed in an IT (if then) block? - ALWAYS_INLINE void sub_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); - ASSERT(rd != ARMRegisters::pc); - ASSERT(rn != ARMRegisters::pc); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_SUB_S_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); - } - - // Not allowed in an IT (if then) block. - ALWAYS_INLINE void sub_S(RegisterID rd, RegisterID rn, RegisterID rm) - { - if (!((rd | rn | rm) & 8)) - m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_reg_T1, rm, rn, rd); - else - sub_S(rd, rn, rm, ShiftTypeAndAmount()); - } - - ALWAYS_INLINE void tst(RegisterID rn, ARMThumbImmediate imm) - { - ASSERT(!BadReg(rn)); - ASSERT(imm.isEncodedImm()); - - m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_TST_imm, rn, (RegisterID)0xf, imm); - } - - ALWAYS_INLINE void tst(RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) - { - ASSERT(!BadReg(rn)); - ASSERT(!BadReg(rm)); - m_formatter.twoWordOp12Reg4FourFours(OP_TST_reg_T2, rn, FourFours(shift.hi4(), 0xf, shift.lo4(), rm)); - } - - ALWAYS_INLINE void tst(RegisterID rn, RegisterID rm) - { - if ((rn | rm) & 8) - tst(rn, rm, ShiftTypeAndAmount()); - else - m_formatter.oneWordOp10Reg3Reg3(OP_TST_reg_T1, rm, rn); - } - - ALWAYS_INLINE void ubfx(RegisterID rd, RegisterID rn, unsigned lsb, unsigned width) - { - ASSERT(lsb < 32); - ASSERT((width >= 1) && (width <= 32)); - ASSERT((lsb + width) <= 32); - m_formatter.twoWordOp12Reg40Imm3Reg4Imm20Imm5(OP_UBFX_T1, rd, rn, (lsb & 0x1c) << 10, (lsb & 0x3) << 6, (width - 1) & 0x1f); - } - - void vadd(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VADD_T2, OP_VADD_T2b, true, rn, rd, rm); - } - - void vcmp(FPDoubleRegisterID rd, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VCMP, OP_VCMPb, true, VFPOperand(4), rd, rm); - } - - void vcmpz(FPDoubleRegisterID rd) - { - m_formatter.vfpOp(OP_VCMP, OP_VCMPb, true, VFPOperand(5), rd, VFPOperand(0)); - } - - void vcvt_signedToFloatingPoint(FPDoubleRegisterID rd, FPSingleRegisterID rm) - { - // boolean values are 64bit (toInt, unsigned, roundZero) - m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(false, false, false), rd, rm); - } - - void vcvt_floatingPointToSigned(FPSingleRegisterID rd, FPDoubleRegisterID rm) - { - // boolean values are 64bit (toInt, unsigned, roundZero) - m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(true, false, true), rd, rm); - } - - void vcvt_floatingPointToUnsigned(FPSingleRegisterID rd, FPDoubleRegisterID rm) - { - // boolean values are 64bit (toInt, unsigned, roundZero) - m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(true, true, true), rd, rm); - } - - void vdiv(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VDIV, OP_VDIVb, true, rn, rd, rm); - } - - void vldr(FPDoubleRegisterID rd, RegisterID rn, int32_t imm) - { - m_formatter.vfpMemOp(OP_VLDR, OP_VLDRb, true, rn, rd, imm); - } - - void flds(FPSingleRegisterID rd, RegisterID rn, int32_t imm) - { - m_formatter.vfpMemOp(OP_FLDS, OP_FLDSb, false, rn, rd, imm); - } - - void vmov(RegisterID rd, FPSingleRegisterID rn) - { - ASSERT(!BadReg(rd)); - m_formatter.vfpOp(OP_VMOV_StoC, OP_VMOV_StoCb, false, rn, rd, VFPOperand(0)); - } - - void vmov(FPSingleRegisterID rd, RegisterID rn) - { - ASSERT(!BadReg(rn)); - m_formatter.vfpOp(OP_VMOV_CtoS, OP_VMOV_CtoSb, false, rd, rn, VFPOperand(0)); - } - - void vmov(RegisterID rd1, RegisterID rd2, FPDoubleRegisterID rn) - { - ASSERT(!BadReg(rd1)); - ASSERT(!BadReg(rd2)); - m_formatter.vfpOp(OP_VMOV_DtoC, OP_VMOV_DtoCb, true, rd2, VFPOperand(rd1 | 16), rn); - } - - void vmov(FPDoubleRegisterID rd, RegisterID rn1, RegisterID rn2) - { - ASSERT(!BadReg(rn1)); - ASSERT(!BadReg(rn2)); - m_formatter.vfpOp(OP_VMOV_CtoD, OP_VMOV_CtoDb, true, rn2, VFPOperand(rn1 | 16), rd); - } - - void vmov(FPDoubleRegisterID rd, FPDoubleRegisterID rn) - { - m_formatter.vfpOp(OP_VMOV_T2, OP_VMOV_T2b, true, VFPOperand(0), rd, rn); - } - - void vmrs(RegisterID reg = ARMRegisters::pc) - { - ASSERT(reg != ARMRegisters::sp); - m_formatter.vfpOp(OP_VMRS, OP_VMRSb, false, VFPOperand(1), VFPOperand(0x10 | reg), VFPOperand(0)); - } - - void vmul(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VMUL_T2, OP_VMUL_T2b, true, rn, rd, rm); - } - - void vstr(FPDoubleRegisterID rd, RegisterID rn, int32_t imm) - { - m_formatter.vfpMemOp(OP_VSTR, OP_VSTRb, true, rn, rd, imm); - } - - void fsts(FPSingleRegisterID rd, RegisterID rn, int32_t imm) - { - m_formatter.vfpMemOp(OP_FSTS, OP_FSTSb, false, rn, rd, imm); - } - - void vsub(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VSUB_T2, OP_VSUB_T2b, true, rn, rd, rm); - } - - void vabs(FPDoubleRegisterID rd, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VABS_T2, OP_VABS_T2b, true, VFPOperand(16), rd, rm); - } - - void vneg(FPDoubleRegisterID rd, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VNEG_T2, OP_VNEG_T2b, true, VFPOperand(1), rd, rm); - } - - void vsqrt(FPDoubleRegisterID rd, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VSQRT_T1, OP_VSQRT_T1b, true, VFPOperand(17), rd, rm); - } - - void vcvtds(FPDoubleRegisterID rd, FPSingleRegisterID rm) - { - m_formatter.vfpOp(OP_VCVTDS_T1, OP_VCVTDS_T1b, false, VFPOperand(23), rd, rm); - } - - void vcvtsd(FPSingleRegisterID rd, FPDoubleRegisterID rm) - { - m_formatter.vfpOp(OP_VCVTSD_T1, OP_VCVTSD_T1b, true, VFPOperand(23), rd, rm); - } - - void nop() - { - m_formatter.oneWordOp8Imm8(OP_NOP_T1, 0); - } - - AssemblerLabel labelIgnoringWatchpoints() - { - return m_formatter.label(); - } - - AssemblerLabel labelForWatchpoint() - { - AssemblerLabel result = m_formatter.label(); - if (static_cast(result.m_offset) != m_indexOfLastWatchpoint) - result = label(); - m_indexOfLastWatchpoint = result.m_offset; - m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); - return result; - } - - AssemblerLabel label() - { - AssemblerLabel result = m_formatter.label(); - while (UNLIKELY(static_cast(result.m_offset) < m_indexOfTailOfLastWatchpoint)) { - nop(); - result = m_formatter.label(); - } - return result; - } - - AssemblerLabel align(int alignment) - { - while (!m_formatter.isAligned(alignment)) - bkpt(); - - return label(); - } - - static void* getRelocatedAddress(void* code, AssemblerLabel label) - { - ASSERT(label.isSet()); - return reinterpret_cast(reinterpret_cast(code) + label.m_offset); - } - - static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) - { - return b.m_offset - a.m_offset; - } - - int executableOffsetFor(int location) - { - if (!location) - return 0; - return static_cast(m_formatter.data())[location / sizeof(int32_t) - 1]; - } - - int jumpSizeDelta(JumpType jumpType, JumpLinkType jumpLinkType) { return JUMP_ENUM_SIZE(jumpType) - JUMP_ENUM_SIZE(jumpLinkType); } - - // Assembler admin methods: - - static ALWAYS_INLINE bool linkRecordSourceComparator(const LinkRecord& a, const LinkRecord& b) - { - return a.from() < b.from(); - } - - bool canCompact(JumpType jumpType) - { - // The following cannot be compacted: - // JumpFixed: represents custom jump sequence - // JumpNoConditionFixedSize: represents unconditional jump that must remain a fixed size - // JumpConditionFixedSize: represents conditional jump that must remain a fixed size - return (jumpType == JumpNoCondition) || (jumpType == JumpCondition); - } - - JumpLinkType computeJumpType(JumpType jumpType, const uint8_t* from, const uint8_t* to) - { - if (jumpType == JumpFixed) - return LinkInvalid; - - // for patchable jump we must leave space for the longest code sequence - if (jumpType == JumpNoConditionFixedSize) - return LinkBX; - if (jumpType == JumpConditionFixedSize) - return LinkConditionalBX; - - const int paddingSize = JUMP_ENUM_SIZE(jumpType); - - if (jumpType == JumpCondition) { - // 2-byte conditional T1 - const uint16_t* jumpT1Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT1))); - if (canBeJumpT1(jumpT1Location, to)) - return LinkJumpT1; - // 4-byte conditional T3 - const uint16_t* jumpT3Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT3))); - if (canBeJumpT3(jumpT3Location, to)) - return LinkJumpT3; - // 4-byte conditional T4 with IT - const uint16_t* conditionalJumpT4Location = - reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkConditionalJumpT4))); - if (canBeJumpT4(conditionalJumpT4Location, to)) - return LinkConditionalJumpT4; - } else { - // 2-byte unconditional T2 - const uint16_t* jumpT2Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT2))); - if (canBeJumpT2(jumpT2Location, to)) - return LinkJumpT2; - // 4-byte unconditional T4 - const uint16_t* jumpT4Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT4))); - if (canBeJumpT4(jumpT4Location, to)) - return LinkJumpT4; - // use long jump sequence - return LinkBX; - } - - ASSERT(jumpType == JumpCondition); - return LinkConditionalBX; - } - - JumpLinkType computeJumpType(LinkRecord& record, const uint8_t* from, const uint8_t* to) - { - JumpLinkType linkType = computeJumpType(record.type(), from, to); - record.setLinkType(linkType); - return linkType; - } - - void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset) - { - int32_t ptr = regionStart / sizeof(int32_t); - const int32_t end = regionEnd / sizeof(int32_t); - int32_t* offsets = static_cast(m_formatter.data()); - while (ptr < end) - offsets[ptr++] = offset; - } - - Vector& jumpsToLink() - { - std::sort(m_jumpsToLink.begin(), m_jumpsToLink.end(), linkRecordSourceComparator); - return m_jumpsToLink; - } - - void ALWAYS_INLINE link(LinkRecord& record, uint8_t* from, uint8_t* to) - { - switch (record.linkType()) { - case LinkJumpT1: - linkJumpT1(record.condition(), reinterpret_cast_ptr(from), to); - break; - case LinkJumpT2: - linkJumpT2(reinterpret_cast_ptr(from), to); - break; - case LinkJumpT3: - linkJumpT3(record.condition(), reinterpret_cast_ptr(from), to); - break; - case LinkJumpT4: - linkJumpT4(reinterpret_cast_ptr(from), to); - break; - case LinkConditionalJumpT4: - linkConditionalJumpT4(record.condition(), reinterpret_cast_ptr(from), to); - break; - case LinkConditionalBX: - linkConditionalBX(record.condition(), reinterpret_cast_ptr(from), to); - break; - case LinkBX: - linkBX(reinterpret_cast_ptr(from), to); - break; - default: - ASSERT_NOT_REACHED(); - break; - } - } - - void* unlinkedCode() { return m_formatter.data(); } - size_t codeSize() const { return m_formatter.codeSize(); } - - static unsigned getCallReturnOffset(AssemblerLabel call) - { - ASSERT(call.isSet()); - return call.m_offset; - } - - // Linking & patching: - // - // 'link' and 'patch' methods are for use on unprotected code - such as the code - // within the AssemblerBuffer, and code being patched by the patch buffer. Once - // code has been finalized it is (platform support permitting) within a non- - // writable region of memory; to modify the code in an execute-only execuable - // pool the 'repatch' and 'relink' methods should be used. - - void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type, Condition condition) - { - ASSERT(to.isSet()); - ASSERT(from.isSet()); - m_jumpsToLink.append(LinkRecord(from.m_offset, to.m_offset, type, condition)); - } - - static void linkJump(void* code, AssemblerLabel from, void* to) - { - ASSERT(from.isSet()); - - uint16_t* location = reinterpret_cast(reinterpret_cast(code) + from.m_offset); - linkJumpAbsolute(location, to); - } - - static void linkCall(void* code, AssemblerLabel from, void* to) - { - ASSERT(!(reinterpret_cast(code) & 1)); - ASSERT(from.isSet()); - ASSERT(reinterpret_cast(to) & 1); - - setPointer(reinterpret_cast(reinterpret_cast(code) + from.m_offset) - 1, to, false); - } - - static void linkPointer(void* code, AssemblerLabel where, void* value) - { - setPointer(reinterpret_cast(code) + where.m_offset, value, false); - } - - static void relinkJump(void* from, void* to) - { - ASSERT(!(reinterpret_cast(from) & 1)); - ASSERT(!(reinterpret_cast(to) & 1)); - - linkJumpAbsolute(reinterpret_cast(from), to); - - cacheFlush(reinterpret_cast(from) - 5, 5 * sizeof(uint16_t)); - } - - static void relinkCall(void* from, void* to) - { - ASSERT(!(reinterpret_cast(from) & 1)); - ASSERT(reinterpret_cast(to) & 1); - - setPointer(reinterpret_cast(from) - 1, to, true); - } - - static void* readCallTarget(void* from) - { - return readPointer(reinterpret_cast(from) - 1); - } - - static void repatchInt32(void* where, int32_t value) - { - ASSERT(!(reinterpret_cast(where) & 1)); - - setInt32(where, value, true); - } - - static void repatchCompact(void* where, int32_t offset) - { - ASSERT(offset >= -255 && offset <= 255); - - bool add = true; - if (offset < 0) { - add = false; - offset = -offset; - } - - offset |= (add << 9); - offset |= (1 << 10); - offset |= (1 << 11); - - uint16_t* location = reinterpret_cast(where); - location[1] &= ~((1 << 12) - 1); - location[1] |= offset; - cacheFlush(location, sizeof(uint16_t) * 2); - } - - static void repatchPointer(void* where, void* value) - { - ASSERT(!(reinterpret_cast(where) & 1)); - - setPointer(where, value, true); - } - - static void* readPointer(void* where) - { - return reinterpret_cast(readInt32(where)); - } - - static void replaceWithJump(void* instructionStart, void* to) - { - ASSERT(!(bitwise_cast(instructionStart) & 1)); - ASSERT(!(bitwise_cast(to) & 1)); - uint16_t* ptr = reinterpret_cast(instructionStart) + 2; - - linkJumpT4(ptr, to); - cacheFlush(ptr - 2, sizeof(uint16_t) * 2); - } - - static ptrdiff_t maxJumpReplacementSize() - { - return 4; - } - - static void replaceWithLoad(void* instructionStart) - { - ASSERT(!(bitwise_cast(instructionStart) & 1)); - uint16_t* ptr = reinterpret_cast(instructionStart); - switch (ptr[0] & 0xFFF0) { - case OP_LDR_imm_T3: - break; - case OP_ADD_imm_T3: - ASSERT(!(ptr[1] & 0xF000)); - ptr[0] &= 0x000F; - ptr[0] |= OP_LDR_imm_T3; - ptr[1] |= (ptr[1] & 0x0F00) << 4; - ptr[1] &= 0xF0FF; - cacheFlush(ptr, sizeof(uint16_t) * 2); - break; - default: - ASSERT_NOT_REACHED(); - } - } - - static void replaceWithAddressComputation(void* instructionStart) - { - ASSERT(!(bitwise_cast(instructionStart) & 1)); - uint16_t* ptr = reinterpret_cast(instructionStart); - switch (ptr[0] & 0xFFF0) { - case OP_LDR_imm_T3: - ASSERT(!(ptr[1] & 0x0F00)); - ptr[0] &= 0x000F; - ptr[0] |= OP_ADD_imm_T3; - ptr[1] |= (ptr[1] & 0xF000) >> 4; - ptr[1] &= 0x0FFF; - cacheFlush(ptr, sizeof(uint16_t) * 2); - break; - case OP_ADD_imm_T3: - break; - default: - ASSERT_NOT_REACHED(); - } - } - - unsigned debugOffset() { return m_formatter.debugOffset(); } - - static void cacheFlush(void* code, size_t size) - { -#if OS(IOS) - sys_cache_control(kCacheFunctionPrepareForExecution, code, size); -#elif OS(LINUX) - asm volatile( - "push {r7}\n" - "mov r0, %0\n" - "mov r1, %1\n" - "movw r7, #0x2\n" - "movt r7, #0xf\n" - "movs r2, #0x0\n" - "svc 0x0\n" - "pop {r7}\n" - : - : "r" (code), "r" (reinterpret_cast(code) + size) - : "r0", "r1", "r2"); -#elif OS(WINCE) - CacheRangeFlush(code, size, CACHE_SYNC_ALL); -#elif OS(QNX) -#if !ENABLE(ASSEMBLER_WX_EXCLUSIVE) - msync(code, size, MS_INVALIDATE_ICACHE); -#else - UNUSED_PARAM(code); - UNUSED_PARAM(size); -#endif -#else -#error "The cacheFlush support is missing on this platform." -#endif - } - -private: - // VFP operations commonly take one or more 5-bit operands, typically representing a - // floating point register number. This will commonly be encoded in the instruction - // in two parts, with one single bit field, and one 4-bit field. In the case of - // double precision operands the high bit of the register number will be encoded - // separately, and for single precision operands the high bit of the register number - // will be encoded individually. - // VFPOperand encapsulates a 5-bit VFP operand, with bits 0..3 containing the 4-bit - // field to be encoded together in the instruction (the low 4-bits of a double - // register number, or the high 4-bits of a single register number), and bit 4 - // contains the bit value to be encoded individually. - struct VFPOperand { - explicit VFPOperand(uint32_t value) - : m_value(value) - { - ASSERT(!(m_value & ~0x1f)); - } - - VFPOperand(FPDoubleRegisterID reg) - : m_value(reg) - { - } - - VFPOperand(RegisterID reg) - : m_value(reg) - { - } - - VFPOperand(FPSingleRegisterID reg) - : m_value(((reg & 1) << 4) | (reg >> 1)) // rotate the lowest bit of 'reg' to the top. - { - } - - uint32_t bits1() - { - return m_value >> 4; - } - - uint32_t bits4() - { - return m_value & 0xf; - } - - uint32_t m_value; - }; - - VFPOperand vcvtOp(bool toInteger, bool isUnsigned, bool isRoundZero) - { - // Cannot specify rounding when converting to float. - ASSERT(toInteger || !isRoundZero); - - uint32_t op = 0x8; - if (toInteger) { - // opc2 indicates both toInteger & isUnsigned. - op |= isUnsigned ? 0x4 : 0x5; - // 'op' field in instruction is isRoundZero - if (isRoundZero) - op |= 0x10; - } else { - ASSERT(!isRoundZero); - // 'op' field in instruction is isUnsigned - if (!isUnsigned) - op |= 0x10; - } - return VFPOperand(op); - } - - static void setInt32(void* code, uint32_t value, bool flush) - { - uint16_t* location = reinterpret_cast(code); - ASSERT(isMOV_imm_T3(location - 4) && isMOVT(location - 2)); - - ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(value)); - ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(value >> 16)); - location[-4] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); - location[-3] = twoWordOp5i6Imm4Reg4EncodedImmSecond((location[-3] >> 8) & 0xf, lo16); - location[-2] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); - location[-1] = twoWordOp5i6Imm4Reg4EncodedImmSecond((location[-1] >> 8) & 0xf, hi16); - - if (flush) - cacheFlush(location - 4, 4 * sizeof(uint16_t)); - } - - static int32_t readInt32(void* code) - { - uint16_t* location = reinterpret_cast(code); - ASSERT(isMOV_imm_T3(location - 4) && isMOVT(location - 2)); - - ARMThumbImmediate lo16; - ARMThumbImmediate hi16; - decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(lo16, location[-4]); - decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(lo16, location[-3]); - decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(hi16, location[-2]); - decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(hi16, location[-1]); - uint32_t result = hi16.asUInt16(); - result <<= 16; - result |= lo16.asUInt16(); - return static_cast(result); - } - - static void setUInt7ForLoad(void* code, ARMThumbImmediate imm) - { - // Requires us to have planted a LDR_imm_T1 - ASSERT(imm.isValid()); - ASSERT(imm.isUInt7()); - uint16_t* location = reinterpret_cast(code); - location[0] &= ~((static_cast(0x7f) >> 2) << 6); - location[0] |= (imm.getUInt7() >> 2) << 6; - cacheFlush(location, sizeof(uint16_t)); - } - - static void setPointer(void* code, void* value, bool flush) - { - setInt32(code, reinterpret_cast(value), flush); - } - - static bool isB(void* address) - { - uint16_t* instruction = static_cast(address); - return ((instruction[0] & 0xf800) == OP_B_T4a) && ((instruction[1] & 0xd000) == OP_B_T4b); - } - - static bool isBX(void* address) - { - uint16_t* instruction = static_cast(address); - return (instruction[0] & 0xff87) == OP_BX; - } - - static bool isMOV_imm_T3(void* address) - { - uint16_t* instruction = static_cast(address); - return ((instruction[0] & 0xFBF0) == OP_MOV_imm_T3) && ((instruction[1] & 0x8000) == 0); - } - - static bool isMOVT(void* address) - { - uint16_t* instruction = static_cast(address); - return ((instruction[0] & 0xFBF0) == OP_MOVT) && ((instruction[1] & 0x8000) == 0); - } - - static bool isNOP_T1(void* address) - { - uint16_t* instruction = static_cast(address); - return instruction[0] == OP_NOP_T1; - } - - static bool isNOP_T2(void* address) - { - uint16_t* instruction = static_cast(address); - return (instruction[0] == OP_NOP_T2a) && (instruction[1] == OP_NOP_T2b); - } - - static bool canBeJumpT1(const uint16_t* instruction, const void* target) - { - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - // It does not appear to be documented in the ARM ARM (big surprise), but - // for OP_B_T1 the branch displacement encoded in the instruction is 2 - // less than the actual displacement. - relative -= 2; - return ((relative << 23) >> 23) == relative; - } - - static bool canBeJumpT2(const uint16_t* instruction, const void* target) - { - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - // It does not appear to be documented in the ARM ARM (big surprise), but - // for OP_B_T2 the branch displacement encoded in the instruction is 2 - // less than the actual displacement. - relative -= 2; - return ((relative << 20) >> 20) == relative; - } - - static bool canBeJumpT3(const uint16_t* instruction, const void* target) - { - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - return ((relative << 11) >> 11) == relative; - } - - static bool canBeJumpT4(const uint16_t* instruction, const void* target) - { - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - return ((relative << 7) >> 7) == relative; - } - - void linkJumpT1(Condition cond, uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - ASSERT(canBeJumpT1(instruction, target)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - // It does not appear to be documented in the ARM ARM (big surprise), but - // for OP_B_T1 the branch displacement encoded in the instruction is 2 - // less than the actual displacement. - relative -= 2; - - // All branch offsets should be an even distance. - ASSERT(!(relative & 1)); - instruction[-1] = OP_B_T1 | ((cond & 0xf) << 8) | ((relative & 0x1fe) >> 1); - } - - static void linkJumpT2(uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - ASSERT(canBeJumpT2(instruction, target)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - // It does not appear to be documented in the ARM ARM (big surprise), but - // for OP_B_T2 the branch displacement encoded in the instruction is 2 - // less than the actual displacement. - relative -= 2; - - // All branch offsets should be an even distance. - ASSERT(!(relative & 1)); - instruction[-1] = OP_B_T2 | ((relative & 0xffe) >> 1); - } - - void linkJumpT3(Condition cond, uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - ASSERT(canBeJumpT3(instruction, target)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - - // All branch offsets should be an even distance. - ASSERT(!(relative & 1)); - instruction[-2] = OP_B_T3a | ((relative & 0x100000) >> 10) | ((cond & 0xf) << 6) | ((relative & 0x3f000) >> 12); - instruction[-1] = OP_B_T3b | ((relative & 0x80000) >> 8) | ((relative & 0x40000) >> 5) | ((relative & 0xffe) >> 1); - } - - static void linkJumpT4(uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - ASSERT(canBeJumpT4(instruction, target)); - - intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); - // ARM encoding for the top two bits below the sign bit is 'peculiar'. - if (relative >= 0) - relative ^= 0xC00000; - - // All branch offsets should be an even distance. - ASSERT(!(relative & 1)); - instruction[-2] = OP_B_T4a | ((relative & 0x1000000) >> 14) | ((relative & 0x3ff000) >> 12); - instruction[-1] = OP_B_T4b | ((relative & 0x800000) >> 10) | ((relative & 0x400000) >> 11) | ((relative & 0xffe) >> 1); - } - - void linkConditionalJumpT4(Condition cond, uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - instruction[-3] = ifThenElse(cond) | OP_IT; - linkJumpT4(instruction, target); - } - - static void linkBX(uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; - ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) + 1)); - ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) >> 16)); - instruction[-5] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); - instruction[-4] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, lo16); - instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); - instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); - instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); - } - - void linkConditionalBX(Condition cond, uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - linkBX(instruction, target); - instruction[-6] = ifThenElse(cond, true, true) | OP_IT; - } - - static void linkJumpAbsolute(uint16_t* instruction, void* target) - { - // FIMXE: this should be up in the MacroAssembler layer. :-( - ASSERT(!(reinterpret_cast(instruction) & 1)); - ASSERT(!(reinterpret_cast(target) & 1)); - - ASSERT((isMOV_imm_T3(instruction - 5) && isMOVT(instruction - 3) && isBX(instruction - 1)) - || (isNOP_T1(instruction - 5) && isNOP_T2(instruction - 4) && isB(instruction - 2))); - - if (canBeJumpT4(instruction, target)) { - // There may be a better way to fix this, but right now put the NOPs first, since in the - // case of an conditional branch this will be coming after an ITTT predicating *three* - // instructions! Looking backwards to modify the ITTT to an IT is not easy, due to - // variable wdith encoding - the previous instruction might *look* like an ITTT but - // actually be the second half of a 2-word op. - instruction[-5] = OP_NOP_T1; - instruction[-4] = OP_NOP_T2a; - instruction[-3] = OP_NOP_T2b; - linkJumpT4(instruction, target); - } else { - const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; - ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) + 1)); - ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) >> 16)); - instruction[-5] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); - instruction[-4] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, lo16); - instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); - instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); - instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); - } - } - - static uint16_t twoWordOp5i6Imm4Reg4EncodedImmFirst(uint16_t op, ARMThumbImmediate imm) - { - return op | (imm.m_value.i << 10) | imm.m_value.imm4; - } - - static void decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(ARMThumbImmediate& result, uint16_t value) - { - result.m_value.i = (value >> 10) & 1; - result.m_value.imm4 = value & 15; - } - - static uint16_t twoWordOp5i6Imm4Reg4EncodedImmSecond(uint16_t rd, ARMThumbImmediate imm) - { - return (imm.m_value.imm3 << 12) | (rd << 8) | imm.m_value.imm8; - } - - static void decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(ARMThumbImmediate& result, uint16_t value) - { - result.m_value.imm3 = (value >> 12) & 7; - result.m_value.imm8 = value & 255; - } - - class ARMInstructionFormatter { - public: - ALWAYS_INLINE void oneWordOp5Reg3Imm8(OpcodeID op, RegisterID rd, uint8_t imm) - { - m_buffer.putShort(op | (rd << 8) | imm); - } - - ALWAYS_INLINE void oneWordOp5Imm5Reg3Reg3(OpcodeID op, uint8_t imm, RegisterID reg1, RegisterID reg2) - { - m_buffer.putShort(op | (imm << 6) | (reg1 << 3) | reg2); - } - - ALWAYS_INLINE void oneWordOp7Reg3Reg3Reg3(OpcodeID op, RegisterID reg1, RegisterID reg2, RegisterID reg3) - { - m_buffer.putShort(op | (reg1 << 6) | (reg2 << 3) | reg3); - } - - ALWAYS_INLINE void oneWordOp8Imm8(OpcodeID op, uint8_t imm) - { - m_buffer.putShort(op | imm); - } - - ALWAYS_INLINE void oneWordOp8RegReg143(OpcodeID op, RegisterID reg1, RegisterID reg2) - { - m_buffer.putShort(op | ((reg2 & 8) << 4) | (reg1 << 3) | (reg2 & 7)); - } - - ALWAYS_INLINE void oneWordOp9Imm7(OpcodeID op, uint8_t imm) - { - m_buffer.putShort(op | imm); - } - - ALWAYS_INLINE void oneWordOp10Reg3Reg3(OpcodeID op, RegisterID reg1, RegisterID reg2) - { - m_buffer.putShort(op | (reg1 << 3) | reg2); - } - - ALWAYS_INLINE void twoWordOp12Reg4FourFours(OpcodeID1 op, RegisterID reg, FourFours ff) - { - m_buffer.putShort(op | reg); - m_buffer.putShort(ff.m_u.value); - } - - ALWAYS_INLINE void twoWordOp16FourFours(OpcodeID1 op, FourFours ff) - { - m_buffer.putShort(op); - m_buffer.putShort(ff.m_u.value); - } - - ALWAYS_INLINE void twoWordOp16Op16(OpcodeID1 op1, OpcodeID2 op2) - { - m_buffer.putShort(op1); - m_buffer.putShort(op2); - } - - ALWAYS_INLINE void twoWordOp5i6Imm4Reg4EncodedImm(OpcodeID1 op, int imm4, RegisterID rd, ARMThumbImmediate imm) - { - ARMThumbImmediate newImm = imm; - newImm.m_value.imm4 = imm4; - - m_buffer.putShort(ARMv7Assembler::twoWordOp5i6Imm4Reg4EncodedImmFirst(op, newImm)); - m_buffer.putShort(ARMv7Assembler::twoWordOp5i6Imm4Reg4EncodedImmSecond(rd, newImm)); - } - - ALWAYS_INLINE void twoWordOp12Reg4Reg4Imm12(OpcodeID1 op, RegisterID reg1, RegisterID reg2, uint16_t imm) - { - m_buffer.putShort(op | reg1); - m_buffer.putShort((reg2 << 12) | imm); - } - - ALWAYS_INLINE void twoWordOp12Reg40Imm3Reg4Imm20Imm5(OpcodeID1 op, RegisterID reg1, RegisterID reg2, uint16_t imm1, uint16_t imm2, uint16_t imm3) - { - m_buffer.putShort(op | reg1); - m_buffer.putShort((imm1 << 12) | (reg2 << 8) | (imm2 << 6) | imm3); - } - - // Formats up instructions of the pattern: - // 111111111B11aaaa:bbbb222SA2C2cccc - // Where 1s in the pattern come from op1, 2s in the pattern come from op2, S is the provided size bit. - // Operands provide 5 bit values of the form Aaaaa, Bbbbb, Ccccc. - ALWAYS_INLINE void vfpOp(OpcodeID1 op1, OpcodeID2 op2, bool size, VFPOperand a, VFPOperand b, VFPOperand c) - { - ASSERT(!(op1 & 0x004f)); - ASSERT(!(op2 & 0xf1af)); - m_buffer.putShort(op1 | b.bits1() << 6 | a.bits4()); - m_buffer.putShort(op2 | b.bits4() << 12 | size << 8 | a.bits1() << 7 | c.bits1() << 5 | c.bits4()); - } - - // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. - // (i.e. +/-(0..255) 32-bit words) - ALWAYS_INLINE void vfpMemOp(OpcodeID1 op1, OpcodeID2 op2, bool size, RegisterID rn, VFPOperand rd, int32_t imm) - { - bool up = true; - if (imm < 0) { - imm = -imm; - up = false; - } - - uint32_t offset = imm; - ASSERT(!(offset & ~0x3fc)); - offset >>= 2; - - m_buffer.putShort(op1 | (up << 7) | rd.bits1() << 6 | rn); - m_buffer.putShort(op2 | rd.bits4() << 12 | size << 8 | offset); - } - - // Administrative methods: - - size_t codeSize() const { return m_buffer.codeSize(); } - AssemblerLabel label() const { return m_buffer.label(); } - bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); } - void* data() const { return m_buffer.data(); } - - unsigned debugOffset() { return m_buffer.debugOffset(); } - - private: - AssemblerBuffer m_buffer; - } m_formatter; - - Vector m_jumpsToLink; - Vector m_offsets; - int m_indexOfLastWatchpoint; - int m_indexOfTailOfLastWatchpoint; -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) - -#endif // ARMAssembler_h diff --git a/3rdparty/masm/assembler/AbstractMacroAssembler.h b/3rdparty/masm/assembler/AbstractMacroAssembler.h deleted file mode 100644 index ee78ef84eb..0000000000 --- a/3rdparty/masm/assembler/AbstractMacroAssembler.h +++ /dev/null @@ -1,792 +0,0 @@ -/* - * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef AbstractMacroAssembler_h -#define AbstractMacroAssembler_h - -#include "AssemblerBuffer.h" -#include "CodeLocation.h" -#include "MacroAssemblerCodeRef.h" -#include -#include -#include - -#if ENABLE(ASSEMBLER) - - -#if PLATFORM(QT) -#define ENABLE_JIT_CONSTANT_BLINDING 0 -#endif - -#ifndef ENABLE_JIT_CONSTANT_BLINDING -#define ENABLE_JIT_CONSTANT_BLINDING 1 -#endif - -namespace JSC { - -class JumpReplacementWatchpoint; -class LinkBuffer; -class RepatchBuffer; -class Watchpoint; -namespace DFG { -struct OSRExit; -} - -template -class AbstractMacroAssembler { -public: - friend class JITWriteBarrierBase; - typedef AssemblerType AssemblerType_T; - - typedef MacroAssemblerCodePtr CodePtr; - typedef MacroAssemblerCodeRef CodeRef; - - class Jump; - - typedef typename AssemblerType::RegisterID RegisterID; - - // Section 1: MacroAssembler operand types - // - // The following types are used as operands to MacroAssembler operations, - // describing immediate and memory operands to the instructions to be planted. - - enum Scale { - TimesOne, - TimesTwo, - TimesFour, - TimesEight, - }; - - // Address: - // - // Describes a simple base-offset address. - struct Address { - explicit Address(RegisterID base, int32_t offset = 0) - : base(base) - , offset(offset) - { - } - - RegisterID base; - int32_t offset; - }; - - struct ExtendedAddress { - explicit ExtendedAddress(RegisterID base, intptr_t offset = 0) - : base(base) - , offset(offset) - { - } - - RegisterID base; - intptr_t offset; - }; - - // ImplicitAddress: - // - // This class is used for explicit 'load' and 'store' operations - // (as opposed to situations in which a memory operand is provided - // to a generic operation, such as an integer arithmetic instruction). - // - // In the case of a load (or store) operation we want to permit - // addresses to be implicitly constructed, e.g. the two calls: - // - // load32(Address(addrReg), destReg); - // load32(addrReg, destReg); - // - // Are equivalent, and the explicit wrapping of the Address in the former - // is unnecessary. - struct ImplicitAddress { - ImplicitAddress(RegisterID base) - : base(base) - , offset(0) - { - } - - ImplicitAddress(Address address) - : base(address.base) - , offset(address.offset) - { - } - - RegisterID base; - int32_t offset; - }; - - // BaseIndex: - // - // Describes a complex addressing mode. - struct BaseIndex { - BaseIndex(RegisterID base, RegisterID index, Scale scale, int32_t offset = 0) - : base(base) - , index(index) - , scale(scale) - , offset(offset) - { - } - - RegisterID base; - RegisterID index; - Scale scale; - int32_t offset; - }; - - // AbsoluteAddress: - // - // Describes an memory operand given by a pointer. For regular load & store - // operations an unwrapped void* will be used, rather than using this. - struct AbsoluteAddress { - explicit AbsoluteAddress(const void* ptr) - : m_ptr(ptr) - { - } - - const void* m_ptr; - }; - - // TrustedImmPtr: - // - // A pointer sized immediate operand to an instruction - this is wrapped - // in a class requiring explicit construction in order to differentiate - // from pointers used as absolute addresses to memory operations - struct TrustedImmPtr { - TrustedImmPtr() { } - - explicit TrustedImmPtr(const void* value) - : m_value(value) - { - } - - // This is only here so that TrustedImmPtr(0) does not confuse the C++ - // overload handling rules. - explicit TrustedImmPtr(int value) - : m_value(0) - { - ASSERT_UNUSED(value, !value); - } - - explicit TrustedImmPtr(size_t value) - : m_value(reinterpret_cast(value)) - { - } - - intptr_t asIntptr() - { - return reinterpret_cast(m_value); - } - - const void* m_value; - }; - - struct ImmPtr : -#if ENABLE(JIT_CONSTANT_BLINDING) - private TrustedImmPtr -#else - public TrustedImmPtr -#endif - { - explicit ImmPtr(const void* value) - : TrustedImmPtr(value) - { - } - - TrustedImmPtr asTrustedImmPtr() { return *this; } - }; - - // TrustedImm32: - // - // A 32bit immediate operand to an instruction - this is wrapped in a - // class requiring explicit construction in order to prevent RegisterIDs - // (which are implemented as an enum) from accidentally being passed as - // immediate values. - struct TrustedImm32 { - TrustedImm32() { } - - explicit TrustedImm32(int32_t value) - : m_value(value) - { - } - -#if !CPU(X86_64) - explicit TrustedImm32(TrustedImmPtr ptr) - : m_value(ptr.asIntptr()) - { - } -#endif - - int32_t m_value; - }; - - - struct Imm32 : -#if ENABLE(JIT_CONSTANT_BLINDING) - private TrustedImm32 -#else - public TrustedImm32 -#endif - { - explicit Imm32(int32_t value) - : TrustedImm32(value) - { - } -#if !CPU(X86_64) - explicit Imm32(TrustedImmPtr ptr) - : TrustedImm32(ptr) - { - } -#endif - const TrustedImm32& asTrustedImm32() const { return *this; } - - }; - - // TrustedImm64: - // - // A 64bit immediate operand to an instruction - this is wrapped in a - // class requiring explicit construction in order to prevent RegisterIDs - // (which are implemented as an enum) from accidentally being passed as - // immediate values. - struct TrustedImm64 { - TrustedImm64() { } - - explicit TrustedImm64(int64_t value) - : m_value(value) - { - } - -#if CPU(X86_64) - explicit TrustedImm64(TrustedImmPtr ptr) - : m_value(ptr.asIntptr()) - { - } -#endif - - int64_t m_value; - }; - - struct Imm64 : -#if ENABLE(JIT_CONSTANT_BLINDING) - private TrustedImm64 -#else - public TrustedImm64 -#endif - { - explicit Imm64(int64_t value) - : TrustedImm64(value) - { - } -#if CPU(X86_64) - explicit Imm64(TrustedImmPtr ptr) - : TrustedImm64(ptr) - { - } -#endif - const TrustedImm64& asTrustedImm64() const { return *this; } - }; - - // Section 2: MacroAssembler code buffer handles - // - // The following types are used to reference items in the code buffer - // during JIT code generation. For example, the type Jump is used to - // track the location of a jump instruction so that it may later be - // linked to a label marking its destination. - - - // Label: - // - // A Label records a point in the generated instruction stream, typically such that - // it may be used as a destination for a jump. - class Label { - template - friend class AbstractMacroAssembler; - friend struct DFG::OSRExit; - friend class Jump; - friend class JumpReplacementWatchpoint; - friend class MacroAssemblerCodeRef; - friend class LinkBuffer; - friend class Watchpoint; - - public: - Label() - { - } - - Label(AbstractMacroAssembler* masm) - : m_label(masm->m_assembler.label()) - { - } - - bool isSet() const { return m_label.isSet(); } - private: - AssemblerLabel m_label; - }; - - // ConvertibleLoadLabel: - // - // A ConvertibleLoadLabel records a loadPtr instruction that can be patched to an addPtr - // so that: - // - // loadPtr(Address(a, i), b) - // - // becomes: - // - // addPtr(TrustedImmPtr(i), a, b) - class ConvertibleLoadLabel { - template - friend class AbstractMacroAssembler; - friend class LinkBuffer; - - public: - ConvertibleLoadLabel() - { - } - - ConvertibleLoadLabel(AbstractMacroAssembler* masm) - : m_label(masm->m_assembler.labelIgnoringWatchpoints()) - { - } - - bool isSet() const { return m_label.isSet(); } - private: - AssemblerLabel m_label; - }; - - // DataLabelPtr: - // - // A DataLabelPtr is used to refer to a location in the code containing a pointer to be - // patched after the code has been generated. - class DataLabelPtr { - template - friend class AbstractMacroAssembler; - friend class LinkBuffer; - public: - DataLabelPtr() - { - } - - DataLabelPtr(AbstractMacroAssembler* masm) - : m_label(masm->m_assembler.label()) - { - } - - bool isSet() const { return m_label.isSet(); } - - private: - AssemblerLabel m_label; - }; - - // DataLabel32: - // - // A DataLabelPtr is used to refer to a location in the code containing a pointer to be - // patched after the code has been generated. - class DataLabel32 { - template - friend class AbstractMacroAssembler; - friend class LinkBuffer; - public: - DataLabel32() - { - } - - DataLabel32(AbstractMacroAssembler* masm) - : m_label(masm->m_assembler.label()) - { - } - - AssemblerLabel label() const { return m_label; } - - private: - AssemblerLabel m_label; - }; - - // DataLabelCompact: - // - // A DataLabelCompact is used to refer to a location in the code containing a - // compact immediate to be patched after the code has been generated. - class DataLabelCompact { - template - friend class AbstractMacroAssembler; - friend class LinkBuffer; - public: - DataLabelCompact() - { - } - - DataLabelCompact(AbstractMacroAssembler* masm) - : m_label(masm->m_assembler.label()) - { - } - - DataLabelCompact(AssemblerLabel label) - : m_label(label) - { - } - - private: - AssemblerLabel m_label; - }; - - // Call: - // - // A Call object is a reference to a call instruction that has been planted - // into the code buffer - it is typically used to link the call, setting the - // relative offset such that when executed it will call to the desired - // destination. - class Call { - template - friend class AbstractMacroAssembler; - - public: - enum Flags { - None = 0x0, - Linkable = 0x1, - Near = 0x2, - LinkableNear = 0x3, - }; - - Call() - : m_flags(None) - { - } - - Call(AssemblerLabel jmp, Flags flags) - : m_label(jmp) - , m_flags(flags) - { - } - - bool isFlagSet(Flags flag) - { - return m_flags & flag; - } - - static Call fromTailJump(Jump jump) - { - return Call(jump.m_label, Linkable); - } - - AssemblerLabel m_label; - private: - Flags m_flags; - }; - - // Jump: - // - // A jump object is a reference to a jump instruction that has been planted - // into the code buffer - it is typically used to link the jump, setting the - // relative offset such that when executed it will jump to the desired - // destination. - class Jump { - template - friend class AbstractMacroAssembler; - friend class Call; - friend struct DFG::OSRExit; - friend class LinkBuffer; - public: - Jump() - { - } - -#if CPU(ARM_THUMB2) - // Fixme: this information should be stored in the instruction stream, not in the Jump object. - Jump(AssemblerLabel jmp, ARMv7Assembler::JumpType type, ARMv7Assembler::Condition condition = ARMv7Assembler::ConditionInvalid) - : m_label(jmp) - , m_type(type) - , m_condition(condition) - { - } -#elif CPU(SH4) - Jump(AssemblerLabel jmp, SH4Assembler::JumpType type = SH4Assembler::JumpFar) - : m_label(jmp) - , m_type(type) - { - } -#else - Jump(AssemblerLabel jmp) - : m_label(jmp) - { - } -#endif - - Label label() const - { - Label result; - result.m_label = m_label; - return result; - } - - void link(AbstractMacroAssembler* masm) const - { -#if CPU(ARM_THUMB2) - masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type, m_condition); -#elif CPU(SH4) - masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type); -#else - masm->m_assembler.linkJump(m_label, masm->m_assembler.label()); -#endif - } - - void linkTo(Label label, AbstractMacroAssembler* masm) const - { -#if CPU(ARM_THUMB2) - masm->m_assembler.linkJump(m_label, label.m_label, m_type, m_condition); -#else - masm->m_assembler.linkJump(m_label, label.m_label); -#endif - } - - bool isSet() const { return m_label.isSet(); } - - private: - AssemblerLabel m_label; -#if CPU(ARM_THUMB2) - ARMv7Assembler::JumpType m_type; - ARMv7Assembler::Condition m_condition; -#endif -#if CPU(SH4) - SH4Assembler::JumpType m_type; -#endif - }; - - struct PatchableJump { - PatchableJump() - { - } - - explicit PatchableJump(Jump jump) - : m_jump(jump) - { - } - - operator Jump&() { return m_jump; } - - Jump m_jump; - }; - - // JumpList: - // - // A JumpList is a set of Jump objects. - // All jumps in the set will be linked to the same destination. - class JumpList { - friend class LinkBuffer; - - public: - typedef Vector JumpVector; - - JumpList() { } - - JumpList(Jump jump) - { - append(jump); - } - - void link(AbstractMacroAssembler* masm) - { - size_t size = m_jumps.size(); - for (size_t i = 0; i < size; ++i) - m_jumps[i].link(masm); - m_jumps.clear(); - } - - void linkTo(Label label, AbstractMacroAssembler* masm) - { - size_t size = m_jumps.size(); - for (size_t i = 0; i < size; ++i) - m_jumps[i].linkTo(label, masm); - m_jumps.clear(); - } - - void append(Jump jump) - { - m_jumps.append(jump); - } - - void append(const JumpList& other) - { - m_jumps.append(other.m_jumps.begin(), other.m_jumps.size()); - } - - bool empty() - { - return !m_jumps.size(); - } - - void clear() - { - m_jumps.clear(); - } - - const JumpVector& jumps() const { return m_jumps; } - - private: - JumpVector m_jumps; - }; - - - // Section 3: Misc admin methods -#if ENABLE(DFG_JIT) - Label labelIgnoringWatchpoints() - { - Label result; - result.m_label = m_assembler.labelIgnoringWatchpoints(); - return result; - } -#else - Label labelIgnoringWatchpoints() - { - return label(); - } -#endif - - Label label() - { - return Label(this); - } - - void padBeforePatch() - { - // Rely on the fact that asking for a label already does the padding. - (void)label(); - } - - Label watchpointLabel() - { - Label result; - result.m_label = m_assembler.labelForWatchpoint(); - return result; - } - - Label align() - { - m_assembler.align(16); - return Label(this); - } - - template - static ptrdiff_t differenceBetween(T from, U to) - { - return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_label); - } - - static ptrdiff_t differenceBetweenCodePtr(const MacroAssemblerCodePtr& a, const MacroAssemblerCodePtr& b) - { - return reinterpret_cast(b.executableAddress()) - reinterpret_cast(a.executableAddress()); - } - - unsigned debugOffset() { return m_assembler.debugOffset(); } - - ALWAYS_INLINE static void cacheFlush(void* code, size_t size) - { - AssemblerType::cacheFlush(code, size); - } -protected: - AbstractMacroAssembler() - : m_randomSource(cryptographicallyRandomNumber()) - { - } - - AssemblerType m_assembler; - - uint32_t random() - { - return m_randomSource.getUint32(); - } - - WeakRandom m_randomSource; - -#if ENABLE(JIT_CONSTANT_BLINDING) - static bool scratchRegisterForBlinding() { return false; } - static bool shouldBlindForSpecificArch(uint32_t) { return true; } - static bool shouldBlindForSpecificArch(uint64_t) { return true; } -#endif - - friend class LinkBuffer; - friend class RepatchBuffer; - - static void linkJump(void* code, Jump jump, CodeLocationLabel target) - { - AssemblerType::linkJump(code, jump.m_label, target.dataLocation()); - } - - static void linkPointer(void* code, AssemblerLabel label, void* value) - { - AssemblerType::linkPointer(code, label, value); - } - - static void* getLinkerAddress(void* code, AssemblerLabel label) - { - return AssemblerType::getRelocatedAddress(code, label); - } - - static unsigned getLinkerCallReturnOffset(Call call) - { - return AssemblerType::getCallReturnOffset(call.m_label); - } - - static void repatchJump(CodeLocationJump jump, CodeLocationLabel destination) - { - AssemblerType::relinkJump(jump.dataLocation(), destination.dataLocation()); - } - - static void repatchNearCall(CodeLocationNearCall nearCall, CodeLocationLabel destination) - { - AssemblerType::relinkCall(nearCall.dataLocation(), destination.executableAddress()); - } - - static void repatchCompact(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) - { - AssemblerType::repatchCompact(dataLabelCompact.dataLocation(), value); - } - - static void repatchInt32(CodeLocationDataLabel32 dataLabel32, int32_t value) - { - AssemblerType::repatchInt32(dataLabel32.dataLocation(), value); - } - - static void repatchPointer(CodeLocationDataLabelPtr dataLabelPtr, void* value) - { - AssemblerType::repatchPointer(dataLabelPtr.dataLocation(), value); - } - - static void* readPointer(CodeLocationDataLabelPtr dataLabelPtr) - { - return AssemblerType::readPointer(dataLabelPtr.dataLocation()); - } - - static void replaceWithLoad(CodeLocationConvertibleLoad label) - { - AssemblerType::replaceWithLoad(label.dataLocation()); - } - - static void replaceWithAddressComputation(CodeLocationConvertibleLoad label) - { - AssemblerType::replaceWithAddressComputation(label.dataLocation()); - } -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // AbstractMacroAssembler_h diff --git a/3rdparty/masm/assembler/AssemblerBuffer.h b/3rdparty/masm/assembler/AssemblerBuffer.h deleted file mode 100644 index bc52801ba7..0000000000 --- a/3rdparty/masm/assembler/AssemblerBuffer.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef AssemblerBuffer_h -#define AssemblerBuffer_h - -#if ENABLE(ASSEMBLER) - -#include "ExecutableAllocator.h" -#include "JITCompilationEffort.h" -#include "JSGlobalData.h" -#include "stdint.h" -#include -#include -#include -#include - -namespace JSC { - - struct AssemblerLabel { - AssemblerLabel() - : m_offset(std::numeric_limits::max()) - { - } - - explicit AssemblerLabel(uint32_t offset) - : m_offset(offset) - { - } - - bool isSet() const { return (m_offset != std::numeric_limits::max()); } - - AssemblerLabel labelAtOffset(int offset) const - { - return AssemblerLabel(m_offset + offset); - } - - uint32_t m_offset; - }; - - class AssemblerBuffer { - static const int inlineCapacity = 128; - public: - AssemblerBuffer() - : m_storage(inlineCapacity) - , m_buffer(&(*m_storage.begin())) - , m_capacity(inlineCapacity) - , m_index(0) - { - } - - ~AssemblerBuffer() - { - } - - bool isAvailable(int space) - { - return m_index + space <= m_capacity; - } - - void ensureSpace(int space) - { - if (!isAvailable(space)) - grow(); - } - - bool isAligned(int alignment) const - { - return !(m_index & (alignment - 1)); - } - - template - void putIntegral(IntegralType value) - { - ensureSpace(sizeof(IntegralType)); - putIntegralUnchecked(value); - } - - template - void putIntegralUnchecked(IntegralType value) - { - ASSERT(isAvailable(sizeof(IntegralType))); - *reinterpret_cast_ptr(m_buffer + m_index) = value; - m_index += sizeof(IntegralType); - } - - void putByteUnchecked(int8_t value) { putIntegralUnchecked(value); } - void putByte(int8_t value) { putIntegral(value); } - void putShortUnchecked(int16_t value) { putIntegralUnchecked(value); } - void putShort(int16_t value) { putIntegral(value); } - void putIntUnchecked(int32_t value) { putIntegralUnchecked(value); } - void putInt(int32_t value) { putIntegral(value); } - void putInt64Unchecked(int64_t value) { putIntegralUnchecked(value); } - void putInt64(int64_t value) { putIntegral(value); } - - void* data() const - { - return m_buffer; - } - - size_t codeSize() const - { - return m_index; - } - - AssemblerLabel label() const - { - return AssemblerLabel(m_index); - } - - PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) - { - if (!m_index) - return 0; - - RefPtr result = globalData.executableAllocator.allocate(globalData, m_index, ownerUID, effort); - - if (!result) - return 0; - - ExecutableAllocator::makeWritable(result->start(), result->sizeInBytes()); - - memcpy(result->start(), m_buffer, m_index); - - return result.release(); - } - - unsigned debugOffset() { return m_index; } - - protected: - void append(const char* data, int size) - { - if (!isAvailable(size)) - grow(size); - - memcpy(m_buffer + m_index, data, size); - m_index += size; - } - - void grow(int extraCapacity = 0) - { - m_capacity += m_capacity / 2 + extraCapacity; - - m_storage.grow(m_capacity); - m_buffer = &(*m_storage.begin()); - } - - private: - Vector m_storage; - char* m_buffer; - int m_capacity; - int m_index; - }; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // AssemblerBuffer_h diff --git a/3rdparty/masm/assembler/AssemblerBufferWithConstantPool.h b/3rdparty/masm/assembler/AssemblerBufferWithConstantPool.h deleted file mode 100644 index 5377ef0c7a..0000000000 --- a/3rdparty/masm/assembler/AssemblerBufferWithConstantPool.h +++ /dev/null @@ -1,342 +0,0 @@ -/* - * Copyright (C) 2009 University of Szeged - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef AssemblerBufferWithConstantPool_h -#define AssemblerBufferWithConstantPool_h - -#if ENABLE(ASSEMBLER) - -#include "AssemblerBuffer.h" -#include - -#define ASSEMBLER_HAS_CONSTANT_POOL 1 - -namespace JSC { - -/* - On a constant pool 4 or 8 bytes data can be stored. The values can be - constants or addresses. The addresses should be 32 or 64 bits. The constants - should be double-precisions float or integer numbers which are hard to be - encoded as few machine instructions. - - TODO: The pool is desinged to handle both 32 and 64 bits values, but - currently only the 4 bytes constants are implemented and tested. - - The AssemblerBuffer can contain multiple constant pools. Each pool is inserted - into the instruction stream - protected by a jump instruction from the - execution flow. - - The flush mechanism is called when no space remain to insert the next instruction - into the pool. Three values are used to determine when the constant pool itself - have to be inserted into the instruction stream (Assembler Buffer): - - - maxPoolSize: size of the constant pool in bytes, this value cannot be - larger than the maximum offset of a PC relative memory load - - - barrierSize: size of jump instruction in bytes which protects the - constant pool from execution - - - maxInstructionSize: maximum length of a machine instruction in bytes - - There are some callbacks which solve the target architecture specific - address handling: - - - TYPE patchConstantPoolLoad(TYPE load, int value): - patch the 'load' instruction with the index of the constant in the - constant pool and return the patched instruction. - - - void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr): - patch the a PC relative load instruction at 'loadAddr' address with the - final relative offset. The offset can be computed with help of - 'constPoolAddr' (the address of the constant pool) and index of the - constant (which is stored previously in the load instruction itself). - - - TYPE placeConstantPoolBarrier(int size): - return with a constant pool barrier instruction which jumps over the - constant pool. - - The 'put*WithConstant*' functions should be used to place a data into the - constant pool. -*/ - -template -class AssemblerBufferWithConstantPool : public AssemblerBuffer { - typedef SegmentedVector LoadOffsets; - using AssemblerBuffer::putIntegral; - using AssemblerBuffer::putIntegralUnchecked; -public: - typedef struct { - short high; - short low; - } TwoShorts; - - enum { - UniqueConst, - ReusableConst, - UnusedEntry, - }; - - AssemblerBufferWithConstantPool() - : AssemblerBuffer() - , m_numConsts(0) - , m_maxDistance(maxPoolSize) - , m_lastConstDelta(0) - { - m_pool = static_cast(fastMalloc(maxPoolSize)); - m_mask = static_cast(fastMalloc(maxPoolSize / sizeof(uint32_t))); - } - - ~AssemblerBufferWithConstantPool() - { - fastFree(m_mask); - fastFree(m_pool); - } - - void ensureSpace(int space) - { - flushIfNoSpaceFor(space); - AssemblerBuffer::ensureSpace(space); - } - - void ensureSpace(int insnSpace, int constSpace) - { - flushIfNoSpaceFor(insnSpace, constSpace); - AssemblerBuffer::ensureSpace(insnSpace); - } - - void ensureSpaceForAnyInstruction(int amount = 1) - { - flushIfNoSpaceFor(amount * maxInstructionSize, amount * sizeof(uint64_t)); - } - - bool isAligned(int alignment) - { - flushIfNoSpaceFor(alignment); - return AssemblerBuffer::isAligned(alignment); - } - - void putByteUnchecked(int value) - { - AssemblerBuffer::putByteUnchecked(value); - correctDeltas(1); - } - - void putByte(int value) - { - flushIfNoSpaceFor(1); - AssemblerBuffer::putByte(value); - correctDeltas(1); - } - - void putShortUnchecked(int value) - { - AssemblerBuffer::putShortUnchecked(value); - correctDeltas(2); - } - - void putShort(int value) - { - flushIfNoSpaceFor(2); - AssemblerBuffer::putShort(value); - correctDeltas(2); - } - - void putIntUnchecked(int value) - { - AssemblerBuffer::putIntUnchecked(value); - correctDeltas(4); - } - - void putInt(int value) - { - flushIfNoSpaceFor(4); - AssemblerBuffer::putInt(value); - correctDeltas(4); - } - - void putInt64Unchecked(int64_t value) - { - AssemblerBuffer::putInt64Unchecked(value); - correctDeltas(8); - } - - void putIntegral(TwoShorts value) - { - putIntegral(value.high); - putIntegral(value.low); - } - - void putIntegralUnchecked(TwoShorts value) - { - putIntegralUnchecked(value.high); - putIntegralUnchecked(value.low); - } - - PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) - { - flushConstantPool(false); - return AssemblerBuffer::executableCopy(globalData, ownerUID, effort); - } - - void putShortWithConstantInt(uint16_t insn, uint32_t constant, bool isReusable = false) - { - putIntegralWithConstantInt(insn, constant, isReusable); - } - - void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false) - { - putIntegralWithConstantInt(insn, constant, isReusable); - } - - // This flushing mechanism can be called after any unconditional jumps. - void flushWithoutBarrier(bool isForced = false) - { - // Flush if constant pool is more than 60% full to avoid overuse of this function. - if (isForced || 5 * static_cast(m_numConsts) > 3 * maxPoolSize / sizeof(uint32_t)) - flushConstantPool(false); - } - - uint32_t* poolAddress() - { - return m_pool; - } - - int sizeOfConstantPool() - { - return m_numConsts; - } - -private: - void correctDeltas(int insnSize) - { - m_maxDistance -= insnSize; - m_lastConstDelta -= insnSize; - if (m_lastConstDelta < 0) - m_lastConstDelta = 0; - } - - void correctDeltas(int insnSize, int constSize) - { - correctDeltas(insnSize); - - m_maxDistance -= m_lastConstDelta; - m_lastConstDelta = constSize; - } - - template - void putIntegralWithConstantInt(IntegralType insn, uint32_t constant, bool isReusable) - { - if (!m_numConsts) - m_maxDistance = maxPoolSize; - flushIfNoSpaceFor(sizeof(IntegralType), 4); - - m_loadOffsets.append(codeSize()); - if (isReusable) { - for (int i = 0; i < m_numConsts; ++i) { - if (m_mask[i] == ReusableConst && m_pool[i] == constant) { - putIntegral(static_cast(AssemblerType::patchConstantPoolLoad(insn, i))); - correctDeltas(sizeof(IntegralType)); - return; - } - } - } - - m_pool[m_numConsts] = constant; - m_mask[m_numConsts] = static_cast(isReusable ? ReusableConst : UniqueConst); - - putIntegral(static_cast(AssemblerType::patchConstantPoolLoad(insn, m_numConsts))); - ++m_numConsts; - - correctDeltas(sizeof(IntegralType), 4); - } - - void flushConstantPool(bool useBarrier = true) - { - if (m_numConsts == 0) - return; - int alignPool = (codeSize() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1); - - if (alignPool) - alignPool = sizeof(uint64_t) - alignPool; - - // Callback to protect the constant pool from execution - if (useBarrier) - putIntegral(AssemblerType::placeConstantPoolBarrier(m_numConsts * sizeof(uint32_t) + alignPool)); - - if (alignPool) { - if (alignPool & 1) - AssemblerBuffer::putByte(AssemblerType::padForAlign8); - if (alignPool & 2) - AssemblerBuffer::putShort(AssemblerType::padForAlign16); - if (alignPool & 4) - AssemblerBuffer::putInt(AssemblerType::padForAlign32); - } - - int constPoolOffset = codeSize(); - append(reinterpret_cast(m_pool), m_numConsts * sizeof(uint32_t)); - - // Patch each PC relative load - for (LoadOffsets::Iterator iter = m_loadOffsets.begin(); iter != m_loadOffsets.end(); ++iter) { - void* loadAddr = reinterpret_cast(data()) + *iter; - AssemblerType::patchConstantPoolLoad(loadAddr, reinterpret_cast(data()) + constPoolOffset); - } - - m_loadOffsets.clear(); - m_numConsts = 0; - } - - void flushIfNoSpaceFor(int nextInsnSize) - { - if (m_numConsts == 0) - return; - int lastConstDelta = m_lastConstDelta > nextInsnSize ? m_lastConstDelta - nextInsnSize : 0; - if ((m_maxDistance < nextInsnSize + lastConstDelta + barrierSize + (int)sizeof(uint32_t))) - flushConstantPool(); - } - - void flushIfNoSpaceFor(int nextInsnSize, int nextConstSize) - { - if (m_numConsts == 0) - return; - if ((m_maxDistance < nextInsnSize + m_lastConstDelta + nextConstSize + barrierSize + (int)sizeof(uint32_t)) || - (m_numConsts * sizeof(uint32_t) + nextConstSize >= maxPoolSize)) - flushConstantPool(); - } - - uint32_t* m_pool; - char* m_mask; - LoadOffsets m_loadOffsets; - - int m_numConsts; - int m_maxDistance; - int m_lastConstDelta; -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // AssemblerBufferWithConstantPool_h diff --git a/3rdparty/masm/assembler/CodeLocation.h b/3rdparty/masm/assembler/CodeLocation.h deleted file mode 100644 index 86d1f2b755..0000000000 --- a/3rdparty/masm/assembler/CodeLocation.h +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef CodeLocation_h -#define CodeLocation_h - -#include "MacroAssemblerCodeRef.h" - -#if ENABLE(ASSEMBLER) - -namespace JSC { - -class CodeLocationInstruction; -class CodeLocationLabel; -class CodeLocationJump; -class CodeLocationCall; -class CodeLocationNearCall; -class CodeLocationDataLabelCompact; -class CodeLocationDataLabel32; -class CodeLocationDataLabelPtr; -class CodeLocationConvertibleLoad; - -// The CodeLocation* types are all pretty much do-nothing wrappers around -// CodePtr (or MacroAssemblerCodePtr, to give it its full name). These -// classes only exist to provide type-safety when linking and patching code. -// -// The one new piece of functionallity introduced by these classes is the -// ability to create (or put another way, to re-discover) another CodeLocation -// at an offset from one you already know. When patching code to optimize it -// we often want to patch a number of instructions that are short, fixed -// offsets apart. To reduce memory overhead we will only retain a pointer to -// one of the instructions, and we will use the *AtOffset methods provided by -// CodeLocationCommon to find the other points in the code to modify. -class CodeLocationCommon : public MacroAssemblerCodePtr { -public: - CodeLocationInstruction instructionAtOffset(int offset); - CodeLocationLabel labelAtOffset(int offset); - CodeLocationJump jumpAtOffset(int offset); - CodeLocationCall callAtOffset(int offset); - CodeLocationNearCall nearCallAtOffset(int offset); - CodeLocationDataLabelPtr dataLabelPtrAtOffset(int offset); - CodeLocationDataLabel32 dataLabel32AtOffset(int offset); - CodeLocationDataLabelCompact dataLabelCompactAtOffset(int offset); - CodeLocationConvertibleLoad convertibleLoadAtOffset(int offset); - -protected: - CodeLocationCommon() - { - } - - CodeLocationCommon(MacroAssemblerCodePtr location) - : MacroAssemblerCodePtr(location) - { - } -}; - -class CodeLocationInstruction : public CodeLocationCommon { -public: - CodeLocationInstruction() {} - explicit CodeLocationInstruction(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) {} - explicit CodeLocationInstruction(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} -}; - -class CodeLocationLabel : public CodeLocationCommon { -public: - CodeLocationLabel() {} - explicit CodeLocationLabel(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) {} - explicit CodeLocationLabel(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} -}; - -class CodeLocationJump : public CodeLocationCommon { -public: - CodeLocationJump() {} - explicit CodeLocationJump(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) {} - explicit CodeLocationJump(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} -}; - -class CodeLocationCall : public CodeLocationCommon { -public: - CodeLocationCall() {} - explicit CodeLocationCall(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) {} - explicit CodeLocationCall(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} -}; - -class CodeLocationNearCall : public CodeLocationCommon { -public: - CodeLocationNearCall() {} - explicit CodeLocationNearCall(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) {} - explicit CodeLocationNearCall(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} -}; - -class CodeLocationDataLabel32 : public CodeLocationCommon { -public: - CodeLocationDataLabel32() {} - explicit CodeLocationDataLabel32(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) {} - explicit CodeLocationDataLabel32(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} -}; - -class CodeLocationDataLabelCompact : public CodeLocationCommon { -public: - CodeLocationDataLabelCompact() { } - explicit CodeLocationDataLabelCompact(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) { } - explicit CodeLocationDataLabelCompact(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) { } -}; - -class CodeLocationDataLabelPtr : public CodeLocationCommon { -public: - CodeLocationDataLabelPtr() {} - explicit CodeLocationDataLabelPtr(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) {} - explicit CodeLocationDataLabelPtr(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} -}; - -class CodeLocationConvertibleLoad : public CodeLocationCommon { -public: - CodeLocationConvertibleLoad() { } - explicit CodeLocationConvertibleLoad(MacroAssemblerCodePtr location) - : CodeLocationCommon(location) { } - explicit CodeLocationConvertibleLoad(void* location) - : CodeLocationCommon(MacroAssemblerCodePtr(location)) { } -}; - -inline CodeLocationInstruction CodeLocationCommon::instructionAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationInstruction(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationLabel CodeLocationCommon::labelAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationLabel(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationJump CodeLocationCommon::jumpAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationJump(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationCall CodeLocationCommon::callAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationCall(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationNearCall CodeLocationCommon::nearCallAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationNearCall(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationDataLabelPtr CodeLocationCommon::dataLabelPtrAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationDataLabelPtr(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationDataLabel32 CodeLocationCommon::dataLabel32AtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationDataLabel32(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationDataLabelCompact CodeLocationCommon::dataLabelCompactAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationDataLabelCompact(reinterpret_cast(dataLocation()) + offset); -} - -inline CodeLocationConvertibleLoad CodeLocationCommon::convertibleLoadAtOffset(int offset) -{ - ASSERT_VALID_CODE_OFFSET(offset); - return CodeLocationConvertibleLoad(reinterpret_cast(dataLocation()) + offset); -} - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // CodeLocation_h diff --git a/3rdparty/masm/assembler/LinkBuffer.cpp b/3rdparty/masm/assembler/LinkBuffer.cpp deleted file mode 100644 index c269157ba5..0000000000 --- a/3rdparty/masm/assembler/LinkBuffer.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "LinkBuffer.h" - -#if ENABLE(ASSEMBLER) - -#include "Options.h" - -namespace JSC { - -LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithoutDisassembly() -{ - performFinalization(); - - return CodeRef(m_executableMemory); -} - -LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithDisassembly(const char* format, ...) -{ - ASSERT(Options::showDisassembly() || Options::showDFGDisassembly()); - - CodeRef result = finalizeCodeWithoutDisassembly(); - - dataLogF("Generated JIT code for "); - va_list argList; - va_start(argList, format); - WTF::dataLogFV(format, argList); - va_end(argList); - dataLogF(":\n"); - - dataLogF(" Code at [%p, %p):\n", result.code().executableAddress(), static_cast(result.code().executableAddress()) + result.size()); - disassemble(result.code(), m_size, " ", WTF::dataFile()); - - return result; -} - -void LinkBuffer::linkCode(void* ownerUID, JITCompilationEffort effort) -{ - ASSERT(!m_code); -#if !ENABLE(BRANCH_COMPACTION) - m_executableMemory = m_assembler->m_assembler.executableCopy(*m_globalData, ownerUID, effort); - if (!m_executableMemory) - return; - m_code = m_executableMemory->start(); - m_size = m_assembler->m_assembler.codeSize(); - ASSERT(m_code); -#else - m_initialSize = m_assembler->m_assembler.codeSize(); - m_executableMemory = m_globalData->executableAllocator.allocate(*m_globalData, m_initialSize, ownerUID, effort); - if (!m_executableMemory) - return; - m_code = (uint8_t*)m_executableMemory->start(); - ASSERT(m_code); - ExecutableAllocator::makeWritable(m_code, m_initialSize); - uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode(); - uint8_t* outData = reinterpret_cast(m_code); - int readPtr = 0; - int writePtr = 0; - Vector& jumpsToLink = m_assembler->jumpsToLink(); - unsigned jumpCount = jumpsToLink.size(); - for (unsigned i = 0; i < jumpCount; ++i) { - int offset = readPtr - writePtr; - ASSERT(!(offset & 1)); - - // Copy the instructions from the last jump to the current one. - size_t regionSize = jumpsToLink[i].from() - readPtr; - uint16_t* copySource = reinterpret_cast_ptr(inData + readPtr); - uint16_t* copyEnd = reinterpret_cast_ptr(inData + readPtr + regionSize); - uint16_t* copyDst = reinterpret_cast_ptr(outData + writePtr); - ASSERT(!(regionSize % 2)); - ASSERT(!(readPtr % 2)); - ASSERT(!(writePtr % 2)); - while (copySource != copyEnd) - *copyDst++ = *copySource++; - m_assembler->recordLinkOffsets(readPtr, jumpsToLink[i].from(), offset); - readPtr += regionSize; - writePtr += regionSize; - - // Calculate absolute address of the jump target, in the case of backwards - // branches we need to be precise, forward branches we are pessimistic - const uint8_t* target; - if (jumpsToLink[i].to() >= jumpsToLink[i].from()) - target = outData + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far - else - target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); - - JumpLinkType jumpLinkType = m_assembler->computeJumpType(jumpsToLink[i], outData + writePtr, target); - // Compact branch if we can... - if (m_assembler->canCompact(jumpsToLink[i].type())) { - // Step back in the write stream - int32_t delta = m_assembler->jumpSizeDelta(jumpsToLink[i].type(), jumpLinkType); - if (delta) { - writePtr -= delta; - m_assembler->recordLinkOffsets(jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr); - } - } - jumpsToLink[i].setFrom(writePtr); - } - // Copy everything after the last jump - memcpy(outData + writePtr, inData + readPtr, m_initialSize - readPtr); - m_assembler->recordLinkOffsets(readPtr, m_initialSize, readPtr - writePtr); - - for (unsigned i = 0; i < jumpCount; ++i) { - uint8_t* location = outData + jumpsToLink[i].from(); - uint8_t* target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); - m_assembler->link(jumpsToLink[i], location, target); - } - - jumpsToLink.clear(); - m_size = writePtr + m_initialSize - readPtr; - m_executableMemory->shrink(m_size); - -#if DUMP_LINK_STATISTICS - dumpLinkStatistics(m_code, m_initialSize, m_size); -#endif -#if DUMP_CODE - dumpCode(m_code, m_size); -#endif -#endif -} - -void LinkBuffer::performFinalization() -{ -#ifndef NDEBUG - ASSERT(!m_completed); - ASSERT(isValid()); - m_completed = true; -#endif - -#if ENABLE(BRANCH_COMPACTION) - ExecutableAllocator::makeExecutable(code(), m_initialSize); -#else - ExecutableAllocator::makeExecutable(code(), m_size); -#endif - MacroAssembler::cacheFlush(code(), m_size); -} - -#if DUMP_LINK_STATISTICS -void LinkBuffer::dumpLinkStatistics(void* code, size_t initializeSize, size_t finalSize) -{ - static unsigned linkCount = 0; - static unsigned totalInitialSize = 0; - static unsigned totalFinalSize = 0; - linkCount++; - totalInitialSize += initialSize; - totalFinalSize += finalSize; - dataLogF("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", - code, static_cast(initialSize), static_cast(finalSize), - static_cast(initialSize - finalSize), - 100.0 * (initialSize - finalSize) / initialSize); - dataLogF("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", - linkCount, totalInitialSize, totalFinalSize, totalInitialSize - totalFinalSize, - 100.0 * (totalInitialSize - totalFinalSize) / totalInitialSize); -} -#endif - -#if DUMP_CODE -void LinkBuffer::dumpCode(void* code, size_t size) -{ -#if CPU(ARM_THUMB2) - // Dump the generated code in an asm file format that can be assembled and then disassembled - // for debugging purposes. For example, save this output as jit.s: - // gcc -arch armv7 -c jit.s - // otool -tv jit.o - static unsigned codeCount = 0; - unsigned short* tcode = static_cast(code); - size_t tsize = size / sizeof(short); - char nameBuf[128]; - snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); - dataLogF("\t.syntax unified\n" - "\t.section\t__TEXT,__text,regular,pure_instructions\n" - "\t.globl\t%s\n" - "\t.align 2\n" - "\t.code 16\n" - "\t.thumb_func\t%s\n" - "# %p\n" - "%s:\n", nameBuf, nameBuf, code, nameBuf); - - for (unsigned i = 0; i < tsize; i++) - dataLogF("\t.short\t0x%x\n", tcode[i]); -#elif CPU(ARM_TRADITIONAL) - // gcc -c jit.s - // objdump -D jit.o - static unsigned codeCount = 0; - unsigned int* tcode = static_cast(code); - size_t tsize = size / sizeof(unsigned int); - char nameBuf[128]; - snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); - dataLogF("\t.globl\t%s\n" - "\t.align 4\n" - "\t.code 32\n" - "\t.text\n" - "# %p\n" - "%s:\n", nameBuf, code, nameBuf); - - for (unsigned i = 0; i < tsize; i++) - dataLogF("\t.long\t0x%x\n", tcode[i]); -#endif -} -#endif - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - - diff --git a/3rdparty/masm/assembler/LinkBuffer.h b/3rdparty/masm/assembler/LinkBuffer.h deleted file mode 100644 index e1882433c1..0000000000 --- a/3rdparty/masm/assembler/LinkBuffer.h +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright (C) 2009, 2010, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef LinkBuffer_h -#define LinkBuffer_h - -#if ENABLE(ASSEMBLER) - -#define DUMP_LINK_STATISTICS 0 -#define DUMP_CODE 0 - -#define GLOBAL_THUNK_ID reinterpret_cast(static_cast(-1)) -#define REGEXP_CODE_ID reinterpret_cast(static_cast(-2)) - -#include "JITCompilationEffort.h" -#include "MacroAssembler.h" -#include -#include - -namespace JSC { - -class JSGlobalData; - -// LinkBuffer: -// -// This class assists in linking code generated by the macro assembler, once code generation -// has been completed, and the code has been copied to is final location in memory. At this -// time pointers to labels within the code may be resolved, and relative offsets to external -// addresses may be fixed. -// -// Specifically: -// * Jump objects may be linked to external targets, -// * The address of Jump objects may taken, such that it can later be relinked. -// * The return address of a Call may be acquired. -// * The address of a Label pointing into the code may be resolved. -// * The value referenced by a DataLabel may be set. -// -class LinkBuffer { - WTF_MAKE_NONCOPYABLE(LinkBuffer); - typedef MacroAssemblerCodeRef CodeRef; - typedef MacroAssemblerCodePtr CodePtr; - typedef MacroAssembler::Label Label; - typedef MacroAssembler::Jump Jump; - typedef MacroAssembler::PatchableJump PatchableJump; - typedef MacroAssembler::JumpList JumpList; - typedef MacroAssembler::Call Call; - typedef MacroAssembler::DataLabelCompact DataLabelCompact; - typedef MacroAssembler::DataLabel32 DataLabel32; - typedef MacroAssembler::DataLabelPtr DataLabelPtr; - typedef MacroAssembler::ConvertibleLoadLabel ConvertibleLoadLabel; -#if ENABLE(BRANCH_COMPACTION) - typedef MacroAssembler::LinkRecord LinkRecord; - typedef MacroAssembler::JumpLinkType JumpLinkType; -#endif - -public: - LinkBuffer(JSGlobalData& globalData, MacroAssembler* masm, void* ownerUID, JITCompilationEffort effort = JITCompilationMustSucceed) - : m_size(0) -#if ENABLE(BRANCH_COMPACTION) - , m_initialSize(0) -#endif - , m_code(0) - , m_assembler(masm) - , m_globalData(&globalData) -#ifndef NDEBUG - , m_completed(false) - , m_effort(effort) -#endif - { - linkCode(ownerUID, effort); - } - - ~LinkBuffer() - { - ASSERT(m_completed || (!m_executableMemory && m_effort == JITCompilationCanFail)); - } - - bool didFailToAllocate() const - { - return !m_executableMemory; - } - - bool isValid() const - { - return !didFailToAllocate(); - } - - // These methods are used to link or set values at code generation time. - - void link(Call call, FunctionPtr function) - { - ASSERT(call.isFlagSet(Call::Linkable)); - call.m_label = applyOffset(call.m_label); - MacroAssembler::linkCall(code(), call, function); - } - - void link(Jump jump, CodeLocationLabel label) - { - jump.m_label = applyOffset(jump.m_label); - MacroAssembler::linkJump(code(), jump, label); - } - - void link(JumpList list, CodeLocationLabel label) - { - for (unsigned i = 0; i < list.m_jumps.size(); ++i) - link(list.m_jumps[i], label); - } - - void patch(DataLabelPtr label, void* value) - { - AssemblerLabel target = applyOffset(label.m_label); - MacroAssembler::linkPointer(code(), target, value); - } - - void patch(DataLabelPtr label, CodeLocationLabel value) - { - AssemblerLabel target = applyOffset(label.m_label); - MacroAssembler::linkPointer(code(), target, value.executableAddress()); - } - - // These methods are used to obtain handles to allow the code to be relinked / repatched later. - - CodeLocationCall locationOf(Call call) - { - ASSERT(call.isFlagSet(Call::Linkable)); - ASSERT(!call.isFlagSet(Call::Near)); - return CodeLocationCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_label))); - } - - CodeLocationNearCall locationOfNearCall(Call call) - { - ASSERT(call.isFlagSet(Call::Linkable)); - ASSERT(call.isFlagSet(Call::Near)); - return CodeLocationNearCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_label))); - } - - CodeLocationLabel locationOf(PatchableJump jump) - { - return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(jump.m_jump.m_label))); - } - - CodeLocationLabel locationOf(Label label) - { - return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); - } - - CodeLocationDataLabelPtr locationOf(DataLabelPtr label) - { - return CodeLocationDataLabelPtr(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); - } - - CodeLocationDataLabel32 locationOf(DataLabel32 label) - { - return CodeLocationDataLabel32(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); - } - - CodeLocationDataLabelCompact locationOf(DataLabelCompact label) - { - return CodeLocationDataLabelCompact(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); - } - - CodeLocationConvertibleLoad locationOf(ConvertibleLoadLabel label) - { - return CodeLocationConvertibleLoad(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); - } - - // This method obtains the return address of the call, given as an offset from - // the start of the code. - unsigned returnAddressOffset(Call call) - { - call.m_label = applyOffset(call.m_label); - return MacroAssembler::getLinkerCallReturnOffset(call); - } - - uint32_t offsetOf(Label label) - { - return applyOffset(label.m_label).m_offset; - } - - // Upon completion of all patching 'FINALIZE_CODE()' should be called once to - // complete generation of the code. Alternatively, call - // finalizeCodeWithoutDisassembly() directly if you have your own way of - // displaying disassembly. - - CodeRef finalizeCodeWithoutDisassembly(); - CodeRef finalizeCodeWithDisassembly(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); - - CodePtr trampolineAt(Label label) - { - return CodePtr(MacroAssembler::AssemblerType_T::getRelocatedAddress(code(), applyOffset(label.m_label))); - } - - void* debugAddress() - { - return m_code; - } - - size_t debugSize() - { - return m_size; - } - -private: - template T applyOffset(T src) - { -#if ENABLE(BRANCH_COMPACTION) - src.m_offset -= m_assembler->executableOffsetFor(src.m_offset); -#endif - return src; - } - - // Keep this private! - the underlying code should only be obtained externally via finalizeCode(). - void* code() - { - return m_code; - } - - void linkCode(void* ownerUID, JITCompilationEffort); - - void performFinalization(); - -#if DUMP_LINK_STATISTICS - static void dumpLinkStatistics(void* code, size_t initialSize, size_t finalSize); -#endif - -#if DUMP_CODE - static void dumpCode(void* code, size_t); -#endif - - RefPtr m_executableMemory; - size_t m_size; -#if ENABLE(BRANCH_COMPACTION) - size_t m_initialSize; -#endif - void* m_code; - MacroAssembler* m_assembler; - JSGlobalData* m_globalData; -#ifndef NDEBUG - bool m_completed; - JITCompilationEffort m_effort; -#endif -}; - -#define FINALIZE_CODE_IF(condition, linkBufferReference, dataLogFArgumentsForHeading) \ - (UNLIKELY((condition)) \ - ? ((linkBufferReference).finalizeCodeWithDisassembly dataLogFArgumentsForHeading) \ - : (linkBufferReference).finalizeCodeWithoutDisassembly()) - -// Use this to finalize code, like so: -// -// CodeRef code = FINALIZE_CODE(linkBuffer, ("my super thingy number %d", number)); -// -// Which, in disassembly mode, will print: -// -// Generated JIT code for my super thingy number 42: -// Code at [0x123456, 0x234567]: -// 0x123456: mov $0, 0 -// 0x12345a: ret -// -// ... and so on. -// -// Note that the dataLogFArgumentsForHeading are only evaluated when showDisassembly -// is true, so you can hide expensive disassembly-only computations inside there. - -#define FINALIZE_CODE(linkBufferReference, dataLogFArgumentsForHeading) \ - FINALIZE_CODE_IF(Options::showDisassembly(), linkBufferReference, dataLogFArgumentsForHeading) - -#define FINALIZE_DFG_CODE(linkBufferReference, dataLogFArgumentsForHeading) \ - FINALIZE_CODE_IF((Options::showDisassembly() || Options::showDFGDisassembly()), linkBufferReference, dataLogFArgumentsForHeading) - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // LinkBuffer_h diff --git a/3rdparty/masm/assembler/MIPSAssembler.h b/3rdparty/masm/assembler/MIPSAssembler.h deleted file mode 100644 index 026f87e52a..0000000000 --- a/3rdparty/masm/assembler/MIPSAssembler.h +++ /dev/null @@ -1,1032 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * Copyright (C) 2009 University of Szeged - * All rights reserved. - * Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY MIPS TECHNOLOGIES, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MIPS TECHNOLOGIES, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MIPSAssembler_h -#define MIPSAssembler_h - -#if ENABLE(ASSEMBLER) && CPU(MIPS) - -#include "AssemblerBuffer.h" -#include "JITCompilationEffort.h" -#include -#include - -namespace JSC { - -typedef uint32_t MIPSWord; - -namespace MIPSRegisters { -typedef enum { - r0 = 0, - r1, - r2, - r3, - r4, - r5, - r6, - r7, - r8, - r9, - r10, - r11, - r12, - r13, - r14, - r15, - r16, - r17, - r18, - r19, - r20, - r21, - r22, - r23, - r24, - r25, - r26, - r27, - r28, - r29, - r30, - r31, - zero = r0, - at = r1, - v0 = r2, - v1 = r3, - a0 = r4, - a1 = r5, - a2 = r6, - a3 = r7, - t0 = r8, - t1 = r9, - t2 = r10, - t3 = r11, - t4 = r12, - t5 = r13, - t6 = r14, - t7 = r15, - s0 = r16, - s1 = r17, - s2 = r18, - s3 = r19, - s4 = r20, - s5 = r21, - s6 = r22, - s7 = r23, - t8 = r24, - t9 = r25, - k0 = r26, - k1 = r27, - gp = r28, - sp = r29, - fp = r30, - ra = r31 -} RegisterID; - -typedef enum { - f0, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - f18, - f19, - f20, - f21, - f22, - f23, - f24, - f25, - f26, - f27, - f28, - f29, - f30, - f31 -} FPRegisterID; - -} // namespace MIPSRegisters - -class MIPSAssembler { -public: - typedef MIPSRegisters::RegisterID RegisterID; - typedef MIPSRegisters::FPRegisterID FPRegisterID; - typedef SegmentedVector Jumps; - - MIPSAssembler() - { - } - - // MIPS instruction opcode field position - enum { - OP_SH_RD = 11, - OP_SH_RT = 16, - OP_SH_RS = 21, - OP_SH_SHAMT = 6, - OP_SH_CODE = 16, - OP_SH_FD = 6, - OP_SH_FS = 11, - OP_SH_FT = 16 - }; - - void emitInst(MIPSWord op) - { - void* oldBase = m_buffer.data(); - - m_buffer.putInt(op); - - void* newBase = m_buffer.data(); - if (oldBase != newBase) - relocateJumps(oldBase, newBase); - } - - void nop() - { - emitInst(0x00000000); - } - - /* Need to insert one load data delay nop for mips1. */ - void loadDelayNop() - { -#if WTF_MIPS_ISA(1) - nop(); -#endif - } - - /* Need to insert one coprocessor access delay nop for mips1. */ - void copDelayNop() - { -#if WTF_MIPS_ISA(1) - nop(); -#endif - } - - void move(RegisterID rd, RegisterID rs) - { - /* addu */ - emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS)); - } - - /* Set an immediate value to a register. This may generate 1 or 2 - instructions. */ - void li(RegisterID dest, int imm) - { - if (imm >= -32768 && imm <= 32767) - addiu(dest, MIPSRegisters::zero, imm); - else if (imm >= 0 && imm < 65536) - ori(dest, MIPSRegisters::zero, imm); - else { - lui(dest, imm >> 16); - if (imm & 0xffff) - ori(dest, dest, imm); - } - } - - void lui(RegisterID rt, int imm) - { - emitInst(0x3c000000 | (rt << OP_SH_RT) | (imm & 0xffff)); - } - - void addiu(RegisterID rt, RegisterID rs, int imm) - { - emitInst(0x24000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); - } - - void addu(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void subu(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x00000023 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void mult(RegisterID rs, RegisterID rt) - { - emitInst(0x00000018 | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void div(RegisterID rs, RegisterID rt) - { - emitInst(0x0000001a | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void mfhi(RegisterID rd) - { - emitInst(0x00000010 | (rd << OP_SH_RD)); - } - - void mflo(RegisterID rd) - { - emitInst(0x00000012 | (rd << OP_SH_RD)); - } - - void mul(RegisterID rd, RegisterID rs, RegisterID rt) - { -#if WTF_MIPS_ISA_AT_LEAST(32) - emitInst(0x70000002 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); -#else - mult(rs, rt); - mflo(rd); -#endif - } - - void andInsn(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x00000024 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void andi(RegisterID rt, RegisterID rs, int imm) - { - emitInst(0x30000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); - } - - void nor(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x00000027 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void orInsn(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x00000025 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void ori(RegisterID rt, RegisterID rs, int imm) - { - emitInst(0x34000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); - } - - void xorInsn(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x00000026 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void xori(RegisterID rt, RegisterID rs, int imm) - { - emitInst(0x38000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); - } - - void slt(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x0000002a | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void sltu(RegisterID rd, RegisterID rs, RegisterID rt) - { - emitInst(0x0000002b | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); - } - - void sltiu(RegisterID rt, RegisterID rs, int imm) - { - emitInst(0x2c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); - } - - void sll(RegisterID rd, RegisterID rt, int shamt) - { - emitInst(0x00000000 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); - } - - void sllv(RegisterID rd, RegisterID rt, int rs) - { - emitInst(0x00000004 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); - } - - void sra(RegisterID rd, RegisterID rt, int shamt) - { - emitInst(0x00000003 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); - } - - void srav(RegisterID rd, RegisterID rt, RegisterID rs) - { - emitInst(0x00000007 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); - } - - void srl(RegisterID rd, RegisterID rt, int shamt) - { - emitInst(0x00000002 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); - } - - void srlv(RegisterID rd, RegisterID rt, RegisterID rs) - { - emitInst(0x00000006 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); - } - - void lb(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0x80000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - loadDelayNop(); - } - - void lbu(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0x90000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - loadDelayNop(); - } - - void lw(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0x8c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - loadDelayNop(); - } - - void lwl(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0x88000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - loadDelayNop(); - } - - void lwr(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0x98000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - loadDelayNop(); - } - - void lh(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0x84000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - loadDelayNop(); - } - - void lhu(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0x94000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - loadDelayNop(); - } - - void sb(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0xa0000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - } - - void sh(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0xa4000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - } - - void sw(RegisterID rt, RegisterID rs, int offset) - { - emitInst(0xac000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); - } - - void jr(RegisterID rs) - { - emitInst(0x00000008 | (rs << OP_SH_RS)); - } - - void jalr(RegisterID rs) - { - emitInst(0x0000f809 | (rs << OP_SH_RS)); - } - - void jal() - { - emitInst(0x0c000000); - } - - void bkpt() - { - int value = 512; /* BRK_BUG */ - emitInst(0x0000000d | ((value & 0x3ff) << OP_SH_CODE)); - } - - void bgez(RegisterID rs, int imm) - { - emitInst(0x04010000 | (rs << OP_SH_RS) | (imm & 0xffff)); - } - - void bltz(RegisterID rs, int imm) - { - emitInst(0x04000000 | (rs << OP_SH_RS) | (imm & 0xffff)); - } - - void beq(RegisterID rs, RegisterID rt, int imm) - { - emitInst(0x10000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff)); - } - - void bne(RegisterID rs, RegisterID rt, int imm) - { - emitInst(0x14000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff)); - } - - void bc1t() - { - emitInst(0x45010000); - } - - void bc1f() - { - emitInst(0x45000000); - } - - void appendJump() - { - m_jumps.append(m_buffer.label()); - } - - void addd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200000 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - } - - void subd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200001 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - } - - void muld(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200002 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - } - - void divd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200003 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - } - - void lwc1(FPRegisterID ft, RegisterID rs, int offset) - { - emitInst(0xc4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); - copDelayNop(); - } - - void ldc1(FPRegisterID ft, RegisterID rs, int offset) - { - emitInst(0xd4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); - } - - void swc1(FPRegisterID ft, RegisterID rs, int offset) - { - emitInst(0xe4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); - } - - void sdc1(FPRegisterID ft, RegisterID rs, int offset) - { - emitInst(0xf4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); - } - - void mtc1(RegisterID rt, FPRegisterID fs) - { - emitInst(0x44800000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); - copDelayNop(); - } - - void mthc1(RegisterID rt, FPRegisterID fs) - { - emitInst(0x44e00000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); - copDelayNop(); - } - - void mfc1(RegisterID rt, FPRegisterID fs) - { - emitInst(0x44000000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); - copDelayNop(); - } - - void sqrtd(FPRegisterID fd, FPRegisterID fs) - { - emitInst(0x46200004 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); - } - - void truncwd(FPRegisterID fd, FPRegisterID fs) - { - emitInst(0x4620000d | (fd << OP_SH_FD) | (fs << OP_SH_FS)); - } - - void cvtdw(FPRegisterID fd, FPRegisterID fs) - { - emitInst(0x46800021 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); - } - - void cvtds(FPRegisterID fd, FPRegisterID fs) - { - emitInst(0x46000021 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); - } - - void cvtwd(FPRegisterID fd, FPRegisterID fs) - { - emitInst(0x46200024 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); - } - - void cvtsd(FPRegisterID fd, FPRegisterID fs) - { - emitInst(0x46200020 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); - } - - void ceqd(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200032 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void cngtd(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x4620003f | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void cnged(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x4620003d | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void cltd(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x4620003c | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void cled(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x4620003e | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void cueqd(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200033 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void coled(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200036 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void coltd(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200034 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void culed(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200037 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - void cultd(FPRegisterID fs, FPRegisterID ft) - { - emitInst(0x46200035 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); - copDelayNop(); - } - - // General helpers - - AssemblerLabel labelIgnoringWatchpoints() - { - return m_buffer.label(); - } - - AssemblerLabel label() - { - return m_buffer.label(); - } - - AssemblerLabel align(int alignment) - { - while (!m_buffer.isAligned(alignment)) - bkpt(); - - return label(); - } - - static void* getRelocatedAddress(void* code, AssemblerLabel label) - { - return reinterpret_cast(reinterpret_cast(code) + label.m_offset); - } - - static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) - { - return b.m_offset - a.m_offset; - } - - // Assembler admin methods: - - size_t codeSize() const - { - return m_buffer.codeSize(); - } - - PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) - { - RefPtr result = m_buffer.executableCopy(globalData, ownerUID, effort); - if (!result) - return 0; - - relocateJumps(m_buffer.data(), result->start()); - return result.release(); - } - - unsigned debugOffset() { return m_buffer.debugOffset(); } - - // Assembly helpers for moving data between fp and registers. - void vmov(RegisterID rd1, RegisterID rd2, FPRegisterID rn) - { - mfc1(rd1, rn); - mfc1(rd2, FPRegisterID(rn + 1)); - } - - void vmov(FPRegisterID rd, RegisterID rn1, RegisterID rn2) - { - mtc1(rn1, rd); - mtc1(rn2, FPRegisterID(rd + 1)); - } - - static unsigned getCallReturnOffset(AssemblerLabel call) - { - // The return address is after a call and a delay slot instruction - return call.m_offset; - } - - // Linking & patching: - // - // 'link' and 'patch' methods are for use on unprotected code - such as the code - // within the AssemblerBuffer, and code being patched by the patch buffer. Once - // code has been finalized it is (platform support permitting) within a non- - // writable region of memory; to modify the code in an execute-only execuable - // pool the 'repatch' and 'relink' methods should be used. - - void linkJump(AssemblerLabel from, AssemblerLabel to) - { - ASSERT(to.isSet()); - ASSERT(from.isSet()); - MIPSWord* insn = reinterpret_cast(reinterpret_cast(m_buffer.data()) + from.m_offset); - MIPSWord* toPos = reinterpret_cast(reinterpret_cast(m_buffer.data()) + to.m_offset); - - ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5))); - insn = insn - 6; - linkWithOffset(insn, toPos); - } - - static void linkJump(void* code, AssemblerLabel from, void* to) - { - ASSERT(from.isSet()); - MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); - - ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5))); - insn = insn - 6; - linkWithOffset(insn, to); - } - - static void linkCall(void* code, AssemblerLabel from, void* to) - { - MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); - linkCallInternal(insn, to); - } - - static void linkPointer(void* code, AssemblerLabel from, void* to) - { - MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); - ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui - *insn = (*insn & 0xffff0000) | ((reinterpret_cast(to) >> 16) & 0xffff); - insn++; - ASSERT((*insn & 0xfc000000) == 0x34000000); // ori - *insn = (*insn & 0xffff0000) | (reinterpret_cast(to) & 0xffff); - } - - static void relinkJump(void* from, void* to) - { - MIPSWord* insn = reinterpret_cast(from); - - ASSERT(!(*(insn - 1)) && !(*(insn - 5))); - insn = insn - 6; - int flushSize = linkWithOffset(insn, to); - - cacheFlush(insn, flushSize); - } - - static void relinkCall(void* from, void* to) - { - void* start; - int size = linkCallInternal(from, to); - if (size == sizeof(MIPSWord)) - start = reinterpret_cast(reinterpret_cast(from) - 2 * sizeof(MIPSWord)); - else - start = reinterpret_cast(reinterpret_cast(from) - 4 * sizeof(MIPSWord)); - - cacheFlush(start, size); - } - - static void repatchInt32(void* from, int32_t to) - { - MIPSWord* insn = reinterpret_cast(from); - ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui - *insn = (*insn & 0xffff0000) | ((to >> 16) & 0xffff); - insn++; - ASSERT((*insn & 0xfc000000) == 0x34000000); // ori - *insn = (*insn & 0xffff0000) | (to & 0xffff); - insn--; - cacheFlush(insn, 2 * sizeof(MIPSWord)); - } - - static int32_t readInt32(void* from) - { - MIPSWord* insn = reinterpret_cast(from); - ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui - int32_t result = (*insn & 0x0000ffff) << 16; - insn++; - ASSERT((*insn & 0xfc000000) == 0x34000000); // ori - result |= *insn & 0x0000ffff; - return result; - } - - static void repatchCompact(void* where, int32_t value) - { - repatchInt32(where, value); - } - - static void repatchPointer(void* from, void* to) - { - repatchInt32(from, reinterpret_cast(to)); - } - - static void* readPointer(void* from) - { - return reinterpret_cast(readInt32(from)); - } - - static void* readCallTarget(void* from) - { - MIPSWord* insn = reinterpret_cast(from); - insn -= 4; - ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui - int32_t result = (*insn & 0x0000ffff) << 16; - insn++; - ASSERT((*insn & 0xfc000000) == 0x34000000); // ori - result |= *insn & 0x0000ffff; - return reinterpret_cast(result); - } - - static void cacheFlush(void* code, size_t size) - { -#if GCC_VERSION_AT_LEAST(4, 3, 0) -#if WTF_MIPS_ISA_REV(2) && !GCC_VERSION_AT_LEAST(4, 4, 3) - int lineSize; - asm("rdhwr %0, $1" : "=r" (lineSize)); - // - // Modify "start" and "end" to avoid GCC 4.3.0-4.4.2 bug in - // mips_expand_synci_loop that may execute synci one more time. - // "start" points to the fisrt byte of the cache line. - // "end" points to the last byte of the line before the last cache line. - // Because size is always a multiple of 4, this is safe to set - // "end" to the last byte. - // - intptr_t start = reinterpret_cast(code) & (-lineSize); - intptr_t end = ((reinterpret_cast(code) + size - 1) & (-lineSize)) - 1; - __builtin___clear_cache(reinterpret_cast(start), reinterpret_cast(end)); -#else - intptr_t end = reinterpret_cast(code) + size; - __builtin___clear_cache(reinterpret_cast(code), reinterpret_cast(end)); -#endif -#else - _flush_cache(reinterpret_cast(code), size, BCACHE); -#endif - } - - static void revertJumpToMove(void* instructionStart, RegisterID rt, int imm) - { - MIPSWord* insn = static_cast(instructionStart) + 1; - ASSERT((*insn & 0xfc000000) == 0x34000000); - *insn = (*insn & 0xfc1f0000) | (imm & 0xffff); - cacheFlush(insn, sizeof(MIPSWord)); - } - - static void replaceWithJump(void* instructionStart, void* to) - { - MIPSWord* instruction = reinterpret_cast(instructionStart); - intptr_t jumpTo = reinterpret_cast(to); - - // lui - instruction[0] = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((jumpTo >> 16) & 0xffff); - // ori - instruction[1] = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (jumpTo & 0xffff); - // jr - instruction[2] = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); - // nop - instruction[3] = 0x0; - - cacheFlush(instruction, sizeof(MIPSWord) * 4); - } - - static void replaceWithLoad(void* instructionStart) - { - MIPSWord* insn = reinterpret_cast(instructionStart); - ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui - insn++; - ASSERT((*insn & 0xfc0007ff) == 0x00000021); // addu - insn++; - *insn = 0x8c000000 | ((*insn) & 0x3ffffff); // lw - cacheFlush(insn, 4); - } - - static void replaceWithAddressComputation(void* instructionStart) - { - MIPSWord* insn = reinterpret_cast(instructionStart); - ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui - insn++; - ASSERT((*insn & 0xfc0007ff) == 0x00000021); // addu - insn++; - *insn = 0x24000000 | ((*insn) & 0x3ffffff); // addiu - cacheFlush(insn, 4); - } - -private: - /* Update each jump in the buffer of newBase. */ - void relocateJumps(void* oldBase, void* newBase) - { - // Check each jump - for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) { - int pos = iter->m_offset; - MIPSWord* insn = reinterpret_cast(reinterpret_cast(newBase) + pos); - insn = insn + 2; - // Need to make sure we have 5 valid instructions after pos - if ((unsigned)pos >= m_buffer.codeSize() - 5 * sizeof(MIPSWord)) - continue; - - if ((*insn & 0xfc000000) == 0x08000000) { // j - int offset = *insn & 0x03ffffff; - int oldInsnAddress = (int)insn - (int)newBase + (int)oldBase; - int topFourBits = (oldInsnAddress + 4) >> 28; - int oldTargetAddress = (topFourBits << 28) | (offset << 2); - int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase; - int newInsnAddress = (int)insn; - if (((newInsnAddress + 4) >> 28) == (newTargetAddress >> 28)) - *insn = 0x08000000 | ((newTargetAddress >> 2) & 0x3ffffff); - else { - /* lui */ - *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); - /* ori */ - *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); - /* jr */ - *(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); - } - } else if ((*insn & 0xffe00000) == 0x3c000000) { // lui - int high = (*insn & 0xffff) << 16; - int low = *(insn + 1) & 0xffff; - int oldTargetAddress = high | low; - int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase; - /* lui */ - *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); - /* ori */ - *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); - } - } - } - - static int linkWithOffset(MIPSWord* insn, void* to) - { - ASSERT((*insn & 0xfc000000) == 0x10000000 // beq - || (*insn & 0xfc000000) == 0x14000000 // bne - || (*insn & 0xffff0000) == 0x45010000 // bc1t - || (*insn & 0xffff0000) == 0x45000000); // bc1f - intptr_t diff = (reinterpret_cast(to) - reinterpret_cast(insn) - 4) >> 2; - - if (diff < -32768 || diff > 32767 || *(insn + 2) != 0x10000003) { - /* - Convert the sequence: - beq $2, $3, target - nop - b 1f - nop - nop - nop - 1: - - to the new sequence if possible: - bne $2, $3, 1f - nop - j target - nop - nop - nop - 1: - - OR to the new sequence: - bne $2, $3, 1f - nop - lui $25, target >> 16 - ori $25, $25, target & 0xffff - jr $25 - nop - 1: - - Note: beq/bne/bc1t/bc1f are converted to bne/beq/bc1f/bc1t. - */ - - if (*(insn + 2) == 0x10000003) { - if ((*insn & 0xfc000000) == 0x10000000) // beq - *insn = (*insn & 0x03ff0000) | 0x14000005; // bne - else if ((*insn & 0xfc000000) == 0x14000000) // bne - *insn = (*insn & 0x03ff0000) | 0x10000005; // beq - else if ((*insn & 0xffff0000) == 0x45010000) // bc1t - *insn = 0x45000005; // bc1f - else if ((*insn & 0xffff0000) == 0x45000000) // bc1f - *insn = 0x45010005; // bc1t - else - ASSERT(0); - } - - insn = insn + 2; - if ((reinterpret_cast(insn) + 4) >> 28 - == reinterpret_cast(to) >> 28) { - *insn = 0x08000000 | ((reinterpret_cast(to) >> 2) & 0x3ffffff); - *(insn + 1) = 0; - return 4 * sizeof(MIPSWord); - } - - intptr_t newTargetAddress = reinterpret_cast(to); - /* lui */ - *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); - /* ori */ - *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); - /* jr */ - *(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); - return 5 * sizeof(MIPSWord); - } - - *insn = (*insn & 0xffff0000) | (diff & 0xffff); - return sizeof(MIPSWord); - } - - static int linkCallInternal(void* from, void* to) - { - MIPSWord* insn = reinterpret_cast(from); - insn = insn - 4; - - if ((*(insn + 2) & 0xfc000000) == 0x0c000000) { // jal - if ((reinterpret_cast(from) - 4) >> 28 - == reinterpret_cast(to) >> 28) { - *(insn + 2) = 0x0c000000 | ((reinterpret_cast(to) >> 2) & 0x3ffffff); - return sizeof(MIPSWord); - } - - /* lui $25, (to >> 16) & 0xffff */ - *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((reinterpret_cast(to) >> 16) & 0xffff); - /* ori $25, $25, to & 0xffff */ - *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (reinterpret_cast(to) & 0xffff); - /* jalr $25 */ - *(insn + 2) = 0x0000f809 | (MIPSRegisters::t9 << OP_SH_RS); - return 3 * sizeof(MIPSWord); - } - - ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui - ASSERT((*(insn + 1) & 0xfc000000) == 0x34000000); // ori - - /* lui */ - *insn = (*insn & 0xffff0000) | ((reinterpret_cast(to) >> 16) & 0xffff); - /* ori */ - *(insn + 1) = (*(insn + 1) & 0xffff0000) | (reinterpret_cast(to) & 0xffff); - return 2 * sizeof(MIPSWord); - } - - AssemblerBuffer m_buffer; - Jumps m_jumps; -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) && CPU(MIPS) - -#endif // MIPSAssembler_h diff --git a/3rdparty/masm/assembler/MacroAssembler.h b/3rdparty/masm/assembler/MacroAssembler.h deleted file mode 100644 index 3d57340f93..0000000000 --- a/3rdparty/masm/assembler/MacroAssembler.h +++ /dev/null @@ -1,1464 +0,0 @@ -/* - * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssembler_h -#define MacroAssembler_h - -#include - -#if ENABLE(ASSEMBLER) - -#if CPU(ARM_THUMB2) -#include "MacroAssemblerARMv7.h" -namespace JSC { typedef MacroAssemblerARMv7 MacroAssemblerBase; }; - -#elif CPU(ARM_TRADITIONAL) -#include "MacroAssemblerARM.h" -namespace JSC { typedef MacroAssemblerARM MacroAssemblerBase; }; - -#elif CPU(MIPS) -#include "MacroAssemblerMIPS.h" -namespace JSC { -typedef MacroAssemblerMIPS MacroAssemblerBase; -}; - -#elif CPU(X86) -#include "MacroAssemblerX86.h" -namespace JSC { typedef MacroAssemblerX86 MacroAssemblerBase; }; - -#elif CPU(X86_64) -#include "MacroAssemblerX86_64.h" -namespace JSC { typedef MacroAssemblerX86_64 MacroAssemblerBase; }; - -#elif CPU(SH4) -#include "MacroAssemblerSH4.h" -namespace JSC { -typedef MacroAssemblerSH4 MacroAssemblerBase; -}; - -#else -#error "The MacroAssembler is not supported on this platform." -#endif - -namespace JSC { - -class MacroAssembler : public MacroAssemblerBase { -public: - - using MacroAssemblerBase::pop; - using MacroAssemblerBase::jump; - using MacroAssemblerBase::branch32; - using MacroAssemblerBase::move; - -#if ENABLE(JIT_CONSTANT_BLINDING) - using MacroAssemblerBase::add32; - using MacroAssemblerBase::and32; - using MacroAssemblerBase::branchAdd32; - using MacroAssemblerBase::branchMul32; - using MacroAssemblerBase::branchSub32; - using MacroAssemblerBase::lshift32; - using MacroAssemblerBase::or32; - using MacroAssemblerBase::rshift32; - using MacroAssemblerBase::store32; - using MacroAssemblerBase::sub32; - using MacroAssemblerBase::urshift32; - using MacroAssemblerBase::xor32; -#endif - - static const double twoToThe32; // This is super useful for some double code. - - // Utilities used by the DFG JIT. -#if ENABLE(DFG_JIT) - using MacroAssemblerBase::invert; - - static DoubleCondition invert(DoubleCondition cond) - { - switch (cond) { - case DoubleEqual: - return DoubleNotEqualOrUnordered; - case DoubleNotEqual: - return DoubleEqualOrUnordered; - case DoubleGreaterThan: - return DoubleLessThanOrEqualOrUnordered; - case DoubleGreaterThanOrEqual: - return DoubleLessThanOrUnordered; - case DoubleLessThan: - return DoubleGreaterThanOrEqualOrUnordered; - case DoubleLessThanOrEqual: - return DoubleGreaterThanOrUnordered; - case DoubleEqualOrUnordered: - return DoubleNotEqual; - case DoubleNotEqualOrUnordered: - return DoubleEqual; - case DoubleGreaterThanOrUnordered: - return DoubleLessThanOrEqual; - case DoubleGreaterThanOrEqualOrUnordered: - return DoubleLessThan; - case DoubleLessThanOrUnordered: - return DoubleGreaterThanOrEqual; - case DoubleLessThanOrEqualOrUnordered: - return DoubleGreaterThan; - default: - ASSERT_NOT_REACHED(); - return DoubleEqual; // make compiler happy - } - } - - static bool isInvertible(ResultCondition cond) - { - switch (cond) { - case Zero: - case NonZero: - return true; - default: - return false; - } - } - - static ResultCondition invert(ResultCondition cond) - { - switch (cond) { - case Zero: - return NonZero; - case NonZero: - return Zero; - default: - ASSERT_NOT_REACHED(); - return Zero; // Make compiler happy for release builds. - } - } -#endif - - // Platform agnostic onvenience functions, - // described in terms of other macro assembly methods. - void pop() - { - addPtr(TrustedImm32(sizeof(void*)), stackPointerRegister); - } - - void peek(RegisterID dest, int index = 0) - { - loadPtr(Address(stackPointerRegister, (index * sizeof(void*))), dest); - } - - Address addressForPoke(int index) - { - return Address(stackPointerRegister, (index * sizeof(void*))); - } - - void poke(RegisterID src, int index = 0) - { - storePtr(src, addressForPoke(index)); - } - - void poke(TrustedImm32 value, int index = 0) - { - store32(value, addressForPoke(index)); - } - - void poke(TrustedImmPtr imm, int index = 0) - { - storePtr(imm, addressForPoke(index)); - } - -#if CPU(X86_64) - void peek64(RegisterID dest, int index = 0) - { - load64(Address(stackPointerRegister, (index * sizeof(void*))), dest); - } - - void poke(TrustedImm64 value, int index = 0) - { - store64(value, addressForPoke(index)); - } - - void poke64(RegisterID src, int index = 0) - { - store64(src, addressForPoke(index)); - } -#endif - - - // Backwards banches, these are currently all implemented using existing forwards branch mechanisms. - void branchPtr(RelationalCondition cond, RegisterID op1, TrustedImmPtr imm, Label target) - { - branchPtr(cond, op1, imm).linkTo(target, this); - } - void branchPtr(RelationalCondition cond, RegisterID op1, ImmPtr imm, Label target) - { - branchPtr(cond, op1, imm).linkTo(target, this); - } - - void branch32(RelationalCondition cond, RegisterID op1, RegisterID op2, Label target) - { - branch32(cond, op1, op2).linkTo(target, this); - } - - void branch32(RelationalCondition cond, RegisterID op1, TrustedImm32 imm, Label target) - { - branch32(cond, op1, imm).linkTo(target, this); - } - - void branch32(RelationalCondition cond, RegisterID op1, Imm32 imm, Label target) - { - branch32(cond, op1, imm).linkTo(target, this); - } - - void branch32(RelationalCondition cond, RegisterID left, Address right, Label target) - { - branch32(cond, left, right).linkTo(target, this); - } - - Jump branch32(RelationalCondition cond, TrustedImm32 left, RegisterID right) - { - return branch32(commute(cond), right, left); - } - - Jump branch32(RelationalCondition cond, Imm32 left, RegisterID right) - { - return branch32(commute(cond), right, left); - } - - void branchTestPtr(ResultCondition cond, RegisterID reg, Label target) - { - branchTestPtr(cond, reg).linkTo(target, this); - } - -#if !CPU(ARM_THUMB2) - PatchableJump patchableBranchPtr(RelationalCondition cond, Address left, TrustedImmPtr right = TrustedImmPtr(0)) - { - return PatchableJump(branchPtr(cond, left, right)); - } - - PatchableJump patchableBranchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - return PatchableJump(branchPtrWithPatch(cond, left, dataLabel, initialRightValue)); - } - - PatchableJump patchableJump() - { - return PatchableJump(jump()); - } - - PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - return PatchableJump(branchTest32(cond, reg, mask)); - } -#endif // !CPU(ARM_THUMB2) - -#if !CPU(ARM) - PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) - { - return PatchableJump(branch32(cond, reg, imm)); - } -#endif // !(CPU(ARM) - - void jump(Label target) - { - jump().linkTo(target, this); - } - - // Commute a relational condition, returns a new condition that will produce - // the same results given the same inputs but with their positions exchanged. - static RelationalCondition commute(RelationalCondition condition) - { - switch (condition) { - case Above: - return Below; - case AboveOrEqual: - return BelowOrEqual; - case Below: - return Above; - case BelowOrEqual: - return AboveOrEqual; - case GreaterThan: - return LessThan; - case GreaterThanOrEqual: - return LessThanOrEqual; - case LessThan: - return GreaterThan; - case LessThanOrEqual: - return GreaterThanOrEqual; - default: - break; - } - - ASSERT(condition == Equal || condition == NotEqual); - return condition; - } - - static const unsigned BlindingModulus = 64; - bool shouldConsiderBlinding() - { - return !(random() & (BlindingModulus - 1)); - } - - // Ptr methods - // On 32-bit platforms (i.e. x86), these methods directly map onto their 32-bit equivalents. - // FIXME: should this use a test for 32-bitness instead of this specific exception? -#if !CPU(X86_64) - void addPtr(Address src, RegisterID dest) - { - add32(src, dest); - } - - void addPtr(AbsoluteAddress src, RegisterID dest) - { - add32(src, dest); - } - - void addPtr(RegisterID src, RegisterID dest) - { - add32(src, dest); - } - - void addPtr(TrustedImm32 imm, RegisterID srcDest) - { - add32(imm, srcDest); - } - - void addPtr(TrustedImmPtr imm, RegisterID dest) - { - add32(TrustedImm32(imm), dest); - } - - void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - add32(imm, src, dest); - } - - void addPtr(TrustedImm32 imm, AbsoluteAddress address) - { - add32(imm, address); - } - - void andPtr(RegisterID src, RegisterID dest) - { - and32(src, dest); - } - - void andPtr(TrustedImm32 imm, RegisterID srcDest) - { - and32(imm, srcDest); - } - - void negPtr(RegisterID dest) - { - neg32(dest); - } - - void orPtr(RegisterID src, RegisterID dest) - { - or32(src, dest); - } - - void orPtr(RegisterID op1, RegisterID op2, RegisterID dest) - { - or32(op1, op2, dest); - } - - void orPtr(TrustedImmPtr imm, RegisterID dest) - { - or32(TrustedImm32(imm), dest); - } - - void orPtr(TrustedImm32 imm, RegisterID dest) - { - or32(imm, dest); - } - - void subPtr(RegisterID src, RegisterID dest) - { - sub32(src, dest); - } - - void subPtr(TrustedImm32 imm, RegisterID dest) - { - sub32(imm, dest); - } - - void subPtr(TrustedImmPtr imm, RegisterID dest) - { - sub32(TrustedImm32(imm), dest); - } - - void xorPtr(RegisterID src, RegisterID dest) - { - xor32(src, dest); - } - - void xorPtr(TrustedImm32 imm, RegisterID srcDest) - { - xor32(imm, srcDest); - } - - - void loadPtr(ImplicitAddress address, RegisterID dest) - { - load32(address, dest); - } - - void loadPtr(BaseIndex address, RegisterID dest) - { - load32(address, dest); - } - - void loadPtr(const void* address, RegisterID dest) - { - load32(address, dest); - } - - DataLabel32 loadPtrWithAddressOffsetPatch(Address address, RegisterID dest) - { - return load32WithAddressOffsetPatch(address, dest); - } - - DataLabelCompact loadPtrWithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - return load32WithCompactAddressOffsetPatch(address, dest); - } - - void move(ImmPtr imm, RegisterID dest) - { - move(Imm32(imm.asTrustedImmPtr()), dest); - } - - void comparePtr(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - compare32(cond, left, right, dest); - } - - void storePtr(RegisterID src, ImplicitAddress address) - { - store32(src, address); - } - - void storePtr(RegisterID src, BaseIndex address) - { - store32(src, address); - } - - void storePtr(RegisterID src, void* address) - { - store32(src, address); - } - - void storePtr(TrustedImmPtr imm, ImplicitAddress address) - { - store32(TrustedImm32(imm), address); - } - - void storePtr(ImmPtr imm, Address address) - { - store32(Imm32(imm.asTrustedImmPtr()), address); - } - - void storePtr(TrustedImmPtr imm, void* address) - { - store32(TrustedImm32(imm), address); - } - - DataLabel32 storePtrWithAddressOffsetPatch(RegisterID src, Address address) - { - return store32WithAddressOffsetPatch(src, address); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, RegisterID right) - { - return branch32(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, TrustedImmPtr right) - { - return branch32(cond, left, TrustedImm32(right)); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, ImmPtr right) - { - return branch32(cond, left, Imm32(right.asTrustedImmPtr())); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, Address right) - { - return branch32(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, Address left, RegisterID right) - { - return branch32(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - return branch32(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, Address left, TrustedImmPtr right) - { - return branch32(cond, left, TrustedImm32(right)); - } - - Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, TrustedImmPtr right) - { - return branch32(cond, left, TrustedImm32(right)); - } - - Jump branchSubPtr(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchSub32(cond, src, dest); - } - - Jump branchTestPtr(ResultCondition cond, RegisterID reg, RegisterID mask) - { - return branchTest32(cond, reg, mask); - } - - Jump branchTestPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - return branchTest32(cond, reg, mask); - } - - Jump branchTestPtr(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - return branchTest32(cond, address, mask); - } - - Jump branchTestPtr(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - return branchTest32(cond, address, mask); - } - - Jump branchAddPtr(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchAdd32(cond, src, dest); - } - - Jump branchSubPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - return branchSub32(cond, imm, dest); - } - using MacroAssemblerBase::branchTest8; - Jump branchTest8(ResultCondition cond, ExtendedAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - return MacroAssemblerBase::branchTest8(cond, Address(address.base, address.offset), mask); - } -#else - void addPtr(RegisterID src, RegisterID dest) - { - add64(src, dest); - } - - void addPtr(Address src, RegisterID dest) - { - add64(src, dest); - } - - void addPtr(TrustedImm32 imm, RegisterID srcDest) - { - add64(imm, srcDest); - } - - void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - add64(imm, src, dest); - } - - void addPtr(TrustedImm32 imm, Address address) - { - add64(imm, address); - } - - void addPtr(AbsoluteAddress src, RegisterID dest) - { - add64(src, dest); - } - - void addPtr(TrustedImmPtr imm, RegisterID dest) - { - add64(TrustedImm64(imm), dest); - } - - void addPtr(TrustedImm32 imm, AbsoluteAddress address) - { - add64(imm, address); - } - - void andPtr(RegisterID src, RegisterID dest) - { - and64(src, dest); - } - - void andPtr(TrustedImm32 imm, RegisterID srcDest) - { - and64(imm, srcDest); - } - - void negPtr(RegisterID dest) - { - neg64(dest); - } - - void orPtr(RegisterID src, RegisterID dest) - { - or64(src, dest); - } - - void orPtr(TrustedImm32 imm, RegisterID dest) - { - or64(imm, dest); - } - - void orPtr(TrustedImmPtr imm, RegisterID dest) - { - or64(TrustedImm64(imm), dest); - } - - void orPtr(RegisterID op1, RegisterID op2, RegisterID dest) - { - or64(op1, op2, dest); - } - - void orPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - or64(imm, src, dest); - } - - void rotateRightPtr(TrustedImm32 imm, RegisterID srcDst) - { - rotateRight64(imm, srcDst); - } - - void subPtr(RegisterID src, RegisterID dest) - { - sub64(src, dest); - } - - void subPtr(TrustedImm32 imm, RegisterID dest) - { - sub64(imm, dest); - } - - void subPtr(TrustedImmPtr imm, RegisterID dest) - { - sub64(TrustedImm64(imm), dest); - } - - void xorPtr(RegisterID src, RegisterID dest) - { - xor64(src, dest); - } - - void xorPtr(RegisterID src, Address dest) - { - xor64(src, dest); - } - - void xorPtr(TrustedImm32 imm, RegisterID srcDest) - { - xor64(imm, srcDest); - } - - void loadPtr(ImplicitAddress address, RegisterID dest) - { - load64(address, dest); - } - - void loadPtr(BaseIndex address, RegisterID dest) - { - load64(address, dest); - } - - void loadPtr(const void* address, RegisterID dest) - { - load64(address, dest); - } - - DataLabel32 loadPtrWithAddressOffsetPatch(Address address, RegisterID dest) - { - return load64WithAddressOffsetPatch(address, dest); - } - - DataLabelCompact loadPtrWithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - return load64WithCompactAddressOffsetPatch(address, dest); - } - - void storePtr(RegisterID src, ImplicitAddress address) - { - store64(src, address); - } - - void storePtr(RegisterID src, BaseIndex address) - { - store64(src, address); - } - - void storePtr(RegisterID src, void* address) - { - store64(src, address); - } - - void storePtr(TrustedImmPtr imm, ImplicitAddress address) - { - store64(TrustedImm64(imm), address); - } - - void storePtr(TrustedImmPtr imm, BaseIndex address) - { - store64(TrustedImm64(imm), address); - } - - DataLabel32 storePtrWithAddressOffsetPatch(RegisterID src, Address address) - { - return store64WithAddressOffsetPatch(src, address); - } - - void comparePtr(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - compare64(cond, left, right, dest); - } - - void comparePtr(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) - { - compare64(cond, left, right, dest); - } - - void testPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) - { - test64(cond, reg, mask, dest); - } - - void testPtr(ResultCondition cond, RegisterID reg, RegisterID mask, RegisterID dest) - { - test64(cond, reg, mask, dest); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, RegisterID right) - { - return branch64(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, TrustedImmPtr right) - { - return branch64(cond, left, TrustedImm64(right)); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, Address right) - { - return branch64(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, Address left, RegisterID right) - { - return branch64(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - return branch64(cond, left, right); - } - - Jump branchPtr(RelationalCondition cond, Address left, TrustedImmPtr right) - { - return branch64(cond, left, TrustedImm64(right)); - } - - Jump branchTestPtr(ResultCondition cond, RegisterID reg, RegisterID mask) - { - return branchTest64(cond, reg, mask); - } - - Jump branchTestPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - return branchTest64(cond, reg, mask); - } - - Jump branchTestPtr(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - return branchTest64(cond, address, mask); - } - - Jump branchTestPtr(ResultCondition cond, Address address, RegisterID reg) - { - return branchTest64(cond, address, reg); - } - - Jump branchTestPtr(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - return branchTest64(cond, address, mask); - } - - Jump branchTestPtr(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - return branchTest64(cond, address, mask); - } - - Jump branchAddPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - return branchAdd64(cond, imm, dest); - } - - Jump branchAddPtr(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchAdd64(cond, src, dest); - } - - Jump branchSubPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - return branchSub64(cond, imm, dest); - } - - Jump branchSubPtr(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchSub64(cond, src, dest); - } - - Jump branchSubPtr(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) - { - return branchSub64(cond, src1, src2, dest); - } - -#if ENABLE(JIT_CONSTANT_BLINDING) - using MacroAssemblerBase::and64; - using MacroAssemblerBase::convertInt32ToDouble; - using MacroAssemblerBase::store64; - bool shouldBlindDouble(double value) - { - // Don't trust NaN or +/-Infinity - if (!isfinite(value)) - return shouldConsiderBlinding(); - - // Try to force normalisation, and check that there's no change - // in the bit pattern - if (bitwise_cast(value * 1.0) != bitwise_cast(value)) - return shouldConsiderBlinding(); - - value = abs(value); - // Only allow a limited set of fractional components - double scaledValue = value * 8; - if (scaledValue / 8 != value) - return shouldConsiderBlinding(); - double frac = scaledValue - floor(scaledValue); - if (frac != 0.0) - return shouldConsiderBlinding(); - - return value > 0xff; - } - - bool shouldBlind(ImmPtr imm) - { -#if !defined(NDEBUG) - UNUSED_PARAM(imm); - // Debug always blind all constants, if only so we know - // if we've broken blinding during patch development. - return true; -#endif - - // First off we'll special case common, "safe" values to avoid hurting - // performance too much - uintptr_t value = imm.asTrustedImmPtr().asIntptr(); - switch (value) { - case 0xffff: - case 0xffffff: - case 0xffffffffL: - case 0xffffffffffL: - case 0xffffffffffffL: - case 0xffffffffffffffL: - case 0xffffffffffffffffL: - return false; - default: { - if (value <= 0xff) - return false; - if (~value <= 0xff) - return false; - } - } - - if (!shouldConsiderBlinding()) - return false; - - return shouldBlindForSpecificArch(value); - } - - struct RotatedImmPtr { - RotatedImmPtr(uintptr_t v1, uint8_t v2) - : value(v1) - , rotation(v2) - { - } - TrustedImmPtr value; - TrustedImm32 rotation; - }; - - RotatedImmPtr rotationBlindConstant(ImmPtr imm) - { - uint8_t rotation = random() % (sizeof(void*) * 8); - uintptr_t value = imm.asTrustedImmPtr().asIntptr(); - value = (value << rotation) | (value >> (sizeof(void*) * 8 - rotation)); - return RotatedImmPtr(value, rotation); - } - - void loadRotationBlindedConstant(RotatedImmPtr constant, RegisterID dest) - { - move(constant.value, dest); - rotateRightPtr(constant.rotation, dest); - } - - bool shouldBlind(Imm64 imm) - { -#if !defined(NDEBUG) - UNUSED_PARAM(imm); - // Debug always blind all constants, if only so we know - // if we've broken blinding during patch development. - return true; -#endif - - // First off we'll special case common, "safe" values to avoid hurting - // performance too much - uint64_t value = imm.asTrustedImm64().m_value; - switch (value) { - case 0xffff: - case 0xffffff: - case 0xffffffffL: - case 0xffffffffffL: - case 0xffffffffffffL: - case 0xffffffffffffffL: - case 0xffffffffffffffffL: - return false; - default: { - if (value <= 0xff) - return false; - if (~value <= 0xff) - return false; - - JSValue jsValue = JSValue::decode(value); - if (jsValue.isInt32()) - return shouldBlind(Imm32(jsValue.asInt32())); - if (jsValue.isDouble() && !shouldBlindDouble(jsValue.asDouble())) - return false; - - if (!shouldBlindDouble(bitwise_cast(value))) - return false; - } - } - - if (!shouldConsiderBlinding()) - return false; - - return shouldBlindForSpecificArch(value); - } - - struct RotatedImm64 { - RotatedImm64(uint64_t v1, uint8_t v2) - : value(v1) - , rotation(v2) - { - } - TrustedImm64 value; - TrustedImm32 rotation; - }; - - RotatedImm64 rotationBlindConstant(Imm64 imm) - { - uint8_t rotation = random() % (sizeof(int64_t) * 8); - uint64_t value = imm.asTrustedImm64().m_value; - value = (value << rotation) | (value >> (sizeof(int64_t) * 8 - rotation)); - return RotatedImm64(value, rotation); - } - - void loadRotationBlindedConstant(RotatedImm64 constant, RegisterID dest) - { - move(constant.value, dest); - rotateRight64(constant.rotation, dest); - } - - void convertInt32ToDouble(Imm32 imm, FPRegisterID dest) - { - if (shouldBlind(imm)) { - RegisterID scratchRegister = scratchRegisterForBlinding(); - loadXorBlindedConstant(xorBlindConstant(imm), scratchRegister); - convertInt32ToDouble(scratchRegister, dest); - } else - convertInt32ToDouble(imm.asTrustedImm32(), dest); - } - - void move(ImmPtr imm, RegisterID dest) - { - if (shouldBlind(imm)) - loadRotationBlindedConstant(rotationBlindConstant(imm), dest); - else - move(imm.asTrustedImmPtr(), dest); - } - - void move(Imm64 imm, RegisterID dest) - { - if (shouldBlind(imm)) - loadRotationBlindedConstant(rotationBlindConstant(imm), dest); - else - move(imm.asTrustedImm64(), dest); - } - - void and64(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = andBlindedConstant(imm); - and64(key.value1, dest); - and64(key.value2, dest); - } else - and64(imm.asTrustedImm32(), dest); - } - - Jump branchPtr(RelationalCondition cond, RegisterID left, ImmPtr right) - { - if (shouldBlind(right)) { - RegisterID scratchRegister = scratchRegisterForBlinding(); - loadRotationBlindedConstant(rotationBlindConstant(right), scratchRegister); - return branchPtr(cond, left, scratchRegister); - } - return branchPtr(cond, left, right.asTrustedImmPtr()); - } - - void storePtr(ImmPtr imm, Address dest) - { - if (shouldBlind(imm)) { - RegisterID scratchRegister = scratchRegisterForBlinding(); - loadRotationBlindedConstant(rotationBlindConstant(imm), scratchRegister); - storePtr(scratchRegister, dest); - } else - storePtr(imm.asTrustedImmPtr(), dest); - } - - void store64(Imm64 imm, Address dest) - { - if (shouldBlind(imm)) { - RegisterID scratchRegister = scratchRegisterForBlinding(); - loadRotationBlindedConstant(rotationBlindConstant(imm), scratchRegister); - store64(scratchRegister, dest); - } else - store64(imm.asTrustedImm64(), dest); - } - -#endif - -#endif // !CPU(X86_64) - -#if ENABLE(JIT_CONSTANT_BLINDING) - bool shouldBlind(Imm32 imm) - { -#if !defined(NDEBUG) - UNUSED_PARAM(imm); - // Debug always blind all constants, if only so we know - // if we've broken blinding during patch development. - return true; -#else - - // First off we'll special case common, "safe" values to avoid hurting - // performance too much - uint32_t value = imm.asTrustedImm32().m_value; - switch (value) { - case 0xffff: - case 0xffffff: - case 0xffffffff: - return false; - default: - if (value <= 0xff) - return false; - if (~value <= 0xff) - return false; - } - - if (!shouldConsiderBlinding()) - return false; - - return shouldBlindForSpecificArch(value); -#endif - } - - struct BlindedImm32 { - BlindedImm32(int32_t v1, int32_t v2) - : value1(v1) - , value2(v2) - { - } - TrustedImm32 value1; - TrustedImm32 value2; - }; - - uint32_t keyForConstant(uint32_t value, uint32_t& mask) - { - uint32_t key = random(); - if (value <= 0xff) - mask = 0xff; - else if (value <= 0xffff) - mask = 0xffff; - else if (value <= 0xffffff) - mask = 0xffffff; - else - mask = 0xffffffff; - return key & mask; - } - - uint32_t keyForConstant(uint32_t value) - { - uint32_t mask = 0; - return keyForConstant(value, mask); - } - - BlindedImm32 xorBlindConstant(Imm32 imm) - { - uint32_t baseValue = imm.asTrustedImm32().m_value; - uint32_t key = keyForConstant(baseValue); - return BlindedImm32(baseValue ^ key, key); - } - - BlindedImm32 additionBlindedConstant(Imm32 imm) - { - // The addition immediate may be used as a pointer offset. Keep aligned based on "imm". - static uint32_t maskTable[4] = { 0xfffffffc, 0xffffffff, 0xfffffffe, 0xffffffff }; - - uint32_t baseValue = imm.asTrustedImm32().m_value; - uint32_t key = keyForConstant(baseValue) & maskTable[baseValue & 3]; - if (key > baseValue) - key = key - baseValue; - return BlindedImm32(baseValue - key, key); - } - - BlindedImm32 andBlindedConstant(Imm32 imm) - { - uint32_t baseValue = imm.asTrustedImm32().m_value; - uint32_t mask = 0; - uint32_t key = keyForConstant(baseValue, mask); - ASSERT((baseValue & mask) == baseValue); - return BlindedImm32(((baseValue & key) | ~key) & mask, ((baseValue & ~key) | key) & mask); - } - - BlindedImm32 orBlindedConstant(Imm32 imm) - { - uint32_t baseValue = imm.asTrustedImm32().m_value; - uint32_t mask = 0; - uint32_t key = keyForConstant(baseValue, mask); - ASSERT((baseValue & mask) == baseValue); - return BlindedImm32((baseValue & key) & mask, (baseValue & ~key) & mask); - } - - void loadXorBlindedConstant(BlindedImm32 constant, RegisterID dest) - { - move(constant.value1, dest); - xor32(constant.value2, dest); - } - - void add32(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = additionBlindedConstant(imm); - add32(key.value1, dest); - add32(key.value2, dest); - } else - add32(imm.asTrustedImm32(), dest); - } - - void addPtr(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = additionBlindedConstant(imm); - addPtr(key.value1, dest); - addPtr(key.value2, dest); - } else - addPtr(imm.asTrustedImm32(), dest); - } - - void and32(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = andBlindedConstant(imm); - and32(key.value1, dest); - and32(key.value2, dest); - } else - and32(imm.asTrustedImm32(), dest); - } - - void andPtr(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = andBlindedConstant(imm); - andPtr(key.value1, dest); - andPtr(key.value2, dest); - } else - andPtr(imm.asTrustedImm32(), dest); - } - - void and32(Imm32 imm, RegisterID src, RegisterID dest) - { - if (shouldBlind(imm)) { - if (src == dest) - return and32(imm.asTrustedImm32(), dest); - loadXorBlindedConstant(xorBlindConstant(imm), dest); - and32(src, dest); - } else - and32(imm.asTrustedImm32(), src, dest); - } - - void move(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) - loadXorBlindedConstant(xorBlindConstant(imm), dest); - else - move(imm.asTrustedImm32(), dest); - } - - void or32(Imm32 imm, RegisterID src, RegisterID dest) - { - if (shouldBlind(imm)) { - if (src == dest) - return or32(imm, dest); - loadXorBlindedConstant(xorBlindConstant(imm), dest); - or32(src, dest); - } else - or32(imm.asTrustedImm32(), src, dest); - } - - void or32(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = orBlindedConstant(imm); - or32(key.value1, dest); - or32(key.value2, dest); - } else - or32(imm.asTrustedImm32(), dest); - } - - void poke(Imm32 value, int index = 0) - { - store32(value, addressForPoke(index)); - } - - void poke(ImmPtr value, int index = 0) - { - storePtr(value, addressForPoke(index)); - } - -#if CPU(X86_64) - void poke(Imm64 value, int index = 0) - { - store64(value, addressForPoke(index)); - } -#endif - - void store32(Imm32 imm, Address dest) - { - if (shouldBlind(imm)) { -#if CPU(X86) || CPU(X86_64) - BlindedImm32 blind = xorBlindConstant(imm); - store32(blind.value1, dest); - xor32(blind.value2, dest); -#else - if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { - loadXorBlindedConstant(xorBlindConstant(imm), scratchRegister); - store32(scratchRegister, dest); - } else { - // If we don't have a scratch register available for use, we'll just - // place a random number of nops. - uint32_t nopCount = random() & 3; - while (nopCount--) - nop(); - store32(imm.asTrustedImm32(), dest); - } -#endif - } else - store32(imm.asTrustedImm32(), dest); - } - - void sub32(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = additionBlindedConstant(imm); - sub32(key.value1, dest); - sub32(key.value2, dest); - } else - sub32(imm.asTrustedImm32(), dest); - } - - void subPtr(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 key = additionBlindedConstant(imm); - subPtr(key.value1, dest); - subPtr(key.value2, dest); - } else - subPtr(imm.asTrustedImm32(), dest); - } - - void xor32(Imm32 imm, RegisterID src, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 blind = xorBlindConstant(imm); - xor32(blind.value1, src, dest); - xor32(blind.value2, dest); - } else - xor32(imm.asTrustedImm32(), src, dest); - } - - void xor32(Imm32 imm, RegisterID dest) - { - if (shouldBlind(imm)) { - BlindedImm32 blind = xorBlindConstant(imm); - xor32(blind.value1, dest); - xor32(blind.value2, dest); - } else - xor32(imm.asTrustedImm32(), dest); - } - - Jump branch32(RelationalCondition cond, RegisterID left, Imm32 right) - { - if (shouldBlind(right)) { - if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { - loadXorBlindedConstant(xorBlindConstant(right), scratchRegister); - return branch32(cond, left, scratchRegister); - } - // If we don't have a scratch register available for use, we'll just - // place a random number of nops. - uint32_t nopCount = random() & 3; - while (nopCount--) - nop(); - return branch32(cond, left, right.asTrustedImm32()); - } - - return branch32(cond, left, right.asTrustedImm32()); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, Imm32 imm, RegisterID dest) - { - if (src == dest) { - if (!scratchRegisterForBlinding()) { - // Release mode ASSERT, if this fails we will perform incorrect codegen. - CRASH(); - } - } - if (shouldBlind(imm)) { - if (src == dest) { - if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { - move(src, scratchRegister); - src = scratchRegister; - } - } - loadXorBlindedConstant(xorBlindConstant(imm), dest); - return branchAdd32(cond, src, dest); - } - return branchAdd32(cond, src, imm.asTrustedImm32(), dest); - } - - Jump branchMul32(ResultCondition cond, Imm32 imm, RegisterID src, RegisterID dest) - { - if (src == dest) { - if (!scratchRegisterForBlinding()) { - // Release mode ASSERT, if this fails we will perform incorrect codegen. - CRASH(); - } - } - if (shouldBlind(imm)) { - if (src == dest) { - if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { - move(src, scratchRegister); - src = scratchRegister; - } - } - loadXorBlindedConstant(xorBlindConstant(imm), dest); - return branchMul32(cond, src, dest); - } - return branchMul32(cond, imm.asTrustedImm32(), src, dest); - } - - // branchSub32 takes a scratch register as 32 bit platforms make use of this, - // with src == dst, and on x86-32 we don't have a platform scratch register. - Jump branchSub32(ResultCondition cond, RegisterID src, Imm32 imm, RegisterID dest, RegisterID scratch) - { - if (shouldBlind(imm)) { - ASSERT(scratch != dest); - ASSERT(scratch != src); - loadXorBlindedConstant(xorBlindConstant(imm), scratch); - return branchSub32(cond, src, scratch, dest); - } - return branchSub32(cond, src, imm.asTrustedImm32(), dest); - } - - // Immediate shifts only have 5 controllable bits - // so we'll consider them safe for now. - TrustedImm32 trustedImm32ForShift(Imm32 imm) - { - return TrustedImm32(imm.asTrustedImm32().m_value & 31); - } - - void lshift32(Imm32 imm, RegisterID dest) - { - lshift32(trustedImm32ForShift(imm), dest); - } - - void lshift32(RegisterID src, Imm32 amount, RegisterID dest) - { - lshift32(src, trustedImm32ForShift(amount), dest); - } - - void rshift32(Imm32 imm, RegisterID dest) - { - rshift32(trustedImm32ForShift(imm), dest); - } - - void rshift32(RegisterID src, Imm32 amount, RegisterID dest) - { - rshift32(src, trustedImm32ForShift(amount), dest); - } - - void urshift32(Imm32 imm, RegisterID dest) - { - urshift32(trustedImm32ForShift(imm), dest); - } - - void urshift32(RegisterID src, Imm32 amount, RegisterID dest) - { - urshift32(src, trustedImm32ForShift(amount), dest); - } -#endif -}; - -} // namespace JSC - -#else // ENABLE(ASSEMBLER) - -// If there is no assembler for this platform, at least allow code to make references to -// some of the things it would otherwise define, albeit without giving that code any way -// of doing anything useful. -class MacroAssembler { -private: - MacroAssembler() { } - -public: - - enum RegisterID { NoRegister }; - enum FPRegisterID { NoFPRegister }; -}; - -#endif // ENABLE(ASSEMBLER) - -#endif // MacroAssembler_h diff --git a/3rdparty/masm/assembler/MacroAssemblerARM.cpp b/3rdparty/masm/assembler/MacroAssemblerARM.cpp deleted file mode 100644 index 98dc3e9879..0000000000 --- a/3rdparty/masm/assembler/MacroAssemblerARM.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2009 University of Szeged - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" - -#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) - -#include "MacroAssemblerARM.h" - -#if OS(LINUX) -#include -#include -#include -#include -#include -#include -#endif - -namespace JSC { - -static bool isVFPPresent() -{ -#if OS(LINUX) - int fd = open("/proc/self/auxv", O_RDONLY); - if (fd > 0) { - Elf32_auxv_t aux; - while (read(fd, &aux, sizeof(Elf32_auxv_t))) { - if (aux.a_type == AT_HWCAP) { - close(fd); - return aux.a_un.a_val & HWCAP_VFP; - } - } - close(fd); - } -#endif - -#if (COMPILER(RVCT) && defined(__TARGET_FPU_VFP)) || (COMPILER(GCC) && defined(__VFP_FP__)) - return true; -#else - return false; -#endif -} - -const bool MacroAssemblerARM::s_isVFPPresent = isVFPPresent(); - -#if CPU(ARMV5_OR_LOWER) -/* On ARMv5 and below, natural alignment is required. */ -void MacroAssemblerARM::load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) -{ - ARMWord op2; - - ASSERT(address.scale >= 0 && address.scale <= 3); - op2 = m_assembler.lsl(address.index, static_cast(address.scale)); - - if (address.offset >= 0 && address.offset + 0x2 <= 0xff) { - m_assembler.add(ARMRegisters::S0, address.base, op2); - m_assembler.halfDtrUp(ARMAssembler::LoadUint16, dest, ARMRegisters::S0, ARMAssembler::getOp2Half(address.offset)); - m_assembler.halfDtrUp(ARMAssembler::LoadUint16, ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Half(address.offset + 0x2)); - } else if (address.offset < 0 && address.offset >= -0xff) { - m_assembler.add(ARMRegisters::S0, address.base, op2); - m_assembler.halfDtrDown(ARMAssembler::LoadUint16, dest, ARMRegisters::S0, ARMAssembler::getOp2Half(-address.offset)); - m_assembler.halfDtrDown(ARMAssembler::LoadUint16, ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Half(-address.offset - 0x2)); - } else { - m_assembler.moveImm(address.offset, ARMRegisters::S0); - m_assembler.add(ARMRegisters::S0, ARMRegisters::S0, op2); - m_assembler.halfDtrUpRegister(ARMAssembler::LoadUint16, dest, address.base, ARMRegisters::S0); - m_assembler.add(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::Op2Immediate | 0x2); - m_assembler.halfDtrUpRegister(ARMAssembler::LoadUint16, ARMRegisters::S0, address.base, ARMRegisters::S0); - } - m_assembler.orr(dest, dest, m_assembler.lsl(ARMRegisters::S0, 16)); -} -#endif - -} - -#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) diff --git a/3rdparty/masm/assembler/MacroAssemblerARM.h b/3rdparty/masm/assembler/MacroAssemblerARM.h deleted file mode 100644 index 527126b438..0000000000 --- a/3rdparty/masm/assembler/MacroAssemblerARM.h +++ /dev/null @@ -1,1383 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. - * Copyright (C) 2009, 2010 University of Szeged - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssemblerARM_h -#define MacroAssemblerARM_h - -#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) - -#include "ARMAssembler.h" -#include "AbstractMacroAssembler.h" - -namespace JSC { - -class MacroAssemblerARM : public AbstractMacroAssembler { - static const int DoubleConditionMask = 0x0f; - static const int DoubleConditionBitSpecial = 0x10; - COMPILE_ASSERT(!(DoubleConditionBitSpecial & DoubleConditionMask), DoubleConditionBitSpecial_should_not_interfere_with_ARMAssembler_Condition_codes); -public: - typedef ARMRegisters::FPRegisterID FPRegisterID; - - enum RelationalCondition { - Equal = ARMAssembler::EQ, - NotEqual = ARMAssembler::NE, - Above = ARMAssembler::HI, - AboveOrEqual = ARMAssembler::CS, - Below = ARMAssembler::CC, - BelowOrEqual = ARMAssembler::LS, - GreaterThan = ARMAssembler::GT, - GreaterThanOrEqual = ARMAssembler::GE, - LessThan = ARMAssembler::LT, - LessThanOrEqual = ARMAssembler::LE - }; - - enum ResultCondition { - Overflow = ARMAssembler::VS, - Signed = ARMAssembler::MI, - Zero = ARMAssembler::EQ, - NonZero = ARMAssembler::NE - }; - - enum DoubleCondition { - // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. - DoubleEqual = ARMAssembler::EQ, - DoubleNotEqual = ARMAssembler::NE | DoubleConditionBitSpecial, - DoubleGreaterThan = ARMAssembler::GT, - DoubleGreaterThanOrEqual = ARMAssembler::GE, - DoubleLessThan = ARMAssembler::CC, - DoubleLessThanOrEqual = ARMAssembler::LS, - // If either operand is NaN, these conditions always evaluate to true. - DoubleEqualOrUnordered = ARMAssembler::EQ | DoubleConditionBitSpecial, - DoubleNotEqualOrUnordered = ARMAssembler::NE, - DoubleGreaterThanOrUnordered = ARMAssembler::HI, - DoubleGreaterThanOrEqualOrUnordered = ARMAssembler::CS, - DoubleLessThanOrUnordered = ARMAssembler::LT, - DoubleLessThanOrEqualOrUnordered = ARMAssembler::LE, - }; - - static const RegisterID stackPointerRegister = ARMRegisters::sp; - static const RegisterID linkRegister = ARMRegisters::lr; - - static const Scale ScalePtr = TimesFour; - - void add32(RegisterID src, RegisterID dest) - { - m_assembler.adds(dest, dest, src); - } - - void add32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.adds(dest, op1, op2); - } - - void add32(TrustedImm32 imm, Address address) - { - load32(address, ARMRegisters::S1); - add32(imm, ARMRegisters::S1); - store32(ARMRegisters::S1, address); - } - - void add32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.adds(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void add32(AbsoluteAddress src, RegisterID dest) - { - move(TrustedImmPtr(src.m_ptr), ARMRegisters::S1); - m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); - add32(ARMRegisters::S1, dest); - } - - void add32(Address src, RegisterID dest) - { - load32(src, ARMRegisters::S1); - add32(ARMRegisters::S1, dest); - } - - void add32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.adds(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void and32(RegisterID src, RegisterID dest) - { - m_assembler.bitAnds(dest, dest, src); - } - - void and32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.bitAnds(dest, op1, op2); - } - - void and32(TrustedImm32 imm, RegisterID dest) - { - ARMWord w = m_assembler.getImm(imm.m_value, ARMRegisters::S0, true); - if (w & ARMAssembler::Op2InvertedImmediate) - m_assembler.bics(dest, dest, w & ~ARMAssembler::Op2InvertedImmediate); - else - m_assembler.bitAnds(dest, dest, w); - } - - void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - ARMWord w = m_assembler.getImm(imm.m_value, ARMRegisters::S0, true); - if (w & ARMAssembler::Op2InvertedImmediate) - m_assembler.bics(dest, src, w & ~ARMAssembler::Op2InvertedImmediate); - else - m_assembler.bitAnds(dest, src, w); - } - - void lshift32(RegisterID shiftAmount, RegisterID dest) - { - lshift32(dest, shiftAmount, dest); - } - - void lshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) - { - ARMWord w = ARMAssembler::getOp2Byte(0x1f); - m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); - - m_assembler.movs(dest, m_assembler.lslRegister(src, ARMRegisters::S0)); - } - - void lshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.movs(dest, m_assembler.lsl(dest, imm.m_value & 0x1f)); - } - - void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.movs(dest, m_assembler.lsl(src, imm.m_value & 0x1f)); - } - - void mul32(RegisterID op1, RegisterID op2, RegisterID dest) - { - if (op2 == dest) { - if (op1 == dest) { - move(op2, ARMRegisters::S0); - op2 = ARMRegisters::S0; - } else { - // Swap the operands. - RegisterID tmp = op1; - op1 = op2; - op2 = tmp; - } - } - m_assembler.muls(dest, op1, op2); - } - - void mul32(RegisterID src, RegisterID dest) - { - mul32(src, dest, dest); - } - - void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(imm, ARMRegisters::S0); - m_assembler.muls(dest, src, ARMRegisters::S0); - } - - void neg32(RegisterID srcDest) - { - m_assembler.rsbs(srcDest, srcDest, ARMAssembler::getOp2Byte(0)); - } - - void or32(RegisterID src, RegisterID dest) - { - m_assembler.orrs(dest, dest, src); - } - - void or32(RegisterID src, AbsoluteAddress dest) - { - move(TrustedImmPtr(dest.m_ptr), ARMRegisters::S0); - load32(Address(ARMRegisters::S0), ARMRegisters::S1); - or32(src, ARMRegisters::S1); - store32(ARMRegisters::S1, ARMRegisters::S0); - } - - void or32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.orrs(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - m_assembler.orrs(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void or32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.orrs(dest, op1, op2); - } - - void rshift32(RegisterID shiftAmount, RegisterID dest) - { - rshift32(dest, shiftAmount, dest); - } - - void rshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) - { - ARMWord w = ARMAssembler::getOp2Byte(0x1f); - m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); - - m_assembler.movs(dest, m_assembler.asrRegister(src, ARMRegisters::S0)); - } - - void rshift32(TrustedImm32 imm, RegisterID dest) - { - rshift32(dest, imm, dest); - } - - void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.movs(dest, m_assembler.asr(src, imm.m_value & 0x1f)); - } - - void urshift32(RegisterID shiftAmount, RegisterID dest) - { - urshift32(dest, shiftAmount, dest); - } - - void urshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) - { - ARMWord w = ARMAssembler::getOp2Byte(0x1f); - m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); - - m_assembler.movs(dest, m_assembler.lsrRegister(src, ARMRegisters::S0)); - } - - void urshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.movs(dest, m_assembler.lsr(dest, imm.m_value & 0x1f)); - } - - void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.movs(dest, m_assembler.lsr(src, imm.m_value & 0x1f)); - } - - void sub32(RegisterID src, RegisterID dest) - { - m_assembler.subs(dest, dest, src); - } - - void sub32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.subs(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void sub32(TrustedImm32 imm, Address address) - { - load32(address, ARMRegisters::S1); - sub32(imm, ARMRegisters::S1); - store32(ARMRegisters::S1, address); - } - - void sub32(Address src, RegisterID dest) - { - load32(src, ARMRegisters::S1); - sub32(ARMRegisters::S1, dest); - } - - void sub32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.subs(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void xor32(RegisterID src, RegisterID dest) - { - m_assembler.eors(dest, dest, src); - } - - void xor32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.eors(dest, op1, op2); - } - - void xor32(TrustedImm32 imm, RegisterID dest) - { - if (imm.m_value == -1) - m_assembler.mvns(dest, dest); - else - m_assembler.eors(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (imm.m_value == -1) - m_assembler.mvns(dest, src); - else - m_assembler.eors(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void countLeadingZeros32(RegisterID src, RegisterID dest) - { -#if WTF_ARM_ARCH_AT_LEAST(5) - m_assembler.clz(dest, src); -#else - UNUSED_PARAM(src); - UNUSED_PARAM(dest); - ASSERT_NOT_REACHED(); -#endif - } - - void load8(ImplicitAddress address, RegisterID dest) - { - m_assembler.dataTransfer32(ARMAssembler::LoadUint8, dest, address.base, address.offset); - } - - void load8(BaseIndex address, RegisterID dest) - { - m_assembler.baseIndexTransfer32(ARMAssembler::LoadUint8, dest, address.base, address.index, static_cast(address.scale), address.offset); - } - - void load8Signed(BaseIndex address, RegisterID dest) - { - m_assembler.baseIndexTransfer16(ARMAssembler::LoadInt8, dest, address.base, address.index, static_cast(address.scale), address.offset); - } - - void load16(ImplicitAddress address, RegisterID dest) - { - m_assembler.dataTransfer16(ARMAssembler::LoadUint16, dest, address.base, address.offset); - } - - void load16(BaseIndex address, RegisterID dest) - { - m_assembler.baseIndexTransfer16(ARMAssembler::LoadUint16, dest, address.base, address.index, static_cast(address.scale), address.offset); - } - - void load16Signed(BaseIndex address, RegisterID dest) - { - m_assembler.baseIndexTransfer16(ARMAssembler::LoadInt16, dest, address.base, address.index, static_cast(address.scale), address.offset); - } - - void load32(ImplicitAddress address, RegisterID dest) - { - m_assembler.dataTransfer32(ARMAssembler::LoadUint32, dest, address.base, address.offset); - } - - void load32(BaseIndex address, RegisterID dest) - { - m_assembler.baseIndexTransfer32(ARMAssembler::LoadUint32, dest, address.base, address.index, static_cast(address.scale), address.offset); - } - -#if CPU(ARMV5_OR_LOWER) - void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest); -#else - void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) - { - load32(address, dest); - } -#endif - - void load16Unaligned(BaseIndex address, RegisterID dest) - { - load16(address, dest); - } - - ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) - { - ConvertibleLoadLabel result(this); - ASSERT(address.offset >= 0 && address.offset <= 255); - m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, address.base, address.offset); - return result; - } - - DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) - { - DataLabel32 dataLabel(this); - m_assembler.ldrUniqueImmediate(ARMRegisters::S0, 0); - m_assembler.dtrUpRegister(ARMAssembler::LoadUint32, dest, address.base, ARMRegisters::S0); - return dataLabel; - } - - static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) - { - return value >= -4095 && value <= 4095; - } - - DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - DataLabelCompact dataLabel(this); - ASSERT(isCompactPtrAlignedAddressOffset(address.offset)); - if (address.offset >= 0) - m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, address.base, address.offset); - else - m_assembler.dtrDown(ARMAssembler::LoadUint32, dest, address.base, address.offset); - return dataLabel; - } - - DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) - { - DataLabel32 dataLabel(this); - m_assembler.ldrUniqueImmediate(ARMRegisters::S0, 0); - m_assembler.dtrUpRegister(ARMAssembler::StoreUint32, src, address.base, ARMRegisters::S0); - return dataLabel; - } - - void store8(RegisterID src, BaseIndex address) - { - m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint8, src, address.base, address.index, static_cast(address.scale), address.offset); - } - - void store8(TrustedImm32 imm, void* address) - { - move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); - m_assembler.moveImm(imm.m_value, ARMRegisters::S1); - m_assembler.dtrUp(ARMAssembler::StoreUint8, ARMRegisters::S1, ARMRegisters::S0, 0); - } - - void store16(RegisterID src, BaseIndex address) - { - m_assembler.baseIndexTransfer16(ARMAssembler::StoreUint16, src, address.base, address.index, static_cast(address.scale), address.offset); - } - - void store32(RegisterID src, ImplicitAddress address) - { - m_assembler.dataTransfer32(ARMAssembler::StoreUint32, src, address.base, address.offset); - } - - void store32(RegisterID src, BaseIndex address) - { - m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint32, src, address.base, address.index, static_cast(address.scale), address.offset); - } - - void store32(TrustedImm32 imm, ImplicitAddress address) - { - move(imm, ARMRegisters::S1); - store32(ARMRegisters::S1, address); - } - - void store32(TrustedImm32 imm, BaseIndex address) - { - move(imm, ARMRegisters::S1); - m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint32, ARMRegisters::S1, address.base, address.index, static_cast(address.scale), address.offset); - } - - void store32(RegisterID src, void* address) - { - m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); - m_assembler.dtrUp(ARMAssembler::StoreUint32, src, ARMRegisters::S0, 0); - } - - void store32(TrustedImm32 imm, void* address) - { - m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); - m_assembler.moveImm(imm.m_value, ARMRegisters::S1); - m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); - } - - void pop(RegisterID dest) - { - m_assembler.pop(dest); - } - - void push(RegisterID src) - { - m_assembler.push(src); - } - - void push(Address address) - { - load32(address, ARMRegisters::S1); - push(ARMRegisters::S1); - } - - void push(TrustedImm32 imm) - { - move(imm, ARMRegisters::S0); - push(ARMRegisters::S0); - } - - void move(TrustedImm32 imm, RegisterID dest) - { - m_assembler.moveImm(imm.m_value, dest); - } - - void move(RegisterID src, RegisterID dest) - { - if (src != dest) - m_assembler.mov(dest, src); - } - - void move(TrustedImmPtr imm, RegisterID dest) - { - move(TrustedImm32(imm), dest); - } - - void swap(RegisterID reg1, RegisterID reg2) - { - m_assembler.mov(ARMRegisters::S0, reg1); - m_assembler.mov(reg1, reg2); - m_assembler.mov(reg2, ARMRegisters::S0); - } - - void signExtend32ToPtr(RegisterID src, RegisterID dest) - { - if (src != dest) - move(src, dest); - } - - void zeroExtend32ToPtr(RegisterID src, RegisterID dest) - { - if (src != dest) - move(src, dest); - } - - Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) - { - load8(left, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - ASSERT(!(right.m_value & 0xFFFFFF00)); - load8(left, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right, int useConstantPool = 0) - { - m_assembler.cmp(left, right); - return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); - } - - Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right, int useConstantPool = 0) - { - internalCompare32(left, right); - return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); - } - - Jump branch32(RelationalCondition cond, RegisterID left, Address right) - { - load32(right, ARMRegisters::S1); - return branch32(cond, left, ARMRegisters::S1); - } - - Jump branch32(RelationalCondition cond, Address left, RegisterID right) - { - load32(left, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) - { - load32(left, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - load32(left, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - load32WithUnalignedHalfWords(left, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - load8(address, ARMRegisters::S1); - return branchTest32(cond, ARMRegisters::S1, mask); - } - - Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); - load8(Address(ARMRegisters::S1), ARMRegisters::S1); - return branchTest32(cond, ARMRegisters::S1, mask); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) - { - ASSERT((cond == Zero) || (cond == NonZero)); - m_assembler.tst(reg, mask); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - ASSERT((cond == Zero) || (cond == NonZero)); - ARMWord w = m_assembler.getImm(mask.m_value, ARMRegisters::S0, true); - if (w & ARMAssembler::Op2InvertedImmediate) - m_assembler.bics(ARMRegisters::S0, reg, w & ~ARMAssembler::Op2InvertedImmediate); - else - m_assembler.tst(reg, w); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - load32(address, ARMRegisters::S1); - return branchTest32(cond, ARMRegisters::S1, mask); - } - - Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - load32(address, ARMRegisters::S1); - return branchTest32(cond, ARMRegisters::S1, mask); - } - - Jump jump() - { - return Jump(m_assembler.jmp()); - } - - void jump(RegisterID target) - { - m_assembler.bx(target); - } - - void jump(Address address) - { - load32(address, ARMRegisters::pc); - } - - void jump(AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), ARMRegisters::S0); - load32(Address(ARMRegisters::S0, 0), ARMRegisters::pc); - } - - void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) - { - m_assembler.vmov(dest1, dest2, src); - } - - void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) - { - UNUSED_PARAM(scratch); - m_assembler.vmov(dest, src1, src2); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - add32(src, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchAdd32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - add32(op1, op2, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - add32(imm, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - add32(src, imm, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - add32(imm, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - void mull32(RegisterID op1, RegisterID op2, RegisterID dest) - { - if (op2 == dest) { - if (op1 == dest) { - move(op2, ARMRegisters::S0); - op2 = ARMRegisters::S0; - } else { - // Swap the operands. - RegisterID tmp = op1; - op1 = op2; - op2 = tmp; - } - } - m_assembler.mull(ARMRegisters::S1, dest, op1, op2); - m_assembler.cmp(ARMRegisters::S1, m_assembler.asr(dest, 31)); - } - - Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - if (cond == Overflow) { - mull32(src1, src2, dest); - cond = NonZero; - } - else - mul32(src1, src2, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchMul32(cond, src, dest, dest); - } - - Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - if (cond == Overflow) { - move(imm, ARMRegisters::S0); - mull32(ARMRegisters::S0, src, dest); - cond = NonZero; - } - else - mul32(imm, src, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - sub32(src, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - sub32(imm, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - sub32(src, imm, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchSub32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - m_assembler.subs(dest, op1, op2); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchNeg32(ResultCondition cond, RegisterID srcDest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - neg32(srcDest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); - or32(src, dest); - return Jump(m_assembler.jmp(ARMCondition(cond))); - } - - PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) - { - internalCompare32(reg, imm); - Jump jump(m_assembler.loadBranchTarget(ARMRegisters::S1, ARMCondition(cond), true)); - m_assembler.bx(ARMRegisters::S1, ARMCondition(cond)); - return PatchableJump(jump); - } - - void breakpoint() - { - m_assembler.bkpt(0); - } - - Call nearCall() - { - m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true); - return Call(m_assembler.blx(ARMRegisters::S1), Call::LinkableNear); - } - - Call call(RegisterID target) - { - return Call(m_assembler.blx(target), Call::None); - } - - void call(Address address) - { - call32(address.base, address.offset); - } - - void ret() - { - m_assembler.bx(linkRegister); - } - - void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) - { - m_assembler.cmp(left, right); - m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); - m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); - } - - void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - m_assembler.cmp(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); - m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); - m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); - } - - void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) - { - load8(left, ARMRegisters::S1); - compare32(cond, ARMRegisters::S1, right, dest); - } - - void test32(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) - { - if (mask.m_value == -1) - m_assembler.cmp(0, reg); - else - m_assembler.tst(reg, m_assembler.getImm(mask.m_value, ARMRegisters::S0)); - m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); - m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); - } - - void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - load32(address, ARMRegisters::S1); - test32(cond, ARMRegisters::S1, mask, dest); - } - - void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - load8(address, ARMRegisters::S1); - test32(cond, ARMRegisters::S1, mask, dest); - } - - void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - m_assembler.add(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); - } - - void add32(TrustedImm32 imm, AbsoluteAddress address) - { - m_assembler.ldrUniqueImmediate(ARMRegisters::S1, reinterpret_cast(address.m_ptr)); - m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); - add32(imm, ARMRegisters::S1); - m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address.m_ptr)); - m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); - } - - void add64(TrustedImm32 imm, AbsoluteAddress address) - { - ARMWord tmp; - - move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); - m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S0, ARMRegisters::S1, 0); - - if ((tmp = ARMAssembler::getOp2(imm.m_value)) != ARMAssembler::InvalidImmediate) - m_assembler.adds(ARMRegisters::S0, ARMRegisters::S0, tmp); - else if ((tmp = ARMAssembler::getOp2(-imm.m_value)) != ARMAssembler::InvalidImmediate) - m_assembler.subs(ARMRegisters::S0, ARMRegisters::S0, tmp); - else { - m_assembler.adds(ARMRegisters::S0, ARMRegisters::S0, m_assembler.getImm(imm.m_value, ARMRegisters::S1)); - move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); - } - m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S0, ARMRegisters::S1, 0); - - m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S0, ARMRegisters::S1, sizeof(ARMWord)); - if (imm.m_value >= 0) - m_assembler.adc(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); - else - m_assembler.sbc(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); - m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S0, ARMRegisters::S1, sizeof(ARMWord)); - } - - void sub32(TrustedImm32 imm, AbsoluteAddress address) - { - m_assembler.ldrUniqueImmediate(ARMRegisters::S1, reinterpret_cast(address.m_ptr)); - m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); - sub32(imm, ARMRegisters::S1); - m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address.m_ptr)); - m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); - } - - void load32(const void* address, RegisterID dest) - { - m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); - m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, ARMRegisters::S0, 0); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - load32(left.m_ptr, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) - { - load32(left.m_ptr, ARMRegisters::S1); - return branch32(cond, ARMRegisters::S1, right); - } - - void relativeTableJump(RegisterID index, int scale) - { - ASSERT(scale >= 0 && scale <= 31); - m_assembler.add(ARMRegisters::pc, ARMRegisters::pc, m_assembler.lsl(index, scale)); - - // NOP the default prefetching - m_assembler.mov(ARMRegisters::r0, ARMRegisters::r0); - } - - Call call() - { - ensureSpace(2 * sizeof(ARMWord), sizeof(ARMWord)); - m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true); - return Call(m_assembler.blx(ARMRegisters::S1), Call::Linkable); - } - - Call tailRecursiveCall() - { - return Call::fromTailJump(jump()); - } - - Call makeTailRecursiveCall(Jump oldJump) - { - return Call::fromTailJump(oldJump); - } - - DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) - { - DataLabelPtr dataLabel(this); - m_assembler.ldrUniqueImmediate(dest, reinterpret_cast(initialValue.m_value)); - return dataLabel; - } - - Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S1); - Jump jump = branch32(cond, left, ARMRegisters::S1, true); - return jump; - } - - Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - load32(left, ARMRegisters::S1); - dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S0); - Jump jump = branch32(cond, ARMRegisters::S0, ARMRegisters::S1, true); - return jump; - } - - DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) - { - DataLabelPtr dataLabel = moveWithPatch(initialValue, ARMRegisters::S1); - store32(ARMRegisters::S1, address); - return dataLabel; - } - - DataLabelPtr storePtrWithPatch(ImplicitAddress address) - { - return storePtrWithPatch(TrustedImmPtr(0), address); - } - - // Floating point operators - static bool supportsFloatingPoint() - { - return s_isVFPPresent; - } - - static bool supportsFloatingPointTruncate() - { - return false; - } - - static bool supportsFloatingPointSqrt() - { - return s_isVFPPresent; - } - static bool supportsFloatingPointAbs() { return false; } - - void loadFloat(BaseIndex address, FPRegisterID dest) - { - m_assembler.baseIndexTransferFloat(ARMAssembler::LoadFloat, dest, address.base, address.index, static_cast(address.scale), address.offset); - } - - void loadDouble(ImplicitAddress address, FPRegisterID dest) - { - m_assembler.dataTransferFloat(ARMAssembler::LoadDouble, dest, address.base, address.offset); - } - - void loadDouble(BaseIndex address, FPRegisterID dest) - { - m_assembler.baseIndexTransferFloat(ARMAssembler::LoadDouble, dest, address.base, address.index, static_cast(address.scale), address.offset); - } - - void loadDouble(const void* address, FPRegisterID dest) - { - move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); - m_assembler.doubleDtrUp(ARMAssembler::LoadDouble, dest, ARMRegisters::S0, 0); - } - - void storeFloat(FPRegisterID src, BaseIndex address) - { - m_assembler.baseIndexTransferFloat(ARMAssembler::StoreFloat, src, address.base, address.index, static_cast(address.scale), address.offset); - } - - void storeDouble(FPRegisterID src, ImplicitAddress address) - { - m_assembler.dataTransferFloat(ARMAssembler::StoreDouble, src, address.base, address.offset); - } - - void storeDouble(FPRegisterID src, BaseIndex address) - { - m_assembler.baseIndexTransferFloat(ARMAssembler::StoreDouble, src, address.base, address.index, static_cast(address.scale), address.offset); - } - - void storeDouble(FPRegisterID src, const void* address) - { - move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); - m_assembler.dataTransferFloat(ARMAssembler::StoreDouble, src, ARMRegisters::S0, 0); - } - - void moveDouble(FPRegisterID src, FPRegisterID dest) - { - if (src != dest) - m_assembler.vmov_f64(dest, src); - } - - void addDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vadd_f64(dest, dest, src); - } - - void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vadd_f64(dest, op1, op2); - } - - void addDouble(Address src, FPRegisterID dest) - { - loadDouble(src, ARMRegisters::SD0); - addDouble(ARMRegisters::SD0, dest); - } - - void addDouble(AbsoluteAddress address, FPRegisterID dest) - { - loadDouble(address.m_ptr, ARMRegisters::SD0); - addDouble(ARMRegisters::SD0, dest); - } - - void divDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vdiv_f64(dest, dest, src); - } - - void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vdiv_f64(dest, op1, op2); - } - - void divDouble(Address src, FPRegisterID dest) - { - ASSERT_NOT_REACHED(); // Untested - loadDouble(src, ARMRegisters::SD0); - divDouble(ARMRegisters::SD0, dest); - } - - void subDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vsub_f64(dest, dest, src); - } - - void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vsub_f64(dest, op1, op2); - } - - void subDouble(Address src, FPRegisterID dest) - { - loadDouble(src, ARMRegisters::SD0); - subDouble(ARMRegisters::SD0, dest); - } - - void mulDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vmul_f64(dest, dest, src); - } - - void mulDouble(Address src, FPRegisterID dest) - { - loadDouble(src, ARMRegisters::SD0); - mulDouble(ARMRegisters::SD0, dest); - } - - void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vmul_f64(dest, op1, op2); - } - - void sqrtDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vsqrt_f64(dest, src); - } - - void absDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vabs_f64(dest, src); - } - - void negateDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vneg_f64(dest, src); - } - - void convertInt32ToDouble(RegisterID src, FPRegisterID dest) - { - m_assembler.vmov_vfp32(dest << 1, src); - m_assembler.vcvt_f64_s32(dest, dest << 1); - } - - void convertInt32ToDouble(Address src, FPRegisterID dest) - { - load32(src, ARMRegisters::S1); - convertInt32ToDouble(ARMRegisters::S1, dest); - } - - void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) - { - move(TrustedImmPtr(src.m_ptr), ARMRegisters::S1); - load32(Address(ARMRegisters::S1), ARMRegisters::S1); - convertInt32ToDouble(ARMRegisters::S1, dest); - } - - void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) - { - m_assembler.vcvt_f64_f32(dst, src); - } - - void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) - { - m_assembler.vcvt_f32_f64(dst, src); - } - - Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) - { - m_assembler.vcmp_f64(left, right); - m_assembler.vmrs_apsr(); - if (cond & DoubleConditionBitSpecial) - m_assembler.cmp(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::VS); - return Jump(m_assembler.jmp(static_cast(cond & ~DoubleConditionMask))); - } - - // Truncates 'src' to an integer, and places the resulting 'dest'. - // If the result is not representable as a 32 bit value, branch. - // May also branch for some values that are representable in 32 bits - // (specifically, in this case, INT_MIN). - enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; - Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) - { - truncateDoubleToInt32(src, dest); - - m_assembler.add(ARMRegisters::S0, dest, ARMAssembler::getOp2Byte(1)); - m_assembler.bic(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(1)); - - ARMWord w = ARMAssembler::getOp2(0x80000000); - ASSERT(w != ARMAssembler::InvalidImmediate); - m_assembler.cmp(ARMRegisters::S0, w); - return Jump(m_assembler.jmp(branchType == BranchIfTruncateFailed ? ARMAssembler::EQ : ARMAssembler::NE)); - } - - Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) - { - truncateDoubleToUint32(src, dest); - - m_assembler.add(ARMRegisters::S0, dest, ARMAssembler::getOp2Byte(1)); - m_assembler.bic(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(1)); - - m_assembler.cmp(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); - return Jump(m_assembler.jmp(branchType == BranchIfTruncateFailed ? ARMAssembler::EQ : ARMAssembler::NE)); - } - - // Result is undefined if the value is outside of the integer range. - void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) - { - m_assembler.vcvt_s32_f64(ARMRegisters::SD0 << 1, src); - m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); - } - - void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) - { - m_assembler.vcvt_u32_f64(ARMRegisters::SD0 << 1, src); - m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); - } - - // Convert 'src' to an integer, and places the resulting 'dest'. - // If the result is not representable as a 32 bit value, branch. - // May also branch for some values that are representable in 32 bits - // (specifically, in this case, 0). - void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) - { - m_assembler.vcvt_s32_f64(ARMRegisters::SD0 << 1, src); - m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); - - // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. - m_assembler.vcvt_f64_s32(ARMRegisters::SD0, ARMRegisters::SD0 << 1); - failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, ARMRegisters::SD0)); - - // If the result is zero, it might have been -0.0, and 0.0 equals to -0.0 - failureCases.append(branchTest32(Zero, dest)); - } - - Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) - { - m_assembler.mov(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); - convertInt32ToDouble(ARMRegisters::S0, scratch); - return branchDouble(DoubleNotEqual, reg, scratch); - } - - Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) - { - m_assembler.mov(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); - convertInt32ToDouble(ARMRegisters::S0, scratch); - return branchDouble(DoubleEqualOrUnordered, reg, scratch); - } - - // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. - static RelationalCondition invert(RelationalCondition cond) - { - ASSERT((static_cast(cond & 0x0fffffff)) == 0 && static_cast(cond) < static_cast(ARMAssembler::AL)); - return static_cast(cond ^ 0x10000000); - } - - void nop() - { - m_assembler.nop(); - } - - static FunctionPtr readCallTarget(CodeLocationCall call) - { - return FunctionPtr(reinterpret_cast(ARMAssembler::readCallTarget(call.dataLocation()))); - } - - static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) - { - ARMAssembler::replaceWithJump(instructionStart.dataLocation(), destination.dataLocation()); - } - - static ptrdiff_t maxJumpReplacementSize() - { - ARMAssembler::maxJumpReplacementSize(); - return 0; - } - - static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } - - static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) - { - UNREACHABLE_FOR_PLATFORM(); - return CodeLocationLabel(); - } - - static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) - { - return label.labelAtOffset(0); - } - - static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID reg, void* initialValue) - { - ARMAssembler::revertJump(instructionStart.dataLocation(), reg, reinterpret_cast(initialValue) & 0xffff); - } - - static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) - { - UNREACHABLE_FOR_PLATFORM(); - } - -protected: - ARMAssembler::Condition ARMCondition(RelationalCondition cond) - { - return static_cast(cond); - } - - ARMAssembler::Condition ARMCondition(ResultCondition cond) - { - return static_cast(cond); - } - - void ensureSpace(int insnSpace, int constSpace) - { - m_assembler.ensureSpace(insnSpace, constSpace); - } - - int sizeOfConstantPool() - { - return m_assembler.sizeOfConstantPool(); - } - - void call32(RegisterID base, int32_t offset) - { - load32(Address(base, offset), ARMRegisters::S1); - m_assembler.blx(ARMRegisters::S1); - } - -private: - friend class LinkBuffer; - friend class RepatchBuffer; - - void internalCompare32(RegisterID left, TrustedImm32 right) - { - ARMWord tmp = (static_cast(right.m_value) == 0x80000000) ? ARMAssembler::InvalidImmediate : m_assembler.getOp2(-right.m_value); - if (tmp != ARMAssembler::InvalidImmediate) - m_assembler.cmn(left, tmp); - else - m_assembler.cmp(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); - } - - static void linkCall(void* code, Call call, FunctionPtr function) - { - ARMAssembler::linkCall(code, call.m_label, function.value()); - } - - static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) - { - ARMAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } - - static void repatchCall(CodeLocationCall call, FunctionPtr destination) - { - ARMAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } - - static const bool s_isVFPPresent; -}; - -} - -#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) - -#endif // MacroAssemblerARM_h diff --git a/3rdparty/masm/assembler/MacroAssemblerARMv7.h b/3rdparty/masm/assembler/MacroAssemblerARMv7.h deleted file mode 100644 index 8d7a3a69aa..0000000000 --- a/3rdparty/masm/assembler/MacroAssemblerARMv7.h +++ /dev/null @@ -1,1903 +0,0 @@ -/* - * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. - * Copyright (C) 2010 University of Szeged - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssemblerARMv7_h -#define MacroAssemblerARMv7_h - -#if ENABLE(ASSEMBLER) - -#include "ARMv7Assembler.h" -#include "AbstractMacroAssembler.h" - -namespace JSC { - -class MacroAssemblerARMv7 : public AbstractMacroAssembler { - // FIXME: switch dataTempRegister & addressTempRegister, or possibly use r7? - // - dTR is likely used more than aTR, and we'll get better instruction - // encoding if it's in the low 8 registers. - static const RegisterID dataTempRegister = ARMRegisters::ip; - static const RegisterID addressTempRegister = ARMRegisters::r3; - - static const ARMRegisters::FPDoubleRegisterID fpTempRegister = ARMRegisters::d7; - inline ARMRegisters::FPSingleRegisterID fpTempRegisterAsSingle() { return ARMRegisters::asSingle(fpTempRegister); } - -public: - MacroAssemblerARMv7() - : m_makeJumpPatchable(false) - { - } - - typedef ARMv7Assembler::LinkRecord LinkRecord; - typedef ARMv7Assembler::JumpType JumpType; - typedef ARMv7Assembler::JumpLinkType JumpLinkType; - - static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) - { - return value >= -255 && value <= 255; - } - - Vector& jumpsToLink() { return m_assembler.jumpsToLink(); } - void* unlinkedCode() { return m_assembler.unlinkedCode(); } - bool canCompact(JumpType jumpType) { return m_assembler.canCompact(jumpType); } - JumpLinkType computeJumpType(JumpType jumpType, const uint8_t* from, const uint8_t* to) { return m_assembler.computeJumpType(jumpType, from, to); } - JumpLinkType computeJumpType(LinkRecord& record, const uint8_t* from, const uint8_t* to) { return m_assembler.computeJumpType(record, from, to); } - void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset) {return m_assembler.recordLinkOffsets(regionStart, regionEnd, offset); } - int jumpSizeDelta(JumpType jumpType, JumpLinkType jumpLinkType) { return m_assembler.jumpSizeDelta(jumpType, jumpLinkType); } - void link(LinkRecord& record, uint8_t* from, uint8_t* to) { return m_assembler.link(record, from, to); } - - struct ArmAddress { - enum AddressType { - HasOffset, - HasIndex, - } type; - RegisterID base; - union { - int32_t offset; - struct { - RegisterID index; - Scale scale; - }; - } u; - - explicit ArmAddress(RegisterID base, int32_t offset = 0) - : type(HasOffset) - , base(base) - { - u.offset = offset; - } - - explicit ArmAddress(RegisterID base, RegisterID index, Scale scale = TimesOne) - : type(HasIndex) - , base(base) - { - u.index = index; - u.scale = scale; - } - }; - -public: - typedef ARMRegisters::FPDoubleRegisterID FPRegisterID; - - static const Scale ScalePtr = TimesFour; - - enum RelationalCondition { - Equal = ARMv7Assembler::ConditionEQ, - NotEqual = ARMv7Assembler::ConditionNE, - Above = ARMv7Assembler::ConditionHI, - AboveOrEqual = ARMv7Assembler::ConditionHS, - Below = ARMv7Assembler::ConditionLO, - BelowOrEqual = ARMv7Assembler::ConditionLS, - GreaterThan = ARMv7Assembler::ConditionGT, - GreaterThanOrEqual = ARMv7Assembler::ConditionGE, - LessThan = ARMv7Assembler::ConditionLT, - LessThanOrEqual = ARMv7Assembler::ConditionLE - }; - - enum ResultCondition { - Overflow = ARMv7Assembler::ConditionVS, - Signed = ARMv7Assembler::ConditionMI, - Zero = ARMv7Assembler::ConditionEQ, - NonZero = ARMv7Assembler::ConditionNE - }; - - enum DoubleCondition { - // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. - DoubleEqual = ARMv7Assembler::ConditionEQ, - DoubleNotEqual = ARMv7Assembler::ConditionVC, // Not the right flag! check for this & handle differently. - DoubleGreaterThan = ARMv7Assembler::ConditionGT, - DoubleGreaterThanOrEqual = ARMv7Assembler::ConditionGE, - DoubleLessThan = ARMv7Assembler::ConditionLO, - DoubleLessThanOrEqual = ARMv7Assembler::ConditionLS, - // If either operand is NaN, these conditions always evaluate to true. - DoubleEqualOrUnordered = ARMv7Assembler::ConditionVS, // Not the right flag! check for this & handle differently. - DoubleNotEqualOrUnordered = ARMv7Assembler::ConditionNE, - DoubleGreaterThanOrUnordered = ARMv7Assembler::ConditionHI, - DoubleGreaterThanOrEqualOrUnordered = ARMv7Assembler::ConditionHS, - DoubleLessThanOrUnordered = ARMv7Assembler::ConditionLT, - DoubleLessThanOrEqualOrUnordered = ARMv7Assembler::ConditionLE, - }; - - static const RegisterID stackPointerRegister = ARMRegisters::sp; - static const RegisterID linkRegister = ARMRegisters::lr; - - // Integer arithmetic operations: - // - // Operations are typically two operand - operation(source, srcDst) - // For many operations the source may be an TrustedImm32, the srcDst operand - // may often be a memory location (explictly described using an Address - // object). - - void add32(RegisterID src, RegisterID dest) - { - m_assembler.add(dest, dest, src); - } - - void add32(TrustedImm32 imm, RegisterID dest) - { - add32(imm, dest, dest); - } - - void add32(AbsoluteAddress src, RegisterID dest) - { - load32(src.m_ptr, dataTempRegister); - add32(dataTempRegister, dest); - } - - void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.add(dest, src, armImm); - else { - move(imm, dataTempRegister); - m_assembler.add(dest, src, dataTempRegister); - } - } - - void add32(TrustedImm32 imm, Address address) - { - load32(address, dataTempRegister); - - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.add(dataTempRegister, dataTempRegister, armImm); - else { - // Hrrrm, since dataTempRegister holds the data loaded, - // use addressTempRegister to hold the immediate. - move(imm, addressTempRegister); - m_assembler.add(dataTempRegister, dataTempRegister, addressTempRegister); - } - - store32(dataTempRegister, address); - } - - void add32(Address src, RegisterID dest) - { - load32(src, dataTempRegister); - add32(dataTempRegister, dest); - } - - void add32(TrustedImm32 imm, AbsoluteAddress address) - { - load32(address.m_ptr, dataTempRegister); - - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.add(dataTempRegister, dataTempRegister, armImm); - else { - // Hrrrm, since dataTempRegister holds the data loaded, - // use addressTempRegister to hold the immediate. - move(imm, addressTempRegister); - m_assembler.add(dataTempRegister, dataTempRegister, addressTempRegister); - } - - store32(dataTempRegister, address.m_ptr); - } - - void add64(TrustedImm32 imm, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), addressTempRegister); - - m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(0)); - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.add_S(dataTempRegister, dataTempRegister, armImm); - else { - move(imm, addressTempRegister); - m_assembler.add_S(dataTempRegister, dataTempRegister, addressTempRegister); - move(TrustedImmPtr(address.m_ptr), addressTempRegister); - } - m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(0)); - - m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(4)); - m_assembler.adc(dataTempRegister, dataTempRegister, ARMThumbImmediate::makeEncodedImm(imm.m_value >> 31)); - m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(4)); - } - - void and32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.ARM_and(dest, op1, op2); - } - - void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.ARM_and(dest, src, armImm); - else { - move(imm, dataTempRegister); - m_assembler.ARM_and(dest, src, dataTempRegister); - } - } - - void and32(RegisterID src, RegisterID dest) - { - and32(dest, src, dest); - } - - void and32(TrustedImm32 imm, RegisterID dest) - { - and32(imm, dest, dest); - } - - void countLeadingZeros32(RegisterID src, RegisterID dest) - { - m_assembler.clz(dest, src); - } - - void lshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) - { - // Clamp the shift to the range 0..31 - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); - ASSERT(armImm.isValid()); - m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); - - m_assembler.lsl(dest, src, dataTempRegister); - } - - void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.lsl(dest, src, imm.m_value & 0x1f); - } - - void lshift32(RegisterID shiftAmount, RegisterID dest) - { - lshift32(dest, shiftAmount, dest); - } - - void lshift32(TrustedImm32 imm, RegisterID dest) - { - lshift32(dest, imm, dest); - } - - void mul32(RegisterID src, RegisterID dest) - { - m_assembler.smull(dest, dataTempRegister, dest, src); - } - - void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(imm, dataTempRegister); - m_assembler.smull(dest, dataTempRegister, src, dataTempRegister); - } - - void neg32(RegisterID srcDest) - { - m_assembler.neg(srcDest, srcDest); - } - - void or32(RegisterID src, RegisterID dest) - { - m_assembler.orr(dest, dest, src); - } - - void or32(RegisterID src, AbsoluteAddress dest) - { - move(TrustedImmPtr(dest.m_ptr), addressTempRegister); - load32(addressTempRegister, dataTempRegister); - or32(src, dataTempRegister); - store32(dataTempRegister, addressTempRegister); - } - - void or32(TrustedImm32 imm, RegisterID dest) - { - or32(imm, dest, dest); - } - - void or32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.orr(dest, op1, op2); - } - - void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.orr(dest, src, armImm); - else { - move(imm, dataTempRegister); - m_assembler.orr(dest, src, dataTempRegister); - } - } - - void rshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) - { - // Clamp the shift to the range 0..31 - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); - ASSERT(armImm.isValid()); - m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); - - m_assembler.asr(dest, src, dataTempRegister); - } - - void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.asr(dest, src, imm.m_value & 0x1f); - } - - void rshift32(RegisterID shiftAmount, RegisterID dest) - { - rshift32(dest, shiftAmount, dest); - } - - void rshift32(TrustedImm32 imm, RegisterID dest) - { - rshift32(dest, imm, dest); - } - - void urshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) - { - // Clamp the shift to the range 0..31 - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); - ASSERT(armImm.isValid()); - m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); - - m_assembler.lsr(dest, src, dataTempRegister); - } - - void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.lsr(dest, src, imm.m_value & 0x1f); - } - - void urshift32(RegisterID shiftAmount, RegisterID dest) - { - urshift32(dest, shiftAmount, dest); - } - - void urshift32(TrustedImm32 imm, RegisterID dest) - { - urshift32(dest, imm, dest); - } - - void sub32(RegisterID src, RegisterID dest) - { - m_assembler.sub(dest, dest, src); - } - - void sub32(TrustedImm32 imm, RegisterID dest) - { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.sub(dest, dest, armImm); - else { - move(imm, dataTempRegister); - m_assembler.sub(dest, dest, dataTempRegister); - } - } - - void sub32(TrustedImm32 imm, Address address) - { - load32(address, dataTempRegister); - - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.sub(dataTempRegister, dataTempRegister, armImm); - else { - // Hrrrm, since dataTempRegister holds the data loaded, - // use addressTempRegister to hold the immediate. - move(imm, addressTempRegister); - m_assembler.sub(dataTempRegister, dataTempRegister, addressTempRegister); - } - - store32(dataTempRegister, address); - } - - void sub32(Address src, RegisterID dest) - { - load32(src, dataTempRegister); - sub32(dataTempRegister, dest); - } - - void sub32(TrustedImm32 imm, AbsoluteAddress address) - { - load32(address.m_ptr, dataTempRegister); - - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.sub(dataTempRegister, dataTempRegister, armImm); - else { - // Hrrrm, since dataTempRegister holds the data loaded, - // use addressTempRegister to hold the immediate. - move(imm, addressTempRegister); - m_assembler.sub(dataTempRegister, dataTempRegister, addressTempRegister); - } - - store32(dataTempRegister, address.m_ptr); - } - - void xor32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.eor(dest, op1, op2); - } - - void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (imm.m_value == -1) { - m_assembler.mvn(dest, src); - return; - } - - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.eor(dest, src, armImm); - else { - move(imm, dataTempRegister); - m_assembler.eor(dest, src, dataTempRegister); - } - } - - void xor32(RegisterID src, RegisterID dest) - { - xor32(dest, src, dest); - } - - void xor32(TrustedImm32 imm, RegisterID dest) - { - if (imm.m_value == -1) - m_assembler.mvn(dest, dest); - else - xor32(imm, dest, dest); - } - - - // Memory access operations: - // - // Loads are of the form load(address, destination) and stores of the form - // store(source, address). The source for a store may be an TrustedImm32. Address - // operand objects to loads and store will be implicitly constructed if a - // register is passed. - -private: - void load32(ArmAddress address, RegisterID dest) - { - if (address.type == ArmAddress::HasIndex) - m_assembler.ldr(dest, address.base, address.u.index, address.u.scale); - else if (address.u.offset >= 0) { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); - ASSERT(armImm.isValid()); - m_assembler.ldr(dest, address.base, armImm); - } else { - ASSERT(address.u.offset >= -255); - m_assembler.ldr(dest, address.base, address.u.offset, true, false); - } - } - - void load16(ArmAddress address, RegisterID dest) - { - if (address.type == ArmAddress::HasIndex) - m_assembler.ldrh(dest, address.base, address.u.index, address.u.scale); - else if (address.u.offset >= 0) { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); - ASSERT(armImm.isValid()); - m_assembler.ldrh(dest, address.base, armImm); - } else { - ASSERT(address.u.offset >= -255); - m_assembler.ldrh(dest, address.base, address.u.offset, true, false); - } - } - - void load16Signed(ArmAddress address, RegisterID dest) - { - ASSERT(address.type == ArmAddress::HasIndex); - m_assembler.ldrsh(dest, address.base, address.u.index, address.u.scale); - } - - void load8(ArmAddress address, RegisterID dest) - { - if (address.type == ArmAddress::HasIndex) - m_assembler.ldrb(dest, address.base, address.u.index, address.u.scale); - else if (address.u.offset >= 0) { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); - ASSERT(armImm.isValid()); - m_assembler.ldrb(dest, address.base, armImm); - } else { - ASSERT(address.u.offset >= -255); - m_assembler.ldrb(dest, address.base, address.u.offset, true, false); - } - } - - void load8Signed(ArmAddress address, RegisterID dest) - { - ASSERT(address.type == ArmAddress::HasIndex); - m_assembler.ldrsb(dest, address.base, address.u.index, address.u.scale); - } - -protected: - void store32(RegisterID src, ArmAddress address) - { - if (address.type == ArmAddress::HasIndex) - m_assembler.str(src, address.base, address.u.index, address.u.scale); - else if (address.u.offset >= 0) { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); - ASSERT(armImm.isValid()); - m_assembler.str(src, address.base, armImm); - } else { - ASSERT(address.u.offset >= -255); - m_assembler.str(src, address.base, address.u.offset, true, false); - } - } - -private: - void store8(RegisterID src, ArmAddress address) - { - if (address.type == ArmAddress::HasIndex) - m_assembler.strb(src, address.base, address.u.index, address.u.scale); - else if (address.u.offset >= 0) { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); - ASSERT(armImm.isValid()); - m_assembler.strb(src, address.base, armImm); - } else { - ASSERT(address.u.offset >= -255); - m_assembler.strb(src, address.base, address.u.offset, true, false); - } - } - - void store16(RegisterID src, ArmAddress address) - { - if (address.type == ArmAddress::HasIndex) - m_assembler.strh(src, address.base, address.u.index, address.u.scale); - else if (address.u.offset >= 0) { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); - ASSERT(armImm.isValid()); - m_assembler.strh(src, address.base, armImm); - } else { - ASSERT(address.u.offset >= -255); - m_assembler.strh(src, address.base, address.u.offset, true, false); - } - } - -public: - void load32(ImplicitAddress address, RegisterID dest) - { - load32(setupArmAddress(address), dest); - } - - void load32(BaseIndex address, RegisterID dest) - { - load32(setupArmAddress(address), dest); - } - - void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) - { - load32(setupArmAddress(address), dest); - } - - void load16Unaligned(BaseIndex address, RegisterID dest) - { - load16(setupArmAddress(address), dest); - } - - void load32(const void* address, RegisterID dest) - { - move(TrustedImmPtr(address), addressTempRegister); - m_assembler.ldr(dest, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); - } - - ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) - { - ConvertibleLoadLabel result(this); - ASSERT(address.offset >= 0 && address.offset <= 255); - m_assembler.ldrWide8BitImmediate(dest, address.base, address.offset); - return result; - } - - void load8(ImplicitAddress address, RegisterID dest) - { - load8(setupArmAddress(address), dest); - } - - void load8Signed(ImplicitAddress, RegisterID) - { - UNREACHABLE_FOR_PLATFORM(); - } - - void load8(BaseIndex address, RegisterID dest) - { - load8(setupArmAddress(address), dest); - } - - void load8Signed(BaseIndex address, RegisterID dest) - { - load8Signed(setupArmAddress(address), dest); - } - - DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) - { - DataLabel32 label = moveWithPatch(TrustedImm32(address.offset), dataTempRegister); - load32(ArmAddress(address.base, dataTempRegister), dest); - return label; - } - - DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - padBeforePatch(); - - RegisterID base = address.base; - - DataLabelCompact label(this); - ASSERT(isCompactPtrAlignedAddressOffset(address.offset)); - - m_assembler.ldr(dest, base, address.offset, true, false); - return label; - } - - void load16(BaseIndex address, RegisterID dest) - { - m_assembler.ldrh(dest, makeBaseIndexBase(address), address.index, address.scale); - } - - void load16Signed(BaseIndex address, RegisterID dest) - { - load16Signed(setupArmAddress(address), dest); - } - - void load16(ImplicitAddress address, RegisterID dest) - { - ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.offset); - if (armImm.isValid()) - m_assembler.ldrh(dest, address.base, armImm); - else { - move(TrustedImm32(address.offset), dataTempRegister); - m_assembler.ldrh(dest, address.base, dataTempRegister); - } - } - - void load16Signed(ImplicitAddress, RegisterID) - { - UNREACHABLE_FOR_PLATFORM(); - } - - DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) - { - DataLabel32 label = moveWithPatch(TrustedImm32(address.offset), dataTempRegister); - store32(src, ArmAddress(address.base, dataTempRegister)); - return label; - } - - void store32(RegisterID src, ImplicitAddress address) - { - store32(src, setupArmAddress(address)); - } - - void store32(RegisterID src, BaseIndex address) - { - store32(src, setupArmAddress(address)); - } - - void store32(TrustedImm32 imm, ImplicitAddress address) - { - move(imm, dataTempRegister); - store32(dataTempRegister, setupArmAddress(address)); - } - - void store32(TrustedImm32 imm, BaseIndex address) - { - move(imm, dataTempRegister); - store32(dataTempRegister, setupArmAddress(address)); - } - - void store32(RegisterID src, const void* address) - { - move(TrustedImmPtr(address), addressTempRegister); - m_assembler.str(src, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); - } - - void store32(TrustedImm32 imm, const void* address) - { - move(imm, dataTempRegister); - store32(dataTempRegister, address); - } - - void store8(RegisterID src, BaseIndex address) - { - store8(src, setupArmAddress(address)); - } - - void store8(RegisterID src, void* address) - { - move(TrustedImmPtr(address), addressTempRegister); - store8(src, ArmAddress(addressTempRegister, 0)); - } - - void store8(TrustedImm32 imm, void* address) - { - move(imm, dataTempRegister); - store8(dataTempRegister, address); - } - - void store16(RegisterID src, BaseIndex address) - { - store16(src, setupArmAddress(address)); - } - - // Possibly clobbers src, but not on this architecture. - void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) - { - m_assembler.vmov(dest1, dest2, src); - } - - void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) - { - UNUSED_PARAM(scratch); - m_assembler.vmov(dest, src1, src2); - } - -#if ENABLE(JIT_CONSTANT_BLINDING) - static bool shouldBlindForSpecificArch(uint32_t value) - { - ARMThumbImmediate immediate = ARMThumbImmediate::makeEncodedImm(value); - - // Couldn't be encoded as an immediate, so assume it's untrusted. - if (!immediate.isValid()) - return true; - - // If we can encode the immediate, we have less than 16 attacker - // controlled bits. - if (immediate.isEncodedImm()) - return false; - - // Don't let any more than 12 bits of an instruction word - // be controlled by an attacker. - return !immediate.isUInt12(); - } -#endif - - // Floating-point operations: - - static bool supportsFloatingPoint() { return true; } - static bool supportsFloatingPointTruncate() { return true; } - static bool supportsFloatingPointSqrt() { return true; } - static bool supportsFloatingPointAbs() { return true; } - - void loadDouble(ImplicitAddress address, FPRegisterID dest) - { - RegisterID base = address.base; - int32_t offset = address.offset; - - // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. - if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { - add32(TrustedImm32(offset), base, addressTempRegister); - base = addressTempRegister; - offset = 0; - } - - m_assembler.vldr(dest, base, offset); - } - - void loadFloat(ImplicitAddress address, FPRegisterID dest) - { - RegisterID base = address.base; - int32_t offset = address.offset; - - // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. - if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { - add32(TrustedImm32(offset), base, addressTempRegister); - base = addressTempRegister; - offset = 0; - } - - m_assembler.flds(ARMRegisters::asSingle(dest), base, offset); - } - - void loadDouble(BaseIndex address, FPRegisterID dest) - { - move(address.index, addressTempRegister); - lshift32(TrustedImm32(address.scale), addressTempRegister); - add32(address.base, addressTempRegister); - loadDouble(Address(addressTempRegister, address.offset), dest); - } - - void loadFloat(BaseIndex address, FPRegisterID dest) - { - move(address.index, addressTempRegister); - lshift32(TrustedImm32(address.scale), addressTempRegister); - add32(address.base, addressTempRegister); - loadFloat(Address(addressTempRegister, address.offset), dest); - } - - void moveDouble(FPRegisterID src, FPRegisterID dest) - { - if (src != dest) - m_assembler.vmov(dest, src); - } - - void loadDouble(const void* address, FPRegisterID dest) - { - move(TrustedImmPtr(address), addressTempRegister); - m_assembler.vldr(dest, addressTempRegister, 0); - } - - void storeDouble(FPRegisterID src, ImplicitAddress address) - { - RegisterID base = address.base; - int32_t offset = address.offset; - - // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. - if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { - add32(TrustedImm32(offset), base, addressTempRegister); - base = addressTempRegister; - offset = 0; - } - - m_assembler.vstr(src, base, offset); - } - - void storeFloat(FPRegisterID src, ImplicitAddress address) - { - RegisterID base = address.base; - int32_t offset = address.offset; - - // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. - if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { - add32(TrustedImm32(offset), base, addressTempRegister); - base = addressTempRegister; - offset = 0; - } - - m_assembler.fsts(ARMRegisters::asSingle(src), base, offset); - } - - void storeDouble(FPRegisterID src, const void* address) - { - move(TrustedImmPtr(address), addressTempRegister); - storeDouble(src, addressTempRegister); - } - - void storeDouble(FPRegisterID src, BaseIndex address) - { - move(address.index, addressTempRegister); - lshift32(TrustedImm32(address.scale), addressTempRegister); - add32(address.base, addressTempRegister); - storeDouble(src, Address(addressTempRegister, address.offset)); - } - - void storeFloat(FPRegisterID src, BaseIndex address) - { - move(address.index, addressTempRegister); - lshift32(TrustedImm32(address.scale), addressTempRegister); - add32(address.base, addressTempRegister); - storeFloat(src, Address(addressTempRegister, address.offset)); - } - - void addDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vadd(dest, dest, src); - } - - void addDouble(Address src, FPRegisterID dest) - { - loadDouble(src, fpTempRegister); - addDouble(fpTempRegister, dest); - } - - void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vadd(dest, op1, op2); - } - - void addDouble(AbsoluteAddress address, FPRegisterID dest) - { - loadDouble(address.m_ptr, fpTempRegister); - m_assembler.vadd(dest, dest, fpTempRegister); - } - - void divDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vdiv(dest, dest, src); - } - - void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vdiv(dest, op1, op2); - } - - void subDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vsub(dest, dest, src); - } - - void subDouble(Address src, FPRegisterID dest) - { - loadDouble(src, fpTempRegister); - subDouble(fpTempRegister, dest); - } - - void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vsub(dest, op1, op2); - } - - void mulDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vmul(dest, dest, src); - } - - void mulDouble(Address src, FPRegisterID dest) - { - loadDouble(src, fpTempRegister); - mulDouble(fpTempRegister, dest); - } - - void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.vmul(dest, op1, op2); - } - - void sqrtDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vsqrt(dest, src); - } - - void absDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vabs(dest, src); - } - - void negateDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.vneg(dest, src); - } - - void convertInt32ToDouble(RegisterID src, FPRegisterID dest) - { - m_assembler.vmov(fpTempRegister, src, src); - m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); - } - - void convertInt32ToDouble(Address address, FPRegisterID dest) - { - // Fixme: load directly into the fpr! - load32(address, dataTempRegister); - m_assembler.vmov(fpTempRegister, dataTempRegister, dataTempRegister); - m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); - } - - void convertInt32ToDouble(AbsoluteAddress address, FPRegisterID dest) - { - // Fixme: load directly into the fpr! - load32(address.m_ptr, dataTempRegister); - m_assembler.vmov(fpTempRegister, dataTempRegister, dataTempRegister); - m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); - } - - void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) - { - m_assembler.vcvtds(dst, ARMRegisters::asSingle(src)); - } - - void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) - { - m_assembler.vcvtsd(ARMRegisters::asSingle(dst), src); - } - - Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) - { - m_assembler.vcmp(left, right); - m_assembler.vmrs(); - - if (cond == DoubleNotEqual) { - // ConditionNE jumps if NotEqual *or* unordered - force the unordered cases not to jump. - Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); - Jump result = makeBranch(ARMv7Assembler::ConditionNE); - unordered.link(this); - return result; - } - if (cond == DoubleEqualOrUnordered) { - Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); - Jump notEqual = makeBranch(ARMv7Assembler::ConditionNE); - unordered.link(this); - // We get here if either unordered or equal. - Jump result = jump(); - notEqual.link(this); - return result; - } - return makeBranch(cond); - } - - enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; - Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) - { - // Convert into dest. - m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); - m_assembler.vmov(dest, fpTempRegisterAsSingle()); - - // Calculate 2x dest. If the value potentially underflowed, it will have - // clamped to 0x80000000, so 2x dest is zero in this case. In the case of - // overflow the result will be equal to -2. - Jump underflow = branchAdd32(Zero, dest, dest, dataTempRegister); - Jump noOverflow = branch32(NotEqual, dataTempRegister, TrustedImm32(-2)); - - // For BranchIfTruncateSuccessful, we branch if 'noOverflow' jumps. - underflow.link(this); - if (branchType == BranchIfTruncateSuccessful) - return noOverflow; - - // We'll reach the current point in the code on failure, so plant a - // jump here & link the success case. - Jump failure = jump(); - noOverflow.link(this); - return failure; - } - - Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) - { - m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); - m_assembler.vmov(dest, fpTempRegisterAsSingle()); - - Jump overflow = branch32(Equal, dest, TrustedImm32(0x7fffffff)); - Jump success = branch32(GreaterThanOrEqual, dest, TrustedImm32(0)); - overflow.link(this); - - if (branchType == BranchIfTruncateSuccessful) - return success; - - Jump failure = jump(); - success.link(this); - return failure; - } - - // Result is undefined if the value is outside of the integer range. - void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) - { - m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); - m_assembler.vmov(dest, fpTempRegisterAsSingle()); - } - - void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) - { - m_assembler.vcvt_floatingPointToUnsigned(fpTempRegisterAsSingle(), src); - m_assembler.vmov(dest, fpTempRegisterAsSingle()); - } - - // Convert 'src' to an integer, and places the resulting 'dest'. - // If the result is not representable as a 32 bit value, branch. - // May also branch for some values that are representable in 32 bits - // (specifically, in this case, 0). - void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID) - { - m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); - m_assembler.vmov(dest, fpTempRegisterAsSingle()); - - // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. - m_assembler.vcvt_signedToFloatingPoint(fpTempRegister, fpTempRegisterAsSingle()); - failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, fpTempRegister)); - - // If the result is zero, it might have been -0.0, and the double comparison won't catch this! - failureCases.append(branchTest32(Zero, dest)); - } - - Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID) - { - m_assembler.vcmpz(reg); - m_assembler.vmrs(); - Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); - Jump result = makeBranch(ARMv7Assembler::ConditionNE); - unordered.link(this); - return result; - } - - Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID) - { - m_assembler.vcmpz(reg); - m_assembler.vmrs(); - Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); - Jump notEqual = makeBranch(ARMv7Assembler::ConditionNE); - unordered.link(this); - // We get here if either unordered or equal. - Jump result = jump(); - notEqual.link(this); - return result; - } - - // Stack manipulation operations: - // - // The ABI is assumed to provide a stack abstraction to memory, - // containing machine word sized units of data. Push and pop - // operations add and remove a single register sized unit of data - // to or from the stack. Peek and poke operations read or write - // values on the stack, without moving the current stack position. - - void pop(RegisterID dest) - { - // store postindexed with writeback - m_assembler.ldr(dest, ARMRegisters::sp, sizeof(void*), false, true); - } - - void push(RegisterID src) - { - // store preindexed with writeback - m_assembler.str(src, ARMRegisters::sp, -sizeof(void*), true, true); - } - - void push(Address address) - { - load32(address, dataTempRegister); - push(dataTempRegister); - } - - void push(TrustedImm32 imm) - { - move(imm, dataTempRegister); - push(dataTempRegister); - } - - // Register move operations: - // - // Move values in registers. - - void move(TrustedImm32 imm, RegisterID dest) - { - uint32_t value = imm.m_value; - - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(value); - - if (armImm.isValid()) - m_assembler.mov(dest, armImm); - else if ((armImm = ARMThumbImmediate::makeEncodedImm(~value)).isValid()) - m_assembler.mvn(dest, armImm); - else { - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(value)); - if (value & 0xffff0000) - m_assembler.movt(dest, ARMThumbImmediate::makeUInt16(value >> 16)); - } - } - - void move(RegisterID src, RegisterID dest) - { - if (src != dest) - m_assembler.mov(dest, src); - } - - void move(TrustedImmPtr imm, RegisterID dest) - { - move(TrustedImm32(imm), dest); - } - - void swap(RegisterID reg1, RegisterID reg2) - { - move(reg1, dataTempRegister); - move(reg2, reg1); - move(dataTempRegister, reg2); - } - - void signExtend32ToPtr(RegisterID src, RegisterID dest) - { - move(src, dest); - } - - void zeroExtend32ToPtr(RegisterID src, RegisterID dest) - { - move(src, dest); - } - - // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. - static RelationalCondition invert(RelationalCondition cond) - { - return static_cast(cond ^ 1); - } - - void nop() - { - m_assembler.nop(); - } - - static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) - { - ARMv7Assembler::replaceWithJump(instructionStart.dataLocation(), destination.dataLocation()); - } - - static ptrdiff_t maxJumpReplacementSize() - { - return ARMv7Assembler::maxJumpReplacementSize(); - } - - // Forwards / external control flow operations: - // - // This set of jump and conditional branch operations return a Jump - // object which may linked at a later point, allow forwards jump, - // or jumps that will require external linkage (after the code has been - // relocated). - // - // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge - // respecitvely, for unsigned comparisons the names b, a, be, and ae are - // used (representing the names 'below' and 'above'). - // - // Operands to the comparision are provided in the expected order, e.g. - // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when - // treated as a signed 32bit value, is less than or equal to 5. - // - // jz and jnz test whether the first operand is equal to zero, and take - // an optional second operand of a mask under which to perform the test. -private: - - // Should we be using TEQ for equal/not-equal? - void compare32(RegisterID left, TrustedImm32 right) - { - int32_t imm = right.m_value; - if (!imm) - m_assembler.tst(left, left); - else { - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm); - if (armImm.isValid()) - m_assembler.cmp(left, armImm); - else if ((armImm = ARMThumbImmediate::makeEncodedImm(-imm)).isValid()) - m_assembler.cmn(left, armImm); - else { - move(TrustedImm32(imm), dataTempRegister); - m_assembler.cmp(left, dataTempRegister); - } - } - } - - void test32(RegisterID reg, TrustedImm32 mask) - { - int32_t imm = mask.m_value; - - if (imm == -1) - m_assembler.tst(reg, reg); - else { - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm); - if (armImm.isValid()) - m_assembler.tst(reg, armImm); - else { - move(mask, dataTempRegister); - m_assembler.tst(reg, dataTempRegister); - } - } - } - -public: - Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) - { - m_assembler.cmp(left, right); - return Jump(makeBranch(cond)); - } - - Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) - { - compare32(left, right); - return Jump(makeBranch(cond)); - } - - Jump branch32(RelationalCondition cond, RegisterID left, Address right) - { - load32(right, dataTempRegister); - return branch32(cond, left, dataTempRegister); - } - - Jump branch32(RelationalCondition cond, Address left, RegisterID right) - { - load32(left, dataTempRegister); - return branch32(cond, dataTempRegister, right); - } - - Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) - { - // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ - load32(left, addressTempRegister); - return branch32(cond, addressTempRegister, right); - } - - Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ - load32(left, addressTempRegister); - return branch32(cond, addressTempRegister, right); - } - - Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ - load32WithUnalignedHalfWords(left, addressTempRegister); - return branch32(cond, addressTempRegister, right); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - load32(left.m_ptr, dataTempRegister); - return branch32(cond, dataTempRegister, right); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) - { - // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ - load32(left.m_ptr, addressTempRegister); - return branch32(cond, addressTempRegister, right); - } - - Jump branch8(RelationalCondition cond, RegisterID left, TrustedImm32 right) - { - compare32(left, right); - return Jump(makeBranch(cond)); - } - - Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) - { - ASSERT(!(0xffffff00 & right.m_value)); - // use addressTempRegister incase the branch8 we call uses dataTempRegister. :-/ - load8(left, addressTempRegister); - return branch8(cond, addressTempRegister, right); - } - - Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - ASSERT(!(0xffffff00 & right.m_value)); - // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ - load8(left, addressTempRegister); - return branch32(cond, addressTempRegister, right); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) - { - m_assembler.tst(reg, mask); - return Jump(makeBranch(cond)); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - test32(reg, mask); - return Jump(makeBranch(cond)); - } - - Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - // use addressTempRegister incase the branchTest32 we call uses dataTempRegister. :-/ - load32(address, addressTempRegister); - return branchTest32(cond, addressTempRegister, mask); - } - - Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - // use addressTempRegister incase the branchTest32 we call uses dataTempRegister. :-/ - load32(address, addressTempRegister); - return branchTest32(cond, addressTempRegister, mask); - } - - Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - // use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/ - load8(address, addressTempRegister); - return branchTest32(cond, addressTempRegister, mask); - } - - Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - // use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/ - move(TrustedImmPtr(address.m_ptr), addressTempRegister); - load8(Address(addressTempRegister), addressTempRegister); - return branchTest32(cond, addressTempRegister, mask); - } - - void jump(RegisterID target) - { - m_assembler.bx(target); - } - - // Address is a memory location containing the address to jump to - void jump(Address address) - { - load32(address, dataTempRegister); - m_assembler.bx(dataTempRegister); - } - - void jump(AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), dataTempRegister); - load32(Address(dataTempRegister), dataTempRegister); - m_assembler.bx(dataTempRegister); - } - - - // Arithmetic control flow operations: - // - // This set of conditional branch operations branch based - // on the result of an arithmetic operation. The operation - // is performed as normal, storing the result. - // - // * jz operations branch if the result is zero. - // * jo operations branch if the (signed) arithmetic - // operation caused an overflow to occur. - - Jump branchAdd32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.add_S(dest, op1, op2); - return Jump(makeBranch(cond)); - } - - Jump branchAdd32(ResultCondition cond, RegisterID op1, TrustedImm32 imm, RegisterID dest) - { - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.add_S(dest, op1, armImm); - else { - move(imm, dataTempRegister); - m_assembler.add_S(dest, op1, dataTempRegister); - } - return Jump(makeBranch(cond)); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchAdd32(cond, dest, src, dest); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - return branchAdd32(cond, dest, imm, dest); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) - { - // Move the high bits of the address into addressTempRegister, - // and load the value into dataTempRegister. - move(TrustedImmPtr(dest.m_ptr), addressTempRegister); - m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); - - // Do the add. - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.add_S(dataTempRegister, dataTempRegister, armImm); - else { - // If the operand does not fit into an immediate then load it temporarily - // into addressTempRegister; since we're overwriting addressTempRegister - // we'll need to reload it with the high bits of the address afterwards. - move(imm, addressTempRegister); - m_assembler.add_S(dataTempRegister, dataTempRegister, addressTempRegister); - move(TrustedImmPtr(dest.m_ptr), addressTempRegister); - } - - // Store the result. - m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); - - return Jump(makeBranch(cond)); - } - - Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) - { - m_assembler.smull(dest, dataTempRegister, src1, src2); - - if (cond == Overflow) { - m_assembler.asr(addressTempRegister, dest, 31); - return branch32(NotEqual, addressTempRegister, dataTempRegister); - } - - return branchTest32(cond, dest); - } - - Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchMul32(cond, src, dest, dest); - } - - Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(imm, dataTempRegister); - return branchMul32(cond, dataTempRegister, src, dest); - } - - Jump branchNeg32(ResultCondition cond, RegisterID srcDest) - { - ARMThumbImmediate zero = ARMThumbImmediate::makeUInt12(0); - m_assembler.sub_S(srcDest, zero, srcDest); - return Jump(makeBranch(cond)); - } - - Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) - { - m_assembler.orr_S(dest, dest, src); - return Jump(makeBranch(cond)); - } - - Jump branchSub32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.sub_S(dest, op1, op2); - return Jump(makeBranch(cond)); - } - - Jump branchSub32(ResultCondition cond, RegisterID op1, TrustedImm32 imm, RegisterID dest) - { - ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); - if (armImm.isValid()) - m_assembler.sub_S(dest, op1, armImm); - else { - move(imm, dataTempRegister); - m_assembler.sub_S(dest, op1, dataTempRegister); - } - return Jump(makeBranch(cond)); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) - { - return branchSub32(cond, dest, src, dest); - } - - Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - return branchSub32(cond, dest, imm, dest); - } - - void relativeTableJump(RegisterID index, int scale) - { - ASSERT(scale >= 0 && scale <= 31); - - // dataTempRegister will point after the jump if index register contains zero - move(ARMRegisters::pc, dataTempRegister); - m_assembler.add(dataTempRegister, dataTempRegister, ARMThumbImmediate::makeEncodedImm(9)); - - ShiftTypeAndAmount shift(SRType_LSL, scale); - m_assembler.add(dataTempRegister, dataTempRegister, index, shift); - jump(dataTempRegister); - } - - // Miscellaneous operations: - - void breakpoint(uint8_t imm = 0) - { - m_assembler.bkpt(imm); - } - - ALWAYS_INLINE Call nearCall() - { - moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); - return Call(m_assembler.blx(dataTempRegister), Call::LinkableNear); - } - - ALWAYS_INLINE Call call() - { - moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); - return Call(m_assembler.blx(dataTempRegister), Call::Linkable); - } - - ALWAYS_INLINE Call call(RegisterID target) - { - return Call(m_assembler.blx(target), Call::None); - } - - ALWAYS_INLINE Call call(Address address) - { - load32(address, dataTempRegister); - return Call(m_assembler.blx(dataTempRegister), Call::None); - } - - ALWAYS_INLINE void ret() - { - m_assembler.bx(linkRegister); - } - - void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) - { - m_assembler.cmp(left, right); - m_assembler.it(armV7Condition(cond), false); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); - } - - void compare32(RelationalCondition cond, Address left, RegisterID right, RegisterID dest) - { - load32(left, dataTempRegister); - compare32(cond, dataTempRegister, right, dest); - } - - void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) - { - load8(left, addressTempRegister); - compare32(cond, addressTempRegister, right, dest); - } - - void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - compare32(left, right); - m_assembler.it(armV7Condition(cond), false); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); - } - - // FIXME: - // The mask should be optional... paerhaps the argument order should be - // dest-src, operations always have a dest? ... possibly not true, considering - // asm ops like test, or pseudo ops like pop(). - void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - load32(address, dataTempRegister); - test32(dataTempRegister, mask); - m_assembler.it(armV7Condition(cond), false); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); - } - - void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - load8(address, dataTempRegister); - test32(dataTempRegister, mask); - m_assembler.it(armV7Condition(cond), false); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); - m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); - } - - ALWAYS_INLINE DataLabel32 moveWithPatch(TrustedImm32 imm, RegisterID dst) - { - padBeforePatch(); - moveFixedWidthEncoding(imm, dst); - return DataLabel32(this); - } - - ALWAYS_INLINE DataLabelPtr moveWithPatch(TrustedImmPtr imm, RegisterID dst) - { - padBeforePatch(); - moveFixedWidthEncoding(TrustedImm32(imm), dst); - return DataLabelPtr(this); - } - - ALWAYS_INLINE Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - dataLabel = moveWithPatch(initialRightValue, dataTempRegister); - return branch32(cond, left, dataTempRegister); - } - - ALWAYS_INLINE Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - load32(left, addressTempRegister); - dataLabel = moveWithPatch(initialRightValue, dataTempRegister); - return branch32(cond, addressTempRegister, dataTempRegister); - } - - PatchableJump patchableBranchPtr(RelationalCondition cond, Address left, TrustedImmPtr right = TrustedImmPtr(0)) - { - m_makeJumpPatchable = true; - Jump result = branch32(cond, left, TrustedImm32(right)); - m_makeJumpPatchable = false; - return PatchableJump(result); - } - - PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - m_makeJumpPatchable = true; - Jump result = branchTest32(cond, reg, mask); - m_makeJumpPatchable = false; - return PatchableJump(result); - } - - PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) - { - m_makeJumpPatchable = true; - Jump result = branch32(cond, reg, imm); - m_makeJumpPatchable = false; - return PatchableJump(result); - } - - PatchableJump patchableBranchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - m_makeJumpPatchable = true; - Jump result = branchPtrWithPatch(cond, left, dataLabel, initialRightValue); - m_makeJumpPatchable = false; - return PatchableJump(result); - } - - PatchableJump patchableJump() - { - padBeforePatch(); - m_makeJumpPatchable = true; - Jump result = jump(); - m_makeJumpPatchable = false; - return PatchableJump(result); - } - - ALWAYS_INLINE DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) - { - DataLabelPtr label = moveWithPatch(initialValue, dataTempRegister); - store32(dataTempRegister, address); - return label; - } - ALWAYS_INLINE DataLabelPtr storePtrWithPatch(ImplicitAddress address) { return storePtrWithPatch(TrustedImmPtr(0), address); } - - - ALWAYS_INLINE Call tailRecursiveCall() - { - // Like a normal call, but don't link. - moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); - return Call(m_assembler.bx(dataTempRegister), Call::Linkable); - } - - ALWAYS_INLINE Call makeTailRecursiveCall(Jump oldJump) - { - oldJump.link(this); - return tailRecursiveCall(); - } - - - int executableOffsetFor(int location) - { - return m_assembler.executableOffsetFor(location); - } - - static FunctionPtr readCallTarget(CodeLocationCall call) - { - return FunctionPtr(reinterpret_cast(ARMv7Assembler::readCallTarget(call.dataLocation()))); - } - - static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } - - static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) - { - const unsigned twoWordOpSize = 4; - return label.labelAtOffset(-twoWordOpSize * 2); - } - - static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) - { - ARMv7Assembler::revertJumpTo_movT3(instructionStart.dataLocation(), dataTempRegister, ARMThumbImmediate::makeUInt16(reinterpret_cast(initialValue) & 0xffff)); - } - - static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) - { - UNREACHABLE_FOR_PLATFORM(); - return CodeLocationLabel(); - } - - static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel, Address, void*) - { - UNREACHABLE_FOR_PLATFORM(); - } - -protected: - ALWAYS_INLINE Jump jump() - { - m_assembler.label(); // Force nop-padding if we're in the middle of a watchpoint. - moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); - return Jump(m_assembler.bx(dataTempRegister), m_makeJumpPatchable ? ARMv7Assembler::JumpNoConditionFixedSize : ARMv7Assembler::JumpNoCondition); - } - - ALWAYS_INLINE Jump makeBranch(ARMv7Assembler::Condition cond) - { - m_assembler.label(); // Force nop-padding if we're in the middle of a watchpoint. - m_assembler.it(cond, true, true); - moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); - return Jump(m_assembler.bx(dataTempRegister), m_makeJumpPatchable ? ARMv7Assembler::JumpConditionFixedSize : ARMv7Assembler::JumpCondition, cond); - } - ALWAYS_INLINE Jump makeBranch(RelationalCondition cond) { return makeBranch(armV7Condition(cond)); } - ALWAYS_INLINE Jump makeBranch(ResultCondition cond) { return makeBranch(armV7Condition(cond)); } - ALWAYS_INLINE Jump makeBranch(DoubleCondition cond) { return makeBranch(armV7Condition(cond)); } - - ArmAddress setupArmAddress(BaseIndex address) - { - if (address.offset) { - ARMThumbImmediate imm = ARMThumbImmediate::makeUInt12OrEncodedImm(address.offset); - if (imm.isValid()) - m_assembler.add(addressTempRegister, address.base, imm); - else { - move(TrustedImm32(address.offset), addressTempRegister); - m_assembler.add(addressTempRegister, addressTempRegister, address.base); - } - - return ArmAddress(addressTempRegister, address.index, address.scale); - } else - return ArmAddress(address.base, address.index, address.scale); - } - - ArmAddress setupArmAddress(Address address) - { - if ((address.offset >= -0xff) && (address.offset <= 0xfff)) - return ArmAddress(address.base, address.offset); - - move(TrustedImm32(address.offset), addressTempRegister); - return ArmAddress(address.base, addressTempRegister); - } - - ArmAddress setupArmAddress(ImplicitAddress address) - { - if ((address.offset >= -0xff) && (address.offset <= 0xfff)) - return ArmAddress(address.base, address.offset); - - move(TrustedImm32(address.offset), addressTempRegister); - return ArmAddress(address.base, addressTempRegister); - } - - RegisterID makeBaseIndexBase(BaseIndex address) - { - if (!address.offset) - return address.base; - - ARMThumbImmediate imm = ARMThumbImmediate::makeUInt12OrEncodedImm(address.offset); - if (imm.isValid()) - m_assembler.add(addressTempRegister, address.base, imm); - else { - move(TrustedImm32(address.offset), addressTempRegister); - m_assembler.add(addressTempRegister, addressTempRegister, address.base); - } - - return addressTempRegister; - } - - void moveFixedWidthEncoding(TrustedImm32 imm, RegisterID dst) - { - uint32_t value = imm.m_value; - m_assembler.movT3(dst, ARMThumbImmediate::makeUInt16(value & 0xffff)); - m_assembler.movt(dst, ARMThumbImmediate::makeUInt16(value >> 16)); - } - - ARMv7Assembler::Condition armV7Condition(RelationalCondition cond) - { - return static_cast(cond); - } - - ARMv7Assembler::Condition armV7Condition(ResultCondition cond) - { - return static_cast(cond); - } - - ARMv7Assembler::Condition armV7Condition(DoubleCondition cond) - { - return static_cast(cond); - } - -private: - friend class LinkBuffer; - friend class RepatchBuffer; - - static void linkCall(void* code, Call call, FunctionPtr function) - { - ARMv7Assembler::linkCall(code, call.m_label, function.value()); - } - - static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) - { - ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } - - static void repatchCall(CodeLocationCall call, FunctionPtr destination) - { - ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } - - bool m_makeJumpPatchable; -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // MacroAssemblerARMv7_h diff --git a/3rdparty/masm/assembler/MacroAssemblerCodeRef.h b/3rdparty/masm/assembler/MacroAssemblerCodeRef.h deleted file mode 100644 index c2af24060a..0000000000 --- a/3rdparty/masm/assembler/MacroAssemblerCodeRef.h +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright (C) 2009, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssemblerCodeRef_h -#define MacroAssemblerCodeRef_h - -#include "Disassembler.h" -#include "ExecutableAllocator.h" -#include "LLIntData.h" -#include -#include -#include -#include - -// ASSERT_VALID_CODE_POINTER checks that ptr is a non-null pointer, and that it is a valid -// instruction address on the platform (for example, check any alignment requirements). -#if CPU(ARM_THUMB2) -// ARM/thumb instructions must be 16-bit aligned, but all code pointers to be loaded -// into the processor are decorated with the bottom bit set, indicating that this is -// thumb code (as oposed to 32-bit traditional ARM). The first test checks for both -// decorated and undectorated null, and the second test ensures that the pointer is -// decorated. -#define ASSERT_VALID_CODE_POINTER(ptr) \ - ASSERT(reinterpret_cast(ptr) & ~1); \ - ASSERT(reinterpret_cast(ptr) & 1) -#define ASSERT_VALID_CODE_OFFSET(offset) \ - ASSERT(!(offset & 1)) // Must be multiple of 2. -#else -#define ASSERT_VALID_CODE_POINTER(ptr) \ - ASSERT(ptr) -#define ASSERT_VALID_CODE_OFFSET(offset) // Anything goes! -#endif - -#if CPU(X86) && OS(WINDOWS) -#define CALLING_CONVENTION_IS_STDCALL 1 -#ifndef CDECL -#if COMPILER(MSVC) -#define CDECL __cdecl -#else -#define CDECL __attribute__ ((__cdecl)) -#endif // COMPILER(MSVC) -#endif // CDECL -#else -#define CALLING_CONVENTION_IS_STDCALL 0 -#endif - -#if CPU(X86) -#define HAS_FASTCALL_CALLING_CONVENTION 1 -#ifndef FASTCALL -#if COMPILER(MSVC) -#define FASTCALL __fastcall -#else -#define FASTCALL __attribute__ ((fastcall)) -#endif // COMPILER(MSVC) -#endif // FASTCALL -#else -#define HAS_FASTCALL_CALLING_CONVENTION 0 -#endif // CPU(X86) - -namespace JSC { - -// FunctionPtr: -// -// FunctionPtr should be used to wrap pointers to C/C++ functions in JSC -// (particularly, the stub functions). -class FunctionPtr { -public: - FunctionPtr() - : m_value(0) - { - } - - template - FunctionPtr(returnType(*value)()) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType(*value)(argType1)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType(*value)(argType1, argType2)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType(*value)(argType1, argType2, argType3)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType(*value)(argType1, argType2, argType3, argType4)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType(*value)(argType1, argType2, argType3, argType4, argType5)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - -// MSVC doesn't seem to treat functions with different calling conventions as -// different types; these methods already defined for fastcall, below. -#if CALLING_CONVENTION_IS_STDCALL && !OS(WINDOWS) - - template - FunctionPtr(returnType (CDECL *value)()) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (CDECL *value)(argType1)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (CDECL *value)(argType1, argType2)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (CDECL *value)(argType1, argType2, argType3)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (CDECL *value)(argType1, argType2, argType3, argType4)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } -#endif - -#if HAS_FASTCALL_CALLING_CONVENTION - - template - FunctionPtr(returnType (FASTCALL *value)()) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (FASTCALL *value)(argType1)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (FASTCALL *value)(argType1, argType2)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (FASTCALL *value)(argType1, argType2, argType3)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - template - FunctionPtr(returnType (FASTCALL *value)(argType1, argType2, argType3, argType4)) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } -#endif - - template - explicit FunctionPtr(FunctionType* value) - // Using a C-ctyle cast here to avoid compiler error on RVTC: - // Error: #694: reinterpret_cast cannot cast away const or other type qualifiers - // (I guess on RVTC function pointers have a different constness to GCC/MSVC?) - : m_value((void*)value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - void* value() const { return m_value; } - void* executableAddress() const { return m_value; } - - -private: - void* m_value; -}; - -// ReturnAddressPtr: -// -// ReturnAddressPtr should be used to wrap return addresses generated by processor -// 'call' instructions exectued in JIT code. We use return addresses to look up -// exception and optimization information, and to repatch the call instruction -// that is the source of the return address. -class ReturnAddressPtr { -public: - ReturnAddressPtr() - : m_value(0) - { - } - - explicit ReturnAddressPtr(void* value) - : m_value(value) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - explicit ReturnAddressPtr(FunctionPtr function) - : m_value(function.value()) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - void* value() const { return m_value; } - -private: - void* m_value; -}; - -// MacroAssemblerCodePtr: -// -// MacroAssemblerCodePtr should be used to wrap pointers to JIT generated code. -class MacroAssemblerCodePtr { -public: - MacroAssemblerCodePtr() - : m_value(0) - { - } - - explicit MacroAssemblerCodePtr(void* value) -#if CPU(ARM_THUMB2) - // Decorate the pointer as a thumb code pointer. - : m_value(reinterpret_cast(value) + 1) -#else - : m_value(value) -#endif - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - static MacroAssemblerCodePtr createFromExecutableAddress(void* value) - { - ASSERT_VALID_CODE_POINTER(value); - MacroAssemblerCodePtr result; - result.m_value = value; - return result; - } - -#if ENABLE(LLINT) - static MacroAssemblerCodePtr createLLIntCodePtr(LLIntCode codeId) - { - return createFromExecutableAddress(LLInt::getCodePtr(codeId)); - } -#endif - - explicit MacroAssemblerCodePtr(ReturnAddressPtr ra) - : m_value(ra.value()) - { - ASSERT_VALID_CODE_POINTER(m_value); - } - - void* executableAddress() const { return m_value; } -#if CPU(ARM_THUMB2) - // To use this pointer as a data address remove the decoration. - void* dataLocation() const { ASSERT_VALID_CODE_POINTER(m_value); return reinterpret_cast(m_value) - 1; } -#else - void* dataLocation() const { ASSERT_VALID_CODE_POINTER(m_value); return m_value; } -#endif - - bool operator!() const - { - return !m_value; - } - -private: - void* m_value; -}; - -// MacroAssemblerCodeRef: -// -// A reference to a section of JIT generated code. A CodeRef consists of a -// pointer to the code, and a ref pointer to the pool from within which it -// was allocated. -class MacroAssemblerCodeRef { -private: - // This is private because it's dangerous enough that we want uses of it - // to be easy to find - hence the static create method below. - explicit MacroAssemblerCodeRef(MacroAssemblerCodePtr codePtr) - : m_codePtr(codePtr) - { - ASSERT(m_codePtr); - } - -public: - MacroAssemblerCodeRef() - { - } - - MacroAssemblerCodeRef(PassRefPtr executableMemory) - : m_codePtr(executableMemory->start()) - , m_executableMemory(executableMemory) - { - ASSERT(m_executableMemory->isManaged()); - ASSERT(m_executableMemory->start()); - ASSERT(m_codePtr); - } - - // Use this only when you know that the codePtr refers to code that is - // already being kept alive through some other means. Typically this means - // that codePtr is immortal. - static MacroAssemblerCodeRef createSelfManagedCodeRef(MacroAssemblerCodePtr codePtr) - { - return MacroAssemblerCodeRef(codePtr); - } - -#if ENABLE(LLINT) - // Helper for creating self-managed code refs from LLInt. - static MacroAssemblerCodeRef createLLIntCodeRef(LLIntCode codeId) - { - return createSelfManagedCodeRef(MacroAssemblerCodePtr::createFromExecutableAddress(LLInt::getCodePtr(codeId))); - } -#endif - - ExecutableMemoryHandle* executableMemory() const - { - return m_executableMemory.get(); - } - - MacroAssemblerCodePtr code() const - { - return m_codePtr; - } - - size_t size() const - { - if (!m_executableMemory) - return 0; - return m_executableMemory->sizeInBytes(); - } - - bool tryToDisassemble(const char* prefix) const - { - return JSC::tryToDisassemble(m_codePtr, size(), prefix, WTF::dataFile()); - } - - bool operator!() const { return !m_codePtr; } - -private: - MacroAssemblerCodePtr m_codePtr; - RefPtr m_executableMemory; -}; - -} // namespace JSC - -#endif // MacroAssemblerCodeRef_h diff --git a/3rdparty/masm/assembler/MacroAssemblerMIPS.h b/3rdparty/masm/assembler/MacroAssemblerMIPS.h deleted file mode 100644 index 3ab2553001..0000000000 --- a/3rdparty/masm/assembler/MacroAssemblerMIPS.h +++ /dev/null @@ -1,2316 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All rights reserved. - * Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY MIPS TECHNOLOGIES, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MIPS TECHNOLOGIES, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssemblerMIPS_h -#define MacroAssemblerMIPS_h - -#if ENABLE(ASSEMBLER) && CPU(MIPS) - -#include "AbstractMacroAssembler.h" -#include "MIPSAssembler.h" - -namespace JSC { - -class MacroAssemblerMIPS : public AbstractMacroAssembler { -public: - typedef MIPSRegisters::FPRegisterID FPRegisterID; - - MacroAssemblerMIPS() - : m_fixedWidth(false) - { - } - - static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) - { - return value >= -2147483647 - 1 && value <= 2147483647; - } - - static const Scale ScalePtr = TimesFour; - - // For storing immediate number - static const RegisterID immTempRegister = MIPSRegisters::t0; - // For storing data loaded from the memory - static const RegisterID dataTempRegister = MIPSRegisters::t1; - // For storing address base - static const RegisterID addrTempRegister = MIPSRegisters::t2; - // For storing compare result - static const RegisterID cmpTempRegister = MIPSRegisters::t3; - - // FP temp register - static const FPRegisterID fpTempRegister = MIPSRegisters::f16; - - static const int MaximumCompactPtrAlignedAddressOffset = 0x7FFFFFFF; - - enum RelationalCondition { - Equal, - NotEqual, - Above, - AboveOrEqual, - Below, - BelowOrEqual, - GreaterThan, - GreaterThanOrEqual, - LessThan, - LessThanOrEqual - }; - - enum ResultCondition { - Overflow, - Signed, - Zero, - NonZero - }; - - enum DoubleCondition { - DoubleEqual, - DoubleNotEqual, - DoubleGreaterThan, - DoubleGreaterThanOrEqual, - DoubleLessThan, - DoubleLessThanOrEqual, - DoubleEqualOrUnordered, - DoubleNotEqualOrUnordered, - DoubleGreaterThanOrUnordered, - DoubleGreaterThanOrEqualOrUnordered, - DoubleLessThanOrUnordered, - DoubleLessThanOrEqualOrUnordered - }; - - static const RegisterID stackPointerRegister = MIPSRegisters::sp; - static const RegisterID returnAddressRegister = MIPSRegisters::ra; - - // Integer arithmetic operations: - // - // Operations are typically two operand - operation(source, srcDst) - // For many operations the source may be an TrustedImm32, the srcDst operand - // may often be a memory location (explictly described using an Address - // object). - - void add32(RegisterID src, RegisterID dest) - { - m_assembler.addu(dest, dest, src); - } - - void add32(TrustedImm32 imm, RegisterID dest) - { - add32(imm, dest, dest); - } - - void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (imm.m_value >= -32768 && imm.m_value <= 32767 - && !m_fixedWidth) { - /* - addiu dest, src, imm - */ - m_assembler.addiu(dest, src, imm.m_value); - } else { - /* - li immTemp, imm - addu dest, src, immTemp - */ - move(imm, immTempRegister); - m_assembler.addu(dest, src, immTempRegister); - } - } - - void add32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - add32(imm, src, dest); - } - - void add32(TrustedImm32 imm, Address address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - lw dataTemp, offset(base) - li immTemp, imm - addu dataTemp, dataTemp, immTemp - sw dataTemp, offset(base) - */ - m_assembler.lw(dataTempRegister, address.base, address.offset); - if (imm.m_value >= -32768 && imm.m_value <= 32767 - && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, imm.m_value); - else { - move(imm, immTempRegister); - m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); - } - m_assembler.sw(dataTempRegister, address.base, address.offset); - } else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - lw dataTemp, (offset & 0xffff)(addrTemp) - li immtemp, imm - addu dataTemp, dataTemp, immTemp - sw dataTemp, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); - - if (imm.m_value >= -32768 && imm.m_value <= 32767 && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, imm.m_value); - else { - move(imm, immTempRegister); - m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); - } - m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); - } - } - - void add32(Address src, RegisterID dest) - { - load32(src, dataTempRegister); - add32(dataTempRegister, dest); - } - - void add32(AbsoluteAddress src, RegisterID dest) - { - load32(src.m_ptr, dataTempRegister); - add32(dataTempRegister, dest); - } - - void add32(RegisterID src, Address dest) - { - if (dest.offset >= -32768 && dest.offset <= 32767 && !m_fixedWidth) { - /* - lw dataTemp, offset(base) - addu dataTemp, dataTemp, src - sw dataTemp, offset(base) - */ - m_assembler.lw(dataTempRegister, dest.base, dest.offset); - m_assembler.addu(dataTempRegister, dataTempRegister, src); - m_assembler.sw(dataTempRegister, dest.base, dest.offset); - } else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - lw dataTemp, (offset & 0xffff)(addrTemp) - addu dataTemp, dataTemp, src - sw dataTemp, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (dest.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, dest.base); - m_assembler.lw(dataTempRegister, addrTempRegister, dest.offset); - m_assembler.addu(dataTempRegister, dataTempRegister, src); - m_assembler.sw(dataTempRegister, addrTempRegister, dest.offset); - } - } - - void add32(TrustedImm32 imm, AbsoluteAddress address) - { - /* - li addrTemp, address - li immTemp, imm - lw cmpTemp, 0(addrTemp) - addu dataTemp, cmpTemp, immTemp - sw dataTemp, 0(addrTemp) - */ - move(TrustedImmPtr(address.m_ptr), addrTempRegister); - m_assembler.lw(cmpTempRegister, addrTempRegister, 0); - if (imm.m_value >= -32768 && imm.m_value <= 32767 && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, cmpTempRegister, imm.m_value); - else { - move(imm, immTempRegister); - m_assembler.addu(dataTempRegister, cmpTempRegister, immTempRegister); - } - m_assembler.sw(dataTempRegister, addrTempRegister, 0); - } - - void add64(TrustedImm32 imm, AbsoluteAddress address) - { - /* - add32(imm, address) - sltu immTemp, dataTemp, cmpTemp # set carry-in bit - lw dataTemp, 4(addrTemp) - addiu dataTemp, imm.m_value >> 31 ? -1 : 0 - addu dataTemp, dataTemp, immTemp - sw dataTemp, 4(addrTemp) - */ - add32(imm, address); - m_assembler.sltu(immTempRegister, dataTempRegister, cmpTempRegister); - m_assembler.lw(dataTempRegister, addrTempRegister, 4); - if (imm.m_value >> 31) - m_assembler.addiu(dataTempRegister, dataTempRegister, -1); - m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); - m_assembler.sw(dataTempRegister, addrTempRegister, 4); - } - - void and32(RegisterID src, RegisterID dest) - { - m_assembler.andInsn(dest, dest, src); - } - - void and32(TrustedImm32 imm, RegisterID dest) - { - if (!imm.m_value && !m_fixedWidth) - move(MIPSRegisters::zero, dest); - else if (imm.m_value > 0 && imm.m_value < 65535 && !m_fixedWidth) - m_assembler.andi(dest, dest, imm.m_value); - else { - /* - li immTemp, imm - and dest, dest, immTemp - */ - move(imm, immTempRegister); - m_assembler.andInsn(dest, dest, immTempRegister); - } - } - - void lshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.sll(dest, dest, imm.m_value); - } - - void lshift32(RegisterID shiftAmount, RegisterID dest) - { - m_assembler.sllv(dest, dest, shiftAmount); - } - - void mul32(RegisterID src, RegisterID dest) - { - m_assembler.mul(dest, dest, src); - } - - void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (!imm.m_value && !m_fixedWidth) - move(MIPSRegisters::zero, dest); - else if (imm.m_value == 1 && !m_fixedWidth) - move(src, dest); - else { - /* - li dataTemp, imm - mul dest, src, dataTemp - */ - move(imm, dataTempRegister); - m_assembler.mul(dest, src, dataTempRegister); - } - } - - void neg32(RegisterID srcDest) - { - m_assembler.subu(srcDest, MIPSRegisters::zero, srcDest); - } - - void or32(RegisterID src, RegisterID dest) - { - m_assembler.orInsn(dest, dest, src); - } - - void or32(RegisterID op1, RegisterID op2, RegisterID dest) - { - m_assembler.orInsn(dest, op1, op2); - } - - void or32(TrustedImm32 imm, RegisterID dest) - { - if (!imm.m_value && !m_fixedWidth) - return; - - if (imm.m_value > 0 && imm.m_value < 65535 - && !m_fixedWidth) { - m_assembler.ori(dest, dest, imm.m_value); - return; - } - - /* - li dataTemp, imm - or dest, dest, dataTemp - */ - move(imm, dataTempRegister); - m_assembler.orInsn(dest, dest, dataTempRegister); - } - - void or32(RegisterID src, AbsoluteAddress dest) - { - load32(dest.m_ptr, dataTempRegister); - m_assembler.orInsn(dataTempRegister, dataTempRegister, src); - store32(dataTempRegister, dest.m_ptr); - } - - void rshift32(RegisterID shiftAmount, RegisterID dest) - { - m_assembler.srav(dest, dest, shiftAmount); - } - - void rshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.sra(dest, dest, imm.m_value); - } - - void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - m_assembler.sra(dest, src, imm.m_value); - } - - void urshift32(RegisterID shiftAmount, RegisterID dest) - { - m_assembler.srlv(dest, dest, shiftAmount); - } - - void urshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.srl(dest, dest, imm.m_value); - } - - void sub32(RegisterID src, RegisterID dest) - { - m_assembler.subu(dest, dest, src); - } - - void sub32(TrustedImm32 imm, RegisterID dest) - { - if (imm.m_value >= -32767 && imm.m_value <= 32768 - && !m_fixedWidth) { - /* - addiu dest, src, imm - */ - m_assembler.addiu(dest, dest, -imm.m_value); - } else { - /* - li immTemp, imm - subu dest, src, immTemp - */ - move(imm, immTempRegister); - m_assembler.subu(dest, dest, immTempRegister); - } - } - - void sub32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - if (imm.m_value >= -32767 && imm.m_value <= 32768 - && !m_fixedWidth) { - /* - addiu dest, src, imm - */ - m_assembler.addiu(dest, src, -imm.m_value); - } else { - /* - li immTemp, imm - subu dest, src, immTemp - */ - move(imm, immTempRegister); - m_assembler.subu(dest, src, immTempRegister); - } - } - - void sub32(TrustedImm32 imm, Address address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - lw dataTemp, offset(base) - li immTemp, imm - subu dataTemp, dataTemp, immTemp - sw dataTemp, offset(base) - */ - m_assembler.lw(dataTempRegister, address.base, address.offset); - if (imm.m_value >= -32767 && imm.m_value <= 32768 && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); - else { - move(imm, immTempRegister); - m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); - } - m_assembler.sw(dataTempRegister, address.base, address.offset); - } else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - lw dataTemp, (offset & 0xffff)(addrTemp) - li immtemp, imm - subu dataTemp, dataTemp, immTemp - sw dataTemp, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); - - if (imm.m_value >= -32767 && imm.m_value <= 32768 - && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); - else { - move(imm, immTempRegister); - m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); - } - m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); - } - } - - void sub32(Address src, RegisterID dest) - { - load32(src, dataTempRegister); - sub32(dataTempRegister, dest); - } - - void sub32(TrustedImm32 imm, AbsoluteAddress address) - { - /* - li addrTemp, address - li immTemp, imm - lw dataTemp, 0(addrTemp) - subu dataTemp, dataTemp, immTemp - sw dataTemp, 0(addrTemp) - */ - move(TrustedImmPtr(address.m_ptr), addrTempRegister); - m_assembler.lw(dataTempRegister, addrTempRegister, 0); - - if (imm.m_value >= -32767 && imm.m_value <= 32768 && !m_fixedWidth) - m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); - else { - move(imm, immTempRegister); - m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); - } - m_assembler.sw(dataTempRegister, addrTempRegister, 0); - } - - void xor32(RegisterID src, RegisterID dest) - { - m_assembler.xorInsn(dest, dest, src); - } - - void xor32(TrustedImm32 imm, RegisterID dest) - { - if (imm.m_value == -1) { - m_assembler.nor(dest, dest, MIPSRegisters::zero); - return; - } - - /* - li immTemp, imm - xor dest, dest, immTemp - */ - move(imm, immTempRegister); - m_assembler.xorInsn(dest, dest, immTempRegister); - } - - void sqrtDouble(FPRegisterID src, FPRegisterID dst) - { - m_assembler.sqrtd(dst, src); - } - - void absDouble(FPRegisterID, FPRegisterID) - { - ASSERT_NOT_REACHED(); - } - - ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) - { - ConvertibleLoadLabel result(this); - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - lw dest, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lw(dest, addrTempRegister, address.offset); - return result; - } - - // Memory access operations: - // - // Loads are of the form load(address, destination) and stores of the form - // store(source, address). The source for a store may be an TrustedImm32. Address - // operand objects to loads and store will be implicitly constructed if a - // register is passed. - - /* Need to use zero-extened load byte for load8. */ - void load8(ImplicitAddress address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) - m_assembler.lbu(dest, address.base, address.offset); - else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - lbu dest, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lbu(dest, addrTempRegister, address.offset); - } - } - - void load8(BaseIndex address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lbu dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lbu(dest, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - lbu dest, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.lbu(dest, addrTempRegister, address.offset); - } - } - - void load8Signed(BaseIndex address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lb dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lb(dest, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - lb dest, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.lb(dest, addrTempRegister, address.offset); - } - } - - void load32(ImplicitAddress address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) - m_assembler.lw(dest, address.base, address.offset); - else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - lw dest, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lw(dest, addrTempRegister, address.offset); - } - } - - void load32(BaseIndex address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lw dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lw(dest, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - lw dest, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.lw(dest, addrTempRegister, address.offset); - } - } - - void load16Unaligned(BaseIndex address, RegisterID dest) - { - load16(address, dest); - } - - void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32764 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - (Big-Endian) - lwl dest, address.offset(addrTemp) - lwr dest, address.offset+3(addrTemp) - (Little-Endian) - lwl dest, address.offset+3(addrTemp) - lwr dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); -#if CPU(BIG_ENDIAN) - m_assembler.lwl(dest, addrTempRegister, address.offset); - m_assembler.lwr(dest, addrTempRegister, address.offset + 3); -#else - m_assembler.lwl(dest, addrTempRegister, address.offset + 3); - m_assembler.lwr(dest, addrTempRegister, address.offset); - -#endif - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, address.offset >> 16 - ori immTemp, immTemp, address.offset & 0xffff - addu addrTemp, addrTemp, immTemp - (Big-Endian) - lw dest, 0(at) - lw dest, 3(at) - (Little-Endian) - lw dest, 3(at) - lw dest, 0(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, address.offset >> 16); - m_assembler.ori(immTempRegister, immTempRegister, address.offset); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); -#if CPU(BIG_ENDIAN) - m_assembler.lwl(dest, addrTempRegister, 0); - m_assembler.lwr(dest, addrTempRegister, 3); -#else - m_assembler.lwl(dest, addrTempRegister, 3); - m_assembler.lwr(dest, addrTempRegister, 0); -#endif - } - } - - void load32(const void* address, RegisterID dest) - { - /* - li addrTemp, address - lw dest, 0(addrTemp) - */ - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.lw(dest, addrTempRegister, 0); - } - - DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) - { - m_fixedWidth = true; - /* - lui addrTemp, address.offset >> 16 - ori addrTemp, addrTemp, address.offset & 0xffff - addu addrTemp, addrTemp, address.base - lw dest, 0(addrTemp) - */ - DataLabel32 dataLabel(this); - move(TrustedImm32(address.offset), addrTempRegister); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lw(dest, addrTempRegister, 0); - m_fixedWidth = false; - return dataLabel; - } - - DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - DataLabelCompact dataLabel(this); - load32WithAddressOffsetPatch(address, dest); - return dataLabel; - } - - /* Need to use zero-extened load half-word for load16. */ - void load16(ImplicitAddress address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) - m_assembler.lhu(dest, address.base, address.offset); - else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - lhu dest, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lhu(dest, addrTempRegister, address.offset); - } - } - - /* Need to use zero-extened load half-word for load16. */ - void load16(BaseIndex address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lhu dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lhu(dest, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - lhu dest, (address.offset & 0xffff)(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.lhu(dest, addrTempRegister, address.offset); - } - } - - void load16Signed(BaseIndex address, RegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lh dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lh(dest, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - lh dest, (address.offset & 0xffff)(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.lh(dest, addrTempRegister, address.offset); - } - } - - DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) - { - m_fixedWidth = true; - /* - lui addrTemp, address.offset >> 16 - ori addrTemp, addrTemp, address.offset & 0xffff - addu addrTemp, addrTemp, address.base - sw src, 0(addrTemp) - */ - DataLabel32 dataLabel(this); - move(TrustedImm32(address.offset), addrTempRegister); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.sw(src, addrTempRegister, 0); - m_fixedWidth = false; - return dataLabel; - } - - void store8(RegisterID src, BaseIndex address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - sb src, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.sb(src, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - sb src, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.sb(src, addrTempRegister, address.offset); - } - } - - void store8(TrustedImm32 imm, void* address) - { - /* - li immTemp, imm - li addrTemp, address - sb src, 0(addrTemp) - */ - if (!imm.m_value && !m_fixedWidth) { - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.sb(MIPSRegisters::zero, addrTempRegister, 0); - } else { - move(imm, immTempRegister); - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.sb(immTempRegister, addrTempRegister, 0); - } - } - - void store16(RegisterID src, BaseIndex address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - sh src, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.sh(src, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - sh src, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.sh(src, addrTempRegister, address.offset); - } - } - - void store32(RegisterID src, ImplicitAddress address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) - m_assembler.sw(src, address.base, address.offset); - else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - sw src, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.sw(src, addrTempRegister, address.offset); - } - } - - void store32(RegisterID src, BaseIndex address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - sw src, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.sw(src, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - sw src, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.sw(src, addrTempRegister, address.offset); - } - } - - void store32(TrustedImm32 imm, ImplicitAddress address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - if (!imm.m_value) - m_assembler.sw(MIPSRegisters::zero, address.base, address.offset); - else { - move(imm, immTempRegister); - m_assembler.sw(immTempRegister, address.base, address.offset); - } - } else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - sw immTemp, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - if (!imm.m_value && !m_fixedWidth) - m_assembler.sw(MIPSRegisters::zero, addrTempRegister, address.offset); - else { - move(imm, immTempRegister); - m_assembler.sw(immTempRegister, addrTempRegister, address.offset); - } - } - } - - void store32(RegisterID src, const void* address) - { - /* - li addrTemp, address - sw src, 0(addrTemp) - */ - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.sw(src, addrTempRegister, 0); - } - - void store32(TrustedImm32 imm, const void* address) - { - /* - li immTemp, imm - li addrTemp, address - sw src, 0(addrTemp) - */ - if (!imm.m_value && !m_fixedWidth) { - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.sw(MIPSRegisters::zero, addrTempRegister, 0); - } else { - move(imm, immTempRegister); - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.sw(immTempRegister, addrTempRegister, 0); - } - } - - // Floating-point operations: - - static bool supportsFloatingPoint() - { -#if WTF_MIPS_DOUBLE_FLOAT - return true; -#else - return false; -#endif - } - - static bool supportsFloatingPointTruncate() - { -#if WTF_MIPS_DOUBLE_FLOAT && WTF_MIPS_ISA_AT_LEAST(2) - return true; -#else - return false; -#endif - } - - static bool supportsFloatingPointSqrt() - { -#if WTF_MIPS_DOUBLE_FLOAT && WTF_MIPS_ISA_AT_LEAST(2) - return true; -#else - return false; -#endif - } - static bool supportsFloatingPointAbs() { return false; } - - // Stack manipulation operations: - // - // The ABI is assumed to provide a stack abstraction to memory, - // containing machine word sized units of data. Push and pop - // operations add and remove a single register sized unit of data - // to or from the stack. Peek and poke operations read or write - // values on the stack, without moving the current stack position. - - void pop(RegisterID dest) - { - m_assembler.lw(dest, MIPSRegisters::sp, 0); - m_assembler.addiu(MIPSRegisters::sp, MIPSRegisters::sp, 4); - } - - void push(RegisterID src) - { - m_assembler.addiu(MIPSRegisters::sp, MIPSRegisters::sp, -4); - m_assembler.sw(src, MIPSRegisters::sp, 0); - } - - void push(Address address) - { - load32(address, dataTempRegister); - push(dataTempRegister); - } - - void push(TrustedImm32 imm) - { - move(imm, immTempRegister); - push(immTempRegister); - } - - // Register move operations: - // - // Move values in registers. - - void move(TrustedImm32 imm, RegisterID dest) - { - if (!imm.m_value && !m_fixedWidth) - move(MIPSRegisters::zero, dest); - else if (m_fixedWidth) { - m_assembler.lui(dest, imm.m_value >> 16); - m_assembler.ori(dest, dest, imm.m_value); - } else - m_assembler.li(dest, imm.m_value); - } - - void move(RegisterID src, RegisterID dest) - { - if (src != dest || m_fixedWidth) - m_assembler.move(dest, src); - } - - void move(TrustedImmPtr imm, RegisterID dest) - { - move(TrustedImm32(imm), dest); - } - - void swap(RegisterID reg1, RegisterID reg2) - { - move(reg1, immTempRegister); - move(reg2, reg1); - move(immTempRegister, reg2); - } - - void signExtend32ToPtr(RegisterID src, RegisterID dest) - { - if (src != dest || m_fixedWidth) - move(src, dest); - } - - void zeroExtend32ToPtr(RegisterID src, RegisterID dest) - { - if (src != dest || m_fixedWidth) - move(src, dest); - } - - // Forwards / external control flow operations: - // - // This set of jump and conditional branch operations return a Jump - // object which may linked at a later point, allow forwards jump, - // or jumps that will require external linkage (after the code has been - // relocated). - // - // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge - // respecitvely, for unsigned comparisons the names b, a, be, and ae are - // used (representing the names 'below' and 'above'). - // - // Operands to the comparision are provided in the expected order, e.g. - // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when - // treated as a signed 32bit value, is less than or equal to 5. - // - // jz and jnz test whether the first operand is equal to zero, and take - // an optional second operand of a mask under which to perform the test. - - Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) - { - // Make sure the immediate value is unsigned 8 bits. - ASSERT(!(right.m_value & 0xFFFFFF00)); - load8(left, dataTempRegister); - move(right, immTempRegister); - return branch32(cond, dataTempRegister, immTempRegister); - } - - void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) - { - // Make sure the immediate value is unsigned 8 bits. - ASSERT(!(right.m_value & 0xFFFFFF00)); - load8(left, dataTempRegister); - move(right, immTempRegister); - compare32(cond, dataTempRegister, immTempRegister, dest); - } - - Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - ASSERT(!(right.m_value & 0xFFFFFF00)); - load8(left, dataTempRegister); - // Be careful that the previous load8() uses immTempRegister. - // So, we need to put move() after load8(). - move(right, immTempRegister); - return branch32(cond, dataTempRegister, immTempRegister); - } - - Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) - { - if (cond == Equal) - return branchEqual(left, right); - if (cond == NotEqual) - return branchNotEqual(left, right); - if (cond == Above) { - m_assembler.sltu(cmpTempRegister, right, left); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == AboveOrEqual) { - m_assembler.sltu(cmpTempRegister, left, right); - return branchEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == Below) { - m_assembler.sltu(cmpTempRegister, left, right); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == BelowOrEqual) { - m_assembler.sltu(cmpTempRegister, right, left); - return branchEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == GreaterThan) { - m_assembler.slt(cmpTempRegister, right, left); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == GreaterThanOrEqual) { - m_assembler.slt(cmpTempRegister, left, right); - return branchEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == LessThan) { - m_assembler.slt(cmpTempRegister, left, right); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == LessThanOrEqual) { - m_assembler.slt(cmpTempRegister, right, left); - return branchEqual(cmpTempRegister, MIPSRegisters::zero); - } - ASSERT(0); - - return Jump(); - } - - Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) - { - move(right, immTempRegister); - return branch32(cond, left, immTempRegister); - } - - Jump branch32(RelationalCondition cond, RegisterID left, Address right) - { - load32(right, dataTempRegister); - return branch32(cond, left, dataTempRegister); - } - - Jump branch32(RelationalCondition cond, Address left, RegisterID right) - { - load32(left, dataTempRegister); - return branch32(cond, dataTempRegister, right); - } - - Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) - { - load32(left, dataTempRegister); - move(right, immTempRegister); - return branch32(cond, dataTempRegister, immTempRegister); - } - - Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - load32(left, dataTempRegister); - // Be careful that the previous load32() uses immTempRegister. - // So, we need to put move() after load32(). - move(right, immTempRegister); - return branch32(cond, dataTempRegister, immTempRegister); - } - - Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - load32WithUnalignedHalfWords(left, dataTempRegister); - // Be careful that the previous load32WithUnalignedHalfWords() - // uses immTempRegister. - // So, we need to put move() after load32WithUnalignedHalfWords(). - move(right, immTempRegister); - return branch32(cond, dataTempRegister, immTempRegister); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - load32(left.m_ptr, dataTempRegister); - return branch32(cond, dataTempRegister, right); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) - { - load32(left.m_ptr, dataTempRegister); - move(right, immTempRegister); - return branch32(cond, dataTempRegister, immTempRegister); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) - { - ASSERT((cond == Zero) || (cond == NonZero)); - m_assembler.andInsn(cmpTempRegister, reg, mask); - if (cond == Zero) - return branchEqual(cmpTempRegister, MIPSRegisters::zero); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - ASSERT((cond == Zero) || (cond == NonZero)); - if (mask.m_value == -1 && !m_fixedWidth) { - if (cond == Zero) - return branchEqual(reg, MIPSRegisters::zero); - return branchNotEqual(reg, MIPSRegisters::zero); - } - move(mask, immTempRegister); - return branchTest32(cond, reg, immTempRegister); - } - - Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - load32(address, dataTempRegister); - return branchTest32(cond, dataTempRegister, mask); - } - - Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - load32(address, dataTempRegister); - return branchTest32(cond, dataTempRegister, mask); - } - - Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - load8(address, dataTempRegister); - return branchTest32(cond, dataTempRegister, mask); - } - - Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - move(TrustedImmPtr(address.m_ptr), dataTempRegister); - load8(Address(dataTempRegister), dataTempRegister); - return branchTest32(cond, dataTempRegister, mask); - } - - Jump jump() - { - return branchEqual(MIPSRegisters::zero, MIPSRegisters::zero); - } - - void jump(RegisterID target) - { - m_assembler.jr(target); - m_assembler.nop(); - } - - void jump(Address address) - { - m_fixedWidth = true; - load32(address, MIPSRegisters::t9); - m_assembler.jr(MIPSRegisters::t9); - m_assembler.nop(); - m_fixedWidth = false; - } - - void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) - { - m_assembler.vmov(dest1, dest2, src); - } - - void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) - { - UNUSED_PARAM(scratch); - m_assembler.vmov(dest, src1, src2); - } - - // Arithmetic control flow operations: - // - // This set of conditional branch operations branch based - // on the result of an arithmetic operation. The operation - // is performed as normal, storing the result. - // - // * jz operations branch if the result is zero. - // * jo operations branch if the (signed) arithmetic - // operation caused an overflow to occur. - - Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - if (cond == Overflow) { - /* - move dest, dataTemp - xor cmpTemp, dataTemp, src - bltz cmpTemp, No_overflow # diff sign bit -> no overflow - addu dest, dataTemp, src - xor cmpTemp, dest, dataTemp - bgez cmpTemp, No_overflow # same sign big -> no overflow - nop - b Overflow - nop - nop - nop - nop - nop - No_overflow: - */ - move(dest, dataTempRegister); - m_assembler.xorInsn(cmpTempRegister, dataTempRegister, src); - m_assembler.bltz(cmpTempRegister, 10); - m_assembler.addu(dest, dataTempRegister, src); - m_assembler.xorInsn(cmpTempRegister, dest, dataTempRegister); - m_assembler.bgez(cmpTempRegister, 7); - m_assembler.nop(); - return jump(); - } - if (cond == Signed) { - add32(src, dest); - // Check if dest is negative. - m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == Zero) { - add32(src, dest); - return branchEqual(dest, MIPSRegisters::zero); - } - if (cond == NonZero) { - add32(src, dest); - return branchNotEqual(dest, MIPSRegisters::zero); - } - ASSERT(0); - return Jump(); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - move(imm, immTempRegister); - return branchAdd32(cond, immTempRegister, dest); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) - { - move(imm, immTempRegister); - move(src, dest); - return branchAdd32(cond, immTempRegister, dest); - } - - Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - if (cond == Overflow) { - /* - mult src, dest - mfhi dataTemp - mflo dest - sra addrTemp, dest, 31 - beq dataTemp, addrTemp, No_overflow # all sign bits (bit 63 to bit 31) are the same -> no overflow - nop - b Overflow - nop - nop - nop - nop - nop - No_overflow: - */ - m_assembler.mult(src, dest); - m_assembler.mfhi(dataTempRegister); - m_assembler.mflo(dest); - m_assembler.sra(addrTempRegister, dest, 31); - m_assembler.beq(dataTempRegister, addrTempRegister, 7); - m_assembler.nop(); - return jump(); - } - if (cond == Signed) { - mul32(src, dest); - // Check if dest is negative. - m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == Zero) { - mul32(src, dest); - return branchEqual(dest, MIPSRegisters::zero); - } - if (cond == NonZero) { - mul32(src, dest); - return branchNotEqual(dest, MIPSRegisters::zero); - } - ASSERT(0); - return Jump(); - } - - Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(imm, immTempRegister); - move(src, dest); - return branchMul32(cond, immTempRegister, dest); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - if (cond == Overflow) { - /* - move dest, dataTemp - xor cmpTemp, dataTemp, src - bgez cmpTemp, No_overflow # same sign bit -> no overflow - subu dest, dataTemp, src - xor cmpTemp, dest, dataTemp - bgez cmpTemp, No_overflow # same sign bit -> no overflow - nop - b Overflow - nop - nop - nop - nop - nop - No_overflow: - */ - move(dest, dataTempRegister); - m_assembler.xorInsn(cmpTempRegister, dataTempRegister, src); - m_assembler.bgez(cmpTempRegister, 10); - m_assembler.subu(dest, dataTempRegister, src); - m_assembler.xorInsn(cmpTempRegister, dest, dataTempRegister); - m_assembler.bgez(cmpTempRegister, 7); - m_assembler.nop(); - return jump(); - } - if (cond == Signed) { - sub32(src, dest); - // Check if dest is negative. - m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == Zero) { - sub32(src, dest); - return branchEqual(dest, MIPSRegisters::zero); - } - if (cond == NonZero) { - sub32(src, dest); - return branchNotEqual(dest, MIPSRegisters::zero); - } - ASSERT(0); - return Jump(); - } - - Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - move(imm, immTempRegister); - return branchSub32(cond, immTempRegister, dest); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) - { - move(imm, immTempRegister); - move(src, dest); - return branchSub32(cond, immTempRegister, dest); - } - - Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); - if (cond == Signed) { - or32(src, dest); - // Check if dest is negative. - m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); - return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); - } - if (cond == Zero) { - or32(src, dest); - return branchEqual(dest, MIPSRegisters::zero); - } - if (cond == NonZero) { - or32(src, dest); - return branchNotEqual(dest, MIPSRegisters::zero); - } - ASSERT(0); - return Jump(); - } - - // Miscellaneous operations: - - void breakpoint() - { - m_assembler.bkpt(); - } - - Call nearCall() - { - /* We need two words for relaxation. */ - m_assembler.nop(); - m_assembler.nop(); - m_assembler.jal(); - m_assembler.nop(); - return Call(m_assembler.label(), Call::LinkableNear); - } - - Call call() - { - m_assembler.lui(MIPSRegisters::t9, 0); - m_assembler.ori(MIPSRegisters::t9, MIPSRegisters::t9, 0); - m_assembler.jalr(MIPSRegisters::t9); - m_assembler.nop(); - return Call(m_assembler.label(), Call::Linkable); - } - - Call call(RegisterID target) - { - m_assembler.jalr(target); - m_assembler.nop(); - return Call(m_assembler.label(), Call::None); - } - - Call call(Address address) - { - m_fixedWidth = true; - load32(address, MIPSRegisters::t9); - m_assembler.jalr(MIPSRegisters::t9); - m_assembler.nop(); - m_fixedWidth = false; - return Call(m_assembler.label(), Call::None); - } - - void ret() - { - m_assembler.jr(MIPSRegisters::ra); - m_assembler.nop(); - } - - void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) - { - if (cond == Equal) { - m_assembler.xorInsn(dest, left, right); - m_assembler.sltiu(dest, dest, 1); - } else if (cond == NotEqual) { - m_assembler.xorInsn(dest, left, right); - m_assembler.sltu(dest, MIPSRegisters::zero, dest); - } else if (cond == Above) - m_assembler.sltu(dest, right, left); - else if (cond == AboveOrEqual) { - m_assembler.sltu(dest, left, right); - m_assembler.xori(dest, dest, 1); - } else if (cond == Below) - m_assembler.sltu(dest, left, right); - else if (cond == BelowOrEqual) { - m_assembler.sltu(dest, right, left); - m_assembler.xori(dest, dest, 1); - } else if (cond == GreaterThan) - m_assembler.slt(dest, right, left); - else if (cond == GreaterThanOrEqual) { - m_assembler.slt(dest, left, right); - m_assembler.xori(dest, dest, 1); - } else if (cond == LessThan) - m_assembler.slt(dest, left, right); - else if (cond == LessThanOrEqual) { - m_assembler.slt(dest, right, left); - m_assembler.xori(dest, dest, 1); - } - } - - void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - move(right, immTempRegister); - compare32(cond, left, immTempRegister, dest); - } - - void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - ASSERT((cond == Zero) || (cond == NonZero)); - load8(address, dataTempRegister); - if (mask.m_value == -1 && !m_fixedWidth) { - if (cond == Zero) - m_assembler.sltiu(dest, dataTempRegister, 1); - else - m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); - } else { - move(mask, immTempRegister); - m_assembler.andInsn(cmpTempRegister, dataTempRegister, immTempRegister); - if (cond == Zero) - m_assembler.sltiu(dest, cmpTempRegister, 1); - else - m_assembler.sltu(dest, MIPSRegisters::zero, cmpTempRegister); - } - } - - void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - ASSERT((cond == Zero) || (cond == NonZero)); - load32(address, dataTempRegister); - if (mask.m_value == -1 && !m_fixedWidth) { - if (cond == Zero) - m_assembler.sltiu(dest, dataTempRegister, 1); - else - m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); - } else { - move(mask, immTempRegister); - m_assembler.andInsn(cmpTempRegister, dataTempRegister, immTempRegister); - if (cond == Zero) - m_assembler.sltiu(dest, cmpTempRegister, 1); - else - m_assembler.sltu(dest, MIPSRegisters::zero, cmpTempRegister); - } - } - - DataLabel32 moveWithPatch(TrustedImm32 imm, RegisterID dest) - { - m_fixedWidth = true; - DataLabel32 label(this); - move(imm, dest); - m_fixedWidth = false; - return label; - } - - DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) - { - m_fixedWidth = true; - DataLabelPtr label(this); - move(initialValue, dest); - m_fixedWidth = false; - return label; - } - - Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - m_fixedWidth = true; - dataLabel = moveWithPatch(initialRightValue, immTempRegister); - Jump temp = branch32(cond, left, immTempRegister); - m_fixedWidth = false; - return temp; - } - - Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - m_fixedWidth = true; - load32(left, dataTempRegister); - dataLabel = moveWithPatch(initialRightValue, immTempRegister); - Jump temp = branch32(cond, dataTempRegister, immTempRegister); - m_fixedWidth = false; - return temp; - } - - DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) - { - m_fixedWidth = true; - DataLabelPtr dataLabel = moveWithPatch(initialValue, dataTempRegister); - store32(dataTempRegister, address); - m_fixedWidth = false; - return dataLabel; - } - - DataLabelPtr storePtrWithPatch(ImplicitAddress address) - { - return storePtrWithPatch(TrustedImmPtr(0), address); - } - - Call tailRecursiveCall() - { - // Like a normal call, but don't update the returned address register - m_fixedWidth = true; - move(TrustedImm32(0), MIPSRegisters::t9); - m_assembler.jr(MIPSRegisters::t9); - m_assembler.nop(); - m_fixedWidth = false; - return Call(m_assembler.label(), Call::Linkable); - } - - Call makeTailRecursiveCall(Jump oldJump) - { - oldJump.link(this); - return tailRecursiveCall(); - } - - void loadFloat(BaseIndex address, FPRegisterID dest) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lwc1 dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lwc1(dest, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - lwc1 dest, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.lwc1(dest, addrTempRegister, address.offset); - } - } - - void loadDouble(ImplicitAddress address, FPRegisterID dest) - { -#if WTF_MIPS_ISA(1) - /* - li addrTemp, address.offset - addu addrTemp, addrTemp, base - lwc1 dest, 0(addrTemp) - lwc1 dest+1, 4(addrTemp) - */ - move(TrustedImm32(address.offset), addrTempRegister); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lwc1(dest, addrTempRegister, 0); - m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); -#else - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - m_assembler.ldc1(dest, address.base, address.offset); - } else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - ldc1 dest, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.ldc1(dest, addrTempRegister, address.offset); - } -#endif - } - - void loadDouble(BaseIndex address, FPRegisterID dest) - { -#if WTF_MIPS_ISA(1) - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lwc1 dest, address.offset(addrTemp) - lwc1 dest+1, (address.offset+4)(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lwc1(dest, addrTempRegister, address.offset); - m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, address.offset + 4); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - lwc1 dest, (address.offset & 0xffff)(at) - lwc1 dest+4, (address.offset & 0xffff + 4)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.lwc1(dest, addrTempRegister, address.offset); - m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, address.offset + 4); - } -#else - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - ldc1 dest, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.ldc1(dest, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - ldc1 dest, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.ldc1(dest, addrTempRegister, address.offset); - } -#endif - } - - void loadDouble(const void* address, FPRegisterID dest) - { -#if WTF_MIPS_ISA(1) - /* - li addrTemp, address - lwc1 dest, 0(addrTemp) - lwc1 dest+1, 4(addrTemp) - */ - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.lwc1(dest, addrTempRegister, 0); - m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); -#else - /* - li addrTemp, address - ldc1 dest, 0(addrTemp) - */ - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.ldc1(dest, addrTempRegister, 0); -#endif - } - - void storeFloat(FPRegisterID src, BaseIndex address) - { - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - swc1 src, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.swc1(src, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - swc1 src, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.swc1(src, addrTempRegister, address.offset); - } - } - - void storeDouble(FPRegisterID src, ImplicitAddress address) - { -#if WTF_MIPS_ISA(1) - /* - li addrTemp, address.offset - addu addrTemp, addrTemp, base - swc1 dest, 0(addrTemp) - swc1 dest+1, 4(addrTemp) - */ - move(TrustedImm32(address.offset), addrTempRegister); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.swc1(src, addrTempRegister, 0); - m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, 4); -#else - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) - m_assembler.sdc1(src, address.base, address.offset); - else { - /* - lui addrTemp, (offset + 0x8000) >> 16 - addu addrTemp, addrTemp, base - sdc1 src, (offset & 0xffff)(addrTemp) - */ - m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.sdc1(src, addrTempRegister, address.offset); - } -#endif - } - - void storeDouble(FPRegisterID src, BaseIndex address) - { -#if WTF_MIPS_ISA(1) - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - swc1 src, address.offset(addrTemp) - swc1 src+1, (address.offset + 4)(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.swc1(src, addrTempRegister, address.offset); - m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, address.offset + 4); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - swc1 src, (address.offset & 0xffff)(at) - swc1 src+1, (address.offset & 0xffff + 4)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.swc1(src, addrTempRegister, address.offset); - m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, address.offset + 4); - } -#else - if (address.offset >= -32768 && address.offset <= 32767 - && !m_fixedWidth) { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - sdc1 src, address.offset(addrTemp) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.sdc1(src, addrTempRegister, address.offset); - } else { - /* - sll addrTemp, address.index, address.scale - addu addrTemp, addrTemp, address.base - lui immTemp, (address.offset + 0x8000) >> 16 - addu addrTemp, addrTemp, immTemp - sdc1 src, (address.offset & 0xffff)(at) - */ - m_assembler.sll(addrTempRegister, address.index, address.scale); - m_assembler.addu(addrTempRegister, addrTempRegister, address.base); - m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); - m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); - m_assembler.sdc1(src, addrTempRegister, address.offset); - } -#endif - } - - void storeDouble(FPRegisterID src, const void* address) - { -#if WTF_MIPS_ISA(1) - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.swc1(src, addrTempRegister, 0); - m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, 4); -#else - move(TrustedImmPtr(address), addrTempRegister); - m_assembler.sdc1(src, addrTempRegister, 0); -#endif - } - - void addDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.addd(dest, dest, src); - } - - void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - m_assembler.addd(dest, op1, op2); - } - - void addDouble(Address src, FPRegisterID dest) - { - loadDouble(src, fpTempRegister); - m_assembler.addd(dest, dest, fpTempRegister); - } - - void addDouble(AbsoluteAddress address, FPRegisterID dest) - { - loadDouble(address.m_ptr, fpTempRegister); - m_assembler.addd(dest, dest, fpTempRegister); - } - - void subDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.subd(dest, dest, src); - } - - void subDouble(Address src, FPRegisterID dest) - { - loadDouble(src, fpTempRegister); - m_assembler.subd(dest, dest, fpTempRegister); - } - - void mulDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.muld(dest, dest, src); - } - - void mulDouble(Address src, FPRegisterID dest) - { - loadDouble(src, fpTempRegister); - m_assembler.muld(dest, dest, fpTempRegister); - } - - void divDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.divd(dest, dest, src); - } - - void convertInt32ToDouble(RegisterID src, FPRegisterID dest) - { - m_assembler.mtc1(src, fpTempRegister); - m_assembler.cvtdw(dest, fpTempRegister); - } - - void convertInt32ToDouble(Address src, FPRegisterID dest) - { - load32(src, dataTempRegister); - m_assembler.mtc1(dataTempRegister, fpTempRegister); - m_assembler.cvtdw(dest, fpTempRegister); - } - - void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) - { - load32(src.m_ptr, dataTempRegister); - m_assembler.mtc1(dataTempRegister, fpTempRegister); - m_assembler.cvtdw(dest, fpTempRegister); - } - - void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) - { - m_assembler.cvtds(dst, src); - } - - void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) - { - m_assembler.cvtsd(dst, src); - } - - void insertRelaxationWords() - { - /* We need four words for relaxation. */ - m_assembler.beq(MIPSRegisters::zero, MIPSRegisters::zero, 3); // Jump over nops; - m_assembler.nop(); - m_assembler.nop(); - m_assembler.nop(); - } - - Jump branchTrue() - { - m_assembler.appendJump(); - m_assembler.bc1t(); - m_assembler.nop(); - insertRelaxationWords(); - return Jump(m_assembler.label()); - } - - Jump branchFalse() - { - m_assembler.appendJump(); - m_assembler.bc1f(); - m_assembler.nop(); - insertRelaxationWords(); - return Jump(m_assembler.label()); - } - - Jump branchEqual(RegisterID rs, RegisterID rt) - { - m_assembler.appendJump(); - m_assembler.beq(rs, rt, 0); - m_assembler.nop(); - insertRelaxationWords(); - return Jump(m_assembler.label()); - } - - Jump branchNotEqual(RegisterID rs, RegisterID rt) - { - m_assembler.appendJump(); - m_assembler.bne(rs, rt, 0); - m_assembler.nop(); - insertRelaxationWords(); - return Jump(m_assembler.label()); - } - - Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) - { - if (cond == DoubleEqual) { - m_assembler.ceqd(left, right); - return branchTrue(); - } - if (cond == DoubleNotEqual) { - m_assembler.cueqd(left, right); - return branchFalse(); // false - } - if (cond == DoubleGreaterThan) { - m_assembler.cngtd(left, right); - return branchFalse(); // false - } - if (cond == DoubleGreaterThanOrEqual) { - m_assembler.cnged(left, right); - return branchFalse(); // false - } - if (cond == DoubleLessThan) { - m_assembler.cltd(left, right); - return branchTrue(); - } - if (cond == DoubleLessThanOrEqual) { - m_assembler.cled(left, right); - return branchTrue(); - } - if (cond == DoubleEqualOrUnordered) { - m_assembler.cueqd(left, right); - return branchTrue(); - } - if (cond == DoubleNotEqualOrUnordered) { - m_assembler.ceqd(left, right); - return branchFalse(); // false - } - if (cond == DoubleGreaterThanOrUnordered) { - m_assembler.coled(left, right); - return branchFalse(); // false - } - if (cond == DoubleGreaterThanOrEqualOrUnordered) { - m_assembler.coltd(left, right); - return branchFalse(); // false - } - if (cond == DoubleLessThanOrUnordered) { - m_assembler.cultd(left, right); - return branchTrue(); - } - if (cond == DoubleLessThanOrEqualOrUnordered) { - m_assembler.culed(left, right); - return branchTrue(); - } - ASSERT(0); - - return Jump(); - } - - // Truncates 'src' to an integer, and places the resulting 'dest'. - // If the result is not representable as a 32 bit value, branch. - // May also branch for some values that are representable in 32 bits - // (specifically, in this case, INT_MAX 0x7fffffff). - Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) - { - m_assembler.truncwd(fpTempRegister, src); - m_assembler.mfc1(dest, fpTempRegister); - return branch32(Equal, dest, TrustedImm32(0x7fffffff)); - } - - // Convert 'src' to an integer, and places the resulting 'dest'. - // If the result is not representable as a 32 bit value, branch. - // May also branch for some values that are representable in 32 bits - // (specifically, in this case, 0). - void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) - { - m_assembler.cvtwd(fpTempRegister, src); - m_assembler.mfc1(dest, fpTempRegister); - - // If the result is zero, it might have been -0.0, and the double comparison won't catch this! - failureCases.append(branch32(Equal, dest, MIPSRegisters::zero)); - - // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. - convertInt32ToDouble(dest, fpTemp); - failureCases.append(branchDouble(DoubleNotEqualOrUnordered, fpTemp, src)); - } - - Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) - { -#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 - m_assembler.mtc1(MIPSRegisters::zero, scratch); - m_assembler.mthc1(MIPSRegisters::zero, scratch); -#else - m_assembler.mtc1(MIPSRegisters::zero, scratch); - m_assembler.mtc1(MIPSRegisters::zero, FPRegisterID(scratch + 1)); -#endif - return branchDouble(DoubleNotEqual, reg, scratch); - } - - Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) - { -#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 - m_assembler.mtc1(MIPSRegisters::zero, scratch); - m_assembler.mthc1(MIPSRegisters::zero, scratch); -#else - m_assembler.mtc1(MIPSRegisters::zero, scratch); - m_assembler.mtc1(MIPSRegisters::zero, FPRegisterID(scratch + 1)); -#endif - return branchDouble(DoubleEqualOrUnordered, reg, scratch); - } - - void nop() - { - m_assembler.nop(); - } - - static FunctionPtr readCallTarget(CodeLocationCall call) - { - return FunctionPtr(reinterpret_cast(MIPSAssembler::readCallTarget(call.dataLocation()))); - } - - static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) - { - ASSERT_NOT_REACHED(); - } - - static ptrdiff_t maxJumpReplacementSize() - { - ASSERT_NOT_REACHED(); - return 0; - } - - static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } - - static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) - { - return label.labelAtOffset(0); - } - - static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) - { - MIPSAssembler::revertJumpToMove(instructionStart.dataLocation(), immTempRegister, reinterpret_cast(initialValue) & 0xffff); - } - - static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) - { - UNREACHABLE_FOR_PLATFORM(); - return CodeLocationLabel(); - } - - static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) - { - UNREACHABLE_FOR_PLATFORM(); - } - - -private: - // If m_fixedWidth is true, we will generate a fixed number of instructions. - // Otherwise, we can emit any number of instructions. - bool m_fixedWidth; - - friend class LinkBuffer; - friend class RepatchBuffer; - - static void linkCall(void* code, Call call, FunctionPtr function) - { - MIPSAssembler::linkCall(code, call.m_label, function.value()); - } - - static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) - { - MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } - - static void repatchCall(CodeLocationCall call, FunctionPtr destination) - { - MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } - -}; - -} - -#endif // ENABLE(ASSEMBLER) && CPU(MIPS) - -#endif // MacroAssemblerMIPS_h diff --git a/3rdparty/masm/assembler/MacroAssemblerSH4.cpp b/3rdparty/masm/assembler/MacroAssemblerSH4.cpp deleted file mode 100644 index 59de3ff48c..0000000000 --- a/3rdparty/masm/assembler/MacroAssemblerSH4.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2011 STMicroelectronics. All rights reserved. - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "config.h" - -#if ENABLE(ASSEMBLER) && CPU(SH4) - -#include "MacroAssemblerSH4.h" - -namespace JSC { - -void MacroAssemblerSH4::linkCall(void* code, Call call, FunctionPtr function) -{ - SH4Assembler::linkCall(code, call.m_label, function.value()); -} - -void MacroAssemblerSH4::repatchCall(CodeLocationCall call, CodeLocationLabel destination) -{ - SH4Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); -} - -void MacroAssemblerSH4::repatchCall(CodeLocationCall call, FunctionPtr destination) -{ - SH4Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); -} - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) diff --git a/3rdparty/masm/assembler/MacroAssemblerSH4.h b/3rdparty/masm/assembler/MacroAssemblerSH4.h deleted file mode 100644 index ef210f80cb..0000000000 --- a/3rdparty/masm/assembler/MacroAssemblerSH4.h +++ /dev/null @@ -1,2293 +0,0 @@ -/* - * Copyright (C) 2009-2011 STMicroelectronics. All rights reserved. - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef MacroAssemblerSH4_h -#define MacroAssemblerSH4_h - -#if ENABLE(ASSEMBLER) && CPU(SH4) - -#include "SH4Assembler.h" -#include "AbstractMacroAssembler.h" -#include - -namespace JSC { - -class MacroAssemblerSH4 : public AbstractMacroAssembler { -public: - typedef SH4Assembler::FPRegisterID FPRegisterID; - - static const Scale ScalePtr = TimesFour; - static const FPRegisterID fscratch = SH4Registers::fr10; - static const RegisterID stackPointerRegister = SH4Registers::sp; - static const RegisterID linkRegister = SH4Registers::pr; - static const RegisterID scratchReg3 = SH4Registers::r13; - - static const int MaximumCompactPtrAlignedAddressOffset = 60; - - static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) - { - return (value >= 0) && (value <= MaximumCompactPtrAlignedAddressOffset); - } - - enum RelationalCondition { - Equal = SH4Assembler::EQ, - NotEqual = SH4Assembler::NE, - Above = SH4Assembler::HI, - AboveOrEqual = SH4Assembler::HS, - Below = SH4Assembler::LI, - BelowOrEqual = SH4Assembler::LS, - GreaterThan = SH4Assembler::GT, - GreaterThanOrEqual = SH4Assembler::GE, - LessThan = SH4Assembler::LT, - LessThanOrEqual = SH4Assembler::LE - }; - - enum ResultCondition { - Overflow = SH4Assembler::OF, - Signed = SH4Assembler::SI, - Zero = SH4Assembler::EQ, - NonZero = SH4Assembler::NE - }; - - enum DoubleCondition { - // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. - DoubleEqual = SH4Assembler::EQ, - DoubleNotEqual = SH4Assembler::NE, - DoubleGreaterThan = SH4Assembler::GT, - DoubleGreaterThanOrEqual = SH4Assembler::GE, - DoubleLessThan = SH4Assembler::LT, - DoubleLessThanOrEqual = SH4Assembler::LE, - // If either operand is NaN, these conditions always evaluate to true. - DoubleEqualOrUnordered = SH4Assembler::EQU, - DoubleNotEqualOrUnordered = SH4Assembler::NEU, - DoubleGreaterThanOrUnordered = SH4Assembler::GTU, - DoubleGreaterThanOrEqualOrUnordered = SH4Assembler::GEU, - DoubleLessThanOrUnordered = SH4Assembler::LTU, - DoubleLessThanOrEqualOrUnordered = SH4Assembler::LEU, - }; - - RegisterID claimScratch() - { - return m_assembler.claimScratch(); - } - - void releaseScratch(RegisterID reg) - { - m_assembler.releaseScratch(reg); - } - - // Integer arithmetic operations - - void add32(RegisterID src, RegisterID dest) - { - m_assembler.addlRegReg(src, dest); - } - - void add32(TrustedImm32 imm, RegisterID dest) - { - if (m_assembler.isImmediate(imm.m_value)) { - m_assembler.addlImm8r(imm.m_value, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant(imm.m_value, scr); - m_assembler.addlRegReg(scr, dest); - releaseScratch(scr); - } - - void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (src != dest) - m_assembler.movlRegReg(src, dest); - add32(imm, dest); - } - - void add32(TrustedImm32 imm, Address address) - { - RegisterID scr = claimScratch(); - load32(address, scr); - add32(imm, scr); - store32(scr, address); - releaseScratch(scr); - } - - void add32(Address src, RegisterID dest) - { - RegisterID scr = claimScratch(); - load32(src, scr); - m_assembler.addlRegReg(scr, dest); - releaseScratch(scr); - } - - void add32(AbsoluteAddress src, RegisterID dest) - { - RegisterID scr = claimScratch(); - load32(src.m_ptr, scr); - m_assembler.addlRegReg(scr, dest); - releaseScratch(scr); - } - - void and32(RegisterID src, RegisterID dest) - { - m_assembler.andlRegReg(src, dest); - } - - void and32(TrustedImm32 imm, RegisterID dest) - { - if ((imm.m_value <= 255) && (imm.m_value >= 0) && (dest == SH4Registers::r0)) { - m_assembler.andlImm8r(imm.m_value, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant((imm.m_value), scr); - m_assembler.andlRegReg(scr, dest); - releaseScratch(scr); - } - - void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (src != dest) { - move(imm, dest); - and32(src, dest); - return; - } - - and32(imm, dest); - } - - void lshift32(RegisterID shiftamount, RegisterID dest) - { - if (shiftamount == SH4Registers::r0) - m_assembler.andlImm8r(0x1f, shiftamount); - else { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(0x1f, scr); - m_assembler.andlRegReg(scr, shiftamount); - releaseScratch(scr); - } - m_assembler.shllRegReg(dest, shiftamount); - } - - void rshift32(int imm, RegisterID dest) - { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(-imm, scr); - m_assembler.shaRegReg(dest, scr); - releaseScratch(scr); - } - - void lshift32(TrustedImm32 imm, RegisterID dest) - { - if (!imm.m_value) - return; - - if ((imm.m_value == 1) || (imm.m_value == 2) || (imm.m_value == 8) || (imm.m_value == 16)) { - m_assembler.shllImm8r(imm.m_value, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant((imm.m_value & 0x1f) , scr); - m_assembler.shllRegReg(dest, scr); - releaseScratch(scr); - } - - void lshift32(RegisterID src, TrustedImm32 shiftamount, RegisterID dest) - { - if (src != dest) - move(src, dest); - - lshift32(shiftamount, dest); - } - - void mul32(RegisterID src, RegisterID dest) - { - m_assembler.imullRegReg(src, dest); - m_assembler.stsmacl(dest); - } - - void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - RegisterID scr = claimScratch(); - move(imm, scr); - if (src != dest) - move(src, dest); - mul32(scr, dest); - releaseScratch(scr); - } - - void or32(RegisterID src, RegisterID dest) - { - m_assembler.orlRegReg(src, dest); - } - - void or32(TrustedImm32 imm, RegisterID dest) - { - if ((imm.m_value <= 255) && (imm.m_value >= 0) && (dest == SH4Registers::r0)) { - m_assembler.orlImm8r(imm.m_value, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant(imm.m_value, scr); - m_assembler.orlRegReg(scr, dest); - releaseScratch(scr); - } - - void or32(RegisterID op1, RegisterID op2, RegisterID dest) - { - if (op1 == op2) - move(op1, dest); - else if (op1 == dest) - or32(op2, dest); - else { - move(op2, dest); - or32(op1, dest); - } - } - - -void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (src != dest) { - move(imm, dest); - or32(src, dest); - return; - } - - or32(imm, dest); - } - - void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - if (src != dest) { - move(imm, dest); - xor32(src, dest); - return; - } - - xor32(imm, dest); - } - - void rshift32(RegisterID shiftamount, RegisterID dest) - { - if (shiftamount == SH4Registers::r0) - m_assembler.andlImm8r(0x1f, shiftamount); - else { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(0x1f, scr); - m_assembler.andlRegReg(scr, shiftamount); - releaseScratch(scr); - } - m_assembler.neg(shiftamount, shiftamount); - m_assembler.shaRegReg(dest, shiftamount); - } - - void rshift32(TrustedImm32 imm, RegisterID dest) - { - if (imm.m_value & 0x1f) - rshift32(imm.m_value & 0x1f, dest); - } - - void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - if (src != dest) - move(src, dest); - rshift32(imm, dest); - } - - void sub32(RegisterID src, RegisterID dest) - { - m_assembler.sublRegReg(src, dest); - } - - void sub32(TrustedImm32 imm, AbsoluteAddress address, RegisterID scratchReg) - { - RegisterID result = claimScratch(); - - m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); - m_assembler.movlMemReg(scratchReg, result); - - if (m_assembler.isImmediate(-imm.m_value)) - m_assembler.addlImm8r(-imm.m_value, result); - else { - m_assembler.loadConstant(imm.m_value, scratchReg3); - m_assembler.sublRegReg(scratchReg3, result); - } - - store32(result, scratchReg); - releaseScratch(result); - } - - void sub32(TrustedImm32 imm, AbsoluteAddress address) - { - RegisterID result = claimScratch(); - RegisterID scratchReg = claimScratch(); - - m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); - m_assembler.movlMemReg(scratchReg, result); - - if (m_assembler.isImmediate(-imm.m_value)) - m_assembler.addlImm8r(-imm.m_value, result); - else { - m_assembler.loadConstant(imm.m_value, scratchReg3); - m_assembler.sublRegReg(scratchReg3, result); - } - - store32(result, scratchReg); - releaseScratch(result); - releaseScratch(scratchReg); - } - - void add32(TrustedImm32 imm, AbsoluteAddress address, RegisterID scratchReg) - { - RegisterID result = claimScratch(); - - m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); - m_assembler.movlMemReg(scratchReg, result); - - if (m_assembler.isImmediate(imm.m_value)) - m_assembler.addlImm8r(imm.m_value, result); - else { - m_assembler.loadConstant(imm.m_value, scratchReg3); - m_assembler.addlRegReg(scratchReg3, result); - } - - store32(result, scratchReg); - releaseScratch(result); - } - - void add32(TrustedImm32 imm, AbsoluteAddress address) - { - RegisterID result = claimScratch(); - RegisterID scratchReg = claimScratch(); - - m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); - m_assembler.movlMemReg(scratchReg, result); - - if (m_assembler.isImmediate(imm.m_value)) - m_assembler.addlImm8r(imm.m_value, result); - else { - m_assembler.loadConstant(imm.m_value, scratchReg3); - m_assembler.addlRegReg(scratchReg3, result); - } - - store32(result, scratchReg); - releaseScratch(result); - releaseScratch(scratchReg); - } - - void add64(TrustedImm32 imm, AbsoluteAddress address) - { - RegisterID scr1 = claimScratch(); - RegisterID scr2 = claimScratch(); - - // Add 32-bit LSB first. - m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scr1); - m_assembler.movlMemReg(scr1, scr1); // scr1 = 32-bit LSB of int64 @ address - m_assembler.loadConstant(imm.m_value, scr2); - m_assembler.clrt(); - m_assembler.addclRegReg(scr1, scr2); - m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scr1); - m_assembler.movlRegMem(scr2, scr1); // Update address with 32-bit LSB result. - - // Then add 32-bit MSB. - m_assembler.addlImm8r(4, scr1); - m_assembler.movlMemReg(scr1, scr1); // scr1 = 32-bit MSB of int64 @ address - m_assembler.movt(scr2); - if (imm.m_value < 0) - m_assembler.addlImm8r(-1, scr2); // Sign extend imm value if needed. - m_assembler.addvlRegReg(scr2, scr1); - m_assembler.loadConstant(reinterpret_cast(address.m_ptr) + 4, scr2); - m_assembler.movlRegMem(scr1, scr2); // Update (address + 4) with 32-bit MSB result. - - releaseScratch(scr2); - releaseScratch(scr1); - } - - void sub32(TrustedImm32 imm, RegisterID dest) - { - if (m_assembler.isImmediate(-imm.m_value)) { - m_assembler.addlImm8r(-imm.m_value, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant(imm.m_value, scr); - m_assembler.sublRegReg(scr, dest); - releaseScratch(scr); - } - - void sub32(Address src, RegisterID dest) - { - RegisterID scr = claimScratch(); - load32(src, scr); - m_assembler.sublRegReg(scr, dest); - releaseScratch(scr); - } - - void xor32(RegisterID src, RegisterID dest) - { - m_assembler.xorlRegReg(src, dest); - } - - void xor32(TrustedImm32 imm, RegisterID srcDest) - { - if (imm.m_value == -1) { - m_assembler.notlReg(srcDest, srcDest); - return; - } - - if ((srcDest != SH4Registers::r0) || (imm.m_value > 255) || (imm.m_value < 0)) { - RegisterID scr = claimScratch(); - m_assembler.loadConstant((imm.m_value), scr); - m_assembler.xorlRegReg(scr, srcDest); - releaseScratch(scr); - return; - } - - m_assembler.xorlImm8r(imm.m_value, srcDest); - } - - void compare32(int imm, RegisterID dst, RelationalCondition cond) - { - if (((cond == Equal) || (cond == NotEqual)) && (dst == SH4Registers::r0) && m_assembler.isImmediate(imm)) { - m_assembler.cmpEqImmR0(imm, dst); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant(imm, scr); - m_assembler.cmplRegReg(scr, dst, SH4Condition(cond)); - releaseScratch(scr); - } - - void compare32(int offset, RegisterID base, RegisterID left, RelationalCondition cond) - { - RegisterID scr = claimScratch(); - if (!offset) { - m_assembler.movlMemReg(base, scr); - m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); - releaseScratch(scr); - return; - } - - if ((offset < 0) || (offset >= 64)) { - m_assembler.loadConstant(offset, scr); - m_assembler.addlRegReg(base, scr); - m_assembler.movlMemReg(scr, scr); - m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); - releaseScratch(scr); - return; - } - - m_assembler.movlMemReg(offset >> 2, base, scr); - m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); - releaseScratch(scr); - } - - void testImm(int imm, int offset, RegisterID base) - { - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - - if ((offset < 0) || (offset >= 64)) { - m_assembler.loadConstant(offset, scr); - m_assembler.addlRegReg(base, scr); - m_assembler.movlMemReg(scr, scr); - } else if (offset) - m_assembler.movlMemReg(offset >> 2, base, scr); - else - m_assembler.movlMemReg(base, scr); - if (m_assembler.isImmediate(imm)) - m_assembler.movImm8(imm, scr1); - else - m_assembler.loadConstant(imm, scr1); - - m_assembler.testlRegReg(scr, scr1); - releaseScratch(scr); - releaseScratch(scr1); - } - - void testlImm(int imm, RegisterID dst) - { - if ((dst == SH4Registers::r0) && (imm <= 255) && (imm >= 0)) { - m_assembler.testlImm8r(imm, dst); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant(imm, scr); - m_assembler.testlRegReg(scr, dst); - releaseScratch(scr); - } - - void compare32(RegisterID right, int offset, RegisterID base, RelationalCondition cond) - { - if (!offset) { - RegisterID scr = claimScratch(); - m_assembler.movlMemReg(base, scr); - m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); - releaseScratch(scr); - return; - } - - if ((offset < 0) || (offset >= 64)) { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(offset, scr); - m_assembler.addlRegReg(base, scr); - m_assembler.movlMemReg(scr, scr); - m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); - releaseScratch(scr); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.movlMemReg(offset >> 2, base, scr); - m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); - releaseScratch(scr); - } - - void compare32(int imm, int offset, RegisterID base, RelationalCondition cond) - { - if (!offset) { - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - m_assembler.movlMemReg(base, scr); - m_assembler.loadConstant(imm, scr1); - m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); - releaseScratch(scr1); - releaseScratch(scr); - return; - } - - if ((offset < 0) || (offset >= 64)) { - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - m_assembler.loadConstant(offset, scr); - m_assembler.addlRegReg(base, scr); - m_assembler.movlMemReg(scr, scr); - m_assembler.loadConstant(imm, scr1); - m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); - releaseScratch(scr1); - releaseScratch(scr); - return; - } - - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - m_assembler.movlMemReg(offset >> 2, base, scr); - m_assembler.loadConstant(imm, scr1); - m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); - releaseScratch(scr1); - releaseScratch(scr); - } - - // Memory access operation - - void load32(ImplicitAddress address, RegisterID dest) - { - load32(address.base, address.offset, dest); - } - - void load8(ImplicitAddress address, RegisterID dest) - { - load8(address.base, address.offset, dest); - } - - void load8(BaseIndex address, RegisterID dest) - { - RegisterID scr = claimScratch(); - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - load8(scr, address.offset, dest); - releaseScratch(scr); - } - - void load8Signed(BaseIndex address, RegisterID dest) - { - RegisterID scr = claimScratch(); - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - load8Signed(scr, address.offset, dest); - releaseScratch(scr); - } - - void load32(BaseIndex address, RegisterID dest) - { - RegisterID scr = claimScratch(); - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - load32(scr, address.offset, dest); - releaseScratch(scr); - } - - void load32(const void* address, RegisterID dest) - { - m_assembler.loadConstant(reinterpret_cast(const_cast(address)), dest); - m_assembler.movlMemReg(dest, dest); - } - - void load32(RegisterID base, int offset, RegisterID dest) - { - if (!offset) { - m_assembler.movlMemReg(base, dest); - return; - } - - if ((offset >= 0) && (offset < 64)) { - m_assembler.movlMemReg(offset >> 2, base, dest); - return; - } - - if ((dest == SH4Registers::r0) && (dest != base)) { - m_assembler.loadConstant((offset), dest); - m_assembler.movlR0mr(base, dest); - return; - } - - RegisterID scr; - if (dest == base) - scr = claimScratch(); - else - scr = dest; - m_assembler.loadConstant((offset), scr); - m_assembler.addlRegReg(base, scr); - m_assembler.movlMemReg(scr, dest); - - if (dest == base) - releaseScratch(scr); - } - - void load8Signed(RegisterID base, int offset, RegisterID dest) - { - if (!offset) { - m_assembler.movbMemReg(base, dest); - return; - } - - if ((offset > 0) && (offset < 64) && (dest == SH4Registers::r0)) { - m_assembler.movbMemReg(offset, base, dest); - return; - } - - if (base != dest) { - m_assembler.loadConstant((offset), dest); - m_assembler.addlRegReg(base, dest); - m_assembler.movbMemReg(dest, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant((offset), scr); - m_assembler.addlRegReg(base, scr); - m_assembler.movbMemReg(scr, dest); - releaseScratch(scr); - } - - void load8(RegisterID base, int offset, RegisterID dest) - { - if (!offset) { - m_assembler.movbMemReg(base, dest); - m_assembler.extub(dest, dest); - return; - } - - if ((offset > 0) && (offset < 64) && (dest == SH4Registers::r0)) { - m_assembler.movbMemReg(offset, base, dest); - m_assembler.extub(dest, dest); - return; - } - - if (base != dest) { - m_assembler.loadConstant((offset), dest); - m_assembler.addlRegReg(base, dest); - m_assembler.movbMemReg(dest, dest); - m_assembler.extub(dest, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant((offset), scr); - m_assembler.addlRegReg(base, scr); - m_assembler.movbMemReg(scr, dest); - m_assembler.extub(dest, dest); - releaseScratch(scr); - } - - void load32(RegisterID r0, RegisterID src, RegisterID dst) - { - ASSERT(r0 == SH4Registers::r0); - m_assembler.movlR0mr(src, dst); - } - - void load32(RegisterID src, RegisterID dst) - { - m_assembler.movlMemReg(src, dst); - } - - void load16(ImplicitAddress address, RegisterID dest) - { - if (!address.offset) { - m_assembler.movwMemReg(address.base, dest); - extuw(dest, dest); - return; - } - - if ((address.offset > 0) && (address.offset < 64) && (dest == SH4Registers::r0)) { - m_assembler.movwMemReg(address.offset, address.base, dest); - extuw(dest, dest); - return; - } - - if (address.base != dest) { - m_assembler.loadConstant((address.offset), dest); - m_assembler.addlRegReg(address.base, dest); - m_assembler.movwMemReg(dest, dest); - extuw(dest, dest); - return; - } - - RegisterID scr = claimScratch(); - m_assembler.loadConstant((address.offset), scr); - m_assembler.addlRegReg(address.base, scr); - m_assembler.movwMemReg(scr, dest); - extuw(dest, dest); - releaseScratch(scr); - } - - void load16Unaligned(BaseIndex address, RegisterID dest) - { - - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - - if (address.offset) - add32(TrustedImm32(address.offset), scr); - - add32(address.base, scr); - load8(scr, scr1); - add32(TrustedImm32(1), scr); - load8(scr, dest); - m_assembler.shllImm8r(8, dest); - or32(scr1, dest); - - releaseScratch(scr); - releaseScratch(scr1); - } - - void load16(RegisterID src, RegisterID dest) - { - m_assembler.movwMemReg(src, dest); - extuw(dest, dest); - } - - void load16Signed(RegisterID src, RegisterID dest) - { - m_assembler.movwMemReg(src, dest); - } - - void load16(RegisterID r0, RegisterID src, RegisterID dest) - { - ASSERT(r0 == SH4Registers::r0); - m_assembler.movwR0mr(src, dest); - extuw(dest, dest); - } - - void load16Signed(RegisterID r0, RegisterID src, RegisterID dest) - { - ASSERT(r0 == SH4Registers::r0); - m_assembler.movwR0mr(src, dest); - } - - void load16(BaseIndex address, RegisterID dest) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - - if (address.offset) - add32(TrustedImm32(address.offset), scr); - if (address.base == SH4Registers::r0) - load16(address.base, scr, dest); - else { - add32(address.base, scr); - load16(scr, dest); - } - - releaseScratch(scr); - } - - void load16Signed(BaseIndex address, RegisterID dest) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - - if (address.offset) - add32(TrustedImm32(address.offset), scr); - if (address.base == SH4Registers::r0) - load16Signed(address.base, scr, dest); - else { - add32(address.base, scr); - load16Signed(scr, dest); - } - - releaseScratch(scr); - } - - void store8(RegisterID src, BaseIndex address) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - - m_assembler.movbRegMem(src, scr); - - releaseScratch(scr); - } - - void store16(RegisterID src, BaseIndex address) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - - m_assembler.movwRegMem(src, scr); - - releaseScratch(scr); - } - - void store32(RegisterID src, ImplicitAddress address) - { - RegisterID scr = claimScratch(); - store32(src, address.offset, address.base, scr); - releaseScratch(scr); - } - - void store32(RegisterID src, int offset, RegisterID base, RegisterID scr) - { - if (!offset) { - m_assembler.movlRegMem(src, base); - return; - } - - if ((offset >=0) && (offset < 64)) { - m_assembler.movlRegMem(src, offset >> 2, base); - return; - } - - m_assembler.loadConstant((offset), scr); - if (scr == SH4Registers::r0) { - m_assembler.movlRegMemr0(src, base); - return; - } - - m_assembler.addlRegReg(base, scr); - m_assembler.movlRegMem(src, scr); - } - - void store32(RegisterID src, RegisterID offset, RegisterID base) - { - ASSERT(offset == SH4Registers::r0); - m_assembler.movlRegMemr0(src, base); - } - - void store32(RegisterID src, RegisterID dst) - { - m_assembler.movlRegMem(src, dst); - } - - void store32(TrustedImm32 imm, ImplicitAddress address) - { - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - m_assembler.loadConstant((imm.m_value), scr); - store32(scr, address.offset, address.base, scr1); - releaseScratch(scr); - releaseScratch(scr1); - } - - void store32(RegisterID src, BaseIndex address) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - store32(src, Address(scr, address.offset)); - - releaseScratch(scr); - } - - void store32(TrustedImm32 imm, void* address) - { - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - m_assembler.loadConstant((imm.m_value), scr); - m_assembler.loadConstant(reinterpret_cast(address), scr1); - m_assembler.movlRegMem(scr, scr1); - releaseScratch(scr); - releaseScratch(scr1); - } - - void store32(RegisterID src, void* address) - { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(reinterpret_cast(address), scr); - m_assembler.movlRegMem(src, scr); - releaseScratch(scr); - } - - DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) - { - RegisterID scr = claimScratch(); - DataLabel32 label(this); - m_assembler.loadConstantUnReusable(address.offset, scr); - m_assembler.addlRegReg(address.base, scr); - m_assembler.movlMemReg(scr, dest); - releaseScratch(scr); - return label; - } - - DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) - { - RegisterID scr = claimScratch(); - DataLabel32 label(this); - m_assembler.loadConstantUnReusable(address.offset, scr); - m_assembler.addlRegReg(address.base, scr); - m_assembler.movlRegMem(src, scr); - releaseScratch(scr); - return label; - } - - DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - DataLabelCompact dataLabel(this); - ASSERT(address.offset <= MaximumCompactPtrAlignedAddressOffset); - ASSERT(address.offset >= 0); - m_assembler.movlMemRegCompact(address.offset >> 2, address.base, dest); - return dataLabel; - } - - ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) - { - ConvertibleLoadLabel result(this); - - RegisterID scr = claimScratch(); - m_assembler.movImm8(address.offset, scr); - m_assembler.addlRegReg(address.base, scr); - m_assembler.movlMemReg(scr, dest); - releaseScratch(scr); - - return result; - } - - // Floating-point operations - - static bool supportsFloatingPoint() { return true; } - static bool supportsFloatingPointTruncate() { return true; } - static bool supportsFloatingPointSqrt() { return true; } - static bool supportsFloatingPointAbs() { return false; } - - void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) - { - m_assembler.fldsfpul((FPRegisterID)(src + 1)); - m_assembler.stsfpulReg(dest1); - m_assembler.fldsfpul(src); - m_assembler.stsfpulReg(dest2); - } - - void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) - { - UNUSED_PARAM(scratch); - m_assembler.ldsrmfpul(src1); - m_assembler.fstsfpul((FPRegisterID)(dest + 1)); - m_assembler.ldsrmfpul(src2); - m_assembler.fstsfpul(dest); - } - - void loadFloat(BaseIndex address, FPRegisterID dest) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - if (address.offset) - add32(TrustedImm32(address.offset), scr); - - m_assembler.fmovsReadrm(scr, dest); - releaseScratch(scr); - } - - void loadDouble(BaseIndex address, FPRegisterID dest) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - if (address.offset) - add32(TrustedImm32(address.offset), scr); - - m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); - m_assembler.fmovsReadrm(scr, dest); - releaseScratch(scr); - } - - void loadDouble(ImplicitAddress address, FPRegisterID dest) - { - RegisterID scr = claimScratch(); - - m_assembler.loadConstant(address.offset, scr); - if (address.base == SH4Registers::r0) { - m_assembler.fmovsReadr0r(scr, (FPRegisterID)(dest + 1)); - m_assembler.addlImm8r(4, scr); - m_assembler.fmovsReadr0r(scr, dest); - releaseScratch(scr); - return; - } - - m_assembler.addlRegReg(address.base, scr); - m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); - m_assembler.fmovsReadrm(scr, dest); - releaseScratch(scr); - } - - void loadDouble(const void* address, FPRegisterID dest) - { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(reinterpret_cast(address), scr); - m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); - m_assembler.fmovsReadrm(scr, dest); - releaseScratch(scr); - } - - void storeFloat(FPRegisterID src, BaseIndex address) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - if (address.offset) - add32(TrustedImm32(address.offset), scr); - - m_assembler.fmovsWriterm(src, scr); - - releaseScratch(scr); - } - - void storeDouble(FPRegisterID src, ImplicitAddress address) - { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(address.offset, scr); - m_assembler.addlRegReg(address.base, scr); - m_assembler.fmovsWriterm((FPRegisterID)(src + 1), scr); - m_assembler.addlImm8r(4, scr); - m_assembler.fmovsWriterm(src, scr); - releaseScratch(scr); - } - - void storeDouble(FPRegisterID src, BaseIndex address) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - if (address.offset) - add32(TrustedImm32(address.offset), scr); - - m_assembler.fmovsWriterm((FPRegisterID)(src + 1), scr); - m_assembler.addlImm8r(4, scr); - m_assembler.fmovsWriterm(src, scr); - - releaseScratch(scr); - } - - void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - if (op1 == dest) - m_assembler.daddRegReg(op2, dest); - else { - m_assembler.dmovRegReg(op1, dest); - m_assembler.daddRegReg(op2, dest); - } - } - - void addDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.daddRegReg(src, dest); - } - - void addDouble(AbsoluteAddress address, FPRegisterID dest) - { - loadDouble(address.m_ptr, fscratch); - addDouble(fscratch, dest); - } - - void addDouble(Address address, FPRegisterID dest) - { - loadDouble(address, fscratch); - addDouble(fscratch, dest); - } - - void subDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.dsubRegReg(src, dest); - } - - void subDouble(Address address, FPRegisterID dest) - { - loadDouble(address, fscratch); - subDouble(fscratch, dest); - } - - void mulDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.dmulRegReg(src, dest); - } - - void mulDouble(Address address, FPRegisterID dest) - { - loadDouble(address, fscratch); - mulDouble(fscratch, dest); - } - - void divDouble(FPRegisterID src, FPRegisterID dest) - { - m_assembler.ddivRegReg(src, dest); - } - - void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) - { - m_assembler.fldsfpul(src); - m_assembler.dcnvsd(dst); - } - - void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) - { - m_assembler.dcnvds(src); - m_assembler.fstsfpul(dst); - } - - void convertInt32ToDouble(RegisterID src, FPRegisterID dest) - { - m_assembler.ldsrmfpul(src); - m_assembler.floatfpulDreg(dest); - } - - void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) - { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(reinterpret_cast(src.m_ptr), scr); - convertInt32ToDouble(scr, dest); - releaseScratch(scr); - } - - void convertInt32ToDouble(Address src, FPRegisterID dest) - { - RegisterID scr = claimScratch(); - load32(src, scr); - convertInt32ToDouble(scr, dest); - releaseScratch(scr); - } - - void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) - { - RegisterID scr = claimScratch(); - RegisterID scr1 = claimScratch(); - Jump m_jump; - JumpList end; - - if (dest != SH4Registers::r0) - move(SH4Registers::r0, scr1); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - - if (address.offset) - add32(TrustedImm32(address.offset), scr); - - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 68, sizeof(uint32_t)); - move(scr, SH4Registers::r0); - m_assembler.andlImm8r(0x3, SH4Registers::r0); - m_assembler.cmpEqImmR0(0x0, SH4Registers::r0); - m_jump = Jump(m_assembler.jne(), SH4Assembler::JumpNear); - if (dest != SH4Registers::r0) - move(scr1, SH4Registers::r0); - - load32(scr, dest); - end.append(Jump(m_assembler.bra(), SH4Assembler::JumpNear)); - m_assembler.nop(); - m_jump.link(this); - m_assembler.andlImm8r(0x1, SH4Registers::r0); - m_assembler.cmpEqImmR0(0x0, SH4Registers::r0); - - if (dest != SH4Registers::r0) - move(scr1, SH4Registers::r0); - - m_jump = Jump(m_assembler.jne(), SH4Assembler::JumpNear); - load16(scr, scr1); - add32(TrustedImm32(2), scr); - load16(scr, dest); - m_assembler.shllImm8r(16, dest); - or32(scr1, dest); - end.append(Jump(m_assembler.bra(), SH4Assembler::JumpNear)); - m_assembler.nop(); - m_jump.link(this); - load8(scr, scr1); - add32(TrustedImm32(1), scr); - load16(scr, dest); - m_assembler.shllImm8r(8, dest); - or32(dest, scr1); - add32(TrustedImm32(2), scr); - load8(scr, dest); - m_assembler.shllImm8r(8, dest); - m_assembler.shllImm8r(16, dest); - or32(scr1, dest); - end.link(this); - - releaseScratch(scr); - releaseScratch(scr1); - } - - Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - RegisterID scr = scratchReg3; - load32WithUnalignedHalfWords(left, scr); - if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) - m_assembler.testlRegReg(scr, scr); - else - compare32(right.m_value, scr, cond); - - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) - { - m_assembler.movImm8(0, scratchReg3); - convertInt32ToDouble(scratchReg3, scratch); - return branchDouble(DoubleNotEqual, reg, scratch); - } - - Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) - { - m_assembler.movImm8(0, scratchReg3); - convertInt32ToDouble(scratchReg3, scratch); - return branchDouble(DoubleEqualOrUnordered, reg, scratch); - } - - Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) - { - if (cond == DoubleEqual) { - m_assembler.dcmppeq(right, left); - return branchTrue(); - } - - if (cond == DoubleNotEqual) { - RegisterID scr = claimScratch(); - JumpList end; - m_assembler.loadConstant(0x7fbfffff, scratchReg3); - m_assembler.dcnvds(right); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcnvds(left); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcmppeq(right, left); - releaseScratch(scr); - Jump m_jump = branchFalse(); - end.link(this); - return m_jump; - } - - if (cond == DoubleGreaterThan) { - m_assembler.dcmppgt(right, left); - return branchTrue(); - } - - if (cond == DoubleGreaterThanOrEqual) { - m_assembler.dcmppgt(left, right); - return branchFalse(); - } - - if (cond == DoubleLessThan) { - m_assembler.dcmppgt(left, right); - return branchTrue(); - } - - if (cond == DoubleLessThanOrEqual) { - m_assembler.dcmppgt(right, left); - return branchFalse(); - } - - if (cond == DoubleEqualOrUnordered) { - RegisterID scr = claimScratch(); - JumpList end; - m_assembler.loadConstant(0x7fbfffff, scratchReg3); - m_assembler.dcnvds(right); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcnvds(left); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcmppeq(left, right); - Jump m_jump = Jump(m_assembler.je()); - end.link(this); - m_assembler.extraInstrForBranch(scr); - releaseScratch(scr); - return m_jump; - } - - if (cond == DoubleGreaterThanOrUnordered) { - RegisterID scr = claimScratch(); - JumpList end; - m_assembler.loadConstant(0x7fbfffff, scratchReg3); - m_assembler.dcnvds(right); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcnvds(left); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcmppgt(right, left); - Jump m_jump = Jump(m_assembler.je()); - end.link(this); - m_assembler.extraInstrForBranch(scr); - releaseScratch(scr); - return m_jump; - } - - if (cond == DoubleGreaterThanOrEqualOrUnordered) { - RegisterID scr = claimScratch(); - JumpList end; - m_assembler.loadConstant(0x7fbfffff, scratchReg3); - m_assembler.dcnvds(right); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcnvds(left); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcmppgt(left, right); - Jump m_jump = Jump(m_assembler.jne()); - end.link(this); - m_assembler.extraInstrForBranch(scr); - releaseScratch(scr); - return m_jump; - } - - if (cond == DoubleLessThanOrUnordered) { - RegisterID scr = claimScratch(); - JumpList end; - m_assembler.loadConstant(0x7fbfffff, scratchReg3); - m_assembler.dcnvds(right); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcnvds(left); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcmppgt(left, right); - Jump m_jump = Jump(m_assembler.je()); - end.link(this); - m_assembler.extraInstrForBranch(scr); - releaseScratch(scr); - return m_jump; - } - - if (cond == DoubleLessThanOrEqualOrUnordered) { - RegisterID scr = claimScratch(); - JumpList end; - m_assembler.loadConstant(0x7fbfffff, scratchReg3); - m_assembler.dcnvds(right); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcnvds(left); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcmppgt(right, left); - Jump m_jump = Jump(m_assembler.jne()); - end.link(this); - m_assembler.extraInstrForBranch(scr); - releaseScratch(scr); - return m_jump; - } - - ASSERT(cond == DoubleNotEqualOrUnordered); - RegisterID scr = claimScratch(); - JumpList end; - m_assembler.loadConstant(0x7fbfffff, scratchReg3); - m_assembler.dcnvds(right); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcnvds(left); - m_assembler.stsfpulReg(scr); - m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); - end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); - m_assembler.dcmppeq(right, left); - Jump m_jump = Jump(m_assembler.jne()); - end.link(this); - m_assembler.extraInstrForBranch(scr); - releaseScratch(scr); - return m_jump; - } - - Jump branchTrue() - { - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 6, sizeof(uint32_t)); - Jump m_jump = Jump(m_assembler.je()); - m_assembler.extraInstrForBranch(scratchReg3); - return m_jump; - } - - Jump branchFalse() - { - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 6, sizeof(uint32_t)); - Jump m_jump = Jump(m_assembler.jne()); - m_assembler.extraInstrForBranch(scratchReg3); - return m_jump; - } - - Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - RegisterID scr = claimScratch(); - move(left.index, scr); - lshift32(TrustedImm32(left.scale), scr); - add32(left.base, scr); - load32(scr, left.offset, scr); - compare32(right.m_value, scr, cond); - releaseScratch(scr); - - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - void sqrtDouble(FPRegisterID src, FPRegisterID dest) - { - if (dest != src) - m_assembler.dmovRegReg(src, dest); - m_assembler.dsqrt(dest); - } - - void absDouble(FPRegisterID, FPRegisterID) - { - ASSERT_NOT_REACHED(); - } - - Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - RegisterID addressTempRegister = claimScratch(); - load8(address, addressTempRegister); - Jump jmp = branchTest32(cond, addressTempRegister, mask); - releaseScratch(addressTempRegister); - return jmp; - } - - Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - RegisterID addressTempRegister = claimScratch(); - move(TrustedImmPtr(address.m_ptr), addressTempRegister); - load8(Address(addressTempRegister), addressTempRegister); - Jump jmp = branchTest32(cond, addressTempRegister, mask); - releaseScratch(addressTempRegister); - return jmp; - } - - void signExtend32ToPtr(RegisterID src, RegisterID dest) - { - if (src != dest) - move(src, dest); - } - - Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) - { - RegisterID addressTempRegister = claimScratch(); - load8(left, addressTempRegister); - Jump jmp = branch32(cond, addressTempRegister, right); - releaseScratch(addressTempRegister); - return jmp; - } - - void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) - { - RegisterID addressTempRegister = claimScratch(); - load8(left, addressTempRegister); - compare32(cond, addressTempRegister, right, dest); - releaseScratch(addressTempRegister); - } - - Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) - { - m_assembler.ftrcdrmfpul(src); - m_assembler.stsfpulReg(dest); - m_assembler.loadConstant(0x7fffffff, scratchReg3); - m_assembler.cmplRegReg(dest, scratchReg3, SH4Condition(Equal)); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 14, sizeof(uint32_t)); - m_assembler.branch(BT_OPCODE, 2); - m_assembler.addlImm8r(1, scratchReg3); - m_assembler.cmplRegReg(dest, scratchReg3, SH4Condition(Equal)); - return branchTrue(); - } - - // Stack manipulation operations - - void pop(RegisterID dest) - { - m_assembler.popReg(dest); - } - - void push(RegisterID src) - { - m_assembler.pushReg(src); - } - - void push(Address address) - { - if (!address.offset) { - push(address.base); - return; - } - - if ((address.offset < 0) || (address.offset >= 64)) { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(address.offset, scr); - m_assembler.addlRegReg(address.base, scr); - m_assembler.movlMemReg(scr, SH4Registers::sp); - m_assembler.addlImm8r(-4, SH4Registers::sp); - releaseScratch(scr); - return; - } - - m_assembler.movlMemReg(address.offset >> 2, address.base, SH4Registers::sp); - m_assembler.addlImm8r(-4, SH4Registers::sp); - } - - void push(TrustedImm32 imm) - { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(imm.m_value, scr); - push(scr); - releaseScratch(scr); - } - - // Register move operations - - void move(TrustedImm32 imm, RegisterID dest) - { - m_assembler.loadConstant(imm.m_value, dest); - } - - DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) - { - m_assembler.ensureSpace(m_assembler.maxInstructionSize, sizeof(uint32_t)); - DataLabelPtr dataLabel(this); - m_assembler.loadConstantUnReusable(reinterpret_cast(initialValue.m_value), dest); - return dataLabel; - } - - void move(RegisterID src, RegisterID dest) - { - if (src != dest) - m_assembler.movlRegReg(src, dest); - } - - void move(TrustedImmPtr imm, RegisterID dest) - { - m_assembler.loadConstant(imm.asIntptr(), dest); - } - - void extuw(RegisterID src, RegisterID dst) - { - m_assembler.extuw(src, dst); - } - - void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) - { - m_assembler.cmplRegReg(right, left, SH4Condition(cond)); - if (cond != NotEqual) { - m_assembler.movt(dest); - return; - } - - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 4); - m_assembler.movImm8(0, dest); - m_assembler.branch(BT_OPCODE, 0); - m_assembler.movImm8(1, dest); - } - - void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - if (left != dest) { - move(right, dest); - compare32(cond, left, dest, dest); - return; - } - - RegisterID scr = claimScratch(); - move(right, scr); - compare32(cond, left, scr, dest); - releaseScratch(scr); - } - - void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - ASSERT((cond == Zero) || (cond == NonZero)); - - load8(address, dest); - if (mask.m_value == -1) - compare32(0, dest, static_cast(cond)); - else - testlImm(mask.m_value, dest); - if (cond != NonZero) { - m_assembler.movt(dest); - return; - } - - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 4); - m_assembler.movImm8(0, dest); - m_assembler.branch(BT_OPCODE, 0); - m_assembler.movImm8(1, dest); - } - - void loadPtrLinkReg(ImplicitAddress address) - { - RegisterID scr = claimScratch(); - load32(address, scr); - m_assembler.ldspr(scr); - releaseScratch(scr); - } - - Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) - { - m_assembler.cmplRegReg(right, left, SH4Condition(cond)); - /* BT label => BF off - nop LDR reg - nop braf @reg - nop nop - */ - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) - { - if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) - m_assembler.testlRegReg(left, left); - else - compare32(right.m_value, left, cond); - - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branch32(RelationalCondition cond, RegisterID left, Address right) - { - compare32(right.offset, right.base, left, cond); - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branch32(RelationalCondition cond, Address left, RegisterID right) - { - compare32(right, left.offset, left.base, cond); - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) - { - compare32(right.m_value, left.offset, left.base, cond); - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - RegisterID scr = claimScratch(); - - move(TrustedImm32(reinterpret_cast(left.m_ptr)), scr); - m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); - releaseScratch(scr); - - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) - { - RegisterID addressTempRegister = claimScratch(); - - m_assembler.loadConstant(reinterpret_cast(left.m_ptr), addressTempRegister); - m_assembler.movlMemReg(addressTempRegister, addressTempRegister); - compare32(right.m_value, addressTempRegister, cond); - releaseScratch(addressTempRegister); - - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - ASSERT(!(right.m_value & 0xFFFFFF00)); - RegisterID scr = claimScratch(); - - move(left.index, scr); - lshift32(TrustedImm32(left.scale), scr); - - if (left.offset) - add32(TrustedImm32(left.offset), scr); - add32(left.base, scr); - load8(scr, scr); - RegisterID scr1 = claimScratch(); - m_assembler.loadConstant(right.m_value, scr1); - releaseScratch(scr); - releaseScratch(scr1); - - return branch32(cond, scr, scr1); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) - { - ASSERT((cond == Zero) || (cond == NonZero)); - - m_assembler.testlRegReg(reg, mask); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - ASSERT((cond == Zero) || (cond == NonZero)); - - if (mask.m_value == -1) - m_assembler.testlRegReg(reg, reg); - else - testlImm(mask.m_value, reg); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - ASSERT((cond == Zero) || (cond == NonZero)); - - if (mask.m_value == -1) - compare32(0, address.offset, address.base, static_cast(cond)); - else - testImm(mask.m_value, address.offset, address.base); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - RegisterID scr = claimScratch(); - - move(address.index, scr); - lshift32(TrustedImm32(address.scale), scr); - add32(address.base, scr); - load32(scr, address.offset, scr); - - if (mask.m_value == -1) - m_assembler.testlRegReg(scr, scr); - else - testlImm(mask.m_value, scr); - - releaseScratch(scr); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump jump() - { - return Jump(m_assembler.jmp()); - } - - void jump(RegisterID target) - { - m_assembler.jmpReg(target); - } - - void jump(Address address) - { - RegisterID scr = claimScratch(); - - if ((address.offset < 0) || (address.offset >= 64)) { - m_assembler.loadConstant(address.offset, scr); - m_assembler.addlRegReg(address.base, scr); - m_assembler.movlMemReg(scr, scr); - } else if (address.offset) - m_assembler.movlMemReg(address.offset >> 2, address.base, scr); - else - m_assembler.movlMemReg(address.base, scr); - m_assembler.jmpReg(scr); - - releaseScratch(scr); - } - - // Arithmetic control flow operations - - Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - - if (cond == Overflow) { - m_assembler.addvlRegReg(src, dest); - return branchTrue(); - } - - if (cond == Signed) { - m_assembler.addlRegReg(src, dest); - // Check if dest is negative - m_assembler.cmppz(dest); - return branchFalse(); - } - - m_assembler.addlRegReg(src, dest); - compare32(0, dest, Equal); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - - move(imm, scratchReg3); - return branchAdd32(cond, scratchReg3, dest); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - - if (src != dest) - move(src, dest); - - if (cond == Overflow) { - move(imm, scratchReg3); - m_assembler.addvlRegReg(scratchReg3, dest); - return branchTrue(); - } - - add32(imm, dest); - - if (cond == Signed) { - m_assembler.cmppz(dest); - return branchFalse(); - } - - compare32(0, dest, Equal); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - - if (cond == Overflow) { - RegisterID scr1 = claimScratch(); - RegisterID scr = claimScratch(); - m_assembler.dmullRegReg(src, dest); - m_assembler.stsmacl(dest); - m_assembler.movImm8(-31, scr); - m_assembler.movlRegReg(dest, scr1); - m_assembler.shaRegReg(scr1, scr); - m_assembler.stsmach(scr); - m_assembler.cmplRegReg(scr, scr1, SH4Condition(Equal)); - releaseScratch(scr1); - releaseScratch(scr); - return branchFalse(); - } - - m_assembler.imullRegReg(src, dest); - m_assembler.stsmacl(dest); - if (cond == Signed) { - // Check if dest is negative - m_assembler.cmppz(dest); - return branchFalse(); - } - - compare32(0, dest, static_cast(cond)); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - - move(imm, scratchReg3); - if (src != dest) - move(src, dest); - - return branchMul32(cond, scratchReg3, dest); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - - if (cond == Overflow) { - m_assembler.subvlRegReg(src, dest); - return branchTrue(); - } - - if (cond == Signed) { - // Check if dest is negative - m_assembler.sublRegReg(src, dest); - compare32(0, dest, LessThan); - return branchTrue(); - } - - sub32(src, dest); - compare32(0, dest, static_cast(cond)); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); - - move(imm, scratchReg3); - return branchSub32(cond, scratchReg3, dest); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) - { - move(imm, scratchReg3); - if (src != dest) - move(src, dest); - return branchSub32(cond, scratchReg3, dest); - } - - Jump branchSub32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) - { - if (src1 != dest) - move(src1, dest); - return branchSub32(cond, src2, dest); - } - - Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) - { - ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); - - if (cond == Signed) { - or32(src, dest); - compare32(0, dest, static_cast(LessThan)); - return branchTrue(); - } - - or32(src, dest); - compare32(0, dest, static_cast(cond)); - - if (cond == NonZero) // NotEqual - return branchFalse(); - return branchTrue(); - } - - void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) - { - m_assembler.ftrcdrmfpul(src); - m_assembler.stsfpulReg(dest); - convertInt32ToDouble(dest, fscratch); - failureCases.append(branchDouble(DoubleNotEqualOrUnordered, fscratch, src)); - - if (dest == SH4Registers::r0) - m_assembler.cmpEqImmR0(0, dest); - else { - m_assembler.movImm8(0, scratchReg3); - m_assembler.cmplRegReg(scratchReg3, dest, SH4Condition(Equal)); - } - failureCases.append(branchTrue()); - } - - void neg32(RegisterID dst) - { - m_assembler.neg(dst, dst); - } - - void urshift32(RegisterID shiftamount, RegisterID dest) - { - if (shiftamount == SH4Registers::r0) - m_assembler.andlImm8r(0x1f, shiftamount); - else { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(0x1f, scr); - m_assembler.andlRegReg(scr, shiftamount); - releaseScratch(scr); - } - m_assembler.neg(shiftamount, shiftamount); - m_assembler.shllRegReg(dest, shiftamount); - } - - void urshift32(TrustedImm32 imm, RegisterID dest) - { - RegisterID scr = claimScratch(); - m_assembler.loadConstant(-(imm.m_value & 0x1f), scr); - m_assembler.shaRegReg(dest, scr); - releaseScratch(scr); - } - - void urshift32(RegisterID src, TrustedImm32 shiftamount, RegisterID dest) - { - if (src != dest) - move(src, dest); - - urshift32(shiftamount, dest); - } - - Call call() - { - return Call(m_assembler.call(), Call::Linkable); - } - - Call nearCall() - { - return Call(m_assembler.call(), Call::LinkableNear); - } - - Call call(RegisterID target) - { - return Call(m_assembler.call(target), Call::None); - } - - void call(Address address, RegisterID target) - { - load32(address.base, address.offset, target); - m_assembler.ensureSpace(m_assembler.maxInstructionSize + 2); - m_assembler.branch(JSR_OPCODE, target); - m_assembler.nop(); - } - - void breakpoint() - { - m_assembler.bkpt(); - m_assembler.nop(); - } - - Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - RegisterID dataTempRegister = claimScratch(); - - dataLabel = moveWithPatch(initialRightValue, dataTempRegister); - m_assembler.cmplRegReg(dataTempRegister, left, SH4Condition(cond)); - releaseScratch(dataTempRegister); - - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - RegisterID scr = claimScratch(); - - m_assembler.loadConstant(left.offset, scr); - m_assembler.addlRegReg(left.base, scr); - m_assembler.movlMemReg(scr, scr); - RegisterID scr1 = claimScratch(); - dataLabel = moveWithPatch(initialRightValue, scr1); - m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); - releaseScratch(scr); - releaseScratch(scr1); - - if (cond == NotEqual) - return branchFalse(); - return branchTrue(); - } - - void ret() - { - m_assembler.ret(); - m_assembler.nop(); - } - - DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) - { - RegisterID scr = claimScratch(); - DataLabelPtr label = moveWithPatch(initialValue, scr); - store32(scr, address); - releaseScratch(scr); - return label; - } - - DataLabelPtr storePtrWithPatch(ImplicitAddress address) { return storePtrWithPatch(TrustedImmPtr(0), address); } - - int sizeOfConstantPool() - { - return m_assembler.sizeOfConstantPool(); - } - - Call tailRecursiveCall() - { - RegisterID scr = claimScratch(); - - m_assembler.loadConstantUnReusable(0x0, scr, true); - Jump m_jump = Jump(m_assembler.jmp(scr)); - releaseScratch(scr); - - return Call::fromTailJump(m_jump); - } - - Call makeTailRecursiveCall(Jump oldJump) - { - oldJump.link(this); - return tailRecursiveCall(); - } - - void nop() - { - m_assembler.nop(); - } - - static FunctionPtr readCallTarget(CodeLocationCall call) - { - return FunctionPtr(reinterpret_cast(SH4Assembler::readCallTarget(call.dataLocation()))); - } - - static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) - { - ASSERT_NOT_REACHED(); - } - - static ptrdiff_t maxJumpReplacementSize() - { - ASSERT_NOT_REACHED(); - return 0; - } - - static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } - - static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) - { - return label.labelAtOffset(0); - } - - static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) - { - SH4Assembler::revertJump(instructionStart.dataLocation(), reinterpret_cast(initialValue) & 0xffff); - } - - static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) - { - UNREACHABLE_FOR_PLATFORM(); - return CodeLocationLabel(); - } - - static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) - { - UNREACHABLE_FOR_PLATFORM(); - } - -protected: - SH4Assembler::Condition SH4Condition(RelationalCondition cond) - { - return static_cast(cond); - } - - SH4Assembler::Condition SH4Condition(ResultCondition cond) - { - return static_cast(cond); - } -private: - friend class LinkBuffer; - friend class RepatchBuffer; - - static void linkCall(void*, Call, FunctionPtr); - static void repatchCall(CodeLocationCall, CodeLocationLabel); - static void repatchCall(CodeLocationCall, FunctionPtr); -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // MacroAssemblerSH4_h diff --git a/3rdparty/masm/assembler/MacroAssemblerX86.h b/3rdparty/masm/assembler/MacroAssemblerX86.h deleted file mode 100644 index 27a030edfd..0000000000 --- a/3rdparty/masm/assembler/MacroAssemblerX86.h +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssemblerX86_h -#define MacroAssemblerX86_h - -#if ENABLE(ASSEMBLER) && CPU(X86) - -#include "MacroAssemblerX86Common.h" - -namespace JSC { - -class MacroAssemblerX86 : public MacroAssemblerX86Common { -public: - static const Scale ScalePtr = TimesFour; - - using MacroAssemblerX86Common::add32; - using MacroAssemblerX86Common::and32; - using MacroAssemblerX86Common::branchAdd32; - using MacroAssemblerX86Common::branchSub32; - using MacroAssemblerX86Common::sub32; - using MacroAssemblerX86Common::or32; - using MacroAssemblerX86Common::load32; - using MacroAssemblerX86Common::store32; - using MacroAssemblerX86Common::store8; - using MacroAssemblerX86Common::branch32; - using MacroAssemblerX86Common::call; - using MacroAssemblerX86Common::jump; - using MacroAssemblerX86Common::addDouble; - using MacroAssemblerX86Common::loadDouble; - using MacroAssemblerX86Common::storeDouble; - using MacroAssemblerX86Common::convertInt32ToDouble; - using MacroAssemblerX86Common::branchTest8; - - void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - m_assembler.leal_mr(imm.m_value, src, dest); - } - - void add32(TrustedImm32 imm, AbsoluteAddress address) - { - m_assembler.addl_im(imm.m_value, address.m_ptr); - } - - void add32(AbsoluteAddress address, RegisterID dest) - { - m_assembler.addl_mr(address.m_ptr, dest); - } - - void add64(TrustedImm32 imm, AbsoluteAddress address) - { - m_assembler.addl_im(imm.m_value, address.m_ptr); - m_assembler.adcl_im(imm.m_value >> 31, reinterpret_cast(address.m_ptr) + sizeof(int32_t)); - } - - void and32(TrustedImm32 imm, AbsoluteAddress address) - { - m_assembler.andl_im(imm.m_value, address.m_ptr); - } - - void or32(TrustedImm32 imm, AbsoluteAddress address) - { - m_assembler.orl_im(imm.m_value, address.m_ptr); - } - - void or32(RegisterID reg, AbsoluteAddress address) - { - m_assembler.orl_rm(reg, address.m_ptr); - } - - void sub32(TrustedImm32 imm, AbsoluteAddress address) - { - m_assembler.subl_im(imm.m_value, address.m_ptr); - } - - void load32(const void* address, RegisterID dest) - { - m_assembler.movl_mr(address, dest); - } - - ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) - { - ConvertibleLoadLabel result = ConvertibleLoadLabel(this); - m_assembler.movl_mr(address.offset, address.base, dest); - return result; - } - - void addDouble(AbsoluteAddress address, FPRegisterID dest) - { - m_assembler.addsd_mr(address.m_ptr, dest); - } - - void storeDouble(FPRegisterID src, const void* address) - { - ASSERT(isSSE2Present()); - m_assembler.movsd_rm(src, address); - } - - void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) - { - m_assembler.cvtsi2sd_mr(src.m_ptr, dest); - } - - void store32(TrustedImm32 imm, void* address) - { - m_assembler.movl_i32m(imm.m_value, address); - } - - void store32(RegisterID src, void* address) - { - m_assembler.movl_rm(src, address); - } - - void store8(TrustedImm32 imm, void* address) - { - ASSERT(-128 <= imm.m_value && imm.m_value < 128); - m_assembler.movb_i8m(imm.m_value, address); - } - - // Possibly clobbers src. - void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) - { - movePackedToInt32(src, dest1); - rshiftPacked(TrustedImm32(32), src); - movePackedToInt32(src, dest2); - } - - void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) - { - moveInt32ToPacked(src1, dest); - moveInt32ToPacked(src2, scratch); - lshiftPacked(TrustedImm32(32), scratch); - orPacked(scratch, dest); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) - { - m_assembler.addl_im(imm.m_value, dest.m_ptr); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) - { - m_assembler.subl_im(imm.m_value, dest.m_ptr); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - m_assembler.cmpl_rm(right, left.m_ptr); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) - { - m_assembler.cmpl_im(right.m_value, left.m_ptr); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Call call() - { - return Call(m_assembler.call(), Call::Linkable); - } - - // Address is a memory location containing the address to jump to - void jump(AbsoluteAddress address) - { - m_assembler.jmp_m(address.m_ptr); - } - - Call tailRecursiveCall() - { - return Call::fromTailJump(jump()); - } - - Call makeTailRecursiveCall(Jump oldJump) - { - return Call::fromTailJump(oldJump); - } - - - DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) - { - padBeforePatch(); - m_assembler.movl_i32r(initialValue.asIntptr(), dest); - return DataLabelPtr(this); - } - - Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - ASSERT(mask.m_value >= -128 && mask.m_value <= 255); - if (mask.m_value == -1) - m_assembler.cmpb_im(0, address.m_ptr); - else - m_assembler.testb_im(mask.m_value, address.m_ptr); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - padBeforePatch(); - m_assembler.cmpl_ir_force32(initialRightValue.asIntptr(), left); - dataLabel = DataLabelPtr(this); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - padBeforePatch(); - m_assembler.cmpl_im_force32(initialRightValue.asIntptr(), left.offset, left.base); - dataLabel = DataLabelPtr(this); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) - { - padBeforePatch(); - m_assembler.movl_i32m(initialValue.asIntptr(), address.offset, address.base); - return DataLabelPtr(this); - } - - static bool supportsFloatingPoint() { return isSSE2Present(); } - // See comment on MacroAssemblerARMv7::supportsFloatingPointTruncate() - static bool supportsFloatingPointTruncate() { return isSSE2Present(); } - static bool supportsFloatingPointSqrt() { return isSSE2Present(); } - static bool supportsFloatingPointAbs() { return isSSE2Present(); } - - static FunctionPtr readCallTarget(CodeLocationCall call) - { - intptr_t offset = reinterpret_cast(call.dataLocation())[-1]; - return FunctionPtr(reinterpret_cast(reinterpret_cast(call.dataLocation()) + offset)); - } - - static bool canJumpReplacePatchableBranchPtrWithPatch() { return true; } - - static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) - { - const int opcodeBytes = 1; - const int modRMBytes = 1; - const int immediateBytes = 4; - const int totalBytes = opcodeBytes + modRMBytes + immediateBytes; - ASSERT(totalBytes >= maxJumpReplacementSize()); - return label.labelAtOffset(-totalBytes); - } - - static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) - { - const int opcodeBytes = 1; - const int modRMBytes = 1; - const int offsetBytes = 0; - const int immediateBytes = 4; - const int totalBytes = opcodeBytes + modRMBytes + offsetBytes + immediateBytes; - ASSERT(totalBytes >= maxJumpReplacementSize()); - return label.labelAtOffset(-totalBytes); - } - - static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID reg, void* initialValue) - { - X86Assembler::revertJumpTo_cmpl_ir_force32(instructionStart.executableAddress(), reinterpret_cast(initialValue), reg); - } - - static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address address, void* initialValue) - { - ASSERT(!address.offset); - X86Assembler::revertJumpTo_cmpl_im_force32(instructionStart.executableAddress(), reinterpret_cast(initialValue), 0, address.base); - } - -private: - friend class LinkBuffer; - friend class RepatchBuffer; - - static void linkCall(void* code, Call call, FunctionPtr function) - { - X86Assembler::linkCall(code, call.m_label, function.value()); - } - - static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) - { - X86Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } - - static void repatchCall(CodeLocationCall call, FunctionPtr destination) - { - X86Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); - } -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // MacroAssemblerX86_h diff --git a/3rdparty/masm/assembler/MacroAssemblerX86Common.h b/3rdparty/masm/assembler/MacroAssemblerX86Common.h deleted file mode 100644 index 53cb80c210..0000000000 --- a/3rdparty/masm/assembler/MacroAssemblerX86Common.h +++ /dev/null @@ -1,1541 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssemblerX86Common_h -#define MacroAssemblerX86Common_h - -#if ENABLE(ASSEMBLER) - -#include "X86Assembler.h" -#include "AbstractMacroAssembler.h" - -namespace JSC { - -class MacroAssemblerX86Common : public AbstractMacroAssembler { -protected: -#if CPU(X86_64) - static const X86Registers::RegisterID scratchRegister = X86Registers::r11; -#endif - - static const int DoubleConditionBitInvert = 0x10; - static const int DoubleConditionBitSpecial = 0x20; - static const int DoubleConditionBits = DoubleConditionBitInvert | DoubleConditionBitSpecial; - -public: - typedef X86Assembler::FPRegisterID FPRegisterID; - typedef X86Assembler::XMMRegisterID XMMRegisterID; - - static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) - { - return value >= -128 && value <= 127; - } - - enum RelationalCondition { - Equal = X86Assembler::ConditionE, - NotEqual = X86Assembler::ConditionNE, - Above = X86Assembler::ConditionA, - AboveOrEqual = X86Assembler::ConditionAE, - Below = X86Assembler::ConditionB, - BelowOrEqual = X86Assembler::ConditionBE, - GreaterThan = X86Assembler::ConditionG, - GreaterThanOrEqual = X86Assembler::ConditionGE, - LessThan = X86Assembler::ConditionL, - LessThanOrEqual = X86Assembler::ConditionLE - }; - - enum ResultCondition { - Overflow = X86Assembler::ConditionO, - Signed = X86Assembler::ConditionS, - Zero = X86Assembler::ConditionE, - NonZero = X86Assembler::ConditionNE - }; - - enum DoubleCondition { - // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. - DoubleEqual = X86Assembler::ConditionE | DoubleConditionBitSpecial, - DoubleNotEqual = X86Assembler::ConditionNE, - DoubleGreaterThan = X86Assembler::ConditionA, - DoubleGreaterThanOrEqual = X86Assembler::ConditionAE, - DoubleLessThan = X86Assembler::ConditionA | DoubleConditionBitInvert, - DoubleLessThanOrEqual = X86Assembler::ConditionAE | DoubleConditionBitInvert, - // If either operand is NaN, these conditions always evaluate to true. - DoubleEqualOrUnordered = X86Assembler::ConditionE, - DoubleNotEqualOrUnordered = X86Assembler::ConditionNE | DoubleConditionBitSpecial, - DoubleGreaterThanOrUnordered = X86Assembler::ConditionB | DoubleConditionBitInvert, - DoubleGreaterThanOrEqualOrUnordered = X86Assembler::ConditionBE | DoubleConditionBitInvert, - DoubleLessThanOrUnordered = X86Assembler::ConditionB, - DoubleLessThanOrEqualOrUnordered = X86Assembler::ConditionBE, - }; - COMPILE_ASSERT( - !((X86Assembler::ConditionE | X86Assembler::ConditionNE | X86Assembler::ConditionA | X86Assembler::ConditionAE | X86Assembler::ConditionB | X86Assembler::ConditionBE) & DoubleConditionBits), - DoubleConditionBits_should_not_interfere_with_X86Assembler_Condition_codes); - - static const RegisterID stackPointerRegister = X86Registers::esp; - -#if ENABLE(JIT_CONSTANT_BLINDING) - static bool shouldBlindForSpecificArch(uint32_t value) { return value >= 0x00ffffff; } -#if CPU(X86_64) - static bool shouldBlindForSpecificArch(uint64_t value) { return value >= 0x00ffffff; } -#if OS(DARWIN) // On 64-bit systems other than DARWIN uint64_t and uintptr_t are the same type so overload is prohibited. - static bool shouldBlindForSpecificArch(uintptr_t value) { return value >= 0x00ffffff; } -#endif -#endif -#endif - - // Integer arithmetic operations: - // - // Operations are typically two operand - operation(source, srcDst) - // For many operations the source may be an TrustedImm32, the srcDst operand - // may often be a memory location (explictly described using an Address - // object). - - void add32(RegisterID src, RegisterID dest) - { - m_assembler.addl_rr(src, dest); - } - - void add32(TrustedImm32 imm, Address address) - { - m_assembler.addl_im(imm.m_value, address.offset, address.base); - } - - void add32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.addl_ir(imm.m_value, dest); - } - - void add32(Address src, RegisterID dest) - { - m_assembler.addl_mr(src.offset, src.base, dest); - } - - void add32(RegisterID src, Address dest) - { - m_assembler.addl_rm(src, dest.offset, dest.base); - } - - void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - m_assembler.leal_mr(imm.m_value, src, dest); - } - - void and32(RegisterID src, RegisterID dest) - { - m_assembler.andl_rr(src, dest); - } - - void and32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.andl_ir(imm.m_value, dest); - } - - void and32(RegisterID src, Address dest) - { - m_assembler.andl_rm(src, dest.offset, dest.base); - } - - void and32(Address src, RegisterID dest) - { - m_assembler.andl_mr(src.offset, src.base, dest); - } - - void and32(TrustedImm32 imm, Address address) - { - m_assembler.andl_im(imm.m_value, address.offset, address.base); - } - - void and32(RegisterID op1, RegisterID op2, RegisterID dest) - { - if (op1 == op2) - zeroExtend32ToPtr(op1, dest); - else if (op1 == dest) - and32(op2, dest); - else { - move(op2, dest); - and32(op1, dest); - } - } - - void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(src, dest); - and32(imm, dest); - } - - void lshift32(RegisterID shift_amount, RegisterID dest) - { - ASSERT(shift_amount != dest); - - if (shift_amount == X86Registers::ecx) - m_assembler.shll_CLr(dest); - else { - // On x86 we can only shift by ecx; if asked to shift by another register we'll - // need rejig the shift amount into ecx first, and restore the registers afterwards. - // If we dest is ecx, then shift the swapped register! - swap(shift_amount, X86Registers::ecx); - m_assembler.shll_CLr(dest == X86Registers::ecx ? shift_amount : dest); - swap(shift_amount, X86Registers::ecx); - } - } - - void lshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) - { - ASSERT(shift_amount != dest); - - if (src != dest) - move(src, dest); - lshift32(shift_amount, dest); - } - - void lshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.shll_i8r(imm.m_value, dest); - } - - void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - if (src != dest) - move(src, dest); - lshift32(imm, dest); - } - - void mul32(RegisterID src, RegisterID dest) - { - m_assembler.imull_rr(src, dest); - } - - void mul32(Address src, RegisterID dest) - { - m_assembler.imull_mr(src.offset, src.base, dest); - } - - void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - m_assembler.imull_i32r(src, imm.m_value, dest); - } - - void neg32(RegisterID srcDest) - { - m_assembler.negl_r(srcDest); - } - - void neg32(Address srcDest) - { - m_assembler.negl_m(srcDest.offset, srcDest.base); - } - - void or32(RegisterID src, RegisterID dest) - { - m_assembler.orl_rr(src, dest); - } - - void or32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.orl_ir(imm.m_value, dest); - } - - void or32(RegisterID src, Address dest) - { - m_assembler.orl_rm(src, dest.offset, dest.base); - } - - void or32(Address src, RegisterID dest) - { - m_assembler.orl_mr(src.offset, src.base, dest); - } - - void or32(TrustedImm32 imm, Address address) - { - m_assembler.orl_im(imm.m_value, address.offset, address.base); - } - - void or32(RegisterID op1, RegisterID op2, RegisterID dest) - { - if (op1 == op2) - zeroExtend32ToPtr(op1, dest); - else if (op1 == dest) - or32(op2, dest); - else { - move(op2, dest); - or32(op1, dest); - } - } - - void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(src, dest); - or32(imm, dest); - } - - void rshift32(RegisterID shift_amount, RegisterID dest) - { - ASSERT(shift_amount != dest); - - if (shift_amount == X86Registers::ecx) - m_assembler.sarl_CLr(dest); - else { - // On x86 we can only shift by ecx; if asked to shift by another register we'll - // need rejig the shift amount into ecx first, and restore the registers afterwards. - // If we dest is ecx, then shift the swapped register! - swap(shift_amount, X86Registers::ecx); - m_assembler.sarl_CLr(dest == X86Registers::ecx ? shift_amount : dest); - swap(shift_amount, X86Registers::ecx); - } - } - - void rshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) - { - ASSERT(shift_amount != dest); - - if (src != dest) - move(src, dest); - rshift32(shift_amount, dest); - } - - void rshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.sarl_i8r(imm.m_value, dest); - } - - void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - if (src != dest) - move(src, dest); - rshift32(imm, dest); - } - - void urshift32(RegisterID shift_amount, RegisterID dest) - { - ASSERT(shift_amount != dest); - - if (shift_amount == X86Registers::ecx) - m_assembler.shrl_CLr(dest); - else { - // On x86 we can only shift by ecx; if asked to shift by another register we'll - // need rejig the shift amount into ecx first, and restore the registers afterwards. - // If we dest is ecx, then shift the swapped register! - swap(shift_amount, X86Registers::ecx); - m_assembler.shrl_CLr(dest == X86Registers::ecx ? shift_amount : dest); - swap(shift_amount, X86Registers::ecx); - } - } - - void urshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) - { - ASSERT(shift_amount != dest); - - if (src != dest) - move(src, dest); - urshift32(shift_amount, dest); - } - - void urshift32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.shrl_i8r(imm.m_value, dest); - } - - void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) - { - if (src != dest) - move(src, dest); - urshift32(imm, dest); - } - - void sub32(RegisterID src, RegisterID dest) - { - m_assembler.subl_rr(src, dest); - } - - void sub32(TrustedImm32 imm, RegisterID dest) - { - m_assembler.subl_ir(imm.m_value, dest); - } - - void sub32(TrustedImm32 imm, Address address) - { - m_assembler.subl_im(imm.m_value, address.offset, address.base); - } - - void sub32(Address src, RegisterID dest) - { - m_assembler.subl_mr(src.offset, src.base, dest); - } - - void sub32(RegisterID src, Address dest) - { - m_assembler.subl_rm(src, dest.offset, dest.base); - } - - void xor32(RegisterID src, RegisterID dest) - { - m_assembler.xorl_rr(src, dest); - } - - void xor32(TrustedImm32 imm, Address dest) - { - if (imm.m_value == -1) - m_assembler.notl_m(dest.offset, dest.base); - else - m_assembler.xorl_im(imm.m_value, dest.offset, dest.base); - } - - void xor32(TrustedImm32 imm, RegisterID dest) - { - if (imm.m_value == -1) - m_assembler.notl_r(dest); - else - m_assembler.xorl_ir(imm.m_value, dest); - } - - void xor32(RegisterID src, Address dest) - { - m_assembler.xorl_rm(src, dest.offset, dest.base); - } - - void xor32(Address src, RegisterID dest) - { - m_assembler.xorl_mr(src.offset, src.base, dest); - } - - void xor32(RegisterID op1, RegisterID op2, RegisterID dest) - { - if (op1 == op2) - move(TrustedImm32(0), dest); - else if (op1 == dest) - xor32(op2, dest); - else { - move(op2, dest); - xor32(op1, dest); - } - } - - void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(src, dest); - xor32(imm, dest); - } - - void sqrtDouble(FPRegisterID src, FPRegisterID dst) - { - m_assembler.sqrtsd_rr(src, dst); - } - - void absDouble(FPRegisterID src, FPRegisterID dst) - { - ASSERT(src != dst); - static const double negativeZeroConstant = -0.0; - loadDouble(&negativeZeroConstant, dst); - m_assembler.andnpd_rr(src, dst); - } - - void negateDouble(FPRegisterID src, FPRegisterID dst) - { - ASSERT(src != dst); - static const double negativeZeroConstant = -0.0; - loadDouble(&negativeZeroConstant, dst); - m_assembler.xorpd_rr(src, dst); - } - - - // Memory access operations: - // - // Loads are of the form load(address, destination) and stores of the form - // store(source, address). The source for a store may be an TrustedImm32. Address - // operand objects to loads and store will be implicitly constructed if a - // register is passed. - - void load32(ImplicitAddress address, RegisterID dest) - { - m_assembler.movl_mr(address.offset, address.base, dest); - } - - void load32(BaseIndex address, RegisterID dest) - { - m_assembler.movl_mr(address.offset, address.base, address.index, address.scale, dest); - } - - void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) - { - load32(address, dest); - } - - void load16Unaligned(BaseIndex address, RegisterID dest) - { - load16(address, dest); - } - - DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) - { - padBeforePatch(); - m_assembler.movl_mr_disp32(address.offset, address.base, dest); - return DataLabel32(this); - } - - DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - padBeforePatch(); - m_assembler.movl_mr_disp8(address.offset, address.base, dest); - return DataLabelCompact(this); - } - - static void repatchCompact(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) - { - ASSERT(isCompactPtrAlignedAddressOffset(value)); - AssemblerType_T::repatchCompact(dataLabelCompact.dataLocation(), value); - } - - DataLabelCompact loadCompactWithAddressOffsetPatch(Address address, RegisterID dest) - { - padBeforePatch(); - m_assembler.movl_mr_disp8(address.offset, address.base, dest); - return DataLabelCompact(this); - } - - void load8(BaseIndex address, RegisterID dest) - { - m_assembler.movzbl_mr(address.offset, address.base, address.index, address.scale, dest); - } - - void load8(ImplicitAddress address, RegisterID dest) - { - m_assembler.movzbl_mr(address.offset, address.base, dest); - } - - void load8Signed(BaseIndex address, RegisterID dest) - { - m_assembler.movsbl_mr(address.offset, address.base, address.index, address.scale, dest); - } - - void load8Signed(ImplicitAddress address, RegisterID dest) - { - m_assembler.movsbl_mr(address.offset, address.base, dest); - } - - void load16(BaseIndex address, RegisterID dest) - { - m_assembler.movzwl_mr(address.offset, address.base, address.index, address.scale, dest); - } - - void load16(Address address, RegisterID dest) - { - m_assembler.movzwl_mr(address.offset, address.base, dest); - } - - void load16Signed(BaseIndex address, RegisterID dest) - { - m_assembler.movswl_mr(address.offset, address.base, address.index, address.scale, dest); - } - - void load16Signed(Address address, RegisterID dest) - { - m_assembler.movswl_mr(address.offset, address.base, dest); - } - - DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) - { - padBeforePatch(); - m_assembler.movl_rm_disp32(src, address.offset, address.base); - return DataLabel32(this); - } - - void store32(RegisterID src, ImplicitAddress address) - { - m_assembler.movl_rm(src, address.offset, address.base); - } - - void store32(RegisterID src, BaseIndex address) - { - m_assembler.movl_rm(src, address.offset, address.base, address.index, address.scale); - } - - void store32(TrustedImm32 imm, ImplicitAddress address) - { - m_assembler.movl_i32m(imm.m_value, address.offset, address.base); - } - - void store32(TrustedImm32 imm, BaseIndex address) - { - m_assembler.movl_i32m(imm.m_value, address.offset, address.base, address.index, address.scale); - } - - void store8(TrustedImm32 imm, Address address) - { - ASSERT(-128 <= imm.m_value && imm.m_value < 128); - m_assembler.movb_i8m(imm.m_value, address.offset, address.base); - } - - void store8(TrustedImm32 imm, BaseIndex address) - { - ASSERT(-128 <= imm.m_value && imm.m_value < 128); - m_assembler.movb_i8m(imm.m_value, address.offset, address.base, address.index, address.scale); - } - - void store8(RegisterID src, BaseIndex address) - { -#if CPU(X86) - // On 32-bit x86 we can only store from the first 4 registers; - // esp..edi are mapped to the 'h' registers! - if (src >= 4) { - // Pick a temporary register. - RegisterID temp; - if (address.base != X86Registers::eax && address.index != X86Registers::eax) - temp = X86Registers::eax; - else if (address.base != X86Registers::ebx && address.index != X86Registers::ebx) - temp = X86Registers::ebx; - else { - ASSERT(address.base != X86Registers::ecx && address.index != X86Registers::ecx); - temp = X86Registers::ecx; - } - - // Swap to the temporary register to perform the store. - swap(src, temp); - m_assembler.movb_rm(temp, address.offset, address.base, address.index, address.scale); - swap(src, temp); - return; - } -#endif - m_assembler.movb_rm(src, address.offset, address.base, address.index, address.scale); - } - - void store16(RegisterID src, BaseIndex address) - { -#if CPU(X86) - // On 32-bit x86 we can only store from the first 4 registers; - // esp..edi are mapped to the 'h' registers! - if (src >= 4) { - // Pick a temporary register. - RegisterID temp; - if (address.base != X86Registers::eax && address.index != X86Registers::eax) - temp = X86Registers::eax; - else if (address.base != X86Registers::ebx && address.index != X86Registers::ebx) - temp = X86Registers::ebx; - else { - ASSERT(address.base != X86Registers::ecx && address.index != X86Registers::ecx); - temp = X86Registers::ecx; - } - - // Swap to the temporary register to perform the store. - swap(src, temp); - m_assembler.movw_rm(temp, address.offset, address.base, address.index, address.scale); - swap(src, temp); - return; - } -#endif - m_assembler.movw_rm(src, address.offset, address.base, address.index, address.scale); - } - - - // Floating-point operation: - // - // Presently only supports SSE, not x87 floating point. - - void moveDouble(FPRegisterID src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - if (src != dest) - m_assembler.movsd_rr(src, dest); - } - - void loadDouble(const void* address, FPRegisterID dest) - { -#if CPU(X86) - ASSERT(isSSE2Present()); - m_assembler.movsd_mr(address, dest); -#else - move(TrustedImmPtr(address), scratchRegister); - loadDouble(scratchRegister, dest); -#endif - } - - void loadDouble(ImplicitAddress address, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.movsd_mr(address.offset, address.base, dest); - } - - void loadDouble(BaseIndex address, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.movsd_mr(address.offset, address.base, address.index, address.scale, dest); - } - void loadFloat(BaseIndex address, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.movss_mr(address.offset, address.base, address.index, address.scale, dest); - } - - void storeDouble(FPRegisterID src, ImplicitAddress address) - { - ASSERT(isSSE2Present()); - m_assembler.movsd_rm(src, address.offset, address.base); - } - - void storeDouble(FPRegisterID src, BaseIndex address) - { - ASSERT(isSSE2Present()); - m_assembler.movsd_rm(src, address.offset, address.base, address.index, address.scale); - } - - void storeFloat(FPRegisterID src, BaseIndex address) - { - ASSERT(isSSE2Present()); - m_assembler.movss_rm(src, address.offset, address.base, address.index, address.scale); - } - - void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) - { - ASSERT(isSSE2Present()); - m_assembler.cvtsd2ss_rr(src, dst); - } - - void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) - { - ASSERT(isSSE2Present()); - m_assembler.cvtss2sd_rr(src, dst); - } - - void addDouble(FPRegisterID src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.addsd_rr(src, dest); - } - - void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - if (op1 == dest) - addDouble(op2, dest); - else { - moveDouble(op2, dest); - addDouble(op1, dest); - } - } - - void addDouble(Address src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.addsd_mr(src.offset, src.base, dest); - } - - void divDouble(FPRegisterID src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.divsd_rr(src, dest); - } - - void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - // B := A / B is invalid. - ASSERT(op1 == dest || op2 != dest); - - moveDouble(op1, dest); - divDouble(op2, dest); - } - - void divDouble(Address src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.divsd_mr(src.offset, src.base, dest); - } - - void subDouble(FPRegisterID src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.subsd_rr(src, dest); - } - - void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - // B := A - B is invalid. - ASSERT(op1 == dest || op2 != dest); - - moveDouble(op1, dest); - subDouble(op2, dest); - } - - void subDouble(Address src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.subsd_mr(src.offset, src.base, dest); - } - - void mulDouble(FPRegisterID src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.mulsd_rr(src, dest); - } - - void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - if (op1 == dest) - mulDouble(op2, dest); - else { - moveDouble(op2, dest); - mulDouble(op1, dest); - } - } - - void mulDouble(Address src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.mulsd_mr(src.offset, src.base, dest); - } - - void convertInt32ToDouble(RegisterID src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.cvtsi2sd_rr(src, dest); - } - - void convertInt32ToDouble(Address src, FPRegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.cvtsi2sd_mr(src.offset, src.base, dest); - } - - Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) - { - ASSERT(isSSE2Present()); - - if (cond & DoubleConditionBitInvert) - m_assembler.ucomisd_rr(left, right); - else - m_assembler.ucomisd_rr(right, left); - - if (cond == DoubleEqual) { - if (left == right) - return Jump(m_assembler.jnp()); - Jump isUnordered(m_assembler.jp()); - Jump result = Jump(m_assembler.je()); - isUnordered.link(this); - return result; - } else if (cond == DoubleNotEqualOrUnordered) { - if (left == right) - return Jump(m_assembler.jp()); - Jump isUnordered(m_assembler.jp()); - Jump isEqual(m_assembler.je()); - isUnordered.link(this); - Jump result = jump(); - isEqual.link(this); - return result; - } - - ASSERT(!(cond & DoubleConditionBitSpecial)); - return Jump(m_assembler.jCC(static_cast(cond & ~DoubleConditionBits))); - } - - // Truncates 'src' to an integer, and places the resulting 'dest'. - // If the result is not representable as a 32 bit value, branch. - // May also branch for some values that are representable in 32 bits - // (specifically, in this case, INT_MIN). - enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; - Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) - { - ASSERT(isSSE2Present()); - m_assembler.cvttsd2si_rr(src, dest); - return branch32(branchType ? NotEqual : Equal, dest, TrustedImm32(0x80000000)); - } - - Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) - { - ASSERT(isSSE2Present()); - m_assembler.cvttsd2si_rr(src, dest); - return branch32(branchType ? GreaterThanOrEqual : LessThan, dest, TrustedImm32(0)); - } - - void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.cvttsd2si_rr(src, dest); - } - -#if CPU(X86_64) - void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) - { - ASSERT(isSSE2Present()); - m_assembler.cvttsd2siq_rr(src, dest); - } -#endif - - // Convert 'src' to an integer, and places the resulting 'dest'. - // If the result is not representable as a 32 bit value, branch. - // May also branch for some values that are representable in 32 bits - // (specifically, in this case, 0). - void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) - { - ASSERT(isSSE2Present()); - m_assembler.cvttsd2si_rr(src, dest); - - // If the result is zero, it might have been -0.0, and the double comparison won't catch this! - failureCases.append(branchTest32(Zero, dest)); - - // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. - convertInt32ToDouble(dest, fpTemp); - m_assembler.ucomisd_rr(fpTemp, src); - failureCases.append(m_assembler.jp()); - failureCases.append(m_assembler.jne()); - } - - Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) - { - ASSERT(isSSE2Present()); - m_assembler.xorpd_rr(scratch, scratch); - return branchDouble(DoubleNotEqual, reg, scratch); - } - - Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) - { - ASSERT(isSSE2Present()); - m_assembler.xorpd_rr(scratch, scratch); - return branchDouble(DoubleEqualOrUnordered, reg, scratch); - } - - void lshiftPacked(TrustedImm32 imm, XMMRegisterID reg) - { - ASSERT(isSSE2Present()); - m_assembler.psllq_i8r(imm.m_value, reg); - } - - void rshiftPacked(TrustedImm32 imm, XMMRegisterID reg) - { - ASSERT(isSSE2Present()); - m_assembler.psrlq_i8r(imm.m_value, reg); - } - - void orPacked(XMMRegisterID src, XMMRegisterID dst) - { - ASSERT(isSSE2Present()); - m_assembler.por_rr(src, dst); - } - - void moveInt32ToPacked(RegisterID src, XMMRegisterID dst) - { - ASSERT(isSSE2Present()); - m_assembler.movd_rr(src, dst); - } - - void movePackedToInt32(XMMRegisterID src, RegisterID dst) - { - ASSERT(isSSE2Present()); - m_assembler.movd_rr(src, dst); - } - - // Stack manipulation operations: - // - // The ABI is assumed to provide a stack abstraction to memory, - // containing machine word sized units of data. Push and pop - // operations add and remove a single register sized unit of data - // to or from the stack. Peek and poke operations read or write - // values on the stack, without moving the current stack position. - - void pop(RegisterID dest) - { - m_assembler.pop_r(dest); - } - - void push(RegisterID src) - { - m_assembler.push_r(src); - } - - void push(Address address) - { - m_assembler.push_m(address.offset, address.base); - } - - void push(TrustedImm32 imm) - { - m_assembler.push_i32(imm.m_value); - } - - - // Register move operations: - // - // Move values in registers. - - void move(TrustedImm32 imm, RegisterID dest) - { - // Note: on 64-bit the TrustedImm32 value is zero extended into the register, it - // may be useful to have a separate version that sign extends the value? - if (!imm.m_value) - m_assembler.xorl_rr(dest, dest); - else - m_assembler.movl_i32r(imm.m_value, dest); - } - -#if CPU(X86_64) - void move(RegisterID src, RegisterID dest) - { - // Note: on 64-bit this is is a full register move; perhaps it would be - // useful to have separate move32 & movePtr, with move32 zero extending? - if (src != dest) - m_assembler.movq_rr(src, dest); - } - - void move(TrustedImmPtr imm, RegisterID dest) - { - m_assembler.movq_i64r(imm.asIntptr(), dest); - } - - void move(TrustedImm64 imm, RegisterID dest) - { - m_assembler.movq_i64r(imm.m_value, dest); - } - - void swap(RegisterID reg1, RegisterID reg2) - { - if (reg1 != reg2) - m_assembler.xchgq_rr(reg1, reg2); - } - - void signExtend32ToPtr(RegisterID src, RegisterID dest) - { - m_assembler.movsxd_rr(src, dest); - } - - void zeroExtend32ToPtr(RegisterID src, RegisterID dest) - { - m_assembler.movl_rr(src, dest); - } -#else - void move(RegisterID src, RegisterID dest) - { - if (src != dest) - m_assembler.movl_rr(src, dest); - } - - void move(TrustedImmPtr imm, RegisterID dest) - { - m_assembler.movl_i32r(imm.asIntptr(), dest); - } - - void swap(RegisterID reg1, RegisterID reg2) - { - if (reg1 != reg2) - m_assembler.xchgl_rr(reg1, reg2); - } - - void signExtend32ToPtr(RegisterID src, RegisterID dest) - { - move(src, dest); - } - - void zeroExtend32ToPtr(RegisterID src, RegisterID dest) - { - move(src, dest); - } -#endif - - - // Forwards / external control flow operations: - // - // This set of jump and conditional branch operations return a Jump - // object which may linked at a later point, allow forwards jump, - // or jumps that will require external linkage (after the code has been - // relocated). - // - // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge - // respecitvely, for unsigned comparisons the names b, a, be, and ae are - // used (representing the names 'below' and 'above'). - // - // Operands to the comparision are provided in the expected order, e.g. - // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when - // treated as a signed 32bit value, is less than or equal to 5. - // - // jz and jnz test whether the first operand is equal to zero, and take - // an optional second operand of a mask under which to perform the test. - -public: - Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) - { - m_assembler.cmpb_im(right.m_value, left.offset, left.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) - { - m_assembler.cmpl_rr(right, left); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) - { - if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) - m_assembler.testl_rr(left, left); - else - m_assembler.cmpl_ir(right.m_value, left); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, RegisterID left, Address right) - { - m_assembler.cmpl_mr(right.offset, right.base, left); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, Address left, RegisterID right) - { - m_assembler.cmpl_rm(right, left.offset, left.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) - { - m_assembler.cmpl_im(right.m_value, left.offset, left.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - m_assembler.cmpl_im(right.m_value, left.offset, left.base, left.index, left.scale); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - return branch32(cond, left, right); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) - { - m_assembler.testl_rr(reg, mask); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - // if we are only interested in the low seven bits, this can be tested with a testb - if (mask.m_value == -1) - m_assembler.testl_rr(reg, reg); - else - m_assembler.testl_i32r(mask.m_value, reg); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - if (mask.m_value == -1) - m_assembler.cmpl_im(0, address.offset, address.base); - else - m_assembler.testl_i32m(mask.m_value, address.offset, address.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - if (mask.m_value == -1) - m_assembler.cmpl_im(0, address.offset, address.base, address.index, address.scale); - else - m_assembler.testl_i32m(mask.m_value, address.offset, address.base, address.index, address.scale); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - // Byte in TrustedImm32 is not well defined, so be a little permisive here, but don't accept nonsense values. - ASSERT(mask.m_value >= -128 && mask.m_value <= 255); - if (mask.m_value == -1) - m_assembler.cmpb_im(0, address.offset, address.base); - else - m_assembler.testb_im(mask.m_value, address.offset, address.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest8(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - // Byte in TrustedImm32 is not well defined, so be a little permisive here, but don't accept nonsense values. - ASSERT(mask.m_value >= -128 && mask.m_value <= 255); - if (mask.m_value == -1) - m_assembler.cmpb_im(0, address.offset, address.base, address.index, address.scale); - else - m_assembler.testb_im(mask.m_value, address.offset, address.base, address.index, address.scale); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) - { - ASSERT(!(right.m_value & 0xFFFFFF00)); - - m_assembler.cmpb_im(right.m_value, left.offset, left.base, left.index, left.scale); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump jump() - { - return Jump(m_assembler.jmp()); - } - - void jump(RegisterID target) - { - m_assembler.jmp_r(target); - } - - // Address is a memory location containing the address to jump to - void jump(Address address) - { - m_assembler.jmp_m(address.offset, address.base); - } - - - // Arithmetic control flow operations: - // - // This set of conditional branch operations branch based - // on the result of an arithmetic operation. The operation - // is performed as normal, storing the result. - // - // * jz operations branch if the result is zero. - // * jo operations branch if the (signed) arithmetic - // operation caused an overflow to occur. - - Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) - { - add32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - add32(imm, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 src, Address dest) - { - add32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, Address dest) - { - add32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchAdd32(ResultCondition cond, Address src, RegisterID dest) - { - add32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) - { - if (src1 == dest) - return branchAdd32(cond, src2, dest); - move(src2, dest); - return branchAdd32(cond, src1, dest); - } - - Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) - { - move(src, dest); - return branchAdd32(cond, imm, dest); - } - - Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) - { - mul32(src, dest); - if (cond != Overflow) - m_assembler.testl_rr(dest, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchMul32(ResultCondition cond, Address src, RegisterID dest) - { - mul32(src, dest); - if (cond != Overflow) - m_assembler.testl_rr(dest, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) - { - mul32(imm, src, dest); - if (cond != Overflow) - m_assembler.testl_rr(dest, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) - { - if (src1 == dest) - return branchMul32(cond, src2, dest); - move(src2, dest); - return branchMul32(cond, src1, dest); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) - { - sub32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - sub32(imm, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub32(ResultCondition cond, TrustedImm32 imm, Address dest) - { - sub32(imm, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub32(ResultCondition cond, RegisterID src, Address dest) - { - sub32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub32(ResultCondition cond, Address src, RegisterID dest) - { - sub32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) - { - // B := A - B is invalid. - ASSERT(src1 == dest || src2 != dest); - - move(src1, dest); - return branchSub32(cond, src2, dest); - } - - Jump branchSub32(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) - { - move(src1, dest); - return branchSub32(cond, src2, dest); - } - - Jump branchNeg32(ResultCondition cond, RegisterID srcDest) - { - neg32(srcDest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) - { - or32(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - - // Miscellaneous operations: - - void breakpoint() - { - m_assembler.int3(); - } - - Call nearCall() - { - return Call(m_assembler.call(), Call::LinkableNear); - } - - Call call(RegisterID target) - { - return Call(m_assembler.call(target), Call::None); - } - - void call(Address address) - { - m_assembler.call_m(address.offset, address.base); - } - - void ret() - { - m_assembler.ret(); - } - - void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) - { - m_assembler.cmpb_im(right.m_value, left.offset, left.base); - set32(x86Condition(cond), dest); - } - - void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) - { - m_assembler.cmpl_rr(right, left); - set32(x86Condition(cond), dest); - } - - void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) - m_assembler.testl_rr(left, left); - else - m_assembler.cmpl_ir(right.m_value, left); - set32(x86Condition(cond), dest); - } - - // FIXME: - // The mask should be optional... perhaps the argument order should be - // dest-src, operations always have a dest? ... possibly not true, considering - // asm ops like test, or pseudo ops like pop(). - - void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - if (mask.m_value == -1) - m_assembler.cmpb_im(0, address.offset, address.base); - else - m_assembler.testb_im(mask.m_value, address.offset, address.base); - set32(x86Condition(cond), dest); - } - - void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) - { - if (mask.m_value == -1) - m_assembler.cmpl_im(0, address.offset, address.base); - else - m_assembler.testl_i32m(mask.m_value, address.offset, address.base); - set32(x86Condition(cond), dest); - } - - // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. - static RelationalCondition invert(RelationalCondition cond) - { - return static_cast(cond ^ 1); - } - - void nop() - { - m_assembler.nop(); - } - - static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) - { - X86Assembler::replaceWithJump(instructionStart.executableAddress(), destination.executableAddress()); - } - - static ptrdiff_t maxJumpReplacementSize() - { - return X86Assembler::maxJumpReplacementSize(); - } - -protected: - X86Assembler::Condition x86Condition(RelationalCondition cond) - { - return static_cast(cond); - } - - X86Assembler::Condition x86Condition(ResultCondition cond) - { - return static_cast(cond); - } - - void set32(X86Assembler::Condition cond, RegisterID dest) - { -#if CPU(X86) - // On 32-bit x86 we can only set the first 4 registers; - // esp..edi are mapped to the 'h' registers! - if (dest >= 4) { - m_assembler.xchgl_rr(dest, X86Registers::eax); - m_assembler.setCC_r(cond, X86Registers::eax); - m_assembler.movzbl_rr(X86Registers::eax, X86Registers::eax); - m_assembler.xchgl_rr(dest, X86Registers::eax); - return; - } -#endif - m_assembler.setCC_r(cond, dest); - m_assembler.movzbl_rr(dest, dest); - } - -private: - // Only MacroAssemblerX86 should be using the following method; SSE2 is always available on - // x86_64, and clients & subclasses of MacroAssembler should be using 'supportsFloatingPoint()'. - friend class MacroAssemblerX86; - -#if CPU(X86) -#if OS(MAC_OS_X) - - // All X86 Macs are guaranteed to support at least SSE2, - static bool isSSE2Present() - { - return true; - } - -#else // OS(MAC_OS_X) - - enum SSE2CheckState { - NotCheckedSSE2, - HasSSE2, - NoSSE2 - }; - - static bool isSSE2Present() - { - if (s_sse2CheckState == NotCheckedSSE2) { - // Default the flags value to zero; if the compiler is - // not MSVC or GCC we will read this as SSE2 not present. - int flags = 0; -#if COMPILER(MSVC) - _asm { - mov eax, 1 // cpuid function 1 gives us the standard feature set - cpuid; - mov flags, edx; - } -#elif COMPILER(GCC) - asm ( - "movl $0x1, %%eax;" - "pushl %%ebx;" - "cpuid;" - "popl %%ebx;" - "movl %%edx, %0;" - : "=g" (flags) - : - : "%eax", "%ecx", "%edx" - ); -#endif - static const int SSE2FeatureBit = 1 << 26; - s_sse2CheckState = (flags & SSE2FeatureBit) ? HasSSE2 : NoSSE2; - } - // Only check once. - ASSERT(s_sse2CheckState != NotCheckedSSE2); - - return s_sse2CheckState == HasSSE2; - } - - static SSE2CheckState s_sse2CheckState; - -#endif // OS(MAC_OS_X) -#elif !defined(NDEBUG) // CPU(X86) - - // On x86-64 we should never be checking for SSE2 in a non-debug build, - // but non debug add this method to keep the asserts above happy. - static bool isSSE2Present() - { - return true; - } - -#endif -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // MacroAssemblerX86Common_h diff --git a/3rdparty/masm/assembler/MacroAssemblerX86_64.h b/3rdparty/masm/assembler/MacroAssemblerX86_64.h deleted file mode 100644 index c711e6f8da..0000000000 --- a/3rdparty/masm/assembler/MacroAssemblerX86_64.h +++ /dev/null @@ -1,643 +0,0 @@ -/* - * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MacroAssemblerX86_64_h -#define MacroAssemblerX86_64_h - -#if ENABLE(ASSEMBLER) && CPU(X86_64) - -#include "MacroAssemblerX86Common.h" - -#define REPTACH_OFFSET_CALL_R11 3 - -namespace JSC { - -class MacroAssemblerX86_64 : public MacroAssemblerX86Common { -public: - static const Scale ScalePtr = TimesEight; - - using MacroAssemblerX86Common::add32; - using MacroAssemblerX86Common::and32; - using MacroAssemblerX86Common::branchAdd32; - using MacroAssemblerX86Common::or32; - using MacroAssemblerX86Common::sub32; - using MacroAssemblerX86Common::load32; - using MacroAssemblerX86Common::store32; - using MacroAssemblerX86Common::store8; - using MacroAssemblerX86Common::call; - using MacroAssemblerX86Common::jump; - using MacroAssemblerX86Common::addDouble; - using MacroAssemblerX86Common::loadDouble; - using MacroAssemblerX86Common::convertInt32ToDouble; - - void add32(TrustedImm32 imm, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - add32(imm, Address(scratchRegister)); - } - - void and32(TrustedImm32 imm, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - and32(imm, Address(scratchRegister)); - } - - void add32(AbsoluteAddress address, RegisterID dest) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - add32(Address(scratchRegister), dest); - } - - void or32(TrustedImm32 imm, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - or32(imm, Address(scratchRegister)); - } - - void or32(RegisterID reg, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - or32(reg, Address(scratchRegister)); - } - - void sub32(TrustedImm32 imm, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - sub32(imm, Address(scratchRegister)); - } - - void load32(const void* address, RegisterID dest) - { - if (dest == X86Registers::eax) - m_assembler.movl_mEAX(address); - else { - move(TrustedImmPtr(address), dest); - load32(dest, dest); - } - } - - void addDouble(AbsoluteAddress address, FPRegisterID dest) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - m_assembler.addsd_mr(0, scratchRegister, dest); - } - - void convertInt32ToDouble(TrustedImm32 imm, FPRegisterID dest) - { - move(imm, scratchRegister); - m_assembler.cvtsi2sd_rr(scratchRegister, dest); - } - - void store32(TrustedImm32 imm, void* address) - { - move(TrustedImmPtr(address), scratchRegister); - store32(imm, scratchRegister); - } - - void store8(TrustedImm32 imm, void* address) - { - move(TrustedImmPtr(address), scratchRegister); - store8(imm, Address(scratchRegister)); - } - - Call call() - { - DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); - Call result = Call(m_assembler.call(scratchRegister), Call::Linkable); - ASSERT_UNUSED(label, differenceBetween(label, result) == REPTACH_OFFSET_CALL_R11); - return result; - } - - // Address is a memory location containing the address to jump to - void jump(AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - jump(Address(scratchRegister)); - } - - Call tailRecursiveCall() - { - DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); - Jump newJump = Jump(m_assembler.jmp_r(scratchRegister)); - ASSERT_UNUSED(label, differenceBetween(label, newJump) == REPTACH_OFFSET_CALL_R11); - return Call::fromTailJump(newJump); - } - - Call makeTailRecursiveCall(Jump oldJump) - { - oldJump.link(this); - DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); - Jump newJump = Jump(m_assembler.jmp_r(scratchRegister)); - ASSERT_UNUSED(label, differenceBetween(label, newJump) == REPTACH_OFFSET_CALL_R11); - return Call::fromTailJump(newJump); - } - - Jump branchAdd32(ResultCondition cond, TrustedImm32 src, AbsoluteAddress dest) - { - move(TrustedImmPtr(dest.m_ptr), scratchRegister); - add32(src, Address(scratchRegister)); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - void add64(RegisterID src, RegisterID dest) - { - m_assembler.addq_rr(src, dest); - } - - void add64(Address src, RegisterID dest) - { - m_assembler.addq_mr(src.offset, src.base, dest); - } - - void add64(AbsoluteAddress src, RegisterID dest) - { - move(TrustedImmPtr(src.m_ptr), scratchRegister); - add64(Address(scratchRegister), dest); - } - - void add64(TrustedImm32 imm, RegisterID srcDest) - { - m_assembler.addq_ir(imm.m_value, srcDest); - } - - void add64(TrustedImm64 imm, RegisterID dest) - { - move(imm, scratchRegister); - add64(scratchRegister, dest); - } - - void add64(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - m_assembler.leaq_mr(imm.m_value, src, dest); - } - - void add64(TrustedImm32 imm, Address address) - { - m_assembler.addq_im(imm.m_value, address.offset, address.base); - } - - void add64(TrustedImm32 imm, AbsoluteAddress address) - { - move(TrustedImmPtr(address.m_ptr), scratchRegister); - add64(imm, Address(scratchRegister)); - } - - void and64(RegisterID src, RegisterID dest) - { - m_assembler.andq_rr(src, dest); - } - - void and64(TrustedImm32 imm, RegisterID srcDest) - { - m_assembler.andq_ir(imm.m_value, srcDest); - } - - void neg64(RegisterID dest) - { - m_assembler.negq_r(dest); - } - - void or64(RegisterID src, RegisterID dest) - { - m_assembler.orq_rr(src, dest); - } - - void or64(TrustedImm64 imm, RegisterID dest) - { - move(imm, scratchRegister); - or64(scratchRegister, dest); - } - - void or64(TrustedImm32 imm, RegisterID dest) - { - m_assembler.orq_ir(imm.m_value, dest); - } - - void or64(RegisterID op1, RegisterID op2, RegisterID dest) - { - if (op1 == op2) - move(op1, dest); - else if (op1 == dest) - or64(op2, dest); - else { - move(op2, dest); - or64(op1, dest); - } - } - - void or64(TrustedImm32 imm, RegisterID src, RegisterID dest) - { - move(src, dest); - or64(imm, dest); - } - - void rotateRight64(TrustedImm32 imm, RegisterID srcDst) - { - m_assembler.rorq_i8r(imm.m_value, srcDst); - } - - void sub64(RegisterID src, RegisterID dest) - { - m_assembler.subq_rr(src, dest); - } - - void sub64(TrustedImm32 imm, RegisterID dest) - { - m_assembler.subq_ir(imm.m_value, dest); - } - - void sub64(TrustedImm64 imm, RegisterID dest) - { - move(imm, scratchRegister); - sub64(scratchRegister, dest); - } - - void xor64(RegisterID src, RegisterID dest) - { - m_assembler.xorq_rr(src, dest); - } - - void xor64(RegisterID src, Address dest) - { - m_assembler.xorq_rm(src, dest.offset, dest.base); - } - - void xor64(TrustedImm32 imm, RegisterID srcDest) - { - m_assembler.xorq_ir(imm.m_value, srcDest); - } - - void load64(ImplicitAddress address, RegisterID dest) - { - m_assembler.movq_mr(address.offset, address.base, dest); - } - - void load64(BaseIndex address, RegisterID dest) - { - m_assembler.movq_mr(address.offset, address.base, address.index, address.scale, dest); - } - - void load64(const void* address, RegisterID dest) - { - if (dest == X86Registers::eax) - m_assembler.movq_mEAX(address); - else { - move(TrustedImmPtr(address), dest); - load64(dest, dest); - } - } - - DataLabel32 load64WithAddressOffsetPatch(Address address, RegisterID dest) - { - padBeforePatch(); - m_assembler.movq_mr_disp32(address.offset, address.base, dest); - return DataLabel32(this); - } - - DataLabelCompact load64WithCompactAddressOffsetPatch(Address address, RegisterID dest) - { - padBeforePatch(); - m_assembler.movq_mr_disp8(address.offset, address.base, dest); - return DataLabelCompact(this); - } - - void store64(RegisterID src, ImplicitAddress address) - { - m_assembler.movq_rm(src, address.offset, address.base); - } - - void store64(RegisterID src, BaseIndex address) - { - m_assembler.movq_rm(src, address.offset, address.base, address.index, address.scale); - } - - void store64(RegisterID src, void* address) - { - if (src == X86Registers::eax) - m_assembler.movq_EAXm(address); - else { - move(TrustedImmPtr(address), scratchRegister); - store64(src, scratchRegister); - } - } - - void store64(TrustedImm64 imm, ImplicitAddress address) - { - move(imm, scratchRegister); - store64(scratchRegister, address); - } - - void store64(TrustedImm64 imm, BaseIndex address) - { - move(imm, scratchRegister); - m_assembler.movq_rm(scratchRegister, address.offset, address.base, address.index, address.scale); - } - - DataLabel32 store64WithAddressOffsetPatch(RegisterID src, Address address) - { - padBeforePatch(); - m_assembler.movq_rm_disp32(src, address.offset, address.base); - return DataLabel32(this); - } - - void move64ToDouble(RegisterID src, FPRegisterID dest) - { - m_assembler.movq_rr(src, dest); - } - - void moveDoubleTo64(FPRegisterID src, RegisterID dest) - { - m_assembler.movq_rr(src, dest); - } - - void compare64(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) - { - if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) - m_assembler.testq_rr(left, left); - else - m_assembler.cmpq_ir(right.m_value, left); - m_assembler.setCC_r(x86Condition(cond), dest); - m_assembler.movzbl_rr(dest, dest); - } - - void compare64(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) - { - m_assembler.cmpq_rr(right, left); - m_assembler.setCC_r(x86Condition(cond), dest); - m_assembler.movzbl_rr(dest, dest); - } - - Jump branch64(RelationalCondition cond, RegisterID left, RegisterID right) - { - m_assembler.cmpq_rr(right, left); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch64(RelationalCondition cond, RegisterID left, TrustedImm64 right) - { - if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) { - m_assembler.testq_rr(left, left); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - move(right, scratchRegister); - return branch64(cond, left, scratchRegister); - } - - Jump branch64(RelationalCondition cond, RegisterID left, Address right) - { - m_assembler.cmpq_mr(right.offset, right.base, left); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch64(RelationalCondition cond, AbsoluteAddress left, RegisterID right) - { - move(TrustedImmPtr(left.m_ptr), scratchRegister); - return branch64(cond, Address(scratchRegister), right); - } - - Jump branch64(RelationalCondition cond, Address left, RegisterID right) - { - m_assembler.cmpq_rm(right, left.offset, left.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branch64(RelationalCondition cond, Address left, TrustedImm64 right) - { - move(right, scratchRegister); - return branch64(cond, left, scratchRegister); - } - - Jump branchTest64(ResultCondition cond, RegisterID reg, RegisterID mask) - { - m_assembler.testq_rr(reg, mask); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest64(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) - { - // if we are only interested in the low seven bits, this can be tested with a testb - if (mask.m_value == -1) - m_assembler.testq_rr(reg, reg); - else if ((mask.m_value & ~0x7f) == 0) - m_assembler.testb_i8r(mask.m_value, reg); - else - m_assembler.testq_i32r(mask.m_value, reg); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - void test64(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) - { - if (mask.m_value == -1) - m_assembler.testq_rr(reg, reg); - else if ((mask.m_value & ~0x7f) == 0) - m_assembler.testb_i8r(mask.m_value, reg); - else - m_assembler.testq_i32r(mask.m_value, reg); - set32(x86Condition(cond), dest); - } - - void test64(ResultCondition cond, RegisterID reg, RegisterID mask, RegisterID dest) - { - m_assembler.testq_rr(reg, mask); - set32(x86Condition(cond), dest); - } - - Jump branchTest64(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - load64(address.m_ptr, scratchRegister); - return branchTest64(cond, scratchRegister, mask); - } - - Jump branchTest64(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) - { - if (mask.m_value == -1) - m_assembler.cmpq_im(0, address.offset, address.base); - else - m_assembler.testq_i32m(mask.m_value, address.offset, address.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest64(ResultCondition cond, Address address, RegisterID reg) - { - m_assembler.testq_rm(reg, address.offset, address.base); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchTest64(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) - { - if (mask.m_value == -1) - m_assembler.cmpq_im(0, address.offset, address.base, address.index, address.scale); - else - m_assembler.testq_i32m(mask.m_value, address.offset, address.base, address.index, address.scale); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - - Jump branchAdd64(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - add64(imm, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchAdd64(ResultCondition cond, RegisterID src, RegisterID dest) - { - add64(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub64(ResultCondition cond, TrustedImm32 imm, RegisterID dest) - { - sub64(imm, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub64(ResultCondition cond, RegisterID src, RegisterID dest) - { - sub64(src, dest); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - - Jump branchSub64(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) - { - move(src1, dest); - return branchSub64(cond, src2, dest); - } - - ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) - { - ConvertibleLoadLabel result = ConvertibleLoadLabel(this); - m_assembler.movq_mr(address.offset, address.base, dest); - return result; - } - - DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) - { - padBeforePatch(); - m_assembler.movq_i64r(initialValue.asIntptr(), dest); - return DataLabelPtr(this); - } - - Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - dataLabel = moveWithPatch(initialRightValue, scratchRegister); - return branch64(cond, left, scratchRegister); - } - - Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) - { - dataLabel = moveWithPatch(initialRightValue, scratchRegister); - return branch64(cond, left, scratchRegister); - } - - DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) - { - DataLabelPtr label = moveWithPatch(initialValue, scratchRegister); - store64(scratchRegister, address); - return label; - } - - using MacroAssemblerX86Common::branchTest8; - Jump branchTest8(ResultCondition cond, ExtendedAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - TrustedImmPtr addr(reinterpret_cast(address.offset)); - MacroAssemblerX86Common::move(addr, scratchRegister); - return MacroAssemblerX86Common::branchTest8(cond, BaseIndex(scratchRegister, address.base, TimesOne), mask); - } - - Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) - { - MacroAssemblerX86Common::move(TrustedImmPtr(address.m_ptr), scratchRegister); - return MacroAssemblerX86Common::branchTest8(cond, Address(scratchRegister), mask); - } - - static bool supportsFloatingPoint() { return true; } - // See comment on MacroAssemblerARMv7::supportsFloatingPointTruncate() - static bool supportsFloatingPointTruncate() { return true; } - static bool supportsFloatingPointSqrt() { return true; } - static bool supportsFloatingPointAbs() { return true; } - - static FunctionPtr readCallTarget(CodeLocationCall call) - { - return FunctionPtr(X86Assembler::readPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation())); - } - - static RegisterID scratchRegisterForBlinding() { return scratchRegister; } - - static bool canJumpReplacePatchableBranchPtrWithPatch() { return true; } - - static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) - { - const int rexBytes = 1; - const int opcodeBytes = 1; - const int immediateBytes = 8; - const int totalBytes = rexBytes + opcodeBytes + immediateBytes; - ASSERT(totalBytes >= maxJumpReplacementSize()); - return label.labelAtOffset(-totalBytes); - } - - static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) - { - return startOfBranchPtrWithPatchOnRegister(label); - } - - static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) - { - X86Assembler::revertJumpTo_movq_i64r(instructionStart.executableAddress(), reinterpret_cast(initialValue), scratchRegister); - } - - static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) - { - X86Assembler::revertJumpTo_movq_i64r(instructionStart.executableAddress(), reinterpret_cast(initialValue), scratchRegister); - } - -private: - friend class LinkBuffer; - friend class RepatchBuffer; - - static void linkCall(void* code, Call call, FunctionPtr function) - { - if (!call.isFlagSet(Call::Near)) - X86Assembler::linkPointer(code, call.m_label.labelAtOffset(-REPTACH_OFFSET_CALL_R11), function.value()); - else - X86Assembler::linkCall(code, call.m_label, function.value()); - } - - static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) - { - X86Assembler::repatchPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation(), destination.executableAddress()); - } - - static void repatchCall(CodeLocationCall call, FunctionPtr destination) - { - X86Assembler::repatchPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation(), destination.executableAddress()); - } - -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // MacroAssemblerX86_64_h diff --git a/3rdparty/masm/assembler/RepatchBuffer.h b/3rdparty/masm/assembler/RepatchBuffer.h deleted file mode 100644 index dbb56f9ad5..0000000000 --- a/3rdparty/masm/assembler/RepatchBuffer.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef RepatchBuffer_h -#define RepatchBuffer_h - -#if ENABLE(JIT) - -#include "CodeBlock.h" -#include -#include - -namespace JSC { - -// RepatchBuffer: -// -// This class is used to modify code after code generation has been completed, -// and after the code has potentially already been executed. This mechanism is -// used to apply optimizations to the code. -// -class RepatchBuffer { - typedef MacroAssemblerCodePtr CodePtr; - -public: - RepatchBuffer(CodeBlock* codeBlock) - { - JITCode& code = codeBlock->getJITCode(); - m_start = code.start(); - m_size = code.size(); - - ExecutableAllocator::makeWritable(m_start, m_size); - } - - ~RepatchBuffer() - { - ExecutableAllocator::makeExecutable(m_start, m_size); - } - - void relink(CodeLocationJump jump, CodeLocationLabel destination) - { - MacroAssembler::repatchJump(jump, destination); - } - - void relink(CodeLocationCall call, CodeLocationLabel destination) - { - MacroAssembler::repatchCall(call, destination); - } - - void relink(CodeLocationCall call, FunctionPtr destination) - { - MacroAssembler::repatchCall(call, destination); - } - - void relink(CodeLocationNearCall nearCall, CodePtr destination) - { - MacroAssembler::repatchNearCall(nearCall, CodeLocationLabel(destination)); - } - - void relink(CodeLocationNearCall nearCall, CodeLocationLabel destination) - { - MacroAssembler::repatchNearCall(nearCall, destination); - } - - void repatch(CodeLocationDataLabel32 dataLabel32, int32_t value) - { - MacroAssembler::repatchInt32(dataLabel32, value); - } - - void repatch(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) - { - MacroAssembler::repatchCompact(dataLabelCompact, value); - } - - void repatch(CodeLocationDataLabelPtr dataLabelPtr, void* value) - { - MacroAssembler::repatchPointer(dataLabelPtr, value); - } - - void relinkCallerToTrampoline(ReturnAddressPtr returnAddress, CodeLocationLabel label) - { - relink(CodeLocationCall(CodePtr(returnAddress)), label); - } - - void relinkCallerToTrampoline(ReturnAddressPtr returnAddress, CodePtr newCalleeFunction) - { - relinkCallerToTrampoline(returnAddress, CodeLocationLabel(newCalleeFunction)); - } - - void relinkCallerToFunction(ReturnAddressPtr returnAddress, FunctionPtr function) - { - relink(CodeLocationCall(CodePtr(returnAddress)), function); - } - - void relinkNearCallerToTrampoline(ReturnAddressPtr returnAddress, CodeLocationLabel label) - { - relink(CodeLocationNearCall(CodePtr(returnAddress)), label); - } - - void relinkNearCallerToTrampoline(ReturnAddressPtr returnAddress, CodePtr newCalleeFunction) - { - relinkNearCallerToTrampoline(returnAddress, CodeLocationLabel(newCalleeFunction)); - } - - void replaceWithLoad(CodeLocationConvertibleLoad label) - { - MacroAssembler::replaceWithLoad(label); - } - - void replaceWithAddressComputation(CodeLocationConvertibleLoad label) - { - MacroAssembler::replaceWithAddressComputation(label); - } - - void setLoadInstructionIsActive(CodeLocationConvertibleLoad label, bool isActive) - { - if (isActive) - replaceWithLoad(label); - else - replaceWithAddressComputation(label); - } - - static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) - { - return MacroAssembler::startOfBranchPtrWithPatchOnRegister(label); - } - - static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) - { - return MacroAssembler::startOfPatchableBranchPtrWithPatchOnAddress(label); - } - - void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) - { - MacroAssembler::replaceWithJump(instructionStart, destination); - } - - // This is a *bit* of a silly API, since we currently always also repatch the - // immediate after calling this. But I'm fine with that, since this just feels - // less yucky. - void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, MacroAssembler::RegisterID reg, void* value) - { - MacroAssembler::revertJumpReplacementToBranchPtrWithPatch(instructionStart, reg, value); - } - - void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, MacroAssembler::Address address, void* value) - { - MacroAssembler::revertJumpReplacementToPatchableBranchPtrWithPatch(instructionStart, address, value); - } - -private: - void* m_start; - size_t m_size; -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) - -#endif // RepatchBuffer_h diff --git a/3rdparty/masm/assembler/SH4Assembler.h b/3rdparty/masm/assembler/SH4Assembler.h deleted file mode 100644 index 39f5585be1..0000000000 --- a/3rdparty/masm/assembler/SH4Assembler.h +++ /dev/null @@ -1,2152 +0,0 @@ -/* - * Copyright (C) 2009-2011 STMicroelectronics. All rights reserved. - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SH4Assembler_h -#define SH4Assembler_h - -#if ENABLE(ASSEMBLER) && CPU(SH4) - -#include "AssemblerBuffer.h" -#include "AssemblerBufferWithConstantPool.h" -#include "JITCompilationEffort.h" -#include -#include -#include -#include -#include -#include - -#ifndef NDEBUG -#define SH4_ASSEMBLER_TRACING -#endif - -namespace JSC { -typedef uint16_t SH4Word; - -enum { - INVALID_OPCODE = 0xffff, - ADD_OPCODE = 0x300c, - ADDIMM_OPCODE = 0x7000, - ADDC_OPCODE = 0x300e, - ADDV_OPCODE = 0x300f, - AND_OPCODE = 0x2009, - ANDIMM_OPCODE = 0xc900, - DIV0_OPCODE = 0x2007, - DIV1_OPCODE = 0x3004, - BF_OPCODE = 0x8b00, - BFS_OPCODE = 0x8f00, - BRA_OPCODE = 0xa000, - BRAF_OPCODE = 0x0023, - NOP_OPCODE = 0x0009, - BSR_OPCODE = 0xb000, - RTS_OPCODE = 0x000b, - BT_OPCODE = 0x8900, - BTS_OPCODE = 0x8d00, - BSRF_OPCODE = 0x0003, - BRK_OPCODE = 0x003b, - FTRC_OPCODE = 0xf03d, - CMPEQ_OPCODE = 0x3000, - CMPEQIMM_OPCODE = 0x8800, - CMPGE_OPCODE = 0x3003, - CMPGT_OPCODE = 0x3007, - CMPHI_OPCODE = 0x3006, - CMPHS_OPCODE = 0x3002, - CMPPL_OPCODE = 0x4015, - CMPPZ_OPCODE = 0x4011, - CMPSTR_OPCODE = 0x200c, - DT_OPCODE = 0x4010, - FCMPEQ_OPCODE = 0xf004, - FCMPGT_OPCODE = 0xf005, - FMOV_OPCODE = 0xf00c, - FADD_OPCODE = 0xf000, - FMUL_OPCODE = 0xf002, - FSUB_OPCODE = 0xf001, - FDIV_OPCODE = 0xf003, - FNEG_OPCODE = 0xf04d, - JMP_OPCODE = 0x402b, - JSR_OPCODE = 0x400b, - LDSPR_OPCODE = 0x402a, - LDSLPR_OPCODE = 0x4026, - MOV_OPCODE = 0x6003, - MOVIMM_OPCODE = 0xe000, - MOVB_WRITE_RN_OPCODE = 0x2000, - MOVB_WRITE_RNDEC_OPCODE = 0x2004, - MOVB_WRITE_R0RN_OPCODE = 0x0004, - MOVB_WRITE_OFFGBR_OPCODE = 0xc000, - MOVB_WRITE_OFFRN_OPCODE = 0x8000, - MOVB_READ_RM_OPCODE = 0x6000, - MOVB_READ_RMINC_OPCODE = 0x6004, - MOVB_READ_R0RM_OPCODE = 0x000c, - MOVB_READ_OFFGBR_OPCODE = 0xc400, - MOVB_READ_OFFRM_OPCODE = 0x8400, - MOVL_WRITE_RN_OPCODE = 0x2002, - MOVL_WRITE_RNDEC_OPCODE = 0x2006, - MOVL_WRITE_R0RN_OPCODE = 0x0006, - MOVL_WRITE_OFFGBR_OPCODE = 0xc200, - MOVL_WRITE_OFFRN_OPCODE = 0x1000, - MOVL_READ_RM_OPCODE = 0x6002, - MOVL_READ_RMINC_OPCODE = 0x6006, - MOVL_READ_R0RM_OPCODE = 0x000e, - MOVL_READ_OFFGBR_OPCODE = 0xc600, - MOVL_READ_OFFPC_OPCODE = 0xd000, - MOVL_READ_OFFRM_OPCODE = 0x5000, - MOVW_WRITE_RN_OPCODE = 0x2001, - MOVW_READ_RM_OPCODE = 0x6001, - MOVW_READ_R0RM_OPCODE = 0x000d, - MOVW_READ_OFFRM_OPCODE = 0x8500, - MOVW_READ_OFFPC_OPCODE = 0x9000, - MOVA_READ_OFFPC_OPCODE = 0xc700, - MOVT_OPCODE = 0x0029, - MULL_OPCODE = 0x0007, - DMULL_L_OPCODE = 0x3005, - STSMACL_OPCODE = 0x001a, - STSMACH_OPCODE = 0x000a, - DMULSL_OPCODE = 0x300d, - NEG_OPCODE = 0x600b, - NEGC_OPCODE = 0x600a, - NOT_OPCODE = 0x6007, - OR_OPCODE = 0x200b, - ORIMM_OPCODE = 0xcb00, - ORBIMM_OPCODE = 0xcf00, - SETS_OPCODE = 0x0058, - SETT_OPCODE = 0x0018, - SHAD_OPCODE = 0x400c, - SHAL_OPCODE = 0x4020, - SHAR_OPCODE = 0x4021, - SHLD_OPCODE = 0x400d, - SHLL_OPCODE = 0x4000, - SHLL2_OPCODE = 0x4008, - SHLL8_OPCODE = 0x4018, - SHLL16_OPCODE = 0x4028, - SHLR_OPCODE = 0x4001, - SHLR2_OPCODE = 0x4009, - SHLR8_OPCODE = 0x4019, - SHLR16_OPCODE = 0x4029, - STSPR_OPCODE = 0x002a, - STSLPR_OPCODE = 0x4022, - FLOAT_OPCODE = 0xf02d, - SUB_OPCODE = 0x3008, - SUBC_OPCODE = 0x300a, - SUBV_OPCODE = 0x300b, - TST_OPCODE = 0x2008, - TSTIMM_OPCODE = 0xc800, - TSTB_OPCODE = 0xcc00, - EXTUB_OPCODE = 0x600c, - EXTUW_OPCODE = 0x600d, - XOR_OPCODE = 0x200a, - XORIMM_OPCODE = 0xca00, - XORB_OPCODE = 0xce00, - FMOVS_READ_RM_INC_OPCODE = 0xf009, - FMOVS_READ_RM_OPCODE = 0xf008, - FMOVS_READ_R0RM_OPCODE = 0xf006, - FMOVS_WRITE_RN_OPCODE = 0xf00a, - FMOVS_WRITE_RN_DEC_OPCODE = 0xf00b, - FMOVS_WRITE_R0RN_OPCODE = 0xf007, - FCNVDS_DRM_FPUL_OPCODE = 0xf0bd, - FCNVSD_FPUL_DRN_OPCODE = 0xf0ad, - LDS_RM_FPUL_OPCODE = 0x405a, - FLDS_FRM_FPUL_OPCODE = 0xf01d, - STS_FPUL_RN_OPCODE = 0x005a, - FSTS_FPUL_FRN_OPCODE = 0xF00d, - LDSFPSCR_OPCODE = 0x406a, - STSFPSCR_OPCODE = 0x006a, - LDSRMFPUL_OPCODE = 0x405a, - FSTSFPULFRN_OPCODE = 0xf00d, - FSQRT_OPCODE = 0xf06d, - FSCHG_OPCODE = 0xf3fd, - CLRT_OPCODE = 8, -}; - -namespace SH4Registers { -typedef enum { - r0, - r1, - r2, - r3, - r4, - r5, - r6, - r7, - r8, - r9, - r10, - r11, - r12, - r13, - r14, fp = r14, - r15, sp = r15, - pc, - pr, -} RegisterID; - -typedef enum { - fr0, dr0 = fr0, - fr1, - fr2, dr2 = fr2, - fr3, - fr4, dr4 = fr4, - fr5, - fr6, dr6 = fr6, - fr7, - fr8, dr8 = fr8, - fr9, - fr10, dr10 = fr10, - fr11, - fr12, dr12 = fr12, - fr13, - fr14, dr14 = fr14, - fr15, -} FPRegisterID; -} - -inline uint16_t getOpcodeGroup1(uint16_t opc, int rm, int rn) -{ - return (opc | ((rm & 0xf) << 8) | ((rn & 0xf) << 4)); -} - -inline uint16_t getOpcodeGroup2(uint16_t opc, int rm) -{ - return (opc | ((rm & 0xf) << 8)); -} - -inline uint16_t getOpcodeGroup3(uint16_t opc, int rm, int rn) -{ - return (opc | ((rm & 0xf) << 8) | (rn & 0xff)); -} - -inline uint16_t getOpcodeGroup4(uint16_t opc, int rm, int rn, int offset) -{ - return (opc | ((rm & 0xf) << 8) | ((rn & 0xf) << 4) | (offset & 0xf)); -} - -inline uint16_t getOpcodeGroup5(uint16_t opc, int rm) -{ - return (opc | (rm & 0xff)); -} - -inline uint16_t getOpcodeGroup6(uint16_t opc, int rm) -{ - return (opc | (rm & 0xfff)); -} - -inline uint16_t getOpcodeGroup7(uint16_t opc, int rm) -{ - return (opc | ((rm & 0x7) << 9)); -} - -inline uint16_t getOpcodeGroup8(uint16_t opc, int rm, int rn) -{ - return (opc | ((rm & 0x7) << 9) | ((rn & 0x7) << 5)); -} - -inline uint16_t getOpcodeGroup9(uint16_t opc, int rm, int rn) -{ - return (opc | ((rm & 0xf) << 8) | ((rn & 0x7) << 5)); -} - -inline uint16_t getOpcodeGroup10(uint16_t opc, int rm, int rn) -{ - return (opc | ((rm & 0x7) << 9) | ((rn & 0xf) << 4)); -} - -inline uint16_t getOpcodeGroup11(uint16_t opc, int rm, int rn) -{ - return (opc | ((rm & 0xf) << 4) | (rn & 0xf)); -} - -inline uint16_t getRn(uint16_t x) -{ - return ((x & 0xf00) >> 8); -} - -inline uint16_t getRm(uint16_t x) -{ - return ((x & 0xf0) >> 4); -} - -inline uint16_t getDisp(uint16_t x) -{ - return (x & 0xf); -} - -inline uint16_t getImm8(uint16_t x) -{ - return (x & 0xff); -} - -inline uint16_t getImm12(uint16_t x) -{ - return (x & 0xfff); -} - -inline uint16_t getDRn(uint16_t x) -{ - return ((x & 0xe00) >> 9); -} - -inline uint16_t getDRm(uint16_t x) -{ - return ((x & 0xe0) >> 5); -} - -class SH4Assembler { -public: - typedef SH4Registers::RegisterID RegisterID; - typedef SH4Registers::FPRegisterID FPRegisterID; - typedef AssemblerBufferWithConstantPool<512, 4, 2, SH4Assembler> SH4Buffer; - static const RegisterID scratchReg1 = SH4Registers::r3; - static const RegisterID scratchReg2 = SH4Registers::r11; - static const uint32_t maxInstructionSize = 16; - - enum { - padForAlign8 = 0x00, - padForAlign16 = 0x0009, - padForAlign32 = 0x00090009, - }; - - enum JumpType { - JumpFar, - JumpNear - }; - - SH4Assembler() - { - m_claimscratchReg = 0x0; - } - - // SH4 condition codes - typedef enum { - EQ = 0x0, // Equal - NE = 0x1, // Not Equal - HS = 0x2, // Unsigend Greater Than equal - HI = 0x3, // Unsigend Greater Than - LS = 0x4, // Unsigend Lower or Same - LI = 0x5, // Unsigend Lower - GE = 0x6, // Greater or Equal - LT = 0x7, // Less Than - GT = 0x8, // Greater Than - LE = 0x9, // Less or Equal - OF = 0xa, // OverFlow - SI = 0xb, // Signed - EQU= 0xc, // Equal or unordered(NaN) - NEU= 0xd, - GTU= 0xe, - GEU= 0xf, - LTU= 0x10, - LEU= 0x11, - } Condition; - - // Opaque label types -public: - bool isImmediate(int constant) - { - return ((constant <= 127) && (constant >= -128)); - } - - RegisterID claimScratch() - { - ASSERT((m_claimscratchReg != 0x3)); - - if (!(m_claimscratchReg & 0x1)) { - m_claimscratchReg = (m_claimscratchReg | 0x1); - return scratchReg1; - } - - m_claimscratchReg = (m_claimscratchReg | 0x2); - return scratchReg2; - } - - void releaseScratch(RegisterID scratchR) - { - if (scratchR == scratchReg1) - m_claimscratchReg = (m_claimscratchReg & 0x2); - else - m_claimscratchReg = (m_claimscratchReg & 0x1); - } - - // Stack operations - - void pushReg(RegisterID reg) - { - if (reg == SH4Registers::pr) { - oneShortOp(getOpcodeGroup2(STSLPR_OPCODE, SH4Registers::sp)); - return; - } - - oneShortOp(getOpcodeGroup1(MOVL_WRITE_RNDEC_OPCODE, SH4Registers::sp, reg)); - } - - void popReg(RegisterID reg) - { - if (reg == SH4Registers::pr) { - oneShortOp(getOpcodeGroup2(LDSLPR_OPCODE, SH4Registers::sp)); - return; - } - - oneShortOp(getOpcodeGroup1(MOVL_READ_RMINC_OPCODE, reg, SH4Registers::sp)); - } - - void movt(RegisterID dst) - { - uint16_t opc = getOpcodeGroup2(MOVT_OPCODE, dst); - oneShortOp(opc); - } - - // Arithmetic operations - - void addlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(ADD_OPCODE, dst, src); - oneShortOp(opc); - } - - void addclRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(ADDC_OPCODE, dst, src); - oneShortOp(opc); - } - - void addvlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(ADDV_OPCODE, dst, src); - oneShortOp(opc); - } - - void addlImm8r(int imm8, RegisterID dst) - { - ASSERT((imm8 <= 127) && (imm8 >= -128)); - - uint16_t opc = getOpcodeGroup3(ADDIMM_OPCODE, dst, imm8); - oneShortOp(opc); - } - - void andlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(AND_OPCODE, dst, src); - oneShortOp(opc); - } - - void andlImm8r(int imm8, RegisterID dst) - { - ASSERT((imm8 <= 255) && (imm8 >= 0)); - ASSERT(dst == SH4Registers::r0); - - uint16_t opc = getOpcodeGroup5(ANDIMM_OPCODE, imm8); - oneShortOp(opc); - } - - void div1lRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(DIV1_OPCODE, dst, src); - oneShortOp(opc); - } - - void div0lRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(DIV0_OPCODE, dst, src); - oneShortOp(opc); - } - - void notlReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(NOT_OPCODE, dst, src); - oneShortOp(opc); - } - - void orlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(OR_OPCODE, dst, src); - oneShortOp(opc); - } - - void orlImm8r(int imm8, RegisterID dst) - { - ASSERT((imm8 <= 255) && (imm8 >= 0)); - ASSERT(dst == SH4Registers::r0); - - uint16_t opc = getOpcodeGroup5(ORIMM_OPCODE, imm8); - oneShortOp(opc); - } - - void sublRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(SUB_OPCODE, dst, src); - oneShortOp(opc); - } - - void subvlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(SUBV_OPCODE, dst, src); - oneShortOp(opc); - } - - void xorlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(XOR_OPCODE, dst, src); - oneShortOp(opc); - } - - void xorlImm8r(int imm8, RegisterID dst) - { - ASSERT((imm8 <= 255) && (imm8 >= 0)); - ASSERT(dst == SH4Registers::r0); - - uint16_t opc = getOpcodeGroup5(XORIMM_OPCODE, imm8); - oneShortOp(opc); - } - - void shllImm8r(int imm, RegisterID dst) - { - switch (imm) { - case 1: - oneShortOp(getOpcodeGroup2(SHLL_OPCODE, dst)); - break; - case 2: - oneShortOp(getOpcodeGroup2(SHLL2_OPCODE, dst)); - break; - case 8: - oneShortOp(getOpcodeGroup2(SHLL8_OPCODE, dst)); - break; - case 16: - oneShortOp(getOpcodeGroup2(SHLL16_OPCODE, dst)); - break; - default: - ASSERT_NOT_REACHED(); - } - } - - void neg(RegisterID dst, RegisterID src) - { - uint16_t opc = getOpcodeGroup1(NEG_OPCODE, dst, src); - oneShortOp(opc); - } - - void shllRegReg(RegisterID dst, RegisterID rShift) - { - uint16_t opc = getOpcodeGroup1(SHLD_OPCODE, dst, rShift); - oneShortOp(opc); - } - - void shlrRegReg(RegisterID dst, RegisterID rShift) - { - neg(rShift, rShift); - shllRegReg(dst, rShift); - } - - void sharRegReg(RegisterID dst, RegisterID rShift) - { - neg(rShift, rShift); - shaRegReg(dst, rShift); - } - - void shaRegReg(RegisterID dst, RegisterID rShift) - { - uint16_t opc = getOpcodeGroup1(SHAD_OPCODE, dst, rShift); - oneShortOp(opc); - } - - void shlrImm8r(int imm, RegisterID dst) - { - switch (imm) { - case 1: - oneShortOp(getOpcodeGroup2(SHLR_OPCODE, dst)); - break; - case 2: - oneShortOp(getOpcodeGroup2(SHLR2_OPCODE, dst)); - break; - case 8: - oneShortOp(getOpcodeGroup2(SHLR8_OPCODE, dst)); - break; - case 16: - oneShortOp(getOpcodeGroup2(SHLR16_OPCODE, dst)); - break; - default: - ASSERT_NOT_REACHED(); - } - } - - void imullRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MULL_OPCODE, dst, src); - oneShortOp(opc); - } - - void dmullRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(DMULL_L_OPCODE, dst, src); - oneShortOp(opc); - } - - void dmulslRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(DMULSL_OPCODE, dst, src); - oneShortOp(opc); - } - - void stsmacl(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(STSMACL_OPCODE, reg); - oneShortOp(opc); - } - - void stsmach(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(STSMACH_OPCODE, reg); - oneShortOp(opc); - } - - // Comparisons - - void cmplRegReg(RegisterID left, RegisterID right, Condition cond) - { - switch (cond) { - case NE: - oneShortOp(getOpcodeGroup1(CMPEQ_OPCODE, right, left)); - break; - case GT: - oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, right, left)); - break; - case EQ: - oneShortOp(getOpcodeGroup1(CMPEQ_OPCODE, right, left)); - break; - case GE: - oneShortOp(getOpcodeGroup1(CMPGE_OPCODE, right, left)); - break; - case HS: - oneShortOp(getOpcodeGroup1(CMPHS_OPCODE, right, left)); - break; - case HI: - oneShortOp(getOpcodeGroup1(CMPHI_OPCODE, right, left)); - break; - case LI: - oneShortOp(getOpcodeGroup1(CMPHI_OPCODE, left, right)); - break; - case LS: - oneShortOp(getOpcodeGroup1(CMPHS_OPCODE, left, right)); - break; - case LE: - oneShortOp(getOpcodeGroup1(CMPGE_OPCODE, left, right)); - break; - case LT: - oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, left, right)); - break; - default: - ASSERT_NOT_REACHED(); - } - } - - void cmppl(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(CMPPL_OPCODE, reg); - oneShortOp(opc); - } - - void cmppz(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(CMPPZ_OPCODE, reg); - oneShortOp(opc); - } - - void cmpEqImmR0(int imm, RegisterID dst) - { - uint16_t opc = getOpcodeGroup5(CMPEQIMM_OPCODE, imm); - oneShortOp(opc); - } - - void testlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(TST_OPCODE, dst, src); - oneShortOp(opc); - } - - void testlImm8r(int imm, RegisterID dst) - { - ASSERT((dst == SH4Registers::r0) && (imm <= 255) && (imm >= 0)); - - uint16_t opc = getOpcodeGroup5(TSTIMM_OPCODE, imm); - oneShortOp(opc); - } - - void nop() - { - oneShortOp(NOP_OPCODE, false); - } - - void sett() - { - oneShortOp(SETT_OPCODE); - } - - void clrt() - { - oneShortOp(CLRT_OPCODE); - } - - void fschg() - { - oneShortOp(FSCHG_OPCODE); - } - - void bkpt() - { - oneShortOp(BRK_OPCODE, false); - } - - void branch(uint16_t opc, int label) - { - switch (opc) { - case BT_OPCODE: - ASSERT((label <= 127) && (label >= -128)); - oneShortOp(getOpcodeGroup5(BT_OPCODE, label)); - break; - case BRA_OPCODE: - ASSERT((label <= 2047) && (label >= -2048)); - oneShortOp(getOpcodeGroup6(BRA_OPCODE, label)); - break; - case BF_OPCODE: - ASSERT((label <= 127) && (label >= -128)); - oneShortOp(getOpcodeGroup5(BF_OPCODE, label)); - break; - default: - ASSERT_NOT_REACHED(); - } - } - - void branch(uint16_t opc, RegisterID reg) - { - switch (opc) { - case BRAF_OPCODE: - oneShortOp(getOpcodeGroup2(BRAF_OPCODE, reg)); - break; - case JMP_OPCODE: - oneShortOp(getOpcodeGroup2(JMP_OPCODE, reg)); - break; - case JSR_OPCODE: - oneShortOp(getOpcodeGroup2(JSR_OPCODE, reg)); - break; - case BSRF_OPCODE: - oneShortOp(getOpcodeGroup2(BSRF_OPCODE, reg)); - break; - default: - ASSERT_NOT_REACHED(); - } - } - - void ldspr(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(LDSPR_OPCODE, reg); - oneShortOp(opc); - } - - void stspr(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(STSPR_OPCODE, reg); - oneShortOp(opc); - } - - void extub(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(EXTUB_OPCODE, dst, src); - oneShortOp(opc); - } - - void extuw(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(EXTUW_OPCODE, dst, src); - oneShortOp(opc); - } - - // float operations - - void ldsrmfpul(RegisterID src) - { - uint16_t opc = getOpcodeGroup2(LDS_RM_FPUL_OPCODE, src); - oneShortOp(opc); - } - - void fneg(FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup2(FNEG_OPCODE, dst); - oneShortOp(opc, true, false); - } - - void fsqrt(FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup2(FSQRT_OPCODE, dst); - oneShortOp(opc, true, false); - } - - void stsfpulReg(RegisterID src) - { - uint16_t opc = getOpcodeGroup2(STS_FPUL_RN_OPCODE, src); - oneShortOp(opc); - } - - void floatfpulfrn(FPRegisterID src) - { - uint16_t opc = getOpcodeGroup2(FLOAT_OPCODE, src); - oneShortOp(opc, true, false); - } - - void fmull(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup1(FMUL_OPCODE, dst, src); - oneShortOp(opc, true, false); - } - - void fmovsReadrm(RegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_OPCODE, dst, src); - oneShortOp(opc, true, false); - } - - void fmovsWriterm(FPRegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_RN_OPCODE, dst, src); - oneShortOp(opc, true, false); - } - - void fmovsWriter0r(FPRegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_R0RN_OPCODE, dst, src); - oneShortOp(opc, true, false); - } - - void fmovsReadr0r(RegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup1(FMOVS_READ_R0RM_OPCODE, dst, src); - oneShortOp(opc, true, false); - } - - void fmovsReadrminc(RegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_INC_OPCODE, dst, src); - oneShortOp(opc, true, false); - } - - void fmovsWriterndec(FPRegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_RN_DEC_OPCODE, dst, src); - oneShortOp(opc, true, false); - } - - void ftrcRegfpul(FPRegisterID src) - { - uint16_t opc = getOpcodeGroup2(FTRC_OPCODE, src); - oneShortOp(opc, true, false); - } - - void fldsfpul(FPRegisterID src) - { - uint16_t opc = getOpcodeGroup2(FLDS_FRM_FPUL_OPCODE, src); - oneShortOp(opc); - } - - void fstsfpul(FPRegisterID src) - { - uint16_t opc = getOpcodeGroup2(FSTS_FPUL_FRN_OPCODE, src); - oneShortOp(opc); - } - - void ldsfpscr(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(LDSFPSCR_OPCODE, reg); - oneShortOp(opc); - } - - void stsfpscr(RegisterID reg) - { - uint16_t opc = getOpcodeGroup2(STSFPSCR_OPCODE, reg); - oneShortOp(opc); - } - - // double operations - - void dcnvds(FPRegisterID src) - { - uint16_t opc = getOpcodeGroup7(FCNVDS_DRM_FPUL_OPCODE, src >> 1); - oneShortOp(opc); - } - - void dcnvsd(FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup7(FCNVSD_FPUL_DRN_OPCODE, dst >> 1); - oneShortOp(opc); - } - - void dcmppeq(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup8(FCMPEQ_OPCODE, dst >> 1, src >> 1); - oneShortOp(opc); - } - - void dcmppgt(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup8(FCMPGT_OPCODE, dst >> 1, src >> 1); - oneShortOp(opc); - } - - void dmulRegReg(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup8(FMUL_OPCODE, dst >> 1, src >> 1); - oneShortOp(opc); - } - - void dsubRegReg(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup8(FSUB_OPCODE, dst >> 1, src >> 1); - oneShortOp(opc); - } - - void daddRegReg(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup8(FADD_OPCODE, dst >> 1, src >> 1); - oneShortOp(opc); - } - - void dmovRegReg(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup8(FMOV_OPCODE, dst >> 1, src >> 1); - oneShortOp(opc); - } - - void ddivRegReg(FPRegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup8(FDIV_OPCODE, dst >> 1, src >> 1); - oneShortOp(opc); - } - - void dsqrt(FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup7(FSQRT_OPCODE, dst >> 1); - oneShortOp(opc); - } - - void dneg(FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup7(FNEG_OPCODE, dst >> 1); - oneShortOp(opc); - } - - void fmovReadrm(RegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup10(FMOVS_READ_RM_OPCODE, dst >> 1, src); - oneShortOp(opc); - } - - void fmovWriterm(FPRegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_RN_OPCODE, dst, src >> 1); - oneShortOp(opc); - } - - void fmovWriter0r(FPRegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_R0RN_OPCODE, dst, src >> 1); - oneShortOp(opc); - } - - void fmovReadr0r(RegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup10(FMOVS_READ_R0RM_OPCODE, dst >> 1, src); - oneShortOp(opc); - } - - void fmovReadrminc(RegisterID src, FPRegisterID dst) - { - uint16_t opc = getOpcodeGroup10(FMOVS_READ_RM_INC_OPCODE, dst >> 1, src); - oneShortOp(opc); - } - - void fmovWriterndec(FPRegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_RN_DEC_OPCODE, dst, src >> 1); - oneShortOp(opc); - } - - void floatfpulDreg(FPRegisterID src) - { - uint16_t opc = getOpcodeGroup7(FLOAT_OPCODE, src >> 1); - oneShortOp(opc); - } - - void ftrcdrmfpul(FPRegisterID src) - { - uint16_t opc = getOpcodeGroup7(FTRC_OPCODE, src >> 1); - oneShortOp(opc); - } - - // Various move ops - - void movImm8(int imm8, RegisterID dst) - { - ASSERT((imm8 <= 127) && (imm8 >= -128)); - - uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, imm8); - oneShortOp(opc); - } - - void movlRegReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOV_OPCODE, dst, src); - oneShortOp(opc); - } - - void movwRegMem(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVW_WRITE_RN_OPCODE, dst, src); - oneShortOp(opc); - } - - void movwMemReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVW_READ_RM_OPCODE, dst, src); - oneShortOp(opc); - } - - void movwPCReg(int offset, RegisterID base, RegisterID dst) - { - ASSERT(base == SH4Registers::pc); - ASSERT((offset <= 255) && (offset >= 0)); - - uint16_t opc = getOpcodeGroup3(MOVW_READ_OFFPC_OPCODE, dst, offset); - oneShortOp(opc); - } - - void movwMemReg(int offset, RegisterID base, RegisterID dst) - { - ASSERT(dst == SH4Registers::r0); - - uint16_t opc = getOpcodeGroup11(MOVW_READ_OFFRM_OPCODE, base, offset); - oneShortOp(opc); - } - - void movwR0mr(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVW_READ_R0RM_OPCODE, dst, src); - oneShortOp(opc); - } - - void movlRegMem(RegisterID src, int offset, RegisterID base) - { - ASSERT((offset <= 15) && (offset >= 0)); - - if (!offset) { - oneShortOp(getOpcodeGroup1(MOVL_WRITE_RN_OPCODE, base, src)); - return; - } - - oneShortOp(getOpcodeGroup4(MOVL_WRITE_OFFRN_OPCODE, base, src, offset)); - } - - void movlRegMem(RegisterID src, RegisterID base) - { - uint16_t opc = getOpcodeGroup1(MOVL_WRITE_RN_OPCODE, base, src); - oneShortOp(opc); - } - - void movlMemReg(int offset, RegisterID base, RegisterID dst) - { - if (base == SH4Registers::pc) { - ASSERT((offset <= 255) && (offset >= 0)); - oneShortOp(getOpcodeGroup3(MOVL_READ_OFFPC_OPCODE, dst, offset)); - return; - } - - ASSERT((offset <= 15) && (offset >= 0)); - if (!offset) { - oneShortOp(getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base)); - return; - } - - oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset)); - } - - void movlMemRegCompact(int offset, RegisterID base, RegisterID dst) - { - oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset)); - } - - void movbRegMem(RegisterID src, RegisterID base) - { - uint16_t opc = getOpcodeGroup1(MOVB_WRITE_RN_OPCODE, base, src); - oneShortOp(opc); - } - - void movbMemReg(int offset, RegisterID base, RegisterID dst) - { - ASSERT(dst == SH4Registers::r0); - - uint16_t opc = getOpcodeGroup11(MOVB_READ_OFFRM_OPCODE, base, offset); - oneShortOp(opc); - } - - void movbR0mr(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVB_READ_R0RM_OPCODE, dst, src); - oneShortOp(opc); - } - - void movbMemReg(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVB_READ_RM_OPCODE, dst, src); - oneShortOp(opc); - } - - void movlMemReg(RegisterID base, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base); - oneShortOp(opc); - } - - void movlMemRegIn(RegisterID base, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVL_READ_RMINC_OPCODE, dst, base); - oneShortOp(opc); - } - - void movlR0mr(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVL_READ_R0RM_OPCODE, dst, src); - oneShortOp(opc); - } - - void movlRegMemr0(RegisterID src, RegisterID dst) - { - uint16_t opc = getOpcodeGroup1(MOVL_WRITE_R0RN_OPCODE, dst, src); - oneShortOp(opc); - } - - void movlImm8r(int imm8, RegisterID dst) - { - ASSERT((imm8 <= 127) && (imm8 >= -128)); - - uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, imm8); - oneShortOp(opc); - } - - void loadConstant(uint32_t constant, RegisterID dst) - { - if (((int)constant <= 0x7f) && ((int)constant >= -0x80)) { - movImm8(constant, dst); - return; - } - - uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, 0); - - m_buffer.ensureSpace(maxInstructionSize, sizeof(uint32_t)); - printInstr(getOpcodeGroup3(MOVIMM_OPCODE, dst, constant), m_buffer.codeSize()); - m_buffer.putShortWithConstantInt(opc, constant, true); - } - - void loadConstantUnReusable(uint32_t constant, RegisterID dst, bool ensureSpace = false) - { - uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, 0); - - if (ensureSpace) - m_buffer.ensureSpace(maxInstructionSize, sizeof(uint32_t)); - - printInstr(getOpcodeGroup3(MOVIMM_OPCODE, dst, constant), m_buffer.codeSize()); - m_buffer.putShortWithConstantInt(opc, constant); - } - - // Flow control - - AssemblerLabel call() - { - RegisterID scr = claimScratch(); - m_buffer.ensureSpace(maxInstructionSize + 4, sizeof(uint32_t)); - loadConstantUnReusable(0x0, scr); - branch(JSR_OPCODE, scr); - nop(); - releaseScratch(scr); - return m_buffer.label(); - } - - AssemblerLabel call(RegisterID dst) - { - m_buffer.ensureSpace(maxInstructionSize + 2); - branch(JSR_OPCODE, dst); - nop(); - return m_buffer.label(); - } - - AssemblerLabel jmp() - { - RegisterID scr = claimScratch(); - m_buffer.ensureSpace(maxInstructionSize + 4, sizeof(uint32_t)); - AssemblerLabel label = m_buffer.label(); - loadConstantUnReusable(0x0, scr); - branch(BRAF_OPCODE, scr); - nop(); - releaseScratch(scr); - return label; - } - - void extraInstrForBranch(RegisterID dst) - { - loadConstantUnReusable(0x0, dst); - nop(); - nop(); - } - - AssemblerLabel jmp(RegisterID dst) - { - jmpReg(dst); - return m_buffer.label(); - } - - void jmpReg(RegisterID dst) - { - m_buffer.ensureSpace(maxInstructionSize + 2); - branch(JMP_OPCODE, dst); - nop(); - } - - AssemblerLabel jne() - { - AssemblerLabel label = m_buffer.label(); - branch(BF_OPCODE, 0); - return label; - } - - AssemblerLabel je() - { - AssemblerLabel label = m_buffer.label(); - branch(BT_OPCODE, 0); - return label; - } - - AssemblerLabel bra() - { - AssemblerLabel label = m_buffer.label(); - branch(BRA_OPCODE, 0); - return label; - } - - void ret() - { - m_buffer.ensureSpace(maxInstructionSize + 2); - oneShortOp(RTS_OPCODE, false); - } - - AssemblerLabel labelIgnoringWatchpoints() - { - m_buffer.ensureSpaceForAnyInstruction(); - return m_buffer.label(); - } - - AssemblerLabel label() - { - m_buffer.ensureSpaceForAnyInstruction(); - return m_buffer.label(); - } - - int sizeOfConstantPool() - { - return m_buffer.sizeOfConstantPool(); - } - - AssemblerLabel align(int alignment) - { - m_buffer.ensureSpace(maxInstructionSize + 2); - while (!m_buffer.isAligned(alignment)) { - nop(); - m_buffer.ensureSpace(maxInstructionSize + 2); - } - return label(); - } - - static void changePCrelativeAddress(int offset, uint16_t* instructionPtr, uint32_t newAddress) - { - uint32_t address = (offset << 2) + ((reinterpret_cast(instructionPtr) + 4) &(~0x3)); - *reinterpret_cast(address) = newAddress; - } - - static uint32_t readPCrelativeAddress(int offset, uint16_t* instructionPtr) - { - uint32_t address = (offset << 2) + ((reinterpret_cast(instructionPtr) + 4) &(~0x3)); - return *reinterpret_cast(address); - } - - static uint16_t* getInstructionPtr(void* code, int offset) - { - return reinterpret_cast (reinterpret_cast(code) + offset); - } - - static void linkJump(void* code, AssemblerLabel from, void* to) - { - ASSERT(from.isSet()); - - uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset); - uint16_t instruction = *instructionPtr; - int offsetBits = (reinterpret_cast(to) - reinterpret_cast(code)) - from.m_offset; - - if (((instruction & 0xff00) == BT_OPCODE) || ((instruction & 0xff00) == BF_OPCODE)) { - /* BT label ==> BF 2 - nop LDR reg - nop braf @reg - nop nop - */ - offsetBits -= 8; - instruction ^= 0x0202; - *instructionPtr++ = instruction; - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); - instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); - *instructionPtr = instruction; - printBlockInstr(instructionPtr - 2, from.m_offset, 3); - return; - } - - /* MOV #imm, reg => LDR reg - braf @reg braf @reg - nop nop - */ - ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); - - offsetBits -= 4; - if (offsetBits >= -4096 && offsetBits <= 4094) { - *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); - *(++instructionPtr) = NOP_OPCODE; - printBlockInstr(instructionPtr - 1, from.m_offset, 2); - return; - } - - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); - printInstr(*instructionPtr, from.m_offset + 2); - } - - static void linkCall(void* code, AssemblerLabel from, void* to) - { - uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset); - instructionPtr -= 3; - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(to)); - } - - static void linkPointer(void* code, AssemblerLabel where, void* value) - { - uint16_t* instructionPtr = getInstructionPtr(code, where.m_offset); - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(value)); - } - - static unsigned getCallReturnOffset(AssemblerLabel call) - { - ASSERT(call.isSet()); - return call.m_offset; - } - - static uint32_t* getLdrImmAddressOnPool(SH4Word* insn, uint32_t* constPool) - { - return (constPool + (*insn & 0xff)); - } - - static SH4Word patchConstantPoolLoad(SH4Word load, int value) - { - return ((load & ~0xff) | value); - } - - static SH4Buffer::TwoShorts placeConstantPoolBarrier(int offset) - { - ASSERT(((offset >> 1) <=2047) && ((offset >> 1) >= -2048)); - - SH4Buffer::TwoShorts m_barrier; - m_barrier.high = (BRA_OPCODE | (offset >> 1)); - m_barrier.low = NOP_OPCODE; - printInstr(((BRA_OPCODE | (offset >> 1))), 0); - printInstr(NOP_OPCODE, 0); - return m_barrier; - } - - static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr) - { - SH4Word* instructionPtr = reinterpret_cast(loadAddr); - SH4Word instruction = *instructionPtr; - SH4Word index = instruction & 0xff; - - if ((instruction & 0xf000) != MOVIMM_OPCODE) - return; - - ASSERT((((reinterpret_cast(constPoolAddr) - reinterpret_cast(loadAddr)) + index * 4)) < 1024); - - int offset = reinterpret_cast(constPoolAddr) + (index * 4) - ((reinterpret_cast(instructionPtr) & ~0x03) + 4); - instruction &=0xf00; - instruction |= 0xd000; - offset &= 0x03ff; - instruction |= (offset >> 2); - *instructionPtr = instruction; - printInstr(instruction, reinterpret_cast(loadAddr)); - } - - static void repatchPointer(void* where, void* value) - { - patchPointer(where, value); - } - - static void* readPointer(void* code) - { - return reinterpret_cast(readInt32(code)); - } - - static void repatchInt32(void* where, int32_t value) - { - uint16_t* instructionPtr = reinterpret_cast(where); - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, value); - } - - static void repatchCompact(void* where, int32_t value) - { - ASSERT(value >= 0); - ASSERT(value <= 60); - *reinterpret_cast(where) = ((*reinterpret_cast(where) & 0xfff0) | (value >> 2)); - cacheFlush(reinterpret_cast(where), sizeof(uint16_t)); - } - - static void relinkCall(void* from, void* to) - { - uint16_t* instructionPtr = reinterpret_cast(from); - instructionPtr -= 3; - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(to)); - } - - static void relinkJump(void* from, void* to) - { - uint16_t* instructionPtr = reinterpret_cast (from); - uint16_t instruction = *instructionPtr; - int32_t offsetBits = (reinterpret_cast(to) - reinterpret_cast(from)); - - if (((*instructionPtr & 0xff00) == BT_OPCODE) || ((*instructionPtr & 0xff00) == BF_OPCODE)) { - offsetBits -= 8; - instructionPtr++; - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); - instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); - *instructionPtr = instruction; - printBlockInstr(instructionPtr, reinterpret_cast(from) + 1, 3); - return; - } - - ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); - offsetBits -= 4; - if (offsetBits >= -4096 && offsetBits <= 4094) { - *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); - *(++instructionPtr) = NOP_OPCODE; - printBlockInstr(instructionPtr - 2, reinterpret_cast(from), 2); - return; - } - - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); - printInstr(*instructionPtr, reinterpret_cast(from)); - } - - // Linking & patching - - static void revertJump(void* instructionStart, SH4Word imm) - { - SH4Word *insn = reinterpret_cast(instructionStart); - SH4Word disp; - - ASSERT((insn[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); - - disp = insn[0] & 0x00ff; - insn += 2 + (disp << 1); // PC += 4 + (disp*4) - insn = (SH4Word *) ((unsigned) insn & (~3)); - insn[0] = imm; - cacheFlush(insn, sizeof(SH4Word)); - } - - void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type = JumpFar) - { - ASSERT(to.isSet()); - ASSERT(from.isSet()); - - uint16_t* instructionPtr = getInstructionPtr(data(), from.m_offset); - uint16_t instruction = *instructionPtr; - int offsetBits; - - if (type == JumpNear) { - ASSERT((instruction == BT_OPCODE) || (instruction == BF_OPCODE) || (instruction == BRA_OPCODE)); - int offset = (codeSize() - from.m_offset) - 4; - *instructionPtr++ = instruction | (offset >> 1); - printInstr(*instructionPtr, from.m_offset + 2); - return; - } - - if (((instruction & 0xff00) == BT_OPCODE) || ((instruction & 0xff00) == BF_OPCODE)) { - /* BT label => BF 2 - nop LDR reg - nop braf @reg - nop nop - */ - offsetBits = (to.m_offset - from.m_offset) - 8; - instruction ^= 0x0202; - *instructionPtr++ = instruction; - if ((*instructionPtr & 0xf000) == 0xe000) { - uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress()); - *addr = offsetBits; - } else - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); - instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); - *instructionPtr = instruction; - printBlockInstr(instructionPtr - 2, from.m_offset, 3); - return; - } - - /* MOV # imm, reg => LDR reg - braf @reg braf @reg - nop nop - */ - ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); - offsetBits = (to.m_offset - from.m_offset) - 4; - if (offsetBits >= -4096 && offsetBits <= 4094) { - *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); - *(++instructionPtr) = NOP_OPCODE; - printBlockInstr(instructionPtr - 1, from.m_offset, 2); - return; - } - - instruction = *instructionPtr; - if ((instruction & 0xf000) == 0xe000) { - uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress()); - *addr = offsetBits - 2; - printInstr(*instructionPtr, from.m_offset + 2); - return; - } - - changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); - printInstr(*instructionPtr, from.m_offset + 2); - } - - static void* getRelocatedAddress(void* code, AssemblerLabel label) - { - return reinterpret_cast(reinterpret_cast(code) + label.m_offset); - } - - static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) - { - return b.m_offset - a.m_offset; - } - - static void patchPointer(void* code, AssemblerLabel where, void* value) - { - patchPointer(reinterpret_cast(code) + where.m_offset, value); - } - - static void patchPointer(void* code, void* value) - { - patchInt32(code, reinterpret_cast(value)); - } - - static void patchInt32(void* code, uint32_t value) - { - changePCrelativeAddress((*(reinterpret_cast(code)) & 0xff), reinterpret_cast(code), value); - } - - static uint32_t readInt32(void* code) - { - return readPCrelativeAddress((*(reinterpret_cast(code)) & 0xff), reinterpret_cast(code)); - } - - static void* readCallTarget(void* from) - { - uint16_t* instructionPtr = static_cast(from); - instructionPtr -= 3; - return reinterpret_cast(readPCrelativeAddress((*instructionPtr & 0xff), instructionPtr)); - } - - PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) - { - return m_buffer.executableCopy(globalData, ownerUID, effort); - } - - static void cacheFlush(void* code, size_t size) - { -#if !OS(LINUX) -#error "The cacheFlush support is missing on this platform." -#elif defined CACHEFLUSH_D_L2 - syscall(__NR_cacheflush, reinterpret_cast(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I | CACHEFLUSH_D_L2); -#else - syscall(__NR_cacheflush, reinterpret_cast(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I); -#endif - } - - void prefix(uint16_t pre) - { - m_buffer.putByte(pre); - } - - void oneShortOp(uint16_t opcode, bool checksize = true, bool isDouble = true) - { - printInstr(opcode, m_buffer.codeSize(), isDouble); - if (checksize) - m_buffer.ensureSpace(maxInstructionSize); - m_buffer.putShortUnchecked(opcode); - } - - void ensureSpace(int space) - { - m_buffer.ensureSpace(space); - } - - void ensureSpace(int insnSpace, int constSpace) - { - m_buffer.ensureSpace(insnSpace, constSpace); - } - - // Administrative methods - - void* data() const { return m_buffer.data(); } - size_t codeSize() const { return m_buffer.codeSize(); } - -#ifdef SH4_ASSEMBLER_TRACING - static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true) - { - if (!getenv("JavaScriptCoreDumpJIT")) - return; - - const char *format = 0; - printfStdoutInstr("offset: 0x%8.8x\t", size); - switch (opc) { - case BRK_OPCODE: - format = " BRK\n"; - break; - case NOP_OPCODE: - format = " NOP\n"; - break; - case RTS_OPCODE: - format =" *RTS\n"; - break; - case SETS_OPCODE: - format = " SETS\n"; - break; - case SETT_OPCODE: - format = " SETT\n"; - break; - case CLRT_OPCODE: - format = " CLRT\n"; - break; - case FSCHG_OPCODE: - format = " FSCHG\n"; - break; - } - if (format) { - printfStdoutInstr(format); - return; - } - switch (opc & 0xf0ff) { - case BRAF_OPCODE: - format = " *BRAF R%d\n"; - break; - case DT_OPCODE: - format = " DT R%d\n"; - break; - case CMPPL_OPCODE: - format = " CMP/PL R%d\n"; - break; - case CMPPZ_OPCODE: - format = " CMP/PZ R%d\n"; - break; - case JMP_OPCODE: - format = " *JMP @R%d\n"; - break; - case JSR_OPCODE: - format = " *JSR @R%d\n"; - break; - case LDSPR_OPCODE: - format = " LDS R%d, PR\n"; - break; - case LDSLPR_OPCODE: - format = " LDS.L @R%d+, PR\n"; - break; - case MOVT_OPCODE: - format = " MOVT R%d\n"; - break; - case SHAL_OPCODE: - format = " SHAL R%d\n"; - break; - case SHAR_OPCODE: - format = " SHAR R%d\n"; - break; - case SHLL_OPCODE: - format = " SHLL R%d\n"; - break; - case SHLL2_OPCODE: - format = " SHLL2 R%d\n"; - break; - case SHLL8_OPCODE: - format = " SHLL8 R%d\n"; - break; - case SHLL16_OPCODE: - format = " SHLL16 R%d\n"; - break; - case SHLR_OPCODE: - format = " SHLR R%d\n"; - break; - case SHLR2_OPCODE: - format = " SHLR2 R%d\n"; - break; - case SHLR8_OPCODE: - format = " SHLR8 R%d\n"; - break; - case SHLR16_OPCODE: - format = " SHLR16 R%d\n"; - break; - case STSPR_OPCODE: - format = " STS PR, R%d\n"; - break; - case STSLPR_OPCODE: - format = " STS.L PR, @-R%d\n"; - break; - case LDS_RM_FPUL_OPCODE: - format = " LDS R%d, FPUL\n"; - break; - case STS_FPUL_RN_OPCODE: - format = " STS FPUL, R%d \n"; - break; - case FLDS_FRM_FPUL_OPCODE: - format = " FLDS FR%d, FPUL\n"; - break; - case FSTS_FPUL_FRN_OPCODE: - format = " FSTS FPUL, R%d \n"; - break; - case LDSFPSCR_OPCODE: - format = " LDS R%d, FPSCR \n"; - break; - case STSFPSCR_OPCODE: - format = " STS FPSCR, R%d \n"; - break; - case STSMACL_OPCODE: - format = " STS MACL, R%d \n"; - break; - case STSMACH_OPCODE: - format = " STS MACH, R%d \n"; - break; - case BSRF_OPCODE: - format = " *BSRF R%d"; - break; - case FTRC_OPCODE: - format = " FTRC FR%d, FPUL\n"; - break; - } - if (format) { - printfStdoutInstr(format, getRn(opc)); - return; - } - switch (opc & 0xf0ff) { - case FNEG_OPCODE: - format = " FNEG DR%d\n"; - break; - case FLOAT_OPCODE: - format = " FLOAT DR%d\n"; - break; - case FTRC_OPCODE: - format = " FTRC FR%d, FPUL\n"; - break; - case FSQRT_OPCODE: - format = " FSQRT FR%d\n"; - break; - case FCNVDS_DRM_FPUL_OPCODE: - format = " FCNVDS FR%d, FPUL\n"; - break; - case FCNVSD_FPUL_DRN_OPCODE: - format = " FCNVSD FPUL, FR%d\n"; - break; - } - if (format) { - if (isdoubleInst) - printfStdoutInstr(format, getDRn(opc) << 1); - else - printfStdoutInstr(format, getRn(opc)); - return; - } - switch (opc & 0xf00f) { - case ADD_OPCODE: - format = " ADD R%d, R%d\n"; - break; - case ADDC_OPCODE: - format = " ADDC R%d, R%d\n"; - break; - case ADDV_OPCODE: - format = " ADDV R%d, R%d\n"; - break; - case AND_OPCODE: - format = " AND R%d, R%d\n"; - break; - case DIV1_OPCODE: - format = " DIV1 R%d, R%d\n"; - break; - case CMPEQ_OPCODE: - format = " CMP/EQ R%d, R%d\n"; - break; - case CMPGE_OPCODE: - format = " CMP/GE R%d, R%d\n"; - break; - case CMPGT_OPCODE: - format = " CMP/GT R%d, R%d\n"; - break; - case CMPHI_OPCODE: - format = " CMP/HI R%d, R%d\n"; - break; - case CMPHS_OPCODE: - format = " CMP/HS R%d, R%d\n"; - break; - case MOV_OPCODE: - format = " MOV R%d, R%d\n"; - break; - case MOVB_WRITE_RN_OPCODE: - format = " MOV.B R%d, @R%d\n"; - break; - case MOVB_WRITE_RNDEC_OPCODE: - format = " MOV.B R%d, @-R%d\n"; - break; - case MOVB_WRITE_R0RN_OPCODE: - format = " MOV.B R%d, @(R0, R%d)\n"; - break; - case MOVB_READ_RM_OPCODE: - format = " MOV.B @R%d, R%d\n"; - break; - case MOVB_READ_RMINC_OPCODE: - format = " MOV.B @R%d+, R%d\n"; - break; - case MOVB_READ_R0RM_OPCODE: - format = " MOV.B @(R0, R%d), R%d\n"; - break; - case MOVL_WRITE_RN_OPCODE: - format = " MOV.L R%d, @R%d\n"; - break; - case MOVL_WRITE_RNDEC_OPCODE: - format = " MOV.L R%d, @-R%d\n"; - break; - case MOVL_WRITE_R0RN_OPCODE: - format = " MOV.L R%d, @(R0, R%d)\n"; - break; - case MOVL_READ_RM_OPCODE: - format = " MOV.L @R%d, R%d\n"; - break; - case MOVL_READ_RMINC_OPCODE: - format = " MOV.L @R%d+, R%d\n"; - break; - case MOVL_READ_R0RM_OPCODE: - format = " MOV.L @(R0, R%d), R%d\n"; - break; - case MULL_OPCODE: - format = " MUL.L R%d, R%d\n"; - break; - case DMULL_L_OPCODE: - format = " DMULU.L R%d, R%d\n"; - break; - case DMULSL_OPCODE: - format = " DMULS.L R%d, R%d\n"; - break; - case NEG_OPCODE: - format = " NEG R%d, R%d\n"; - break; - case NEGC_OPCODE: - format = " NEGC R%d, R%d\n"; - break; - case NOT_OPCODE: - format = " NOT R%d, R%d\n"; - break; - case OR_OPCODE: - format = " OR R%d, R%d\n"; - break; - case SHAD_OPCODE: - format = " SHAD R%d, R%d\n"; - break; - case SHLD_OPCODE: - format = " SHLD R%d, R%d\n"; - break; - case SUB_OPCODE: - format = " SUB R%d, R%d\n"; - break; - case SUBC_OPCODE: - format = " SUBC R%d, R%d\n"; - break; - case SUBV_OPCODE: - format = " SUBV R%d, R%d\n"; - break; - case TST_OPCODE: - format = " TST R%d, R%d\n"; - break; - case XOR_OPCODE: - format = " XOR R%d, R%d\n";break; - case MOVW_WRITE_RN_OPCODE: - format = " MOV.W R%d, @R%d\n"; - break; - case MOVW_READ_RM_OPCODE: - format = " MOV.W @R%d, R%d\n"; - break; - case MOVW_READ_R0RM_OPCODE: - format = " MOV.W @(R0, R%d), R%d\n"; - break; - case EXTUB_OPCODE: - format = " EXTU.B R%d, R%d\n"; - break; - case EXTUW_OPCODE: - format = " EXTU.W R%d, R%d\n"; - break; - } - if (format) { - printfStdoutInstr(format, getRm(opc), getRn(opc)); - return; - } - switch (opc & 0xf00f) { - case FSUB_OPCODE: - format = " FSUB FR%d, FR%d\n"; - break; - case FADD_OPCODE: - format = " FADD FR%d, FR%d\n"; - break; - case FDIV_OPCODE: - format = " FDIV FR%d, FR%d\n"; - break; - case FMUL_OPCODE: - format = " DMULL FR%d, FR%d\n"; - break; - case FMOV_OPCODE: - format = " FMOV FR%d, FR%d\n"; - break; - case FCMPEQ_OPCODE: - format = " FCMP/EQ FR%d, FR%d\n"; - break; - case FCMPGT_OPCODE: - format = " FCMP/GT FR%d, FR%d\n"; - break; - } - if (format) { - if (isdoubleInst) - printfStdoutInstr(format, getDRm(opc) << 1, getDRn(opc) << 1); - else - printfStdoutInstr(format, getRm(opc), getRn(opc)); - return; - } - switch (opc & 0xf00f) { - case FMOVS_WRITE_RN_DEC_OPCODE: - format = " %s FR%d, @-R%d\n"; - break; - case FMOVS_WRITE_RN_OPCODE: - format = " %s FR%d, @R%d\n"; - break; - case FMOVS_WRITE_R0RN_OPCODE: - format = " %s FR%d, @(R0, R%d)\n"; - break; - } - if (format) { - if (isdoubleInst) - printfStdoutInstr(format, "FMOV", getDRm(opc) << 1, getDRn(opc)); - else - printfStdoutInstr(format, "FMOV.S", getRm(opc), getRn(opc)); - return; - } - switch (opc & 0xf00f) { - case FMOVS_READ_RM_OPCODE: - format = " %s @R%d, FR%d\n"; - break; - case FMOVS_READ_RM_INC_OPCODE: - format = " %s @R%d+, FR%d\n"; - break; - case FMOVS_READ_R0RM_OPCODE: - format = " %s @(R0, R%d), FR%d\n"; - break; - } - if (format) { - if (isdoubleInst) - printfStdoutInstr(format, "FMOV", getDRm(opc), getDRn(opc) << 1); - else - printfStdoutInstr(format, "FMOV.S", getRm(opc), getRn(opc)); - return; - } - switch (opc & 0xff00) { - case BF_OPCODE: - format = " BF %d\n"; - break; - case BFS_OPCODE: - format = " *BF/S %d\n"; - break; - case ANDIMM_OPCODE: - format = " AND #%d, R0\n"; - break; - case BT_OPCODE: - format = " BT %d\n"; - break; - case BTS_OPCODE: - format = " *BT/S %d\n"; - break; - case CMPEQIMM_OPCODE: - format = " CMP/EQ #%d, R0\n"; - break; - case MOVB_WRITE_OFFGBR_OPCODE: - format = " MOV.B R0, @(%d, GBR)\n"; - break; - case MOVB_READ_OFFGBR_OPCODE: - format = " MOV.B @(%d, GBR), R0\n"; - break; - case MOVL_WRITE_OFFGBR_OPCODE: - format = " MOV.L R0, @(%d, GBR)\n"; - break; - case MOVL_READ_OFFGBR_OPCODE: - format = " MOV.L @(%d, GBR), R0\n"; - break; - case MOVA_READ_OFFPC_OPCODE: - format = " MOVA @(%d, PC), R0\n"; - break; - case ORIMM_OPCODE: - format = " OR #%d, R0\n"; - break; - case ORBIMM_OPCODE: - format = " OR.B #%d, @(R0, GBR)\n"; - break; - case TSTIMM_OPCODE: - format = " TST #%d, R0\n"; - break; - case TSTB_OPCODE: - format = " TST.B %d, @(R0, GBR)\n"; - break; - case XORIMM_OPCODE: - format = " XOR #%d, R0\n"; - break; - case XORB_OPCODE: - format = " XOR.B %d, @(R0, GBR)\n"; - break; - } - if (format) { - printfStdoutInstr(format, getImm8(opc)); - return; - } - switch (opc & 0xff00) { - case MOVB_WRITE_OFFRN_OPCODE: - format = " MOV.B R0, @(%d, R%d)\n"; - break; - case MOVB_READ_OFFRM_OPCODE: - format = " MOV.B @(%d, R%d), R0\n"; - break; - } - if (format) { - printfStdoutInstr(format, getDisp(opc), getRm(opc)); - return; - } - switch (opc & 0xf000) { - case BRA_OPCODE: - format = " *BRA %d\n"; - break; - case BSR_OPCODE: - format = " *BSR %d\n"; - break; - } - if (format) { - printfStdoutInstr(format, getImm12(opc)); - return; - } - switch (opc & 0xf000) { - case MOVL_READ_OFFPC_OPCODE: - format = " MOV.L @(%d, PC), R%d\n"; - break; - case ADDIMM_OPCODE: - format = " ADD #%d, R%d\n"; - break; - case MOVIMM_OPCODE: - format = " MOV #%d, R%d\n"; - break; - case MOVW_READ_OFFPC_OPCODE: - format = " MOV.W @(%d, PC), R%d\n"; - break; - } - if (format) { - printfStdoutInstr(format, getImm8(opc), getRn(opc)); - return; - } - switch (opc & 0xf000) { - case MOVL_WRITE_OFFRN_OPCODE: - format = " MOV.L R%d, @(%d, R%d)\n"; - printfStdoutInstr(format, getRm(opc), getDisp(opc), getRn(opc)); - break; - case MOVL_READ_OFFRM_OPCODE: - format = " MOV.L @(%d, R%d), R%d\n"; - printfStdoutInstr(format, getDisp(opc), getRm(opc), getRn(opc)); - break; - } - } - - static void printfStdoutInstr(const char* format, ...) - { - if (getenv("JavaScriptCoreDumpJIT")) { - va_list args; - va_start(args, format); - vprintfStdoutInstr(format, args); - va_end(args); - } - } - - static void vprintfStdoutInstr(const char* format, va_list args) - { - if (getenv("JavaScriptCoreDumpJIT")) - WTF::dataLogFV(format, args); - } - - static void printBlockInstr(uint16_t* first, unsigned offset, int nbInstr) - { - printfStdoutInstr(">> repatch instructions after link\n"); - for (int i = 0; i <= nbInstr; i++) - printInstr(*(first + i), offset + i); - printfStdoutInstr(">> end repatch\n"); - } -#else - static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true) { }; - static void printBlockInstr(uint16_t* first, unsigned offset, int nbInstr) { }; -#endif - - static void replaceWithLoad(void* instructionStart) - { - SH4Word* insPtr = reinterpret_cast(instructionStart); - - insPtr += 2; // skip MOV and ADD opcodes - - if (((*insPtr) & 0xf00f) != MOVL_READ_RM_OPCODE) { - *insPtr = MOVL_READ_RM_OPCODE | (*insPtr & 0x0ff0); - cacheFlush(insPtr, sizeof(SH4Word)); - } - } - - static void replaceWithAddressComputation(void* instructionStart) - { - SH4Word* insPtr = reinterpret_cast(instructionStart); - - insPtr += 2; // skip MOV and ADD opcodes - - if (((*insPtr) & 0xf00f) != MOV_OPCODE) { - *insPtr = MOV_OPCODE | (*insPtr & 0x0ff0); - cacheFlush(insPtr, sizeof(SH4Word)); - } - } - -private: - SH4Buffer m_buffer; - int m_claimscratchReg; -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) && CPU(SH4) - -#endif // SH4Assembler_h diff --git a/3rdparty/masm/assembler/X86Assembler.h b/3rdparty/masm/assembler/X86Assembler.h deleted file mode 100644 index 25ff6f0a50..0000000000 --- a/3rdparty/masm/assembler/X86Assembler.h +++ /dev/null @@ -1,2540 +0,0 @@ -/* - * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef X86Assembler_h -#define X86Assembler_h - -#if ENABLE(ASSEMBLER) && (CPU(X86) || CPU(X86_64)) - -#include "AssemblerBuffer.h" -#include "JITCompilationEffort.h" -#include -#include -#include - -namespace JSC { - -inline bool CAN_SIGN_EXTEND_8_32(int32_t value) { return value == (int32_t)(signed char)value; } - -namespace X86Registers { - typedef enum { - eax, - ecx, - edx, - ebx, - esp, - ebp, - esi, - edi, - -#if CPU(X86_64) - r8, - r9, - r10, - r11, - r12, - r13, - r14, - r15, -#endif - } RegisterID; - - typedef enum { - xmm0, - xmm1, - xmm2, - xmm3, - xmm4, - xmm5, - xmm6, - xmm7, - } XMMRegisterID; -} - -class X86Assembler { -public: - typedef X86Registers::RegisterID RegisterID; - typedef X86Registers::XMMRegisterID XMMRegisterID; - typedef XMMRegisterID FPRegisterID; - - typedef enum { - ConditionO, - ConditionNO, - ConditionB, - ConditionAE, - ConditionE, - ConditionNE, - ConditionBE, - ConditionA, - ConditionS, - ConditionNS, - ConditionP, - ConditionNP, - ConditionL, - ConditionGE, - ConditionLE, - ConditionG, - - ConditionC = ConditionB, - ConditionNC = ConditionAE, - } Condition; - -private: - typedef enum { - OP_ADD_EvGv = 0x01, - OP_ADD_GvEv = 0x03, - OP_OR_EvGv = 0x09, - OP_OR_GvEv = 0x0B, - OP_2BYTE_ESCAPE = 0x0F, - OP_AND_EvGv = 0x21, - OP_AND_GvEv = 0x23, - OP_SUB_EvGv = 0x29, - OP_SUB_GvEv = 0x2B, - PRE_PREDICT_BRANCH_NOT_TAKEN = 0x2E, - OP_XOR_EvGv = 0x31, - OP_XOR_GvEv = 0x33, - OP_CMP_EvGv = 0x39, - OP_CMP_GvEv = 0x3B, -#if CPU(X86_64) - PRE_REX = 0x40, -#endif - OP_PUSH_EAX = 0x50, - OP_POP_EAX = 0x58, -#if CPU(X86_64) - OP_MOVSXD_GvEv = 0x63, -#endif - PRE_OPERAND_SIZE = 0x66, - PRE_SSE_66 = 0x66, - OP_PUSH_Iz = 0x68, - OP_IMUL_GvEvIz = 0x69, - OP_GROUP1_EbIb = 0x80, - OP_GROUP1_EvIz = 0x81, - OP_GROUP1_EvIb = 0x83, - OP_TEST_EbGb = 0x84, - OP_TEST_EvGv = 0x85, - OP_XCHG_EvGv = 0x87, - OP_MOV_EbGb = 0x88, - OP_MOV_EvGv = 0x89, - OP_MOV_GvEv = 0x8B, - OP_LEA = 0x8D, - OP_GROUP1A_Ev = 0x8F, - OP_NOP = 0x90, - OP_CDQ = 0x99, - OP_MOV_EAXOv = 0xA1, - OP_MOV_OvEAX = 0xA3, - OP_MOV_EAXIv = 0xB8, - OP_GROUP2_EvIb = 0xC1, - OP_RET = 0xC3, - OP_GROUP11_EvIb = 0xC6, - OP_GROUP11_EvIz = 0xC7, - OP_INT3 = 0xCC, - OP_GROUP2_Ev1 = 0xD1, - OP_GROUP2_EvCL = 0xD3, - OP_ESCAPE_DD = 0xDD, - OP_CALL_rel32 = 0xE8, - OP_JMP_rel32 = 0xE9, - PRE_SSE_F2 = 0xF2, - PRE_SSE_F3 = 0xF3, - OP_HLT = 0xF4, - OP_GROUP3_EbIb = 0xF6, - OP_GROUP3_Ev = 0xF7, - OP_GROUP3_EvIz = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test. - OP_GROUP5_Ev = 0xFF, - } OneByteOpcodeID; - - typedef enum { - OP2_MOVSD_VsdWsd = 0x10, - OP2_MOVSD_WsdVsd = 0x11, - OP2_MOVSS_VsdWsd = 0x10, - OP2_MOVSS_WsdVsd = 0x11, - OP2_CVTSI2SD_VsdEd = 0x2A, - OP2_CVTTSD2SI_GdWsd = 0x2C, - OP2_UCOMISD_VsdWsd = 0x2E, - OP2_ADDSD_VsdWsd = 0x58, - OP2_MULSD_VsdWsd = 0x59, - OP2_CVTSD2SS_VsdWsd = 0x5A, - OP2_CVTSS2SD_VsdWsd = 0x5A, - OP2_SUBSD_VsdWsd = 0x5C, - OP2_DIVSD_VsdWsd = 0x5E, - OP2_SQRTSD_VsdWsd = 0x51, - OP2_ANDNPD_VpdWpd = 0x55, - OP2_XORPD_VpdWpd = 0x57, - OP2_MOVD_VdEd = 0x6E, - OP2_MOVD_EdVd = 0x7E, - OP2_JCC_rel32 = 0x80, - OP_SETCC = 0x90, - OP2_IMUL_GvEv = 0xAF, - OP2_MOVZX_GvEb = 0xB6, - OP2_MOVSX_GvEb = 0xBE, - OP2_MOVZX_GvEw = 0xB7, - OP2_MOVSX_GvEw = 0xBF, - OP2_PEXTRW_GdUdIb = 0xC5, - OP2_PSLLQ_UdqIb = 0x73, - OP2_PSRLQ_UdqIb = 0x73, - OP2_POR_VdqWdq = 0XEB, - } TwoByteOpcodeID; - - TwoByteOpcodeID jccRel32(Condition cond) - { - return (TwoByteOpcodeID)(OP2_JCC_rel32 + cond); - } - - TwoByteOpcodeID setccOpcode(Condition cond) - { - return (TwoByteOpcodeID)(OP_SETCC + cond); - } - - typedef enum { - GROUP1_OP_ADD = 0, - GROUP1_OP_OR = 1, - GROUP1_OP_ADC = 2, - GROUP1_OP_AND = 4, - GROUP1_OP_SUB = 5, - GROUP1_OP_XOR = 6, - GROUP1_OP_CMP = 7, - - GROUP1A_OP_POP = 0, - - GROUP2_OP_ROL = 0, - GROUP2_OP_ROR = 1, - GROUP2_OP_RCL = 2, - GROUP2_OP_RCR = 3, - - GROUP2_OP_SHL = 4, - GROUP2_OP_SHR = 5, - GROUP2_OP_SAR = 7, - - GROUP3_OP_TEST = 0, - GROUP3_OP_NOT = 2, - GROUP3_OP_NEG = 3, - GROUP3_OP_IDIV = 7, - - GROUP5_OP_CALLN = 2, - GROUP5_OP_JMPN = 4, - GROUP5_OP_PUSH = 6, - - GROUP11_MOV = 0, - - GROUP14_OP_PSLLQ = 6, - GROUP14_OP_PSRLQ = 2, - - ESCAPE_DD_FSTP_doubleReal = 3, - } GroupOpcodeID; - - class X86InstructionFormatter; -public: - - X86Assembler() - : m_indexOfLastWatchpoint(INT_MIN) - , m_indexOfTailOfLastWatchpoint(INT_MIN) - { - } - - // Stack operations: - - void push_r(RegisterID reg) - { - m_formatter.oneByteOp(OP_PUSH_EAX, reg); - } - - void pop_r(RegisterID reg) - { - m_formatter.oneByteOp(OP_POP_EAX, reg); - } - - void push_i32(int imm) - { - m_formatter.oneByteOp(OP_PUSH_Iz); - m_formatter.immediate32(imm); - } - - void push_m(int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_PUSH, base, offset); - } - - void pop_m(int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP1A_Ev, GROUP1A_OP_POP, base, offset); - } - - // Arithmetic operations: - -#if !CPU(X86_64) - void adcl_im(int imm, const void* addr) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADC, addr); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADC, addr); - m_formatter.immediate32(imm); - } - } -#endif - - void addl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_ADD_EvGv, src, dst); - } - - void addl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp(OP_ADD_GvEv, dst, base, offset); - } - -#if !CPU(X86_64) - void addl_mr(const void* addr, RegisterID dst) - { - m_formatter.oneByteOp(OP_ADD_GvEv, dst, addr); - } -#endif - - void addl_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_ADD_EvGv, src, base, offset); - } - - void addl_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, dst); - m_formatter.immediate32(imm); - } - } - - void addl_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, offset); - m_formatter.immediate32(imm); - } - } - -#if CPU(X86_64) - void addq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_ADD_EvGv, src, dst); - } - - void addq_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp64(OP_ADD_GvEv, dst, base, offset); - } - - void addq_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_ADD, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_ADD, dst); - m_formatter.immediate32(imm); - } - } - - void addq_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, offset); - m_formatter.immediate32(imm); - } - } -#else - void addl_im(int imm, const void* addr) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, addr); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, addr); - m_formatter.immediate32(imm); - } - } -#endif - - void andl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_AND_EvGv, src, dst); - } - - void andl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp(OP_AND_GvEv, dst, base, offset); - } - - void andl_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_AND_EvGv, src, base, offset); - } - - void andl_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, dst); - m_formatter.immediate32(imm); - } - } - - void andl_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, base, offset); - m_formatter.immediate32(imm); - } - } - -#if CPU(X86_64) - void andq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_AND_EvGv, src, dst); - } - - void andq_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_AND, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_AND, dst); - m_formatter.immediate32(imm); - } - } -#else - void andl_im(int imm, const void* addr) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, addr); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, addr); - m_formatter.immediate32(imm); - } - } -#endif - - void negl_r(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, dst); - } - -#if CPU(X86_64) - void negq_r(RegisterID dst) - { - m_formatter.oneByteOp64(OP_GROUP3_Ev, GROUP3_OP_NEG, dst); - } -#endif - - void negl_m(int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, base, offset); - } - - void notl_r(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NOT, dst); - } - - void notl_m(int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NOT, base, offset); - } - - void orl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_OR_EvGv, src, dst); - } - - void orl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp(OP_OR_GvEv, dst, base, offset); - } - - void orl_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_OR_EvGv, src, base, offset); - } - - void orl_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, dst); - m_formatter.immediate32(imm); - } - } - - void orl_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, base, offset); - m_formatter.immediate32(imm); - } - } - -#if CPU(X86_64) - void orq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_OR_EvGv, src, dst); - } - - void orq_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_OR, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_OR, dst); - m_formatter.immediate32(imm); - } - } -#else - void orl_im(int imm, const void* addr) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, addr); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, addr); - m_formatter.immediate32(imm); - } - } - - void orl_rm(RegisterID src, const void* addr) - { - m_formatter.oneByteOp(OP_OR_EvGv, src, addr); - } -#endif - - void subl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_SUB_EvGv, src, dst); - } - - void subl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp(OP_SUB_GvEv, dst, base, offset); - } - - void subl_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_SUB_EvGv, src, base, offset); - } - - void subl_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, dst); - m_formatter.immediate32(imm); - } - } - - void subl_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, base, offset); - m_formatter.immediate32(imm); - } - } - -#if CPU(X86_64) - void subq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_SUB_EvGv, src, dst); - } - - void subq_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_SUB, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_SUB, dst); - m_formatter.immediate32(imm); - } - } -#else - void subl_im(int imm, const void* addr) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, addr); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, addr); - m_formatter.immediate32(imm); - } - } -#endif - - void xorl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_XOR_EvGv, src, dst); - } - - void xorl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp(OP_XOR_GvEv, dst, base, offset); - } - - void xorl_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_XOR_EvGv, src, base, offset); - } - - void xorl_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_XOR, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_XOR, base, offset); - m_formatter.immediate32(imm); - } - } - - void xorl_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_XOR, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_XOR, dst); - m_formatter.immediate32(imm); - } - } - -#if CPU(X86_64) - void xorq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_XOR_EvGv, src, dst); - } - - void xorq_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_XOR, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_XOR, dst); - m_formatter.immediate32(imm); - } - } - - void xorq_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp64(OP_XOR_EvGv, src, base, offset); - } - - void rorq_i8r(int imm, RegisterID dst) - { - if (imm == 1) - m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_ROR, dst); - else { - m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_ROR, dst); - m_formatter.immediate8(imm); - } - } - -#endif - - void sarl_i8r(int imm, RegisterID dst) - { - if (imm == 1) - m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SAR, dst); - else { - m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SAR, dst); - m_formatter.immediate8(imm); - } - } - - void sarl_CLr(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst); - } - - void shrl_i8r(int imm, RegisterID dst) - { - if (imm == 1) - m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHR, dst); - else { - m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHR, dst); - m_formatter.immediate8(imm); - } - } - - void shrl_CLr(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHR, dst); - } - - void shll_i8r(int imm, RegisterID dst) - { - if (imm == 1) - m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHL, dst); - else { - m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHL, dst); - m_formatter.immediate8(imm); - } - } - - void shll_CLr(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHL, dst); - } - -#if CPU(X86_64) - void sarq_CLr(RegisterID dst) - { - m_formatter.oneByteOp64(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst); - } - - void sarq_i8r(int imm, RegisterID dst) - { - if (imm == 1) - m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_SAR, dst); - else { - m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_SAR, dst); - m_formatter.immediate8(imm); - } - } -#endif - - void imull_rr(RegisterID src, RegisterID dst) - { - m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, src); - } - - void imull_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, base, offset); - } - - void imull_i32r(RegisterID src, int32_t value, RegisterID dst) - { - m_formatter.oneByteOp(OP_IMUL_GvEvIz, dst, src); - m_formatter.immediate32(value); - } - - void idivl_r(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_IDIV, dst); - } - - // Comparisons: - - void cmpl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_CMP_EvGv, src, dst); - } - - void cmpl_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_CMP_EvGv, src, base, offset); - } - - void cmpl_mr(int offset, RegisterID base, RegisterID src) - { - m_formatter.oneByteOp(OP_CMP_GvEv, src, base, offset); - } - - void cmpl_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); - m_formatter.immediate32(imm); - } - } - - void cmpl_ir_force32(int imm, RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); - m_formatter.immediate32(imm); - } - - void cmpl_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); - m_formatter.immediate32(imm); - } - } - - void cmpb_im(int imm, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, offset); - m_formatter.immediate8(imm); - } - - void cmpb_im(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, index, scale, offset); - m_formatter.immediate8(imm); - } - -#if CPU(X86) - void cmpb_im(int imm, const void* addr) - { - m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, addr); - m_formatter.immediate8(imm); - } -#endif - - void cmpl_im(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); - m_formatter.immediate32(imm); - } - } - - void cmpl_im_force32(int imm, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); - m_formatter.immediate32(imm); - } - -#if CPU(X86_64) - void cmpq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_CMP_EvGv, src, dst); - } - - void cmpq_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp64(OP_CMP_EvGv, src, base, offset); - } - - void cmpq_mr(int offset, RegisterID base, RegisterID src) - { - m_formatter.oneByteOp64(OP_CMP_GvEv, src, base, offset); - } - - void cmpq_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); - m_formatter.immediate32(imm); - } - } - - void cmpq_im(int imm, int offset, RegisterID base) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); - m_formatter.immediate32(imm); - } - } - - void cmpq_im(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); - m_formatter.immediate32(imm); - } - } -#else - void cmpl_rm(RegisterID reg, const void* addr) - { - m_formatter.oneByteOp(OP_CMP_EvGv, reg, addr); - } - - void cmpl_im(int imm, const void* addr) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, addr); - m_formatter.immediate8(imm); - } else { - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, addr); - m_formatter.immediate32(imm); - } - } -#endif - - void cmpw_ir(int imm, RegisterID dst) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); - m_formatter.immediate8(imm); - } else { - m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); - m_formatter.immediate16(imm); - } - } - - void cmpw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp(OP_CMP_EvGv, src, base, index, scale, offset); - } - - void cmpw_im(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - if (CAN_SIGN_EXTEND_8_32(imm)) { - m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); - m_formatter.immediate8(imm); - } else { - m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); - m_formatter.immediate16(imm); - } - } - - void testl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_TEST_EvGv, src, dst); - } - - void testl_i32r(int imm, RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, dst); - m_formatter.immediate32(imm); - } - - void testl_i32m(int imm, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, offset); - m_formatter.immediate32(imm); - } - - void testb_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp8(OP_TEST_EbGb, src, dst); - } - - void testb_im(int imm, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, offset); - m_formatter.immediate8(imm); - } - - void testb_im(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, index, scale, offset); - m_formatter.immediate8(imm); - } - -#if CPU(X86) - void testb_im(int imm, const void* addr) - { - m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, addr); - m_formatter.immediate8(imm); - } -#endif - - void testl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset); - m_formatter.immediate32(imm); - } - -#if CPU(X86_64) - void testq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_TEST_EvGv, src, dst); - } - - void testq_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp64(OP_TEST_EvGv, src, base, offset); - } - - void testq_i32r(int imm, RegisterID dst) - { - m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, dst); - m_formatter.immediate32(imm); - } - - void testq_i32m(int imm, int offset, RegisterID base) - { - m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, offset); - m_formatter.immediate32(imm); - } - - void testq_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset); - m_formatter.immediate32(imm); - } -#endif - - void testw_rr(RegisterID src, RegisterID dst) - { - m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp(OP_TEST_EvGv, src, dst); - } - - void testb_i8r(int imm, RegisterID dst) - { - m_formatter.oneByteOp8(OP_GROUP3_EbIb, GROUP3_OP_TEST, dst); - m_formatter.immediate8(imm); - } - - void setCC_r(Condition cond, RegisterID dst) - { - m_formatter.twoByteOp8(setccOpcode(cond), (GroupOpcodeID)0, dst); - } - - void sete_r(RegisterID dst) - { - m_formatter.twoByteOp8(setccOpcode(ConditionE), (GroupOpcodeID)0, dst); - } - - void setz_r(RegisterID dst) - { - sete_r(dst); - } - - void setne_r(RegisterID dst) - { - m_formatter.twoByteOp8(setccOpcode(ConditionNE), (GroupOpcodeID)0, dst); - } - - void setnz_r(RegisterID dst) - { - setne_r(dst); - } - - // Various move ops: - - void cdq() - { - m_formatter.oneByteOp(OP_CDQ); - } - - void fstpl(int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_ESCAPE_DD, ESCAPE_DD_FSTP_doubleReal, base, offset); - } - - void xchgl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_XCHG_EvGv, src, dst); - } - -#if CPU(X86_64) - void xchgq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_XCHG_EvGv, src, dst); - } -#endif - - void movl_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp(OP_MOV_EvGv, src, dst); - } - - void movl_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_MOV_EvGv, src, base, offset); - } - - void movl_rm_disp32(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset); - } - - void movl_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset); - } - - void movl_mEAX(const void* addr) - { - m_formatter.oneByteOp(OP_MOV_EAXOv); -#if CPU(X86_64) - m_formatter.immediate64(reinterpret_cast(addr)); -#else - m_formatter.immediate32(reinterpret_cast(addr)); -#endif - } - - void movl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, offset); - } - - void movl_mr_disp32(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp_disp32(OP_MOV_GvEv, dst, base, offset); - } - - void movl_mr_disp8(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp_disp8(OP_MOV_GvEv, dst, base, offset); - } - - void movl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) - { - m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, index, scale, offset); - } - - void movl_i32r(int imm, RegisterID dst) - { - m_formatter.oneByteOp(OP_MOV_EAXIv, dst); - m_formatter.immediate32(imm); - } - - void movl_i32m(int imm, int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, offset); - m_formatter.immediate32(imm); - } - - void movl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, index, scale, offset); - m_formatter.immediate32(imm); - } - -#if !CPU(X86_64) - void movb_i8m(int imm, const void* addr) - { - ASSERT(-128 <= imm && imm < 128); - m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, addr); - m_formatter.immediate8(imm); - } -#endif - - void movb_i8m(int imm, int offset, RegisterID base) - { - ASSERT(-128 <= imm && imm < 128); - m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, offset); - m_formatter.immediate8(imm); - } - - void movb_i8m(int imm, int offset, RegisterID base, RegisterID index, int scale) - { - ASSERT(-128 <= imm && imm < 128); - m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, index, scale, offset); - m_formatter.immediate8(imm); - } - - void movb_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp8(OP_MOV_EbGb, src, base, index, scale, offset); - } - - void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.prefix(PRE_OPERAND_SIZE); - m_formatter.oneByteOp8(OP_MOV_EvGv, src, base, index, scale, offset); - } - - void movl_EAXm(const void* addr) - { - m_formatter.oneByteOp(OP_MOV_OvEAX); -#if CPU(X86_64) - m_formatter.immediate64(reinterpret_cast(addr)); -#else - m_formatter.immediate32(reinterpret_cast(addr)); -#endif - } - -#if CPU(X86_64) - void movq_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_MOV_EvGv, src, dst); - } - - void movq_rm(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp64(OP_MOV_EvGv, src, base, offset); - } - - void movq_rm_disp32(RegisterID src, int offset, RegisterID base) - { - m_formatter.oneByteOp64_disp32(OP_MOV_EvGv, src, base, offset); - } - - void movq_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.oneByteOp64(OP_MOV_EvGv, src, base, index, scale, offset); - } - - void movq_mEAX(const void* addr) - { - m_formatter.oneByteOp64(OP_MOV_EAXOv); - m_formatter.immediate64(reinterpret_cast(addr)); - } - - void movq_EAXm(const void* addr) - { - m_formatter.oneByteOp64(OP_MOV_OvEAX); - m_formatter.immediate64(reinterpret_cast(addr)); - } - - void movq_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp64(OP_MOV_GvEv, dst, base, offset); - } - - void movq_mr_disp32(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp64_disp32(OP_MOV_GvEv, dst, base, offset); - } - - void movq_mr_disp8(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp64_disp8(OP_MOV_GvEv, dst, base, offset); - } - - void movq_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) - { - m_formatter.oneByteOp64(OP_MOV_GvEv, dst, base, index, scale, offset); - } - - void movq_i32m(int imm, int offset, RegisterID base) - { - m_formatter.oneByteOp64(OP_GROUP11_EvIz, GROUP11_MOV, base, offset); - m_formatter.immediate32(imm); - } - - void movq_i64r(int64_t imm, RegisterID dst) - { - m_formatter.oneByteOp64(OP_MOV_EAXIv, dst); - m_formatter.immediate64(imm); - } - - void movsxd_rr(RegisterID src, RegisterID dst) - { - m_formatter.oneByteOp64(OP_MOVSXD_GvEv, dst, src); - } - - -#else - void movl_rm(RegisterID src, const void* addr) - { - if (src == X86Registers::eax) - movl_EAXm(addr); - else - m_formatter.oneByteOp(OP_MOV_EvGv, src, addr); - } - - void movl_mr(const void* addr, RegisterID dst) - { - if (dst == X86Registers::eax) - movl_mEAX(addr); - else - m_formatter.oneByteOp(OP_MOV_GvEv, dst, addr); - } - - void movl_i32m(int imm, const void* addr) - { - m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, addr); - m_formatter.immediate32(imm); - } -#endif - - void movzwl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, offset); - } - - void movzwl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, index, scale, offset); - } - - void movswl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, offset); - } - - void movswl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, index, scale, offset); - } - - void movzbl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, offset); - } - - void movzbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, index, scale, offset); - } - - void movsbl_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, offset); - } - - void movsbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) - { - m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, index, scale, offset); - } - - void movzbl_rr(RegisterID src, RegisterID dst) - { - // In 64-bit, this may cause an unnecessary REX to be planted (if the dst register - // is in the range ESP-EDI, and the src would not have required a REX). Unneeded - // REX prefixes are defined to be silently ignored by the processor. - m_formatter.twoByteOp8(OP2_MOVZX_GvEb, dst, src); - } - - void leal_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp(OP_LEA, dst, base, offset); - } -#if CPU(X86_64) - void leaq_mr(int offset, RegisterID base, RegisterID dst) - { - m_formatter.oneByteOp64(OP_LEA, dst, base, offset); - } -#endif - - // Flow control: - - AssemblerLabel call() - { - m_formatter.oneByteOp(OP_CALL_rel32); - return m_formatter.immediateRel32(); - } - - AssemblerLabel call(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, dst); - return m_formatter.label(); - } - - void call_m(int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, base, offset); - } - - AssemblerLabel jmp() - { - m_formatter.oneByteOp(OP_JMP_rel32); - return m_formatter.immediateRel32(); - } - - // Return a AssemblerLabel so we have a label to the jump, so we can use this - // To make a tail recursive call on x86-64. The MacroAssembler - // really shouldn't wrap this as a Jump, since it can't be linked. :-/ - AssemblerLabel jmp_r(RegisterID dst) - { - m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, dst); - return m_formatter.label(); - } - - void jmp_m(int offset, RegisterID base) - { - m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, base, offset); - } - -#if !CPU(X86_64) - void jmp_m(const void* address) - { - m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, address); - } -#endif - - AssemblerLabel jne() - { - m_formatter.twoByteOp(jccRel32(ConditionNE)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jnz() - { - return jne(); - } - - AssemblerLabel je() - { - m_formatter.twoByteOp(jccRel32(ConditionE)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jz() - { - return je(); - } - - AssemblerLabel jl() - { - m_formatter.twoByteOp(jccRel32(ConditionL)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jb() - { - m_formatter.twoByteOp(jccRel32(ConditionB)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jle() - { - m_formatter.twoByteOp(jccRel32(ConditionLE)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jbe() - { - m_formatter.twoByteOp(jccRel32(ConditionBE)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jge() - { - m_formatter.twoByteOp(jccRel32(ConditionGE)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jg() - { - m_formatter.twoByteOp(jccRel32(ConditionG)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel ja() - { - m_formatter.twoByteOp(jccRel32(ConditionA)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jae() - { - m_formatter.twoByteOp(jccRel32(ConditionAE)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jo() - { - m_formatter.twoByteOp(jccRel32(ConditionO)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jnp() - { - m_formatter.twoByteOp(jccRel32(ConditionNP)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jp() - { - m_formatter.twoByteOp(jccRel32(ConditionP)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel js() - { - m_formatter.twoByteOp(jccRel32(ConditionS)); - return m_formatter.immediateRel32(); - } - - AssemblerLabel jCC(Condition cond) - { - m_formatter.twoByteOp(jccRel32(cond)); - return m_formatter.immediateRel32(); - } - - // SSE operations: - - void addsd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, (RegisterID)src); - } - - void addsd_mr(int offset, RegisterID base, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, base, offset); - } - -#if !CPU(X86_64) - void addsd_mr(const void* address, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, address); - } -#endif - - void cvtsi2sd_rr(RegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src); - } - - void cvtsi2sd_mr(int offset, RegisterID base, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, offset); - } - -#if !CPU(X86_64) - void cvtsi2sd_mr(const void* address, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, address); - } -#endif - - void cvttsd2si_rr(XMMRegisterID src, RegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src); - } - - void cvtsd2ss_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_CVTSD2SS_VsdWsd, dst, (RegisterID)src); - } - - void cvtss2sd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F3); - m_formatter.twoByteOp(OP2_CVTSS2SD_VsdWsd, dst, (RegisterID)src); - } - -#if CPU(X86_64) - void cvttsd2siq_rr(XMMRegisterID src, RegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp64(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src); - } -#endif - - void movd_rr(XMMRegisterID src, RegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_MOVD_EdVd, (RegisterID)src, dst); - } - - void movd_rr(RegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_MOVD_VdEd, (RegisterID)dst, src); - } - -#if CPU(X86_64) - void movq_rr(XMMRegisterID src, RegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp64(OP2_MOVD_EdVd, (RegisterID)src, dst); - } - - void movq_rr(RegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp64(OP2_MOVD_VdEd, (RegisterID)dst, src); - } -#endif - - void movsd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, (RegisterID)src); - } - - void movsd_rm(XMMRegisterID src, int offset, RegisterID base) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset); - } - - void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset); - } - - void movss_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale) - { - m_formatter.prefix(PRE_SSE_F3); - m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset); - } - - void movsd_mr(int offset, RegisterID base, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset); - } - - void movsd_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, dst, base, index, scale, offset); - } - - void movss_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F3); - m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, dst, base, index, scale, offset); - } - -#if !CPU(X86_64) - void movsd_mr(const void* address, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address); - } - void movsd_rm(XMMRegisterID src, const void* address) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address); - } -#endif - - void mulsd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, (RegisterID)src); - } - - void mulsd_mr(int offset, RegisterID base, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, base, offset); - } - - void pextrw_irr(int whichWord, XMMRegisterID src, RegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_PEXTRW_GdUdIb, (RegisterID)dst, (RegisterID)src); - m_formatter.immediate8(whichWord); - } - - void psllq_i8r(int imm, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp8(OP2_PSLLQ_UdqIb, GROUP14_OP_PSLLQ, (RegisterID)dst); - m_formatter.immediate8(imm); - } - - void psrlq_i8r(int imm, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp8(OP2_PSRLQ_UdqIb, GROUP14_OP_PSRLQ, (RegisterID)dst); - m_formatter.immediate8(imm); - } - - void por_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_POR_VdqWdq, (RegisterID)dst, (RegisterID)src); - } - - void subsd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, (RegisterID)src); - } - - void subsd_mr(int offset, RegisterID base, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, base, offset); - } - - void ucomisd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, (RegisterID)src); - } - - void ucomisd_mr(int offset, RegisterID base, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, base, offset); - } - - void divsd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, (RegisterID)src); - } - - void divsd_mr(int offset, RegisterID base, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, base, offset); - } - - void xorpd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src); - } - - void andnpd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_66); - m_formatter.twoByteOp(OP2_ANDNPD_VpdWpd, (RegisterID)dst, (RegisterID)src); - } - - void sqrtsd_rr(XMMRegisterID src, XMMRegisterID dst) - { - m_formatter.prefix(PRE_SSE_F2); - m_formatter.twoByteOp(OP2_SQRTSD_VsdWsd, (RegisterID)dst, (RegisterID)src); - } - - // Misc instructions: - - void int3() - { - m_formatter.oneByteOp(OP_INT3); - } - - void ret() - { - m_formatter.oneByteOp(OP_RET); - } - - void predictNotTaken() - { - m_formatter.prefix(PRE_PREDICT_BRANCH_NOT_TAKEN); - } - - // Assembler admin methods: - - size_t codeSize() const - { - return m_formatter.codeSize(); - } - - AssemblerLabel labelForWatchpoint() - { - AssemblerLabel result = m_formatter.label(); - if (static_cast(result.m_offset) != m_indexOfLastWatchpoint) - result = label(); - m_indexOfLastWatchpoint = result.m_offset; - m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); - return result; - } - - AssemblerLabel labelIgnoringWatchpoints() - { - return m_formatter.label(); - } - - AssemblerLabel label() - { - AssemblerLabel result = m_formatter.label(); - while (UNLIKELY(static_cast(result.m_offset) < m_indexOfTailOfLastWatchpoint)) { - nop(); - result = m_formatter.label(); - } - return result; - } - - AssemblerLabel align(int alignment) - { - while (!m_formatter.isAligned(alignment)) - m_formatter.oneByteOp(OP_HLT); - - return label(); - } - - // Linking & patching: - // - // 'link' and 'patch' methods are for use on unprotected code - such as the code - // within the AssemblerBuffer, and code being patched by the patch buffer. Once - // code has been finalized it is (platform support permitting) within a non- - // writable region of memory; to modify the code in an execute-only execuable - // pool the 'repatch' and 'relink' methods should be used. - - void linkJump(AssemblerLabel from, AssemblerLabel to) - { - ASSERT(from.isSet()); - ASSERT(to.isSet()); - - char* code = reinterpret_cast(m_formatter.data()); - ASSERT(!reinterpret_cast(code + from.m_offset)[-1]); - setRel32(code + from.m_offset, code + to.m_offset); - } - - static void linkJump(void* code, AssemblerLabel from, void* to) - { - ASSERT(from.isSet()); - - setRel32(reinterpret_cast(code) + from.m_offset, to); - } - - static void linkCall(void* code, AssemblerLabel from, void* to) - { - ASSERT(from.isSet()); - - setRel32(reinterpret_cast(code) + from.m_offset, to); - } - - static void linkPointer(void* code, AssemblerLabel where, void* value) - { - ASSERT(where.isSet()); - - setPointer(reinterpret_cast(code) + where.m_offset, value); - } - - static void relinkJump(void* from, void* to) - { - setRel32(from, to); - } - - static void relinkCall(void* from, void* to) - { - setRel32(from, to); - } - - static void repatchCompact(void* where, int32_t value) - { - ASSERT(value >= std::numeric_limits::min()); - ASSERT(value <= std::numeric_limits::max()); - setInt8(where, value); - } - - static void repatchInt32(void* where, int32_t value) - { - setInt32(where, value); - } - - static void repatchPointer(void* where, void* value) - { - setPointer(where, value); - } - - static void* readPointer(void* where) - { - return reinterpret_cast(where)[-1]; - } - - static void replaceWithJump(void* instructionStart, void* to) - { - uint8_t* ptr = reinterpret_cast(instructionStart); - uint8_t* dstPtr = reinterpret_cast(to); - intptr_t distance = (intptr_t)(dstPtr - (ptr + 5)); - ptr[0] = static_cast(OP_JMP_rel32); - *reinterpret_cast(ptr + 1) = static_cast(distance); - } - - static ptrdiff_t maxJumpReplacementSize() - { - return 5; - } - -#if CPU(X86_64) - static void revertJumpTo_movq_i64r(void* instructionStart, int64_t imm, RegisterID dst) - { - const int rexBytes = 1; - const int opcodeBytes = 1; - ASSERT(rexBytes + opcodeBytes <= maxJumpReplacementSize()); - uint8_t* ptr = reinterpret_cast(instructionStart); - ptr[0] = PRE_REX | (1 << 3) | (dst >> 3); - ptr[1] = OP_MOV_EAXIv | (dst & 7); - - union { - uint64_t asWord; - uint8_t asBytes[8]; - } u; - u.asWord = imm; - for (unsigned i = rexBytes + opcodeBytes; i < static_cast(maxJumpReplacementSize()); ++i) - ptr[i] = u.asBytes[i - rexBytes - opcodeBytes]; - } -#endif - - static void revertJumpTo_cmpl_ir_force32(void* instructionStart, int32_t imm, RegisterID dst) - { - const int opcodeBytes = 1; - const int modRMBytes = 1; - ASSERT(opcodeBytes + modRMBytes <= maxJumpReplacementSize()); - uint8_t* ptr = reinterpret_cast(instructionStart); - ptr[0] = OP_GROUP1_EvIz; - ptr[1] = (X86InstructionFormatter::ModRmRegister << 6) | (GROUP1_OP_CMP << 3) | dst; - union { - uint32_t asWord; - uint8_t asBytes[4]; - } u; - u.asWord = imm; - for (unsigned i = opcodeBytes + modRMBytes; i < static_cast(maxJumpReplacementSize()); ++i) - ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes]; - } - - static void revertJumpTo_cmpl_im_force32(void* instructionStart, int32_t imm, int offset, RegisterID dst) - { - ASSERT_UNUSED(offset, !offset); - const int opcodeBytes = 1; - const int modRMBytes = 1; - ASSERT(opcodeBytes + modRMBytes <= maxJumpReplacementSize()); - uint8_t* ptr = reinterpret_cast(instructionStart); - ptr[0] = OP_GROUP1_EvIz; - ptr[1] = (X86InstructionFormatter::ModRmMemoryNoDisp << 6) | (GROUP1_OP_CMP << 3) | dst; - union { - uint32_t asWord; - uint8_t asBytes[4]; - } u; - u.asWord = imm; - for (unsigned i = opcodeBytes + modRMBytes; i < static_cast(maxJumpReplacementSize()); ++i) - ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes]; - } - - static void replaceWithLoad(void* instructionStart) - { - uint8_t* ptr = reinterpret_cast(instructionStart); -#if CPU(X86_64) - if ((*ptr & ~15) == PRE_REX) - ptr++; -#endif - switch (*ptr) { - case OP_MOV_GvEv: - break; - case OP_LEA: - *ptr = OP_MOV_GvEv; - break; - default: - ASSERT_NOT_REACHED(); - } - } - - static void replaceWithAddressComputation(void* instructionStart) - { - uint8_t* ptr = reinterpret_cast(instructionStart); -#if CPU(X86_64) - if ((*ptr & ~15) == PRE_REX) - ptr++; -#endif - switch (*ptr) { - case OP_MOV_GvEv: - *ptr = OP_LEA; - break; - case OP_LEA: - break; - default: - ASSERT_NOT_REACHED(); - } - } - - static unsigned getCallReturnOffset(AssemblerLabel call) - { - ASSERT(call.isSet()); - return call.m_offset; - } - - static void* getRelocatedAddress(void* code, AssemblerLabel label) - { - ASSERT(label.isSet()); - return reinterpret_cast(reinterpret_cast(code) + label.m_offset); - } - - static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) - { - return b.m_offset - a.m_offset; - } - - PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) - { - return m_formatter.executableCopy(globalData, ownerUID, effort); - } - - unsigned debugOffset() { return m_formatter.debugOffset(); } - - void nop() - { - m_formatter.oneByteOp(OP_NOP); - } - - // This is a no-op on x86 - ALWAYS_INLINE static void cacheFlush(void*, size_t) { } - -private: - - static void setPointer(void* where, void* value) - { - reinterpret_cast(where)[-1] = value; - } - - static void setInt32(void* where, int32_t value) - { - reinterpret_cast(where)[-1] = value; - } - - static void setInt8(void* where, int8_t value) - { - reinterpret_cast(where)[-1] = value; - } - - static void setRel32(void* from, void* to) - { - intptr_t offset = reinterpret_cast(to) - reinterpret_cast(from); - ASSERT(offset == static_cast(offset)); - - setInt32(from, offset); - } - - class X86InstructionFormatter { - - static const int maxInstructionSize = 16; - - public: - - enum ModRmMode { - ModRmMemoryNoDisp, - ModRmMemoryDisp8, - ModRmMemoryDisp32, - ModRmRegister, - }; - - // Legacy prefix bytes: - // - // These are emmitted prior to the instruction. - - void prefix(OneByteOpcodeID pre) - { - m_buffer.putByte(pre); - } - - // Word-sized operands / no operand instruction formatters. - // - // In addition to the opcode, the following operand permutations are supported: - // * None - instruction takes no operands. - // * One register - the low three bits of the RegisterID are added into the opcode. - // * Two registers - encode a register form ModRm (for all ModRm formats, the reg field is passed first, and a GroupOpcodeID may be passed in its place). - // * Three argument ModRM - a register, and a register and an offset describing a memory operand. - // * Five argument ModRM - a register, and a base register, an index, scale, and offset describing a memory operand. - // - // For 32-bit x86 targets, the address operand may also be provided as a void*. - // On 64-bit targets REX prefixes will be planted as necessary, where high numbered registers are used. - // - // The twoByteOp methods plant two-byte Intel instructions sequences (first opcode byte 0x0F). - - void oneByteOp(OneByteOpcodeID opcode) - { - m_buffer.ensureSpace(maxInstructionSize); - m_buffer.putByteUnchecked(opcode); - } - - void oneByteOp(OneByteOpcodeID opcode, RegisterID reg) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(0, 0, reg); - m_buffer.putByteUnchecked(opcode + (reg & 7)); - } - - void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, 0, rm); - m_buffer.putByteUnchecked(opcode); - registerModRM(reg, rm); - } - - void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, 0, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, base, offset); - } - - void oneByteOp_disp32(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, 0, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM_disp32(reg, base, offset); - } - - void oneByteOp_disp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, 0, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM_disp8(reg, base, offset); - } - - void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, index, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, base, index, scale, offset); - } - -#if !CPU(X86_64) - void oneByteOp(OneByteOpcodeID opcode, int reg, const void* address) - { - m_buffer.ensureSpace(maxInstructionSize); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, address); - } -#endif - - void twoByteOp(TwoByteOpcodeID opcode) - { - m_buffer.ensureSpace(maxInstructionSize); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - } - - void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, 0, rm); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - registerModRM(reg, rm); - } - - void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID base, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, 0, base); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, base, offset); - } - - void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIfNeeded(reg, index, base); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, base, index, scale, offset); - } - -#if !CPU(X86_64) - void twoByteOp(TwoByteOpcodeID opcode, int reg, const void* address) - { - m_buffer.ensureSpace(maxInstructionSize); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, address); - } -#endif - -#if CPU(X86_64) - // Quad-word-sized operands: - // - // Used to format 64-bit operantions, planting a REX.w prefix. - // When planting d64 or f64 instructions, not requiring a REX.w prefix, - // the normal (non-'64'-postfixed) formatters should be used. - - void oneByteOp64(OneByteOpcodeID opcode) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(0, 0, 0); - m_buffer.putByteUnchecked(opcode); - } - - void oneByteOp64(OneByteOpcodeID opcode, RegisterID reg) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(0, 0, reg); - m_buffer.putByteUnchecked(opcode + (reg & 7)); - } - - void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(reg, 0, rm); - m_buffer.putByteUnchecked(opcode); - registerModRM(reg, rm); - } - - void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(reg, 0, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, base, offset); - } - - void oneByteOp64_disp32(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(reg, 0, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM_disp32(reg, base, offset); - } - - void oneByteOp64_disp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(reg, 0, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM_disp8(reg, base, offset); - } - - void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(reg, index, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, base, index, scale, offset); - } - - void twoByteOp64(TwoByteOpcodeID opcode, int reg, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexW(reg, 0, rm); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - registerModRM(reg, rm); - } -#endif - - // Byte-operands: - // - // These methods format byte operations. Byte operations differ from the normal - // formatters in the circumstances under which they will decide to emit REX prefixes. - // These should be used where any register operand signifies a byte register. - // - // The disctinction is due to the handling of register numbers in the range 4..7 on - // x86-64. These register numbers may either represent the second byte of the first - // four registers (ah..bh) or the first byte of the second four registers (spl..dil). - // - // Since ah..bh cannot be used in all permutations of operands (specifically cannot - // be accessed where a REX prefix is present), these are likely best treated as - // deprecated. In order to ensure the correct registers spl..dil are selected a - // REX prefix will be emitted for any byte register operand in the range 4..15. - // - // These formatters may be used in instructions where a mix of operand sizes, in which - // case an unnecessary REX will be emitted, for example: - // movzbl %al, %edi - // In this case a REX will be planted since edi is 7 (and were this a byte operand - // a REX would be required to specify dil instead of bh). Unneeded REX prefixes will - // be silently ignored by the processor. - // - // Address operands should still be checked using regRequiresRex(), while byteRegRequiresRex() - // is provided to check byte register operands. - - void oneByteOp8(OneByteOpcodeID opcode, GroupOpcodeID groupOp, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(byteRegRequiresRex(rm), 0, 0, rm); - m_buffer.putByteUnchecked(opcode); - registerModRM(groupOp, rm); - } - - void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(byteRegRequiresRex(reg) || byteRegRequiresRex(rm), reg, 0, rm); - m_buffer.putByteUnchecked(opcode); - registerModRM(reg, rm); - } - - void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(byteRegRequiresRex(reg) || regRequiresRex(index) || regRequiresRex(base), reg, index, base); - m_buffer.putByteUnchecked(opcode); - memoryModRM(reg, base, index, scale, offset); - } - - void twoByteOp8(TwoByteOpcodeID opcode, RegisterID reg, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(byteRegRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - registerModRM(reg, rm); - } - - void twoByteOp8(TwoByteOpcodeID opcode, GroupOpcodeID groupOp, RegisterID rm) - { - m_buffer.ensureSpace(maxInstructionSize); - emitRexIf(byteRegRequiresRex(rm), 0, 0, rm); - m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); - m_buffer.putByteUnchecked(opcode); - registerModRM(groupOp, rm); - } - - // Immediates: - // - // An immedaite should be appended where appropriate after an op has been emitted. - // The writes are unchecked since the opcode formatters above will have ensured space. - - void immediate8(int imm) - { - m_buffer.putByteUnchecked(imm); - } - - void immediate16(int imm) - { - m_buffer.putShortUnchecked(imm); - } - - void immediate32(int imm) - { - m_buffer.putIntUnchecked(imm); - } - - void immediate64(int64_t imm) - { - m_buffer.putInt64Unchecked(imm); - } - - AssemblerLabel immediateRel32() - { - m_buffer.putIntUnchecked(0); - return label(); - } - - // Administrative methods: - - size_t codeSize() const { return m_buffer.codeSize(); } - AssemblerLabel label() const { return m_buffer.label(); } - bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); } - void* data() const { return m_buffer.data(); } - - PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) - { - return m_buffer.executableCopy(globalData, ownerUID, effort); - } - - unsigned debugOffset() { return m_buffer.debugOffset(); } - - private: - - // Internals; ModRm and REX formatters. - - static const RegisterID noBase = X86Registers::ebp; - static const RegisterID hasSib = X86Registers::esp; - static const RegisterID noIndex = X86Registers::esp; -#if CPU(X86_64) - static const RegisterID noBase2 = X86Registers::r13; - static const RegisterID hasSib2 = X86Registers::r12; - - // Registers r8 & above require a REX prefixe. - inline bool regRequiresRex(int reg) - { - return (reg >= X86Registers::r8); - } - - // Byte operand register spl & above require a REX prefix (to prevent the 'H' registers be accessed). - inline bool byteRegRequiresRex(int reg) - { - return (reg >= X86Registers::esp); - } - - // Format a REX prefix byte. - inline void emitRex(bool w, int r, int x, int b) - { - ASSERT(r >= 0); - ASSERT(x >= 0); - ASSERT(b >= 0); - m_buffer.putByteUnchecked(PRE_REX | ((int)w << 3) | ((r>>3)<<2) | ((x>>3)<<1) | (b>>3)); - } - - // Used to plant a REX byte with REX.w set (for 64-bit operations). - inline void emitRexW(int r, int x, int b) - { - emitRex(true, r, x, b); - } - - // Used for operations with byte operands - use byteRegRequiresRex() to check register operands, - // regRequiresRex() to check other registers (i.e. address base & index). - inline void emitRexIf(bool condition, int r, int x, int b) - { - if (condition) emitRex(false, r, x, b); - } - - // Used for word sized operations, will plant a REX prefix if necessary (if any register is r8 or above). - inline void emitRexIfNeeded(int r, int x, int b) - { - emitRexIf(regRequiresRex(r) || regRequiresRex(x) || regRequiresRex(b), r, x, b); - } -#else - // No REX prefix bytes on 32-bit x86. - inline bool regRequiresRex(int) { return false; } - inline bool byteRegRequiresRex(int) { return false; } - inline void emitRexIf(bool, int, int, int) {} - inline void emitRexIfNeeded(int, int, int) {} -#endif - - void putModRm(ModRmMode mode, int reg, RegisterID rm) - { - m_buffer.putByteUnchecked((mode << 6) | ((reg & 7) << 3) | (rm & 7)); - } - - void putModRmSib(ModRmMode mode, int reg, RegisterID base, RegisterID index, int scale) - { - ASSERT(mode != ModRmRegister); - - putModRm(mode, reg, hasSib); - m_buffer.putByteUnchecked((scale << 6) | ((index & 7) << 3) | (base & 7)); - } - - void registerModRM(int reg, RegisterID rm) - { - putModRm(ModRmRegister, reg, rm); - } - - void memoryModRM(int reg, RegisterID base, int offset) - { - // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. -#if CPU(X86_64) - if ((base == hasSib) || (base == hasSib2)) { -#else - if (base == hasSib) { -#endif - if (!offset) // No need to check if the base is noBase, since we know it is hasSib! - putModRmSib(ModRmMemoryNoDisp, reg, base, noIndex, 0); - else if (CAN_SIGN_EXTEND_8_32(offset)) { - putModRmSib(ModRmMemoryDisp8, reg, base, noIndex, 0); - m_buffer.putByteUnchecked(offset); - } else { - putModRmSib(ModRmMemoryDisp32, reg, base, noIndex, 0); - m_buffer.putIntUnchecked(offset); - } - } else { -#if CPU(X86_64) - if (!offset && (base != noBase) && (base != noBase2)) -#else - if (!offset && (base != noBase)) -#endif - putModRm(ModRmMemoryNoDisp, reg, base); - else if (CAN_SIGN_EXTEND_8_32(offset)) { - putModRm(ModRmMemoryDisp8, reg, base); - m_buffer.putByteUnchecked(offset); - } else { - putModRm(ModRmMemoryDisp32, reg, base); - m_buffer.putIntUnchecked(offset); - } - } - } - - void memoryModRM_disp8(int reg, RegisterID base, int offset) - { - // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. - ASSERT(CAN_SIGN_EXTEND_8_32(offset)); -#if CPU(X86_64) - if ((base == hasSib) || (base == hasSib2)) { -#else - if (base == hasSib) { -#endif - putModRmSib(ModRmMemoryDisp8, reg, base, noIndex, 0); - m_buffer.putByteUnchecked(offset); - } else { - putModRm(ModRmMemoryDisp8, reg, base); - m_buffer.putByteUnchecked(offset); - } - } - - void memoryModRM_disp32(int reg, RegisterID base, int offset) - { - // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. -#if CPU(X86_64) - if ((base == hasSib) || (base == hasSib2)) { -#else - if (base == hasSib) { -#endif - putModRmSib(ModRmMemoryDisp32, reg, base, noIndex, 0); - m_buffer.putIntUnchecked(offset); - } else { - putModRm(ModRmMemoryDisp32, reg, base); - m_buffer.putIntUnchecked(offset); - } - } - - void memoryModRM(int reg, RegisterID base, RegisterID index, int scale, int offset) - { - ASSERT(index != noIndex); - -#if CPU(X86_64) - if (!offset && (base != noBase) && (base != noBase2)) -#else - if (!offset && (base != noBase)) -#endif - putModRmSib(ModRmMemoryNoDisp, reg, base, index, scale); - else if (CAN_SIGN_EXTEND_8_32(offset)) { - putModRmSib(ModRmMemoryDisp8, reg, base, index, scale); - m_buffer.putByteUnchecked(offset); - } else { - putModRmSib(ModRmMemoryDisp32, reg, base, index, scale); - m_buffer.putIntUnchecked(offset); - } - } - -#if !CPU(X86_64) - void memoryModRM(int reg, const void* address) - { - // noBase + ModRmMemoryNoDisp means noBase + ModRmMemoryDisp32! - putModRm(ModRmMemoryNoDisp, reg, noBase); - m_buffer.putIntUnchecked(reinterpret_cast(address)); - } -#endif - - AssemblerBuffer m_buffer; - } m_formatter; - int m_indexOfLastWatchpoint; - int m_indexOfTailOfLastWatchpoint; -}; - -} // namespace JSC - -#endif // ENABLE(ASSEMBLER) && CPU(X86) - -#endif // X86Assembler_h diff --git a/3rdparty/masm/config.h b/3rdparty/masm/config.h deleted file mode 100644 index 5f59f311e3..0000000000 --- a/3rdparty/masm/config.h +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef MASM_CONFIG_H -#define MASM_CONFIG_H - -#include -#ifdef __cplusplus -#include -#include -#include -#include -#else -#include -#endif -#include - -#endif // MASM_CONFIG_H diff --git a/3rdparty/masm/create_regex_tables b/3rdparty/masm/create_regex_tables deleted file mode 100644 index bd799ba044..0000000000 --- a/3rdparty/masm/create_regex_tables +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright (C) 2010 Apple Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import sys - -types = { - "wordchar": { "UseTable" : True, "data": ['_', ('0','9'), ('A', 'Z'), ('a','z')]}, - "nonwordchar": { "UseTable" : True, "Inverse": "wordchar", "data": ['`', (0, ord('0') - 1), (ord('9') + 1, ord('A') - 1), (ord('Z') + 1, ord('_') - 1), (ord('z') + 1, 0xffff)]}, - "newline": { "UseTable" : False, "data": ['\n', '\r', 0x2028, 0x2029]}, - "spaces": { "UseTable" : True, "data": [' ', ('\t', '\r'), 0xa0, 0x1680, 0x180e, 0x2028, 0x2029, 0x202f, 0x205f, 0x3000, (0x2000, 0x200a), 0xfeff]}, - "nonspaces": { "UseTable" : True, "Inverse": "spaces", "data": [(0, ord('\t') - 1), (ord('\r') + 1, ord(' ') - 1), (ord(' ') + 1, 0x009f), (0x00a1, 0x167f), (0x1681, 0x180d), (0x180f, 0x1fff), (0x200b, 0x2027), (0x202a, 0x202e), (0x2030, 0x205e), (0x2060, 0x2fff), (0x3001, 0xfefe), (0xff00, 0xffff)]}, - "digits": { "UseTable" : False, "data": [('0', '9')]}, - "nondigits": { "UseTable" : False, "Inverse": "digits", "data": [(0, ord('0') - 1), (ord('9') + 1, 0xffff)] } -} -entriesPerLine = 50 -arrays = ""; -functions = ""; -emitTables = (len(sys.argv) < 2 or sys.argv[1] != "--no-tables") - -for name, classes in types.items(): - ranges = []; - size = 0; - for _class in classes["data"]: - if type(_class) == str: - ranges.append((ord(_class), ord(_class))) - elif type(_class) == int: - ranges.append((_class, _class)) - else: - (min, max) = _class; - if type(min) == str: - min = ord(min) - if type(max) == str: - max = ord(max) - if max > 0x7f and min <= 0x7f: - ranges.append((min, 0x7f)) - min = 0x80 - ranges.append((min,max)) - ranges.sort(); - - if emitTables and classes["UseTable"] and (not "Inverse" in classes): - array = ("static const char _%sData[65536] = {\n" % name); - i = 0 - for (min,max) in ranges: - while i < min: - i = i + 1 - array += ('0,') - if (i % entriesPerLine == 0) and (i != 0): - array += ('\n') - while i <= max: - i = i + 1 - if (i == 65536): - array += ("1") - else: - array += ('1,') - if (i % entriesPerLine == 0) and (i != 0): - array += ('\n') - while i < 0xffff: - array += ("0,") - i = i + 1; - if (i % entriesPerLine == 0) and (i != 0): - array += ('\n') - if i == 0xffff: - array += ("0") - array += ("\n};\n\n"); - arrays += array - - # Generate createFunction: - function = ""; - function += ("CharacterClass* %sCreate()\n" % name) - function += ("{\n") - if emitTables and classes["UseTable"]: - if "Inverse" in classes: - function += (" CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_%sData, true));\n" % (classes["Inverse"])) - else: - function += (" CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_%sData, false));\n" % (name)) - else: - function += (" CharacterClass* characterClass = new CharacterClass(0);\n") - for (min, max) in ranges: - if (min == max): - if (min > 127): - function += (" characterClass->m_matchesUnicode.append(0x%04x);\n" % min) - else: - function += (" characterClass->m_matches.append(0x%02x);\n" % min) - continue - if (min > 127) or (max > 127): - function += (" characterClass->m_rangesUnicode.append(CharacterRange(0x%04x, 0x%04x));\n" % (min, max)) - else: - function += (" characterClass->m_ranges.append(CharacterRange(0x%02x, 0x%02x));\n" % (min, max)) - function += (" return characterClass;\n") - function += ("}\n\n") - functions += function - -if (len(sys.argv) > 1): - f = open(sys.argv[-1], "w") - f.write(arrays) - f.write(functions) - f.close() -else: - print(arrays) - print(functions) - diff --git a/3rdparty/masm/disassembler/Disassembler.cpp b/3rdparty/masm/disassembler/Disassembler.cpp deleted file mode 100644 index 3fed2cdab8..0000000000 --- a/3rdparty/masm/disassembler/Disassembler.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "Disassembler.h" - -#include "MacroAssemblerCodeRef.h" -#include - -namespace JSC { - -void disassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const char* prefix, PrintStream& out) -{ - if (tryToDisassemble(codePtr, size, prefix, out)) - return; - - out.printf("%sdisassembly not available for range %p...%p\n", prefix, codePtr.executableAddress(), static_cast(codePtr.executableAddress()) + size); -} - -} // namespace JSC - diff --git a/3rdparty/masm/disassembler/Disassembler.h b/3rdparty/masm/disassembler/Disassembler.h deleted file mode 100644 index a087a657b3..0000000000 --- a/3rdparty/masm/disassembler/Disassembler.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef Disassembler_h -#define Disassembler_h - -#include -#include - -namespace JSC { - -class MacroAssemblerCodePtr; - -#if ENABLE(DISASSEMBLER) -bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, PrintStream&); -#else -inline bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char*, PrintStream&) -{ - return false; -} -#endif - -// Prints either the disassembly, or a line of text indicating that disassembly failed and -// the range of machine code addresses. -void disassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, PrintStream& out); - -} // namespace JSC - -#endif // Disassembler_h - diff --git a/3rdparty/masm/disassembler/UDis86Disassembler.cpp b/3rdparty/masm/disassembler/UDis86Disassembler.cpp deleted file mode 100644 index 63c235b920..0000000000 --- a/3rdparty/masm/disassembler/UDis86Disassembler.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "Disassembler.h" - -#if USE(UDIS86) - -#include "MacroAssemblerCodeRef.h" -#include "udis86.h" - -namespace JSC { - -bool tryToDisassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const char* prefix, PrintStream& out) -{ - ud_t disassembler; - ud_init(&disassembler); - ud_set_input_buffer(&disassembler, static_cast(codePtr.executableAddress()), size); -#if CPU(X86_64) - ud_set_mode(&disassembler, 64); -#else - ud_set_mode(&disassembler, 32); -#endif - ud_set_pc(&disassembler, bitwise_cast(codePtr.executableAddress())); - ud_set_syntax(&disassembler, UD_SYN_ATT); - - uint64_t currentPC = disassembler.pc; - while (ud_disassemble(&disassembler)) { - char pcString[20]; - snprintf(pcString, sizeof(pcString), "0x%lx", static_cast(currentPC)); - out.printf("%s%16s: %s\n", prefix, pcString, ud_insn_asm(&disassembler)); - currentPC = disassembler.pc; - } - - return true; -} - -} // namespace JSC - -#endif // USE(UDIS86) - diff --git a/3rdparty/masm/disassembler/udis86/differences.txt b/3rdparty/masm/disassembler/udis86/differences.txt deleted file mode 100644 index dc225b6ffe..0000000000 --- a/3rdparty/masm/disassembler/udis86/differences.txt +++ /dev/null @@ -1,24 +0,0 @@ -This documents the differences between the stock version of udis86 and the one found -here: - -- All files not named "udis86" were prefixed with "udis86". - -- assert() has been changed to ASSERT() - -- Mass rename of udis86_input.h inp_ prefixed functions and macros to ud_inp_ to - avoid namespace pollution. - -- Removal of KERNEL checks. - -- Added #include of udis86_extern.h in udis86_decode.c. - -- Removed s_ie__pause and s_ie__nop from udis86_decode.c, since they weren't used. - -- Made udis86_syn.h use WTF_ATTRIBUTE_PRINTF. This required making a bunch of little - fixes to make the compiler's format string warnings go away. - -- Made the code in udis86_syn.h use vsnprintf() instead of vsprintf(). - -- Fixed udis86_syn-att.c's jump destination printing to work correctly in 64-bit mode. - -- Add --outputDir option to itab.py. diff --git a/3rdparty/masm/disassembler/udis86/itab.py b/3rdparty/masm/disassembler/udis86/itab.py deleted file mode 100644 index 07e20a6e10..0000000000 --- a/3rdparty/masm/disassembler/udis86/itab.py +++ /dev/null @@ -1,360 +0,0 @@ -# udis86 - scripts/itab.py -# -# Copyright (c) 2009 Vivek Thampi -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from optparse import OptionParser -import os -import sys - -sys.path.append( '../scripts' ); - -import ud_optable -import ud_opcode - -class UdItabGenerator( ud_opcode.UdOpcodeTables ): - - OperandDict = { - "Ap" : [ "OP_A" , "SZ_P" ], - "E" : [ "OP_E" , "SZ_NA" ], - "Eb" : [ "OP_E" , "SZ_B" ], - "Ew" : [ "OP_E" , "SZ_W" ], - "Ev" : [ "OP_E" , "SZ_V" ], - "Ed" : [ "OP_E" , "SZ_D" ], - "Eq" : [ "OP_E" , "SZ_Q" ], - "Ez" : [ "OP_E" , "SZ_Z" ], - "Ex" : [ "OP_E" , "SZ_MDQ" ], - "Ep" : [ "OP_E" , "SZ_P" ], - "G" : [ "OP_G" , "SZ_NA" ], - "Gb" : [ "OP_G" , "SZ_B" ], - "Gw" : [ "OP_G" , "SZ_W" ], - "Gv" : [ "OP_G" , "SZ_V" ], - "Gy" : [ "OP_G" , "SZ_MDQ" ], - "Gy" : [ "OP_G" , "SZ_MDQ" ], - "Gd" : [ "OP_G" , "SZ_D" ], - "Gq" : [ "OP_G" , "SZ_Q" ], - "Gx" : [ "OP_G" , "SZ_MDQ" ], - "Gz" : [ "OP_G" , "SZ_Z" ], - "M" : [ "OP_M" , "SZ_NA" ], - "Mb" : [ "OP_M" , "SZ_B" ], - "Mw" : [ "OP_M" , "SZ_W" ], - "Ms" : [ "OP_M" , "SZ_W" ], - "Md" : [ "OP_M" , "SZ_D" ], - "Mq" : [ "OP_M" , "SZ_Q" ], - "Mt" : [ "OP_M" , "SZ_T" ], - "Mo" : [ "OP_M" , "SZ_O" ], - "MwRv" : [ "OP_MR" , "SZ_WV" ], - "MdRy" : [ "OP_MR" , "SZ_DY" ], - "MbRv" : [ "OP_MR" , "SZ_BV" ], - "I1" : [ "OP_I1" , "SZ_NA" ], - "I3" : [ "OP_I3" , "SZ_NA" ], - "Ib" : [ "OP_I" , "SZ_B" ], - "Isb" : [ "OP_I" , "SZ_SB" ], - "Iw" : [ "OP_I" , "SZ_W" ], - "Iv" : [ "OP_I" , "SZ_V" ], - "Iz" : [ "OP_I" , "SZ_Z" ], - "Jv" : [ "OP_J" , "SZ_V" ], - "Jz" : [ "OP_J" , "SZ_Z" ], - "Jb" : [ "OP_J" , "SZ_B" ], - "R" : [ "OP_R" , "SZ_RDQ" ], - "C" : [ "OP_C" , "SZ_NA" ], - "D" : [ "OP_D" , "SZ_NA" ], - "S" : [ "OP_S" , "SZ_NA" ], - "Ob" : [ "OP_O" , "SZ_B" ], - "Ow" : [ "OP_O" , "SZ_W" ], - "Ov" : [ "OP_O" , "SZ_V" ], - "V" : [ "OP_V" , "SZ_O" ], - "W" : [ "OP_W" , "SZ_O" ], - "Wsd" : [ "OP_W" , "SZ_O" ], - "Wss" : [ "OP_W" , "SZ_O" ], - "P" : [ "OP_P" , "SZ_Q" ], - "Q" : [ "OP_Q" , "SZ_Q" ], - "VR" : [ "OP_VR" , "SZ_O" ], - "PR" : [ "OP_PR" , "SZ_Q" ], - "AL" : [ "OP_AL" , "SZ_NA" ], - "CL" : [ "OP_CL" , "SZ_NA" ], - "DL" : [ "OP_DL" , "SZ_NA" ], - "BL" : [ "OP_BL" , "SZ_NA" ], - "AH" : [ "OP_AH" , "SZ_NA" ], - "CH" : [ "OP_CH" , "SZ_NA" ], - "DH" : [ "OP_DH" , "SZ_NA" ], - "BH" : [ "OP_BH" , "SZ_NA" ], - "AX" : [ "OP_AX" , "SZ_NA" ], - "CX" : [ "OP_CX" , "SZ_NA" ], - "DX" : [ "OP_DX" , "SZ_NA" ], - "BX" : [ "OP_BX" , "SZ_NA" ], - "SI" : [ "OP_SI" , "SZ_NA" ], - "DI" : [ "OP_DI" , "SZ_NA" ], - "SP" : [ "OP_SP" , "SZ_NA" ], - "BP" : [ "OP_BP" , "SZ_NA" ], - "eAX" : [ "OP_eAX" , "SZ_NA" ], - "eCX" : [ "OP_eCX" , "SZ_NA" ], - "eDX" : [ "OP_eDX" , "SZ_NA" ], - "eBX" : [ "OP_eBX" , "SZ_NA" ], - "eSI" : [ "OP_eSI" , "SZ_NA" ], - "eDI" : [ "OP_eDI" , "SZ_NA" ], - "eSP" : [ "OP_eSP" , "SZ_NA" ], - "eBP" : [ "OP_eBP" , "SZ_NA" ], - "rAX" : [ "OP_rAX" , "SZ_NA" ], - "rCX" : [ "OP_rCX" , "SZ_NA" ], - "rBX" : [ "OP_rBX" , "SZ_NA" ], - "rDX" : [ "OP_rDX" , "SZ_NA" ], - "rSI" : [ "OP_rSI" , "SZ_NA" ], - "rDI" : [ "OP_rDI" , "SZ_NA" ], - "rSP" : [ "OP_rSP" , "SZ_NA" ], - "rBP" : [ "OP_rBP" , "SZ_NA" ], - "ES" : [ "OP_ES" , "SZ_NA" ], - "CS" : [ "OP_CS" , "SZ_NA" ], - "DS" : [ "OP_DS" , "SZ_NA" ], - "SS" : [ "OP_SS" , "SZ_NA" ], - "GS" : [ "OP_GS" , "SZ_NA" ], - "FS" : [ "OP_FS" , "SZ_NA" ], - "ST0" : [ "OP_ST0" , "SZ_NA" ], - "ST1" : [ "OP_ST1" , "SZ_NA" ], - "ST2" : [ "OP_ST2" , "SZ_NA" ], - "ST3" : [ "OP_ST3" , "SZ_NA" ], - "ST4" : [ "OP_ST4" , "SZ_NA" ], - "ST5" : [ "OP_ST5" , "SZ_NA" ], - "ST6" : [ "OP_ST6" , "SZ_NA" ], - "ST7" : [ "OP_ST7" , "SZ_NA" ], - "NONE" : [ "OP_NONE" , "SZ_NA" ], - "ALr8b" : [ "OP_ALr8b" , "SZ_NA" ], - "CLr9b" : [ "OP_CLr9b" , "SZ_NA" ], - "DLr10b" : [ "OP_DLr10b" , "SZ_NA" ], - "BLr11b" : [ "OP_BLr11b" , "SZ_NA" ], - "AHr12b" : [ "OP_AHr12b" , "SZ_NA" ], - "CHr13b" : [ "OP_CHr13b" , "SZ_NA" ], - "DHr14b" : [ "OP_DHr14b" , "SZ_NA" ], - "BHr15b" : [ "OP_BHr15b" , "SZ_NA" ], - "rAXr8" : [ "OP_rAXr8" , "SZ_NA" ], - "rCXr9" : [ "OP_rCXr9" , "SZ_NA" ], - "rDXr10" : [ "OP_rDXr10" , "SZ_NA" ], - "rBXr11" : [ "OP_rBXr11" , "SZ_NA" ], - "rSPr12" : [ "OP_rSPr12" , "SZ_NA" ], - "rBPr13" : [ "OP_rBPr13" , "SZ_NA" ], - "rSIr14" : [ "OP_rSIr14" , "SZ_NA" ], - "rDIr15" : [ "OP_rDIr15" , "SZ_NA" ], - "jWP" : [ "OP_J" , "SZ_WP" ], - "jDP" : [ "OP_J" , "SZ_DP" ], - - } - - # - # opcode prefix dictionary - # - PrefixDict = { - "aso" : "P_aso", - "oso" : "P_oso", - "rexw" : "P_rexw", - "rexb" : "P_rexb", - "rexx" : "P_rexx", - "rexr" : "P_rexr", - "seg" : "P_seg", - "inv64" : "P_inv64", - "def64" : "P_def64", - "depM" : "P_depM", - "cast1" : "P_c1", - "cast2" : "P_c2", - "cast3" : "P_c3", - "cast" : "P_cast", - "sext" : "P_sext" - } - - InvalidEntryIdx = 0 - InvalidEntry = { 'type' : 'invalid', - 'mnemonic' : 'invalid', - 'operands' : '', - 'prefixes' : '', - 'meta' : '' } - - Itab = [] # instruction table - ItabIdx = 1 # instruction table index - GtabIdx = 0 # group table index - GtabMeta = [] - - ItabLookup = {} - - MnemonicAliases = ( "invalid", "3dnow", "none", "db", "pause" ) - - def __init__( self, outputDir ): - # first itab entry (0) is Invalid - self.Itab.append( self.InvalidEntry ) - self.MnemonicsTable.extend( self.MnemonicAliases ) - self.outputDir = outputDir - - def toGroupId( self, id ): - return 0x8000 | id - - def genLookupTable( self, table, scope = '' ): - idxArray = [ ] - ( tabIdx, self.GtabIdx ) = ( self.GtabIdx, self.GtabIdx + 1 ) - self.GtabMeta.append( { 'type' : table[ 'type' ], 'meta' : table[ 'meta' ] } ) - - for _idx in range( self.sizeOfTable( table[ 'type' ] ) ): - idx = "%02x" % _idx - - e = self.InvalidEntry - i = self.InvalidEntryIdx - - if idx in table[ 'entries' ].keys(): - e = table[ 'entries' ][ idx ] - - # leaf node (insn) - if e[ 'type' ] == 'insn': - ( i, self.ItabIdx ) = ( self.ItabIdx, self.ItabIdx + 1 ) - self.Itab.append( e ) - elif e[ 'type' ] != 'invalid': - i = self.genLookupTable( e, 'static' ) - - idxArray.append( i ) - - name = "ud_itab__%s" % tabIdx - self.ItabLookup[ tabIdx ] = name - - self.ItabC.write( "\n" ); - if len( scope ): - self.ItabC.write( scope + ' ' ) - self.ItabC.write( "const uint16_t %s[] = {\n" % name ) - for i in range( len( idxArray ) ): - if i > 0 and i % 4 == 0: - self.ItabC.write( "\n" ) - if ( i%4 == 0 ): - self.ItabC.write( " /* %2x */" % i) - if idxArray[ i ] >= 0x8000: - self.ItabC.write( "%12s," % ("GROUP(%d)" % ( ~0x8000 & idxArray[ i ] ))) - else: - self.ItabC.write( "%12d," % ( idxArray[ i ] )) - self.ItabC.write( "\n" ) - self.ItabC.write( "};\n" ) - - return self.toGroupId( tabIdx ) - - def genLookupTableList( self ): - self.ItabC.write( "\n\n" ); - self.ItabC.write( "struct ud_lookup_table_list_entry ud_lookup_table_list[] = {\n" ) - for i in range( len( self.GtabMeta ) ): - f0 = self.ItabLookup[ i ] + "," - f1 = ( self.nameOfTable( self.GtabMeta[ i ][ 'type' ] ) ) + "," - f2 = "\"%s\"" % self.GtabMeta[ i ][ 'meta' ] - self.ItabC.write( " /* %03d */ { %s %s %s },\n" % ( i, f0, f1, f2 ) ) - self.ItabC.write( "};" ) - - def genInsnTable( self ): - self.ItabC.write( "struct ud_itab_entry ud_itab[] = {\n" ); - idx = 0 - for e in self.Itab: - opr_c = [ "O_NONE", "O_NONE", "O_NONE" ] - pfx_c = [] - opr = e[ 'operands' ] - for i in range(len(opr)): - if not (opr[i] in self.OperandDict.keys()): - print "error: invalid operand declaration: %s\n" % opr[i] - opr_c[i] = "O_" + opr[i] - opr = "%s %s %s" % (opr_c[0] + ",", opr_c[1] + ",", opr_c[2]) - - for p in e['prefixes']: - if not ( p in self.PrefixDict.keys() ): - print "error: invalid prefix specification: %s \n" % pfx - pfx_c.append( self.PrefixDict[p] ) - if len(e['prefixes']) == 0: - pfx_c.append( "P_none" ) - pfx = "|".join( pfx_c ) - - self.ItabC.write( " /* %04d */ { UD_I%s %s, %s },\n" \ - % ( idx, e[ 'mnemonic' ] + ',', opr, pfx ) ) - idx += 1 - self.ItabC.write( "};\n" ) - - self.ItabC.write( "\n\n" ); - self.ItabC.write( "const char * ud_mnemonics_str[] = {\n" ) - self.ItabC.write( ",\n ".join( [ "\"%s\"" % m for m in self.MnemonicsTable ] ) ) - self.ItabC.write( "\n};\n" ) - - - def genItabH( self ): - self.ItabH = open( os.path.join(self.outputDir, "udis86_itab.h"), "w" ) - - # Generate Table Type Enumeration - self.ItabH.write( "#ifndef UD_ITAB_H\n" ) - self.ItabH.write( "#define UD_ITAB_H\n\n" ) - - # table type enumeration - self.ItabH.write( "/* ud_table_type -- lookup table types (see lookup.c) */\n" ) - self.ItabH.write( "enum ud_table_type {\n " ) - enum = [ self.TableInfo[ k ][ 'name' ] for k in self.TableInfo.keys() ] - self.ItabH.write( ",\n ".join( enum ) ) - self.ItabH.write( "\n};\n\n" ); - - # mnemonic enumeration - self.ItabH.write( "/* ud_mnemonic -- mnemonic constants */\n" ) - enum = "enum ud_mnemonic_code {\n " - enum += ",\n ".join( [ "UD_I%s" % m for m in self.MnemonicsTable ] ) - enum += "\n} UD_ATTR_PACKED;\n" - self.ItabH.write( enum ) - self.ItabH.write( "\n" ) - - self.ItabH.write("\n/* itab entry operand definitions */\n"); - operands = self.OperandDict.keys() - operands.sort() - for o in operands: - self.ItabH.write("#define O_%-7s { %-12s %-8s }\n" % - (o, self.OperandDict[o][0] + ",", self.OperandDict[o][1])); - self.ItabH.write("\n\n"); - - self.ItabH.write( "extern const char * ud_mnemonics_str[];\n" ) - - self.ItabH.write( "#define GROUP(n) (0x8000 | (n))" ) - - self.ItabH.write( "\n#endif /* UD_ITAB_H */\n" ) - - self.ItabH.close() - - - def genItabC( self ): - self.ItabC = open( os.path.join(self.outputDir, "udis86_itab.c"), "w" ) - self.ItabC.write( "/* itab.c -- generated by itab.py, do no edit" ) - self.ItabC.write( " */\n" ); - self.ItabC.write( "#include \"udis86_decode.h\"\n\n" ); - - self.genLookupTable( self.OpcodeTable0 ) - self.genLookupTableList() - self.genInsnTable() - - self.ItabC.close() - - def genItab( self ): - self.genItabC() - self.genItabH() - -def main(): - parser = OptionParser() - parser.add_option("--outputDir", dest="outputDir", default="") - options, args = parser.parse_args() - generator = UdItabGenerator(os.path.normpath(options.outputDir)) - optableXmlParser = ud_optable.UdOptableXmlParser() - optableXmlParser.parse( args[ 0 ], generator.addInsnDef ) - - generator.genItab() - -if __name__ == '__main__': - main() diff --git a/3rdparty/masm/disassembler/udis86/optable.xml b/3rdparty/masm/disassembler/udis86/optable.xml deleted file mode 100644 index 14b4ac5935..0000000000 --- a/3rdparty/masm/disassembler/udis86/optable.xml +++ /dev/null @@ -1,8959 +0,0 @@ - - - - - - aaa - - 37 - inv64 - - - - - aad - - d5 - Ib - inv64 - - - - - aam - - d4 - Ib - inv64 - - - - - aas - - 3f - inv64 - - - - - adc - - aso rexr rexx rexb - 10 - Eb Gb - - - aso oso rexw rexr rexx rexb - 11 - Ev Gv - - - aso rexr rexx rexb - 12 - Gb Eb - - - aso oso rexw rexr rexx rexb - 13 - Gv Ev - - - 14 - AL Ib - - - oso rexw - 15 - rAX Iz - sext - - - aso rexr rexx rexb - 80 /reg=2 - Eb Ib - - - aso rexr rexx rexb - 82 /reg=2 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 81 /reg=2 - Ev Iz - sext - - - aso oso rexw rexr rexx rexb - 83 /reg=2 - Ev Ib - sext - - - - - add - - aso rexr rexx rexb - 00 - Eb Gb - - - aso oso rexw rexr rexx rexb - 01 - Ev Gv - - - aso rexr rexx rexb - 02 - Gb Eb - - - aso oso rexw rexr rexx rexb - 03 - Gv Ev - - - 04 - AL Ib - - - oso rexw - 05 - rAX Iz - sext - - - aso rexr rexx rexb - 80 /reg=0 - Eb Ib - - - aso rexr rexx rexb - 82 /reg=0 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 81 /reg=0 - Ev Iz - sext - - - aso oso rexw rexr rexx rexb - 83 /reg=0 - Ev Ib - sext - - - - - - - addpd - - aso rexr rexx rexb - sse66 0f 58 - V W - - - - - addps - - aso rexr rexx rexb - 0f 58 - V W - - - - - addsd - - aso rexr rexx rexb - ssef2 0f 58 - V W - - - - - addss - - aso rexr rexx rexb - ssef3 0f 58 - V W - - - - - and - - aso rexr rexx rexb - 20 - Eb Gb - - - aso oso rexw rexr rexx rexb - 21 - Ev Gv - - - aso rexr rexx rexb - 22 - Gb Eb - - - aso oso rexw rexr rexx rexb - 23 - Gv Ev - - - 24 - AL Ib - - - oso rexw - 25 - rAX Iz - sext - - - aso rexw rexr rexx rexb - 80 /reg=4 - Eb Ib - - - aso rexr rexx rexb - 82 /reg=4 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 81 /reg=4 - Ev Iz - sext - - - aso oso rexw rexr rexx rexb - 83 /reg=4 - Ev Ib - sext - - - - - andpd - - aso rexr rexx rexb - sse66 0f 54 - V W - - - - - andps - - aso rexr rexx rexb - 0f 54 - V W - - - - - andnpd - - aso rexr rexx rexb - sse66 0f 55 - V W - - - - - andnps - - aso rexr rexx rexb - 0f 55 - V W - - - - - arpl - - aso - 63 /m=16 - Ew Gw - inv64 - - - aso - 63 /m=32 - Ew Gw - inv64 - - - - - movsxd - - aso oso rexw rexx rexr rexb - 63 /m=64 - Gv Ed - - - - - bound - - aso oso - 62 - Gv M - inv64 - - - - - bsf - - aso oso rexw rexr rexx rexb - 0f bc - Gv Ev - - - - - bsr - - aso oso rexw rexr rexx rexb - 0f bd - Gv Ev - - - - - bswap - - oso rexw rexb - 0f c8 - rAXr8 - - - oso rexw rexb - 0f c9 - rCXr9 - - - oso rexw rexb - 0f ca - rDXr10 - - - oso rexw rexb - 0f cb - rBXr11 - - - oso rexw rexb - 0f cc - rSPr12 - - - oso rexw rexb - 0f cd - rBPr13 - - - oso rexw rexb - 0f ce - rSIr14 - - - oso rexw rexb - 0f cf - rDIr15 - - - - - bt - - aso oso rexw rexr rexx rexb - 0f ba /reg=4 - Ev Ib - - - aso oso rexw rexr rexx rexb - 0f a3 - Ev Gv - - - - - btc - - aso oso rexw rexr rexx rexb - 0f bb - Ev Gv - - - aso oso rexw rexr rexx rexb - 0f ba /reg=7 - Ev Ib - - - - - btr - - aso oso rexw rexr rexx rexb - 0f b3 - Ev Gv - - - aso oso rexw rexr rexx rexb - 0f ba /reg=6 - Ev Ib - - - - - bts - - aso oso rexw rexr rexx rexb - 0f ab - Ev Gv - - - aso oso rexw rexr rexx rexb - 0f ba /reg=5 - Ev Ib - - - - - call - - aso oso rexw rexr rexx rexb - ff /reg=2 - Ev - def64 - - - aso oso rexw rexr rexx rexb - ff /reg=3 - Ep - - - oso - e8 - Jz - def64 - - - oso - 9a - Ap - inv64 - - - - - cbw - - oso rexw - 98 /o=16 - - - - - cwde - - oso rexw - 98 /o=32 - - - - - cdqe - - oso rexw - 98 /o=64 - - - - - clc - - f8 - - - - - cld - - fc - - - - - clflush - - aso rexw rexr rexx rexb - 0f ae /reg=7 /mod=!11 - M - - - - - clgi - amd - - 0f 01 /reg=3 /mod=11 /rm=5 - - - - - cli - - fa - - - - - clts - - 0f 06 - - - - - cmc - - f5 - - - - - cmovo - - aso oso rexw rexr rexx rexb - 0f 40 - Gv Ev - - - - - cmovno - - aso oso rexw rexr rexx rexb - 0f 41 - Gv Ev - - - - - cmovb - - aso oso rexw rexr rexx rexb - 0f 42 - Gv Ev - - - - - cmovae - - aso oso rexw rexr rexx rexb - 0f 43 - Gv Ev - - - - - cmovz - - aso oso rexw rexr rexx rexb - 0f 44 - Gv Ev - - - - - cmovnz - - aso oso rexw rexr rexx rexb - 0f 45 - Gv Ev - - - - - cmovbe - - aso oso rexw rexr rexx rexb - 0f 46 - Gv Ev - - - - - cmova - - aso oso rexw rexr rexx rexb - 0f 47 - Gv Ev - - - - - cmovs - - aso oso rexw rexr rexx rexb - 0f 48 - Gv Ev - - - - - cmovns - - aso oso rexw rexr rexx rexb - 0f 49 - Gv Ev - - - - - cmovp - - aso oso rexw rexr rexx rexb - 0f 4a - Gv Ev - - - - - cmovnp - - aso oso rexw rexr rexx rexb - 0f 4b - Gv Ev - - - - - cmovl - - aso oso rexw rexr rexx rexb - 0f 4c - Gv Ev - - - - - cmovge - - aso oso rexw rexr rexx rexb - 0f 4d - Gv Ev - - - - - cmovle - - aso oso rexw rexr rexx rexb - 0f 4e - Gv Ev - - - - - cmovg - - aso oso rexw rexr rexx rexb - 0f 4f - Gv Ev - - - - - cmp - - aso rexr rexx rexb - 38 - Eb Gb - - - aso oso rexw rexr rexx rexb - 39 - Ev Gv - - - aso rexr rexx rexb - 3a - Gb Eb - - - aso oso rexw rexr rexx rexb - 3b - Gv Ev - - - 3c - AL Ib - - - oso rexw - 3d - rAX Iz - - - aso rexr rexx rexb - 80 /reg=7 - Eb Ib - - - aso rexr rexx rexb - 82 /reg=7 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 81 /reg=7 - Ev Iz - - - aso oso rexw rexr rexx rexb - 83 /reg=7 - Ev Ib - - - - - cmppd - - aso rexr rexx rexb - sse66 0f c2 - V W Ib - - - - - cmpps - - aso rexr rexx rexb - 0f c2 - V W Ib - - - - - cmpsb - - a6 - - - - - cmpsw - - oso rexw - a7 /o=16 - - - - - cmpsd - - oso rexw - a7 /o=32 - - - aso rexr rexx rexb - ssef2 0f c2 - V W Ib - - - - - cmpsq - - oso rexw - a7 /o=64 - - - - - cmpss - - aso rexr rexx rexb - ssef3 0f c2 - V W Ib - - - - - cmpxchg - - aso rexr rexx rexb - 0f b0 - Eb Gb - - - aso oso rexw rexr rexx rexb - 0f b1 - Ev Gv - - - - - cmpxchg8b - - aso rexr rexx rexb - 0f c7 /reg=1 - M - - - - - comisd - - aso rexr rexx rexb - sse66 0f 2f - V W - - - - - comiss - - aso rexr rexx rexb - 0f 2f - V W - - - - - cpuid - - 0f a2 - - - - - cvtdq2pd - - aso rexr rexx rexb - ssef3 0f e6 - V W - - - - - cvtdq2ps - - aso rexr rexx rexb - 0f 5b - V W - - - - - cvtpd2dq - - aso rexr rexx rexb - ssef2 0f e6 - V W - - - - - cvtpd2pi - - aso rexr rexx rexb - sse66 0f 2d - P W - - - - - cvtpd2ps - - aso rexr rexx rexb - sse66 0f 5a - V W - - - - - cvtpi2ps - - aso rexr rexx rexb - 0f 2a - V Q - - - - - cvtpi2pd - - aso rexr rexx rexb - sse66 0f 2a - V Q - - - - - cvtps2dq - - aso rexr rexx rexb - sse66 0f 5b - V W - - - - - cvtps2pi - - aso rexr rexx rexb - 0f 2d - P W - - - - - cvtps2pd - - aso rexr rexx rexb - 0f 5a - V W - - - - - cvtsd2si - - aso rexw rexr rexx rexb - ssef2 0f 2d - Gy W - - - - - cvtsd2ss - - aso rexr rexx rexb - ssef2 0f 5a - V W - - - - - cvtsi2ss - - aso rexw rexr rexx rexb - ssef3 0f 2a - V Ex - - - - - cvtss2si - - aso rexw rexr rexx rexb - ssef3 0f 2d - Gy W - - - - - cvtss2sd - - aso rexr rexx rexb - ssef3 0f 5a - V W - - - - - cvttpd2pi - - aso rexr rexx rexb - sse66 0f 2c - P W - - - - - cvttpd2dq - - aso rexr rexx rexb - sse66 0f e6 - V W - - - - - cvttps2dq - - aso rexr rexx rexb - ssef3 0f 5b - V W - - - - - cvttps2pi - - aso rexr rexx rexb - 0f 2c - P W - - - - - cvttsd2si - - aso rexw rexr rexx rexb - ssef2 0f 2c - Gy Wsd - - - - - cvtsi2sd - - aso rexw rexr rexx rexb - ssef2 0f 2a - V Ex - - - - - cvttss2si - - aso rexw rexr rexx rexb - ssef3 0f 2c - Gy Wsd - - - - - cwd - - oso rexw - 99 /o=16 - - - - - cdq - - oso rexw - 99 /o=32 - - - - - cqo - - oso rexw - 99 /o=64 - - - - - daa - - 27 - inv64 - - - - - das - - 2f - inv64 - - - - - dec - - oso - 48 - eAX - - - oso - 49 - eCX - - - oso - 4a - eDX - - - oso - 4b - eBX - - - oso - 4c - eSP - - - oso - 4d - eBP - - - oso - 4e - eSI - - - oso - 4f - eDI - - - aso rexw rexr rexx rexb - fe /reg=1 - Eb - - - aso oso rexw rexr rexx rexb - ff /reg=1 - Ev - - - - - div - - aso oso rexw rexr rexx rexb - f7 /reg=6 - Ev - - - aso rexw rexr rexx rexb - f6 /reg=6 - Eb - - - - - divpd - - aso rexr rexx rexb - sse66 0f 5e - V W - - - - - divps - - aso rexr rexx rexb - 0f 5e - V W - - - - - divsd - - aso rexr rexx rexb - ssef2 0f 5e - V W - - - - - divss - - aso rexr rexx rexb - ssef3 0f 5e - V W - - - - - emms - - 0f 77 - - - - - enter - - c8 - Iw Ib - def64 depM - - - - - f2xm1 - X87 - - d9 /mod=11 /x87=30 - - - - - fabs - X87 - - d9 /mod=11 /x87=21 - - - - - fadd - X87 - - aso rexr rexx rexb - dc /mod=!11 /reg=0 - Mq - - - aso rexr rexx rexb - d8 /mod=!11 /reg=0 - Md - - - dc /mod=11 /x87=00 - ST0 ST0 - - - dc /mod=11 /x87=01 - ST1 ST0 - - - dc /mod=11 /x87=02 - ST2 ST0 - - - dc /mod=11 /x87=03 - ST3 ST0 - - - dc /mod=11 /x87=04 - ST4 ST0 - - - dc /mod=11 /x87=05 - ST5 ST0 - - - dc /mod=11 /x87=06 - ST6 ST0 - - - dc /mod=11 /x87=07 - ST7 ST0 - - - d8 /mod=11 /x87=00 - ST0 ST0 - - - d8 /mod=11 /x87=01 - ST0 ST1 - - - d8 /mod=11 /x87=02 - ST0 ST2 - - - d8 /mod=11 /x87=03 - ST0 ST3 - - - d8 /mod=11 /x87=04 - ST0 ST4 - - - d8 /mod=11 /x87=05 - ST0 ST5 - - - d8 /mod=11 /x87=06 - ST0 ST6 - - - d8 /mod=11 /x87=07 - ST0 ST7 - - - - - faddp - X87 - - de /mod=11 /x87=00 - ST0 ST0 - - - de /mod=11 /x87=01 - ST1 ST0 - - - de /mod=11 /x87=02 - ST2 ST0 - - - de /mod=11 /x87=03 - ST3 ST0 - - - de /mod=11 /x87=04 - ST4 ST0 - - - de /mod=11 /x87=05 - ST5 ST0 - - - de /mod=11 /x87=06 - ST6 ST0 - - - de /mod=11 /x87=07 - ST7 ST0 - - - - - fbld - X87 - - aso rexr rexx rexb - df /mod=!11 /reg=4 - Mt - - - - - fbstp - X87 - - aso rexr rexx rexb - df /mod=!11 /reg=6 - Mt - - - - - fchs - X87 - - d9 /mod=11 /x87=20 - - - - - fclex - X87 - - db /mod=11 /x87=22 - - - - - fcmovb - X87 - - da /mod=11 /x87=00 - ST0 ST0 - - - da /mod=11 /x87=01 - ST0 ST1 - - - da /mod=11 /x87=02 - ST0 ST2 - - - da /mod=11 /x87=03 - ST0 ST3 - - - da /mod=11 /x87=04 - ST0 ST4 - - - da /mod=11 /x87=05 - ST0 ST5 - - - da /mod=11 /x87=06 - ST0 ST6 - - - da /mod=11 /x87=07 - ST0 ST7 - - - - - fcmove - X87 - - da /mod=11 /x87=08 - ST0 ST0 - - - da /mod=11 /x87=09 - ST0 ST1 - - - da /mod=11 /x87=0a - ST0 ST2 - - - da /mod=11 /x87=0b - ST0 ST3 - - - da /mod=11 /x87=0c - ST0 ST4 - - - da /mod=11 /x87=0d - ST0 ST5 - - - da /mod=11 /x87=0e - ST0 ST6 - - - da /mod=11 /x87=0f - ST0 ST7 - - - - - fcmovbe - X87 - - da /mod=11 /x87=10 - ST0 ST0 - - - da /mod=11 /x87=11 - ST0 ST1 - - - da /mod=11 /x87=12 - ST0 ST2 - - - da /mod=11 /x87=13 - ST0 ST3 - - - da /mod=11 /x87=14 - ST0 ST4 - - - da /mod=11 /x87=15 - ST0 ST5 - - - da /mod=11 /x87=16 - ST0 ST6 - - - da /mod=11 /x87=17 - ST0 ST7 - - - - - fcmovu - X87 - - da /mod=11 /x87=18 - ST0 ST0 - - - da /mod=11 /x87=19 - ST0 ST1 - - - da /mod=11 /x87=1a - ST0 ST2 - - - da /mod=11 /x87=1b - ST0 ST3 - - - da /mod=11 /x87=1c - ST0 ST4 - - - da /mod=11 /x87=1d - ST0 ST5 - - - da /mod=11 /x87=1e - ST0 ST6 - - - da /mod=11 /x87=1f - ST0 ST7 - - - - - fcmovnb - X87 - - db /mod=11 /x87=00 - ST0 ST0 - - - db /mod=11 /x87=01 - ST0 ST1 - - - db /mod=11 /x87=02 - ST0 ST2 - - - db /mod=11 /x87=03 - ST0 ST3 - - - db /mod=11 /x87=04 - ST0 ST4 - - - db /mod=11 /x87=05 - ST0 ST5 - - - db /mod=11 /x87=06 - ST0 ST6 - - - db /mod=11 /x87=07 - ST0 ST7 - - - - - fcmovne - X87 - - db /mod=11 /x87=08 - ST0 ST0 - - - db /mod=11 /x87=09 - ST0 ST1 - - - db /mod=11 /x87=0a - ST0 ST2 - - - db /mod=11 /x87=0b - ST0 ST3 - - - db /mod=11 /x87=0c - ST0 ST4 - - - db /mod=11 /x87=0d - ST0 ST5 - - - db /mod=11 /x87=0e - ST0 ST6 - - - db /mod=11 /x87=0f - ST0 ST7 - - - - - fcmovnbe - X87 - - db /mod=11 /x87=10 - ST0 ST0 - - - db /mod=11 /x87=11 - ST0 ST1 - - - db /mod=11 /x87=12 - ST0 ST2 - - - db /mod=11 /x87=13 - ST0 ST3 - - - db /mod=11 /x87=14 - ST0 ST4 - - - db /mod=11 /x87=15 - ST0 ST5 - - - db /mod=11 /x87=16 - ST0 ST6 - - - db /mod=11 /x87=17 - ST0 ST7 - - - - - fcmovnu - X87 - - db /mod=11 /x87=18 - ST0 ST0 - - - db /mod=11 /x87=19 - ST0 ST1 - - - db /mod=11 /x87=1a - ST0 ST2 - - - db /mod=11 /x87=1b - ST0 ST3 - - - db /mod=11 /x87=1c - ST0 ST4 - - - db /mod=11 /x87=1d - ST0 ST5 - - - db /mod=11 /x87=1e - ST0 ST6 - - - db /mod=11 /x87=1f - ST0 ST7 - - - - - fucomi - X87 - - db /mod=11 /x87=28 - ST0 ST0 - - - db /mod=11 /x87=29 - ST0 ST1 - - - db /mod=11 /x87=2a - ST0 ST2 - - - db /mod=11 /x87=2b - ST0 ST3 - - - db /mod=11 /x87=2c - ST0 ST4 - - - db /mod=11 /x87=2d - ST0 ST5 - - - db /mod=11 /x87=2e - ST0 ST6 - - - db /mod=11 /x87=2f - ST0 ST7 - - - - - fcom - X87 - - aso rexr rexx rexb - d8 /mod=!11 /reg=2 - Md - - - aso rexr rexx rexb - dc /mod=!11 /reg=2 - Mq - - - d8 /mod=11 /x87=10 - ST0 ST0 - - - d8 /mod=11 /x87=11 - ST0 ST1 - - - d8 /mod=11 /x87=12 - ST0 ST2 - - - d8 /mod=11 /x87=13 - ST0 ST3 - - - d8 /mod=11 /x87=14 - ST0 ST4 - - - d8 /mod=11 /x87=15 - ST0 ST5 - - - d8 /mod=11 /x87=16 - ST0 ST6 - - - d8 /mod=11 /x87=17 - ST0 ST7 - - - - - fcom2 - X87 UNDOC - - dc /mod=11 /x87=10 - ST0 - - - dc /mod=11 /x87=11 - ST1 - - - dc /mod=11 /x87=12 - ST2 - - - dc /mod=11 /x87=13 - ST3 - - - dc /mod=11 /x87=14 - ST4 - - - dc /mod=11 /x87=15 - ST5 - - - dc /mod=11 /x87=16 - ST6 - - - dc /mod=11 /x87=17 - ST7 - - - - - fcomp3 - X87 UNDOC - - dc /mod=11 /x87=18 - ST0 - - - dc /mod=11 /x87=19 - ST1 - - - dc /mod=11 /x87=1a - ST2 - - - dc /mod=11 /x87=1b - ST3 - - - dc /mod=11 /x87=1c - ST4 - - - dc /mod=11 /x87=1d - ST5 - - - dc /mod=11 /x87=1e - ST6 - - - dc /mod=11 /x87=1f - ST7 - - - - - fcomi - X87 - - db /mod=11 /x87=30 - ST0 ST0 - - - db /mod=11 /x87=31 - ST0 ST1 - - - db /mod=11 /x87=32 - ST0 ST2 - - - db /mod=11 /x87=33 - ST0 ST3 - - - db /mod=11 /x87=34 - ST0 ST4 - - - db /mod=11 /x87=35 - ST0 ST5 - - - db /mod=11 /x87=36 - ST0 ST6 - - - db /mod=11 /x87=37 - ST0 ST7 - - - - - fucomip - X87 - - df /mod=11 /x87=28 - ST0 ST0 - - - df /mod=11 /x87=29 - ST0 ST1 - - - df /mod=11 /x87=2a - ST0 ST2 - - - df /mod=11 /x87=2b - ST0 ST3 - - - df /mod=11 /x87=2c - ST0 ST4 - - - df /mod=11 /x87=2d - ST0 ST5 - - - df /mod=11 /x87=2e - ST0 ST6 - - - df /mod=11 /x87=2f - ST0 ST7 - - - - - fcomip - X87 - - df /mod=11 /x87=30 - ST0 ST0 - - - df /mod=11 /x87=31 - ST0 ST1 - - - df /mod=11 /x87=32 - ST0 ST2 - - - df /mod=11 /x87=33 - ST0 ST3 - - - df /mod=11 /x87=34 - ST0 ST4 - - - df /mod=11 /x87=35 - ST0 ST5 - - - df /mod=11 /x87=36 - ST0 ST6 - - - df /mod=11 /x87=37 - ST0 ST7 - - - - - fcomp - X87 - - aso rexr rexx rexb - d8 /mod=!11 /reg=3 - Md - - - aso rexr rexx rexb - dc /mod=!11 /reg=3 - Mq - - - d8 /mod=11 /x87=18 - ST0 ST0 - - - d8 /mod=11 /x87=19 - ST0 ST1 - - - d8 /mod=11 /x87=1a - ST0 ST2 - - - d8 /mod=11 /x87=1b - ST0 ST3 - - - d8 /mod=11 /x87=1c - ST0 ST4 - - - d8 /mod=11 /x87=1d - ST0 ST5 - - - d8 /mod=11 /x87=1e - ST0 ST6 - - - d8 /mod=11 /x87=1f - ST0 ST7 - - - - - fcomp5 - X87 UNDOC - - de /mod=11 /x87=10 - ST0 - - - de /mod=11 /x87=11 - ST1 - - - de /mod=11 /x87=12 - ST2 - - - de /mod=11 /x87=13 - ST3 - - - de /mod=11 /x87=14 - ST4 - - - de /mod=11 /x87=15 - ST5 - - - de /mod=11 /x87=16 - ST6 - - - de /mod=11 /x87=17 - ST7 - - - - - fcompp - X87 - - de /mod=11 /x87=19 - - - - - fcos - X87 - - d9 /mod=11 /x87=3f - - - - - fdecstp - X87 - - d9 /mod=11 /x87=36 - - - - - fdiv - X87 - - aso rexr rexx rexb - dc /mod=!11 /reg=6 - Mq - - - dc /mod=11 /x87=38 - ST0 ST0 - - - dc /mod=11 /x87=39 - ST1 ST0 - - - dc /mod=11 /x87=3a - ST2 ST0 - - - dc /mod=11 /x87=3b - ST3 ST0 - - - dc /mod=11 /x87=3c - ST4 ST0 - - - dc /mod=11 /x87=3d - ST5 ST0 - - - dc /mod=11 /x87=3e - ST6 ST0 - - - dc /mod=11 /x87=3f - ST7 ST0 - - - aso rexr rexx rexb - d8 /mod=!11 /reg=6 - Md - - - d8 /mod=11 /x87=30 - ST0 ST0 - - - d8 /mod=11 /x87=31 - ST0 ST1 - - - d8 /mod=11 /x87=32 - ST0 ST2 - - - d8 /mod=11 /x87=33 - ST0 ST3 - - - d8 /mod=11 /x87=34 - ST0 ST4 - - - d8 /mod=11 /x87=35 - ST0 ST5 - - - d8 /mod=11 /x87=36 - ST0 ST6 - - - d8 /mod=11 /x87=37 - ST0 ST7 - - - - - fdivp - X87 - - de /mod=11 /x87=38 - ST0 ST0 - - - de /mod=11 /x87=39 - ST1 ST0 - - - de /mod=11 /x87=3a - ST2 ST0 - - - de /mod=11 /x87=3b - ST3 ST0 - - - de /mod=11 /x87=3c - ST4 ST0 - - - de /mod=11 /x87=3d - ST5 ST0 - - - de /mod=11 /x87=3e - ST6 ST0 - - - de /mod=11 /x87=3f - ST7 ST0 - - - - - fdivr - X87 - - aso rexr rexx rexb - dc /mod=!11 /reg=7 - Mq - - - dc /mod=11 /x87=30 - ST0 ST0 - - - dc /mod=11 /x87=31 - ST1 ST0 - - - dc /mod=11 /x87=32 - ST2 ST0 - - - dc /mod=11 /x87=33 - ST3 ST0 - - - dc /mod=11 /x87=34 - ST4 ST0 - - - dc /mod=11 /x87=35 - ST5 ST0 - - - dc /mod=11 /x87=36 - ST6 ST0 - - - dc /mod=11 /x87=37 - ST7 ST0 - - - aso rexr rexx rexb - d8 /mod=!11 /reg=7 - Md - - - d8 /mod=11 /x87=38 - ST0 ST0 - - - d8 /mod=11 /x87=39 - ST0 ST1 - - - d8 /mod=11 /x87=3a - ST0 ST2 - - - d8 /mod=11 /x87=3b - ST0 ST3 - - - d8 /mod=11 /x87=3c - ST0 ST4 - - - d8 /mod=11 /x87=3d - ST0 ST5 - - - d8 /mod=11 /x87=3e - ST0 ST6 - - - d8 /mod=11 /x87=3f - ST0 ST7 - - - - - fdivrp - X87 - - de /mod=11 /x87=30 - ST0 ST0 - - - de /mod=11 /x87=31 - ST1 ST0 - - - de /mod=11 /x87=32 - ST2 ST0 - - - de /mod=11 /x87=33 - ST3 ST0 - - - de /mod=11 /x87=34 - ST4 ST0 - - - de /mod=11 /x87=35 - ST5 ST0 - - - de /mod=11 /x87=36 - ST6 ST0 - - - de /mod=11 /x87=37 - ST7 ST0 - - - - - femms - - 0f 0e - - - - - ffree - X87 - - dd /mod=11 /x87=00 - ST0 - - - dd /mod=11 /x87=01 - ST1 - - - dd /mod=11 /x87=02 - ST2 - - - dd /mod=11 /x87=03 - ST3 - - - dd /mod=11 /x87=04 - ST4 - - - dd /mod=11 /x87=05 - ST5 - - - dd /mod=11 /x87=06 - ST6 - - - dd /mod=11 /x87=07 - ST7 - - - - - ffreep - X87 - - df /mod=11 /x87=00 - ST0 - - - df /mod=11 /x87=01 - ST1 - - - df /mod=11 /x87=02 - ST2 - - - df /mod=11 /x87=03 - ST3 - - - df /mod=11 /x87=04 - ST4 - - - df /mod=11 /x87=05 - ST5 - - - df /mod=11 /x87=06 - ST6 - - - df /mod=11 /x87=07 - ST7 - - - - - ficom - X87 - - aso rexr rexx rexb - de /mod=!11 /reg=2 - Mw - - - aso rexr rexx rexb - da /mod=!11 /reg=2 - Md - - - - - ficomp - X87 - - aso rexr rexx rexb - de /mod=!11 /reg=3 - Mw - - - aso rexr rexx rexb - da /mod=!11 /reg=3 - Md - - - - - fild - X87 - - aso rexr rexx rexb - df /mod=!11 /reg=0 - Mw - - - aso rexr rexx rexb - df /mod=!11 /reg=5 - Mq - - - aso rexr rexx rexb - db /mod=!11 /reg=0 - Md - - - - - fncstp - X87 - - d9 /mod=11 /x87=37 - - - - - fninit - X87 - - db /mod=11 /x87=23 - - - - - fiadd - X87 - - aso rexr rexx rexb - da /mod=!11 /reg=0 - Md - - - aso rexr rexx rexb - de /mod=!11 /reg=0 - Mw - - - - - fidivr - X87 - - aso rexr rexx rexb - da /mod=!11 /reg=7 - Md - - - aso rexr rexx rexb - de /mod=!11 /reg=7 - Mw - - - - - fidiv - X87 - - aso rexr rexx rexb - da /mod=!11 /reg=6 - Md - - - aso rexr rexx rexb - de /mod=!11 /reg=6 - Mw - - - - - fisub - X87 - - aso rexr rexx rexb - da /mod=!11 /reg=4 - Md - - - aso rexr rexx rexb - de /mod=!11 /reg=4 - Mw - - - - - fisubr - X87 - - aso rexr rexx rexb - da /mod=!11 /reg=5 - Md - - - aso rexr rexx rexb - de /mod=!11 /reg=5 - Mw - - - - - fist - X87 - - aso rexr rexx rexb - df /mod=!11 /reg=2 - Mw - - - aso rexr rexx rexb - db /mod=!11 /reg=2 - Md - - - - - fistp - X87 - - aso rexr rexx rexb - df /mod=!11 /reg=3 - Mw - - - aso rexr rexx rexb - df /mod=!11 /reg=7 - Mq - - - aso rexr rexx rexb - db /mod=!11 /reg=3 - Md - - - - - fisttp - X87 - - aso rexr rexx rexb - db /mod=!11 /reg=1 - Md - - - aso rexr rexx rexb - dd /mod=!11 /reg=1 - Mq - - - aso rexr rexx rexb - df /mod=!11 /reg=1 - Mw - - - - - fld - X87 - - aso rexr rexx rexb - db /mod=!11 /reg=5 - Mt - - - aso rexr rexx rexb - dd /mod=!11 /reg=0 - Mq - - - aso rexr rexx rexb - d9 /mod=!11 /reg=0 - Md - - - d9 /mod=11 /x87=00 - ST0 - - - d9 /mod=11 /x87=01 - ST1 - - - d9 /mod=11 /x87=02 - ST2 - - - d9 /mod=11 /x87=03 - ST3 - - - d9 /mod=11 /x87=04 - ST4 - - - d9 /mod=11 /x87=05 - ST5 - - - d9 /mod=11 /x87=06 - ST6 - - - d9 /mod=11 /x87=07 - ST7 - - - - - fld1 - X87 - - d9 /mod=11 /x87=28 - - - - - fldl2t - X87 - - d9 /mod=11 /x87=29 - - - - - fldl2e - X87 - - d9 /mod=11 /x87=2a - - - - - fldlpi - X87 - - d9 /mod=11 /x87=2b - - - - - fldlg2 - X87 - - d9 /mod=11 /x87=2c - - - - - fldln2 - X87 - - d9 /mod=11 /x87=2d - - - - - fldz - X87 - - d9 /mod=11 /x87=2e - - - - - fldcw - X87 - - aso rexr rexx rexb - d9 /mod=!11 /reg=5 - Mw - - - - - fldenv - X87 - - aso rexr rexx rexb - d9 /mod=!11 /reg=4 - M - - - - - fmul - X87 - - aso rexr rexx rexb - dc /mod=!11 /reg=1 - Mq - - - dc /mod=11 /x87=08 - ST0 ST0 - - - dc /mod=11 /x87=09 - ST1 ST0 - - - dc /mod=11 /x87=0a - ST2 ST0 - - - dc /mod=11 /x87=0b - ST3 ST0 - - - dc /mod=11 /x87=0c - ST4 ST0 - - - dc /mod=11 /x87=0d - ST5 ST0 - - - dc /mod=11 /x87=0e - ST6 ST0 - - - dc /mod=11 /x87=0f - ST7 ST0 - - - aso rexr rexx rexb - d8 /mod=!11 /reg=1 - Md - - - d8 /mod=11 /x87=08 - ST0 ST0 - - - d8 /mod=11 /x87=09 - ST0 ST1 - - - d8 /mod=11 /x87=0a - ST0 ST2 - - - d8 /mod=11 /x87=0b - ST0 ST3 - - - d8 /mod=11 /x87=0c - ST0 ST4 - - - d8 /mod=11 /x87=0d - ST0 ST5 - - - d8 /mod=11 /x87=0e - ST0 ST6 - - - d8 /mod=11 /x87=0f - ST0 ST7 - - - - - fmulp - X87 - - de /mod=11 /x87=08 - ST0 ST0 - - - de /mod=11 /x87=09 - ST1 ST0 - - - de /mod=11 /x87=0a - ST2 ST0 - - - de /mod=11 /x87=0b - ST3 ST0 - - - de /mod=11 /x87=0c - ST4 ST0 - - - de /mod=11 /x87=0d - ST5 ST0 - - - de /mod=11 /x87=0e - ST6 ST0 - - - de /mod=11 /x87=0f - ST7 ST0 - - - - - fimul - X87 - - aso rexr rexx rexb - da /mod=!11 /reg=1 - Md - - - aso rexr rexx rexb - de /mod=!11 /reg=1 - Mw - - - - - fnop - X87 - - d9 /mod=11 /x87=10 - - - - - fpatan - X87 - - d9 /mod=11 /x87=33 - - - - - fprem - X87 - - d9 /mod=11 /x87=38 - - - - - fprem1 - X87 - - d9 /mod=11 /x87=35 - - - - - fptan - X87 - - d9 /mod=11 /x87=32 - - - - - frndint - X87 - - d9 /mod=11 /x87=3c - - - - - frstor - X87 - - aso rexr rexx rexb - dd /mod=!11 /reg=4 - M - - - - - fnsave - X87 - - aso rexr rexx rexb - dd /mod=!11 /reg=6 - M - - - - - fscale - X87 - - d9 /mod=11 /x87=3d - - - - - fsin - X87 - - d9 /mod=11 /x87=3e - - - - - fsincos - X87 - - d9 /mod=11 /x87=3b - - - - - fsqrt - X87 - - d9 /mod=11 /x87=3a - - - - - fstp - X87 - - aso rexr rexx rexb - db /mod=!11 /reg=7 - Mt - - - aso rexr rexx rexb - dd /mod=!11 /reg=3 - Mq - - - aso rexr rexx rexb - d9 /mod=!11 /reg=3 - Md - - - dd /mod=11 /x87=18 - ST0 - - - dd /mod=11 /x87=19 - ST1 - - - dd /mod=11 /x87=1a - ST2 - - - dd /mod=11 /x87=1b - ST3 - - - dd /mod=11 /x87=1c - ST4 - - - dd /mod=11 /x87=1d - ST5 - - - dd /mod=11 /x87=1e - ST6 - - - dd /mod=11 /x87=1f - ST7 - - - - - fstp1 - - d9 /mod=11 /x87=18 - ST0 - - - d9 /mod=11 /x87=19 - ST1 - - - d9 /mod=11 /x87=1a - ST2 - - - d9 /mod=11 /x87=1b - ST3 - - - d9 /mod=11 /x87=1c - ST4 - - - d9 /mod=11 /x87=1d - ST5 - - - d9 /mod=11 /x87=1e - ST6 - - - d9 /mod=11 /x87=1f - ST7 - - - - - fstp8 - - df /mod=11 /x87=10 - ST0 - - - df /mod=11 /x87=11 - ST1 - - - df /mod=11 /x87=12 - ST2 - - - df /mod=11 /x87=13 - ST3 - - - df /mod=11 /x87=14 - ST4 - - - df /mod=11 /x87=15 - ST5 - - - df /mod=11 /x87=16 - ST6 - - - df /mod=11 /x87=17 - ST7 - - - - - fstp9 - - df /mod=11 /x87=18 - ST0 - - - df /mod=11 /x87=19 - ST1 - - - df /mod=11 /x87=1a - ST2 - - - df /mod=11 /x87=1b - ST3 - - - df /mod=11 /x87=1c - ST4 - - - df /mod=11 /x87=1d - ST5 - - - df /mod=11 /x87=1e - ST6 - - - df /mod=11 /x87=1f - ST7 - - - - - fst - X87 - - aso rexr rexx rexb - d9 /mod=!11 /reg=2 - Md - - - aso rexr rexx rexb - dd /mod=!11 /reg=2 - Mq - - - dd /mod=11 /x87=10 - ST0 - - - dd /mod=11 /x87=11 - ST1 - - - dd /mod=11 /x87=12 - ST2 - - - dd /mod=11 /x87=13 - ST3 - - - dd /mod=11 /x87=14 - ST4 - - - dd /mod=11 /x87=15 - ST5 - - - dd /mod=11 /x87=16 - ST6 - - - dd /mod=11 /x87=17 - ST7 - - - - - fnstcw - X87 - - aso rexr rexx rexb - d9 /mod=!11 /reg=7 - Mw - - - - - fnstenv - X87 - - aso rexr rexx rexb - d9 /mod=!11 /reg=6 - M - - - - - fnstsw - X87 - - aso rexr rexx rexb - dd /mod=!11 /reg=7 - Mw - - - df /mod=11 /x87=20 - AX - - - - - fsub - X87 - - aso rexr rexx rexb - d8 /mod=!11 /reg=4 - Md - - - aso rexr rexx rexb - dc /mod=!11 /reg=4 - Mq - - - d8 /mod=11 /x87=20 - ST0 ST0 - - - d8 /mod=11 /x87=21 - ST0 ST1 - - - d8 /mod=11 /x87=22 - ST0 ST2 - - - d8 /mod=11 /x87=23 - ST0 ST3 - - - d8 /mod=11 /x87=24 - ST0 ST4 - - - d8 /mod=11 /x87=25 - ST0 ST5 - - - d8 /mod=11 /x87=26 - ST0 ST6 - - - d8 /mod=11 /x87=27 - ST0 ST7 - - - dc /mod=11 /x87=28 - ST0 ST0 - - - dc /mod=11 /x87=29 - ST1 ST0 - - - dc /mod=11 /x87=2a - ST2 ST0 - - - dc /mod=11 /x87=2b - ST3 ST0 - - - dc /mod=11 /x87=2c - ST4 ST0 - - - dc /mod=11 /x87=2d - ST5 ST0 - - - dc /mod=11 /x87=2e - ST6 ST0 - - - dc /mod=11 /x87=2f - ST7 ST0 - - - - - fsubp - X87 - - de /mod=11 /x87=28 - ST0 ST0 - - - de /mod=11 /x87=29 - ST1 ST0 - - - de /mod=11 /x87=2a - ST2 ST0 - - - de /mod=11 /x87=2b - ST3 ST0 - - - de /mod=11 /x87=2c - ST4 ST0 - - - de /mod=11 /x87=2d - ST5 ST0 - - - de /mod=11 /x87=2e - ST6 ST0 - - - de /mod=11 /x87=2f - ST7 ST0 - - - - - fsubr - X87 - - aso rexr rexx rexb - dc /mod=!11 /reg=5 - Mq - - - d8 /mod=11 /x87=28 - ST0 ST0 - - - d8 /mod=11 /x87=29 - ST0 ST1 - - - d8 /mod=11 /x87=2a - ST0 ST2 - - - d8 /mod=11 /x87=2b - ST0 ST3 - - - d8 /mod=11 /x87=2c - ST0 ST4 - - - d8 /mod=11 /x87=2d - ST0 ST5 - - - d8 /mod=11 /x87=2e - ST0 ST6 - - - d8 /mod=11 /x87=2f - ST0 ST7 - - - dc /mod=11 /x87=20 - ST0 ST0 - - - dc /mod=11 /x87=21 - ST1 ST0 - - - dc /mod=11 /x87=22 - ST2 ST0 - - - dc /mod=11 /x87=23 - ST3 ST0 - - - dc /mod=11 /x87=24 - ST4 ST0 - - - dc /mod=11 /x87=25 - ST5 ST0 - - - dc /mod=11 /x87=26 - ST6 ST0 - - - dc /mod=11 /x87=27 - ST7 ST0 - - - aso rexr rexx rexb - d8 /mod=!11 /reg=5 - Md - - - - - fsubrp - X87 - - de /mod=11 /x87=20 - ST0 ST0 - - - de /mod=11 /x87=21 - ST1 ST0 - - - de /mod=11 /x87=22 - ST2 ST0 - - - de /mod=11 /x87=23 - ST3 ST0 - - - de /mod=11 /x87=24 - ST4 ST0 - - - de /mod=11 /x87=25 - ST5 ST0 - - - de /mod=11 /x87=26 - ST6 ST0 - - - de /mod=11 /x87=27 - ST7 ST0 - - - - - ftst - X87 - - d9 /mod=11 /x87=24 - - - - - fucom - X87 - - dd /mod=11 /x87=20 - ST0 - - - dd /mod=11 /x87=21 - ST1 - - - dd /mod=11 /x87=22 - ST2 - - - dd /mod=11 /x87=23 - ST3 - - - dd /mod=11 /x87=24 - ST4 - - - dd /mod=11 /x87=25 - ST5 - - - dd /mod=11 /x87=26 - ST6 - - - dd /mod=11 /x87=27 - ST7 - - - - - fucomp - X87 - - dd /mod=11 /x87=28 - ST0 - - - dd /mod=11 /x87=29 - ST1 - - - dd /mod=11 /x87=2a - ST2 - - - dd /mod=11 /x87=2b - ST3 - - - dd /mod=11 /x87=2c - ST4 - - - dd /mod=11 /x87=2d - ST5 - - - dd /mod=11 /x87=2e - ST6 - - - dd /mod=11 /x87=2f - ST7 - - - - - fucompp - X87 - - da /mod=11 /x87=29 - - - - - fxam - X87 - - d9 /mod=11 /x87=25 - - - - - fxch - X87 - - d9 /mod=11 /x87=08 - ST0 ST0 - - - d9 /mod=11 /x87=09 - ST0 ST1 - - - d9 /mod=11 /x87=0a - ST0 ST2 - - - d9 /mod=11 /x87=0b - ST0 ST3 - - - d9 /mod=11 /x87=0c - ST0 ST4 - - - d9 /mod=11 /x87=0d - ST0 ST5 - - - d9 /mod=11 /x87=0e - ST0 ST6 - - - d9 /mod=11 /x87=0f - ST0 ST7 - - - - - fxch4 - X87 - - dd /mod=11 /x87=08 - ST0 - - - dd /mod=11 /x87=09 - ST1 - - - dd /mod=11 /x87=0a - ST2 - - - dd /mod=11 /x87=0b - ST3 - - - dd /mod=11 /x87=0c - ST4 - - - dd /mod=11 /x87=0d - ST5 - - - dd /mod=11 /x87=0e - ST6 - - - dd /mod=11 /x87=0f - ST7 - - - - - fxch7 - X87 - - df /mod=11 /x87=08 - ST0 - - - df /mod=11 /x87=09 - ST1 - - - df /mod=11 /x87=0a - ST2 - - - df /mod=11 /x87=0b - ST3 - - - df /mod=11 /x87=0c - ST4 - - - df /mod=11 /x87=0d - ST5 - - - df /mod=11 /x87=0e - ST6 - - - df /mod=11 /x87=0f - ST7 - - - - - fxrstor - - aso rexw rexr rexx rexb - 0f ae /mod=11 /reg=1 - M - - - - - fxsave - - aso rexw rexr rexx rexb - 0f ae /mod=11 /reg=0 - M - - - - - fpxtract - X87 - - d9 /mod=11 /x87=34 - - - - - fyl2x - X87 - - d9 /mod=11 /x87=31 - - - - - fyl2xp1 - X87 - - d9 /mod=11 /x87=39 - - - - - hlt - - f4 - - - - - idiv - - aso oso rexw rexr rexx rexb - f7 /reg=7 - Ev - - - aso rexw rexr rexx rexb - f6 /reg=7 - Eb - - - - - in - - e4 - AL Ib - - - oso - e5 - eAX Ib - - - ec - AL DX - - - oso - ed - eAX DX - - - - - imul - - aso oso rexw rexr rexx rexb - 0f af - Gv Ev - - - aso rexw rexr rexx rexb - f6 /reg=5 - Eb - - - aso oso rexw rexr rexx rexb - f7 /reg=5 - Ev - - - aso oso rexw rexr rexx rexb - 69 - Gv Ev Iz - sext - - - aso oso rexw rexr rexx rexb - 6b - Gv Ev Ib - sext - - - - - inc - - oso - 40 - eAX - - - oso - 41 - eCX - - - oso - 42 - eDX - - - oso - 43 - eBX - - - oso - 44 - eSP - - - oso - 45 - eBP - - - oso - 46 - eSI - - - oso - 47 - eDI - - - aso oso rexw rexr rexx rexb - ff /reg=0 - Ev - - - aso rexw rexr rexx rexb - fe /reg=0 - Eb - - - - - insb - - 6c - - - - - insw - - oso - 6d /o=16 - - - - - insd - - oso - 6d /o=32 - - - - - int1 - - f1 - - - - - int3 - - cc - - - - - int - - cd - Ib - - - - - into - - ce - inv64 - - - - - invd - - 0f 08 - - - - - invept - intel - - sse66 0f 38 80 /m=32 - Gd Mo - - - sse66 0f 38 80 /m=64 - Gq Mo - - - - - invlpg - - aso rexr rexx rexb - 0f 01 /reg=7 /mod=!11 - M - - - - - invlpga - amd - - 0f 01 /reg=3 /mod=11 /rm=7 - - - - - invvpid - intel - - sse66 0f 38 81 /m=32 - Gd Mo - - - sse66 0f 38 81 /m=64 - Gq Mo - - - - - iretw - - oso rexw - cf /o=16 - - - - - iretd - - oso rexw - cf /o=32 - - - - - iretq - - oso rexw - cf /o=64 - - - - - jo - - 70 - Jb - - - oso - 0f 80 - Jz - def64 depM - - - - - jno - - 71 - Jb - - - oso - 0f 81 - Jz - def64 depM - - - - - jb - - 72 - Jb - - - oso - 0f 82 - Jz - def64 depM - - - - - jae - - 73 - Jb - - - oso - 0f 83 - Jz - def64 depM - - - - - jz - - 74 - Jb - - - oso - 0f 84 - Jz - def64 depM - - - - - jnz - - 75 - Jb - - - oso - 0f 85 - Jz - def64 depM - - - - - jbe - - 76 - Jb - - - oso - 0f 86 - Jz - def64 depM - - - - - ja - - 77 - Jb - - - oso - 0f 87 - Jz - def64 depM - - - - - js - - 78 - Jb - - - oso - 0f 88 - Jz - def64 depM - - - - - jns - - 79 - Jb - - - oso - 0f 89 - Jz - def64 depM - - - - - jp - - 7a - Jb - - - oso - 0f 8a - Jz - def64 depM - - - - - jnp - - 7b - Jb - - - oso - 0f 8b - Jz - def64 depM - - - - - jl - - 7c - Jb - - - oso - 0f 8c - Jz - def64 depM - - - - - jge - - 7d - Jb - - - oso - 0f 8d - Jz - def64 depM - - - - - jle - - 7e - Jb - - - oso - 0f 8e - Jz - def64 depM - - - - - jg - - 7f - Jb - - - oso - 0f 8f - Jz - def64 depM - - - - - jcxz - - aso - e3 /a=16 - Jb - - - - - jecxz - - aso - e3 /a=32 - Jb - - - - - jrcxz - - aso - e3 /a=64 - Jb - - - - - jmp - - aso oso rexw rexr rexx rexb - ff /reg=4 - Ev - def64 depM - - - aso oso rexw rexr rexx rexb - ff /reg=5 - Ep - - - oso - e9 - Jz - def64 depM - cast - - - ea - Ap - inv64 - - - eb - Jb - - - - - lahf - - 9f - - - - - lar - - aso oso rexw rexr rexx rexb - 0f 02 - Gv Ew - - - - - lddqu - - aso rexr rexx rexb - ssef2 0f f0 - V M - - - - - ldmxcsr - - aso rexw rexr rexx rexb - 0f ae /reg=2 /mod=11 - Md - - - - - lds - - aso oso - c5 - Gv M - inv64 - - - - - lea - - aso oso rexw rexr rexx rexb - 8d - Gv M - - - - - les - - aso oso - c4 - Gv M - inv64 - - - - - lfs - - aso oso rexw rexr rexx rexb - 0f b4 - Gz M - - - - - lgs - - aso oso rexw rexr rexx rexb - 0f b5 - Gz M - - - - - lidt - - aso rexr rexx rexb - 0f 01 /reg=3 /mod=!11 - M - - - - - lss - - aso oso rexw rexr rexx rexb - 0f b2 - Gz M - - - - - leave - - c9 - - - - - lfence - - 0f ae /reg=5 /mod=11 /rm=0 - - - 0f ae /reg=5 /mod=11 /rm=1 - - - 0f ae /reg=5 /mod=11 /rm=2 - - - 0f ae /reg=5 /mod=11 /rm=3 - - - 0f ae /reg=5 /mod=11 /rm=4 - - - 0f ae /reg=5 /mod=11 /rm=5 - - - 0f ae /reg=5 /mod=11 /rm=6 - - - 0f ae /reg=5 /mod=11 /rm=7 - - - - - lgdt - - aso rexr rexx rexb - 0f 01 /reg=2 /mod=!11 - M - - - - - lldt - - aso rexr rexx rexb - 0f 00 /reg=2 - Ew - - - - - lmsw - - aso rexr rexx rexb - 0f 01 /reg=6 /mod=!11 - Ew - - - - - lock - - f0 - - - - - lodsb - - seg - ac - - - - - lodsw - - seg oso rexw - ad /o=16 - - - - - lodsd - - seg oso rexw - ad /o=32 - - - - - lodsq - - seg oso rexw - ad /o=64 - - - - - loopnz - - e0 - Jb - - - - - loope - - e1 - Jb - - - - - loop - - e2 - Jb - - - - - lsl - - aso oso rexw rexr rexx rexb - 0f 03 - Gv Ew - - - - - ltr - - aso rexr rexx rexb - 0f 00 /reg=3 - Ew - - - - - maskmovq - - aso rexr rexx rexb - 0f f7 - P PR - - - - - maxpd - - aso rexr rexx rexb - sse66 0f 5f - V W - - - - - maxps - - aso rexr rexx rexb - 0f 5f - V W - - - - - maxsd - - aso rexr rexx rexb - ssef2 0f 5f - V W - - - - - maxss - - aso rexr rexx rexb - ssef3 0f 5f - V W - - - - - mfence - - 0f ae /reg=6 /mod=11 /rm=0 - - - 0f ae /reg=6 /mod=11 /rm=1 - - - 0f ae /reg=6 /mod=11 /rm=2 - - - 0f ae /reg=6 /mod=11 /rm=3 - - - 0f ae /reg=6 /mod=11 /rm=4 - - - 0f ae /reg=6 /mod=11 /rm=5 - - - 0f ae /reg=6 /mod=11 /rm=6 - - - 0f ae /reg=6 /mod=11 /rm=7 - - - - - minpd - - aso rexr rexx rexb - sse66 0f 5d - V W - - - - - minps - - aso rexr rexx rexb - 0f 5d - V W - - - - - minsd - - aso rexr rexx rexb - ssef2 0f 5d - V W - - - - - minss - - aso rexr rexx rexb - ssef3 0f 5d - V W - - - - - monitor - - 0f 01 /reg=1 /mod=11 /rm=0 - - - - - montmul - - 0f a6 /mod=11 /rm=0 /reg=0 - - - - - mov - - aso rexw rexr rexx rexb - c6 /reg=0 - Eb Ib - - - aso oso rexw rexr rexx rexb - c7 /reg=0 - Ev Iz - - - aso rexr rexx rexb - 88 - Eb Gb - - - aso oso rexw rexr rexx rexb - 89 - Ev Gv - - - aso rexr rexx rexb - 8a - Gb Eb - - - aso oso rexw rexr rexx rexb - 8b - Gv Ev - - - aso oso rexr rexx rexb - 8c - Ev S - - - aso oso rexr rexx rexb - 8e - S Ev - - - a0 - AL Ob - - - aso oso rexw - a1 - rAX Ov - - - a2 - Ob AL - - - aso oso rexw - a3 - Ov rAX - - - rexb - b0 - ALr8b Ib - - - rexb - b1 - CLr9b Ib - - - rexb - b2 - DLr10b Ib - - - rexb - b3 - BLr11b Ib - - - rexb - b4 - AHr12b Ib - - - rexb - b5 - CHr13b Ib - - - rexb - b6 - DHr14b Ib - - - rexb - b7 - BHr15b Ib - - - oso rexw rexb - b8 - rAXr8 Iv - - - oso rexw rexb - b9 - rCXr9 Iv - - - oso rexw rexb - ba - rDXr10 Iv - - - oso rexw rexb - bb - rBXr11 Iv - - - oso rexw rexb - bc - rSPr12 Iv - - - oso rexw rexb - bd - rBPr13 Iv - - - oso rexw rexb - be - rSIr14 Iv - - - oso rexw rexb - bf - rDIr15 Iv - - - rexr - 0f 20 - R C - - - rexr - 0f 21 - R D - - - rexr - 0f 22 - C R - - - rexr - 0f 23 - D R - - - - - movapd - - aso rexr rexx rexb - sse66 0f 28 - V W - - - aso rexr rexx rexb - sse66 0f 29 - W V - - - - - movaps - - aso rexr rexx rexb - 0f 28 - V W - - - aso rexr rexx rexb - 0f 29 - W V - - - - - movd - - aso rexw rexr rexx rexb - sse66 0f 6e - V Ex - - - aso rexr rexx rexb - 0f 6e - P Ex - - - aso rexw rexr rexx rexb - sse66 0f 7e - Ex V - - - aso rexr rexx rexb - 0f 7e - Ex P - - - - - movhpd - - aso rexr rexx rexb - sse66 0f 16 /mod=!11 - V M - - - aso rexr rexx rexb - sse66 0f 17 - M V - - - - - movhps - - aso rexr rexx rexb - 0f 16 /mod=!11 - V M - - - aso rexr rexx rexb - 0f 17 - M V - - - - - movlhps - - aso rexr rexx rexb - 0f 16 /mod=11 - V VR - - - - - movlpd - - aso rexr rexx rexb - sse66 0f 12 /mod=!11 - V M - - - aso rexr rexx rexb - sse66 0f 13 - M V - - - - - movlps - - aso rexr rexx rexb - 0f 12 /mod=!11 - V M - - - aso rexr rexx rexb - 0f 13 - M V - - - - - movhlps - - aso rexr rexx rexb - 0f 12 /mod=11 - V VR - - - - - movmskpd - - oso rexr rexb - sse66 0f 50 - Gd VR - - - - - movmskps - - oso rexr rexb - 0f 50 - Gd VR - - - - - movntdq - - aso rexr rexx rexb - sse66 0f e7 - M V - - - - - movnti - - aso rexw rexr rexx rexb - 0f c3 - M Gy - - - - - movntpd - - aso rexr rexx rexb - sse66 0f 2b - M V - - - - - movntps - - aso rexr rexx rexb - 0f 2b - M V - - - - - movntq - - 0f e7 - M P - - - - - movq - - aso rexr rexx rexb - 0f 6f - P Q - - - aso rexr rexx rexb - sse66 0f d6 - W V - - - aso rexr rexx rexb - ssef3 0f 7e - V W - - - aso rexr rexx rexb - 0f 7f - Q P - - - - - movsb - - seg - a4 - - - - - movsw - - seg oso rexw - a5 /o=16 - - - - - movsd - - seg oso rexw - a5 /o=32 - - - aso rexr rexx rexb - ssef2 0f 10 - V W - - - aso rexr rexx rexb - ssef2 0f 11 - W V - - - - - movsq - - seg oso rexw - a5 /o=64 - - - - - movss - - aso rexr rexx rexb - ssef3 0f 10 - V W - - - aso rexr rexx rexb - ssef3 0f 11 - W V - - - - - movsx - - aso oso rexw rexr rexx rexb - 0f be - Gv Eb - - - aso oso rexw rexr rexx rexb - 0f bf - Gv Ew - - - - - movupd - - aso rexr rexx rexb - sse66 0f 10 - V W - - - aso rexr rexx rexb - sse66 0f 11 - W V - - - - - movups - - aso rexr rexx rexb - 0f 10 - V W - - - aso rexr rexx rexb - 0f 11 - W V - - - - - movzx - - aso oso rexw rexr rexx rexb - 0f b6 - Gv Eb - - - aso oso rexw rexr rexx rexb - 0f b7 - Gv Ew - - - - - mul - - aso rexw rexr rexx rexb - f6 /reg=4 - Eb - - - aso oso rexw rexr rexx rexb - f7 /reg=4 - Ev - - - - - mulpd - - aso rexr rexx rexb - sse66 0f 59 - V W - - - - - mulps - - aso rexr rexx rexb - 0f 59 - V W - - - - - mulsd - - aso rexr rexx rexb - ssef2 0f 59 - V W - - - - - mulss - - aso rexr rexx rexb - ssef3 0f 59 - V W - - - - - mwait - - 0f 01 /reg=1 /mod=11 /rm=1 - - - - - neg - - aso rexw rexr rexx rexb - f6 /reg=3 - Eb - - - aso oso rexw rexr rexx rexb - f7 /reg=3 - Ev - - - - - nop - - 90 - - - aso rexr rexx rexb - 0f 19 - M - - - aso rexr rexx rexb - 0f 1a - M - - - aso rexr rexx rexb - 0f 1b - M - - - aso rexr rexx rexb - 0f 1c - M - - - aso rexr rexx rexb - 0f 1d - M - - - aso rexr rexx rexb - 0f 1e - M - - - aso rexr rexx rexb - 0f 1f - M - - - - - not - - aso rexw rexr rexx rexb - f6 /reg=2 - Eb - - - aso oso rexw rexr rexx rexb - f7 /reg=2 - Ev - - - - - or - - aso rexr rexx rexb - 08 - Eb Gb - - - aso oso rexw rexr rexx rexb - 09 - Ev Gv - - - aso rexr rexx rexb - 0a - Gb Eb - - - aso oso rexw rexr rexx rexb - 0b - Gv Ev - - - 0c - AL Ib - - - oso rexw - 0d - rAX Iz - sext - - - aso rexr rexx rexb - 80 /reg=1 - Eb Ib - - - aso oso rexw rexr rexx rexb - 81 /reg=1 - Ev Iz - sext - - - aso rexr rexx rexb - 82 /reg=1 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 83 /reg=1 - Ev Ib - sext - - - - - orpd - - aso rexr rexx rexb - sse66 0f 56 - V W - - - - - orps - - aso rexr rexx rexb - 0f 56 - V W - - - - - out - - e6 - Ib AL - - - oso - e7 - Ib eAX - - - ee - DX AL - - - oso - ef - DX eAX - - - - - outsb - - 6e - - - - - outsw - - oso - 6f /o=16 - - - - - outsd - - oso - 6f /o=32 - - - - - outsq - - oso - 6f /o=64 - - - - - packsswb - - aso rexr rexx rexb - sse66 0f 63 - V W - - - aso rexr rexx rexb - 0f 63 - P Q - - - - - packssdw - - aso rexr rexx rexb - sse66 0f 6b - V W - - - aso rexr rexx rexb - 0f 6b - P Q - - - - - packuswb - - aso rexr rexx rexb - sse66 0f 67 - V W - - - aso rexr rexx rexb - 0f 67 - P Q - - - - - paddb - - aso rexr rexx rexb - sse66 0f fc - V W - - - aso rexr rexx rexb - 0f fc - P Q - - - - - paddw - - aso rexr rexx rexb - 0f fd - P Q - - - aso rexr rexx rexb - sse66 0f fd - V W - - - - - paddd - - aso rexr rexx rexb - 0f fe - P Q - - - aso rexr rexx rexb - sse66 0f fe - V W - - - - - - paddsb - - aso rexr rexx rexb - 0f ec - P Q - - - aso rexr rexx rexb - sse66 0f ec - V W - - - - - paddsw - - aso rexr rexx rexb - 0f ed - P Q - - - aso rexr rexx rexb - sse66 0f ed - V W - - - - - paddusb - - aso rexr rexx rexb - 0f dc - P Q - - - aso rexr rexx rexb - sse66 0f dc - V W - - - - - paddusw - - aso rexr rexx rexb - 0f dd - P Q - - - aso rexr rexx rexb - sse66 0f dd - V W - - - - - pand - - aso rexr rexx rexb - sse66 0f db - V W - - - aso rexr rexx rexb - 0f db - P Q - - - - - pandn - - aso rexr rexx rexb - sse66 0f df - V W - - - aso rexr rexx rexb - 0f df - P Q - - - - - pavgb - - aso rexr rexx rexb - sse66 0f e0 - V W - - - aso rexr rexx rexb - 0f e0 - P Q - - - - - pavgw - - aso rexr rexx rexb - sse66 0f e3 - V W - - - aso rexr rexx rexb - 0f e3 - P Q - - - - - pcmpeqb - - aso rexr rexx rexb - 0f 74 - P Q - - - aso rexr rexx rexb - sse66 0f 74 - V W - - - - - pcmpeqw - - aso rexr rexx rexb - 0f 75 - P Q - - - aso rexr rexx rexb - sse66 0f 75 - V W - - - - - pcmpeqd - - aso rexr rexx rexb - 0f 76 - P Q - - - aso rexr rexx rexb - sse66 0f 76 - V W - - - - - pcmpgtb - - aso rexr rexx rexb - sse66 0f 64 - V W - - - aso rexr rexx rexb - 0f 64 - P Q - - - - - pcmpgtw - - aso rexr rexx rexb - sse66 0f 65 - V W - - - aso rexr rexx rexb - 0f 65 - P Q - - - - - pcmpgtd - - aso rexr rexx rexb - sse66 0f 66 - V W - - - aso rexr rexx rexb - 0f 66 - P Q - - - - - pextrb - - aso rexr rexb - sse66 0f 3a 14 - MbRv V Ib - def64 - - - - - pextrd - - aso rexr rexw rexb - sse66 0f 3a 16 /o=16 - Ev V Ib - - - aso rexr rexw rexb - sse66 0f 3a 16 /o=32 - Ev V Ib - - - - - pextrq - - aso rexr rexw rexb - sse66 0f 3a 16 /o=64 - Ev V Ib - def64 - - - - - pextrw - - aso rexr rexb - sse66 0f c5 - Gd VR Ib - - - aso oso rexw rexr rexx rexb - 0f c5 - Gd PR Ib - - - - - pinsrw - - aso oso rexw rexr rexx rexb - 0f c4 - P Ew Ib - - - aso rexw rexr rexx rexb - sse66 0f c4 - V Ew Ib - - - - - pmaddwd - - aso rexr rexx rexb - 0f f5 - P Q - - - aso rexr rexx rexb - sse66 0f f5 - V W - - - - - pmaxsw - - aso rexr rexx rexb - sse66 0f ee - V W - - - aso rexr rexx rexb - 0f ee - P Q - - - - - pmaxub - - aso rexr rexx rexb - 0f de - P Q - - - aso rexr rexx rexb - sse66 0f de - V W - - - - - pminsw - - aso rexr rexx rexb - sse66 0f ea - V W - - - aso rexr rexx rexb - 0f ea - P Q - - - - - pminub - - aso rexr rexx rexb - sse66 0f da - V W - - - aso rexr rexx rexb - 0f da - P Q - - - - - pmovmskb - - rexr rexb - sse66 0f d7 - Gd VR - - - oso rexr rexb - 0f d7 - Gd PR - - - - - pmulhuw - - aso rexr rexx rexb - 0f e4 - P Q - - - aso rexr rexx rexb - sse66 0f e4 - V W - - - - - pmulhw - - aso rexr rexx rexb - sse66 0f e5 - V W - - - aso rexr rexx rexb - 0f e5 - P Q - - - - - pmullw - - aso rexr rexx rexb - 0f d5 - P Q - - - aso rexr rexx rexb - sse66 0f d5 - V W - - - - - pop - - 07 - ES - inv64 - - - 17 - SS - inv64 - - - 1f - DS - inv64 - - - 0f a9 - GS - - - 0f a1 - FS - - - oso rexb - 58 - rAXr8 - def64 depM - - - oso rexb - 59 - rCXr9 - def64 depM - - - oso rexb - 5a - rDXr10 - def64 depM - - - oso rexb - 5b - rBXr11 - def64 depM - - - oso rexb - 5c - rSPr12 - def64 depM - - - oso rexb - 5d - rBPr13 - def64 depM - - - oso rexb - 5e - rSIr14 - def64 depM - - - oso rexb - 5f - rDIr15 - def64 depM - - - aso oso rexw rexr rexx rexb - 8f /reg=0 - Ev - def64 depM - - - - - popa - - oso - 61 /o=16 - inv64 - - - - - popad - - oso - 61 /o=32 - inv64 - - - - - popfw - - oso - 9d /m=32 /o=16 - def64 depM - - - oso - 9d /m=16 /o=16 - def64 depM - - - - - popfd - - oso - 9d /m=16 /o=32 - def64 depM - - - oso - 9d /m=32 /o=32 - def64 depM - - - - - popfq - - oso - 9d /m=64 /o=64 - def64 depM - - - - - por - - aso rexr rexx rexb - sse66 0f eb - V W - - - aso rexr rexx rexb - 0f eb - P Q - - - - - prefetch - - aso rexw rexr rexx rexb - 0f 0d /reg=0 - M - - - aso rexw rexr rexx rexb - 0f 0d /reg=1 - M - - - aso rexw rexr rexx rexb - 0f 0d /reg=2 - M - - - aso rexw rexr rexx rexb - 0f 0d /reg=3 - M - - - aso rexw rexr rexx rexb - 0f 0d /reg=4 - M - - - aso rexw rexr rexx rexb - 0f 0d /reg=5 - M - - - aso rexw rexr rexx rexb - 0f 0d /reg=6 - M - - - aso rexw rexr rexx rexb - 0f 0d /reg=7 - M - - - - - prefetchnta - - aso rexw rexr rexx rexb - 0f 18 /reg=0 - M - - - - - prefetcht0 - - aso rexw rexr rexx rexb - 0f 18 /reg=1 - M - - - - - prefetcht1 - - aso rexw rexr rexx rexb - 0f 18 /reg=2 - M - - - - - prefetcht2 - - aso rexw rexr rexx rexb - 0f 18 /reg=3 - M - - - - - psadbw - - aso rexr rexx rexb - sse66 0f f6 - V W - - - aso rexr rexx rexb - 0f f6 - P Q - - - - - pshufw - - aso rexr rexx rexb - 0f 70 - P Q Ib - - - - - psllw - - aso rexr rexx rexb - sse66 0f f1 - V W - - - aso rexr rexx rexb - 0f f1 - P Q - - - rexb - sse66 0f 71 /reg=6 - VR Ib - - - 0f 71 /reg=6 - PR Ib - - - - - pslld - - aso rexr rexx rexb - sse66 0f f2 - V W - - - aso rexr rexx rexb - 0f f2 - P Q - - - rexb - sse66 0f 72 /reg=6 - VR Ib - - - 0f 72 /reg=6 - PR Ib - - - - - psllq - - aso rexr rexx rexb - sse66 0f f3 - V W - - - aso rexr rexx rexb - 0f f3 - P Q - - - rexb - sse66 0f 73 /reg=6 - VR Ib - - - 0f 73 /reg=6 - PR Ib - - - - - psraw - - aso rexr rexx rexb - 0f e1 - P Q - - - aso rexr rexx rexb - sse66 0f e1 - V W - - - rexb - sse66 0f 71 /reg=4 - VR Ib - - - 0f 71 /reg=4 - PR Ib - - - - - psrad - - 0f 72 /reg=4 - PR Ib - - - aso rexr rexx rexb - sse66 0f e2 - V W - - - aso rexr rexx rexb - 0f e2 - P Q - - - rexb - sse66 0f 72 /reg=4 - VR Ib - - - - - psrlw - - 0f 71 /reg=2 - PR Ib - - - aso rexr rexx rexb - 0f d1 - P Q - - - aso rexr rexx rexb - sse66 0f d1 - V W - - - rexb - sse66 0f 71 /reg=2 - VR Ib - - - - - psrld - - 0f 72 /reg=2 - PR Ib - - - aso rexr rexx rexb - 0f d2 - P Q - - - aso rexr rexx rexb - sse66 0f d2 - V W - - - rexb - sse66 0f 72 /reg=2 - VR Ib - - - - - psrlq - - 0f 73 /reg=2 - PR Ib - - - aso rexr rexx rexb - 0f d3 - P Q - - - aso rexr rexx rexb - sse66 0f d3 - V W - - - rexb - sse66 0f 73 /reg=2 - VR Ib - - - - - psubb - - aso rexr rexx rexb - sse66 0f f8 - V W - - - aso rexr rexx rexb - 0f f8 - P Q - - - - - psubw - - aso rexr rexx rexb - sse66 0f f9 - V W - - - aso rexr rexx rexb - 0f f9 - P Q - - - - - psubd - - aso rexr rexx rexb - 0f fa - P Q - - - aso rexr rexx rexb - sse66 0f fa - V W - - - - - psubsb - - aso rexr rexx rexb - 0f e8 - P Q - - - aso rexr rexx rexb - sse66 0f e8 - V W - - - - - psubsw - - aso rexr rexx rexb - 0f e9 - P Q - - - aso rexr rexx rexb - sse66 0f e9 - V W - - - - - psubusb - - aso rexr rexx rexb - 0f d8 - P Q - - - aso rexr rexx rexb - sse66 0f d8 - V W - - - - - psubusw - - aso rexr rexx rexb - 0f d9 - P Q - - - aso rexr rexx rexb - sse66 0f d9 - V W - - - - - punpckhbw - - aso rexr rexx rexb - sse66 0f 68 - V W - - - aso rexr rexx rexb - 0f 68 - P Q - - - - - punpckhwd - - aso rexr rexx rexb - sse66 0f 69 - V W - - - aso rexr rexx rexb - 0f 69 - P Q - - - - - punpckhdq - - aso rexr rexx rexb - sse66 0f 6a - V W - - - aso rexr rexx rexb - 0f 6a - P Q - - - - - punpcklbw - - aso rexr rexx rexb - sse66 0f 60 - V W - - - aso rexr rexx rexb - 0f 60 - P Q - - - - - punpcklwd - - aso rexr rexx rexb - sse66 0f 61 - V W - - - aso rexr rexx rexb - 0f 61 - P Q - - - - - punpckldq - - aso rexr rexx rexb - sse66 0f 62 - V W - - - aso rexr rexx rexb - 0f 62 - P Q - - - - - pi2fw - - 0f 0f /3dnow=0c - P Q - - - - - pi2fd - - 0f 0f /3dnow=0d - P Q - - - - - pf2iw - - 0f 0f /3dnow=1c - P Q - - - - - pf2id - - 0f 0f /3dnow=1d - P Q - - - - - pfnacc - - 0f 0f /3dnow=8a - P Q - - - - - pfpnacc - - 0f 0f /3dnow=8e - P Q - - - - - pfcmpge - - 0f 0f /3dnow=90 - P Q - - - - - pfmin - - 0f 0f /3dnow=94 - P Q - - - - - pfrcp - - 0f 0f /3dnow=96 - P Q - - - - - pfrsqrt - - 0f 0f /3dnow=97 - P Q - - - - - pfsub - - 0f 0f /3dnow=9a - P Q - - - - - pfadd - - 0f 0f /3dnow=9e - P Q - - - - - pfcmpgt - - 0f 0f /3dnow=a0 - P Q - - - - - pfmax - - 0f 0f /3dnow=a4 - P Q - - - - - pfrcpit1 - - 0f 0f /3dnow=a6 - P Q - - - - - pfrsqit1 - - 0f 0f /3dnow=a7 - P Q - - - - - pfsubr - - 0f 0f /3dnow=aa - P Q - - - - - pfacc - - 0f 0f /3dnow=ae - P Q - - - - - pfcmpeq - - 0f 0f /3dnow=b0 - P Q - - - - - pfmul - - 0f 0f /3dnow=b4 - P Q - - - - - pfrcpit2 - - 0f 0f /3dnow=b6 - P Q - - - - - pmulhrw - - 0f 0f /3dnow=b7 - P Q - - - - - pswapd - - 0f 0f /3dnow=bb - P Q - - - - - pavgusb - - 0f 0f /3dnow=bf - P Q - - - - - push - - 06 - ES - inv64 - - - 0e - CS - inv64 - - - 16 - SS - inv64 - - - 1e - DS - inv64 - - - 0f a8 - GS - - - 0f a0 - FS - - - oso rexb - 50 - rAXr8 - def64 depM - - - oso rexb - 51 - rCXr9 - def64 depM - - - oso rexb - 52 - rDXr10 - def64 depM - - - oso rexb - 53 - rBXr11 - def64 depM - - - oso rexb - 54 - rSPr12 - def64 depM - - - oso rexb - 55 - rBPr13 - def64 depM - - - oso rexb - 56 - rSIr14 - def64 depM - - - oso rexb - 57 - rDIr15 - def64 depM - - - oso - 68 - Iz - cast - - - aso oso rexw rexr rexx rexb - ff /reg=6 - Ev - def64 - - - 6a - Ib - sext - - - - - pusha - - oso - 60 /o=16 - inv64 - - - - - pushad - - oso - 60 /o=32 - inv64 - - - - - pushfw - - oso - 9c /m=32 /o=16 - def64 - - - oso - 9c /m=16 /o=16 - def64 - - - oso rexw - 9c /m=64 /o=16 - def64 - - - - - pushfd - - oso - 9c /m=16 /o=32 - def64 - - - oso - 9c /m=32 /o=32 - def64 - - - - - pushfq - - oso rexw - 9c /m=64 /o=32 - def64 - - - oso rexw - 9c /m=64 /o=64 - def64 - - - - - pxor - - aso rexr rexx rexb - sse66 0f ef - V W - - - aso rexr rexx rexb - 0f ef - P Q - - - - - rcl - - aso rexw rexr rexx rexb - c0 /reg=2 - Eb Ib - - - aso oso rexw rexr rexx rexb - c1 /reg=2 - Ev Ib - - - aso rexw rexr rexx rexb - d0 /reg=2 - Eb I1 - - - aso rexw rexr rexx rexb - d2 /reg=2 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d3 /reg=2 - Ev CL - cast - - - aso oso rexw rexr rexx rexb - d1 /reg=2 - Ev I1 - - - - - rcr - - aso rexw rexr rexx rexb - d0 /reg=3 - Eb I1 - - - aso oso rexw rexr rexx rexb - c1 /reg=3 - Ev Ib - - - aso rexw rexr rexx rexb - c0 /reg=3 - Eb Ib - - - aso oso rexw rexr rexx rexb - d1 /reg=3 - Ev I1 - - - aso rexw rexr rexx rexb - d2 /reg=3 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d3 /reg=3 - Ev CL - cast - - - - - rol - - aso rexw rexr rexx rexb - c0 /reg=0 - Eb Ib - - - aso rexw rexr rexx rexb - d0 /reg=0 - Eb I1 - - - aso oso rexw rexr rexx rexb - d1 /reg=0 - Ev I1 - - - aso rexw rexr rexx rexb - d2 /reg=0 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d3 /reg=0 - Ev CL - cast - - - aso oso rexw rexr rexx rexb - c1 /reg=0 - Ev Ib - - - - - ror - - aso rexw rexr rexx rexb - d0 /reg=1 - Eb I1 - - - aso rexw rexr rexx rexb - c0 /reg=1 - Eb Ib - - - aso oso rexw rexr rexx rexb - c1 /reg=1 - Ev Ib - - - aso oso rexw rexr rexx rexb - d1 /reg=1 - Ev I1 - - - aso rexw rexr rexx rexb - d2 /reg=1 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d3 /reg=1 - Ev CL - cast - - - - - rcpps - - aso rexr rexx rexb - 0f 53 - V W - - - - - rcpss - - aso rexr rexx rexb - ssef3 0f 53 - V W - - - - - rdmsr - - 0f 32 - - - - - rdpmc - - 0f 33 - - - - - rdtsc - - 0f 31 - - - - - rdtscp - amd - - 0f 01 /reg=7 /mod=11 /rm=1 - - - - - repne - - f2 - - - - - rep - - f3 - - - - - ret - - c2 - Iw - - - c3 - - - - - retf - - ca - Iw - - - cb - - - - - rsm - - 0f aa - - - - - rsqrtps - - aso rexr rexx rexb - 0f 52 - V W - - - - - rsqrtss - - aso rexr rexx rexb - ssef3 0f 52 - V W - - - - - sahf - - 9e - - - - - sal - - - - salc - - d6 - inv64 - - - - - sar - - aso oso rexw rexr rexx rexb - d1 /reg=7 - Ev I1 - - - aso rexw rexr rexx rexb - c0 /reg=7 - Eb Ib - - - aso rexw rexr rexx rexb - d0 /reg=7 - Eb I1 - - - aso oso rexw rexr rexx rexb - c1 /reg=7 - Ev Ib - - - aso rexw rexr rexx rexb - d2 /reg=7 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d3 /reg=7 - Ev CL - cast - - - - - shl - - aso rexw rexr rexx rexb - c0 /reg=6 - Eb Ib - - - aso oso rexw rexr rexx rexb - c1 /reg=6 - Ev Ib - - - aso rexw rexr rexx rexb - d0 /reg=6 - Eb I1 - - - aso rexw rexr rexx rexb - d2 /reg=6 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d3 /reg=6 - Ev CL - cast - - - aso oso rexw rexr rexx rexb - c1 /reg=4 - Ev Ib - - - aso rexr rexx rexb - d2 /reg=4 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d1 /reg=4 - Ev I1 - - - aso rexw rexr rexx rexb - d0 /reg=4 - Eb I1 - - - aso rexw rexr rexx rexb - c0 /reg=4 - Eb Ib - - - aso oso rexw rexr rexx rexb - d3 /reg=4 - Ev CL - - - aso oso rexw rexr rexx rexb - d1 /reg=6 - Ev I1 - - - - - shr - - aso oso rexw rexr rexx rexb - c1 /reg=5 - Ev Ib - - - aso rexw rexr rexx rexb - d2 /reg=5 - Eb CL - cast - - - aso oso rexw rexr rexx rexb - d1 /reg=5 - Ev I1 - - - aso rexw rexr rexx rexb - d0 /reg=5 - Eb I1 - - - aso rexw rexr rexx rexb - c0 /reg=5 - Eb Ib - - - aso oso rexw rexr rexx rexb - d3 /reg=5 - Ev CL - cast - - - - - sbb - - aso rexr rexx rexb - 18 - Eb Gb - - - aso oso rexw rexr rexx rexb - 19 - Ev Gv - - - aso rexr rexx rexb - 1a - Gb Eb - - - aso oso rexw rexr rexx rexb - 1b - Gv Ev - - - 1c - AL Ib - - - oso rexw - 1d - rAX Iz - sext - - - aso rexr rexx rexb - 80 /reg=3 - Eb Ib - - - aso oso rexw rexr rexx rexb - 81 /reg=3 - Ev Iz - sext - - - aso rexr rexx rexb - 82 /reg=3 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 83 /reg=3 - Ev Ib - sext - - - - - scasb - - ae - - - - - scasw - - oso rexw - af /o=16 - - - - - scasd - - oso rexw - af /o=32 - - - - - scasq - - oso rexw - af /o=64 - - - - - seto - - aso rexr rexx rexb - 0f 90 - Eb - - - - - setno - - aso rexr rexx rexb - 0f 91 - Eb - - - - - setb - - aso rexr rexx rexb - 0f 92 - Eb - - - - - setnb - - aso rexr rexx rexb - 0f 93 - Eb - - - - - setz - - aso rexr rexx rexb - 0f 94 - Eb - - - - - setnz - - aso rexr rexx rexb - 0f 95 - Eb - - - - - setbe - - aso rexr rexx rexb - 0f 96 - Eb - - - - - seta - - aso rexr rexx rexb - 0f 97 - Eb - - - - - sets - - aso rexr rexx rexb - 0f 98 - Eb - - - - - setns - - aso rexr rexx rexb - 0f 99 - Eb - - - - - setp - - aso rexr rexx rexb - 0f 9a - Eb - - - - - setnp - - aso rexr rexx rexb - 0f 9b - Eb - - - - - setl - - aso rexr rexx rexb - 0f 9c - Eb - - - - - setge - - aso rexr rexx rexb - 0f 9d - Eb - - - - - setle - - aso rexr rexx rexb - 0f 9e - Eb - - - - - setg - - aso rexr rexx rexb - 0f 9f - Eb - - - - - sfence - - 0f ae /reg=7 /mod=11 /rm=0 - - - 0f ae /reg=7 /mod=11 /rm=1 - - - 0f ae /reg=7 /mod=11 /rm=2 - - - 0f ae /reg=7 /mod=11 /rm=3 - - - 0f ae /reg=7 /mod=11 /rm=4 - - - 0f ae /reg=7 /mod=11 /rm=5 - - - 0f ae /reg=7 /mod=11 /rm=6 - - - 0f ae /reg=7 /mod=11 /rm=7 - - - - - sgdt - - aso rexr rexx rexb - 0f 01 /reg=0 /mod=!11 - M - - - - - shld - - aso oso rexw rexr rexx rexb - 0f a4 - Ev Gv Ib - - - aso oso rexw rexr rexx rexb - 0f a5 - Ev Gv CL - - - - - shrd - - aso oso rexw rexr rexx rexb - 0f ac - Ev Gv Ib - - - aso oso rexw rexr rexx rexb - 0f ad - Ev Gv CL - - - - - shufpd - - aso rexr rexx rexb - sse66 0f c6 - V W Ib - - - - - shufps - - aso rexr rexx rexb - 0f c6 - V W Ib - - - - - sidt - - aso rexr rexx rexb - 0f 01 /reg=1 /mod=!11 - M - - - - - sldt - - aso oso rexr rexx rexb - 0f 00 /reg=0 - MwRv - - - - - smsw - - aso rexr rexx rexb - 0f 01 /reg=4 /mod=!11 - M - - - - - sqrtps - - aso rexr rexx rexb - 0f 51 - V W - - - - - sqrtpd - - aso rexr rexx rexb - sse66 0f 51 - V W - - - - - sqrtsd - - aso rexr rexx rexb - ssef2 0f 51 - V W - - - - - sqrtss - - aso rexr rexx rexb - ssef3 0f 51 - V W - - - - - stc - - f9 - - - - - std - - fd - - - - - stgi - amd - - 0f 01 /reg=3 /mod=11 /rm=4 - - - - - sti - - fb - - - - - skinit - amd - - 0f 01 /reg=3 /mod=11 /rm=6 - - - - - stmxcsr - - aso rexw rexr rexx rexb - 0f ae /mod=11 /reg=3 - Md - - - - - stosb - - seg - aa - - - - - stosw - - seg oso rexw - ab /o=16 - - - - - stosd - - seg oso rexw - ab /o=32 - - - - - stosq - - seg oso rexw - ab /o=64 - - - - - str - - aso oso rexr rexx rexb - 0f 00 /reg=1 - Ev - - - - - sub - - aso rexr rexx rexb - 28 - Eb Gb - - - aso oso rexw rexr rexx rexb - 29 - Ev Gv - - - aso rexr rexx rexb - 2a - Gb Eb - - - aso oso rexw rexr rexx rexb - 2b - Gv Ev - - - 2c - AL Ib - - - oso rexw - 2d - rAX Iz - sext - - - aso rexr rexx rexb - 80 /reg=5 - Eb Ib - - - aso oso rexw rexr rexx rexb - 81 /reg=5 - Ev Iz - sext - - - aso rexr rexx rexb - 82 /reg=5 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 83 /reg=5 - Ev Ib - sext - - - - - subpd - - aso rexr rexx rexb - sse66 0f 5c - V W - - - - - subps - - aso rexr rexx rexb - 0f 5c - V W - - - - - subsd - - aso rexr rexx rexb - ssef2 0f 5c - V W - - - - - subss - - aso rexr rexx rexb - ssef3 0f 5c - V W - - - - - swapgs - - 0f 01 /reg=7 /mod=11 /rm=0 - - - - - syscall - - 0f 05 - - - - - sysenter - - 0f 34 - inv64 - - - - - sysexit - - 0f 35 - - - - - sysret - - 0f 07 - - - - - test - - aso rexw rexr rexx rexb - f6 /reg=0 - Eb Ib - - - aso rexr rexx rexb - 84 - Eb Gb - - - aso oso rexw rexr rexx rexb - 85 - Ev Gv - - - a8 - AL Ib - - - oso rexw - a9 - rAX Iz - sext - - - aso rexw rexr rexx rexb - f6 /reg=1 - Eb Ib - - - aso oso rexw rexr rexx rexb - f7 /reg=0 - Ev Iz - sext - - - aso oso rexw rexr rexx rexb - f7 /reg=1 - Ev Iz - sext - - - - - ucomisd - - aso rexr rexx rexb - sse66 0f 2e - V W - - - - - ucomiss - - aso rexr rexx rexb - 0f 2e - V W - - - - - ud2 - - 0f 0b - - - - - unpckhpd - - aso rexr rexx rexb - sse66 0f 15 - V W - - - - - unpckhps - - aso rexr rexx rexb - 0f 15 - V W - - - - - unpcklps - - aso rexr rexx rexb - 0f 14 - V W - - - - - unpcklpd - - aso rexr rexx rexb - sse66 0f 14 - V W - - - - - verr - - aso rexr rexx rexb - 0f 00 /reg=4 - Ew - - - - - verw - - aso rexr rexx rexb - 0f 00 /reg=5 - Ew - - - - - vmcall - intel - - 0f 01 /reg=0 /mod=11 /rm=1 - - - - - vmclear - intel - - aso rexr rexx rexb - sse66 0f c7 /reg=6 - Mq - - - - - vmxon - intel - - aso rexr rexx rexb - ssef3 0f c7 /reg=6 - Mq - - - - - vmptrld - intel - - aso rexr rexx rexb - 0f c7 /reg=6 - Mq - - - - - vmptrst - intel - - aso rexr rexx rexb - 0f c7 /reg=7 - Mq - - - - - vmlaunch - intel - - 0f 01 /reg=0 /mod=11 /rm=2 - - - - - vmresume - intel - - 0f 01 /reg=0 /mod=11 /rm=3 - - - - - vmxoff - intel - - 0f 01 /reg=0 /mod=11 /rm=4 - - - - - vmread - intel - - aso rexr rexx rexb - 0f 78 /m=16 - Ed Gd - def64 - - - aso rexr rexx rexb - 0f 78 /m=32 - Ed Gd - def64 - - - aso rexr rexx rexb - 0f 78 /m=64 - Eq Gq - def64 - - - - - vmwrite - intel - - aso rexr rexx rexb - 0f 79 /m=16 - Gd Ed - def64 - - - aso rexr rexx rexb - 0f 79 /m=32 - Gd Ed - def64 - - - aso rexr rexx rexb - 0f 79 /m=64 - Gq Eq - def64 - - - - - vmrun - amd - - 0f 01 /reg=3 /mod=11 /rm=0 - - - - - vmmcall - amd - - 0f 01 /reg=3 /mod=11 /rm=1 - - - - - vmload - amd - - 0f 01 /reg=3 /mod=11 /rm=2 - - - - - vmsave - amd - - 0f 01 /reg=3 /mod=11 /rm=3 - - - - - wait - - 9b - - - - - wbinvd - - 0f 09 - - - - - wrmsr - - 0f 30 - - - - - xadd - - aso oso rexr rexx rexb - 0f c0 - Eb Gb - - - aso oso rexw rexr rexx rexb - 0f c1 - Ev Gv - - - - - xchg - - aso rexr rexx rexb - 86 - Eb Gb - - - aso oso rexw rexr rexx rexb - 87 - Ev Gv - - - oso rexw rexb - 90 - rAXr8 rAX - - - oso rexw rexb - 91 - rCXr9 rAX - - - oso rexw rexb - 92 - rDXr10 rAX - - - oso rexw rexb - 93 - rBXr11 rAX - - - oso rexw rexb - 94 - rSPr12 rAX - - - oso rexw rexb - 95 - rBPr13 rAX - - - oso rexw rexb - 96 - rSIr14 rAX - - - oso rexw rexb - 97 - rDIr15 rAX - - - - - xlatb - - rexw - d7 - - - - - xor - - aso rexr rexx rexb - 30 - Eb Gb - - - aso oso rexw rexr rexx rexb - 31 - Ev Gv - - - aso rexr rexx rexb - 32 - Gb Eb - - - aso oso rexw rexr rexx rexb - 33 - Gv Ev - - - 34 - AL Ib - - - oso rexw - 35 - rAX Iz - sext - - - aso rexr rexx rexb - 80 /reg=6 - Eb Ib - - - aso oso rexw rexr rexx rexb - 81 /reg=6 - Ev Iz - sext - - - aso rexr rexx rexb - 82 /reg=6 - Eb Ib - inv64 - - - aso oso rexw rexr rexx rexb - 83 /reg=6 - Ev Ib - sext - - - - - xorpd - - aso rexr rexx rexb - sse66 0f 57 - V W - - - - - xorps - - aso rexr rexx rexb - 0f 57 - V W - - - - - xcryptecb - - 0f a7 /mod=11 /rm=0 /reg=1 - - - - - xcryptcbc - - 0f a7 /mod=11 /rm=0 /reg=2 - - - - - xcryptctr - - 0f a7 /mod=11 /rm=0 /reg=3 - - - - - xcryptcfb - - 0f a7 /mod=11 /rm=0 /reg=4 - - - - - xcryptofb - - 0f a7 /mod=11 /rm=0 /reg=5 - - - - - xsha1 - - 0f a6 /mod=11 /rm=0 /reg=1 - - - - - xsha256 - - 0f a6 /mod=11 /rm=0 /reg=2 - - - - - xstore - - 0f a7 /mod=11 /rm=0 /reg=0 - - - - - db - - - - - - movdqa - - aso rexr rexx rexb - sse66 0f 7f - W V - - - aso rexr rexx rexb - sse66 0f 6f - V W - - - - - movdq2q - - aso rexb - ssef2 0f d6 - P VR - - - - - movdqu - - aso rexr rexx rexb - ssef3 0f 6f - V W - - - aso rexr rexx rexb - ssef3 0f 7f - W V - - - - - movq2dq - - aso - ssef3 0f d6 - V PR - - - - - paddq - - aso rexr rexx rexb - 0f d4 - P Q - - - aso rexr rexx rexb - sse66 0f d4 - V W - - - - - psubq - - aso rexr rexx rexb - sse66 0f fb - V W - - - aso rexr rexx rexb - 0f fb - P Q - - - - - pmuludq - - aso rexr rexx rexb - 0f f4 - P Q - - - aso rexr rexx rexb - sse66 0f f4 - V W - - - - - pshufhw - - aso rexr rexx rexb - ssef3 0f 70 - V W Ib - - - - - pshuflw - - aso rexr rexx rexb - ssef2 0f 70 - V W Ib - - - - - pshufd - - aso rexr rexx rexb - sse66 0f 70 - V W Ib - - - - - pslldq - - rexb - sse66 0f 73 /reg=7 - VR Ib - - - - - psrldq - - rexb - sse66 0f 73 /reg=3 - VR Ib - - - - - punpckhqdq - - aso rexr rexx rexb - sse66 0f 6d - V W - - - - - punpcklqdq - - aso rexr rexx rexb - sse66 0f 6c - V W - - - - - - - addsubpd - - aso rexr rexx rexb - sse66 0f d0 - V W - - - - - addsubps - - aso rexr rexx rexb - ssef2 0f d0 - V W - - - - - haddpd - - aso rexr rexx rexb - sse66 0f 7c - V W - - - - - haddps - - aso rexr rexx rexb - ssef2 0f 7c - V W - - - - - hsubpd - - aso rexr rexx rexb - sse66 0f 7d - V W - - - - - hsubps - - aso rexr rexx rexb - ssef2 0f 7d - V W - - - - - movddup - - aso rexr rexx rexb - ssef2 0f 12 /mod=11 - V W - - - aso rexr rexx rexb - ssef2 0f 12 /mod=!11 - V W - - - - - movshdup - - aso rexr rexx rexb - ssef3 0f 16 /mod=11 - V W - - - aso rexr rexx rexb - ssef3 0f 16 /mod=!11 - V W - - - - - movsldup - - aso rexr rexx rexb - ssef3 0f 12 /mod=11 - V W - - - aso rexr rexx rexb - ssef3 0f 12 /mod=!11 - V W - - - - - - - pabsb - - aso rexr rexx rexb - 0f 38 1c - P Q - - - aso rexr rexx rexb - sse66 0f 38 1c - V W - - - - - pabsw - - aso rexr rexx rexb - 0f 38 1d - P Q - - - aso rexr rexx rexb - sse66 0f 38 1d - V W - - - - - pabsd - - aso rexr rexx rexb - 0f 38 1e - P Q - - - aso rexr rexx rexb - sse66 0f 38 1e - V W - - - - - psignb - - aso rexr rexx rexb - 0f 38 00 - P Q - - - aso rexr rexx rexb - sse66 0f 38 00 - V W - - - - - phaddw - - aso rexr rexx rexb - 0f 38 01 - P Q - - - aso rexr rexx rexb - sse66 0f 38 01 - V W - - - - - phaddd - - aso rexr rexx rexb - 0f 38 02 - P Q - - - aso rexr rexx rexb - sse66 0f 38 02 - V W - - - - - phaddsw - - aso rexr rexx rexb - 0f 38 03 - P Q - - - aso rexr rexx rexb - sse66 0f 38 03 - V W - - - - - pmaddubsw - - aso rexr rexx rexb - 0f 38 04 - P Q - - - aso rexr rexx rexb - sse66 0f 38 04 - V W - - - - - phsubw - - aso rexr rexx rexb - 0f 38 05 - P Q - - - aso rexr rexx rexb - sse66 0f 38 05 - V W - - - - - phsubd - - aso rexr rexx rexb - 0f 38 06 - P Q - - - aso rexr rexx rexb - sse66 0f 38 06 - V W - - - - - phsubsw - - aso rexr rexx rexb - 0f 38 07 - P Q - - - aso rexr rexx rexb - sse66 0f 38 07 - V W - - - - - psignb - - aso rexr rexx rexb - 0f 38 08 - P Q - - - aso rexr rexx rexb - sse66 0f 38 08 - V W - - - - - psignd - - aso rexr rexx rexb - 0f 38 0a - P Q - - - aso rexr rexx rexb - sse66 0f 38 0a - V W - - - - - psignw - - aso rexr rexx rexb - 0f 38 09 - P Q - - - aso rexr rexx rexb - sse66 0f 38 09 - V W - - - - - pmulhrsw - - aso rexr rexx rexb - 0f 38 0b - P Q - - - aso rexr rexx rexb - sse66 0f 38 0b - V W - - - - - palignr - - aso rexr rexx rexb - 0f 3a 0f - P Q Ib - - - aso rexr rexx rexb - sse66 0f 3a 0f - V W Ib - - - - - - - pblendvb - - aso rexr rexx rexb - sse66 0f 38 10 - V W - - - - - pmuldq - - aso rexr rexx rexb - sse66 0f 38 28 - V W - - - - - pminsb - - aso rexr rexx rexb - sse66 0f 38 38 - V W - - - - - pminsd - - aso rexr rexx rexb - sse66 0f 38 39 - V W - - - - - pminuw - - aso rexr rexx rexb - sse66 0f 38 3a - V W - - - - - pminud - - aso rexr rexx rexb - sse66 0f 38 3b - V W - - - - - pmaxsb - - aso rexr rexx rexb - sse66 0f 38 3c - V W - - - - - pmaxsd - - aso rexr rexx rexb - sse66 0f 38 3d - V W - - - - - pmaxud - - aso rexr rexx rexb - sse66 0f 38 3f - V W - - - - - pmulld - - aso rexr rexx rexb - sse66 0f 38 40 - V W - - - - - phminposuw - - aso rexr rexx rexb - sse66 0f 38 41 - V W - - - - - roundps - - aso rexr rexx rexb - sse66 0f 3a 08 - V W Ib - - - - - roundpd - - aso rexr rexx rexb - sse66 0f 3a 09 - V W Ib - - - - - roundss - - aso rexr rexx rexb - sse66 0f 3a 0a - V W Ib - - - - - roundsd - - aso rexr rexx rexb - sse66 0f 3a 0b - V W Ib - - - - - blendpd - - aso rexr rexx rexb - sse66 0f 3a 0d - V W Ib - - - - - pblendw - - aso rexr rexx rexb - sse66 0f 3a 0e - V W Ib - - - - - blendps - - aso rexr rexx rexb - sse66 0f 3a 0c - V W Ib - - - - - blendvpd - - aso rexr rexx rexb - sse66 0f 38 15 - V W - - - - - blendvps - - aso rexr rexx rexb - sse66 0f 38 14 - V W - - - - - dpps - - aso rexr rexx rexb - sse66 0f 3a 40 - V W Ib - - - - - dppd - - aso rexr rexx rexb - sse66 0f 3a 41 - V W Ib - - - - - mpsadbw - - aso rexr rexx rexb - sse66 0f 3a 42 - V W Ib - - - - - extractps - - aso rexr rexw rexb - sse66 0f 3a 17 - MdRy V Ib - - - - - invalid - - - diff --git a/3rdparty/masm/disassembler/udis86/ud_opcode.py b/3rdparty/masm/disassembler/udis86/ud_opcode.py deleted file mode 100644 index f301b52461..0000000000 --- a/3rdparty/masm/disassembler/udis86/ud_opcode.py +++ /dev/null @@ -1,235 +0,0 @@ -# udis86 - scripts/ud_opcode.py -# -# Copyright (c) 2009 Vivek Thampi -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -class UdOpcodeTables: - - TableInfo = { - 'opctbl' : { 'name' : 'UD_TAB__OPC_TABLE', 'size' : 256 }, - '/sse' : { 'name' : 'UD_TAB__OPC_SSE', 'size' : 4 }, - '/reg' : { 'name' : 'UD_TAB__OPC_REG', 'size' : 8 }, - '/rm' : { 'name' : 'UD_TAB__OPC_RM', 'size' : 8 }, - '/mod' : { 'name' : 'UD_TAB__OPC_MOD', 'size' : 2 }, - '/m' : { 'name' : 'UD_TAB__OPC_MODE', 'size' : 3 }, - '/x87' : { 'name' : 'UD_TAB__OPC_X87', 'size' : 64 }, - '/a' : { 'name' : 'UD_TAB__OPC_ASIZE', 'size' : 3 }, - '/o' : { 'name' : 'UD_TAB__OPC_OSIZE', 'size' : 3 }, - '/3dnow' : { 'name' : 'UD_TAB__OPC_3DNOW', 'size' : 256 }, - 'vendor' : { 'name' : 'UD_TAB__OPC_VENDOR', 'size' : 3 }, - } - - OpcodeTable0 = { - 'type' : 'opctbl', - 'entries' : {}, - 'meta' : 'table0' - } - - OpcExtIndex = { - - # ssef2, ssef3, sse66 - 'sse': { - 'none' : '00', - 'f2' : '01', - 'f3' : '02', - '66' : '03' - }, - - # /mod= - 'mod': { - '!11' : '00', - '11' : '01' - }, - - # /m=, /o=, /a= - 'mode': { - '16' : '00', - '32' : '01', - '64' : '02' - }, - - 'vendor' : { - 'amd' : '00', - 'intel' : '01', - 'any' : '02' - } - } - - InsnTable = [] - MnemonicsTable = [] - - ThreeDNowTable = {} - - def sizeOfTable( self, t ): - return self.TableInfo[ t ][ 'size' ] - - def nameOfTable( self, t ): - return self.TableInfo[ t ][ 'name' ] - - # - # Updates a table entry: If the entry doesn't exist - # it will create the entry, otherwise, it will walk - # while validating the path. - # - def updateTable( self, table, index, type, meta ): - if not index in table[ 'entries' ]: - table[ 'entries' ][ index ] = { 'type' : type, 'entries' : {}, 'meta' : meta } - if table[ 'entries' ][ index ][ 'type' ] != type: - raise NameError( "error: violation in opcode mapping (overwrite) %s with %s." % - ( table[ 'entries' ][ index ][ 'type' ], type) ) - return table[ 'entries' ][ index ] - - class Insn: - """An abstract type representing an instruction in the opcode map. - """ - - # A mapping of opcode extensions to their representational - # values used in the opcode map. - OpcExtMap = { - '/rm' : lambda v: "%02x" % int(v, 16), - '/x87' : lambda v: "%02x" % int(v, 16), - '/3dnow' : lambda v: "%02x" % int(v, 16), - '/reg' : lambda v: "%02x" % int(v, 16), - # modrm.mod - # (!11, 11) => (00, 01) - '/mod' : lambda v: '00' if v == '!11' else '01', - # Mode extensions: - # (16, 32, 64) => (00, 01, 02) - '/o' : lambda v: "%02x" % (int(v) / 32), - '/a' : lambda v: "%02x" % (int(v) / 32), - '/m' : lambda v: "%02x" % (int(v) / 32), - '/sse' : lambda v: UdOpcodeTables.OpcExtIndex['sse'][v] - } - - def __init__(self, prefixes, mnemonic, opcodes, operands, vendor): - self.opcodes = opcodes - self.prefixes = prefixes - self.mnemonic = mnemonic - self.operands = operands - self.vendor = vendor - self.opcext = {} - - ssePrefix = None - if self.opcodes[0] in ('ssef2', 'ssef3', 'sse66'): - ssePrefix = self.opcodes[0][3:] - self.opcodes.pop(0) - - # do some preliminary decoding of the instruction type - # 1byte, 2byte or 3byte instruction? - self.nByteInsn = 1 - if self.opcodes[0] == '0f': # 2byte - # 2+ byte opcodes are always disambiguated by an - # sse prefix, unless it is a 3d now instruction - # which is 0f 0f ... - if self.opcodes[1] != '0f' and ssePrefix is None: - ssePrefix = 'none' - if self.opcodes[1] in ('38', '3a'): # 3byte - self.nByteInsn = 3 - else: - self.nByteInsn = 2 - - # The opcode that indexes into the opcode table. - self.opcode = self.opcodes[self.nByteInsn - 1] - - # Record opcode extensions - for opcode in self.opcodes[self.nByteInsn:]: - arg, val = opcode.split('=') - self.opcext[arg] = self.OpcExtMap[arg](val) - - # Record sse extension: the reason sse extension is handled - # separately is that historically sse was handled as a first - # class opcode, not as an extension. Now that sse is handled - # as an extension, we do the manual conversion here, as opposed - # to modifying the opcode xml file. - if ssePrefix is not None: - self.opcext['/sse'] = self.OpcExtMap['/sse'](ssePrefix) - - def parse(self, table, insn): - index = insn.opcodes[0]; - if insn.nByteInsn > 1: - assert index == '0f' - table = self.updateTable(table, index, 'opctbl', '0f') - index = insn.opcodes[1] - - if insn.nByteInsn == 3: - table = self.updateTable(table, index, 'opctbl', index) - index = insn.opcodes[2] - - # Walk down the tree, create levels as needed, for opcode - # extensions. The order is important, and determines how - # well the opcode table is packed. Also note, /sse must be - # before /o, because /sse may consume operand size prefix - # affect the outcome of /o. - for ext in ('/mod', '/x87', '/reg', '/rm', '/sse', - '/o', '/a', '/m', '/3dnow'): - if ext in insn.opcext: - table = self.updateTable(table, index, ext, ext) - index = insn.opcext[ext] - - # additional table for disambiguating vendor - if len(insn.vendor): - table = self.updateTable(table, index, 'vendor', insn.vendor) - index = self.OpcExtIndex['vendor'][insn.vendor] - - # make leaf node entries - leaf = self.updateTable(table, index, 'insn', '') - - leaf['mnemonic'] = insn.mnemonic - leaf['prefixes'] = insn.prefixes - leaf['operands'] = insn.operands - - # add instruction to linear table of instruction forms - self.InsnTable.append({ 'prefixes' : insn.prefixes, - 'mnemonic' : insn.mnemonic, - 'operands' : insn.operands }) - - # add mnemonic to mnemonic table - if not insn.mnemonic in self.MnemonicsTable: - self.MnemonicsTable.append(insn.mnemonic) - - - # Adds an instruction definition to the opcode tables - def addInsnDef( self, prefixes, mnemonic, opcodes, operands, vendor ): - insn = self.Insn(prefixes=prefixes, - mnemonic=mnemonic, - opcodes=opcodes, - operands=operands, - vendor=vendor) - self.parse(self.OpcodeTable0, insn) - - def print_table( self, table, pfxs ): - print "%s |" % pfxs - keys = table[ 'entries' ].keys() - if ( len( keys ) ): - keys.sort() - for idx in keys: - e = table[ 'entries' ][ idx ] - if e[ 'type' ] == 'insn': - print "%s |-<%s>" % ( pfxs, idx ), - print "%s %s" % ( e[ 'mnemonic' ], ' '.join( e[ 'operands'] ) ) - else: - print "%s |-<%s> %s" % ( pfxs, idx, e['type'] ) - self.print_table( e, pfxs + ' |' ) - - def print_tree( self ): - self.print_table( self.OpcodeTable0, '' ) diff --git a/3rdparty/masm/disassembler/udis86/ud_optable.py b/3rdparty/masm/disassembler/udis86/ud_optable.py deleted file mode 100644 index 5b5c55d3b8..0000000000 --- a/3rdparty/masm/disassembler/udis86/ud_optable.py +++ /dev/null @@ -1,103 +0,0 @@ -# udis86 - scripts/ud_optable.py (optable.xml parser) -# -# Copyright (c) 2009 Vivek Thampi -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import os -import sys -from xml.dom import minidom - -class UdOptableXmlParser: - - def parseDef( self, node ): - ven = '' - pfx = [] - opc = [] - opr = [] - for def_node in node.childNodes: - if not def_node.localName: - continue - if def_node.localName == 'pfx': - pfx = def_node.firstChild.data.split(); - elif def_node.localName == 'opc': - opc = def_node.firstChild.data.split(); - elif def_node.localName == 'opr': - opr = def_node.firstChild.data.split(); - elif def_node.localName == 'mode': - pfx.extend( def_node.firstChild.data.split() ); - elif def_node.localName == 'syn': - pfx.extend( def_node.firstChild.data.split() ); - elif def_node.localName == 'vendor': - ven = ( def_node.firstChild.data ); - else: - print "warning: invalid node - %s" % def_node.localName - continue - return ( pfx, opc, opr, ven ) - - def parse( self, xml, fn ): - xmlDoc = minidom.parse( xml ) - self.TlNode = xmlDoc.firstChild - - while self.TlNode and self.TlNode.localName != "x86optable": - self.TlNode = self.TlNode.nextSibling - - for insnNode in self.TlNode.childNodes: - if not insnNode.localName: - continue - if insnNode.localName != "instruction": - print "warning: invalid insn node - %s" % insnNode.localName - continue - - mnemonic = insnNode.getElementsByTagName( 'mnemonic' )[ 0 ].firstChild.data - vendor = '' - - for node in insnNode.childNodes: - if node.localName == 'vendor': - vendor = node.firstChild.data - elif node.localName == 'def': - ( prefixes, opcodes, operands, local_vendor ) = \ - self.parseDef( node ) - if ( len( local_vendor ) ): - vendor = local_vendor - # callback - fn( prefixes, mnemonic, opcodes, operands, vendor ) - - -def printFn( pfx, mnm, opc, opr, ven ): - print 'def: ', - if len( pfx ): - print ' '.join( pfx ), - print "%s %s %s %s" % \ - ( mnm, ' '.join( opc ), ' '.join( opr ), ven ) - - -def parse( xml, callback ): - parser = UdOptableXmlParser() - parser.parse( xml, callback ) - -def main(): - parser = UdOptableXmlParser() - parser.parse( sys.argv[ 1 ], printFn ) - -if __name__ == "__main__": - main() diff --git a/3rdparty/masm/disassembler/udis86/udis86.c b/3rdparty/masm/disassembler/udis86/udis86.c deleted file mode 100644 index fbf76707a0..0000000000 --- a/3rdparty/masm/disassembler/udis86/udis86.c +++ /dev/null @@ -1,183 +0,0 @@ -/* udis86 - libudis86/udis86.c - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include - -#if USE(UDIS86) - -#include "udis86_input.h" -#include "udis86_extern.h" - -#ifndef __UD_STANDALONE__ -# include -# include -#endif /* __UD_STANDALONE__ */ - -/* ============================================================================= - * ud_init() - Initializes ud_t object. - * ============================================================================= - */ -extern void -ud_init(struct ud* u) -{ - memset((void*)u, 0, sizeof(struct ud)); - ud_set_mode(u, 16); - u->mnemonic = UD_Iinvalid; - ud_set_pc(u, 0); -#ifndef __UD_STANDALONE__ - ud_set_input_file(u, stdin); -#endif /* __UD_STANDALONE__ */ -} - -/* ============================================================================= - * ud_disassemble() - disassembles one instruction and returns the number of - * bytes disassembled. A zero means end of disassembly. - * ============================================================================= - */ -extern unsigned int -ud_disassemble(struct ud* u) -{ - if (ud_input_end(u)) - return 0; - - - u->insn_buffer[0] = u->insn_hexcode[0] = 0; - - - if (ud_decode(u) == 0) - return 0; - if (u->translator) - u->translator(u); - return ud_insn_len(u); -} - -/* ============================================================================= - * ud_set_mode() - Set Disassemly Mode. - * ============================================================================= - */ -extern void -ud_set_mode(struct ud* u, uint8_t m) -{ - switch(m) { - case 16: - case 32: - case 64: u->dis_mode = m ; return; - default: u->dis_mode = 16; return; - } -} - -/* ============================================================================= - * ud_set_vendor() - Set vendor. - * ============================================================================= - */ -extern void -ud_set_vendor(struct ud* u, unsigned v) -{ - switch(v) { - case UD_VENDOR_INTEL: - u->vendor = v; - break; - case UD_VENDOR_ANY: - u->vendor = v; - break; - default: - u->vendor = UD_VENDOR_AMD; - } -} - -/* ============================================================================= - * ud_set_pc() - Sets code origin. - * ============================================================================= - */ -extern void -ud_set_pc(struct ud* u, uint64_t o) -{ - u->pc = o; -} - -/* ============================================================================= - * ud_set_syntax() - Sets the output syntax. - * ============================================================================= - */ -extern void -ud_set_syntax(struct ud* u, void (*t)(struct ud*)) -{ - u->translator = t; -} - -/* ============================================================================= - * ud_insn() - returns the disassembled instruction - * ============================================================================= - */ -extern char* -ud_insn_asm(struct ud* u) -{ - return u->insn_buffer; -} - -/* ============================================================================= - * ud_insn_offset() - Returns the offset. - * ============================================================================= - */ -extern uint64_t -ud_insn_off(struct ud* u) -{ - return u->insn_offset; -} - - -/* ============================================================================= - * ud_insn_hex() - Returns hex form of disassembled instruction. - * ============================================================================= - */ -extern char* -ud_insn_hex(struct ud* u) -{ - return u->insn_hexcode; -} - -/* ============================================================================= - * ud_insn_ptr() - Returns code disassembled. - * ============================================================================= - */ -extern uint8_t* -ud_insn_ptr(struct ud* u) -{ - return u->inp_sess; -} - -/* ============================================================================= - * ud_insn_len() - Returns the count of bytes disassembled. - * ============================================================================= - */ -extern unsigned int -ud_insn_len(struct ud* u) -{ - return u->inp_ctr; -} - -#endif // USE(UDIS86) diff --git a/3rdparty/masm/disassembler/udis86/udis86.h b/3rdparty/masm/disassembler/udis86/udis86.h deleted file mode 100644 index baaf495e04..0000000000 --- a/3rdparty/masm/disassembler/udis86/udis86.h +++ /dev/null @@ -1,33 +0,0 @@ -/* udis86 - udis86.h - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef UDIS86_H -#define UDIS86_H - -#include "udis86_types.h" -#include "udis86_extern.h" -#include "udis86_itab.h" - -#endif diff --git a/3rdparty/masm/disassembler/udis86/udis86_decode.c b/3rdparty/masm/disassembler/udis86/udis86_decode.c deleted file mode 100644 index a3fd576655..0000000000 --- a/3rdparty/masm/disassembler/udis86/udis86_decode.c +++ /dev/null @@ -1,1142 +0,0 @@ -/* udis86 - libudis86/decode.c - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include - -#if USE(UDIS86) - -#include "udis86_extern.h" -#include "udis86_types.h" -#include "udis86_input.h" -#include "udis86_decode.h" -#include - -#define dbg(x, n...) -/* #define dbg printf */ - -#ifndef __UD_STANDALONE__ -# include -#endif /* __UD_STANDALONE__ */ - -/* The max number of prefixes to an instruction */ -#define MAX_PREFIXES 15 - -/* instruction aliases and special cases */ -static struct ud_itab_entry s_ie__invalid = - { UD_Iinvalid, O_NONE, O_NONE, O_NONE, P_none }; - -static int -decode_ext(struct ud *u, uint16_t ptr); - - -static inline int -eff_opr_mode(int dis_mode, int rex_w, int pfx_opr) -{ - if (dis_mode == 64) { - return rex_w ? 64 : (pfx_opr ? 16 : 32); - } else if (dis_mode == 32) { - return pfx_opr ? 16 : 32; - } else { - ASSERT(dis_mode == 16); - return pfx_opr ? 32 : 16; - } -} - - -static inline int -eff_adr_mode(int dis_mode, int pfx_adr) -{ - if (dis_mode == 64) { - return pfx_adr ? 32 : 64; - } else if (dis_mode == 32) { - return pfx_adr ? 16 : 32; - } else { - ASSERT(dis_mode == 16); - return pfx_adr ? 32 : 16; - } -} - - -/* Looks up mnemonic code in the mnemonic string table - * Returns NULL if the mnemonic code is invalid - */ -const char * ud_lookup_mnemonic( enum ud_mnemonic_code c ) -{ - return ud_mnemonics_str[ c ]; -} - - -/* - * decode_prefixes - * - * Extracts instruction prefixes. - */ -static int -decode_prefixes(struct ud *u) -{ - unsigned int have_pfx = 1; - unsigned int i; - uint8_t curr; - - /* if in error state, bail out */ - if ( u->error ) - return -1; - - /* keep going as long as there are prefixes available */ - for ( i = 0; have_pfx ; ++i ) { - - /* Get next byte. */ - ud_inp_next(u); - if ( u->error ) - return -1; - curr = ud_inp_curr( u ); - - /* rex prefixes in 64bit mode */ - if ( u->dis_mode == 64 && ( curr & 0xF0 ) == 0x40 ) { - u->pfx_rex = curr; - } else { - switch ( curr ) - { - case 0x2E : - u->pfx_seg = UD_R_CS; - u->pfx_rex = 0; - break; - case 0x36 : - u->pfx_seg = UD_R_SS; - u->pfx_rex = 0; - break; - case 0x3E : - u->pfx_seg = UD_R_DS; - u->pfx_rex = 0; - break; - case 0x26 : - u->pfx_seg = UD_R_ES; - u->pfx_rex = 0; - break; - case 0x64 : - u->pfx_seg = UD_R_FS; - u->pfx_rex = 0; - break; - case 0x65 : - u->pfx_seg = UD_R_GS; - u->pfx_rex = 0; - break; - case 0x67 : /* adress-size override prefix */ - u->pfx_adr = 0x67; - u->pfx_rex = 0; - break; - case 0xF0 : - u->pfx_lock = 0xF0; - u->pfx_rex = 0; - break; - case 0x66: - /* the 0x66 sse prefix is only effective if no other sse prefix - * has already been specified. - */ - if ( !u->pfx_insn ) u->pfx_insn = 0x66; - u->pfx_opr = 0x66; - u->pfx_rex = 0; - break; - case 0xF2: - u->pfx_insn = 0xF2; - u->pfx_repne = 0xF2; - u->pfx_rex = 0; - break; - case 0xF3: - u->pfx_insn = 0xF3; - u->pfx_rep = 0xF3; - u->pfx_repe = 0xF3; - u->pfx_rex = 0; - break; - default : - /* No more prefixes */ - have_pfx = 0; - break; - } - } - - /* check if we reached max instruction length */ - if ( i + 1 == MAX_INSN_LENGTH ) { - u->error = 1; - break; - } - } - - /* return status */ - if ( u->error ) - return -1; - - /* rewind back one byte in stream, since the above loop - * stops with a non-prefix byte. - */ - ud_inp_back(u); - return 0; -} - - -static inline unsigned int modrm( struct ud * u ) -{ - if ( !u->have_modrm ) { - u->modrm = ud_inp_next( u ); - u->have_modrm = 1; - } - return u->modrm; -} - - -static unsigned int resolve_operand_size( const struct ud * u, unsigned int s ) -{ - switch ( s ) - { - case SZ_V: - return ( u->opr_mode ); - case SZ_Z: - return ( u->opr_mode == 16 ) ? 16 : 32; - case SZ_P: - return ( u->opr_mode == 16 ) ? SZ_WP : SZ_DP; - case SZ_MDQ: - return ( u->opr_mode == 16 ) ? 32 : u->opr_mode; - case SZ_RDQ: - return ( u->dis_mode == 64 ) ? 64 : 32; - default: - return s; - } -} - - -static int resolve_mnemonic( struct ud* u ) -{ - /* far/near flags */ - u->br_far = 0; - u->br_near = 0; - /* readjust operand sizes for call/jmp instrcutions */ - if ( u->mnemonic == UD_Icall || u->mnemonic == UD_Ijmp ) { - /* WP: 16:16 pointer */ - if ( u->operand[ 0 ].size == SZ_WP ) { - u->operand[ 0 ].size = 16; - u->br_far = 1; - u->br_near= 0; - /* DP: 32:32 pointer */ - } else if ( u->operand[ 0 ].size == SZ_DP ) { - u->operand[ 0 ].size = 32; - u->br_far = 1; - u->br_near= 0; - } else { - u->br_far = 0; - u->br_near= 1; - } - /* resolve 3dnow weirdness. */ - } else if ( u->mnemonic == UD_I3dnow ) { - u->mnemonic = ud_itab[ u->le->table[ ud_inp_curr( u ) ] ].mnemonic; - } - /* SWAPGS is only valid in 64bits mode */ - if ( u->mnemonic == UD_Iswapgs && u->dis_mode != 64 ) { - u->error = 1; - return -1; - } - - if (u->mnemonic == UD_Ixchg) { - if ((u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_AX && - u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_AX) || - (u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_EAX && - u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_EAX)) { - u->operand[0].type = UD_NONE; - u->operand[1].type = UD_NONE; - u->mnemonic = UD_Inop; - } - } - - if (u->mnemonic == UD_Inop && u->pfx_rep) { - u->pfx_rep = 0; - u->mnemonic = UD_Ipause; - } - return 0; -} - - -/* ----------------------------------------------------------------------------- - * decode_a()- Decodes operands of the type seg:offset - * ----------------------------------------------------------------------------- - */ -static void -decode_a(struct ud* u, struct ud_operand *op) -{ - if (u->opr_mode == 16) { - /* seg16:off16 */ - op->type = UD_OP_PTR; - op->size = 32; - op->lval.ptr.off = ud_inp_uint16(u); - op->lval.ptr.seg = ud_inp_uint16(u); - } else { - /* seg16:off32 */ - op->type = UD_OP_PTR; - op->size = 48; - op->lval.ptr.off = ud_inp_uint32(u); - op->lval.ptr.seg = ud_inp_uint16(u); - } -} - -/* ----------------------------------------------------------------------------- - * decode_gpr() - Returns decoded General Purpose Register - * ----------------------------------------------------------------------------- - */ -static enum ud_type -decode_gpr(register struct ud* u, unsigned int s, unsigned char rm) -{ - s = resolve_operand_size(u, s); - - switch (s) { - case 64: - return UD_R_RAX + rm; - case SZ_DP: - case 32: - return UD_R_EAX + rm; - case SZ_WP: - case 16: - return UD_R_AX + rm; - case 8: - if (u->dis_mode == 64 && u->pfx_rex) { - if (rm >= 4) - return UD_R_SPL + (rm-4); - return UD_R_AL + rm; - } else return UD_R_AL + rm; - default: - return 0; - } -} - -/* ----------------------------------------------------------------------------- - * resolve_gpr64() - 64bit General Purpose Register-Selection. - * ----------------------------------------------------------------------------- - */ -static enum ud_type -resolve_gpr64(struct ud* u, enum ud_operand_code gpr_op, enum ud_operand_size * size) -{ - if (gpr_op >= OP_rAXr8 && gpr_op <= OP_rDIr15) - gpr_op = (gpr_op - OP_rAXr8) | (REX_B(u->pfx_rex) << 3); - else gpr_op = (gpr_op - OP_rAX); - - if (u->opr_mode == 16) { - *size = 16; - return gpr_op + UD_R_AX; - } - if (u->dis_mode == 32 || - (u->opr_mode == 32 && ! (REX_W(u->pfx_rex) || u->default64))) { - *size = 32; - return gpr_op + UD_R_EAX; - } - - *size = 64; - return gpr_op + UD_R_RAX; -} - -/* ----------------------------------------------------------------------------- - * resolve_gpr32 () - 32bit General Purpose Register-Selection. - * ----------------------------------------------------------------------------- - */ -static enum ud_type -resolve_gpr32(struct ud* u, enum ud_operand_code gpr_op) -{ - gpr_op = gpr_op - OP_eAX; - - if (u->opr_mode == 16) - return gpr_op + UD_R_AX; - - return gpr_op + UD_R_EAX; -} - -/* ----------------------------------------------------------------------------- - * resolve_reg() - Resolves the register type - * ----------------------------------------------------------------------------- - */ -static enum ud_type -resolve_reg(struct ud* u, unsigned int type, unsigned char i) -{ - switch (type) { - case T_MMX : return UD_R_MM0 + (i & 7); - case T_XMM : return UD_R_XMM0 + i; - case T_CRG : return UD_R_CR0 + i; - case T_DBG : return UD_R_DR0 + i; - case T_SEG : { - /* - * Only 6 segment registers, anything else is an error. - */ - if ((i & 7) > 5) { - u->error = 1; - } else { - return UD_R_ES + (i & 7); - } - } - case T_NONE: - default: return UD_NONE; - } -} - -/* ----------------------------------------------------------------------------- - * decode_imm() - Decodes Immediate values. - * ----------------------------------------------------------------------------- - */ -static void -decode_imm(struct ud* u, unsigned int s, struct ud_operand *op) -{ - op->size = resolve_operand_size(u, s); - op->type = UD_OP_IMM; - - switch (op->size) { - case 8: op->lval.sbyte = ud_inp_uint8(u); break; - case 16: op->lval.uword = ud_inp_uint16(u); break; - case 32: op->lval.udword = ud_inp_uint32(u); break; - case 64: op->lval.uqword = ud_inp_uint64(u); break; - default: return; - } -} - - -/* - * decode_modrm_reg - * - * Decodes reg field of mod/rm byte - * - */ -static void -decode_modrm_reg(struct ud *u, - struct ud_operand *operand, - unsigned int type, - unsigned int size) -{ - uint8_t reg = (REX_R(u->pfx_rex) << 3) | MODRM_REG(modrm(u)); - operand->type = UD_OP_REG; - operand->size = resolve_operand_size(u, size); - - if (type == T_GPR) { - operand->base = decode_gpr(u, operand->size, reg); - } else { - operand->base = resolve_reg(u, type, reg); - } -} - - -/* - * decode_modrm_rm - * - * Decodes rm field of mod/rm byte - * - */ -static void -decode_modrm_rm(struct ud *u, - struct ud_operand *op, - unsigned char type, - unsigned int size) - -{ - unsigned char mod, rm, reg; - - /* get mod, r/m and reg fields */ - mod = MODRM_MOD(modrm(u)); - rm = (REX_B(u->pfx_rex) << 3) | MODRM_RM(modrm(u)); - reg = (REX_R(u->pfx_rex) << 3) | MODRM_REG(modrm(u)); - - op->size = resolve_operand_size(u, size); - - /* - * If mod is 11b, then the modrm.rm specifies a register. - * - */ - if (mod == 3) { - op->type = UD_OP_REG; - if (type == T_GPR) { - op->base = decode_gpr(u, op->size, rm); - } else { - op->base = resolve_reg(u, type, (REX_B(u->pfx_rex) << 3) | (rm & 7)); - } - return; - } - - - /* - * !11 => Memory Address - */ - op->type = UD_OP_MEM; - - if (u->adr_mode == 64) { - op->base = UD_R_RAX + rm; - if (mod == 1) { - op->offset = 8; - } else if (mod == 2) { - op->offset = 32; - } else if (mod == 0 && (rm & 7) == 5) { - op->base = UD_R_RIP; - op->offset = 32; - } else { - op->offset = 0; - } - /* - * Scale-Index-Base (SIB) - */ - if ((rm & 7) == 4) { - ud_inp_next(u); - - op->scale = (1 << SIB_S(ud_inp_curr(u))) & ~1; - op->index = UD_R_RAX + (SIB_I(ud_inp_curr(u)) | (REX_X(u->pfx_rex) << 3)); - op->base = UD_R_RAX + (SIB_B(ud_inp_curr(u)) | (REX_B(u->pfx_rex) << 3)); - - /* special conditions for base reference */ - if (op->index == UD_R_RSP) { - op->index = UD_NONE; - op->scale = UD_NONE; - } - - if (op->base == UD_R_RBP || op->base == UD_R_R13) { - if (mod == 0) { - op->base = UD_NONE; - } - if (mod == 1) { - op->offset = 8; - } else { - op->offset = 32; - } - } - } - } else if (u->adr_mode == 32) { - op->base = UD_R_EAX + rm; - if (mod == 1) { - op->offset = 8; - } else if (mod == 2) { - op->offset = 32; - } else if (mod == 0 && rm == 5) { - op->base = UD_NONE; - op->offset = 32; - } else { - op->offset = 0; - } - - /* Scale-Index-Base (SIB) */ - if ((rm & 7) == 4) { - ud_inp_next(u); - - op->scale = (1 << SIB_S(ud_inp_curr(u))) & ~1; - op->index = UD_R_EAX + (SIB_I(ud_inp_curr(u)) | (REX_X(u->pfx_rex) << 3)); - op->base = UD_R_EAX + (SIB_B(ud_inp_curr(u)) | (REX_B(u->pfx_rex) << 3)); - - if (op->index == UD_R_ESP) { - op->index = UD_NONE; - op->scale = UD_NONE; - } - - /* special condition for base reference */ - if (op->base == UD_R_EBP) { - if (mod == 0) { - op->base = UD_NONE; - } - if (mod == 1) { - op->offset = 8; - } else { - op->offset = 32; - } - } - } - } else { - const unsigned int bases[] = { UD_R_BX, UD_R_BX, UD_R_BP, UD_R_BP, - UD_R_SI, UD_R_DI, UD_R_BP, UD_R_BX }; - const unsigned int indices[] = { UD_R_SI, UD_R_DI, UD_R_SI, UD_R_DI, - UD_NONE, UD_NONE, UD_NONE, UD_NONE }; - op->base = bases[rm & 7]; - op->index = indices[rm & 7]; - if (mod == 0 && rm == 6) { - op->offset= 16; - op->base = UD_NONE; - } else if (mod == 1) { - op->offset = 8; - } else if (mod == 2) { - op->offset = 16; - } - } - - /* - * extract offset, if any - */ - switch (op->offset) { - case 8 : op->lval.ubyte = ud_inp_uint8(u); break; - case 16: op->lval.uword = ud_inp_uint16(u); break; - case 32: op->lval.udword = ud_inp_uint32(u); break; - case 64: op->lval.uqword = ud_inp_uint64(u); break; - default: break; - } -} - -/* ----------------------------------------------------------------------------- - * decode_o() - Decodes offset - * ----------------------------------------------------------------------------- - */ -static void -decode_o(struct ud* u, unsigned int s, struct ud_operand *op) -{ - switch (u->adr_mode) { - case 64: - op->offset = 64; - op->lval.uqword = ud_inp_uint64(u); - break; - case 32: - op->offset = 32; - op->lval.udword = ud_inp_uint32(u); - break; - case 16: - op->offset = 16; - op->lval.uword = ud_inp_uint16(u); - break; - default: - return; - } - op->type = UD_OP_MEM; - op->size = resolve_operand_size(u, s); -} - -/* ----------------------------------------------------------------------------- - * decode_operands() - Disassembles Operands. - * ----------------------------------------------------------------------------- - */ -static int -decode_operand(struct ud *u, - struct ud_operand *operand, - enum ud_operand_code type, - unsigned int size) -{ - switch (type) { - case OP_A : - decode_a(u, operand); - break; - case OP_MR: - if (MODRM_MOD(modrm(u)) == 3) { - decode_modrm_rm(u, operand, T_GPR, - size == SZ_DY ? SZ_MDQ : SZ_V); - } else if (size == SZ_WV) { - decode_modrm_rm( u, operand, T_GPR, SZ_W); - } else if (size == SZ_BV) { - decode_modrm_rm( u, operand, T_GPR, SZ_B); - } else if (size == SZ_DY) { - decode_modrm_rm( u, operand, T_GPR, SZ_D); - } else { - ASSERT(!"unexpected size"); - } - break; - case OP_M: - if (MODRM_MOD(modrm(u)) == 3) { - u->error = 1; - } - /* intended fall through */ - case OP_E: - decode_modrm_rm(u, operand, T_GPR, size); - break; - break; - case OP_G: - decode_modrm_reg(u, operand, T_GPR, size); - break; - case OP_I: - decode_imm(u, size, operand); - break; - case OP_I1: - operand->type = UD_OP_CONST; - operand->lval.udword = 1; - break; - case OP_PR: - if (MODRM_MOD(modrm(u)) != 3) { - u->error = 1; - } - decode_modrm_rm(u, operand, T_MMX, size); - break; - case OP_P: - decode_modrm_reg(u, operand, T_MMX, size); - break; - case OP_VR: - if (MODRM_MOD(modrm(u)) != 3) { - u->error = 1; - } - /* intended fall through */ - case OP_W: - decode_modrm_rm(u, operand, T_XMM, size); - break; - case OP_V: - decode_modrm_reg(u, operand, T_XMM, size); - break; - case OP_S: - decode_modrm_reg(u, operand, T_SEG, size); - break; - case OP_AL: - case OP_CL: - case OP_DL: - case OP_BL: - case OP_AH: - case OP_CH: - case OP_DH: - case OP_BH: - operand->type = UD_OP_REG; - operand->base = UD_R_AL + (type - OP_AL); - operand->size = 8; - break; - case OP_DX: - operand->type = UD_OP_REG; - operand->base = UD_R_DX; - operand->size = 16; - break; - case OP_O: - decode_o(u, size, operand); - break; - case OP_rAXr8: - case OP_rCXr9: - case OP_rDXr10: - case OP_rBXr11: - case OP_rSPr12: - case OP_rBPr13: - case OP_rSIr14: - case OP_rDIr15: - case OP_rAX: - case OP_rCX: - case OP_rDX: - case OP_rBX: - case OP_rSP: - case OP_rBP: - case OP_rSI: - case OP_rDI: - operand->type = UD_OP_REG; - operand->base = resolve_gpr64(u, type, &operand->size); - break; - case OP_ALr8b: - case OP_CLr9b: - case OP_DLr10b: - case OP_BLr11b: - case OP_AHr12b: - case OP_CHr13b: - case OP_DHr14b: - case OP_BHr15b: { - ud_type_t gpr = (type - OP_ALr8b) + UD_R_AL - + (REX_B(u->pfx_rex) << 3); - if (UD_R_AH <= gpr && u->pfx_rex) { - gpr = gpr + 4; - } - operand->type = UD_OP_REG; - operand->base = gpr; - break; - } - case OP_eAX: - case OP_eCX: - case OP_eDX: - case OP_eBX: - case OP_eSP: - case OP_eBP: - case OP_eSI: - case OP_eDI: - operand->type = UD_OP_REG; - operand->base = resolve_gpr32(u, type); - operand->size = u->opr_mode == 16 ? 16 : 32; - break; - case OP_ES: - case OP_CS: - case OP_DS: - case OP_SS: - case OP_FS: - case OP_GS: - /* in 64bits mode, only fs and gs are allowed */ - if (u->dis_mode == 64) { - if (type != OP_FS && type != OP_GS) { - u->error= 1; - } - } - operand->type = UD_OP_REG; - operand->base = (type - OP_ES) + UD_R_ES; - operand->size = 16; - break; - case OP_J : - decode_imm(u, size, operand); - operand->type = UD_OP_JIMM; - break ; - case OP_Q: - decode_modrm_rm(u, operand, T_MMX, size); - break; - case OP_R : - decode_modrm_rm(u, operand, T_GPR, size); - break; - case OP_C: - decode_modrm_reg(u, operand, T_CRG, size); - break; - case OP_D: - decode_modrm_reg(u, operand, T_DBG, size); - break; - case OP_I3 : - operand->type = UD_OP_CONST; - operand->lval.sbyte = 3; - break; - case OP_ST0: - case OP_ST1: - case OP_ST2: - case OP_ST3: - case OP_ST4: - case OP_ST5: - case OP_ST6: - case OP_ST7: - operand->type = UD_OP_REG; - operand->base = (type - OP_ST0) + UD_R_ST0; - operand->size = 0; - break; - case OP_AX: - operand->type = UD_OP_REG; - operand->base = UD_R_AX; - operand->size = 16; - break; - default : - operand->type = UD_NONE; - break; - } - return 0; -} - - -/* - * decode_operands - * - * Disassemble upto 3 operands of the current instruction being - * disassembled. By the end of the function, the operand fields - * of the ud structure will have been filled. - */ -static int -decode_operands(struct ud* u) -{ - decode_operand(u, &u->operand[0], - u->itab_entry->operand1.type, - u->itab_entry->operand1.size); - decode_operand(u, &u->operand[1], - u->itab_entry->operand2.type, - u->itab_entry->operand2.size); - decode_operand(u, &u->operand[2], - u->itab_entry->operand3.type, - u->itab_entry->operand3.size); - return 0; -} - -/* ----------------------------------------------------------------------------- - * clear_insn() - clear instruction structure - * ----------------------------------------------------------------------------- - */ -static void -clear_insn(register struct ud* u) -{ - u->error = 0; - u->pfx_seg = 0; - u->pfx_opr = 0; - u->pfx_adr = 0; - u->pfx_lock = 0; - u->pfx_repne = 0; - u->pfx_rep = 0; - u->pfx_repe = 0; - u->pfx_rex = 0; - u->pfx_insn = 0; - u->mnemonic = UD_Inone; - u->itab_entry = NULL; - u->have_modrm = 0; - - memset( &u->operand[ 0 ], 0, sizeof( struct ud_operand ) ); - memset( &u->operand[ 1 ], 0, sizeof( struct ud_operand ) ); - memset( &u->operand[ 2 ], 0, sizeof( struct ud_operand ) ); -} - -static int -resolve_mode( struct ud* u ) -{ - /* if in error state, bail out */ - if ( u->error ) return -1; - - /* propagate prefix effects */ - if ( u->dis_mode == 64 ) { /* set 64bit-mode flags */ - - /* Check validity of instruction m64 */ - if ( P_INV64( u->itab_entry->prefix ) ) { - u->error = 1; - return -1; - } - - /* effective rex prefix is the effective mask for the - * instruction hard-coded in the opcode map. - */ - u->pfx_rex = ( u->pfx_rex & 0x40 ) | - ( u->pfx_rex & REX_PFX_MASK( u->itab_entry->prefix ) ); - - /* whether this instruction has a default operand size of - * 64bit, also hardcoded into the opcode map. - */ - u->default64 = P_DEF64( u->itab_entry->prefix ); - /* calculate effective operand size */ - if ( REX_W( u->pfx_rex ) ) { - u->opr_mode = 64; - } else if ( u->pfx_opr ) { - u->opr_mode = 16; - } else { - /* unless the default opr size of instruction is 64, - * the effective operand size in the absence of rex.w - * prefix is 32. - */ - u->opr_mode = ( u->default64 ) ? 64 : 32; - } - - /* calculate effective address size */ - u->adr_mode = (u->pfx_adr) ? 32 : 64; - } else if ( u->dis_mode == 32 ) { /* set 32bit-mode flags */ - u->opr_mode = ( u->pfx_opr ) ? 16 : 32; - u->adr_mode = ( u->pfx_adr ) ? 16 : 32; - } else if ( u->dis_mode == 16 ) { /* set 16bit-mode flags */ - u->opr_mode = ( u->pfx_opr ) ? 32 : 16; - u->adr_mode = ( u->pfx_adr ) ? 32 : 16; - } - - /* These flags determine which operand to apply the operand size - * cast to. - */ - u->c1 = ( P_C1( u->itab_entry->prefix ) ) ? 1 : 0; - u->c2 = ( P_C2( u->itab_entry->prefix ) ) ? 1 : 0; - u->c3 = ( P_C3( u->itab_entry->prefix ) ) ? 1 : 0; - - /* set flags for implicit addressing */ - u->implicit_addr = P_IMPADDR( u->itab_entry->prefix ); - - return 0; -} - -static int gen_hex( struct ud *u ) -{ - unsigned int i; - unsigned char *src_ptr = ud_inp_sess( u ); - char* src_hex; - - /* bail out if in error stat. */ - if ( u->error ) return -1; - /* output buffer pointe */ - src_hex = ( char* ) u->insn_hexcode; - /* for each byte used to decode instruction */ - for ( i = 0; i < u->inp_ctr; ++i, ++src_ptr) { - sprintf( src_hex, "%02x", *src_ptr & 0xFF ); - src_hex += 2; - } - return 0; -} - - -static inline int -decode_insn(struct ud *u, uint16_t ptr) -{ - ASSERT((ptr & 0x8000) == 0); - u->itab_entry = &ud_itab[ ptr ]; - u->mnemonic = u->itab_entry->mnemonic; - return (resolve_mode(u) == 0 && - decode_operands(u) == 0 && - resolve_mnemonic(u) == 0) ? 0 : -1; -} - - -/* - * decode_3dnow() - * - * Decoding 3dnow is a little tricky because of its strange opcode - * structure. The final opcode disambiguation depends on the last - * byte that comes after the operands have been decoded. Fortunately, - * all 3dnow instructions have the same set of operand types. So we - * go ahead and decode the instruction by picking an arbitrarily chosen - * valid entry in the table, decode the operands, and read the final - * byte to resolve the menmonic. - */ -static inline int -decode_3dnow(struct ud* u) -{ - uint16_t ptr; - ASSERT(u->le->type == UD_TAB__OPC_3DNOW); - ASSERT(u->le->table[0xc] != 0); - decode_insn(u, u->le->table[0xc]); - ud_inp_next(u); - if (u->error) { - return -1; - } - ptr = u->le->table[ud_inp_curr(u)]; - ASSERT((ptr & 0x8000) == 0); - u->mnemonic = ud_itab[ptr].mnemonic; - return 0; -} - - -static int -decode_ssepfx(struct ud *u) -{ - uint8_t idx = ((u->pfx_insn & 0xf) + 1) / 2; - if (u->le->table[idx] == 0) { - idx = 0; - } - if (idx && u->le->table[idx] != 0) { - /* - * "Consume" the prefix as a part of the opcode, so it is no - * longer exported as an instruction prefix. - */ - switch (u->pfx_insn) { - case 0xf2: - u->pfx_repne = 0; - break; - case 0xf3: - u->pfx_rep = 0; - u->pfx_repe = 0; - break; - case 0x66: - u->pfx_opr = 0; - break; - } - } - return decode_ext(u, u->le->table[idx]); -} - - -/* - * decode_ext() - * - * Decode opcode extensions (if any) - */ -static int -decode_ext(struct ud *u, uint16_t ptr) -{ - uint8_t idx = 0; - if ((ptr & 0x8000) == 0) { - return decode_insn(u, ptr); - } - u->le = &ud_lookup_table_list[(~0x8000 & ptr)]; - if (u->le->type == UD_TAB__OPC_3DNOW) { - return decode_3dnow(u); - } - - switch (u->le->type) { - case UD_TAB__OPC_MOD: - /* !11 = 0, 11 = 1 */ - idx = (MODRM_MOD(modrm(u)) + 1) / 4; - break; - /* disassembly mode/operand size/address size based tables. - * 16 = 0,, 32 = 1, 64 = 2 - */ - case UD_TAB__OPC_MODE: - idx = u->dis_mode / 32; - break; - case UD_TAB__OPC_OSIZE: - idx = eff_opr_mode(u->dis_mode, REX_W(u->pfx_rex), u->pfx_opr) / 32; - break; - case UD_TAB__OPC_ASIZE: - idx = eff_adr_mode(u->dis_mode, u->pfx_adr) / 32; - break; - case UD_TAB__OPC_X87: - idx = modrm(u) - 0xC0; - break; - case UD_TAB__OPC_VENDOR: - if (u->vendor == UD_VENDOR_ANY) { - /* choose a valid entry */ - idx = (u->le->table[idx] != 0) ? 0 : 1; - } else if (u->vendor == UD_VENDOR_AMD) { - idx = 0; - } else { - idx = 1; - } - break; - case UD_TAB__OPC_RM: - idx = MODRM_RM(modrm(u)); - break; - case UD_TAB__OPC_REG: - idx = MODRM_REG(modrm(u)); - break; - case UD_TAB__OPC_SSE: - return decode_ssepfx(u); - default: - ASSERT(!"not reached"); - break; - } - - return decode_ext(u, u->le->table[idx]); -} - - -static inline int -decode_opcode(struct ud *u) -{ - uint16_t ptr; - ASSERT(u->le->type == UD_TAB__OPC_TABLE); - ud_inp_next(u); - if (u->error) { - return -1; - } - ptr = u->le->table[ud_inp_curr(u)]; - if (ptr & 0x8000) { - u->le = &ud_lookup_table_list[ptr & ~0x8000]; - if (u->le->type == UD_TAB__OPC_TABLE) { - return decode_opcode(u); - } - } - return decode_ext(u, ptr); -} - - -/* ============================================================================= - * ud_decode() - Instruction decoder. Returns the number of bytes decoded. - * ============================================================================= - */ -unsigned int -ud_decode(struct ud *u) -{ - ud_inp_start(u); - clear_insn(u); - u->le = &ud_lookup_table_list[0]; - u->error = decode_prefixes(u) == -1 || - decode_opcode(u) == -1 || - u->error; - /* Handle decode error. */ - if (u->error) { - /* clear out the decode data. */ - clear_insn(u); - /* mark the sequence of bytes as invalid. */ - u->itab_entry = & s_ie__invalid; - u->mnemonic = u->itab_entry->mnemonic; - } - - /* maybe this stray segment override byte - * should be spewed out? - */ - if ( !P_SEG( u->itab_entry->prefix ) && - u->operand[0].type != UD_OP_MEM && - u->operand[1].type != UD_OP_MEM ) - u->pfx_seg = 0; - - u->insn_offset = u->pc; /* set offset of instruction */ - u->insn_fill = 0; /* set translation buffer index to 0 */ - u->pc += u->inp_ctr; /* move program counter by bytes decoded */ - gen_hex( u ); /* generate hex code */ - - /* return number of bytes disassembled. */ - return u->inp_ctr; -} - -/* -vim: set ts=2 sw=2 expandtab -*/ - -#endif // USE(UDIS86) diff --git a/3rdparty/masm/disassembler/udis86/udis86_decode.h b/3rdparty/masm/disassembler/udis86/udis86_decode.h deleted file mode 100644 index 940ed5ad6f..0000000000 --- a/3rdparty/masm/disassembler/udis86/udis86_decode.h +++ /dev/null @@ -1,258 +0,0 @@ -/* udis86 - libudis86/decode.h - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef UD_DECODE_H -#define UD_DECODE_H - -#include "udis86_types.h" -#include "udis86_itab.h" - -#define MAX_INSN_LENGTH 15 - -/* register classes */ -#define T_NONE 0 -#define T_GPR 1 -#define T_MMX 2 -#define T_CRG 3 -#define T_DBG 4 -#define T_SEG 5 -#define T_XMM 6 - -/* itab prefix bits */ -#define P_none ( 0 ) -#define P_cast ( 1 << 0 ) -#define P_CAST(n) ( ( n >> 0 ) & 1 ) -#define P_c1 ( 1 << 0 ) -#define P_C1(n) ( ( n >> 0 ) & 1 ) -#define P_rexb ( 1 << 1 ) -#define P_REXB(n) ( ( n >> 1 ) & 1 ) -#define P_depM ( 1 << 2 ) -#define P_DEPM(n) ( ( n >> 2 ) & 1 ) -#define P_c3 ( 1 << 3 ) -#define P_C3(n) ( ( n >> 3 ) & 1 ) -#define P_inv64 ( 1 << 4 ) -#define P_INV64(n) ( ( n >> 4 ) & 1 ) -#define P_rexw ( 1 << 5 ) -#define P_REXW(n) ( ( n >> 5 ) & 1 ) -#define P_c2 ( 1 << 6 ) -#define P_C2(n) ( ( n >> 6 ) & 1 ) -#define P_def64 ( 1 << 7 ) -#define P_DEF64(n) ( ( n >> 7 ) & 1 ) -#define P_rexr ( 1 << 8 ) -#define P_REXR(n) ( ( n >> 8 ) & 1 ) -#define P_oso ( 1 << 9 ) -#define P_OSO(n) ( ( n >> 9 ) & 1 ) -#define P_aso ( 1 << 10 ) -#define P_ASO(n) ( ( n >> 10 ) & 1 ) -#define P_rexx ( 1 << 11 ) -#define P_REXX(n) ( ( n >> 11 ) & 1 ) -#define P_ImpAddr ( 1 << 12 ) -#define P_IMPADDR(n) ( ( n >> 12 ) & 1 ) -#define P_seg ( 1 << 13 ) -#define P_SEG(n) ( ( n >> 13 ) & 1 ) -#define P_sext ( 1 << 14 ) -#define P_SEXT(n) ( ( n >> 14 ) & 1 ) - -/* rex prefix bits */ -#define REX_W(r) ( ( 0xF & ( r ) ) >> 3 ) -#define REX_R(r) ( ( 0x7 & ( r ) ) >> 2 ) -#define REX_X(r) ( ( 0x3 & ( r ) ) >> 1 ) -#define REX_B(r) ( ( 0x1 & ( r ) ) >> 0 ) -#define REX_PFX_MASK(n) ( ( P_REXW(n) << 3 ) | \ - ( P_REXR(n) << 2 ) | \ - ( P_REXX(n) << 1 ) | \ - ( P_REXB(n) << 0 ) ) - -/* scable-index-base bits */ -#define SIB_S(b) ( ( b ) >> 6 ) -#define SIB_I(b) ( ( ( b ) >> 3 ) & 7 ) -#define SIB_B(b) ( ( b ) & 7 ) - -/* modrm bits */ -#define MODRM_REG(b) ( ( ( b ) >> 3 ) & 7 ) -#define MODRM_NNN(b) ( ( ( b ) >> 3 ) & 7 ) -#define MODRM_MOD(b) ( ( ( b ) >> 6 ) & 3 ) -#define MODRM_RM(b) ( ( b ) & 7 ) - -/* operand type constants -- order is important! */ - -enum ud_operand_code { - OP_NONE, - - OP_A, OP_E, OP_M, OP_G, - OP_I, - - OP_AL, OP_CL, OP_DL, OP_BL, - OP_AH, OP_CH, OP_DH, OP_BH, - - OP_ALr8b, OP_CLr9b, OP_DLr10b, OP_BLr11b, - OP_AHr12b, OP_CHr13b, OP_DHr14b, OP_BHr15b, - - OP_AX, OP_CX, OP_DX, OP_BX, - OP_SI, OP_DI, OP_SP, OP_BP, - - OP_rAX, OP_rCX, OP_rDX, OP_rBX, - OP_rSP, OP_rBP, OP_rSI, OP_rDI, - - OP_rAXr8, OP_rCXr9, OP_rDXr10, OP_rBXr11, - OP_rSPr12, OP_rBPr13, OP_rSIr14, OP_rDIr15, - - OP_eAX, OP_eCX, OP_eDX, OP_eBX, - OP_eSP, OP_eBP, OP_eSI, OP_eDI, - - OP_ES, OP_CS, OP_SS, OP_DS, - OP_FS, OP_GS, - - OP_ST0, OP_ST1, OP_ST2, OP_ST3, - OP_ST4, OP_ST5, OP_ST6, OP_ST7, - - OP_J, OP_S, OP_O, - OP_I1, OP_I3, - - OP_V, OP_W, OP_Q, OP_P, - - OP_R, OP_C, OP_D, OP_VR, OP_PR, - - OP_MR -} UD_ATTR_PACKED; - - -/* operand size constants */ - -enum ud_operand_size { - SZ_NA = 0, - SZ_Z = 1, - SZ_V = 2, - SZ_P = 3, - SZ_WP = 4, - SZ_DP = 5, - SZ_MDQ = 6, - SZ_RDQ = 7, - - /* the following values are used as is, - * and thus hard-coded. changing them - * will break internals - */ - SZ_B = 8, - SZ_W = 16, - SZ_D = 32, - SZ_Q = 64, - SZ_T = 80, - SZ_O = 128, - - SZ_WV = 17, - SZ_BV = 18, - SZ_DY = 19 - -} UD_ATTR_PACKED; - - -/* A single operand of an entry in the instruction table. - * (internal use only) - */ -struct ud_itab_entry_operand -{ - enum ud_operand_code type; - enum ud_operand_size size; -}; - - -/* A single entry in an instruction table. - *(internal use only) - */ -struct ud_itab_entry -{ - enum ud_mnemonic_code mnemonic; - struct ud_itab_entry_operand operand1; - struct ud_itab_entry_operand operand2; - struct ud_itab_entry_operand operand3; - uint32_t prefix; -}; - -struct ud_lookup_table_list_entry { - const uint16_t *table; - enum ud_table_type type; - const char *meta; -}; - - -static inline unsigned int sse_pfx_idx( const unsigned int pfx ) -{ - /* 00 = 0 - * f2 = 1 - * f3 = 2 - * 66 = 3 - */ - return ( ( pfx & 0xf ) + 1 ) / 2; -} - -static inline unsigned int mode_idx( const unsigned int mode ) -{ - /* 16 = 0 - * 32 = 1 - * 64 = 2 - */ - return ( mode / 32 ); -} - -static inline unsigned int modrm_mod_idx( const unsigned int mod ) -{ - /* !11 = 0 - * 11 = 1 - */ - return ( mod + 1 ) / 4; -} - -static inline unsigned int vendor_idx( const unsigned int vendor ) -{ - switch ( vendor ) { - case UD_VENDOR_AMD: return 0; - case UD_VENDOR_INTEL: return 1; - case UD_VENDOR_ANY: return 2; - default: return 2; - } -} - -static inline unsigned int is_group_ptr( uint16_t ptr ) -{ - return ( 0x8000 & ptr ); -} - -static inline unsigned int group_idx( uint16_t ptr ) -{ - return ( ~0x8000 & ptr ); -} - - -extern struct ud_itab_entry ud_itab[]; -extern struct ud_lookup_table_list_entry ud_lookup_table_list[]; - -#endif /* UD_DECODE_H */ - -/* vim:cindent - * vim:expandtab - * vim:ts=4 - * vim:sw=4 - */ diff --git a/3rdparty/masm/disassembler/udis86/udis86_extern.h b/3rdparty/masm/disassembler/udis86/udis86_extern.h deleted file mode 100644 index 8e87721e8c..0000000000 --- a/3rdparty/masm/disassembler/udis86/udis86_extern.h +++ /dev/null @@ -1,88 +0,0 @@ -/* udis86 - libudis86/extern.h - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef UD_EXTERN_H -#define UD_EXTERN_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "udis86_types.h" - -/* ============================= PUBLIC API ================================= */ - -extern void ud_init(struct ud*); - -extern void ud_set_mode(struct ud*, uint8_t); - -extern void ud_set_pc(struct ud*, uint64_t); - -extern void ud_set_input_hook(struct ud*, int (*)(struct ud*)); - -extern void ud_set_input_buffer(struct ud*, uint8_t*, size_t); - -#ifndef __UD_STANDALONE__ -extern void ud_set_input_file(struct ud*, FILE*); -#endif /* __UD_STANDALONE__ */ - -extern void ud_set_vendor(struct ud*, unsigned); - -extern void ud_set_syntax(struct ud*, void (*)(struct ud*)); - -extern void ud_input_skip(struct ud*, size_t); - -extern int ud_input_end(struct ud*); - -extern unsigned int ud_decode(struct ud*); - -extern unsigned int ud_disassemble(struct ud*); - -extern void ud_translate_intel(struct ud*); - -extern void ud_translate_att(struct ud*); - -extern char* ud_insn_asm(struct ud* u); - -extern uint8_t* ud_insn_ptr(struct ud* u); - -extern uint64_t ud_insn_off(struct ud*); - -extern char* ud_insn_hex(struct ud*); - -extern unsigned int ud_insn_len(struct ud* u); - -extern const char* ud_lookup_mnemonic(enum ud_mnemonic_code c); - -extern void ud_set_user_opaque_data(struct ud*, void*); - -extern void *ud_get_user_opaque_data(struct ud*); - -/* ========================================================================== */ - -#ifdef __cplusplus -} -#endif -#endif diff --git a/3rdparty/masm/disassembler/udis86/udis86_input.c b/3rdparty/masm/disassembler/udis86/udis86_input.c deleted file mode 100644 index 76c6cccf36..0000000000 --- a/3rdparty/masm/disassembler/udis86/udis86_input.c +++ /dev/null @@ -1,263 +0,0 @@ -/* udis86 - libudis86/input.c - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "config.h" -#include - -#if USE(UDIS86) - -#include "udis86_extern.h" -#include "udis86_types.h" -#include "udis86_input.h" - -/* ----------------------------------------------------------------------------- - * inp_buff_hook() - Hook for buffered inputs. - * ----------------------------------------------------------------------------- - */ -static int -inp_buff_hook(struct ud* u) -{ - if (u->inp_buff < u->inp_buff_end) - return *u->inp_buff++; - else return -1; -} - -#ifndef __UD_STANDALONE__ -/* ----------------------------------------------------------------------------- - * inp_file_hook() - Hook for FILE inputs. - * ----------------------------------------------------------------------------- - */ -static int -inp_file_hook(struct ud* u) -{ - return fgetc(u->inp_file); -} -#endif /* __UD_STANDALONE__*/ - -/* ============================================================================= - * ud_inp_set_hook() - Sets input hook. - * ============================================================================= - */ -extern void -ud_set_input_hook(register struct ud* u, int (*hook)(struct ud*)) -{ - u->inp_hook = hook; - ud_inp_init(u); -} - -extern void -ud_set_user_opaque_data( struct ud * u, void * opaque ) -{ - u->user_opaque_data = opaque; -} - -extern void * -ud_get_user_opaque_data( struct ud * u ) -{ - return u->user_opaque_data; -} - -/* ============================================================================= - * ud_inp_set_buffer() - Set buffer as input. - * ============================================================================= - */ -extern void -ud_set_input_buffer(register struct ud* u, uint8_t* buf, size_t len) -{ - u->inp_hook = inp_buff_hook; - u->inp_buff = buf; - u->inp_buff_end = buf + len; - ud_inp_init(u); -} - -#ifndef __UD_STANDALONE__ -/* ============================================================================= - * ud_input_set_file() - Set buffer as input. - * ============================================================================= - */ -extern void -ud_set_input_file(register struct ud* u, FILE* f) -{ - u->inp_hook = inp_file_hook; - u->inp_file = f; - ud_inp_init(u); -} -#endif /* __UD_STANDALONE__ */ - -/* ============================================================================= - * ud_input_skip() - Skip n input bytes. - * ============================================================================= - */ -extern void -ud_input_skip(struct ud* u, size_t n) -{ - while (n--) { - u->inp_hook(u); - } -} - -/* ============================================================================= - * ud_input_end() - Test for end of input. - * ============================================================================= - */ -extern int -ud_input_end(struct ud* u) -{ - return (u->inp_curr == u->inp_fill) && u->inp_end; -} - -/* ----------------------------------------------------------------------------- - * ud_inp_next() - Loads and returns the next byte from input. - * - * inp_curr and inp_fill are pointers to the cache. The program is written based - * on the property that they are 8-bits in size, and will eventually wrap around - * forming a circular buffer. So, the size of the cache is 256 in size, kind of - * unnecessary yet optimized. - * - * A buffer inp_sess stores the bytes disassembled for a single session. - * ----------------------------------------------------------------------------- - */ -extern uint8_t ud_inp_next(struct ud* u) -{ - int c = -1; - /* if current pointer is not upto the fill point in the - * input cache. - */ - if ( u->inp_curr != u->inp_fill ) { - c = u->inp_cache[ ++u->inp_curr ]; - /* if !end-of-input, call the input hook and get a byte */ - } else if ( u->inp_end || ( c = u->inp_hook( u ) ) == -1 ) { - /* end-of-input, mark it as an error, since the decoder, - * expected a byte more. - */ - u->error = 1; - /* flag end of input */ - u->inp_end = 1; - return 0; - } else { - /* increment pointers, we have a new byte. */ - u->inp_curr = ++u->inp_fill; - /* add the byte to the cache */ - u->inp_cache[ u->inp_fill ] = c; - } - /* record bytes input per decode-session. */ - u->inp_sess[ u->inp_ctr++ ] = c; - /* return byte */ - return ( uint8_t ) c; -} - -/* ----------------------------------------------------------------------------- - * ud_inp_back() - Move back a single byte in the stream. - * ----------------------------------------------------------------------------- - */ -extern void -ud_inp_back(struct ud* u) -{ - if ( u->inp_ctr > 0 ) { - --u->inp_curr; - --u->inp_ctr; - } -} - -/* ----------------------------------------------------------------------------- - * ud_inp_peek() - Peek into the next byte in source. - * ----------------------------------------------------------------------------- - */ -extern uint8_t -ud_inp_peek(struct ud* u) -{ - uint8_t r = ud_inp_next(u); - if ( !u->error ) ud_inp_back(u); /* Don't backup if there was an error */ - return r; -} - -/* ----------------------------------------------------------------------------- - * ud_inp_move() - Move ahead n input bytes. - * ----------------------------------------------------------------------------- - */ -extern void -ud_inp_move(struct ud* u, size_t n) -{ - while (n--) - ud_inp_next(u); -} - -/*------------------------------------------------------------------------------ - * ud_inp_uintN() - return uintN from source. - *------------------------------------------------------------------------------ - */ -extern uint8_t -ud_inp_uint8(struct ud* u) -{ - return ud_inp_next(u); -} - -extern uint16_t -ud_inp_uint16(struct ud* u) -{ - uint16_t r, ret; - - ret = ud_inp_next(u); - r = ud_inp_next(u); - return ret | (r << 8); -} - -extern uint32_t -ud_inp_uint32(struct ud* u) -{ - uint32_t r, ret; - - ret = ud_inp_next(u); - r = ud_inp_next(u); - ret = ret | (r << 8); - r = ud_inp_next(u); - ret = ret | (r << 16); - r = ud_inp_next(u); - return ret | (r << 24); -} - -extern uint64_t -ud_inp_uint64(struct ud* u) -{ - uint64_t r, ret; - - ret = ud_inp_next(u); - r = ud_inp_next(u); - ret = ret | (r << 8); - r = ud_inp_next(u); - ret = ret | (r << 16); - r = ud_inp_next(u); - ret = ret | (r << 24); - r = ud_inp_next(u); - ret = ret | (r << 32); - r = ud_inp_next(u); - ret = ret | (r << 40); - r = ud_inp_next(u); - ret = ret | (r << 48); - r = ud_inp_next(u); - return ret | (r << 56); -} - -#endif // USE(UDIS86) diff --git a/3rdparty/masm/disassembler/udis86/udis86_input.h b/3rdparty/masm/disassembler/udis86/udis86_input.h deleted file mode 100644 index 96865a88b5..0000000000 --- a/3rdparty/masm/disassembler/udis86/udis86_input.h +++ /dev/null @@ -1,67 +0,0 @@ -/* udis86 - libudis86/input.h - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef UD_INPUT_H -#define UD_INPUT_H - -#include "udis86_types.h" - -uint8_t ud_inp_next(struct ud*); -uint8_t ud_inp_peek(struct ud*); -uint8_t ud_inp_uint8(struct ud*); -uint16_t ud_inp_uint16(struct ud*); -uint32_t ud_inp_uint32(struct ud*); -uint64_t ud_inp_uint64(struct ud*); -void ud_inp_move(struct ud*, size_t); -void ud_inp_back(struct ud*); - -/* ud_inp_init() - Initializes the input system. */ -#define ud_inp_init(u) \ -do { \ - u->inp_curr = 0; \ - u->inp_fill = 0; \ - u->inp_ctr = 0; \ - u->inp_end = 0; \ -} while (0) - -/* ud_inp_start() - Should be called before each de-code operation. */ -#define ud_inp_start(u) u->inp_ctr = 0 - -/* ud_inp_back() - Resets the current pointer to its position before the current - * instruction disassembly was started. - */ -#define ud_inp_reset(u) \ -do { \ - u->inp_curr -= u->inp_ctr; \ - u->inp_ctr = 0; \ -} while (0) - -/* ud_inp_sess() - Returns the pointer to current session. */ -#define ud_inp_sess(u) (u->inp_sess) - -/* inp_cur() - Returns the current input byte. */ -#define ud_inp_curr(u) ((u)->inp_cache[(u)->inp_curr]) - -#endif diff --git a/3rdparty/masm/disassembler/udis86/udis86_itab_holder.c b/3rdparty/masm/disassembler/udis86/udis86_itab_holder.c deleted file mode 100644 index d5d8726d6a..0000000000 --- a/3rdparty/masm/disassembler/udis86/udis86_itab_holder.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include - -#if USE(UDIS86) - -#include "udis86_itab.c" - -#endif - diff --git a/3rdparty/masm/disassembler/udis86/udis86_syn-att.c b/3rdparty/masm/disassembler/udis86/udis86_syn-att.c deleted file mode 100644 index 155a34ca29..0000000000 --- a/3rdparty/masm/disassembler/udis86/udis86_syn-att.c +++ /dev/null @@ -1,253 +0,0 @@ -/* udis86 - libudis86/syn-att.c - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "config.h" -#include - -#if USE(UDIS86) - -#include "udis86_types.h" -#include "udis86_extern.h" -#include "udis86_decode.h" -#include "udis86_itab.h" -#include "udis86_syn.h" - -/* ----------------------------------------------------------------------------- - * opr_cast() - Prints an operand cast. - * ----------------------------------------------------------------------------- - */ -static void -opr_cast(struct ud* u, struct ud_operand* op) -{ - switch(op->size) { - case 16 : case 32 : - mkasm(u, "*"); break; - default: break; - } -} - -/* ----------------------------------------------------------------------------- - * gen_operand() - Generates assembly output for each operand. - * ----------------------------------------------------------------------------- - */ -static void -gen_operand(struct ud* u, struct ud_operand* op) -{ - switch(op->type) { - case UD_OP_REG: - mkasm(u, "%%%s", ud_reg_tab[op->base - UD_R_AL]); - break; - - case UD_OP_MEM: - if (u->br_far) opr_cast(u, op); - if (u->pfx_seg) - mkasm(u, "%%%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); - if (op->offset == 8) { - if (op->lval.sbyte < 0) - mkasm(u, "-0x%x", (-op->lval.sbyte) & 0xff); - else mkasm(u, "0x%x", op->lval.sbyte); - } - else if (op->offset == 16) - mkasm(u, "0x%x", op->lval.uword); - else if (op->offset == 32) - mkasm(u, "0x%lx", (unsigned long)op->lval.udword); - else if (op->offset == 64) - mkasm(u, "0x" FMT64 "x", op->lval.uqword); - - if (op->base) - mkasm(u, "(%%%s", ud_reg_tab[op->base - UD_R_AL]); - if (op->index) { - if (op->base) - mkasm(u, ","); - else mkasm(u, "("); - mkasm(u, "%%%s", ud_reg_tab[op->index - UD_R_AL]); - } - if (op->scale) - mkasm(u, ",%d", op->scale); - if (op->base || op->index) - mkasm(u, ")"); - break; - - case UD_OP_IMM: { - int64_t imm = 0; - uint64_t sext_mask = 0xffffffffffffffffull; - unsigned sext_size = op->size; - - switch (op->size) { - case 8: imm = op->lval.sbyte; break; - case 16: imm = op->lval.sword; break; - case 32: imm = op->lval.sdword; break; - case 64: imm = op->lval.sqword; break; - } - if ( P_SEXT( u->itab_entry->prefix ) ) { - sext_size = u->operand[ 0 ].size; - if ( u->mnemonic == UD_Ipush ) - /* push sign-extends to operand size */ - sext_size = u->opr_mode; - } - if ( sext_size < 64 ) - sext_mask = ( 1ull << sext_size ) - 1; - mkasm( u, "$0x" FMT64 "x", imm & sext_mask ); - - break; - } - - case UD_OP_JIMM: - switch (op->size) { - case 8: - mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sbyte); - break; - case 16: - mkasm(u, "0x" FMT64 "x", (u->pc + op->lval.sword) & 0xffff ); - break; - case 32: - if (u->dis_mode == 32) - mkasm(u, "0x" FMT64 "x", (u->pc + op->lval.sdword) & 0xffffffff); - else - mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sdword); - break; - default:break; - } - break; - - case UD_OP_PTR: - switch (op->size) { - case 32: - mkasm(u, "$0x%x, $0x%x", op->lval.ptr.seg, - op->lval.ptr.off & 0xFFFF); - break; - case 48: - mkasm(u, "$0x%x, $0x%lx", op->lval.ptr.seg, - (unsigned long)op->lval.ptr.off); - break; - } - break; - - default: return; - } -} - -/* ============================================================================= - * translates to AT&T syntax - * ============================================================================= - */ -extern void -ud_translate_att(struct ud *u) -{ - int size = 0; - - /* check if P_OSO prefix is used */ - if (! P_OSO(u->itab_entry->prefix) && u->pfx_opr) { - switch (u->dis_mode) { - case 16: - mkasm(u, "o32 "); - break; - case 32: - case 64: - mkasm(u, "o16 "); - break; - } - } - - /* check if P_ASO prefix was used */ - if (! P_ASO(u->itab_entry->prefix) && u->pfx_adr) { - switch (u->dis_mode) { - case 16: - mkasm(u, "a32 "); - break; - case 32: - mkasm(u, "a16 "); - break; - case 64: - mkasm(u, "a32 "); - break; - } - } - - if (u->pfx_lock) - mkasm(u, "lock "); - if (u->pfx_rep) - mkasm(u, "rep "); - if (u->pfx_repne) - mkasm(u, "repne "); - - /* special instructions */ - switch (u->mnemonic) { - case UD_Iretf: - mkasm(u, "lret "); - break; - case UD_Idb: - mkasm(u, ".byte 0x%x", u->operand[0].lval.ubyte); - return; - case UD_Ijmp: - case UD_Icall: - if (u->br_far) mkasm(u, "l"); - mkasm(u, "%s", ud_lookup_mnemonic(u->mnemonic)); - break; - case UD_Ibound: - case UD_Ienter: - if (u->operand[0].type != UD_NONE) - gen_operand(u, &u->operand[0]); - if (u->operand[1].type != UD_NONE) { - mkasm(u, ","); - gen_operand(u, &u->operand[1]); - } - return; - default: - mkasm(u, "%s", ud_lookup_mnemonic(u->mnemonic)); - } - - if (u->c1) - size = u->operand[0].size; - else if (u->c2) - size = u->operand[1].size; - else if (u->c3) - size = u->operand[2].size; - - if (size == 8) - mkasm(u, "b"); - else if (size == 16) - mkasm(u, "w"); - else if (size == 64) - mkasm(u, "q"); - - mkasm(u, " "); - - if (u->operand[2].type != UD_NONE) { - gen_operand(u, &u->operand[2]); - mkasm(u, ", "); - } - - if (u->operand[1].type != UD_NONE) { - gen_operand(u, &u->operand[1]); - mkasm(u, ", "); - } - - if (u->operand[0].type != UD_NONE) - gen_operand(u, &u->operand[0]); -} - -#endif // USE(UDIS86) - diff --git a/3rdparty/masm/disassembler/udis86/udis86_syn-intel.c b/3rdparty/masm/disassembler/udis86/udis86_syn-intel.c deleted file mode 100644 index d250bd449c..0000000000 --- a/3rdparty/masm/disassembler/udis86/udis86_syn-intel.c +++ /dev/null @@ -1,279 +0,0 @@ -/* udis86 - libudis86/syn-intel.c - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "config.h" -#include - -#if USE(UDIS86) - -#include "udis86_types.h" -#include "udis86_extern.h" -#include "udis86_decode.h" -#include "udis86_itab.h" -#include "udis86_syn.h" - -/* ----------------------------------------------------------------------------- - * opr_cast() - Prints an operand cast. - * ----------------------------------------------------------------------------- - */ -static void -opr_cast(struct ud* u, struct ud_operand* op) -{ - switch(op->size) { - case 8: mkasm(u, "byte " ); break; - case 16: mkasm(u, "word " ); break; - case 32: mkasm(u, "dword "); break; - case 64: mkasm(u, "qword "); break; - case 80: mkasm(u, "tword "); break; - default: break; - } - if (u->br_far) - mkasm(u, "far "); -} - -/* ----------------------------------------------------------------------------- - * gen_operand() - Generates assembly output for each operand. - * ----------------------------------------------------------------------------- - */ -static void gen_operand(struct ud* u, struct ud_operand* op, int syn_cast) -{ - switch(op->type) { - case UD_OP_REG: - mkasm(u, "%s", ud_reg_tab[op->base - UD_R_AL]); - break; - - case UD_OP_MEM: { - - int op_f = 0; - - if (syn_cast) - opr_cast(u, op); - - mkasm(u, "["); - - if (u->pfx_seg) - mkasm(u, "%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); - - if (op->base) { - mkasm(u, "%s", ud_reg_tab[op->base - UD_R_AL]); - op_f = 1; - } - - if (op->index) { - if (op_f) - mkasm(u, "+"); - mkasm(u, "%s", ud_reg_tab[op->index - UD_R_AL]); - op_f = 1; - } - - if (op->scale) - mkasm(u, "*%d", op->scale); - - if (op->offset == 8) { - if (op->lval.sbyte < 0) - mkasm(u, "-0x%x", -op->lval.sbyte); - else mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.sbyte); - } - else if (op->offset == 16) - mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.uword); - else if (op->offset == 32) { - if (u->adr_mode == 64) { - if (op->lval.sdword < 0) - mkasm(u, "-0x%x", -op->lval.sdword); - else mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.sdword); - } - else mkasm(u, "%s0x%lx", (op_f) ? "+" : "", (unsigned long)op->lval.udword); - } - else if (op->offset == 64) - mkasm(u, "%s0x" FMT64 "x", (op_f) ? "+" : "", op->lval.uqword); - - mkasm(u, "]"); - break; - } - - case UD_OP_IMM: { - int64_t imm = 0; - uint64_t sext_mask = 0xffffffffffffffffull; - unsigned sext_size = op->size; - - if (syn_cast) - opr_cast(u, op); - switch (op->size) { - case 8: imm = op->lval.sbyte; break; - case 16: imm = op->lval.sword; break; - case 32: imm = op->lval.sdword; break; - case 64: imm = op->lval.sqword; break; - } - if ( P_SEXT( u->itab_entry->prefix ) ) { - sext_size = u->operand[ 0 ].size; - if ( u->mnemonic == UD_Ipush ) - /* push sign-extends to operand size */ - sext_size = u->opr_mode; - } - if ( sext_size < 64 ) - sext_mask = ( 1ull << sext_size ) - 1; - mkasm( u, "0x" FMT64 "x", imm & sext_mask ); - - break; - } - - - case UD_OP_JIMM: - if (syn_cast) opr_cast(u, op); - switch (op->size) { - case 8: - mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sbyte); - break; - case 16: - mkasm(u, "0x" FMT64 "x", ( u->pc + op->lval.sword ) & 0xffff ); - break; - case 32: - mkasm(u, "0x" FMT64 "x", ( u->pc + op->lval.sdword ) & 0xfffffffful ); - break; - default:break; - } - break; - - case UD_OP_PTR: - switch (op->size) { - case 32: - mkasm(u, "word 0x%x:0x%x", op->lval.ptr.seg, - op->lval.ptr.off & 0xFFFF); - break; - case 48: - mkasm(u, "dword 0x%x:0x%lx", op->lval.ptr.seg, - (unsigned long)op->lval.ptr.off); - break; - } - break; - - case UD_OP_CONST: - if (syn_cast) opr_cast(u, op); - mkasm(u, "%d", op->lval.udword); - break; - - default: return; - } -} - -/* ============================================================================= - * translates to intel syntax - * ============================================================================= - */ -extern void ud_translate_intel(struct ud* u) -{ - /* -- prefixes -- */ - - /* check if P_OSO prefix is used */ - if (! P_OSO(u->itab_entry->prefix) && u->pfx_opr) { - switch (u->dis_mode) { - case 16: - mkasm(u, "o32 "); - break; - case 32: - case 64: - mkasm(u, "o16 "); - break; - } - } - - /* check if P_ASO prefix was used */ - if (! P_ASO(u->itab_entry->prefix) && u->pfx_adr) { - switch (u->dis_mode) { - case 16: - mkasm(u, "a32 "); - break; - case 32: - mkasm(u, "a16 "); - break; - case 64: - mkasm(u, "a32 "); - break; - } - } - - if ( u->pfx_seg && - u->operand[0].type != UD_OP_MEM && - u->operand[1].type != UD_OP_MEM ) { - mkasm(u, "%s ", ud_reg_tab[u->pfx_seg - UD_R_AL]); - } - if (u->pfx_lock) - mkasm(u, "lock "); - if (u->pfx_rep) - mkasm(u, "rep "); - if (u->pfx_repne) - mkasm(u, "repne "); - - /* print the instruction mnemonic */ - mkasm(u, "%s ", ud_lookup_mnemonic(u->mnemonic)); - - /* operand 1 */ - if (u->operand[0].type != UD_NONE) { - int cast = 0; - if ( u->operand[0].type == UD_OP_IMM && - u->operand[1].type == UD_NONE ) - cast = u->c1; - if ( u->operand[0].type == UD_OP_MEM ) { - cast = u->c1; - if ( u->operand[1].type == UD_OP_IMM || - u->operand[1].type == UD_OP_CONST ) - cast = 1; - if ( u->operand[1].type == UD_NONE ) - cast = 1; - if ( ( u->operand[0].size != u->operand[1].size ) && u->operand[1].size ) - cast = 1; - } else if ( u->operand[ 0 ].type == UD_OP_JIMM ) { - if ( u->operand[ 0 ].size > 8 ) cast = 1; - } - gen_operand(u, &u->operand[0], cast); - } - /* operand 2 */ - if (u->operand[1].type != UD_NONE) { - int cast = 0; - mkasm(u, ", "); - if ( u->operand[1].type == UD_OP_MEM ) { - cast = u->c1; - - if ( u->operand[0].type != UD_OP_REG ) - cast = 1; - if ( u->operand[0].size != u->operand[1].size && u->operand[1].size ) - cast = 1; - if ( u->operand[0].type == UD_OP_REG && - u->operand[0].base >= UD_R_ES && - u->operand[0].base <= UD_R_GS ) - cast = 0; - } - gen_operand(u, &u->operand[1], cast ); - } - - /* operand 3 */ - if (u->operand[2].type != UD_NONE) { - mkasm(u, ", "); - gen_operand(u, &u->operand[2], u->c3); - } -} - -#endif // USE(UDIS86) - diff --git a/3rdparty/masm/disassembler/udis86/udis86_syn.c b/3rdparty/masm/disassembler/udis86/udis86_syn.c deleted file mode 100644 index 80391b4a08..0000000000 --- a/3rdparty/masm/disassembler/udis86/udis86_syn.c +++ /dev/null @@ -1,87 +0,0 @@ -/* udis86 - libudis86/syn.c - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include - -#if USE(UDIS86) - -/* ----------------------------------------------------------------------------- - * Intel Register Table - Order Matters (types.h)! - * ----------------------------------------------------------------------------- - */ -const char* ud_reg_tab[] = -{ - "al", "cl", "dl", "bl", - "ah", "ch", "dh", "bh", - "spl", "bpl", "sil", "dil", - "r8b", "r9b", "r10b", "r11b", - "r12b", "r13b", "r14b", "r15b", - - "ax", "cx", "dx", "bx", - "sp", "bp", "si", "di", - "r8w", "r9w", "r10w", "r11w", - "r12w", "r13W" , "r14w", "r15w", - - "eax", "ecx", "edx", "ebx", - "esp", "ebp", "esi", "edi", - "r8d", "r9d", "r10d", "r11d", - "r12d", "r13d", "r14d", "r15d", - - "rax", "rcx", "rdx", "rbx", - "rsp", "rbp", "rsi", "rdi", - "r8", "r9", "r10", "r11", - "r12", "r13", "r14", "r15", - - "es", "cs", "ss", "ds", - "fs", "gs", - - "cr0", "cr1", "cr2", "cr3", - "cr4", "cr5", "cr6", "cr7", - "cr8", "cr9", "cr10", "cr11", - "cr12", "cr13", "cr14", "cr15", - - "dr0", "dr1", "dr2", "dr3", - "dr4", "dr5", "dr6", "dr7", - "dr8", "dr9", "dr10", "dr11", - "dr12", "dr13", "dr14", "dr15", - - "mm0", "mm1", "mm2", "mm3", - "mm4", "mm5", "mm6", "mm7", - - "st0", "st1", "st2", "st3", - "st4", "st5", "st6", "st7", - - "xmm0", "xmm1", "xmm2", "xmm3", - "xmm4", "xmm5", "xmm6", "xmm7", - "xmm8", "xmm9", "xmm10", "xmm11", - "xmm12", "xmm13", "xmm14", "xmm15", - - "rip" -}; - -#endif // USE(UDIS86) - diff --git a/3rdparty/masm/disassembler/udis86/udis86_syn.h b/3rdparty/masm/disassembler/udis86/udis86_syn.h deleted file mode 100644 index e8636163ef..0000000000 --- a/3rdparty/masm/disassembler/udis86/udis86_syn.h +++ /dev/null @@ -1,47 +0,0 @@ -/* udis86 - libudis86/syn.h - * - * Copyright (c) 2002-2009 - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef UD_SYN_H -#define UD_SYN_H - -#include "udis86_types.h" -#include - -#ifndef __UD_STANDALONE__ -# include -#endif /* __UD_STANDALONE__ */ - -extern const char* ud_reg_tab[]; - -static void mkasm(struct ud* u, const char* fmt, ...) WTF_ATTRIBUTE_PRINTF(2, 3); -static void mkasm(struct ud* u, const char* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - u->insn_fill += vsnprintf((char*) u->insn_buffer + u->insn_fill, UD_STRING_BUFFER_SIZE - u->insn_fill, fmt, ap); - va_end(ap); -} - -#endif diff --git a/3rdparty/masm/disassembler/udis86/udis86_types.h b/3rdparty/masm/disassembler/udis86/udis86_types.h deleted file mode 100644 index 320d1ca491..0000000000 --- a/3rdparty/masm/disassembler/udis86/udis86_types.h +++ /dev/null @@ -1,238 +0,0 @@ -/* udis86 - libudis86/types.h - * - * Copyright (c) 2002-2009 Vivek Thampi - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef UD_TYPES_H -#define UD_TYPES_H - -#ifndef __UD_STANDALONE__ -# include -#endif /* __UD_STANDALONE__ */ - -/* gcc specific extensions */ -#ifdef __GNUC__ -# define UD_ATTR_PACKED __attribute__((packed)) -#else -# define UD_ATTR_PACKED -#endif /* UD_ATTR_PACKED */ - -#ifdef _MSC_VER -# define FMT64 "%I64" - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; -#else -# define FMT64 "%ll" -# ifndef __UD_STANDALONE__ -# include -# endif /* __UD_STANDALONE__ */ -#endif - -/* ----------------------------------------------------------------------------- - * All possible "types" of objects in udis86. Order is Important! - * ----------------------------------------------------------------------------- - */ -enum ud_type -{ - UD_NONE, - - /* 8 bit GPRs */ - UD_R_AL, UD_R_CL, UD_R_DL, UD_R_BL, - UD_R_AH, UD_R_CH, UD_R_DH, UD_R_BH, - UD_R_SPL, UD_R_BPL, UD_R_SIL, UD_R_DIL, - UD_R_R8B, UD_R_R9B, UD_R_R10B, UD_R_R11B, - UD_R_R12B, UD_R_R13B, UD_R_R14B, UD_R_R15B, - - /* 16 bit GPRs */ - UD_R_AX, UD_R_CX, UD_R_DX, UD_R_BX, - UD_R_SP, UD_R_BP, UD_R_SI, UD_R_DI, - UD_R_R8W, UD_R_R9W, UD_R_R10W, UD_R_R11W, - UD_R_R12W, UD_R_R13W, UD_R_R14W, UD_R_R15W, - - /* 32 bit GPRs */ - UD_R_EAX, UD_R_ECX, UD_R_EDX, UD_R_EBX, - UD_R_ESP, UD_R_EBP, UD_R_ESI, UD_R_EDI, - UD_R_R8D, UD_R_R9D, UD_R_R10D, UD_R_R11D, - UD_R_R12D, UD_R_R13D, UD_R_R14D, UD_R_R15D, - - /* 64 bit GPRs */ - UD_R_RAX, UD_R_RCX, UD_R_RDX, UD_R_RBX, - UD_R_RSP, UD_R_RBP, UD_R_RSI, UD_R_RDI, - UD_R_R8, UD_R_R9, UD_R_R10, UD_R_R11, - UD_R_R12, UD_R_R13, UD_R_R14, UD_R_R15, - - /* segment registers */ - UD_R_ES, UD_R_CS, UD_R_SS, UD_R_DS, - UD_R_FS, UD_R_GS, - - /* control registers*/ - UD_R_CR0, UD_R_CR1, UD_R_CR2, UD_R_CR3, - UD_R_CR4, UD_R_CR5, UD_R_CR6, UD_R_CR7, - UD_R_CR8, UD_R_CR9, UD_R_CR10, UD_R_CR11, - UD_R_CR12, UD_R_CR13, UD_R_CR14, UD_R_CR15, - - /* debug registers */ - UD_R_DR0, UD_R_DR1, UD_R_DR2, UD_R_DR3, - UD_R_DR4, UD_R_DR5, UD_R_DR6, UD_R_DR7, - UD_R_DR8, UD_R_DR9, UD_R_DR10, UD_R_DR11, - UD_R_DR12, UD_R_DR13, UD_R_DR14, UD_R_DR15, - - /* mmx registers */ - UD_R_MM0, UD_R_MM1, UD_R_MM2, UD_R_MM3, - UD_R_MM4, UD_R_MM5, UD_R_MM6, UD_R_MM7, - - /* x87 registers */ - UD_R_ST0, UD_R_ST1, UD_R_ST2, UD_R_ST3, - UD_R_ST4, UD_R_ST5, UD_R_ST6, UD_R_ST7, - - /* extended multimedia registers */ - UD_R_XMM0, UD_R_XMM1, UD_R_XMM2, UD_R_XMM3, - UD_R_XMM4, UD_R_XMM5, UD_R_XMM6, UD_R_XMM7, - UD_R_XMM8, UD_R_XMM9, UD_R_XMM10, UD_R_XMM11, - UD_R_XMM12, UD_R_XMM13, UD_R_XMM14, UD_R_XMM15, - - UD_R_RIP, - - /* Operand Types */ - UD_OP_REG, UD_OP_MEM, UD_OP_PTR, UD_OP_IMM, - UD_OP_JIMM, UD_OP_CONST -}; - -#include "udis86_itab.h" - -/* ----------------------------------------------------------------------------- - * struct ud_operand - Disassembled instruction Operand. - * ----------------------------------------------------------------------------- - */ -struct ud_operand -{ - enum ud_type type; - uint8_t size; - union { - int8_t sbyte; - uint8_t ubyte; - int16_t sword; - uint16_t uword; - int32_t sdword; - uint32_t udword; - int64_t sqword; - uint64_t uqword; - - struct { - uint16_t seg; - uint32_t off; - } ptr; - } lval; - - enum ud_type base; - enum ud_type index; - uint8_t offset; - uint8_t scale; -}; - -#define UD_STRING_BUFFER_SIZE 64 - -/* ----------------------------------------------------------------------------- - * struct ud - The udis86 object. - * ----------------------------------------------------------------------------- - */ -struct ud -{ - int (*inp_hook) (struct ud*); - uint8_t inp_curr; - uint8_t inp_fill; -#ifndef __UD_STANDALONE__ - FILE* inp_file; -#endif - uint8_t inp_ctr; - uint8_t* inp_buff; - uint8_t* inp_buff_end; - uint8_t inp_end; - void (*translator)(struct ud*); - uint64_t insn_offset; - char insn_hexcode[32]; - char insn_buffer[UD_STRING_BUFFER_SIZE]; - unsigned int insn_fill; - uint8_t dis_mode; - uint64_t pc; - uint8_t vendor; - struct map_entry* mapen; - enum ud_mnemonic_code mnemonic; - struct ud_operand operand[3]; - uint8_t error; - uint8_t pfx_rex; - uint8_t pfx_seg; - uint8_t pfx_opr; - uint8_t pfx_adr; - uint8_t pfx_lock; - uint8_t pfx_rep; - uint8_t pfx_repe; - uint8_t pfx_repne; - uint8_t pfx_insn; - uint8_t default64; - uint8_t opr_mode; - uint8_t adr_mode; - uint8_t br_far; - uint8_t br_near; - uint8_t implicit_addr; - uint8_t c1; - uint8_t c2; - uint8_t c3; - uint8_t inp_cache[256]; - uint8_t inp_sess[64]; - uint8_t have_modrm; - uint8_t modrm; - void * user_opaque_data; - struct ud_itab_entry * itab_entry; - struct ud_lookup_table_list_entry *le; -}; - -/* ----------------------------------------------------------------------------- - * Type-definitions - * ----------------------------------------------------------------------------- - */ -typedef enum ud_type ud_type_t; -typedef enum ud_mnemonic_code ud_mnemonic_code_t; - -typedef struct ud ud_t; -typedef struct ud_operand ud_operand_t; - -#define UD_SYN_INTEL ud_translate_intel -#define UD_SYN_ATT ud_translate_att -#define UD_EOI -1 -#define UD_INP_CACHE_SZ 32 -#define UD_VENDOR_AMD 0 -#define UD_VENDOR_INTEL 1 -#define UD_VENDOR_ANY 2 - -#define bail_out(ud,error_code) longjmp( (ud)->bailout, error_code ) -#define try_decode(ud) if ( setjmp( (ud)->bailout ) == 0 ) -#define catch_error() else - -#endif diff --git a/3rdparty/masm/jit/JITCompilationEffort.h b/3rdparty/masm/jit/JITCompilationEffort.h deleted file mode 100644 index 5eb6801789..0000000000 --- a/3rdparty/masm/jit/JITCompilationEffort.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef JITCompilationEffort_h -#define JITCompilationEffort_h - -namespace JSC { - -enum JITCompilationEffort { - JITCompilationCanFail, - JITCompilationMustSucceed -}; - -} // namespace JSC - -#endif // JITCompilationEffort_h - diff --git a/3rdparty/masm/masm.pri b/3rdparty/masm/masm.pri deleted file mode 100644 index a6d11f633c..0000000000 --- a/3rdparty/masm/masm.pri +++ /dev/null @@ -1,107 +0,0 @@ - -HEADERS += $$PWD/assembler/*.h -SOURCES += $$PWD/assembler/ARMAssembler.cpp -SOURCES += $$PWD/assembler/ARMv7Assembler.cpp -SOURCES += $$PWD/assembler/MacroAssemblerARM.cpp -SOURCES += $$PWD/assembler/MacroAssemblerSH4.cpp -SOURCES += $$PWD/assembler/LinkBuffer.cpp - -SOURCES += $$PWD/wtf/PrintStream.cpp -HEADERS += $$PWD/wtf/PrintStream.h - -SOURCES += $$PWD/wtf/FilePrintStream.cpp -HEADERS += $$PWD/wtf/FilePrintStream.h - -HEADERS += $$PWD/wtf/RawPointer.h - -win32: SOURCES += $$PWD/wtf/OSAllocatorWin.cpp -else: SOURCES += $$PWD/wtf/OSAllocatorPosix.cpp -HEADERS += $$PWD/wtf/OSAllocator.h - -SOURCES += $$PWD/wtf/PageAllocationAligned.cpp -HEADERS += $$PWD/wtf/PageAllocationAligned.h -HEADERS += $$PWD/wtf/PageAllocation.h - -SOURCES += $$PWD/wtf/PageBlock.cpp -HEADERS += $$PWD/wtf/PageBlock.h - -HEADERS += $$PWD/wtf/PageReservation.h - -SOURCES += $$PWD/stubs/WTFStubs.cpp -HEADERS += $$PWD/stubs/WTFStubs.h - -DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" - -DEFINES += ENABLE_LLINT=0 -DEFINES += ENABLE_DFG_JIT=0 -DEFINES += ENABLE_JIT=1 -DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 -DEFINES += ENABLE_ASSEMBLER=1 - -DEFINES += BUILDING_QT__ - -INCLUDEPATH += $$PWD/jit -INCLUDEPATH += $$PWD/assembler -INCLUDEPATH += $$PWD/runtime -INCLUDEPATH += $$PWD/wtf -INCLUDEPATH += $$PWD/stubs -INCLUDEPATH += $$PWD/stubs/wtf -INCLUDEPATH += $$PWD - -DEFINES += WTF_USE_UDIS86=1 -INCLUDEPATH += $$PWD/disassembler -INCLUDEPATH += $$PWD/disassembler/udis86 -INCLUDEPATH += $$_OUT_PWD -SOURCES += $$PWD/disassembler/Disassembler.cpp -SOURCES += $$PWD/disassembler/UDis86Disassembler.cpp -SOURCES += $$PWD/disassembler/udis86/udis86.c -SOURCES += $$PWD/disassembler/udis86/udis86_decode.c -SOURCES += $$PWD/disassembler/udis86/udis86_input.c -SOURCES += $$PWD/disassembler/udis86/udis86_itab_holder.c -SOURCES += $$PWD/disassembler/udis86/udis86_syn-att.c -SOURCES += $$PWD/disassembler/udis86/udis86_syn.c -SOURCES += $$PWD/disassembler/udis86/udis86_syn-intel.c - -DEFINES += ENABLE_YARR_JIT=0 -SOURCES += \ - $$PWD/yarr/YarrCanonicalizeUCS2.cpp \ - $$PWD/yarr/YarrInterpreter.cpp \ - $$PWD/yarr/YarrPattern.cpp \ - $$PWD/yarr/YarrSyntaxChecker.cpp - -HEADERS += $$PWD/yarr/*.h - -retgen.output = RegExpJitTables.h -retgen.script = $$PWD/create_regex_tables -retgen.input = retgen.script -retgen.CONFIG += no_link -retgen.commands = python $$retgen.script > ${QMAKE_FILE_OUT} -QMAKE_EXTRA_COMPILERS += retgen - -ITAB = $$PWD/disassembler/udis86/optable.xml -udis86.output = udis86_itab.h -udis86.input = ITAB -udis86.CONFIG += no_link -udis86.commands = python $$PWD/disassembler/udis86/itab.py ${QMAKE_FILE_IN} -QMAKE_EXTRA_COMPILERS += udis86 - -udis86_tab_cfile.target = $$OUT_PWD/udis86_itab.c -udis86_tab_cfile.depends = udis86_itab.h -QMAKE_EXTRA_TARGETS += udis86_tab_cfile - -# Taken from WebKit/Tools/qmake/mkspecs/features/unix/default_post.prf -linux-g++* { - greaterThan(QT_GCC_MAJOR_VERSION, 3):greaterThan(QT_GCC_MINOR_VERSION, 5) { - !contains(QMAKE_CXXFLAGS, -std=(c|gnu)\\+\\+(0x|11)) { - # We need to deactivate those warnings because some names conflicts with upcoming c++0x types (e.g.nullptr). - QMAKE_CXXFLAGS_WARN_ON += -Wno-c++0x-compat - QMAKE_CXXFLAGS += -Wno-c++0x-compat - } - } -} - -# Don't warn about OVERRIDE and FINAL, since they are feature-checked anyways -*clang:!contains(QMAKE_CXXFLAGS, -std=c++11) { - QMAKE_CXXFLAGS += -Wno-c++11-extensions - QMAKE_OBJECTIVE_CFLAGS += -Wno-c++11-extensions -} diff --git a/3rdparty/masm/runtime/MatchResult.h b/3rdparty/masm/runtime/MatchResult.h deleted file mode 100644 index d87c8516b0..0000000000 --- a/3rdparty/masm/runtime/MatchResult.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MatchResult_h -#define MatchResult_h - -typedef uint64_t EncodedMatchResult; - -struct MatchResult { - ALWAYS_INLINE MatchResult(size_t start, size_t end) - : start(start) - , end(end) - { - } - - explicit ALWAYS_INLINE MatchResult(EncodedMatchResult encoded) - { - union u { - uint64_t encoded; - struct s { - size_t start; - size_t end; - } split; - } value; - value.encoded = encoded; - start = value.split.start; - end = value.split.end; - } - - ALWAYS_INLINE static MatchResult failed() - { - return MatchResult(WTF::notFound, 0); - } - - ALWAYS_INLINE operator bool() - { - return start != WTF::notFound; - } - - ALWAYS_INLINE bool empty() - { - return start == end; - } - - size_t start; - size_t end; -}; - -#endif diff --git a/3rdparty/masm/stubs/ExecutableAllocator.h b/3rdparty/masm/stubs/ExecutableAllocator.h deleted file mode 100644 index 7b3004aa90..0000000000 --- a/3rdparty/masm/stubs/ExecutableAllocator.h +++ /dev/null @@ -1,105 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef MASM_EXECUTABLEALLOCATOR_H -#define MASM_EXECUTABLEALLOCATOR_H - -#include -#include - -#include -#include - -namespace JSC { - -class JSGlobalData; - -struct ExecutableMemoryHandle : public RefCounted { - ExecutableMemoryHandle(int size) - : m_size(size) - { - static size_t pageSize = sysconf(_SC_PAGESIZE); - m_size = (m_size + pageSize - 1) & ~(pageSize - 1); -#if OS(DARWIN) -# define MAP_ANONYMOUS MAP_ANON -#endif - m_data = mmap(0, m_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - } - ~ExecutableMemoryHandle() - { - munmap(m_data, m_size); - } - - inline void shrink(size_t) { - // ### TODO. - } - - inline bool isManaged() const { return true; } - - void* start() { return m_data; } - int sizeInBytes() { return m_size; } - - void* m_data; - int m_size; -}; - -struct ExecutableAllocator { - PassRefPtr allocate(JSGlobalData&, int size, void*, int) - { - return adoptRef(new ExecutableMemoryHandle(size)); - } - - static void makeWritable(void*, int) - { - } - - static void makeExecutable(void* addr, int size) - { - static size_t pageSize = sysconf(_SC_PAGESIZE); - size_t iaddr = reinterpret_cast(addr); - size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); - int mode = PROT_READ | PROT_WRITE | PROT_EXEC; - mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode); - } -}; - -} - -#endif // MASM_EXECUTABLEALLOCATOR_H diff --git a/3rdparty/masm/stubs/JSGlobalData.h b/3rdparty/masm/stubs/JSGlobalData.h deleted file mode 100644 index 112bedddd7..0000000000 --- a/3rdparty/masm/stubs/JSGlobalData.h +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef MASM_JSGLOBALDATA_H -#define MASM_JSGLOBALDATA_H - -#include "ExecutableAllocator.h" -#include "WeakRandom.h" - -namespace JSC { - -class JSGlobalData { -public: - ExecutableAllocator executableAllocator; -}; - -} - -#endif // MASM_JSGLOBALDATA_H diff --git a/3rdparty/masm/stubs/LLIntData.h b/3rdparty/masm/stubs/LLIntData.h deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/3rdparty/masm/stubs/Options.h b/3rdparty/masm/stubs/Options.h deleted file mode 100644 index b95e4354e2..0000000000 --- a/3rdparty/masm/stubs/Options.h +++ /dev/null @@ -1,53 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef OPTIONS_H -#define OPTIONS_H - -namespace JSC { - -struct Options { - static bool showDisassembly() { return true; } - static bool showDFGDisassembly() { return true; } -}; - -} - -#endif // MASM_STUBS/OPTIONS_H diff --git a/3rdparty/masm/stubs/WTFStubs.cpp b/3rdparty/masm/stubs/WTFStubs.cpp deleted file mode 100644 index 530804fe3e..0000000000 --- a/3rdparty/masm/stubs/WTFStubs.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include -#include -#include -#include -#include -#include - -namespace WTF { - -void* fastMalloc(size_t size) -{ - return malloc(size); -} - -void* fastRealloc(void* ptr, size_t size) -{ - return realloc(ptr, size); -} - -void fastFree(void* ptr) -{ - free(ptr); -} - -uint32_t cryptographicallyRandomNumber() -{ - return 0; -} - -static FilePrintStream* s_dataFile; - -void setDataFile(FILE* f) -{ - delete s_dataFile; - s_dataFile = new FilePrintStream(f, FilePrintStream::Borrow); -} - -FilePrintStream& dataFile() -{ - if (!s_dataFile) - s_dataFile = new FilePrintStream(stderr, FilePrintStream::Borrow); - return *s_dataFile; -} - -void dataLogFV(const char* format, va_list args) -{ - char buffer[1024]; - vsnprintf(buffer, sizeof(buffer), format, args); - qDebug("%s", buffer); -} - -void dataLogF(const char* format, ...) -{ - char buffer[1024]; - va_list args; - va_start(args, format); - vsnprintf(buffer, sizeof(buffer), format, args); - va_end(args); - qDebug("%s", buffer); -} - -void dataLogFString(const char* str) -{ - qDebug("%s", str); -} - -} - -extern "C" { - -void WTFReportAssertionFailure(const char* /*file*/, int /*line*/, const char* /*function*/, const char* /*assertion*/) -{ -} - -void WTFReportBacktrace() -{ -} - -void WTFInvokeCrashHook() -{ -} - -} - - -#if ENABLE(ASSEMBLER) && CPU(X86) && !OS(MAC_OS_X) -#include - -JSC::MacroAssemblerX86Common::SSE2CheckState JSC::MacroAssemblerX86Common::s_sse2CheckState = JSC::MacroAssemblerX86Common::NotCheckedSSE2; -#endif - diff --git a/3rdparty/masm/stubs/WTFStubs.h b/3rdparty/masm/stubs/WTFStubs.h deleted file mode 100644 index ec77d25da7..0000000000 --- a/3rdparty/masm/stubs/WTFStubs.h +++ /dev/null @@ -1,50 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef WTFSTUBS_H -#define WTFSTUBS_H - -namespace WTF { - -void setDataFile(FILE* f); - -} - -#endif // WTFSTUBS_H diff --git a/3rdparty/masm/stubs/wtf/FastAllocBase.h b/3rdparty/masm/stubs/wtf/FastAllocBase.h deleted file mode 100644 index a062a885af..0000000000 --- a/3rdparty/masm/stubs/wtf/FastAllocBase.h +++ /dev/null @@ -1,48 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef FASTALLOCBASE_H -#define FASTALLOCBASE_H - -/* Dummy empty header file, only needed for #include source compatibility */ - -#define WTF_MAKE_FAST_ALLOCATED - -#endif // FASTALLOCBASE_H diff --git a/3rdparty/masm/stubs/wtf/FastMalloc.h b/3rdparty/masm/stubs/wtf/FastMalloc.h deleted file mode 100644 index 1248c79dec..0000000000 --- a/3rdparty/masm/stubs/wtf/FastMalloc.h +++ /dev/null @@ -1,46 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef FASTMALLOC_H -#define FASTMALLOC_H - -/* Dummy empty header file, only needed for #include source compatibility */ - -#endif // FASTMALLOC_H diff --git a/3rdparty/masm/stubs/wtf/Noncopyable.h b/3rdparty/masm/stubs/wtf/Noncopyable.h deleted file mode 100644 index d3d1eed6d1..0000000000 --- a/3rdparty/masm/stubs/wtf/Noncopyable.h +++ /dev/null @@ -1,48 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef NONCOPYABLE_H -#define NONCOPYABLE_H - -#include - -#define WTF_MAKE_NONCOPYABLE(x) Q_DISABLE_COPY(x) - -#endif // NONCOPYABLE_H diff --git a/3rdparty/masm/stubs/wtf/OwnPtr.h b/3rdparty/masm/stubs/wtf/OwnPtr.h deleted file mode 100644 index 31d2f1efa3..0000000000 --- a/3rdparty/masm/stubs/wtf/OwnPtr.h +++ /dev/null @@ -1,46 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef OWNPTR_H -#define OWNPTR_H - -#include "PassOwnPtr.h" - -#endif // OWNPTR_H diff --git a/3rdparty/masm/stubs/wtf/PassOwnPtr.h b/3rdparty/masm/stubs/wtf/PassOwnPtr.h deleted file mode 100644 index f9b84e7b57..0000000000 --- a/3rdparty/masm/stubs/wtf/PassOwnPtr.h +++ /dev/null @@ -1,116 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef PASSOWNPTR_H -#define PASSOWNPTR_H - -#include - -template class PassOwnPtr; -template PassOwnPtr adoptPtr(PtrType*); - -template -struct OwnPtr : public QScopedPointer -{ - OwnPtr() {} - OwnPtr(const PassOwnPtr &ptr) - : QScopedPointer(ptr.leakRef()) - {} - - OwnPtr& operator=(const OwnPtr& other) - { - this->reset(const_cast &>(other).take()); - return *this; - } - - T* get() const { return this->data(); } - - PassOwnPtr release() - { - return adoptPtr(this->take()); - } -}; - -template -class PassOwnPtr { -public: - PassOwnPtr() {} - - PassOwnPtr(T* ptr) - : m_ptr(ptr) - { - } - - PassOwnPtr(const PassOwnPtr& other) - : m_ptr(other.leakRef()) - { - } - - PassOwnPtr(const OwnPtr& other) - : m_ptr(other.take()) - { - } - - ~PassOwnPtr() - { - } - - T* operator->() const { return m_ptr.data(); } - - T* leakRef() const { return m_ptr.take(); } - -private: - template friend PassOwnPtr adoptPtr(PtrType*); - - PassOwnPtr& operator=(const PassOwnPtr&) - {} - mutable QScopedPointer m_ptr; -}; - -template -PassOwnPtr adoptPtr(T* ptr) -{ - PassOwnPtr result; - result.m_ptr.reset(ptr); - return result; -} - - -#endif // PASSOWNPTR_H diff --git a/3rdparty/masm/stubs/wtf/PassRefPtr.h b/3rdparty/masm/stubs/wtf/PassRefPtr.h deleted file mode 100644 index d97be1c330..0000000000 --- a/3rdparty/masm/stubs/wtf/PassRefPtr.h +++ /dev/null @@ -1,101 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef PASSREFPTR_H -#define PASSREFPTR_H - -template class RefPtr; - -template -class PassRefPtr { -public: - PassRefPtr() : m_ptr(0) {} - - PassRefPtr(T* ptr) - : m_ptr(ptr) - { - if (m_ptr) - m_ptr->ref(); - } - - PassRefPtr(const PassRefPtr& other) - : m_ptr(other.leakRef()) - { - } - - PassRefPtr(const RefPtr& other) - : m_ptr(other.get()) - { - if (m_ptr) - m_ptr->ref(); - } - - ~PassRefPtr() - { - if (m_ptr) - m_ptr->deref(); - } - - T* operator->() const { return m_ptr; } - - T* leakRef() const - { - T* result = m_ptr; - m_ptr = 0; - return result; - } - -private: - PassRefPtr& operator=(const PassRefPtr&) - {} - - template friend PassRefPtr adoptRef(PtrType*); - mutable T* m_ptr; -}; - -template -PassRefPtr adoptRef(T* ptr) -{ - PassRefPtr result; - result.m_ptr = ptr; - return result; -} - -#endif // PASSREFPTR_H diff --git a/3rdparty/masm/stubs/wtf/RefCounted.h b/3rdparty/masm/stubs/wtf/RefCounted.h deleted file mode 100644 index 4fc9ad9074..0000000000 --- a/3rdparty/masm/stubs/wtf/RefCounted.h +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef REFCOUNTED_H -#define REFCOUNTED_H - -#include "PassRefPtr.h" - -template -class RefCounted { -public: - RefCounted() : m_refCount(1) {} - ~RefCounted() - { - deref(); - } - - void ref() - { - ++m_refCount; - } - - void deref() - { - if (!--m_refCount) - delete static_cast(this); - } - -protected: - int m_refCount; -}; - -#endif // REFCOUNTED_H diff --git a/3rdparty/masm/stubs/wtf/RefPtr.h b/3rdparty/masm/stubs/wtf/RefPtr.h deleted file mode 100644 index 929b493b4b..0000000000 --- a/3rdparty/masm/stubs/wtf/RefPtr.h +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef REFPTR_H -#define REFPTR_H - -#include "PassRefPtr.h" - -template -class RefPtr { -public: - RefPtr() : m_ptr(0) {} - RefPtr(const RefPtr &other) - : m_ptr(other.m_ptr) - { - if (m_ptr) - m_ptr->ref(); - } - - RefPtr& operator=(const RefPtr& other) - { - if (other.m_ptr) - other.m_ptr->ref(); - if (m_ptr) - m_ptr->deref(); - m_ptr = other.m_ptr; - return *this; - } - - RefPtr(const PassRefPtr& other) - : m_ptr(other.leakRef()) - { - } - - ~RefPtr() - { - if (m_ptr) - m_ptr->deref(); - } - - T* operator->() const { return m_ptr; } - T* get() const { return m_ptr; } - bool operator!() const { return !m_ptr; } - - PassRefPtr release() - { - T* ptr = m_ptr; - m_ptr = 0; - return adoptRef(ptr); - } - -private: - T* m_ptr; -}; - -#endif // REFPTR_H diff --git a/3rdparty/masm/stubs/wtf/TypeTraits.h b/3rdparty/masm/stubs/wtf/TypeTraits.h deleted file mode 100644 index 9b626a7a53..0000000000 --- a/3rdparty/masm/stubs/wtf/TypeTraits.h +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef TYPETRAITS_H -#define TYPETRAITS_H - -namespace WTF { - -template -struct IsSameType { - static const bool value = false; -}; - -template -struct IsSameType { - static const bool value = true; -}; - -} - -#endif // TYPETRAITS_H diff --git a/3rdparty/masm/stubs/wtf/UnusedParam.h b/3rdparty/masm/stubs/wtf/UnusedParam.h deleted file mode 100644 index a676bdf303..0000000000 --- a/3rdparty/masm/stubs/wtf/UnusedParam.h +++ /dev/null @@ -1,48 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef UNUSEDPARAM_H -#define UNUSEDPARAM_H - -#include - -#define UNUSED_PARAM(x) Q_UNUSED(x) - -#endif // UNUSEDPARAM_H diff --git a/3rdparty/masm/stubs/wtf/Vector.h b/3rdparty/masm/stubs/wtf/Vector.h deleted file mode 100644 index 1feea851e1..0000000000 --- a/3rdparty/masm/stubs/wtf/Vector.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef VECTOR_H -#define VECTOR_H - -#include -#include -#include -#include - -namespace WTF { - -template -class Vector : public std::vector { -public: - Vector() {} - Vector(int initialSize) : std::vector(initialSize) {} - - inline void append(const T& value) - { this->push_back(value); } - - inline void append(const Vector& vector) - { - this->insert(this->end(), vector.begin(), vector.end()); - } - - using std::vector::insert; - - inline void insert(size_t position, T value) - { this->insert(this->begin() + position, value); } - - inline void grow(size_t size) - { this->resize(size); } - - inline void shrink(size_t size) - { this->erase(this->begin() + size, this->end()); } - - inline void remove(size_t position) - { this->erase(this->begin() + position); } - - inline bool isEmpty() const { return this->empty(); } - - inline T &last() { return *(this->begin() + this->size() - 1); } -}; - -template -void deleteAllValues(const Vector &vector) -{ - qDeleteAll(vector); -} - -} - -using WTF::Vector; -using WTF::deleteAllValues; - -#endif // VECTOR_H diff --git a/3rdparty/masm/stubs/wtf/text/CString.h b/3rdparty/masm/stubs/wtf/text/CString.h deleted file mode 100644 index c9a65e5c0b..0000000000 --- a/3rdparty/masm/stubs/wtf/text/CString.h +++ /dev/null @@ -1,44 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef CSTRING_H -#define CSTRING_H - -#endif // CSTRING_H diff --git a/3rdparty/masm/stubs/wtf/text/WTFString.h b/3rdparty/masm/stubs/wtf/text/WTFString.h deleted file mode 100644 index d157dc7adc..0000000000 --- a/3rdparty/masm/stubs/wtf/text/WTFString.h +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef WTFSTRING_H -#define WTFSTRING_H - -#include -#include -#include - -namespace WTF { - -class String : public QString -{ -public: - String(const QString& s) : QString(s) {} - bool is8Bit() const { return false; } - const unsigned char *characters8() const { return 0; } - const UChar *characters16() const { return reinterpret_cast(constData()); } - - template - const T* getCharacters() const; - -}; - -template <> -inline const unsigned char* String::getCharacters() const { return characters8(); } -template <> -inline const UChar* String::getCharacters() const { return characters16(); } - -} - -// Don't import WTF::String into the global namespace to avoid conflicts with QQmlJS::VM::String -namespace JSC { - using WTF::String; -} - -#endif // WTFSTRING_H diff --git a/3rdparty/masm/stubs/wtf/unicode/Unicode.h b/3rdparty/masm/stubs/wtf/unicode/Unicode.h deleted file mode 100644 index d61bc64c5a..0000000000 --- a/3rdparty/masm/stubs/wtf/unicode/Unicode.h +++ /dev/null @@ -1,59 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef UNICODE_H -#define UNICODE_H - -#include - -typedef unsigned char LChar; -typedef uint16_t UChar; - -namespace Unicode { - inline UChar toLower(UChar ch) { - return QChar::toLower(ch); - } - - inline UChar toUpper(UChar ch) { - return QChar::toUpper(ch); - } -} - -#endif // UNICODE_H diff --git a/3rdparty/masm/wtf/ASCIICType.h b/3rdparty/masm/wtf/ASCIICType.h deleted file mode 100644 index 18e108e1bf..0000000000 --- a/3rdparty/masm/wtf/ASCIICType.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_ASCIICType_h -#define WTF_ASCIICType_h - -#include - -// The behavior of many of the functions in the header is dependent -// on the current locale. But in the WebKit project, all uses of those functions -// are in code processing something that's not locale-specific. These equivalents -// for some of the functions are named more explicitly, not dependent -// on the C library locale, and we should also optimize them as needed. - -// All functions return false or leave the character unchanged if passed a character -// that is outside the range 0-7F. So they can be used on Unicode strings or -// characters if the intent is to do processing only if the character is ASCII. - -namespace WTF { - -template inline bool isASCII(CharType c) -{ - return !(c & ~0x7F); -} - -template inline bool isASCIIAlpha(CharType c) -{ - return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; -} - -template inline bool isASCIIDigit(CharType c) -{ - return c >= '0' && c <= '9'; -} - -template inline bool isASCIIAlphanumeric(CharType c) -{ - return isASCIIDigit(c) || isASCIIAlpha(c); -} - -template inline bool isASCIIHexDigit(CharType c) -{ - return isASCIIDigit(c) || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); -} - -template inline bool isASCIILower(CharType c) -{ - return c >= 'a' && c <= 'z'; -} - -template inline bool isASCIIOctalDigit(CharType c) -{ - return (c >= '0') & (c <= '7'); -} - -template inline bool isASCIIPrintable(CharType c) -{ - return c >= ' ' && c <= '~'; -} - -/* - Statistics from a run of Apple's page load test for callers of isASCIISpace: - - character count - --------- ----- - non-spaces 689383 - 20 space 294720 - 0A \n 89059 - 09 \t 28320 - 0D \r 0 - 0C \f 0 - 0B \v 0 - */ -template inline bool isASCIISpace(CharType c) -{ - return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); -} - -template inline bool isASCIIUpper(CharType c) -{ - return c >= 'A' && c <= 'Z'; -} - -template inline CharType toASCIILower(CharType c) -{ - return c | ((c >= 'A' && c <= 'Z') << 5); -} - -template inline CharType toASCIILowerUnchecked(CharType character) -{ - // This function can be used for comparing any input character - // to a lowercase English character. The isASCIIAlphaCaselessEqual - // below should be used for regular comparison of ASCII alpha - // characters, but switch statements in CSS tokenizer require - // direct use of this function. - return character | 0x20; -} - -template inline CharType toASCIIUpper(CharType c) -{ - return c & ~((c >= 'a' && c <= 'z') << 5); -} - -template inline int toASCIIHexValue(CharType c) -{ - ASSERT(isASCIIHexDigit(c)); - return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; -} - -template inline int toASCIIHexValue(CharType upperValue, CharType lowerValue) -{ - ASSERT(isASCIIHexDigit(upperValue) && isASCIIHexDigit(lowerValue)); - return ((toASCIIHexValue(upperValue) << 4) & 0xF0) | toASCIIHexValue(lowerValue); -} - -inline char lowerNibbleToASCIIHexDigit(char c) -{ - char nibble = c & 0xF; - return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; -} - -inline char upperNibbleToASCIIHexDigit(char c) -{ - char nibble = (c >> 4) & 0xF; - return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; -} - -template inline bool isASCIIAlphaCaselessEqual(CharType cssCharacter, char character) -{ - // This function compares a (preferrably) constant ASCII - // lowercase letter to any input character. - ASSERT(character >= 'a' && character <= 'z'); - return LIKELY(toASCIILowerUnchecked(cssCharacter) == character); -} - -} - -using WTF::isASCII; -using WTF::isASCIIAlpha; -using WTF::isASCIIAlphanumeric; -using WTF::isASCIIDigit; -using WTF::isASCIIHexDigit; -using WTF::isASCIILower; -using WTF::isASCIIOctalDigit; -using WTF::isASCIIPrintable; -using WTF::isASCIISpace; -using WTF::isASCIIUpper; -using WTF::toASCIIHexValue; -using WTF::toASCIILower; -using WTF::toASCIILowerUnchecked; -using WTF::toASCIIUpper; -using WTF::lowerNibbleToASCIIHexDigit; -using WTF::upperNibbleToASCIIHexDigit; -using WTF::isASCIIAlphaCaselessEqual; - -#endif diff --git a/3rdparty/masm/wtf/Assertions.h b/3rdparty/masm/wtf/Assertions.h deleted file mode 100644 index 7e079ab187..0000000000 --- a/3rdparty/masm/wtf/Assertions.h +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_Assertions_h -#define WTF_Assertions_h - -/* - no namespaces because this file has to be includable from C and Objective-C - - Note, this file uses many GCC extensions, but it should be compatible with - C, Objective C, C++, and Objective C++. - - For non-debug builds, everything is disabled by default. - Defining any of the symbols explicitly prevents this from having any effect. - - MSVC7 note: variadic macro support was added in MSVC8, so for now we disable - those macros in MSVC7. For more info, see the MSDN document on variadic - macros here: - - http://msdn2.microsoft.com/en-us/library/ms177415(VS.80).aspx -*/ - -#include - -#include - -#if !COMPILER(MSVC) -#include -#endif - -#ifdef NDEBUG -/* Disable ASSERT* macros in release mode. */ -#define ASSERTIONS_DISABLED_DEFAULT 1 -#else -#define ASSERTIONS_DISABLED_DEFAULT 0 -#endif - -#if COMPILER(MSVC7_OR_LOWER) -#define HAVE_VARIADIC_MACRO 0 -#else -#define HAVE_VARIADIC_MACRO 1 -#endif - -#ifndef BACKTRACE_DISABLED -#define BACKTRACE_DISABLED ASSERTIONS_DISABLED_DEFAULT -#endif - -#ifndef ASSERT_DISABLED -#define ASSERT_DISABLED ASSERTIONS_DISABLED_DEFAULT -#endif - -#ifndef ASSERT_MSG_DISABLED -#if HAVE(VARIADIC_MACRO) -#define ASSERT_MSG_DISABLED ASSERTIONS_DISABLED_DEFAULT -#else -#define ASSERT_MSG_DISABLED 1 -#endif -#endif - -#ifndef ASSERT_ARG_DISABLED -#define ASSERT_ARG_DISABLED ASSERTIONS_DISABLED_DEFAULT -#endif - -#ifndef FATAL_DISABLED -#if HAVE(VARIADIC_MACRO) -#define FATAL_DISABLED ASSERTIONS_DISABLED_DEFAULT -#else -#define FATAL_DISABLED 1 -#endif -#endif - -#ifndef ERROR_DISABLED -#if HAVE(VARIADIC_MACRO) -#define ERROR_DISABLED ASSERTIONS_DISABLED_DEFAULT -#else -#define ERROR_DISABLED 1 -#endif -#endif - -#ifndef LOG_DISABLED -#if HAVE(VARIADIC_MACRO) -#define LOG_DISABLED ASSERTIONS_DISABLED_DEFAULT -#else -#define LOG_DISABLED 1 -#endif -#endif - -#if COMPILER(GCC) -#define WTF_PRETTY_FUNCTION __PRETTY_FUNCTION__ -#else -#define WTF_PRETTY_FUNCTION __FUNCTION__ -#endif - -/* WTF logging functions can process %@ in the format string to log a NSObject* but the printf format attribute - emits a warning when %@ is used in the format string. Until is resolved we can't include - the attribute when being used from Objective-C code in case it decides to use %@. */ -#if COMPILER(GCC) && !defined(__OBJC__) -#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) __attribute__((__format__(printf, formatStringArgument, extraArguments))) -#else -#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) -#endif - -/* These helper functions are always declared, but not necessarily always defined if the corresponding function is disabled. */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { WTFLogChannelOff, WTFLogChannelOn } WTFLogChannelState; - -typedef struct { - unsigned mask; - const char *defaultName; - WTFLogChannelState state; -} WTFLogChannel; - -WTF_EXPORT_PRIVATE void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion); -WTF_EXPORT_PRIVATE void WTFReportAssertionFailureWithMessage(const char* file, int line, const char* function, const char* assertion, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6); -WTF_EXPORT_PRIVATE void WTFReportArgumentAssertionFailure(const char* file, int line, const char* function, const char* argName, const char* assertion); -WTF_EXPORT_PRIVATE void WTFReportFatalError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5); -WTF_EXPORT_PRIVATE void WTFReportError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5); -WTF_EXPORT_PRIVATE void WTFLog(WTFLogChannel*, const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); -WTF_EXPORT_PRIVATE void WTFLogVerbose(const char* file, int line, const char* function, WTFLogChannel*, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6); -WTF_EXPORT_PRIVATE void WTFLogAlways(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); - -WTF_EXPORT_PRIVATE void WTFGetBacktrace(void** stack, int* size); -WTF_EXPORT_PRIVATE void WTFReportBacktrace(); -WTF_EXPORT_PRIVATE void WTFPrintBacktrace(void** stack, int size); - -typedef void (*WTFCrashHookFunction)(); -WTF_EXPORT_PRIVATE void WTFSetCrashHook(WTFCrashHookFunction); -WTF_EXPORT_PRIVATE void WTFInvokeCrashHook(); -WTF_EXPORT_PRIVATE void WTFInstallReportBacktraceOnCrashHook(); - -#ifdef __cplusplus -} -#endif - -/* CRASH() - Raises a fatal error resulting in program termination and triggering either the debugger or the crash reporter. - - Use CRASH() in response to known, unrecoverable errors like out-of-memory. - Macro is enabled in both debug and release mode. - To test for unknown errors and verify assumptions, use ASSERT instead, to avoid impacting performance in release builds. - - Signals are ignored by the crash reporter on OS X so we must do better. -*/ -#ifndef CRASH -#if COMPILER(CLANG) -#define CRASH() \ - (WTFReportBacktrace(), \ - WTFInvokeCrashHook(), \ - (*(int *)(uintptr_t)0xbbadbeef = 0), \ - __builtin_trap()) -#else -#define CRASH() \ - (WTFReportBacktrace(), \ - WTFInvokeCrashHook(), \ - (*(int *)(uintptr_t)0xbbadbeef = 0), \ - ((void(*)())0)() /* More reliable, but doesn't say BBADBEEF */ \ - ) -#endif -#endif - -#if COMPILER(CLANG) -#define NO_RETURN_DUE_TO_CRASH NO_RETURN -#else -#define NO_RETURN_DUE_TO_CRASH -#endif - - -/* BACKTRACE - - Print a backtrace to the same location as ASSERT messages. -*/ - -#if BACKTRACE_DISABLED - -#define BACKTRACE() ((void)0) - -#else - -#define BACKTRACE() do { \ - WTFReportBacktrace(); \ -} while(false) - -#endif - -/* ASSERT, ASSERT_NOT_REACHED, ASSERT_UNUSED - - These macros are compiled out of release builds. - Expressions inside them are evaluated in debug builds only. -*/ - -#if OS(WINCE) && !PLATFORM(TORCHMOBILE) -/* FIXME: We include this here only to avoid a conflict with the ASSERT macro. */ -#include -#undef min -#undef max -#undef ERROR -#endif - -#if OS(WINDOWS) -/* FIXME: Change to use something other than ASSERT to avoid this conflict with the underlying platform */ -#undef ASSERT -#endif - -#if ASSERT_DISABLED - -#define ASSERT(assertion) ((void)0) -#define ASSERT_AT(assertion, file, line, function) ((void)0) -#define ASSERT_NOT_REACHED() ((void)0) -#define NO_RETURN_DUE_TO_ASSERT - -#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) -template -inline void assertUnused(T& x) { (void)x; } -#define ASSERT_UNUSED(variable, assertion) (assertUnused(variable)) -#else -#define ASSERT_UNUSED(variable, assertion) ((void)variable) -#endif - -#else - -#define ASSERT(assertion) \ - (!(assertion) ? \ - (WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion), \ - CRASH()) : \ - (void)0) - -#define ASSERT_AT(assertion, file, line, function) \ - (!(assertion) ? \ - (WTFReportAssertionFailure(file, line, function, #assertion), \ - CRASH()) : \ - (void)0) - -#define ASSERT_NOT_REACHED() do { \ - WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, 0); \ - CRASH(); \ -} while (0) - -#define ASSERT_UNUSED(variable, assertion) ASSERT(assertion) - -#define NO_RETURN_DUE_TO_ASSERT NO_RETURN_DUE_TO_CRASH - -#endif - -/* ASSERT_WITH_MESSAGE */ - -#if COMPILER(MSVC7_OR_LOWER) -#define ASSERT_WITH_MESSAGE(assertion) ((void)0) -#elif ASSERT_MSG_DISABLED -#define ASSERT_WITH_MESSAGE(assertion, ...) ((void)0) -#else -#define ASSERT_WITH_MESSAGE(assertion, ...) do \ - if (!(assertion)) { \ - WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ - CRASH(); \ - } \ -while (0) -#endif - -/* ASSERT_WITH_MESSAGE_UNUSED */ - -#if COMPILER(MSVC7_OR_LOWER) -#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion) ((void)0) -#elif ASSERT_MSG_DISABLED -#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) -template -inline void assertWithMessageUnused(T& x) { (void)x; } -#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) (assertWithMessageUnused(variable)) -#else -#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) ((void)variable) -#endif -#else -#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) do \ - if (!(assertion)) { \ - WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ - CRASH(); \ - } \ -while (0) -#endif - - -/* ASSERT_ARG */ - -#if ASSERT_ARG_DISABLED - -#define ASSERT_ARG(argName, assertion) ((void)0) - -#else - -#define ASSERT_ARG(argName, assertion) do \ - if (!(assertion)) { \ - WTFReportArgumentAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #argName, #assertion); \ - CRASH(); \ - } \ -while (0) - -#endif - -/* COMPILE_ASSERT */ -#ifndef COMPILE_ASSERT -#if COMPILER_SUPPORTS(C_STATIC_ASSERT) -#define COMPILE_ASSERT(exp, name) _Static_assert((exp), #name) -#else -#define COMPILE_ASSERT(exp, name) typedef int dummy##name [(exp) ? 1 : -1] -#endif -#endif - -/* FATAL */ - -#if COMPILER(MSVC7_OR_LOWER) -#define FATAL() ((void)0) -#elif FATAL_DISABLED -#define FATAL(...) ((void)0) -#else -#define FATAL(...) do { \ - WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__); \ - CRASH(); \ -} while (0) -#endif - -/* LOG_ERROR */ - -#if COMPILER(MSVC7_OR_LOWER) -#define LOG_ERROR() ((void)0) -#elif ERROR_DISABLED -#define LOG_ERROR(...) ((void)0) -#else -#define LOG_ERROR(...) WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__) -#endif - -/* LOG */ - -#if COMPILER(MSVC7_OR_LOWER) -#define LOG() ((void)0) -#elif LOG_DISABLED -#define LOG(channel, ...) ((void)0) -#else -#define LOG(channel, ...) WTFLog(&JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__) -#define JOIN_LOG_CHANNEL_WITH_PREFIX(prefix, channel) JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) -#define JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) prefix ## channel -#endif - -/* LOG_VERBOSE */ - -#if COMPILER(MSVC7_OR_LOWER) -#define LOG_VERBOSE(channel) ((void)0) -#elif LOG_DISABLED -#define LOG_VERBOSE(channel, ...) ((void)0) -#else -#define LOG_VERBOSE(channel, ...) WTFLogVerbose(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, &JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__) -#endif - -/* UNREACHABLE_FOR_PLATFORM */ - -#if COMPILER(CLANG) -// This would be a macro except that its use of #pragma works best around -// a function. Hence it uses macro naming convention. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmissing-noreturn" -static inline void UNREACHABLE_FOR_PLATFORM() -{ - ASSERT_NOT_REACHED(); -} -#pragma clang diagnostic pop -#else -#define UNREACHABLE_FOR_PLATFORM() ASSERT_NOT_REACHED() -#endif - -#endif /* WTF_Assertions_h */ diff --git a/3rdparty/masm/wtf/Atomics.h b/3rdparty/masm/wtf/Atomics.h deleted file mode 100644 index 750e1092ad..0000000000 --- a/3rdparty/masm/wtf/Atomics.h +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (C) 2007, 2008, 2010, 2012 Apple Inc. All rights reserved. - * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * Note: The implementations of InterlockedIncrement and InterlockedDecrement are based - * on atomic_increment and atomic_exchange_and_add from the Boost C++ Library. The license - * is virtually identical to the Apple license above but is included here for completeness. - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, - * must be included in all copies of the Software, in whole or in part, and - * all derivative works of the Software, unless such copies or derivative - * works are solely in the form of machine-executable object code generated by - * a source language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef Atomics_h -#define Atomics_h - -#include -#include -#include - -#if OS(WINDOWS) -#include -#elif OS(DARWIN) -#include -#elif OS(QNX) -#include -#elif OS(ANDROID) -#include -#elif COMPILER(GCC) -#if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2))) && !defined(__LSB_VERSION__) -#include -#else -#include -#endif -#endif - -namespace WTF { - -#if OS(WINDOWS) -#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 - -#if COMPILER(MINGW) || COMPILER(MSVC7_OR_LOWER) || OS(WINCE) -inline int atomicIncrement(int* addend) { return InterlockedIncrement(reinterpret_cast(addend)); } -inline int atomicDecrement(int* addend) { return InterlockedDecrement(reinterpret_cast(addend)); } -#else -inline int atomicIncrement(int volatile* addend) { return InterlockedIncrement(reinterpret_cast(addend)); } -inline int atomicDecrement(int volatile* addend) { return InterlockedDecrement(reinterpret_cast(addend)); } -#endif - -#elif OS(DARWIN) -#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 - -inline int atomicIncrement(int volatile* addend) { return OSAtomicIncrement32Barrier(const_cast(addend)); } -inline int atomicDecrement(int volatile* addend) { return OSAtomicDecrement32Barrier(const_cast(addend)); } - -inline int64_t atomicIncrement(int64_t volatile* addend) { return OSAtomicIncrement64Barrier(const_cast(addend)); } -inline int64_t atomicDecrement(int64_t volatile* addend) { return OSAtomicDecrement64Barrier(const_cast(addend)); } - -#elif OS(QNX) -#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 - -// Note, atomic_{add, sub}_value() return the previous value of addend's content. -inline int atomicIncrement(int volatile* addend) { return static_cast(atomic_add_value(reinterpret_cast(addend), 1)) + 1; } -inline int atomicDecrement(int volatile* addend) { return static_cast(atomic_sub_value(reinterpret_cast(addend), 1)) - 1; } - -#elif OS(ANDROID) -#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 - -// Note, __atomic_{inc, dec}() return the previous value of addend's content. -inline int atomicIncrement(int volatile* addend) { return __atomic_inc(addend) + 1; } -inline int atomicDecrement(int volatile* addend) { return __atomic_dec(addend) - 1; } - -#elif COMPILER(GCC) && !CPU(SPARC64) // sizeof(_Atomic_word) != sizeof(int) on sparc64 gcc -#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 - -inline int atomicIncrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, 1) + 1; } -inline int atomicDecrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, -1) - 1; } - -#endif - -#if OS(WINDOWS) -inline bool weakCompareAndSwap(volatile unsigned* location, unsigned expected, unsigned newValue) -{ -#if OS(WINCE) - return InterlockedCompareExchange(reinterpret_cast(const_cast(location)), static_cast(newValue), static_cast(expected)) == static_cast(expected); -#else - return InterlockedCompareExchange(reinterpret_cast(location), static_cast(newValue), static_cast(expected)) == static_cast(expected); -#endif -} - -inline bool weakCompareAndSwap(void*volatile* location, void* expected, void* newValue) -{ - return InterlockedCompareExchangePointer(location, newValue, expected) == expected; -} -#else // OS(WINDOWS) --> not windows -#if COMPILER(GCC) && !COMPILER(CLANG) // Work around a gcc bug -inline bool weakCompareAndSwap(volatile unsigned* location, unsigned expected, unsigned newValue) -#else -inline bool weakCompareAndSwap(unsigned* location, unsigned expected, unsigned newValue) -#endif -{ -#if ENABLE(COMPARE_AND_SWAP) -#if CPU(X86) || CPU(X86_64) - unsigned char result; - asm volatile( - "lock; cmpxchgl %3, %2\n\t" - "sete %1" - : "+a"(expected), "=q"(result), "+m"(*location) - : "r"(newValue) - : "memory" - ); -#elif CPU(ARM_THUMB2) - unsigned tmp; - unsigned result; - asm volatile( - "movw %1, #1\n\t" - "ldrex %2, %0\n\t" - "cmp %3, %2\n\t" - "bne.n 0f\n\t" - "strex %1, %4, %0\n\t" - "0:" - : "+Q"(*location), "=&r"(result), "=&r"(tmp) - : "r"(expected), "r"(newValue) - : "memory"); - result = !result; -#else -#error "Bad architecture for compare and swap." -#endif - return result; -#else - UNUSED_PARAM(location); - UNUSED_PARAM(expected); - UNUSED_PARAM(newValue); - CRASH(); - return false; -#endif -} - -inline bool weakCompareAndSwap(void*volatile* location, void* expected, void* newValue) -{ -#if ENABLE(COMPARE_AND_SWAP) -#if CPU(X86_64) - bool result; - asm volatile( - "lock; cmpxchgq %3, %2\n\t" - "sete %1" - : "+a"(expected), "=q"(result), "+m"(*location) - : "r"(newValue) - : "memory" - ); - return result; -#else - return weakCompareAndSwap(bitwise_cast(location), bitwise_cast(expected), bitwise_cast(newValue)); -#endif -#else // ENABLE(COMPARE_AND_SWAP) - UNUSED_PARAM(location); - UNUSED_PARAM(expected); - UNUSED_PARAM(newValue); - CRASH(); - return 0; -#endif // ENABLE(COMPARE_AND_SWAP) -} -#endif // OS(WINDOWS) (end of the not-windows case) - -inline bool weakCompareAndSwapUIntPtr(volatile uintptr_t* location, uintptr_t expected, uintptr_t newValue) -{ - return weakCompareAndSwap(reinterpret_cast(location), reinterpret_cast(expected), reinterpret_cast(newValue)); -} - -#if CPU(ARM_THUMB2) - -inline void memoryBarrierAfterLock() -{ - asm volatile("dmb" ::: "memory"); -} - -inline void memoryBarrierBeforeUnlock() -{ - asm volatile("dmb" ::: "memory"); -} - -#else - -inline void memoryBarrierAfterLock() { } -inline void memoryBarrierBeforeUnlock() { } - -#endif - -} // namespace WTF - -#if USE(LOCKFREE_THREADSAFEREFCOUNTED) -using WTF::atomicDecrement; -using WTF::atomicIncrement; -#endif - -#endif // Atomics_h diff --git a/3rdparty/masm/wtf/BumpPointerAllocator.h b/3rdparty/masm/wtf/BumpPointerAllocator.h deleted file mode 100644 index 3b2cfd974a..0000000000 --- a/3rdparty/masm/wtf/BumpPointerAllocator.h +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef BumpPointerAllocator_h -#define BumpPointerAllocator_h - -#include -#include -#include - -namespace WTF { - -#define MINIMUM_BUMP_POOL_SIZE 0x1000 - -class BumpPointerPool { -public: - // ensureCapacity will check whether the current pool has capacity to - // allocate 'size' bytes of memory If it does not, it will attempt to - // allocate a new pool (which will be added to this one in a chain). - // - // If allocation fails (out of memory) this method will return null. - // If the return value is non-null, then callers should update any - // references they have to this current (possibly full) BumpPointerPool - // to instead point to the newly returned BumpPointerPool. - BumpPointerPool* ensureCapacity(size_t size) - { - void* allocationEnd = static_cast(m_current) + size; - ASSERT(allocationEnd > m_current); // check for overflow - if (allocationEnd <= static_cast(this)) - return this; - return ensureCapacityCrossPool(this, size); - } - - // alloc should only be called after calling ensureCapacity; as such - // alloc will never fail. - void* alloc(size_t size) - { - void* current = m_current; - void* allocationEnd = static_cast(current) + size; - ASSERT(allocationEnd > current); // check for overflow - ASSERT(allocationEnd <= static_cast(this)); - m_current = allocationEnd; - return current; - } - - // The dealloc method releases memory allocated using alloc. Memory - // must be released in a LIFO fashion, e.g. if the client calls alloc - // four times, returning pointer A, B, C, D, then the only valid order - // in which these may be deallocaed is D, C, B, A. - // - // The client may optionally skip some deallocations. In the example - // above, it would be valid to only explicitly dealloc C, A (D being - // dealloced along with C, B along with A). - // - // If pointer was not allocated from this pool (or pools) then dealloc - // will CRASH(). Callers should update any references they have to - // this current BumpPointerPool to instead point to the returned - // BumpPointerPool. - BumpPointerPool* dealloc(void* position) - { - if ((position >= m_start) && (position <= static_cast(this))) { - ASSERT(position <= m_current); - m_current = position; - return this; - } - return deallocCrossPool(this, position); - } - -private: - // Placement operator new, returns the last 'size' bytes of allocation for use as this. - void* operator new(size_t size, const PageAllocation& allocation) - { - ASSERT(size < allocation.size()); - return reinterpret_cast(reinterpret_cast(allocation.base()) + allocation.size()) - size; - } - - BumpPointerPool(const PageAllocation& allocation) - : m_current(allocation.base()) - , m_start(allocation.base()) - , m_next(0) - , m_previous(0) - , m_allocation(allocation) - { - } - - static BumpPointerPool* create(size_t minimumCapacity = 0) - { - // Add size of BumpPointerPool object, check for overflow. - minimumCapacity += sizeof(BumpPointerPool); - if (minimumCapacity < sizeof(BumpPointerPool)) - return 0; - - size_t poolSize = std::max(static_cast(MINIMUM_BUMP_POOL_SIZE), WTF::pageSize()); - while (poolSize < minimumCapacity) { - poolSize <<= 1; - // The following if check relies on MINIMUM_BUMP_POOL_SIZE being a power of 2! - ASSERT(!(MINIMUM_BUMP_POOL_SIZE & (MINIMUM_BUMP_POOL_SIZE - 1))); - if (!poolSize) - return 0; - } - - PageAllocation allocation = PageAllocation::allocate(poolSize); - if (!!allocation) - return new (allocation) BumpPointerPool(allocation); - return 0; - } - - void shrink() - { - ASSERT(!m_previous); - m_current = m_start; - while (m_next) { - BumpPointerPool* nextNext = m_next->m_next; - m_next->destroy(); - m_next = nextNext; - } - } - - void destroy() - { - m_allocation.deallocate(); - } - - static BumpPointerPool* ensureCapacityCrossPool(BumpPointerPool* previousPool, size_t size) - { - // The pool passed should not have capacity, so we'll start with the next one. - ASSERT(previousPool); - ASSERT((static_cast(previousPool->m_current) + size) > previousPool->m_current); // check for overflow - ASSERT((static_cast(previousPool->m_current) + size) > static_cast(previousPool)); - BumpPointerPool* pool = previousPool->m_next; - - while (true) { - if (!pool) { - // We've run to the end; allocate a new pool. - pool = BumpPointerPool::create(size); - previousPool->m_next = pool; - pool->m_previous = previousPool; - return pool; - } - - // - void* current = pool->m_current; - void* allocationEnd = static_cast(current) + size; - ASSERT(allocationEnd > current); // check for overflow - if (allocationEnd <= static_cast(pool)) - return pool; - } - } - - static BumpPointerPool* deallocCrossPool(BumpPointerPool* pool, void* position) - { - // Should only be called if position is not in the current pool. - ASSERT((position < pool->m_start) || (position > static_cast(pool))); - - while (true) { - // Unwind the current pool to the start, move back in the chain to the previous pool. - pool->m_current = pool->m_start; - pool = pool->m_previous; - - // position was nowhere in the chain! - if (!pool) - CRASH(); - - if ((position >= pool->m_start) && (position <= static_cast(pool))) { - ASSERT(position <= pool->m_current); - pool->m_current = position; - return pool; - } - } - } - - void* m_current; - void* m_start; - BumpPointerPool* m_next; - BumpPointerPool* m_previous; - PageAllocation m_allocation; - - friend class BumpPointerAllocator; -}; - -// A BumpPointerAllocator manages a set of BumpPointerPool objects, which -// can be used for LIFO (stack like) allocation. -// -// To begin allocating using this class call startAllocator(). The result -// of this method will be null if the initial pool allocation fails, or a -// pointer to a BumpPointerPool object that can be used to perform -// allocations. Whilst running no memory will be released until -// stopAllocator() is called. At this point all allocations made through -// this allocator will be reaped, and underlying memory may be freed. -// -// (In practice we will still hold on to the initial pool to allow allocation -// to be quickly restared, but aditional pools will be freed). -// -// This allocator is non-renetrant, it is encumbant on the clients to ensure -// startAllocator() is not called again until stopAllocator() has been called. -class BumpPointerAllocator { -public: - BumpPointerAllocator() - : m_head(0) - { - } - - ~BumpPointerAllocator() - { - if (m_head) - m_head->destroy(); - } - - BumpPointerPool* startAllocator() - { - if (!m_head) - m_head = BumpPointerPool::create(); - return m_head; - } - - void stopAllocator() - { - if (m_head) - m_head->shrink(); - } - -private: - BumpPointerPool* m_head; -}; - -} - -using WTF::BumpPointerAllocator; - -#endif // BumpPointerAllocator_h diff --git a/3rdparty/masm/wtf/CheckedArithmetic.h b/3rdparty/masm/wtf/CheckedArithmetic.h deleted file mode 100644 index f5d3b75500..0000000000 --- a/3rdparty/masm/wtf/CheckedArithmetic.h +++ /dev/null @@ -1,708 +0,0 @@ -/* - * Copyright (C) 2011 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef CheckedArithmetic_h -#define CheckedArithmetic_h - -#include -#include - -#include -#include - -/* Checked - * - * This class provides a mechanism to perform overflow-safe integer arithmetic - * without having to manually ensure that you have all the required bounds checks - * directly in your code. - * - * There are two modes of operation: - * - The default is Checked, and crashes at the point - * and overflow has occurred. - * - The alternative is Checked, which uses an additional - * byte of storage to track whether an overflow has occurred, subsequent - * unchecked operations will crash if an overflow has occured - * - * It is possible to provide a custom overflow handler, in which case you need - * to support these functions: - * - void overflowed(); - * This function is called when an operation has produced an overflow. - * - bool hasOverflowed(); - * This function must return true if overflowed() has been called on an - * instance and false if it has not. - * - void clearOverflow(); - * Used to reset overflow tracking when a value is being overwritten with - * a new value. - * - * Checked works for all integer types, with the following caveats: - * - Mixing signedness of operands is only supported for types narrower than - * 64bits. - * - It does have a performance impact, so tight loops may want to be careful - * when using it. - * - */ - -namespace WTF { - -class CrashOnOverflow { -protected: - NO_RETURN_DUE_TO_CRASH void overflowed() - { - CRASH(); - } - - void clearOverflow() { } - -public: - bool hasOverflowed() const { return false; } -}; - -class RecordOverflow { -protected: - RecordOverflow() - : m_overflowed(false) - { - } - - void overflowed() - { - m_overflowed = true; - } - - void clearOverflow() - { - m_overflowed = false; - } - -public: - bool hasOverflowed() const { return m_overflowed; } - -private: - unsigned char m_overflowed; -}; - -template class Checked; -template struct RemoveChecked; -template struct RemoveChecked >; - -template ::is_signed, bool sourceSigned = std::numeric_limits::is_signed> struct BoundsChecker; -template struct BoundsChecker { - static bool inBounds(Source value) - { - // Same signedness so implicit type conversion will always increase precision - // to widest type - return value <= std::numeric_limits::max(); - } -}; - -template struct BoundsChecker { - static bool inBounds(Source value) - { - // Same signedness so implicit type conversion will always increase precision - // to widest type - return std::numeric_limits::min() <= value && value <= std::numeric_limits::max(); - } -}; - -template struct BoundsChecker { - static bool inBounds(Source value) - { - // Target is unsigned so any value less than zero is clearly unsafe - if (value < 0) - return false; - // If our (unsigned) Target is the same or greater width we can - // convert value to type Target without losing precision - if (sizeof(Target) >= sizeof(Source)) - return static_cast(value) <= std::numeric_limits::max(); - // The signed Source type has greater precision than the target so - // max(Target) -> Source will widen. - return value <= static_cast(std::numeric_limits::max()); - } -}; - -template struct BoundsChecker { - static bool inBounds(Source value) - { - // Signed target with an unsigned source - if (sizeof(Target) <= sizeof(Source)) - return value <= static_cast(std::numeric_limits::max()); - // Target is Wider than Source so we're guaranteed to fit any value in - // unsigned Source - return true; - } -}; - -template ::value || (sizeof(Target) > sizeof(Source)) > struct BoundsCheckElider; -template struct BoundsCheckElider { - static bool inBounds(Source) { return true; } -}; -template struct BoundsCheckElider : public BoundsChecker { -}; - -template static inline bool isInBounds(Source value) -{ - return BoundsCheckElider::inBounds(value); -} - -template struct RemoveChecked { - typedef T CleanType; - static const CleanType DefaultValue = 0; -}; - -template struct RemoveChecked > { - typedef typename RemoveChecked::CleanType CleanType; - static const CleanType DefaultValue = 0; -}; - -template struct RemoveChecked > { - typedef typename RemoveChecked::CleanType CleanType; - static const CleanType DefaultValue = 0; -}; - -// The ResultBase and SignednessSelector are used to workaround typeof not being -// available in MSVC -template sizeof(V)), bool sameSize = (sizeof(U) == sizeof(V))> struct ResultBase; -template struct ResultBase { - typedef U ResultType; -}; - -template struct ResultBase { - typedef V ResultType; -}; - -template struct ResultBase { - typedef U ResultType; -}; - -template ::is_signed, bool vIsSigned = std::numeric_limits::is_signed> struct SignednessSelector; -template struct SignednessSelector { - typedef U ResultType; -}; - -template struct SignednessSelector { - typedef U ResultType; -}; - -template struct SignednessSelector { - typedef V ResultType; -}; - -template struct SignednessSelector { - typedef U ResultType; -}; - -template struct ResultBase { - typedef typename SignednessSelector::ResultType ResultType; -}; - -template struct Result : ResultBase::CleanType, typename RemoveChecked::CleanType> { -}; - -template ::ResultType, - bool lhsSigned = std::numeric_limits::is_signed, bool rhsSigned = std::numeric_limits::is_signed> struct ArithmeticOperations; - -template struct ArithmeticOperations { - // LHS and RHS are signed types - - // Helper function - static inline bool signsMatch(LHS lhs, RHS rhs) - { - return (lhs ^ rhs) >= 0; - } - - static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - if (signsMatch(lhs, rhs)) { - if (lhs >= 0) { - if ((std::numeric_limits::max() - rhs) < lhs) - return false; - } else { - ResultType temp = lhs - std::numeric_limits::min(); - if (rhs < -temp) - return false; - } - } // if the signs do not match this operation can't overflow - result = lhs + rhs; - return true; - } - - static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - if (!signsMatch(lhs, rhs)) { - if (lhs >= 0) { - if (lhs > std::numeric_limits::max() + rhs) - return false; - } else { - if (rhs > std::numeric_limits::max() + lhs) - return false; - } - } // if the signs match this operation can't overflow - result = lhs - rhs; - return true; - } - - static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - if (signsMatch(lhs, rhs)) { - if (lhs >= 0) { - if (lhs && (std::numeric_limits::max() / lhs) < rhs) - return false; - } else { - if (static_cast(lhs) == std::numeric_limits::min() || static_cast(rhs) == std::numeric_limits::min()) - return false; - if ((std::numeric_limits::max() / -lhs) < -rhs) - return false; - } - } else { - if (lhs < 0) { - if (rhs && lhs < (std::numeric_limits::min() / rhs)) - return false; - } else { - if (lhs && rhs < (std::numeric_limits::min() / lhs)) - return false; - } - } - result = lhs * rhs; - return true; - } - - static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } - -}; - -template struct ArithmeticOperations { - // LHS and RHS are unsigned types so bounds checks are nice and easy - static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - ResultType temp = lhs + rhs; - if (temp < lhs) - return false; - result = temp; - return true; - } - - static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - ResultType temp = lhs - rhs; - if (temp > lhs) - return false; - result = temp; - return true; - } - - static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - ResultType temp = lhs * rhs; - if (temp < lhs) - return false; - result = temp; - return true; - } - - static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } - -}; - -template struct ArithmeticOperations { - static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) - { - int64_t temp = lhs + rhs; - if (temp < std::numeric_limits::min()) - return false; - if (temp > std::numeric_limits::max()) - return false; - result = static_cast(temp); - return true; - } - - static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) - { - int64_t temp = lhs - rhs; - if (temp < std::numeric_limits::min()) - return false; - if (temp > std::numeric_limits::max()) - return false; - result = static_cast(temp); - return true; - } - - static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) - { - int64_t temp = lhs * rhs; - if (temp < std::numeric_limits::min()) - return false; - if (temp > std::numeric_limits::max()) - return false; - result = static_cast(temp); - return true; - } - - static inline bool equals(int lhs, unsigned rhs) - { - return static_cast(lhs) == static_cast(rhs); - } -}; - -template struct ArithmeticOperations { - static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) - { - return ArithmeticOperations::add(rhs, lhs, result); - } - - static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) - { - return ArithmeticOperations::sub(lhs, rhs, result); - } - - static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) - { - return ArithmeticOperations::multiply(rhs, lhs, result); - } - - static inline bool equals(unsigned lhs, int rhs) - { - return ArithmeticOperations::equals(rhs, lhs); - } -}; - -template static inline bool safeAdd(U lhs, V rhs, R& result) -{ - return ArithmeticOperations::add(lhs, rhs, result); -} - -template static inline bool safeSub(U lhs, V rhs, R& result) -{ - return ArithmeticOperations::sub(lhs, rhs, result); -} - -template static inline bool safeMultiply(U lhs, V rhs, R& result) -{ - return ArithmeticOperations::multiply(lhs, rhs, result); -} - -template static inline bool safeEquals(U lhs, V rhs) -{ - return ArithmeticOperations::equals(lhs, rhs); -} - -enum ResultOverflowedTag { ResultOverflowed }; - -// FIXME: Needed to workaround http://llvm.org/bugs/show_bug.cgi?id=10801 -static inline bool workAroundClangBug() { return true; } - -template class Checked : public OverflowHandler { -public: - template friend class Checked; - Checked() - : m_value(0) - { - } - - Checked(ResultOverflowedTag) - : m_value(0) - { - // FIXME: Remove this when clang fixes http://llvm.org/bugs/show_bug.cgi?id=10801 - if (workAroundClangBug()) - this->overflowed(); - } - - template Checked(U value) - { - if (!isInBounds(value)) - this->overflowed(); - m_value = static_cast(value); - } - - template Checked(const Checked& rhs) - : m_value(rhs.m_value) - { - if (rhs.hasOverflowed()) - this->overflowed(); - } - - template Checked(const Checked& rhs) - : OverflowHandler(rhs) - { - if (!isInBounds(rhs.m_value)) - this->overflowed(); - m_value = static_cast(rhs.m_value); - } - - template Checked(const Checked& rhs) - { - if (rhs.hasOverflowed()) - this->overflowed(); - if (!isInBounds(rhs.m_value)) - this->overflowed(); - m_value = static_cast(rhs.m_value); - } - - const Checked& operator=(Checked rhs) - { - this->clearOverflow(); - if (rhs.hasOverflowed()) - this->overflowed(); - m_value = static_cast(rhs.m_value); - return *this; - } - - template const Checked& operator=(U value) - { - return *this = Checked(value); - } - - template const Checked& operator=(const Checked& rhs) - { - return *this = Checked(rhs); - } - - // prefix - const Checked& operator++() - { - if (m_value == std::numeric_limits::max()) - this->overflowed(); - m_value++; - return *this; - } - - const Checked& operator--() - { - if (m_value == std::numeric_limits::min()) - this->overflowed(); - m_value--; - return *this; - } - - // postfix operators - const Checked operator++(int) - { - if (m_value == std::numeric_limits::max()) - this->overflowed(); - return Checked(m_value++); - } - - const Checked operator--(int) - { - if (m_value == std::numeric_limits::min()) - this->overflowed(); - return Checked(m_value--); - } - - // Boolean operators - bool operator!() const - { - if (this->hasOverflowed()) - CRASH(); - return !m_value; - } - - typedef void* (Checked::*UnspecifiedBoolType); - operator UnspecifiedBoolType*() const - { - if (this->hasOverflowed()) - CRASH(); - return (m_value) ? reinterpret_cast(1) : 0; - } - - // Value accessors. unsafeGet() will crash if there's been an overflow. - T unsafeGet() const - { - if (this->hasOverflowed()) - CRASH(); - return m_value; - } - - bool safeGet(T& value) const WARN_UNUSED_RETURN - { - value = m_value; - return this->hasOverflowed(); - } - - // Mutating assignment - template const Checked operator+=(U rhs) - { - if (!safeAdd(m_value, rhs, m_value)) - this->overflowed(); - return *this; - } - - template const Checked operator-=(U rhs) - { - if (!safeSub(m_value, rhs, m_value)) - this->overflowed(); - return *this; - } - - template const Checked operator*=(U rhs) - { - if (!safeMultiply(m_value, rhs, m_value)) - this->overflowed(); - return *this; - } - - const Checked operator*=(double rhs) - { - double result = rhs * m_value; - // Handle +/- infinity and NaN - if (!(std::numeric_limits::min() <= result && std::numeric_limits::max() >= result)) - this->overflowed(); - m_value = (T)result; - return *this; - } - - const Checked operator*=(float rhs) - { - return *this *= (double)rhs; - } - - template const Checked operator+=(Checked rhs) - { - if (rhs.hasOverflowed()) - this->overflowed(); - return *this += rhs.m_value; - } - - template const Checked operator-=(Checked rhs) - { - if (rhs.hasOverflowed()) - this->overflowed(); - return *this -= rhs.m_value; - } - - template const Checked operator*=(Checked rhs) - { - if (rhs.hasOverflowed()) - this->overflowed(); - return *this *= rhs.m_value; - } - - // Equality comparisons - template bool operator==(Checked rhs) - { - return unsafeGet() == rhs.unsafeGet(); - } - - template bool operator==(U rhs) - { - if (this->hasOverflowed()) - this->overflowed(); - return safeEquals(m_value, rhs); - } - - template const Checked operator==(Checked rhs) - { - return unsafeGet() == Checked(rhs.unsafeGet()); - } - - template bool operator!=(U rhs) - { - return !(*this == rhs); - } - -private: - // Disallow implicit conversion of floating point to integer types - Checked(float); - Checked(double); - void operator=(float); - void operator=(double); - void operator+=(float); - void operator+=(double); - void operator-=(float); - void operator-=(double); - T m_value; -}; - -template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, Checked rhs) -{ - U x = 0; - V y = 0; - bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); - typename Result::ResultType result = 0; - overflowed |= !safeAdd(x, y, result); - if (overflowed) - return ResultOverflowed; - return result; -} - -template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, Checked rhs) -{ - U x = 0; - V y = 0; - bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); - typename Result::ResultType result = 0; - overflowed |= !safeSub(x, y, result); - if (overflowed) - return ResultOverflowed; - return result; -} - -template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, Checked rhs) -{ - U x = 0; - V y = 0; - bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); - typename Result::ResultType result = 0; - overflowed |= !safeMultiply(x, y, result); - if (overflowed) - return ResultOverflowed; - return result; -} - -template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, V rhs) -{ - return lhs + Checked(rhs); -} - -template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, V rhs) -{ - return lhs - Checked(rhs); -} - -template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, V rhs) -{ - return lhs * Checked(rhs); -} - -template static inline Checked::ResultType, OverflowHandler> operator+(U lhs, Checked rhs) -{ - return Checked(lhs) + rhs; -} - -template static inline Checked::ResultType, OverflowHandler> operator-(U lhs, Checked rhs) -{ - return Checked(lhs) - rhs; -} - -template static inline Checked::ResultType, OverflowHandler> operator*(U lhs, Checked rhs) -{ - return Checked(lhs) * rhs; -} - -} - -using WTF::Checked; -using WTF::RecordOverflow; - -#endif diff --git a/3rdparty/masm/wtf/Compiler.h b/3rdparty/masm/wtf/Compiler.h deleted file mode 100644 index a9ef419c18..0000000000 --- a/3rdparty/masm/wtf/Compiler.h +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_Compiler_h -#define WTF_Compiler_h - -/* COMPILER() - the compiler being used to build the project */ -#define COMPILER(WTF_FEATURE) (defined WTF_COMPILER_##WTF_FEATURE && WTF_COMPILER_##WTF_FEATURE) - -/* COMPILER_SUPPORTS() - whether the compiler being used to build the project supports the given feature. */ -#define COMPILER_SUPPORTS(WTF_COMPILER_FEATURE) (defined WTF_COMPILER_SUPPORTS_##WTF_COMPILER_FEATURE && WTF_COMPILER_SUPPORTS_##WTF_COMPILER_FEATURE) - -/* COMPILER_QUIRK() - whether the compiler being used to build the project requires a given quirk. */ -#define COMPILER_QUIRK(WTF_COMPILER_QUIRK) (defined WTF_COMPILER_QUIRK_##WTF_COMPILER_QUIRK && WTF_COMPILER_QUIRK_##WTF_COMPILER_QUIRK) - -/* ==== COMPILER() - the compiler being used to build the project ==== */ - -/* COMPILER(CLANG) - Clang */ -#if defined(__clang__) -#define WTF_COMPILER_CLANG 1 - -#ifndef __has_extension -#define __has_extension __has_feature /* Compatibility with older versions of clang */ -#endif - -#define CLANG_PRAGMA(PRAGMA) _Pragma(PRAGMA) - -/* Specific compiler features */ -#define WTF_COMPILER_SUPPORTS_CXX_VARIADIC_TEMPLATES __has_extension(cxx_variadic_templates) - -/* There is a bug in clang that comes with Xcode 4.2 where AtomicStrings can't be implicitly converted to Strings - in the presence of move constructors and/or move assignment operators. This bug has been fixed in Xcode 4.3 clang, so we - check for both cxx_rvalue_references as well as the unrelated cxx_nonstatic_member_init feature which we know was added in 4.3 */ -#define WTF_COMPILER_SUPPORTS_CXX_RVALUE_REFERENCES __has_extension(cxx_rvalue_references) && __has_extension(cxx_nonstatic_member_init) - -#define WTF_COMPILER_SUPPORTS_CXX_DELETED_FUNCTIONS __has_extension(cxx_deleted_functions) -#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR __has_feature(cxx_nullptr) -#define WTF_COMPILER_SUPPORTS_CXX_EXPLICIT_CONVERSIONS __has_feature(cxx_explicit_conversions) -#define WTF_COMPILER_SUPPORTS_BLOCKS __has_feature(blocks) -#define WTF_COMPILER_SUPPORTS_C_STATIC_ASSERT __has_extension(c_static_assert) -#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL __has_extension(cxx_override_control) -#define WTF_COMPILER_SUPPORTS_HAS_TRIVIAL_DESTRUCTOR __has_extension(has_trivial_destructor) - -#endif - -#ifndef CLANG_PRAGMA -#define CLANG_PRAGMA(PRAGMA) -#endif - -/* COMPILER(MSVC) - Microsoft Visual C++ */ -/* COMPILER(MSVC7_OR_LOWER) - Microsoft Visual C++ 2003 or lower*/ -/* COMPILER(MSVC9_OR_LOWER) - Microsoft Visual C++ 2008 or lower*/ -#if defined(_MSC_VER) -#define WTF_COMPILER_MSVC 1 -#if _MSC_VER < 1400 -#define WTF_COMPILER_MSVC7_OR_LOWER 1 -#elif _MSC_VER < 1600 -#define WTF_COMPILER_MSVC9_OR_LOWER 1 -#endif - -/* Specific compiler features */ -#if !COMPILER(CLANG) && _MSC_VER >= 1600 -#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 -#endif - -#if !COMPILER(CLANG) -#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL 1 -#define WTF_COMPILER_QUIRK_FINAL_IS_CALLED_SEALED 1 -#endif - -#endif - -/* COMPILER(RVCT) - ARM RealView Compilation Tools */ -/* COMPILER(RVCT4_OR_GREATER) - ARM RealView Compilation Tools 4.0 or greater */ -#if defined(__CC_ARM) || defined(__ARMCC__) -#define WTF_COMPILER_RVCT 1 -#define RVCT_VERSION_AT_LEAST(major, minor, patch, build) (__ARMCC_VERSION >= (major * 100000 + minor * 10000 + patch * 1000 + build)) -#else -/* Define this for !RVCT compilers, just so we can write things like RVCT_VERSION_AT_LEAST(3, 0, 0, 0). */ -#define RVCT_VERSION_AT_LEAST(major, minor, patch, build) 0 -#endif - -/* COMPILER(GCCE) - GNU Compiler Collection for Embedded */ -#if defined(__GCCE__) -#define WTF_COMPILER_GCCE 1 -#define GCCE_VERSION (__GCCE__ * 10000 + __GCCE_MINOR__ * 100 + __GCCE_PATCHLEVEL__) -#define GCCE_VERSION_AT_LEAST(major, minor, patch) (GCCE_VERSION >= (major * 10000 + minor * 100 + patch)) -#endif - -/* COMPILER(GCC) - GNU Compiler Collection */ -/* --gnu option of the RVCT compiler also defines __GNUC__ */ -#if defined(__GNUC__) && !COMPILER(RVCT) -#define WTF_COMPILER_GCC 1 -#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) -#define GCC_VERSION_AT_LEAST(major, minor, patch) (GCC_VERSION >= (major * 10000 + minor * 100 + patch)) -#else -/* Define this for !GCC compilers, just so we can write things like GCC_VERSION_AT_LEAST(4, 1, 0). */ -#define GCC_VERSION_AT_LEAST(major, minor, patch) 0 -#endif - -/* Specific compiler features */ -#if COMPILER(GCC) && !COMPILER(CLANG) -#if GCC_VERSION_AT_LEAST(4, 7, 0) && defined(__cplusplus) && __cplusplus >= 201103L -#define WTF_COMPILER_SUPPORTS_CXX_RVALUE_REFERENCES 1 -#define WTF_COMPILER_SUPPORTS_CXX_DELETED_FUNCTIONS 1 -#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 -#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL 1 -#define WTF_COMPILER_QUIRK_GCC11_GLOBAL_ISINF_ISNAN 1 - -#elif GCC_VERSION_AT_LEAST(4, 6, 0) && defined(__GXX_EXPERIMENTAL_CXX0X__) -#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 -#define WTF_COMPILER_QUIRK_GCC11_GLOBAL_ISINF_ISNAN 1 -#endif - -#endif - -/* COMPILER(MINGW) - MinGW GCC */ -/* COMPILER(MINGW64) - mingw-w64 GCC - only used as additional check to exclude mingw.org specific functions */ -#if defined(__MINGW32__) -#define WTF_COMPILER_MINGW 1 -#include <_mingw.h> /* private MinGW header */ - #if defined(__MINGW64_VERSION_MAJOR) /* best way to check for mingw-w64 vs mingw.org */ - #define WTF_COMPILER_MINGW64 1 - #endif /* __MINGW64_VERSION_MAJOR */ -#endif /* __MINGW32__ */ - -/* COMPILER(INTEL) - Intel C++ Compiler */ -#if defined(__INTEL_COMPILER) -#define WTF_COMPILER_INTEL 1 -#endif - -/* COMPILER(SUNCC) */ -#if defined(__SUNPRO_CC) || defined(__SUNPRO_C) -#define WTF_COMPILER_SUNCC 1 -#endif - -/* ==== Compiler features ==== */ - - -/* ALWAYS_INLINE */ - -#ifndef ALWAYS_INLINE -#if COMPILER(GCC) && defined(NDEBUG) && !COMPILER(MINGW) -#define ALWAYS_INLINE inline __attribute__((__always_inline__)) -#elif (COMPILER(MSVC) || COMPILER(RVCT)) && defined(NDEBUG) -#define ALWAYS_INLINE __forceinline -#else -#define ALWAYS_INLINE inline -#endif -#endif - - -/* NEVER_INLINE */ - -#ifndef NEVER_INLINE -#if COMPILER(GCC) -#define NEVER_INLINE __attribute__((__noinline__)) -#elif COMPILER(RVCT) -#define NEVER_INLINE __declspec(noinline) -#else -#define NEVER_INLINE -#endif -#endif - - -/* UNLIKELY */ - -#ifndef UNLIKELY -#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(3, 0, 0, 0) && defined(__GNUC__)) -#define UNLIKELY(x) __builtin_expect((x), 0) -#else -#define UNLIKELY(x) (x) -#endif -#endif - - -/* LIKELY */ - -#ifndef LIKELY -#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(3, 0, 0, 0) && defined(__GNUC__)) -#define LIKELY(x) __builtin_expect((x), 1) -#else -#define LIKELY(x) (x) -#endif -#endif - - -/* NO_RETURN */ - - -#ifndef NO_RETURN -#if COMPILER(GCC) -#define NO_RETURN __attribute((__noreturn__)) -#elif COMPILER(MSVC) || COMPILER(RVCT) -#define NO_RETURN __declspec(noreturn) -#else -#define NO_RETURN -#endif -#endif - - -/* NO_RETURN_WITH_VALUE */ - -#ifndef NO_RETURN_WITH_VALUE -#if !COMPILER(MSVC) -#define NO_RETURN_WITH_VALUE NO_RETURN -#else -#define NO_RETURN_WITH_VALUE -#endif -#endif - - -/* WARN_UNUSED_RETURN */ - -#if COMPILER(GCC) -#define WARN_UNUSED_RETURN __attribute__ ((warn_unused_result)) -#else -#define WARN_UNUSED_RETURN -#endif - -/* OVERRIDE and FINAL */ - -#if COMPILER_SUPPORTS(CXX_OVERRIDE_CONTROL) -#define OVERRIDE override - -#if COMPILER_QUIRK(FINAL_IS_CALLED_SEALED) -#define FINAL sealed -#else -#define FINAL final -#endif - -#else -#define OVERRIDE -#define FINAL -#endif - -/* REFERENCED_FROM_ASM */ - -#ifndef REFERENCED_FROM_ASM -#if COMPILER(GCC) -#define REFERENCED_FROM_ASM __attribute__((used)) -#else -#define REFERENCED_FROM_ASM -#endif -#endif - -/* OBJC_CLASS */ - -#ifndef OBJC_CLASS -#ifdef __OBJC__ -#define OBJC_CLASS @class -#else -#define OBJC_CLASS class -#endif -#endif - -/* ABI */ -#if defined(__ARM_EABI__) || defined(__EABI__) -#define WTF_COMPILER_SUPPORTS_EABI 1 -#endif - -#endif /* WTF_Compiler_h */ diff --git a/3rdparty/masm/wtf/CryptographicallyRandomNumber.h b/3rdparty/masm/wtf/CryptographicallyRandomNumber.h deleted file mode 100644 index 2262b6c3b3..0000000000 --- a/3rdparty/masm/wtf/CryptographicallyRandomNumber.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_CryptographicallyRandomNumber_h -#define WTF_CryptographicallyRandomNumber_h - -#include - -namespace WTF { - -#if USE(OS_RANDOMNESS) -WTF_EXPORT_PRIVATE uint32_t cryptographicallyRandomNumber(); -WTF_EXPORT_PRIVATE void cryptographicallyRandomValues(void* buffer, size_t length); -#endif - -} - -#if USE(OS_RANDOMNESS) -using WTF::cryptographicallyRandomNumber; -using WTF::cryptographicallyRandomValues; -#endif - -#endif diff --git a/3rdparty/masm/wtf/DataLog.h b/3rdparty/masm/wtf/DataLog.h deleted file mode 100644 index 0bd8efe727..0000000000 --- a/3rdparty/masm/wtf/DataLog.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef DataLog_h -#define DataLog_h - -#include -#include -#include -#include -#include - -namespace WTF { - -WTF_EXPORT_PRIVATE FilePrintStream& dataFile(); - -WTF_EXPORT_PRIVATE void dataLogFV(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(1, 0); -WTF_EXPORT_PRIVATE void dataLogF(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); -WTF_EXPORT_PRIVATE void dataLogFString(const char*); - -template -void dataLog(const T& value) -{ - dataFile().print(value); -} - -template -void dataLog(const T1& value1, const T2& value2) -{ - dataFile().print(value1, value2); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3) -{ - dataFile().print(value1, value2, value3); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4) -{ - dataFile().print(value1, value2, value3, value4); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5) -{ - dataFile().print(value1, value2, value3, value4, value5); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6) -{ - dataFile().print(value1, value2, value3, value4, value5, value6); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11, value12); -} - -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12, const T13& value13) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11, value12, value13); -} - -} // namespace WTF - -using WTF::dataLog; -using WTF::dataLogF; -using WTF::dataLogFString; - -#endif // DataLog_h - diff --git a/3rdparty/masm/wtf/DynamicAnnotations.h b/3rdparty/masm/wtf/DynamicAnnotations.h deleted file mode 100644 index 38acce35e6..0000000000 --- a/3rdparty/masm/wtf/DynamicAnnotations.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_DynamicAnnotations_h -#define WTF_DynamicAnnotations_h - -/* This file defines dynamic annotations for use with dynamic analysis - * tool such as ThreadSanitizer, Valgrind, etc. - * - * Dynamic annotation is a source code annotation that affects - * the generated code (that is, the annotation is not a comment). - * Each such annotation is attached to a particular - * instruction and/or to a particular object (address) in the program. - * - * By using dynamic annotations a developer can give more details to the dynamic - * analysis tool to improve its precision. - * - * In C/C++ program the annotations are represented as C macros. - * With the default build flags, these macros are empty, hence don't affect - * performance of a compiled binary. - * If dynamic annotations are enabled, they just call no-op functions. - * The dynamic analysis tools can intercept these functions and replace them - * with their own implementations. - * - * See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations for more information. - */ - -#if USE(DYNAMIC_ANNOTATIONS) -/* Tell data race detector that we're not interested in reports on the given address range. */ -#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) WTFAnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description) -#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) WTFAnnotateBenignRaceSized(__FILE__, __LINE__, pointer, sizeof(*(pointer)), description) - -/* Annotations for user-defined synchronization mechanisms. - * These annotations can be used to define happens-before arcs in user-defined - * synchronization mechanisms: the race detector will infer an arc from - * the former to the latter when they share the same argument pointer. - * - * The most common case requiring annotations is atomic reference counting: - * bool deref() { - * ANNOTATE_HAPPENS_BEFORE(&m_refCount); - * if (!atomicDecrement(&m_refCount)) { - * // m_refCount is now 0 - * ANNOTATE_HAPPENS_AFTER(&m_refCount); - * // "return true; happens-after each atomicDecrement of m_refCount" - * return true; - * } - * return false; - * } - */ -#define WTF_ANNOTATE_HAPPENS_BEFORE(address) WTFAnnotateHappensBefore(__FILE__, __LINE__, address) -#define WTF_ANNOTATE_HAPPENS_AFTER(address) WTFAnnotateHappensAfter(__FILE__, __LINE__, address) - -#ifdef __cplusplus -extern "C" { -#endif -/* Don't use these directly, use the above macros instead. */ -void WTFAnnotateBenignRaceSized(const char* file, int line, const volatile void* memory, long size, const char* description); -void WTFAnnotateHappensBefore(const char* file, int line, const volatile void* address); -void WTFAnnotateHappensAfter(const char* file, int line, const volatile void* address); -#ifdef __cplusplus -} // extern "C" -#endif - -#else // USE(DYNAMIC_ANNOTATIONS) -/* These macros are empty when dynamic annotations are not enabled so you can - * use them without affecting the performance of release binaries. */ -#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) -#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) -#define WTF_ANNOTATE_HAPPENS_BEFORE(address) -#define WTF_ANNOTATE_HAPPENS_AFTER(address) -#endif // USE(DYNAMIC_ANNOTATIONS) - -#endif // WTF_DynamicAnnotations_h diff --git a/3rdparty/masm/wtf/FilePrintStream.cpp b/3rdparty/masm/wtf/FilePrintStream.cpp deleted file mode 100644 index b5ab25e0bf..0000000000 --- a/3rdparty/masm/wtf/FilePrintStream.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "FilePrintStream.h" - -namespace WTF { - -FilePrintStream::FilePrintStream(FILE* file, AdoptionMode adoptionMode) - : m_file(file) - , m_adoptionMode(adoptionMode) -{ -} - -FilePrintStream::~FilePrintStream() -{ - if (m_adoptionMode == Borrow) - return; - fclose(m_file); -} - -PassOwnPtr FilePrintStream::open(const char* filename, const char* mode) -{ - FILE* file = fopen(filename, mode); - if (!file) - return PassOwnPtr(); - - return adoptPtr(new FilePrintStream(file)); -} - -void FilePrintStream::vprintf(const char* format, va_list argList) -{ - vfprintf(m_file, format, argList); -} - -void FilePrintStream::flush() -{ - fflush(m_file); -} - -} // namespace WTF - diff --git a/3rdparty/masm/wtf/FilePrintStream.h b/3rdparty/masm/wtf/FilePrintStream.h deleted file mode 100644 index bdeab4c479..0000000000 --- a/3rdparty/masm/wtf/FilePrintStream.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef FilePrintStream_h -#define FilePrintStream_h - -#include -#include -#include - -namespace WTF { - -class FilePrintStream : public PrintStream { -public: - enum AdoptionMode { - Adopt, - Borrow - }; - - FilePrintStream(FILE*, AdoptionMode = Adopt); - virtual ~FilePrintStream(); - - static PassOwnPtr open(const char* filename, const char* mode); - - FILE* file() { return m_file; } - - void vprintf(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(2, 0); - void flush(); - -private: - FILE* m_file; - AdoptionMode m_adoptionMode; -}; - -} // namespace WTF - -using WTF::FilePrintStream; - -#endif // FilePrintStream_h - diff --git a/3rdparty/masm/wtf/Locker.h b/3rdparty/masm/wtf/Locker.h deleted file mode 100644 index c465b99ea4..0000000000 --- a/3rdparty/masm/wtf/Locker.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef Locker_h -#define Locker_h - -#include - -namespace WTF { - -template class Locker { - WTF_MAKE_NONCOPYABLE(Locker); -public: - Locker(T& lockable) : m_lockable(lockable) { m_lockable.lock(); } - ~Locker() { m_lockable.unlock(); } -private: - T& m_lockable; -}; - -} - -using WTF::Locker; - -#endif diff --git a/3rdparty/masm/wtf/NotFound.h b/3rdparty/masm/wtf/NotFound.h deleted file mode 100644 index 4263bcecab..0000000000 --- a/3rdparty/masm/wtf/NotFound.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef NotFound_h -#define NotFound_h - -namespace WTF { - - const size_t notFound = static_cast(-1); - -} // namespace WTF - -using WTF::notFound; - -#endif // NotFound_h diff --git a/3rdparty/masm/wtf/NullPtr.h b/3rdparty/masm/wtf/NullPtr.h deleted file mode 100644 index 98c05140d8..0000000000 --- a/3rdparty/masm/wtf/NullPtr.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - -Copyright (C) 2010 Apple Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef NullPtr_h -#define NullPtr_h - -// For compilers and standard libraries that do not yet include it, this adds the -// nullptr_t type and nullptr object. They are defined in the same namespaces they -// would be in compiler and library that had the support. - -#include - -#if COMPILER_SUPPORTS(CXX_NULLPTR) || defined(_LIBCPP_VERSION) - -#include - -// libstdc++ supports nullptr_t starting with gcc 4.6. -#if defined(__GLIBCXX__) && __GLIBCXX__ < 20110325 -namespace std { -typedef decltype(nullptr) nullptr_t; -} -#endif - -#else - -namespace std { -class WTF_EXPORT_PRIVATE nullptr_t { }; -} -extern WTF_EXPORT_PRIVATE std::nullptr_t nullptr; - -#endif - -#endif diff --git a/3rdparty/masm/wtf/OSAllocator.h b/3rdparty/masm/wtf/OSAllocator.h deleted file mode 100644 index a12a467497..0000000000 --- a/3rdparty/masm/wtf/OSAllocator.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef OSAllocator_h -#define OSAllocator_h - -#include -#include -#include - -namespace WTF { - -class OSAllocator { -public: - enum Usage { - UnknownUsage = -1, - FastMallocPages = VM_TAG_FOR_TCMALLOC_MEMORY, - JSGCHeapPages = VM_TAG_FOR_COLLECTOR_MEMORY, - JSVMStackPages = VM_TAG_FOR_REGISTERFILE_MEMORY, - JSJITCodePages = VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY, - }; - - // These methods are symmetric; reserveUncommitted allocates VM in an uncommitted state, - // releaseDecommitted should be called on a region of VM allocated by a single reservation, - // the memory must all currently be in a decommitted state. - static void* reserveUncommitted(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool includesGuardPages = false); - WTF_EXPORT_PRIVATE static void releaseDecommitted(void*, size_t); - - // These methods are symmetric; they commit or decommit a region of VM (uncommitted VM should - // never be accessed, since the OS may not have attached physical memory for these regions). - // Clients should only call commit on uncommitted regions and decommit on committed regions. - static void commit(void*, size_t, bool writable, bool executable); - static void decommit(void*, size_t); - - // These methods are symmetric; reserveAndCommit allocates VM in an committed state, - // decommitAndRelease should be called on a region of VM allocated by a single reservation, - // the memory must all currently be in a committed state. - WTF_EXPORT_PRIVATE static void* reserveAndCommit(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool includesGuardPages = false); - static void decommitAndRelease(void* base, size_t size); - - // These methods are akin to reserveAndCommit/decommitAndRelease, above - however rather than - // committing/decommitting the entire region additional parameters allow a subregion to be - // specified. - static void* reserveAndCommit(size_t reserveSize, size_t commitSize, Usage = UnknownUsage, bool writable = true, bool executable = false); - static void decommitAndRelease(void* releaseBase, size_t releaseSize, void* decommitBase, size_t decommitSize); - - // Reallocate an existing, committed allocation. - // The prior allocation must be fully comitted, and the new size will also be fully committed. - // This interface is provided since it may be possible to optimize this operation on some platforms. - template - static T* reallocateCommitted(T*, size_t oldSize, size_t newSize, Usage = UnknownUsage, bool writable = true, bool executable = false); -}; - -inline void* OSAllocator::reserveAndCommit(size_t reserveSize, size_t commitSize, Usage usage, bool writable, bool executable) -{ - void* base = reserveUncommitted(reserveSize, usage, writable, executable); - commit(base, commitSize, writable, executable); - return base; -} - -inline void OSAllocator::decommitAndRelease(void* releaseBase, size_t releaseSize, void* decommitBase, size_t decommitSize) -{ - ASSERT(decommitBase >= releaseBase && (static_cast(decommitBase) + decommitSize) <= (static_cast(releaseBase) + releaseSize)); -#if OS(WINCE) - // On most platforms we can actually skip this final decommit; releasing the VM will - // implicitly decommit any physical memory in the region. This is not true on WINCE. - decommit(decommitBase, decommitSize); -#else - UNUSED_PARAM(decommitBase); - UNUSED_PARAM(decommitSize); -#endif - releaseDecommitted(releaseBase, releaseSize); -} - -inline void OSAllocator::decommitAndRelease(void* base, size_t size) -{ - decommitAndRelease(base, size, base, size); -} - -template -inline T* OSAllocator::reallocateCommitted(T* oldBase, size_t oldSize, size_t newSize, Usage usage, bool writable, bool executable) -{ - void* newBase = reserveAndCommit(newSize, usage, writable, executable); - memcpy(newBase, oldBase, std::min(oldSize, newSize)); - decommitAndRelease(oldBase, oldSize); - return static_cast(newBase); -} - -} // namespace WTF - -using WTF::OSAllocator; - -#endif // OSAllocator_h diff --git a/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/3rdparty/masm/wtf/OSAllocatorPosix.cpp deleted file mode 100644 index b5b903b8a3..0000000000 --- a/3rdparty/masm/wtf/OSAllocatorPosix.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "OSAllocator.h" - -#include "PageAllocation.h" -#include -#include -#include -#include - -namespace WTF { - -void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) -{ -#if OS(QNX) - // Reserve memory with PROT_NONE and MAP_LAZY so it isn't committed now. - void* result = mmap(0, bytes, PROT_NONE, MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); - if (result == MAP_FAILED) - CRASH(); -#elif OS(LINUX) - void* result = mmap(0, bytes, PROT_NONE, MAP_NORESERVE | MAP_PRIVATE | MAP_ANON, -1, 0); - if (result == MAP_FAILED) - CRASH(); - madvise(result, bytes, MADV_DONTNEED); -#else - void* result = reserveAndCommit(bytes, usage, writable, executable, includesGuardPages); -#if HAVE(MADV_FREE_REUSE) - // To support the "reserve then commit" model, we have to initially decommit. - while (madvise(result, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { } -#endif - -#endif // OS(QNX) - - return result; -} - -void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) -{ - // All POSIX reservations start out logically committed. - int protection = PROT_READ; - if (writable) - protection |= PROT_WRITE; - if (executable) - protection |= PROT_EXEC; - - int flags = MAP_PRIVATE | MAP_ANON; -#if PLATFORM(IOS) - if (executable) - flags |= MAP_JIT; -#endif - -#if OS(DARWIN) - int fd = usage; -#else - UNUSED_PARAM(usage); - int fd = -1; -#endif - - void* result = 0; -#if (OS(DARWIN) && CPU(X86_64)) - if (executable) { - ASSERT(includesGuardPages); - // Cook up an address to allocate at, using the following recipe: - // 17 bits of zero, stay in userspace kids. - // 26 bits of randomness for ASLR. - // 21 bits of zero, at least stay aligned within one level of the pagetables. - // - // But! - as a temporary workaround for some plugin problems (rdar://problem/6812854), - // for now instead of 2^26 bits of ASLR lets stick with 25 bits of randomization plus - // 2^24, which should put up somewhere in the middle of userspace (in the address range - // 0x200000000000 .. 0x5fffffffffff). - intptr_t randomLocation = 0; - randomLocation = arc4random() & ((1 << 25) - 1); - randomLocation += (1 << 24); - randomLocation <<= 21; - result = reinterpret_cast(randomLocation); - } -#endif - - result = mmap(result, bytes, protection, flags, fd, 0); - if (result == MAP_FAILED) { -#if ENABLE(LLINT) - if (executable) - result = 0; - else -#endif - CRASH(); - } - if (result && includesGuardPages) { - // We use mmap to remap the guardpages rather than using mprotect as - // mprotect results in multiple references to the code region. This - // breaks the madvise based mechanism we use to return physical memory - // to the OS. - mmap(result, pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0); - mmap(static_cast(result) + bytes - pageSize(), pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0); - } - return result; -} - -void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable) -{ -#if OS(QNX) - int protection = PROT_READ; - if (writable) - protection |= PROT_WRITE; - if (executable) - protection |= PROT_EXEC; - if (MAP_FAILED == mmap(address, bytes, protection, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0)) - CRASH(); -#elif OS(LINUX) - int protection = PROT_READ; - if (writable) - protection |= PROT_WRITE; - if (executable) - protection |= PROT_EXEC; - if (mprotect(address, bytes, protection)) - CRASH(); - madvise(address, bytes, MADV_WILLNEED); -#elif HAVE(MADV_FREE_REUSE) - UNUSED_PARAM(writable); - UNUSED_PARAM(executable); - while (madvise(address, bytes, MADV_FREE_REUSE) == -1 && errno == EAGAIN) { } -#else - // Non-MADV_FREE_REUSE reservations automatically commit on demand. - UNUSED_PARAM(address); - UNUSED_PARAM(bytes); - UNUSED_PARAM(writable); - UNUSED_PARAM(executable); -#endif -} - -void OSAllocator::decommit(void* address, size_t bytes) -{ -#if OS(QNX) - // Use PROT_NONE and MAP_LAZY to decommit the pages. - mmap(address, bytes, PROT_NONE, MAP_FIXED | MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); -#elif OS(LINUX) - madvise(address, bytes, MADV_DONTNEED); - if (mprotect(address, bytes, PROT_NONE)) - CRASH(); -#elif HAVE(MADV_FREE_REUSE) - while (madvise(address, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { } -#elif HAVE(MADV_FREE) - while (madvise(address, bytes, MADV_FREE) == -1 && errno == EAGAIN) { } -#elif HAVE(MADV_DONTNEED) - while (madvise(address, bytes, MADV_DONTNEED) == -1 && errno == EAGAIN) { } -#else - UNUSED_PARAM(address); - UNUSED_PARAM(bytes); -#endif -} - -void OSAllocator::releaseDecommitted(void* address, size_t bytes) -{ - int result = munmap(address, bytes); - if (result == -1) - CRASH(); -} - -} // namespace WTF diff --git a/3rdparty/masm/wtf/OSAllocatorWin.cpp b/3rdparty/masm/wtf/OSAllocatorWin.cpp deleted file mode 100644 index 7f5d9b8904..0000000000 --- a/3rdparty/masm/wtf/OSAllocatorWin.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "OSAllocator.h" - -#include "windows.h" -#include - -namespace WTF { - -static inline DWORD protection(bool writable, bool executable) -{ - return executable ? - (writable ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ) : - (writable ? PAGE_READWRITE : PAGE_READONLY); -} - -void* OSAllocator::reserveUncommitted(size_t bytes, Usage, bool writable, bool executable, bool) -{ - void* result = VirtualAlloc(0, bytes, MEM_RESERVE, protection(writable, executable)); - if (!result) - CRASH(); - return result; -} - -void* OSAllocator::reserveAndCommit(size_t bytes, Usage, bool writable, bool executable, bool) -{ - void* result = VirtualAlloc(0, bytes, MEM_RESERVE | MEM_COMMIT, protection(writable, executable)); - if (!result) - CRASH(); - return result; -} - -void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable) -{ - void* result = VirtualAlloc(address, bytes, MEM_COMMIT, protection(writable, executable)); - if (!result) - CRASH(); -} - -void OSAllocator::decommit(void* address, size_t bytes) -{ - bool result = VirtualFree(address, bytes, MEM_DECOMMIT); - if (!result) - CRASH(); -} - -void OSAllocator::releaseDecommitted(void* address, size_t bytes) -{ - // According to http://msdn.microsoft.com/en-us/library/aa366892(VS.85).aspx, - // dwSize must be 0 if dwFreeType is MEM_RELEASE. - bool result = VirtualFree(address, 0, MEM_RELEASE); - if (!result) - CRASH(); -} - -} // namespace WTF diff --git a/3rdparty/masm/wtf/PageAllocation.h b/3rdparty/masm/wtf/PageAllocation.h deleted file mode 100644 index 18d31880c0..0000000000 --- a/3rdparty/masm/wtf/PageAllocation.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef PageAllocation_h -#define PageAllocation_h - -#include -#include -#include -#include -#include -#include - -#if OS(DARWIN) -#include -#include -#endif - -#if OS(WINDOWS) -#include -#include -#endif - -#if HAVE(ERRNO_H) -#include -#endif - -#if HAVE(MMAP) -#include -#include -#endif - -namespace WTF { - -/* - PageAllocation - - The PageAllocation class provides a cross-platform memory allocation interface - with similar capabilities to posix mmap/munmap. Memory is allocated by calling - PageAllocation::allocate, and deallocated by calling deallocate on the - PageAllocation object. The PageAllocation holds the allocation's base pointer - and size. - - The allocate method is passed the size required (which must be a multiple of - the system page size, which can be accessed using PageAllocation::pageSize). - Callers may also optinally provide a flag indicating the usage (for use by - system memory usage tracking tools, where implemented), and boolean values - specifying the required protection (defaulting to writable, non-executable). -*/ - -class PageAllocation : private PageBlock { -public: - PageAllocation() - { - } - - using PageBlock::size; - using PageBlock::base; - -#ifndef __clang__ - using PageBlock::operator bool; -#else - // FIXME: This is a workaround for , wherein Clang incorrectly emits an access - // control warning when a client tries to use operator bool exposed above via "using PageBlock::operator bool". - operator bool() const { return PageBlock::operator bool(); } -#endif - - static PageAllocation allocate(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) - { - ASSERT(isPageAligned(size)); - return PageAllocation(OSAllocator::reserveAndCommit(size, usage, writable, executable), size); - } - - void deallocate() - { - // Clear base & size before calling release; if this is *inside* allocation - // then we won't be able to clear then after deallocating the memory. - PageAllocation tmp; - std::swap(tmp, *this); - - ASSERT(tmp); - ASSERT(!*this); - - OSAllocator::decommitAndRelease(tmp.base(), tmp.size()); - } - -private: - PageAllocation(void* base, size_t size) - : PageBlock(base, size, false) - { - } -}; - -} // namespace WTF - -using WTF::PageAllocation; - -#endif // PageAllocation_h diff --git a/3rdparty/masm/wtf/PageAllocationAligned.cpp b/3rdparty/masm/wtf/PageAllocationAligned.cpp deleted file mode 100644 index 6f54710d0b..0000000000 --- a/3rdparty/masm/wtf/PageAllocationAligned.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "PageAllocationAligned.h" - -namespace WTF { - -PageAllocationAligned PageAllocationAligned::allocate(size_t size, size_t alignment, OSAllocator::Usage usage, bool writable, bool executable) -{ - ASSERT(isPageAligned(size)); - ASSERT(isPageAligned(alignment)); - ASSERT(isPowerOfTwo(alignment)); - ASSERT(size >= alignment); - size_t alignmentMask = alignment - 1; - -#if OS(DARWIN) - int flags = VM_FLAGS_ANYWHERE; - if (usage != OSAllocator::UnknownUsage) - flags |= usage; - int protection = PROT_READ; - if (writable) - protection |= PROT_WRITE; - if (executable) - protection |= PROT_EXEC; - - vm_address_t address = 0; - vm_map(current_task(), &address, size, alignmentMask, flags, MEMORY_OBJECT_NULL, 0, FALSE, protection, PROT_READ | PROT_WRITE | PROT_EXEC, VM_INHERIT_DEFAULT); - return PageAllocationAligned(reinterpret_cast(address), size); -#else - size_t alignmentDelta = alignment - pageSize(); - - // Resererve with suffcient additional VM to correctly align. - size_t reservationSize = size + alignmentDelta; - void* reservationBase = OSAllocator::reserveUncommitted(reservationSize, usage, writable, executable); - - // Select an aligned region within the reservation and commit. - void* alignedBase = reinterpret_cast(reservationBase) & alignmentMask - ? reinterpret_cast((reinterpret_cast(reservationBase) & ~alignmentMask) + alignment) - : reservationBase; - OSAllocator::commit(alignedBase, size, writable, executable); - - return PageAllocationAligned(alignedBase, size, reservationBase, reservationSize); -#endif -} - -void PageAllocationAligned::deallocate() -{ - // Clear base & size before calling release; if this is *inside* allocation - // then we won't be able to clear then after deallocating the memory. - PageAllocationAligned tmp; - std::swap(tmp, *this); - - ASSERT(tmp); - ASSERT(!*this); - -#if OS(DARWIN) - vm_deallocate(current_task(), reinterpret_cast(tmp.base()), tmp.size()); -#else - ASSERT(tmp.m_reservation.contains(tmp.base(), tmp.size())); - OSAllocator::decommitAndRelease(tmp.m_reservation.base(), tmp.m_reservation.size(), tmp.base(), tmp.size()); -#endif -} - -} // namespace WTF diff --git a/3rdparty/masm/wtf/PageAllocationAligned.h b/3rdparty/masm/wtf/PageAllocationAligned.h deleted file mode 100644 index c018dabd8e..0000000000 --- a/3rdparty/masm/wtf/PageAllocationAligned.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef PageAllocationAligned_h -#define PageAllocationAligned_h - -#include -#include - -namespace WTF { - -class PageAllocationAligned : private PageBlock { -public: - PageAllocationAligned() - { - } - - using PageBlock::operator bool; - using PageBlock::size; - using PageBlock::base; - - static PageAllocationAligned allocate(size_t size, size_t alignment, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false); - - void deallocate(); - -private: -#if OS(DARWIN) - PageAllocationAligned(void* base, size_t size) - : PageBlock(base, size, false) - { - } -#else - PageAllocationAligned(void* base, size_t size, void* reservationBase, size_t reservationSize) - : PageBlock(base, size, false) - , m_reservation(reservationBase, reservationSize, false) - { - } - - PageBlock m_reservation; -#endif -}; - - -} // namespace WTF - -using WTF::PageAllocationAligned; - -#endif // PageAllocationAligned_h diff --git a/3rdparty/masm/wtf/PageBlock.cpp b/3rdparty/masm/wtf/PageBlock.cpp deleted file mode 100644 index 8bbd7eb600..0000000000 --- a/3rdparty/masm/wtf/PageBlock.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "PageBlock.h" - -#if OS(UNIX) -#include -#endif - -#if OS(WINDOWS) -#include -#include -#endif - -namespace WTF { - -static size_t s_pageSize; -static size_t s_pageMask; - -#if OS(UNIX) - -inline size_t systemPageSize() -{ - return getpagesize(); -} - -#elif OS(WINDOWS) - -inline size_t systemPageSize() -{ - static size_t size = 0; - SYSTEM_INFO system_info; - GetSystemInfo(&system_info); - size = system_info.dwPageSize; - return size; -} - -#endif - -size_t pageSize() -{ - if (!s_pageSize) - s_pageSize = systemPageSize(); - ASSERT(isPowerOfTwo(s_pageSize)); - return s_pageSize; -} - -size_t pageMask() -{ - if (!s_pageMask) - s_pageMask = ~(pageSize() - 1); - return s_pageMask; -} - -} // namespace WTF diff --git a/3rdparty/masm/wtf/PageBlock.h b/3rdparty/masm/wtf/PageBlock.h deleted file mode 100644 index 56e5570178..0000000000 --- a/3rdparty/masm/wtf/PageBlock.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef PageBlock_h -#define PageBlock_h - -namespace WTF { - -WTF_EXPORT_PRIVATE size_t pageSize(); -WTF_EXPORT_PRIVATE size_t pageMask(); -inline bool isPageAligned(void* address) { return !(reinterpret_cast(address) & (pageSize() - 1)); } -inline bool isPageAligned(size_t size) { return !(size & (pageSize() - 1)); } -inline bool isPowerOfTwo(size_t size) { return !(size & (size - 1)); } - -class PageBlock { -public: - PageBlock(); - PageBlock(const PageBlock&); - PageBlock(void*, size_t, bool hasGuardPages); - - void* base() const { return m_base; } - size_t size() const { return m_size; } - - operator bool() const { return !!m_realBase; } - - bool contains(void* containedBase, size_t containedSize) - { - return containedBase >= m_base - && (static_cast(containedBase) + containedSize) <= (static_cast(m_base) + m_size); - } - -private: - void* m_realBase; - void* m_base; - size_t m_size; -}; - -inline PageBlock::PageBlock() - : m_realBase(0) - , m_base(0) - , m_size(0) -{ -} - -inline PageBlock::PageBlock(const PageBlock& other) - : m_realBase(other.m_realBase) - , m_base(other.m_base) - , m_size(other.m_size) -{ -} - -inline PageBlock::PageBlock(void* base, size_t size, bool hasGuardPages) - : m_realBase(base) - , m_base(static_cast(base) + ((base && hasGuardPages) ? pageSize() : 0)) - , m_size(size) -{ -} - -} // namespace WTF - -using WTF::pageSize; -using WTF::isPageAligned; -using WTF::isPageAligned; -using WTF::isPowerOfTwo; - -#endif // PageBlock_h diff --git a/3rdparty/masm/wtf/PageReservation.h b/3rdparty/masm/wtf/PageReservation.h deleted file mode 100644 index 77783ebcc4..0000000000 --- a/3rdparty/masm/wtf/PageReservation.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef PageReservation_h -#define PageReservation_h - -#include - -namespace WTF { - -/* - PageReservation - - Like PageAllocation, the PageReservation class provides a cross-platform memory - allocation interface, but with a set of capabilities more similar to that of - VirtualAlloc than posix mmap. PageReservation can be used to allocate virtual - memory without committing physical memory pages using PageReservation::reserve. - Following a call to reserve all memory in the region is in a decommited state, - in which the memory should not be used (accessing the memory may cause a fault). - - Before using memory it must be committed by calling commit, which is passed start - and size values (both of which require system page size granularity). One the - committed memory is no longer needed 'decommit' may be called to return the - memory to its devommitted state. Commit should only be called on memory that is - currently decommitted, and decommit should only be called on memory regions that - are currently committed. All memory should be decommited before the reservation - is deallocated. Values in memory may not be retained accross a pair of calls if - the region of memory is decommitted and then committed again. - - Memory protection should not be changed on decommitted memory, and if protection - is changed on memory while it is committed it should be returned to the orignal - protection before decommit is called. -*/ - -class PageReservation : private PageBlock { -public: - PageReservation() - : m_committed(0) - , m_writable(false) - , m_executable(false) - { - } - - using PageBlock::base; - using PageBlock::size; - -#ifndef __clang__ - using PageBlock::operator bool; -#else - // FIXME: This is a workaround for , wherein Clang incorrectly emits an access - // control warning when a client tries to use operator bool exposed above via "using PageBlock::operator bool". - operator bool() const { return PageBlock::operator bool(); } -#endif - - void commit(void* start, size_t size) - { - ASSERT(*this); - ASSERT(isPageAligned(start)); - ASSERT(isPageAligned(size)); - ASSERT(contains(start, size)); - - m_committed += size; - OSAllocator::commit(start, size, m_writable, m_executable); - } - - void decommit(void* start, size_t size) - { - ASSERT(*this); - ASSERT(isPageAligned(start)); - ASSERT(isPageAligned(size)); - ASSERT(contains(start, size)); - - m_committed -= size; - OSAllocator::decommit(start, size); - } - - size_t committed() - { - return m_committed; - } - - static PageReservation reserve(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) - { - ASSERT(isPageAligned(size)); - return PageReservation(OSAllocator::reserveUncommitted(size, usage, writable, executable), size, writable, executable, false); - } - - static PageReservation reserveWithGuardPages(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) - { - ASSERT(isPageAligned(size)); - return PageReservation(OSAllocator::reserveUncommitted(size + pageSize() * 2, usage, writable, executable, true), size, writable, executable, true); - } - - void deallocate() - { - ASSERT(!m_committed); - - // Clear base & size before calling release; if this is *inside* allocation - // then we won't be able to clear then after deallocating the memory. - PageReservation tmp; - std::swap(tmp, *this); - - ASSERT(tmp); - ASSERT(!*this); - - OSAllocator::releaseDecommitted(tmp.base(), tmp.size()); - } - -private: - PageReservation(void* base, size_t size, bool writable, bool executable, bool hasGuardPages) - : PageBlock(base, size, hasGuardPages) - , m_committed(0) - , m_writable(writable) - , m_executable(executable) - { - } - - size_t m_committed; - bool m_writable; - bool m_executable; -}; - -} - -using WTF::PageReservation; - -#endif // PageReservation_h diff --git a/3rdparty/masm/wtf/Platform.h b/3rdparty/masm/wtf/Platform.h deleted file mode 100644 index f2fd3b0ad5..0000000000 --- a/3rdparty/masm/wtf/Platform.h +++ /dev/null @@ -1,1212 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. - * Copyright (C) 2007-2009 Torch Mobile, Inc. - * Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_Platform_h -#define WTF_Platform_h - -/* Include compiler specific macros */ -#include - -/* ==== PLATFORM handles OS, operating environment, graphics API, and - CPU. This macro will be phased out in favor of platform adaptation - macros, policy decision macros, and top-level port definitions. ==== */ -#define PLATFORM(WTF_FEATURE) (defined WTF_PLATFORM_##WTF_FEATURE && WTF_PLATFORM_##WTF_FEATURE) - - -/* ==== Platform adaptation macros: these describe properties of the target environment. ==== */ - -/* CPU() - the target CPU architecture */ -#define CPU(WTF_FEATURE) (defined WTF_CPU_##WTF_FEATURE && WTF_CPU_##WTF_FEATURE) -/* HAVE() - specific system features (headers, functions or similar) that are present or not */ -#define HAVE(WTF_FEATURE) (defined HAVE_##WTF_FEATURE && HAVE_##WTF_FEATURE) -/* OS() - underlying operating system; only to be used for mandated low-level services like - virtual memory, not to choose a GUI toolkit */ -#define OS(WTF_FEATURE) (defined WTF_OS_##WTF_FEATURE && WTF_OS_##WTF_FEATURE) - - -/* ==== Policy decision macros: these define policy choices for a particular port. ==== */ - -/* USE() - use a particular third-party library or optional OS service */ -#define USE(WTF_FEATURE) (defined WTF_USE_##WTF_FEATURE && WTF_USE_##WTF_FEATURE) -/* ENABLE() - turn on a specific feature of WebKit */ -#define ENABLE(WTF_FEATURE) (defined ENABLE_##WTF_FEATURE && ENABLE_##WTF_FEATURE) - - -/* ==== CPU() - the target CPU architecture ==== */ - -/* This also defines CPU(BIG_ENDIAN) or CPU(MIDDLE_ENDIAN) or neither, as appropriate. */ - -/* CPU(ALPHA) - DEC Alpha */ -#if defined(__alpha__) -#define WTF_CPU_ALPHA 1 -#endif - -/* CPU(IA64) - Itanium / IA-64 */ -#if defined(__ia64__) -#define WTF_CPU_IA64 1 -/* 32-bit mode on Itanium */ -#if !defined(__LP64__) -#define WTF_CPU_IA64_32 1 -#endif -#endif - -/* CPU(MIPS) - MIPS 32-bit */ -/* Note: Only O32 ABI is tested, so we enable it for O32 ABI for now. */ -#if (defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_)) \ - && defined(_ABIO32) -#define WTF_CPU_MIPS 1 -#if defined(__MIPSEB__) -#define WTF_CPU_BIG_ENDIAN 1 -#endif -#define WTF_MIPS_PIC (defined __PIC__) -#define WTF_MIPS_ARCH __mips -#define WTF_MIPS_ISA(v) (defined WTF_MIPS_ARCH && WTF_MIPS_ARCH == v) -#define WTF_MIPS_ISA_AT_LEAST(v) (defined WTF_MIPS_ARCH && WTF_MIPS_ARCH >= v) -#define WTF_MIPS_ARCH_REV __mips_isa_rev -#define WTF_MIPS_ISA_REV(v) (defined WTF_MIPS_ARCH_REV && WTF_MIPS_ARCH_REV == v) -#define WTF_MIPS_DOUBLE_FLOAT (defined __mips_hard_float && !defined __mips_single_float) -#define WTF_MIPS_FP64 (defined __mips_fpr && __mips_fpr == 64) -/* MIPS requires allocators to use aligned memory */ -#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 -#endif /* MIPS */ - -/* CPU(PPC) - PowerPC 32-bit */ -#if defined(__ppc__) \ - || defined(__PPC__) \ - || defined(__powerpc__) \ - || defined(__powerpc) \ - || defined(__POWERPC__) \ - || defined(_M_PPC) \ - || defined(__PPC) -#define WTF_CPU_PPC 1 -#define WTF_CPU_BIG_ENDIAN 1 -#endif - -/* CPU(PPC64) - PowerPC 64-bit */ -#if defined(__ppc64__) \ - || defined(__PPC64__) -#define WTF_CPU_PPC64 1 -#define WTF_CPU_BIG_ENDIAN 1 -#endif - -/* CPU(SH4) - SuperH SH-4 */ -#if defined(__SH4__) -#define WTF_CPU_SH4 1 -#endif - -/* CPU(SPARC32) - SPARC 32-bit */ -#if defined(__sparc) && !defined(__arch64__) || defined(__sparcv8) -#define WTF_CPU_SPARC32 1 -#define WTF_CPU_BIG_ENDIAN 1 -#endif - -/* CPU(SPARC64) - SPARC 64-bit */ -#if defined(__sparc__) && defined(__arch64__) || defined (__sparcv9) -#define WTF_CPU_SPARC64 1 -#define WTF_CPU_BIG_ENDIAN 1 -#endif - -/* CPU(SPARC) - any SPARC, true for CPU(SPARC32) and CPU(SPARC64) */ -#if CPU(SPARC32) || CPU(SPARC64) -#define WTF_CPU_SPARC 1 -#endif - -/* CPU(S390X) - S390 64-bit */ -#if defined(__s390x__) -#define WTF_CPU_S390X 1 -#define WTF_CPU_BIG_ENDIAN 1 -#endif - -/* CPU(S390) - S390 32-bit */ -#if defined(__s390__) -#define WTF_CPU_S390 1 -#define WTF_CPU_BIG_ENDIAN 1 -#endif - -/* CPU(X86) - i386 / x86 32-bit */ -#if defined(__i386__) \ - || defined(i386) \ - || defined(_M_IX86) \ - || defined(_X86_) \ - || defined(__THW_INTEL) -#define WTF_CPU_X86 1 -#endif - -/* CPU(X86_64) - AMD64 / Intel64 / x86_64 64-bit */ -#if defined(__x86_64__) \ - || defined(_M_X64) -#define WTF_CPU_X86_64 1 -#endif - -/* CPU(ARM) - ARM, any version*/ -#if defined(arm) \ - || defined(__arm__) \ - || defined(ARM) \ - || defined(_ARM_) -#define WTF_CPU_ARM 1 - -#if defined(__ARM_PCS_VFP) -#define WTF_CPU_ARM_HARDFP 1 -#endif - -#if defined(__ARMEB__) || (COMPILER(RVCT) && defined(__BIG_ENDIAN)) -#define WTF_CPU_BIG_ENDIAN 1 - -#elif !defined(__ARM_EABI__) \ - && !defined(__EABI__) \ - && !defined(__VFP_FP__) \ - && !defined(_WIN32_WCE) \ - && !defined(ANDROID) -#define WTF_CPU_MIDDLE_ENDIAN 1 - -#endif - -#define WTF_ARM_ARCH_AT_LEAST(N) (CPU(ARM) && WTF_ARM_ARCH_VERSION >= N) - -/* Set WTF_ARM_ARCH_VERSION */ -#if defined(__ARM_ARCH_4__) \ - || defined(__ARM_ARCH_4T__) \ - || defined(__MARM_ARMV4__) \ - || defined(_ARMV4I_) -#define WTF_ARM_ARCH_VERSION 4 - -#elif defined(__ARM_ARCH_5__) \ - || defined(__ARM_ARCH_5T__) \ - || defined(__MARM_ARMV5__) -#define WTF_ARM_ARCH_VERSION 5 - -#elif defined(__ARM_ARCH_5E__) \ - || defined(__ARM_ARCH_5TE__) \ - || defined(__ARM_ARCH_5TEJ__) -#define WTF_ARM_ARCH_VERSION 5 -/*ARMv5TE requires allocators to use aligned memory*/ -#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 - -#elif defined(__ARM_ARCH_6__) \ - || defined(__ARM_ARCH_6J__) \ - || defined(__ARM_ARCH_6K__) \ - || defined(__ARM_ARCH_6Z__) \ - || defined(__ARM_ARCH_6ZK__) \ - || defined(__ARM_ARCH_6T2__) \ - || defined(__ARMV6__) -#define WTF_ARM_ARCH_VERSION 6 - -#elif defined(__ARM_ARCH_7A__) \ - || defined(__ARM_ARCH_7R__) -#define WTF_ARM_ARCH_VERSION 7 - -/* RVCT sets _TARGET_ARCH_ARM */ -#elif defined(__TARGET_ARCH_ARM) -#define WTF_ARM_ARCH_VERSION __TARGET_ARCH_ARM - -#if defined(__TARGET_ARCH_5E) \ - || defined(__TARGET_ARCH_5TE) \ - || defined(__TARGET_ARCH_5TEJ) -/*ARMv5TE requires allocators to use aligned memory*/ -#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 -#endif - -#else -#define WTF_ARM_ARCH_VERSION 0 - -#endif - -/* Set WTF_THUMB_ARCH_VERSION */ -#if defined(__ARM_ARCH_4T__) -#define WTF_THUMB_ARCH_VERSION 1 - -#elif defined(__ARM_ARCH_5T__) \ - || defined(__ARM_ARCH_5TE__) \ - || defined(__ARM_ARCH_5TEJ__) -#define WTF_THUMB_ARCH_VERSION 2 - -#elif defined(__ARM_ARCH_6J__) \ - || defined(__ARM_ARCH_6K__) \ - || defined(__ARM_ARCH_6Z__) \ - || defined(__ARM_ARCH_6ZK__) \ - || defined(__ARM_ARCH_6M__) -#define WTF_THUMB_ARCH_VERSION 3 - -#elif defined(__ARM_ARCH_6T2__) \ - || defined(__ARM_ARCH_7__) \ - || defined(__ARM_ARCH_7A__) \ - || defined(__ARM_ARCH_7R__) \ - || defined(__ARM_ARCH_7M__) -#define WTF_THUMB_ARCH_VERSION 4 - -/* RVCT sets __TARGET_ARCH_THUMB */ -#elif defined(__TARGET_ARCH_THUMB) -#define WTF_THUMB_ARCH_VERSION __TARGET_ARCH_THUMB - -#else -#define WTF_THUMB_ARCH_VERSION 0 -#endif - - -/* CPU(ARMV5_OR_LOWER) - ARM instruction set v5 or earlier */ -/* On ARMv5 and below the natural alignment is required. - And there are some other differences for v5 or earlier. */ -#if !defined(ARMV5_OR_LOWER) && !WTF_ARM_ARCH_AT_LEAST(6) -#define WTF_CPU_ARMV5_OR_LOWER 1 -#endif - - -/* CPU(ARM_TRADITIONAL) - Thumb2 is not available, only traditional ARM (v4 or greater) */ -/* CPU(ARM_THUMB2) - Thumb2 instruction set is available */ -/* Only one of these will be defined. */ -#if !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) -# if defined(thumb2) || defined(__thumb2__) \ - || ((defined(__thumb) || defined(__thumb__)) && WTF_THUMB_ARCH_VERSION == 4) -# define WTF_CPU_ARM_TRADITIONAL 0 -# define WTF_CPU_ARM_THUMB2 1 -# elif WTF_ARM_ARCH_AT_LEAST(4) -# define WTF_CPU_ARM_TRADITIONAL 1 -# define WTF_CPU_ARM_THUMB2 0 -# else -# error "Not supported ARM architecture" -# endif -#elif CPU(ARM_TRADITIONAL) && CPU(ARM_THUMB2) /* Sanity Check */ -# error "Cannot use both of WTF_CPU_ARM_TRADITIONAL and WTF_CPU_ARM_THUMB2 platforms" -#endif /* !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) */ - -#if defined(__ARM_NEON__) && !defined(WTF_CPU_ARM_NEON) -#define WTF_CPU_ARM_NEON 1 -#endif - -#if CPU(ARM_NEON) && (!COMPILER(GCC) || GCC_VERSION_AT_LEAST(4, 7, 0)) -// All NEON intrinsics usage can be disabled by this macro. -#define HAVE_ARM_NEON_INTRINSICS 1 -#endif - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) -#define WTF_CPU_ARM_VFP 1 -#endif - -#if defined(__ARM_ARCH_7S__) -#define WTF_CPU_APPLE_ARMV7S 1 -#endif - -#endif /* ARM */ - -#if CPU(ARM) || CPU(MIPS) || CPU(SH4) || CPU(SPARC) -#define WTF_CPU_NEEDS_ALIGNED_ACCESS 1 -#endif - -/* ==== OS() - underlying operating system; only to be used for mandated low-level services like - virtual memory, not to choose a GUI toolkit ==== */ - -/* OS(ANDROID) - Android */ -#ifdef ANDROID -#define WTF_OS_ANDROID 1 -#endif - -/* OS(AIX) - AIX */ -#ifdef _AIX -#define WTF_OS_AIX 1 -#endif - -/* OS(DARWIN) - Any Darwin-based OS, including Mac OS X and iPhone OS */ -#ifdef __APPLE__ -#define WTF_OS_DARWIN 1 - -#include -#include -#include -#endif - -/* OS(IOS) - iOS */ -/* OS(MAC_OS_X) - Mac OS X (not including iOS) */ -#if OS(DARWIN) && ((defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED) \ - || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) \ - || (defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR)) -#define WTF_OS_IOS 1 -#elif OS(DARWIN) && defined(TARGET_OS_MAC) && TARGET_OS_MAC -#define WTF_OS_MAC_OS_X 1 - -/* FIXME: These can be removed after sufficient time has passed since the removal of BUILDING_ON / TARGETING macros. */ - -#define ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED 0 / 0 -#define ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED 0 / 0 - -#define BUILDING_ON_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED -#define BUILDING_ON_SNOW_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED -#define BUILDING_ON_LION ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED - -#define TARGETING_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED -#define TARGETING_SNOW_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED -#define TARGETING_LION ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED -#endif - -/* OS(FREEBSD) - FreeBSD */ -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) -#define WTF_OS_FREEBSD 1 -#endif - -/* OS(HURD) - GNU/Hurd */ -#ifdef __GNU__ -#define WTF_OS_HURD 1 -#endif - -/* OS(LINUX) - Linux */ -#ifdef __linux__ -#define WTF_OS_LINUX 1 -#endif - -/* OS(NETBSD) - NetBSD */ -#if defined(__NetBSD__) -#define WTF_OS_NETBSD 1 -#endif - -/* OS(OPENBSD) - OpenBSD */ -#ifdef __OpenBSD__ -#define WTF_OS_OPENBSD 1 -#endif - -/* OS(QNX) - QNX */ -#if defined(__QNXNTO__) -#define WTF_OS_QNX 1 -#endif - -/* OS(SOLARIS) - Solaris */ -#if defined(sun) || defined(__sun) -#define WTF_OS_SOLARIS 1 -#endif - -/* OS(WINCE) - Windows CE; note that for this platform OS(WINDOWS) is also defined */ -#if defined(_WIN32_WCE) -#define WTF_OS_WINCE 1 -#endif - -/* OS(WINDOWS) - Any version of Windows */ -#if defined(WIN32) || defined(_WIN32) -#define WTF_OS_WINDOWS 1 -#endif - -#define WTF_OS_WIN ERROR "USE WINDOWS WITH OS NOT WIN" -#define WTF_OS_MAC ERROR "USE MAC_OS_X WITH OS NOT MAC" - -/* OS(UNIX) - Any Unix-like system */ -#if OS(AIX) \ - || OS(ANDROID) \ - || OS(DARWIN) \ - || OS(FREEBSD) \ - || OS(HURD) \ - || OS(LINUX) \ - || OS(NETBSD) \ - || OS(OPENBSD) \ - || OS(QNX) \ - || OS(SOLARIS) \ - || defined(unix) \ - || defined(__unix) \ - || defined(__unix__) -#define WTF_OS_UNIX 1 -#endif - -/* Operating environments */ - -/* FIXME: these are all mixes of OS, operating environment and policy choices. */ -/* PLATFORM(CHROMIUM) */ -/* PLATFORM(QT) */ -/* PLATFORM(WX) */ -/* PLATFORM(EFL) */ -/* PLATFORM(GTK) */ -/* PLATFORM(BLACKBERRY) */ -/* PLATFORM(MAC) */ -/* PLATFORM(WIN) */ -#if defined(BUILDING_CHROMIUM__) -#define WTF_PLATFORM_CHROMIUM 1 -#elif defined(BUILDING_QT__) -#define WTF_PLATFORM_QT 1 -#elif defined(BUILDING_WX__) -#define WTF_PLATFORM_WX 1 -#elif defined(BUILDING_EFL__) -#define WTF_PLATFORM_EFL 1 -#elif defined(BUILDING_GTK__) -#define WTF_PLATFORM_GTK 1 -#elif defined(BUILDING_BLACKBERRY__) -#define WTF_PLATFORM_BLACKBERRY 1 -#elif OS(DARWIN) -#define WTF_PLATFORM_MAC 1 -#elif OS(WINDOWS) -#define WTF_PLATFORM_WIN 1 -#endif - -/* PLATFORM(IOS) */ -/* FIXME: this is sometimes used as an OS switch and sometimes for higher-level things */ -#if (defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) -#define WTF_PLATFORM_IOS 1 -#endif - -/* PLATFORM(IOS_SIMULATOR) */ -#if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR -#define WTF_PLATFORM_IOS 1 -#define WTF_PLATFORM_IOS_SIMULATOR 1 -#endif - -/* Graphics engines */ - -/* USE(CG) and PLATFORM(CI) */ -#if PLATFORM(MAC) || PLATFORM(IOS) -#define WTF_USE_CG 1 -#endif -#if PLATFORM(MAC) || PLATFORM(IOS) || (PLATFORM(WIN) && USE(CG)) -#define WTF_USE_CA 1 -#endif - -/* USE(SKIA) for Win/Linux/Mac/Android */ -#if PLATFORM(CHROMIUM) -#if OS(DARWIN) -#define WTF_USE_SKIA 1 -#define WTF_USE_ICCJPEG 1 -#define WTF_USE_QCMSLIB 1 -#elif OS(ANDROID) -#define WTF_USE_SKIA 1 -#define WTF_USE_LOW_QUALITY_IMAGE_INTERPOLATION 1 -#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_DITHERING 1 -#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_FANCY_UPSAMPLING 1 -#else -#define WTF_USE_SKIA 1 -#define WTF_USE_ICCJPEG 1 -#define WTF_USE_QCMSLIB 1 -#endif -#endif - -#if OS(QNX) -#define USE_SYSTEM_MALLOC 1 -#endif - -#if PLATFORM(BLACKBERRY) -#define WTF_USE_MERSENNE_TWISTER_19937 1 -#define WTF_USE_SKIA 1 -#define WTF_USE_LOW_QUALITY_IMAGE_INTERPOLATION 1 -#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_DITHERING 1 -#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_FANCY_UPSAMPLING 1 -#endif - -#if PLATFORM(GTK) -#define WTF_USE_CAIRO 1 -#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 -#endif - - -#if OS(WINCE) -#define WTF_USE_MERSENNE_TWISTER_19937 1 -#endif - -/* On Windows, use QueryPerformanceCounter by default */ -#if OS(WINDOWS) -#define WTF_USE_QUERY_PERFORMANCE_COUNTER 1 -#endif - -#if OS(WINCE) && !PLATFORM(QT) -#define NOSHLWAPI /* shlwapi.h not available on WinCe */ - -/* MSDN documentation says these functions are provided with uspce.lib. But we cannot find this file. */ -#define __usp10__ /* disable "usp10.h" */ - -#define _INC_ASSERT /* disable "assert.h" */ -#define assert(x) - -#endif /* OS(WINCE) && !PLATFORM(QT) */ - -#if OS(WINCE) && !PLATFORM(QT) -#define WTF_USE_WCHAR_UNICODE 1 -#elif PLATFORM(GTK) -/* The GTK+ Unicode backend is configurable */ -#else -#define WTF_USE_ICU_UNICODE 1 -#endif - -#if PLATFORM(MAC) && !PLATFORM(IOS) -#if CPU(X86_64) -#define WTF_USE_PLUGIN_HOST_PROCESS 1 -#endif -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 -#define ENABLE_GESTURE_EVENTS 1 -#define ENABLE_RUBBER_BANDING 1 -#define WTF_USE_SCROLLBAR_PAINTER 1 -#define HAVE_XPC 1 -#endif -#if !defined(ENABLE_DASHBOARD_SUPPORT) -#define ENABLE_DASHBOARD_SUPPORT 1 -#endif -#define WTF_USE_CF 1 -#define WTF_USE_PTHREADS 1 -#define HAVE_READLINE 1 -#define HAVE_RUNLOOP_TIMER 1 -#define ENABLE_FULLSCREEN_API 1 -#define ENABLE_SMOOTH_SCROLLING 1 -#define ENABLE_WEB_ARCHIVE 1 -#define ENABLE_WEB_AUDIO 1 -#if defined(ENABLE_VIDEO) -#define ENABLE_VIDEO_TRACK 1 -#endif -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 -#define HAVE_LAYER_HOSTING_IN_WINDOW_SERVER 1 -#endif -#define WTF_USE_APPKIT 1 -#define WTF_USE_SECURITY_FRAMEWORK 1 -#endif /* PLATFORM(MAC) && !PLATFORM(IOS) */ - -#if PLATFORM(CHROMIUM) && OS(DARWIN) -#define WTF_USE_CF 1 -#define WTF_USE_PTHREADS 1 -#define WTF_USE_WK_SCROLLBAR_PAINTER 1 -#endif - -#if PLATFORM(CHROMIUM) -#if OS(DARWIN) -/* We can't override the global operator new and delete on OS(DARWIN) because - * some object are allocated by WebKit and deallocated by the embedder. */ -#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 -#else /* !OS(DARWIN) */ -/* On non-OS(DARWIN), the "system malloc" is actually TCMalloc anyway, so there's - * no need to use WebKit's copy of TCMalloc. */ -#define USE_SYSTEM_MALLOC 1 -#endif /* OS(DARWIN) */ -#endif /* PLATFORM(CHROMIUM) */ - -#if PLATFORM(IOS) -#define DONT_FINALIZE_ON_MAIN_THREAD 1 -#endif - -#if PLATFORM(QT) && OS(DARWIN) -#define WTF_USE_CF 1 -#endif - -#if OS(DARWIN) && !PLATFORM(GTK) && !PLATFORM(QT) -#define ENABLE_PURGEABLE_MEMORY 1 -#endif - -#if PLATFORM(IOS) -#define ENABLE_CONTEXT_MENUS 0 -#define ENABLE_DRAG_SUPPORT 0 -#define ENABLE_GEOLOCATION 1 -#define ENABLE_ICONDATABASE 0 -#define ENABLE_INSPECTOR 1 -#define ENABLE_NETSCAPE_PLUGIN_API 0 -#define ENABLE_ORIENTATION_EVENTS 1 -#define ENABLE_REPAINT_THROTTLING 1 -#define ENABLE_WEB_ARCHIVE 1 -#define HAVE_READLINE 1 -#define WTF_USE_CF 1 -#define WTF_USE_CFNETWORK 1 -#define WTF_USE_NETWORK_CFDATA_ARRAY_CALLBACK 1 -#define WTF_USE_PTHREADS 1 - -#if PLATFORM(IOS_SIMULATOR) - #define ENABLE_JIT 0 - #define ENABLE_YARR_JIT 0 -#else - #define ENABLE_JIT 1 - #define ENABLE_LLINT 1 - #define ENABLE_YARR_JIT 1 -#endif - -#define WTF_USE_APPKIT 0 -#define WTF_USE_SECURITY_FRAMEWORK 0 -#endif - -#if PLATFORM(WIN) && !OS(WINCE) -#define WTF_USE_CF 1 -#endif - -#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(WIN_CAIRO) -#define WTF_USE_CFNETWORK 1 -#endif - -#if USE(CFNETWORK) || PLATFORM(MAC) || PLATFORM(IOS) -#define WTF_USE_CFURLCACHE 1 -#endif - -#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(QT) -#define ENABLE_WEB_ARCHIVE 1 -#endif - -#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(WIN_CAIRO) && !PLATFORM(QT) -#define ENABLE_FULLSCREEN_API 1 -#endif - -#if PLATFORM(WX) -#if !CPU(PPC) -#if !defined(ENABLE_ASSEMBLER) -#define ENABLE_ASSEMBLER 1 -#endif -#define ENABLE_JIT 1 -#endif -#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 -#define ENABLE_LLINT 0 -#if OS(DARWIN) -#define WTF_USE_CF 1 -#define ENABLE_WEB_ARCHIVE 1 -#endif -#endif - -#if OS(UNIX) && (PLATFORM(GTK) || PLATFORM(QT)) -#define WTF_USE_PTHREADS 1 -#endif - -#if !defined(HAVE_ACCESSIBILITY) -#if PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && !OS(ANDROID)) || PLATFORM(EFL) -#define HAVE_ACCESSIBILITY 1 -#endif -#endif /* !defined(HAVE_ACCESSIBILITY) */ - -#if OS(UNIX) -#define HAVE_SIGNAL_H 1 -#define WTF_USE_OS_RANDOMNESS 1 -#endif - -#if (OS(FREEBSD) || OS(OPENBSD)) && !defined(__GLIBC__) -#define HAVE_PTHREAD_NP_H 1 -#endif - -#if !defined(HAVE_VASPRINTF) -#if !COMPILER(MSVC) && !COMPILER(RVCT) && !COMPILER(MINGW) && !(COMPILER(GCC) && OS(QNX)) -#define HAVE_VASPRINTF 1 -#endif -#endif - -#if !defined(HAVE_STRNSTR) -#if OS(DARWIN) || (OS(FREEBSD) && !defined(__GLIBC__)) -#define HAVE_STRNSTR 1 -#endif -#endif - -#if !OS(WINDOWS) && !OS(SOLARIS) \ - && !OS(RVCT) \ - && !OS(ANDROID) -#define HAVE_TM_GMTOFF 1 -#define HAVE_TM_ZONE 1 -#define HAVE_TIMEGM 1 -#endif - -#if OS(DARWIN) - -#define HAVE_ERRNO_H 1 -#define HAVE_LANGINFO_H 1 -#define HAVE_MMAP 1 -#define HAVE_MERGESORT 1 -#define HAVE_STRINGS_H 1 -#define HAVE_SYS_PARAM_H 1 -#define HAVE_SYS_TIME_H 1 -#define HAVE_SYS_TIMEB_H 1 -#define WTF_USE_ACCELERATE 1 - -#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 - -#define HAVE_DISPATCH_H 1 -#define HAVE_HOSTED_CORE_ANIMATION 1 - -#if !PLATFORM(IOS) -#define HAVE_MADV_FREE_REUSE 1 -#define HAVE_MADV_FREE 1 -#define HAVE_PTHREAD_SETNAME_NP 1 -#endif - -#endif - -#if PLATFORM(IOS) -#define HAVE_MADV_FREE 1 -#define HAVE_PTHREAD_SETNAME_NP 1 -#endif - -#elif OS(WINDOWS) - -#if !OS(WINCE) -#define HAVE_SYS_TIMEB_H 1 -#define HAVE_ALIGNED_MALLOC 1 -#define HAVE_ISDEBUGGERPRESENT 1 -#endif -#define HAVE_VIRTUALALLOC 1 -#define WTF_USE_OS_RANDOMNESS 1 - -#elif OS(QNX) - -#define HAVE_ERRNO_H 1 -#define HAVE_MMAP 1 -#define HAVE_MADV_FREE_REUSE 1 -#define HAVE_MADV_FREE 1 -#define HAVE_STRINGS_H 1 -#define HAVE_SYS_PARAM_H 1 -#define HAVE_SYS_TIME_H 1 -#define WTF_USE_PTHREADS 1 - -#elif OS(ANDROID) - -#define HAVE_ERRNO_H 1 -#define HAVE_NMAP 1 -#define HAVE_STRINGS_H 1 -#define HAVE_SYS_PARAM_H 1 -#define HAVE_SYS_TIME_H 1 - -#else - -/* FIXME: is this actually used or do other platforms generate their own config.h? */ - -#define HAVE_ERRNO_H 1 -#define HAVE_LANGINFO_H 1 -#define HAVE_MMAP 1 -#define HAVE_STRINGS_H 1 -#define HAVE_SYS_PARAM_H 1 -#define HAVE_SYS_TIME_H 1 - -#endif - -/* ENABLE macro defaults */ - -#if PLATFORM(QT) -/* We must not customize the global operator new and delete for the Qt port. */ -#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 -#if !OS(UNIX) -#define USE_SYSTEM_MALLOC 1 -#endif -#endif - -#if !defined(ENABLE_ICONDATABASE) -#define ENABLE_ICONDATABASE 1 -#endif - -#if !defined(ENABLE_SQL_DATABASE) -#define ENABLE_SQL_DATABASE 1 -#endif - -#if !defined(ENABLE_JAVASCRIPT_DEBUGGER) -#define ENABLE_JAVASCRIPT_DEBUGGER 1 -#endif - -#if !defined(ENABLE_FTPDIR) -#define ENABLE_FTPDIR 1 -#endif - -#if !defined(ENABLE_CONTEXT_MENUS) -#define ENABLE_CONTEXT_MENUS 1 -#endif - -#if !defined(ENABLE_DRAG_SUPPORT) -#define ENABLE_DRAG_SUPPORT 1 -#endif - -#if !defined(ENABLE_INSPECTOR) -#define ENABLE_INSPECTOR 1 -#endif - -#if !defined(ENABLE_NETSCAPE_PLUGIN_API) -#define ENABLE_NETSCAPE_PLUGIN_API 1 -#endif - -#if !defined(ENABLE_GLOBAL_FASTMALLOC_NEW) -#define ENABLE_GLOBAL_FASTMALLOC_NEW 1 -#endif - -#if !defined(ENABLE_PARSED_STYLE_SHEET_CACHING) -#define ENABLE_PARSED_STYLE_SHEET_CACHING 1 -#endif - -#if !defined(ENABLE_SUBPIXEL_LAYOUT) -#if PLATFORM(CHROMIUM) -#define ENABLE_SUBPIXEL_LAYOUT 1 -#else -#define ENABLE_SUBPIXEL_LAYOUT 0 -#endif -#endif - -#if !defined(ENABLE_SATURATED_LAYOUT_ARITHMETIC) -#define ENABLE_SATURATED_LAYOUT_ARITHMETIC 0 -#endif - -#if ENABLE(ENABLE_SATURATED_LAYOUT_ARITHMETIC) && !ENABLE(ENABLE_SUBPIXEL_LAYOUT) -#error "ENABLE_SATURATED_LAYOUT_ARITHMETIC requires ENABLE_SUBPIXEL_LAYOUT" -#endif - -#if ENABLE(INPUT_TYPE_DATE) || ENABLE(INPUT_TYPE_DATETIME) || ENABLE(INPUT_TYPE_DATETIMELOCAL) || ENABLE(INPUT_TYPE_MONTH) || ENABLE(INPUT_TYPE_TIME) || ENABLE(INPUT_TYPE_WEEK) -#define ENABLE_DATE_AND_TIME_INPUT_TYPES 1 -#endif - -#define ENABLE_DEBUG_WITH_BREAKPOINT 0 -#define ENABLE_SAMPLING_COUNTERS 0 -#define ENABLE_SAMPLING_FLAGS 0 -#define ENABLE_SAMPLING_REGIONS 0 -#define ENABLE_OPCODE_SAMPLING 0 -#define ENABLE_CODEBLOCK_SAMPLING 0 -#if ENABLE(CODEBLOCK_SAMPLING) && !ENABLE(OPCODE_SAMPLING) -#error "CODEBLOCK_SAMPLING requires OPCODE_SAMPLING" -#endif -#if ENABLE(OPCODE_SAMPLING) || ENABLE(SAMPLING_FLAGS) || ENABLE(SAMPLING_REGIONS) -#define ENABLE_SAMPLING_THREAD 1 -#endif - -#if !defined(ENABLE_TEXT_CARET) && !PLATFORM(IOS) -#define ENABLE_TEXT_CARET 1 -#endif - -#if !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32_64) -#if (CPU(X86_64) && (OS(UNIX) || OS(WINDOWS))) \ - || (CPU(IA64) && !CPU(IA64_32)) \ - || CPU(ALPHA) \ - || CPU(SPARC64) \ - || CPU(S390X) \ - || CPU(PPC64) -#define WTF_USE_JSVALUE64 1 -#else -#define WTF_USE_JSVALUE32_64 1 -#endif -#endif /* !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32_64) */ - -/* Disable the JIT on versions of GCC prior to 4.1 */ -#if !defined(ENABLE_JIT) && COMPILER(GCC) && !GCC_VERSION_AT_LEAST(4, 1, 0) -#define ENABLE_JIT 0 -#endif - -/* JIT is not implemented for Windows 64-bit */ -#if !defined(ENABLE_JIT) && OS(WINDOWS) && CPU(X86_64) -#define ENABLE_JIT 0 -#define ENABLE_YARR_JIT 0 -#endif - -#if !defined(ENABLE_JIT) && CPU(SH4) && PLATFORM(QT) -#define ENABLE_JIT 1 -#endif - -/* The JIT is enabled by default on all x86, x86-64, ARM & MIPS platforms. */ -#if !defined(ENABLE_JIT) \ - && (CPU(X86) || CPU(X86_64) || CPU(ARM) || CPU(MIPS)) \ - && (OS(DARWIN) || !COMPILER(GCC) || GCC_VERSION_AT_LEAST(4, 1, 0)) \ - && !OS(WINCE) \ - && !(OS(QNX) && !PLATFORM(QT)) /* We use JIT in QNX Qt */ -#define ENABLE_JIT 1 -#endif - -/* If possible, try to enable a disassembler. This is optional. We proceed in two - steps: first we try to find some disassembler that we can use, and then we - decide if the high-level disassembler API can be enabled. */ -#if !defined(WTF_USE_UDIS86) && ENABLE(JIT) && (PLATFORM(MAC) || (PLATFORM(QT) && OS(LINUX))) \ - && (CPU(X86) || CPU(X86_64)) -#define WTF_USE_UDIS86 1 -#endif - -#if !defined(ENABLE_DISASSEMBLER) && USE(UDIS86) -#define ENABLE_DISASSEMBLER 1 -#endif - -/* On the GTK+ port we take an extra precaution for LLINT support: - * We disable it on x86 builds if the build target doesn't support SSE2 - * instructions (LLINT requires SSE2 on this platform). */ -#if !defined(ENABLE_LLINT) && PLATFORM(GTK) && CPU(X86) && COMPILER(GCC) \ - && !defined(__SSE2__) -#define ENABLE_LLINT 0 -#endif - -/* On some of the platforms where we have a JIT, we want to also have the - low-level interpreter. */ -#if !defined(ENABLE_LLINT) \ - && ENABLE(JIT) \ - && (OS(DARWIN) || OS(LINUX)) \ - && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(QT)) \ - && (CPU(X86) || CPU(X86_64) || CPU(ARM_THUMB2)) -#define ENABLE_LLINT 1 -#endif - -#if !defined(ENABLE_DFG_JIT) && ENABLE(JIT) && !COMPILER(MSVC) -/* Enable the DFG JIT on X86 and X86_64. Only tested on Mac and GNU/Linux. */ -#if (CPU(X86) || CPU(X86_64)) && (PLATFORM(MAC) || OS(LINUX)) -#define ENABLE_DFG_JIT 1 -#endif -/* Enable the DFG JIT on ARMv7. Only tested on iOS and Qt Linux. */ -#if CPU(ARM_THUMB2) && (PLATFORM(IOS) || PLATFORM(BLACKBERRY) || PLATFORM(QT)) -#define ENABLE_DFG_JIT 1 -#endif -/* Enable the DFG JIT on ARM. */ -#if CPU(ARM_TRADITIONAL) -#define ENABLE_DFG_JIT 1 -#endif -#endif - -/* If the jit is not available, enable the LLInt C Loop: */ -#if !ENABLE(JIT) -#undef ENABLE_LLINT /* Undef so that we can redefine it. */ -#undef ENABLE_LLINT_C_LOOP /* Undef so that we can redefine it. */ -#undef ENABLE_DFG_JIT /* Undef so that we can redefine it. */ -#define ENABLE_LLINT 1 -#define ENABLE_LLINT_C_LOOP 1 -#define ENABLE_DFG_JIT 0 -#endif - -/* Do a sanity check to make sure that we at least have one execution engine in - use: */ -#if !(ENABLE(JIT) || ENABLE(LLINT)) -#error You have to have at least one execution model enabled to build JSC -#endif - -/* Profiling of types and values used by JIT code. DFG_JIT depends on it, but you - can enable it manually with DFG turned off if you want to use it as a standalone - profiler. In that case, you probably want to also enable VERBOSE_VALUE_PROFILE - below. */ -#if !defined(ENABLE_VALUE_PROFILER) && ENABLE(DFG_JIT) -#define ENABLE_VALUE_PROFILER 1 -#endif - -#if !defined(ENABLE_VERBOSE_VALUE_PROFILE) && ENABLE(VALUE_PROFILER) -#define ENABLE_VERBOSE_VALUE_PROFILE 0 -#endif - -#if !defined(ENABLE_SIMPLE_HEAP_PROFILING) -#define ENABLE_SIMPLE_HEAP_PROFILING 0 -#endif - -/* Counts uses of write barriers using sampling counters. Be sure to also - set ENABLE_SAMPLING_COUNTERS to 1. */ -#if !defined(ENABLE_WRITE_BARRIER_PROFILING) -#define ENABLE_WRITE_BARRIER_PROFILING 0 -#endif - -/* Configure the JIT */ -#if CPU(X86) && COMPILER(MSVC) -#define JSC_HOST_CALL __fastcall -#elif CPU(X86) && COMPILER(GCC) -#define JSC_HOST_CALL __attribute__ ((fastcall)) -#else -#define JSC_HOST_CALL -#endif - -/* Configure the interpreter */ -#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(4, 0, 0, 0) && defined(__GNUC__)) -#define HAVE_COMPUTED_GOTO 1 -#endif - -/* Determine if we need to enable Computed Goto Opcodes or not: */ -#if HAVE(COMPUTED_GOTO) && ENABLE(LLINT) -#define ENABLE_COMPUTED_GOTO_OPCODES 1 -#endif - -/* Regular Expression Tracing - Set to 1 to trace RegExp's in jsc. Results dumped at exit */ -#define ENABLE_REGEXP_TRACING 0 - -/* Yet Another Regex Runtime - turned on by default for JIT enabled ports. */ -#if !defined(ENABLE_YARR_JIT) && (ENABLE(JIT) || ENABLE(LLINT_C_LOOP)) && !PLATFORM(CHROMIUM) && !(OS(QNX) && PLATFORM(QT)) -#define ENABLE_YARR_JIT 1 - -/* Setting this flag compares JIT results with interpreter results. */ -#define ENABLE_YARR_JIT_DEBUG 0 -#endif - -/* If either the JIT or the RegExp JIT is enabled, then the Assembler must be - enabled as well: */ -#if ENABLE(JIT) || ENABLE(YARR_JIT) -#if defined(ENABLE_ASSEMBLER) && !ENABLE_ASSEMBLER -#error "Cannot enable the JIT or RegExp JIT without enabling the Assembler" -#else -#undef ENABLE_ASSEMBLER -#define ENABLE_ASSEMBLER 1 -#endif -#endif - -/* Pick which allocator to use; we only need an executable allocator if the assembler is compiled in. - On x86-64 we use a single fixed mmap, on other platforms we mmap on demand. */ -#if ENABLE(ASSEMBLER) -#if CPU(X86_64) || PLATFORM(IOS) -#define ENABLE_EXECUTABLE_ALLOCATOR_FIXED 1 -#else -#define ENABLE_EXECUTABLE_ALLOCATOR_DEMAND 1 -#endif -#endif - -#if !defined(ENABLE_PAN_SCROLLING) && OS(WINDOWS) -#define ENABLE_PAN_SCROLLING 1 -#endif - -/*Add other platforms as they update their platfrom specific code to handle TextRun's with 8 bit data. */ -#if PLATFORM(MAC) -#define ENABLE_8BIT_TEXTRUN 1 -#endif - -/* Use the QXmlStreamReader implementation for XMLDocumentParser */ -/* Use the QXmlQuery implementation for XSLTProcessor */ -#if PLATFORM(QT) -#if !USE(LIBXML2) -#define WTF_USE_QXMLSTREAM 1 -#define WTF_USE_QXMLQUERY 1 -#endif -#endif - -/* Accelerated compositing */ -#if PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(QT) || (PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(WIN_CAIRO)) -#define WTF_USE_ACCELERATED_COMPOSITING 1 -#endif - -#if PLATFORM(MAC) || PLATFORM(IOS) -#define ENABLE_CSS_IMAGE_SET 1 -#endif - -#if ENABLE(WEBGL) && !defined(WTF_USE_3D_GRAPHICS) -#define WTF_USE_3D_GRAPHICS 1 -#endif - -/* Qt always uses Texture Mapper */ -#if PLATFORM(QT) -#define WTF_USE_TEXTURE_MAPPER 1 -#endif - -#if USE(TEXTURE_MAPPER) && USE(3D_GRAPHICS) && !defined(WTF_USE_TEXTURE_MAPPER_GL) -#define WTF_USE_TEXTURE_MAPPER_GL 1 -#endif - -/* Compositing on the UI-process in WebKit2 */ -#if PLATFORM(QT) -#define WTF_USE_COORDINATED_GRAPHICS 1 -#endif - -#if PLATFORM(MAC) || PLATFORM(IOS) -#define WTF_USE_PROTECTION_SPACE_AUTH_CALLBACK 1 -#endif - -#if !ENABLE(NETSCAPE_PLUGIN_API) || (ENABLE(NETSCAPE_PLUGIN_API) && ((OS(UNIX) && (PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(WX))) || PLATFORM(EFL))) -#define ENABLE_PLUGIN_PACKAGE_SIMPLE_HASH 1 -#endif - -#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 -#define ENABLE_THREADED_SCROLLING 1 -#endif - -/* Set up a define for a common error that is intended to cause a build error -- thus the space after Error. */ -#define WTF_PLATFORM_CFNETWORK Error USE_macro_should_be_used_with_CFNETWORK - -/* FIXME: Eventually we should enable this for all platforms and get rid of the define. */ -#if PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL) -#define WTF_USE_PLATFORM_STRATEGIES 1 -#endif - -#if PLATFORM(WIN) -#define WTF_USE_CROSS_PLATFORM_CONTEXT_MENUS 1 -#endif - -#if PLATFORM(MAC) && HAVE(ACCESSIBILITY) -#define WTF_USE_ACCESSIBILITY_CONTEXT_MENUS 1 -#endif - -#if CPU(ARM_THUMB2) -#define ENABLE_BRANCH_COMPACTION 1 -#endif - -#if !defined(ENABLE_THREADING_LIBDISPATCH) && HAVE(DISPATCH_H) -#define ENABLE_THREADING_LIBDISPATCH 1 -#elif !defined(ENABLE_THREADING_OPENMP) && defined(_OPENMP) -#define ENABLE_THREADING_OPENMP 1 -#elif !defined(THREADING_GENERIC) -#define ENABLE_THREADING_GENERIC 1 -#endif - -#if ENABLE(GLIB_SUPPORT) -#include -#endif - -/* FIXME: This define won't be needed once #27551 is fully landed. However, - since most ports try to support sub-project independence, adding new headers - to WTF causes many ports to break, and so this way we can address the build - breakages one port at a time. */ -#if !defined(WTF_USE_EXPORT_MACROS) && (PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(WX)) -#define WTF_USE_EXPORT_MACROS 1 -#endif - -#if !defined(WTF_USE_EXPORT_MACROS_FOR_TESTING) && (PLATFORM(GTK) || PLATFORM(WIN)) -#define WTF_USE_EXPORT_MACROS_FOR_TESTING 1 -#endif - -#if (PLATFORM(QT) && !OS(DARWIN) && !OS(WINDOWS)) || PLATFORM(GTK) || PLATFORM(EFL) -#define WTF_USE_UNIX_DOMAIN_SOCKETS 1 -#endif - -#if !defined(ENABLE_COMPARE_AND_SWAP) && (OS(WINDOWS) || (COMPILER(GCC) && (CPU(X86) || CPU(X86_64) || CPU(ARM_THUMB2)))) -#define ENABLE_COMPARE_AND_SWAP 1 -#endif - -#define ENABLE_OBJECT_MARK_LOGGING 0 - -#if !defined(ENABLE_PARALLEL_GC) && !ENABLE(OBJECT_MARK_LOGGING) && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(BLACKBERRY) || PLATFORM(GTK)) && ENABLE(COMPARE_AND_SWAP) -#define ENABLE_PARALLEL_GC 1 -#elif PLATFORM(QT) -// Parallel GC is temporarily disabled on Qt because of regular crashes, see https://bugs.webkit.org/show_bug.cgi?id=90957 for details -#define ENABLE_PARALLEL_GC 0 -#endif - -#if !defined(ENABLE_GC_VALIDATION) && !defined(NDEBUG) -#define ENABLE_GC_VALIDATION 1 -#endif - -#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 -#define WTF_USE_AVFOUNDATION 1 -#endif - -#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 -#define WTF_USE_COREMEDIA 1 -#endif - -#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 -#define HAVE_AVFOUNDATION_TEXT_TRACK_SUPPORT 1 -#endif - -#if PLATFORM(MAC) || PLATFORM(GTK) || PLATFORM(EFL) || (PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(WIN_CAIRO)) || PLATFORM(BLACKBERRY) -#define WTF_USE_REQUEST_ANIMATION_FRAME_TIMER 1 -#endif - -#if PLATFORM(MAC) || PLATFORM(BLACKBERRY) -#define WTF_USE_REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR 1 -#endif - -#if PLATFORM(MAC) && (PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) -#define HAVE_INVERTED_WHEEL_EVENTS 1 -#endif - -#if PLATFORM(MAC) -#define WTF_USE_COREAUDIO 1 -#endif - -#if !defined(WTF_USE_V8) && PLATFORM(CHROMIUM) -#define WTF_USE_V8 1 -#endif - -/* Not using V8 implies using JSC and vice versa */ -#if !USE(V8) -#define WTF_USE_JSC 1 -#endif - -#if ENABLE(NOTIFICATIONS) && PLATFORM(MAC) -#define ENABLE_TEXT_NOTIFICATIONS_ONLY 1 -#endif - -#if !defined(WTF_USE_ZLIB) && !PLATFORM(QT) -#define WTF_USE_ZLIB 1 -#endif - -#if PLATFORM(QT) -#include -#if defined(QT_OPENGL_ES_2) && !defined(WTF_USE_OPENGL_ES_2) -#define WTF_USE_OPENGL_ES_2 1 -#endif -#endif - -#endif /* WTF_Platform_h */ diff --git a/3rdparty/masm/wtf/PossiblyNull.h b/3rdparty/masm/wtf/PossiblyNull.h deleted file mode 100644 index 46a7d713be..0000000000 --- a/3rdparty/masm/wtf/PossiblyNull.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef PossiblyNull_h -#define PossiblyNull_h - -#include - -namespace WTF { - -template struct PossiblyNull { - PossiblyNull(T data) - : m_data(data) - { - } - PossiblyNull(const PossiblyNull& source) - : m_data(source.m_data) - { - source.m_data = 0; - } - ~PossiblyNull() { ASSERT(!m_data); } - bool getValue(T& out) WARN_UNUSED_RETURN; -private: - mutable T m_data; -}; - -template bool PossiblyNull::getValue(T& out) -{ - out = m_data; - bool result = !!m_data; - m_data = 0; - return result; -} - -} - -#endif diff --git a/3rdparty/masm/wtf/PrintStream.cpp b/3rdparty/masm/wtf/PrintStream.cpp deleted file mode 100644 index 7dd4060971..0000000000 --- a/3rdparty/masm/wtf/PrintStream.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "PrintStream.h" - -#include - -namespace WTF { - -PrintStream::PrintStream() { } -PrintStream::~PrintStream() { } // Force the vtable to be in this module - -void PrintStream::printf(const char* format, ...) -{ - va_list argList; - va_start(argList, format); - vprintf(format, argList); - va_end(argList); -} - -void PrintStream::flush() -{ -} - -void printInternal(PrintStream& out, const char* string) -{ - out.printf("%s", string); -} - -void printInternal(PrintStream& out, bool value) -{ - if (value) - out.print("true"); - else - out.print("false"); -} - -void printInternal(PrintStream& out, int value) -{ - out.printf("%d", value); -} - -void printInternal(PrintStream& out, unsigned value) -{ - out.printf("%u", value); -} - -void printInternal(PrintStream& out, long value) -{ - out.printf("%ld", value); -} - -void printInternal(PrintStream& out, unsigned long value) -{ - out.printf("%lu", value); -} - -void printInternal(PrintStream& out, long long value) -{ - out.printf("%lld", value); -} - -void printInternal(PrintStream& out, unsigned long long value) -{ - out.printf("%llu", value); -} - -void printInternal(PrintStream& out, float value) -{ - out.print(static_cast(value)); -} - -void printInternal(PrintStream& out, double value) -{ - out.printf("%lf", value); -} - -void printInternal(PrintStream& out, RawPointer value) -{ - out.printf("%p", value.value()); -} - -void dumpCharacter(PrintStream& out, char value) -{ - out.printf("%c", value); -} - -} // namespace WTF - diff --git a/3rdparty/masm/wtf/PrintStream.h b/3rdparty/masm/wtf/PrintStream.h deleted file mode 100644 index 4134dcf182..0000000000 --- a/3rdparty/masm/wtf/PrintStream.h +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef PrintStream_h -#define PrintStream_h - -#include -#include -#include -#include -#include -#include - -namespace WTF { - -class CString; -class String; - -class PrintStream { - WTF_MAKE_FAST_ALLOCATED; WTF_MAKE_NONCOPYABLE(PrintStream); -public: - PrintStream(); - virtual ~PrintStream(); - - void printf(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); - virtual void vprintf(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(2, 0) = 0; - - // Typically a no-op for many subclasses of PrintStream, this is a hint that - // the implementation should flush its buffers if it had not done so already. - virtual void flush(); - - template - void print(const T& value) - { - printInternal(*this, value); - } - - template - void print(const T1& value1, const T2& value2) - { - print(value1); - print(value2); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3) - { - print(value1); - print(value2); - print(value3); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4) - { - print(value1); - print(value2); - print(value3); - print(value4); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - print(value10); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - print(value10); - print(value11); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - print(value10); - print(value11); - print(value12); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12, const T13& value13) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - print(value10); - print(value11); - print(value12); - print(value13); - } -}; - -void printInternal(PrintStream&, const char*); -void printInternal(PrintStream&, const CString&); -void printInternal(PrintStream&, const String&); -inline void printInternal(PrintStream& out, char* value) { printInternal(out, static_cast(value)); } -inline void printInternal(PrintStream& out, CString& value) { printInternal(out, static_cast(value)); } -inline void printInternal(PrintStream& out, String& value) { printInternal(out, static_cast(value)); } -void printInternal(PrintStream&, bool); -void printInternal(PrintStream&, int); -void printInternal(PrintStream&, unsigned); -void printInternal(PrintStream&, long); -void printInternal(PrintStream&, unsigned long); -void printInternal(PrintStream&, long long); -void printInternal(PrintStream&, unsigned long long); -void printInternal(PrintStream&, float); -void printInternal(PrintStream&, double); -void printInternal(PrintStream&, RawPointer); - -template -void printInternal(PrintStream& out, const T& value) -{ - value.dump(out); -} - -#define MAKE_PRINT_ADAPTOR(Name, Type, function) \ - class Name { \ - public: \ - Name(const Type& value) \ - : m_value(value) \ - { \ - } \ - void dump(PrintStream& out) const \ - { \ - function(out, m_value); \ - } \ - private: \ - Type m_value; \ - } - -#define MAKE_PRINT_METHOD_ADAPTOR(Name, Type, method) \ - class Name { \ - public: \ - Name(const Type& value) \ - : m_value(value) \ - { \ - } \ - void dump(PrintStream& out) const \ - { \ - m_value.method(out); \ - } \ - private: \ - Type m_value; \ - } - -// Use an adaptor-based dumper for characters to avoid situations where -// you've "compressed" an integer to a character and it ends up printing -// as ASCII when you wanted it to print as a number. -void dumpCharacter(PrintStream&, char); -MAKE_PRINT_ADAPTOR(CharacterDump, char, dumpCharacter); - -template -class PointerDump { -public: - PointerDump(const T* ptr) - : m_ptr(ptr) - { - } - - void dump(PrintStream& out) const - { - if (m_ptr) - printInternal(out, *m_ptr); - else - out.print("(null)"); - } -private: - const T* m_ptr; -}; - -template -PointerDump pointerDump(const T* ptr) { return PointerDump(ptr); } - -} // namespace WTF - -using WTF::CharacterDump; -using WTF::PointerDump; -using WTF::PrintStream; -using WTF::pointerDump; - -#endif // PrintStream_h - diff --git a/3rdparty/masm/wtf/RawPointer.h b/3rdparty/masm/wtf/RawPointer.h deleted file mode 100644 index 6dc7292fb4..0000000000 --- a/3rdparty/masm/wtf/RawPointer.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef RawPointer_h -#define RawPointer_h - -namespace WTF { - -class RawPointer { -public: - RawPointer() - : m_value(0) - { - } - - explicit RawPointer(void* value) - : m_value(value) - { - } - - explicit RawPointer(const void* value) - : m_value(value) - { - } - - const void* value() const { return m_value; } - -private: - const void* m_value; -}; - -} // namespace WTF - -using WTF::RawPointer; - -#endif // RawPointer_h diff --git a/3rdparty/masm/wtf/StdLibExtras.h b/3rdparty/masm/wtf/StdLibExtras.h deleted file mode 100644 index f5e9f78df1..0000000000 --- a/3rdparty/masm/wtf/StdLibExtras.h +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WTF_StdLibExtras_h -#define WTF_StdLibExtras_h - -#include -#include - -// Use these to declare and define a static local variable (static T;) so that -// it is leaked so that its destructors are not called at exit. Using this -// macro also allows workarounds a compiler bug present in Apple's version of GCC 4.0.1. -#ifndef DEFINE_STATIC_LOCAL -#if COMPILER(GCC) && defined(__APPLE_CC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 1 -#define DEFINE_STATIC_LOCAL(type, name, arguments) \ - static type* name##Ptr = new type arguments; \ - type& name = *name##Ptr -#else -#define DEFINE_STATIC_LOCAL(type, name, arguments) \ - static type& name = *new type arguments -#endif -#endif - -// Use this macro to declare and define a debug-only global variable that may have a -// non-trivial constructor and destructor. When building with clang, this will suppress -// warnings about global constructors and exit-time destructors. -#ifndef NDEBUG -#if COMPILER(CLANG) -#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \ - _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \ - static type name arguments; \ - _Pragma("clang diagnostic pop") -#else -#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ - static type name arguments; -#endif // COMPILER(CLANG) -#else -#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) -#endif // NDEBUG - -// OBJECT_OFFSETOF: Like the C++ offsetof macro, but you can use it with classes. -// The magic number 0x4000 is insignificant. We use it to avoid using NULL, since -// NULL can cause compiler problems, especially in cases of multiple inheritance. -#define OBJECT_OFFSETOF(class, field) (reinterpret_cast(&(reinterpret_cast(0x4000)->field)) - 0x4000) - -// STRINGIZE: Can convert any value to quoted string, even expandable macros -#define STRINGIZE(exp) #exp -#define STRINGIZE_VALUE_OF(exp) STRINGIZE(exp) - -/* - * The reinterpret_cast([pointer to Type2]) expressions - where - * sizeof(Type1) > sizeof(Type2) - cause the following warning on ARM with GCC: - * increases required alignment of target type. - * - * An implicit or an extra static_cast bypasses the warning. - * For more info see the following bugzilla entries: - * - https://bugs.webkit.org/show_bug.cgi?id=38045 - * - http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43976 - */ -#if (CPU(ARM) || CPU(MIPS)) && COMPILER(GCC) -template -bool isPointerTypeAlignmentOkay(Type* ptr) -{ - return !(reinterpret_cast(ptr) % __alignof__(Type)); -} - -template -TypePtr reinterpret_cast_ptr(void* ptr) -{ - ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast(ptr))); - return reinterpret_cast(ptr); -} - -template -TypePtr reinterpret_cast_ptr(const void* ptr) -{ - ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast(ptr))); - return reinterpret_cast(ptr); -} -#else -template -bool isPointerTypeAlignmentOkay(Type*) -{ - return true; -} -#define reinterpret_cast_ptr reinterpret_cast -#endif - -namespace WTF { - -static const size_t KB = 1024; -static const size_t MB = 1024 * 1024; - -inline bool isPointerAligned(void* p) -{ - return !((intptr_t)(p) & (sizeof(char*) - 1)); -} - -inline bool is8ByteAligned(void* p) -{ - return !((uintptr_t)(p) & (sizeof(double) - 1)); -} - -/* - * C++'s idea of a reinterpret_cast lacks sufficient cojones. - */ -template -inline TO bitwise_cast(FROM from) -{ - COMPILE_ASSERT(sizeof(TO) == sizeof(FROM), WTF_bitwise_cast_sizeof_casted_types_is_equal); - union { - FROM from; - TO to; - } u; - u.from = from; - return u.to; -} - -template -inline To safeCast(From value) -{ - ASSERT(isInBounds(value)); - return static_cast(value); -} - -// Returns a count of the number of bits set in 'bits'. -inline size_t bitCount(unsigned bits) -{ - bits = bits - ((bits >> 1) & 0x55555555); - bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); - return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; -} - -// Macro that returns a compile time constant with the length of an array, but gives an error if passed a non-array. -template char (&ArrayLengthHelperFunction(T (&)[Size]))[Size]; -// GCC needs some help to deduce a 0 length array. -#if COMPILER(GCC) -template char (&ArrayLengthHelperFunction(T (&)[0]))[0]; -#endif -#define WTF_ARRAY_LENGTH(array) sizeof(::WTF::ArrayLengthHelperFunction(array)) - -// Efficient implementation that takes advantage of powers of two. -inline size_t roundUpToMultipleOf(size_t divisor, size_t x) -{ - ASSERT(divisor && !(divisor & (divisor - 1))); - size_t remainderMask = divisor - 1; - return (x + remainderMask) & ~remainderMask; -} -template inline size_t roundUpToMultipleOf(size_t x) -{ - COMPILE_ASSERT(divisor && !(divisor & (divisor - 1)), divisor_is_a_power_of_two); - return roundUpToMultipleOf(divisor, x); -} - -enum BinarySearchMode { - KeyMustBePresentInArray, - KeyMightNotBePresentInArray, - ReturnAdjacentElementIfKeyIsNotPresent -}; - -template -inline ArrayElementType* binarySearchImpl(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) -{ - size_t offset = 0; - while (size > 1) { - int pos = (size - 1) >> 1; - KeyType val = extractKey(&array[offset + pos]); - - if (val == key) - return &array[offset + pos]; - // The item we are looking for is smaller than the item being check; reduce the value of 'size', - // chopping off the right hand half of the array. - if (key < val) - size = pos; - // Discard all values in the left hand half of the array, up to and including the item at pos. - else { - size -= (pos + 1); - offset += (pos + 1); - } - - ASSERT(mode != KeyMustBePresentInArray || size); - } - - if (mode == KeyMightNotBePresentInArray && !size) - return 0; - - ArrayElementType* result = &array[offset]; - - if (mode == KeyMightNotBePresentInArray && key != extractKey(result)) - return 0; - - if (mode == KeyMustBePresentInArray) { - ASSERT(size == 1); - ASSERT(key == extractKey(result)); - } - - return result; -} - -// If the element is not found, crash if asserts are enabled, and behave like approximateBinarySearch in release builds. -template -inline ArrayElementType* binarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) -{ - return binarySearchImpl(array, size, key, extractKey); -} - -// Return zero if the element is not found. -template -inline ArrayElementType* tryBinarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) -{ - return binarySearchImpl(array, size, key, extractKey); -} - -// Return the element that is either to the left, or the right, of where the element would have been found. -template -inline ArrayElementType* approximateBinarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) -{ - return binarySearchImpl(array, size, key, extractKey); -} - -// Variants of the above that use const. -template -inline ArrayElementType* binarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) -{ - return binarySearchImpl(const_cast(array), size, key, extractKey); -} -template -inline ArrayElementType* tryBinarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) -{ - return binarySearchImpl(const_cast(array), size, key, extractKey); -} -template -inline ArrayElementType* approximateBinarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) -{ - return binarySearchImpl(const_cast(array), size, key, extractKey); -} - -} // namespace WTF - -// This version of placement new omits a 0 check. -enum NotNullTag { NotNull }; -inline void* operator new(size_t, NotNullTag, void* location) -{ - ASSERT(location); - return location; -} - -using WTF::KB; -using WTF::MB; -using WTF::isPointerAligned; -using WTF::is8ByteAligned; -using WTF::binarySearch; -using WTF::tryBinarySearch; -using WTF::approximateBinarySearch; -using WTF::bitwise_cast; -using WTF::safeCast; - -#endif // WTF_StdLibExtras_h diff --git a/3rdparty/masm/wtf/VMTags.h b/3rdparty/masm/wtf/VMTags.h deleted file mode 100644 index 117bc3721e..0000000000 --- a/3rdparty/masm/wtf/VMTags.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef VMTags_h -#define VMTags_h - -// On Mac OS X, the VM subsystem allows tagging memory requested from mmap and vm_map -// in order to aid tools that inspect system memory use. -#if OS(DARWIN) - -#include - -#if defined(VM_MEMORY_TCMALLOC) -#define VM_TAG_FOR_TCMALLOC_MEMORY VM_MAKE_TAG(VM_MEMORY_TCMALLOC) -#else -#define VM_TAG_FOR_TCMALLOC_MEMORY VM_MAKE_TAG(53) -#endif // defined(VM_MEMORY_TCMALLOC) - -#if defined(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) -#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) -#else -#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY VM_MAKE_TAG(64) -#endif // defined(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) - -#if defined(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) -#define VM_TAG_FOR_REGISTERFILE_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) -#else -#define VM_TAG_FOR_REGISTERFILE_MEMORY VM_MAKE_TAG(65) -#endif // defined(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) - -#if defined(VM_MEMORY_JAVASCRIPT_CORE) -#define VM_TAG_FOR_COLLECTOR_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_CORE) -#else -#define VM_TAG_FOR_COLLECTOR_MEMORY VM_MAKE_TAG(63) -#endif // defined(VM_MEMORY_JAVASCRIPT_CORE) - -#if defined(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) -#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY VM_MAKE_TAG(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) -#else -#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY VM_MAKE_TAG(69) -#endif // defined(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) - -#else // OS(DARWIN) - -#define VM_TAG_FOR_TCMALLOC_MEMORY -1 -#define VM_TAG_FOR_COLLECTOR_MEMORY -1 -#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY -1 -#define VM_TAG_FOR_REGISTERFILE_MEMORY -1 -#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY -1 - -#endif // OS(DARWIN) - -#endif // VMTags_h diff --git a/3rdparty/masm/yarr/Yarr.h b/3rdparty/masm/yarr/Yarr.h deleted file mode 100644 index d393e9fa90..0000000000 --- a/3rdparty/masm/yarr/Yarr.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef Yarr_h -#define Yarr_h - -#include "YarrInterpreter.h" -#include "YarrPattern.h" - -namespace JSC { namespace Yarr { - -#define YarrStackSpaceForBackTrackInfoPatternCharacter 1 // Only for !fixed quantifiers. -#define YarrStackSpaceForBackTrackInfoCharacterClass 1 // Only for !fixed quantifiers. -#define YarrStackSpaceForBackTrackInfoBackReference 2 -#define YarrStackSpaceForBackTrackInfoAlternative 1 // One per alternative. -#define YarrStackSpaceForBackTrackInfoParentheticalAssertion 1 -#define YarrStackSpaceForBackTrackInfoParenthesesOnce 1 // Only for !fixed quantifiers. -#define YarrStackSpaceForBackTrackInfoParenthesesTerminal 1 -#define YarrStackSpaceForBackTrackInfoParentheses 2 - -static const unsigned quantifyInfinite = UINT_MAX; -static const unsigned offsetNoMatch = (unsigned)-1; - -// The below limit restricts the number of "recursive" match calls in order to -// avoid spending exponential time on complex regular expressions. -static const unsigned matchLimit = 1000000; - -enum JSRegExpResult { - JSRegExpMatch = 1, - JSRegExpNoMatch = 0, - JSRegExpErrorNoMatch = -1, - JSRegExpErrorHitLimit = -2, - JSRegExpErrorNoMemory = -3, - JSRegExpErrorInternal = -4 -}; - -enum YarrCharSize { - Char8, - Char16 -}; - -} } // namespace JSC::Yarr - -#endif // Yarr_h - diff --git a/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp b/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp deleted file mode 100644 index 7bb3d08eb5..0000000000 --- a/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// DO NOT EDIT! - this file autogenerated by YarrCanonicalizeUCS2.js - -#include "config.h" -#include "YarrCanonicalizeUCS2.h" - -namespace JSC { namespace Yarr { - -#include - -uint16_t ucs2CharacterSet0[] = { 0x01c4u, 0x01c5u, 0x01c6u, 0 }; -uint16_t ucs2CharacterSet1[] = { 0x01c7u, 0x01c8u, 0x01c9u, 0 }; -uint16_t ucs2CharacterSet2[] = { 0x01cau, 0x01cbu, 0x01ccu, 0 }; -uint16_t ucs2CharacterSet3[] = { 0x01f1u, 0x01f2u, 0x01f3u, 0 }; -uint16_t ucs2CharacterSet4[] = { 0x0392u, 0x03b2u, 0x03d0u, 0 }; -uint16_t ucs2CharacterSet5[] = { 0x0395u, 0x03b5u, 0x03f5u, 0 }; -uint16_t ucs2CharacterSet6[] = { 0x0398u, 0x03b8u, 0x03d1u, 0 }; -uint16_t ucs2CharacterSet7[] = { 0x0345u, 0x0399u, 0x03b9u, 0x1fbeu, 0 }; -uint16_t ucs2CharacterSet8[] = { 0x039au, 0x03bau, 0x03f0u, 0 }; -uint16_t ucs2CharacterSet9[] = { 0x00b5u, 0x039cu, 0x03bcu, 0 }; -uint16_t ucs2CharacterSet10[] = { 0x03a0u, 0x03c0u, 0x03d6u, 0 }; -uint16_t ucs2CharacterSet11[] = { 0x03a1u, 0x03c1u, 0x03f1u, 0 }; -uint16_t ucs2CharacterSet12[] = { 0x03a3u, 0x03c2u, 0x03c3u, 0 }; -uint16_t ucs2CharacterSet13[] = { 0x03a6u, 0x03c6u, 0x03d5u, 0 }; -uint16_t ucs2CharacterSet14[] = { 0x1e60u, 0x1e61u, 0x1e9bu, 0 }; - -static const size_t UCS2_CANONICALIZATION_SETS = 15; -uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = { - ucs2CharacterSet0, - ucs2CharacterSet1, - ucs2CharacterSet2, - ucs2CharacterSet3, - ucs2CharacterSet4, - ucs2CharacterSet5, - ucs2CharacterSet6, - ucs2CharacterSet7, - ucs2CharacterSet8, - ucs2CharacterSet9, - ucs2CharacterSet10, - ucs2CharacterSet11, - ucs2CharacterSet12, - ucs2CharacterSet13, - ucs2CharacterSet14, -}; - -const size_t UCS2_CANONICALIZATION_RANGES = 364; -UCS2CanonicalizationRange rangeInfo[UCS2_CANONICALIZATION_RANGES] = { - { 0x0000u, 0x0040u, 0x0000u, CanonicalizeUnique }, - { 0x0041u, 0x005au, 0x0020u, CanonicalizeRangeLo }, - { 0x005bu, 0x0060u, 0x0000u, CanonicalizeUnique }, - { 0x0061u, 0x007au, 0x0020u, CanonicalizeRangeHi }, - { 0x007bu, 0x00b4u, 0x0000u, CanonicalizeUnique }, - { 0x00b5u, 0x00b5u, 0x0009u, CanonicalizeSet }, - { 0x00b6u, 0x00bfu, 0x0000u, CanonicalizeUnique }, - { 0x00c0u, 0x00d6u, 0x0020u, CanonicalizeRangeLo }, - { 0x00d7u, 0x00d7u, 0x0000u, CanonicalizeUnique }, - { 0x00d8u, 0x00deu, 0x0020u, CanonicalizeRangeLo }, - { 0x00dfu, 0x00dfu, 0x0000u, CanonicalizeUnique }, - { 0x00e0u, 0x00f6u, 0x0020u, CanonicalizeRangeHi }, - { 0x00f7u, 0x00f7u, 0x0000u, CanonicalizeUnique }, - { 0x00f8u, 0x00feu, 0x0020u, CanonicalizeRangeHi }, - { 0x00ffu, 0x00ffu, 0x0079u, CanonicalizeRangeLo }, - { 0x0100u, 0x012fu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0130u, 0x0131u, 0x0000u, CanonicalizeUnique }, - { 0x0132u, 0x0137u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0138u, 0x0138u, 0x0000u, CanonicalizeUnique }, - { 0x0139u, 0x0148u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x0149u, 0x0149u, 0x0000u, CanonicalizeUnique }, - { 0x014au, 0x0177u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0178u, 0x0178u, 0x0079u, CanonicalizeRangeHi }, - { 0x0179u, 0x017eu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x017fu, 0x017fu, 0x0000u, CanonicalizeUnique }, - { 0x0180u, 0x0180u, 0x00c3u, CanonicalizeRangeLo }, - { 0x0181u, 0x0181u, 0x00d2u, CanonicalizeRangeLo }, - { 0x0182u, 0x0185u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0186u, 0x0186u, 0x00ceu, CanonicalizeRangeLo }, - { 0x0187u, 0x0188u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x0189u, 0x018au, 0x00cdu, CanonicalizeRangeLo }, - { 0x018bu, 0x018cu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x018du, 0x018du, 0x0000u, CanonicalizeUnique }, - { 0x018eu, 0x018eu, 0x004fu, CanonicalizeRangeLo }, - { 0x018fu, 0x018fu, 0x00cau, CanonicalizeRangeLo }, - { 0x0190u, 0x0190u, 0x00cbu, CanonicalizeRangeLo }, - { 0x0191u, 0x0192u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x0193u, 0x0193u, 0x00cdu, CanonicalizeRangeLo }, - { 0x0194u, 0x0194u, 0x00cfu, CanonicalizeRangeLo }, - { 0x0195u, 0x0195u, 0x0061u, CanonicalizeRangeLo }, - { 0x0196u, 0x0196u, 0x00d3u, CanonicalizeRangeLo }, - { 0x0197u, 0x0197u, 0x00d1u, CanonicalizeRangeLo }, - { 0x0198u, 0x0199u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x019au, 0x019au, 0x00a3u, CanonicalizeRangeLo }, - { 0x019bu, 0x019bu, 0x0000u, CanonicalizeUnique }, - { 0x019cu, 0x019cu, 0x00d3u, CanonicalizeRangeLo }, - { 0x019du, 0x019du, 0x00d5u, CanonicalizeRangeLo }, - { 0x019eu, 0x019eu, 0x0082u, CanonicalizeRangeLo }, - { 0x019fu, 0x019fu, 0x00d6u, CanonicalizeRangeLo }, - { 0x01a0u, 0x01a5u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x01a6u, 0x01a6u, 0x00dau, CanonicalizeRangeLo }, - { 0x01a7u, 0x01a8u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x01a9u, 0x01a9u, 0x00dau, CanonicalizeRangeLo }, - { 0x01aau, 0x01abu, 0x0000u, CanonicalizeUnique }, - { 0x01acu, 0x01adu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x01aeu, 0x01aeu, 0x00dau, CanonicalizeRangeLo }, - { 0x01afu, 0x01b0u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x01b1u, 0x01b2u, 0x00d9u, CanonicalizeRangeLo }, - { 0x01b3u, 0x01b6u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x01b7u, 0x01b7u, 0x00dbu, CanonicalizeRangeLo }, - { 0x01b8u, 0x01b9u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x01bau, 0x01bbu, 0x0000u, CanonicalizeUnique }, - { 0x01bcu, 0x01bdu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x01beu, 0x01beu, 0x0000u, CanonicalizeUnique }, - { 0x01bfu, 0x01bfu, 0x0038u, CanonicalizeRangeLo }, - { 0x01c0u, 0x01c3u, 0x0000u, CanonicalizeUnique }, - { 0x01c4u, 0x01c6u, 0x0000u, CanonicalizeSet }, - { 0x01c7u, 0x01c9u, 0x0001u, CanonicalizeSet }, - { 0x01cau, 0x01ccu, 0x0002u, CanonicalizeSet }, - { 0x01cdu, 0x01dcu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x01ddu, 0x01ddu, 0x004fu, CanonicalizeRangeHi }, - { 0x01deu, 0x01efu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x01f0u, 0x01f0u, 0x0000u, CanonicalizeUnique }, - { 0x01f1u, 0x01f3u, 0x0003u, CanonicalizeSet }, - { 0x01f4u, 0x01f5u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x01f6u, 0x01f6u, 0x0061u, CanonicalizeRangeHi }, - { 0x01f7u, 0x01f7u, 0x0038u, CanonicalizeRangeHi }, - { 0x01f8u, 0x021fu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0220u, 0x0220u, 0x0082u, CanonicalizeRangeHi }, - { 0x0221u, 0x0221u, 0x0000u, CanonicalizeUnique }, - { 0x0222u, 0x0233u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0234u, 0x0239u, 0x0000u, CanonicalizeUnique }, - { 0x023au, 0x023au, 0x2a2bu, CanonicalizeRangeLo }, - { 0x023bu, 0x023cu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x023du, 0x023du, 0x00a3u, CanonicalizeRangeHi }, - { 0x023eu, 0x023eu, 0x2a28u, CanonicalizeRangeLo }, - { 0x023fu, 0x0240u, 0x2a3fu, CanonicalizeRangeLo }, - { 0x0241u, 0x0242u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x0243u, 0x0243u, 0x00c3u, CanonicalizeRangeHi }, - { 0x0244u, 0x0244u, 0x0045u, CanonicalizeRangeLo }, - { 0x0245u, 0x0245u, 0x0047u, CanonicalizeRangeLo }, - { 0x0246u, 0x024fu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0250u, 0x0250u, 0x2a1fu, CanonicalizeRangeLo }, - { 0x0251u, 0x0251u, 0x2a1cu, CanonicalizeRangeLo }, - { 0x0252u, 0x0252u, 0x2a1eu, CanonicalizeRangeLo }, - { 0x0253u, 0x0253u, 0x00d2u, CanonicalizeRangeHi }, - { 0x0254u, 0x0254u, 0x00ceu, CanonicalizeRangeHi }, - { 0x0255u, 0x0255u, 0x0000u, CanonicalizeUnique }, - { 0x0256u, 0x0257u, 0x00cdu, CanonicalizeRangeHi }, - { 0x0258u, 0x0258u, 0x0000u, CanonicalizeUnique }, - { 0x0259u, 0x0259u, 0x00cau, CanonicalizeRangeHi }, - { 0x025au, 0x025au, 0x0000u, CanonicalizeUnique }, - { 0x025bu, 0x025bu, 0x00cbu, CanonicalizeRangeHi }, - { 0x025cu, 0x025fu, 0x0000u, CanonicalizeUnique }, - { 0x0260u, 0x0260u, 0x00cdu, CanonicalizeRangeHi }, - { 0x0261u, 0x0262u, 0x0000u, CanonicalizeUnique }, - { 0x0263u, 0x0263u, 0x00cfu, CanonicalizeRangeHi }, - { 0x0264u, 0x0264u, 0x0000u, CanonicalizeUnique }, - { 0x0265u, 0x0265u, 0xa528u, CanonicalizeRangeLo }, - { 0x0266u, 0x0267u, 0x0000u, CanonicalizeUnique }, - { 0x0268u, 0x0268u, 0x00d1u, CanonicalizeRangeHi }, - { 0x0269u, 0x0269u, 0x00d3u, CanonicalizeRangeHi }, - { 0x026au, 0x026au, 0x0000u, CanonicalizeUnique }, - { 0x026bu, 0x026bu, 0x29f7u, CanonicalizeRangeLo }, - { 0x026cu, 0x026eu, 0x0000u, CanonicalizeUnique }, - { 0x026fu, 0x026fu, 0x00d3u, CanonicalizeRangeHi }, - { 0x0270u, 0x0270u, 0x0000u, CanonicalizeUnique }, - { 0x0271u, 0x0271u, 0x29fdu, CanonicalizeRangeLo }, - { 0x0272u, 0x0272u, 0x00d5u, CanonicalizeRangeHi }, - { 0x0273u, 0x0274u, 0x0000u, CanonicalizeUnique }, - { 0x0275u, 0x0275u, 0x00d6u, CanonicalizeRangeHi }, - { 0x0276u, 0x027cu, 0x0000u, CanonicalizeUnique }, - { 0x027du, 0x027du, 0x29e7u, CanonicalizeRangeLo }, - { 0x027eu, 0x027fu, 0x0000u, CanonicalizeUnique }, - { 0x0280u, 0x0280u, 0x00dau, CanonicalizeRangeHi }, - { 0x0281u, 0x0282u, 0x0000u, CanonicalizeUnique }, - { 0x0283u, 0x0283u, 0x00dau, CanonicalizeRangeHi }, - { 0x0284u, 0x0287u, 0x0000u, CanonicalizeUnique }, - { 0x0288u, 0x0288u, 0x00dau, CanonicalizeRangeHi }, - { 0x0289u, 0x0289u, 0x0045u, CanonicalizeRangeHi }, - { 0x028au, 0x028bu, 0x00d9u, CanonicalizeRangeHi }, - { 0x028cu, 0x028cu, 0x0047u, CanonicalizeRangeHi }, - { 0x028du, 0x0291u, 0x0000u, CanonicalizeUnique }, - { 0x0292u, 0x0292u, 0x00dbu, CanonicalizeRangeHi }, - { 0x0293u, 0x0344u, 0x0000u, CanonicalizeUnique }, - { 0x0345u, 0x0345u, 0x0007u, CanonicalizeSet }, - { 0x0346u, 0x036fu, 0x0000u, CanonicalizeUnique }, - { 0x0370u, 0x0373u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0374u, 0x0375u, 0x0000u, CanonicalizeUnique }, - { 0x0376u, 0x0377u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0378u, 0x037au, 0x0000u, CanonicalizeUnique }, - { 0x037bu, 0x037du, 0x0082u, CanonicalizeRangeLo }, - { 0x037eu, 0x0385u, 0x0000u, CanonicalizeUnique }, - { 0x0386u, 0x0386u, 0x0026u, CanonicalizeRangeLo }, - { 0x0387u, 0x0387u, 0x0000u, CanonicalizeUnique }, - { 0x0388u, 0x038au, 0x0025u, CanonicalizeRangeLo }, - { 0x038bu, 0x038bu, 0x0000u, CanonicalizeUnique }, - { 0x038cu, 0x038cu, 0x0040u, CanonicalizeRangeLo }, - { 0x038du, 0x038du, 0x0000u, CanonicalizeUnique }, - { 0x038eu, 0x038fu, 0x003fu, CanonicalizeRangeLo }, - { 0x0390u, 0x0390u, 0x0000u, CanonicalizeUnique }, - { 0x0391u, 0x0391u, 0x0020u, CanonicalizeRangeLo }, - { 0x0392u, 0x0392u, 0x0004u, CanonicalizeSet }, - { 0x0393u, 0x0394u, 0x0020u, CanonicalizeRangeLo }, - { 0x0395u, 0x0395u, 0x0005u, CanonicalizeSet }, - { 0x0396u, 0x0397u, 0x0020u, CanonicalizeRangeLo }, - { 0x0398u, 0x0398u, 0x0006u, CanonicalizeSet }, - { 0x0399u, 0x0399u, 0x0007u, CanonicalizeSet }, - { 0x039au, 0x039au, 0x0008u, CanonicalizeSet }, - { 0x039bu, 0x039bu, 0x0020u, CanonicalizeRangeLo }, - { 0x039cu, 0x039cu, 0x0009u, CanonicalizeSet }, - { 0x039du, 0x039fu, 0x0020u, CanonicalizeRangeLo }, - { 0x03a0u, 0x03a0u, 0x000au, CanonicalizeSet }, - { 0x03a1u, 0x03a1u, 0x000bu, CanonicalizeSet }, - { 0x03a2u, 0x03a2u, 0x0000u, CanonicalizeUnique }, - { 0x03a3u, 0x03a3u, 0x000cu, CanonicalizeSet }, - { 0x03a4u, 0x03a5u, 0x0020u, CanonicalizeRangeLo }, - { 0x03a6u, 0x03a6u, 0x000du, CanonicalizeSet }, - { 0x03a7u, 0x03abu, 0x0020u, CanonicalizeRangeLo }, - { 0x03acu, 0x03acu, 0x0026u, CanonicalizeRangeHi }, - { 0x03adu, 0x03afu, 0x0025u, CanonicalizeRangeHi }, - { 0x03b0u, 0x03b0u, 0x0000u, CanonicalizeUnique }, - { 0x03b1u, 0x03b1u, 0x0020u, CanonicalizeRangeHi }, - { 0x03b2u, 0x03b2u, 0x0004u, CanonicalizeSet }, - { 0x03b3u, 0x03b4u, 0x0020u, CanonicalizeRangeHi }, - { 0x03b5u, 0x03b5u, 0x0005u, CanonicalizeSet }, - { 0x03b6u, 0x03b7u, 0x0020u, CanonicalizeRangeHi }, - { 0x03b8u, 0x03b8u, 0x0006u, CanonicalizeSet }, - { 0x03b9u, 0x03b9u, 0x0007u, CanonicalizeSet }, - { 0x03bau, 0x03bau, 0x0008u, CanonicalizeSet }, - { 0x03bbu, 0x03bbu, 0x0020u, CanonicalizeRangeHi }, - { 0x03bcu, 0x03bcu, 0x0009u, CanonicalizeSet }, - { 0x03bdu, 0x03bfu, 0x0020u, CanonicalizeRangeHi }, - { 0x03c0u, 0x03c0u, 0x000au, CanonicalizeSet }, - { 0x03c1u, 0x03c1u, 0x000bu, CanonicalizeSet }, - { 0x03c2u, 0x03c3u, 0x000cu, CanonicalizeSet }, - { 0x03c4u, 0x03c5u, 0x0020u, CanonicalizeRangeHi }, - { 0x03c6u, 0x03c6u, 0x000du, CanonicalizeSet }, - { 0x03c7u, 0x03cbu, 0x0020u, CanonicalizeRangeHi }, - { 0x03ccu, 0x03ccu, 0x0040u, CanonicalizeRangeHi }, - { 0x03cdu, 0x03ceu, 0x003fu, CanonicalizeRangeHi }, - { 0x03cfu, 0x03cfu, 0x0008u, CanonicalizeRangeLo }, - { 0x03d0u, 0x03d0u, 0x0004u, CanonicalizeSet }, - { 0x03d1u, 0x03d1u, 0x0006u, CanonicalizeSet }, - { 0x03d2u, 0x03d4u, 0x0000u, CanonicalizeUnique }, - { 0x03d5u, 0x03d5u, 0x000du, CanonicalizeSet }, - { 0x03d6u, 0x03d6u, 0x000au, CanonicalizeSet }, - { 0x03d7u, 0x03d7u, 0x0008u, CanonicalizeRangeHi }, - { 0x03d8u, 0x03efu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x03f0u, 0x03f0u, 0x0008u, CanonicalizeSet }, - { 0x03f1u, 0x03f1u, 0x000bu, CanonicalizeSet }, - { 0x03f2u, 0x03f2u, 0x0007u, CanonicalizeRangeLo }, - { 0x03f3u, 0x03f4u, 0x0000u, CanonicalizeUnique }, - { 0x03f5u, 0x03f5u, 0x0005u, CanonicalizeSet }, - { 0x03f6u, 0x03f6u, 0x0000u, CanonicalizeUnique }, - { 0x03f7u, 0x03f8u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x03f9u, 0x03f9u, 0x0007u, CanonicalizeRangeHi }, - { 0x03fau, 0x03fbu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x03fcu, 0x03fcu, 0x0000u, CanonicalizeUnique }, - { 0x03fdu, 0x03ffu, 0x0082u, CanonicalizeRangeHi }, - { 0x0400u, 0x040fu, 0x0050u, CanonicalizeRangeLo }, - { 0x0410u, 0x042fu, 0x0020u, CanonicalizeRangeLo }, - { 0x0430u, 0x044fu, 0x0020u, CanonicalizeRangeHi }, - { 0x0450u, 0x045fu, 0x0050u, CanonicalizeRangeHi }, - { 0x0460u, 0x0481u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0482u, 0x0489u, 0x0000u, CanonicalizeUnique }, - { 0x048au, 0x04bfu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x04c0u, 0x04c0u, 0x000fu, CanonicalizeRangeLo }, - { 0x04c1u, 0x04ceu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x04cfu, 0x04cfu, 0x000fu, CanonicalizeRangeHi }, - { 0x04d0u, 0x0527u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x0528u, 0x0530u, 0x0000u, CanonicalizeUnique }, - { 0x0531u, 0x0556u, 0x0030u, CanonicalizeRangeLo }, - { 0x0557u, 0x0560u, 0x0000u, CanonicalizeUnique }, - { 0x0561u, 0x0586u, 0x0030u, CanonicalizeRangeHi }, - { 0x0587u, 0x109fu, 0x0000u, CanonicalizeUnique }, - { 0x10a0u, 0x10c5u, 0x1c60u, CanonicalizeRangeLo }, - { 0x10c6u, 0x1d78u, 0x0000u, CanonicalizeUnique }, - { 0x1d79u, 0x1d79u, 0x8a04u, CanonicalizeRangeLo }, - { 0x1d7au, 0x1d7cu, 0x0000u, CanonicalizeUnique }, - { 0x1d7du, 0x1d7du, 0x0ee6u, CanonicalizeRangeLo }, - { 0x1d7eu, 0x1dffu, 0x0000u, CanonicalizeUnique }, - { 0x1e00u, 0x1e5fu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x1e60u, 0x1e61u, 0x000eu, CanonicalizeSet }, - { 0x1e62u, 0x1e95u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x1e96u, 0x1e9au, 0x0000u, CanonicalizeUnique }, - { 0x1e9bu, 0x1e9bu, 0x000eu, CanonicalizeSet }, - { 0x1e9cu, 0x1e9fu, 0x0000u, CanonicalizeUnique }, - { 0x1ea0u, 0x1effu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x1f00u, 0x1f07u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f08u, 0x1f0fu, 0x0008u, CanonicalizeRangeHi }, - { 0x1f10u, 0x1f15u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f16u, 0x1f17u, 0x0000u, CanonicalizeUnique }, - { 0x1f18u, 0x1f1du, 0x0008u, CanonicalizeRangeHi }, - { 0x1f1eu, 0x1f1fu, 0x0000u, CanonicalizeUnique }, - { 0x1f20u, 0x1f27u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f28u, 0x1f2fu, 0x0008u, CanonicalizeRangeHi }, - { 0x1f30u, 0x1f37u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f38u, 0x1f3fu, 0x0008u, CanonicalizeRangeHi }, - { 0x1f40u, 0x1f45u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f46u, 0x1f47u, 0x0000u, CanonicalizeUnique }, - { 0x1f48u, 0x1f4du, 0x0008u, CanonicalizeRangeHi }, - { 0x1f4eu, 0x1f50u, 0x0000u, CanonicalizeUnique }, - { 0x1f51u, 0x1f51u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f52u, 0x1f52u, 0x0000u, CanonicalizeUnique }, - { 0x1f53u, 0x1f53u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f54u, 0x1f54u, 0x0000u, CanonicalizeUnique }, - { 0x1f55u, 0x1f55u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f56u, 0x1f56u, 0x0000u, CanonicalizeUnique }, - { 0x1f57u, 0x1f57u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f58u, 0x1f58u, 0x0000u, CanonicalizeUnique }, - { 0x1f59u, 0x1f59u, 0x0008u, CanonicalizeRangeHi }, - { 0x1f5au, 0x1f5au, 0x0000u, CanonicalizeUnique }, - { 0x1f5bu, 0x1f5bu, 0x0008u, CanonicalizeRangeHi }, - { 0x1f5cu, 0x1f5cu, 0x0000u, CanonicalizeUnique }, - { 0x1f5du, 0x1f5du, 0x0008u, CanonicalizeRangeHi }, - { 0x1f5eu, 0x1f5eu, 0x0000u, CanonicalizeUnique }, - { 0x1f5fu, 0x1f5fu, 0x0008u, CanonicalizeRangeHi }, - { 0x1f60u, 0x1f67u, 0x0008u, CanonicalizeRangeLo }, - { 0x1f68u, 0x1f6fu, 0x0008u, CanonicalizeRangeHi }, - { 0x1f70u, 0x1f71u, 0x004au, CanonicalizeRangeLo }, - { 0x1f72u, 0x1f75u, 0x0056u, CanonicalizeRangeLo }, - { 0x1f76u, 0x1f77u, 0x0064u, CanonicalizeRangeLo }, - { 0x1f78u, 0x1f79u, 0x0080u, CanonicalizeRangeLo }, - { 0x1f7au, 0x1f7bu, 0x0070u, CanonicalizeRangeLo }, - { 0x1f7cu, 0x1f7du, 0x007eu, CanonicalizeRangeLo }, - { 0x1f7eu, 0x1fafu, 0x0000u, CanonicalizeUnique }, - { 0x1fb0u, 0x1fb1u, 0x0008u, CanonicalizeRangeLo }, - { 0x1fb2u, 0x1fb7u, 0x0000u, CanonicalizeUnique }, - { 0x1fb8u, 0x1fb9u, 0x0008u, CanonicalizeRangeHi }, - { 0x1fbau, 0x1fbbu, 0x004au, CanonicalizeRangeHi }, - { 0x1fbcu, 0x1fbdu, 0x0000u, CanonicalizeUnique }, - { 0x1fbeu, 0x1fbeu, 0x0007u, CanonicalizeSet }, - { 0x1fbfu, 0x1fc7u, 0x0000u, CanonicalizeUnique }, - { 0x1fc8u, 0x1fcbu, 0x0056u, CanonicalizeRangeHi }, - { 0x1fccu, 0x1fcfu, 0x0000u, CanonicalizeUnique }, - { 0x1fd0u, 0x1fd1u, 0x0008u, CanonicalizeRangeLo }, - { 0x1fd2u, 0x1fd7u, 0x0000u, CanonicalizeUnique }, - { 0x1fd8u, 0x1fd9u, 0x0008u, CanonicalizeRangeHi }, - { 0x1fdau, 0x1fdbu, 0x0064u, CanonicalizeRangeHi }, - { 0x1fdcu, 0x1fdfu, 0x0000u, CanonicalizeUnique }, - { 0x1fe0u, 0x1fe1u, 0x0008u, CanonicalizeRangeLo }, - { 0x1fe2u, 0x1fe4u, 0x0000u, CanonicalizeUnique }, - { 0x1fe5u, 0x1fe5u, 0x0007u, CanonicalizeRangeLo }, - { 0x1fe6u, 0x1fe7u, 0x0000u, CanonicalizeUnique }, - { 0x1fe8u, 0x1fe9u, 0x0008u, CanonicalizeRangeHi }, - { 0x1feau, 0x1febu, 0x0070u, CanonicalizeRangeHi }, - { 0x1fecu, 0x1fecu, 0x0007u, CanonicalizeRangeHi }, - { 0x1fedu, 0x1ff7u, 0x0000u, CanonicalizeUnique }, - { 0x1ff8u, 0x1ff9u, 0x0080u, CanonicalizeRangeHi }, - { 0x1ffau, 0x1ffbu, 0x007eu, CanonicalizeRangeHi }, - { 0x1ffcu, 0x2131u, 0x0000u, CanonicalizeUnique }, - { 0x2132u, 0x2132u, 0x001cu, CanonicalizeRangeLo }, - { 0x2133u, 0x214du, 0x0000u, CanonicalizeUnique }, - { 0x214eu, 0x214eu, 0x001cu, CanonicalizeRangeHi }, - { 0x214fu, 0x215fu, 0x0000u, CanonicalizeUnique }, - { 0x2160u, 0x216fu, 0x0010u, CanonicalizeRangeLo }, - { 0x2170u, 0x217fu, 0x0010u, CanonicalizeRangeHi }, - { 0x2180u, 0x2182u, 0x0000u, CanonicalizeUnique }, - { 0x2183u, 0x2184u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x2185u, 0x24b5u, 0x0000u, CanonicalizeUnique }, - { 0x24b6u, 0x24cfu, 0x001au, CanonicalizeRangeLo }, - { 0x24d0u, 0x24e9u, 0x001au, CanonicalizeRangeHi }, - { 0x24eau, 0x2bffu, 0x0000u, CanonicalizeUnique }, - { 0x2c00u, 0x2c2eu, 0x0030u, CanonicalizeRangeLo }, - { 0x2c2fu, 0x2c2fu, 0x0000u, CanonicalizeUnique }, - { 0x2c30u, 0x2c5eu, 0x0030u, CanonicalizeRangeHi }, - { 0x2c5fu, 0x2c5fu, 0x0000u, CanonicalizeUnique }, - { 0x2c60u, 0x2c61u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x2c62u, 0x2c62u, 0x29f7u, CanonicalizeRangeHi }, - { 0x2c63u, 0x2c63u, 0x0ee6u, CanonicalizeRangeHi }, - { 0x2c64u, 0x2c64u, 0x29e7u, CanonicalizeRangeHi }, - { 0x2c65u, 0x2c65u, 0x2a2bu, CanonicalizeRangeHi }, - { 0x2c66u, 0x2c66u, 0x2a28u, CanonicalizeRangeHi }, - { 0x2c67u, 0x2c6cu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x2c6du, 0x2c6du, 0x2a1cu, CanonicalizeRangeHi }, - { 0x2c6eu, 0x2c6eu, 0x29fdu, CanonicalizeRangeHi }, - { 0x2c6fu, 0x2c6fu, 0x2a1fu, CanonicalizeRangeHi }, - { 0x2c70u, 0x2c70u, 0x2a1eu, CanonicalizeRangeHi }, - { 0x2c71u, 0x2c71u, 0x0000u, CanonicalizeUnique }, - { 0x2c72u, 0x2c73u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x2c74u, 0x2c74u, 0x0000u, CanonicalizeUnique }, - { 0x2c75u, 0x2c76u, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x2c77u, 0x2c7du, 0x0000u, CanonicalizeUnique }, - { 0x2c7eu, 0x2c7fu, 0x2a3fu, CanonicalizeRangeHi }, - { 0x2c80u, 0x2ce3u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0x2ce4u, 0x2ceau, 0x0000u, CanonicalizeUnique }, - { 0x2cebu, 0x2ceeu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0x2cefu, 0x2cffu, 0x0000u, CanonicalizeUnique }, - { 0x2d00u, 0x2d25u, 0x1c60u, CanonicalizeRangeHi }, - { 0x2d26u, 0xa63fu, 0x0000u, CanonicalizeUnique }, - { 0xa640u, 0xa66du, 0x0000u, CanonicalizeAlternatingAligned }, - { 0xa66eu, 0xa67fu, 0x0000u, CanonicalizeUnique }, - { 0xa680u, 0xa697u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0xa698u, 0xa721u, 0x0000u, CanonicalizeUnique }, - { 0xa722u, 0xa72fu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0xa730u, 0xa731u, 0x0000u, CanonicalizeUnique }, - { 0xa732u, 0xa76fu, 0x0000u, CanonicalizeAlternatingAligned }, - { 0xa770u, 0xa778u, 0x0000u, CanonicalizeUnique }, - { 0xa779u, 0xa77cu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0xa77du, 0xa77du, 0x8a04u, CanonicalizeRangeHi }, - { 0xa77eu, 0xa787u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0xa788u, 0xa78au, 0x0000u, CanonicalizeUnique }, - { 0xa78bu, 0xa78cu, 0x0000u, CanonicalizeAlternatingUnaligned }, - { 0xa78du, 0xa78du, 0xa528u, CanonicalizeRangeHi }, - { 0xa78eu, 0xa78fu, 0x0000u, CanonicalizeUnique }, - { 0xa790u, 0xa791u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0xa792u, 0xa79fu, 0x0000u, CanonicalizeUnique }, - { 0xa7a0u, 0xa7a9u, 0x0000u, CanonicalizeAlternatingAligned }, - { 0xa7aau, 0xff20u, 0x0000u, CanonicalizeUnique }, - { 0xff21u, 0xff3au, 0x0020u, CanonicalizeRangeLo }, - { 0xff3bu, 0xff40u, 0x0000u, CanonicalizeUnique }, - { 0xff41u, 0xff5au, 0x0020u, CanonicalizeRangeHi }, - { 0xff5bu, 0xffffu, 0x0000u, CanonicalizeUnique }, -}; - -const size_t LATIN_CANONICALIZATION_RANGES = 20; -LatinCanonicalizationRange latinRangeInfo[LATIN_CANONICALIZATION_RANGES] = { - { 0x0000u, 0x0040u, 0x0000u, CanonicalizeLatinSelf }, - { 0x0041u, 0x005au, 0x0000u, CanonicalizeLatinMask0x20 }, - { 0x005bu, 0x0060u, 0x0000u, CanonicalizeLatinSelf }, - { 0x0061u, 0x007au, 0x0000u, CanonicalizeLatinMask0x20 }, - { 0x007bu, 0x00bfu, 0x0000u, CanonicalizeLatinSelf }, - { 0x00c0u, 0x00d6u, 0x0000u, CanonicalizeLatinMask0x20 }, - { 0x00d7u, 0x00d7u, 0x0000u, CanonicalizeLatinSelf }, - { 0x00d8u, 0x00deu, 0x0000u, CanonicalizeLatinMask0x20 }, - { 0x00dfu, 0x00dfu, 0x0000u, CanonicalizeLatinSelf }, - { 0x00e0u, 0x00f6u, 0x0000u, CanonicalizeLatinMask0x20 }, - { 0x00f7u, 0x00f7u, 0x0000u, CanonicalizeLatinSelf }, - { 0x00f8u, 0x00feu, 0x0000u, CanonicalizeLatinMask0x20 }, - { 0x00ffu, 0x00ffu, 0x0000u, CanonicalizeLatinSelf }, - { 0x0100u, 0x0177u, 0x0000u, CanonicalizeLatinInvalid }, - { 0x0178u, 0x0178u, 0x00ffu, CanonicalizeLatinOther }, - { 0x0179u, 0x039bu, 0x0000u, CanonicalizeLatinInvalid }, - { 0x039cu, 0x039cu, 0x00b5u, CanonicalizeLatinOther }, - { 0x039du, 0x03bbu, 0x0000u, CanonicalizeLatinInvalid }, - { 0x03bcu, 0x03bcu, 0x00b5u, CanonicalizeLatinOther }, - { 0x03bdu, 0xffffu, 0x0000u, CanonicalizeLatinInvalid }, -}; - -} } // JSC::Yarr - diff --git a/3rdparty/masm/yarr/YarrCanonicalizeUCS2.h b/3rdparty/masm/yarr/YarrCanonicalizeUCS2.h deleted file mode 100644 index be0ead43d2..0000000000 --- a/3rdparty/masm/yarr/YarrCanonicalizeUCS2.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef YarrCanonicalizeUCS2_H -#define YarrCanonicalizeUCS2_H - -#include -#include - -namespace JSC { namespace Yarr { - -// This set of data (autogenerated using YarrCanonicalizeUCS2.js into YarrCanonicalizeUCS2.cpp) -// provides information for each UCS2 code point as to the set of code points that it should -// match under the ES5.1 case insensitive RegExp matching rules, specified in 15.10.2.8. -enum UCS2CanonicalizationType { - CanonicalizeUnique, // No canonically equal values, e.g. 0x0. - CanonicalizeSet, // Value indicates a set in characterSetInfo. - CanonicalizeRangeLo, // Value is positive delta to pair, E.g. 0x41 has value 0x20, -> 0x61. - CanonicalizeRangeHi, // Value is positive delta to pair, E.g. 0x61 has value 0x20, -> 0x41. - CanonicalizeAlternatingAligned, // Aligned consequtive pair, e.g. 0x1f4,0x1f5. - CanonicalizeAlternatingUnaligned, // Unaligned consequtive pair, e.g. 0x241,0x242. -}; -struct UCS2CanonicalizationRange { uint16_t begin, end, value, type; }; -extern const size_t UCS2_CANONICALIZATION_RANGES; -extern uint16_t* characterSetInfo[]; -extern UCS2CanonicalizationRange rangeInfo[]; - -// This table is similar to the full rangeInfo table, however this maps from UCS2 codepoints to -// the set of Latin1 codepoints that could match. -enum LatinCanonicalizationType { - CanonicalizeLatinSelf, // This character is in the Latin1 range, but has no canonical equivalent in the range. - CanonicalizeLatinMask0x20, // One of a pair of characters, under the mask 0x20. - CanonicalizeLatinOther, // This character is not in the Latin1 range, but canonicalizes to another that is. - CanonicalizeLatinInvalid, // Cannot match against Latin1 input. -}; -struct LatinCanonicalizationRange { uint16_t begin, end, value, type; }; -extern const size_t LATIN_CANONICALIZATION_RANGES; -extern LatinCanonicalizationRange latinRangeInfo[]; - -// This searches in log2 time over ~364 entries, so should typically result in 8 compares. -inline UCS2CanonicalizationRange* rangeInfoFor(UChar ch) -{ - UCS2CanonicalizationRange* info = rangeInfo; - size_t entries = UCS2_CANONICALIZATION_RANGES; - - while (true) { - size_t candidate = entries >> 1; - UCS2CanonicalizationRange* candidateInfo = info + candidate; - if (ch < candidateInfo->begin) - entries = candidate; - else if (ch <= candidateInfo->end) - return candidateInfo; - else { - info = candidateInfo + 1; - entries -= (candidate + 1); - } - } -} - -// Should only be called for characters that have one canonically matching value. -inline UChar getCanonicalPair(UCS2CanonicalizationRange* info, UChar ch) -{ - ASSERT(ch >= info->begin && ch <= info->end); - switch (info->type) { - case CanonicalizeRangeLo: - return ch + info->value; - case CanonicalizeRangeHi: - return ch - info->value; - case CanonicalizeAlternatingAligned: - return ch ^ 1; - case CanonicalizeAlternatingUnaligned: - return ((ch - 1) ^ 1) + 1; - default: - ASSERT_NOT_REACHED(); - } - ASSERT_NOT_REACHED(); - return 0; -} - -// Returns true if no other UCS2 codepoint can match this value. -inline bool isCanonicallyUnique(UChar ch) -{ - return rangeInfoFor(ch)->type == CanonicalizeUnique; -} - -// Returns true if values are equal, under the canonicalization rules. -inline bool areCanonicallyEquivalent(UChar a, UChar b) -{ - UCS2CanonicalizationRange* info = rangeInfoFor(a); - switch (info->type) { - case CanonicalizeUnique: - return a == b; - case CanonicalizeSet: { - for (uint16_t* set = characterSetInfo[info->value]; (a = *set); ++set) { - if (a == b) - return true; - } - return false; - } - case CanonicalizeRangeLo: - return (a == b) || (a + info->value == b); - case CanonicalizeRangeHi: - return (a == b) || (a - info->value == b); - case CanonicalizeAlternatingAligned: - return (a | 1) == (b | 1); - case CanonicalizeAlternatingUnaligned: - return ((a - 1) | 1) == ((b - 1) | 1); - } - - ASSERT_NOT_REACHED(); - return false; -} - -} } // JSC::Yarr - -#endif diff --git a/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js b/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js deleted file mode 100644 index 00361dd46e..0000000000 --- a/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// See ES 5.1, 15.10.2.8 -function canonicalize(ch) -{ - var u = String.fromCharCode(ch).toUpperCase(); - if (u.length > 1) - return ch; - var cu = u.charCodeAt(0); - if (ch >= 128 && cu < 128) - return ch; - return cu; -} - -var MAX_UCS2 = 0xFFFF; -var MAX_LATIN = 0xFF; - -var groupedCanonically = []; -// Pass 1: populate groupedCanonically - this is mapping from canonicalized -// values back to the set of character code that canonicalize to them. -for (var i = 0; i <= MAX_UCS2; ++i) { - var ch = canonicalize(i); - if (!groupedCanonically[ch]) - groupedCanonically[ch] = []; - groupedCanonically[ch].push(i); -} - -var typeInfo = []; -var latinTypeInfo = []; -var characterSetInfo = []; -// Pass 2: populate typeInfo & characterSetInfo. For every character calculate -// a typeInfo value, described by the types above, and a value payload. -for (cu in groupedCanonically) { - // The set of characters that canonicalize to cu - var characters = groupedCanonically[cu]; - - // If there is only one, it is unique. - if (characters.length == 1) { - typeInfo[characters[0]] = "CanonicalizeUnique:0"; - latinTypeInfo[characters[0]] = characters[0] <= MAX_LATIN ? "CanonicalizeLatinSelf:0" : "CanonicalizeLatinInvalid:0"; - continue; - } - - // Sort the array. - characters.sort(function(x,y){return x-y;}); - - // If there are more than two characters, create an entry in characterSetInfo. - if (characters.length > 2) { - for (i in characters) - typeInfo[characters[i]] = "CanonicalizeSet:" + characterSetInfo.length; - characterSetInfo.push(characters); - - if (characters[1] <= MAX_LATIN) - throw new Error("sets with more than one latin character not supported!"); - if (characters[0] <= MAX_LATIN) { - for (i in characters) - latinTypeInfo[characters[i]] = "CanonicalizeLatinOther:" + characters[0]; - latinTypeInfo[characters[0]] = "CanonicalizeLatinSelf:0"; - } else { - for (i in characters) - latinTypeInfo[characters[i]] = "CanonicalizeLatinInvalid:0"; - } - - continue; - } - - // We have a pair, mark alternating ranges, otherwise track whether this is the low or high partner. - var lo = characters[0]; - var hi = characters[1]; - var delta = hi - lo; - if (delta == 1) { - var type = lo & 1 ? "CanonicalizeAlternatingUnaligned:0" : "CanonicalizeAlternatingAligned:0"; - typeInfo[lo] = type; - typeInfo[hi] = type; - } else { - typeInfo[lo] = "CanonicalizeRangeLo:" + delta; - typeInfo[hi] = "CanonicalizeRangeHi:" + delta; - } - - if (lo > MAX_LATIN) { - latinTypeInfo[lo] = "CanonicalizeLatinInvalid:0"; - latinTypeInfo[hi] = "CanonicalizeLatinInvalid:0"; - } else if (hi > MAX_LATIN) { - latinTypeInfo[lo] = "CanonicalizeLatinSelf:0"; - latinTypeInfo[hi] = "CanonicalizeLatinOther:" + lo; - } else { - if (delta != 0x20 || lo & 0x20) - throw new Error("pairs of latin characters that don't mask with 0x20 not supported!"); - latinTypeInfo[lo] = "CanonicalizeLatinMask0x20:0"; - latinTypeInfo[hi] = "CanonicalizeLatinMask0x20:0"; - } -} - -var rangeInfo = []; -// Pass 3: coallesce types into ranges. -for (var end = 0; end <= MAX_UCS2; ++end) { - var begin = end; - var type = typeInfo[end]; - while (end < MAX_UCS2 && typeInfo[end + 1] == type) - ++end; - rangeInfo.push({begin:begin, end:end, type:type}); -} - -var latinRangeInfo = []; -// Pass 4: coallesce latin-1 types into ranges. -for (var end = 0; end <= MAX_UCS2; ++end) { - var begin = end; - var type = latinTypeInfo[end]; - while (end < MAX_UCS2 && latinTypeInfo[end + 1] == type) - ++end; - latinRangeInfo.push({begin:begin, end:end, type:type}); -} - - -// Helper function to convert a number to a fixed width hex representation of a C uint16_t. -function hex(x) -{ - var s = Number(x).toString(16); - while (s.length < 4) - s = 0 + s; - return "0x" + s + "u"; -} - -var copyright = ( - "/*" + "\n" + - " * Copyright (C) 2012 Apple Inc. All rights reserved." + "\n" + - " *" + "\n" + - " * Redistribution and use in source and binary forms, with or without" + "\n" + - " * modification, are permitted provided that the following conditions" + "\n" + - " * are met:" + "\n" + - " * 1. Redistributions of source code must retain the above copyright" + "\n" + - " * notice, this list of conditions and the following disclaimer." + "\n" + - " * 2. Redistributions in binary form must reproduce the above copyright" + "\n" + - " * notice, this list of conditions and the following disclaimer in the" + "\n" + - " * documentation and/or other materials provided with the distribution." + "\n" + - " *" + "\n" + - " * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY" + "\n" + - " * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE" + "\n" + - " * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR" + "\n" + - " * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR" + "\n" + - " * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL," + "\n" + - " * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO," + "\n" + - " * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR" + "\n" + - " * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY" + "\n" + - " * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" + "\n" + - " * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" + "\n" + - " * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. " + "\n" + - " */"); - -print(copyright); -print(); -print("// DO NOT EDIT! - this file autogenerated by YarrCanonicalizeUCS2.js"); -print(); -print('#include "config.h"'); -print('#include "YarrCanonicalizeUCS2.h"'); -print(); -print("namespace JSC { namespace Yarr {"); -print(); -print("#include "); -print(); - -for (i in characterSetInfo) { - var characters = "" - var set = characterSetInfo[i]; - for (var j in set) - characters += hex(set[j]) + ", "; - print("uint16_t ucs2CharacterSet" + i + "[] = { " + characters + "0 };"); -} -print(); -print("static const size_t UCS2_CANONICALIZATION_SETS = " + characterSetInfo.length + ";"); -print("uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = {"); -for (i in characterSetInfo) -print(" ucs2CharacterSet" + i + ","); -print("};"); -print(); -print("const size_t UCS2_CANONICALIZATION_RANGES = " + rangeInfo.length + ";"); -print("UCS2CanonicalizationRange rangeInfo[UCS2_CANONICALIZATION_RANGES] = {"); -for (i in rangeInfo) { - var info = rangeInfo[i]; - var typeAndValue = info.type.split(':'); - print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },"); -} -print("};"); -print(); -print("const size_t LATIN_CANONICALIZATION_RANGES = " + latinRangeInfo.length + ";"); -print("LatinCanonicalizationRange latinRangeInfo[LATIN_CANONICALIZATION_RANGES] = {"); -for (i in latinRangeInfo) { - var info = latinRangeInfo[i]; - var typeAndValue = info.type.split(':'); - print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },"); -} -print("};"); -print(); -print("} } // JSC::Yarr"); -print(); - diff --git a/3rdparty/masm/yarr/YarrInterpreter.cpp b/3rdparty/masm/yarr/YarrInterpreter.cpp deleted file mode 100644 index 31603f6d34..0000000000 --- a/3rdparty/masm/yarr/YarrInterpreter.cpp +++ /dev/null @@ -1,1964 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "YarrInterpreter.h" - -#include "Yarr.h" -#include "YarrCanonicalizeUCS2.h" -#include -#include -#include -#include - -#ifndef NDEBUG -#include -#endif - -using namespace WTF; - -namespace JSC { namespace Yarr { - -template -class Interpreter { -public: - struct ParenthesesDisjunctionContext; - - struct BackTrackInfoPatternCharacter { - uintptr_t matchAmount; - }; - struct BackTrackInfoCharacterClass { - uintptr_t matchAmount; - }; - struct BackTrackInfoBackReference { - uintptr_t begin; // Not really needed for greedy quantifiers. - uintptr_t matchAmount; // Not really needed for fixed quantifiers. - }; - struct BackTrackInfoAlternative { - uintptr_t offset; - }; - struct BackTrackInfoParentheticalAssertion { - uintptr_t begin; - }; - struct BackTrackInfoParenthesesOnce { - uintptr_t begin; - }; - struct BackTrackInfoParenthesesTerminal { - uintptr_t begin; - }; - struct BackTrackInfoParentheses { - uintptr_t matchAmount; - ParenthesesDisjunctionContext* lastContext; - }; - - static inline void appendParenthesesDisjunctionContext(BackTrackInfoParentheses* backTrack, ParenthesesDisjunctionContext* context) - { - context->next = backTrack->lastContext; - backTrack->lastContext = context; - ++backTrack->matchAmount; - } - - static inline void popParenthesesDisjunctionContext(BackTrackInfoParentheses* backTrack) - { - ASSERT(backTrack->matchAmount); - ASSERT(backTrack->lastContext); - backTrack->lastContext = backTrack->lastContext->next; - --backTrack->matchAmount; - } - - struct DisjunctionContext - { - DisjunctionContext() - : term(0) - { - } - - void* operator new(size_t, void* where) - { - return where; - } - - int term; - unsigned matchBegin; - unsigned matchEnd; - uintptr_t frame[1]; - }; - - DisjunctionContext* allocDisjunctionContext(ByteDisjunction* disjunction) - { - size_t size = sizeof(DisjunctionContext) - sizeof(uintptr_t) + disjunction->m_frameSize * sizeof(uintptr_t); - allocatorPool = allocatorPool->ensureCapacity(size); - if (!allocatorPool) - CRASH(); - return new (allocatorPool->alloc(size)) DisjunctionContext(); - } - - void freeDisjunctionContext(DisjunctionContext* context) - { - allocatorPool = allocatorPool->dealloc(context); - } - - struct ParenthesesDisjunctionContext - { - ParenthesesDisjunctionContext(unsigned* output, ByteTerm& term) - : next(0) - { - unsigned firstSubpatternId = term.atom.subpatternId; - unsigned numNestedSubpatterns = term.atom.parenthesesDisjunction->m_numSubpatterns; - - for (unsigned i = 0; i < (numNestedSubpatterns << 1); ++i) { - subpatternBackup[i] = output[(firstSubpatternId << 1) + i]; - output[(firstSubpatternId << 1) + i] = offsetNoMatch; - } - - new (getDisjunctionContext(term)) DisjunctionContext(); - } - - void* operator new(size_t, void* where) - { - return where; - } - - void restoreOutput(unsigned* output, unsigned firstSubpatternId, unsigned numNestedSubpatterns) - { - for (unsigned i = 0; i < (numNestedSubpatterns << 1); ++i) - output[(firstSubpatternId << 1) + i] = subpatternBackup[i]; - } - - DisjunctionContext* getDisjunctionContext(ByteTerm& term) - { - return reinterpret_cast(&(subpatternBackup[term.atom.parenthesesDisjunction->m_numSubpatterns << 1])); - } - - ParenthesesDisjunctionContext* next; - unsigned subpatternBackup[1]; - }; - - ParenthesesDisjunctionContext* allocParenthesesDisjunctionContext(ByteDisjunction* disjunction, unsigned* output, ByteTerm& term) - { - size_t size = sizeof(ParenthesesDisjunctionContext) - sizeof(unsigned) + (term.atom.parenthesesDisjunction->m_numSubpatterns << 1) * sizeof(unsigned) + sizeof(DisjunctionContext) - sizeof(uintptr_t) + disjunction->m_frameSize * sizeof(uintptr_t); - allocatorPool = allocatorPool->ensureCapacity(size); - if (!allocatorPool) - CRASH(); - return new (allocatorPool->alloc(size)) ParenthesesDisjunctionContext(output, term); - } - - void freeParenthesesDisjunctionContext(ParenthesesDisjunctionContext* context) - { - allocatorPool = allocatorPool->dealloc(context); - } - - class InputStream { - public: - InputStream(const CharType* input, unsigned start, unsigned length) - : input(input) - , pos(start) - , length(length) - { - } - - void next() - { - ++pos; - } - - void rewind(unsigned amount) - { - ASSERT(pos >= amount); - pos -= amount; - } - - int read() - { - ASSERT(pos < length); - if (pos < length) - return input[pos]; - return -1; - } - - int readPair() - { - ASSERT(pos + 1 < length); - return input[pos] | input[pos + 1] << 16; - } - - int readChecked(unsigned negativePositionOffest) - { - if (pos < negativePositionOffest) - CRASH(); - unsigned p = pos - negativePositionOffest; - ASSERT(p < length); - return input[p]; - } - - int reread(unsigned from) - { - ASSERT(from < length); - return input[from]; - } - - int prev() - { - ASSERT(!(pos > length)); - if (pos && length) - return input[pos - 1]; - return -1; - } - - unsigned getPos() - { - return pos; - } - - void setPos(unsigned p) - { - pos = p; - } - - bool atStart() - { - return pos == 0; - } - - bool atEnd() - { - return pos == length; - } - - unsigned end() - { - return length; - } - - bool checkInput(unsigned count) - { - if (((pos + count) <= length) && ((pos + count) >= pos)) { - pos += count; - return true; - } - return false; - } - - void uncheckInput(unsigned count) - { - if (pos < count) - CRASH(); - pos -= count; - } - - bool atStart(unsigned negativePositionOffest) - { - return pos == negativePositionOffest; - } - - bool atEnd(unsigned negativePositionOffest) - { - if (pos < negativePositionOffest) - CRASH(); - return (pos - negativePositionOffest) == length; - } - - bool isAvailableInput(unsigned offset) - { - return (((pos + offset) <= length) && ((pos + offset) >= pos)); - } - - private: - const CharType* input; - unsigned pos; - unsigned length; - }; - - bool testCharacterClass(CharacterClass* characterClass, int ch) - { - if (ch & 0xFF80) { - for (unsigned i = 0; i < characterClass->m_matchesUnicode.size(); ++i) - if (ch == characterClass->m_matchesUnicode[i]) - return true; - for (unsigned i = 0; i < characterClass->m_rangesUnicode.size(); ++i) - if ((ch >= characterClass->m_rangesUnicode[i].begin) && (ch <= characterClass->m_rangesUnicode[i].end)) - return true; - } else { - for (unsigned i = 0; i < characterClass->m_matches.size(); ++i) - if (ch == characterClass->m_matches[i]) - return true; - for (unsigned i = 0; i < characterClass->m_ranges.size(); ++i) - if ((ch >= characterClass->m_ranges[i].begin) && (ch <= characterClass->m_ranges[i].end)) - return true; - } - - return false; - } - - bool checkCharacter(int testChar, unsigned negativeInputOffset) - { - return testChar == input.readChecked(negativeInputOffset); - } - - bool checkCasedCharacter(int loChar, int hiChar, unsigned negativeInputOffset) - { - int ch = input.readChecked(negativeInputOffset); - return (loChar == ch) || (hiChar == ch); - } - - bool checkCharacterClass(CharacterClass* characterClass, bool invert, unsigned negativeInputOffset) - { - bool match = testCharacterClass(characterClass, input.readChecked(negativeInputOffset)); - return invert ? !match : match; - } - - bool tryConsumeBackReference(int matchBegin, int matchEnd, unsigned negativeInputOffset) - { - unsigned matchSize = (unsigned)(matchEnd - matchBegin); - - if (!input.checkInput(matchSize)) - return false; - - if (pattern->m_ignoreCase) { - for (unsigned i = 0; i < matchSize; ++i) { - int oldCh = input.reread(matchBegin + i); - int ch = input.readChecked(negativeInputOffset + matchSize - i); - - if (oldCh == ch) - continue; - - // The definition for canonicalize (see ES 5.1, 15.10.2.8) means that - // unicode values are never allowed to match against ascii ones. - if (isASCII(oldCh) || isASCII(ch)) { - if (toASCIIUpper(oldCh) == toASCIIUpper(ch)) - continue; - } else if (areCanonicallyEquivalent(oldCh, ch)) - continue; - - input.uncheckInput(matchSize); - return false; - } - } else { - for (unsigned i = 0; i < matchSize; ++i) { - if (!checkCharacter(input.reread(matchBegin + i), negativeInputOffset + matchSize - i)) { - input.uncheckInput(matchSize); - return false; - } - } - } - - return true; - } - - bool matchAssertionBOL(ByteTerm& term) - { - return (input.atStart(term.inputPosition)) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition + 1))); - } - - bool matchAssertionEOL(ByteTerm& term) - { - if (term.inputPosition) - return (input.atEnd(term.inputPosition)) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition))); - - return (input.atEnd()) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.read())); - } - - bool matchAssertionWordBoundary(ByteTerm& term) - { - bool prevIsWordchar = !input.atStart(term.inputPosition) && testCharacterClass(pattern->wordcharCharacterClass, input.readChecked(term.inputPosition + 1)); - bool readIsWordchar; - if (term.inputPosition) - readIsWordchar = !input.atEnd(term.inputPosition) && testCharacterClass(pattern->wordcharCharacterClass, input.readChecked(term.inputPosition)); - else - readIsWordchar = !input.atEnd() && testCharacterClass(pattern->wordcharCharacterClass, input.read()); - - bool wordBoundary = prevIsWordchar != readIsWordchar; - return term.invert() ? !wordBoundary : wordBoundary; - } - - bool backtrackPatternCharacter(ByteTerm& term, DisjunctionContext* context) - { - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - switch (term.atom.quantityType) { - case QuantifierFixedCount: - break; - - case QuantifierGreedy: - if (backTrack->matchAmount) { - --backTrack->matchAmount; - input.uncheckInput(1); - return true; - } - break; - - case QuantifierNonGreedy: - if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { - ++backTrack->matchAmount; - if (checkCharacter(term.atom.patternCharacter, term.inputPosition + 1)) - return true; - } - input.uncheckInput(backTrack->matchAmount); - break; - } - - return false; - } - - bool backtrackPatternCasedCharacter(ByteTerm& term, DisjunctionContext* context) - { - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - switch (term.atom.quantityType) { - case QuantifierFixedCount: - break; - - case QuantifierGreedy: - if (backTrack->matchAmount) { - --backTrack->matchAmount; - input.uncheckInput(1); - return true; - } - break; - - case QuantifierNonGreedy: - if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { - ++backTrack->matchAmount; - if (checkCasedCharacter(term.atom.casedCharacter.lo, term.atom.casedCharacter.hi, term.inputPosition + 1)) - return true; - } - input.uncheckInput(backTrack->matchAmount); - break; - } - - return false; - } - - bool matchCharacterClass(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeCharacterClass); - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - switch (term.atom.quantityType) { - case QuantifierFixedCount: { - for (unsigned matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) { - if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition - matchAmount)) - return false; - } - return true; - } - - case QuantifierGreedy: { - unsigned matchAmount = 0; - while ((matchAmount < term.atom.quantityCount) && input.checkInput(1)) { - if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) { - input.uncheckInput(1); - break; - } - ++matchAmount; - } - backTrack->matchAmount = matchAmount; - - return true; - } - - case QuantifierNonGreedy: - backTrack->matchAmount = 0; - return true; - } - - ASSERT_NOT_REACHED(); - return false; - } - - bool backtrackCharacterClass(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeCharacterClass); - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - switch (term.atom.quantityType) { - case QuantifierFixedCount: - break; - - case QuantifierGreedy: - if (backTrack->matchAmount) { - --backTrack->matchAmount; - input.uncheckInput(1); - return true; - } - break; - - case QuantifierNonGreedy: - if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { - ++backTrack->matchAmount; - if (checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) - return true; - } - input.uncheckInput(backTrack->matchAmount); - break; - } - - return false; - } - - bool matchBackReference(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeBackReference); - BackTrackInfoBackReference* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - unsigned matchBegin = output[(term.atom.subpatternId << 1)]; - unsigned matchEnd = output[(term.atom.subpatternId << 1) + 1]; - - // If the end position of the referenced match hasn't set yet then the backreference in the same parentheses where it references to that. - // In this case the result of match is empty string like when it references to a parentheses with zero-width match. - // Eg.: /(a\1)/ - if (matchEnd == offsetNoMatch) - return true; - - if (matchBegin == offsetNoMatch) - return true; - - ASSERT(matchBegin <= matchEnd); - - if (matchBegin == matchEnd) - return true; - - switch (term.atom.quantityType) { - case QuantifierFixedCount: { - backTrack->begin = input.getPos(); - for (unsigned matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) { - if (!tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) { - input.setPos(backTrack->begin); - return false; - } - } - return true; - } - - case QuantifierGreedy: { - unsigned matchAmount = 0; - while ((matchAmount < term.atom.quantityCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) - ++matchAmount; - backTrack->matchAmount = matchAmount; - return true; - } - - case QuantifierNonGreedy: - backTrack->begin = input.getPos(); - backTrack->matchAmount = 0; - return true; - } - - ASSERT_NOT_REACHED(); - return false; - } - - bool backtrackBackReference(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeBackReference); - BackTrackInfoBackReference* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - unsigned matchBegin = output[(term.atom.subpatternId << 1)]; - unsigned matchEnd = output[(term.atom.subpatternId << 1) + 1]; - - if (matchBegin == offsetNoMatch) - return false; - - ASSERT(matchBegin <= matchEnd); - - if (matchBegin == matchEnd) - return false; - - switch (term.atom.quantityType) { - case QuantifierFixedCount: - // for quantityCount == 1, could rewind. - input.setPos(backTrack->begin); - break; - - case QuantifierGreedy: - if (backTrack->matchAmount) { - --backTrack->matchAmount; - input.rewind(matchEnd - matchBegin); - return true; - } - break; - - case QuantifierNonGreedy: - if ((backTrack->matchAmount < term.atom.quantityCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) { - ++backTrack->matchAmount; - return true; - } - input.setPos(backTrack->begin); - break; - } - - return false; - } - - void recordParenthesesMatch(ByteTerm& term, ParenthesesDisjunctionContext* context) - { - if (term.capture()) { - unsigned subpatternId = term.atom.subpatternId; - output[(subpatternId << 1)] = context->getDisjunctionContext(term)->matchBegin + term.inputPosition; - output[(subpatternId << 1) + 1] = context->getDisjunctionContext(term)->matchEnd + term.inputPosition; - } - } - void resetMatches(ByteTerm& term, ParenthesesDisjunctionContext* context) - { - unsigned firstSubpatternId = term.atom.subpatternId; - unsigned count = term.atom.parenthesesDisjunction->m_numSubpatterns; - context->restoreOutput(output, firstSubpatternId, count); - } - JSRegExpResult parenthesesDoBacktrack(ByteTerm& term, BackTrackInfoParentheses* backTrack) - { - while (backTrack->matchAmount) { - ParenthesesDisjunctionContext* context = backTrack->lastContext; - - JSRegExpResult result = matchDisjunction(term.atom.parenthesesDisjunction, context->getDisjunctionContext(term), true); - if (result == JSRegExpMatch) - return JSRegExpMatch; - - resetMatches(term, context); - popParenthesesDisjunctionContext(backTrack); - freeParenthesesDisjunctionContext(context); - - if (result != JSRegExpNoMatch) - return result; - } - - return JSRegExpNoMatch; - } - - bool matchParenthesesOnceBegin(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceBegin); - ASSERT(term.atom.quantityCount == 1); - - BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - switch (term.atom.quantityType) { - case QuantifierGreedy: { - // set this speculatively; if we get to the parens end this will be true. - backTrack->begin = input.getPos(); - break; - } - case QuantifierNonGreedy: { - backTrack->begin = notFound; - context->term += term.atom.parenthesesWidth; - return true; - } - case QuantifierFixedCount: - break; - } - - if (term.capture()) { - unsigned subpatternId = term.atom.subpatternId; - output[(subpatternId << 1)] = input.getPos() - term.inputPosition; - } - - return true; - } - - bool matchParenthesesOnceEnd(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceEnd); - ASSERT(term.atom.quantityCount == 1); - - if (term.capture()) { - unsigned subpatternId = term.atom.subpatternId; - output[(subpatternId << 1) + 1] = input.getPos() + term.inputPosition; - } - - if (term.atom.quantityType == QuantifierFixedCount) - return true; - - BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); - return backTrack->begin != input.getPos(); - } - - bool backtrackParenthesesOnceBegin(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceBegin); - ASSERT(term.atom.quantityCount == 1); - - BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - if (term.capture()) { - unsigned subpatternId = term.atom.subpatternId; - output[(subpatternId << 1)] = offsetNoMatch; - output[(subpatternId << 1) + 1] = offsetNoMatch; - } - - switch (term.atom.quantityType) { - case QuantifierGreedy: - // if we backtrack to this point, there is another chance - try matching nothing. - ASSERT(backTrack->begin != notFound); - backTrack->begin = notFound; - context->term += term.atom.parenthesesWidth; - return true; - case QuantifierNonGreedy: - ASSERT(backTrack->begin != notFound); - case QuantifierFixedCount: - break; - } - - return false; - } - - bool backtrackParenthesesOnceEnd(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceEnd); - ASSERT(term.atom.quantityCount == 1); - - BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - switch (term.atom.quantityType) { - case QuantifierGreedy: - if (backTrack->begin == notFound) { - context->term -= term.atom.parenthesesWidth; - return false; - } - case QuantifierNonGreedy: - if (backTrack->begin == notFound) { - backTrack->begin = input.getPos(); - if (term.capture()) { - // Technically this access to inputPosition should be accessing the begin term's - // inputPosition, but for repeats other than fixed these values should be - // the same anyway! (We don't pre-check for greedy or non-greedy matches.) - ASSERT((&term - term.atom.parenthesesWidth)->type == ByteTerm::TypeParenthesesSubpatternOnceBegin); - ASSERT((&term - term.atom.parenthesesWidth)->inputPosition == term.inputPosition); - unsigned subpatternId = term.atom.subpatternId; - output[subpatternId << 1] = input.getPos() + term.inputPosition; - } - context->term -= term.atom.parenthesesWidth; - return true; - } - case QuantifierFixedCount: - break; - } - - return false; - } - - bool matchParenthesesTerminalBegin(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); - ASSERT(term.atom.quantityType == QuantifierGreedy); - ASSERT(term.atom.quantityCount == quantifyInfinite); - ASSERT(!term.capture()); - - BackTrackInfoParenthesesTerminal* backTrack = reinterpret_cast(context->frame + term.frameLocation); - backTrack->begin = input.getPos(); - return true; - } - - bool matchParenthesesTerminalEnd(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalEnd); - - BackTrackInfoParenthesesTerminal* backTrack = reinterpret_cast(context->frame + term.frameLocation); - // Empty match is a failed match. - if (backTrack->begin == input.getPos()) - return false; - - // Successful match! Okay, what's next? - loop around and try to match moar! - context->term -= (term.atom.parenthesesWidth + 1); - return true; - } - - bool backtrackParenthesesTerminalBegin(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); - ASSERT(term.atom.quantityType == QuantifierGreedy); - ASSERT(term.atom.quantityCount == quantifyInfinite); - ASSERT(!term.capture()); - - // If we backtrack to this point, we have failed to match this iteration of the parens. - // Since this is greedy / zero minimum a failed is also accepted as a match! - context->term += term.atom.parenthesesWidth; - return true; - } - - bool backtrackParenthesesTerminalEnd(ByteTerm&, DisjunctionContext*) - { - // 'Terminal' parentheses are at the end of the regex, and as such a match past end - // should always be returned as a successful match - we should never backtrack to here. - ASSERT_NOT_REACHED(); - return false; - } - - bool matchParentheticalAssertionBegin(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParentheticalAssertionBegin); - ASSERT(term.atom.quantityCount == 1); - - BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - backTrack->begin = input.getPos(); - return true; - } - - bool matchParentheticalAssertionEnd(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParentheticalAssertionEnd); - ASSERT(term.atom.quantityCount == 1); - - BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - input.setPos(backTrack->begin); - - // We've reached the end of the parens; if they are inverted, this is failure. - if (term.invert()) { - context->term -= term.atom.parenthesesWidth; - return false; - } - - return true; - } - - bool backtrackParentheticalAssertionBegin(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParentheticalAssertionBegin); - ASSERT(term.atom.quantityCount == 1); - - // We've failed to match parens; if they are inverted, this is win! - if (term.invert()) { - context->term += term.atom.parenthesesWidth; - return true; - } - - return false; - } - - bool backtrackParentheticalAssertionEnd(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParentheticalAssertionEnd); - ASSERT(term.atom.quantityCount == 1); - - BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); - - input.setPos(backTrack->begin); - - context->term -= term.atom.parenthesesWidth; - return false; - } - - JSRegExpResult matchParentheses(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpattern); - - BackTrackInfoParentheses* backTrack = reinterpret_cast(context->frame + term.frameLocation); - ByteDisjunction* disjunctionBody = term.atom.parenthesesDisjunction; - - backTrack->matchAmount = 0; - backTrack->lastContext = 0; - - switch (term.atom.quantityType) { - case QuantifierFixedCount: { - // While we haven't yet reached our fixed limit, - while (backTrack->matchAmount < term.atom.quantityCount) { - // Try to do a match, and it it succeeds, add it to the list. - ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); - JSRegExpResult result = matchDisjunction(disjunctionBody, context->getDisjunctionContext(term)); - if (result == JSRegExpMatch) - appendParenthesesDisjunctionContext(backTrack, context); - else { - // The match failed; try to find an alternate point to carry on from. - resetMatches(term, context); - freeParenthesesDisjunctionContext(context); - - if (result != JSRegExpNoMatch) - return result; - JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack); - if (backtrackResult != JSRegExpMatch) - return backtrackResult; - } - } - - ASSERT(backTrack->matchAmount == term.atom.quantityCount); - ParenthesesDisjunctionContext* context = backTrack->lastContext; - recordParenthesesMatch(term, context); - return JSRegExpMatch; - } - - case QuantifierGreedy: { - while (backTrack->matchAmount < term.atom.quantityCount) { - ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); - JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); - if (result == JSRegExpMatch) - appendParenthesesDisjunctionContext(backTrack, context); - else { - resetMatches(term, context); - freeParenthesesDisjunctionContext(context); - - if (result != JSRegExpNoMatch) - return result; - - break; - } - } - - if (backTrack->matchAmount) { - ParenthesesDisjunctionContext* context = backTrack->lastContext; - recordParenthesesMatch(term, context); - } - return JSRegExpMatch; - } - - case QuantifierNonGreedy: - return JSRegExpMatch; - } - - ASSERT_NOT_REACHED(); - return JSRegExpErrorNoMatch; - } - - // Rules for backtracking differ depending on whether this is greedy or non-greedy. - // - // Greedy matches never should try just adding more - you should already have done - // the 'more' cases. Always backtrack, at least a leetle bit. However cases where - // you backtrack an item off the list needs checking, since we'll never have matched - // the one less case. Tracking forwards, still add as much as possible. - // - // Non-greedy, we've already done the one less case, so don't match on popping. - // We haven't done the one more case, so always try to add that. - // - JSRegExpResult backtrackParentheses(ByteTerm& term, DisjunctionContext* context) - { - ASSERT(term.type == ByteTerm::TypeParenthesesSubpattern); - - BackTrackInfoParentheses* backTrack = reinterpret_cast(context->frame + term.frameLocation); - ByteDisjunction* disjunctionBody = term.atom.parenthesesDisjunction; - - switch (term.atom.quantityType) { - case QuantifierFixedCount: { - ASSERT(backTrack->matchAmount == term.atom.quantityCount); - - ParenthesesDisjunctionContext* context = 0; - JSRegExpResult result = parenthesesDoBacktrack(term, backTrack); - - if (result != JSRegExpMatch) - return result; - - // While we haven't yet reached our fixed limit, - while (backTrack->matchAmount < term.atom.quantityCount) { - // Try to do a match, and it it succeeds, add it to the list. - context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); - result = matchDisjunction(disjunctionBody, context->getDisjunctionContext(term)); - - if (result == JSRegExpMatch) - appendParenthesesDisjunctionContext(backTrack, context); - else { - // The match failed; try to find an alternate point to carry on from. - resetMatches(term, context); - freeParenthesesDisjunctionContext(context); - - if (result != JSRegExpNoMatch) - return result; - JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack); - if (backtrackResult != JSRegExpMatch) - return backtrackResult; - } - } - - ASSERT(backTrack->matchAmount == term.atom.quantityCount); - context = backTrack->lastContext; - recordParenthesesMatch(term, context); - return JSRegExpMatch; - } - - case QuantifierGreedy: { - if (!backTrack->matchAmount) - return JSRegExpNoMatch; - - ParenthesesDisjunctionContext* context = backTrack->lastContext; - JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term), true); - if (result == JSRegExpMatch) { - while (backTrack->matchAmount < term.atom.quantityCount) { - ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); - JSRegExpResult parenthesesResult = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); - if (parenthesesResult == JSRegExpMatch) - appendParenthesesDisjunctionContext(backTrack, context); - else { - resetMatches(term, context); - freeParenthesesDisjunctionContext(context); - - if (parenthesesResult != JSRegExpNoMatch) - return parenthesesResult; - - break; - } - } - } else { - resetMatches(term, context); - popParenthesesDisjunctionContext(backTrack); - freeParenthesesDisjunctionContext(context); - - if (result != JSRegExpNoMatch) - return result; - } - - if (backTrack->matchAmount) { - ParenthesesDisjunctionContext* context = backTrack->lastContext; - recordParenthesesMatch(term, context); - } - return JSRegExpMatch; - } - - case QuantifierNonGreedy: { - // If we've not reached the limit, try to add one more match. - if (backTrack->matchAmount < term.atom.quantityCount) { - ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); - JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); - if (result == JSRegExpMatch) { - appendParenthesesDisjunctionContext(backTrack, context); - recordParenthesesMatch(term, context); - return JSRegExpMatch; - } - - resetMatches(term, context); - freeParenthesesDisjunctionContext(context); - - if (result != JSRegExpNoMatch) - return result; - } - - // Nope - okay backtrack looking for an alternative. - while (backTrack->matchAmount) { - ParenthesesDisjunctionContext* context = backTrack->lastContext; - JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term), true); - if (result == JSRegExpMatch) { - // successful backtrack! we're back in the game! - if (backTrack->matchAmount) { - context = backTrack->lastContext; - recordParenthesesMatch(term, context); - } - return JSRegExpMatch; - } - - // pop a match off the stack - resetMatches(term, context); - popParenthesesDisjunctionContext(backTrack); - freeParenthesesDisjunctionContext(context); - - if (result != JSRegExpNoMatch) - return result; - } - - return JSRegExpNoMatch; - } - } - - ASSERT_NOT_REACHED(); - return JSRegExpErrorNoMatch; - } - - bool matchDotStarEnclosure(ByteTerm& term, DisjunctionContext* context) - { - UNUSED_PARAM(term); - unsigned matchBegin = context->matchBegin; - - if (matchBegin) { - for (matchBegin--; true; matchBegin--) { - if (testCharacterClass(pattern->newlineCharacterClass, input.reread(matchBegin))) { - ++matchBegin; - break; - } - - if (!matchBegin) - break; - } - } - - unsigned matchEnd = input.getPos(); - - for (; (matchEnd != input.end()) - && (!testCharacterClass(pattern->newlineCharacterClass, input.reread(matchEnd))); matchEnd++) { } - - if (((matchBegin && term.anchors.m_bol) - || ((matchEnd != input.end()) && term.anchors.m_eol)) - && !pattern->m_multiline) - return false; - - context->matchBegin = matchBegin; - context->matchEnd = matchEnd; - return true; - } - -#define MATCH_NEXT() { ++context->term; goto matchAgain; } -#define BACKTRACK() { --context->term; goto backtrack; } -#define currentTerm() (disjunction->terms[context->term]) - JSRegExpResult matchDisjunction(ByteDisjunction* disjunction, DisjunctionContext* context, bool btrack = false) - { - if (!--remainingMatchCount) - return JSRegExpErrorHitLimit; - - if (btrack) - BACKTRACK(); - - context->matchBegin = input.getPos(); - context->term = 0; - - matchAgain: - ASSERT(context->term < static_cast(disjunction->terms.size())); - - switch (currentTerm().type) { - case ByteTerm::TypeSubpatternBegin: - MATCH_NEXT(); - case ByteTerm::TypeSubpatternEnd: - context->matchEnd = input.getPos(); - return JSRegExpMatch; - - case ByteTerm::TypeBodyAlternativeBegin: - MATCH_NEXT(); - case ByteTerm::TypeBodyAlternativeDisjunction: - case ByteTerm::TypeBodyAlternativeEnd: - context->matchEnd = input.getPos(); - return JSRegExpMatch; - - case ByteTerm::TypeAlternativeBegin: - MATCH_NEXT(); - case ByteTerm::TypeAlternativeDisjunction: - case ByteTerm::TypeAlternativeEnd: { - int offset = currentTerm().alternative.end; - BackTrackInfoAlternative* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); - backTrack->offset = offset; - context->term += offset; - MATCH_NEXT(); - } - - case ByteTerm::TypeAssertionBOL: - if (matchAssertionBOL(currentTerm())) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeAssertionEOL: - if (matchAssertionEOL(currentTerm())) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeAssertionWordBoundary: - if (matchAssertionWordBoundary(currentTerm())) - MATCH_NEXT(); - BACKTRACK(); - - case ByteTerm::TypePatternCharacterOnce: - case ByteTerm::TypePatternCharacterFixed: { - for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityCount; ++matchAmount) { - if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition - matchAmount)) - BACKTRACK(); - } - MATCH_NEXT(); - } - case ByteTerm::TypePatternCharacterGreedy: { - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); - unsigned matchAmount = 0; - while ((matchAmount < currentTerm().atom.quantityCount) && input.checkInput(1)) { - if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition + 1)) { - input.uncheckInput(1); - break; - } - ++matchAmount; - } - backTrack->matchAmount = matchAmount; - - MATCH_NEXT(); - } - case ByteTerm::TypePatternCharacterNonGreedy: { - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); - backTrack->matchAmount = 0; - MATCH_NEXT(); - } - - case ByteTerm::TypePatternCasedCharacterOnce: - case ByteTerm::TypePatternCasedCharacterFixed: { - for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityCount; ++matchAmount) { - if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition - matchAmount)) - BACKTRACK(); - } - MATCH_NEXT(); - } - case ByteTerm::TypePatternCasedCharacterGreedy: { - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); - unsigned matchAmount = 0; - while ((matchAmount < currentTerm().atom.quantityCount) && input.checkInput(1)) { - if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition + 1)) { - input.uncheckInput(1); - break; - } - ++matchAmount; - } - backTrack->matchAmount = matchAmount; - - MATCH_NEXT(); - } - case ByteTerm::TypePatternCasedCharacterNonGreedy: { - BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); - backTrack->matchAmount = 0; - MATCH_NEXT(); - } - - case ByteTerm::TypeCharacterClass: - if (matchCharacterClass(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeBackReference: - if (matchBackReference(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpattern: { - JSRegExpResult result = matchParentheses(currentTerm(), context); - - if (result == JSRegExpMatch) { - MATCH_NEXT(); - } else if (result != JSRegExpNoMatch) - return result; - - BACKTRACK(); - } - case ByteTerm::TypeParenthesesSubpatternOnceBegin: - if (matchParenthesesOnceBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternOnceEnd: - if (matchParenthesesOnceEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternTerminalBegin: - if (matchParenthesesTerminalBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternTerminalEnd: - if (matchParenthesesTerminalEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParentheticalAssertionBegin: - if (matchParentheticalAssertionBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParentheticalAssertionEnd: - if (matchParentheticalAssertionEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - - case ByteTerm::TypeCheckInput: - if (input.checkInput(currentTerm().checkInputCount)) - MATCH_NEXT(); - BACKTRACK(); - - case ByteTerm::TypeUncheckInput: - input.uncheckInput(currentTerm().checkInputCount); - MATCH_NEXT(); - - case ByteTerm::TypeDotStarEnclosure: - if (matchDotStarEnclosure(currentTerm(), context)) - return JSRegExpMatch; - BACKTRACK(); - } - - // We should never fall-through to here. - ASSERT_NOT_REACHED(); - - backtrack: - ASSERT(context->term < static_cast(disjunction->terms.size())); - - switch (currentTerm().type) { - case ByteTerm::TypeSubpatternBegin: - return JSRegExpNoMatch; - case ByteTerm::TypeSubpatternEnd: - ASSERT_NOT_REACHED(); - - case ByteTerm::TypeBodyAlternativeBegin: - case ByteTerm::TypeBodyAlternativeDisjunction: { - int offset = currentTerm().alternative.next; - context->term += offset; - if (offset > 0) - MATCH_NEXT(); - - if (input.atEnd()) - return JSRegExpNoMatch; - - input.next(); - - context->matchBegin = input.getPos(); - - if (currentTerm().alternative.onceThrough) - context->term += currentTerm().alternative.next; - - MATCH_NEXT(); - } - case ByteTerm::TypeBodyAlternativeEnd: - ASSERT_NOT_REACHED(); - - case ByteTerm::TypeAlternativeBegin: - case ByteTerm::TypeAlternativeDisjunction: { - int offset = currentTerm().alternative.next; - context->term += offset; - if (offset > 0) - MATCH_NEXT(); - BACKTRACK(); - } - case ByteTerm::TypeAlternativeEnd: { - // We should never backtrack back into an alternative of the main body of the regex. - BackTrackInfoAlternative* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); - unsigned offset = backTrack->offset; - context->term -= offset; - BACKTRACK(); - } - - case ByteTerm::TypeAssertionBOL: - case ByteTerm::TypeAssertionEOL: - case ByteTerm::TypeAssertionWordBoundary: - BACKTRACK(); - - case ByteTerm::TypePatternCharacterOnce: - case ByteTerm::TypePatternCharacterFixed: - case ByteTerm::TypePatternCharacterGreedy: - case ByteTerm::TypePatternCharacterNonGreedy: - if (backtrackPatternCharacter(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypePatternCasedCharacterOnce: - case ByteTerm::TypePatternCasedCharacterFixed: - case ByteTerm::TypePatternCasedCharacterGreedy: - case ByteTerm::TypePatternCasedCharacterNonGreedy: - if (backtrackPatternCasedCharacter(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeCharacterClass: - if (backtrackCharacterClass(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeBackReference: - if (backtrackBackReference(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpattern: { - JSRegExpResult result = backtrackParentheses(currentTerm(), context); - - if (result == JSRegExpMatch) { - MATCH_NEXT(); - } else if (result != JSRegExpNoMatch) - return result; - - BACKTRACK(); - } - case ByteTerm::TypeParenthesesSubpatternOnceBegin: - if (backtrackParenthesesOnceBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternOnceEnd: - if (backtrackParenthesesOnceEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternTerminalBegin: - if (backtrackParenthesesTerminalBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternTerminalEnd: - if (backtrackParenthesesTerminalEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParentheticalAssertionBegin: - if (backtrackParentheticalAssertionBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParentheticalAssertionEnd: - if (backtrackParentheticalAssertionEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - - case ByteTerm::TypeCheckInput: - input.uncheckInput(currentTerm().checkInputCount); - BACKTRACK(); - - case ByteTerm::TypeUncheckInput: - input.checkInput(currentTerm().checkInputCount); - BACKTRACK(); - - case ByteTerm::TypeDotStarEnclosure: - ASSERT_NOT_REACHED(); - } - - ASSERT_NOT_REACHED(); - return JSRegExpErrorNoMatch; - } - - JSRegExpResult matchNonZeroDisjunction(ByteDisjunction* disjunction, DisjunctionContext* context, bool btrack = false) - { - JSRegExpResult result = matchDisjunction(disjunction, context, btrack); - - if (result == JSRegExpMatch) { - while (context->matchBegin == context->matchEnd) { - result = matchDisjunction(disjunction, context, true); - if (result != JSRegExpMatch) - return result; - } - return JSRegExpMatch; - } - - return result; - } - - unsigned interpret() - { - if (!input.isAvailableInput(0)) - return offsetNoMatch; - - for (unsigned i = 0; i < pattern->m_body->m_numSubpatterns + 1; ++i) - output[i << 1] = offsetNoMatch; - - allocatorPool = pattern->m_allocator->startAllocator(); - if (!allocatorPool) - CRASH(); - - DisjunctionContext* context = allocDisjunctionContext(pattern->m_body.get()); - - JSRegExpResult result = matchDisjunction(pattern->m_body.get(), context, false); - if (result == JSRegExpMatch) { - output[0] = context->matchBegin; - output[1] = context->matchEnd; - } - - freeDisjunctionContext(context); - - pattern->m_allocator->stopAllocator(); - - ASSERT((result == JSRegExpMatch) == (output[0] != offsetNoMatch)); - return output[0]; - } - - Interpreter(BytecodePattern* pattern, unsigned* output, const CharType* input, unsigned length, unsigned start) - : pattern(pattern) - , output(output) - , input(input, start, length) - , allocatorPool(0) - , remainingMatchCount(matchLimit) - { - } - -private: - BytecodePattern* pattern; - unsigned* output; - InputStream input; - BumpPointerPool* allocatorPool; - unsigned remainingMatchCount; -}; - - - -class ByteCompiler { - struct ParenthesesStackEntry { - unsigned beginTerm; - unsigned savedAlternativeIndex; - ParenthesesStackEntry(unsigned beginTerm, unsigned savedAlternativeIndex/*, unsigned subpatternId, bool capture = false*/) - : beginTerm(beginTerm) - , savedAlternativeIndex(savedAlternativeIndex) - { - } - }; - -public: - ByteCompiler(YarrPattern& pattern) - : m_pattern(pattern) - { - m_currentAlternativeIndex = 0; - } - - PassOwnPtr compile(BumpPointerAllocator* allocator) - { - regexBegin(m_pattern.m_numSubpatterns, m_pattern.m_body->m_callFrameSize, m_pattern.m_body->m_alternatives[0]->onceThrough()); - emitDisjunction(m_pattern.m_body); - regexEnd(); - - return adoptPtr(new BytecodePattern(m_bodyDisjunction.release(), m_allParenthesesInfo, m_pattern, allocator)); - } - - void checkInput(unsigned count) - { - m_bodyDisjunction->terms.append(ByteTerm::CheckInput(count)); - } - - void uncheckInput(unsigned count) - { - m_bodyDisjunction->terms.append(ByteTerm::UncheckInput(count)); - } - - void assertionBOL(unsigned inputPosition) - { - m_bodyDisjunction->terms.append(ByteTerm::BOL(inputPosition)); - } - - void assertionEOL(unsigned inputPosition) - { - m_bodyDisjunction->terms.append(ByteTerm::EOL(inputPosition)); - } - - void assertionWordBoundary(bool invert, unsigned inputPosition) - { - m_bodyDisjunction->terms.append(ByteTerm::WordBoundary(invert, inputPosition)); - } - - void atomPatternCharacter(UChar ch, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - { - if (m_pattern.m_ignoreCase) { - UChar lo = Unicode::toLower(ch); - UChar hi = Unicode::toUpper(ch); - - if (lo != hi) { - m_bodyDisjunction->terms.append(ByteTerm(lo, hi, inputPosition, frameLocation, quantityCount, quantityType)); - return; - } - } - - m_bodyDisjunction->terms.append(ByteTerm(ch, inputPosition, frameLocation, quantityCount, quantityType)); - } - - void atomCharacterClass(CharacterClass* characterClass, bool invert, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - { - m_bodyDisjunction->terms.append(ByteTerm(characterClass, invert, inputPosition)); - - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType; - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; - } - - void atomBackReference(unsigned subpatternId, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - { - ASSERT(subpatternId); - - m_bodyDisjunction->terms.append(ByteTerm::BackReference(subpatternId, inputPosition)); - - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType; - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; - } - - void atomParenthesesOnceBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) - { - int beginTerm = m_bodyDisjunction->terms.size(); - - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceBegin, subpatternId, capture, false, inputPosition)); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; - m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; - - m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); - m_currentAlternativeIndex = beginTerm + 1; - } - - void atomParenthesesTerminalBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) - { - int beginTerm = m_bodyDisjunction->terms.size(); - - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternTerminalBegin, subpatternId, capture, false, inputPosition)); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; - m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; - - m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); - m_currentAlternativeIndex = beginTerm + 1; - } - - void atomParenthesesSubpatternBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) - { - // Errrk! - this is a little crazy, we initially generate as a TypeParenthesesSubpatternOnceBegin, - // then fix this up at the end! - simplifying this should make it much clearer. - // https://bugs.webkit.org/show_bug.cgi?id=50136 - - int beginTerm = m_bodyDisjunction->terms.size(); - - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceBegin, subpatternId, capture, false, inputPosition)); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; - m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; - - m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); - m_currentAlternativeIndex = beginTerm + 1; - } - - void atomParentheticalAssertionBegin(unsigned subpatternId, bool invert, unsigned frameLocation, unsigned alternativeFrameLocation) - { - int beginTerm = m_bodyDisjunction->terms.size(); - - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParentheticalAssertionBegin, subpatternId, false, invert, 0)); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; - m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; - - m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); - m_currentAlternativeIndex = beginTerm + 1; - } - - void atomParentheticalAssertionEnd(unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - { - unsigned beginTerm = popParenthesesStack(); - closeAlternative(beginTerm + 1); - unsigned endTerm = m_bodyDisjunction->terms.size(); - - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParentheticalAssertionBegin); - - bool invert = m_bodyDisjunction->terms[beginTerm].invert(); - unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; - - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParentheticalAssertionEnd, subpatternId, false, invert, inputPosition)); - m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; - m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; - m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; - - m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; - m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; - } - - void assertionDotStarEnclosure(bool bolAnchored, bool eolAnchored) - { - m_bodyDisjunction->terms.append(ByteTerm::DotStarEnclosure(bolAnchored, eolAnchored)); - } - - unsigned popParenthesesStack() - { - ASSERT(m_parenthesesStack.size()); - int stackEnd = m_parenthesesStack.size() - 1; - unsigned beginTerm = m_parenthesesStack[stackEnd].beginTerm; - m_currentAlternativeIndex = m_parenthesesStack[stackEnd].savedAlternativeIndex; - m_parenthesesStack.shrink(stackEnd); - - ASSERT(beginTerm < m_bodyDisjunction->terms.size()); - ASSERT(m_currentAlternativeIndex < m_bodyDisjunction->terms.size()); - - return beginTerm; - } - -#ifndef NDEBUG - void dumpDisjunction(ByteDisjunction* disjunction) - { - dataLogF("ByteDisjunction(%p):\n\t", disjunction); - for (unsigned i = 0; i < disjunction->terms.size(); ++i) - dataLogF("{ %d } ", disjunction->terms[i].type); - dataLogF("\n"); - } -#endif - - void closeAlternative(int beginTerm) - { - int origBeginTerm = beginTerm; - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeAlternativeBegin); - int endIndex = m_bodyDisjunction->terms.size(); - - unsigned frameLocation = m_bodyDisjunction->terms[beginTerm].frameLocation; - - if (!m_bodyDisjunction->terms[beginTerm].alternative.next) - m_bodyDisjunction->terms.remove(beginTerm); - else { - while (m_bodyDisjunction->terms[beginTerm].alternative.next) { - beginTerm += m_bodyDisjunction->terms[beginTerm].alternative.next; - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeAlternativeDisjunction); - m_bodyDisjunction->terms[beginTerm].alternative.end = endIndex - beginTerm; - m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; - } - - m_bodyDisjunction->terms[beginTerm].alternative.next = origBeginTerm - beginTerm; - - m_bodyDisjunction->terms.append(ByteTerm::AlternativeEnd()); - m_bodyDisjunction->terms[endIndex].frameLocation = frameLocation; - } - } - - void closeBodyAlternative() - { - int beginTerm = 0; - int origBeginTerm = 0; - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeBodyAlternativeBegin); - int endIndex = m_bodyDisjunction->terms.size(); - - unsigned frameLocation = m_bodyDisjunction->terms[beginTerm].frameLocation; - - while (m_bodyDisjunction->terms[beginTerm].alternative.next) { - beginTerm += m_bodyDisjunction->terms[beginTerm].alternative.next; - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeBodyAlternativeDisjunction); - m_bodyDisjunction->terms[beginTerm].alternative.end = endIndex - beginTerm; - m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; - } - - m_bodyDisjunction->terms[beginTerm].alternative.next = origBeginTerm - beginTerm; - - m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeEnd()); - m_bodyDisjunction->terms[endIndex].frameLocation = frameLocation; - } - - void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType, unsigned callFrameSize = 0) - { - unsigned beginTerm = popParenthesesStack(); - closeAlternative(beginTerm + 1); - unsigned endTerm = m_bodyDisjunction->terms.size(); - - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternOnceBegin); - - ByteTerm& parenthesesBegin = m_bodyDisjunction->terms[beginTerm]; - - bool capture = parenthesesBegin.capture(); - unsigned subpatternId = parenthesesBegin.atom.subpatternId; - - unsigned numSubpatterns = lastSubpatternId - subpatternId + 1; - ByteDisjunction* parenthesesDisjunction = new ByteDisjunction(numSubpatterns, callFrameSize); - - parenthesesDisjunction->terms.append(ByteTerm::SubpatternBegin()); - for (unsigned termInParentheses = beginTerm + 1; termInParentheses < endTerm; ++termInParentheses) - parenthesesDisjunction->terms.append(m_bodyDisjunction->terms[termInParentheses]); - parenthesesDisjunction->terms.append(ByteTerm::SubpatternEnd()); - - m_bodyDisjunction->terms.shrink(beginTerm); - - m_allParenthesesInfo.append(parenthesesDisjunction); - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture, inputPosition)); - - m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; - m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; - } - - void atomParenthesesOnceEnd(int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - { - unsigned beginTerm = popParenthesesStack(); - closeAlternative(beginTerm + 1); - unsigned endTerm = m_bodyDisjunction->terms.size(); - - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternOnceBegin); - - bool capture = m_bodyDisjunction->terms[beginTerm].capture(); - unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; - - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceEnd, subpatternId, capture, false, inputPosition)); - m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; - m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; - m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; - - m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; - m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; - } - - void atomParenthesesTerminalEnd(int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - { - unsigned beginTerm = popParenthesesStack(); - closeAlternative(beginTerm + 1); - unsigned endTerm = m_bodyDisjunction->terms.size(); - - ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); - - bool capture = m_bodyDisjunction->terms[beginTerm].capture(); - unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; - - m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternTerminalEnd, subpatternId, capture, false, inputPosition)); - m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; - m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; - m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; - - m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; - m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); - m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; - } - - void regexBegin(unsigned numSubpatterns, unsigned callFrameSize, bool onceThrough) - { - m_bodyDisjunction = adoptPtr(new ByteDisjunction(numSubpatterns, callFrameSize)); - m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeBegin(onceThrough)); - m_bodyDisjunction->terms[0].frameLocation = 0; - m_currentAlternativeIndex = 0; - } - - void regexEnd() - { - closeBodyAlternative(); - } - - void alternativeBodyDisjunction(bool onceThrough) - { - int newAlternativeIndex = m_bodyDisjunction->terms.size(); - m_bodyDisjunction->terms[m_currentAlternativeIndex].alternative.next = newAlternativeIndex - m_currentAlternativeIndex; - m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeDisjunction(onceThrough)); - - m_currentAlternativeIndex = newAlternativeIndex; - } - - void alternativeDisjunction() - { - int newAlternativeIndex = m_bodyDisjunction->terms.size(); - m_bodyDisjunction->terms[m_currentAlternativeIndex].alternative.next = newAlternativeIndex - m_currentAlternativeIndex; - m_bodyDisjunction->terms.append(ByteTerm::AlternativeDisjunction()); - - m_currentAlternativeIndex = newAlternativeIndex; - } - - void emitDisjunction(PatternDisjunction* disjunction, unsigned inputCountAlreadyChecked = 0, unsigned parenthesesInputCountAlreadyChecked = 0) - { - for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { - unsigned currentCountAlreadyChecked = inputCountAlreadyChecked; - - PatternAlternative* alternative = disjunction->m_alternatives[alt]; - - if (alt) { - if (disjunction == m_pattern.m_body) - alternativeBodyDisjunction(alternative->onceThrough()); - else - alternativeDisjunction(); - } - - unsigned minimumSize = alternative->m_minimumSize; - ASSERT(minimumSize >= parenthesesInputCountAlreadyChecked); - unsigned countToCheck = minimumSize - parenthesesInputCountAlreadyChecked; - - if (countToCheck) { - checkInput(countToCheck); - currentCountAlreadyChecked += countToCheck; - } - - for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { - PatternTerm& term = alternative->m_terms[i]; - - switch (term.type) { - case PatternTerm::TypeAssertionBOL: - assertionBOL(currentCountAlreadyChecked - term.inputPosition); - break; - - case PatternTerm::TypeAssertionEOL: - assertionEOL(currentCountAlreadyChecked - term.inputPosition); - break; - - case PatternTerm::TypeAssertionWordBoundary: - assertionWordBoundary(term.invert(), currentCountAlreadyChecked - term.inputPosition); - break; - - case PatternTerm::TypePatternCharacter: - atomPatternCharacter(term.patternCharacter, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); - break; - - case PatternTerm::TypeCharacterClass: - atomCharacterClass(term.characterClass, term.invert(), currentCountAlreadyChecked- term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); - break; - - case PatternTerm::TypeBackReference: - atomBackReference(term.backReferenceSubpatternId, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); - break; - - case PatternTerm::TypeForwardReference: - break; - - case PatternTerm::TypeParenthesesSubpattern: { - unsigned disjunctionAlreadyCheckedCount = 0; - if (term.quantityCount == 1 && !term.parentheses.isCopy) { - unsigned alternativeFrameLocation = term.frameLocation; - // For QuantifierFixedCount we pre-check the minimum size; for greedy/non-greedy we reserve a slot in the frame. - if (term.quantityType == QuantifierFixedCount) - disjunctionAlreadyCheckedCount = term.parentheses.disjunction->m_minimumSize; - else - alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; - unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; - atomParenthesesOnceBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, alternativeFrameLocation); - emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount); - atomParenthesesOnceEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType); - } else if (term.parentheses.isTerminal) { - unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; - atomParenthesesTerminalBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, term.frameLocation + YarrStackSpaceForBackTrackInfoParenthesesOnce); - emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount); - atomParenthesesTerminalEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType); - } else { - unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; - atomParenthesesSubpatternBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, 0); - emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, 0); - atomParenthesesSubpatternEnd(term.parentheses.lastSubpatternId, delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType, term.parentheses.disjunction->m_callFrameSize); - } - break; - } - - case PatternTerm::TypeParentheticalAssertion: { - unsigned alternativeFrameLocation = term.frameLocation + YarrStackSpaceForBackTrackInfoParentheticalAssertion; - - ASSERT(currentCountAlreadyChecked >= static_cast(term.inputPosition)); - unsigned positiveInputOffset = currentCountAlreadyChecked - static_cast(term.inputPosition); - unsigned uncheckAmount = 0; - if (positiveInputOffset > term.parentheses.disjunction->m_minimumSize) { - uncheckAmount = positiveInputOffset - term.parentheses.disjunction->m_minimumSize; - uncheckInput(uncheckAmount); - currentCountAlreadyChecked -= uncheckAmount; - } - - atomParentheticalAssertionBegin(term.parentheses.subpatternId, term.invert(), term.frameLocation, alternativeFrameLocation); - emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, positiveInputOffset - uncheckAmount); - atomParentheticalAssertionEnd(0, term.frameLocation, term.quantityCount, term.quantityType); - if (uncheckAmount) { - checkInput(uncheckAmount); - currentCountAlreadyChecked += uncheckAmount; - } - break; - } - - case PatternTerm::TypeDotStarEnclosure: - assertionDotStarEnclosure(term.anchors.bolAnchor, term.anchors.eolAnchor); - break; - } - } - } - } - -private: - YarrPattern& m_pattern; - OwnPtr m_bodyDisjunction; - unsigned m_currentAlternativeIndex; - Vector m_parenthesesStack; - Vector m_allParenthesesInfo; -}; - -PassOwnPtr byteCompile(YarrPattern& pattern, BumpPointerAllocator* allocator) -{ - return ByteCompiler(pattern).compile(allocator); -} - -unsigned interpret(BytecodePattern* bytecode, const String& input, unsigned start, unsigned* output) -{ - if (input.is8Bit()) - return Interpreter(bytecode, output, input.characters8(), input.length(), start).interpret(); - return Interpreter(bytecode, output, input.characters16(), input.length(), start).interpret(); -} - -unsigned interpret(BytecodePattern* bytecode, const LChar* input, unsigned length, unsigned start, unsigned* output) -{ - return Interpreter(bytecode, output, input, length, start).interpret(); -} - -unsigned interpret(BytecodePattern* bytecode, const UChar* input, unsigned length, unsigned start, unsigned* output) -{ - return Interpreter(bytecode, output, input, length, start).interpret(); -} - -// These should be the same for both UChar & LChar. -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoPatternCharacter) == (YarrStackSpaceForBackTrackInfoPatternCharacter * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoPatternCharacter); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoCharacterClass) == (YarrStackSpaceForBackTrackInfoCharacterClass * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoCharacterClass); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoBackReference) == (YarrStackSpaceForBackTrackInfoBackReference * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoBackReference); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoAlternative) == (YarrStackSpaceForBackTrackInfoAlternative * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoAlternative); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParentheticalAssertion) == (YarrStackSpaceForBackTrackInfoParentheticalAssertion * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheticalAssertion); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParenthesesOnce) == (YarrStackSpaceForBackTrackInfoParenthesesOnce * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParenthesesOnce); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParentheses) == (YarrStackSpaceForBackTrackInfoParentheses * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheses); - - -} } diff --git a/3rdparty/masm/yarr/YarrInterpreter.h b/3rdparty/masm/yarr/YarrInterpreter.h deleted file mode 100644 index fb60bd979d..0000000000 --- a/3rdparty/masm/yarr/YarrInterpreter.h +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef YarrInterpreter_h -#define YarrInterpreter_h - -#include "YarrPattern.h" -#include -#include - -namespace WTF { -class BumpPointerAllocator; -} -using WTF::BumpPointerAllocator; - -namespace JSC { namespace Yarr { - -class ByteDisjunction; - -struct ByteTerm { - enum Type { - TypeBodyAlternativeBegin, - TypeBodyAlternativeDisjunction, - TypeBodyAlternativeEnd, - TypeAlternativeBegin, - TypeAlternativeDisjunction, - TypeAlternativeEnd, - TypeSubpatternBegin, - TypeSubpatternEnd, - TypeAssertionBOL, - TypeAssertionEOL, - TypeAssertionWordBoundary, - TypePatternCharacterOnce, - TypePatternCharacterFixed, - TypePatternCharacterGreedy, - TypePatternCharacterNonGreedy, - TypePatternCasedCharacterOnce, - TypePatternCasedCharacterFixed, - TypePatternCasedCharacterGreedy, - TypePatternCasedCharacterNonGreedy, - TypeCharacterClass, - TypeBackReference, - TypeParenthesesSubpattern, - TypeParenthesesSubpatternOnceBegin, - TypeParenthesesSubpatternOnceEnd, - TypeParenthesesSubpatternTerminalBegin, - TypeParenthesesSubpatternTerminalEnd, - TypeParentheticalAssertionBegin, - TypeParentheticalAssertionEnd, - TypeCheckInput, - TypeUncheckInput, - TypeDotStarEnclosure, - } type; - union { - struct { - union { - UChar patternCharacter; - struct { - UChar lo; - UChar hi; - } casedCharacter; - CharacterClass* characterClass; - unsigned subpatternId; - }; - union { - ByteDisjunction* parenthesesDisjunction; - unsigned parenthesesWidth; - }; - QuantifierType quantityType; - unsigned quantityCount; - } atom; - struct { - int next; - int end; - bool onceThrough; - } alternative; - struct { - bool m_bol : 1; - bool m_eol : 1; - } anchors; - unsigned checkInputCount; - }; - unsigned frameLocation; - bool m_capture : 1; - bool m_invert : 1; - unsigned inputPosition; - - ByteTerm(UChar ch, int inputPos, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - : frameLocation(frameLocation) - , m_capture(false) - , m_invert(false) - { - switch (quantityType) { - case QuantifierFixedCount: - type = (quantityCount == 1) ? ByteTerm::TypePatternCharacterOnce : ByteTerm::TypePatternCharacterFixed; - break; - case QuantifierGreedy: - type = ByteTerm::TypePatternCharacterGreedy; - break; - case QuantifierNonGreedy: - type = ByteTerm::TypePatternCharacterNonGreedy; - break; - } - - atom.patternCharacter = ch; - atom.quantityType = quantityType; - atom.quantityCount = quantityCount.unsafeGet(); - inputPosition = inputPos; - } - - ByteTerm(UChar lo, UChar hi, int inputPos, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) - : frameLocation(frameLocation) - , m_capture(false) - , m_invert(false) - { - switch (quantityType) { - case QuantifierFixedCount: - type = (quantityCount == 1) ? ByteTerm::TypePatternCasedCharacterOnce : ByteTerm::TypePatternCasedCharacterFixed; - break; - case QuantifierGreedy: - type = ByteTerm::TypePatternCasedCharacterGreedy; - break; - case QuantifierNonGreedy: - type = ByteTerm::TypePatternCasedCharacterNonGreedy; - break; - } - - atom.casedCharacter.lo = lo; - atom.casedCharacter.hi = hi; - atom.quantityType = quantityType; - atom.quantityCount = quantityCount.unsafeGet(); - inputPosition = inputPos; - } - - ByteTerm(CharacterClass* characterClass, bool invert, int inputPos) - : type(ByteTerm::TypeCharacterClass) - , m_capture(false) - , m_invert(invert) - { - atom.characterClass = characterClass; - atom.quantityType = QuantifierFixedCount; - atom.quantityCount = 1; - inputPosition = inputPos; - } - - ByteTerm(Type type, unsigned subpatternId, ByteDisjunction* parenthesesInfo, bool capture, int inputPos) - : type(type) - , m_capture(capture) - , m_invert(false) - { - atom.subpatternId = subpatternId; - atom.parenthesesDisjunction = parenthesesInfo; - atom.quantityType = QuantifierFixedCount; - atom.quantityCount = 1; - inputPosition = inputPos; - } - - ByteTerm(Type type, bool invert = false) - : type(type) - , m_capture(false) - , m_invert(invert) - { - atom.quantityType = QuantifierFixedCount; - atom.quantityCount = 1; - } - - ByteTerm(Type type, unsigned subpatternId, bool capture, bool invert, int inputPos) - : type(type) - , m_capture(capture) - , m_invert(invert) - { - atom.subpatternId = subpatternId; - atom.quantityType = QuantifierFixedCount; - atom.quantityCount = 1; - inputPosition = inputPos; - } - - static ByteTerm BOL(int inputPos) - { - ByteTerm term(TypeAssertionBOL); - term.inputPosition = inputPos; - return term; - } - - static ByteTerm CheckInput(Checked count) - { - ByteTerm term(TypeCheckInput); - term.checkInputCount = count.unsafeGet(); - return term; - } - - static ByteTerm UncheckInput(Checked count) - { - ByteTerm term(TypeUncheckInput); - term.checkInputCount = count.unsafeGet(); - return term; - } - - static ByteTerm EOL(int inputPos) - { - ByteTerm term(TypeAssertionEOL); - term.inputPosition = inputPos; - return term; - } - - static ByteTerm WordBoundary(bool invert, int inputPos) - { - ByteTerm term(TypeAssertionWordBoundary, invert); - term.inputPosition = inputPos; - return term; - } - - static ByteTerm BackReference(unsigned subpatternId, int inputPos) - { - return ByteTerm(TypeBackReference, subpatternId, false, false, inputPos); - } - - static ByteTerm BodyAlternativeBegin(bool onceThrough) - { - ByteTerm term(TypeBodyAlternativeBegin); - term.alternative.next = 0; - term.alternative.end = 0; - term.alternative.onceThrough = onceThrough; - return term; - } - - static ByteTerm BodyAlternativeDisjunction(bool onceThrough) - { - ByteTerm term(TypeBodyAlternativeDisjunction); - term.alternative.next = 0; - term.alternative.end = 0; - term.alternative.onceThrough = onceThrough; - return term; - } - - static ByteTerm BodyAlternativeEnd() - { - ByteTerm term(TypeBodyAlternativeEnd); - term.alternative.next = 0; - term.alternative.end = 0; - term.alternative.onceThrough = false; - return term; - } - - static ByteTerm AlternativeBegin() - { - ByteTerm term(TypeAlternativeBegin); - term.alternative.next = 0; - term.alternative.end = 0; - term.alternative.onceThrough = false; - return term; - } - - static ByteTerm AlternativeDisjunction() - { - ByteTerm term(TypeAlternativeDisjunction); - term.alternative.next = 0; - term.alternative.end = 0; - term.alternative.onceThrough = false; - return term; - } - - static ByteTerm AlternativeEnd() - { - ByteTerm term(TypeAlternativeEnd); - term.alternative.next = 0; - term.alternative.end = 0; - term.alternative.onceThrough = false; - return term; - } - - static ByteTerm SubpatternBegin() - { - return ByteTerm(TypeSubpatternBegin); - } - - static ByteTerm SubpatternEnd() - { - return ByteTerm(TypeSubpatternEnd); - } - - static ByteTerm DotStarEnclosure(bool bolAnchor, bool eolAnchor) - { - ByteTerm term(TypeDotStarEnclosure); - term.anchors.m_bol = bolAnchor; - term.anchors.m_eol = eolAnchor; - return term; - } - - bool invert() - { - return m_invert; - } - - bool capture() - { - return m_capture; - } -}; - -class ByteDisjunction { - WTF_MAKE_FAST_ALLOCATED; -public: - ByteDisjunction(unsigned numSubpatterns, unsigned frameSize) - : m_numSubpatterns(numSubpatterns) - , m_frameSize(frameSize) - { - } - - Vector terms; - unsigned m_numSubpatterns; - unsigned m_frameSize; -}; - -struct BytecodePattern { - WTF_MAKE_FAST_ALLOCATED; -public: - BytecodePattern(PassOwnPtr body, Vector allParenthesesInfo, YarrPattern& pattern, BumpPointerAllocator* allocator) - : m_body(body) - , m_ignoreCase(pattern.m_ignoreCase) - , m_multiline(pattern.m_multiline) - , m_allocator(allocator) - { - newlineCharacterClass = pattern.newlineCharacterClass(); - wordcharCharacterClass = pattern.wordcharCharacterClass(); - - m_allParenthesesInfo.append(allParenthesesInfo); - m_userCharacterClasses.append(pattern.m_userCharacterClasses); - // 'Steal' the YarrPattern's CharacterClasses! We clear its - // array, so that it won't delete them on destruction. We'll - // take responsibility for that. - pattern.m_userCharacterClasses.clear(); - } - - ~BytecodePattern() - { - deleteAllValues(m_allParenthesesInfo); - deleteAllValues(m_userCharacterClasses); - } - - OwnPtr m_body; - bool m_ignoreCase; - bool m_multiline; - // Each BytecodePattern is associated with a RegExp, each RegExp is associated - // with a JSGlobalData. Cache a pointer to out JSGlobalData's m_regExpAllocator. - BumpPointerAllocator* m_allocator; - - CharacterClass* newlineCharacterClass; - CharacterClass* wordcharCharacterClass; - -private: - Vector m_allParenthesesInfo; - Vector m_userCharacterClasses; -}; - -JS_EXPORT_PRIVATE PassOwnPtr byteCompile(YarrPattern&, BumpPointerAllocator*); -JS_EXPORT_PRIVATE unsigned interpret(BytecodePattern*, const String& input, unsigned start, unsigned* output); -unsigned interpret(BytecodePattern*, const LChar* input, unsigned length, unsigned start, unsigned* output); -unsigned interpret(BytecodePattern*, const UChar* input, unsigned length, unsigned start, unsigned* output); - -} } // namespace JSC::Yarr - -#endif // YarrInterpreter_h diff --git a/3rdparty/masm/yarr/YarrJIT.cpp b/3rdparty/masm/yarr/YarrJIT.cpp deleted file mode 100644 index ce84e2c74f..0000000000 --- a/3rdparty/masm/yarr/YarrJIT.cpp +++ /dev/null @@ -1,2667 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "YarrJIT.h" - -#include -#include "LinkBuffer.h" -#include "Options.h" -#include "Yarr.h" -#include "YarrCanonicalizeUCS2.h" - -#if ENABLE(YARR_JIT) - -using namespace WTF; - -namespace JSC { namespace Yarr { - -template -class YarrGenerator : private MacroAssembler { - friend void jitCompile(JSGlobalData*, YarrCodeBlock& jitObject, const String& pattern, unsigned& numSubpatterns, const char*& error, bool ignoreCase, bool multiline); - -#if CPU(ARM) - static const RegisterID input = ARMRegisters::r0; - static const RegisterID index = ARMRegisters::r1; - static const RegisterID length = ARMRegisters::r2; - static const RegisterID output = ARMRegisters::r4; - - static const RegisterID regT0 = ARMRegisters::r5; - static const RegisterID regT1 = ARMRegisters::r6; - - static const RegisterID returnRegister = ARMRegisters::r0; - static const RegisterID returnRegister2 = ARMRegisters::r1; -#elif CPU(MIPS) - static const RegisterID input = MIPSRegisters::a0; - static const RegisterID index = MIPSRegisters::a1; - static const RegisterID length = MIPSRegisters::a2; - static const RegisterID output = MIPSRegisters::a3; - - static const RegisterID regT0 = MIPSRegisters::t4; - static const RegisterID regT1 = MIPSRegisters::t5; - - static const RegisterID returnRegister = MIPSRegisters::v0; - static const RegisterID returnRegister2 = MIPSRegisters::v1; -#elif CPU(SH4) - static const RegisterID input = SH4Registers::r4; - static const RegisterID index = SH4Registers::r5; - static const RegisterID length = SH4Registers::r6; - static const RegisterID output = SH4Registers::r7; - - static const RegisterID regT0 = SH4Registers::r0; - static const RegisterID regT1 = SH4Registers::r1; - - static const RegisterID returnRegister = SH4Registers::r0; - static const RegisterID returnRegister2 = SH4Registers::r1; -#elif CPU(X86) - static const RegisterID input = X86Registers::eax; - static const RegisterID index = X86Registers::edx; - static const RegisterID length = X86Registers::ecx; - static const RegisterID output = X86Registers::edi; - - static const RegisterID regT0 = X86Registers::ebx; - static const RegisterID regT1 = X86Registers::esi; - - static const RegisterID returnRegister = X86Registers::eax; - static const RegisterID returnRegister2 = X86Registers::edx; -#elif CPU(X86_64) - static const RegisterID input = X86Registers::edi; - static const RegisterID index = X86Registers::esi; - static const RegisterID length = X86Registers::edx; - static const RegisterID output = X86Registers::ecx; - - static const RegisterID regT0 = X86Registers::eax; - static const RegisterID regT1 = X86Registers::ebx; - - static const RegisterID returnRegister = X86Registers::eax; - static const RegisterID returnRegister2 = X86Registers::edx; -#endif - - void optimizeAlternative(PatternAlternative* alternative) - { - if (!alternative->m_terms.size()) - return; - - for (unsigned i = 0; i < alternative->m_terms.size() - 1; ++i) { - PatternTerm& term = alternative->m_terms[i]; - PatternTerm& nextTerm = alternative->m_terms[i + 1]; - - if ((term.type == PatternTerm::TypeCharacterClass) - && (term.quantityType == QuantifierFixedCount) - && (nextTerm.type == PatternTerm::TypePatternCharacter) - && (nextTerm.quantityType == QuantifierFixedCount)) { - PatternTerm termCopy = term; - alternative->m_terms[i] = nextTerm; - alternative->m_terms[i + 1] = termCopy; - } - } - } - - void matchCharacterClassRange(RegisterID character, JumpList& failures, JumpList& matchDest, const CharacterRange* ranges, unsigned count, unsigned* matchIndex, const UChar* matches, unsigned matchCount) - { - do { - // pick which range we're going to generate - int which = count >> 1; - char lo = ranges[which].begin; - char hi = ranges[which].end; - - // check if there are any ranges or matches below lo. If not, just jl to failure - - // if there is anything else to check, check that first, if it falls through jmp to failure. - if ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) { - Jump loOrAbove = branch32(GreaterThanOrEqual, character, Imm32((unsigned short)lo)); - - // generate code for all ranges before this one - if (which) - matchCharacterClassRange(character, failures, matchDest, ranges, which, matchIndex, matches, matchCount); - - while ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) { - matchDest.append(branch32(Equal, character, Imm32((unsigned short)matches[*matchIndex]))); - ++*matchIndex; - } - failures.append(jump()); - - loOrAbove.link(this); - } else if (which) { - Jump loOrAbove = branch32(GreaterThanOrEqual, character, Imm32((unsigned short)lo)); - - matchCharacterClassRange(character, failures, matchDest, ranges, which, matchIndex, matches, matchCount); - failures.append(jump()); - - loOrAbove.link(this); - } else - failures.append(branch32(LessThan, character, Imm32((unsigned short)lo))); - - while ((*matchIndex < matchCount) && (matches[*matchIndex] <= hi)) - ++*matchIndex; - - matchDest.append(branch32(LessThanOrEqual, character, Imm32((unsigned short)hi))); - // fall through to here, the value is above hi. - - // shuffle along & loop around if there are any more matches to handle. - unsigned next = which + 1; - ranges += next; - count -= next; - } while (count); - } - - void matchCharacterClass(RegisterID character, JumpList& matchDest, const CharacterClass* charClass) - { - if (charClass->m_table) { - ExtendedAddress tableEntry(character, reinterpret_cast(charClass->m_table->m_table)); - matchDest.append(branchTest8(charClass->m_table->m_inverted ? Zero : NonZero, tableEntry)); - return; - } - Jump unicodeFail; - if (charClass->m_matchesUnicode.size() || charClass->m_rangesUnicode.size()) { - Jump isAscii = branch32(LessThanOrEqual, character, TrustedImm32(0x7f)); - - if (charClass->m_matchesUnicode.size()) { - for (unsigned i = 0; i < charClass->m_matchesUnicode.size(); ++i) { - UChar ch = charClass->m_matchesUnicode[i]; - matchDest.append(branch32(Equal, character, Imm32(ch))); - } - } - - if (charClass->m_rangesUnicode.size()) { - for (unsigned i = 0; i < charClass->m_rangesUnicode.size(); ++i) { - UChar lo = charClass->m_rangesUnicode[i].begin; - UChar hi = charClass->m_rangesUnicode[i].end; - - Jump below = branch32(LessThan, character, Imm32(lo)); - matchDest.append(branch32(LessThanOrEqual, character, Imm32(hi))); - below.link(this); - } - } - - unicodeFail = jump(); - isAscii.link(this); - } - - if (charClass->m_ranges.size()) { - unsigned matchIndex = 0; - JumpList failures; - matchCharacterClassRange(character, failures, matchDest, charClass->m_ranges.begin(), charClass->m_ranges.size(), &matchIndex, charClass->m_matches.begin(), charClass->m_matches.size()); - while (matchIndex < charClass->m_matches.size()) - matchDest.append(branch32(Equal, character, Imm32((unsigned short)charClass->m_matches[matchIndex++]))); - - failures.link(this); - } else if (charClass->m_matches.size()) { - // optimization: gather 'a','A' etc back together, can mask & test once. - Vector matchesAZaz; - - for (unsigned i = 0; i < charClass->m_matches.size(); ++i) { - char ch = charClass->m_matches[i]; - if (m_pattern.m_ignoreCase) { - if (isASCIILower(ch)) { - matchesAZaz.append(ch); - continue; - } - if (isASCIIUpper(ch)) - continue; - } - matchDest.append(branch32(Equal, character, Imm32((unsigned short)ch))); - } - - if (unsigned countAZaz = matchesAZaz.size()) { - or32(TrustedImm32(32), character); - for (unsigned i = 0; i < countAZaz; ++i) - matchDest.append(branch32(Equal, character, TrustedImm32(matchesAZaz[i]))); - } - } - - if (charClass->m_matchesUnicode.size() || charClass->m_rangesUnicode.size()) - unicodeFail.link(this); - } - - // Jumps if input not available; will have (incorrectly) incremented already! - Jump jumpIfNoAvailableInput(unsigned countToCheck = 0) - { - if (countToCheck) - add32(Imm32(countToCheck), index); - return branch32(Above, index, length); - } - - Jump jumpIfAvailableInput(unsigned countToCheck) - { - add32(Imm32(countToCheck), index); - return branch32(BelowOrEqual, index, length); - } - - Jump checkInput() - { - return branch32(BelowOrEqual, index, length); - } - - Jump atEndOfInput() - { - return branch32(Equal, index, length); - } - - Jump notAtEndOfInput() - { - return branch32(NotEqual, index, length); - } - - Jump jumpIfCharNotEquals(UChar ch, int inputPosition, RegisterID character) - { - readCharacter(inputPosition, character); - - // For case-insesitive compares, non-ascii characters that have different - // upper & lower case representations are converted to a character class. - ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); - if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { - or32(TrustedImm32(0x20), character); - ch |= 0x20; - } - - return branch32(NotEqual, character, Imm32(ch)); - } - - void readCharacter(int inputPosition, RegisterID reg) - { - if (m_charSize == Char8) - load8(BaseIndex(input, index, TimesOne, inputPosition * sizeof(char)), reg); - else - load16(BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), reg); - } - - void storeToFrame(RegisterID reg, unsigned frameLocation) - { - poke(reg, frameLocation); - } - - void storeToFrame(TrustedImm32 imm, unsigned frameLocation) - { - poke(imm, frameLocation); - } - - DataLabelPtr storeToFrameWithPatch(unsigned frameLocation) - { - return storePtrWithPatch(TrustedImmPtr(0), Address(stackPointerRegister, frameLocation * sizeof(void*))); - } - - void loadFromFrame(unsigned frameLocation, RegisterID reg) - { - peek(reg, frameLocation); - } - - void loadFromFrameAndJump(unsigned frameLocation) - { - jump(Address(stackPointerRegister, frameLocation * sizeof(void*))); - } - - void initCallFrame() - { - unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; - if (callFrameSize) - subPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); - } - void removeCallFrame() - { - unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; - if (callFrameSize) - addPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); - } - - // Used to record subpatters, should only be called if compileMode is IncludeSubpatterns. - void setSubpatternStart(RegisterID reg, unsigned subpattern) - { - ASSERT(subpattern); - // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( - store32(reg, Address(output, (subpattern << 1) * sizeof(int))); - } - void setSubpatternEnd(RegisterID reg, unsigned subpattern) - { - ASSERT(subpattern); - // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( - store32(reg, Address(output, ((subpattern << 1) + 1) * sizeof(int))); - } - void clearSubpatternStart(unsigned subpattern) - { - ASSERT(subpattern); - // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( - store32(TrustedImm32(-1), Address(output, (subpattern << 1) * sizeof(int))); - } - - // We use one of three different strategies to track the start of the current match, - // while matching. - // 1) If the pattern has a fixed size, do nothing! - we calculate the value lazily - // at the end of matching. This is irrespective of compileMode, and in this case - // these methods should never be called. - // 2) If we're compiling IncludeSubpatterns, 'output' contains a pointer to an output - // vector, store the match start in the output vector. - // 3) If we're compiling MatchOnly, 'output' is unused, store the match start directly - // in this register. - void setMatchStart(RegisterID reg) - { - ASSERT(!m_pattern.m_body->m_hasFixedSize); - if (compileMode == IncludeSubpatterns) - store32(reg, output); - else - move(reg, output); - } - void getMatchStart(RegisterID reg) - { - ASSERT(!m_pattern.m_body->m_hasFixedSize); - if (compileMode == IncludeSubpatterns) - load32(output, reg); - else - move(output, reg); - } - - enum YarrOpCode { - // These nodes wrap body alternatives - those in the main disjunction, - // rather than subpatterns or assertions. These are chained together in - // a doubly linked list, with a 'begin' node for the first alternative, - // a 'next' node for each subsequent alternative, and an 'end' node at - // the end. In the case of repeating alternatives, the 'end' node also - // has a reference back to 'begin'. - OpBodyAlternativeBegin, - OpBodyAlternativeNext, - OpBodyAlternativeEnd, - // Similar to the body alternatives, but used for subpatterns with two - // or more alternatives. - OpNestedAlternativeBegin, - OpNestedAlternativeNext, - OpNestedAlternativeEnd, - // Used for alternatives in subpatterns where there is only a single - // alternative (backtrackingis easier in these cases), or for alternatives - // which never need to be backtracked (those in parenthetical assertions, - // terminal subpatterns). - OpSimpleNestedAlternativeBegin, - OpSimpleNestedAlternativeNext, - OpSimpleNestedAlternativeEnd, - // Used to wrap 'Once' subpattern matches (quantityCount == 1). - OpParenthesesSubpatternOnceBegin, - OpParenthesesSubpatternOnceEnd, - // Used to wrap 'Terminal' subpattern matches (at the end of the regexp). - OpParenthesesSubpatternTerminalBegin, - OpParenthesesSubpatternTerminalEnd, - // Used to wrap parenthetical assertions. - OpParentheticalAssertionBegin, - OpParentheticalAssertionEnd, - // Wraps all simple terms (pattern characters, character classes). - OpTerm, - // Where an expression contains only 'once through' body alternatives - // and no repeating ones, this op is used to return match failure. - OpMatchFailed - }; - - // This structure is used to hold the compiled opcode information, - // including reference back to the original PatternTerm/PatternAlternatives, - // and JIT compilation data structures. - struct YarrOp { - explicit YarrOp(PatternTerm* term) - : m_op(OpTerm) - , m_term(term) - , m_isDeadCode(false) - { - } - - explicit YarrOp(YarrOpCode op) - : m_op(op) - , m_isDeadCode(false) - { - } - - // The operation, as a YarrOpCode, and also a reference to the PatternTerm. - YarrOpCode m_op; - PatternTerm* m_term; - - // For alternatives, this holds the PatternAlternative and doubly linked - // references to this alternative's siblings. In the case of the - // OpBodyAlternativeEnd node at the end of a section of repeating nodes, - // m_nextOp will reference the OpBodyAlternativeBegin node of the first - // repeating alternative. - PatternAlternative* m_alternative; - size_t m_previousOp; - size_t m_nextOp; - - // Used to record a set of Jumps out of the generated code, typically - // used for jumps out to backtracking code, and a single reentry back - // into the code for a node (likely where a backtrack will trigger - // rematching). - Label m_reentry; - JumpList m_jumps; - - // Used for backtracking when the prior alternative did not consume any - // characters but matched. - Jump m_zeroLengthMatch; - - // This flag is used to null out the second pattern character, when - // two are fused to match a pair together. - bool m_isDeadCode; - - // Currently used in the case of some of the more complex management of - // 'm_checked', to cache the offset used in this alternative, to avoid - // recalculating it. - int m_checkAdjust; - - // Used by OpNestedAlternativeNext/End to hold the pointer to the - // value that will be pushed into the pattern's frame to return to, - // upon backtracking back into the disjunction. - DataLabelPtr m_returnAddress; - }; - - // BacktrackingState - // This class encapsulates information about the state of code generation - // whilst generating the code for backtracking, when a term fails to match. - // Upon entry to code generation of the backtracking code for a given node, - // the Backtracking state will hold references to all control flow sources - // that are outputs in need of further backtracking from the prior node - // generated (which is the subsequent operation in the regular expression, - // and in the m_ops Vector, since we generated backtracking backwards). - // These references to control flow take the form of: - // - A jump list of jumps, to be linked to code that will backtrack them - // further. - // - A set of DataLabelPtr values, to be populated with values to be - // treated effectively as return addresses backtracking into complex - // subpatterns. - // - A flag indicating that the current sequence of generated code up to - // this point requires backtracking. - class BacktrackingState { - public: - BacktrackingState() - : m_pendingFallthrough(false) - { - } - - // Add a jump or jumps, a return address, or set the flag indicating - // that the current 'fallthrough' control flow requires backtracking. - void append(const Jump& jump) - { - m_laterFailures.append(jump); - } - void append(JumpList& jumpList) - { - m_laterFailures.append(jumpList); - } - void append(const DataLabelPtr& returnAddress) - { - m_pendingReturns.append(returnAddress); - } - void fallthrough() - { - ASSERT(!m_pendingFallthrough); - m_pendingFallthrough = true; - } - - // These methods clear the backtracking state, either linking to the - // current location, a provided label, or copying the backtracking out - // to a JumpList. All actions may require code generation to take place, - // and as such are passed a pointer to the assembler. - void link(MacroAssembler* assembler) - { - if (m_pendingReturns.size()) { - Label here(assembler); - for (unsigned i = 0; i < m_pendingReturns.size(); ++i) - m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], here)); - m_pendingReturns.clear(); - } - m_laterFailures.link(assembler); - m_laterFailures.clear(); - m_pendingFallthrough = false; - } - void linkTo(Label label, MacroAssembler* assembler) - { - if (m_pendingReturns.size()) { - for (unsigned i = 0; i < m_pendingReturns.size(); ++i) - m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], label)); - m_pendingReturns.clear(); - } - if (m_pendingFallthrough) - assembler->jump(label); - m_laterFailures.linkTo(label, assembler); - m_laterFailures.clear(); - m_pendingFallthrough = false; - } - void takeBacktracksToJumpList(JumpList& jumpList, MacroAssembler* assembler) - { - if (m_pendingReturns.size()) { - Label here(assembler); - for (unsigned i = 0; i < m_pendingReturns.size(); ++i) - m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], here)); - m_pendingReturns.clear(); - m_pendingFallthrough = true; - } - if (m_pendingFallthrough) - jumpList.append(assembler->jump()); - jumpList.append(m_laterFailures); - m_laterFailures.clear(); - m_pendingFallthrough = false; - } - - bool isEmpty() - { - return m_laterFailures.empty() && m_pendingReturns.isEmpty() && !m_pendingFallthrough; - } - - // Called at the end of code generation to link all return addresses. - void linkDataLabels(LinkBuffer& linkBuffer) - { - ASSERT(isEmpty()); - for (unsigned i = 0; i < m_backtrackRecords.size(); ++i) - linkBuffer.patch(m_backtrackRecords[i].m_dataLabel, linkBuffer.locationOf(m_backtrackRecords[i].m_backtrackLocation)); - } - - private: - struct ReturnAddressRecord { - ReturnAddressRecord(DataLabelPtr dataLabel, Label backtrackLocation) - : m_dataLabel(dataLabel) - , m_backtrackLocation(backtrackLocation) - { - } - - DataLabelPtr m_dataLabel; - Label m_backtrackLocation; - }; - - JumpList m_laterFailures; - bool m_pendingFallthrough; - Vector m_pendingReturns; - Vector m_backtrackRecords; - }; - - // Generation methods: - // =================== - - // This method provides a default implementation of backtracking common - // to many terms; terms commonly jump out of the forwards matching path - // on any failed conditions, and add these jumps to the m_jumps list. If - // no special handling is required we can often just backtrack to m_jumps. - void backtrackTermDefault(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - m_backtrackingState.append(op.m_jumps); - } - - void generateAssertionBOL(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - if (m_pattern.m_multiline) { - const RegisterID character = regT0; - - JumpList matchDest; - if (!term->inputPosition) - matchDest.append(branch32(Equal, index, Imm32(m_checked))); - - readCharacter((term->inputPosition - m_checked) - 1, character); - matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass()); - op.m_jumps.append(jump()); - - matchDest.link(this); - } else { - // Erk, really should poison out these alternatives early. :-/ - if (term->inputPosition) - op.m_jumps.append(jump()); - else - op.m_jumps.append(branch32(NotEqual, index, Imm32(m_checked))); - } - } - void backtrackAssertionBOL(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - void generateAssertionEOL(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - if (m_pattern.m_multiline) { - const RegisterID character = regT0; - - JumpList matchDest; - if (term->inputPosition == m_checked) - matchDest.append(atEndOfInput()); - - readCharacter(term->inputPosition - m_checked, character); - matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass()); - op.m_jumps.append(jump()); - - matchDest.link(this); - } else { - if (term->inputPosition == m_checked) - op.m_jumps.append(notAtEndOfInput()); - // Erk, really should poison out these alternatives early. :-/ - else - op.m_jumps.append(jump()); - } - } - void backtrackAssertionEOL(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - // Also falls though on nextIsNotWordChar. - void matchAssertionWordchar(size_t opIndex, JumpList& nextIsWordChar, JumpList& nextIsNotWordChar) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID character = regT0; - - if (term->inputPosition == m_checked) - nextIsNotWordChar.append(atEndOfInput()); - - readCharacter((term->inputPosition - m_checked), character); - matchCharacterClass(character, nextIsWordChar, m_pattern.wordcharCharacterClass()); - } - - void generateAssertionWordBoundary(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID character = regT0; - - Jump atBegin; - JumpList matchDest; - if (!term->inputPosition) - atBegin = branch32(Equal, index, Imm32(m_checked)); - readCharacter((term->inputPosition - m_checked) - 1, character); - matchCharacterClass(character, matchDest, m_pattern.wordcharCharacterClass()); - if (!term->inputPosition) - atBegin.link(this); - - // We fall through to here if the last character was not a wordchar. - JumpList nonWordCharThenWordChar; - JumpList nonWordCharThenNonWordChar; - if (term->invert()) { - matchAssertionWordchar(opIndex, nonWordCharThenNonWordChar, nonWordCharThenWordChar); - nonWordCharThenWordChar.append(jump()); - } else { - matchAssertionWordchar(opIndex, nonWordCharThenWordChar, nonWordCharThenNonWordChar); - nonWordCharThenNonWordChar.append(jump()); - } - op.m_jumps.append(nonWordCharThenNonWordChar); - - // We jump here if the last character was a wordchar. - matchDest.link(this); - JumpList wordCharThenWordChar; - JumpList wordCharThenNonWordChar; - if (term->invert()) { - matchAssertionWordchar(opIndex, wordCharThenNonWordChar, wordCharThenWordChar); - wordCharThenWordChar.append(jump()); - } else { - matchAssertionWordchar(opIndex, wordCharThenWordChar, wordCharThenNonWordChar); - // This can fall-though! - } - - op.m_jumps.append(wordCharThenWordChar); - - nonWordCharThenWordChar.link(this); - wordCharThenNonWordChar.link(this); - } - void backtrackAssertionWordBoundary(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - void generatePatternCharacterOnce(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - - if (op.m_isDeadCode) - return; - - // m_ops always ends with a OpBodyAlternativeEnd or OpMatchFailed - // node, so there must always be at least one more node. - ASSERT(opIndex + 1 < m_ops.size()); - YarrOp* nextOp = &m_ops[opIndex + 1]; - - PatternTerm* term = op.m_term; - UChar ch = term->patternCharacter; - - if ((ch > 0xff) && (m_charSize == Char8)) { - // Have a 16 bit pattern character and an 8 bit string - short circuit - op.m_jumps.append(jump()); - return; - } - - const RegisterID character = regT0; - int maxCharactersAtOnce = m_charSize == Char8 ? 4 : 2; - unsigned ignoreCaseMask = 0; - int allCharacters = ch; - int numberCharacters; - int startTermPosition = term->inputPosition; - - // For case-insesitive compares, non-ascii characters that have different - // upper & lower case representations are converted to a character class. - ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); - - if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) - ignoreCaseMask |= 32; - - for (numberCharacters = 1; numberCharacters < maxCharactersAtOnce && nextOp->m_op == OpTerm; ++numberCharacters, nextOp = &m_ops[opIndex + numberCharacters]) { - PatternTerm* nextTerm = nextOp->m_term; - - if (nextTerm->type != PatternTerm::TypePatternCharacter - || nextTerm->quantityType != QuantifierFixedCount - || nextTerm->quantityCount != 1 - || nextTerm->inputPosition != (startTermPosition + numberCharacters)) - break; - - nextOp->m_isDeadCode = true; - - int shiftAmount = (m_charSize == Char8 ? 8 : 16) * numberCharacters; - - UChar currentCharacter = nextTerm->patternCharacter; - - if ((currentCharacter > 0xff) && (m_charSize == Char8)) { - // Have a 16 bit pattern character and an 8 bit string - short circuit - op.m_jumps.append(jump()); - return; - } - - // For case-insesitive compares, non-ascii characters that have different - // upper & lower case representations are converted to a character class. - ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(currentCharacter) || isCanonicallyUnique(currentCharacter)); - - allCharacters |= (currentCharacter << shiftAmount); - - if ((m_pattern.m_ignoreCase) && (isASCIIAlpha(currentCharacter))) - ignoreCaseMask |= 32 << shiftAmount; - } - - if (m_charSize == Char8) { - switch (numberCharacters) { - case 1: - op.m_jumps.append(jumpIfCharNotEquals(ch, startTermPosition - m_checked, character)); - return; - case 2: { - BaseIndex address(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); - load16Unaligned(address, character); - break; - } - case 3: { - BaseIndex highAddress(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); - load16Unaligned(highAddress, character); - if (ignoreCaseMask) - or32(Imm32(ignoreCaseMask), character); - op.m_jumps.append(branch32(NotEqual, character, Imm32((allCharacters & 0xffff) | ignoreCaseMask))); - op.m_jumps.append(jumpIfCharNotEquals(allCharacters >> 16, startTermPosition + 2 - m_checked, character)); - return; - } - case 4: { - BaseIndex address(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); - load32WithUnalignedHalfWords(address, character); - break; - } - } - } else { - switch (numberCharacters) { - case 1: - op.m_jumps.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); - return; - case 2: - BaseIndex address(input, index, TimesTwo, (term->inputPosition - m_checked) * sizeof(UChar)); - load32WithUnalignedHalfWords(address, character); - break; - } - } - - if (ignoreCaseMask) - or32(Imm32(ignoreCaseMask), character); - op.m_jumps.append(branch32(NotEqual, character, Imm32(allCharacters | ignoreCaseMask))); - return; - } - void backtrackPatternCharacterOnce(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - void generatePatternCharacterFixed(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - UChar ch = term->patternCharacter; - - const RegisterID character = regT0; - const RegisterID countRegister = regT1; - - move(index, countRegister); - sub32(Imm32(term->quantityCount.unsafeGet()), countRegister); - - Label loop(this); - BaseIndex address(input, countRegister, m_charScale, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(m_charSize == Char8 ? sizeof(char) : sizeof(UChar))).unsafeGet()); - - if (m_charSize == Char8) - load8(address, character); - else - load16(address, character); - - // For case-insesitive compares, non-ascii characters that have different - // upper & lower case representations are converted to a character class. - ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); - if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { - or32(TrustedImm32(0x20), character); - ch |= 0x20; - } - - op.m_jumps.append(branch32(NotEqual, character, Imm32(ch))); - add32(TrustedImm32(1), countRegister); - branch32(NotEqual, countRegister, index).linkTo(loop, this); - } - void backtrackPatternCharacterFixed(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - void generatePatternCharacterGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - UChar ch = term->patternCharacter; - - const RegisterID character = regT0; - const RegisterID countRegister = regT1; - - move(TrustedImm32(0), countRegister); - - // Unless have a 16 bit pattern character and an 8 bit string - short circuit - if (!((ch > 0xff) && (m_charSize == Char8))) { - JumpList failures; - Label loop(this); - failures.append(atEndOfInput()); - failures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); - - add32(TrustedImm32(1), countRegister); - add32(TrustedImm32(1), index); - if (term->quantityCount == quantifyInfinite) - jump(loop); - else - branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this); - - failures.link(this); - } - op.m_reentry = label(); - - storeToFrame(countRegister, term->frameLocation); - } - void backtrackPatternCharacterGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID countRegister = regT1; - - m_backtrackingState.link(this); - - loadFromFrame(term->frameLocation, countRegister); - m_backtrackingState.append(branchTest32(Zero, countRegister)); - sub32(TrustedImm32(1), countRegister); - sub32(TrustedImm32(1), index); - jump(op.m_reentry); - } - - void generatePatternCharacterNonGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID countRegister = regT1; - - move(TrustedImm32(0), countRegister); - op.m_reentry = label(); - storeToFrame(countRegister, term->frameLocation); - } - void backtrackPatternCharacterNonGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - UChar ch = term->patternCharacter; - - const RegisterID character = regT0; - const RegisterID countRegister = regT1; - - m_backtrackingState.link(this); - - loadFromFrame(term->frameLocation, countRegister); - - // Unless have a 16 bit pattern character and an 8 bit string - short circuit - if (!((ch > 0xff) && (m_charSize == Char8))) { - JumpList nonGreedyFailures; - nonGreedyFailures.append(atEndOfInput()); - if (term->quantityCount != quantifyInfinite) - nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet()))); - nonGreedyFailures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); - - add32(TrustedImm32(1), countRegister); - add32(TrustedImm32(1), index); - - jump(op.m_reentry); - nonGreedyFailures.link(this); - } - - sub32(countRegister, index); - m_backtrackingState.fallthrough(); - } - - void generateCharacterClassOnce(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID character = regT0; - - JumpList matchDest; - readCharacter(term->inputPosition - m_checked, character); - matchCharacterClass(character, matchDest, term->characterClass); - - if (term->invert()) - op.m_jumps.append(matchDest); - else { - op.m_jumps.append(jump()); - matchDest.link(this); - } - } - void backtrackCharacterClassOnce(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - void generateCharacterClassFixed(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID character = regT0; - const RegisterID countRegister = regT1; - - move(index, countRegister); - sub32(Imm32(term->quantityCount.unsafeGet()), countRegister); - - Label loop(this); - JumpList matchDest; - if (m_charSize == Char8) - load8(BaseIndex(input, countRegister, TimesOne, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(sizeof(char))).unsafeGet()), character); - else - load16(BaseIndex(input, countRegister, TimesTwo, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(sizeof(UChar))).unsafeGet()), character); - matchCharacterClass(character, matchDest, term->characterClass); - - if (term->invert()) - op.m_jumps.append(matchDest); - else { - op.m_jumps.append(jump()); - matchDest.link(this); - } - - add32(TrustedImm32(1), countRegister); - branch32(NotEqual, countRegister, index).linkTo(loop, this); - } - void backtrackCharacterClassFixed(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - void generateCharacterClassGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID character = regT0; - const RegisterID countRegister = regT1; - - move(TrustedImm32(0), countRegister); - - JumpList failures; - Label loop(this); - failures.append(atEndOfInput()); - - if (term->invert()) { - readCharacter(term->inputPosition - m_checked, character); - matchCharacterClass(character, failures, term->characterClass); - } else { - JumpList matchDest; - readCharacter(term->inputPosition - m_checked, character); - matchCharacterClass(character, matchDest, term->characterClass); - failures.append(jump()); - matchDest.link(this); - } - - add32(TrustedImm32(1), countRegister); - add32(TrustedImm32(1), index); - if (term->quantityCount != quantifyInfinite) { - branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this); - failures.append(jump()); - } else - jump(loop); - - failures.link(this); - op.m_reentry = label(); - - storeToFrame(countRegister, term->frameLocation); - } - void backtrackCharacterClassGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID countRegister = regT1; - - m_backtrackingState.link(this); - - loadFromFrame(term->frameLocation, countRegister); - m_backtrackingState.append(branchTest32(Zero, countRegister)); - sub32(TrustedImm32(1), countRegister); - sub32(TrustedImm32(1), index); - jump(op.m_reentry); - } - - void generateCharacterClassNonGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID countRegister = regT1; - - move(TrustedImm32(0), countRegister); - op.m_reentry = label(); - storeToFrame(countRegister, term->frameLocation); - } - void backtrackCharacterClassNonGreedy(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID character = regT0; - const RegisterID countRegister = regT1; - - JumpList nonGreedyFailures; - - m_backtrackingState.link(this); - - loadFromFrame(term->frameLocation, countRegister); - - nonGreedyFailures.append(atEndOfInput()); - nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet()))); - - JumpList matchDest; - readCharacter(term->inputPosition - m_checked, character); - matchCharacterClass(character, matchDest, term->characterClass); - - if (term->invert()) - nonGreedyFailures.append(matchDest); - else { - nonGreedyFailures.append(jump()); - matchDest.link(this); - } - - add32(TrustedImm32(1), countRegister); - add32(TrustedImm32(1), index); - - jump(op.m_reentry); - - nonGreedyFailures.link(this); - sub32(countRegister, index); - m_backtrackingState.fallthrough(); - } - - void generateDotStarEnclosure(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - const RegisterID character = regT0; - const RegisterID matchPos = regT1; - - JumpList foundBeginningNewLine; - JumpList saveStartIndex; - JumpList foundEndingNewLine; - - ASSERT(!m_pattern.m_body->m_hasFixedSize); - getMatchStart(matchPos); - - saveStartIndex.append(branchTest32(Zero, matchPos)); - Label findBOLLoop(this); - sub32(TrustedImm32(1), matchPos); - if (m_charSize == Char8) - load8(BaseIndex(input, matchPos, TimesOne, 0), character); - else - load16(BaseIndex(input, matchPos, TimesTwo, 0), character); - matchCharacterClass(character, foundBeginningNewLine, m_pattern.newlineCharacterClass()); - branchTest32(NonZero, matchPos).linkTo(findBOLLoop, this); - saveStartIndex.append(jump()); - - foundBeginningNewLine.link(this); - add32(TrustedImm32(1), matchPos); // Advance past newline - saveStartIndex.link(this); - - if (!m_pattern.m_multiline && term->anchors.bolAnchor) - op.m_jumps.append(branchTest32(NonZero, matchPos)); - - ASSERT(!m_pattern.m_body->m_hasFixedSize); - setMatchStart(matchPos); - - move(index, matchPos); - - Label findEOLLoop(this); - foundEndingNewLine.append(branch32(Equal, matchPos, length)); - if (m_charSize == Char8) - load8(BaseIndex(input, matchPos, TimesOne, 0), character); - else - load16(BaseIndex(input, matchPos, TimesTwo, 0), character); - matchCharacterClass(character, foundEndingNewLine, m_pattern.newlineCharacterClass()); - add32(TrustedImm32(1), matchPos); - jump(findEOLLoop); - - foundEndingNewLine.link(this); - - if (!m_pattern.m_multiline && term->anchors.eolAnchor) - op.m_jumps.append(branch32(NotEqual, matchPos, length)); - - move(matchPos, index); - } - - void backtrackDotStarEnclosure(size_t opIndex) - { - backtrackTermDefault(opIndex); - } - - // Code generation/backtracking for simple terms - // (pattern characters, character classes, and assertions). - // These methods farm out work to the set of functions above. - void generateTerm(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - switch (term->type) { - case PatternTerm::TypePatternCharacter: - switch (term->quantityType) { - case QuantifierFixedCount: - if (term->quantityCount == 1) - generatePatternCharacterOnce(opIndex); - else - generatePatternCharacterFixed(opIndex); - break; - case QuantifierGreedy: - generatePatternCharacterGreedy(opIndex); - break; - case QuantifierNonGreedy: - generatePatternCharacterNonGreedy(opIndex); - break; - } - break; - - case PatternTerm::TypeCharacterClass: - switch (term->quantityType) { - case QuantifierFixedCount: - if (term->quantityCount == 1) - generateCharacterClassOnce(opIndex); - else - generateCharacterClassFixed(opIndex); - break; - case QuantifierGreedy: - generateCharacterClassGreedy(opIndex); - break; - case QuantifierNonGreedy: - generateCharacterClassNonGreedy(opIndex); - break; - } - break; - - case PatternTerm::TypeAssertionBOL: - generateAssertionBOL(opIndex); - break; - - case PatternTerm::TypeAssertionEOL: - generateAssertionEOL(opIndex); - break; - - case PatternTerm::TypeAssertionWordBoundary: - generateAssertionWordBoundary(opIndex); - break; - - case PatternTerm::TypeForwardReference: - break; - - case PatternTerm::TypeParenthesesSubpattern: - case PatternTerm::TypeParentheticalAssertion: - ASSERT_NOT_REACHED(); - case PatternTerm::TypeBackReference: - m_shouldFallBack = true; - break; - case PatternTerm::TypeDotStarEnclosure: - generateDotStarEnclosure(opIndex); - break; - } - } - void backtrackTerm(size_t opIndex) - { - YarrOp& op = m_ops[opIndex]; - PatternTerm* term = op.m_term; - - switch (term->type) { - case PatternTerm::TypePatternCharacter: - switch (term->quantityType) { - case QuantifierFixedCount: - if (term->quantityCount == 1) - backtrackPatternCharacterOnce(opIndex); - else - backtrackPatternCharacterFixed(opIndex); - break; - case QuantifierGreedy: - backtrackPatternCharacterGreedy(opIndex); - break; - case QuantifierNonGreedy: - backtrackPatternCharacterNonGreedy(opIndex); - break; - } - break; - - case PatternTerm::TypeCharacterClass: - switch (term->quantityType) { - case QuantifierFixedCount: - if (term->quantityCount == 1) - backtrackCharacterClassOnce(opIndex); - else - backtrackCharacterClassFixed(opIndex); - break; - case QuantifierGreedy: - backtrackCharacterClassGreedy(opIndex); - break; - case QuantifierNonGreedy: - backtrackCharacterClassNonGreedy(opIndex); - break; - } - break; - - case PatternTerm::TypeAssertionBOL: - backtrackAssertionBOL(opIndex); - break; - - case PatternTerm::TypeAssertionEOL: - backtrackAssertionEOL(opIndex); - break; - - case PatternTerm::TypeAssertionWordBoundary: - backtrackAssertionWordBoundary(opIndex); - break; - - case PatternTerm::TypeForwardReference: - break; - - case PatternTerm::TypeParenthesesSubpattern: - case PatternTerm::TypeParentheticalAssertion: - ASSERT_NOT_REACHED(); - - case PatternTerm::TypeDotStarEnclosure: - backtrackDotStarEnclosure(opIndex); - break; - - case PatternTerm::TypeBackReference: - m_shouldFallBack = true; - break; - } - } - - void generate() - { - // Forwards generate the matching code. - ASSERT(m_ops.size()); - size_t opIndex = 0; - - do { - YarrOp& op = m_ops[opIndex]; - switch (op.m_op) { - - case OpTerm: - generateTerm(opIndex); - break; - - // OpBodyAlternativeBegin/Next/End - // - // These nodes wrap the set of alternatives in the body of the regular expression. - // There may be either one or two chains of OpBodyAlternative nodes, one representing - // the 'once through' sequence of alternatives (if any exist), and one representing - // the repeating alternatives (again, if any exist). - // - // Upon normal entry to the Begin alternative, we will check that input is available. - // Reentry to the Begin alternative will take place after the check has taken place, - // and will assume that the input position has already been progressed as appropriate. - // - // Entry to subsequent Next/End alternatives occurs when the prior alternative has - // successfully completed a match - return a success state from JIT code. - // - // Next alternatives allow for reentry optimized to suit backtracking from its - // preceding alternative. It expects the input position to still be set to a position - // appropriate to its predecessor, and it will only perform an input check if the - // predecessor had a minimum size less than its own. - // - // In the case 'once through' expressions, the End node will also have a reentry - // point to jump to when the last alternative fails. Again, this expects the input - // position to still reflect that expected by the prior alternative. - case OpBodyAlternativeBegin: { - PatternAlternative* alternative = op.m_alternative; - - // Upon entry at the head of the set of alternatives, check if input is available - // to run the first alternative. (This progresses the input position). - op.m_jumps.append(jumpIfNoAvailableInput(alternative->m_minimumSize)); - // We will reenter after the check, and assume the input position to have been - // set as appropriate to this alternative. - op.m_reentry = label(); - - m_checked += alternative->m_minimumSize; - break; - } - case OpBodyAlternativeNext: - case OpBodyAlternativeEnd: { - PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; - PatternAlternative* alternative = op.m_alternative; - - // If we get here, the prior alternative matched - return success. - - // Adjust the stack pointer to remove the pattern's frame. - removeCallFrame(); - - // Load appropriate values into the return register and the first output - // slot, and return. In the case of pattern with a fixed size, we will - // not have yet set the value in the first - ASSERT(index != returnRegister); - if (m_pattern.m_body->m_hasFixedSize) { - move(index, returnRegister); - if (priorAlternative->m_minimumSize) - sub32(Imm32(priorAlternative->m_minimumSize), returnRegister); - if (compileMode == IncludeSubpatterns) - store32(returnRegister, output); - } else - getMatchStart(returnRegister); - if (compileMode == IncludeSubpatterns) - store32(index, Address(output, 4)); - move(index, returnRegister2); - - generateReturn(); - - // This is the divide between the tail of the prior alternative, above, and - // the head of the subsequent alternative, below. - - if (op.m_op == OpBodyAlternativeNext) { - // This is the reentry point for the Next alternative. We expect any code - // that jumps here to do so with the input position matching that of the - // PRIOR alteranative, and we will only check input availability if we - // need to progress it forwards. - op.m_reentry = label(); - if (alternative->m_minimumSize > priorAlternative->m_minimumSize) { - add32(Imm32(alternative->m_minimumSize - priorAlternative->m_minimumSize), index); - op.m_jumps.append(jumpIfNoAvailableInput()); - } else if (priorAlternative->m_minimumSize > alternative->m_minimumSize) - sub32(Imm32(priorAlternative->m_minimumSize - alternative->m_minimumSize), index); - } else if (op.m_nextOp == notFound) { - // This is the reentry point for the End of 'once through' alternatives, - // jumped to when the last alternative fails to match. - op.m_reentry = label(); - sub32(Imm32(priorAlternative->m_minimumSize), index); - } - - if (op.m_op == OpBodyAlternativeNext) - m_checked += alternative->m_minimumSize; - m_checked -= priorAlternative->m_minimumSize; - break; - } - - // OpSimpleNestedAlternativeBegin/Next/End - // OpNestedAlternativeBegin/Next/End - // - // These nodes are used to handle sets of alternatives that are nested within - // subpatterns and parenthetical assertions. The 'simple' forms are used where - // we do not need to be able to backtrack back into any alternative other than - // the last, the normal forms allow backtracking into any alternative. - // - // Each Begin/Next node is responsible for planting an input check to ensure - // sufficient input is available on entry. Next nodes additionally need to - // jump to the end - Next nodes use the End node's m_jumps list to hold this - // set of jumps. - // - // In the non-simple forms, successful alternative matches must store a - // 'return address' using a DataLabelPtr, used to store the address to jump - // to when backtracking, to get to the code for the appropriate alternative. - case OpSimpleNestedAlternativeBegin: - case OpNestedAlternativeBegin: { - PatternTerm* term = op.m_term; - PatternAlternative* alternative = op.m_alternative; - PatternDisjunction* disjunction = term->parentheses.disjunction; - - // Calculate how much input we need to check for, and if non-zero check. - op.m_checkAdjust = alternative->m_minimumSize; - if ((term->quantityType == QuantifierFixedCount) && (term->type != PatternTerm::TypeParentheticalAssertion)) - op.m_checkAdjust -= disjunction->m_minimumSize; - if (op.m_checkAdjust) - op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust)); - - m_checked += op.m_checkAdjust; - break; - } - case OpSimpleNestedAlternativeNext: - case OpNestedAlternativeNext: { - PatternTerm* term = op.m_term; - PatternAlternative* alternative = op.m_alternative; - PatternDisjunction* disjunction = term->parentheses.disjunction; - - // In the non-simple case, store a 'return address' so we can backtrack correctly. - if (op.m_op == OpNestedAlternativeNext) { - unsigned parenthesesFrameLocation = term->frameLocation; - unsigned alternativeFrameLocation = parenthesesFrameLocation; - if (term->quantityType != QuantifierFixedCount) - alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; - op.m_returnAddress = storeToFrameWithPatch(alternativeFrameLocation); - } - - if (term->quantityType != QuantifierFixedCount && !m_ops[op.m_previousOp].m_alternative->m_minimumSize) { - // If the previous alternative matched without consuming characters then - // backtrack to try to match while consumming some input. - op.m_zeroLengthMatch = branch32(Equal, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); - } - - // If we reach here then the last alternative has matched - jump to the - // End node, to skip over any further alternatives. - // - // FIXME: this is logically O(N^2) (though N can be expected to be very - // small). We could avoid this either by adding an extra jump to the JIT - // data structures, or by making backtracking code that jumps to Next - // alternatives are responsible for checking that input is available (if - // we didn't need to plant the input checks, then m_jumps would be free). - YarrOp* endOp = &m_ops[op.m_nextOp]; - while (endOp->m_nextOp != notFound) { - ASSERT(endOp->m_op == OpSimpleNestedAlternativeNext || endOp->m_op == OpNestedAlternativeNext); - endOp = &m_ops[endOp->m_nextOp]; - } - ASSERT(endOp->m_op == OpSimpleNestedAlternativeEnd || endOp->m_op == OpNestedAlternativeEnd); - endOp->m_jumps.append(jump()); - - // This is the entry point for the next alternative. - op.m_reentry = label(); - - // Calculate how much input we need to check for, and if non-zero check. - op.m_checkAdjust = alternative->m_minimumSize; - if ((term->quantityType == QuantifierFixedCount) && (term->type != PatternTerm::TypeParentheticalAssertion)) - op.m_checkAdjust -= disjunction->m_minimumSize; - if (op.m_checkAdjust) - op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust)); - - YarrOp& lastOp = m_ops[op.m_previousOp]; - m_checked -= lastOp.m_checkAdjust; - m_checked += op.m_checkAdjust; - break; - } - case OpSimpleNestedAlternativeEnd: - case OpNestedAlternativeEnd: { - PatternTerm* term = op.m_term; - - // In the non-simple case, store a 'return address' so we can backtrack correctly. - if (op.m_op == OpNestedAlternativeEnd) { - unsigned parenthesesFrameLocation = term->frameLocation; - unsigned alternativeFrameLocation = parenthesesFrameLocation; - if (term->quantityType != QuantifierFixedCount) - alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; - op.m_returnAddress = storeToFrameWithPatch(alternativeFrameLocation); - } - - if (term->quantityType != QuantifierFixedCount && !m_ops[op.m_previousOp].m_alternative->m_minimumSize) { - // If the previous alternative matched without consuming characters then - // backtrack to try to match while consumming some input. - op.m_zeroLengthMatch = branch32(Equal, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); - } - - // If this set of alternatives contains more than one alternative, - // then the Next nodes will have planted jumps to the End, and added - // them to this node's m_jumps list. - op.m_jumps.link(this); - op.m_jumps.clear(); - - YarrOp& lastOp = m_ops[op.m_previousOp]; - m_checked -= lastOp.m_checkAdjust; - break; - } - - // OpParenthesesSubpatternOnceBegin/End - // - // These nodes support (optionally) capturing subpatterns, that have a - // quantity count of 1 (this covers fixed once, and ?/?? quantifiers). - case OpParenthesesSubpatternOnceBegin: { - PatternTerm* term = op.m_term; - unsigned parenthesesFrameLocation = term->frameLocation; - const RegisterID indexTemporary = regT0; - ASSERT(term->quantityCount == 1); - - // Upon entry to a Greedy quantified set of parenthese store the index. - // We'll use this for two purposes: - // - To indicate which iteration we are on of mathing the remainder of - // the expression after the parentheses - the first, including the - // match within the parentheses, or the second having skipped over them. - // - To check for empty matches, which must be rejected. - // - // At the head of a NonGreedy set of parentheses we'll immediately set the - // value on the stack to -1 (indicating a match skipping the subpattern), - // and plant a jump to the end. We'll also plant a label to backtrack to - // to reenter the subpattern later, with a store to set up index on the - // second iteration. - // - // FIXME: for capturing parens, could use the index in the capture array? - if (term->quantityType == QuantifierGreedy) - storeToFrame(index, parenthesesFrameLocation); - else if (term->quantityType == QuantifierNonGreedy) { - storeToFrame(TrustedImm32(-1), parenthesesFrameLocation); - op.m_jumps.append(jump()); - op.m_reentry = label(); - storeToFrame(index, parenthesesFrameLocation); - } - - // If the parenthese are capturing, store the starting index value to the - // captures array, offsetting as necessary. - // - // FIXME: could avoid offsetting this value in JIT code, apply - // offsets only afterwards, at the point the results array is - // being accessed. - if (term->capture() && compileMode == IncludeSubpatterns) { - int inputOffset = term->inputPosition - m_checked; - if (term->quantityType == QuantifierFixedCount) - inputOffset -= term->parentheses.disjunction->m_minimumSize; - if (inputOffset) { - move(index, indexTemporary); - add32(Imm32(inputOffset), indexTemporary); - setSubpatternStart(indexTemporary, term->parentheses.subpatternId); - } else - setSubpatternStart(index, term->parentheses.subpatternId); - } - break; - } - case OpParenthesesSubpatternOnceEnd: { - PatternTerm* term = op.m_term; - const RegisterID indexTemporary = regT0; - ASSERT(term->quantityCount == 1); - -#ifndef NDEBUG - // Runtime ASSERT to make sure that the nested alternative handled the - // "no input consumed" check. - if (term->quantityType != QuantifierFixedCount && !term->parentheses.disjunction->m_minimumSize) { - Jump pastBreakpoint; - pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); - breakpoint(); - pastBreakpoint.link(this); - } -#endif - - // If the parenthese are capturing, store the ending index value to the - // captures array, offsetting as necessary. - // - // FIXME: could avoid offsetting this value in JIT code, apply - // offsets only afterwards, at the point the results array is - // being accessed. - if (term->capture() && compileMode == IncludeSubpatterns) { - int inputOffset = term->inputPosition - m_checked; - if (inputOffset) { - move(index, indexTemporary); - add32(Imm32(inputOffset), indexTemporary); - setSubpatternEnd(indexTemporary, term->parentheses.subpatternId); - } else - setSubpatternEnd(index, term->parentheses.subpatternId); - } - - // If the parentheses are quantified Greedy then add a label to jump back - // to if get a failed match from after the parentheses. For NonGreedy - // parentheses, link the jump from before the subpattern to here. - if (term->quantityType == QuantifierGreedy) - op.m_reentry = label(); - else if (term->quantityType == QuantifierNonGreedy) { - YarrOp& beginOp = m_ops[op.m_previousOp]; - beginOp.m_jumps.link(this); - } - break; - } - - // OpParenthesesSubpatternTerminalBegin/End - case OpParenthesesSubpatternTerminalBegin: { - PatternTerm* term = op.m_term; - ASSERT(term->quantityType == QuantifierGreedy); - ASSERT(term->quantityCount == quantifyInfinite); - ASSERT(!term->capture()); - - // Upon entry set a label to loop back to. - op.m_reentry = label(); - - // Store the start index of the current match; we need to reject zero - // length matches. - storeToFrame(index, term->frameLocation); - break; - } - case OpParenthesesSubpatternTerminalEnd: { - YarrOp& beginOp = m_ops[op.m_previousOp]; -#ifndef NDEBUG - PatternTerm* term = op.m_term; - - // Runtime ASSERT to make sure that the nested alternative handled the - // "no input consumed" check. - Jump pastBreakpoint; - pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); - breakpoint(); - pastBreakpoint.link(this); -#endif - - // We know that the match is non-zero, we can accept it and - // loop back up to the head of the subpattern. - jump(beginOp.m_reentry); - - // This is the entry point to jump to when we stop matching - we will - // do so once the subpattern cannot match any more. - op.m_reentry = label(); - break; - } - - // OpParentheticalAssertionBegin/End - case OpParentheticalAssertionBegin: { - PatternTerm* term = op.m_term; - - // Store the current index - assertions should not update index, so - // we will need to restore it upon a successful match. - unsigned parenthesesFrameLocation = term->frameLocation; - storeToFrame(index, parenthesesFrameLocation); - - // Check - op.m_checkAdjust = m_checked - term->inputPosition; - if (op.m_checkAdjust) - sub32(Imm32(op.m_checkAdjust), index); - - m_checked -= op.m_checkAdjust; - break; - } - case OpParentheticalAssertionEnd: { - PatternTerm* term = op.m_term; - - // Restore the input index value. - unsigned parenthesesFrameLocation = term->frameLocation; - loadFromFrame(parenthesesFrameLocation, index); - - // If inverted, a successful match of the assertion must be treated - // as a failure, so jump to backtracking. - if (term->invert()) { - op.m_jumps.append(jump()); - op.m_reentry = label(); - } - - YarrOp& lastOp = m_ops[op.m_previousOp]; - m_checked += lastOp.m_checkAdjust; - break; - } - - case OpMatchFailed: - removeCallFrame(); - move(TrustedImmPtr((void*)WTF::notFound), returnRegister); - move(TrustedImm32(0), returnRegister2); - generateReturn(); - break; - } - - ++opIndex; - } while (opIndex < m_ops.size()); - } - - void backtrack() - { - // Backwards generate the backtracking code. - size_t opIndex = m_ops.size(); - ASSERT(opIndex); - - do { - --opIndex; - YarrOp& op = m_ops[opIndex]; - switch (op.m_op) { - - case OpTerm: - backtrackTerm(opIndex); - break; - - // OpBodyAlternativeBegin/Next/End - // - // For each Begin/Next node representing an alternative, we need to decide what to do - // in two circumstances: - // - If we backtrack back into this node, from within the alternative. - // - If the input check at the head of the alternative fails (if this exists). - // - // We treat these two cases differently since in the former case we have slightly - // more information - since we are backtracking out of a prior alternative we know - // that at least enough input was available to run it. For example, given the regular - // expression /a|b/, if we backtrack out of the first alternative (a failed pattern - // character match of 'a'), then we need not perform an additional input availability - // check before running the second alternative. - // - // Backtracking required differs for the last alternative, which in the case of the - // repeating set of alternatives must loop. The code generated for the last alternative - // will also be used to handle all input check failures from any prior alternatives - - // these require similar functionality, in seeking the next available alternative for - // which there is sufficient input. - // - // Since backtracking of all other alternatives simply requires us to link backtracks - // to the reentry point for the subsequent alternative, we will only be generating any - // code when backtracking the last alternative. - case OpBodyAlternativeBegin: - case OpBodyAlternativeNext: { - PatternAlternative* alternative = op.m_alternative; - - if (op.m_op == OpBodyAlternativeNext) { - PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; - m_checked += priorAlternative->m_minimumSize; - } - m_checked -= alternative->m_minimumSize; - - // Is this the last alternative? If not, then if we backtrack to this point we just - // need to jump to try to match the next alternative. - if (m_ops[op.m_nextOp].m_op != OpBodyAlternativeEnd) { - m_backtrackingState.linkTo(m_ops[op.m_nextOp].m_reentry, this); - break; - } - YarrOp& endOp = m_ops[op.m_nextOp]; - - YarrOp* beginOp = &op; - while (beginOp->m_op != OpBodyAlternativeBegin) { - ASSERT(beginOp->m_op == OpBodyAlternativeNext); - beginOp = &m_ops[beginOp->m_previousOp]; - } - - bool onceThrough = endOp.m_nextOp == notFound; - - // First, generate code to handle cases where we backtrack out of an attempted match - // of the last alternative. If this is a 'once through' set of alternatives then we - // have nothing to do - link this straight through to the End. - if (onceThrough) - m_backtrackingState.linkTo(endOp.m_reentry, this); - else { - // If we don't need to move the input poistion, and the pattern has a fixed size - // (in which case we omit the store of the start index until the pattern has matched) - // then we can just link the backtrack out of the last alternative straight to the - // head of the first alternative. - if (m_pattern.m_body->m_hasFixedSize - && (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize) - && (alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize == 1)) - m_backtrackingState.linkTo(beginOp->m_reentry, this); - else { - // We need to generate a trampoline of code to execute before looping back - // around to the first alternative. - m_backtrackingState.link(this); - - // If the pattern size is not fixed, then store the start index, for use if we match. - if (!m_pattern.m_body->m_hasFixedSize) { - if (alternative->m_minimumSize == 1) - setMatchStart(index); - else { - move(index, regT0); - if (alternative->m_minimumSize) - sub32(Imm32(alternative->m_minimumSize - 1), regT0); - else - add32(TrustedImm32(1), regT0); - setMatchStart(regT0); - } - } - - // Generate code to loop. Check whether the last alternative is longer than the - // first (e.g. /a|xy/ or /a|xyz/). - if (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize) { - // We want to loop, and increment input position. If the delta is 1, it is - // already correctly incremented, if more than one then decrement as appropriate. - unsigned delta = alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize; - ASSERT(delta); - if (delta != 1) - sub32(Imm32(delta - 1), index); - jump(beginOp->m_reentry); - } else { - // If the first alternative has minimum size 0xFFFFFFFFu, then there cannot - // be sufficent input available to handle this, so just fall through. - unsigned delta = beginOp->m_alternative->m_minimumSize - alternative->m_minimumSize; - if (delta != 0xFFFFFFFFu) { - // We need to check input because we are incrementing the input. - add32(Imm32(delta + 1), index); - checkInput().linkTo(beginOp->m_reentry, this); - } - } - } - } - - // We can reach this point in the code in two ways: - // - Fallthrough from the code above (a repeating alternative backtracked out of its - // last alternative, and did not have sufficent input to run the first). - // - We will loop back up to the following label when a releating alternative loops, - // following a failed input check. - // - // Either way, we have just failed the input check for the first alternative. - Label firstInputCheckFailed(this); - - // Generate code to handle input check failures from alternatives except the last. - // prevOp is the alternative we're handling a bail out from (initially Begin), and - // nextOp is the alternative we will be attempting to reenter into. - // - // We will link input check failures from the forwards matching path back to the code - // that can handle them. - YarrOp* prevOp = beginOp; - YarrOp* nextOp = &m_ops[beginOp->m_nextOp]; - while (nextOp->m_op != OpBodyAlternativeEnd) { - prevOp->m_jumps.link(this); - - // We only get here if an input check fails, it is only worth checking again - // if the next alternative has a minimum size less than the last. - if (prevOp->m_alternative->m_minimumSize > nextOp->m_alternative->m_minimumSize) { - // FIXME: if we added an extra label to YarrOp, we could avoid needing to - // subtract delta back out, and reduce this code. Should performance test - // the benefit of this. - unsigned delta = prevOp->m_alternative->m_minimumSize - nextOp->m_alternative->m_minimumSize; - sub32(Imm32(delta), index); - Jump fail = jumpIfNoAvailableInput(); - add32(Imm32(delta), index); - jump(nextOp->m_reentry); - fail.link(this); - } else if (prevOp->m_alternative->m_minimumSize < nextOp->m_alternative->m_minimumSize) - add32(Imm32(nextOp->m_alternative->m_minimumSize - prevOp->m_alternative->m_minimumSize), index); - prevOp = nextOp; - nextOp = &m_ops[nextOp->m_nextOp]; - } - - // We fall through to here if there is insufficient input to run the last alternative. - - // If there is insufficient input to run the last alternative, then for 'once through' - // alternatives we are done - just jump back up into the forwards matching path at the End. - if (onceThrough) { - op.m_jumps.linkTo(endOp.m_reentry, this); - jump(endOp.m_reentry); - break; - } - - // For repeating alternatives, link any input check failure from the last alternative to - // this point. - op.m_jumps.link(this); - - bool needsToUpdateMatchStart = !m_pattern.m_body->m_hasFixedSize; - - // Check for cases where input position is already incremented by 1 for the last - // alternative (this is particularly useful where the minimum size of the body - // disjunction is 0, e.g. /a*|b/). - if (needsToUpdateMatchStart && alternative->m_minimumSize == 1) { - // index is already incremented by 1, so just store it now! - setMatchStart(index); - needsToUpdateMatchStart = false; - } - - // Check whether there is sufficient input to loop. Increment the input position by - // one, and check. Also add in the minimum disjunction size before checking - there - // is no point in looping if we're just going to fail all the input checks around - // the next iteration. - ASSERT(alternative->m_minimumSize >= m_pattern.m_body->m_minimumSize); - if (alternative->m_minimumSize == m_pattern.m_body->m_minimumSize) { - // If the last alternative had the same minimum size as the disjunction, - // just simply increment input pos by 1, no adjustment based on minimum size. - add32(TrustedImm32(1), index); - } else { - // If the minumum for the last alternative was one greater than than that - // for the disjunction, we're already progressed by 1, nothing to do! - unsigned delta = (alternative->m_minimumSize - m_pattern.m_body->m_minimumSize) - 1; - if (delta) - sub32(Imm32(delta), index); - } - Jump matchFailed = jumpIfNoAvailableInput(); - - if (needsToUpdateMatchStart) { - if (!m_pattern.m_body->m_minimumSize) - setMatchStart(index); - else { - move(index, regT0); - sub32(Imm32(m_pattern.m_body->m_minimumSize), regT0); - setMatchStart(regT0); - } - } - - // Calculate how much more input the first alternative requires than the minimum - // for the body as a whole. If no more is needed then we dont need an additional - // input check here - jump straight back up to the start of the first alternative. - if (beginOp->m_alternative->m_minimumSize == m_pattern.m_body->m_minimumSize) - jump(beginOp->m_reentry); - else { - if (beginOp->m_alternative->m_minimumSize > m_pattern.m_body->m_minimumSize) - add32(Imm32(beginOp->m_alternative->m_minimumSize - m_pattern.m_body->m_minimumSize), index); - else - sub32(Imm32(m_pattern.m_body->m_minimumSize - beginOp->m_alternative->m_minimumSize), index); - checkInput().linkTo(beginOp->m_reentry, this); - jump(firstInputCheckFailed); - } - - // We jump to here if we iterate to the point that there is insufficient input to - // run any matches, and need to return a failure state from JIT code. - matchFailed.link(this); - - removeCallFrame(); - move(TrustedImmPtr((void*)WTF::notFound), returnRegister); - move(TrustedImm32(0), returnRegister2); - generateReturn(); - break; - } - case OpBodyAlternativeEnd: { - // We should never backtrack back into a body disjunction. - ASSERT(m_backtrackingState.isEmpty()); - - PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; - m_checked += priorAlternative->m_minimumSize; - break; - } - - // OpSimpleNestedAlternativeBegin/Next/End - // OpNestedAlternativeBegin/Next/End - // - // Generate code for when we backtrack back out of an alternative into - // a Begin or Next node, or when the entry input count check fails. If - // there are more alternatives we need to jump to the next alternative, - // if not we backtrack back out of the current set of parentheses. - // - // In the case of non-simple nested assertions we need to also link the - // 'return address' appropriately to backtrack back out into the correct - // alternative. - case OpSimpleNestedAlternativeBegin: - case OpSimpleNestedAlternativeNext: - case OpNestedAlternativeBegin: - case OpNestedAlternativeNext: { - YarrOp& nextOp = m_ops[op.m_nextOp]; - bool isBegin = op.m_previousOp == notFound; - bool isLastAlternative = nextOp.m_nextOp == notFound; - ASSERT(isBegin == (op.m_op == OpSimpleNestedAlternativeBegin || op.m_op == OpNestedAlternativeBegin)); - ASSERT(isLastAlternative == (nextOp.m_op == OpSimpleNestedAlternativeEnd || nextOp.m_op == OpNestedAlternativeEnd)); - - // Treat an input check failure the same as a failed match. - m_backtrackingState.append(op.m_jumps); - - // Set the backtracks to jump to the appropriate place. We may need - // to link the backtracks in one of three different way depending on - // the type of alternative we are dealing with: - // - A single alternative, with no simplings. - // - The last alternative of a set of two or more. - // - An alternative other than the last of a set of two or more. - // - // In the case of a single alternative on its own, we don't need to - // jump anywhere - if the alternative fails to match we can just - // continue to backtrack out of the parentheses without jumping. - // - // In the case of the last alternative in a set of more than one, we - // need to jump to return back out to the beginning. We'll do so by - // adding a jump to the End node's m_jumps list, and linking this - // when we come to generate the Begin node. For alternatives other - // than the last, we need to jump to the next alternative. - // - // If the alternative had adjusted the input position we must link - // backtracking to here, correct, and then jump on. If not we can - // link the backtracks directly to their destination. - if (op.m_checkAdjust) { - // Handle the cases where we need to link the backtracks here. - m_backtrackingState.link(this); - sub32(Imm32(op.m_checkAdjust), index); - if (!isLastAlternative) { - // An alternative that is not the last should jump to its successor. - jump(nextOp.m_reentry); - } else if (!isBegin) { - // The last of more than one alternatives must jump back to the beginning. - nextOp.m_jumps.append(jump()); - } else { - // A single alternative on its own can fall through. - m_backtrackingState.fallthrough(); - } - } else { - // Handle the cases where we can link the backtracks directly to their destinations. - if (!isLastAlternative) { - // An alternative that is not the last should jump to its successor. - m_backtrackingState.linkTo(nextOp.m_reentry, this); - } else if (!isBegin) { - // The last of more than one alternatives must jump back to the beginning. - m_backtrackingState.takeBacktracksToJumpList(nextOp.m_jumps, this); - } - // In the case of a single alternative on its own do nothing - it can fall through. - } - - // If there is a backtrack jump from a zero length match link it here. - if (op.m_zeroLengthMatch.isSet()) - m_backtrackingState.append(op.m_zeroLengthMatch); - - // At this point we've handled the backtracking back into this node. - // Now link any backtracks that need to jump to here. - - // For non-simple alternatives, link the alternative's 'return address' - // so that we backtrack back out into the previous alternative. - if (op.m_op == OpNestedAlternativeNext) - m_backtrackingState.append(op.m_returnAddress); - - // If there is more than one alternative, then the last alternative will - // have planted a jump to be linked to the end. This jump was added to the - // End node's m_jumps list. If we are back at the beginning, link it here. - if (isBegin) { - YarrOp* endOp = &m_ops[op.m_nextOp]; - while (endOp->m_nextOp != notFound) { - ASSERT(endOp->m_op == OpSimpleNestedAlternativeNext || endOp->m_op == OpNestedAlternativeNext); - endOp = &m_ops[endOp->m_nextOp]; - } - ASSERT(endOp->m_op == OpSimpleNestedAlternativeEnd || endOp->m_op == OpNestedAlternativeEnd); - m_backtrackingState.append(endOp->m_jumps); - } - - if (!isBegin) { - YarrOp& lastOp = m_ops[op.m_previousOp]; - m_checked += lastOp.m_checkAdjust; - } - m_checked -= op.m_checkAdjust; - break; - } - case OpSimpleNestedAlternativeEnd: - case OpNestedAlternativeEnd: { - PatternTerm* term = op.m_term; - - // If there is a backtrack jump from a zero length match link it here. - if (op.m_zeroLengthMatch.isSet()) - m_backtrackingState.append(op.m_zeroLengthMatch); - - // If we backtrack into the end of a simple subpattern do nothing; - // just continue through into the last alternative. If we backtrack - // into the end of a non-simple set of alterntives we need to jump - // to the backtracking return address set up during generation. - if (op.m_op == OpNestedAlternativeEnd) { - m_backtrackingState.link(this); - - // Plant a jump to the return address. - unsigned parenthesesFrameLocation = term->frameLocation; - unsigned alternativeFrameLocation = parenthesesFrameLocation; - if (term->quantityType != QuantifierFixedCount) - alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; - loadFromFrameAndJump(alternativeFrameLocation); - - // Link the DataLabelPtr associated with the end of the last - // alternative to this point. - m_backtrackingState.append(op.m_returnAddress); - } - - YarrOp& lastOp = m_ops[op.m_previousOp]; - m_checked += lastOp.m_checkAdjust; - break; - } - - // OpParenthesesSubpatternOnceBegin/End - // - // When we are backtracking back out of a capturing subpattern we need - // to clear the start index in the matches output array, to record that - // this subpattern has not been captured. - // - // When backtracking back out of a Greedy quantified subpattern we need - // to catch this, and try running the remainder of the alternative after - // the subpattern again, skipping the parentheses. - // - // Upon backtracking back into a quantified set of parentheses we need to - // check whether we were currently skipping the subpattern. If not, we - // can backtrack into them, if we were we need to either backtrack back - // out of the start of the parentheses, or jump back to the forwards - // matching start, depending of whether the match is Greedy or NonGreedy. - case OpParenthesesSubpatternOnceBegin: { - PatternTerm* term = op.m_term; - ASSERT(term->quantityCount == 1); - - // We only need to backtrack to thispoint if capturing or greedy. - if ((term->capture() && compileMode == IncludeSubpatterns) || term->quantityType == QuantifierGreedy) { - m_backtrackingState.link(this); - - // If capturing, clear the capture (we only need to reset start). - if (term->capture() && compileMode == IncludeSubpatterns) - clearSubpatternStart(term->parentheses.subpatternId); - - // If Greedy, jump to the end. - if (term->quantityType == QuantifierGreedy) { - // Clear the flag in the stackframe indicating we ran through the subpattern. - unsigned parenthesesFrameLocation = term->frameLocation; - storeToFrame(TrustedImm32(-1), parenthesesFrameLocation); - // Jump to after the parentheses, skipping the subpattern. - jump(m_ops[op.m_nextOp].m_reentry); - // A backtrack from after the parentheses, when skipping the subpattern, - // will jump back to here. - op.m_jumps.link(this); - } - - m_backtrackingState.fallthrough(); - } - break; - } - case OpParenthesesSubpatternOnceEnd: { - PatternTerm* term = op.m_term; - - if (term->quantityType != QuantifierFixedCount) { - m_backtrackingState.link(this); - - // Check whether we should backtrack back into the parentheses, or if we - // are currently in a state where we had skipped over the subpattern - // (in which case the flag value on the stack will be -1). - unsigned parenthesesFrameLocation = term->frameLocation; - Jump hadSkipped = branch32(Equal, Address(stackPointerRegister, parenthesesFrameLocation * sizeof(void*)), TrustedImm32(-1)); - - if (term->quantityType == QuantifierGreedy) { - // For Greedy parentheses, we skip after having already tried going - // through the subpattern, so if we get here we're done. - YarrOp& beginOp = m_ops[op.m_previousOp]; - beginOp.m_jumps.append(hadSkipped); - } else { - // For NonGreedy parentheses, we try skipping the subpattern first, - // so if we get here we need to try running through the subpattern - // next. Jump back to the start of the parentheses in the forwards - // matching path. - ASSERT(term->quantityType == QuantifierNonGreedy); - YarrOp& beginOp = m_ops[op.m_previousOp]; - hadSkipped.linkTo(beginOp.m_reentry, this); - } - - m_backtrackingState.fallthrough(); - } - - m_backtrackingState.append(op.m_jumps); - break; - } - - // OpParenthesesSubpatternTerminalBegin/End - // - // Terminal subpatterns will always match - there is nothing after them to - // force a backtrack, and they have a minimum count of 0, and as such will - // always produce an acceptable result. - case OpParenthesesSubpatternTerminalBegin: { - // We will backtrack to this point once the subpattern cannot match any - // more. Since no match is accepted as a successful match (we are Greedy - // quantified with a minimum of zero) jump back to the forwards matching - // path at the end. - YarrOp& endOp = m_ops[op.m_nextOp]; - m_backtrackingState.linkTo(endOp.m_reentry, this); - break; - } - case OpParenthesesSubpatternTerminalEnd: - // We should never be backtracking to here (hence the 'terminal' in the name). - ASSERT(m_backtrackingState.isEmpty()); - m_backtrackingState.append(op.m_jumps); - break; - - // OpParentheticalAssertionBegin/End - case OpParentheticalAssertionBegin: { - PatternTerm* term = op.m_term; - YarrOp& endOp = m_ops[op.m_nextOp]; - - // We need to handle the backtracks upon backtracking back out - // of a parenthetical assertion if either we need to correct - // the input index, or the assertion was inverted. - if (op.m_checkAdjust || term->invert()) { - m_backtrackingState.link(this); - - if (op.m_checkAdjust) - add32(Imm32(op.m_checkAdjust), index); - - // In an inverted assertion failure to match the subpattern - // is treated as a successful match - jump to the end of the - // subpattern. We already have adjusted the input position - // back to that before the assertion, which is correct. - if (term->invert()) - jump(endOp.m_reentry); - - m_backtrackingState.fallthrough(); - } - - // The End node's jump list will contain any backtracks into - // the end of the assertion. Also, if inverted, we will have - // added the failure caused by a successful match to this. - m_backtrackingState.append(endOp.m_jumps); - - m_checked += op.m_checkAdjust; - break; - } - case OpParentheticalAssertionEnd: { - // FIXME: We should really be clearing any nested subpattern - // matches on bailing out from after the pattern. Firefox has - // this bug too (presumably because they use YARR!) - - // Never backtrack into an assertion; later failures bail to before the begin. - m_backtrackingState.takeBacktracksToJumpList(op.m_jumps, this); - - YarrOp& lastOp = m_ops[op.m_previousOp]; - m_checked -= lastOp.m_checkAdjust; - break; - } - - case OpMatchFailed: - break; - } - - } while (opIndex); - } - - // Compilation methods: - // ==================== - - // opCompileParenthesesSubpattern - // Emits ops for a subpattern (set of parentheses). These consist - // of a set of alternatives wrapped in an outer set of nodes for - // the parentheses. - // Supported types of parentheses are 'Once' (quantityCount == 1) - // and 'Terminal' (non-capturing parentheses quantified as greedy - // and infinite). - // Alternatives will use the 'Simple' set of ops if either the - // subpattern is terminal (in which case we will never need to - // backtrack), or if the subpattern only contains one alternative. - void opCompileParenthesesSubpattern(PatternTerm* term) - { - YarrOpCode parenthesesBeginOpCode; - YarrOpCode parenthesesEndOpCode; - YarrOpCode alternativeBeginOpCode = OpSimpleNestedAlternativeBegin; - YarrOpCode alternativeNextOpCode = OpSimpleNestedAlternativeNext; - YarrOpCode alternativeEndOpCode = OpSimpleNestedAlternativeEnd; - - // We can currently only compile quantity 1 subpatterns that are - // not copies. We generate a copy in the case of a range quantifier, - // e.g. /(?:x){3,9}/, or /(?:x)+/ (These are effectively expanded to - // /(?:x){3,3}(?:x){0,6}/ and /(?:x)(?:x)*/ repectively). The problem - // comes where the subpattern is capturing, in which case we would - // need to restore the capture from the first subpattern upon a - // failure in the second. - if (term->quantityCount == 1 && !term->parentheses.isCopy) { - // Select the 'Once' nodes. - parenthesesBeginOpCode = OpParenthesesSubpatternOnceBegin; - parenthesesEndOpCode = OpParenthesesSubpatternOnceEnd; - - // If there is more than one alternative we cannot use the 'simple' nodes. - if (term->parentheses.disjunction->m_alternatives.size() != 1) { - alternativeBeginOpCode = OpNestedAlternativeBegin; - alternativeNextOpCode = OpNestedAlternativeNext; - alternativeEndOpCode = OpNestedAlternativeEnd; - } - } else if (term->parentheses.isTerminal) { - // Select the 'Terminal' nodes. - parenthesesBeginOpCode = OpParenthesesSubpatternTerminalBegin; - parenthesesEndOpCode = OpParenthesesSubpatternTerminalEnd; - } else { - // This subpattern is not supported by the JIT. - m_shouldFallBack = true; - return; - } - - size_t parenBegin = m_ops.size(); - m_ops.append(parenthesesBeginOpCode); - - m_ops.append(alternativeBeginOpCode); - m_ops.last().m_previousOp = notFound; - m_ops.last().m_term = term; - Vector& alternatives = term->parentheses.disjunction->m_alternatives; - for (unsigned i = 0; i < alternatives.size(); ++i) { - size_t lastOpIndex = m_ops.size() - 1; - - PatternAlternative* nestedAlternative = alternatives[i]; - opCompileAlternative(nestedAlternative); - - size_t thisOpIndex = m_ops.size(); - m_ops.append(YarrOp(alternativeNextOpCode)); - - YarrOp& lastOp = m_ops[lastOpIndex]; - YarrOp& thisOp = m_ops[thisOpIndex]; - - lastOp.m_alternative = nestedAlternative; - lastOp.m_nextOp = thisOpIndex; - thisOp.m_previousOp = lastOpIndex; - thisOp.m_term = term; - } - YarrOp& lastOp = m_ops.last(); - ASSERT(lastOp.m_op == alternativeNextOpCode); - lastOp.m_op = alternativeEndOpCode; - lastOp.m_alternative = 0; - lastOp.m_nextOp = notFound; - - size_t parenEnd = m_ops.size(); - m_ops.append(parenthesesEndOpCode); - - m_ops[parenBegin].m_term = term; - m_ops[parenBegin].m_previousOp = notFound; - m_ops[parenBegin].m_nextOp = parenEnd; - m_ops[parenEnd].m_term = term; - m_ops[parenEnd].m_previousOp = parenBegin; - m_ops[parenEnd].m_nextOp = notFound; - } - - // opCompileParentheticalAssertion - // Emits ops for a parenthetical assertion. These consist of an - // OpSimpleNestedAlternativeBegin/Next/End set of nodes wrapping - // the alternatives, with these wrapped by an outer pair of - // OpParentheticalAssertionBegin/End nodes. - // We can always use the OpSimpleNestedAlternative nodes in the - // case of parenthetical assertions since these only ever match - // once, and will never backtrack back into the assertion. - void opCompileParentheticalAssertion(PatternTerm* term) - { - size_t parenBegin = m_ops.size(); - m_ops.append(OpParentheticalAssertionBegin); - - m_ops.append(OpSimpleNestedAlternativeBegin); - m_ops.last().m_previousOp = notFound; - m_ops.last().m_term = term; - Vector& alternatives = term->parentheses.disjunction->m_alternatives; - for (unsigned i = 0; i < alternatives.size(); ++i) { - size_t lastOpIndex = m_ops.size() - 1; - - PatternAlternative* nestedAlternative = alternatives[i]; - opCompileAlternative(nestedAlternative); - - size_t thisOpIndex = m_ops.size(); - m_ops.append(YarrOp(OpSimpleNestedAlternativeNext)); - - YarrOp& lastOp = m_ops[lastOpIndex]; - YarrOp& thisOp = m_ops[thisOpIndex]; - - lastOp.m_alternative = nestedAlternative; - lastOp.m_nextOp = thisOpIndex; - thisOp.m_previousOp = lastOpIndex; - thisOp.m_term = term; - } - YarrOp& lastOp = m_ops.last(); - ASSERT(lastOp.m_op == OpSimpleNestedAlternativeNext); - lastOp.m_op = OpSimpleNestedAlternativeEnd; - lastOp.m_alternative = 0; - lastOp.m_nextOp = notFound; - - size_t parenEnd = m_ops.size(); - m_ops.append(OpParentheticalAssertionEnd); - - m_ops[parenBegin].m_term = term; - m_ops[parenBegin].m_previousOp = notFound; - m_ops[parenBegin].m_nextOp = parenEnd; - m_ops[parenEnd].m_term = term; - m_ops[parenEnd].m_previousOp = parenBegin; - m_ops[parenEnd].m_nextOp = notFound; - } - - // opCompileAlternative - // Called to emit nodes for all terms in an alternative. - void opCompileAlternative(PatternAlternative* alternative) - { - optimizeAlternative(alternative); - - for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { - PatternTerm* term = &alternative->m_terms[i]; - - switch (term->type) { - case PatternTerm::TypeParenthesesSubpattern: - opCompileParenthesesSubpattern(term); - break; - - case PatternTerm::TypeParentheticalAssertion: - opCompileParentheticalAssertion(term); - break; - - default: - m_ops.append(term); - } - } - } - - // opCompileBody - // This method compiles the body disjunction of the regular expression. - // The body consists of two sets of alternatives - zero or more 'once - // through' (BOL anchored) alternatives, followed by zero or more - // repeated alternatives. - // For each of these two sets of alteratives, if not empty they will be - // wrapped in a set of OpBodyAlternativeBegin/Next/End nodes (with the - // 'begin' node referencing the first alternative, and 'next' nodes - // referencing any further alternatives. The begin/next/end nodes are - // linked together in a doubly linked list. In the case of repeating - // alternatives, the end node is also linked back to the beginning. - // If no repeating alternatives exist, then a OpMatchFailed node exists - // to return the failing result. - void opCompileBody(PatternDisjunction* disjunction) - { - Vector& alternatives = disjunction->m_alternatives; - size_t currentAlternativeIndex = 0; - - // Emit the 'once through' alternatives. - if (alternatives.size() && alternatives[0]->onceThrough()) { - m_ops.append(YarrOp(OpBodyAlternativeBegin)); - m_ops.last().m_previousOp = notFound; - - do { - size_t lastOpIndex = m_ops.size() - 1; - PatternAlternative* alternative = alternatives[currentAlternativeIndex]; - opCompileAlternative(alternative); - - size_t thisOpIndex = m_ops.size(); - m_ops.append(YarrOp(OpBodyAlternativeNext)); - - YarrOp& lastOp = m_ops[lastOpIndex]; - YarrOp& thisOp = m_ops[thisOpIndex]; - - lastOp.m_alternative = alternative; - lastOp.m_nextOp = thisOpIndex; - thisOp.m_previousOp = lastOpIndex; - - ++currentAlternativeIndex; - } while (currentAlternativeIndex < alternatives.size() && alternatives[currentAlternativeIndex]->onceThrough()); - - YarrOp& lastOp = m_ops.last(); - - ASSERT(lastOp.m_op == OpBodyAlternativeNext); - lastOp.m_op = OpBodyAlternativeEnd; - lastOp.m_alternative = 0; - lastOp.m_nextOp = notFound; - } - - if (currentAlternativeIndex == alternatives.size()) { - m_ops.append(YarrOp(OpMatchFailed)); - return; - } - - // Emit the repeated alternatives. - size_t repeatLoop = m_ops.size(); - m_ops.append(YarrOp(OpBodyAlternativeBegin)); - m_ops.last().m_previousOp = notFound; - do { - size_t lastOpIndex = m_ops.size() - 1; - PatternAlternative* alternative = alternatives[currentAlternativeIndex]; - ASSERT(!alternative->onceThrough()); - opCompileAlternative(alternative); - - size_t thisOpIndex = m_ops.size(); - m_ops.append(YarrOp(OpBodyAlternativeNext)); - - YarrOp& lastOp = m_ops[lastOpIndex]; - YarrOp& thisOp = m_ops[thisOpIndex]; - - lastOp.m_alternative = alternative; - lastOp.m_nextOp = thisOpIndex; - thisOp.m_previousOp = lastOpIndex; - - ++currentAlternativeIndex; - } while (currentAlternativeIndex < alternatives.size()); - YarrOp& lastOp = m_ops.last(); - ASSERT(lastOp.m_op == OpBodyAlternativeNext); - lastOp.m_op = OpBodyAlternativeEnd; - lastOp.m_alternative = 0; - lastOp.m_nextOp = repeatLoop; - } - - void generateEnter() - { -#if CPU(X86_64) - push(X86Registers::ebp); - move(stackPointerRegister, X86Registers::ebp); - push(X86Registers::ebx); -#elif CPU(X86) - push(X86Registers::ebp); - move(stackPointerRegister, X86Registers::ebp); - // TODO: do we need spill registers to fill the output pointer if there are no sub captures? - push(X86Registers::ebx); - push(X86Registers::edi); - push(X86Registers::esi); - // load output into edi (2 = saved ebp + return address). - #if COMPILER(MSVC) - loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), input); - loadPtr(Address(X86Registers::ebp, 3 * sizeof(void*)), index); - loadPtr(Address(X86Registers::ebp, 4 * sizeof(void*)), length); - if (compileMode == IncludeSubpatterns) - loadPtr(Address(X86Registers::ebp, 5 * sizeof(void*)), output); - #else - if (compileMode == IncludeSubpatterns) - loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), output); - #endif -#elif CPU(ARM) - push(ARMRegisters::r4); - push(ARMRegisters::r5); - push(ARMRegisters::r6); -#if CPU(ARM_TRADITIONAL) - push(ARMRegisters::r8); // scratch register -#endif - if (compileMode == IncludeSubpatterns) - move(ARMRegisters::r3, output); -#elif CPU(SH4) - push(SH4Registers::r11); - push(SH4Registers::r13); -#elif CPU(MIPS) - // Do nothing. -#endif - } - - void generateReturn() - { -#if CPU(X86_64) - pop(X86Registers::ebx); - pop(X86Registers::ebp); -#elif CPU(X86) - pop(X86Registers::esi); - pop(X86Registers::edi); - pop(X86Registers::ebx); - pop(X86Registers::ebp); -#elif CPU(ARM) -#if CPU(ARM_TRADITIONAL) - pop(ARMRegisters::r8); // scratch register -#endif - pop(ARMRegisters::r6); - pop(ARMRegisters::r5); - pop(ARMRegisters::r4); -#elif CPU(SH4) - pop(SH4Registers::r13); - pop(SH4Registers::r11); -#elif CPU(MIPS) - // Do nothing -#endif - ret(); - } - -public: - YarrGenerator(YarrPattern& pattern, YarrCharSize charSize) - : m_pattern(pattern) - , m_charSize(charSize) - , m_charScale(m_charSize == Char8 ? TimesOne: TimesTwo) - , m_shouldFallBack(false) - , m_checked(0) - { - } - - void compile(JSGlobalData* globalData, YarrCodeBlock& jitObject) - { - generateEnter(); - - Jump hasInput = checkInput(); - move(TrustedImmPtr((void*)WTF::notFound), returnRegister); - move(TrustedImm32(0), returnRegister2); - generateReturn(); - hasInput.link(this); - - if (compileMode == IncludeSubpatterns) { - for (unsigned i = 0; i < m_pattern.m_numSubpatterns + 1; ++i) - store32(TrustedImm32(-1), Address(output, (i << 1) * sizeof(int))); - } - - if (!m_pattern.m_body->m_hasFixedSize) - setMatchStart(index); - - initCallFrame(); - - // Compile the pattern to the internal 'YarrOp' representation. - opCompileBody(m_pattern.m_body); - - // If we encountered anything we can't handle in the JIT code - // (e.g. backreferences) then return early. - if (m_shouldFallBack) { - jitObject.setFallBack(true); - return; - } - - generate(); - backtrack(); - - // Link & finalize the code. - LinkBuffer linkBuffer(*globalData, this, REGEXP_CODE_ID); - m_backtrackingState.linkDataLabels(linkBuffer); - - if (compileMode == MatchOnly) { - if (m_charSize == Char8) - jitObject.set8BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, ("Match-only 8-bit regular expression"))); - else - jitObject.set16BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, ("Match-only 16-bit regular expression"))); - } else { - if (m_charSize == Char8) - jitObject.set8BitCode(FINALIZE_CODE(linkBuffer, ("8-bit regular expression"))); - else - jitObject.set16BitCode(FINALIZE_CODE(linkBuffer, ("16-bit regular expression"))); - } - jitObject.setFallBack(m_shouldFallBack); - } - -private: - YarrPattern& m_pattern; - - YarrCharSize m_charSize; - - Scale m_charScale; - - // Used to detect regular expression constructs that are not currently - // supported in the JIT; fall back to the interpreter when this is detected. - bool m_shouldFallBack; - - // The regular expression expressed as a linear sequence of operations. - Vector m_ops; - - // This records the current input offset being applied due to the current - // set of alternatives we are nested within. E.g. when matching the - // character 'b' within the regular expression /abc/, we will know that - // the minimum size for the alternative is 3, checked upon entry to the - // alternative, and that 'b' is at offset 1 from the start, and as such - // when matching 'b' we need to apply an offset of -2 to the load. - // - // FIXME: This should go away. Rather than tracking this value throughout - // code generation, we should gather this information up front & store it - // on the YarrOp structure. - int m_checked; - - // This class records state whilst generating the backtracking path of code. - BacktrackingState m_backtrackingState; -}; - -void jitCompile(YarrPattern& pattern, YarrCharSize charSize, JSGlobalData* globalData, YarrCodeBlock& jitObject, YarrJITCompileMode mode) -{ - if (mode == MatchOnly) - YarrGenerator(pattern, charSize).compile(globalData, jitObject); - else - YarrGenerator(pattern, charSize).compile(globalData, jitObject); -} - -}} - -#endif diff --git a/3rdparty/masm/yarr/YarrJIT.h b/3rdparty/masm/yarr/YarrJIT.h deleted file mode 100644 index bb7033fdea..0000000000 --- a/3rdparty/masm/yarr/YarrJIT.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef YarrJIT_h -#define YarrJIT_h - -#if ENABLE(YARR_JIT) - -#include "JSGlobalData.h" -#include "MacroAssemblerCodeRef.h" -#include "MatchResult.h" -#include "Yarr.h" -#include "YarrPattern.h" - -#if CPU(X86) && !COMPILER(MSVC) -#define YARR_CALL __attribute__ ((regparm (3))) -#else -#define YARR_CALL -#endif - -namespace JSC { - -class JSGlobalData; -class ExecutablePool; - -namespace Yarr { - -class YarrCodeBlock { -#if CPU(X86_64) - typedef MatchResult (*YarrJITCode8)(const LChar* input, unsigned start, unsigned length, int* output) YARR_CALL; - typedef MatchResult (*YarrJITCode16)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL; - typedef MatchResult (*YarrJITCodeMatchOnly8)(const LChar* input, unsigned start, unsigned length) YARR_CALL; - typedef MatchResult (*YarrJITCodeMatchOnly16)(const UChar* input, unsigned start, unsigned length) YARR_CALL; -#else - typedef EncodedMatchResult (*YarrJITCode8)(const LChar* input, unsigned start, unsigned length, int* output) YARR_CALL; - typedef EncodedMatchResult (*YarrJITCode16)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL; - typedef EncodedMatchResult (*YarrJITCodeMatchOnly8)(const LChar* input, unsigned start, unsigned length) YARR_CALL; - typedef EncodedMatchResult (*YarrJITCodeMatchOnly16)(const UChar* input, unsigned start, unsigned length) YARR_CALL; -#endif - -public: - YarrCodeBlock() - : m_needFallBack(false) - { - } - - ~YarrCodeBlock() - { - } - - void setFallBack(bool fallback) { m_needFallBack = fallback; } - bool isFallBack() { return m_needFallBack; } - - bool has8BitCode() { return m_ref8.size(); } - bool has16BitCode() { return m_ref16.size(); } - void set8BitCode(MacroAssemblerCodeRef ref) { m_ref8 = ref; } - void set16BitCode(MacroAssemblerCodeRef ref) { m_ref16 = ref; } - - bool has8BitCodeMatchOnly() { return m_matchOnly8.size(); } - bool has16BitCodeMatchOnly() { return m_matchOnly16.size(); } - void set8BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly8 = matchOnly; } - void set16BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly16 = matchOnly; } - - MatchResult execute(const LChar* input, unsigned start, unsigned length, int* output) - { - ASSERT(has8BitCode()); - return MatchResult(reinterpret_cast(m_ref8.code().executableAddress())(input, start, length, output)); - } - - MatchResult execute(const UChar* input, unsigned start, unsigned length, int* output) - { - ASSERT(has16BitCode()); - return MatchResult(reinterpret_cast(m_ref16.code().executableAddress())(input, start, length, output)); - } - - MatchResult execute(const LChar* input, unsigned start, unsigned length) - { - ASSERT(has8BitCodeMatchOnly()); - return MatchResult(reinterpret_cast(m_matchOnly8.code().executableAddress())(input, start, length)); - } - - MatchResult execute(const UChar* input, unsigned start, unsigned length) - { - ASSERT(has16BitCodeMatchOnly()); - return MatchResult(reinterpret_cast(m_matchOnly16.code().executableAddress())(input, start, length)); - } - -#if ENABLE(REGEXP_TRACING) - void *getAddr() { return m_ref.code().executableAddress(); } -#endif - - void clear() - { - m_ref8 = MacroAssemblerCodeRef(); - m_ref16 = MacroAssemblerCodeRef(); - m_matchOnly8 = MacroAssemblerCodeRef(); - m_matchOnly16 = MacroAssemblerCodeRef(); - m_needFallBack = false; - } - -private: - MacroAssemblerCodeRef m_ref8; - MacroAssemblerCodeRef m_ref16; - MacroAssemblerCodeRef m_matchOnly8; - MacroAssemblerCodeRef m_matchOnly16; - bool m_needFallBack; -}; - -enum YarrJITCompileMode { - MatchOnly, - IncludeSubpatterns -}; -void jitCompile(YarrPattern&, YarrCharSize, JSGlobalData*, YarrCodeBlock& jitObject, YarrJITCompileMode = IncludeSubpatterns); - -} } // namespace JSC::Yarr - -#endif - -#endif // YarrJIT_h diff --git a/3rdparty/masm/yarr/YarrParser.h b/3rdparty/masm/yarr/YarrParser.h deleted file mode 100644 index 4bab1a0903..0000000000 --- a/3rdparty/masm/yarr/YarrParser.h +++ /dev/null @@ -1,880 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef YarrParser_h -#define YarrParser_h - -#include "Yarr.h" -#include -#include -#include - -namespace JSC { namespace Yarr { - -#define REGEXP_ERROR_PREFIX "Invalid regular expression: " - -enum BuiltInCharacterClassID { - DigitClassID, - SpaceClassID, - WordClassID, - NewlineClassID, -}; - -// The Parser class should not be used directly - only via the Yarr::parse() method. -template -class Parser { -private: - template - friend const char* parse(FriendDelegate&, const String& pattern, unsigned backReferenceLimit); - - enum ErrorCode { - NoError, - PatternTooLarge, - QuantifierOutOfOrder, - QuantifierWithoutAtom, - QuantifierTooLarge, - MissingParentheses, - ParenthesesUnmatched, - ParenthesesTypeInvalid, - CharacterClassUnmatched, - CharacterClassOutOfOrder, - EscapeUnterminated, - NumberOfErrorCodes - }; - - /* - * CharacterClassParserDelegate: - * - * The class CharacterClassParserDelegate is used in the parsing of character - * classes. This class handles detection of character ranges. This class - * implements enough of the delegate interface such that it can be passed to - * parseEscape() as an EscapeDelegate. This allows parseEscape() to be reused - * to perform the parsing of escape characters in character sets. - */ - class CharacterClassParserDelegate { - public: - CharacterClassParserDelegate(Delegate& delegate, ErrorCode& err) - : m_delegate(delegate) - , m_err(err) - , m_state(Empty) - , m_character(0) - { - } - - /* - * begin(): - * - * Called at beginning of construction. - */ - void begin(bool invert) - { - m_delegate.atomCharacterClassBegin(invert); - } - - /* - * atomPatternCharacter(): - * - * This method is called either from parseCharacterClass() (for an unescaped - * character in a character class), or from parseEscape(). In the former case - * the value true will be passed for the argument 'hyphenIsRange', and in this - * mode we will allow a hypen to be treated as indicating a range (i.e. /[a-z]/ - * is different to /[a\-z]/). - */ - void atomPatternCharacter(UChar ch, bool hyphenIsRange = false) - { - switch (m_state) { - case AfterCharacterClass: - // Following a builtin character class we need look out for a hyphen. - // We're looking for invalid ranges, such as /[\d-x]/ or /[\d-\d]/. - // If we see a hyphen following a charater class then unlike usual - // we'll report it to the delegate immediately, and put ourself into - // a poisoned state. Any following calls to add another character or - // character class will result in an error. (A hypen following a - // character-class is itself valid, but only at the end of a regex). - if (hyphenIsRange && ch == '-') { - m_delegate.atomCharacterClassAtom('-'); - m_state = AfterCharacterClassHyphen; - return; - } - // Otherwise just fall through - cached character so treat this as Empty. - - case Empty: - m_character = ch; - m_state = CachedCharacter; - return; - - case CachedCharacter: - if (hyphenIsRange && ch == '-') - m_state = CachedCharacterHyphen; - else { - m_delegate.atomCharacterClassAtom(m_character); - m_character = ch; - } - return; - - case CachedCharacterHyphen: - if (ch < m_character) { - m_err = CharacterClassOutOfOrder; - return; - } - m_delegate.atomCharacterClassRange(m_character, ch); - m_state = Empty; - return; - - // See coment in atomBuiltInCharacterClass below. - // This too is technically an error, per ECMA-262, and again we - // we chose to allow this. Note a subtlely here that while we - // diverge from the spec's definition of CharacterRange we do - // remain in compliance with the grammar. For example, consider - // the expression /[\d-a-z]/. We comply with the grammar in - // this case by not allowing a-z to be matched as a range. - case AfterCharacterClassHyphen: - m_delegate.atomCharacterClassAtom(ch); - m_state = Empty; - return; - } - } - - /* - * atomBuiltInCharacterClass(): - * - * Adds a built-in character class, called by parseEscape(). - */ - void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert) - { - switch (m_state) { - case CachedCharacter: - // Flush the currently cached character, then fall through. - m_delegate.atomCharacterClassAtom(m_character); - - case Empty: - case AfterCharacterClass: - m_state = AfterCharacterClass; - m_delegate.atomCharacterClassBuiltIn(classID, invert); - return; - - // If we hit either of these cases, we have an invalid range that - // looks something like /[x-\d]/ or /[\d-\d]/. - // According to ECMA-262 this should be a syntax error, but - // empirical testing shows this to break teh webz. Instead we - // comply with to the ECMA-262 grammar, and assume the grammar to - // have matched the range correctly, but tweak our interpretation - // of CharacterRange. Effectively we implicitly handle the hyphen - // as if it were escaped, e.g. /[\w-_]/ is treated as /[\w\-_]/. - case CachedCharacterHyphen: - m_delegate.atomCharacterClassAtom(m_character); - m_delegate.atomCharacterClassAtom('-'); - // fall through - case AfterCharacterClassHyphen: - m_delegate.atomCharacterClassBuiltIn(classID, invert); - m_state = Empty; - return; - } - } - - /* - * end(): - * - * Called at end of construction. - */ - void end() - { - if (m_state == CachedCharacter) - m_delegate.atomCharacterClassAtom(m_character); - else if (m_state == CachedCharacterHyphen) { - m_delegate.atomCharacterClassAtom(m_character); - m_delegate.atomCharacterClassAtom('-'); - } - m_delegate.atomCharacterClassEnd(); - } - - // parseEscape() should never call these delegate methods when - // invoked with inCharacterClass set. - NO_RETURN_DUE_TO_ASSERT void assertionWordBoundary(bool) { ASSERT_NOT_REACHED(); } - NO_RETURN_DUE_TO_ASSERT void atomBackReference(unsigned) { ASSERT_NOT_REACHED(); } - - private: - Delegate& m_delegate; - ErrorCode& m_err; - enum CharacterClassConstructionState { - Empty, - CachedCharacter, - CachedCharacterHyphen, - AfterCharacterClass, - AfterCharacterClassHyphen, - } m_state; - UChar m_character; - }; - - Parser(Delegate& delegate, const String& pattern, unsigned backReferenceLimit) - : m_delegate(delegate) - , m_backReferenceLimit(backReferenceLimit) - , m_err(NoError) - , m_data(pattern.getCharacters()) - , m_size(pattern.length()) - , m_index(0) - , m_parenthesesNestingDepth(0) - { - } - - /* - * parseEscape(): - * - * Helper for parseTokens() AND parseCharacterClass(). - * Unlike the other parser methods, this function does not report tokens - * directly to the member delegate (m_delegate), instead tokens are - * emitted to the delegate provided as an argument. In the case of atom - * escapes, parseTokens() will call parseEscape() passing m_delegate as - * an argument, and as such the escape will be reported to the delegate. - * - * However this method may also be used by parseCharacterClass(), in which - * case a CharacterClassParserDelegate will be passed as the delegate that - * tokens should be added to. A boolean flag is also provided to indicate - * whether that an escape in a CharacterClass is being parsed (some parsing - * rules change in this context). - * - * The boolean value returned by this method indicates whether the token - * parsed was an atom (outside of a characted class \b and \B will be - * interpreted as assertions). - */ - template - bool parseEscape(EscapeDelegate& delegate) - { - ASSERT(!m_err); - ASSERT(peek() == '\\'); - consume(); - - if (atEndOfPattern()) { - m_err = EscapeUnterminated; - return false; - } - - switch (peek()) { - // Assertions - case 'b': - consume(); - if (inCharacterClass) - delegate.atomPatternCharacter('\b'); - else { - delegate.assertionWordBoundary(false); - return false; - } - break; - case 'B': - consume(); - if (inCharacterClass) - delegate.atomPatternCharacter('B'); - else { - delegate.assertionWordBoundary(true); - return false; - } - break; - - // CharacterClassEscape - case 'd': - consume(); - delegate.atomBuiltInCharacterClass(DigitClassID, false); - break; - case 's': - consume(); - delegate.atomBuiltInCharacterClass(SpaceClassID, false); - break; - case 'w': - consume(); - delegate.atomBuiltInCharacterClass(WordClassID, false); - break; - case 'D': - consume(); - delegate.atomBuiltInCharacterClass(DigitClassID, true); - break; - case 'S': - consume(); - delegate.atomBuiltInCharacterClass(SpaceClassID, true); - break; - case 'W': - consume(); - delegate.atomBuiltInCharacterClass(WordClassID, true); - break; - - // DecimalEscape - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - // To match Firefox, we parse an invalid backreference in the range [1-7] as an octal escape. - // First, try to parse this as backreference. - if (!inCharacterClass) { - ParseState state = saveState(); - - unsigned backReference = consumeNumber(); - if (backReference <= m_backReferenceLimit) { - delegate.atomBackReference(backReference); - break; - } - - restoreState(state); - } - - // Not a backreference, and not octal. - if (peek() >= '8') { - delegate.atomPatternCharacter('\\'); - break; - } - - // Fall-through to handle this as an octal escape. - } - - // Octal escape - case '0': - delegate.atomPatternCharacter(consumeOctal()); - break; - - // ControlEscape - case 'f': - consume(); - delegate.atomPatternCharacter('\f'); - break; - case 'n': - consume(); - delegate.atomPatternCharacter('\n'); - break; - case 'r': - consume(); - delegate.atomPatternCharacter('\r'); - break; - case 't': - consume(); - delegate.atomPatternCharacter('\t'); - break; - case 'v': - consume(); - delegate.atomPatternCharacter('\v'); - break; - - // ControlLetter - case 'c': { - ParseState state = saveState(); - consume(); - if (!atEndOfPattern()) { - int control = consume(); - - // To match Firefox, inside a character class, we also accept numbers and '_' as control characters. - if (inCharacterClass ? WTF::isASCIIAlphanumeric(control) || (control == '_') : WTF::isASCIIAlpha(control)) { - delegate.atomPatternCharacter(control & 0x1f); - break; - } - } - restoreState(state); - delegate.atomPatternCharacter('\\'); - break; - } - - // HexEscape - case 'x': { - consume(); - int x = tryConsumeHex(2); - if (x == -1) - delegate.atomPatternCharacter('x'); - else - delegate.atomPatternCharacter(x); - break; - } - - // UnicodeEscape - case 'u': { - consume(); - int u = tryConsumeHex(4); - if (u == -1) - delegate.atomPatternCharacter('u'); - else - delegate.atomPatternCharacter(u); - break; - } - - // IdentityEscape - default: - delegate.atomPatternCharacter(consume()); - } - - return true; - } - - /* - * parseAtomEscape(), parseCharacterClassEscape(): - * - * These methods alias to parseEscape(). - */ - bool parseAtomEscape() - { - return parseEscape(m_delegate); - } - void parseCharacterClassEscape(CharacterClassParserDelegate& delegate) - { - parseEscape(delegate); - } - - /* - * parseCharacterClass(): - * - * Helper for parseTokens(); calls dirctly and indirectly (via parseCharacterClassEscape) - * to an instance of CharacterClassParserDelegate, to describe the character class to the - * delegate. - */ - void parseCharacterClass() - { - ASSERT(!m_err); - ASSERT(peek() == '['); - consume(); - - CharacterClassParserDelegate characterClassConstructor(m_delegate, m_err); - - characterClassConstructor.begin(tryConsume('^')); - - while (!atEndOfPattern()) { - switch (peek()) { - case ']': - consume(); - characterClassConstructor.end(); - return; - - case '\\': - parseCharacterClassEscape(characterClassConstructor); - break; - - default: - characterClassConstructor.atomPatternCharacter(consume(), true); - } - - if (m_err) - return; - } - - m_err = CharacterClassUnmatched; - } - - /* - * parseParenthesesBegin(): - * - * Helper for parseTokens(); checks for parentheses types other than regular capturing subpatterns. - */ - void parseParenthesesBegin() - { - ASSERT(!m_err); - ASSERT(peek() == '('); - consume(); - - if (tryConsume('?')) { - if (atEndOfPattern()) { - m_err = ParenthesesTypeInvalid; - return; - } - - switch (consume()) { - case ':': - m_delegate.atomParenthesesSubpatternBegin(false); - break; - - case '=': - m_delegate.atomParentheticalAssertionBegin(); - break; - - case '!': - m_delegate.atomParentheticalAssertionBegin(true); - break; - - default: - m_err = ParenthesesTypeInvalid; - } - } else - m_delegate.atomParenthesesSubpatternBegin(); - - ++m_parenthesesNestingDepth; - } - - /* - * parseParenthesesEnd(): - * - * Helper for parseTokens(); checks for parse errors (due to unmatched parentheses). - */ - void parseParenthesesEnd() - { - ASSERT(!m_err); - ASSERT(peek() == ')'); - consume(); - - if (m_parenthesesNestingDepth > 0) - m_delegate.atomParenthesesEnd(); - else - m_err = ParenthesesUnmatched; - - --m_parenthesesNestingDepth; - } - - /* - * parseQuantifier(): - * - * Helper for parseTokens(); checks for parse errors and non-greedy quantifiers. - */ - void parseQuantifier(bool lastTokenWasAnAtom, unsigned min, unsigned max) - { - ASSERT(!m_err); - ASSERT(min <= max); - - if (min == UINT_MAX) { - m_err = QuantifierTooLarge; - return; - } - - if (lastTokenWasAnAtom) - m_delegate.quantifyAtom(min, max, !tryConsume('?')); - else - m_err = QuantifierWithoutAtom; - } - - /* - * parseTokens(): - * - * This method loops over the input pattern reporting tokens to the delegate. - * The method returns when a parse error is detected, or the end of the pattern - * is reached. One piece of state is tracked around the loop, which is whether - * the last token passed to the delegate was an atom (this is necessary to detect - * a parse error when a quantifier provided without an atom to quantify). - */ - void parseTokens() - { - bool lastTokenWasAnAtom = false; - - while (!atEndOfPattern()) { - switch (peek()) { - case '|': - consume(); - m_delegate.disjunction(); - lastTokenWasAnAtom = false; - break; - - case '(': - parseParenthesesBegin(); - lastTokenWasAnAtom = false; - break; - - case ')': - parseParenthesesEnd(); - lastTokenWasAnAtom = true; - break; - - case '^': - consume(); - m_delegate.assertionBOL(); - lastTokenWasAnAtom = false; - break; - - case '$': - consume(); - m_delegate.assertionEOL(); - lastTokenWasAnAtom = false; - break; - - case '.': - consume(); - m_delegate.atomBuiltInCharacterClass(NewlineClassID, true); - lastTokenWasAnAtom = true; - break; - - case '[': - parseCharacterClass(); - lastTokenWasAnAtom = true; - break; - - case '\\': - lastTokenWasAnAtom = parseAtomEscape(); - break; - - case '*': - consume(); - parseQuantifier(lastTokenWasAnAtom, 0, quantifyInfinite); - lastTokenWasAnAtom = false; - break; - - case '+': - consume(); - parseQuantifier(lastTokenWasAnAtom, 1, quantifyInfinite); - lastTokenWasAnAtom = false; - break; - - case '?': - consume(); - parseQuantifier(lastTokenWasAnAtom, 0, 1); - lastTokenWasAnAtom = false; - break; - - case '{': { - ParseState state = saveState(); - - consume(); - if (peekIsDigit()) { - unsigned min = consumeNumber(); - unsigned max = min; - - if (tryConsume(',')) - max = peekIsDigit() ? consumeNumber() : quantifyInfinite; - - if (tryConsume('}')) { - if (min <= max) - parseQuantifier(lastTokenWasAnAtom, min, max); - else - m_err = QuantifierOutOfOrder; - lastTokenWasAnAtom = false; - break; - } - } - - restoreState(state); - } // if we did not find a complete quantifer, fall through to the default case. - - default: - m_delegate.atomPatternCharacter(consume()); - lastTokenWasAnAtom = true; - } - - if (m_err) - return; - } - - if (m_parenthesesNestingDepth > 0) - m_err = MissingParentheses; - } - - /* - * parse(): - * - * This method calls parseTokens() to parse over the input and converts any - * error code to a const char* for a result. - */ - const char* parse() - { - if (m_size > MAX_PATTERN_SIZE) - m_err = PatternTooLarge; - else - parseTokens(); - ASSERT(atEndOfPattern() || m_err); - - // The order of this array must match the ErrorCode enum. - static const char* errorMessages[NumberOfErrorCodes] = { - 0, // NoError - REGEXP_ERROR_PREFIX "regular expression too large", - REGEXP_ERROR_PREFIX "numbers out of order in {} quantifier", - REGEXP_ERROR_PREFIX "nothing to repeat", - REGEXP_ERROR_PREFIX "number too large in {} quantifier", - REGEXP_ERROR_PREFIX "missing )", - REGEXP_ERROR_PREFIX "unmatched parentheses", - REGEXP_ERROR_PREFIX "unrecognized character after (?", - REGEXP_ERROR_PREFIX "missing terminating ] for character class", - REGEXP_ERROR_PREFIX "range out of order in character class", - REGEXP_ERROR_PREFIX "\\ at end of pattern" - }; - - return errorMessages[m_err]; - } - - // Misc helper functions: - - typedef unsigned ParseState; - - ParseState saveState() - { - return m_index; - } - - void restoreState(ParseState state) - { - m_index = state; - } - - bool atEndOfPattern() - { - ASSERT(m_index <= m_size); - return m_index == m_size; - } - - int peek() - { - ASSERT(m_index < m_size); - return m_data[m_index]; - } - - bool peekIsDigit() - { - return !atEndOfPattern() && WTF::isASCIIDigit(peek()); - } - - unsigned peekDigit() - { - ASSERT(peekIsDigit()); - return peek() - '0'; - } - - int consume() - { - ASSERT(m_index < m_size); - return m_data[m_index++]; - } - - unsigned consumeDigit() - { - ASSERT(peekIsDigit()); - return consume() - '0'; - } - - unsigned consumeNumber() - { - unsigned n = consumeDigit(); - // check for overflow. - for (unsigned newValue; peekIsDigit() && ((newValue = n * 10 + peekDigit()) >= n); ) { - n = newValue; - consume(); - } - return n; - } - - unsigned consumeOctal() - { - ASSERT(WTF::isASCIIOctalDigit(peek())); - - unsigned n = consumeDigit(); - while (n < 32 && !atEndOfPattern() && WTF::isASCIIOctalDigit(peek())) - n = n * 8 + consumeDigit(); - return n; - } - - bool tryConsume(UChar ch) - { - if (atEndOfPattern() || (m_data[m_index] != ch)) - return false; - ++m_index; - return true; - } - - int tryConsumeHex(int count) - { - ParseState state = saveState(); - - int n = 0; - while (count--) { - if (atEndOfPattern() || !WTF::isASCIIHexDigit(peek())) { - restoreState(state); - return -1; - } - n = (n << 4) | WTF::toASCIIHexValue(consume()); - } - return n; - } - - Delegate& m_delegate; - unsigned m_backReferenceLimit; - ErrorCode m_err; - const CharType* m_data; - unsigned m_size; - unsigned m_index; - unsigned m_parenthesesNestingDepth; - - // Derived by empirical testing of compile time in PCRE and WREC. - static const unsigned MAX_PATTERN_SIZE = 1024 * 1024; -}; - -/* - * Yarr::parse(): - * - * The parse method is passed a pattern to be parsed and a delegate upon which - * callbacks will be made to record the parsed tokens forming the regex. - * Yarr::parse() returns null on success, or a const C string providing an error - * message where a parse error occurs. - * - * The Delegate must implement the following interface: - * - * void assertionBOL(); - * void assertionEOL(); - * void assertionWordBoundary(bool invert); - * - * void atomPatternCharacter(UChar ch); - * void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert); - * void atomCharacterClassBegin(bool invert) - * void atomCharacterClassAtom(UChar ch) - * void atomCharacterClassRange(UChar begin, UChar end) - * void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert) - * void atomCharacterClassEnd() - * void atomParenthesesSubpatternBegin(bool capture = true); - * void atomParentheticalAssertionBegin(bool invert = false); - * void atomParenthesesEnd(); - * void atomBackReference(unsigned subpatternId); - * - * void quantifyAtom(unsigned min, unsigned max, bool greedy); - * - * void disjunction(); - * - * The regular expression is described by a sequence of assertion*() and atom*() - * callbacks to the delegate, describing the terms in the regular expression. - * Following an atom a quantifyAtom() call may occur to indicate that the previous - * atom should be quantified. In the case of atoms described across multiple - * calls (parentheses and character classes) the call to quantifyAtom() will come - * after the call to the atom*End() method, never after atom*Begin(). - * - * Character classes may either be described by a single call to - * atomBuiltInCharacterClass(), or by a sequence of atomCharacterClass*() calls. - * In the latter case, ...Begin() will be called, followed by a sequence of - * calls to ...Atom(), ...Range(), and ...BuiltIn(), followed by a call to ...End(). - * - * Sequences of atoms and assertions are broken into alternatives via calls to - * disjunction(). Assertions, atoms, and disjunctions emitted between calls to - * atomParenthesesBegin() and atomParenthesesEnd() form the body of a subpattern. - * atomParenthesesBegin() is passed a subpatternId. In the case of a regular - * capturing subpattern, this will be the subpatternId associated with these - * parentheses, and will also by definition be the lowest subpatternId of these - * parentheses and of any nested paretheses. The atomParenthesesEnd() method - * is passed the subpatternId of the last capturing subexpression nested within - * these paretheses. In the case of a capturing subpattern with no nested - * capturing subpatterns, the same subpatternId will be passed to the begin and - * end functions. In the case of non-capturing subpatterns the subpatternId - * passed to the begin method is also the first possible subpatternId that might - * be nested within these paretheses. If a set of non-capturing parentheses does - * not contain any capturing subpatterns, then the subpatternId passed to begin - * will be greater than the subpatternId passed to end. - */ - -template -const char* parse(Delegate& delegate, const String& pattern, unsigned backReferenceLimit = quantifyInfinite) -{ - if (pattern.is8Bit()) - return Parser(delegate, pattern, backReferenceLimit).parse(); - return Parser(delegate, pattern, backReferenceLimit).parse(); -} - -} } // namespace JSC::Yarr - -#endif // YarrParser_h diff --git a/3rdparty/masm/yarr/YarrPattern.cpp b/3rdparty/masm/yarr/YarrPattern.cpp deleted file mode 100644 index c953a38d2f..0000000000 --- a/3rdparty/masm/yarr/YarrPattern.cpp +++ /dev/null @@ -1,874 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "YarrPattern.h" - -#include "Yarr.h" -#include "YarrCanonicalizeUCS2.h" -#include "YarrParser.h" -#include - -using namespace WTF; - -namespace JSC { namespace Yarr { - -#include "RegExpJitTables.h" - -class CharacterClassConstructor { -public: - CharacterClassConstructor(bool isCaseInsensitive = false) - : m_isCaseInsensitive(isCaseInsensitive) - { - } - - void reset() - { - m_matches.clear(); - m_ranges.clear(); - m_matchesUnicode.clear(); - m_rangesUnicode.clear(); - } - - void append(const CharacterClass* other) - { - for (size_t i = 0; i < other->m_matches.size(); ++i) - addSorted(m_matches, other->m_matches[i]); - for (size_t i = 0; i < other->m_ranges.size(); ++i) - addSortedRange(m_ranges, other->m_ranges[i].begin, other->m_ranges[i].end); - for (size_t i = 0; i < other->m_matchesUnicode.size(); ++i) - addSorted(m_matchesUnicode, other->m_matchesUnicode[i]); - for (size_t i = 0; i < other->m_rangesUnicode.size(); ++i) - addSortedRange(m_rangesUnicode, other->m_rangesUnicode[i].begin, other->m_rangesUnicode[i].end); - } - - void putChar(UChar ch) - { - // Handle ascii cases. - if (ch <= 0x7f) { - if (m_isCaseInsensitive && isASCIIAlpha(ch)) { - addSorted(m_matches, toASCIIUpper(ch)); - addSorted(m_matches, toASCIILower(ch)); - } else - addSorted(m_matches, ch); - return; - } - - // Simple case, not a case-insensitive match. - if (!m_isCaseInsensitive) { - addSorted(m_matchesUnicode, ch); - return; - } - - // Add multiple matches, if necessary. - UCS2CanonicalizationRange* info = rangeInfoFor(ch); - if (info->type == CanonicalizeUnique) - addSorted(m_matchesUnicode, ch); - else - putUnicodeIgnoreCase(ch, info); - } - - void putUnicodeIgnoreCase(UChar ch, UCS2CanonicalizationRange* info) - { - ASSERT(m_isCaseInsensitive); - ASSERT(ch > 0x7f); - ASSERT(ch >= info->begin && ch <= info->end); - ASSERT(info->type != CanonicalizeUnique); - if (info->type == CanonicalizeSet) { - for (uint16_t* set = characterSetInfo[info->value]; (ch = *set); ++set) - addSorted(m_matchesUnicode, ch); - } else { - addSorted(m_matchesUnicode, ch); - addSorted(m_matchesUnicode, getCanonicalPair(info, ch)); - } - } - - void putRange(UChar lo, UChar hi) - { - if (lo <= 0x7f) { - char asciiLo = lo; - char asciiHi = std::min(hi, (UChar)0x7f); - addSortedRange(m_ranges, lo, asciiHi); - - if (m_isCaseInsensitive) { - if ((asciiLo <= 'Z') && (asciiHi >= 'A')) - addSortedRange(m_ranges, std::max(asciiLo, 'A')+('a'-'A'), std::min(asciiHi, 'Z')+('a'-'A')); - if ((asciiLo <= 'z') && (asciiHi >= 'a')) - addSortedRange(m_ranges, std::max(asciiLo, 'a')+('A'-'a'), std::min(asciiHi, 'z')+('A'-'a')); - } - } - if (hi <= 0x7f) - return; - - lo = std::max(lo, (UChar)0x80); - addSortedRange(m_rangesUnicode, lo, hi); - - if (!m_isCaseInsensitive) - return; - - UCS2CanonicalizationRange* info = rangeInfoFor(lo); - while (true) { - // Handle the range [lo .. end] - UChar end = std::min(info->end, hi); - - switch (info->type) { - case CanonicalizeUnique: - // Nothing to do - no canonical equivalents. - break; - case CanonicalizeSet: { - UChar ch; - for (uint16_t* set = characterSetInfo[info->value]; (ch = *set); ++set) - addSorted(m_matchesUnicode, ch); - break; - } - case CanonicalizeRangeLo: - addSortedRange(m_rangesUnicode, lo + info->value, end + info->value); - break; - case CanonicalizeRangeHi: - addSortedRange(m_rangesUnicode, lo - info->value, end - info->value); - break; - case CanonicalizeAlternatingAligned: - // Use addSortedRange since there is likely an abutting range to combine with. - if (lo & 1) - addSortedRange(m_rangesUnicode, lo - 1, lo - 1); - if (!(end & 1)) - addSortedRange(m_rangesUnicode, end + 1, end + 1); - break; - case CanonicalizeAlternatingUnaligned: - // Use addSortedRange since there is likely an abutting range to combine with. - if (!(lo & 1)) - addSortedRange(m_rangesUnicode, lo - 1, lo - 1); - if (end & 1) - addSortedRange(m_rangesUnicode, end + 1, end + 1); - break; - } - - if (hi == end) - return; - - ++info; - lo = info->begin; - }; - - } - - CharacterClass* charClass() - { - CharacterClass* characterClass = new CharacterClass(0); - - characterClass->m_matches.swap(m_matches); - characterClass->m_ranges.swap(m_ranges); - characterClass->m_matchesUnicode.swap(m_matchesUnicode); - characterClass->m_rangesUnicode.swap(m_rangesUnicode); - - return characterClass; - } - -private: - void addSorted(Vector& matches, UChar ch) - { - unsigned pos = 0; - unsigned range = matches.size(); - - // binary chop, find position to insert char. - while (range) { - unsigned index = range >> 1; - - int val = matches[pos+index] - ch; - if (!val) - return; - else if (val > 0) - range = index; - else { - pos += (index+1); - range -= (index+1); - } - } - - if (pos == matches.size()) - matches.append(ch); - else - matches.insert(pos, ch); - } - - void addSortedRange(Vector& ranges, UChar lo, UChar hi) - { - unsigned end = ranges.size(); - - // Simple linear scan - I doubt there are that many ranges anyway... - // feel free to fix this with something faster (eg binary chop). - for (unsigned i = 0; i < end; ++i) { - // does the new range fall before the current position in the array - if (hi < ranges[i].begin) { - // optional optimization: concatenate appending ranges? - may not be worthwhile. - if (hi == (ranges[i].begin - 1)) { - ranges[i].begin = lo; - return; - } - ranges.insert(i, CharacterRange(lo, hi)); - return; - } - // Okay, since we didn't hit the last case, the end of the new range is definitely at or after the begining - // If the new range start at or before the end of the last range, then the overlap (if it starts one after the - // end of the last range they concatenate, which is just as good. - if (lo <= (ranges[i].end + 1)) { - // found an intersect! we'll replace this entry in the array. - ranges[i].begin = std::min(ranges[i].begin, lo); - ranges[i].end = std::max(ranges[i].end, hi); - - // now check if the new range can subsume any subsequent ranges. - unsigned next = i+1; - // each iteration of the loop we will either remove something from the list, or break the loop. - while (next < ranges.size()) { - if (ranges[next].begin <= (ranges[i].end + 1)) { - // the next entry now overlaps / concatenates this one. - ranges[i].end = std::max(ranges[i].end, ranges[next].end); - ranges.remove(next); - } else - break; - } - - return; - } - } - - // CharacterRange comes after all existing ranges. - ranges.append(CharacterRange(lo, hi)); - } - - bool m_isCaseInsensitive; - - Vector m_matches; - Vector m_ranges; - Vector m_matchesUnicode; - Vector m_rangesUnicode; -}; - -class YarrPatternConstructor { -public: - YarrPatternConstructor(YarrPattern& pattern) - : m_pattern(pattern) - , m_characterClassConstructor(pattern.m_ignoreCase) - , m_invertParentheticalAssertion(false) - { - m_pattern.m_body = new PatternDisjunction(); - m_alternative = m_pattern.m_body->addNewAlternative(); - m_pattern.m_disjunctions.append(m_pattern.m_body); - } - - ~YarrPatternConstructor() - { - } - - void reset() - { - m_pattern.reset(); - m_characterClassConstructor.reset(); - - m_pattern.m_body = new PatternDisjunction(); - m_alternative = m_pattern.m_body->addNewAlternative(); - m_pattern.m_disjunctions.append(m_pattern.m_body); - } - - void assertionBOL() - { - if (!m_alternative->m_terms.size() & !m_invertParentheticalAssertion) { - m_alternative->m_startsWithBOL = true; - m_alternative->m_containsBOL = true; - m_pattern.m_containsBOL = true; - } - m_alternative->m_terms.append(PatternTerm::BOL()); - } - void assertionEOL() - { - m_alternative->m_terms.append(PatternTerm::EOL()); - } - void assertionWordBoundary(bool invert) - { - m_alternative->m_terms.append(PatternTerm::WordBoundary(invert)); - } - - void atomPatternCharacter(UChar ch) - { - // We handle case-insensitive checking of unicode characters which do have both - // cases by handling them as if they were defined using a CharacterClass. - if (!m_pattern.m_ignoreCase || isASCII(ch)) { - m_alternative->m_terms.append(PatternTerm(ch)); - return; - } - - UCS2CanonicalizationRange* info = rangeInfoFor(ch); - if (info->type == CanonicalizeUnique) { - m_alternative->m_terms.append(PatternTerm(ch)); - return; - } - - m_characterClassConstructor.putUnicodeIgnoreCase(ch, info); - CharacterClass* newCharacterClass = m_characterClassConstructor.charClass(); - m_pattern.m_userCharacterClasses.append(newCharacterClass); - m_alternative->m_terms.append(PatternTerm(newCharacterClass, false)); - } - - void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert) - { - switch (classID) { - case DigitClassID: - m_alternative->m_terms.append(PatternTerm(m_pattern.digitsCharacterClass(), invert)); - break; - case SpaceClassID: - m_alternative->m_terms.append(PatternTerm(m_pattern.spacesCharacterClass(), invert)); - break; - case WordClassID: - m_alternative->m_terms.append(PatternTerm(m_pattern.wordcharCharacterClass(), invert)); - break; - case NewlineClassID: - m_alternative->m_terms.append(PatternTerm(m_pattern.newlineCharacterClass(), invert)); - break; - } - } - - void atomCharacterClassBegin(bool invert = false) - { - m_invertCharacterClass = invert; - } - - void atomCharacterClassAtom(UChar ch) - { - m_characterClassConstructor.putChar(ch); - } - - void atomCharacterClassRange(UChar begin, UChar end) - { - m_characterClassConstructor.putRange(begin, end); - } - - void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert) - { - ASSERT(classID != NewlineClassID); - - switch (classID) { - case DigitClassID: - m_characterClassConstructor.append(invert ? m_pattern.nondigitsCharacterClass() : m_pattern.digitsCharacterClass()); - break; - - case SpaceClassID: - m_characterClassConstructor.append(invert ? m_pattern.nonspacesCharacterClass() : m_pattern.spacesCharacterClass()); - break; - - case WordClassID: - m_characterClassConstructor.append(invert ? m_pattern.nonwordcharCharacterClass() : m_pattern.wordcharCharacterClass()); - break; - - default: - ASSERT_NOT_REACHED(); - } - } - - void atomCharacterClassEnd() - { - CharacterClass* newCharacterClass = m_characterClassConstructor.charClass(); - m_pattern.m_userCharacterClasses.append(newCharacterClass); - m_alternative->m_terms.append(PatternTerm(newCharacterClass, m_invertCharacterClass)); - } - - void atomParenthesesSubpatternBegin(bool capture = true) - { - unsigned subpatternId = m_pattern.m_numSubpatterns + 1; - if (capture) - m_pattern.m_numSubpatterns++; - - PatternDisjunction* parenthesesDisjunction = new PatternDisjunction(m_alternative); - m_pattern.m_disjunctions.append(parenthesesDisjunction); - m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture, false)); - m_alternative = parenthesesDisjunction->addNewAlternative(); - } - - void atomParentheticalAssertionBegin(bool invert = false) - { - PatternDisjunction* parenthesesDisjunction = new PatternDisjunction(m_alternative); - m_pattern.m_disjunctions.append(parenthesesDisjunction); - m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParentheticalAssertion, m_pattern.m_numSubpatterns + 1, parenthesesDisjunction, false, invert)); - m_alternative = parenthesesDisjunction->addNewAlternative(); - m_invertParentheticalAssertion = invert; - } - - void atomParenthesesEnd() - { - ASSERT(m_alternative->m_parent); - ASSERT(m_alternative->m_parent->m_parent); - - PatternDisjunction* parenthesesDisjunction = m_alternative->m_parent; - m_alternative = m_alternative->m_parent->m_parent; - - PatternTerm& lastTerm = m_alternative->lastTerm(); - - unsigned numParenAlternatives = parenthesesDisjunction->m_alternatives.size(); - unsigned numBOLAnchoredAlts = 0; - - for (unsigned i = 0; i < numParenAlternatives; i++) { - // Bubble up BOL flags - if (parenthesesDisjunction->m_alternatives[i]->m_startsWithBOL) - numBOLAnchoredAlts++; - } - - if (numBOLAnchoredAlts) { - m_alternative->m_containsBOL = true; - // If all the alternatives in parens start with BOL, then so does this one - if (numBOLAnchoredAlts == numParenAlternatives) - m_alternative->m_startsWithBOL = true; - } - - lastTerm.parentheses.lastSubpatternId = m_pattern.m_numSubpatterns; - m_invertParentheticalAssertion = false; - } - - void atomBackReference(unsigned subpatternId) - { - ASSERT(subpatternId); - m_pattern.m_containsBackreferences = true; - m_pattern.m_maxBackReference = std::max(m_pattern.m_maxBackReference, subpatternId); - - if (subpatternId > m_pattern.m_numSubpatterns) { - m_alternative->m_terms.append(PatternTerm::ForwardReference()); - return; - } - - PatternAlternative* currentAlternative = m_alternative; - ASSERT(currentAlternative); - - // Note to self: if we waited until the AST was baked, we could also remove forwards refs - while ((currentAlternative = currentAlternative->m_parent->m_parent)) { - PatternTerm& term = currentAlternative->lastTerm(); - ASSERT((term.type == PatternTerm::TypeParenthesesSubpattern) || (term.type == PatternTerm::TypeParentheticalAssertion)); - - if ((term.type == PatternTerm::TypeParenthesesSubpattern) && term.capture() && (subpatternId == term.parentheses.subpatternId)) { - m_alternative->m_terms.append(PatternTerm::ForwardReference()); - return; - } - } - - m_alternative->m_terms.append(PatternTerm(subpatternId)); - } - - // deep copy the argument disjunction. If filterStartsWithBOL is true, - // skip alternatives with m_startsWithBOL set true. - PatternDisjunction* copyDisjunction(PatternDisjunction* disjunction, bool filterStartsWithBOL = false) - { - PatternDisjunction* newDisjunction = 0; - for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { - PatternAlternative* alternative = disjunction->m_alternatives[alt]; - if (!filterStartsWithBOL || !alternative->m_startsWithBOL) { - if (!newDisjunction) { - newDisjunction = new PatternDisjunction(); - newDisjunction->m_parent = disjunction->m_parent; - } - PatternAlternative* newAlternative = newDisjunction->addNewAlternative(); - for (unsigned i = 0; i < alternative->m_terms.size(); ++i) - newAlternative->m_terms.append(copyTerm(alternative->m_terms[i], filterStartsWithBOL)); - } - } - - if (newDisjunction) - m_pattern.m_disjunctions.append(newDisjunction); - return newDisjunction; - } - - PatternTerm copyTerm(PatternTerm& term, bool filterStartsWithBOL = false) - { - if ((term.type != PatternTerm::TypeParenthesesSubpattern) && (term.type != PatternTerm::TypeParentheticalAssertion)) - return PatternTerm(term); - - PatternTerm termCopy = term; - termCopy.parentheses.disjunction = copyDisjunction(termCopy.parentheses.disjunction, filterStartsWithBOL); - return termCopy; - } - - void quantifyAtom(unsigned min, unsigned max, bool greedy) - { - ASSERT(min <= max); - ASSERT(m_alternative->m_terms.size()); - - if (!max) { - m_alternative->removeLastTerm(); - return; - } - - PatternTerm& term = m_alternative->lastTerm(); - ASSERT(term.type > PatternTerm::TypeAssertionWordBoundary); - ASSERT((term.quantityCount == 1) && (term.quantityType == QuantifierFixedCount)); - - if (term.type == PatternTerm::TypeParentheticalAssertion) { - // If an assertion is quantified with a minimum count of zero, it can simply be removed. - // This arises from the RepeatMatcher behaviour in the spec. Matching an assertion never - // results in any input being consumed, however the continuation passed to the assertion - // (called in steps, 8c and 9 of the RepeatMatcher definition, ES5.1 15.10.2.5) will - // reject all zero length matches (see step 2.1). A match from the continuation of the - // expression will still be accepted regardless (via steps 8a and 11) - the upshot of all - // this is that matches from the assertion are not required, and won't be accepted anyway, - // so no need to ever run it. - if (!min) - m_alternative->removeLastTerm(); - // We never need to run an assertion more than once. Subsequent interations will be run - // with the same start index (since assertions are non-capturing) and the same captures - // (per step 4 of RepeatMatcher in ES5.1 15.10.2.5), and as such will always produce the - // same result and captures. If the first match succeeds then the subsequent (min - 1) - // matches will too. Any additional optional matches will fail (on the same basis as the - // minimum zero quantified assertions, above), but this will still result in a match. - return; - } - - if (min == 0) - term.quantify(max, greedy ? QuantifierGreedy : QuantifierNonGreedy); - else if (min == max) - term.quantify(min, QuantifierFixedCount); - else { - term.quantify(min, QuantifierFixedCount); - m_alternative->m_terms.append(copyTerm(term)); - // NOTE: this term is interesting from an analysis perspective, in that it can be ignored..... - m_alternative->lastTerm().quantify((max == quantifyInfinite) ? max : max - min, greedy ? QuantifierGreedy : QuantifierNonGreedy); - if (m_alternative->lastTerm().type == PatternTerm::TypeParenthesesSubpattern) - m_alternative->lastTerm().parentheses.isCopy = true; - } - } - - void disjunction() - { - m_alternative = m_alternative->m_parent->addNewAlternative(); - } - - unsigned setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition) - { - alternative->m_hasFixedSize = true; - Checked currentInputPosition = initialInputPosition; - - for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { - PatternTerm& term = alternative->m_terms[i]; - - switch (term.type) { - case PatternTerm::TypeAssertionBOL: - case PatternTerm::TypeAssertionEOL: - case PatternTerm::TypeAssertionWordBoundary: - term.inputPosition = currentInputPosition.unsafeGet(); - break; - - case PatternTerm::TypeBackReference: - term.inputPosition = currentInputPosition.unsafeGet(); - term.frameLocation = currentCallFrameSize; - currentCallFrameSize += YarrStackSpaceForBackTrackInfoBackReference; - alternative->m_hasFixedSize = false; - break; - - case PatternTerm::TypeForwardReference: - break; - - case PatternTerm::TypePatternCharacter: - term.inputPosition = currentInputPosition.unsafeGet(); - if (term.quantityType != QuantifierFixedCount) { - term.frameLocation = currentCallFrameSize; - currentCallFrameSize += YarrStackSpaceForBackTrackInfoPatternCharacter; - alternative->m_hasFixedSize = false; - } else - currentInputPosition += term.quantityCount; - break; - - case PatternTerm::TypeCharacterClass: - term.inputPosition = currentInputPosition.unsafeGet(); - if (term.quantityType != QuantifierFixedCount) { - term.frameLocation = currentCallFrameSize; - currentCallFrameSize += YarrStackSpaceForBackTrackInfoCharacterClass; - alternative->m_hasFixedSize = false; - } else - currentInputPosition += term.quantityCount; - break; - - case PatternTerm::TypeParenthesesSubpattern: - // Note: for fixed once parentheses we will ensure at least the minimum is available; others are on their own. - term.frameLocation = currentCallFrameSize; - if (term.quantityCount == 1 && !term.parentheses.isCopy) { - if (term.quantityType != QuantifierFixedCount) - currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesOnce; - currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet()); - // If quantity is fixed, then pre-check its minimum size. - if (term.quantityType == QuantifierFixedCount) - currentInputPosition += term.parentheses.disjunction->m_minimumSize; - term.inputPosition = currentInputPosition.unsafeGet(); - } else if (term.parentheses.isTerminal) { - currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesTerminal; - currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet()); - term.inputPosition = currentInputPosition.unsafeGet(); - } else { - term.inputPosition = currentInputPosition.unsafeGet(); - setupDisjunctionOffsets(term.parentheses.disjunction, 0, currentInputPosition.unsafeGet()); - currentCallFrameSize += YarrStackSpaceForBackTrackInfoParentheses; - } - // Fixed count of 1 could be accepted, if they have a fixed size *AND* if all alternatives are of the same length. - alternative->m_hasFixedSize = false; - break; - - case PatternTerm::TypeParentheticalAssertion: - term.inputPosition = currentInputPosition.unsafeGet(); - term.frameLocation = currentCallFrameSize; - currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition.unsafeGet()); - break; - - case PatternTerm::TypeDotStarEnclosure: - alternative->m_hasFixedSize = false; - term.inputPosition = initialInputPosition; - break; - } - } - - alternative->m_minimumSize = (currentInputPosition - initialInputPosition).unsafeGet(); - return currentCallFrameSize; - } - - unsigned setupDisjunctionOffsets(PatternDisjunction* disjunction, unsigned initialCallFrameSize, unsigned initialInputPosition) - { - if ((disjunction != m_pattern.m_body) && (disjunction->m_alternatives.size() > 1)) - initialCallFrameSize += YarrStackSpaceForBackTrackInfoAlternative; - - unsigned minimumInputSize = UINT_MAX; - unsigned maximumCallFrameSize = 0; - bool hasFixedSize = true; - - for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { - PatternAlternative* alternative = disjunction->m_alternatives[alt]; - unsigned currentAlternativeCallFrameSize = setupAlternativeOffsets(alternative, initialCallFrameSize, initialInputPosition); - minimumInputSize = std::min(minimumInputSize, alternative->m_minimumSize); - maximumCallFrameSize = std::max(maximumCallFrameSize, currentAlternativeCallFrameSize); - hasFixedSize &= alternative->m_hasFixedSize; - } - - ASSERT(minimumInputSize != UINT_MAX); - ASSERT(maximumCallFrameSize >= initialCallFrameSize); - - disjunction->m_hasFixedSize = hasFixedSize; - disjunction->m_minimumSize = minimumInputSize; - disjunction->m_callFrameSize = maximumCallFrameSize; - return maximumCallFrameSize; - } - - void setupOffsets() - { - setupDisjunctionOffsets(m_pattern.m_body, 0, 0); - } - - // This optimization identifies sets of parentheses that we will never need to backtrack. - // In these cases we do not need to store state from prior iterations. - // We can presently avoid backtracking for: - // * where the parens are at the end of the regular expression (last term in any of the - // alternatives of the main body disjunction). - // * where the parens are non-capturing, and quantified unbounded greedy (*). - // * where the parens do not contain any capturing subpatterns. - void checkForTerminalParentheses() - { - // This check is much too crude; should be just checking whether the candidate - // node contains nested capturing subpatterns, not the whole expression! - if (m_pattern.m_numSubpatterns) - return; - - Vector& alternatives = m_pattern.m_body->m_alternatives; - for (size_t i = 0; i < alternatives.size(); ++i) { - Vector& terms = alternatives[i]->m_terms; - if (terms.size()) { - PatternTerm& term = terms.last(); - if (term.type == PatternTerm::TypeParenthesesSubpattern - && term.quantityType == QuantifierGreedy - && term.quantityCount == quantifyInfinite - && !term.capture()) - term.parentheses.isTerminal = true; - } - } - } - - void optimizeBOL() - { - // Look for expressions containing beginning of line (^) anchoring and unroll them. - // e.g. /^a|^b|c/ becomes /^a|^b|c/ which is executed once followed by /c/ which loops - // This code relies on the parsing code tagging alternatives with m_containsBOL and - // m_startsWithBOL and rolling those up to containing alternatives. - // At this point, this is only valid for non-multiline expressions. - PatternDisjunction* disjunction = m_pattern.m_body; - - if (!m_pattern.m_containsBOL || m_pattern.m_multiline) - return; - - PatternDisjunction* loopDisjunction = copyDisjunction(disjunction, true); - - // Set alternatives in disjunction to "onceThrough" - for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) - disjunction->m_alternatives[alt]->setOnceThrough(); - - if (loopDisjunction) { - // Move alternatives from loopDisjunction to disjunction - for (unsigned alt = 0; alt < loopDisjunction->m_alternatives.size(); ++alt) - disjunction->m_alternatives.append(loopDisjunction->m_alternatives[alt]); - - loopDisjunction->m_alternatives.clear(); - } - } - - bool containsCapturingTerms(PatternAlternative* alternative, size_t firstTermIndex, size_t lastTermIndex) - { - Vector& terms = alternative->m_terms; - - for (size_t termIndex = firstTermIndex; termIndex <= lastTermIndex; ++termIndex) { - PatternTerm& term = terms[termIndex]; - - if (term.m_capture) - return true; - - if (term.type == PatternTerm::TypeParenthesesSubpattern) { - PatternDisjunction* nestedDisjunction = term.parentheses.disjunction; - for (unsigned alt = 0; alt < nestedDisjunction->m_alternatives.size(); ++alt) { - if (containsCapturingTerms(nestedDisjunction->m_alternatives[alt], 0, nestedDisjunction->m_alternatives[alt]->m_terms.size() - 1)) - return true; - } - } - } - - return false; - } - - // This optimization identifies alternatives in the form of - // [^].*[?].*[$] for expressions that don't have any - // capturing terms. The alternative is changed to - // followed by processing of the dot stars to find and adjust the - // beginning and the end of the match. - void optimizeDotStarWrappedExpressions() - { - Vector& alternatives = m_pattern.m_body->m_alternatives; - if (alternatives.size() != 1) - return; - - PatternAlternative* alternative = alternatives[0]; - Vector& terms = alternative->m_terms; - if (terms.size() >= 3) { - bool startsWithBOL = false; - bool endsWithEOL = false; - size_t termIndex, firstExpressionTerm, lastExpressionTerm; - - termIndex = 0; - if (terms[termIndex].type == PatternTerm::TypeAssertionBOL) { - startsWithBOL = true; - ++termIndex; - } - - PatternTerm& firstNonAnchorTerm = terms[termIndex]; - if ((firstNonAnchorTerm.type != PatternTerm::TypeCharacterClass) || (firstNonAnchorTerm.characterClass != m_pattern.newlineCharacterClass()) || !((firstNonAnchorTerm.quantityType == QuantifierGreedy) || (firstNonAnchorTerm.quantityType == QuantifierNonGreedy))) - return; - - firstExpressionTerm = termIndex + 1; - - termIndex = terms.size() - 1; - if (terms[termIndex].type == PatternTerm::TypeAssertionEOL) { - endsWithEOL = true; - --termIndex; - } - - PatternTerm& lastNonAnchorTerm = terms[termIndex]; - if ((lastNonAnchorTerm.type != PatternTerm::TypeCharacterClass) || (lastNonAnchorTerm.characterClass != m_pattern.newlineCharacterClass()) || (lastNonAnchorTerm.quantityType != QuantifierGreedy)) - return; - - lastExpressionTerm = termIndex - 1; - - if (firstExpressionTerm > lastExpressionTerm) - return; - - if (!containsCapturingTerms(alternative, firstExpressionTerm, lastExpressionTerm)) { - for (termIndex = terms.size() - 1; termIndex > lastExpressionTerm; --termIndex) - terms.remove(termIndex); - - for (termIndex = firstExpressionTerm; termIndex > 0; --termIndex) - terms.remove(termIndex - 1); - - terms.append(PatternTerm(startsWithBOL, endsWithEOL)); - - m_pattern.m_containsBOL = false; - } - } - } - -private: - YarrPattern& m_pattern; - PatternAlternative* m_alternative; - CharacterClassConstructor m_characterClassConstructor; - bool m_invertCharacterClass; - bool m_invertParentheticalAssertion; -}; - -const char* YarrPattern::compile(const String& patternString) -{ - YarrPatternConstructor constructor(*this); - - if (const char* error = parse(constructor, patternString)) - return error; - - // If the pattern contains illegal backreferences reset & reparse. - // Quoting Netscape's "What's new in JavaScript 1.2", - // "Note: if the number of left parentheses is less than the number specified - // in \#, the \# is taken as an octal escape as described in the next row." - if (containsIllegalBackReference()) { - unsigned numSubpatterns = m_numSubpatterns; - - constructor.reset(); -#if !ASSERT_DISABLED - const char* error = -#endif - parse(constructor, patternString, numSubpatterns); - - ASSERT(!error); - ASSERT(numSubpatterns == m_numSubpatterns); - } - - constructor.checkForTerminalParentheses(); - constructor.optimizeDotStarWrappedExpressions(); - constructor.optimizeBOL(); - - constructor.setupOffsets(); - - return 0; -} - -YarrPattern::YarrPattern(const String& pattern, bool ignoreCase, bool multiline, const char** error) - : m_ignoreCase(ignoreCase) - , m_multiline(multiline) - , m_containsBackreferences(false) - , m_containsBOL(false) - , m_numSubpatterns(0) - , m_maxBackReference(0) - , newlineCached(0) - , digitsCached(0) - , spacesCached(0) - , wordcharCached(0) - , nondigitsCached(0) - , nonspacesCached(0) - , nonwordcharCached(0) -{ - *error = compile(pattern); -} - -} } diff --git a/3rdparty/masm/yarr/YarrPattern.h b/3rdparty/masm/yarr/YarrPattern.h deleted file mode 100644 index 14e89b8e09..0000000000 --- a/3rdparty/masm/yarr/YarrPattern.h +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef YarrPattern_h -#define YarrPattern_h - -#include -#include -#include -#include -#include - -namespace JSC { namespace Yarr { - -struct PatternDisjunction; - -struct CharacterRange { - UChar begin; - UChar end; - - CharacterRange(UChar begin, UChar end) - : begin(begin) - , end(end) - { - } -}; - -struct CharacterClassTable : RefCounted { - const char* m_table; - bool m_inverted; - static PassRefPtr create(const char* table, bool inverted) - { - return adoptRef(new CharacterClassTable(table, inverted)); - } - -private: - CharacterClassTable(const char* table, bool inverted) - : m_table(table) - , m_inverted(inverted) - { - } -}; - -struct CharacterClass { - WTF_MAKE_FAST_ALLOCATED; -public: - // All CharacterClass instances have to have the full set of matches and ranges, - // they may have an optional table for faster lookups (which must match the - // specified matches and ranges) - CharacterClass(PassRefPtr table) - : m_table(table) - { - } - Vector m_matches; - Vector m_ranges; - Vector m_matchesUnicode; - Vector m_rangesUnicode; - RefPtr m_table; -}; - -enum QuantifierType { - QuantifierFixedCount, - QuantifierGreedy, - QuantifierNonGreedy, -}; - -struct PatternTerm { - enum Type { - TypeAssertionBOL, - TypeAssertionEOL, - TypeAssertionWordBoundary, - TypePatternCharacter, - TypeCharacterClass, - TypeBackReference, - TypeForwardReference, - TypeParenthesesSubpattern, - TypeParentheticalAssertion, - TypeDotStarEnclosure, - } type; - bool m_capture :1; - bool m_invert :1; - union { - UChar patternCharacter; - CharacterClass* characterClass; - unsigned backReferenceSubpatternId; - struct { - PatternDisjunction* disjunction; - unsigned subpatternId; - unsigned lastSubpatternId; - bool isCopy; - bool isTerminal; - } parentheses; - struct { - bool bolAnchor : 1; - bool eolAnchor : 1; - } anchors; - }; - QuantifierType quantityType; - Checked quantityCount; - int inputPosition; - unsigned frameLocation; - - PatternTerm(UChar ch) - : type(PatternTerm::TypePatternCharacter) - , m_capture(false) - , m_invert(false) - { - patternCharacter = ch; - quantityType = QuantifierFixedCount; - quantityCount = 1; - } - - PatternTerm(CharacterClass* charClass, bool invert) - : type(PatternTerm::TypeCharacterClass) - , m_capture(false) - , m_invert(invert) - { - characterClass = charClass; - quantityType = QuantifierFixedCount; - quantityCount = 1; - } - - PatternTerm(Type type, unsigned subpatternId, PatternDisjunction* disjunction, bool capture = false, bool invert = false) - : type(type) - , m_capture(capture) - , m_invert(invert) - { - parentheses.disjunction = disjunction; - parentheses.subpatternId = subpatternId; - parentheses.isCopy = false; - parentheses.isTerminal = false; - quantityType = QuantifierFixedCount; - quantityCount = 1; - } - - PatternTerm(Type type, bool invert = false) - : type(type) - , m_capture(false) - , m_invert(invert) - { - quantityType = QuantifierFixedCount; - quantityCount = 1; - } - - PatternTerm(unsigned spatternId) - : type(TypeBackReference) - , m_capture(false) - , m_invert(false) - { - backReferenceSubpatternId = spatternId; - quantityType = QuantifierFixedCount; - quantityCount = 1; - } - - PatternTerm(bool bolAnchor, bool eolAnchor) - : type(TypeDotStarEnclosure) - , m_capture(false) - , m_invert(false) - { - anchors.bolAnchor = bolAnchor; - anchors.eolAnchor = eolAnchor; - quantityType = QuantifierFixedCount; - quantityCount = 1; - } - - static PatternTerm ForwardReference() - { - return PatternTerm(TypeForwardReference); - } - - static PatternTerm BOL() - { - return PatternTerm(TypeAssertionBOL); - } - - static PatternTerm EOL() - { - return PatternTerm(TypeAssertionEOL); - } - - static PatternTerm WordBoundary(bool invert) - { - return PatternTerm(TypeAssertionWordBoundary, invert); - } - - bool invert() - { - return m_invert; - } - - bool capture() - { - return m_capture; - } - - void quantify(unsigned count, QuantifierType type) - { - quantityCount = count; - quantityType = type; - } -}; - -struct PatternAlternative { - WTF_MAKE_FAST_ALLOCATED; -public: - PatternAlternative(PatternDisjunction* disjunction) - : m_parent(disjunction) - , m_onceThrough(false) - , m_hasFixedSize(false) - , m_startsWithBOL(false) - , m_containsBOL(false) - { - } - - PatternTerm& lastTerm() - { - ASSERT(m_terms.size()); - return m_terms[m_terms.size() - 1]; - } - - void removeLastTerm() - { - ASSERT(m_terms.size()); - m_terms.shrink(m_terms.size() - 1); - } - - void setOnceThrough() - { - m_onceThrough = true; - } - - bool onceThrough() - { - return m_onceThrough; - } - - Vector m_terms; - PatternDisjunction* m_parent; - unsigned m_minimumSize; - bool m_onceThrough : 1; - bool m_hasFixedSize : 1; - bool m_startsWithBOL : 1; - bool m_containsBOL : 1; -}; - -struct PatternDisjunction { - WTF_MAKE_FAST_ALLOCATED; -public: - PatternDisjunction(PatternAlternative* parent = 0) - : m_parent(parent) - , m_hasFixedSize(false) - { - } - - ~PatternDisjunction() - { - deleteAllValues(m_alternatives); - } - - PatternAlternative* addNewAlternative() - { - PatternAlternative* alternative = new PatternAlternative(this); - m_alternatives.append(alternative); - return alternative; - } - - Vector m_alternatives; - PatternAlternative* m_parent; - unsigned m_minimumSize; - unsigned m_callFrameSize; - bool m_hasFixedSize; -}; - -// You probably don't want to be calling these functions directly -// (please to be calling newlineCharacterClass() et al on your -// friendly neighborhood YarrPattern instance to get nicely -// cached copies). -CharacterClass* newlineCreate(); -CharacterClass* digitsCreate(); -CharacterClass* spacesCreate(); -CharacterClass* wordcharCreate(); -CharacterClass* nondigitsCreate(); -CharacterClass* nonspacesCreate(); -CharacterClass* nonwordcharCreate(); - -struct TermChain { - TermChain(PatternTerm term) - : term(term) - {} - - PatternTerm term; - Vector hotTerms; -}; - -struct YarrPattern { - JS_EXPORT_PRIVATE YarrPattern(const String& pattern, bool ignoreCase, bool multiline, const char** error); - - ~YarrPattern() - { - deleteAllValues(m_disjunctions); - deleteAllValues(m_userCharacterClasses); - } - - void reset() - { - m_numSubpatterns = 0; - m_maxBackReference = 0; - - m_containsBackreferences = false; - m_containsBOL = false; - - newlineCached = 0; - digitsCached = 0; - spacesCached = 0; - wordcharCached = 0; - nondigitsCached = 0; - nonspacesCached = 0; - nonwordcharCached = 0; - - deleteAllValues(m_disjunctions); - m_disjunctions.clear(); - deleteAllValues(m_userCharacterClasses); - m_userCharacterClasses.clear(); - } - - bool containsIllegalBackReference() - { - return m_maxBackReference > m_numSubpatterns; - } - - CharacterClass* newlineCharacterClass() - { - if (!newlineCached) - m_userCharacterClasses.append(newlineCached = newlineCreate()); - return newlineCached; - } - CharacterClass* digitsCharacterClass() - { - if (!digitsCached) - m_userCharacterClasses.append(digitsCached = digitsCreate()); - return digitsCached; - } - CharacterClass* spacesCharacterClass() - { - if (!spacesCached) - m_userCharacterClasses.append(spacesCached = spacesCreate()); - return spacesCached; - } - CharacterClass* wordcharCharacterClass() - { - if (!wordcharCached) - m_userCharacterClasses.append(wordcharCached = wordcharCreate()); - return wordcharCached; - } - CharacterClass* nondigitsCharacterClass() - { - if (!nondigitsCached) - m_userCharacterClasses.append(nondigitsCached = nondigitsCreate()); - return nondigitsCached; - } - CharacterClass* nonspacesCharacterClass() - { - if (!nonspacesCached) - m_userCharacterClasses.append(nonspacesCached = nonspacesCreate()); - return nonspacesCached; - } - CharacterClass* nonwordcharCharacterClass() - { - if (!nonwordcharCached) - m_userCharacterClasses.append(nonwordcharCached = nonwordcharCreate()); - return nonwordcharCached; - } - - bool m_ignoreCase : 1; - bool m_multiline : 1; - bool m_containsBackreferences : 1; - bool m_containsBOL : 1; - unsigned m_numSubpatterns; - unsigned m_maxBackReference; - PatternDisjunction* m_body; - Vector m_disjunctions; - Vector m_userCharacterClasses; - -private: - const char* compile(const String& patternString); - - CharacterClass* newlineCached; - CharacterClass* digitsCached; - CharacterClass* spacesCached; - CharacterClass* wordcharCached; - CharacterClass* nondigitsCached; - CharacterClass* nonspacesCached; - CharacterClass* nonwordcharCached; -}; - -} } // namespace JSC::Yarr - -#endif // YarrPattern_h diff --git a/3rdparty/masm/yarr/YarrSyntaxChecker.cpp b/3rdparty/masm/yarr/YarrSyntaxChecker.cpp deleted file mode 100644 index aa98c4a354..0000000000 --- a/3rdparty/masm/yarr/YarrSyntaxChecker.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2011 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "YarrSyntaxChecker.h" - -#include "YarrParser.h" - -namespace JSC { namespace Yarr { - -class SyntaxChecker { -public: - void assertionBOL() {} - void assertionEOL() {} - void assertionWordBoundary(bool) {} - void atomPatternCharacter(UChar) {} - void atomBuiltInCharacterClass(BuiltInCharacterClassID, bool) {} - void atomCharacterClassBegin(bool = false) {} - void atomCharacterClassAtom(UChar) {} - void atomCharacterClassRange(UChar, UChar) {} - void atomCharacterClassBuiltIn(BuiltInCharacterClassID, bool) {} - void atomCharacterClassEnd() {} - void atomParenthesesSubpatternBegin(bool = true) {} - void atomParentheticalAssertionBegin(bool = false) {} - void atomParenthesesEnd() {} - void atomBackReference(unsigned) {} - void quantifyAtom(unsigned, unsigned, bool) {} - void disjunction() {} -}; - -const char* checkSyntax(const String& pattern) -{ - SyntaxChecker syntaxChecker; - return parse(syntaxChecker, pattern); -} - -}} // JSC::YARR diff --git a/3rdparty/masm/yarr/YarrSyntaxChecker.h b/3rdparty/masm/yarr/YarrSyntaxChecker.h deleted file mode 100644 index 104ced3ab4..0000000000 --- a/3rdparty/masm/yarr/YarrSyntaxChecker.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2011 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef YarrSyntaxChecker_h -#define YarrSyntaxChecker_h - -#include - -namespace JSC { namespace Yarr { - -const char* checkSyntax(const String& pattern); - -}} // JSC::YARR - -#endif // YarrSyntaxChecker_h - diff --git a/3rdparty/masm/yarr/yarr.pri b/3rdparty/masm/yarr/yarr.pri deleted file mode 100644 index 7e9b4d3f3b..0000000000 --- a/3rdparty/masm/yarr/yarr.pri +++ /dev/null @@ -1,12 +0,0 @@ -# ------------------------------------------------------------------- -# Project file for YARR -# -# See 'Tools/qmake/README' for an overview of the build system -# ------------------------------------------------------------------- - -SOURCES += \ - $$PWD/YarrInterpreter.cpp \ - $$PWD/YarrPattern.cpp \ - $$PWD/YarrSyntaxChecker.cpp \ - $$PWD/YarrCanonicalizeUCS2.cpp - diff --git a/debugging.cpp b/debugging.cpp deleted file mode 100644 index 61ae0e62d0..0000000000 --- a/debugging.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#include "debugging.h" -#include "qv4object.h" -#include "qv4functionobject.h" -#include - -#define LOW_LEVEL_DEBUGGING_HELPERS - -using namespace QQmlJS; -using namespace QQmlJS::Debugging; - -FunctionState::FunctionState(VM::ExecutionContext *context) - : _context(context) -{ - if (debugger()) - debugger()->enterFunction(this); -} - -FunctionState::~FunctionState() -{ - if (debugger()) - debugger()->leaveFunction(this); -} - -VM::Value *FunctionState::argument(unsigned idx) -{ - if (idx < _context->argumentCount) - return _context->arguments + idx; - else - return 0; -} - -VM::Value *FunctionState::local(unsigned idx) -{ - if (idx < _context->variableCount()) - return _context->locals + idx; - return 0; -} - -#ifdef LOW_LEVEL_DEBUGGING_HELPERS -Debugger *globalInstance = 0; - -void printStackTrace() -{ - if (globalInstance) - globalInstance->printStackTrace(); - else - std::cerr << "No debugger." << std::endl; -} -#endif // DO_TRACE_INSTR - -Debugger::Debugger(VM::ExecutionEngine *engine) - : _engine(engine) -{ -#ifdef LOW_LEVEL_DEBUGGING_HELPERS - globalInstance = this; -#endif // DO_TRACE_INSTR -} - -Debugger::~Debugger() -{ -#ifdef LOW_LEVEL_DEBUGGING_HELPERS - globalInstance = 0; -#endif // DO_TRACE_INSTR - - qDeleteAll(_functionInfo.values()); -} - -void Debugger::addFunction(IR::Function *function) -{ - _functionInfo.insert(function, new FunctionDebugInfo(function)); -} - -void Debugger::setSourceLocation(IR::Function *function, unsigned line, unsigned column) -{ - _functionInfo[function]->setSourceLocation(line, column); -} - -void Debugger::mapFunction(VM::Function *vmf, IR::Function *irf) -{ - _vmToIr.insert(vmf, irf); -} - -FunctionDebugInfo *Debugger::debugInfo(VM::FunctionObject *function) const -{ - if (!function) - return 0; - - if (VM::ScriptFunction *sf = function->asScriptFunction()) - return _functionInfo[irFunction(sf->function)]; - else - return 0; -} - -QString Debugger::name(VM::FunctionObject *function) const -{ - if (FunctionDebugInfo *i = debugInfo(function)) - return i->name; - - return QString(); -} - -void Debugger::aboutToCall(VM::FunctionObject *function, VM::ExecutionContext *context) -{ - _callStack.append(CallInfo(context, function)); -} - -void Debugger::justLeft(VM::ExecutionContext *context) -{ - int idx = callIndex(context); - if (idx < 0) - qDebug() << "Oops, leaving a function that was not registered...?"; - else - _callStack.resize(idx); -} - -void Debugger::enterFunction(FunctionState *state) -{ - _callStack[callIndex(state->context())].state = state; - -#ifdef DO_TRACE_INSTR - QString n = name(_callStack[callIndex(state->context())].function); - std::cerr << "*** Entering \"" << qPrintable(n) << "\" with " << state->context()->argumentCount << " args" << std::endl; -// for (unsigned i = 0; i < state->context()->variableEnvironment->argumentCount; ++i) -// std::cerr << " " << i << ": " << currentArg(i) << std::endl; -#endif // DO_TRACE_INSTR -} - -void Debugger::leaveFunction(FunctionState *state) -{ - _callStack[callIndex(state->context())].state = 0; -} - -void Debugger::aboutToThrow(VM::Value *value) -{ - qDebug() << "*** We are about to throw...:" << value->toString(currentState()->context())->toQString(); -} - -FunctionState *Debugger::currentState() const -{ - if (_callStack.isEmpty()) - return 0; - else - return _callStack.last().state; -} - -const char *Debugger::currentArg(unsigned idx) const -{ - FunctionState *state = currentState(); - return qPrintable(state->argument(idx)->toString(state->context())->toQString()); -} - -const char *Debugger::currentLocal(unsigned idx) const -{ - FunctionState *state = currentState(); - return qPrintable(state->local(idx)->toString(state->context())->toQString()); -} - -const char *Debugger::currentTemp(unsigned idx) const -{ - FunctionState *state = currentState(); - return qPrintable(state->temp(idx)->toString(state->context())->toQString()); -} - -void Debugger::printStackTrace() const -{ - for (int i = _callStack.size() - 1; i >=0; --i) { - QString n = name(_callStack[i].function); - std::cerr << "\tframe #" << i << ": " << qPrintable(n) << std::endl; - } -} - -int Debugger::callIndex(VM::ExecutionContext *context) -{ - for (int idx = _callStack.size() - 1; idx >= 0; --idx) { - if (_callStack[idx].context == context) - return idx; - } - - return -1; -} - -IR::Function *Debugger::irFunction(VM::Function *vmf) const -{ - return _vmToIr[vmf]; -} diff --git a/debugging.h b/debugging.h deleted file mode 100644 index e6626c53ce..0000000000 --- a/debugging.h +++ /dev/null @@ -1,140 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#ifndef DEBUGGING_H -#define DEBUGGING_H - -#include "qmljs_engine.h" -#include "qmljs_environment.h" - -#include - -namespace QQmlJS { - -namespace IR { -struct BasicBlock; -struct Function; -} // namespace IR - -namespace Debugging { - -class Debugger; - -struct FunctionDebugInfo { // TODO: use opaque d-pointers here - QString name; - unsigned startLine, startColumn; - - FunctionDebugInfo(IR::Function *function): - startLine(0), startColumn(0) - { - if (function->name) - name = *function->name; - } - - void setSourceLocation(unsigned line, unsigned column) - { startLine = line; startColumn = column; } -}; - -class FunctionState -{ -public: - FunctionState(VM::ExecutionContext *context); - virtual ~FunctionState(); - - virtual VM::Value *argument(unsigned idx); - virtual VM::Value *local(unsigned idx); - virtual VM::Value *temp(unsigned idx) = 0; - - VM::ExecutionContext *context() const - { return _context; } - - Debugger *debugger() const - { return _context->engine->debugger; } - -private: - VM::ExecutionContext *_context; -}; - -struct CallInfo -{ - VM::ExecutionContext *context; - VM::FunctionObject *function; - FunctionState *state; - - CallInfo(VM::ExecutionContext *context = 0, VM::FunctionObject *function = 0, FunctionState *state = 0) - : context(context) - , function(function) - , state(state) - {} -}; - -class Debugger -{ -public: - Debugger(VM::ExecutionEngine *_engine); - ~Debugger(); - -public: // compile-time interface - void addFunction(IR::Function *function); - void setSourceLocation(IR::Function *function, unsigned line, unsigned column); - void mapFunction(VM::Function *vmf, IR::Function *irf); - -public: // run-time querying interface - FunctionDebugInfo *debugInfo(VM::FunctionObject *function) const; - QString name(VM::FunctionObject *function) const; - -public: // execution hooks - void aboutToCall(VM::FunctionObject *function, VM::ExecutionContext *context); - void justLeft(VM::ExecutionContext *context); - void enterFunction(FunctionState *state); - void leaveFunction(FunctionState *state); - void aboutToThrow(VM::Value *value); - -public: // debugging hooks - FunctionState *currentState() const; - const char *currentArg(unsigned idx) const; - const char *currentLocal(unsigned idx) const; - const char *currentTemp(unsigned idx) const; - void printStackTrace() const; - -private: - int callIndex(VM::ExecutionContext *context); - IR::Function *irFunction(VM::Function *vmf) const; - -private: // TODO: use opaque d-pointers here - VM::ExecutionEngine *_engine; - QHash _functionInfo; - QHash _vmToIr; - QVector _callStack; -}; - -} // namespace Debugging -} // namespace QQmlJS - -#endif // DEBUGGING_H diff --git a/llvm_runtime.cpp b/llvm_runtime.cpp deleted file mode 100644 index 212c8d17d5..0000000000 --- a/llvm_runtime.cpp +++ /dev/null @@ -1,523 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qmljs_runtime.h" -#include "qmljs_environment.h" -#include -#include - -using namespace QQmlJS::VM; - -extern "C" { - -Value __qmljs_llvm_return(ExecutionContext */*ctx*/, Value *result) -{ - return *result; -} - -Value *__qmljs_llvm_get_argument(ExecutionContext *ctx, int index) -{ - return &ctx->arguments[index]; -} - -void __qmljs_llvm_init_undefined(Value *result) -{ - *result = Value::undefinedValue(); -} - -void __qmljs_llvm_init_null(Value *result) -{ - *result = Value::nullValue(); -} - -void __qmljs_llvm_init_boolean(Value *result, bool value) -{ - *result = Value::fromBoolean(value); -} - -void __qmljs_llvm_init_number(Value *result, double value) -{ - *result = Value::fromDouble(value); -} - -void __qmljs_llvm_init_string(ExecutionContext *ctx, Value *result, const char *str) -{ - *result = Value::fromString(__qmljs_string_from_utf8(ctx, str)); -} - -void __qmljs_llvm_init_closure(ExecutionContext *ctx, Value *result, - String *name, bool hasDirectEval, - bool usesArgumentsObject, bool isStrict, - bool hasNestedFunctions, - String **formals, unsigned formalCount, - String **locals, unsigned localCount) -{ - Function *clos = __qmljs_register_function(ctx, name, hasDirectEval, - usesArgumentsObject, isStrict, - hasNestedFunctions, - formals, formalCount, - locals, localCount); - *result = __qmljs_init_closure(clos, ctx); -} - -bool __qmljs_llvm_to_boolean(ExecutionContext *ctx, const Value *value) -{ - return __qmljs_to_boolean(*value, ctx); -} - -void __qmljs_llvm_bit_and(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_bit_and(*left, *right, ctx); -} - -void __qmljs_llvm_bit_or(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_bit_or(*left, *right, ctx); -} - -void __qmljs_llvm_bit_xor(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_bit_xor(*left, *right, ctx); -} - -void __qmljs_llvm_add(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_add(*left, *right, ctx); -} - -void __qmljs_llvm_sub(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_sub(*left, *right, ctx); -} - -void __qmljs_llvm_mul(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_mul(*left, *right, ctx); -} - -void __qmljs_llvm_div(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_div(*left, *right, ctx); -} - -void __qmljs_llvm_mod(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_mod(*left, *right, ctx); -} - -void __qmljs_llvm_shl(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_shl(*left, *right, ctx); -} - -void __qmljs_llvm_shr(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_shr(*left, *right, ctx); -} - -void __qmljs_llvm_ushr(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_ushr(*left, *right, ctx); -} - -void __qmljs_llvm_gt(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_gt(*left, *right, ctx); -} - -void __qmljs_llvm_lt(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_lt(*left, *right, ctx); -} - -void __qmljs_llvm_ge(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_ge(*left, *right, ctx); -} - -void __qmljs_llvm_le(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_le(*left, *right, ctx); -} - -void __qmljs_llvm_eq(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_eq(*left, *right, ctx); -} - -void __qmljs_llvm_ne(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_ne(*left, *right, ctx); -} - -void __qmljs_llvm_se(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_se(*left, *right, ctx); -} - -void __qmljs_llvm_sne(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_sne(*left, *right, ctx); -} - -void __qmljs_llvm_instanceof(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_instanceof(*left, *right, ctx); -} - -void __qmljs_llvm_in(ExecutionContext *ctx, Value *result, Value *left, Value *right) -{ - *result = __qmljs_in(*left, *right, ctx); -} - -void __qmljs_llvm_uplus(ExecutionContext *ctx, Value *result, const Value *value) -{ - *result = __qmljs_uplus(*value, ctx); -} - -void __qmljs_llvm_uminus(ExecutionContext *ctx, Value *result, const Value *value) -{ - *result = __qmljs_uminus(*value, ctx); -} - -void __qmljs_llvm_compl(ExecutionContext *ctx, Value *result, const Value *value) -{ - *result = __qmljs_compl(*value, ctx); -} - -void __qmljs_llvm_not(ExecutionContext *ctx, Value *result, const Value *value) -{ - *result = __qmljs_not(*value, ctx); -} - -void __qmljs_llvm_inplace_bit_and_name(ExecutionContext *ctx, String *dest, Value *src) -{ - __qmljs_inplace_bit_and_name(*src, dest, ctx); -} - -void __qmljs_llvm_inplace_bit_or_name(ExecutionContext *ctx, String *dest, Value *src) -{ - __qmljs_inplace_bit_or_name(*src, dest, ctx); -} - -void __qmljs_llvm_inplace_bit_xor_name(ExecutionContext *ctx, String *dest, Value *src) -{ - __qmljs_inplace_bit_xor_name(*src, dest, ctx); -} - -void __qmljs_llvm_inplace_add_name(ExecutionContext *ctx, String *dest, Value *src) -{ - __qmljs_inplace_add_name(*src, dest, ctx); -} - -void __qmljs_llvm_inplace_sub_name(ExecutionContext *ctx, String *dest, Value *src) -{ - __qmljs_inplace_sub_name(*src, dest, ctx); -} - -void __qmljs_llvm_inplace_mul_name(ExecutionContext *ctx, String *dest, Value *src) -{ - __qmljs_inplace_mul_name(*src, dest, ctx); -} - -void __qmljs_llvm_inplace_div_name(ExecutionContext *ctx, String *dest, Value *src) -{ - __qmljs_inplace_div_name(*src, dest, ctx); -} - -void __qmljs_llvm_inplace_mod_name(ExecutionContext *ctx, String *dest, Value *src) -{ - __qmljs_inplace_mod_name(*src, dest, ctx); -} - -void __qmljs_llvm_inplace_shl_name(ExecutionContext *ctx, String *dest, Value *src) -{ - __qmljs_inplace_shl_name(*src, dest, ctx); -} - -void __qmljs_llvm_inplace_shr_name(ExecutionContext *ctx, String *dest, Value *src) -{ - __qmljs_inplace_shr_name(*src, dest, ctx); -} - -void __qmljs_llvm_inplace_ushr_name(ExecutionContext *ctx, String *dest, Value *src) -{ - __qmljs_inplace_ushr_name(*src, dest, ctx); -} - -void __qmljs_llvm_inplace_bit_and_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) -{ - __qmljs_inplace_bit_and_element(*base, *index, *value, ctx); -} - -void __qmljs_llvm_inplace_bit_or_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) -{ - __qmljs_inplace_bit_or_element(*base, *index, *value, ctx); -} - -void __qmljs_llvm_inplace_bit_xor_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) -{ - __qmljs_inplace_bit_xor_element(*base, *index, *value, ctx); -} - -void __qmljs_llvm_inplace_add_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) -{ - __qmljs_inplace_add_element(*base, *index, *value, ctx); -} - -void __qmljs_llvm_inplace_sub_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) -{ - __qmljs_inplace_sub_element(*base, *index, *value, ctx); -} - -void __qmljs_llvm_inplace_mul_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) -{ - __qmljs_inplace_mul_element(*base, *index, *value, ctx); -} - -void __qmljs_llvm_inplace_div_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) -{ - __qmljs_inplace_div_element(*base, *index, *value, ctx); -} - -void __qmljs_llvm_inplace_mod_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) -{ - __qmljs_inplace_mod_element(*base, *index, *value, ctx); -} - -void __qmljs_llvm_inplace_shl_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) -{ - __qmljs_inplace_shl_element(*base, *index, *value, ctx); -} - -void __qmljs_llvm_inplace_shr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) -{ - __qmljs_inplace_shr_element(*base, *index, *value, ctx); -} - -void __qmljs_llvm_inplace_ushr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) -{ - __qmljs_inplace_ushr_element(*base, *index, *value, ctx); -} - -void __qmljs_llvm_inplace_bit_and_member(ExecutionContext *ctx, Value *value, Value *base, String *member) -{ - __qmljs_inplace_bit_and_member(*value, *base, member, ctx); -} - -void __qmljs_llvm_inplace_bit_or_member(ExecutionContext *ctx, Value *value, Value *base, String *member) -{ - __qmljs_inplace_bit_or_member(*value, *base, member, ctx); -} - -void __qmljs_llvm_inplace_bit_xor_member(ExecutionContext *ctx, Value *value, Value *base, String *member) -{ - __qmljs_inplace_bit_xor_member(*value, *base, member, ctx); -} - -void __qmljs_llvm_inplace_add_member(ExecutionContext *ctx, Value *value, Value *base, String *member) -{ - __qmljs_inplace_add_member(*value, *base, member, ctx); -} - -void __qmljs_llvm_inplace_sub_member(ExecutionContext *ctx, Value *value, Value *base, String *member) -{ - __qmljs_inplace_sub_member(*value, *base, member, ctx); -} - -void __qmljs_llvm_inplace_mul_member(ExecutionContext *ctx, Value *value, Value *base, String *member) -{ - __qmljs_inplace_mul_member(*value, *base, member, ctx); -} - -void __qmljs_llvm_inplace_div_member(ExecutionContext *ctx, Value *value, Value *base, String *member) -{ - __qmljs_inplace_div_member(*value, *base, member, ctx); -} - -void __qmljs_llvm_inplace_mod_member(ExecutionContext *ctx, Value *value, Value *base, String *member) -{ - __qmljs_inplace_mod_member(*value, *base, member, ctx); -} - -void __qmljs_llvm_inplace_shl_member(ExecutionContext *ctx, Value *value, Value *base, String *member) -{ - __qmljs_inplace_shl_member(*value, *base, member, ctx); -} - -void __qmljs_llvm_inplace_shr_member(ExecutionContext *ctx, Value *value, Value *base, String *member) -{ - __qmljs_inplace_shr_member(*value, *base, member, ctx); -} - -void __qmljs_llvm_inplace_ushr_member(ExecutionContext *ctx, Value *value, Value *base, String *member) -{ - __qmljs_inplace_ushr_member(*value, *base, member, ctx); -} - -String *__qmljs_llvm_identifier_from_utf8(ExecutionContext *ctx, const char *str) -{ - return __qmljs_identifier_from_utf8(ctx, str); // ### make it unique -} - -void __qmljs_llvm_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) -{ - *result = __qmljs_call_activation_property(context, name, args, argc); -} - -void __qmljs_llvm_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) -{ - Value that = thisObject ? *thisObject : Value::undefinedValue(); - *result = __qmljs_call_value(context, that, *func, args, argc); -} - -void __qmljs_llvm_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) -{ - *result = __qmljs_construct_activation_property(context, name, args, argc); -} - -void __qmljs_llvm_construct_value(ExecutionContext *context, Value *result, const Value *func, Value *args, int argc) -{ - *result = __qmljs_construct_value(context, *func, args, argc); -} - -void __qmljs_llvm_get_activation_property(ExecutionContext *ctx, Value *result, String *name) -{ - *result = __qmljs_get_activation_property(ctx, name); -} - -void __qmljs_llvm_set_activation_property(ExecutionContext *ctx, String *name, Value *value) -{ - __qmljs_set_activation_property(ctx, name, *value); -} - -void __qmljs_llvm_get_property(ExecutionContext *ctx, Value *result, Value *object, String *name) -{ - *result = __qmljs_get_property(ctx, *object, name); -} - -void __qmljs_llvm_call_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) -{ - *result = __qmljs_call_property(context, *base, name, args, argc); -} - -void __qmljs_llvm_construct_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) -{ - *result = __qmljs_construct_property(context, *base, name, args, argc); -} - -void __qmljs_llvm_get_element(ExecutionContext *ctx, Value *result, Value *object, Value *index) -{ - *result = __qmljs_get_element(ctx, *object, *index); -} - -void __qmljs_llvm_set_element(ExecutionContext *ctx, Value *object, Value *index, Value *value) -{ - __qmljs_set_element(ctx, *object, *index, *value); -} - -void __qmljs_llvm_set_property(ExecutionContext *ctx, Value *object, String *name, Value *value) -{ - __qmljs_set_property(ctx, *object, name, *value); -} - -void __qmljs_llvm_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) -{ - __qmljs_builtin_declare_var(ctx, deletable, name); -} - -void __qmljs_llvm_typeof(ExecutionContext *ctx, Value *result, const Value *value) -{ - *result = __qmljs_builtin_typeof(*value, ctx); -} - -void __qmljs_llvm_throw(ExecutionContext *context, Value *value) -{ - __qmljs_throw(*value, context); -} - -void __qmljs_llvm_create_exception_handler(ExecutionContext *context, Value *result) -{ - void *buf = __qmljs_create_exception_handler(context); - *result = Value::fromInt32(setjmp(* static_cast(buf))); -} - -void __qmljs_llvm_delete_exception_handler(ExecutionContext *context) -{ - __qmljs_delete_exception_handler(context); -} - -void __qmljs_llvm_get_exception(ExecutionContext *context, Value *result) -{ - *result = __qmljs_get_exception(context); -} - -void __qmljs_llvm_foreach_iterator_object(ExecutionContext *context, Value *result, Value *in) -{ - *result = __qmljs_foreach_iterator_object(*in, context); -} - -void __qmljs_llvm_foreach_next_property_name(Value *result, Value *it) -{ - *result = __qmljs_foreach_next_property_name(*it); -} - -void __qmljs_llvm_get_this_object(ExecutionContext *ctx, Value *result) -{ - *result = __qmljs_get_thisObject(ctx); -} - -void __qmljs_llvm_delete_subscript(ExecutionContext *ctx, Value *result, Value *base, Value *index) -{ - *result = __qmljs_delete_subscript(ctx, *base, *index); -} - -void __qmljs_llvm_delete_member(ExecutionContext *ctx, Value *result, Value *base, String *name) -{ - *result = __qmljs_delete_member(ctx, *base, name); -} - -void __qmljs_llvm_delete_name(ExecutionContext *ctx, Value *result, String *name) -{ - *result = __qmljs_delete_name(ctx, name); -} - -} // extern "C" diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 07e8b3cc24..0000000000 --- a/main.cpp +++ /dev/null @@ -1,426 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QMLJS_NO_LLVM -# include "qv4_llvm_p.h" -#endif - -#include "debugging.h" -#include "qv4object.h" -#include "qmljs_runtime.h" -#include "qv4functionobject.h" -#include "qv4errorobject.h" -#include "qv4globalobject.h" -#include "qv4codegen_p.h" -#include "qv4isel_masm_p.h" -#include "qv4isel_moth_p.h" -#include "qv4vme_moth_p.h" -#include "qv4syntaxchecker_p.h" -#include "qv4objectproto.h" -#include "qv4isel_p.h" -#include "qv4mm.h" -#include "qmljs_environment.h" - -#include -#include -#include -#include -#include - -#include -#include - -namespace builtins { - -using namespace QQmlJS::VM; - -struct Print: FunctionObject -{ - Print(ExecutionContext *scope): FunctionObject(scope) { - name = scope->engine->newString("print"); - } - - virtual Value call(ExecutionContext *ctx) - { - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { - String *s = ctx->argument(i).toString(ctx); - if (i) - std::cout << ' '; - std::cout << qPrintable(s->toQString()); - } - std::cout << std::endl; - return Value::undefinedValue(); - } -}; - -struct TestHarnessError: FunctionObject -{ - TestHarnessError(ExecutionContext *scope, bool &errorInTestHarness): FunctionObject(scope), errorOccurred(errorInTestHarness) { - name = scope->engine->newString("$ERROR"); - } - - virtual Value call(ExecutionContext *ctx) - { - errorOccurred = true; - - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { - String *s = ctx->argument(i).toString(ctx); - if (i) - std::cerr << ' '; - std::cerr << qPrintable(s->toQString()); - } - std::cerr << std::endl; - return Value::undefinedValue(); - } - - bool &errorOccurred; -}; - -struct GC: public FunctionObject -{ - GC(ExecutionContext* scope) - : FunctionObject(scope) - { - name = scope->engine->newString("gc"); - } - virtual Value call(ExecutionContext *ctx) - { - ctx->engine->memoryManager->runGC(); - return Value::undefinedValue(); - } -}; - -} // builtins - -static void showException(QQmlJS::VM::ExecutionContext *ctx) -{ - QQmlJS::VM::ErrorObject *e = ctx->engine->exception.asErrorObject(); - if (!e) { - std::cerr << "Uncaught exception: " << qPrintable(ctx->engine->exception.toString(ctx)->toQString()) << std::endl; - return; - } - - if (QQmlJS::VM::SyntaxErrorObject *err = e->asSyntaxError()) { - QQmlJS::VM::DiagnosticMessage *msg = err->message(); - if (!msg) { - std::cerr << "Uncaught exception: Syntax error" << std::endl; - return; - } - - for (; msg; msg = msg->next) { - std::cerr << qPrintable(msg->buildFullMessage(ctx)->toQString()) << std::endl; - } - } else { - std::cerr << "Uncaught exception: " << qPrintable(e->__get__(ctx, ctx->engine->identifier(QStringLiteral("message")), 0).toString(ctx)->toQString()) << std::endl; - } -} - -#ifndef QMLJS_NO_LLVM -int executeLLVMCode(void *codePtr) -{ - using namespace QQmlJS; - - if (!codePtr) - return EXIT_FAILURE; - void (*code)(VM::ExecutionContext *) = (void (*)(VM::ExecutionContext *)) codePtr; - - QScopedPointer iSelFactory(new QQmlJS::Moth::ISelFactory); - VM::ExecutionEngine vm(iSelFactory.data()); - VM::ExecutionContext *ctx = vm.rootContext; - - QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); - globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), - QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::Print(ctx))); - - void * buf = __qmljs_create_exception_handler(ctx); - if (setjmp(*(jmp_buf *)buf)) { - showException(ctx); - return EXIT_FAILURE; - } - - code(ctx); - return EXIT_SUCCESS; -} - -int compile(const QString &fileName, const QString &source, QQmlJS::LLVMOutputType outputType) -{ - using namespace QQmlJS; - - IR::Module module; - QQmlJS::Engine ee, *engine = ⅇ - Lexer lexer(engine); - lexer.setCode(source, 1, false); - Parser parser(engine); - - const bool parsed = parser.parseProgram(); - - foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { - std::cerr << qPrintable(fileName) << ':' - << m.loc.startLine << ':' - << m.loc.startColumn << ": error: " - << qPrintable(m.message) << std::endl; - } - - if (!parsed) - return EXIT_FAILURE; - - using namespace AST; - Program *program = AST::cast(parser.rootNode()); - - class MyErrorHandler: public ErrorHandler { - public: - virtual void syntaxError(QQmlJS::VM::DiagnosticMessage *message) { - for (; message; message = message->next) { - std::cerr << qPrintable(message->fileName) << ':' - << message->startLine << ':' - << message->startColumn << ": " - << (message->type == QQmlJS::VM::DiagnosticMessage::Error ? "error" : "warning") << ": " - << qPrintable(message->message) << std::endl; - } - } - } errorHandler; - - Codegen cg(&errorHandler, false); - // FIXME: if the program is empty, we should we generate an empty %entry, or give an error? - /*IR::Function *globalCode =*/ cg(fileName, program, &module); - - int (*exec)(void *) = outputType == LLVMOutputJit ? executeLLVMCode : 0; - return compileWithLLVM(&module, fileName, outputType, exec); -} - -int compileFiles(const QStringList &files, QQmlJS::LLVMOutputType outputType) -{ - foreach (const QString &fileName, files) { - QFile file(fileName); - if (file.open(QFile::ReadOnly)) { - QString source = QString::fromUtf8(file.readAll()); - int result = compile(fileName, source, outputType); - if (result != EXIT_SUCCESS) - return result; - } else { - std::cerr << "Error: cannot open file " << fileName.toUtf8().constData() << std::endl; - return EXIT_FAILURE; - } - } - return EXIT_SUCCESS; -} - -int evaluateCompiledCode(const QStringList &files) -{ - using namespace QQmlJS; - - foreach (const QString &libName, files) { - QFileInfo libInfo(libName); - QLibrary lib(libInfo.absoluteFilePath()); - lib.load(); - QFunctionPointer ptr = lib.resolve("%entry"); -// qDebug("_%%entry resolved to address %p", ptr); - int result = executeLLVMCode((void *) ptr); - if (result != EXIT_SUCCESS) - return result; - } - - return EXIT_SUCCESS; -} - -#endif - - -int main(int argc, char *argv[]) -{ - QCoreApplication app(argc, argv); - QStringList args = app.arguments(); - args.removeFirst(); - - enum { - use_masm, - use_moth, - use_llvm_compiler, - use_llvm_runtime, - use_llvm_jit - } mode = use_masm; - -#ifndef QMLJS_NO_LLVM - QQmlJS::LLVMOutputType fileType = QQmlJS::LLVMOutputObject; -#endif // QMLJS_NO_LLVM - bool enableDebugging = false; - - if (!args.isEmpty()) { - if (args.first() == QLatin1String("-d") || args.first() == QLatin1String("--debug")) { - enableDebugging = true; - args.removeFirst(); - } - } - - if (!args.isEmpty()) { - if (args.first() == QLatin1String("--jit")) { - mode = use_masm; - args.removeFirst(); - } - - if (args.first() == QLatin1String("--interpret")) { - mode = use_moth; - args.removeFirst(); - } - -#ifndef QMLJS_NO_LLVM - if (args.first() == QLatin1String("--compile")) { - mode = use_llvm_compiler; - args.removeFirst(); - - if (!args.isEmpty() && args.first() == QLatin1String("-t")) { - args.removeFirst(); - // Note: keep this list in sync with the enum! - static QStringList fileTypes = QStringList() << QLatin1String("ll") << QLatin1String("bc") << QLatin1String("asm") << QLatin1String("obj"); - if (args.isEmpty() || !fileTypes.contains(args.first())) { - std::cerr << "file types: ll, bc, asm, obj" << std::endl; - return EXIT_FAILURE; - } - fileType = (QQmlJS::LLVMOutputType) fileTypes.indexOf(args.first()); - args.removeFirst(); - } - } - - if (args.first() == QLatin1String("--aot")) { - mode = use_llvm_runtime; - args.removeFirst(); - } - - if (args.first() == QLatin1String("--llvm-jit")) { - mode = use_llvm_jit; - args.removeFirst(); - } -#endif // QMLJS_NO_LLVM - if (args.first() == QLatin1String("--help")) { - std::cerr << "Usage: v4 [|--debug|-d] [|--jit|--interpret|--compile|--aot|--llvm-jit] file..." << std::endl; - return EXIT_SUCCESS; - } - } - - switch (mode) { -#ifdef QMLJS_NO_LLVM - case use_llvm_compiler: - case use_llvm_runtime: - case use_llvm_jit: - std::cerr << "LLVM backend was not built, compiler is unavailable." << std::endl; - return EXIT_FAILURE; -#else // QMLJS_NO_LLVM - case use_llvm_jit: - return compileFiles(args, QQmlJS::LLVMOutputJit); - case use_llvm_compiler: - return compileFiles(args, fileType); - case use_llvm_runtime: - return evaluateCompiledCode(args); -#endif // QMLJS_NO_LLVM - case use_masm: - case use_moth: { - QScopedPointer iSelFactory; - if (mode == use_moth) { - iSelFactory.reset(new QQmlJS::Moth::ISelFactory); - } else { - iSelFactory.reset(new QQmlJS::MASM::ISelFactory); - } - - QQmlJS::VM::ExecutionEngine vm(iSelFactory.data()); - - QScopedPointer debugger; - if (enableDebugging) - debugger.reset(new QQmlJS::Debugging::Debugger(&vm)); - vm.debugger = debugger.data(); - - QQmlJS::VM::ExecutionContext *ctx = vm.rootContext; - - QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); - QQmlJS::VM::Object *print = new (ctx->engine->memoryManager) builtins::Print(ctx); - print->prototype = ctx->engine->objectPrototype; - globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), - QQmlJS::VM::Value::fromObject(print)); - QQmlJS::VM::Object *gc = new (ctx->engine->memoryManager) builtins::GC(ctx); - gc->prototype = ctx->engine->objectPrototype; - globalObject->__put__(ctx, vm.identifier(QStringLiteral("gc")), - QQmlJS::VM::Value::fromObject(gc)); - - bool errorInTestHarness = false; - if (!qgetenv("IN_TEST_HARNESS").isEmpty()) - globalObject->__put__(ctx, vm.identifier(QStringLiteral("$ERROR")), - QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::TestHarnessError(ctx, errorInTestHarness))); - - foreach (const QString &fn, args) { - QFile file(fn); - if (file.open(QFile::ReadOnly)) { - const QString code = QString::fromUtf8(file.readAll()); - file.close(); - - void * buf = __qmljs_create_exception_handler(ctx); - if (setjmp(*(jmp_buf *)buf)) { - showException(ctx); - return EXIT_FAILURE; - } - - QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode, - /*strictMode =*/ false, /*inheritContext =*/ false); - if (!f) - continue; - vm.globalCode = f; - - ctx->strictMode = f->isStrict; - if (debugger) - debugger->aboutToCall(0, ctx); - QQmlJS::VM::Value result = f->code(ctx, f->codeData); - if (debugger) - debugger->justLeft(ctx); - if (!result.isUndefined()) { - if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) - std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; - } - - if (errorInTestHarness) - return EXIT_FAILURE; - } else { - std::cerr << "Error: cannot open file " << fn.toUtf8().constData() << std::endl; - return EXIT_FAILURE; - } - } - - vm.memoryManager->dumpStats(); - } return EXIT_SUCCESS; - } // switch (mode) -} diff --git a/moth/moth.pri b/moth/moth.pri deleted file mode 100644 index 73bd893286..0000000000 --- a/moth/moth.pri +++ /dev/null @@ -1,13 +0,0 @@ -INCLUDEPATH += $$PWD - -HEADERS += \ - $$PWD/qv4isel_moth_p.h \ - $$PWD/qv4instr_moth_p.h \ - $$PWD/qv4vme_moth_p.h - -SOURCES += \ - $$PWD/qv4isel_moth.cpp \ - $$PWD/qv4instr_moth.cpp \ - $$PWD/qv4vme_moth.cpp - -#DEFINES += DO_TRACE_INSTR diff --git a/moth/qv4instr_moth.cpp b/moth/qv4instr_moth.cpp deleted file mode 100644 index a2bad39e00..0000000000 --- a/moth/qv4instr_moth.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "qv4instr_moth_p.h" - -using namespace QQmlJS; -using namespace QQmlJS::Moth; - -int Instr::size(Type type) -{ -#define MOTH_RETURN_INSTR_SIZE(I, FMT) case I: return InstrMeta<(int)I>::Size; - switch (type) { - FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE) - default: return 0; - } -#undef MOTH_RETURN_INSTR_SIZE -} - diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h deleted file mode 100644 index 741ddba79d..0000000000 --- a/moth/qv4instr_moth_p.h +++ /dev/null @@ -1,514 +0,0 @@ -#ifndef QV4INSTR_MOTH_P_H -#define QV4INSTR_MOTH_P_H - -#include -#include "qv4object.h" - -#define FOR_EACH_MOTH_INSTR(F) \ - F(Ret, ret) \ - F(LoadValue, loadValue) \ - F(LoadClosure, loadClosure) \ - F(MoveTemp, moveTemp) \ - F(LoadName, loadName) \ - F(StoreName, storeName) \ - F(LoadElement, loadElement) \ - F(StoreElement, storeElement) \ - F(LoadProperty, loadProperty) \ - F(StoreProperty, storeProperty) \ - F(Push, push) \ - F(CallValue, callValue) \ - F(CallProperty, callProperty) \ - F(CallElement, callElement) \ - F(CallActivationProperty, callActivationProperty) \ - F(CallBuiltinThrow, callBuiltinThrow) \ - F(CallBuiltinCreateExceptionHandler, callBuiltinCreateExceptionHandler) \ - F(CallBuiltinDeleteExceptionHandler, callBuiltinDeleteExceptionHandler) \ - F(CallBuiltinGetException, callBuiltinGetException) \ - F(CallBuiltinPushScope, callBuiltinPushScope) \ - F(CallBuiltinPopScope, callBuiltinPopScope) \ - F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ - F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \ - F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ - F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ - F(CallBuiltinDeleteName, callBuiltinDeleteName) \ - F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \ - F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \ - F(CallBuiltinTypeofName, callBuiltinTypeofName) \ - F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \ - F(CallBuiltinPostIncMember, callBuiltinPostIncMember) \ - F(CallBuiltinPostIncSubscript, callBuiltinPostIncSubscript) \ - F(CallBuiltinPostIncName, callBuiltinPostIncName) \ - F(CallBuiltinPostIncValue, callBuiltinPostIncValue) \ - F(CallBuiltinPostDecMember, callBuiltinPostDecMember) \ - F(CallBuiltinPostDecSubscript, callBuiltinPostDecSubscript) \ - F(CallBuiltinPostDecName, callBuiltinPostDecName) \ - F(CallBuiltinPostDecValue, callBuiltinPostDecValue) \ - F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ - F(CallBuiltinDefineGetterSetter, callBuiltinDefineGetterSetter) \ - F(CallBuiltinDefineProperty, callBuiltinDefineProperty) \ - F(CallBuiltinDefineArrayProperty, callBuiltinDefineArrayProperty) \ - F(CreateValue, createValue) \ - F(CreateProperty, createProperty) \ - F(CreateActivationProperty, createActivationProperty) \ - F(Jump, jump) \ - F(CJump, cjump) \ - F(Unop, unop) \ - F(Binop, binop) \ - F(LoadThis, loadThis) \ - F(InplaceElementOp, inplaceElementOp) \ - F(InplaceMemberOp, inplaceMemberOp) \ - F(InplaceNameOp, inplaceNameOp) - -#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) -# define MOTH_THREADED_INTERPRETER -#endif - -#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QQmlJS::Moth::Instr) - 1) - -#ifdef MOTH_THREADED_INTERPRETER -# define MOTH_INSTR_HEADER void *code; -#else -# define MOTH_INSTR_HEADER quint8 instructionType; -#endif - -#define MOTH_INSTR_ENUM(I, FMT) I, -#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QQmlJS::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK) - - -namespace QQmlJS { -namespace Moth { - -union Instr -{ - struct Param { - enum { - ValueType = 0, - ArgumentType = 1, - LocalType = 2, - TempType = 3 - }; - VM::Value value; - unsigned type : 2; - unsigned index : 30; - - bool isValue() const { return type == ValueType; } - bool isArgument() const { return type == ArgumentType; } - bool isLocal() const { return type == LocalType; } - bool isTemp() const { return type == TempType; } - - static Param createValue(const VM::Value &v) - { - Param p; - p.type = ValueType; - p.value = v; - return p; - } - - static Param createArgument(unsigned idx) - { - Param p; - p.type = ArgumentType; - p.index = idx; - return p; - } - - static Param createLocal(unsigned idx) - { - Param p; - p.type = LocalType; - p.index = idx; - return p; - } - - static Param createTemp(unsigned idx) - { - Param p; - p.type = TempType; - p.index = idx; - return p; - } - }; - - enum Type { - FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) - }; - - struct instr_common { - MOTH_INSTR_HEADER - }; - struct instr_ret { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_loadValue { - MOTH_INSTR_HEADER - Param value; - Param result; - }; - struct instr_moveTemp { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_loadClosure { - MOTH_INSTR_HEADER - VM::Function *value; - Param result; - }; - struct instr_loadName { - MOTH_INSTR_HEADER - VM::String *name; - Param result; - }; - struct instr_storeName { - MOTH_INSTR_HEADER - VM::String *name; - Param source; - }; - struct instr_loadProperty { - MOTH_INSTR_HEADER - VM::String *name; - Param base; - Param result; - }; - struct instr_storeProperty { - MOTH_INSTR_HEADER - VM::String *name; - Param base; - Param source; - }; - struct instr_loadElement { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_storeElement { - MOTH_INSTR_HEADER - Param base; - Param index; - Param source; - }; - struct instr_push { - MOTH_INSTR_HEADER - quint32 value; - }; - struct instr_callValue { - MOTH_INSTR_HEADER - quint32 argc; - quint32 args; - Param dest; - Param result; - }; - struct instr_callProperty { - MOTH_INSTR_HEADER - VM::String *name; - quint32 argc; - quint32 args; - Param base; - Param result; - }; - struct instr_callElement { - MOTH_INSTR_HEADER - Param base; - Param index; - quint32 argc; - quint32 args; - Param result; - }; - struct instr_callActivationProperty { - MOTH_INSTR_HEADER - VM::String *name; - quint32 argc; - quint32 args; - Param result; - }; - struct instr_callBuiltinThrow { - MOTH_INSTR_HEADER - Param arg; - }; - struct instr_callBuiltinCreateExceptionHandler { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_callBuiltinDeleteExceptionHandler { - MOTH_INSTR_HEADER - }; - struct instr_callBuiltinGetException { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_callBuiltinPushScope { - MOTH_INSTR_HEADER - Param arg; - }; - struct instr_callBuiltinPopScope { - MOTH_INSTR_HEADER - }; - struct instr_callBuiltinForeachIteratorObject { - MOTH_INSTR_HEADER - Param arg; - Param result; - }; - struct instr_callBuiltinForeachNextPropertyName { - MOTH_INSTR_HEADER - Param arg; - Param result; - }; - struct instr_callBuiltinDeleteMember { - MOTH_INSTR_HEADER - VM::String *member; - Param base; - Param result; - }; - struct instr_callBuiltinDeleteSubscript { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_callBuiltinDeleteName { - MOTH_INSTR_HEADER - VM::String *name; - Param result; - }; - struct instr_callBuiltinTypeofMember { - MOTH_INSTR_HEADER - VM::String *member; - Param base; - Param result; - }; - struct instr_callBuiltinTypeofSubscript { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_callBuiltinTypeofName { - MOTH_INSTR_HEADER - VM::String *name; - Param result; - }; - struct instr_callBuiltinTypeofValue { - MOTH_INSTR_HEADER - Param value; - Param result; - }; - struct instr_callBuiltinPostIncMember { - MOTH_INSTR_HEADER - Param base; - VM::String *member; - Param result; - }; - struct instr_callBuiltinPostIncSubscript { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_callBuiltinPostIncName { - MOTH_INSTR_HEADER - VM::String *name; - Param result; - }; - struct instr_callBuiltinPostIncValue { - MOTH_INSTR_HEADER - Param value; - Param result; - }; - struct instr_callBuiltinPostDecMember { - MOTH_INSTR_HEADER - Param base; - VM::String *member; - Param result; - }; - struct instr_callBuiltinPostDecSubscript { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_callBuiltinPostDecName { - MOTH_INSTR_HEADER - VM::String *name; - Param result; - }; - struct instr_callBuiltinPostDecValue { - MOTH_INSTR_HEADER - Param value; - Param result; - }; - struct instr_callBuiltinDeclareVar { - MOTH_INSTR_HEADER - VM::String *varName; - bool isDeletable; - }; - struct instr_callBuiltinDefineGetterSetter { - MOTH_INSTR_HEADER - VM::String *name; - Param object; - Param getter; - Param setter; - }; - struct instr_callBuiltinDefineProperty { - MOTH_INSTR_HEADER - VM::String *name; - Param object; - Param value; - }; - struct instr_callBuiltinDefineArrayProperty { - MOTH_INSTR_HEADER - Param object; - Param value; - int index; - }; - struct instr_createValue { - MOTH_INSTR_HEADER - quint32 argc; - quint32 args; - Param func; - Param result; - }; - struct instr_createProperty { - MOTH_INSTR_HEADER - VM::String *name; - quint32 argc; - quint32 args; - Param base; - Param result; - }; - struct instr_createActivationProperty { - MOTH_INSTR_HEADER - VM::String *name; - quint32 argc; - quint32 args; - Param result; - }; - struct instr_jump { - MOTH_INSTR_HEADER - ptrdiff_t offset; - }; - struct instr_cjump { - MOTH_INSTR_HEADER - ptrdiff_t offset; - Param condition; - }; - struct instr_unop { - MOTH_INSTR_HEADER - VM::Value (*alu)(const VM::Value value, VM::ExecutionContext *ctx); - Param source; - Param result; - }; - struct instr_binop { - MOTH_INSTR_HEADER - VM::Value (*alu)(const VM::Value , const VM::Value, VM::ExecutionContext *); - Param lhs; - Param rhs; - Param result; - }; - struct instr_loadThis { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_inplaceElementOp { - MOTH_INSTR_HEADER - void (*alu)(VM::Value, VM::Value, VM::Value, VM::ExecutionContext *); - Param base; - Param index; - Param source; - }; - struct instr_inplaceMemberOp { - MOTH_INSTR_HEADER - void (*alu)(VM::Value, VM::Value, VM::String *, VM::ExecutionContext *); - VM::String *member; - Param base; - Param source; - }; - struct instr_inplaceNameOp { - MOTH_INSTR_HEADER - void (*alu)(VM::Value, VM::String *, VM::ExecutionContext *); - VM::String *name; - Param source; - }; - - instr_common common; - instr_ret ret; - instr_loadValue loadValue; - instr_moveTemp moveTemp; - instr_loadClosure loadClosure; - instr_loadName loadName; - instr_storeName storeName; - instr_loadElement loadElement; - instr_storeElement storeElement; - instr_loadProperty loadProperty; - instr_storeProperty storeProperty; - instr_push push; - instr_callValue callValue; - instr_callProperty callProperty; - instr_callElement callElement; - instr_callActivationProperty callActivationProperty; - instr_callBuiltinThrow callBuiltinThrow; - instr_callBuiltinCreateExceptionHandler callBuiltinCreateExceptionHandler; - instr_callBuiltinDeleteExceptionHandler callBuiltinDeleteExceptionHandler; - instr_callBuiltinGetException callBuiltinGetException; - instr_callBuiltinPushScope callBuiltinPushScope; - instr_callBuiltinPopScope callBuiltinPopScope; - instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; - instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName; - instr_callBuiltinDeleteMember callBuiltinDeleteMember; - instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; - instr_callBuiltinDeleteName callBuiltinDeleteName; - instr_callBuiltinTypeofMember callBuiltinTypeofMember; - instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript; - instr_callBuiltinTypeofName callBuiltinTypeofName; - instr_callBuiltinTypeofValue callBuiltinTypeofValue; - instr_callBuiltinPostIncMember callBuiltinPostIncMember; - instr_callBuiltinPostIncSubscript callBuiltinPostIncSubscript; - instr_callBuiltinPostIncName callBuiltinPostIncName; - instr_callBuiltinPostIncValue callBuiltinPostIncValue; - instr_callBuiltinPostDecMember callBuiltinPostDecMember; - instr_callBuiltinPostDecSubscript callBuiltinPostDecSubscript; - instr_callBuiltinPostDecName callBuiltinPostDecName; - instr_callBuiltinPostDecValue callBuiltinPostDecValue; - instr_callBuiltinDeclareVar callBuiltinDeclareVar; - instr_callBuiltinDefineGetterSetter callBuiltinDefineGetterSetter; - instr_callBuiltinDefineProperty callBuiltinDefineProperty; - instr_callBuiltinDefineArrayProperty callBuiltinDefineArrayProperty; - instr_createValue createValue; - instr_createProperty createProperty; - instr_createActivationProperty createActivationProperty; - instr_jump jump; - instr_cjump cjump; - instr_unop unop; - instr_binop binop; - instr_loadThis loadThis; - instr_inplaceElementOp inplaceElementOp; - instr_inplaceMemberOp inplaceMemberOp; - instr_inplaceNameOp inplaceNameOp; - - static int size(Type type); -}; - -template -struct InstrMeta { -}; - -#define MOTH_INSTR_META_TEMPLATE(I, FMT) \ - template<> struct InstrMeta<(int)Instr::I> { \ - enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \ - typedef Instr::instr_##FMT DataType; \ - static const DataType &data(const Instr &instr) { return instr.FMT; } \ - static void setData(Instr &instr, const DataType &v) { instr.FMT = v; } \ - }; -FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); -#undef MOTH_INSTR_META_TEMPLATE - -template -class InstrData : public InstrMeta::DataType -{ -}; - -} // namespace Moth -} // namespace QQmlJS - -#endif // QV4INSTR_MOTH_P_H diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp deleted file mode 100644 index 5522be4c32..0000000000 --- a/moth/qv4isel_moth.cpp +++ /dev/null @@ -1,975 +0,0 @@ -#include "qv4isel_util_p.h" -#include "qv4isel_moth_p.h" -#include "qv4vme_moth_p.h" -#include "qv4functionobject.h" -#include "qv4regexpobject.h" -#include "debugging.h" - -using namespace QQmlJS; -using namespace QQmlJS::Moth; - -namespace { - -QTextStream qout(stderr, QIODevice::WriteOnly); - -//#define DEBUG_TEMP_COMPRESSION -#ifdef DEBUG_TEMP_COMPRESSION -# define DBTC(x) x -#else // !DEBUG_TEMP_COMPRESSION -# define DBTC(x) -#endif // DEBUG_TEMP_COMPRESSION -class CompressTemps: public IR::StmtVisitor, IR::ExprVisitor -{ -public: - void run(IR::Function *function) - { - DBTC(qDebug() << "starting on function" << (*function->name) << "with" << function->tempCount << "temps.";) - - _seenTemps.clear(); - _nextFree = 0; - _active.reserve(function->tempCount); - _localCount = function->locals.size(); - - QVector pinned; - foreach (IR::BasicBlock *block, function->basicBlocks) { - if (IR::Stmt *last = block->terminator()) { - const QBitArray &liveOut = last->d->liveOut; - for (int i = 0, ei = liveOut.size(); i < ei; ++i) { - if (liveOut.at(i) && !pinned.contains(i)) { - pinned.append(i); - add(i - _localCount, _nextFree); - } - } - } - } - _pinnedCount = _nextFree; - - int maxUsed = _nextFree; - - foreach (IR::BasicBlock *block, function->basicBlocks) { - DBTC(qDebug("L%d:", block->index)); - - for (int i = 0, ei = block->statements.size(); i < ei; ++i ) { - _currentStatement = block->statements[i]; - if (i == 0) - expireOld(); - - DBTC(_currentStatement->dump(qout);qout<d) - _currentStatement->accept(this); - } - maxUsed = std::max(maxUsed, _nextFree); - } - DBTC(qDebug() << "function" << (*function->name) << "uses" << maxUsed << "temps.";) - function->tempCount = maxUsed + _localCount; - } - -private: - virtual void visitConst(IR::Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(IR::Name *) {} - virtual void visitClosure(IR::Closure *) {} - virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); } - virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(IR::Member *e) { e->base->accept(this); } - virtual void visitExp(IR::Exp *s) { s->expr->accept(this); } - virtual void visitEnter(IR::Enter *) {} - virtual void visitLeave(IR::Leave *) {} - virtual void visitJump(IR::Jump *) {} - virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); } - virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } - - virtual void visitTemp(IR::Temp *e) { - if (_seenTemps.contains(e)) - return; - _seenTemps.insert(e); - - if (e->index < 0) - return; - if (e->index < _localCount) // don't optimise locals yet. - return; - - e->index = remap(e->index - _localCount) + _localCount; - } - - virtual void visitCall(IR::Call *e) { - e->base->accept(this); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitNew(IR::New *e) { - e->base->accept(this); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitMove(IR::Move *s) { - s->target->accept(this); - s->source->accept(this); - } - - int remap(int tempIndex) { - for (ActiveTemps::const_iterator i = _active.begin(), ei = _active.end(); i < ei; ++i) { - if (i->first == tempIndex) { - DBTC(qDebug() << " lookup" << (tempIndex + _localCount) << "->" << (i->second + _localCount);) - return i->second; - } - } - - int firstFree = expireOld(); - add(tempIndex, firstFree); - return firstFree; - } - - void add(int tempIndex, int firstFree) { - if (_nextFree <= firstFree) - _nextFree = firstFree + 1; - _active.prepend(qMakePair(tempIndex, firstFree)); - DBTC(qDebug() << " add" << (tempIndex + _localCount) << "->" << (firstFree+ _localCount);) - } - - int expireOld() { - Q_ASSERT(_currentStatement->d); - - const QBitArray &liveIn = _currentStatement->d->liveIn; - QBitArray inUse(_nextFree); - int i = 0; - while (i < _active.size()) { - const QPair &p = _active[i]; - - if (p.second < _pinnedCount) { - inUse.setBit(p.second); - ++i; - continue; - } - - if (liveIn[p.first + _localCount]) { - inUse[p.second] = true; - ++i; - } else { - DBTC(qDebug() << " remove" << (p.first + _localCount) << "->" << (p.second + _localCount);) - _active.remove(i); - } - } - for (int i = 0, ei = inUse.size(); i < ei; ++i) - if (!inUse[i]) - return i; - return _nextFree; - } - -private: - typedef QVector > ActiveTemps; - ActiveTemps _active; - QSet _seenTemps; - IR::Stmt *_currentStatement; - int _localCount; - int _nextFree; - int _pinnedCount; -}; -#undef DBTC - -typedef VM::Value (*ALUFunction)(const VM::Value, const VM::Value, VM::ExecutionContext*); -inline ALUFunction aluOpFunction(IR::AluOp op) -{ - switch (op) { - case IR::OpInvalid: - return 0; - case IR::OpIfTrue: - return 0; - case IR::OpNot: - return 0; - case IR::OpUMinus: - return 0; - case IR::OpUPlus: - return 0; - case IR::OpCompl: - return 0; - case IR::OpBitAnd: - return VM::__qmljs_bit_and; - case IR::OpBitOr: - return VM::__qmljs_bit_or; - case IR::OpBitXor: - return VM::__qmljs_bit_xor; - case IR::OpAdd: - return VM::__qmljs_add; - case IR::OpSub: - return VM::__qmljs_sub; - case IR::OpMul: - return VM::__qmljs_mul; - case IR::OpDiv: - return VM::__qmljs_div; - case IR::OpMod: - return VM::__qmljs_mod; - case IR::OpLShift: - return VM::__qmljs_shl; - case IR::OpRShift: - return VM::__qmljs_shr; - case IR::OpURShift: - return VM::__qmljs_ushr; - case IR::OpGt: - return VM::__qmljs_gt; - case IR::OpLt: - return VM::__qmljs_lt; - case IR::OpGe: - return VM::__qmljs_ge; - case IR::OpLe: - return VM::__qmljs_le; - case IR::OpEqual: - return VM::__qmljs_eq; - case IR::OpNotEqual: - return VM::__qmljs_ne; - case IR::OpStrictEqual: - return VM::__qmljs_se; - case IR::OpStrictNotEqual: - return VM::__qmljs_sne; - case IR::OpInstanceof: - return VM::__qmljs_instanceof; - case IR::OpIn: - return VM::__qmljs_in; - case IR::OpAnd: - return 0; - case IR::OpOr: - return 0; - default: - assert(!"Unknown AluOp"); - return 0; - } -}; - -} // anonymous namespace - -InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module) - : EvalInstructionSelection(engine, module) - , _function(0) - , _vmFunction(0) - , _block(0) - , _codeStart(0) - , _codeNext(0) - , _codeEnd(0) -{ -} - -InstructionSelection::~InstructionSelection() -{ -} - -void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) -{ - IR::BasicBlock *block; - - QHash > patches; - QHash addrs; - - int codeSize = 4096; - uchar *codeStart = new uchar[codeSize]; - uchar *codeNext = codeStart; - uchar *codeEnd = codeStart + codeSize; - - qSwap(_function, function); - qSwap(_vmFunction, vmFunction); - qSwap(block, _block); - qSwap(patches, _patches); - qSwap(addrs, _addrs); - qSwap(codeStart, _codeStart); - qSwap(codeNext, _codeNext); - qSwap(codeEnd, _codeEnd); - - // TODO: FIXME: fix the temp compression with the new temp index layout. -// CompressTemps().run(_function); - - int locals = frameSize(); - assert(locals >= 0); - - Instruction::Push push; - push.value = quint32(locals); - addInstruction(push); - - foreach (_block, _function->basicBlocks) { - _addrs.insert(_block, _codeNext - _codeStart); - - foreach (IR::Stmt *s, _block->statements) - s->accept(this); - } - - patchJumpAddresses(); - - _vmFunction->code = VME::exec; - _vmFunction->codeData = squeezeCode(); - - qSwap(_function, function); - qSwap(_vmFunction, vmFunction); - qSwap(block, _block); - qSwap(patches, _patches); - qSwap(addrs, _addrs); - qSwap(codeStart, _codeStart); - qSwap(codeNext, _codeNext); - qSwap(codeEnd, _codeEnd); - - delete[] codeStart; -} - -void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) -{ - Instruction::CallValue call; - prepareCallArgs(args, call.argc, call.args); - call.dest = getParam(value); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) -{ - // call the property on the loaded base - Instruction::CallProperty call; - call.base = getParam(base); - call.name = identifier(name); - prepareCallArgs(args, call.argc, call.args); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) -{ - // call the property on the loaded base - Instruction::CallElement call; - call.base = getParam(base); - call.index = getParam(index); - prepareCallArgs(args, call.argc, call.args); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::constructActivationProperty(IR::Name *func, - IR::ExprList *args, - IR::Temp *result) -{ - Instruction::CreateActivationProperty create; - create.name = identifier(*func->id); - prepareCallArgs(args, create.argc, create.args); - create.result = getResultParam(result); - addInstruction(create); -} - -void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) -{ - Instruction::CreateProperty create; - create.base = getParam(base); - create.name = identifier(name); - prepareCallArgs(args, create.argc, create.args); - create.result = getResultParam(result); - addInstruction(create); -} - -void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) -{ - Instruction::CreateValue create; - create.func = getParam(value); - prepareCallArgs(args, create.argc, create.args); - create.result = getResultParam(result); - addInstruction(create); -} - -void InstructionSelection::loadThisObject(IR::Temp *temp) -{ - Instruction::LoadThis load; - load.result = getResultParam(temp); - addInstruction(load); -} - -void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) -{ - assert(sourceConst); - - Instruction::LoadValue load; - load.value = getParam(sourceConst); - load.result = getResultParam(targetTemp); - addInstruction(load); -} - -void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) -{ - Instruction::LoadValue load; - load.value = Instr::Param::createValue(VM::Value::fromString(identifier(str))); - load.result = getResultParam(targetTemp); - addInstruction(load); -} - -void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) -{ - Instruction::LoadValue load; - load.value = Instr::Param::createValue( - VM::Value::fromObject(engine()->newRegExpObject( - *sourceRegexp->value, - sourceRegexp->flags))); - load.result = getResultParam(targetTemp); - addInstruction(load); -} - -void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) -{ - Instruction::LoadName load; - load.name = identifier(name); - load.result = getResultParam(temp); - addInstruction(load); -} - -void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) -{ - Instruction::StoreName store; - store.source = getParam(source); - store.name = identifier(targetName); - addInstruction(store); -} - -void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) -{ - VM::Function *vmFunc = vmFunction(closure->value); - assert(vmFunc); - Instruction::LoadClosure load; - load.value = vmFunc; - load.result = getResultParam(target); - addInstruction(load); -} - -void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::Temp *target) -{ - Instruction::LoadProperty load; - load.base = getParam(base); - load.name = identifier(name); - load.result = getResultParam(target); - addInstruction(load); -} - -void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) -{ - Instruction::StoreProperty store; - store.base = getParam(targetBase); - store.name = identifier(targetName); - store.source = getParam(source); - addInstruction(store); -} - -void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) -{ - Instruction::LoadElement load; - load.base = getParam(base); - load.index = getParam(index); - load.result = getResultParam(target); - addInstruction(load); -} - -void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) -{ - Instruction::StoreElement store; - store.base = getParam(targetBase); - store.index = getParam(targetIndex); - store.source = getParam(source); - addInstruction(store); -} - -void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) -{ - Instruction::MoveTemp move; - move.source = getParam(sourceTemp); - move.result = getResultParam(targetTemp); - addInstruction(move); -} - -void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) -{ - VM::Value (*op)(const VM::Value value, VM::ExecutionContext *ctx) = 0; - switch (oper) { - case IR::OpIfTrue: assert(!"unreachable"); break; - case IR::OpNot: op = VM::__qmljs_not; break; - case IR::OpUMinus: op = VM::__qmljs_uminus; break; - case IR::OpUPlus: op = VM::__qmljs_uplus; break; - case IR::OpCompl: op = VM::__qmljs_compl; break; - case IR::OpIncrement: op = VM::__qmljs_increment; break; - case IR::OpDecrement: op = VM::__qmljs_decrement; break; - default: assert(!"unreachable"); break; - } // switch - - if (op) { - Instruction::Unop unop; - unop.alu = op; - unop.source = getParam(sourceTemp); - unop.result = getResultParam(targetTemp); - addInstruction(unop); - } else { - qWarning(" UNOP1"); - } -} - -void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) -{ - Instruction::Binop binop; - binop.alu = aluOpFunction(oper); - binop.lhs = getParam(leftSource); - binop.rhs = getParam(rightSource); - binop.result = getResultParam(target); - addInstruction(binop); -} - -void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) -{ - void (*op)(VM::Value value, VM::String *name, VM::ExecutionContext *ctx) = 0; - switch (oper) { - case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_name; break; - case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_name; break; - case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_name; break; - case IR::OpAdd: op = VM::__qmljs_inplace_add_name; break; - case IR::OpSub: op = VM::__qmljs_inplace_sub_name; break; - case IR::OpMul: op = VM::__qmljs_inplace_mul_name; break; - case IR::OpDiv: op = VM::__qmljs_inplace_div_name; break; - case IR::OpMod: op = VM::__qmljs_inplace_mod_name; break; - case IR::OpLShift: op = VM::__qmljs_inplace_shl_name; break; - case IR::OpRShift: op = VM::__qmljs_inplace_shr_name; break; - case IR::OpURShift: op = VM::__qmljs_inplace_ushr_name; break; - default: break; - } - - if (op) { - Instruction::InplaceNameOp ieo; - ieo.alu = op; - ieo.name = identifier(targetName); - ieo.source = getParam(sourceExpr); - addInstruction(ieo); - } -} - -void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) -{ - void (*op)(VM::Value base, VM::Value index, VM::Value value, VM::ExecutionContext *ctx) = 0; - switch (oper) { - case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_element; break; - case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_element; break; - case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_element; break; - case IR::OpAdd: op = VM::__qmljs_inplace_add_element; break; - case IR::OpSub: op = VM::__qmljs_inplace_sub_element; break; - case IR::OpMul: op = VM::__qmljs_inplace_mul_element; break; - case IR::OpDiv: op = VM::__qmljs_inplace_div_element; break; - case IR::OpMod: op = VM::__qmljs_inplace_mod_element; break; - case IR::OpLShift: op = VM::__qmljs_inplace_shl_element; break; - case IR::OpRShift: op = VM::__qmljs_inplace_shr_element; break; - case IR::OpURShift: op = VM::__qmljs_inplace_ushr_element; break; - default: break; - } - - Instruction::InplaceElementOp ieo; - ieo.alu = op; - ieo.base = getParam(targetBaseTemp); - ieo.index = getParam(targetIndexTemp); - ieo.source = getParam(sourceExpr); - addInstruction(ieo); -} - -void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) -{ - void (*op)(VM::Value value, VM::Value base, VM::String *name, VM::ExecutionContext *ctx) = 0; - switch (oper) { - case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_member; break; - case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_member; break; - case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_member; break; - case IR::OpAdd: op = VM::__qmljs_inplace_add_member; break; - case IR::OpSub: op = VM::__qmljs_inplace_sub_member; break; - case IR::OpMul: op = VM::__qmljs_inplace_mul_member; break; - case IR::OpDiv: op = VM::__qmljs_inplace_div_member; break; - case IR::OpMod: op = VM::__qmljs_inplace_mod_member; break; - case IR::OpLShift: op = VM::__qmljs_inplace_shl_member; break; - case IR::OpRShift: op = VM::__qmljs_inplace_shr_member; break; - case IR::OpURShift: op = VM::__qmljs_inplace_ushr_member; break; - default: break; - } - - Instruction::InplaceMemberOp imo; - imo.alu = op; - imo.base = getParam(targetBase); - imo.member = identifier(targetName); - imo.source = getParam(source); - addInstruction(imo); -} - -void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 &args) -{ - bool singleArgIsTemp = false; - if (e && e->next == 0) { - // ok, only 1 argument in the call... - const int idx = e->expr->asTemp()->index; - // We can only pass a reference into the stack, which holds temps that - // are not arguments (idx >= 0) nor locals (idx >= localCound). - singleArgIsTemp = idx >= _function->locals.size(); - } - - if (singleArgIsTemp) { - // We pass single arguments as references to the stack, but only if it's not a local or an argument. - argc = 1; - args = e->expr->asTemp()->index - _function->locals.size(); - } else if (e) { - // We need to move all the temps into the function arg array - int argLocation = outgoingArgumentTempStart(); - assert(argLocation >= 0); - argc = 0; - args = argLocation; - while (e) { - Instruction::MoveTemp move; - move.source = getParam(e->expr); - move.result = Instr::Param::createTemp(argLocation); - addInstruction(move); - ++argLocation; - ++argc; - e = e->next; - } - } else { - argc = 0; - args = 0; - } -} - -void InstructionSelection::visitJump(IR::Jump *s) -{ - Instruction::Jump jump; - jump.offset = 0; - ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); - - _patches[s->target].append(loc); -} - -void InstructionSelection::visitCJump(IR::CJump *s) -{ - Instr::Param condition; - if (IR::Temp *t = s->cond->asTemp()) { - condition = getResultParam(t); - } else if (IR::Binop *b = s->cond->asBinop()) { - condition = getResultParam(0); - Instruction::Binop binop; - binop.alu = aluOpFunction(b->op); - binop.lhs = getParam(b->left); - binop.rhs = getParam(b->right); - binop.result = condition; - addInstruction(binop); - } else { - Q_UNIMPLEMENTED(); - } - - Instruction::CJump jump; - jump.offset = 0; - jump.condition = condition; - ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); - _patches[s->iftrue].append(trueLoc); - - if (_block->index + 1 != s->iffalse->index) { - Instruction::Jump jump; - jump.offset = 0; - ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); - _patches[s->iffalse].append(falseLoc); - } -} - -void InstructionSelection::visitRet(IR::Ret *s) -{ - Instruction::Ret ret; - ret.result = getParam(s->expr); - addInstruction(ret); -} - -void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) -{ - Instruction::CallActivationProperty call; - call.name = identifier(*func->id); - prepareCallArgs(args, call.argc, call.args); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) -{ - Instruction::CallBuiltinTypeofMember call; - call.base = getParam(base); - call.member = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) -{ - Instruction::CallBuiltinTypeofSubscript call; - call.base = getParam(base); - call.index = getParam(index); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result) -{ - Instruction::CallBuiltinTypeofName call; - call.name = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) -{ - Instruction::CallBuiltinTypeofValue call; - call.value = getParam(value); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) -{ - Instruction::CallBuiltinDeleteMember call; - call.base = getParam(base); - call.member = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) -{ - Instruction::CallBuiltinDeleteSubscript call; - call.base = getParam(base); - call.index = getParam(index); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) -{ - Instruction::CallBuiltinDeleteName call; - call.name = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) -{ - Instruction::LoadValue load; - load.value = Instr::Param::createValue(VM::Value::fromBoolean(false)); - load.result = getResultParam(result); - addInstruction(load); -} - -void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) -{ - Instruction::CallBuiltinPostDecMember call; - call.base = getParam(base); - call.member = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) -{ - Instruction::CallBuiltinPostDecSubscript call; - call.base = getParam(base); - call.index = getParam(index); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result) -{ - Instruction::CallBuiltinPostDecName call; - call.name = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) -{ - Instruction::CallBuiltinPostDecValue call; - call.value = getParam(value); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) -{ - Instruction::CallBuiltinPostIncMember call; - call.base = getParam(base); - call.member = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) -{ - Instruction::CallBuiltinPostIncSubscript call; - call.base = getParam(base); - call.index = getParam(index); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result) -{ - Instruction::CallBuiltinPostIncName call; - call.name = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) -{ - Instruction::CallBuiltinPostIncValue call; - call.value = getParam(value); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinThrow(IR::Temp *arg) -{ - Instruction::CallBuiltinThrow call; - call.arg = getParam(arg); - addInstruction(call); -} - -void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) -{ - Instruction::CallBuiltinCreateExceptionHandler call; - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeleteExceptionHandler() -{ - Instruction::CallBuiltinDeleteExceptionHandler call; - addInstruction(call); -} - -void InstructionSelection::callBuiltinGetException(IR::Temp *result) -{ - Instruction::CallBuiltinGetException call; - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) -{ - Instruction::CallBuiltinForeachIteratorObject call; - call.arg = getParam(arg); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) -{ - Instruction::CallBuiltinForeachNextPropertyName call; - call.arg = getParam(arg); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) -{ - Instruction::CallBuiltinPushScope call; - call.arg = getParam(arg); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPopScope() -{ - Instruction::CallBuiltinPopScope call; - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) -{ - Instruction::CallBuiltinDeclareVar call; - call.isDeletable = deletable; - call.varName = identifier(name); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) -{ - Instruction::CallBuiltinDefineGetterSetter call; - call.object = getParam(object); - call.name = identifier(name); - call.getter = getParam(getter); - call.setter = getParam(setter); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) -{ - Instruction::CallBuiltinDefineProperty call; - call.object = getParam(object); - call.name = identifier(name); - call.value = getParam(value); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) -{ - Instruction::CallBuiltinDefineArrayProperty call; - call.object = getParam(object); - call.index = index; - call.value = getParam(value); - addInstruction(call); -} - -ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) -{ -#ifdef MOTH_THREADED_INTERPRETER - instr.common.code = VME::instructionJumpTable()[static_cast(type)]; -#else - instr.common.instructionType = type; -#endif - - int instructionSize = Instr::size(type); - if (_codeEnd - _codeNext < instructionSize) { - int currSize = _codeEnd - _codeStart; - uchar *newCode = new uchar[currSize * 2]; - ::memset(newCode + currSize, 0, currSize); - ::memcpy(newCode, _codeStart, currSize); - _codeNext = _codeNext - _codeStart + newCode; - delete[] _codeStart; - _codeStart = newCode; - _codeEnd = _codeStart + currSize * 2; - } - - ::memcpy(_codeNext, reinterpret_cast(&instr), instructionSize); - ptrdiff_t ptrOffset = _codeNext - _codeStart; - _codeNext += instructionSize; - - return ptrOffset; -} - -void InstructionSelection::patchJumpAddresses() -{ - typedef QHash >::ConstIterator PatchIt; - for (PatchIt i = _patches.begin(), ei = _patches.end(); i != ei; ++i) { - Q_ASSERT(_addrs.contains(i.key())); - ptrdiff_t target = _addrs.value(i.key()); - - const QVector &patchList = i.value(); - for (int ii = 0, eii = patchList.count(); ii < eii; ++ii) { - ptrdiff_t patch = patchList.at(ii); - - *((ptrdiff_t *)(_codeStart + patch)) = target - patch; - } - } - - _patches.clear(); - _addrs.clear(); -} - -uchar *InstructionSelection::squeezeCode() const -{ - int codeSize = _codeNext - _codeStart; - uchar *squeezed = new uchar[codeSize]; - ::memcpy(squeezed, _codeStart, codeSize); - return squeezed; -} - -VM::String *InstructionSelection::identifier(const QString &s) -{ - VM::String *str = engine()->identifier(s); - _vmFunction->identifiers.append(str); - return str; -} diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h deleted file mode 100644 index 90b191b627..0000000000 --- a/moth/qv4isel_moth_p.h +++ /dev/null @@ -1,167 +0,0 @@ -#ifndef QV4ISEL_MOTH_P_H -#define QV4ISEL_MOTH_P_H - -#include "qv4isel_p.h" -#include "qv4ir_p.h" -#include "qv4object.h" -#include "qv4instr_moth_p.h" - -namespace QQmlJS { -namespace Moth { - -class InstructionSelection: - public IR::InstructionSelection, - public EvalInstructionSelection -{ -public: - InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); - ~InstructionSelection(); - - virtual void run(VM::Function *vmFunction, IR::Function *function); - -protected: - virtual void visitJump(IR::Jump *); - virtual void visitCJump(IR::CJump *); - virtual void visitRet(IR::Ret *); - - virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result); - virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result); - virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); - virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); - virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result); - virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result); - virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); - virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); - virtual void callBuiltinDeleteValue(IR::Temp *result); - virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result); - virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); - virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result); - virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result); - virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result); - virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); - virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); - virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); - virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); - virtual void callBuiltinDeleteExceptionHandler(); - virtual void callBuiltinGetException(IR::Temp *result); - virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); - virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); - virtual void callBuiltinPushWithScope(IR::Temp *arg); - virtual void callBuiltinPopScope(); - virtual void callBuiltinDeclareVar(bool deletable, const QString &name); - virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); - virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); - virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); - virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); - virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); - virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result); - virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); - virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); - virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); - virtual void loadThisObject(IR::Temp *temp); - virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp); - virtual void loadString(const QString &str, IR::Temp *targetTemp); - virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); - virtual void getActivationProperty(const QString &name, IR::Temp *temp); - virtual void setActivationProperty(IR::Expr *source, const QString &targetName); - virtual void initClosure(IR::Closure *closure, IR::Temp *target); - virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); - virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); - virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target); - virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); - virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); - virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); - virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); - virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); - virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); - virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); - -private: - struct Instruction { -#define MOTH_INSTR_DATA_TYPEDEF(I, FMT) typedef InstrData I; - FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF) -#undef MOTH_INSTR_DATA_TYPEDEF - private: - Instruction(); - }; - - Instr::Param getParam(IR::Expr *e) - { - Q_ASSERT(e); - - typedef Instr::Param Param; - if (IR::Const *c = e->asConst()) { - return Param::createValue(convertToValue(c)); - } else if (IR::Temp *t = e->asTemp()) { - const int index = t->index; - if (index < 0) { - return Param::createArgument(-index - 1); - } else { - const int localCount = _function->locals.size(); - if (index < localCount) - return Param::createLocal(index); - else - return Param::createTemp(index - localCount); - } - } else { - Q_UNIMPLEMENTED(); - return Param(); - } - } - - Instr::Param getResultParam(IR::Temp *result) - { - if (result) - return getParam(result); - else - return Instr::Param::createTemp(scratchTempIndex()); - } - - void simpleMove(IR::Move *); - void prepareCallArgs(IR::ExprList *, quint32 &, quint32 &); - - int outgoingArgumentTempStart() const { return _function->tempCount - _function->locals.size(); } - int scratchTempIndex() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; } - int frameSize() const { return scratchTempIndex() + 1; } - - template - inline ptrdiff_t addInstruction(const InstrData &data); - ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); - void patchJumpAddresses(); - uchar *squeezeCode() const; - - VM::String *identifier(const QString &s); - - IR::Function *_function; - VM::Function *_vmFunction; - IR::BasicBlock *_block; - - QHash > _patches; - QHash _addrs; - - uchar *_codeStart; - uchar *_codeNext; - uchar *_codeEnd; -}; - -class ISelFactory: public EvalISelFactory -{ -public: - virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module) - { return new InstructionSelection(engine, module); } -}; - -template -ptrdiff_t InstructionSelection::addInstruction(const InstrData &data) -{ - Instr genericInstr; - InstrMeta::setData(genericInstr, data); - return addInstructionHelper(static_cast(InstrT), genericInstr); -} - -} // namespace Moth -} // namespace QQmlJS - -#endif // QV4ISEL_MOTH_P_H diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp deleted file mode 100644 index a3ab1de46a..0000000000 --- a/moth/qv4vme_moth.cpp +++ /dev/null @@ -1,483 +0,0 @@ -#include "qv4vme_moth_p.h" -#include "qv4instr_moth_p.h" -#include "qmljs_value.h" -#include "debugging.h" - -#include - -#include - -#ifdef DO_TRACE_INSTR -# define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); -# define TRACE(n, str, ...) { char buf[4096]; snprintf(buf, 4096, str, __VA_ARGS__); fprintf(stderr, " %s : %s\n", #n, buf); } -#else -# define TRACE_INSTR(I) -# define TRACE(n, str, ...) -#endif // DO_TRACE_INSTR - -using namespace QQmlJS; -using namespace QQmlJS::Moth; - -class FunctionState: public Debugging::FunctionState -{ -public: - FunctionState(QQmlJS::VM::ExecutionContext *context, const uchar **code) - : Debugging::FunctionState(context) - , stack(0) - , stackSize(0) - , code(code) - {} - - virtual VM::Value *temp(unsigned idx) { return stack + idx; } - - void setStack(VM::Value *stack, unsigned stackSize) - { this->stack = stack; this->stackSize = stackSize; } - -private: - VM::Value *stack; - unsigned stackSize; - const uchar **code; -}; - -#define MOTH_BEGIN_INSTR_COMMON(I) { \ - const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ - code += InstrMeta<(int)Instr::I>::Size; \ - Q_UNUSED(instr); \ - TRACE_INSTR(I) - -#ifdef MOTH_THREADED_INTERPRETER - -# define MOTH_BEGIN_INSTR(I) op_##I: \ - MOTH_BEGIN_INSTR_COMMON(I) - -# define MOTH_NEXT_INSTR(I) { \ - genericInstr = reinterpret_cast(code); \ - goto *genericInstr->common.code; \ - } - -# define MOTH_END_INSTR(I) } \ - genericInstr = reinterpret_cast(code); \ - goto *genericInstr->common.code; \ - -#else - -# define MOTH_BEGIN_INSTR(I) \ - case Instr::I: \ - MOTH_BEGIN_INSTR_COMMON(I) - -# define MOTH_NEXT_INSTR(I) { \ - break; \ - } - -# define MOTH_END_INSTR(I) } \ - break; - -#endif - -static inline VM::Value *getValueRef(QQmlJS::VM::ExecutionContext *context, - VM::Value* stack, - const Instr::Param ¶m -#if !defined(QT_NO_DEBUG) - , unsigned stackSize -#endif - ) -{ -#ifdef DO_TRACE_INSTR - if (param.isValue()) { - fprintf(stderr, " value\n"); - } else if (param.isArgument()) { - fprintf(stderr, " argument %d\n", param.index); - } else if (param.isLocal()) { - fprintf(stderr, " local %d\n", param.index); - } else if (param.isTemp()) { - fprintf(stderr, " temp %d\n", param.index); - } else { - Q_ASSERT(!"INVALID"); - } -#endif // DO_TRACE_INSTR - - if (param.isValue()) { - return const_cast(¶m.value); - } else if (param.isArgument()) { - const unsigned arg = param.index; - Q_ASSERT(arg >= 0); - Q_ASSERT((unsigned) arg < context->argumentCount); - Q_ASSERT(context->arguments); - return context->arguments + arg; - } else if (param.isLocal()) { - const unsigned index = param.index; - Q_ASSERT(index >= 0); - Q_ASSERT(index < context->variableCount()); - Q_ASSERT(context->locals); - return context->locals + index; - } else if (param.isTemp()) { - Q_ASSERT(param.index < stackSize); - return stack + param.index; - } else { - Q_UNIMPLEMENTED(); - return 0; - } -} - -#if defined(QT_NO_DEBUG) -# define VALUE(param) *getValueRef(context, stack, param) -# define VALUEPTR(param) getValueRef(context, stack, param) -#else -# define VALUE(param) *getValueRef(context, stack, param, stackSize) -# define VALUEPTR(param) getValueRef(context, stack, param, stackSize) -#endif - -VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code -#ifdef MOTH_THREADED_INTERPRETER - , void ***storeJumpTable -#endif - ) -{ -#ifdef DO_TRACE_INSTR - qDebug("Starting VME with context=%p and code=%p", context, code); -#endif // DO_TRACE_INSTR - -#ifdef MOTH_THREADED_INTERPRETER - if (storeJumpTable) { -#define MOTH_INSTR_ADDR(I, FMT) &&op_##I, - static void *jumpTable[] = { - FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) - }; -#undef MOTH_INSTR_ADDR - *storeJumpTable = jumpTable; - return VM::Value::undefinedValue(); - } -#endif - - VM::Value *stack = 0; - unsigned stackSize = 0; - FunctionState state(context, &code); - -#ifdef MOTH_THREADED_INTERPRETER - const Instr *genericInstr = reinterpret_cast(code); - goto *genericInstr->common.code; -#else - for (;;) { - const Instr *genericInstr = reinterpret_cast(code); - switch (genericInstr->common.instructionType) { -#endif - - MOTH_BEGIN_INSTR(MoveTemp) - VALUE(instr.result) = VALUE(instr.source); - MOTH_END_INSTR(MoveTemp) - - MOTH_BEGIN_INSTR(LoadValue) -// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); - VALUE(instr.result) = VALUE(instr.value); - MOTH_END_INSTR(LoadValue) - - MOTH_BEGIN_INSTR(LoadClosure) - VALUE(instr.result) = __qmljs_init_closure(instr.value, context); - MOTH_END_INSTR(LoadClosure) - - MOTH_BEGIN_INSTR(LoadName) - TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); - VALUE(instr.result) = __qmljs_get_activation_property(context, instr.name); - MOTH_END_INSTR(LoadName) - - MOTH_BEGIN_INSTR(StoreName) - TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); - __qmljs_set_activation_property(context, instr.name, VALUE(instr.source)); - MOTH_END_INSTR(StoreName) - - MOTH_BEGIN_INSTR(LoadElement) - VALUE(instr.result) = __qmljs_get_element(context, VALUE(instr.base), VALUE(instr.index)); - MOTH_END_INSTR(LoadElement) - - MOTH_BEGIN_INSTR(StoreElement) - __qmljs_set_element(context, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); - MOTH_END_INSTR(StoreElement) - - MOTH_BEGIN_INSTR(LoadProperty) - VALUE(instr.result) = __qmljs_get_property(context, VALUE(instr.base), instr.name); - MOTH_END_INSTR(LoadProperty) - - MOTH_BEGIN_INSTR(StoreProperty) - __qmljs_set_property(context, VALUE(instr.base), instr.name, VALUE(instr.source)); - MOTH_END_INSTR(StoreProperty) - - MOTH_BEGIN_INSTR(Push) - TRACE(inline, "stack size: %u", instr.value); - stackSize = instr.value; - stack = static_cast(alloca(stackSize * sizeof(VM::Value))); - state.setStack(stack, stackSize); - MOTH_END_INSTR(Push) - - MOTH_BEGIN_INSTR(CallValue) -#ifdef DO_TRACE_INSTR - if (Debugging::Debugger *debugger = context->engine->debugger) { - if (VM::FunctionObject *o = (VALUE(instr.dest)).asFunctionObject()) { - if (Debugging::FunctionDebugInfo *info = debugger->debugInfo(o)) { - QString n = debugger->name(o); - std::cerr << "*** Call to \"" << (n.isNull() ? "" : qPrintable(n)) << "\" defined @" << info->startLine << ":" << info->startColumn << std::endl; - } - } - } -#endif // DO_TRACE_INSTR - Q_ASSERT(instr.args + instr.argc < stackSize); - VM::Value *args = stack + instr.args; - VALUE(instr.result) = __qmljs_call_value(context, VM::Value::undefinedValue(), VALUE(instr.dest), args, instr.argc); - MOTH_END_INSTR(CallValue) - - MOTH_BEGIN_INSTR(CallProperty) - TRACE(property name, "%s", qPrintable(instr.name->toQString())); - Q_ASSERT(instr.args + instr.argc < stackSize); - VM::Value *args = stack + instr.args; - VALUE(instr.result) = __qmljs_call_property(context, VALUE(instr.base), instr.name, args, instr.argc); - MOTH_END_INSTR(CallProperty) - - MOTH_BEGIN_INSTR(CallElement) - Q_ASSERT(instr.args + instr.argc < stackSize); - VM::Value *args = stack + instr.args; - VALUE(instr.result) = __qmljs_call_element(context, VALUE(instr.base), VALUE(instr.index), args, instr.argc); - MOTH_END_INSTR(CallProperty) - - MOTH_BEGIN_INSTR(CallActivationProperty) - Q_ASSERT(instr.args + instr.argc < stackSize); - VM::Value *args = stack + instr.args; - VALUE(instr.result) = __qmljs_call_activation_property(context, instr.name, args, instr.argc); - MOTH_END_INSTR(CallActivationProperty) - - MOTH_BEGIN_INSTR(CallBuiltinThrow) - __qmljs_builtin_throw(VALUE(instr.arg), context); - MOTH_END_INSTR(CallBuiltinThrow) - - MOTH_BEGIN_INSTR(CallBuiltinCreateExceptionHandler) - void *buf = __qmljs_create_exception_handler(context); - // The result is the only value we need from the instr to - // continue execution when an exception is caught. - VM::Value *result = getValueRef(context, stack, instr.result -#if !defined(QT_NO_DEBUG) - , stackSize -#endif - ); - int didThrow = setjmp(* static_cast(buf)); - // Two ways to come here: after a create, or after a throw. - if (didThrow) - // At this point, the interpreter state can be anything but - // valid, so first restore the state. - restoreState(context, result, code); - else - // Save the state and any variables we need when catching an - // exception, so we can restore the state at that point. - saveState(context, result, code); - *result = VM::Value::fromInt32(didThrow); - MOTH_END_INSTR(CallBuiltinCreateExceptionHandler) - - MOTH_BEGIN_INSTR(CallBuiltinDeleteExceptionHandler) - __qmljs_delete_exception_handler(context); - MOTH_END_INSTR(CallBuiltinDeleteExceptionHandler) - - MOTH_BEGIN_INSTR(CallBuiltinGetException) - VALUE(instr.result) = __qmljs_get_exception(context); - MOTH_END_INSTR(CallBuiltinGetException) - - MOTH_BEGIN_INSTR(CallBuiltinPushScope) - context = __qmljs_builtin_push_with_scope(VALUE(instr.arg), context); - MOTH_END_INSTR(CallBuiltinPushScope) - - MOTH_BEGIN_INSTR(CallBuiltinPopScope) - context = __qmljs_builtin_pop_scope(context); - MOTH_END_INSTR(CallBuiltinPopScope) - - MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) - VALUE(instr.result) = __qmljs_foreach_iterator_object(VALUE(instr.arg), context); - MOTH_END_INSTR(CallBuiltinForeachIteratorObject) - - MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) - VALUE(instr.result) = __qmljs_foreach_next_property_name(VALUE(instr.arg)); - MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) - - MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) - VALUE(instr.result) = __qmljs_delete_member(context, VALUE(instr.base), instr.member); - MOTH_END_INSTR(CallBuiltinDeleteMember) - - MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) - VALUE(instr.result) = __qmljs_delete_subscript(context, VALUE(instr.base), VALUE(instr.index)); - MOTH_END_INSTR(CallBuiltinDeleteSubscript) - - MOTH_BEGIN_INSTR(CallBuiltinDeleteName) - VALUE(instr.result) = __qmljs_delete_name(context, instr.name); - MOTH_END_INSTR(CallBuiltinDeleteName) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) - VALUE(instr.result) = __qmljs_builtin_typeof_member(VALUE(instr.base), instr.member, context); - MOTH_END_INSTR(CallBuiltinTypeofMember) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) - VALUE(instr.result) = __qmljs_builtin_typeof_element(VALUE(instr.base), VALUE(instr.index), context); - MOTH_END_INSTR(CallBuiltinTypeofSubscript) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofName) - VALUE(instr.result) = __qmljs_builtin_typeof_name(instr.name, context); - MOTH_END_INSTR(CallBuiltinTypeofName) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) - VALUE(instr.result) = __qmljs_builtin_typeof(VALUE(instr.value), context); - MOTH_END_INSTR(CallBuiltinTypeofValue) - - MOTH_BEGIN_INSTR(CallBuiltinPostIncMember) - VALUE(instr.result) = __qmljs_builtin_post_increment_member(VALUE(instr.base), instr.member, context); - MOTH_END_INSTR(CallBuiltinTypeofMember) - - MOTH_BEGIN_INSTR(CallBuiltinPostIncSubscript) - VALUE(instr.result) = __qmljs_builtin_post_increment_element(VALUE(instr.base), VALUE(instr.index), context); - MOTH_END_INSTR(CallBuiltinTypeofSubscript) - - MOTH_BEGIN_INSTR(CallBuiltinPostIncName) - VALUE(instr.result) = __qmljs_builtin_post_increment_name(instr.name, context); - MOTH_END_INSTR(CallBuiltinTypeofName) - - MOTH_BEGIN_INSTR(CallBuiltinPostIncValue) - VALUE(instr.result) = __qmljs_builtin_post_increment(VALUEPTR(instr.value), context); - MOTH_END_INSTR(CallBuiltinTypeofValue) - - MOTH_BEGIN_INSTR(CallBuiltinPostDecMember) - VALUE(instr.result) = __qmljs_builtin_post_decrement_member(VALUE(instr.base), instr.member, context); - MOTH_END_INSTR(CallBuiltinTypeofMember) - - MOTH_BEGIN_INSTR(CallBuiltinPostDecSubscript) - VALUE(instr.result) = __qmljs_builtin_post_decrement_element(VALUE(instr.base), VALUE(instr.index), context); - MOTH_END_INSTR(CallBuiltinTypeofSubscript) - - MOTH_BEGIN_INSTR(CallBuiltinPostDecName) - VALUE(instr.result) = __qmljs_builtin_post_decrement_name(instr.name, context); - MOTH_END_INSTR(CallBuiltinTypeofName) - - MOTH_BEGIN_INSTR(CallBuiltinPostDecValue) - VALUE(instr.result) = __qmljs_builtin_post_decrement(VALUEPTR(instr.value), context); - MOTH_END_INSTR(CallBuiltinTypeofValue) - - MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) - __qmljs_builtin_declare_var(context, instr.isDeletable, instr.varName); - MOTH_END_INSTR(CallBuiltinDeclareVar) - - MOTH_BEGIN_INSTR(CallBuiltinDefineGetterSetter) - __qmljs_builtin_define_getter_setter(VALUE(instr.object), instr.name, VALUE(instr.getter), VALUE(instr.setter), context); - MOTH_END_INSTR(CallBuiltinDefineGetterSetter) - - MOTH_BEGIN_INSTR(CallBuiltinDefineProperty) - __qmljs_builtin_define_property(VALUE(instr.object), instr.name, VALUE(instr.value), context); - MOTH_END_INSTR(CallBuiltinDefineProperty) - - MOTH_BEGIN_INSTR(CallBuiltinDefineArrayProperty) - __qmljs_builtin_define_array_property(VALUE(instr.object), instr.index, VALUE(instr.value), context); - MOTH_END_INSTR(CallBuiltinDefineArrayProperty) - - MOTH_BEGIN_INSTR(CreateValue) - Q_ASSERT(instr.args + instr.argc < stackSize); - VM::Value *args = stack + instr.args; - VALUE(instr.result) = __qmljs_construct_value(context, VALUE(instr.func), args, instr.argc); - MOTH_END_INSTR(CreateValue) - - MOTH_BEGIN_INSTR(CreateProperty) - Q_ASSERT(instr.args + instr.argc < stackSize); - VM::Value *args = stack + instr.args; - VALUE(instr.result) = __qmljs_construct_property(context, VALUE(instr.base), instr.name, args, instr.argc); - MOTH_END_INSTR(CreateProperty) - - MOTH_BEGIN_INSTR(CreateActivationProperty) - TRACE(inline, "property name = %s, args = %d, argc = %d", instr.name->toQString().toUtf8().constData(), instr.args, instr.argc); - Q_ASSERT(instr.args + instr.argc < stackSize); - VM::Value *args = stack + instr.args; - VALUE(instr.result) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); - MOTH_END_INSTR(CreateActivationProperty) - - MOTH_BEGIN_INSTR(Jump) - code = ((uchar *)&instr.offset) + instr.offset; - MOTH_END_INSTR(Jump) - - MOTH_BEGIN_INSTR(CJump) - uint cond = __qmljs_to_boolean(VALUE(instr.condition), context); - TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); - if (cond) - code = ((uchar *)&instr.offset) + instr.offset; - MOTH_END_INSTR(CJump) - - MOTH_BEGIN_INSTR(Unop) - VALUE(instr.result) = instr.alu(VALUE(instr.source), context); - MOTH_END_INSTR(Unop) - - MOTH_BEGIN_INSTR(Binop) - VALUE(instr.result) = instr.alu(VALUE(instr.lhs), VALUE(instr.rhs), context); - MOTH_END_INSTR(Binop) - - MOTH_BEGIN_INSTR(Ret) - VM::Value &result = VALUE(instr.result); -// TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); - return result; - MOTH_END_INSTR(Ret) - - MOTH_BEGIN_INSTR(LoadThis) - VALUE(instr.result) = __qmljs_get_thisObject(context); - MOTH_END_INSTR(LoadThis) - - MOTH_BEGIN_INSTR(InplaceElementOp) - instr.alu(VALUE(instr.base), - VALUE(instr.index), - VALUE(instr.source), - context); - MOTH_END_INSTR(InplaceElementOp) - - MOTH_BEGIN_INSTR(InplaceMemberOp) - instr.alu(VALUE(instr.source), - VALUE(instr.base), - instr.member, - context); - MOTH_END_INSTR(InplaceMemberOp) - - MOTH_BEGIN_INSTR(InplaceNameOp) - TRACE(name, "%s", instr.name->toQString().toUtf8().constData()); - instr.alu(VALUE(instr.source), - instr.name, - context); - MOTH_END_INSTR(InplaceNameOp) - -#ifdef MOTH_THREADED_INTERPRETER - // nothing to do -#else - default: - qFatal("QQmlJS::Moth::VME: Internal error - unknown instruction %d", genericInstr->common.instructionType); - break; - } - } -#endif - -} - -#ifdef MOTH_THREADED_INTERPRETER -void **VME::instructionJumpTable() -{ - static void **jumpTable = 0; - if (!jumpTable) { - VME dummy; - dummy(0, 0, &jumpTable); - } - return jumpTable; -} -#endif - -VM::Value VME::exec(VM::ExecutionContext *ctxt, const uchar *code) -{ - VME vme; - return vme(ctxt, code); -} - -void VME::restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code) -{ - VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); - target = handler.target; - code = handler.code; -} - -void VME::saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code) -{ - VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); - handler.target = target; - handler.code = code; -} diff --git a/moth/qv4vme_moth_p.h b/moth/qv4vme_moth_p.h deleted file mode 100644 index 2fd877f7b9..0000000000 --- a/moth/qv4vme_moth_p.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef QV4VME_MOTH_P_H -#define QV4VME_MOTH_P_H - -#include "qmljs_runtime.h" -#include "qv4instr_moth_p.h" - -namespace QQmlJS { -namespace VM { - struct Value; -} - -namespace Moth { - -class VME -{ -public: - static VM::Value exec(VM::ExecutionContext *, const uchar *); - - VM::Value operator()(QQmlJS::VM::ExecutionContext *, const uchar *code -#ifdef MOTH_THREADED_INTERPRETER - , void ***storeJumpTable = 0 -#endif - ); - -#ifdef MOTH_THREADED_INTERPRETER - static void **instructionJumpTable(); -#endif - -private: - static void restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code); - static void saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code); -}; - -} // namespace Moth -} // namespace QQmlJS - -#endif // QV4VME_MOTH_P_H diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp deleted file mode 100644 index 0b1b14b13e..0000000000 --- a/qmljs_engine.cpp +++ /dev/null @@ -1,493 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "qv4mm.h" -#include -#include -#include -#include - -namespace QQmlJS { -namespace VM { - -ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) - : memoryManager(new QQmlJS::VM::MemoryManager) - , iselFactory(factory) - , debugger(0) - , globalObject(Value::nullValue()) - , globalCode(0) - , exception(Value::nullValue()) -{ - MemoryManager::GCBlocker gcBlocker(memoryManager); - - memoryManager->setExecutionEngine(this); - - rootContext = newContext(); - rootContext->init(this); - current = rootContext; - - id_length = identifier(QStringLiteral("length")); - id_prototype = identifier(QStringLiteral("prototype")); - id_constructor = identifier(QStringLiteral("constructor")); - id_arguments = identifier(QStringLiteral("arguments")); - id_caller = identifier(QStringLiteral("caller")); - id_this = identifier(QStringLiteral("this")); - id___proto__ = identifier(QStringLiteral("__proto__")); - id_enumerable = identifier(QStringLiteral("enumerable")); - id_configurable = identifier(QStringLiteral("configurable")); - id_writable = identifier(QStringLiteral("writable")); - id_value = identifier(QStringLiteral("value")); - id_get = identifier(QStringLiteral("get")); - id_set = identifier(QStringLiteral("set")); - id_eval = identifier(QStringLiteral("eval")); - - objectPrototype = new (memoryManager) ObjectPrototype(); - stringPrototype = new (memoryManager) StringPrototype(rootContext); - numberPrototype = new (memoryManager) NumberPrototype(); - booleanPrototype = new (memoryManager) BooleanPrototype(); - arrayPrototype = new (memoryManager) ArrayPrototype(rootContext); - datePrototype = new (memoryManager) DatePrototype(); - functionPrototype = new (memoryManager) FunctionPrototype(rootContext); - regExpPrototype = new (memoryManager) RegExpPrototype(this); - errorPrototype = new (memoryManager) ErrorPrototype(this); - evalErrorPrototype = new (memoryManager) EvalErrorPrototype(rootContext); - rangeErrorPrototype = new (memoryManager) RangeErrorPrototype(rootContext); - referenceErrorPrototype = new (memoryManager) ReferenceErrorPrototype(rootContext); - syntaxErrorPrototype = new (memoryManager) SyntaxErrorPrototype(rootContext); - typeErrorPrototype = new (memoryManager) TypeErrorPrototype(rootContext); - uRIErrorPrototype = new (memoryManager) URIErrorPrototype(rootContext); - - stringPrototype->prototype = objectPrototype; - numberPrototype->prototype = objectPrototype; - booleanPrototype->prototype = objectPrototype; - arrayPrototype->prototype = objectPrototype; - datePrototype->prototype = objectPrototype; - functionPrototype->prototype = objectPrototype; - regExpPrototype->prototype = objectPrototype; - errorPrototype->prototype = objectPrototype; - evalErrorPrototype->prototype = objectPrototype; - rangeErrorPrototype->prototype = objectPrototype; - referenceErrorPrototype->prototype = objectPrototype; - syntaxErrorPrototype->prototype = objectPrototype; - typeErrorPrototype->prototype = objectPrototype; - uRIErrorPrototype->prototype = objectPrototype; - - objectCtor = Value::fromObject(new (memoryManager) ObjectCtor(rootContext)); - stringCtor = Value::fromObject(new (memoryManager) StringCtor(rootContext)); - numberCtor = Value::fromObject(new (memoryManager) NumberCtor(rootContext)); - booleanCtor = Value::fromObject(new (memoryManager) BooleanCtor(rootContext)); - arrayCtor = Value::fromObject(new (memoryManager) ArrayCtor(rootContext)); - functionCtor = Value::fromObject(new (memoryManager) FunctionCtor(rootContext)); - dateCtor = Value::fromObject(new (memoryManager) DateCtor(rootContext)); - regExpCtor = Value::fromObject(new (memoryManager) RegExpCtor(rootContext)); - errorCtor = Value::fromObject(new (memoryManager) ErrorCtor(rootContext)); - evalErrorCtor = Value::fromObject(new (memoryManager) EvalErrorCtor(rootContext)); - rangeErrorCtor = Value::fromObject(new (memoryManager) RangeErrorCtor(rootContext)); - referenceErrorCtor = Value::fromObject(new (memoryManager) ReferenceErrorCtor(rootContext)); - syntaxErrorCtor = Value::fromObject(new (memoryManager) SyntaxErrorCtor(rootContext)); - typeErrorCtor = Value::fromObject(new (memoryManager) TypeErrorCtor(rootContext)); - uRIErrorCtor = Value::fromObject(new (memoryManager) URIErrorCtor(rootContext)); - - objectCtor.objectValue()->prototype = functionPrototype; - stringCtor.objectValue()->prototype = functionPrototype; - numberCtor.objectValue()->prototype = functionPrototype; - booleanCtor.objectValue()->prototype = functionPrototype; - arrayCtor.objectValue()->prototype = functionPrototype; - functionCtor.objectValue()->prototype = functionPrototype; - dateCtor.objectValue()->prototype = functionPrototype; - regExpCtor.objectValue()->prototype = functionPrototype; - errorCtor.objectValue()->prototype = functionPrototype; - evalErrorCtor.objectValue()->prototype = functionPrototype; - rangeErrorCtor.objectValue()->prototype = functionPrototype; - referenceErrorCtor.objectValue()->prototype = functionPrototype; - syntaxErrorCtor.objectValue()->prototype = functionPrototype; - typeErrorCtor.objectValue()->prototype = functionPrototype; - uRIErrorCtor.objectValue()->prototype = functionPrototype; - - objectPrototype->init(rootContext, objectCtor); - stringPrototype->init(rootContext, stringCtor); - numberPrototype->init(rootContext, numberCtor); - booleanPrototype->init(rootContext, booleanCtor); - arrayPrototype->init(rootContext, arrayCtor); - datePrototype->init(rootContext, dateCtor); - functionPrototype->init(rootContext, functionCtor); - regExpPrototype->init(rootContext, regExpCtor); - errorPrototype->init(rootContext, errorCtor); - evalErrorPrototype->init(rootContext, evalErrorCtor); - rangeErrorPrototype->init(rootContext, rangeErrorCtor); - referenceErrorPrototype->init(rootContext, referenceErrorCtor); - syntaxErrorPrototype->init(rootContext, syntaxErrorCtor); - typeErrorPrototype->init(rootContext, typeErrorCtor); - uRIErrorPrototype->init(rootContext, uRIErrorCtor); - - // - // set up the global object - // - VM::Object *glo = newObject(/*rootContext*/); - globalObject = Value::fromObject(glo); - rootContext->activation = glo; - rootContext->thisObject = Value::fromObject(glo); - - glo->defineDefaultProperty(rootContext, QStringLiteral("Object"), objectCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("String"), stringCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("Number"), numberCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("Boolean"), booleanCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("Array"), arrayCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("Function"), functionCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("Date"), dateCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("RegExp"), regExpCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("Error"), errorCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("EvalError"), evalErrorCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("RangeError"), rangeErrorCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("ReferenceError"), referenceErrorCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("SyntaxError"), syntaxErrorCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("TypeError"), typeErrorCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("URIError"), uRIErrorCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("Math"), Value::fromObject(newMathObject(rootContext))); - glo->defineDefaultProperty(rootContext, QStringLiteral("JSON"), Value::fromObject(new (memoryManager) JsonObject(rootContext))); - - glo->defineReadonlyProperty(this, QStringLiteral("undefined"), Value::undefinedValue()); - glo->defineReadonlyProperty(this, QStringLiteral("NaN"), Value::fromDouble(nan(""))); - glo->defineReadonlyProperty(this, QStringLiteral("Infinity"), Value::fromDouble(INFINITY)); - - evalFunction = new (memoryManager) EvalFunction(rootContext); - glo->defineDefaultProperty(rootContext, QStringLiteral("eval"), Value::fromObject(evalFunction)); - - glo->defineDefaultProperty(rootContext, QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); - glo->defineDefaultProperty(rootContext, QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); - glo->defineDefaultProperty(rootContext, QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1); - glo->defineDefaultProperty(rootContext, QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1); - glo->defineDefaultProperty(rootContext, QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1); - glo->defineDefaultProperty(rootContext, QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1); - glo->defineDefaultProperty(rootContext, QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1); - glo->defineDefaultProperty(rootContext, QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1); - glo->defineDefaultProperty(rootContext, QStringLiteral("escape"), GlobalFunctions::method_escape, 1); - glo->defineDefaultProperty(rootContext, QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1); -} - -ExecutionEngine::~ExecutionEngine() -{ - delete globalObject.asObject(); - rootContext->destroy(); - delete rootContext; - qDeleteAll(functions); - delete memoryManager; -} - -ExecutionContext *ExecutionEngine::newContext() -{ - return new ExecutionContext(); -} - -String *ExecutionEngine::identifier(const QString &s) -{ - return new (memoryManager) String(s); -} - -Function *ExecutionEngine::newFunction(const QString &name) -{ - VM::Function *f = new VM::Function(identifier(name)); - functions.append(f); - return f; -} - -FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) -{ - BuiltinFunctionOld *f = new (memoryManager) BuiltinFunctionOld(scope, name, code); - return f; -} - -FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)) -{ - BuiltinFunction *f = new (memoryManager) BuiltinFunction(scope, name, code); - return f; -} - -FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, VM::Function *function) -{ - assert(function); - - ScriptFunction *f = new (memoryManager) ScriptFunction(scope, function); - return f; -} - -BoundFunction *ExecutionEngine::newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) -{ - assert(target); - - BoundFunction *f = new (memoryManager) BoundFunction(scope, target, boundThis, boundArgs); - return f; -} - - -Object *ExecutionEngine::newObject() -{ - Object *object = new (memoryManager) Object(); - object->prototype = objectPrototype; - return object; -} - -FunctionObject *ExecutionEngine::newObjectCtor(ExecutionContext *ctx) -{ - return new (memoryManager) ObjectCtor(ctx); -} - -String *ExecutionEngine::newString(const QString &s) -{ - return new (memoryManager) String(s); -} - -Object *ExecutionEngine::newStringObject(ExecutionContext *ctx, const Value &value) -{ - StringObject *object = new (memoryManager) StringObject(ctx, value); - object->prototype = stringPrototype; - return object; -} - -FunctionObject *ExecutionEngine::newStringCtor(ExecutionContext *ctx) -{ - return new (memoryManager) StringCtor(ctx); -} - -Object *ExecutionEngine::newNumberObject(const Value &value) -{ - NumberObject *object = new (memoryManager) NumberObject(value); - object->prototype = numberPrototype; - return object; -} - -FunctionObject *ExecutionEngine::newNumberCtor(ExecutionContext *ctx) -{ - return new (memoryManager) NumberCtor(ctx); -} - -Object *ExecutionEngine::newBooleanObject(const Value &value) -{ - Object *object = new (memoryManager) BooleanObject(value); - object->prototype = booleanPrototype; - return object; -} - -FunctionObject *ExecutionEngine::newBooleanCtor(ExecutionContext *ctx) -{ - return new (memoryManager) BooleanCtor(ctx); -} - -Object *ExecutionEngine::newFunctionObject(ExecutionContext *ctx) -{ - Object *object = new (memoryManager) FunctionObject(ctx); - object->prototype = functionPrototype; - return object; -} - -ArrayObject *ExecutionEngine::newArrayObject(ExecutionContext *ctx) -{ - ArrayObject *object = new (memoryManager) ArrayObject(ctx); - object->prototype = arrayPrototype; - return object; -} - -ArrayObject *ExecutionEngine::newArrayObject(ExecutionContext *ctx, const Array &value) -{ - ArrayObject *object = new (memoryManager) ArrayObject(ctx, value); - object->prototype = arrayPrototype; - return object; -} - -FunctionObject *ExecutionEngine::newArrayCtor(ExecutionContext *ctx) -{ - return new (memoryManager) ArrayCtor(ctx); -} - -Object *ExecutionEngine::newDateObject(const Value &value) -{ - Object *object = new (memoryManager) DateObject(value); - object->prototype = datePrototype; - return object; -} - -FunctionObject *ExecutionEngine::newDateCtor(ExecutionContext *ctx) -{ - return new (memoryManager) DateCtor(ctx); -} - -RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) -{ - bool global = (flags & IR::RegExp::RegExp_Global); - bool ignoreCase = false; - bool multiline = false; - if (flags & IR::RegExp::RegExp_IgnoreCase) - ignoreCase = true; - if (flags & IR::RegExp::RegExp_Multiline) - multiline = true; - - return newRegExpObject(RegExp::create(this, pattern, ignoreCase, multiline), global); -} - -RegExpObject *ExecutionEngine::newRegExpObject(PassRefPtr re, bool global) -{ - RegExpObject *object = new (memoryManager) RegExpObject(this, re, global); - object->prototype = regExpPrototype; - return object; -} - -FunctionObject *ExecutionEngine::newRegExpCtor(ExecutionContext *ctx) -{ - return new (memoryManager) RegExpCtor(ctx); -} - -Object *ExecutionEngine::newErrorObject(const Value &value) -{ - ErrorObject *object = new (memoryManager) ErrorObject(this, value); - object->prototype = errorPrototype; - return object; -} - -Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) -{ - return new (memoryManager) SyntaxErrorObject(ctx, message); -} - -Object *ExecutionEngine::newReferenceErrorObject(ExecutionContext *ctx, const QString &message) -{ - return new (memoryManager) ReferenceErrorObject(ctx, message); -} - -Object *ExecutionEngine::newTypeErrorObject(ExecutionContext *ctx, const QString &message) -{ - return new (memoryManager) TypeErrorObject(ctx, message); -} - -Object *ExecutionEngine::newRangeErrorObject(ExecutionContext *ctx, const QString &message) -{ - return new (memoryManager) RangeErrorObject(ctx, message); -} - -Object *ExecutionEngine::newURIErrorObject(ExecutionContext *ctx, Value message) -{ - return new (memoryManager) URIErrorObject(ctx, message); -} - -Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) -{ - MathObject *object = new (memoryManager) MathObject(ctx); - object->prototype = objectPrototype; - return object; -} - -Object *ExecutionEngine::newActivationObject() -{ - return new (memoryManager) Object(); -} - -Object *ExecutionEngine::newForEachIteratorObject(ExecutionContext *ctx, Object *o) -{ - return new (memoryManager) ForEachIteratorObject(ctx, o); -} - -void ExecutionEngine::requireArgumentsAccessors(int n) -{ - if (n <= argumentsAccessors.size()) - return; - - uint oldSize = argumentsAccessors.size(); - argumentsAccessors.resize(n); - for (int i = oldSize; i < n; ++i) { - FunctionObject *get = new (memoryManager) ArgumentsGetterFunction(rootContext, i); - get->prototype = functionPrototype; - FunctionObject *set = new (memoryManager) ArgumentsSetterFunction(rootContext, i); - set->prototype = functionPrototype; - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(get, set); - pd.configurable = PropertyDescriptor::Enabled; - pd.enumberable = PropertyDescriptor::Enabled; - argumentsAccessors[i] = pd; - } -} - -void ExecutionEngine::markObjects() -{ - globalObject.mark(); - - if (globalCode) - globalCode->mark(); - - exception.mark(); - - for (int i = 0; i < argumentsAccessors.size(); ++i) { - const PropertyDescriptor &pd = argumentsAccessors.at(i); - pd.get->mark(); - pd.set->mark(); - } - - - for (int i = 0; i < functions.size(); ++i) - functions.at(i)->mark(); - - id_length->mark(); - id_prototype->mark(); - id_constructor->mark(); - id_arguments->mark(); - id_caller->mark(); - id_this->mark(); - id___proto__->mark(); - id_enumerable->mark(); - id_configurable->mark(); - id_writable->mark(); - id_value->mark(); - id_get->mark(); - id_set->mark(); - id_eval->mark(); -} - -} // namespace VM -} // namespace QQmlJS diff --git a/qmljs_engine.h b/qmljs_engine.h deleted file mode 100644 index 01b1c511f6..0000000000 --- a/qmljs_engine.h +++ /dev/null @@ -1,234 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QMLJS_ENGINE_H -#define QMLJS_ENGINE_H - -#include -#include -#include -#include - -#include -#include - -namespace QQmlJS { - -namespace Debugging { -class Debugger; -} // namespace Debugging - -namespace VM { - -struct Value; -class Array; -struct Function; -struct Object; -struct BooleanObject; -struct NumberObject; -struct StringObject; -struct ArrayObject; -struct DateObject; -struct FunctionObject; -struct BoundFunction; -struct RegExpObject; -struct ErrorObject; -struct ArgumentsObject; -struct ExecutionContext; -struct ExecutionEngine; -class MemoryManager; - -struct ObjectPrototype; -struct StringPrototype; -struct NumberPrototype; -struct BooleanPrototype; -struct ArrayPrototype; -struct FunctionPrototype; -struct DatePrototype; -struct RegExpPrototype; -struct ErrorPrototype; -struct EvalErrorPrototype; -struct RangeErrorPrototype; -struct ReferenceErrorPrototype; -struct SyntaxErrorPrototype; -struct TypeErrorPrototype; -struct URIErrorPrototype; -struct EvalFunction; - -class RegExp; - -struct ExecutionEngine -{ - MemoryManager *memoryManager; - EvalISelFactory *iselFactory; - ExecutionContext *current; - ExecutionContext *rootContext; - WTF::BumpPointerAllocator bumperPointerAllocator; // Used by Yarr Regex engine. - - Debugging::Debugger *debugger; - - Value globalObject; - - VM::Function *globalCode; - - Value objectCtor; - Value stringCtor; - Value numberCtor; - Value booleanCtor; - Value arrayCtor; - Value functionCtor; - Value dateCtor; - Value regExpCtor; - Value errorCtor; - Value evalErrorCtor; - Value rangeErrorCtor; - Value referenceErrorCtor; - Value syntaxErrorCtor; - Value typeErrorCtor; - Value uRIErrorCtor; - - ObjectPrototype *objectPrototype; - StringPrototype *stringPrototype; - NumberPrototype *numberPrototype; - BooleanPrototype *booleanPrototype; - ArrayPrototype *arrayPrototype; - FunctionPrototype *functionPrototype; - DatePrototype *datePrototype; - RegExpPrototype *regExpPrototype; - ErrorPrototype *errorPrototype; - EvalErrorPrototype *evalErrorPrototype; - RangeErrorPrototype *rangeErrorPrototype; - ReferenceErrorPrototype *referenceErrorPrototype; - SyntaxErrorPrototype *syntaxErrorPrototype; - TypeErrorPrototype *typeErrorPrototype; - URIErrorPrototype *uRIErrorPrototype; - - EvalFunction *evalFunction; - - QVector argumentsAccessors; - - String *id_length; - String *id_prototype; - String *id_constructor; - String *id_arguments; - String *id_caller; - String *id_this; - String *id___proto__; - String *id_enumerable; - String *id_configurable; - String *id_writable; - String *id_value; - String *id_get; - String *id_set; - String *id_eval; - - struct ExceptionHandler { - ExecutionContext *context; - const uchar *code; // Interpreter state - Value *target; // Interpreter state - jmp_buf stackFrame; - }; - - QVector unwindStack; - Value exception; - - QVector functions; - - ExecutionEngine(EvalISelFactory *iselFactory); - ~ExecutionEngine(); - - ExecutionContext *newContext(); - - String *identifier(const QString &s); - - VM::Function *newFunction(const QString &name); - - FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); - FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)); - FunctionObject *newScriptFunction(ExecutionContext *scope, VM::Function *function); - BoundFunction *newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); - - Object *newObject(); - FunctionObject *newObjectCtor(ExecutionContext *ctx); - - String *newString(const QString &s); - Object *newStringObject(ExecutionContext *ctx, const Value &value); - FunctionObject *newStringCtor(ExecutionContext *ctx); - - Object *newNumberObject(const Value &value); - FunctionObject *newNumberCtor(ExecutionContext *ctx); - - Object *newBooleanObject(const Value &value); - FunctionObject *newBooleanCtor(ExecutionContext *ctx); - - Object *newFunctionObject(ExecutionContext *ctx); - - ArrayObject *newArrayObject(ExecutionContext *ctx); - ArrayObject *newArrayObject(ExecutionContext *ctx, const Array &value); - FunctionObject *newArrayCtor(ExecutionContext *ctx); - - Object *newDateObject(const Value &value); - FunctionObject *newDateCtor(ExecutionContext *ctx); - - RegExpObject *newRegExpObject(const QString &pattern, int flags); - RegExpObject *newRegExpObject(PassRefPtr re, bool global); - FunctionObject *newRegExpCtor(ExecutionContext *ctx); - - Object *newErrorObject(const Value &value); - Object *newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message); - Object *newReferenceErrorObject(ExecutionContext *ctx, const QString &message); - Object *newTypeErrorObject(ExecutionContext *ctx, const QString &message); - Object *newRangeErrorObject(ExecutionContext *ctx, const QString &message); - Object *newURIErrorObject(ExecutionContext *ctx, Value message); - - Object *newMathObject(ExecutionContext *ctx); - Object *newActivationObject(); - - Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o); - - void requireArgumentsAccessors(int n); - - void markObjects(); -}; - -} // namespace VM -} // namespace QQmlJS - -#endif diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp deleted file mode 100644 index 9042a435a0..0000000000 --- a/qmljs_environment.cpp +++ /dev/null @@ -1,564 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include "debugging.h" -#include -#include -#include -#include "qv4mm.h" -#include - -namespace QQmlJS { -namespace VM { - -DiagnosticMessage::DiagnosticMessage() - : offset(0) - , length(0) - , startLine(0) - , startColumn(0) - , type(0) - , next(0) -{} - -DiagnosticMessage::~DiagnosticMessage() -{ - delete next; -} - -String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const -{ - QString msg; - if (!fileName.isEmpty()) - msg = fileName + QLatin1Char(':'); - msg += QString::number(startLine) + QLatin1Char(':') + QString::number(startColumn) + QLatin1String(": "); - if (type == QQmlJS::VM::DiagnosticMessage::Error) - msg += QLatin1String("error"); - else - msg += QLatin1String("warning"); - msg += ": " + message; - - return ctx->engine->newString(msg); -} - -bool ExecutionContext::hasBinding(String *name) const -{ - if (!function) - return false; - - for (unsigned int i = 0; i < function->varCount; ++i) { - if (function->varList[i]->isEqualTo(name)) - return true; - } - for (unsigned int i = 0; i < function->formalParameterCount; ++i) { - if (function->formalParameterList[i]->isEqualTo(name)) - return true; - } - if (activation) - return activation->__hasProperty__(this, name); - return false; -} - -void ExecutionContext::createMutableBinding(String *name, bool deletable) -{ - if (!activation) - activation = engine->newActivationObject(); - - if (activation->__hasProperty__(this, name)) - return; - PropertyDescriptor desc; - desc.value = Value::undefinedValue(); - desc.type = PropertyDescriptor::Data; - desc.configurable = deletable ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; - desc.writable = PropertyDescriptor::Enabled; - desc.enumberable = PropertyDescriptor::Enabled; - activation->__defineOwnProperty__(this, name, &desc); -} - -bool ExecutionContext::setMutableBinding(ExecutionContext *scope, String *name, Value value) -{ - // ### throw if scope->strict is true, and it would change an immutable binding - if (function) { - for (unsigned int i = 0; i < function->varCount; ++i) - if (function->varList[i]->isEqualTo(name)) { - locals[i] = value; - return true; - } - for (int i = (int)function->formalParameterCount - 1; i >= 0; --i) - if (function->formalParameterList[i]->isEqualTo(name)) { - arguments[i] = value; - return true; - } - } - - if (activation && activation->__hasProperty__(scope, name)) { - activation->__put__(scope, name, value); - return true; - } - - return false; -} - -Value ExecutionContext::getBindingValue(ExecutionContext *scope, String *name, bool strict) const -{ - Q_UNUSED(strict); - assert(function); - - if (function) { - for (unsigned int i = 0; i < function->varCount; ++i) - if (function->varList[i]->isEqualTo(name)) - return locals[i]; - for (int i = (int)function->formalParameterCount - 1; i >= 0; --i) - if (function->formalParameterList[i]->isEqualTo(name)) - return arguments[i]; - } - - if (activation) { - bool hasProperty = false; - Value v = activation->__get__(scope, name, &hasProperty); - if (hasProperty) - return v; - } - assert(false); -} - -bool ExecutionContext::deleteBinding(ExecutionContext *scope, String *name) -{ - if (activation) - activation->__delete__(scope, name); - - if (scope->strictMode) - __qmljs_throw_type_error(scope); - return false; -} - -ExecutionContext *ExecutionContext::createWithScope(Object *with) -{ - ExecutionContext *withCtx = engine->newContext(); - withCtx->init(this, with); - engine->current = withCtx; - return withCtx; -} - -ExecutionContext *ExecutionContext::popScope() -{ - assert(engine->current == this); - assert(withObject != 0); - - engine->current = parent; - parent = 0; - return engine->current; -} - -String * const *ExecutionContext::formals() const -{ - return function ? function->formalParameterList : 0; -} - -unsigned int ExecutionContext::formalCount() const -{ - return function ? function->formalParameterCount : 0; -} - -String * const *ExecutionContext::variables() const -{ - return function ? function->varList : 0; -} - -unsigned int ExecutionContext::variableCount() const -{ - return function ? function->varCount : 0; -} - - -void ExecutionContext::init(ExecutionEngine *eng) -{ - engine = eng; - parent = 0; - outer = 0; - thisObject = eng->globalObject; - - function = 0; - arguments = 0; - argumentCount = 0; - locals = 0; - strictMode = false; - activation = 0; - withObject = 0; - - eng->exception = Value::undefinedValue(); -} - -void ExecutionContext::init(ExecutionContext *p, Object *with) -{ - engine = p->engine; - parent = p; - outer = p; - thisObject = p->thisObject; - - function = 0; - arguments = 0; - argumentCount = 0; - locals = 0; - strictMode = false; - activation = 0; - withObject = with; -} - -void ExecutionContext::destroy() -{ - delete[] arguments; - delete[] locals; -} - -bool ExecutionContext::deleteProperty(String *name) -{ - bool hasWith = false; - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { - if (ctx->withObject) { - hasWith = true; - if (ctx->withObject->__hasProperty__(this, name)) - return ctx->withObject->__delete__(this, name); - } else { - if (ctx->activation && ctx->activation->__hasProperty__(this, name)) - return ctx->activation->__delete__(this, name); - } - if (FunctionObject *f = ctx->function) { - if (f->needsActivation || hasWith) { - for (unsigned int i = 0; i < f->varCount; ++i) - if (f->varList[i]->isEqualTo(name)) - return false; - for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) - if (f->formalParameterList[i]->isEqualTo(name)) - return false; - } - } - } - if (strictMode) - throwSyntaxError(0); - return true; -} - -void ExecutionContext::mark() -{ - thisObject.mark(); - if (function) - function->mark(); - for (unsigned arg = 0, lastArg = formalCount(); arg < lastArg; ++arg) - arguments[arg].mark(); - for (unsigned local = 0, lastLocal = variableCount(); local < lastLocal; ++local) - locals[local].mark(); - if (activation) - activation->mark(); - if (withObject) - withObject->mark(); -} - -void ExecutionContext::setProperty(String *name, Value value) -{ -// qDebug() << "=== SetProperty" << value.toString(this)->toQString(); - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { - if (Object *w = ctx->withObject) { -// qDebug() << ctx << "hasWith"; - if (w->__hasProperty__(ctx, name)) { -// qDebug() << " withHasProp"; - w->__put__(ctx, name, value); - return; - } - } else { -// qDebug() << ctx << "setting mutable binding"; - if (ctx->setMutableBinding(this, name, value)) - return; - } - } - if (strictMode || name->isEqualTo(engine->id_this)) - throwReferenceError(Value::fromString(name)); - engine->globalObject.objectValue()->__put__(this, name, value); -} - -Value ExecutionContext::getProperty(String *name) -{ - if (name->isEqualTo(engine->id_this)) - return thisObject; - - bool hasWith = false; -// qDebug() << "=== getProperty" << name->toQString(); - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { - if (Object *w = ctx->withObject) { - hasWith = true; -// qDebug() << ctx << "hasWith"; - bool hasProperty = false; - Value v = w->__get__(ctx, name, &hasProperty); - if (hasProperty) { -// qDebug() << " withHasProp"; - return v; - } - continue; - } - - if (FunctionObject *f = ctx->function) { - if (f->needsActivation || hasWith) { - for (unsigned int i = 0; i < f->varCount; ++i) - if (f->varList[i]->isEqualTo(name)) - return ctx->locals[i]; - for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) - if (f->formalParameterList[i]->isEqualTo(name)) - return ctx->arguments[i]; - } - } - if (ctx->activation) { - bool hasProperty = false; - Value v = ctx->activation->__get__(ctx, name, &hasProperty); - if (hasProperty) - return v; - } - } - throwReferenceError(Value::fromString(name)); - return Value::undefinedValue(); -} - -Value ExecutionContext::getPropertyNoThrow(String *name) -{ - if (name->isEqualTo(engine->id_this)) - return thisObject; - - bool hasWith = false; - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { - if (Object *w = ctx->withObject) { - hasWith = true; - bool hasProperty = false; - Value v = w->__get__(ctx, name, &hasProperty); - if (hasProperty) - return v; - continue; - } - - if (FunctionObject *f = ctx->function) { - if (f->needsActivation || hasWith) { - for (unsigned int i = 0; i < f->varCount; ++i) - if (f->varList[i]->isEqualTo(name)) - return ctx->locals[i]; - for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) - if (f->formalParameterList[i]->isEqualTo(name)) - return ctx->arguments[i]; - } - } - if (ctx->activation) { - bool hasProperty = false; - Value v = ctx->activation->__get__(ctx, name, &hasProperty); - if (hasProperty) - return v; - } - } - return Value::undefinedValue(); -} - -Value ExecutionContext::getPropertyAndBase(String *name, Object **base) -{ - *base = 0; - - if (name->isEqualTo(engine->id_this)) - return thisObject; - - bool hasWith = false; - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { - if (Object *w = ctx->withObject) { - hasWith = true; - bool hasProperty = false; - Value v = w->__get__(ctx, name, &hasProperty); - if (hasProperty) { - *base = w; - return v; - } - continue; - } - - if (FunctionObject *f = ctx->function) { - if (f->needsActivation || hasWith) { - for (unsigned int i = 0; i < f->varCount; ++i) - if (f->varList[i]->isEqualTo(name)) - return ctx->locals[i]; - for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) - if (f->formalParameterList[i]->isEqualTo(name)) - return ctx->arguments[i]; - } - } - if (ctx->activation) { - bool hasProperty = false; - Value v = ctx->activation->__get__(ctx, name, &hasProperty); - if (hasProperty) - return v; - } - } - throwReferenceError(Value::fromString(name)); - return Value::undefinedValue(); -} - - - -void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) -{ - Value lhs = getProperty(name); - value = op(lhs, value, this); - setProperty(name, value); -} - -void ExecutionContext::throwError(Value value) -{ - __qmljs_builtin_throw(value, this); -} - -void ExecutionContext::throwError(const QString &message) -{ - Value v = Value::fromString(this, message); - throwError(Value::fromObject(engine->newErrorObject(v))); -} - -void ExecutionContext::throwSyntaxError(DiagnosticMessage *message) -{ - throwError(Value::fromObject(engine->newSyntaxErrorObject(this, message))); -} - -void ExecutionContext::throwTypeError() -{ - throwError(Value::fromObject(engine->newTypeErrorObject(this, QStringLiteral("Type error")))); -} - -void ExecutionContext::throwUnimplemented(const QString &message) -{ - Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message); - throwError(Value::fromObject(engine->newErrorObject(v))); -} - -void ExecutionContext::throwReferenceError(Value value) -{ - String *s = value.toString(this); - QString msg = s->toQString() + QStringLiteral(" is not defined"); - throwError(Value::fromObject(engine->newReferenceErrorObject(this, msg))); -} - -void ExecutionContext::throwRangeError(Value value) -{ - String *s = value.toString(this); - QString msg = s->toQString() + QStringLiteral(" out of range"); - throwError(Value::fromObject(engine->newRangeErrorObject(this, msg))); -} - -void ExecutionContext::throwURIError(Value msg) -{ - throwError(Value::fromObject(engine->newURIErrorObject(this, msg))); -} - -void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc) -{ - MemoryManager::GCBlocker blockGC(parent->engine->memoryManager); - - engine = parent->engine; - this->parent = parent; - outer = f->scope; - engine->current = this; - - function = f; - strictMode = f->strictMode; - - thisObject = that; - if (!strictMode && !thisObject.isObject()) { - if (thisObject.isUndefined() || thisObject.isNull()) - thisObject = engine->globalObject; - else - thisObject = thisObject.toObject(this); - } - - locals = function->varCount ? reinterpret_cast(this + 1) : 0; - if (locals) - std::fill(locals, locals + function->varCount, Value::undefinedValue()); - - arguments = args; - argumentCount = argc; - if (function->needsActivation || argc < function->formalParameterCount){ - argumentCount = qMax(argc, function->formalParameterCount); - arguments = reinterpret_cast(this + 1) + function->varCount; - if (argc) - std::copy(args, args + argc, arguments); - if (argc < function->formalParameterCount) - std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); - } - - - activation = 0; - withObject = 0; - - if (function->usesArgumentsObject) { - ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this, function->formalParameterCount, argc); - args->prototype = engine->objectPrototype; - Value arguments = Value::fromObject(args); - createMutableBinding(engine->id_arguments, false); - setMutableBinding(this, engine->id_arguments, arguments); - } - - if (engine->debugger) - engine->debugger->aboutToCall(f, this); -} - -void ExecutionContext::leaveCallContext() -{ - if (!function->needsActivation) - locals = 0; - engine->current = parent; - parent = 0; - - if (engine->debugger) - engine->debugger->justLeft(this); -} - -void ExecutionContext::wireUpPrototype() -{ - assert(thisObject.isObject()); - - Value proto = function->__get__(this, engine->id_prototype); - if (proto.isObject()) - thisObject.objectValue()->prototype = proto.objectValue(); - else - thisObject.objectValue()->prototype = engine->objectPrototype; -} - -} // namespace VM -} // namespace QQmlJS diff --git a/qmljs_environment.h b/qmljs_environment.h deleted file mode 100644 index c21cab55ac..0000000000 --- a/qmljs_environment.h +++ /dev/null @@ -1,149 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QMLJS_ENVIRONMENT_H -#define QMLJS_ENVIRONMENT_H - -#include - -namespace QQmlJS { -namespace VM { - -struct Value; -struct Object; -struct ExecutionEngine; -struct ExecutionContext; -struct DeclarativeEnvironment; - -struct DiagnosticMessage -{ - enum { Error, Warning }; - - QString fileName; - quint32 offset; - quint32 length; - quint32 startLine; - unsigned startColumn: 31; - unsigned type: 1; - QString message; - DiagnosticMessage *next; - - DiagnosticMessage(); - ~DiagnosticMessage(); - String *buildFullMessage(ExecutionContext *ctx) const; -}; - -struct ExecutionContext -{ - ExecutionEngine *engine; - ExecutionContext *parent; - ExecutionContext *outer; - Value thisObject; - - FunctionObject *function; - - Value *arguments; - unsigned int argumentCount; - Value *locals; - - String * const *formals() const; - unsigned int formalCount() const; - String * const *variables() const; - unsigned int variableCount() const; - - bool strictMode; - - Object *activation; - Object *withObject; - - void init(ExecutionEngine *e); - void init(ExecutionContext *p, Object *with); - void destroy(); - - bool hasBinding(String *name) const; - void createMutableBinding(String *name, bool deletable); - bool setMutableBinding(ExecutionContext *scope, String *name, Value value); - Value getBindingValue(ExecutionContext *scope, String *name, bool strict) const; - bool deleteBinding(ExecutionContext *ctx, String *name); - - ExecutionContext *createWithScope(Object *with); - ExecutionContext *popScope(); - - void initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc); - void leaveCallContext(); - - void wireUpPrototype(); - - void throwError(Value value); - void throwError(const QString &message); - void throwSyntaxError(DiagnosticMessage *message); - void throwTypeError(); - void throwReferenceError(Value value); - void throwRangeError(Value value); - void throwURIError(Value msg); - void throwUnimplemented(const QString &message); - - void setProperty(String *name, Value value); - Value getProperty(String *name); - Value getPropertyNoThrow(String *name); - Value getPropertyAndBase(String *name, Object **base); - void inplaceBitOp(Value value, String *name, BinOp op); - bool deleteProperty(String *name); - - inline Value argument(unsigned int index = 0) - { - if (index < argumentCount) - return arguments[index]; - return Value::undefinedValue(); - } - - void mark(); - -}; - -/* Function *f, int argc */ -#define requiredMemoryForExecutionContect(f, argc) \ - sizeof(ExecutionContext) + sizeof(Value) * (f->varCount + qMax((uint)argc, f->formalParameterCount)) - - -} // namespace VM -} // namespace QQmlJS - -#endif diff --git a/qmljs_math.h b/qmljs_math.h deleted file mode 100644 index 4645c5a324..0000000000 --- a/qmljs_math.h +++ /dev/null @@ -1,106 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QMLJS_MATH_H -#define QMLJS_MATH_H - -#ifndef QMLJS_LLVM_RUNTIME -# include -#endif // QMLJS_LLVM_RUNTIME -#include - -#if !defined(QMLJS_LLVM_RUNTIME) && 1 //CPU(X86_64) - -namespace QQmlJS { -namespace VM { - -static inline Value add_int32(int a, int b) -{ - quint8 overflow = 0; - int aa = a; - - asm ("addl %2, %1\n" - "seto %0" - : "=q" (overflow), "=r" (aa) - : "r" (b), "1" (aa) - : "cc" - ); - if (!overflow) - return Value::fromInt32(aa); - return Value::fromDouble((double)a + (double)b); -} - -static inline Value sub_int32(int a, int b) -{ - quint8 overflow = 0; - int aa = a; - - asm ("subl %2, %1\n" - "seto %0" - : "=q" (overflow), "=r" (aa) - : "r" (b), "1" (aa) - : "cc" - ); - if (!overflow) - return Value::fromInt32(aa); - return Value::fromDouble((double)a - (double)b); -} - -static inline Value mul_int32(int a, int b) -{ - quint8 overflow = 0; - int aa = a; - - asm ("imul %2, %1\n" - "setc %0" - : "=q" (overflow), "=r" (aa) - : "r" (b), "1" (aa) - : "cc" - ); - if (!overflow) - return Value::fromInt32(aa); - return Value::fromDouble((double)a * (double)b); -} - -} // namespace VM -} // namespace QQmlJS - -#endif // !defined(QMLJS_LLVM_RUNTIME) && 1 //CPU(X86_64) -#endif // QMLJS_MATH_H diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp deleted file mode 100644 index 9e50217554..0000000000 --- a/qmljs_runtime.cpp +++ /dev/null @@ -1,1160 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "debugging.h" -#include "qmljs_runtime.h" -#include "qv4object.h" -#include "qv4ir_p.h" -#include "qv4objectproto.h" -#include "qv4globalobject.h" -#include "qv4stringobject.h" -#include "private/qlocale_tools_p.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "3rdparty/double-conversion/double-conversion.h" - -namespace QQmlJS { -namespace VM { - -QString numberToString(double num, int radix = 10) -{ - if (std::isnan(num)) { - return QStringLiteral("NaN"); - } else if (qIsInf(num)) { - return QLatin1String(num < 0 ? "-Infinity" : "Infinity"); - } - - if (radix == 10) { - char str[100]; - double_conversion::StringBuilder builder(str, sizeof(str)); - double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToShortest(num, &builder); - return QString::fromLatin1(builder.Finalize()); - } - - QString str; - bool negative = false; - - if (num < 0) { - negative = true; - num = -num; - } - - double frac = num - ::floor(num); - num = Value::toInteger(num); - - do { - char c = (char)::fmod(num, radix); - c = (c < 10) ? (c + '0') : (c - 10 + 'a'); - str.prepend(QLatin1Char(c)); - num = ::floor(num / radix); - } while (num != 0); - - if (frac != 0) { - str.append(QLatin1Char('.')); - do { - frac = frac * radix; - char c = (char)::floor(frac); - c = (c < 10) ? (c + '0') : (c - 10 + 'a'); - str.append(QLatin1Char(c)); - frac = frac - ::floor(frac); - } while (frac != 0); - } - - if (negative) - str.prepend(QLatin1Char('-')); - - return str; -} - -extern "C" { - -Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx) -{ - assert(clos); - return Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); -} - -Function *__qmljs_register_function(ExecutionContext *ctx, String *name, - bool hasDirectEval, - bool usesArgumentsObject, bool isStrict, - bool hasNestedFunctions, - String **formals, unsigned formalCount, - String **locals, unsigned localCount) -{ - Function *f = ctx->engine->newFunction(name ? name->toQString() : QString()); - - f->hasDirectEval = hasDirectEval; - f->usesArgumentsObject = usesArgumentsObject; - f->isStrict = isStrict; - f->hasNestedFunctions = hasNestedFunctions; - - for (unsigned i = 0; i < formalCount; ++i) - if (formals[i]) - f->formals.append(formals[i]); - for (unsigned i = 0; i < localCount; ++i) - if (locals[i]) - f->locals.append(locals[i]); - - return f; -} - -Value __qmljs_string_literal_undefined(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->identifier(QStringLiteral("undefined"))); -} - -Value __qmljs_string_literal_null(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->identifier(QStringLiteral("null"))); -} - -Value __qmljs_string_literal_true(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->identifier(QStringLiteral("true"))); -} - -Value __qmljs_string_literal_false(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->identifier(QStringLiteral("false"))); -} - -Value __qmljs_string_literal_object(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->identifier(QStringLiteral("object"))); -} - -Value __qmljs_string_literal_boolean(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->identifier(QStringLiteral("boolean"))); -} - -Value __qmljs_string_literal_number(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->identifier(QStringLiteral("number"))); -} - -Value __qmljs_string_literal_string(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->identifier(QStringLiteral("string"))); -} - -Value __qmljs_string_literal_function(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->identifier(QStringLiteral("function"))); -} - -Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index) -{ - if (Object *o = base.asObject()) { - uint n = UINT_MAX; - if (index.isInteger()) - n = index.integerValue(); - else if (index.isDouble()) - n = index.doubleValue(); - if (n < UINT_MAX) - return Value::fromBoolean(o->__delete__(ctx, n)); - } - - String *name = index.toString(ctx); - return __qmljs_delete_member(ctx, base, name); -} - -Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name) -{ - Value obj = base.toObject(ctx); - return Value::fromBoolean(obj.objectValue()->__delete__(ctx, name)); -} - -Value __qmljs_delete_name(ExecutionContext *ctx, String *name) -{ - return Value::fromBoolean(ctx->deleteProperty(name)); -} - -Value __qmljs_add_helper(Value left, Value right, ExecutionContext *ctx) -{ - Value pleft = __qmljs_to_primitive(left, ctx, PREFERREDTYPE_HINT); - Value pright = __qmljs_to_primitive(right, ctx, PREFERREDTYPE_HINT); - if (pleft.isString() || pright.isString()) { - if (!pleft.isString()) - pleft = __qmljs_to_string(pleft, ctx); - if (!pright.isString()) - pright = __qmljs_to_string(pright, ctx); - String *string = __qmljs_string_concat(ctx, pleft.stringValue(), pright.stringValue()); - return Value::fromString(string); - } - double x = __qmljs_to_number(pleft, ctx); - double y = __qmljs_to_number(pright, ctx); - return Value::fromDouble(x + y); -} - -Value __qmljs_instanceof(Value left, Value right, ExecutionContext *ctx) -{ - if (FunctionObject *function = right.asFunctionObject()) { - bool r = function->hasInstance(ctx, left); - return Value::fromBoolean(r); - } - - return __qmljs_throw_type_error(ctx); -} - -Value __qmljs_in(Value left, Value right, ExecutionContext *ctx) -{ - if (right.isObject()) { - String *s = left.toString(ctx); - bool r = right.objectValue()->__hasProperty__(ctx, s); - return Value::fromBoolean(r); - } else { - return __qmljs_throw_type_error(ctx); - } -} - -void __qmljs_inplace_bit_and_name(Value value, String *name, ExecutionContext *ctx) -{ - ctx->inplaceBitOp(value, name, __qmljs_bit_and); -} - -void __qmljs_inplace_bit_or_name(Value value, String *name, ExecutionContext *ctx) -{ - ctx->inplaceBitOp(value, name, __qmljs_bit_or); -} - -void __qmljs_inplace_bit_xor_name(Value value, String *name, ExecutionContext *ctx) -{ - ctx->inplaceBitOp(value, name, __qmljs_bit_xor); -} - -void __qmljs_inplace_add_name(Value value, String *name, ExecutionContext *ctx) -{ - ctx->inplaceBitOp(value, name, __qmljs_add); -} - -void __qmljs_inplace_sub_name(Value value, String *name, ExecutionContext *ctx) -{ - ctx->inplaceBitOp(value, name, __qmljs_sub); -} - -void __qmljs_inplace_mul_name(Value value, String *name, ExecutionContext *ctx) -{ - ctx->inplaceBitOp(value, name, __qmljs_mul); -} - -void __qmljs_inplace_div_name(Value value, String *name, ExecutionContext *ctx) -{ - ctx->inplaceBitOp(value, name, __qmljs_div); -} - -void __qmljs_inplace_mod_name(Value value, String *name, ExecutionContext *ctx) -{ - ctx->inplaceBitOp(value, name, __qmljs_mod); -} - -void __qmljs_inplace_shl_name(Value value, String *name, ExecutionContext *ctx) -{ - ctx->inplaceBitOp(value, name, __qmljs_shl); -} - -void __qmljs_inplace_shr_name(Value value, String *name, ExecutionContext *ctx) -{ - ctx->inplaceBitOp(value, name, __qmljs_shr); -} - -void __qmljs_inplace_ushr_name(Value value, String *name, ExecutionContext *ctx) -{ - ctx->inplaceBitOp(value, name, __qmljs_ushr); -} - -void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, ExecutionContext *ctx) -{ - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_bit_and, ctx); -} - -void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, ExecutionContext *ctx) -{ - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_bit_or, ctx); -} - -void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, ExecutionContext *ctx) -{ - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_bit_xor, ctx); -} - -void __qmljs_inplace_add_element(Value base, Value index, Value value, ExecutionContext *ctx) -{ - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_add, ctx); -} - -void __qmljs_inplace_sub_element(Value base, Value index, Value value, ExecutionContext *ctx) -{ - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_sub, ctx); -} - -void __qmljs_inplace_mul_element(Value base, Value index, Value value, ExecutionContext *ctx) -{ - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_mul, ctx); -} - -void __qmljs_inplace_div_element(Value base, Value index, Value value, ExecutionContext *ctx) -{ - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_div, ctx); -} - -void __qmljs_inplace_mod_element(Value base, Value index, Value value, ExecutionContext *ctx) -{ - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_mod, ctx); -} - -void __qmljs_inplace_shl_element(Value base, Value index, Value value, ExecutionContext *ctx) -{ - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_shl, ctx); -} - -void __qmljs_inplace_shr_element(Value base, Value index, Value value, ExecutionContext *ctx) -{ - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_shr, ctx); -} - -void __qmljs_inplace_ushr_element(Value base, Value index, Value value, ExecutionContext *ctx) -{ - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_ushr, ctx); -} - -void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, ExecutionContext *ctx) -{ - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_bit_and, ctx); -} - -void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, ExecutionContext *ctx) -{ - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_bit_or, ctx); -} - -void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, ExecutionContext *ctx) -{ - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_bit_xor, ctx); -} - -void __qmljs_inplace_add_member(Value value, Value base, String *name, ExecutionContext *ctx) -{ - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_add, ctx); -} - -void __qmljs_inplace_sub_member(Value value, Value base, String *name, ExecutionContext *ctx) -{ - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_sub, ctx); -} - -void __qmljs_inplace_mul_member(Value value, Value base, String *name, ExecutionContext *ctx) -{ - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_mul, ctx); -} - -void __qmljs_inplace_div_member(Value value, Value base, String *name, ExecutionContext *ctx) -{ - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_div, ctx); -} - -void __qmljs_inplace_mod_member(Value value, Value base, String *name, ExecutionContext *ctx) -{ - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_mod, ctx); -} - -void __qmljs_inplace_shl_member(Value value, Value base, String *name, ExecutionContext *ctx) -{ - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_shl, ctx); -} - -void __qmljs_inplace_shr_member(Value value, Value base, String *name, ExecutionContext *ctx) -{ - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_shr, ctx); -} - -void __qmljs_inplace_ushr_member(Value value, Value base, String *name, ExecutionContext *ctx) -{ - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_ushr, ctx); -} - -String *__qmljs_string_from_utf8(ExecutionContext *ctx, const char *s) -{ - return ctx->engine->newString(QString::fromUtf8(s)); -} - -String *__qmljs_identifier_from_utf8(ExecutionContext *ctx, const char *s) -{ - return ctx->engine->identifier(QString::fromUtf8(s)); -} - -int __qmljs_string_length(ExecutionContext *, String *string) -{ - return string->toQString().length(); -} - -double __qmljs_string_to_number(const String *string) -{ - QString s = string->toQString(); - s = s.trimmed(); - if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) - return s.toLong(0, 16); - bool ok; - QByteArray ba = s.toLatin1(); - const char *begin = ba.constData(); - const char *end = 0; - double d = qstrtod(begin, &end, &ok); - if (end - begin != ba.size()) { - if (ba == "Infinity" || ba == "+Infinity") - d = INFINITY; - else if (ba == "-Infinity") - d = -INFINITY; - else - d = nan(""); - } - return d; -} - -Value __qmljs_string_from_number(ExecutionContext *ctx, double number) -{ - String *string = ctx->engine->newString(numberToString(number, 10)); - return Value::fromString(string); -} - -Bool __qmljs_string_compare(ExecutionContext *, String *left, String *right) -{ - return left->toQString() < right->toQString(); -} - -Bool __qmljs_string_equal(String *left, String *right) -{ - return left->isEqualTo(right); -} - -String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second) -{ - return ctx->engine->newString(first->toQString() + second->toQString()); -} - -Bool __qmljs_is_function(Value value) -{ - return value.objectValue()->asFunctionObject() != 0; -} - -Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int typeHint) -{ - if (typeHint == PREFERREDTYPE_HINT) { - if (object.asDateObject()) - typeHint = STRING_HINT; - else - typeHint = NUMBER_HINT; - } - - String *meth1 = ctx->engine->identifier("toString"); - String *meth2 = ctx->engine->identifier("valueOf"); - - if (typeHint == NUMBER_HINT) - qSwap(meth1, meth2); - - assert(object.isObject()); - Object *oo = object.objectValue(); - - Value conv = oo->__get__(ctx, meth1); - if (FunctionObject *o = conv.asFunctionObject()) { - Value r = o->call(ctx, object, 0, 0); - if (r.isPrimitive()) - return r; - } - - conv = oo->__get__(ctx, meth2); - if (FunctionObject *o = conv.asFunctionObject()) { - Value r = o->call(ctx, object, 0, 0); - if (r.isPrimitive()) - return r; - } - - ctx->throwTypeError(); - return Value::undefinedValue(); -} - -Value __qmljs_throw_type_error(ExecutionContext *ctx) -{ - ctx->throwTypeError(); - return Value::undefinedValue(); -} - -Value __qmljs_new_object(ExecutionContext *ctx) -{ - return Value::fromObject(ctx->engine->newObject()); -} - -Value __qmljs_new_boolean_object(ExecutionContext *ctx, bool boolean) -{ - Value value = Value::fromBoolean(boolean); - return Value::fromObject(ctx->engine->newBooleanObject(value)); -} - -Value __qmljs_new_number_object(ExecutionContext *ctx, double number) -{ - Value value = Value::fromDouble(number); - return Value::fromObject(ctx->engine->newNumberObject(value)); -} - -Value __qmljs_new_string_object(ExecutionContext *ctx, String *string) -{ - Value value = Value::fromString(string); - return Value::fromObject(ctx->engine->newStringObject(ctx, value)); -} - -void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Value value) -{ - if (! object.isObject()) - object = __qmljs_to_object(object, ctx); - object.objectValue()->__put__(ctx, name, value); -} - -Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) -{ - uint type = object.type(); - uint idx = index.asArrayIndex(); - - if (type != Value::Object_Type) { - if (type == Value::String_Type && idx < UINT_MAX) { - String *str = object.stringValue(); - if (idx >= (uint)str->toQString().length()) - return Value::undefinedValue(); - const QString s = str->toQString().mid(idx, 1); - return Value::fromString(ctx, s); - } - - object = __qmljs_to_object(object, ctx); - } - - Object *o = object.objectValue(); - - if (idx < UINT_MAX) { - const PropertyDescriptor *p = o->array.nonSparseAt(idx); - if (p && p->type == PropertyDescriptor::Data) - return p->value; - - return o->__get__(ctx, idx); - } - - String *name = index.toString(ctx); - return o->__get__(ctx, name); -} - -void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value value) -{ - if (!object.isObject()) - object = __qmljs_to_object(object, ctx); - - Object *o = object.objectValue(); - - uint idx = index.asArrayIndex(); - if (idx < UINT_MAX) { - PropertyDescriptor *p = o->array.nonSparseAtRef(idx); - if (p && p->type == PropertyDescriptor::Data && p->isWritable()) { - p->value = value; - return; - } - o->__put__(ctx, idx, value); - return; - } - - String *name = index.toString(ctx); - o->__put__(ctx, name, value); -} - -Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx) -{ - if (!in.isNull() && !in.isUndefined()) - in = __qmljs_to_object(in, ctx); - Object *it = ctx->engine->newForEachIteratorObject(ctx, in.asObject()); - return Value::fromObject(it); -} - -Value __qmljs_foreach_next_property_name(Value foreach_iterator) -{ - assert(foreach_iterator.isObject()); - - ForEachIteratorObject *it = static_cast(foreach_iterator.objectValue()); - assert(it->asForeachIteratorObject()); - - return it->nextPropertyName(); -} - - -void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, Value value) -{ - ctx->setProperty(name, value); -} - -Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name) -{ - if (object.isObject()) { - return object.objectValue()->__get__(ctx, name); - } else if (object.isString() && name->isEqualTo(ctx->engine->id_length)) { - return Value::fromInt32(object.stringValue()->toQString().length()); - } else { - object = __qmljs_to_object(object, ctx); - - if (object.isObject()) { - return object.objectValue()->__get__(ctx, name); - } else { - ctx->throwTypeError(); - return Value::undefinedValue(); - } - } -} - -Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name) -{ - return ctx->getProperty(name); -} - -Value __qmljs_get_thisObject(ExecutionContext *ctx) -{ - return ctx->thisObject; -} - -uint __qmljs_equal(Value x, Value y, ExecutionContext *ctx) -{ - if (x.type() == y.type()) { - switch (x.type()) { - case Value::Undefined_Type: - return true; - case Value::Null_Type: - return true; - case Value::Boolean_Type: - return x.booleanValue() == y.booleanValue(); - break; - case Value::Integer_Type: - return x.integerValue() == y.integerValue(); - case Value::String_Type: - return x.stringValue()->isEqualTo(y.stringValue()); - case Value::Object_Type: - return x.objectValue() == y.objectValue(); - default: // double - return x.doubleValue() == y.doubleValue(); - } - // unreachable - } else { - if (x.isNumber() && y.isNumber()) - return x.asDouble() == y.asDouble(); - if (x.isNull() && y.isUndefined()) { - return true; - } else if (x.isUndefined() && y.isNull()) { - return true; - } else if (x.isNumber() && y.isString()) { - Value ny = Value::fromDouble(__qmljs_to_number(y, ctx)); - return __qmljs_equal(x, ny, ctx); - } else if (x.isString() && y.isNumber()) { - Value nx = Value::fromDouble(__qmljs_to_number(x, ctx)); - return __qmljs_equal(nx, y, ctx); - } else if (x.isBoolean()) { - Value nx = Value::fromDouble((double) x.booleanValue()); - return __qmljs_equal(nx, y, ctx); - } else if (y.isBoolean()) { - Value ny = Value::fromDouble((double) y.booleanValue()); - return __qmljs_equal(x, ny, ctx); - } else if ((x.isNumber() || x.isString()) && y.isObject()) { - Value py = __qmljs_to_primitive(y, ctx, PREFERREDTYPE_HINT); - return __qmljs_equal(x, py, ctx); - } else if (x.isObject() && (y.isNumber() || y.isString())) { - Value px = __qmljs_to_primitive(x, ctx, PREFERREDTYPE_HINT); - return __qmljs_equal(px, y, ctx); - } - } - - return false; -} - -Value __qmljs_call_activation_property(ExecutionContext *context, String *name, Value *args, int argc) -{ - Object *base; - Value func = context->getPropertyAndBase(name, &base); - FunctionObject *o = func.asFunctionObject(); - if (!o) - context->throwTypeError(); - - Value thisObject = base ? Value::fromObject(base) : Value::undefinedValue(); - - if (o == context->engine->evalFunction && name->isEqualTo(context->engine->id_eval)) - return static_cast(o)->evalCall(context, thisObject, args, argc, true); - - return o->call(context, thisObject, args, argc); -} - -Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String *name, Value *args, int argc) -{ - Object *baseObject; - if (thisObject.isString()) { - baseObject = context->engine->stringPrototype; - } else { - if (!thisObject.isObject()) - thisObject = __qmljs_to_object(thisObject, context); - - assert(thisObject.isObject()); - baseObject = thisObject.objectValue(); - } - - Value func = baseObject->__get__(context, name); - FunctionObject *o = func.asFunctionObject(); - if (!o) - context->throwTypeError(); - - return o->call(context, thisObject, args, argc); -} - -Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc) -{ - Value thisObject = that; - if (!thisObject.isObject()) - thisObject = __qmljs_to_object(thisObject, context); - - assert(thisObject.isObject()); - Object *baseObject = thisObject.objectValue(); - - Value func = baseObject->__get__(context, index.toString(context)); - FunctionObject *o = func.asFunctionObject(); - if (!o) - context->throwTypeError(); - - return o->call(context, thisObject, args, argc); -} - -Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc) -{ - FunctionObject *o = func.asFunctionObject(); - if (!o) - context->throwTypeError(); - return o->call(context, thisObject, args, argc); -} - -Value __qmljs_construct_activation_property(ExecutionContext *context, String *name, Value *args, int argc) -{ - Value func = context->getProperty(name); - return __qmljs_construct_value(context, func, args, argc); -} - -Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc) -{ - if (FunctionObject *f = func.asFunctionObject()) - return f->construct(context, args, argc); - - context->throwTypeError(); - return Value::undefinedValue(); -} - -Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc) -{ - Value thisObject = base; - if (!thisObject.isObject()) - thisObject = __qmljs_to_object(base, context); - - Value func = thisObject.objectValue()->__get__(context, name); - if (FunctionObject *f = func.asFunctionObject()) - return f->construct(context, args, argc); - - context->throwTypeError(); - return Value::undefinedValue(); -} - -void __qmljs_throw(Value value, ExecutionContext *context) -{ - assert(!context->engine->unwindStack.isEmpty()); - - if (context->engine->debugger) - context->engine->debugger->aboutToThrow(&value); - - ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); - - // clean up call contexts - while (context != handler.context) { - ExecutionContext *parent = context->parent; - if (!context->withObject) - context->leaveCallContext(); - context = parent; - } - - context->engine->exception = value; - - longjmp(handler.stackFrame, 1); -} - -void *__qmljs_create_exception_handler(ExecutionContext *context) -{ - context->engine->exception = Value::undefinedValue(); - context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler()); - ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); - handler.context = context; - return handler.stackFrame; -} - -void __qmljs_delete_exception_handler(ExecutionContext *context) -{ - assert(!context->engine->unwindStack.isEmpty()); - - context->engine->unwindStack.pop_back(); -} - -Value __qmljs_get_exception(ExecutionContext *context) -{ - return context->engine->exception; -} - -Value __qmljs_builtin_typeof(Value value, ExecutionContext *ctx) -{ - switch (value.type()) { - case Value::Undefined_Type: - return __qmljs_string_literal_undefined(ctx); - break; - case Value::Null_Type: - return __qmljs_string_literal_object(ctx); - break; - case Value::Boolean_Type: - return __qmljs_string_literal_boolean(ctx); - break; - case Value::String_Type: - return __qmljs_string_literal_string(ctx); - break; - case Value::Object_Type: - if (__qmljs_is_callable(value, ctx)) - return __qmljs_string_literal_function(ctx); - else - return __qmljs_string_literal_object(ctx); // ### implementation-defined - break; - default: - return __qmljs_string_literal_number(ctx); - break; - } -} - -Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context) -{ - return __qmljs_builtin_typeof(context->getPropertyNoThrow(name), context); -} - -Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext *context) -{ - Value obj = base.toObject(context); - return __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context); -} - -Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context) -{ - String *name = index.toString(context); - Value obj = base.toObject(context); - return __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context); -} - -Value __qmljs_builtin_post_increment(Value *val, ExecutionContext *ctx) -{ - if (val->isInteger() && val->integerValue() < INT_MAX) { - Value retval = *val; - val->int_32 += 1; - return retval; - } - - double d = __qmljs_to_number(*val, ctx); - *val = Value::fromDouble(d + 1); - return Value::fromDouble(d); -} - -Value __qmljs_builtin_post_increment_name(String *name, ExecutionContext *context) -{ - Value v = context->getProperty(name); - Value retval; - - if (v.isInteger() && v.integerValue() < INT_MAX) { - retval = v; - v.int_32 += 1; - } else { - double d = __qmljs_to_number(v, context); - retval = Value::fromDouble(d); - v = Value::fromDouble(d + 1); - } - - context->setProperty(name, v); - return retval; -} - -Value __qmljs_builtin_post_increment_member(Value base, String *name, ExecutionContext *context) -{ - Object *o = __qmljs_to_object(base, context).objectValue(); - - Value v = o->__get__(context, name); - Value retval; - - if (v.isInteger() && v.integerValue() < INT_MAX) { - retval = v; - v.int_32 += 1; - } else { - double d = __qmljs_to_number(v, context); - retval = Value::fromDouble(d); - v = Value::fromDouble(d + 1); - } - - o->__put__(context, name, v); - return retval; -} - -Value __qmljs_builtin_post_increment_element(Value base, Value index, ExecutionContext *context) -{ - Object *o = __qmljs_to_object(base, context).objectValue(); - - uint idx = index.asArrayIndex(); - - if (idx == UINT_MAX) { - String *s = index.toString(context); - return __qmljs_builtin_post_increment_member(base, s, context); - } - - Value v = o->__get__(context, idx); - Value retval; - - if (v.isInteger() && v.integerValue() < INT_MAX) { - retval = v; - v.int_32 += 1; - } else { - double d = __qmljs_to_number(v, context); - retval = Value::fromDouble(d); - v = Value::fromDouble(d + 1); - } - - o->__put__(context, idx, v); - return retval; -} - -Value __qmljs_builtin_post_decrement(Value *val, ExecutionContext *ctx) -{ - if (val->isInteger() && val->integerValue() > INT_MIN) { - Value retval = *val; - val->int_32 -= 1; - return retval; - } - - double d = __qmljs_to_number(*val, ctx); - *val = Value::fromDouble(d - 1); - return Value::fromDouble(d); -} - -Value __qmljs_builtin_post_decrement_name(String *name, ExecutionContext *context) -{ - Value v = context->getProperty(name); - Value retval; - - if (v.isInteger() && v.integerValue() > INT_MIN) { - retval = v; - v.int_32 -= 1; - } else { - double d = __qmljs_to_number(v, context); - retval = Value::fromDouble(d); - v = Value::fromDouble(d - 1); - } - - context->setProperty(name, v); - return retval; -} - -Value __qmljs_builtin_post_decrement_member(Value base, String *name, ExecutionContext *context) -{ - Object *o = __qmljs_to_object(base, context).objectValue(); - - Value v = o->__get__(context, name); - Value retval; - - if (v.isInteger() && v.integerValue() > INT_MIN) { - retval = v; - v.int_32 -= 1; - } else { - double d = __qmljs_to_number(v, context); - retval = Value::fromDouble(d); - v = Value::fromDouble(d - 1); - } - - o->__put__(context, name, v); - return retval; -} - -Value __qmljs_builtin_post_decrement_element(Value base, Value index, ExecutionContext *context) -{ - Object *o = __qmljs_to_object(base, context).objectValue(); - - uint idx = index.asArrayIndex(); - - if (idx == UINT_MAX) { - String *s = index.toString(context); - return __qmljs_builtin_post_decrement_member(base, s, context); - } - - Value v = o->__get__(context, idx); - Value retval; - - if (v.isInteger() && v.integerValue() > INT_MIN) { - retval = v; - v.int_32 -= 1; - } else { - double d = __qmljs_to_number(v, context); - retval = Value::fromDouble(d); - v = Value::fromDouble(d - 1); - } - - o->__put__(context, idx, v); - return retval; -} - -void __qmljs_builtin_throw(Value val, ExecutionContext *context) -{ - __qmljs_throw(val, context); -} - -ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx) -{ - Object *obj = __qmljs_to_object(o, ctx).asObject(); - return ctx->createWithScope(obj); -} - -ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx) -{ - return ctx->popScope(); -} - -void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) -{ - ctx->createMutableBinding(name, deletable); -} - -void __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) -{ - Object *o = object.asObject(); - assert(o); - - PropertyDescriptor pd; - pd.value = val; - pd.type = PropertyDescriptor::Data; - pd.writable = PropertyDescriptor::Enabled; - pd.enumberable = PropertyDescriptor::Enabled; - pd.configurable = PropertyDescriptor::Enabled; - o->__defineOwnProperty__(ctx, name, &pd); -} - -void __qmljs_builtin_define_array_property(Value object, int index, Value val, ExecutionContext *ctx) -{ - Object *o = object.asObject(); - assert(o); - - PropertyDescriptor pd; - pd.value = val; - pd.type = PropertyDescriptor::Data; - pd.writable = PropertyDescriptor::Enabled; - pd.enumberable = PropertyDescriptor::Enabled; - pd.configurable = PropertyDescriptor::Enabled; - o->__defineOwnProperty__(ctx, index, &pd); -} - -void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx) -{ - Object *o = object.asObject(); - assert(o); - - PropertyDescriptor pd; - pd.get = getter.asFunctionObject(); - pd.set = setter.asFunctionObject(); - pd.type = PropertyDescriptor::Accessor; - pd.writable = PropertyDescriptor::Undefined; - pd.enumberable = PropertyDescriptor::Enabled; - pd.configurable = PropertyDescriptor::Enabled; - o->__defineOwnProperty__(ctx, name, &pd); -} - -Value __qmljs_increment(Value value, ExecutionContext *ctx) -{ - TRACE1(value); - - if (value.isInteger()) - return Value::fromInt32(value.integerValue() + 1); - - double d = __qmljs_to_number(value, ctx); - return Value::fromDouble(d + 1); -} - -Value __qmljs_decrement(Value value, ExecutionContext *ctx) -{ - TRACE1(value); - - if (value.isInteger()) - return Value::fromInt32(value.integerValue() - 1); - - double d = __qmljs_to_number(value, ctx); - return Value::fromDouble(d - 1); -} - -} // extern "C" - - -} // namespace VM -} // namespace QQmlJS diff --git a/qmljs_runtime.h b/qmljs_runtime.h deleted file mode 100644 index 830dc9a628..0000000000 --- a/qmljs_runtime.h +++ /dev/null @@ -1,861 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QMLJS_RUNTIME_H -#define QMLJS_RUNTIME_H - -#include -#include - -#include -#include -#include - -#include -#include - -#ifdef DO_TRACE_INSTR -# define TRACE1(x) fprintf(stderr, " %s\n", __FUNCTION__); -# define TRACE2(x, y) fprintf(stderr, " %s\n", __FUNCTION__); -#else -# define TRACE1(x) -# define TRACE2(x, y) -#endif // TRACE1 - -namespace QQmlJS { -namespace VM { - -enum TypeHint { - PREFERREDTYPE_HINT, - NUMBER_HINT, - STRING_HINT -}; - -struct Function; -struct Object; -struct String; -struct PropertyDescriptor; -struct ExecutionContext; -struct FunctionObject; -struct BooleanObject; -struct NumberObject; -struct StringObject; -struct DateObject; -struct RegExpObject; -struct ArrayObject; -struct ErrorObject; -struct ExecutionEngine; - -extern "C" { - -// context -Value __qmljs_call_activation_property(ExecutionContext *, String *name, Value *args, int argc); -Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, Value *args, int argc); -Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc); -Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc); - -Value __qmljs_construct_activation_property(ExecutionContext *, String *name, Value *args, int argc); -Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc); -Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc); - -Value __qmljs_builtin_typeof(Value val, ExecutionContext *ctx); -Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context); -Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext *context); -Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context); - -Value __qmljs_builtin_post_increment(Value *val, ExecutionContext *ctx); -Value __qmljs_builtin_post_increment_name(String *name, ExecutionContext *context); -Value __qmljs_builtin_post_increment_member(Value base, String *name, ExecutionContext *context); -Value __qmljs_builtin_post_increment_element(Value base, Value index, ExecutionContext *context); - -Value __qmljs_builtin_post_decrement(Value *val, ExecutionContext *ctx); -Value __qmljs_builtin_post_decrement_name(String *name, ExecutionContext *context); -Value __qmljs_builtin_post_decrement_member(Value base, String *name, ExecutionContext *context); -Value __qmljs_builtin_post_decrement_element(Value base, Value index, ExecutionContext *context); - -void __qmljs_builtin_throw(Value val, ExecutionContext *context); -void __qmljs_builtin_rethrow(ExecutionContext *context); -ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx); -ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx); -void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name); -void __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx); -void __qmljs_builtin_define_array_property(Value object, int index, Value val, ExecutionContext *ctx); -void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); - -// constructors -Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx); -VM::Function *__qmljs_register_function(ExecutionContext *ctx, String *name, - bool hasDirectEval, - bool usesArgumentsObject, bool isStrict, - bool hasNestedFunctions, - String **formals, unsigned formalCount, - String **locals, unsigned localCount); - -Bool __qmljs_is_function(Value value); - -// string literals -Value __qmljs_string_literal_undefined(ExecutionContext *ctx); -Value __qmljs_string_literal_null(ExecutionContext *ctx); -Value __qmljs_string_literal_true(ExecutionContext *ctx); -Value __qmljs_string_literal_false(ExecutionContext *ctx); -Value __qmljs_string_literal_object(ExecutionContext *ctx); -Value __qmljs_string_literal_boolean(ExecutionContext *ctx); -Value __qmljs_string_literal_number(ExecutionContext *ctx); -Value __qmljs_string_literal_string(ExecutionContext *ctx); -Value __qmljs_string_literal_function(ExecutionContext *ctx); - -// strings -String *__qmljs_string_from_utf8(ExecutionContext *ctx, const char *s); -int __qmljs_string_length(ExecutionContext *ctx, String *string); -double __qmljs_string_to_number(const String *string); -Value __qmljs_string_from_number(ExecutionContext *ctx, double number); -Bool __qmljs_string_compare(ExecutionContext *ctx, String *left, String *right); -Bool __qmljs_string_equal(String *left, String *right); -String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second); -String *__qmljs_identifier_from_utf8(ExecutionContext *ctx, const char *s); - -// objects -Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int typeHint); -Value __qmljs_throw_type_error(ExecutionContext *ctx); -Value __qmljs_new_object(ExecutionContext *ctx); -Value __qmljs_new_boolean_object(ExecutionContext *ctx, bool boolean); -Value __qmljs_new_number_object(ExecutionContext *ctx, double n); -Value __qmljs_new_string_object(ExecutionContext *ctx, String *string); -void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, Value value); -void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Value value); -Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name); -Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name); - -Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index); -void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value value); - -// For each -Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx); -Value __qmljs_foreach_next_property_name(Value foreach_iterator); - -// context -Value __qmljs_get_thisObject(ExecutionContext *ctx); - -// type conversion and testing -Value __qmljs_to_primitive(Value value, ExecutionContext *ctx, int typeHint); -Bool __qmljs_to_boolean(Value value, ExecutionContext *ctx); -double __qmljs_to_number(Value value, ExecutionContext *ctx); -double __qmljs_to_integer(Value value, ExecutionContext *ctx); -int __qmljs_to_int32(Value value, ExecutionContext *ctx); -unsigned short __qmljs_to_uint16(Value value, ExecutionContext *ctx); -Value __qmljs_to_string(Value value, ExecutionContext *ctx); -Value __qmljs_to_object(Value value, ExecutionContext *ctx); -//uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *value); -Bool __qmljs_is_callable(Value value, ExecutionContext *ctx); -Value __qmljs_default_value(Value value, ExecutionContext *ctx, int typeHint); - -Bool __qmljs_equal(Value x, Value y, ExecutionContext *ctx); -Bool __qmljs_strict_equal(Value x, Value y); - -// unary operators -Value __qmljs_uplus(Value value, ExecutionContext *ctx); -Value __qmljs_uminus(Value value, ExecutionContext *ctx); -Value __qmljs_compl(Value value, ExecutionContext *ctx); -Value __qmljs_not(Value value, ExecutionContext *ctx); -Value __qmljs_increment(Value value, ExecutionContext *ctx); -Value __qmljs_decrement(Value value, ExecutionContext *ctx); - -Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index); -Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name); -Value __qmljs_delete_name(ExecutionContext *ctx, String *name); - -void __qmljs_throw(Value value, ExecutionContext *context); -// actually returns a jmp_buf * -void *__qmljs_create_exception_handler(ExecutionContext *context); -void __qmljs_delete_exception_handler(ExecutionContext *context); -Value __qmljs_get_exception(ExecutionContext *context); - -// binary operators -typedef Value (*BinOp)(Value left, Value right, ExecutionContext *ctx); - -Value __qmljs_instanceof(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_in(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_bit_or(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_bit_xor(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_bit_and(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_add(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_sub(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_mul(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_div(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_mod(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_shl(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_shr(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_ushr(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_gt(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_lt(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_ge(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_le(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_eq(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_ne(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_se(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_sne(Value left, Value right, ExecutionContext *ctx); - -Value __qmljs_add_helper(Value left, Value right, ExecutionContext *ctx); - -void __qmljs_inplace_bit_and_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_bit_or_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_bit_xor_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_add_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_sub_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_mul_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_div_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_mod_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_shl_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_shr_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_ushr_name(Value value, String *name, ExecutionContext *ctx); - -void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_add_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_sub_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_mul_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_div_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_mod_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_shl_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_shr_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_ushr_element(Value base, Value index, Value value, ExecutionContext *ctx); - -void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_add_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_sub_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_mul_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_div_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_mod_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_shl_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_shr_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_ushr_member(Value value, Value base, String *name, ExecutionContext *ctx); - -Bool __qmljs_cmp_gt(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_lt(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_ge(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_le(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_eq(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_ne(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_se(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_sne(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_instanceof(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_in(Value left, Value right, ExecutionContext *ctx); - -// type conversion and testing -inline Value __qmljs_to_primitive(Value value, ExecutionContext *ctx, int typeHint) -{ - if (!value.isObject()) - return value; - return __qmljs_default_value(value, ctx, typeHint); -} - -inline Bool __qmljs_to_boolean(Value value, ExecutionContext *ctx) -{ - switch (value.type()) { - case Value::Undefined_Type: - case Value::Null_Type: - return false; - case Value::Boolean_Type: - case Value::Integer_Type: - return (bool)value.int_32; - case Value::String_Type: - return __qmljs_string_length(ctx, value.stringValue()) > 0; - case Value::Object_Type: - return true; - default: // double - if (! value.doubleValue() || std::isnan(value.doubleValue())) - return false; - return true; - } -} - -inline double __qmljs_to_number(Value value, ExecutionContext *ctx) -{ - switch (value.type()) { - case Value::Undefined_Type: - return nan(""); - case Value::Null_Type: - return 0; - case Value::Boolean_Type: - return (value.booleanValue() ? 1. : 0.); - case Value::Integer_Type: - return value.int_32; - case Value::String_Type: - return __qmljs_string_to_number(value.stringValue()); - case Value::Object_Type: { - Value prim = __qmljs_to_primitive(value, ctx, NUMBER_HINT); - return __qmljs_to_number(prim, ctx); - } - default: // double - return value.doubleValue(); - } -} - -inline double __qmljs_to_integer(Value value, ExecutionContext *ctx) -{ - if (value.isConvertibleToInt()) - return value.int_32; - - return Value::toInteger(__qmljs_to_number(value, ctx)); -} - -inline int __qmljs_to_int32(Value value, ExecutionContext *ctx) -{ - if (value.isConvertibleToInt()) - return value.int_32; - - return Value::toInt32(__qmljs_to_number(value, ctx)); -} - -inline unsigned short __qmljs_to_uint16(Value value, ExecutionContext *ctx) -{ - if (value.isConvertibleToInt()) - return (ushort)(uint)value.integerValue(); - - double number = __qmljs_to_number(value, ctx); - - double D16 = 65536.0; - if ((number >= 0 && number < D16)) - return static_cast(number); - - if (!std::isfinite(number)) - return +0; - - double d = ::floor(::fabs(number)); - if (std::signbit(number)) - d = -d; - - number = ::fmod(d , D16); - - if (number < 0) - number += D16; - - return (unsigned short)number; -} - -inline Value __qmljs_to_string(Value value, ExecutionContext *ctx) -{ - switch (value.type()) { - case Value::Undefined_Type: - return __qmljs_string_literal_undefined(ctx); - break; - case Value::Null_Type: - return __qmljs_string_literal_null(ctx); - break; - case Value::Boolean_Type: - if (value.booleanValue()) - return __qmljs_string_literal_true(ctx); - else - return __qmljs_string_literal_false(ctx); - break; - case Value::String_Type: - return value; - break; - case Value::Object_Type: { - Value prim = __qmljs_to_primitive(value, ctx, STRING_HINT); - if (prim.isPrimitive()) - return __qmljs_to_string(prim, ctx); - else - return __qmljs_throw_type_error(ctx); - break; - } - case Value::Integer_Type: - return __qmljs_string_from_number(ctx, value.int_32); - break; - default: // double - return __qmljs_string_from_number(ctx, value.doubleValue()); - break; - - } // switch -} - -inline Value __qmljs_to_object(Value value, ExecutionContext *ctx) -{ - switch (value.type()) { - case Value::Undefined_Type: - case Value::Null_Type: - return __qmljs_throw_type_error(ctx); - break; - case Value::Boolean_Type: - return __qmljs_new_boolean_object(ctx, value.booleanValue()); - break; - case Value::String_Type: - return __qmljs_new_string_object(ctx, value.stringValue()); - break; - case Value::Object_Type: - return value; - break; - case Value::Integer_Type: - return __qmljs_new_number_object(ctx, value.int_32); - break; - default: // double - return __qmljs_new_number_object(ctx, value.doubleValue()); - break; - } -} - -/* -inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *value) -{ - switch (value->type()) { - case Value::Undefined_Type: - case Value::Null_Type: - *result = __qmljs_throw_type_error(ctx); - return false; - default: - return true; - } -} -*/ - -inline Bool __qmljs_is_callable(Value value, ExecutionContext * /*ctx*/) -{ - if (value.isObject()) - return __qmljs_is_function(value); - else - return false; -} - -inline Value __qmljs_default_value(Value value, ExecutionContext *ctx, int typeHint) -{ - if (value.isObject()) - return __qmljs_object_default_value(ctx, value, typeHint); - return Value::undefinedValue(); -} - - -inline Value __qmljs_uplus(Value value, ExecutionContext *ctx) -{ - TRACE1(value); - - if (value.tryIntegerConversion()) - return value; - - double n = __qmljs_to_number(value, ctx); - return Value::fromDouble(n); -} - -inline Value __qmljs_uminus(Value value, ExecutionContext *ctx) -{ - TRACE1(value); - - // +0 != -0, so we need to convert to double when negating 0 - if (value.isInteger() && value.integerValue()) - return Value::fromInt32(-value.integerValue()); - double n = __qmljs_to_number(value, ctx); - return Value::fromDouble(-n); -} - -inline Value __qmljs_compl(Value value, ExecutionContext *ctx) -{ - TRACE1(value); - - int n; - if (value.isConvertibleToInt()) - n = value.int_32; - else - n = Value::toInt32(__qmljs_to_number(value, ctx)); - - return Value::fromInt32(~n); -} - -inline Value __qmljs_not(Value value, ExecutionContext *ctx) -{ - TRACE1(value); - - bool b = __qmljs_to_boolean(value, ctx); - return Value::fromBoolean(!b); -} - -// binary operators -inline Value __qmljs_bit_or(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - if (Value::integerCompatible(left, right)) - return Value::fromInt32(left.integerValue() | right.integerValue()); - - int lval = Value::toInt32(__qmljs_to_number(left, ctx)); - int rval = Value::toInt32(__qmljs_to_number(right, ctx)); - return Value::fromInt32(lval | rval); -} - -inline Value __qmljs_bit_xor(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - if (Value::integerCompatible(left, right)) - return Value::fromInt32(left.integerValue() ^ right.integerValue()); - - int lval = Value::toInt32(__qmljs_to_number(left, ctx)); - int rval = Value::toInt32(__qmljs_to_number(right, ctx)); - return Value::fromInt32(lval ^ rval); -} - -inline Value __qmljs_bit_and(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - if (Value::integerCompatible(left, right)) - return Value::fromInt32(left.integerValue() & right.integerValue()); - - int lval = Value::toInt32(__qmljs_to_number(left, ctx)); - int rval = Value::toInt32(__qmljs_to_number(right, ctx)); - return Value::fromInt32(lval & rval); -} - -inline Value __qmljs_add(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - -#ifndef QMLJS_LLVM_RUNTIME - if (Value::integerCompatible(left, right)) - return add_int32(left.integerValue(), right.integerValue()); -#endif // QMLJS_LLVM_RUNTIME - - if (Value::bothDouble(left, right)) - return Value::fromDouble(left.doubleValue() + right.doubleValue()); - - return __qmljs_add_helper(left, right, ctx); -} - -inline Value __qmljs_sub(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - -#ifndef QMLJS_LLVM_RUNTIME - if (Value::integerCompatible(left, right)) - return sub_int32(left.integerValue(), right.integerValue()); -#endif // QMLJS_LLVM_RUNTIME - - double lval = __qmljs_to_number(left, ctx); - double rval = __qmljs_to_number(right, ctx); - return Value::fromDouble(lval - rval); -} - -inline Value __qmljs_mul(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - -#ifndef QMLJS_LLVM_RUNTIME - if (Value::integerCompatible(left, right)) - return mul_int32(left.integerValue(), right.integerValue()); -#endif // QMLJS_LLVM_RUNTIME - - double lval = __qmljs_to_number(left, ctx); - double rval = __qmljs_to_number(right, ctx); - return Value::fromDouble(lval * rval); -} - -inline Value __qmljs_div(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - double lval = __qmljs_to_number(left, ctx); - double rval = __qmljs_to_number(right, ctx); - return Value::fromDouble(lval / rval); -} - -inline Value __qmljs_mod(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - if (Value::integerCompatible(left, right) && right.integerValue() != 0) - return Value::fromInt32(left.integerValue() % right.integerValue()); - - double lval = __qmljs_to_number(left, ctx); - double rval = __qmljs_to_number(right, ctx); - return Value::fromDouble(fmod(lval, rval)); -} - -inline Value __qmljs_shl(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - if (Value::integerCompatible(left, right)) - return Value::fromInt32(left.integerValue() << ((uint(right.integerValue()) & 0x1f))); - - int lval = Value::toInt32(__qmljs_to_number(left, ctx)); - unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; - return Value::fromInt32(lval << rval); -} - -inline Value __qmljs_shr(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - if (Value::integerCompatible(left, right)) - return Value::fromInt32(left.integerValue() >> ((uint(right.integerValue()) & 0x1f))); - - int lval = Value::toInt32(__qmljs_to_number(left, ctx)); - unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; - return Value::fromInt32(lval >> rval); -} - -inline Value __qmljs_ushr(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - uint result; - if (Value::integerCompatible(left, right)) { - result = uint(left.integerValue()) >> (uint(right.integerValue()) & 0x1f); - } else { - unsigned lval = Value::toUInt32(__qmljs_to_number(left, ctx)); - unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; - result = lval >> rval; - } - - if (result > INT_MAX) - return Value::fromDouble(result); - return Value::fromInt32(result); -} - -inline Value __qmljs_gt(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - return Value::fromBoolean(__qmljs_cmp_gt(left, right, ctx)); -} - -inline Value __qmljs_lt(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - return Value::fromBoolean(__qmljs_cmp_lt(left, right, ctx)); -} - -inline Value __qmljs_ge(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - return Value::fromBoolean(__qmljs_cmp_ge(left, right, ctx)); -} - -inline Value __qmljs_le(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - return Value::fromBoolean(__qmljs_cmp_le(left, right, ctx)); -} - -inline Value __qmljs_eq(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - return Value::fromBoolean(__qmljs_cmp_eq(left, right, ctx)); -} - -inline Value __qmljs_ne(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - return Value::fromBoolean(!__qmljs_cmp_eq(left, right, ctx)); -} - -inline Value __qmljs_se(Value left, Value right, ExecutionContext *) -{ - TRACE2(left, right); - - bool r = __qmljs_strict_equal(left, right); - return Value::fromBoolean(r); -} - -inline Value __qmljs_sne(Value left, Value right, ExecutionContext *) -{ - TRACE2(left, right); - - bool r = ! __qmljs_strict_equal(left, right); - return Value::fromBoolean(r); -} - -inline Bool __qmljs_cmp_gt(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); - right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); - - if (Value::integerCompatible(left, right)) - return left.integerValue() > right.integerValue(); - if (Value::bothDouble(left, right)) { - return left.doubleValue() > right.doubleValue(); - } else if (left.isString() && right.isString()) { - return __qmljs_string_compare(ctx, right.stringValue(), left.stringValue()); - } else { - double l = __qmljs_to_number(left, ctx); - double r = __qmljs_to_number(right, ctx); - return l > r; - } -} - -inline Bool __qmljs_cmp_lt(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); - right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); - - if (Value::integerCompatible(left, right)) - return left.integerValue() < right.integerValue(); - if (Value::bothDouble(left, right)) { - return left.doubleValue() < right.doubleValue(); - } else if (left.isString() && right.isString()) { - return __qmljs_string_compare(ctx, left.stringValue(), right.stringValue()); - } else { - double l = __qmljs_to_number(left, ctx); - double r = __qmljs_to_number(right, ctx); - return l < r; - } -} - -inline Bool __qmljs_cmp_ge(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); - right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); - - if (Value::integerCompatible(left, right)) - return left.integerValue() >= right.integerValue(); - if (Value::bothDouble(left, right)) { - return left.doubleValue() >= right.doubleValue(); - } else if (left.isString() && right.isString()) { - return !__qmljs_string_compare(ctx, left.stringValue(), right.stringValue()); - } else { - double l = __qmljs_to_number(left, ctx); - double r = __qmljs_to_number(right, ctx); - return l >= r; - } -} - -inline Bool __qmljs_cmp_le(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); - right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); - - if (Value::integerCompatible(left, right)) - return left.integerValue() <= right.integerValue(); - if (Value::bothDouble(left, right)) { - return left.doubleValue() <= right.doubleValue(); - } else if (left.isString() && right.isString()) { - return !__qmljs_string_compare(ctx, right.stringValue(), left.stringValue()); - } else { - double l = __qmljs_to_number(left, ctx); - double r = __qmljs_to_number(right, ctx); - return l <= r; - } -} - -inline Bool __qmljs_cmp_eq(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - // need to test for doubles first as NaN != NaN - if (Value::bothDouble(left, right)) - return left.doubleValue() == right.doubleValue(); - if (left.val == right.val) - return true; - if (left.isString() && right.isString()) - return __qmljs_string_equal(left.stringValue(), right.stringValue()); - - return __qmljs_equal(left, right, ctx); -} - -inline Bool __qmljs_cmp_ne(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - return !__qmljs_cmp_eq(left, right, ctx); -} - -inline Bool __qmljs_cmp_se(Value left, Value right, ExecutionContext *) -{ - TRACE2(left, right); - - return __qmljs_strict_equal(left, right); -} - -inline Bool __qmljs_cmp_sne(Value left, Value right, ExecutionContext *) -{ - TRACE2(left, right); - - return ! __qmljs_strict_equal(left, right); -} - -inline Bool __qmljs_cmp_instanceof(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - Value v = __qmljs_instanceof(left, right, ctx); - return v.booleanValue(); -} - -inline uint __qmljs_cmp_in(Value left, Value right, ExecutionContext *ctx) -{ - TRACE2(left, right); - - Value v = __qmljs_in(left, right, ctx); - return v.booleanValue(); -} - -inline Bool __qmljs_strict_equal(Value x, Value y) -{ - TRACE2(x, y); - - if (x.isDouble() || y.isDouble()) - return x.asDouble() == y.asDouble(); - if (x.rawValue() == y.rawValue()) - return true; - if (x.isString() && y.isString()) - return __qmljs_string_equal(x.stringValue(), y.stringValue()); - return false; -} - -} // extern "C" - -} // namespace VM -} // namespace QQmlJS - -#endif // QMLJS_RUNTIME_H diff --git a/qmljs_value.cpp b/qmljs_value.cpp deleted file mode 100644 index 8dba13a9c9..0000000000 --- a/qmljs_value.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include -#include -#include - -namespace QQmlJS { -namespace VM { - - -int Value::toUInt16(ExecutionContext *ctx) const -{ - return __qmljs_to_uint16(*this, ctx); -} - -Bool Value::toBoolean(ExecutionContext *ctx) const -{ - return __qmljs_to_boolean(*this, ctx); -} - -double Value::toInteger(ExecutionContext *ctx) const -{ - return __qmljs_to_integer(*this, ctx); -} - -double Value::toNumber(ExecutionContext *ctx) const -{ - return __qmljs_to_number(*this, ctx); -} - -String *Value::toString(ExecutionContext *ctx) const -{ - Value v = __qmljs_to_string(*this, ctx); - assert(v.isString()); - return v.stringValue(); -} - -Value Value::toObject(ExecutionContext *ctx) const -{ - return __qmljs_to_object(*this, ctx); -} - -bool Value::sameValue(Value other) const { - if (val == other.val) - return true; - if (isString() && other.isString()) - return stringValue()->isEqualTo(other.stringValue()); - if (isInteger() && int_32 == 0 && other.dbl == 0) - return true; - if (dbl == 0 && other.isInteger() && other.int_32 == 0) - return true; - return false; -} - -Value Value::fromString(ExecutionContext *ctx, const QString &s) -{ - return fromString(ctx->engine->newString(s)); -} - -int Value::toInt32(double number) -{ - const double D32 = 4294967296.0; - const double D31 = D32 / 2.0; - - if ((number >= -D31 && number < D31)) - return static_cast(number); - - - if (!std::isfinite(number)) - return 0; - - double d = ::floor(::fabs(number)); - if (std::signbit(number)) - d = -d; - - number = ::fmod(d , D32); - - if (number < -D31) - number += D32; - else if (number >= D31) - number -= D32; - - return int(number); -} - -unsigned int Value::toUInt32(double number) -{ - const double D32 = 4294967296.0; - if ((number >= 0 && number < D32)) - return static_cast(number); - - if (!std::isfinite(number)) - return +0; - - double d = ::floor(::fabs(number)); - if (std::signbit(number)) - d = -d; - - number = ::fmod(d , D32); - - if (number < 0) - number += D32; - - return unsigned(number); -} - -double Value::toInteger(double number) -{ - if (std::isnan(number)) - return +0; - else if (! number || std::isinf(number)) - return number; - const double v = floor(fabs(number)); - return std::signbit(number) ? -v : v; -} - -Value Value::property(ExecutionContext *ctx, String *name) const -{ - return isObject() ? objectValue()->__get__(ctx, name) : undefinedValue(); -} - - - -} // namespace VM -} // namespace QQmlJS diff --git a/qmljs_value.h b/qmljs_value.h deleted file mode 100644 index 401b61fc8f..0000000000 --- a/qmljs_value.h +++ /dev/null @@ -1,481 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QMLJS_VALUE_H -#define QMLJS_VALUE_H - -#include - -#include -#include -#include -#include -#include - -namespace QQmlJS { -namespace VM { - -struct String; -struct ExecutionContext; -struct ExecutionEngine; -struct Value; - -extern "C" { -double __qmljs_to_number(Value value, ExecutionContext *ctx); -} - -typedef uint Bool; - - -struct Value -{ - union { - quint64 val; - double dbl; - struct { -#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN - uint tag; -#endif - union { - uint uint_32; - int int_32; -#if CPU(X86_64) -#else - Managed *m; - Object *o; - String *s; -#endif - }; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - uint tag; -#endif - }; - }; - - enum Masks { - NotDouble_Mask = 0xfffc0000, - Type_Mask = 0xffff8000, - Immediate_Mask = NotDouble_Mask | 0x00008000, - Tag_Shift = 32 - }; - enum ValueType { - Undefined_Type = Immediate_Mask | 0x00000, - Null_Type = Immediate_Mask | 0x10000, - Boolean_Type = Immediate_Mask | 0x20000, - Integer_Type = Immediate_Mask | 0x30000, - Object_Type = NotDouble_Mask | 0x00000, - String_Type = NotDouble_Mask | 0x10000 - }; - - enum ImmediateFlags { - ConvertibleToInt = Immediate_Mask | 0x1 - }; - - enum ValueTypeInternal { - _Undefined_Type = Undefined_Type, - _Null_Type = Null_Type | ConvertibleToInt, - _Boolean_Type = Boolean_Type | ConvertibleToInt, - _Integer_Type = Integer_Type | ConvertibleToInt, - _Object_Type = Object_Type, - _String_Type = String_Type - - }; - - inline unsigned type() const { - return tag & Type_Mask; - } - - inline bool isUndefined() const { return tag == _Undefined_Type; } - inline bool isNull() const { return tag == _Null_Type; } - inline bool isBoolean() const { return tag == _Boolean_Type; } - inline bool isInteger() const { return tag == _Integer_Type; } - inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } - inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } -#if CPU(X86_64) - inline bool isString() const { return (tag & Type_Mask) == String_Type; } - inline bool isObject() const { return (tag & Type_Mask) == Object_Type; } -#else - inline bool isString() const { return tag == String_Type; } - inline bool isObject() const { return tag == Object_Type; } -#endif - inline bool isConvertibleToInt() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } - - Bool booleanValue() const { - return int_32; - } - double doubleValue() const { - return dbl; - } - void setDouble(double d) { - dbl = d; - } - double asDouble() const { - if (tag == _Integer_Type) - return int_32; - return dbl; - } - int integerValue() const { - return int_32; - } - -#if CPU(X86_64) - String *stringValue() const { - return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift)); - } - Object *objectValue() const { - return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift)); - } - Managed *managed() const { - return (Managed *)(val & ~(quint64(Type_Mask) << Tag_Shift)); - } -#else - String *stringValue() const { - return s; - } - Object *objectValue() const { - return o; - } - Managed *managed() const { - return m; - } -#endif - - quint64 rawValue() const { - return val; - } - - static Value undefinedValue(); - static Value nullValue(); - static Value fromBoolean(Bool b); - static Value fromDouble(double d); - static Value fromInt32(int i); - static Value fromUInt32(uint i); - static Value fromString(String *s); - static Value fromObject(Object *o); - -#ifndef QMLJS_LLVM_RUNTIME - static Value fromString(ExecutionContext *ctx, const QString &fromString); -#endif - - static double toInteger(double fromNumber); - static int toInt32(double value); - static unsigned int toUInt32(double value); - - int toUInt16(ExecutionContext *ctx) const; - int toInt32(ExecutionContext *ctx) const; - unsigned int toUInt32(ExecutionContext *ctx) const; - - Bool toBoolean(ExecutionContext *ctx) const; - double toInteger(ExecutionContext *ctx) const; - double toNumber(ExecutionContext *ctx) const; - String *toString(ExecutionContext *ctx) const; - Value toObject(ExecutionContext *ctx) const; - - inline bool isPrimitive() const { return !isObject(); } -#if CPU(X86_64) - inline bool integerCompatible() const { - const quint64 mask = quint64(ConvertibleToInt) << 32; - return (val & mask) == mask; - } - static inline bool integerCompatible(Value a, Value b) { - const quint64 mask = quint64(ConvertibleToInt) << 32; - return ((a.val & b.val) & mask) == mask; - } - static inline bool bothDouble(Value a, Value b) { - const quint64 mask = quint64(NotDouble_Mask) << 32; - return ((a.val | b.val) & mask) != mask; - } -#else - inline bool integerCompatible() const { - return (tag & ConvertibleToInt) == ConvertibleToInt; - } - static inline bool integerCompatible(Value a, Value b) { - return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; - } - static inline bool bothDouble(Value a, Value b) { - return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask; - } -#endif - inline bool tryIntegerConversion() { - bool b = isConvertibleToInt(); - if (b) - tag = _Integer_Type; - return b; - } - - Managed *asManaged() const; - Object *asObject() const; - FunctionObject *asFunctionObject() const; - BooleanObject *asBooleanObject() const; - NumberObject *asNumberObject() const; - StringObject *asStringObject() const; - DateObject *asDateObject() const; - RegExpObject *asRegExpObject() const; - ArrayObject *asArrayObject() const; - ErrorObject *asErrorObject() const; - uint asArrayIndex() const; - uint asArrayLength(ExecutionContext *ctx, bool *ok) const; - - Value property(ExecutionContext *ctx, String *name) const; - - // Section 9.12 - bool sameValue(Value other) const; - - void mark() const { - Managed *m = asManaged(); - if (m) - m->mark(); - } -}; - -inline Value Value::undefinedValue() -{ - Value v; -#if CPU(X86_64) - v.val = quint64(_Undefined_Type) << Tag_Shift; -#else - v.tag = _Undefined_Type; - v.int_32 = 0; -#endif - return v; -} - -inline Value Value::nullValue() -{ - Value v; -#if CPU(X86_64) - v.val = quint64(_Null_Type) << Tag_Shift; -#else - v.tag = _Null_Type; - v.int_32 = 0; -#endif - return v; -} - -inline Value Value::fromBoolean(Bool b) -{ - Value v; - v.tag = _Boolean_Type; - v.int_32 = (bool)b; - return v; -} - -inline Value Value::fromDouble(double d) -{ - Value v; - v.dbl = d; - return v; -} - -inline Value Value::fromInt32(int i) -{ - Value v; - v.tag = _Integer_Type; - v.int_32 = i; - return v; -} - -inline Value Value::fromUInt32(uint i) -{ - Value v; - if (i < INT_MAX) { - v.tag = _Integer_Type; - v.int_32 = (int)i; - } else { - v.dbl = i; - } - return v; -} - -inline Value Value::fromString(String *s) -{ - Value v; -#if CPU(X86_64) - v.val = (quint64)s; - v.val |= quint64(_String_Type) << Tag_Shift; -#else - v.tag = _String_Type; - v.s = s; -#endif - return v; -} - -inline Value Value::fromObject(Object *o) -{ - Value v; -#if CPU(X86_64) - v.val = (quint64)o; - v.val |= quint64(_Object_Type) << Tag_Shift; -#else - v.tag = _Object_Type; - v.o = o; -#endif - return v; -} - -inline int Value::toInt32(ExecutionContext *ctx) const -{ - if (isConvertibleToInt()) - return int_32; - double d; - if (isDouble()) - d = dbl; - else - d = __qmljs_to_number(*this, ctx); - - const double D32 = 4294967296.0; - const double D31 = D32 / 2.0; - - if ((d >= -D31 && d < D31)) - return static_cast(d); - - return Value::toInt32(__qmljs_to_number(*this, ctx)); -} - -inline unsigned int Value::toUInt32(ExecutionContext *ctx) const { - if (isConvertibleToInt()) - return (unsigned) int_32; - double d; - if (isDouble()) - d = dbl; - else - d = __qmljs_to_number(*this, ctx); - - const double D32 = 4294967296.0; - if (dbl >= 0 && dbl < D32) - return static_cast(dbl); - return toUInt32(d); -} - -inline uint Value::asArrayIndex() const -{ - if (isInteger() && int_32 >= 0) - return (uint)int_32; - if (!isDouble()) - return UINT_MAX; - uint idx = (uint)dbl; - if (idx != dbl) - return UINT_MAX; - return idx; -} - -inline uint Value::asArrayLength(ExecutionContext *ctx, bool *ok) const -{ - *ok = true; - if (isConvertibleToInt() && int_32 >= 0) - return (uint)int_32; - if (isDouble()) { - uint idx = (uint)dbl; - if ((double)idx != dbl) { - *ok = false; - return UINT_MAX; - } - return idx; - } - if (isString()) - return stringValue()->toUInt(ok); - - uint idx = toUInt32(ctx); - double d = toNumber(ctx); - if (d != idx) { - *ok = false; - return UINT_MAX; - } - return idx; -} - - -inline Managed *Value::asManaged() const -{ - if (isObject() || isString()) - return managed(); - return 0; -} - -inline Object *Value::asObject() const -{ - return isObject() ? objectValue() : 0; -} - -inline FunctionObject *Value::asFunctionObject() const -{ - return isObject() ? managed()->asFunctionObject() : 0; -} - -inline BooleanObject *Value::asBooleanObject() const -{ - return isObject() ? managed()->asBooleanObject() : 0; -} - -inline NumberObject *Value::asNumberObject() const -{ - return isObject() ? managed()->asNumberObject() : 0; -} - -inline StringObject *Value::asStringObject() const -{ - return isObject() ? managed()->asStringObject() : 0; -} - -inline DateObject *Value::asDateObject() const -{ - return isObject() ? managed()->asDateObject() : 0; -} - -inline RegExpObject *Value::asRegExpObject() const -{ - return isObject() ? managed()->asRegExpObject() : 0; -} - -inline ArrayObject *Value::asArrayObject() const -{ - return isObject() ? managed()->asArrayObject() : 0; -} - -inline ErrorObject *Value::asErrorObject() const -{ - return isObject() ? managed()->asErrorObject() : 0; -} - - -} // namespace VM -} // namespace QQmlJS - -#endif diff --git a/qv4_llvm_p.h b/qv4_llvm_p.h deleted file mode 100644 index c0f18e555c..0000000000 --- a/qv4_llvm_p.h +++ /dev/null @@ -1,52 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#ifndef QV4_LLVM_P_H -#define QV4_LLVM_P_H - -#include "qv4ir_p.h" - -#include - -namespace QQmlJS { - -// Note: keep this enum in sync with the command-line option! -enum LLVMOutputType { - LLVMOutputJit = -1, - LLVMOutputIR = 0, // .ll - LLVMOutputBitcode = 1, // .bc - LLVMOutputAssembler = 2, // .s - LLVMOutputObject = 3 // .o -}; - -int compileWithLLVM(IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*exec)(void *)); - -} // QQmlJS - -#endif // QV4_LLVM_P_H diff --git a/qv4argumentsobject.cpp b/qv4argumentsobject.cpp deleted file mode 100644 index f7327506e9..0000000000 --- a/qv4argumentsobject.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include - -namespace QQmlJS { -namespace VM { - - -ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount) - : context(context) -{ - type = Type_ArgumentsObject; - - defineDefaultProperty(context->engine->id_length, Value::fromInt32(actualParameterCount)); - if (context->strictMode) { - for (uint i = 0; i < context->argumentCount; ++i) - Object::__put__(context, QString::number(i), context->arguments[i]); - FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, __qmljs_throw_type_error); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); - pd.configurable = PropertyDescriptor::Disabled; - pd.enumberable = PropertyDescriptor::Disabled; - __defineOwnProperty__(context, QStringLiteral("callee"), &pd); - __defineOwnProperty__(context, QStringLiteral("caller"), &pd); - } else { - uint numAccessors = qMin(formalParameterCount, actualParameterCount); - context->engine->requireArgumentsAccessors(numAccessors); - for (uint i = 0; i < (uint)numAccessors; ++i) { - mappedArguments.append(context->argument(i)); - __defineOwnProperty__(context, i, &context->engine->argumentsAccessors.at(i)); - } - PropertyDescriptor pd; - pd.type = PropertyDescriptor::Data; - pd.writable = PropertyDescriptor::Enabled; - pd.configurable = PropertyDescriptor::Enabled; - pd.enumberable = PropertyDescriptor::Enabled; - for (uint i = numAccessors; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) { - pd.value = context->argument(i); - __defineOwnProperty__(context, i, &pd); - } - defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); - isNonStrictArgumentsObject = true; - } -} - -bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc) -{ - PropertyDescriptor *pd = array.at(index); - PropertyDescriptor map; - bool isMapped = false; - if (pd && index < (uint)mappedArguments.size()) - isMapped = pd->isAccessor() && pd->get == context->engine->argumentsAccessors.at(index).get; - - if (isMapped) { - map = *pd; - pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Enabled; - pd->configurable = PropertyDescriptor::Enabled; - pd->enumberable = PropertyDescriptor::Enabled; - pd->value = mappedArguments.at(index); - } - - isNonStrictArgumentsObject = false; - bool strict = ctx->strictMode; - ctx->strictMode = false; - bool result = Object::__defineOwnProperty__(ctx, index, desc); - ctx->strictMode = strict; - isNonStrictArgumentsObject = true; - - if (isMapped && desc->isData()) { - if (desc->type != PropertyDescriptor::Generic) { - Value arg = desc->value; - map.set->call(ctx, Value::fromObject(this), &arg, 1); - } - if (desc->writable != PropertyDescriptor::Disabled) - *pd = map; - } - - if (ctx->strictMode && !result) - __qmljs_throw_type_error(ctx); - return result; -} - -void ArgumentsObject::markObjects() -{ - for (int i = 0; i < mappedArguments.size(); ++i) { - Managed *m = mappedArguments.at(i).asManaged(); - if (m) - m->mark(); - } - Object::markObjects(); -} - - -Value ArgumentsGetterFunction::call(ExecutionContext *ctx, Value thisObject, Value *, int) -{ - Object *that = thisObject.asObject(); - if (!that) - __qmljs_throw_type_error(ctx); - ArgumentsObject *o = that->asArgumentsObject(); - if (!o) - __qmljs_throw_type_error(ctx); - - assert(index < o->context->argumentCount); - return o->context->argument(index); -} - -Value ArgumentsSetterFunction::call(ExecutionContext *ctx, Value thisObject, Value *args, int argc) -{ - Object *that = thisObject.asObject(); - if (!that) - __qmljs_throw_type_error(ctx); - ArgumentsObject *o = that->asArgumentsObject(); - if (!o) - __qmljs_throw_type_error(ctx); - - assert(index < o->context->argumentCount); - o->context->arguments[index] = argc ? args[0] : Value::undefinedValue(); - return Value::undefinedValue(); -} - - -} -} diff --git a/qv4argumentsobject.h b/qv4argumentsobject.h deleted file mode 100644 index adbaf5db1a..0000000000 --- a/qv4argumentsobject.h +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4ARGUMENTSOBJECTS_H -#define QV4ARGUMENTSOBJECTS_H - -#include -#include - -namespace QQmlJS { -namespace VM { - -struct ArgumentsGetterFunction: FunctionObject -{ - uint index; - - ArgumentsGetterFunction(ExecutionContext *scope, uint index) - : FunctionObject(scope), index(index) {} - - virtual Value call(ExecutionContext *ctx, Value thisObject, Value *, int); -}; - -struct ArgumentsSetterFunction: FunctionObject -{ - uint index; - - ArgumentsSetterFunction(ExecutionContext *scope, uint index) - : FunctionObject(scope), index(index) {} - - virtual Value call(ExecutionContext *ctx, Value thisObject, Value *args, int argc); -}; - - -struct ArgumentsObject: Object { - ExecutionContext *context; - QVector mappedArguments; - ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount); - - bool defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); - - virtual void markObjects(); -}; - -} -} - -#endif - diff --git a/qv4array.cpp b/qv4array.cpp deleted file mode 100644 index 9879b2f372..0000000000 --- a/qv4array.cpp +++ /dev/null @@ -1,649 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4array.h" -#include "qmljs_runtime.h" -#include "qv4object.h" -#include "qv4functionobject.h" -#include - -#ifdef QT_QMAP_DEBUG -# include -# include -#endif - -namespace QQmlJS { -namespace VM { - -bool ArrayElementLessThan::operator()(const PropertyDescriptor &p1, const PropertyDescriptor &p2) const -{ - if (p1.type == PropertyDescriptor::Generic) - return false; - if (p2.type == PropertyDescriptor::Generic) - return true; - Value v1 = thisObject->getValue(m_context, &p1); - Value v2 = thisObject->getValue(m_context, &p2); - - if (v1.isUndefined()) - return false; - if (v2.isUndefined()) - return true; - if (!m_comparefn.isUndefined()) { - Value args[] = { v1, v2 }; - Value result = __qmljs_call_value(m_context, Value::undefinedValue(), m_comparefn, args, 2); - return result.toNumber(m_context) <= 0; - } - return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); -} - - -const SparseArrayNode *SparseArrayNode::nextNode() const -{ - const SparseArrayNode *n = this; - if (n->right) { - n = n->right; - while (n->left) - n = n->left; - } else { - const SparseArrayNode *y = n->parent(); - while (y && n == y->right) { - n = y; - y = n->parent(); - } - n = y; - } - return n; -} - -const SparseArrayNode *SparseArrayNode::previousNode() const -{ - const SparseArrayNode *n = this; - if (n->left) { - n = n->left; - while (n->right) - n = n->right; - } else { - const SparseArrayNode *y = n->parent(); - while (y && n == y->left) { - n = y; - y = n->parent(); - } - n = y; - } - return n; -} - -SparseArrayNode *SparseArrayNode::copy(SparseArray *d) const -{ - SparseArrayNode *n = d->createNode(size_left, 0, false); - n->value = value; - n->setColor(color()); - if (left) { - n->left = left->copy(d); - n->left->setParent(n); - } else { - n->left = 0; - } - if (right) { - n->right = right->copy(d); - n->right->setParent(n); - } else { - n->right = 0; - } - return n; -} - -/* - x y - \ / \ - y --> x b - / \ \ - a b a -*/ -void SparseArray::rotateLeft(SparseArrayNode *x) -{ - SparseArrayNode *&root = header.left; - SparseArrayNode *y = x->right; - x->right = y->left; - if (y->left != 0) - y->left->setParent(x); - y->setParent(x->parent()); - if (x == root) - root = y; - else if (x == x->parent()->left) - x->parent()->left = y; - else - x->parent()->right = y; - y->left = x; - x->setParent(y); - y->size_left += x->size_left; -} - - -/* - x y - / / \ - y --> a x - / \ / - a b b -*/ -void SparseArray::rotateRight(SparseArrayNode *x) -{ - SparseArrayNode *&root = header.left; - SparseArrayNode *y = x->left; - x->left = y->right; - if (y->right != 0) - y->right->setParent(x); - y->setParent(x->parent()); - if (x == root) - root = y; - else if (x == x->parent()->right) - x->parent()->right = y; - else - x->parent()->left = y; - y->right = x; - x->setParent(y); - x->size_left -= y->size_left; -} - - -void SparseArray::rebalance(SparseArrayNode *x) -{ - SparseArrayNode *&root = header.left; - x->setColor(SparseArrayNode::Red); - while (x != root && x->parent()->color() == SparseArrayNode::Red) { - if (x->parent() == x->parent()->parent()->left) { - SparseArrayNode *y = x->parent()->parent()->right; - if (y && y->color() == SparseArrayNode::Red) { - x->parent()->setColor(SparseArrayNode::Black); - y->setColor(SparseArrayNode::Black); - x->parent()->parent()->setColor(SparseArrayNode::Red); - x = x->parent()->parent(); - } else { - if (x == x->parent()->right) { - x = x->parent(); - rotateLeft(x); - } - x->parent()->setColor(SparseArrayNode::Black); - x->parent()->parent()->setColor(SparseArrayNode::Red); - rotateRight (x->parent()->parent()); - } - } else { - SparseArrayNode *y = x->parent()->parent()->left; - if (y && y->color() == SparseArrayNode::Red) { - x->parent()->setColor(SparseArrayNode::Black); - y->setColor(SparseArrayNode::Black); - x->parent()->parent()->setColor(SparseArrayNode::Red); - x = x->parent()->parent(); - } else { - if (x == x->parent()->left) { - x = x->parent(); - rotateRight(x); - } - x->parent()->setColor(SparseArrayNode::Black); - x->parent()->parent()->setColor(SparseArrayNode::Red); - rotateLeft(x->parent()->parent()); - } - } - } - root->setColor(SparseArrayNode::Black); -} - -void SparseArray::deleteNode(SparseArrayNode *z) -{ - SparseArrayNode *&root = header.left; - SparseArrayNode *y = z; - SparseArrayNode *x; - SparseArrayNode *x_parent; - if (y->left == 0) { - x = y->right; - if (y == mostLeftNode) { - if (x) - mostLeftNode = x; // It cannot have (left) children due the red black invariant. - else - mostLeftNode = y->parent(); - } - } else { - if (y->right == 0) { - x = y->left; - } else { - y = y->right; - while (y->left != 0) - y = y->left; - x = y->right; - } - } - if (y != z) { - z->left->setParent(y); - y->left = z->left; - if (y != z->right) { - x_parent = y->parent(); - if (x) - x->setParent(y->parent()); - y->parent()->left = x; - y->right = z->right; - z->right->setParent(y); - } else { - x_parent = y; - } - if (root == z) - root = y; - else if (z->parent()->left == z) - z->parent()->left = y; - else - z->parent()->right = y; - y->setParent(z->parent()); - // Swap the colors - SparseArrayNode::Color c = y->color(); - y->setColor(z->color()); - z->setColor(c); - y = z; - } else { - x_parent = y->parent(); - if (x) - x->setParent(y->parent()); - if (root == z) - root = x; - else if (z->parent()->left == z) - z->parent()->left = x; - else - z->parent()->right = x; - } - if (y->color() != SparseArrayNode::Red) { - while (x != root && (x == 0 || x->color() == SparseArrayNode::Black)) { - if (x == x_parent->left) { - SparseArrayNode *w = x_parent->right; - if (w->color() == SparseArrayNode::Red) { - w->setColor(SparseArrayNode::Black); - x_parent->setColor(SparseArrayNode::Red); - rotateLeft(x_parent); - w = x_parent->right; - } - if ((w->left == 0 || w->left->color() == SparseArrayNode::Black) && - (w->right == 0 || w->right->color() == SparseArrayNode::Black)) { - w->setColor(SparseArrayNode::Red); - x = x_parent; - x_parent = x_parent->parent(); - } else { - if (w->right == 0 || w->right->color() == SparseArrayNode::Black) { - if (w->left) - w->left->setColor(SparseArrayNode::Black); - w->setColor(SparseArrayNode::Red); - rotateRight(w); - w = x_parent->right; - } - w->setColor(x_parent->color()); - x_parent->setColor(SparseArrayNode::Black); - if (w->right) - w->right->setColor(SparseArrayNode::Black); - rotateLeft(x_parent); - break; - } - } else { - SparseArrayNode *w = x_parent->left; - if (w->color() == SparseArrayNode::Red) { - w->setColor(SparseArrayNode::Black); - x_parent->setColor(SparseArrayNode::Red); - rotateRight(x_parent); - w = x_parent->left; - } - if ((w->right == 0 || w->right->color() == SparseArrayNode::Black) && - (w->left == 0 || w->left->color() == SparseArrayNode::Black)) { - w->setColor(SparseArrayNode::Red); - x = x_parent; - x_parent = x_parent->parent(); - } else { - if (w->left == 0 || w->left->color() == SparseArrayNode::Black) { - if (w->right) - w->right->setColor(SparseArrayNode::Black); - w->setColor(SparseArrayNode::Red); - rotateLeft(w); - w = x_parent->left; - } - w->setColor(x_parent->color()); - x_parent->setColor(SparseArrayNode::Black); - if (w->left) - w->left->setColor(SparseArrayNode::Black); - rotateRight(x_parent); - break; - } - } - } - if (x) - x->setColor(SparseArrayNode::Black); - } - free(y); - --numEntries; -} - -void SparseArray::recalcMostLeftNode() -{ - mostLeftNode = &header; - while (mostLeftNode->left) - mostLeftNode = mostLeftNode->left; -} - -static inline int qMapAlignmentThreshold() -{ - // malloc on 32-bit platforms should return pointers that are 8-byte - // aligned or more while on 64-bit platforms they should be 16-byte aligned - // or more - return 2 * sizeof(void*); -} - -static inline void *qMapAllocate(int alloc, int alignment) -{ - return alignment > qMapAlignmentThreshold() - ? qMallocAligned(alloc, alignment) - : ::malloc(alloc); -} - -static inline void qMapDeallocate(SparseArrayNode *node, int alignment) -{ - if (alignment > qMapAlignmentThreshold()) - qFreeAligned(node); - else - ::free(node); -} - -SparseArrayNode *SparseArray::createNode(uint sl, SparseArrayNode *parent, bool left) -{ - SparseArrayNode *node = static_cast(qMapAllocate(sizeof(SparseArrayNode), Q_ALIGNOF(SparseArrayNode))); - Q_CHECK_PTR(node); - - node->p = (quintptr)parent; - node->left = 0; - node->right = 0; - node->size_left = sl; - node->value = UINT_MAX; - ++numEntries; - - if (parent) { - if (left) { - parent->left = node; - if (parent == mostLeftNode) - mostLeftNode = node; - } else { - parent->right = node; - } - node->setParent(parent); - rebalance(node); - } - return node; -} - -void SparseArray::freeTree(SparseArrayNode *root, int alignment) -{ - if (root->left) - freeTree(root->left, alignment); - if (root->right) - freeTree(root->right, alignment); - qMapDeallocate(root, alignment); -} - -SparseArray::SparseArray() - : numEntries(0) -{ - header.p = 0; - header.left = 0; - header.right = 0; - mostLeftNode = &header; -} - -SparseArray::SparseArray(const SparseArray &other) -{ - header.p = 0; - header.right = 0; - if (other.header.left) { - header.left = other.header.left->copy(this); - header.left->setParent(&header); - recalcMostLeftNode(); - } -} - -SparseArrayNode *SparseArray::insert(uint akey) -{ - SparseArrayNode *n = root(); - SparseArrayNode *y = end(); - bool left = true; - uint s = akey; - while (n) { - y = n; - if (s == n->size_left) { - return n; - } else if (s < n->size_left) { - left = true; - n = n->left; - } else { - left = false; - s -= n->size_left; - n = n->right; - } - } - - return createNode(s, y, left); -} - -Array::Array(const Array &other) - : len(other.len) - , lengthProperty(0) - , values(other.values) - , sparse(0) -{ - freeList = other.freeList; - if (other.sparse) - sparse = new SparseArray(*other.sparse); -} - - -Value Array::indexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o) -{ - bool protoHasArray = false; - Object *p = o; - while ((p = p->prototype)) - if (p->array.length()) - protoHasArray = true; - - if (protoHasArray) { - // lets be safe and slow - for (uint i = fromIndex; i < endIndex; ++i) { - bool exists; - Value value = o->__get__(ctx, i, &exists); - if (exists && __qmljs_strict_equal(value, v)) - return Value::fromDouble(i); - } - } else if (sparse) { - for (SparseArrayNode *n = sparse->lowerBound(fromIndex); n && n->key() < endIndex; n = n->nextNode()) { - bool exists; - Value value = o->getValueChecked(ctx, descriptor(n->value), &exists); - if (exists && __qmljs_strict_equal(value, v)) - return Value::fromDouble(n->key()); - } - } else { - if ((int) endIndex > values.size()) - endIndex = values.size(); - PropertyDescriptor *pd = values.data() + offset; - PropertyDescriptor *end = pd + endIndex; - pd += fromIndex; - while (pd < end) { - bool exists; - Value value = o->getValueChecked(ctx, pd, &exists); - if (exists && __qmljs_strict_equal(value, v)) - return Value::fromDouble(pd - offset - values.constData()); - ++pd; - } - } - return Value::fromInt32(-1); -} - -void Array::concat(const Array &other) -{ - initSparse(); - int newLen = len + other.length(); - if (other.sparse) - initSparse(); - if (sparse) { - if (other.sparse) { - for (const SparseArrayNode *it = other.sparse->begin(); it != other.sparse->end(); it = it->nextNode()) - set(len + it->key(), other.descriptor(it->value)); - } else { - int oldSize = values.size(); - values.resize(oldSize + other.length()); - memcpy(values.data() + oldSize, other.values.constData() + other.offset, other.length()*sizeof(PropertyDescriptor)); - for (uint i = 0; i < other.length(); ++i) { - SparseArrayNode *n = sparse->insert(len + i); - n->value = oldSize + i; - } - } - } else { - int oldSize = values.size(); - values.resize(oldSize + other.length()); - memcpy(values.data() + oldSize, other.values.constData() + other.offset, other.length()*sizeof(PropertyDescriptor)); - } - setLengthUnchecked(newLen); -} - -void Array::sort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len) -{ - if (sparse) { - context->throwUnimplemented("Array::sort unimplemented for sparse arrays"); - return; - delete sparse; - } - - ArrayElementLessThan lessThan(context, thisObject, comparefn); - if (len > values.size() - offset) - len = values.size() - offset; - PropertyDescriptor *begin = values.begin() + offset; - std::sort(begin, begin + len, lessThan); -} - - -void Array::initSparse() -{ - if (!sparse) { - sparse = new SparseArray; - for (int i = offset; i < values.size(); ++i) { - SparseArrayNode *n = sparse->insert(i - offset); - n->value = i; - } - - if (offset) { - int o = offset; - for (int i = 0; i < o - 1; ++i) { - values[i].type = PropertyDescriptor::Generic; - values[i].value = Value::fromInt32(i + 1); - } - values[o - 1].type = PropertyDescriptor::Generic; - values[o - 1].value = Value::fromInt32(values.size()); - freeList = 0; - } else { - freeList = values.size(); - } - } -} - -bool Array::setLength(uint newLen) { - if (lengthProperty && !lengthProperty->isWritable()) - return false; - uint oldLen = length(); - bool ok = true; - if (newLen < oldLen) { - if (sparse) { - SparseArrayNode *begin = sparse->lowerBound(newLen); - SparseArrayNode *it = sparse->end()->previousNode(); - while (1) { - PropertyDescriptor &pd = values[it->value]; - if (pd.type != PropertyDescriptor::Generic && !pd.isConfigurable()) { - ok = false; - newLen = it->key() + 1; - break; - } - pd.type = PropertyDescriptor::Generic; - pd.value.tag = Value::_Undefined_Type; - pd.value.int_32 = freeList; - freeList = it->value; - bool brk = (it == begin); - SparseArrayNode *prev = it->previousNode(); - sparse->erase(it); - if (brk) - break; - it = prev; - } - } else { - PropertyDescriptor *it = values.data() + values.size(); - const PropertyDescriptor *begin = values.constData() + offset + newLen; - while (--it >= begin) { - if (it->type != PropertyDescriptor::Generic && !it->isConfigurable()) { - ok = false; - newLen = it - values.data() + offset + 1; - break; - } - } - values.resize(newLen + offset); - } - } else { - if (newLen >= 0x100000) - initSparse(); - } - setLengthUnchecked(newLen); - return ok; -} - -void Array::markObjects() const -{ - uint i = sparse ? 0 : offset; - for (; i < (uint)values.size(); ++i) { - const PropertyDescriptor &pd = values.at(i); - if (pd.isData()) { - if (Managed *m = pd.value.asManaged()) - m->mark(); - } else if (pd.isAccessor()) { - if (pd.get) - pd.get->mark(); - if (pd.set) - pd.set->mark(); - } - } -} - -} -} diff --git a/qv4array.h b/qv4array.h deleted file mode 100644 index 19aa99b4bb..0000000000 --- a/qv4array.h +++ /dev/null @@ -1,638 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4ARRAY_H -#define QV4ARRAY_H - -#include -#include -#include -#include - -#ifdef Q_MAP_DEBUG -#include -#endif - -#include - -namespace QQmlJS { -namespace VM { - -struct SparseArray; - -class ArrayElementLessThan -{ -public: - inline ArrayElementLessThan(ExecutionContext *context, Object *thisObject, const Value &comparefn) - : m_context(context), thisObject(thisObject), m_comparefn(comparefn) {} - - bool operator()(const PropertyDescriptor &v1, const PropertyDescriptor &v2) const; - -private: - ExecutionContext *m_context; - Object *thisObject; - Value m_comparefn; -}; - - -struct SparseArrayNode -{ - quintptr p; - SparseArrayNode *left; - SparseArrayNode *right; - uint size_left; - uint value; - - enum Color { Red = 0, Black = 1 }; - enum { Mask = 3 }; // reserve the second bit as well - - const SparseArrayNode *nextNode() const; - SparseArrayNode *nextNode() { return const_cast(const_cast(this)->nextNode()); } - const SparseArrayNode *previousNode() const; - SparseArrayNode *previousNode() { return const_cast(const_cast(this)->previousNode()); } - - Color color() const { return Color(p & 1); } - void setColor(Color c) { if (c == Black) p |= Black; else p &= ~Black; } - SparseArrayNode *parent() const { return reinterpret_cast(p & ~Mask); } - void setParent(SparseArrayNode *pp) { p = (p & Mask) | quintptr(pp); } - - uint key() const { - uint k = size_left; - const SparseArrayNode *n = this; - while (SparseArrayNode *p = n->parent()) { - if (p && p->right == n) - k += p->size_left; - n = p; - } - return k; - } - - SparseArrayNode *copy(SparseArray *d) const; - - SparseArrayNode *lowerBound(uint key); - SparseArrayNode *upperBound(uint key); -}; - - -inline SparseArrayNode *SparseArrayNode::lowerBound(uint akey) -{ - SparseArrayNode *n = this; - SparseArrayNode *last = 0; - while (n) { - if (akey <= n->size_left) { - last = n; - n = n->left; - } else { - akey -= n->size_left; - n = n->right; - } - } - return last; -} - - -inline SparseArrayNode *SparseArrayNode::upperBound(uint akey) -{ - SparseArrayNode *n = this; - SparseArrayNode *last = 0; - while (n) { - if (akey < n->size_left) { - last = n; - n = n->left; - } else { - akey -= n->size_left; - n = n->right; - } - } - return last; -} - - - -struct Q_CORE_EXPORT SparseArray -{ - SparseArray(); - ~SparseArray() { - if (root()) - freeTree(header.left, Q_ALIGNOF(SparseArrayNode)); - } - - SparseArray(const SparseArray &other); -private: - SparseArray &operator=(const SparseArray &other); - - int numEntries; - SparseArrayNode header; - SparseArrayNode *mostLeftNode; - - void rotateLeft(SparseArrayNode *x); - void rotateRight(SparseArrayNode *x); - void rebalance(SparseArrayNode *x); - void recalcMostLeftNode(); - - SparseArrayNode *root() const { return header.left; } - - void deleteNode(SparseArrayNode *z); - - -public: - SparseArrayNode *createNode(uint sl, SparseArrayNode *parent, bool left); - void freeTree(SparseArrayNode *root, int alignment); - - SparseArrayNode *findNode(uint akey) const; - - uint pop_front(); - void push_front(uint at); - uint pop_back(uint len); - void push_back(uint at, uint len); - - QList keys() const; - - const SparseArrayNode *end() const { return &header; } - SparseArrayNode *end() { return &header; } - const SparseArrayNode *begin() const { if (root()) return mostLeftNode; return end(); } - SparseArrayNode *begin() { if (root()) return mostLeftNode; return end(); } - - SparseArrayNode *erase(SparseArrayNode *n); - - SparseArrayNode *lowerBound(uint key); - const SparseArrayNode *lowerBound(uint key) const; - SparseArrayNode *upperBound(uint key); - const SparseArrayNode *upperBound(uint key) const; - SparseArrayNode *insert(uint akey); - - // STL compatibility - typedef uint key_type; - typedef int mapped_type; - typedef qptrdiff difference_type; - typedef int size_type; - -#ifdef Q_MAP_DEBUG - void dump() const; -#endif -}; - -inline SparseArrayNode *SparseArray::findNode(uint akey) const -{ - SparseArrayNode *n = root(); - - while (n) { - if (akey == n->size_left) { - return n; - } else if (akey < n->size_left) { - n = n->left; - } else { - akey -= n->size_left; - n = n->right; - } - } - - return 0; -} - -inline uint SparseArray::pop_front() -{ - uint idx = UINT_MAX ; - - SparseArrayNode *n = findNode(0); - if (n) { - idx = n->value; - deleteNode(n); - // adjust all size_left indices on the path to leftmost item by 1 - SparseArrayNode *n = root(); - while (n) { - n->size_left -= 1; - n = n->left; - } - } - return idx; -} - -inline void SparseArray::push_front(uint value) -{ - // adjust all size_left indices on the path to leftmost item by 1 - SparseArrayNode *n = root(); - while (n) { - n->size_left += 1; - n = n->left; - } - n = insert(0); - n->value = value; -} - -inline uint SparseArray::pop_back(uint len) -{ - uint idx = UINT_MAX; - if (!len) - return idx; - - SparseArrayNode *n = findNode(len - 1); - if (n) { - idx = n->value; - deleteNode(n); - } - return idx; -} - -inline void SparseArray::push_back(uint index, uint len) -{ - SparseArrayNode *n = insert(len); - n->value = index; -} - -#ifdef Q_MAP_DEBUG - -void SparseArray::dump() const -{ - const_iterator it = begin(); - qDebug() << "map dump:"; - while (it != end()) { - const SparseArrayNode *n = it.i; - int depth = 0; - while (n && n != root()) { - ++depth; - n = n->parent(); - } - QByteArray space(4*depth, ' '); - qDebug() << space << (it.i->color() == SparseArrayNode::Red ? "Red " : "Black") << it.i << it.i->left << it.i->right - << it.key() << it.value(); - ++it; - } - qDebug() << "---------"; -} -#endif - - -inline SparseArrayNode *SparseArray::erase(SparseArrayNode *n) -{ - if (n == end()) - return n; - - SparseArrayNode *next = n->nextNode(); - deleteNode(n); - return next; -} - -inline QList SparseArray::keys() const -{ - QList res; - res.reserve(numEntries); - SparseArrayNode *n = mostLeftNode; - while (n != end()) { - res.append(n->key()); - n = n->nextNode(); - } - return res; -} - -inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const -{ - const SparseArrayNode *lb = root()->lowerBound(akey); - if (!lb) - lb = end(); - return lb; -} - - -inline SparseArrayNode *SparseArray::lowerBound(uint akey) -{ - SparseArrayNode *lb = root()->lowerBound(akey); - if (!lb) - lb = end(); - return lb; -} - - -inline const SparseArrayNode *SparseArray::upperBound(uint akey) const -{ - const SparseArrayNode *ub = root()->upperBound(akey); - if (!ub) - ub = end(); - return ub; -} - - -inline SparseArrayNode *SparseArray::upperBound(uint akey) -{ - SparseArrayNode *ub = root()->upperBound(akey); - if (!ub) - ub = end(); - return ub; -} - - -class Array -{ - friend struct ArrayPrototype; - - uint len; - PropertyDescriptor *lengthProperty; - union { - uint freeList; - uint offset; - }; - QVector values; - SparseArray *sparse; - - void fillDescriptor(PropertyDescriptor *pd, Value v) - { - pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Enabled; - pd->enumberable = PropertyDescriptor::Enabled; - pd->configurable = PropertyDescriptor::Enabled; - pd->value = v; - } - - uint allocValue() { - uint idx = freeList; - if (values.size() <= (int)freeList) - values.resize(++freeList); - else - freeList = values.at(freeList).value.integerValue(); - return idx; - } - - uint allocValue(Value v) { - uint idx = allocValue(); - PropertyDescriptor *pd = &values[idx]; - fillDescriptor(pd, v); - return idx; - } - void freeValue(int idx) { - PropertyDescriptor &pd = values[idx]; - pd.type = PropertyDescriptor::Generic; - pd.value.tag = Value::_Undefined_Type; - pd.value.int_32 = freeList; - freeList = idx; - } - - PropertyDescriptor *descriptor(uint index) { - PropertyDescriptor *pd = values.data() + index; - if (!sparse) - pd += offset; - return pd; - } - const PropertyDescriptor *descriptor(uint index) const { - const PropertyDescriptor *pd = values.data() + index; - if (!sparse) - pd += offset; - return pd; - } - - void getHeadRoom() { - assert(!sparse && !offset); - offset = qMax(values.size() >> 2, 16); - QVector newValues(values.size() + offset); - memcpy(newValues.data() + offset, values.constData(), values.size()*sizeof(PropertyDescriptor)); - values = newValues; - } - -public: - Array() : len(0), lengthProperty(0), offset(0), sparse(0) {} - Array(const Array &other); - ~Array() { delete sparse; } - void initSparse(); - - uint length() const { return len; } - bool setLength(uint newLen); - - void setLengthProperty(PropertyDescriptor *pd) { lengthProperty = pd; } - PropertyDescriptor *getLengthProperty() { return lengthProperty; } - - void setLengthUnchecked(uint l) { - len = l; - if (lengthProperty) - lengthProperty->value = Value::fromUInt32(l); - } - - PropertyDescriptor *insert(uint index) { - PropertyDescriptor *pd; - if (!sparse && (index < 0x1000 || index < len + (len >> 2))) { - if (index + offset >= (uint)values.size()) { - values.resize(offset + index + 1); - for (uint i = len + 1; i < index; ++i) { - values[i].type = PropertyDescriptor::Generic; - values[i].value.tag = Value::_Undefined_Type; - } - } - pd = descriptor(index); - } else { - initSparse(); - SparseArrayNode *n = sparse->insert(index); - if (n->value == UINT_MAX) - n->value = allocValue(); - pd = descriptor(n->value); - } - if (index >= len) - setLengthUnchecked(index + 1); - return pd; - } - - void set(uint index, const PropertyDescriptor *pd) { - *insert(index) = *pd; - } - - void set(uint index, Value value) { - PropertyDescriptor *pd = insert(index); - fillDescriptor(pd, value); - } - - bool deleteIndex(uint index) { - if (index >= len) - return true; - PropertyDescriptor *pd = 0; - if (!sparse) { - pd = at(index); - } else { - SparseArrayNode *n = sparse->findNode(index); - if (n) - pd = descriptor(n->value); - } - if (!pd || pd->type == PropertyDescriptor::Generic) - return true; - if (!pd->isConfigurable()) - return false; - pd->type = PropertyDescriptor::Generic; - pd->value.tag = Value::_Undefined_Type; - if (sparse) { - pd->value.int_32 = freeList; - freeList = pd - values.constData(); - } - return true; - } - - PropertyDescriptor *at(uint index) { - if (!sparse) { - if (index >= values.size() - offset) - return 0; - return values.data() + index + offset; - } else { - SparseArrayNode *n = sparse->findNode(index); - if (!n) - return 0; - return values.data() + n->value; - } - } - - const PropertyDescriptor *nonSparseAt(uint index) const { - if (sparse) - return 0; - index += offset; - if (index >= (uint)values.size()) - return 0; - return values.constData() + index; - } - - PropertyDescriptor *nonSparseAtRef(uint index) { - if (sparse) - return 0; - index += offset; - if (index >= (uint)values.size()) - return 0; - return values.data() + index; - } - - const PropertyDescriptor *at(uint index) const { - if (!sparse) { - if (index >= values.size() - offset) - return 0; - return values.constData() + index + offset; - } else { - SparseArrayNode *n = sparse->findNode(index); - if (!n) - return 0; - return values.constData() + n->value; - } - } - - void markObjects() const; - - void push_front(Value v) { - if (!sparse) { - if (!offset) - getHeadRoom(); - - PropertyDescriptor pd; - fillDescriptor(&pd, v); - --offset; - values[offset] = pd; - } else { - uint idx = allocValue(v); - sparse->push_front(idx); - } - setLengthUnchecked(len + 1); - } - PropertyDescriptor *front() { - PropertyDescriptor *pd = 0; - if (!sparse) { - if (len) - pd = values.data() + offset; - } else { - SparseArrayNode *n = sparse->findNode(0); - if (n) - pd = descriptor(n->value); - } - if (pd && pd->type == PropertyDescriptor::Generic) - return 0; - return pd; - } - void pop_front() { - if (!len) - return; - if (!sparse) { - ++offset; - } else { - uint idx = sparse->pop_front(); - freeValue(idx); - } - setLengthUnchecked(len - 1); - } - void push_back(Value v) { - if (!sparse) { - PropertyDescriptor pd; - fillDescriptor(&pd, v); - values.append(pd); - } else { - uint idx = allocValue(v); - sparse->push_back(idx, len); - } - setLengthUnchecked(len + 1); - } - PropertyDescriptor *back() { - PropertyDescriptor *pd = 0; - if (!sparse) { - if (len) - pd = values.data() + offset + len; - } else { - SparseArrayNode *n = sparse->findNode(len - 1); - if (n) - pd = descriptor(n->value); - } - if (pd && pd->type == PropertyDescriptor::Generic) - return 0; - return pd; - } - void pop_back() { - if (!len) - return; - if (!sparse) { - values.resize(values.size() - 1); - } else { - uint idx = sparse->pop_back(len); - if (idx != UINT_MAX) - freeValue(idx); - } - setLengthUnchecked(len - 1); - } - - SparseArrayNode *sparseLowerBound(uint idx) { return sparse ? sparse->lowerBound(idx) : 0; } - SparseArrayNode *sparseBegin() { return sparse ? sparse->begin() : 0; } - SparseArrayNode *sparseEnd() { return sparse ? sparse->end() : 0; } - - void concat(const Array &other); - void sort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len); - Value indexOf(Value v, uint fromIndex, uint len, ExecutionContext *ctx, Object *o); -}; - -} -} - -#endif // QMAP_H diff --git a/qv4arrayobject.cpp b/qv4arrayobject.cpp deleted file mode 100644 index cbf34d3077..0000000000 --- a/qv4arrayobject.cpp +++ /dev/null @@ -1,798 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4arrayobject.h" -#include "qv4array.h" - -using namespace QQmlJS::VM; - -ArrayCtor::ArrayCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value ArrayCtor::call(ExecutionContext *ctx) -{ - ArrayObject *a = ctx->engine->newArrayObject(ctx); - Array &value = a->array; - if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { - bool ok; - uint len = ctx->argument(0).asArrayLength(ctx, &ok); - - if (!ok) { - ctx->throwRangeError(ctx->argument(0)); - return Value::undefinedValue(); - } - - value.setLengthUnchecked(len); - } else { - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { - value.set(i, ctx->argument(i)); - } - } - - return Value::fromObject(a); -} - -void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isArray"), method_isArray, 1); - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); - defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); - defineDefaultProperty(ctx, QStringLiteral("join"), method_join, 1); - defineDefaultProperty(ctx, QStringLiteral("pop"), method_pop, 0); - defineDefaultProperty(ctx, QStringLiteral("push"), method_push, 1); - defineDefaultProperty(ctx, QStringLiteral("reverse"), method_reverse, 0); - defineDefaultProperty(ctx, QStringLiteral("shift"), method_shift, 0); - defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); - defineDefaultProperty(ctx, QStringLiteral("sort"), method_sort, 1); - defineDefaultProperty(ctx, QStringLiteral("splice"), method_splice, 2); - defineDefaultProperty(ctx, QStringLiteral("unshift"), method_unshift, 1); - defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); - defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); - defineDefaultProperty(ctx, QStringLiteral("every"), method_every, 1); - defineDefaultProperty(ctx, QStringLiteral("some"), method_some, 1); - defineDefaultProperty(ctx, QStringLiteral("forEach"), method_forEach, 1); - defineDefaultProperty(ctx, QStringLiteral("map"), method_map, 1); - defineDefaultProperty(ctx, QStringLiteral("filter"), method_filter, 1); - defineDefaultProperty(ctx, QStringLiteral("reduce"), method_reduce, 1); - defineDefaultProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 1); -} - -uint ArrayPrototype::getLength(ExecutionContext *ctx, Object *o) -{ - if (o->isArrayObject()) - return o->array.length(); - return o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); -} - -Value ArrayPrototype::method_isArray(ExecutionContext *ctx) -{ - Value arg = ctx->argument(0); - bool isArray = arg.asArrayObject(); - return Value::fromBoolean(isArray); -} - -Value ArrayPrototype::method_toString(ExecutionContext *ctx) -{ - return method_join(ctx); -} - -Value ArrayPrototype::method_toLocaleString(ExecutionContext *ctx) -{ - return method_toString(ctx); -} - -Value ArrayPrototype::method_concat(ExecutionContext *ctx) -{ - Array result; - - if (ArrayObject *instance = ctx->thisObject.asArrayObject()) - result = instance->array; - else { - QString v = ctx->thisObject.toString(ctx)->toQString(); - result.set(0, Value::fromString(ctx, v)); - } - - for (uint i = 0; i < ctx->argumentCount; ++i) { - quint32 k = result.length(); - Value arg = ctx->argument(i); - - if (ArrayObject *elt = arg.asArrayObject()) - result.concat(elt->array); - - else - result.set(k, arg); - } - - return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); -} - -Value ArrayPrototype::method_join(ExecutionContext *ctx) -{ - Value arg = ctx->argument(0); - - QString r4; - if (arg.isUndefined()) - r4 = QStringLiteral(","); - else - r4 = arg.toString(ctx)->toQString(); - - Value self = ctx->thisObject; - const Value length = self.property(ctx, ctx->engine->id_length); - const quint32 r2 = Value::toUInt32(length.isUndefined() ? 0 : length.toNumber(ctx)); - - static QSet visitedArrayElements; - - if (! r2 || visitedArrayElements.contains(self.objectValue())) - return Value::fromString(ctx, QString()); - - // avoid infinite recursion - visitedArrayElements.insert(self.objectValue()); - - QString R; - - // ### FIXME - if (ArrayObject *a = self.asArrayObject()) { - for (uint i = 0; i < a->array.length(); ++i) { - if (i) - R += r4; - - Value e = a->__get__(ctx, i); - if (! (e.isUndefined() || e.isNull())) - R += e.toString(ctx)->toQString(); - } - } else { - // - // crazy! - // - Value r6 = self.property(ctx, ctx->engine->identifier(QStringLiteral("0"))); - if (!(r6.isUndefined() || r6.isNull())) - R = r6.toString(ctx)->toQString(); - - for (quint32 k = 1; k < r2; ++k) { - R += r4; - - String *name = Value::fromDouble(k).toString(ctx); - Value r12 = self.property(ctx, name); - - if (! (r12.isUndefined() || r12.isNull())) - R += r12.toString(ctx)->toQString(); - } - } - - visitedArrayElements.remove(self.objectValue()); - return Value::fromString(ctx, R); -} - -Value ArrayPrototype::method_pop(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - uint len = getLength(ctx, instance); - - if (!len) { - if (!instance->isArrayObject()) - instance->__put__(ctx, ctx->engine->id_length, Value::fromInt32(0)); - return Value::undefinedValue(); - } - - Value result = instance->__get__(ctx, len - 1); - - instance->__delete__(ctx, len - 1); - if (instance->isArrayObject()) - instance->array.setLengthUnchecked(len - 1); - else - instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - 1)); - return result; -} - -Value ArrayPrototype::method_push(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - uint len = getLength(ctx, instance); - - if (len + ctx->argumentCount < len) { - // ughh... - double l = len; - for (double i = 0; i < ctx->argumentCount; ++i) { - Value idx = Value::fromDouble(l + i); - instance->__put__(ctx, idx.toString(ctx), ctx->argument(i)); - } - double newLen = l + ctx->argumentCount; - if (!instance->isArrayObject()) - instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); - else - ctx->throwRangeError(Value::fromString(ctx, QStringLiteral("Array.prototype.push: Overflow"))); - return Value::fromDouble(newLen); - } - - bool protoHasArray = false; - Object *p = instance; - while ((p = p->prototype)) - if (p->array.length()) - protoHasArray = true; - - if (!protoHasArray && len == instance->array.length()) { - for (uint i = 0; i < ctx->argumentCount; ++i) { - Value v = ctx->argument(i); - instance->array.push_back(v); - } - } else { - for (uint i = 0; i < ctx->argumentCount; ++i) - instance->__put__(ctx, len + i, ctx->argument(i)); - } - uint newLen = len + ctx->argumentCount; - if (!instance->isArrayObject()) - instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); - - if (newLen < INT_MAX) - return Value::fromInt32(newLen); - return Value::fromDouble((double)newLen); - -} - -Value ArrayPrototype::method_reverse(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - uint length = getLength(ctx, instance); - - int lo = 0, hi = length - 1; - - for (; lo < hi; ++lo, --hi) { - bool loExists, hiExists; - Value lval = instance->__get__(ctx, lo, &loExists); - Value hval = instance->__get__(ctx, hi, &hiExists); - if (hiExists) - instance->__put__(ctx, lo, hval); - else - instance->__delete__(ctx, lo); - if (loExists) - instance->__put__(ctx, hi, lval); - else - instance->__delete__(ctx, hi); - } - return Value::fromObject(instance); -} - -Value ArrayPrototype::method_shift(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - uint len = getLength(ctx, instance); - - if (!len) { - if (!instance->isArrayObject()) - instance->__put__(ctx, ctx->engine->id_length, Value::fromInt32(0)); - return Value::undefinedValue(); - } - - Value result = instance->getValueChecked(ctx, instance->array.front()); - - bool protoHasArray = false; - Object *p = instance; - while ((p = p->prototype)) - if (p->array.length()) - protoHasArray = true; - - if (!protoHasArray && len >= instance->array.length()) { - instance->array.pop_front(); - } else { - // do it the slow way - for (uint k = 1; k < len; ++k) { - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (exists) - instance->__put__(ctx, k - 1, v); - else - instance->__delete__(ctx, k - 1); - } - instance->__delete__(ctx, len - 1); - } - - if (!instance->isArrayObject()) - instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - 1)); - return result; -} - -Value ArrayPrototype::method_slice(ExecutionContext *ctx) -{ - Object *o = ctx->thisObject.toObject(ctx).objectValue(); - - Array result; - uint len = o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); - double s = ctx->argument(0).toInteger(ctx); - uint start; - if (s < 0) - start = (uint)qMax(len + s, 0.); - else if (s > len) - start = len; - else - start = (uint) s; - uint end = len; - if (!ctx->argument(1).isUndefined()) { - double e = ctx->argument(1).toInteger(ctx); - if (e < 0) - end = (uint)qMax(len + e, 0.); - else if (e > len) - end = len; - else - end = (uint) e; - } - - uint n = 0; - for (uint i = start; i < end; ++i) { - bool exists; - Value v = o->__get__(ctx, i, &exists); - if (exists) - result.set(n, v); - ++n; - } - return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); -} - -Value ArrayPrototype::method_sort(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - Value comparefn = ctx->argument(0); - instance->array.sort(ctx, instance, comparefn, len); - return ctx->thisObject; -} - -Value ArrayPrototype::method_splice(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - uint len = getLength(ctx, instance); - - ArrayObject *newArray = ctx->engine->newArrayObject(ctx); - - double rs = ctx->argument(0).toInteger(ctx); - uint start; - if (rs < 0) - start = (uint) qMax(0., len + rs); - else - start = (uint) qMin(rs, (double)len); - - uint deleteCount = (uint)qMin(qMax(ctx->argument(1).toInteger(ctx), 0.), (double)(len - start)); - - newArray->array.values.resize(deleteCount); - PropertyDescriptor *pd = newArray->array.values.data(); - for (uint i = 0; i < deleteCount; ++i) { - pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Enabled; - pd->enumberable = PropertyDescriptor::Enabled; - pd->configurable = PropertyDescriptor::Enabled; - pd->value = instance->__get__(ctx, start + i); - ++pd; - } - newArray->array.setLengthUnchecked(deleteCount); - - uint itemCount = ctx->argumentCount < 2 ? 0 : ctx->argumentCount - 2; - - if (itemCount < deleteCount) { - for (uint k = start; k < len - deleteCount; ++k) { - bool exists; - Value v = instance->__get__(ctx, k + deleteCount, &exists); - if (exists) - instance->array.set(k + itemCount, v); - else - instance->__delete__(ctx, k + itemCount); - } - for (uint k = len; k > len - deleteCount + itemCount; --k) - instance->__delete__(ctx, k - 1); - } else if (itemCount > deleteCount) { - uint k = len - deleteCount; - while (k > start) { - bool exists; - Value v = instance->__get__(ctx, k + deleteCount - 1, &exists); - if (exists) - instance->array.set(k + itemCount - 1, v); - else - instance->__delete__(ctx, k + itemCount - 1); - --k; - } - } - - for (uint i = 0; i < itemCount; ++i) - instance->array.set(start + i, ctx->argument(i + 2)); - - ctx->strictMode = true; - instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - deleteCount + itemCount)); - - return Value::fromObject(newArray); -} - -Value ArrayPrototype::method_unshift(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - uint len = getLength(ctx, instance); - - bool protoHasArray = false; - Object *p = instance; - while ((p = p->prototype)) - if (p->array.length()) - protoHasArray = true; - - if (!protoHasArray && len >= instance->array.length()) { - for (int i = ctx->argumentCount - 1; i >= 0; --i) { - Value v = ctx->argument(i); - instance->array.push_front(v); - } - } else { - for (uint k = len; k > 0; --k) { - bool exists; - Value v = instance->__get__(ctx, k - 1, &exists); - if (exists) - instance->__put__(ctx, k + ctx->argumentCount - 1, v); - else - instance->__delete__(ctx, k + ctx->argumentCount - 1); - } - for (uint i = 0; i < ctx->argumentCount; ++i) - instance->__put__(ctx, i, ctx->argument(i)); - } - uint newLen = len + ctx->argumentCount; - if (!instance->isArrayObject()) - instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); - - if (newLen < INT_MAX) - return Value::fromInt32(newLen); - return Value::fromDouble((double)newLen); -} - -Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - uint len = getLength(ctx, instance); - if (!len) - return Value::fromInt32(-1); - - Value searchValue; - uint fromIndex = 0; - - if (ctx->argumentCount >= 1) - searchValue = ctx->argument(0); - else - searchValue = Value::undefinedValue(); - - if (ctx->argumentCount >= 2) { - double f = ctx->argument(1).toInteger(ctx); - if (f >= len) - return Value::fromInt32(-1); - if (f < 0) - f = qMax(len + f, 0.); - fromIndex = (uint) f; - } - - if (instance->isStringObject()) { - for (uint k = fromIndex; k < len; ++k) { - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (exists && __qmljs_strict_equal(v, searchValue)) - return Value::fromDouble(k); - } - return Value::fromInt32(-1); - } - - return instance->array.indexOf(searchValue, fromIndex, len, ctx, instance); -} - -Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - uint len = getLength(ctx, instance); - if (!len) - return Value::fromInt32(-1); - - Value searchValue; - uint fromIndex = len; - - if (ctx->argumentCount >= 1) - searchValue = ctx->argument(0); - else - searchValue = Value::undefinedValue(); - - if (ctx->argumentCount >= 2) { - double f = ctx->argument(1).toInteger(ctx); - if (f > 0) - f = qMin(f, (double)(len - 1)); - else if (f < 0) { - f = len + f; - if (f < 0) - return Value::fromInt32(-1); - } - fromIndex = (uint) f + 1; - } - - for (uint k = fromIndex; k > 0;) { - --k; - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (exists && __qmljs_strict_equal(v, searchValue)) - return Value::fromDouble(k); - } - return Value::fromInt32(-1); -} - -Value ArrayPrototype::method_every(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - __qmljs_throw_type_error(ctx); - - Value thisArg = ctx->argument(1); - - bool ok = true; - for (uint k = 0; ok && k < len; ++k) { - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value r = callback->call(ctx, thisArg, args, 3); - ok = __qmljs_to_boolean(r, ctx); - } - return Value::fromBoolean(ok); -} - -Value ArrayPrototype::method_some(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - __qmljs_throw_type_error(ctx); - - Value thisArg = ctx->argument(1); - - for (uint k = 0; k < len; ++k) { - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value r = callback->call(ctx, thisArg, args, 3); - if (__qmljs_to_boolean(r, ctx)) - return Value::fromBoolean(true); - } - return Value::fromBoolean(false); -} - -Value ArrayPrototype::method_forEach(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - __qmljs_throw_type_error(ctx); - - Value thisArg = ctx->argument(1); - - for (uint k = 0; k < len; ++k) { - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - callback->call(ctx, thisArg, args, 3); - } - return Value::undefinedValue(); -} - -Value ArrayPrototype::method_map(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - __qmljs_throw_type_error(ctx); - - Value thisArg = ctx->argument(1); - - ArrayObject *a = ctx->engine->newArrayObject(ctx); - a->array.setLength(len); - - for (uint k = 0; k < len; ++k) { - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value mapped = callback->call(ctx, thisArg, args, 3); - a->array.set(k, mapped); - } - return Value::fromObject(a); -} - -Value ArrayPrototype::method_filter(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - __qmljs_throw_type_error(ctx); - - Value thisArg = ctx->argument(1); - - ArrayObject *a = ctx->engine->newArrayObject(ctx); - - uint to = 0; - for (uint k = 0; k < len; ++k) { - bool exists; - Value v = instance->__get__(ctx, k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value selected = callback->call(ctx, thisArg, args, 3); - if (__qmljs_to_boolean(selected, ctx)) { - a->array.set(to, v); - ++to; - } - } - return Value::fromObject(a); -} - -Value ArrayPrototype::method_reduce(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - __qmljs_throw_type_error(ctx); - - uint k = 0; - Value acc; - if (ctx->argumentCount > 1) { - acc = ctx->argument(1); - } else { - bool kPresent = false; - while (k < len && !kPresent) { - Value v = instance->__get__(ctx, k, &kPresent); - if (kPresent) - acc = v; - ++k; - } - if (!kPresent) - __qmljs_throw_type_error(ctx); - } - - while (k < len) { - bool kPresent; - Value v = instance->__get__(ctx, k, &kPresent); - if (kPresent) { - Value args[4]; - args[0] = acc; - args[1] = v; - args[2] = Value::fromDouble(k); - args[3] = ctx->thisObject; - acc = callback->call(ctx, Value::undefinedValue(), args, 4); - } - ++k; - } - return acc; -} - -Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) -{ - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - __qmljs_throw_type_error(ctx); - - if (len == 0) { - if (ctx->argumentCount == 1) - __qmljs_throw_type_error(ctx); - return ctx->argument(1); - } - - uint k = len; - Value acc; - if (ctx->argumentCount > 1) { - acc = ctx->argument(1); - } else { - bool kPresent = false; - while (k > 0 && !kPresent) { - Value v = instance->__get__(ctx, k - 1, &kPresent); - if (kPresent) - acc = v; - --k; - } - if (!kPresent) - __qmljs_throw_type_error(ctx); - } - - while (k > 0) { - bool kPresent; - Value v = instance->__get__(ctx, k - 1, &kPresent); - if (kPresent) { - Value args[4]; - args[0] = acc; - args[1] = v; - args[2] = Value::fromDouble(k - 1); - args[3] = ctx->thisObject; - acc = callback->call(ctx, Value::undefinedValue(), args, 4); - } - --k; - } - return acc; -} - diff --git a/qv4arrayobject.h b/qv4arrayobject.h deleted file mode 100644 index 8a15546c1c..0000000000 --- a/qv4arrayobject.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4ARRAYOBJECT_H -#define QV4ARRAYOBJECT_H - -#include "qv4object.h" -#include "qv4functionobject.h" -#include - -namespace QQmlJS { -namespace VM { - - -struct ArrayCtor: FunctionObject -{ - ArrayCtor(ExecutionContext *scope); - - virtual Value call(ExecutionContext *ctx); -}; - -struct ArrayPrototype: ArrayObject -{ - ArrayPrototype(ExecutionContext *context) : ArrayObject(context) {} - - void init(ExecutionContext *ctx, const Value &ctor); - - static uint getLength(ExecutionContext *ctx, Object *o); - - static Value method_isArray(ExecutionContext *ctx); - static Value method_toString(ExecutionContext *ctx); - static Value method_toLocaleString(ExecutionContext *ctx); - static Value method_concat(ExecutionContext *ctx); - static Value method_join(ExecutionContext *ctx); - static Value method_pop(ExecutionContext *ctx); - static Value method_push(ExecutionContext *ctx); - static Value method_reverse(ExecutionContext *ctx); - static Value method_shift(ExecutionContext *ctx); - static Value method_slice(ExecutionContext *ctx); - static Value method_sort(ExecutionContext *ctx); - static Value method_splice(ExecutionContext *ctx); - static Value method_unshift(ExecutionContext *ctx); - static Value method_indexOf(ExecutionContext *ctx); - static Value method_lastIndexOf(ExecutionContext *ctx); - static Value method_every(ExecutionContext *ctx); - static Value method_some(ExecutionContext *ctx); - static Value method_forEach(ExecutionContext *ctx); - static Value method_map(ExecutionContext *ctx); - static Value method_filter(ExecutionContext *ctx); - static Value method_reduce(ExecutionContext *ctx); - static Value method_reduceRight(ExecutionContext *ctx); -}; - - -} // end of namespace VM -} // end of namespace QQmlJS - -#endif // QV4ECMAOBJECTS_P_H diff --git a/qv4booleanobject.cpp b/qv4booleanobject.cpp deleted file mode 100644 index 9df0f23a6a..0000000000 --- a/qv4booleanobject.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4booleanobject.h" - -using namespace QQmlJS::VM; - -BooleanCtor::BooleanCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value BooleanCtor::construct(ExecutionContext *ctx) -{ - const double n = ctx->argument(0).toBoolean(ctx); - return Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n))); -} - -Value BooleanCtor::call(ExecutionContext *ctx) -{ - bool value = ctx->argumentCount ? ctx->argument(0).toBoolean(ctx) : 0; - return Value::fromBoolean(value); -} - -void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); -} - -Value BooleanPrototype::method_toString(ExecutionContext *ctx) -{ - bool result; - if (ctx->thisObject.isBoolean()) { - result = ctx->thisObject.booleanValue(); - } else { - BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); - if (!thisObject) - ctx->throwTypeError(); - result = thisObject->value.booleanValue(); - } - - return Value::fromString(ctx, QLatin1String(result ? "true" : "false")); -} - -Value BooleanPrototype::method_valueOf(ExecutionContext *ctx) -{ - BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); - if (!thisObject) - ctx->throwTypeError(); - - return thisObject->value; -} diff --git a/qv4booleanobject.h b/qv4booleanobject.h deleted file mode 100644 index 44d87b1d50..0000000000 --- a/qv4booleanobject.h +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4BOOLEANOBJECT_H -#define QBOOLEANOBJECT_H - -#include "qv4object.h" -#include "qv4functionobject.h" -#include - -namespace QQmlJS { -namespace VM { - -struct BooleanCtor: FunctionObject -{ - BooleanCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct BooleanPrototype: BooleanObject -{ - BooleanPrototype(): BooleanObject(Value::fromBoolean(false)) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_toString(ExecutionContext *ctx); - static Value method_valueOf(ExecutionContext *ctx); -}; - - -} // end of namespace VM -} // end of namespace QQmlJS - -#endif // QV4ECMAOBJECTS_P_H diff --git a/qv4codegen.cpp b/qv4codegen.cpp deleted file mode 100644 index 4c4f7dd1d6..0000000000 --- a/qv4codegen.cpp +++ /dev/null @@ -1,2614 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4codegen_p.h" -#include "debugging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace QQmlJS; -using namespace AST; - -namespace { -QTextStream qout(stdout, QIODevice::WriteOnly); - -void dfs(IR::BasicBlock *block, - QSet *V, - QVector *blocks) -{ - if (! V->contains(block)) { - V->insert(block); - - foreach (IR::BasicBlock *succ, block->out) - dfs(succ, V, blocks); - - blocks->append(block); - } -} - -struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor -{ - IR::Function *_function; - IR::Stmt *_stmt; - - ComputeUseDef(IR::Function *function) - : _function(function) - , _stmt(0) {} - - void operator()(IR::Stmt *s) { - assert(! s->d); - s->d = new IR::Stmt::Data; - qSwap(_stmt, s); - _stmt->accept(this); - qSwap(_stmt, s); - } - - virtual void visitConst(IR::Const *) {} - virtual void visitString(IR::String *) {} - virtual void visitRegExp(IR::RegExp *) {} - virtual void visitName(IR::Name *) {} - virtual void visitClosure(IR::Closure *) {} - virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); } - virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(IR::Member *e) { e->base->accept(this); } - virtual void visitExp(IR::Exp *s) { s->expr->accept(this); } - virtual void visitEnter(IR::Enter *) {} - virtual void visitLeave(IR::Leave *) {} - virtual void visitJump(IR::Jump *) {} - virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); } - virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } - - virtual void visitTemp(IR::Temp *e) { - if (e->index < 0) - return; - - if (! _stmt->d->uses.contains(e->index)) - _stmt->d->uses.append(e->index); - } - - virtual void visitCall(IR::Call *e) { - e->base->accept(this); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitNew(IR::New *e) { - e->base->accept(this); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitMove(IR::Move *s) { - if (IR::Temp *t = s->target->asTemp()) { - if (t->index >= 0) { - if (! _stmt->d->defs.contains(t->index)) - _stmt->d->defs.append(t->index); - } - } else { - s->target->accept(this); - } - s->source->accept(this); - } -}; - -void liveness(IR::Function *function) -{ - QSet V; - QVector blocks; - - ComputeUseDef computeUseDef(function); - foreach (IR::BasicBlock *block, function->basicBlocks) { - foreach (IR::Stmt *s, block->statements) - computeUseDef(s); - } - - dfs(function->basicBlocks.at(0), &V, &blocks); - - bool changed; - do { - changed = false; - - foreach (IR::BasicBlock *block, blocks) { - const QBitArray previousLiveIn = block->liveIn; - const QBitArray previousLiveOut = block->liveOut; - QBitArray live(function->tempCount); - foreach (IR::BasicBlock *succ, block->out) - live |= succ->liveIn; - block->liveOut = live; - for (int i = block->statements.size() - 1; i != -1; --i) { - IR::Stmt *s = block->statements.at(i); - s->d->liveOut = live; - foreach (unsigned d, s->d->defs) - live.clearBit(d); - foreach (unsigned u, s->d->uses) - live.setBit(u); - s->d->liveIn = live; - } - block->liveIn = live; - if (! changed) { - if (previousLiveIn != block->liveIn || previousLiveOut != block->liveOut) - changed = true; - } - } - } while (changed); -} - -} // end of anonymous namespace - -class Codegen::ScanFunctions: Visitor -{ -public: - ScanFunctions(Codegen *cg) - : _cg(cg) - , _env(0) - { - } - - void operator()(Node *node) - { - if (node) - node->accept(this); - } - - inline void enterEnvironment(Node *node) - { - Environment *e = _cg->newEnvironment(node, _env); - if (!e->isStrict) - e->isStrict = _cg->_strictMode; - _envStack.append(e); - _env = e; - } - - inline void leaveEnvironment() - { - _envStack.pop(); - _env = _envStack.isEmpty() ? 0 : _envStack.top(); - } - -protected: - using Visitor::visit; - using Visitor::endVisit; - - void checkDirectivePrologue(SourceElements *ast) - { - for (SourceElements *it = ast; it; it = it->next) { - if (StatementSourceElement *stmt = cast(it->element)) { - if (ExpressionStatement *expr = cast(stmt->statement)) { - if (StringLiteral *strLit = cast(expr->expression)) { - if (strLit->value == QLatin1String("use strict")) { - _env->isStrict = true; - } else { - // TODO: give a warning. - } - continue; - } - } - } - - break; - } - } - - void checkName(const QStringRef &name, const SourceLocation &loc) - { - if (_env->isStrict) { - if (name == QLatin1String("implements") - || name == QLatin1String("interface") - || name == QLatin1String("let") - || name == QLatin1String("package") - || name == QLatin1String("private") - || name == QLatin1String("protected") - || name == QLatin1String("public") - || name == QLatin1String("static") - || name == QLatin1String("yield")) { - _cg->throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Unexpected strict mode reserved word")); - } - } - } - void checkForArguments(AST::FormalParameterList *parameters) - { - while (parameters) { - if (parameters->name == QStringLiteral("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - parameters = parameters->next; - } - } - - virtual bool visit(Program *ast) - { - enterEnvironment(ast); - checkDirectivePrologue(ast->elements); - return true; - } - - virtual void endVisit(Program *) - { - leaveEnvironment(); - } - - virtual bool visit(CallExpression *ast) - { - if (! _env->hasDirectEval) { - if (IdentifierExpression *id = cast(ast->base)) { - if (id->name == QStringLiteral("eval")) { - if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown) - _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; - _env->hasDirectEval = true; - } - } - } - int argc = 0; - for (ArgumentList *it = ast->arguments; it; it = it->next) - ++argc; - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); - return true; - } - - virtual bool visit(NewMemberExpression *ast) - { - int argc = 0; - for (ArgumentList *it = ast->arguments; it; it = it->next) - ++argc; - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); - return true; - } - - virtual bool visit(VariableDeclaration *ast) - { - if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) - _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); - checkName(ast->name, ast->identifierToken); - if (ast->name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration); - return true; - } - - virtual bool visit(IdentifierExpression *ast) - { - checkName(ast->name, ast->identifierToken); - if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; - return true; - } - - virtual bool visit(FunctionExpression *ast) - { - if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) - _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Function name may not be eval or arguments in strict mode")); - enterFunction(ast, ast->name.toString(), ast->formals, ast->body); - return true; - } - - virtual void endVisit(FunctionExpression *) - { - leaveEnvironment(); - } - - virtual bool visit(PropertyGetterSetter *ast) - { - enterFunction(ast, QString(), ast->formals, ast->functionBody); - return true; - } - - virtual void endVisit(PropertyGetterSetter *) - { - leaveEnvironment(); - } - - virtual bool visit(FunctionDeclaration *ast) - { - if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) - _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Function name may not be eval or arguments in strict mode")); - enterFunction(ast, ast->name.toString(), ast->formals, ast->body, ast); - return true; - } - - virtual void endVisit(FunctionDeclaration *) - { - leaveEnvironment(); - } - - virtual bool visit(WithStatement *ast) - { - if (_env->isStrict) { - _cg->throwSyntaxError(ast->withToken, QCoreApplication::translate("qv4codegen", "'with' statement is not allowed in strict mode")); - return false; - } - - return true; - } - -private: - void enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionDeclaration *decl = 0) - { - bool wasStrict = false; - if (_env) { - _env->hasNestedFunctions = true; - _env->enter(name, Environment::FunctionDefinition, decl); - if (name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - wasStrict = _env->isStrict; - } - - enterEnvironment(ast); - checkForArguments(formals); - - if (body) - checkDirectivePrologue(body->elements); - - if (wasStrict || _env->isStrict) { - QStringList args; - for (FormalParameterList *it = formals; it; it = it->next) { - QString arg = it->name.toString(); - if (args.contains(arg)) - _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "Duplicate parameter name '%1' in strict mode").arg(arg)); - if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) - _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "'%1' cannot be used as parameter name in strict mode").arg(arg)); - args += arg; - } - } - } - - - Codegen *_cg; - Environment *_env; - QStack _envStack; -}; - -Codegen::Codegen(VM::ExecutionContext *context, bool strict) - : _module(0) - , _function(0) - , _block(0) - , _exitBlock(0) - , _throwBlock(0) - , _returnAddress(0) - , _mode(GlobalCode) - , _env(0) - , _loop(0) - , _labelledStatement(0) - , _scopeAndFinally(0) - , _context(context) - , _strictMode(strict) - , _debugger(context->engine->debugger) - , _errorHandler(0) -{ -} - -Codegen::Codegen(ErrorHandler *errorHandler, bool strictMode) - : _module(0) - , _function(0) - , _block(0) - , _exitBlock(0) - , _throwBlock(0) - , _returnAddress(0) - , _mode(GlobalCode) - , _env(0) - , _loop(0) - , _labelledStatement(0) - , _scopeAndFinally(0) - , _context(0) - , _strictMode(strictMode) - , _debugger(0) - , _errorHandler(errorHandler) -{ -} - -IR::Function *Codegen::operator()(const QString &fileName, Program *node, - IR::Module *module, Mode mode, - const QStringList &inheritedLocals) -{ - assert(node); - - _fileName = fileName; - _module = module; - _env = 0; - - ScanFunctions scan(this); - scan(node); - - IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, - node->elements, mode, inheritedLocals); - if (_debugger) { - if (node->elements->element) { - SourceLocation loc = node->elements->element->firstSourceLocation(); - _debugger->setSourceLocation(globalCode, loc.startLine, loc.startColumn); - } - } - - foreach (IR::Function *function, _module->functions) { - linearize(function); - } - - qDeleteAll(_envMap); - _envMap.clear(); - - return globalCode; -} - -IR::Function *Codegen::operator()(const QString &fileName, AST::FunctionExpression *ast, IR::Module *module) -{ - _fileName = fileName; - _module = module; - _env = 0; - - ScanFunctions scan(this); - // fake a global environment - scan.enterEnvironment(0); - scan(ast); - scan.leaveEnvironment(); - - IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); - if (_debugger) - _debugger->setSourceLocation(function, ast->functionToken.startLine, ast->functionToken.startColumn); - - foreach (IR::Function *function, _module->functions) { - linearize(function); - } - - qDeleteAll(_envMap); - _envMap.clear(); - - return function; -} - - -void Codegen::enterEnvironment(Node *node) -{ - _env = _envMap.value(node); - assert(_env); -} - -void Codegen::leaveEnvironment() -{ - assert(_env); - _env = _env->parent; -} - -void Codegen::enterLoop(Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock) -{ - _loop = new Loop(node, breakBlock, continueBlock, _loop); - _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement - _loop->scopeAndFinally = _scopeAndFinally; - _labelledStatement = 0; -} - -void Codegen::leaveLoop() -{ - Loop *current = _loop; - _loop = _loop->parent; - delete current; -} - -IR::Expr *Codegen::member(IR::Expr *base, const QString *name) -{ - if (base->asTemp() /*|| base->asName()*/) - return _block->MEMBER(base->asTemp(), name); - else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), base); - return _block->MEMBER(_block->TEMP(t), name); - } -} - -IR::Expr *Codegen::subscript(IR::Expr *base, IR::Expr *index) -{ - if (! base->asTemp()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), base); - base = _block->TEMP(t); - } - - if (! index->asTemp()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), index); - index = _block->TEMP(t); - } - - assert(base->asTemp() && index->asTemp()); - return _block->SUBSCRIPT(base->asTemp(), index->asTemp()); -} - -IR::Expr *Codegen::argument(IR::Expr *expr) -{ - if (expr && ! expr->asTemp()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - return expr; -} - -// keeps references alive, converts other expressions to temps -IR::Expr *Codegen::reference(IR::Expr *expr) -{ - if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember() && !expr->asSubscript()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - return expr; -} - -IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr) -{ - if (IR::Const *c = expr->asConst()) { - if (c->type == IR::NumberType) { - switch (op) { - case IR::OpNot: - return _block->CONST(IR::BoolType, !c->value); - case IR::OpUMinus: - return _block->CONST(IR::NumberType, -c->value); - case IR::OpUPlus: - return expr; - case IR::OpCompl: - return _block->CONST(IR::NumberType, ~VM::Value::toInt32(c->value)); - case IR::OpIncrement: - return _block->CONST(IR::NumberType, c->value + 1); - case IR::OpDecrement: - return _block->CONST(IR::NumberType, c->value - 1); - default: - break; - } - } - } - if (! expr->asTemp()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - assert(expr->asTemp()); - return _block->UNOP(op, expr->asTemp()); -} - -IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) -{ - if (IR::Const *c1 = left->asConst()) { - if (IR::Const *c2 = right->asConst()) { - if (c1->type == IR::NumberType && c2->type == IR::NumberType) { - switch (op) { - case IR::OpAdd: return _block->CONST(IR::NumberType, c1->value + c2->value); - case IR::OpAnd: return _block->CONST(IR::BoolType, c1->value ? c2->value : 0); - case IR::OpBitAnd: return _block->CONST(IR::NumberType, int(c1->value) & int(c2->value)); - case IR::OpBitOr: return _block->CONST(IR::NumberType, int(c1->value) | int(c2->value)); - case IR::OpBitXor: return _block->CONST(IR::NumberType, int(c1->value) ^ int(c2->value)); - case IR::OpDiv: return _block->CONST(IR::NumberType, c1->value / c2->value); - case IR::OpEqual: return _block->CONST(IR::BoolType, c1->value == c2->value); - case IR::OpNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value); - case IR::OpStrictEqual: return _block->CONST(IR::BoolType, c1->value == c2->value); - case IR::OpStrictNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value); - case IR::OpGe: return _block->CONST(IR::BoolType, c1->value >= c2->value); - case IR::OpGt: return _block->CONST(IR::BoolType, c1->value > c2->value); - case IR::OpLe: return _block->CONST(IR::BoolType, c1->value <= c2->value); - case IR::OpLt: return _block->CONST(IR::BoolType, c1->value < c2->value); - case IR::OpLShift: return _block->CONST(IR::NumberType, VM::Value::toInt32(c1->value) << (VM::Value::toUInt32(c2->value) & 0x1f)); - case IR::OpMod: return _block->CONST(IR::NumberType, ::fmod(c1->value, c2->value)); - case IR::OpMul: return _block->CONST(IR::NumberType, c1->value * c2->value); - case IR::OpOr: return _block->CONST(IR::NumberType, c1->value ? c1->value : c2->value); - case IR::OpRShift: return _block->CONST(IR::NumberType, VM::Value::toInt32(c1->value) >> (VM::Value::toUInt32(c2->value) & 0x1f)); - case IR::OpSub: return _block->CONST(IR::NumberType, c1->value - c2->value); - case IR::OpURShift: return _block->CONST(IR::NumberType,VM::Value::toUInt32(c1->value) >> (VM::Value::toUInt32(c2->value) & 0x1f)); - - case IR::OpInstanceof: - case IR::OpIn: - assert(!"unreachabe"); - break; - - case IR::OpIfTrue: // unary ops - case IR::OpNot: - case IR::OpUMinus: - case IR::OpUPlus: - case IR::OpCompl: - case IR::OpIncrement: - case IR::OpDecrement: - case IR::OpInvalid: - break; - } - } - } - } else if (op == IR::OpAdd) { - if (IR::String *s1 = left->asString()) { - if (IR::String *s2 = right->asString()) { - return _block->STRING(_function->newString(*s1->value + *s2->value)); - } - } - } - - if (!left->asTemp() && !left->asConst()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), left); - left = _block->TEMP(t); - } - - if (!right->asTemp() && !right->asConst()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), right); - right = _block->TEMP(t); - } - - assert(left->asTemp() || left->asConst()); - assert(right->asTemp() || right->asConst()); - - return _block->BINOP(op, left, right); -} - -IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args) -{ - base = reference(base); - return _block->CALL(base, args); -} - -void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) -{ - assert(target->isLValue()); - - if (!source->asTemp() && !source->asConst() && (op != IR::OpInvalid || ! target->asTemp())) { - unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), source); - source = _block->TEMP(t); - } - - _block->MOVE(target, source, op); -} - -void Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) -{ - if (! (cond->asTemp() || cond->asBinop())) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), cond); - cond = _block->TEMP(t); - } - _block->CJUMP(cond, iftrue, iffalse); -} - -void Codegen::accept(Node *node) -{ - if (node) - node->accept(this); -} - -void Codegen::statement(Statement *ast) -{ - accept(ast); -} - -void Codegen::statement(ExpressionNode *ast) -{ - if (! ast) { - return; - } else { - Result r(nx); - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); - if (r.format == ex) { - if (r->asCall()) { - _block->EXP(*r); // the nest nx representation for calls is EXP(CALL(c..)) - } else if (r->asTemp()) { - // there is nothing to do - } else { - unsigned t = _block->newTemp(); - move(_block->TEMP(t), *r); - } - } - } -} - -void Codegen::condition(ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) -{ - if (ast) { - Result r(iftrue, iffalse); - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); - if (r.format == ex) { - cjump(*r, r.iftrue, r.iffalse); - } - } -} - -Codegen::Result Codegen::expression(ExpressionNode *ast) -{ - Result r; - if (ast) { - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); - } - return r; -} - -QString Codegen::propertyName(PropertyName *ast) -{ - QString p; - if (ast) { - qSwap(_property, p); - accept(ast); - qSwap(_property, p); - } - return p; -} - -Codegen::Result Codegen::sourceElement(SourceElement *ast) -{ - Result r(nx); - if (ast) { - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); - } - return r; -} - -Codegen::UiMember Codegen::uiObjectMember(UiObjectMember *ast) -{ - UiMember m; - if (ast) { - qSwap(_uiMember, m); - accept(ast); - qSwap(_uiMember, m); - } - return m; -} - -void Codegen::functionBody(FunctionBody *ast) -{ - if (ast) - sourceElements(ast->elements); -} - -void Codegen::program(Program *ast) -{ - if (ast) { - sourceElements(ast->elements); - } -} - -void Codegen::sourceElements(SourceElements *ast) -{ - for (SourceElements *it = ast; it; it = it->next) { - sourceElement(it->element); - } -} - -void Codegen::variableDeclaration(VariableDeclaration *ast) -{ - IR::Expr *initializer = 0; - if (!ast->expression) - return; - Result expr = expression(ast->expression); - assert(expr.code); - initializer = *expr; - - if (! _env->parent || _function->insideWith) { - // it's global code. - move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), initializer); - } else { - const int index = _env->findMember(ast->name.toString()); - assert(index != -1); - move(_block->TEMP(index), initializer); - } -} - -void Codegen::variableDeclarationList(VariableDeclarationList *ast) -{ - for (VariableDeclarationList *it = ast; it; it = it->next) { - variableDeclaration(it->declaration); - } -} - - -bool Codegen::visit(ArgumentList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(CaseBlock *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(CaseClause *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(CaseClauses *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(Catch *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(DefaultClause *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(ElementList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(Elision *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(Finally *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(FormalParameterList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(FunctionBody *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(Program *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(PropertyAssignmentList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(PropertyNameAndValue *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(PropertyGetterSetter *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(SourceElements *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(StatementList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiArrayMemberList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiImport *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiImportList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiObjectInitializer *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiObjectMemberList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiParameterList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiProgram *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiQualifiedId *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(VariableDeclaration *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(VariableDeclarationList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(Expression *ast) -{ - statement(ast->left); - accept(ast->right); - return false; -} - -bool Codegen::visit(ArrayLiteral *ast) -{ - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Array"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); - int index = 0; - unsigned value = 0; - for (ElementList *it = ast->elements; it; it = it->next) { - for (Elision *elision = it->elision; elision; elision = elision->next) - ++index; - Result expr = expression(it->expression); - - IR::ExprList *args = _function->New(); - IR::ExprList *current = args; - current->expr = _block->TEMP(t); - current->next = _function->New(); - current = current->next; - current->expr = _block->CONST(IR::NumberType, index); - current->next = _function->New(); - current = current->next; - - if (!value) - value = _block->newTemp(); - move(_block->TEMP(value), *expr); - // __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) - current->expr = _block->TEMP(value); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_define_array_property, 0, 0), args)); - - ++index; - } - if (ast->elision) { - for (Elision *elision = ast->elision->next; elision; elision = elision->next) - ++index; - // ### the new string leaks - move(member(_block->TEMP(t), _function->newString(QStringLiteral("length"))), _block->CONST(IR::NumberType, index + 1)); - } - _expr.code = _block->TEMP(t); - return false; -} - -bool Codegen::visit(ArrayMemberExpression *ast) -{ - Result base = expression(ast->base); - Result index = expression(ast->expression); - _expr.code = subscript(*base, *index); - return false; -} - -static IR::AluOp baseOp(int op) -{ - switch ((QSOperator::Op) op) { - case QSOperator::InplaceAnd: return IR::OpBitAnd; - case QSOperator::InplaceSub: return IR::OpSub; - case QSOperator::InplaceDiv: return IR::OpDiv; - case QSOperator::InplaceAdd: return IR::OpAdd; - case QSOperator::InplaceLeftShift: return IR::OpLShift; - case QSOperator::InplaceMod: return IR::OpMod; - case QSOperator::InplaceMul: return IR::OpMul; - case QSOperator::InplaceOr: return IR::OpBitOr; - case QSOperator::InplaceRightShift: return IR::OpRShift; - case QSOperator::InplaceURightShift: return IR::OpURShift; - case QSOperator::InplaceXor: return IR::OpBitXor; - default: return IR::OpInvalid; - } -} - -bool Codegen::visit(BinaryExpression *ast) -{ - if (ast->op == QSOperator::And) { - if (_expr.accept(cx)) { - IR::BasicBlock *iftrue = _function->newBasicBlock(); - condition(ast->left, iftrue, _expr.iffalse); - _block = iftrue; - condition(ast->right, _expr.iftrue, _expr.iffalse); - } else { - IR::BasicBlock *iftrue = _function->newBasicBlock(); - IR::BasicBlock *endif = _function->newBasicBlock(); - - const unsigned r = _block->newTemp(); - - move(_block->TEMP(r), *expression(ast->left)); - cjump(_block->TEMP(r), iftrue, endif); - _block = iftrue; - move(_block->TEMP(r), *expression(ast->right)); - _block->JUMP(endif); - - _expr.code = _block->TEMP(r); - _block = endif; - } - return false; - } else if (ast->op == QSOperator::Or) { - if (_expr.accept(cx)) { - IR::BasicBlock *iffalse = _function->newBasicBlock(); - condition(ast->left, _expr.iftrue, iffalse); - _block = iffalse; - condition(ast->right, _expr.iftrue, _expr.iffalse); - } else { - IR::BasicBlock *iffalse = _function->newBasicBlock(); - IR::BasicBlock *endif = _function->newBasicBlock(); - - const unsigned r = _block->newTemp(); - move(_block->TEMP(r), *expression(ast->left)); - cjump(_block->TEMP(r), endif, iffalse); - _block = iffalse; - move(_block->TEMP(r), *expression(ast->right)); - _block->JUMP(endif); - - _block = endif; - _expr.code = _block->TEMP(r); - } - return false; - } - - IR::Expr* left = *expression(ast->left); - throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()); - - switch (ast->op) { - case QSOperator::Or: - case QSOperator::And: - break; - - case QSOperator::Assign: { - IR::Expr* right = *expression(ast->right); - if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) - throwReferenceError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of assignment operator is not an lvalue")); - - if (_expr.accept(nx)) { - move(left, right); - } else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), right); - move(left, _block->TEMP(t)); - _expr.code = left; - } - break; - } - - case QSOperator::InplaceAnd: - case QSOperator::InplaceSub: - case QSOperator::InplaceDiv: - case QSOperator::InplaceAdd: - case QSOperator::InplaceLeftShift: - case QSOperator::InplaceMod: - case QSOperator::InplaceMul: - case QSOperator::InplaceOr: - case QSOperator::InplaceRightShift: - case QSOperator::InplaceURightShift: - case QSOperator::InplaceXor: { - IR::Expr* right = *expression(ast->right); - if (!left->isLValue()) - throwSyntaxError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of inplace operator is not an lvalue")); - - if (_expr.accept(nx)) { - move(left, right, baseOp(ast->op)); - } else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), right); - move(left, _block->TEMP(t), baseOp(ast->op)); - _expr.code = left; - } - break; - } - - case QSOperator::In: - case QSOperator::InstanceOf: - case QSOperator::Equal: - case QSOperator::NotEqual: - case QSOperator::Ge: - case QSOperator::Gt: - case QSOperator::Le: - case QSOperator::Lt: - case QSOperator::StrictEqual: - case QSOperator::StrictNotEqual: { - if (!left->asTemp() && !left->asConst()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), left); - left = _block->TEMP(t); - } - - IR::Expr* right = *expression(ast->right); - - if (_expr.accept(cx)) { - cjump(binop(IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse); - } else { - IR::Expr *e = binop(IR::binaryOperator(ast->op), left, right); - if (e->asConst() || e->asString()) - _expr.code = e; - else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), e); - _expr.code = _block->TEMP(t); - } - } - break; - } - - case QSOperator::Add: - case QSOperator::BitAnd: - case QSOperator::BitOr: - case QSOperator::BitXor: - case QSOperator::Div: - case QSOperator::LShift: - case QSOperator::Mod: - case QSOperator::Mul: - case QSOperator::RShift: - case QSOperator::Sub: - case QSOperator::URShift: { - if (!left->asTemp() && !left->asConst()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), left); - left = _block->TEMP(t); - } - - IR::Expr* right = *expression(ast->right); - - IR::Expr *e = binop(IR::binaryOperator(ast->op), left, right); - if (e->asConst() || e->asString()) - _expr.code = e; - else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), e); - _expr.code = _block->TEMP(t); - } - break; - } - - } // switch - - return false; -} - -bool Codegen::visit(CallExpression *ast) -{ - Result base = expression(ast->base); - IR::ExprList *args = 0, **args_it = &args; - for (ArgumentList *it = ast->arguments; it; it = it->next) { - Result arg = expression(it->expression); - IR::Expr *actual = argument(*arg); - *args_it = _function->New(); - (*args_it)->init(actual); - args_it = &(*args_it)->next; - } - _expr.code = call(*base, args); - return false; -} - -bool Codegen::visit(ConditionalExpression *ast) -{ - IR::BasicBlock *iftrue = _function->newBasicBlock(); - IR::BasicBlock *iffalse = _function->newBasicBlock(); - IR::BasicBlock *endif = _function->newBasicBlock(); - - const unsigned t = _block->newTemp(); - - condition(ast->expression, iftrue, iffalse); - - _block = iftrue; - move(_block->TEMP(t), *expression(ast->ok)); - _block->JUMP(endif); - - _block = iffalse; - move(_block->TEMP(t), *expression(ast->ko)); - _block->JUMP(endif); - - _block = endif; - - _expr.code = _block->TEMP(t); - - return false; -} - -bool Codegen::visit(DeleteExpression *ast) -{ - IR::Expr* expr = *expression(ast->expression); - // Temporaries cannot be deleted - if (expr->asTemp() && expr->asTemp()->index < _env->members.size()) { - // Trying to delete a function argument might throw. - if (_function->isStrict && expr->asTemp()->index < 0) - throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); - _expr.code = _block->CONST(IR::BoolType, 0); - return false; - } - if (_function->isStrict && expr->asName()) - throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); - - // [[11.4.1]] Return true if it's not a reference - if (expr->asConst() || expr->asString()) { - _expr.code = _block->CONST(IR::BoolType, 1); - return false; - } - - // Return values from calls are also not a reference, but we have to - // perform the call to allow for side effects. - if (expr->asCall()) { - _block->EXP(expr); - _expr.code = _block->CONST(IR::BoolType, 1); - return false; - } - if (expr->asTemp() && expr->asTemp()->index >= _env->members.size()) { - _expr.code = _block->CONST(IR::BoolType, 1); - return false; - } - - IR::ExprList *args = _function->New(); - args->init(reference(expr)); - _expr.code = call(_block->NAME(IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args); - return false; -} - -bool Codegen::visit(FalseLiteral *) -{ - if (_expr.accept(cx)) { - _block->JUMP(_expr.iffalse); - } else { - _expr.code = _block->CONST(IR::BoolType, 0); - } - return false; -} - -bool Codegen::visit(FieldMemberExpression *ast) -{ - Result base = expression(ast->base); - _expr.code = member(*base, _function->newString(ast->name.toString())); - return false; -} - -bool Codegen::visit(FunctionExpression *ast) -{ - IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); - if (_debugger) - _debugger->setSourceLocation(function, ast->functionToken.startLine, ast->functionToken.startColumn); - _expr.code = _block->CLOSURE(function); - return false; -} - -IR::Expr *Codegen::identifier(const QString &name, int line, int col) -{ - int index = _env->findMember(name); - - if (! _function->hasDirectEval && !_function->insideWith && _env->parent) { - if (index != -1) { - return _block->TEMP(index); - } - index = indexOfArgument(&name); - if (index != -1) { - return _block->TEMP(-(index + 1)); - } - } - - if (index >= _env->members.size()) { - // named local variable, e.g. in a catch statement - return _block->TEMP(index); - } - - return _block->NAME(name, line, col); - -} - -bool Codegen::visit(IdentifierExpression *ast) -{ - _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn); - return false; -} - -bool Codegen::visit(NestedExpression *ast) -{ - accept(ast->expression); - return false; -} - -bool Codegen::visit(NewExpression *ast) -{ - Result base = expression(ast->expression); - IR::Expr *expr = *base; - if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - _expr.code = _block->NEW(expr, 0); - return false; -} - -bool Codegen::visit(NewMemberExpression *ast) -{ - Result base = expression(ast->base); - IR::Expr *expr = *base; - if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - - IR::ExprList *args = 0, **args_it = &args; - for (ArgumentList *it = ast->arguments; it; it = it->next) { - Result arg = expression(it->expression); - IR::Expr *actual = argument(*arg); - *args_it = _function->New(); - (*args_it)->init(actual); - args_it = &(*args_it)->next; - } - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), _block->NEW(expr, args)); - _expr.code = _block->TEMP(t); - return false; -} - -bool Codegen::visit(NotExpression *ast) -{ - Result expr = expression(ast->expression); - const unsigned r = _block->newTemp(); - move(_block->TEMP(r), unop(IR::OpNot, *expr)); - _expr.code = _block->TEMP(r); - return false; -} - -bool Codegen::visit(NullExpression *) -{ - if (_expr.accept(cx)) _block->JUMP(_expr.iffalse); - else _expr.code = _block->CONST(IR::NullType, 0); - - return false; -} - -bool Codegen::visit(NumericLiteral *ast) -{ - if (_expr.accept(cx)) { - if (ast->value) _block->JUMP(_expr.iftrue); - else _block->JUMP(_expr.iffalse); - } else { - _expr.code = _block->CONST(IR::NumberType, ast->value); - } - return false; -} - -struct ObjectPropertyValue { - IR::Expr *value; - IR::Function *getter; - IR::Function *setter; -}; - -bool Codegen::visit(ObjectLiteral *ast) -{ - QMap valueMap; - - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Object"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); - for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { - if (PropertyNameAndValue *nv = AST::cast(it->assignment)) { - QString name = propertyName(nv->name); - Result value = expression(nv->value); - ObjectPropertyValue &v = valueMap[name]; - if (v.getter || v.setter || (_function->isStrict && v.value)) - throwSyntaxError(nv->lastSourceLocation(), - QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); - - valueMap[name].value = *value; - } else if (PropertyGetterSetter *gs = AST::cast(it->assignment)) { - QString name = propertyName(gs->name); - IR::Function *function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0); - if (_debugger) - _debugger->setSourceLocation(function, gs->getSetToken.startLine, gs->getSetToken.startColumn); - ObjectPropertyValue &v = valueMap[name]; - if (v.value || - (gs->type == PropertyGetterSetter::Getter && v.getter) || - (gs->type == PropertyGetterSetter::Setter && v.setter)) - throwSyntaxError(gs->lastSourceLocation(), - QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); - if (gs->type == PropertyGetterSetter::Getter) - v.getter = function; - else - v.setter = function; - } else { - Q_UNREACHABLE(); - } - } - if (!valueMap.isEmpty()) { - unsigned value = 0; - unsigned getter = 0; - unsigned setter = 0; - for (QMap::const_iterator it = valueMap.constBegin(); it != valueMap.constEnd(); ++it) { - IR::ExprList *args = _function->New(); - IR::ExprList *current = args; - current->expr = _block->TEMP(t); - current->next = _function->New(); - current = current->next; - current->expr = _block->NAME(it.key(), 0, 0); - current->next = _function->New(); - current = current->next; - - if (it->value) { - if (!value) - value = _block->newTemp(); - move(_block->TEMP(value), it->value); - // __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) - current->expr = _block->TEMP(value); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_define_property, 0, 0), args)); - } else { - if (!getter) { - getter = _block->newTemp(); - setter = _block->newTemp(); - } - move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(IR::UndefinedType, 0)); - move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(IR::UndefinedType, 0)); - - - // __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); - current->expr = _block->TEMP(getter); - current->next = _function->New(); - current = current->next; - current->expr = _block->TEMP(setter); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_define_getter_setter, 0, 0), args)); - } - } - } - - _expr.code = _block->TEMP(t); - return false; -} - -bool Codegen::visit(PostDecrementExpression *ast) -{ - Result expr = expression(ast->base); - if (!expr->isLValue()) - throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); - throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); - - IR::ExprList *args = _function->New(); - args->init(*expr); - _expr.code = call(_block->NAME(IR::Name::builtin_postdecrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); - return false; -} - -bool Codegen::visit(PostIncrementExpression *ast) -{ - Result expr = expression(ast->base); - if (!expr->isLValue()) - throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); - throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); - - IR::ExprList *args = _function->New(); - args->init(*expr); - _expr.code = call(_block->NAME(IR::Name::builtin_postincrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); - return false; -} - -bool Codegen::visit(PreDecrementExpression *ast) -{ - Result expr = expression(ast->expression); - throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); - move(*expr, unop(IR::OpDecrement, *expr)); - if (_expr.accept(nx)) { - // nothing to do - } else { - _expr.code = *expr; - } - return false; -} - -bool Codegen::visit(PreIncrementExpression *ast) -{ - Result expr = expression(ast->expression); - throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); - move(*expr, unop(IR::OpIncrement, *expr)); - if (_expr.accept(nx)) { - // nothing to do - } else { - _expr.code = *expr; - } - return false; -} - -bool Codegen::visit(RegExpLiteral *ast) -{ - _expr.code = _block->REGEXP(_function->newString(ast->pattern.toString()), ast->flags); - return false; -} - -bool Codegen::visit(StringLiteral *ast) -{ - _expr.code = _block->STRING(_function->newString(ast->value.toString())); - return false; -} - -bool Codegen::visit(ThisExpression *ast) -{ - _expr.code = _block->NAME(QStringLiteral("this"), ast->thisToken.startLine, ast->thisToken.startColumn); - return false; -} - -bool Codegen::visit(TildeExpression *ast) -{ - Result expr = expression(ast->expression); - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), unop(IR::OpCompl, *expr)); - _expr.code = _block->TEMP(t); - return false; -} - -bool Codegen::visit(TrueLiteral *) -{ - if (_expr.accept(cx)) { - _block->JUMP(_expr.iftrue); - } else { - _expr.code = _block->CONST(IR::BoolType, 1); - } - return false; -} - -bool Codegen::visit(TypeOfExpression *ast) -{ - Result expr = expression(ast->expression); - IR::ExprList *args = _function->New(); - args->init(reference(*expr)); - _expr.code = call(_block->NAME(IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args); - return false; -} - -bool Codegen::visit(UnaryMinusExpression *ast) -{ - Result expr = expression(ast->expression); - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), unop(IR::OpUMinus, *expr)); - _expr.code = _block->TEMP(t); - return false; -} - -bool Codegen::visit(UnaryPlusExpression *ast) -{ - Result expr = expression(ast->expression); - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), unop(IR::OpUPlus, *expr)); - _expr.code = _block->TEMP(t); - return false; -} - -bool Codegen::visit(VoidExpression *ast) -{ - statement(ast->expression); - _expr.code = _block->CONST(IR::UndefinedType, 0); - return false; -} - -bool Codegen::visit(FunctionDeclaration * /*ast*/) -{ - _expr.accept(nx); - return false; -} - -void Codegen::linearize(IR::Function *function) -{ - IR::BasicBlock *exitBlock = function->basicBlocks.last(); - assert(exitBlock->isTerminated()); - assert(exitBlock->terminator()->asRet()); - - QSet V; - V.insert(exitBlock); - - QVector trace; - - for (int i = 0; i < function->basicBlocks.size(); ++i) { - IR::BasicBlock *block = function->basicBlocks.at(i); - if (!block->isTerminated() && (i + 1) < function->basicBlocks.size()) { - IR::BasicBlock *next = function->basicBlocks.at(i + 1); - block->JUMP(next); - } - } - - struct I { static void trace(IR::BasicBlock *block, QSet *V, - QVector *output) { - if (block == 0 || V->contains(block)) - return; - - V->insert(block); - block->index = output->size(); - output->append(block); - - if (IR::Stmt *term = block->terminator()) { - if (IR::Jump *j = term->asJump()) { - trace(j->target, V, output); - } else if (IR::CJump *cj = term->asCJump()) { - if (! V->contains(cj->iffalse)) - trace(cj->iffalse, V, output); - else - trace(cj->iftrue, V, output); - } - } - - // We could do this for each type above, but it is safer to have a - // "catchall" here - for (int ii = 0; ii < block->out.count(); ++ii) - trace(block->out.at(ii), V, output); - } - }; - - I::trace(function->basicBlocks.first(), &V, &trace); - - V.insert(exitBlock); - exitBlock->index = trace.size(); - trace.append(exitBlock); - - QVarLengthArray blocksToDelete; - foreach (IR::BasicBlock *b, function->basicBlocks) - if (!V.contains(b)) { - foreach (IR::BasicBlock *out, b->out) { - int idx = out->in.indexOf(b); - if (idx >= 0) - out->in.remove(idx); - } - blocksToDelete.append(b); - } - qDeleteAll(blocksToDelete); - function->basicBlocks = trace; - -#ifndef QV4_NO_LIVENESS - liveness(function); -#endif - - static bool showCode = !qgetenv("SHOW_CODE").isNull(); - if (showCode) { - QVector code; - QHash leader; - - foreach (IR::BasicBlock *block, function->basicBlocks) { - leader.insert(block->statements.first(), block); - foreach (IR::Stmt *s, block->statements) { - code.append(s); - } - } - - QString name; - if (function->name && !function->name->isEmpty()) - name = *function->name; - else - name.sprintf("%p", function); - - qout << "function " << name << "("; - for (int i = 0; i < function->formals.size(); ++i) { - if (i != 0) - qout << ", "; - qout << *function->formals.at(i); - } - qout << ")" << endl - << "{" << endl; - - foreach (const QString *local, function->locals) { - qout << " var " << *local << ';' << endl; - } - - for (int i = 0; i < code.size(); ++i) { - IR::Stmt *s = code.at(i); - - if (IR::BasicBlock *bb = leader.value(s)) { - qout << endl; - QByteArray str; - str.append('L'); - str.append(QByteArray::number(bb->index)); - str.append(':'); - for (int i = 66 - str.length(); i; --i) - str.append(' '); - qout << str; - qout << "// predecessor blocks:"; - foreach (IR::BasicBlock *in, bb->in) - qout << " L" << in->index; - qout << endl; - } - IR::Stmt *n = (i + 1) < code.size() ? code.at(i + 1) : 0; - if (n && s->asJump() && s->asJump()->target == leader.value(n)) { - continue; - } - - QByteArray str; - QBuffer buf(&str); - buf.open(QIODevice::WriteOnly); - QTextStream out(&buf); - s->dump(out, IR::Stmt::MIR); - out.flush(); - -#ifndef QV4_NO_LIVENESS - for (int i = 60 - str.size(); i >= 0; --i) - str.append(' '); - - qout << " " << str; - - // if (! s->uses.isEmpty()) { - // qout << " // uses:"; - // foreach (unsigned use, s->uses) { - // qout << " %" << use; - // } - // } - - // if (! s->defs.isEmpty()) { - // qout << " // defs:"; - // foreach (unsigned def, s->defs) { - // qout << " %" << def; - // } - // } - - if (! s->d->liveOut.isEmpty()) { - qout << " // lives out:"; - for (int i = 0; i < s->d->liveOut.size(); ++i) { - if (s->d->liveOut.testBit(i)) - qout << " %" << i; - } - } -#else - qout << " " << str; -#endif - - qout << endl; - - if (n && s->asCJump() && s->asCJump()->iffalse != leader.value(n)) { - qout << " goto L" << s->asCJump()->iffalse << ";" << endl; - } - } - - qout << "}" << endl - << endl; - } -} - -IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, - AST::FormalParameterList *formals, - AST::SourceElements *body, Mode mode, - const QStringList &inheritedLocals) -{ - qSwap(_mode, mode); // enter function code. - - ScopeAndFinally *scopeAndFinally = 0; - - enterEnvironment(ast); - IR::Function *function = _module->newFunction(name, _function); - - if (_debugger) - _debugger->addFunction(function); - IR::BasicBlock *entryBlock = function->newBasicBlock(); - IR::BasicBlock *exitBlock = function->newBasicBlock(IR::Function::DontInsertBlock); - IR::BasicBlock *throwBlock = function->newBasicBlock(); - function->hasDirectEval = _env->hasDirectEval; - function->usesArgumentsObject = (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed); - function->maxNumberOfArguments = _env->maxNumberOfArguments; - function->isStrict = _env->isStrict; - - // variables in global code are properties of the global context object, not locals as with other functions. - if (_mode == FunctionCode) { - for (Environment::MemberMap::iterator it = _env->members.begin(); it != _env->members.end(); ++it) { - const QString &local = it.key(); - function->LOCAL(local); - unsigned t = entryBlock->newTemp(); - (*it).index = t; - } - } else { - if (!_env->isStrict) { - foreach (const QString &inheritedLocal, inheritedLocals) { - function->LOCAL(inheritedLocal); - unsigned tempIndex = entryBlock->newTemp(); - Environment::Member member = { Environment::UndefinedMember, tempIndex, 0 }; - _env->members.insert(inheritedLocal, member); - } - } - - IR::ExprList *args = 0; - for (Environment::MemberMap::const_iterator it = _env->members.constBegin(); it != _env->members.constEnd(); ++it) { - const QString &local = it.key(); - IR::ExprList *next = function->New(); - next->expr = entryBlock->NAME(local, 0, 0); - next->next = args; - args = next; - } - if (args) { - IR::ExprList *next = function->New(); - next->expr = entryBlock->CONST(IR::BoolType, mode == EvalCode); - next->next = args; - args = next; - - entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(IR::Name::builtin_declare_vars, 0, 0), args)); - } - } - - unsigned returnAddress = entryBlock->newTemp(); - - entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(IR::UndefinedType, 0)); - exitBlock->RET(exitBlock->TEMP(returnAddress)); - IR::ExprList *throwArgs = function->New(); - throwArgs->expr = throwBlock->TEMP(returnAddress); - throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); - Loop *loop = 0; - - qSwap(_function, function); - qSwap(_block, entryBlock); - qSwap(_exitBlock, exitBlock); - qSwap(_throwBlock, throwBlock); - qSwap(_returnAddress, returnAddress); - qSwap(_scopeAndFinally, scopeAndFinally); - qSwap(_loop, loop); - - for (FormalParameterList *it = formals; it; it = it->next) { - _function->RECEIVE(it->name.toString()); - } - - foreach (const Environment::Member &member, _env->members) { - if (member.function) { - IR::Function *function = defineFunction(member.function->name.toString(), member.function, member.function->formals, - member.function->body ? member.function->body->elements : 0); - if (_debugger) - _debugger->setSourceLocation(function, member.function->functionToken.startLine, member.function->functionToken.startColumn); - if (! _env->parent) { - move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), - _block->CLOSURE(function)); - } else { - assert(member.index >= 0); - move(_block->TEMP(member.index), _block->CLOSURE(function)); - } - } - } - - sourceElements(body); - - _function->insertBasicBlock(_exitBlock); - - _block->JUMP(_exitBlock); - - qSwap(_function, function); - qSwap(_block, entryBlock); - qSwap(_exitBlock, exitBlock); - qSwap(_throwBlock, throwBlock); - qSwap(_returnAddress, returnAddress); - qSwap(_scopeAndFinally, scopeAndFinally); - qSwap(_loop, loop); - - leaveEnvironment(); - - qSwap(_mode, mode); - - return function; -} - -int Codegen::indexOfArgument(const QStringRef &string) const -{ - for (int i = _function->formals.size() - 1; i >= 0; --i) { - if (*_function->formals.at(i) == string) - return i; - } - return -1; -} - -bool Codegen::visit(IdentifierPropertyName *ast) -{ - _property = ast->id.toString(); - return false; -} - -bool Codegen::visit(NumericLiteralPropertyName *ast) -{ - _property = QString::number(ast->id, 'g', 16); - return false; -} - -bool Codegen::visit(StringLiteralPropertyName *ast) -{ - _property = ast->id.toString(); - return false; -} - -bool Codegen::visit(FunctionSourceElement *ast) -{ - statement(ast->declaration); - return false; -} - -bool Codegen::visit(StatementSourceElement *ast) -{ - statement(ast->statement); - return false; -} - -bool Codegen::visit(Block *ast) -{ - for (StatementList *it = ast->statements; it; it = it->next) { - statement(it->statement); - } - return false; -} - -bool Codegen::visit(BreakStatement *ast) -{ - if (!_loop) - throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Break outside of loop")); - Loop *loop = 0; - if (ast->label.isEmpty()) - loop = _loop; - else { - for (loop = _loop; loop; loop = loop->parent) { - if (loop->labelledStatement && loop->labelledStatement->label == ast->label) - break; - } - if (!loop) - throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); - } - unwindException(loop->scopeAndFinally); - _block->JUMP(loop->breakBlock); - return false; -} - -bool Codegen::visit(ContinueStatement *ast) -{ - Loop *loop = 0; - if (ast->label.isEmpty()) { - for (loop = _loop; loop; loop = loop->parent) { - if (loop->continueBlock) - break; - } - } else { - for (loop = _loop; loop; loop = loop->parent) { - if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { - if (!loop->continueBlock) - loop = 0; - break; - } - } - if (!loop) - throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); - } - if (!loop) - throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "continue outside of loop")); - unwindException(loop->scopeAndFinally); - _block->JUMP(loop->continueBlock); - return false; -} - -bool Codegen::visit(DebuggerStatement *) -{ - assert(!"not implemented"); - return false; -} - -bool Codegen::visit(DoWhileStatement *ast) -{ - IR::BasicBlock *loopbody = _function->newBasicBlock(); - IR::BasicBlock *loopcond = _function->newBasicBlock(); - IR::BasicBlock *loopend = _function->newBasicBlock(); - - enterLoop(ast, loopend, loopcond); - - _block->JUMP(loopbody); - - _block = loopbody; - statement(ast->statement); - _block->JUMP(loopcond); - - _block = loopcond; - condition(ast->expression, loopbody, loopend); - - _block = loopend; - - leaveLoop(); - - return false; -} - -bool Codegen::visit(EmptyStatement *) -{ - return false; -} - -bool Codegen::visit(ExpressionStatement *ast) -{ - if (_mode == EvalCode) { - Result e = expression(ast->expression); - if (*e) - move(_block->TEMP(_returnAddress), *e); - } else { - statement(ast->expression); - } - return false; -} - -bool Codegen::visit(ForEachStatement *ast) -{ - IR::BasicBlock *foreachin = _function->newBasicBlock(); - IR::BasicBlock *foreachbody = _function->newBasicBlock(); - IR::BasicBlock *foreachend = _function->newBasicBlock(); - - enterLoop(ast, foreachend, foreachin); - - int iterator = _block->newTemp(); - move(_block->TEMP(iterator), *expression(ast->expression)); - IR::ExprList *args = _function->New(); - args->init(_block->TEMP(iterator)); - move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args)); - - _block->JUMP(foreachin); - - _block = foreachbody; - int temp = _block->newTemp(); - move(*expression(ast->initialiser), _block->TEMP(temp)); - statement(ast->statement); - _block->JUMP(foreachin); - - _block = foreachin; - - args = _function->New(); - args->init(_block->TEMP(iterator)); - move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args)); - int null = _block->newTemp(); - move(_block->TEMP(null), _block->CONST(IR::NullType, 0)); - cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); - _block = foreachend; - - leaveLoop(); - return false; -} - -bool Codegen::visit(ForStatement *ast) -{ - IR::BasicBlock *forcond = _function->newBasicBlock(); - IR::BasicBlock *forbody = _function->newBasicBlock(); - IR::BasicBlock *forstep = _function->newBasicBlock(); - IR::BasicBlock *forend = _function->newBasicBlock(); - - enterLoop(ast, forend, forstep); - - statement(ast->initialiser); - _block->JUMP(forcond); - - _block = forcond; - condition(ast->condition, forbody, forend); - - _block = forbody; - statement(ast->statement); - _block->JUMP(forstep); - - _block = forstep; - statement(ast->expression); - _block->JUMP(forcond); - _block = forend; - - leaveLoop(); - - return false; -} - -bool Codegen::visit(IfStatement *ast) -{ - IR::BasicBlock *iftrue = _function->newBasicBlock(); - IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock() : 0; - IR::BasicBlock *endif = _function->newBasicBlock(); - condition(ast->expression, iftrue, ast->ko ? iffalse : endif); - - _block = iftrue; - statement(ast->ok); - _block->JUMP(endif); - - if (ast->ko) { - _block = iffalse; - statement(ast->ko); - _block->JUMP(endif); - } - - _block = endif; - - return false; -} - -bool Codegen::visit(LabelledStatement *ast) -{ - _labelledStatement = ast; - - if (AST::cast(ast->statement) || - AST::cast(ast->statement) || - AST::cast(ast->statement) || - AST::cast(ast->statement) || - AST::cast(ast->statement) || - AST::cast(ast->statement) || - AST::cast(ast->statement)) { - statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. - } else { - IR::BasicBlock *breakBlock = _function->newBasicBlock(); - enterLoop(ast->statement, breakBlock, /*continueBlock*/ 0); - statement(ast->statement); - _block->JUMP(breakBlock); - _block = breakBlock; - leaveLoop(); - } - - return false; -} - -bool Codegen::visit(LocalForEachStatement *ast) -{ - IR::BasicBlock *foreachin = _function->newBasicBlock(); - IR::BasicBlock *foreachbody = _function->newBasicBlock(); - IR::BasicBlock *foreachend = _function->newBasicBlock(); - - enterLoop(ast, foreachend, foreachin); - - variableDeclaration(ast->declaration); - - int iterator = _block->newTemp(); - move(_block->TEMP(iterator), *expression(ast->expression)); - IR::ExprList *args = _function->New(); - args->init(_block->TEMP(iterator)); - move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args)); - - _block->JUMP(foreachin); - - _block = foreachbody; - int temp = _block->newTemp(); - move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); - statement(ast->statement); - _block->JUMP(foreachin); - - _block = foreachin; - - args = _function->New(); - args->init(_block->TEMP(iterator)); - move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args)); - int null = _block->newTemp(); - move(_block->TEMP(null), _block->CONST(IR::NullType, 0)); - cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); - _block = foreachend; - - leaveLoop(); - return false; -} - -bool Codegen::visit(LocalForStatement *ast) -{ - IR::BasicBlock *forcond = _function->newBasicBlock(); - IR::BasicBlock *forbody = _function->newBasicBlock(); - IR::BasicBlock *forstep = _function->newBasicBlock(); - IR::BasicBlock *forend = _function->newBasicBlock(); - - enterLoop(ast, forend, forstep); - - variableDeclarationList(ast->declarations); - _block->JUMP(forcond); - - _block = forcond; - condition(ast->condition, forbody, forend); - - _block = forbody; - statement(ast->statement); - _block->JUMP(forstep); - - _block = forstep; - statement(ast->expression); - _block->JUMP(forcond); - _block = forend; - - leaveLoop(); - - return false; -} - -bool Codegen::visit(ReturnStatement *ast) -{ - if (_mode != FunctionCode) - throwSyntaxError(ast->returnToken, QCoreApplication::translate("qv4codegen", "Return statement outside of function")); - if (ast->expression) { - Result expr = expression(ast->expression); - move(_block->TEMP(_returnAddress), *expr); - } - unwindException(0); - - _block->JUMP(_exitBlock); - return false; -} - -bool Codegen::visit(SwitchStatement *ast) -{ - IR::BasicBlock *switchend = _function->newBasicBlock(); - - if (ast->block) { - Result lhs = expression(ast->expression); - IR::BasicBlock *switchcond = _block; - - QHash blockMap; - - enterLoop(ast, switchend, 0); - - for (CaseClauses *it = ast->block->clauses; it; it = it->next) { - CaseClause *clause = it->clause; - - _block = _function->newBasicBlock(); - blockMap[clause] = _block; - - for (StatementList *it2 = clause->statements; it2; it2 = it2->next) - statement(it2->statement); - } - - if (ast->block->defaultClause) { - _block = _function->newBasicBlock(); - blockMap[ast->block->defaultClause] = _block; - - for (StatementList *it2 = ast->block->defaultClause->statements; it2; it2 = it2->next) - statement(it2->statement); - } - - for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { - CaseClause *clause = it->clause; - - _block = _function->newBasicBlock(); - blockMap[clause] = _block; - - for (StatementList *it2 = clause->statements; it2; it2 = it2->next) - statement(it2->statement); - } - - leaveLoop(); - - _block->JUMP(switchend); - - _block = switchcond; - for (CaseClauses *it = ast->block->clauses; it; it = it->next) { - CaseClause *clause = it->clause; - Result rhs = expression(clause->expression); - IR::BasicBlock *iftrue = blockMap[clause]; - IR::BasicBlock *iffalse = _function->newBasicBlock(); - cjump(binop(IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); - _block = iffalse; - } - - for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { - CaseClause *clause = it->clause; - Result rhs = expression(clause->expression); - IR::BasicBlock *iftrue = blockMap[clause]; - IR::BasicBlock *iffalse = _function->newBasicBlock(); - cjump(binop(IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); - _block = iffalse; - } - - if (ast->block->defaultClause) { - _block->JUMP(blockMap[ast->block->defaultClause]); - } - } - - _block->JUMP(switchend); - - _block = switchend; - return false; -} - -bool Codegen::visit(ThrowStatement *ast) -{ - Result expr = expression(ast->expression); - move(_block->TEMP(_returnAddress), *expr); - _block->JUMP(_throwBlock); - return false; -} - -bool Codegen::visit(TryStatement *ast) -{ - if (_function->isStrict && ast->catchExpression && - (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) - throwSyntaxError(ast->catchExpression->identifierToken, QCoreApplication::translate("qv4codegen", "Catch variable name may not be eval or arguments in strict mode")); - - IR::BasicBlock *tryBody = _function->newBasicBlock(); - IR::BasicBlock *catchBody = ast->catchExpression ? _function->newBasicBlock() : 0; - // We always need a finally body to clean up the exception handler - IR::BasicBlock *finallyBody = _function->newBasicBlock(); - - int inCatch = 0; - if (catchBody) { - inCatch = _block->newTemp(); - move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, false)); - } - - int hasException = _block->newTemp(); - move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_create_exception_handler, 0, 0), 0)); - - // Pass the hidden "inCatch" and "hasException" TEMPs to the - // builtin_delete_exception_handler, in order to have those TEMPs alive for - // the duration of the exception handling block. - IR::ExprList *deleteExceptionArgs = _function->New(); - deleteExceptionArgs->init(_block->TEMP(hasException)); - if (inCatch) { - deleteExceptionArgs->next = _function->New(); - deleteExceptionArgs->next->init(_block->TEMP(inCatch)); - } - - ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression, deleteExceptionArgs); - _scopeAndFinally = &tcf; - - _block->CJUMP(_block->TEMP(hasException), catchBody ? catchBody : finallyBody, tryBody); - - _block = tryBody; - statement(ast->statement); - _block->JUMP(finallyBody); - - // regular flow does not go into the catch statement - if (catchBody) { - _block = catchBody; - - if (inCatch != 0) { - // check if an exception got thrown within catch. Go to finally - // and then rethrow - IR::BasicBlock *b = _function->newBasicBlock(); - _block->CJUMP(_block->TEMP(inCatch), finallyBody, b); - _block = b; - } - // if we have finally we need to clear the exception here, so we don't rethrow - move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, true)); - move(_block->TEMP(hasException), _block->CONST(IR::BoolType, false)); - - const int exception = _block->newTemp(); - move(_block->TEMP(exception), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0)); - - // the variable used in the catch statement is local and hides any global - // variable with the same name. - const Environment::Member undefinedMember = { Environment::UndefinedMember, -1 , 0 }; - const Environment::Member catchMember = { Environment::VariableDefinition, exception, 0 }; - Environment::Member m = _env->members.value(ast->catchExpression->name.toString(), undefinedMember); - _env->members.insert(ast->catchExpression->name.toString(), catchMember); - - statement(ast->catchExpression->statement); - - // reset the variable name to the one from the outer scope - if (m.type == Environment::UndefinedMember) - _env->members.remove(ast->catchExpression->name.toString()); - else - _env->members.insert(ast->catchExpression->name.toString(), m); - _block->JUMP(finallyBody); - } - - _scopeAndFinally = tcf.parent; - - IR::BasicBlock *after = _function->newBasicBlock(); - _block = finallyBody; - - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), deleteExceptionArgs)); - int exception_to_rethrow = _block->newTemp(); - move(_block->TEMP(exception_to_rethrow), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0)); - - if (ast->finallyExpression && ast->finallyExpression->statement) - statement(ast->finallyExpression->statement); - - IR::BasicBlock *rethrowBlock = _function->newBasicBlock(); - _block->CJUMP(_block->TEMP(hasException), rethrowBlock, after); - _block = rethrowBlock; - move(_block->TEMP(_returnAddress), _block->TEMP(exception_to_rethrow)); - _block->JUMP(_throwBlock); - - _block = after; - - return false; -} - -void Codegen::unwindException(Codegen::ScopeAndFinally *outest) -{ - ScopeAndFinally *scopeAndFinally = _scopeAndFinally; - qSwap(_scopeAndFinally, scopeAndFinally); - while (_scopeAndFinally != outest) { - if (_scopeAndFinally->popScope) { - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0))); - _scopeAndFinally = _scopeAndFinally->parent; - } else { - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), _scopeAndFinally->deleteExceptionArgs)); - ScopeAndFinally *tc = _scopeAndFinally; - _scopeAndFinally = tc->parent; - if (tc->finally && tc->finally->statement) - statement(tc->finally->statement); - } - } - qSwap(_scopeAndFinally, scopeAndFinally); -} - -bool Codegen::visit(VariableStatement *ast) -{ - variableDeclarationList(ast->declarations); - return false; -} - -bool Codegen::visit(WhileStatement *ast) -{ - IR::BasicBlock *whilecond = _function->newBasicBlock(); - IR::BasicBlock *whilebody = _function->newBasicBlock(); - IR::BasicBlock *whileend = _function->newBasicBlock(); - - enterLoop(ast, whileend, whilecond); - - _block->JUMP(whilecond); - _block = whilecond; - condition(ast->expression, whilebody, whileend); - - _block = whilebody; - statement(ast->statement); - _block->JUMP(whilecond); - - _block = whileend; - leaveLoop(); - - return false; -} - -bool Codegen::visit(WithStatement *ast) -{ - IR::BasicBlock *withBlock = _function->newBasicBlock(); - - _block->JUMP(withBlock); - _block = withBlock; - int withObject = _block->newTemp(); - _block->MOVE(_block->TEMP(withObject), *expression(ast->expression)); - IR::ExprList *args = _function->New(); - args->init(_block->TEMP(withObject)); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with_scope, 0, 0), args)); - - ++_function->insideWith; - { - ScopeAndFinally scope(_scopeAndFinally); - _scopeAndFinally = &scope; - statement(ast->statement); - _scopeAndFinally = scope.parent; - } - --_function->insideWith; - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); - - IR::BasicBlock *next = _function->newBasicBlock(); - _block->JUMP(next); - _block = next; - - return false; -} - -bool Codegen::visit(UiArrayBinding *) -{ - assert(!"not implemented"); - return false; -} - -bool Codegen::visit(UiObjectBinding *) -{ - assert(!"not implemented"); - return false; -} - -bool Codegen::visit(UiObjectDefinition *) -{ - assert(!"not implemented"); - return false; -} - -bool Codegen::visit(UiPublicMember *) -{ - assert(!"not implemented"); - return false; -} - -bool Codegen::visit(UiScriptBinding *) -{ - assert(!"not implemented"); - return false; -} - -bool Codegen::visit(UiSourceElement *) -{ - assert(!"not implemented"); - return false; -} - -void Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(IR::Expr *expr, const SourceLocation& loc) -{ - if (!_env->isStrict) - return; - IR::Name *n = expr->asName(); - if (!n) - return; - if (*n->id == QLatin1String("eval") || *n->id == QLatin1String("arguments")) - throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); -} - -void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) -{ - VM::DiagnosticMessage *msg = new VM::DiagnosticMessage; - msg->fileName = _fileName; - msg->offset = loc.begin(); - msg->startLine = loc.startLine; - msg->startColumn = loc.startColumn; - msg->message = detail; - if (_context) - _context->throwSyntaxError(msg); - else if (_errorHandler) - _errorHandler->syntaxError(msg); - else - Q_ASSERT(!"No error handler available."); -} - -void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail) -{ - if (_context) - _context->throwReferenceError(VM::Value::fromString(_context, detail)); - else if (_errorHandler) - throwSyntaxError(loc, detail); - else - Q_ASSERT(!"No error handler available."); -} diff --git a/qv4codegen_p.h b/qv4codegen_p.h deleted file mode 100644 index 89953c8ff9..0000000000 --- a/qv4codegen_p.h +++ /dev/null @@ -1,420 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4CODEGEN_P_H -#define QV4CODEGEN_P_H - -#include "qv4ir_p.h" -#include -#include -#include - -namespace QQmlJS { - -namespace AST { -class UiParameterList; -} - -namespace VM { -struct DiagnosticMessage; -struct ExecutionContext; -} - -namespace Debugging { -class Debugger; -} // namespace Debugging - -class ErrorHandler -{ -public: - virtual void syntaxError(VM::DiagnosticMessage *message) = 0; -}; - -class Codegen: protected AST::Visitor -{ -public: - Codegen(VM::ExecutionContext *ctx, bool strict); - Codegen(ErrorHandler *errorHandler, bool strictMode); - - enum Mode { - GlobalCode, - EvalCode, - FunctionCode - }; - - IR::Function *operator()(const QString &fileName, AST::Program *ast, IR::Module *module, Mode mode = GlobalCode, const QStringList &inheritedLocals = QStringList()); - IR::Function *operator()(const QString &fileName, AST::FunctionExpression *ast, IR::Module *module); - -protected: - enum Format { ex, cx, nx }; - struct Result { - IR::Expr *code; - IR::BasicBlock *iftrue; - IR::BasicBlock *iffalse; - Format format; - Format requested; - - explicit Result(Format requested = ex) - : code(0) - , iftrue(0) - , iffalse(0) - , format(ex) - , requested(requested) {} - - explicit Result(IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) - : code(0) - , iftrue(iftrue) - , iffalse(iffalse) - , format(ex) - , requested(cx) {} - - inline IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; } - inline IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; } - - bool accept(Format f) - { - if (requested == f) { - format = f; - return true; - } - return false; - } - }; - - struct Environment { - Environment *parent; - - enum MemberType { - UndefinedMember, - VariableDefinition, - VariableDeclaration, - FunctionDefinition - }; - struct Member { - MemberType type; - int index; - AST::FunctionDeclaration *function; - }; - typedef QMap MemberMap; - - MemberMap members; - int maxNumberOfArguments; - bool hasDirectEval; - bool hasNestedFunctions; - bool isStrict; - enum UsesArgumentsObject { - ArgumentsObjectUnknown, - ArgumentsObjectNotUsed, - ArgumentsObjectUsed - }; - - UsesArgumentsObject usesArgumentsObject; - - Environment(Environment *parent) - : parent(parent) - , maxNumberOfArguments(0) - , hasDirectEval(false) - , hasNestedFunctions(false) - , isStrict(false) - , usesArgumentsObject(ArgumentsObjectUnknown) - { - if (parent && parent->isStrict) - isStrict = true; - } - - int findMember(const QString &name) const - { - MemberMap::const_iterator it = members.find(name); - if (it == members.end()) - return -1; - assert((*it).index != -1 || !parent); - return (*it).index; - } - - bool lookupMember(const QString &name, Environment **scope, int *index, int *distance) - { - Environment *it = this; - *distance = 0; - for (; it; it = it->parent, ++(*distance)) { - int idx = it->findMember(name); - if (idx != -1) { - *scope = it; - *index = idx; - return true; - } - } - return false; - } - - void enter(const QString &name, MemberType type, AST::FunctionDeclaration *function = 0) - { - if (! name.isEmpty()) { - MemberMap::iterator it = members.find(name); - if (it == members.end()) { - Member m; - m.index = -1; - m.type = type; - m.function = function; - members.insert(name, m); - } else { - if ((*it).type <= type) { - (*it).type = type; - (*it).function = function; - } - } - } - } - }; - - Environment *newEnvironment(AST::Node *node, Environment *parent) - { - Environment *env = new Environment(parent); - _envMap.insert(node, env); - return env; - } - - struct UiMember { - }; - - struct ScopeAndFinally { - ScopeAndFinally *parent; - AST::Finally *finally; - IR::ExprList *deleteExceptionArgs; - bool popScope; - - ScopeAndFinally(ScopeAndFinally *parent) : parent(parent), finally(0), deleteExceptionArgs(0), popScope(true) {} - ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally, IR::ExprList *deleteExceptionArgs) - : parent(parent), finally(finally), deleteExceptionArgs(deleteExceptionArgs), popScope(false) - {} - }; - - struct Loop { - AST::LabelledStatement *labelledStatement; - AST::Statement *node; - IR::BasicBlock *breakBlock; - IR::BasicBlock *continueBlock; - Loop *parent; - ScopeAndFinally *scopeAndFinally; - - Loop(AST::Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock, Loop *parent) - : labelledStatement(0), node(node), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {} - }; - - void enterEnvironment(AST::Node *node); - void leaveEnvironment(); - - void enterLoop(AST::Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock); - void leaveLoop(); - - - IR::Expr *member(IR::Expr *base, const QString *name); - IR::Expr *subscript(IR::Expr *base, IR::Expr *index); - IR::Expr *argument(IR::Expr *expr); - IR::Expr *reference(IR::Expr *expr); - IR::Expr *unop(IR::AluOp op, IR::Expr *expr); - IR::Expr *binop(IR::AluOp op, IR::Expr *left, IR::Expr *right); - IR::Expr *call(IR::Expr *base, IR::ExprList *args); - void move(IR::Expr *target, IR::Expr *source, IR::AluOp op = IR::OpInvalid); - void cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse); - - void linearize(IR::Function *function); - IR::Function *defineFunction(const QString &name, AST::Node *ast, - AST::FormalParameterList *formals, - AST::SourceElements *body, - Mode mode = FunctionCode, - const QStringList &inheritedLocals = QStringList()); - int indexOfArgument(const QStringRef &string) const; - - void unwindException(ScopeAndFinally *outest); - - void statement(AST::Statement *ast); - void statement(AST::ExpressionNode *ast); - void condition(AST::ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse); - Result expression(AST::ExpressionNode *ast); - QString propertyName(AST::PropertyName *ast); - Result sourceElement(AST::SourceElement *ast); - UiMember uiObjectMember(AST::UiObjectMember *ast); - - void accept(AST::Node *node); - - void functionBody(AST::FunctionBody *ast); - void program(AST::Program *ast); - void sourceElements(AST::SourceElements *ast); - void variableDeclaration(AST::VariableDeclaration *ast); - void variableDeclarationList(AST::VariableDeclarationList *ast); - - IR::Expr *identifier(const QString &name, int line = 0, int col = 0); - - // nodes - virtual bool visit(AST::ArgumentList *ast); - virtual bool visit(AST::CaseBlock *ast); - virtual bool visit(AST::CaseClause *ast); - virtual bool visit(AST::CaseClauses *ast); - virtual bool visit(AST::Catch *ast); - virtual bool visit(AST::DefaultClause *ast); - virtual bool visit(AST::ElementList *ast); - virtual bool visit(AST::Elision *ast); - virtual bool visit(AST::Finally *ast); - virtual bool visit(AST::FormalParameterList *ast); - virtual bool visit(AST::FunctionBody *ast); - virtual bool visit(AST::Program *ast); - virtual bool visit(AST::PropertyNameAndValue *ast); - virtual bool visit(AST::PropertyAssignmentList *ast); - virtual bool visit(AST::PropertyGetterSetter *ast); - virtual bool visit(AST::SourceElements *ast); - virtual bool visit(AST::StatementList *ast); - virtual bool visit(AST::UiArrayMemberList *ast); - virtual bool visit(AST::UiImport *ast); - virtual bool visit(AST::UiImportList *ast); - virtual bool visit(AST::UiObjectInitializer *ast); - virtual bool visit(AST::UiObjectMemberList *ast); - virtual bool visit(AST::UiParameterList *ast); - virtual bool visit(AST::UiProgram *ast); - virtual bool visit(AST::UiQualifiedId *ast); - virtual bool visit(AST::VariableDeclaration *ast); - virtual bool visit(AST::VariableDeclarationList *ast); - - // expressions - virtual bool visit(AST::Expression *ast); - virtual bool visit(AST::ArrayLiteral *ast); - virtual bool visit(AST::ArrayMemberExpression *ast); - virtual bool visit(AST::BinaryExpression *ast); - virtual bool visit(AST::CallExpression *ast); - virtual bool visit(AST::ConditionalExpression *ast); - virtual bool visit(AST::DeleteExpression *ast); - virtual bool visit(AST::FalseLiteral *ast); - virtual bool visit(AST::FieldMemberExpression *ast); - virtual bool visit(AST::FunctionExpression *ast); - virtual bool visit(AST::IdentifierExpression *ast); - virtual bool visit(AST::NestedExpression *ast); - virtual bool visit(AST::NewExpression *ast); - virtual bool visit(AST::NewMemberExpression *ast); - virtual bool visit(AST::NotExpression *ast); - virtual bool visit(AST::NullExpression *ast); - virtual bool visit(AST::NumericLiteral *ast); - virtual bool visit(AST::ObjectLiteral *ast); - virtual bool visit(AST::PostDecrementExpression *ast); - virtual bool visit(AST::PostIncrementExpression *ast); - virtual bool visit(AST::PreDecrementExpression *ast); - virtual bool visit(AST::PreIncrementExpression *ast); - virtual bool visit(AST::RegExpLiteral *ast); - virtual bool visit(AST::StringLiteral *ast); - virtual bool visit(AST::ThisExpression *ast); - virtual bool visit(AST::TildeExpression *ast); - virtual bool visit(AST::TrueLiteral *ast); - virtual bool visit(AST::TypeOfExpression *ast); - virtual bool visit(AST::UnaryMinusExpression *ast); - virtual bool visit(AST::UnaryPlusExpression *ast); - virtual bool visit(AST::VoidExpression *ast); - virtual bool visit(AST::FunctionDeclaration *ast); - - // property names - virtual bool visit(AST::IdentifierPropertyName *ast); - virtual bool visit(AST::NumericLiteralPropertyName *ast); - virtual bool visit(AST::StringLiteralPropertyName *ast); - - // source elements - virtual bool visit(AST::FunctionSourceElement *ast); - virtual bool visit(AST::StatementSourceElement *ast); - - // statements - virtual bool visit(AST::Block *ast); - virtual bool visit(AST::BreakStatement *ast); - virtual bool visit(AST::ContinueStatement *ast); - virtual bool visit(AST::DebuggerStatement *ast); - virtual bool visit(AST::DoWhileStatement *ast); - virtual bool visit(AST::EmptyStatement *ast); - virtual bool visit(AST::ExpressionStatement *ast); - virtual bool visit(AST::ForEachStatement *ast); - virtual bool visit(AST::ForStatement *ast); - virtual bool visit(AST::IfStatement *ast); - virtual bool visit(AST::LabelledStatement *ast); - virtual bool visit(AST::LocalForEachStatement *ast); - virtual bool visit(AST::LocalForStatement *ast); - virtual bool visit(AST::ReturnStatement *ast); - virtual bool visit(AST::SwitchStatement *ast); - virtual bool visit(AST::ThrowStatement *ast); - virtual bool visit(AST::TryStatement *ast); - virtual bool visit(AST::VariableStatement *ast); - virtual bool visit(AST::WhileStatement *ast); - virtual bool visit(AST::WithStatement *ast); - - // ui object members - virtual bool visit(AST::UiArrayBinding *ast); - virtual bool visit(AST::UiObjectBinding *ast); - virtual bool visit(AST::UiObjectDefinition *ast); - virtual bool visit(AST::UiPublicMember *ast); - virtual bool visit(AST::UiScriptBinding *ast); - virtual bool visit(AST::UiSourceElement *ast); - - void throwSyntaxErrorOnEvalOrArgumentsInStrictMode(IR::Expr* expr, const AST::SourceLocation &loc); - - void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); - void throwReferenceError(const AST::SourceLocation &loc, const QString &detail); - -private: - QString _fileName; - Result _expr; - QString _property; - UiMember _uiMember; - IR::Module *_module; - IR::Function *_function; - IR::BasicBlock *_block; - IR::BasicBlock *_exitBlock; - IR::BasicBlock *_throwBlock; - unsigned _returnAddress; - Mode _mode; - Environment *_env; - Loop *_loop; - AST::LabelledStatement *_labelledStatement; - ScopeAndFinally *_scopeAndFinally; - QHash _envMap; - QHash _functionMap; - VM::ExecutionContext *_context; - bool _strictMode; - Debugging::Debugger *_debugger; - ErrorHandler *_errorHandler; - - class ScanFunctions; -}; - -} // end of namespace QQmlJS - -#endif // QV4CODEGEN_P_H diff --git a/qv4dateobject.cpp b/qv4dateobject.cpp deleted file mode 100644 index b9a4446087..0000000000 --- a/qv4dateobject.cpp +++ /dev/null @@ -1,1313 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qv4dateobject.h" -#include "qv4objectproto.h" -#include "qv4mm.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifndef Q_WS_WIN -# include -# ifndef Q_OS_VXWORKS -# include -# else -# include "qplatformdefs.h" -# endif -#else -# include -#endif - -using namespace QQmlJS::VM; - -static const double HoursPerDay = 24.0; -static const double MinutesPerHour = 60.0; -static const double SecondsPerMinute = 60.0; -static const double msPerSecond = 1000.0; -static const double msPerMinute = 60000.0; -static const double msPerHour = 3600000.0; -static const double msPerDay = 86400000.0; - -static double LocalTZA = 0.0; // initialized at startup - -static inline double TimeWithinDay(double t) -{ - double r = ::fmod(t, msPerDay); - return (r >= 0) ? r : r + msPerDay; -} - -static inline int HourFromTime(double t) -{ - int r = int(::fmod(::floor(t / msPerHour), HoursPerDay)); - return (r >= 0) ? r : r + int(HoursPerDay); -} - -static inline int MinFromTime(double t) -{ - int r = int(::fmod(::floor(t / msPerMinute), MinutesPerHour)); - return (r >= 0) ? r : r + int(MinutesPerHour); -} - -static inline int SecFromTime(double t) -{ - int r = int(::fmod(::floor(t / msPerSecond), SecondsPerMinute)); - return (r >= 0) ? r : r + int(SecondsPerMinute); -} - -static inline int msFromTime(double t) -{ - int r = int(::fmod(t, msPerSecond)); - return (r >= 0) ? r : r + int(msPerSecond); -} - -static inline double Day(double t) -{ - return ::floor(t / msPerDay); -} - -static inline double DaysInYear(double y) -{ - if (::fmod(y, 4)) - return 365; - - else if (::fmod(y, 100)) - return 366; - - else if (::fmod(y, 400)) - return 365; - - return 366; -} - -static inline double DayFromYear(double y) -{ - return 365 * (y - 1970) - + ::floor((y - 1969) / 4) - - ::floor((y - 1901) / 100) - + ::floor((y - 1601) / 400); -} - -static inline double TimeFromYear(double y) -{ - return msPerDay * DayFromYear(y); -} - -static inline double YearFromTime(double t) -{ - int y = 1970; - y += (int) ::floor(t / (msPerDay * 365.2425)); - - double t2 = TimeFromYear(y); - return (t2 > t) ? y - 1 : ((t2 + msPerDay * DaysInYear(y)) <= t) ? y + 1 : y; -} - -static inline bool InLeapYear(double t) -{ - double x = DaysInYear(YearFromTime(t)); - if (x == 365) - return 0; - - assert(x == 366); - return 1; -} - -static inline double DayWithinYear(double t) -{ - return Day(t) - DayFromYear(YearFromTime(t)); -} - -static inline double MonthFromTime(double t) -{ - double d = DayWithinYear(t); - double l = InLeapYear(t); - - if (d < 31.0) - return 0; - - else if (d < 59.0 + l) - return 1; - - else if (d < 90.0 + l) - return 2; - - else if (d < 120.0 + l) - return 3; - - else if (d < 151.0 + l) - return 4; - - else if (d < 181.0 + l) - return 5; - - else if (d < 212.0 + l) - return 6; - - else if (d < 243.0 + l) - return 7; - - else if (d < 273.0 + l) - return 8; - - else if (d < 304.0 + l) - return 9; - - else if (d < 334.0 + l) - return 10; - - else if (d < 365.0 + l) - return 11; - - return qSNaN(); // ### assert? -} - -static inline double DateFromTime(double t) -{ - int m = (int) Value::toInteger(MonthFromTime(t)); - double d = DayWithinYear(t); - double l = InLeapYear(t); - - switch (m) { - case 0: return d + 1.0; - case 1: return d - 30.0; - case 2: return d - 58.0 - l; - case 3: return d - 89.0 - l; - case 4: return d - 119.0 - l; - case 5: return d - 150.0 - l; - case 6: return d - 180.0 - l; - case 7: return d - 211.0 - l; - case 8: return d - 242.0 - l; - case 9: return d - 272.0 - l; - case 10: return d - 303.0 - l; - case 11: return d - 333.0 - l; - } - - return qSNaN(); // ### assert -} - -static inline double WeekDay(double t) -{ - double r = ::fmod (Day(t) + 4.0, 7.0); - return (r >= 0) ? r : r + 7.0; -} - - -static inline double MakeTime(double hour, double min, double sec, double ms) -{ - return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms; -} - -static inline double DayFromMonth(double month, double leap) -{ - switch ((int) month) { - case 0: return 0; - case 1: return 31.0; - case 2: return 59.0 + leap; - case 3: return 90.0 + leap; - case 4: return 120.0 + leap; - case 5: return 151.0 + leap; - case 6: return 181.0 + leap; - case 7: return 212.0 + leap; - case 8: return 243.0 + leap; - case 9: return 273.0 + leap; - case 10: return 304.0 + leap; - case 11: return 334.0 + leap; - } - - return qSNaN(); // ### assert? -} - -static double MakeDay(double year, double month, double day) -{ - year += ::floor(month / 12.0); - - month = ::fmod(month, 12.0); - if (month < 0) - month += 12.0; - - double d = DayFromYear(year); - bool leap = InLeapYear(d*msPerDay); - - d += DayFromMonth(month, leap); - d += day - 1; - - return d; -} - -static inline double MakeDate(double day, double time) -{ - return day * msPerDay + time; -} - -static inline double DaylightSavingTA(double t) -{ -#ifndef Q_WS_WIN - long int tt = (long int)(t / msPerSecond); - struct tm tmtm; - if (!localtime_r((const time_t*)&tt, &tmtm)) - return 0; - return (tmtm.tm_isdst > 0) ? msPerHour : 0; -#else - Q_UNUSED(t); - /// ### implement me - return 0; -#endif -} - -static inline double LocalTime(double t) -{ - return t + LocalTZA + DaylightSavingTA(t); -} - -static inline double UTC(double t) -{ - return t - LocalTZA - DaylightSavingTA(t - LocalTZA); -} - -static inline double currentTime() -{ -#ifndef Q_WS_WIN - struct timeval tv; - - gettimeofday(&tv, 0); - return ::floor(tv.tv_sec * msPerSecond + (tv.tv_usec / 1000.0)); -#else - SYSTEMTIME st; - GetSystemTime(&st); - FILETIME ft; - SystemTimeToFileTime(&st, &ft); - LARGE_INTEGER li; - li.LowPart = ft.dwLowDateTime; - li.HighPart = ft.dwHighDateTime; - return double(li.QuadPart - Q_INT64_C(116444736000000000)) / 10000.0; -#endif -} - -static inline double TimeClip(double t) -{ - if (! qIsFinite(t) || fabs(t) > 8.64e15) - return qSNaN(); - return Value::toInteger(t); -} - -static inline double FromDateTime(const QDateTime &dt) -{ - if (!dt.isValid()) - return qSNaN(); - QDate date = dt.date(); - QTime taim = dt.time(); - int year = date.year(); - int month = date.month() - 1; - int day = date.day(); - int hours = taim.hour(); - int mins = taim.minute(); - int secs = taim.second(); - int ms = taim.msec(); - double t = MakeDate(MakeDay(year, month, day), - MakeTime(hours, mins, secs, ms)); - if (dt.timeSpec() == Qt::LocalTime) - t = UTC(t); - return TimeClip(t); -} - -static inline double ParseString(const QString &s) -{ - // first try the format defined in 15.9.1.15, only if that fails fall back to - // QDateTime for parsing - - // the define string format is YYYY-MM-DDTHH:mm:ss.sssZ - // It can be date or time only, and the second and later components - // of both fields are optional - // and extended syntax for negative and large positive years exists: +/-YYYYYY - - enum Format { - Year, - Month, - Day, - Hour, - Minute, - Second, - MilliSecond, - TimezoneHour, - TimezoneMinute, - Done - }; - - const QChar *ch = s.constData(); - const QChar *end = ch + s.length(); - - uint format = Year; - int current = 0; - int currentSize = 0; - bool extendedYear = false; - - int yearSign = 1; - int year = 0; - int month = 0; - int day = 1; - int hour = 0; - int minute = 0; - int second = 0; - int msec = 0; - int offsetSign = 1; - int offset = 0; - - bool error = false; - if (*ch == '+' || *ch == '-') { - extendedYear = true; - if (*ch == '-') - yearSign = -1; - ++ch; - } - while (ch <= end) { - if (*ch >= '0' && *ch <= '9') { - current *= 10; - current += ch->unicode() - '0'; - ++currentSize; - } else { // other char, delimits field - switch (format) { - case Year: - year = current; - if (extendedYear) - error = (currentSize != 6); - else - error = (currentSize != 4); - break; - case Month: - month = current - 1; - error = (currentSize != 2) || month > 11; - break; - case Day: - day = current; - error = (currentSize != 2) || day > 31; - break; - case Hour: - hour = current; - error = (currentSize != 2) || hour > 24; - break; - case Minute: - minute = current; - error = (currentSize != 2) || minute > 60; - break; - case Second: - second = current; - error = (currentSize != 2) || second > 60; - break; - case MilliSecond: - msec = current; - error = (currentSize != 3); - break; - case TimezoneHour: - offset = current*60; - error = (currentSize != 2) || offset > 23*60; - break; - case TimezoneMinute: - offset += current; - error = (currentSize != 2) || current >= 60; - break; - } - if (*ch == 'T') { - if (format >= Hour) - error = true; - format = Hour; - } else if (*ch == '-') { - if (format < Day) - ++format; - else if (format < Minute) - error = true; - else if (format >= TimezoneHour) - error = true; - else { - offsetSign = -1; - format = TimezoneHour; - } - } else if (*ch == ':') { - if (format != Hour && format != Minute && format != TimezoneHour) - error = true; - ++format; - } else if (*ch == '.') { - if (format != Second) - error = true; - ++format; - } else if (*ch == '+') { - if (format < Minute || format >= TimezoneHour) - error = true; - format = TimezoneHour; - } else if (*ch == 'Z' || *ch == 0) { - format = Done; - } - current = 0; - currentSize = 0; - } - if (error || format == Done) - break; - ++ch; - } - - if (!error) { - double t = MakeDate(MakeDay(year * yearSign, month, day), MakeTime(hour, minute, second, msec)); - t += offset * offsetSign * 60 * 1000; - return t; - } - - QDateTime dt = QDateTime::fromString(s, Qt::TextDate); - if (!dt.isValid()) - dt = QDateTime::fromString(s, Qt::ISODate); - if (!dt.isValid()) { - QStringList formats; - formats << QStringLiteral("M/d/yyyy") - << QStringLiteral("M/d/yyyy hh:mm") - << QStringLiteral("M/d/yyyy hh:mm A") - - << QStringLiteral("M/d/yyyy, hh:mm") - << QStringLiteral("M/d/yyyy, hh:mm A") - - << QStringLiteral("MMM d yyyy") - << QStringLiteral("MMM d yyyy hh:mm") - << QStringLiteral("MMM d yyyy hh:mm:ss") - << QStringLiteral("MMM d yyyy, hh:mm") - << QStringLiteral("MMM d yyyy, hh:mm:ss") - - << QStringLiteral("MMMM d yyyy") - << QStringLiteral("MMMM d yyyy hh:mm") - << QStringLiteral("MMMM d yyyy hh:mm:ss") - << QStringLiteral("MMMM d yyyy, hh:mm") - << QStringLiteral("MMMM d yyyy, hh:mm:ss") - - << QStringLiteral("MMM d, yyyy") - << QStringLiteral("MMM d, yyyy hh:mm") - << QStringLiteral("MMM d, yyyy hh:mm:ss") - - << QStringLiteral("MMMM d, yyyy") - << QStringLiteral("MMMM d, yyyy hh:mm") - << QStringLiteral("MMMM d, yyyy hh:mm:ss") - - << QStringLiteral("d MMM yyyy") - << QStringLiteral("d MMM yyyy hh:mm") - << QStringLiteral("d MMM yyyy hh:mm:ss") - << QStringLiteral("d MMM yyyy, hh:mm") - << QStringLiteral("d MMM yyyy, hh:mm:ss") - - << QStringLiteral("d MMMM yyyy") - << QStringLiteral("d MMMM yyyy hh:mm") - << QStringLiteral("d MMMM yyyy hh:mm:ss") - << QStringLiteral("d MMMM yyyy, hh:mm") - << QStringLiteral("d MMMM yyyy, hh:mm:ss") - - << QStringLiteral("d MMM, yyyy") - << QStringLiteral("d MMM, yyyy hh:mm") - << QStringLiteral("d MMM, yyyy hh:mm:ss") - - << QStringLiteral("d MMMM, yyyy") - << QStringLiteral("d MMMM, yyyy hh:mm") - << QStringLiteral("d MMMM, yyyy hh:mm:ss"); - - for (int i = 0; i < formats.size(); ++i) { - dt = QDateTime::fromString(s, formats.at(i)); - if (dt.isValid()) - break; - } - } - return FromDateTime(dt); -} - -/*! - \internal - - Converts the ECMA Date value \tt (in UTC form) to QDateTime - according to \a spec. -*/ -static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) -{ - if (std::isnan(t)) - return QDateTime(); - if (spec == Qt::LocalTime) - t = LocalTime(t); - int year = int(YearFromTime(t)); - int month = int(MonthFromTime(t) + 1); - int day = int(DateFromTime(t)); - int hours = HourFromTime(t); - int mins = MinFromTime(t); - int secs = SecFromTime(t); - int ms = msFromTime(t); - return QDateTime(QDate(year, month, day), QTime(hours, mins, secs, ms), spec); -} - -static inline QString ToString(double t) -{ - if (std::isnan(t)) - return QStringLiteral("Invalid Date"); - QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT"); - double tzoffset = LocalTZA + DaylightSavingTA(t); - if (tzoffset) { - int hours = static_cast(::fabs(tzoffset) / 1000 / 60 / 60); - int mins = int(::fabs(tzoffset) / 1000 / 60) % 60; - str.append(QLatin1Char((tzoffset > 0) ? '+' : '-')); - if (hours < 10) - str.append(QLatin1Char('0')); - str.append(QString::number(hours)); - if (mins < 10) - str.append(QLatin1Char('0')); - str.append(QString::number(mins)); - } - return str; -} - -static inline QString ToUTCString(double t) -{ - if (std::isnan(t)) - return QStringLiteral("Invalid Date"); - return ToDateTime(t, Qt::UTC).toString() + QStringLiteral(" GMT"); -} - -static inline QString ToDateString(double t) -{ - return ToDateTime(t, Qt::LocalTime).date().toString(); -} - -static inline QString ToTimeString(double t) -{ - return ToDateTime(t, Qt::LocalTime).time().toString(); -} - -static inline QString ToLocaleString(double t) -{ - return ToDateTime(t, Qt::LocalTime).toString(Qt::LocaleDate); -} - -static inline QString ToLocaleDateString(double t) -{ - return ToDateTime(t, Qt::LocalTime).date().toString(Qt::LocaleDate); -} - -static inline QString ToLocaleTimeString(double t) -{ - return ToDateTime(t, Qt::LocalTime).time().toString(Qt::LocaleDate); -} - -static double getLocalTZA() -{ -#ifndef Q_WS_WIN - struct tm t; - time_t curr; - time(&curr); - localtime_r(&curr, &t); - time_t locl = mktime(&t); - gmtime_r(&curr, &t); - time_t globl = mktime(&t); - return double(locl - globl) * 1000.0; -#else - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - return -tzInfo.Bias * 60.0 * 1000.0; -#endif -} - - -DateCtor::DateCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value DateCtor::construct(ExecutionContext *ctx) -{ - double t = 0; - - if (ctx->argumentCount == 0) - t = currentTime(); - - else if (ctx->argumentCount == 1) { - Value arg = ctx->argument(0); - if (DateObject *d = arg.asDateObject()) - arg = d->value; - else - arg = __qmljs_to_primitive(arg, ctx, PREFERREDTYPE_HINT); - - if (arg.isString()) - t = ParseString(arg.stringValue()->toQString()); - else - t = TimeClip(arg.toNumber(ctx)); - } - - else { // ctx->argumentCount > 1 - double year = ctx->argument(0).toNumber(ctx); - double month = ctx->argument(1).toNumber(ctx); - double day = ctx->argumentCount >= 3 ? ctx->argument(2).toNumber(ctx) : 1; - double hours = ctx->argumentCount >= 4 ? ctx->argument(3).toNumber(ctx) : 0; - double mins = ctx->argumentCount >= 5 ? ctx->argument(4).toNumber(ctx) : 0; - double secs = ctx->argumentCount >= 6 ? ctx->argument(5).toNumber(ctx) : 0; - double ms = ctx->argumentCount >= 7 ? ctx->argument(6).toNumber(ctx) : 0; - if (year >= 0 && year <= 99) - year += 1900; - t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); - t = TimeClip(UTC(t)); - } - - Object *d = ctx->engine->newDateObject(Value::fromDouble(t)); - return Value::fromObject(d); -} - -Value DateCtor::call(ExecutionContext *ctx) -{ - double t = currentTime(); - return Value::fromString(ctx, ToString(t)); -} - -void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(7)); - LocalTZA = getLocalTZA(); - - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("parse"), method_parse, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("now"), method_now, 0); - - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("toDateString"), method_toDateString, 0); - defineDefaultProperty(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); - defineDefaultProperty(ctx, QStringLiteral("getTime"), method_getTime, 0); - defineDefaultProperty(ctx, QStringLiteral("getYear"), method_getYear, 0); - defineDefaultProperty(ctx, QStringLiteral("getFullYear"), method_getFullYear, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0); - defineDefaultProperty(ctx, QStringLiteral("getMonth"), method_getMonth, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCMonth"), method_getUTCMonth, 0); - defineDefaultProperty(ctx, QStringLiteral("getDate"), method_getDate, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCDate"), method_getUTCDate, 0); - defineDefaultProperty(ctx, QStringLiteral("getDay"), method_getDay, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCDay"), method_getUTCDay, 0); - defineDefaultProperty(ctx, QStringLiteral("getHours"), method_getHours, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCHours"), method_getUTCHours, 0); - defineDefaultProperty(ctx, QStringLiteral("getMinutes"), method_getMinutes, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0); - defineDefaultProperty(ctx, QStringLiteral("getSeconds"), method_getSeconds, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0); - defineDefaultProperty(ctx, QStringLiteral("getMilliseconds"), method_getMilliseconds, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0); - defineDefaultProperty(ctx, QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0); - defineDefaultProperty(ctx, QStringLiteral("setTime"), method_setTime, 1); - defineDefaultProperty(ctx, QStringLiteral("setMilliseconds"), method_setMilliseconds, 1); - defineDefaultProperty(ctx, QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1); - defineDefaultProperty(ctx, QStringLiteral("setSeconds"), method_setSeconds, 2); - defineDefaultProperty(ctx, QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2); - defineDefaultProperty(ctx, QStringLiteral("setMinutes"), method_setMinutes, 3); - defineDefaultProperty(ctx, QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3); - defineDefaultProperty(ctx, QStringLiteral("setHours"), method_setHours, 4); - defineDefaultProperty(ctx, QStringLiteral("setUTCHours"), method_setUTCHours, 4); - defineDefaultProperty(ctx, QStringLiteral("setDate"), method_setDate, 1); - defineDefaultProperty(ctx, QStringLiteral("setUTCDate"), method_setUTCDate, 1); - defineDefaultProperty(ctx, QStringLiteral("setMonth"), method_setMonth, 2); - defineDefaultProperty(ctx, QStringLiteral("setUTCMonth"), method_setUTCMonth, 2); - defineDefaultProperty(ctx, QStringLiteral("setYear"), method_setYear, 1); - defineDefaultProperty(ctx, QStringLiteral("setFullYear"), method_setFullYear, 3); - defineDefaultProperty(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); - defineDefaultProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); - defineDefaultProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); - defineDefaultProperty(ctx, QStringLiteral("toISOString"), method_toISOString, 0); - defineDefaultProperty(ctx, QStringLiteral("toJSON"), method_toJSON, 1); -} - -double DatePrototype::getThisDate(ExecutionContext *ctx) -{ - if (DateObject *thisObject = ctx->thisObject.asDateObject()) - return thisObject->value.asDouble(); - else { - ctx->throwTypeError(); - return 0; - } -} - -Value DatePrototype::method_parse(ExecutionContext *ctx) -{ - return Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString())); -} - -Value DatePrototype::method_UTC(ExecutionContext *ctx) -{ - const int numArgs = ctx->argumentCount; - if (numArgs >= 2) { - double year = ctx->argument(0).toNumber(ctx); - double month = ctx->argument(1).toNumber(ctx); - double day = numArgs >= 3 ? ctx->argument(2).toNumber(ctx) : 1; - double hours = numArgs >= 4 ? ctx->argument(3).toNumber(ctx) : 0; - double mins = numArgs >= 5 ? ctx->argument(4).toNumber(ctx) : 0; - double secs = numArgs >= 6 ? ctx->argument(5).toNumber(ctx) : 0; - double ms = numArgs >= 7 ? ctx->argument(6).toNumber(ctx) : 0; - if (year >= 0 && year <= 99) - year += 1900; - double t = MakeDate(MakeDay(year, month, day), - MakeTime(hours, mins, secs, ms)); - return Value::fromDouble(TimeClip(t)); - } - return Value::undefinedValue(); -} - -Value DatePrototype::method_now(ExecutionContext *ctx) -{ - Q_UNUSED(ctx); - double t = currentTime(); - return Value::fromDouble(t); -} - -Value DatePrototype::method_toString(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToString(t)); -} - -Value DatePrototype::method_toDateString(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToDateString(t)); -} - -Value DatePrototype::method_toTimeString(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToTimeString(t)); -} - -Value DatePrototype::method_toLocaleString(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToLocaleString(t)); -} - -Value DatePrototype::method_toLocaleDateString(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToLocaleDateString(t)); -} - -Value DatePrototype::method_toLocaleTimeString(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToLocaleTimeString(t)); -} - -Value DatePrototype::method_valueOf(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getTime(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getYear(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = YearFromTime(LocalTime(t)) - 1900; - return Value::fromDouble(t); -} - -Value DatePrototype::method_getFullYear(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = YearFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCFullYear(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = YearFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getMonth(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = MonthFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCMonth(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = MonthFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getDate(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = DateFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCDate(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = DateFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getDay(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = WeekDay(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCDay(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = WeekDay(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getHours(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = HourFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCHours(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = HourFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getMinutes(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = MinFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCMinutes(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = MinFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getSeconds(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = SecFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCSeconds(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = SecFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getMilliseconds(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = msFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCMilliseconds(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = msFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getTimezoneOffset(ExecutionContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = (t - LocalTime(t)) / msPerMinute; - return Value::fromDouble(t); -} - -Value DatePrototype::method_setTime(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - self->value.setDouble(TimeClip(ctx->argument(0).toNumber(ctx))); - return self->value; -} - -Value DatePrototype::method_setMilliseconds(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double ms = ctx->argument(0).toNumber(ctx); - self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); - return self->value; -} - -Value DatePrototype::method_setUTCMilliseconds(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double ms = ctx->argument(0).toNumber(ctx); - self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); - return self->value; -} - -Value DatePrototype::method_setSeconds(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double sec = ctx->argument(0).toNumber(ctx); - double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCSeconds(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double sec = ctx->argument(0).toNumber(ctx); - double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setMinutes(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double min = ctx->argument(0).toNumber(ctx); - double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); - double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCMinutes(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double min = ctx->argument(0).toNumber(ctx); - double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); - double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setHours(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double hour = ctx->argument(0).toNumber(ctx); - double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); - double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); - double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCHours(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double hour = ctx->argument(0).toNumber(ctx); - double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); - double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); - double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setDate(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double date = ctx->argument(0).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCDate(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double date = ctx->argument(0).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setMonth(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double month = ctx->argument(0).toNumber(ctx); - double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCMonth(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double month = ctx->argument(0).toNumber(ctx); - double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setYear(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - if (std::isnan(t)) - t = 0; - else - t = LocalTime(t); - double year = ctx->argument(0).toNumber(ctx); - double r; - if (std::isnan(year)) { - r = qSNaN(); - } else { - if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99)) - year += 1900; - r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); - r = UTC(MakeDate(r, TimeWithinDay(t))); - r = TimeClip(r); - } - self->value.setDouble(r); - return self->value; -} - -Value DatePrototype::method_setUTCFullYear(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double year = ctx->argument(0).toNumber(ctx); - double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); - double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setFullYear(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - if (std::isnan(t)) - t = 0; - double year = ctx->argument(0).toNumber(ctx); - double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); - double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); - t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_toUTCString(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - return Value::fromString(ctx, ToUTCString(t)); -} - -static void addZeroPrefixedInt(QString &str, int num, int nDigits) -{ - str.resize(str.size() + nDigits); - - QChar *c = str.data() + str.size() - 1; - while (nDigits) { - *c = QChar(num % 10 + '0'); - num /= 10; - --c; - --nDigits; - } -} - -Value DatePrototype::method_toISOString(ExecutionContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - if (!std::isfinite(t)) - ctx->throwRangeError(ctx->thisObject); - - QString result; - int year = (int)YearFromTime(t); - if (year < 0 || year > 9999) { - if (qAbs(year) >= 1000000) - return Value::fromString(ctx, QStringLiteral("Invalid Date")); - result += year < 0 ? '-' : '+'; - year = qAbs(year); - addZeroPrefixedInt(result, year, 6); - } else { - addZeroPrefixedInt(result, year, 4); - } - result += '-'; - addZeroPrefixedInt(result, (int)MonthFromTime(t) + 1, 2); - result += '-'; - addZeroPrefixedInt(result, (int)DateFromTime(t), 2); - result += 'T'; - addZeroPrefixedInt(result, HourFromTime(t), 2); - result += ':'; - addZeroPrefixedInt(result, MinFromTime(t), 2); - result += ':'; - addZeroPrefixedInt(result, SecFromTime(t), 2); - result += '.'; - addZeroPrefixedInt(result, msFromTime(t), 3); - result += 'Z'; - - return Value::fromString(ctx, result); -} - -Value DatePrototype::method_toJSON(ExecutionContext *ctx) -{ - Value O = __qmljs_to_object(ctx->thisObject, ctx); - Value tv = __qmljs_to_primitive(O, ctx, NUMBER_HINT); - - if (tv.isNumber() && !std::isfinite(tv.toNumber(ctx))) - return Value::nullValue(); - - FunctionObject *toIso = O.objectValue()->__get__(ctx, ctx->engine->identifier(QStringLiteral("toISOString"))).asFunctionObject(); - - if (!toIso) - __qmljs_throw_type_error(ctx); - - return toIso->call(ctx, ctx->thisObject, 0, 0); -} diff --git a/qv4dateobject.h b/qv4dateobject.h deleted file mode 100644 index ede4473217..0000000000 --- a/qv4dateobject.h +++ /dev/null @@ -1,125 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4DATEOBJECT_P_H -#define QV4DATEOBJECT_P_H - -#include "qv4object.h" -#include "qv4functionobject.h" -#include - -namespace QQmlJS { -namespace VM { - -struct DateObject: Object { - Value value; - DateObject(const Value &value): value(value) { type = Type_DateObject; } -}; - -struct DateCtor: FunctionObject -{ - DateCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct DatePrototype: DateObject -{ - DatePrototype(): DateObject(Value::fromDouble(qSNaN())) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static double getThisDate(ExecutionContext *ctx); - - static Value method_parse(ExecutionContext *ctx); - static Value method_UTC(ExecutionContext *ctx); - static Value method_now(ExecutionContext *ctx); - - static Value method_toString(ExecutionContext *ctx); - static Value method_toDateString(ExecutionContext *ctx); - static Value method_toTimeString(ExecutionContext *ctx); - static Value method_toLocaleString(ExecutionContext *ctx); - static Value method_toLocaleDateString(ExecutionContext *ctx); - static Value method_toLocaleTimeString(ExecutionContext *ctx); - static Value method_valueOf(ExecutionContext *ctx); - static Value method_getTime(ExecutionContext *ctx); - static Value method_getYear(ExecutionContext *ctx); - static Value method_getFullYear(ExecutionContext *ctx); - static Value method_getUTCFullYear(ExecutionContext *ctx); - static Value method_getMonth(ExecutionContext *ctx); - static Value method_getUTCMonth(ExecutionContext *ctx); - static Value method_getDate(ExecutionContext *ctx); - static Value method_getUTCDate(ExecutionContext *ctx); - static Value method_getDay(ExecutionContext *ctx); - static Value method_getUTCDay(ExecutionContext *ctx); - static Value method_getHours(ExecutionContext *ctx); - static Value method_getUTCHours(ExecutionContext *ctx); - static Value method_getMinutes(ExecutionContext *ctx); - static Value method_getUTCMinutes(ExecutionContext *ctx); - static Value method_getSeconds(ExecutionContext *ctx); - static Value method_getUTCSeconds(ExecutionContext *ctx); - static Value method_getMilliseconds(ExecutionContext *ctx); - static Value method_getUTCMilliseconds(ExecutionContext *ctx); - static Value method_getTimezoneOffset(ExecutionContext *ctx); - static Value method_setTime(ExecutionContext *ctx); - static Value method_setMilliseconds(ExecutionContext *ctx); - static Value method_setUTCMilliseconds(ExecutionContext *ctx); - static Value method_setSeconds(ExecutionContext *ctx); - static Value method_setUTCSeconds(ExecutionContext *ctx); - static Value method_setMinutes(ExecutionContext *ctx); - static Value method_setUTCMinutes(ExecutionContext *ctx); - static Value method_setHours(ExecutionContext *ctx); - static Value method_setUTCHours(ExecutionContext *ctx); - static Value method_setDate(ExecutionContext *ctx); - static Value method_setUTCDate(ExecutionContext *ctx); - static Value method_setMonth(ExecutionContext *ctx); - static Value method_setUTCMonth(ExecutionContext *ctx); - static Value method_setYear(ExecutionContext *ctx); - static Value method_setFullYear(ExecutionContext *ctx); - static Value method_setUTCFullYear(ExecutionContext *ctx); - static Value method_toUTCString(ExecutionContext *ctx); - static Value method_toISOString(ExecutionContext *ctx); - static Value method_toJSON(ExecutionContext *ctx); -}; - -} // end of namespace VM -} // end of namespace QQmlJS - -#endif // QV4ECMAOBJECTS_P_H diff --git a/qv4errorobject.cpp b/qv4errorobject.cpp deleted file mode 100644 index 9d4a067f35..0000000000 --- a/qv4errorobject.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qv4errorobject.h" -#include "qv4mm.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifndef Q_WS_WIN -# include -# ifndef Q_OS_VXWORKS -# include -# else -# include "qplatformdefs.h" -# endif -#else -# include -#endif - -using namespace QQmlJS::VM; - -ErrorObject::ErrorObject(ExecutionEngine* engine, const Value &message) - : errorType(Error) -{ - type = Type_ErrorObject; - - if (message.type() != Value::Undefined_Type) - defineDefaultProperty(engine->identifier(QStringLiteral("message")), message); -} - -void ErrorObject::setNameProperty(ExecutionContext *ctx) -{ - defineDefaultProperty(ctx, QLatin1String("name"), Value::fromString(ctx, className())); -} - -SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) - : ErrorObject(ctx->engine, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0)) - , msg(message) -{ - errorType = SyntaxError; - prototype = ctx->engine->syntaxErrorPrototype; - setNameProperty(ctx); -} - - - -EvalErrorObject::EvalErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) -{ - errorType = EvalError; - setNameProperty(ctx); - prototype = ctx->engine->evalErrorPrototype; -} - -RangeErrorObject::RangeErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) -{ - errorType = RangeError; - setNameProperty(ctx); - prototype = ctx->engine->rangeErrorPrototype; -} - -RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) -{ - errorType = RangeError; - setNameProperty(ctx); - prototype = ctx->engine->rangeErrorPrototype; -} - -ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) -{ - errorType = ReferenceError; - setNameProperty(ctx); - prototype = ctx->engine->referenceErrorPrototype; -} - -ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) -{ - errorType = ReferenceError; - setNameProperty(ctx); - prototype = ctx->engine->referenceErrorPrototype; -} - -TypeErrorObject::TypeErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) -{ - errorType = TypeError; - setNameProperty(ctx); - prototype = ctx->engine->typeErrorPrototype; -} - -TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) -{ - errorType = TypeError; - setNameProperty(ctx); - prototype = ctx->engine->typeErrorPrototype; -} - -URIErrorObject::URIErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) -{ - errorType = URIError; - setNameProperty(ctx); - prototype = ctx->engine->uRIErrorPrototype; -} - -URIErrorObject::URIErrorObject(ExecutionContext *ctx, Value msg) - : ErrorObject(ctx->engine, msg) -{ - errorType = URIError; - setNameProperty(ctx); - prototype = ctx->engine->uRIErrorPrototype; -} - - -ErrorCtor::ErrorCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value ErrorCtor::construct(ExecutionContext *ctx) -{ - return Value::fromObject(ctx->engine->newErrorObject(ctx->argument(0))); -} - -Value ErrorCtor::call(ExecutionContext *ctx) -{ - return construct(ctx); -} - -Value EvalErrorCtor::construct(ExecutionContext *ctx) -{ - return Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx)); -} - -Value RangeErrorCtor::construct(ExecutionContext *ctx) -{ - return Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx)); -} - -Value ReferenceErrorCtor::construct(ExecutionContext *ctx) -{ - return Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx)); -} - -Value SyntaxErrorCtor::construct(ExecutionContext *ctx) -{ - return Value::fromObject(new (ctx->engine->memoryManager) SyntaxErrorObject(ctx, 0)); -} - -Value TypeErrorCtor::construct(ExecutionContext *ctx) -{ - return Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx)); -} - -Value URIErrorCtor::construct(ExecutionContext *ctx) -{ - return Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx)); -} - -void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(obj)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - obj->defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - obj->defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - obj->defineDefaultProperty(ctx, QStringLiteral("message"), Value::fromString(ctx, QString())); - obj->defineDefaultProperty(ctx, QStringLiteral("name"), Value::fromString(ctx, QStringLiteral("Error"))); -} - -Value ErrorPrototype::method_toString(ExecutionContext *ctx) -{ - Object *o = ctx->thisObject.asObject(); - if (!o) - __qmljs_throw_type_error(ctx); - - Value name = o->__get__(ctx, ctx->engine->newString(QString::fromLatin1("name"))); - QString qname; - if (name.isUndefined()) - qname = QString::fromLatin1("Error"); - else - qname = __qmljs_to_string(name, ctx).stringValue()->toQString(); - - Value message = o->__get__(ctx, ctx->engine->newString(QString::fromLatin1("message"))); - QString qmessage; - if (!message.isUndefined()) - qmessage = __qmljs_to_string(message, ctx).stringValue()->toQString(); - - QString str; - if (qname.isEmpty()) { - str = qmessage; - } else if (qmessage.isEmpty()) { - str = qname; - } else { - str = qname + QLatin1String(": ") + qmessage; - } - - return Value::fromString(ctx, str); -} diff --git a/qv4errorobject.h b/qv4errorobject.h deleted file mode 100644 index d2d2340fae..0000000000 --- a/qv4errorobject.h +++ /dev/null @@ -1,206 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4ERROROBJECT_H -#define QV4ERROROBJECT_H - -#include "qv4object.h" -#include "qv4functionobject.h" - -namespace QQmlJS { -namespace VM { - -struct ErrorObject: Object { - enum ErrorType { - Error, - EvalError, - RangeError, - ReferenceError, - SyntaxError, - TypeError, - URIError - }; - ErrorType errorType; - - ErrorObject(ExecutionEngine* engine, const Value &message); - - virtual struct SyntaxErrorObject *asSyntaxError() { return 0; } - -protected: - void setNameProperty(ExecutionContext *ctx); -}; - -struct EvalErrorObject: ErrorObject { - EvalErrorObject(ExecutionContext *ctx); -}; - -struct RangeErrorObject: ErrorObject { - RangeErrorObject(ExecutionContext *ctx); - RangeErrorObject(ExecutionContext *ctx, const QString &msg); -}; - -struct ReferenceErrorObject: ErrorObject { - ReferenceErrorObject(ExecutionContext *ctx); - ReferenceErrorObject(ExecutionContext *ctx, const QString &msg); -}; - -struct SyntaxErrorObject: ErrorObject { - SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg); - ~SyntaxErrorObject() { delete msg; } - - virtual SyntaxErrorObject *asSyntaxError() { return this; } - DiagnosticMessage *message() { return msg; } - -private: - DiagnosticMessage *msg; -}; - -struct TypeErrorObject: ErrorObject { - TypeErrorObject(ExecutionContext *ctx); - TypeErrorObject(ExecutionContext *ctx, const QString &msg); -}; - -struct URIErrorObject: ErrorObject { - URIErrorObject(ExecutionContext *ctx); - URIErrorObject(ExecutionContext *ctx, Value); -}; - -struct ErrorCtor: FunctionObject -{ - ErrorCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct EvalErrorCtor: ErrorCtor -{ - EvalErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - - virtual Value construct(ExecutionContext *ctx); -}; - -struct RangeErrorCtor: ErrorCtor -{ - RangeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - - virtual Value construct(ExecutionContext *ctx); -}; - -struct ReferenceErrorCtor: ErrorCtor -{ - ReferenceErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - - virtual Value construct(ExecutionContext *ctx); -}; - -struct SyntaxErrorCtor: ErrorCtor -{ - SyntaxErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - - virtual Value construct(ExecutionContext *ctx); -}; - -struct TypeErrorCtor: ErrorCtor -{ - TypeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - - virtual Value construct(ExecutionContext *ctx); -}; - -struct URIErrorCtor: ErrorCtor -{ - URIErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - - virtual Value construct(ExecutionContext *ctx); -}; - - -struct ErrorPrototype: ErrorObject -{ - // ### shouldn't be undefined - ErrorPrototype(ExecutionEngine* engine): ErrorObject(engine, Value::undefinedValue()) {} - void init(ExecutionContext *ctx, const Value &ctor) { init(ctx, ctor, this); } - - static void init(ExecutionContext *ctx, const Value &ctor, Object *obj); - static Value method_toString(ExecutionContext *ctx); -}; - -struct EvalErrorPrototype: EvalErrorObject -{ - EvalErrorPrototype(ExecutionContext *ctx): EvalErrorObject(ctx) {} - void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } -}; - -struct RangeErrorPrototype: RangeErrorObject -{ - RangeErrorPrototype(ExecutionContext *ctx): RangeErrorObject(ctx) {} - void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } -}; - -struct ReferenceErrorPrototype: ReferenceErrorObject -{ - ReferenceErrorPrototype(ExecutionContext *ctx): ReferenceErrorObject(ctx) {} - void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } -}; - -struct SyntaxErrorPrototype: SyntaxErrorObject -{ - SyntaxErrorPrototype(ExecutionContext *ctx): SyntaxErrorObject(ctx, 0) {} - void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } -}; - -struct TypeErrorPrototype: TypeErrorObject -{ - TypeErrorPrototype(ExecutionContext *ctx): TypeErrorObject(ctx) {} - void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } -}; - -struct URIErrorPrototype: URIErrorObject -{ - URIErrorPrototype(ExecutionContext *ctx): URIErrorObject(ctx) {} - void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } -}; - - -} // end of namespace VM -} // end of namespace QQmlJS - -#endif // QV4ECMAOBJECTS_P_H diff --git a/qv4functionobject.cpp b/qv4functionobject.cpp deleted file mode 100644 index f5e37def0c..0000000000 --- a/qv4functionobject.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4object.h" -#include "qv4ir_p.h" -#include "qv4isel_p.h" -#include "qv4objectproto.h" -#include "qv4stringobject.h" -#include "qv4mm.h" - -#include -#include -#include -#include -#include -#include -#include "private/qlocale_tools_p.h" - -#include -#include -#include -#include -#include -#include - -using namespace QQmlJS::VM; - - -Function::~Function() -{ - delete[] codeData; -} - -void Function::mark() -{ - if (name) - name->mark(); - for (int i = 0; i < formals.size(); ++i) - formals.at(i)->mark(); - for (int i = 0; i < locals.size(); ++i) - locals.at(i)->mark(); - for (int i = 0; i < generatedValues.size(); ++i) - if (Managed *m = generatedValues.at(i).asManaged()) - m->mark(); - for (int i = 0; i < identifiers.size(); ++i) - identifiers.at(i)->mark(); -} - -FunctionObject::FunctionObject(ExecutionContext *scope) - : scope(scope) - , name(0) - , formalParameterList(0) - , varList(0) - , formalParameterCount(0) - , varCount(0) -{ - prototype = scope->engine->functionPrototype; - - type = Type_FunctionObject; - needsActivation = false; - usesArgumentsObject = false; - strictMode = false; -} - -bool FunctionObject::hasInstance(ExecutionContext *ctx, const Value &value) -{ - if (! value.isObject()) - return false; - - Value o = __get__(ctx, ctx->engine->id_prototype); - if (! o.isObject()) { - ctx->throwTypeError(); - return false; - } - - Object *v = value.objectValue(); - while (v) { - v = v->prototype; - - if (! v) - break; - else if (o.objectValue() == v) - return true; - } - - return false; -} - -Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc) -{ - Object *obj = context->engine->newObject(); - Value proto = __get__(context, context->engine->id_prototype); - if (proto.isObject()) - obj->prototype = proto.objectValue(); - - uint size = requiredMemoryForExecutionContect(this, argc); - ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); - - ctx->initCallContext(context, Value::fromObject(obj), this, args, argc); - Value result = construct(ctx); - ctx->leaveCallContext(); - - if (result.isObject()) - return result; - return Value::fromObject(obj); -} - -Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *args, int argc) -{ - uint size = requiredMemoryForExecutionContect(this, argc); - ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); - - - ctx->initCallContext(context, thisObject, this, args, argc); - if (isBuiltinFunction) { - // Built-in functions allow for the this object to be null or undefined. This overrides - // the behaviour of changing thisObject to the global object if null/undefined and allows - // the built-in functions for example to throw a type error if null is passed. - if (thisObject.isNull() || thisObject.isUndefined()) - ctx->thisObject = thisObject; - } - Value result = call(ctx); - ctx->leaveCallContext(); - return result; -} - -void FunctionObject::markObjects() -{ - if (name) - name->mark(); - // these are marked in VM::Function: -// for (uint i = 0; i < formalParameterCount; ++i) -// formalParameterList[i]->mark(); -// for (uint i = 0; i < varCount; ++i) -// varList[i]->mark(); - scope->mark(); - Object::markObjects(); -} - -Value FunctionObject::call(ExecutionContext *ctx) -{ - Q_UNUSED(ctx); - return Value::undefinedValue(); -} - -Value FunctionObject::construct(ExecutionContext *ctx) -{ - return call(ctx); -} - - -FunctionCtor::FunctionCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -// 15.3.2 -Value FunctionCtor::construct(ExecutionContext *ctx) -{ - MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); - - QString args; - QString body; - if (ctx->argumentCount > 0) { - for (uint i = 0; i < ctx->argumentCount - 1; ++i) { - if (i) - args += QLatin1String(", "); - args += ctx->argument(i).toString(ctx)->toQString(); - } - body = ctx->argument(ctx->argumentCount - 1).toString(ctx)->toQString(); - } - - QString function = QLatin1String("function(") + args + QLatin1String("){") + body + QLatin1String("}"); - - QQmlJS::Engine ee, *engine = ⅇ - Lexer lexer(engine); - lexer.setCode(function, 1, false); - Parser parser(engine); - - const bool parsed = parser.parseExpression(); - - if (!parsed) - ctx->throwSyntaxError(0); - - using namespace AST; - FunctionExpression *fe = AST::cast(parser.rootNode()); - if (!fe) - ctx->throwSyntaxError(0); - - IR::Module module; - - Codegen cg(ctx, ctx->strictMode); - IR::Function *irf = cg(QString(), fe, &module); - - QScopedPointer isel(ctx->engine->iselFactory->create(ctx->engine, &module)); - VM::Function *vmf = isel->vmFunction(irf); - - return Value::fromObject(ctx->engine->newScriptFunction(ctx->engine->rootContext, vmf)); -} - -// 15.3.1: This is equivalent to new Function(...) -Value FunctionCtor::call(ExecutionContext *ctx) -{ - return construct(ctx); -} - -void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2); - defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 1); - defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 1); - - defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(0)); -} - -Value FunctionPrototype::method_toString(ExecutionContext *ctx) -{ - FunctionObject *fun = ctx->thisObject.asFunctionObject(); - if (!fun) - ctx->throwTypeError(); - - return Value::fromString(ctx, QStringLiteral("function() { [code] }")); -} - -Value FunctionPrototype::method_apply(ExecutionContext *ctx) -{ - Value thisArg = ctx->argument(0); - - Value arg = ctx->argument(1); - QVector args; - - if (Object *arr = arg.asObject()) { - quint32 len = arr->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); - for (quint32 i = 0; i < len; ++i) { - Value a = arr->__get__(ctx, i); - args.append(a); - } - } else if (!(arg.isUndefined() || arg.isNull())) { - ctx->throwTypeError(); - return Value::undefinedValue(); - } - - FunctionObject *o = ctx->thisObject.asFunctionObject(); - if (!o) - ctx->throwTypeError(); - - return o->call(ctx, thisArg, args.data(), args.size()); -} - -Value FunctionPrototype::method_call(ExecutionContext *ctx) -{ - Value thisArg = ctx->argument(0); - - QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); - if (ctx->argumentCount) - qCopy(ctx->arguments + 1, - ctx->arguments + ctx->argumentCount, args.begin()); - - FunctionObject *o = ctx->thisObject.asFunctionObject(); - if (!o) - ctx->throwTypeError(); - - return o->call(ctx, thisArg, args.data(), args.size()); -} - -Value FunctionPrototype::method_bind(ExecutionContext *ctx) -{ - FunctionObject *target = ctx->thisObject.asFunctionObject(); - if (!target) - ctx->throwTypeError(); - - Value boundThis = ctx->argument(0); - QVector boundArgs; - for (uint i = 1; i < ctx->argumentCount; ++i) - boundArgs += ctx->argument(i); - - - BoundFunction *f = ctx->engine->newBoundFunction(ctx, target, boundThis, boundArgs); - return Value::fromObject(f); -} - -ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) - : FunctionObject(scope) - , function(function) -{ - assert(function); - assert(function->code); - - // global function - if (!scope) - return; - - MemoryManager::GCBlocker gcBlocker(scope->engine->memoryManager); - - name = function->name; - needsActivation = function->needsActivation(); - usesArgumentsObject = function->usesArgumentsObject; - strictMode = function->isStrict; - formalParameterCount = function->formals.size(); - formalParameterList = function->formals.constData(); - defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(formalParameterCount)); - - varCount = function->locals.size(); - varList = function->locals.constData(); - - Object *proto = scope->engine->newObject(); - proto->defineDefaultProperty(scope->engine->id_constructor, Value::fromObject(this)); - PropertyDescriptor *pd = members->insert(scope->engine->id_prototype); - pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Enabled; - pd->enumberable = PropertyDescriptor::Disabled; - pd->configurable = PropertyDescriptor::Disabled; - pd->value = Value::fromObject(proto); - - if (scope->strictMode) { - FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); - pd.configurable = PropertyDescriptor::Disabled; - pd.enumberable = PropertyDescriptor::Disabled; - __defineOwnProperty__(scope, QStringLiteral("caller"), &pd); - __defineOwnProperty__(scope, QStringLiteral("arguments"), &pd); - } -} - -ScriptFunction::~ScriptFunction() -{ -} - -Value ScriptFunction::call(VM::ExecutionContext *ctx) -{ - assert(function->code); - return function->code(ctx, function->codeData); -} - -void ScriptFunction::markObjects() -{ - function->mark(); - FunctionObject::markObjects(); -} - - -BuiltinFunctionOld::BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) - : FunctionObject(scope) - , code(code) -{ - this->name = name; - isBuiltinFunction = true; -} - -Value BuiltinFunctionOld::construct(ExecutionContext *ctx) -{ - ctx->throwTypeError(); - return Value::undefinedValue(); -} - -BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)) - : FunctionObject(scope) - , code(code) -{ - this->name = name; - isBuiltinFunction = true; -} - -Value BuiltinFunction::construct(ExecutionContext *ctx) -{ - ctx->throwTypeError(); - return Value::undefinedValue(); -} - - -BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) - : FunctionObject(scope) - , target(target) - , boundThis(boundThis) - , boundArgs(boundArgs) -{ - int len = target->__get__(scope, scope->engine->id_length).toUInt32(scope); - len -= boundArgs.size(); - if (len < 0) - len = 0; - defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(len)); - - FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); - pd.configurable = PropertyDescriptor::Disabled; - pd.enumberable = PropertyDescriptor::Disabled; - *members->insert(scope->engine->id_arguments) = pd; - *members->insert(scope->engine->id_caller) = pd; -} - -Value BoundFunction::call(ExecutionContext *context, Value, Value *args, int argc) -{ - Value *newArgs = static_cast(alloca(sizeof(Value)*(boundArgs.size() + argc))); - memcpy(newArgs, boundArgs.constData(), boundArgs.size()*sizeof(Value)); - memcpy(newArgs + boundArgs.size(), args, argc*sizeof(Value)); - - return target->call(context, boundThis, newArgs, boundArgs.size() + argc); -} - -Value BoundFunction::construct(ExecutionContext *context, Value *args, int argc) -{ - Value *newArgs = static_cast(alloca(sizeof(Value)*(boundArgs.size() + argc))); - memcpy(newArgs, boundArgs.constData(), boundArgs.size()*sizeof(Value)); - memcpy(newArgs + boundArgs.size(), args, argc*sizeof(Value)); - - return target->construct(context, newArgs, boundArgs.size() + argc); -} - -bool BoundFunction::hasInstance(ExecutionContext *ctx, const Value &value) -{ - return target->hasInstance(ctx, value); -} - -void BoundFunction::markObjects() -{ - target->mark(); - if (Managed *m = boundThis.asManaged()) - m->mark(); - for (int i = 0; i < boundArgs.size(); ++i) - if (Managed *m = boundArgs.at(i).asManaged()) - m->mark(); - FunctionObject::markObjects(); -} diff --git a/qv4functionobject.h b/qv4functionobject.h deleted file mode 100644 index 822cee9491..0000000000 --- a/qv4functionobject.h +++ /dev/null @@ -1,225 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4FUNCTIONOBJECT_H -#define QV4FUNCTIONOBJECT_H - -#include "qmljs_runtime.h" -#include "qmljs_engine.h" -#include "qmljs_environment.h" -#include "qv4object.h" -#include "qv4array.h" -#include "qv4string.h" -#include "qv4codegen_p.h" -#include "qv4isel_p.h" -#include "qv4managed.h" -#include "qv4propertydescriptor.h" -#include "qv4propertytable.h" -#include "qv4objectiterator.h" -#include "qv4regexp.h" - -#include -#include -#include -#include -#include - -namespace QQmlJS { - -namespace VM { - -struct Value; -struct Function; -struct Object; -struct ObjectIterator; -struct BooleanObject; -struct NumberObject; -struct StringObject; -struct ArrayObject; -struct DateObject; -struct FunctionObject; -struct RegExpObject; -struct ErrorObject; -struct ArgumentsObject; -struct ExecutionContext; -struct ExecutionEngine; -class MemoryManager; - -struct ObjectPrototype; -struct StringPrototype; -struct NumberPrototype; -struct BooleanPrototype; -struct ArrayPrototype; -struct FunctionPrototype; -struct DatePrototype; -struct RegExpPrototype; -struct ErrorPrototype; -struct EvalErrorPrototype; -struct RangeErrorPrototype; -struct ReferenceErrorPrototype; -struct SyntaxErrorPrototype; -struct TypeErrorPrototype; -struct URIErrorPrototype; - -struct Function { - String *name; - - VM::Value (*code)(VM::ExecutionContext *, const uchar *); - const uchar *codeData; - JSC::MacroAssemblerCodeRef codeRef; - - QVector formals; - QVector locals; - QVector generatedValues; - QVector identifiers; - - bool hasNestedFunctions : 1; - bool hasDirectEval : 1; - bool usesArgumentsObject : 1; - bool isStrict : 1; - - Function(String *name) - : name(name) - , code(0) - , codeData(0) - , hasNestedFunctions(0) - , hasDirectEval(false) - , usesArgumentsObject(false) - , isStrict(false) - {} - ~Function(); - - inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; } - - void mark(); -}; - -struct FunctionObject: Object { - ExecutionContext *scope; - String *name; - String * const *formalParameterList; - String * const *varList; - unsigned int formalParameterCount; - unsigned int varCount; - - FunctionObject(ExecutionContext *scope); - - virtual bool hasInstance(ExecutionContext *ctx, const Value &value); - - virtual Value construct(ExecutionContext *context, Value *args, int argc); - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); - - virtual struct ScriptFunction *asScriptFunction() { return 0; } - - virtual void markObjects(); - -protected: - virtual Value call(ExecutionContext *ctx); - virtual Value construct(ExecutionContext *ctx); -}; - -struct FunctionCtor: FunctionObject -{ - FunctionCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct FunctionPrototype: FunctionObject -{ - FunctionPrototype(ExecutionContext *ctx): FunctionObject(ctx) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_toString(ExecutionContext *ctx); - static Value method_apply(ExecutionContext *ctx); - static Value method_call(ExecutionContext *ctx); - static Value method_bind(ExecutionContext *ctx); -}; - -struct BuiltinFunctionOld: FunctionObject { - Value (*code)(ExecutionContext *); - - BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); - virtual Value call(ExecutionContext *ctx) { return code(ctx); } - virtual Value construct(ExecutionContext *ctx); -}; - -struct BuiltinFunction: FunctionObject { - Value (*code)(ExecutionContext *parentContext, Value thisObject, Value *args, int argc); - - BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)); - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc) { - return code(context, thisObject, args, argc); - } - virtual Value construct(ExecutionContext *ctx); -}; - -struct ScriptFunction: FunctionObject { - VM::Function *function; - - ScriptFunction(ExecutionContext *scope, VM::Function *function); - virtual ~ScriptFunction(); - - virtual Value call(ExecutionContext *ctx); - - virtual ScriptFunction *asScriptFunction() { return this; } - - virtual void markObjects(); -}; - -struct BoundFunction: FunctionObject { - FunctionObject *target; - Value boundThis; - QVector boundArgs; - - BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); - virtual ~BoundFunction() {} - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); - virtual Value construct(ExecutionContext *context, Value *args, int argc); - virtual bool hasInstance(ExecutionContext *ctx, const Value &value); - virtual void markObjects(); -}; - -} // namespace VM -} // namespace QQmlJS - -#endif // QMLJS_OBJECTS_H diff --git a/qv4globalobject.cpp b/qv4globalobject.cpp deleted file mode 100644 index 74cd32d88a..0000000000 --- a/qv4globalobject.cpp +++ /dev/null @@ -1,670 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4globalobject.h" -#include "qv4mm.h" -#include "qmljs_value.h" -#include "qmljs_environment.h" - -#include -#include -#include -#include -#include -#include -#include "private/qlocale_tools_p.h" - -#include -#include -#include -#include - -using namespace QQmlJS::VM; - -static inline char toHex(char c) -{ - static const char hexnumbers[] = "0123456789ABCDEF"; - return hexnumbers[c & 0xf]; -} - -static int fromHex(QChar ch) -{ - ushort c = ch.unicode(); - if ((c >= '0') && (c <= '9')) - return c - '0'; - if ((c >= 'A') && (c <= 'F')) - return c - 'A' + 10; - if ((c >= 'a') && (c <= 'f')) - return c - 'a' + 10; - return -1; -} - -static QString escape(const QString &input) -{ - QString output; - output.reserve(input.size() * 3); - const int length = input.length(); - for (int i = 0; i < length; ++i) { - ushort uc = input.at(i).unicode(); - if (uc < 0x100) { - if ( (uc > 0x60 && uc < 0x7B) - || (uc > 0x3F && uc < 0x5B) - || (uc > 0x2C && uc < 0x3A) - || (uc == 0x2A) - || (uc == 0x2B) - || (uc == 0x5F)) { - output.append(QChar(uc)); - } else { - output.append('%'); - output.append(QChar(toHex(uc >> 4))); - output.append(QChar(toHex(uc))); - } - } else { - output.append('%'); - output.append('u'); - output.append(QChar(toHex(uc >> 12))); - output.append(QChar(toHex(uc >> 8))); - output.append(QChar(toHex(uc >> 4))); - output.append(QChar(toHex(uc))); - } - } - return output; -} - -static QString unescape(const QString &input) -{ - QString result; - result.reserve(input.length()); - int i = 0; - const int length = input.length(); - while (i < length) { - QChar c = input.at(i++); - if ((c == '%') && (i + 1 < length)) { - QChar a = input.at(i); - if ((a == 'u') && (i + 4 < length)) { - int d3 = fromHex(input.at(i+1)); - int d2 = fromHex(input.at(i+2)); - int d1 = fromHex(input.at(i+3)); - int d0 = fromHex(input.at(i+4)); - if ((d3 != -1) && (d2 != -1) && (d1 != -1) && (d0 != -1)) { - ushort uc = ushort((d3 << 12) | (d2 << 8) | (d1 << 4) | d0); - result.append(QChar(uc)); - i += 5; - } else { - result.append(c); - } - } else { - int d1 = fromHex(a); - int d0 = fromHex(input.at(i+1)); - if ((d1 != -1) && (d0 != -1)) { - c = (d1 << 4) | d0; - i += 2; - } - result.append(c); - } - } else { - result.append(c); - } - } - return result; -} - -static const char uriReserved[] = ";/?:@&=+$,"; -static const char uriUnescaped[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()"; - -static void addEscapeSequence(QString &output, uchar ch) -{ - output.append(QLatin1Char('%')); - output.append(QLatin1Char(toHex(ch >> 4))); - output.append(QLatin1Char(toHex(ch & 0xf))); -} - -static QString encode(const QString &input, const QString &unescapedSet, bool *ok) -{ - *ok = true; - QString output; - const int length = input.length(); - int i = 0; - while (i < length) { - const QChar c = input.at(i); - if (!unescapedSet.contains(c)) { - uint uc = c.unicode(); - if ((uc >= 0xDC00) && (uc <= 0xDFFF)) { - *ok = false; - break; - } - if (!((uc < 0xD800) || (uc > 0xDBFF))) { - ++i; - if (i == length) { - *ok = false; - break; - } - const uint uc2 = input.at(i).unicode(); - if ((uc2 < 0xDC00) || (uc2 > 0xDFFF)) { - *ok = false; - break; - } - uc = ((uc - 0xD800) * 0x400) + (uc2 - 0xDC00) + 0x10000; - } - if (uc < 0x80) { - addEscapeSequence(output, (uchar)uc); - } else { - if (uc < 0x0800) { - addEscapeSequence(output, 0xc0 | ((uchar) (uc >> 6))); - } else { - - if (QChar::requiresSurrogates(uc)) { - addEscapeSequence(output, 0xf0 | ((uchar) (uc >> 18))); - addEscapeSequence(output, 0x80 | (((uchar) (uc >> 12)) & 0x3f)); - } else { - addEscapeSequence(output, 0xe0 | (((uchar) (uc >> 12)) & 0x3f)); - } - addEscapeSequence(output, 0x80 | (((uchar) (uc >> 6)) & 0x3f)); - } - addEscapeSequence(output, 0x80 | ((uchar) (uc&0x3f))); - } - } else { - output.append(c); - } - ++i; - } - if (i != length) - *ok = false; - return output; -} - -static QString decode(const QString &input, const QString &reservedSet, bool *ok) -{ - *ok = true; - QString output; - const int length = input.length(); - int i = 0; - const QChar percent = QLatin1Char('%'); - while (i < length) { - const QChar ch = input.at(i); - if (ch == percent) { - int start = i; - if (i + 2 >= length) - goto error; - - int d1 = fromHex(input.at(i+1)); - int d0 = fromHex(input.at(i+2)); - if ((d1 == -1) || (d0 == -1)) - goto error; - - int b = (d1 << 4) | d0; - i += 2; - if (b & 0x80) { - int uc; - int min_uc; - int need; - if ((b & 0xe0) == 0xc0) { - uc = b & 0x1f; - need = 1; - min_uc = 0x80; - } else if ((b & 0xf0) == 0xe0) { - uc = b & 0x0f; - need = 2; - min_uc = 0x800; - } else if ((b & 0xf8) == 0xf0) { - uc = b & 0x07; - need = 3; - min_uc = 0x10000; - } else { - goto error; - } - - if (i + (3 * need) >= length) - goto error; - - for (int j = 0; j < need; ++j) { - ++i; - if (input.at(i) != percent) - goto error; - - d1 = fromHex(input.at(i+1)); - d0 = fromHex(input.at(i+2)); - if ((d1 == -1) || (d0 == -1)) - goto error; - - b = (d1 << 4) | d0; - if ((b & 0xC0) != 0x80) - goto error; - - i += 2; - uc = (uc << 6) + (b & 0x3f); - } - if (uc < min_uc) - goto error; - - if (uc < 0x10000) { - output.append(QChar(uc)); - } else { - if (uc > 0x10FFFF) - goto error; - - ushort l = ushort(((uc - 0x10000) & 0x3FF) + 0xDC00); - ushort h = ushort((((uc - 0x10000) >> 10) & 0x3FF) + 0xD800); - output.append(QChar(h)); - output.append(QChar(l)); - } - } else { - QChar z(b); - if (!reservedSet.contains(z)) { - output.append(z); - } else { - output.append(input.mid(start, i - start + 1)); - } - } - } else { - output.append(ch); - } - ++i; - } - if (i != length) - *ok = false; - return output; - error: - *ok = false; - return QString(); -} - - -EvalFunction::EvalFunction(ExecutionContext *scope) - : FunctionObject(scope) -{ - name = scope->engine->id_eval; - defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); -} - -Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc, bool directCall) -{ - if (argc < 1) - return Value::undefinedValue(); - - ExecutionContext *ctx = context; - - if (!directCall) { - // the context for eval should be the global scope - while (ctx->parent) - ctx = ctx->parent; - } - - if (!args[0].isString()) - return args[0]; - - const QString code = args[0].stringValue()->toQString(); - bool inheritContext = !ctx->strictMode; - - QQmlJS::VM::Function *f = parseSource(context, QStringLiteral("eval code"), - code, QQmlJS::Codegen::EvalCode, - (directCall && context->strictMode), inheritContext); - - if (!f) - return Value::undefinedValue(); - - bool strict = f->isStrict || (directCall && context->strictMode); - - uint size = requiredMemoryForExecutionContect(this, argc); - ExecutionContext *k = static_cast(alloca(size)); - - if (strict) { - ctx = k; - ctx->initCallContext(context, context->thisObject, this, args, argc); - } - - // set the correct strict mode flag on the context - bool cstrict = ctx->strictMode; - ctx->strictMode = strict; - - Value result = f->code(ctx, f->codeData); - - ctx->strictMode = cstrict; - - if (strict) - ctx->leaveCallContext(); - - return result; -} - - -Value EvalFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) -{ - // indirect call - return evalCall(context, thisObject, args, argc, false); -} - -Value EvalFunction::construct(ExecutionContext *ctx) -{ - ctx->throwTypeError(); - return Value::undefinedValue(); -} - -QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, - const QString &fileName, const QString &source, - QQmlJS::Codegen::Mode mode, - bool strictMode, bool inheritContext) -{ - using namespace QQmlJS; - - MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); - - VM::ExecutionEngine *vm = ctx->engine; - IR::Module module; - VM::Function *globalCode = 0; - - { - QQmlJS::Engine ee, *engine = ⅇ - Lexer lexer(engine); - lexer.setCode(source, 1, false); - Parser parser(engine); - - const bool parsed = parser.parseProgram(); - - VM::DiagnosticMessage *error = 0, **errIt = &error; - foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { - if (m.isError()) { - *errIt = new VM::DiagnosticMessage; - (*errIt)->fileName = fileName; - (*errIt)->offset = m.loc.offset; - (*errIt)->length = m.loc.length; - (*errIt)->startLine = m.loc.startLine; - (*errIt)->startColumn = m.loc.startColumn; - (*errIt)->type = VM::DiagnosticMessage::Error; - (*errIt)->message = m.message; - errIt = &(*errIt)->next; - } else { - std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn - << ": warning: " << qPrintable(m.message) << std::endl; - } - } - if (error) - ctx->throwSyntaxError(error); - - if (parsed) { - using namespace AST; - Program *program = AST::cast(parser.rootNode()); - if (!program) { - // if parsing was successful, and we have no program, then - // we're done...: - return 0; - } - - QStringList inheritedLocals; - if (inheritContext) - for (String * const *i = ctx->variables(), * const *ei = i + ctx->variableCount(); i < ei; ++i) - inheritedLocals.append(*i ? (*i)->toQString() : QString()); - - Codegen cg(ctx, strictMode); - IR::Function *globalIRCode = cg(fileName, program, &module, mode, inheritedLocals); - QScopedPointer isel(ctx->engine->iselFactory->create(vm, &module)); - if (globalIRCode) - globalCode = isel->vmFunction(globalIRCode); - } - - if (! globalCode) - // ### should be a syntax error - __qmljs_throw_type_error(ctx); - } - - return globalCode; -} - -static inline int toInt(const QChar &qc, int R) -{ - ushort c = qc.unicode(); - int v = -1; - if (c >= '0' && c <= '9') - v = c - '0'; - else if (c >= 'A' && c <= 'Z') - v = c - 'A' + 10; - else if (c >= 'a' && c <= 'z') - v = c - 'a' + 10; - if (v >= 0 && v < R) - return v; - else - return -1; -} - -// parseInt [15.1.2.2] -Value GlobalFunctions::method_parseInt(ExecutionContext *context) -{ - Value string = context->argument(0); - Value radix = context->argument(1); - int R = radix.isUndefined() ? 0 : radix.toInt32(context); - - // [15.1.2.2] step by step: - String *inputString = string.toString(context); // 1 - QString trimmed = inputString->toQString().trimmed(); // 2 - const QChar *pos = trimmed.constData(); - const QChar *end = pos + trimmed.length(); - - int sign = 1; // 3 - if (pos != end) { - if (*pos == QLatin1Char('-')) - sign = -1; // 4 - if (*pos == QLatin1Char('-') || *pos == QLatin1Char('+')) - ++pos; // 5 - } - bool stripPrefix = true; // 7 - if (R) { // 8 - if (R < 2 || R > 36) - return Value::fromDouble(nan("")); // 8a - if (R != 16) - stripPrefix = false; // 8b - } else { // 9 - R = 10; // 9a - } - if (stripPrefix) { // 10 - if ((end - pos >= 2) - && (pos[0] == QLatin1Char('0')) - && (pos[1] == QLatin1Char('x') || pos[1] == QLatin1Char('X'))) { // 10a - pos += 2; - R = 16; - } - } - // 11: Z is progressively built below - // 13: this is handled by the toInt function - if (pos == end) // 12 - return Value::fromDouble(nan("")); - bool overflow = false; - qint64 v_overflow; - unsigned overflow_digit_count = 0; - int d = toInt(*pos++, R); - if (d == -1) - return Value::fromDouble(nan("")); - qint64 v = d; - while (pos != end) { - d = toInt(*pos++, R); - if (d == -1) - break; - if (overflow) { - if (overflow_digit_count == 0) { - v_overflow = v; - v = 0; - } - ++overflow_digit_count; - v = v * R + d; - } else { - qint64 vNew = v * R + d; - if (vNew < v) { - overflow = true; - --pos; - } else { - v = vNew; - } - } - } - - if (overflow) { - double result = (double) v_overflow * pow(R, overflow_digit_count); - result += v; - return Value::fromDouble(sign * result); - } else { - return Value::fromDouble(sign * (double) v); // 15 - } -} - -// parseFloat [15.1.2.3] -Value GlobalFunctions::method_parseFloat(ExecutionContext *context) -{ - Value string = context->argument(0); - - // [15.1.2.3] step by step: - String *inputString = string.toString(context); // 1 - QString trimmed = inputString->toQString().trimmed(); // 2 - - // 4: - if (trimmed.startsWith(QLatin1String("Infinity")) - || trimmed.startsWith(QLatin1String("+Infinity"))) - return Value::fromDouble(INFINITY); - if (trimmed.startsWith("-Infinity")) - return Value::fromDouble(-INFINITY); - QByteArray ba = trimmed.toLatin1(); - bool ok; - const char *begin = ba.constData(); - const char *end = 0; - double d = qstrtod(begin, &end, &ok); - if (end - begin == 0) - return Value::fromDouble(nan("")); // 3 - else - return Value::fromDouble(d); -} - -/// isNaN [15.1.2.4] -Value GlobalFunctions::method_isNaN(ExecutionContext *context) -{ - const Value &v = context->argument(0); - if (v.integerCompatible()) - return Value::fromBoolean(false); - - double d = v.toNumber(context); - return Value::fromBoolean(std::isnan(d)); -} - -/// isFinite [15.1.2.5] -Value GlobalFunctions::method_isFinite(ExecutionContext *context) -{ - const Value &v = context->argument(0); - if (v.integerCompatible()) - return Value::fromBoolean(true); - - double d = v.toNumber(context); - return Value::fromBoolean(std::isfinite(d)); -} - -/// decodeURI [15.1.3.1] -Value GlobalFunctions::method_decodeURI(ExecutionContext *parentCtx, Value, Value *argv, int argc) -{ - if (argc == 0) - return Value::undefinedValue(); - - QString uriString = argv[0].toString(parentCtx)->toQString(); - bool ok; - QString out = decode(uriString, QString::fromUtf8(uriReserved) + QString::fromUtf8("#"), &ok); - if (!ok) - parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); - - return Value::fromString(parentCtx, out); -} - -/// decodeURIComponent [15.1.3.2] -Value GlobalFunctions::method_decodeURIComponent(ExecutionContext *parentCtx, Value, Value *argv, int argc) -{ - if (argc == 0) - return Value::undefinedValue(); - - QString uriString = argv[0].toString(parentCtx)->toQString(); - bool ok; - QString out = decode(uriString, QString(), &ok); - if (!ok) - parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); - - return Value::fromString(parentCtx, out); -} - -/// encodeURI [15.1.3.3] -Value GlobalFunctions::method_encodeURI(ExecutionContext *parentCtx, Value, Value *argv, int argc) -{ - if (argc == 0) - return Value::undefinedValue(); - - QString uriString = argv[0].toString(parentCtx)->toQString(); - bool ok; - QString out = encode(uriString, QLatin1String(uriReserved) + QLatin1String(uriUnescaped) + QString::fromUtf8("#"), &ok); - if (!ok) - parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); - - return Value::fromString(parentCtx, out); -} - -/// encodeURIComponent [15.1.3.4] -Value GlobalFunctions::method_encodeURIComponent(ExecutionContext *parentCtx, Value, Value *argv, int argc) -{ - if (argc == 0) - return Value::undefinedValue(); - - QString uriString = argv[0].toString(parentCtx)->toQString(); - bool ok; - QString out = encode(uriString, QString(uriUnescaped), &ok); - if (!ok) - parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); - - return Value::fromString(parentCtx, out); -} - -Value GlobalFunctions::method_escape(ExecutionContext *context) -{ - if (!context->argumentCount) - return Value::fromString(context, QStringLiteral("undefined")); - - QString str = context->argument(0).toString(context)->toQString(); - return Value::fromString(context, escape(str)); -} - -Value GlobalFunctions::method_unescape(ExecutionContext *context) -{ - if (!context->argumentCount) - return Value::fromString(context, QStringLiteral("undefined")); - - QString str = context->argument(0).toString(context)->toQString(); - return Value::fromString(context, unescape(str)); -} diff --git a/qv4globalobject.h b/qv4globalobject.h deleted file mode 100644 index fecad46daa..0000000000 --- a/qv4globalobject.h +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4GLOBALOBJECT_H -#define QV4GLOBALOBJECT_H - -#include "qv4functionobject.h" - -namespace QQmlJS { - -namespace VM { - -struct EvalFunction : FunctionObject -{ - EvalFunction(ExecutionContext *scope); - - static QQmlJS::VM::Function *parseSource(QQmlJS::VM::ExecutionContext *ctx, - const QString &fileName, - const QString &source, - QQmlJS::Codegen::Mode mode, bool strictMode, - bool inheritContext); - - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); - Value evalCall(ExecutionContext *context, Value thisObject, Value *args, int argc, bool directCall); - - Value construct(ExecutionContext *ctx); -}; - -struct GlobalFunctions -{ - static Value method_parseInt(ExecutionContext *context); - static Value method_parseFloat(ExecutionContext *context); - static Value method_isNaN(ExecutionContext *context); - static Value method_isFinite(ExecutionContext *context); - static Value method_decodeURI(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_decodeURIComponent(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_encodeURI(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_encodeURIComponent(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_escape(ExecutionContext *context); - static Value method_unescape(ExecutionContext *context); -}; - -} // namespace VM -} // namespace QQmlJS - -#endif // QMLJS_OBJECTS_H diff --git a/qv4ir.cpp b/qv4ir.cpp deleted file mode 100644 index 1c2a9d9e62..0000000000 --- a/qv4ir.cpp +++ /dev/null @@ -1,670 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4ir_p.h" -#include - -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QQmlJS { -namespace IR { - -const char *typeName(Type t) -{ - switch (t) { - case UndefinedType: return "undefined"; - case NullType: return "null"; - case BoolType: return "bool"; - case NumberType: return "number"; - default: return "invalid"; - } -} - -const char *opname(AluOp op) -{ - switch (op) { - case OpInvalid: return "?"; - - case OpIfTrue: return "(bool)"; - case OpNot: return "!"; - case OpUMinus: return "-"; - case OpUPlus: return "+"; - case OpCompl: return "~"; - case OpIncrement: return "++"; - case OpDecrement: return "--"; - - case OpBitAnd: return "&"; - case OpBitOr: return "|"; - case OpBitXor: return "^"; - - case OpAdd: return "+"; - case OpSub: return "-"; - case OpMul: return "*"; - case OpDiv: return "/"; - case OpMod: return "%"; - - case OpLShift: return "<<"; - case OpRShift: return ">>"; - case OpURShift: return ">>>"; - - case OpGt: return ">"; - case OpLt: return "<"; - case OpGe: return ">="; - case OpLe: return "<="; - case OpEqual: return "=="; - case OpNotEqual: return "!="; - case OpStrictEqual: return "==="; - case OpStrictNotEqual: return "!=="; - - case OpInstanceof: return "instanceof"; - case OpIn: return "in"; - - case OpAnd: return "&&"; - case OpOr: return "||"; - - default: return "?"; - - } // switch -} - -AluOp binaryOperator(int op) -{ - switch (static_cast(op)) { - case QSOperator::Add: return OpAdd; - case QSOperator::And: return OpAnd; - case QSOperator::BitAnd: return OpBitAnd; - case QSOperator::BitOr: return OpBitOr; - case QSOperator::BitXor: return OpBitXor; - case QSOperator::Div: return OpDiv; - case QSOperator::Equal: return OpEqual; - case QSOperator::Ge: return OpGe; - case QSOperator::Gt: return OpGt; - case QSOperator::Le: return OpLe; - case QSOperator::LShift: return OpLShift; - case QSOperator::Lt: return OpLt; - case QSOperator::Mod: return OpMod; - case QSOperator::Mul: return OpMul; - case QSOperator::NotEqual: return OpNotEqual; - case QSOperator::Or: return OpOr; - case QSOperator::RShift: return OpRShift; - case QSOperator::StrictEqual: return OpStrictEqual; - case QSOperator::StrictNotEqual: return OpStrictNotEqual; - case QSOperator::Sub: return OpSub; - case QSOperator::URShift: return OpURShift; - case QSOperator::InstanceOf: return OpInstanceof; - case QSOperator::In: return OpIn; - default: return OpInvalid; - } -} - -void Const::dump(QTextStream &out) -{ - switch (type) { - case QQmlJS::IR::UndefinedType: - out << "undefined"; - break; - case QQmlJS::IR::NullType: - out << "null"; - break; - case QQmlJS::IR::BoolType: - out << (value ? "true" : "false"); - break; - default: - out << QString::number(value, 'g', 16); - break; - } -} - -void String::dump(QTextStream &out) -{ - out << '"' << escape(*value) << '"'; -} - -QString String::escape(const QString &s) -{ - QString r; - for (int i = 0; i < s.length(); ++i) { - const QChar ch = s.at(i); - if (ch == QLatin1Char('\n')) - r += QStringLiteral("\\n"); - else if (ch == QLatin1Char('\r')) - r += QStringLiteral("\\r"); - else if (ch == QLatin1Char('\\')) - r += QStringLiteral("\\\\"); - else if (ch == QLatin1Char('"')) - r += QStringLiteral("\\\""); - else if (ch == QLatin1Char('\'')) - r += QStringLiteral("\\'"); - else - r += ch; - } - return r; -} - -void RegExp::dump(QTextStream &out) -{ - char f[3]; - int i = 0; - if (flags & RegExp_Global) - f[i++] = 'g'; - if (flags & RegExp_IgnoreCase) - f[i++] = 'i'; - if (flags & RegExp_Multiline) - f[i++] = 'm'; - f[i] = 0; - - out << '/' << *value << '/' << f; -} - -void Name::init(const QString *id, quint32 line, quint32 column) -{ - this->id = id; - this->builtin = builtin_invalid; - this->line = line; - this->column = column; -} - -void Name::init(Builtin builtin, quint32 line, quint32 column) -{ - this->id = 0; - this->builtin = builtin; - this->line = line; - this->column = column; -} - -static const char *builtin_to_string(Name::Builtin b) -{ - switch (b) { - case Name::builtin_invalid: - return "builtin_invalid"; - case Name::builtin_typeof: - return "builtin_typeof"; - case Name::builtin_delete: - return "builtin_delete"; - case Name::builtin_postincrement: - return "builtin_postincrement"; - case Name::builtin_postdecrement: - return "builtin_postdecrement"; - case Name::builtin_throw: - return "builtin_throw"; - case Name::builtin_create_exception_handler: - return "builtin_create_exception_handler"; - case Name::builtin_delete_exception_handler: - return "builtin_delete_exception_handler"; - case Name::builtin_get_exception: - return "builtin_get_exception"; - case IR::Name::builtin_foreach_iterator_object: - return "builtin_foreach_iterator_object"; - case IR::Name::builtin_foreach_next_property_name: - return "builtin_foreach_next_property_name"; - case IR::Name::builtin_push_with_scope: - return "builtin_push_with_scope"; - case IR::Name::builtin_pop_scope: - return "builtin_pop_scope"; - case IR::Name::builtin_declare_vars: - return "builtin_declare_vars"; - case IR::Name::builtin_define_property: - return "builtin_define_property"; - case IR::Name::builtin_define_array_property: - return "builtin_define_array_property"; - case IR::Name::builtin_define_getter_setter: - return "builtin_define_getter_setter"; - } - return "builtin_(###FIXME)"; -}; - - -void Name::dump(QTextStream &out) -{ - if (id) - out << *id; - else - out << builtin_to_string(builtin); -} - -void Temp::dump(QTextStream &out) -{ - if (index < 0) { - out << '#' << -(index + 1); // negative and 1-based. - } else { - out << '%' << index; // temp - } -} - -void Closure::dump(QTextStream &out) -{ - QString name = value->name ? *value->name : QString(); - if (name.isEmpty()) - name.sprintf("%p", value); - out << "closure(" << name << ')'; -} - -void Unop::dump(QTextStream &out) -{ - out << opname(op); - expr->dump(out); -} - -void Binop::dump(QTextStream &out) -{ - left->dump(out); - out << ' ' << opname(op) << ' '; - right->dump(out); -} - -void Call::dump(QTextStream &out) -{ - base->dump(out); - out << '('; - for (ExprList *it = args; it; it = it->next) { - if (it != args) - out << ", "; - it->expr->dump(out); - } - out << ')'; -} - -void New::dump(QTextStream &out) -{ - out << "new "; - base->dump(out); - out << '('; - for (ExprList *it = args; it; it = it->next) { - if (it != args) - out << ", "; - it->expr->dump(out); - } - out << ')'; -} - -void Subscript::dump(QTextStream &out) -{ - base->dump(out); - out << '['; - index->dump(out); - out << ']'; -} - -void Member::dump(QTextStream &out) -{ - base->dump(out); - out << '.' << *name; -} - -void Exp::dump(QTextStream &out, Mode) -{ - out << "(void) "; - expr->dump(out); - out << ';'; -} - -void Enter::dump(QTextStream &out, Mode) -{ - out << "%enter("; - expr->dump(out); - out << ");"; -} - -void Leave::dump(QTextStream &out, Mode) -{ - out << "%leave"; - out << ';'; -} - -void Move::dump(QTextStream &out, Mode) -{ - target->dump(out); - out << ' '; - if (op != OpInvalid) - out << opname(op); - out << "= "; -// if (source->type != target->type) -// out << typeName(source->type) << "_to_" << typeName(target->type) << '('; - source->dump(out); -// if (source->type != target->type) -// out << ')'; - out << ';'; -} - -void Jump::dump(QTextStream &out, Mode mode) -{ - Q_UNUSED(mode); - out << "goto " << 'L' << target->index << ';'; -} - -void CJump::dump(QTextStream &out, Mode mode) -{ - Q_UNUSED(mode); - out << "if ("; - cond->dump(out); - if (mode == HIR) - out << ") goto " << 'L' << iftrue->index << "; else goto " << 'L' << iffalse->index << ';'; - else - out << ") goto " << 'L' << iftrue->index << ";"; -} - -void Ret::dump(QTextStream &out, Mode) -{ - out << "return"; - if (expr) { - out << ' '; - expr->dump(out); - } - out << ';'; -} - -Function *Module::newFunction(const QString &name, Function *outer) -{ - Function *f = new Function(this, name); - functions.append(f); - if (!outer) { - assert(!rootFunction); - rootFunction = f; - } else { - outer->nestedFunctions.append(f); - } - return f; -} - -Module::~Module() -{ - foreach (Function *f, functions) { - delete f; - } -} - -Function::~Function() -{ - // destroy the Stmt::Data blocks manually, because memory pool cleanup won't - // call the Stmt destructors. - foreach (IR::BasicBlock *b, basicBlocks) - foreach (IR::Stmt *s, b->statements) - s->destroyData(); - - qDeleteAll(basicBlocks); - pool = 0; - module = 0; -} - - -const QString *Function::newString(const QString &text) -{ - return &*strings.insert(text); -} - -BasicBlock *Function::newBasicBlock(BasicBlockInsertMode mode) -{ - BasicBlock *block = new BasicBlock(this); - return mode == InsertBlock ? insertBasicBlock(block) : block; -} - -void Function::dump(QTextStream &out, Stmt::Mode mode) -{ - QString n = name ? *name : QString(); - if (n.isEmpty()) - n.sprintf("%p", this); - out << "function " << n << "() {" << endl; - foreach (const QString *formal, formals) - out << "\treceive " << *formal << ';' << endl; - foreach (const QString *local, locals) - out << "\tlocal " << *local << ';' << endl; - foreach (BasicBlock *bb, basicBlocks) - bb->dump(out, mode); - out << '}' << endl; -} - -unsigned BasicBlock::newTemp() -{ - return function->tempCount++; -} - -Temp *BasicBlock::TEMP(int index) -{ - Temp *e = function->New(); - e->init(index); - return e; -} - -Expr *BasicBlock::CONST(Type type, double value) -{ - Const *e = function->New(); - e->init(type, value); - return e; -} - -Expr *BasicBlock::STRING(const QString *value) -{ - String *e = function->New(); - e->init(value); - return e; -} - -Expr *BasicBlock::REGEXP(const QString *value, int flags) -{ - RegExp *e = function->New(); - e->init(value, flags); - return e; -} - -Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) -{ - Name *e = function->New(); - e->init(function->newString(id), line, column); - return e; -} - -Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column) -{ - Name *e = function->New(); - e->init(builtin, line, column); - return e; -} - -Closure *BasicBlock::CLOSURE(Function *function) -{ - Closure *clos = function->New(); - clos->init(function); - return clos; -} - -Expr *BasicBlock::UNOP(AluOp op, Temp *expr) -{ - Unop *e = function->New(); - e->init(op, expr); - return e; -} - -Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) -{ - Binop *e = function->New(); - e->init(op, left, right); - return e; -} - -Expr *BasicBlock::CALL(Expr *base, ExprList *args) -{ - Call *e = function->New(); - e->init(base, args); - return e; -} - -Expr *BasicBlock::NEW(Expr *base, ExprList *args) -{ - New *e = function->New(); - e->init(base, args); - return e; -} - -Expr *BasicBlock::SUBSCRIPT(Temp *base, Temp *index) -{ - Subscript *e = function->New(); - e->init(base, index); - return e; -} - -Expr *BasicBlock::MEMBER(Temp *base, const QString *name) -{ - Member*e = function->New(); - e->init(base, name); - return e; -} - -Stmt *BasicBlock::EXP(Expr *expr) -{ - if (isTerminated()) - return 0; - - Exp *s = function->New(); - s->init(expr); - statements.append(s); - return s; -} - -Stmt *BasicBlock::ENTER(Expr *expr) -{ - if (isTerminated()) - return 0; - - Enter *s = function->New(); - s->init(expr); - statements.append(s); - return s; -} - -Stmt *BasicBlock::LEAVE() -{ - if (isTerminated()) - return 0; - - Leave *s = function->New(); - s->init(); - statements.append(s); - return s; -} - -Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op) -{ - if (isTerminated()) - return 0; - - Move *s = function->New(); - s->init(target, source, op); - statements.append(s); - return s; -} - -Stmt *BasicBlock::JUMP(BasicBlock *target) -{ - if (isTerminated()) - return 0; - - Jump *s = function->New(); - s->init(target); - statements.append(s); - - assert(! out.contains(target)); - out.append(target); - - assert(! target->in.contains(this)); - target->in.append(this); - - return s; -} - -Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) -{ - if (isTerminated()) - return 0; - - if (iftrue == iffalse) { - MOVE(TEMP(newTemp()), cond); - return JUMP(iftrue); - } - - CJump *s = function->New(); - s->init(cond, iftrue, iffalse); - statements.append(s); - - assert(! out.contains(iftrue)); - out.append(iftrue); - - assert(! iftrue->in.contains(this)); - iftrue->in.append(this); - - assert(! out.contains(iffalse)); - out.append(iffalse); - - assert(! iffalse->in.contains(this)); - iffalse->in.append(this); - - return s; -} - -Stmt *BasicBlock::RET(Temp *expr) -{ - if (isTerminated()) - return 0; - - Ret *s = function->New(); - s->init(expr); - statements.append(s); - return s; -} - -void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) -{ - out << 'L' << index << ':' << endl; - foreach (Stmt *s, statements) { - out << '\t'; - s->dump(out, mode); - out << endl; - } -} - -} // end of namespace IR -} // end of namespace QQmlJS - -QT_END_NAMESPACE diff --git a/qv4ir_p.h b/qv4ir_p.h deleted file mode 100644 index 64f59f41d4..0000000000 --- a/qv4ir_p.h +++ /dev/null @@ -1,726 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4IR_P_H -#define QV4IR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -#include -#include -#include - -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -class QTextStream; -class QQmlType; - -namespace QQmlJS { - -namespace VM { -struct ExecutionContext; -struct Value; -} - -namespace IR { - -struct BasicBlock; -struct Function; -struct Module; - -struct Stmt; -struct Expr; - -// expressions -struct Const; -struct String; -struct RegExp; -struct Name; -struct Temp; -struct Closure; -struct Unop; -struct Binop; -struct Call; -struct New; -struct Subscript; -struct Member; - -// statements -struct Exp; -struct Enter; -struct Leave; -struct Move; -struct Jump; -struct CJump; -struct Ret; - -enum AluOp { - OpInvalid = 0, - - OpIfTrue, - OpNot, - OpUMinus, - OpUPlus, - OpCompl, - OpIncrement, - OpDecrement, - - OpBitAnd, - OpBitOr, - OpBitXor, - - OpAdd, - OpSub, - OpMul, - OpDiv, - OpMod, - - OpLShift, - OpRShift, - OpURShift, - - OpGt, - OpLt, - OpGe, - OpLe, - OpEqual, - OpNotEqual, - OpStrictEqual, - OpStrictNotEqual, - - OpInstanceof, - OpIn, - - OpAnd, - OpOr, - - LastAluOp = OpOr -}; -AluOp binaryOperator(int op); -const char *opname(IR::AluOp op); - -enum Type { - UndefinedType, - NullType, - BoolType, - NumberType -}; - -struct ExprVisitor { - virtual ~ExprVisitor() {} - virtual void visitConst(Const *) = 0; - virtual void visitString(String *) = 0; - virtual void visitRegExp(RegExp *) = 0; - virtual void visitName(Name *) = 0; - virtual void visitTemp(Temp *) = 0; - virtual void visitClosure(Closure *) = 0; - virtual void visitUnop(Unop *) = 0; - virtual void visitBinop(Binop *) = 0; - virtual void visitCall(Call *) = 0; - virtual void visitNew(New *) = 0; - virtual void visitSubscript(Subscript *) = 0; - virtual void visitMember(Member *) = 0; -}; - -struct StmtVisitor { - virtual ~StmtVisitor() {} - virtual void visitExp(Exp *) = 0; - virtual void visitEnter(Enter *) = 0; - virtual void visitLeave(Leave *) = 0; - virtual void visitMove(Move *) = 0; - virtual void visitJump(Jump *) = 0; - virtual void visitCJump(CJump *) = 0; - virtual void visitRet(Ret *) = 0; -}; - -struct Expr { - virtual ~Expr() {} - virtual void accept(ExprVisitor *) = 0; - virtual bool isLValue() { return false; } - virtual Const *asConst() { return 0; } - virtual String *asString() { return 0; } - virtual RegExp *asRegExp() { return 0; } - virtual Name *asName() { return 0; } - virtual Temp *asTemp() { return 0; } - virtual Closure *asClosure() { return 0; } - virtual Unop *asUnop() { return 0; } - virtual Binop *asBinop() { return 0; } - virtual Call *asCall() { return 0; } - virtual New *asNew() { return 0; } - virtual Subscript *asSubscript() { return 0; } - virtual Member *asMember() { return 0; } - virtual void dump(QTextStream &out) = 0; -}; - -struct ExprList { - Expr *expr; - ExprList *next; - - void init(Expr *expr, ExprList *next = 0) - { - this->expr = expr; - this->next = next; - } -}; - -struct Const: Expr { - Type type; - double value; - - void init(Type type, double value) - { - this->type = type; - this->value = value; - } - - virtual void accept(ExprVisitor *v) { v->visitConst(this); } - virtual Const *asConst() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct String: Expr { - const QString *value; - - void init(const QString *value) - { - this->value = value; - } - - virtual void accept(ExprVisitor *v) { v->visitString(this); } - virtual String *asString() { return this; } - - virtual void dump(QTextStream &out); - static QString escape(const QString &s); -}; - -struct RegExp: Expr { - // needs to be compatible with the flags in the lexer - enum Flags { - RegExp_Global = 0x01, - RegExp_IgnoreCase = 0x02, - RegExp_Multiline = 0x04 - }; - - const QString *value; - int flags; - - void init(const QString *value, int flags) - { - this->value = value; - this->flags = flags; - } - - virtual void accept(ExprVisitor *v) { v->visitRegExp(this); } - virtual RegExp *asRegExp() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Name: Expr { - enum Builtin { - builtin_invalid, - builtin_typeof, - builtin_delete, - builtin_postincrement, - builtin_postdecrement, - builtin_throw, - builtin_create_exception_handler, - builtin_delete_exception_handler, - builtin_get_exception, - builtin_foreach_iterator_object, - builtin_foreach_next_property_name, - builtin_push_with_scope, - builtin_pop_scope, - builtin_declare_vars, - builtin_define_property, - builtin_define_array_property, - builtin_define_getter_setter - }; - - const QString *id; - Builtin builtin; - quint32 line; - quint32 column; - - void init(const QString *id, quint32 line, quint32 column); - void init(Builtin builtin, quint32 line, quint32 column); - - virtual void accept(ExprVisitor *v) { v->visitName(this); } - virtual bool isLValue() { return true; } - virtual Name *asName() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Temp: Expr { - int index; - - void init(int index) - { - this->index = index; - } - - virtual void accept(ExprVisitor *v) { v->visitTemp(this); } - virtual bool isLValue() { return true; } - virtual Temp *asTemp() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Closure: Expr { - Function *value; - - void init(Function *value) - { - this->value = value; - } - - virtual void accept(ExprVisitor *v) { v->visitClosure(this); } - virtual Closure *asClosure() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Unop: Expr { - AluOp op; - Temp *expr; - - void init(AluOp op, Temp *expr) - { - this->op = op; - this->expr = expr; - } - - virtual void accept(ExprVisitor *v) { v->visitUnop(this); } - virtual Unop *asUnop() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Binop: Expr { - AluOp op; - Expr *left; // Temp or Const - Expr *right; // Temp or Const - - void init(AluOp op, Expr *left, Expr *right) - { - this->op = op; - this->left = left; - this->right = right; - } - - virtual void accept(ExprVisitor *v) { v->visitBinop(this); } - virtual Binop *asBinop() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Call: Expr { - Expr *base; // Name, Member, Temp - ExprList *args; // List of Temps - - void init(Expr *base, ExprList *args) - { - this->base = base; - this->args = args; - } - - Expr *onlyArgument() const { - if (args && ! args->next) - return args->expr; - return 0; - } - - virtual void accept(ExprVisitor *v) { v->visitCall(this); } - virtual Call *asCall() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct New: Expr { - Expr *base; // Name, Member, Temp - ExprList *args; // List of Temps - - void init(Expr *base, ExprList *args) - { - this->base = base; - this->args = args; - } - - Expr *onlyArgument() const { - if (args && ! args->next) - return args->expr; - return 0; - } - - virtual void accept(ExprVisitor *v) { v->visitNew(this); } - virtual New *asNew() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Subscript: Expr { - Temp *base; - Temp *index; - - void init(Temp *base, Temp *index) - { - this->base = base; - this->index = index; - } - - virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } - virtual bool isLValue() { return true; } - virtual Subscript *asSubscript() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Member: Expr { - Temp *base; - const QString *name; - - void init(Temp *base, const QString *name) - { - this->base = base; - this->name = name; - } - - virtual void accept(ExprVisitor *v) { v->visitMember(this); } - virtual bool isLValue() { return true; } - virtual Member *asMember() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Stmt { - enum Mode { - HIR, - MIR - }; - - struct Data { - QVector uses; - QVector defs; - QBitArray liveIn; - QBitArray liveOut; - }; - - Data *d; - - Stmt(): d(0) {} - virtual ~Stmt() { Q_UNREACHABLE(); } - virtual Stmt *asTerminator() { return 0; } - - virtual void accept(StmtVisitor *) = 0; - virtual Exp *asExp() { return 0; } - virtual Move *asMove() { return 0; } - virtual Enter *asEnter() { return 0; } - virtual Leave *asLeave() { return 0; } - virtual Jump *asJump() { return 0; } - virtual CJump *asCJump() { return 0; } - virtual Ret *asRet() { return 0; } - virtual void dump(QTextStream &out, Mode mode = HIR) = 0; - - void destroyData() { - delete d; - d = 0; - } -}; - -struct Exp: Stmt { - Expr *expr; - - void init(Expr *expr) - { - this->expr = expr; - } - - virtual void accept(StmtVisitor *v) { v->visitExp(this); } - virtual Exp *asExp() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Move: Stmt { - Expr *target; // LHS - Temp, Name, Member or Subscript - Expr *source; - AluOp op; - - void init(Expr *target, Expr *source, AluOp op) - { - this->target = target; - this->source = source; - this->op = op; - } - - virtual void accept(StmtVisitor *v) { v->visitMove(this); } - virtual Move *asMove() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Enter: Stmt { - Expr *expr; - - void init(Expr *expr) - { - this->expr = expr; - } - - virtual void accept(StmtVisitor *v) { v->visitEnter(this); } - virtual Enter *asEnter() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Leave: Stmt { - void init() {} - - virtual void accept(StmtVisitor *v) { v->visitLeave(this); } - virtual Leave *asLeave() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Jump: Stmt { - BasicBlock *target; - - void init(BasicBlock *target) - { - this->target = target; - } - - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitJump(this); } - virtual Jump *asJump() { return this; } - - virtual void dump(QTextStream &out, Mode mode); -}; - -struct CJump: Stmt { - Expr *cond; // Temp, Binop - BasicBlock *iftrue; - BasicBlock *iffalse; - - void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) - { - this->cond = cond; - this->iftrue = iftrue; - this->iffalse = iffalse; - } - - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitCJump(this); } - virtual CJump *asCJump() { return this; } - - virtual void dump(QTextStream &out, Mode mode); -}; - -struct Ret: Stmt { - Temp *expr; - - void init(Temp *expr) - { - this->expr = expr; - } - - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitRet(this); } - virtual Ret *asRet() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Module { - MemoryPool pool; - QVector functions; - Function *rootFunction; - - Function *newFunction(const QString &name, Function *outer); - - Module() : rootFunction(0) {} - ~Module(); -}; - -struct Function { - Module *module; - MemoryPool *pool; - const QString *name; - QVector basicBlocks; - int tempCount; - int maxNumberOfArguments; - QSet strings; - QList formals; - QList locals; - QVector nestedFunctions; - - int insideWith; - - uint hasDirectEval: 1; - uint usesArgumentsObject : 1; - uint isStrict: 1; - uint unused : 29; - - template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } - - Function(Module *module, const QString &name) - : module(module) - , pool(&module->pool) - , tempCount(0) - , maxNumberOfArguments(0) - , insideWith(0) - , hasDirectEval(false) - , usesArgumentsObject(false) - , isStrict(false) - , unused(0) - { this->name = newString(name); } - - ~Function(); - - enum BasicBlockInsertMode { - InsertBlock, - DontInsertBlock - }; - - BasicBlock *newBasicBlock(BasicBlockInsertMode mode = InsertBlock); - const QString *newString(const QString &text); - - void RECEIVE(const QString &name) { formals.append(newString(name)); } - void LOCAL(const QString &name) { locals.append(newString(name)); } - - inline BasicBlock *insertBasicBlock(BasicBlock *block) { basicBlocks.append(block); return block; } - - void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); -}; - -struct BasicBlock { - Function *function; - QVector statements; - QVector in; - QVector out; - QBitArray liveIn; - QBitArray liveOut; - int index; - int offset; - - BasicBlock(Function *function): function(function), index(-1), offset(-1) {} - ~BasicBlock() {} - - template inline Instr i(Instr i) { statements.append(i); return i; } - - inline bool isEmpty() const { - return statements.isEmpty(); - } - - inline Stmt *terminator() const { - if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0) - return statements.at(statements.size() - 1); - return 0; - } - - inline bool isTerminated() const { - if (terminator() != 0) - return true; - return false; - } - - unsigned newTemp(); - - Temp *TEMP(int index); - - Expr *CONST(Type type, double value); - Expr *STRING(const QString *value); - Expr *REGEXP(const QString *value, int flags); - - Name *NAME(const QString &id, quint32 line, quint32 column); - Name *NAME(Name::Builtin builtin, quint32 line, quint32 column); - - Closure *CLOSURE(Function *function); - - Expr *UNOP(AluOp op, Temp *expr); - Expr *BINOP(AluOp op, Expr *left, Expr *right); - Expr *CALL(Expr *base, ExprList *args = 0); - Expr *NEW(Expr *base, ExprList *args = 0); - Expr *SUBSCRIPT(Temp *base, Temp *index); - Expr *MEMBER(Temp *base, const QString *name); - - Stmt *EXP(Expr *expr); - Stmt *ENTER(Expr *expr); - Stmt *LEAVE(); - - Stmt *MOVE(Expr *target, Expr *source, AluOp op = IR::OpInvalid); - - Stmt *JUMP(BasicBlock *target); - Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); - Stmt *RET(Temp *expr); - - void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); -}; - -} // end of namespace IR - -} // end of namespace QQmlJS - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // QV4IR_P_H diff --git a/qv4isel_llvm.cpp b/qv4isel_llvm.cpp deleted file mode 100644 index e575fa0bd1..0000000000 --- a/qv4isel_llvm.cpp +++ /dev/null @@ -1,1387 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wunused-parameter" -#endif // __clang__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __clang__ -# pragma clang diagnostic pop -#endif // __clang__ - -#include -#include -#include -#include -#include -#include - -// These includes have to come last, because WTF/Platform.h defines some macros -// with very unfriendly names that collide with class fields in LLVM. -#include "qv4isel_llvm_p.h" -#include "qv4_llvm_p.h" -#include "qv4ir_p.h" -#include "qv4string.h" - -namespace QQmlJS { - -int compileWithLLVM(IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*exec)(void *)) -{ - Q_ASSERT(module); - Q_ASSERT(exec || outputType != LLVMOutputJit); - - // TODO: should this be done here? - LLVMInitializeX86TargetInfo(); - LLVMInitializeX86Target(); - LLVMInitializeX86AsmPrinter(); - LLVMInitializeX86AsmParser(); - LLVMInitializeX86Disassembler(); - LLVMInitializeX86TargetMC(); - - //---- - - llvm::InitializeNativeTarget(); - LLVM::InstructionSelection llvmIsel(llvm::getGlobalContext()); - - const QString moduleName = QFileInfo(fileName).fileName(); - llvm::StringRef moduleId(moduleName.toUtf8().constData()); - llvm::Module *llvmModule = new llvm::Module(moduleId, llvmIsel.getContext()); - - if (outputType == LLVMOutputJit) { - // The execution engine takes ownership of the model. No need to delete it anymore. - std::string errStr; - llvm::ExecutionEngine *execEngine = llvm::EngineBuilder(llvmModule) -// .setUseMCJIT(true) - .setErrorStr(&errStr).create(); - if (!execEngine) { - std::cerr << "Could not create LLVM JIT: " << errStr << std::endl; - return EXIT_FAILURE; - } - - llvm::FunctionPassManager functionPassManager(llvmModule); - // Set up the optimizer pipeline. Start with registering info about how the - // target lays out data structures. - functionPassManager.add(new llvm::DataLayout(*execEngine->getDataLayout())); - // Promote allocas to registers. - functionPassManager.add(llvm::createPromoteMemoryToRegisterPass()); - // Provide basic AliasAnalysis support for GVN. - functionPassManager.add(llvm::createBasicAliasAnalysisPass()); - // Do simple "peephole" optimizations and bit-twiddling optzns. - functionPassManager.add(llvm::createInstructionCombiningPass()); - // Reassociate expressions. - functionPassManager.add(llvm::createReassociatePass()); - // Eliminate Common SubExpressions. - functionPassManager.add(llvm::createGVNPass()); - // Simplify the control flow graph (deleting unreachable blocks, etc). - functionPassManager.add(llvm::createCFGSimplificationPass()); - - functionPassManager.doInitialization(); - - llvmIsel.buildLLVMModule(module, llvmModule, &functionPassManager); - - llvm::Function *entryPoint = llvmModule->getFunction("%entry"); - Q_ASSERT(entryPoint); - void *funcPtr = execEngine->getPointerToFunction(entryPoint); - return exec(funcPtr); - } else { - llvm::FunctionPassManager functionPassManager(llvmModule); - // Set up the optimizer pipeline. - // Promote allocas to registers. - functionPassManager.add(llvm::createPromoteMemoryToRegisterPass()); - // Provide basic AliasAnalysis support for GVN. - functionPassManager.add(llvm::createBasicAliasAnalysisPass()); - // Do simple "peephole" optimizations and bit-twiddling optzns. - functionPassManager.add(llvm::createInstructionCombiningPass()); - // Reassociate expressions. - functionPassManager.add(llvm::createReassociatePass()); - // Eliminate Common SubExpressions. - functionPassManager.add(llvm::createGVNPass()); - // Simplify the control flow graph (deleting unreachable blocks, etc). - functionPassManager.add(llvm::createCFGSimplificationPass()); - - functionPassManager.doInitialization(); - - llvmIsel.buildLLVMModule(module, llvmModule, &functionPassManager); - - // TODO: if output type is .ll, print the module to file - - const std::string triple = llvm::sys::getDefaultTargetTriple(); - - std::string err; - const llvm::Target *target = llvm::TargetRegistry::lookupTarget(triple, err); - if (! err.empty()) { - std::cerr << err << ", triple: " << triple << std::endl; - assert(!"cannot create target for the host triple"); - } - - std::string cpu; - std::string features; - llvm::TargetOptions options; - llvm::TargetMachine *targetMachine = target->createTargetMachine(triple, cpu, features, options, llvm::Reloc::PIC_); - assert(targetMachine); - - llvm::TargetMachine::CodeGenFileType ft; - QString ofName; - - if (outputType == LLVMOutputObject) { - ft = llvm::TargetMachine::CGFT_ObjectFile; - ofName = fileName + QLatin1String(".o"); - } else if (outputType == LLVMOutputAssembler) { - ft = llvm::TargetMachine::CGFT_AssemblyFile; - ofName = fileName + QLatin1String(".s"); - } else { - // ft is not used. - ofName = fileName + QLatin1String(".ll"); - } - - llvm::raw_fd_ostream dest(ofName.toUtf8().constData(), err, llvm::raw_fd_ostream::F_Binary); - llvm::formatted_raw_ostream destf(dest); - if (!err.empty()) { - std::cerr << err << std::endl; - delete llvmModule; - } - - llvm::PassManager globalPassManager; - globalPassManager.add(llvm::createScalarReplAggregatesPass()); - globalPassManager.add(llvm::createInstructionCombiningPass()); - globalPassManager.add(llvm::createGlobalOptimizerPass()); - globalPassManager.add(llvm::createFunctionInliningPass(25)); -// globalPassManager.add(llvm::createFunctionInliningPass(125)); - - if (outputType == LLVMOutputObject || outputType == LLVMOutputAssembler) { - if (targetMachine->addPassesToEmitFile(globalPassManager, destf, ft)) { - std::cerr << err << " (probably no DataLayout in TargetMachine)" << std::endl; - } else { - globalPassManager.run(*llvmModule); - - destf.flush(); - dest.flush(); - } - } else { // .ll - globalPassManager.run(*llvmModule); - llvmModule->print(destf, 0); - - destf.flush(); - dest.flush(); - } - - delete llvmModule; - return EXIT_SUCCESS; - } -} - -} // QQmlJS - -using namespace QQmlJS; -using namespace QQmlJS::LLVM; - -namespace { -QTextStream qerr(stderr, QIODevice::WriteOnly); -} - -InstructionSelection::InstructionSelection(llvm::LLVMContext &context) - : llvm::IRBuilder<>(context) - , _llvmModule(0) - , _llvmFunction(0) - , _llvmValue(0) - , _numberTy(0) - , _valueTy(0) - , _contextPtrTy(0) - , _stringPtrTy(0) - , _functionTy(0) - , _allocaInsertPoint(0) - , _function(0) - , _block(0) - , _fpm(0) -{ -} - -void InstructionSelection::buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm) -{ - qSwap(_llvmModule, llvmModule); - qSwap(_fpm, fpm); - - _numberTy = getDoubleTy(); - - std::string err; - - llvm::OwningPtr buffer; - qDebug()<<"llvm runtime:"<getTypeByName("struct.QQmlJS::VM::Value"); - _contextPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::ExecutionContext")->getPointerTo(); - _stringPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::String")->getPointerTo(); - - { - llvm::Type *args[] = { _contextPtrTy }; - _functionTy = llvm::FunctionType::get(getVoidTy(), llvm::makeArrayRef(args), false); - } - - - foreach (IR::Function *function, module->functions) - (void) compileLLVMFunction(function); - qSwap(_fpm, fpm); - qSwap(_llvmModule, llvmModule); -} - -void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinThrow(IR::Temp *arg) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinDeleteExceptionHandler() -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinGetException(IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinPopScope() -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) -{ - llvm::ConstantInt *isDeletable = getInt1(deletable != 0); - llvm::Value *varName = getIdentifier(name); - CreateCall3(getRuntimeFunction("__qmljs_builtin_declare_var"), - _llvmFunction->arg_begin(), isDeletable, varName); -} - -void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::constructActivationProperty(IR::Name *func, - IR::ExprList *args, - IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::loadThisObject(IR::Temp *temp) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::loadConst(IR::Const *con, IR::Temp *temp) -{ - llvm::Value *target = getLLVMTemp(temp); - llvm::Value *source = CreateLoad(createValue(con)); - CreateStore(source, target); -} - -void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); -} - -void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) -{ - llvm::Value *name = getIdentifier(targetName); - llvm::Value *src = toValuePtr(source); - CreateCall3(getRuntimeFunction("__qmljs_llvm_set_activation_property"), - _llvmFunction->arg_begin(), name, src); -} - -void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) -{ - IR::Function *f = closure->value; - QString name; - if (f->name) - name = *f->name; - - llvm::Value *args[] = { - _llvmFunction->arg_begin(), - getLLVMTemp(target), - getIdentifier(name), - getInt1(f->hasDirectEval), - getInt1(f->usesArgumentsObject), - getInt1(f->isStrict), - getInt1(!f->nestedFunctions.isEmpty()), - genStringList(f->formals, "formals", "formal"), - getInt32(f->formals.size()), - genStringList(f->locals, "locals", "local"), - getInt32(f->locals.size()) - }; - llvm::Function *callee = _llvmModule->getFunction("__qmljs_llvm_init_closure"); - CreateCall(callee, args); -} - -void InstructionSelection::getProperty(IR::Temp *sourceBase, const QString &sourceName, IR::Temp *target) -{ - llvm::Value *base = getLLVMTempReference(sourceBase); - llvm::Value *name = getIdentifier(sourceName); - llvm::Value *t = getLLVMTemp(target); - CreateCall4(getRuntimeFunction("__qmljs_llvm_get_property"), - _llvmFunction->arg_begin(), t, base, name); -} - -void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) -{ - llvm::Value *base = getLLVMTempReference(targetBase); - llvm::Value *name = getIdentifier(targetName); - llvm::Value *src = toValuePtr(source); - CreateCall4(getRuntimeFunction("__qmljs_llvm_set_property"), - _llvmFunction->arg_begin(), base, name, src); -} - -void InstructionSelection::getElement(IR::Temp *sourceBase, IR::Temp *sourceIndex, IR::Temp *target) -{ - // TODO - assert(!"TODO!"); - Q_UNREACHABLE(); - - llvm::Value *base = getLLVMTempReference(sourceBase); - llvm::Value *index = getLLVMTempReference(sourceIndex); - llvm::Value *t = getLLVMTemp(target); - CreateCall4(getRuntimeFunction("__qmljs_llvm_get_element"), - _llvmFunction->arg_begin(), t, base, index); -} - -void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) -{ - llvm::Value *base = getLLVMTempReference(targetBase); - llvm::Value *index = getLLVMTempReference(targetIndex); - llvm::Value *src = toValuePtr(source); - CreateCall4(getRuntimeFunction("__qmljs_llvm_set_element"), - _llvmFunction->arg_begin(), base, index, src); -} - -void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) -{ - llvm::Value *t = getLLVMTemp(targetTemp); - llvm::Value *s = getLLVMTemp(sourceTemp); - CreateStore(s, t); -} - -void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) -{ - const char *opName = 0; - switch (oper) { - case IR::OpNot: opName = "__qmljs_not"; break; - case IR::OpUMinus: opName = "__qmljs_uminus"; break; - case IR::OpUPlus: opName = "__qmljs_uplus"; break; - case IR::OpCompl: opName = "__qmljs_compl"; break; - case IR::OpIncrement: opName = "__qmljs_increment"; break; - case IR::OpDecrement: opName = "__qmljs_decrement"; break; - default: assert(!"unreachable"); break; - } - - if (opName) { - llvm::Value *t = getLLVMTemp(targetTemp); - llvm::Value *s = getLLVMTemp(sourceTemp); - CreateCall3(getRuntimeFunction(opName), - _llvmFunction->arg_begin(), t, s); - } -} - -void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) -{ - const char *opName = 0; - switch (oper) { - case IR::OpBitAnd: opName = "__qmljs_llvm_bit_and"; break; - case IR::OpBitOr: opName = "__qmljs_llvm_bit_or"; break; - case IR::OpBitXor: opName = "__qmljs_llvm_bit_xor"; break; - case IR::OpAdd: opName = "__qmljs_llvm_add"; break; - case IR::OpSub: opName = "__qmljs_llvm_sub"; break; - case IR::OpMul: opName = "__qmljs_llvm_mul"; break; - case IR::OpDiv: opName = "__qmljs_llvm_div"; break; - case IR::OpMod: opName = "__qmljs_llvm_mod"; break; - case IR::OpLShift: opName = "__qmljs_llvm_shl"; break; - case IR::OpRShift: opName = "__qmljs_llvm_shr"; break; - case IR::OpURShift: opName = "__qmljs_llvm_ushr"; break; - default: - Q_UNREACHABLE(); - break; - } - - if (opName) { - llvm::Value *t = getLLVMTemp(target); - llvm::Value *s1 = toValuePtr(leftSource); - llvm::Value *s2 = toValuePtr(rightSource); - CreateCall4(getRuntimeFunction(opName), - _llvmFunction->arg_begin(), t, s1, s2); - return; - } -} - -void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) -{ - const char *opName = 0; - switch (oper) { - case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_name"; break; - case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_name"; break; - case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_name"; break; - case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_name"; break; - case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_name"; break; - case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_name"; break; - case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_name"; break; - case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_name"; break; - case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_name"; break; - case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_name"; break; - case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_name"; break; - default: - Q_UNREACHABLE(); - break; - } - - if (opName) { - llvm::Value *dst = getIdentifier(targetName); - llvm::Value *src = toValuePtr(sourceExpr); - CreateCall3(getRuntimeFunction(opName), - _llvmFunction->arg_begin(), dst, src); - return; - } -} - -void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) -{ - const char *opName = 0; - switch (oper) { - case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_element"; break; - case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_element"; break; - case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_element"; break; - case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_element"; break; - case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_element"; break; - case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_element"; break; - case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_element"; break; - case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_element"; break; - case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_element"; break; - case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_element"; break; - case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_element"; break; - default: - Q_UNREACHABLE(); - break; - } - - if (opName) { - llvm::Value *base = getLLVMTemp(targetBaseTemp); - llvm::Value *index = getLLVMTemp(targetIndexTemp); - llvm::Value *value = toValuePtr(sourceExpr); - CreateCall4(getRuntimeFunction(opName), - _llvmFunction->arg_begin(), base, index, value); - } -} - -void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) -{ - const char *opName = 0; - switch (oper) { - case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_member"; break; - case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_member"; break; - case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_member"; break; - case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_member"; break; - case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_member"; break; - case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_member"; break; - case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_member"; break; - case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_member"; break; - case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_member"; break; - case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_member"; break; - case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_member"; break; - default: - Q_UNREACHABLE(); - break; - } - - if (opName) { - llvm::Value *base = getLLVMTemp(targetBase); - llvm::Value *member = getIdentifier(targetName); - llvm::Value *value = toValuePtr(source); - CreateCall4(getRuntimeFunction(opName), - _llvmFunction->arg_begin(), value, base, member); - } -} - -llvm::Function *InstructionSelection::getLLVMFunction(IR::Function *function) -{ - llvm::Function *&f = _functionMap[function]; - if (! f) { - QString name = QStringLiteral("__qmljs_native_"); - if (function->name) { - if (*function->name == QStringLiteral("%entry")) - name = *function->name; - else - name += *function->name; - } - f = llvm::Function::Create(_functionTy, llvm::Function::ExternalLinkage, // ### make it internal - qPrintable(name), _llvmModule); - } - return f; -} - -llvm::Function *InstructionSelection::compileLLVMFunction(IR::Function *function) -{ - llvm::Function *llvmFunction = getLLVMFunction(function); - - QHash blockMap; - QVector tempMap; - - qSwap(_llvmFunction, llvmFunction); - qSwap(_function, function); - qSwap(_tempMap, tempMap); - qSwap(_blockMap, blockMap); - - // create the LLVM blocks - foreach (IR::BasicBlock *block, _function->basicBlocks) - (void) getLLVMBasicBlock(block); - - // entry block - SetInsertPoint(getLLVMBasicBlock(_function->basicBlocks.first())); - - llvm::Instruction *allocaInsertPoint = new llvm::BitCastInst(llvm::UndefValue::get(getInt32Ty()), - getInt32Ty(), "", GetInsertBlock()); - qSwap(_allocaInsertPoint, allocaInsertPoint); - - for (int i = 0; i < _function->tempCount; ++i) { - llvm::AllocaInst *t = newLLVMTemp(_valueTy); - _tempMap.append(t); - } - - foreach (llvm::Value *t, _tempMap) { - CreateStore(llvm::Constant::getNullValue(_valueTy), t); - } - -// CreateCall(getRuntimeFunction("__qmljs_llvm_init_this_object"), -// _llvmFunction->arg_begin()); - - foreach (IR::BasicBlock *block, _function->basicBlocks) { - qSwap(_block, block); - SetInsertPoint(getLLVMBasicBlock(_block)); - foreach (IR::Stmt *s, _block->statements) - s->accept(this); - qSwap(_block, block); - } - - qSwap(_allocaInsertPoint, allocaInsertPoint); - - allocaInsertPoint->eraseFromParent(); - - qSwap(_blockMap, blockMap); - qSwap(_tempMap, tempMap); - qSwap(_function, function); - qSwap(_llvmFunction, llvmFunction); - - // Validate the generated code, checking for consistency. - llvm::verifyFunction(*llvmFunction); - // Optimize the function. - if (_fpm) - _fpm->run(*llvmFunction); - - return llvmFunction; -} - -llvm::BasicBlock *InstructionSelection::getLLVMBasicBlock(IR::BasicBlock *block) -{ - llvm::BasicBlock *&llvmBlock = _blockMap[block]; - if (! llvmBlock) - llvmBlock = llvm::BasicBlock::Create(getContext(), llvm::Twine(), - _llvmFunction); - return llvmBlock; -} - -llvm::Value *InstructionSelection::getLLVMTempReference(IR::Expr *expr) -{ - if (IR::Temp *t = expr->asTemp()) - return getLLVMTemp(t); - - assert(!"TODO!"); - llvm::Value *addr = newLLVMTemp(_valueTy); -// CreateStore(getLLVMValue(expr), addr); - return addr; -} - -llvm::Value *InstructionSelection::getLLVMCondition(IR::Expr *expr) -{ - llvm::Value *value = 0; - if (IR::Temp *t = expr->asTemp()) { - value = getLLVMTemp(t); - } else { - assert(!"TODO!"); - Q_UNREACHABLE(); - -#if 0 - value = getLLVMValue(expr); - if (! value) { - Q_UNIMPLEMENTED(); - return getInt1(false); - } - - llvm::Value *tmp = newLLVMTemp(_valueTy); - CreateStore(value, tmp); - value = tmp; -#endif - } - - return CreateCall2(getRuntimeFunction("__qmljs_llvm_to_boolean"), - _llvmFunction->arg_begin(), - value); -} - -llvm::Value *InstructionSelection::getLLVMTemp(IR::Temp *temp) -{ - if (temp->index < 0) { - const int index = -temp->index -1; - return CreateCall2(getRuntimeFunction("__qmljs_llvm_get_argument"), - _llvmFunction->arg_begin(), getInt32(index)); - } - - return _tempMap[temp->index]; -} - -llvm::Value *InstructionSelection::getStringPtr(const QString &s) -{ - llvm::Value *&value = _stringMap[s]; - if (! value) { - const QByteArray bytes = s.toUtf8(); - value = CreateGlobalStringPtr(llvm::StringRef(bytes.constData(), bytes.size())); - _stringMap[s] = value; - } - return value; -} - -llvm::Value *InstructionSelection::getIdentifier(const QString &s) -{ - llvm::Value *str = getStringPtr(s); - llvm::Value *id = CreateCall2(getRuntimeFunction("__qmljs_identifier_from_utf8"), - _llvmFunction->arg_begin(), str); - return id; -} - -void InstructionSelection::visitJump(IR::Jump *s) -{ - CreateBr(getLLVMBasicBlock(s->target)); -} - -void InstructionSelection::visitCJump(IR::CJump *s) -{ - CreateCondBr(getLLVMCondition(s->cond), - getLLVMBasicBlock(s->iftrue), - getLLVMBasicBlock(s->iffalse)); -} - -void InstructionSelection::visitRet(IR::Ret *s) -{ - IR::Temp *t = s->expr->asTemp(); - assert(t != 0); - llvm::Value *result = getLLVMTemp(t); - llvm::Value *ctx = _llvmFunction->arg_begin(); - CreateCall2(getRuntimeFunction("__qmljs_llvm_return"), ctx, result); - CreateRetVoid(); -} - -#if 0 -void InstructionSelection::visitString(IR::String *e) -{ - llvm::Value *tmp = newLLVMTemp(_valueTy); - CreateCall3(getRuntimeFunction("__qmljs_llvm_init_string"), - _llvmFunction->arg_begin(), tmp, - getStringPtr(*e->value)); - _llvmValue = CreateLoad(tmp); -} -#endif - -llvm::AllocaInst *InstructionSelection::newLLVMTemp(llvm::Type *type, llvm::Value *size) -{ - llvm::AllocaInst *addr = new llvm::AllocaInst(type, size, llvm::Twine(), _allocaInsertPoint); - return addr; -} - -llvm::Value * InstructionSelection::genArguments(IR::ExprList *exprs, int &argc) -{ - llvm::Value *args = 0; - - argc = 0; - for (IR::ExprList *it = exprs; it; it = it->next) - ++argc; - - if (argc) - args = newLLVMTemp(_valueTy, getInt32(argc)); - else - args = llvm::Constant::getNullValue(_valueTy->getPointerTo()); - - int i = 0; - for (IR::ExprList *it = exprs; it; it = it->next) { -// llvm::Value *arg = getLLVMValue(it->expr); -// CreateStore(arg, CreateConstGEP1_32(args, i++)); - } - - return args; -} - -void InstructionSelection::genCallMember(IR::Call *e, llvm::Value *result) -{ - if (! result) - result = newLLVMTemp(_valueTy); - - IR::Member *m = e->base->asMember(); - llvm::Value *thisObject = getLLVMTemp(m->base->asTemp()); - llvm::Value *name = getIdentifier(*m->name); - - int argc = 0; - llvm::Value *args = genArguments(e->args, argc); - - llvm::Value *actuals[] = { - _llvmFunction->arg_begin(), - result, - thisObject, - name, - args, - getInt32(argc) - }; - - CreateCall(getRuntimeFunction("__qmljs_llvm_call_property"), llvm::ArrayRef(actuals)); - _llvmValue = CreateLoad(result); -} - -void InstructionSelection::genConstructMember(IR::New *e, llvm::Value *result) -{ - if (! result) - result = newLLVMTemp(_valueTy); - - IR::Member *m = e->base->asMember(); - llvm::Value *thisObject = getLLVMTemp(m->base->asTemp()); - llvm::Value *name = getIdentifier(*m->name); - - int argc = 0; - llvm::Value *args = genArguments(e->args, argc); - - llvm::Value *actuals[] = { - _llvmFunction->arg_begin(), - result, - thisObject, - name, - args, - getInt32(argc) - }; - - CreateCall(getRuntimeFunction("__qmljs_llvm_construct_property"), llvm::ArrayRef(actuals)); - _llvmValue = CreateLoad(result); -} - -void InstructionSelection::genCallTemp(IR::Call *e, llvm::Value *result) -{ - if (! result) - result = newLLVMTemp(_valueTy); - - llvm::Value *func = getLLVMTempReference(e->base); - - int argc = 0; - llvm::Value *args = genArguments(e->args, argc); - - llvm::Value *thisObject = llvm::Constant::getNullValue(_valueTy->getPointerTo()); - - llvm::Value *actuals[] = { - _llvmFunction->arg_begin(), - result, - thisObject, - func, - args, - getInt32(argc) - }; - - CreateCall(getRuntimeFunction("__qmljs_llvm_call_value"), actuals); - - _llvmValue = CreateLoad(result); -} - -void InstructionSelection::genConstructTemp(IR::New *e, llvm::Value *result) -{ - if (! result) - result = newLLVMTemp(_valueTy); - - llvm::Value *func = getLLVMTempReference(e->base); - - int argc = 0; - llvm::Value *args = genArguments(e->args, argc); - - llvm::Value *actuals[] = { - _llvmFunction->arg_begin(), - result, - func, - args, - getInt32(argc) - }; - - CreateCall(getRuntimeFunction("__qmljs_llvm_construct_value"), actuals); - - _llvmValue = CreateLoad(result); -} - -void InstructionSelection::genCallName(IR::Call *e, llvm::Value *result) -{ - IR::Name *base = e->base->asName(); - - if (! result) - result = newLLVMTemp(_valueTy); - - if (! base->id) { - switch (base->builtin) { - case IR::Name::builtin_invalid: - break; - - case IR::Name::builtin_typeof: - CreateCall3(getRuntimeFunction("__qmljs_llvm_typeof"), - _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); - _llvmValue = CreateLoad(result); - return; - - case IR::Name::builtin_throw: - CreateCall2(getRuntimeFunction("__qmljs_llvm_throw"), - _llvmFunction->arg_begin(), getLLVMTempReference(e->args->expr)); - _llvmValue = llvm::UndefValue::get(_valueTy); - return; - - case IR::Name::builtin_create_exception_handler: - CreateCall2(getRuntimeFunction("__qmljs_llvm_create_exception_handler"), - _llvmFunction->arg_begin(), result); - _llvmValue = CreateLoad(result); - return; - - case IR::Name::builtin_delete_exception_handler: - CreateCall(getRuntimeFunction("__qmljs_llvm_delete_exception_handler"), - _llvmFunction->arg_begin()); - return; - - case IR::Name::builtin_get_exception: - CreateCall2(getRuntimeFunction("__qmljs_llvm_get_exception"), - _llvmFunction->arg_begin(), result); - _llvmValue = CreateLoad(result); - return; - - case IR::Name::builtin_foreach_iterator_object: - CreateCall3(getRuntimeFunction("__qmljs_llvm_foreach_iterator_object"), - _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); - _llvmValue = CreateLoad(result); - return; - - case IR::Name::builtin_foreach_next_property_name: - CreateCall2(getRuntimeFunction("__qmljs_llvm_foreach_next_property_name"), - result, getLLVMTempReference(e->args->expr)); - _llvmValue = CreateLoad(result); - return; - - case IR::Name::builtin_delete: { - if (IR::Subscript *subscript = e->args->expr->asSubscript()) { - CreateCall4(getRuntimeFunction("__qmljs_llvm_delete_subscript"), - _llvmFunction->arg_begin(), - result, - getLLVMTempReference(subscript->base), - getLLVMTempReference(subscript->index)); - _llvmValue = CreateLoad(result); - return; - } else if (IR::Member *member = e->args->expr->asMember()) { - CreateCall4(getRuntimeFunction("__qmljs_llvm_delete_member"), - _llvmFunction->arg_begin(), - result, - getLLVMTempReference(member->base), - getIdentifier(*member->name)); - _llvmValue = CreateLoad(result); - return; - } else if (IR::Name *name = e->args->expr->asName()) { - CreateCall3(getRuntimeFunction("__qmljs_llvm_delete_property"), - _llvmFunction->arg_begin(), - result, - getIdentifier(*name->id)); - _llvmValue = CreateLoad(result); - return; - } else { - CreateCall3(getRuntimeFunction("__qmljs_llvm_delete_value"), - _llvmFunction->arg_begin(), - result, - getLLVMTempReference(e->args->expr)); - _llvmValue = CreateLoad(result); - return; - } - } break; - - default: - Q_UNREACHABLE(); - } - } else { - llvm::Value *name = getIdentifier(*base->id); - - int argc = 0; - llvm::Value *args = genArguments(e->args, argc); - - CreateCall5(getRuntimeFunction("__qmljs_llvm_call_activation_property"), - _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); - - _llvmValue = CreateLoad(result); - } -} - -void InstructionSelection::genConstructName(IR::New *e, llvm::Value *result) -{ - IR::Name *base = e->base->asName(); - - if (! result) - result = newLLVMTemp(_valueTy); - - if (! base->id) { - Q_UNREACHABLE(); - } else { - llvm::Value *name = getIdentifier(*base->id); - - int argc = 0; - llvm::Value *args = genArguments(e->args, argc); - - CreateCall5(getRuntimeFunction("__qmljs_llvm_construct_activation_property"), - _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); - - _llvmValue = CreateLoad(result); - } -} - -#if 0 -void InstructionSelection::visitCall(IR::Call *e) -{ - if (e->base->asMember()) { - genCallMember(e); - } else if (e->base->asTemp()) { - genCallTemp(e); - } else if (e->base->asName()) { - genCallName(e); - } else if (IR::Temp *t = e->base->asTemp()) { - llvm::Value *base = getLLVMTemp(t); - - int argc = 0; - llvm::Value *args = genArguments(e->args, argc); - - llvm::Value *result = newLLVMTemp(_valueTy); - CreateStore(llvm::Constant::getNullValue(_valueTy), result); - CreateCall5(getRuntimeFunction("__qmljs_llvm_call_value"), - _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); - _llvmValue = CreateLoad(result); - } else { - Q_UNIMPLEMENTED(); - } -} -#endif - -#if 0 -void InstructionSelection::visitNew(IR::New *e) -{ - if (e->base->asMember()) { - genConstructMember(e); - } else if (e->base->asTemp()) { - genConstructTemp(e); - } else if (e->base->asName()) { - genConstructName(e); - } else if (IR::Temp *t = e->base->asTemp()) { - llvm::Value *base = getLLVMTemp(t); - - int argc = 0; - llvm::Value *args = genArguments(e->args, argc); - - llvm::Value *result = newLLVMTemp(_valueTy); - CreateStore(llvm::Constant::getNullValue(_valueTy), result); - CreateCall5(getRuntimeFunction("__qmljs_llvm_construct_value"), - _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); - _llvmValue = CreateLoad(result); - } else { - Q_UNIMPLEMENTED(); - } -} -#endif - -#if 0 -void InstructionSelection::visitSubscript(IR::Subscript *e) -{ - llvm::Value *result = newLLVMTemp(_valueTy); - llvm::Value *base = getLLVMTempReference(e->base); - llvm::Value *index = getLLVMTempReference(e->index); - CreateCall4(getRuntimeFunction("__qmljs_llvm_get_element"), - _llvmFunction->arg_begin(), result, base, index); - _llvmValue = CreateLoad(result); -} -#endif - -#if 0 -void InstructionSelection::visitMember(IR::Member *e) -{ - llvm::Value *result = newLLVMTemp(_valueTy); - llvm::Value *base = getLLVMTempReference(e->base); - llvm::Value *name = getIdentifier(*e->name); - - CreateCall4(getRuntimeFunction("__qmljs_llvm_get_property"), - _llvmFunction->arg_begin(), result, base, name); - _llvmValue = CreateLoad(result); -} -#endif - -llvm::Function *InstructionSelection::getRuntimeFunction(llvm::StringRef str) -{ - llvm::Function *func = _llvmModule->getFunction(str); - if (!func) { - std::cerr << "Cannot find runtime function \"" - << str.str() << "\"!" << std::endl; - assert(func); - } - return func; -} - -llvm::Value *InstructionSelection::createValue(IR::Const *e) -{ - llvm::Value *tmp = newLLVMTemp(_valueTy); - - switch (e->type) { - case IR::UndefinedType: - CreateCall(getRuntimeFunction("__qmljs_llvm_init_undefined"), tmp); - break; - - case IR::NullType: - CreateCall(getRuntimeFunction("__qmljs_llvm_init_null"), tmp); - break; - - case IR::BoolType: - CreateCall2(getRuntimeFunction("__qmljs_llvm_init_boolean"), tmp, - getInt1(e->value ? 1 : 0)); - break; - - case IR::NumberType: - CreateCall2(getRuntimeFunction("__qmljs_llvm_init_number"), tmp, - llvm::ConstantFP::get(_numberTy, e->value)); - break; - - default: - Q_UNREACHABLE(); - } - - return tmp; -} - -llvm::Value *InstructionSelection::toValuePtr(IR::Expr *e) -{ - if (IR::Temp *t = e->asTemp()) { - return getLLVMTemp(t); - } else if (IR::Const *c = e->asConst()) { - return createValue(c); - } else { - Q_UNREACHABLE(); - } -} - -llvm::Value *InstructionSelection::genStringList(const QList &strings, const char *arrayName, const char *elementName) -{ - llvm::Value *array = CreateAlloca(_stringPtrTy, getInt32(strings.size()), - arrayName); - for (int i = 0, ei = strings.size(); i < ei; ++i) { - llvm::Value *el; - if (const QString *string = strings.at(i)) - el = getIdentifier(*string); - else - el = llvm::Constant::getNullValue(_stringPtrTy); - llvm::Value *ptr = CreateGEP(array, getInt32(i), elementName); - CreateStore(el, ptr); - } - - return array; -} diff --git a/qv4isel_llvm_p.h b/qv4isel_llvm_p.h deleted file mode 100644 index 46fd2fefa4..0000000000 --- a/qv4isel_llvm_p.h +++ /dev/null @@ -1,177 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4ISEL_LLVM_P_H -#define QV4ISEL_LLVM_P_H - -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wunused-parameter" -#endif // __clang__ - -#include -#include -#include - -#ifdef __clang__ -# pragma clang diagnostic pop -#endif // __clang__ - -#include "qv4isel_p.h" -#include "qv4ir_p.h" - -namespace QQmlJS { -namespace LLVM { - -class InstructionSelection: - public llvm::IRBuilder<>, - public IR::InstructionSelection -{ -public: - InstructionSelection(llvm::LLVMContext &context); - - void buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm); - -public: // methods from InstructionSelection: - virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result); - virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result); - virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); - virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); - virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result); - virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result); - virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); - virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); - virtual void callBuiltinDeleteValue(IR::Temp *result); - virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result); - virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); - virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result); - virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result); - virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result); - virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); - virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); - virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); - virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); - virtual void callBuiltinDeleteExceptionHandler(); - virtual void callBuiltinGetException(IR::Temp *result); - virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); - virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); - virtual void callBuiltinPushWithScope(IR::Temp *arg); - virtual void callBuiltinPopScope(); - virtual void callBuiltinDeclareVar(bool deletable, const QString &name); - virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); - virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); - virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); - virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); - virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); - virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result); - virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); - virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); - virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); - virtual void loadThisObject(IR::Temp *temp); - virtual void loadConst(IR::Const *con, IR::Temp *temp); - virtual void loadString(const QString &str, IR::Temp *targetTemp); - virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); - virtual void getActivationProperty(const QString &name, IR::Temp *temp); - virtual void setActivationProperty(IR::Expr *source, const QString &targetName); - virtual void initClosure(IR::Closure *closure, IR::Temp *target); - virtual void getProperty(IR::Temp *sourceBase, const QString &sourceName, IR::Temp *target); - virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); - virtual void getElement(IR::Temp *sourceBase, IR::Temp *sourceIndex, IR::Temp *target); - virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); - virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); - virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); - virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); - virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); - virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); - virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); - -public: // visitor methods for StmtVisitor: - virtual void visitJump(IR::Jump *); - virtual void visitCJump(IR::CJump *); - virtual void visitRet(IR::Ret *); - -private: - llvm::Function *getRuntimeFunction(llvm::StringRef str); - llvm::Function *getLLVMFunction(IR::Function *function); - llvm::Function *compileLLVMFunction(IR::Function *function); - llvm::BasicBlock *getLLVMBasicBlock(IR::BasicBlock *block); - llvm::Value *getLLVMTempReference(IR::Expr *expr); - llvm::Value *getLLVMCondition(IR::Expr *expr); - llvm::Value *getLLVMTemp(IR::Temp *temp); - llvm::Value *getStringPtr(const QString &s); - llvm::Value *getIdentifier(const QString &s); - llvm::AllocaInst *newLLVMTemp(llvm::Type *type, llvm::Value *size = 0); - llvm::Value * genArguments(IR::ExprList *args, int &argc); - void genCallTemp(IR::Call *e, llvm::Value *result = 0); - void genCallName(IR::Call *e, llvm::Value *result = 0); - void genCallMember(IR::Call *e, llvm::Value *result = 0); - void genConstructTemp(IR::New *e, llvm::Value *result = 0); - void genConstructName(IR::New *e, llvm::Value *result = 0); - void genConstructMember(IR::New *e, llvm::Value *result = 0); - llvm::Value *createValue(IR::Const *e); - llvm::Value *toValuePtr(IR::Expr *e); - llvm::Value *genStringList(const QList &strings, - const char *arrayName, const char *elementName); - - -private: - llvm::Module *_llvmModule; - llvm::Function *_llvmFunction; - llvm::Value *_llvmValue; - llvm::Type *_numberTy; - llvm::Type *_valueTy; - llvm::Type *_contextPtrTy; - llvm::Type *_stringPtrTy; - llvm::FunctionType *_functionTy; - llvm::Instruction *_allocaInsertPoint; - IR::Function *_function; - IR::BasicBlock *_block; - QHash _functionMap; - QHash _blockMap; - QVector _tempMap; - QHash _stringMap; - llvm::FunctionPassManager *_fpm; -}; - -} // LLVM namespace -} // QQmlJS namespace - -#endif // QV4ISEL_LLVM_P_H diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp deleted file mode 100644 index a4a136c656..0000000000 --- a/qv4isel_masm.cpp +++ /dev/null @@ -1,933 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4isel_masm_p.h" -#include "qmljs_runtime.h" -#include "qv4object.h" -#include "qv4functionobject.h" -#include "qv4regexpobject.h" - -#include -#include - -#include -#include -#include - -#ifndef NO_UDIS86 -# include -#endif - -using namespace QQmlJS; -using namespace QQmlJS::MASM; -using namespace QQmlJS::VM; - -Assembler::Assembler(IR::Function* function) - : _function(function) -{ -} - -void Assembler::registerBlock(IR::BasicBlock* block) -{ - _addrs[block] = label(); -} - -void Assembler::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) -{ - if (current->index + 1 != target->index) - _patches[target].append(jump()); -} - -void Assembler::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) -{ - _patches[targetBlock].append(targetJump); -} - -Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, IR::Temp *t) -{ - int32_t offset = 0; - if (t->index < 0) { - const int arg = -t->index - 1; - loadPtr(Address(ContextRegister, offsetof(ExecutionContext, arguments)), reg); - offset = arg * sizeof(Value); - } else if (t->index < _function->locals.size()) { - loadPtr(Address(ContextRegister, offsetof(ExecutionContext, locals)), reg); - offset = t->index * sizeof(Value); - } else { - const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size() + 1; - // StackFrameRegister points to its old value on the stack, so even for the first temp we need to - // subtract at least sizeof(Value). - offset = - sizeof(Value) * (arg + 1); - reg = StackFrameRegister; - } - return Pointer(reg, offset); -} - -template -void Assembler::copyValue(Result result, Source source) -{ -#ifdef VALUE_FITS_IN_REGISTER - // Use ReturnValueRegister as "scratch" register because loadArgument - // and storeArgument are functions that may need a scratch register themselves. - loadArgument(source, ReturnValueRegister); - storeArgument(ReturnValueRegister, result); -#else - loadDouble(source, FPGpr0); - storeDouble(FPGpr0, result); -#endif -} - -void Assembler::storeValue(VM::Value value, IR::Temp* destination) -{ - Address addr = loadTempAddress(ScratchRegister, destination); - storeValue(value, addr); -} - -void Assembler::enterStandardStackFrame(int locals) -{ -#if CPU(ARM) - push(JSC::ARMRegisters::lr); -#endif - push(StackFrameRegister); - move(StackPointerRegister, StackFrameRegister); - - // space for the locals and the ContextRegister - int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*); - -#if CPU(X86) || CPU(X86_64) - frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX -#endif - subPtr(TrustedImm32(frameSize), StackPointerRegister); - -#if CPU(X86) || CPU(ARM) - for (int saveReg = CalleeSavedFirstRegister; saveReg <= CalleeSavedLastRegister; ++saveReg) - push(static_cast(saveReg)); -#endif - // save the ContextRegister - storePtr(ContextRegister, StackPointerRegister); -} - -void Assembler::leaveStandardStackFrame(int locals) -{ - // restore the ContextRegister - loadPtr(StackPointerRegister, ContextRegister); - -#if CPU(X86) || CPU(ARM) - for (int saveReg = CalleeSavedLastRegister; saveReg >= CalleeSavedFirstRegister; --saveReg) - pop(static_cast(saveReg)); -#endif - // space for the locals and the ContextRegister - int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*); -#if CPU(X86) || CPU(X86_64) - frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX -#endif - addPtr(TrustedImm32(frameSize), StackPointerRegister); - - pop(StackFrameRegister); -#if CPU(ARM) - pop(JSC::ARMRegisters::lr); -#endif -} - - - -#define OP(op) \ - { isel_stringIfy(op), op, 0, 0 } - -#define INLINE_OP(op, memOp, immOp) \ - { isel_stringIfy(op), op, memOp, immOp } - -#define NULL_OP \ - { 0, 0, 0, 0 } - -const Assembler::BinaryOperationInfo Assembler::binaryOperations[QQmlJS::IR::LastAluOp + 1] = { - NULL_OP, // OpInvalid - NULL_OP, // OpIfTrue - NULL_OP, // OpNot - NULL_OP, // OpUMinus - NULL_OP, // OpUPlus - NULL_OP, // OpCompl - NULL_OP, // OpIncrement - NULL_OP, // OpDecrement - - INLINE_OP(__qmljs_bit_and, &Assembler::inline_and32, &Assembler::inline_and32), // OpBitAnd - INLINE_OP(__qmljs_bit_or, &Assembler::inline_or32, &Assembler::inline_or32), // OpBitOr - INLINE_OP(__qmljs_bit_xor, &Assembler::inline_xor32, &Assembler::inline_xor32), // OpBitXor - - INLINE_OP(__qmljs_add, &Assembler::inline_add32, &Assembler::inline_add32), // OpAdd - INLINE_OP(__qmljs_sub, &Assembler::inline_sub32, &Assembler::inline_sub32), // OpSub - INLINE_OP(__qmljs_mul, &Assembler::inline_mul32, &Assembler::inline_mul32), // OpMul - - OP(__qmljs_div), // OpDiv - OP(__qmljs_mod), // OpMod - - INLINE_OP(__qmljs_shl, &Assembler::inline_shl32, &Assembler::inline_shl32), // OpLShift - INLINE_OP(__qmljs_shr, &Assembler::inline_shr32, &Assembler::inline_shr32), // OpRShift - INLINE_OP(__qmljs_ushr, &Assembler::inline_ushr32, &Assembler::inline_ushr32), // OpURShift - - OP(__qmljs_gt), // OpGt - OP(__qmljs_lt), // OpLt - OP(__qmljs_ge), // OpGe - OP(__qmljs_le), // OpLe - OP(__qmljs_eq), // OpEqual - OP(__qmljs_ne), // OpNotEqual - OP(__qmljs_se), // OpStrictEqual - OP(__qmljs_sne), // OpStrictNotEqual - - OP(__qmljs_instanceof), // OpInstanceof - OP(__qmljs_in), // OpIn - - NULL_OP, // OpAnd - NULL_OP // OpOr -}; - -void Assembler::generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Expr* left, IR::Expr* right) -{ - const BinaryOperationInfo& info = binaryOperations[operation]; - if (!info.fallbackImplementation) { - assert(!"unreachable"); - return; - } - - Value leftConst = Value::undefinedValue(); - Value rightConst = Value::undefinedValue(); - - bool canDoInline = info.inlineMemRegOp && info.inlineImmRegOp; - - if (canDoInline) { - if (left->asConst()) { - leftConst = convertToValue(left->asConst()); - canDoInline = canDoInline && leftConst.tryIntegerConversion(); - } - if (right->asConst()) { - rightConst = convertToValue(right->asConst()); - canDoInline = canDoInline && rightConst.tryIntegerConversion(); - } - } - - Jump binOpFinished; - - if (canDoInline) { - - Jump leftTypeCheck; - if (left->asTemp()) { - Address typeAddress = loadTempAddress(ScratchRegister, left->asTemp()); - typeAddress.offset += offsetof(VM::Value, tag); - leftTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); - } - - Jump rightTypeCheck; - if (right->asTemp()) { - Address typeAddress = loadTempAddress(ScratchRegister, right->asTemp()); - typeAddress.offset += offsetof(VM::Value, tag); - rightTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); - } - - if (left->asTemp()) { - Address leftValue = loadTempAddress(ScratchRegister, left->asTemp()); - leftValue.offset += offsetof(VM::Value, int_32); - load32(leftValue, IntegerOpRegister); - } else { // left->asConst() - move(TrustedImm32(leftConst.integerValue()), IntegerOpRegister); - } - - Jump overflowCheck; - - if (right->asTemp()) { - Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); - rightValue.offset += offsetof(VM::Value, int_32); - - overflowCheck = (this->*info.inlineMemRegOp)(rightValue, IntegerOpRegister); - } else { // right->asConst() - overflowCheck = (this->*info.inlineImmRegOp)(TrustedImm32(rightConst.integerValue()), IntegerOpRegister); - } - - Address resultAddr = loadTempAddress(ScratchRegister, target); - Address resultValueAddr = resultAddr; - resultValueAddr.offset += offsetof(VM::Value, int_32); - store32(IntegerOpRegister, resultValueAddr); - - Address resultTypeAddr = resultAddr; - resultTypeAddr.offset += offsetof(VM::Value, tag); - store32(TrustedImm32(VM::Value::_Integer_Type), resultTypeAddr); - - binOpFinished = jump(); - - if (leftTypeCheck.isSet()) - leftTypeCheck.link(this); - if (rightTypeCheck.isSet()) - rightTypeCheck.link(this); - if (overflowCheck.isSet()) - overflowCheck.link(this); - } - - // Fallback - generateFunctionCallImp(target, info.name, info.fallbackImplementation, left, right, ContextRegister); - - if (binOpFinished.isSet()) - binOpFinished.link(this); -} -#if OS(LINUX) -static void printDisassembledOutputWithCalls(const char* output, const QHash& functions) -{ - QByteArray processedOutput(output); - for (QHash::ConstIterator it = functions.begin(), end = functions.end(); - it != end; ++it) { - QByteArray ptrString = QByteArray::number(qlonglong(it.key()), 16); - ptrString.prepend("0x"); - processedOutput = processedOutput.replace(ptrString, it.value()); - } - fprintf(stderr, "%s\n", processedOutput.constData()); -} -#endif - -void Assembler::link(VM::Function *vmFunc) -{ - QHashIterator > it(_patches); - while (it.hasNext()) { - it.next(); - IR::BasicBlock *block = it.key(); - Label target = _addrs.value(block); - assert(target.isSet()); - foreach (Jump jump, it.value()) - jump.linkTo(target, this); - } - - JSC::JSGlobalData dummy; - JSC::LinkBuffer linkBuffer(dummy, this, 0); - QHash functions; - foreach (CallToLink ctl, _callsToLink) { - linkBuffer.link(ctl.call, ctl.externalFunction); - functions[ctl.externalFunction.value()] = ctl.functionName; - } - - static bool showCode = !qgetenv("SHOW_CODE").isNull(); - if (showCode) { -#if OS(LINUX) - char* disasmOutput = 0; - size_t disasmLength = 0; - FILE* disasmStream = open_memstream(&disasmOutput, &disasmLength); - WTF::setDataFile(disasmStream); -#endif - - QByteArray name = _function->name->toUtf8(); - vmFunc->codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); - - WTF::setDataFile(stderr); -#if OS(LINUX) - fclose(disasmStream); -#if CPU(X86) || CPU(X86_64) - printDisassembledOutputWithCalls(disasmOutput, functions); -#endif - free(disasmOutput); -#endif - } else { - vmFunc->codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); - } - - vmFunc->code = (Value (*)(VM::ExecutionContext *, const uchar *)) vmFunc->codeRef.code().executableAddress(); -} - -InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module) - : EvalInstructionSelection(engine, module) - , _block(0) - , _function(0) - , _vmFunction(0) - , _asm(0) -{ -} - -InstructionSelection::~InstructionSelection() -{ - delete _asm; -} - -void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) -{ - qSwap(_function, function); - qSwap(_vmFunction, vmFunction); - Assembler* oldAssembler = _asm; - _asm = new Assembler(_function); - - int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) + 1; - locals = (locals + 1) & ~1; - _asm->enterStandardStackFrame(locals); - - int contextPointer = 0; -#ifndef VALUE_FITS_IN_REGISTER - // When the return VM value doesn't fit into a register, then - // the caller provides a pointer for storage as first argument. - // That shifts the index the context pointer argument by one. - contextPointer++; -#endif -#if CPU(X86) - _asm->loadPtr(addressForArgument(contextPointer), Assembler::ContextRegister); -#elif CPU(X86_64) || CPU(ARM) - _asm->move(_asm->registerForArgument(contextPointer), Assembler::ContextRegister); -#else - assert(!"TODO"); -#endif - - foreach (IR::BasicBlock *block, _function->basicBlocks) { - _block = block; - _asm->registerBlock(_block); - foreach (IR::Stmt *s, block->statements) { - s->accept(this); - } - } - - _asm->leaveStandardStackFrame(locals); -#ifndef VALUE_FITS_IN_REGISTER - // Emulate ret(n) instruction - // Pop off return address into scratch register ... - _asm->pop(Assembler::ScratchRegister); - // ... and overwrite the invisible argument with - // the return address. - _asm->poke(Assembler::ScratchRegister); -#endif - _asm->ret(); - - _asm->link(_vmFunction); - - qSwap(_vmFunction, vmFunction); - qSwap(_function, function); - delete _asm; - _asm = oldAssembler; -} - -void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) -{ - callRuntimeMethod(result, __qmljs_call_activation_property, func, args); -} - -void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_builtin_typeof_member, base, identifier(name), Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_builtin_typeof_element, base, index, Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_builtin_typeof_name, identifier(name), Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_builtin_typeof, value, Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_delete_member, Assembler::ContextRegister, base, identifier(name)); -} - -void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_delete_subscript, Assembler::ContextRegister, base, index); -} - -void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_delete_name, Assembler::ContextRegister, identifier(name)); -} - -void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) -{ - _asm->storeValue(Value::fromBoolean(false), result); -} - -void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_builtin_post_increment_member, base, identifier(name), Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_builtin_post_increment_element, base, index, Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_builtin_post_increment_name, identifier(name), Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_builtin_post_increment, Assembler::PointerToValue(value), Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_builtin_post_decrement_member, base, identifier(name), Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_builtin_post_decrement_element, base, index, Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_builtin_post_decrement_name, identifier(name), Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_builtin_post_decrement, Assembler::PointerToValue(value), Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinThrow(IR::Temp *arg) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, arg, Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) -{ - generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_create_exception_handler, Assembler::ContextRegister); - generateFunctionCall(Assembler::ReturnValueRegister, setjmp, Assembler::ReturnValueRegister); - Address addr = _asm->loadTempAddress(Assembler::ScratchRegister, result); - _asm->store32(Assembler::ReturnValueRegister, addr); - addr.offset += 4; - _asm->store32(Assembler::TrustedImm32(Value::Boolean_Type), addr); -} - -void InstructionSelection::callBuiltinDeleteExceptionHandler() -{ - generateFunctionCall(Assembler::Void, __qmljs_delete_exception_handler, Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinGetException(IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_get_exception, Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_foreach_iterator_object, arg, Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) -{ - generateFunctionCall(result, __qmljs_foreach_next_property_name, arg); -} - -void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) -{ - generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with_scope, arg, Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinPopScope() -{ - generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_pop_scope, Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_declare_var, Assembler::ContextRegister, - Assembler::TrustedImm32(deletable), identifier(name)); -} - -void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter, - object, identifier(name), getter, setter, Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property, - object, identifier(name), value, Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_define_array_property, - object, Assembler::TrustedImm32(index), value, Assembler::ContextRegister); -} - -void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) -{ - int argc = prepareVariableArguments(args); - IR::Temp* thisObject = 0; - generateFunctionCall(result, __qmljs_call_value, Assembler::ContextRegister, thisObject, value, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); -} - -void InstructionSelection::loadThisObject(IR::Temp *temp) -{ - generateFunctionCall(temp, __qmljs_get_thisObject, Assembler::ContextRegister); -} - -void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) -{ - _asm->storeValue(convertToValue(sourceConst), targetTemp); -} - -void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) -{ - Value v = Value::fromString(identifier(str)); - _asm->storeValue(v, targetTemp); -} - -void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) -{ - Value v = Value::fromObject(engine()->newRegExpObject(*sourceRegexp->value, - sourceRegexp->flags)); - _vmFunction->generatedValues.append(v); - _asm->storeValue(v, targetTemp); -} - -void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) -{ - String *propertyName = identifier(name); - generateFunctionCall(temp, __qmljs_get_activation_property, Assembler::ContextRegister, propertyName); -} - -void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) -{ - String *propertyName = identifier(targetName); - generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, Assembler::ContextRegister, propertyName, source); -} - -void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) -{ - VM::Function *vmFunc = vmFunction(closure->value); - assert(vmFunc); - generateFunctionCall(target, __qmljs_init_closure, Assembler::TrustedImmPtr(vmFunc), Assembler::ContextRegister); -} - -void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::Temp *target) -{ - generateFunctionCall(target, __qmljs_get_property, Assembler::ContextRegister, base, identifier(name)); -} - -void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) -{ - generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, targetBase, identifier(targetName), source); -} - -void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) -{ - generateFunctionCall(target, __qmljs_get_element, Assembler::ContextRegister, base, index); -} - -void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) -{ - generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, targetBase, targetIndex, source); -} - -void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) -{ - _asm->copyValue(targetTemp, sourceTemp); -} - -#define setOp(op, opName, operation) \ - do { op = operation; opName = isel_stringIfy(operation); } while (0) - -void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) -{ - Value (*op)(const Value value, ExecutionContext *ctx) = 0; - const char *opName = 0; - switch (oper) { - case IR::OpIfTrue: assert(!"unreachable"); break; - case IR::OpNot: setOp(op, opName, __qmljs_not); break; - case IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break; - case IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break; - case IR::OpCompl: setOp(op, opName, __qmljs_compl); break; - case IR::OpIncrement: setOp(op, opName, __qmljs_increment); break; - case IR::OpDecrement: setOp(op, opName, __qmljs_decrement); break; - default: assert(!"unreachable"); break; - } // switch - - if (op) - _asm->generateFunctionCallImp(targetTemp, opName, op, sourceTemp, - Assembler::ContextRegister); -} - -void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) -{ - _asm->generateBinOp(oper, target, leftSource, rightSource); -} - -void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) -{ - void (*op)(const Value value, String *name, ExecutionContext *ctx) = 0; - const char *opName = 0; - switch (oper) { - case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break; - case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_name); break; - case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_name); break; - case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_name); break; - case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_name); break; - case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_name); break; - case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_name); break; - case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_name); break; - case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_name); break; - case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_name); break; - case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_name); break; - default: - Q_UNREACHABLE(); - break; - } - if (op) { - _asm->generateFunctionCallImp(Assembler::Void, opName, op, sourceExpr, identifier(targetName), Assembler::ContextRegister); - } -} - -void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) -{ - void (*op)(Value base, Value index, Value value, ExecutionContext *ctx) = 0; - const char *opName = 0; - switch (oper) { - case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; - case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_element); break; - case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_element); break; - case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_element); break; - case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_element); break; - case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_element); break; - case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_element); break; - case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_element); break; - case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_element); break; - case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_element); break; - case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_element); break; - default: - Q_UNREACHABLE(); - break; - } - - if (op) { - _asm->generateFunctionCallImp(Assembler::Void, opName, op, targetBaseTemp, targetIndexTemp, sourceExpr, Assembler::ContextRegister); - } -} - -void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) -{ - void (*op)(Value value, Value base, String *name, ExecutionContext *ctx) = 0; - const char *opName = 0; - switch (oper) { - case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; - case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_member); break; - case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_member); break; - case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_member); break; - case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_member); break; - case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_member); break; - case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_member); break; - case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_member); break; - case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_member); break; - case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_member); break; - case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_member); break; - default: - Q_UNREACHABLE(); - break; - } - - if (op) { - String* member = identifier(targetName); - _asm->generateFunctionCallImp(Assembler::Void, opName, op, source, targetBase, member, Assembler::ContextRegister); - } -} - -void InstructionSelection::callProperty(IR::Temp *base, const QString &name, - IR::ExprList *args, IR::Temp *result) -{ - assert(base != 0); - - int argc = prepareVariableArguments(args); - generateFunctionCall(result, __qmljs_call_property, - Assembler::ContextRegister, base, identifier(name), - baseAddressForCallArguments(), - Assembler::TrustedImm32(argc)); -} - -void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) -{ - assert(base != 0); - - int argc = prepareVariableArguments(args); - generateFunctionCall(result, __qmljs_call_element, - Assembler::ContextRegister, base, index, - baseAddressForCallArguments(), - Assembler::TrustedImm32(argc)); -} - -String *InstructionSelection::identifier(const QString &s) -{ - String *str = engine()->identifier(s); - _vmFunction->identifiers.append(str); - return str; -} - -void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result) -{ - assert(func != 0); - - callRuntimeMethod(result, __qmljs_construct_activation_property, func, args); -} - -void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) -{ - int argc = prepareVariableArguments(args); - generateFunctionCall(result, __qmljs_construct_property, Assembler::ContextRegister, base, identifier(name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); -} - -void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) -{ - assert(value != 0); - - int argc = prepareVariableArguments(args); - generateFunctionCall(result, __qmljs_construct_value, Assembler::ContextRegister, value, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); -} - -void InstructionSelection::visitJump(IR::Jump *s) -{ - _asm->jumpToBlock(_block, s->target); -} - -void InstructionSelection::visitCJump(IR::CJump *s) -{ - if (IR::Temp *t = s->cond->asTemp()) { - Address temp = _asm->loadTempAddress(Assembler::ScratchRegister, t); - Address tag = temp; - tag.offset += offsetof(VM::Value, tag); - Assembler::Jump booleanConversion = _asm->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(VM::Value::Boolean_Type)); - - Address data = temp; - data.offset += offsetof(VM::Value, int_32); - _asm->load32(data, Assembler::ReturnValueRegister); - Assembler::Jump testBoolean = _asm->jump(); - - booleanConversion.link(_asm); - { - generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, t, Assembler::ContextRegister); - } - - testBoolean.link(_asm); - Assembler::Jump target = _asm->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); - _asm->addPatch(s->iftrue, target); - - _asm->jumpToBlock(_block, s->iffalse); - return; - } else if (IR::Binop *b = s->cond->asBinop()) { - if ((b->left->asTemp() || b->left->asConst()) && - (b->right->asTemp() || b->right->asConst())) { - Bool (*op)(const Value, const Value, ExecutionContext *ctx) = 0; - const char *opName = 0; - switch (b->op) { - default: Q_UNREACHABLE(); assert(!"todo"); break; - case IR::OpGt: setOp(op, opName, __qmljs_cmp_gt); break; - case IR::OpLt: setOp(op, opName, __qmljs_cmp_lt); break; - case IR::OpGe: setOp(op, opName, __qmljs_cmp_ge); break; - case IR::OpLe: setOp(op, opName, __qmljs_cmp_le); break; - case IR::OpEqual: setOp(op, opName, __qmljs_cmp_eq); break; - case IR::OpNotEqual: setOp(op, opName, __qmljs_cmp_ne); break; - case IR::OpStrictEqual: setOp(op, opName, __qmljs_cmp_se); break; - case IR::OpStrictNotEqual: setOp(op, opName, __qmljs_cmp_sne); break; - case IR::OpInstanceof: setOp(op, opName, __qmljs_cmp_instanceof); break; - case IR::OpIn: setOp(op, opName, __qmljs_cmp_in); break; - } // switch - - _asm->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, b->left, b->right, Assembler::ContextRegister); - - Assembler::Jump target = _asm->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); - _asm->addPatch(s->iftrue, target); - - _asm->jumpToBlock(_block, s->iffalse); - return; - } else { - assert(!"wip"); - } - Q_UNIMPLEMENTED(); - } - Q_UNIMPLEMENTED(); - assert(!"TODO"); -} - -void InstructionSelection::visitRet(IR::Ret *s) -{ - if (IR::Temp *t = s->expr->asTemp()) { -#ifdef VALUE_FITS_IN_REGISTER - _asm->copyValue(Assembler::ReturnValueRegister, t); -#else - _asm->loadPtr(addressForArgument(0), Assembler::ReturnValueRegister); - _asm->copyValue(Address(Assembler::ReturnValueRegister, 0), t); -#endif - return; - } - Q_UNIMPLEMENTED(); - Q_UNUSED(s); -} - -int InstructionSelection::prepareVariableArguments(IR::ExprList* args) -{ - int argc = 0; - for (IR::ExprList *it = args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (IR::ExprList *it = args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - _asm->copyValue(argumentAddressForCall(i), arg); - } - - return argc; -} - -void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, ActivationMethod method, IR::Expr *base, IR::ExprList *args) -{ - IR::Name *baseName = base->asName(); - assert(baseName != 0); - - int argc = prepareVariableArguments(args); - _asm->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); -} - -void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args) -{ - int argc = prepareVariableArguments(args); - _asm->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); -} - - diff --git a/qv4isel_masm_p.h b/qv4isel_masm_p.h deleted file mode 100644 index 5a4a29b154..0000000000 --- a/qv4isel_masm_p.h +++ /dev/null @@ -1,797 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4ISEL_MASM_P_H -#define QV4ISEL_MASM_P_H - -#include "qv4ir_p.h" -#include "qv4isel_p.h" -#include "qv4isel_util_p.h" -#include "qv4object.h" -#include "qmljs_runtime.h" - -#include -#include -#include -#include - -namespace QQmlJS { -namespace MASM { - -class Assembler : public JSC::MacroAssembler -{ -public: - Assembler(IR::Function* function); -#if CPU(X86) - -#undef VALUE_FITS_IN_REGISTER - - static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; - static const RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const RegisterID ContextRegister = JSC::X86Registers::esi; - static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID ScratchRegister = JSC::X86Registers::ecx; - static const RegisterID CalleeSavedFirstRegister = ScratchRegister; - static const RegisterID CalleeSavedLastRegister = ScratchRegister; - static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; - static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - - static const int RegisterSize = 4; - - static const int RegisterArgumentCount = 0; - static RegisterID registerForArgument(int) - { - assert(false); - // Not reached. - return JSC::X86Registers::eax; - } -#elif CPU(X86_64) - -#define VALUE_FITS_IN_REGISTER - - static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; - static const RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const RegisterID ContextRegister = JSC::X86Registers::r14; - static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID ScratchRegister = JSC::X86Registers::r10; - static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; - static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - - static const int RegisterSize = 8; - - static const int RegisterArgumentCount = 6; - static RegisterID registerForArgument(int index) - { - static RegisterID regs[RegisterArgumentCount] = { - JSC::X86Registers::edi, - JSC::X86Registers::esi, - JSC::X86Registers::edx, - JSC::X86Registers::ecx, - JSC::X86Registers::r8, - JSC::X86Registers::r9 - }; - assert(index >= 0 && index < RegisterArgumentCount); - return regs[index]; - }; -#elif CPU(ARM) - -#undef VALUE_FITS_IN_REGISTER - - static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4; - static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; - static const RegisterID ContextRegister = JSC::ARMRegisters::r5; - static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; - static const RegisterID ScratchRegister = JSC::ARMRegisters::r6; - static const RegisterID CalleeSavedFirstRegister = JSC::ARMRegisters::r4; - static const RegisterID CalleeSavedLastRegister = JSC::ARMRegisters::r11; - static const RegisterID IntegerOpRegister = JSC::X86Registers::r0; - static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; - - static const int RegisterSize = 4; - - static const RegisterID RegisterArgument1 = JSC::ARMRegisters::r0; - static const RegisterID RegisterArgument2 = JSC::ARMRegisters::r1; - static const RegisterID RegisterArgument3 = JSC::ARMRegisters::r2; - static const RegisterID RegisterArgument4 = JSC::ARMRegisters::r3; - - static const int RegisterArgumentCount = 4; - static RegisterID registerForArgument(int index) - { - assert(index >= 0 && index < RegisterArgumentCount); - return static_cast(JSC::ARMRegisters::r0 + index); - }; -#else -#error Argh. -#endif - - // Explicit type to allow distinguishing between - // pushing an address itself or the value it points - // to onto the stack when calling functions. - struct Pointer : public Address - { - explicit Pointer(const Address& addr) - : Address(addr) - {} - explicit Pointer(RegisterID reg, int32_t offset) - : Address(reg, offset) - {} - }; - - struct VoidType {}; - static const VoidType Void; - - - typedef JSC::FunctionPtr FunctionPtr; - - struct CallToLink { - Call call; - FunctionPtr externalFunction; - const char* functionName; - }; - struct PointerToValue { - PointerToValue(IR::Temp *value) : value(value) {} - IR::Temp *value; - }; - - void callAbsolute(const char* functionName, FunctionPtr function) { - CallToLink ctl; - ctl.call = call(); - ctl.externalFunction = function; - ctl.functionName = functionName; - _callsToLink.append(ctl); - } - - void registerBlock(IR::BasicBlock*); - void jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target); - void addPatch(IR::BasicBlock* targetBlock, Jump targetJump); - - Pointer loadTempAddress(RegisterID reg, IR::Temp *t); - - void loadArgument(RegisterID source, RegisterID dest) - { - move(source, dest); - } - - void loadArgument(TrustedImmPtr ptr, RegisterID dest) - { - move(TrustedImmPtr(ptr), dest); - } - - void loadArgument(const Pointer& ptr, RegisterID dest) - { - addPtr(TrustedImm32(ptr.offset), ptr.base, dest); - } - - void loadArgument(PointerToValue temp, RegisterID dest) - { - assert(temp.value); - - Pointer addr = loadTempAddress(dest, temp.value); - loadArgument(addr, dest); - } - -#ifdef VALUE_FITS_IN_REGISTER - void loadArgument(IR::Temp* temp, RegisterID dest) - { - if (!temp) { - VM::Value undefined = VM::Value::undefinedValue(); - move(TrustedImm64(undefined.val), dest); - } else { - Pointer addr = loadTempAddress(dest, temp); - load64(addr, dest); - } - } - - void loadArgument(IR::Const* c, RegisterID dest) - { - VM::Value v = convertToValue(c); - move(TrustedImm64(v.val), dest); - } - - void loadArgument(IR::Expr* expr, RegisterID dest) - { - if (!expr) { - VM::Value undefined = VM::Value::undefinedValue(); - move(TrustedImm64(undefined.val), dest); - } else if (expr->asTemp()){ - loadArgument(expr->asTemp(), dest); - } else if (expr->asConst()) { - loadArgument(expr->asConst(), dest); - } else { - assert(!"unimplemented expression type in loadArgument"); - } - } -#else - void loadArgument(IR::Expr*, RegisterID) - { - assert(!"unimplemented: expression in loadArgument"); - } -#endif - - void loadArgument(VM::String* string, RegisterID dest) - { - loadArgument(TrustedImmPtr(string), dest); - } - - void loadArgument(TrustedImm32 imm32, RegisterID dest) - { - xorPtr(dest, dest); - if (imm32.m_value) - move(imm32, dest); - } - - void storeArgument(RegisterID src, IR::Temp *temp) - { - if (temp) { - Pointer addr = loadTempAddress(ScratchRegister, temp); -#ifdef VALUE_FITS_IN_REGISTER - store64(src, addr); -#else - // If the value doesn't fit into a register, then the - // register contains the address to where the argument - // (return value) is stored. Copy it from there. - copyValue(addr, Pointer(src, 0)); -#endif - } - } - -#ifdef VALUE_FITS_IN_REGISTER - void storeArgument(RegisterID src, const Pointer &dest) - { - store64(src, dest); - } -#endif - - void storeArgument(RegisterID src, RegisterID dest) - { - move(src, dest); - } - - void storeArgument(RegisterID, VoidType) - { - } - - using JSC::MacroAssembler::push; - - void push(const Pointer& ptr) - { - addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister); - push(ScratchRegister); - } - - void push(VM::Value value) - { -#ifdef VALUE_FITS_IN_REGISTER - move(TrustedImm64(value.val), ScratchRegister); - push(ScratchRegister); -#else - move(TrustedImm32(value.tag), ScratchRegister); - push(ScratchRegister); - move(TrustedImm32(value.int_32), ScratchRegister); - push(ScratchRegister); -#endif - } - - void push(PointerToValue temp) - { - assert (temp.value); - - Pointer ptr = loadTempAddress(ScratchRegister, temp.value); - push(ptr); - } - - void push(IR::Temp* temp) - { - if (temp) { - Address addr = loadTempAddress(ScratchRegister, temp); - addr.offset += 4; - push(addr); - addr.offset -= 4; - push(addr); - } else { - VM::Value undefined = VM::Value::undefinedValue(); - push(undefined); - } - } - - void push(IR::Const* c) - { - VM::Value v = convertToValue(c); - push(v); - } - - void push(IR::Expr* e) - { - if (!e) { - VM::Value undefined = VM::Value::undefinedValue(); - push(undefined); - } else if (IR::Const *c = e->asConst()) - push(c); - else if (IR::Temp *t = e->asTemp()) { - push(t); - } else { - assert(!"Trying to push an expression that is not a Temp or Const"); - } - } - - void push(TrustedImmPtr ptr) - { - move(TrustedImmPtr(ptr), ScratchRegister); - push(ScratchRegister); - } - - void push(VM::String* name) - { - push(TrustedImmPtr(name)); - } - - using JSC::MacroAssembler::loadDouble; - void loadDouble(IR::Temp* temp, FPRegisterID dest) - { - Pointer ptr = loadTempAddress(ScratchRegister, temp); - loadDouble(ptr, dest); - } - - using JSC::MacroAssembler::storeDouble; - void storeDouble(FPRegisterID source, IR::Temp* temp) - { - Pointer ptr = loadTempAddress(ScratchRegister, temp); - storeDouble(source, ptr); - } - - template - void copyValue(Result result, Source source); - - void storeValue(VM::Value value, Address destination) - { -#ifdef VALUE_FITS_IN_REGISTER - store64(TrustedImm64(value.val), destination); -#else - store32(TrustedImm32(value.int_32), destination); - destination.offset += 4; - store32(TrustedImm32(value.tag), destination); -#endif - } - - void storeValue(VM::Value value, IR::Temp* temp); - - void enterStandardStackFrame(int locals); - void leaveStandardStackFrame(int locals); - - void callFunctionPrologue() - { -#if CPU(X86) - // Callee might clobber it :( - push(ContextRegister); -#endif - } - void callFunctionEpilogue() - { -#if CPU(X86) - pop(ContextRegister); -#endif - } - - static inline int sizeOfArgument(VoidType) - { return 0; } - static inline int sizeOfArgument(RegisterID) - { return RegisterSize; } - static inline int sizeOfArgument(IR::Temp*) - { return 8; } // Size of value - static inline int sizeOfArgument(IR::Expr*) - { return 8; } // Size of value - static inline int sizeOfArgument(const Pointer&) - { return sizeof(void*); } - static inline int sizeOfArgument(VM::String* string) - { return sizeof(string); } - static inline int sizeOfArgument(const PointerToValue &) - { return sizeof(void *); } - static inline int sizeOfArgument(TrustedImmPtr) - { return sizeof(void*); } - static inline int sizeOfArgument(TrustedImm32) - { return 4; } - - struct ArgumentLoader - { - ArgumentLoader(Assembler* _assembler, int totalNumberOfArguments) - : assembler(_assembler) - , stackSpaceForArguments(0) - , currentRegisterIndex(qMin(totalNumberOfArguments - 1, RegisterArgumentCount - 1)) - { - } - - template - void load(T argument) - { - if (currentRegisterIndex >= 0) { - assembler->loadArgument(argument, registerForArgument(currentRegisterIndex)); - --currentRegisterIndex; - } else { - assembler->push(argument); - stackSpaceForArguments += sizeOfArgument(argument); - } - } - - void load(VoidType) - { - if (currentRegisterIndex >= 0) - --currentRegisterIndex; - } - - Assembler *assembler; - int stackSpaceForArguments; - int currentRegisterIndex; - }; - - template - void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) - { - callFunctionPrologue(); - - int totalNumberOfArgs = 5; - - // If necessary reserve space for the return value on the stack and - // pass the pointer to it as the first hidden parameter. - bool returnValueOnStack = false; - int sizeOfReturnValueOnStack = sizeOfArgument(r); - if (sizeOfReturnValueOnStack > RegisterSize) { - sub32(TrustedImm32(sizeOfReturnValueOnStack), StackPointerRegister); - ++totalNumberOfArgs; - returnValueOnStack = true; - } - - ArgumentLoader l(this, totalNumberOfArgs); - l.load(arg5); - l.load(arg4); - l.load(arg3); - l.load(arg2); - l.load(arg1); - - if (returnValueOnStack) { - // Load address of return value - l.load(Pointer(StackPointerRegister, l.stackSpaceForArguments)); - } - - callAbsolute(functionName, function); - - int stackSizeToCorrect = l.stackSpaceForArguments; - if (returnValueOnStack) { - stackSizeToCorrect -= sizeof(void*); // Callee removed the hidden argument (address of return value) - stackSizeToCorrect += sizeOfReturnValueOnStack; - } - - storeArgument(ReturnValueRegister, r); - - if (stackSizeToCorrect) - add32(TrustedImm32(stackSizeToCorrect), StackPointerRegister); - - callFunctionEpilogue(); - } - - template - void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) - { - generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType()); - } - - template - void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) - { - generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType()); - } - - template - void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) - { - generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType()); - } - - template - void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1) - { - generateFunctionCallImp(r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType()); - } - - typedef Jump (Assembler::*MemRegBinOp)(Address, RegisterID); - typedef Jump (Assembler::*ImmRegBinOp)(TrustedImm32, RegisterID); - - struct BinaryOperationInfo { - const char *name; - VM::Value (*fallbackImplementation)(const VM::Value, const VM::Value, VM::ExecutionContext *); - MemRegBinOp inlineMemRegOp; - ImmRegBinOp inlineImmRegOp; - }; - - static const BinaryOperationInfo binaryOperations[QQmlJS::IR::LastAluOp + 1]; - - void generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Expr* left, IR::Expr* right); - - Jump inline_add32(Address addr, RegisterID reg) - { - return branchAdd32(Overflow, addr, reg); - } - - Jump inline_add32(TrustedImm32 imm, RegisterID reg) - { - return branchAdd32(Overflow, imm, reg); - } - - Jump inline_sub32(Address addr, RegisterID reg) - { - return branchSub32(Overflow, addr, reg); - } - - Jump inline_sub32(TrustedImm32 imm, RegisterID reg) - { - return branchSub32(Overflow, imm, reg); - } - - Jump inline_mul32(Address addr, RegisterID reg) - { - return branchMul32(Overflow, addr, reg); - } - - Jump inline_mul32(TrustedImm32 imm, RegisterID reg) - { - return branchMul32(Overflow, imm, reg, reg); - } - - Jump inline_shl32(Address addr, RegisterID reg) - { - load32(addr, ScratchRegister); - and32(TrustedImm32(0x1f), ScratchRegister); - lshift32(ScratchRegister, reg); - return Jump(); - } - - Jump inline_shl32(TrustedImm32 imm, RegisterID reg) - { - imm.m_value &= 0x1f; - lshift32(imm, reg); - return Jump(); - } - - Jump inline_shr32(Address addr, RegisterID reg) - { - load32(addr, ScratchRegister); - and32(TrustedImm32(0x1f), ScratchRegister); - rshift32(ScratchRegister, reg); - return Jump(); - } - - Jump inline_shr32(TrustedImm32 imm, RegisterID reg) - { - imm.m_value &= 0x1f; - rshift32(imm, reg); - return Jump(); - } - - Jump inline_ushr32(Address addr, RegisterID reg) - { - load32(addr, ScratchRegister); - and32(TrustedImm32(0x1f), ScratchRegister); - urshift32(ScratchRegister, reg); - return Jump(); - } - - Jump inline_ushr32(TrustedImm32 imm, RegisterID reg) - { - imm.m_value &= 0x1f; - urshift32(imm, reg); - return Jump(); - } - - Jump inline_and32(Address addr, RegisterID reg) - { - and32(addr, reg); - return Jump(); - } - - Jump inline_and32(TrustedImm32 imm, RegisterID reg) - { - and32(imm, reg); - return Jump(); - } - - Jump inline_or32(Address addr, RegisterID reg) - { - or32(addr, reg); - return Jump(); - } - - Jump inline_or32(TrustedImm32 imm, RegisterID reg) - { - or32(imm, reg); - return Jump(); - } - - Jump inline_xor32(Address addr, RegisterID reg) - { - xor32(addr, reg); - return Jump(); - } - - Jump inline_xor32(TrustedImm32 imm, RegisterID reg) - { - xor32(imm, reg); - return Jump(); - } - - void link(VM::Function *vmFunc); - -private: - IR::Function* _function; - QHash _addrs; - QHash > _patches; - QList _callsToLink; -}; - -class InstructionSelection: - protected IR::InstructionSelection, - public EvalInstructionSelection -{ -public: - InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); - ~InstructionSelection(); - - virtual void run(VM::Function *vmFunction, IR::Function *function); - -protected: - virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result); - virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result); - virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); - virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); - virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result); - virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result); - virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); - virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); - virtual void callBuiltinDeleteValue(IR::Temp *result); - virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result); - virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); - virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result); - virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result); - virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result); - virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); - virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); - virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); - virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); - virtual void callBuiltinDeleteExceptionHandler(); - virtual void callBuiltinGetException(IR::Temp *result); - virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); - virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); - virtual void callBuiltinPushWithScope(IR::Temp *arg); - virtual void callBuiltinPopScope(); - virtual void callBuiltinDeclareVar(bool deletable, const QString &name); - virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); - virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); - virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); - virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); - virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result); - virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); - virtual void loadThisObject(IR::Temp *temp); - virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp); - virtual void loadString(const QString &str, IR::Temp *targetTemp); - virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); - virtual void getActivationProperty(const QString &name, IR::Temp *temp); - virtual void setActivationProperty(IR::Expr *source, const QString &targetName); - virtual void initClosure(IR::Closure *closure, IR::Temp *target); - virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); - virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); - virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target); - virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); - virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); - virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); - virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); - virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); - virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); - virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); - - typedef Assembler::Address Address; - typedef Assembler::Pointer Pointer; - - Address addressForArgument(int index) const - { - if (index < Assembler::RegisterArgumentCount) - return Address(_asm->registerForArgument(index), 0); - - // StackFrameRegister points to its old value on the stack, and above - // it we have the return address, hence the need to step over two - // values before reaching the first argument. - return Address(Assembler::StackFrameRegister, (index - Assembler::RegisterArgumentCount + 2) * sizeof(void*)); - } - - // Some run-time functions take (Value* args, int argc). This function is for populating - // the args. - Pointer argumentAddressForCall(int argument) - { - const int index = _function->maxNumberOfArguments - argument; - return Pointer(Assembler::StackFrameRegister, sizeof(VM::Value) * (-index) - - sizeof(void*) // size of ebp - ); - } - Pointer baseAddressForCallArguments() - { - return argumentAddressForCall(0); - } - - VM::String *identifier(const QString &s); - virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); - virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); - virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); - - virtual void visitJump(IR::Jump *); - virtual void visitCJump(IR::CJump *); - virtual void visitRet(IR::Ret *); - -private: - #define isel_stringIfyx(s) #s - #define isel_stringIfy(s) isel_stringIfyx(s) - - #define generateFunctionCall(t, function, ...) \ - _asm->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) - - int prepareVariableArguments(IR::ExprList* args); - - typedef VM::Value (*ActivationMethod)(VM::ExecutionContext *, VM::String *name, VM::Value *args, int argc); - typedef VM::Value (*BuiltinMethod)(VM::ExecutionContext *, VM::Value *args, int argc); - void callRuntimeMethodImp(IR::Temp *result, const char* name, ActivationMethod method, IR::Expr *base, IR::ExprList *args); - void callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args); -#define callRuntimeMethod(result, function, ...) \ - callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__) - - IR::BasicBlock *_block; - IR::Function* _function; - VM::Function* _vmFunction; - Assembler* _asm; -}; - -class ISelFactory: public EvalISelFactory -{ -public: - virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module) - { return new InstructionSelection(engine, module); } -}; - -} // end of namespace MASM -} // end of namespace QQmlJS - -#endif // QV4ISEL_MASM_P_H diff --git a/qv4isel_p.cpp b/qv4isel_p.cpp deleted file mode 100644 index b408be3673..0000000000 --- a/qv4isel_p.cpp +++ /dev/null @@ -1,414 +0,0 @@ -#include "debugging.h" -#include "qmljs_engine.h" -#include "qv4ir_p.h" -#include "qv4isel_p.h" -#include "qv4isel_util_p.h" -#include "qv4functionobject.h" - -#include - -#include - -namespace { -QTextStream qout(stderr, QIODevice::WriteOnly); -} // anonymous namespace - -using namespace QQmlJS; -using namespace QQmlJS::IR; - -EvalInstructionSelection::EvalInstructionSelection(VM::ExecutionEngine *engine, Module *module) - : _engine(engine) -{ - assert(engine); - assert(module); - - createFunctionMapping(engine, module->rootFunction); - foreach (IR::Function *f, module->functions) { - assert(_irToVM.contains(f)); - } -} - -EvalInstructionSelection::~EvalInstructionSelection() -{} - -EvalISelFactory::~EvalISelFactory() -{} - -VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngine *engine, Function *irFunction) -{ - VM::Function *vmFunction = engine->newFunction(irFunction->name ? *irFunction->name : QString()); - _irToVM.insert(irFunction, vmFunction); - - vmFunction->hasDirectEval = irFunction->hasDirectEval; - vmFunction->usesArgumentsObject = irFunction->usesArgumentsObject; - vmFunction->hasNestedFunctions = !irFunction->nestedFunctions.isEmpty(); - vmFunction->isStrict = irFunction->isStrict; - - foreach (const QString *formal, irFunction->formals) - if (formal) - vmFunction->formals.append(engine->identifier(*formal)); - foreach (const QString *local, irFunction->locals) - if (local) - vmFunction->locals.append(engine->identifier(*local)); - - foreach (IR::Function *function, irFunction->nestedFunctions) - createFunctionMapping(engine, function); - - - if (engine->debugger) - engine->debugger->mapFunction(vmFunction, irFunction); - - return vmFunction; -} - -VM::Function *EvalInstructionSelection::vmFunction(Function *f) { - VM::Function *function = _irToVM[f]; - if (!function->code) - run(function, f); - return function; -} - -void InstructionSelection::visitMove(IR::Move *s) -{ - if (s->op == IR::OpInvalid) { - if (IR::Name *n = s->target->asName()) { - if (s->source->asTemp() || s->source->asConst()) { - setActivationProperty(s->source, *n->id); - return; - } - } else if (IR::Temp *t = s->target->asTemp()) { - if (IR::Name *n = s->source->asName()) { - if (*n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. - loadThisObject(t); - else - getActivationProperty(*n->id, t); - return; - } else if (IR::Const *c = s->source->asConst()) { - loadConst(c, t); - return; - } else if (IR::Temp *t2 = s->source->asTemp()) { - copyValue(t2, t); - return; - } else if (IR::String *str = s->source->asString()) { - loadString(*str->value, t); - return; - } else if (IR::RegExp *re = s->source->asRegExp()) { - loadRegexp(re, t); - return; - } else if (IR::Closure *clos = s->source->asClosure()) { - initClosure(clos, t); - return; - } else if (IR::New *ctor = s->source->asNew()) { - if (Name *func = ctor->base->asName()) { - constructActivationProperty(func, ctor->args, t); - return; - } else if (IR::Member *member = ctor->base->asMember()) { - constructProperty(member->base->asTemp(), *member->name, ctor->args, t); - return; - } else if (IR::Temp *value = ctor->base->asTemp()) { - constructValue(value, ctor->args, t); - return; - } - } else if (IR::Member *m = s->source->asMember()) { - if (IR::Temp *base = m->base->asTemp()) { - getProperty(base, *m->name, t); - return; - } - } else if (IR::Subscript *ss = s->source->asSubscript()) { - getElement(ss->base->asTemp(), ss->index->asTemp(), t); - return; - } else if (IR::Unop *u = s->source->asUnop()) { - if (IR::Temp *e = u->expr->asTemp()) { - unop(u->op, e, t); - return; - } - } else if (IR::Binop *b = s->source->asBinop()) { - if ((b->left->asTemp() || b->left->asConst()) - && (b->right->asTemp() || b->right->asConst())) { - binop(b->op, b->left, b->right, t); - return; - } - } else if (IR::Call *c = s->source->asCall()) { - if (c->base->asName()) { - callBuiltin(c, t); - return; - } else if (Member *member = c->base->asMember()) { - callProperty(member->base, *member->name, c->args, t); - return; - } else if (Subscript *s = c->base->asSubscript()) { - callSubscript(s->base, s->index, c->args, t); - return; - } else if (IR::Temp *value = c->base->asTemp()) { - callValue(value, c->args, t); - return; - } - } - } else if (IR::Member *m = s->target->asMember()) { - if (IR::Temp *base = m->base->asTemp()) { - if (s->source->asTemp() || s->source->asConst()) { - setProperty(s->source, base, *m->name); - return; - } - } - } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (s->source->asTemp() || s->source->asConst()) { - setElement(s->source, ss->base->asTemp(), ss->index->asTemp()); - return; - } - } - } else { - // inplace assignment, e.g. x += 1, ++x, ... - if (IR::Temp *t = s->target->asTemp()) { - if (s->source->asTemp() || s->source->asConst()) { - binop(s->op, t, s->source, t); - return; - } - } else if (IR::Name *n = s->target->asName()) { - if (s->source->asTemp() || s->source->asConst()) { - inplaceNameOp(s->op, s->source, *n->id); - return; - } - } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (s->source->asTemp() || s->source->asConst()) { - inplaceElementOp(s->op, s->source, ss->base->asTemp(), - ss->index->asTemp()); - return; - } - } else if (IR::Member *m = s->target->asMember()) { - if (s->source->asTemp() || s->source->asConst()) { - inplaceMemberOp(s->op, s->source, m->base->asTemp(), *m->name); - return; - } - } - } - - // For anything else...: - Q_UNIMPLEMENTED(); - s->dump(qout, IR::Stmt::MIR); - qout << endl; - assert(!"TODO"); -} - -InstructionSelection::~InstructionSelection() -{ -} - -void InstructionSelection::visitEnter(Enter *) -{ - Q_UNREACHABLE(); -} - -void InstructionSelection::visitLeave(Leave *) -{ - Q_UNREACHABLE(); -} - -void InstructionSelection::visitExp(IR::Exp *s) -{ - if (IR::Call *c = s->expr->asCall()) { - // These are calls where the result is ignored. - if (c->base->asName()) { - callBuiltin(c, 0); - } else if (Temp *value = c->base->asTemp()) { - callValue(value, c->args, 0); - } else if (Member *member = c->base->asMember()) { - callProperty(member->base, *member->name, c->args, 0); - } else if (Subscript *s = c->base->asSubscript()) { - callSubscript(s->base, s->index, c->args, 0); - } else { - Q_UNIMPLEMENTED(); - } - } else { - Q_UNIMPLEMENTED(); - } -} - -void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result) -{ - IR::Name *baseName = call->base->asName(); - assert(baseName != 0); - - switch (baseName->builtin) { - case IR::Name::builtin_invalid: - callBuiltinInvalid(baseName, call->args, result); - return; - - case IR::Name::builtin_typeof: { - if (IR::Member *m = call->args->expr->asMember()) { - callBuiltinTypeofMember(m->base->asTemp(), *m->name, result); - return; - } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { - callBuiltinTypeofSubscript(ss->base->asTemp(), ss->index->asTemp(), result); - return; - } else if (IR::Name *n = call->args->expr->asName()) { - callBuiltinTypeofName(*n->id, result); - return; - } else if (IR::Temp *arg = call->args->expr->asTemp()){ - assert(arg != 0); - callBuiltinTypeofValue(arg, result); - return; - } - } break; - - case IR::Name::builtin_delete: { - if (IR::Member *m = call->args->expr->asMember()) { - callBuiltinDeleteMember(m->base->asTemp(), *m->name, result); - return; - } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { - callBuiltinDeleteSubscript(ss->base->asTemp(), ss->index->asTemp(), result); - return; - } else if (IR::Name *n = call->args->expr->asName()) { - callBuiltinDeleteName(*n->id, result); - return; - } else if (call->args->expr->asTemp()){ - // TODO: should throw in strict mode - callBuiltinDeleteValue(result); - return; - } - } break; - - case IR::Name::builtin_postincrement: { - if (IR::Member *m = call->args->expr->asMember()) { - callBuiltinPostIncrementMember(m->base->asTemp(), *m->name, result); - return; - } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { - callBuiltinPostIncrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); - return; - } else if (IR::Name *n = call->args->expr->asName()) { - callBuiltinPostIncrementName(*n->id, result); - return; - } else if (IR::Temp *arg = call->args->expr->asTemp()){ - assert(arg != 0); - callBuiltinPostIncrementValue(arg, result); - return; - } - } break; - - case IR::Name::builtin_postdecrement: { - if (IR::Member *m = call->args->expr->asMember()) { - callBuiltinPostDecrementMember(m->base->asTemp(), *m->name, result); - return; - } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { - callBuiltinPostDecrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); - return; - } else if (IR::Name *n = call->args->expr->asName()) { - callBuiltinPostDecrementName(*n->id, result); - return; - } else if (IR::Temp *arg = call->args->expr->asTemp()){ - assert(arg != 0); - callBuiltinPostDecrementValue(arg, result); - return; - } - } break; - - case IR::Name::builtin_throw: { - IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - callBuiltinThrow(arg); - } return; - - case IR::Name::builtin_create_exception_handler: - callBuiltinCreateExceptionHandler(result); - return; - - case IR::Name::builtin_delete_exception_handler: - callBuiltinDeleteExceptionHandler(); - return; - - case IR::Name::builtin_get_exception: - callBuiltinGetException(result); - return; - - case IR::Name::builtin_foreach_iterator_object: { - IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - callBuiltinForeachIteratorObject(arg, result); - } return; - - case IR::Name::builtin_foreach_next_property_name: { - IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - callBuiltinForeachNextPropertyname(arg, result); - } return; - case IR::Name::builtin_push_with_scope: { - IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - callBuiltinPushWithScope(arg); - } return; - - case IR::Name::builtin_pop_scope: - callBuiltinPopScope(); - return; - - case IR::Name::builtin_declare_vars: { - if (!call->args) - return; - IR::Const *deletable = call->args->expr->asConst(); - assert(deletable->type == IR::BoolType); - for (IR::ExprList *it = call->args->next; it; it = it->next) { - IR::Name *arg = it->expr->asName(); - assert(arg != 0); - callBuiltinDeclareVar(deletable->value != 0, *arg->id); - } - } return; - - case IR::Name::builtin_define_getter_setter: { - if (!call->args) - return; - IR::ExprList *args = call->args; - IR::Temp *object = args->expr->asTemp(); - assert(object); - args = args->next; - assert(args); - IR::Name *name = args->expr->asName(); - args = args->next; - assert(args); - IR::Temp *getter = args->expr->asTemp(); - args = args->next; - assert(args); - IR::Temp *setter = args->expr->asTemp(); - - callBuiltinDefineGetterSetter(object, *name->id, getter, setter); - } return; - - case IR::Name::builtin_define_property: { - if (!call->args) - return; - IR::ExprList *args = call->args; - IR::Temp *object = args->expr->asTemp(); - assert(object); - args = args->next; - assert(args); - IR::Name *name = args->expr->asName(); - args = args->next; - assert(args); - IR::Temp *value = args->expr->asTemp(); - - callBuiltinDefineProperty(object, *name->id, value); - } return; - - case IR::Name::builtin_define_array_property: { - if (!call->args) - return; - IR::ExprList *args = call->args; - IR::Temp *object = args->expr->asTemp(); - assert(object); - args = args->next; - assert(args); - IR::Const *index = args->expr->asConst(); - args = args->next; - assert(args); - IR::Temp *value = args->expr->asTemp(); - - callBuiltinDefineArrayProperty(object, int(index->value), value); - } return; - - default: - break; - } - - Q_UNIMPLEMENTED(); - call->dump(qout); qout << endl; - assert(!"TODO!"); - Q_UNREACHABLE(); -} diff --git a/qv4isel_p.h b/qv4isel_p.h deleted file mode 100644 index 0fb3e8de39..0000000000 --- a/qv4isel_p.h +++ /dev/null @@ -1,143 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#ifndef QV4ISEL_P_H -#define QV4ISEL_P_H - -#include "qv4ir_p.h" - -#include -#include - -namespace QQmlJS { - -namespace VM { -struct ExecutionEngine; -struct Function; -} // namespace VM - -class EvalInstructionSelection -{ -public: - EvalInstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); - virtual ~EvalInstructionSelection() = 0; - - VM::Function *vmFunction(IR::Function *f); - -protected: - VM::Function *createFunctionMapping(VM::ExecutionEngine *engine, IR::Function *irFunction); - VM::ExecutionEngine *engine() const { return _engine; } - virtual void run(VM::Function *vmFunction, IR::Function *function) = 0; - -private: - VM::ExecutionEngine *_engine; - QHash _irToVM; -}; - -class EvalISelFactory -{ -public: - virtual ~EvalISelFactory() = 0; - virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module) = 0; -}; - -namespace IR { -class InstructionSelection: protected IR::StmtVisitor -{ -public: - virtual ~InstructionSelection() = 0; - -public: // visitor methods for StmtVisitor: - virtual void visitMove(IR::Move *s); - virtual void visitEnter(IR::Enter *); - virtual void visitLeave(IR::Leave *); - virtual void visitExp(IR::Exp *s); - -public: // to implement by subclasses: - virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) = 0; - virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0; - virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0; - virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result) = 0; - virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) = 0; - virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0; - virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0; - virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result) = 0; - virtual void callBuiltinDeleteValue(IR::Temp *result) = 0; - virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0; - virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0; - virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result) = 0; - virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) = 0; - virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0; - virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0; - virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result) = 0; - virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) = 0; - virtual void callBuiltinThrow(IR::Temp *arg) = 0; - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result) = 0; - virtual void callBuiltinDeleteExceptionHandler() = 0; - virtual void callBuiltinGetException(IR::Temp *result) = 0; - virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) = 0; - virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) = 0; - virtual void callBuiltinPushWithScope(IR::Temp *arg) = 0; - virtual void callBuiltinPopScope() = 0; - virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0; - virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) = 0; - virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) = 0; - virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) = 0; - virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) = 0; - virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) = 0; - virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) = 0; - virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result) = 0; - virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) = 0; - virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) = 0; - virtual void loadThisObject(IR::Temp *temp) = 0; - virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) = 0; - virtual void loadString(const QString &str, IR::Temp *targetTemp) = 0; - virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) = 0; - virtual void getActivationProperty(const QString &name, IR::Temp *temp) = 0; - virtual void setActivationProperty(IR::Expr *source, const QString &targetName) = 0; - virtual void initClosure(IR::Closure *closure, IR::Temp *target) = 0; - virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target) = 0; - virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) = 0; - virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) = 0; - virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) = 0; - virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0; - virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0; - virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) = 0; - virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) = 0; - virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) = 0; - virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) = 0; - -private: - void callBuiltin(IR::Call *c, IR::Temp *temp); -}; -} // namespace IR - -} // namespace QQmlJS - -#endif // QV4ISEL_P_H diff --git a/qv4isel_util_p.h b/qv4isel_util_p.h deleted file mode 100644 index 693af03e88..0000000000 --- a/qv4isel_util_p.h +++ /dev/null @@ -1,63 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#ifndef QV4ISEL_UTIL_P_H -#define QV4ISEL_UTIL_P_H - -#include "qmljs_runtime.h" -#include "qv4ir_p.h" - -namespace QQmlJS { - -inline VM::Value convertToValue(IR::Const *c) -{ - switch (c->type) { - case IR::NullType: - return VM::Value::nullValue(); - case IR::UndefinedType: - return VM::Value::undefinedValue(); - case IR::BoolType: - return VM::Value::fromBoolean(c->value != 0); - case IR::NumberType: { - int ival = (int)c->value; - // +0 != -0, so we need to convert to double when negating 0 - if (ival == c->value && c->value != -0) { - return VM::Value::fromInt32(ival); - } else { - return VM::Value::fromDouble(c->value); - } - } - default: - Q_UNREACHABLE(); - } -} - -} // namespace QQmlJS - -#endif // QV4ISEL_UTIL_P_H diff --git a/qv4jsonobject.cpp b/qv4jsonobject.cpp deleted file mode 100644 index fa39476f4e..0000000000 --- a/qv4jsonobject.cpp +++ /dev/null @@ -1,935 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace QQmlJS { -namespace VM { - -//#define PARSER_DEBUG -#ifdef PARSER_DEBUG -static int indent = 0; -#define BEGIN qDebug() << QByteArray(4*indent++, ' ').constData() -#define END --indent -#define DEBUG qDebug() << QByteArray(4*indent, ' ').constData() -#else -#define BEGIN if (1) ; else qDebug() -#define END do {} while (0) -#define DEBUG if (1) ; else qDebug() -#endif - - -class Parser -{ -public: - Parser(ExecutionContext *context, const QChar *json, int length); - - Value parse(QJsonParseError *error); - -private: - inline bool eatSpace(); - inline QChar nextToken(); - - Value parseObject(); - Value parseArray(); - bool parseMember(Object *o); - bool parseString(QString *string); - bool parseValue(Value *val); - bool parseNumber(Value *val); - - ExecutionContext *context; - const QChar *head; - const QChar *json; - const QChar *end; - - int nestingLevel; - QJsonParseError::ParseError lastError; -}; - -static const int nestingLimit = 1024; - - -Parser::Parser(ExecutionContext *context, const QChar *json, int length) - : context(context), head(json), json(json), nestingLevel(0), lastError(QJsonParseError::NoError) -{ - end = json + length; -} - - - -/* - -begin-array = ws %x5B ws ; [ left square bracket - -begin-object = ws %x7B ws ; { left curly bracket - -end-array = ws %x5D ws ; ] right square bracket - -end-object = ws %x7D ws ; } right curly bracket - -name-separator = ws %x3A ws ; : colon - -value-separator = ws %x2C ws ; , comma - -Insignificant whitespace is allowed before or after any of the six -structural characters. - -ws = *( - %x20 / ; Space - %x09 / ; Horizontal tab - %x0A / ; Line feed or New line - %x0D ; Carriage return - ) - -*/ - -enum { - Space = 0x20, - Tab = 0x09, - LineFeed = 0x0a, - Return = 0x0d, - BeginArray = 0x5b, - BeginObject = 0x7b, - EndArray = 0x5d, - EndObject = 0x7d, - NameSeparator = 0x3a, - ValueSeparator = 0x2c, - Quote = 0x22 -}; - -bool Parser::eatSpace() -{ - while (json < end) { - if (*json > Space) - break; - if (*json != Space && - *json != Tab && - *json != LineFeed && - *json != Return) - break; - ++json; - } - return (json < end); -} - -QChar Parser::nextToken() -{ - if (!eatSpace()) - return 0; - QChar token = *json++; - switch (token.unicode()) { - case BeginArray: - case BeginObject: - case NameSeparator: - case ValueSeparator: - case EndArray: - case EndObject: - eatSpace(); - case Quote: - break; - default: - token = 0; - break; - } - return token; -} - -/* - JSON-text = object / array -*/ -Value Parser::parse(QJsonParseError *error) -{ -#ifdef PARSER_DEBUG - indent = 0; - qDebug() << ">>>>> parser begin"; -#endif - - eatSpace(); - - Value v; - if (!parseValue(&v)) { -#ifdef PARSER_DEBUG - qDebug() << ">>>>> parser error"; -#endif - if (lastError == QJsonParseError::NoError) - lastError = QJsonParseError::IllegalValue; - error->offset = json - head; - error->error = lastError; - return Value::undefinedValue(); - } - - // some input left... - if (eatSpace()) { - lastError = QJsonParseError::IllegalValue; - error->offset = json - head; - error->error = lastError; - return Value::undefinedValue(); - } - - END; - error->offset = 0; - error->error = QJsonParseError::NoError; - return v; -} - -/* - object = begin-object [ member *( value-separator member ) ] - end-object -*/ - -Value Parser::parseObject() -{ - if (++nestingLevel > nestingLimit) { - lastError = QJsonParseError::DeepNesting; - return Value::undefinedValue(); - } - - BEGIN << "parseObject pos=" << json; - - Object *o = context->engine->newObject(); - Value objectVal = Value::fromObject(o); - - QChar token = nextToken(); - while (token == Quote) { - if (!parseMember(o)) - return Value::undefinedValue(); - token = nextToken(); - if (token != ValueSeparator) - break; - token = nextToken(); - if (token == EndObject) { - lastError = QJsonParseError::MissingObject; - return Value::undefinedValue(); - } - } - - DEBUG << "end token=" << token; - if (token != EndObject) { - lastError = QJsonParseError::UnterminatedObject; - return Value::undefinedValue(); - } - - END; - - --nestingLevel; - return objectVal; -} - -/* - member = string name-separator value -*/ -bool Parser::parseMember(Object *o) -{ - BEGIN << "parseMember"; - if (!o->members) - o->members.reset(new PropertyTable()); - - QString key; - if (!parseString(&key)) - return false; - QChar token = nextToken(); - if (token != NameSeparator) { - lastError = QJsonParseError::MissingNameSeparator; - return false; - } - Value val; - if (!parseValue(&val)) - return false; - - PropertyDescriptor *p = o->members->insert(context->engine->identifier(key)); - p->value = val; - - END; - return true; -} - -/* - array = begin-array [ value *( value-separator value ) ] end-array -*/ -Value Parser::parseArray() -{ - BEGIN << "parseArray"; - Array array; - - if (++nestingLevel > nestingLimit) { - lastError = QJsonParseError::DeepNesting; - return Value::undefinedValue(); - } - - if (!eatSpace()) { - lastError = QJsonParseError::UnterminatedArray; - return Value::undefinedValue(); - } - if (*json == EndArray) { - nextToken(); - } else { - uint index = 0; - while (1) { - Value val; - if (!parseValue(&val)) - return Value::undefinedValue(); - array.set(index, val); - QChar token = nextToken(); - if (token == EndArray) - break; - else if (token != ValueSeparator) { - if (!eatSpace()) - lastError = QJsonParseError::UnterminatedArray; - else - lastError = QJsonParseError::MissingValueSeparator; - return Value::undefinedValue(); - } - ++index; - } - } - - DEBUG << "size =" << array.length(); - END; - - --nestingLevel; - return Value::fromObject(context->engine->newArrayObject(context, array)); -} - -/* -value = false / null / true / object / array / number / string - -*/ - -bool Parser::parseValue(Value *val) -{ - BEGIN << "parse Value" << *json; - - switch ((json++)->unicode()) { - case 'n': - if (end - json < 4) { - lastError = QJsonParseError::IllegalValue; - return false; - } - if (*json++ == 'u' && - *json++ == 'l' && - *json++ == 'l') { - *val = Value::nullValue(); - DEBUG << "value: null"; - END; - return true; - } - lastError = QJsonParseError::IllegalValue; - return false; - case 't': - if (end - json < 4) { - lastError = QJsonParseError::IllegalValue; - return false; - } - if (*json++ == 'r' && - *json++ == 'u' && - *json++ == 'e') { - *val = Value::fromBoolean(true); - DEBUG << "value: true"; - END; - return true; - } - lastError = QJsonParseError::IllegalValue; - return false; - case 'f': - if (end - json < 5) { - lastError = QJsonParseError::IllegalValue; - return false; - } - if (*json++ == 'a' && - *json++ == 'l' && - *json++ == 's' && - *json++ == 'e') { - *val = Value::fromBoolean(false); - DEBUG << "value: false"; - END; - return true; - } - lastError = QJsonParseError::IllegalValue; - return false; - case Quote: { - QString value; - if (!parseString(&value)) - return false; - DEBUG << "value: string"; - END; - *val = Value::fromString(context, value); - return true; - } - case BeginArray: { - *val = parseArray(); - if (val->isUndefined()) - return false; - DEBUG << "value: array"; - END; - return true; - } - case BeginObject: { - *val = parseObject(); - if (val->isUndefined()) - return false; - DEBUG << "value: object"; - END; - return true; - } - case EndArray: - lastError = QJsonParseError::MissingObject; - return false; - default: - --json; - if (!parseNumber(val)) - return false; - DEBUG << "value: number"; - END; - } - - return true; -} - - - - - -/* - number = [ minus ] int [ frac ] [ exp ] - decimal-point = %x2E ; . - digit1-9 = %x31-39 ; 1-9 - e = %x65 / %x45 ; e E - exp = e [ minus / plus ] 1*DIGIT - frac = decimal-point 1*DIGIT - int = zero / ( digit1-9 *DIGIT ) - minus = %x2D ; - - plus = %x2B ; + - zero = %x30 ; 0 - -*/ - -bool Parser::parseNumber(Value *val) -{ - BEGIN << "parseNumber" << *json; - - const QChar *start = json; - bool isInt = true; - - // minus - if (json < end && *json == '-') - ++json; - - // int = zero / ( digit1-9 *DIGIT ) - if (json < end && *json == '0') { - ++json; - } else { - while (json < end && *json >= '0' && *json <= '9') - ++json; - } - - // frac = decimal-point 1*DIGIT - if (json < end && *json == '.') { - isInt = false; - ++json; - while (json < end && *json >= '0' && *json <= '9') - ++json; - } - - // exp = e [ minus / plus ] 1*DIGIT - if (json < end && (*json == 'e' || *json == 'E')) { - isInt = false; - ++json; - if (json < end && (*json == '-' || *json == '+')) - ++json; - while (json < end && *json >= '0' && *json <= '9') - ++json; - } - - QString number(start, json - start); - DEBUG << "numberstring" << number; - - if (isInt) { - bool ok; - int n = number.toInt(&ok); - if (ok && n < (1<<25) && n > -(1<<25)) { - *val = Value::fromInt32(n); - END; - return true; - } - } - - bool ok; - double d; - d = number.toDouble(&ok); - - if (!ok) { - lastError = QJsonParseError::IllegalNumber; - return false; - } - - * val = Value::fromDouble(d); - - END; - return true; -} - -/* - - string = quotation-mark *char quotation-mark - - char = unescaped / - escape ( - %x22 / ; " quotation mark U+0022 - %x5C / ; \ reverse solidus U+005C - %x2F / ; / solidus U+002F - %x62 / ; b backspace U+0008 - %x66 / ; f form feed U+000C - %x6E / ; n line feed U+000A - %x72 / ; r carriage return U+000D - %x74 / ; t tab U+0009 - %x75 4HEXDIG ) ; uXXXX U+XXXX - - escape = %x5C ; \ - - quotation-mark = %x22 ; " - - unescaped = %x20-21 / %x23-5B / %x5D-10FFFF - */ -static inline bool addHexDigit(QChar digit, uint *result) -{ - ushort d = digit.unicode(); - *result <<= 4; - if (d >= '0' && d <= '9') - *result |= (d - '0'); - else if (d >= 'a' && d <= 'f') - *result |= (d - 'a') + 10; - else if (d >= 'A' && d <= 'F') - *result |= (d - 'A') + 10; - else - return false; - return true; -} - -static inline bool scanEscapeSequence(const QChar *&json, const QChar *end, uint *ch) -{ - ++json; - if (json >= end) - return false; - - DEBUG << "scan escape"; - uint escaped = (json++)->unicode(); - switch (escaped) { - case '"': - *ch = '"'; break; - case '\\': - *ch = '\\'; break; - case '/': - *ch = '/'; break; - case 'b': - *ch = 0x8; break; - case 'f': - *ch = 0xc; break; - case 'n': - *ch = 0xa; break; - case 'r': - *ch = 0xd; break; - case 't': - *ch = 0x9; break; - case 'u': { - *ch = 0; - if (json > end - 4) - return false; - for (int i = 0; i < 4; ++i) { - if (!addHexDigit(*json, ch)) - return false; - ++json; - } - if (*ch <= 0x1f) - return false; - return true; - } - default: - return false; - } - return true; -} - - -bool Parser::parseString(QString *string) -{ - BEGIN << "parse string stringPos=" << json; - - while (json < end) { - if (*json == '"') - break; - else if (*json == '\\') { - uint ch = 0; - if (!scanEscapeSequence(json, end, &ch)) { - lastError = QJsonParseError::IllegalEscapeSequence; - return false; - } - qDebug() << "scanEscape" << hex << ch; - if (QChar::requiresSurrogates(ch)) { - *string += QChar::highSurrogate(ch); - *string += QChar::lowSurrogate(ch); - } else { - *string += QChar(ch); - } - } else { - if (json->unicode() <= 0x1f) { - lastError = QJsonParseError::IllegalEscapeSequence; - return false; - } - *string += *json; - ++json; - } - } - ++json; - - if (json > end) { - lastError = QJsonParseError::UnterminatedString; - return false; - } - - END; - return true; -} - - -struct Stringify -{ - ExecutionContext *ctx; - FunctionObject *replacerFunction; - QVector propertyList; - QString gap; - QString indent; - - QStack stack; - - Stringify(ExecutionContext *ctx) : ctx(ctx), replacerFunction(0) {} - - QString Str(const QString &key, Value value); - QString JA(ArrayObject *a); - QString JO(Object *o); - - QString makeMember(const QString &key, Value v); -}; - -static QString quote(const QString &str) -{ - QString product = "\""; - for (int i = 0; i < str.length(); ++i) { - QChar c = str.at(i); - switch (c.unicode()) { - case '"': - product += "\\\""; - break; - case '\\': - product += "\\\\"; - break; - case '\b': - product += "\\b"; - break; - case '\f': - product += "\\f"; - break; - case '\n': - product += "\\n"; - break; - case '\r': - product += "\\r"; - break; - case '\t': - product += "\\t"; - break; - default: - if (c.unicode() <= 0x1f) { - product += "\\u00"; - product += c.unicode() > 0xf ? '1' : '0'; - product += "0123456789abcdef"[c.unicode() & 0xf]; - } else { - product += c; - } - } - } - product += '"'; - return product; -} - -QString Stringify::Str(const QString &key, Value value) -{ - QString result; - - if (Object *o = value.asObject()) { - FunctionObject *toJSON = o->__get__(ctx, ctx->engine->identifier(QStringLiteral("toJSON"))).asFunctionObject(); - if (toJSON) { - Value arg = Value::fromString(ctx, key); - value = toJSON->call(ctx, value, &arg, 1); - } - } - - if (replacerFunction) { - Object *holder = ctx->engine->newObject(); - Value holderValue = Value::fromObject(holder); - holder->__put__(ctx, QString(), value); - Value args[2]; - args[0] = Value::fromString(ctx, key); - args[1] = value; - value = replacerFunction->call(ctx, holderValue, args, 2); - } - - if (Object *o = value.asObject()) { - if (NumberObject *n = o->asNumberObject()) - value = n->value; - else if (StringObject *so = o->asStringObject()) - value = so->value; - else if (BooleanObject *b =o->asBooleanObject()) - value = b->value; - } - - if (value.isNull()) - return QStringLiteral("null"); - if (value.isBoolean()) - return value.booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); - if (value.isString()) - return quote(value.stringValue()->toQString()); - - if (value.isNumber()) { - double d = value.toNumber(ctx); - return std::isfinite(d) ? value.toString(ctx)->toQString() : QStringLiteral("null"); - } - - if (Object *o = value.asObject()) { - if (!o->asFunctionObject()) { - if (o->asArrayObject()) - return JA(static_cast(o)); - else - return JO(o); - } - } - - return QString(); -} - -QString Stringify::makeMember(const QString &key, Value v) -{ - QString strP = Str(key, v); - if (!strP.isEmpty()) { - QString member = quote(key) + ':'; - if (!gap.isEmpty()) - member += ' '; - member += strP; - return member; - } - return QString(); -} - -QString Stringify::JO(Object *o) -{ - if (stack.contains(o)) - ctx->throwTypeError(); - - QString result; - stack.push(o); - QString stepback = indent; - indent += gap; - - QStringList partial; - if (propertyList.isEmpty()) { - ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); - - while (1) { - String *name; - uint index; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - Value v = o->getValueChecked(ctx, pd); - QString key; - if (name) - key = name->toQString(); - else - key = QString::number(index); - QString member = makeMember(key, v); - if (!member.isEmpty()) - partial += member; - } - } else { - for (int i = 0; i < propertyList.size(); ++i) { - bool exists; - Value v = o->__get__(ctx, propertyList.at(i), &exists); - if (!exists) - continue; - QString member = makeMember(propertyList.at(i)->toQString(), v); - if (!member.isEmpty()) - partial += member; - } - } - - if (partial.isEmpty()) { - result = QStringLiteral("{}"); - } else if (gap.isEmpty()) { - result = "{" + partial.join(",") + "}"; - } else { - QString separator = ",\n" + indent; - result = "{\n" + indent + partial.join(separator) + "\n" + stepback + "}"; - } - - indent = stepback; - stack.pop(); - return result; -} - -QString Stringify::JA(ArrayObject *a) -{ - if (stack.contains(a)) - ctx->throwTypeError(); - - QString result; - stack.push(a); - QString stepback = indent; - indent += gap; - - QStringList partial; - uint len = a->array.length(); - for (uint i = 0; i < len; ++i) { - bool exists; - Value v = a->__get__(ctx, i, &exists); - if (!exists) { - partial += QStringLiteral("null"); - continue; - } - QString strP = Str(QString::number(i), v); - if (!strP.isEmpty()) - partial += strP; - else - partial += QStringLiteral("null"); - } - - if (partial.isEmpty()) { - result = QStringLiteral("[]"); - } else if (gap.isEmpty()) { - result = "[" + partial.join(",") + "]"; - } else { - QString separator = ",\n" + indent; - result = "[\n" + indent + partial.join(separator) + "\n" + stepback + "]"; - } - - indent = stepback; - stack.pop(); - return result; -} - - -JsonObject::JsonObject(ExecutionContext *context) - : Object() -{ - type = Type_JSONObject; - prototype = context->engine->objectPrototype; - - defineDefaultProperty(context, QStringLiteral("parse"), method_parse, 2); - defineDefaultProperty(context, QStringLiteral("stringify"), method_stringify, 3); -} - - -Value JsonObject::method_parse(ExecutionContext *ctx) -{ - QString jtext = ctx->argument(0).toString(ctx)->toQString(); - - DEBUG << "parsing source = " << jtext; - Parser parser(ctx, jtext.constData(), jtext.length()); - QJsonParseError error; - Value result = parser.parse(&error); - if (error.error != QJsonParseError::NoError) { - DEBUG << "parse error" << error.errorString(); - ctx->throwSyntaxError(0); - } - - return result; -} - -Value JsonObject::method_stringify(ExecutionContext *ctx) -{ - Stringify stringify(ctx); - - Object *o = ctx->argument(1).asObject(); - if (o) { - stringify.replacerFunction = o->asFunctionObject(); - if (o->isArrayObject()) { - for (uint i = 0; i < o->array.length(); ++i) { - Value v = o->__get__(ctx, i); - if (v.asNumberObject() || v.asStringObject() || v.isNumber()) - v = __qmljs_to_string(v, ctx); - if (v.isString()) { - String *s = v.stringValue(); - if (!stringify.propertyList.contains(s)) - stringify.propertyList.append(s); - } - } - } - } - - Value s = ctx->argument(2); - if (NumberObject *n = s.asNumberObject()) - s = n->value; - else if (StringObject *so = s.asStringObject()) - s = so->value; - - if (s.isNumber()) { - stringify.gap = QString(qMin(10, (int)s.toInteger(ctx)), ' '); - } else if (s.isString()) { - stringify.gap = s.stringValue()->toQString().left(10); - } - - - QString result = stringify.Str(QString(), ctx->argument(0)); - if (result.isEmpty()) - return Value::undefinedValue(); - return Value::fromString(ctx, result); -} - - - -} -} diff --git a/qv4jsonobject.h b/qv4jsonobject.h deleted file mode 100644 index 7c1df7c40b..0000000000 --- a/qv4jsonobject.h +++ /dev/null @@ -1,61 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4JSONOBJECTS_H -#define QV4SJONOBJECTS_H - -#include - -namespace QQmlJS { -namespace VM { - -struct JsonObject : Object { - JsonObject(ExecutionContext *context); - - static Value method_parse(ExecutionContext *ctx); - static Value method_stringify(ExecutionContext *ctx); - -}; - -} -} - -#endif - diff --git a/qv4managed.cpp b/qv4managed.cpp deleted file mode 100644 index 8156878508..0000000000 --- a/qv4managed.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4managed.h" -#include "qv4mm.h" -#include "qv4errorobject.h" - -using namespace QQmlJS::VM; - -Managed::~Managed() -{ - nextFree = 0; - inUse = 0; -} - -void *Managed::operator new(size_t size, MemoryManager *mm) -{ - assert(mm); - - return mm->allocManaged(size); -} - -void Managed::operator delete(void *ptr) -{ - if (!ptr) - return; - - Managed *m = static_cast(ptr); - m->~Managed(); -} - - -QString Managed::className() const -{ - const char *s = 0; - switch (Type(type)) { - case Type_Invalid: - case Type_String: - return QString(); - case Type_Object: - s = "Object"; - break; - case Type_ArrayObject: - s = "Array"; - break; - case Type_FunctionObject: - s = "Function"; - break; - case Type_BooleanObject: - s = "Boolean"; - break; - case Type_NumberObject: - s = "Number"; - break; - case Type_StringObject: - s = "String"; - break; - case Type_DateObject: - s = "Date"; - break; - case Type_RegExpObject: - s = "RegExp"; - break; - case Type_ErrorObject: - switch (static_cast(this)->errorType) { - case ErrorObject::Error: - s = "Error"; - break; - case ErrorObject::EvalError: - s = "EvalError"; - break; - case ErrorObject::RangeError: - s = "RangeError"; - break; - case ErrorObject::ReferenceError: - s = "ReferenceError"; - break; - case ErrorObject::SyntaxError: - s = "SyntaxError"; - break; - case ErrorObject::TypeError: - s = "TypeError"; - break; - case ErrorObject::URIError: - s = "URIError"; - break; - } - break; - case Type_ArgumentsObject: - s = "Arguments"; - break; - case Type_JSONObject: - s = "JSON"; - break; - case Type_MathObject: - s = "Math"; - break; - case Type_ForeachIteratorObject: - s = "__ForeachIterator"; - break; - } - return QString::fromLatin1(s); -} diff --git a/qv4managed.h b/qv4managed.h deleted file mode 100644 index 7b6768d166..0000000000 --- a/qv4managed.h +++ /dev/null @@ -1,172 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QMLJS_MANAGED_H -#define QMLJS_MANAGED_H - -#include -#include -#include -#include - -namespace QQmlJS { - -namespace VM { - -class MemoryManager; -struct String; -struct Object; -struct ObjectPrototype; -struct ExecutionContext; -struct ScriptFunction; - -struct BooleanObject; -struct NumberObject; -struct StringObject; -struct ArrayObject; -struct DateObject; -struct FunctionObject; -struct RegExpObject; -struct ErrorObject; -struct ArgumentsObject; -struct JSONObject; -struct ForeachIteratorObject; - -struct Managed -{ -private: - void *operator new(size_t); - Managed(const Managed &other); - void operator = (const Managed &other); - -protected: - Managed() - : markBit(0) - , inUse(1) - , extensible(1) - , isNonStrictArgumentsObject(0) - , isBuiltinFunction(0) - , needsActivation(0) - , usesArgumentsObject(0) - , strictMode(0) - , type(Type_Invalid) - , unused(0) - , stringHash(0) - {} - virtual ~Managed(); - -public: - void *operator new(size_t size, MemoryManager *mm); - void operator delete(void *ptr); - - inline void mark() { - if (markBit) - return; - markBit = 1; - if (type != Type_String) - markObjects(); - } - - enum Type { - Type_Invalid, - Type_String, - Type_Object, - Type_ArrayObject, - Type_FunctionObject, - Type_BooleanObject, - Type_NumberObject, - Type_StringObject, - Type_DateObject, - Type_RegExpObject, - Type_ErrorObject, - Type_ArgumentsObject, - Type_JSONObject, - Type_MathObject, - Type_ForeachIteratorObject - }; - - String *asString() { return reinterpret_cast(this); } - Object *asObject() { return reinterpret_cast(this); } - ArrayObject *asArrayObject() { return type == Type_ArrayObject ? reinterpret_cast(this) : 0; } - FunctionObject *asFunctionObject() { return type == Type_FunctionObject ? reinterpret_cast(this) : 0; } - BooleanObject *asBooleanObject() { return type == Type_BooleanObject ? reinterpret_cast(this) : 0; } - NumberObject *asNumberObject() { return type == Type_NumberObject ? reinterpret_cast(this) : 0; } - StringObject *asStringObject() { return type == Type_StringObject ? reinterpret_cast(this) : 0; } - DateObject *asDateObject() { return type == Type_DateObject ? reinterpret_cast(this) : 0; } - RegExpObject *asRegExpObject() { return type == Type_RegExpObject ? reinterpret_cast(this) : 0; } - ErrorObject *asErrorObject() { return type == Type_ErrorObject ? reinterpret_cast(this) : 0; } - ArgumentsObject *asArgumentsObject() { return type == Type_ArgumentsObject ? reinterpret_cast(this) : 0; } - JSONObject *asJSONObject() { return type == Type_JSONObject ? reinterpret_cast(this) : 0; } - ForeachIteratorObject *asForeachIteratorObject() { return type == Type_ForeachIteratorObject ? reinterpret_cast(this) : 0; } - - bool isArrayObject() const { return type == Type_ArrayObject; } - bool isStringObject() const { return type == Type_StringObject; } - - QString className() const; - -protected: - virtual void markObjects() {} - - union { - Managed *nextFree; - struct { - quintptr markBit : 1; - quintptr inUse : 1; - quintptr extensible : 1; // used by Object - quintptr isNonStrictArgumentsObject : 1; - quintptr isBuiltinFunction : 1; // used by FunctionObject - quintptr needsActivation : 1; // used by FunctionObject - quintptr usesArgumentsObject : 1; // used by FunctionObject - quintptr strictMode : 1; // used by FunctionObject - quintptr type : 8; - quintptr unused : 16; - mutable quintptr stringHash : 32; - }; - }; - -private: - friend class MemoryManager; - friend struct ExecutionContext; -}; - -} -} - -#endif diff --git a/qv4mathobject.cpp b/qv4mathobject.cpp deleted file mode 100644 index e40faba861..0000000000 --- a/qv4mathobject.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4mathobject.h" - -#include -#include -#include - -using namespace QQmlJS::VM; - -static const double qt_PI = 2.0 * ::asin(1.0); - -MathObject::MathObject(ExecutionContext *ctx) -{ - type = Type_MathObject; - - defineReadonlyProperty(ctx->engine, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("LN10"), Value::fromDouble(::log(10.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("LOG2E"), Value::fromDouble(1.0/::log(2.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("LOG10E"), Value::fromDouble(1.0/::log(10.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("PI"), Value::fromDouble(qt_PI)); - defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT1_2"), Value::fromDouble(::sqrt(0.5))); - defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT2"), Value::fromDouble(::sqrt(2.0))); - - defineDefaultProperty(ctx, QStringLiteral("abs"), method_abs, 1); - defineDefaultProperty(ctx, QStringLiteral("acos"), method_acos, 1); - defineDefaultProperty(ctx, QStringLiteral("asin"), method_asin, 0); - defineDefaultProperty(ctx, QStringLiteral("atan"), method_atan, 1); - defineDefaultProperty(ctx, QStringLiteral("atan2"), method_atan2, 2); - defineDefaultProperty(ctx, QStringLiteral("ceil"), method_ceil, 1); - defineDefaultProperty(ctx, QStringLiteral("cos"), method_cos, 1); - defineDefaultProperty(ctx, QStringLiteral("exp"), method_exp, 1); - defineDefaultProperty(ctx, QStringLiteral("floor"), method_floor, 1); - defineDefaultProperty(ctx, QStringLiteral("log"), method_log, 1); - defineDefaultProperty(ctx, QStringLiteral("max"), method_max, 2); - defineDefaultProperty(ctx, QStringLiteral("min"), method_min, 2); - defineDefaultProperty(ctx, QStringLiteral("pow"), method_pow, 2); - defineDefaultProperty(ctx, QStringLiteral("random"), method_random, 0); - defineDefaultProperty(ctx, QStringLiteral("round"), method_round, 1); - defineDefaultProperty(ctx, QStringLiteral("sin"), method_sin, 1); - defineDefaultProperty(ctx, QStringLiteral("sqrt"), method_sqrt, 1); - defineDefaultProperty(ctx, QStringLiteral("tan"), method_tan, 1); -} - -/* copies the sign from y to x and returns the result */ -static double copySign(double x, double y) -{ - uchar *xch = (uchar *)&x; - uchar *ych = (uchar *)&y; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - xch[0] = (xch[0] & 0x7f) | (ych[0] & 0x80); - else - xch[7] = (xch[7] & 0x7f) | (ych[7] & 0x80); - return x; -} - -Value MathObject::method_abs(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (v == 0) // 0 | -0 - return Value::fromDouble(0); - - return Value::fromDouble(v < 0 ? -v : v); -} - -Value MathObject::method_acos(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (v > 1) - return Value::fromDouble(qSNaN()); - - return Value::fromDouble(::acos(v)); -} - -Value MathObject::method_asin(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (v > 1) - return Value::fromDouble(qSNaN()); - else - return Value::fromDouble(::asin(v)); -} - -Value MathObject::method_atan(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (v == 0.0) - return Value::fromDouble(v); - else - return Value::fromDouble(::atan(v)); -} - -Value MathObject::method_atan2(ExecutionContext *ctx) -{ - double v1 = ctx->argument(0).toNumber(ctx); - double v2 = ctx->argument(1).toNumber(ctx); - if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) { - return Value::fromDouble(copySign(0, -1.0)); - } - if ((v1 == 0.0) && (v2 == 0.0)) { - if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) { - return Value::fromDouble(qt_PI); - } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) { - return Value::fromDouble(-qt_PI); - } - } - return Value::fromDouble(::atan2(v1, v2)); -} - -Value MathObject::method_ceil(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (v < 0.0 && v > -1.0) - return Value::fromDouble(copySign(0, -1.0)); - else - return Value::fromDouble(::ceil(v)); -} - -Value MathObject::method_cos(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - return Value::fromDouble(::cos(v)); -} - -Value MathObject::method_exp(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (qIsInf(v)) { - if (copySign(1.0, v) == -1.0) - return Value::fromDouble(0); - else - return Value::fromDouble(qInf()); - } else { - return Value::fromDouble(::exp(v)); - } -} - -Value MathObject::method_floor(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - return Value::fromDouble(::floor(v)); -} - -Value MathObject::method_log(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (v < 0) - return Value::fromDouble(qSNaN()); - else - return Value::fromDouble(::log(v)); -} - -Value MathObject::method_max(ExecutionContext *ctx) -{ - double mx = -qInf(); - for (unsigned i = 0; i < ctx->argumentCount; ++i) { - double x = ctx->argument(i).toNumber(ctx); - if (x > mx || std::isnan(x)) - mx = x; - } - return Value::fromDouble(mx); -} - -Value MathObject::method_min(ExecutionContext *ctx) -{ - double mx = qInf(); - for (unsigned i = 0; i < ctx->argumentCount; ++i) { - double x = ctx->argument(i).toNumber(ctx); - if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) - || (x < mx) || std::isnan(x)) { - mx = x; - } - } - return Value::fromDouble(mx); -} - -Value MathObject::method_pow(ExecutionContext *parentCtx, Value, Value *argv, int argc) -{ - double x = argc > 0 ? argv[0].toNumber(parentCtx) : qSNaN(); - double y = argc > 1 ? argv[1].toNumber(parentCtx) : qSNaN(); - - if (std::isnan(y)) - return Value::fromDouble(qSNaN()); - - if (y == 0) { - return Value::fromDouble(1); - } else if (((x == 1) || (x == -1)) && std::isinf(y)) { - return Value::fromDouble(qSNaN()); - } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { - return Value::fromDouble(qInf()); - } else if ((x == 0) && copySign(1.0, x) == -1.0) { - if (y < 0) { - if (::fmod(-y, 2.0) == 1.0) - return Value::fromDouble(-qInf()); - else - return Value::fromDouble(qInf()); - } else if (y > 0) { - if (::fmod(y, 2.0) == 1.0) - return Value::fromDouble(copySign(0, -1.0)); - else - return Value::fromDouble(0); - } - } - -#ifdef Q_OS_AIX - else if (qIsInf(x) && copySign(1.0, x) == -1.0) { - if (y > 0) { - if (::fmod(y, 2.0) == 1.0) - return Value::fromDouble(-qInf()); - else - return Value::fromDouble(qInf()); - } else if (y < 0) { - if (::fmod(-y, 2.0) == 1.0) - return Value::fromDouble(copySign(0, -1.0)); - else - return Value::fromDouble(0); - } - } -#endif - else { - return Value::fromDouble(::pow(x, y)); - } - // ### - return Value::fromDouble(qSNaN()); -} - -Value MathObject::method_random(ExecutionContext */*ctx*/) -{ - return Value::fromDouble(qrand() / (double) RAND_MAX); -} - -Value MathObject::method_round(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - v = copySign(::floor(v + 0.5), v); - return Value::fromDouble(v); -} - -Value MathObject::method_sin(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - return Value::fromDouble(::sin(v)); -} - -Value MathObject::method_sqrt(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - return Value::fromDouble(::sqrt(v)); -} - -Value MathObject::method_tan(ExecutionContext *ctx) -{ - double v = ctx->argument(0).toNumber(ctx); - if (v == 0.0) - return Value::fromDouble(v); - else - return Value::fromDouble(::tan(v)); -} - diff --git a/qv4mathobject.h b/qv4mathobject.h deleted file mode 100644 index c8428d2942..0000000000 --- a/qv4mathobject.h +++ /dev/null @@ -1,78 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4MATHOBJECT_H -#define QV$MATHOBJECT_H - -#include "qv4object.h" - -namespace QQmlJS { - -namespace VM { - -struct MathObject: Object -{ - MathObject(ExecutionContext *ctx); - virtual QString className() { return QStringLiteral("Math"); } - - static Value method_abs(ExecutionContext *ctx); - static Value method_acos(ExecutionContext *ctx); - static Value method_asin(ExecutionContext *ctx); - static Value method_atan(ExecutionContext *ctx); - static Value method_atan2(ExecutionContext *ctx); - static Value method_ceil(ExecutionContext *ctx); - static Value method_cos(ExecutionContext *ctx); - static Value method_exp(ExecutionContext *ctx); - static Value method_floor(ExecutionContext *ctx); - static Value method_log(ExecutionContext *ctx); - static Value method_max(ExecutionContext *ctx); - static Value method_min(ExecutionContext *ctx); - static Value method_pow(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_random(ExecutionContext *ctx); - static Value method_round(ExecutionContext *ctx); - static Value method_sin(ExecutionContext *ctx); - static Value method_sqrt(ExecutionContext *ctx); - static Value method_tan(ExecutionContext *ctx); -}; - -} // namespace VM -} // namespace QQmlJS - -#endif // QMLJS_OBJECTS_H diff --git a/qv4mm.cpp b/qv4mm.cpp deleted file mode 100644 index c77ec1eaba..0000000000 --- a/qv4mm.cpp +++ /dev/null @@ -1,396 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#include "qmljs_engine.h" -#include "qv4object.h" -#include "qv4objectproto.h" -#include "qv4mm.h" -#include "PageAllocation.h" -#include "StdLibExtras.h" - -#include -#include -#include -#include - -#include -#include -#include - -using namespace QQmlJS::VM; -using namespace WTF; - -static const std::size_t CHUNK_SIZE = 65536; - -struct MemoryManager::Data -{ - bool enableGC; - bool gcBlocked; - bool scribble; - bool aggressiveGC; - ExecutionEngine *engine; - - enum { MaxItemSize = 256 }; - Managed *smallItems[MaxItemSize/16]; - uint nChunks[MaxItemSize/16]; - struct Chunk { - PageAllocation memory; - int chunkSize; - }; - - QVector heapChunks; - QHash protectedObject; - - // statistics: -#ifdef DETAILED_MM_STATS - QVector allocSizeCounters; -#endif // DETAILED_MM_STATS - - Data(bool enableGC) - : enableGC(enableGC) - , gcBlocked(false) - , engine(0) - { - memset(smallItems, 0, sizeof(smallItems)); - memset(nChunks, 0, sizeof(nChunks)); - scribble = qgetenv("MM_NO_SCRIBBLE").isEmpty(); - aggressiveGC = !qgetenv("MM_AGGRESSIVE_GC").isEmpty(); - } - - ~Data() - { - for (QVector::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) - i->memory.deallocate(); - } -}; - -namespace QQmlJS { namespace VM { - -bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::Chunk &b) -{ - return a.memory.base() < b.memory.base(); -} - -} } // namespace QQmlJS::VM - -MemoryManager::MemoryManager() - : m_d(new Data(true)) -{ - setEnableGC(true); -} - -Managed *MemoryManager::alloc(std::size_t size) -{ - if (m_d->aggressiveGC) - runGC(); -#ifdef DETAILED_MM_STATS - willAllocate(size); -#endif // DETAILED_MM_STATS - - assert(size >= 16); - assert(size % 16 == 0); - - size_t pos = size >> 4; - - // fits into a small bucket - assert(size < MemoryManager::Data::MaxItemSize); - - Managed *m = m_d->smallItems[pos]; - if (m) - goto found; - - // try to free up space, otherwise allocate - if (!m_d->aggressiveGC) { - runGC(); - m = m_d->smallItems[pos]; - if (m) - goto found; - } - - // no free item available, allocate a new chunk - { - // allocate larger chunks at a time to avoid excessive GC, but cap at 64M chunks - uint shift = ++m_d->nChunks[pos]; - if (shift > 10) - shift = 10; - std::size_t allocSize = std::max(size, CHUNK_SIZE*(1 << shift)); - allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); - Data::Chunk allocation; - allocation.memory = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); - allocation.chunkSize = size; - m_d->heapChunks.append(allocation); - qSort(m_d->heapChunks); - char *chunk = (char *)allocation.memory.base(); - char *end = chunk + allocation.memory.size() - size; - memset(chunk, 0, allocation.memory.size()); - Managed **last = &m_d->smallItems[pos]; - while (chunk <= end) { - Managed *o = reinterpret_cast(chunk); - o->inUse = 0; - o->markBit = 0; - *last = o; - last = &o->nextFree; - chunk += size; - } - *last = 0; - m = m_d->smallItems[pos]; - } - - found: - m_d->smallItems[pos] = m->nextFree; - return m; -} - -void MemoryManager::scribble(Managed *obj, int c, int size) const -{ - if (m_d->scribble) - ::memset((void *)(obj + 1), c, size - sizeof(Managed)); -} - -void MemoryManager::mark() -{ - m_d->engine->markObjects(); - - for (ExecutionContext *ctxt = engine()->current; ctxt; ctxt = ctxt->parent) - ctxt->mark(); - - for (QHash::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it) - it.key()->mark(); - - collectFromStack(); - - return; -} - -std::size_t MemoryManager::sweep() -{ - std::size_t freedCount = 0; - - for (QVector::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) - freedCount += sweep(reinterpret_cast(i->memory.base()), i->memory.size(), i->chunkSize); - - return freedCount; -} - -std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t size) -{ -// qDebug("chunkStart @ %p, size=%x, pos=%x (%x)", chunkStart, size, size>>4, m_d->smallItems[size >> 4]); - std::size_t freedCount = 0; - - Managed **f = &m_d->smallItems[size >> 4]; - - for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize - size; chunk <= chunkEnd; ) { - Managed *m = reinterpret_cast(chunk); -// qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s", -// chunk, m->size, (m->inUse ? "yes" : "no"), (m->markBit ? "true" : "false")); - - assert((intptr_t) chunk % 16 == 0); - - chunk = chunk + size; - if (m->inUse) { - if (m->markBit) { - m->markBit = 0; - } else { -// qDebug() << "-- collecting it." << m << *f << &m->nextFree; - m->~Managed(); - - m->nextFree = *f; - f = &m->nextFree; - //scribble(m, 0x99, size); - ++freedCount; - } - } - } - - return freedCount; -} - -bool MemoryManager::isGCBlocked() const -{ - return m_d->gcBlocked; -} - -void MemoryManager::setGCBlocked(bool blockGC) -{ - m_d->gcBlocked = blockGC; -} - -void MemoryManager::runGC() -{ - if (!m_d->enableGC || m_d->gcBlocked) { -// qDebug() << "Not running GC."; - return; - } - -// QTime t; t.start(); - -// qDebug() << ">>>>>>>>runGC"; - - mark(); -// std::cerr << "GC: marked " << marks -// << " objects in " << t.elapsed() -// << "ms" << std::endl; - -// t.restart(); - /*std::size_t freedCount =*/ sweep(); -// std::cerr << "GC: sweep freed " << freedCount -// << " objects in " << t.elapsed() -// << "ms" << std::endl; -} - -void MemoryManager::setEnableGC(bool enableGC) -{ - m_d->enableGC = enableGC; -} - -MemoryManager::~MemoryManager() -{ - sweep(); -} - -void MemoryManager::protect(Managed *m) -{ - ++m_d->protectedObject[m]; -} - -void MemoryManager::unprotect(Managed *m) -{ - if (!--m_d->protectedObject[m]) - m_d->protectedObject.remove(m); -} - -static inline void add(QVector &values, const Value &v) -{ - if (Object *o = v.asObject()) - values.append(o); -} - -void MemoryManager::setExecutionEngine(ExecutionEngine *engine) -{ - m_d->engine = engine; -} - -void MemoryManager::dumpStats() const -{ -#ifdef DETAILED_MM_STATS - std::cerr << "=================" << std::endl; - std::cerr << "Allocation stats:" << std::endl; - std::cerr << "Requests for each chunk size:" << std::endl; - for (int i = 0; i < m_d->allocSizeCounters.size(); ++i) { - if (unsigned count = m_d->allocSizeCounters[i]) { - std::cerr << "\t" << (i << 4) << " bytes chunks: " << count << std::endl; - } - } -#endif // DETAILED_MM_STATS -} - -ExecutionEngine *MemoryManager::engine() const -{ - return m_d->engine; -} - -#ifdef DETAILED_MM_STATS -void MemoryManager::willAllocate(std::size_t size) -{ - unsigned alignedSize = (size + 15) >> 4; - QVector &counters = m_d->allocSizeCounters; - if ((unsigned) counters.size() < alignedSize + 1) - counters.resize(alignedSize + 1); - counters[alignedSize]++; -} - -#endif // DETAILED_MM_STATS - -void MemoryManager::collectFromStack() const -{ - if (!m_d->heapChunks.count()) - return; - - quintptr valueOnStack = 0; - -#if USE(PTHREADS) -#if OS(DARWIN) - void* stackTop = 0; - stackTop = pthread_get_stackaddr_np(pthread_self()); - quintptr *top = static_cast(stackTop); -#else - void* stackBottom = 0; - pthread_attr_t attr; - pthread_getattr_np(pthread_self(), &attr); - size_t stackSize = 0; - pthread_attr_getstack(&attr, &stackBottom, &stackSize); - pthread_attr_destroy(&attr); - - quintptr *top = static_cast(stackBottom) + stackSize/sizeof(quintptr); -#endif -#endif -// qDebug() << "stack:" << hex << stackTop << stackSize << (stackTop + stackSize); - - quintptr *current = (&valueOnStack) + 1; -// qDebug() << "collectFromStack" << top << current << &valueOnStack; - - char** heapChunkBoundaries = (char**)alloca(m_d->heapChunks.count() * 2 * sizeof(char*)); - char** heapChunkBoundariesEnd = heapChunkBoundaries + 2 * m_d->heapChunks.count(); - int i = 0; - for (QVector::Iterator it = m_d->heapChunks.begin(), end = - m_d->heapChunks.end(); it != end; ++it) { - heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()); - heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()) + it->memory.size(); - } - - for (; current < top; ++current) { - char* genericPtr = -#if CPU(X86_64) - reinterpret_cast((*current) & ~(quint64(Value::Type_Mask) << Value::Tag_Shift)); -#else - reinterpret_cast(*current); -#endif - - if (genericPtr < *heapChunkBoundaries || genericPtr >= *(heapChunkBoundariesEnd - 1)) - continue; - int index = qLowerBound(heapChunkBoundaries, heapChunkBoundariesEnd, genericPtr) - heapChunkBoundaries; - // An odd index means the pointer is _before_ the end of a heap chunk and therefore valid. - if (index & 1) { - int size = m_d->heapChunks.at(index >> 1).chunkSize; - Managed *m = reinterpret_cast(genericPtr); -// qDebug() << " inside" << size << m; - - if (((quintptr)m - (quintptr)heapChunkBoundaries[index-1]) % size) - // wrongly aligned value, skip it - continue; - - if (!m->inUse) - // Skip pointers to already freed objects, they are bogus as well - continue; - - m->mark(); -// qDebug() << " marking"; - } - } -} diff --git a/qv4mm.h b/qv4mm.h deleted file mode 100644 index 48d0c3d12e..0000000000 --- a/qv4mm.h +++ /dev/null @@ -1,127 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#ifndef QV4GC_H -#define QV4GC_H - -#include "qv4object.h" - -#include - -#define DETAILED_MM_STATS - -namespace QQmlJS { -namespace VM { - -struct Managed; - -class MemoryManager -{ - MemoryManager(const MemoryManager &); - MemoryManager &operator=(const MemoryManager&); - -public: - struct Data; - - class GCBlocker - { - public: - GCBlocker(MemoryManager *mm) - : mm(mm) - , wasBlocked(mm->isGCBlocked()) - { - mm->setGCBlocked(true); - } - - ~GCBlocker() - { - mm->setGCBlocked(wasBlocked); - } - - private: - MemoryManager *mm; - bool wasBlocked; - }; - -public: - MemoryManager(); - ~MemoryManager(); - - void protect(Managed *m); - void unprotect(Managed *m); - - // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries). - // Note: all occurances of "16" in alloc/dealloc are also due to the alignment. - static inline std::size_t align(std::size_t size) - { return (size + 15) & ~0xf; } - - inline Managed *allocManaged(std::size_t size) - { - size = align(size); - Managed *o = alloc(size); - return o; - } - - bool isGCBlocked() const; - void setGCBlocked(bool blockGC); - void runGC(); - - void setEnableGC(bool enableGC); - void setExecutionEngine(ExecutionEngine *engine); - - void dumpStats() const; - -protected: - /// expects size to be aligned - // TODO: try to inline - Managed *alloc(std::size_t size); - - void scribble(Managed *obj, int c, int size) const; - - ExecutionEngine *engine() const; - -#ifdef DETAILED_MM_STATS - void willAllocate(std::size_t size); -#endif // DETAILED_MM_STATS - -private: - void collectFromStack() const; - void mark(); - std::size_t sweep(); - std::size_t sweep(char *chunkStart, std::size_t chunkSize, size_t size); - -protected: - QScopedPointer m_d; -}; - - -} // namespace VM -} // namespace QQmlJS - -#endif // QV4GC_H diff --git a/qv4numberobject.cpp b/qv4numberobject.cpp deleted file mode 100644 index 08711a8187..0000000000 --- a/qv4numberobject.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4numberobject.h" -#include -#include -#include -#include - - -using namespace QQmlJS::VM; - - -NumberCtor::NumberCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value NumberCtor::construct(ExecutionContext *ctx) -{ - double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; - return Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(d))); -} - -Value NumberCtor::call(ExecutionContext *ctx) -{ - double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; - return Value::fromDouble(d); -} - -void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf())); - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308)); - -#ifdef __INTEL_COMPILER -# pragma warning( push ) -# pragma warning(disable: 239) -#endif - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324)); -#ifdef __INTEL_COMPILER -# pragma warning( pop ) -#endif - - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); - defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); - defineDefaultProperty(ctx, QStringLiteral("toFixed"), method_toFixed, 1); - defineDefaultProperty(ctx, QStringLiteral("toExponential"), method_toExponential); - defineDefaultProperty(ctx, QStringLiteral("toPrecision"), method_toPrecision); -} - -Value NumberPrototype::method_toString(ExecutionContext *ctx) -{ - double num; - if (ctx->thisObject.isNumber()) { - num = ctx->thisObject.asDouble(); - } else { - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - num = thisObject->value.asDouble(); - } - - Value arg = ctx->argument(0); - if (!arg.isUndefined()) { - int radix = arg.toInt32(ctx); - if (radix < 2 || radix > 36) { - ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") - .arg(radix)); - return Value::undefinedValue(); - } - - if (std::isnan(num)) { - return Value::fromString(ctx, QStringLiteral("NaN")); - } else if (qIsInf(num)) { - return Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); - } - - if (radix != 10) { - QString str; - bool negative = false; - if (num < 0) { - negative = true; - num = -num; - } - double frac = num - ::floor(num); - num = Value::toInteger(num); - do { - char c = (char)::fmod(num, radix); - c = (c < 10) ? (c + '0') : (c - 10 + 'a'); - str.prepend(QLatin1Char(c)); - num = ::floor(num / radix); - } while (num != 0); - if (frac != 0) { - str.append(QLatin1Char('.')); - do { - frac = frac * radix; - char c = (char)::floor(frac); - c = (c < 10) ? (c + '0') : (c - 10 + 'a'); - str.append(QLatin1Char(c)); - frac = frac - ::floor(frac); - } while (frac != 0); - } - if (negative) - str.prepend(QLatin1Char('-')); - return Value::fromString(ctx, str); - } - } - - String *str = Value::fromDouble(num).toString(ctx); - return Value::fromString(str); -} - -Value NumberPrototype::method_toLocaleString(ExecutionContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - String *str = thisObject->value.toString(ctx); - return Value::fromString(str); -} - -Value NumberPrototype::method_valueOf(ExecutionContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - return thisObject->value; -} - -Value NumberPrototype::method_toFixed(ExecutionContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - double fdigits = 0; - - if (ctx->argumentCount > 0) - fdigits = ctx->argument(0).toInteger(ctx); - - if (std::isnan(fdigits)) - fdigits = 0; - - if (fdigits < 0 || fdigits > 20) - ctx->throwRangeError(ctx->thisObject); - - double v = thisObject->value.asDouble(); - QString str; - if (std::isnan(v)) - str = QString::fromLatin1("NaN"); - else if (qIsInf(v)) - str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); - else - str = QString::number(v, 'f', int (fdigits)); - return Value::fromString(ctx, str); -} - -Value NumberPrototype::method_toExponential(ExecutionContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - double fdigits = 0; - - if (ctx->argumentCount > 0) - fdigits = ctx->argument(0).toInteger(ctx); - - QString z = QString::number(thisObject->value.asDouble(), 'e', int (fdigits)); - return Value::fromString(ctx, z); -} - -Value NumberPrototype::method_toPrecision(ExecutionContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - double fdigits = 0; - - if (ctx->argumentCount > 0) - fdigits = ctx->argument(0).toInteger(ctx); - - return Value::fromString(ctx, QString::number(thisObject->value.asDouble(), 'g', int (fdigits))); -} diff --git a/qv4numberobject.h b/qv4numberobject.h deleted file mode 100644 index 85f9e1ff2b..0000000000 --- a/qv4numberobject.h +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4NUMBEROBJECT_H -#define QV4NUMBEROBJECT_H - -#include "qv4object.h" -#include "qv4functionobject.h" -#include - -namespace QQmlJS { -namespace VM { - -struct NumberCtor: FunctionObject -{ - NumberCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct NumberPrototype: NumberObject -{ - NumberPrototype(): NumberObject(Value::fromDouble(0)) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_toString(ExecutionContext *ctx); - static Value method_toLocaleString(ExecutionContext *ctx); - static Value method_valueOf(ExecutionContext *ctx); - static Value method_toFixed(ExecutionContext *ctx); - static Value method_toExponential(ExecutionContext *ctx); - static Value method_toPrecision(ExecutionContext *ctx); -}; - - -} // end of namespace VM -} // end of namespace QQmlJS - -#endif // QV4ECMAOBJECTS_P_H diff --git a/qv4object.cpp b/qv4object.cpp deleted file mode 100644 index dadf8479c2..0000000000 --- a/qv4object.cpp +++ /dev/null @@ -1,674 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4object.h" -#include "qv4ir_p.h" -#include "qv4isel_p.h" -#include "qv4objectproto.h" -#include "qv4stringobject.h" -#include "qv4argumentsobject.h" -#include "qv4mm.h" - -#include -#include -#include -#include -#include -#include -#include "private/qlocale_tools_p.h" - -#include -#include -#include -#include -#include -#include - -using namespace QQmlJS::VM; - - -// -// Object -// -Object::~Object() -{ -} - -void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &value) -{ - __put__(ctx, ctx->engine->identifier(name), value); -} - -Value Object::getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const -{ - if (p->isData()) - return p->value; - if (!p->get) - return Value::undefinedValue(); - - return p->get->call(ctx, Value::fromObject(const_cast(this)), 0, 0); -} - -Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const -{ - if (!p || p->type == PropertyDescriptor::Generic) - return Value::undefinedValue(); - return getValue(ctx, p); -} - -Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const -{ - *exists = p && p->type != PropertyDescriptor::Generic; - if (!*exists) - return Value::undefinedValue(); - return getValue(ctx, p); -} - -void Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx) -{ - bool hasProperty = false; - Value v = __get__(ctx, name, &hasProperty); - Value result = op(v, rhs, ctx); - __put__(ctx, name, result); -} - -void Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) -{ - uint idx = index.asArrayIndex(); - if (idx < UINT_MAX) { - bool hasProperty = false; - Value v = __get__(ctx, idx, &hasProperty); - v = op(v, rhs, ctx); - __put__(ctx, idx, v); - return; - } - String *name = index.toString(ctx); - assert(name); - inplaceBinOp(rhs, name, op, ctx); -} - -void Object::defineDefaultProperty(String *name, Value value) -{ - if (!members) - members.reset(new PropertyTable()); - PropertyDescriptor *pd = members->insert(name); - pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Enabled; - pd->enumberable = PropertyDescriptor::Disabled; - pd->configurable = PropertyDescriptor::Enabled; - pd->value = value; -} - -void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value value) -{ - defineDefaultProperty(context->engine->identifier(name), value); -} - -void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int argumentCount) -{ - Q_UNUSED(argumentCount); - String *s = context->engine->identifier(name); - FunctionObject* function = context->engine->newBuiltinFunction(context, s, code); - function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); - defineDefaultProperty(s, Value::fromObject(function)); -} - -void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *, Value, Value *, int), int argumentCount) -{ - Q_UNUSED(argumentCount); - String *s = context->engine->identifier(name); - FunctionObject* function = context->engine->newBuiltinFunction(context, s, code); - function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); - defineDefaultProperty(s, Value::fromObject(function)); -} - -void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value) -{ - defineReadonlyProperty(engine->identifier(name), value); -} - -void Object::defineReadonlyProperty(String *name, Value value) -{ - if (!members) - members.reset(new PropertyTable()); - PropertyDescriptor *pd = members->insert(name); - pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Disabled; - pd->enumberable = PropertyDescriptor::Disabled; - pd->configurable = PropertyDescriptor::Disabled; - pd->value = value; -} - -void Object::markObjects() -{ - if (prototype) - prototype->mark(); - - if (members) { - for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) { - if (!(*it)) - continue; - (*it)->name->mark(); - PropertyDescriptor &pd = (*it)->descriptor; - if (pd.isData()) { - if (Managed *m = pd.value.asManaged()) - m->mark(); - } else if (pd.isAccessor()) { - if (pd.get) - pd.get->mark(); - if (pd.set) - pd.set->mark(); - } - } - } - array.markObjects(); -} - -// Section 8.12.1 -PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *name) -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __getOwnProperty__(ctx, idx); - - if (members) - return members->find(name); - return 0; -} - -PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, uint index) -{ - PropertyDescriptor *p = array.at(index); - if(p && p->type != PropertyDescriptor::Generic) - return p; - if (isStringObject()) - return static_cast(this)->getIndex(ctx, index); - - return 0; -} - -// Section 8.12.2 -PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name) -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __getPropertyDescriptor__(ctx, idx); - - - Object *o = this; - while (o) { - if (o->members) { - if (PropertyDescriptor *p = o->members->find(name)) - return p; - } - o = o->prototype; - } - return 0; -} - -PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, uint index) -{ - Object *o = this; - while (o) { - PropertyDescriptor *p = o->array.at(index); - if(p && p->type != PropertyDescriptor::Generic) - return p; - if (o->isStringObject()) { - p = static_cast(o)->getIndex(ctx, index); - if (p) - return p; - } - o = o->prototype; - } - return 0; -} - -// Section 8.12.3 -Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __get__(ctx, idx, hasProperty); - - if (name->isEqualTo(ctx->engine->id___proto__)) { - if (hasProperty) - *hasProperty = true; - return Value::fromObject(prototype); - } - - if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name)) { - if (hasProperty) - *hasProperty = true; - return getValue(ctx, p); - } - - if (hasProperty) - *hasProperty = false; - return Value::undefinedValue(); -} - -Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) -{ - const PropertyDescriptor *p = __getPropertyDescriptor__(ctx, index); - if (p && p->type != PropertyDescriptor::Generic) { - if (hasProperty) - *hasProperty = true; - return getValue(ctx, p); - } - - if (hasProperty) - *hasProperty = false; - return Value::undefinedValue(); -} - - -// Section 8.12.5 -void Object::__put__(ExecutionContext *ctx, String *name, Value value) -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __put__(ctx, idx, value); - - PropertyDescriptor *pd = __getOwnProperty__(ctx, name); - // clause 1 - if (pd) { - if (pd->isAccessor()) { - if (pd->set) - goto cont; - goto reject; - } else if (!pd->isWritable()) - goto reject; - else if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { - bool ok; - uint l = value.asArrayLength(ctx, &ok); - if (!ok) - ctx->throwRangeError(value); - ok = array.setLength(l); - if (!ok) - goto reject; - } else { - pd->value = value; - } - return; - } else if (!prototype) { - if (!extensible) - goto reject; - } else { - if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, name)) { - if (p->isAccessor()) { - if (p->set) - goto cont; - goto reject; - } - if (!extensible) - goto reject; - if (!p->isWritable()) - goto reject; - } else { - if (!extensible) - goto reject; - } - } - - cont: - - if (!members) - members.reset(new PropertyTable()); - - - // clause 4 - if (!pd && prototype) - pd = prototype->__getPropertyDescriptor__(ctx, name); - - // Clause 5 - if (pd && pd->isAccessor()) { - assert(pd->set != 0); - - Value args[1]; - args[0] = value; - pd->set->call(ctx, Value::fromObject(this), args, 1); - return; - } - - { - PropertyDescriptor *p = members->insert(name); - p->type = PropertyDescriptor::Data; - p->value = value; - p->configurable = PropertyDescriptor::Enabled; - p->enumberable = PropertyDescriptor::Enabled; - p->writable = PropertyDescriptor::Enabled; - return; - } - - reject: - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); -} - -void Object::__put__(ExecutionContext *ctx, uint index, Value value) -{ - PropertyDescriptor *pd = __getOwnProperty__(ctx, index); - // clause 1 - if (pd) { - if (pd->isAccessor()) { - if (pd->set) - goto cont; - goto reject; - } else if (!pd->isWritable()) - goto reject; - else - pd->value = value; - return; - } else if (!prototype) { - if (!extensible) - goto reject; - } else { - if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, index)) { - if (p->isAccessor()) { - if (p->set) - goto cont; - goto reject; - } - if (!extensible) - goto reject; - if (!p->isWritable()) - goto reject; - } else { - if (!extensible) - goto reject; - } - } - - cont: - - // clause 4 - if (!pd && prototype) - pd = prototype->__getPropertyDescriptor__(ctx, index); - - // Clause 5 - if (pd && pd->isAccessor()) { - assert(pd->set != 0); - - Value args[1]; - args[0] = value; - pd->set->call(ctx, Value::fromObject(this), args, 1); - return; - } - - array.set(index, value); - return; - - reject: - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); -} - -// Section 8.12.6 -bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __hasProperty__(ctx, idx); - - if (members && members->find(name) != 0) - return true; - - return prototype ? prototype->__hasProperty__(ctx, name) : false; -} - -bool Object::__hasProperty__(const ExecutionContext *ctx, uint index) const -{ - const PropertyDescriptor *p = array.at(index); - if (p && p->type != PropertyDescriptor::Generic) - return true; - - return prototype ? prototype->__hasProperty__(ctx, index) : false; -} - -// Section 8.12.7 -bool Object::__delete__(ExecutionContext *ctx, String *name) -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __delete__(ctx, idx); - - if (members) { - if (PropertyTableEntry *entry = members->findEntry(name)) { - if (entry->descriptor.isConfigurable()) { - members->remove(entry); - return true; - } - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); - return false; - } - } - return true; -} - -bool Object::__delete__(ExecutionContext *ctx, uint index) -{ - if (array.deleteIndex(index)) - return true; - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); - return false; -} - -// Section 8.12.9 -bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc) -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __defineOwnProperty__(ctx, idx, desc); - - PropertyDescriptor *current; - - if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { - PropertyDescriptor *lp = array.getLengthProperty(); - if (desc->isEmpty() || desc->isSubset(lp)) - return true; - if (!lp->isWritable() || desc->type == PropertyDescriptor::Accessor || desc->isConfigurable() || desc->isEnumerable()) - goto reject; - bool succeeded = true; - if (desc->type == PropertyDescriptor::Data) { - bool ok; - uint l = desc->value.asArrayLength(ctx, &ok); - if (!ok) - ctx->throwRangeError(desc->value); - succeeded = array.setLength(l); - } - if (desc->writable == PropertyDescriptor::Disabled) - lp->writable = PropertyDescriptor::Disabled; - if (!succeeded) - goto reject; - return true; - } - - if (!members) - members.reset(new PropertyTable()); - - // Clause 1 - current = __getOwnProperty__(ctx, name); - if (!current) { - // clause 3 - if (!extensible) - goto reject; - // clause 4 - PropertyDescriptor *pd = members->insert(name); - *pd = *desc; - pd->fullyPopulated(); - return true; - } - - return __defineOwnProperty__(ctx, current, desc); -reject: - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); - return false; -} - -bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc) -{ - PropertyDescriptor *current; - - // 15.4.5.1, 4b - if (isArrayObject() && index >= array.length() && !array.getLengthProperty()->isWritable()) - goto reject; - - if (isNonStrictArgumentsObject) - return static_cast(this)->defineOwnProperty(ctx, index, desc); - - // Clause 1 - current = __getOwnProperty__(ctx, index); - if (!current) { - // clause 3 - if (!extensible) - goto reject; - // clause 4 - PropertyDescriptor *pd = array.insert(index); - *pd = *desc; - pd->fullyPopulated(); - return true; - } - - return __defineOwnProperty__(ctx, current, desc); -reject: - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); - return false; -} - -bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc) -{ - // clause 5 - if (desc->isEmpty()) - return true; - - // clause 6 - if (desc->isSubset(current)) - return true; - - // clause 7 - if (!current->isConfigurable()) { - if (desc->isConfigurable()) - goto reject; - if (desc->enumberable != PropertyDescriptor::Undefined && desc->enumberable != current->enumberable) - goto reject; - } - - // clause 8 - if (desc->isGeneric()) - goto accept; - - // clause 9 - if (current->isData() != desc->isData()) { - // 9a - if (!current->isConfigurable()) - goto reject; - if (current->isData()) { - // 9b - current->type = PropertyDescriptor::Accessor; - current->writable = PropertyDescriptor::Undefined; - current->get = 0; - current->set = 0; - } else { - // 9c - current->type = PropertyDescriptor::Data; - current->writable = PropertyDescriptor::Disabled; - current->value = Value::undefinedValue(); - } - } else if (current->isData() && desc->isData()) { // clause 10 - if (!current->isConfigurable() && !current->isWritable()) { - if (desc->isWritable() || !current->value.sameValue(desc->value)) - goto reject; - } - } else { // clause 10 - assert(current->isAccessor() && desc->isAccessor()); - if (!current->isConfigurable()) { - if (desc->get && !(current->get == desc->get || (!current->get && (quintptr)desc->get == 0x1))) - goto reject; - if (desc->set && !(current->set == desc->set || (!current->set && (quintptr)desc->set == 0x1))) - goto reject; - } - } - - accept: - - *current += *desc; - return true; - reject: - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); - return false; -} - - -bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc) -{ - return __defineOwnProperty__(ctx, ctx->engine->identifier(name), desc); -} - - -void ArrayObject::init(ExecutionContext *context) -{ - type = Type_ArrayObject; - - if (!members) - members.reset(new PropertyTable()); - PropertyDescriptor *pd = members->insert(context->engine->id_length); - pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Enabled; - pd->enumberable = PropertyDescriptor::Disabled; - pd->configurable = PropertyDescriptor::Disabled; - pd->value = Value::fromInt32(0); - array.setLengthProperty(pd); -} - - - -void ForEachIteratorObject::markObjects() -{ - Object::markObjects(); - if (it.object) - it.object->mark(); -} - diff --git a/qv4object.h b/qv4object.h deleted file mode 100644 index c50ffe2755..0000000000 --- a/qv4object.h +++ /dev/null @@ -1,191 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QMLJS_OBJECTS_H -#define QMLJS_OBJECTS_H - -#include "qmljs_runtime.h" -#include "qmljs_engine.h" -#include "qmljs_environment.h" -#include "qv4array.h" -#include "qv4string.h" -#include "qv4codegen_p.h" -#include "qv4isel_p.h" -#include "qv4managed.h" -#include "qv4propertydescriptor.h" -#include "qv4propertytable.h" -#include "qv4objectiterator.h" -#include "qv4regexp.h" - -#include -#include -#include -#include -#include - -namespace QQmlJS { - -namespace VM { - -struct Value; -struct Function; -struct Object; -struct ObjectIterator; -struct BooleanObject; -struct NumberObject; -struct StringObject; -struct ArrayObject; -struct DateObject; -struct FunctionObject; -struct RegExpObject; -struct ErrorObject; -struct ArgumentsObject; -struct ExecutionContext; -struct ExecutionEngine; -class MemoryManager; - -struct ObjectPrototype; -struct StringPrototype; -struct NumberPrototype; -struct BooleanPrototype; -struct ArrayPrototype; -struct FunctionPrototype; -struct DatePrototype; -struct RegExpPrototype; -struct ErrorPrototype; -struct EvalErrorPrototype; -struct RangeErrorPrototype; -struct ReferenceErrorPrototype; -struct SyntaxErrorPrototype; -struct TypeErrorPrototype; -struct URIErrorPrototype; - - -struct Object: Managed { - Object *prototype; - QScopedPointer members; - Array array; - - Object() - : prototype(0) { type = Type_Object; } - Object(const Array &a) - : prototype(0), array(a) {} - - virtual ~Object(); - - PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); - PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); - PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name); - PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, uint index); - - Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); - Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); - - void __put__(ExecutionContext *ctx, String *name, Value value); - void __put__(ExecutionContext *ctx, uint index, Value value); - - bool __hasProperty__(const ExecutionContext *ctx, String *name) const; - bool __hasProperty__(const ExecutionContext *ctx, uint index) const; - bool __delete__(ExecutionContext *ctx, String *name); - bool __delete__(ExecutionContext *ctx, uint index); - bool __defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc); - bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc); - bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); - bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc); - - // - // helpers - // - void __put__(ExecutionContext *ctx, const QString &name, const Value &value); - - Value getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const; - Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const; - Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const; - - void inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); - void inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); - - /* The spec default: Writable: true, Enumerable: false, Configurable: true */ - void defineDefaultProperty(String *name, Value value); - void defineDefaultProperty(ExecutionContext *context, const QString &name, Value value); - void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int count = 0); - void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *, Value, Value *, int), int argumentCount = 0); - /* Fixed: Writable: false, Enumerable: false, Configurable: false */ - void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value); - void defineReadonlyProperty(String *name, Value value); - -protected: - virtual void markObjects(); - - friend struct ObjectIterator; -}; - -struct ForEachIteratorObject: Object { - ObjectIterator it; - ForEachIteratorObject(ExecutionContext *ctx, Object *o) - : it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) { type = Type_ForeachIteratorObject; } - - Value nextPropertyName() { return it.nextPropertyNameAsString(); } - -protected: - virtual void markObjects(); -}; - -struct BooleanObject: Object { - Value value; - BooleanObject(const Value &value): value(value) { type = Type_BooleanObject; } -}; - -struct NumberObject: Object { - Value value; - NumberObject(const Value &value): value(value) { type = Type_NumberObject; } -}; - -struct ArrayObject: Object { - ArrayObject(ExecutionContext *ctx) { init(ctx); } - ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setLengthUnchecked(array.length()); } - void init(ExecutionContext *context); -}; - - -} // namespace VM -} // namespace QQmlJS - -#endif // QMLJS_OBJECTS_H diff --git a/qv4objectiterator.cpp b/qv4objectiterator.cpp deleted file mode 100644 index 8da2e7bdba..0000000000 --- a/qv4objectiterator.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "qv4objectiterator.h" -#include "qv4object.h" -#include "qv4stringobject.h" - -namespace QQmlJS { -namespace VM { - -ObjectIterator::ObjectIterator(ExecutionContext *context, Object *o, uint flags) - : context(context) - , object(o) - , current(o) - , arrayNode(0) - , arrayIndex(0) - , tableIndex(0) - , flags(flags) -{ - if (current && current->asStringObject()) - this->flags |= CurrentIsString; -} - -PropertyDescriptor *ObjectIterator::next(String **name, uint *index) -{ - PropertyDescriptor *p = 0; - *name = 0; - *index = UINT_MAX; - while (1) { - if (!current) - break; - - if (flags & CurrentIsString) { - StringObject *s = static_cast(current); - uint slen = s->value.stringValue()->toQString().length(); - while (arrayIndex < slen) { - *index = arrayIndex; - ++arrayIndex; - return s->__getOwnProperty__(context, *index); - } - flags &= ~CurrentIsString; - arrayNode = current->array.sparseBegin(); - // iterate until we're past the end of the string - while (arrayNode && arrayNode->key() < slen) - arrayNode = arrayNode->nextNode(); - } - - if (!arrayIndex) - arrayNode = current->array.sparseBegin(); - - // sparse arrays - if (arrayNode) { - while (arrayNode != current->array.sparseEnd()) { - int k = arrayNode->key(); - p = current->array.at(k); - arrayNode = arrayNode->nextNode(); - if (p && (!(flags & EnumberableOnly) || p->isEnumerable())) { - arrayIndex = k + 1; - *index = k; - return p; - } - } - arrayNode = 0; - arrayIndex = UINT_MAX; - } - // dense arrays - while (arrayIndex < current->array.length()) { - p = current->array.at(arrayIndex); - ++arrayIndex; - if (p && p->type != PropertyDescriptor::Generic && (!(flags & EnumberableOnly) || p->isEnumerable())) { - *index = arrayIndex - 1; - return p; - } - } - - if (!current->members || tableIndex >= (uint)current->members->_propertyCount) { - if (flags & WithProtoChain) - current = current->prototype; - else - current = 0; - if (current && current->asStringObject()) - flags |= CurrentIsString; - else - flags &= ~CurrentIsString; - - - arrayIndex = 0; - tableIndex = 0; - continue; - } - PropertyTableEntry *pt = current->members->_properties[tableIndex]; - ++tableIndex; - // ### check that it's not a repeated attribute - if (pt && (!(flags & EnumberableOnly) || pt->descriptor.isEnumerable())) { - *name = pt->name; - p = &pt->descriptor; - return p; - } - } - return 0; -} - -Value ObjectIterator::nextPropertyName() -{ - uint index; - String *name; - next(&name, &index); - if (name) - return Value::fromString(name); - if (index < UINT_MAX) - return Value::fromDouble(index); - return Value::nullValue(); -} - -Value ObjectIterator::nextPropertyNameAsString() -{ - uint index; - String *name; - next(&name, &index); - if (name) - return Value::fromString(name); - if (index < UINT_MAX) - return __qmljs_to_string(Value::fromDouble(index), context); - return Value::nullValue(); -} - -} -} - diff --git a/qv4objectiterator.h b/qv4objectiterator.h deleted file mode 100644 index baecc25424..0000000000 --- a/qv4objectiterator.h +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4OBJECTITERATOR_H -#define QV4OBJECTITERATOR_H - -#include "qmljs_value.h" - -namespace QQmlJS { -namespace VM { - -struct SparseArrayNode; -struct Object; -struct PropertyDescriptor; - -struct ObjectIterator -{ - enum Flags { - NoFlags = 0, - EnumberableOnly = 0x1, - WithProtoChain = 0x2, - CurrentIsString = 0x4 - }; - - ExecutionContext *context; - Object *object; - Object *current; - SparseArrayNode *arrayNode; - uint arrayIndex; - uint tableIndex; - uint flags; - - ObjectIterator(ExecutionContext *context, Object *o, uint flags); - PropertyDescriptor *next(String **name, uint *index); - Value nextPropertyName(); - Value nextPropertyNameAsString(); -}; - -} -} - -#endif diff --git a/qv4objectproto.cpp b/qv4objectproto.cpp deleted file mode 100644 index 98f205d1d8..0000000000 --- a/qv4objectproto.cpp +++ /dev/null @@ -1,558 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qv4objectproto.h" -#include "qv4mm.h" -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifndef Q_WS_WIN -# include -# ifndef Q_OS_VXWORKS -# include -# else -# include "qplatformdefs.h" -# endif -#else -# include -#endif - -using namespace QQmlJS::VM; - - -// -// Object -// -ObjectCtor::ObjectCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value ObjectCtor::construct(ExecutionContext *ctx) -{ - if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull()) - return ctx->thisObject; - return __qmljs_to_object(ctx->argument(0), ctx); -} - -Value ObjectCtor::call(ExecutionContext *ctx) -{ - if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull()) - return Value::fromObject(ctx->engine->newObject()); - return __qmljs_to_object(ctx->argument(0), ctx); -} - -void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("create"), method_create, 2); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 3); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 2); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("seal"), method_seal, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("freeze"), method_freeze, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("keys"), method_keys, 1); - - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); - defineDefaultProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1); - defineDefaultProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1); - defineDefaultProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1); - defineDefaultProperty(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 0); - defineDefaultProperty(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 0); -} - -Value ObjectPrototype::method_getPrototypeOf(ExecutionContext *ctx) -{ - Value o = ctx->argument(0); - if (! o.isObject()) - ctx->throwTypeError(); - - Object *p = o.objectValue()->prototype; - return p ? Value::fromObject(p) : Value::nullValue(); -} - -Value ObjectPrototype::method_getOwnPropertyDescriptor(ExecutionContext *ctx) -{ - Value O = ctx->argument(0); - if (!O.isObject()) - ctx->throwTypeError(); - - String *name = ctx->argument(1).toString(ctx); - PropertyDescriptor *desc = O.objectValue()->__getOwnProperty__(ctx, name); - return fromPropertyDescriptor(ctx, desc); -} - -Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) -{ - Object *O = ctx->argument(0).asObject(); - if (!O) - ctx->throwTypeError(); - - ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); - Array &a = array->array; - ObjectIterator it(ctx, O, ObjectIterator::NoFlags); - while (1) { - Value v = it.nextPropertyNameAsString(); - if (v.isNull()) - break; - a.push_back(v); - } - return Value::fromObject(array); -} - -Value ObjectPrototype::method_create(ExecutionContext *ctx) -{ - Value O = ctx->argument(0); - if (!O.isObject() && !O.isNull()) - ctx->throwTypeError(); - - Object *newObject = ctx->engine->newObject(); - newObject->prototype = O.objectValue(); - - Value objValue = Value::fromObject(newObject); - if (ctx->argumentCount > 1 && !ctx->argument(1).isUndefined()) { - ctx->arguments[0] = objValue; - method_defineProperties(ctx); - } - - return objValue; -} - -Value ObjectPrototype::method_defineProperty(ExecutionContext *ctx) -{ - Value O = ctx->argument(0); - if (!O.isObject()) - ctx->throwTypeError(); - - String *name = ctx->argument(1).toString(ctx); - - Value attributes = ctx->argument(2); - PropertyDescriptor pd; - toPropertyDescriptor(ctx, attributes, &pd); - - if (!O.objectValue()->__defineOwnProperty__(ctx, name, &pd)) - __qmljs_throw_type_error(ctx); - - return O; -} - -Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) -{ - Value O = ctx->argument(0); - if (!O.isObject()) - ctx->throwTypeError(); - - Object *o = ctx->argument(1).toObject(ctx).objectValue(); - - ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - PropertyDescriptor n; - toPropertyDescriptor(ctx, o->getValue(ctx, pd), &n); - bool ok; - if (name) - ok = O.objectValue()->__defineOwnProperty__(ctx, name, &n); - else - ok = O.objectValue()->__defineOwnProperty__(ctx, index, &n); - if (!ok) - __qmljs_throw_type_error(ctx); - } - - return O; -} - -Value ObjectPrototype::method_seal(ExecutionContext *ctx) -{ - if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->argument(0).objectValue(); - o->extensible = false; - - ObjectIterator it(ctx, o, ObjectIterator::NoFlags); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - pd->configurable = PropertyDescriptor::Disabled; - } - return ctx->argument(0); -} - -Value ObjectPrototype::method_freeze(ExecutionContext *ctx) -{ - if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->argument(0).objectValue(); - o->extensible = false; - - ObjectIterator it(ctx, o, ObjectIterator::NoFlags); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - if (pd->type == PropertyDescriptor::Data) - pd->writable = PropertyDescriptor::Disabled; - pd->configurable = PropertyDescriptor::Disabled; - } - return ctx->argument(0); -} - -Value ObjectPrototype::method_preventExtensions(ExecutionContext *ctx) -{ - if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->argument(0).objectValue(); - o->extensible = false; - return ctx->argument(0); -} - -Value ObjectPrototype::method_isSealed(ExecutionContext *ctx) -{ - if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->argument(0).objectValue(); - if (o->extensible) - return Value::fromBoolean(false); - - ObjectIterator it(ctx, o, ObjectIterator::NoFlags); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - if (pd->configurable != PropertyDescriptor::Disabled) - return Value::fromBoolean(false); - } - return Value::fromBoolean(true); -} - -Value ObjectPrototype::method_isFrozen(ExecutionContext *ctx) -{ - if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->argument(0).objectValue(); - if (o->extensible) - return Value::fromBoolean(false); - - ObjectIterator it(ctx, o, ObjectIterator::NoFlags); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - if (pd->isWritable() || pd->isConfigurable()) - return Value::fromBoolean(false); - } - return Value::fromBoolean(true); -} - -Value ObjectPrototype::method_isExtensible(ExecutionContext *ctx) -{ - if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->argument(0).objectValue(); - return Value::fromBoolean(o->extensible); -} - -Value ObjectPrototype::method_keys(ExecutionContext *ctx) -{ - if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->argument(0).objectValue(); - - ArrayObject *a = ctx->engine->newArrayObject(ctx); - - ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - Value key; - if (name) { - key = Value::fromString(name); - } else { - key = Value::fromDouble(index); - key = __qmljs_to_string(key, ctx); - } - a->array.push_back(key); - } - - return Value::fromObject(a); -} - -Value ObjectPrototype::method_toString(ExecutionContext *ctx) -{ - if (ctx->thisObject.isUndefined()) { - return Value::fromString(ctx, QStringLiteral("[object Undefined]")); - } else if (ctx->thisObject.isNull()) { - return Value::fromString(ctx, QStringLiteral("[object Null]")); - } else { - Value obj = __qmljs_to_object(ctx->thisObject, ctx); - QString className = obj.objectValue()->className(); - return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(className)); - } -} - -Value ObjectPrototype::method_toLocaleString(ExecutionContext *ctx) -{ - Object *o = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - Value ts = o->__get__(ctx, ctx->engine->identifier(QStringLiteral("toString"))); - FunctionObject *f = ts.asFunctionObject(); - if (!f) - __qmljs_throw_type_error(ctx); - return f->call(ctx, Value::fromObject(o), 0, 0); -} - -Value ObjectPrototype::method_valueOf(ExecutionContext *ctx) -{ - return ctx->thisObject.toObject(ctx); -} - -Value ObjectPrototype::method_hasOwnProperty(ExecutionContext *ctx) -{ - String *P = ctx->argument(0).toString(ctx); - Value O = ctx->thisObject.toObject(ctx); - bool r = O.objectValue()->__getOwnProperty__(ctx, P) != 0; - return Value::fromBoolean(r); -} - -Value ObjectPrototype::method_isPrototypeOf(ExecutionContext *ctx) -{ - Value V = ctx->argument(0); - if (! V.isObject()) - return Value::fromBoolean(false); - - Object *O = ctx->thisObject.toObject(ctx).objectValue(); - Object *proto = V.objectValue()->prototype; - while (proto) { - if (O == proto) - return Value::fromBoolean(true); - proto = proto->prototype; - } - return Value::fromBoolean(false); -} - -Value ObjectPrototype::method_propertyIsEnumerable(ExecutionContext *ctx) -{ - String *p = ctx->argument(0).toString(ctx); - - Object *o = ctx->thisObject.toObject(ctx).objectValue(); - PropertyDescriptor *pd = o->__getOwnProperty__(ctx, p); - return Value::fromBoolean(pd && pd->isEnumerable()); -} - -Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx) -{ - if (ctx->argumentCount < 2) - __qmljs_throw_type_error(ctx); - String *prop = ctx->argument(0).toString(ctx); - - FunctionObject *f = ctx->argument(1).asFunctionObject(); - if (!f) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->thisObject.toObject(ctx).objectValue(); - - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(f, 0); - pd.configurable = PropertyDescriptor::Enabled; - pd.enumberable = PropertyDescriptor::Enabled; - o->__defineOwnProperty__(ctx, prop, &pd); - return Value::undefinedValue(); -} - -Value ObjectPrototype::method_defineSetter(ExecutionContext *ctx) -{ - if (ctx->argumentCount < 2) - __qmljs_throw_type_error(ctx); - String *prop = ctx->argument(0).toString(ctx); - - FunctionObject *f = ctx->argument(1).asFunctionObject(); - if (!f) - __qmljs_throw_type_error(ctx); - - Object *o = ctx->thisObject.toObject(ctx).objectValue(); - - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(0, f); - pd.configurable = PropertyDescriptor::Enabled; - pd.enumberable = PropertyDescriptor::Enabled; - o->__defineOwnProperty__(ctx, prop, &pd); - return Value::undefinedValue(); -} - -void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc) -{ - if (!v.isObject()) - __qmljs_throw_type_error(ctx); - - Object *o = v.objectValue(); - - desc->type = PropertyDescriptor::Generic; - - desc->enumberable = PropertyDescriptor::Undefined; - if (o->__hasProperty__(ctx, ctx->engine->id_enumerable)) - desc->enumberable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_enumerable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; - - desc->configurable = PropertyDescriptor::Undefined; - if (o->__hasProperty__(ctx, ctx->engine->id_configurable)) - desc->configurable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_configurable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; - - desc->get = 0; - if (o->__hasProperty__(ctx, ctx->engine->id_get)) { - Value get = o->__get__(ctx, ctx->engine->id_get); - FunctionObject *f = get.asFunctionObject(); - if (f) { - desc->get = f; - } else if (get.isUndefined()) { - desc->get = (FunctionObject *)0x1; - } else { - __qmljs_throw_type_error(ctx); - } - desc->type = PropertyDescriptor::Accessor; - } - - desc->set = 0; - if (o->__hasProperty__(ctx, ctx->engine->id_set)) { - Value set = o->__get__(ctx, ctx->engine->id_set); - FunctionObject *f = set.asFunctionObject(); - if (f) { - desc->set = f; - } else if (set.isUndefined()) { - desc->set = (FunctionObject *)0x1; - } else { - __qmljs_throw_type_error(ctx); - } - desc->type = PropertyDescriptor::Accessor; - } - - desc->writable = PropertyDescriptor::Undefined; - if (o->__hasProperty__(ctx, ctx->engine->id_writable)) { - if (desc->isAccessor()) - __qmljs_throw_type_error(ctx); - desc->writable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_writable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; - // writable forces it to be a data descriptor - desc->value = Value::undefinedValue(); - } - - if (o->__hasProperty__(ctx, ctx->engine->id_value)) { - if (desc->isAccessor()) - __qmljs_throw_type_error(ctx); - desc->value = o->__get__(ctx, ctx->engine->id_value); - desc->type = PropertyDescriptor::Data; - } - -} - - -Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc) -{ - if (!desc) - return Value::undefinedValue(); - - ExecutionEngine *engine = ctx->engine; -// Let obj be the result of creating a new object as if by the expression new Object() where Object is the standard built-in constructor with that name. - Object *o = engine->newObject(); - - PropertyDescriptor pd; - pd.type = PropertyDescriptor::Data; - pd.writable = PropertyDescriptor::Enabled; - pd.enumberable = PropertyDescriptor::Enabled; - pd.configurable = PropertyDescriptor::Enabled; - - if (desc->isData()) { - pd.value = desc->value; - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("value")), &pd); - pd.value = Value::fromBoolean(desc->writable == PropertyDescriptor::Enabled ? true : false); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("writable")), &pd); - } else { - pd.value = desc->get ? Value::fromObject(desc->get) : Value::undefinedValue(); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("get")), &pd); - pd.value = desc->set ? Value::fromObject(desc->set) : Value::undefinedValue(); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("set")), &pd); - } - pd.value = Value::fromBoolean(desc->enumberable == PropertyDescriptor::Enabled ? true : false); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("enumerable")), &pd); - pd.value = Value::fromBoolean(desc->configurable == PropertyDescriptor::Enabled ? true : false); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("configurable")), &pd); - - return Value::fromObject(o); -} diff --git a/qv4objectproto.h b/qv4objectproto.h deleted file mode 100644 index 93527a28ef..0000000000 --- a/qv4objectproto.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4ECMAOBJECTS_P_H -#define QV4ECMAOBJECTS_P_H - -#include "qv4object.h" -#include "qv4functionobject.h" -#include - -namespace QQmlJS { -namespace VM { - -struct ObjectCtor: FunctionObject -{ - ObjectCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct ObjectPrototype: Object -{ - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_getPrototypeOf(ExecutionContext *ctx); - static Value method_getOwnPropertyDescriptor(ExecutionContext *ctx); - static Value method_getOwnPropertyNames(ExecutionContext *ctx); - static Value method_create(ExecutionContext *ctx); - static Value method_defineProperty(ExecutionContext *ctx); - static Value method_defineProperties(ExecutionContext *ctx); - static Value method_seal(ExecutionContext *ctx); - static Value method_freeze(ExecutionContext *ctx); - static Value method_preventExtensions(ExecutionContext *ctx); - static Value method_isSealed(ExecutionContext *ctx); - static Value method_isFrozen(ExecutionContext *ctx); - static Value method_isExtensible(ExecutionContext *ctx); - static Value method_keys(ExecutionContext *ctx); - - static Value method_toString(ExecutionContext *ctx); - static Value method_toLocaleString(ExecutionContext *ctx); - static Value method_valueOf(ExecutionContext *ctx); - static Value method_hasOwnProperty(ExecutionContext *ctx); - static Value method_isPrototypeOf(ExecutionContext *ctx); - static Value method_propertyIsEnumerable(ExecutionContext *ctx); - - static Value method_defineGetter(ExecutionContext *ctx); - static Value method_defineSetter(ExecutionContext *ctx); - - static void toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc); - static Value fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc); -}; - - -} // end of namespace VM -} // end of namespace QQmlJS - -#endif // QV4ECMAOBJECTS_P_H diff --git a/qv4propertydescriptor.h b/qv4propertydescriptor.h deleted file mode 100644 index dc9e1c556d..0000000000 --- a/qv4propertydescriptor.h +++ /dev/null @@ -1,169 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4PROPERTYDESCRIPTOR_H -#define QV4PROPERTYDESCRIPTOR_H - -#include "qmljs_value.h" - -namespace QQmlJS { -namespace VM { - -struct FunctionObject; - -struct PropertyDescriptor { - enum Type { - Generic, - Data, - Accessor - }; - enum State { - Undefined, - Disabled, - Enabled - }; - union { - Value value; - struct { - FunctionObject *get; - FunctionObject *set; - }; - }; - uint type : 8; - uint writable : 8; - uint enumberable : 8; - uint configurable : 8; - - static inline PropertyDescriptor fromValue(Value v) { - PropertyDescriptor pd; - pd.value = v; - pd.type = Data; - pd.writable = Undefined; - pd.enumberable = Undefined; - pd.configurable = Undefined; - return pd; - } - static inline PropertyDescriptor fromAccessor(FunctionObject *getter, FunctionObject *setter) { - PropertyDescriptor pd; - pd.get = getter; - pd.set = setter; - pd.type = Accessor; - pd.writable = Undefined; - pd.enumberable = Undefined; - pd.configurable = Undefined; - return pd; - } - - // Section 8.10 - inline void fullyPopulated() { - if (type == Generic) { - type = Data; - value = Value::undefinedValue(); - } - if (type == Data) { - if (writable == Undefined) - writable = Disabled; - } else { - writable = Undefined; - if ((quintptr)get == 0x1) - get = 0; - if ((quintptr)set == 0x1) - set = 0; - } - if (enumberable == Undefined) - enumberable = Disabled; - if (configurable == Undefined) - configurable = Disabled; - } - - inline bool isData() const { return type == Data || writable != Undefined; } - inline bool isAccessor() const { return type == Accessor; } - inline bool isGeneric() const { return type == Generic && writable == Undefined; } - - inline bool isWritable() const { return writable == Enabled; } - inline bool isEnumerable() const { return enumberable == Enabled; } - inline bool isConfigurable() const { return configurable == Enabled; } - - inline bool isEmpty() const { - return type == Generic && writable == Undefined && enumberable == Undefined && configurable == Undefined; - } - inline bool isSubset(PropertyDescriptor *other) const { - if (type != Generic && type != other->type) - return false; - if (enumberable != Undefined && enumberable != other->enumberable) - return false; - if (configurable != Undefined && configurable != other->configurable) - return false; - if (writable != Undefined && writable != other->writable) - return false; - if (type == Data && !value.sameValue(other->value)) - return false; - if (type == Accessor) { - if (get != other->get) - return false; - if (set != other->set) - return false; - } - return true; - } - inline void operator+=(const PropertyDescriptor &other) { - if (other.enumberable != Undefined) - enumberable = other.enumberable; - if (other.configurable != Undefined) - configurable = other.configurable; - if (other.writable != Undefined) - writable = other.writable; - if (other.type == Accessor) { - type = Accessor; - if (other.get) - get = ((quintptr)other.get == 0x1) ? 0 : other.get; - if (other.set) - set = ((quintptr)other.set == 0x1) ? 0 : other.set; - } else if (other.type == Data){ - type = Data; - value = other.value; - } - } -}; - -} -} - -#endif diff --git a/qv4propertytable.h b/qv4propertytable.h deleted file mode 100644 index 3bd47679b5..0000000000 --- a/qv4propertytable.h +++ /dev/null @@ -1,224 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4PROPERTYTABLE_H -#define QV4PROPERTYTABLE_H - -#include "qmljs_value.h" -#include "qv4propertydescriptor.h" - -namespace QQmlJS { -namespace VM { - -struct ObjectIterator; - -struct PropertyTableEntry { - PropertyDescriptor descriptor; - String *name; - PropertyTableEntry *next; - int index; - - inline PropertyTableEntry(String *name) - : name(name), - next(0), - index(-1) - { } - - inline bool hasName(String *n) const { return name->isEqualTo(n); } - inline unsigned hashValue() const { return name->hashValue(); } -}; - -class PropertyTable -{ - Q_DISABLE_COPY(PropertyTable) - -public: - PropertyTable() - : _properties(0) - , _buckets(0) - , _freeList(0) - , _propertyCount(0) - , _bucketCount(0) - , _primeIdx(-1) - , _allocated(0) - {} - - ~PropertyTable() - { - qDeleteAll(_properties, _properties + _propertyCount); - delete[] _properties; - delete[] _buckets; - } - - typedef PropertyTableEntry **iterator; - inline iterator begin() const { return _properties; } - inline iterator end() const { return _properties + _propertyCount; } - - void remove(PropertyTableEntry *prop) - { - PropertyTableEntry **bucket = _buckets + (prop->hashValue() % _bucketCount); - if (*bucket == prop) { - *bucket = prop->next; - } else { - for (PropertyTableEntry *it = *bucket; it; it = it->next) { - if (it->next == prop) { - it->next = it->next->next; - break; - } - } - } - - _properties[prop->index] = 0; - prop->next = _freeList; - _freeList = prop; - } - - PropertyTableEntry *findEntry(String *name) const - { - if (_properties) { - for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { - if (prop && (prop->name == name || prop->hasName(name))) - return prop; - } - } - - return 0; - } - - PropertyDescriptor *find(String *name) const - { - if (_properties) { - for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { - if (prop && (prop->name == name || prop->hasName(name))) - return &prop->descriptor; - } - } - - return 0; - } - - PropertyDescriptor *insert(String *name) - { - if (PropertyTableEntry *prop = findEntry(name)) - return &prop->descriptor; - - if (_propertyCount == _allocated) { - if (! _allocated) - _allocated = 4; - else - _allocated *= 2; - - PropertyTableEntry **properties = new PropertyTableEntry*[_allocated]; - std::copy(_properties, _properties + _propertyCount, properties); - delete[] _properties; - _properties = properties; - } - - PropertyTableEntry *prop; - if (_freeList) { - prop = _freeList; - prop->name = name; - _freeList = _freeList->next; - } else { - prop = new PropertyTableEntry(name); - } - - prop->index = _propertyCount; - _properties[_propertyCount] = prop; - ++_propertyCount; - - if (! _buckets || 3 * _propertyCount >= 2 * _bucketCount) { - rehash(); - } else { - PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; - prop->next = bucket; - bucket = prop; - } - - return &prop->descriptor; - } - -private: - void rehash() - { - _bucketCount = nextPrime(); - - delete[] _buckets; - _buckets = new PropertyTableEntry *[_bucketCount]; - std::fill(_buckets, _buckets + _bucketCount, (PropertyTableEntry *) 0); - - for (int i = 0; i < _propertyCount; ++i) { - PropertyTableEntry *prop = _properties[i]; - if (prop) { - PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; - prop->next = bucket; - bucket = prop; - } - } - } - - inline int nextPrime() - { - // IMPORTANT: do not add more primes without checking if _primeIdx needs more bits! - static const int primes[] = { - 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853, 25717, 51437, 102877 - }; - - if (_primeIdx < (int) (sizeof(primes)/sizeof(int))) - return primes[++_primeIdx]; - else - return _bucketCount * 2 + 1; // Yes, we're degrading here. But who needs more than about 68000 properties? - } - -private: - friend struct ObjectIterator; - PropertyTableEntry **_properties; - PropertyTableEntry **_buckets; - PropertyTableEntry *_freeList; - int _propertyCount; - int _bucketCount; - int _primeIdx: 5; - int _allocated: 27; -}; - -} -} - -#endif diff --git a/qv4regexp.cpp b/qv4regexp.cpp deleted file mode 100644 index bcef815e25..0000000000 --- a/qv4regexp.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4regexp.h" - -#include "qmljs_engine.h" - -namespace QQmlJS { -namespace VM { - -uint RegExp::match(const QString &string, int start, uint *matchOffsets) -{ - if (!isValid()) - return JSC::Yarr::offsetNoMatch; - - return JSC::Yarr::interpret(m_byteCode.get(), WTF::String(string).characters16(), string.length(), start, matchOffsets); -} - -RegExp::RegExp(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline) - : m_pattern(pattern) - , m_subPatternCount(0) - , m_ignoreCase(ignoreCase) - , m_multiLine(multiline) -{ - if (!engine) - return; - const char* error = 0; - JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiline, &error); - if (error) - return; - m_subPatternCount = yarrPattern.m_numSubpatterns; - m_byteCode = JSC::Yarr::byteCompile(yarrPattern, &engine->bumperPointerAllocator); -} - -} // end of namespace VM -} // end of namespace QQmlJS - - diff --git a/qv4regexp.h b/qv4regexp.h deleted file mode 100644 index 7eae033bec..0000000000 --- a/qv4regexp.h +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4REGEXP_H -#define QV4REGEXP_H - -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -namespace QQmlJS { -namespace VM { - -struct ExecutionEngine; - -class RegExp : public RefCounted -{ -public: - static PassRefPtr create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false) - { return adoptRef(new RegExp(engine, pattern, ignoreCase, multiline)); } - - QString pattern() const { return m_pattern; } - - bool isValid() const { return m_byteCode.get(); } - - uint match(const QString& string, int start, uint *matchOffsets); - - bool ignoreCase() const { return m_ignoreCase; } - bool multiLine() const { return m_multiLine; } - int captureCount() const { return m_subPatternCount + 1; } - -private: - Q_DISABLE_COPY(RegExp); - RegExp(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline); - - const QString m_pattern; - OwnPtr m_byteCode; - int m_subPatternCount; - const bool m_ignoreCase; - const bool m_multiLine; -}; - -} // end of namespace VM -} // end of namespace QQmlJS - -#endif // QV4REGEXP_H diff --git a/qv4regexpobject.cpp b/qv4regexpobject.cpp deleted file mode 100644 index 1f78c56782..0000000000 --- a/qv4regexpobject.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4regexpobject.h" -#include "qv4ir_p.h" -#include "qv4isel_p.h" -#include "qv4objectproto.h" -#include "qv4stringobject.h" -#include "qv4mm.h" - -#include -#include -#include -#include -#include -#include -#include "private/qlocale_tools_p.h" - -#include -#include -#include -#include -#include -#include - -using namespace QQmlJS::VM; - - -RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global) - : value(value) - , global(global) -{ - type = Type_RegExpObject; - - if (!members) - members.reset(new PropertyTable()); - lastIndexProperty = members->insert(engine->identifier(QStringLiteral("lastIndex"))); - lastIndexProperty->type = PropertyDescriptor::Data; - lastIndexProperty->writable = PropertyDescriptor::Enabled; - lastIndexProperty->enumberable = PropertyDescriptor::Disabled; - lastIndexProperty->configurable = PropertyDescriptor::Disabled; - lastIndexProperty->value = Value::fromInt32(0); - if (!this->value.get()) - return; - defineReadonlyProperty(engine->identifier(QStringLiteral("source")), Value::fromString(engine->newString(this->value->pattern()))); - defineReadonlyProperty(engine->identifier(QStringLiteral("global")), Value::fromBoolean(global)); - defineReadonlyProperty(engine->identifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase())); - defineReadonlyProperty(engine->identifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); -} - - -RegExpCtor::RegExpCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value RegExpCtor::construct(ExecutionContext *ctx) -{ - Value r = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); - Value f = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); - if (RegExpObject *re = r.asRegExpObject()) { - if (!f.isUndefined()) - ctx->throwTypeError(); - - RegExpObject *o = ctx->engine->newRegExpObject(re->value, re->global); - return Value::fromObject(o); - } - - if (r.isUndefined()) - r = Value::fromString(ctx, QString()); - else if (!r.isString()) - r = __qmljs_to_string(r, ctx); - - bool global = false; - bool ignoreCase = false; - bool multiLine = false; - if (!f.isUndefined()) { - f = __qmljs_to_string(f, ctx); - QString str = f.stringValue()->toQString(); - for (int i = 0; i < str.length(); ++i) { - if (str.at(i) == QChar('g') && !global) { - global = true; - } else if (str.at(i) == QChar('i') && !ignoreCase) { - ignoreCase = true; - } else if (str.at(i) == QChar('m') && !multiLine) { - multiLine = true; - } else { - ctx->throwSyntaxError(0); - } - } - } - - RefPtr re = RegExp::create(ctx->engine, r.stringValue()->toQString(), ignoreCase, multiLine); - if (!re->isValid()) - ctx->throwSyntaxError(0); - - RegExpObject *o = ctx->engine->newRegExpObject(re, global); - return Value::fromObject(o); -} - -Value RegExpCtor::call(ExecutionContext *ctx) -{ - if (ctx->argumentCount > 0 && ctx->argument(0).asRegExpObject()) { - if (ctx->argumentCount == 1 || ctx->argument(1).isUndefined()) - return ctx->argument(0); - } - - return construct(ctx); -} - -void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(2)); - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 1); - defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 1); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("compile"), method_compile, 2); -} - -Value RegExpPrototype::method_exec(ExecutionContext *ctx) -{ - RegExpObject *r = ctx->thisObject.asRegExpObject(); - if (!r) - ctx->throwTypeError(); - - Value arg = ctx->argument(0); - arg = __qmljs_to_string(arg, ctx); - QString s = arg.stringValue()->toQString(); - - int offset = r->global ? r->lastIndexProperty->value.toInt32(ctx) : 0; - if (offset < 0 || offset > s.length()) { - r->lastIndexProperty->value = Value::fromInt32(0); - return Value::nullValue(); - } - - uint* matchOffsets = (uint*)alloca(r->value->captureCount() * 2 * sizeof(uint)); - int result = r->value->match(s, offset, matchOffsets); - if (result == -1) { - r->lastIndexProperty->value = Value::fromInt32(0); - return Value::nullValue(); - } - - // fill in result data - ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); - for (int i = 0; i < r->value->captureCount(); ++i) { - int start = matchOffsets[i * 2]; - int end = matchOffsets[i * 2 + 1]; - Value entry = Value::undefinedValue(); - if (start != -1 && end != -1) - entry = Value::fromString(ctx, s.mid(start, end - start)); - array->array.push_back(entry); - } - - array->__put__(ctx, QLatin1String("index"), Value::fromInt32(result)); - array->__put__(ctx, QLatin1String("input"), arg); - - if (r->global) - r->lastIndexProperty->value = Value::fromInt32(matchOffsets[1]); - - return Value::fromObject(array); -} - -Value RegExpPrototype::method_test(ExecutionContext *ctx) -{ - Value r = method_exec(ctx); - return Value::fromBoolean(!r.isNull()); -} - -Value RegExpPrototype::method_toString(ExecutionContext *ctx) -{ - RegExpObject *r = ctx->thisObject.asRegExpObject(); - if (!r) - ctx->throwTypeError(); - - QString result = QChar('/') + r->value->pattern(); - result += QChar('/'); - // ### 'g' option missing - if (r->value->ignoreCase()) - result += QChar('i'); - if (r->value->multiLine()) - result += QChar('m'); - return Value::fromString(ctx, result); -} - -Value RegExpPrototype::method_compile(ExecutionContext *ctx) -{ - RegExpObject *r = ctx->thisObject.asRegExpObject(); - if (!r) - ctx->throwTypeError(); - - RegExpObject *re = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ctx->arguments, ctx->argumentCount).asRegExpObject(); - - r->value = re->value; - r->global = re->global; - return Value::undefinedValue(); -} - diff --git a/qv4regexpobject.h b/qv4regexpobject.h deleted file mode 100644 index 50241f2daf..0000000000 --- a/qv4regexpobject.h +++ /dev/null @@ -1,100 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4REGEXPOBJECT_H -#define QV4REGEXPOBJECT_H - -#include "qmljs_runtime.h" -#include "qmljs_engine.h" -#include "qmljs_environment.h" -#include "qv4functionobject.h" -#include "qv4array.h" -#include "qv4string.h" -#include "qv4codegen_p.h" -#include "qv4isel_p.h" -#include "qv4managed.h" -#include "qv4propertydescriptor.h" -#include "qv4propertytable.h" -#include "qv4objectiterator.h" -#include "qv4regexp.h" - -#include -#include -#include -#include -#include - -namespace QQmlJS { - -namespace VM { - -struct RegExpObject: Object { - RefPtr value; - PropertyDescriptor *lastIndexProperty; - bool global; - RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global); -}; - - -struct RegExpCtor: FunctionObject -{ - RegExpCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct RegExpPrototype: RegExpObject -{ - RegExpPrototype(ExecutionEngine* engine): RegExpObject(engine, RegExp::create(0, QString()), false) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_exec(ExecutionContext *ctx); - static Value method_test(ExecutionContext *ctx); - static Value method_toString(ExecutionContext *ctx); - static Value method_compile(ExecutionContext *ctx); -}; - - - -} // namespace VM -} // namespace QQmlJS - -#endif // QMLJS_OBJECTS_H diff --git a/qv4string.cpp b/qv4string.cpp deleted file mode 100644 index 6e0c14d108..0000000000 --- a/qv4string.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4string.h" -#include "qmljs_runtime.h" -#include - -namespace QQmlJS { -namespace VM { - -static uint toArrayIndex(const QChar *ch, const QChar *end) -{ - uint i = ch->unicode() - '0'; - if (i > 9) - return String::InvalidArrayIndex; - ++ch; - // reject "01", "001", ... - if (i == 0 && ch != end) - return String::InvalidArrayIndex; - - while (ch < end) { - uint x = ch->unicode() - '0'; - if (x > 9) - return String::InvalidArrayIndex; - uint n = i*10 + x; - if (n < i) - // overflow - return String::InvalidArrayIndex; - i = n; - ++ch; - } - return i; -} - -uint String::asArrayIndexSlow() const -{ - if (stringHash < LargestHashedArrayIndex) - return stringHash; - - const QChar *ch = _text.constData(); - const QChar *end = ch + _text.length(); - return toArrayIndex(ch, end); -} - -uint String::toUInt(bool *ok) const -{ - *ok = true; - - if (stringHash == InvalidHashValue) - createHashValue(); - if (stringHash < LargestHashedArrayIndex) - return stringHash; - - double d = __qmljs_string_to_number(this); - uint l = (uint)d; - if (d == l) - return l; - *ok = false; - return UINT_MAX; -} - -void String::createHashValue() const -{ - const QChar *ch = _text.constData(); - const QChar *end = ch + _text.length(); - - // array indices get their number as hash value, for large numbers we set to INT_MAX - stringHash = toArrayIndex(ch, end); - if (stringHash < UINT_MAX) { - if (stringHash > INT_MAX) - stringHash = INT_MAX; - return; - } - - uint h = 0xffffffff; - while (ch < end) { - h = 31 * h + ch->unicode(); - ++ch; - } - - // set highest bit to mark it as a non number - stringHash = h | 0xf0000000; -} - -} -} diff --git a/qv4string.h b/qv4string.h deleted file mode 100644 index 34e241218c..0000000000 --- a/qv4string.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4STRING_H -#define QV4STRING_H - -#include -#include - -namespace QQmlJS { -namespace VM { - -struct String : public Managed { - String(const QString &text) - : _text(text) { type = Type_String; stringHash = InvalidHashValue; } - - inline bool isEqualTo(const String *other) const { - if (this == other) - return true; - else if (other && hashValue() == other->hashValue()) - return toQString() == other->toQString(); - return false; - } - - inline const QString &toQString() const { - return _text; - } - - inline unsigned hashValue() const { - if (stringHash == InvalidHashValue) - createHashValue(); - - return stringHash; - } - enum { - InvalidArrayIndex = 0xffffffff, - LargestHashedArrayIndex = 0x7fffffff, - InvalidHashValue = 0xffffffff - }; - uint asArrayIndex() const { - if (stringHash == InvalidHashValue) - createHashValue(); - if (stringHash > LargestHashedArrayIndex) - return InvalidArrayIndex; - if (stringHash < LargestHashedArrayIndex) - return stringHash; - return asArrayIndexSlow(); - } - uint asArrayIndexSlow() const; - uint toUInt(bool *ok) const; - -private: - void createHashValue() const; - - QString _text; -}; - -} -} - -#endif diff --git a/qv4stringobject.cpp b/qv4stringobject.cpp deleted file mode 100644 index 81968cc55a..0000000000 --- a/qv4stringobject.cpp +++ /dev/null @@ -1,722 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qv4stringobject.h" -#include "qv4regexpobject.h" -#include "qv4objectproto.h" -#include "qv4mm.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifndef Q_WS_WIN -# include -# ifndef Q_OS_VXWORKS -# include -# else -# include "qplatformdefs.h" -# endif -#else -# include -#endif - -using namespace QQmlJS::VM; - -StringObject::StringObject(ExecutionContext *ctx, const Value &value) - : value(value) -{ - type = Type_StringObject; - - tmpProperty.type = PropertyDescriptor::Data; - tmpProperty.enumberable = PropertyDescriptor::Enabled; - tmpProperty.writable = PropertyDescriptor::Disabled; - tmpProperty.configurable = PropertyDescriptor::Disabled; - tmpProperty.value = Value::undefinedValue(); - - assert(value.isString()); - defineReadonlyProperty(ctx->engine->id_length, Value::fromUInt32(value.stringValue()->toQString().length())); -} - -PropertyDescriptor *StringObject::getIndex(ExecutionContext *ctx, uint index) -{ - QString str = value.stringValue()->toQString(); - if (index >= (uint)str.length()) - return 0; - String *result = ctx->engine->newString(str.mid(index, 1)); - tmpProperty.value = Value::fromString(result); - return &tmpProperty; -} - -void StringObject::markObjects() -{ - value.stringValue()->mark(); - Object::markObjects(); -} - - -StringCtor::StringCtor(ExecutionContext *scope) - : FunctionObject(scope) -{ -} - -Value StringCtor::construct(ExecutionContext *ctx) -{ - Value value; - if (ctx->argumentCount) - value = Value::fromString(ctx->argument(0).toString(ctx)); - else - value = Value::fromString(ctx, QString()); - return Value::fromObject(ctx->engine->newStringObject(ctx, value)); -} - -Value StringCtor::call(ExecutionContext *ctx) -{ - Value value; - if (ctx->argumentCount) - value = Value::fromString(ctx->argument(0).toString(ctx)); - else - value = Value::fromString(ctx, QString()); - return value; -} - -void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode, 1); - - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_toString); // valueOf and toString are identical - defineDefaultProperty(ctx, QStringLiteral("charAt"), method_charAt, 1); - defineDefaultProperty(ctx, QStringLiteral("charCodeAt"), method_charCodeAt, 1); - defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); - defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); - defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); - defineDefaultProperty(ctx, QStringLiteral("localeCompare"), method_localeCompare, 1); - defineDefaultProperty(ctx, QStringLiteral("match"), method_match, 1); - defineDefaultProperty(ctx, QStringLiteral("replace"), method_replace, 2); - defineDefaultProperty(ctx, QStringLiteral("search"), method_search, 1); - defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); - defineDefaultProperty(ctx, QStringLiteral("split"), method_split, 2); - defineDefaultProperty(ctx, QStringLiteral("substr"), method_substr, 2); - defineDefaultProperty(ctx, QStringLiteral("substring"), method_substring, 2); - defineDefaultProperty(ctx, QStringLiteral("toLowerCase"), method_toLowerCase); - defineDefaultProperty(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); - defineDefaultProperty(ctx, QStringLiteral("toUpperCase"), method_toUpperCase); - defineDefaultProperty(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); - defineDefaultProperty(ctx, QStringLiteral("trim"), method_trim); -} - -static QString getThisString(ExecutionContext *ctx) -{ - String* str = 0; - Value thisObject = ctx->thisObject; - if (StringObject *thisString = thisObject.asStringObject()) - str = thisString->value.stringValue(); - else if (thisObject.isUndefined() || thisObject.isNull()) - ctx->throwTypeError(); - else - str = ctx->thisObject.toString(ctx); - return str->toQString(); -} - -static QString getThisString(ExecutionContext *parentCtx, Value thisObject) -{ - if (thisObject.isString()) - return thisObject.stringValue()->toQString(); - - String* str = 0; - if (StringObject *thisString = thisObject.asStringObject()) - str = thisString->value.stringValue(); - else if (thisObject.isUndefined() || thisObject.isNull()) - parentCtx->throwTypeError(); - else - str = thisObject.toString(parentCtx); - return str->toQString(); -} - -Value StringPrototype::method_toString(ExecutionContext *parentCtx, Value thisObject, Value *, int) -{ - if (thisObject.isString()) - return thisObject; - - StringObject *o = thisObject.asStringObject(); - if (!o) - parentCtx->throwTypeError(); - return o->value; -} - -Value StringPrototype::method_charAt(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) -{ - const QString str = getThisString(parentCtx, thisObject); - - int pos = 0; - if (argc > 0) - pos = (int) argv[0].toInteger(parentCtx); - - QString result; - if (pos >= 0 && pos < str.length()) - result += str.at(pos); - - return Value::fromString(parentCtx, result); -} - -Value StringPrototype::method_charCodeAt(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) -{ - const QString str = getThisString(parentCtx, thisObject); - - int pos = 0; - if (argc > 0) - pos = (int) argv[0].toInteger(parentCtx); - - - if (pos >= 0 && pos < str.length()) - return Value::fromInt32(str.at(pos).unicode()); - - return Value::fromDouble(qSNaN()); -} - -Value StringPrototype::method_concat(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) -{ - QString value = getThisString(parentCtx, thisObject); - - for (int i = 0; i < argc; ++i) { - Value v = __qmljs_to_string(argv[i], parentCtx); - assert(v.isString()); - value += v.stringValue()->toQString(); - } - - return Value::fromString(parentCtx, value); -} - -Value StringPrototype::method_indexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) -{ - QString value = getThisString(parentCtx, thisObject); - - QString searchString; - if (argc) - searchString = argv[0].toString(parentCtx)->toQString(); - - int pos = 0; - if (argc > 1) - pos = (int) argv[1].toInteger(parentCtx); - - int index = -1; - if (! value.isEmpty()) - index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); - - return Value::fromDouble(index); -} - -Value StringPrototype::method_lastIndexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) -{ - const QString value = getThisString(parentCtx, thisObject); - - QString searchString; - if (argc) { - Value v = __qmljs_to_string(argv[0], parentCtx); - searchString = v.stringValue()->toQString(); - } - - Value posArg = argc > 1 ? argv[1] : Value::undefinedValue(); - double position = __qmljs_to_number(posArg, parentCtx); - if (std::isnan(position)) - position = +qInf(); - else - position = trunc(position); - - int pos = trunc(qMin(qMax(position, 0.0), double(value.length()))); - if (!searchString.isEmpty() && pos == value.length()) - --pos; - int index = value.lastIndexOf(searchString, pos); - return Value::fromDouble(index); -} - -Value StringPrototype::method_localeCompare(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) -{ - const QString value = getThisString(parentCtx, thisObject); - const QString that = (argc ? argv[0] : Value::undefinedValue()).toString(parentCtx)->toQString(); - return Value::fromDouble(QString::localeAwareCompare(value, that)); -} - -Value StringPrototype::method_match(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) -{ - if (thisObject.isUndefined() || thisObject.isNull()) - __qmljs_throw_type_error(parentCtx); - - String *s = thisObject.toString(parentCtx); - - Value regexp = argc ? argv[0] : Value::undefinedValue(); - RegExpObject *rx = regexp.asRegExpObject(); - if (!rx) - rx = parentCtx->engine->regExpCtor.asFunctionObject()->construct(parentCtx, ®exp, 1).asRegExpObject(); - - if (!rx) - // ### CHECK - __qmljs_throw_type_error(parentCtx); - - bool global = rx->global; - - // ### use the standard builtin function, not the one that might be redefined in the proto - FunctionObject *exec = parentCtx->engine->regExpPrototype->__get__(parentCtx, parentCtx->engine->identifier(QStringLiteral("exec")), 0).asFunctionObject(); - - Value arg = Value::fromString(s); - if (!global) - return exec->call(parentCtx, Value::fromObject(rx), &arg, 1); - - String *lastIndex = parentCtx->engine->identifier(QStringLiteral("lastIndex")); - rx->__put__(parentCtx, lastIndex, Value::fromInt32(0)); - ArrayObject *a = parentCtx->engine->newArrayObject(parentCtx); - - double previousLastIndex = 0; - uint n = 0; - while (1) { - Value result = exec->call(parentCtx, Value::fromObject(rx), &arg, 1); - if (result.isNull()) - break; - assert(result.isObject()); - double thisIndex = rx->__get__(parentCtx, lastIndex, 0).toInteger(parentCtx); - if (previousLastIndex == thisIndex) { - previousLastIndex = thisIndex + 1; - rx->__put__(parentCtx, lastIndex, Value::fromDouble(previousLastIndex)); - } else { - previousLastIndex = thisIndex; - } - Value matchStr = result.objectValue()->__get__(parentCtx, (uint)0, (bool *)0); - a->array.set(n, matchStr); - ++n; - } - if (!n) - return Value::nullValue(); - - return Value::fromObject(a); - -} - -static QString makeReplacementString(const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) -{ - QString result; - result.reserve(replaceValue.length()); - for (int i = 0; i < replaceValue.length(); ++i) { - if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) { - char ch = replaceValue.at(++i).toLatin1(); - uint substStart = JSC::Yarr::offsetNoMatch; - uint substEnd = JSC::Yarr::offsetNoMatch; - if (ch == '$') { - result += ch; - continue; - } else if (ch == '&') { - substStart = matchOffsets[0]; - substEnd = matchOffsets[1]; - } else if (ch == '`') { - substStart = 0; - substEnd = matchOffsets[0]; - } else if (ch == '\'') { - substStart = matchOffsets[1]; - substEnd = input.length(); - } else if (ch >= '1' && ch <= '9') { - char capture = ch - '0'; - if (capture > 0 && capture < captureCount) { - substStart = matchOffsets[capture * 2]; - substEnd = matchOffsets[capture * 2 + 1]; - } - } else if (ch == '0' && i < replaceValue.length() - 1) { - int capture = (ch - '0') * 10; - ch = replaceValue.at(++i).toLatin1(); - capture += ch - '0'; - if (capture > 0 && capture < captureCount) { - substStart = matchOffsets[capture * 2]; - substEnd = matchOffsets[capture * 2 + 1]; - } - } - if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch) - result += input.midRef(substStart, substEnd - substStart); - } else { - result += replaceValue.at(i); - } - } - return result; -} - -Value StringPrototype::method_replace(ExecutionContext *ctx) -{ - QString string; - if (StringObject *thisString = ctx->thisObject.asStringObject()) - string = thisString->value.stringValue()->toQString(); - else - string = ctx->thisObject.toString(ctx)->toQString(); - - int numCaptures = 0; - QVarLengthArray matchOffsets; - int numStringMatches = 0; - - Value searchValue = ctx->argument(0); - RegExpObject *regExp = searchValue.asRegExpObject(); - if (regExp) { - uint offset = 0; - while (true) { - int oldSize = matchOffsets.size(); - matchOffsets.resize(matchOffsets.size() + regExp->value->captureCount() * 2); - if (regExp->value->match(string, offset, matchOffsets.data() + oldSize) == JSC::Yarr::offsetNoMatch) { - matchOffsets.resize(oldSize); - break; - } - if (!regExp->global) - break; - offset = qMax(offset + 1, matchOffsets[oldSize + 1]); - } - if (regExp->global) - regExp->lastIndexProperty->value = Value::fromUInt32(0); - numStringMatches = matchOffsets.size() / (regExp->value->captureCount() * 2); - numCaptures = regExp->value->captureCount(); - } else { - numCaptures = 1; - QString searchString = searchValue.toString(ctx)->toQString(); - int idx = string.indexOf(searchString); - if (idx != -1) { - numStringMatches = 1; - matchOffsets.resize(2); - matchOffsets[0] = idx; - matchOffsets[1] = idx + searchString.length(); - } - } - - QString result = string; - Value replaceValue = ctx->argument(1); - if (FunctionObject* searchCallback = replaceValue.asFunctionObject()) { - int replacementDelta = 0; - int argc = numCaptures + 2; - Value *args = (Value*)alloca((numCaptures + 2) * sizeof(Value)); - for (int i = 0; i < numStringMatches; ++i) { - for (int k = 0; k < numCaptures; ++k) { - int idx = (i * numCaptures + k) * 2; - uint start = matchOffsets[idx]; - uint end = matchOffsets[idx + 1]; - Value entry = Value::undefinedValue(); - if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch) - entry = Value::fromString(ctx, string.mid(start, end - start)); - args[k] = entry; - } - uint matchStart = matchOffsets[i * numCaptures * 2]; - uint matchEnd = matchOffsets[i * numCaptures * 2 + 1]; - args[numCaptures] = Value::fromUInt32(matchStart); - args[numCaptures + 1] = Value::fromString(ctx, string); - Value replacement = searchCallback->call(ctx, Value::undefinedValue(), args, argc); - QString replacementString = replacement.toString(ctx)->toQString(); - result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacementString); - replacementDelta += replacementString.length() - matchEnd + matchStart; - } - } else { - QString newString = replaceValue.toString(ctx)->toQString(); - int replacementDelta = 0; - - for (int i = 0; i < numStringMatches; ++i) { - int baseIndex = i * numCaptures * 2; - uint matchStart = matchOffsets[baseIndex]; - uint matchEnd = matchOffsets[baseIndex + 1]; - if (matchStart == JSC::Yarr::offsetNoMatch) - continue; - - QString replacement = makeReplacementString(string, newString, matchOffsets.data() + baseIndex, numCaptures); - result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacement); - replacementDelta += replacement.length() - matchEnd + matchStart; - } - } - - return Value::fromString(ctx, result); -} - -Value StringPrototype::method_search(ExecutionContext *ctx) -{ - QString string; - if (StringObject *thisString = ctx->thisObject.asStringObject()) - string = thisString->value.stringValue()->toQString(); - else - string = ctx->thisObject.toString(ctx)->toQString(); - - Value regExpValue = ctx->argument(0); - RegExpObject *regExp = regExpValue.asRegExpObject(); - if (!regExp) { - regExpValue = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ®ExpValue, 1); - regExp = regExpValue.asRegExpObject(); - } - uint* matchOffsets = (uint*)alloca(regExp->value->captureCount() * 2 * sizeof(uint)); - uint result = regExp->value->match(string, /*offset*/0, matchOffsets); - if (result == JSC::Yarr::offsetNoMatch) - return Value::fromInt32(-1); - return Value::fromUInt32(result); -} - -Value StringPrototype::method_slice(ExecutionContext *ctx) -{ - const QString text = getThisString(ctx); - const double length = text.length(); - - double start = ctx->argument(0).toInteger(ctx); - double end = ctx->argument(1).isUndefined() - ? length : ctx->argument(1).toInteger(ctx); - - if (start < 0) - start = qMax(length + start, 0.); - else - start = qMin(start, length); - - if (end < 0) - end = qMax(length + end, 0.); - else - end = qMin(end, length); - - const int intStart = int(start); - const int intEnd = int(end); - - int count = qMax(0, intEnd - intStart); - return Value::fromString(ctx, text.mid(intStart, count)); -} - -Value StringPrototype::method_split(ExecutionContext *ctx) -{ - QString text; - if (StringObject *thisObject = ctx->thisObject.asStringObject()) - text = thisObject->value.stringValue()->toQString(); - else - text = ctx->thisObject.toString(ctx)->toQString(); - - Value separatorValue = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); - Value limitValue = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); - - ArrayObject* array = ctx->engine->newArrayObject(ctx); - Value result = Value::fromObject(array); - - if (separatorValue.isUndefined()) { - if (limitValue.isUndefined()) { - array->array.push_back(Value::fromString(ctx, text)); - return result; - } - return Value::fromString(ctx, text.left(limitValue.toInteger(ctx))); - } - - uint limit = limitValue.isUndefined() ? UINT_MAX : limitValue.toUInt32(ctx); - - if (limit == 0) - return result; - - if (RegExpObject* re = separatorValue.asRegExpObject()) { - if (re->value->pattern().isEmpty()) { - re = 0; - separatorValue = Value::fromString(ctx, QString()); - } - } - - if (RegExpObject* re = separatorValue.asRegExpObject()) { - uint offset = 0; - uint* matchOffsets = (uint*)alloca(re->value->captureCount() * 2 * sizeof(uint)); - while (true) { - uint result = re->value->match(text, offset, matchOffsets); - if (result == JSC::Yarr::offsetNoMatch) - break; - - array->array.push_back(Value::fromString(ctx, text.mid(offset, matchOffsets[0] - offset))); - offset = qMax(offset + 1, matchOffsets[1]); - - if (array->array.length() >= limit) - break; - - for (int i = 1; i < re->value->captureCount(); ++i) { - uint start = matchOffsets[i * 2]; - uint end = matchOffsets[i * 2 + 1]; - array->array.push_back(Value::fromString(ctx, text.mid(start, end - start))); - if (array->array.length() >= limit) - break; - } - } - if (array->array.length() < limit) - array->array.push_back(Value::fromString(ctx, text.mid(offset))); - } else { - QString separator = separatorValue.toString(ctx)->toQString(); - if (separator.isEmpty()) { - for (uint i = 0; i < qMin(limit, uint(text.length())); ++i) - array->array.push_back(Value::fromString(ctx, text.mid(i, 1))); - return result; - } - - int start = 0; - int end; - while ((end = text.indexOf(separator, start)) != -1) { - array->array.push_back(Value::fromString(ctx, text.mid(start, end - start))); - start = end + separator.size(); - if (array->array.length() >= limit) - break; - } - if (array->array.length() < limit && start != -1) - array->array.push_back(Value::fromString(ctx, text.mid(start))); - } - return result; -} - -Value StringPrototype::method_substr(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) -{ - const QString value = getThisString(parentCtx, thisObject); - - double start = 0; - if (argc > 0) - start = argv[0].toInteger(parentCtx); - - double length = +qInf(); - if (argc > 1) - length = argv[1].toInteger(parentCtx); - - double count = value.length(); - if (start < 0) - start = qMax(count + start, 0.0); - - length = qMin(qMax(length, 0.0), count - start); - - qint32 x = Value::toInt32(start); - qint32 y = Value::toInt32(length); - return Value::fromString(parentCtx, value.mid(x, y)); -} - -Value StringPrototype::method_substring(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) -{ - QString value = getThisString(parentCtx, thisObject); - int length = value.length(); - - double start = 0; - double end = length; - - if (argc > 0) - start = argv[0].toInteger(parentCtx); - - Value endValue = argc > 1 ? argv[1] : Value::undefinedValue(); - if (!endValue.isUndefined()) - end = endValue.toInteger(parentCtx); - - if (std::isnan(start) || start < 0) - start = 0; - - if (std::isnan(end) || end < 0) - end = 0; - - if (start > length) - start = length; - - if (end > length) - end = length; - - if (start > end) { - double was = start; - start = end; - end = was; - } - - qint32 x = (int)start; - qint32 y = (int)(end - start); - return Value::fromString(parentCtx, value.mid(x, y)); -} - -Value StringPrototype::method_toLowerCase(ExecutionContext *ctx) -{ - QString value = getThisString(ctx); - return Value::fromString(ctx, value.toLower()); -} - -Value StringPrototype::method_toLocaleLowerCase(ExecutionContext *ctx) -{ - return method_toLowerCase(ctx); -} - -Value StringPrototype::method_toUpperCase(ExecutionContext *ctx) -{ - QString value = getThisString(ctx); - return Value::fromString(ctx, value.toUpper()); -} - -Value StringPrototype::method_toLocaleUpperCase(ExecutionContext *ctx) -{ - return method_toUpperCase(ctx); -} - -Value StringPrototype::method_fromCharCode(ExecutionContext *parentCtx, Value, Value *argv, int argc) -{ - QString str(argc, Qt::Uninitialized); - QChar *ch = str.data(); - for (int i = 0; i < argc; ++i) { - *ch = QChar(argv[i].toUInt16(parentCtx)); - ++ch; - } - return Value::fromString(parentCtx, str); -} - -Value StringPrototype::method_trim(ExecutionContext *ctx) -{ - if (ctx->thisObject.isNull() || ctx->thisObject.isUndefined()) - __qmljs_throw_type_error(ctx); - - QString s = __qmljs_to_string(ctx->thisObject, ctx).stringValue()->toQString(); - const QChar *chars = s.constData(); - int start, end; - for (start = 0; start < s.length(); ++start) { - if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff) - break; - } - for (end = s.length() - 1; end >= start; --end) { - if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff) - break; - } - - return Value::fromString(ctx, QString(chars + start, end - start + 1)); -} diff --git a/qv4stringobject.h b/qv4stringobject.h deleted file mode 100644 index 1becd97a46..0000000000 --- a/qv4stringobject.h +++ /dev/null @@ -1,100 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4STRINGOBJECT_P_H -#define QV4STRINGOBJECT_P_H - -#include "qv4object.h" -#include "qv4functionobject.h" -#include - -namespace QQmlJS { -namespace VM { - -struct StringObject: Object { - Value value; - PropertyDescriptor tmpProperty; - StringObject(ExecutionContext *ctx, const Value &value); - - PropertyDescriptor *getIndex(ExecutionContext *ctx, uint index); - -protected: - virtual void markObjects(); -}; - -struct StringCtor: FunctionObject -{ - StringCtor(ExecutionContext *scope); - - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); -}; - -struct StringPrototype: StringObject -{ - StringPrototype(ExecutionContext *ctx): StringObject(ctx, Value::fromString(ctx, QString())) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_toString(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_charAt(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_charCodeAt(ExecutionContext *, Value thisObject, Value *argv, int argc); - static Value method_concat(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_indexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_lastIndexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_localeCompare(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_match(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_replace(ExecutionContext *ctx); - static Value method_search(ExecutionContext *ctx); - static Value method_slice(ExecutionContext *ctx); - static Value method_split(ExecutionContext *ctx); - static Value method_substr(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_substring(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_toLowerCase(ExecutionContext *ctx); - static Value method_toLocaleLowerCase(ExecutionContext *ctx); - static Value method_toUpperCase(ExecutionContext *ctx); - static Value method_toLocaleUpperCase(ExecutionContext *ctx); - static Value method_fromCharCode(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_trim(ExecutionContext *ctx); -}; - -} // end of namespace VM -} // end of namespace QQmlJS - -#endif // QV4ECMAOBJECTS_P_H diff --git a/qv4syntaxchecker.cpp b/qv4syntaxchecker.cpp deleted file mode 100644 index fcda486af2..0000000000 --- a/qv4syntaxchecker.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4syntaxchecker_p.h" - -using namespace QQmlJS; - -SyntaxChecker::SyntaxChecker() - : Lexer(&m_engine) - , m_stateStack(128) -{ -} - -void QQmlJS::SyntaxChecker::clearText() -{ - m_code.clear(); - m_tokens.clear(); -} - -void SyntaxChecker::appendText(const QString &text) -{ - m_code += text; -} - -QString SyntaxChecker::text() const -{ - return m_code; -} - -bool SyntaxChecker::canEvaluate() -{ - int yyaction = 0; - int yytoken = -1; - int yytos = -1; - - setCode(m_code, 1); - - m_tokens.clear(); - m_tokens.append(T_FEED_JS_PROGRAM); - - do { - if (++yytos == m_stateStack.size()) - m_stateStack.resize(m_stateStack.size() * 2); - - m_stateStack[yytos] = yyaction; - -again: - if (yytoken == -1 && action_index[yyaction] != -TERMINAL_COUNT) { - if (m_tokens.isEmpty()) - yytoken = lex(); - else - yytoken = m_tokens.takeFirst(); - } - - yyaction = t_action(yyaction, yytoken); - if (yyaction > 0) { - if (yyaction == ACCEPT_STATE) { - --yytos; - return true; - } - yytoken = -1; - } else if (yyaction < 0) { - const int ruleno = -yyaction - 1; - yytos -= rhs[ruleno]; - yyaction = nt_action(m_stateStack[yytos], lhs[ruleno] - TERMINAL_COUNT); - } - } while (yyaction); - - const int errorState = m_stateStack[yytos]; - if (t_action(errorState, T_AUTOMATIC_SEMICOLON) && canInsertAutomaticSemicolon(yytoken)) { - yyaction = errorState; - m_tokens.prepend(yytoken); - yytoken = T_SEMICOLON; - goto again; - } - - if (yytoken != EOF_SYMBOL) - return true; - - return false; -} diff --git a/qv4syntaxchecker_p.h b/qv4syntaxchecker_p.h deleted file mode 100644 index 38e123762e..0000000000 --- a/qv4syntaxchecker_p.h +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4SYNTAXCHECKER_P_H -#define QV4SYNTAXCHECKER_P_H - -#include -#include - -#include -#include -#include - -namespace QQmlJS { - -class SyntaxChecker: Lexer -{ -public: - SyntaxChecker(); - - QString text() const; - void clearText(); - void appendText(const QString &text); - - bool canEvaluate(); - -private: - Engine m_engine; - QVector m_stateStack; - QList m_tokens; - QString m_code; -}; - -} // end of QQmlJS namespace - -#endif // QV4SYNTAXCHECKER_P_H diff --git a/src/3rdparty/double-conversion/README b/src/3rdparty/double-conversion/README new file mode 100644 index 0000000000..40ed4a7efd --- /dev/null +++ b/src/3rdparty/double-conversion/README @@ -0,0 +1,6 @@ +This is a copy of the library for binary-decimal and decimal-binary conversion routines for IEEE doubles, taken +from + + http://code.google.com/p/double-conversion/ + +commit e5b34421b763f7bf7e4f9081403db417d5a55a36 diff --git a/src/3rdparty/double-conversion/bignum-dtoa.cc b/src/3rdparty/double-conversion/bignum-dtoa.cc new file mode 100644 index 0000000000..b6c2e85d17 --- /dev/null +++ b/src/3rdparty/double-conversion/bignum-dtoa.cc @@ -0,0 +1,640 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "bignum-dtoa.h" + +#include "bignum.h" +#include "ieee.h" + +namespace double_conversion { + +static int NormalizedExponent(uint64_t significand, int exponent) { + ASSERT(significand != 0); + while ((significand & Double::kHiddenBit) == 0) { + significand = significand << 1; + exponent = exponent - 1; + } + return exponent; +} + + +// Forward declarations: +// Returns an estimation of k such that 10^(k-1) <= v < 10^k. +static int EstimatePower(int exponent); +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. +static void InitialScaledStartValues(uint64_t significand, + int exponent, + bool lower_boundary_is_closer, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus); +// Multiplies numerator/denominator so that its values lies in the range 1-10. +// Returns decimal_point s.t. +// v = numerator'/denominator' * 10^(decimal_point-1) +// where numerator' and denominator' are the values of numerator and +// denominator after the call to this function. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus); +// Generates digits from the left to the right and stops when the generated +// digits yield the shortest decimal representation of v. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector buffer, int* length); +// Generates 'requested_digits' after the decimal point. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector(buffer), int* length); +// Generates 'count' digits of numerator/denominator. +// Once 'count' digits have been produced rounds the result depending on the +// remainder (remainders of exactly .5 round upwards). Might update the +// decimal_point when rounding up (for example for 0.9999). +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector(buffer), int* length); + + +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector buffer, int* length, int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + uint64_t significand; + int exponent; + bool lower_boundary_is_closer; + if (mode == BIGNUM_DTOA_SHORTEST_SINGLE) { + float f = static_cast(v); + ASSERT(f == v); + significand = Single(f).Significand(); + exponent = Single(f).Exponent(); + lower_boundary_is_closer = Single(f).LowerBoundaryIsCloser(); + } else { + significand = Double(v).Significand(); + exponent = Double(v).Exponent(); + lower_boundary_is_closer = Double(v).LowerBoundaryIsCloser(); + } + bool need_boundary_deltas = + (mode == BIGNUM_DTOA_SHORTEST || mode == BIGNUM_DTOA_SHORTEST_SINGLE); + + bool is_even = (significand & 1) == 0; + int normalized_exponent = NormalizedExponent(significand, exponent); + // estimated_power might be too low by 1. + int estimated_power = EstimatePower(normalized_exponent); + + // Shortcut for Fixed. + // The requested digits correspond to the digits after the point. If the + // number is much too small, then there is no need in trying to get any + // digits. + if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) { + buffer[0] = '\0'; + *length = 0; + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + return; + } + + Bignum numerator; + Bignum denominator; + Bignum delta_minus; + Bignum delta_plus; + // Make sure the bignum can grow large enough. The smallest double equals + // 4e-324. In this case the denominator needs fewer than 324*4 binary digits. + // The maximum double is 1.7976931348623157e308 which needs fewer than + // 308*4 binary digits. + ASSERT(Bignum::kMaxSignificantBits >= 324*4); + InitialScaledStartValues(significand, exponent, lower_boundary_is_closer, + estimated_power, need_boundary_deltas, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^estimated_power. + FixupMultiply10(estimated_power, is_even, decimal_point, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^(decimal_point-1), and + // 1 <= (numerator + delta_plus) / denominator < 10 + switch (mode) { + case BIGNUM_DTOA_SHORTEST: + case BIGNUM_DTOA_SHORTEST_SINGLE: + GenerateShortestDigits(&numerator, &denominator, + &delta_minus, &delta_plus, + is_even, buffer, length); + break; + case BIGNUM_DTOA_FIXED: + BignumToFixed(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + case BIGNUM_DTOA_PRECISION: + GenerateCountedDigits(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + default: + UNREACHABLE(); + } + buffer[*length] = '\0'; +} + + +// The procedure starts generating digits from the left to the right and stops +// when the generated digits yield the shortest decimal representation of v. A +// decimal representation of v is a number lying closer to v than to any other +// double, so it converts to v when read. +// +// This is true if d, the decimal representation, is between m- and m+, the +// upper and lower boundaries. d must be strictly between them if !is_even. +// m- := (numerator - delta_minus) / denominator +// m+ := (numerator + delta_plus) / denominator +// +// Precondition: 0 <= (numerator+delta_plus) / denominator < 10. +// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit +// will be produced. This should be the standard precondition. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector buffer, int* length) { + // Small optimization: if delta_minus and delta_plus are the same just reuse + // one of the two bignums. + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_plus = delta_minus; + } + *length = 0; + while (true) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[(*length)++] = digit + '0'; + + // Can we stop already? + // If the remainder of the division is less than the distance to the lower + // boundary we can stop. In this case we simply round down (discarding the + // remainder). + // Similarly we test if we can round up (using the upper boundary). + bool in_delta_room_minus; + bool in_delta_room_plus; + if (is_even) { + in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus); + } else { + in_delta_room_minus = Bignum::Less(*numerator, *delta_minus); + } + if (is_even) { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (!in_delta_room_minus && !in_delta_room_plus) { + // Prepare for next iteration. + numerator->Times10(); + delta_minus->Times10(); + // We optimized delta_plus to be equal to delta_minus (if they share the + // same value). So don't multiply delta_plus if they point to the same + // object. + if (delta_minus != delta_plus) { + delta_plus->Times10(); + } + } else if (in_delta_room_minus && in_delta_room_plus) { + // Let's see if 2*numerator < denominator. + // If yes, then the next digit would be < 5 and we can round down. + int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator); + if (compare < 0) { + // Remaining digits are less than .5. -> Round down (== do nothing). + } else if (compare > 0) { + // Remaining digits are more than .5 of denominator. -> Round up. + // Note that the last digit could not be a '9' as otherwise the whole + // loop would have stopped earlier. + // We still have an assert here in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } else { + // Halfway case. + // TODO(floitsch): need a way to solve half-way cases. + // For now let's round towards even (since this is what Gay seems to + // do). + + if ((buffer[(*length) - 1] - '0') % 2 == 0) { + // Round down => Do nothing. + } else { + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } + } + return; + } else if (in_delta_room_minus) { + // Round down (== do nothing). + return; + } else { // in_delta_room_plus + // Round up. + // Note again that the last digit could not be '9' since this would have + // stopped the loop earlier. + // We still have an ASSERT here, in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) -1] != '9'); + buffer[(*length) - 1]++; + return; + } + } +} + + +// Let v = numerator / denominator < 10. +// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point) +// from left to right. Once 'count' digits have been produced we decide wether +// to round up or down. Remainders of exactly .5 round upwards. Numbers such +// as 9.999999 propagate a carry all the way, and change the +// exponent (decimal_point), when rounding upwards. +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector(buffer), int* length) { + ASSERT(count >= 0); + for (int i = 0; i < count - 1; ++i) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[i] = digit + '0'; + // Prepare for next iteration. + numerator->Times10(); + } + // Generate the last digit. + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + digit++; + } + buffer[count - 1] = digit + '0'; + // Correct bad digits (in case we had a sequence of '9's). Propagate the + // carry until we hat a non-'9' or til we reach the first digit. + for (int i = count - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + if (buffer[0] == '0' + 10) { + // Propagate a carry past the top place. + buffer[0] = '1'; + (*decimal_point)++; + } + *length = count; +} + + +// Generates 'requested_digits' after the decimal point. It might omit +// trailing '0's. If the input number is too small then no digits at all are +// generated (ex.: 2 fixed digits for 0.00001). +// +// Input verifies: 1 <= (numerator + delta) / denominator < 10. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector(buffer), int* length) { + // Note that we have to look at more than just the requested_digits, since + // a number could be rounded up. Example: v=0.5 with requested_digits=0. + // Even though the power of v equals 0 we can't just stop here. + if (-(*decimal_point) > requested_digits) { + // The number is definitively too small. + // Ex: 0.001 with requested_digits == 1. + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + *length = 0; + return; + } else if (-(*decimal_point) == requested_digits) { + // We only need to verify if the number rounds down or up. + // Ex: 0.04 and 0.06 with requested_digits == 1. + ASSERT(*decimal_point == -requested_digits); + // Initially the fraction lies in range (1, 10]. Multiply the denominator + // by 10 so that we can compare more easily. + denominator->Times10(); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + // If the fraction is >= 0.5 then we have to include the rounded + // digit. + buffer[0] = '1'; + *length = 1; + (*decimal_point)++; + } else { + // Note that we caught most of similar cases earlier. + *length = 0; + } + return; + } else { + // The requested digits correspond to the digits after the point. + // The variable 'needed_digits' includes the digits before the point. + int needed_digits = (*decimal_point) + requested_digits; + GenerateCountedDigits(needed_digits, decimal_point, + numerator, denominator, + buffer, length); + } +} + + +// Returns an estimation of k such that 10^(k-1) <= v < 10^k where +// v = f * 2^exponent and 2^52 <= f < 2^53. +// v is hence a normalized double with the given exponent. The output is an +// approximation for the exponent of the decimal approimation .digits * 10^k. +// +// The result might undershoot by 1 in which case 10^k <= v < 10^k+1. +// Note: this property holds for v's upper boundary m+ too. +// 10^k <= m+ < 10^k+1. +// (see explanation below). +// +// Examples: +// EstimatePower(0) => 16 +// EstimatePower(-52) => 0 +// +// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0. +static int EstimatePower(int exponent) { + // This function estimates log10 of v where v = f*2^e (with e == exponent). + // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)). + // Note that f is bounded by its container size. Let p = 53 (the double's + // significand size). Then 2^(p-1) <= f < 2^p. + // + // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close + // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)). + // The computed number undershoots by less than 0.631 (when we compute log3 + // and not log10). + // + // Optimization: since we only need an approximated result this computation + // can be performed on 64 bit integers. On x86/x64 architecture the speedup is + // not really measurable, though. + // + // Since we want to avoid overshooting we decrement by 1e10 so that + // floating-point imprecisions don't affect us. + // + // Explanation for v's boundary m+: the computation takes advantage of + // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement + // (even for denormals where the delta can be much more important). + + const double k1Log10 = 0.30102999566398114; // 1/lg(10) + + // For doubles len(f) == 53 (don't forget the hidden bit). + const int kSignificandSize = Double::kSignificandSize; + double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10); + return static_cast(estimate); +} + + +// See comments for InitialScaledStartValues. +static void InitialScaledStartValuesPositiveExponent( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // A positive exponent implies a positive power. + ASSERT(estimated_power >= 0); + // Since the estimated_power is positive we simply multiply the denominator + // by 10^estimated_power. + + // numerator = v. + numerator->AssignUInt64(significand); + numerator->ShiftLeft(exponent); + // denominator = 10^estimated_power. + denominator->AssignPowerUInt16(10, estimated_power); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + delta_plus->AssignUInt16(1); + delta_plus->ShiftLeft(exponent); + // Same for delta_minus. The adjustments if f == 2^p-1 are done later. + delta_minus->AssignUInt16(1); + delta_minus->ShiftLeft(exponent); + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentPositivePower( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // v = f * 2^e with e < 0, and with estimated_power >= 0. + // This means that e is close to 0 (have a look at how estimated_power is + // computed). + + // numerator = significand + // since v = significand * 2^exponent this is equivalent to + // numerator = v * / 2^-exponent + numerator->AssignUInt64(significand); + // denominator = 10^estimated_power * 2^-exponent (with exponent < 0) + denominator->AssignPowerUInt16(10, estimated_power); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + // Given that the denominator already includes v's exponent the distance + // to the boundaries is simply 1. + delta_plus->AssignUInt16(1); + // Same for delta_minus. The adjustments if f == 2^p-1 are done later. + delta_minus->AssignUInt16(1); + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentNegativePower( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // Instead of multiplying the denominator with 10^estimated_power we + // multiply all values (numerator and deltas) by 10^-estimated_power. + + // Use numerator as temporary container for power_ten. + Bignum* power_ten = numerator; + power_ten->AssignPowerUInt16(10, -estimated_power); + + if (need_boundary_deltas) { + // Since power_ten == numerator we must make a copy of 10^estimated_power + // before we complete the computation of the numerator. + // delta_plus = delta_minus = 10^estimated_power + delta_plus->AssignBignum(*power_ten); + delta_minus->AssignBignum(*power_ten); + } + + // numerator = significand * 2 * 10^-estimated_power + // since v = significand * 2^exponent this is equivalent to + // numerator = v * 10^-estimated_power * 2 * 2^-exponent. + // Remember: numerator has been abused as power_ten. So no need to assign it + // to itself. + ASSERT(numerator == power_ten); + numerator->MultiplyByUInt64(significand); + + // denominator = 2 * 2^-exponent with exponent < 0. + denominator->AssignUInt16(1); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + numerator->ShiftLeft(1); + denominator->ShiftLeft(1); + // With this shift the boundaries have their correct value, since + // delta_plus = 10^-estimated_power, and + // delta_minus = 10^-estimated_power. + // These assignments have been done earlier. + // The adjustments if f == 2^p-1 (lower boundary is closer) are done later. + } +} + + +// Let v = significand * 2^exponent. +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. The functions GenerateShortestDigits and +// GenerateCountedDigits will then convert this ratio to its decimal +// representation d, with the required accuracy. +// Then d * 10^estimated_power is the representation of v. +// (Note: the fraction and the estimated_power might get adjusted before +// generating the decimal representation.) +// +// The initial start values consist of: +// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power. +// - a scaled (common) denominator. +// optionally (used by GenerateShortestDigits to decide if it has the shortest +// decimal converting back to v): +// - v - m-: the distance to the lower boundary. +// - m+ - v: the distance to the upper boundary. +// +// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator. +// +// Let ep == estimated_power, then the returned values will satisfy: +// v / 10^ep = numerator / denominator. +// v's boundarys m- and m+: +// m- / 10^ep == v / 10^ep - delta_minus / denominator +// m+ / 10^ep == v / 10^ep + delta_plus / denominator +// Or in other words: +// m- == v - delta_minus * 10^ep / denominator; +// m+ == v + delta_plus * 10^ep / denominator; +// +// Since 10^(k-1) <= v < 10^k (with k == estimated_power) +// or 10^k <= v < 10^(k+1) +// we then have 0.1 <= numerator/denominator < 1 +// or 1 <= numerator/denominator < 10 +// +// It is then easy to kickstart the digit-generation routine. +// +// The boundary-deltas are only filled if the mode equals BIGNUM_DTOA_SHORTEST +// or BIGNUM_DTOA_SHORTEST_SINGLE. + +static void InitialScaledStartValues(uint64_t significand, + int exponent, + bool lower_boundary_is_closer, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) { + if (exponent >= 0) { + InitialScaledStartValuesPositiveExponent( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else if (estimated_power >= 0) { + InitialScaledStartValuesNegativeExponentPositivePower( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else { + InitialScaledStartValuesNegativeExponentNegativePower( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } + + if (need_boundary_deltas && lower_boundary_is_closer) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the common denominator and adapt all but the delta_minus. + denominator->ShiftLeft(1); // *2 + numerator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } +} + + +// This routine multiplies numerator/denominator so that its values lies in the +// range 1-10. That is after a call to this function we have: +// 1 <= (numerator + delta_plus) /denominator < 10. +// Let numerator the input before modification and numerator' the argument +// after modification, then the output-parameter decimal_point is such that +// numerator / denominator * 10^estimated_power == +// numerator' / denominator' * 10^(decimal_point - 1) +// In some cases estimated_power was too low, and this is already the case. We +// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k == +// estimated_power) but do not touch the numerator or denominator. +// Otherwise the routine multiplies the numerator and the deltas by 10. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + bool in_range; + if (is_even) { + // For IEEE doubles half-way cases (in decimal system numbers ending with 5) + // are rounded to the closest floating-point number with even significand. + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (in_range) { + // Since numerator + delta_plus >= denominator we already have + // 1 <= numerator/denominator < 10. Simply update the estimated_power. + *decimal_point = estimated_power + 1; + } else { + *decimal_point = estimated_power; + numerator->Times10(); + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_minus->Times10(); + delta_plus->AssignBignum(*delta_minus); + } else { + delta_minus->Times10(); + delta_plus->Times10(); + } + } +} + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/bignum-dtoa.h b/src/3rdparty/double-conversion/bignum-dtoa.h new file mode 100644 index 0000000000..34b961992d --- /dev/null +++ b/src/3rdparty/double-conversion/bignum-dtoa.h @@ -0,0 +1,84 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_BIGNUM_DTOA_H_ +#define DOUBLE_CONVERSION_BIGNUM_DTOA_H_ + +#include "utils.h" + +namespace double_conversion { + +enum BignumDtoaMode { + // Return the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate but + // correct) 0.3. + BIGNUM_DTOA_SHORTEST, + // Same as BIGNUM_DTOA_SHORTEST but for single-precision floats. + BIGNUM_DTOA_SHORTEST_SINGLE, + // Return a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + BIGNUM_DTOA_FIXED, + // Return a fixed number of digits, no matter what the exponent is. + BIGNUM_DTOA_PRECISION +}; + +// Converts the given double 'v' to ascii. +// The result should be interpreted as buffer * 10^(point-length). +// The buffer will be null-terminated. +// +// The input v must be > 0 and different from NaN, and Infinity. +// +// The output depends on the given mode: +// - SHORTEST: produce the least amount of digits for which the internal +// identity requirement is still satisfied. If the digits are printed +// (together with the correct exponent) then reading this number will give +// 'v' again. The buffer will choose the representation that is closest to +// 'v'. If there are two at the same distance, than the number is round up. +// In this mode the 'requested_digits' parameter is ignored. +// - FIXED: produces digits necessary to print a given number with +// 'requested_digits' digits after the decimal point. The produced digits +// might be too short in which case the caller has to fill the gaps with '0's. +// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. +// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns +// buffer="2", point=0. +// Note: the length of the returned buffer has no meaning wrt the significance +// of its digits. That is, just because it contains '0's does not mean that +// any other digit would not satisfy the internal identity requirement. +// - PRECISION: produces 'requested_digits' where the first digit is not '0'. +// Even though the length of produced digits usually equals +// 'requested_digits', the function is allowed to return fewer digits, in +// which case the caller has to fill the missing digits with '0's. +// Halfway cases are again rounded up. +// 'BignumDtoa' expects the given buffer to be big enough to hold all digits +// and a terminating null-character. +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector buffer, int* length, int* point); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_BIGNUM_DTOA_H_ diff --git a/src/3rdparty/double-conversion/bignum.cc b/src/3rdparty/double-conversion/bignum.cc new file mode 100644 index 0000000000..747491a089 --- /dev/null +++ b/src/3rdparty/double-conversion/bignum.cc @@ -0,0 +1,764 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "bignum.h" +#include "utils.h" + +namespace double_conversion { + +Bignum::Bignum() + : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) { + for (int i = 0; i < kBigitCapacity; ++i) { + bigits_[i] = 0; + } +} + + +template +static int BitSize(S value) { + return 8 * sizeof(value); +} + +// Guaranteed to lie in one Bigit. +void Bignum::AssignUInt16(uint16_t value) { + ASSERT(kBigitSize >= BitSize(value)); + Zero(); + if (value == 0) return; + + EnsureCapacity(1); + bigits_[0] = value; + used_digits_ = 1; +} + + +void Bignum::AssignUInt64(uint64_t value) { + const int kUInt64Size = 64; + + Zero(); + if (value == 0) return; + + int needed_bigits = kUInt64Size / kBigitSize + 1; + EnsureCapacity(needed_bigits); + for (int i = 0; i < needed_bigits; ++i) { + bigits_[i] = value & kBigitMask; + value = value >> kBigitSize; + } + used_digits_ = needed_bigits; + Clamp(); +} + + +void Bignum::AssignBignum(const Bignum& other) { + exponent_ = other.exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + bigits_[i] = other.bigits_[i]; + } + // Clear the excess digits (if there were any). + for (int i = other.used_digits_; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = other.used_digits_; +} + + +static uint64_t ReadUInt64(Vector buffer, + int from, + int digits_to_read) { + uint64_t result = 0; + for (int i = from; i < from + digits_to_read; ++i) { + int digit = buffer[i] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = result * 10 + digit; + } + return result; +} + + +void Bignum::AssignDecimalString(Vector value) { + // 2^64 = 18446744073709551616 > 10^19 + const int kMaxUint64DecimalDigits = 19; + Zero(); + int length = value.length(); + int pos = 0; + // Let's just say that each digit needs 4 bits. + while (length >= kMaxUint64DecimalDigits) { + uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits); + pos += kMaxUint64DecimalDigits; + length -= kMaxUint64DecimalDigits; + MultiplyByPowerOfTen(kMaxUint64DecimalDigits); + AddUInt64(digits); + } + uint64_t digits = ReadUInt64(value, pos, length); + MultiplyByPowerOfTen(length); + AddUInt64(digits); + Clamp(); +} + + +static int HexCharValue(char c) { + if ('0' <= c && c <= '9') return c - '0'; + if ('a' <= c && c <= 'f') return 10 + c - 'a'; + if ('A' <= c && c <= 'F') return 10 + c - 'A'; + UNREACHABLE(); + return 0; // To make compiler happy. +} + + +void Bignum::AssignHexString(Vector value) { + Zero(); + int length = value.length(); + + int needed_bigits = length * 4 / kBigitSize + 1; + EnsureCapacity(needed_bigits); + int string_index = length - 1; + for (int i = 0; i < needed_bigits - 1; ++i) { + // These bigits are guaranteed to be "full". + Chunk current_bigit = 0; + for (int j = 0; j < kBigitSize / 4; j++) { + current_bigit += HexCharValue(value[string_index--]) << (j * 4); + } + bigits_[i] = current_bigit; + } + used_digits_ = needed_bigits - 1; + + Chunk most_significant_bigit = 0; // Could be = 0; + for (int j = 0; j <= string_index; ++j) { + most_significant_bigit <<= 4; + most_significant_bigit += HexCharValue(value[j]); + } + if (most_significant_bigit != 0) { + bigits_[used_digits_] = most_significant_bigit; + used_digits_++; + } + Clamp(); +} + + +void Bignum::AddUInt64(uint64_t operand) { + if (operand == 0) return; + Bignum other; + other.AssignUInt64(operand); + AddBignum(other); +} + + +void Bignum::AddBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + + // If this has a greater exponent than other append zero-bigits to this. + // After this call exponent_ <= other.exponent_. + Align(other); + + // There are two possibilities: + // aaaaaaaaaaa 0000 (where the 0s represent a's exponent) + // bbbbb 00000000 + // ---------------- + // ccccccccccc 0000 + // or + // aaaaaaaaaa 0000 + // bbbbbbbbb 0000000 + // ----------------- + // cccccccccccc 0000 + // In both cases we might need a carry bigit. + + EnsureCapacity(1 + Max(BigitLength(), other.BigitLength()) - exponent_); + Chunk carry = 0; + int bigit_pos = other.exponent_ - exponent_; + ASSERT(bigit_pos >= 0); + for (int i = 0; i < other.used_digits_; ++i) { + Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + + while (carry != 0) { + Chunk sum = bigits_[bigit_pos] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + used_digits_ = Max(bigit_pos, used_digits_); + ASSERT(IsClamped()); +} + + +void Bignum::SubtractBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + // We require this to be bigger than other. + ASSERT(LessEqual(other, *this)); + + Align(other); + + int offset = other.exponent_ - exponent_; + Chunk borrow = 0; + int i; + for (i = 0; i < other.used_digits_; ++i) { + ASSERT((borrow == 0) || (borrow == 1)); + Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + } + while (borrow != 0) { + Chunk difference = bigits_[i + offset] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + ++i; + } + Clamp(); +} + + +void Bignum::ShiftLeft(int shift_amount) { + if (used_digits_ == 0) return; + exponent_ += shift_amount / kBigitSize; + int local_shift = shift_amount % kBigitSize; + EnsureCapacity(used_digits_ + 1); + BigitsShiftLeft(local_shift); +} + + +void Bignum::MultiplyByUInt32(uint32_t factor) { + if (factor == 1) return; + if (factor == 0) { + Zero(); + return; + } + if (used_digits_ == 0) return; + + // The product of a bigit with the factor is of size kBigitSize + 32. + // Assert that this number + 1 (for the carry) fits into double chunk. + ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1); + DoubleChunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + DoubleChunk product = static_cast(factor) * bigits_[i] + carry; + bigits_[i] = static_cast(product & kBigitMask); + carry = (product >> kBigitSize); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } +} + + +void Bignum::MultiplyByUInt64(uint64_t factor) { + if (factor == 1) return; + if (factor == 0) { + Zero(); + return; + } + ASSERT(kBigitSize < 32); + uint64_t carry = 0; + uint64_t low = factor & 0xFFFFFFFF; + uint64_t high = factor >> 32; + for (int i = 0; i < used_digits_; ++i) { + uint64_t product_low = low * bigits_[i]; + uint64_t product_high = high * bigits_[i]; + uint64_t tmp = (carry & kBigitMask) + product_low; + bigits_[i] = tmp & kBigitMask; + carry = (carry >> kBigitSize) + (tmp >> kBigitSize) + + (product_high << (32 - kBigitSize)); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } +} + + +void Bignum::MultiplyByPowerOfTen(int exponent) { + const uint64_t kFive27 = UINT64_2PART_C(0x6765c793, fa10079d); + const uint16_t kFive1 = 5; + const uint16_t kFive2 = kFive1 * 5; + const uint16_t kFive3 = kFive2 * 5; + const uint16_t kFive4 = kFive3 * 5; + const uint16_t kFive5 = kFive4 * 5; + const uint16_t kFive6 = kFive5 * 5; + const uint32_t kFive7 = kFive6 * 5; + const uint32_t kFive8 = kFive7 * 5; + const uint32_t kFive9 = kFive8 * 5; + const uint32_t kFive10 = kFive9 * 5; + const uint32_t kFive11 = kFive10 * 5; + const uint32_t kFive12 = kFive11 * 5; + const uint32_t kFive13 = kFive12 * 5; + const uint32_t kFive1_to_12[] = + { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6, + kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 }; + + ASSERT(exponent >= 0); + if (exponent == 0) return; + if (used_digits_ == 0) return; + + // We shift by exponent at the end just before returning. + int remaining_exponent = exponent; + while (remaining_exponent >= 27) { + MultiplyByUInt64(kFive27); + remaining_exponent -= 27; + } + while (remaining_exponent >= 13) { + MultiplyByUInt32(kFive13); + remaining_exponent -= 13; + } + if (remaining_exponent > 0) { + MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]); + } + ShiftLeft(exponent); +} + + +void Bignum::Square() { + ASSERT(IsClamped()); + int product_length = 2 * used_digits_; + EnsureCapacity(product_length); + + // Comba multiplication: compute each column separately. + // Example: r = a2a1a0 * b2b1b0. + // r = 1 * a0b0 + + // 10 * (a1b0 + a0b1) + + // 100 * (a2b0 + a1b1 + a0b2) + + // 1000 * (a2b1 + a1b2) + + // 10000 * a2b2 + // + // In the worst case we have to accumulate nb-digits products of digit*digit. + // + // Assert that the additional number of bits in a DoubleChunk are enough to + // sum up used_digits of Bigit*Bigit. + if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) { + UNIMPLEMENTED(); + } + DoubleChunk accumulator = 0; + // First shift the digits so we don't overwrite them. + int copy_offset = used_digits_; + for (int i = 0; i < used_digits_; ++i) { + bigits_[copy_offset + i] = bigits_[i]; + } + // We have two loops to avoid some 'if's in the loop. + for (int i = 0; i < used_digits_; ++i) { + // Process temporary digit i with power i. + // The sum of the two indices must be equal to i. + int bigit_index1 = i; + int bigit_index2 = 0; + // Sum all of the sub-products. + while (bigit_index1 >= 0) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + bigits_[i] = static_cast(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + for (int i = used_digits_; i < product_length; ++i) { + int bigit_index1 = used_digits_ - 1; + int bigit_index2 = i - bigit_index1; + // Invariant: sum of both indices is again equal to i. + // Inner loop runs 0 times on last iteration, emptying accumulator. + while (bigit_index2 < used_digits_) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + // The overwritten bigits_[i] will never be read in further loop iterations, + // because bigit_index1 and bigit_index2 are always greater + // than i - used_digits_. + bigits_[i] = static_cast(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + // Since the result was guaranteed to lie inside the number the + // accumulator must be 0 now. + ASSERT(accumulator == 0); + + // Don't forget to update the used_digits and the exponent. + used_digits_ = product_length; + exponent_ *= 2; + Clamp(); +} + + +void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { + ASSERT(base != 0); + ASSERT(power_exponent >= 0); + if (power_exponent == 0) { + AssignUInt16(1); + return; + } + Zero(); + int shifts = 0; + // We expect base to be in range 2-32, and most often to be 10. + // It does not make much sense to implement different algorithms for counting + // the bits. + while ((base & 1) == 0) { + base >>= 1; + shifts++; + } + int bit_size = 0; + int tmp_base = base; + while (tmp_base != 0) { + tmp_base >>= 1; + bit_size++; + } + int final_size = bit_size * power_exponent; + // 1 extra bigit for the shifting, and one for rounded final_size. + EnsureCapacity(final_size / kBigitSize + 2); + + // Left to Right exponentiation. + int mask = 1; + while (power_exponent >= mask) mask <<= 1; + + // The mask is now pointing to the bit above the most significant 1-bit of + // power_exponent. + // Get rid of first 1-bit; + mask >>= 2; + uint64_t this_value = base; + + bool delayed_multipliciation = false; + const uint64_t max_32bits = 0xFFFFFFFF; + while (mask != 0 && this_value <= max_32bits) { + this_value = this_value * this_value; + // Verify that there is enough space in this_value to perform the + // multiplication. The first bit_size bits must be 0. + if ((power_exponent & mask) != 0) { + uint64_t base_bits_mask = + ~((static_cast(1) << (64 - bit_size)) - 1); + bool high_bits_zero = (this_value & base_bits_mask) == 0; + if (high_bits_zero) { + this_value *= base; + } else { + delayed_multipliciation = true; + } + } + mask >>= 1; + } + AssignUInt64(this_value); + if (delayed_multipliciation) { + MultiplyByUInt32(base); + } + + // Now do the same thing as a bignum. + while (mask != 0) { + Square(); + if ((power_exponent & mask) != 0) { + MultiplyByUInt32(base); + } + mask >>= 1; + } + + // And finally add the saved shifts. + ShiftLeft(shifts * power_exponent); +} + + +// Precondition: this/other < 16bit. +uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + ASSERT(other.used_digits_ > 0); + + // Easy case: if we have less digits than the divisor than the result is 0. + // Note: this handles the case where this == 0, too. + if (BigitLength() < other.BigitLength()) { + return 0; + } + + Align(other); + + uint16_t result = 0; + + // Start by removing multiples of 'other' until both numbers have the same + // number of digits. + while (BigitLength() > other.BigitLength()) { + // This naive approach is extremely inefficient if the this divided other + // might be big. This function is implemented for doubleToString where + // the result should be small (less than 10). + ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16)); + // Remove the multiples of the first digit. + // Example this = 23 and other equals 9. -> Remove 2 multiples. + result += bigits_[used_digits_ - 1]; + SubtractTimes(other, bigits_[used_digits_ - 1]); + } + + ASSERT(BigitLength() == other.BigitLength()); + + // Both bignums are at the same length now. + // Since other has more than 0 digits we know that the access to + // bigits_[used_digits_ - 1] is safe. + Chunk this_bigit = bigits_[used_digits_ - 1]; + Chunk other_bigit = other.bigits_[other.used_digits_ - 1]; + + if (other.used_digits_ == 1) { + // Shortcut for easy (and common) case. + int quotient = this_bigit / other_bigit; + bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient; + result += quotient; + Clamp(); + return result; + } + + int division_estimate = this_bigit / (other_bigit + 1); + result += division_estimate; + SubtractTimes(other, division_estimate); + + if (other_bigit * (division_estimate + 1) > this_bigit) { + // No need to even try to subtract. Even if other's remaining digits were 0 + // another subtraction would be too much. + return result; + } + + while (LessEqual(other, *this)) { + SubtractBignum(other); + result++; + } + return result; +} + + +template +static int SizeInHexChars(S number) { + ASSERT(number > 0); + int result = 0; + while (number != 0) { + number >>= 4; + result++; + } + return result; +} + + +static char HexCharOfValue(int value) { + ASSERT(0 <= value && value <= 16); + if (value < 10) return value + '0'; + return value - 10 + 'A'; +} + + +bool Bignum::ToHexString(char* buffer, int buffer_size) const { + ASSERT(IsClamped()); + // Each bigit must be printable as separate hex-character. + ASSERT(kBigitSize % 4 == 0); + const int kHexCharsPerBigit = kBigitSize / 4; + + if (used_digits_ == 0) { + if (buffer_size < 2) return false; + buffer[0] = '0'; + buffer[1] = '\0'; + return true; + } + // We add 1 for the terminating '\0' character. + int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit + + SizeInHexChars(bigits_[used_digits_ - 1]) + 1; + if (needed_chars > buffer_size) return false; + int string_index = needed_chars - 1; + buffer[string_index--] = '\0'; + for (int i = 0; i < exponent_; ++i) { + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = '0'; + } + } + for (int i = 0; i < used_digits_ - 1; ++i) { + Chunk current_bigit = bigits_[i]; + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = HexCharOfValue(current_bigit & 0xF); + current_bigit >>= 4; + } + } + // And finally the last bigit. + Chunk most_significant_bigit = bigits_[used_digits_ - 1]; + while (most_significant_bigit != 0) { + buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF); + most_significant_bigit >>= 4; + } + return true; +} + + +Bignum::Chunk Bignum::BigitAt(int index) const { + if (index >= BigitLength()) return 0; + if (index < exponent_) return 0; + return bigits_[index - exponent_]; +} + + +int Bignum::Compare(const Bignum& a, const Bignum& b) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + int bigit_length_a = a.BigitLength(); + int bigit_length_b = b.BigitLength(); + if (bigit_length_a < bigit_length_b) return -1; + if (bigit_length_a > bigit_length_b) return +1; + for (int i = bigit_length_a - 1; i >= Min(a.exponent_, b.exponent_); --i) { + Chunk bigit_a = a.BigitAt(i); + Chunk bigit_b = b.BigitAt(i); + if (bigit_a < bigit_b) return -1; + if (bigit_a > bigit_b) return +1; + // Otherwise they are equal up to this digit. Try the next digit. + } + return 0; +} + + +int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + ASSERT(c.IsClamped()); + if (a.BigitLength() < b.BigitLength()) { + return PlusCompare(b, a, c); + } + if (a.BigitLength() + 1 < c.BigitLength()) return -1; + if (a.BigitLength() > c.BigitLength()) return +1; + // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than + // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one + // of 'a'. + if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) { + return -1; + } + + Chunk borrow = 0; + // Starting at min_exponent all digits are == 0. So no need to compare them. + int min_exponent = Min(Min(a.exponent_, b.exponent_), c.exponent_); + for (int i = c.BigitLength() - 1; i >= min_exponent; --i) { + Chunk chunk_a = a.BigitAt(i); + Chunk chunk_b = b.BigitAt(i); + Chunk chunk_c = c.BigitAt(i); + Chunk sum = chunk_a + chunk_b; + if (sum > chunk_c + borrow) { + return +1; + } else { + borrow = chunk_c + borrow - sum; + if (borrow > 1) return -1; + borrow <<= kBigitSize; + } + } + if (borrow == 0) return 0; + return -1; +} + + +void Bignum::Clamp() { + while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) { + used_digits_--; + } + if (used_digits_ == 0) { + // Zero. + exponent_ = 0; + } +} + + +bool Bignum::IsClamped() const { + return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0; +} + + +void Bignum::Zero() { + for (int i = 0; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = 0; + exponent_ = 0; +} + + +void Bignum::Align(const Bignum& other) { + if (exponent_ > other.exponent_) { + // If "X" represents a "hidden" digit (by the exponent) then we are in the + // following case (a == this, b == other): + // a: aaaaaaXXXX or a: aaaaaXXX + // b: bbbbbbX b: bbbbbbbbXX + // We replace some of the hidden digits (X) of a with 0 digits. + // a: aaaaaa000X or a: aaaaa0XX + int zero_digits = exponent_ - other.exponent_; + EnsureCapacity(used_digits_ + zero_digits); + for (int i = used_digits_ - 1; i >= 0; --i) { + bigits_[i + zero_digits] = bigits_[i]; + } + for (int i = 0; i < zero_digits; ++i) { + bigits_[i] = 0; + } + used_digits_ += zero_digits; + exponent_ -= zero_digits; + ASSERT(used_digits_ >= 0); + ASSERT(exponent_ >= 0); + } +} + + +void Bignum::BigitsShiftLeft(int shift_amount) { + ASSERT(shift_amount < kBigitSize); + ASSERT(shift_amount >= 0); + Chunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount); + bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask; + carry = new_carry; + } + if (carry != 0) { + bigits_[used_digits_] = carry; + used_digits_++; + } +} + + +void Bignum::SubtractTimes(const Bignum& other, int factor) { + ASSERT(exponent_ <= other.exponent_); + if (factor < 3) { + for (int i = 0; i < factor; ++i) { + SubtractBignum(other); + } + return; + } + Chunk borrow = 0; + int exponent_diff = other.exponent_ - exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + DoubleChunk product = static_cast(factor) * other.bigits_[i]; + DoubleChunk remove = borrow + product; + Chunk difference = bigits_[i + exponent_diff] - (remove & kBigitMask); + bigits_[i + exponent_diff] = difference & kBigitMask; + borrow = static_cast((difference >> (kChunkSize - 1)) + + (remove >> kBigitSize)); + } + for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) { + if (borrow == 0) return; + Chunk difference = bigits_[i] - borrow; + bigits_[i] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + ++i; + } + Clamp(); +} + + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/bignum.h b/src/3rdparty/double-conversion/bignum.h new file mode 100644 index 0000000000..5ec3544f57 --- /dev/null +++ b/src/3rdparty/double-conversion/bignum.h @@ -0,0 +1,145 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_BIGNUM_H_ +#define DOUBLE_CONVERSION_BIGNUM_H_ + +#include "utils.h" + +namespace double_conversion { + +class Bignum { + public: + // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately. + // This bignum can encode much bigger numbers, since it contains an + // exponent. + static const int kMaxSignificantBits = 3584; + + Bignum(); + void AssignUInt16(uint16_t value); + void AssignUInt64(uint64_t value); + void AssignBignum(const Bignum& other); + + void AssignDecimalString(Vector value); + void AssignHexString(Vector value); + + void AssignPowerUInt16(uint16_t base, int exponent); + + void AddUInt16(uint16_t operand); + void AddUInt64(uint64_t operand); + void AddBignum(const Bignum& other); + // Precondition: this >= other. + void SubtractBignum(const Bignum& other); + + void Square(); + void ShiftLeft(int shift_amount); + void MultiplyByUInt32(uint32_t factor); + void MultiplyByUInt64(uint64_t factor); + void MultiplyByPowerOfTen(int exponent); + void Times10() { return MultiplyByUInt32(10); } + // Pseudocode: + // int result = this / other; + // this = this % other; + // In the worst case this function is in O(this/other). + uint16_t DivideModuloIntBignum(const Bignum& other); + + bool ToHexString(char* buffer, int buffer_size) const; + + // Returns + // -1 if a < b, + // 0 if a == b, and + // +1 if a > b. + static int Compare(const Bignum& a, const Bignum& b); + static bool Equal(const Bignum& a, const Bignum& b) { + return Compare(a, b) == 0; + } + static bool LessEqual(const Bignum& a, const Bignum& b) { + return Compare(a, b) <= 0; + } + static bool Less(const Bignum& a, const Bignum& b) { + return Compare(a, b) < 0; + } + // Returns Compare(a + b, c); + static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c); + // Returns a + b == c + static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) == 0; + } + // Returns a + b <= c + static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) <= 0; + } + // Returns a + b < c + static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) < 0; + } + private: + typedef uint32_t Chunk; + typedef uint64_t DoubleChunk; + + static const int kChunkSize = sizeof(Chunk) * 8; + static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8; + // With bigit size of 28 we loose some bits, but a double still fits easily + // into two chunks, and more importantly we can use the Comba multiplication. + static const int kBigitSize = 28; + static const Chunk kBigitMask = (1 << kBigitSize) - 1; + // Every instance allocates kBigitLength chunks on the stack. Bignums cannot + // grow. There are no checks if the stack-allocated space is sufficient. + static const int kBigitCapacity = kMaxSignificantBits / kBigitSize; + + void EnsureCapacity(int size) { + if (size > kBigitCapacity) { + UNREACHABLE(); + } + } + void Align(const Bignum& other); + void Clamp(); + bool IsClamped() const; + void Zero(); + // Requires this to have enough capacity (no tests done). + // Updates used_digits_ if necessary. + // shift_amount must be < kBigitSize. + void BigitsShiftLeft(int shift_amount); + // BigitLength includes the "hidden" digits encoded in the exponent. + int BigitLength() const { return used_digits_ + exponent_; } + Chunk BigitAt(int index) const; + void SubtractTimes(const Bignum& other, int factor); + + Chunk bigits_buffer_[kBigitCapacity]; + // A vector backed by bigits_buffer_. This way accesses to the array are + // checked for out-of-bounds errors. + Vector bigits_; + int used_digits_; + // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize). + int exponent_; + + DISALLOW_COPY_AND_ASSIGN(Bignum); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_BIGNUM_H_ diff --git a/src/3rdparty/double-conversion/cached-powers.cc b/src/3rdparty/double-conversion/cached-powers.cc new file mode 100644 index 0000000000..c676429194 --- /dev/null +++ b/src/3rdparty/double-conversion/cached-powers.cc @@ -0,0 +1,175 @@ +// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include + +#include "utils.h" + +#include "cached-powers.h" + +namespace double_conversion { + +struct CachedPower { + uint64_t significand; + int16_t binary_exponent; + int16_t decimal_exponent; +}; + +static const CachedPower kCachedPowers[] = { + {UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348}, + {UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340}, + {UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332}, + {UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324}, + {UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316}, + {UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308}, + {UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300}, + {UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292}, + {UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284}, + {UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276}, + {UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268}, + {UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260}, + {UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252}, + {UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244}, + {UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236}, + {UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228}, + {UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220}, + {UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212}, + {UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204}, + {UINT64_2PART_C(0xef340a98, 172aace5), -715, -196}, + {UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188}, + {UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180}, + {UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172}, + {UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164}, + {UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156}, + {UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148}, + {UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140}, + {UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132}, + {UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124}, + {UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116}, + {UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108}, + {UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100}, + {UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92}, + {UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84}, + {UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76}, + {UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68}, + {UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60}, + {UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52}, + {UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44}, + {UINT64_2PART_C(0xaa242499, 697392d3), -183, -36}, + {UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28}, + {UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20}, + {UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12}, + {UINT64_2PART_C(0xd1b71758, e219652c), -77, -4}, + {UINT64_2PART_C(0x9c400000, 00000000), -50, 4}, + {UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12}, + {UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20}, + {UINT64_2PART_C(0x813f3978, f8940984), 30, 28}, + {UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36}, + {UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44}, + {UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52}, + {UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60}, + {UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68}, + {UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76}, + {UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84}, + {UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92}, + {UINT64_2PART_C(0x924d692c, a61be758), 269, 100}, + {UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108}, + {UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116}, + {UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124}, + {UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132}, + {UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140}, + {UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148}, + {UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156}, + {UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164}, + {UINT64_2PART_C(0xa59bc234, db398c25), 508, 172}, + {UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180}, + {UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188}, + {UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196}, + {UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204}, + {UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212}, + {UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220}, + {UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228}, + {UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236}, + {UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244}, + {UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252}, + {UINT64_2PART_C(0xd01fef10, a657842c), 800, 260}, + {UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268}, + {UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276}, + {UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284}, + {UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292}, + {UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300}, + {UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308}, + {UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316}, + {UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324}, + {UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332}, + {UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340}, +}; + +static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers); +static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent. +static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) +// Difference between the decimal exponents in the table above. +const int PowersOfTenCache::kDecimalExponentDistance = 8; +const int PowersOfTenCache::kMinDecimalExponent = -348; +const int PowersOfTenCache::kMaxDecimalExponent = 340; + +void PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent) { + int kQ = DiyFp::kSignificandSize; + double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10); + int foo = kCachedPowersOffset; + int index = + (foo + static_cast(k) - 1) / kDecimalExponentDistance + 1; + ASSERT(0 <= index && index < kCachedPowersLength); + CachedPower cached_power = kCachedPowers[index]; + ASSERT(min_exponent <= cached_power.binary_exponent); + ASSERT(cached_power.binary_exponent <= max_exponent); + *decimal_exponent = cached_power.decimal_exponent; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); +} + + +void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent) { + ASSERT(kMinDecimalExponent <= requested_exponent); + ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance); + int index = + (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance; + CachedPower cached_power = kCachedPowers[index]; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); + *found_exponent = cached_power.decimal_exponent; + ASSERT(*found_exponent <= requested_exponent); + ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance); +} + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/cached-powers.h b/src/3rdparty/double-conversion/cached-powers.h new file mode 100644 index 0000000000..61a50614cf --- /dev/null +++ b/src/3rdparty/double-conversion/cached-powers.h @@ -0,0 +1,64 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_CACHED_POWERS_H_ +#define DOUBLE_CONVERSION_CACHED_POWERS_H_ + +#include "diy-fp.h" + +namespace double_conversion { + +class PowersOfTenCache { + public: + + // Not all powers of ten are cached. The decimal exponent of two neighboring + // cached numbers will differ by kDecimalExponentDistance. + static const int kDecimalExponentDistance; + + static const int kMinDecimalExponent; + static const int kMaxDecimalExponent; + + // Returns a cached power-of-ten with a binary exponent in the range + // [min_exponent; max_exponent] (boundaries included). + static void GetCachedPowerForBinaryExponentRange(int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent); + + // Returns a cached power of ten x ~= 10^k such that + // k <= decimal_exponent < k + kCachedPowersDecimalDistance. + // The given decimal_exponent must satisfy + // kMinDecimalExponent <= requested_exponent, and + // requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance. + static void GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_CACHED_POWERS_H_ diff --git a/src/3rdparty/double-conversion/diy-fp.cc b/src/3rdparty/double-conversion/diy-fp.cc new file mode 100644 index 0000000000..ddd1891b16 --- /dev/null +++ b/src/3rdparty/double-conversion/diy-fp.cc @@ -0,0 +1,57 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +#include "diy-fp.h" +#include "utils.h" + +namespace double_conversion { + +void DiyFp::Multiply(const DiyFp& other) { + // Simply "emulates" a 128 bit multiplication. + // However: the resulting number only contains 64 bits. The least + // significant 64 bits are only used for rounding the most significant 64 + // bits. + const uint64_t kM32 = 0xFFFFFFFFU; + uint64_t a = f_ >> 32; + uint64_t b = f_ & kM32; + uint64_t c = other.f_ >> 32; + uint64_t d = other.f_ & kM32; + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32); + // By adding 1U << 31 to tmp we round the final result. + // Halfway cases will be round up. + tmp += 1U << 31; + uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + e_ += other.e_ + 64; + f_ = result_f; +} + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/diy-fp.h b/src/3rdparty/double-conversion/diy-fp.h new file mode 100644 index 0000000000..9dcf8fbdba --- /dev/null +++ b/src/3rdparty/double-conversion/diy-fp.h @@ -0,0 +1,118 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_DIY_FP_H_ +#define DOUBLE_CONVERSION_DIY_FP_H_ + +#include "utils.h" + +namespace double_conversion { + +// This "Do It Yourself Floating Point" class implements a floating-point number +// with a uint64 significand and an int exponent. Normalized DiyFp numbers will +// have the most significant bit of the significand set. +// Multiplication and Subtraction do not normalize their results. +// DiyFp are not designed to contain special doubles (NaN and Infinity). +class DiyFp { + public: + static const int kSignificandSize = 64; + + DiyFp() : f_(0), e_(0) {} + DiyFp(uint64_t f, int e) : f_(f), e_(e) {} + + // this = this - other. + // The exponents of both numbers must be the same and the significand of this + // must be bigger than the significand of other. + // The result will not be normalized. + void Subtract(const DiyFp& other) { + ASSERT(e_ == other.e_); + ASSERT(f_ >= other.f_); + f_ -= other.f_; + } + + // Returns a - b. + // The exponents of both numbers must be the same and this must be bigger + // than other. The result will not be normalized. + static DiyFp Minus(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Subtract(b); + return result; + } + + + // this = this * other. + void Multiply(const DiyFp& other); + + // returns a * b; + static DiyFp Times(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Multiply(b); + return result; + } + + void Normalize() { + ASSERT(f_ != 0); + uint64_t f = f_; + int e = e_; + + // This method is mainly called for normalizing boundaries. In general + // boundaries need to be shifted by 10 bits. We thus optimize for this case. + const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000); + while ((f & k10MSBits) == 0) { + f <<= 10; + e -= 10; + } + while ((f & kUint64MSB) == 0) { + f <<= 1; + e--; + } + f_ = f; + e_ = e; + } + + static DiyFp Normalize(const DiyFp& a) { + DiyFp result = a; + result.Normalize(); + return result; + } + + uint64_t f() const { return f_; } + int e() const { return e_; } + + void set_f(uint64_t new_value) { f_ = new_value; } + void set_e(int new_value) { e_ = new_value; } + + private: + static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000); + + uint64_t f_; + int e_; +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_DIY_FP_H_ diff --git a/src/3rdparty/double-conversion/double-conversion.cc b/src/3rdparty/double-conversion/double-conversion.cc new file mode 100644 index 0000000000..a79fe92d22 --- /dev/null +++ b/src/3rdparty/double-conversion/double-conversion.cc @@ -0,0 +1,889 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include + +#include "double-conversion.h" + +#include "bignum-dtoa.h" +#include "fast-dtoa.h" +#include "fixed-dtoa.h" +#include "ieee.h" +#include "strtod.h" +#include "utils.h" + +namespace double_conversion { + +const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() { + int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN; + static DoubleToStringConverter converter(flags, + "Infinity", + "NaN", + 'e', + -6, 21, + 6, 0); + return converter; +} + + +bool DoubleToStringConverter::HandleSpecialValues( + double value, + StringBuilder* result_builder) const { + Double double_inspect(value); + if (double_inspect.IsInfinite()) { + if (infinity_symbol_ == NULL) return false; + if (value < 0) { + result_builder->AddCharacter('-'); + } + result_builder->AddString(infinity_symbol_); + return true; + } + if (double_inspect.IsNan()) { + if (nan_symbol_ == NULL) return false; + result_builder->AddString(nan_symbol_); + return true; + } + return false; +} + + +void DoubleToStringConverter::CreateExponentialRepresentation( + const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const { + ASSERT(length != 0); + result_builder->AddCharacter(decimal_digits[0]); + if (length != 1) { + result_builder->AddCharacter('.'); + result_builder->AddSubstring(&decimal_digits[1], length-1); + } + result_builder->AddCharacter(exponent_character_); + if (exponent < 0) { + result_builder->AddCharacter('-'); + exponent = -exponent; + } else { + if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) { + result_builder->AddCharacter('+'); + } + } + if (exponent == 0) { + result_builder->AddCharacter('0'); + return; + } + ASSERT(exponent < 1e4); + const int kMaxExponentLength = 5; + char buffer[kMaxExponentLength + 1]; + buffer[kMaxExponentLength] = '\0'; + int first_char_pos = kMaxExponentLength; + while (exponent > 0) { + buffer[--first_char_pos] = '0' + (exponent % 10); + exponent /= 10; + } + result_builder->AddSubstring(&buffer[first_char_pos], + kMaxExponentLength - first_char_pos); +} + + +void DoubleToStringConverter::CreateDecimalRepresentation( + const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const { + // Create a representation that is padded with zeros if needed. + if (decimal_point <= 0) { + // "0.00000decimal_rep". + result_builder->AddCharacter('0'); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', -decimal_point); + ASSERT(length <= digits_after_point - (-decimal_point)); + result_builder->AddSubstring(decimal_digits, length); + int remaining_digits = digits_after_point - (-decimal_point) - length; + result_builder->AddPadding('0', remaining_digits); + } + } else if (decimal_point >= length) { + // "decimal_rep0000.00000" or "decimal_rep.0000" + result_builder->AddSubstring(decimal_digits, length); + result_builder->AddPadding('0', decimal_point - length); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', digits_after_point); + } + } else { + // "decima.l_rep000" + ASSERT(digits_after_point > 0); + result_builder->AddSubstring(decimal_digits, decimal_point); + result_builder->AddCharacter('.'); + ASSERT(length - decimal_point <= digits_after_point); + result_builder->AddSubstring(&decimal_digits[decimal_point], + length - decimal_point); + int remaining_digits = digits_after_point - (length - decimal_point); + result_builder->AddPadding('0', remaining_digits); + } + if (digits_after_point == 0) { + if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) { + result_builder->AddCharacter('.'); + } + if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) { + result_builder->AddCharacter('0'); + } + } +} + + +bool DoubleToStringConverter::ToShortestIeeeNumber( + double value, + StringBuilder* result_builder, + DoubleToStringConverter::DtoaMode mode) const { + ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE); + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + int decimal_point; + bool sign; + const int kDecimalRepCapacity = kBase10MaximalLength + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = (flags_ & UNIQUE_ZERO) != 0; + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + if ((decimal_in_shortest_low_ <= exponent) && + (exponent < decimal_in_shortest_high_)) { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, + decimal_point, + Max(0, decimal_rep_length - decimal_point), + result_builder); + } else { + CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, + result_builder); + } + return true; +} + + +bool DoubleToStringConverter::ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const { + ASSERT(kMaxFixedDigitsBeforePoint == 60); + const double kFirstNonFixed = 1e60; + + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits > kMaxFixedDigitsAfterPoint) return false; + if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false; + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add space for the '\0' byte. + const int kDecimalRepCapacity = + kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + DoubleToAscii(value, FIXED, requested_digits, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + requested_digits, result_builder); + return true; +} + + +bool DoubleToStringConverter::ToExponential( + double value, + int requested_digits, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits < -1) return false; + if (requested_digits > kMaxExponentialDigits) return false; + + int decimal_point; + bool sign; + // Add space for digit before the decimal point and the '\0' character. + const int kDecimalRepCapacity = kMaxExponentialDigits + 2; + ASSERT(kDecimalRepCapacity > kBase10MaximalLength); + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + if (requested_digits == -1) { + DoubleToAscii(value, SHORTEST, 0, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + } else { + DoubleToAscii(value, PRECISION, requested_digits + 1, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + ASSERT(decimal_rep_length <= requested_digits + 1); + + for (int i = decimal_rep_length; i < requested_digits + 1; ++i) { + decimal_rep[i] = '0'; + } + decimal_rep_length = requested_digits + 1; + } + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + CreateExponentialRepresentation(decimal_rep, + decimal_rep_length, + exponent, + result_builder); + return true; +} + + +bool DoubleToStringConverter::ToPrecision(double value, + int precision, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { + return false; + } + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add one for the terminating null character. + const int kDecimalRepCapacity = kMaxPrecisionDigits + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, PRECISION, precision, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + ASSERT(decimal_rep_length <= precision); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + // The exponent if we print the number as x.xxeyyy. That is with the + // decimal point after the first digit. + int exponent = decimal_point - 1; + + int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0; + if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) || + (decimal_point - precision + extra_zero > + max_trailing_padding_zeroes_in_precision_mode_)) { + // Fill buffer to contain 'precision' digits. + // Usually the buffer is already at the correct length, but 'DoubleToAscii' + // is allowed to return less characters. + for (int i = decimal_rep_length; i < precision; ++i) { + decimal_rep[i] = '0'; + } + + CreateExponentialRepresentation(decimal_rep, + precision, + exponent, + result_builder); + } else { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + Max(0, precision - decimal_point), + result_builder); + } + return true; +} + + +static BignumDtoaMode DtoaToBignumDtoaMode( + DoubleToStringConverter::DtoaMode dtoa_mode) { + switch (dtoa_mode) { + case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST; + case DoubleToStringConverter::SHORTEST_SINGLE: + return BIGNUM_DTOA_SHORTEST_SINGLE; + case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED; + case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION; + default: + UNREACHABLE(); + return BIGNUM_DTOA_SHORTEST; // To silence compiler. + } +} + + +void DoubleToStringConverter::DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point) { + Vector vector(buffer, buffer_length); + ASSERT(!Double(v).IsSpecial()); + ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0); + + if (Double(v).Sign() < 0) { + *sign = true; + v = -v; + } else { + *sign = false; + } + + if (mode == PRECISION && requested_digits == 0) { + vector[0] = '\0'; + *length = 0; + return; + } + + if (v == 0) { + vector[0] = '0'; + vector[1] = '\0'; + *length = 1; + *point = 1; + return; + } + + bool fast_worked; + switch (mode) { + case SHORTEST: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point); + break; + case SHORTEST_SINGLE: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0, + vector, length, point); + break; + case FIXED: + fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point); + break; + case PRECISION: + fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, + vector, length, point); + break; + default: + UNREACHABLE(); + fast_worked = false; + } + if (fast_worked) return; + + // If the fast dtoa didn't succeed use the slower bignum version. + BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); + BignumDtoa(v, bignum_mode, requested_digits, vector, length, point); + vector[*length] = '\0'; +} + + +// Consumes the given substring from the iterator. +// Returns false, if the substring does not match. +static bool ConsumeSubString(const char** current, + const char* end, + const char* substring) { + ASSERT(**current == *substring); + for (substring++; *substring != '\0'; substring++) { + ++*current; + if (*current == end || **current != *substring) return false; + } + ++*current; + return true; +} + + +// Maximum number of significant digits in decimal representation. +// The longest possible double in decimal representation is +// (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074 +// (768 digits). If we parse a number whose first digits are equal to a +// mean of 2 adjacent doubles (that could have up to 769 digits) the result +// must be rounded to the bigger one unless the tail consists of zeros, so +// we don't need to preserve all the digits. +const int kMaxSignificantDigits = 772; + + +// Returns true if a nonspace found and false if the end has reached. +static inline bool AdvanceToNonspace(const char** current, const char* end) { + while (*current != end) { + if (**current != ' ') return true; + ++*current; + } + return false; +} + + +static bool isDigit(int x, int radix) { + return (x >= '0' && x <= '9' && x < '0' + radix) + || (radix > 10 && x >= 'a' && x < 'a' + radix - 10) + || (radix > 10 && x >= 'A' && x < 'A' + radix - 10); +} + + +static double SignedZero(bool sign) { + return sign ? -0.0 : 0.0; +} + + +// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end. +template +static double RadixStringToIeee(const char* current, + const char* end, + bool sign, + bool allow_trailing_junk, + double junk_string_value, + bool read_as_double, + const char** trailing_pointer) { + ASSERT(current != end); + + const int kDoubleSize = Double::kSignificandSize; + const int kSingleSize = Single::kSignificandSize; + const int kSignificandSize = read_as_double? kDoubleSize: kSingleSize; + + // Skip leading 0s. + while (*current == '0') { + ++current; + if (current == end) { + *trailing_pointer = end; + return SignedZero(sign); + } + } + + int64_t number = 0; + int exponent = 0; + const int radix = (1 << radix_log_2); + + do { + int digit; + if (*current >= '0' && *current <= '9' && *current < '0' + radix) { + digit = static_cast(*current) - '0'; + } else if (radix > 10 && *current >= 'a' && *current < 'a' + radix - 10) { + digit = static_cast(*current) - 'a' + 10; + } else if (radix > 10 && *current >= 'A' && *current < 'A' + radix - 10) { + digit = static_cast(*current) - 'A' + 10; + } else { + if (allow_trailing_junk || !AdvanceToNonspace(¤t, end)) { + break; + } else { + return junk_string_value; + } + } + + number = number * radix + digit; + int overflow = static_cast(number >> kSignificandSize); + if (overflow != 0) { + // Overflow occurred. Need to determine which direction to round the + // result. + int overflow_bits_count = 1; + while (overflow > 1) { + overflow_bits_count++; + overflow >>= 1; + } + + int dropped_bits_mask = ((1 << overflow_bits_count) - 1); + int dropped_bits = static_cast(number) & dropped_bits_mask; + number >>= overflow_bits_count; + exponent = overflow_bits_count; + + bool zero_tail = true; + while (true) { + ++current; + if (current == end || !isDigit(*current, radix)) break; + zero_tail = zero_tail && *current == '0'; + exponent += radix_log_2; + } + + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value; + } + + int middle_value = (1 << (overflow_bits_count - 1)); + if (dropped_bits > middle_value) { + number++; // Rounding up. + } else if (dropped_bits == middle_value) { + // Rounding to even to consistency with decimals: half-way case rounds + // up if significant part is odd and down otherwise. + if ((number & 1) != 0 || !zero_tail) { + number++; // Rounding up. + } + } + + // Rounding up may cause overflow. + if ((number & ((int64_t)1 << kSignificandSize)) != 0) { + exponent++; + number >>= 1; + } + break; + } + ++current; + } while (current != end); + + ASSERT(number < ((int64_t)1 << kSignificandSize)); + ASSERT(static_cast(static_cast(number)) == number); + + *trailing_pointer = current; + + if (exponent == 0) { + if (sign) { + if (number == 0) return -0.0; + number = -number; + } + return static_cast(number); + } + + ASSERT(number != 0); + return Double(DiyFp(number, exponent)).value(); +} + + +double StringToDoubleConverter::StringToIeee( + const char* input, + int length, + int* processed_characters_count, + bool read_as_double) { + const char* current = input; + const char* end = input + length; + + *processed_characters_count = 0; + + const bool allow_trailing_junk = (flags_ & ALLOW_TRAILING_JUNK) != 0; + const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0; + const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0; + const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0; + + // To make sure that iterator dereferencing is valid the following + // convention is used: + // 1. Each '++current' statement is followed by check for equality to 'end'. + // 2. If AdvanceToNonspace returned false then current == end. + // 3. If 'current' becomes equal to 'end' the function returns or goes to + // 'parsing_done'. + // 4. 'current' is not dereferenced after the 'parsing_done' label. + // 5. Code before 'parsing_done' may rely on 'current != end'. + if (current == end) return empty_string_value_; + + if (allow_leading_spaces || allow_trailing_spaces) { + if (!AdvanceToNonspace(¤t, end)) { + *processed_characters_count = current - input; + return empty_string_value_; + } + if (!allow_leading_spaces && (input != current)) { + // No leading spaces allowed, but AdvanceToNonspace moved forward. + return junk_string_value_; + } + } + + // The longest form of simplified number is: "-.1eXXX\0". + const int kBufferSize = kMaxSignificantDigits + 10; + char buffer[kBufferSize]; // NOLINT: size is known at compile time. + int buffer_pos = 0; + + // Exponent will be adjusted if insignificant digits of the integer part + // or insignificant leading zeros of the fractional part are dropped. + int exponent = 0; + int significant_digits = 0; + int insignificant_digits = 0; + bool nonzero_digit_dropped = false; + + bool sign = false; + + if (*current == '+' || *current == '-') { + sign = (*current == '-'); + ++current; + const char* next_non_space = current; + // Skip following spaces (if allowed). + if (!AdvanceToNonspace(&next_non_space, end)) return junk_string_value_; + if (!allow_spaces_after_sign && (current != next_non_space)) { + return junk_string_value_; + } + current = next_non_space; + } + + if (infinity_symbol_ != NULL) { + if (*current == infinity_symbol_[0]) { + if (!ConsumeSubString(¤t, end, infinity_symbol_)) { + return junk_string_value_; + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + + ASSERT(buffer_pos == 0); + *processed_characters_count = current - input; + return sign ? -Double::Infinity() : Double::Infinity(); + } + } + + if (nan_symbol_ != NULL) { + if (*current == nan_symbol_[0]) { + if (!ConsumeSubString(¤t, end, nan_symbol_)) { + return junk_string_value_; + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + + ASSERT(buffer_pos == 0); + *processed_characters_count = current - input; + return sign ? -Double::NaN() : Double::NaN(); + } + } + + bool leading_zero = false; + if (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = current - input; + return SignedZero(sign); + } + + leading_zero = true; + + // It could be hexadecimal value. + if ((flags_ & ALLOW_HEX) && (*current == 'x' || *current == 'X')) { + ++current; + if (current == end || !isDigit(*current, 16)) { + return junk_string_value_; // "0x". + } + + const char* tail_pointer = NULL; + double result = RadixStringToIeee<4>(current, + end, + sign, + allow_trailing_junk, + junk_string_value_, + read_as_double, + &tail_pointer); + if (tail_pointer != NULL) { + if (allow_trailing_spaces) AdvanceToNonspace(&tail_pointer, end); + *processed_characters_count = tail_pointer - input; + } + return result; + } + + // Ignore leading zeros in the integer part. + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = current - input; + return SignedZero(sign); + } + } + } + + bool octal = leading_zero && (flags_ & ALLOW_OCTALS) != 0; + + // Copy significant digits of the integer part (if any) to the buffer. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast(*current); + significant_digits++; + // Will later check if it's an octal in the buffer. + } else { + insignificant_digits++; // Move the digit into the exponential part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + octal = octal && *current < '8'; + ++current; + if (current == end) goto parsing_done; + } + + if (significant_digits == 0) { + octal = false; + } + + if (*current == '.') { + if (octal && !allow_trailing_junk) return junk_string_value_; + if (octal) goto parsing_done; + + ++current; + if (current == end) { + if (significant_digits == 0 && !leading_zero) { + return junk_string_value_; + } else { + goto parsing_done; + } + } + + if (significant_digits == 0) { + // octal = false; + // Integer part consists of 0 or is absent. Significant digits start after + // leading zeros (if any). + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = current - input; + return SignedZero(sign); + } + exponent--; // Move this 0 into the exponent. + } + } + + // There is a fractional part. + // We don't emit a '.', but adjust the exponent instead. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast(*current); + significant_digits++; + exponent--; + } else { + // Ignore insignificant digits in the fractional part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + ++current; + if (current == end) goto parsing_done; + } + } + + if (!leading_zero && exponent == 0 && significant_digits == 0) { + // If leading_zeros is true then the string contains zeros. + // If exponent < 0 then string was [+-]\.0*... + // If significant_digits != 0 the string is not equal to 0. + // Otherwise there are no digits in the string. + return junk_string_value_; + } + + // Parse exponential part. + if (*current == 'e' || *current == 'E') { + if (octal && !allow_trailing_junk) return junk_string_value_; + if (octal) goto parsing_done; + ++current; + if (current == end) { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + char sign = '+'; + if (*current == '+' || *current == '-') { + sign = static_cast(*current); + ++current; + if (current == end) { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + } + + if (current == end || *current < '0' || *current > '9') { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + + const int max_exponent = INT_MAX / 2; + ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); + int num = 0; + do { + // Check overflow. + int digit = *current - '0'; + if (num >= max_exponent / 10 + && !(num == max_exponent / 10 && digit <= max_exponent % 10)) { + num = max_exponent; + } else { + num = num * 10 + digit; + } + ++current; + } while (current != end && *current >= '0' && *current <= '9'); + + exponent += (sign == '-' ? -num : num); + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + if (allow_trailing_spaces) { + AdvanceToNonspace(¤t, end); + } + + parsing_done: + exponent += insignificant_digits; + + if (octal) { + double result; + const char* tail_pointer = NULL; + result = RadixStringToIeee<3>(buffer, + buffer + buffer_pos, + sign, + allow_trailing_junk, + junk_string_value_, + read_as_double, + &tail_pointer); + ASSERT(tail_pointer != NULL); + *processed_characters_count = current - input; + return result; + } + + if (nonzero_digit_dropped) { + buffer[buffer_pos++] = '1'; + exponent--; + } + + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos] = '\0'; + + double converted; + if (read_as_double) { + converted = Strtod(Vector(buffer, buffer_pos), exponent); + } else { + converted = Strtof(Vector(buffer, buffer_pos), exponent); + } + *processed_characters_count = current - input; + return sign? -converted: converted; +} + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/double-conversion.h b/src/3rdparty/double-conversion/double-conversion.h new file mode 100644 index 0000000000..f98edae75a --- /dev/null +++ b/src/3rdparty/double-conversion/double-conversion.h @@ -0,0 +1,536 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ +#define DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ + +#include "utils.h" + +namespace double_conversion { + +class DoubleToStringConverter { + public: + // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint + // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the + // function returns false. + static const int kMaxFixedDigitsBeforePoint = 60; + static const int kMaxFixedDigitsAfterPoint = 60; + + // When calling ToExponential with a requested_digits + // parameter > kMaxExponentialDigits then the function returns false. + static const int kMaxExponentialDigits = 120; + + // When calling ToPrecision with a requested_digits + // parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits + // then the function returns false. + static const int kMinPrecisionDigits = 1; + static const int kMaxPrecisionDigits = 120; + + enum Flags { + NO_FLAGS = 0, + EMIT_POSITIVE_EXPONENT_SIGN = 1, + EMIT_TRAILING_DECIMAL_POINT = 2, + EMIT_TRAILING_ZERO_AFTER_POINT = 4, + UNIQUE_ZERO = 8 + }; + + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent + // form, emits a '+' for positive exponents. Example: 1.2e+2. + // - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is + // converted into decimal format then a trailing decimal point is appended. + // Example: 2345.0 is converted to "2345.". + // - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point + // emits a trailing '0'-character. This flag requires the + // EXMIT_TRAILING_DECIMAL_POINT flag. + // Example: 2345.0 is converted to "2345.0". + // - UNIQUE_ZERO: "-0.0" is converted to "0.0". + // + // Infinity symbol and nan_symbol provide the string representation for these + // special values. If the string is NULL and the special value is encountered + // then the conversion functions return false. + // + // The exponent_character is used in exponential representations. It is + // usually 'e' or 'E'. + // + // When converting to the shortest representation the converter will + // represent input numbers in decimal format if they are in the interval + // [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[ + // (lower boundary included, greater boundary excluded). + // Example: with decimal_in_shortest_low = -6 and + // decimal_in_shortest_high = 21: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // When converting to precision mode the converter may add + // max_leading_padding_zeroes before returning the number in exponential + // format. + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarily the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + DoubleToStringConverter(int flags, + const char* infinity_symbol, + const char* nan_symbol, + char exponent_character, + int decimal_in_shortest_low, + int decimal_in_shortest_high, + int max_leading_padding_zeroes_in_precision_mode, + int max_trailing_padding_zeroes_in_precision_mode) + : flags_(flags), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol), + exponent_character_(exponent_character), + decimal_in_shortest_low_(decimal_in_shortest_low), + decimal_in_shortest_high_(decimal_in_shortest_high), + max_leading_padding_zeroes_in_precision_mode_( + max_leading_padding_zeroes_in_precision_mode), + max_trailing_padding_zeroes_in_precision_mode_( + max_trailing_padding_zeroes_in_precision_mode) { + // When 'trailing zero after the point' is set, then 'trailing point' + // must be set too. + ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) || + !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0)); + } + + // Returns a converter following the EcmaScript specification. + static const DoubleToStringConverter& EcmaScriptConverter(); + + // Computes the shortest string of digits that correctly represent the input + // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high + // (see constructor) it then either returns a decimal representation, or an + // exponential representation. + // Example with decimal_in_shortest_low = -6, + // decimal_in_shortest_high = 21, + // EMIT_POSITIVE_EXPONENT_SIGN activated, and + // EMIT_TRAILING_DECIMAL_POINT deactived: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // Note: the conversion may round the output if the returned string + // is accurate enough to uniquely identify the input-number. + // For example the most precise representation of the double 9e59 equals + // "899999999999999918767229449717619953810131273674690656206848", but + // the converter will return the shorter (but still correct) "9e59". + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except when the input value is special and no infinity_symbol or + // nan_symbol has been given to the constructor. + bool ToShortest(double value, StringBuilder* result_builder) const { + return ToShortestIeeeNumber(value, result_builder, SHORTEST); + } + + // Same as ToShortest, but for single-precision floats. + bool ToShortestSingle(float value, StringBuilder* result_builder) const { + return ToShortestIeeeNumber(value, result_builder, SHORTEST_SINGLE); + } + + + // Computes a decimal representation with a fixed number of digits after the + // decimal point. The last emitted digit is rounded. + // + // Examples: + // ToFixed(3.12, 1) -> "3.1" + // ToFixed(3.1415, 3) -> "3.142" + // ToFixed(1234.56789, 4) -> "1234.5679" + // ToFixed(1.23, 5) -> "1.23000" + // ToFixed(0.1, 4) -> "0.1000" + // ToFixed(1e30, 2) -> "1000000000000000019884624838656.00" + // ToFixed(0.1, 30) -> "0.100000000000000005551115123126" + // ToFixed(0.1, 17) -> "0.10000000000000001" + // + // If requested_digits equals 0, then the tail of the result depends on + // the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples, for requested_digits == 0, + // let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be + // - false and false: then 123.45 -> 123 + // 0.678 -> 1 + // - true and false: then 123.45 -> 123. + // 0.678 -> 1. + // - true and true: then 123.45 -> 123.0 + // 0.678 -> 1.0 + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'value' > 10^kMaxFixedDigitsBeforePoint, or + // - 'requested_digits' > kMaxFixedDigitsAfterPoint. + // The last two conditions imply that the result will never contain more than + // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters + // (one additional character for the sign, and one for the decimal point). + bool ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const; + + // Computes a representation in exponential format with requested_digits + // after the decimal point. The last emitted digit is rounded. + // If requested_digits equals -1, then the shortest exponential representation + // is computed. + // + // Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and + // exponent_character set to 'e'. + // ToExponential(3.12, 1) -> "3.1e0" + // ToExponential(5.0, 3) -> "5.000e0" + // ToExponential(0.001, 2) -> "1.00e-3" + // ToExponential(3.1415, -1) -> "3.1415e0" + // ToExponential(3.1415, 4) -> "3.1415e0" + // ToExponential(3.1415, 3) -> "3.142e0" + // ToExponential(123456789000000, 3) -> "1.235e14" + // ToExponential(1000000000000000019884624838656.0, -1) -> "1e30" + // ToExponential(1000000000000000019884624838656.0, 32) -> + // "1.00000000000000001988462483865600e30" + // ToExponential(1234, 0) -> "1e3" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'requested_digits' > kMaxExponentialDigits. + // The last condition implies that the result will never contain more than + // kMaxExponentialDigits + 8 characters (the sign, the digit before the + // decimal point, the decimal point, the exponent character, the + // exponent's sign, and at most 3 exponent digits). + bool ToExponential(double value, + int requested_digits, + StringBuilder* result_builder) const; + + // Computes 'precision' leading digits of the given 'value' and returns them + // either in exponential or decimal format, depending on + // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the + // constructor). + // The last computed digit is rounded. + // + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarily the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no + // EMIT_TRAILING_ZERO_AFTER_POINT: + // ToPrecision(123450.0, 6) -> "123450" + // ToPrecision(123450.0, 5) -> "123450" + // ToPrecision(123450.0, 4) -> "123500" + // ToPrecision(123450.0, 3) -> "123000" + // ToPrecision(123450.0, 2) -> "1.2e5" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - precision < kMinPericisionDigits + // - precision > kMaxPrecisionDigits + // The last condition implies that the result will never contain more than + // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the + // exponent character, the exponent's sign, and at most 3 exponent digits). + bool ToPrecision(double value, + int precision, + StringBuilder* result_builder) const; + + enum DtoaMode { + // Produce the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate + // but correct) 0.3. + SHORTEST, + // Same as SHORTEST, but for single-precision floats. + SHORTEST_SINGLE, + // Produce a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + FIXED, + // Fixed number of digits (independent of the decimal point). + PRECISION + }; + + // The maximal number of digits that are needed to emit a double in base 10. + // A higher precision can be achieved by using more digits, but the shortest + // accurate representation of any double will never use more digits than + // kBase10MaximalLength. + // Note that DoubleToAscii null-terminates its input. So the given buffer + // should be at least kBase10MaximalLength + 1 characters long. + static const int kBase10MaximalLength = 17; + + // Converts the given double 'v' to ascii. 'v' must not be NaN, +Infinity, or + // -Infinity. In SHORTEST_SINGLE-mode this restriction also applies to 'v' + // after it has been casted to a single-precision float. That is, in this + // mode static_cast(v) must not be NaN, +Infinity or -Infinity. + // + // The result should be interpreted as buffer * 10^(point-length). + // + // The output depends on the given mode: + // - SHORTEST: produce the least amount of digits for which the internal + // identity requirement is still satisfied. If the digits are printed + // (together with the correct exponent) then reading this number will give + // 'v' again. The buffer will choose the representation that is closest to + // 'v'. If there are two at the same distance, than the one farther away + // from 0 is chosen (halfway cases - ending with 5 - are rounded up). + // In this mode the 'requested_digits' parameter is ignored. + // - SHORTEST_SINGLE: same as SHORTEST but with single-precision. + // - FIXED: produces digits necessary to print a given number with + // 'requested_digits' digits after the decimal point. The produced digits + // might be too short in which case the caller has to fill the remainder + // with '0's. + // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. + // Halfway cases are rounded towards +/-Infinity (away from 0). The call + // toFixed(0.15, 2) thus returns buffer="2", point=0. + // The returned buffer may contain digits that would be truncated from the + // shortest representation of the input. + // - PRECISION: produces 'requested_digits' where the first digit is not '0'. + // Even though the length of produced digits usually equals + // 'requested_digits', the function is allowed to return fewer digits, in + // which case the caller has to fill the missing digits with '0's. + // Halfway cases are again rounded away from 0. + // DoubleToAscii expects the given buffer to be big enough to hold all + // digits and a terminating null-character. In SHORTEST-mode it expects a + // buffer of at least kBase10MaximalLength + 1. In all other modes the + // requested_digits parameter and the padding-zeroes limit the size of the + // output. Don't forget the decimal point, the exponent character and the + // terminating null-character when computing the maximal output size. + // The given length is only used in debug mode to ensure the buffer is big + // enough. + static void DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point); + + private: + // Implementation for ToShortest and ToShortestSingle. + bool ToShortestIeeeNumber(double value, + StringBuilder* result_builder, + DtoaMode mode) const; + + // If the value is a special value (NaN or Infinity) constructs the + // corresponding string using the configured infinity/nan-symbol. + // If either of them is NULL or the value is not special then the + // function returns false. + bool HandleSpecialValues(double value, StringBuilder* result_builder) const; + // Constructs an exponential representation (i.e. 1.234e56). + // The given exponent assumes a decimal point after the first decimal digit. + void CreateExponentialRepresentation(const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const; + // Creates a decimal representation (i.e 1234.5678). + void CreateDecimalRepresentation(const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const; + + const int flags_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + const char exponent_character_; + const int decimal_in_shortest_low_; + const int decimal_in_shortest_high_; + const int max_leading_padding_zeroes_in_precision_mode_; + const int max_trailing_padding_zeroes_in_precision_mode_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter); +}; + + +class StringToDoubleConverter { + public: + // Enumeration for allowing octals and ignoring junk when converting + // strings to numbers. + enum Flags { + NO_FLAGS = 0, + ALLOW_HEX = 1, + ALLOW_OCTALS = 2, + ALLOW_TRAILING_JUNK = 4, + ALLOW_LEADING_SPACES = 8, + ALLOW_TRAILING_SPACES = 16, + ALLOW_SPACES_AFTER_SIGN = 32 + }; + + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - ALLOW_HEX: recognizes the prefix "0x". Hex numbers may only be integers. + // Ex: StringToDouble("0x1234") -> 4660.0 + // In StringToDouble("0x1234.56") the characters ".56" are trailing + // junk. The result of the call is hence dependent on + // the ALLOW_TRAILING_JUNK flag and/or the junk value. + // With this flag "0x" is a junk-string. Even with ALLOW_TRAILING_JUNK, + // the string will not be parsed as "0" followed by junk. + // + // - ALLOW_OCTALS: recognizes the prefix "0" for octals: + // If a sequence of octal digits starts with '0', then the number is + // read as octal integer. Octal numbers may only be integers. + // Ex: StringToDouble("01234") -> 668.0 + // StringToDouble("012349") -> 12349.0 // Not a sequence of octal + // // digits. + // In StringToDouble("01234.56") the characters ".56" are trailing + // junk. The result of the call is hence dependent on + // the ALLOW_TRAILING_JUNK flag and/or the junk value. + // In StringToDouble("01234e56") the characters "e56" are trailing + // junk, too. + // - ALLOW_TRAILING_JUNK: ignore trailing characters that are not part of + // a double literal. + // - ALLOW_LEADING_SPACES: skip over leading spaces. + // - ALLOW_TRAILING_SPACES: ignore trailing spaces. + // - ALLOW_SPACES_AFTER_SIGN: ignore spaces after the sign. + // Ex: StringToDouble("- 123.2") -> -123.2. + // StringToDouble("+ 123.2") -> 123.2 + // + // empty_string_value is returned when an empty string is given as input. + // If ALLOW_LEADING_SPACES or ALLOW_TRAILING_SPACES are set, then a string + // containing only spaces is converted to the 'empty_string_value', too. + // + // junk_string_value is returned when + // a) ALLOW_TRAILING_JUNK is not set, and a junk character (a character not + // part of a double-literal) is found. + // b) ALLOW_TRAILING_JUNK is set, but the string does not start with a + // double literal. + // + // infinity_symbol and nan_symbol are strings that are used to detect + // inputs that represent infinity and NaN. They can be null, in which case + // they are ignored. + // The conversion routine first reads any possible signs. Then it compares the + // following character of the input-string with the first character of + // the infinity, and nan-symbol. If either matches, the function assumes, that + // a match has been found, and expects the following input characters to match + // the remaining characters of the special-value symbol. + // This means that the following restrictions apply to special-value symbols: + // - they must not start with signs ('+', or '-'), + // - they must not have the same first character. + // - they must not start with digits. + // + // Examples: + // flags = ALLOW_HEX | ALLOW_TRAILING_JUNK, + // empty_string_value = 0.0, + // junk_string_value = NaN, + // infinity_symbol = "infinity", + // nan_symbol = "nan": + // StringToDouble("0x1234") -> 4660.0. + // StringToDouble("0x1234K") -> 4660.0. + // StringToDouble("") -> 0.0 // empty_string_value. + // StringToDouble(" ") -> NaN // junk_string_value. + // StringToDouble(" 1") -> NaN // junk_string_value. + // StringToDouble("0x") -> NaN // junk_string_value. + // StringToDouble("-123.45") -> -123.45. + // StringToDouble("--123.45") -> NaN // junk_string_value. + // StringToDouble("123e45") -> 123e45. + // StringToDouble("123E45") -> 123e45. + // StringToDouble("123e+45") -> 123e45. + // StringToDouble("123E-45") -> 123e-45. + // StringToDouble("123e") -> 123.0 // trailing junk ignored. + // StringToDouble("123e-") -> 123.0 // trailing junk ignored. + // StringToDouble("+NaN") -> NaN // NaN string literal. + // StringToDouble("-infinity") -> -inf. // infinity literal. + // StringToDouble("Infinity") -> NaN // junk_string_value. + // + // flags = ALLOW_OCTAL | ALLOW_LEADING_SPACES, + // empty_string_value = 0.0, + // junk_string_value = NaN, + // infinity_symbol = NULL, + // nan_symbol = NULL: + // StringToDouble("0x1234") -> NaN // junk_string_value. + // StringToDouble("01234") -> 668.0. + // StringToDouble("") -> 0.0 // empty_string_value. + // StringToDouble(" ") -> 0.0 // empty_string_value. + // StringToDouble(" 1") -> 1.0 + // StringToDouble("0x") -> NaN // junk_string_value. + // StringToDouble("0123e45") -> NaN // junk_string_value. + // StringToDouble("01239E45") -> 1239e45. + // StringToDouble("-infinity") -> NaN // junk_string_value. + // StringToDouble("NaN") -> NaN // junk_string_value. + StringToDoubleConverter(int flags, + double empty_string_value, + double junk_string_value, + const char* infinity_symbol, + const char* nan_symbol) + : flags_(flags), + empty_string_value_(empty_string_value), + junk_string_value_(junk_string_value), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol) { + } + + // Performs the conversion. + // The output parameter 'processed_characters_count' is set to the number + // of characters that have been processed to read the number. + // Spaces than are processed with ALLOW_{LEADING|TRAILING}_SPACES are included + // in the 'processed_characters_count'. Trailing junk is never included. + double StringToDouble(const char* buffer, + int length, + int* processed_characters_count) { + return StringToIeee(buffer, length, processed_characters_count, true); + } + + // Same as StringToDouble but reads a float. + // Note that this is not equivalent to static_cast(StringToDouble(...)) + // due to potential double-rounding. + float StringToFloat(const char* buffer, + int length, + int* processed_characters_count) { + return static_cast(StringToIeee(buffer, length, + processed_characters_count, false)); + } + + private: + const int flags_; + const double empty_string_value_; + const double junk_string_value_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + + double StringToIeee(const char* buffer, + int length, + int* processed_characters_count, + bool read_as_double); + + DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter); +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ diff --git a/src/3rdparty/double-conversion/double-conversion.pri b/src/3rdparty/double-conversion/double-conversion.pri new file mode 100644 index 0000000000..8bb37c63e9 --- /dev/null +++ b/src/3rdparty/double-conversion/double-conversion.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $PWD +VPATH += $$PWD +SOURCES += $$PWD/*.cc +HEADERS += $$PWD/*.h diff --git a/src/3rdparty/double-conversion/fast-dtoa.cc b/src/3rdparty/double-conversion/fast-dtoa.cc new file mode 100644 index 0000000000..1a0f823509 --- /dev/null +++ b/src/3rdparty/double-conversion/fast-dtoa.cc @@ -0,0 +1,664 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "fast-dtoa.h" + +#include "cached-powers.h" +#include "diy-fp.h" +#include "ieee.h" + +namespace double_conversion { + +// The minimal and maximal target exponent define the range of w's binary +// exponent, where 'w' is the result of multiplying the input by a cached power +// of ten. +// +// A different range might be chosen on a different platform, to optimize digit +// generation, but a smaller range requires more powers of ten to be cached. +static const int kMinimalTargetExponent = -60; +static const int kMaximalTargetExponent = -32; + + +// Adjusts the last digit of the generated number, and screens out generated +// solutions that may be inaccurate. A solution may be inaccurate if it is +// outside the safe interval, or if we cannot prove that it is closer to the +// input than a neighboring representation of the same length. +// +// Input: * buffer containing the digits of too_high / 10^kappa +// * the buffer's length +// * distance_too_high_w == (too_high - w).f() * unit +// * unsafe_interval == (too_high - too_low).f() * unit +// * rest = (too_high - buffer * 10^kappa).f() * unit +// * ten_kappa = 10^kappa * unit +// * unit = the common multiplier +// Output: returns true if the buffer is guaranteed to contain the closest +// representable number to the input. +// Modifies the generated digits in the buffer to approach (round towards) w. +static bool RoundWeed(Vector buffer, + int length, + uint64_t distance_too_high_w, + uint64_t unsafe_interval, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit) { + uint64_t small_distance = distance_too_high_w - unit; + uint64_t big_distance = distance_too_high_w + unit; + // Let w_low = too_high - big_distance, and + // w_high = too_high - small_distance. + // Note: w_low < w < w_high + // + // The real w (* unit) must lie somewhere inside the interval + // ]w_low; w_high[ (often written as "(w_low; w_high)") + + // Basically the buffer currently contains a number in the unsafe interval + // ]too_low; too_high[ with too_low < w < too_high + // + // too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // ^v 1 unit ^ ^ ^ ^ + // boundary_high --------------------- . . . . + // ^v 1 unit . . . . + // - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . . + // . . ^ . . + // . big_distance . . . + // . . . . rest + // small_distance . . . . + // v . . . . + // w_high - - - - - - - - - - - - - - - - - - . . . . + // ^v 1 unit . . . . + // w ---------------------------------------- . . . . + // ^v 1 unit v . . . + // w_low - - - - - - - - - - - - - - - - - - - - - . . . + // . . v + // buffer --------------------------------------------------+-------+-------- + // . . + // safe_interval . + // v . + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + // ^v 1 unit . + // boundary_low ------------------------- unsafe_interval + // ^v 1 unit v + // too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // + // + // Note that the value of buffer could lie anywhere inside the range too_low + // to too_high. + // + // boundary_low, boundary_high and w are approximations of the real boundaries + // and v (the input number). They are guaranteed to be precise up to one unit. + // In fact the error is guaranteed to be strictly less than one unit. + // + // Anything that lies outside the unsafe interval is guaranteed not to round + // to v when read again. + // Anything that lies inside the safe interval is guaranteed to round to v + // when read again. + // If the number inside the buffer lies inside the unsafe interval but not + // inside the safe interval then we simply do not know and bail out (returning + // false). + // + // Similarly we have to take into account the imprecision of 'w' when finding + // the closest representation of 'w'. If we have two potential + // representations, and one is closer to both w_low and w_high, then we know + // it is closer to the actual value v. + // + // By generating the digits of too_high we got the largest (closest to + // too_high) buffer that is still in the unsafe interval. In the case where + // w_high < buffer < too_high we try to decrement the buffer. + // This way the buffer approaches (rounds towards) w. + // There are 3 conditions that stop the decrementation process: + // 1) the buffer is already below w_high + // 2) decrementing the buffer would make it leave the unsafe interval + // 3) decrementing the buffer would yield a number below w_high and farther + // away than the current number. In other words: + // (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high + // Instead of using the buffer directly we use its distance to too_high. + // Conceptually rest ~= too_high - buffer + // We need to do the following tests in this order to avoid over- and + // underflows. + ASSERT(rest <= unsafe_interval); + while (rest < small_distance && // Negated condition 1 + unsafe_interval - rest >= ten_kappa && // Negated condition 2 + (rest + ten_kappa < small_distance || // buffer{-1} > w_high + small_distance - rest >= rest + ten_kappa - small_distance)) { + buffer[length - 1]--; + rest += ten_kappa; + } + + // We have approached w+ as much as possible. We now test if approaching w- + // would require changing the buffer. If yes, then we have two possible + // representations close to w, but we cannot decide which one is closer. + if (rest < big_distance && + unsafe_interval - rest >= ten_kappa && + (rest + ten_kappa < big_distance || + big_distance - rest > rest + ten_kappa - big_distance)) { + return false; + } + + // Weeding test. + // The safe interval is [too_low + 2 ulp; too_high - 2 ulp] + // Since too_low = too_high - unsafe_interval this is equivalent to + // [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp] + // Conceptually we have: rest ~= too_high - buffer + return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit); +} + + +// Rounds the buffer upwards if the result is closer to v by possibly adding +// 1 to the buffer. If the precision of the calculation is not sufficient to +// round correctly, return false. +// The rounding might shift the whole buffer in which case the kappa is +// adjusted. For example "99", kappa = 3 might become "10", kappa = 4. +// +// If 2*rest > ten_kappa then the buffer needs to be round up. +// rest can have an error of +/- 1 unit. This function accounts for the +// imprecision and returns false, if the rounding direction cannot be +// unambiguously determined. +// +// Precondition: rest < ten_kappa. +static bool RoundWeedCounted(Vector buffer, + int length, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit, + int* kappa) { + ASSERT(rest < ten_kappa); + // The following tests are done in a specific order to avoid overflows. They + // will work correctly with any uint64 values of rest < ten_kappa and unit. + // + // If the unit is too big, then we don't know which way to round. For example + // a unit of 50 means that the real number lies within rest +/- 50. If + // 10^kappa == 40 then there is no way to tell which way to round. + if (unit >= ten_kappa) return false; + // Even if unit is just half the size of 10^kappa we are already completely + // lost. (And after the previous test we know that the expression will not + // over/underflow.) + if (ten_kappa - unit <= unit) return false; + // If 2 * (rest + unit) <= 10^kappa we can safely round down. + if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) { + return true; + } + // If 2 * (rest - unit) >= 10^kappa, then we can safely round up. + if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) { + // Increment the last digit recursively until we find a non '9' digit. + buffer[length - 1]++; + for (int i = length - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + // If the first digit is now '0'+ 10 we had a buffer with all '9's. With the + // exception of the first digit all digits are now '0'. Simply switch the + // first digit to '1' and adjust the kappa. Example: "99" becomes "10" and + // the power (the kappa) is increased. + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*kappa) += 1; + } + return true; + } + return false; +} + +// Returns the biggest power of ten that is less than or equal to the given +// number. We furthermore receive the maximum number of bits 'number' has. +// +// Returns power == 10^(exponent_plus_one-1) such that +// power <= number < power * 10. +// If number_bits == 0 then 0^(0-1) is returned. +// The number of bits must be <= 32. +// Precondition: number < (1 << (number_bits + 1)). + +// Inspired by the method for finding an integer log base 10 from here: +// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 +static unsigned int const kSmallPowersOfTen[] = + {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, + 1000000000}; + +static void BiggestPowerTen(uint32_t number, + int number_bits, + uint32_t* power, + int* exponent_plus_one) { + ASSERT(number < (1u << (number_bits + 1))); + // 1233/4096 is approximately 1/lg(10). + int exponent_plus_one_guess = ((number_bits + 1) * 1233 >> 12); + // We increment to skip over the first entry in the kPowersOf10 table. + // Note: kPowersOf10[i] == 10^(i-1). + exponent_plus_one_guess++; + // We don't have any guarantees that 2^number_bits <= number. + // TODO(floitsch): can we change the 'while' into an 'if'? We definitely see + // number < (2^number_bits - 1), but I haven't encountered + // number < (2^number_bits - 2) yet. + while (number < kSmallPowersOfTen[exponent_plus_one_guess]) { + exponent_plus_one_guess--; + } + *power = kSmallPowersOfTen[exponent_plus_one_guess]; + *exponent_plus_one = exponent_plus_one_guess; +} + +// Generates the digits of input number w. +// w is a floating-point number (DiyFp), consisting of a significand and an +// exponent. Its exponent is bounded by kMinimalTargetExponent and +// kMaximalTargetExponent. +// Hence -60 <= w.e() <= -32. +// +// Returns false if it fails, in which case the generated digits in the buffer +// should not be used. +// Preconditions: +// * low, w and high are correct up to 1 ulp (unit in the last place). That +// is, their error must be less than a unit of their last digits. +// * low.e() == w.e() == high.e() +// * low < w < high, and taking into account their error: low~ <= high~ +// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent +// Postconditions: returns false if procedure fails. +// otherwise: +// * buffer is not null-terminated, but len contains the number of digits. +// * buffer contains the shortest possible decimal digit-sequence +// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the +// correct values of low and high (without their error). +// * if more than one decimal representation gives the minimal number of +// decimal digits then the one closest to W (where W is the correct value +// of w) is chosen. +// Remark: this procedure takes into account the imprecision of its input +// numbers. If the precision is not enough to guarantee all the postconditions +// then false is returned. This usually happens rarely (~0.5%). +// +// Say, for the sake of example, that +// w.e() == -48, and w.f() == 0x1234567890abcdef +// w's value can be computed by w.f() * 2^w.e() +// We can obtain w's integral digits by simply shifting w.f() by -w.e(). +// -> w's integral part is 0x1234 +// w's fractional part is therefore 0x567890abcdef. +// Printing w's integral part is easy (simply print 0x1234 in decimal). +// In order to print its fraction we repeatedly multiply the fraction by 10 and +// get each digit. Example the first digit after the point would be computed by +// (0x567890abcdef * 10) >> 48. -> 3 +// The whole thing becomes slightly more complicated because we want to stop +// once we have enough digits. That is, once the digits inside the buffer +// represent 'w' we can stop. Everything inside the interval low - high +// represents w. However we have to pay attention to low, high and w's +// imprecision. +static bool DigitGen(DiyFp low, + DiyFp w, + DiyFp high, + Vector buffer, + int* length, + int* kappa) { + ASSERT(low.e() == w.e() && w.e() == high.e()); + ASSERT(low.f() + 1 <= high.f() - 1); + ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + // low, w and high are imprecise, but by less than one ulp (unit in the last + // place). + // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that + // the new numbers are outside of the interval we want the final + // representation to lie in. + // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield + // numbers that are certain to lie in the interval. We will use this fact + // later on. + // We will now start by generating the digits within the uncertain + // interval. Later we will weed out representations that lie outside the safe + // interval and thus _might_ lie outside the correct interval. + uint64_t unit = 1; + DiyFp too_low = DiyFp(low.f() - unit, low.e()); + DiyFp too_high = DiyFp(high.f() + unit, high.e()); + // too_low and too_high are guaranteed to lie outside the interval we want the + // generated number in. + DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low); + // We now cut the input number into two parts: the integral digits and the + // fractionals. We will not write any decimal separator though, but adapt + // kappa instead. + // Reminder: we are currently computing the digits (stored inside the buffer) + // such that: too_low < buffer * 10^kappa < too_high + // We use too_high for the digit_generation and stop as soon as possible. + // If we stop early we effectively round down. + DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast(too_high.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = too_high.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent_plus_one; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + &divisor, &divisor_exponent_plus_one); + *kappa = divisor_exponent_plus_one; + *length = 0; + // Loop invariant: buffer = too_high / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divisor exponent + 1. And the divisor is the biggest power of ten + // that is smaller than integrals. + while (*kappa > 0) { + int digit = integrals / divisor; + buffer[*length] = '0' + digit; + (*length)++; + integrals %= divisor; + (*kappa)--; + // Note that kappa now equals the exponent of the divisor and that the + // invariant thus holds again. + uint64_t rest = + (static_cast(integrals) << -one.e()) + fractionals; + // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e()) + // Reminder: unsafe_interval.e() == one.e() + if (rest < unsafe_interval.f()) { + // Rounding down (by not emitting the remaining digits) yields a number + // that lies within the unsafe interval. + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(), + unsafe_interval.f(), rest, + static_cast(divisor) << -one.e(), unit); + } + divisor /= 10; + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (like the interval or 'unit'), too. + // Note that the multiplication by 10 does not overflow, because w.e >= -60 + // and thus one.e >= -60. + ASSERT(one.e() >= -60); + ASSERT(fractionals < one.f()); + ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + while (true) { + fractionals *= 10; + unit *= 10; + unsafe_interval.set_f(unsafe_interval.f() * 10); + // Integer division by one. + int digit = static_cast(fractionals >> -one.e()); + buffer[*length] = '0' + digit; + (*length)++; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + if (fractionals < unsafe_interval.f()) { + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit, + unsafe_interval.f(), fractionals, one.f(), unit); + } + } +} + + + +// Generates (at most) requested_digits digits of input number w. +// w is a floating-point number (DiyFp), consisting of a significand and an +// exponent. Its exponent is bounded by kMinimalTargetExponent and +// kMaximalTargetExponent. +// Hence -60 <= w.e() <= -32. +// +// Returns false if it fails, in which case the generated digits in the buffer +// should not be used. +// Preconditions: +// * w is correct up to 1 ulp (unit in the last place). That +// is, its error must be strictly less than a unit of its last digit. +// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent +// +// Postconditions: returns false if procedure fails. +// otherwise: +// * buffer is not null-terminated, but length contains the number of +// digits. +// * the representation in buffer is the most precise representation of +// requested_digits digits. +// * buffer contains at most requested_digits digits of w. If there are less +// than requested_digits digits then some trailing '0's have been removed. +// * kappa is such that +// w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2. +// +// Remark: This procedure takes into account the imprecision of its input +// numbers. If the precision is not enough to guarantee all the postconditions +// then false is returned. This usually happens rarely, but the failure-rate +// increases with higher requested_digits. +static bool DigitGenCounted(DiyFp w, + int requested_digits, + Vector buffer, + int* length, + int* kappa) { + ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + ASSERT(kMinimalTargetExponent >= -60); + ASSERT(kMaximalTargetExponent <= -32); + // w is assumed to have an error less than 1 unit. Whenever w is scaled we + // also scale its error. + uint64_t w_error = 1; + // We cut the input number into two parts: the integral digits and the + // fractional digits. We don't emit any decimal separator, but adapt kappa + // instead. Example: instead of writing "1.2" we put "12" into the buffer and + // increase kappa by 1. + DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast(w.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = w.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent_plus_one; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + &divisor, &divisor_exponent_plus_one); + *kappa = divisor_exponent_plus_one; + *length = 0; + + // Loop invariant: buffer = w / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divisor exponent + 1. And the divisor is the biggest power of ten + // that is smaller than 'integrals'. + while (*kappa > 0) { + int digit = integrals / divisor; + buffer[*length] = '0' + digit; + (*length)++; + requested_digits--; + integrals %= divisor; + (*kappa)--; + // Note that kappa now equals the exponent of the divisor and that the + // invariant thus holds again. + if (requested_digits == 0) break; + divisor /= 10; + } + + if (requested_digits == 0) { + uint64_t rest = + (static_cast(integrals) << -one.e()) + fractionals; + return RoundWeedCounted(buffer, *length, rest, + static_cast(divisor) << -one.e(), w_error, + kappa); + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (the 'unit'), too. + // Note that the multiplication by 10 does not overflow, because w.e >= -60 + // and thus one.e >= -60. + ASSERT(one.e() >= -60); + ASSERT(fractionals < one.f()); + ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + while (requested_digits > 0 && fractionals > w_error) { + fractionals *= 10; + w_error *= 10; + // Integer division by one. + int digit = static_cast(fractionals >> -one.e()); + buffer[*length] = '0' + digit; + (*length)++; + requested_digits--; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + } + if (requested_digits != 0) return false; + return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error, + kappa); +} + + +// Provides a decimal representation of v. +// Returns true if it succeeds, otherwise the result cannot be trusted. +// There will be *length digits inside the buffer (not null-terminated). +// If the function returns true then +// v == (double) (buffer * 10^decimal_exponent). +// The digits in the buffer are the shortest representation possible: no +// 0.09999999999999999 instead of 0.1. The shorter representation will even be +// chosen even if the longer one would be closer to v. +// The last digit will be closest to the actual v. That is, even if several +// digits might correctly yield 'v' when read again, the closest will be +// computed. +static bool Grisu3(double v, + FastDtoaMode mode, + Vector buffer, + int* length, + int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + // boundary_minus and boundary_plus are the boundaries between v and its + // closest floating-point neighbors. Any number strictly between + // boundary_minus and boundary_plus will round to v when convert to a double. + // Grisu3 will never output representations that lie exactly on a boundary. + DiyFp boundary_minus, boundary_plus; + if (mode == FAST_DTOA_SHORTEST) { + Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus); + } else { + ASSERT(mode == FAST_DTOA_SHORTEST_SINGLE); + float single_v = static_cast(v); + Single(single_v).NormalizedBoundaries(&boundary_minus, &boundary_plus); + } + ASSERT(boundary_plus.e() == w.e()); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, + ten_mk_maximal_binary_exponent, + &ten_mk, &mk); + ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize)); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + ASSERT(scaled_w.e() == + boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize); + // In theory it would be possible to avoid some recomputations by computing + // the difference between w and boundary_minus/plus (a power of 2) and to + // compute scaled_boundary_minus/plus by subtracting/adding from + // scaled_w. However the code becomes much less readable and the speed + // enhancements are not terriffic. + DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk); + DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk); + + // DigitGen will generate the digits of scaled_w. Therefore we have + // v == (double) (scaled_w * 10^-mk). + // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an + // integer than it will be updated. For instance if scaled_w == 1.23 then + // the buffer will be filled with "123" und the decimal_exponent will be + // decreased by 2. + int kappa; + bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + + +// The "counted" version of grisu3 (see above) only generates requested_digits +// number of digits. This version does not generate the shortest representation, +// and with enough requested digits 0.1 will at some point print as 0.9999999... +// Grisu3 is too imprecise for real halfway cases (1.5 will not work) and +// therefore the rounding strategy for halfway cases is irrelevant. +static bool Grisu3Counted(double v, + int requested_digits, + Vector buffer, + int* length, + int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, + ten_mk_maximal_binary_exponent, + &ten_mk, &mk); + ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize)); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + + // We now have (double) (scaled_w * 10^-mk). + // DigitGen will generate the first requested_digits digits of scaled_w and + // return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It + // will not always be exactly the same since DigitGenCounted only produces a + // limited number of digits.) + int kappa; + bool result = DigitGenCounted(scaled_w, requested_digits, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + + +bool FastDtoa(double v, + FastDtoaMode mode, + int requested_digits, + Vector buffer, + int* length, + int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + + bool result = false; + int decimal_exponent = 0; + switch (mode) { + case FAST_DTOA_SHORTEST: + case FAST_DTOA_SHORTEST_SINGLE: + result = Grisu3(v, mode, buffer, length, &decimal_exponent); + break; + case FAST_DTOA_PRECISION: + result = Grisu3Counted(v, requested_digits, + buffer, length, &decimal_exponent); + break; + default: + UNREACHABLE(); + } + if (result) { + *decimal_point = *length + decimal_exponent; + buffer[*length] = '\0'; + } + return result; +} + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/fast-dtoa.h b/src/3rdparty/double-conversion/fast-dtoa.h new file mode 100644 index 0000000000..5f1e8eee5e --- /dev/null +++ b/src/3rdparty/double-conversion/fast-dtoa.h @@ -0,0 +1,88 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_FAST_DTOA_H_ +#define DOUBLE_CONVERSION_FAST_DTOA_H_ + +#include "utils.h" + +namespace double_conversion { + +enum FastDtoaMode { + // Computes the shortest representation of the given input. The returned + // result will be the most accurate number of this length. Longer + // representations might be more accurate. + FAST_DTOA_SHORTEST, + // Same as FAST_DTOA_SHORTEST but for single-precision floats. + FAST_DTOA_SHORTEST_SINGLE, + // Computes a representation where the precision (number of digits) is + // given as input. The precision is independent of the decimal point. + FAST_DTOA_PRECISION +}; + +// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not +// include the terminating '\0' character. +static const int kFastDtoaMaximalLength = 17; +// Same for single-precision numbers. +static const int kFastDtoaMaximalSingleLength = 9; + +// Provides a decimal representation of v. +// The result should be interpreted as buffer * 10^(point - length). +// +// Precondition: +// * v must be a strictly positive finite double. +// +// Returns true if it succeeds, otherwise the result can not be trusted. +// There will be *length digits inside the buffer followed by a null terminator. +// If the function returns true and mode equals +// - FAST_DTOA_SHORTEST, then +// the parameter requested_digits is ignored. +// The result satisfies +// v == (double) (buffer * 10^(point - length)). +// The digits in the buffer are the shortest representation possible. E.g. +// if 0.099999999999 and 0.1 represent the same double then "1" is returned +// with point = 0. +// The last digit will be closest to the actual v. That is, even if several +// digits might correctly yield 'v' when read again, the buffer will contain +// the one closest to v. +// - FAST_DTOA_PRECISION, then +// the buffer contains requested_digits digits. +// the difference v - (buffer * 10^(point-length)) is closest to zero for +// all possible representations of requested_digits digits. +// If there are two values that are equally close, then FastDtoa returns +// false. +// For both modes the buffer must be large enough to hold the result. +bool FastDtoa(double d, + FastDtoaMode mode, + int requested_digits, + Vector buffer, + int* length, + int* decimal_point); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_FAST_DTOA_H_ diff --git a/src/3rdparty/double-conversion/fixed-dtoa.cc b/src/3rdparty/double-conversion/fixed-dtoa.cc new file mode 100644 index 0000000000..d56b1449b2 --- /dev/null +++ b/src/3rdparty/double-conversion/fixed-dtoa.cc @@ -0,0 +1,402 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "fixed-dtoa.h" +#include "ieee.h" + +namespace double_conversion { + +// Represents a 128bit type. This class should be replaced by a native type on +// platforms that support 128bit integers. +class UInt128 { + public: + UInt128() : high_bits_(0), low_bits_(0) { } + UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) { } + + void Multiply(uint32_t multiplicand) { + uint64_t accumulator; + + accumulator = (low_bits_ & kMask32) * multiplicand; + uint32_t part = static_cast(accumulator & kMask32); + accumulator >>= 32; + accumulator = accumulator + (low_bits_ >> 32) * multiplicand; + low_bits_ = (accumulator << 32) + part; + accumulator >>= 32; + accumulator = accumulator + (high_bits_ & kMask32) * multiplicand; + part = static_cast(accumulator & kMask32); + accumulator >>= 32; + accumulator = accumulator + (high_bits_ >> 32) * multiplicand; + high_bits_ = (accumulator << 32) + part; + ASSERT((accumulator >> 32) == 0); + } + + void Shift(int shift_amount) { + ASSERT(-64 <= shift_amount && shift_amount <= 64); + if (shift_amount == 0) { + return; + } else if (shift_amount == -64) { + high_bits_ = low_bits_; + low_bits_ = 0; + } else if (shift_amount == 64) { + low_bits_ = high_bits_; + high_bits_ = 0; + } else if (shift_amount <= 0) { + high_bits_ <<= -shift_amount; + high_bits_ += low_bits_ >> (64 + shift_amount); + low_bits_ <<= -shift_amount; + } else { + low_bits_ >>= shift_amount; + low_bits_ += high_bits_ << (64 - shift_amount); + high_bits_ >>= shift_amount; + } + } + + // Modifies *this to *this MOD (2^power). + // Returns *this DIV (2^power). + int DivModPowerOf2(int power) { + if (power >= 64) { + int result = static_cast(high_bits_ >> (power - 64)); + high_bits_ -= static_cast(result) << (power - 64); + return result; + } else { + uint64_t part_low = low_bits_ >> power; + uint64_t part_high = high_bits_ << (64 - power); + int result = static_cast(part_low + part_high); + high_bits_ = 0; + low_bits_ -= part_low << power; + return result; + } + } + + bool IsZero() const { + return high_bits_ == 0 && low_bits_ == 0; + } + + int BitAt(int position) { + if (position >= 64) { + return static_cast(high_bits_ >> (position - 64)) & 1; + } else { + return static_cast(low_bits_ >> position) & 1; + } + } + + private: + static const uint64_t kMask32 = 0xFFFFFFFF; + // Value == (high_bits_ << 64) + low_bits_ + uint64_t high_bits_; + uint64_t low_bits_; +}; + + +static const int kDoubleSignificandSize = 53; // Includes the hidden bit. + + +static void FillDigits32FixedLength(uint32_t number, int requested_length, + Vector buffer, int* length) { + for (int i = requested_length - 1; i >= 0; --i) { + buffer[(*length) + i] = '0' + number % 10; + number /= 10; + } + *length += requested_length; +} + + +static void FillDigits32(uint32_t number, Vector buffer, int* length) { + int number_length = 0; + // We fill the digits in reverse order and exchange them afterwards. + while (number != 0) { + int digit = number % 10; + number /= 10; + buffer[(*length) + number_length] = '0' + digit; + number_length++; + } + // Exchange the digits. + int i = *length; + int j = *length + number_length - 1; + while (i < j) { + char tmp = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = tmp; + i++; + j--; + } + *length += number_length; +} + + +static void FillDigits64FixedLength(uint64_t number, int requested_length, + Vector buffer, int* length) { + const uint32_t kTen7 = 10000000; + // For efficiency cut the number into 3 uint32_t parts, and print those. + uint32_t part2 = static_cast(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast(number % kTen7); + uint32_t part0 = static_cast(number / kTen7); + + FillDigits32FixedLength(part0, 3, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); +} + + +static void FillDigits64(uint64_t number, Vector buffer, int* length) { + const uint32_t kTen7 = 10000000; + // For efficiency cut the number into 3 uint32_t parts, and print those. + uint32_t part2 = static_cast(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast(number % kTen7); + uint32_t part0 = static_cast(number / kTen7); + + if (part0 != 0) { + FillDigits32(part0, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } else if (part1 != 0) { + FillDigits32(part1, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } else { + FillDigits32(part2, buffer, length); + } +} + + +static void RoundUp(Vector buffer, int* length, int* decimal_point) { + // An empty buffer represents 0. + if (*length == 0) { + buffer[0] = '1'; + *decimal_point = 1; + *length = 1; + return; + } + // Round the last digit until we either have a digit that was not '9' or until + // we reached the first digit. + buffer[(*length) - 1]++; + for (int i = (*length) - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) { + return; + } + buffer[i] = '0'; + buffer[i - 1]++; + } + // If the first digit is now '0' + 10, we would need to set it to '0' and add + // a '1' in front. However we reach the first digit only if all following + // digits had been '9' before rounding up. Now all trailing digits are '0' and + // we simply switch the first digit to '1' and update the decimal-point + // (indicating that the point is now one digit to the right). + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*decimal_point)++; + } +} + + +// The given fractionals number represents a fixed-point number with binary +// point at bit (-exponent). +// Preconditions: +// -128 <= exponent <= 0. +// 0 <= fractionals * 2^exponent < 1 +// The buffer holds the result. +// The function will round its result. During the rounding-process digits not +// generated by this function might be updated, and the decimal-point variable +// might be updated. If this function generates the digits 99 and the buffer +// already contained "199" (thus yielding a buffer of "19999") then a +// rounding-up will change the contents of the buffer to "20000". +static void FillFractionals(uint64_t fractionals, int exponent, + int fractional_count, Vector buffer, + int* length, int* decimal_point) { + ASSERT(-128 <= exponent && exponent <= 0); + // 'fractionals' is a fixed-point number, with binary point at bit + // (-exponent). Inside the function the non-converted remainder of fractionals + // is a fixed-point number, with binary point at bit 'point'. + if (-exponent <= 64) { + // One 64 bit number is sufficient. + ASSERT(fractionals >> 56 == 0); + int point = -exponent; + for (int i = 0; i < fractional_count; ++i) { + if (fractionals == 0) break; + // Instead of multiplying by 10 we multiply by 5 and adjust the point + // location. This way the fractionals variable will not overflow. + // Invariant at the beginning of the loop: fractionals < 2^point. + // Initially we have: point <= 64 and fractionals < 2^56 + // After each iteration the point is decremented by one. + // Note that 5^3 = 125 < 128 = 2^7. + // Therefore three iterations of this loop will not overflow fractionals + // (even without the subtraction at the end of the loop body). At this + // time point will satisfy point <= 61 and therefore fractionals < 2^point + // and any further multiplication of fractionals by 5 will not overflow. + fractionals *= 5; + point--; + int digit = static_cast(fractionals >> point); + buffer[*length] = '0' + digit; + (*length)++; + fractionals -= static_cast(digit) << point; + } + // If the first bit after the point is set we have to round up. + if (((fractionals >> (point - 1)) & 1) == 1) { + RoundUp(buffer, length, decimal_point); + } + } else { // We need 128 bits. + ASSERT(64 < -exponent && -exponent <= 128); + UInt128 fractionals128 = UInt128(fractionals, 0); + fractionals128.Shift(-exponent - 64); + int point = 128; + for (int i = 0; i < fractional_count; ++i) { + if (fractionals128.IsZero()) break; + // As before: instead of multiplying by 10 we multiply by 5 and adjust the + // point location. + // This multiplication will not overflow for the same reasons as before. + fractionals128.Multiply(5); + point--; + int digit = fractionals128.DivModPowerOf2(point); + buffer[*length] = '0' + digit; + (*length)++; + } + if (fractionals128.BitAt(point - 1) == 1) { + RoundUp(buffer, length, decimal_point); + } + } +} + + +// Removes leading and trailing zeros. +// If leading zeros are removed then the decimal point position is adjusted. +static void TrimZeros(Vector buffer, int* length, int* decimal_point) { + while (*length > 0 && buffer[(*length) - 1] == '0') { + (*length)--; + } + int first_non_zero = 0; + while (first_non_zero < *length && buffer[first_non_zero] == '0') { + first_non_zero++; + } + if (first_non_zero != 0) { + for (int i = first_non_zero; i < *length; ++i) { + buffer[i - first_non_zero] = buffer[i]; + } + *length -= first_non_zero; + *decimal_point -= first_non_zero; + } +} + + +bool FastFixedDtoa(double v, + int fractional_count, + Vector buffer, + int* length, + int* decimal_point) { + const uint32_t kMaxUInt32 = 0xFFFFFFFF; + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // v = significand * 2^exponent (with significand a 53bit integer). + // If the exponent is larger than 20 (i.e. we may have a 73bit number) then we + // don't know how to compute the representation. 2^73 ~= 9.5*10^21. + // If necessary this limit could probably be increased, but we don't need + // more. + if (exponent > 20) return false; + if (fractional_count > 20) return false; + *length = 0; + // At most kDoubleSignificandSize bits of the significand are non-zero. + // Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero + // bits: 0..11*..0xxx..53*..xx + if (exponent + kDoubleSignificandSize > 64) { + // The exponent must be > 11. + // + // We know that v = significand * 2^exponent. + // And the exponent > 11. + // We simplify the task by dividing v by 10^17. + // The quotient delivers the first digits, and the remainder fits into a 64 + // bit number. + // Dividing by 10^17 is equivalent to dividing by 5^17*2^17. + const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17 + uint64_t divisor = kFive17; + int divisor_power = 17; + uint64_t dividend = significand; + uint32_t quotient; + uint64_t remainder; + // Let v = f * 2^e with f == significand and e == exponent. + // Then need q (quotient) and r (remainder) as follows: + // v = q * 10^17 + r + // f * 2^e = q * 10^17 + r + // f * 2^e = q * 5^17 * 2^17 + r + // If e > 17 then + // f * 2^(e-17) = q * 5^17 + r/2^17 + // else + // f = q * 5^17 * 2^(17-e) + r/2^e + if (exponent > divisor_power) { + // We only allow exponents of up to 20 and therefore (17 - e) <= 3 + dividend <<= exponent - divisor_power; + quotient = static_cast(dividend / divisor); + remainder = (dividend % divisor) << divisor_power; + } else { + divisor <<= divisor_power - exponent; + quotient = static_cast(dividend / divisor); + remainder = (dividend % divisor) << exponent; + } + FillDigits32(quotient, buffer, length); + FillDigits64FixedLength(remainder, divisor_power, buffer, length); + *decimal_point = *length; + } else if (exponent >= 0) { + // 0 <= exponent <= 11 + significand <<= exponent; + FillDigits64(significand, buffer, length); + *decimal_point = *length; + } else if (exponent > -kDoubleSignificandSize) { + // We have to cut the number. + uint64_t integrals = significand >> -exponent; + uint64_t fractionals = significand - (integrals << -exponent); + if (integrals > kMaxUInt32) { + FillDigits64(integrals, buffer, length); + } else { + FillDigits32(static_cast(integrals), buffer, length); + } + *decimal_point = *length; + FillFractionals(fractionals, exponent, fractional_count, + buffer, length, decimal_point); + } else if (exponent < -128) { + // This configuration (with at most 20 digits) means that all digits must be + // 0. + ASSERT(fractional_count <= 20); + buffer[0] = '\0'; + *length = 0; + *decimal_point = -fractional_count; + } else { + *decimal_point = 0; + FillFractionals(significand, exponent, fractional_count, + buffer, length, decimal_point); + } + TrimZeros(buffer, length, decimal_point); + buffer[*length] = '\0'; + if ((*length) == 0) { + // The string is empty and the decimal_point thus has no importance. Mimick + // Gay's dtoa and and set it to -fractional_count. + *decimal_point = -fractional_count; + } + return true; +} + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/fixed-dtoa.h b/src/3rdparty/double-conversion/fixed-dtoa.h new file mode 100644 index 0000000000..3bdd08e21f --- /dev/null +++ b/src/3rdparty/double-conversion/fixed-dtoa.h @@ -0,0 +1,56 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_FIXED_DTOA_H_ +#define DOUBLE_CONVERSION_FIXED_DTOA_H_ + +#include "utils.h" + +namespace double_conversion { + +// Produces digits necessary to print a given number with +// 'fractional_count' digits after the decimal point. +// The buffer must be big enough to hold the result plus one terminating null +// character. +// +// The produced digits might be too short in which case the caller has to fill +// the gaps with '0's. +// Example: FastFixedDtoa(0.001, 5, ...) is allowed to return buffer = "1", and +// decimal_point = -2. +// Halfway cases are rounded towards +/-Infinity (away from 0). The call +// FastFixedDtoa(0.15, 2, ...) thus returns buffer = "2", decimal_point = 0. +// The returned buffer may contain digits that would be truncated from the +// shortest representation of the input. +// +// This method only works for some parameters. If it can't handle the input it +// returns false. The output is null-terminated when the function succeeds. +bool FastFixedDtoa(double v, int fractional_count, + Vector buffer, int* length, int* decimal_point); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_FIXED_DTOA_H_ diff --git a/src/3rdparty/double-conversion/ieee.h b/src/3rdparty/double-conversion/ieee.h new file mode 100644 index 0000000000..839dc47d45 --- /dev/null +++ b/src/3rdparty/double-conversion/ieee.h @@ -0,0 +1,398 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_DOUBLE_H_ +#define DOUBLE_CONVERSION_DOUBLE_H_ + +#include "diy-fp.h" + +namespace double_conversion { + +// We assume that doubles and uint64_t have the same endianness. +static uint64_t double_to_uint64(double d) { return BitCast(d); } +static double uint64_to_double(uint64_t d64) { return BitCast(d64); } +static uint32_t float_to_uint32(float f) { return BitCast(f); } +static float uint32_to_float(uint32_t d32) { return BitCast(d32); } + +// Helper functions for doubles. +class Double { + public: + static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000); + static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000); + static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit. + static const int kSignificandSize = 53; + + Double() : d64_(0) {} + explicit Double(double d) : d64_(double_to_uint64(d)) {} + explicit Double(uint64_t d64) : d64_(d64) {} + explicit Double(DiyFp diy_fp) + : d64_(DiyFpToUint64(diy_fp)) {} + + // The value encoded by this Double must be greater or equal to +0.0. + // It must not be special (infinity, or NaN). + DiyFp AsDiyFp() const { + ASSERT(Sign() > 0); + ASSERT(!IsSpecial()); + return DiyFp(Significand(), Exponent()); + } + + // The value encoded by this Double must be strictly greater than 0. + DiyFp AsNormalizedDiyFp() const { + ASSERT(value() > 0.0); + uint64_t f = Significand(); + int e = Exponent(); + + // The current double could be a denormal. + while ((f & kHiddenBit) == 0) { + f <<= 1; + e--; + } + // Do the final shifts in one go. + f <<= DiyFp::kSignificandSize - kSignificandSize; + e -= DiyFp::kSignificandSize - kSignificandSize; + return DiyFp(f, e); + } + + // Returns the double's bit as uint64. + uint64_t AsUint64() const { + return d64_; + } + + // Returns the next greater double. Returns +infinity on input +infinity. + double NextDouble() const { + if (d64_ == kInfinity) return Double(kInfinity).value(); + if (Sign() < 0 && Significand() == 0) { + // -0.0 + return 0.0; + } + if (Sign() < 0) { + return Double(d64_ - 1).value(); + } else { + return Double(d64_ + 1).value(); + } + } + + double PreviousDouble() const { + if (d64_ == (kInfinity | kSignMask)) return -Double::Infinity(); + if (Sign() < 0) { + return Double(d64_ + 1).value(); + } else { + if (Significand() == 0) return -0.0; + return Double(d64_ - 1).value(); + } + } + + int Exponent() const { + if (IsDenormal()) return kDenormalExponent; + + uint64_t d64 = AsUint64(); + int biased_e = + static_cast((d64 & kExponentMask) >> kPhysicalSignificandSize); + return biased_e - kExponentBias; + } + + uint64_t Significand() const { + uint64_t d64 = AsUint64(); + uint64_t significand = d64 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + // Returns true if the double is a denormal. + bool IsDenormal() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == kExponentMask; + } + + bool IsNan() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) != 0); + } + + bool IsInfinite() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) == 0); + } + + int Sign() const { + uint64_t d64 = AsUint64(); + return (d64 & kSignMask) == 0? 1: -1; + } + + // Precondition: the value encoded by this Double must be greater or equal + // than +0.0. + DiyFp UpperBoundary() const { + ASSERT(Sign() > 0); + return DiyFp(Significand() * 2 + 1, Exponent() - 1); + } + + // Computes the two boundaries of this. + // The bigger boundary (m_plus) is normalized. The lower boundary has the same + // exponent as m_plus. + // Precondition: the value encoded by this Double must be greater than 0. + void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + ASSERT(value() > 0.0); + DiyFp v = this->AsDiyFp(); + DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); + DiyFp m_minus; + if (LowerBoundaryIsCloser()) { + m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); + } else { + m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); + } + m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); + m_minus.set_e(m_plus.e()); + *out_m_plus = m_plus; + *out_m_minus = m_minus; + } + + bool LowerBoundaryIsCloser() const { + // The boundary is closer if the significand is of the form f == 2^p-1 then + // the lower boundary is closer. + // Think of v = 1000e10 and v- = 9999e9. + // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but + // at a distance of 1e8. + // The only exception is for the smallest normal: the largest denormal is + // at the same distance as its successor. + // Note: denormals have the same exponent as the smallest normals. + bool physical_significand_is_zero = ((AsUint64() & kSignificandMask) == 0); + return physical_significand_is_zero && (Exponent() != kDenormalExponent); + } + + double value() const { return uint64_to_double(d64_); } + + // Returns the significand size for a given order of magnitude. + // If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude. + // This function returns the number of significant binary digits v will have + // once it's encoded into a double. In almost all cases this is equal to + // kSignificandSize. The only exceptions are denormals. They start with + // leading zeroes and their effective significand-size is hence smaller. + static int SignificandSizeForOrderOfMagnitude(int order) { + if (order >= (kDenormalExponent + kSignificandSize)) { + return kSignificandSize; + } + if (order <= kDenormalExponent) return 0; + return order - kDenormalExponent; + } + + static double Infinity() { + return Double(kInfinity).value(); + } + + static double NaN() { + return Double(kNaN).value(); + } + + private: + static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; + static const int kDenormalExponent = -kExponentBias + 1; + static const int kMaxExponent = 0x7FF - kExponentBias; + static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000); + + const uint64_t d64_; + + static uint64_t DiyFpToUint64(DiyFp diy_fp) { + uint64_t significand = diy_fp.f(); + int exponent = diy_fp.e(); + while (significand > kHiddenBit + kSignificandMask) { + significand >>= 1; + exponent++; + } + if (exponent >= kMaxExponent) { + return kInfinity; + } + if (exponent < kDenormalExponent) { + return 0; + } + while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) { + significand <<= 1; + exponent--; + } + uint64_t biased_exponent; + if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) { + biased_exponent = 0; + } else { + biased_exponent = static_cast(exponent + kExponentBias); + } + return (significand & kSignificandMask) | + (biased_exponent << kPhysicalSignificandSize); + } +}; + +class Single { + public: + static const uint32_t kSignMask = 0x80000000; + static const uint32_t kExponentMask = 0x7F800000; + static const uint32_t kSignificandMask = 0x007FFFFF; + static const uint32_t kHiddenBit = 0x00800000; + static const int kPhysicalSignificandSize = 23; // Excludes the hidden bit. + static const int kSignificandSize = 24; + + Single() : d32_(0) {} + explicit Single(float f) : d32_(float_to_uint32(f)) {} + explicit Single(uint32_t d32) : d32_(d32) {} + + // The value encoded by this Single must be greater or equal to +0.0. + // It must not be special (infinity, or NaN). + DiyFp AsDiyFp() const { + ASSERT(Sign() > 0); + ASSERT(!IsSpecial()); + return DiyFp(Significand(), Exponent()); + } + + // Returns the single's bit as uint64. + uint32_t AsUint32() const { + return d32_; + } + + int Exponent() const { + if (IsDenormal()) return kDenormalExponent; + + uint32_t d32 = AsUint32(); + int biased_e = + static_cast((d32 & kExponentMask) >> kPhysicalSignificandSize); + return biased_e - kExponentBias; + } + + uint32_t Significand() const { + uint32_t d32 = AsUint32(); + uint32_t significand = d32 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + // Returns true if the single is a denormal. + bool IsDenormal() const { + uint32_t d32 = AsUint32(); + return (d32 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint32_t d32 = AsUint32(); + return (d32 & kExponentMask) == kExponentMask; + } + + bool IsNan() const { + uint32_t d32 = AsUint32(); + return ((d32 & kExponentMask) == kExponentMask) && + ((d32 & kSignificandMask) != 0); + } + + bool IsInfinite() const { + uint32_t d32 = AsUint32(); + return ((d32 & kExponentMask) == kExponentMask) && + ((d32 & kSignificandMask) == 0); + } + + int Sign() const { + uint32_t d32 = AsUint32(); + return (d32 & kSignMask) == 0? 1: -1; + } + + // Computes the two boundaries of this. + // The bigger boundary (m_plus) is normalized. The lower boundary has the same + // exponent as m_plus. + // Precondition: the value encoded by this Single must be greater than 0. + void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + ASSERT(value() > 0.0); + DiyFp v = this->AsDiyFp(); + DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); + DiyFp m_minus; + if (LowerBoundaryIsCloser()) { + m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); + } else { + m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); + } + m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); + m_minus.set_e(m_plus.e()); + *out_m_plus = m_plus; + *out_m_minus = m_minus; + } + + // Precondition: the value encoded by this Single must be greater or equal + // than +0.0. + DiyFp UpperBoundary() const { + ASSERT(Sign() > 0); + return DiyFp(Significand() * 2 + 1, Exponent() - 1); + } + + bool LowerBoundaryIsCloser() const { + // The boundary is closer if the significand is of the form f == 2^p-1 then + // the lower boundary is closer. + // Think of v = 1000e10 and v- = 9999e9. + // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but + // at a distance of 1e8. + // The only exception is for the smallest normal: the largest denormal is + // at the same distance as its successor. + // Note: denormals have the same exponent as the smallest normals. + bool physical_significand_is_zero = ((AsUint32() & kSignificandMask) == 0); + return physical_significand_is_zero && (Exponent() != kDenormalExponent); + } + + float value() const { return uint32_to_float(d32_); } + + static float Infinity() { + return Single(kInfinity).value(); + } + + static float NaN() { + return Single(kNaN).value(); + } + + private: + static const int kExponentBias = 0x7F + kPhysicalSignificandSize; + static const int kDenormalExponent = -kExponentBias + 1; + static const int kMaxExponent = 0xFF - kExponentBias; + static const uint32_t kInfinity = 0x7F800000; + static const uint32_t kNaN = 0x7FC00000; + + const uint32_t d32_; +}; + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_DOUBLE_H_ diff --git a/src/3rdparty/double-conversion/strtod.cc b/src/3rdparty/double-conversion/strtod.cc new file mode 100644 index 0000000000..9758989f71 --- /dev/null +++ b/src/3rdparty/double-conversion/strtod.cc @@ -0,0 +1,554 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include + +#include "strtod.h" +#include "bignum.h" +#include "cached-powers.h" +#include "ieee.h" + +namespace double_conversion { + +// 2^53 = 9007199254740992. +// Any integer with at most 15 decimal digits will hence fit into a double +// (which has a 53bit significand) without loss of precision. +static const int kMaxExactDoubleIntegerDecimalDigits = 15; +// 2^64 = 18446744073709551616 > 10^19 +static const int kMaxUint64DecimalDigits = 19; + +// Max double: 1.7976931348623157 x 10^308 +// Min non-zero double: 4.9406564584124654 x 10^-324 +// Any x >= 10^309 is interpreted as +infinity. +// Any x <= 10^-324 is interpreted as 0. +// Note that 2.5e-324 (despite being smaller than the min double) will be read +// as non-zero (equal to the min non-zero double). +static const int kMaxDecimalPower = 309; +static const int kMinDecimalPower = -324; + +// 2^64 = 18446744073709551616 +static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); + + +static const double exact_powers_of_ten[] = { + 1.0, // 10^0 + 10.0, + 100.0, + 1000.0, + 10000.0, + 100000.0, + 1000000.0, + 10000000.0, + 100000000.0, + 1000000000.0, + 10000000000.0, // 10^10 + 100000000000.0, + 1000000000000.0, + 10000000000000.0, + 100000000000000.0, + 1000000000000000.0, + 10000000000000000.0, + 100000000000000000.0, + 1000000000000000000.0, + 10000000000000000000.0, + 100000000000000000000.0, // 10^20 + 1000000000000000000000.0, + // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22 + 10000000000000000000000.0 +}; +static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten); + +// Maximum number of significant digits in the decimal representation. +// In fact the value is 772 (see conversions.cc), but to give us some margin +// we round up to 780. +static const int kMaxSignificantDecimalDigits = 780; + +static Vector TrimLeadingZeros(Vector buffer) { + for (int i = 0; i < buffer.length(); i++) { + if (buffer[i] != '0') { + return buffer.SubVector(i, buffer.length()); + } + } + return Vector(buffer.start(), 0); +} + + +static Vector TrimTrailingZeros(Vector buffer) { + for (int i = buffer.length() - 1; i >= 0; --i) { + if (buffer[i] != '0') { + return buffer.SubVector(0, i + 1); + } + } + return Vector(buffer.start(), 0); +} + + +static void CutToMaxSignificantDigits(Vector buffer, + int exponent, + char* significant_buffer, + int* significant_exponent) { + for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) { + significant_buffer[i] = buffer[i]; + } + // The input buffer has been trimmed. Therefore the last digit must be + // different from '0'. + ASSERT(buffer[buffer.length() - 1] != '0'); + // Set the last digit to be non-zero. This is sufficient to guarantee + // correct rounding. + significant_buffer[kMaxSignificantDecimalDigits - 1] = '1'; + *significant_exponent = + exponent + (buffer.length() - kMaxSignificantDecimalDigits); +} + + +// Trims the buffer and cuts it to at most kMaxSignificantDecimalDigits. +// If possible the input-buffer is reused, but if the buffer needs to be +// modified (due to cutting), then the input needs to be copied into the +// buffer_copy_space. +static void TrimAndCut(Vector buffer, int exponent, + char* buffer_copy_space, int space_size, + Vector* trimmed, int* updated_exponent) { + Vector left_trimmed = TrimLeadingZeros(buffer); + Vector right_trimmed = TrimTrailingZeros(left_trimmed); + exponent += left_trimmed.length() - right_trimmed.length(); + if (right_trimmed.length() > kMaxSignificantDecimalDigits) { + ASSERT(space_size >= kMaxSignificantDecimalDigits); + CutToMaxSignificantDigits(right_trimmed, exponent, + buffer_copy_space, updated_exponent); + *trimmed = Vector(buffer_copy_space, + kMaxSignificantDecimalDigits); + } else { + *trimmed = right_trimmed; + *updated_exponent = exponent; + } +} + + +// Reads digits from the buffer and converts them to a uint64. +// Reads in as many digits as fit into a uint64. +// When the string starts with "1844674407370955161" no further digit is read. +// Since 2^64 = 18446744073709551616 it would still be possible read another +// digit if it was less or equal than 6, but this would complicate the code. +static uint64_t ReadUint64(Vector buffer, + int* number_of_read_digits) { + uint64_t result = 0; + int i = 0; + while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) { + int digit = buffer[i++] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = 10 * result + digit; + } + *number_of_read_digits = i; + return result; +} + + +// Reads a DiyFp from the buffer. +// The returned DiyFp is not necessarily normalized. +// If remaining_decimals is zero then the returned DiyFp is accurate. +// Otherwise it has been rounded and has error of at most 1/2 ulp. +static void ReadDiyFp(Vector buffer, + DiyFp* result, + int* remaining_decimals) { + int read_digits; + uint64_t significand = ReadUint64(buffer, &read_digits); + if (buffer.length() == read_digits) { + *result = DiyFp(significand, 0); + *remaining_decimals = 0; + } else { + // Round the significand. + if (buffer[read_digits] >= '5') { + significand++; + } + // Compute the binary exponent. + int exponent = 0; + *result = DiyFp(significand, exponent); + *remaining_decimals = buffer.length() - read_digits; + } +} + + +static bool DoubleStrtod(Vector trimmed, + int exponent, + double* result) { +#if !defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) + // On x86 the floating-point stack can be 64 or 80 bits wide. If it is + // 80 bits wide (as is the case on Linux) then double-rounding occurs and the + // result is not accurate. + // We know that Windows32 uses 64 bits and is therefore accurate. + // Note that the ARM simulator is compiled for 32bits. It therefore exhibits + // the same problem. + return false; +#endif + if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) { + int read_digits; + // The trimmed input fits into a double. + // If the 10^exponent (resp. 10^-exponent) fits into a double too then we + // can compute the result-double simply by multiplying (resp. dividing) the + // two numbers. + // This is possible because IEEE guarantees that floating-point operations + // return the best possible approximation. + if (exponent < 0 && -exponent < kExactPowersOfTenSize) { + // 10^-exponent fits into a double. + *result = static_cast(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result /= exact_powers_of_ten[-exponent]; + return true; + } + if (0 <= exponent && exponent < kExactPowersOfTenSize) { + // 10^exponent fits into a double. + *result = static_cast(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[exponent]; + return true; + } + int remaining_digits = + kMaxExactDoubleIntegerDecimalDigits - trimmed.length(); + if ((0 <= exponent) && + (exponent - remaining_digits < kExactPowersOfTenSize)) { + // The trimmed string was short and we can multiply it with + // 10^remaining_digits. As a result the remaining exponent now fits + // into a double too. + *result = static_cast(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[remaining_digits]; + *result *= exact_powers_of_ten[exponent - remaining_digits]; + return true; + } + } + return false; +} + + +// Returns 10^exponent as an exact DiyFp. +// The given exponent must be in the range [1; kDecimalExponentDistance[. +static DiyFp AdjustmentPowerOfTen(int exponent) { + ASSERT(0 < exponent); + ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance); + // Simply hardcode the remaining powers for the given decimal exponent + // distance. + ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8); + switch (exponent) { + case 1: return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60); + case 2: return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57); + case 3: return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54); + case 4: return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50); + case 5: return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47); + case 6: return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44); + case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40); + default: + UNREACHABLE(); + return DiyFp(0, 0); + } +} + + +// If the function returns true then the result is the correct double. +// Otherwise it is either the correct double or the double that is just below +// the correct double. +static bool DiyFpStrtod(Vector buffer, + int exponent, + double* result) { + DiyFp input; + int remaining_decimals; + ReadDiyFp(buffer, &input, &remaining_decimals); + // Since we may have dropped some digits the input is not accurate. + // If remaining_decimals is different than 0 than the error is at most + // .5 ulp (unit in the last place). + // We don't want to deal with fractions and therefore keep a common + // denominator. + const int kDenominatorLog = 3; + const int kDenominator = 1 << kDenominatorLog; + // Move the remaining decimals into the exponent. + exponent += remaining_decimals; + int error = (remaining_decimals == 0 ? 0 : kDenominator / 2); + + int old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent); + if (exponent < PowersOfTenCache::kMinDecimalExponent) { + *result = 0.0; + return true; + } + DiyFp cached_power; + int cached_decimal_exponent; + PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent, + &cached_power, + &cached_decimal_exponent); + + if (cached_decimal_exponent != exponent) { + int adjustment_exponent = exponent - cached_decimal_exponent; + DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent); + input.Multiply(adjustment_power); + if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) { + // The product of input with the adjustment power fits into a 64 bit + // integer. + ASSERT(DiyFp::kSignificandSize == 64); + } else { + // The adjustment power is exact. There is hence only an error of 0.5. + error += kDenominator / 2; + } + } + + input.Multiply(cached_power); + // The error introduced by a multiplication of a*b equals + // error_a + error_b + error_a*error_b/2^64 + 0.5 + // Substituting a with 'input' and b with 'cached_power' we have + // error_b = 0.5 (all cached powers have an error of less than 0.5 ulp), + // error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64 + int error_b = kDenominator / 2; + int error_ab = (error == 0 ? 0 : 1); // We round up to 1. + int fixed_error = kDenominator / 2; + error += error_b + error_ab + fixed_error; + + old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + // See if the double's significand changes if we add/subtract the error. + int order_of_magnitude = DiyFp::kSignificandSize + input.e(); + int effective_significand_size = + Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude); + int precision_digits_count = + DiyFp::kSignificandSize - effective_significand_size; + if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) { + // This can only happen for very small denormals. In this case the + // half-way multiplied by the denominator exceeds the range of an uint64. + // Simply shift everything to the right. + int shift_amount = (precision_digits_count + kDenominatorLog) - + DiyFp::kSignificandSize + 1; + input.set_f(input.f() >> shift_amount); + input.set_e(input.e() + shift_amount); + // We add 1 for the lost precision of error, and kDenominator for + // the lost precision of input.f(). + error = (error >> shift_amount) + 1 + kDenominator; + precision_digits_count -= shift_amount; + } + // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too. + ASSERT(DiyFp::kSignificandSize == 64); + ASSERT(precision_digits_count < 64); + uint64_t one64 = 1; + uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1; + uint64_t precision_bits = input.f() & precision_bits_mask; + uint64_t half_way = one64 << (precision_digits_count - 1); + precision_bits *= kDenominator; + half_way *= kDenominator; + DiyFp rounded_input(input.f() >> precision_digits_count, + input.e() + precision_digits_count); + if (precision_bits >= half_way + error) { + rounded_input.set_f(rounded_input.f() + 1); + } + // If the last_bits are too close to the half-way case than we are too + // inaccurate and round down. In this case we return false so that we can + // fall back to a more precise algorithm. + + *result = Double(rounded_input).value(); + if (half_way - error < precision_bits && precision_bits < half_way + error) { + // Too imprecise. The caller will have to fall back to a slower version. + // However the returned number is guaranteed to be either the correct + // double, or the next-lower double. + return false; + } else { + return true; + } +} + + +// Returns +// - -1 if buffer*10^exponent < diy_fp. +// - 0 if buffer*10^exponent == diy_fp. +// - +1 if buffer*10^exponent > diy_fp. +// Preconditions: +// buffer.length() + exponent <= kMaxDecimalPower + 1 +// buffer.length() + exponent > kMinDecimalPower +// buffer.length() <= kMaxDecimalSignificantDigits +static int CompareBufferWithDiyFp(Vector buffer, + int exponent, + DiyFp diy_fp) { + ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); + ASSERT(buffer.length() + exponent > kMinDecimalPower); + ASSERT(buffer.length() <= kMaxSignificantDecimalDigits); + // Make sure that the Bignum will be able to hold all our numbers. + // Our Bignum implementation has a separate field for exponents. Shifts will + // consume at most one bigit (< 64 bits). + // ln(10) == 3.3219... + ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits); + Bignum buffer_bignum; + Bignum diy_fp_bignum; + buffer_bignum.AssignDecimalString(buffer); + diy_fp_bignum.AssignUInt64(diy_fp.f()); + if (exponent >= 0) { + buffer_bignum.MultiplyByPowerOfTen(exponent); + } else { + diy_fp_bignum.MultiplyByPowerOfTen(-exponent); + } + if (diy_fp.e() > 0) { + diy_fp_bignum.ShiftLeft(diy_fp.e()); + } else { + buffer_bignum.ShiftLeft(-diy_fp.e()); + } + return Bignum::Compare(buffer_bignum, diy_fp_bignum); +} + + +// Returns true if the guess is the correct double. +// Returns false, when guess is either correct or the next-lower double. +static bool ComputeGuess(Vector trimmed, int exponent, + double* guess) { + if (trimmed.length() == 0) { + *guess = 0.0; + return true; + } + if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) { + *guess = Double::Infinity(); + return true; + } + if (exponent + trimmed.length() <= kMinDecimalPower) { + *guess = 0.0; + return true; + } + + if (DoubleStrtod(trimmed, exponent, guess) || + DiyFpStrtod(trimmed, exponent, guess)) { + return true; + } + if (*guess == Double::Infinity()) { + return true; + } + return false; +} + +double Strtod(Vector buffer, int exponent) { + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector trimmed; + int updated_exponent; + TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, + &trimmed, &updated_exponent); + exponent = updated_exponent; + + double guess; + bool is_correct = ComputeGuess(trimmed, exponent, &guess); + if (is_correct) return guess; + + DiyFp upper_boundary = Double(guess).UpperBoundary(); + int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); + if (comparison < 0) { + return guess; + } else if (comparison > 0) { + return Double(guess).NextDouble(); + } else if ((Double(guess).Significand() & 1) == 0) { + // Round towards even. + return guess; + } else { + return Double(guess).NextDouble(); + } +} + +float Strtof(Vector buffer, int exponent) { + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector trimmed; + int updated_exponent; + TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, + &trimmed, &updated_exponent); + exponent = updated_exponent; + + double double_guess; + bool is_correct = ComputeGuess(trimmed, exponent, &double_guess); + + float float_guess = static_cast(double_guess); + if (float_guess == double_guess) { + // This shortcut triggers for integer values. + return float_guess; + } + + // We must catch double-rounding. Say the double has been rounded up, and is + // now a boundary of a float, and rounds up again. This is why we have to + // look at previous too. + // Example (in decimal numbers): + // input: 12349 + // high-precision (4 digits): 1235 + // low-precision (3 digits): + // when read from input: 123 + // when rounded from high precision: 124. + // To do this we simply look at the neigbors of the correct result and see + // if they would round to the same float. If the guess is not correct we have + // to look at four values (since two different doubles could be the correct + // double). + + double double_next = Double(double_guess).NextDouble(); + double double_previous = Double(double_guess).PreviousDouble(); + + float f1 = static_cast(double_previous); + float f2 = float_guess; + float f3 = static_cast(double_next); + float f4; + if (is_correct) { + f4 = f3; + } else { + double double_next2 = Double(double_next).NextDouble(); + f4 = static_cast(double_next2); + } + ASSERT(f1 <= f2 && f2 <= f3 && f3 <= f4); + + // If the guess doesn't lie near a single-precision boundary we can simply + // return its float-value. + if (f1 == f4) { + return float_guess; + } + + ASSERT((f1 != f2 && f2 == f3 && f3 == f4) || + (f1 == f2 && f2 != f3 && f3 == f4) || + (f1 == f2 && f2 == f3 && f3 != f4)); + + // guess and next are the two possible canditates (in the same way that + // double_guess was the lower candidate for a double-precision guess). + float guess = f1; + float next = f4; + DiyFp upper_boundary; + if (guess == 0.0f) { + float min_float = 1e-45f; + upper_boundary = Double(static_cast(min_float) / 2).AsDiyFp(); + } else { + upper_boundary = Single(guess).UpperBoundary(); + } + int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); + if (comparison < 0) { + return guess; + } else if (comparison > 0) { + return next; + } else if ((Single(guess).Significand() & 1) == 0) { + // Round towards even. + return guess; + } else { + return next; + } +} + +} // namespace double_conversion diff --git a/src/3rdparty/double-conversion/strtod.h b/src/3rdparty/double-conversion/strtod.h new file mode 100644 index 0000000000..ed0293b8f5 --- /dev/null +++ b/src/3rdparty/double-conversion/strtod.h @@ -0,0 +1,45 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_STRTOD_H_ +#define DOUBLE_CONVERSION_STRTOD_H_ + +#include "utils.h" + +namespace double_conversion { + +// The buffer must only contain digits in the range [0-9]. It must not +// contain a dot or a sign. It must not start with '0', and must not be empty. +double Strtod(Vector buffer, int exponent); + +// The buffer must only contain digits in the range [0-9]. It must not +// contain a dot or a sign. It must not start with '0', and must not be empty. +float Strtof(Vector buffer, int exponent); + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_STRTOD_H_ diff --git a/src/3rdparty/double-conversion/utils.h b/src/3rdparty/double-conversion/utils.h new file mode 100644 index 0000000000..767094b8b7 --- /dev/null +++ b/src/3rdparty/double-conversion/utils.h @@ -0,0 +1,313 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DOUBLE_CONVERSION_UTILS_H_ +#define DOUBLE_CONVERSION_UTILS_H_ + +#include +#include + +#include +#ifndef ASSERT +#define ASSERT(condition) (assert(condition)) +#endif +#ifndef UNIMPLEMENTED +#define UNIMPLEMENTED() (abort()) +#endif +#ifndef UNREACHABLE +#define UNREACHABLE() (abort()) +#endif + +// Double operations detection based on target architecture. +// Linux uses a 80bit wide floating point stack on x86. This induces double +// rounding, which in turn leads to wrong results. +// An easy way to test if the floating-point operations are correct is to +// evaluate: 89255.0/1e22. If the floating-point stack is 64 bits wide then +// the result is equal to 89255e-22. +// The best way to test this, is to create a division-function and to compare +// the output of the division with the expected result. (Inlining must be +// disabled.) +// On Linux,x86 89255e-22 != Div_double(89255.0/1e22) +#if defined(_M_X64) || defined(__x86_64__) || \ + defined(__ARMEL__) || defined(__avr32__) || \ + defined(__hppa__) || defined(__ia64__) || \ + defined(__mips__) || defined(__powerpc__) || \ + defined(__sparc__) || defined(__sparc) || defined(__s390__) || \ + defined(__SH4__) || defined(__alpha__) || \ + defined(_MIPS_ARCH_MIPS32R2) +#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 +#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) +#if defined(_WIN32) +// Windows uses a 64bit wide floating point stack. +#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 +#else +#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS +#endif // _WIN32 +#else +#error Target architecture was not detected as supported by Double-Conversion. +#endif + + +#if defined(_WIN32) && !defined(__MINGW32__) + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; // NOLINT +typedef unsigned short uint16_t; // NOLINT +typedef int int32_t; +typedef unsigned int uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +// intptr_t and friends are defined in crtdefs.h through stdio.h. + +#else + +#include + +#endif + +// The following macro works on both 32 and 64-bit platforms. +// Usage: instead of writing 0x1234567890123456 +// write UINT64_2PART_C(0x12345678,90123456); +#define UINT64_2PART_C(a, b) (((static_cast(a) << 32) + 0x##b##u)) + + +// The expression ARRAY_SIZE(a) is a compile-time constant of type +// size_t which represents the number of elements of the given +// array. You should only use ARRAY_SIZE on statically allocated +// arrays. +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast(!(sizeof(a) % sizeof(*(a))))) +#endif + +// A macro to disallow the evil copy constructor and operator= functions +// This should be used in the private: declarations for a class +#ifndef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#ifndef DISALLOW_IMPLICIT_CONSTRUCTORS +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + DISALLOW_COPY_AND_ASSIGN(TypeName) +#endif + +namespace double_conversion { + +static const int kCharSize = sizeof(char); + +// Returns the maximum of the two parameters. +template +static T Max(T a, T b) { + return a < b ? b : a; +} + + +// Returns the minimum of the two parameters. +template +static T Min(T a, T b) { + return a < b ? a : b; +} + + +inline int StrLength(const char* string) { + size_t length = strlen(string); + ASSERT(length == static_cast(static_cast(length))); + return static_cast(length); +} + +// This is a simplified version of V8's Vector class. +template +class Vector { + public: + Vector() : start_(NULL), length_(0) {} + Vector(T* data, int length) : start_(data), length_(length) { + ASSERT(length == 0 || (length > 0 && data != NULL)); + } + + // Returns a vector using the same backing storage as this one, + // spanning from and including 'from', to but not including 'to'. + Vector SubVector(int from, int to) { + ASSERT(to <= length_); + ASSERT(from < to); + ASSERT(0 <= from); + return Vector(start() + from, to - from); + } + + // Returns the length of the vector. + int length() const { return length_; } + + // Returns whether or not the vector is empty. + bool is_empty() const { return length_ == 0; } + + // Returns the pointer to the start of the data in the vector. + T* start() const { return start_; } + + // Access individual vector elements - checks bounds in debug mode. + T& operator[](int index) const { + ASSERT(0 <= index && index < length_); + return start_[index]; + } + + T& first() { return start_[0]; } + + T& last() { return start_[length_ - 1]; } + + private: + T* start_; + int length_; +}; + + +// Helper class for building result strings in a character buffer. The +// purpose of the class is to use safe operations that checks the +// buffer bounds on all operations in debug mode. +class StringBuilder { + public: + StringBuilder(char* buffer, int size) + : buffer_(buffer, size), position_(0) { } + + ~StringBuilder() { if (!is_finalized()) Finalize(); } + + int size() const { return buffer_.length(); } + + // Get the current position in the builder. + int position() const { + ASSERT(!is_finalized()); + return position_; + } + + // Reset the position. + void Reset() { position_ = 0; } + + // Add a single character to the builder. It is not allowed to add + // 0-characters; use the Finalize() method to terminate the string + // instead. + void AddCharacter(char c) { + ASSERT(c != '\0'); + ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_++] = c; + } + + // Add an entire string to the builder. Uses strlen() internally to + // compute the length of the input string. + void AddString(const char* s) { + AddSubstring(s, StrLength(s)); + } + + // Add the first 'n' characters of the given string 's' to the + // builder. The input string must have enough characters. + void AddSubstring(const char* s, int n) { + ASSERT(!is_finalized() && position_ + n < buffer_.length()); + ASSERT(static_cast(n) <= strlen(s)); + memmove(&buffer_[position_], s, n * kCharSize); + position_ += n; + } + + + // Add character padding to the builder. If count is non-positive, + // nothing is added to the builder. + void AddPadding(char c, int count) { + for (int i = 0; i < count; i++) { + AddCharacter(c); + } + } + + // Finalize the string by 0-terminating it and returning the buffer. + char* Finalize() { + ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_] = '\0'; + // Make sure nobody managed to add a 0-character to the + // buffer while building the string. + ASSERT(strlen(buffer_.start()) == static_cast(position_)); + position_ = -1; + ASSERT(is_finalized()); + return buffer_.start(); + } + + private: + Vector buffer_; + int position_; + + bool is_finalized() const { return position_ < 0; } + + DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder); +}; + +// The type-based aliasing rule allows the compiler to assume that pointers of +// different types (for some definition of different) never alias each other. +// Thus the following code does not work: +// +// float f = foo(); +// int fbits = *(int*)(&f); +// +// The compiler 'knows' that the int pointer can't refer to f since the types +// don't match, so the compiler may cache f in a register, leaving random data +// in fbits. Using C++ style casts makes no difference, however a pointer to +// char data is assumed to alias any other pointer. This is the 'memcpy +// exception'. +// +// Bit_cast uses the memcpy exception to move the bits from a variable of one +// type of a variable of another type. Of course the end result is likely to +// be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005) +// will completely optimize BitCast away. +// +// There is an additional use for BitCast. +// Recent gccs will warn when they see casts that may result in breakage due to +// the type-based aliasing rule. If you have checked that there is no breakage +// you can use BitCast to cast one pointer type to another. This confuses gcc +// enough that it can no longer see that you have cast one pointer type to +// another thus avoiding the warning. +template +inline Dest BitCast(const Source& source) { + // Compile time assertion: sizeof(Dest) == sizeof(Source) + // A compile error here means your Dest and Source have different sizes. + typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1]; + + Dest dest; + memmove(&dest, &source, sizeof(dest)); + return dest; +} + +template +inline Dest BitCast(Source* source) { + return BitCast(reinterpret_cast(source)); +} + +} // namespace double_conversion + +#endif // DOUBLE_CONVERSION_UTILS_H_ diff --git a/src/3rdparty/masm/WeakRandom.h b/src/3rdparty/masm/WeakRandom.h new file mode 100644 index 0000000000..325d1f6ac6 --- /dev/null +++ b/src/3rdparty/masm/WeakRandom.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MASM_WEAKRANDOM_H +#define MASM_WEAKRANDOM_H + +#include + +struct WeakRandom { + WeakRandom(int) {} + uint32_t getUint32() { return 0; } +}; + +#endif // MASM_WEAKRANDOM_H diff --git a/src/3rdparty/masm/assembler/ARMAssembler.cpp b/src/3rdparty/masm/assembler/ARMAssembler.cpp new file mode 100644 index 0000000000..9655557a5d --- /dev/null +++ b/src/3rdparty/masm/assembler/ARMAssembler.cpp @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2009 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#include "ARMAssembler.h" + +namespace JSC { + +// Patching helpers + +void ARMAssembler::patchConstantPoolLoad(void* loadAddr, void* constPoolAddr) +{ + ARMWord *ldr = reinterpret_cast(loadAddr); + ARMWord diff = reinterpret_cast(constPoolAddr) - ldr; + ARMWord index = (*ldr & 0xfff) >> 1; + + ASSERT(diff >= 1); + if (diff >= 2 || index > 0) { + diff = (diff + index - 2) * sizeof(ARMWord); + ASSERT(diff <= 0xfff); + *ldr = (*ldr & ~0xfff) | diff; + } else + *ldr = (*ldr & ~(0xfff | ARMAssembler::DataTransferUp)) | sizeof(ARMWord); +} + +// Handle immediates + +ARMWord ARMAssembler::getOp2(ARMWord imm) +{ + int rol; + + if (imm <= 0xff) + return Op2Immediate | imm; + + if ((imm & 0xff000000) == 0) { + imm <<= 8; + rol = 8; + } + else { + imm = (imm << 24) | (imm >> 8); + rol = 0; + } + + if ((imm & 0xff000000) == 0) { + imm <<= 8; + rol += 4; + } + + if ((imm & 0xf0000000) == 0) { + imm <<= 4; + rol += 2; + } + + if ((imm & 0xc0000000) == 0) { + imm <<= 2; + rol += 1; + } + + if ((imm & 0x00ffffff) == 0) + return Op2Immediate | (imm >> 24) | (rol << 8); + + return InvalidImmediate; +} + +int ARMAssembler::genInt(int reg, ARMWord imm, bool positive) +{ + // Step1: Search a non-immediate part + ARMWord mask; + ARMWord imm1; + ARMWord imm2; + int rol; + + mask = 0xff000000; + rol = 8; + while(1) { + if ((imm & mask) == 0) { + imm = (imm << rol) | (imm >> (32 - rol)); + rol = 4 + (rol >> 1); + break; + } + rol += 2; + mask >>= 2; + if (mask & 0x3) { + // rol 8 + imm = (imm << 8) | (imm >> 24); + mask = 0xff00; + rol = 24; + while (1) { + if ((imm & mask) == 0) { + imm = (imm << rol) | (imm >> (32 - rol)); + rol = (rol >> 1) - 8; + break; + } + rol += 2; + mask >>= 2; + if (mask & 0x3) + return 0; + } + break; + } + } + + ASSERT((imm & 0xff) == 0); + + if ((imm & 0xff000000) == 0) { + imm1 = Op2Immediate | ((imm >> 16) & 0xff) | (((rol + 4) & 0xf) << 8); + imm2 = Op2Immediate | ((imm >> 8) & 0xff) | (((rol + 8) & 0xf) << 8); + } else if (imm & 0xc0000000) { + imm1 = Op2Immediate | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8); + imm <<= 8; + rol += 4; + + if ((imm & 0xff000000) == 0) { + imm <<= 8; + rol += 4; + } + + if ((imm & 0xf0000000) == 0) { + imm <<= 4; + rol += 2; + } + + if ((imm & 0xc0000000) == 0) { + imm <<= 2; + rol += 1; + } + + if ((imm & 0x00ffffff) == 0) + imm2 = Op2Immediate | (imm >> 24) | ((rol & 0xf) << 8); + else + return 0; + } else { + if ((imm & 0xf0000000) == 0) { + imm <<= 4; + rol += 2; + } + + if ((imm & 0xc0000000) == 0) { + imm <<= 2; + rol += 1; + } + + imm1 = Op2Immediate | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8); + imm <<= 8; + rol += 4; + + if ((imm & 0xf0000000) == 0) { + imm <<= 4; + rol += 2; + } + + if ((imm & 0xc0000000) == 0) { + imm <<= 2; + rol += 1; + } + + if ((imm & 0x00ffffff) == 0) + imm2 = Op2Immediate | (imm >> 24) | ((rol & 0xf) << 8); + else + return 0; + } + + if (positive) { + mov(reg, imm1); + orr(reg, reg, imm2); + } else { + mvn(reg, imm1); + bic(reg, reg, imm2); + } + + return 1; +} + +ARMWord ARMAssembler::getImm(ARMWord imm, int tmpReg, bool invert) +{ + ARMWord tmp; + + // Do it by 1 instruction + tmp = getOp2(imm); + if (tmp != InvalidImmediate) + return tmp; + + tmp = getOp2(~imm); + if (tmp != InvalidImmediate) { + if (invert) + return tmp | Op2InvertedImmediate; + mvn(tmpReg, tmp); + return tmpReg; + } + + return encodeComplexImm(imm, tmpReg); +} + +void ARMAssembler::moveImm(ARMWord imm, int dest) +{ + ARMWord tmp; + + // Do it by 1 instruction + tmp = getOp2(imm); + if (tmp != InvalidImmediate) { + mov(dest, tmp); + return; + } + + tmp = getOp2(~imm); + if (tmp != InvalidImmediate) { + mvn(dest, tmp); + return; + } + + encodeComplexImm(imm, dest); +} + +ARMWord ARMAssembler::encodeComplexImm(ARMWord imm, int dest) +{ +#if WTF_ARM_ARCH_AT_LEAST(7) + ARMWord tmp = getImm16Op2(imm); + if (tmp != InvalidImmediate) { + movw(dest, tmp); + return dest; + } + movw(dest, getImm16Op2(imm & 0xffff)); + movt(dest, getImm16Op2(imm >> 16)); + return dest; +#else + // Do it by 2 instruction + if (genInt(dest, imm, true)) + return dest; + if (genInt(dest, ~imm, false)) + return dest; + + ldrImmediate(dest, imm); + return dest; +#endif +} + +// Memory load/store helpers + +void ARMAssembler::dataTransfer32(DataTransferTypeA transferType, RegisterID srcDst, RegisterID base, int32_t offset) +{ + if (offset >= 0) { + if (offset <= 0xfff) + dtrUp(transferType, srcDst, base, offset); + else if (offset <= 0xfffff) { + add(ARMRegisters::S0, base, Op2Immediate | (offset >> 12) | (10 << 8)); + dtrUp(transferType, srcDst, ARMRegisters::S0, (offset & 0xfff)); + } else { + moveImm(offset, ARMRegisters::S0); + dtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); + } + } else { + if (offset >= -0xfff) + dtrDown(transferType, srcDst, base, -offset); + else if (offset >= -0xfffff) { + sub(ARMRegisters::S0, base, Op2Immediate | (-offset >> 12) | (10 << 8)); + dtrDown(transferType, srcDst, ARMRegisters::S0, (-offset & 0xfff)); + } else { + moveImm(offset, ARMRegisters::S0); + dtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); + } + } +} + +void ARMAssembler::baseIndexTransfer32(DataTransferTypeA transferType, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) +{ + ASSERT(scale >= 0 && scale <= 3); + ARMWord op2 = lsl(index, scale); + + if (!offset) { + dtrUpRegister(transferType, srcDst, base, op2); + return; + } + + add(ARMRegisters::S1, base, op2); + dataTransfer32(transferType, srcDst, ARMRegisters::S1, offset); +} + +void ARMAssembler::dataTransfer16(DataTransferTypeB transferType, RegisterID srcDst, RegisterID base, int32_t offset) +{ + if (offset >= 0) { + if (offset <= 0xff) + halfDtrUp(transferType, srcDst, base, getOp2Half(offset)); + else if (offset <= 0xffff) { + add(ARMRegisters::S0, base, Op2Immediate | (offset >> 8) | (12 << 8)); + halfDtrUp(transferType, srcDst, ARMRegisters::S0, getOp2Half(offset & 0xff)); + } else { + moveImm(offset, ARMRegisters::S0); + halfDtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); + } + } else { + if (offset >= -0xff) + halfDtrDown(transferType, srcDst, base, getOp2Half(-offset)); + else if (offset >= -0xffff) { + sub(ARMRegisters::S0, base, Op2Immediate | (-offset >> 8) | (12 << 8)); + halfDtrDown(transferType, srcDst, ARMRegisters::S0, getOp2Half(-offset & 0xff)); + } else { + moveImm(offset, ARMRegisters::S0); + halfDtrUpRegister(transferType, srcDst, base, ARMRegisters::S0); + } + } +} + +void ARMAssembler::baseIndexTransfer16(DataTransferTypeB transferType, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) +{ + if (!scale && !offset) { + halfDtrUpRegister(transferType, srcDst, base, index); + return; + } + + add(ARMRegisters::S1, base, lsl(index, scale)); + dataTransfer16(transferType, srcDst, ARMRegisters::S1, offset); +} + +void ARMAssembler::dataTransferFloat(DataTransferTypeFloat transferType, FPRegisterID srcDst, RegisterID base, int32_t offset) +{ + // VFP cannot directly access memory that is not four-byte-aligned + if (!(offset & 0x3)) { + if (offset <= 0x3ff && offset >= 0) { + doubleDtrUp(transferType, srcDst, base, offset >> 2); + return; + } + if (offset <= 0x3ffff && offset >= 0) { + add(ARMRegisters::S0, base, Op2Immediate | (offset >> 10) | (11 << 8)); + doubleDtrUp(transferType, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff); + return; + } + offset = -offset; + + if (offset <= 0x3ff && offset >= 0) { + doubleDtrDown(transferType, srcDst, base, offset >> 2); + return; + } + if (offset <= 0x3ffff && offset >= 0) { + sub(ARMRegisters::S0, base, Op2Immediate | (offset >> 10) | (11 << 8)); + doubleDtrDown(transferType, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff); + return; + } + offset = -offset; + } + + moveImm(offset, ARMRegisters::S0); + add(ARMRegisters::S0, ARMRegisters::S0, base); + doubleDtrUp(transferType, srcDst, ARMRegisters::S0, 0); +} + +void ARMAssembler::baseIndexTransferFloat(DataTransferTypeFloat transferType, FPRegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset) +{ + add(ARMRegisters::S1, base, lsl(index, scale)); + dataTransferFloat(transferType, srcDst, ARMRegisters::S1, offset); +} + +PassRefPtr ARMAssembler::executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) +{ + // 64-bit alignment is required for next constant pool and JIT code as well + m_buffer.flushWithoutBarrier(true); + if (!m_buffer.isAligned(8)) + bkpt(0); + + RefPtr result = m_buffer.executableCopy(globalData, ownerUID, effort); + char* data = reinterpret_cast(result->start()); + + for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) { + // The last bit is set if the constant must be placed on constant pool. + int pos = (iter->m_offset) & (~0x1); + ARMWord* ldrAddr = reinterpret_cast_ptr(data + pos); + ARMWord* addr = getLdrImmAddress(ldrAddr); + if (*addr != InvalidBranchTarget) { + if (!(iter->m_offset & 1)) { + intptr_t difference = reinterpret_cast_ptr(data + *addr) - (ldrAddr + DefaultPrefetchOffset); + + if ((difference <= MaximumBranchOffsetDistance && difference >= MinimumBranchOffsetDistance)) { + *ldrAddr = B | getConditionalField(*ldrAddr) | (difference & BranchOffsetMask); + continue; + } + } + *addr = reinterpret_cast(data + *addr); + } + } + + return result; +} + +#if OS(LINUX) && COMPILER(RVCT) + +__asm void ARMAssembler::cacheFlush(void* code, size_t size) +{ + ARM + push {r7} + add r1, r1, r0 + mov r7, #0xf0000 + add r7, r7, #0x2 + mov r2, #0x0 + svc #0x0 + pop {r7} + bx lr +} + +#endif + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) diff --git a/src/3rdparty/masm/assembler/ARMAssembler.h b/src/3rdparty/masm/assembler/ARMAssembler.h new file mode 100644 index 0000000000..ebab46d98a --- /dev/null +++ b/src/3rdparty/masm/assembler/ARMAssembler.h @@ -0,0 +1,1108 @@ +/* + * Copyright (C) 2009, 2010 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ARMAssembler_h +#define ARMAssembler_h + +#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#include "AssemblerBufferWithConstantPool.h" +#include "JITCompilationEffort.h" +#include +namespace JSC { + + typedef uint32_t ARMWord; + + namespace ARMRegisters { + typedef enum { + r0 = 0, + r1, + r2, + r3, S0 = r3, /* Same as thumb assembler. */ + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, S1 = r12, + r13, sp = r13, + r14, lr = r14, + r15, pc = r15 + } RegisterID; + + typedef enum { + d0, + d1, + d2, + d3, + d4, + d5, + d6, + d7, SD0 = d7, /* Same as thumb assembler. */ + d8, + d9, + d10, + d11, + d12, + d13, + d14, + d15, + d16, + d17, + d18, + d19, + d20, + d21, + d22, + d23, + d24, + d25, + d26, + d27, + d28, + d29, + d30, + d31 + } FPRegisterID; + + } // namespace ARMRegisters + + class ARMAssembler { + public: + typedef ARMRegisters::RegisterID RegisterID; + typedef ARMRegisters::FPRegisterID FPRegisterID; + typedef AssemblerBufferWithConstantPool<2048, 4, 4, ARMAssembler> ARMBuffer; + typedef SegmentedVector Jumps; + + ARMAssembler() + : m_indexOfTailOfLastWatchpoint(1) + { + } + + // ARM conditional constants + typedef enum { + EQ = 0x00000000, // Zero + NE = 0x10000000, // Non-zero + CS = 0x20000000, + CC = 0x30000000, + MI = 0x40000000, + PL = 0x50000000, + VS = 0x60000000, + VC = 0x70000000, + HI = 0x80000000, + LS = 0x90000000, + GE = 0xa0000000, + LT = 0xb0000000, + GT = 0xc0000000, + LE = 0xd0000000, + AL = 0xe0000000 + } Condition; + + // ARM instruction constants + enum { + AND = (0x0 << 21), + EOR = (0x1 << 21), + SUB = (0x2 << 21), + RSB = (0x3 << 21), + ADD = (0x4 << 21), + ADC = (0x5 << 21), + SBC = (0x6 << 21), + RSC = (0x7 << 21), + TST = (0x8 << 21), + TEQ = (0x9 << 21), + CMP = (0xa << 21), + CMN = (0xb << 21), + ORR = (0xc << 21), + MOV = (0xd << 21), + BIC = (0xe << 21), + MVN = (0xf << 21), + MUL = 0x00000090, + MULL = 0x00c00090, + VMOV_F64 = 0x0eb00b40, + VADD_F64 = 0x0e300b00, + VDIV_F64 = 0x0e800b00, + VSUB_F64 = 0x0e300b40, + VMUL_F64 = 0x0e200b00, + VCMP_F64 = 0x0eb40b40, + VSQRT_F64 = 0x0eb10bc0, + VABS_F64 = 0x0eb00bc0, + VNEG_F64 = 0x0eb10b40, + STMDB = 0x09200000, + LDMIA = 0x08b00000, + B = 0x0a000000, + BL = 0x0b000000, + BX = 0x012fff10, + VMOV_VFP64 = 0x0c400a10, + VMOV_ARM64 = 0x0c500a10, + VMOV_VFP32 = 0x0e000a10, + VMOV_ARM32 = 0x0e100a10, + VCVT_F64_S32 = 0x0eb80bc0, + VCVT_S32_F64 = 0x0ebd0b40, + VCVT_U32_F64 = 0x0ebc0b40, + VCVT_F32_F64 = 0x0eb70bc0, + VCVT_F64_F32 = 0x0eb70ac0, + VMRS_APSR = 0x0ef1fa10, + CLZ = 0x016f0f10, + BKPT = 0xe1200070, + BLX = 0x012fff30, +#if WTF_ARM_ARCH_AT_LEAST(7) + MOVW = 0x03000000, + MOVT = 0x03400000, +#endif + NOP = 0xe1a00000, + }; + + enum { + Op2Immediate = (1 << 25), + ImmediateForHalfWordTransfer = (1 << 22), + Op2InvertedImmediate = (1 << 26), + SetConditionalCodes = (1 << 20), + Op2IsRegisterArgument = (1 << 25), + // Data transfer flags. + DataTransferUp = (1 << 23), + DataTransferWriteBack = (1 << 21), + DataTransferPostUpdate = (1 << 24), + DataTransferLoad = (1 << 20), + ByteDataTransfer = (1 << 22), + }; + + enum DataTransferTypeA { + LoadUint32 = 0x05000000 | DataTransferLoad, + LoadUint8 = 0x05400000 | DataTransferLoad, + StoreUint32 = 0x05000000, + StoreUint8 = 0x05400000, + }; + + enum DataTransferTypeB { + LoadUint16 = 0x010000b0 | DataTransferLoad, + LoadInt16 = 0x010000f0 | DataTransferLoad, + LoadInt8 = 0x010000d0 | DataTransferLoad, + StoreUint16 = 0x010000b0, + }; + + enum DataTransferTypeFloat { + LoadFloat = 0x0d000a00 | DataTransferLoad, + LoadDouble = 0x0d000b00 | DataTransferLoad, + StoreFloat = 0x0d000a00, + StoreDouble = 0x0d000b00, + }; + + // Masks of ARM instructions + enum { + BranchOffsetMask = 0x00ffffff, + ConditionalFieldMask = 0xf0000000, + DataTransferOffsetMask = 0xfff, + }; + + enum { + MinimumBranchOffsetDistance = -0x00800000, + MaximumBranchOffsetDistance = 0x007fffff, + }; + + enum { + padForAlign8 = 0x00, + padForAlign16 = 0x0000, + padForAlign32 = 0xe12fff7f // 'bkpt 0xffff' instruction. + }; + + static const ARMWord InvalidImmediate = 0xf0000000; + static const ARMWord InvalidBranchTarget = 0xffffffff; + static const int DefaultPrefetchOffset = 2; + + static const ARMWord BlxInstructionMask = 0x012fff30; + static const ARMWord LdrOrAddInstructionMask = 0x0ff00000; + static const ARMWord LdrPcImmediateInstructionMask = 0x0f7f0000; + + static const ARMWord AddImmediateInstruction = 0x02800000; + static const ARMWord BlxInstruction = 0x012fff30; + static const ARMWord LdrImmediateInstruction = 0x05900000; + static const ARMWord LdrPcImmediateInstruction = 0x051f0000; + + // Instruction formating + + void emitInstruction(ARMWord op, int rd, int rn, ARMWord op2) + { + ASSERT(((op2 & ~Op2Immediate) <= 0xfff) || (((op2 & ~ImmediateForHalfWordTransfer) <= 0xfff))); + m_buffer.putInt(op | RN(rn) | RD(rd) | op2); + } + + void emitDoublePrecisionInstruction(ARMWord op, int dd, int dn, int dm) + { + ASSERT((dd >= 0 && dd <= 31) && (dn >= 0 && dn <= 31) && (dm >= 0 && dm <= 31)); + m_buffer.putInt(op | ((dd & 0xf) << 12) | ((dd & 0x10) << (22 - 4)) + | ((dn & 0xf) << 16) | ((dn & 0x10) << (7 - 4)) + | (dm & 0xf) | ((dm & 0x10) << (5 - 4))); + } + + void emitSinglePrecisionInstruction(ARMWord op, int sd, int sn, int sm) + { + ASSERT((sd >= 0 && sd <= 31) && (sn >= 0 && sn <= 31) && (sm >= 0 && sm <= 31)); + m_buffer.putInt(op | ((sd >> 1) << 12) | ((sd & 0x1) << 22) + | ((sn >> 1) << 16) | ((sn & 0x1) << 7) + | (sm >> 1) | ((sm & 0x1) << 5)); + } + + void bitAnd(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | AND, rd, rn, op2); + } + + void bitAnds(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | AND | SetConditionalCodes, rd, rn, op2); + } + + void eor(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | EOR, rd, rn, op2); + } + + void eors(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | EOR | SetConditionalCodes, rd, rn, op2); + } + + void sub(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | SUB, rd, rn, op2); + } + + void subs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | SUB | SetConditionalCodes, rd, rn, op2); + } + + void rsb(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | RSB, rd, rn, op2); + } + + void rsbs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | RSB | SetConditionalCodes, rd, rn, op2); + } + + void add(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ADD, rd, rn, op2); + } + + void adds(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ADD | SetConditionalCodes, rd, rn, op2); + } + + void adc(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ADC, rd, rn, op2); + } + + void adcs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ADC | SetConditionalCodes, rd, rn, op2); + } + + void sbc(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | SBC, rd, rn, op2); + } + + void sbcs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | SBC | SetConditionalCodes, rd, rn, op2); + } + + void rsc(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | RSC, rd, rn, op2); + } + + void rscs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | RSC | SetConditionalCodes, rd, rn, op2); + } + + void tst(int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | TST | SetConditionalCodes, 0, rn, op2); + } + + void teq(int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | TEQ | SetConditionalCodes, 0, rn, op2); + } + + void cmp(int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | CMP | SetConditionalCodes, 0, rn, op2); + } + + void cmn(int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | CMN | SetConditionalCodes, 0, rn, op2); + } + + void orr(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ORR, rd, rn, op2); + } + + void orrs(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | ORR | SetConditionalCodes, rd, rn, op2); + } + + void mov(int rd, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | MOV, rd, ARMRegisters::r0, op2); + } + +#if WTF_ARM_ARCH_AT_LEAST(7) + void movw(int rd, ARMWord op2, Condition cc = AL) + { + ASSERT((op2 | 0xf0fff) == 0xf0fff); + m_buffer.putInt(toARMWord(cc) | MOVW | RD(rd) | op2); + } + + void movt(int rd, ARMWord op2, Condition cc = AL) + { + ASSERT((op2 | 0xf0fff) == 0xf0fff); + m_buffer.putInt(toARMWord(cc) | MOVT | RD(rd) | op2); + } +#endif + + void movs(int rd, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | MOV | SetConditionalCodes, rd, ARMRegisters::r0, op2); + } + + static void revertJump(void* instructionStart, RegisterID rd, ARMWord imm) + { + ARMWord* insn = reinterpret_cast(instructionStart); + ARMWord* address = getLdrImmAddress(insn); + *address = imm; + } + + void bic(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | BIC, rd, rn, op2); + } + + void bics(int rd, int rn, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | BIC | SetConditionalCodes, rd, rn, op2); + } + + void mvn(int rd, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | MVN, rd, ARMRegisters::r0, op2); + } + + void mvns(int rd, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | MVN | SetConditionalCodes, rd, ARMRegisters::r0, op2); + } + + void mul(int rd, int rn, int rm, Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | MUL | RN(rd) | RS(rn) | RM(rm)); + } + + void muls(int rd, int rn, int rm, Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | MUL | SetConditionalCodes | RN(rd) | RS(rn) | RM(rm)); + } + + void mull(int rdhi, int rdlo, int rn, int rm, Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | MULL | RN(rdhi) | RD(rdlo) | RS(rn) | RM(rm)); + } + + void vmov_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VMOV_F64, dd, 0, dm); + } + + void vadd_f64(int dd, int dn, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VADD_F64, dd, dn, dm); + } + + void vdiv_f64(int dd, int dn, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VDIV_F64, dd, dn, dm); + } + + void vsub_f64(int dd, int dn, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VSUB_F64, dd, dn, dm); + } + + void vmul_f64(int dd, int dn, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VMUL_F64, dd, dn, dm); + } + + void vcmp_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VCMP_F64, dd, 0, dm); + } + + void vsqrt_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VSQRT_F64, dd, 0, dm); + } + + void vabs_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VABS_F64, dd, 0, dm); + } + + void vneg_f64(int dd, int dm, Condition cc = AL) + { + emitDoublePrecisionInstruction(toARMWord(cc) | VNEG_F64, dd, 0, dm); + } + + void ldrImmediate(int rd, ARMWord imm, Condition cc = AL) + { + m_buffer.putIntWithConstantInt(toARMWord(cc) | LoadUint32 | DataTransferUp | RN(ARMRegisters::pc) | RD(rd), imm, true); + } + + void ldrUniqueImmediate(int rd, ARMWord imm, Condition cc = AL) + { + m_buffer.putIntWithConstantInt(toARMWord(cc) | LoadUint32 | DataTransferUp | RN(ARMRegisters::pc) | RD(rd), imm); + } + + void dtrUp(DataTransferTypeA transferType, int rd, int rb, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rb, op2); + } + + void dtrUpRegister(DataTransferTypeA transferType, int rd, int rb, int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | DataTransferUp | Op2IsRegisterArgument, rd, rb, rm); + } + + void dtrDown(DataTransferTypeA transferType, int rd, int rb, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType, rd, rb, op2); + } + + void dtrDownRegister(DataTransferTypeA transferType, int rd, int rb, int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | Op2IsRegisterArgument, rd, rb, rm); + } + + void halfDtrUp(DataTransferTypeB transferType, int rd, int rb, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rb, op2); + } + + void halfDtrUpRegister(DataTransferTypeB transferType, int rd, int rn, int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rn, rm); + } + + void halfDtrDown(DataTransferTypeB transferType, int rd, int rb, ARMWord op2, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType, rd, rb, op2); + } + + void halfDtrDownRegister(DataTransferTypeB transferType, int rd, int rn, int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | transferType, rd, rn, rm); + } + + void doubleDtrUp(DataTransferTypeFloat type, int rd, int rb, ARMWord op2, Condition cc = AL) + { + ASSERT(op2 <= 0xff && rd <= 15); + /* Only d0-d15 and s0, s2, s4 ... s30 are supported. */ + m_buffer.putInt(toARMWord(cc) | DataTransferUp | type | (rd << 12) | RN(rb) | op2); + } + + void doubleDtrDown(DataTransferTypeFloat type, int rd, int rb, ARMWord op2, Condition cc = AL) + { + ASSERT(op2 <= 0xff && rd <= 15); + /* Only d0-d15 and s0, s2, s4 ... s30 are supported. */ + m_buffer.putInt(toARMWord(cc) | type | (rd << 12) | RN(rb) | op2); + } + + void push(int reg, Condition cc = AL) + { + ASSERT(ARMWord(reg) <= 0xf); + m_buffer.putInt(toARMWord(cc) | StoreUint32 | DataTransferWriteBack | RN(ARMRegisters::sp) | RD(reg) | 0x4); + } + + void pop(int reg, Condition cc = AL) + { + ASSERT(ARMWord(reg) <= 0xf); + m_buffer.putInt(toARMWord(cc) | (LoadUint32 ^ DataTransferPostUpdate) | DataTransferUp | RN(ARMRegisters::sp) | RD(reg) | 0x4); + } + + inline void poke(int reg, Condition cc = AL) + { + dtrDown(StoreUint32, ARMRegisters::sp, 0, reg, cc); + } + + inline void peek(int reg, Condition cc = AL) + { + dtrUp(LoadUint32, reg, ARMRegisters::sp, 0, cc); + } + + void vmov_vfp64(int sm, int rt, int rt2, Condition cc = AL) + { + ASSERT(rt != rt2); + m_buffer.putInt(toARMWord(cc) | VMOV_VFP64 | RN(rt2) | RD(rt) | (sm & 0xf) | ((sm & 0x10) << (5 - 4))); + } + + void vmov_arm64(int rt, int rt2, int sm, Condition cc = AL) + { + ASSERT(rt != rt2); + m_buffer.putInt(toARMWord(cc) | VMOV_ARM64 | RN(rt2) | RD(rt) | (sm & 0xf) | ((sm & 0x10) << (5 - 4))); + } + + void vmov_vfp32(int sn, int rt, Condition cc = AL) + { + ASSERT(rt <= 15); + emitSinglePrecisionInstruction(toARMWord(cc) | VMOV_VFP32, rt << 1, sn, 0); + } + + void vmov_arm32(int rt, int sn, Condition cc = AL) + { + ASSERT(rt <= 15); + emitSinglePrecisionInstruction(toARMWord(cc) | VMOV_ARM32, rt << 1, sn, 0); + } + + void vcvt_f64_s32(int dd, int sm, Condition cc = AL) + { + ASSERT(!(sm & 0x1)); // sm must be divisible by 2 + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F64_S32, dd, 0, (sm >> 1)); + } + + void vcvt_s32_f64(int sd, int dm, Condition cc = AL) + { + ASSERT(!(sd & 0x1)); // sd must be divisible by 2 + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_S32_F64, (sd >> 1), 0, dm); + } + + void vcvt_u32_f64(int sd, int dm, Condition cc = AL) + { + ASSERT(!(sd & 0x1)); // sd must be divisible by 2 + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_U32_F64, (sd >> 1), 0, dm); + } + + void vcvt_f64_f32(int dd, int sm, Condition cc = AL) + { + ASSERT(dd <= 15 && sm <= 15); + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F64_F32, dd, 0, sm); + } + + void vcvt_f32_f64(int dd, int sm, Condition cc = AL) + { + ASSERT(dd <= 15 && sm <= 15); + emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F32_F64, dd, 0, sm); + } + + void vmrs_apsr(Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | VMRS_APSR); + } + + void clz(int rd, int rm, Condition cc = AL) + { + m_buffer.putInt(toARMWord(cc) | CLZ | RD(rd) | RM(rm)); + } + + void bkpt(ARMWord value) + { + m_buffer.putInt(BKPT | ((value & 0xff0) << 4) | (value & 0xf)); + } + + void nop() + { + m_buffer.putInt(NOP); + } + + void bx(int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | BX, 0, 0, RM(rm)); + } + + AssemblerLabel blx(int rm, Condition cc = AL) + { + emitInstruction(toARMWord(cc) | BLX, 0, 0, RM(rm)); + return m_buffer.label(); + } + + static ARMWord lsl(int reg, ARMWord value) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(value <= 0x1f); + return reg | (value << 7) | 0x00; + } + + static ARMWord lsr(int reg, ARMWord value) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(value <= 0x1f); + return reg | (value << 7) | 0x20; + } + + static ARMWord asr(int reg, ARMWord value) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(value <= 0x1f); + return reg | (value << 7) | 0x40; + } + + static ARMWord lslRegister(int reg, int shiftReg) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(shiftReg <= ARMRegisters::pc); + return reg | (shiftReg << 8) | 0x10; + } + + static ARMWord lsrRegister(int reg, int shiftReg) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(shiftReg <= ARMRegisters::pc); + return reg | (shiftReg << 8) | 0x30; + } + + static ARMWord asrRegister(int reg, int shiftReg) + { + ASSERT(reg <= ARMRegisters::pc); + ASSERT(shiftReg <= ARMRegisters::pc); + return reg | (shiftReg << 8) | 0x50; + } + + // General helpers + + size_t codeSize() const + { + return m_buffer.codeSize(); + } + + void ensureSpace(int insnSpace, int constSpace) + { + m_buffer.ensureSpace(insnSpace, constSpace); + } + + int sizeOfConstantPool() + { + return m_buffer.sizeOfConstantPool(); + } + + AssemblerLabel labelIgnoringWatchpoints() + { + m_buffer.ensureSpaceForAnyInstruction(); + return m_buffer.label(); + } + + AssemblerLabel labelForWatchpoint() + { + m_buffer.ensureSpaceForAnyInstruction(maxJumpReplacementSize() / sizeof(ARMWord)); + AssemblerLabel result = m_buffer.label(); + if (result.m_offset != (m_indexOfTailOfLastWatchpoint - maxJumpReplacementSize())) + result = label(); + m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); + return label(); + } + + AssemblerLabel label() + { + AssemblerLabel result = labelIgnoringWatchpoints(); + while (result.m_offset + 1 < m_indexOfTailOfLastWatchpoint) { + nop(); + // The available number of instructions are ensured by labelForWatchpoint. + result = m_buffer.label(); + } + return result; + } + + AssemblerLabel align(int alignment) + { + while (!m_buffer.isAligned(alignment)) + mov(ARMRegisters::r0, ARMRegisters::r0); + + return label(); + } + + AssemblerLabel loadBranchTarget(int rd, Condition cc = AL, int useConstantPool = 0) + { + ensureSpace(sizeof(ARMWord), sizeof(ARMWord)); + m_jumps.append(m_buffer.codeSize() | (useConstantPool & 0x1)); + ldrUniqueImmediate(rd, InvalidBranchTarget, cc); + return m_buffer.label(); + } + + AssemblerLabel jmp(Condition cc = AL, int useConstantPool = 0) + { + return loadBranchTarget(ARMRegisters::pc, cc, useConstantPool); + } + + PassRefPtr executableCopy(JSGlobalData&, void* ownerUID, JITCompilationEffort); + + unsigned debugOffset() { return m_buffer.debugOffset(); } + + // DFG assembly helpers for moving data between fp and registers. + void vmov(RegisterID rd1, RegisterID rd2, FPRegisterID rn) + { + vmov_arm64(rd1, rd2, rn); + } + + void vmov(FPRegisterID rd, RegisterID rn1, RegisterID rn2) + { + vmov_vfp64(rd, rn1, rn2); + } + + // Patching helpers + + static ARMWord* getLdrImmAddress(ARMWord* insn) + { + // Check for call + if ((*insn & LdrPcImmediateInstructionMask) != LdrPcImmediateInstruction) { + // Must be BLX + ASSERT((*insn & BlxInstructionMask) == BlxInstruction); + insn--; + } + + // Must be an ldr ..., [pc +/- imm] + ASSERT((*insn & LdrPcImmediateInstructionMask) == LdrPcImmediateInstruction); + + ARMWord addr = reinterpret_cast(insn) + DefaultPrefetchOffset * sizeof(ARMWord); + if (*insn & DataTransferUp) + return reinterpret_cast(addr + (*insn & DataTransferOffsetMask)); + return reinterpret_cast(addr - (*insn & DataTransferOffsetMask)); + } + + static ARMWord* getLdrImmAddressOnPool(ARMWord* insn, uint32_t* constPool) + { + // Must be an ldr ..., [pc +/- imm] + ASSERT((*insn & LdrPcImmediateInstructionMask) == LdrPcImmediateInstruction); + + if (*insn & 0x1) + return reinterpret_cast(constPool + ((*insn & DataTransferOffsetMask) >> 1)); + return getLdrImmAddress(insn); + } + + static void patchPointerInternal(intptr_t from, void* to) + { + ARMWord* insn = reinterpret_cast(from); + ARMWord* addr = getLdrImmAddress(insn); + *addr = reinterpret_cast(to); + } + + static ARMWord patchConstantPoolLoad(ARMWord load, ARMWord value) + { + value = (value << 1) + 1; + ASSERT(!(value & ~DataTransferOffsetMask)); + return (load & ~DataTransferOffsetMask) | value; + } + + static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr); + + // Read pointers + static void* readPointer(void* from) + { + ARMWord* instruction = reinterpret_cast(from); + ARMWord* address = getLdrImmAddress(instruction); + return *reinterpret_cast(address); + } + + // Patch pointers + + static void linkPointer(void* code, AssemblerLabel from, void* to) + { + patchPointerInternal(reinterpret_cast(code) + from.m_offset, to); + } + + static void repatchInt32(void* where, int32_t to) + { + patchPointerInternal(reinterpret_cast(where), reinterpret_cast(to)); + } + + static void repatchCompact(void* where, int32_t value) + { + ARMWord* instruction = reinterpret_cast(where); + ASSERT((*instruction & 0x0f700000) == LoadUint32); + if (value >= 0) + *instruction = (*instruction & 0xff7ff000) | DataTransferUp | value; + else + *instruction = (*instruction & 0xff7ff000) | -value; + cacheFlush(instruction, sizeof(ARMWord)); + } + + static void repatchPointer(void* from, void* to) + { + patchPointerInternal(reinterpret_cast(from), to); + } + + // Linkers + static intptr_t getAbsoluteJumpAddress(void* base, int offset = 0) + { + return reinterpret_cast(base) + offset - sizeof(ARMWord); + } + + void linkJump(AssemblerLabel from, AssemblerLabel to) + { + ARMWord* insn = reinterpret_cast(getAbsoluteJumpAddress(m_buffer.data(), from.m_offset)); + ARMWord* addr = getLdrImmAddressOnPool(insn, m_buffer.poolAddress()); + *addr = toARMWord(to.m_offset); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + patchPointerInternal(getAbsoluteJumpAddress(code, from.m_offset), to); + } + + static void relinkJump(void* from, void* to) + { + patchPointerInternal(getAbsoluteJumpAddress(from), to); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + patchPointerInternal(getAbsoluteJumpAddress(code, from.m_offset), to); + } + + static void relinkCall(void* from, void* to) + { + patchPointerInternal(getAbsoluteJumpAddress(from), to); + } + + static void* readCallTarget(void* from) + { + return reinterpret_cast(readPointer(reinterpret_cast(getAbsoluteJumpAddress(from)))); + } + + static void replaceWithJump(void* instructionStart, void* to) + { + ARMWord* instruction = reinterpret_cast(instructionStart) - 1; + intptr_t difference = reinterpret_cast(to) - (reinterpret_cast(instruction) + DefaultPrefetchOffset * sizeof(ARMWord)); + + if (!(difference & 1)) { + difference >>= 2; + if ((difference <= MaximumBranchOffsetDistance && difference >= MinimumBranchOffsetDistance)) { + // Direct branch. + instruction[0] = B | AL | (difference & BranchOffsetMask); + cacheFlush(instruction, sizeof(ARMWord)); + return; + } + } + + // Load target. + instruction[0] = LoadUint32 | AL | RN(ARMRegisters::pc) | RD(ARMRegisters::pc) | 4; + instruction[1] = reinterpret_cast(to); + cacheFlush(instruction, sizeof(ARMWord) * 2); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return sizeof(ARMWord) * 2; + } + + static void replaceWithLoad(void* instructionStart) + { + ARMWord* instruction = reinterpret_cast(instructionStart); + cacheFlush(instruction, sizeof(ARMWord)); + + ASSERT((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction || (*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction); + if ((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction) { + *instruction = (*instruction & ~LdrOrAddInstructionMask) | LdrImmediateInstruction; + cacheFlush(instruction, sizeof(ARMWord)); + } + } + + static void replaceWithAddressComputation(void* instructionStart) + { + ARMWord* instruction = reinterpret_cast(instructionStart); + cacheFlush(instruction, sizeof(ARMWord)); + + ASSERT((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction || (*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction); + if ((*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction) { + *instruction = (*instruction & ~LdrOrAddInstructionMask) | AddImmediateInstruction; + cacheFlush(instruction, sizeof(ARMWord)); + } + } + + // Address operations + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + // Address differences + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + return call.m_offset; + } + + // Handle immediates + + static ARMWord getOp2(ARMWord imm); + + // Fast case if imm is known to be between 0 and 0xff + static ARMWord getOp2Byte(ARMWord imm) + { + ASSERT(imm <= 0xff); + return Op2Immediate | imm; + } + + static ARMWord getOp2Half(ARMWord imm) + { + ASSERT(imm <= 0xff); + return ImmediateForHalfWordTransfer | (imm & 0x0f) | ((imm & 0xf0) << 4); + } + +#if WTF_ARM_ARCH_AT_LEAST(7) + static ARMWord getImm16Op2(ARMWord imm) + { + if (imm <= 0xffff) + return (imm & 0xf000) << 4 | (imm & 0xfff); + return InvalidImmediate; + } +#endif + ARMWord getImm(ARMWord imm, int tmpReg, bool invert = false); + void moveImm(ARMWord imm, int dest); + ARMWord encodeComplexImm(ARMWord imm, int dest); + + // Memory load/store helpers + + void dataTransfer32(DataTransferTypeA, RegisterID srcDst, RegisterID base, int32_t offset); + void baseIndexTransfer32(DataTransferTypeA, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); + void dataTransfer16(DataTransferTypeB, RegisterID srcDst, RegisterID base, int32_t offset); + void baseIndexTransfer16(DataTransferTypeB, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); + void dataTransferFloat(DataTransferTypeFloat, FPRegisterID srcDst, RegisterID base, int32_t offset); + void baseIndexTransferFloat(DataTransferTypeFloat, FPRegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset); + + // Constant pool hnadlers + + static ARMWord placeConstantPoolBarrier(int offset) + { + offset = (offset - sizeof(ARMWord)) >> 2; + ASSERT((offset <= MaximumBranchOffsetDistance && offset >= MinimumBranchOffsetDistance)); + return AL | B | (offset & BranchOffsetMask); + } + +#if OS(LINUX) && COMPILER(RVCT) + static __asm void cacheFlush(void* code, size_t); +#else + static void cacheFlush(void* code, size_t size) + { +#if OS(LINUX) && COMPILER(GCC) + uintptr_t currentPage = reinterpret_cast(code) & ~(pageSize() - 1); + uintptr_t lastPage = (reinterpret_cast(code) + size) & ~(pageSize() - 1); + do { + asm volatile( + "push {r7}\n" + "mov r0, %0\n" + "mov r1, %1\n" + "mov r7, #0xf0000\n" + "add r7, r7, #0x2\n" + "mov r2, #0x0\n" + "svc 0x0\n" + "pop {r7}\n" + : + : "r" (currentPage), "r" (currentPage + pageSize()) + : "r0", "r1", "r2"); + currentPage += pageSize(); + } while (lastPage >= currentPage); +#elif OS(WINCE) + CacheRangeFlush(code, size, CACHE_SYNC_ALL); +#elif OS(QNX) && ENABLE(ASSEMBLER_WX_EXCLUSIVE) + UNUSED_PARAM(code); + UNUSED_PARAM(size); +#elif OS(QNX) + msync(code, size, MS_INVALIDATE_ICACHE); +#else +#error "The cacheFlush support is missing on this platform." +#endif + } +#endif + + private: + static ARMWord RM(int reg) + { + ASSERT(reg <= ARMRegisters::pc); + return reg; + } + + static ARMWord RS(int reg) + { + ASSERT(reg <= ARMRegisters::pc); + return reg << 8; + } + + static ARMWord RD(int reg) + { + ASSERT(reg <= ARMRegisters::pc); + return reg << 12; + } + + static ARMWord RN(int reg) + { + ASSERT(reg <= ARMRegisters::pc); + return reg << 16; + } + + static ARMWord getConditionalField(ARMWord i) + { + return i & ConditionalFieldMask; + } + + static ARMWord toARMWord(Condition cc) + { + return static_cast(cc); + } + + static ARMWord toARMWord(uint32_t u) + { + return static_cast(u); + } + + int genInt(int reg, ARMWord imm, bool positive); + + ARMBuffer m_buffer; + Jumps m_jumps; + uint32_t m_indexOfTailOfLastWatchpoint; + }; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#endif // ARMAssembler_h diff --git a/src/3rdparty/masm/assembler/ARMv7Assembler.cpp b/src/3rdparty/masm/assembler/ARMv7Assembler.cpp new file mode 100644 index 0000000000..faca66421b --- /dev/null +++ b/src/3rdparty/masm/assembler/ARMv7Assembler.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) + +#include "ARMv7Assembler.h" + +namespace JSC { + +} + +#endif diff --git a/src/3rdparty/masm/assembler/ARMv7Assembler.h b/src/3rdparty/masm/assembler/ARMv7Assembler.h new file mode 100644 index 0000000000..b93ec6e63f --- /dev/null +++ b/src/3rdparty/masm/assembler/ARMv7Assembler.h @@ -0,0 +1,2706 @@ +/* + * Copyright (C) 2009, 2010, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2010 University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ARMAssembler_h +#define ARMAssembler_h + +#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) + +#include "AssemblerBuffer.h" +#include +#include +#include + +namespace JSC { + +namespace ARMRegisters { + typedef enum { + r0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, wr = r7, // thumb work register + r8, + r9, sb = r9, // static base + r10, sl = r10, // stack limit + r11, fp = r11, // frame pointer + r12, ip = r12, + r13, sp = r13, + r14, lr = r14, + r15, pc = r15, + } RegisterID; + + typedef enum { + s0, + s1, + s2, + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + s12, + s13, + s14, + s15, + s16, + s17, + s18, + s19, + s20, + s21, + s22, + s23, + s24, + s25, + s26, + s27, + s28, + s29, + s30, + s31, + } FPSingleRegisterID; + + typedef enum { + d0, + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + d14, + d15, + d16, + d17, + d18, + d19, + d20, + d21, + d22, + d23, + d24, + d25, + d26, + d27, + d28, + d29, + d30, + d31, + } FPDoubleRegisterID; + + typedef enum { + q0, + q1, + q2, + q3, + q4, + q5, + q6, + q7, + q8, + q9, + q10, + q11, + q12, + q13, + q14, + q15, + q16, + q17, + q18, + q19, + q20, + q21, + q22, + q23, + q24, + q25, + q26, + q27, + q28, + q29, + q30, + q31, + } FPQuadRegisterID; + + inline FPSingleRegisterID asSingle(FPDoubleRegisterID reg) + { + ASSERT(reg < d16); + return (FPSingleRegisterID)(reg << 1); + } + + inline FPDoubleRegisterID asDouble(FPSingleRegisterID reg) + { + ASSERT(!(reg & 1)); + return (FPDoubleRegisterID)(reg >> 1); + } +} + +class ARMv7Assembler; +class ARMThumbImmediate { + friend class ARMv7Assembler; + + typedef uint8_t ThumbImmediateType; + static const ThumbImmediateType TypeInvalid = 0; + static const ThumbImmediateType TypeEncoded = 1; + static const ThumbImmediateType TypeUInt16 = 2; + + typedef union { + int16_t asInt; + struct { + unsigned imm8 : 8; + unsigned imm3 : 3; + unsigned i : 1; + unsigned imm4 : 4; + }; + // If this is an encoded immediate, then it may describe a shift, or a pattern. + struct { + unsigned shiftValue7 : 7; + unsigned shiftAmount : 5; + }; + struct { + unsigned immediate : 8; + unsigned pattern : 4; + }; + } ThumbImmediateValue; + + // byte0 contains least significant bit; not using an array to make client code endian agnostic. + typedef union { + int32_t asInt; + struct { + uint8_t byte0; + uint8_t byte1; + uint8_t byte2; + uint8_t byte3; + }; + } PatternBytes; + + ALWAYS_INLINE static void countLeadingZerosPartial(uint32_t& value, int32_t& zeros, const int N) + { + if (value & ~((1 << N) - 1)) /* check for any of the top N bits (of 2N bits) are set */ + value >>= N; /* if any were set, lose the bottom N */ + else /* if none of the top N bits are set, */ + zeros += N; /* then we have identified N leading zeros */ + } + + static int32_t countLeadingZeros(uint32_t value) + { + if (!value) + return 32; + + int32_t zeros = 0; + countLeadingZerosPartial(value, zeros, 16); + countLeadingZerosPartial(value, zeros, 8); + countLeadingZerosPartial(value, zeros, 4); + countLeadingZerosPartial(value, zeros, 2); + countLeadingZerosPartial(value, zeros, 1); + return zeros; + } + + ARMThumbImmediate() + : m_type(TypeInvalid) + { + m_value.asInt = 0; + } + + ARMThumbImmediate(ThumbImmediateType type, ThumbImmediateValue value) + : m_type(type) + , m_value(value) + { + } + + ARMThumbImmediate(ThumbImmediateType type, uint16_t value) + : m_type(TypeUInt16) + { + // Make sure this constructor is only reached with type TypeUInt16; + // this extra parameter makes the code a little clearer by making it + // explicit at call sites which type is being constructed + ASSERT_UNUSED(type, type == TypeUInt16); + + m_value.asInt = value; + } + +public: + static ARMThumbImmediate makeEncodedImm(uint32_t value) + { + ThumbImmediateValue encoding; + encoding.asInt = 0; + + // okay, these are easy. + if (value < 256) { + encoding.immediate = value; + encoding.pattern = 0; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + int32_t leadingZeros = countLeadingZeros(value); + // if there were 24 or more leading zeros, then we'd have hit the (value < 256) case. + ASSERT(leadingZeros < 24); + + // Given a number with bit fields Z:B:C, where count(Z)+count(B)+count(C) == 32, + // Z are the bits known zero, B is the 8-bit immediate, C are the bits to check for + // zero. count(B) == 8, so the count of bits to be checked is 24 - count(Z). + int32_t rightShiftAmount = 24 - leadingZeros; + if (value == ((value >> rightShiftAmount) << rightShiftAmount)) { + // Shift the value down to the low byte position. The assign to + // shiftValue7 drops the implicit top bit. + encoding.shiftValue7 = value >> rightShiftAmount; + // The endoded shift amount is the magnitude of a right rotate. + encoding.shiftAmount = 8 + leadingZeros; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + PatternBytes bytes; + bytes.asInt = value; + + if ((bytes.byte0 == bytes.byte1) && (bytes.byte0 == bytes.byte2) && (bytes.byte0 == bytes.byte3)) { + encoding.immediate = bytes.byte0; + encoding.pattern = 3; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + if ((bytes.byte0 == bytes.byte2) && !(bytes.byte1 | bytes.byte3)) { + encoding.immediate = bytes.byte0; + encoding.pattern = 1; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + if ((bytes.byte1 == bytes.byte3) && !(bytes.byte0 | bytes.byte2)) { + encoding.immediate = bytes.byte1; + encoding.pattern = 2; + return ARMThumbImmediate(TypeEncoded, encoding); + } + + return ARMThumbImmediate(); + } + + static ARMThumbImmediate makeUInt12(int32_t value) + { + return (!(value & 0xfffff000)) + ? ARMThumbImmediate(TypeUInt16, (uint16_t)value) + : ARMThumbImmediate(); + } + + static ARMThumbImmediate makeUInt12OrEncodedImm(int32_t value) + { + // If this is not a 12-bit unsigned it, try making an encoded immediate. + return (!(value & 0xfffff000)) + ? ARMThumbImmediate(TypeUInt16, (uint16_t)value) + : makeEncodedImm(value); + } + + // The 'make' methods, above, return a !isValid() value if the argument + // cannot be represented as the requested type. This methods is called + // 'get' since the argument can always be represented. + static ARMThumbImmediate makeUInt16(uint16_t value) + { + return ARMThumbImmediate(TypeUInt16, value); + } + + bool isValid() + { + return m_type != TypeInvalid; + } + + uint16_t asUInt16() const { return m_value.asInt; } + + // These methods rely on the format of encoded byte values. + bool isUInt3() { return !(m_value.asInt & 0xfff8); } + bool isUInt4() { return !(m_value.asInt & 0xfff0); } + bool isUInt5() { return !(m_value.asInt & 0xffe0); } + bool isUInt6() { return !(m_value.asInt & 0xffc0); } + bool isUInt7() { return !(m_value.asInt & 0xff80); } + bool isUInt8() { return !(m_value.asInt & 0xff00); } + bool isUInt9() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xfe00); } + bool isUInt10() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xfc00); } + bool isUInt12() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xf000); } + bool isUInt16() { return m_type == TypeUInt16; } + uint8_t getUInt3() { ASSERT(isUInt3()); return m_value.asInt; } + uint8_t getUInt4() { ASSERT(isUInt4()); return m_value.asInt; } + uint8_t getUInt5() { ASSERT(isUInt5()); return m_value.asInt; } + uint8_t getUInt6() { ASSERT(isUInt6()); return m_value.asInt; } + uint8_t getUInt7() { ASSERT(isUInt7()); return m_value.asInt; } + uint8_t getUInt8() { ASSERT(isUInt8()); return m_value.asInt; } + uint16_t getUInt9() { ASSERT(isUInt9()); return m_value.asInt; } + uint16_t getUInt10() { ASSERT(isUInt10()); return m_value.asInt; } + uint16_t getUInt12() { ASSERT(isUInt12()); return m_value.asInt; } + uint16_t getUInt16() { ASSERT(isUInt16()); return m_value.asInt; } + + bool isEncodedImm() { return m_type == TypeEncoded; } + +private: + ThumbImmediateType m_type; + ThumbImmediateValue m_value; +}; + +typedef enum { + SRType_LSL, + SRType_LSR, + SRType_ASR, + SRType_ROR, + + SRType_RRX = SRType_ROR +} ARMShiftType; + +class ShiftTypeAndAmount { + friend class ARMv7Assembler; + +public: + ShiftTypeAndAmount() + { + m_u.type = (ARMShiftType)0; + m_u.amount = 0; + } + + ShiftTypeAndAmount(ARMShiftType type, unsigned amount) + { + m_u.type = type; + m_u.amount = amount & 31; + } + + unsigned lo4() { return m_u.lo4; } + unsigned hi4() { return m_u.hi4; } + +private: + union { + struct { + unsigned lo4 : 4; + unsigned hi4 : 4; + }; + struct { + unsigned type : 2; + unsigned amount : 6; + }; + } m_u; +}; + +class ARMv7Assembler { +public: + typedef ARMRegisters::RegisterID RegisterID; + typedef ARMRegisters::FPSingleRegisterID FPSingleRegisterID; + typedef ARMRegisters::FPDoubleRegisterID FPDoubleRegisterID; + typedef ARMRegisters::FPQuadRegisterID FPQuadRegisterID; + + // (HS, LO, HI, LS) -> (AE, B, A, BE) + // (VS, VC) -> (O, NO) + typedef enum { + ConditionEQ, + ConditionNE, + ConditionHS, ConditionCS = ConditionHS, + ConditionLO, ConditionCC = ConditionLO, + ConditionMI, + ConditionPL, + ConditionVS, + ConditionVC, + ConditionHI, + ConditionLS, + ConditionGE, + ConditionLT, + ConditionGT, + ConditionLE, + ConditionAL, + ConditionInvalid + } Condition; + +#define JUMP_ENUM_WITH_SIZE(index, value) (((value) << 3) | (index)) +#define JUMP_ENUM_SIZE(jump) ((jump) >> 3) + enum JumpType { JumpFixed = JUMP_ENUM_WITH_SIZE(0, 0), + JumpNoCondition = JUMP_ENUM_WITH_SIZE(1, 5 * sizeof(uint16_t)), + JumpCondition = JUMP_ENUM_WITH_SIZE(2, 6 * sizeof(uint16_t)), + JumpNoConditionFixedSize = JUMP_ENUM_WITH_SIZE(3, 5 * sizeof(uint16_t)), + JumpConditionFixedSize = JUMP_ENUM_WITH_SIZE(4, 6 * sizeof(uint16_t)) + }; + enum JumpLinkType { + LinkInvalid = JUMP_ENUM_WITH_SIZE(0, 0), + LinkJumpT1 = JUMP_ENUM_WITH_SIZE(1, sizeof(uint16_t)), + LinkJumpT2 = JUMP_ENUM_WITH_SIZE(2, sizeof(uint16_t)), + LinkJumpT3 = JUMP_ENUM_WITH_SIZE(3, 2 * sizeof(uint16_t)), + LinkJumpT4 = JUMP_ENUM_WITH_SIZE(4, 2 * sizeof(uint16_t)), + LinkConditionalJumpT4 = JUMP_ENUM_WITH_SIZE(5, 3 * sizeof(uint16_t)), + LinkBX = JUMP_ENUM_WITH_SIZE(6, 5 * sizeof(uint16_t)), + LinkConditionalBX = JUMP_ENUM_WITH_SIZE(7, 6 * sizeof(uint16_t)) + }; + + class LinkRecord { + public: + LinkRecord(intptr_t from, intptr_t to, JumpType type, Condition condition) + { + data.realTypes.m_from = from; + data.realTypes.m_to = to; + data.realTypes.m_type = type; + data.realTypes.m_linkType = LinkInvalid; + data.realTypes.m_condition = condition; + } + void operator=(const LinkRecord& other) + { + data.copyTypes.content[0] = other.data.copyTypes.content[0]; + data.copyTypes.content[1] = other.data.copyTypes.content[1]; + data.copyTypes.content[2] = other.data.copyTypes.content[2]; + } + intptr_t from() const { return data.realTypes.m_from; } + void setFrom(intptr_t from) { data.realTypes.m_from = from; } + intptr_t to() const { return data.realTypes.m_to; } + JumpType type() const { return data.realTypes.m_type; } + JumpLinkType linkType() const { return data.realTypes.m_linkType; } + void setLinkType(JumpLinkType linkType) { ASSERT(data.realTypes.m_linkType == LinkInvalid); data.realTypes.m_linkType = linkType; } + Condition condition() const { return data.realTypes.m_condition; } + private: + union { + struct RealTypes { + intptr_t m_from : 31; + intptr_t m_to : 31; + JumpType m_type : 8; + JumpLinkType m_linkType : 8; + Condition m_condition : 16; + } realTypes; + struct CopyTypes { + uint32_t content[3]; + } copyTypes; + COMPILE_ASSERT(sizeof(RealTypes) == sizeof(CopyTypes), LinkRecordCopyStructSizeEqualsRealStruct); + } data; + }; + + ARMv7Assembler() + : m_indexOfLastWatchpoint(INT_MIN) + , m_indexOfTailOfLastWatchpoint(INT_MIN) + { + } + +private: + + // ARMv7, Appx-A.6.3 + static bool BadReg(RegisterID reg) + { + return (reg == ARMRegisters::sp) || (reg == ARMRegisters::pc); + } + + uint32_t singleRegisterMask(FPSingleRegisterID rdNum, int highBitsShift, int lowBitShift) + { + uint32_t rdMask = (rdNum >> 1) << highBitsShift; + if (rdNum & 1) + rdMask |= 1 << lowBitShift; + return rdMask; + } + + uint32_t doubleRegisterMask(FPDoubleRegisterID rdNum, int highBitShift, int lowBitsShift) + { + uint32_t rdMask = (rdNum & 0xf) << lowBitsShift; + if (rdNum & 16) + rdMask |= 1 << highBitShift; + return rdMask; + } + + typedef enum { + OP_ADD_reg_T1 = 0x1800, + OP_SUB_reg_T1 = 0x1A00, + OP_ADD_imm_T1 = 0x1C00, + OP_SUB_imm_T1 = 0x1E00, + OP_MOV_imm_T1 = 0x2000, + OP_CMP_imm_T1 = 0x2800, + OP_ADD_imm_T2 = 0x3000, + OP_SUB_imm_T2 = 0x3800, + OP_AND_reg_T1 = 0x4000, + OP_EOR_reg_T1 = 0x4040, + OP_TST_reg_T1 = 0x4200, + OP_RSB_imm_T1 = 0x4240, + OP_CMP_reg_T1 = 0x4280, + OP_ORR_reg_T1 = 0x4300, + OP_MVN_reg_T1 = 0x43C0, + OP_ADD_reg_T2 = 0x4400, + OP_MOV_reg_T1 = 0x4600, + OP_BLX = 0x4700, + OP_BX = 0x4700, + OP_STR_reg_T1 = 0x5000, + OP_STRH_reg_T1 = 0x5200, + OP_STRB_reg_T1 = 0x5400, + OP_LDRSB_reg_T1 = 0x5600, + OP_LDR_reg_T1 = 0x5800, + OP_LDRH_reg_T1 = 0x5A00, + OP_LDRB_reg_T1 = 0x5C00, + OP_LDRSH_reg_T1 = 0x5E00, + OP_STR_imm_T1 = 0x6000, + OP_LDR_imm_T1 = 0x6800, + OP_STRB_imm_T1 = 0x7000, + OP_LDRB_imm_T1 = 0x7800, + OP_STRH_imm_T1 = 0x8000, + OP_LDRH_imm_T1 = 0x8800, + OP_STR_imm_T2 = 0x9000, + OP_LDR_imm_T2 = 0x9800, + OP_ADD_SP_imm_T1 = 0xA800, + OP_ADD_SP_imm_T2 = 0xB000, + OP_SUB_SP_imm_T1 = 0xB080, + OP_BKPT = 0xBE00, + OP_IT = 0xBF00, + OP_NOP_T1 = 0xBF00, + } OpcodeID; + + typedef enum { + OP_B_T1 = 0xD000, + OP_B_T2 = 0xE000, + OP_AND_reg_T2 = 0xEA00, + OP_TST_reg_T2 = 0xEA10, + OP_ORR_reg_T2 = 0xEA40, + OP_ORR_S_reg_T2 = 0xEA50, + OP_ASR_imm_T1 = 0xEA4F, + OP_LSL_imm_T1 = 0xEA4F, + OP_LSR_imm_T1 = 0xEA4F, + OP_ROR_imm_T1 = 0xEA4F, + OP_MVN_reg_T2 = 0xEA6F, + OP_EOR_reg_T2 = 0xEA80, + OP_ADD_reg_T3 = 0xEB00, + OP_ADD_S_reg_T3 = 0xEB10, + OP_SUB_reg_T2 = 0xEBA0, + OP_SUB_S_reg_T2 = 0xEBB0, + OP_CMP_reg_T2 = 0xEBB0, + OP_VMOV_CtoD = 0xEC00, + OP_VMOV_DtoC = 0xEC10, + OP_FSTS = 0xED00, + OP_VSTR = 0xED00, + OP_FLDS = 0xED10, + OP_VLDR = 0xED10, + OP_VMOV_CtoS = 0xEE00, + OP_VMOV_StoC = 0xEE10, + OP_VMUL_T2 = 0xEE20, + OP_VADD_T2 = 0xEE30, + OP_VSUB_T2 = 0xEE30, + OP_VDIV = 0xEE80, + OP_VABS_T2 = 0xEEB0, + OP_VCMP = 0xEEB0, + OP_VCVT_FPIVFP = 0xEEB0, + OP_VMOV_T2 = 0xEEB0, + OP_VMOV_IMM_T2 = 0xEEB0, + OP_VMRS = 0xEEB0, + OP_VNEG_T2 = 0xEEB0, + OP_VSQRT_T1 = 0xEEB0, + OP_VCVTSD_T1 = 0xEEB0, + OP_VCVTDS_T1 = 0xEEB0, + OP_B_T3a = 0xF000, + OP_B_T4a = 0xF000, + OP_AND_imm_T1 = 0xF000, + OP_TST_imm = 0xF010, + OP_ORR_imm_T1 = 0xF040, + OP_MOV_imm_T2 = 0xF040, + OP_MVN_imm = 0xF060, + OP_EOR_imm_T1 = 0xF080, + OP_ADD_imm_T3 = 0xF100, + OP_ADD_S_imm_T3 = 0xF110, + OP_CMN_imm = 0xF110, + OP_ADC_imm = 0xF140, + OP_SUB_imm_T3 = 0xF1A0, + OP_SUB_S_imm_T3 = 0xF1B0, + OP_CMP_imm_T2 = 0xF1B0, + OP_RSB_imm_T2 = 0xF1C0, + OP_RSB_S_imm_T2 = 0xF1D0, + OP_ADD_imm_T4 = 0xF200, + OP_MOV_imm_T3 = 0xF240, + OP_SUB_imm_T4 = 0xF2A0, + OP_MOVT = 0xF2C0, + OP_UBFX_T1 = 0xF3C0, + OP_NOP_T2a = 0xF3AF, + OP_STRB_imm_T3 = 0xF800, + OP_STRB_reg_T2 = 0xF800, + OP_LDRB_imm_T3 = 0xF810, + OP_LDRB_reg_T2 = 0xF810, + OP_STRH_imm_T3 = 0xF820, + OP_STRH_reg_T2 = 0xF820, + OP_LDRH_reg_T2 = 0xF830, + OP_LDRH_imm_T3 = 0xF830, + OP_STR_imm_T4 = 0xF840, + OP_STR_reg_T2 = 0xF840, + OP_LDR_imm_T4 = 0xF850, + OP_LDR_reg_T2 = 0xF850, + OP_STRB_imm_T2 = 0xF880, + OP_LDRB_imm_T2 = 0xF890, + OP_STRH_imm_T2 = 0xF8A0, + OP_LDRH_imm_T2 = 0xF8B0, + OP_STR_imm_T3 = 0xF8C0, + OP_LDR_imm_T3 = 0xF8D0, + OP_LDRSB_reg_T2 = 0xF910, + OP_LDRSH_reg_T2 = 0xF930, + OP_LSL_reg_T2 = 0xFA00, + OP_LSR_reg_T2 = 0xFA20, + OP_ASR_reg_T2 = 0xFA40, + OP_ROR_reg_T2 = 0xFA60, + OP_CLZ = 0xFAB0, + OP_SMULL_T1 = 0xFB80, + } OpcodeID1; + + typedef enum { + OP_VADD_T2b = 0x0A00, + OP_VDIVb = 0x0A00, + OP_FLDSb = 0x0A00, + OP_VLDRb = 0x0A00, + OP_VMOV_IMM_T2b = 0x0A00, + OP_VMOV_T2b = 0x0A40, + OP_VMUL_T2b = 0x0A00, + OP_FSTSb = 0x0A00, + OP_VSTRb = 0x0A00, + OP_VMOV_StoCb = 0x0A10, + OP_VMOV_CtoSb = 0x0A10, + OP_VMOV_DtoCb = 0x0A10, + OP_VMOV_CtoDb = 0x0A10, + OP_VMRSb = 0x0A10, + OP_VABS_T2b = 0x0A40, + OP_VCMPb = 0x0A40, + OP_VCVT_FPIVFPb = 0x0A40, + OP_VNEG_T2b = 0x0A40, + OP_VSUB_T2b = 0x0A40, + OP_VSQRT_T1b = 0x0A40, + OP_VCVTSD_T1b = 0x0A40, + OP_VCVTDS_T1b = 0x0A40, + OP_NOP_T2b = 0x8000, + OP_B_T3b = 0x8000, + OP_B_T4b = 0x9000, + } OpcodeID2; + + struct FourFours { + FourFours(unsigned f3, unsigned f2, unsigned f1, unsigned f0) + { + m_u.f0 = f0; + m_u.f1 = f1; + m_u.f2 = f2; + m_u.f3 = f3; + } + + union { + unsigned value; + struct { + unsigned f0 : 4; + unsigned f1 : 4; + unsigned f2 : 4; + unsigned f3 : 4; + }; + } m_u; + }; + + class ARMInstructionFormatter; + + // false means else! + bool ifThenElseConditionBit(Condition condition, bool isIf) + { + return isIf ? (condition & 1) : !(condition & 1); + } + uint8_t ifThenElse(Condition condition, bool inst2if, bool inst3if, bool inst4if) + { + int mask = (ifThenElseConditionBit(condition, inst2if) << 3) + | (ifThenElseConditionBit(condition, inst3if) << 2) + | (ifThenElseConditionBit(condition, inst4if) << 1) + | 1; + ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); + return (condition << 4) | mask; + } + uint8_t ifThenElse(Condition condition, bool inst2if, bool inst3if) + { + int mask = (ifThenElseConditionBit(condition, inst2if) << 3) + | (ifThenElseConditionBit(condition, inst3if) << 2) + | 2; + ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); + return (condition << 4) | mask; + } + uint8_t ifThenElse(Condition condition, bool inst2if) + { + int mask = (ifThenElseConditionBit(condition, inst2if) << 3) + | 4; + ASSERT((condition != ConditionAL) || !(mask & (mask - 1))); + return (condition << 4) | mask; + } + + uint8_t ifThenElse(Condition condition) + { + int mask = 8; + return (condition << 4) | mask; + } + +public: + + void adc(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isEncodedImm()); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADC_imm, rn, rd, imm); + } + + void add(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + + if (rn == ARMRegisters::sp) { + ASSERT(!(imm.getUInt16() & 3)); + if (!(rd & 8) && imm.isUInt10()) { + m_formatter.oneWordOp5Reg3Imm8(OP_ADD_SP_imm_T1, rd, static_cast(imm.getUInt10() >> 2)); + return; + } else if ((rd == ARMRegisters::sp) && imm.isUInt9()) { + m_formatter.oneWordOp9Imm7(OP_ADD_SP_imm_T2, static_cast(imm.getUInt9() >> 2)); + return; + } + } else if (!((rd | rn) & 8)) { + if (imm.isUInt3()) { + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); + return; + } else if ((rd == rn) && imm.isUInt8()) { + m_formatter.oneWordOp5Reg3Imm8(OP_ADD_imm_T2, rd, imm.getUInt8()); + return; + } + } + + if (imm.isEncodedImm()) + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_imm_T3, rn, rd, imm); + else { + ASSERT(imm.isUInt12()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_imm_T4, rn, rd, imm); + } + } + + ALWAYS_INLINE void add(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ADD_reg_T3, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // NOTE: In an IT block, add doesn't modify the flags register. + ALWAYS_INLINE void add(RegisterID rd, RegisterID rn, RegisterID rm) + { + if (rd == rn) + m_formatter.oneWordOp8RegReg143(OP_ADD_reg_T2, rm, rd); + else if (rd == rm) + m_formatter.oneWordOp8RegReg143(OP_ADD_reg_T2, rn, rd); + else if (!((rd | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_reg_T1, rm, rn, rd); + else + add(rd, rn, rm, ShiftTypeAndAmount()); + } + + // Not allowed in an IT (if then) block. + ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isEncodedImm()); + + if (!((rd | rn) & 8)) { + if (imm.isUInt3()) { + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); + return; + } else if ((rd == rn) && imm.isUInt8()) { + m_formatter.oneWordOp5Reg3Imm8(OP_ADD_imm_T2, rd, imm.getUInt8()); + return; + } + } + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_S_imm_T3, rn, rd, imm); + } + + // Not allowed in an IT (if then) block? + ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ADD_S_reg_T3, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // Not allowed in an IT (if then) block. + ALWAYS_INLINE void add_S(RegisterID rd, RegisterID rn, RegisterID rm) + { + if (!((rd | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_reg_T1, rm, rn, rd); + else + add_S(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(imm.isEncodedImm()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_AND_imm_T1, rn, rd, imm); + } + + ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_AND_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void ARM_and(RegisterID rd, RegisterID rn, RegisterID rm) + { + if ((rd == rn) && !((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_AND_reg_T1, rm, rd); + else if ((rd == rm) && !((rd | rn) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_AND_reg_T1, rn, rd); + else + ARM_and(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void asr(RegisterID rd, RegisterID rm, int32_t shiftAmount) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + ShiftTypeAndAmount shift(SRType_ASR, shiftAmount); + m_formatter.twoWordOp16FourFours(OP_ASR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void asr(RegisterID rd, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ASR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); + } + + // Only allowed in IT (if then) block if last instruction. + ALWAYS_INLINE AssemblerLabel b() + { + m_formatter.twoWordOp16Op16(OP_B_T4a, OP_B_T4b); + return m_formatter.label(); + } + + // Only allowed in IT (if then) block if last instruction. + ALWAYS_INLINE AssemblerLabel blx(RegisterID rm) + { + ASSERT(rm != ARMRegisters::pc); + m_formatter.oneWordOp8RegReg143(OP_BLX, rm, (RegisterID)8); + return m_formatter.label(); + } + + // Only allowed in IT (if then) block if last instruction. + ALWAYS_INLINE AssemblerLabel bx(RegisterID rm) + { + m_formatter.oneWordOp8RegReg143(OP_BX, rm, (RegisterID)0); + return m_formatter.label(); + } + + void bkpt(uint8_t imm = 0) + { + m_formatter.oneWordOp8Imm8(OP_BKPT, imm); + } + + ALWAYS_INLINE void clz(RegisterID rd, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_CLZ, rm, FourFours(0xf, rd, 8, rm)); + } + + ALWAYS_INLINE void cmn(RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isEncodedImm()); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_CMN_imm, rn, (RegisterID)0xf, imm); + } + + ALWAYS_INLINE void cmp(RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isEncodedImm()); + + if (!(rn & 8) && imm.isUInt8()) + m_formatter.oneWordOp5Reg3Imm8(OP_CMP_imm_T1, rn, imm.getUInt8()); + else + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_CMP_imm_T2, rn, (RegisterID)0xf, imm); + } + + ALWAYS_INLINE void cmp(RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_CMP_reg_T2, rn, FourFours(shift.hi4(), 0xf, shift.lo4(), rm)); + } + + ALWAYS_INLINE void cmp(RegisterID rn, RegisterID rm) + { + if ((rn | rm) & 8) + cmp(rn, rm, ShiftTypeAndAmount()); + else + m_formatter.oneWordOp10Reg3Reg3(OP_CMP_reg_T1, rm, rn); + } + + // xor is not spelled with an 'e'. :-( + ALWAYS_INLINE void eor(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(imm.isEncodedImm()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_EOR_imm_T1, rn, rd, imm); + } + + // xor is not spelled with an 'e'. :-( + ALWAYS_INLINE void eor(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_EOR_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // xor is not spelled with an 'e'. :-( + void eor(RegisterID rd, RegisterID rn, RegisterID rm) + { + if ((rd == rn) && !((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_EOR_reg_T1, rm, rd); + else if ((rd == rm) && !((rd | rn) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_EOR_reg_T1, rn, rd); + else + eor(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void it(Condition cond) + { + m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond)); + } + + ALWAYS_INLINE void it(Condition cond, bool inst2if) + { + m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if)); + } + + ALWAYS_INLINE void it(Condition cond, bool inst2if, bool inst3if) + { + m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if, inst3if)); + } + + ALWAYS_INLINE void it(Condition cond, bool inst2if, bool inst3if, bool inst4if) + { + m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if, inst3if, inst4if)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt7()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDR_imm_T1, imm.getUInt7() >> 2, rn, rt); + else if ((rn == ARMRegisters::sp) && !(rt & 8) && imm.isUInt10()) + m_formatter.oneWordOp5Reg3Imm8(OP_LDR_imm_T2, rt, static_cast(imm.getUInt10() >> 2)); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T3, rn, rt, imm.getUInt12()); + } + + ALWAYS_INLINE void ldrWide8BitImmediate(RegisterID rt, RegisterID rn, uint8_t immediate) + { + ASSERT(rn != ARMRegisters::pc); + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T3, rn, rt, immediate); + } + + ALWAYS_INLINE void ldrCompact(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(imm.isUInt7()); + ASSERT(!((rt | rn) & 8)); + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDR_imm_T1, imm.getUInt7() >> 2, rn, rt); + } + + // If index is set, this is a regular offset or a pre-indexed load; + // if index is not set then is is a post-index load. + // + // If wback is set rn is updated - this is a pre or post index load, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT((offset & ~0xff) == 0); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T4, rn, rt, offset); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void ldr(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDR_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDR_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt6()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDRH_imm_T1, imm.getUInt6() >> 2, rn, rt); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRH_imm_T2, rn, rt, imm.getUInt12()); + } + + // If index is set, this is a regular offset or a pre-indexed load; + // if index is not set then is is a post-index load. + // + // If wback is set rn is updated - this is a pre or post index load, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT((offset & ~0xff) == 0); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRH_imm_T3, rn, rt, offset); + } + + ALWAYS_INLINE void ldrh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(!BadReg(rt)); // Memory hint + ASSERT(rn != ARMRegisters::pc); // LDRH (literal) + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRH_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDRH_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + void ldrb(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt5()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDRB_imm_T1, imm.getUInt5(), rn, rt); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRB_imm_T2, rn, rt, imm.getUInt12()); + } + + void ldrb(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + + ASSERT(!(offset & ~0xff)); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRB_imm_T3, rn, rt, offset); + } + + ALWAYS_INLINE void ldrb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); // LDR (literal) + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRB_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDRB_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + void ldrsb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRSB_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDRSB_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + void ldrsh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRSH_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_LDRSH_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + void lsl(RegisterID rd, RegisterID rm, int32_t shiftAmount) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + ShiftTypeAndAmount shift(SRType_LSL, shiftAmount); + m_formatter.twoWordOp16FourFours(OP_LSL_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void lsl(RegisterID rd, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_LSL_reg_T2, rn, FourFours(0xf, rd, 0, rm)); + } + + ALWAYS_INLINE void lsr(RegisterID rd, RegisterID rm, int32_t shiftAmount) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + ShiftTypeAndAmount shift(SRType_LSR, shiftAmount); + m_formatter.twoWordOp16FourFours(OP_LSR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void lsr(RegisterID rd, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_LSR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); + } + + ALWAYS_INLINE void movT3(RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isValid()); + ASSERT(!imm.isEncodedImm()); + ASSERT(!BadReg(rd)); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOV_imm_T3, imm.m_value.imm4, rd, imm); + } + + static void revertJumpTo_movT3(void* instructionStart, RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isValid()); + ASSERT(!imm.isEncodedImm()); + ASSERT(!BadReg(rd)); + + uint16_t* address = static_cast(instructionStart); + address[0] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, imm); + address[1] = twoWordOp5i6Imm4Reg4EncodedImmSecond(rd, imm); + cacheFlush(address, sizeof(uint16_t) * 2); + } + + ALWAYS_INLINE void mov(RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isValid()); + ASSERT(!BadReg(rd)); + + if ((rd < 8) && imm.isUInt8()) + m_formatter.oneWordOp5Reg3Imm8(OP_MOV_imm_T1, rd, imm.getUInt8()); + else if (imm.isEncodedImm()) + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOV_imm_T2, 0xf, rd, imm); + else + movT3(rd, imm); + } + + ALWAYS_INLINE void mov(RegisterID rd, RegisterID rm) + { + m_formatter.oneWordOp8RegReg143(OP_MOV_reg_T1, rm, rd); + } + + ALWAYS_INLINE void movt(RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isUInt16()); + ASSERT(!BadReg(rd)); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOVT, imm.m_value.imm4, rd, imm); + } + + ALWAYS_INLINE void mvn(RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isEncodedImm()); + ASSERT(!BadReg(rd)); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MVN_imm, 0xf, rd, imm); + } + + ALWAYS_INLINE void mvn(RegisterID rd, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp16FourFours(OP_MVN_reg_T2, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void mvn(RegisterID rd, RegisterID rm) + { + if (!((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_MVN_reg_T1, rm, rd); + else + mvn(rd, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void neg(RegisterID rd, RegisterID rm) + { + ARMThumbImmediate zero = ARMThumbImmediate::makeUInt12(0); + sub(rd, zero, rm); + } + + ALWAYS_INLINE void orr(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(imm.isEncodedImm()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ORR_imm_T1, rn, rd, imm); + } + + ALWAYS_INLINE void orr(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ORR_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + void orr(RegisterID rd, RegisterID rn, RegisterID rm) + { + if ((rd == rn) && !((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rm, rd); + else if ((rd == rm) && !((rd | rn) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rn, rd); + else + orr(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void orr_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ORR_S_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + void orr_S(RegisterID rd, RegisterID rn, RegisterID rm) + { + if ((rd == rn) && !((rd | rm) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rm, rd); + else if ((rd == rm) && !((rd | rn) & 8)) + m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rn, rd); + else + orr_S(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void ror(RegisterID rd, RegisterID rm, int32_t shiftAmount) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rm)); + ShiftTypeAndAmount shift(SRType_ROR, shiftAmount); + m_formatter.twoWordOp16FourFours(OP_ROR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + ALWAYS_INLINE void ror(RegisterID rd, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rd)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_ROR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); + } + + ALWAYS_INLINE void smull(RegisterID rdLo, RegisterID rdHi, RegisterID rn, RegisterID rm) + { + ASSERT(!BadReg(rdLo)); + ASSERT(!BadReg(rdHi)); + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + ASSERT(rdLo != rdHi); + m_formatter.twoWordOp12Reg4FourFours(OP_SMULL_T1, rn, FourFours(rdLo, rdHi, 0, rm)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt7()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STR_imm_T1, imm.getUInt7() >> 2, rn, rt); + else if ((rn == ARMRegisters::sp) && !(rt & 8) && imm.isUInt10()) + m_formatter.oneWordOp5Reg3Imm8(OP_STR_imm_T2, rt, static_cast(imm.getUInt10() >> 2)); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STR_imm_T3, rn, rt, imm.getUInt12()); + } + + // If index is set, this is a regular offset or a pre-indexed store; + // if index is not set then is is a post-index store. + // + // If wback is set rn is updated - this is a pre or post index store, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT((offset & ~0xff) == 0); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STR_imm_T4, rn, rt, offset); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STR_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_STR_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt7()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STRB_imm_T1, imm.getUInt7() >> 2, rn, rt); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRB_imm_T2, rn, rt, imm.getUInt12()); + } + + // If index is set, this is a regular offset or a pre-indexed store; + // if index is not set then is is a post-index store. + // + // If wback is set rn is updated - this is a pre or post index store, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT((offset & ~0xff) == 0); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRB_imm_T3, rn, rt, offset); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void strb(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STRB_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_STRB_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isUInt12()); + + if (!((rt | rn) & 8) && imm.isUInt7()) + m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STRH_imm_T1, imm.getUInt7() >> 2, rn, rt); + else + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRH_imm_T2, rn, rt, imm.getUInt12()); + } + + // If index is set, this is a regular offset or a pre-indexed store; + // if index is not set then is is a post-index store. + // + // If wback is set rn is updated - this is a pre or post index store, + // if wback is not set this is a regular offset memory access. + // + // (-255 <= offset <= 255) + // _reg = REG[rn] + // _tmp = _reg + offset + // MEM[index ? _tmp : _reg] = REG[rt] + // if (wback) REG[rn] = _tmp + ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) + { + ASSERT(rt != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(index || wback); + ASSERT(!wback | (rt != rn)); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + ASSERT(!(offset & ~0xff)); + + offset |= (wback << 8); + offset |= (add << 9); + offset |= (index << 10); + offset |= (1 << 11); + + m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STRH_imm_T3, rn, rt, offset); + } + + // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. + ALWAYS_INLINE void strh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift = 0) + { + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + ASSERT(shift <= 3); + + if (!shift && !((rt | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STRH_reg_T1, rm, rn, rt); + else + m_formatter.twoWordOp12Reg4FourFours(OP_STRH_reg_T2, rn, FourFours(rt, 0, shift, rm)); + } + + ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + + if ((rn == ARMRegisters::sp) && (rd == ARMRegisters::sp) && imm.isUInt9()) { + ASSERT(!(imm.getUInt16() & 3)); + m_formatter.oneWordOp9Imm7(OP_SUB_SP_imm_T1, static_cast(imm.getUInt9() >> 2)); + return; + } else if (!((rd | rn) & 8)) { + if (imm.isUInt3()) { + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); + return; + } else if ((rd == rn) && imm.isUInt8()) { + m_formatter.oneWordOp5Reg3Imm8(OP_SUB_imm_T2, rd, imm.getUInt8()); + return; + } + } + + if (imm.isEncodedImm()) + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_imm_T3, rn, rd, imm); + else { + ASSERT(imm.isUInt12()); + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_imm_T4, rn, rd, imm); + } + } + + ALWAYS_INLINE void sub(RegisterID rd, ARMThumbImmediate imm, RegisterID rn) + { + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + ASSERT(imm.isUInt12()); + + if (!((rd | rn) & 8) && !imm.getUInt12()) + m_formatter.oneWordOp10Reg3Reg3(OP_RSB_imm_T1, rn, rd); + else + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_RSB_imm_T2, rn, rd, imm); + } + + ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_SUB_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // NOTE: In an IT block, add doesn't modify the flags register. + ALWAYS_INLINE void sub(RegisterID rd, RegisterID rn, RegisterID rm) + { + if (!((rd | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_reg_T1, rm, rn, rd); + else + sub(rd, rn, rm, ShiftTypeAndAmount()); + } + + // Not allowed in an IT (if then) block. + void sub_S(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) + { + // Rd can only be SP if Rn is also SP. + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + + if ((rn == ARMRegisters::sp) && (rd == ARMRegisters::sp) && imm.isUInt9()) { + ASSERT(!(imm.getUInt16() & 3)); + m_formatter.oneWordOp9Imm7(OP_SUB_SP_imm_T1, static_cast(imm.getUInt9() >> 2)); + return; + } else if (!((rd | rn) & 8)) { + if (imm.isUInt3()) { + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); + return; + } else if ((rd == rn) && imm.isUInt8()) { + m_formatter.oneWordOp5Reg3Imm8(OP_SUB_imm_T2, rd, imm.getUInt8()); + return; + } + } + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_S_imm_T3, rn, rd, imm); + } + + ALWAYS_INLINE void sub_S(RegisterID rd, ARMThumbImmediate imm, RegisterID rn) + { + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(imm.isValid()); + ASSERT(imm.isUInt12()); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_RSB_S_imm_T2, rn, rd, imm); + } + + // Not allowed in an IT (if then) block? + ALWAYS_INLINE void sub_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); + ASSERT(rd != ARMRegisters::pc); + ASSERT(rn != ARMRegisters::pc); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_SUB_S_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); + } + + // Not allowed in an IT (if then) block. + ALWAYS_INLINE void sub_S(RegisterID rd, RegisterID rn, RegisterID rm) + { + if (!((rd | rn | rm) & 8)) + m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_reg_T1, rm, rn, rd); + else + sub_S(rd, rn, rm, ShiftTypeAndAmount()); + } + + ALWAYS_INLINE void tst(RegisterID rn, ARMThumbImmediate imm) + { + ASSERT(!BadReg(rn)); + ASSERT(imm.isEncodedImm()); + + m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_TST_imm, rn, (RegisterID)0xf, imm); + } + + ALWAYS_INLINE void tst(RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) + { + ASSERT(!BadReg(rn)); + ASSERT(!BadReg(rm)); + m_formatter.twoWordOp12Reg4FourFours(OP_TST_reg_T2, rn, FourFours(shift.hi4(), 0xf, shift.lo4(), rm)); + } + + ALWAYS_INLINE void tst(RegisterID rn, RegisterID rm) + { + if ((rn | rm) & 8) + tst(rn, rm, ShiftTypeAndAmount()); + else + m_formatter.oneWordOp10Reg3Reg3(OP_TST_reg_T1, rm, rn); + } + + ALWAYS_INLINE void ubfx(RegisterID rd, RegisterID rn, unsigned lsb, unsigned width) + { + ASSERT(lsb < 32); + ASSERT((width >= 1) && (width <= 32)); + ASSERT((lsb + width) <= 32); + m_formatter.twoWordOp12Reg40Imm3Reg4Imm20Imm5(OP_UBFX_T1, rd, rn, (lsb & 0x1c) << 10, (lsb & 0x3) << 6, (width - 1) & 0x1f); + } + + void vadd(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VADD_T2, OP_VADD_T2b, true, rn, rd, rm); + } + + void vcmp(FPDoubleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VCMP, OP_VCMPb, true, VFPOperand(4), rd, rm); + } + + void vcmpz(FPDoubleRegisterID rd) + { + m_formatter.vfpOp(OP_VCMP, OP_VCMPb, true, VFPOperand(5), rd, VFPOperand(0)); + } + + void vcvt_signedToFloatingPoint(FPDoubleRegisterID rd, FPSingleRegisterID rm) + { + // boolean values are 64bit (toInt, unsigned, roundZero) + m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(false, false, false), rd, rm); + } + + void vcvt_floatingPointToSigned(FPSingleRegisterID rd, FPDoubleRegisterID rm) + { + // boolean values are 64bit (toInt, unsigned, roundZero) + m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(true, false, true), rd, rm); + } + + void vcvt_floatingPointToUnsigned(FPSingleRegisterID rd, FPDoubleRegisterID rm) + { + // boolean values are 64bit (toInt, unsigned, roundZero) + m_formatter.vfpOp(OP_VCVT_FPIVFP, OP_VCVT_FPIVFPb, true, vcvtOp(true, true, true), rd, rm); + } + + void vdiv(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VDIV, OP_VDIVb, true, rn, rd, rm); + } + + void vldr(FPDoubleRegisterID rd, RegisterID rn, int32_t imm) + { + m_formatter.vfpMemOp(OP_VLDR, OP_VLDRb, true, rn, rd, imm); + } + + void flds(FPSingleRegisterID rd, RegisterID rn, int32_t imm) + { + m_formatter.vfpMemOp(OP_FLDS, OP_FLDSb, false, rn, rd, imm); + } + + void vmov(RegisterID rd, FPSingleRegisterID rn) + { + ASSERT(!BadReg(rd)); + m_formatter.vfpOp(OP_VMOV_StoC, OP_VMOV_StoCb, false, rn, rd, VFPOperand(0)); + } + + void vmov(FPSingleRegisterID rd, RegisterID rn) + { + ASSERT(!BadReg(rn)); + m_formatter.vfpOp(OP_VMOV_CtoS, OP_VMOV_CtoSb, false, rd, rn, VFPOperand(0)); + } + + void vmov(RegisterID rd1, RegisterID rd2, FPDoubleRegisterID rn) + { + ASSERT(!BadReg(rd1)); + ASSERT(!BadReg(rd2)); + m_formatter.vfpOp(OP_VMOV_DtoC, OP_VMOV_DtoCb, true, rd2, VFPOperand(rd1 | 16), rn); + } + + void vmov(FPDoubleRegisterID rd, RegisterID rn1, RegisterID rn2) + { + ASSERT(!BadReg(rn1)); + ASSERT(!BadReg(rn2)); + m_formatter.vfpOp(OP_VMOV_CtoD, OP_VMOV_CtoDb, true, rn2, VFPOperand(rn1 | 16), rd); + } + + void vmov(FPDoubleRegisterID rd, FPDoubleRegisterID rn) + { + m_formatter.vfpOp(OP_VMOV_T2, OP_VMOV_T2b, true, VFPOperand(0), rd, rn); + } + + void vmrs(RegisterID reg = ARMRegisters::pc) + { + ASSERT(reg != ARMRegisters::sp); + m_formatter.vfpOp(OP_VMRS, OP_VMRSb, false, VFPOperand(1), VFPOperand(0x10 | reg), VFPOperand(0)); + } + + void vmul(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VMUL_T2, OP_VMUL_T2b, true, rn, rd, rm); + } + + void vstr(FPDoubleRegisterID rd, RegisterID rn, int32_t imm) + { + m_formatter.vfpMemOp(OP_VSTR, OP_VSTRb, true, rn, rd, imm); + } + + void fsts(FPSingleRegisterID rd, RegisterID rn, int32_t imm) + { + m_formatter.vfpMemOp(OP_FSTS, OP_FSTSb, false, rn, rd, imm); + } + + void vsub(FPDoubleRegisterID rd, FPDoubleRegisterID rn, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VSUB_T2, OP_VSUB_T2b, true, rn, rd, rm); + } + + void vabs(FPDoubleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VABS_T2, OP_VABS_T2b, true, VFPOperand(16), rd, rm); + } + + void vneg(FPDoubleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VNEG_T2, OP_VNEG_T2b, true, VFPOperand(1), rd, rm); + } + + void vsqrt(FPDoubleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VSQRT_T1, OP_VSQRT_T1b, true, VFPOperand(17), rd, rm); + } + + void vcvtds(FPDoubleRegisterID rd, FPSingleRegisterID rm) + { + m_formatter.vfpOp(OP_VCVTDS_T1, OP_VCVTDS_T1b, false, VFPOperand(23), rd, rm); + } + + void vcvtsd(FPSingleRegisterID rd, FPDoubleRegisterID rm) + { + m_formatter.vfpOp(OP_VCVTSD_T1, OP_VCVTSD_T1b, true, VFPOperand(23), rd, rm); + } + + void nop() + { + m_formatter.oneWordOp8Imm8(OP_NOP_T1, 0); + } + + AssemblerLabel labelIgnoringWatchpoints() + { + return m_formatter.label(); + } + + AssemblerLabel labelForWatchpoint() + { + AssemblerLabel result = m_formatter.label(); + if (static_cast(result.m_offset) != m_indexOfLastWatchpoint) + result = label(); + m_indexOfLastWatchpoint = result.m_offset; + m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); + return result; + } + + AssemblerLabel label() + { + AssemblerLabel result = m_formatter.label(); + while (UNLIKELY(static_cast(result.m_offset) < m_indexOfTailOfLastWatchpoint)) { + nop(); + result = m_formatter.label(); + } + return result; + } + + AssemblerLabel align(int alignment) + { + while (!m_formatter.isAligned(alignment)) + bkpt(); + + return label(); + } + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + ASSERT(label.isSet()); + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + int executableOffsetFor(int location) + { + if (!location) + return 0; + return static_cast(m_formatter.data())[location / sizeof(int32_t) - 1]; + } + + int jumpSizeDelta(JumpType jumpType, JumpLinkType jumpLinkType) { return JUMP_ENUM_SIZE(jumpType) - JUMP_ENUM_SIZE(jumpLinkType); } + + // Assembler admin methods: + + static ALWAYS_INLINE bool linkRecordSourceComparator(const LinkRecord& a, const LinkRecord& b) + { + return a.from() < b.from(); + } + + bool canCompact(JumpType jumpType) + { + // The following cannot be compacted: + // JumpFixed: represents custom jump sequence + // JumpNoConditionFixedSize: represents unconditional jump that must remain a fixed size + // JumpConditionFixedSize: represents conditional jump that must remain a fixed size + return (jumpType == JumpNoCondition) || (jumpType == JumpCondition); + } + + JumpLinkType computeJumpType(JumpType jumpType, const uint8_t* from, const uint8_t* to) + { + if (jumpType == JumpFixed) + return LinkInvalid; + + // for patchable jump we must leave space for the longest code sequence + if (jumpType == JumpNoConditionFixedSize) + return LinkBX; + if (jumpType == JumpConditionFixedSize) + return LinkConditionalBX; + + const int paddingSize = JUMP_ENUM_SIZE(jumpType); + + if (jumpType == JumpCondition) { + // 2-byte conditional T1 + const uint16_t* jumpT1Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT1))); + if (canBeJumpT1(jumpT1Location, to)) + return LinkJumpT1; + // 4-byte conditional T3 + const uint16_t* jumpT3Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT3))); + if (canBeJumpT3(jumpT3Location, to)) + return LinkJumpT3; + // 4-byte conditional T4 with IT + const uint16_t* conditionalJumpT4Location = + reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkConditionalJumpT4))); + if (canBeJumpT4(conditionalJumpT4Location, to)) + return LinkConditionalJumpT4; + } else { + // 2-byte unconditional T2 + const uint16_t* jumpT2Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT2))); + if (canBeJumpT2(jumpT2Location, to)) + return LinkJumpT2; + // 4-byte unconditional T4 + const uint16_t* jumpT4Location = reinterpret_cast_ptr(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT4))); + if (canBeJumpT4(jumpT4Location, to)) + return LinkJumpT4; + // use long jump sequence + return LinkBX; + } + + ASSERT(jumpType == JumpCondition); + return LinkConditionalBX; + } + + JumpLinkType computeJumpType(LinkRecord& record, const uint8_t* from, const uint8_t* to) + { + JumpLinkType linkType = computeJumpType(record.type(), from, to); + record.setLinkType(linkType); + return linkType; + } + + void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset) + { + int32_t ptr = regionStart / sizeof(int32_t); + const int32_t end = regionEnd / sizeof(int32_t); + int32_t* offsets = static_cast(m_formatter.data()); + while (ptr < end) + offsets[ptr++] = offset; + } + + Vector& jumpsToLink() + { + std::sort(m_jumpsToLink.begin(), m_jumpsToLink.end(), linkRecordSourceComparator); + return m_jumpsToLink; + } + + void ALWAYS_INLINE link(LinkRecord& record, uint8_t* from, uint8_t* to) + { + switch (record.linkType()) { + case LinkJumpT1: + linkJumpT1(record.condition(), reinterpret_cast_ptr(from), to); + break; + case LinkJumpT2: + linkJumpT2(reinterpret_cast_ptr(from), to); + break; + case LinkJumpT3: + linkJumpT3(record.condition(), reinterpret_cast_ptr(from), to); + break; + case LinkJumpT4: + linkJumpT4(reinterpret_cast_ptr(from), to); + break; + case LinkConditionalJumpT4: + linkConditionalJumpT4(record.condition(), reinterpret_cast_ptr(from), to); + break; + case LinkConditionalBX: + linkConditionalBX(record.condition(), reinterpret_cast_ptr(from), to); + break; + case LinkBX: + linkBX(reinterpret_cast_ptr(from), to); + break; + default: + ASSERT_NOT_REACHED(); + break; + } + } + + void* unlinkedCode() { return m_formatter.data(); } + size_t codeSize() const { return m_formatter.codeSize(); } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + ASSERT(call.isSet()); + return call.m_offset; + } + + // Linking & patching: + // + // 'link' and 'patch' methods are for use on unprotected code - such as the code + // within the AssemblerBuffer, and code being patched by the patch buffer. Once + // code has been finalized it is (platform support permitting) within a non- + // writable region of memory; to modify the code in an execute-only execuable + // pool the 'repatch' and 'relink' methods should be used. + + void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type, Condition condition) + { + ASSERT(to.isSet()); + ASSERT(from.isSet()); + m_jumpsToLink.append(LinkRecord(from.m_offset, to.m_offset, type, condition)); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + + uint16_t* location = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + linkJumpAbsolute(location, to); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + ASSERT(!(reinterpret_cast(code) & 1)); + ASSERT(from.isSet()); + ASSERT(reinterpret_cast(to) & 1); + + setPointer(reinterpret_cast(reinterpret_cast(code) + from.m_offset) - 1, to, false); + } + + static void linkPointer(void* code, AssemblerLabel where, void* value) + { + setPointer(reinterpret_cast(code) + where.m_offset, value, false); + } + + static void relinkJump(void* from, void* to) + { + ASSERT(!(reinterpret_cast(from) & 1)); + ASSERT(!(reinterpret_cast(to) & 1)); + + linkJumpAbsolute(reinterpret_cast(from), to); + + cacheFlush(reinterpret_cast(from) - 5, 5 * sizeof(uint16_t)); + } + + static void relinkCall(void* from, void* to) + { + ASSERT(!(reinterpret_cast(from) & 1)); + ASSERT(reinterpret_cast(to) & 1); + + setPointer(reinterpret_cast(from) - 1, to, true); + } + + static void* readCallTarget(void* from) + { + return readPointer(reinterpret_cast(from) - 1); + } + + static void repatchInt32(void* where, int32_t value) + { + ASSERT(!(reinterpret_cast(where) & 1)); + + setInt32(where, value, true); + } + + static void repatchCompact(void* where, int32_t offset) + { + ASSERT(offset >= -255 && offset <= 255); + + bool add = true; + if (offset < 0) { + add = false; + offset = -offset; + } + + offset |= (add << 9); + offset |= (1 << 10); + offset |= (1 << 11); + + uint16_t* location = reinterpret_cast(where); + location[1] &= ~((1 << 12) - 1); + location[1] |= offset; + cacheFlush(location, sizeof(uint16_t) * 2); + } + + static void repatchPointer(void* where, void* value) + { + ASSERT(!(reinterpret_cast(where) & 1)); + + setPointer(where, value, true); + } + + static void* readPointer(void* where) + { + return reinterpret_cast(readInt32(where)); + } + + static void replaceWithJump(void* instructionStart, void* to) + { + ASSERT(!(bitwise_cast(instructionStart) & 1)); + ASSERT(!(bitwise_cast(to) & 1)); + uint16_t* ptr = reinterpret_cast(instructionStart) + 2; + + linkJumpT4(ptr, to); + cacheFlush(ptr - 2, sizeof(uint16_t) * 2); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return 4; + } + + static void replaceWithLoad(void* instructionStart) + { + ASSERT(!(bitwise_cast(instructionStart) & 1)); + uint16_t* ptr = reinterpret_cast(instructionStart); + switch (ptr[0] & 0xFFF0) { + case OP_LDR_imm_T3: + break; + case OP_ADD_imm_T3: + ASSERT(!(ptr[1] & 0xF000)); + ptr[0] &= 0x000F; + ptr[0] |= OP_LDR_imm_T3; + ptr[1] |= (ptr[1] & 0x0F00) << 4; + ptr[1] &= 0xF0FF; + cacheFlush(ptr, sizeof(uint16_t) * 2); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + static void replaceWithAddressComputation(void* instructionStart) + { + ASSERT(!(bitwise_cast(instructionStart) & 1)); + uint16_t* ptr = reinterpret_cast(instructionStart); + switch (ptr[0] & 0xFFF0) { + case OP_LDR_imm_T3: + ASSERT(!(ptr[1] & 0x0F00)); + ptr[0] &= 0x000F; + ptr[0] |= OP_ADD_imm_T3; + ptr[1] |= (ptr[1] & 0xF000) >> 4; + ptr[1] &= 0x0FFF; + cacheFlush(ptr, sizeof(uint16_t) * 2); + break; + case OP_ADD_imm_T3: + break; + default: + ASSERT_NOT_REACHED(); + } + } + + unsigned debugOffset() { return m_formatter.debugOffset(); } + + static void cacheFlush(void* code, size_t size) + { +#if OS(IOS) + sys_cache_control(kCacheFunctionPrepareForExecution, code, size); +#elif OS(LINUX) + asm volatile( + "push {r7}\n" + "mov r0, %0\n" + "mov r1, %1\n" + "movw r7, #0x2\n" + "movt r7, #0xf\n" + "movs r2, #0x0\n" + "svc 0x0\n" + "pop {r7}\n" + : + : "r" (code), "r" (reinterpret_cast(code) + size) + : "r0", "r1", "r2"); +#elif OS(WINCE) + CacheRangeFlush(code, size, CACHE_SYNC_ALL); +#elif OS(QNX) +#if !ENABLE(ASSEMBLER_WX_EXCLUSIVE) + msync(code, size, MS_INVALIDATE_ICACHE); +#else + UNUSED_PARAM(code); + UNUSED_PARAM(size); +#endif +#else +#error "The cacheFlush support is missing on this platform." +#endif + } + +private: + // VFP operations commonly take one or more 5-bit operands, typically representing a + // floating point register number. This will commonly be encoded in the instruction + // in two parts, with one single bit field, and one 4-bit field. In the case of + // double precision operands the high bit of the register number will be encoded + // separately, and for single precision operands the high bit of the register number + // will be encoded individually. + // VFPOperand encapsulates a 5-bit VFP operand, with bits 0..3 containing the 4-bit + // field to be encoded together in the instruction (the low 4-bits of a double + // register number, or the high 4-bits of a single register number), and bit 4 + // contains the bit value to be encoded individually. + struct VFPOperand { + explicit VFPOperand(uint32_t value) + : m_value(value) + { + ASSERT(!(m_value & ~0x1f)); + } + + VFPOperand(FPDoubleRegisterID reg) + : m_value(reg) + { + } + + VFPOperand(RegisterID reg) + : m_value(reg) + { + } + + VFPOperand(FPSingleRegisterID reg) + : m_value(((reg & 1) << 4) | (reg >> 1)) // rotate the lowest bit of 'reg' to the top. + { + } + + uint32_t bits1() + { + return m_value >> 4; + } + + uint32_t bits4() + { + return m_value & 0xf; + } + + uint32_t m_value; + }; + + VFPOperand vcvtOp(bool toInteger, bool isUnsigned, bool isRoundZero) + { + // Cannot specify rounding when converting to float. + ASSERT(toInteger || !isRoundZero); + + uint32_t op = 0x8; + if (toInteger) { + // opc2 indicates both toInteger & isUnsigned. + op |= isUnsigned ? 0x4 : 0x5; + // 'op' field in instruction is isRoundZero + if (isRoundZero) + op |= 0x10; + } else { + ASSERT(!isRoundZero); + // 'op' field in instruction is isUnsigned + if (!isUnsigned) + op |= 0x10; + } + return VFPOperand(op); + } + + static void setInt32(void* code, uint32_t value, bool flush) + { + uint16_t* location = reinterpret_cast(code); + ASSERT(isMOV_imm_T3(location - 4) && isMOVT(location - 2)); + + ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(value)); + ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(value >> 16)); + location[-4] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); + location[-3] = twoWordOp5i6Imm4Reg4EncodedImmSecond((location[-3] >> 8) & 0xf, lo16); + location[-2] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); + location[-1] = twoWordOp5i6Imm4Reg4EncodedImmSecond((location[-1] >> 8) & 0xf, hi16); + + if (flush) + cacheFlush(location - 4, 4 * sizeof(uint16_t)); + } + + static int32_t readInt32(void* code) + { + uint16_t* location = reinterpret_cast(code); + ASSERT(isMOV_imm_T3(location - 4) && isMOVT(location - 2)); + + ARMThumbImmediate lo16; + ARMThumbImmediate hi16; + decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(lo16, location[-4]); + decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(lo16, location[-3]); + decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(hi16, location[-2]); + decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(hi16, location[-1]); + uint32_t result = hi16.asUInt16(); + result <<= 16; + result |= lo16.asUInt16(); + return static_cast(result); + } + + static void setUInt7ForLoad(void* code, ARMThumbImmediate imm) + { + // Requires us to have planted a LDR_imm_T1 + ASSERT(imm.isValid()); + ASSERT(imm.isUInt7()); + uint16_t* location = reinterpret_cast(code); + location[0] &= ~((static_cast(0x7f) >> 2) << 6); + location[0] |= (imm.getUInt7() >> 2) << 6; + cacheFlush(location, sizeof(uint16_t)); + } + + static void setPointer(void* code, void* value, bool flush) + { + setInt32(code, reinterpret_cast(value), flush); + } + + static bool isB(void* address) + { + uint16_t* instruction = static_cast(address); + return ((instruction[0] & 0xf800) == OP_B_T4a) && ((instruction[1] & 0xd000) == OP_B_T4b); + } + + static bool isBX(void* address) + { + uint16_t* instruction = static_cast(address); + return (instruction[0] & 0xff87) == OP_BX; + } + + static bool isMOV_imm_T3(void* address) + { + uint16_t* instruction = static_cast(address); + return ((instruction[0] & 0xFBF0) == OP_MOV_imm_T3) && ((instruction[1] & 0x8000) == 0); + } + + static bool isMOVT(void* address) + { + uint16_t* instruction = static_cast(address); + return ((instruction[0] & 0xFBF0) == OP_MOVT) && ((instruction[1] & 0x8000) == 0); + } + + static bool isNOP_T1(void* address) + { + uint16_t* instruction = static_cast(address); + return instruction[0] == OP_NOP_T1; + } + + static bool isNOP_T2(void* address) + { + uint16_t* instruction = static_cast(address); + return (instruction[0] == OP_NOP_T2a) && (instruction[1] == OP_NOP_T2b); + } + + static bool canBeJumpT1(const uint16_t* instruction, const void* target) + { + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T1 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + return ((relative << 23) >> 23) == relative; + } + + static bool canBeJumpT2(const uint16_t* instruction, const void* target) + { + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T2 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + return ((relative << 20) >> 20) == relative; + } + + static bool canBeJumpT3(const uint16_t* instruction, const void* target) + { + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + return ((relative << 11) >> 11) == relative; + } + + static bool canBeJumpT4(const uint16_t* instruction, const void* target) + { + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + return ((relative << 7) >> 7) == relative; + } + + void linkJumpT1(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + ASSERT(canBeJumpT1(instruction, target)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T1 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-1] = OP_B_T1 | ((cond & 0xf) << 8) | ((relative & 0x1fe) >> 1); + } + + static void linkJumpT2(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + ASSERT(canBeJumpT2(instruction, target)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // It does not appear to be documented in the ARM ARM (big surprise), but + // for OP_B_T2 the branch displacement encoded in the instruction is 2 + // less than the actual displacement. + relative -= 2; + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-1] = OP_B_T2 | ((relative & 0xffe) >> 1); + } + + void linkJumpT3(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + ASSERT(canBeJumpT3(instruction, target)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-2] = OP_B_T3a | ((relative & 0x100000) >> 10) | ((cond & 0xf) << 6) | ((relative & 0x3f000) >> 12); + instruction[-1] = OP_B_T3b | ((relative & 0x80000) >> 8) | ((relative & 0x40000) >> 5) | ((relative & 0xffe) >> 1); + } + + static void linkJumpT4(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + ASSERT(canBeJumpT4(instruction, target)); + + intptr_t relative = reinterpret_cast(target) - (reinterpret_cast(instruction)); + // ARM encoding for the top two bits below the sign bit is 'peculiar'. + if (relative >= 0) + relative ^= 0xC00000; + + // All branch offsets should be an even distance. + ASSERT(!(relative & 1)); + instruction[-2] = OP_B_T4a | ((relative & 0x1000000) >> 14) | ((relative & 0x3ff000) >> 12); + instruction[-1] = OP_B_T4b | ((relative & 0x800000) >> 10) | ((relative & 0x400000) >> 11) | ((relative & 0xffe) >> 1); + } + + void linkConditionalJumpT4(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + instruction[-3] = ifThenElse(cond) | OP_IT; + linkJumpT4(instruction, target); + } + + static void linkBX(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; + ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) + 1)); + ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) >> 16)); + instruction[-5] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); + instruction[-4] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, lo16); + instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); + instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); + instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); + } + + void linkConditionalBX(Condition cond, uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + linkBX(instruction, target); + instruction[-6] = ifThenElse(cond, true, true) | OP_IT; + } + + static void linkJumpAbsolute(uint16_t* instruction, void* target) + { + // FIMXE: this should be up in the MacroAssembler layer. :-( + ASSERT(!(reinterpret_cast(instruction) & 1)); + ASSERT(!(reinterpret_cast(target) & 1)); + + ASSERT((isMOV_imm_T3(instruction - 5) && isMOVT(instruction - 3) && isBX(instruction - 1)) + || (isNOP_T1(instruction - 5) && isNOP_T2(instruction - 4) && isB(instruction - 2))); + + if (canBeJumpT4(instruction, target)) { + // There may be a better way to fix this, but right now put the NOPs first, since in the + // case of an conditional branch this will be coming after an ITTT predicating *three* + // instructions! Looking backwards to modify the ITTT to an IT is not easy, due to + // variable wdith encoding - the previous instruction might *look* like an ITTT but + // actually be the second half of a 2-word op. + instruction[-5] = OP_NOP_T1; + instruction[-4] = OP_NOP_T2a; + instruction[-3] = OP_NOP_T2b; + linkJumpT4(instruction, target); + } else { + const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; + ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) + 1)); + ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast(reinterpret_cast(target) >> 16)); + instruction[-5] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); + instruction[-4] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, lo16); + instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); + instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); + instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); + } + } + + static uint16_t twoWordOp5i6Imm4Reg4EncodedImmFirst(uint16_t op, ARMThumbImmediate imm) + { + return op | (imm.m_value.i << 10) | imm.m_value.imm4; + } + + static void decodeTwoWordOp5i6Imm4Reg4EncodedImmFirst(ARMThumbImmediate& result, uint16_t value) + { + result.m_value.i = (value >> 10) & 1; + result.m_value.imm4 = value & 15; + } + + static uint16_t twoWordOp5i6Imm4Reg4EncodedImmSecond(uint16_t rd, ARMThumbImmediate imm) + { + return (imm.m_value.imm3 << 12) | (rd << 8) | imm.m_value.imm8; + } + + static void decodeTwoWordOp5i6Imm4Reg4EncodedImmSecond(ARMThumbImmediate& result, uint16_t value) + { + result.m_value.imm3 = (value >> 12) & 7; + result.m_value.imm8 = value & 255; + } + + class ARMInstructionFormatter { + public: + ALWAYS_INLINE void oneWordOp5Reg3Imm8(OpcodeID op, RegisterID rd, uint8_t imm) + { + m_buffer.putShort(op | (rd << 8) | imm); + } + + ALWAYS_INLINE void oneWordOp5Imm5Reg3Reg3(OpcodeID op, uint8_t imm, RegisterID reg1, RegisterID reg2) + { + m_buffer.putShort(op | (imm << 6) | (reg1 << 3) | reg2); + } + + ALWAYS_INLINE void oneWordOp7Reg3Reg3Reg3(OpcodeID op, RegisterID reg1, RegisterID reg2, RegisterID reg3) + { + m_buffer.putShort(op | (reg1 << 6) | (reg2 << 3) | reg3); + } + + ALWAYS_INLINE void oneWordOp8Imm8(OpcodeID op, uint8_t imm) + { + m_buffer.putShort(op | imm); + } + + ALWAYS_INLINE void oneWordOp8RegReg143(OpcodeID op, RegisterID reg1, RegisterID reg2) + { + m_buffer.putShort(op | ((reg2 & 8) << 4) | (reg1 << 3) | (reg2 & 7)); + } + + ALWAYS_INLINE void oneWordOp9Imm7(OpcodeID op, uint8_t imm) + { + m_buffer.putShort(op | imm); + } + + ALWAYS_INLINE void oneWordOp10Reg3Reg3(OpcodeID op, RegisterID reg1, RegisterID reg2) + { + m_buffer.putShort(op | (reg1 << 3) | reg2); + } + + ALWAYS_INLINE void twoWordOp12Reg4FourFours(OpcodeID1 op, RegisterID reg, FourFours ff) + { + m_buffer.putShort(op | reg); + m_buffer.putShort(ff.m_u.value); + } + + ALWAYS_INLINE void twoWordOp16FourFours(OpcodeID1 op, FourFours ff) + { + m_buffer.putShort(op); + m_buffer.putShort(ff.m_u.value); + } + + ALWAYS_INLINE void twoWordOp16Op16(OpcodeID1 op1, OpcodeID2 op2) + { + m_buffer.putShort(op1); + m_buffer.putShort(op2); + } + + ALWAYS_INLINE void twoWordOp5i6Imm4Reg4EncodedImm(OpcodeID1 op, int imm4, RegisterID rd, ARMThumbImmediate imm) + { + ARMThumbImmediate newImm = imm; + newImm.m_value.imm4 = imm4; + + m_buffer.putShort(ARMv7Assembler::twoWordOp5i6Imm4Reg4EncodedImmFirst(op, newImm)); + m_buffer.putShort(ARMv7Assembler::twoWordOp5i6Imm4Reg4EncodedImmSecond(rd, newImm)); + } + + ALWAYS_INLINE void twoWordOp12Reg4Reg4Imm12(OpcodeID1 op, RegisterID reg1, RegisterID reg2, uint16_t imm) + { + m_buffer.putShort(op | reg1); + m_buffer.putShort((reg2 << 12) | imm); + } + + ALWAYS_INLINE void twoWordOp12Reg40Imm3Reg4Imm20Imm5(OpcodeID1 op, RegisterID reg1, RegisterID reg2, uint16_t imm1, uint16_t imm2, uint16_t imm3) + { + m_buffer.putShort(op | reg1); + m_buffer.putShort((imm1 << 12) | (reg2 << 8) | (imm2 << 6) | imm3); + } + + // Formats up instructions of the pattern: + // 111111111B11aaaa:bbbb222SA2C2cccc + // Where 1s in the pattern come from op1, 2s in the pattern come from op2, S is the provided size bit. + // Operands provide 5 bit values of the form Aaaaa, Bbbbb, Ccccc. + ALWAYS_INLINE void vfpOp(OpcodeID1 op1, OpcodeID2 op2, bool size, VFPOperand a, VFPOperand b, VFPOperand c) + { + ASSERT(!(op1 & 0x004f)); + ASSERT(!(op2 & 0xf1af)); + m_buffer.putShort(op1 | b.bits1() << 6 | a.bits4()); + m_buffer.putShort(op2 | b.bits4() << 12 | size << 8 | a.bits1() << 7 | c.bits1() << 5 | c.bits4()); + } + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + // (i.e. +/-(0..255) 32-bit words) + ALWAYS_INLINE void vfpMemOp(OpcodeID1 op1, OpcodeID2 op2, bool size, RegisterID rn, VFPOperand rd, int32_t imm) + { + bool up = true; + if (imm < 0) { + imm = -imm; + up = false; + } + + uint32_t offset = imm; + ASSERT(!(offset & ~0x3fc)); + offset >>= 2; + + m_buffer.putShort(op1 | (up << 7) | rd.bits1() << 6 | rn); + m_buffer.putShort(op2 | rd.bits4() << 12 | size << 8 | offset); + } + + // Administrative methods: + + size_t codeSize() const { return m_buffer.codeSize(); } + AssemblerLabel label() const { return m_buffer.label(); } + bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); } + void* data() const { return m_buffer.data(); } + + unsigned debugOffset() { return m_buffer.debugOffset(); } + + private: + AssemblerBuffer m_buffer; + } m_formatter; + + Vector m_jumpsToLink; + Vector m_offsets; + int m_indexOfLastWatchpoint; + int m_indexOfTailOfLastWatchpoint; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) + +#endif // ARMAssembler_h diff --git a/src/3rdparty/masm/assembler/AbstractMacroAssembler.h b/src/3rdparty/masm/assembler/AbstractMacroAssembler.h new file mode 100644 index 0000000000..ee78ef84eb --- /dev/null +++ b/src/3rdparty/masm/assembler/AbstractMacroAssembler.h @@ -0,0 +1,792 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AbstractMacroAssembler_h +#define AbstractMacroAssembler_h + +#include "AssemblerBuffer.h" +#include "CodeLocation.h" +#include "MacroAssemblerCodeRef.h" +#include +#include +#include + +#if ENABLE(ASSEMBLER) + + +#if PLATFORM(QT) +#define ENABLE_JIT_CONSTANT_BLINDING 0 +#endif + +#ifndef ENABLE_JIT_CONSTANT_BLINDING +#define ENABLE_JIT_CONSTANT_BLINDING 1 +#endif + +namespace JSC { + +class JumpReplacementWatchpoint; +class LinkBuffer; +class RepatchBuffer; +class Watchpoint; +namespace DFG { +struct OSRExit; +} + +template +class AbstractMacroAssembler { +public: + friend class JITWriteBarrierBase; + typedef AssemblerType AssemblerType_T; + + typedef MacroAssemblerCodePtr CodePtr; + typedef MacroAssemblerCodeRef CodeRef; + + class Jump; + + typedef typename AssemblerType::RegisterID RegisterID; + + // Section 1: MacroAssembler operand types + // + // The following types are used as operands to MacroAssembler operations, + // describing immediate and memory operands to the instructions to be planted. + + enum Scale { + TimesOne, + TimesTwo, + TimesFour, + TimesEight, + }; + + // Address: + // + // Describes a simple base-offset address. + struct Address { + explicit Address(RegisterID base, int32_t offset = 0) + : base(base) + , offset(offset) + { + } + + RegisterID base; + int32_t offset; + }; + + struct ExtendedAddress { + explicit ExtendedAddress(RegisterID base, intptr_t offset = 0) + : base(base) + , offset(offset) + { + } + + RegisterID base; + intptr_t offset; + }; + + // ImplicitAddress: + // + // This class is used for explicit 'load' and 'store' operations + // (as opposed to situations in which a memory operand is provided + // to a generic operation, such as an integer arithmetic instruction). + // + // In the case of a load (or store) operation we want to permit + // addresses to be implicitly constructed, e.g. the two calls: + // + // load32(Address(addrReg), destReg); + // load32(addrReg, destReg); + // + // Are equivalent, and the explicit wrapping of the Address in the former + // is unnecessary. + struct ImplicitAddress { + ImplicitAddress(RegisterID base) + : base(base) + , offset(0) + { + } + + ImplicitAddress(Address address) + : base(address.base) + , offset(address.offset) + { + } + + RegisterID base; + int32_t offset; + }; + + // BaseIndex: + // + // Describes a complex addressing mode. + struct BaseIndex { + BaseIndex(RegisterID base, RegisterID index, Scale scale, int32_t offset = 0) + : base(base) + , index(index) + , scale(scale) + , offset(offset) + { + } + + RegisterID base; + RegisterID index; + Scale scale; + int32_t offset; + }; + + // AbsoluteAddress: + // + // Describes an memory operand given by a pointer. For regular load & store + // operations an unwrapped void* will be used, rather than using this. + struct AbsoluteAddress { + explicit AbsoluteAddress(const void* ptr) + : m_ptr(ptr) + { + } + + const void* m_ptr; + }; + + // TrustedImmPtr: + // + // A pointer sized immediate operand to an instruction - this is wrapped + // in a class requiring explicit construction in order to differentiate + // from pointers used as absolute addresses to memory operations + struct TrustedImmPtr { + TrustedImmPtr() { } + + explicit TrustedImmPtr(const void* value) + : m_value(value) + { + } + + // This is only here so that TrustedImmPtr(0) does not confuse the C++ + // overload handling rules. + explicit TrustedImmPtr(int value) + : m_value(0) + { + ASSERT_UNUSED(value, !value); + } + + explicit TrustedImmPtr(size_t value) + : m_value(reinterpret_cast(value)) + { + } + + intptr_t asIntptr() + { + return reinterpret_cast(m_value); + } + + const void* m_value; + }; + + struct ImmPtr : +#if ENABLE(JIT_CONSTANT_BLINDING) + private TrustedImmPtr +#else + public TrustedImmPtr +#endif + { + explicit ImmPtr(const void* value) + : TrustedImmPtr(value) + { + } + + TrustedImmPtr asTrustedImmPtr() { return *this; } + }; + + // TrustedImm32: + // + // A 32bit immediate operand to an instruction - this is wrapped in a + // class requiring explicit construction in order to prevent RegisterIDs + // (which are implemented as an enum) from accidentally being passed as + // immediate values. + struct TrustedImm32 { + TrustedImm32() { } + + explicit TrustedImm32(int32_t value) + : m_value(value) + { + } + +#if !CPU(X86_64) + explicit TrustedImm32(TrustedImmPtr ptr) + : m_value(ptr.asIntptr()) + { + } +#endif + + int32_t m_value; + }; + + + struct Imm32 : +#if ENABLE(JIT_CONSTANT_BLINDING) + private TrustedImm32 +#else + public TrustedImm32 +#endif + { + explicit Imm32(int32_t value) + : TrustedImm32(value) + { + } +#if !CPU(X86_64) + explicit Imm32(TrustedImmPtr ptr) + : TrustedImm32(ptr) + { + } +#endif + const TrustedImm32& asTrustedImm32() const { return *this; } + + }; + + // TrustedImm64: + // + // A 64bit immediate operand to an instruction - this is wrapped in a + // class requiring explicit construction in order to prevent RegisterIDs + // (which are implemented as an enum) from accidentally being passed as + // immediate values. + struct TrustedImm64 { + TrustedImm64() { } + + explicit TrustedImm64(int64_t value) + : m_value(value) + { + } + +#if CPU(X86_64) + explicit TrustedImm64(TrustedImmPtr ptr) + : m_value(ptr.asIntptr()) + { + } +#endif + + int64_t m_value; + }; + + struct Imm64 : +#if ENABLE(JIT_CONSTANT_BLINDING) + private TrustedImm64 +#else + public TrustedImm64 +#endif + { + explicit Imm64(int64_t value) + : TrustedImm64(value) + { + } +#if CPU(X86_64) + explicit Imm64(TrustedImmPtr ptr) + : TrustedImm64(ptr) + { + } +#endif + const TrustedImm64& asTrustedImm64() const { return *this; } + }; + + // Section 2: MacroAssembler code buffer handles + // + // The following types are used to reference items in the code buffer + // during JIT code generation. For example, the type Jump is used to + // track the location of a jump instruction so that it may later be + // linked to a label marking its destination. + + + // Label: + // + // A Label records a point in the generated instruction stream, typically such that + // it may be used as a destination for a jump. + class Label { + template + friend class AbstractMacroAssembler; + friend struct DFG::OSRExit; + friend class Jump; + friend class JumpReplacementWatchpoint; + friend class MacroAssemblerCodeRef; + friend class LinkBuffer; + friend class Watchpoint; + + public: + Label() + { + } + + Label(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.label()) + { + } + + bool isSet() const { return m_label.isSet(); } + private: + AssemblerLabel m_label; + }; + + // ConvertibleLoadLabel: + // + // A ConvertibleLoadLabel records a loadPtr instruction that can be patched to an addPtr + // so that: + // + // loadPtr(Address(a, i), b) + // + // becomes: + // + // addPtr(TrustedImmPtr(i), a, b) + class ConvertibleLoadLabel { + template + friend class AbstractMacroAssembler; + friend class LinkBuffer; + + public: + ConvertibleLoadLabel() + { + } + + ConvertibleLoadLabel(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.labelIgnoringWatchpoints()) + { + } + + bool isSet() const { return m_label.isSet(); } + private: + AssemblerLabel m_label; + }; + + // DataLabelPtr: + // + // A DataLabelPtr is used to refer to a location in the code containing a pointer to be + // patched after the code has been generated. + class DataLabelPtr { + template + friend class AbstractMacroAssembler; + friend class LinkBuffer; + public: + DataLabelPtr() + { + } + + DataLabelPtr(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.label()) + { + } + + bool isSet() const { return m_label.isSet(); } + + private: + AssemblerLabel m_label; + }; + + // DataLabel32: + // + // A DataLabelPtr is used to refer to a location in the code containing a pointer to be + // patched after the code has been generated. + class DataLabel32 { + template + friend class AbstractMacroAssembler; + friend class LinkBuffer; + public: + DataLabel32() + { + } + + DataLabel32(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.label()) + { + } + + AssemblerLabel label() const { return m_label; } + + private: + AssemblerLabel m_label; + }; + + // DataLabelCompact: + // + // A DataLabelCompact is used to refer to a location in the code containing a + // compact immediate to be patched after the code has been generated. + class DataLabelCompact { + template + friend class AbstractMacroAssembler; + friend class LinkBuffer; + public: + DataLabelCompact() + { + } + + DataLabelCompact(AbstractMacroAssembler* masm) + : m_label(masm->m_assembler.label()) + { + } + + DataLabelCompact(AssemblerLabel label) + : m_label(label) + { + } + + private: + AssemblerLabel m_label; + }; + + // Call: + // + // A Call object is a reference to a call instruction that has been planted + // into the code buffer - it is typically used to link the call, setting the + // relative offset such that when executed it will call to the desired + // destination. + class Call { + template + friend class AbstractMacroAssembler; + + public: + enum Flags { + None = 0x0, + Linkable = 0x1, + Near = 0x2, + LinkableNear = 0x3, + }; + + Call() + : m_flags(None) + { + } + + Call(AssemblerLabel jmp, Flags flags) + : m_label(jmp) + , m_flags(flags) + { + } + + bool isFlagSet(Flags flag) + { + return m_flags & flag; + } + + static Call fromTailJump(Jump jump) + { + return Call(jump.m_label, Linkable); + } + + AssemblerLabel m_label; + private: + Flags m_flags; + }; + + // Jump: + // + // A jump object is a reference to a jump instruction that has been planted + // into the code buffer - it is typically used to link the jump, setting the + // relative offset such that when executed it will jump to the desired + // destination. + class Jump { + template + friend class AbstractMacroAssembler; + friend class Call; + friend struct DFG::OSRExit; + friend class LinkBuffer; + public: + Jump() + { + } + +#if CPU(ARM_THUMB2) + // Fixme: this information should be stored in the instruction stream, not in the Jump object. + Jump(AssemblerLabel jmp, ARMv7Assembler::JumpType type, ARMv7Assembler::Condition condition = ARMv7Assembler::ConditionInvalid) + : m_label(jmp) + , m_type(type) + , m_condition(condition) + { + } +#elif CPU(SH4) + Jump(AssemblerLabel jmp, SH4Assembler::JumpType type = SH4Assembler::JumpFar) + : m_label(jmp) + , m_type(type) + { + } +#else + Jump(AssemblerLabel jmp) + : m_label(jmp) + { + } +#endif + + Label label() const + { + Label result; + result.m_label = m_label; + return result; + } + + void link(AbstractMacroAssembler* masm) const + { +#if CPU(ARM_THUMB2) + masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type, m_condition); +#elif CPU(SH4) + masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type); +#else + masm->m_assembler.linkJump(m_label, masm->m_assembler.label()); +#endif + } + + void linkTo(Label label, AbstractMacroAssembler* masm) const + { +#if CPU(ARM_THUMB2) + masm->m_assembler.linkJump(m_label, label.m_label, m_type, m_condition); +#else + masm->m_assembler.linkJump(m_label, label.m_label); +#endif + } + + bool isSet() const { return m_label.isSet(); } + + private: + AssemblerLabel m_label; +#if CPU(ARM_THUMB2) + ARMv7Assembler::JumpType m_type; + ARMv7Assembler::Condition m_condition; +#endif +#if CPU(SH4) + SH4Assembler::JumpType m_type; +#endif + }; + + struct PatchableJump { + PatchableJump() + { + } + + explicit PatchableJump(Jump jump) + : m_jump(jump) + { + } + + operator Jump&() { return m_jump; } + + Jump m_jump; + }; + + // JumpList: + // + // A JumpList is a set of Jump objects. + // All jumps in the set will be linked to the same destination. + class JumpList { + friend class LinkBuffer; + + public: + typedef Vector JumpVector; + + JumpList() { } + + JumpList(Jump jump) + { + append(jump); + } + + void link(AbstractMacroAssembler* masm) + { + size_t size = m_jumps.size(); + for (size_t i = 0; i < size; ++i) + m_jumps[i].link(masm); + m_jumps.clear(); + } + + void linkTo(Label label, AbstractMacroAssembler* masm) + { + size_t size = m_jumps.size(); + for (size_t i = 0; i < size; ++i) + m_jumps[i].linkTo(label, masm); + m_jumps.clear(); + } + + void append(Jump jump) + { + m_jumps.append(jump); + } + + void append(const JumpList& other) + { + m_jumps.append(other.m_jumps.begin(), other.m_jumps.size()); + } + + bool empty() + { + return !m_jumps.size(); + } + + void clear() + { + m_jumps.clear(); + } + + const JumpVector& jumps() const { return m_jumps; } + + private: + JumpVector m_jumps; + }; + + + // Section 3: Misc admin methods +#if ENABLE(DFG_JIT) + Label labelIgnoringWatchpoints() + { + Label result; + result.m_label = m_assembler.labelIgnoringWatchpoints(); + return result; + } +#else + Label labelIgnoringWatchpoints() + { + return label(); + } +#endif + + Label label() + { + return Label(this); + } + + void padBeforePatch() + { + // Rely on the fact that asking for a label already does the padding. + (void)label(); + } + + Label watchpointLabel() + { + Label result; + result.m_label = m_assembler.labelForWatchpoint(); + return result; + } + + Label align() + { + m_assembler.align(16); + return Label(this); + } + + template + static ptrdiff_t differenceBetween(T from, U to) + { + return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_label); + } + + static ptrdiff_t differenceBetweenCodePtr(const MacroAssemblerCodePtr& a, const MacroAssemblerCodePtr& b) + { + return reinterpret_cast(b.executableAddress()) - reinterpret_cast(a.executableAddress()); + } + + unsigned debugOffset() { return m_assembler.debugOffset(); } + + ALWAYS_INLINE static void cacheFlush(void* code, size_t size) + { + AssemblerType::cacheFlush(code, size); + } +protected: + AbstractMacroAssembler() + : m_randomSource(cryptographicallyRandomNumber()) + { + } + + AssemblerType m_assembler; + + uint32_t random() + { + return m_randomSource.getUint32(); + } + + WeakRandom m_randomSource; + +#if ENABLE(JIT_CONSTANT_BLINDING) + static bool scratchRegisterForBlinding() { return false; } + static bool shouldBlindForSpecificArch(uint32_t) { return true; } + static bool shouldBlindForSpecificArch(uint64_t) { return true; } +#endif + + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkJump(void* code, Jump jump, CodeLocationLabel target) + { + AssemblerType::linkJump(code, jump.m_label, target.dataLocation()); + } + + static void linkPointer(void* code, AssemblerLabel label, void* value) + { + AssemblerType::linkPointer(code, label, value); + } + + static void* getLinkerAddress(void* code, AssemblerLabel label) + { + return AssemblerType::getRelocatedAddress(code, label); + } + + static unsigned getLinkerCallReturnOffset(Call call) + { + return AssemblerType::getCallReturnOffset(call.m_label); + } + + static void repatchJump(CodeLocationJump jump, CodeLocationLabel destination) + { + AssemblerType::relinkJump(jump.dataLocation(), destination.dataLocation()); + } + + static void repatchNearCall(CodeLocationNearCall nearCall, CodeLocationLabel destination) + { + AssemblerType::relinkCall(nearCall.dataLocation(), destination.executableAddress()); + } + + static void repatchCompact(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) + { + AssemblerType::repatchCompact(dataLabelCompact.dataLocation(), value); + } + + static void repatchInt32(CodeLocationDataLabel32 dataLabel32, int32_t value) + { + AssemblerType::repatchInt32(dataLabel32.dataLocation(), value); + } + + static void repatchPointer(CodeLocationDataLabelPtr dataLabelPtr, void* value) + { + AssemblerType::repatchPointer(dataLabelPtr.dataLocation(), value); + } + + static void* readPointer(CodeLocationDataLabelPtr dataLabelPtr) + { + return AssemblerType::readPointer(dataLabelPtr.dataLocation()); + } + + static void replaceWithLoad(CodeLocationConvertibleLoad label) + { + AssemblerType::replaceWithLoad(label.dataLocation()); + } + + static void replaceWithAddressComputation(CodeLocationConvertibleLoad label) + { + AssemblerType::replaceWithAddressComputation(label.dataLocation()); + } +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // AbstractMacroAssembler_h diff --git a/src/3rdparty/masm/assembler/AssemblerBuffer.h b/src/3rdparty/masm/assembler/AssemblerBuffer.h new file mode 100644 index 0000000000..bc52801ba7 --- /dev/null +++ b/src/3rdparty/masm/assembler/AssemblerBuffer.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AssemblerBuffer_h +#define AssemblerBuffer_h + +#if ENABLE(ASSEMBLER) + +#include "ExecutableAllocator.h" +#include "JITCompilationEffort.h" +#include "JSGlobalData.h" +#include "stdint.h" +#include +#include +#include +#include + +namespace JSC { + + struct AssemblerLabel { + AssemblerLabel() + : m_offset(std::numeric_limits::max()) + { + } + + explicit AssemblerLabel(uint32_t offset) + : m_offset(offset) + { + } + + bool isSet() const { return (m_offset != std::numeric_limits::max()); } + + AssemblerLabel labelAtOffset(int offset) const + { + return AssemblerLabel(m_offset + offset); + } + + uint32_t m_offset; + }; + + class AssemblerBuffer { + static const int inlineCapacity = 128; + public: + AssemblerBuffer() + : m_storage(inlineCapacity) + , m_buffer(&(*m_storage.begin())) + , m_capacity(inlineCapacity) + , m_index(0) + { + } + + ~AssemblerBuffer() + { + } + + bool isAvailable(int space) + { + return m_index + space <= m_capacity; + } + + void ensureSpace(int space) + { + if (!isAvailable(space)) + grow(); + } + + bool isAligned(int alignment) const + { + return !(m_index & (alignment - 1)); + } + + template + void putIntegral(IntegralType value) + { + ensureSpace(sizeof(IntegralType)); + putIntegralUnchecked(value); + } + + template + void putIntegralUnchecked(IntegralType value) + { + ASSERT(isAvailable(sizeof(IntegralType))); + *reinterpret_cast_ptr(m_buffer + m_index) = value; + m_index += sizeof(IntegralType); + } + + void putByteUnchecked(int8_t value) { putIntegralUnchecked(value); } + void putByte(int8_t value) { putIntegral(value); } + void putShortUnchecked(int16_t value) { putIntegralUnchecked(value); } + void putShort(int16_t value) { putIntegral(value); } + void putIntUnchecked(int32_t value) { putIntegralUnchecked(value); } + void putInt(int32_t value) { putIntegral(value); } + void putInt64Unchecked(int64_t value) { putIntegralUnchecked(value); } + void putInt64(int64_t value) { putIntegral(value); } + + void* data() const + { + return m_buffer; + } + + size_t codeSize() const + { + return m_index; + } + + AssemblerLabel label() const + { + return AssemblerLabel(m_index); + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + if (!m_index) + return 0; + + RefPtr result = globalData.executableAllocator.allocate(globalData, m_index, ownerUID, effort); + + if (!result) + return 0; + + ExecutableAllocator::makeWritable(result->start(), result->sizeInBytes()); + + memcpy(result->start(), m_buffer, m_index); + + return result.release(); + } + + unsigned debugOffset() { return m_index; } + + protected: + void append(const char* data, int size) + { + if (!isAvailable(size)) + grow(size); + + memcpy(m_buffer + m_index, data, size); + m_index += size; + } + + void grow(int extraCapacity = 0) + { + m_capacity += m_capacity / 2 + extraCapacity; + + m_storage.grow(m_capacity); + m_buffer = &(*m_storage.begin()); + } + + private: + Vector m_storage; + char* m_buffer; + int m_capacity; + int m_index; + }; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // AssemblerBuffer_h diff --git a/src/3rdparty/masm/assembler/AssemblerBufferWithConstantPool.h b/src/3rdparty/masm/assembler/AssemblerBufferWithConstantPool.h new file mode 100644 index 0000000000..5377ef0c7a --- /dev/null +++ b/src/3rdparty/masm/assembler/AssemblerBufferWithConstantPool.h @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2009 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AssemblerBufferWithConstantPool_h +#define AssemblerBufferWithConstantPool_h + +#if ENABLE(ASSEMBLER) + +#include "AssemblerBuffer.h" +#include + +#define ASSEMBLER_HAS_CONSTANT_POOL 1 + +namespace JSC { + +/* + On a constant pool 4 or 8 bytes data can be stored. The values can be + constants or addresses. The addresses should be 32 or 64 bits. The constants + should be double-precisions float or integer numbers which are hard to be + encoded as few machine instructions. + + TODO: The pool is desinged to handle both 32 and 64 bits values, but + currently only the 4 bytes constants are implemented and tested. + + The AssemblerBuffer can contain multiple constant pools. Each pool is inserted + into the instruction stream - protected by a jump instruction from the + execution flow. + + The flush mechanism is called when no space remain to insert the next instruction + into the pool. Three values are used to determine when the constant pool itself + have to be inserted into the instruction stream (Assembler Buffer): + + - maxPoolSize: size of the constant pool in bytes, this value cannot be + larger than the maximum offset of a PC relative memory load + + - barrierSize: size of jump instruction in bytes which protects the + constant pool from execution + + - maxInstructionSize: maximum length of a machine instruction in bytes + + There are some callbacks which solve the target architecture specific + address handling: + + - TYPE patchConstantPoolLoad(TYPE load, int value): + patch the 'load' instruction with the index of the constant in the + constant pool and return the patched instruction. + + - void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr): + patch the a PC relative load instruction at 'loadAddr' address with the + final relative offset. The offset can be computed with help of + 'constPoolAddr' (the address of the constant pool) and index of the + constant (which is stored previously in the load instruction itself). + + - TYPE placeConstantPoolBarrier(int size): + return with a constant pool barrier instruction which jumps over the + constant pool. + + The 'put*WithConstant*' functions should be used to place a data into the + constant pool. +*/ + +template +class AssemblerBufferWithConstantPool : public AssemblerBuffer { + typedef SegmentedVector LoadOffsets; + using AssemblerBuffer::putIntegral; + using AssemblerBuffer::putIntegralUnchecked; +public: + typedef struct { + short high; + short low; + } TwoShorts; + + enum { + UniqueConst, + ReusableConst, + UnusedEntry, + }; + + AssemblerBufferWithConstantPool() + : AssemblerBuffer() + , m_numConsts(0) + , m_maxDistance(maxPoolSize) + , m_lastConstDelta(0) + { + m_pool = static_cast(fastMalloc(maxPoolSize)); + m_mask = static_cast(fastMalloc(maxPoolSize / sizeof(uint32_t))); + } + + ~AssemblerBufferWithConstantPool() + { + fastFree(m_mask); + fastFree(m_pool); + } + + void ensureSpace(int space) + { + flushIfNoSpaceFor(space); + AssemblerBuffer::ensureSpace(space); + } + + void ensureSpace(int insnSpace, int constSpace) + { + flushIfNoSpaceFor(insnSpace, constSpace); + AssemblerBuffer::ensureSpace(insnSpace); + } + + void ensureSpaceForAnyInstruction(int amount = 1) + { + flushIfNoSpaceFor(amount * maxInstructionSize, amount * sizeof(uint64_t)); + } + + bool isAligned(int alignment) + { + flushIfNoSpaceFor(alignment); + return AssemblerBuffer::isAligned(alignment); + } + + void putByteUnchecked(int value) + { + AssemblerBuffer::putByteUnchecked(value); + correctDeltas(1); + } + + void putByte(int value) + { + flushIfNoSpaceFor(1); + AssemblerBuffer::putByte(value); + correctDeltas(1); + } + + void putShortUnchecked(int value) + { + AssemblerBuffer::putShortUnchecked(value); + correctDeltas(2); + } + + void putShort(int value) + { + flushIfNoSpaceFor(2); + AssemblerBuffer::putShort(value); + correctDeltas(2); + } + + void putIntUnchecked(int value) + { + AssemblerBuffer::putIntUnchecked(value); + correctDeltas(4); + } + + void putInt(int value) + { + flushIfNoSpaceFor(4); + AssemblerBuffer::putInt(value); + correctDeltas(4); + } + + void putInt64Unchecked(int64_t value) + { + AssemblerBuffer::putInt64Unchecked(value); + correctDeltas(8); + } + + void putIntegral(TwoShorts value) + { + putIntegral(value.high); + putIntegral(value.low); + } + + void putIntegralUnchecked(TwoShorts value) + { + putIntegralUnchecked(value.high); + putIntegralUnchecked(value.low); + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + flushConstantPool(false); + return AssemblerBuffer::executableCopy(globalData, ownerUID, effort); + } + + void putShortWithConstantInt(uint16_t insn, uint32_t constant, bool isReusable = false) + { + putIntegralWithConstantInt(insn, constant, isReusable); + } + + void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false) + { + putIntegralWithConstantInt(insn, constant, isReusable); + } + + // This flushing mechanism can be called after any unconditional jumps. + void flushWithoutBarrier(bool isForced = false) + { + // Flush if constant pool is more than 60% full to avoid overuse of this function. + if (isForced || 5 * static_cast(m_numConsts) > 3 * maxPoolSize / sizeof(uint32_t)) + flushConstantPool(false); + } + + uint32_t* poolAddress() + { + return m_pool; + } + + int sizeOfConstantPool() + { + return m_numConsts; + } + +private: + void correctDeltas(int insnSize) + { + m_maxDistance -= insnSize; + m_lastConstDelta -= insnSize; + if (m_lastConstDelta < 0) + m_lastConstDelta = 0; + } + + void correctDeltas(int insnSize, int constSize) + { + correctDeltas(insnSize); + + m_maxDistance -= m_lastConstDelta; + m_lastConstDelta = constSize; + } + + template + void putIntegralWithConstantInt(IntegralType insn, uint32_t constant, bool isReusable) + { + if (!m_numConsts) + m_maxDistance = maxPoolSize; + flushIfNoSpaceFor(sizeof(IntegralType), 4); + + m_loadOffsets.append(codeSize()); + if (isReusable) { + for (int i = 0; i < m_numConsts; ++i) { + if (m_mask[i] == ReusableConst && m_pool[i] == constant) { + putIntegral(static_cast(AssemblerType::patchConstantPoolLoad(insn, i))); + correctDeltas(sizeof(IntegralType)); + return; + } + } + } + + m_pool[m_numConsts] = constant; + m_mask[m_numConsts] = static_cast(isReusable ? ReusableConst : UniqueConst); + + putIntegral(static_cast(AssemblerType::patchConstantPoolLoad(insn, m_numConsts))); + ++m_numConsts; + + correctDeltas(sizeof(IntegralType), 4); + } + + void flushConstantPool(bool useBarrier = true) + { + if (m_numConsts == 0) + return; + int alignPool = (codeSize() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1); + + if (alignPool) + alignPool = sizeof(uint64_t) - alignPool; + + // Callback to protect the constant pool from execution + if (useBarrier) + putIntegral(AssemblerType::placeConstantPoolBarrier(m_numConsts * sizeof(uint32_t) + alignPool)); + + if (alignPool) { + if (alignPool & 1) + AssemblerBuffer::putByte(AssemblerType::padForAlign8); + if (alignPool & 2) + AssemblerBuffer::putShort(AssemblerType::padForAlign16); + if (alignPool & 4) + AssemblerBuffer::putInt(AssemblerType::padForAlign32); + } + + int constPoolOffset = codeSize(); + append(reinterpret_cast(m_pool), m_numConsts * sizeof(uint32_t)); + + // Patch each PC relative load + for (LoadOffsets::Iterator iter = m_loadOffsets.begin(); iter != m_loadOffsets.end(); ++iter) { + void* loadAddr = reinterpret_cast(data()) + *iter; + AssemblerType::patchConstantPoolLoad(loadAddr, reinterpret_cast(data()) + constPoolOffset); + } + + m_loadOffsets.clear(); + m_numConsts = 0; + } + + void flushIfNoSpaceFor(int nextInsnSize) + { + if (m_numConsts == 0) + return; + int lastConstDelta = m_lastConstDelta > nextInsnSize ? m_lastConstDelta - nextInsnSize : 0; + if ((m_maxDistance < nextInsnSize + lastConstDelta + barrierSize + (int)sizeof(uint32_t))) + flushConstantPool(); + } + + void flushIfNoSpaceFor(int nextInsnSize, int nextConstSize) + { + if (m_numConsts == 0) + return; + if ((m_maxDistance < nextInsnSize + m_lastConstDelta + nextConstSize + barrierSize + (int)sizeof(uint32_t)) || + (m_numConsts * sizeof(uint32_t) + nextConstSize >= maxPoolSize)) + flushConstantPool(); + } + + uint32_t* m_pool; + char* m_mask; + LoadOffsets m_loadOffsets; + + int m_numConsts; + int m_maxDistance; + int m_lastConstDelta; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // AssemblerBufferWithConstantPool_h diff --git a/src/3rdparty/masm/assembler/CodeLocation.h b/src/3rdparty/masm/assembler/CodeLocation.h new file mode 100644 index 0000000000..86d1f2b755 --- /dev/null +++ b/src/3rdparty/masm/assembler/CodeLocation.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CodeLocation_h +#define CodeLocation_h + +#include "MacroAssemblerCodeRef.h" + +#if ENABLE(ASSEMBLER) + +namespace JSC { + +class CodeLocationInstruction; +class CodeLocationLabel; +class CodeLocationJump; +class CodeLocationCall; +class CodeLocationNearCall; +class CodeLocationDataLabelCompact; +class CodeLocationDataLabel32; +class CodeLocationDataLabelPtr; +class CodeLocationConvertibleLoad; + +// The CodeLocation* types are all pretty much do-nothing wrappers around +// CodePtr (or MacroAssemblerCodePtr, to give it its full name). These +// classes only exist to provide type-safety when linking and patching code. +// +// The one new piece of functionallity introduced by these classes is the +// ability to create (or put another way, to re-discover) another CodeLocation +// at an offset from one you already know. When patching code to optimize it +// we often want to patch a number of instructions that are short, fixed +// offsets apart. To reduce memory overhead we will only retain a pointer to +// one of the instructions, and we will use the *AtOffset methods provided by +// CodeLocationCommon to find the other points in the code to modify. +class CodeLocationCommon : public MacroAssemblerCodePtr { +public: + CodeLocationInstruction instructionAtOffset(int offset); + CodeLocationLabel labelAtOffset(int offset); + CodeLocationJump jumpAtOffset(int offset); + CodeLocationCall callAtOffset(int offset); + CodeLocationNearCall nearCallAtOffset(int offset); + CodeLocationDataLabelPtr dataLabelPtrAtOffset(int offset); + CodeLocationDataLabel32 dataLabel32AtOffset(int offset); + CodeLocationDataLabelCompact dataLabelCompactAtOffset(int offset); + CodeLocationConvertibleLoad convertibleLoadAtOffset(int offset); + +protected: + CodeLocationCommon() + { + } + + CodeLocationCommon(MacroAssemblerCodePtr location) + : MacroAssemblerCodePtr(location) + { + } +}; + +class CodeLocationInstruction : public CodeLocationCommon { +public: + CodeLocationInstruction() {} + explicit CodeLocationInstruction(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationInstruction(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationLabel : public CodeLocationCommon { +public: + CodeLocationLabel() {} + explicit CodeLocationLabel(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationLabel(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationJump : public CodeLocationCommon { +public: + CodeLocationJump() {} + explicit CodeLocationJump(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationJump(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationCall : public CodeLocationCommon { +public: + CodeLocationCall() {} + explicit CodeLocationCall(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationCall(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationNearCall : public CodeLocationCommon { +public: + CodeLocationNearCall() {} + explicit CodeLocationNearCall(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationNearCall(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationDataLabel32 : public CodeLocationCommon { +public: + CodeLocationDataLabel32() {} + explicit CodeLocationDataLabel32(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationDataLabel32(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationDataLabelCompact : public CodeLocationCommon { +public: + CodeLocationDataLabelCompact() { } + explicit CodeLocationDataLabelCompact(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) { } + explicit CodeLocationDataLabelCompact(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) { } +}; + +class CodeLocationDataLabelPtr : public CodeLocationCommon { +public: + CodeLocationDataLabelPtr() {} + explicit CodeLocationDataLabelPtr(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) {} + explicit CodeLocationDataLabelPtr(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) {} +}; + +class CodeLocationConvertibleLoad : public CodeLocationCommon { +public: + CodeLocationConvertibleLoad() { } + explicit CodeLocationConvertibleLoad(MacroAssemblerCodePtr location) + : CodeLocationCommon(location) { } + explicit CodeLocationConvertibleLoad(void* location) + : CodeLocationCommon(MacroAssemblerCodePtr(location)) { } +}; + +inline CodeLocationInstruction CodeLocationCommon::instructionAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationInstruction(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationLabel CodeLocationCommon::labelAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationLabel(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationJump CodeLocationCommon::jumpAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationJump(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationCall CodeLocationCommon::callAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationCall(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationNearCall CodeLocationCommon::nearCallAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationNearCall(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationDataLabelPtr CodeLocationCommon::dataLabelPtrAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationDataLabelPtr(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationDataLabel32 CodeLocationCommon::dataLabel32AtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationDataLabel32(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationDataLabelCompact CodeLocationCommon::dataLabelCompactAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationDataLabelCompact(reinterpret_cast(dataLocation()) + offset); +} + +inline CodeLocationConvertibleLoad CodeLocationCommon::convertibleLoadAtOffset(int offset) +{ + ASSERT_VALID_CODE_OFFSET(offset); + return CodeLocationConvertibleLoad(reinterpret_cast(dataLocation()) + offset); +} + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // CodeLocation_h diff --git a/src/3rdparty/masm/assembler/LinkBuffer.cpp b/src/3rdparty/masm/assembler/LinkBuffer.cpp new file mode 100644 index 0000000000..c269157ba5 --- /dev/null +++ b/src/3rdparty/masm/assembler/LinkBuffer.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "LinkBuffer.h" + +#if ENABLE(ASSEMBLER) + +#include "Options.h" + +namespace JSC { + +LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithoutDisassembly() +{ + performFinalization(); + + return CodeRef(m_executableMemory); +} + +LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithDisassembly(const char* format, ...) +{ + ASSERT(Options::showDisassembly() || Options::showDFGDisassembly()); + + CodeRef result = finalizeCodeWithoutDisassembly(); + + dataLogF("Generated JIT code for "); + va_list argList; + va_start(argList, format); + WTF::dataLogFV(format, argList); + va_end(argList); + dataLogF(":\n"); + + dataLogF(" Code at [%p, %p):\n", result.code().executableAddress(), static_cast(result.code().executableAddress()) + result.size()); + disassemble(result.code(), m_size, " ", WTF::dataFile()); + + return result; +} + +void LinkBuffer::linkCode(void* ownerUID, JITCompilationEffort effort) +{ + ASSERT(!m_code); +#if !ENABLE(BRANCH_COMPACTION) + m_executableMemory = m_assembler->m_assembler.executableCopy(*m_globalData, ownerUID, effort); + if (!m_executableMemory) + return; + m_code = m_executableMemory->start(); + m_size = m_assembler->m_assembler.codeSize(); + ASSERT(m_code); +#else + m_initialSize = m_assembler->m_assembler.codeSize(); + m_executableMemory = m_globalData->executableAllocator.allocate(*m_globalData, m_initialSize, ownerUID, effort); + if (!m_executableMemory) + return; + m_code = (uint8_t*)m_executableMemory->start(); + ASSERT(m_code); + ExecutableAllocator::makeWritable(m_code, m_initialSize); + uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode(); + uint8_t* outData = reinterpret_cast(m_code); + int readPtr = 0; + int writePtr = 0; + Vector& jumpsToLink = m_assembler->jumpsToLink(); + unsigned jumpCount = jumpsToLink.size(); + for (unsigned i = 0; i < jumpCount; ++i) { + int offset = readPtr - writePtr; + ASSERT(!(offset & 1)); + + // Copy the instructions from the last jump to the current one. + size_t regionSize = jumpsToLink[i].from() - readPtr; + uint16_t* copySource = reinterpret_cast_ptr(inData + readPtr); + uint16_t* copyEnd = reinterpret_cast_ptr(inData + readPtr + regionSize); + uint16_t* copyDst = reinterpret_cast_ptr(outData + writePtr); + ASSERT(!(regionSize % 2)); + ASSERT(!(readPtr % 2)); + ASSERT(!(writePtr % 2)); + while (copySource != copyEnd) + *copyDst++ = *copySource++; + m_assembler->recordLinkOffsets(readPtr, jumpsToLink[i].from(), offset); + readPtr += regionSize; + writePtr += regionSize; + + // Calculate absolute address of the jump target, in the case of backwards + // branches we need to be precise, forward branches we are pessimistic + const uint8_t* target; + if (jumpsToLink[i].to() >= jumpsToLink[i].from()) + target = outData + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far + else + target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); + + JumpLinkType jumpLinkType = m_assembler->computeJumpType(jumpsToLink[i], outData + writePtr, target); + // Compact branch if we can... + if (m_assembler->canCompact(jumpsToLink[i].type())) { + // Step back in the write stream + int32_t delta = m_assembler->jumpSizeDelta(jumpsToLink[i].type(), jumpLinkType); + if (delta) { + writePtr -= delta; + m_assembler->recordLinkOffsets(jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr); + } + } + jumpsToLink[i].setFrom(writePtr); + } + // Copy everything after the last jump + memcpy(outData + writePtr, inData + readPtr, m_initialSize - readPtr); + m_assembler->recordLinkOffsets(readPtr, m_initialSize, readPtr - writePtr); + + for (unsigned i = 0; i < jumpCount; ++i) { + uint8_t* location = outData + jumpsToLink[i].from(); + uint8_t* target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); + m_assembler->link(jumpsToLink[i], location, target); + } + + jumpsToLink.clear(); + m_size = writePtr + m_initialSize - readPtr; + m_executableMemory->shrink(m_size); + +#if DUMP_LINK_STATISTICS + dumpLinkStatistics(m_code, m_initialSize, m_size); +#endif +#if DUMP_CODE + dumpCode(m_code, m_size); +#endif +#endif +} + +void LinkBuffer::performFinalization() +{ +#ifndef NDEBUG + ASSERT(!m_completed); + ASSERT(isValid()); + m_completed = true; +#endif + +#if ENABLE(BRANCH_COMPACTION) + ExecutableAllocator::makeExecutable(code(), m_initialSize); +#else + ExecutableAllocator::makeExecutable(code(), m_size); +#endif + MacroAssembler::cacheFlush(code(), m_size); +} + +#if DUMP_LINK_STATISTICS +void LinkBuffer::dumpLinkStatistics(void* code, size_t initializeSize, size_t finalSize) +{ + static unsigned linkCount = 0; + static unsigned totalInitialSize = 0; + static unsigned totalFinalSize = 0; + linkCount++; + totalInitialSize += initialSize; + totalFinalSize += finalSize; + dataLogF("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", + code, static_cast(initialSize), static_cast(finalSize), + static_cast(initialSize - finalSize), + 100.0 * (initialSize - finalSize) / initialSize); + dataLogF("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", + linkCount, totalInitialSize, totalFinalSize, totalInitialSize - totalFinalSize, + 100.0 * (totalInitialSize - totalFinalSize) / totalInitialSize); +} +#endif + +#if DUMP_CODE +void LinkBuffer::dumpCode(void* code, size_t size) +{ +#if CPU(ARM_THUMB2) + // Dump the generated code in an asm file format that can be assembled and then disassembled + // for debugging purposes. For example, save this output as jit.s: + // gcc -arch armv7 -c jit.s + // otool -tv jit.o + static unsigned codeCount = 0; + unsigned short* tcode = static_cast(code); + size_t tsize = size / sizeof(short); + char nameBuf[128]; + snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); + dataLogF("\t.syntax unified\n" + "\t.section\t__TEXT,__text,regular,pure_instructions\n" + "\t.globl\t%s\n" + "\t.align 2\n" + "\t.code 16\n" + "\t.thumb_func\t%s\n" + "# %p\n" + "%s:\n", nameBuf, nameBuf, code, nameBuf); + + for (unsigned i = 0; i < tsize; i++) + dataLogF("\t.short\t0x%x\n", tcode[i]); +#elif CPU(ARM_TRADITIONAL) + // gcc -c jit.s + // objdump -D jit.o + static unsigned codeCount = 0; + unsigned int* tcode = static_cast(code); + size_t tsize = size / sizeof(unsigned int); + char nameBuf[128]; + snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); + dataLogF("\t.globl\t%s\n" + "\t.align 4\n" + "\t.code 32\n" + "\t.text\n" + "# %p\n" + "%s:\n", nameBuf, code, nameBuf); + + for (unsigned i = 0; i < tsize; i++) + dataLogF("\t.long\t0x%x\n", tcode[i]); +#endif +} +#endif + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + + diff --git a/src/3rdparty/masm/assembler/LinkBuffer.h b/src/3rdparty/masm/assembler/LinkBuffer.h new file mode 100644 index 0000000000..e1882433c1 --- /dev/null +++ b/src/3rdparty/masm/assembler/LinkBuffer.h @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2009, 2010, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LinkBuffer_h +#define LinkBuffer_h + +#if ENABLE(ASSEMBLER) + +#define DUMP_LINK_STATISTICS 0 +#define DUMP_CODE 0 + +#define GLOBAL_THUNK_ID reinterpret_cast(static_cast(-1)) +#define REGEXP_CODE_ID reinterpret_cast(static_cast(-2)) + +#include "JITCompilationEffort.h" +#include "MacroAssembler.h" +#include +#include + +namespace JSC { + +class JSGlobalData; + +// LinkBuffer: +// +// This class assists in linking code generated by the macro assembler, once code generation +// has been completed, and the code has been copied to is final location in memory. At this +// time pointers to labels within the code may be resolved, and relative offsets to external +// addresses may be fixed. +// +// Specifically: +// * Jump objects may be linked to external targets, +// * The address of Jump objects may taken, such that it can later be relinked. +// * The return address of a Call may be acquired. +// * The address of a Label pointing into the code may be resolved. +// * The value referenced by a DataLabel may be set. +// +class LinkBuffer { + WTF_MAKE_NONCOPYABLE(LinkBuffer); + typedef MacroAssemblerCodeRef CodeRef; + typedef MacroAssemblerCodePtr CodePtr; + typedef MacroAssembler::Label Label; + typedef MacroAssembler::Jump Jump; + typedef MacroAssembler::PatchableJump PatchableJump; + typedef MacroAssembler::JumpList JumpList; + typedef MacroAssembler::Call Call; + typedef MacroAssembler::DataLabelCompact DataLabelCompact; + typedef MacroAssembler::DataLabel32 DataLabel32; + typedef MacroAssembler::DataLabelPtr DataLabelPtr; + typedef MacroAssembler::ConvertibleLoadLabel ConvertibleLoadLabel; +#if ENABLE(BRANCH_COMPACTION) + typedef MacroAssembler::LinkRecord LinkRecord; + typedef MacroAssembler::JumpLinkType JumpLinkType; +#endif + +public: + LinkBuffer(JSGlobalData& globalData, MacroAssembler* masm, void* ownerUID, JITCompilationEffort effort = JITCompilationMustSucceed) + : m_size(0) +#if ENABLE(BRANCH_COMPACTION) + , m_initialSize(0) +#endif + , m_code(0) + , m_assembler(masm) + , m_globalData(&globalData) +#ifndef NDEBUG + , m_completed(false) + , m_effort(effort) +#endif + { + linkCode(ownerUID, effort); + } + + ~LinkBuffer() + { + ASSERT(m_completed || (!m_executableMemory && m_effort == JITCompilationCanFail)); + } + + bool didFailToAllocate() const + { + return !m_executableMemory; + } + + bool isValid() const + { + return !didFailToAllocate(); + } + + // These methods are used to link or set values at code generation time. + + void link(Call call, FunctionPtr function) + { + ASSERT(call.isFlagSet(Call::Linkable)); + call.m_label = applyOffset(call.m_label); + MacroAssembler::linkCall(code(), call, function); + } + + void link(Jump jump, CodeLocationLabel label) + { + jump.m_label = applyOffset(jump.m_label); + MacroAssembler::linkJump(code(), jump, label); + } + + void link(JumpList list, CodeLocationLabel label) + { + for (unsigned i = 0; i < list.m_jumps.size(); ++i) + link(list.m_jumps[i], label); + } + + void patch(DataLabelPtr label, void* value) + { + AssemblerLabel target = applyOffset(label.m_label); + MacroAssembler::linkPointer(code(), target, value); + } + + void patch(DataLabelPtr label, CodeLocationLabel value) + { + AssemblerLabel target = applyOffset(label.m_label); + MacroAssembler::linkPointer(code(), target, value.executableAddress()); + } + + // These methods are used to obtain handles to allow the code to be relinked / repatched later. + + CodeLocationCall locationOf(Call call) + { + ASSERT(call.isFlagSet(Call::Linkable)); + ASSERT(!call.isFlagSet(Call::Near)); + return CodeLocationCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_label))); + } + + CodeLocationNearCall locationOfNearCall(Call call) + { + ASSERT(call.isFlagSet(Call::Linkable)); + ASSERT(call.isFlagSet(Call::Near)); + return CodeLocationNearCall(MacroAssembler::getLinkerAddress(code(), applyOffset(call.m_label))); + } + + CodeLocationLabel locationOf(PatchableJump jump) + { + return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(jump.m_jump.m_label))); + } + + CodeLocationLabel locationOf(Label label) + { + return CodeLocationLabel(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + CodeLocationDataLabelPtr locationOf(DataLabelPtr label) + { + return CodeLocationDataLabelPtr(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + CodeLocationDataLabel32 locationOf(DataLabel32 label) + { + return CodeLocationDataLabel32(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + CodeLocationDataLabelCompact locationOf(DataLabelCompact label) + { + return CodeLocationDataLabelCompact(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + CodeLocationConvertibleLoad locationOf(ConvertibleLoadLabel label) + { + return CodeLocationConvertibleLoad(MacroAssembler::getLinkerAddress(code(), applyOffset(label.m_label))); + } + + // This method obtains the return address of the call, given as an offset from + // the start of the code. + unsigned returnAddressOffset(Call call) + { + call.m_label = applyOffset(call.m_label); + return MacroAssembler::getLinkerCallReturnOffset(call); + } + + uint32_t offsetOf(Label label) + { + return applyOffset(label.m_label).m_offset; + } + + // Upon completion of all patching 'FINALIZE_CODE()' should be called once to + // complete generation of the code. Alternatively, call + // finalizeCodeWithoutDisassembly() directly if you have your own way of + // displaying disassembly. + + CodeRef finalizeCodeWithoutDisassembly(); + CodeRef finalizeCodeWithDisassembly(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); + + CodePtr trampolineAt(Label label) + { + return CodePtr(MacroAssembler::AssemblerType_T::getRelocatedAddress(code(), applyOffset(label.m_label))); + } + + void* debugAddress() + { + return m_code; + } + + size_t debugSize() + { + return m_size; + } + +private: + template T applyOffset(T src) + { +#if ENABLE(BRANCH_COMPACTION) + src.m_offset -= m_assembler->executableOffsetFor(src.m_offset); +#endif + return src; + } + + // Keep this private! - the underlying code should only be obtained externally via finalizeCode(). + void* code() + { + return m_code; + } + + void linkCode(void* ownerUID, JITCompilationEffort); + + void performFinalization(); + +#if DUMP_LINK_STATISTICS + static void dumpLinkStatistics(void* code, size_t initialSize, size_t finalSize); +#endif + +#if DUMP_CODE + static void dumpCode(void* code, size_t); +#endif + + RefPtr m_executableMemory; + size_t m_size; +#if ENABLE(BRANCH_COMPACTION) + size_t m_initialSize; +#endif + void* m_code; + MacroAssembler* m_assembler; + JSGlobalData* m_globalData; +#ifndef NDEBUG + bool m_completed; + JITCompilationEffort m_effort; +#endif +}; + +#define FINALIZE_CODE_IF(condition, linkBufferReference, dataLogFArgumentsForHeading) \ + (UNLIKELY((condition)) \ + ? ((linkBufferReference).finalizeCodeWithDisassembly dataLogFArgumentsForHeading) \ + : (linkBufferReference).finalizeCodeWithoutDisassembly()) + +// Use this to finalize code, like so: +// +// CodeRef code = FINALIZE_CODE(linkBuffer, ("my super thingy number %d", number)); +// +// Which, in disassembly mode, will print: +// +// Generated JIT code for my super thingy number 42: +// Code at [0x123456, 0x234567]: +// 0x123456: mov $0, 0 +// 0x12345a: ret +// +// ... and so on. +// +// Note that the dataLogFArgumentsForHeading are only evaluated when showDisassembly +// is true, so you can hide expensive disassembly-only computations inside there. + +#define FINALIZE_CODE(linkBufferReference, dataLogFArgumentsForHeading) \ + FINALIZE_CODE_IF(Options::showDisassembly(), linkBufferReference, dataLogFArgumentsForHeading) + +#define FINALIZE_DFG_CODE(linkBufferReference, dataLogFArgumentsForHeading) \ + FINALIZE_CODE_IF((Options::showDisassembly() || Options::showDFGDisassembly()), linkBufferReference, dataLogFArgumentsForHeading) + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // LinkBuffer_h diff --git a/src/3rdparty/masm/assembler/MIPSAssembler.h b/src/3rdparty/masm/assembler/MIPSAssembler.h new file mode 100644 index 0000000000..026f87e52a --- /dev/null +++ b/src/3rdparty/masm/assembler/MIPSAssembler.h @@ -0,0 +1,1032 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 University of Szeged + * All rights reserved. + * Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY MIPS TECHNOLOGIES, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MIPS TECHNOLOGIES, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MIPSAssembler_h +#define MIPSAssembler_h + +#if ENABLE(ASSEMBLER) && CPU(MIPS) + +#include "AssemblerBuffer.h" +#include "JITCompilationEffort.h" +#include +#include + +namespace JSC { + +typedef uint32_t MIPSWord; + +namespace MIPSRegisters { +typedef enum { + r0 = 0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, + r16, + r17, + r18, + r19, + r20, + r21, + r22, + r23, + r24, + r25, + r26, + r27, + r28, + r29, + r30, + r31, + zero = r0, + at = r1, + v0 = r2, + v1 = r3, + a0 = r4, + a1 = r5, + a2 = r6, + a3 = r7, + t0 = r8, + t1 = r9, + t2 = r10, + t3 = r11, + t4 = r12, + t5 = r13, + t6 = r14, + t7 = r15, + s0 = r16, + s1 = r17, + s2 = r18, + s3 = r19, + s4 = r20, + s5 = r21, + s6 = r22, + s7 = r23, + t8 = r24, + t9 = r25, + k0 = r26, + k1 = r27, + gp = r28, + sp = r29, + fp = r30, + ra = r31 +} RegisterID; + +typedef enum { + f0, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + f22, + f23, + f24, + f25, + f26, + f27, + f28, + f29, + f30, + f31 +} FPRegisterID; + +} // namespace MIPSRegisters + +class MIPSAssembler { +public: + typedef MIPSRegisters::RegisterID RegisterID; + typedef MIPSRegisters::FPRegisterID FPRegisterID; + typedef SegmentedVector Jumps; + + MIPSAssembler() + { + } + + // MIPS instruction opcode field position + enum { + OP_SH_RD = 11, + OP_SH_RT = 16, + OP_SH_RS = 21, + OP_SH_SHAMT = 6, + OP_SH_CODE = 16, + OP_SH_FD = 6, + OP_SH_FS = 11, + OP_SH_FT = 16 + }; + + void emitInst(MIPSWord op) + { + void* oldBase = m_buffer.data(); + + m_buffer.putInt(op); + + void* newBase = m_buffer.data(); + if (oldBase != newBase) + relocateJumps(oldBase, newBase); + } + + void nop() + { + emitInst(0x00000000); + } + + /* Need to insert one load data delay nop for mips1. */ + void loadDelayNop() + { +#if WTF_MIPS_ISA(1) + nop(); +#endif + } + + /* Need to insert one coprocessor access delay nop for mips1. */ + void copDelayNop() + { +#if WTF_MIPS_ISA(1) + nop(); +#endif + } + + void move(RegisterID rd, RegisterID rs) + { + /* addu */ + emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS)); + } + + /* Set an immediate value to a register. This may generate 1 or 2 + instructions. */ + void li(RegisterID dest, int imm) + { + if (imm >= -32768 && imm <= 32767) + addiu(dest, MIPSRegisters::zero, imm); + else if (imm >= 0 && imm < 65536) + ori(dest, MIPSRegisters::zero, imm); + else { + lui(dest, imm >> 16); + if (imm & 0xffff) + ori(dest, dest, imm); + } + } + + void lui(RegisterID rt, int imm) + { + emitInst(0x3c000000 | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void addiu(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x24000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void addu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000021 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void subu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000023 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void mult(RegisterID rs, RegisterID rt) + { + emitInst(0x00000018 | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void div(RegisterID rs, RegisterID rt) + { + emitInst(0x0000001a | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void mfhi(RegisterID rd) + { + emitInst(0x00000010 | (rd << OP_SH_RD)); + } + + void mflo(RegisterID rd) + { + emitInst(0x00000012 | (rd << OP_SH_RD)); + } + + void mul(RegisterID rd, RegisterID rs, RegisterID rt) + { +#if WTF_MIPS_ISA_AT_LEAST(32) + emitInst(0x70000002 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); +#else + mult(rs, rt); + mflo(rd); +#endif + } + + void andInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000024 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void andi(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x30000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void nor(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000027 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void orInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000025 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void ori(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x34000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void xorInsn(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x00000026 | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void xori(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x38000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void slt(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x0000002a | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void sltu(RegisterID rd, RegisterID rs, RegisterID rt) + { + emitInst(0x0000002b | (rd << OP_SH_RD) | (rs << OP_SH_RS) | (rt << OP_SH_RT)); + } + + void sltiu(RegisterID rt, RegisterID rs, int imm) + { + emitInst(0x2c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void sll(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000000 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void sllv(RegisterID rd, RegisterID rt, int rs) + { + emitInst(0x00000004 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); + } + + void sra(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000003 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void srav(RegisterID rd, RegisterID rt, RegisterID rs) + { + emitInst(0x00000007 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); + } + + void srl(RegisterID rd, RegisterID rt, int shamt) + { + emitInst(0x00000002 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | ((shamt & 0x1f) << OP_SH_SHAMT)); + } + + void srlv(RegisterID rd, RegisterID rt, RegisterID rs) + { + emitInst(0x00000006 | (rd << OP_SH_RD) | (rt << OP_SH_RT) | (rs << OP_SH_RS)); + } + + void lb(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x80000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void lbu(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x90000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void lw(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x8c000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void lwl(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x88000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void lwr(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x98000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void lh(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x84000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void lhu(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0x94000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + loadDelayNop(); + } + + void sb(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0xa0000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + } + + void sh(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0xa4000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + } + + void sw(RegisterID rt, RegisterID rs, int offset) + { + emitInst(0xac000000 | (rt << OP_SH_RT) | (rs << OP_SH_RS) | (offset & 0xffff)); + } + + void jr(RegisterID rs) + { + emitInst(0x00000008 | (rs << OP_SH_RS)); + } + + void jalr(RegisterID rs) + { + emitInst(0x0000f809 | (rs << OP_SH_RS)); + } + + void jal() + { + emitInst(0x0c000000); + } + + void bkpt() + { + int value = 512; /* BRK_BUG */ + emitInst(0x0000000d | ((value & 0x3ff) << OP_SH_CODE)); + } + + void bgez(RegisterID rs, int imm) + { + emitInst(0x04010000 | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void bltz(RegisterID rs, int imm) + { + emitInst(0x04000000 | (rs << OP_SH_RS) | (imm & 0xffff)); + } + + void beq(RegisterID rs, RegisterID rt, int imm) + { + emitInst(0x10000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void bne(RegisterID rs, RegisterID rt, int imm) + { + emitInst(0x14000000 | (rs << OP_SH_RS) | (rt << OP_SH_RT) | (imm & 0xffff)); + } + + void bc1t() + { + emitInst(0x45010000); + } + + void bc1f() + { + emitInst(0x45000000); + } + + void appendJump() + { + m_jumps.append(m_buffer.label()); + } + + void addd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200000 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + } + + void subd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200001 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + } + + void muld(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200002 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + } + + void divd(FPRegisterID fd, FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200003 | (fd << OP_SH_FD) | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + } + + void lwc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xc4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); + copDelayNop(); + } + + void ldc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xd4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); + } + + void swc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xe4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); + } + + void sdc1(FPRegisterID ft, RegisterID rs, int offset) + { + emitInst(0xf4000000 | (ft << OP_SH_FT) | (rs << OP_SH_RS) | (offset & 0xffff)); + } + + void mtc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44800000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void mthc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44e00000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void mfc1(RegisterID rt, FPRegisterID fs) + { + emitInst(0x44000000 | (fs << OP_SH_FS) | (rt << OP_SH_RT)); + copDelayNop(); + } + + void sqrtd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200004 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void truncwd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x4620000d | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtdw(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46800021 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtds(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46000021 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtwd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200024 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void cvtsd(FPRegisterID fd, FPRegisterID fs) + { + emitInst(0x46200020 | (fd << OP_SH_FD) | (fs << OP_SH_FS)); + } + + void ceqd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200032 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cngtd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003f | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cnged(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003d | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cltd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003c | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cled(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x4620003e | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cueqd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200033 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void coled(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200036 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void coltd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200034 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void culed(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200037 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + void cultd(FPRegisterID fs, FPRegisterID ft) + { + emitInst(0x46200035 | (fs << OP_SH_FS) | (ft << OP_SH_FT)); + copDelayNop(); + } + + // General helpers + + AssemblerLabel labelIgnoringWatchpoints() + { + return m_buffer.label(); + } + + AssemblerLabel label() + { + return m_buffer.label(); + } + + AssemblerLabel align(int alignment) + { + while (!m_buffer.isAligned(alignment)) + bkpt(); + + return label(); + } + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + // Assembler admin methods: + + size_t codeSize() const + { + return m_buffer.codeSize(); + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + RefPtr result = m_buffer.executableCopy(globalData, ownerUID, effort); + if (!result) + return 0; + + relocateJumps(m_buffer.data(), result->start()); + return result.release(); + } + + unsigned debugOffset() { return m_buffer.debugOffset(); } + + // Assembly helpers for moving data between fp and registers. + void vmov(RegisterID rd1, RegisterID rd2, FPRegisterID rn) + { + mfc1(rd1, rn); + mfc1(rd2, FPRegisterID(rn + 1)); + } + + void vmov(FPRegisterID rd, RegisterID rn1, RegisterID rn2) + { + mtc1(rn1, rd); + mtc1(rn2, FPRegisterID(rd + 1)); + } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + // The return address is after a call and a delay slot instruction + return call.m_offset; + } + + // Linking & patching: + // + // 'link' and 'patch' methods are for use on unprotected code - such as the code + // within the AssemblerBuffer, and code being patched by the patch buffer. Once + // code has been finalized it is (platform support permitting) within a non- + // writable region of memory; to modify the code in an execute-only execuable + // pool the 'repatch' and 'relink' methods should be used. + + void linkJump(AssemblerLabel from, AssemblerLabel to) + { + ASSERT(to.isSet()); + ASSERT(from.isSet()); + MIPSWord* insn = reinterpret_cast(reinterpret_cast(m_buffer.data()) + from.m_offset); + MIPSWord* toPos = reinterpret_cast(reinterpret_cast(m_buffer.data()) + to.m_offset); + + ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5))); + insn = insn - 6; + linkWithOffset(insn, toPos); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + + ASSERT(!(*(insn - 1)) && !(*(insn - 2)) && !(*(insn - 3)) && !(*(insn - 5))); + insn = insn - 6; + linkWithOffset(insn, to); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + linkCallInternal(insn, to); + } + + static void linkPointer(void* code, AssemblerLabel from, void* to) + { + MIPSWord* insn = reinterpret_cast(reinterpret_cast(code) + from.m_offset); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + *insn = (*insn & 0xffff0000) | ((reinterpret_cast(to) >> 16) & 0xffff); + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + *insn = (*insn & 0xffff0000) | (reinterpret_cast(to) & 0xffff); + } + + static void relinkJump(void* from, void* to) + { + MIPSWord* insn = reinterpret_cast(from); + + ASSERT(!(*(insn - 1)) && !(*(insn - 5))); + insn = insn - 6; + int flushSize = linkWithOffset(insn, to); + + cacheFlush(insn, flushSize); + } + + static void relinkCall(void* from, void* to) + { + void* start; + int size = linkCallInternal(from, to); + if (size == sizeof(MIPSWord)) + start = reinterpret_cast(reinterpret_cast(from) - 2 * sizeof(MIPSWord)); + else + start = reinterpret_cast(reinterpret_cast(from) - 4 * sizeof(MIPSWord)); + + cacheFlush(start, size); + } + + static void repatchInt32(void* from, int32_t to) + { + MIPSWord* insn = reinterpret_cast(from); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + *insn = (*insn & 0xffff0000) | ((to >> 16) & 0xffff); + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + *insn = (*insn & 0xffff0000) | (to & 0xffff); + insn--; + cacheFlush(insn, 2 * sizeof(MIPSWord)); + } + + static int32_t readInt32(void* from) + { + MIPSWord* insn = reinterpret_cast(from); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + int32_t result = (*insn & 0x0000ffff) << 16; + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + result |= *insn & 0x0000ffff; + return result; + } + + static void repatchCompact(void* where, int32_t value) + { + repatchInt32(where, value); + } + + static void repatchPointer(void* from, void* to) + { + repatchInt32(from, reinterpret_cast(to)); + } + + static void* readPointer(void* from) + { + return reinterpret_cast(readInt32(from)); + } + + static void* readCallTarget(void* from) + { + MIPSWord* insn = reinterpret_cast(from); + insn -= 4; + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + int32_t result = (*insn & 0x0000ffff) << 16; + insn++; + ASSERT((*insn & 0xfc000000) == 0x34000000); // ori + result |= *insn & 0x0000ffff; + return reinterpret_cast(result); + } + + static void cacheFlush(void* code, size_t size) + { +#if GCC_VERSION_AT_LEAST(4, 3, 0) +#if WTF_MIPS_ISA_REV(2) && !GCC_VERSION_AT_LEAST(4, 4, 3) + int lineSize; + asm("rdhwr %0, $1" : "=r" (lineSize)); + // + // Modify "start" and "end" to avoid GCC 4.3.0-4.4.2 bug in + // mips_expand_synci_loop that may execute synci one more time. + // "start" points to the fisrt byte of the cache line. + // "end" points to the last byte of the line before the last cache line. + // Because size is always a multiple of 4, this is safe to set + // "end" to the last byte. + // + intptr_t start = reinterpret_cast(code) & (-lineSize); + intptr_t end = ((reinterpret_cast(code) + size - 1) & (-lineSize)) - 1; + __builtin___clear_cache(reinterpret_cast(start), reinterpret_cast(end)); +#else + intptr_t end = reinterpret_cast(code) + size; + __builtin___clear_cache(reinterpret_cast(code), reinterpret_cast(end)); +#endif +#else + _flush_cache(reinterpret_cast(code), size, BCACHE); +#endif + } + + static void revertJumpToMove(void* instructionStart, RegisterID rt, int imm) + { + MIPSWord* insn = static_cast(instructionStart) + 1; + ASSERT((*insn & 0xfc000000) == 0x34000000); + *insn = (*insn & 0xfc1f0000) | (imm & 0xffff); + cacheFlush(insn, sizeof(MIPSWord)); + } + + static void replaceWithJump(void* instructionStart, void* to) + { + MIPSWord* instruction = reinterpret_cast(instructionStart); + intptr_t jumpTo = reinterpret_cast(to); + + // lui + instruction[0] = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((jumpTo >> 16) & 0xffff); + // ori + instruction[1] = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (jumpTo & 0xffff); + // jr + instruction[2] = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); + // nop + instruction[3] = 0x0; + + cacheFlush(instruction, sizeof(MIPSWord) * 4); + } + + static void replaceWithLoad(void* instructionStart) + { + MIPSWord* insn = reinterpret_cast(instructionStart); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + insn++; + ASSERT((*insn & 0xfc0007ff) == 0x00000021); // addu + insn++; + *insn = 0x8c000000 | ((*insn) & 0x3ffffff); // lw + cacheFlush(insn, 4); + } + + static void replaceWithAddressComputation(void* instructionStart) + { + MIPSWord* insn = reinterpret_cast(instructionStart); + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + insn++; + ASSERT((*insn & 0xfc0007ff) == 0x00000021); // addu + insn++; + *insn = 0x24000000 | ((*insn) & 0x3ffffff); // addiu + cacheFlush(insn, 4); + } + +private: + /* Update each jump in the buffer of newBase. */ + void relocateJumps(void* oldBase, void* newBase) + { + // Check each jump + for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) { + int pos = iter->m_offset; + MIPSWord* insn = reinterpret_cast(reinterpret_cast(newBase) + pos); + insn = insn + 2; + // Need to make sure we have 5 valid instructions after pos + if ((unsigned)pos >= m_buffer.codeSize() - 5 * sizeof(MIPSWord)) + continue; + + if ((*insn & 0xfc000000) == 0x08000000) { // j + int offset = *insn & 0x03ffffff; + int oldInsnAddress = (int)insn - (int)newBase + (int)oldBase; + int topFourBits = (oldInsnAddress + 4) >> 28; + int oldTargetAddress = (topFourBits << 28) | (offset << 2); + int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase; + int newInsnAddress = (int)insn; + if (((newInsnAddress + 4) >> 28) == (newTargetAddress >> 28)) + *insn = 0x08000000 | ((newTargetAddress >> 2) & 0x3ffffff); + else { + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + /* jr */ + *(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); + } + } else if ((*insn & 0xffe00000) == 0x3c000000) { // lui + int high = (*insn & 0xffff) << 16; + int low = *(insn + 1) & 0xffff; + int oldTargetAddress = high | low; + int newTargetAddress = oldTargetAddress - (int)oldBase + (int)newBase; + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + } + } + } + + static int linkWithOffset(MIPSWord* insn, void* to) + { + ASSERT((*insn & 0xfc000000) == 0x10000000 // beq + || (*insn & 0xfc000000) == 0x14000000 // bne + || (*insn & 0xffff0000) == 0x45010000 // bc1t + || (*insn & 0xffff0000) == 0x45000000); // bc1f + intptr_t diff = (reinterpret_cast(to) - reinterpret_cast(insn) - 4) >> 2; + + if (diff < -32768 || diff > 32767 || *(insn + 2) != 0x10000003) { + /* + Convert the sequence: + beq $2, $3, target + nop + b 1f + nop + nop + nop + 1: + + to the new sequence if possible: + bne $2, $3, 1f + nop + j target + nop + nop + nop + 1: + + OR to the new sequence: + bne $2, $3, 1f + nop + lui $25, target >> 16 + ori $25, $25, target & 0xffff + jr $25 + nop + 1: + + Note: beq/bne/bc1t/bc1f are converted to bne/beq/bc1f/bc1t. + */ + + if (*(insn + 2) == 0x10000003) { + if ((*insn & 0xfc000000) == 0x10000000) // beq + *insn = (*insn & 0x03ff0000) | 0x14000005; // bne + else if ((*insn & 0xfc000000) == 0x14000000) // bne + *insn = (*insn & 0x03ff0000) | 0x10000005; // beq + else if ((*insn & 0xffff0000) == 0x45010000) // bc1t + *insn = 0x45000005; // bc1f + else if ((*insn & 0xffff0000) == 0x45000000) // bc1f + *insn = 0x45010005; // bc1t + else + ASSERT(0); + } + + insn = insn + 2; + if ((reinterpret_cast(insn) + 4) >> 28 + == reinterpret_cast(to) >> 28) { + *insn = 0x08000000 | ((reinterpret_cast(to) >> 2) & 0x3ffffff); + *(insn + 1) = 0; + return 4 * sizeof(MIPSWord); + } + + intptr_t newTargetAddress = reinterpret_cast(to); + /* lui */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((newTargetAddress >> 16) & 0xffff); + /* ori */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (newTargetAddress & 0xffff); + /* jr */ + *(insn + 2) = 0x00000008 | (MIPSRegisters::t9 << OP_SH_RS); + return 5 * sizeof(MIPSWord); + } + + *insn = (*insn & 0xffff0000) | (diff & 0xffff); + return sizeof(MIPSWord); + } + + static int linkCallInternal(void* from, void* to) + { + MIPSWord* insn = reinterpret_cast(from); + insn = insn - 4; + + if ((*(insn + 2) & 0xfc000000) == 0x0c000000) { // jal + if ((reinterpret_cast(from) - 4) >> 28 + == reinterpret_cast(to) >> 28) { + *(insn + 2) = 0x0c000000 | ((reinterpret_cast(to) >> 2) & 0x3ffffff); + return sizeof(MIPSWord); + } + + /* lui $25, (to >> 16) & 0xffff */ + *insn = 0x3c000000 | (MIPSRegisters::t9 << OP_SH_RT) | ((reinterpret_cast(to) >> 16) & 0xffff); + /* ori $25, $25, to & 0xffff */ + *(insn + 1) = 0x34000000 | (MIPSRegisters::t9 << OP_SH_RT) | (MIPSRegisters::t9 << OP_SH_RS) | (reinterpret_cast(to) & 0xffff); + /* jalr $25 */ + *(insn + 2) = 0x0000f809 | (MIPSRegisters::t9 << OP_SH_RS); + return 3 * sizeof(MIPSWord); + } + + ASSERT((*insn & 0xffe00000) == 0x3c000000); // lui + ASSERT((*(insn + 1) & 0xfc000000) == 0x34000000); // ori + + /* lui */ + *insn = (*insn & 0xffff0000) | ((reinterpret_cast(to) >> 16) & 0xffff); + /* ori */ + *(insn + 1) = (*(insn + 1) & 0xffff0000) | (reinterpret_cast(to) & 0xffff); + return 2 * sizeof(MIPSWord); + } + + AssemblerBuffer m_buffer; + Jumps m_jumps; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(MIPS) + +#endif // MIPSAssembler_h diff --git a/src/3rdparty/masm/assembler/MacroAssembler.h b/src/3rdparty/masm/assembler/MacroAssembler.h new file mode 100644 index 0000000000..3d57340f93 --- /dev/null +++ b/src/3rdparty/masm/assembler/MacroAssembler.h @@ -0,0 +1,1464 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssembler_h +#define MacroAssembler_h + +#include + +#if ENABLE(ASSEMBLER) + +#if CPU(ARM_THUMB2) +#include "MacroAssemblerARMv7.h" +namespace JSC { typedef MacroAssemblerARMv7 MacroAssemblerBase; }; + +#elif CPU(ARM_TRADITIONAL) +#include "MacroAssemblerARM.h" +namespace JSC { typedef MacroAssemblerARM MacroAssemblerBase; }; + +#elif CPU(MIPS) +#include "MacroAssemblerMIPS.h" +namespace JSC { +typedef MacroAssemblerMIPS MacroAssemblerBase; +}; + +#elif CPU(X86) +#include "MacroAssemblerX86.h" +namespace JSC { typedef MacroAssemblerX86 MacroAssemblerBase; }; + +#elif CPU(X86_64) +#include "MacroAssemblerX86_64.h" +namespace JSC { typedef MacroAssemblerX86_64 MacroAssemblerBase; }; + +#elif CPU(SH4) +#include "MacroAssemblerSH4.h" +namespace JSC { +typedef MacroAssemblerSH4 MacroAssemblerBase; +}; + +#else +#error "The MacroAssembler is not supported on this platform." +#endif + +namespace JSC { + +class MacroAssembler : public MacroAssemblerBase { +public: + + using MacroAssemblerBase::pop; + using MacroAssemblerBase::jump; + using MacroAssemblerBase::branch32; + using MacroAssemblerBase::move; + +#if ENABLE(JIT_CONSTANT_BLINDING) + using MacroAssemblerBase::add32; + using MacroAssemblerBase::and32; + using MacroAssemblerBase::branchAdd32; + using MacroAssemblerBase::branchMul32; + using MacroAssemblerBase::branchSub32; + using MacroAssemblerBase::lshift32; + using MacroAssemblerBase::or32; + using MacroAssemblerBase::rshift32; + using MacroAssemblerBase::store32; + using MacroAssemblerBase::sub32; + using MacroAssemblerBase::urshift32; + using MacroAssemblerBase::xor32; +#endif + + static const double twoToThe32; // This is super useful for some double code. + + // Utilities used by the DFG JIT. +#if ENABLE(DFG_JIT) + using MacroAssemblerBase::invert; + + static DoubleCondition invert(DoubleCondition cond) + { + switch (cond) { + case DoubleEqual: + return DoubleNotEqualOrUnordered; + case DoubleNotEqual: + return DoubleEqualOrUnordered; + case DoubleGreaterThan: + return DoubleLessThanOrEqualOrUnordered; + case DoubleGreaterThanOrEqual: + return DoubleLessThanOrUnordered; + case DoubleLessThan: + return DoubleGreaterThanOrEqualOrUnordered; + case DoubleLessThanOrEqual: + return DoubleGreaterThanOrUnordered; + case DoubleEqualOrUnordered: + return DoubleNotEqual; + case DoubleNotEqualOrUnordered: + return DoubleEqual; + case DoubleGreaterThanOrUnordered: + return DoubleLessThanOrEqual; + case DoubleGreaterThanOrEqualOrUnordered: + return DoubleLessThan; + case DoubleLessThanOrUnordered: + return DoubleGreaterThanOrEqual; + case DoubleLessThanOrEqualOrUnordered: + return DoubleGreaterThan; + default: + ASSERT_NOT_REACHED(); + return DoubleEqual; // make compiler happy + } + } + + static bool isInvertible(ResultCondition cond) + { + switch (cond) { + case Zero: + case NonZero: + return true; + default: + return false; + } + } + + static ResultCondition invert(ResultCondition cond) + { + switch (cond) { + case Zero: + return NonZero; + case NonZero: + return Zero; + default: + ASSERT_NOT_REACHED(); + return Zero; // Make compiler happy for release builds. + } + } +#endif + + // Platform agnostic onvenience functions, + // described in terms of other macro assembly methods. + void pop() + { + addPtr(TrustedImm32(sizeof(void*)), stackPointerRegister); + } + + void peek(RegisterID dest, int index = 0) + { + loadPtr(Address(stackPointerRegister, (index * sizeof(void*))), dest); + } + + Address addressForPoke(int index) + { + return Address(stackPointerRegister, (index * sizeof(void*))); + } + + void poke(RegisterID src, int index = 0) + { + storePtr(src, addressForPoke(index)); + } + + void poke(TrustedImm32 value, int index = 0) + { + store32(value, addressForPoke(index)); + } + + void poke(TrustedImmPtr imm, int index = 0) + { + storePtr(imm, addressForPoke(index)); + } + +#if CPU(X86_64) + void peek64(RegisterID dest, int index = 0) + { + load64(Address(stackPointerRegister, (index * sizeof(void*))), dest); + } + + void poke(TrustedImm64 value, int index = 0) + { + store64(value, addressForPoke(index)); + } + + void poke64(RegisterID src, int index = 0) + { + store64(src, addressForPoke(index)); + } +#endif + + + // Backwards banches, these are currently all implemented using existing forwards branch mechanisms. + void branchPtr(RelationalCondition cond, RegisterID op1, TrustedImmPtr imm, Label target) + { + branchPtr(cond, op1, imm).linkTo(target, this); + } + void branchPtr(RelationalCondition cond, RegisterID op1, ImmPtr imm, Label target) + { + branchPtr(cond, op1, imm).linkTo(target, this); + } + + void branch32(RelationalCondition cond, RegisterID op1, RegisterID op2, Label target) + { + branch32(cond, op1, op2).linkTo(target, this); + } + + void branch32(RelationalCondition cond, RegisterID op1, TrustedImm32 imm, Label target) + { + branch32(cond, op1, imm).linkTo(target, this); + } + + void branch32(RelationalCondition cond, RegisterID op1, Imm32 imm, Label target) + { + branch32(cond, op1, imm).linkTo(target, this); + } + + void branch32(RelationalCondition cond, RegisterID left, Address right, Label target) + { + branch32(cond, left, right).linkTo(target, this); + } + + Jump branch32(RelationalCondition cond, TrustedImm32 left, RegisterID right) + { + return branch32(commute(cond), right, left); + } + + Jump branch32(RelationalCondition cond, Imm32 left, RegisterID right) + { + return branch32(commute(cond), right, left); + } + + void branchTestPtr(ResultCondition cond, RegisterID reg, Label target) + { + branchTestPtr(cond, reg).linkTo(target, this); + } + +#if !CPU(ARM_THUMB2) + PatchableJump patchableBranchPtr(RelationalCondition cond, Address left, TrustedImmPtr right = TrustedImmPtr(0)) + { + return PatchableJump(branchPtr(cond, left, right)); + } + + PatchableJump patchableBranchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + return PatchableJump(branchPtrWithPatch(cond, left, dataLabel, initialRightValue)); + } + + PatchableJump patchableJump() + { + return PatchableJump(jump()); + } + + PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + return PatchableJump(branchTest32(cond, reg, mask)); + } +#endif // !CPU(ARM_THUMB2) + +#if !CPU(ARM) + PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) + { + return PatchableJump(branch32(cond, reg, imm)); + } +#endif // !(CPU(ARM) + + void jump(Label target) + { + jump().linkTo(target, this); + } + + // Commute a relational condition, returns a new condition that will produce + // the same results given the same inputs but with their positions exchanged. + static RelationalCondition commute(RelationalCondition condition) + { + switch (condition) { + case Above: + return Below; + case AboveOrEqual: + return BelowOrEqual; + case Below: + return Above; + case BelowOrEqual: + return AboveOrEqual; + case GreaterThan: + return LessThan; + case GreaterThanOrEqual: + return LessThanOrEqual; + case LessThan: + return GreaterThan; + case LessThanOrEqual: + return GreaterThanOrEqual; + default: + break; + } + + ASSERT(condition == Equal || condition == NotEqual); + return condition; + } + + static const unsigned BlindingModulus = 64; + bool shouldConsiderBlinding() + { + return !(random() & (BlindingModulus - 1)); + } + + // Ptr methods + // On 32-bit platforms (i.e. x86), these methods directly map onto their 32-bit equivalents. + // FIXME: should this use a test for 32-bitness instead of this specific exception? +#if !CPU(X86_64) + void addPtr(Address src, RegisterID dest) + { + add32(src, dest); + } + + void addPtr(AbsoluteAddress src, RegisterID dest) + { + add32(src, dest); + } + + void addPtr(RegisterID src, RegisterID dest) + { + add32(src, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID srcDest) + { + add32(imm, srcDest); + } + + void addPtr(TrustedImmPtr imm, RegisterID dest) + { + add32(TrustedImm32(imm), dest); + } + + void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + add32(imm, src, dest); + } + + void addPtr(TrustedImm32 imm, AbsoluteAddress address) + { + add32(imm, address); + } + + void andPtr(RegisterID src, RegisterID dest) + { + and32(src, dest); + } + + void andPtr(TrustedImm32 imm, RegisterID srcDest) + { + and32(imm, srcDest); + } + + void negPtr(RegisterID dest) + { + neg32(dest); + } + + void orPtr(RegisterID src, RegisterID dest) + { + or32(src, dest); + } + + void orPtr(RegisterID op1, RegisterID op2, RegisterID dest) + { + or32(op1, op2, dest); + } + + void orPtr(TrustedImmPtr imm, RegisterID dest) + { + or32(TrustedImm32(imm), dest); + } + + void orPtr(TrustedImm32 imm, RegisterID dest) + { + or32(imm, dest); + } + + void subPtr(RegisterID src, RegisterID dest) + { + sub32(src, dest); + } + + void subPtr(TrustedImm32 imm, RegisterID dest) + { + sub32(imm, dest); + } + + void subPtr(TrustedImmPtr imm, RegisterID dest) + { + sub32(TrustedImm32(imm), dest); + } + + void xorPtr(RegisterID src, RegisterID dest) + { + xor32(src, dest); + } + + void xorPtr(TrustedImm32 imm, RegisterID srcDest) + { + xor32(imm, srcDest); + } + + + void loadPtr(ImplicitAddress address, RegisterID dest) + { + load32(address, dest); + } + + void loadPtr(BaseIndex address, RegisterID dest) + { + load32(address, dest); + } + + void loadPtr(const void* address, RegisterID dest) + { + load32(address, dest); + } + + DataLabel32 loadPtrWithAddressOffsetPatch(Address address, RegisterID dest) + { + return load32WithAddressOffsetPatch(address, dest); + } + + DataLabelCompact loadPtrWithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + return load32WithCompactAddressOffsetPatch(address, dest); + } + + void move(ImmPtr imm, RegisterID dest) + { + move(Imm32(imm.asTrustedImmPtr()), dest); + } + + void comparePtr(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + compare32(cond, left, right, dest); + } + + void storePtr(RegisterID src, ImplicitAddress address) + { + store32(src, address); + } + + void storePtr(RegisterID src, BaseIndex address) + { + store32(src, address); + } + + void storePtr(RegisterID src, void* address) + { + store32(src, address); + } + + void storePtr(TrustedImmPtr imm, ImplicitAddress address) + { + store32(TrustedImm32(imm), address); + } + + void storePtr(ImmPtr imm, Address address) + { + store32(Imm32(imm.asTrustedImmPtr()), address); + } + + void storePtr(TrustedImmPtr imm, void* address) + { + store32(TrustedImm32(imm), address); + } + + DataLabel32 storePtrWithAddressOffsetPatch(RegisterID src, Address address) + { + return store32WithAddressOffsetPatch(src, address); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, RegisterID right) + { + return branch32(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, TrustedImmPtr right) + { + return branch32(cond, left, TrustedImm32(right)); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, ImmPtr right) + { + return branch32(cond, left, Imm32(right.asTrustedImmPtr())); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, Address right) + { + return branch32(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, Address left, RegisterID right) + { + return branch32(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + return branch32(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, Address left, TrustedImmPtr right) + { + return branch32(cond, left, TrustedImm32(right)); + } + + Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, TrustedImmPtr right) + { + return branch32(cond, left, TrustedImm32(right)); + } + + Jump branchSubPtr(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchSub32(cond, src, dest); + } + + Jump branchTestPtr(ResultCondition cond, RegisterID reg, RegisterID mask) + { + return branchTest32(cond, reg, mask); + } + + Jump branchTestPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest32(cond, reg, mask); + } + + Jump branchTestPtr(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest32(cond, address, mask); + } + + Jump branchTestPtr(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest32(cond, address, mask); + } + + Jump branchAddPtr(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchAdd32(cond, src, dest); + } + + Jump branchSubPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchSub32(cond, imm, dest); + } + using MacroAssemblerBase::branchTest8; + Jump branchTest8(ResultCondition cond, ExtendedAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + return MacroAssemblerBase::branchTest8(cond, Address(address.base, address.offset), mask); + } +#else + void addPtr(RegisterID src, RegisterID dest) + { + add64(src, dest); + } + + void addPtr(Address src, RegisterID dest) + { + add64(src, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID srcDest) + { + add64(imm, srcDest); + } + + void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + add64(imm, src, dest); + } + + void addPtr(TrustedImm32 imm, Address address) + { + add64(imm, address); + } + + void addPtr(AbsoluteAddress src, RegisterID dest) + { + add64(src, dest); + } + + void addPtr(TrustedImmPtr imm, RegisterID dest) + { + add64(TrustedImm64(imm), dest); + } + + void addPtr(TrustedImm32 imm, AbsoluteAddress address) + { + add64(imm, address); + } + + void andPtr(RegisterID src, RegisterID dest) + { + and64(src, dest); + } + + void andPtr(TrustedImm32 imm, RegisterID srcDest) + { + and64(imm, srcDest); + } + + void negPtr(RegisterID dest) + { + neg64(dest); + } + + void orPtr(RegisterID src, RegisterID dest) + { + or64(src, dest); + } + + void orPtr(TrustedImm32 imm, RegisterID dest) + { + or64(imm, dest); + } + + void orPtr(TrustedImmPtr imm, RegisterID dest) + { + or64(TrustedImm64(imm), dest); + } + + void orPtr(RegisterID op1, RegisterID op2, RegisterID dest) + { + or64(op1, op2, dest); + } + + void orPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + or64(imm, src, dest); + } + + void rotateRightPtr(TrustedImm32 imm, RegisterID srcDst) + { + rotateRight64(imm, srcDst); + } + + void subPtr(RegisterID src, RegisterID dest) + { + sub64(src, dest); + } + + void subPtr(TrustedImm32 imm, RegisterID dest) + { + sub64(imm, dest); + } + + void subPtr(TrustedImmPtr imm, RegisterID dest) + { + sub64(TrustedImm64(imm), dest); + } + + void xorPtr(RegisterID src, RegisterID dest) + { + xor64(src, dest); + } + + void xorPtr(RegisterID src, Address dest) + { + xor64(src, dest); + } + + void xorPtr(TrustedImm32 imm, RegisterID srcDest) + { + xor64(imm, srcDest); + } + + void loadPtr(ImplicitAddress address, RegisterID dest) + { + load64(address, dest); + } + + void loadPtr(BaseIndex address, RegisterID dest) + { + load64(address, dest); + } + + void loadPtr(const void* address, RegisterID dest) + { + load64(address, dest); + } + + DataLabel32 loadPtrWithAddressOffsetPatch(Address address, RegisterID dest) + { + return load64WithAddressOffsetPatch(address, dest); + } + + DataLabelCompact loadPtrWithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + return load64WithCompactAddressOffsetPatch(address, dest); + } + + void storePtr(RegisterID src, ImplicitAddress address) + { + store64(src, address); + } + + void storePtr(RegisterID src, BaseIndex address) + { + store64(src, address); + } + + void storePtr(RegisterID src, void* address) + { + store64(src, address); + } + + void storePtr(TrustedImmPtr imm, ImplicitAddress address) + { + store64(TrustedImm64(imm), address); + } + + void storePtr(TrustedImmPtr imm, BaseIndex address) + { + store64(TrustedImm64(imm), address); + } + + DataLabel32 storePtrWithAddressOffsetPatch(RegisterID src, Address address) + { + return store64WithAddressOffsetPatch(src, address); + } + + void comparePtr(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + compare64(cond, left, right, dest); + } + + void comparePtr(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + compare64(cond, left, right, dest); + } + + void testPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) + { + test64(cond, reg, mask, dest); + } + + void testPtr(ResultCondition cond, RegisterID reg, RegisterID mask, RegisterID dest) + { + test64(cond, reg, mask, dest); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, RegisterID right) + { + return branch64(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, TrustedImmPtr right) + { + return branch64(cond, left, TrustedImm64(right)); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, Address right) + { + return branch64(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, Address left, RegisterID right) + { + return branch64(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + return branch64(cond, left, right); + } + + Jump branchPtr(RelationalCondition cond, Address left, TrustedImmPtr right) + { + return branch64(cond, left, TrustedImm64(right)); + } + + Jump branchTestPtr(ResultCondition cond, RegisterID reg, RegisterID mask) + { + return branchTest64(cond, reg, mask); + } + + Jump branchTestPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest64(cond, reg, mask); + } + + Jump branchTestPtr(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest64(cond, address, mask); + } + + Jump branchTestPtr(ResultCondition cond, Address address, RegisterID reg) + { + return branchTest64(cond, address, reg); + } + + Jump branchTestPtr(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest64(cond, address, mask); + } + + Jump branchTestPtr(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest64(cond, address, mask); + } + + Jump branchAddPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchAdd64(cond, imm, dest); + } + + Jump branchAddPtr(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchAdd64(cond, src, dest); + } + + Jump branchSubPtr(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchSub64(cond, imm, dest); + } + + Jump branchSubPtr(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchSub64(cond, src, dest); + } + + Jump branchSubPtr(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) + { + return branchSub64(cond, src1, src2, dest); + } + +#if ENABLE(JIT_CONSTANT_BLINDING) + using MacroAssemblerBase::and64; + using MacroAssemblerBase::convertInt32ToDouble; + using MacroAssemblerBase::store64; + bool shouldBlindDouble(double value) + { + // Don't trust NaN or +/-Infinity + if (!isfinite(value)) + return shouldConsiderBlinding(); + + // Try to force normalisation, and check that there's no change + // in the bit pattern + if (bitwise_cast(value * 1.0) != bitwise_cast(value)) + return shouldConsiderBlinding(); + + value = abs(value); + // Only allow a limited set of fractional components + double scaledValue = value * 8; + if (scaledValue / 8 != value) + return shouldConsiderBlinding(); + double frac = scaledValue - floor(scaledValue); + if (frac != 0.0) + return shouldConsiderBlinding(); + + return value > 0xff; + } + + bool shouldBlind(ImmPtr imm) + { +#if !defined(NDEBUG) + UNUSED_PARAM(imm); + // Debug always blind all constants, if only so we know + // if we've broken blinding during patch development. + return true; +#endif + + // First off we'll special case common, "safe" values to avoid hurting + // performance too much + uintptr_t value = imm.asTrustedImmPtr().asIntptr(); + switch (value) { + case 0xffff: + case 0xffffff: + case 0xffffffffL: + case 0xffffffffffL: + case 0xffffffffffffL: + case 0xffffffffffffffL: + case 0xffffffffffffffffL: + return false; + default: { + if (value <= 0xff) + return false; + if (~value <= 0xff) + return false; + } + } + + if (!shouldConsiderBlinding()) + return false; + + return shouldBlindForSpecificArch(value); + } + + struct RotatedImmPtr { + RotatedImmPtr(uintptr_t v1, uint8_t v2) + : value(v1) + , rotation(v2) + { + } + TrustedImmPtr value; + TrustedImm32 rotation; + }; + + RotatedImmPtr rotationBlindConstant(ImmPtr imm) + { + uint8_t rotation = random() % (sizeof(void*) * 8); + uintptr_t value = imm.asTrustedImmPtr().asIntptr(); + value = (value << rotation) | (value >> (sizeof(void*) * 8 - rotation)); + return RotatedImmPtr(value, rotation); + } + + void loadRotationBlindedConstant(RotatedImmPtr constant, RegisterID dest) + { + move(constant.value, dest); + rotateRightPtr(constant.rotation, dest); + } + + bool shouldBlind(Imm64 imm) + { +#if !defined(NDEBUG) + UNUSED_PARAM(imm); + // Debug always blind all constants, if only so we know + // if we've broken blinding during patch development. + return true; +#endif + + // First off we'll special case common, "safe" values to avoid hurting + // performance too much + uint64_t value = imm.asTrustedImm64().m_value; + switch (value) { + case 0xffff: + case 0xffffff: + case 0xffffffffL: + case 0xffffffffffL: + case 0xffffffffffffL: + case 0xffffffffffffffL: + case 0xffffffffffffffffL: + return false; + default: { + if (value <= 0xff) + return false; + if (~value <= 0xff) + return false; + + JSValue jsValue = JSValue::decode(value); + if (jsValue.isInt32()) + return shouldBlind(Imm32(jsValue.asInt32())); + if (jsValue.isDouble() && !shouldBlindDouble(jsValue.asDouble())) + return false; + + if (!shouldBlindDouble(bitwise_cast(value))) + return false; + } + } + + if (!shouldConsiderBlinding()) + return false; + + return shouldBlindForSpecificArch(value); + } + + struct RotatedImm64 { + RotatedImm64(uint64_t v1, uint8_t v2) + : value(v1) + , rotation(v2) + { + } + TrustedImm64 value; + TrustedImm32 rotation; + }; + + RotatedImm64 rotationBlindConstant(Imm64 imm) + { + uint8_t rotation = random() % (sizeof(int64_t) * 8); + uint64_t value = imm.asTrustedImm64().m_value; + value = (value << rotation) | (value >> (sizeof(int64_t) * 8 - rotation)); + return RotatedImm64(value, rotation); + } + + void loadRotationBlindedConstant(RotatedImm64 constant, RegisterID dest) + { + move(constant.value, dest); + rotateRight64(constant.rotation, dest); + } + + void convertInt32ToDouble(Imm32 imm, FPRegisterID dest) + { + if (shouldBlind(imm)) { + RegisterID scratchRegister = scratchRegisterForBlinding(); + loadXorBlindedConstant(xorBlindConstant(imm), scratchRegister); + convertInt32ToDouble(scratchRegister, dest); + } else + convertInt32ToDouble(imm.asTrustedImm32(), dest); + } + + void move(ImmPtr imm, RegisterID dest) + { + if (shouldBlind(imm)) + loadRotationBlindedConstant(rotationBlindConstant(imm), dest); + else + move(imm.asTrustedImmPtr(), dest); + } + + void move(Imm64 imm, RegisterID dest) + { + if (shouldBlind(imm)) + loadRotationBlindedConstant(rotationBlindConstant(imm), dest); + else + move(imm.asTrustedImm64(), dest); + } + + void and64(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = andBlindedConstant(imm); + and64(key.value1, dest); + and64(key.value2, dest); + } else + and64(imm.asTrustedImm32(), dest); + } + + Jump branchPtr(RelationalCondition cond, RegisterID left, ImmPtr right) + { + if (shouldBlind(right)) { + RegisterID scratchRegister = scratchRegisterForBlinding(); + loadRotationBlindedConstant(rotationBlindConstant(right), scratchRegister); + return branchPtr(cond, left, scratchRegister); + } + return branchPtr(cond, left, right.asTrustedImmPtr()); + } + + void storePtr(ImmPtr imm, Address dest) + { + if (shouldBlind(imm)) { + RegisterID scratchRegister = scratchRegisterForBlinding(); + loadRotationBlindedConstant(rotationBlindConstant(imm), scratchRegister); + storePtr(scratchRegister, dest); + } else + storePtr(imm.asTrustedImmPtr(), dest); + } + + void store64(Imm64 imm, Address dest) + { + if (shouldBlind(imm)) { + RegisterID scratchRegister = scratchRegisterForBlinding(); + loadRotationBlindedConstant(rotationBlindConstant(imm), scratchRegister); + store64(scratchRegister, dest); + } else + store64(imm.asTrustedImm64(), dest); + } + +#endif + +#endif // !CPU(X86_64) + +#if ENABLE(JIT_CONSTANT_BLINDING) + bool shouldBlind(Imm32 imm) + { +#if !defined(NDEBUG) + UNUSED_PARAM(imm); + // Debug always blind all constants, if only so we know + // if we've broken blinding during patch development. + return true; +#else + + // First off we'll special case common, "safe" values to avoid hurting + // performance too much + uint32_t value = imm.asTrustedImm32().m_value; + switch (value) { + case 0xffff: + case 0xffffff: + case 0xffffffff: + return false; + default: + if (value <= 0xff) + return false; + if (~value <= 0xff) + return false; + } + + if (!shouldConsiderBlinding()) + return false; + + return shouldBlindForSpecificArch(value); +#endif + } + + struct BlindedImm32 { + BlindedImm32(int32_t v1, int32_t v2) + : value1(v1) + , value2(v2) + { + } + TrustedImm32 value1; + TrustedImm32 value2; + }; + + uint32_t keyForConstant(uint32_t value, uint32_t& mask) + { + uint32_t key = random(); + if (value <= 0xff) + mask = 0xff; + else if (value <= 0xffff) + mask = 0xffff; + else if (value <= 0xffffff) + mask = 0xffffff; + else + mask = 0xffffffff; + return key & mask; + } + + uint32_t keyForConstant(uint32_t value) + { + uint32_t mask = 0; + return keyForConstant(value, mask); + } + + BlindedImm32 xorBlindConstant(Imm32 imm) + { + uint32_t baseValue = imm.asTrustedImm32().m_value; + uint32_t key = keyForConstant(baseValue); + return BlindedImm32(baseValue ^ key, key); + } + + BlindedImm32 additionBlindedConstant(Imm32 imm) + { + // The addition immediate may be used as a pointer offset. Keep aligned based on "imm". + static uint32_t maskTable[4] = { 0xfffffffc, 0xffffffff, 0xfffffffe, 0xffffffff }; + + uint32_t baseValue = imm.asTrustedImm32().m_value; + uint32_t key = keyForConstant(baseValue) & maskTable[baseValue & 3]; + if (key > baseValue) + key = key - baseValue; + return BlindedImm32(baseValue - key, key); + } + + BlindedImm32 andBlindedConstant(Imm32 imm) + { + uint32_t baseValue = imm.asTrustedImm32().m_value; + uint32_t mask = 0; + uint32_t key = keyForConstant(baseValue, mask); + ASSERT((baseValue & mask) == baseValue); + return BlindedImm32(((baseValue & key) | ~key) & mask, ((baseValue & ~key) | key) & mask); + } + + BlindedImm32 orBlindedConstant(Imm32 imm) + { + uint32_t baseValue = imm.asTrustedImm32().m_value; + uint32_t mask = 0; + uint32_t key = keyForConstant(baseValue, mask); + ASSERT((baseValue & mask) == baseValue); + return BlindedImm32((baseValue & key) & mask, (baseValue & ~key) & mask); + } + + void loadXorBlindedConstant(BlindedImm32 constant, RegisterID dest) + { + move(constant.value1, dest); + xor32(constant.value2, dest); + } + + void add32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = additionBlindedConstant(imm); + add32(key.value1, dest); + add32(key.value2, dest); + } else + add32(imm.asTrustedImm32(), dest); + } + + void addPtr(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = additionBlindedConstant(imm); + addPtr(key.value1, dest); + addPtr(key.value2, dest); + } else + addPtr(imm.asTrustedImm32(), dest); + } + + void and32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = andBlindedConstant(imm); + and32(key.value1, dest); + and32(key.value2, dest); + } else + and32(imm.asTrustedImm32(), dest); + } + + void andPtr(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = andBlindedConstant(imm); + andPtr(key.value1, dest); + andPtr(key.value2, dest); + } else + andPtr(imm.asTrustedImm32(), dest); + } + + void and32(Imm32 imm, RegisterID src, RegisterID dest) + { + if (shouldBlind(imm)) { + if (src == dest) + return and32(imm.asTrustedImm32(), dest); + loadXorBlindedConstant(xorBlindConstant(imm), dest); + and32(src, dest); + } else + and32(imm.asTrustedImm32(), src, dest); + } + + void move(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) + loadXorBlindedConstant(xorBlindConstant(imm), dest); + else + move(imm.asTrustedImm32(), dest); + } + + void or32(Imm32 imm, RegisterID src, RegisterID dest) + { + if (shouldBlind(imm)) { + if (src == dest) + return or32(imm, dest); + loadXorBlindedConstant(xorBlindConstant(imm), dest); + or32(src, dest); + } else + or32(imm.asTrustedImm32(), src, dest); + } + + void or32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = orBlindedConstant(imm); + or32(key.value1, dest); + or32(key.value2, dest); + } else + or32(imm.asTrustedImm32(), dest); + } + + void poke(Imm32 value, int index = 0) + { + store32(value, addressForPoke(index)); + } + + void poke(ImmPtr value, int index = 0) + { + storePtr(value, addressForPoke(index)); + } + +#if CPU(X86_64) + void poke(Imm64 value, int index = 0) + { + store64(value, addressForPoke(index)); + } +#endif + + void store32(Imm32 imm, Address dest) + { + if (shouldBlind(imm)) { +#if CPU(X86) || CPU(X86_64) + BlindedImm32 blind = xorBlindConstant(imm); + store32(blind.value1, dest); + xor32(blind.value2, dest); +#else + if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { + loadXorBlindedConstant(xorBlindConstant(imm), scratchRegister); + store32(scratchRegister, dest); + } else { + // If we don't have a scratch register available for use, we'll just + // place a random number of nops. + uint32_t nopCount = random() & 3; + while (nopCount--) + nop(); + store32(imm.asTrustedImm32(), dest); + } +#endif + } else + store32(imm.asTrustedImm32(), dest); + } + + void sub32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = additionBlindedConstant(imm); + sub32(key.value1, dest); + sub32(key.value2, dest); + } else + sub32(imm.asTrustedImm32(), dest); + } + + void subPtr(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 key = additionBlindedConstant(imm); + subPtr(key.value1, dest); + subPtr(key.value2, dest); + } else + subPtr(imm.asTrustedImm32(), dest); + } + + void xor32(Imm32 imm, RegisterID src, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 blind = xorBlindConstant(imm); + xor32(blind.value1, src, dest); + xor32(blind.value2, dest); + } else + xor32(imm.asTrustedImm32(), src, dest); + } + + void xor32(Imm32 imm, RegisterID dest) + { + if (shouldBlind(imm)) { + BlindedImm32 blind = xorBlindConstant(imm); + xor32(blind.value1, dest); + xor32(blind.value2, dest); + } else + xor32(imm.asTrustedImm32(), dest); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Imm32 right) + { + if (shouldBlind(right)) { + if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { + loadXorBlindedConstant(xorBlindConstant(right), scratchRegister); + return branch32(cond, left, scratchRegister); + } + // If we don't have a scratch register available for use, we'll just + // place a random number of nops. + uint32_t nopCount = random() & 3; + while (nopCount--) + nop(); + return branch32(cond, left, right.asTrustedImm32()); + } + + return branch32(cond, left, right.asTrustedImm32()); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, Imm32 imm, RegisterID dest) + { + if (src == dest) { + if (!scratchRegisterForBlinding()) { + // Release mode ASSERT, if this fails we will perform incorrect codegen. + CRASH(); + } + } + if (shouldBlind(imm)) { + if (src == dest) { + if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { + move(src, scratchRegister); + src = scratchRegister; + } + } + loadXorBlindedConstant(xorBlindConstant(imm), dest); + return branchAdd32(cond, src, dest); + } + return branchAdd32(cond, src, imm.asTrustedImm32(), dest); + } + + Jump branchMul32(ResultCondition cond, Imm32 imm, RegisterID src, RegisterID dest) + { + if (src == dest) { + if (!scratchRegisterForBlinding()) { + // Release mode ASSERT, if this fails we will perform incorrect codegen. + CRASH(); + } + } + if (shouldBlind(imm)) { + if (src == dest) { + if (RegisterID scratchRegister = (RegisterID)scratchRegisterForBlinding()) { + move(src, scratchRegister); + src = scratchRegister; + } + } + loadXorBlindedConstant(xorBlindConstant(imm), dest); + return branchMul32(cond, src, dest); + } + return branchMul32(cond, imm.asTrustedImm32(), src, dest); + } + + // branchSub32 takes a scratch register as 32 bit platforms make use of this, + // with src == dst, and on x86-32 we don't have a platform scratch register. + Jump branchSub32(ResultCondition cond, RegisterID src, Imm32 imm, RegisterID dest, RegisterID scratch) + { + if (shouldBlind(imm)) { + ASSERT(scratch != dest); + ASSERT(scratch != src); + loadXorBlindedConstant(xorBlindConstant(imm), scratch); + return branchSub32(cond, src, scratch, dest); + } + return branchSub32(cond, src, imm.asTrustedImm32(), dest); + } + + // Immediate shifts only have 5 controllable bits + // so we'll consider them safe for now. + TrustedImm32 trustedImm32ForShift(Imm32 imm) + { + return TrustedImm32(imm.asTrustedImm32().m_value & 31); + } + + void lshift32(Imm32 imm, RegisterID dest) + { + lshift32(trustedImm32ForShift(imm), dest); + } + + void lshift32(RegisterID src, Imm32 amount, RegisterID dest) + { + lshift32(src, trustedImm32ForShift(amount), dest); + } + + void rshift32(Imm32 imm, RegisterID dest) + { + rshift32(trustedImm32ForShift(imm), dest); + } + + void rshift32(RegisterID src, Imm32 amount, RegisterID dest) + { + rshift32(src, trustedImm32ForShift(amount), dest); + } + + void urshift32(Imm32 imm, RegisterID dest) + { + urshift32(trustedImm32ForShift(imm), dest); + } + + void urshift32(RegisterID src, Imm32 amount, RegisterID dest) + { + urshift32(src, trustedImm32ForShift(amount), dest); + } +#endif +}; + +} // namespace JSC + +#else // ENABLE(ASSEMBLER) + +// If there is no assembler for this platform, at least allow code to make references to +// some of the things it would otherwise define, albeit without giving that code any way +// of doing anything useful. +class MacroAssembler { +private: + MacroAssembler() { } + +public: + + enum RegisterID { NoRegister }; + enum FPRegisterID { NoFPRegister }; +}; + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssembler_h diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARM.cpp b/src/3rdparty/masm/assembler/MacroAssemblerARM.cpp new file mode 100644 index 0000000000..98dc3e9879 --- /dev/null +++ b/src/3rdparty/masm/assembler/MacroAssemblerARM.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2009 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#include "MacroAssemblerARM.h" + +#if OS(LINUX) +#include +#include +#include +#include +#include +#include +#endif + +namespace JSC { + +static bool isVFPPresent() +{ +#if OS(LINUX) + int fd = open("/proc/self/auxv", O_RDONLY); + if (fd > 0) { + Elf32_auxv_t aux; + while (read(fd, &aux, sizeof(Elf32_auxv_t))) { + if (aux.a_type == AT_HWCAP) { + close(fd); + return aux.a_un.a_val & HWCAP_VFP; + } + } + close(fd); + } +#endif + +#if (COMPILER(RVCT) && defined(__TARGET_FPU_VFP)) || (COMPILER(GCC) && defined(__VFP_FP__)) + return true; +#else + return false; +#endif +} + +const bool MacroAssemblerARM::s_isVFPPresent = isVFPPresent(); + +#if CPU(ARMV5_OR_LOWER) +/* On ARMv5 and below, natural alignment is required. */ +void MacroAssemblerARM::load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) +{ + ARMWord op2; + + ASSERT(address.scale >= 0 && address.scale <= 3); + op2 = m_assembler.lsl(address.index, static_cast(address.scale)); + + if (address.offset >= 0 && address.offset + 0x2 <= 0xff) { + m_assembler.add(ARMRegisters::S0, address.base, op2); + m_assembler.halfDtrUp(ARMAssembler::LoadUint16, dest, ARMRegisters::S0, ARMAssembler::getOp2Half(address.offset)); + m_assembler.halfDtrUp(ARMAssembler::LoadUint16, ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Half(address.offset + 0x2)); + } else if (address.offset < 0 && address.offset >= -0xff) { + m_assembler.add(ARMRegisters::S0, address.base, op2); + m_assembler.halfDtrDown(ARMAssembler::LoadUint16, dest, ARMRegisters::S0, ARMAssembler::getOp2Half(-address.offset)); + m_assembler.halfDtrDown(ARMAssembler::LoadUint16, ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Half(-address.offset - 0x2)); + } else { + m_assembler.moveImm(address.offset, ARMRegisters::S0); + m_assembler.add(ARMRegisters::S0, ARMRegisters::S0, op2); + m_assembler.halfDtrUpRegister(ARMAssembler::LoadUint16, dest, address.base, ARMRegisters::S0); + m_assembler.add(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::Op2Immediate | 0x2); + m_assembler.halfDtrUpRegister(ARMAssembler::LoadUint16, ARMRegisters::S0, address.base, ARMRegisters::S0); + } + m_assembler.orr(dest, dest, m_assembler.lsl(ARMRegisters::S0, 16)); +} +#endif + +} + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARM.h b/src/3rdparty/masm/assembler/MacroAssemblerARM.h new file mode 100644 index 0000000000..527126b438 --- /dev/null +++ b/src/3rdparty/masm/assembler/MacroAssemblerARM.h @@ -0,0 +1,1383 @@ +/* + * Copyright (C) 2008 Apple Inc. + * Copyright (C) 2009, 2010 University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerARM_h +#define MacroAssemblerARM_h + +#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#include "ARMAssembler.h" +#include "AbstractMacroAssembler.h" + +namespace JSC { + +class MacroAssemblerARM : public AbstractMacroAssembler { + static const int DoubleConditionMask = 0x0f; + static const int DoubleConditionBitSpecial = 0x10; + COMPILE_ASSERT(!(DoubleConditionBitSpecial & DoubleConditionMask), DoubleConditionBitSpecial_should_not_interfere_with_ARMAssembler_Condition_codes); +public: + typedef ARMRegisters::FPRegisterID FPRegisterID; + + enum RelationalCondition { + Equal = ARMAssembler::EQ, + NotEqual = ARMAssembler::NE, + Above = ARMAssembler::HI, + AboveOrEqual = ARMAssembler::CS, + Below = ARMAssembler::CC, + BelowOrEqual = ARMAssembler::LS, + GreaterThan = ARMAssembler::GT, + GreaterThanOrEqual = ARMAssembler::GE, + LessThan = ARMAssembler::LT, + LessThanOrEqual = ARMAssembler::LE + }; + + enum ResultCondition { + Overflow = ARMAssembler::VS, + Signed = ARMAssembler::MI, + Zero = ARMAssembler::EQ, + NonZero = ARMAssembler::NE + }; + + enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleEqual = ARMAssembler::EQ, + DoubleNotEqual = ARMAssembler::NE | DoubleConditionBitSpecial, + DoubleGreaterThan = ARMAssembler::GT, + DoubleGreaterThanOrEqual = ARMAssembler::GE, + DoubleLessThan = ARMAssembler::CC, + DoubleLessThanOrEqual = ARMAssembler::LS, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = ARMAssembler::EQ | DoubleConditionBitSpecial, + DoubleNotEqualOrUnordered = ARMAssembler::NE, + DoubleGreaterThanOrUnordered = ARMAssembler::HI, + DoubleGreaterThanOrEqualOrUnordered = ARMAssembler::CS, + DoubleLessThanOrUnordered = ARMAssembler::LT, + DoubleLessThanOrEqualOrUnordered = ARMAssembler::LE, + }; + + static const RegisterID stackPointerRegister = ARMRegisters::sp; + static const RegisterID linkRegister = ARMRegisters::lr; + + static const Scale ScalePtr = TimesFour; + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.adds(dest, dest, src); + } + + void add32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.adds(dest, op1, op2); + } + + void add32(TrustedImm32 imm, Address address) + { + load32(address, ARMRegisters::S1); + add32(imm, ARMRegisters::S1); + store32(ARMRegisters::S1, address); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.adds(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void add32(AbsoluteAddress src, RegisterID dest) + { + move(TrustedImmPtr(src.m_ptr), ARMRegisters::S1); + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); + add32(ARMRegisters::S1, dest); + } + + void add32(Address src, RegisterID dest) + { + load32(src, ARMRegisters::S1); + add32(ARMRegisters::S1, dest); + } + + void add32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.adds(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void and32(RegisterID src, RegisterID dest) + { + m_assembler.bitAnds(dest, dest, src); + } + + void and32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.bitAnds(dest, op1, op2); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + ARMWord w = m_assembler.getImm(imm.m_value, ARMRegisters::S0, true); + if (w & ARMAssembler::Op2InvertedImmediate) + m_assembler.bics(dest, dest, w & ~ARMAssembler::Op2InvertedImmediate); + else + m_assembler.bitAnds(dest, dest, w); + } + + void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ARMWord w = m_assembler.getImm(imm.m_value, ARMRegisters::S0, true); + if (w & ARMAssembler::Op2InvertedImmediate) + m_assembler.bics(dest, src, w & ~ARMAssembler::Op2InvertedImmediate); + else + m_assembler.bitAnds(dest, src, w); + } + + void lshift32(RegisterID shiftAmount, RegisterID dest) + { + lshift32(dest, shiftAmount, dest); + } + + void lshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + ARMWord w = ARMAssembler::getOp2Byte(0x1f); + m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); + + m_assembler.movs(dest, m_assembler.lslRegister(src, ARMRegisters::S0)); + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.lsl(dest, imm.m_value & 0x1f)); + } + + void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.lsl(src, imm.m_value & 0x1f)); + } + + void mul32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op2 == dest) { + if (op1 == dest) { + move(op2, ARMRegisters::S0); + op2 = ARMRegisters::S0; + } else { + // Swap the operands. + RegisterID tmp = op1; + op1 = op2; + op2 = tmp; + } + } + m_assembler.muls(dest, op1, op2); + } + + void mul32(RegisterID src, RegisterID dest) + { + mul32(src, dest, dest); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(imm, ARMRegisters::S0); + m_assembler.muls(dest, src, ARMRegisters::S0); + } + + void neg32(RegisterID srcDest) + { + m_assembler.rsbs(srcDest, srcDest, ARMAssembler::getOp2Byte(0)); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orrs(dest, dest, src); + } + + void or32(RegisterID src, AbsoluteAddress dest) + { + move(TrustedImmPtr(dest.m_ptr), ARMRegisters::S0); + load32(Address(ARMRegisters::S0), ARMRegisters::S1); + or32(src, ARMRegisters::S1); + store32(ARMRegisters::S1, ARMRegisters::S0); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.orrs(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.orrs(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.orrs(dest, op1, op2); + } + + void rshift32(RegisterID shiftAmount, RegisterID dest) + { + rshift32(dest, shiftAmount, dest); + } + + void rshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + ARMWord w = ARMAssembler::getOp2Byte(0x1f); + m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); + + m_assembler.movs(dest, m_assembler.asrRegister(src, ARMRegisters::S0)); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + rshift32(dest, imm, dest); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.asr(src, imm.m_value & 0x1f)); + } + + void urshift32(RegisterID shiftAmount, RegisterID dest) + { + urshift32(dest, shiftAmount, dest); + } + + void urshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + ARMWord w = ARMAssembler::getOp2Byte(0x1f); + m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w); + + m_assembler.movs(dest, m_assembler.lsrRegister(src, ARMRegisters::S0)); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.lsr(dest, imm.m_value & 0x1f)); + } + + void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.movs(dest, m_assembler.lsr(src, imm.m_value & 0x1f)); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.subs(dest, dest, src); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.subs(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void sub32(TrustedImm32 imm, Address address) + { + load32(address, ARMRegisters::S1); + sub32(imm, ARMRegisters::S1); + store32(ARMRegisters::S1, address); + } + + void sub32(Address src, RegisterID dest) + { + load32(src, ARMRegisters::S1); + sub32(ARMRegisters::S1, dest); + } + + void sub32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.subs(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void xor32(RegisterID src, RegisterID dest) + { + m_assembler.eors(dest, dest, src); + } + + void xor32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.eors(dest, op1, op2); + } + + void xor32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value == -1) + m_assembler.mvns(dest, dest); + else + m_assembler.eors(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (imm.m_value == -1) + m_assembler.mvns(dest, src); + else + m_assembler.eors(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void countLeadingZeros32(RegisterID src, RegisterID dest) + { +#if WTF_ARM_ARCH_AT_LEAST(5) + m_assembler.clz(dest, src); +#else + UNUSED_PARAM(src); + UNUSED_PARAM(dest); + ASSERT_NOT_REACHED(); +#endif + } + + void load8(ImplicitAddress address, RegisterID dest) + { + m_assembler.dataTransfer32(ARMAssembler::LoadUint8, dest, address.base, address.offset); + } + + void load8(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer32(ARMAssembler::LoadUint8, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void load8Signed(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer16(ARMAssembler::LoadInt8, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void load16(ImplicitAddress address, RegisterID dest) + { + m_assembler.dataTransfer16(ARMAssembler::LoadUint16, dest, address.base, address.offset); + } + + void load16(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer16(ARMAssembler::LoadUint16, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void load16Signed(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer16(ARMAssembler::LoadInt16, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void load32(ImplicitAddress address, RegisterID dest) + { + m_assembler.dataTransfer32(ARMAssembler::LoadUint32, dest, address.base, address.offset); + } + + void load32(BaseIndex address, RegisterID dest) + { + m_assembler.baseIndexTransfer32(ARMAssembler::LoadUint32, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + +#if CPU(ARMV5_OR_LOWER) + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest); +#else + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + load32(address, dest); + } +#endif + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + load16(address, dest); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result(this); + ASSERT(address.offset >= 0 && address.offset <= 255); + m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, address.base, address.offset); + return result; + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabel32 dataLabel(this); + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, 0); + m_assembler.dtrUpRegister(ARMAssembler::LoadUint32, dest, address.base, ARMRegisters::S0); + return dataLabel; + } + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return value >= -4095 && value <= 4095; + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabelCompact dataLabel(this); + ASSERT(isCompactPtrAlignedAddressOffset(address.offset)); + if (address.offset >= 0) + m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, address.base, address.offset); + else + m_assembler.dtrDown(ARMAssembler::LoadUint32, dest, address.base, address.offset); + return dataLabel; + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + DataLabel32 dataLabel(this); + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, 0); + m_assembler.dtrUpRegister(ARMAssembler::StoreUint32, src, address.base, ARMRegisters::S0); + return dataLabel; + } + + void store8(RegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint8, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void store8(TrustedImm32 imm, void* address) + { + move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); + m_assembler.moveImm(imm.m_value, ARMRegisters::S1); + m_assembler.dtrUp(ARMAssembler::StoreUint8, ARMRegisters::S1, ARMRegisters::S0, 0); + } + + void store16(RegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransfer16(ARMAssembler::StoreUint16, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void store32(RegisterID src, ImplicitAddress address) + { + m_assembler.dataTransfer32(ARMAssembler::StoreUint32, src, address.base, address.offset); + } + + void store32(RegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint32, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + move(imm, ARMRegisters::S1); + store32(ARMRegisters::S1, address); + } + + void store32(TrustedImm32 imm, BaseIndex address) + { + move(imm, ARMRegisters::S1); + m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint32, ARMRegisters::S1, address.base, address.index, static_cast(address.scale), address.offset); + } + + void store32(RegisterID src, void* address) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); + m_assembler.dtrUp(ARMAssembler::StoreUint32, src, ARMRegisters::S0, 0); + } + + void store32(TrustedImm32 imm, void* address) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); + m_assembler.moveImm(imm.m_value, ARMRegisters::S1); + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); + } + + void pop(RegisterID dest) + { + m_assembler.pop(dest); + } + + void push(RegisterID src) + { + m_assembler.push(src); + } + + void push(Address address) + { + load32(address, ARMRegisters::S1); + push(ARMRegisters::S1); + } + + void push(TrustedImm32 imm) + { + move(imm, ARMRegisters::S0); + push(ARMRegisters::S0); + } + + void move(TrustedImm32 imm, RegisterID dest) + { + m_assembler.moveImm(imm.m_value, dest); + } + + void move(RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.mov(dest, src); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + move(TrustedImm32(imm), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + m_assembler.mov(ARMRegisters::S0, reg1); + m_assembler.mov(reg1, reg2); + m_assembler.mov(reg2, ARMRegisters::S0); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest) + move(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest) + move(src, dest); + } + + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + load8(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(right.m_value & 0xFFFFFF00)); + load8(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right, int useConstantPool = 0) + { + m_assembler.cmp(left, right); + return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right, int useConstantPool = 0) + { + internalCompare32(left, right); + return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool)); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + load32(right, ARMRegisters::S1); + return branch32(cond, left, ARMRegisters::S1); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + load32(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + load32(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + load32(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + load32WithUnalignedHalfWords(left, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + load8(address, ARMRegisters::S1); + return branchTest32(cond, ARMRegisters::S1, mask); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); + load8(Address(ARMRegisters::S1), ARMRegisters::S1); + return branchTest32(cond, ARMRegisters::S1, mask); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + ASSERT((cond == Zero) || (cond == NonZero)); + m_assembler.tst(reg, mask); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + ARMWord w = m_assembler.getImm(mask.m_value, ARMRegisters::S0, true); + if (w & ARMAssembler::Op2InvertedImmediate) + m_assembler.bics(ARMRegisters::S0, reg, w & ~ARMAssembler::Op2InvertedImmediate); + else + m_assembler.tst(reg, w); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + load32(address, ARMRegisters::S1); + return branchTest32(cond, ARMRegisters::S1, mask); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + load32(address, ARMRegisters::S1); + return branchTest32(cond, ARMRegisters::S1, mask); + } + + Jump jump() + { + return Jump(m_assembler.jmp()); + } + + void jump(RegisterID target) + { + m_assembler.bx(target); + } + + void jump(Address address) + { + load32(address, ARMRegisters::pc); + } + + void jump(AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), ARMRegisters::S0); + load32(Address(ARMRegisters::S0, 0), ARMRegisters::pc); + } + + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + m_assembler.vmov(dest1, dest2, src); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + UNUSED_PARAM(scratch); + m_assembler.vmov(dest, src1, src2); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(src, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchAdd32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(op1, op2, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(src, imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + add32(imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + void mull32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op2 == dest) { + if (op1 == dest) { + move(op2, ARMRegisters::S0); + op2 = ARMRegisters::S0; + } else { + // Swap the operands. + RegisterID tmp = op1; + op1 = op2; + op2 = tmp; + } + } + m_assembler.mull(ARMRegisters::S1, dest, op1, op2); + m_assembler.cmp(ARMRegisters::S1, m_assembler.asr(dest, 31)); + } + + Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + mull32(src1, src2, dest); + cond = NonZero; + } + else + mul32(src1, src2, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchMul32(cond, src, dest, dest); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + move(imm, ARMRegisters::S0); + mull32(ARMRegisters::S0, src, dest); + cond = NonZero; + } + else + mul32(imm, src, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + sub32(src, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + sub32(imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + sub32(src, imm, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + m_assembler.subs(dest, op1, op2); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchNeg32(ResultCondition cond, RegisterID srcDest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + neg32(srcDest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); + or32(src, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + + PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) + { + internalCompare32(reg, imm); + Jump jump(m_assembler.loadBranchTarget(ARMRegisters::S1, ARMCondition(cond), true)); + m_assembler.bx(ARMRegisters::S1, ARMCondition(cond)); + return PatchableJump(jump); + } + + void breakpoint() + { + m_assembler.bkpt(0); + } + + Call nearCall() + { + m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true); + return Call(m_assembler.blx(ARMRegisters::S1), Call::LinkableNear); + } + + Call call(RegisterID target) + { + return Call(m_assembler.blx(target), Call::None); + } + + void call(Address address) + { + call32(address.base, address.offset); + } + + void ret() + { + m_assembler.bx(linkRegister); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmp(left, right); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + m_assembler.cmp(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + load8(left, ARMRegisters::S1); + compare32(cond, ARMRegisters::S1, right, dest); + } + + void test32(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) + { + if (mask.m_value == -1) + m_assembler.cmp(0, reg); + else + m_assembler.tst(reg, m_assembler.getImm(mask.m_value, ARMRegisters::S0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(0)); + m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond)); + } + + void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + load32(address, ARMRegisters::S1); + test32(cond, ARMRegisters::S1, mask, dest); + } + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + load8(address, ARMRegisters::S1); + test32(cond, ARMRegisters::S1, mask, dest); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.add(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S1, reinterpret_cast(address.m_ptr)); + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); + add32(imm, ARMRegisters::S1); + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address.m_ptr)); + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + ARMWord tmp; + + move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S0, ARMRegisters::S1, 0); + + if ((tmp = ARMAssembler::getOp2(imm.m_value)) != ARMAssembler::InvalidImmediate) + m_assembler.adds(ARMRegisters::S0, ARMRegisters::S0, tmp); + else if ((tmp = ARMAssembler::getOp2(-imm.m_value)) != ARMAssembler::InvalidImmediate) + m_assembler.subs(ARMRegisters::S0, ARMRegisters::S0, tmp); + else { + m_assembler.adds(ARMRegisters::S0, ARMRegisters::S0, m_assembler.getImm(imm.m_value, ARMRegisters::S1)); + move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1); + } + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S0, ARMRegisters::S1, 0); + + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S0, ARMRegisters::S1, sizeof(ARMWord)); + if (imm.m_value >= 0) + m_assembler.adc(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + else + m_assembler.sbc(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S0, ARMRegisters::S1, sizeof(ARMWord)); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S1, reinterpret_cast(address.m_ptr)); + m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0); + sub32(imm, ARMRegisters::S1); + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address.m_ptr)); + m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0); + } + + void load32(const void* address, RegisterID dest) + { + m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast(address)); + m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, ARMRegisters::S0, 0); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + load32(left.m_ptr, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + load32(left.m_ptr, ARMRegisters::S1); + return branch32(cond, ARMRegisters::S1, right); + } + + void relativeTableJump(RegisterID index, int scale) + { + ASSERT(scale >= 0 && scale <= 31); + m_assembler.add(ARMRegisters::pc, ARMRegisters::pc, m_assembler.lsl(index, scale)); + + // NOP the default prefetching + m_assembler.mov(ARMRegisters::r0, ARMRegisters::r0); + } + + Call call() + { + ensureSpace(2 * sizeof(ARMWord), sizeof(ARMWord)); + m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true); + return Call(m_assembler.blx(ARMRegisters::S1), Call::Linkable); + } + + Call tailRecursiveCall() + { + return Call::fromTailJump(jump()); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + return Call::fromTailJump(oldJump); + } + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + DataLabelPtr dataLabel(this); + m_assembler.ldrUniqueImmediate(dest, reinterpret_cast(initialValue.m_value)); + return dataLabel; + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S1); + Jump jump = branch32(cond, left, ARMRegisters::S1, true); + return jump; + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + load32(left, ARMRegisters::S1); + dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S0); + Jump jump = branch32(cond, ARMRegisters::S0, ARMRegisters::S1, true); + return jump; + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + DataLabelPtr dataLabel = moveWithPatch(initialValue, ARMRegisters::S1); + store32(ARMRegisters::S1, address); + return dataLabel; + } + + DataLabelPtr storePtrWithPatch(ImplicitAddress address) + { + return storePtrWithPatch(TrustedImmPtr(0), address); + } + + // Floating point operators + static bool supportsFloatingPoint() + { + return s_isVFPPresent; + } + + static bool supportsFloatingPointTruncate() + { + return false; + } + + static bool supportsFloatingPointSqrt() + { + return s_isVFPPresent; + } + static bool supportsFloatingPointAbs() { return false; } + + void loadFloat(BaseIndex address, FPRegisterID dest) + { + m_assembler.baseIndexTransferFloat(ARMAssembler::LoadFloat, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { + m_assembler.dataTransferFloat(ARMAssembler::LoadDouble, dest, address.base, address.offset); + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { + m_assembler.baseIndexTransferFloat(ARMAssembler::LoadDouble, dest, address.base, address.index, static_cast(address.scale), address.offset); + } + + void loadDouble(const void* address, FPRegisterID dest) + { + move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); + m_assembler.doubleDtrUp(ARMAssembler::LoadDouble, dest, ARMRegisters::S0, 0); + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransferFloat(ARMAssembler::StoreFloat, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { + m_assembler.dataTransferFloat(ARMAssembler::StoreDouble, src, address.base, address.offset); + } + + void storeDouble(FPRegisterID src, BaseIndex address) + { + m_assembler.baseIndexTransferFloat(ARMAssembler::StoreDouble, src, address.base, address.index, static_cast(address.scale), address.offset); + } + + void storeDouble(FPRegisterID src, const void* address) + { + move(TrustedImm32(reinterpret_cast(address)), ARMRegisters::S0); + m_assembler.dataTransferFloat(ARMAssembler::StoreDouble, src, ARMRegisters::S0, 0); + } + + void moveDouble(FPRegisterID src, FPRegisterID dest) + { + if (src != dest) + m_assembler.vmov_f64(dest, src); + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vadd_f64(dest, dest, src); + } + + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vadd_f64(dest, op1, op2); + } + + void addDouble(Address src, FPRegisterID dest) + { + loadDouble(src, ARMRegisters::SD0); + addDouble(ARMRegisters::SD0, dest); + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + loadDouble(address.m_ptr, ARMRegisters::SD0); + addDouble(ARMRegisters::SD0, dest); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vdiv_f64(dest, dest, src); + } + + void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vdiv_f64(dest, op1, op2); + } + + void divDouble(Address src, FPRegisterID dest) + { + ASSERT_NOT_REACHED(); // Untested + loadDouble(src, ARMRegisters::SD0); + divDouble(ARMRegisters::SD0, dest); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vsub_f64(dest, dest, src); + } + + void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vsub_f64(dest, op1, op2); + } + + void subDouble(Address src, FPRegisterID dest) + { + loadDouble(src, ARMRegisters::SD0); + subDouble(ARMRegisters::SD0, dest); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vmul_f64(dest, dest, src); + } + + void mulDouble(Address src, FPRegisterID dest) + { + loadDouble(src, ARMRegisters::SD0); + mulDouble(ARMRegisters::SD0, dest); + } + + void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vmul_f64(dest, op1, op2); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vsqrt_f64(dest, src); + } + + void absDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vabs_f64(dest, src); + } + + void negateDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vneg_f64(dest, src); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.vmov_vfp32(dest << 1, src); + m_assembler.vcvt_f64_s32(dest, dest << 1); + } + + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + load32(src, ARMRegisters::S1); + convertInt32ToDouble(ARMRegisters::S1, dest); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + move(TrustedImmPtr(src.m_ptr), ARMRegisters::S1); + load32(Address(ARMRegisters::S1), ARMRegisters::S1); + convertInt32ToDouble(ARMRegisters::S1, dest); + } + + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.vcvt_f64_f32(dst, src); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.vcvt_f32_f64(dst, src); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + m_assembler.vcmp_f64(left, right); + m_assembler.vmrs_apsr(); + if (cond & DoubleConditionBitSpecial) + m_assembler.cmp(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::VS); + return Jump(m_assembler.jmp(static_cast(cond & ~DoubleConditionMask))); + } + + // Truncates 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, INT_MIN). + enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + truncateDoubleToInt32(src, dest); + + m_assembler.add(ARMRegisters::S0, dest, ARMAssembler::getOp2Byte(1)); + m_assembler.bic(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(1)); + + ARMWord w = ARMAssembler::getOp2(0x80000000); + ASSERT(w != ARMAssembler::InvalidImmediate); + m_assembler.cmp(ARMRegisters::S0, w); + return Jump(m_assembler.jmp(branchType == BranchIfTruncateFailed ? ARMAssembler::EQ : ARMAssembler::NE)); + } + + Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + truncateDoubleToUint32(src, dest); + + m_assembler.add(ARMRegisters::S0, dest, ARMAssembler::getOp2Byte(1)); + m_assembler.bic(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(1)); + + m_assembler.cmp(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + return Jump(m_assembler.jmp(branchType == BranchIfTruncateFailed ? ARMAssembler::EQ : ARMAssembler::NE)); + } + + // Result is undefined if the value is outside of the integer range. + void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + m_assembler.vcvt_s32_f64(ARMRegisters::SD0 << 1, src); + m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); + } + + void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) + { + m_assembler.vcvt_u32_f64(ARMRegisters::SD0 << 1, src); + m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); + } + + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + m_assembler.vcvt_s32_f64(ARMRegisters::SD0 << 1, src); + m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + m_assembler.vcvt_f64_s32(ARMRegisters::SD0, ARMRegisters::SD0 << 1); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, ARMRegisters::SD0)); + + // If the result is zero, it might have been -0.0, and 0.0 equals to -0.0 + failureCases.append(branchTest32(Zero, dest)); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) + { + m_assembler.mov(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + convertInt32ToDouble(ARMRegisters::S0, scratch); + return branchDouble(DoubleNotEqual, reg, scratch); + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) + { + m_assembler.mov(ARMRegisters::S0, ARMAssembler::getOp2Byte(0)); + convertInt32ToDouble(ARMRegisters::S0, scratch); + return branchDouble(DoubleEqualOrUnordered, reg, scratch); + } + + // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. + static RelationalCondition invert(RelationalCondition cond) + { + ASSERT((static_cast(cond & 0x0fffffff)) == 0 && static_cast(cond) < static_cast(ARMAssembler::AL)); + return static_cast(cond ^ 0x10000000); + } + + void nop() + { + m_assembler.nop(); + } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(reinterpret_cast(ARMAssembler::readCallTarget(call.dataLocation()))); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + ARMAssembler::replaceWithJump(instructionStart.dataLocation(), destination.dataLocation()); + } + + static ptrdiff_t maxJumpReplacementSize() + { + ARMAssembler::maxJumpReplacementSize(); + return 0; + } + + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + return label.labelAtOffset(0); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID reg, void* initialValue) + { + ARMAssembler::revertJump(instructionStart.dataLocation(), reg, reinterpret_cast(initialValue) & 0xffff); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + UNREACHABLE_FOR_PLATFORM(); + } + +protected: + ARMAssembler::Condition ARMCondition(RelationalCondition cond) + { + return static_cast(cond); + } + + ARMAssembler::Condition ARMCondition(ResultCondition cond) + { + return static_cast(cond); + } + + void ensureSpace(int insnSpace, int constSpace) + { + m_assembler.ensureSpace(insnSpace, constSpace); + } + + int sizeOfConstantPool() + { + return m_assembler.sizeOfConstantPool(); + } + + void call32(RegisterID base, int32_t offset) + { + load32(Address(base, offset), ARMRegisters::S1); + m_assembler.blx(ARMRegisters::S1); + } + +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + void internalCompare32(RegisterID left, TrustedImm32 right) + { + ARMWord tmp = (static_cast(right.m_value) == 0x80000000) ? ARMAssembler::InvalidImmediate : m_assembler.getOp2(-right.m_value); + if (tmp != ARMAssembler::InvalidImmediate) + m_assembler.cmn(left, tmp); + else + m_assembler.cmp(left, m_assembler.getImm(right.m_value, ARMRegisters::S0)); + } + + static void linkCall(void* code, Call call, FunctionPtr function) + { + ARMAssembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + ARMAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + ARMAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static const bool s_isVFPPresent; +}; + +} + +#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL) + +#endif // MacroAssemblerARM_h diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h b/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h new file mode 100644 index 0000000000..8d7a3a69aa --- /dev/null +++ b/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h @@ -0,0 +1,1903 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerARMv7_h +#define MacroAssemblerARMv7_h + +#if ENABLE(ASSEMBLER) + +#include "ARMv7Assembler.h" +#include "AbstractMacroAssembler.h" + +namespace JSC { + +class MacroAssemblerARMv7 : public AbstractMacroAssembler { + // FIXME: switch dataTempRegister & addressTempRegister, or possibly use r7? + // - dTR is likely used more than aTR, and we'll get better instruction + // encoding if it's in the low 8 registers. + static const RegisterID dataTempRegister = ARMRegisters::ip; + static const RegisterID addressTempRegister = ARMRegisters::r3; + + static const ARMRegisters::FPDoubleRegisterID fpTempRegister = ARMRegisters::d7; + inline ARMRegisters::FPSingleRegisterID fpTempRegisterAsSingle() { return ARMRegisters::asSingle(fpTempRegister); } + +public: + MacroAssemblerARMv7() + : m_makeJumpPatchable(false) + { + } + + typedef ARMv7Assembler::LinkRecord LinkRecord; + typedef ARMv7Assembler::JumpType JumpType; + typedef ARMv7Assembler::JumpLinkType JumpLinkType; + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return value >= -255 && value <= 255; + } + + Vector& jumpsToLink() { return m_assembler.jumpsToLink(); } + void* unlinkedCode() { return m_assembler.unlinkedCode(); } + bool canCompact(JumpType jumpType) { return m_assembler.canCompact(jumpType); } + JumpLinkType computeJumpType(JumpType jumpType, const uint8_t* from, const uint8_t* to) { return m_assembler.computeJumpType(jumpType, from, to); } + JumpLinkType computeJumpType(LinkRecord& record, const uint8_t* from, const uint8_t* to) { return m_assembler.computeJumpType(record, from, to); } + void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset) {return m_assembler.recordLinkOffsets(regionStart, regionEnd, offset); } + int jumpSizeDelta(JumpType jumpType, JumpLinkType jumpLinkType) { return m_assembler.jumpSizeDelta(jumpType, jumpLinkType); } + void link(LinkRecord& record, uint8_t* from, uint8_t* to) { return m_assembler.link(record, from, to); } + + struct ArmAddress { + enum AddressType { + HasOffset, + HasIndex, + } type; + RegisterID base; + union { + int32_t offset; + struct { + RegisterID index; + Scale scale; + }; + } u; + + explicit ArmAddress(RegisterID base, int32_t offset = 0) + : type(HasOffset) + , base(base) + { + u.offset = offset; + } + + explicit ArmAddress(RegisterID base, RegisterID index, Scale scale = TimesOne) + : type(HasIndex) + , base(base) + { + u.index = index; + u.scale = scale; + } + }; + +public: + typedef ARMRegisters::FPDoubleRegisterID FPRegisterID; + + static const Scale ScalePtr = TimesFour; + + enum RelationalCondition { + Equal = ARMv7Assembler::ConditionEQ, + NotEqual = ARMv7Assembler::ConditionNE, + Above = ARMv7Assembler::ConditionHI, + AboveOrEqual = ARMv7Assembler::ConditionHS, + Below = ARMv7Assembler::ConditionLO, + BelowOrEqual = ARMv7Assembler::ConditionLS, + GreaterThan = ARMv7Assembler::ConditionGT, + GreaterThanOrEqual = ARMv7Assembler::ConditionGE, + LessThan = ARMv7Assembler::ConditionLT, + LessThanOrEqual = ARMv7Assembler::ConditionLE + }; + + enum ResultCondition { + Overflow = ARMv7Assembler::ConditionVS, + Signed = ARMv7Assembler::ConditionMI, + Zero = ARMv7Assembler::ConditionEQ, + NonZero = ARMv7Assembler::ConditionNE + }; + + enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleEqual = ARMv7Assembler::ConditionEQ, + DoubleNotEqual = ARMv7Assembler::ConditionVC, // Not the right flag! check for this & handle differently. + DoubleGreaterThan = ARMv7Assembler::ConditionGT, + DoubleGreaterThanOrEqual = ARMv7Assembler::ConditionGE, + DoubleLessThan = ARMv7Assembler::ConditionLO, + DoubleLessThanOrEqual = ARMv7Assembler::ConditionLS, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = ARMv7Assembler::ConditionVS, // Not the right flag! check for this & handle differently. + DoubleNotEqualOrUnordered = ARMv7Assembler::ConditionNE, + DoubleGreaterThanOrUnordered = ARMv7Assembler::ConditionHI, + DoubleGreaterThanOrEqualOrUnordered = ARMv7Assembler::ConditionHS, + DoubleLessThanOrUnordered = ARMv7Assembler::ConditionLT, + DoubleLessThanOrEqualOrUnordered = ARMv7Assembler::ConditionLE, + }; + + static const RegisterID stackPointerRegister = ARMRegisters::sp; + static const RegisterID linkRegister = ARMRegisters::lr; + + // Integer arithmetic operations: + // + // Operations are typically two operand - operation(source, srcDst) + // For many operations the source may be an TrustedImm32, the srcDst operand + // may often be a memory location (explictly described using an Address + // object). + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.add(dest, dest, src); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + add32(imm, dest, dest); + } + + void add32(AbsoluteAddress src, RegisterID dest) + { + load32(src.m_ptr, dataTempRegister); + add32(dataTempRegister, dest); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add(dest, src, armImm); + else { + move(imm, dataTempRegister); + m_assembler.add(dest, src, dataTempRegister); + } + } + + void add32(TrustedImm32 imm, Address address) + { + load32(address, dataTempRegister); + + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add(dataTempRegister, dataTempRegister, armImm); + else { + // Hrrrm, since dataTempRegister holds the data loaded, + // use addressTempRegister to hold the immediate. + move(imm, addressTempRegister); + m_assembler.add(dataTempRegister, dataTempRegister, addressTempRegister); + } + + store32(dataTempRegister, address); + } + + void add32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + add32(dataTempRegister, dest); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + load32(address.m_ptr, dataTempRegister); + + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add(dataTempRegister, dataTempRegister, armImm); + else { + // Hrrrm, since dataTempRegister holds the data loaded, + // use addressTempRegister to hold the immediate. + move(imm, addressTempRegister); + m_assembler.add(dataTempRegister, dataTempRegister, addressTempRegister); + } + + store32(dataTempRegister, address.m_ptr); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), addressTempRegister); + + m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(0)); + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add_S(dataTempRegister, dataTempRegister, armImm); + else { + move(imm, addressTempRegister); + m_assembler.add_S(dataTempRegister, dataTempRegister, addressTempRegister); + move(TrustedImmPtr(address.m_ptr), addressTempRegister); + } + m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(0)); + + m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(4)); + m_assembler.adc(dataTempRegister, dataTempRegister, ARMThumbImmediate::makeEncodedImm(imm.m_value >> 31)); + m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(4)); + } + + void and32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.ARM_and(dest, op1, op2); + } + + void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.ARM_and(dest, src, armImm); + else { + move(imm, dataTempRegister); + m_assembler.ARM_and(dest, src, dataTempRegister); + } + } + + void and32(RegisterID src, RegisterID dest) + { + and32(dest, src, dest); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + and32(imm, dest, dest); + } + + void countLeadingZeros32(RegisterID src, RegisterID dest) + { + m_assembler.clz(dest, src); + } + + void lshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + // Clamp the shift to the range 0..31 + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); + ASSERT(armImm.isValid()); + m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); + + m_assembler.lsl(dest, src, dataTempRegister); + } + + void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.lsl(dest, src, imm.m_value & 0x1f); + } + + void lshift32(RegisterID shiftAmount, RegisterID dest) + { + lshift32(dest, shiftAmount, dest); + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + lshift32(dest, imm, dest); + } + + void mul32(RegisterID src, RegisterID dest) + { + m_assembler.smull(dest, dataTempRegister, dest, src); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(imm, dataTempRegister); + m_assembler.smull(dest, dataTempRegister, src, dataTempRegister); + } + + void neg32(RegisterID srcDest) + { + m_assembler.neg(srcDest, srcDest); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orr(dest, dest, src); + } + + void or32(RegisterID src, AbsoluteAddress dest) + { + move(TrustedImmPtr(dest.m_ptr), addressTempRegister); + load32(addressTempRegister, dataTempRegister); + or32(src, dataTempRegister); + store32(dataTempRegister, addressTempRegister); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + or32(imm, dest, dest); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.orr(dest, op1, op2); + } + + void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.orr(dest, src, armImm); + else { + move(imm, dataTempRegister); + m_assembler.orr(dest, src, dataTempRegister); + } + } + + void rshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + // Clamp the shift to the range 0..31 + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); + ASSERT(armImm.isValid()); + m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); + + m_assembler.asr(dest, src, dataTempRegister); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.asr(dest, src, imm.m_value & 0x1f); + } + + void rshift32(RegisterID shiftAmount, RegisterID dest) + { + rshift32(dest, shiftAmount, dest); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + rshift32(dest, imm, dest); + } + + void urshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest) + { + // Clamp the shift to the range 0..31 + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); + ASSERT(armImm.isValid()); + m_assembler.ARM_and(dataTempRegister, shiftAmount, armImm); + + m_assembler.lsr(dest, src, dataTempRegister); + } + + void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.lsr(dest, src, imm.m_value & 0x1f); + } + + void urshift32(RegisterID shiftAmount, RegisterID dest) + { + urshift32(dest, shiftAmount, dest); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + urshift32(dest, imm, dest); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.sub(dest, dest, src); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.sub(dest, dest, armImm); + else { + move(imm, dataTempRegister); + m_assembler.sub(dest, dest, dataTempRegister); + } + } + + void sub32(TrustedImm32 imm, Address address) + { + load32(address, dataTempRegister); + + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.sub(dataTempRegister, dataTempRegister, armImm); + else { + // Hrrrm, since dataTempRegister holds the data loaded, + // use addressTempRegister to hold the immediate. + move(imm, addressTempRegister); + m_assembler.sub(dataTempRegister, dataTempRegister, addressTempRegister); + } + + store32(dataTempRegister, address); + } + + void sub32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + sub32(dataTempRegister, dest); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + load32(address.m_ptr, dataTempRegister); + + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.sub(dataTempRegister, dataTempRegister, armImm); + else { + // Hrrrm, since dataTempRegister holds the data loaded, + // use addressTempRegister to hold the immediate. + move(imm, addressTempRegister); + m_assembler.sub(dataTempRegister, dataTempRegister, addressTempRegister); + } + + store32(dataTempRegister, address.m_ptr); + } + + void xor32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.eor(dest, op1, op2); + } + + void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (imm.m_value == -1) { + m_assembler.mvn(dest, src); + return; + } + + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.eor(dest, src, armImm); + else { + move(imm, dataTempRegister); + m_assembler.eor(dest, src, dataTempRegister); + } + } + + void xor32(RegisterID src, RegisterID dest) + { + xor32(dest, src, dest); + } + + void xor32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value == -1) + m_assembler.mvn(dest, dest); + else + xor32(imm, dest, dest); + } + + + // Memory access operations: + // + // Loads are of the form load(address, destination) and stores of the form + // store(source, address). The source for a store may be an TrustedImm32. Address + // operand objects to loads and store will be implicitly constructed if a + // register is passed. + +private: + void load32(ArmAddress address, RegisterID dest) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.ldr(dest, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.ldr(dest, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.ldr(dest, address.base, address.u.offset, true, false); + } + } + + void load16(ArmAddress address, RegisterID dest) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.ldrh(dest, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.ldrh(dest, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.ldrh(dest, address.base, address.u.offset, true, false); + } + } + + void load16Signed(ArmAddress address, RegisterID dest) + { + ASSERT(address.type == ArmAddress::HasIndex); + m_assembler.ldrsh(dest, address.base, address.u.index, address.u.scale); + } + + void load8(ArmAddress address, RegisterID dest) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.ldrb(dest, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.ldrb(dest, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.ldrb(dest, address.base, address.u.offset, true, false); + } + } + + void load8Signed(ArmAddress address, RegisterID dest) + { + ASSERT(address.type == ArmAddress::HasIndex); + m_assembler.ldrsb(dest, address.base, address.u.index, address.u.scale); + } + +protected: + void store32(RegisterID src, ArmAddress address) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.str(src, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.str(src, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.str(src, address.base, address.u.offset, true, false); + } + } + +private: + void store8(RegisterID src, ArmAddress address) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.strb(src, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.strb(src, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.strb(src, address.base, address.u.offset, true, false); + } + } + + void store16(RegisterID src, ArmAddress address) + { + if (address.type == ArmAddress::HasIndex) + m_assembler.strh(src, address.base, address.u.index, address.u.scale); + else if (address.u.offset >= 0) { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset); + ASSERT(armImm.isValid()); + m_assembler.strh(src, address.base, armImm); + } else { + ASSERT(address.u.offset >= -255); + m_assembler.strh(src, address.base, address.u.offset, true, false); + } + } + +public: + void load32(ImplicitAddress address, RegisterID dest) + { + load32(setupArmAddress(address), dest); + } + + void load32(BaseIndex address, RegisterID dest) + { + load32(setupArmAddress(address), dest); + } + + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + load32(setupArmAddress(address), dest); + } + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + load16(setupArmAddress(address), dest); + } + + void load32(const void* address, RegisterID dest) + { + move(TrustedImmPtr(address), addressTempRegister); + m_assembler.ldr(dest, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result(this); + ASSERT(address.offset >= 0 && address.offset <= 255); + m_assembler.ldrWide8BitImmediate(dest, address.base, address.offset); + return result; + } + + void load8(ImplicitAddress address, RegisterID dest) + { + load8(setupArmAddress(address), dest); + } + + void load8Signed(ImplicitAddress, RegisterID) + { + UNREACHABLE_FOR_PLATFORM(); + } + + void load8(BaseIndex address, RegisterID dest) + { + load8(setupArmAddress(address), dest); + } + + void load8Signed(BaseIndex address, RegisterID dest) + { + load8Signed(setupArmAddress(address), dest); + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabel32 label = moveWithPatch(TrustedImm32(address.offset), dataTempRegister); + load32(ArmAddress(address.base, dataTempRegister), dest); + return label; + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + + RegisterID base = address.base; + + DataLabelCompact label(this); + ASSERT(isCompactPtrAlignedAddressOffset(address.offset)); + + m_assembler.ldr(dest, base, address.offset, true, false); + return label; + } + + void load16(BaseIndex address, RegisterID dest) + { + m_assembler.ldrh(dest, makeBaseIndexBase(address), address.index, address.scale); + } + + void load16Signed(BaseIndex address, RegisterID dest) + { + load16Signed(setupArmAddress(address), dest); + } + + void load16(ImplicitAddress address, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.offset); + if (armImm.isValid()) + m_assembler.ldrh(dest, address.base, armImm); + else { + move(TrustedImm32(address.offset), dataTempRegister); + m_assembler.ldrh(dest, address.base, dataTempRegister); + } + } + + void load16Signed(ImplicitAddress, RegisterID) + { + UNREACHABLE_FOR_PLATFORM(); + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + DataLabel32 label = moveWithPatch(TrustedImm32(address.offset), dataTempRegister); + store32(src, ArmAddress(address.base, dataTempRegister)); + return label; + } + + void store32(RegisterID src, ImplicitAddress address) + { + store32(src, setupArmAddress(address)); + } + + void store32(RegisterID src, BaseIndex address) + { + store32(src, setupArmAddress(address)); + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + move(imm, dataTempRegister); + store32(dataTempRegister, setupArmAddress(address)); + } + + void store32(TrustedImm32 imm, BaseIndex address) + { + move(imm, dataTempRegister); + store32(dataTempRegister, setupArmAddress(address)); + } + + void store32(RegisterID src, const void* address) + { + move(TrustedImmPtr(address), addressTempRegister); + m_assembler.str(src, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); + } + + void store32(TrustedImm32 imm, const void* address) + { + move(imm, dataTempRegister); + store32(dataTempRegister, address); + } + + void store8(RegisterID src, BaseIndex address) + { + store8(src, setupArmAddress(address)); + } + + void store8(RegisterID src, void* address) + { + move(TrustedImmPtr(address), addressTempRegister); + store8(src, ArmAddress(addressTempRegister, 0)); + } + + void store8(TrustedImm32 imm, void* address) + { + move(imm, dataTempRegister); + store8(dataTempRegister, address); + } + + void store16(RegisterID src, BaseIndex address) + { + store16(src, setupArmAddress(address)); + } + + // Possibly clobbers src, but not on this architecture. + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + m_assembler.vmov(dest1, dest2, src); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + UNUSED_PARAM(scratch); + m_assembler.vmov(dest, src1, src2); + } + +#if ENABLE(JIT_CONSTANT_BLINDING) + static bool shouldBlindForSpecificArch(uint32_t value) + { + ARMThumbImmediate immediate = ARMThumbImmediate::makeEncodedImm(value); + + // Couldn't be encoded as an immediate, so assume it's untrusted. + if (!immediate.isValid()) + return true; + + // If we can encode the immediate, we have less than 16 attacker + // controlled bits. + if (immediate.isEncodedImm()) + return false; + + // Don't let any more than 12 bits of an instruction word + // be controlled by an attacker. + return !immediate.isUInt12(); + } +#endif + + // Floating-point operations: + + static bool supportsFloatingPoint() { return true; } + static bool supportsFloatingPointTruncate() { return true; } + static bool supportsFloatingPointSqrt() { return true; } + static bool supportsFloatingPointAbs() { return true; } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { + RegisterID base = address.base; + int32_t offset = address.offset; + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { + add32(TrustedImm32(offset), base, addressTempRegister); + base = addressTempRegister; + offset = 0; + } + + m_assembler.vldr(dest, base, offset); + } + + void loadFloat(ImplicitAddress address, FPRegisterID dest) + { + RegisterID base = address.base; + int32_t offset = address.offset; + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { + add32(TrustedImm32(offset), base, addressTempRegister); + base = addressTempRegister; + offset = 0; + } + + m_assembler.flds(ARMRegisters::asSingle(dest), base, offset); + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { + move(address.index, addressTempRegister); + lshift32(TrustedImm32(address.scale), addressTempRegister); + add32(address.base, addressTempRegister); + loadDouble(Address(addressTempRegister, address.offset), dest); + } + + void loadFloat(BaseIndex address, FPRegisterID dest) + { + move(address.index, addressTempRegister); + lshift32(TrustedImm32(address.scale), addressTempRegister); + add32(address.base, addressTempRegister); + loadFloat(Address(addressTempRegister, address.offset), dest); + } + + void moveDouble(FPRegisterID src, FPRegisterID dest) + { + if (src != dest) + m_assembler.vmov(dest, src); + } + + void loadDouble(const void* address, FPRegisterID dest) + { + move(TrustedImmPtr(address), addressTempRegister); + m_assembler.vldr(dest, addressTempRegister, 0); + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { + RegisterID base = address.base; + int32_t offset = address.offset; + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { + add32(TrustedImm32(offset), base, addressTempRegister); + base = addressTempRegister; + offset = 0; + } + + m_assembler.vstr(src, base, offset); + } + + void storeFloat(FPRegisterID src, ImplicitAddress address) + { + RegisterID base = address.base; + int32_t offset = address.offset; + + // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. + if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) { + add32(TrustedImm32(offset), base, addressTempRegister); + base = addressTempRegister; + offset = 0; + } + + m_assembler.fsts(ARMRegisters::asSingle(src), base, offset); + } + + void storeDouble(FPRegisterID src, const void* address) + { + move(TrustedImmPtr(address), addressTempRegister); + storeDouble(src, addressTempRegister); + } + + void storeDouble(FPRegisterID src, BaseIndex address) + { + move(address.index, addressTempRegister); + lshift32(TrustedImm32(address.scale), addressTempRegister); + add32(address.base, addressTempRegister); + storeDouble(src, Address(addressTempRegister, address.offset)); + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + move(address.index, addressTempRegister); + lshift32(TrustedImm32(address.scale), addressTempRegister); + add32(address.base, addressTempRegister); + storeFloat(src, Address(addressTempRegister, address.offset)); + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vadd(dest, dest, src); + } + + void addDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + addDouble(fpTempRegister, dest); + } + + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vadd(dest, op1, op2); + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + loadDouble(address.m_ptr, fpTempRegister); + m_assembler.vadd(dest, dest, fpTempRegister); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vdiv(dest, dest, src); + } + + void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vdiv(dest, op1, op2); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vsub(dest, dest, src); + } + + void subDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + subDouble(fpTempRegister, dest); + } + + void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vsub(dest, op1, op2); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vmul(dest, dest, src); + } + + void mulDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + mulDouble(fpTempRegister, dest); + } + + void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.vmul(dest, op1, op2); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vsqrt(dest, src); + } + + void absDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vabs(dest, src); + } + + void negateDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.vneg(dest, src); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.vmov(fpTempRegister, src, src); + m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); + } + + void convertInt32ToDouble(Address address, FPRegisterID dest) + { + // Fixme: load directly into the fpr! + load32(address, dataTempRegister); + m_assembler.vmov(fpTempRegister, dataTempRegister, dataTempRegister); + m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); + } + + void convertInt32ToDouble(AbsoluteAddress address, FPRegisterID dest) + { + // Fixme: load directly into the fpr! + load32(address.m_ptr, dataTempRegister); + m_assembler.vmov(fpTempRegister, dataTempRegister, dataTempRegister); + m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); + } + + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.vcvtds(dst, ARMRegisters::asSingle(src)); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.vcvtsd(ARMRegisters::asSingle(dst), src); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + m_assembler.vcmp(left, right); + m_assembler.vmrs(); + + if (cond == DoubleNotEqual) { + // ConditionNE jumps if NotEqual *or* unordered - force the unordered cases not to jump. + Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); + Jump result = makeBranch(ARMv7Assembler::ConditionNE); + unordered.link(this); + return result; + } + if (cond == DoubleEqualOrUnordered) { + Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); + Jump notEqual = makeBranch(ARMv7Assembler::ConditionNE); + unordered.link(this); + // We get here if either unordered or equal. + Jump result = jump(); + notEqual.link(this); + return result; + } + return makeBranch(cond); + } + + enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + // Convert into dest. + m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + + // Calculate 2x dest. If the value potentially underflowed, it will have + // clamped to 0x80000000, so 2x dest is zero in this case. In the case of + // overflow the result will be equal to -2. + Jump underflow = branchAdd32(Zero, dest, dest, dataTempRegister); + Jump noOverflow = branch32(NotEqual, dataTempRegister, TrustedImm32(-2)); + + // For BranchIfTruncateSuccessful, we branch if 'noOverflow' jumps. + underflow.link(this); + if (branchType == BranchIfTruncateSuccessful) + return noOverflow; + + // We'll reach the current point in the code on failure, so plant a + // jump here & link the success case. + Jump failure = jump(); + noOverflow.link(this); + return failure; + } + + Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + + Jump overflow = branch32(Equal, dest, TrustedImm32(0x7fffffff)); + Jump success = branch32(GreaterThanOrEqual, dest, TrustedImm32(0)); + overflow.link(this); + + if (branchType == BranchIfTruncateSuccessful) + return success; + + Jump failure = jump(); + success.link(this); + return failure; + } + + // Result is undefined if the value is outside of the integer range. + void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + } + + void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) + { + m_assembler.vcvt_floatingPointToUnsigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + } + + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID) + { + m_assembler.vcvt_floatingPointToSigned(fpTempRegisterAsSingle(), src); + m_assembler.vmov(dest, fpTempRegisterAsSingle()); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + m_assembler.vcvt_signedToFloatingPoint(fpTempRegister, fpTempRegisterAsSingle()); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, fpTempRegister)); + + // If the result is zero, it might have been -0.0, and the double comparison won't catch this! + failureCases.append(branchTest32(Zero, dest)); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID) + { + m_assembler.vcmpz(reg); + m_assembler.vmrs(); + Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); + Jump result = makeBranch(ARMv7Assembler::ConditionNE); + unordered.link(this); + return result; + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID) + { + m_assembler.vcmpz(reg); + m_assembler.vmrs(); + Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); + Jump notEqual = makeBranch(ARMv7Assembler::ConditionNE); + unordered.link(this); + // We get here if either unordered or equal. + Jump result = jump(); + notEqual.link(this); + return result; + } + + // Stack manipulation operations: + // + // The ABI is assumed to provide a stack abstraction to memory, + // containing machine word sized units of data. Push and pop + // operations add and remove a single register sized unit of data + // to or from the stack. Peek and poke operations read or write + // values on the stack, without moving the current stack position. + + void pop(RegisterID dest) + { + // store postindexed with writeback + m_assembler.ldr(dest, ARMRegisters::sp, sizeof(void*), false, true); + } + + void push(RegisterID src) + { + // store preindexed with writeback + m_assembler.str(src, ARMRegisters::sp, -sizeof(void*), true, true); + } + + void push(Address address) + { + load32(address, dataTempRegister); + push(dataTempRegister); + } + + void push(TrustedImm32 imm) + { + move(imm, dataTempRegister); + push(dataTempRegister); + } + + // Register move operations: + // + // Move values in registers. + + void move(TrustedImm32 imm, RegisterID dest) + { + uint32_t value = imm.m_value; + + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(value); + + if (armImm.isValid()) + m_assembler.mov(dest, armImm); + else if ((armImm = ARMThumbImmediate::makeEncodedImm(~value)).isValid()) + m_assembler.mvn(dest, armImm); + else { + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(value)); + if (value & 0xffff0000) + m_assembler.movt(dest, ARMThumbImmediate::makeUInt16(value >> 16)); + } + } + + void move(RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.mov(dest, src); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + move(TrustedImm32(imm), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + move(reg1, dataTempRegister); + move(reg2, reg1); + move(dataTempRegister, reg2); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + move(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + move(src, dest); + } + + // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. + static RelationalCondition invert(RelationalCondition cond) + { + return static_cast(cond ^ 1); + } + + void nop() + { + m_assembler.nop(); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + ARMv7Assembler::replaceWithJump(instructionStart.dataLocation(), destination.dataLocation()); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return ARMv7Assembler::maxJumpReplacementSize(); + } + + // Forwards / external control flow operations: + // + // This set of jump and conditional branch operations return a Jump + // object which may linked at a later point, allow forwards jump, + // or jumps that will require external linkage (after the code has been + // relocated). + // + // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge + // respecitvely, for unsigned comparisons the names b, a, be, and ae are + // used (representing the names 'below' and 'above'). + // + // Operands to the comparision are provided in the expected order, e.g. + // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when + // treated as a signed 32bit value, is less than or equal to 5. + // + // jz and jnz test whether the first operand is equal to zero, and take + // an optional second operand of a mask under which to perform the test. +private: + + // Should we be using TEQ for equal/not-equal? + void compare32(RegisterID left, TrustedImm32 right) + { + int32_t imm = right.m_value; + if (!imm) + m_assembler.tst(left, left); + else { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm); + if (armImm.isValid()) + m_assembler.cmp(left, armImm); + else if ((armImm = ARMThumbImmediate::makeEncodedImm(-imm)).isValid()) + m_assembler.cmn(left, armImm); + else { + move(TrustedImm32(imm), dataTempRegister); + m_assembler.cmp(left, dataTempRegister); + } + } + } + + void test32(RegisterID reg, TrustedImm32 mask) + { + int32_t imm = mask.m_value; + + if (imm == -1) + m_assembler.tst(reg, reg); + else { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm); + if (armImm.isValid()) + m_assembler.tst(reg, armImm); + else { + move(mask, dataTempRegister); + m_assembler.tst(reg, dataTempRegister); + } + } + } + +public: + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) + { + m_assembler.cmp(left, right); + return Jump(makeBranch(cond)); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + compare32(left, right); + return Jump(makeBranch(cond)); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + load32(right, dataTempRegister); + return branch32(cond, left, dataTempRegister); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + load32(left, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load32(left, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load32(left, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load32WithUnalignedHalfWords(left, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + load32(left.m_ptr, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load32(left.m_ptr, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branch8(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + compare32(left, right); + return Jump(makeBranch(cond)); + } + + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + ASSERT(!(0xffffff00 & right.m_value)); + // use addressTempRegister incase the branch8 we call uses dataTempRegister. :-/ + load8(left, addressTempRegister); + return branch8(cond, addressTempRegister, right); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(0xffffff00 & right.m_value)); + // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/ + load8(left, addressTempRegister); + return branch32(cond, addressTempRegister, right); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + m_assembler.tst(reg, mask); + return Jump(makeBranch(cond)); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + test32(reg, mask); + return Jump(makeBranch(cond)); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + // use addressTempRegister incase the branchTest32 we call uses dataTempRegister. :-/ + load32(address, addressTempRegister); + return branchTest32(cond, addressTempRegister, mask); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + // use addressTempRegister incase the branchTest32 we call uses dataTempRegister. :-/ + load32(address, addressTempRegister); + return branchTest32(cond, addressTempRegister, mask); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + // use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/ + load8(address, addressTempRegister); + return branchTest32(cond, addressTempRegister, mask); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + // use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/ + move(TrustedImmPtr(address.m_ptr), addressTempRegister); + load8(Address(addressTempRegister), addressTempRegister); + return branchTest32(cond, addressTempRegister, mask); + } + + void jump(RegisterID target) + { + m_assembler.bx(target); + } + + // Address is a memory location containing the address to jump to + void jump(Address address) + { + load32(address, dataTempRegister); + m_assembler.bx(dataTempRegister); + } + + void jump(AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), dataTempRegister); + load32(Address(dataTempRegister), dataTempRegister); + m_assembler.bx(dataTempRegister); + } + + + // Arithmetic control flow operations: + // + // This set of conditional branch operations branch based + // on the result of an arithmetic operation. The operation + // is performed as normal, storing the result. + // + // * jz operations branch if the result is zero. + // * jo operations branch if the (signed) arithmetic + // operation caused an overflow to occur. + + Jump branchAdd32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.add_S(dest, op1, op2); + return Jump(makeBranch(cond)); + } + + Jump branchAdd32(ResultCondition cond, RegisterID op1, TrustedImm32 imm, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add_S(dest, op1, armImm); + else { + move(imm, dataTempRegister); + m_assembler.add_S(dest, op1, dataTempRegister); + } + return Jump(makeBranch(cond)); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchAdd32(cond, dest, src, dest); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchAdd32(cond, dest, imm, dest); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) + { + // Move the high bits of the address into addressTempRegister, + // and load the value into dataTempRegister. + move(TrustedImmPtr(dest.m_ptr), addressTempRegister); + m_assembler.ldr(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); + + // Do the add. + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.add_S(dataTempRegister, dataTempRegister, armImm); + else { + // If the operand does not fit into an immediate then load it temporarily + // into addressTempRegister; since we're overwriting addressTempRegister + // we'll need to reload it with the high bits of the address afterwards. + move(imm, addressTempRegister); + m_assembler.add_S(dataTempRegister, dataTempRegister, addressTempRegister); + move(TrustedImmPtr(dest.m_ptr), addressTempRegister); + } + + // Store the result. + m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); + + return Jump(makeBranch(cond)); + } + + Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + m_assembler.smull(dest, dataTempRegister, src1, src2); + + if (cond == Overflow) { + m_assembler.asr(addressTempRegister, dest, 31); + return branch32(NotEqual, addressTempRegister, dataTempRegister); + } + + return branchTest32(cond, dest); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchMul32(cond, src, dest, dest); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(imm, dataTempRegister); + return branchMul32(cond, dataTempRegister, src, dest); + } + + Jump branchNeg32(ResultCondition cond, RegisterID srcDest) + { + ARMThumbImmediate zero = ARMThumbImmediate::makeUInt12(0); + m_assembler.sub_S(srcDest, zero, srcDest); + return Jump(makeBranch(cond)); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + m_assembler.orr_S(dest, dest, src); + return Jump(makeBranch(cond)); + } + + Jump branchSub32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.sub_S(dest, op1, op2); + return Jump(makeBranch(cond)); + } + + Jump branchSub32(ResultCondition cond, RegisterID op1, TrustedImm32 imm, RegisterID dest) + { + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value); + if (armImm.isValid()) + m_assembler.sub_S(dest, op1, armImm); + else { + move(imm, dataTempRegister); + m_assembler.sub_S(dest, op1, dataTempRegister); + } + return Jump(makeBranch(cond)); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + return branchSub32(cond, dest, src, dest); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + return branchSub32(cond, dest, imm, dest); + } + + void relativeTableJump(RegisterID index, int scale) + { + ASSERT(scale >= 0 && scale <= 31); + + // dataTempRegister will point after the jump if index register contains zero + move(ARMRegisters::pc, dataTempRegister); + m_assembler.add(dataTempRegister, dataTempRegister, ARMThumbImmediate::makeEncodedImm(9)); + + ShiftTypeAndAmount shift(SRType_LSL, scale); + m_assembler.add(dataTempRegister, dataTempRegister, index, shift); + jump(dataTempRegister); + } + + // Miscellaneous operations: + + void breakpoint(uint8_t imm = 0) + { + m_assembler.bkpt(imm); + } + + ALWAYS_INLINE Call nearCall() + { + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Call(m_assembler.blx(dataTempRegister), Call::LinkableNear); + } + + ALWAYS_INLINE Call call() + { + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Call(m_assembler.blx(dataTempRegister), Call::Linkable); + } + + ALWAYS_INLINE Call call(RegisterID target) + { + return Call(m_assembler.blx(target), Call::None); + } + + ALWAYS_INLINE Call call(Address address) + { + load32(address, dataTempRegister); + return Call(m_assembler.blx(dataTempRegister), Call::None); + } + + ALWAYS_INLINE void ret() + { + m_assembler.bx(linkRegister); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmp(left, right); + m_assembler.it(armV7Condition(cond), false); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); + } + + void compare32(RelationalCondition cond, Address left, RegisterID right, RegisterID dest) + { + load32(left, dataTempRegister); + compare32(cond, dataTempRegister, right, dest); + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + load8(left, addressTempRegister); + compare32(cond, addressTempRegister, right, dest); + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + compare32(left, right); + m_assembler.it(armV7Condition(cond), false); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); + } + + // FIXME: + // The mask should be optional... paerhaps the argument order should be + // dest-src, operations always have a dest? ... possibly not true, considering + // asm ops like test, or pseudo ops like pop(). + void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + load32(address, dataTempRegister); + test32(dataTempRegister, mask); + m_assembler.it(armV7Condition(cond), false); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); + } + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + load8(address, dataTempRegister); + test32(dataTempRegister, mask); + m_assembler.it(armV7Condition(cond), false); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1)); + m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0)); + } + + ALWAYS_INLINE DataLabel32 moveWithPatch(TrustedImm32 imm, RegisterID dst) + { + padBeforePatch(); + moveFixedWidthEncoding(imm, dst); + return DataLabel32(this); + } + + ALWAYS_INLINE DataLabelPtr moveWithPatch(TrustedImmPtr imm, RegisterID dst) + { + padBeforePatch(); + moveFixedWidthEncoding(TrustedImm32(imm), dst); + return DataLabelPtr(this); + } + + ALWAYS_INLINE Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + dataLabel = moveWithPatch(initialRightValue, dataTempRegister); + return branch32(cond, left, dataTempRegister); + } + + ALWAYS_INLINE Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + load32(left, addressTempRegister); + dataLabel = moveWithPatch(initialRightValue, dataTempRegister); + return branch32(cond, addressTempRegister, dataTempRegister); + } + + PatchableJump patchableBranchPtr(RelationalCondition cond, Address left, TrustedImmPtr right = TrustedImmPtr(0)) + { + m_makeJumpPatchable = true; + Jump result = branch32(cond, left, TrustedImm32(right)); + m_makeJumpPatchable = false; + return PatchableJump(result); + } + + PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + m_makeJumpPatchable = true; + Jump result = branchTest32(cond, reg, mask); + m_makeJumpPatchable = false; + return PatchableJump(result); + } + + PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm) + { + m_makeJumpPatchable = true; + Jump result = branch32(cond, reg, imm); + m_makeJumpPatchable = false; + return PatchableJump(result); + } + + PatchableJump patchableBranchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + m_makeJumpPatchable = true; + Jump result = branchPtrWithPatch(cond, left, dataLabel, initialRightValue); + m_makeJumpPatchable = false; + return PatchableJump(result); + } + + PatchableJump patchableJump() + { + padBeforePatch(); + m_makeJumpPatchable = true; + Jump result = jump(); + m_makeJumpPatchable = false; + return PatchableJump(result); + } + + ALWAYS_INLINE DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + DataLabelPtr label = moveWithPatch(initialValue, dataTempRegister); + store32(dataTempRegister, address); + return label; + } + ALWAYS_INLINE DataLabelPtr storePtrWithPatch(ImplicitAddress address) { return storePtrWithPatch(TrustedImmPtr(0), address); } + + + ALWAYS_INLINE Call tailRecursiveCall() + { + // Like a normal call, but don't link. + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Call(m_assembler.bx(dataTempRegister), Call::Linkable); + } + + ALWAYS_INLINE Call makeTailRecursiveCall(Jump oldJump) + { + oldJump.link(this); + return tailRecursiveCall(); + } + + + int executableOffsetFor(int location) + { + return m_assembler.executableOffsetFor(location); + } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(reinterpret_cast(ARMv7Assembler::readCallTarget(call.dataLocation()))); + } + + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + const unsigned twoWordOpSize = 4; + return label.labelAtOffset(-twoWordOpSize * 2); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) + { + ARMv7Assembler::revertJumpTo_movT3(instructionStart.dataLocation(), dataTempRegister, ARMThumbImmediate::makeUInt16(reinterpret_cast(initialValue) & 0xffff)); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel, Address, void*) + { + UNREACHABLE_FOR_PLATFORM(); + } + +protected: + ALWAYS_INLINE Jump jump() + { + m_assembler.label(); // Force nop-padding if we're in the middle of a watchpoint. + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Jump(m_assembler.bx(dataTempRegister), m_makeJumpPatchable ? ARMv7Assembler::JumpNoConditionFixedSize : ARMv7Assembler::JumpNoCondition); + } + + ALWAYS_INLINE Jump makeBranch(ARMv7Assembler::Condition cond) + { + m_assembler.label(); // Force nop-padding if we're in the middle of a watchpoint. + m_assembler.it(cond, true, true); + moveFixedWidthEncoding(TrustedImm32(0), dataTempRegister); + return Jump(m_assembler.bx(dataTempRegister), m_makeJumpPatchable ? ARMv7Assembler::JumpConditionFixedSize : ARMv7Assembler::JumpCondition, cond); + } + ALWAYS_INLINE Jump makeBranch(RelationalCondition cond) { return makeBranch(armV7Condition(cond)); } + ALWAYS_INLINE Jump makeBranch(ResultCondition cond) { return makeBranch(armV7Condition(cond)); } + ALWAYS_INLINE Jump makeBranch(DoubleCondition cond) { return makeBranch(armV7Condition(cond)); } + + ArmAddress setupArmAddress(BaseIndex address) + { + if (address.offset) { + ARMThumbImmediate imm = ARMThumbImmediate::makeUInt12OrEncodedImm(address.offset); + if (imm.isValid()) + m_assembler.add(addressTempRegister, address.base, imm); + else { + move(TrustedImm32(address.offset), addressTempRegister); + m_assembler.add(addressTempRegister, addressTempRegister, address.base); + } + + return ArmAddress(addressTempRegister, address.index, address.scale); + } else + return ArmAddress(address.base, address.index, address.scale); + } + + ArmAddress setupArmAddress(Address address) + { + if ((address.offset >= -0xff) && (address.offset <= 0xfff)) + return ArmAddress(address.base, address.offset); + + move(TrustedImm32(address.offset), addressTempRegister); + return ArmAddress(address.base, addressTempRegister); + } + + ArmAddress setupArmAddress(ImplicitAddress address) + { + if ((address.offset >= -0xff) && (address.offset <= 0xfff)) + return ArmAddress(address.base, address.offset); + + move(TrustedImm32(address.offset), addressTempRegister); + return ArmAddress(address.base, addressTempRegister); + } + + RegisterID makeBaseIndexBase(BaseIndex address) + { + if (!address.offset) + return address.base; + + ARMThumbImmediate imm = ARMThumbImmediate::makeUInt12OrEncodedImm(address.offset); + if (imm.isValid()) + m_assembler.add(addressTempRegister, address.base, imm); + else { + move(TrustedImm32(address.offset), addressTempRegister); + m_assembler.add(addressTempRegister, addressTempRegister, address.base); + } + + return addressTempRegister; + } + + void moveFixedWidthEncoding(TrustedImm32 imm, RegisterID dst) + { + uint32_t value = imm.m_value; + m_assembler.movT3(dst, ARMThumbImmediate::makeUInt16(value & 0xffff)); + m_assembler.movt(dst, ARMThumbImmediate::makeUInt16(value >> 16)); + } + + ARMv7Assembler::Condition armV7Condition(RelationalCondition cond) + { + return static_cast(cond); + } + + ARMv7Assembler::Condition armV7Condition(ResultCondition cond) + { + return static_cast(cond); + } + + ARMv7Assembler::Condition armV7Condition(DoubleCondition cond) + { + return static_cast(cond); + } + +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + ARMv7Assembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + bool m_makeJumpPatchable; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerARMv7_h diff --git a/src/3rdparty/masm/assembler/MacroAssemblerCodeRef.h b/src/3rdparty/masm/assembler/MacroAssemblerCodeRef.h new file mode 100644 index 0000000000..c2af24060a --- /dev/null +++ b/src/3rdparty/masm/assembler/MacroAssemblerCodeRef.h @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2009, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerCodeRef_h +#define MacroAssemblerCodeRef_h + +#include "Disassembler.h" +#include "ExecutableAllocator.h" +#include "LLIntData.h" +#include +#include +#include +#include + +// ASSERT_VALID_CODE_POINTER checks that ptr is a non-null pointer, and that it is a valid +// instruction address on the platform (for example, check any alignment requirements). +#if CPU(ARM_THUMB2) +// ARM/thumb instructions must be 16-bit aligned, but all code pointers to be loaded +// into the processor are decorated with the bottom bit set, indicating that this is +// thumb code (as oposed to 32-bit traditional ARM). The first test checks for both +// decorated and undectorated null, and the second test ensures that the pointer is +// decorated. +#define ASSERT_VALID_CODE_POINTER(ptr) \ + ASSERT(reinterpret_cast(ptr) & ~1); \ + ASSERT(reinterpret_cast(ptr) & 1) +#define ASSERT_VALID_CODE_OFFSET(offset) \ + ASSERT(!(offset & 1)) // Must be multiple of 2. +#else +#define ASSERT_VALID_CODE_POINTER(ptr) \ + ASSERT(ptr) +#define ASSERT_VALID_CODE_OFFSET(offset) // Anything goes! +#endif + +#if CPU(X86) && OS(WINDOWS) +#define CALLING_CONVENTION_IS_STDCALL 1 +#ifndef CDECL +#if COMPILER(MSVC) +#define CDECL __cdecl +#else +#define CDECL __attribute__ ((__cdecl)) +#endif // COMPILER(MSVC) +#endif // CDECL +#else +#define CALLING_CONVENTION_IS_STDCALL 0 +#endif + +#if CPU(X86) +#define HAS_FASTCALL_CALLING_CONVENTION 1 +#ifndef FASTCALL +#if COMPILER(MSVC) +#define FASTCALL __fastcall +#else +#define FASTCALL __attribute__ ((fastcall)) +#endif // COMPILER(MSVC) +#endif // FASTCALL +#else +#define HAS_FASTCALL_CALLING_CONVENTION 0 +#endif // CPU(X86) + +namespace JSC { + +// FunctionPtr: +// +// FunctionPtr should be used to wrap pointers to C/C++ functions in JSC +// (particularly, the stub functions). +class FunctionPtr { +public: + FunctionPtr() + : m_value(0) + { + } + + template + FunctionPtr(returnType(*value)()) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1, argType2)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1, argType2, argType3)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1, argType2, argType3, argType4)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType(*value)(argType1, argType2, argType3, argType4, argType5)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + +// MSVC doesn't seem to treat functions with different calling conventions as +// different types; these methods already defined for fastcall, below. +#if CALLING_CONVENTION_IS_STDCALL && !OS(WINDOWS) + + template + FunctionPtr(returnType (CDECL *value)()) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (CDECL *value)(argType1)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (CDECL *value)(argType1, argType2)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (CDECL *value)(argType1, argType2, argType3)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (CDECL *value)(argType1, argType2, argType3, argType4)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } +#endif + +#if HAS_FASTCALL_CALLING_CONVENTION + + template + FunctionPtr(returnType (FASTCALL *value)()) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (FASTCALL *value)(argType1)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (FASTCALL *value)(argType1, argType2)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (FASTCALL *value)(argType1, argType2, argType3)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + template + FunctionPtr(returnType (FASTCALL *value)(argType1, argType2, argType3, argType4)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } +#endif + + template + explicit FunctionPtr(FunctionType* value) + // Using a C-ctyle cast here to avoid compiler error on RVTC: + // Error: #694: reinterpret_cast cannot cast away const or other type qualifiers + // (I guess on RVTC function pointers have a different constness to GCC/MSVC?) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + void* value() const { return m_value; } + void* executableAddress() const { return m_value; } + + +private: + void* m_value; +}; + +// ReturnAddressPtr: +// +// ReturnAddressPtr should be used to wrap return addresses generated by processor +// 'call' instructions exectued in JIT code. We use return addresses to look up +// exception and optimization information, and to repatch the call instruction +// that is the source of the return address. +class ReturnAddressPtr { +public: + ReturnAddressPtr() + : m_value(0) + { + } + + explicit ReturnAddressPtr(void* value) + : m_value(value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + explicit ReturnAddressPtr(FunctionPtr function) + : m_value(function.value()) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + void* value() const { return m_value; } + +private: + void* m_value; +}; + +// MacroAssemblerCodePtr: +// +// MacroAssemblerCodePtr should be used to wrap pointers to JIT generated code. +class MacroAssemblerCodePtr { +public: + MacroAssemblerCodePtr() + : m_value(0) + { + } + + explicit MacroAssemblerCodePtr(void* value) +#if CPU(ARM_THUMB2) + // Decorate the pointer as a thumb code pointer. + : m_value(reinterpret_cast(value) + 1) +#else + : m_value(value) +#endif + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + static MacroAssemblerCodePtr createFromExecutableAddress(void* value) + { + ASSERT_VALID_CODE_POINTER(value); + MacroAssemblerCodePtr result; + result.m_value = value; + return result; + } + +#if ENABLE(LLINT) + static MacroAssemblerCodePtr createLLIntCodePtr(LLIntCode codeId) + { + return createFromExecutableAddress(LLInt::getCodePtr(codeId)); + } +#endif + + explicit MacroAssemblerCodePtr(ReturnAddressPtr ra) + : m_value(ra.value()) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + + void* executableAddress() const { return m_value; } +#if CPU(ARM_THUMB2) + // To use this pointer as a data address remove the decoration. + void* dataLocation() const { ASSERT_VALID_CODE_POINTER(m_value); return reinterpret_cast(m_value) - 1; } +#else + void* dataLocation() const { ASSERT_VALID_CODE_POINTER(m_value); return m_value; } +#endif + + bool operator!() const + { + return !m_value; + } + +private: + void* m_value; +}; + +// MacroAssemblerCodeRef: +// +// A reference to a section of JIT generated code. A CodeRef consists of a +// pointer to the code, and a ref pointer to the pool from within which it +// was allocated. +class MacroAssemblerCodeRef { +private: + // This is private because it's dangerous enough that we want uses of it + // to be easy to find - hence the static create method below. + explicit MacroAssemblerCodeRef(MacroAssemblerCodePtr codePtr) + : m_codePtr(codePtr) + { + ASSERT(m_codePtr); + } + +public: + MacroAssemblerCodeRef() + { + } + + MacroAssemblerCodeRef(PassRefPtr executableMemory) + : m_codePtr(executableMemory->start()) + , m_executableMemory(executableMemory) + { + ASSERT(m_executableMemory->isManaged()); + ASSERT(m_executableMemory->start()); + ASSERT(m_codePtr); + } + + // Use this only when you know that the codePtr refers to code that is + // already being kept alive through some other means. Typically this means + // that codePtr is immortal. + static MacroAssemblerCodeRef createSelfManagedCodeRef(MacroAssemblerCodePtr codePtr) + { + return MacroAssemblerCodeRef(codePtr); + } + +#if ENABLE(LLINT) + // Helper for creating self-managed code refs from LLInt. + static MacroAssemblerCodeRef createLLIntCodeRef(LLIntCode codeId) + { + return createSelfManagedCodeRef(MacroAssemblerCodePtr::createFromExecutableAddress(LLInt::getCodePtr(codeId))); + } +#endif + + ExecutableMemoryHandle* executableMemory() const + { + return m_executableMemory.get(); + } + + MacroAssemblerCodePtr code() const + { + return m_codePtr; + } + + size_t size() const + { + if (!m_executableMemory) + return 0; + return m_executableMemory->sizeInBytes(); + } + + bool tryToDisassemble(const char* prefix) const + { + return JSC::tryToDisassemble(m_codePtr, size(), prefix, WTF::dataFile()); + } + + bool operator!() const { return !m_codePtr; } + +private: + MacroAssemblerCodePtr m_codePtr; + RefPtr m_executableMemory; +}; + +} // namespace JSC + +#endif // MacroAssemblerCodeRef_h diff --git a/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h b/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h new file mode 100644 index 0000000000..3ab2553001 --- /dev/null +++ b/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h @@ -0,0 +1,2316 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY MIPS TECHNOLOGIES, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MIPS TECHNOLOGIES, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerMIPS_h +#define MacroAssemblerMIPS_h + +#if ENABLE(ASSEMBLER) && CPU(MIPS) + +#include "AbstractMacroAssembler.h" +#include "MIPSAssembler.h" + +namespace JSC { + +class MacroAssemblerMIPS : public AbstractMacroAssembler { +public: + typedef MIPSRegisters::FPRegisterID FPRegisterID; + + MacroAssemblerMIPS() + : m_fixedWidth(false) + { + } + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return value >= -2147483647 - 1 && value <= 2147483647; + } + + static const Scale ScalePtr = TimesFour; + + // For storing immediate number + static const RegisterID immTempRegister = MIPSRegisters::t0; + // For storing data loaded from the memory + static const RegisterID dataTempRegister = MIPSRegisters::t1; + // For storing address base + static const RegisterID addrTempRegister = MIPSRegisters::t2; + // For storing compare result + static const RegisterID cmpTempRegister = MIPSRegisters::t3; + + // FP temp register + static const FPRegisterID fpTempRegister = MIPSRegisters::f16; + + static const int MaximumCompactPtrAlignedAddressOffset = 0x7FFFFFFF; + + enum RelationalCondition { + Equal, + NotEqual, + Above, + AboveOrEqual, + Below, + BelowOrEqual, + GreaterThan, + GreaterThanOrEqual, + LessThan, + LessThanOrEqual + }; + + enum ResultCondition { + Overflow, + Signed, + Zero, + NonZero + }; + + enum DoubleCondition { + DoubleEqual, + DoubleNotEqual, + DoubleGreaterThan, + DoubleGreaterThanOrEqual, + DoubleLessThan, + DoubleLessThanOrEqual, + DoubleEqualOrUnordered, + DoubleNotEqualOrUnordered, + DoubleGreaterThanOrUnordered, + DoubleGreaterThanOrEqualOrUnordered, + DoubleLessThanOrUnordered, + DoubleLessThanOrEqualOrUnordered + }; + + static const RegisterID stackPointerRegister = MIPSRegisters::sp; + static const RegisterID returnAddressRegister = MIPSRegisters::ra; + + // Integer arithmetic operations: + // + // Operations are typically two operand - operation(source, srcDst) + // For many operations the source may be an TrustedImm32, the srcDst operand + // may often be a memory location (explictly described using an Address + // object). + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.addu(dest, dest, src); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + add32(imm, dest, dest); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (imm.m_value >= -32768 && imm.m_value <= 32767 + && !m_fixedWidth) { + /* + addiu dest, src, imm + */ + m_assembler.addiu(dest, src, imm.m_value); + } else { + /* + li immTemp, imm + addu dest, src, immTemp + */ + move(imm, immTempRegister); + m_assembler.addu(dest, src, immTempRegister); + } + } + + void add32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + add32(imm, src, dest); + } + + void add32(TrustedImm32 imm, Address address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + li immTemp, imm + addu dataTemp, dataTemp, immTemp + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, address.base, address.offset); + if (imm.m_value >= -32768 && imm.m_value <= 32767 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + li immtemp, imm + addu dataTemp, dataTemp, immTemp + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); + + if (imm.m_value >= -32768 && imm.m_value <= 32767 && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); + } + } + + void add32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + add32(dataTempRegister, dest); + } + + void add32(AbsoluteAddress src, RegisterID dest) + { + load32(src.m_ptr, dataTempRegister); + add32(dataTempRegister, dest); + } + + void add32(RegisterID src, Address dest) + { + if (dest.offset >= -32768 && dest.offset <= 32767 && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + addu dataTemp, dataTemp, src + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, dest.base, dest.offset); + m_assembler.addu(dataTempRegister, dataTempRegister, src); + m_assembler.sw(dataTempRegister, dest.base, dest.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + addu dataTemp, dataTemp, src + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (dest.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, dest.base); + m_assembler.lw(dataTempRegister, addrTempRegister, dest.offset); + m_assembler.addu(dataTempRegister, dataTempRegister, src); + m_assembler.sw(dataTempRegister, addrTempRegister, dest.offset); + } + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + /* + li addrTemp, address + li immTemp, imm + lw cmpTemp, 0(addrTemp) + addu dataTemp, cmpTemp, immTemp + sw dataTemp, 0(addrTemp) + */ + move(TrustedImmPtr(address.m_ptr), addrTempRegister); + m_assembler.lw(cmpTempRegister, addrTempRegister, 0); + if (imm.m_value >= -32768 && imm.m_value <= 32767 && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, cmpTempRegister, imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.addu(dataTempRegister, cmpTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, 0); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + /* + add32(imm, address) + sltu immTemp, dataTemp, cmpTemp # set carry-in bit + lw dataTemp, 4(addrTemp) + addiu dataTemp, imm.m_value >> 31 ? -1 : 0 + addu dataTemp, dataTemp, immTemp + sw dataTemp, 4(addrTemp) + */ + add32(imm, address); + m_assembler.sltu(immTempRegister, dataTempRegister, cmpTempRegister); + m_assembler.lw(dataTempRegister, addrTempRegister, 4); + if (imm.m_value >> 31) + m_assembler.addiu(dataTempRegister, dataTempRegister, -1); + m_assembler.addu(dataTempRegister, dataTempRegister, immTempRegister); + m_assembler.sw(dataTempRegister, addrTempRegister, 4); + } + + void and32(RegisterID src, RegisterID dest) + { + m_assembler.andInsn(dest, dest, src); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (imm.m_value > 0 && imm.m_value < 65535 && !m_fixedWidth) + m_assembler.andi(dest, dest, imm.m_value); + else { + /* + li immTemp, imm + and dest, dest, immTemp + */ + move(imm, immTempRegister); + m_assembler.andInsn(dest, dest, immTempRegister); + } + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.sll(dest, dest, imm.m_value); + } + + void lshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.sllv(dest, dest, shiftAmount); + } + + void mul32(RegisterID src, RegisterID dest) + { + m_assembler.mul(dest, dest, src); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (!imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (imm.m_value == 1 && !m_fixedWidth) + move(src, dest); + else { + /* + li dataTemp, imm + mul dest, src, dataTemp + */ + move(imm, dataTempRegister); + m_assembler.mul(dest, src, dataTempRegister); + } + } + + void neg32(RegisterID srcDest) + { + m_assembler.subu(srcDest, MIPSRegisters::zero, srcDest); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orInsn(dest, dest, src); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + m_assembler.orInsn(dest, op1, op2); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_value && !m_fixedWidth) + return; + + if (imm.m_value > 0 && imm.m_value < 65535 + && !m_fixedWidth) { + m_assembler.ori(dest, dest, imm.m_value); + return; + } + + /* + li dataTemp, imm + or dest, dest, dataTemp + */ + move(imm, dataTempRegister); + m_assembler.orInsn(dest, dest, dataTempRegister); + } + + void or32(RegisterID src, AbsoluteAddress dest) + { + load32(dest.m_ptr, dataTempRegister); + m_assembler.orInsn(dataTempRegister, dataTempRegister, src); + store32(dataTempRegister, dest.m_ptr); + } + + void rshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.srav(dest, dest, shiftAmount); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.sra(dest, dest, imm.m_value); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + m_assembler.sra(dest, src, imm.m_value); + } + + void urshift32(RegisterID shiftAmount, RegisterID dest) + { + m_assembler.srlv(dest, dest, shiftAmount); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.srl(dest, dest, imm.m_value); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.subu(dest, dest, src); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) { + /* + addiu dest, src, imm + */ + m_assembler.addiu(dest, dest, -imm.m_value); + } else { + /* + li immTemp, imm + subu dest, src, immTemp + */ + move(imm, immTempRegister); + m_assembler.subu(dest, dest, immTempRegister); + } + } + + void sub32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) { + /* + addiu dest, src, imm + */ + m_assembler.addiu(dest, src, -imm.m_value); + } else { + /* + li immTemp, imm + subu dest, src, immTemp + */ + move(imm, immTempRegister); + m_assembler.subu(dest, src, immTempRegister); + } + } + + void sub32(TrustedImm32 imm, Address address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + lw dataTemp, offset(base) + li immTemp, imm + subu dataTemp, dataTemp, immTemp + sw dataTemp, offset(base) + */ + m_assembler.lw(dataTempRegister, address.base, address.offset); + if (imm.m_value >= -32767 && imm.m_value <= 32768 && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dataTemp, (offset & 0xffff)(addrTemp) + li immtemp, imm + subu dataTemp, dataTemp, immTemp + sw dataTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dataTempRegister, addrTempRegister, address.offset); + + if (imm.m_value >= -32767 && imm.m_value <= 32768 + && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, address.offset); + } + } + + void sub32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + sub32(dataTempRegister, dest); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + /* + li addrTemp, address + li immTemp, imm + lw dataTemp, 0(addrTemp) + subu dataTemp, dataTemp, immTemp + sw dataTemp, 0(addrTemp) + */ + move(TrustedImmPtr(address.m_ptr), addrTempRegister); + m_assembler.lw(dataTempRegister, addrTempRegister, 0); + + if (imm.m_value >= -32767 && imm.m_value <= 32768 && !m_fixedWidth) + m_assembler.addiu(dataTempRegister, dataTempRegister, -imm.m_value); + else { + move(imm, immTempRegister); + m_assembler.subu(dataTempRegister, dataTempRegister, immTempRegister); + } + m_assembler.sw(dataTempRegister, addrTempRegister, 0); + } + + void xor32(RegisterID src, RegisterID dest) + { + m_assembler.xorInsn(dest, dest, src); + } + + void xor32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value == -1) { + m_assembler.nor(dest, dest, MIPSRegisters::zero); + return; + } + + /* + li immTemp, imm + xor dest, dest, immTemp + */ + move(imm, immTempRegister); + m_assembler.xorInsn(dest, dest, immTempRegister); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.sqrtd(dst, src); + } + + void absDouble(FPRegisterID, FPRegisterID) + { + ASSERT_NOT_REACHED(); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result(this); + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, address.offset); + return result; + } + + // Memory access operations: + // + // Loads are of the form load(address, destination) and stores of the form + // store(source, address). The source for a store may be an TrustedImm32. Address + // operand objects to loads and store will be implicitly constructed if a + // register is passed. + + /* Need to use zero-extened load byte for load8. */ + void load8(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lbu(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lbu dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lbu(dest, addrTempRegister, address.offset); + } + } + + void load8(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lbu dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lbu(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lbu dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lbu(dest, addrTempRegister, address.offset); + } + } + + void load8Signed(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lb dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lb(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lb dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lb(dest, addrTempRegister, address.offset); + } + } + + void load32(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lw(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lw dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, address.offset); + } + } + + void load32(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lw dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lw dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lw(dest, addrTempRegister, address.offset); + } + } + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + load16(address, dest); + } + + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32764 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + (Big-Endian) + lwl dest, address.offset(addrTemp) + lwr dest, address.offset+3(addrTemp) + (Little-Endian) + lwl dest, address.offset+3(addrTemp) + lwr dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); +#if CPU(BIG_ENDIAN) + m_assembler.lwl(dest, addrTempRegister, address.offset); + m_assembler.lwr(dest, addrTempRegister, address.offset + 3); +#else + m_assembler.lwl(dest, addrTempRegister, address.offset + 3); + m_assembler.lwr(dest, addrTempRegister, address.offset); + +#endif + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, address.offset >> 16 + ori immTemp, immTemp, address.offset & 0xffff + addu addrTemp, addrTemp, immTemp + (Big-Endian) + lw dest, 0(at) + lw dest, 3(at) + (Little-Endian) + lw dest, 3(at) + lw dest, 0(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, address.offset >> 16); + m_assembler.ori(immTempRegister, immTempRegister, address.offset); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); +#if CPU(BIG_ENDIAN) + m_assembler.lwl(dest, addrTempRegister, 0); + m_assembler.lwr(dest, addrTempRegister, 3); +#else + m_assembler.lwl(dest, addrTempRegister, 3); + m_assembler.lwr(dest, addrTempRegister, 0); +#endif + } + } + + void load32(const void* address, RegisterID dest) + { + /* + li addrTemp, address + lw dest, 0(addrTemp) + */ + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.lw(dest, addrTempRegister, 0); + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + lw dest, 0(addrTemp) + */ + DataLabel32 dataLabel(this); + move(TrustedImm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lw(dest, addrTempRegister, 0); + m_fixedWidth = false; + return dataLabel; + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabelCompact dataLabel(this); + load32WithAddressOffsetPatch(address, dest); + return dataLabel; + } + + /* Need to use zero-extened load half-word for load16. */ + void load16(ImplicitAddress address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.lhu(dest, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + lhu dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } + } + + /* Need to use zero-extened load half-word for load16. */ + void load16(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lhu dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lhu dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lhu(dest, addrTempRegister, address.offset); + } + } + + void load16Signed(BaseIndex address, RegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lh dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lh(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lh dest, (address.offset & 0xffff)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lh(dest, addrTempRegister, address.offset); + } + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + m_fixedWidth = true; + /* + lui addrTemp, address.offset >> 16 + ori addrTemp, addrTemp, address.offset & 0xffff + addu addrTemp, addrTemp, address.base + sw src, 0(addrTemp) + */ + DataLabel32 dataLabel(this); + move(TrustedImm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, 0); + m_fixedWidth = false; + return dataLabel; + } + + void store8(RegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sb src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sb(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sb src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.sb(src, addrTempRegister, address.offset); + } + } + + void store8(TrustedImm32 imm, void* address) + { + /* + li immTemp, imm + li addrTemp, address + sb src, 0(addrTemp) + */ + if (!imm.m_value && !m_fixedWidth) { + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sb(MIPSRegisters::zero, addrTempRegister, 0); + } else { + move(imm, immTempRegister); + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sb(immTempRegister, addrTempRegister, 0); + } + } + + void store16(RegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sh src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sh(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sh src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.sh(src, addrTempRegister, address.offset); + } + } + + void store32(RegisterID src, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.sw(src, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sw src, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, address.offset); + } + } + + void store32(RegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sw src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sw(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sw src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.sw(src, addrTempRegister, address.offset); + } + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + if (!imm.m_value) + m_assembler.sw(MIPSRegisters::zero, address.base, address.offset); + else { + move(imm, immTempRegister); + m_assembler.sw(immTempRegister, address.base, address.offset); + } + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sw immTemp, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + if (!imm.m_value && !m_fixedWidth) + m_assembler.sw(MIPSRegisters::zero, addrTempRegister, address.offset); + else { + move(imm, immTempRegister); + m_assembler.sw(immTempRegister, addrTempRegister, address.offset); + } + } + } + + void store32(RegisterID src, const void* address) + { + /* + li addrTemp, address + sw src, 0(addrTemp) + */ + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sw(src, addrTempRegister, 0); + } + + void store32(TrustedImm32 imm, const void* address) + { + /* + li immTemp, imm + li addrTemp, address + sw src, 0(addrTemp) + */ + if (!imm.m_value && !m_fixedWidth) { + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sw(MIPSRegisters::zero, addrTempRegister, 0); + } else { + move(imm, immTempRegister); + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sw(immTempRegister, addrTempRegister, 0); + } + } + + // Floating-point operations: + + static bool supportsFloatingPoint() + { +#if WTF_MIPS_DOUBLE_FLOAT + return true; +#else + return false; +#endif + } + + static bool supportsFloatingPointTruncate() + { +#if WTF_MIPS_DOUBLE_FLOAT && WTF_MIPS_ISA_AT_LEAST(2) + return true; +#else + return false; +#endif + } + + static bool supportsFloatingPointSqrt() + { +#if WTF_MIPS_DOUBLE_FLOAT && WTF_MIPS_ISA_AT_LEAST(2) + return true; +#else + return false; +#endif + } + static bool supportsFloatingPointAbs() { return false; } + + // Stack manipulation operations: + // + // The ABI is assumed to provide a stack abstraction to memory, + // containing machine word sized units of data. Push and pop + // operations add and remove a single register sized unit of data + // to or from the stack. Peek and poke operations read or write + // values on the stack, without moving the current stack position. + + void pop(RegisterID dest) + { + m_assembler.lw(dest, MIPSRegisters::sp, 0); + m_assembler.addiu(MIPSRegisters::sp, MIPSRegisters::sp, 4); + } + + void push(RegisterID src) + { + m_assembler.addiu(MIPSRegisters::sp, MIPSRegisters::sp, -4); + m_assembler.sw(src, MIPSRegisters::sp, 0); + } + + void push(Address address) + { + load32(address, dataTempRegister); + push(dataTempRegister); + } + + void push(TrustedImm32 imm) + { + move(imm, immTempRegister); + push(immTempRegister); + } + + // Register move operations: + // + // Move values in registers. + + void move(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_value && !m_fixedWidth) + move(MIPSRegisters::zero, dest); + else if (m_fixedWidth) { + m_assembler.lui(dest, imm.m_value >> 16); + m_assembler.ori(dest, dest, imm.m_value); + } else + m_assembler.li(dest, imm.m_value); + } + + void move(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + m_assembler.move(dest, src); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + move(TrustedImm32(imm), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + move(reg1, immTempRegister); + move(reg2, reg1); + move(immTempRegister, reg2); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + move(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest || m_fixedWidth) + move(src, dest); + } + + // Forwards / external control flow operations: + // + // This set of jump and conditional branch operations return a Jump + // object which may linked at a later point, allow forwards jump, + // or jumps that will require external linkage (after the code has been + // relocated). + // + // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge + // respecitvely, for unsigned comparisons the names b, a, be, and ae are + // used (representing the names 'below' and 'above'). + // + // Operands to the comparision are provided in the expected order, e.g. + // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when + // treated as a signed 32bit value, is less than or equal to 5. + // + // jz and jnz test whether the first operand is equal to zero, and take + // an optional second operand of a mask under which to perform the test. + + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + // Make sure the immediate value is unsigned 8 bits. + ASSERT(!(right.m_value & 0xFFFFFF00)); + load8(left, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + // Make sure the immediate value is unsigned 8 bits. + ASSERT(!(right.m_value & 0xFFFFFF00)); + load8(left, dataTempRegister); + move(right, immTempRegister); + compare32(cond, dataTempRegister, immTempRegister, dest); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(right.m_value & 0xFFFFFF00)); + load8(left, dataTempRegister); + // Be careful that the previous load8() uses immTempRegister. + // So, we need to put move() after load8(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) + { + if (cond == Equal) + return branchEqual(left, right); + if (cond == NotEqual) + return branchNotEqual(left, right); + if (cond == Above) { + m_assembler.sltu(cmpTempRegister, right, left); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == AboveOrEqual) { + m_assembler.sltu(cmpTempRegister, left, right); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Below) { + m_assembler.sltu(cmpTempRegister, left, right); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == BelowOrEqual) { + m_assembler.sltu(cmpTempRegister, right, left); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == GreaterThan) { + m_assembler.slt(cmpTempRegister, right, left); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == GreaterThanOrEqual) { + m_assembler.slt(cmpTempRegister, left, right); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == LessThan) { + m_assembler.slt(cmpTempRegister, left, right); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == LessThanOrEqual) { + m_assembler.slt(cmpTempRegister, right, left); + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + } + ASSERT(0); + + return Jump(); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + move(right, immTempRegister); + return branch32(cond, left, immTempRegister); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + load32(right, dataTempRegister); + return branch32(cond, left, dataTempRegister); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + load32(left, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + load32(left, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + load32(left, dataTempRegister); + // Be careful that the previous load32() uses immTempRegister. + // So, we need to put move() after load32(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + load32WithUnalignedHalfWords(left, dataTempRegister); + // Be careful that the previous load32WithUnalignedHalfWords() + // uses immTempRegister. + // So, we need to put move() after load32WithUnalignedHalfWords(). + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + load32(left.m_ptr, dataTempRegister); + return branch32(cond, dataTempRegister, right); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + load32(left.m_ptr, dataTempRegister); + move(right, immTempRegister); + return branch32(cond, dataTempRegister, immTempRegister); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + ASSERT((cond == Zero) || (cond == NonZero)); + m_assembler.andInsn(cmpTempRegister, reg, mask); + if (cond == Zero) + return branchEqual(cmpTempRegister, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + return branchEqual(reg, MIPSRegisters::zero); + return branchNotEqual(reg, MIPSRegisters::zero); + } + move(mask, immTempRegister); + return branchTest32(cond, reg, immTempRegister); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + load32(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + load32(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + load8(address, dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + move(TrustedImmPtr(address.m_ptr), dataTempRegister); + load8(Address(dataTempRegister), dataTempRegister); + return branchTest32(cond, dataTempRegister, mask); + } + + Jump jump() + { + return branchEqual(MIPSRegisters::zero, MIPSRegisters::zero); + } + + void jump(RegisterID target) + { + m_assembler.jr(target); + m_assembler.nop(); + } + + void jump(Address address) + { + m_fixedWidth = true; + load32(address, MIPSRegisters::t9); + m_assembler.jr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + } + + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + m_assembler.vmov(dest1, dest2, src); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + UNUSED_PARAM(scratch); + m_assembler.vmov(dest, src1, src2); + } + + // Arithmetic control flow operations: + // + // This set of conditional branch operations branch based + // on the result of an arithmetic operation. The operation + // is performed as normal, storing the result. + // + // * jz operations branch if the result is zero. + // * jo operations branch if the (signed) arithmetic + // operation caused an overflow to occur. + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + move dest, dataTemp + xor cmpTemp, dataTemp, src + bltz cmpTemp, No_overflow # diff sign bit -> no overflow + addu dest, dataTemp, src + xor cmpTemp, dest, dataTemp + bgez cmpTemp, No_overflow # same sign big -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + move(dest, dataTempRegister); + m_assembler.xorInsn(cmpTempRegister, dataTempRegister, src); + m_assembler.bltz(cmpTempRegister, 10); + m_assembler.addu(dest, dataTempRegister, src); + m_assembler.xorInsn(cmpTempRegister, dest, dataTempRegister); + m_assembler.bgez(cmpTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + add32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + add32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + add32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + return branchAdd32(cond, immTempRegister, dest); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + move(src, dest); + return branchAdd32(cond, immTempRegister, dest); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + mult src, dest + mfhi dataTemp + mflo dest + sra addrTemp, dest, 31 + beq dataTemp, addrTemp, No_overflow # all sign bits (bit 63 to bit 31) are the same -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + m_assembler.mult(src, dest); + m_assembler.mfhi(dataTempRegister); + m_assembler.mflo(dest); + m_assembler.sra(addrTempRegister, dest, 31); + m_assembler.beq(dataTempRegister, addrTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + mul32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + mul32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + mul32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(imm, immTempRegister); + move(src, dest); + return branchMul32(cond, immTempRegister, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Overflow) { + /* + move dest, dataTemp + xor cmpTemp, dataTemp, src + bgez cmpTemp, No_overflow # same sign bit -> no overflow + subu dest, dataTemp, src + xor cmpTemp, dest, dataTemp + bgez cmpTemp, No_overflow # same sign bit -> no overflow + nop + b Overflow + nop + nop + nop + nop + nop + No_overflow: + */ + move(dest, dataTempRegister); + m_assembler.xorInsn(cmpTempRegister, dataTempRegister, src); + m_assembler.bgez(cmpTempRegister, 10); + m_assembler.subu(dest, dataTempRegister, src); + m_assembler.xorInsn(cmpTempRegister, dest, dataTempRegister); + m_assembler.bgez(cmpTempRegister, 7); + m_assembler.nop(); + return jump(); + } + if (cond == Signed) { + sub32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + sub32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + sub32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + return branchSub32(cond, immTempRegister, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + move(imm, immTempRegister); + move(src, dest); + return branchSub32(cond, immTempRegister, dest); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); + if (cond == Signed) { + or32(src, dest); + // Check if dest is negative. + m_assembler.slt(cmpTempRegister, dest, MIPSRegisters::zero); + return branchNotEqual(cmpTempRegister, MIPSRegisters::zero); + } + if (cond == Zero) { + or32(src, dest); + return branchEqual(dest, MIPSRegisters::zero); + } + if (cond == NonZero) { + or32(src, dest); + return branchNotEqual(dest, MIPSRegisters::zero); + } + ASSERT(0); + return Jump(); + } + + // Miscellaneous operations: + + void breakpoint() + { + m_assembler.bkpt(); + } + + Call nearCall() + { + /* We need two words for relaxation. */ + m_assembler.nop(); + m_assembler.nop(); + m_assembler.jal(); + m_assembler.nop(); + return Call(m_assembler.label(), Call::LinkableNear); + } + + Call call() + { + m_assembler.lui(MIPSRegisters::t9, 0); + m_assembler.ori(MIPSRegisters::t9, MIPSRegisters::t9, 0); + m_assembler.jalr(MIPSRegisters::t9); + m_assembler.nop(); + return Call(m_assembler.label(), Call::Linkable); + } + + Call call(RegisterID target) + { + m_assembler.jalr(target); + m_assembler.nop(); + return Call(m_assembler.label(), Call::None); + } + + Call call(Address address) + { + m_fixedWidth = true; + load32(address, MIPSRegisters::t9); + m_assembler.jalr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + return Call(m_assembler.label(), Call::None); + } + + void ret() + { + m_assembler.jr(MIPSRegisters::ra); + m_assembler.nop(); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + if (cond == Equal) { + m_assembler.xorInsn(dest, left, right); + m_assembler.sltiu(dest, dest, 1); + } else if (cond == NotEqual) { + m_assembler.xorInsn(dest, left, right); + m_assembler.sltu(dest, MIPSRegisters::zero, dest); + } else if (cond == Above) + m_assembler.sltu(dest, right, left); + else if (cond == AboveOrEqual) { + m_assembler.sltu(dest, left, right); + m_assembler.xori(dest, dest, 1); + } else if (cond == Below) + m_assembler.sltu(dest, left, right); + else if (cond == BelowOrEqual) { + m_assembler.sltu(dest, right, left); + m_assembler.xori(dest, dest, 1); + } else if (cond == GreaterThan) + m_assembler.slt(dest, right, left); + else if (cond == GreaterThanOrEqual) { + m_assembler.slt(dest, left, right); + m_assembler.xori(dest, dest, 1); + } else if (cond == LessThan) + m_assembler.slt(dest, left, right); + else if (cond == LessThanOrEqual) { + m_assembler.slt(dest, right, left); + m_assembler.xori(dest, dest, 1); + } + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + move(right, immTempRegister); + compare32(cond, left, immTempRegister, dest); + } + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + ASSERT((cond == Zero) || (cond == NonZero)); + load8(address, dataTempRegister); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + m_assembler.sltiu(dest, dataTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); + } else { + move(mask, immTempRegister); + m_assembler.andInsn(cmpTempRegister, dataTempRegister, immTempRegister); + if (cond == Zero) + m_assembler.sltiu(dest, cmpTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, cmpTempRegister); + } + } + + void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + ASSERT((cond == Zero) || (cond == NonZero)); + load32(address, dataTempRegister); + if (mask.m_value == -1 && !m_fixedWidth) { + if (cond == Zero) + m_assembler.sltiu(dest, dataTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, dataTempRegister); + } else { + move(mask, immTempRegister); + m_assembler.andInsn(cmpTempRegister, dataTempRegister, immTempRegister); + if (cond == Zero) + m_assembler.sltiu(dest, cmpTempRegister, 1); + else + m_assembler.sltu(dest, MIPSRegisters::zero, cmpTempRegister); + } + } + + DataLabel32 moveWithPatch(TrustedImm32 imm, RegisterID dest) + { + m_fixedWidth = true; + DataLabel32 label(this); + move(imm, dest); + m_fixedWidth = false; + return label; + } + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + m_fixedWidth = true; + DataLabelPtr label(this); + move(initialValue, dest); + m_fixedWidth = false; + return label; + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + m_fixedWidth = true; + dataLabel = moveWithPatch(initialRightValue, immTempRegister); + Jump temp = branch32(cond, left, immTempRegister); + m_fixedWidth = false; + return temp; + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + m_fixedWidth = true; + load32(left, dataTempRegister); + dataLabel = moveWithPatch(initialRightValue, immTempRegister); + Jump temp = branch32(cond, dataTempRegister, immTempRegister); + m_fixedWidth = false; + return temp; + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + m_fixedWidth = true; + DataLabelPtr dataLabel = moveWithPatch(initialValue, dataTempRegister); + store32(dataTempRegister, address); + m_fixedWidth = false; + return dataLabel; + } + + DataLabelPtr storePtrWithPatch(ImplicitAddress address) + { + return storePtrWithPatch(TrustedImmPtr(0), address); + } + + Call tailRecursiveCall() + { + // Like a normal call, but don't update the returned address register + m_fixedWidth = true; + move(TrustedImm32(0), MIPSRegisters::t9); + m_assembler.jr(MIPSRegisters::t9); + m_assembler.nop(); + m_fixedWidth = false; + return Call(m_assembler.label(), Call::Linkable); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + oldJump.link(this); + return tailRecursiveCall(); + } + + void loadFloat(BaseIndex address, FPRegisterID dest) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lwc1 dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lwc1 dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + } + } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { +#if WTF_MIPS_ISA(1) + /* + li addrTemp, address.offset + addu addrTemp, addrTemp, base + lwc1 dest, 0(addrTemp) + lwc1 dest+1, 4(addrTemp) + */ + move(TrustedImm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lwc1(dest, addrTempRegister, 0); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + m_assembler.ldc1(dest, address.base, address.offset); + } else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + ldc1 dest, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.ldc1(dest, addrTempRegister, address.offset); + } +#endif + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { +#if WTF_MIPS_ISA(1) + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lwc1 dest, address.offset(addrTemp) + lwc1 dest+1, (address.offset+4)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, address.offset + 4); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + lwc1 dest, (address.offset & 0xffff)(at) + lwc1 dest+4, (address.offset & 0xffff + 4)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.lwc1(dest, addrTempRegister, address.offset); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, address.offset + 4); + } +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + ldc1 dest, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.ldc1(dest, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + ldc1 dest, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.ldc1(dest, addrTempRegister, address.offset); + } +#endif + } + + void loadDouble(const void* address, FPRegisterID dest) + { +#if WTF_MIPS_ISA(1) + /* + li addrTemp, address + lwc1 dest, 0(addrTemp) + lwc1 dest+1, 4(addrTemp) + */ + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.lwc1(dest, addrTempRegister, 0); + m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4); +#else + /* + li addrTemp, address + ldc1 dest, 0(addrTemp) + */ + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.ldc1(dest, addrTempRegister, 0); +#endif + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + swc1 src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.swc1(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + swc1 src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.swc1(src, addrTempRegister, address.offset); + } + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { +#if WTF_MIPS_ISA(1) + /* + li addrTemp, address.offset + addu addrTemp, addrTemp, base + swc1 dest, 0(addrTemp) + swc1 dest+1, 4(addrTemp) + */ + move(TrustedImm32(address.offset), addrTempRegister); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.swc1(src, addrTempRegister, 0); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, 4); +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) + m_assembler.sdc1(src, address.base, address.offset); + else { + /* + lui addrTemp, (offset + 0x8000) >> 16 + addu addrTemp, addrTemp, base + sdc1 src, (offset & 0xffff)(addrTemp) + */ + m_assembler.lui(addrTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sdc1(src, addrTempRegister, address.offset); + } +#endif + } + + void storeDouble(FPRegisterID src, BaseIndex address) + { +#if WTF_MIPS_ISA(1) + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + swc1 src, address.offset(addrTemp) + swc1 src+1, (address.offset + 4)(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.swc1(src, addrTempRegister, address.offset); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, address.offset + 4); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + swc1 src, (address.offset & 0xffff)(at) + swc1 src+1, (address.offset & 0xffff + 4)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.swc1(src, addrTempRegister, address.offset); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, address.offset + 4); + } +#else + if (address.offset >= -32768 && address.offset <= 32767 + && !m_fixedWidth) { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + sdc1 src, address.offset(addrTemp) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.sdc1(src, addrTempRegister, address.offset); + } else { + /* + sll addrTemp, address.index, address.scale + addu addrTemp, addrTemp, address.base + lui immTemp, (address.offset + 0x8000) >> 16 + addu addrTemp, addrTemp, immTemp + sdc1 src, (address.offset & 0xffff)(at) + */ + m_assembler.sll(addrTempRegister, address.index, address.scale); + m_assembler.addu(addrTempRegister, addrTempRegister, address.base); + m_assembler.lui(immTempRegister, (address.offset + 0x8000) >> 16); + m_assembler.addu(addrTempRegister, addrTempRegister, immTempRegister); + m_assembler.sdc1(src, addrTempRegister, address.offset); + } +#endif + } + + void storeDouble(FPRegisterID src, const void* address) + { +#if WTF_MIPS_ISA(1) + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.swc1(src, addrTempRegister, 0); + m_assembler.swc1(FPRegisterID(src + 1), addrTempRegister, 4); +#else + move(TrustedImmPtr(address), addrTempRegister); + m_assembler.sdc1(src, addrTempRegister, 0); +#endif + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.addd(dest, dest, src); + } + + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + m_assembler.addd(dest, op1, op2); + } + + void addDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.addd(dest, dest, fpTempRegister); + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + loadDouble(address.m_ptr, fpTempRegister); + m_assembler.addd(dest, dest, fpTempRegister); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.subd(dest, dest, src); + } + + void subDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.subd(dest, dest, fpTempRegister); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.muld(dest, dest, src); + } + + void mulDouble(Address src, FPRegisterID dest) + { + loadDouble(src, fpTempRegister); + m_assembler.muld(dest, dest, fpTempRegister); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.divd(dest, dest, src); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.mtc1(src, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + load32(src, dataTempRegister); + m_assembler.mtc1(dataTempRegister, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + load32(src.m_ptr, dataTempRegister); + m_assembler.mtc1(dataTempRegister, fpTempRegister); + m_assembler.cvtdw(dest, fpTempRegister); + } + + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.cvtds(dst, src); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.cvtsd(dst, src); + } + + void insertRelaxationWords() + { + /* We need four words for relaxation. */ + m_assembler.beq(MIPSRegisters::zero, MIPSRegisters::zero, 3); // Jump over nops; + m_assembler.nop(); + m_assembler.nop(); + m_assembler.nop(); + } + + Jump branchTrue() + { + m_assembler.appendJump(); + m_assembler.bc1t(); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.label()); + } + + Jump branchFalse() + { + m_assembler.appendJump(); + m_assembler.bc1f(); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.label()); + } + + Jump branchEqual(RegisterID rs, RegisterID rt) + { + m_assembler.appendJump(); + m_assembler.beq(rs, rt, 0); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.label()); + } + + Jump branchNotEqual(RegisterID rs, RegisterID rt) + { + m_assembler.appendJump(); + m_assembler.bne(rs, rt, 0); + m_assembler.nop(); + insertRelaxationWords(); + return Jump(m_assembler.label()); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + if (cond == DoubleEqual) { + m_assembler.ceqd(left, right); + return branchTrue(); + } + if (cond == DoubleNotEqual) { + m_assembler.cueqd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThan) { + m_assembler.cngtd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrEqual) { + m_assembler.cnged(left, right); + return branchFalse(); // false + } + if (cond == DoubleLessThan) { + m_assembler.cltd(left, right); + return branchTrue(); + } + if (cond == DoubleLessThanOrEqual) { + m_assembler.cled(left, right); + return branchTrue(); + } + if (cond == DoubleEqualOrUnordered) { + m_assembler.cueqd(left, right); + return branchTrue(); + } + if (cond == DoubleNotEqualOrUnordered) { + m_assembler.ceqd(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrUnordered) { + m_assembler.coled(left, right); + return branchFalse(); // false + } + if (cond == DoubleGreaterThanOrEqualOrUnordered) { + m_assembler.coltd(left, right); + return branchFalse(); // false + } + if (cond == DoubleLessThanOrUnordered) { + m_assembler.cultd(left, right); + return branchTrue(); + } + if (cond == DoubleLessThanOrEqualOrUnordered) { + m_assembler.culed(left, right); + return branchTrue(); + } + ASSERT(0); + + return Jump(); + } + + // Truncates 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, INT_MAX 0x7fffffff). + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + m_assembler.truncwd(fpTempRegister, src); + m_assembler.mfc1(dest, fpTempRegister); + return branch32(Equal, dest, TrustedImm32(0x7fffffff)); + } + + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + m_assembler.cvtwd(fpTempRegister, src); + m_assembler.mfc1(dest, fpTempRegister); + + // If the result is zero, it might have been -0.0, and the double comparison won't catch this! + failureCases.append(branch32(Equal, dest, MIPSRegisters::zero)); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + convertInt32ToDouble(dest, fpTemp); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, fpTemp, src)); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) + { +#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 + m_assembler.mtc1(MIPSRegisters::zero, scratch); + m_assembler.mthc1(MIPSRegisters::zero, scratch); +#else + m_assembler.mtc1(MIPSRegisters::zero, scratch); + m_assembler.mtc1(MIPSRegisters::zero, FPRegisterID(scratch + 1)); +#endif + return branchDouble(DoubleNotEqual, reg, scratch); + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) + { +#if WTF_MIPS_ISA_REV(2) && WTF_MIPS_FP64 + m_assembler.mtc1(MIPSRegisters::zero, scratch); + m_assembler.mthc1(MIPSRegisters::zero, scratch); +#else + m_assembler.mtc1(MIPSRegisters::zero, scratch); + m_assembler.mtc1(MIPSRegisters::zero, FPRegisterID(scratch + 1)); +#endif + return branchDouble(DoubleEqualOrUnordered, reg, scratch); + } + + void nop() + { + m_assembler.nop(); + } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(reinterpret_cast(MIPSAssembler::readCallTarget(call.dataLocation()))); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + ASSERT_NOT_REACHED(); + } + + static ptrdiff_t maxJumpReplacementSize() + { + ASSERT_NOT_REACHED(); + return 0; + } + + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + return label.labelAtOffset(0); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) + { + MIPSAssembler::revertJumpToMove(instructionStart.dataLocation(), immTempRegister, reinterpret_cast(initialValue) & 0xffff); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + UNREACHABLE_FOR_PLATFORM(); + } + + +private: + // If m_fixedWidth is true, we will generate a fixed number of instructions. + // Otherwise, we can emit any number of instructions. + bool m_fixedWidth; + + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + MIPSAssembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + +}; + +} + +#endif // ENABLE(ASSEMBLER) && CPU(MIPS) + +#endif // MacroAssemblerMIPS_h diff --git a/src/3rdparty/masm/assembler/MacroAssemblerSH4.cpp b/src/3rdparty/masm/assembler/MacroAssemblerSH4.cpp new file mode 100644 index 0000000000..59de3ff48c --- /dev/null +++ b/src/3rdparty/masm/assembler/MacroAssemblerSH4.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 STMicroelectronics. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "config.h" + +#if ENABLE(ASSEMBLER) && CPU(SH4) + +#include "MacroAssemblerSH4.h" + +namespace JSC { + +void MacroAssemblerSH4::linkCall(void* code, Call call, FunctionPtr function) +{ + SH4Assembler::linkCall(code, call.m_label, function.value()); +} + +void MacroAssemblerSH4::repatchCall(CodeLocationCall call, CodeLocationLabel destination) +{ + SH4Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); +} + +void MacroAssemblerSH4::repatchCall(CodeLocationCall call, FunctionPtr destination) +{ + SH4Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); +} + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) diff --git a/src/3rdparty/masm/assembler/MacroAssemblerSH4.h b/src/3rdparty/masm/assembler/MacroAssemblerSH4.h new file mode 100644 index 0000000000..ef210f80cb --- /dev/null +++ b/src/3rdparty/masm/assembler/MacroAssemblerSH4.h @@ -0,0 +1,2293 @@ +/* + * Copyright (C) 2009-2011 STMicroelectronics. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MacroAssemblerSH4_h +#define MacroAssemblerSH4_h + +#if ENABLE(ASSEMBLER) && CPU(SH4) + +#include "SH4Assembler.h" +#include "AbstractMacroAssembler.h" +#include + +namespace JSC { + +class MacroAssemblerSH4 : public AbstractMacroAssembler { +public: + typedef SH4Assembler::FPRegisterID FPRegisterID; + + static const Scale ScalePtr = TimesFour; + static const FPRegisterID fscratch = SH4Registers::fr10; + static const RegisterID stackPointerRegister = SH4Registers::sp; + static const RegisterID linkRegister = SH4Registers::pr; + static const RegisterID scratchReg3 = SH4Registers::r13; + + static const int MaximumCompactPtrAlignedAddressOffset = 60; + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return (value >= 0) && (value <= MaximumCompactPtrAlignedAddressOffset); + } + + enum RelationalCondition { + Equal = SH4Assembler::EQ, + NotEqual = SH4Assembler::NE, + Above = SH4Assembler::HI, + AboveOrEqual = SH4Assembler::HS, + Below = SH4Assembler::LI, + BelowOrEqual = SH4Assembler::LS, + GreaterThan = SH4Assembler::GT, + GreaterThanOrEqual = SH4Assembler::GE, + LessThan = SH4Assembler::LT, + LessThanOrEqual = SH4Assembler::LE + }; + + enum ResultCondition { + Overflow = SH4Assembler::OF, + Signed = SH4Assembler::SI, + Zero = SH4Assembler::EQ, + NonZero = SH4Assembler::NE + }; + + enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleEqual = SH4Assembler::EQ, + DoubleNotEqual = SH4Assembler::NE, + DoubleGreaterThan = SH4Assembler::GT, + DoubleGreaterThanOrEqual = SH4Assembler::GE, + DoubleLessThan = SH4Assembler::LT, + DoubleLessThanOrEqual = SH4Assembler::LE, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = SH4Assembler::EQU, + DoubleNotEqualOrUnordered = SH4Assembler::NEU, + DoubleGreaterThanOrUnordered = SH4Assembler::GTU, + DoubleGreaterThanOrEqualOrUnordered = SH4Assembler::GEU, + DoubleLessThanOrUnordered = SH4Assembler::LTU, + DoubleLessThanOrEqualOrUnordered = SH4Assembler::LEU, + }; + + RegisterID claimScratch() + { + return m_assembler.claimScratch(); + } + + void releaseScratch(RegisterID reg) + { + m_assembler.releaseScratch(reg); + } + + // Integer arithmetic operations + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.addlRegReg(src, dest); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + if (m_assembler.isImmediate(imm.m_value)) { + m_assembler.addlImm8r(imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm.m_value, scr); + m_assembler.addlRegReg(scr, dest); + releaseScratch(scr); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.movlRegReg(src, dest); + add32(imm, dest); + } + + void add32(TrustedImm32 imm, Address address) + { + RegisterID scr = claimScratch(); + load32(address, scr); + add32(imm, scr); + store32(scr, address); + releaseScratch(scr); + } + + void add32(Address src, RegisterID dest) + { + RegisterID scr = claimScratch(); + load32(src, scr); + m_assembler.addlRegReg(scr, dest); + releaseScratch(scr); + } + + void add32(AbsoluteAddress src, RegisterID dest) + { + RegisterID scr = claimScratch(); + load32(src.m_ptr, scr); + m_assembler.addlRegReg(scr, dest); + releaseScratch(scr); + } + + void and32(RegisterID src, RegisterID dest) + { + m_assembler.andlRegReg(src, dest); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + if ((imm.m_value <= 255) && (imm.m_value >= 0) && (dest == SH4Registers::r0)) { + m_assembler.andlImm8r(imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((imm.m_value), scr); + m_assembler.andlRegReg(scr, dest); + releaseScratch(scr); + } + + void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (src != dest) { + move(imm, dest); + and32(src, dest); + return; + } + + and32(imm, dest); + } + + void lshift32(RegisterID shiftamount, RegisterID dest) + { + if (shiftamount == SH4Registers::r0) + m_assembler.andlImm8r(0x1f, shiftamount); + else { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(0x1f, scr); + m_assembler.andlRegReg(scr, shiftamount); + releaseScratch(scr); + } + m_assembler.shllRegReg(dest, shiftamount); + } + + void rshift32(int imm, RegisterID dest) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(-imm, scr); + m_assembler.shaRegReg(dest, scr); + releaseScratch(scr); + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + if (!imm.m_value) + return; + + if ((imm.m_value == 1) || (imm.m_value == 2) || (imm.m_value == 8) || (imm.m_value == 16)) { + m_assembler.shllImm8r(imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((imm.m_value & 0x1f) , scr); + m_assembler.shllRegReg(dest, scr); + releaseScratch(scr); + } + + void lshift32(RegisterID src, TrustedImm32 shiftamount, RegisterID dest) + { + if (src != dest) + move(src, dest); + + lshift32(shiftamount, dest); + } + + void mul32(RegisterID src, RegisterID dest) + { + m_assembler.imullRegReg(src, dest); + m_assembler.stsmacl(dest); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + RegisterID scr = claimScratch(); + move(imm, scr); + if (src != dest) + move(src, dest); + mul32(scr, dest); + releaseScratch(scr); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orlRegReg(src, dest); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + if ((imm.m_value <= 255) && (imm.m_value >= 0) && (dest == SH4Registers::r0)) { + m_assembler.orlImm8r(imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm.m_value, scr); + m_assembler.orlRegReg(scr, dest); + releaseScratch(scr); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + move(op1, dest); + else if (op1 == dest) + or32(op2, dest); + else { + move(op2, dest); + or32(op1, dest); + } + } + + +void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (src != dest) { + move(imm, dest); + or32(src, dest); + return; + } + + or32(imm, dest); + } + + void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + if (src != dest) { + move(imm, dest); + xor32(src, dest); + return; + } + + xor32(imm, dest); + } + + void rshift32(RegisterID shiftamount, RegisterID dest) + { + if (shiftamount == SH4Registers::r0) + m_assembler.andlImm8r(0x1f, shiftamount); + else { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(0x1f, scr); + m_assembler.andlRegReg(scr, shiftamount); + releaseScratch(scr); + } + m_assembler.neg(shiftamount, shiftamount); + m_assembler.shaRegReg(dest, shiftamount); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value & 0x1f) + rshift32(imm.m_value & 0x1f, dest); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (src != dest) + move(src, dest); + rshift32(imm, dest); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.sublRegReg(src, dest); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address, RegisterID scratchReg) + { + RegisterID result = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); + m_assembler.movlMemReg(scratchReg, result); + + if (m_assembler.isImmediate(-imm.m_value)) + m_assembler.addlImm8r(-imm.m_value, result); + else { + m_assembler.loadConstant(imm.m_value, scratchReg3); + m_assembler.sublRegReg(scratchReg3, result); + } + + store32(result, scratchReg); + releaseScratch(result); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + RegisterID result = claimScratch(); + RegisterID scratchReg = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); + m_assembler.movlMemReg(scratchReg, result); + + if (m_assembler.isImmediate(-imm.m_value)) + m_assembler.addlImm8r(-imm.m_value, result); + else { + m_assembler.loadConstant(imm.m_value, scratchReg3); + m_assembler.sublRegReg(scratchReg3, result); + } + + store32(result, scratchReg); + releaseScratch(result); + releaseScratch(scratchReg); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address, RegisterID scratchReg) + { + RegisterID result = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); + m_assembler.movlMemReg(scratchReg, result); + + if (m_assembler.isImmediate(imm.m_value)) + m_assembler.addlImm8r(imm.m_value, result); + else { + m_assembler.loadConstant(imm.m_value, scratchReg3); + m_assembler.addlRegReg(scratchReg3, result); + } + + store32(result, scratchReg); + releaseScratch(result); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + RegisterID result = claimScratch(); + RegisterID scratchReg = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scratchReg); + m_assembler.movlMemReg(scratchReg, result); + + if (m_assembler.isImmediate(imm.m_value)) + m_assembler.addlImm8r(imm.m_value, result); + else { + m_assembler.loadConstant(imm.m_value, scratchReg3); + m_assembler.addlRegReg(scratchReg3, result); + } + + store32(result, scratchReg); + releaseScratch(result); + releaseScratch(scratchReg); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + RegisterID scr1 = claimScratch(); + RegisterID scr2 = claimScratch(); + + // Add 32-bit LSB first. + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scr1); + m_assembler.movlMemReg(scr1, scr1); // scr1 = 32-bit LSB of int64 @ address + m_assembler.loadConstant(imm.m_value, scr2); + m_assembler.clrt(); + m_assembler.addclRegReg(scr1, scr2); + m_assembler.loadConstant(reinterpret_cast(address.m_ptr), scr1); + m_assembler.movlRegMem(scr2, scr1); // Update address with 32-bit LSB result. + + // Then add 32-bit MSB. + m_assembler.addlImm8r(4, scr1); + m_assembler.movlMemReg(scr1, scr1); // scr1 = 32-bit MSB of int64 @ address + m_assembler.movt(scr2); + if (imm.m_value < 0) + m_assembler.addlImm8r(-1, scr2); // Sign extend imm value if needed. + m_assembler.addvlRegReg(scr2, scr1); + m_assembler.loadConstant(reinterpret_cast(address.m_ptr) + 4, scr2); + m_assembler.movlRegMem(scr1, scr2); // Update (address + 4) with 32-bit MSB result. + + releaseScratch(scr2); + releaseScratch(scr1); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + if (m_assembler.isImmediate(-imm.m_value)) { + m_assembler.addlImm8r(-imm.m_value, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm.m_value, scr); + m_assembler.sublRegReg(scr, dest); + releaseScratch(scr); + } + + void sub32(Address src, RegisterID dest) + { + RegisterID scr = claimScratch(); + load32(src, scr); + m_assembler.sublRegReg(scr, dest); + releaseScratch(scr); + } + + void xor32(RegisterID src, RegisterID dest) + { + m_assembler.xorlRegReg(src, dest); + } + + void xor32(TrustedImm32 imm, RegisterID srcDest) + { + if (imm.m_value == -1) { + m_assembler.notlReg(srcDest, srcDest); + return; + } + + if ((srcDest != SH4Registers::r0) || (imm.m_value > 255) || (imm.m_value < 0)) { + RegisterID scr = claimScratch(); + m_assembler.loadConstant((imm.m_value), scr); + m_assembler.xorlRegReg(scr, srcDest); + releaseScratch(scr); + return; + } + + m_assembler.xorlImm8r(imm.m_value, srcDest); + } + + void compare32(int imm, RegisterID dst, RelationalCondition cond) + { + if (((cond == Equal) || (cond == NotEqual)) && (dst == SH4Registers::r0) && m_assembler.isImmediate(imm)) { + m_assembler.cmpEqImmR0(imm, dst); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm, scr); + m_assembler.cmplRegReg(scr, dst, SH4Condition(cond)); + releaseScratch(scr); + } + + void compare32(int offset, RegisterID base, RegisterID left, RelationalCondition cond) + { + RegisterID scr = claimScratch(); + if (!offset) { + m_assembler.movlMemReg(base, scr); + m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); + releaseScratch(scr); + return; + } + + if ((offset < 0) || (offset >= 64)) { + m_assembler.loadConstant(offset, scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, scr); + m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); + releaseScratch(scr); + return; + } + + m_assembler.movlMemReg(offset >> 2, base, scr); + m_assembler.cmplRegReg(scr, left, SH4Condition(cond)); + releaseScratch(scr); + } + + void testImm(int imm, int offset, RegisterID base) + { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + + if ((offset < 0) || (offset >= 64)) { + m_assembler.loadConstant(offset, scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, scr); + } else if (offset) + m_assembler.movlMemReg(offset >> 2, base, scr); + else + m_assembler.movlMemReg(base, scr); + if (m_assembler.isImmediate(imm)) + m_assembler.movImm8(imm, scr1); + else + m_assembler.loadConstant(imm, scr1); + + m_assembler.testlRegReg(scr, scr1); + releaseScratch(scr); + releaseScratch(scr1); + } + + void testlImm(int imm, RegisterID dst) + { + if ((dst == SH4Registers::r0) && (imm <= 255) && (imm >= 0)) { + m_assembler.testlImm8r(imm, dst); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm, scr); + m_assembler.testlRegReg(scr, dst); + releaseScratch(scr); + } + + void compare32(RegisterID right, int offset, RegisterID base, RelationalCondition cond) + { + if (!offset) { + RegisterID scr = claimScratch(); + m_assembler.movlMemReg(base, scr); + m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); + releaseScratch(scr); + return; + } + + if ((offset < 0) || (offset >= 64)) { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(offset, scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, scr); + m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); + releaseScratch(scr); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.movlMemReg(offset >> 2, base, scr); + m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); + releaseScratch(scr); + } + + void compare32(int imm, int offset, RegisterID base, RelationalCondition cond) + { + if (!offset) { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.movlMemReg(base, scr); + m_assembler.loadConstant(imm, scr1); + m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); + releaseScratch(scr1); + releaseScratch(scr); + return; + } + + if ((offset < 0) || (offset >= 64)) { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.loadConstant(offset, scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, scr); + m_assembler.loadConstant(imm, scr1); + m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); + releaseScratch(scr1); + releaseScratch(scr); + return; + } + + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.movlMemReg(offset >> 2, base, scr); + m_assembler.loadConstant(imm, scr1); + m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); + releaseScratch(scr1); + releaseScratch(scr); + } + + // Memory access operation + + void load32(ImplicitAddress address, RegisterID dest) + { + load32(address.base, address.offset, dest); + } + + void load8(ImplicitAddress address, RegisterID dest) + { + load8(address.base, address.offset, dest); + } + + void load8(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + load8(scr, address.offset, dest); + releaseScratch(scr); + } + + void load8Signed(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + load8Signed(scr, address.offset, dest); + releaseScratch(scr); + } + + void load32(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + load32(scr, address.offset, dest); + releaseScratch(scr); + } + + void load32(const void* address, RegisterID dest) + { + m_assembler.loadConstant(reinterpret_cast(const_cast(address)), dest); + m_assembler.movlMemReg(dest, dest); + } + + void load32(RegisterID base, int offset, RegisterID dest) + { + if (!offset) { + m_assembler.movlMemReg(base, dest); + return; + } + + if ((offset >= 0) && (offset < 64)) { + m_assembler.movlMemReg(offset >> 2, base, dest); + return; + } + + if ((dest == SH4Registers::r0) && (dest != base)) { + m_assembler.loadConstant((offset), dest); + m_assembler.movlR0mr(base, dest); + return; + } + + RegisterID scr; + if (dest == base) + scr = claimScratch(); + else + scr = dest; + m_assembler.loadConstant((offset), scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movlMemReg(scr, dest); + + if (dest == base) + releaseScratch(scr); + } + + void load8Signed(RegisterID base, int offset, RegisterID dest) + { + if (!offset) { + m_assembler.movbMemReg(base, dest); + return; + } + + if ((offset > 0) && (offset < 64) && (dest == SH4Registers::r0)) { + m_assembler.movbMemReg(offset, base, dest); + return; + } + + if (base != dest) { + m_assembler.loadConstant((offset), dest); + m_assembler.addlRegReg(base, dest); + m_assembler.movbMemReg(dest, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((offset), scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movbMemReg(scr, dest); + releaseScratch(scr); + } + + void load8(RegisterID base, int offset, RegisterID dest) + { + if (!offset) { + m_assembler.movbMemReg(base, dest); + m_assembler.extub(dest, dest); + return; + } + + if ((offset > 0) && (offset < 64) && (dest == SH4Registers::r0)) { + m_assembler.movbMemReg(offset, base, dest); + m_assembler.extub(dest, dest); + return; + } + + if (base != dest) { + m_assembler.loadConstant((offset), dest); + m_assembler.addlRegReg(base, dest); + m_assembler.movbMemReg(dest, dest); + m_assembler.extub(dest, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((offset), scr); + m_assembler.addlRegReg(base, scr); + m_assembler.movbMemReg(scr, dest); + m_assembler.extub(dest, dest); + releaseScratch(scr); + } + + void load32(RegisterID r0, RegisterID src, RegisterID dst) + { + ASSERT(r0 == SH4Registers::r0); + m_assembler.movlR0mr(src, dst); + } + + void load32(RegisterID src, RegisterID dst) + { + m_assembler.movlMemReg(src, dst); + } + + void load16(ImplicitAddress address, RegisterID dest) + { + if (!address.offset) { + m_assembler.movwMemReg(address.base, dest); + extuw(dest, dest); + return; + } + + if ((address.offset > 0) && (address.offset < 64) && (dest == SH4Registers::r0)) { + m_assembler.movwMemReg(address.offset, address.base, dest); + extuw(dest, dest); + return; + } + + if (address.base != dest) { + m_assembler.loadConstant((address.offset), dest); + m_assembler.addlRegReg(address.base, dest); + m_assembler.movwMemReg(dest, dest); + extuw(dest, dest); + return; + } + + RegisterID scr = claimScratch(); + m_assembler.loadConstant((address.offset), scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movwMemReg(scr, dest); + extuw(dest, dest); + releaseScratch(scr); + } + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + add32(address.base, scr); + load8(scr, scr1); + add32(TrustedImm32(1), scr); + load8(scr, dest); + m_assembler.shllImm8r(8, dest); + or32(scr1, dest); + + releaseScratch(scr); + releaseScratch(scr1); + } + + void load16(RegisterID src, RegisterID dest) + { + m_assembler.movwMemReg(src, dest); + extuw(dest, dest); + } + + void load16Signed(RegisterID src, RegisterID dest) + { + m_assembler.movwMemReg(src, dest); + } + + void load16(RegisterID r0, RegisterID src, RegisterID dest) + { + ASSERT(r0 == SH4Registers::r0); + m_assembler.movwR0mr(src, dest); + extuw(dest, dest); + } + + void load16Signed(RegisterID r0, RegisterID src, RegisterID dest) + { + ASSERT(r0 == SH4Registers::r0); + m_assembler.movwR0mr(src, dest); + } + + void load16(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + + if (address.offset) + add32(TrustedImm32(address.offset), scr); + if (address.base == SH4Registers::r0) + load16(address.base, scr, dest); + else { + add32(address.base, scr); + load16(scr, dest); + } + + releaseScratch(scr); + } + + void load16Signed(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + + if (address.offset) + add32(TrustedImm32(address.offset), scr); + if (address.base == SH4Registers::r0) + load16Signed(address.base, scr, dest); + else { + add32(address.base, scr); + load16Signed(scr, dest); + } + + releaseScratch(scr); + } + + void store8(RegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + + m_assembler.movbRegMem(src, scr); + + releaseScratch(scr); + } + + void store16(RegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + + m_assembler.movwRegMem(src, scr); + + releaseScratch(scr); + } + + void store32(RegisterID src, ImplicitAddress address) + { + RegisterID scr = claimScratch(); + store32(src, address.offset, address.base, scr); + releaseScratch(scr); + } + + void store32(RegisterID src, int offset, RegisterID base, RegisterID scr) + { + if (!offset) { + m_assembler.movlRegMem(src, base); + return; + } + + if ((offset >=0) && (offset < 64)) { + m_assembler.movlRegMem(src, offset >> 2, base); + return; + } + + m_assembler.loadConstant((offset), scr); + if (scr == SH4Registers::r0) { + m_assembler.movlRegMemr0(src, base); + return; + } + + m_assembler.addlRegReg(base, scr); + m_assembler.movlRegMem(src, scr); + } + + void store32(RegisterID src, RegisterID offset, RegisterID base) + { + ASSERT(offset == SH4Registers::r0); + m_assembler.movlRegMemr0(src, base); + } + + void store32(RegisterID src, RegisterID dst) + { + m_assembler.movlRegMem(src, dst); + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.loadConstant((imm.m_value), scr); + store32(scr, address.offset, address.base, scr1); + releaseScratch(scr); + releaseScratch(scr1); + } + + void store32(RegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + store32(src, Address(scr, address.offset)); + + releaseScratch(scr); + } + + void store32(TrustedImm32 imm, void* address) + { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + m_assembler.loadConstant((imm.m_value), scr); + m_assembler.loadConstant(reinterpret_cast(address), scr1); + m_assembler.movlRegMem(scr, scr1); + releaseScratch(scr); + releaseScratch(scr1); + } + + void store32(RegisterID src, void* address) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(reinterpret_cast(address), scr); + m_assembler.movlRegMem(src, scr); + releaseScratch(scr); + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + RegisterID scr = claimScratch(); + DataLabel32 label(this); + m_assembler.loadConstantUnReusable(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlMemReg(scr, dest); + releaseScratch(scr); + return label; + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + RegisterID scr = claimScratch(); + DataLabel32 label(this); + m_assembler.loadConstantUnReusable(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlRegMem(src, scr); + releaseScratch(scr); + return label; + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + DataLabelCompact dataLabel(this); + ASSERT(address.offset <= MaximumCompactPtrAlignedAddressOffset); + ASSERT(address.offset >= 0); + m_assembler.movlMemRegCompact(address.offset >> 2, address.base, dest); + return dataLabel; + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result(this); + + RegisterID scr = claimScratch(); + m_assembler.movImm8(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlMemReg(scr, dest); + releaseScratch(scr); + + return result; + } + + // Floating-point operations + + static bool supportsFloatingPoint() { return true; } + static bool supportsFloatingPointTruncate() { return true; } + static bool supportsFloatingPointSqrt() { return true; } + static bool supportsFloatingPointAbs() { return false; } + + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + m_assembler.fldsfpul((FPRegisterID)(src + 1)); + m_assembler.stsfpulReg(dest1); + m_assembler.fldsfpul(src); + m_assembler.stsfpulReg(dest2); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + UNUSED_PARAM(scratch); + m_assembler.ldsrmfpul(src1); + m_assembler.fstsfpul((FPRegisterID)(dest + 1)); + m_assembler.ldsrmfpul(src2); + m_assembler.fstsfpul(dest); + } + + void loadFloat(BaseIndex address, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.fmovsReadrm(scr, dest); + releaseScratch(scr); + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); + m_assembler.fmovsReadrm(scr, dest); + releaseScratch(scr); + } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + + m_assembler.loadConstant(address.offset, scr); + if (address.base == SH4Registers::r0) { + m_assembler.fmovsReadr0r(scr, (FPRegisterID)(dest + 1)); + m_assembler.addlImm8r(4, scr); + m_assembler.fmovsReadr0r(scr, dest); + releaseScratch(scr); + return; + } + + m_assembler.addlRegReg(address.base, scr); + m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); + m_assembler.fmovsReadrm(scr, dest); + releaseScratch(scr); + } + + void loadDouble(const void* address, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(reinterpret_cast(address), scr); + m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1)); + m_assembler.fmovsReadrm(scr, dest); + releaseScratch(scr); + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.fmovsWriterm(src, scr); + + releaseScratch(scr); + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.fmovsWriterm((FPRegisterID)(src + 1), scr); + m_assembler.addlImm8r(4, scr); + m_assembler.fmovsWriterm(src, scr); + releaseScratch(scr); + } + + void storeDouble(FPRegisterID src, BaseIndex address) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.fmovsWriterm((FPRegisterID)(src + 1), scr); + m_assembler.addlImm8r(4, scr); + m_assembler.fmovsWriterm(src, scr); + + releaseScratch(scr); + } + + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + if (op1 == dest) + m_assembler.daddRegReg(op2, dest); + else { + m_assembler.dmovRegReg(op1, dest); + m_assembler.daddRegReg(op2, dest); + } + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.daddRegReg(src, dest); + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + loadDouble(address.m_ptr, fscratch); + addDouble(fscratch, dest); + } + + void addDouble(Address address, FPRegisterID dest) + { + loadDouble(address, fscratch); + addDouble(fscratch, dest); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.dsubRegReg(src, dest); + } + + void subDouble(Address address, FPRegisterID dest) + { + loadDouble(address, fscratch); + subDouble(fscratch, dest); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.dmulRegReg(src, dest); + } + + void mulDouble(Address address, FPRegisterID dest) + { + loadDouble(address, fscratch); + mulDouble(fscratch, dest); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.ddivRegReg(src, dest); + } + + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.fldsfpul(src); + m_assembler.dcnvsd(dst); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + m_assembler.dcnvds(src); + m_assembler.fstsfpul(dst); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.ldsrmfpul(src); + m_assembler.floatfpulDreg(dest); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(reinterpret_cast(src.m_ptr), scr); + convertInt32ToDouble(scr, dest); + releaseScratch(scr); + } + + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + RegisterID scr = claimScratch(); + load32(src, scr); + convertInt32ToDouble(scr, dest); + releaseScratch(scr); + } + + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + RegisterID scr = claimScratch(); + RegisterID scr1 = claimScratch(); + Jump m_jump; + JumpList end; + + if (dest != SH4Registers::r0) + move(SH4Registers::r0, scr1); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + + if (address.offset) + add32(TrustedImm32(address.offset), scr); + + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 68, sizeof(uint32_t)); + move(scr, SH4Registers::r0); + m_assembler.andlImm8r(0x3, SH4Registers::r0); + m_assembler.cmpEqImmR0(0x0, SH4Registers::r0); + m_jump = Jump(m_assembler.jne(), SH4Assembler::JumpNear); + if (dest != SH4Registers::r0) + move(scr1, SH4Registers::r0); + + load32(scr, dest); + end.append(Jump(m_assembler.bra(), SH4Assembler::JumpNear)); + m_assembler.nop(); + m_jump.link(this); + m_assembler.andlImm8r(0x1, SH4Registers::r0); + m_assembler.cmpEqImmR0(0x0, SH4Registers::r0); + + if (dest != SH4Registers::r0) + move(scr1, SH4Registers::r0); + + m_jump = Jump(m_assembler.jne(), SH4Assembler::JumpNear); + load16(scr, scr1); + add32(TrustedImm32(2), scr); + load16(scr, dest); + m_assembler.shllImm8r(16, dest); + or32(scr1, dest); + end.append(Jump(m_assembler.bra(), SH4Assembler::JumpNear)); + m_assembler.nop(); + m_jump.link(this); + load8(scr, scr1); + add32(TrustedImm32(1), scr); + load16(scr, dest); + m_assembler.shllImm8r(8, dest); + or32(dest, scr1); + add32(TrustedImm32(2), scr); + load8(scr, dest); + m_assembler.shllImm8r(8, dest); + m_assembler.shllImm8r(16, dest); + or32(scr1, dest); + end.link(this); + + releaseScratch(scr); + releaseScratch(scr1); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + RegisterID scr = scratchReg3; + load32WithUnalignedHalfWords(left, scr); + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testlRegReg(scr, scr); + else + compare32(right.m_value, scr, cond); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) + { + m_assembler.movImm8(0, scratchReg3); + convertInt32ToDouble(scratchReg3, scratch); + return branchDouble(DoubleNotEqual, reg, scratch); + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) + { + m_assembler.movImm8(0, scratchReg3); + convertInt32ToDouble(scratchReg3, scratch); + return branchDouble(DoubleEqualOrUnordered, reg, scratch); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + if (cond == DoubleEqual) { + m_assembler.dcmppeq(right, left); + return branchTrue(); + } + + if (cond == DoubleNotEqual) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppeq(right, left); + releaseScratch(scr); + Jump m_jump = branchFalse(); + end.link(this); + return m_jump; + } + + if (cond == DoubleGreaterThan) { + m_assembler.dcmppgt(right, left); + return branchTrue(); + } + + if (cond == DoubleGreaterThanOrEqual) { + m_assembler.dcmppgt(left, right); + return branchFalse(); + } + + if (cond == DoubleLessThan) { + m_assembler.dcmppgt(left, right); + return branchTrue(); + } + + if (cond == DoubleLessThanOrEqual) { + m_assembler.dcmppgt(right, left); + return branchFalse(); + } + + if (cond == DoubleEqualOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppeq(left, right); + Jump m_jump = Jump(m_assembler.je()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + if (cond == DoubleGreaterThanOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppgt(right, left); + Jump m_jump = Jump(m_assembler.je()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + if (cond == DoubleGreaterThanOrEqualOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppgt(left, right); + Jump m_jump = Jump(m_assembler.jne()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + if (cond == DoubleLessThanOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppgt(left, right); + Jump m_jump = Jump(m_assembler.je()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + if (cond == DoubleLessThanOrEqualOrUnordered) { + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppgt(right, left); + Jump m_jump = Jump(m_assembler.jne()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + ASSERT(cond == DoubleNotEqualOrUnordered); + RegisterID scr = claimScratch(); + JumpList end; + m_assembler.loadConstant(0x7fbfffff, scratchReg3); + m_assembler.dcnvds(right); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcnvds(left); + m_assembler.stsfpulReg(scr); + m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal)); + end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear)); + m_assembler.dcmppeq(right, left); + Jump m_jump = Jump(m_assembler.jne()); + end.link(this); + m_assembler.extraInstrForBranch(scr); + releaseScratch(scr); + return m_jump; + } + + Jump branchTrue() + { + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 6, sizeof(uint32_t)); + Jump m_jump = Jump(m_assembler.je()); + m_assembler.extraInstrForBranch(scratchReg3); + return m_jump; + } + + Jump branchFalse() + { + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 6, sizeof(uint32_t)); + Jump m_jump = Jump(m_assembler.jne()); + m_assembler.extraInstrForBranch(scratchReg3); + return m_jump; + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + RegisterID scr = claimScratch(); + move(left.index, scr); + lshift32(TrustedImm32(left.scale), scr); + add32(left.base, scr); + load32(scr, left.offset, scr); + compare32(right.m_value, scr, cond); + releaseScratch(scr); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dest) + { + if (dest != src) + m_assembler.dmovRegReg(src, dest); + m_assembler.dsqrt(dest); + } + + void absDouble(FPRegisterID, FPRegisterID) + { + ASSERT_NOT_REACHED(); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + RegisterID addressTempRegister = claimScratch(); + load8(address, addressTempRegister); + Jump jmp = branchTest32(cond, addressTempRegister, mask); + releaseScratch(addressTempRegister); + return jmp; + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + RegisterID addressTempRegister = claimScratch(); + move(TrustedImmPtr(address.m_ptr), addressTempRegister); + load8(Address(addressTempRegister), addressTempRegister); + Jump jmp = branchTest32(cond, addressTempRegister, mask); + releaseScratch(addressTempRegister); + return jmp; + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + if (src != dest) + move(src, dest); + } + + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + RegisterID addressTempRegister = claimScratch(); + load8(left, addressTempRegister); + Jump jmp = branch32(cond, addressTempRegister, right); + releaseScratch(addressTempRegister); + return jmp; + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + RegisterID addressTempRegister = claimScratch(); + load8(left, addressTempRegister); + compare32(cond, addressTempRegister, right, dest); + releaseScratch(addressTempRegister); + } + + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + m_assembler.ftrcdrmfpul(src); + m_assembler.stsfpulReg(dest); + m_assembler.loadConstant(0x7fffffff, scratchReg3); + m_assembler.cmplRegReg(dest, scratchReg3, SH4Condition(Equal)); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 14, sizeof(uint32_t)); + m_assembler.branch(BT_OPCODE, 2); + m_assembler.addlImm8r(1, scratchReg3); + m_assembler.cmplRegReg(dest, scratchReg3, SH4Condition(Equal)); + return branchTrue(); + } + + // Stack manipulation operations + + void pop(RegisterID dest) + { + m_assembler.popReg(dest); + } + + void push(RegisterID src) + { + m_assembler.pushReg(src); + } + + void push(Address address) + { + if (!address.offset) { + push(address.base); + return; + } + + if ((address.offset < 0) || (address.offset >= 64)) { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlMemReg(scr, SH4Registers::sp); + m_assembler.addlImm8r(-4, SH4Registers::sp); + releaseScratch(scr); + return; + } + + m_assembler.movlMemReg(address.offset >> 2, address.base, SH4Registers::sp); + m_assembler.addlImm8r(-4, SH4Registers::sp); + } + + void push(TrustedImm32 imm) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(imm.m_value, scr); + push(scr); + releaseScratch(scr); + } + + // Register move operations + + void move(TrustedImm32 imm, RegisterID dest) + { + m_assembler.loadConstant(imm.m_value, dest); + } + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + m_assembler.ensureSpace(m_assembler.maxInstructionSize, sizeof(uint32_t)); + DataLabelPtr dataLabel(this); + m_assembler.loadConstantUnReusable(reinterpret_cast(initialValue.m_value), dest); + return dataLabel; + } + + void move(RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.movlRegReg(src, dest); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + m_assembler.loadConstant(imm.asIntptr(), dest); + } + + void extuw(RegisterID src, RegisterID dst) + { + m_assembler.extuw(src, dst); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmplRegReg(right, left, SH4Condition(cond)); + if (cond != NotEqual) { + m_assembler.movt(dest); + return; + } + + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 4); + m_assembler.movImm8(0, dest); + m_assembler.branch(BT_OPCODE, 0); + m_assembler.movImm8(1, dest); + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + if (left != dest) { + move(right, dest); + compare32(cond, left, dest, dest); + return; + } + + RegisterID scr = claimScratch(); + move(right, scr); + compare32(cond, left, scr, dest); + releaseScratch(scr); + } + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + ASSERT((cond == Zero) || (cond == NonZero)); + + load8(address, dest); + if (mask.m_value == -1) + compare32(0, dest, static_cast(cond)); + else + testlImm(mask.m_value, dest); + if (cond != NonZero) { + m_assembler.movt(dest); + return; + } + + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 4); + m_assembler.movImm8(0, dest); + m_assembler.branch(BT_OPCODE, 0); + m_assembler.movImm8(1, dest); + } + + void loadPtrLinkReg(ImplicitAddress address) + { + RegisterID scr = claimScratch(); + load32(address, scr); + m_assembler.ldspr(scr); + releaseScratch(scr); + } + + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) + { + m_assembler.cmplRegReg(right, left, SH4Condition(cond)); + /* BT label => BF off + nop LDR reg + nop braf @reg + nop nop + */ + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testlRegReg(left, left); + else + compare32(right.m_value, left, cond); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + compare32(right.offset, right.base, left, cond); + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + compare32(right, left.offset, left.base, cond); + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + compare32(right.m_value, left.offset, left.base, cond); + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + RegisterID scr = claimScratch(); + + move(TrustedImm32(reinterpret_cast(left.m_ptr)), scr); + m_assembler.cmplRegReg(right, scr, SH4Condition(cond)); + releaseScratch(scr); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + RegisterID addressTempRegister = claimScratch(); + + m_assembler.loadConstant(reinterpret_cast(left.m_ptr), addressTempRegister); + m_assembler.movlMemReg(addressTempRegister, addressTempRegister); + compare32(right.m_value, addressTempRegister, cond); + releaseScratch(addressTempRegister); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(right.m_value & 0xFFFFFF00)); + RegisterID scr = claimScratch(); + + move(left.index, scr); + lshift32(TrustedImm32(left.scale), scr); + + if (left.offset) + add32(TrustedImm32(left.offset), scr); + add32(left.base, scr); + load8(scr, scr); + RegisterID scr1 = claimScratch(); + m_assembler.loadConstant(right.m_value, scr1); + releaseScratch(scr); + releaseScratch(scr1); + + return branch32(cond, scr, scr1); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + ASSERT((cond == Zero) || (cond == NonZero)); + + m_assembler.testlRegReg(reg, mask); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + + if (mask.m_value == -1) + m_assembler.testlRegReg(reg, reg); + else + testlImm(mask.m_value, reg); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT((cond == Zero) || (cond == NonZero)); + + if (mask.m_value == -1) + compare32(0, address.offset, address.base, static_cast(cond)); + else + testImm(mask.m_value, address.offset, address.base); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + RegisterID scr = claimScratch(); + + move(address.index, scr); + lshift32(TrustedImm32(address.scale), scr); + add32(address.base, scr); + load32(scr, address.offset, scr); + + if (mask.m_value == -1) + m_assembler.testlRegReg(scr, scr); + else + testlImm(mask.m_value, scr); + + releaseScratch(scr); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump jump() + { + return Jump(m_assembler.jmp()); + } + + void jump(RegisterID target) + { + m_assembler.jmpReg(target); + } + + void jump(Address address) + { + RegisterID scr = claimScratch(); + + if ((address.offset < 0) || (address.offset >= 64)) { + m_assembler.loadConstant(address.offset, scr); + m_assembler.addlRegReg(address.base, scr); + m_assembler.movlMemReg(scr, scr); + } else if (address.offset) + m_assembler.movlMemReg(address.offset >> 2, address.base, scr); + else + m_assembler.movlMemReg(address.base, scr); + m_assembler.jmpReg(scr); + + releaseScratch(scr); + } + + // Arithmetic control flow operations + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (cond == Overflow) { + m_assembler.addvlRegReg(src, dest); + return branchTrue(); + } + + if (cond == Signed) { + m_assembler.addlRegReg(src, dest); + // Check if dest is negative + m_assembler.cmppz(dest); + return branchFalse(); + } + + m_assembler.addlRegReg(src, dest); + compare32(0, dest, Equal); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + move(imm, scratchReg3); + return branchAdd32(cond, scratchReg3, dest); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (src != dest) + move(src, dest); + + if (cond == Overflow) { + move(imm, scratchReg3); + m_assembler.addvlRegReg(scratchReg3, dest); + return branchTrue(); + } + + add32(imm, dest); + + if (cond == Signed) { + m_assembler.cmppz(dest); + return branchFalse(); + } + + compare32(0, dest, Equal); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (cond == Overflow) { + RegisterID scr1 = claimScratch(); + RegisterID scr = claimScratch(); + m_assembler.dmullRegReg(src, dest); + m_assembler.stsmacl(dest); + m_assembler.movImm8(-31, scr); + m_assembler.movlRegReg(dest, scr1); + m_assembler.shaRegReg(scr1, scr); + m_assembler.stsmach(scr); + m_assembler.cmplRegReg(scr, scr1, SH4Condition(Equal)); + releaseScratch(scr1); + releaseScratch(scr); + return branchFalse(); + } + + m_assembler.imullRegReg(src, dest); + m_assembler.stsmacl(dest); + if (cond == Signed) { + // Check if dest is negative + m_assembler.cmppz(dest); + return branchFalse(); + } + + compare32(0, dest, static_cast(cond)); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + move(imm, scratchReg3); + if (src != dest) + move(src, dest); + + return branchMul32(cond, scratchReg3, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (cond == Overflow) { + m_assembler.subvlRegReg(src, dest); + return branchTrue(); + } + + if (cond == Signed) { + // Check if dest is negative + m_assembler.sublRegReg(src, dest); + compare32(0, dest, LessThan); + return branchTrue(); + } + + sub32(src, dest); + compare32(0, dest, static_cast(cond)); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero)); + + move(imm, scratchReg3); + return branchSub32(cond, scratchReg3, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + move(imm, scratchReg3); + if (src != dest) + move(src, dest); + return branchSub32(cond, scratchReg3, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + if (src1 != dest) + move(src1, dest); + return branchSub32(cond, src2, dest); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); + + if (cond == Signed) { + or32(src, dest); + compare32(0, dest, static_cast(LessThan)); + return branchTrue(); + } + + or32(src, dest); + compare32(0, dest, static_cast(cond)); + + if (cond == NonZero) // NotEqual + return branchFalse(); + return branchTrue(); + } + + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + m_assembler.ftrcdrmfpul(src); + m_assembler.stsfpulReg(dest); + convertInt32ToDouble(dest, fscratch); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, fscratch, src)); + + if (dest == SH4Registers::r0) + m_assembler.cmpEqImmR0(0, dest); + else { + m_assembler.movImm8(0, scratchReg3); + m_assembler.cmplRegReg(scratchReg3, dest, SH4Condition(Equal)); + } + failureCases.append(branchTrue()); + } + + void neg32(RegisterID dst) + { + m_assembler.neg(dst, dst); + } + + void urshift32(RegisterID shiftamount, RegisterID dest) + { + if (shiftamount == SH4Registers::r0) + m_assembler.andlImm8r(0x1f, shiftamount); + else { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(0x1f, scr); + m_assembler.andlRegReg(scr, shiftamount); + releaseScratch(scr); + } + m_assembler.neg(shiftamount, shiftamount); + m_assembler.shllRegReg(dest, shiftamount); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + RegisterID scr = claimScratch(); + m_assembler.loadConstant(-(imm.m_value & 0x1f), scr); + m_assembler.shaRegReg(dest, scr); + releaseScratch(scr); + } + + void urshift32(RegisterID src, TrustedImm32 shiftamount, RegisterID dest) + { + if (src != dest) + move(src, dest); + + urshift32(shiftamount, dest); + } + + Call call() + { + return Call(m_assembler.call(), Call::Linkable); + } + + Call nearCall() + { + return Call(m_assembler.call(), Call::LinkableNear); + } + + Call call(RegisterID target) + { + return Call(m_assembler.call(target), Call::None); + } + + void call(Address address, RegisterID target) + { + load32(address.base, address.offset, target); + m_assembler.ensureSpace(m_assembler.maxInstructionSize + 2); + m_assembler.branch(JSR_OPCODE, target); + m_assembler.nop(); + } + + void breakpoint() + { + m_assembler.bkpt(); + m_assembler.nop(); + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + RegisterID dataTempRegister = claimScratch(); + + dataLabel = moveWithPatch(initialRightValue, dataTempRegister); + m_assembler.cmplRegReg(dataTempRegister, left, SH4Condition(cond)); + releaseScratch(dataTempRegister); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + RegisterID scr = claimScratch(); + + m_assembler.loadConstant(left.offset, scr); + m_assembler.addlRegReg(left.base, scr); + m_assembler.movlMemReg(scr, scr); + RegisterID scr1 = claimScratch(); + dataLabel = moveWithPatch(initialRightValue, scr1); + m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond)); + releaseScratch(scr); + releaseScratch(scr1); + + if (cond == NotEqual) + return branchFalse(); + return branchTrue(); + } + + void ret() + { + m_assembler.ret(); + m_assembler.nop(); + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + RegisterID scr = claimScratch(); + DataLabelPtr label = moveWithPatch(initialValue, scr); + store32(scr, address); + releaseScratch(scr); + return label; + } + + DataLabelPtr storePtrWithPatch(ImplicitAddress address) { return storePtrWithPatch(TrustedImmPtr(0), address); } + + int sizeOfConstantPool() + { + return m_assembler.sizeOfConstantPool(); + } + + Call tailRecursiveCall() + { + RegisterID scr = claimScratch(); + + m_assembler.loadConstantUnReusable(0x0, scr, true); + Jump m_jump = Jump(m_assembler.jmp(scr)); + releaseScratch(scr); + + return Call::fromTailJump(m_jump); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + oldJump.link(this); + return tailRecursiveCall(); + } + + void nop() + { + m_assembler.nop(); + } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(reinterpret_cast(SH4Assembler::readCallTarget(call.dataLocation()))); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + ASSERT_NOT_REACHED(); + } + + static ptrdiff_t maxJumpReplacementSize() + { + ASSERT_NOT_REACHED(); + return 0; + } + + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + return label.labelAtOffset(0); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) + { + SH4Assembler::revertJump(instructionStart.dataLocation(), reinterpret_cast(initialValue) & 0xffff); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + UNREACHABLE_FOR_PLATFORM(); + } + +protected: + SH4Assembler::Condition SH4Condition(RelationalCondition cond) + { + return static_cast(cond); + } + + SH4Assembler::Condition SH4Condition(ResultCondition cond) + { + return static_cast(cond); + } +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void*, Call, FunctionPtr); + static void repatchCall(CodeLocationCall, CodeLocationLabel); + static void repatchCall(CodeLocationCall, FunctionPtr); +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerSH4_h diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86.h b/src/3rdparty/masm/assembler/MacroAssemblerX86.h new file mode 100644 index 0000000000..27a030edfd --- /dev/null +++ b/src/3rdparty/masm/assembler/MacroAssemblerX86.h @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerX86_h +#define MacroAssemblerX86_h + +#if ENABLE(ASSEMBLER) && CPU(X86) + +#include "MacroAssemblerX86Common.h" + +namespace JSC { + +class MacroAssemblerX86 : public MacroAssemblerX86Common { +public: + static const Scale ScalePtr = TimesFour; + + using MacroAssemblerX86Common::add32; + using MacroAssemblerX86Common::and32; + using MacroAssemblerX86Common::branchAdd32; + using MacroAssemblerX86Common::branchSub32; + using MacroAssemblerX86Common::sub32; + using MacroAssemblerX86Common::or32; + using MacroAssemblerX86Common::load32; + using MacroAssemblerX86Common::store32; + using MacroAssemblerX86Common::store8; + using MacroAssemblerX86Common::branch32; + using MacroAssemblerX86Common::call; + using MacroAssemblerX86Common::jump; + using MacroAssemblerX86Common::addDouble; + using MacroAssemblerX86Common::loadDouble; + using MacroAssemblerX86Common::storeDouble; + using MacroAssemblerX86Common::convertInt32ToDouble; + using MacroAssemblerX86Common::branchTest8; + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.leal_mr(imm.m_value, src, dest); + } + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.addl_im(imm.m_value, address.m_ptr); + } + + void add32(AbsoluteAddress address, RegisterID dest) + { + m_assembler.addl_mr(address.m_ptr, dest); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.addl_im(imm.m_value, address.m_ptr); + m_assembler.adcl_im(imm.m_value >> 31, reinterpret_cast(address.m_ptr) + sizeof(int32_t)); + } + + void and32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.andl_im(imm.m_value, address.m_ptr); + } + + void or32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.orl_im(imm.m_value, address.m_ptr); + } + + void or32(RegisterID reg, AbsoluteAddress address) + { + m_assembler.orl_rm(reg, address.m_ptr); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + m_assembler.subl_im(imm.m_value, address.m_ptr); + } + + void load32(const void* address, RegisterID dest) + { + m_assembler.movl_mr(address, dest); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result = ConvertibleLoadLabel(this); + m_assembler.movl_mr(address.offset, address.base, dest); + return result; + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + m_assembler.addsd_mr(address.m_ptr, dest); + } + + void storeDouble(FPRegisterID src, const void* address) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_rm(src, address); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + m_assembler.cvtsi2sd_mr(src.m_ptr, dest); + } + + void store32(TrustedImm32 imm, void* address) + { + m_assembler.movl_i32m(imm.m_value, address); + } + + void store32(RegisterID src, void* address) + { + m_assembler.movl_rm(src, address); + } + + void store8(TrustedImm32 imm, void* address) + { + ASSERT(-128 <= imm.m_value && imm.m_value < 128); + m_assembler.movb_i8m(imm.m_value, address); + } + + // Possibly clobbers src. + void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2) + { + movePackedToInt32(src, dest1); + rshiftPacked(TrustedImm32(32), src); + movePackedToInt32(src, dest2); + } + + void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch) + { + moveInt32ToPacked(src1, dest); + moveInt32ToPacked(src2, scratch); + lshiftPacked(TrustedImm32(32), scratch); + orPacked(scratch, dest); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) + { + m_assembler.addl_im(imm.m_value, dest.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) + { + m_assembler.subl_im(imm.m_value, dest.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + m_assembler.cmpl_rm(right, left.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right) + { + m_assembler.cmpl_im(right.m_value, left.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Call call() + { + return Call(m_assembler.call(), Call::Linkable); + } + + // Address is a memory location containing the address to jump to + void jump(AbsoluteAddress address) + { + m_assembler.jmp_m(address.m_ptr); + } + + Call tailRecursiveCall() + { + return Call::fromTailJump(jump()); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + return Call::fromTailJump(oldJump); + } + + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + padBeforePatch(); + m_assembler.movl_i32r(initialValue.asIntptr(), dest); + return DataLabelPtr(this); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + ASSERT(mask.m_value >= -128 && mask.m_value <= 255); + if (mask.m_value == -1) + m_assembler.cmpb_im(0, address.m_ptr); + else + m_assembler.testb_im(mask.m_value, address.m_ptr); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + padBeforePatch(); + m_assembler.cmpl_ir_force32(initialRightValue.asIntptr(), left); + dataLabel = DataLabelPtr(this); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + padBeforePatch(); + m_assembler.cmpl_im_force32(initialRightValue.asIntptr(), left.offset, left.base); + dataLabel = DataLabelPtr(this); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + padBeforePatch(); + m_assembler.movl_i32m(initialValue.asIntptr(), address.offset, address.base); + return DataLabelPtr(this); + } + + static bool supportsFloatingPoint() { return isSSE2Present(); } + // See comment on MacroAssemblerARMv7::supportsFloatingPointTruncate() + static bool supportsFloatingPointTruncate() { return isSSE2Present(); } + static bool supportsFloatingPointSqrt() { return isSSE2Present(); } + static bool supportsFloatingPointAbs() { return isSSE2Present(); } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + intptr_t offset = reinterpret_cast(call.dataLocation())[-1]; + return FunctionPtr(reinterpret_cast(reinterpret_cast(call.dataLocation()) + offset)); + } + + static bool canJumpReplacePatchableBranchPtrWithPatch() { return true; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + const int opcodeBytes = 1; + const int modRMBytes = 1; + const int immediateBytes = 4; + const int totalBytes = opcodeBytes + modRMBytes + immediateBytes; + ASSERT(totalBytes >= maxJumpReplacementSize()); + return label.labelAtOffset(-totalBytes); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) + { + const int opcodeBytes = 1; + const int modRMBytes = 1; + const int offsetBytes = 0; + const int immediateBytes = 4; + const int totalBytes = opcodeBytes + modRMBytes + offsetBytes + immediateBytes; + ASSERT(totalBytes >= maxJumpReplacementSize()); + return label.labelAtOffset(-totalBytes); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID reg, void* initialValue) + { + X86Assembler::revertJumpTo_cmpl_ir_force32(instructionStart.executableAddress(), reinterpret_cast(initialValue), reg); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address address, void* initialValue) + { + ASSERT(!address.offset); + X86Assembler::revertJumpTo_cmpl_im_force32(instructionStart.executableAddress(), reinterpret_cast(initialValue), 0, address.base); + } + +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + X86Assembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + X86Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + X86Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); + } +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerX86_h diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86Common.h b/src/3rdparty/masm/assembler/MacroAssemblerX86Common.h new file mode 100644 index 0000000000..53cb80c210 --- /dev/null +++ b/src/3rdparty/masm/assembler/MacroAssemblerX86Common.h @@ -0,0 +1,1541 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerX86Common_h +#define MacroAssemblerX86Common_h + +#if ENABLE(ASSEMBLER) + +#include "X86Assembler.h" +#include "AbstractMacroAssembler.h" + +namespace JSC { + +class MacroAssemblerX86Common : public AbstractMacroAssembler { +protected: +#if CPU(X86_64) + static const X86Registers::RegisterID scratchRegister = X86Registers::r11; +#endif + + static const int DoubleConditionBitInvert = 0x10; + static const int DoubleConditionBitSpecial = 0x20; + static const int DoubleConditionBits = DoubleConditionBitInvert | DoubleConditionBitSpecial; + +public: + typedef X86Assembler::FPRegisterID FPRegisterID; + typedef X86Assembler::XMMRegisterID XMMRegisterID; + + static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value) + { + return value >= -128 && value <= 127; + } + + enum RelationalCondition { + Equal = X86Assembler::ConditionE, + NotEqual = X86Assembler::ConditionNE, + Above = X86Assembler::ConditionA, + AboveOrEqual = X86Assembler::ConditionAE, + Below = X86Assembler::ConditionB, + BelowOrEqual = X86Assembler::ConditionBE, + GreaterThan = X86Assembler::ConditionG, + GreaterThanOrEqual = X86Assembler::ConditionGE, + LessThan = X86Assembler::ConditionL, + LessThanOrEqual = X86Assembler::ConditionLE + }; + + enum ResultCondition { + Overflow = X86Assembler::ConditionO, + Signed = X86Assembler::ConditionS, + Zero = X86Assembler::ConditionE, + NonZero = X86Assembler::ConditionNE + }; + + enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleEqual = X86Assembler::ConditionE | DoubleConditionBitSpecial, + DoubleNotEqual = X86Assembler::ConditionNE, + DoubleGreaterThan = X86Assembler::ConditionA, + DoubleGreaterThanOrEqual = X86Assembler::ConditionAE, + DoubleLessThan = X86Assembler::ConditionA | DoubleConditionBitInvert, + DoubleLessThanOrEqual = X86Assembler::ConditionAE | DoubleConditionBitInvert, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = X86Assembler::ConditionE, + DoubleNotEqualOrUnordered = X86Assembler::ConditionNE | DoubleConditionBitSpecial, + DoubleGreaterThanOrUnordered = X86Assembler::ConditionB | DoubleConditionBitInvert, + DoubleGreaterThanOrEqualOrUnordered = X86Assembler::ConditionBE | DoubleConditionBitInvert, + DoubleLessThanOrUnordered = X86Assembler::ConditionB, + DoubleLessThanOrEqualOrUnordered = X86Assembler::ConditionBE, + }; + COMPILE_ASSERT( + !((X86Assembler::ConditionE | X86Assembler::ConditionNE | X86Assembler::ConditionA | X86Assembler::ConditionAE | X86Assembler::ConditionB | X86Assembler::ConditionBE) & DoubleConditionBits), + DoubleConditionBits_should_not_interfere_with_X86Assembler_Condition_codes); + + static const RegisterID stackPointerRegister = X86Registers::esp; + +#if ENABLE(JIT_CONSTANT_BLINDING) + static bool shouldBlindForSpecificArch(uint32_t value) { return value >= 0x00ffffff; } +#if CPU(X86_64) + static bool shouldBlindForSpecificArch(uint64_t value) { return value >= 0x00ffffff; } +#if OS(DARWIN) // On 64-bit systems other than DARWIN uint64_t and uintptr_t are the same type so overload is prohibited. + static bool shouldBlindForSpecificArch(uintptr_t value) { return value >= 0x00ffffff; } +#endif +#endif +#endif + + // Integer arithmetic operations: + // + // Operations are typically two operand - operation(source, srcDst) + // For many operations the source may be an TrustedImm32, the srcDst operand + // may often be a memory location (explictly described using an Address + // object). + + void add32(RegisterID src, RegisterID dest) + { + m_assembler.addl_rr(src, dest); + } + + void add32(TrustedImm32 imm, Address address) + { + m_assembler.addl_im(imm.m_value, address.offset, address.base); + } + + void add32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.addl_ir(imm.m_value, dest); + } + + void add32(Address src, RegisterID dest) + { + m_assembler.addl_mr(src.offset, src.base, dest); + } + + void add32(RegisterID src, Address dest) + { + m_assembler.addl_rm(src, dest.offset, dest.base); + } + + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.leal_mr(imm.m_value, src, dest); + } + + void and32(RegisterID src, RegisterID dest) + { + m_assembler.andl_rr(src, dest); + } + + void and32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.andl_ir(imm.m_value, dest); + } + + void and32(RegisterID src, Address dest) + { + m_assembler.andl_rm(src, dest.offset, dest.base); + } + + void and32(Address src, RegisterID dest) + { + m_assembler.andl_mr(src.offset, src.base, dest); + } + + void and32(TrustedImm32 imm, Address address) + { + m_assembler.andl_im(imm.m_value, address.offset, address.base); + } + + void and32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + zeroExtend32ToPtr(op1, dest); + else if (op1 == dest) + and32(op2, dest); + else { + move(op2, dest); + and32(op1, dest); + } + } + + void and32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(src, dest); + and32(imm, dest); + } + + void lshift32(RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (shift_amount == X86Registers::ecx) + m_assembler.shll_CLr(dest); + else { + // On x86 we can only shift by ecx; if asked to shift by another register we'll + // need rejig the shift amount into ecx first, and restore the registers afterwards. + // If we dest is ecx, then shift the swapped register! + swap(shift_amount, X86Registers::ecx); + m_assembler.shll_CLr(dest == X86Registers::ecx ? shift_amount : dest); + swap(shift_amount, X86Registers::ecx); + } + } + + void lshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (src != dest) + move(src, dest); + lshift32(shift_amount, dest); + } + + void lshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.shll_i8r(imm.m_value, dest); + } + + void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (src != dest) + move(src, dest); + lshift32(imm, dest); + } + + void mul32(RegisterID src, RegisterID dest) + { + m_assembler.imull_rr(src, dest); + } + + void mul32(Address src, RegisterID dest) + { + m_assembler.imull_mr(src.offset, src.base, dest); + } + + void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.imull_i32r(src, imm.m_value, dest); + } + + void neg32(RegisterID srcDest) + { + m_assembler.negl_r(srcDest); + } + + void neg32(Address srcDest) + { + m_assembler.negl_m(srcDest.offset, srcDest.base); + } + + void or32(RegisterID src, RegisterID dest) + { + m_assembler.orl_rr(src, dest); + } + + void or32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.orl_ir(imm.m_value, dest); + } + + void or32(RegisterID src, Address dest) + { + m_assembler.orl_rm(src, dest.offset, dest.base); + } + + void or32(Address src, RegisterID dest) + { + m_assembler.orl_mr(src.offset, src.base, dest); + } + + void or32(TrustedImm32 imm, Address address) + { + m_assembler.orl_im(imm.m_value, address.offset, address.base); + } + + void or32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + zeroExtend32ToPtr(op1, dest); + else if (op1 == dest) + or32(op2, dest); + else { + move(op2, dest); + or32(op1, dest); + } + } + + void or32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(src, dest); + or32(imm, dest); + } + + void rshift32(RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (shift_amount == X86Registers::ecx) + m_assembler.sarl_CLr(dest); + else { + // On x86 we can only shift by ecx; if asked to shift by another register we'll + // need rejig the shift amount into ecx first, and restore the registers afterwards. + // If we dest is ecx, then shift the swapped register! + swap(shift_amount, X86Registers::ecx); + m_assembler.sarl_CLr(dest == X86Registers::ecx ? shift_amount : dest); + swap(shift_amount, X86Registers::ecx); + } + } + + void rshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (src != dest) + move(src, dest); + rshift32(shift_amount, dest); + } + + void rshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.sarl_i8r(imm.m_value, dest); + } + + void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (src != dest) + move(src, dest); + rshift32(imm, dest); + } + + void urshift32(RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (shift_amount == X86Registers::ecx) + m_assembler.shrl_CLr(dest); + else { + // On x86 we can only shift by ecx; if asked to shift by another register we'll + // need rejig the shift amount into ecx first, and restore the registers afterwards. + // If we dest is ecx, then shift the swapped register! + swap(shift_amount, X86Registers::ecx); + m_assembler.shrl_CLr(dest == X86Registers::ecx ? shift_amount : dest); + swap(shift_amount, X86Registers::ecx); + } + } + + void urshift32(RegisterID src, RegisterID shift_amount, RegisterID dest) + { + ASSERT(shift_amount != dest); + + if (src != dest) + move(src, dest); + urshift32(shift_amount, dest); + } + + void urshift32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.shrl_i8r(imm.m_value, dest); + } + + void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest) + { + if (src != dest) + move(src, dest); + urshift32(imm, dest); + } + + void sub32(RegisterID src, RegisterID dest) + { + m_assembler.subl_rr(src, dest); + } + + void sub32(TrustedImm32 imm, RegisterID dest) + { + m_assembler.subl_ir(imm.m_value, dest); + } + + void sub32(TrustedImm32 imm, Address address) + { + m_assembler.subl_im(imm.m_value, address.offset, address.base); + } + + void sub32(Address src, RegisterID dest) + { + m_assembler.subl_mr(src.offset, src.base, dest); + } + + void sub32(RegisterID src, Address dest) + { + m_assembler.subl_rm(src, dest.offset, dest.base); + } + + void xor32(RegisterID src, RegisterID dest) + { + m_assembler.xorl_rr(src, dest); + } + + void xor32(TrustedImm32 imm, Address dest) + { + if (imm.m_value == -1) + m_assembler.notl_m(dest.offset, dest.base); + else + m_assembler.xorl_im(imm.m_value, dest.offset, dest.base); + } + + void xor32(TrustedImm32 imm, RegisterID dest) + { + if (imm.m_value == -1) + m_assembler.notl_r(dest); + else + m_assembler.xorl_ir(imm.m_value, dest); + } + + void xor32(RegisterID src, Address dest) + { + m_assembler.xorl_rm(src, dest.offset, dest.base); + } + + void xor32(Address src, RegisterID dest) + { + m_assembler.xorl_mr(src.offset, src.base, dest); + } + + void xor32(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + move(TrustedImm32(0), dest); + else if (op1 == dest) + xor32(op2, dest); + else { + move(op2, dest); + xor32(op1, dest); + } + } + + void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(src, dest); + xor32(imm, dest); + } + + void sqrtDouble(FPRegisterID src, FPRegisterID dst) + { + m_assembler.sqrtsd_rr(src, dst); + } + + void absDouble(FPRegisterID src, FPRegisterID dst) + { + ASSERT(src != dst); + static const double negativeZeroConstant = -0.0; + loadDouble(&negativeZeroConstant, dst); + m_assembler.andnpd_rr(src, dst); + } + + void negateDouble(FPRegisterID src, FPRegisterID dst) + { + ASSERT(src != dst); + static const double negativeZeroConstant = -0.0; + loadDouble(&negativeZeroConstant, dst); + m_assembler.xorpd_rr(src, dst); + } + + + // Memory access operations: + // + // Loads are of the form load(address, destination) and stores of the form + // store(source, address). The source for a store may be an TrustedImm32. Address + // operand objects to loads and store will be implicitly constructed if a + // register is passed. + + void load32(ImplicitAddress address, RegisterID dest) + { + m_assembler.movl_mr(address.offset, address.base, dest); + } + + void load32(BaseIndex address, RegisterID dest) + { + m_assembler.movl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest) + { + load32(address, dest); + } + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + load16(address, dest); + } + + DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movl_mr_disp32(address.offset, address.base, dest); + return DataLabel32(this); + } + + DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movl_mr_disp8(address.offset, address.base, dest); + return DataLabelCompact(this); + } + + static void repatchCompact(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) + { + ASSERT(isCompactPtrAlignedAddressOffset(value)); + AssemblerType_T::repatchCompact(dataLabelCompact.dataLocation(), value); + } + + DataLabelCompact loadCompactWithAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movl_mr_disp8(address.offset, address.base, dest); + return DataLabelCompact(this); + } + + void load8(BaseIndex address, RegisterID dest) + { + m_assembler.movzbl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load8(ImplicitAddress address, RegisterID dest) + { + m_assembler.movzbl_mr(address.offset, address.base, dest); + } + + void load8Signed(BaseIndex address, RegisterID dest) + { + m_assembler.movsbl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load8Signed(ImplicitAddress address, RegisterID dest) + { + m_assembler.movsbl_mr(address.offset, address.base, dest); + } + + void load16(BaseIndex address, RegisterID dest) + { + m_assembler.movzwl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load16(Address address, RegisterID dest) + { + m_assembler.movzwl_mr(address.offset, address.base, dest); + } + + void load16Signed(BaseIndex address, RegisterID dest) + { + m_assembler.movswl_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load16Signed(Address address, RegisterID dest) + { + m_assembler.movswl_mr(address.offset, address.base, dest); + } + + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) + { + padBeforePatch(); + m_assembler.movl_rm_disp32(src, address.offset, address.base); + return DataLabel32(this); + } + + void store32(RegisterID src, ImplicitAddress address) + { + m_assembler.movl_rm(src, address.offset, address.base); + } + + void store32(RegisterID src, BaseIndex address) + { + m_assembler.movl_rm(src, address.offset, address.base, address.index, address.scale); + } + + void store32(TrustedImm32 imm, ImplicitAddress address) + { + m_assembler.movl_i32m(imm.m_value, address.offset, address.base); + } + + void store32(TrustedImm32 imm, BaseIndex address) + { + m_assembler.movl_i32m(imm.m_value, address.offset, address.base, address.index, address.scale); + } + + void store8(TrustedImm32 imm, Address address) + { + ASSERT(-128 <= imm.m_value && imm.m_value < 128); + m_assembler.movb_i8m(imm.m_value, address.offset, address.base); + } + + void store8(TrustedImm32 imm, BaseIndex address) + { + ASSERT(-128 <= imm.m_value && imm.m_value < 128); + m_assembler.movb_i8m(imm.m_value, address.offset, address.base, address.index, address.scale); + } + + void store8(RegisterID src, BaseIndex address) + { +#if CPU(X86) + // On 32-bit x86 we can only store from the first 4 registers; + // esp..edi are mapped to the 'h' registers! + if (src >= 4) { + // Pick a temporary register. + RegisterID temp; + if (address.base != X86Registers::eax && address.index != X86Registers::eax) + temp = X86Registers::eax; + else if (address.base != X86Registers::ebx && address.index != X86Registers::ebx) + temp = X86Registers::ebx; + else { + ASSERT(address.base != X86Registers::ecx && address.index != X86Registers::ecx); + temp = X86Registers::ecx; + } + + // Swap to the temporary register to perform the store. + swap(src, temp); + m_assembler.movb_rm(temp, address.offset, address.base, address.index, address.scale); + swap(src, temp); + return; + } +#endif + m_assembler.movb_rm(src, address.offset, address.base, address.index, address.scale); + } + + void store16(RegisterID src, BaseIndex address) + { +#if CPU(X86) + // On 32-bit x86 we can only store from the first 4 registers; + // esp..edi are mapped to the 'h' registers! + if (src >= 4) { + // Pick a temporary register. + RegisterID temp; + if (address.base != X86Registers::eax && address.index != X86Registers::eax) + temp = X86Registers::eax; + else if (address.base != X86Registers::ebx && address.index != X86Registers::ebx) + temp = X86Registers::ebx; + else { + ASSERT(address.base != X86Registers::ecx && address.index != X86Registers::ecx); + temp = X86Registers::ecx; + } + + // Swap to the temporary register to perform the store. + swap(src, temp); + m_assembler.movw_rm(temp, address.offset, address.base, address.index, address.scale); + swap(src, temp); + return; + } +#endif + m_assembler.movw_rm(src, address.offset, address.base, address.index, address.scale); + } + + + // Floating-point operation: + // + // Presently only supports SSE, not x87 floating point. + + void moveDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (src != dest) + m_assembler.movsd_rr(src, dest); + } + + void loadDouble(const void* address, FPRegisterID dest) + { +#if CPU(X86) + ASSERT(isSSE2Present()); + m_assembler.movsd_mr(address, dest); +#else + move(TrustedImmPtr(address), scratchRegister); + loadDouble(scratchRegister, dest); +#endif + } + + void loadDouble(ImplicitAddress address, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_mr(address.offset, address.base, dest); + } + + void loadDouble(BaseIndex address, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_mr(address.offset, address.base, address.index, address.scale, dest); + } + void loadFloat(BaseIndex address, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.movss_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void storeDouble(FPRegisterID src, ImplicitAddress address) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_rm(src, address.offset, address.base); + } + + void storeDouble(FPRegisterID src, BaseIndex address) + { + ASSERT(isSSE2Present()); + m_assembler.movsd_rm(src, address.offset, address.base, address.index, address.scale); + } + + void storeFloat(FPRegisterID src, BaseIndex address) + { + ASSERT(isSSE2Present()); + m_assembler.movss_rm(src, address.offset, address.base, address.index, address.scale); + } + + void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.cvtsd2ss_rr(src, dst); + } + + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.cvtss2sd_rr(src, dst); + } + + void addDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.addsd_rr(src, dest); + } + + void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op1 == dest) + addDouble(op2, dest); + else { + moveDouble(op2, dest); + addDouble(op1, dest); + } + } + + void addDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.addsd_mr(src.offset, src.base, dest); + } + + void divDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.divsd_rr(src, dest); + } + + void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + // B := A / B is invalid. + ASSERT(op1 == dest || op2 != dest); + + moveDouble(op1, dest); + divDouble(op2, dest); + } + + void divDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.divsd_mr(src.offset, src.base, dest); + } + + void subDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.subsd_rr(src, dest); + } + + void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + // B := A - B is invalid. + ASSERT(op1 == dest || op2 != dest); + + moveDouble(op1, dest); + subDouble(op2, dest); + } + + void subDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.subsd_mr(src.offset, src.base, dest); + } + + void mulDouble(FPRegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.mulsd_rr(src, dest); + } + + void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + if (op1 == dest) + mulDouble(op2, dest); + else { + moveDouble(op2, dest); + mulDouble(op1, dest); + } + } + + void mulDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.mulsd_mr(src.offset, src.base, dest); + } + + void convertInt32ToDouble(RegisterID src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.cvtsi2sd_rr(src, dest); + } + + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.cvtsi2sd_mr(src.offset, src.base, dest); + } + + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) + { + ASSERT(isSSE2Present()); + + if (cond & DoubleConditionBitInvert) + m_assembler.ucomisd_rr(left, right); + else + m_assembler.ucomisd_rr(right, left); + + if (cond == DoubleEqual) { + if (left == right) + return Jump(m_assembler.jnp()); + Jump isUnordered(m_assembler.jp()); + Jump result = Jump(m_assembler.je()); + isUnordered.link(this); + return result; + } else if (cond == DoubleNotEqualOrUnordered) { + if (left == right) + return Jump(m_assembler.jp()); + Jump isUnordered(m_assembler.jp()); + Jump isEqual(m_assembler.je()); + isUnordered.link(this); + Jump result = jump(); + isEqual.link(this); + return result; + } + + ASSERT(!(cond & DoubleConditionBitSpecial)); + return Jump(m_assembler.jCC(static_cast(cond & ~DoubleConditionBits))); + } + + // Truncates 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, INT_MIN). + enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful }; + Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2si_rr(src, dest); + return branch32(branchType ? NotEqual : Equal, dest, TrustedImm32(0x80000000)); + } + + Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2si_rr(src, dest); + return branch32(branchType ? GreaterThanOrEqual : LessThan, dest, TrustedImm32(0)); + } + + void truncateDoubleToInt32(FPRegisterID src, RegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2si_rr(src, dest); + } + +#if CPU(X86_64) + void truncateDoubleToUint32(FPRegisterID src, RegisterID dest) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2siq_rr(src, dest); + } +#endif + + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2si_rr(src, dest); + + // If the result is zero, it might have been -0.0, and the double comparison won't catch this! + failureCases.append(branchTest32(Zero, dest)); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + convertInt32ToDouble(dest, fpTemp); + m_assembler.ucomisd_rr(fpTemp, src); + failureCases.append(m_assembler.jp()); + failureCases.append(m_assembler.jne()); + } + + Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch) + { + ASSERT(isSSE2Present()); + m_assembler.xorpd_rr(scratch, scratch); + return branchDouble(DoubleNotEqual, reg, scratch); + } + + Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch) + { + ASSERT(isSSE2Present()); + m_assembler.xorpd_rr(scratch, scratch); + return branchDouble(DoubleEqualOrUnordered, reg, scratch); + } + + void lshiftPacked(TrustedImm32 imm, XMMRegisterID reg) + { + ASSERT(isSSE2Present()); + m_assembler.psllq_i8r(imm.m_value, reg); + } + + void rshiftPacked(TrustedImm32 imm, XMMRegisterID reg) + { + ASSERT(isSSE2Present()); + m_assembler.psrlq_i8r(imm.m_value, reg); + } + + void orPacked(XMMRegisterID src, XMMRegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.por_rr(src, dst); + } + + void moveInt32ToPacked(RegisterID src, XMMRegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.movd_rr(src, dst); + } + + void movePackedToInt32(XMMRegisterID src, RegisterID dst) + { + ASSERT(isSSE2Present()); + m_assembler.movd_rr(src, dst); + } + + // Stack manipulation operations: + // + // The ABI is assumed to provide a stack abstraction to memory, + // containing machine word sized units of data. Push and pop + // operations add and remove a single register sized unit of data + // to or from the stack. Peek and poke operations read or write + // values on the stack, without moving the current stack position. + + void pop(RegisterID dest) + { + m_assembler.pop_r(dest); + } + + void push(RegisterID src) + { + m_assembler.push_r(src); + } + + void push(Address address) + { + m_assembler.push_m(address.offset, address.base); + } + + void push(TrustedImm32 imm) + { + m_assembler.push_i32(imm.m_value); + } + + + // Register move operations: + // + // Move values in registers. + + void move(TrustedImm32 imm, RegisterID dest) + { + // Note: on 64-bit the TrustedImm32 value is zero extended into the register, it + // may be useful to have a separate version that sign extends the value? + if (!imm.m_value) + m_assembler.xorl_rr(dest, dest); + else + m_assembler.movl_i32r(imm.m_value, dest); + } + +#if CPU(X86_64) + void move(RegisterID src, RegisterID dest) + { + // Note: on 64-bit this is is a full register move; perhaps it would be + // useful to have separate move32 & movePtr, with move32 zero extending? + if (src != dest) + m_assembler.movq_rr(src, dest); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + m_assembler.movq_i64r(imm.asIntptr(), dest); + } + + void move(TrustedImm64 imm, RegisterID dest) + { + m_assembler.movq_i64r(imm.m_value, dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + if (reg1 != reg2) + m_assembler.xchgq_rr(reg1, reg2); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + m_assembler.movsxd_rr(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + m_assembler.movl_rr(src, dest); + } +#else + void move(RegisterID src, RegisterID dest) + { + if (src != dest) + m_assembler.movl_rr(src, dest); + } + + void move(TrustedImmPtr imm, RegisterID dest) + { + m_assembler.movl_i32r(imm.asIntptr(), dest); + } + + void swap(RegisterID reg1, RegisterID reg2) + { + if (reg1 != reg2) + m_assembler.xchgl_rr(reg1, reg2); + } + + void signExtend32ToPtr(RegisterID src, RegisterID dest) + { + move(src, dest); + } + + void zeroExtend32ToPtr(RegisterID src, RegisterID dest) + { + move(src, dest); + } +#endif + + + // Forwards / external control flow operations: + // + // This set of jump and conditional branch operations return a Jump + // object which may linked at a later point, allow forwards jump, + // or jumps that will require external linkage (after the code has been + // relocated). + // + // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge + // respecitvely, for unsigned comparisons the names b, a, be, and ae are + // used (representing the names 'below' and 'above'). + // + // Operands to the comparision are provided in the expected order, e.g. + // jle32(reg1, TrustedImm32(5)) will branch if the value held in reg1, when + // treated as a signed 32bit value, is less than or equal to 5. + // + // jz and jnz test whether the first operand is equal to zero, and take + // an optional second operand of a mask under which to perform the test. + +public: + Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right) + { + m_assembler.cmpb_im(right.m_value, left.offset, left.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right) + { + m_assembler.cmpl_rr(right, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testl_rr(left, left); + else + m_assembler.cmpl_ir(right.m_value, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, RegisterID left, Address right) + { + m_assembler.cmpl_mr(right.offset, right.base, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, Address left, RegisterID right) + { + m_assembler.cmpl_rm(right, left.offset, left.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right) + { + m_assembler.cmpl_im(right.m_value, left.offset, left.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + m_assembler.cmpl_im(right.m_value, left.offset, left.base, left.index, left.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + return branch32(cond, left, right); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask) + { + m_assembler.testl_rr(reg, mask); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + // if we are only interested in the low seven bits, this can be tested with a testb + if (mask.m_value == -1) + m_assembler.testl_rr(reg, reg); + else + m_assembler.testl_i32r(mask.m_value, reg); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + if (mask.m_value == -1) + m_assembler.cmpl_im(0, address.offset, address.base); + else + m_assembler.testl_i32m(mask.m_value, address.offset, address.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + if (mask.m_value == -1) + m_assembler.cmpl_im(0, address.offset, address.base, address.index, address.scale); + else + m_assembler.testl_i32m(mask.m_value, address.offset, address.base, address.index, address.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + // Byte in TrustedImm32 is not well defined, so be a little permisive here, but don't accept nonsense values. + ASSERT(mask.m_value >= -128 && mask.m_value <= 255); + if (mask.m_value == -1) + m_assembler.cmpb_im(0, address.offset, address.base); + else + m_assembler.testb_im(mask.m_value, address.offset, address.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest8(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + // Byte in TrustedImm32 is not well defined, so be a little permisive here, but don't accept nonsense values. + ASSERT(mask.m_value >= -128 && mask.m_value <= 255); + if (mask.m_value == -1) + m_assembler.cmpb_im(0, address.offset, address.base, address.index, address.scale); + else + m_assembler.testb_im(mask.m_value, address.offset, address.base, address.index, address.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right) + { + ASSERT(!(right.m_value & 0xFFFFFF00)); + + m_assembler.cmpb_im(right.m_value, left.offset, left.base, left.index, left.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump jump() + { + return Jump(m_assembler.jmp()); + } + + void jump(RegisterID target) + { + m_assembler.jmp_r(target); + } + + // Address is a memory location containing the address to jump to + void jump(Address address) + { + m_assembler.jmp_m(address.offset, address.base); + } + + + // Arithmetic control flow operations: + // + // This set of conditional branch operations branch based + // on the result of an arithmetic operation. The operation + // is performed as normal, storing the result. + // + // * jz operations branch if the result is zero. + // * jo operations branch if the (signed) arithmetic + // operation caused an overflow to occur. + + Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest) + { + add32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + add32(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 src, Address dest) + { + add32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, Address dest) + { + add32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, Address src, RegisterID dest) + { + add32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + if (src1 == dest) + return branchAdd32(cond, src2, dest); + move(src2, dest); + return branchAdd32(cond, src1, dest); + } + + Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest) + { + move(src, dest); + return branchAdd32(cond, imm, dest); + } + + Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest) + { + mul32(src, dest); + if (cond != Overflow) + m_assembler.testl_rr(dest, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchMul32(ResultCondition cond, Address src, RegisterID dest) + { + mul32(src, dest); + if (cond != Overflow) + m_assembler.testl_rr(dest, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest) + { + mul32(imm, src, dest); + if (cond != Overflow) + m_assembler.testl_rr(dest, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + if (src1 == dest) + return branchMul32(cond, src2, dest); + move(src2, dest); + return branchMul32(cond, src1, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest) + { + sub32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + sub32(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, TrustedImm32 imm, Address dest) + { + sub32(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID src, Address dest) + { + sub32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, Address src, RegisterID dest) + { + sub32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) + { + // B := A - B is invalid. + ASSERT(src1 == dest || src2 != dest); + + move(src1, dest); + return branchSub32(cond, src2, dest); + } + + Jump branchSub32(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) + { + move(src1, dest); + return branchSub32(cond, src2, dest); + } + + Jump branchNeg32(ResultCondition cond, RegisterID srcDest) + { + neg32(srcDest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest) + { + or32(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + + // Miscellaneous operations: + + void breakpoint() + { + m_assembler.int3(); + } + + Call nearCall() + { + return Call(m_assembler.call(), Call::LinkableNear); + } + + Call call(RegisterID target) + { + return Call(m_assembler.call(target), Call::None); + } + + void call(Address address) + { + m_assembler.call_m(address.offset, address.base); + } + + void ret() + { + m_assembler.ret(); + } + + void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest) + { + m_assembler.cmpb_im(right.m_value, left.offset, left.base); + set32(x86Condition(cond), dest); + } + + void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmpl_rr(right, left); + set32(x86Condition(cond), dest); + } + + void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testl_rr(left, left); + else + m_assembler.cmpl_ir(right.m_value, left); + set32(x86Condition(cond), dest); + } + + // FIXME: + // The mask should be optional... perhaps the argument order should be + // dest-src, operations always have a dest? ... possibly not true, considering + // asm ops like test, or pseudo ops like pop(). + + void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + if (mask.m_value == -1) + m_assembler.cmpb_im(0, address.offset, address.base); + else + m_assembler.testb_im(mask.m_value, address.offset, address.base); + set32(x86Condition(cond), dest); + } + + void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest) + { + if (mask.m_value == -1) + m_assembler.cmpl_im(0, address.offset, address.base); + else + m_assembler.testl_i32m(mask.m_value, address.offset, address.base); + set32(x86Condition(cond), dest); + } + + // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc. + static RelationalCondition invert(RelationalCondition cond) + { + return static_cast(cond ^ 1); + } + + void nop() + { + m_assembler.nop(); + } + + static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + X86Assembler::replaceWithJump(instructionStart.executableAddress(), destination.executableAddress()); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return X86Assembler::maxJumpReplacementSize(); + } + +protected: + X86Assembler::Condition x86Condition(RelationalCondition cond) + { + return static_cast(cond); + } + + X86Assembler::Condition x86Condition(ResultCondition cond) + { + return static_cast(cond); + } + + void set32(X86Assembler::Condition cond, RegisterID dest) + { +#if CPU(X86) + // On 32-bit x86 we can only set the first 4 registers; + // esp..edi are mapped to the 'h' registers! + if (dest >= 4) { + m_assembler.xchgl_rr(dest, X86Registers::eax); + m_assembler.setCC_r(cond, X86Registers::eax); + m_assembler.movzbl_rr(X86Registers::eax, X86Registers::eax); + m_assembler.xchgl_rr(dest, X86Registers::eax); + return; + } +#endif + m_assembler.setCC_r(cond, dest); + m_assembler.movzbl_rr(dest, dest); + } + +private: + // Only MacroAssemblerX86 should be using the following method; SSE2 is always available on + // x86_64, and clients & subclasses of MacroAssembler should be using 'supportsFloatingPoint()'. + friend class MacroAssemblerX86; + +#if CPU(X86) +#if OS(MAC_OS_X) + + // All X86 Macs are guaranteed to support at least SSE2, + static bool isSSE2Present() + { + return true; + } + +#else // OS(MAC_OS_X) + + enum SSE2CheckState { + NotCheckedSSE2, + HasSSE2, + NoSSE2 + }; + + static bool isSSE2Present() + { + if (s_sse2CheckState == NotCheckedSSE2) { + // Default the flags value to zero; if the compiler is + // not MSVC or GCC we will read this as SSE2 not present. + int flags = 0; +#if COMPILER(MSVC) + _asm { + mov eax, 1 // cpuid function 1 gives us the standard feature set + cpuid; + mov flags, edx; + } +#elif COMPILER(GCC) + asm ( + "movl $0x1, %%eax;" + "pushl %%ebx;" + "cpuid;" + "popl %%ebx;" + "movl %%edx, %0;" + : "=g" (flags) + : + : "%eax", "%ecx", "%edx" + ); +#endif + static const int SSE2FeatureBit = 1 << 26; + s_sse2CheckState = (flags & SSE2FeatureBit) ? HasSSE2 : NoSSE2; + } + // Only check once. + ASSERT(s_sse2CheckState != NotCheckedSSE2); + + return s_sse2CheckState == HasSSE2; + } + + static SSE2CheckState s_sse2CheckState; + +#endif // OS(MAC_OS_X) +#elif !defined(NDEBUG) // CPU(X86) + + // On x86-64 we should never be checking for SSE2 in a non-debug build, + // but non debug add this method to keep the asserts above happy. + static bool isSSE2Present() + { + return true; + } + +#endif +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerX86Common_h diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h b/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h new file mode 100644 index 0000000000..c711e6f8da --- /dev/null +++ b/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h @@ -0,0 +1,643 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MacroAssemblerX86_64_h +#define MacroAssemblerX86_64_h + +#if ENABLE(ASSEMBLER) && CPU(X86_64) + +#include "MacroAssemblerX86Common.h" + +#define REPTACH_OFFSET_CALL_R11 3 + +namespace JSC { + +class MacroAssemblerX86_64 : public MacroAssemblerX86Common { +public: + static const Scale ScalePtr = TimesEight; + + using MacroAssemblerX86Common::add32; + using MacroAssemblerX86Common::and32; + using MacroAssemblerX86Common::branchAdd32; + using MacroAssemblerX86Common::or32; + using MacroAssemblerX86Common::sub32; + using MacroAssemblerX86Common::load32; + using MacroAssemblerX86Common::store32; + using MacroAssemblerX86Common::store8; + using MacroAssemblerX86Common::call; + using MacroAssemblerX86Common::jump; + using MacroAssemblerX86Common::addDouble; + using MacroAssemblerX86Common::loadDouble; + using MacroAssemblerX86Common::convertInt32ToDouble; + + void add32(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + add32(imm, Address(scratchRegister)); + } + + void and32(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + and32(imm, Address(scratchRegister)); + } + + void add32(AbsoluteAddress address, RegisterID dest) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + add32(Address(scratchRegister), dest); + } + + void or32(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + or32(imm, Address(scratchRegister)); + } + + void or32(RegisterID reg, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + or32(reg, Address(scratchRegister)); + } + + void sub32(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + sub32(imm, Address(scratchRegister)); + } + + void load32(const void* address, RegisterID dest) + { + if (dest == X86Registers::eax) + m_assembler.movl_mEAX(address); + else { + move(TrustedImmPtr(address), dest); + load32(dest, dest); + } + } + + void addDouble(AbsoluteAddress address, FPRegisterID dest) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + m_assembler.addsd_mr(0, scratchRegister, dest); + } + + void convertInt32ToDouble(TrustedImm32 imm, FPRegisterID dest) + { + move(imm, scratchRegister); + m_assembler.cvtsi2sd_rr(scratchRegister, dest); + } + + void store32(TrustedImm32 imm, void* address) + { + move(TrustedImmPtr(address), scratchRegister); + store32(imm, scratchRegister); + } + + void store8(TrustedImm32 imm, void* address) + { + move(TrustedImmPtr(address), scratchRegister); + store8(imm, Address(scratchRegister)); + } + + Call call() + { + DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); + Call result = Call(m_assembler.call(scratchRegister), Call::Linkable); + ASSERT_UNUSED(label, differenceBetween(label, result) == REPTACH_OFFSET_CALL_R11); + return result; + } + + // Address is a memory location containing the address to jump to + void jump(AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + jump(Address(scratchRegister)); + } + + Call tailRecursiveCall() + { + DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); + Jump newJump = Jump(m_assembler.jmp_r(scratchRegister)); + ASSERT_UNUSED(label, differenceBetween(label, newJump) == REPTACH_OFFSET_CALL_R11); + return Call::fromTailJump(newJump); + } + + Call makeTailRecursiveCall(Jump oldJump) + { + oldJump.link(this); + DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister); + Jump newJump = Jump(m_assembler.jmp_r(scratchRegister)); + ASSERT_UNUSED(label, differenceBetween(label, newJump) == REPTACH_OFFSET_CALL_R11); + return Call::fromTailJump(newJump); + } + + Jump branchAdd32(ResultCondition cond, TrustedImm32 src, AbsoluteAddress dest) + { + move(TrustedImmPtr(dest.m_ptr), scratchRegister); + add32(src, Address(scratchRegister)); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + void add64(RegisterID src, RegisterID dest) + { + m_assembler.addq_rr(src, dest); + } + + void add64(Address src, RegisterID dest) + { + m_assembler.addq_mr(src.offset, src.base, dest); + } + + void add64(AbsoluteAddress src, RegisterID dest) + { + move(TrustedImmPtr(src.m_ptr), scratchRegister); + add64(Address(scratchRegister), dest); + } + + void add64(TrustedImm32 imm, RegisterID srcDest) + { + m_assembler.addq_ir(imm.m_value, srcDest); + } + + void add64(TrustedImm64 imm, RegisterID dest) + { + move(imm, scratchRegister); + add64(scratchRegister, dest); + } + + void add64(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + m_assembler.leaq_mr(imm.m_value, src, dest); + } + + void add64(TrustedImm32 imm, Address address) + { + m_assembler.addq_im(imm.m_value, address.offset, address.base); + } + + void add64(TrustedImm32 imm, AbsoluteAddress address) + { + move(TrustedImmPtr(address.m_ptr), scratchRegister); + add64(imm, Address(scratchRegister)); + } + + void and64(RegisterID src, RegisterID dest) + { + m_assembler.andq_rr(src, dest); + } + + void and64(TrustedImm32 imm, RegisterID srcDest) + { + m_assembler.andq_ir(imm.m_value, srcDest); + } + + void neg64(RegisterID dest) + { + m_assembler.negq_r(dest); + } + + void or64(RegisterID src, RegisterID dest) + { + m_assembler.orq_rr(src, dest); + } + + void or64(TrustedImm64 imm, RegisterID dest) + { + move(imm, scratchRegister); + or64(scratchRegister, dest); + } + + void or64(TrustedImm32 imm, RegisterID dest) + { + m_assembler.orq_ir(imm.m_value, dest); + } + + void or64(RegisterID op1, RegisterID op2, RegisterID dest) + { + if (op1 == op2) + move(op1, dest); + else if (op1 == dest) + or64(op2, dest); + else { + move(op2, dest); + or64(op1, dest); + } + } + + void or64(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + move(src, dest); + or64(imm, dest); + } + + void rotateRight64(TrustedImm32 imm, RegisterID srcDst) + { + m_assembler.rorq_i8r(imm.m_value, srcDst); + } + + void sub64(RegisterID src, RegisterID dest) + { + m_assembler.subq_rr(src, dest); + } + + void sub64(TrustedImm32 imm, RegisterID dest) + { + m_assembler.subq_ir(imm.m_value, dest); + } + + void sub64(TrustedImm64 imm, RegisterID dest) + { + move(imm, scratchRegister); + sub64(scratchRegister, dest); + } + + void xor64(RegisterID src, RegisterID dest) + { + m_assembler.xorq_rr(src, dest); + } + + void xor64(RegisterID src, Address dest) + { + m_assembler.xorq_rm(src, dest.offset, dest.base); + } + + void xor64(TrustedImm32 imm, RegisterID srcDest) + { + m_assembler.xorq_ir(imm.m_value, srcDest); + } + + void load64(ImplicitAddress address, RegisterID dest) + { + m_assembler.movq_mr(address.offset, address.base, dest); + } + + void load64(BaseIndex address, RegisterID dest) + { + m_assembler.movq_mr(address.offset, address.base, address.index, address.scale, dest); + } + + void load64(const void* address, RegisterID dest) + { + if (dest == X86Registers::eax) + m_assembler.movq_mEAX(address); + else { + move(TrustedImmPtr(address), dest); + load64(dest, dest); + } + } + + DataLabel32 load64WithAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movq_mr_disp32(address.offset, address.base, dest); + return DataLabel32(this); + } + + DataLabelCompact load64WithCompactAddressOffsetPatch(Address address, RegisterID dest) + { + padBeforePatch(); + m_assembler.movq_mr_disp8(address.offset, address.base, dest); + return DataLabelCompact(this); + } + + void store64(RegisterID src, ImplicitAddress address) + { + m_assembler.movq_rm(src, address.offset, address.base); + } + + void store64(RegisterID src, BaseIndex address) + { + m_assembler.movq_rm(src, address.offset, address.base, address.index, address.scale); + } + + void store64(RegisterID src, void* address) + { + if (src == X86Registers::eax) + m_assembler.movq_EAXm(address); + else { + move(TrustedImmPtr(address), scratchRegister); + store64(src, scratchRegister); + } + } + + void store64(TrustedImm64 imm, ImplicitAddress address) + { + move(imm, scratchRegister); + store64(scratchRegister, address); + } + + void store64(TrustedImm64 imm, BaseIndex address) + { + move(imm, scratchRegister); + m_assembler.movq_rm(scratchRegister, address.offset, address.base, address.index, address.scale); + } + + DataLabel32 store64WithAddressOffsetPatch(RegisterID src, Address address) + { + padBeforePatch(); + m_assembler.movq_rm_disp32(src, address.offset, address.base); + return DataLabel32(this); + } + + void move64ToDouble(RegisterID src, FPRegisterID dest) + { + m_assembler.movq_rr(src, dest); + } + + void moveDoubleTo64(FPRegisterID src, RegisterID dest) + { + m_assembler.movq_rr(src, dest); + } + + void compare64(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) + m_assembler.testq_rr(left, left); + else + m_assembler.cmpq_ir(right.m_value, left); + m_assembler.setCC_r(x86Condition(cond), dest); + m_assembler.movzbl_rr(dest, dest); + } + + void compare64(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest) + { + m_assembler.cmpq_rr(right, left); + m_assembler.setCC_r(x86Condition(cond), dest); + m_assembler.movzbl_rr(dest, dest); + } + + Jump branch64(RelationalCondition cond, RegisterID left, RegisterID right) + { + m_assembler.cmpq_rr(right, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch64(RelationalCondition cond, RegisterID left, TrustedImm64 right) + { + if (((cond == Equal) || (cond == NotEqual)) && !right.m_value) { + m_assembler.testq_rr(left, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + move(right, scratchRegister); + return branch64(cond, left, scratchRegister); + } + + Jump branch64(RelationalCondition cond, RegisterID left, Address right) + { + m_assembler.cmpq_mr(right.offset, right.base, left); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch64(RelationalCondition cond, AbsoluteAddress left, RegisterID right) + { + move(TrustedImmPtr(left.m_ptr), scratchRegister); + return branch64(cond, Address(scratchRegister), right); + } + + Jump branch64(RelationalCondition cond, Address left, RegisterID right) + { + m_assembler.cmpq_rm(right, left.offset, left.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branch64(RelationalCondition cond, Address left, TrustedImm64 right) + { + move(right, scratchRegister); + return branch64(cond, left, scratchRegister); + } + + Jump branchTest64(ResultCondition cond, RegisterID reg, RegisterID mask) + { + m_assembler.testq_rr(reg, mask); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest64(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) + { + // if we are only interested in the low seven bits, this can be tested with a testb + if (mask.m_value == -1) + m_assembler.testq_rr(reg, reg); + else if ((mask.m_value & ~0x7f) == 0) + m_assembler.testb_i8r(mask.m_value, reg); + else + m_assembler.testq_i32r(mask.m_value, reg); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + void test64(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) + { + if (mask.m_value == -1) + m_assembler.testq_rr(reg, reg); + else if ((mask.m_value & ~0x7f) == 0) + m_assembler.testb_i8r(mask.m_value, reg); + else + m_assembler.testq_i32r(mask.m_value, reg); + set32(x86Condition(cond), dest); + } + + void test64(ResultCondition cond, RegisterID reg, RegisterID mask, RegisterID dest) + { + m_assembler.testq_rr(reg, mask); + set32(x86Condition(cond), dest); + } + + Jump branchTest64(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + load64(address.m_ptr, scratchRegister); + return branchTest64(cond, scratchRegister, mask); + } + + Jump branchTest64(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1)) + { + if (mask.m_value == -1) + m_assembler.cmpq_im(0, address.offset, address.base); + else + m_assembler.testq_i32m(mask.m_value, address.offset, address.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest64(ResultCondition cond, Address address, RegisterID reg) + { + m_assembler.testq_rm(reg, address.offset, address.base); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchTest64(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1)) + { + if (mask.m_value == -1) + m_assembler.cmpq_im(0, address.offset, address.base, address.index, address.scale); + else + m_assembler.testq_i32m(mask.m_value, address.offset, address.base, address.index, address.scale); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + + Jump branchAdd64(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + add64(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchAdd64(ResultCondition cond, RegisterID src, RegisterID dest) + { + add64(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub64(ResultCondition cond, TrustedImm32 imm, RegisterID dest) + { + sub64(imm, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub64(ResultCondition cond, RegisterID src, RegisterID dest) + { + sub64(src, dest); + return Jump(m_assembler.jCC(x86Condition(cond))); + } + + Jump branchSub64(ResultCondition cond, RegisterID src1, TrustedImm32 src2, RegisterID dest) + { + move(src1, dest); + return branchSub64(cond, src2, dest); + } + + ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) + { + ConvertibleLoadLabel result = ConvertibleLoadLabel(this); + m_assembler.movq_mr(address.offset, address.base, dest); + return result; + } + + DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest) + { + padBeforePatch(); + m_assembler.movq_i64r(initialValue.asIntptr(), dest); + return DataLabelPtr(this); + } + + Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + dataLabel = moveWithPatch(initialRightValue, scratchRegister); + return branch64(cond, left, scratchRegister); + } + + Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) + { + dataLabel = moveWithPatch(initialRightValue, scratchRegister); + return branch64(cond, left, scratchRegister); + } + + DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) + { + DataLabelPtr label = moveWithPatch(initialValue, scratchRegister); + store64(scratchRegister, address); + return label; + } + + using MacroAssemblerX86Common::branchTest8; + Jump branchTest8(ResultCondition cond, ExtendedAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + TrustedImmPtr addr(reinterpret_cast(address.offset)); + MacroAssemblerX86Common::move(addr, scratchRegister); + return MacroAssemblerX86Common::branchTest8(cond, BaseIndex(scratchRegister, address.base, TimesOne), mask); + } + + Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + MacroAssemblerX86Common::move(TrustedImmPtr(address.m_ptr), scratchRegister); + return MacroAssemblerX86Common::branchTest8(cond, Address(scratchRegister), mask); + } + + static bool supportsFloatingPoint() { return true; } + // See comment on MacroAssemblerARMv7::supportsFloatingPointTruncate() + static bool supportsFloatingPointTruncate() { return true; } + static bool supportsFloatingPointSqrt() { return true; } + static bool supportsFloatingPointAbs() { return true; } + + static FunctionPtr readCallTarget(CodeLocationCall call) + { + return FunctionPtr(X86Assembler::readPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation())); + } + + static RegisterID scratchRegisterForBlinding() { return scratchRegister; } + + static bool canJumpReplacePatchableBranchPtrWithPatch() { return true; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + const int rexBytes = 1; + const int opcodeBytes = 1; + const int immediateBytes = 8; + const int totalBytes = rexBytes + opcodeBytes + immediateBytes; + ASSERT(totalBytes >= maxJumpReplacementSize()); + return label.labelAtOffset(-totalBytes); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) + { + return startOfBranchPtrWithPatchOnRegister(label); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + X86Assembler::revertJumpTo_movq_i64r(instructionStart.executableAddress(), reinterpret_cast(initialValue), scratchRegister); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) + { + X86Assembler::revertJumpTo_movq_i64r(instructionStart.executableAddress(), reinterpret_cast(initialValue), scratchRegister); + } + +private: + friend class LinkBuffer; + friend class RepatchBuffer; + + static void linkCall(void* code, Call call, FunctionPtr function) + { + if (!call.isFlagSet(Call::Near)) + X86Assembler::linkPointer(code, call.m_label.labelAtOffset(-REPTACH_OFFSET_CALL_R11), function.value()); + else + X86Assembler::linkCall(code, call.m_label, function.value()); + } + + static void repatchCall(CodeLocationCall call, CodeLocationLabel destination) + { + X86Assembler::repatchPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation(), destination.executableAddress()); + } + + static void repatchCall(CodeLocationCall call, FunctionPtr destination) + { + X86Assembler::repatchPointer(call.dataLabelPtrAtOffset(-REPTACH_OFFSET_CALL_R11).dataLocation(), destination.executableAddress()); + } + +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // MacroAssemblerX86_64_h diff --git a/src/3rdparty/masm/assembler/RepatchBuffer.h b/src/3rdparty/masm/assembler/RepatchBuffer.h new file mode 100644 index 0000000000..dbb56f9ad5 --- /dev/null +++ b/src/3rdparty/masm/assembler/RepatchBuffer.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RepatchBuffer_h +#define RepatchBuffer_h + +#if ENABLE(JIT) + +#include "CodeBlock.h" +#include +#include + +namespace JSC { + +// RepatchBuffer: +// +// This class is used to modify code after code generation has been completed, +// and after the code has potentially already been executed. This mechanism is +// used to apply optimizations to the code. +// +class RepatchBuffer { + typedef MacroAssemblerCodePtr CodePtr; + +public: + RepatchBuffer(CodeBlock* codeBlock) + { + JITCode& code = codeBlock->getJITCode(); + m_start = code.start(); + m_size = code.size(); + + ExecutableAllocator::makeWritable(m_start, m_size); + } + + ~RepatchBuffer() + { + ExecutableAllocator::makeExecutable(m_start, m_size); + } + + void relink(CodeLocationJump jump, CodeLocationLabel destination) + { + MacroAssembler::repatchJump(jump, destination); + } + + void relink(CodeLocationCall call, CodeLocationLabel destination) + { + MacroAssembler::repatchCall(call, destination); + } + + void relink(CodeLocationCall call, FunctionPtr destination) + { + MacroAssembler::repatchCall(call, destination); + } + + void relink(CodeLocationNearCall nearCall, CodePtr destination) + { + MacroAssembler::repatchNearCall(nearCall, CodeLocationLabel(destination)); + } + + void relink(CodeLocationNearCall nearCall, CodeLocationLabel destination) + { + MacroAssembler::repatchNearCall(nearCall, destination); + } + + void repatch(CodeLocationDataLabel32 dataLabel32, int32_t value) + { + MacroAssembler::repatchInt32(dataLabel32, value); + } + + void repatch(CodeLocationDataLabelCompact dataLabelCompact, int32_t value) + { + MacroAssembler::repatchCompact(dataLabelCompact, value); + } + + void repatch(CodeLocationDataLabelPtr dataLabelPtr, void* value) + { + MacroAssembler::repatchPointer(dataLabelPtr, value); + } + + void relinkCallerToTrampoline(ReturnAddressPtr returnAddress, CodeLocationLabel label) + { + relink(CodeLocationCall(CodePtr(returnAddress)), label); + } + + void relinkCallerToTrampoline(ReturnAddressPtr returnAddress, CodePtr newCalleeFunction) + { + relinkCallerToTrampoline(returnAddress, CodeLocationLabel(newCalleeFunction)); + } + + void relinkCallerToFunction(ReturnAddressPtr returnAddress, FunctionPtr function) + { + relink(CodeLocationCall(CodePtr(returnAddress)), function); + } + + void relinkNearCallerToTrampoline(ReturnAddressPtr returnAddress, CodeLocationLabel label) + { + relink(CodeLocationNearCall(CodePtr(returnAddress)), label); + } + + void relinkNearCallerToTrampoline(ReturnAddressPtr returnAddress, CodePtr newCalleeFunction) + { + relinkNearCallerToTrampoline(returnAddress, CodeLocationLabel(newCalleeFunction)); + } + + void replaceWithLoad(CodeLocationConvertibleLoad label) + { + MacroAssembler::replaceWithLoad(label); + } + + void replaceWithAddressComputation(CodeLocationConvertibleLoad label) + { + MacroAssembler::replaceWithAddressComputation(label); + } + + void setLoadInstructionIsActive(CodeLocationConvertibleLoad label, bool isActive) + { + if (isActive) + replaceWithLoad(label); + else + replaceWithAddressComputation(label); + } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + return MacroAssembler::startOfBranchPtrWithPatchOnRegister(label); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) + { + return MacroAssembler::startOfPatchableBranchPtrWithPatchOnAddress(label); + } + + void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + MacroAssembler::replaceWithJump(instructionStart, destination); + } + + // This is a *bit* of a silly API, since we currently always also repatch the + // immediate after calling this. But I'm fine with that, since this just feels + // less yucky. + void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, MacroAssembler::RegisterID reg, void* value) + { + MacroAssembler::revertJumpReplacementToBranchPtrWithPatch(instructionStart, reg, value); + } + + void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, MacroAssembler::Address address, void* value) + { + MacroAssembler::revertJumpReplacementToPatchableBranchPtrWithPatch(instructionStart, address, value); + } + +private: + void* m_start; + size_t m_size; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) + +#endif // RepatchBuffer_h diff --git a/src/3rdparty/masm/assembler/SH4Assembler.h b/src/3rdparty/masm/assembler/SH4Assembler.h new file mode 100644 index 0000000000..39f5585be1 --- /dev/null +++ b/src/3rdparty/masm/assembler/SH4Assembler.h @@ -0,0 +1,2152 @@ +/* + * Copyright (C) 2009-2011 STMicroelectronics. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SH4Assembler_h +#define SH4Assembler_h + +#if ENABLE(ASSEMBLER) && CPU(SH4) + +#include "AssemblerBuffer.h" +#include "AssemblerBufferWithConstantPool.h" +#include "JITCompilationEffort.h" +#include +#include +#include +#include +#include +#include + +#ifndef NDEBUG +#define SH4_ASSEMBLER_TRACING +#endif + +namespace JSC { +typedef uint16_t SH4Word; + +enum { + INVALID_OPCODE = 0xffff, + ADD_OPCODE = 0x300c, + ADDIMM_OPCODE = 0x7000, + ADDC_OPCODE = 0x300e, + ADDV_OPCODE = 0x300f, + AND_OPCODE = 0x2009, + ANDIMM_OPCODE = 0xc900, + DIV0_OPCODE = 0x2007, + DIV1_OPCODE = 0x3004, + BF_OPCODE = 0x8b00, + BFS_OPCODE = 0x8f00, + BRA_OPCODE = 0xa000, + BRAF_OPCODE = 0x0023, + NOP_OPCODE = 0x0009, + BSR_OPCODE = 0xb000, + RTS_OPCODE = 0x000b, + BT_OPCODE = 0x8900, + BTS_OPCODE = 0x8d00, + BSRF_OPCODE = 0x0003, + BRK_OPCODE = 0x003b, + FTRC_OPCODE = 0xf03d, + CMPEQ_OPCODE = 0x3000, + CMPEQIMM_OPCODE = 0x8800, + CMPGE_OPCODE = 0x3003, + CMPGT_OPCODE = 0x3007, + CMPHI_OPCODE = 0x3006, + CMPHS_OPCODE = 0x3002, + CMPPL_OPCODE = 0x4015, + CMPPZ_OPCODE = 0x4011, + CMPSTR_OPCODE = 0x200c, + DT_OPCODE = 0x4010, + FCMPEQ_OPCODE = 0xf004, + FCMPGT_OPCODE = 0xf005, + FMOV_OPCODE = 0xf00c, + FADD_OPCODE = 0xf000, + FMUL_OPCODE = 0xf002, + FSUB_OPCODE = 0xf001, + FDIV_OPCODE = 0xf003, + FNEG_OPCODE = 0xf04d, + JMP_OPCODE = 0x402b, + JSR_OPCODE = 0x400b, + LDSPR_OPCODE = 0x402a, + LDSLPR_OPCODE = 0x4026, + MOV_OPCODE = 0x6003, + MOVIMM_OPCODE = 0xe000, + MOVB_WRITE_RN_OPCODE = 0x2000, + MOVB_WRITE_RNDEC_OPCODE = 0x2004, + MOVB_WRITE_R0RN_OPCODE = 0x0004, + MOVB_WRITE_OFFGBR_OPCODE = 0xc000, + MOVB_WRITE_OFFRN_OPCODE = 0x8000, + MOVB_READ_RM_OPCODE = 0x6000, + MOVB_READ_RMINC_OPCODE = 0x6004, + MOVB_READ_R0RM_OPCODE = 0x000c, + MOVB_READ_OFFGBR_OPCODE = 0xc400, + MOVB_READ_OFFRM_OPCODE = 0x8400, + MOVL_WRITE_RN_OPCODE = 0x2002, + MOVL_WRITE_RNDEC_OPCODE = 0x2006, + MOVL_WRITE_R0RN_OPCODE = 0x0006, + MOVL_WRITE_OFFGBR_OPCODE = 0xc200, + MOVL_WRITE_OFFRN_OPCODE = 0x1000, + MOVL_READ_RM_OPCODE = 0x6002, + MOVL_READ_RMINC_OPCODE = 0x6006, + MOVL_READ_R0RM_OPCODE = 0x000e, + MOVL_READ_OFFGBR_OPCODE = 0xc600, + MOVL_READ_OFFPC_OPCODE = 0xd000, + MOVL_READ_OFFRM_OPCODE = 0x5000, + MOVW_WRITE_RN_OPCODE = 0x2001, + MOVW_READ_RM_OPCODE = 0x6001, + MOVW_READ_R0RM_OPCODE = 0x000d, + MOVW_READ_OFFRM_OPCODE = 0x8500, + MOVW_READ_OFFPC_OPCODE = 0x9000, + MOVA_READ_OFFPC_OPCODE = 0xc700, + MOVT_OPCODE = 0x0029, + MULL_OPCODE = 0x0007, + DMULL_L_OPCODE = 0x3005, + STSMACL_OPCODE = 0x001a, + STSMACH_OPCODE = 0x000a, + DMULSL_OPCODE = 0x300d, + NEG_OPCODE = 0x600b, + NEGC_OPCODE = 0x600a, + NOT_OPCODE = 0x6007, + OR_OPCODE = 0x200b, + ORIMM_OPCODE = 0xcb00, + ORBIMM_OPCODE = 0xcf00, + SETS_OPCODE = 0x0058, + SETT_OPCODE = 0x0018, + SHAD_OPCODE = 0x400c, + SHAL_OPCODE = 0x4020, + SHAR_OPCODE = 0x4021, + SHLD_OPCODE = 0x400d, + SHLL_OPCODE = 0x4000, + SHLL2_OPCODE = 0x4008, + SHLL8_OPCODE = 0x4018, + SHLL16_OPCODE = 0x4028, + SHLR_OPCODE = 0x4001, + SHLR2_OPCODE = 0x4009, + SHLR8_OPCODE = 0x4019, + SHLR16_OPCODE = 0x4029, + STSPR_OPCODE = 0x002a, + STSLPR_OPCODE = 0x4022, + FLOAT_OPCODE = 0xf02d, + SUB_OPCODE = 0x3008, + SUBC_OPCODE = 0x300a, + SUBV_OPCODE = 0x300b, + TST_OPCODE = 0x2008, + TSTIMM_OPCODE = 0xc800, + TSTB_OPCODE = 0xcc00, + EXTUB_OPCODE = 0x600c, + EXTUW_OPCODE = 0x600d, + XOR_OPCODE = 0x200a, + XORIMM_OPCODE = 0xca00, + XORB_OPCODE = 0xce00, + FMOVS_READ_RM_INC_OPCODE = 0xf009, + FMOVS_READ_RM_OPCODE = 0xf008, + FMOVS_READ_R0RM_OPCODE = 0xf006, + FMOVS_WRITE_RN_OPCODE = 0xf00a, + FMOVS_WRITE_RN_DEC_OPCODE = 0xf00b, + FMOVS_WRITE_R0RN_OPCODE = 0xf007, + FCNVDS_DRM_FPUL_OPCODE = 0xf0bd, + FCNVSD_FPUL_DRN_OPCODE = 0xf0ad, + LDS_RM_FPUL_OPCODE = 0x405a, + FLDS_FRM_FPUL_OPCODE = 0xf01d, + STS_FPUL_RN_OPCODE = 0x005a, + FSTS_FPUL_FRN_OPCODE = 0xF00d, + LDSFPSCR_OPCODE = 0x406a, + STSFPSCR_OPCODE = 0x006a, + LDSRMFPUL_OPCODE = 0x405a, + FSTSFPULFRN_OPCODE = 0xf00d, + FSQRT_OPCODE = 0xf06d, + FSCHG_OPCODE = 0xf3fd, + CLRT_OPCODE = 8, +}; + +namespace SH4Registers { +typedef enum { + r0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + r13, + r14, fp = r14, + r15, sp = r15, + pc, + pr, +} RegisterID; + +typedef enum { + fr0, dr0 = fr0, + fr1, + fr2, dr2 = fr2, + fr3, + fr4, dr4 = fr4, + fr5, + fr6, dr6 = fr6, + fr7, + fr8, dr8 = fr8, + fr9, + fr10, dr10 = fr10, + fr11, + fr12, dr12 = fr12, + fr13, + fr14, dr14 = fr14, + fr15, +} FPRegisterID; +} + +inline uint16_t getOpcodeGroup1(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0xf) << 8) | ((rn & 0xf) << 4)); +} + +inline uint16_t getOpcodeGroup2(uint16_t opc, int rm) +{ + return (opc | ((rm & 0xf) << 8)); +} + +inline uint16_t getOpcodeGroup3(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0xf) << 8) | (rn & 0xff)); +} + +inline uint16_t getOpcodeGroup4(uint16_t opc, int rm, int rn, int offset) +{ + return (opc | ((rm & 0xf) << 8) | ((rn & 0xf) << 4) | (offset & 0xf)); +} + +inline uint16_t getOpcodeGroup5(uint16_t opc, int rm) +{ + return (opc | (rm & 0xff)); +} + +inline uint16_t getOpcodeGroup6(uint16_t opc, int rm) +{ + return (opc | (rm & 0xfff)); +} + +inline uint16_t getOpcodeGroup7(uint16_t opc, int rm) +{ + return (opc | ((rm & 0x7) << 9)); +} + +inline uint16_t getOpcodeGroup8(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0x7) << 9) | ((rn & 0x7) << 5)); +} + +inline uint16_t getOpcodeGroup9(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0xf) << 8) | ((rn & 0x7) << 5)); +} + +inline uint16_t getOpcodeGroup10(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0x7) << 9) | ((rn & 0xf) << 4)); +} + +inline uint16_t getOpcodeGroup11(uint16_t opc, int rm, int rn) +{ + return (opc | ((rm & 0xf) << 4) | (rn & 0xf)); +} + +inline uint16_t getRn(uint16_t x) +{ + return ((x & 0xf00) >> 8); +} + +inline uint16_t getRm(uint16_t x) +{ + return ((x & 0xf0) >> 4); +} + +inline uint16_t getDisp(uint16_t x) +{ + return (x & 0xf); +} + +inline uint16_t getImm8(uint16_t x) +{ + return (x & 0xff); +} + +inline uint16_t getImm12(uint16_t x) +{ + return (x & 0xfff); +} + +inline uint16_t getDRn(uint16_t x) +{ + return ((x & 0xe00) >> 9); +} + +inline uint16_t getDRm(uint16_t x) +{ + return ((x & 0xe0) >> 5); +} + +class SH4Assembler { +public: + typedef SH4Registers::RegisterID RegisterID; + typedef SH4Registers::FPRegisterID FPRegisterID; + typedef AssemblerBufferWithConstantPool<512, 4, 2, SH4Assembler> SH4Buffer; + static const RegisterID scratchReg1 = SH4Registers::r3; + static const RegisterID scratchReg2 = SH4Registers::r11; + static const uint32_t maxInstructionSize = 16; + + enum { + padForAlign8 = 0x00, + padForAlign16 = 0x0009, + padForAlign32 = 0x00090009, + }; + + enum JumpType { + JumpFar, + JumpNear + }; + + SH4Assembler() + { + m_claimscratchReg = 0x0; + } + + // SH4 condition codes + typedef enum { + EQ = 0x0, // Equal + NE = 0x1, // Not Equal + HS = 0x2, // Unsigend Greater Than equal + HI = 0x3, // Unsigend Greater Than + LS = 0x4, // Unsigend Lower or Same + LI = 0x5, // Unsigend Lower + GE = 0x6, // Greater or Equal + LT = 0x7, // Less Than + GT = 0x8, // Greater Than + LE = 0x9, // Less or Equal + OF = 0xa, // OverFlow + SI = 0xb, // Signed + EQU= 0xc, // Equal or unordered(NaN) + NEU= 0xd, + GTU= 0xe, + GEU= 0xf, + LTU= 0x10, + LEU= 0x11, + } Condition; + + // Opaque label types +public: + bool isImmediate(int constant) + { + return ((constant <= 127) && (constant >= -128)); + } + + RegisterID claimScratch() + { + ASSERT((m_claimscratchReg != 0x3)); + + if (!(m_claimscratchReg & 0x1)) { + m_claimscratchReg = (m_claimscratchReg | 0x1); + return scratchReg1; + } + + m_claimscratchReg = (m_claimscratchReg | 0x2); + return scratchReg2; + } + + void releaseScratch(RegisterID scratchR) + { + if (scratchR == scratchReg1) + m_claimscratchReg = (m_claimscratchReg & 0x2); + else + m_claimscratchReg = (m_claimscratchReg & 0x1); + } + + // Stack operations + + void pushReg(RegisterID reg) + { + if (reg == SH4Registers::pr) { + oneShortOp(getOpcodeGroup2(STSLPR_OPCODE, SH4Registers::sp)); + return; + } + + oneShortOp(getOpcodeGroup1(MOVL_WRITE_RNDEC_OPCODE, SH4Registers::sp, reg)); + } + + void popReg(RegisterID reg) + { + if (reg == SH4Registers::pr) { + oneShortOp(getOpcodeGroup2(LDSLPR_OPCODE, SH4Registers::sp)); + return; + } + + oneShortOp(getOpcodeGroup1(MOVL_READ_RMINC_OPCODE, reg, SH4Registers::sp)); + } + + void movt(RegisterID dst) + { + uint16_t opc = getOpcodeGroup2(MOVT_OPCODE, dst); + oneShortOp(opc); + } + + // Arithmetic operations + + void addlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(ADD_OPCODE, dst, src); + oneShortOp(opc); + } + + void addclRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(ADDC_OPCODE, dst, src); + oneShortOp(opc); + } + + void addvlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(ADDV_OPCODE, dst, src); + oneShortOp(opc); + } + + void addlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 127) && (imm8 >= -128)); + + uint16_t opc = getOpcodeGroup3(ADDIMM_OPCODE, dst, imm8); + oneShortOp(opc); + } + + void andlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(AND_OPCODE, dst, src); + oneShortOp(opc); + } + + void andlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 255) && (imm8 >= 0)); + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup5(ANDIMM_OPCODE, imm8); + oneShortOp(opc); + } + + void div1lRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(DIV1_OPCODE, dst, src); + oneShortOp(opc); + } + + void div0lRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(DIV0_OPCODE, dst, src); + oneShortOp(opc); + } + + void notlReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(NOT_OPCODE, dst, src); + oneShortOp(opc); + } + + void orlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(OR_OPCODE, dst, src); + oneShortOp(opc); + } + + void orlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 255) && (imm8 >= 0)); + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup5(ORIMM_OPCODE, imm8); + oneShortOp(opc); + } + + void sublRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(SUB_OPCODE, dst, src); + oneShortOp(opc); + } + + void subvlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(SUBV_OPCODE, dst, src); + oneShortOp(opc); + } + + void xorlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(XOR_OPCODE, dst, src); + oneShortOp(opc); + } + + void xorlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 255) && (imm8 >= 0)); + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup5(XORIMM_OPCODE, imm8); + oneShortOp(opc); + } + + void shllImm8r(int imm, RegisterID dst) + { + switch (imm) { + case 1: + oneShortOp(getOpcodeGroup2(SHLL_OPCODE, dst)); + break; + case 2: + oneShortOp(getOpcodeGroup2(SHLL2_OPCODE, dst)); + break; + case 8: + oneShortOp(getOpcodeGroup2(SHLL8_OPCODE, dst)); + break; + case 16: + oneShortOp(getOpcodeGroup2(SHLL16_OPCODE, dst)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void neg(RegisterID dst, RegisterID src) + { + uint16_t opc = getOpcodeGroup1(NEG_OPCODE, dst, src); + oneShortOp(opc); + } + + void shllRegReg(RegisterID dst, RegisterID rShift) + { + uint16_t opc = getOpcodeGroup1(SHLD_OPCODE, dst, rShift); + oneShortOp(opc); + } + + void shlrRegReg(RegisterID dst, RegisterID rShift) + { + neg(rShift, rShift); + shllRegReg(dst, rShift); + } + + void sharRegReg(RegisterID dst, RegisterID rShift) + { + neg(rShift, rShift); + shaRegReg(dst, rShift); + } + + void shaRegReg(RegisterID dst, RegisterID rShift) + { + uint16_t opc = getOpcodeGroup1(SHAD_OPCODE, dst, rShift); + oneShortOp(opc); + } + + void shlrImm8r(int imm, RegisterID dst) + { + switch (imm) { + case 1: + oneShortOp(getOpcodeGroup2(SHLR_OPCODE, dst)); + break; + case 2: + oneShortOp(getOpcodeGroup2(SHLR2_OPCODE, dst)); + break; + case 8: + oneShortOp(getOpcodeGroup2(SHLR8_OPCODE, dst)); + break; + case 16: + oneShortOp(getOpcodeGroup2(SHLR16_OPCODE, dst)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void imullRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MULL_OPCODE, dst, src); + oneShortOp(opc); + } + + void dmullRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(DMULL_L_OPCODE, dst, src); + oneShortOp(opc); + } + + void dmulslRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(DMULSL_OPCODE, dst, src); + oneShortOp(opc); + } + + void stsmacl(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(STSMACL_OPCODE, reg); + oneShortOp(opc); + } + + void stsmach(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(STSMACH_OPCODE, reg); + oneShortOp(opc); + } + + // Comparisons + + void cmplRegReg(RegisterID left, RegisterID right, Condition cond) + { + switch (cond) { + case NE: + oneShortOp(getOpcodeGroup1(CMPEQ_OPCODE, right, left)); + break; + case GT: + oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, right, left)); + break; + case EQ: + oneShortOp(getOpcodeGroup1(CMPEQ_OPCODE, right, left)); + break; + case GE: + oneShortOp(getOpcodeGroup1(CMPGE_OPCODE, right, left)); + break; + case HS: + oneShortOp(getOpcodeGroup1(CMPHS_OPCODE, right, left)); + break; + case HI: + oneShortOp(getOpcodeGroup1(CMPHI_OPCODE, right, left)); + break; + case LI: + oneShortOp(getOpcodeGroup1(CMPHI_OPCODE, left, right)); + break; + case LS: + oneShortOp(getOpcodeGroup1(CMPHS_OPCODE, left, right)); + break; + case LE: + oneShortOp(getOpcodeGroup1(CMPGE_OPCODE, left, right)); + break; + case LT: + oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, left, right)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void cmppl(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(CMPPL_OPCODE, reg); + oneShortOp(opc); + } + + void cmppz(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(CMPPZ_OPCODE, reg); + oneShortOp(opc); + } + + void cmpEqImmR0(int imm, RegisterID dst) + { + uint16_t opc = getOpcodeGroup5(CMPEQIMM_OPCODE, imm); + oneShortOp(opc); + } + + void testlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(TST_OPCODE, dst, src); + oneShortOp(opc); + } + + void testlImm8r(int imm, RegisterID dst) + { + ASSERT((dst == SH4Registers::r0) && (imm <= 255) && (imm >= 0)); + + uint16_t opc = getOpcodeGroup5(TSTIMM_OPCODE, imm); + oneShortOp(opc); + } + + void nop() + { + oneShortOp(NOP_OPCODE, false); + } + + void sett() + { + oneShortOp(SETT_OPCODE); + } + + void clrt() + { + oneShortOp(CLRT_OPCODE); + } + + void fschg() + { + oneShortOp(FSCHG_OPCODE); + } + + void bkpt() + { + oneShortOp(BRK_OPCODE, false); + } + + void branch(uint16_t opc, int label) + { + switch (opc) { + case BT_OPCODE: + ASSERT((label <= 127) && (label >= -128)); + oneShortOp(getOpcodeGroup5(BT_OPCODE, label)); + break; + case BRA_OPCODE: + ASSERT((label <= 2047) && (label >= -2048)); + oneShortOp(getOpcodeGroup6(BRA_OPCODE, label)); + break; + case BF_OPCODE: + ASSERT((label <= 127) && (label >= -128)); + oneShortOp(getOpcodeGroup5(BF_OPCODE, label)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void branch(uint16_t opc, RegisterID reg) + { + switch (opc) { + case BRAF_OPCODE: + oneShortOp(getOpcodeGroup2(BRAF_OPCODE, reg)); + break; + case JMP_OPCODE: + oneShortOp(getOpcodeGroup2(JMP_OPCODE, reg)); + break; + case JSR_OPCODE: + oneShortOp(getOpcodeGroup2(JSR_OPCODE, reg)); + break; + case BSRF_OPCODE: + oneShortOp(getOpcodeGroup2(BSRF_OPCODE, reg)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + + void ldspr(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(LDSPR_OPCODE, reg); + oneShortOp(opc); + } + + void stspr(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(STSPR_OPCODE, reg); + oneShortOp(opc); + } + + void extub(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(EXTUB_OPCODE, dst, src); + oneShortOp(opc); + } + + void extuw(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(EXTUW_OPCODE, dst, src); + oneShortOp(opc); + } + + // float operations + + void ldsrmfpul(RegisterID src) + { + uint16_t opc = getOpcodeGroup2(LDS_RM_FPUL_OPCODE, src); + oneShortOp(opc); + } + + void fneg(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup2(FNEG_OPCODE, dst); + oneShortOp(opc, true, false); + } + + void fsqrt(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup2(FSQRT_OPCODE, dst); + oneShortOp(opc, true, false); + } + + void stsfpulReg(RegisterID src) + { + uint16_t opc = getOpcodeGroup2(STS_FPUL_RN_OPCODE, src); + oneShortOp(opc); + } + + void floatfpulfrn(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup2(FLOAT_OPCODE, src); + oneShortOp(opc, true, false); + } + + void fmull(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMUL_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsReadrm(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsWriterm(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_RN_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsWriter0r(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_R0RN_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsReadr0r(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_READ_R0RM_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsReadrminc(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_INC_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void fmovsWriterndec(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_RN_DEC_OPCODE, dst, src); + oneShortOp(opc, true, false); + } + + void ftrcRegfpul(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup2(FTRC_OPCODE, src); + oneShortOp(opc, true, false); + } + + void fldsfpul(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup2(FLDS_FRM_FPUL_OPCODE, src); + oneShortOp(opc); + } + + void fstsfpul(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup2(FSTS_FPUL_FRN_OPCODE, src); + oneShortOp(opc); + } + + void ldsfpscr(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(LDSFPSCR_OPCODE, reg); + oneShortOp(opc); + } + + void stsfpscr(RegisterID reg) + { + uint16_t opc = getOpcodeGroup2(STSFPSCR_OPCODE, reg); + oneShortOp(opc); + } + + // double operations + + void dcnvds(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup7(FCNVDS_DRM_FPUL_OPCODE, src >> 1); + oneShortOp(opc); + } + + void dcnvsd(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup7(FCNVSD_FPUL_DRN_OPCODE, dst >> 1); + oneShortOp(opc); + } + + void dcmppeq(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FCMPEQ_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dcmppgt(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FCMPGT_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dmulRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FMUL_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dsubRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FSUB_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void daddRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FADD_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dmovRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FMOV_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void ddivRegReg(FPRegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup8(FDIV_OPCODE, dst >> 1, src >> 1); + oneShortOp(opc); + } + + void dsqrt(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup7(FSQRT_OPCODE, dst >> 1); + oneShortOp(opc); + } + + void dneg(FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup7(FNEG_OPCODE, dst >> 1); + oneShortOp(opc); + } + + void fmovReadrm(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup10(FMOVS_READ_RM_OPCODE, dst >> 1, src); + oneShortOp(opc); + } + + void fmovWriterm(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_RN_OPCODE, dst, src >> 1); + oneShortOp(opc); + } + + void fmovWriter0r(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_R0RN_OPCODE, dst, src >> 1); + oneShortOp(opc); + } + + void fmovReadr0r(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup10(FMOVS_READ_R0RM_OPCODE, dst >> 1, src); + oneShortOp(opc); + } + + void fmovReadrminc(RegisterID src, FPRegisterID dst) + { + uint16_t opc = getOpcodeGroup10(FMOVS_READ_RM_INC_OPCODE, dst >> 1, src); + oneShortOp(opc); + } + + void fmovWriterndec(FPRegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_RN_DEC_OPCODE, dst, src >> 1); + oneShortOp(opc); + } + + void floatfpulDreg(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup7(FLOAT_OPCODE, src >> 1); + oneShortOp(opc); + } + + void ftrcdrmfpul(FPRegisterID src) + { + uint16_t opc = getOpcodeGroup7(FTRC_OPCODE, src >> 1); + oneShortOp(opc); + } + + // Various move ops + + void movImm8(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 127) && (imm8 >= -128)); + + uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, imm8); + oneShortOp(opc); + } + + void movlRegReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOV_OPCODE, dst, src); + oneShortOp(opc); + } + + void movwRegMem(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVW_WRITE_RN_OPCODE, dst, src); + oneShortOp(opc); + } + + void movwMemReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVW_READ_RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movwPCReg(int offset, RegisterID base, RegisterID dst) + { + ASSERT(base == SH4Registers::pc); + ASSERT((offset <= 255) && (offset >= 0)); + + uint16_t opc = getOpcodeGroup3(MOVW_READ_OFFPC_OPCODE, dst, offset); + oneShortOp(opc); + } + + void movwMemReg(int offset, RegisterID base, RegisterID dst) + { + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup11(MOVW_READ_OFFRM_OPCODE, base, offset); + oneShortOp(opc); + } + + void movwR0mr(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVW_READ_R0RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movlRegMem(RegisterID src, int offset, RegisterID base) + { + ASSERT((offset <= 15) && (offset >= 0)); + + if (!offset) { + oneShortOp(getOpcodeGroup1(MOVL_WRITE_RN_OPCODE, base, src)); + return; + } + + oneShortOp(getOpcodeGroup4(MOVL_WRITE_OFFRN_OPCODE, base, src, offset)); + } + + void movlRegMem(RegisterID src, RegisterID base) + { + uint16_t opc = getOpcodeGroup1(MOVL_WRITE_RN_OPCODE, base, src); + oneShortOp(opc); + } + + void movlMemReg(int offset, RegisterID base, RegisterID dst) + { + if (base == SH4Registers::pc) { + ASSERT((offset <= 255) && (offset >= 0)); + oneShortOp(getOpcodeGroup3(MOVL_READ_OFFPC_OPCODE, dst, offset)); + return; + } + + ASSERT((offset <= 15) && (offset >= 0)); + if (!offset) { + oneShortOp(getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base)); + return; + } + + oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset)); + } + + void movlMemRegCompact(int offset, RegisterID base, RegisterID dst) + { + oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset)); + } + + void movbRegMem(RegisterID src, RegisterID base) + { + uint16_t opc = getOpcodeGroup1(MOVB_WRITE_RN_OPCODE, base, src); + oneShortOp(opc); + } + + void movbMemReg(int offset, RegisterID base, RegisterID dst) + { + ASSERT(dst == SH4Registers::r0); + + uint16_t opc = getOpcodeGroup11(MOVB_READ_OFFRM_OPCODE, base, offset); + oneShortOp(opc); + } + + void movbR0mr(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVB_READ_R0RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movbMemReg(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVB_READ_RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movlMemReg(RegisterID base, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base); + oneShortOp(opc); + } + + void movlMemRegIn(RegisterID base, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVL_READ_RMINC_OPCODE, dst, base); + oneShortOp(opc); + } + + void movlR0mr(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVL_READ_R0RM_OPCODE, dst, src); + oneShortOp(opc); + } + + void movlRegMemr0(RegisterID src, RegisterID dst) + { + uint16_t opc = getOpcodeGroup1(MOVL_WRITE_R0RN_OPCODE, dst, src); + oneShortOp(opc); + } + + void movlImm8r(int imm8, RegisterID dst) + { + ASSERT((imm8 <= 127) && (imm8 >= -128)); + + uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, imm8); + oneShortOp(opc); + } + + void loadConstant(uint32_t constant, RegisterID dst) + { + if (((int)constant <= 0x7f) && ((int)constant >= -0x80)) { + movImm8(constant, dst); + return; + } + + uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, 0); + + m_buffer.ensureSpace(maxInstructionSize, sizeof(uint32_t)); + printInstr(getOpcodeGroup3(MOVIMM_OPCODE, dst, constant), m_buffer.codeSize()); + m_buffer.putShortWithConstantInt(opc, constant, true); + } + + void loadConstantUnReusable(uint32_t constant, RegisterID dst, bool ensureSpace = false) + { + uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, 0); + + if (ensureSpace) + m_buffer.ensureSpace(maxInstructionSize, sizeof(uint32_t)); + + printInstr(getOpcodeGroup3(MOVIMM_OPCODE, dst, constant), m_buffer.codeSize()); + m_buffer.putShortWithConstantInt(opc, constant); + } + + // Flow control + + AssemblerLabel call() + { + RegisterID scr = claimScratch(); + m_buffer.ensureSpace(maxInstructionSize + 4, sizeof(uint32_t)); + loadConstantUnReusable(0x0, scr); + branch(JSR_OPCODE, scr); + nop(); + releaseScratch(scr); + return m_buffer.label(); + } + + AssemblerLabel call(RegisterID dst) + { + m_buffer.ensureSpace(maxInstructionSize + 2); + branch(JSR_OPCODE, dst); + nop(); + return m_buffer.label(); + } + + AssemblerLabel jmp() + { + RegisterID scr = claimScratch(); + m_buffer.ensureSpace(maxInstructionSize + 4, sizeof(uint32_t)); + AssemblerLabel label = m_buffer.label(); + loadConstantUnReusable(0x0, scr); + branch(BRAF_OPCODE, scr); + nop(); + releaseScratch(scr); + return label; + } + + void extraInstrForBranch(RegisterID dst) + { + loadConstantUnReusable(0x0, dst); + nop(); + nop(); + } + + AssemblerLabel jmp(RegisterID dst) + { + jmpReg(dst); + return m_buffer.label(); + } + + void jmpReg(RegisterID dst) + { + m_buffer.ensureSpace(maxInstructionSize + 2); + branch(JMP_OPCODE, dst); + nop(); + } + + AssemblerLabel jne() + { + AssemblerLabel label = m_buffer.label(); + branch(BF_OPCODE, 0); + return label; + } + + AssemblerLabel je() + { + AssemblerLabel label = m_buffer.label(); + branch(BT_OPCODE, 0); + return label; + } + + AssemblerLabel bra() + { + AssemblerLabel label = m_buffer.label(); + branch(BRA_OPCODE, 0); + return label; + } + + void ret() + { + m_buffer.ensureSpace(maxInstructionSize + 2); + oneShortOp(RTS_OPCODE, false); + } + + AssemblerLabel labelIgnoringWatchpoints() + { + m_buffer.ensureSpaceForAnyInstruction(); + return m_buffer.label(); + } + + AssemblerLabel label() + { + m_buffer.ensureSpaceForAnyInstruction(); + return m_buffer.label(); + } + + int sizeOfConstantPool() + { + return m_buffer.sizeOfConstantPool(); + } + + AssemblerLabel align(int alignment) + { + m_buffer.ensureSpace(maxInstructionSize + 2); + while (!m_buffer.isAligned(alignment)) { + nop(); + m_buffer.ensureSpace(maxInstructionSize + 2); + } + return label(); + } + + static void changePCrelativeAddress(int offset, uint16_t* instructionPtr, uint32_t newAddress) + { + uint32_t address = (offset << 2) + ((reinterpret_cast(instructionPtr) + 4) &(~0x3)); + *reinterpret_cast(address) = newAddress; + } + + static uint32_t readPCrelativeAddress(int offset, uint16_t* instructionPtr) + { + uint32_t address = (offset << 2) + ((reinterpret_cast(instructionPtr) + 4) &(~0x3)); + return *reinterpret_cast(address); + } + + static uint16_t* getInstructionPtr(void* code, int offset) + { + return reinterpret_cast (reinterpret_cast(code) + offset); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + + uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset); + uint16_t instruction = *instructionPtr; + int offsetBits = (reinterpret_cast(to) - reinterpret_cast(code)) - from.m_offset; + + if (((instruction & 0xff00) == BT_OPCODE) || ((instruction & 0xff00) == BF_OPCODE)) { + /* BT label ==> BF 2 + nop LDR reg + nop braf @reg + nop nop + */ + offsetBits -= 8; + instruction ^= 0x0202; + *instructionPtr++ = instruction; + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); + instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); + *instructionPtr = instruction; + printBlockInstr(instructionPtr - 2, from.m_offset, 3); + return; + } + + /* MOV #imm, reg => LDR reg + braf @reg braf @reg + nop nop + */ + ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); + + offsetBits -= 4; + if (offsetBits >= -4096 && offsetBits <= 4094) { + *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); + *(++instructionPtr) = NOP_OPCODE; + printBlockInstr(instructionPtr - 1, from.m_offset, 2); + return; + } + + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); + printInstr(*instructionPtr, from.m_offset + 2); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset); + instructionPtr -= 3; + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(to)); + } + + static void linkPointer(void* code, AssemblerLabel where, void* value) + { + uint16_t* instructionPtr = getInstructionPtr(code, where.m_offset); + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(value)); + } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + ASSERT(call.isSet()); + return call.m_offset; + } + + static uint32_t* getLdrImmAddressOnPool(SH4Word* insn, uint32_t* constPool) + { + return (constPool + (*insn & 0xff)); + } + + static SH4Word patchConstantPoolLoad(SH4Word load, int value) + { + return ((load & ~0xff) | value); + } + + static SH4Buffer::TwoShorts placeConstantPoolBarrier(int offset) + { + ASSERT(((offset >> 1) <=2047) && ((offset >> 1) >= -2048)); + + SH4Buffer::TwoShorts m_barrier; + m_barrier.high = (BRA_OPCODE | (offset >> 1)); + m_barrier.low = NOP_OPCODE; + printInstr(((BRA_OPCODE | (offset >> 1))), 0); + printInstr(NOP_OPCODE, 0); + return m_barrier; + } + + static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr) + { + SH4Word* instructionPtr = reinterpret_cast(loadAddr); + SH4Word instruction = *instructionPtr; + SH4Word index = instruction & 0xff; + + if ((instruction & 0xf000) != MOVIMM_OPCODE) + return; + + ASSERT((((reinterpret_cast(constPoolAddr) - reinterpret_cast(loadAddr)) + index * 4)) < 1024); + + int offset = reinterpret_cast(constPoolAddr) + (index * 4) - ((reinterpret_cast(instructionPtr) & ~0x03) + 4); + instruction &=0xf00; + instruction |= 0xd000; + offset &= 0x03ff; + instruction |= (offset >> 2); + *instructionPtr = instruction; + printInstr(instruction, reinterpret_cast(loadAddr)); + } + + static void repatchPointer(void* where, void* value) + { + patchPointer(where, value); + } + + static void* readPointer(void* code) + { + return reinterpret_cast(readInt32(code)); + } + + static void repatchInt32(void* where, int32_t value) + { + uint16_t* instructionPtr = reinterpret_cast(where); + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, value); + } + + static void repatchCompact(void* where, int32_t value) + { + ASSERT(value >= 0); + ASSERT(value <= 60); + *reinterpret_cast(where) = ((*reinterpret_cast(where) & 0xfff0) | (value >> 2)); + cacheFlush(reinterpret_cast(where), sizeof(uint16_t)); + } + + static void relinkCall(void* from, void* to) + { + uint16_t* instructionPtr = reinterpret_cast(from); + instructionPtr -= 3; + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast(to)); + } + + static void relinkJump(void* from, void* to) + { + uint16_t* instructionPtr = reinterpret_cast (from); + uint16_t instruction = *instructionPtr; + int32_t offsetBits = (reinterpret_cast(to) - reinterpret_cast(from)); + + if (((*instructionPtr & 0xff00) == BT_OPCODE) || ((*instructionPtr & 0xff00) == BF_OPCODE)) { + offsetBits -= 8; + instructionPtr++; + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); + instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); + *instructionPtr = instruction; + printBlockInstr(instructionPtr, reinterpret_cast(from) + 1, 3); + return; + } + + ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); + offsetBits -= 4; + if (offsetBits >= -4096 && offsetBits <= 4094) { + *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); + *(++instructionPtr) = NOP_OPCODE; + printBlockInstr(instructionPtr - 2, reinterpret_cast(from), 2); + return; + } + + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); + printInstr(*instructionPtr, reinterpret_cast(from)); + } + + // Linking & patching + + static void revertJump(void* instructionStart, SH4Word imm) + { + SH4Word *insn = reinterpret_cast(instructionStart); + SH4Word disp; + + ASSERT((insn[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE); + + disp = insn[0] & 0x00ff; + insn += 2 + (disp << 1); // PC += 4 + (disp*4) + insn = (SH4Word *) ((unsigned) insn & (~3)); + insn[0] = imm; + cacheFlush(insn, sizeof(SH4Word)); + } + + void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type = JumpFar) + { + ASSERT(to.isSet()); + ASSERT(from.isSet()); + + uint16_t* instructionPtr = getInstructionPtr(data(), from.m_offset); + uint16_t instruction = *instructionPtr; + int offsetBits; + + if (type == JumpNear) { + ASSERT((instruction == BT_OPCODE) || (instruction == BF_OPCODE) || (instruction == BRA_OPCODE)); + int offset = (codeSize() - from.m_offset) - 4; + *instructionPtr++ = instruction | (offset >> 1); + printInstr(*instructionPtr, from.m_offset + 2); + return; + } + + if (((instruction & 0xff00) == BT_OPCODE) || ((instruction & 0xff00) == BF_OPCODE)) { + /* BT label => BF 2 + nop LDR reg + nop braf @reg + nop nop + */ + offsetBits = (to.m_offset - from.m_offset) - 8; + instruction ^= 0x0202; + *instructionPtr++ = instruction; + if ((*instructionPtr & 0xf000) == 0xe000) { + uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress()); + *addr = offsetBits; + } else + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits); + instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00)); + *instructionPtr = instruction; + printBlockInstr(instructionPtr - 2, from.m_offset, 3); + return; + } + + /* MOV # imm, reg => LDR reg + braf @reg braf @reg + nop nop + */ + ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE); + offsetBits = (to.m_offset - from.m_offset) - 4; + if (offsetBits >= -4096 && offsetBits <= 4094) { + *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1); + *(++instructionPtr) = NOP_OPCODE; + printBlockInstr(instructionPtr - 1, from.m_offset, 2); + return; + } + + instruction = *instructionPtr; + if ((instruction & 0xf000) == 0xe000) { + uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress()); + *addr = offsetBits - 2; + printInstr(*instructionPtr, from.m_offset + 2); + return; + } + + changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2); + printInstr(*instructionPtr, from.m_offset + 2); + } + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + static void patchPointer(void* code, AssemblerLabel where, void* value) + { + patchPointer(reinterpret_cast(code) + where.m_offset, value); + } + + static void patchPointer(void* code, void* value) + { + patchInt32(code, reinterpret_cast(value)); + } + + static void patchInt32(void* code, uint32_t value) + { + changePCrelativeAddress((*(reinterpret_cast(code)) & 0xff), reinterpret_cast(code), value); + } + + static uint32_t readInt32(void* code) + { + return readPCrelativeAddress((*(reinterpret_cast(code)) & 0xff), reinterpret_cast(code)); + } + + static void* readCallTarget(void* from) + { + uint16_t* instructionPtr = static_cast(from); + instructionPtr -= 3; + return reinterpret_cast(readPCrelativeAddress((*instructionPtr & 0xff), instructionPtr)); + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + return m_buffer.executableCopy(globalData, ownerUID, effort); + } + + static void cacheFlush(void* code, size_t size) + { +#if !OS(LINUX) +#error "The cacheFlush support is missing on this platform." +#elif defined CACHEFLUSH_D_L2 + syscall(__NR_cacheflush, reinterpret_cast(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I | CACHEFLUSH_D_L2); +#else + syscall(__NR_cacheflush, reinterpret_cast(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I); +#endif + } + + void prefix(uint16_t pre) + { + m_buffer.putByte(pre); + } + + void oneShortOp(uint16_t opcode, bool checksize = true, bool isDouble = true) + { + printInstr(opcode, m_buffer.codeSize(), isDouble); + if (checksize) + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putShortUnchecked(opcode); + } + + void ensureSpace(int space) + { + m_buffer.ensureSpace(space); + } + + void ensureSpace(int insnSpace, int constSpace) + { + m_buffer.ensureSpace(insnSpace, constSpace); + } + + // Administrative methods + + void* data() const { return m_buffer.data(); } + size_t codeSize() const { return m_buffer.codeSize(); } + +#ifdef SH4_ASSEMBLER_TRACING + static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true) + { + if (!getenv("JavaScriptCoreDumpJIT")) + return; + + const char *format = 0; + printfStdoutInstr("offset: 0x%8.8x\t", size); + switch (opc) { + case BRK_OPCODE: + format = " BRK\n"; + break; + case NOP_OPCODE: + format = " NOP\n"; + break; + case RTS_OPCODE: + format =" *RTS\n"; + break; + case SETS_OPCODE: + format = " SETS\n"; + break; + case SETT_OPCODE: + format = " SETT\n"; + break; + case CLRT_OPCODE: + format = " CLRT\n"; + break; + case FSCHG_OPCODE: + format = " FSCHG\n"; + break; + } + if (format) { + printfStdoutInstr(format); + return; + } + switch (opc & 0xf0ff) { + case BRAF_OPCODE: + format = " *BRAF R%d\n"; + break; + case DT_OPCODE: + format = " DT R%d\n"; + break; + case CMPPL_OPCODE: + format = " CMP/PL R%d\n"; + break; + case CMPPZ_OPCODE: + format = " CMP/PZ R%d\n"; + break; + case JMP_OPCODE: + format = " *JMP @R%d\n"; + break; + case JSR_OPCODE: + format = " *JSR @R%d\n"; + break; + case LDSPR_OPCODE: + format = " LDS R%d, PR\n"; + break; + case LDSLPR_OPCODE: + format = " LDS.L @R%d+, PR\n"; + break; + case MOVT_OPCODE: + format = " MOVT R%d\n"; + break; + case SHAL_OPCODE: + format = " SHAL R%d\n"; + break; + case SHAR_OPCODE: + format = " SHAR R%d\n"; + break; + case SHLL_OPCODE: + format = " SHLL R%d\n"; + break; + case SHLL2_OPCODE: + format = " SHLL2 R%d\n"; + break; + case SHLL8_OPCODE: + format = " SHLL8 R%d\n"; + break; + case SHLL16_OPCODE: + format = " SHLL16 R%d\n"; + break; + case SHLR_OPCODE: + format = " SHLR R%d\n"; + break; + case SHLR2_OPCODE: + format = " SHLR2 R%d\n"; + break; + case SHLR8_OPCODE: + format = " SHLR8 R%d\n"; + break; + case SHLR16_OPCODE: + format = " SHLR16 R%d\n"; + break; + case STSPR_OPCODE: + format = " STS PR, R%d\n"; + break; + case STSLPR_OPCODE: + format = " STS.L PR, @-R%d\n"; + break; + case LDS_RM_FPUL_OPCODE: + format = " LDS R%d, FPUL\n"; + break; + case STS_FPUL_RN_OPCODE: + format = " STS FPUL, R%d \n"; + break; + case FLDS_FRM_FPUL_OPCODE: + format = " FLDS FR%d, FPUL\n"; + break; + case FSTS_FPUL_FRN_OPCODE: + format = " FSTS FPUL, R%d \n"; + break; + case LDSFPSCR_OPCODE: + format = " LDS R%d, FPSCR \n"; + break; + case STSFPSCR_OPCODE: + format = " STS FPSCR, R%d \n"; + break; + case STSMACL_OPCODE: + format = " STS MACL, R%d \n"; + break; + case STSMACH_OPCODE: + format = " STS MACH, R%d \n"; + break; + case BSRF_OPCODE: + format = " *BSRF R%d"; + break; + case FTRC_OPCODE: + format = " FTRC FR%d, FPUL\n"; + break; + } + if (format) { + printfStdoutInstr(format, getRn(opc)); + return; + } + switch (opc & 0xf0ff) { + case FNEG_OPCODE: + format = " FNEG DR%d\n"; + break; + case FLOAT_OPCODE: + format = " FLOAT DR%d\n"; + break; + case FTRC_OPCODE: + format = " FTRC FR%d, FPUL\n"; + break; + case FSQRT_OPCODE: + format = " FSQRT FR%d\n"; + break; + case FCNVDS_DRM_FPUL_OPCODE: + format = " FCNVDS FR%d, FPUL\n"; + break; + case FCNVSD_FPUL_DRN_OPCODE: + format = " FCNVSD FPUL, FR%d\n"; + break; + } + if (format) { + if (isdoubleInst) + printfStdoutInstr(format, getDRn(opc) << 1); + else + printfStdoutInstr(format, getRn(opc)); + return; + } + switch (opc & 0xf00f) { + case ADD_OPCODE: + format = " ADD R%d, R%d\n"; + break; + case ADDC_OPCODE: + format = " ADDC R%d, R%d\n"; + break; + case ADDV_OPCODE: + format = " ADDV R%d, R%d\n"; + break; + case AND_OPCODE: + format = " AND R%d, R%d\n"; + break; + case DIV1_OPCODE: + format = " DIV1 R%d, R%d\n"; + break; + case CMPEQ_OPCODE: + format = " CMP/EQ R%d, R%d\n"; + break; + case CMPGE_OPCODE: + format = " CMP/GE R%d, R%d\n"; + break; + case CMPGT_OPCODE: + format = " CMP/GT R%d, R%d\n"; + break; + case CMPHI_OPCODE: + format = " CMP/HI R%d, R%d\n"; + break; + case CMPHS_OPCODE: + format = " CMP/HS R%d, R%d\n"; + break; + case MOV_OPCODE: + format = " MOV R%d, R%d\n"; + break; + case MOVB_WRITE_RN_OPCODE: + format = " MOV.B R%d, @R%d\n"; + break; + case MOVB_WRITE_RNDEC_OPCODE: + format = " MOV.B R%d, @-R%d\n"; + break; + case MOVB_WRITE_R0RN_OPCODE: + format = " MOV.B R%d, @(R0, R%d)\n"; + break; + case MOVB_READ_RM_OPCODE: + format = " MOV.B @R%d, R%d\n"; + break; + case MOVB_READ_RMINC_OPCODE: + format = " MOV.B @R%d+, R%d\n"; + break; + case MOVB_READ_R0RM_OPCODE: + format = " MOV.B @(R0, R%d), R%d\n"; + break; + case MOVL_WRITE_RN_OPCODE: + format = " MOV.L R%d, @R%d\n"; + break; + case MOVL_WRITE_RNDEC_OPCODE: + format = " MOV.L R%d, @-R%d\n"; + break; + case MOVL_WRITE_R0RN_OPCODE: + format = " MOV.L R%d, @(R0, R%d)\n"; + break; + case MOVL_READ_RM_OPCODE: + format = " MOV.L @R%d, R%d\n"; + break; + case MOVL_READ_RMINC_OPCODE: + format = " MOV.L @R%d+, R%d\n"; + break; + case MOVL_READ_R0RM_OPCODE: + format = " MOV.L @(R0, R%d), R%d\n"; + break; + case MULL_OPCODE: + format = " MUL.L R%d, R%d\n"; + break; + case DMULL_L_OPCODE: + format = " DMULU.L R%d, R%d\n"; + break; + case DMULSL_OPCODE: + format = " DMULS.L R%d, R%d\n"; + break; + case NEG_OPCODE: + format = " NEG R%d, R%d\n"; + break; + case NEGC_OPCODE: + format = " NEGC R%d, R%d\n"; + break; + case NOT_OPCODE: + format = " NOT R%d, R%d\n"; + break; + case OR_OPCODE: + format = " OR R%d, R%d\n"; + break; + case SHAD_OPCODE: + format = " SHAD R%d, R%d\n"; + break; + case SHLD_OPCODE: + format = " SHLD R%d, R%d\n"; + break; + case SUB_OPCODE: + format = " SUB R%d, R%d\n"; + break; + case SUBC_OPCODE: + format = " SUBC R%d, R%d\n"; + break; + case SUBV_OPCODE: + format = " SUBV R%d, R%d\n"; + break; + case TST_OPCODE: + format = " TST R%d, R%d\n"; + break; + case XOR_OPCODE: + format = " XOR R%d, R%d\n";break; + case MOVW_WRITE_RN_OPCODE: + format = " MOV.W R%d, @R%d\n"; + break; + case MOVW_READ_RM_OPCODE: + format = " MOV.W @R%d, R%d\n"; + break; + case MOVW_READ_R0RM_OPCODE: + format = " MOV.W @(R0, R%d), R%d\n"; + break; + case EXTUB_OPCODE: + format = " EXTU.B R%d, R%d\n"; + break; + case EXTUW_OPCODE: + format = " EXTU.W R%d, R%d\n"; + break; + } + if (format) { + printfStdoutInstr(format, getRm(opc), getRn(opc)); + return; + } + switch (opc & 0xf00f) { + case FSUB_OPCODE: + format = " FSUB FR%d, FR%d\n"; + break; + case FADD_OPCODE: + format = " FADD FR%d, FR%d\n"; + break; + case FDIV_OPCODE: + format = " FDIV FR%d, FR%d\n"; + break; + case FMUL_OPCODE: + format = " DMULL FR%d, FR%d\n"; + break; + case FMOV_OPCODE: + format = " FMOV FR%d, FR%d\n"; + break; + case FCMPEQ_OPCODE: + format = " FCMP/EQ FR%d, FR%d\n"; + break; + case FCMPGT_OPCODE: + format = " FCMP/GT FR%d, FR%d\n"; + break; + } + if (format) { + if (isdoubleInst) + printfStdoutInstr(format, getDRm(opc) << 1, getDRn(opc) << 1); + else + printfStdoutInstr(format, getRm(opc), getRn(opc)); + return; + } + switch (opc & 0xf00f) { + case FMOVS_WRITE_RN_DEC_OPCODE: + format = " %s FR%d, @-R%d\n"; + break; + case FMOVS_WRITE_RN_OPCODE: + format = " %s FR%d, @R%d\n"; + break; + case FMOVS_WRITE_R0RN_OPCODE: + format = " %s FR%d, @(R0, R%d)\n"; + break; + } + if (format) { + if (isdoubleInst) + printfStdoutInstr(format, "FMOV", getDRm(opc) << 1, getDRn(opc)); + else + printfStdoutInstr(format, "FMOV.S", getRm(opc), getRn(opc)); + return; + } + switch (opc & 0xf00f) { + case FMOVS_READ_RM_OPCODE: + format = " %s @R%d, FR%d\n"; + break; + case FMOVS_READ_RM_INC_OPCODE: + format = " %s @R%d+, FR%d\n"; + break; + case FMOVS_READ_R0RM_OPCODE: + format = " %s @(R0, R%d), FR%d\n"; + break; + } + if (format) { + if (isdoubleInst) + printfStdoutInstr(format, "FMOV", getDRm(opc), getDRn(opc) << 1); + else + printfStdoutInstr(format, "FMOV.S", getRm(opc), getRn(opc)); + return; + } + switch (opc & 0xff00) { + case BF_OPCODE: + format = " BF %d\n"; + break; + case BFS_OPCODE: + format = " *BF/S %d\n"; + break; + case ANDIMM_OPCODE: + format = " AND #%d, R0\n"; + break; + case BT_OPCODE: + format = " BT %d\n"; + break; + case BTS_OPCODE: + format = " *BT/S %d\n"; + break; + case CMPEQIMM_OPCODE: + format = " CMP/EQ #%d, R0\n"; + break; + case MOVB_WRITE_OFFGBR_OPCODE: + format = " MOV.B R0, @(%d, GBR)\n"; + break; + case MOVB_READ_OFFGBR_OPCODE: + format = " MOV.B @(%d, GBR), R0\n"; + break; + case MOVL_WRITE_OFFGBR_OPCODE: + format = " MOV.L R0, @(%d, GBR)\n"; + break; + case MOVL_READ_OFFGBR_OPCODE: + format = " MOV.L @(%d, GBR), R0\n"; + break; + case MOVA_READ_OFFPC_OPCODE: + format = " MOVA @(%d, PC), R0\n"; + break; + case ORIMM_OPCODE: + format = " OR #%d, R0\n"; + break; + case ORBIMM_OPCODE: + format = " OR.B #%d, @(R0, GBR)\n"; + break; + case TSTIMM_OPCODE: + format = " TST #%d, R0\n"; + break; + case TSTB_OPCODE: + format = " TST.B %d, @(R0, GBR)\n"; + break; + case XORIMM_OPCODE: + format = " XOR #%d, R0\n"; + break; + case XORB_OPCODE: + format = " XOR.B %d, @(R0, GBR)\n"; + break; + } + if (format) { + printfStdoutInstr(format, getImm8(opc)); + return; + } + switch (opc & 0xff00) { + case MOVB_WRITE_OFFRN_OPCODE: + format = " MOV.B R0, @(%d, R%d)\n"; + break; + case MOVB_READ_OFFRM_OPCODE: + format = " MOV.B @(%d, R%d), R0\n"; + break; + } + if (format) { + printfStdoutInstr(format, getDisp(opc), getRm(opc)); + return; + } + switch (opc & 0xf000) { + case BRA_OPCODE: + format = " *BRA %d\n"; + break; + case BSR_OPCODE: + format = " *BSR %d\n"; + break; + } + if (format) { + printfStdoutInstr(format, getImm12(opc)); + return; + } + switch (opc & 0xf000) { + case MOVL_READ_OFFPC_OPCODE: + format = " MOV.L @(%d, PC), R%d\n"; + break; + case ADDIMM_OPCODE: + format = " ADD #%d, R%d\n"; + break; + case MOVIMM_OPCODE: + format = " MOV #%d, R%d\n"; + break; + case MOVW_READ_OFFPC_OPCODE: + format = " MOV.W @(%d, PC), R%d\n"; + break; + } + if (format) { + printfStdoutInstr(format, getImm8(opc), getRn(opc)); + return; + } + switch (opc & 0xf000) { + case MOVL_WRITE_OFFRN_OPCODE: + format = " MOV.L R%d, @(%d, R%d)\n"; + printfStdoutInstr(format, getRm(opc), getDisp(opc), getRn(opc)); + break; + case MOVL_READ_OFFRM_OPCODE: + format = " MOV.L @(%d, R%d), R%d\n"; + printfStdoutInstr(format, getDisp(opc), getRm(opc), getRn(opc)); + break; + } + } + + static void printfStdoutInstr(const char* format, ...) + { + if (getenv("JavaScriptCoreDumpJIT")) { + va_list args; + va_start(args, format); + vprintfStdoutInstr(format, args); + va_end(args); + } + } + + static void vprintfStdoutInstr(const char* format, va_list args) + { + if (getenv("JavaScriptCoreDumpJIT")) + WTF::dataLogFV(format, args); + } + + static void printBlockInstr(uint16_t* first, unsigned offset, int nbInstr) + { + printfStdoutInstr(">> repatch instructions after link\n"); + for (int i = 0; i <= nbInstr; i++) + printInstr(*(first + i), offset + i); + printfStdoutInstr(">> end repatch\n"); + } +#else + static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true) { }; + static void printBlockInstr(uint16_t* first, unsigned offset, int nbInstr) { }; +#endif + + static void replaceWithLoad(void* instructionStart) + { + SH4Word* insPtr = reinterpret_cast(instructionStart); + + insPtr += 2; // skip MOV and ADD opcodes + + if (((*insPtr) & 0xf00f) != MOVL_READ_RM_OPCODE) { + *insPtr = MOVL_READ_RM_OPCODE | (*insPtr & 0x0ff0); + cacheFlush(insPtr, sizeof(SH4Word)); + } + } + + static void replaceWithAddressComputation(void* instructionStart) + { + SH4Word* insPtr = reinterpret_cast(instructionStart); + + insPtr += 2; // skip MOV and ADD opcodes + + if (((*insPtr) & 0xf00f) != MOV_OPCODE) { + *insPtr = MOV_OPCODE | (*insPtr & 0x0ff0); + cacheFlush(insPtr, sizeof(SH4Word)); + } + } + +private: + SH4Buffer m_buffer; + int m_claimscratchReg; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(SH4) + +#endif // SH4Assembler_h diff --git a/src/3rdparty/masm/assembler/X86Assembler.h b/src/3rdparty/masm/assembler/X86Assembler.h new file mode 100644 index 0000000000..25ff6f0a50 --- /dev/null +++ b/src/3rdparty/masm/assembler/X86Assembler.h @@ -0,0 +1,2540 @@ +/* + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef X86Assembler_h +#define X86Assembler_h + +#if ENABLE(ASSEMBLER) && (CPU(X86) || CPU(X86_64)) + +#include "AssemblerBuffer.h" +#include "JITCompilationEffort.h" +#include +#include +#include + +namespace JSC { + +inline bool CAN_SIGN_EXTEND_8_32(int32_t value) { return value == (int32_t)(signed char)value; } + +namespace X86Registers { + typedef enum { + eax, + ecx, + edx, + ebx, + esp, + ebp, + esi, + edi, + +#if CPU(X86_64) + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, +#endif + } RegisterID; + + typedef enum { + xmm0, + xmm1, + xmm2, + xmm3, + xmm4, + xmm5, + xmm6, + xmm7, + } XMMRegisterID; +} + +class X86Assembler { +public: + typedef X86Registers::RegisterID RegisterID; + typedef X86Registers::XMMRegisterID XMMRegisterID; + typedef XMMRegisterID FPRegisterID; + + typedef enum { + ConditionO, + ConditionNO, + ConditionB, + ConditionAE, + ConditionE, + ConditionNE, + ConditionBE, + ConditionA, + ConditionS, + ConditionNS, + ConditionP, + ConditionNP, + ConditionL, + ConditionGE, + ConditionLE, + ConditionG, + + ConditionC = ConditionB, + ConditionNC = ConditionAE, + } Condition; + +private: + typedef enum { + OP_ADD_EvGv = 0x01, + OP_ADD_GvEv = 0x03, + OP_OR_EvGv = 0x09, + OP_OR_GvEv = 0x0B, + OP_2BYTE_ESCAPE = 0x0F, + OP_AND_EvGv = 0x21, + OP_AND_GvEv = 0x23, + OP_SUB_EvGv = 0x29, + OP_SUB_GvEv = 0x2B, + PRE_PREDICT_BRANCH_NOT_TAKEN = 0x2E, + OP_XOR_EvGv = 0x31, + OP_XOR_GvEv = 0x33, + OP_CMP_EvGv = 0x39, + OP_CMP_GvEv = 0x3B, +#if CPU(X86_64) + PRE_REX = 0x40, +#endif + OP_PUSH_EAX = 0x50, + OP_POP_EAX = 0x58, +#if CPU(X86_64) + OP_MOVSXD_GvEv = 0x63, +#endif + PRE_OPERAND_SIZE = 0x66, + PRE_SSE_66 = 0x66, + OP_PUSH_Iz = 0x68, + OP_IMUL_GvEvIz = 0x69, + OP_GROUP1_EbIb = 0x80, + OP_GROUP1_EvIz = 0x81, + OP_GROUP1_EvIb = 0x83, + OP_TEST_EbGb = 0x84, + OP_TEST_EvGv = 0x85, + OP_XCHG_EvGv = 0x87, + OP_MOV_EbGb = 0x88, + OP_MOV_EvGv = 0x89, + OP_MOV_GvEv = 0x8B, + OP_LEA = 0x8D, + OP_GROUP1A_Ev = 0x8F, + OP_NOP = 0x90, + OP_CDQ = 0x99, + OP_MOV_EAXOv = 0xA1, + OP_MOV_OvEAX = 0xA3, + OP_MOV_EAXIv = 0xB8, + OP_GROUP2_EvIb = 0xC1, + OP_RET = 0xC3, + OP_GROUP11_EvIb = 0xC6, + OP_GROUP11_EvIz = 0xC7, + OP_INT3 = 0xCC, + OP_GROUP2_Ev1 = 0xD1, + OP_GROUP2_EvCL = 0xD3, + OP_ESCAPE_DD = 0xDD, + OP_CALL_rel32 = 0xE8, + OP_JMP_rel32 = 0xE9, + PRE_SSE_F2 = 0xF2, + PRE_SSE_F3 = 0xF3, + OP_HLT = 0xF4, + OP_GROUP3_EbIb = 0xF6, + OP_GROUP3_Ev = 0xF7, + OP_GROUP3_EvIz = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test. + OP_GROUP5_Ev = 0xFF, + } OneByteOpcodeID; + + typedef enum { + OP2_MOVSD_VsdWsd = 0x10, + OP2_MOVSD_WsdVsd = 0x11, + OP2_MOVSS_VsdWsd = 0x10, + OP2_MOVSS_WsdVsd = 0x11, + OP2_CVTSI2SD_VsdEd = 0x2A, + OP2_CVTTSD2SI_GdWsd = 0x2C, + OP2_UCOMISD_VsdWsd = 0x2E, + OP2_ADDSD_VsdWsd = 0x58, + OP2_MULSD_VsdWsd = 0x59, + OP2_CVTSD2SS_VsdWsd = 0x5A, + OP2_CVTSS2SD_VsdWsd = 0x5A, + OP2_SUBSD_VsdWsd = 0x5C, + OP2_DIVSD_VsdWsd = 0x5E, + OP2_SQRTSD_VsdWsd = 0x51, + OP2_ANDNPD_VpdWpd = 0x55, + OP2_XORPD_VpdWpd = 0x57, + OP2_MOVD_VdEd = 0x6E, + OP2_MOVD_EdVd = 0x7E, + OP2_JCC_rel32 = 0x80, + OP_SETCC = 0x90, + OP2_IMUL_GvEv = 0xAF, + OP2_MOVZX_GvEb = 0xB6, + OP2_MOVSX_GvEb = 0xBE, + OP2_MOVZX_GvEw = 0xB7, + OP2_MOVSX_GvEw = 0xBF, + OP2_PEXTRW_GdUdIb = 0xC5, + OP2_PSLLQ_UdqIb = 0x73, + OP2_PSRLQ_UdqIb = 0x73, + OP2_POR_VdqWdq = 0XEB, + } TwoByteOpcodeID; + + TwoByteOpcodeID jccRel32(Condition cond) + { + return (TwoByteOpcodeID)(OP2_JCC_rel32 + cond); + } + + TwoByteOpcodeID setccOpcode(Condition cond) + { + return (TwoByteOpcodeID)(OP_SETCC + cond); + } + + typedef enum { + GROUP1_OP_ADD = 0, + GROUP1_OP_OR = 1, + GROUP1_OP_ADC = 2, + GROUP1_OP_AND = 4, + GROUP1_OP_SUB = 5, + GROUP1_OP_XOR = 6, + GROUP1_OP_CMP = 7, + + GROUP1A_OP_POP = 0, + + GROUP2_OP_ROL = 0, + GROUP2_OP_ROR = 1, + GROUP2_OP_RCL = 2, + GROUP2_OP_RCR = 3, + + GROUP2_OP_SHL = 4, + GROUP2_OP_SHR = 5, + GROUP2_OP_SAR = 7, + + GROUP3_OP_TEST = 0, + GROUP3_OP_NOT = 2, + GROUP3_OP_NEG = 3, + GROUP3_OP_IDIV = 7, + + GROUP5_OP_CALLN = 2, + GROUP5_OP_JMPN = 4, + GROUP5_OP_PUSH = 6, + + GROUP11_MOV = 0, + + GROUP14_OP_PSLLQ = 6, + GROUP14_OP_PSRLQ = 2, + + ESCAPE_DD_FSTP_doubleReal = 3, + } GroupOpcodeID; + + class X86InstructionFormatter; +public: + + X86Assembler() + : m_indexOfLastWatchpoint(INT_MIN) + , m_indexOfTailOfLastWatchpoint(INT_MIN) + { + } + + // Stack operations: + + void push_r(RegisterID reg) + { + m_formatter.oneByteOp(OP_PUSH_EAX, reg); + } + + void pop_r(RegisterID reg) + { + m_formatter.oneByteOp(OP_POP_EAX, reg); + } + + void push_i32(int imm) + { + m_formatter.oneByteOp(OP_PUSH_Iz); + m_formatter.immediate32(imm); + } + + void push_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_PUSH, base, offset); + } + + void pop_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP1A_Ev, GROUP1A_OP_POP, base, offset); + } + + // Arithmetic operations: + +#if !CPU(X86_64) + void adcl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADC, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADC, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void addl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_ADD_EvGv, src, dst); + } + + void addl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_ADD_GvEv, dst, base, offset); + } + +#if !CPU(X86_64) + void addl_mr(const void* addr, RegisterID dst) + { + m_formatter.oneByteOp(OP_ADD_GvEv, dst, addr); + } +#endif + + void addl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_ADD_EvGv, src, base, offset); + } + + void addl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, dst); + m_formatter.immediate32(imm); + } + } + + void addl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, offset); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void addq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_ADD_EvGv, src, dst); + } + + void addq_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64(OP_ADD_GvEv, dst, base, offset); + } + + void addq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_ADD, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_ADD, dst); + m_formatter.immediate32(imm); + } + } + + void addq_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_ADD, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_ADD, base, offset); + m_formatter.immediate32(imm); + } + } +#else + void addl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void andl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_AND_EvGv, src, dst); + } + + void andl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_AND_GvEv, dst, base, offset); + } + + void andl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_AND_EvGv, src, base, offset); + } + + void andl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, dst); + m_formatter.immediate32(imm); + } + } + + void andl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, base, offset); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void andq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_AND_EvGv, src, dst); + } + + void andq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_AND, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_AND, dst); + m_formatter.immediate32(imm); + } + } +#else + void andl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_AND, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_AND, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void negl_r(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, dst); + } + +#if CPU(X86_64) + void negq_r(RegisterID dst) + { + m_formatter.oneByteOp64(OP_GROUP3_Ev, GROUP3_OP_NEG, dst); + } +#endif + + void negl_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, base, offset); + } + + void notl_r(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NOT, dst); + } + + void notl_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NOT, base, offset); + } + + void orl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_OR_EvGv, src, dst); + } + + void orl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_OR_GvEv, dst, base, offset); + } + + void orl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_OR_EvGv, src, base, offset); + } + + void orl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, dst); + m_formatter.immediate32(imm); + } + } + + void orl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, base, offset); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void orq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_OR_EvGv, src, dst); + } + + void orq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_OR, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_OR, dst); + m_formatter.immediate32(imm); + } + } +#else + void orl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_OR, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_OR, addr); + m_formatter.immediate32(imm); + } + } + + void orl_rm(RegisterID src, const void* addr) + { + m_formatter.oneByteOp(OP_OR_EvGv, src, addr); + } +#endif + + void subl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_SUB_EvGv, src, dst); + } + + void subl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_SUB_GvEv, dst, base, offset); + } + + void subl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_SUB_EvGv, src, base, offset); + } + + void subl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, dst); + m_formatter.immediate32(imm); + } + } + + void subl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, base, offset); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void subq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_SUB_EvGv, src, dst); + } + + void subq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_SUB, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_SUB, dst); + m_formatter.immediate32(imm); + } + } +#else + void subl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void xorl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_XOR_EvGv, src, dst); + } + + void xorl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_XOR_GvEv, dst, base, offset); + } + + void xorl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_XOR_EvGv, src, base, offset); + } + + void xorl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_XOR, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_XOR, base, offset); + m_formatter.immediate32(imm); + } + } + + void xorl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_XOR, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_XOR, dst); + m_formatter.immediate32(imm); + } + } + +#if CPU(X86_64) + void xorq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_XOR_EvGv, src, dst); + } + + void xorq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_XOR, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_XOR, dst); + m_formatter.immediate32(imm); + } + } + + void xorq_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_XOR_EvGv, src, base, offset); + } + + void rorq_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_ROR, dst); + else { + m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_ROR, dst); + m_formatter.immediate8(imm); + } + } + +#endif + + void sarl_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SAR, dst); + else { + m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SAR, dst); + m_formatter.immediate8(imm); + } + } + + void sarl_CLr(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst); + } + + void shrl_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHR, dst); + else { + m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHR, dst); + m_formatter.immediate8(imm); + } + } + + void shrl_CLr(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHR, dst); + } + + void shll_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHL, dst); + else { + m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHL, dst); + m_formatter.immediate8(imm); + } + } + + void shll_CLr(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHL, dst); + } + +#if CPU(X86_64) + void sarq_CLr(RegisterID dst) + { + m_formatter.oneByteOp64(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst); + } + + void sarq_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_SAR, dst); + else { + m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_SAR, dst); + m_formatter.immediate8(imm); + } + } +#endif + + void imull_rr(RegisterID src, RegisterID dst) + { + m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, src); + } + + void imull_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, base, offset); + } + + void imull_i32r(RegisterID src, int32_t value, RegisterID dst) + { + m_formatter.oneByteOp(OP_IMUL_GvEvIz, dst, src); + m_formatter.immediate32(value); + } + + void idivl_r(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_IDIV, dst); + } + + // Comparisons: + + void cmpl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_CMP_EvGv, src, dst); + } + + void cmpl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_CMP_EvGv, src, base, offset); + } + + void cmpl_mr(int offset, RegisterID base, RegisterID src) + { + m_formatter.oneByteOp(OP_CMP_GvEv, src, base, offset); + } + + void cmpl_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); + m_formatter.immediate32(imm); + } + } + + void cmpl_ir_force32(int imm, RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); + m_formatter.immediate32(imm); + } + + void cmpl_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); + m_formatter.immediate32(imm); + } + } + + void cmpb_im(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, offset); + m_formatter.immediate8(imm); + } + + void cmpb_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate8(imm); + } + +#if CPU(X86) + void cmpb_im(int imm, const void* addr) + { + m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, addr); + m_formatter.immediate8(imm); + } +#endif + + void cmpl_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate32(imm); + } + } + + void cmpl_im_force32(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); + m_formatter.immediate32(imm); + } + +#if CPU(X86_64) + void cmpq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_CMP_EvGv, src, dst); + } + + void cmpq_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_CMP_EvGv, src, base, offset); + } + + void cmpq_mr(int offset, RegisterID base, RegisterID src) + { + m_formatter.oneByteOp64(OP_CMP_GvEv, src, base, offset); + } + + void cmpq_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); + m_formatter.immediate32(imm); + } + } + + void cmpq_im(int imm, int offset, RegisterID base) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset); + m_formatter.immediate32(imm); + } + } + + void cmpq_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp64(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp64(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate32(imm); + } + } +#else + void cmpl_rm(RegisterID reg, const void* addr) + { + m_formatter.oneByteOp(OP_CMP_EvGv, reg, addr); + } + + void cmpl_im(int imm, const void* addr) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, addr); + m_formatter.immediate8(imm); + } else { + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, addr); + m_formatter.immediate32(imm); + } + } +#endif + + void cmpw_ir(int imm, RegisterID dst) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, dst); + m_formatter.immediate8(imm); + } else { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst); + m_formatter.immediate16(imm); + } + } + + void cmpw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_CMP_EvGv, src, base, index, scale, offset); + } + + void cmpw_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + if (CAN_SIGN_EXTEND_8_32(imm)) { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate8(imm); + } else { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, index, scale, offset); + m_formatter.immediate16(imm); + } + } + + void testl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_TEST_EvGv, src, dst); + } + + void testl_i32r(int imm, RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, dst); + m_formatter.immediate32(imm); + } + + void testl_i32m(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, offset); + m_formatter.immediate32(imm); + } + + void testb_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp8(OP_TEST_EbGb, src, dst); + } + + void testb_im(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, offset); + m_formatter.immediate8(imm); + } + + void testb_im(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, index, scale, offset); + m_formatter.immediate8(imm); + } + +#if CPU(X86) + void testb_im(int imm, const void* addr) + { + m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, addr); + m_formatter.immediate8(imm); + } +#endif + + void testl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset); + m_formatter.immediate32(imm); + } + +#if CPU(X86_64) + void testq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_TEST_EvGv, src, dst); + } + + void testq_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_TEST_EvGv, src, base, offset); + } + + void testq_i32r(int imm, RegisterID dst) + { + m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, dst); + m_formatter.immediate32(imm); + } + + void testq_i32m(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, offset); + m_formatter.immediate32(imm); + } + + void testq_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset); + m_formatter.immediate32(imm); + } +#endif + + void testw_rr(RegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp(OP_TEST_EvGv, src, dst); + } + + void testb_i8r(int imm, RegisterID dst) + { + m_formatter.oneByteOp8(OP_GROUP3_EbIb, GROUP3_OP_TEST, dst); + m_formatter.immediate8(imm); + } + + void setCC_r(Condition cond, RegisterID dst) + { + m_formatter.twoByteOp8(setccOpcode(cond), (GroupOpcodeID)0, dst); + } + + void sete_r(RegisterID dst) + { + m_formatter.twoByteOp8(setccOpcode(ConditionE), (GroupOpcodeID)0, dst); + } + + void setz_r(RegisterID dst) + { + sete_r(dst); + } + + void setne_r(RegisterID dst) + { + m_formatter.twoByteOp8(setccOpcode(ConditionNE), (GroupOpcodeID)0, dst); + } + + void setnz_r(RegisterID dst) + { + setne_r(dst); + } + + // Various move ops: + + void cdq() + { + m_formatter.oneByteOp(OP_CDQ); + } + + void fstpl(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_ESCAPE_DD, ESCAPE_DD_FSTP_doubleReal, base, offset); + } + + void xchgl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_XCHG_EvGv, src, dst); + } + +#if CPU(X86_64) + void xchgq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_XCHG_EvGv, src, dst); + } +#endif + + void movl_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp(OP_MOV_EvGv, src, dst); + } + + void movl_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_MOV_EvGv, src, base, offset); + } + + void movl_rm_disp32(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset); + } + + void movl_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset); + } + + void movl_mEAX(const void* addr) + { + m_formatter.oneByteOp(OP_MOV_EAXOv); +#if CPU(X86_64) + m_formatter.immediate64(reinterpret_cast(addr)); +#else + m_formatter.immediate32(reinterpret_cast(addr)); +#endif + } + + void movl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, offset); + } + + void movl_mr_disp32(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp_disp32(OP_MOV_GvEv, dst, base, offset); + } + + void movl_mr_disp8(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp_disp8(OP_MOV_GvEv, dst, base, offset); + } + + void movl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, index, scale, offset); + } + + void movl_i32r(int imm, RegisterID dst) + { + m_formatter.oneByteOp(OP_MOV_EAXIv, dst); + m_formatter.immediate32(imm); + } + + void movl_i32m(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, offset); + m_formatter.immediate32(imm); + } + + void movl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, base, index, scale, offset); + m_formatter.immediate32(imm); + } + +#if !CPU(X86_64) + void movb_i8m(int imm, const void* addr) + { + ASSERT(-128 <= imm && imm < 128); + m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, addr); + m_formatter.immediate8(imm); + } +#endif + + void movb_i8m(int imm, int offset, RegisterID base) + { + ASSERT(-128 <= imm && imm < 128); + m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, offset); + m_formatter.immediate8(imm); + } + + void movb_i8m(int imm, int offset, RegisterID base, RegisterID index, int scale) + { + ASSERT(-128 <= imm && imm < 128); + m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, index, scale, offset); + m_formatter.immediate8(imm); + } + + void movb_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp8(OP_MOV_EbGb, src, base, index, scale, offset); + } + + void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.prefix(PRE_OPERAND_SIZE); + m_formatter.oneByteOp8(OP_MOV_EvGv, src, base, index, scale, offset); + } + + void movl_EAXm(const void* addr) + { + m_formatter.oneByteOp(OP_MOV_OvEAX); +#if CPU(X86_64) + m_formatter.immediate64(reinterpret_cast(addr)); +#else + m_formatter.immediate32(reinterpret_cast(addr)); +#endif + } + +#if CPU(X86_64) + void movq_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOV_EvGv, src, dst); + } + + void movq_rm(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_MOV_EvGv, src, base, offset); + } + + void movq_rm_disp32(RegisterID src, int offset, RegisterID base) + { + m_formatter.oneByteOp64_disp32(OP_MOV_EvGv, src, base, offset); + } + + void movq_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.oneByteOp64(OP_MOV_EvGv, src, base, index, scale, offset); + } + + void movq_mEAX(const void* addr) + { + m_formatter.oneByteOp64(OP_MOV_EAXOv); + m_formatter.immediate64(reinterpret_cast(addr)); + } + + void movq_EAXm(const void* addr) + { + m_formatter.oneByteOp64(OP_MOV_OvEAX); + m_formatter.immediate64(reinterpret_cast(addr)); + } + + void movq_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOV_GvEv, dst, base, offset); + } + + void movq_mr_disp32(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64_disp32(OP_MOV_GvEv, dst, base, offset); + } + + void movq_mr_disp8(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64_disp8(OP_MOV_GvEv, dst, base, offset); + } + + void movq_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOV_GvEv, dst, base, index, scale, offset); + } + + void movq_i32m(int imm, int offset, RegisterID base) + { + m_formatter.oneByteOp64(OP_GROUP11_EvIz, GROUP11_MOV, base, offset); + m_formatter.immediate32(imm); + } + + void movq_i64r(int64_t imm, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOV_EAXIv, dst); + m_formatter.immediate64(imm); + } + + void movsxd_rr(RegisterID src, RegisterID dst) + { + m_formatter.oneByteOp64(OP_MOVSXD_GvEv, dst, src); + } + + +#else + void movl_rm(RegisterID src, const void* addr) + { + if (src == X86Registers::eax) + movl_EAXm(addr); + else + m_formatter.oneByteOp(OP_MOV_EvGv, src, addr); + } + + void movl_mr(const void* addr, RegisterID dst) + { + if (dst == X86Registers::eax) + movl_mEAX(addr); + else + m_formatter.oneByteOp(OP_MOV_GvEv, dst, addr); + } + + void movl_i32m(int imm, const void* addr) + { + m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, addr); + m_formatter.immediate32(imm); + } +#endif + + void movzwl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, offset); + } + + void movzwl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, index, scale, offset); + } + + void movswl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, offset); + } + + void movswl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, index, scale, offset); + } + + void movzbl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, offset); + } + + void movzbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, index, scale, offset); + } + + void movsbl_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, offset); + } + + void movsbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst) + { + m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, index, scale, offset); + } + + void movzbl_rr(RegisterID src, RegisterID dst) + { + // In 64-bit, this may cause an unnecessary REX to be planted (if the dst register + // is in the range ESP-EDI, and the src would not have required a REX). Unneeded + // REX prefixes are defined to be silently ignored by the processor. + m_formatter.twoByteOp8(OP2_MOVZX_GvEb, dst, src); + } + + void leal_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp(OP_LEA, dst, base, offset); + } +#if CPU(X86_64) + void leaq_mr(int offset, RegisterID base, RegisterID dst) + { + m_formatter.oneByteOp64(OP_LEA, dst, base, offset); + } +#endif + + // Flow control: + + AssemblerLabel call() + { + m_formatter.oneByteOp(OP_CALL_rel32); + return m_formatter.immediateRel32(); + } + + AssemblerLabel call(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, dst); + return m_formatter.label(); + } + + void call_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, base, offset); + } + + AssemblerLabel jmp() + { + m_formatter.oneByteOp(OP_JMP_rel32); + return m_formatter.immediateRel32(); + } + + // Return a AssemblerLabel so we have a label to the jump, so we can use this + // To make a tail recursive call on x86-64. The MacroAssembler + // really shouldn't wrap this as a Jump, since it can't be linked. :-/ + AssemblerLabel jmp_r(RegisterID dst) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, dst); + return m_formatter.label(); + } + + void jmp_m(int offset, RegisterID base) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, base, offset); + } + +#if !CPU(X86_64) + void jmp_m(const void* address) + { + m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, address); + } +#endif + + AssemblerLabel jne() + { + m_formatter.twoByteOp(jccRel32(ConditionNE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jnz() + { + return jne(); + } + + AssemblerLabel je() + { + m_formatter.twoByteOp(jccRel32(ConditionE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jz() + { + return je(); + } + + AssemblerLabel jl() + { + m_formatter.twoByteOp(jccRel32(ConditionL)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jb() + { + m_formatter.twoByteOp(jccRel32(ConditionB)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jle() + { + m_formatter.twoByteOp(jccRel32(ConditionLE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jbe() + { + m_formatter.twoByteOp(jccRel32(ConditionBE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jge() + { + m_formatter.twoByteOp(jccRel32(ConditionGE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jg() + { + m_formatter.twoByteOp(jccRel32(ConditionG)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel ja() + { + m_formatter.twoByteOp(jccRel32(ConditionA)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jae() + { + m_formatter.twoByteOp(jccRel32(ConditionAE)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jo() + { + m_formatter.twoByteOp(jccRel32(ConditionO)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jnp() + { + m_formatter.twoByteOp(jccRel32(ConditionNP)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jp() + { + m_formatter.twoByteOp(jccRel32(ConditionP)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel js() + { + m_formatter.twoByteOp(jccRel32(ConditionS)); + return m_formatter.immediateRel32(); + } + + AssemblerLabel jCC(Condition cond) + { + m_formatter.twoByteOp(jccRel32(cond)); + return m_formatter.immediateRel32(); + } + + // SSE operations: + + void addsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void addsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, base, offset); + } + +#if !CPU(X86_64) + void addsd_mr(const void* address, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, address); + } +#endif + + void cvtsi2sd_rr(RegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src); + } + + void cvtsi2sd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, offset); + } + +#if !CPU(X86_64) + void cvtsi2sd_mr(const void* address, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, address); + } +#endif + + void cvttsd2si_rr(XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src); + } + + void cvtsd2ss_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_CVTSD2SS_VsdWsd, dst, (RegisterID)src); + } + + void cvtss2sd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F3); + m_formatter.twoByteOp(OP2_CVTSS2SD_VsdWsd, dst, (RegisterID)src); + } + +#if CPU(X86_64) + void cvttsd2siq_rr(XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp64(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src); + } +#endif + + void movd_rr(XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_MOVD_EdVd, (RegisterID)src, dst); + } + + void movd_rr(RegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_MOVD_VdEd, (RegisterID)dst, src); + } + +#if CPU(X86_64) + void movq_rr(XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp64(OP2_MOVD_EdVd, (RegisterID)src, dst); + } + + void movq_rr(RegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp64(OP2_MOVD_VdEd, (RegisterID)dst, src); + } +#endif + + void movsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void movsd_rm(XMMRegisterID src, int offset, RegisterID base) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset); + } + + void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset); + } + + void movss_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale) + { + m_formatter.prefix(PRE_SSE_F3); + m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset); + } + + void movsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset); + } + + void movsd_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, dst, base, index, scale, offset); + } + + void movss_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F3); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, dst, base, index, scale, offset); + } + +#if !CPU(X86_64) + void movsd_mr(const void* address, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address); + } + void movsd_rm(XMMRegisterID src, const void* address) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address); + } +#endif + + void mulsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void mulsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, base, offset); + } + + void pextrw_irr(int whichWord, XMMRegisterID src, RegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_PEXTRW_GdUdIb, (RegisterID)dst, (RegisterID)src); + m_formatter.immediate8(whichWord); + } + + void psllq_i8r(int imm, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp8(OP2_PSLLQ_UdqIb, GROUP14_OP_PSLLQ, (RegisterID)dst); + m_formatter.immediate8(imm); + } + + void psrlq_i8r(int imm, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp8(OP2_PSRLQ_UdqIb, GROUP14_OP_PSRLQ, (RegisterID)dst); + m_formatter.immediate8(imm); + } + + void por_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_POR_VdqWdq, (RegisterID)dst, (RegisterID)src); + } + + void subsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void subsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, base, offset); + } + + void ucomisd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void ucomisd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, base, offset); + } + + void divsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + void divsd_mr(int offset, RegisterID base, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, base, offset); + } + + void xorpd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src); + } + + void andnpd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp(OP2_ANDNPD_VpdWpd, (RegisterID)dst, (RegisterID)src); + } + + void sqrtsd_rr(XMMRegisterID src, XMMRegisterID dst) + { + m_formatter.prefix(PRE_SSE_F2); + m_formatter.twoByteOp(OP2_SQRTSD_VsdWsd, (RegisterID)dst, (RegisterID)src); + } + + // Misc instructions: + + void int3() + { + m_formatter.oneByteOp(OP_INT3); + } + + void ret() + { + m_formatter.oneByteOp(OP_RET); + } + + void predictNotTaken() + { + m_formatter.prefix(PRE_PREDICT_BRANCH_NOT_TAKEN); + } + + // Assembler admin methods: + + size_t codeSize() const + { + return m_formatter.codeSize(); + } + + AssemblerLabel labelForWatchpoint() + { + AssemblerLabel result = m_formatter.label(); + if (static_cast(result.m_offset) != m_indexOfLastWatchpoint) + result = label(); + m_indexOfLastWatchpoint = result.m_offset; + m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize(); + return result; + } + + AssemblerLabel labelIgnoringWatchpoints() + { + return m_formatter.label(); + } + + AssemblerLabel label() + { + AssemblerLabel result = m_formatter.label(); + while (UNLIKELY(static_cast(result.m_offset) < m_indexOfTailOfLastWatchpoint)) { + nop(); + result = m_formatter.label(); + } + return result; + } + + AssemblerLabel align(int alignment) + { + while (!m_formatter.isAligned(alignment)) + m_formatter.oneByteOp(OP_HLT); + + return label(); + } + + // Linking & patching: + // + // 'link' and 'patch' methods are for use on unprotected code - such as the code + // within the AssemblerBuffer, and code being patched by the patch buffer. Once + // code has been finalized it is (platform support permitting) within a non- + // writable region of memory; to modify the code in an execute-only execuable + // pool the 'repatch' and 'relink' methods should be used. + + void linkJump(AssemblerLabel from, AssemblerLabel to) + { + ASSERT(from.isSet()); + ASSERT(to.isSet()); + + char* code = reinterpret_cast(m_formatter.data()); + ASSERT(!reinterpret_cast(code + from.m_offset)[-1]); + setRel32(code + from.m_offset, code + to.m_offset); + } + + static void linkJump(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + + setRel32(reinterpret_cast(code) + from.m_offset, to); + } + + static void linkCall(void* code, AssemblerLabel from, void* to) + { + ASSERT(from.isSet()); + + setRel32(reinterpret_cast(code) + from.m_offset, to); + } + + static void linkPointer(void* code, AssemblerLabel where, void* value) + { + ASSERT(where.isSet()); + + setPointer(reinterpret_cast(code) + where.m_offset, value); + } + + static void relinkJump(void* from, void* to) + { + setRel32(from, to); + } + + static void relinkCall(void* from, void* to) + { + setRel32(from, to); + } + + static void repatchCompact(void* where, int32_t value) + { + ASSERT(value >= std::numeric_limits::min()); + ASSERT(value <= std::numeric_limits::max()); + setInt8(where, value); + } + + static void repatchInt32(void* where, int32_t value) + { + setInt32(where, value); + } + + static void repatchPointer(void* where, void* value) + { + setPointer(where, value); + } + + static void* readPointer(void* where) + { + return reinterpret_cast(where)[-1]; + } + + static void replaceWithJump(void* instructionStart, void* to) + { + uint8_t* ptr = reinterpret_cast(instructionStart); + uint8_t* dstPtr = reinterpret_cast(to); + intptr_t distance = (intptr_t)(dstPtr - (ptr + 5)); + ptr[0] = static_cast(OP_JMP_rel32); + *reinterpret_cast(ptr + 1) = static_cast(distance); + } + + static ptrdiff_t maxJumpReplacementSize() + { + return 5; + } + +#if CPU(X86_64) + static void revertJumpTo_movq_i64r(void* instructionStart, int64_t imm, RegisterID dst) + { + const int rexBytes = 1; + const int opcodeBytes = 1; + ASSERT(rexBytes + opcodeBytes <= maxJumpReplacementSize()); + uint8_t* ptr = reinterpret_cast(instructionStart); + ptr[0] = PRE_REX | (1 << 3) | (dst >> 3); + ptr[1] = OP_MOV_EAXIv | (dst & 7); + + union { + uint64_t asWord; + uint8_t asBytes[8]; + } u; + u.asWord = imm; + for (unsigned i = rexBytes + opcodeBytes; i < static_cast(maxJumpReplacementSize()); ++i) + ptr[i] = u.asBytes[i - rexBytes - opcodeBytes]; + } +#endif + + static void revertJumpTo_cmpl_ir_force32(void* instructionStart, int32_t imm, RegisterID dst) + { + const int opcodeBytes = 1; + const int modRMBytes = 1; + ASSERT(opcodeBytes + modRMBytes <= maxJumpReplacementSize()); + uint8_t* ptr = reinterpret_cast(instructionStart); + ptr[0] = OP_GROUP1_EvIz; + ptr[1] = (X86InstructionFormatter::ModRmRegister << 6) | (GROUP1_OP_CMP << 3) | dst; + union { + uint32_t asWord; + uint8_t asBytes[4]; + } u; + u.asWord = imm; + for (unsigned i = opcodeBytes + modRMBytes; i < static_cast(maxJumpReplacementSize()); ++i) + ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes]; + } + + static void revertJumpTo_cmpl_im_force32(void* instructionStart, int32_t imm, int offset, RegisterID dst) + { + ASSERT_UNUSED(offset, !offset); + const int opcodeBytes = 1; + const int modRMBytes = 1; + ASSERT(opcodeBytes + modRMBytes <= maxJumpReplacementSize()); + uint8_t* ptr = reinterpret_cast(instructionStart); + ptr[0] = OP_GROUP1_EvIz; + ptr[1] = (X86InstructionFormatter::ModRmMemoryNoDisp << 6) | (GROUP1_OP_CMP << 3) | dst; + union { + uint32_t asWord; + uint8_t asBytes[4]; + } u; + u.asWord = imm; + for (unsigned i = opcodeBytes + modRMBytes; i < static_cast(maxJumpReplacementSize()); ++i) + ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes]; + } + + static void replaceWithLoad(void* instructionStart) + { + uint8_t* ptr = reinterpret_cast(instructionStart); +#if CPU(X86_64) + if ((*ptr & ~15) == PRE_REX) + ptr++; +#endif + switch (*ptr) { + case OP_MOV_GvEv: + break; + case OP_LEA: + *ptr = OP_MOV_GvEv; + break; + default: + ASSERT_NOT_REACHED(); + } + } + + static void replaceWithAddressComputation(void* instructionStart) + { + uint8_t* ptr = reinterpret_cast(instructionStart); +#if CPU(X86_64) + if ((*ptr & ~15) == PRE_REX) + ptr++; +#endif + switch (*ptr) { + case OP_MOV_GvEv: + *ptr = OP_LEA; + break; + case OP_LEA: + break; + default: + ASSERT_NOT_REACHED(); + } + } + + static unsigned getCallReturnOffset(AssemblerLabel call) + { + ASSERT(call.isSet()); + return call.m_offset; + } + + static void* getRelocatedAddress(void* code, AssemblerLabel label) + { + ASSERT(label.isSet()); + return reinterpret_cast(reinterpret_cast(code) + label.m_offset); + } + + static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b) + { + return b.m_offset - a.m_offset; + } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + return m_formatter.executableCopy(globalData, ownerUID, effort); + } + + unsigned debugOffset() { return m_formatter.debugOffset(); } + + void nop() + { + m_formatter.oneByteOp(OP_NOP); + } + + // This is a no-op on x86 + ALWAYS_INLINE static void cacheFlush(void*, size_t) { } + +private: + + static void setPointer(void* where, void* value) + { + reinterpret_cast(where)[-1] = value; + } + + static void setInt32(void* where, int32_t value) + { + reinterpret_cast(where)[-1] = value; + } + + static void setInt8(void* where, int8_t value) + { + reinterpret_cast(where)[-1] = value; + } + + static void setRel32(void* from, void* to) + { + intptr_t offset = reinterpret_cast(to) - reinterpret_cast(from); + ASSERT(offset == static_cast(offset)); + + setInt32(from, offset); + } + + class X86InstructionFormatter { + + static const int maxInstructionSize = 16; + + public: + + enum ModRmMode { + ModRmMemoryNoDisp, + ModRmMemoryDisp8, + ModRmMemoryDisp32, + ModRmRegister, + }; + + // Legacy prefix bytes: + // + // These are emmitted prior to the instruction. + + void prefix(OneByteOpcodeID pre) + { + m_buffer.putByte(pre); + } + + // Word-sized operands / no operand instruction formatters. + // + // In addition to the opcode, the following operand permutations are supported: + // * None - instruction takes no operands. + // * One register - the low three bits of the RegisterID are added into the opcode. + // * Two registers - encode a register form ModRm (for all ModRm formats, the reg field is passed first, and a GroupOpcodeID may be passed in its place). + // * Three argument ModRM - a register, and a register and an offset describing a memory operand. + // * Five argument ModRM - a register, and a base register, an index, scale, and offset describing a memory operand. + // + // For 32-bit x86 targets, the address operand may also be provided as a void*. + // On 64-bit targets REX prefixes will be planted as necessary, where high numbered registers are used. + // + // The twoByteOp methods plant two-byte Intel instructions sequences (first opcode byte 0x0F). + + void oneByteOp(OneByteOpcodeID opcode) + { + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putByteUnchecked(opcode); + } + + void oneByteOp(OneByteOpcodeID opcode, RegisterID reg) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(0, 0, reg); + m_buffer.putByteUnchecked(opcode + (reg & 7)); + } + + void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, rm); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, offset); + } + + void oneByteOp_disp32(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp32(reg, base, offset); + } + + void oneByteOp_disp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp8(reg, base, offset); + } + + void oneByteOp(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, index, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, index, scale, offset); + } + +#if !CPU(X86_64) + void oneByteOp(OneByteOpcodeID opcode, int reg, const void* address) + { + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, address); + } +#endif + + void twoByteOp(TwoByteOpcodeID opcode) + { + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + } + + void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, rm); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, 0, base); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, offset); + } + + void twoByteOp(TwoByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIfNeeded(reg, index, base); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, index, scale, offset); + } + +#if !CPU(X86_64) + void twoByteOp(TwoByteOpcodeID opcode, int reg, const void* address) + { + m_buffer.ensureSpace(maxInstructionSize); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, address); + } +#endif + +#if CPU(X86_64) + // Quad-word-sized operands: + // + // Used to format 64-bit operantions, planting a REX.w prefix. + // When planting d64 or f64 instructions, not requiring a REX.w prefix, + // the normal (non-'64'-postfixed) formatters should be used. + + void oneByteOp64(OneByteOpcodeID opcode) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(0, 0, 0); + m_buffer.putByteUnchecked(opcode); + } + + void oneByteOp64(OneByteOpcodeID opcode, RegisterID reg) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(0, 0, reg); + m_buffer.putByteUnchecked(opcode + (reg & 7)); + } + + void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, rm); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, offset); + } + + void oneByteOp64_disp32(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp32(reg, base, offset); + } + + void oneByteOp64_disp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM_disp8(reg, base, offset); + } + + void oneByteOp64(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, index, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, index, scale, offset); + } + + void twoByteOp64(TwoByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexW(reg, 0, rm); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } +#endif + + // Byte-operands: + // + // These methods format byte operations. Byte operations differ from the normal + // formatters in the circumstances under which they will decide to emit REX prefixes. + // These should be used where any register operand signifies a byte register. + // + // The disctinction is due to the handling of register numbers in the range 4..7 on + // x86-64. These register numbers may either represent the second byte of the first + // four registers (ah..bh) or the first byte of the second four registers (spl..dil). + // + // Since ah..bh cannot be used in all permutations of operands (specifically cannot + // be accessed where a REX prefix is present), these are likely best treated as + // deprecated. In order to ensure the correct registers spl..dil are selected a + // REX prefix will be emitted for any byte register operand in the range 4..15. + // + // These formatters may be used in instructions where a mix of operand sizes, in which + // case an unnecessary REX will be emitted, for example: + // movzbl %al, %edi + // In this case a REX will be planted since edi is 7 (and were this a byte operand + // a REX would be required to specify dil instead of bh). Unneeded REX prefixes will + // be silently ignored by the processor. + // + // Address operands should still be checked using regRequiresRex(), while byteRegRequiresRex() + // is provided to check byte register operands. + + void oneByteOp8(OneByteOpcodeID opcode, GroupOpcodeID groupOp, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(rm), 0, 0, rm); + m_buffer.putByteUnchecked(opcode); + registerModRM(groupOp, rm); + } + + void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(reg) || byteRegRequiresRex(rm), reg, 0, rm); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(reg) || regRequiresRex(index) || regRequiresRex(base), reg, index, base); + m_buffer.putByteUnchecked(opcode); + memoryModRM(reg, base, index, scale, offset); + } + + void twoByteOp8(TwoByteOpcodeID opcode, RegisterID reg, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + registerModRM(reg, rm); + } + + void twoByteOp8(TwoByteOpcodeID opcode, GroupOpcodeID groupOp, RegisterID rm) + { + m_buffer.ensureSpace(maxInstructionSize); + emitRexIf(byteRegRequiresRex(rm), 0, 0, rm); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + registerModRM(groupOp, rm); + } + + // Immediates: + // + // An immedaite should be appended where appropriate after an op has been emitted. + // The writes are unchecked since the opcode formatters above will have ensured space. + + void immediate8(int imm) + { + m_buffer.putByteUnchecked(imm); + } + + void immediate16(int imm) + { + m_buffer.putShortUnchecked(imm); + } + + void immediate32(int imm) + { + m_buffer.putIntUnchecked(imm); + } + + void immediate64(int64_t imm) + { + m_buffer.putInt64Unchecked(imm); + } + + AssemblerLabel immediateRel32() + { + m_buffer.putIntUnchecked(0); + return label(); + } + + // Administrative methods: + + size_t codeSize() const { return m_buffer.codeSize(); } + AssemblerLabel label() const { return m_buffer.label(); } + bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); } + void* data() const { return m_buffer.data(); } + + PassRefPtr executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort) + { + return m_buffer.executableCopy(globalData, ownerUID, effort); + } + + unsigned debugOffset() { return m_buffer.debugOffset(); } + + private: + + // Internals; ModRm and REX formatters. + + static const RegisterID noBase = X86Registers::ebp; + static const RegisterID hasSib = X86Registers::esp; + static const RegisterID noIndex = X86Registers::esp; +#if CPU(X86_64) + static const RegisterID noBase2 = X86Registers::r13; + static const RegisterID hasSib2 = X86Registers::r12; + + // Registers r8 & above require a REX prefixe. + inline bool regRequiresRex(int reg) + { + return (reg >= X86Registers::r8); + } + + // Byte operand register spl & above require a REX prefix (to prevent the 'H' registers be accessed). + inline bool byteRegRequiresRex(int reg) + { + return (reg >= X86Registers::esp); + } + + // Format a REX prefix byte. + inline void emitRex(bool w, int r, int x, int b) + { + ASSERT(r >= 0); + ASSERT(x >= 0); + ASSERT(b >= 0); + m_buffer.putByteUnchecked(PRE_REX | ((int)w << 3) | ((r>>3)<<2) | ((x>>3)<<1) | (b>>3)); + } + + // Used to plant a REX byte with REX.w set (for 64-bit operations). + inline void emitRexW(int r, int x, int b) + { + emitRex(true, r, x, b); + } + + // Used for operations with byte operands - use byteRegRequiresRex() to check register operands, + // regRequiresRex() to check other registers (i.e. address base & index). + inline void emitRexIf(bool condition, int r, int x, int b) + { + if (condition) emitRex(false, r, x, b); + } + + // Used for word sized operations, will plant a REX prefix if necessary (if any register is r8 or above). + inline void emitRexIfNeeded(int r, int x, int b) + { + emitRexIf(regRequiresRex(r) || regRequiresRex(x) || regRequiresRex(b), r, x, b); + } +#else + // No REX prefix bytes on 32-bit x86. + inline bool regRequiresRex(int) { return false; } + inline bool byteRegRequiresRex(int) { return false; } + inline void emitRexIf(bool, int, int, int) {} + inline void emitRexIfNeeded(int, int, int) {} +#endif + + void putModRm(ModRmMode mode, int reg, RegisterID rm) + { + m_buffer.putByteUnchecked((mode << 6) | ((reg & 7) << 3) | (rm & 7)); + } + + void putModRmSib(ModRmMode mode, int reg, RegisterID base, RegisterID index, int scale) + { + ASSERT(mode != ModRmRegister); + + putModRm(mode, reg, hasSib); + m_buffer.putByteUnchecked((scale << 6) | ((index & 7) << 3) | (base & 7)); + } + + void registerModRM(int reg, RegisterID rm) + { + putModRm(ModRmRegister, reg, rm); + } + + void memoryModRM(int reg, RegisterID base, int offset) + { + // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. +#if CPU(X86_64) + if ((base == hasSib) || (base == hasSib2)) { +#else + if (base == hasSib) { +#endif + if (!offset) // No need to check if the base is noBase, since we know it is hasSib! + putModRmSib(ModRmMemoryNoDisp, reg, base, noIndex, 0); + else if (CAN_SIGN_EXTEND_8_32(offset)) { + putModRmSib(ModRmMemoryDisp8, reg, base, noIndex, 0); + m_buffer.putByteUnchecked(offset); + } else { + putModRmSib(ModRmMemoryDisp32, reg, base, noIndex, 0); + m_buffer.putIntUnchecked(offset); + } + } else { +#if CPU(X86_64) + if (!offset && (base != noBase) && (base != noBase2)) +#else + if (!offset && (base != noBase)) +#endif + putModRm(ModRmMemoryNoDisp, reg, base); + else if (CAN_SIGN_EXTEND_8_32(offset)) { + putModRm(ModRmMemoryDisp8, reg, base); + m_buffer.putByteUnchecked(offset); + } else { + putModRm(ModRmMemoryDisp32, reg, base); + m_buffer.putIntUnchecked(offset); + } + } + } + + void memoryModRM_disp8(int reg, RegisterID base, int offset) + { + // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. + ASSERT(CAN_SIGN_EXTEND_8_32(offset)); +#if CPU(X86_64) + if ((base == hasSib) || (base == hasSib2)) { +#else + if (base == hasSib) { +#endif + putModRmSib(ModRmMemoryDisp8, reg, base, noIndex, 0); + m_buffer.putByteUnchecked(offset); + } else { + putModRm(ModRmMemoryDisp8, reg, base); + m_buffer.putByteUnchecked(offset); + } + } + + void memoryModRM_disp32(int reg, RegisterID base, int offset) + { + // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there. +#if CPU(X86_64) + if ((base == hasSib) || (base == hasSib2)) { +#else + if (base == hasSib) { +#endif + putModRmSib(ModRmMemoryDisp32, reg, base, noIndex, 0); + m_buffer.putIntUnchecked(offset); + } else { + putModRm(ModRmMemoryDisp32, reg, base); + m_buffer.putIntUnchecked(offset); + } + } + + void memoryModRM(int reg, RegisterID base, RegisterID index, int scale, int offset) + { + ASSERT(index != noIndex); + +#if CPU(X86_64) + if (!offset && (base != noBase) && (base != noBase2)) +#else + if (!offset && (base != noBase)) +#endif + putModRmSib(ModRmMemoryNoDisp, reg, base, index, scale); + else if (CAN_SIGN_EXTEND_8_32(offset)) { + putModRmSib(ModRmMemoryDisp8, reg, base, index, scale); + m_buffer.putByteUnchecked(offset); + } else { + putModRmSib(ModRmMemoryDisp32, reg, base, index, scale); + m_buffer.putIntUnchecked(offset); + } + } + +#if !CPU(X86_64) + void memoryModRM(int reg, const void* address) + { + // noBase + ModRmMemoryNoDisp means noBase + ModRmMemoryDisp32! + putModRm(ModRmMemoryNoDisp, reg, noBase); + m_buffer.putIntUnchecked(reinterpret_cast(address)); + } +#endif + + AssemblerBuffer m_buffer; + } m_formatter; + int m_indexOfLastWatchpoint; + int m_indexOfTailOfLastWatchpoint; +}; + +} // namespace JSC + +#endif // ENABLE(ASSEMBLER) && CPU(X86) + +#endif // X86Assembler_h diff --git a/src/3rdparty/masm/config.h b/src/3rdparty/masm/config.h new file mode 100644 index 0000000000..5f59f311e3 --- /dev/null +++ b/src/3rdparty/masm/config.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MASM_CONFIG_H +#define MASM_CONFIG_H + +#include +#ifdef __cplusplus +#include +#include +#include +#include +#else +#include +#endif +#include + +#endif // MASM_CONFIG_H diff --git a/src/3rdparty/masm/create_regex_tables b/src/3rdparty/masm/create_regex_tables new file mode 100644 index 0000000000..bd799ba044 --- /dev/null +++ b/src/3rdparty/masm/create_regex_tables @@ -0,0 +1,121 @@ +# Copyright (C) 2010 Apple Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import sys + +types = { + "wordchar": { "UseTable" : True, "data": ['_', ('0','9'), ('A', 'Z'), ('a','z')]}, + "nonwordchar": { "UseTable" : True, "Inverse": "wordchar", "data": ['`', (0, ord('0') - 1), (ord('9') + 1, ord('A') - 1), (ord('Z') + 1, ord('_') - 1), (ord('z') + 1, 0xffff)]}, + "newline": { "UseTable" : False, "data": ['\n', '\r', 0x2028, 0x2029]}, + "spaces": { "UseTable" : True, "data": [' ', ('\t', '\r'), 0xa0, 0x1680, 0x180e, 0x2028, 0x2029, 0x202f, 0x205f, 0x3000, (0x2000, 0x200a), 0xfeff]}, + "nonspaces": { "UseTable" : True, "Inverse": "spaces", "data": [(0, ord('\t') - 1), (ord('\r') + 1, ord(' ') - 1), (ord(' ') + 1, 0x009f), (0x00a1, 0x167f), (0x1681, 0x180d), (0x180f, 0x1fff), (0x200b, 0x2027), (0x202a, 0x202e), (0x2030, 0x205e), (0x2060, 0x2fff), (0x3001, 0xfefe), (0xff00, 0xffff)]}, + "digits": { "UseTable" : False, "data": [('0', '9')]}, + "nondigits": { "UseTable" : False, "Inverse": "digits", "data": [(0, ord('0') - 1), (ord('9') + 1, 0xffff)] } +} +entriesPerLine = 50 +arrays = ""; +functions = ""; +emitTables = (len(sys.argv) < 2 or sys.argv[1] != "--no-tables") + +for name, classes in types.items(): + ranges = []; + size = 0; + for _class in classes["data"]: + if type(_class) == str: + ranges.append((ord(_class), ord(_class))) + elif type(_class) == int: + ranges.append((_class, _class)) + else: + (min, max) = _class; + if type(min) == str: + min = ord(min) + if type(max) == str: + max = ord(max) + if max > 0x7f and min <= 0x7f: + ranges.append((min, 0x7f)) + min = 0x80 + ranges.append((min,max)) + ranges.sort(); + + if emitTables and classes["UseTable"] and (not "Inverse" in classes): + array = ("static const char _%sData[65536] = {\n" % name); + i = 0 + for (min,max) in ranges: + while i < min: + i = i + 1 + array += ('0,') + if (i % entriesPerLine == 0) and (i != 0): + array += ('\n') + while i <= max: + i = i + 1 + if (i == 65536): + array += ("1") + else: + array += ('1,') + if (i % entriesPerLine == 0) and (i != 0): + array += ('\n') + while i < 0xffff: + array += ("0,") + i = i + 1; + if (i % entriesPerLine == 0) and (i != 0): + array += ('\n') + if i == 0xffff: + array += ("0") + array += ("\n};\n\n"); + arrays += array + + # Generate createFunction: + function = ""; + function += ("CharacterClass* %sCreate()\n" % name) + function += ("{\n") + if emitTables and classes["UseTable"]: + if "Inverse" in classes: + function += (" CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_%sData, true));\n" % (classes["Inverse"])) + else: + function += (" CharacterClass* characterClass = new CharacterClass(CharacterClassTable::create(_%sData, false));\n" % (name)) + else: + function += (" CharacterClass* characterClass = new CharacterClass(0);\n") + for (min, max) in ranges: + if (min == max): + if (min > 127): + function += (" characterClass->m_matchesUnicode.append(0x%04x);\n" % min) + else: + function += (" characterClass->m_matches.append(0x%02x);\n" % min) + continue + if (min > 127) or (max > 127): + function += (" characterClass->m_rangesUnicode.append(CharacterRange(0x%04x, 0x%04x));\n" % (min, max)) + else: + function += (" characterClass->m_ranges.append(CharacterRange(0x%02x, 0x%02x));\n" % (min, max)) + function += (" return characterClass;\n") + function += ("}\n\n") + functions += function + +if (len(sys.argv) > 1): + f = open(sys.argv[-1], "w") + f.write(arrays) + f.write(functions) + f.close() +else: + print(arrays) + print(functions) + diff --git a/src/3rdparty/masm/disassembler/Disassembler.cpp b/src/3rdparty/masm/disassembler/Disassembler.cpp new file mode 100644 index 0000000000..3fed2cdab8 --- /dev/null +++ b/src/3rdparty/masm/disassembler/Disassembler.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Disassembler.h" + +#include "MacroAssemblerCodeRef.h" +#include + +namespace JSC { + +void disassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const char* prefix, PrintStream& out) +{ + if (tryToDisassemble(codePtr, size, prefix, out)) + return; + + out.printf("%sdisassembly not available for range %p...%p\n", prefix, codePtr.executableAddress(), static_cast(codePtr.executableAddress()) + size); +} + +} // namespace JSC + diff --git a/src/3rdparty/masm/disassembler/Disassembler.h b/src/3rdparty/masm/disassembler/Disassembler.h new file mode 100644 index 0000000000..a087a657b3 --- /dev/null +++ b/src/3rdparty/masm/disassembler/Disassembler.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Disassembler_h +#define Disassembler_h + +#include +#include + +namespace JSC { + +class MacroAssemblerCodePtr; + +#if ENABLE(DISASSEMBLER) +bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, PrintStream&); +#else +inline bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char*, PrintStream&) +{ + return false; +} +#endif + +// Prints either the disassembly, or a line of text indicating that disassembly failed and +// the range of machine code addresses. +void disassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, PrintStream& out); + +} // namespace JSC + +#endif // Disassembler_h + diff --git a/src/3rdparty/masm/disassembler/UDis86Disassembler.cpp b/src/3rdparty/masm/disassembler/UDis86Disassembler.cpp new file mode 100644 index 0000000000..63c235b920 --- /dev/null +++ b/src/3rdparty/masm/disassembler/UDis86Disassembler.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Disassembler.h" + +#if USE(UDIS86) + +#include "MacroAssemblerCodeRef.h" +#include "udis86.h" + +namespace JSC { + +bool tryToDisassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const char* prefix, PrintStream& out) +{ + ud_t disassembler; + ud_init(&disassembler); + ud_set_input_buffer(&disassembler, static_cast(codePtr.executableAddress()), size); +#if CPU(X86_64) + ud_set_mode(&disassembler, 64); +#else + ud_set_mode(&disassembler, 32); +#endif + ud_set_pc(&disassembler, bitwise_cast(codePtr.executableAddress())); + ud_set_syntax(&disassembler, UD_SYN_ATT); + + uint64_t currentPC = disassembler.pc; + while (ud_disassemble(&disassembler)) { + char pcString[20]; + snprintf(pcString, sizeof(pcString), "0x%lx", static_cast(currentPC)); + out.printf("%s%16s: %s\n", prefix, pcString, ud_insn_asm(&disassembler)); + currentPC = disassembler.pc; + } + + return true; +} + +} // namespace JSC + +#endif // USE(UDIS86) + diff --git a/src/3rdparty/masm/disassembler/udis86/differences.txt b/src/3rdparty/masm/disassembler/udis86/differences.txt new file mode 100644 index 0000000000..dc225b6ffe --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/differences.txt @@ -0,0 +1,24 @@ +This documents the differences between the stock version of udis86 and the one found +here: + +- All files not named "udis86" were prefixed with "udis86". + +- assert() has been changed to ASSERT() + +- Mass rename of udis86_input.h inp_ prefixed functions and macros to ud_inp_ to + avoid namespace pollution. + +- Removal of KERNEL checks. + +- Added #include of udis86_extern.h in udis86_decode.c. + +- Removed s_ie__pause and s_ie__nop from udis86_decode.c, since they weren't used. + +- Made udis86_syn.h use WTF_ATTRIBUTE_PRINTF. This required making a bunch of little + fixes to make the compiler's format string warnings go away. + +- Made the code in udis86_syn.h use vsnprintf() instead of vsprintf(). + +- Fixed udis86_syn-att.c's jump destination printing to work correctly in 64-bit mode. + +- Add --outputDir option to itab.py. diff --git a/src/3rdparty/masm/disassembler/udis86/itab.py b/src/3rdparty/masm/disassembler/udis86/itab.py new file mode 100644 index 0000000000..07e20a6e10 --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/itab.py @@ -0,0 +1,360 @@ +# udis86 - scripts/itab.py +# +# Copyright (c) 2009 Vivek Thampi +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from optparse import OptionParser +import os +import sys + +sys.path.append( '../scripts' ); + +import ud_optable +import ud_opcode + +class UdItabGenerator( ud_opcode.UdOpcodeTables ): + + OperandDict = { + "Ap" : [ "OP_A" , "SZ_P" ], + "E" : [ "OP_E" , "SZ_NA" ], + "Eb" : [ "OP_E" , "SZ_B" ], + "Ew" : [ "OP_E" , "SZ_W" ], + "Ev" : [ "OP_E" , "SZ_V" ], + "Ed" : [ "OP_E" , "SZ_D" ], + "Eq" : [ "OP_E" , "SZ_Q" ], + "Ez" : [ "OP_E" , "SZ_Z" ], + "Ex" : [ "OP_E" , "SZ_MDQ" ], + "Ep" : [ "OP_E" , "SZ_P" ], + "G" : [ "OP_G" , "SZ_NA" ], + "Gb" : [ "OP_G" , "SZ_B" ], + "Gw" : [ "OP_G" , "SZ_W" ], + "Gv" : [ "OP_G" , "SZ_V" ], + "Gy" : [ "OP_G" , "SZ_MDQ" ], + "Gy" : [ "OP_G" , "SZ_MDQ" ], + "Gd" : [ "OP_G" , "SZ_D" ], + "Gq" : [ "OP_G" , "SZ_Q" ], + "Gx" : [ "OP_G" , "SZ_MDQ" ], + "Gz" : [ "OP_G" , "SZ_Z" ], + "M" : [ "OP_M" , "SZ_NA" ], + "Mb" : [ "OP_M" , "SZ_B" ], + "Mw" : [ "OP_M" , "SZ_W" ], + "Ms" : [ "OP_M" , "SZ_W" ], + "Md" : [ "OP_M" , "SZ_D" ], + "Mq" : [ "OP_M" , "SZ_Q" ], + "Mt" : [ "OP_M" , "SZ_T" ], + "Mo" : [ "OP_M" , "SZ_O" ], + "MwRv" : [ "OP_MR" , "SZ_WV" ], + "MdRy" : [ "OP_MR" , "SZ_DY" ], + "MbRv" : [ "OP_MR" , "SZ_BV" ], + "I1" : [ "OP_I1" , "SZ_NA" ], + "I3" : [ "OP_I3" , "SZ_NA" ], + "Ib" : [ "OP_I" , "SZ_B" ], + "Isb" : [ "OP_I" , "SZ_SB" ], + "Iw" : [ "OP_I" , "SZ_W" ], + "Iv" : [ "OP_I" , "SZ_V" ], + "Iz" : [ "OP_I" , "SZ_Z" ], + "Jv" : [ "OP_J" , "SZ_V" ], + "Jz" : [ "OP_J" , "SZ_Z" ], + "Jb" : [ "OP_J" , "SZ_B" ], + "R" : [ "OP_R" , "SZ_RDQ" ], + "C" : [ "OP_C" , "SZ_NA" ], + "D" : [ "OP_D" , "SZ_NA" ], + "S" : [ "OP_S" , "SZ_NA" ], + "Ob" : [ "OP_O" , "SZ_B" ], + "Ow" : [ "OP_O" , "SZ_W" ], + "Ov" : [ "OP_O" , "SZ_V" ], + "V" : [ "OP_V" , "SZ_O" ], + "W" : [ "OP_W" , "SZ_O" ], + "Wsd" : [ "OP_W" , "SZ_O" ], + "Wss" : [ "OP_W" , "SZ_O" ], + "P" : [ "OP_P" , "SZ_Q" ], + "Q" : [ "OP_Q" , "SZ_Q" ], + "VR" : [ "OP_VR" , "SZ_O" ], + "PR" : [ "OP_PR" , "SZ_Q" ], + "AL" : [ "OP_AL" , "SZ_NA" ], + "CL" : [ "OP_CL" , "SZ_NA" ], + "DL" : [ "OP_DL" , "SZ_NA" ], + "BL" : [ "OP_BL" , "SZ_NA" ], + "AH" : [ "OP_AH" , "SZ_NA" ], + "CH" : [ "OP_CH" , "SZ_NA" ], + "DH" : [ "OP_DH" , "SZ_NA" ], + "BH" : [ "OP_BH" , "SZ_NA" ], + "AX" : [ "OP_AX" , "SZ_NA" ], + "CX" : [ "OP_CX" , "SZ_NA" ], + "DX" : [ "OP_DX" , "SZ_NA" ], + "BX" : [ "OP_BX" , "SZ_NA" ], + "SI" : [ "OP_SI" , "SZ_NA" ], + "DI" : [ "OP_DI" , "SZ_NA" ], + "SP" : [ "OP_SP" , "SZ_NA" ], + "BP" : [ "OP_BP" , "SZ_NA" ], + "eAX" : [ "OP_eAX" , "SZ_NA" ], + "eCX" : [ "OP_eCX" , "SZ_NA" ], + "eDX" : [ "OP_eDX" , "SZ_NA" ], + "eBX" : [ "OP_eBX" , "SZ_NA" ], + "eSI" : [ "OP_eSI" , "SZ_NA" ], + "eDI" : [ "OP_eDI" , "SZ_NA" ], + "eSP" : [ "OP_eSP" , "SZ_NA" ], + "eBP" : [ "OP_eBP" , "SZ_NA" ], + "rAX" : [ "OP_rAX" , "SZ_NA" ], + "rCX" : [ "OP_rCX" , "SZ_NA" ], + "rBX" : [ "OP_rBX" , "SZ_NA" ], + "rDX" : [ "OP_rDX" , "SZ_NA" ], + "rSI" : [ "OP_rSI" , "SZ_NA" ], + "rDI" : [ "OP_rDI" , "SZ_NA" ], + "rSP" : [ "OP_rSP" , "SZ_NA" ], + "rBP" : [ "OP_rBP" , "SZ_NA" ], + "ES" : [ "OP_ES" , "SZ_NA" ], + "CS" : [ "OP_CS" , "SZ_NA" ], + "DS" : [ "OP_DS" , "SZ_NA" ], + "SS" : [ "OP_SS" , "SZ_NA" ], + "GS" : [ "OP_GS" , "SZ_NA" ], + "FS" : [ "OP_FS" , "SZ_NA" ], + "ST0" : [ "OP_ST0" , "SZ_NA" ], + "ST1" : [ "OP_ST1" , "SZ_NA" ], + "ST2" : [ "OP_ST2" , "SZ_NA" ], + "ST3" : [ "OP_ST3" , "SZ_NA" ], + "ST4" : [ "OP_ST4" , "SZ_NA" ], + "ST5" : [ "OP_ST5" , "SZ_NA" ], + "ST6" : [ "OP_ST6" , "SZ_NA" ], + "ST7" : [ "OP_ST7" , "SZ_NA" ], + "NONE" : [ "OP_NONE" , "SZ_NA" ], + "ALr8b" : [ "OP_ALr8b" , "SZ_NA" ], + "CLr9b" : [ "OP_CLr9b" , "SZ_NA" ], + "DLr10b" : [ "OP_DLr10b" , "SZ_NA" ], + "BLr11b" : [ "OP_BLr11b" , "SZ_NA" ], + "AHr12b" : [ "OP_AHr12b" , "SZ_NA" ], + "CHr13b" : [ "OP_CHr13b" , "SZ_NA" ], + "DHr14b" : [ "OP_DHr14b" , "SZ_NA" ], + "BHr15b" : [ "OP_BHr15b" , "SZ_NA" ], + "rAXr8" : [ "OP_rAXr8" , "SZ_NA" ], + "rCXr9" : [ "OP_rCXr9" , "SZ_NA" ], + "rDXr10" : [ "OP_rDXr10" , "SZ_NA" ], + "rBXr11" : [ "OP_rBXr11" , "SZ_NA" ], + "rSPr12" : [ "OP_rSPr12" , "SZ_NA" ], + "rBPr13" : [ "OP_rBPr13" , "SZ_NA" ], + "rSIr14" : [ "OP_rSIr14" , "SZ_NA" ], + "rDIr15" : [ "OP_rDIr15" , "SZ_NA" ], + "jWP" : [ "OP_J" , "SZ_WP" ], + "jDP" : [ "OP_J" , "SZ_DP" ], + + } + + # + # opcode prefix dictionary + # + PrefixDict = { + "aso" : "P_aso", + "oso" : "P_oso", + "rexw" : "P_rexw", + "rexb" : "P_rexb", + "rexx" : "P_rexx", + "rexr" : "P_rexr", + "seg" : "P_seg", + "inv64" : "P_inv64", + "def64" : "P_def64", + "depM" : "P_depM", + "cast1" : "P_c1", + "cast2" : "P_c2", + "cast3" : "P_c3", + "cast" : "P_cast", + "sext" : "P_sext" + } + + InvalidEntryIdx = 0 + InvalidEntry = { 'type' : 'invalid', + 'mnemonic' : 'invalid', + 'operands' : '', + 'prefixes' : '', + 'meta' : '' } + + Itab = [] # instruction table + ItabIdx = 1 # instruction table index + GtabIdx = 0 # group table index + GtabMeta = [] + + ItabLookup = {} + + MnemonicAliases = ( "invalid", "3dnow", "none", "db", "pause" ) + + def __init__( self, outputDir ): + # first itab entry (0) is Invalid + self.Itab.append( self.InvalidEntry ) + self.MnemonicsTable.extend( self.MnemonicAliases ) + self.outputDir = outputDir + + def toGroupId( self, id ): + return 0x8000 | id + + def genLookupTable( self, table, scope = '' ): + idxArray = [ ] + ( tabIdx, self.GtabIdx ) = ( self.GtabIdx, self.GtabIdx + 1 ) + self.GtabMeta.append( { 'type' : table[ 'type' ], 'meta' : table[ 'meta' ] } ) + + for _idx in range( self.sizeOfTable( table[ 'type' ] ) ): + idx = "%02x" % _idx + + e = self.InvalidEntry + i = self.InvalidEntryIdx + + if idx in table[ 'entries' ].keys(): + e = table[ 'entries' ][ idx ] + + # leaf node (insn) + if e[ 'type' ] == 'insn': + ( i, self.ItabIdx ) = ( self.ItabIdx, self.ItabIdx + 1 ) + self.Itab.append( e ) + elif e[ 'type' ] != 'invalid': + i = self.genLookupTable( e, 'static' ) + + idxArray.append( i ) + + name = "ud_itab__%s" % tabIdx + self.ItabLookup[ tabIdx ] = name + + self.ItabC.write( "\n" ); + if len( scope ): + self.ItabC.write( scope + ' ' ) + self.ItabC.write( "const uint16_t %s[] = {\n" % name ) + for i in range( len( idxArray ) ): + if i > 0 and i % 4 == 0: + self.ItabC.write( "\n" ) + if ( i%4 == 0 ): + self.ItabC.write( " /* %2x */" % i) + if idxArray[ i ] >= 0x8000: + self.ItabC.write( "%12s," % ("GROUP(%d)" % ( ~0x8000 & idxArray[ i ] ))) + else: + self.ItabC.write( "%12d," % ( idxArray[ i ] )) + self.ItabC.write( "\n" ) + self.ItabC.write( "};\n" ) + + return self.toGroupId( tabIdx ) + + def genLookupTableList( self ): + self.ItabC.write( "\n\n" ); + self.ItabC.write( "struct ud_lookup_table_list_entry ud_lookup_table_list[] = {\n" ) + for i in range( len( self.GtabMeta ) ): + f0 = self.ItabLookup[ i ] + "," + f1 = ( self.nameOfTable( self.GtabMeta[ i ][ 'type' ] ) ) + "," + f2 = "\"%s\"" % self.GtabMeta[ i ][ 'meta' ] + self.ItabC.write( " /* %03d */ { %s %s %s },\n" % ( i, f0, f1, f2 ) ) + self.ItabC.write( "};" ) + + def genInsnTable( self ): + self.ItabC.write( "struct ud_itab_entry ud_itab[] = {\n" ); + idx = 0 + for e in self.Itab: + opr_c = [ "O_NONE", "O_NONE", "O_NONE" ] + pfx_c = [] + opr = e[ 'operands' ] + for i in range(len(opr)): + if not (opr[i] in self.OperandDict.keys()): + print "error: invalid operand declaration: %s\n" % opr[i] + opr_c[i] = "O_" + opr[i] + opr = "%s %s %s" % (opr_c[0] + ",", opr_c[1] + ",", opr_c[2]) + + for p in e['prefixes']: + if not ( p in self.PrefixDict.keys() ): + print "error: invalid prefix specification: %s \n" % pfx + pfx_c.append( self.PrefixDict[p] ) + if len(e['prefixes']) == 0: + pfx_c.append( "P_none" ) + pfx = "|".join( pfx_c ) + + self.ItabC.write( " /* %04d */ { UD_I%s %s, %s },\n" \ + % ( idx, e[ 'mnemonic' ] + ',', opr, pfx ) ) + idx += 1 + self.ItabC.write( "};\n" ) + + self.ItabC.write( "\n\n" ); + self.ItabC.write( "const char * ud_mnemonics_str[] = {\n" ) + self.ItabC.write( ",\n ".join( [ "\"%s\"" % m for m in self.MnemonicsTable ] ) ) + self.ItabC.write( "\n};\n" ) + + + def genItabH( self ): + self.ItabH = open( os.path.join(self.outputDir, "udis86_itab.h"), "w" ) + + # Generate Table Type Enumeration + self.ItabH.write( "#ifndef UD_ITAB_H\n" ) + self.ItabH.write( "#define UD_ITAB_H\n\n" ) + + # table type enumeration + self.ItabH.write( "/* ud_table_type -- lookup table types (see lookup.c) */\n" ) + self.ItabH.write( "enum ud_table_type {\n " ) + enum = [ self.TableInfo[ k ][ 'name' ] for k in self.TableInfo.keys() ] + self.ItabH.write( ",\n ".join( enum ) ) + self.ItabH.write( "\n};\n\n" ); + + # mnemonic enumeration + self.ItabH.write( "/* ud_mnemonic -- mnemonic constants */\n" ) + enum = "enum ud_mnemonic_code {\n " + enum += ",\n ".join( [ "UD_I%s" % m for m in self.MnemonicsTable ] ) + enum += "\n} UD_ATTR_PACKED;\n" + self.ItabH.write( enum ) + self.ItabH.write( "\n" ) + + self.ItabH.write("\n/* itab entry operand definitions */\n"); + operands = self.OperandDict.keys() + operands.sort() + for o in operands: + self.ItabH.write("#define O_%-7s { %-12s %-8s }\n" % + (o, self.OperandDict[o][0] + ",", self.OperandDict[o][1])); + self.ItabH.write("\n\n"); + + self.ItabH.write( "extern const char * ud_mnemonics_str[];\n" ) + + self.ItabH.write( "#define GROUP(n) (0x8000 | (n))" ) + + self.ItabH.write( "\n#endif /* UD_ITAB_H */\n" ) + + self.ItabH.close() + + + def genItabC( self ): + self.ItabC = open( os.path.join(self.outputDir, "udis86_itab.c"), "w" ) + self.ItabC.write( "/* itab.c -- generated by itab.py, do no edit" ) + self.ItabC.write( " */\n" ); + self.ItabC.write( "#include \"udis86_decode.h\"\n\n" ); + + self.genLookupTable( self.OpcodeTable0 ) + self.genLookupTableList() + self.genInsnTable() + + self.ItabC.close() + + def genItab( self ): + self.genItabC() + self.genItabH() + +def main(): + parser = OptionParser() + parser.add_option("--outputDir", dest="outputDir", default="") + options, args = parser.parse_args() + generator = UdItabGenerator(os.path.normpath(options.outputDir)) + optableXmlParser = ud_optable.UdOptableXmlParser() + optableXmlParser.parse( args[ 0 ], generator.addInsnDef ) + + generator.genItab() + +if __name__ == '__main__': + main() diff --git a/src/3rdparty/masm/disassembler/udis86/optable.xml b/src/3rdparty/masm/disassembler/udis86/optable.xml new file mode 100644 index 0000000000..14b4ac5935 --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/optable.xml @@ -0,0 +1,8959 @@ + + + + + + aaa + + 37 + inv64 + + + + + aad + + d5 + Ib + inv64 + + + + + aam + + d4 + Ib + inv64 + + + + + aas + + 3f + inv64 + + + + + adc + + aso rexr rexx rexb + 10 + Eb Gb + + + aso oso rexw rexr rexx rexb + 11 + Ev Gv + + + aso rexr rexx rexb + 12 + Gb Eb + + + aso oso rexw rexr rexx rexb + 13 + Gv Ev + + + 14 + AL Ib + + + oso rexw + 15 + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=2 + Eb Ib + + + aso rexr rexx rexb + 82 /reg=2 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 81 /reg=2 + Ev Iz + sext + + + aso oso rexw rexr rexx rexb + 83 /reg=2 + Ev Ib + sext + + + + + add + + aso rexr rexx rexb + 00 + Eb Gb + + + aso oso rexw rexr rexx rexb + 01 + Ev Gv + + + aso rexr rexx rexb + 02 + Gb Eb + + + aso oso rexw rexr rexx rexb + 03 + Gv Ev + + + 04 + AL Ib + + + oso rexw + 05 + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=0 + Eb Ib + + + aso rexr rexx rexb + 82 /reg=0 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 81 /reg=0 + Ev Iz + sext + + + aso oso rexw rexr rexx rexb + 83 /reg=0 + Ev Ib + sext + + + + + + + addpd + + aso rexr rexx rexb + sse66 0f 58 + V W + + + + + addps + + aso rexr rexx rexb + 0f 58 + V W + + + + + addsd + + aso rexr rexx rexb + ssef2 0f 58 + V W + + + + + addss + + aso rexr rexx rexb + ssef3 0f 58 + V W + + + + + and + + aso rexr rexx rexb + 20 + Eb Gb + + + aso oso rexw rexr rexx rexb + 21 + Ev Gv + + + aso rexr rexx rexb + 22 + Gb Eb + + + aso oso rexw rexr rexx rexb + 23 + Gv Ev + + + 24 + AL Ib + + + oso rexw + 25 + rAX Iz + sext + + + aso rexw rexr rexx rexb + 80 /reg=4 + Eb Ib + + + aso rexr rexx rexb + 82 /reg=4 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 81 /reg=4 + Ev Iz + sext + + + aso oso rexw rexr rexx rexb + 83 /reg=4 + Ev Ib + sext + + + + + andpd + + aso rexr rexx rexb + sse66 0f 54 + V W + + + + + andps + + aso rexr rexx rexb + 0f 54 + V W + + + + + andnpd + + aso rexr rexx rexb + sse66 0f 55 + V W + + + + + andnps + + aso rexr rexx rexb + 0f 55 + V W + + + + + arpl + + aso + 63 /m=16 + Ew Gw + inv64 + + + aso + 63 /m=32 + Ew Gw + inv64 + + + + + movsxd + + aso oso rexw rexx rexr rexb + 63 /m=64 + Gv Ed + + + + + bound + + aso oso + 62 + Gv M + inv64 + + + + + bsf + + aso oso rexw rexr rexx rexb + 0f bc + Gv Ev + + + + + bsr + + aso oso rexw rexr rexx rexb + 0f bd + Gv Ev + + + + + bswap + + oso rexw rexb + 0f c8 + rAXr8 + + + oso rexw rexb + 0f c9 + rCXr9 + + + oso rexw rexb + 0f ca + rDXr10 + + + oso rexw rexb + 0f cb + rBXr11 + + + oso rexw rexb + 0f cc + rSPr12 + + + oso rexw rexb + 0f cd + rBPr13 + + + oso rexw rexb + 0f ce + rSIr14 + + + oso rexw rexb + 0f cf + rDIr15 + + + + + bt + + aso oso rexw rexr rexx rexb + 0f ba /reg=4 + Ev Ib + + + aso oso rexw rexr rexx rexb + 0f a3 + Ev Gv + + + + + btc + + aso oso rexw rexr rexx rexb + 0f bb + Ev Gv + + + aso oso rexw rexr rexx rexb + 0f ba /reg=7 + Ev Ib + + + + + btr + + aso oso rexw rexr rexx rexb + 0f b3 + Ev Gv + + + aso oso rexw rexr rexx rexb + 0f ba /reg=6 + Ev Ib + + + + + bts + + aso oso rexw rexr rexx rexb + 0f ab + Ev Gv + + + aso oso rexw rexr rexx rexb + 0f ba /reg=5 + Ev Ib + + + + + call + + aso oso rexw rexr rexx rexb + ff /reg=2 + Ev + def64 + + + aso oso rexw rexr rexx rexb + ff /reg=3 + Ep + + + oso + e8 + Jz + def64 + + + oso + 9a + Ap + inv64 + + + + + cbw + + oso rexw + 98 /o=16 + + + + + cwde + + oso rexw + 98 /o=32 + + + + + cdqe + + oso rexw + 98 /o=64 + + + + + clc + + f8 + + + + + cld + + fc + + + + + clflush + + aso rexw rexr rexx rexb + 0f ae /reg=7 /mod=!11 + M + + + + + clgi + amd + + 0f 01 /reg=3 /mod=11 /rm=5 + + + + + cli + + fa + + + + + clts + + 0f 06 + + + + + cmc + + f5 + + + + + cmovo + + aso oso rexw rexr rexx rexb + 0f 40 + Gv Ev + + + + + cmovno + + aso oso rexw rexr rexx rexb + 0f 41 + Gv Ev + + + + + cmovb + + aso oso rexw rexr rexx rexb + 0f 42 + Gv Ev + + + + + cmovae + + aso oso rexw rexr rexx rexb + 0f 43 + Gv Ev + + + + + cmovz + + aso oso rexw rexr rexx rexb + 0f 44 + Gv Ev + + + + + cmovnz + + aso oso rexw rexr rexx rexb + 0f 45 + Gv Ev + + + + + cmovbe + + aso oso rexw rexr rexx rexb + 0f 46 + Gv Ev + + + + + cmova + + aso oso rexw rexr rexx rexb + 0f 47 + Gv Ev + + + + + cmovs + + aso oso rexw rexr rexx rexb + 0f 48 + Gv Ev + + + + + cmovns + + aso oso rexw rexr rexx rexb + 0f 49 + Gv Ev + + + + + cmovp + + aso oso rexw rexr rexx rexb + 0f 4a + Gv Ev + + + + + cmovnp + + aso oso rexw rexr rexx rexb + 0f 4b + Gv Ev + + + + + cmovl + + aso oso rexw rexr rexx rexb + 0f 4c + Gv Ev + + + + + cmovge + + aso oso rexw rexr rexx rexb + 0f 4d + Gv Ev + + + + + cmovle + + aso oso rexw rexr rexx rexb + 0f 4e + Gv Ev + + + + + cmovg + + aso oso rexw rexr rexx rexb + 0f 4f + Gv Ev + + + + + cmp + + aso rexr rexx rexb + 38 + Eb Gb + + + aso oso rexw rexr rexx rexb + 39 + Ev Gv + + + aso rexr rexx rexb + 3a + Gb Eb + + + aso oso rexw rexr rexx rexb + 3b + Gv Ev + + + 3c + AL Ib + + + oso rexw + 3d + rAX Iz + + + aso rexr rexx rexb + 80 /reg=7 + Eb Ib + + + aso rexr rexx rexb + 82 /reg=7 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 81 /reg=7 + Ev Iz + + + aso oso rexw rexr rexx rexb + 83 /reg=7 + Ev Ib + + + + + cmppd + + aso rexr rexx rexb + sse66 0f c2 + V W Ib + + + + + cmpps + + aso rexr rexx rexb + 0f c2 + V W Ib + + + + + cmpsb + + a6 + + + + + cmpsw + + oso rexw + a7 /o=16 + + + + + cmpsd + + oso rexw + a7 /o=32 + + + aso rexr rexx rexb + ssef2 0f c2 + V W Ib + + + + + cmpsq + + oso rexw + a7 /o=64 + + + + + cmpss + + aso rexr rexx rexb + ssef3 0f c2 + V W Ib + + + + + cmpxchg + + aso rexr rexx rexb + 0f b0 + Eb Gb + + + aso oso rexw rexr rexx rexb + 0f b1 + Ev Gv + + + + + cmpxchg8b + + aso rexr rexx rexb + 0f c7 /reg=1 + M + + + + + comisd + + aso rexr rexx rexb + sse66 0f 2f + V W + + + + + comiss + + aso rexr rexx rexb + 0f 2f + V W + + + + + cpuid + + 0f a2 + + + + + cvtdq2pd + + aso rexr rexx rexb + ssef3 0f e6 + V W + + + + + cvtdq2ps + + aso rexr rexx rexb + 0f 5b + V W + + + + + cvtpd2dq + + aso rexr rexx rexb + ssef2 0f e6 + V W + + + + + cvtpd2pi + + aso rexr rexx rexb + sse66 0f 2d + P W + + + + + cvtpd2ps + + aso rexr rexx rexb + sse66 0f 5a + V W + + + + + cvtpi2ps + + aso rexr rexx rexb + 0f 2a + V Q + + + + + cvtpi2pd + + aso rexr rexx rexb + sse66 0f 2a + V Q + + + + + cvtps2dq + + aso rexr rexx rexb + sse66 0f 5b + V W + + + + + cvtps2pi + + aso rexr rexx rexb + 0f 2d + P W + + + + + cvtps2pd + + aso rexr rexx rexb + 0f 5a + V W + + + + + cvtsd2si + + aso rexw rexr rexx rexb + ssef2 0f 2d + Gy W + + + + + cvtsd2ss + + aso rexr rexx rexb + ssef2 0f 5a + V W + + + + + cvtsi2ss + + aso rexw rexr rexx rexb + ssef3 0f 2a + V Ex + + + + + cvtss2si + + aso rexw rexr rexx rexb + ssef3 0f 2d + Gy W + + + + + cvtss2sd + + aso rexr rexx rexb + ssef3 0f 5a + V W + + + + + cvttpd2pi + + aso rexr rexx rexb + sse66 0f 2c + P W + + + + + cvttpd2dq + + aso rexr rexx rexb + sse66 0f e6 + V W + + + + + cvttps2dq + + aso rexr rexx rexb + ssef3 0f 5b + V W + + + + + cvttps2pi + + aso rexr rexx rexb + 0f 2c + P W + + + + + cvttsd2si + + aso rexw rexr rexx rexb + ssef2 0f 2c + Gy Wsd + + + + + cvtsi2sd + + aso rexw rexr rexx rexb + ssef2 0f 2a + V Ex + + + + + cvttss2si + + aso rexw rexr rexx rexb + ssef3 0f 2c + Gy Wsd + + + + + cwd + + oso rexw + 99 /o=16 + + + + + cdq + + oso rexw + 99 /o=32 + + + + + cqo + + oso rexw + 99 /o=64 + + + + + daa + + 27 + inv64 + + + + + das + + 2f + inv64 + + + + + dec + + oso + 48 + eAX + + + oso + 49 + eCX + + + oso + 4a + eDX + + + oso + 4b + eBX + + + oso + 4c + eSP + + + oso + 4d + eBP + + + oso + 4e + eSI + + + oso + 4f + eDI + + + aso rexw rexr rexx rexb + fe /reg=1 + Eb + + + aso oso rexw rexr rexx rexb + ff /reg=1 + Ev + + + + + div + + aso oso rexw rexr rexx rexb + f7 /reg=6 + Ev + + + aso rexw rexr rexx rexb + f6 /reg=6 + Eb + + + + + divpd + + aso rexr rexx rexb + sse66 0f 5e + V W + + + + + divps + + aso rexr rexx rexb + 0f 5e + V W + + + + + divsd + + aso rexr rexx rexb + ssef2 0f 5e + V W + + + + + divss + + aso rexr rexx rexb + ssef3 0f 5e + V W + + + + + emms + + 0f 77 + + + + + enter + + c8 + Iw Ib + def64 depM + + + + + f2xm1 + X87 + + d9 /mod=11 /x87=30 + + + + + fabs + X87 + + d9 /mod=11 /x87=21 + + + + + fadd + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=0 + Mq + + + aso rexr rexx rexb + d8 /mod=!11 /reg=0 + Md + + + dc /mod=11 /x87=00 + ST0 ST0 + + + dc /mod=11 /x87=01 + ST1 ST0 + + + dc /mod=11 /x87=02 + ST2 ST0 + + + dc /mod=11 /x87=03 + ST3 ST0 + + + dc /mod=11 /x87=04 + ST4 ST0 + + + dc /mod=11 /x87=05 + ST5 ST0 + + + dc /mod=11 /x87=06 + ST6 ST0 + + + dc /mod=11 /x87=07 + ST7 ST0 + + + d8 /mod=11 /x87=00 + ST0 ST0 + + + d8 /mod=11 /x87=01 + ST0 ST1 + + + d8 /mod=11 /x87=02 + ST0 ST2 + + + d8 /mod=11 /x87=03 + ST0 ST3 + + + d8 /mod=11 /x87=04 + ST0 ST4 + + + d8 /mod=11 /x87=05 + ST0 ST5 + + + d8 /mod=11 /x87=06 + ST0 ST6 + + + d8 /mod=11 /x87=07 + ST0 ST7 + + + + + faddp + X87 + + de /mod=11 /x87=00 + ST0 ST0 + + + de /mod=11 /x87=01 + ST1 ST0 + + + de /mod=11 /x87=02 + ST2 ST0 + + + de /mod=11 /x87=03 + ST3 ST0 + + + de /mod=11 /x87=04 + ST4 ST0 + + + de /mod=11 /x87=05 + ST5 ST0 + + + de /mod=11 /x87=06 + ST6 ST0 + + + de /mod=11 /x87=07 + ST7 ST0 + + + + + fbld + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=4 + Mt + + + + + fbstp + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=6 + Mt + + + + + fchs + X87 + + d9 /mod=11 /x87=20 + + + + + fclex + X87 + + db /mod=11 /x87=22 + + + + + fcmovb + X87 + + da /mod=11 /x87=00 + ST0 ST0 + + + da /mod=11 /x87=01 + ST0 ST1 + + + da /mod=11 /x87=02 + ST0 ST2 + + + da /mod=11 /x87=03 + ST0 ST3 + + + da /mod=11 /x87=04 + ST0 ST4 + + + da /mod=11 /x87=05 + ST0 ST5 + + + da /mod=11 /x87=06 + ST0 ST6 + + + da /mod=11 /x87=07 + ST0 ST7 + + + + + fcmove + X87 + + da /mod=11 /x87=08 + ST0 ST0 + + + da /mod=11 /x87=09 + ST0 ST1 + + + da /mod=11 /x87=0a + ST0 ST2 + + + da /mod=11 /x87=0b + ST0 ST3 + + + da /mod=11 /x87=0c + ST0 ST4 + + + da /mod=11 /x87=0d + ST0 ST5 + + + da /mod=11 /x87=0e + ST0 ST6 + + + da /mod=11 /x87=0f + ST0 ST7 + + + + + fcmovbe + X87 + + da /mod=11 /x87=10 + ST0 ST0 + + + da /mod=11 /x87=11 + ST0 ST1 + + + da /mod=11 /x87=12 + ST0 ST2 + + + da /mod=11 /x87=13 + ST0 ST3 + + + da /mod=11 /x87=14 + ST0 ST4 + + + da /mod=11 /x87=15 + ST0 ST5 + + + da /mod=11 /x87=16 + ST0 ST6 + + + da /mod=11 /x87=17 + ST0 ST7 + + + + + fcmovu + X87 + + da /mod=11 /x87=18 + ST0 ST0 + + + da /mod=11 /x87=19 + ST0 ST1 + + + da /mod=11 /x87=1a + ST0 ST2 + + + da /mod=11 /x87=1b + ST0 ST3 + + + da /mod=11 /x87=1c + ST0 ST4 + + + da /mod=11 /x87=1d + ST0 ST5 + + + da /mod=11 /x87=1e + ST0 ST6 + + + da /mod=11 /x87=1f + ST0 ST7 + + + + + fcmovnb + X87 + + db /mod=11 /x87=00 + ST0 ST0 + + + db /mod=11 /x87=01 + ST0 ST1 + + + db /mod=11 /x87=02 + ST0 ST2 + + + db /mod=11 /x87=03 + ST0 ST3 + + + db /mod=11 /x87=04 + ST0 ST4 + + + db /mod=11 /x87=05 + ST0 ST5 + + + db /mod=11 /x87=06 + ST0 ST6 + + + db /mod=11 /x87=07 + ST0 ST7 + + + + + fcmovne + X87 + + db /mod=11 /x87=08 + ST0 ST0 + + + db /mod=11 /x87=09 + ST0 ST1 + + + db /mod=11 /x87=0a + ST0 ST2 + + + db /mod=11 /x87=0b + ST0 ST3 + + + db /mod=11 /x87=0c + ST0 ST4 + + + db /mod=11 /x87=0d + ST0 ST5 + + + db /mod=11 /x87=0e + ST0 ST6 + + + db /mod=11 /x87=0f + ST0 ST7 + + + + + fcmovnbe + X87 + + db /mod=11 /x87=10 + ST0 ST0 + + + db /mod=11 /x87=11 + ST0 ST1 + + + db /mod=11 /x87=12 + ST0 ST2 + + + db /mod=11 /x87=13 + ST0 ST3 + + + db /mod=11 /x87=14 + ST0 ST4 + + + db /mod=11 /x87=15 + ST0 ST5 + + + db /mod=11 /x87=16 + ST0 ST6 + + + db /mod=11 /x87=17 + ST0 ST7 + + + + + fcmovnu + X87 + + db /mod=11 /x87=18 + ST0 ST0 + + + db /mod=11 /x87=19 + ST0 ST1 + + + db /mod=11 /x87=1a + ST0 ST2 + + + db /mod=11 /x87=1b + ST0 ST3 + + + db /mod=11 /x87=1c + ST0 ST4 + + + db /mod=11 /x87=1d + ST0 ST5 + + + db /mod=11 /x87=1e + ST0 ST6 + + + db /mod=11 /x87=1f + ST0 ST7 + + + + + fucomi + X87 + + db /mod=11 /x87=28 + ST0 ST0 + + + db /mod=11 /x87=29 + ST0 ST1 + + + db /mod=11 /x87=2a + ST0 ST2 + + + db /mod=11 /x87=2b + ST0 ST3 + + + db /mod=11 /x87=2c + ST0 ST4 + + + db /mod=11 /x87=2d + ST0 ST5 + + + db /mod=11 /x87=2e + ST0 ST6 + + + db /mod=11 /x87=2f + ST0 ST7 + + + + + fcom + X87 + + aso rexr rexx rexb + d8 /mod=!11 /reg=2 + Md + + + aso rexr rexx rexb + dc /mod=!11 /reg=2 + Mq + + + d8 /mod=11 /x87=10 + ST0 ST0 + + + d8 /mod=11 /x87=11 + ST0 ST1 + + + d8 /mod=11 /x87=12 + ST0 ST2 + + + d8 /mod=11 /x87=13 + ST0 ST3 + + + d8 /mod=11 /x87=14 + ST0 ST4 + + + d8 /mod=11 /x87=15 + ST0 ST5 + + + d8 /mod=11 /x87=16 + ST0 ST6 + + + d8 /mod=11 /x87=17 + ST0 ST7 + + + + + fcom2 + X87 UNDOC + + dc /mod=11 /x87=10 + ST0 + + + dc /mod=11 /x87=11 + ST1 + + + dc /mod=11 /x87=12 + ST2 + + + dc /mod=11 /x87=13 + ST3 + + + dc /mod=11 /x87=14 + ST4 + + + dc /mod=11 /x87=15 + ST5 + + + dc /mod=11 /x87=16 + ST6 + + + dc /mod=11 /x87=17 + ST7 + + + + + fcomp3 + X87 UNDOC + + dc /mod=11 /x87=18 + ST0 + + + dc /mod=11 /x87=19 + ST1 + + + dc /mod=11 /x87=1a + ST2 + + + dc /mod=11 /x87=1b + ST3 + + + dc /mod=11 /x87=1c + ST4 + + + dc /mod=11 /x87=1d + ST5 + + + dc /mod=11 /x87=1e + ST6 + + + dc /mod=11 /x87=1f + ST7 + + + + + fcomi + X87 + + db /mod=11 /x87=30 + ST0 ST0 + + + db /mod=11 /x87=31 + ST0 ST1 + + + db /mod=11 /x87=32 + ST0 ST2 + + + db /mod=11 /x87=33 + ST0 ST3 + + + db /mod=11 /x87=34 + ST0 ST4 + + + db /mod=11 /x87=35 + ST0 ST5 + + + db /mod=11 /x87=36 + ST0 ST6 + + + db /mod=11 /x87=37 + ST0 ST7 + + + + + fucomip + X87 + + df /mod=11 /x87=28 + ST0 ST0 + + + df /mod=11 /x87=29 + ST0 ST1 + + + df /mod=11 /x87=2a + ST0 ST2 + + + df /mod=11 /x87=2b + ST0 ST3 + + + df /mod=11 /x87=2c + ST0 ST4 + + + df /mod=11 /x87=2d + ST0 ST5 + + + df /mod=11 /x87=2e + ST0 ST6 + + + df /mod=11 /x87=2f + ST0 ST7 + + + + + fcomip + X87 + + df /mod=11 /x87=30 + ST0 ST0 + + + df /mod=11 /x87=31 + ST0 ST1 + + + df /mod=11 /x87=32 + ST0 ST2 + + + df /mod=11 /x87=33 + ST0 ST3 + + + df /mod=11 /x87=34 + ST0 ST4 + + + df /mod=11 /x87=35 + ST0 ST5 + + + df /mod=11 /x87=36 + ST0 ST6 + + + df /mod=11 /x87=37 + ST0 ST7 + + + + + fcomp + X87 + + aso rexr rexx rexb + d8 /mod=!11 /reg=3 + Md + + + aso rexr rexx rexb + dc /mod=!11 /reg=3 + Mq + + + d8 /mod=11 /x87=18 + ST0 ST0 + + + d8 /mod=11 /x87=19 + ST0 ST1 + + + d8 /mod=11 /x87=1a + ST0 ST2 + + + d8 /mod=11 /x87=1b + ST0 ST3 + + + d8 /mod=11 /x87=1c + ST0 ST4 + + + d8 /mod=11 /x87=1d + ST0 ST5 + + + d8 /mod=11 /x87=1e + ST0 ST6 + + + d8 /mod=11 /x87=1f + ST0 ST7 + + + + + fcomp5 + X87 UNDOC + + de /mod=11 /x87=10 + ST0 + + + de /mod=11 /x87=11 + ST1 + + + de /mod=11 /x87=12 + ST2 + + + de /mod=11 /x87=13 + ST3 + + + de /mod=11 /x87=14 + ST4 + + + de /mod=11 /x87=15 + ST5 + + + de /mod=11 /x87=16 + ST6 + + + de /mod=11 /x87=17 + ST7 + + + + + fcompp + X87 + + de /mod=11 /x87=19 + + + + + fcos + X87 + + d9 /mod=11 /x87=3f + + + + + fdecstp + X87 + + d9 /mod=11 /x87=36 + + + + + fdiv + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=6 + Mq + + + dc /mod=11 /x87=38 + ST0 ST0 + + + dc /mod=11 /x87=39 + ST1 ST0 + + + dc /mod=11 /x87=3a + ST2 ST0 + + + dc /mod=11 /x87=3b + ST3 ST0 + + + dc /mod=11 /x87=3c + ST4 ST0 + + + dc /mod=11 /x87=3d + ST5 ST0 + + + dc /mod=11 /x87=3e + ST6 ST0 + + + dc /mod=11 /x87=3f + ST7 ST0 + + + aso rexr rexx rexb + d8 /mod=!11 /reg=6 + Md + + + d8 /mod=11 /x87=30 + ST0 ST0 + + + d8 /mod=11 /x87=31 + ST0 ST1 + + + d8 /mod=11 /x87=32 + ST0 ST2 + + + d8 /mod=11 /x87=33 + ST0 ST3 + + + d8 /mod=11 /x87=34 + ST0 ST4 + + + d8 /mod=11 /x87=35 + ST0 ST5 + + + d8 /mod=11 /x87=36 + ST0 ST6 + + + d8 /mod=11 /x87=37 + ST0 ST7 + + + + + fdivp + X87 + + de /mod=11 /x87=38 + ST0 ST0 + + + de /mod=11 /x87=39 + ST1 ST0 + + + de /mod=11 /x87=3a + ST2 ST0 + + + de /mod=11 /x87=3b + ST3 ST0 + + + de /mod=11 /x87=3c + ST4 ST0 + + + de /mod=11 /x87=3d + ST5 ST0 + + + de /mod=11 /x87=3e + ST6 ST0 + + + de /mod=11 /x87=3f + ST7 ST0 + + + + + fdivr + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=7 + Mq + + + dc /mod=11 /x87=30 + ST0 ST0 + + + dc /mod=11 /x87=31 + ST1 ST0 + + + dc /mod=11 /x87=32 + ST2 ST0 + + + dc /mod=11 /x87=33 + ST3 ST0 + + + dc /mod=11 /x87=34 + ST4 ST0 + + + dc /mod=11 /x87=35 + ST5 ST0 + + + dc /mod=11 /x87=36 + ST6 ST0 + + + dc /mod=11 /x87=37 + ST7 ST0 + + + aso rexr rexx rexb + d8 /mod=!11 /reg=7 + Md + + + d8 /mod=11 /x87=38 + ST0 ST0 + + + d8 /mod=11 /x87=39 + ST0 ST1 + + + d8 /mod=11 /x87=3a + ST0 ST2 + + + d8 /mod=11 /x87=3b + ST0 ST3 + + + d8 /mod=11 /x87=3c + ST0 ST4 + + + d8 /mod=11 /x87=3d + ST0 ST5 + + + d8 /mod=11 /x87=3e + ST0 ST6 + + + d8 /mod=11 /x87=3f + ST0 ST7 + + + + + fdivrp + X87 + + de /mod=11 /x87=30 + ST0 ST0 + + + de /mod=11 /x87=31 + ST1 ST0 + + + de /mod=11 /x87=32 + ST2 ST0 + + + de /mod=11 /x87=33 + ST3 ST0 + + + de /mod=11 /x87=34 + ST4 ST0 + + + de /mod=11 /x87=35 + ST5 ST0 + + + de /mod=11 /x87=36 + ST6 ST0 + + + de /mod=11 /x87=37 + ST7 ST0 + + + + + femms + + 0f 0e + + + + + ffree + X87 + + dd /mod=11 /x87=00 + ST0 + + + dd /mod=11 /x87=01 + ST1 + + + dd /mod=11 /x87=02 + ST2 + + + dd /mod=11 /x87=03 + ST3 + + + dd /mod=11 /x87=04 + ST4 + + + dd /mod=11 /x87=05 + ST5 + + + dd /mod=11 /x87=06 + ST6 + + + dd /mod=11 /x87=07 + ST7 + + + + + ffreep + X87 + + df /mod=11 /x87=00 + ST0 + + + df /mod=11 /x87=01 + ST1 + + + df /mod=11 /x87=02 + ST2 + + + df /mod=11 /x87=03 + ST3 + + + df /mod=11 /x87=04 + ST4 + + + df /mod=11 /x87=05 + ST5 + + + df /mod=11 /x87=06 + ST6 + + + df /mod=11 /x87=07 + ST7 + + + + + ficom + X87 + + aso rexr rexx rexb + de /mod=!11 /reg=2 + Mw + + + aso rexr rexx rexb + da /mod=!11 /reg=2 + Md + + + + + ficomp + X87 + + aso rexr rexx rexb + de /mod=!11 /reg=3 + Mw + + + aso rexr rexx rexb + da /mod=!11 /reg=3 + Md + + + + + fild + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=0 + Mw + + + aso rexr rexx rexb + df /mod=!11 /reg=5 + Mq + + + aso rexr rexx rexb + db /mod=!11 /reg=0 + Md + + + + + fncstp + X87 + + d9 /mod=11 /x87=37 + + + + + fninit + X87 + + db /mod=11 /x87=23 + + + + + fiadd + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=0 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=0 + Mw + + + + + fidivr + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=7 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=7 + Mw + + + + + fidiv + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=6 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=6 + Mw + + + + + fisub + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=4 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=4 + Mw + + + + + fisubr + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=5 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=5 + Mw + + + + + fist + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=2 + Mw + + + aso rexr rexx rexb + db /mod=!11 /reg=2 + Md + + + + + fistp + X87 + + aso rexr rexx rexb + df /mod=!11 /reg=3 + Mw + + + aso rexr rexx rexb + df /mod=!11 /reg=7 + Mq + + + aso rexr rexx rexb + db /mod=!11 /reg=3 + Md + + + + + fisttp + X87 + + aso rexr rexx rexb + db /mod=!11 /reg=1 + Md + + + aso rexr rexx rexb + dd /mod=!11 /reg=1 + Mq + + + aso rexr rexx rexb + df /mod=!11 /reg=1 + Mw + + + + + fld + X87 + + aso rexr rexx rexb + db /mod=!11 /reg=5 + Mt + + + aso rexr rexx rexb + dd /mod=!11 /reg=0 + Mq + + + aso rexr rexx rexb + d9 /mod=!11 /reg=0 + Md + + + d9 /mod=11 /x87=00 + ST0 + + + d9 /mod=11 /x87=01 + ST1 + + + d9 /mod=11 /x87=02 + ST2 + + + d9 /mod=11 /x87=03 + ST3 + + + d9 /mod=11 /x87=04 + ST4 + + + d9 /mod=11 /x87=05 + ST5 + + + d9 /mod=11 /x87=06 + ST6 + + + d9 /mod=11 /x87=07 + ST7 + + + + + fld1 + X87 + + d9 /mod=11 /x87=28 + + + + + fldl2t + X87 + + d9 /mod=11 /x87=29 + + + + + fldl2e + X87 + + d9 /mod=11 /x87=2a + + + + + fldlpi + X87 + + d9 /mod=11 /x87=2b + + + + + fldlg2 + X87 + + d9 /mod=11 /x87=2c + + + + + fldln2 + X87 + + d9 /mod=11 /x87=2d + + + + + fldz + X87 + + d9 /mod=11 /x87=2e + + + + + fldcw + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=5 + Mw + + + + + fldenv + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=4 + M + + + + + fmul + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=1 + Mq + + + dc /mod=11 /x87=08 + ST0 ST0 + + + dc /mod=11 /x87=09 + ST1 ST0 + + + dc /mod=11 /x87=0a + ST2 ST0 + + + dc /mod=11 /x87=0b + ST3 ST0 + + + dc /mod=11 /x87=0c + ST4 ST0 + + + dc /mod=11 /x87=0d + ST5 ST0 + + + dc /mod=11 /x87=0e + ST6 ST0 + + + dc /mod=11 /x87=0f + ST7 ST0 + + + aso rexr rexx rexb + d8 /mod=!11 /reg=1 + Md + + + d8 /mod=11 /x87=08 + ST0 ST0 + + + d8 /mod=11 /x87=09 + ST0 ST1 + + + d8 /mod=11 /x87=0a + ST0 ST2 + + + d8 /mod=11 /x87=0b + ST0 ST3 + + + d8 /mod=11 /x87=0c + ST0 ST4 + + + d8 /mod=11 /x87=0d + ST0 ST5 + + + d8 /mod=11 /x87=0e + ST0 ST6 + + + d8 /mod=11 /x87=0f + ST0 ST7 + + + + + fmulp + X87 + + de /mod=11 /x87=08 + ST0 ST0 + + + de /mod=11 /x87=09 + ST1 ST0 + + + de /mod=11 /x87=0a + ST2 ST0 + + + de /mod=11 /x87=0b + ST3 ST0 + + + de /mod=11 /x87=0c + ST4 ST0 + + + de /mod=11 /x87=0d + ST5 ST0 + + + de /mod=11 /x87=0e + ST6 ST0 + + + de /mod=11 /x87=0f + ST7 ST0 + + + + + fimul + X87 + + aso rexr rexx rexb + da /mod=!11 /reg=1 + Md + + + aso rexr rexx rexb + de /mod=!11 /reg=1 + Mw + + + + + fnop + X87 + + d9 /mod=11 /x87=10 + + + + + fpatan + X87 + + d9 /mod=11 /x87=33 + + + + + fprem + X87 + + d9 /mod=11 /x87=38 + + + + + fprem1 + X87 + + d9 /mod=11 /x87=35 + + + + + fptan + X87 + + d9 /mod=11 /x87=32 + + + + + frndint + X87 + + d9 /mod=11 /x87=3c + + + + + frstor + X87 + + aso rexr rexx rexb + dd /mod=!11 /reg=4 + M + + + + + fnsave + X87 + + aso rexr rexx rexb + dd /mod=!11 /reg=6 + M + + + + + fscale + X87 + + d9 /mod=11 /x87=3d + + + + + fsin + X87 + + d9 /mod=11 /x87=3e + + + + + fsincos + X87 + + d9 /mod=11 /x87=3b + + + + + fsqrt + X87 + + d9 /mod=11 /x87=3a + + + + + fstp + X87 + + aso rexr rexx rexb + db /mod=!11 /reg=7 + Mt + + + aso rexr rexx rexb + dd /mod=!11 /reg=3 + Mq + + + aso rexr rexx rexb + d9 /mod=!11 /reg=3 + Md + + + dd /mod=11 /x87=18 + ST0 + + + dd /mod=11 /x87=19 + ST1 + + + dd /mod=11 /x87=1a + ST2 + + + dd /mod=11 /x87=1b + ST3 + + + dd /mod=11 /x87=1c + ST4 + + + dd /mod=11 /x87=1d + ST5 + + + dd /mod=11 /x87=1e + ST6 + + + dd /mod=11 /x87=1f + ST7 + + + + + fstp1 + + d9 /mod=11 /x87=18 + ST0 + + + d9 /mod=11 /x87=19 + ST1 + + + d9 /mod=11 /x87=1a + ST2 + + + d9 /mod=11 /x87=1b + ST3 + + + d9 /mod=11 /x87=1c + ST4 + + + d9 /mod=11 /x87=1d + ST5 + + + d9 /mod=11 /x87=1e + ST6 + + + d9 /mod=11 /x87=1f + ST7 + + + + + fstp8 + + df /mod=11 /x87=10 + ST0 + + + df /mod=11 /x87=11 + ST1 + + + df /mod=11 /x87=12 + ST2 + + + df /mod=11 /x87=13 + ST3 + + + df /mod=11 /x87=14 + ST4 + + + df /mod=11 /x87=15 + ST5 + + + df /mod=11 /x87=16 + ST6 + + + df /mod=11 /x87=17 + ST7 + + + + + fstp9 + + df /mod=11 /x87=18 + ST0 + + + df /mod=11 /x87=19 + ST1 + + + df /mod=11 /x87=1a + ST2 + + + df /mod=11 /x87=1b + ST3 + + + df /mod=11 /x87=1c + ST4 + + + df /mod=11 /x87=1d + ST5 + + + df /mod=11 /x87=1e + ST6 + + + df /mod=11 /x87=1f + ST7 + + + + + fst + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=2 + Md + + + aso rexr rexx rexb + dd /mod=!11 /reg=2 + Mq + + + dd /mod=11 /x87=10 + ST0 + + + dd /mod=11 /x87=11 + ST1 + + + dd /mod=11 /x87=12 + ST2 + + + dd /mod=11 /x87=13 + ST3 + + + dd /mod=11 /x87=14 + ST4 + + + dd /mod=11 /x87=15 + ST5 + + + dd /mod=11 /x87=16 + ST6 + + + dd /mod=11 /x87=17 + ST7 + + + + + fnstcw + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=7 + Mw + + + + + fnstenv + X87 + + aso rexr rexx rexb + d9 /mod=!11 /reg=6 + M + + + + + fnstsw + X87 + + aso rexr rexx rexb + dd /mod=!11 /reg=7 + Mw + + + df /mod=11 /x87=20 + AX + + + + + fsub + X87 + + aso rexr rexx rexb + d8 /mod=!11 /reg=4 + Md + + + aso rexr rexx rexb + dc /mod=!11 /reg=4 + Mq + + + d8 /mod=11 /x87=20 + ST0 ST0 + + + d8 /mod=11 /x87=21 + ST0 ST1 + + + d8 /mod=11 /x87=22 + ST0 ST2 + + + d8 /mod=11 /x87=23 + ST0 ST3 + + + d8 /mod=11 /x87=24 + ST0 ST4 + + + d8 /mod=11 /x87=25 + ST0 ST5 + + + d8 /mod=11 /x87=26 + ST0 ST6 + + + d8 /mod=11 /x87=27 + ST0 ST7 + + + dc /mod=11 /x87=28 + ST0 ST0 + + + dc /mod=11 /x87=29 + ST1 ST0 + + + dc /mod=11 /x87=2a + ST2 ST0 + + + dc /mod=11 /x87=2b + ST3 ST0 + + + dc /mod=11 /x87=2c + ST4 ST0 + + + dc /mod=11 /x87=2d + ST5 ST0 + + + dc /mod=11 /x87=2e + ST6 ST0 + + + dc /mod=11 /x87=2f + ST7 ST0 + + + + + fsubp + X87 + + de /mod=11 /x87=28 + ST0 ST0 + + + de /mod=11 /x87=29 + ST1 ST0 + + + de /mod=11 /x87=2a + ST2 ST0 + + + de /mod=11 /x87=2b + ST3 ST0 + + + de /mod=11 /x87=2c + ST4 ST0 + + + de /mod=11 /x87=2d + ST5 ST0 + + + de /mod=11 /x87=2e + ST6 ST0 + + + de /mod=11 /x87=2f + ST7 ST0 + + + + + fsubr + X87 + + aso rexr rexx rexb + dc /mod=!11 /reg=5 + Mq + + + d8 /mod=11 /x87=28 + ST0 ST0 + + + d8 /mod=11 /x87=29 + ST0 ST1 + + + d8 /mod=11 /x87=2a + ST0 ST2 + + + d8 /mod=11 /x87=2b + ST0 ST3 + + + d8 /mod=11 /x87=2c + ST0 ST4 + + + d8 /mod=11 /x87=2d + ST0 ST5 + + + d8 /mod=11 /x87=2e + ST0 ST6 + + + d8 /mod=11 /x87=2f + ST0 ST7 + + + dc /mod=11 /x87=20 + ST0 ST0 + + + dc /mod=11 /x87=21 + ST1 ST0 + + + dc /mod=11 /x87=22 + ST2 ST0 + + + dc /mod=11 /x87=23 + ST3 ST0 + + + dc /mod=11 /x87=24 + ST4 ST0 + + + dc /mod=11 /x87=25 + ST5 ST0 + + + dc /mod=11 /x87=26 + ST6 ST0 + + + dc /mod=11 /x87=27 + ST7 ST0 + + + aso rexr rexx rexb + d8 /mod=!11 /reg=5 + Md + + + + + fsubrp + X87 + + de /mod=11 /x87=20 + ST0 ST0 + + + de /mod=11 /x87=21 + ST1 ST0 + + + de /mod=11 /x87=22 + ST2 ST0 + + + de /mod=11 /x87=23 + ST3 ST0 + + + de /mod=11 /x87=24 + ST4 ST0 + + + de /mod=11 /x87=25 + ST5 ST0 + + + de /mod=11 /x87=26 + ST6 ST0 + + + de /mod=11 /x87=27 + ST7 ST0 + + + + + ftst + X87 + + d9 /mod=11 /x87=24 + + + + + fucom + X87 + + dd /mod=11 /x87=20 + ST0 + + + dd /mod=11 /x87=21 + ST1 + + + dd /mod=11 /x87=22 + ST2 + + + dd /mod=11 /x87=23 + ST3 + + + dd /mod=11 /x87=24 + ST4 + + + dd /mod=11 /x87=25 + ST5 + + + dd /mod=11 /x87=26 + ST6 + + + dd /mod=11 /x87=27 + ST7 + + + + + fucomp + X87 + + dd /mod=11 /x87=28 + ST0 + + + dd /mod=11 /x87=29 + ST1 + + + dd /mod=11 /x87=2a + ST2 + + + dd /mod=11 /x87=2b + ST3 + + + dd /mod=11 /x87=2c + ST4 + + + dd /mod=11 /x87=2d + ST5 + + + dd /mod=11 /x87=2e + ST6 + + + dd /mod=11 /x87=2f + ST7 + + + + + fucompp + X87 + + da /mod=11 /x87=29 + + + + + fxam + X87 + + d9 /mod=11 /x87=25 + + + + + fxch + X87 + + d9 /mod=11 /x87=08 + ST0 ST0 + + + d9 /mod=11 /x87=09 + ST0 ST1 + + + d9 /mod=11 /x87=0a + ST0 ST2 + + + d9 /mod=11 /x87=0b + ST0 ST3 + + + d9 /mod=11 /x87=0c + ST0 ST4 + + + d9 /mod=11 /x87=0d + ST0 ST5 + + + d9 /mod=11 /x87=0e + ST0 ST6 + + + d9 /mod=11 /x87=0f + ST0 ST7 + + + + + fxch4 + X87 + + dd /mod=11 /x87=08 + ST0 + + + dd /mod=11 /x87=09 + ST1 + + + dd /mod=11 /x87=0a + ST2 + + + dd /mod=11 /x87=0b + ST3 + + + dd /mod=11 /x87=0c + ST4 + + + dd /mod=11 /x87=0d + ST5 + + + dd /mod=11 /x87=0e + ST6 + + + dd /mod=11 /x87=0f + ST7 + + + + + fxch7 + X87 + + df /mod=11 /x87=08 + ST0 + + + df /mod=11 /x87=09 + ST1 + + + df /mod=11 /x87=0a + ST2 + + + df /mod=11 /x87=0b + ST3 + + + df /mod=11 /x87=0c + ST4 + + + df /mod=11 /x87=0d + ST5 + + + df /mod=11 /x87=0e + ST6 + + + df /mod=11 /x87=0f + ST7 + + + + + fxrstor + + aso rexw rexr rexx rexb + 0f ae /mod=11 /reg=1 + M + + + + + fxsave + + aso rexw rexr rexx rexb + 0f ae /mod=11 /reg=0 + M + + + + + fpxtract + X87 + + d9 /mod=11 /x87=34 + + + + + fyl2x + X87 + + d9 /mod=11 /x87=31 + + + + + fyl2xp1 + X87 + + d9 /mod=11 /x87=39 + + + + + hlt + + f4 + + + + + idiv + + aso oso rexw rexr rexx rexb + f7 /reg=7 + Ev + + + aso rexw rexr rexx rexb + f6 /reg=7 + Eb + + + + + in + + e4 + AL Ib + + + oso + e5 + eAX Ib + + + ec + AL DX + + + oso + ed + eAX DX + + + + + imul + + aso oso rexw rexr rexx rexb + 0f af + Gv Ev + + + aso rexw rexr rexx rexb + f6 /reg=5 + Eb + + + aso oso rexw rexr rexx rexb + f7 /reg=5 + Ev + + + aso oso rexw rexr rexx rexb + 69 + Gv Ev Iz + sext + + + aso oso rexw rexr rexx rexb + 6b + Gv Ev Ib + sext + + + + + inc + + oso + 40 + eAX + + + oso + 41 + eCX + + + oso + 42 + eDX + + + oso + 43 + eBX + + + oso + 44 + eSP + + + oso + 45 + eBP + + + oso + 46 + eSI + + + oso + 47 + eDI + + + aso oso rexw rexr rexx rexb + ff /reg=0 + Ev + + + aso rexw rexr rexx rexb + fe /reg=0 + Eb + + + + + insb + + 6c + + + + + insw + + oso + 6d /o=16 + + + + + insd + + oso + 6d /o=32 + + + + + int1 + + f1 + + + + + int3 + + cc + + + + + int + + cd + Ib + + + + + into + + ce + inv64 + + + + + invd + + 0f 08 + + + + + invept + intel + + sse66 0f 38 80 /m=32 + Gd Mo + + + sse66 0f 38 80 /m=64 + Gq Mo + + + + + invlpg + + aso rexr rexx rexb + 0f 01 /reg=7 /mod=!11 + M + + + + + invlpga + amd + + 0f 01 /reg=3 /mod=11 /rm=7 + + + + + invvpid + intel + + sse66 0f 38 81 /m=32 + Gd Mo + + + sse66 0f 38 81 /m=64 + Gq Mo + + + + + iretw + + oso rexw + cf /o=16 + + + + + iretd + + oso rexw + cf /o=32 + + + + + iretq + + oso rexw + cf /o=64 + + + + + jo + + 70 + Jb + + + oso + 0f 80 + Jz + def64 depM + + + + + jno + + 71 + Jb + + + oso + 0f 81 + Jz + def64 depM + + + + + jb + + 72 + Jb + + + oso + 0f 82 + Jz + def64 depM + + + + + jae + + 73 + Jb + + + oso + 0f 83 + Jz + def64 depM + + + + + jz + + 74 + Jb + + + oso + 0f 84 + Jz + def64 depM + + + + + jnz + + 75 + Jb + + + oso + 0f 85 + Jz + def64 depM + + + + + jbe + + 76 + Jb + + + oso + 0f 86 + Jz + def64 depM + + + + + ja + + 77 + Jb + + + oso + 0f 87 + Jz + def64 depM + + + + + js + + 78 + Jb + + + oso + 0f 88 + Jz + def64 depM + + + + + jns + + 79 + Jb + + + oso + 0f 89 + Jz + def64 depM + + + + + jp + + 7a + Jb + + + oso + 0f 8a + Jz + def64 depM + + + + + jnp + + 7b + Jb + + + oso + 0f 8b + Jz + def64 depM + + + + + jl + + 7c + Jb + + + oso + 0f 8c + Jz + def64 depM + + + + + jge + + 7d + Jb + + + oso + 0f 8d + Jz + def64 depM + + + + + jle + + 7e + Jb + + + oso + 0f 8e + Jz + def64 depM + + + + + jg + + 7f + Jb + + + oso + 0f 8f + Jz + def64 depM + + + + + jcxz + + aso + e3 /a=16 + Jb + + + + + jecxz + + aso + e3 /a=32 + Jb + + + + + jrcxz + + aso + e3 /a=64 + Jb + + + + + jmp + + aso oso rexw rexr rexx rexb + ff /reg=4 + Ev + def64 depM + + + aso oso rexw rexr rexx rexb + ff /reg=5 + Ep + + + oso + e9 + Jz + def64 depM + cast + + + ea + Ap + inv64 + + + eb + Jb + + + + + lahf + + 9f + + + + + lar + + aso oso rexw rexr rexx rexb + 0f 02 + Gv Ew + + + + + lddqu + + aso rexr rexx rexb + ssef2 0f f0 + V M + + + + + ldmxcsr + + aso rexw rexr rexx rexb + 0f ae /reg=2 /mod=11 + Md + + + + + lds + + aso oso + c5 + Gv M + inv64 + + + + + lea + + aso oso rexw rexr rexx rexb + 8d + Gv M + + + + + les + + aso oso + c4 + Gv M + inv64 + + + + + lfs + + aso oso rexw rexr rexx rexb + 0f b4 + Gz M + + + + + lgs + + aso oso rexw rexr rexx rexb + 0f b5 + Gz M + + + + + lidt + + aso rexr rexx rexb + 0f 01 /reg=3 /mod=!11 + M + + + + + lss + + aso oso rexw rexr rexx rexb + 0f b2 + Gz M + + + + + leave + + c9 + + + + + lfence + + 0f ae /reg=5 /mod=11 /rm=0 + + + 0f ae /reg=5 /mod=11 /rm=1 + + + 0f ae /reg=5 /mod=11 /rm=2 + + + 0f ae /reg=5 /mod=11 /rm=3 + + + 0f ae /reg=5 /mod=11 /rm=4 + + + 0f ae /reg=5 /mod=11 /rm=5 + + + 0f ae /reg=5 /mod=11 /rm=6 + + + 0f ae /reg=5 /mod=11 /rm=7 + + + + + lgdt + + aso rexr rexx rexb + 0f 01 /reg=2 /mod=!11 + M + + + + + lldt + + aso rexr rexx rexb + 0f 00 /reg=2 + Ew + + + + + lmsw + + aso rexr rexx rexb + 0f 01 /reg=6 /mod=!11 + Ew + + + + + lock + + f0 + + + + + lodsb + + seg + ac + + + + + lodsw + + seg oso rexw + ad /o=16 + + + + + lodsd + + seg oso rexw + ad /o=32 + + + + + lodsq + + seg oso rexw + ad /o=64 + + + + + loopnz + + e0 + Jb + + + + + loope + + e1 + Jb + + + + + loop + + e2 + Jb + + + + + lsl + + aso oso rexw rexr rexx rexb + 0f 03 + Gv Ew + + + + + ltr + + aso rexr rexx rexb + 0f 00 /reg=3 + Ew + + + + + maskmovq + + aso rexr rexx rexb + 0f f7 + P PR + + + + + maxpd + + aso rexr rexx rexb + sse66 0f 5f + V W + + + + + maxps + + aso rexr rexx rexb + 0f 5f + V W + + + + + maxsd + + aso rexr rexx rexb + ssef2 0f 5f + V W + + + + + maxss + + aso rexr rexx rexb + ssef3 0f 5f + V W + + + + + mfence + + 0f ae /reg=6 /mod=11 /rm=0 + + + 0f ae /reg=6 /mod=11 /rm=1 + + + 0f ae /reg=6 /mod=11 /rm=2 + + + 0f ae /reg=6 /mod=11 /rm=3 + + + 0f ae /reg=6 /mod=11 /rm=4 + + + 0f ae /reg=6 /mod=11 /rm=5 + + + 0f ae /reg=6 /mod=11 /rm=6 + + + 0f ae /reg=6 /mod=11 /rm=7 + + + + + minpd + + aso rexr rexx rexb + sse66 0f 5d + V W + + + + + minps + + aso rexr rexx rexb + 0f 5d + V W + + + + + minsd + + aso rexr rexx rexb + ssef2 0f 5d + V W + + + + + minss + + aso rexr rexx rexb + ssef3 0f 5d + V W + + + + + monitor + + 0f 01 /reg=1 /mod=11 /rm=0 + + + + + montmul + + 0f a6 /mod=11 /rm=0 /reg=0 + + + + + mov + + aso rexw rexr rexx rexb + c6 /reg=0 + Eb Ib + + + aso oso rexw rexr rexx rexb + c7 /reg=0 + Ev Iz + + + aso rexr rexx rexb + 88 + Eb Gb + + + aso oso rexw rexr rexx rexb + 89 + Ev Gv + + + aso rexr rexx rexb + 8a + Gb Eb + + + aso oso rexw rexr rexx rexb + 8b + Gv Ev + + + aso oso rexr rexx rexb + 8c + Ev S + + + aso oso rexr rexx rexb + 8e + S Ev + + + a0 + AL Ob + + + aso oso rexw + a1 + rAX Ov + + + a2 + Ob AL + + + aso oso rexw + a3 + Ov rAX + + + rexb + b0 + ALr8b Ib + + + rexb + b1 + CLr9b Ib + + + rexb + b2 + DLr10b Ib + + + rexb + b3 + BLr11b Ib + + + rexb + b4 + AHr12b Ib + + + rexb + b5 + CHr13b Ib + + + rexb + b6 + DHr14b Ib + + + rexb + b7 + BHr15b Ib + + + oso rexw rexb + b8 + rAXr8 Iv + + + oso rexw rexb + b9 + rCXr9 Iv + + + oso rexw rexb + ba + rDXr10 Iv + + + oso rexw rexb + bb + rBXr11 Iv + + + oso rexw rexb + bc + rSPr12 Iv + + + oso rexw rexb + bd + rBPr13 Iv + + + oso rexw rexb + be + rSIr14 Iv + + + oso rexw rexb + bf + rDIr15 Iv + + + rexr + 0f 20 + R C + + + rexr + 0f 21 + R D + + + rexr + 0f 22 + C R + + + rexr + 0f 23 + D R + + + + + movapd + + aso rexr rexx rexb + sse66 0f 28 + V W + + + aso rexr rexx rexb + sse66 0f 29 + W V + + + + + movaps + + aso rexr rexx rexb + 0f 28 + V W + + + aso rexr rexx rexb + 0f 29 + W V + + + + + movd + + aso rexw rexr rexx rexb + sse66 0f 6e + V Ex + + + aso rexr rexx rexb + 0f 6e + P Ex + + + aso rexw rexr rexx rexb + sse66 0f 7e + Ex V + + + aso rexr rexx rexb + 0f 7e + Ex P + + + + + movhpd + + aso rexr rexx rexb + sse66 0f 16 /mod=!11 + V M + + + aso rexr rexx rexb + sse66 0f 17 + M V + + + + + movhps + + aso rexr rexx rexb + 0f 16 /mod=!11 + V M + + + aso rexr rexx rexb + 0f 17 + M V + + + + + movlhps + + aso rexr rexx rexb + 0f 16 /mod=11 + V VR + + + + + movlpd + + aso rexr rexx rexb + sse66 0f 12 /mod=!11 + V M + + + aso rexr rexx rexb + sse66 0f 13 + M V + + + + + movlps + + aso rexr rexx rexb + 0f 12 /mod=!11 + V M + + + aso rexr rexx rexb + 0f 13 + M V + + + + + movhlps + + aso rexr rexx rexb + 0f 12 /mod=11 + V VR + + + + + movmskpd + + oso rexr rexb + sse66 0f 50 + Gd VR + + + + + movmskps + + oso rexr rexb + 0f 50 + Gd VR + + + + + movntdq + + aso rexr rexx rexb + sse66 0f e7 + M V + + + + + movnti + + aso rexw rexr rexx rexb + 0f c3 + M Gy + + + + + movntpd + + aso rexr rexx rexb + sse66 0f 2b + M V + + + + + movntps + + aso rexr rexx rexb + 0f 2b + M V + + + + + movntq + + 0f e7 + M P + + + + + movq + + aso rexr rexx rexb + 0f 6f + P Q + + + aso rexr rexx rexb + sse66 0f d6 + W V + + + aso rexr rexx rexb + ssef3 0f 7e + V W + + + aso rexr rexx rexb + 0f 7f + Q P + + + + + movsb + + seg + a4 + + + + + movsw + + seg oso rexw + a5 /o=16 + + + + + movsd + + seg oso rexw + a5 /o=32 + + + aso rexr rexx rexb + ssef2 0f 10 + V W + + + aso rexr rexx rexb + ssef2 0f 11 + W V + + + + + movsq + + seg oso rexw + a5 /o=64 + + + + + movss + + aso rexr rexx rexb + ssef3 0f 10 + V W + + + aso rexr rexx rexb + ssef3 0f 11 + W V + + + + + movsx + + aso oso rexw rexr rexx rexb + 0f be + Gv Eb + + + aso oso rexw rexr rexx rexb + 0f bf + Gv Ew + + + + + movupd + + aso rexr rexx rexb + sse66 0f 10 + V W + + + aso rexr rexx rexb + sse66 0f 11 + W V + + + + + movups + + aso rexr rexx rexb + 0f 10 + V W + + + aso rexr rexx rexb + 0f 11 + W V + + + + + movzx + + aso oso rexw rexr rexx rexb + 0f b6 + Gv Eb + + + aso oso rexw rexr rexx rexb + 0f b7 + Gv Ew + + + + + mul + + aso rexw rexr rexx rexb + f6 /reg=4 + Eb + + + aso oso rexw rexr rexx rexb + f7 /reg=4 + Ev + + + + + mulpd + + aso rexr rexx rexb + sse66 0f 59 + V W + + + + + mulps + + aso rexr rexx rexb + 0f 59 + V W + + + + + mulsd + + aso rexr rexx rexb + ssef2 0f 59 + V W + + + + + mulss + + aso rexr rexx rexb + ssef3 0f 59 + V W + + + + + mwait + + 0f 01 /reg=1 /mod=11 /rm=1 + + + + + neg + + aso rexw rexr rexx rexb + f6 /reg=3 + Eb + + + aso oso rexw rexr rexx rexb + f7 /reg=3 + Ev + + + + + nop + + 90 + + + aso rexr rexx rexb + 0f 19 + M + + + aso rexr rexx rexb + 0f 1a + M + + + aso rexr rexx rexb + 0f 1b + M + + + aso rexr rexx rexb + 0f 1c + M + + + aso rexr rexx rexb + 0f 1d + M + + + aso rexr rexx rexb + 0f 1e + M + + + aso rexr rexx rexb + 0f 1f + M + + + + + not + + aso rexw rexr rexx rexb + f6 /reg=2 + Eb + + + aso oso rexw rexr rexx rexb + f7 /reg=2 + Ev + + + + + or + + aso rexr rexx rexb + 08 + Eb Gb + + + aso oso rexw rexr rexx rexb + 09 + Ev Gv + + + aso rexr rexx rexb + 0a + Gb Eb + + + aso oso rexw rexr rexx rexb + 0b + Gv Ev + + + 0c + AL Ib + + + oso rexw + 0d + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=1 + Eb Ib + + + aso oso rexw rexr rexx rexb + 81 /reg=1 + Ev Iz + sext + + + aso rexr rexx rexb + 82 /reg=1 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 83 /reg=1 + Ev Ib + sext + + + + + orpd + + aso rexr rexx rexb + sse66 0f 56 + V W + + + + + orps + + aso rexr rexx rexb + 0f 56 + V W + + + + + out + + e6 + Ib AL + + + oso + e7 + Ib eAX + + + ee + DX AL + + + oso + ef + DX eAX + + + + + outsb + + 6e + + + + + outsw + + oso + 6f /o=16 + + + + + outsd + + oso + 6f /o=32 + + + + + outsq + + oso + 6f /o=64 + + + + + packsswb + + aso rexr rexx rexb + sse66 0f 63 + V W + + + aso rexr rexx rexb + 0f 63 + P Q + + + + + packssdw + + aso rexr rexx rexb + sse66 0f 6b + V W + + + aso rexr rexx rexb + 0f 6b + P Q + + + + + packuswb + + aso rexr rexx rexb + sse66 0f 67 + V W + + + aso rexr rexx rexb + 0f 67 + P Q + + + + + paddb + + aso rexr rexx rexb + sse66 0f fc + V W + + + aso rexr rexx rexb + 0f fc + P Q + + + + + paddw + + aso rexr rexx rexb + 0f fd + P Q + + + aso rexr rexx rexb + sse66 0f fd + V W + + + + + paddd + + aso rexr rexx rexb + 0f fe + P Q + + + aso rexr rexx rexb + sse66 0f fe + V W + + + + + + paddsb + + aso rexr rexx rexb + 0f ec + P Q + + + aso rexr rexx rexb + sse66 0f ec + V W + + + + + paddsw + + aso rexr rexx rexb + 0f ed + P Q + + + aso rexr rexx rexb + sse66 0f ed + V W + + + + + paddusb + + aso rexr rexx rexb + 0f dc + P Q + + + aso rexr rexx rexb + sse66 0f dc + V W + + + + + paddusw + + aso rexr rexx rexb + 0f dd + P Q + + + aso rexr rexx rexb + sse66 0f dd + V W + + + + + pand + + aso rexr rexx rexb + sse66 0f db + V W + + + aso rexr rexx rexb + 0f db + P Q + + + + + pandn + + aso rexr rexx rexb + sse66 0f df + V W + + + aso rexr rexx rexb + 0f df + P Q + + + + + pavgb + + aso rexr rexx rexb + sse66 0f e0 + V W + + + aso rexr rexx rexb + 0f e0 + P Q + + + + + pavgw + + aso rexr rexx rexb + sse66 0f e3 + V W + + + aso rexr rexx rexb + 0f e3 + P Q + + + + + pcmpeqb + + aso rexr rexx rexb + 0f 74 + P Q + + + aso rexr rexx rexb + sse66 0f 74 + V W + + + + + pcmpeqw + + aso rexr rexx rexb + 0f 75 + P Q + + + aso rexr rexx rexb + sse66 0f 75 + V W + + + + + pcmpeqd + + aso rexr rexx rexb + 0f 76 + P Q + + + aso rexr rexx rexb + sse66 0f 76 + V W + + + + + pcmpgtb + + aso rexr rexx rexb + sse66 0f 64 + V W + + + aso rexr rexx rexb + 0f 64 + P Q + + + + + pcmpgtw + + aso rexr rexx rexb + sse66 0f 65 + V W + + + aso rexr rexx rexb + 0f 65 + P Q + + + + + pcmpgtd + + aso rexr rexx rexb + sse66 0f 66 + V W + + + aso rexr rexx rexb + 0f 66 + P Q + + + + + pextrb + + aso rexr rexb + sse66 0f 3a 14 + MbRv V Ib + def64 + + + + + pextrd + + aso rexr rexw rexb + sse66 0f 3a 16 /o=16 + Ev V Ib + + + aso rexr rexw rexb + sse66 0f 3a 16 /o=32 + Ev V Ib + + + + + pextrq + + aso rexr rexw rexb + sse66 0f 3a 16 /o=64 + Ev V Ib + def64 + + + + + pextrw + + aso rexr rexb + sse66 0f c5 + Gd VR Ib + + + aso oso rexw rexr rexx rexb + 0f c5 + Gd PR Ib + + + + + pinsrw + + aso oso rexw rexr rexx rexb + 0f c4 + P Ew Ib + + + aso rexw rexr rexx rexb + sse66 0f c4 + V Ew Ib + + + + + pmaddwd + + aso rexr rexx rexb + 0f f5 + P Q + + + aso rexr rexx rexb + sse66 0f f5 + V W + + + + + pmaxsw + + aso rexr rexx rexb + sse66 0f ee + V W + + + aso rexr rexx rexb + 0f ee + P Q + + + + + pmaxub + + aso rexr rexx rexb + 0f de + P Q + + + aso rexr rexx rexb + sse66 0f de + V W + + + + + pminsw + + aso rexr rexx rexb + sse66 0f ea + V W + + + aso rexr rexx rexb + 0f ea + P Q + + + + + pminub + + aso rexr rexx rexb + sse66 0f da + V W + + + aso rexr rexx rexb + 0f da + P Q + + + + + pmovmskb + + rexr rexb + sse66 0f d7 + Gd VR + + + oso rexr rexb + 0f d7 + Gd PR + + + + + pmulhuw + + aso rexr rexx rexb + 0f e4 + P Q + + + aso rexr rexx rexb + sse66 0f e4 + V W + + + + + pmulhw + + aso rexr rexx rexb + sse66 0f e5 + V W + + + aso rexr rexx rexb + 0f e5 + P Q + + + + + pmullw + + aso rexr rexx rexb + 0f d5 + P Q + + + aso rexr rexx rexb + sse66 0f d5 + V W + + + + + pop + + 07 + ES + inv64 + + + 17 + SS + inv64 + + + 1f + DS + inv64 + + + 0f a9 + GS + + + 0f a1 + FS + + + oso rexb + 58 + rAXr8 + def64 depM + + + oso rexb + 59 + rCXr9 + def64 depM + + + oso rexb + 5a + rDXr10 + def64 depM + + + oso rexb + 5b + rBXr11 + def64 depM + + + oso rexb + 5c + rSPr12 + def64 depM + + + oso rexb + 5d + rBPr13 + def64 depM + + + oso rexb + 5e + rSIr14 + def64 depM + + + oso rexb + 5f + rDIr15 + def64 depM + + + aso oso rexw rexr rexx rexb + 8f /reg=0 + Ev + def64 depM + + + + + popa + + oso + 61 /o=16 + inv64 + + + + + popad + + oso + 61 /o=32 + inv64 + + + + + popfw + + oso + 9d /m=32 /o=16 + def64 depM + + + oso + 9d /m=16 /o=16 + def64 depM + + + + + popfd + + oso + 9d /m=16 /o=32 + def64 depM + + + oso + 9d /m=32 /o=32 + def64 depM + + + + + popfq + + oso + 9d /m=64 /o=64 + def64 depM + + + + + por + + aso rexr rexx rexb + sse66 0f eb + V W + + + aso rexr rexx rexb + 0f eb + P Q + + + + + prefetch + + aso rexw rexr rexx rexb + 0f 0d /reg=0 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=1 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=2 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=3 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=4 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=5 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=6 + M + + + aso rexw rexr rexx rexb + 0f 0d /reg=7 + M + + + + + prefetchnta + + aso rexw rexr rexx rexb + 0f 18 /reg=0 + M + + + + + prefetcht0 + + aso rexw rexr rexx rexb + 0f 18 /reg=1 + M + + + + + prefetcht1 + + aso rexw rexr rexx rexb + 0f 18 /reg=2 + M + + + + + prefetcht2 + + aso rexw rexr rexx rexb + 0f 18 /reg=3 + M + + + + + psadbw + + aso rexr rexx rexb + sse66 0f f6 + V W + + + aso rexr rexx rexb + 0f f6 + P Q + + + + + pshufw + + aso rexr rexx rexb + 0f 70 + P Q Ib + + + + + psllw + + aso rexr rexx rexb + sse66 0f f1 + V W + + + aso rexr rexx rexb + 0f f1 + P Q + + + rexb + sse66 0f 71 /reg=6 + VR Ib + + + 0f 71 /reg=6 + PR Ib + + + + + pslld + + aso rexr rexx rexb + sse66 0f f2 + V W + + + aso rexr rexx rexb + 0f f2 + P Q + + + rexb + sse66 0f 72 /reg=6 + VR Ib + + + 0f 72 /reg=6 + PR Ib + + + + + psllq + + aso rexr rexx rexb + sse66 0f f3 + V W + + + aso rexr rexx rexb + 0f f3 + P Q + + + rexb + sse66 0f 73 /reg=6 + VR Ib + + + 0f 73 /reg=6 + PR Ib + + + + + psraw + + aso rexr rexx rexb + 0f e1 + P Q + + + aso rexr rexx rexb + sse66 0f e1 + V W + + + rexb + sse66 0f 71 /reg=4 + VR Ib + + + 0f 71 /reg=4 + PR Ib + + + + + psrad + + 0f 72 /reg=4 + PR Ib + + + aso rexr rexx rexb + sse66 0f e2 + V W + + + aso rexr rexx rexb + 0f e2 + P Q + + + rexb + sse66 0f 72 /reg=4 + VR Ib + + + + + psrlw + + 0f 71 /reg=2 + PR Ib + + + aso rexr rexx rexb + 0f d1 + P Q + + + aso rexr rexx rexb + sse66 0f d1 + V W + + + rexb + sse66 0f 71 /reg=2 + VR Ib + + + + + psrld + + 0f 72 /reg=2 + PR Ib + + + aso rexr rexx rexb + 0f d2 + P Q + + + aso rexr rexx rexb + sse66 0f d2 + V W + + + rexb + sse66 0f 72 /reg=2 + VR Ib + + + + + psrlq + + 0f 73 /reg=2 + PR Ib + + + aso rexr rexx rexb + 0f d3 + P Q + + + aso rexr rexx rexb + sse66 0f d3 + V W + + + rexb + sse66 0f 73 /reg=2 + VR Ib + + + + + psubb + + aso rexr rexx rexb + sse66 0f f8 + V W + + + aso rexr rexx rexb + 0f f8 + P Q + + + + + psubw + + aso rexr rexx rexb + sse66 0f f9 + V W + + + aso rexr rexx rexb + 0f f9 + P Q + + + + + psubd + + aso rexr rexx rexb + 0f fa + P Q + + + aso rexr rexx rexb + sse66 0f fa + V W + + + + + psubsb + + aso rexr rexx rexb + 0f e8 + P Q + + + aso rexr rexx rexb + sse66 0f e8 + V W + + + + + psubsw + + aso rexr rexx rexb + 0f e9 + P Q + + + aso rexr rexx rexb + sse66 0f e9 + V W + + + + + psubusb + + aso rexr rexx rexb + 0f d8 + P Q + + + aso rexr rexx rexb + sse66 0f d8 + V W + + + + + psubusw + + aso rexr rexx rexb + 0f d9 + P Q + + + aso rexr rexx rexb + sse66 0f d9 + V W + + + + + punpckhbw + + aso rexr rexx rexb + sse66 0f 68 + V W + + + aso rexr rexx rexb + 0f 68 + P Q + + + + + punpckhwd + + aso rexr rexx rexb + sse66 0f 69 + V W + + + aso rexr rexx rexb + 0f 69 + P Q + + + + + punpckhdq + + aso rexr rexx rexb + sse66 0f 6a + V W + + + aso rexr rexx rexb + 0f 6a + P Q + + + + + punpcklbw + + aso rexr rexx rexb + sse66 0f 60 + V W + + + aso rexr rexx rexb + 0f 60 + P Q + + + + + punpcklwd + + aso rexr rexx rexb + sse66 0f 61 + V W + + + aso rexr rexx rexb + 0f 61 + P Q + + + + + punpckldq + + aso rexr rexx rexb + sse66 0f 62 + V W + + + aso rexr rexx rexb + 0f 62 + P Q + + + + + pi2fw + + 0f 0f /3dnow=0c + P Q + + + + + pi2fd + + 0f 0f /3dnow=0d + P Q + + + + + pf2iw + + 0f 0f /3dnow=1c + P Q + + + + + pf2id + + 0f 0f /3dnow=1d + P Q + + + + + pfnacc + + 0f 0f /3dnow=8a + P Q + + + + + pfpnacc + + 0f 0f /3dnow=8e + P Q + + + + + pfcmpge + + 0f 0f /3dnow=90 + P Q + + + + + pfmin + + 0f 0f /3dnow=94 + P Q + + + + + pfrcp + + 0f 0f /3dnow=96 + P Q + + + + + pfrsqrt + + 0f 0f /3dnow=97 + P Q + + + + + pfsub + + 0f 0f /3dnow=9a + P Q + + + + + pfadd + + 0f 0f /3dnow=9e + P Q + + + + + pfcmpgt + + 0f 0f /3dnow=a0 + P Q + + + + + pfmax + + 0f 0f /3dnow=a4 + P Q + + + + + pfrcpit1 + + 0f 0f /3dnow=a6 + P Q + + + + + pfrsqit1 + + 0f 0f /3dnow=a7 + P Q + + + + + pfsubr + + 0f 0f /3dnow=aa + P Q + + + + + pfacc + + 0f 0f /3dnow=ae + P Q + + + + + pfcmpeq + + 0f 0f /3dnow=b0 + P Q + + + + + pfmul + + 0f 0f /3dnow=b4 + P Q + + + + + pfrcpit2 + + 0f 0f /3dnow=b6 + P Q + + + + + pmulhrw + + 0f 0f /3dnow=b7 + P Q + + + + + pswapd + + 0f 0f /3dnow=bb + P Q + + + + + pavgusb + + 0f 0f /3dnow=bf + P Q + + + + + push + + 06 + ES + inv64 + + + 0e + CS + inv64 + + + 16 + SS + inv64 + + + 1e + DS + inv64 + + + 0f a8 + GS + + + 0f a0 + FS + + + oso rexb + 50 + rAXr8 + def64 depM + + + oso rexb + 51 + rCXr9 + def64 depM + + + oso rexb + 52 + rDXr10 + def64 depM + + + oso rexb + 53 + rBXr11 + def64 depM + + + oso rexb + 54 + rSPr12 + def64 depM + + + oso rexb + 55 + rBPr13 + def64 depM + + + oso rexb + 56 + rSIr14 + def64 depM + + + oso rexb + 57 + rDIr15 + def64 depM + + + oso + 68 + Iz + cast + + + aso oso rexw rexr rexx rexb + ff /reg=6 + Ev + def64 + + + 6a + Ib + sext + + + + + pusha + + oso + 60 /o=16 + inv64 + + + + + pushad + + oso + 60 /o=32 + inv64 + + + + + pushfw + + oso + 9c /m=32 /o=16 + def64 + + + oso + 9c /m=16 /o=16 + def64 + + + oso rexw + 9c /m=64 /o=16 + def64 + + + + + pushfd + + oso + 9c /m=16 /o=32 + def64 + + + oso + 9c /m=32 /o=32 + def64 + + + + + pushfq + + oso rexw + 9c /m=64 /o=32 + def64 + + + oso rexw + 9c /m=64 /o=64 + def64 + + + + + pxor + + aso rexr rexx rexb + sse66 0f ef + V W + + + aso rexr rexx rexb + 0f ef + P Q + + + + + rcl + + aso rexw rexr rexx rexb + c0 /reg=2 + Eb Ib + + + aso oso rexw rexr rexx rexb + c1 /reg=2 + Ev Ib + + + aso rexw rexr rexx rexb + d0 /reg=2 + Eb I1 + + + aso rexw rexr rexx rexb + d2 /reg=2 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=2 + Ev CL + cast + + + aso oso rexw rexr rexx rexb + d1 /reg=2 + Ev I1 + + + + + rcr + + aso rexw rexr rexx rexb + d0 /reg=3 + Eb I1 + + + aso oso rexw rexr rexx rexb + c1 /reg=3 + Ev Ib + + + aso rexw rexr rexx rexb + c0 /reg=3 + Eb Ib + + + aso oso rexw rexr rexx rexb + d1 /reg=3 + Ev I1 + + + aso rexw rexr rexx rexb + d2 /reg=3 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=3 + Ev CL + cast + + + + + rol + + aso rexw rexr rexx rexb + c0 /reg=0 + Eb Ib + + + aso rexw rexr rexx rexb + d0 /reg=0 + Eb I1 + + + aso oso rexw rexr rexx rexb + d1 /reg=0 + Ev I1 + + + aso rexw rexr rexx rexb + d2 /reg=0 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=0 + Ev CL + cast + + + aso oso rexw rexr rexx rexb + c1 /reg=0 + Ev Ib + + + + + ror + + aso rexw rexr rexx rexb + d0 /reg=1 + Eb I1 + + + aso rexw rexr rexx rexb + c0 /reg=1 + Eb Ib + + + aso oso rexw rexr rexx rexb + c1 /reg=1 + Ev Ib + + + aso oso rexw rexr rexx rexb + d1 /reg=1 + Ev I1 + + + aso rexw rexr rexx rexb + d2 /reg=1 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=1 + Ev CL + cast + + + + + rcpps + + aso rexr rexx rexb + 0f 53 + V W + + + + + rcpss + + aso rexr rexx rexb + ssef3 0f 53 + V W + + + + + rdmsr + + 0f 32 + + + + + rdpmc + + 0f 33 + + + + + rdtsc + + 0f 31 + + + + + rdtscp + amd + + 0f 01 /reg=7 /mod=11 /rm=1 + + + + + repne + + f2 + + + + + rep + + f3 + + + + + ret + + c2 + Iw + + + c3 + + + + + retf + + ca + Iw + + + cb + + + + + rsm + + 0f aa + + + + + rsqrtps + + aso rexr rexx rexb + 0f 52 + V W + + + + + rsqrtss + + aso rexr rexx rexb + ssef3 0f 52 + V W + + + + + sahf + + 9e + + + + + sal + + + + salc + + d6 + inv64 + + + + + sar + + aso oso rexw rexr rexx rexb + d1 /reg=7 + Ev I1 + + + aso rexw rexr rexx rexb + c0 /reg=7 + Eb Ib + + + aso rexw rexr rexx rexb + d0 /reg=7 + Eb I1 + + + aso oso rexw rexr rexx rexb + c1 /reg=7 + Ev Ib + + + aso rexw rexr rexx rexb + d2 /reg=7 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=7 + Ev CL + cast + + + + + shl + + aso rexw rexr rexx rexb + c0 /reg=6 + Eb Ib + + + aso oso rexw rexr rexx rexb + c1 /reg=6 + Ev Ib + + + aso rexw rexr rexx rexb + d0 /reg=6 + Eb I1 + + + aso rexw rexr rexx rexb + d2 /reg=6 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d3 /reg=6 + Ev CL + cast + + + aso oso rexw rexr rexx rexb + c1 /reg=4 + Ev Ib + + + aso rexr rexx rexb + d2 /reg=4 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d1 /reg=4 + Ev I1 + + + aso rexw rexr rexx rexb + d0 /reg=4 + Eb I1 + + + aso rexw rexr rexx rexb + c0 /reg=4 + Eb Ib + + + aso oso rexw rexr rexx rexb + d3 /reg=4 + Ev CL + + + aso oso rexw rexr rexx rexb + d1 /reg=6 + Ev I1 + + + + + shr + + aso oso rexw rexr rexx rexb + c1 /reg=5 + Ev Ib + + + aso rexw rexr rexx rexb + d2 /reg=5 + Eb CL + cast + + + aso oso rexw rexr rexx rexb + d1 /reg=5 + Ev I1 + + + aso rexw rexr rexx rexb + d0 /reg=5 + Eb I1 + + + aso rexw rexr rexx rexb + c0 /reg=5 + Eb Ib + + + aso oso rexw rexr rexx rexb + d3 /reg=5 + Ev CL + cast + + + + + sbb + + aso rexr rexx rexb + 18 + Eb Gb + + + aso oso rexw rexr rexx rexb + 19 + Ev Gv + + + aso rexr rexx rexb + 1a + Gb Eb + + + aso oso rexw rexr rexx rexb + 1b + Gv Ev + + + 1c + AL Ib + + + oso rexw + 1d + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=3 + Eb Ib + + + aso oso rexw rexr rexx rexb + 81 /reg=3 + Ev Iz + sext + + + aso rexr rexx rexb + 82 /reg=3 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 83 /reg=3 + Ev Ib + sext + + + + + scasb + + ae + + + + + scasw + + oso rexw + af /o=16 + + + + + scasd + + oso rexw + af /o=32 + + + + + scasq + + oso rexw + af /o=64 + + + + + seto + + aso rexr rexx rexb + 0f 90 + Eb + + + + + setno + + aso rexr rexx rexb + 0f 91 + Eb + + + + + setb + + aso rexr rexx rexb + 0f 92 + Eb + + + + + setnb + + aso rexr rexx rexb + 0f 93 + Eb + + + + + setz + + aso rexr rexx rexb + 0f 94 + Eb + + + + + setnz + + aso rexr rexx rexb + 0f 95 + Eb + + + + + setbe + + aso rexr rexx rexb + 0f 96 + Eb + + + + + seta + + aso rexr rexx rexb + 0f 97 + Eb + + + + + sets + + aso rexr rexx rexb + 0f 98 + Eb + + + + + setns + + aso rexr rexx rexb + 0f 99 + Eb + + + + + setp + + aso rexr rexx rexb + 0f 9a + Eb + + + + + setnp + + aso rexr rexx rexb + 0f 9b + Eb + + + + + setl + + aso rexr rexx rexb + 0f 9c + Eb + + + + + setge + + aso rexr rexx rexb + 0f 9d + Eb + + + + + setle + + aso rexr rexx rexb + 0f 9e + Eb + + + + + setg + + aso rexr rexx rexb + 0f 9f + Eb + + + + + sfence + + 0f ae /reg=7 /mod=11 /rm=0 + + + 0f ae /reg=7 /mod=11 /rm=1 + + + 0f ae /reg=7 /mod=11 /rm=2 + + + 0f ae /reg=7 /mod=11 /rm=3 + + + 0f ae /reg=7 /mod=11 /rm=4 + + + 0f ae /reg=7 /mod=11 /rm=5 + + + 0f ae /reg=7 /mod=11 /rm=6 + + + 0f ae /reg=7 /mod=11 /rm=7 + + + + + sgdt + + aso rexr rexx rexb + 0f 01 /reg=0 /mod=!11 + M + + + + + shld + + aso oso rexw rexr rexx rexb + 0f a4 + Ev Gv Ib + + + aso oso rexw rexr rexx rexb + 0f a5 + Ev Gv CL + + + + + shrd + + aso oso rexw rexr rexx rexb + 0f ac + Ev Gv Ib + + + aso oso rexw rexr rexx rexb + 0f ad + Ev Gv CL + + + + + shufpd + + aso rexr rexx rexb + sse66 0f c6 + V W Ib + + + + + shufps + + aso rexr rexx rexb + 0f c6 + V W Ib + + + + + sidt + + aso rexr rexx rexb + 0f 01 /reg=1 /mod=!11 + M + + + + + sldt + + aso oso rexr rexx rexb + 0f 00 /reg=0 + MwRv + + + + + smsw + + aso rexr rexx rexb + 0f 01 /reg=4 /mod=!11 + M + + + + + sqrtps + + aso rexr rexx rexb + 0f 51 + V W + + + + + sqrtpd + + aso rexr rexx rexb + sse66 0f 51 + V W + + + + + sqrtsd + + aso rexr rexx rexb + ssef2 0f 51 + V W + + + + + sqrtss + + aso rexr rexx rexb + ssef3 0f 51 + V W + + + + + stc + + f9 + + + + + std + + fd + + + + + stgi + amd + + 0f 01 /reg=3 /mod=11 /rm=4 + + + + + sti + + fb + + + + + skinit + amd + + 0f 01 /reg=3 /mod=11 /rm=6 + + + + + stmxcsr + + aso rexw rexr rexx rexb + 0f ae /mod=11 /reg=3 + Md + + + + + stosb + + seg + aa + + + + + stosw + + seg oso rexw + ab /o=16 + + + + + stosd + + seg oso rexw + ab /o=32 + + + + + stosq + + seg oso rexw + ab /o=64 + + + + + str + + aso oso rexr rexx rexb + 0f 00 /reg=1 + Ev + + + + + sub + + aso rexr rexx rexb + 28 + Eb Gb + + + aso oso rexw rexr rexx rexb + 29 + Ev Gv + + + aso rexr rexx rexb + 2a + Gb Eb + + + aso oso rexw rexr rexx rexb + 2b + Gv Ev + + + 2c + AL Ib + + + oso rexw + 2d + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=5 + Eb Ib + + + aso oso rexw rexr rexx rexb + 81 /reg=5 + Ev Iz + sext + + + aso rexr rexx rexb + 82 /reg=5 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 83 /reg=5 + Ev Ib + sext + + + + + subpd + + aso rexr rexx rexb + sse66 0f 5c + V W + + + + + subps + + aso rexr rexx rexb + 0f 5c + V W + + + + + subsd + + aso rexr rexx rexb + ssef2 0f 5c + V W + + + + + subss + + aso rexr rexx rexb + ssef3 0f 5c + V W + + + + + swapgs + + 0f 01 /reg=7 /mod=11 /rm=0 + + + + + syscall + + 0f 05 + + + + + sysenter + + 0f 34 + inv64 + + + + + sysexit + + 0f 35 + + + + + sysret + + 0f 07 + + + + + test + + aso rexw rexr rexx rexb + f6 /reg=0 + Eb Ib + + + aso rexr rexx rexb + 84 + Eb Gb + + + aso oso rexw rexr rexx rexb + 85 + Ev Gv + + + a8 + AL Ib + + + oso rexw + a9 + rAX Iz + sext + + + aso rexw rexr rexx rexb + f6 /reg=1 + Eb Ib + + + aso oso rexw rexr rexx rexb + f7 /reg=0 + Ev Iz + sext + + + aso oso rexw rexr rexx rexb + f7 /reg=1 + Ev Iz + sext + + + + + ucomisd + + aso rexr rexx rexb + sse66 0f 2e + V W + + + + + ucomiss + + aso rexr rexx rexb + 0f 2e + V W + + + + + ud2 + + 0f 0b + + + + + unpckhpd + + aso rexr rexx rexb + sse66 0f 15 + V W + + + + + unpckhps + + aso rexr rexx rexb + 0f 15 + V W + + + + + unpcklps + + aso rexr rexx rexb + 0f 14 + V W + + + + + unpcklpd + + aso rexr rexx rexb + sse66 0f 14 + V W + + + + + verr + + aso rexr rexx rexb + 0f 00 /reg=4 + Ew + + + + + verw + + aso rexr rexx rexb + 0f 00 /reg=5 + Ew + + + + + vmcall + intel + + 0f 01 /reg=0 /mod=11 /rm=1 + + + + + vmclear + intel + + aso rexr rexx rexb + sse66 0f c7 /reg=6 + Mq + + + + + vmxon + intel + + aso rexr rexx rexb + ssef3 0f c7 /reg=6 + Mq + + + + + vmptrld + intel + + aso rexr rexx rexb + 0f c7 /reg=6 + Mq + + + + + vmptrst + intel + + aso rexr rexx rexb + 0f c7 /reg=7 + Mq + + + + + vmlaunch + intel + + 0f 01 /reg=0 /mod=11 /rm=2 + + + + + vmresume + intel + + 0f 01 /reg=0 /mod=11 /rm=3 + + + + + vmxoff + intel + + 0f 01 /reg=0 /mod=11 /rm=4 + + + + + vmread + intel + + aso rexr rexx rexb + 0f 78 /m=16 + Ed Gd + def64 + + + aso rexr rexx rexb + 0f 78 /m=32 + Ed Gd + def64 + + + aso rexr rexx rexb + 0f 78 /m=64 + Eq Gq + def64 + + + + + vmwrite + intel + + aso rexr rexx rexb + 0f 79 /m=16 + Gd Ed + def64 + + + aso rexr rexx rexb + 0f 79 /m=32 + Gd Ed + def64 + + + aso rexr rexx rexb + 0f 79 /m=64 + Gq Eq + def64 + + + + + vmrun + amd + + 0f 01 /reg=3 /mod=11 /rm=0 + + + + + vmmcall + amd + + 0f 01 /reg=3 /mod=11 /rm=1 + + + + + vmload + amd + + 0f 01 /reg=3 /mod=11 /rm=2 + + + + + vmsave + amd + + 0f 01 /reg=3 /mod=11 /rm=3 + + + + + wait + + 9b + + + + + wbinvd + + 0f 09 + + + + + wrmsr + + 0f 30 + + + + + xadd + + aso oso rexr rexx rexb + 0f c0 + Eb Gb + + + aso oso rexw rexr rexx rexb + 0f c1 + Ev Gv + + + + + xchg + + aso rexr rexx rexb + 86 + Eb Gb + + + aso oso rexw rexr rexx rexb + 87 + Ev Gv + + + oso rexw rexb + 90 + rAXr8 rAX + + + oso rexw rexb + 91 + rCXr9 rAX + + + oso rexw rexb + 92 + rDXr10 rAX + + + oso rexw rexb + 93 + rBXr11 rAX + + + oso rexw rexb + 94 + rSPr12 rAX + + + oso rexw rexb + 95 + rBPr13 rAX + + + oso rexw rexb + 96 + rSIr14 rAX + + + oso rexw rexb + 97 + rDIr15 rAX + + + + + xlatb + + rexw + d7 + + + + + xor + + aso rexr rexx rexb + 30 + Eb Gb + + + aso oso rexw rexr rexx rexb + 31 + Ev Gv + + + aso rexr rexx rexb + 32 + Gb Eb + + + aso oso rexw rexr rexx rexb + 33 + Gv Ev + + + 34 + AL Ib + + + oso rexw + 35 + rAX Iz + sext + + + aso rexr rexx rexb + 80 /reg=6 + Eb Ib + + + aso oso rexw rexr rexx rexb + 81 /reg=6 + Ev Iz + sext + + + aso rexr rexx rexb + 82 /reg=6 + Eb Ib + inv64 + + + aso oso rexw rexr rexx rexb + 83 /reg=6 + Ev Ib + sext + + + + + xorpd + + aso rexr rexx rexb + sse66 0f 57 + V W + + + + + xorps + + aso rexr rexx rexb + 0f 57 + V W + + + + + xcryptecb + + 0f a7 /mod=11 /rm=0 /reg=1 + + + + + xcryptcbc + + 0f a7 /mod=11 /rm=0 /reg=2 + + + + + xcryptctr + + 0f a7 /mod=11 /rm=0 /reg=3 + + + + + xcryptcfb + + 0f a7 /mod=11 /rm=0 /reg=4 + + + + + xcryptofb + + 0f a7 /mod=11 /rm=0 /reg=5 + + + + + xsha1 + + 0f a6 /mod=11 /rm=0 /reg=1 + + + + + xsha256 + + 0f a6 /mod=11 /rm=0 /reg=2 + + + + + xstore + + 0f a7 /mod=11 /rm=0 /reg=0 + + + + + db + + + + + + movdqa + + aso rexr rexx rexb + sse66 0f 7f + W V + + + aso rexr rexx rexb + sse66 0f 6f + V W + + + + + movdq2q + + aso rexb + ssef2 0f d6 + P VR + + + + + movdqu + + aso rexr rexx rexb + ssef3 0f 6f + V W + + + aso rexr rexx rexb + ssef3 0f 7f + W V + + + + + movq2dq + + aso + ssef3 0f d6 + V PR + + + + + paddq + + aso rexr rexx rexb + 0f d4 + P Q + + + aso rexr rexx rexb + sse66 0f d4 + V W + + + + + psubq + + aso rexr rexx rexb + sse66 0f fb + V W + + + aso rexr rexx rexb + 0f fb + P Q + + + + + pmuludq + + aso rexr rexx rexb + 0f f4 + P Q + + + aso rexr rexx rexb + sse66 0f f4 + V W + + + + + pshufhw + + aso rexr rexx rexb + ssef3 0f 70 + V W Ib + + + + + pshuflw + + aso rexr rexx rexb + ssef2 0f 70 + V W Ib + + + + + pshufd + + aso rexr rexx rexb + sse66 0f 70 + V W Ib + + + + + pslldq + + rexb + sse66 0f 73 /reg=7 + VR Ib + + + + + psrldq + + rexb + sse66 0f 73 /reg=3 + VR Ib + + + + + punpckhqdq + + aso rexr rexx rexb + sse66 0f 6d + V W + + + + + punpcklqdq + + aso rexr rexx rexb + sse66 0f 6c + V W + + + + + + + addsubpd + + aso rexr rexx rexb + sse66 0f d0 + V W + + + + + addsubps + + aso rexr rexx rexb + ssef2 0f d0 + V W + + + + + haddpd + + aso rexr rexx rexb + sse66 0f 7c + V W + + + + + haddps + + aso rexr rexx rexb + ssef2 0f 7c + V W + + + + + hsubpd + + aso rexr rexx rexb + sse66 0f 7d + V W + + + + + hsubps + + aso rexr rexx rexb + ssef2 0f 7d + V W + + + + + movddup + + aso rexr rexx rexb + ssef2 0f 12 /mod=11 + V W + + + aso rexr rexx rexb + ssef2 0f 12 /mod=!11 + V W + + + + + movshdup + + aso rexr rexx rexb + ssef3 0f 16 /mod=11 + V W + + + aso rexr rexx rexb + ssef3 0f 16 /mod=!11 + V W + + + + + movsldup + + aso rexr rexx rexb + ssef3 0f 12 /mod=11 + V W + + + aso rexr rexx rexb + ssef3 0f 12 /mod=!11 + V W + + + + + + + pabsb + + aso rexr rexx rexb + 0f 38 1c + P Q + + + aso rexr rexx rexb + sse66 0f 38 1c + V W + + + + + pabsw + + aso rexr rexx rexb + 0f 38 1d + P Q + + + aso rexr rexx rexb + sse66 0f 38 1d + V W + + + + + pabsd + + aso rexr rexx rexb + 0f 38 1e + P Q + + + aso rexr rexx rexb + sse66 0f 38 1e + V W + + + + + psignb + + aso rexr rexx rexb + 0f 38 00 + P Q + + + aso rexr rexx rexb + sse66 0f 38 00 + V W + + + + + phaddw + + aso rexr rexx rexb + 0f 38 01 + P Q + + + aso rexr rexx rexb + sse66 0f 38 01 + V W + + + + + phaddd + + aso rexr rexx rexb + 0f 38 02 + P Q + + + aso rexr rexx rexb + sse66 0f 38 02 + V W + + + + + phaddsw + + aso rexr rexx rexb + 0f 38 03 + P Q + + + aso rexr rexx rexb + sse66 0f 38 03 + V W + + + + + pmaddubsw + + aso rexr rexx rexb + 0f 38 04 + P Q + + + aso rexr rexx rexb + sse66 0f 38 04 + V W + + + + + phsubw + + aso rexr rexx rexb + 0f 38 05 + P Q + + + aso rexr rexx rexb + sse66 0f 38 05 + V W + + + + + phsubd + + aso rexr rexx rexb + 0f 38 06 + P Q + + + aso rexr rexx rexb + sse66 0f 38 06 + V W + + + + + phsubsw + + aso rexr rexx rexb + 0f 38 07 + P Q + + + aso rexr rexx rexb + sse66 0f 38 07 + V W + + + + + psignb + + aso rexr rexx rexb + 0f 38 08 + P Q + + + aso rexr rexx rexb + sse66 0f 38 08 + V W + + + + + psignd + + aso rexr rexx rexb + 0f 38 0a + P Q + + + aso rexr rexx rexb + sse66 0f 38 0a + V W + + + + + psignw + + aso rexr rexx rexb + 0f 38 09 + P Q + + + aso rexr rexx rexb + sse66 0f 38 09 + V W + + + + + pmulhrsw + + aso rexr rexx rexb + 0f 38 0b + P Q + + + aso rexr rexx rexb + sse66 0f 38 0b + V W + + + + + palignr + + aso rexr rexx rexb + 0f 3a 0f + P Q Ib + + + aso rexr rexx rexb + sse66 0f 3a 0f + V W Ib + + + + + + + pblendvb + + aso rexr rexx rexb + sse66 0f 38 10 + V W + + + + + pmuldq + + aso rexr rexx rexb + sse66 0f 38 28 + V W + + + + + pminsb + + aso rexr rexx rexb + sse66 0f 38 38 + V W + + + + + pminsd + + aso rexr rexx rexb + sse66 0f 38 39 + V W + + + + + pminuw + + aso rexr rexx rexb + sse66 0f 38 3a + V W + + + + + pminud + + aso rexr rexx rexb + sse66 0f 38 3b + V W + + + + + pmaxsb + + aso rexr rexx rexb + sse66 0f 38 3c + V W + + + + + pmaxsd + + aso rexr rexx rexb + sse66 0f 38 3d + V W + + + + + pmaxud + + aso rexr rexx rexb + sse66 0f 38 3f + V W + + + + + pmulld + + aso rexr rexx rexb + sse66 0f 38 40 + V W + + + + + phminposuw + + aso rexr rexx rexb + sse66 0f 38 41 + V W + + + + + roundps + + aso rexr rexx rexb + sse66 0f 3a 08 + V W Ib + + + + + roundpd + + aso rexr rexx rexb + sse66 0f 3a 09 + V W Ib + + + + + roundss + + aso rexr rexx rexb + sse66 0f 3a 0a + V W Ib + + + + + roundsd + + aso rexr rexx rexb + sse66 0f 3a 0b + V W Ib + + + + + blendpd + + aso rexr rexx rexb + sse66 0f 3a 0d + V W Ib + + + + + pblendw + + aso rexr rexx rexb + sse66 0f 3a 0e + V W Ib + + + + + blendps + + aso rexr rexx rexb + sse66 0f 3a 0c + V W Ib + + + + + blendvpd + + aso rexr rexx rexb + sse66 0f 38 15 + V W + + + + + blendvps + + aso rexr rexx rexb + sse66 0f 38 14 + V W + + + + + dpps + + aso rexr rexx rexb + sse66 0f 3a 40 + V W Ib + + + + + dppd + + aso rexr rexx rexb + sse66 0f 3a 41 + V W Ib + + + + + mpsadbw + + aso rexr rexx rexb + sse66 0f 3a 42 + V W Ib + + + + + extractps + + aso rexr rexw rexb + sse66 0f 3a 17 + MdRy V Ib + + + + + invalid + + + diff --git a/src/3rdparty/masm/disassembler/udis86/ud_opcode.py b/src/3rdparty/masm/disassembler/udis86/ud_opcode.py new file mode 100644 index 0000000000..f301b52461 --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/ud_opcode.py @@ -0,0 +1,235 @@ +# udis86 - scripts/ud_opcode.py +# +# Copyright (c) 2009 Vivek Thampi +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +class UdOpcodeTables: + + TableInfo = { + 'opctbl' : { 'name' : 'UD_TAB__OPC_TABLE', 'size' : 256 }, + '/sse' : { 'name' : 'UD_TAB__OPC_SSE', 'size' : 4 }, + '/reg' : { 'name' : 'UD_TAB__OPC_REG', 'size' : 8 }, + '/rm' : { 'name' : 'UD_TAB__OPC_RM', 'size' : 8 }, + '/mod' : { 'name' : 'UD_TAB__OPC_MOD', 'size' : 2 }, + '/m' : { 'name' : 'UD_TAB__OPC_MODE', 'size' : 3 }, + '/x87' : { 'name' : 'UD_TAB__OPC_X87', 'size' : 64 }, + '/a' : { 'name' : 'UD_TAB__OPC_ASIZE', 'size' : 3 }, + '/o' : { 'name' : 'UD_TAB__OPC_OSIZE', 'size' : 3 }, + '/3dnow' : { 'name' : 'UD_TAB__OPC_3DNOW', 'size' : 256 }, + 'vendor' : { 'name' : 'UD_TAB__OPC_VENDOR', 'size' : 3 }, + } + + OpcodeTable0 = { + 'type' : 'opctbl', + 'entries' : {}, + 'meta' : 'table0' + } + + OpcExtIndex = { + + # ssef2, ssef3, sse66 + 'sse': { + 'none' : '00', + 'f2' : '01', + 'f3' : '02', + '66' : '03' + }, + + # /mod= + 'mod': { + '!11' : '00', + '11' : '01' + }, + + # /m=, /o=, /a= + 'mode': { + '16' : '00', + '32' : '01', + '64' : '02' + }, + + 'vendor' : { + 'amd' : '00', + 'intel' : '01', + 'any' : '02' + } + } + + InsnTable = [] + MnemonicsTable = [] + + ThreeDNowTable = {} + + def sizeOfTable( self, t ): + return self.TableInfo[ t ][ 'size' ] + + def nameOfTable( self, t ): + return self.TableInfo[ t ][ 'name' ] + + # + # Updates a table entry: If the entry doesn't exist + # it will create the entry, otherwise, it will walk + # while validating the path. + # + def updateTable( self, table, index, type, meta ): + if not index in table[ 'entries' ]: + table[ 'entries' ][ index ] = { 'type' : type, 'entries' : {}, 'meta' : meta } + if table[ 'entries' ][ index ][ 'type' ] != type: + raise NameError( "error: violation in opcode mapping (overwrite) %s with %s." % + ( table[ 'entries' ][ index ][ 'type' ], type) ) + return table[ 'entries' ][ index ] + + class Insn: + """An abstract type representing an instruction in the opcode map. + """ + + # A mapping of opcode extensions to their representational + # values used in the opcode map. + OpcExtMap = { + '/rm' : lambda v: "%02x" % int(v, 16), + '/x87' : lambda v: "%02x" % int(v, 16), + '/3dnow' : lambda v: "%02x" % int(v, 16), + '/reg' : lambda v: "%02x" % int(v, 16), + # modrm.mod + # (!11, 11) => (00, 01) + '/mod' : lambda v: '00' if v == '!11' else '01', + # Mode extensions: + # (16, 32, 64) => (00, 01, 02) + '/o' : lambda v: "%02x" % (int(v) / 32), + '/a' : lambda v: "%02x" % (int(v) / 32), + '/m' : lambda v: "%02x" % (int(v) / 32), + '/sse' : lambda v: UdOpcodeTables.OpcExtIndex['sse'][v] + } + + def __init__(self, prefixes, mnemonic, opcodes, operands, vendor): + self.opcodes = opcodes + self.prefixes = prefixes + self.mnemonic = mnemonic + self.operands = operands + self.vendor = vendor + self.opcext = {} + + ssePrefix = None + if self.opcodes[0] in ('ssef2', 'ssef3', 'sse66'): + ssePrefix = self.opcodes[0][3:] + self.opcodes.pop(0) + + # do some preliminary decoding of the instruction type + # 1byte, 2byte or 3byte instruction? + self.nByteInsn = 1 + if self.opcodes[0] == '0f': # 2byte + # 2+ byte opcodes are always disambiguated by an + # sse prefix, unless it is a 3d now instruction + # which is 0f 0f ... + if self.opcodes[1] != '0f' and ssePrefix is None: + ssePrefix = 'none' + if self.opcodes[1] in ('38', '3a'): # 3byte + self.nByteInsn = 3 + else: + self.nByteInsn = 2 + + # The opcode that indexes into the opcode table. + self.opcode = self.opcodes[self.nByteInsn - 1] + + # Record opcode extensions + for opcode in self.opcodes[self.nByteInsn:]: + arg, val = opcode.split('=') + self.opcext[arg] = self.OpcExtMap[arg](val) + + # Record sse extension: the reason sse extension is handled + # separately is that historically sse was handled as a first + # class opcode, not as an extension. Now that sse is handled + # as an extension, we do the manual conversion here, as opposed + # to modifying the opcode xml file. + if ssePrefix is not None: + self.opcext['/sse'] = self.OpcExtMap['/sse'](ssePrefix) + + def parse(self, table, insn): + index = insn.opcodes[0]; + if insn.nByteInsn > 1: + assert index == '0f' + table = self.updateTable(table, index, 'opctbl', '0f') + index = insn.opcodes[1] + + if insn.nByteInsn == 3: + table = self.updateTable(table, index, 'opctbl', index) + index = insn.opcodes[2] + + # Walk down the tree, create levels as needed, for opcode + # extensions. The order is important, and determines how + # well the opcode table is packed. Also note, /sse must be + # before /o, because /sse may consume operand size prefix + # affect the outcome of /o. + for ext in ('/mod', '/x87', '/reg', '/rm', '/sse', + '/o', '/a', '/m', '/3dnow'): + if ext in insn.opcext: + table = self.updateTable(table, index, ext, ext) + index = insn.opcext[ext] + + # additional table for disambiguating vendor + if len(insn.vendor): + table = self.updateTable(table, index, 'vendor', insn.vendor) + index = self.OpcExtIndex['vendor'][insn.vendor] + + # make leaf node entries + leaf = self.updateTable(table, index, 'insn', '') + + leaf['mnemonic'] = insn.mnemonic + leaf['prefixes'] = insn.prefixes + leaf['operands'] = insn.operands + + # add instruction to linear table of instruction forms + self.InsnTable.append({ 'prefixes' : insn.prefixes, + 'mnemonic' : insn.mnemonic, + 'operands' : insn.operands }) + + # add mnemonic to mnemonic table + if not insn.mnemonic in self.MnemonicsTable: + self.MnemonicsTable.append(insn.mnemonic) + + + # Adds an instruction definition to the opcode tables + def addInsnDef( self, prefixes, mnemonic, opcodes, operands, vendor ): + insn = self.Insn(prefixes=prefixes, + mnemonic=mnemonic, + opcodes=opcodes, + operands=operands, + vendor=vendor) + self.parse(self.OpcodeTable0, insn) + + def print_table( self, table, pfxs ): + print "%s |" % pfxs + keys = table[ 'entries' ].keys() + if ( len( keys ) ): + keys.sort() + for idx in keys: + e = table[ 'entries' ][ idx ] + if e[ 'type' ] == 'insn': + print "%s |-<%s>" % ( pfxs, idx ), + print "%s %s" % ( e[ 'mnemonic' ], ' '.join( e[ 'operands'] ) ) + else: + print "%s |-<%s> %s" % ( pfxs, idx, e['type'] ) + self.print_table( e, pfxs + ' |' ) + + def print_tree( self ): + self.print_table( self.OpcodeTable0, '' ) diff --git a/src/3rdparty/masm/disassembler/udis86/ud_optable.py b/src/3rdparty/masm/disassembler/udis86/ud_optable.py new file mode 100644 index 0000000000..5b5c55d3b8 --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/ud_optable.py @@ -0,0 +1,103 @@ +# udis86 - scripts/ud_optable.py (optable.xml parser) +# +# Copyright (c) 2009 Vivek Thampi +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import os +import sys +from xml.dom import minidom + +class UdOptableXmlParser: + + def parseDef( self, node ): + ven = '' + pfx = [] + opc = [] + opr = [] + for def_node in node.childNodes: + if not def_node.localName: + continue + if def_node.localName == 'pfx': + pfx = def_node.firstChild.data.split(); + elif def_node.localName == 'opc': + opc = def_node.firstChild.data.split(); + elif def_node.localName == 'opr': + opr = def_node.firstChild.data.split(); + elif def_node.localName == 'mode': + pfx.extend( def_node.firstChild.data.split() ); + elif def_node.localName == 'syn': + pfx.extend( def_node.firstChild.data.split() ); + elif def_node.localName == 'vendor': + ven = ( def_node.firstChild.data ); + else: + print "warning: invalid node - %s" % def_node.localName + continue + return ( pfx, opc, opr, ven ) + + def parse( self, xml, fn ): + xmlDoc = minidom.parse( xml ) + self.TlNode = xmlDoc.firstChild + + while self.TlNode and self.TlNode.localName != "x86optable": + self.TlNode = self.TlNode.nextSibling + + for insnNode in self.TlNode.childNodes: + if not insnNode.localName: + continue + if insnNode.localName != "instruction": + print "warning: invalid insn node - %s" % insnNode.localName + continue + + mnemonic = insnNode.getElementsByTagName( 'mnemonic' )[ 0 ].firstChild.data + vendor = '' + + for node in insnNode.childNodes: + if node.localName == 'vendor': + vendor = node.firstChild.data + elif node.localName == 'def': + ( prefixes, opcodes, operands, local_vendor ) = \ + self.parseDef( node ) + if ( len( local_vendor ) ): + vendor = local_vendor + # callback + fn( prefixes, mnemonic, opcodes, operands, vendor ) + + +def printFn( pfx, mnm, opc, opr, ven ): + print 'def: ', + if len( pfx ): + print ' '.join( pfx ), + print "%s %s %s %s" % \ + ( mnm, ' '.join( opc ), ' '.join( opr ), ven ) + + +def parse( xml, callback ): + parser = UdOptableXmlParser() + parser.parse( xml, callback ) + +def main(): + parser = UdOptableXmlParser() + parser.parse( sys.argv[ 1 ], printFn ) + +if __name__ == "__main__": + main() diff --git a/src/3rdparty/masm/disassembler/udis86/udis86.c b/src/3rdparty/masm/disassembler/udis86/udis86.c new file mode 100644 index 0000000000..fbf76707a0 --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/udis86.c @@ -0,0 +1,183 @@ +/* udis86 - libudis86/udis86.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_input.h" +#include "udis86_extern.h" + +#ifndef __UD_STANDALONE__ +# include +# include +#endif /* __UD_STANDALONE__ */ + +/* ============================================================================= + * ud_init() - Initializes ud_t object. + * ============================================================================= + */ +extern void +ud_init(struct ud* u) +{ + memset((void*)u, 0, sizeof(struct ud)); + ud_set_mode(u, 16); + u->mnemonic = UD_Iinvalid; + ud_set_pc(u, 0); +#ifndef __UD_STANDALONE__ + ud_set_input_file(u, stdin); +#endif /* __UD_STANDALONE__ */ +} + +/* ============================================================================= + * ud_disassemble() - disassembles one instruction and returns the number of + * bytes disassembled. A zero means end of disassembly. + * ============================================================================= + */ +extern unsigned int +ud_disassemble(struct ud* u) +{ + if (ud_input_end(u)) + return 0; + + + u->insn_buffer[0] = u->insn_hexcode[0] = 0; + + + if (ud_decode(u) == 0) + return 0; + if (u->translator) + u->translator(u); + return ud_insn_len(u); +} + +/* ============================================================================= + * ud_set_mode() - Set Disassemly Mode. + * ============================================================================= + */ +extern void +ud_set_mode(struct ud* u, uint8_t m) +{ + switch(m) { + case 16: + case 32: + case 64: u->dis_mode = m ; return; + default: u->dis_mode = 16; return; + } +} + +/* ============================================================================= + * ud_set_vendor() - Set vendor. + * ============================================================================= + */ +extern void +ud_set_vendor(struct ud* u, unsigned v) +{ + switch(v) { + case UD_VENDOR_INTEL: + u->vendor = v; + break; + case UD_VENDOR_ANY: + u->vendor = v; + break; + default: + u->vendor = UD_VENDOR_AMD; + } +} + +/* ============================================================================= + * ud_set_pc() - Sets code origin. + * ============================================================================= + */ +extern void +ud_set_pc(struct ud* u, uint64_t o) +{ + u->pc = o; +} + +/* ============================================================================= + * ud_set_syntax() - Sets the output syntax. + * ============================================================================= + */ +extern void +ud_set_syntax(struct ud* u, void (*t)(struct ud*)) +{ + u->translator = t; +} + +/* ============================================================================= + * ud_insn() - returns the disassembled instruction + * ============================================================================= + */ +extern char* +ud_insn_asm(struct ud* u) +{ + return u->insn_buffer; +} + +/* ============================================================================= + * ud_insn_offset() - Returns the offset. + * ============================================================================= + */ +extern uint64_t +ud_insn_off(struct ud* u) +{ + return u->insn_offset; +} + + +/* ============================================================================= + * ud_insn_hex() - Returns hex form of disassembled instruction. + * ============================================================================= + */ +extern char* +ud_insn_hex(struct ud* u) +{ + return u->insn_hexcode; +} + +/* ============================================================================= + * ud_insn_ptr() - Returns code disassembled. + * ============================================================================= + */ +extern uint8_t* +ud_insn_ptr(struct ud* u) +{ + return u->inp_sess; +} + +/* ============================================================================= + * ud_insn_len() - Returns the count of bytes disassembled. + * ============================================================================= + */ +extern unsigned int +ud_insn_len(struct ud* u) +{ + return u->inp_ctr; +} + +#endif // USE(UDIS86) diff --git a/src/3rdparty/masm/disassembler/udis86/udis86.h b/src/3rdparty/masm/disassembler/udis86/udis86.h new file mode 100644 index 0000000000..baaf495e04 --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/udis86.h @@ -0,0 +1,33 @@ +/* udis86 - udis86.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UDIS86_H +#define UDIS86_H + +#include "udis86_types.h" +#include "udis86_extern.h" +#include "udis86_itab.h" + +#endif diff --git a/src/3rdparty/masm/disassembler/udis86/udis86_decode.c b/src/3rdparty/masm/disassembler/udis86/udis86_decode.c new file mode 100644 index 0000000000..a3fd576655 --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/udis86_decode.c @@ -0,0 +1,1142 @@ +/* udis86 - libudis86/decode.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_extern.h" +#include "udis86_types.h" +#include "udis86_input.h" +#include "udis86_decode.h" +#include + +#define dbg(x, n...) +/* #define dbg printf */ + +#ifndef __UD_STANDALONE__ +# include +#endif /* __UD_STANDALONE__ */ + +/* The max number of prefixes to an instruction */ +#define MAX_PREFIXES 15 + +/* instruction aliases and special cases */ +static struct ud_itab_entry s_ie__invalid = + { UD_Iinvalid, O_NONE, O_NONE, O_NONE, P_none }; + +static int +decode_ext(struct ud *u, uint16_t ptr); + + +static inline int +eff_opr_mode(int dis_mode, int rex_w, int pfx_opr) +{ + if (dis_mode == 64) { + return rex_w ? 64 : (pfx_opr ? 16 : 32); + } else if (dis_mode == 32) { + return pfx_opr ? 16 : 32; + } else { + ASSERT(dis_mode == 16); + return pfx_opr ? 32 : 16; + } +} + + +static inline int +eff_adr_mode(int dis_mode, int pfx_adr) +{ + if (dis_mode == 64) { + return pfx_adr ? 32 : 64; + } else if (dis_mode == 32) { + return pfx_adr ? 16 : 32; + } else { + ASSERT(dis_mode == 16); + return pfx_adr ? 32 : 16; + } +} + + +/* Looks up mnemonic code in the mnemonic string table + * Returns NULL if the mnemonic code is invalid + */ +const char * ud_lookup_mnemonic( enum ud_mnemonic_code c ) +{ + return ud_mnemonics_str[ c ]; +} + + +/* + * decode_prefixes + * + * Extracts instruction prefixes. + */ +static int +decode_prefixes(struct ud *u) +{ + unsigned int have_pfx = 1; + unsigned int i; + uint8_t curr; + + /* if in error state, bail out */ + if ( u->error ) + return -1; + + /* keep going as long as there are prefixes available */ + for ( i = 0; have_pfx ; ++i ) { + + /* Get next byte. */ + ud_inp_next(u); + if ( u->error ) + return -1; + curr = ud_inp_curr( u ); + + /* rex prefixes in 64bit mode */ + if ( u->dis_mode == 64 && ( curr & 0xF0 ) == 0x40 ) { + u->pfx_rex = curr; + } else { + switch ( curr ) + { + case 0x2E : + u->pfx_seg = UD_R_CS; + u->pfx_rex = 0; + break; + case 0x36 : + u->pfx_seg = UD_R_SS; + u->pfx_rex = 0; + break; + case 0x3E : + u->pfx_seg = UD_R_DS; + u->pfx_rex = 0; + break; + case 0x26 : + u->pfx_seg = UD_R_ES; + u->pfx_rex = 0; + break; + case 0x64 : + u->pfx_seg = UD_R_FS; + u->pfx_rex = 0; + break; + case 0x65 : + u->pfx_seg = UD_R_GS; + u->pfx_rex = 0; + break; + case 0x67 : /* adress-size override prefix */ + u->pfx_adr = 0x67; + u->pfx_rex = 0; + break; + case 0xF0 : + u->pfx_lock = 0xF0; + u->pfx_rex = 0; + break; + case 0x66: + /* the 0x66 sse prefix is only effective if no other sse prefix + * has already been specified. + */ + if ( !u->pfx_insn ) u->pfx_insn = 0x66; + u->pfx_opr = 0x66; + u->pfx_rex = 0; + break; + case 0xF2: + u->pfx_insn = 0xF2; + u->pfx_repne = 0xF2; + u->pfx_rex = 0; + break; + case 0xF3: + u->pfx_insn = 0xF3; + u->pfx_rep = 0xF3; + u->pfx_repe = 0xF3; + u->pfx_rex = 0; + break; + default : + /* No more prefixes */ + have_pfx = 0; + break; + } + } + + /* check if we reached max instruction length */ + if ( i + 1 == MAX_INSN_LENGTH ) { + u->error = 1; + break; + } + } + + /* return status */ + if ( u->error ) + return -1; + + /* rewind back one byte in stream, since the above loop + * stops with a non-prefix byte. + */ + ud_inp_back(u); + return 0; +} + + +static inline unsigned int modrm( struct ud * u ) +{ + if ( !u->have_modrm ) { + u->modrm = ud_inp_next( u ); + u->have_modrm = 1; + } + return u->modrm; +} + + +static unsigned int resolve_operand_size( const struct ud * u, unsigned int s ) +{ + switch ( s ) + { + case SZ_V: + return ( u->opr_mode ); + case SZ_Z: + return ( u->opr_mode == 16 ) ? 16 : 32; + case SZ_P: + return ( u->opr_mode == 16 ) ? SZ_WP : SZ_DP; + case SZ_MDQ: + return ( u->opr_mode == 16 ) ? 32 : u->opr_mode; + case SZ_RDQ: + return ( u->dis_mode == 64 ) ? 64 : 32; + default: + return s; + } +} + + +static int resolve_mnemonic( struct ud* u ) +{ + /* far/near flags */ + u->br_far = 0; + u->br_near = 0; + /* readjust operand sizes for call/jmp instrcutions */ + if ( u->mnemonic == UD_Icall || u->mnemonic == UD_Ijmp ) { + /* WP: 16:16 pointer */ + if ( u->operand[ 0 ].size == SZ_WP ) { + u->operand[ 0 ].size = 16; + u->br_far = 1; + u->br_near= 0; + /* DP: 32:32 pointer */ + } else if ( u->operand[ 0 ].size == SZ_DP ) { + u->operand[ 0 ].size = 32; + u->br_far = 1; + u->br_near= 0; + } else { + u->br_far = 0; + u->br_near= 1; + } + /* resolve 3dnow weirdness. */ + } else if ( u->mnemonic == UD_I3dnow ) { + u->mnemonic = ud_itab[ u->le->table[ ud_inp_curr( u ) ] ].mnemonic; + } + /* SWAPGS is only valid in 64bits mode */ + if ( u->mnemonic == UD_Iswapgs && u->dis_mode != 64 ) { + u->error = 1; + return -1; + } + + if (u->mnemonic == UD_Ixchg) { + if ((u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_AX && + u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_AX) || + (u->operand[0].type == UD_OP_REG && u->operand[0].base == UD_R_EAX && + u->operand[1].type == UD_OP_REG && u->operand[1].base == UD_R_EAX)) { + u->operand[0].type = UD_NONE; + u->operand[1].type = UD_NONE; + u->mnemonic = UD_Inop; + } + } + + if (u->mnemonic == UD_Inop && u->pfx_rep) { + u->pfx_rep = 0; + u->mnemonic = UD_Ipause; + } + return 0; +} + + +/* ----------------------------------------------------------------------------- + * decode_a()- Decodes operands of the type seg:offset + * ----------------------------------------------------------------------------- + */ +static void +decode_a(struct ud* u, struct ud_operand *op) +{ + if (u->opr_mode == 16) { + /* seg16:off16 */ + op->type = UD_OP_PTR; + op->size = 32; + op->lval.ptr.off = ud_inp_uint16(u); + op->lval.ptr.seg = ud_inp_uint16(u); + } else { + /* seg16:off32 */ + op->type = UD_OP_PTR; + op->size = 48; + op->lval.ptr.off = ud_inp_uint32(u); + op->lval.ptr.seg = ud_inp_uint16(u); + } +} + +/* ----------------------------------------------------------------------------- + * decode_gpr() - Returns decoded General Purpose Register + * ----------------------------------------------------------------------------- + */ +static enum ud_type +decode_gpr(register struct ud* u, unsigned int s, unsigned char rm) +{ + s = resolve_operand_size(u, s); + + switch (s) { + case 64: + return UD_R_RAX + rm; + case SZ_DP: + case 32: + return UD_R_EAX + rm; + case SZ_WP: + case 16: + return UD_R_AX + rm; + case 8: + if (u->dis_mode == 64 && u->pfx_rex) { + if (rm >= 4) + return UD_R_SPL + (rm-4); + return UD_R_AL + rm; + } else return UD_R_AL + rm; + default: + return 0; + } +} + +/* ----------------------------------------------------------------------------- + * resolve_gpr64() - 64bit General Purpose Register-Selection. + * ----------------------------------------------------------------------------- + */ +static enum ud_type +resolve_gpr64(struct ud* u, enum ud_operand_code gpr_op, enum ud_operand_size * size) +{ + if (gpr_op >= OP_rAXr8 && gpr_op <= OP_rDIr15) + gpr_op = (gpr_op - OP_rAXr8) | (REX_B(u->pfx_rex) << 3); + else gpr_op = (gpr_op - OP_rAX); + + if (u->opr_mode == 16) { + *size = 16; + return gpr_op + UD_R_AX; + } + if (u->dis_mode == 32 || + (u->opr_mode == 32 && ! (REX_W(u->pfx_rex) || u->default64))) { + *size = 32; + return gpr_op + UD_R_EAX; + } + + *size = 64; + return gpr_op + UD_R_RAX; +} + +/* ----------------------------------------------------------------------------- + * resolve_gpr32 () - 32bit General Purpose Register-Selection. + * ----------------------------------------------------------------------------- + */ +static enum ud_type +resolve_gpr32(struct ud* u, enum ud_operand_code gpr_op) +{ + gpr_op = gpr_op - OP_eAX; + + if (u->opr_mode == 16) + return gpr_op + UD_R_AX; + + return gpr_op + UD_R_EAX; +} + +/* ----------------------------------------------------------------------------- + * resolve_reg() - Resolves the register type + * ----------------------------------------------------------------------------- + */ +static enum ud_type +resolve_reg(struct ud* u, unsigned int type, unsigned char i) +{ + switch (type) { + case T_MMX : return UD_R_MM0 + (i & 7); + case T_XMM : return UD_R_XMM0 + i; + case T_CRG : return UD_R_CR0 + i; + case T_DBG : return UD_R_DR0 + i; + case T_SEG : { + /* + * Only 6 segment registers, anything else is an error. + */ + if ((i & 7) > 5) { + u->error = 1; + } else { + return UD_R_ES + (i & 7); + } + } + case T_NONE: + default: return UD_NONE; + } +} + +/* ----------------------------------------------------------------------------- + * decode_imm() - Decodes Immediate values. + * ----------------------------------------------------------------------------- + */ +static void +decode_imm(struct ud* u, unsigned int s, struct ud_operand *op) +{ + op->size = resolve_operand_size(u, s); + op->type = UD_OP_IMM; + + switch (op->size) { + case 8: op->lval.sbyte = ud_inp_uint8(u); break; + case 16: op->lval.uword = ud_inp_uint16(u); break; + case 32: op->lval.udword = ud_inp_uint32(u); break; + case 64: op->lval.uqword = ud_inp_uint64(u); break; + default: return; + } +} + + +/* + * decode_modrm_reg + * + * Decodes reg field of mod/rm byte + * + */ +static void +decode_modrm_reg(struct ud *u, + struct ud_operand *operand, + unsigned int type, + unsigned int size) +{ + uint8_t reg = (REX_R(u->pfx_rex) << 3) | MODRM_REG(modrm(u)); + operand->type = UD_OP_REG; + operand->size = resolve_operand_size(u, size); + + if (type == T_GPR) { + operand->base = decode_gpr(u, operand->size, reg); + } else { + operand->base = resolve_reg(u, type, reg); + } +} + + +/* + * decode_modrm_rm + * + * Decodes rm field of mod/rm byte + * + */ +static void +decode_modrm_rm(struct ud *u, + struct ud_operand *op, + unsigned char type, + unsigned int size) + +{ + unsigned char mod, rm, reg; + + /* get mod, r/m and reg fields */ + mod = MODRM_MOD(modrm(u)); + rm = (REX_B(u->pfx_rex) << 3) | MODRM_RM(modrm(u)); + reg = (REX_R(u->pfx_rex) << 3) | MODRM_REG(modrm(u)); + + op->size = resolve_operand_size(u, size); + + /* + * If mod is 11b, then the modrm.rm specifies a register. + * + */ + if (mod == 3) { + op->type = UD_OP_REG; + if (type == T_GPR) { + op->base = decode_gpr(u, op->size, rm); + } else { + op->base = resolve_reg(u, type, (REX_B(u->pfx_rex) << 3) | (rm & 7)); + } + return; + } + + + /* + * !11 => Memory Address + */ + op->type = UD_OP_MEM; + + if (u->adr_mode == 64) { + op->base = UD_R_RAX + rm; + if (mod == 1) { + op->offset = 8; + } else if (mod == 2) { + op->offset = 32; + } else if (mod == 0 && (rm & 7) == 5) { + op->base = UD_R_RIP; + op->offset = 32; + } else { + op->offset = 0; + } + /* + * Scale-Index-Base (SIB) + */ + if ((rm & 7) == 4) { + ud_inp_next(u); + + op->scale = (1 << SIB_S(ud_inp_curr(u))) & ~1; + op->index = UD_R_RAX + (SIB_I(ud_inp_curr(u)) | (REX_X(u->pfx_rex) << 3)); + op->base = UD_R_RAX + (SIB_B(ud_inp_curr(u)) | (REX_B(u->pfx_rex) << 3)); + + /* special conditions for base reference */ + if (op->index == UD_R_RSP) { + op->index = UD_NONE; + op->scale = UD_NONE; + } + + if (op->base == UD_R_RBP || op->base == UD_R_R13) { + if (mod == 0) { + op->base = UD_NONE; + } + if (mod == 1) { + op->offset = 8; + } else { + op->offset = 32; + } + } + } + } else if (u->adr_mode == 32) { + op->base = UD_R_EAX + rm; + if (mod == 1) { + op->offset = 8; + } else if (mod == 2) { + op->offset = 32; + } else if (mod == 0 && rm == 5) { + op->base = UD_NONE; + op->offset = 32; + } else { + op->offset = 0; + } + + /* Scale-Index-Base (SIB) */ + if ((rm & 7) == 4) { + ud_inp_next(u); + + op->scale = (1 << SIB_S(ud_inp_curr(u))) & ~1; + op->index = UD_R_EAX + (SIB_I(ud_inp_curr(u)) | (REX_X(u->pfx_rex) << 3)); + op->base = UD_R_EAX + (SIB_B(ud_inp_curr(u)) | (REX_B(u->pfx_rex) << 3)); + + if (op->index == UD_R_ESP) { + op->index = UD_NONE; + op->scale = UD_NONE; + } + + /* special condition for base reference */ + if (op->base == UD_R_EBP) { + if (mod == 0) { + op->base = UD_NONE; + } + if (mod == 1) { + op->offset = 8; + } else { + op->offset = 32; + } + } + } + } else { + const unsigned int bases[] = { UD_R_BX, UD_R_BX, UD_R_BP, UD_R_BP, + UD_R_SI, UD_R_DI, UD_R_BP, UD_R_BX }; + const unsigned int indices[] = { UD_R_SI, UD_R_DI, UD_R_SI, UD_R_DI, + UD_NONE, UD_NONE, UD_NONE, UD_NONE }; + op->base = bases[rm & 7]; + op->index = indices[rm & 7]; + if (mod == 0 && rm == 6) { + op->offset= 16; + op->base = UD_NONE; + } else if (mod == 1) { + op->offset = 8; + } else if (mod == 2) { + op->offset = 16; + } + } + + /* + * extract offset, if any + */ + switch (op->offset) { + case 8 : op->lval.ubyte = ud_inp_uint8(u); break; + case 16: op->lval.uword = ud_inp_uint16(u); break; + case 32: op->lval.udword = ud_inp_uint32(u); break; + case 64: op->lval.uqword = ud_inp_uint64(u); break; + default: break; + } +} + +/* ----------------------------------------------------------------------------- + * decode_o() - Decodes offset + * ----------------------------------------------------------------------------- + */ +static void +decode_o(struct ud* u, unsigned int s, struct ud_operand *op) +{ + switch (u->adr_mode) { + case 64: + op->offset = 64; + op->lval.uqword = ud_inp_uint64(u); + break; + case 32: + op->offset = 32; + op->lval.udword = ud_inp_uint32(u); + break; + case 16: + op->offset = 16; + op->lval.uword = ud_inp_uint16(u); + break; + default: + return; + } + op->type = UD_OP_MEM; + op->size = resolve_operand_size(u, s); +} + +/* ----------------------------------------------------------------------------- + * decode_operands() - Disassembles Operands. + * ----------------------------------------------------------------------------- + */ +static int +decode_operand(struct ud *u, + struct ud_operand *operand, + enum ud_operand_code type, + unsigned int size) +{ + switch (type) { + case OP_A : + decode_a(u, operand); + break; + case OP_MR: + if (MODRM_MOD(modrm(u)) == 3) { + decode_modrm_rm(u, operand, T_GPR, + size == SZ_DY ? SZ_MDQ : SZ_V); + } else if (size == SZ_WV) { + decode_modrm_rm( u, operand, T_GPR, SZ_W); + } else if (size == SZ_BV) { + decode_modrm_rm( u, operand, T_GPR, SZ_B); + } else if (size == SZ_DY) { + decode_modrm_rm( u, operand, T_GPR, SZ_D); + } else { + ASSERT(!"unexpected size"); + } + break; + case OP_M: + if (MODRM_MOD(modrm(u)) == 3) { + u->error = 1; + } + /* intended fall through */ + case OP_E: + decode_modrm_rm(u, operand, T_GPR, size); + break; + break; + case OP_G: + decode_modrm_reg(u, operand, T_GPR, size); + break; + case OP_I: + decode_imm(u, size, operand); + break; + case OP_I1: + operand->type = UD_OP_CONST; + operand->lval.udword = 1; + break; + case OP_PR: + if (MODRM_MOD(modrm(u)) != 3) { + u->error = 1; + } + decode_modrm_rm(u, operand, T_MMX, size); + break; + case OP_P: + decode_modrm_reg(u, operand, T_MMX, size); + break; + case OP_VR: + if (MODRM_MOD(modrm(u)) != 3) { + u->error = 1; + } + /* intended fall through */ + case OP_W: + decode_modrm_rm(u, operand, T_XMM, size); + break; + case OP_V: + decode_modrm_reg(u, operand, T_XMM, size); + break; + case OP_S: + decode_modrm_reg(u, operand, T_SEG, size); + break; + case OP_AL: + case OP_CL: + case OP_DL: + case OP_BL: + case OP_AH: + case OP_CH: + case OP_DH: + case OP_BH: + operand->type = UD_OP_REG; + operand->base = UD_R_AL + (type - OP_AL); + operand->size = 8; + break; + case OP_DX: + operand->type = UD_OP_REG; + operand->base = UD_R_DX; + operand->size = 16; + break; + case OP_O: + decode_o(u, size, operand); + break; + case OP_rAXr8: + case OP_rCXr9: + case OP_rDXr10: + case OP_rBXr11: + case OP_rSPr12: + case OP_rBPr13: + case OP_rSIr14: + case OP_rDIr15: + case OP_rAX: + case OP_rCX: + case OP_rDX: + case OP_rBX: + case OP_rSP: + case OP_rBP: + case OP_rSI: + case OP_rDI: + operand->type = UD_OP_REG; + operand->base = resolve_gpr64(u, type, &operand->size); + break; + case OP_ALr8b: + case OP_CLr9b: + case OP_DLr10b: + case OP_BLr11b: + case OP_AHr12b: + case OP_CHr13b: + case OP_DHr14b: + case OP_BHr15b: { + ud_type_t gpr = (type - OP_ALr8b) + UD_R_AL + + (REX_B(u->pfx_rex) << 3); + if (UD_R_AH <= gpr && u->pfx_rex) { + gpr = gpr + 4; + } + operand->type = UD_OP_REG; + operand->base = gpr; + break; + } + case OP_eAX: + case OP_eCX: + case OP_eDX: + case OP_eBX: + case OP_eSP: + case OP_eBP: + case OP_eSI: + case OP_eDI: + operand->type = UD_OP_REG; + operand->base = resolve_gpr32(u, type); + operand->size = u->opr_mode == 16 ? 16 : 32; + break; + case OP_ES: + case OP_CS: + case OP_DS: + case OP_SS: + case OP_FS: + case OP_GS: + /* in 64bits mode, only fs and gs are allowed */ + if (u->dis_mode == 64) { + if (type != OP_FS && type != OP_GS) { + u->error= 1; + } + } + operand->type = UD_OP_REG; + operand->base = (type - OP_ES) + UD_R_ES; + operand->size = 16; + break; + case OP_J : + decode_imm(u, size, operand); + operand->type = UD_OP_JIMM; + break ; + case OP_Q: + decode_modrm_rm(u, operand, T_MMX, size); + break; + case OP_R : + decode_modrm_rm(u, operand, T_GPR, size); + break; + case OP_C: + decode_modrm_reg(u, operand, T_CRG, size); + break; + case OP_D: + decode_modrm_reg(u, operand, T_DBG, size); + break; + case OP_I3 : + operand->type = UD_OP_CONST; + operand->lval.sbyte = 3; + break; + case OP_ST0: + case OP_ST1: + case OP_ST2: + case OP_ST3: + case OP_ST4: + case OP_ST5: + case OP_ST6: + case OP_ST7: + operand->type = UD_OP_REG; + operand->base = (type - OP_ST0) + UD_R_ST0; + operand->size = 0; + break; + case OP_AX: + operand->type = UD_OP_REG; + operand->base = UD_R_AX; + operand->size = 16; + break; + default : + operand->type = UD_NONE; + break; + } + return 0; +} + + +/* + * decode_operands + * + * Disassemble upto 3 operands of the current instruction being + * disassembled. By the end of the function, the operand fields + * of the ud structure will have been filled. + */ +static int +decode_operands(struct ud* u) +{ + decode_operand(u, &u->operand[0], + u->itab_entry->operand1.type, + u->itab_entry->operand1.size); + decode_operand(u, &u->operand[1], + u->itab_entry->operand2.type, + u->itab_entry->operand2.size); + decode_operand(u, &u->operand[2], + u->itab_entry->operand3.type, + u->itab_entry->operand3.size); + return 0; +} + +/* ----------------------------------------------------------------------------- + * clear_insn() - clear instruction structure + * ----------------------------------------------------------------------------- + */ +static void +clear_insn(register struct ud* u) +{ + u->error = 0; + u->pfx_seg = 0; + u->pfx_opr = 0; + u->pfx_adr = 0; + u->pfx_lock = 0; + u->pfx_repne = 0; + u->pfx_rep = 0; + u->pfx_repe = 0; + u->pfx_rex = 0; + u->pfx_insn = 0; + u->mnemonic = UD_Inone; + u->itab_entry = NULL; + u->have_modrm = 0; + + memset( &u->operand[ 0 ], 0, sizeof( struct ud_operand ) ); + memset( &u->operand[ 1 ], 0, sizeof( struct ud_operand ) ); + memset( &u->operand[ 2 ], 0, sizeof( struct ud_operand ) ); +} + +static int +resolve_mode( struct ud* u ) +{ + /* if in error state, bail out */ + if ( u->error ) return -1; + + /* propagate prefix effects */ + if ( u->dis_mode == 64 ) { /* set 64bit-mode flags */ + + /* Check validity of instruction m64 */ + if ( P_INV64( u->itab_entry->prefix ) ) { + u->error = 1; + return -1; + } + + /* effective rex prefix is the effective mask for the + * instruction hard-coded in the opcode map. + */ + u->pfx_rex = ( u->pfx_rex & 0x40 ) | + ( u->pfx_rex & REX_PFX_MASK( u->itab_entry->prefix ) ); + + /* whether this instruction has a default operand size of + * 64bit, also hardcoded into the opcode map. + */ + u->default64 = P_DEF64( u->itab_entry->prefix ); + /* calculate effective operand size */ + if ( REX_W( u->pfx_rex ) ) { + u->opr_mode = 64; + } else if ( u->pfx_opr ) { + u->opr_mode = 16; + } else { + /* unless the default opr size of instruction is 64, + * the effective operand size in the absence of rex.w + * prefix is 32. + */ + u->opr_mode = ( u->default64 ) ? 64 : 32; + } + + /* calculate effective address size */ + u->adr_mode = (u->pfx_adr) ? 32 : 64; + } else if ( u->dis_mode == 32 ) { /* set 32bit-mode flags */ + u->opr_mode = ( u->pfx_opr ) ? 16 : 32; + u->adr_mode = ( u->pfx_adr ) ? 16 : 32; + } else if ( u->dis_mode == 16 ) { /* set 16bit-mode flags */ + u->opr_mode = ( u->pfx_opr ) ? 32 : 16; + u->adr_mode = ( u->pfx_adr ) ? 32 : 16; + } + + /* These flags determine which operand to apply the operand size + * cast to. + */ + u->c1 = ( P_C1( u->itab_entry->prefix ) ) ? 1 : 0; + u->c2 = ( P_C2( u->itab_entry->prefix ) ) ? 1 : 0; + u->c3 = ( P_C3( u->itab_entry->prefix ) ) ? 1 : 0; + + /* set flags for implicit addressing */ + u->implicit_addr = P_IMPADDR( u->itab_entry->prefix ); + + return 0; +} + +static int gen_hex( struct ud *u ) +{ + unsigned int i; + unsigned char *src_ptr = ud_inp_sess( u ); + char* src_hex; + + /* bail out if in error stat. */ + if ( u->error ) return -1; + /* output buffer pointe */ + src_hex = ( char* ) u->insn_hexcode; + /* for each byte used to decode instruction */ + for ( i = 0; i < u->inp_ctr; ++i, ++src_ptr) { + sprintf( src_hex, "%02x", *src_ptr & 0xFF ); + src_hex += 2; + } + return 0; +} + + +static inline int +decode_insn(struct ud *u, uint16_t ptr) +{ + ASSERT((ptr & 0x8000) == 0); + u->itab_entry = &ud_itab[ ptr ]; + u->mnemonic = u->itab_entry->mnemonic; + return (resolve_mode(u) == 0 && + decode_operands(u) == 0 && + resolve_mnemonic(u) == 0) ? 0 : -1; +} + + +/* + * decode_3dnow() + * + * Decoding 3dnow is a little tricky because of its strange opcode + * structure. The final opcode disambiguation depends on the last + * byte that comes after the operands have been decoded. Fortunately, + * all 3dnow instructions have the same set of operand types. So we + * go ahead and decode the instruction by picking an arbitrarily chosen + * valid entry in the table, decode the operands, and read the final + * byte to resolve the menmonic. + */ +static inline int +decode_3dnow(struct ud* u) +{ + uint16_t ptr; + ASSERT(u->le->type == UD_TAB__OPC_3DNOW); + ASSERT(u->le->table[0xc] != 0); + decode_insn(u, u->le->table[0xc]); + ud_inp_next(u); + if (u->error) { + return -1; + } + ptr = u->le->table[ud_inp_curr(u)]; + ASSERT((ptr & 0x8000) == 0); + u->mnemonic = ud_itab[ptr].mnemonic; + return 0; +} + + +static int +decode_ssepfx(struct ud *u) +{ + uint8_t idx = ((u->pfx_insn & 0xf) + 1) / 2; + if (u->le->table[idx] == 0) { + idx = 0; + } + if (idx && u->le->table[idx] != 0) { + /* + * "Consume" the prefix as a part of the opcode, so it is no + * longer exported as an instruction prefix. + */ + switch (u->pfx_insn) { + case 0xf2: + u->pfx_repne = 0; + break; + case 0xf3: + u->pfx_rep = 0; + u->pfx_repe = 0; + break; + case 0x66: + u->pfx_opr = 0; + break; + } + } + return decode_ext(u, u->le->table[idx]); +} + + +/* + * decode_ext() + * + * Decode opcode extensions (if any) + */ +static int +decode_ext(struct ud *u, uint16_t ptr) +{ + uint8_t idx = 0; + if ((ptr & 0x8000) == 0) { + return decode_insn(u, ptr); + } + u->le = &ud_lookup_table_list[(~0x8000 & ptr)]; + if (u->le->type == UD_TAB__OPC_3DNOW) { + return decode_3dnow(u); + } + + switch (u->le->type) { + case UD_TAB__OPC_MOD: + /* !11 = 0, 11 = 1 */ + idx = (MODRM_MOD(modrm(u)) + 1) / 4; + break; + /* disassembly mode/operand size/address size based tables. + * 16 = 0,, 32 = 1, 64 = 2 + */ + case UD_TAB__OPC_MODE: + idx = u->dis_mode / 32; + break; + case UD_TAB__OPC_OSIZE: + idx = eff_opr_mode(u->dis_mode, REX_W(u->pfx_rex), u->pfx_opr) / 32; + break; + case UD_TAB__OPC_ASIZE: + idx = eff_adr_mode(u->dis_mode, u->pfx_adr) / 32; + break; + case UD_TAB__OPC_X87: + idx = modrm(u) - 0xC0; + break; + case UD_TAB__OPC_VENDOR: + if (u->vendor == UD_VENDOR_ANY) { + /* choose a valid entry */ + idx = (u->le->table[idx] != 0) ? 0 : 1; + } else if (u->vendor == UD_VENDOR_AMD) { + idx = 0; + } else { + idx = 1; + } + break; + case UD_TAB__OPC_RM: + idx = MODRM_RM(modrm(u)); + break; + case UD_TAB__OPC_REG: + idx = MODRM_REG(modrm(u)); + break; + case UD_TAB__OPC_SSE: + return decode_ssepfx(u); + default: + ASSERT(!"not reached"); + break; + } + + return decode_ext(u, u->le->table[idx]); +} + + +static inline int +decode_opcode(struct ud *u) +{ + uint16_t ptr; + ASSERT(u->le->type == UD_TAB__OPC_TABLE); + ud_inp_next(u); + if (u->error) { + return -1; + } + ptr = u->le->table[ud_inp_curr(u)]; + if (ptr & 0x8000) { + u->le = &ud_lookup_table_list[ptr & ~0x8000]; + if (u->le->type == UD_TAB__OPC_TABLE) { + return decode_opcode(u); + } + } + return decode_ext(u, ptr); +} + + +/* ============================================================================= + * ud_decode() - Instruction decoder. Returns the number of bytes decoded. + * ============================================================================= + */ +unsigned int +ud_decode(struct ud *u) +{ + ud_inp_start(u); + clear_insn(u); + u->le = &ud_lookup_table_list[0]; + u->error = decode_prefixes(u) == -1 || + decode_opcode(u) == -1 || + u->error; + /* Handle decode error. */ + if (u->error) { + /* clear out the decode data. */ + clear_insn(u); + /* mark the sequence of bytes as invalid. */ + u->itab_entry = & s_ie__invalid; + u->mnemonic = u->itab_entry->mnemonic; + } + + /* maybe this stray segment override byte + * should be spewed out? + */ + if ( !P_SEG( u->itab_entry->prefix ) && + u->operand[0].type != UD_OP_MEM && + u->operand[1].type != UD_OP_MEM ) + u->pfx_seg = 0; + + u->insn_offset = u->pc; /* set offset of instruction */ + u->insn_fill = 0; /* set translation buffer index to 0 */ + u->pc += u->inp_ctr; /* move program counter by bytes decoded */ + gen_hex( u ); /* generate hex code */ + + /* return number of bytes disassembled. */ + return u->inp_ctr; +} + +/* +vim: set ts=2 sw=2 expandtab +*/ + +#endif // USE(UDIS86) diff --git a/src/3rdparty/masm/disassembler/udis86/udis86_decode.h b/src/3rdparty/masm/disassembler/udis86/udis86_decode.h new file mode 100644 index 0000000000..940ed5ad6f --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/udis86_decode.h @@ -0,0 +1,258 @@ +/* udis86 - libudis86/decode.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_DECODE_H +#define UD_DECODE_H + +#include "udis86_types.h" +#include "udis86_itab.h" + +#define MAX_INSN_LENGTH 15 + +/* register classes */ +#define T_NONE 0 +#define T_GPR 1 +#define T_MMX 2 +#define T_CRG 3 +#define T_DBG 4 +#define T_SEG 5 +#define T_XMM 6 + +/* itab prefix bits */ +#define P_none ( 0 ) +#define P_cast ( 1 << 0 ) +#define P_CAST(n) ( ( n >> 0 ) & 1 ) +#define P_c1 ( 1 << 0 ) +#define P_C1(n) ( ( n >> 0 ) & 1 ) +#define P_rexb ( 1 << 1 ) +#define P_REXB(n) ( ( n >> 1 ) & 1 ) +#define P_depM ( 1 << 2 ) +#define P_DEPM(n) ( ( n >> 2 ) & 1 ) +#define P_c3 ( 1 << 3 ) +#define P_C3(n) ( ( n >> 3 ) & 1 ) +#define P_inv64 ( 1 << 4 ) +#define P_INV64(n) ( ( n >> 4 ) & 1 ) +#define P_rexw ( 1 << 5 ) +#define P_REXW(n) ( ( n >> 5 ) & 1 ) +#define P_c2 ( 1 << 6 ) +#define P_C2(n) ( ( n >> 6 ) & 1 ) +#define P_def64 ( 1 << 7 ) +#define P_DEF64(n) ( ( n >> 7 ) & 1 ) +#define P_rexr ( 1 << 8 ) +#define P_REXR(n) ( ( n >> 8 ) & 1 ) +#define P_oso ( 1 << 9 ) +#define P_OSO(n) ( ( n >> 9 ) & 1 ) +#define P_aso ( 1 << 10 ) +#define P_ASO(n) ( ( n >> 10 ) & 1 ) +#define P_rexx ( 1 << 11 ) +#define P_REXX(n) ( ( n >> 11 ) & 1 ) +#define P_ImpAddr ( 1 << 12 ) +#define P_IMPADDR(n) ( ( n >> 12 ) & 1 ) +#define P_seg ( 1 << 13 ) +#define P_SEG(n) ( ( n >> 13 ) & 1 ) +#define P_sext ( 1 << 14 ) +#define P_SEXT(n) ( ( n >> 14 ) & 1 ) + +/* rex prefix bits */ +#define REX_W(r) ( ( 0xF & ( r ) ) >> 3 ) +#define REX_R(r) ( ( 0x7 & ( r ) ) >> 2 ) +#define REX_X(r) ( ( 0x3 & ( r ) ) >> 1 ) +#define REX_B(r) ( ( 0x1 & ( r ) ) >> 0 ) +#define REX_PFX_MASK(n) ( ( P_REXW(n) << 3 ) | \ + ( P_REXR(n) << 2 ) | \ + ( P_REXX(n) << 1 ) | \ + ( P_REXB(n) << 0 ) ) + +/* scable-index-base bits */ +#define SIB_S(b) ( ( b ) >> 6 ) +#define SIB_I(b) ( ( ( b ) >> 3 ) & 7 ) +#define SIB_B(b) ( ( b ) & 7 ) + +/* modrm bits */ +#define MODRM_REG(b) ( ( ( b ) >> 3 ) & 7 ) +#define MODRM_NNN(b) ( ( ( b ) >> 3 ) & 7 ) +#define MODRM_MOD(b) ( ( ( b ) >> 6 ) & 3 ) +#define MODRM_RM(b) ( ( b ) & 7 ) + +/* operand type constants -- order is important! */ + +enum ud_operand_code { + OP_NONE, + + OP_A, OP_E, OP_M, OP_G, + OP_I, + + OP_AL, OP_CL, OP_DL, OP_BL, + OP_AH, OP_CH, OP_DH, OP_BH, + + OP_ALr8b, OP_CLr9b, OP_DLr10b, OP_BLr11b, + OP_AHr12b, OP_CHr13b, OP_DHr14b, OP_BHr15b, + + OP_AX, OP_CX, OP_DX, OP_BX, + OP_SI, OP_DI, OP_SP, OP_BP, + + OP_rAX, OP_rCX, OP_rDX, OP_rBX, + OP_rSP, OP_rBP, OP_rSI, OP_rDI, + + OP_rAXr8, OP_rCXr9, OP_rDXr10, OP_rBXr11, + OP_rSPr12, OP_rBPr13, OP_rSIr14, OP_rDIr15, + + OP_eAX, OP_eCX, OP_eDX, OP_eBX, + OP_eSP, OP_eBP, OP_eSI, OP_eDI, + + OP_ES, OP_CS, OP_SS, OP_DS, + OP_FS, OP_GS, + + OP_ST0, OP_ST1, OP_ST2, OP_ST3, + OP_ST4, OP_ST5, OP_ST6, OP_ST7, + + OP_J, OP_S, OP_O, + OP_I1, OP_I3, + + OP_V, OP_W, OP_Q, OP_P, + + OP_R, OP_C, OP_D, OP_VR, OP_PR, + + OP_MR +} UD_ATTR_PACKED; + + +/* operand size constants */ + +enum ud_operand_size { + SZ_NA = 0, + SZ_Z = 1, + SZ_V = 2, + SZ_P = 3, + SZ_WP = 4, + SZ_DP = 5, + SZ_MDQ = 6, + SZ_RDQ = 7, + + /* the following values are used as is, + * and thus hard-coded. changing them + * will break internals + */ + SZ_B = 8, + SZ_W = 16, + SZ_D = 32, + SZ_Q = 64, + SZ_T = 80, + SZ_O = 128, + + SZ_WV = 17, + SZ_BV = 18, + SZ_DY = 19 + +} UD_ATTR_PACKED; + + +/* A single operand of an entry in the instruction table. + * (internal use only) + */ +struct ud_itab_entry_operand +{ + enum ud_operand_code type; + enum ud_operand_size size; +}; + + +/* A single entry in an instruction table. + *(internal use only) + */ +struct ud_itab_entry +{ + enum ud_mnemonic_code mnemonic; + struct ud_itab_entry_operand operand1; + struct ud_itab_entry_operand operand2; + struct ud_itab_entry_operand operand3; + uint32_t prefix; +}; + +struct ud_lookup_table_list_entry { + const uint16_t *table; + enum ud_table_type type; + const char *meta; +}; + + +static inline unsigned int sse_pfx_idx( const unsigned int pfx ) +{ + /* 00 = 0 + * f2 = 1 + * f3 = 2 + * 66 = 3 + */ + return ( ( pfx & 0xf ) + 1 ) / 2; +} + +static inline unsigned int mode_idx( const unsigned int mode ) +{ + /* 16 = 0 + * 32 = 1 + * 64 = 2 + */ + return ( mode / 32 ); +} + +static inline unsigned int modrm_mod_idx( const unsigned int mod ) +{ + /* !11 = 0 + * 11 = 1 + */ + return ( mod + 1 ) / 4; +} + +static inline unsigned int vendor_idx( const unsigned int vendor ) +{ + switch ( vendor ) { + case UD_VENDOR_AMD: return 0; + case UD_VENDOR_INTEL: return 1; + case UD_VENDOR_ANY: return 2; + default: return 2; + } +} + +static inline unsigned int is_group_ptr( uint16_t ptr ) +{ + return ( 0x8000 & ptr ); +} + +static inline unsigned int group_idx( uint16_t ptr ) +{ + return ( ~0x8000 & ptr ); +} + + +extern struct ud_itab_entry ud_itab[]; +extern struct ud_lookup_table_list_entry ud_lookup_table_list[]; + +#endif /* UD_DECODE_H */ + +/* vim:cindent + * vim:expandtab + * vim:ts=4 + * vim:sw=4 + */ diff --git a/src/3rdparty/masm/disassembler/udis86/udis86_extern.h b/src/3rdparty/masm/disassembler/udis86/udis86_extern.h new file mode 100644 index 0000000000..8e87721e8c --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/udis86_extern.h @@ -0,0 +1,88 @@ +/* udis86 - libudis86/extern.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_EXTERN_H +#define UD_EXTERN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "udis86_types.h" + +/* ============================= PUBLIC API ================================= */ + +extern void ud_init(struct ud*); + +extern void ud_set_mode(struct ud*, uint8_t); + +extern void ud_set_pc(struct ud*, uint64_t); + +extern void ud_set_input_hook(struct ud*, int (*)(struct ud*)); + +extern void ud_set_input_buffer(struct ud*, uint8_t*, size_t); + +#ifndef __UD_STANDALONE__ +extern void ud_set_input_file(struct ud*, FILE*); +#endif /* __UD_STANDALONE__ */ + +extern void ud_set_vendor(struct ud*, unsigned); + +extern void ud_set_syntax(struct ud*, void (*)(struct ud*)); + +extern void ud_input_skip(struct ud*, size_t); + +extern int ud_input_end(struct ud*); + +extern unsigned int ud_decode(struct ud*); + +extern unsigned int ud_disassemble(struct ud*); + +extern void ud_translate_intel(struct ud*); + +extern void ud_translate_att(struct ud*); + +extern char* ud_insn_asm(struct ud* u); + +extern uint8_t* ud_insn_ptr(struct ud* u); + +extern uint64_t ud_insn_off(struct ud*); + +extern char* ud_insn_hex(struct ud*); + +extern unsigned int ud_insn_len(struct ud* u); + +extern const char* ud_lookup_mnemonic(enum ud_mnemonic_code c); + +extern void ud_set_user_opaque_data(struct ud*, void*); + +extern void *ud_get_user_opaque_data(struct ud*); + +/* ========================================================================== */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/3rdparty/masm/disassembler/udis86/udis86_input.c b/src/3rdparty/masm/disassembler/udis86/udis86_input.c new file mode 100644 index 0000000000..76c6cccf36 --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/udis86_input.c @@ -0,0 +1,263 @@ +/* udis86 - libudis86/input.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_extern.h" +#include "udis86_types.h" +#include "udis86_input.h" + +/* ----------------------------------------------------------------------------- + * inp_buff_hook() - Hook for buffered inputs. + * ----------------------------------------------------------------------------- + */ +static int +inp_buff_hook(struct ud* u) +{ + if (u->inp_buff < u->inp_buff_end) + return *u->inp_buff++; + else return -1; +} + +#ifndef __UD_STANDALONE__ +/* ----------------------------------------------------------------------------- + * inp_file_hook() - Hook for FILE inputs. + * ----------------------------------------------------------------------------- + */ +static int +inp_file_hook(struct ud* u) +{ + return fgetc(u->inp_file); +} +#endif /* __UD_STANDALONE__*/ + +/* ============================================================================= + * ud_inp_set_hook() - Sets input hook. + * ============================================================================= + */ +extern void +ud_set_input_hook(register struct ud* u, int (*hook)(struct ud*)) +{ + u->inp_hook = hook; + ud_inp_init(u); +} + +extern void +ud_set_user_opaque_data( struct ud * u, void * opaque ) +{ + u->user_opaque_data = opaque; +} + +extern void * +ud_get_user_opaque_data( struct ud * u ) +{ + return u->user_opaque_data; +} + +/* ============================================================================= + * ud_inp_set_buffer() - Set buffer as input. + * ============================================================================= + */ +extern void +ud_set_input_buffer(register struct ud* u, uint8_t* buf, size_t len) +{ + u->inp_hook = inp_buff_hook; + u->inp_buff = buf; + u->inp_buff_end = buf + len; + ud_inp_init(u); +} + +#ifndef __UD_STANDALONE__ +/* ============================================================================= + * ud_input_set_file() - Set buffer as input. + * ============================================================================= + */ +extern void +ud_set_input_file(register struct ud* u, FILE* f) +{ + u->inp_hook = inp_file_hook; + u->inp_file = f; + ud_inp_init(u); +} +#endif /* __UD_STANDALONE__ */ + +/* ============================================================================= + * ud_input_skip() - Skip n input bytes. + * ============================================================================= + */ +extern void +ud_input_skip(struct ud* u, size_t n) +{ + while (n--) { + u->inp_hook(u); + } +} + +/* ============================================================================= + * ud_input_end() - Test for end of input. + * ============================================================================= + */ +extern int +ud_input_end(struct ud* u) +{ + return (u->inp_curr == u->inp_fill) && u->inp_end; +} + +/* ----------------------------------------------------------------------------- + * ud_inp_next() - Loads and returns the next byte from input. + * + * inp_curr and inp_fill are pointers to the cache. The program is written based + * on the property that they are 8-bits in size, and will eventually wrap around + * forming a circular buffer. So, the size of the cache is 256 in size, kind of + * unnecessary yet optimized. + * + * A buffer inp_sess stores the bytes disassembled for a single session. + * ----------------------------------------------------------------------------- + */ +extern uint8_t ud_inp_next(struct ud* u) +{ + int c = -1; + /* if current pointer is not upto the fill point in the + * input cache. + */ + if ( u->inp_curr != u->inp_fill ) { + c = u->inp_cache[ ++u->inp_curr ]; + /* if !end-of-input, call the input hook and get a byte */ + } else if ( u->inp_end || ( c = u->inp_hook( u ) ) == -1 ) { + /* end-of-input, mark it as an error, since the decoder, + * expected a byte more. + */ + u->error = 1; + /* flag end of input */ + u->inp_end = 1; + return 0; + } else { + /* increment pointers, we have a new byte. */ + u->inp_curr = ++u->inp_fill; + /* add the byte to the cache */ + u->inp_cache[ u->inp_fill ] = c; + } + /* record bytes input per decode-session. */ + u->inp_sess[ u->inp_ctr++ ] = c; + /* return byte */ + return ( uint8_t ) c; +} + +/* ----------------------------------------------------------------------------- + * ud_inp_back() - Move back a single byte in the stream. + * ----------------------------------------------------------------------------- + */ +extern void +ud_inp_back(struct ud* u) +{ + if ( u->inp_ctr > 0 ) { + --u->inp_curr; + --u->inp_ctr; + } +} + +/* ----------------------------------------------------------------------------- + * ud_inp_peek() - Peek into the next byte in source. + * ----------------------------------------------------------------------------- + */ +extern uint8_t +ud_inp_peek(struct ud* u) +{ + uint8_t r = ud_inp_next(u); + if ( !u->error ) ud_inp_back(u); /* Don't backup if there was an error */ + return r; +} + +/* ----------------------------------------------------------------------------- + * ud_inp_move() - Move ahead n input bytes. + * ----------------------------------------------------------------------------- + */ +extern void +ud_inp_move(struct ud* u, size_t n) +{ + while (n--) + ud_inp_next(u); +} + +/*------------------------------------------------------------------------------ + * ud_inp_uintN() - return uintN from source. + *------------------------------------------------------------------------------ + */ +extern uint8_t +ud_inp_uint8(struct ud* u) +{ + return ud_inp_next(u); +} + +extern uint16_t +ud_inp_uint16(struct ud* u) +{ + uint16_t r, ret; + + ret = ud_inp_next(u); + r = ud_inp_next(u); + return ret | (r << 8); +} + +extern uint32_t +ud_inp_uint32(struct ud* u) +{ + uint32_t r, ret; + + ret = ud_inp_next(u); + r = ud_inp_next(u); + ret = ret | (r << 8); + r = ud_inp_next(u); + ret = ret | (r << 16); + r = ud_inp_next(u); + return ret | (r << 24); +} + +extern uint64_t +ud_inp_uint64(struct ud* u) +{ + uint64_t r, ret; + + ret = ud_inp_next(u); + r = ud_inp_next(u); + ret = ret | (r << 8); + r = ud_inp_next(u); + ret = ret | (r << 16); + r = ud_inp_next(u); + ret = ret | (r << 24); + r = ud_inp_next(u); + ret = ret | (r << 32); + r = ud_inp_next(u); + ret = ret | (r << 40); + r = ud_inp_next(u); + ret = ret | (r << 48); + r = ud_inp_next(u); + return ret | (r << 56); +} + +#endif // USE(UDIS86) diff --git a/src/3rdparty/masm/disassembler/udis86/udis86_input.h b/src/3rdparty/masm/disassembler/udis86/udis86_input.h new file mode 100644 index 0000000000..96865a88b5 --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/udis86_input.h @@ -0,0 +1,67 @@ +/* udis86 - libudis86/input.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_INPUT_H +#define UD_INPUT_H + +#include "udis86_types.h" + +uint8_t ud_inp_next(struct ud*); +uint8_t ud_inp_peek(struct ud*); +uint8_t ud_inp_uint8(struct ud*); +uint16_t ud_inp_uint16(struct ud*); +uint32_t ud_inp_uint32(struct ud*); +uint64_t ud_inp_uint64(struct ud*); +void ud_inp_move(struct ud*, size_t); +void ud_inp_back(struct ud*); + +/* ud_inp_init() - Initializes the input system. */ +#define ud_inp_init(u) \ +do { \ + u->inp_curr = 0; \ + u->inp_fill = 0; \ + u->inp_ctr = 0; \ + u->inp_end = 0; \ +} while (0) + +/* ud_inp_start() - Should be called before each de-code operation. */ +#define ud_inp_start(u) u->inp_ctr = 0 + +/* ud_inp_back() - Resets the current pointer to its position before the current + * instruction disassembly was started. + */ +#define ud_inp_reset(u) \ +do { \ + u->inp_curr -= u->inp_ctr; \ + u->inp_ctr = 0; \ +} while (0) + +/* ud_inp_sess() - Returns the pointer to current session. */ +#define ud_inp_sess(u) (u->inp_sess) + +/* inp_cur() - Returns the current input byte. */ +#define ud_inp_curr(u) ((u)->inp_cache[(u)->inp_curr]) + +#endif diff --git a/src/3rdparty/masm/disassembler/udis86/udis86_itab_holder.c b/src/3rdparty/masm/disassembler/udis86/udis86_itab_holder.c new file mode 100644 index 0000000000..d5d8726d6a --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/udis86_itab_holder.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_itab.c" + +#endif + diff --git a/src/3rdparty/masm/disassembler/udis86/udis86_syn-att.c b/src/3rdparty/masm/disassembler/udis86/udis86_syn-att.c new file mode 100644 index 0000000000..155a34ca29 --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/udis86_syn-att.c @@ -0,0 +1,253 @@ +/* udis86 - libudis86/syn-att.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_types.h" +#include "udis86_extern.h" +#include "udis86_decode.h" +#include "udis86_itab.h" +#include "udis86_syn.h" + +/* ----------------------------------------------------------------------------- + * opr_cast() - Prints an operand cast. + * ----------------------------------------------------------------------------- + */ +static void +opr_cast(struct ud* u, struct ud_operand* op) +{ + switch(op->size) { + case 16 : case 32 : + mkasm(u, "*"); break; + default: break; + } +} + +/* ----------------------------------------------------------------------------- + * gen_operand() - Generates assembly output for each operand. + * ----------------------------------------------------------------------------- + */ +static void +gen_operand(struct ud* u, struct ud_operand* op) +{ + switch(op->type) { + case UD_OP_REG: + mkasm(u, "%%%s", ud_reg_tab[op->base - UD_R_AL]); + break; + + case UD_OP_MEM: + if (u->br_far) opr_cast(u, op); + if (u->pfx_seg) + mkasm(u, "%%%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); + if (op->offset == 8) { + if (op->lval.sbyte < 0) + mkasm(u, "-0x%x", (-op->lval.sbyte) & 0xff); + else mkasm(u, "0x%x", op->lval.sbyte); + } + else if (op->offset == 16) + mkasm(u, "0x%x", op->lval.uword); + else if (op->offset == 32) + mkasm(u, "0x%lx", (unsigned long)op->lval.udword); + else if (op->offset == 64) + mkasm(u, "0x" FMT64 "x", op->lval.uqword); + + if (op->base) + mkasm(u, "(%%%s", ud_reg_tab[op->base - UD_R_AL]); + if (op->index) { + if (op->base) + mkasm(u, ","); + else mkasm(u, "("); + mkasm(u, "%%%s", ud_reg_tab[op->index - UD_R_AL]); + } + if (op->scale) + mkasm(u, ",%d", op->scale); + if (op->base || op->index) + mkasm(u, ")"); + break; + + case UD_OP_IMM: { + int64_t imm = 0; + uint64_t sext_mask = 0xffffffffffffffffull; + unsigned sext_size = op->size; + + switch (op->size) { + case 8: imm = op->lval.sbyte; break; + case 16: imm = op->lval.sword; break; + case 32: imm = op->lval.sdword; break; + case 64: imm = op->lval.sqword; break; + } + if ( P_SEXT( u->itab_entry->prefix ) ) { + sext_size = u->operand[ 0 ].size; + if ( u->mnemonic == UD_Ipush ) + /* push sign-extends to operand size */ + sext_size = u->opr_mode; + } + if ( sext_size < 64 ) + sext_mask = ( 1ull << sext_size ) - 1; + mkasm( u, "$0x" FMT64 "x", imm & sext_mask ); + + break; + } + + case UD_OP_JIMM: + switch (op->size) { + case 8: + mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sbyte); + break; + case 16: + mkasm(u, "0x" FMT64 "x", (u->pc + op->lval.sword) & 0xffff ); + break; + case 32: + if (u->dis_mode == 32) + mkasm(u, "0x" FMT64 "x", (u->pc + op->lval.sdword) & 0xffffffff); + else + mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sdword); + break; + default:break; + } + break; + + case UD_OP_PTR: + switch (op->size) { + case 32: + mkasm(u, "$0x%x, $0x%x", op->lval.ptr.seg, + op->lval.ptr.off & 0xFFFF); + break; + case 48: + mkasm(u, "$0x%x, $0x%lx", op->lval.ptr.seg, + (unsigned long)op->lval.ptr.off); + break; + } + break; + + default: return; + } +} + +/* ============================================================================= + * translates to AT&T syntax + * ============================================================================= + */ +extern void +ud_translate_att(struct ud *u) +{ + int size = 0; + + /* check if P_OSO prefix is used */ + if (! P_OSO(u->itab_entry->prefix) && u->pfx_opr) { + switch (u->dis_mode) { + case 16: + mkasm(u, "o32 "); + break; + case 32: + case 64: + mkasm(u, "o16 "); + break; + } + } + + /* check if P_ASO prefix was used */ + if (! P_ASO(u->itab_entry->prefix) && u->pfx_adr) { + switch (u->dis_mode) { + case 16: + mkasm(u, "a32 "); + break; + case 32: + mkasm(u, "a16 "); + break; + case 64: + mkasm(u, "a32 "); + break; + } + } + + if (u->pfx_lock) + mkasm(u, "lock "); + if (u->pfx_rep) + mkasm(u, "rep "); + if (u->pfx_repne) + mkasm(u, "repne "); + + /* special instructions */ + switch (u->mnemonic) { + case UD_Iretf: + mkasm(u, "lret "); + break; + case UD_Idb: + mkasm(u, ".byte 0x%x", u->operand[0].lval.ubyte); + return; + case UD_Ijmp: + case UD_Icall: + if (u->br_far) mkasm(u, "l"); + mkasm(u, "%s", ud_lookup_mnemonic(u->mnemonic)); + break; + case UD_Ibound: + case UD_Ienter: + if (u->operand[0].type != UD_NONE) + gen_operand(u, &u->operand[0]); + if (u->operand[1].type != UD_NONE) { + mkasm(u, ","); + gen_operand(u, &u->operand[1]); + } + return; + default: + mkasm(u, "%s", ud_lookup_mnemonic(u->mnemonic)); + } + + if (u->c1) + size = u->operand[0].size; + else if (u->c2) + size = u->operand[1].size; + else if (u->c3) + size = u->operand[2].size; + + if (size == 8) + mkasm(u, "b"); + else if (size == 16) + mkasm(u, "w"); + else if (size == 64) + mkasm(u, "q"); + + mkasm(u, " "); + + if (u->operand[2].type != UD_NONE) { + gen_operand(u, &u->operand[2]); + mkasm(u, ", "); + } + + if (u->operand[1].type != UD_NONE) { + gen_operand(u, &u->operand[1]); + mkasm(u, ", "); + } + + if (u->operand[0].type != UD_NONE) + gen_operand(u, &u->operand[0]); +} + +#endif // USE(UDIS86) + diff --git a/src/3rdparty/masm/disassembler/udis86/udis86_syn-intel.c b/src/3rdparty/masm/disassembler/udis86/udis86_syn-intel.c new file mode 100644 index 0000000000..d250bd449c --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/udis86_syn-intel.c @@ -0,0 +1,279 @@ +/* udis86 - libudis86/syn-intel.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include + +#if USE(UDIS86) + +#include "udis86_types.h" +#include "udis86_extern.h" +#include "udis86_decode.h" +#include "udis86_itab.h" +#include "udis86_syn.h" + +/* ----------------------------------------------------------------------------- + * opr_cast() - Prints an operand cast. + * ----------------------------------------------------------------------------- + */ +static void +opr_cast(struct ud* u, struct ud_operand* op) +{ + switch(op->size) { + case 8: mkasm(u, "byte " ); break; + case 16: mkasm(u, "word " ); break; + case 32: mkasm(u, "dword "); break; + case 64: mkasm(u, "qword "); break; + case 80: mkasm(u, "tword "); break; + default: break; + } + if (u->br_far) + mkasm(u, "far "); +} + +/* ----------------------------------------------------------------------------- + * gen_operand() - Generates assembly output for each operand. + * ----------------------------------------------------------------------------- + */ +static void gen_operand(struct ud* u, struct ud_operand* op, int syn_cast) +{ + switch(op->type) { + case UD_OP_REG: + mkasm(u, "%s", ud_reg_tab[op->base - UD_R_AL]); + break; + + case UD_OP_MEM: { + + int op_f = 0; + + if (syn_cast) + opr_cast(u, op); + + mkasm(u, "["); + + if (u->pfx_seg) + mkasm(u, "%s:", ud_reg_tab[u->pfx_seg - UD_R_AL]); + + if (op->base) { + mkasm(u, "%s", ud_reg_tab[op->base - UD_R_AL]); + op_f = 1; + } + + if (op->index) { + if (op_f) + mkasm(u, "+"); + mkasm(u, "%s", ud_reg_tab[op->index - UD_R_AL]); + op_f = 1; + } + + if (op->scale) + mkasm(u, "*%d", op->scale); + + if (op->offset == 8) { + if (op->lval.sbyte < 0) + mkasm(u, "-0x%x", -op->lval.sbyte); + else mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.sbyte); + } + else if (op->offset == 16) + mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.uword); + else if (op->offset == 32) { + if (u->adr_mode == 64) { + if (op->lval.sdword < 0) + mkasm(u, "-0x%x", -op->lval.sdword); + else mkasm(u, "%s0x%x", (op_f) ? "+" : "", op->lval.sdword); + } + else mkasm(u, "%s0x%lx", (op_f) ? "+" : "", (unsigned long)op->lval.udword); + } + else if (op->offset == 64) + mkasm(u, "%s0x" FMT64 "x", (op_f) ? "+" : "", op->lval.uqword); + + mkasm(u, "]"); + break; + } + + case UD_OP_IMM: { + int64_t imm = 0; + uint64_t sext_mask = 0xffffffffffffffffull; + unsigned sext_size = op->size; + + if (syn_cast) + opr_cast(u, op); + switch (op->size) { + case 8: imm = op->lval.sbyte; break; + case 16: imm = op->lval.sword; break; + case 32: imm = op->lval.sdword; break; + case 64: imm = op->lval.sqword; break; + } + if ( P_SEXT( u->itab_entry->prefix ) ) { + sext_size = u->operand[ 0 ].size; + if ( u->mnemonic == UD_Ipush ) + /* push sign-extends to operand size */ + sext_size = u->opr_mode; + } + if ( sext_size < 64 ) + sext_mask = ( 1ull << sext_size ) - 1; + mkasm( u, "0x" FMT64 "x", imm & sext_mask ); + + break; + } + + + case UD_OP_JIMM: + if (syn_cast) opr_cast(u, op); + switch (op->size) { + case 8: + mkasm(u, "0x" FMT64 "x", u->pc + op->lval.sbyte); + break; + case 16: + mkasm(u, "0x" FMT64 "x", ( u->pc + op->lval.sword ) & 0xffff ); + break; + case 32: + mkasm(u, "0x" FMT64 "x", ( u->pc + op->lval.sdword ) & 0xfffffffful ); + break; + default:break; + } + break; + + case UD_OP_PTR: + switch (op->size) { + case 32: + mkasm(u, "word 0x%x:0x%x", op->lval.ptr.seg, + op->lval.ptr.off & 0xFFFF); + break; + case 48: + mkasm(u, "dword 0x%x:0x%lx", op->lval.ptr.seg, + (unsigned long)op->lval.ptr.off); + break; + } + break; + + case UD_OP_CONST: + if (syn_cast) opr_cast(u, op); + mkasm(u, "%d", op->lval.udword); + break; + + default: return; + } +} + +/* ============================================================================= + * translates to intel syntax + * ============================================================================= + */ +extern void ud_translate_intel(struct ud* u) +{ + /* -- prefixes -- */ + + /* check if P_OSO prefix is used */ + if (! P_OSO(u->itab_entry->prefix) && u->pfx_opr) { + switch (u->dis_mode) { + case 16: + mkasm(u, "o32 "); + break; + case 32: + case 64: + mkasm(u, "o16 "); + break; + } + } + + /* check if P_ASO prefix was used */ + if (! P_ASO(u->itab_entry->prefix) && u->pfx_adr) { + switch (u->dis_mode) { + case 16: + mkasm(u, "a32 "); + break; + case 32: + mkasm(u, "a16 "); + break; + case 64: + mkasm(u, "a32 "); + break; + } + } + + if ( u->pfx_seg && + u->operand[0].type != UD_OP_MEM && + u->operand[1].type != UD_OP_MEM ) { + mkasm(u, "%s ", ud_reg_tab[u->pfx_seg - UD_R_AL]); + } + if (u->pfx_lock) + mkasm(u, "lock "); + if (u->pfx_rep) + mkasm(u, "rep "); + if (u->pfx_repne) + mkasm(u, "repne "); + + /* print the instruction mnemonic */ + mkasm(u, "%s ", ud_lookup_mnemonic(u->mnemonic)); + + /* operand 1 */ + if (u->operand[0].type != UD_NONE) { + int cast = 0; + if ( u->operand[0].type == UD_OP_IMM && + u->operand[1].type == UD_NONE ) + cast = u->c1; + if ( u->operand[0].type == UD_OP_MEM ) { + cast = u->c1; + if ( u->operand[1].type == UD_OP_IMM || + u->operand[1].type == UD_OP_CONST ) + cast = 1; + if ( u->operand[1].type == UD_NONE ) + cast = 1; + if ( ( u->operand[0].size != u->operand[1].size ) && u->operand[1].size ) + cast = 1; + } else if ( u->operand[ 0 ].type == UD_OP_JIMM ) { + if ( u->operand[ 0 ].size > 8 ) cast = 1; + } + gen_operand(u, &u->operand[0], cast); + } + /* operand 2 */ + if (u->operand[1].type != UD_NONE) { + int cast = 0; + mkasm(u, ", "); + if ( u->operand[1].type == UD_OP_MEM ) { + cast = u->c1; + + if ( u->operand[0].type != UD_OP_REG ) + cast = 1; + if ( u->operand[0].size != u->operand[1].size && u->operand[1].size ) + cast = 1; + if ( u->operand[0].type == UD_OP_REG && + u->operand[0].base >= UD_R_ES && + u->operand[0].base <= UD_R_GS ) + cast = 0; + } + gen_operand(u, &u->operand[1], cast ); + } + + /* operand 3 */ + if (u->operand[2].type != UD_NONE) { + mkasm(u, ", "); + gen_operand(u, &u->operand[2], u->c3); + } +} + +#endif // USE(UDIS86) + diff --git a/src/3rdparty/masm/disassembler/udis86/udis86_syn.c b/src/3rdparty/masm/disassembler/udis86/udis86_syn.c new file mode 100644 index 0000000000..80391b4a08 --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/udis86_syn.c @@ -0,0 +1,87 @@ +/* udis86 - libudis86/syn.c + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include + +#if USE(UDIS86) + +/* ----------------------------------------------------------------------------- + * Intel Register Table - Order Matters (types.h)! + * ----------------------------------------------------------------------------- + */ +const char* ud_reg_tab[] = +{ + "al", "cl", "dl", "bl", + "ah", "ch", "dh", "bh", + "spl", "bpl", "sil", "dil", + "r8b", "r9b", "r10b", "r11b", + "r12b", "r13b", "r14b", "r15b", + + "ax", "cx", "dx", "bx", + "sp", "bp", "si", "di", + "r8w", "r9w", "r10w", "r11w", + "r12w", "r13W" , "r14w", "r15w", + + "eax", "ecx", "edx", "ebx", + "esp", "ebp", "esi", "edi", + "r8d", "r9d", "r10d", "r11d", + "r12d", "r13d", "r14d", "r15d", + + "rax", "rcx", "rdx", "rbx", + "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", + "r12", "r13", "r14", "r15", + + "es", "cs", "ss", "ds", + "fs", "gs", + + "cr0", "cr1", "cr2", "cr3", + "cr4", "cr5", "cr6", "cr7", + "cr8", "cr9", "cr10", "cr11", + "cr12", "cr13", "cr14", "cr15", + + "dr0", "dr1", "dr2", "dr3", + "dr4", "dr5", "dr6", "dr7", + "dr8", "dr9", "dr10", "dr11", + "dr12", "dr13", "dr14", "dr15", + + "mm0", "mm1", "mm2", "mm3", + "mm4", "mm5", "mm6", "mm7", + + "st0", "st1", "st2", "st3", + "st4", "st5", "st6", "st7", + + "xmm0", "xmm1", "xmm2", "xmm3", + "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10", "xmm11", + "xmm12", "xmm13", "xmm14", "xmm15", + + "rip" +}; + +#endif // USE(UDIS86) + diff --git a/src/3rdparty/masm/disassembler/udis86/udis86_syn.h b/src/3rdparty/masm/disassembler/udis86/udis86_syn.h new file mode 100644 index 0000000000..e8636163ef --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/udis86_syn.h @@ -0,0 +1,47 @@ +/* udis86 - libudis86/syn.h + * + * Copyright (c) 2002-2009 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_SYN_H +#define UD_SYN_H + +#include "udis86_types.h" +#include + +#ifndef __UD_STANDALONE__ +# include +#endif /* __UD_STANDALONE__ */ + +extern const char* ud_reg_tab[]; + +static void mkasm(struct ud* u, const char* fmt, ...) WTF_ATTRIBUTE_PRINTF(2, 3); +static void mkasm(struct ud* u, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + u->insn_fill += vsnprintf((char*) u->insn_buffer + u->insn_fill, UD_STRING_BUFFER_SIZE - u->insn_fill, fmt, ap); + va_end(ap); +} + +#endif diff --git a/src/3rdparty/masm/disassembler/udis86/udis86_types.h b/src/3rdparty/masm/disassembler/udis86/udis86_types.h new file mode 100644 index 0000000000..320d1ca491 --- /dev/null +++ b/src/3rdparty/masm/disassembler/udis86/udis86_types.h @@ -0,0 +1,238 @@ +/* udis86 - libudis86/types.h + * + * Copyright (c) 2002-2009 Vivek Thampi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef UD_TYPES_H +#define UD_TYPES_H + +#ifndef __UD_STANDALONE__ +# include +#endif /* __UD_STANDALONE__ */ + +/* gcc specific extensions */ +#ifdef __GNUC__ +# define UD_ATTR_PACKED __attribute__((packed)) +#else +# define UD_ATTR_PACKED +#endif /* UD_ATTR_PACKED */ + +#ifdef _MSC_VER +# define FMT64 "%I64" + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; +#else +# define FMT64 "%ll" +# ifndef __UD_STANDALONE__ +# include +# endif /* __UD_STANDALONE__ */ +#endif + +/* ----------------------------------------------------------------------------- + * All possible "types" of objects in udis86. Order is Important! + * ----------------------------------------------------------------------------- + */ +enum ud_type +{ + UD_NONE, + + /* 8 bit GPRs */ + UD_R_AL, UD_R_CL, UD_R_DL, UD_R_BL, + UD_R_AH, UD_R_CH, UD_R_DH, UD_R_BH, + UD_R_SPL, UD_R_BPL, UD_R_SIL, UD_R_DIL, + UD_R_R8B, UD_R_R9B, UD_R_R10B, UD_R_R11B, + UD_R_R12B, UD_R_R13B, UD_R_R14B, UD_R_R15B, + + /* 16 bit GPRs */ + UD_R_AX, UD_R_CX, UD_R_DX, UD_R_BX, + UD_R_SP, UD_R_BP, UD_R_SI, UD_R_DI, + UD_R_R8W, UD_R_R9W, UD_R_R10W, UD_R_R11W, + UD_R_R12W, UD_R_R13W, UD_R_R14W, UD_R_R15W, + + /* 32 bit GPRs */ + UD_R_EAX, UD_R_ECX, UD_R_EDX, UD_R_EBX, + UD_R_ESP, UD_R_EBP, UD_R_ESI, UD_R_EDI, + UD_R_R8D, UD_R_R9D, UD_R_R10D, UD_R_R11D, + UD_R_R12D, UD_R_R13D, UD_R_R14D, UD_R_R15D, + + /* 64 bit GPRs */ + UD_R_RAX, UD_R_RCX, UD_R_RDX, UD_R_RBX, + UD_R_RSP, UD_R_RBP, UD_R_RSI, UD_R_RDI, + UD_R_R8, UD_R_R9, UD_R_R10, UD_R_R11, + UD_R_R12, UD_R_R13, UD_R_R14, UD_R_R15, + + /* segment registers */ + UD_R_ES, UD_R_CS, UD_R_SS, UD_R_DS, + UD_R_FS, UD_R_GS, + + /* control registers*/ + UD_R_CR0, UD_R_CR1, UD_R_CR2, UD_R_CR3, + UD_R_CR4, UD_R_CR5, UD_R_CR6, UD_R_CR7, + UD_R_CR8, UD_R_CR9, UD_R_CR10, UD_R_CR11, + UD_R_CR12, UD_R_CR13, UD_R_CR14, UD_R_CR15, + + /* debug registers */ + UD_R_DR0, UD_R_DR1, UD_R_DR2, UD_R_DR3, + UD_R_DR4, UD_R_DR5, UD_R_DR6, UD_R_DR7, + UD_R_DR8, UD_R_DR9, UD_R_DR10, UD_R_DR11, + UD_R_DR12, UD_R_DR13, UD_R_DR14, UD_R_DR15, + + /* mmx registers */ + UD_R_MM0, UD_R_MM1, UD_R_MM2, UD_R_MM3, + UD_R_MM4, UD_R_MM5, UD_R_MM6, UD_R_MM7, + + /* x87 registers */ + UD_R_ST0, UD_R_ST1, UD_R_ST2, UD_R_ST3, + UD_R_ST4, UD_R_ST5, UD_R_ST6, UD_R_ST7, + + /* extended multimedia registers */ + UD_R_XMM0, UD_R_XMM1, UD_R_XMM2, UD_R_XMM3, + UD_R_XMM4, UD_R_XMM5, UD_R_XMM6, UD_R_XMM7, + UD_R_XMM8, UD_R_XMM9, UD_R_XMM10, UD_R_XMM11, + UD_R_XMM12, UD_R_XMM13, UD_R_XMM14, UD_R_XMM15, + + UD_R_RIP, + + /* Operand Types */ + UD_OP_REG, UD_OP_MEM, UD_OP_PTR, UD_OP_IMM, + UD_OP_JIMM, UD_OP_CONST +}; + +#include "udis86_itab.h" + +/* ----------------------------------------------------------------------------- + * struct ud_operand - Disassembled instruction Operand. + * ----------------------------------------------------------------------------- + */ +struct ud_operand +{ + enum ud_type type; + uint8_t size; + union { + int8_t sbyte; + uint8_t ubyte; + int16_t sword; + uint16_t uword; + int32_t sdword; + uint32_t udword; + int64_t sqword; + uint64_t uqword; + + struct { + uint16_t seg; + uint32_t off; + } ptr; + } lval; + + enum ud_type base; + enum ud_type index; + uint8_t offset; + uint8_t scale; +}; + +#define UD_STRING_BUFFER_SIZE 64 + +/* ----------------------------------------------------------------------------- + * struct ud - The udis86 object. + * ----------------------------------------------------------------------------- + */ +struct ud +{ + int (*inp_hook) (struct ud*); + uint8_t inp_curr; + uint8_t inp_fill; +#ifndef __UD_STANDALONE__ + FILE* inp_file; +#endif + uint8_t inp_ctr; + uint8_t* inp_buff; + uint8_t* inp_buff_end; + uint8_t inp_end; + void (*translator)(struct ud*); + uint64_t insn_offset; + char insn_hexcode[32]; + char insn_buffer[UD_STRING_BUFFER_SIZE]; + unsigned int insn_fill; + uint8_t dis_mode; + uint64_t pc; + uint8_t vendor; + struct map_entry* mapen; + enum ud_mnemonic_code mnemonic; + struct ud_operand operand[3]; + uint8_t error; + uint8_t pfx_rex; + uint8_t pfx_seg; + uint8_t pfx_opr; + uint8_t pfx_adr; + uint8_t pfx_lock; + uint8_t pfx_rep; + uint8_t pfx_repe; + uint8_t pfx_repne; + uint8_t pfx_insn; + uint8_t default64; + uint8_t opr_mode; + uint8_t adr_mode; + uint8_t br_far; + uint8_t br_near; + uint8_t implicit_addr; + uint8_t c1; + uint8_t c2; + uint8_t c3; + uint8_t inp_cache[256]; + uint8_t inp_sess[64]; + uint8_t have_modrm; + uint8_t modrm; + void * user_opaque_data; + struct ud_itab_entry * itab_entry; + struct ud_lookup_table_list_entry *le; +}; + +/* ----------------------------------------------------------------------------- + * Type-definitions + * ----------------------------------------------------------------------------- + */ +typedef enum ud_type ud_type_t; +typedef enum ud_mnemonic_code ud_mnemonic_code_t; + +typedef struct ud ud_t; +typedef struct ud_operand ud_operand_t; + +#define UD_SYN_INTEL ud_translate_intel +#define UD_SYN_ATT ud_translate_att +#define UD_EOI -1 +#define UD_INP_CACHE_SZ 32 +#define UD_VENDOR_AMD 0 +#define UD_VENDOR_INTEL 1 +#define UD_VENDOR_ANY 2 + +#define bail_out(ud,error_code) longjmp( (ud)->bailout, error_code ) +#define try_decode(ud) if ( setjmp( (ud)->bailout ) == 0 ) +#define catch_error() else + +#endif diff --git a/src/3rdparty/masm/jit/JITCompilationEffort.h b/src/3rdparty/masm/jit/JITCompilationEffort.h new file mode 100644 index 0000000000..5eb6801789 --- /dev/null +++ b/src/3rdparty/masm/jit/JITCompilationEffort.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JITCompilationEffort_h +#define JITCompilationEffort_h + +namespace JSC { + +enum JITCompilationEffort { + JITCompilationCanFail, + JITCompilationMustSucceed +}; + +} // namespace JSC + +#endif // JITCompilationEffort_h + diff --git a/src/3rdparty/masm/masm.pri b/src/3rdparty/masm/masm.pri new file mode 100644 index 0000000000..92ad32d4ea --- /dev/null +++ b/src/3rdparty/masm/masm.pri @@ -0,0 +1,108 @@ + +HEADERS += $$PWD/assembler/*.h +SOURCES += $$PWD/assembler/ARMAssembler.cpp +SOURCES += $$PWD/assembler/ARMv7Assembler.cpp +SOURCES += $$PWD/assembler/MacroAssemblerARM.cpp +SOURCES += $$PWD/assembler/MacroAssemblerSH4.cpp +SOURCES += $$PWD/assembler/LinkBuffer.cpp + +HEADERS += $$PWD/wtf/*.h +SOURCES += $$PWD/wtf/PrintStream.cpp +HEADERS += $$PWD/wtf/PrintStream.h + +SOURCES += $$PWD/wtf/FilePrintStream.cpp +HEADERS += $$PWD/wtf/FilePrintStream.h + +HEADERS += $$PWD/wtf/RawPointer.h + +win32: SOURCES += $$PWD/wtf/OSAllocatorWin.cpp +else: SOURCES += $$PWD/wtf/OSAllocatorPosix.cpp +HEADERS += $$PWD/wtf/OSAllocator.h + +SOURCES += $$PWD/wtf/PageAllocationAligned.cpp +HEADERS += $$PWD/wtf/PageAllocationAligned.h +HEADERS += $$PWD/wtf/PageAllocation.h + +SOURCES += $$PWD/wtf/PageBlock.cpp +HEADERS += $$PWD/wtf/PageBlock.h + +HEADERS += $$PWD/wtf/PageReservation.h + +SOURCES += $$PWD/stubs/WTFStubs.cpp +HEADERS += $$PWD/stubs/WTFStubs.h + +DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" + +DEFINES += ENABLE_LLINT=0 +DEFINES += ENABLE_DFG_JIT=0 +DEFINES += ENABLE_JIT=1 +DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 +DEFINES += ENABLE_ASSEMBLER=1 + +DEFINES += BUILDING_QT__ + +INCLUDEPATH += $$PWD/jit +INCLUDEPATH += $$PWD/assembler +INCLUDEPATH += $$PWD/runtime +INCLUDEPATH += $$PWD/wtf +INCLUDEPATH += $$PWD/stubs +INCLUDEPATH += $$PWD/stubs/wtf +INCLUDEPATH += $$PWD + +DEFINES += WTF_USE_UDIS86=1 +INCLUDEPATH += $$PWD/disassembler +INCLUDEPATH += $$PWD/disassembler/udis86 +INCLUDEPATH += $$_OUT_PWD +SOURCES += $$PWD/disassembler/Disassembler.cpp +SOURCES += $$PWD/disassembler/UDis86Disassembler.cpp +SOURCES += $$PWD/disassembler/udis86/udis86.c +SOURCES += $$PWD/disassembler/udis86/udis86_decode.c +SOURCES += $$PWD/disassembler/udis86/udis86_input.c +SOURCES += $$PWD/disassembler/udis86/udis86_itab_holder.c +SOURCES += $$PWD/disassembler/udis86/udis86_syn-att.c +SOURCES += $$PWD/disassembler/udis86/udis86_syn.c +SOURCES += $$PWD/disassembler/udis86/udis86_syn-intel.c + +DEFINES += ENABLE_YARR_JIT=0 +SOURCES += \ + $$PWD/yarr/YarrCanonicalizeUCS2.cpp \ + $$PWD/yarr/YarrInterpreter.cpp \ + $$PWD/yarr/YarrPattern.cpp \ + $$PWD/yarr/YarrSyntaxChecker.cpp + +HEADERS += $$PWD/yarr/*.h + +retgen.output = RegExpJitTables.h +retgen.script = $$PWD/create_regex_tables +retgen.input = retgen.script +retgen.CONFIG += no_link +retgen.commands = python $$retgen.script > ${QMAKE_FILE_OUT} +QMAKE_EXTRA_COMPILERS += retgen + +ITAB = $$PWD/disassembler/udis86/optable.xml +udis86.output = udis86_itab.h +udis86.input = ITAB +udis86.CONFIG += no_link +udis86.commands = python $$PWD/disassembler/udis86/itab.py ${QMAKE_FILE_IN} +QMAKE_EXTRA_COMPILERS += udis86 + +udis86_tab_cfile.target = $$OUT_PWD/udis86_itab.c +udis86_tab_cfile.depends = udis86_itab.h +QMAKE_EXTRA_TARGETS += udis86_tab_cfile + +# Taken from WebKit/Tools/qmake/mkspecs/features/unix/default_post.prf +linux-g++* { + greaterThan(QT_GCC_MAJOR_VERSION, 3):greaterThan(QT_GCC_MINOR_VERSION, 5) { + !contains(QMAKE_CXXFLAGS, -std=(c|gnu)\\+\\+(0x|11)) { + # We need to deactivate those warnings because some names conflicts with upcoming c++0x types (e.g.nullptr). + QMAKE_CXXFLAGS_WARN_ON += -Wno-c++0x-compat + QMAKE_CXXFLAGS += -Wno-c++0x-compat + } + } +} + +# Don't warn about OVERRIDE and FINAL, since they are feature-checked anyways +*clang:!contains(QMAKE_CXXFLAGS, -std=c++11) { + QMAKE_CXXFLAGS += -Wno-c++11-extensions + QMAKE_OBJECTIVE_CFLAGS += -Wno-c++11-extensions +} diff --git a/src/3rdparty/masm/runtime/MatchResult.h b/src/3rdparty/masm/runtime/MatchResult.h new file mode 100644 index 0000000000..d87c8516b0 --- /dev/null +++ b/src/3rdparty/masm/runtime/MatchResult.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MatchResult_h +#define MatchResult_h + +typedef uint64_t EncodedMatchResult; + +struct MatchResult { + ALWAYS_INLINE MatchResult(size_t start, size_t end) + : start(start) + , end(end) + { + } + + explicit ALWAYS_INLINE MatchResult(EncodedMatchResult encoded) + { + union u { + uint64_t encoded; + struct s { + size_t start; + size_t end; + } split; + } value; + value.encoded = encoded; + start = value.split.start; + end = value.split.end; + } + + ALWAYS_INLINE static MatchResult failed() + { + return MatchResult(WTF::notFound, 0); + } + + ALWAYS_INLINE operator bool() + { + return start != WTF::notFound; + } + + ALWAYS_INLINE bool empty() + { + return start == end; + } + + size_t start; + size_t end; +}; + +#endif diff --git a/src/3rdparty/masm/stubs/ExecutableAllocator.h b/src/3rdparty/masm/stubs/ExecutableAllocator.h new file mode 100644 index 0000000000..7b3004aa90 --- /dev/null +++ b/src/3rdparty/masm/stubs/ExecutableAllocator.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef MASM_EXECUTABLEALLOCATOR_H +#define MASM_EXECUTABLEALLOCATOR_H + +#include +#include + +#include +#include + +namespace JSC { + +class JSGlobalData; + +struct ExecutableMemoryHandle : public RefCounted { + ExecutableMemoryHandle(int size) + : m_size(size) + { + static size_t pageSize = sysconf(_SC_PAGESIZE); + m_size = (m_size + pageSize - 1) & ~(pageSize - 1); +#if OS(DARWIN) +# define MAP_ANONYMOUS MAP_ANON +#endif + m_data = mmap(0, m_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + } + ~ExecutableMemoryHandle() + { + munmap(m_data, m_size); + } + + inline void shrink(size_t) { + // ### TODO. + } + + inline bool isManaged() const { return true; } + + void* start() { return m_data; } + int sizeInBytes() { return m_size; } + + void* m_data; + int m_size; +}; + +struct ExecutableAllocator { + PassRefPtr allocate(JSGlobalData&, int size, void*, int) + { + return adoptRef(new ExecutableMemoryHandle(size)); + } + + static void makeWritable(void*, int) + { + } + + static void makeExecutable(void* addr, int size) + { + static size_t pageSize = sysconf(_SC_PAGESIZE); + size_t iaddr = reinterpret_cast(addr); + size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); + int mode = PROT_READ | PROT_WRITE | PROT_EXEC; + mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode); + } +}; + +} + +#endif // MASM_EXECUTABLEALLOCATOR_H diff --git a/src/3rdparty/masm/stubs/JSGlobalData.h b/src/3rdparty/masm/stubs/JSGlobalData.h new file mode 100644 index 0000000000..112bedddd7 --- /dev/null +++ b/src/3rdparty/masm/stubs/JSGlobalData.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef MASM_JSGLOBALDATA_H +#define MASM_JSGLOBALDATA_H + +#include "ExecutableAllocator.h" +#include "WeakRandom.h" + +namespace JSC { + +class JSGlobalData { +public: + ExecutableAllocator executableAllocator; +}; + +} + +#endif // MASM_JSGLOBALDATA_H diff --git a/src/3rdparty/masm/stubs/LLIntData.h b/src/3rdparty/masm/stubs/LLIntData.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/3rdparty/masm/stubs/Options.h b/src/3rdparty/masm/stubs/Options.h new file mode 100644 index 0000000000..b95e4354e2 --- /dev/null +++ b/src/3rdparty/masm/stubs/Options.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef OPTIONS_H +#define OPTIONS_H + +namespace JSC { + +struct Options { + static bool showDisassembly() { return true; } + static bool showDFGDisassembly() { return true; } +}; + +} + +#endif // MASM_STUBS/OPTIONS_H diff --git a/src/3rdparty/masm/stubs/WTFStubs.cpp b/src/3rdparty/masm/stubs/WTFStubs.cpp new file mode 100644 index 0000000000..530804fe3e --- /dev/null +++ b/src/3rdparty/masm/stubs/WTFStubs.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include +#include +#include + +namespace WTF { + +void* fastMalloc(size_t size) +{ + return malloc(size); +} + +void* fastRealloc(void* ptr, size_t size) +{ + return realloc(ptr, size); +} + +void fastFree(void* ptr) +{ + free(ptr); +} + +uint32_t cryptographicallyRandomNumber() +{ + return 0; +} + +static FilePrintStream* s_dataFile; + +void setDataFile(FILE* f) +{ + delete s_dataFile; + s_dataFile = new FilePrintStream(f, FilePrintStream::Borrow); +} + +FilePrintStream& dataFile() +{ + if (!s_dataFile) + s_dataFile = new FilePrintStream(stderr, FilePrintStream::Borrow); + return *s_dataFile; +} + +void dataLogFV(const char* format, va_list args) +{ + char buffer[1024]; + vsnprintf(buffer, sizeof(buffer), format, args); + qDebug("%s", buffer); +} + +void dataLogF(const char* format, ...) +{ + char buffer[1024]; + va_list args; + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + qDebug("%s", buffer); +} + +void dataLogFString(const char* str) +{ + qDebug("%s", str); +} + +} + +extern "C" { + +void WTFReportAssertionFailure(const char* /*file*/, int /*line*/, const char* /*function*/, const char* /*assertion*/) +{ +} + +void WTFReportBacktrace() +{ +} + +void WTFInvokeCrashHook() +{ +} + +} + + +#if ENABLE(ASSEMBLER) && CPU(X86) && !OS(MAC_OS_X) +#include + +JSC::MacroAssemblerX86Common::SSE2CheckState JSC::MacroAssemblerX86Common::s_sse2CheckState = JSC::MacroAssemblerX86Common::NotCheckedSSE2; +#endif + diff --git a/src/3rdparty/masm/stubs/WTFStubs.h b/src/3rdparty/masm/stubs/WTFStubs.h new file mode 100644 index 0000000000..ec77d25da7 --- /dev/null +++ b/src/3rdparty/masm/stubs/WTFStubs.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef WTFSTUBS_H +#define WTFSTUBS_H + +namespace WTF { + +void setDataFile(FILE* f); + +} + +#endif // WTFSTUBS_H diff --git a/src/3rdparty/masm/stubs/wtf/FastAllocBase.h b/src/3rdparty/masm/stubs/wtf/FastAllocBase.h new file mode 100644 index 0000000000..a062a885af --- /dev/null +++ b/src/3rdparty/masm/stubs/wtf/FastAllocBase.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef FASTALLOCBASE_H +#define FASTALLOCBASE_H + +/* Dummy empty header file, only needed for #include source compatibility */ + +#define WTF_MAKE_FAST_ALLOCATED + +#endif // FASTALLOCBASE_H diff --git a/src/3rdparty/masm/stubs/wtf/FastMalloc.h b/src/3rdparty/masm/stubs/wtf/FastMalloc.h new file mode 100644 index 0000000000..1248c79dec --- /dev/null +++ b/src/3rdparty/masm/stubs/wtf/FastMalloc.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef FASTMALLOC_H +#define FASTMALLOC_H + +/* Dummy empty header file, only needed for #include source compatibility */ + +#endif // FASTMALLOC_H diff --git a/src/3rdparty/masm/stubs/wtf/Noncopyable.h b/src/3rdparty/masm/stubs/wtf/Noncopyable.h new file mode 100644 index 0000000000..d3d1eed6d1 --- /dev/null +++ b/src/3rdparty/masm/stubs/wtf/Noncopyable.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef NONCOPYABLE_H +#define NONCOPYABLE_H + +#include + +#define WTF_MAKE_NONCOPYABLE(x) Q_DISABLE_COPY(x) + +#endif // NONCOPYABLE_H diff --git a/src/3rdparty/masm/stubs/wtf/OwnPtr.h b/src/3rdparty/masm/stubs/wtf/OwnPtr.h new file mode 100644 index 0000000000..31d2f1efa3 --- /dev/null +++ b/src/3rdparty/masm/stubs/wtf/OwnPtr.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef OWNPTR_H +#define OWNPTR_H + +#include "PassOwnPtr.h" + +#endif // OWNPTR_H diff --git a/src/3rdparty/masm/stubs/wtf/PassOwnPtr.h b/src/3rdparty/masm/stubs/wtf/PassOwnPtr.h new file mode 100644 index 0000000000..f9b84e7b57 --- /dev/null +++ b/src/3rdparty/masm/stubs/wtf/PassOwnPtr.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef PASSOWNPTR_H +#define PASSOWNPTR_H + +#include + +template class PassOwnPtr; +template PassOwnPtr adoptPtr(PtrType*); + +template +struct OwnPtr : public QScopedPointer +{ + OwnPtr() {} + OwnPtr(const PassOwnPtr &ptr) + : QScopedPointer(ptr.leakRef()) + {} + + OwnPtr& operator=(const OwnPtr& other) + { + this->reset(const_cast &>(other).take()); + return *this; + } + + T* get() const { return this->data(); } + + PassOwnPtr release() + { + return adoptPtr(this->take()); + } +}; + +template +class PassOwnPtr { +public: + PassOwnPtr() {} + + PassOwnPtr(T* ptr) + : m_ptr(ptr) + { + } + + PassOwnPtr(const PassOwnPtr& other) + : m_ptr(other.leakRef()) + { + } + + PassOwnPtr(const OwnPtr& other) + : m_ptr(other.take()) + { + } + + ~PassOwnPtr() + { + } + + T* operator->() const { return m_ptr.data(); } + + T* leakRef() const { return m_ptr.take(); } + +private: + template friend PassOwnPtr adoptPtr(PtrType*); + + PassOwnPtr& operator=(const PassOwnPtr&) + {} + mutable QScopedPointer m_ptr; +}; + +template +PassOwnPtr adoptPtr(T* ptr) +{ + PassOwnPtr result; + result.m_ptr.reset(ptr); + return result; +} + + +#endif // PASSOWNPTR_H diff --git a/src/3rdparty/masm/stubs/wtf/PassRefPtr.h b/src/3rdparty/masm/stubs/wtf/PassRefPtr.h new file mode 100644 index 0000000000..d97be1c330 --- /dev/null +++ b/src/3rdparty/masm/stubs/wtf/PassRefPtr.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef PASSREFPTR_H +#define PASSREFPTR_H + +template class RefPtr; + +template +class PassRefPtr { +public: + PassRefPtr() : m_ptr(0) {} + + PassRefPtr(T* ptr) + : m_ptr(ptr) + { + if (m_ptr) + m_ptr->ref(); + } + + PassRefPtr(const PassRefPtr& other) + : m_ptr(other.leakRef()) + { + } + + PassRefPtr(const RefPtr& other) + : m_ptr(other.get()) + { + if (m_ptr) + m_ptr->ref(); + } + + ~PassRefPtr() + { + if (m_ptr) + m_ptr->deref(); + } + + T* operator->() const { return m_ptr; } + + T* leakRef() const + { + T* result = m_ptr; + m_ptr = 0; + return result; + } + +private: + PassRefPtr& operator=(const PassRefPtr&) + {} + + template friend PassRefPtr adoptRef(PtrType*); + mutable T* m_ptr; +}; + +template +PassRefPtr adoptRef(T* ptr) +{ + PassRefPtr result; + result.m_ptr = ptr; + return result; +} + +#endif // PASSREFPTR_H diff --git a/src/3rdparty/masm/stubs/wtf/RefCounted.h b/src/3rdparty/masm/stubs/wtf/RefCounted.h new file mode 100644 index 0000000000..4fc9ad9074 --- /dev/null +++ b/src/3rdparty/masm/stubs/wtf/RefCounted.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef REFCOUNTED_H +#define REFCOUNTED_H + +#include "PassRefPtr.h" + +template +class RefCounted { +public: + RefCounted() : m_refCount(1) {} + ~RefCounted() + { + deref(); + } + + void ref() + { + ++m_refCount; + } + + void deref() + { + if (!--m_refCount) + delete static_cast(this); + } + +protected: + int m_refCount; +}; + +#endif // REFCOUNTED_H diff --git a/src/3rdparty/masm/stubs/wtf/RefPtr.h b/src/3rdparty/masm/stubs/wtf/RefPtr.h new file mode 100644 index 0000000000..929b493b4b --- /dev/null +++ b/src/3rdparty/masm/stubs/wtf/RefPtr.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef REFPTR_H +#define REFPTR_H + +#include "PassRefPtr.h" + +template +class RefPtr { +public: + RefPtr() : m_ptr(0) {} + RefPtr(const RefPtr &other) + : m_ptr(other.m_ptr) + { + if (m_ptr) + m_ptr->ref(); + } + + RefPtr& operator=(const RefPtr& other) + { + if (other.m_ptr) + other.m_ptr->ref(); + if (m_ptr) + m_ptr->deref(); + m_ptr = other.m_ptr; + return *this; + } + + RefPtr(const PassRefPtr& other) + : m_ptr(other.leakRef()) + { + } + + ~RefPtr() + { + if (m_ptr) + m_ptr->deref(); + } + + T* operator->() const { return m_ptr; } + T* get() const { return m_ptr; } + bool operator!() const { return !m_ptr; } + + PassRefPtr release() + { + T* ptr = m_ptr; + m_ptr = 0; + return adoptRef(ptr); + } + +private: + T* m_ptr; +}; + +#endif // REFPTR_H diff --git a/src/3rdparty/masm/stubs/wtf/TypeTraits.h b/src/3rdparty/masm/stubs/wtf/TypeTraits.h new file mode 100644 index 0000000000..9b626a7a53 --- /dev/null +++ b/src/3rdparty/masm/stubs/wtf/TypeTraits.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef TYPETRAITS_H +#define TYPETRAITS_H + +namespace WTF { + +template +struct IsSameType { + static const bool value = false; +}; + +template +struct IsSameType { + static const bool value = true; +}; + +} + +#endif // TYPETRAITS_H diff --git a/src/3rdparty/masm/stubs/wtf/UnusedParam.h b/src/3rdparty/masm/stubs/wtf/UnusedParam.h new file mode 100644 index 0000000000..a676bdf303 --- /dev/null +++ b/src/3rdparty/masm/stubs/wtf/UnusedParam.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef UNUSEDPARAM_H +#define UNUSEDPARAM_H + +#include + +#define UNUSED_PARAM(x) Q_UNUSED(x) + +#endif // UNUSEDPARAM_H diff --git a/src/3rdparty/masm/stubs/wtf/Vector.h b/src/3rdparty/masm/stubs/wtf/Vector.h new file mode 100644 index 0000000000..1feea851e1 --- /dev/null +++ b/src/3rdparty/masm/stubs/wtf/Vector.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef VECTOR_H +#define VECTOR_H + +#include +#include +#include +#include + +namespace WTF { + +template +class Vector : public std::vector { +public: + Vector() {} + Vector(int initialSize) : std::vector(initialSize) {} + + inline void append(const T& value) + { this->push_back(value); } + + inline void append(const Vector& vector) + { + this->insert(this->end(), vector.begin(), vector.end()); + } + + using std::vector::insert; + + inline void insert(size_t position, T value) + { this->insert(this->begin() + position, value); } + + inline void grow(size_t size) + { this->resize(size); } + + inline void shrink(size_t size) + { this->erase(this->begin() + size, this->end()); } + + inline void remove(size_t position) + { this->erase(this->begin() + position); } + + inline bool isEmpty() const { return this->empty(); } + + inline T &last() { return *(this->begin() + this->size() - 1); } +}; + +template +void deleteAllValues(const Vector &vector) +{ + qDeleteAll(vector); +} + +} + +using WTF::Vector; +using WTF::deleteAllValues; + +#endif // VECTOR_H diff --git a/src/3rdparty/masm/stubs/wtf/text/CString.h b/src/3rdparty/masm/stubs/wtf/text/CString.h new file mode 100644 index 0000000000..c9a65e5c0b --- /dev/null +++ b/src/3rdparty/masm/stubs/wtf/text/CString.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CSTRING_H +#define CSTRING_H + +#endif // CSTRING_H diff --git a/src/3rdparty/masm/stubs/wtf/text/WTFString.h b/src/3rdparty/masm/stubs/wtf/text/WTFString.h new file mode 100644 index 0000000000..d157dc7adc --- /dev/null +++ b/src/3rdparty/masm/stubs/wtf/text/WTFString.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef WTFSTRING_H +#define WTFSTRING_H + +#include +#include +#include + +namespace WTF { + +class String : public QString +{ +public: + String(const QString& s) : QString(s) {} + bool is8Bit() const { return false; } + const unsigned char *characters8() const { return 0; } + const UChar *characters16() const { return reinterpret_cast(constData()); } + + template + const T* getCharacters() const; + +}; + +template <> +inline const unsigned char* String::getCharacters() const { return characters8(); } +template <> +inline const UChar* String::getCharacters() const { return characters16(); } + +} + +// Don't import WTF::String into the global namespace to avoid conflicts with QQmlJS::VM::String +namespace JSC { + using WTF::String; +} + +#endif // WTFSTRING_H diff --git a/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h b/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h new file mode 100644 index 0000000000..d61bc64c5a --- /dev/null +++ b/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef UNICODE_H +#define UNICODE_H + +#include + +typedef unsigned char LChar; +typedef uint16_t UChar; + +namespace Unicode { + inline UChar toLower(UChar ch) { + return QChar::toLower(ch); + } + + inline UChar toUpper(UChar ch) { + return QChar::toUpper(ch); + } +} + +#endif // UNICODE_H diff --git a/src/3rdparty/masm/wtf/ASCIICType.h b/src/3rdparty/masm/wtf/ASCIICType.h new file mode 100644 index 0000000000..18e108e1bf --- /dev/null +++ b/src/3rdparty/masm/wtf/ASCIICType.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_ASCIICType_h +#define WTF_ASCIICType_h + +#include + +// The behavior of many of the functions in the header is dependent +// on the current locale. But in the WebKit project, all uses of those functions +// are in code processing something that's not locale-specific. These equivalents +// for some of the functions are named more explicitly, not dependent +// on the C library locale, and we should also optimize them as needed. + +// All functions return false or leave the character unchanged if passed a character +// that is outside the range 0-7F. So they can be used on Unicode strings or +// characters if the intent is to do processing only if the character is ASCII. + +namespace WTF { + +template inline bool isASCII(CharType c) +{ + return !(c & ~0x7F); +} + +template inline bool isASCIIAlpha(CharType c) +{ + return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; +} + +template inline bool isASCIIDigit(CharType c) +{ + return c >= '0' && c <= '9'; +} + +template inline bool isASCIIAlphanumeric(CharType c) +{ + return isASCIIDigit(c) || isASCIIAlpha(c); +} + +template inline bool isASCIIHexDigit(CharType c) +{ + return isASCIIDigit(c) || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); +} + +template inline bool isASCIILower(CharType c) +{ + return c >= 'a' && c <= 'z'; +} + +template inline bool isASCIIOctalDigit(CharType c) +{ + return (c >= '0') & (c <= '7'); +} + +template inline bool isASCIIPrintable(CharType c) +{ + return c >= ' ' && c <= '~'; +} + +/* + Statistics from a run of Apple's page load test for callers of isASCIISpace: + + character count + --------- ----- + non-spaces 689383 + 20 space 294720 + 0A \n 89059 + 09 \t 28320 + 0D \r 0 + 0C \f 0 + 0B \v 0 + */ +template inline bool isASCIISpace(CharType c) +{ + return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); +} + +template inline bool isASCIIUpper(CharType c) +{ + return c >= 'A' && c <= 'Z'; +} + +template inline CharType toASCIILower(CharType c) +{ + return c | ((c >= 'A' && c <= 'Z') << 5); +} + +template inline CharType toASCIILowerUnchecked(CharType character) +{ + // This function can be used for comparing any input character + // to a lowercase English character. The isASCIIAlphaCaselessEqual + // below should be used for regular comparison of ASCII alpha + // characters, but switch statements in CSS tokenizer require + // direct use of this function. + return character | 0x20; +} + +template inline CharType toASCIIUpper(CharType c) +{ + return c & ~((c >= 'a' && c <= 'z') << 5); +} + +template inline int toASCIIHexValue(CharType c) +{ + ASSERT(isASCIIHexDigit(c)); + return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; +} + +template inline int toASCIIHexValue(CharType upperValue, CharType lowerValue) +{ + ASSERT(isASCIIHexDigit(upperValue) && isASCIIHexDigit(lowerValue)); + return ((toASCIIHexValue(upperValue) << 4) & 0xF0) | toASCIIHexValue(lowerValue); +} + +inline char lowerNibbleToASCIIHexDigit(char c) +{ + char nibble = c & 0xF; + return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; +} + +inline char upperNibbleToASCIIHexDigit(char c) +{ + char nibble = (c >> 4) & 0xF; + return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; +} + +template inline bool isASCIIAlphaCaselessEqual(CharType cssCharacter, char character) +{ + // This function compares a (preferrably) constant ASCII + // lowercase letter to any input character. + ASSERT(character >= 'a' && character <= 'z'); + return LIKELY(toASCIILowerUnchecked(cssCharacter) == character); +} + +} + +using WTF::isASCII; +using WTF::isASCIIAlpha; +using WTF::isASCIIAlphanumeric; +using WTF::isASCIIDigit; +using WTF::isASCIIHexDigit; +using WTF::isASCIILower; +using WTF::isASCIIOctalDigit; +using WTF::isASCIIPrintable; +using WTF::isASCIISpace; +using WTF::isASCIIUpper; +using WTF::toASCIIHexValue; +using WTF::toASCIILower; +using WTF::toASCIILowerUnchecked; +using WTF::toASCIIUpper; +using WTF::lowerNibbleToASCIIHexDigit; +using WTF::upperNibbleToASCIIHexDigit; +using WTF::isASCIIAlphaCaselessEqual; + +#endif diff --git a/src/3rdparty/masm/wtf/Assertions.h b/src/3rdparty/masm/wtf/Assertions.h new file mode 100644 index 0000000000..7e079ab187 --- /dev/null +++ b/src/3rdparty/masm/wtf/Assertions.h @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_Assertions_h +#define WTF_Assertions_h + +/* + no namespaces because this file has to be includable from C and Objective-C + + Note, this file uses many GCC extensions, but it should be compatible with + C, Objective C, C++, and Objective C++. + + For non-debug builds, everything is disabled by default. + Defining any of the symbols explicitly prevents this from having any effect. + + MSVC7 note: variadic macro support was added in MSVC8, so for now we disable + those macros in MSVC7. For more info, see the MSDN document on variadic + macros here: + + http://msdn2.microsoft.com/en-us/library/ms177415(VS.80).aspx +*/ + +#include + +#include + +#if !COMPILER(MSVC) +#include +#endif + +#ifdef NDEBUG +/* Disable ASSERT* macros in release mode. */ +#define ASSERTIONS_DISABLED_DEFAULT 1 +#else +#define ASSERTIONS_DISABLED_DEFAULT 0 +#endif + +#if COMPILER(MSVC7_OR_LOWER) +#define HAVE_VARIADIC_MACRO 0 +#else +#define HAVE_VARIADIC_MACRO 1 +#endif + +#ifndef BACKTRACE_DISABLED +#define BACKTRACE_DISABLED ASSERTIONS_DISABLED_DEFAULT +#endif + +#ifndef ASSERT_DISABLED +#define ASSERT_DISABLED ASSERTIONS_DISABLED_DEFAULT +#endif + +#ifndef ASSERT_MSG_DISABLED +#if HAVE(VARIADIC_MACRO) +#define ASSERT_MSG_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define ASSERT_MSG_DISABLED 1 +#endif +#endif + +#ifndef ASSERT_ARG_DISABLED +#define ASSERT_ARG_DISABLED ASSERTIONS_DISABLED_DEFAULT +#endif + +#ifndef FATAL_DISABLED +#if HAVE(VARIADIC_MACRO) +#define FATAL_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define FATAL_DISABLED 1 +#endif +#endif + +#ifndef ERROR_DISABLED +#if HAVE(VARIADIC_MACRO) +#define ERROR_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define ERROR_DISABLED 1 +#endif +#endif + +#ifndef LOG_DISABLED +#if HAVE(VARIADIC_MACRO) +#define LOG_DISABLED ASSERTIONS_DISABLED_DEFAULT +#else +#define LOG_DISABLED 1 +#endif +#endif + +#if COMPILER(GCC) +#define WTF_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#define WTF_PRETTY_FUNCTION __FUNCTION__ +#endif + +/* WTF logging functions can process %@ in the format string to log a NSObject* but the printf format attribute + emits a warning when %@ is used in the format string. Until is resolved we can't include + the attribute when being used from Objective-C code in case it decides to use %@. */ +#if COMPILER(GCC) && !defined(__OBJC__) +#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) __attribute__((__format__(printf, formatStringArgument, extraArguments))) +#else +#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) +#endif + +/* These helper functions are always declared, but not necessarily always defined if the corresponding function is disabled. */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { WTFLogChannelOff, WTFLogChannelOn } WTFLogChannelState; + +typedef struct { + unsigned mask; + const char *defaultName; + WTFLogChannelState state; +} WTFLogChannel; + +WTF_EXPORT_PRIVATE void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion); +WTF_EXPORT_PRIVATE void WTFReportAssertionFailureWithMessage(const char* file, int line, const char* function, const char* assertion, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6); +WTF_EXPORT_PRIVATE void WTFReportArgumentAssertionFailure(const char* file, int line, const char* function, const char* argName, const char* assertion); +WTF_EXPORT_PRIVATE void WTFReportFatalError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5); +WTF_EXPORT_PRIVATE void WTFReportError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5); +WTF_EXPORT_PRIVATE void WTFLog(WTFLogChannel*, const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); +WTF_EXPORT_PRIVATE void WTFLogVerbose(const char* file, int line, const char* function, WTFLogChannel*, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6); +WTF_EXPORT_PRIVATE void WTFLogAlways(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); + +WTF_EXPORT_PRIVATE void WTFGetBacktrace(void** stack, int* size); +WTF_EXPORT_PRIVATE void WTFReportBacktrace(); +WTF_EXPORT_PRIVATE void WTFPrintBacktrace(void** stack, int size); + +typedef void (*WTFCrashHookFunction)(); +WTF_EXPORT_PRIVATE void WTFSetCrashHook(WTFCrashHookFunction); +WTF_EXPORT_PRIVATE void WTFInvokeCrashHook(); +WTF_EXPORT_PRIVATE void WTFInstallReportBacktraceOnCrashHook(); + +#ifdef __cplusplus +} +#endif + +/* CRASH() - Raises a fatal error resulting in program termination and triggering either the debugger or the crash reporter. + + Use CRASH() in response to known, unrecoverable errors like out-of-memory. + Macro is enabled in both debug and release mode. + To test for unknown errors and verify assumptions, use ASSERT instead, to avoid impacting performance in release builds. + + Signals are ignored by the crash reporter on OS X so we must do better. +*/ +#ifndef CRASH +#if COMPILER(CLANG) +#define CRASH() \ + (WTFReportBacktrace(), \ + WTFInvokeCrashHook(), \ + (*(int *)(uintptr_t)0xbbadbeef = 0), \ + __builtin_trap()) +#else +#define CRASH() \ + (WTFReportBacktrace(), \ + WTFInvokeCrashHook(), \ + (*(int *)(uintptr_t)0xbbadbeef = 0), \ + ((void(*)())0)() /* More reliable, but doesn't say BBADBEEF */ \ + ) +#endif +#endif + +#if COMPILER(CLANG) +#define NO_RETURN_DUE_TO_CRASH NO_RETURN +#else +#define NO_RETURN_DUE_TO_CRASH +#endif + + +/* BACKTRACE + + Print a backtrace to the same location as ASSERT messages. +*/ + +#if BACKTRACE_DISABLED + +#define BACKTRACE() ((void)0) + +#else + +#define BACKTRACE() do { \ + WTFReportBacktrace(); \ +} while(false) + +#endif + +/* ASSERT, ASSERT_NOT_REACHED, ASSERT_UNUSED + + These macros are compiled out of release builds. + Expressions inside them are evaluated in debug builds only. +*/ + +#if OS(WINCE) && !PLATFORM(TORCHMOBILE) +/* FIXME: We include this here only to avoid a conflict with the ASSERT macro. */ +#include +#undef min +#undef max +#undef ERROR +#endif + +#if OS(WINDOWS) +/* FIXME: Change to use something other than ASSERT to avoid this conflict with the underlying platform */ +#undef ASSERT +#endif + +#if ASSERT_DISABLED + +#define ASSERT(assertion) ((void)0) +#define ASSERT_AT(assertion, file, line, function) ((void)0) +#define ASSERT_NOT_REACHED() ((void)0) +#define NO_RETURN_DUE_TO_ASSERT + +#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) +template +inline void assertUnused(T& x) { (void)x; } +#define ASSERT_UNUSED(variable, assertion) (assertUnused(variable)) +#else +#define ASSERT_UNUSED(variable, assertion) ((void)variable) +#endif + +#else + +#define ASSERT(assertion) \ + (!(assertion) ? \ + (WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion), \ + CRASH()) : \ + (void)0) + +#define ASSERT_AT(assertion, file, line, function) \ + (!(assertion) ? \ + (WTFReportAssertionFailure(file, line, function, #assertion), \ + CRASH()) : \ + (void)0) + +#define ASSERT_NOT_REACHED() do { \ + WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, 0); \ + CRASH(); \ +} while (0) + +#define ASSERT_UNUSED(variable, assertion) ASSERT(assertion) + +#define NO_RETURN_DUE_TO_ASSERT NO_RETURN_DUE_TO_CRASH + +#endif + +/* ASSERT_WITH_MESSAGE */ + +#if COMPILER(MSVC7_OR_LOWER) +#define ASSERT_WITH_MESSAGE(assertion) ((void)0) +#elif ASSERT_MSG_DISABLED +#define ASSERT_WITH_MESSAGE(assertion, ...) ((void)0) +#else +#define ASSERT_WITH_MESSAGE(assertion, ...) do \ + if (!(assertion)) { \ + WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ + CRASH(); \ + } \ +while (0) +#endif + +/* ASSERT_WITH_MESSAGE_UNUSED */ + +#if COMPILER(MSVC7_OR_LOWER) +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion) ((void)0) +#elif ASSERT_MSG_DISABLED +#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) +template +inline void assertWithMessageUnused(T& x) { (void)x; } +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) (assertWithMessageUnused(variable)) +#else +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) ((void)variable) +#endif +#else +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) do \ + if (!(assertion)) { \ + WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ + CRASH(); \ + } \ +while (0) +#endif + + +/* ASSERT_ARG */ + +#if ASSERT_ARG_DISABLED + +#define ASSERT_ARG(argName, assertion) ((void)0) + +#else + +#define ASSERT_ARG(argName, assertion) do \ + if (!(assertion)) { \ + WTFReportArgumentAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #argName, #assertion); \ + CRASH(); \ + } \ +while (0) + +#endif + +/* COMPILE_ASSERT */ +#ifndef COMPILE_ASSERT +#if COMPILER_SUPPORTS(C_STATIC_ASSERT) +#define COMPILE_ASSERT(exp, name) _Static_assert((exp), #name) +#else +#define COMPILE_ASSERT(exp, name) typedef int dummy##name [(exp) ? 1 : -1] +#endif +#endif + +/* FATAL */ + +#if COMPILER(MSVC7_OR_LOWER) +#define FATAL() ((void)0) +#elif FATAL_DISABLED +#define FATAL(...) ((void)0) +#else +#define FATAL(...) do { \ + WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__); \ + CRASH(); \ +} while (0) +#endif + +/* LOG_ERROR */ + +#if COMPILER(MSVC7_OR_LOWER) +#define LOG_ERROR() ((void)0) +#elif ERROR_DISABLED +#define LOG_ERROR(...) ((void)0) +#else +#define LOG_ERROR(...) WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__) +#endif + +/* LOG */ + +#if COMPILER(MSVC7_OR_LOWER) +#define LOG() ((void)0) +#elif LOG_DISABLED +#define LOG(channel, ...) ((void)0) +#else +#define LOG(channel, ...) WTFLog(&JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__) +#define JOIN_LOG_CHANNEL_WITH_PREFIX(prefix, channel) JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) +#define JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) prefix ## channel +#endif + +/* LOG_VERBOSE */ + +#if COMPILER(MSVC7_OR_LOWER) +#define LOG_VERBOSE(channel) ((void)0) +#elif LOG_DISABLED +#define LOG_VERBOSE(channel, ...) ((void)0) +#else +#define LOG_VERBOSE(channel, ...) WTFLogVerbose(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, &JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__) +#endif + +/* UNREACHABLE_FOR_PLATFORM */ + +#if COMPILER(CLANG) +// This would be a macro except that its use of #pragma works best around +// a function. Hence it uses macro naming convention. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-noreturn" +static inline void UNREACHABLE_FOR_PLATFORM() +{ + ASSERT_NOT_REACHED(); +} +#pragma clang diagnostic pop +#else +#define UNREACHABLE_FOR_PLATFORM() ASSERT_NOT_REACHED() +#endif + +#endif /* WTF_Assertions_h */ diff --git a/src/3rdparty/masm/wtf/Atomics.h b/src/3rdparty/masm/wtf/Atomics.h new file mode 100644 index 0000000000..750e1092ad --- /dev/null +++ b/src/3rdparty/masm/wtf/Atomics.h @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2007, 2008, 2010, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Note: The implementations of InterlockedIncrement and InterlockedDecrement are based + * on atomic_increment and atomic_exchange_and_add from the Boost C++ Library. The license + * is virtually identical to the Apple license above but is included here for completeness. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef Atomics_h +#define Atomics_h + +#include +#include +#include + +#if OS(WINDOWS) +#include +#elif OS(DARWIN) +#include +#elif OS(QNX) +#include +#elif OS(ANDROID) +#include +#elif COMPILER(GCC) +#if ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2))) && !defined(__LSB_VERSION__) +#include +#else +#include +#endif +#endif + +namespace WTF { + +#if OS(WINDOWS) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +#if COMPILER(MINGW) || COMPILER(MSVC7_OR_LOWER) || OS(WINCE) +inline int atomicIncrement(int* addend) { return InterlockedIncrement(reinterpret_cast(addend)); } +inline int atomicDecrement(int* addend) { return InterlockedDecrement(reinterpret_cast(addend)); } +#else +inline int atomicIncrement(int volatile* addend) { return InterlockedIncrement(reinterpret_cast(addend)); } +inline int atomicDecrement(int volatile* addend) { return InterlockedDecrement(reinterpret_cast(addend)); } +#endif + +#elif OS(DARWIN) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +inline int atomicIncrement(int volatile* addend) { return OSAtomicIncrement32Barrier(const_cast(addend)); } +inline int atomicDecrement(int volatile* addend) { return OSAtomicDecrement32Barrier(const_cast(addend)); } + +inline int64_t atomicIncrement(int64_t volatile* addend) { return OSAtomicIncrement64Barrier(const_cast(addend)); } +inline int64_t atomicDecrement(int64_t volatile* addend) { return OSAtomicDecrement64Barrier(const_cast(addend)); } + +#elif OS(QNX) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +// Note, atomic_{add, sub}_value() return the previous value of addend's content. +inline int atomicIncrement(int volatile* addend) { return static_cast(atomic_add_value(reinterpret_cast(addend), 1)) + 1; } +inline int atomicDecrement(int volatile* addend) { return static_cast(atomic_sub_value(reinterpret_cast(addend), 1)) - 1; } + +#elif OS(ANDROID) +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +// Note, __atomic_{inc, dec}() return the previous value of addend's content. +inline int atomicIncrement(int volatile* addend) { return __atomic_inc(addend) + 1; } +inline int atomicDecrement(int volatile* addend) { return __atomic_dec(addend) - 1; } + +#elif COMPILER(GCC) && !CPU(SPARC64) // sizeof(_Atomic_word) != sizeof(int) on sparc64 gcc +#define WTF_USE_LOCKFREE_THREADSAFEREFCOUNTED 1 + +inline int atomicIncrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, 1) + 1; } +inline int atomicDecrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, -1) - 1; } + +#endif + +#if OS(WINDOWS) +inline bool weakCompareAndSwap(volatile unsigned* location, unsigned expected, unsigned newValue) +{ +#if OS(WINCE) + return InterlockedCompareExchange(reinterpret_cast(const_cast(location)), static_cast(newValue), static_cast(expected)) == static_cast(expected); +#else + return InterlockedCompareExchange(reinterpret_cast(location), static_cast(newValue), static_cast(expected)) == static_cast(expected); +#endif +} + +inline bool weakCompareAndSwap(void*volatile* location, void* expected, void* newValue) +{ + return InterlockedCompareExchangePointer(location, newValue, expected) == expected; +} +#else // OS(WINDOWS) --> not windows +#if COMPILER(GCC) && !COMPILER(CLANG) // Work around a gcc bug +inline bool weakCompareAndSwap(volatile unsigned* location, unsigned expected, unsigned newValue) +#else +inline bool weakCompareAndSwap(unsigned* location, unsigned expected, unsigned newValue) +#endif +{ +#if ENABLE(COMPARE_AND_SWAP) +#if CPU(X86) || CPU(X86_64) + unsigned char result; + asm volatile( + "lock; cmpxchgl %3, %2\n\t" + "sete %1" + : "+a"(expected), "=q"(result), "+m"(*location) + : "r"(newValue) + : "memory" + ); +#elif CPU(ARM_THUMB2) + unsigned tmp; + unsigned result; + asm volatile( + "movw %1, #1\n\t" + "ldrex %2, %0\n\t" + "cmp %3, %2\n\t" + "bne.n 0f\n\t" + "strex %1, %4, %0\n\t" + "0:" + : "+Q"(*location), "=&r"(result), "=&r"(tmp) + : "r"(expected), "r"(newValue) + : "memory"); + result = !result; +#else +#error "Bad architecture for compare and swap." +#endif + return result; +#else + UNUSED_PARAM(location); + UNUSED_PARAM(expected); + UNUSED_PARAM(newValue); + CRASH(); + return false; +#endif +} + +inline bool weakCompareAndSwap(void*volatile* location, void* expected, void* newValue) +{ +#if ENABLE(COMPARE_AND_SWAP) +#if CPU(X86_64) + bool result; + asm volatile( + "lock; cmpxchgq %3, %2\n\t" + "sete %1" + : "+a"(expected), "=q"(result), "+m"(*location) + : "r"(newValue) + : "memory" + ); + return result; +#else + return weakCompareAndSwap(bitwise_cast(location), bitwise_cast(expected), bitwise_cast(newValue)); +#endif +#else // ENABLE(COMPARE_AND_SWAP) + UNUSED_PARAM(location); + UNUSED_PARAM(expected); + UNUSED_PARAM(newValue); + CRASH(); + return 0; +#endif // ENABLE(COMPARE_AND_SWAP) +} +#endif // OS(WINDOWS) (end of the not-windows case) + +inline bool weakCompareAndSwapUIntPtr(volatile uintptr_t* location, uintptr_t expected, uintptr_t newValue) +{ + return weakCompareAndSwap(reinterpret_cast(location), reinterpret_cast(expected), reinterpret_cast(newValue)); +} + +#if CPU(ARM_THUMB2) + +inline void memoryBarrierAfterLock() +{ + asm volatile("dmb" ::: "memory"); +} + +inline void memoryBarrierBeforeUnlock() +{ + asm volatile("dmb" ::: "memory"); +} + +#else + +inline void memoryBarrierAfterLock() { } +inline void memoryBarrierBeforeUnlock() { } + +#endif + +} // namespace WTF + +#if USE(LOCKFREE_THREADSAFEREFCOUNTED) +using WTF::atomicDecrement; +using WTF::atomicIncrement; +#endif + +#endif // Atomics_h diff --git a/src/3rdparty/masm/wtf/BumpPointerAllocator.h b/src/3rdparty/masm/wtf/BumpPointerAllocator.h new file mode 100644 index 0000000000..3b2cfd974a --- /dev/null +++ b/src/3rdparty/masm/wtf/BumpPointerAllocator.h @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BumpPointerAllocator_h +#define BumpPointerAllocator_h + +#include +#include +#include + +namespace WTF { + +#define MINIMUM_BUMP_POOL_SIZE 0x1000 + +class BumpPointerPool { +public: + // ensureCapacity will check whether the current pool has capacity to + // allocate 'size' bytes of memory If it does not, it will attempt to + // allocate a new pool (which will be added to this one in a chain). + // + // If allocation fails (out of memory) this method will return null. + // If the return value is non-null, then callers should update any + // references they have to this current (possibly full) BumpPointerPool + // to instead point to the newly returned BumpPointerPool. + BumpPointerPool* ensureCapacity(size_t size) + { + void* allocationEnd = static_cast(m_current) + size; + ASSERT(allocationEnd > m_current); // check for overflow + if (allocationEnd <= static_cast(this)) + return this; + return ensureCapacityCrossPool(this, size); + } + + // alloc should only be called after calling ensureCapacity; as such + // alloc will never fail. + void* alloc(size_t size) + { + void* current = m_current; + void* allocationEnd = static_cast(current) + size; + ASSERT(allocationEnd > current); // check for overflow + ASSERT(allocationEnd <= static_cast(this)); + m_current = allocationEnd; + return current; + } + + // The dealloc method releases memory allocated using alloc. Memory + // must be released in a LIFO fashion, e.g. if the client calls alloc + // four times, returning pointer A, B, C, D, then the only valid order + // in which these may be deallocaed is D, C, B, A. + // + // The client may optionally skip some deallocations. In the example + // above, it would be valid to only explicitly dealloc C, A (D being + // dealloced along with C, B along with A). + // + // If pointer was not allocated from this pool (or pools) then dealloc + // will CRASH(). Callers should update any references they have to + // this current BumpPointerPool to instead point to the returned + // BumpPointerPool. + BumpPointerPool* dealloc(void* position) + { + if ((position >= m_start) && (position <= static_cast(this))) { + ASSERT(position <= m_current); + m_current = position; + return this; + } + return deallocCrossPool(this, position); + } + +private: + // Placement operator new, returns the last 'size' bytes of allocation for use as this. + void* operator new(size_t size, const PageAllocation& allocation) + { + ASSERT(size < allocation.size()); + return reinterpret_cast(reinterpret_cast(allocation.base()) + allocation.size()) - size; + } + + BumpPointerPool(const PageAllocation& allocation) + : m_current(allocation.base()) + , m_start(allocation.base()) + , m_next(0) + , m_previous(0) + , m_allocation(allocation) + { + } + + static BumpPointerPool* create(size_t minimumCapacity = 0) + { + // Add size of BumpPointerPool object, check for overflow. + minimumCapacity += sizeof(BumpPointerPool); + if (minimumCapacity < sizeof(BumpPointerPool)) + return 0; + + size_t poolSize = std::max(static_cast(MINIMUM_BUMP_POOL_SIZE), WTF::pageSize()); + while (poolSize < minimumCapacity) { + poolSize <<= 1; + // The following if check relies on MINIMUM_BUMP_POOL_SIZE being a power of 2! + ASSERT(!(MINIMUM_BUMP_POOL_SIZE & (MINIMUM_BUMP_POOL_SIZE - 1))); + if (!poolSize) + return 0; + } + + PageAllocation allocation = PageAllocation::allocate(poolSize); + if (!!allocation) + return new (allocation) BumpPointerPool(allocation); + return 0; + } + + void shrink() + { + ASSERT(!m_previous); + m_current = m_start; + while (m_next) { + BumpPointerPool* nextNext = m_next->m_next; + m_next->destroy(); + m_next = nextNext; + } + } + + void destroy() + { + m_allocation.deallocate(); + } + + static BumpPointerPool* ensureCapacityCrossPool(BumpPointerPool* previousPool, size_t size) + { + // The pool passed should not have capacity, so we'll start with the next one. + ASSERT(previousPool); + ASSERT((static_cast(previousPool->m_current) + size) > previousPool->m_current); // check for overflow + ASSERT((static_cast(previousPool->m_current) + size) > static_cast(previousPool)); + BumpPointerPool* pool = previousPool->m_next; + + while (true) { + if (!pool) { + // We've run to the end; allocate a new pool. + pool = BumpPointerPool::create(size); + previousPool->m_next = pool; + pool->m_previous = previousPool; + return pool; + } + + // + void* current = pool->m_current; + void* allocationEnd = static_cast(current) + size; + ASSERT(allocationEnd > current); // check for overflow + if (allocationEnd <= static_cast(pool)) + return pool; + } + } + + static BumpPointerPool* deallocCrossPool(BumpPointerPool* pool, void* position) + { + // Should only be called if position is not in the current pool. + ASSERT((position < pool->m_start) || (position > static_cast(pool))); + + while (true) { + // Unwind the current pool to the start, move back in the chain to the previous pool. + pool->m_current = pool->m_start; + pool = pool->m_previous; + + // position was nowhere in the chain! + if (!pool) + CRASH(); + + if ((position >= pool->m_start) && (position <= static_cast(pool))) { + ASSERT(position <= pool->m_current); + pool->m_current = position; + return pool; + } + } + } + + void* m_current; + void* m_start; + BumpPointerPool* m_next; + BumpPointerPool* m_previous; + PageAllocation m_allocation; + + friend class BumpPointerAllocator; +}; + +// A BumpPointerAllocator manages a set of BumpPointerPool objects, which +// can be used for LIFO (stack like) allocation. +// +// To begin allocating using this class call startAllocator(). The result +// of this method will be null if the initial pool allocation fails, or a +// pointer to a BumpPointerPool object that can be used to perform +// allocations. Whilst running no memory will be released until +// stopAllocator() is called. At this point all allocations made through +// this allocator will be reaped, and underlying memory may be freed. +// +// (In practice we will still hold on to the initial pool to allow allocation +// to be quickly restared, but aditional pools will be freed). +// +// This allocator is non-renetrant, it is encumbant on the clients to ensure +// startAllocator() is not called again until stopAllocator() has been called. +class BumpPointerAllocator { +public: + BumpPointerAllocator() + : m_head(0) + { + } + + ~BumpPointerAllocator() + { + if (m_head) + m_head->destroy(); + } + + BumpPointerPool* startAllocator() + { + if (!m_head) + m_head = BumpPointerPool::create(); + return m_head; + } + + void stopAllocator() + { + if (m_head) + m_head->shrink(); + } + +private: + BumpPointerPool* m_head; +}; + +} + +using WTF::BumpPointerAllocator; + +#endif // BumpPointerAllocator_h diff --git a/src/3rdparty/masm/wtf/CheckedArithmetic.h b/src/3rdparty/masm/wtf/CheckedArithmetic.h new file mode 100644 index 0000000000..f5d3b75500 --- /dev/null +++ b/src/3rdparty/masm/wtf/CheckedArithmetic.h @@ -0,0 +1,708 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CheckedArithmetic_h +#define CheckedArithmetic_h + +#include +#include + +#include +#include + +/* Checked + * + * This class provides a mechanism to perform overflow-safe integer arithmetic + * without having to manually ensure that you have all the required bounds checks + * directly in your code. + * + * There are two modes of operation: + * - The default is Checked, and crashes at the point + * and overflow has occurred. + * - The alternative is Checked, which uses an additional + * byte of storage to track whether an overflow has occurred, subsequent + * unchecked operations will crash if an overflow has occured + * + * It is possible to provide a custom overflow handler, in which case you need + * to support these functions: + * - void overflowed(); + * This function is called when an operation has produced an overflow. + * - bool hasOverflowed(); + * This function must return true if overflowed() has been called on an + * instance and false if it has not. + * - void clearOverflow(); + * Used to reset overflow tracking when a value is being overwritten with + * a new value. + * + * Checked works for all integer types, with the following caveats: + * - Mixing signedness of operands is only supported for types narrower than + * 64bits. + * - It does have a performance impact, so tight loops may want to be careful + * when using it. + * + */ + +namespace WTF { + +class CrashOnOverflow { +protected: + NO_RETURN_DUE_TO_CRASH void overflowed() + { + CRASH(); + } + + void clearOverflow() { } + +public: + bool hasOverflowed() const { return false; } +}; + +class RecordOverflow { +protected: + RecordOverflow() + : m_overflowed(false) + { + } + + void overflowed() + { + m_overflowed = true; + } + + void clearOverflow() + { + m_overflowed = false; + } + +public: + bool hasOverflowed() const { return m_overflowed; } + +private: + unsigned char m_overflowed; +}; + +template class Checked; +template struct RemoveChecked; +template struct RemoveChecked >; + +template ::is_signed, bool sourceSigned = std::numeric_limits::is_signed> struct BoundsChecker; +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Same signedness so implicit type conversion will always increase precision + // to widest type + return value <= std::numeric_limits::max(); + } +}; + +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Same signedness so implicit type conversion will always increase precision + // to widest type + return std::numeric_limits::min() <= value && value <= std::numeric_limits::max(); + } +}; + +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Target is unsigned so any value less than zero is clearly unsafe + if (value < 0) + return false; + // If our (unsigned) Target is the same or greater width we can + // convert value to type Target without losing precision + if (sizeof(Target) >= sizeof(Source)) + return static_cast(value) <= std::numeric_limits::max(); + // The signed Source type has greater precision than the target so + // max(Target) -> Source will widen. + return value <= static_cast(std::numeric_limits::max()); + } +}; + +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Signed target with an unsigned source + if (sizeof(Target) <= sizeof(Source)) + return value <= static_cast(std::numeric_limits::max()); + // Target is Wider than Source so we're guaranteed to fit any value in + // unsigned Source + return true; + } +}; + +template ::value || (sizeof(Target) > sizeof(Source)) > struct BoundsCheckElider; +template struct BoundsCheckElider { + static bool inBounds(Source) { return true; } +}; +template struct BoundsCheckElider : public BoundsChecker { +}; + +template static inline bool isInBounds(Source value) +{ + return BoundsCheckElider::inBounds(value); +} + +template struct RemoveChecked { + typedef T CleanType; + static const CleanType DefaultValue = 0; +}; + +template struct RemoveChecked > { + typedef typename RemoveChecked::CleanType CleanType; + static const CleanType DefaultValue = 0; +}; + +template struct RemoveChecked > { + typedef typename RemoveChecked::CleanType CleanType; + static const CleanType DefaultValue = 0; +}; + +// The ResultBase and SignednessSelector are used to workaround typeof not being +// available in MSVC +template sizeof(V)), bool sameSize = (sizeof(U) == sizeof(V))> struct ResultBase; +template struct ResultBase { + typedef U ResultType; +}; + +template struct ResultBase { + typedef V ResultType; +}; + +template struct ResultBase { + typedef U ResultType; +}; + +template ::is_signed, bool vIsSigned = std::numeric_limits::is_signed> struct SignednessSelector; +template struct SignednessSelector { + typedef U ResultType; +}; + +template struct SignednessSelector { + typedef U ResultType; +}; + +template struct SignednessSelector { + typedef V ResultType; +}; + +template struct SignednessSelector { + typedef U ResultType; +}; + +template struct ResultBase { + typedef typename SignednessSelector::ResultType ResultType; +}; + +template struct Result : ResultBase::CleanType, typename RemoveChecked::CleanType> { +}; + +template ::ResultType, + bool lhsSigned = std::numeric_limits::is_signed, bool rhsSigned = std::numeric_limits::is_signed> struct ArithmeticOperations; + +template struct ArithmeticOperations { + // LHS and RHS are signed types + + // Helper function + static inline bool signsMatch(LHS lhs, RHS rhs) + { + return (lhs ^ rhs) >= 0; + } + + static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if ((std::numeric_limits::max() - rhs) < lhs) + return false; + } else { + ResultType temp = lhs - std::numeric_limits::min(); + if (rhs < -temp) + return false; + } + } // if the signs do not match this operation can't overflow + result = lhs + rhs; + return true; + } + + static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (!signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if (lhs > std::numeric_limits::max() + rhs) + return false; + } else { + if (rhs > std::numeric_limits::max() + lhs) + return false; + } + } // if the signs match this operation can't overflow + result = lhs - rhs; + return true; + } + + static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if (lhs && (std::numeric_limits::max() / lhs) < rhs) + return false; + } else { + if (static_cast(lhs) == std::numeric_limits::min() || static_cast(rhs) == std::numeric_limits::min()) + return false; + if ((std::numeric_limits::max() / -lhs) < -rhs) + return false; + } + } else { + if (lhs < 0) { + if (rhs && lhs < (std::numeric_limits::min() / rhs)) + return false; + } else { + if (lhs && rhs < (std::numeric_limits::min() / lhs)) + return false; + } + } + result = lhs * rhs; + return true; + } + + static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } + +}; + +template struct ArithmeticOperations { + // LHS and RHS are unsigned types so bounds checks are nice and easy + static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs + rhs; + if (temp < lhs) + return false; + result = temp; + return true; + } + + static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs - rhs; + if (temp > lhs) + return false; + result = temp; + return true; + } + + static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs * rhs; + if (temp < lhs) + return false; + result = temp; + return true; + } + + static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } + +}; + +template struct ArithmeticOperations { + static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs + rhs; + if (temp < std::numeric_limits::min()) + return false; + if (temp > std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs - rhs; + if (temp < std::numeric_limits::min()) + return false; + if (temp > std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs * rhs; + if (temp < std::numeric_limits::min()) + return false; + if (temp > std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool equals(int lhs, unsigned rhs) + { + return static_cast(lhs) == static_cast(rhs); + } +}; + +template struct ArithmeticOperations { + static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations::add(rhs, lhs, result); + } + + static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations::sub(lhs, rhs, result); + } + + static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations::multiply(rhs, lhs, result); + } + + static inline bool equals(unsigned lhs, int rhs) + { + return ArithmeticOperations::equals(rhs, lhs); + } +}; + +template static inline bool safeAdd(U lhs, V rhs, R& result) +{ + return ArithmeticOperations::add(lhs, rhs, result); +} + +template static inline bool safeSub(U lhs, V rhs, R& result) +{ + return ArithmeticOperations::sub(lhs, rhs, result); +} + +template static inline bool safeMultiply(U lhs, V rhs, R& result) +{ + return ArithmeticOperations::multiply(lhs, rhs, result); +} + +template static inline bool safeEquals(U lhs, V rhs) +{ + return ArithmeticOperations::equals(lhs, rhs); +} + +enum ResultOverflowedTag { ResultOverflowed }; + +// FIXME: Needed to workaround http://llvm.org/bugs/show_bug.cgi?id=10801 +static inline bool workAroundClangBug() { return true; } + +template class Checked : public OverflowHandler { +public: + template friend class Checked; + Checked() + : m_value(0) + { + } + + Checked(ResultOverflowedTag) + : m_value(0) + { + // FIXME: Remove this when clang fixes http://llvm.org/bugs/show_bug.cgi?id=10801 + if (workAroundClangBug()) + this->overflowed(); + } + + template Checked(U value) + { + if (!isInBounds(value)) + this->overflowed(); + m_value = static_cast(value); + } + + template Checked(const Checked& rhs) + : m_value(rhs.m_value) + { + if (rhs.hasOverflowed()) + this->overflowed(); + } + + template Checked(const Checked& rhs) + : OverflowHandler(rhs) + { + if (!isInBounds(rhs.m_value)) + this->overflowed(); + m_value = static_cast(rhs.m_value); + } + + template Checked(const Checked& rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + if (!isInBounds(rhs.m_value)) + this->overflowed(); + m_value = static_cast(rhs.m_value); + } + + const Checked& operator=(Checked rhs) + { + this->clearOverflow(); + if (rhs.hasOverflowed()) + this->overflowed(); + m_value = static_cast(rhs.m_value); + return *this; + } + + template const Checked& operator=(U value) + { + return *this = Checked(value); + } + + template const Checked& operator=(const Checked& rhs) + { + return *this = Checked(rhs); + } + + // prefix + const Checked& operator++() + { + if (m_value == std::numeric_limits::max()) + this->overflowed(); + m_value++; + return *this; + } + + const Checked& operator--() + { + if (m_value == std::numeric_limits::min()) + this->overflowed(); + m_value--; + return *this; + } + + // postfix operators + const Checked operator++(int) + { + if (m_value == std::numeric_limits::max()) + this->overflowed(); + return Checked(m_value++); + } + + const Checked operator--(int) + { + if (m_value == std::numeric_limits::min()) + this->overflowed(); + return Checked(m_value--); + } + + // Boolean operators + bool operator!() const + { + if (this->hasOverflowed()) + CRASH(); + return !m_value; + } + + typedef void* (Checked::*UnspecifiedBoolType); + operator UnspecifiedBoolType*() const + { + if (this->hasOverflowed()) + CRASH(); + return (m_value) ? reinterpret_cast(1) : 0; + } + + // Value accessors. unsafeGet() will crash if there's been an overflow. + T unsafeGet() const + { + if (this->hasOverflowed()) + CRASH(); + return m_value; + } + + bool safeGet(T& value) const WARN_UNUSED_RETURN + { + value = m_value; + return this->hasOverflowed(); + } + + // Mutating assignment + template const Checked operator+=(U rhs) + { + if (!safeAdd(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + template const Checked operator-=(U rhs) + { + if (!safeSub(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + template const Checked operator*=(U rhs) + { + if (!safeMultiply(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + const Checked operator*=(double rhs) + { + double result = rhs * m_value; + // Handle +/- infinity and NaN + if (!(std::numeric_limits::min() <= result && std::numeric_limits::max() >= result)) + this->overflowed(); + m_value = (T)result; + return *this; + } + + const Checked operator*=(float rhs) + { + return *this *= (double)rhs; + } + + template const Checked operator+=(Checked rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this += rhs.m_value; + } + + template const Checked operator-=(Checked rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this -= rhs.m_value; + } + + template const Checked operator*=(Checked rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this *= rhs.m_value; + } + + // Equality comparisons + template bool operator==(Checked rhs) + { + return unsafeGet() == rhs.unsafeGet(); + } + + template bool operator==(U rhs) + { + if (this->hasOverflowed()) + this->overflowed(); + return safeEquals(m_value, rhs); + } + + template const Checked operator==(Checked rhs) + { + return unsafeGet() == Checked(rhs.unsafeGet()); + } + + template bool operator!=(U rhs) + { + return !(*this == rhs); + } + +private: + // Disallow implicit conversion of floating point to integer types + Checked(float); + Checked(double); + void operator=(float); + void operator=(double); + void operator+=(float); + void operator+=(double); + void operator-=(float); + void operator-=(double); + T m_value; +}; + +template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, Checked rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result::ResultType result = 0; + overflowed |= !safeAdd(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, Checked rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result::ResultType result = 0; + overflowed |= !safeSub(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, Checked rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result::ResultType result = 0; + overflowed |= !safeMultiply(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, V rhs) +{ + return lhs + Checked(rhs); +} + +template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, V rhs) +{ + return lhs - Checked(rhs); +} + +template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, V rhs) +{ + return lhs * Checked(rhs); +} + +template static inline Checked::ResultType, OverflowHandler> operator+(U lhs, Checked rhs) +{ + return Checked(lhs) + rhs; +} + +template static inline Checked::ResultType, OverflowHandler> operator-(U lhs, Checked rhs) +{ + return Checked(lhs) - rhs; +} + +template static inline Checked::ResultType, OverflowHandler> operator*(U lhs, Checked rhs) +{ + return Checked(lhs) * rhs; +} + +} + +using WTF::Checked; +using WTF::RecordOverflow; + +#endif diff --git a/src/3rdparty/masm/wtf/Compiler.h b/src/3rdparty/masm/wtf/Compiler.h new file mode 100644 index 0000000000..a9ef419c18 --- /dev/null +++ b/src/3rdparty/masm/wtf/Compiler.h @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_Compiler_h +#define WTF_Compiler_h + +/* COMPILER() - the compiler being used to build the project */ +#define COMPILER(WTF_FEATURE) (defined WTF_COMPILER_##WTF_FEATURE && WTF_COMPILER_##WTF_FEATURE) + +/* COMPILER_SUPPORTS() - whether the compiler being used to build the project supports the given feature. */ +#define COMPILER_SUPPORTS(WTF_COMPILER_FEATURE) (defined WTF_COMPILER_SUPPORTS_##WTF_COMPILER_FEATURE && WTF_COMPILER_SUPPORTS_##WTF_COMPILER_FEATURE) + +/* COMPILER_QUIRK() - whether the compiler being used to build the project requires a given quirk. */ +#define COMPILER_QUIRK(WTF_COMPILER_QUIRK) (defined WTF_COMPILER_QUIRK_##WTF_COMPILER_QUIRK && WTF_COMPILER_QUIRK_##WTF_COMPILER_QUIRK) + +/* ==== COMPILER() - the compiler being used to build the project ==== */ + +/* COMPILER(CLANG) - Clang */ +#if defined(__clang__) +#define WTF_COMPILER_CLANG 1 + +#ifndef __has_extension +#define __has_extension __has_feature /* Compatibility with older versions of clang */ +#endif + +#define CLANG_PRAGMA(PRAGMA) _Pragma(PRAGMA) + +/* Specific compiler features */ +#define WTF_COMPILER_SUPPORTS_CXX_VARIADIC_TEMPLATES __has_extension(cxx_variadic_templates) + +/* There is a bug in clang that comes with Xcode 4.2 where AtomicStrings can't be implicitly converted to Strings + in the presence of move constructors and/or move assignment operators. This bug has been fixed in Xcode 4.3 clang, so we + check for both cxx_rvalue_references as well as the unrelated cxx_nonstatic_member_init feature which we know was added in 4.3 */ +#define WTF_COMPILER_SUPPORTS_CXX_RVALUE_REFERENCES __has_extension(cxx_rvalue_references) && __has_extension(cxx_nonstatic_member_init) + +#define WTF_COMPILER_SUPPORTS_CXX_DELETED_FUNCTIONS __has_extension(cxx_deleted_functions) +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR __has_feature(cxx_nullptr) +#define WTF_COMPILER_SUPPORTS_CXX_EXPLICIT_CONVERSIONS __has_feature(cxx_explicit_conversions) +#define WTF_COMPILER_SUPPORTS_BLOCKS __has_feature(blocks) +#define WTF_COMPILER_SUPPORTS_C_STATIC_ASSERT __has_extension(c_static_assert) +#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL __has_extension(cxx_override_control) +#define WTF_COMPILER_SUPPORTS_HAS_TRIVIAL_DESTRUCTOR __has_extension(has_trivial_destructor) + +#endif + +#ifndef CLANG_PRAGMA +#define CLANG_PRAGMA(PRAGMA) +#endif + +/* COMPILER(MSVC) - Microsoft Visual C++ */ +/* COMPILER(MSVC7_OR_LOWER) - Microsoft Visual C++ 2003 or lower*/ +/* COMPILER(MSVC9_OR_LOWER) - Microsoft Visual C++ 2008 or lower*/ +#if defined(_MSC_VER) +#define WTF_COMPILER_MSVC 1 +#if _MSC_VER < 1400 +#define WTF_COMPILER_MSVC7_OR_LOWER 1 +#elif _MSC_VER < 1600 +#define WTF_COMPILER_MSVC9_OR_LOWER 1 +#endif + +/* Specific compiler features */ +#if !COMPILER(CLANG) && _MSC_VER >= 1600 +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 +#endif + +#if !COMPILER(CLANG) +#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL 1 +#define WTF_COMPILER_QUIRK_FINAL_IS_CALLED_SEALED 1 +#endif + +#endif + +/* COMPILER(RVCT) - ARM RealView Compilation Tools */ +/* COMPILER(RVCT4_OR_GREATER) - ARM RealView Compilation Tools 4.0 or greater */ +#if defined(__CC_ARM) || defined(__ARMCC__) +#define WTF_COMPILER_RVCT 1 +#define RVCT_VERSION_AT_LEAST(major, minor, patch, build) (__ARMCC_VERSION >= (major * 100000 + minor * 10000 + patch * 1000 + build)) +#else +/* Define this for !RVCT compilers, just so we can write things like RVCT_VERSION_AT_LEAST(3, 0, 0, 0). */ +#define RVCT_VERSION_AT_LEAST(major, minor, patch, build) 0 +#endif + +/* COMPILER(GCCE) - GNU Compiler Collection for Embedded */ +#if defined(__GCCE__) +#define WTF_COMPILER_GCCE 1 +#define GCCE_VERSION (__GCCE__ * 10000 + __GCCE_MINOR__ * 100 + __GCCE_PATCHLEVEL__) +#define GCCE_VERSION_AT_LEAST(major, minor, patch) (GCCE_VERSION >= (major * 10000 + minor * 100 + patch)) +#endif + +/* COMPILER(GCC) - GNU Compiler Collection */ +/* --gnu option of the RVCT compiler also defines __GNUC__ */ +#if defined(__GNUC__) && !COMPILER(RVCT) +#define WTF_COMPILER_GCC 1 +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#define GCC_VERSION_AT_LEAST(major, minor, patch) (GCC_VERSION >= (major * 10000 + minor * 100 + patch)) +#else +/* Define this for !GCC compilers, just so we can write things like GCC_VERSION_AT_LEAST(4, 1, 0). */ +#define GCC_VERSION_AT_LEAST(major, minor, patch) 0 +#endif + +/* Specific compiler features */ +#if COMPILER(GCC) && !COMPILER(CLANG) +#if GCC_VERSION_AT_LEAST(4, 7, 0) && defined(__cplusplus) && __cplusplus >= 201103L +#define WTF_COMPILER_SUPPORTS_CXX_RVALUE_REFERENCES 1 +#define WTF_COMPILER_SUPPORTS_CXX_DELETED_FUNCTIONS 1 +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 +#define WTF_COMPILER_SUPPORTS_CXX_OVERRIDE_CONTROL 1 +#define WTF_COMPILER_QUIRK_GCC11_GLOBAL_ISINF_ISNAN 1 + +#elif GCC_VERSION_AT_LEAST(4, 6, 0) && defined(__GXX_EXPERIMENTAL_CXX0X__) +#define WTF_COMPILER_SUPPORTS_CXX_NULLPTR 1 +#define WTF_COMPILER_QUIRK_GCC11_GLOBAL_ISINF_ISNAN 1 +#endif + +#endif + +/* COMPILER(MINGW) - MinGW GCC */ +/* COMPILER(MINGW64) - mingw-w64 GCC - only used as additional check to exclude mingw.org specific functions */ +#if defined(__MINGW32__) +#define WTF_COMPILER_MINGW 1 +#include <_mingw.h> /* private MinGW header */ + #if defined(__MINGW64_VERSION_MAJOR) /* best way to check for mingw-w64 vs mingw.org */ + #define WTF_COMPILER_MINGW64 1 + #endif /* __MINGW64_VERSION_MAJOR */ +#endif /* __MINGW32__ */ + +/* COMPILER(INTEL) - Intel C++ Compiler */ +#if defined(__INTEL_COMPILER) +#define WTF_COMPILER_INTEL 1 +#endif + +/* COMPILER(SUNCC) */ +#if defined(__SUNPRO_CC) || defined(__SUNPRO_C) +#define WTF_COMPILER_SUNCC 1 +#endif + +/* ==== Compiler features ==== */ + + +/* ALWAYS_INLINE */ + +#ifndef ALWAYS_INLINE +#if COMPILER(GCC) && defined(NDEBUG) && !COMPILER(MINGW) +#define ALWAYS_INLINE inline __attribute__((__always_inline__)) +#elif (COMPILER(MSVC) || COMPILER(RVCT)) && defined(NDEBUG) +#define ALWAYS_INLINE __forceinline +#else +#define ALWAYS_INLINE inline +#endif +#endif + + +/* NEVER_INLINE */ + +#ifndef NEVER_INLINE +#if COMPILER(GCC) +#define NEVER_INLINE __attribute__((__noinline__)) +#elif COMPILER(RVCT) +#define NEVER_INLINE __declspec(noinline) +#else +#define NEVER_INLINE +#endif +#endif + + +/* UNLIKELY */ + +#ifndef UNLIKELY +#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(3, 0, 0, 0) && defined(__GNUC__)) +#define UNLIKELY(x) __builtin_expect((x), 0) +#else +#define UNLIKELY(x) (x) +#endif +#endif + + +/* LIKELY */ + +#ifndef LIKELY +#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(3, 0, 0, 0) && defined(__GNUC__)) +#define LIKELY(x) __builtin_expect((x), 1) +#else +#define LIKELY(x) (x) +#endif +#endif + + +/* NO_RETURN */ + + +#ifndef NO_RETURN +#if COMPILER(GCC) +#define NO_RETURN __attribute((__noreturn__)) +#elif COMPILER(MSVC) || COMPILER(RVCT) +#define NO_RETURN __declspec(noreturn) +#else +#define NO_RETURN +#endif +#endif + + +/* NO_RETURN_WITH_VALUE */ + +#ifndef NO_RETURN_WITH_VALUE +#if !COMPILER(MSVC) +#define NO_RETURN_WITH_VALUE NO_RETURN +#else +#define NO_RETURN_WITH_VALUE +#endif +#endif + + +/* WARN_UNUSED_RETURN */ + +#if COMPILER(GCC) +#define WARN_UNUSED_RETURN __attribute__ ((warn_unused_result)) +#else +#define WARN_UNUSED_RETURN +#endif + +/* OVERRIDE and FINAL */ + +#if COMPILER_SUPPORTS(CXX_OVERRIDE_CONTROL) +#define OVERRIDE override + +#if COMPILER_QUIRK(FINAL_IS_CALLED_SEALED) +#define FINAL sealed +#else +#define FINAL final +#endif + +#else +#define OVERRIDE +#define FINAL +#endif + +/* REFERENCED_FROM_ASM */ + +#ifndef REFERENCED_FROM_ASM +#if COMPILER(GCC) +#define REFERENCED_FROM_ASM __attribute__((used)) +#else +#define REFERENCED_FROM_ASM +#endif +#endif + +/* OBJC_CLASS */ + +#ifndef OBJC_CLASS +#ifdef __OBJC__ +#define OBJC_CLASS @class +#else +#define OBJC_CLASS class +#endif +#endif + +/* ABI */ +#if defined(__ARM_EABI__) || defined(__EABI__) +#define WTF_COMPILER_SUPPORTS_EABI 1 +#endif + +#endif /* WTF_Compiler_h */ diff --git a/src/3rdparty/masm/wtf/CryptographicallyRandomNumber.h b/src/3rdparty/masm/wtf/CryptographicallyRandomNumber.h new file mode 100644 index 0000000000..2262b6c3b3 --- /dev/null +++ b/src/3rdparty/masm/wtf/CryptographicallyRandomNumber.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_CryptographicallyRandomNumber_h +#define WTF_CryptographicallyRandomNumber_h + +#include + +namespace WTF { + +#if USE(OS_RANDOMNESS) +WTF_EXPORT_PRIVATE uint32_t cryptographicallyRandomNumber(); +WTF_EXPORT_PRIVATE void cryptographicallyRandomValues(void* buffer, size_t length); +#endif + +} + +#if USE(OS_RANDOMNESS) +using WTF::cryptographicallyRandomNumber; +using WTF::cryptographicallyRandomValues; +#endif + +#endif diff --git a/src/3rdparty/masm/wtf/DataLog.h b/src/3rdparty/masm/wtf/DataLog.h new file mode 100644 index 0000000000..0bd8efe727 --- /dev/null +++ b/src/3rdparty/masm/wtf/DataLog.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DataLog_h +#define DataLog_h + +#include +#include +#include +#include +#include + +namespace WTF { + +WTF_EXPORT_PRIVATE FilePrintStream& dataFile(); + +WTF_EXPORT_PRIVATE void dataLogFV(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(1, 0); +WTF_EXPORT_PRIVATE void dataLogF(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); +WTF_EXPORT_PRIVATE void dataLogFString(const char*); + +template +void dataLog(const T& value) +{ + dataFile().print(value); +} + +template +void dataLog(const T1& value1, const T2& value2) +{ + dataFile().print(value1, value2); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3) +{ + dataFile().print(value1, value2, value3); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4) +{ + dataFile().print(value1, value2, value3, value4); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5) +{ + dataFile().print(value1, value2, value3, value4, value5); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6) +{ + dataFile().print(value1, value2, value3, value4, value5, value6); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11, value12); +} + +template +void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12, const T13& value13) +{ + dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11, value12, value13); +} + +} // namespace WTF + +using WTF::dataLog; +using WTF::dataLogF; +using WTF::dataLogFString; + +#endif // DataLog_h + diff --git a/src/3rdparty/masm/wtf/DynamicAnnotations.h b/src/3rdparty/masm/wtf/DynamicAnnotations.h new file mode 100644 index 0000000000..38acce35e6 --- /dev/null +++ b/src/3rdparty/masm/wtf/DynamicAnnotations.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_DynamicAnnotations_h +#define WTF_DynamicAnnotations_h + +/* This file defines dynamic annotations for use with dynamic analysis + * tool such as ThreadSanitizer, Valgrind, etc. + * + * Dynamic annotation is a source code annotation that affects + * the generated code (that is, the annotation is not a comment). + * Each such annotation is attached to a particular + * instruction and/or to a particular object (address) in the program. + * + * By using dynamic annotations a developer can give more details to the dynamic + * analysis tool to improve its precision. + * + * In C/C++ program the annotations are represented as C macros. + * With the default build flags, these macros are empty, hence don't affect + * performance of a compiled binary. + * If dynamic annotations are enabled, they just call no-op functions. + * The dynamic analysis tools can intercept these functions and replace them + * with their own implementations. + * + * See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations for more information. + */ + +#if USE(DYNAMIC_ANNOTATIONS) +/* Tell data race detector that we're not interested in reports on the given address range. */ +#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) WTFAnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description) +#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) WTFAnnotateBenignRaceSized(__FILE__, __LINE__, pointer, sizeof(*(pointer)), description) + +/* Annotations for user-defined synchronization mechanisms. + * These annotations can be used to define happens-before arcs in user-defined + * synchronization mechanisms: the race detector will infer an arc from + * the former to the latter when they share the same argument pointer. + * + * The most common case requiring annotations is atomic reference counting: + * bool deref() { + * ANNOTATE_HAPPENS_BEFORE(&m_refCount); + * if (!atomicDecrement(&m_refCount)) { + * // m_refCount is now 0 + * ANNOTATE_HAPPENS_AFTER(&m_refCount); + * // "return true; happens-after each atomicDecrement of m_refCount" + * return true; + * } + * return false; + * } + */ +#define WTF_ANNOTATE_HAPPENS_BEFORE(address) WTFAnnotateHappensBefore(__FILE__, __LINE__, address) +#define WTF_ANNOTATE_HAPPENS_AFTER(address) WTFAnnotateHappensAfter(__FILE__, __LINE__, address) + +#ifdef __cplusplus +extern "C" { +#endif +/* Don't use these directly, use the above macros instead. */ +void WTFAnnotateBenignRaceSized(const char* file, int line, const volatile void* memory, long size, const char* description); +void WTFAnnotateHappensBefore(const char* file, int line, const volatile void* address); +void WTFAnnotateHappensAfter(const char* file, int line, const volatile void* address); +#ifdef __cplusplus +} // extern "C" +#endif + +#else // USE(DYNAMIC_ANNOTATIONS) +/* These macros are empty when dynamic annotations are not enabled so you can + * use them without affecting the performance of release binaries. */ +#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) +#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) +#define WTF_ANNOTATE_HAPPENS_BEFORE(address) +#define WTF_ANNOTATE_HAPPENS_AFTER(address) +#endif // USE(DYNAMIC_ANNOTATIONS) + +#endif // WTF_DynamicAnnotations_h diff --git a/src/3rdparty/masm/wtf/FilePrintStream.cpp b/src/3rdparty/masm/wtf/FilePrintStream.cpp new file mode 100644 index 0000000000..b5ab25e0bf --- /dev/null +++ b/src/3rdparty/masm/wtf/FilePrintStream.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FilePrintStream.h" + +namespace WTF { + +FilePrintStream::FilePrintStream(FILE* file, AdoptionMode adoptionMode) + : m_file(file) + , m_adoptionMode(adoptionMode) +{ +} + +FilePrintStream::~FilePrintStream() +{ + if (m_adoptionMode == Borrow) + return; + fclose(m_file); +} + +PassOwnPtr FilePrintStream::open(const char* filename, const char* mode) +{ + FILE* file = fopen(filename, mode); + if (!file) + return PassOwnPtr(); + + return adoptPtr(new FilePrintStream(file)); +} + +void FilePrintStream::vprintf(const char* format, va_list argList) +{ + vfprintf(m_file, format, argList); +} + +void FilePrintStream::flush() +{ + fflush(m_file); +} + +} // namespace WTF + diff --git a/src/3rdparty/masm/wtf/FilePrintStream.h b/src/3rdparty/masm/wtf/FilePrintStream.h new file mode 100644 index 0000000000..bdeab4c479 --- /dev/null +++ b/src/3rdparty/masm/wtf/FilePrintStream.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FilePrintStream_h +#define FilePrintStream_h + +#include +#include +#include + +namespace WTF { + +class FilePrintStream : public PrintStream { +public: + enum AdoptionMode { + Adopt, + Borrow + }; + + FilePrintStream(FILE*, AdoptionMode = Adopt); + virtual ~FilePrintStream(); + + static PassOwnPtr open(const char* filename, const char* mode); + + FILE* file() { return m_file; } + + void vprintf(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(2, 0); + void flush(); + +private: + FILE* m_file; + AdoptionMode m_adoptionMode; +}; + +} // namespace WTF + +using WTF::FilePrintStream; + +#endif // FilePrintStream_h + diff --git a/src/3rdparty/masm/wtf/Locker.h b/src/3rdparty/masm/wtf/Locker.h new file mode 100644 index 0000000000..c465b99ea4 --- /dev/null +++ b/src/3rdparty/masm/wtf/Locker.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef Locker_h +#define Locker_h + +#include + +namespace WTF { + +template class Locker { + WTF_MAKE_NONCOPYABLE(Locker); +public: + Locker(T& lockable) : m_lockable(lockable) { m_lockable.lock(); } + ~Locker() { m_lockable.unlock(); } +private: + T& m_lockable; +}; + +} + +using WTF::Locker; + +#endif diff --git a/src/3rdparty/masm/wtf/NotFound.h b/src/3rdparty/masm/wtf/NotFound.h new file mode 100644 index 0000000000..4263bcecab --- /dev/null +++ b/src/3rdparty/masm/wtf/NotFound.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NotFound_h +#define NotFound_h + +namespace WTF { + + const size_t notFound = static_cast(-1); + +} // namespace WTF + +using WTF::notFound; + +#endif // NotFound_h diff --git a/src/3rdparty/masm/wtf/NullPtr.h b/src/3rdparty/masm/wtf/NullPtr.h new file mode 100644 index 0000000000..98c05140d8 --- /dev/null +++ b/src/3rdparty/masm/wtf/NullPtr.h @@ -0,0 +1,56 @@ +/* + +Copyright (C) 2010 Apple Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef NullPtr_h +#define NullPtr_h + +// For compilers and standard libraries that do not yet include it, this adds the +// nullptr_t type and nullptr object. They are defined in the same namespaces they +// would be in compiler and library that had the support. + +#include + +#if COMPILER_SUPPORTS(CXX_NULLPTR) || defined(_LIBCPP_VERSION) + +#include + +// libstdc++ supports nullptr_t starting with gcc 4.6. +#if defined(__GLIBCXX__) && __GLIBCXX__ < 20110325 +namespace std { +typedef decltype(nullptr) nullptr_t; +} +#endif + +#else + +namespace std { +class WTF_EXPORT_PRIVATE nullptr_t { }; +} +extern WTF_EXPORT_PRIVATE std::nullptr_t nullptr; + +#endif + +#endif diff --git a/src/3rdparty/masm/wtf/OSAllocator.h b/src/3rdparty/masm/wtf/OSAllocator.h new file mode 100644 index 0000000000..a12a467497 --- /dev/null +++ b/src/3rdparty/masm/wtf/OSAllocator.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OSAllocator_h +#define OSAllocator_h + +#include +#include +#include + +namespace WTF { + +class OSAllocator { +public: + enum Usage { + UnknownUsage = -1, + FastMallocPages = VM_TAG_FOR_TCMALLOC_MEMORY, + JSGCHeapPages = VM_TAG_FOR_COLLECTOR_MEMORY, + JSVMStackPages = VM_TAG_FOR_REGISTERFILE_MEMORY, + JSJITCodePages = VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY, + }; + + // These methods are symmetric; reserveUncommitted allocates VM in an uncommitted state, + // releaseDecommitted should be called on a region of VM allocated by a single reservation, + // the memory must all currently be in a decommitted state. + static void* reserveUncommitted(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool includesGuardPages = false); + WTF_EXPORT_PRIVATE static void releaseDecommitted(void*, size_t); + + // These methods are symmetric; they commit or decommit a region of VM (uncommitted VM should + // never be accessed, since the OS may not have attached physical memory for these regions). + // Clients should only call commit on uncommitted regions and decommit on committed regions. + static void commit(void*, size_t, bool writable, bool executable); + static void decommit(void*, size_t); + + // These methods are symmetric; reserveAndCommit allocates VM in an committed state, + // decommitAndRelease should be called on a region of VM allocated by a single reservation, + // the memory must all currently be in a committed state. + WTF_EXPORT_PRIVATE static void* reserveAndCommit(size_t, Usage = UnknownUsage, bool writable = true, bool executable = false, bool includesGuardPages = false); + static void decommitAndRelease(void* base, size_t size); + + // These methods are akin to reserveAndCommit/decommitAndRelease, above - however rather than + // committing/decommitting the entire region additional parameters allow a subregion to be + // specified. + static void* reserveAndCommit(size_t reserveSize, size_t commitSize, Usage = UnknownUsage, bool writable = true, bool executable = false); + static void decommitAndRelease(void* releaseBase, size_t releaseSize, void* decommitBase, size_t decommitSize); + + // Reallocate an existing, committed allocation. + // The prior allocation must be fully comitted, and the new size will also be fully committed. + // This interface is provided since it may be possible to optimize this operation on some platforms. + template + static T* reallocateCommitted(T*, size_t oldSize, size_t newSize, Usage = UnknownUsage, bool writable = true, bool executable = false); +}; + +inline void* OSAllocator::reserveAndCommit(size_t reserveSize, size_t commitSize, Usage usage, bool writable, bool executable) +{ + void* base = reserveUncommitted(reserveSize, usage, writable, executable); + commit(base, commitSize, writable, executable); + return base; +} + +inline void OSAllocator::decommitAndRelease(void* releaseBase, size_t releaseSize, void* decommitBase, size_t decommitSize) +{ + ASSERT(decommitBase >= releaseBase && (static_cast(decommitBase) + decommitSize) <= (static_cast(releaseBase) + releaseSize)); +#if OS(WINCE) + // On most platforms we can actually skip this final decommit; releasing the VM will + // implicitly decommit any physical memory in the region. This is not true on WINCE. + decommit(decommitBase, decommitSize); +#else + UNUSED_PARAM(decommitBase); + UNUSED_PARAM(decommitSize); +#endif + releaseDecommitted(releaseBase, releaseSize); +} + +inline void OSAllocator::decommitAndRelease(void* base, size_t size) +{ + decommitAndRelease(base, size, base, size); +} + +template +inline T* OSAllocator::reallocateCommitted(T* oldBase, size_t oldSize, size_t newSize, Usage usage, bool writable, bool executable) +{ + void* newBase = reserveAndCommit(newSize, usage, writable, executable); + memcpy(newBase, oldBase, std::min(oldSize, newSize)); + decommitAndRelease(oldBase, oldSize); + return static_cast(newBase); +} + +} // namespace WTF + +using WTF::OSAllocator; + +#endif // OSAllocator_h diff --git a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp new file mode 100644 index 0000000000..b5b903b8a3 --- /dev/null +++ b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "OSAllocator.h" + +#include "PageAllocation.h" +#include +#include +#include +#include + +namespace WTF { + +void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) +{ +#if OS(QNX) + // Reserve memory with PROT_NONE and MAP_LAZY so it isn't committed now. + void* result = mmap(0, bytes, PROT_NONE, MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); + if (result == MAP_FAILED) + CRASH(); +#elif OS(LINUX) + void* result = mmap(0, bytes, PROT_NONE, MAP_NORESERVE | MAP_PRIVATE | MAP_ANON, -1, 0); + if (result == MAP_FAILED) + CRASH(); + madvise(result, bytes, MADV_DONTNEED); +#else + void* result = reserveAndCommit(bytes, usage, writable, executable, includesGuardPages); +#if HAVE(MADV_FREE_REUSE) + // To support the "reserve then commit" model, we have to initially decommit. + while (madvise(result, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { } +#endif + +#endif // OS(QNX) + + return result; +} + +void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) +{ + // All POSIX reservations start out logically committed. + int protection = PROT_READ; + if (writable) + protection |= PROT_WRITE; + if (executable) + protection |= PROT_EXEC; + + int flags = MAP_PRIVATE | MAP_ANON; +#if PLATFORM(IOS) + if (executable) + flags |= MAP_JIT; +#endif + +#if OS(DARWIN) + int fd = usage; +#else + UNUSED_PARAM(usage); + int fd = -1; +#endif + + void* result = 0; +#if (OS(DARWIN) && CPU(X86_64)) + if (executable) { + ASSERT(includesGuardPages); + // Cook up an address to allocate at, using the following recipe: + // 17 bits of zero, stay in userspace kids. + // 26 bits of randomness for ASLR. + // 21 bits of zero, at least stay aligned within one level of the pagetables. + // + // But! - as a temporary workaround for some plugin problems (rdar://problem/6812854), + // for now instead of 2^26 bits of ASLR lets stick with 25 bits of randomization plus + // 2^24, which should put up somewhere in the middle of userspace (in the address range + // 0x200000000000 .. 0x5fffffffffff). + intptr_t randomLocation = 0; + randomLocation = arc4random() & ((1 << 25) - 1); + randomLocation += (1 << 24); + randomLocation <<= 21; + result = reinterpret_cast(randomLocation); + } +#endif + + result = mmap(result, bytes, protection, flags, fd, 0); + if (result == MAP_FAILED) { +#if ENABLE(LLINT) + if (executable) + result = 0; + else +#endif + CRASH(); + } + if (result && includesGuardPages) { + // We use mmap to remap the guardpages rather than using mprotect as + // mprotect results in multiple references to the code region. This + // breaks the madvise based mechanism we use to return physical memory + // to the OS. + mmap(result, pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0); + mmap(static_cast(result) + bytes - pageSize(), pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0); + } + return result; +} + +void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable) +{ +#if OS(QNX) + int protection = PROT_READ; + if (writable) + protection |= PROT_WRITE; + if (executable) + protection |= PROT_EXEC; + if (MAP_FAILED == mmap(address, bytes, protection, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0)) + CRASH(); +#elif OS(LINUX) + int protection = PROT_READ; + if (writable) + protection |= PROT_WRITE; + if (executable) + protection |= PROT_EXEC; + if (mprotect(address, bytes, protection)) + CRASH(); + madvise(address, bytes, MADV_WILLNEED); +#elif HAVE(MADV_FREE_REUSE) + UNUSED_PARAM(writable); + UNUSED_PARAM(executable); + while (madvise(address, bytes, MADV_FREE_REUSE) == -1 && errno == EAGAIN) { } +#else + // Non-MADV_FREE_REUSE reservations automatically commit on demand. + UNUSED_PARAM(address); + UNUSED_PARAM(bytes); + UNUSED_PARAM(writable); + UNUSED_PARAM(executable); +#endif +} + +void OSAllocator::decommit(void* address, size_t bytes) +{ +#if OS(QNX) + // Use PROT_NONE and MAP_LAZY to decommit the pages. + mmap(address, bytes, PROT_NONE, MAP_FIXED | MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); +#elif OS(LINUX) + madvise(address, bytes, MADV_DONTNEED); + if (mprotect(address, bytes, PROT_NONE)) + CRASH(); +#elif HAVE(MADV_FREE_REUSE) + while (madvise(address, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { } +#elif HAVE(MADV_FREE) + while (madvise(address, bytes, MADV_FREE) == -1 && errno == EAGAIN) { } +#elif HAVE(MADV_DONTNEED) + while (madvise(address, bytes, MADV_DONTNEED) == -1 && errno == EAGAIN) { } +#else + UNUSED_PARAM(address); + UNUSED_PARAM(bytes); +#endif +} + +void OSAllocator::releaseDecommitted(void* address, size_t bytes) +{ + int result = munmap(address, bytes); + if (result == -1) + CRASH(); +} + +} // namespace WTF diff --git a/src/3rdparty/masm/wtf/OSAllocatorWin.cpp b/src/3rdparty/masm/wtf/OSAllocatorWin.cpp new file mode 100644 index 0000000000..7f5d9b8904 --- /dev/null +++ b/src/3rdparty/masm/wtf/OSAllocatorWin.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "OSAllocator.h" + +#include "windows.h" +#include + +namespace WTF { + +static inline DWORD protection(bool writable, bool executable) +{ + return executable ? + (writable ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ) : + (writable ? PAGE_READWRITE : PAGE_READONLY); +} + +void* OSAllocator::reserveUncommitted(size_t bytes, Usage, bool writable, bool executable, bool) +{ + void* result = VirtualAlloc(0, bytes, MEM_RESERVE, protection(writable, executable)); + if (!result) + CRASH(); + return result; +} + +void* OSAllocator::reserveAndCommit(size_t bytes, Usage, bool writable, bool executable, bool) +{ + void* result = VirtualAlloc(0, bytes, MEM_RESERVE | MEM_COMMIT, protection(writable, executable)); + if (!result) + CRASH(); + return result; +} + +void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable) +{ + void* result = VirtualAlloc(address, bytes, MEM_COMMIT, protection(writable, executable)); + if (!result) + CRASH(); +} + +void OSAllocator::decommit(void* address, size_t bytes) +{ + bool result = VirtualFree(address, bytes, MEM_DECOMMIT); + if (!result) + CRASH(); +} + +void OSAllocator::releaseDecommitted(void* address, size_t bytes) +{ + // According to http://msdn.microsoft.com/en-us/library/aa366892(VS.85).aspx, + // dwSize must be 0 if dwFreeType is MEM_RELEASE. + bool result = VirtualFree(address, 0, MEM_RELEASE); + if (!result) + CRASH(); +} + +} // namespace WTF diff --git a/src/3rdparty/masm/wtf/PageAllocation.h b/src/3rdparty/masm/wtf/PageAllocation.h new file mode 100644 index 0000000000..18d31880c0 --- /dev/null +++ b/src/3rdparty/masm/wtf/PageAllocation.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PageAllocation_h +#define PageAllocation_h + +#include +#include +#include +#include +#include +#include + +#if OS(DARWIN) +#include +#include +#endif + +#if OS(WINDOWS) +#include +#include +#endif + +#if HAVE(ERRNO_H) +#include +#endif + +#if HAVE(MMAP) +#include +#include +#endif + +namespace WTF { + +/* + PageAllocation + + The PageAllocation class provides a cross-platform memory allocation interface + with similar capabilities to posix mmap/munmap. Memory is allocated by calling + PageAllocation::allocate, and deallocated by calling deallocate on the + PageAllocation object. The PageAllocation holds the allocation's base pointer + and size. + + The allocate method is passed the size required (which must be a multiple of + the system page size, which can be accessed using PageAllocation::pageSize). + Callers may also optinally provide a flag indicating the usage (for use by + system memory usage tracking tools, where implemented), and boolean values + specifying the required protection (defaulting to writable, non-executable). +*/ + +class PageAllocation : private PageBlock { +public: + PageAllocation() + { + } + + using PageBlock::size; + using PageBlock::base; + +#ifndef __clang__ + using PageBlock::operator bool; +#else + // FIXME: This is a workaround for , wherein Clang incorrectly emits an access + // control warning when a client tries to use operator bool exposed above via "using PageBlock::operator bool". + operator bool() const { return PageBlock::operator bool(); } +#endif + + static PageAllocation allocate(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) + { + ASSERT(isPageAligned(size)); + return PageAllocation(OSAllocator::reserveAndCommit(size, usage, writable, executable), size); + } + + void deallocate() + { + // Clear base & size before calling release; if this is *inside* allocation + // then we won't be able to clear then after deallocating the memory. + PageAllocation tmp; + std::swap(tmp, *this); + + ASSERT(tmp); + ASSERT(!*this); + + OSAllocator::decommitAndRelease(tmp.base(), tmp.size()); + } + +private: + PageAllocation(void* base, size_t size) + : PageBlock(base, size, false) + { + } +}; + +} // namespace WTF + +using WTF::PageAllocation; + +#endif // PageAllocation_h diff --git a/src/3rdparty/masm/wtf/PageAllocationAligned.cpp b/src/3rdparty/masm/wtf/PageAllocationAligned.cpp new file mode 100644 index 0000000000..6f54710d0b --- /dev/null +++ b/src/3rdparty/masm/wtf/PageAllocationAligned.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PageAllocationAligned.h" + +namespace WTF { + +PageAllocationAligned PageAllocationAligned::allocate(size_t size, size_t alignment, OSAllocator::Usage usage, bool writable, bool executable) +{ + ASSERT(isPageAligned(size)); + ASSERT(isPageAligned(alignment)); + ASSERT(isPowerOfTwo(alignment)); + ASSERT(size >= alignment); + size_t alignmentMask = alignment - 1; + +#if OS(DARWIN) + int flags = VM_FLAGS_ANYWHERE; + if (usage != OSAllocator::UnknownUsage) + flags |= usage; + int protection = PROT_READ; + if (writable) + protection |= PROT_WRITE; + if (executable) + protection |= PROT_EXEC; + + vm_address_t address = 0; + vm_map(current_task(), &address, size, alignmentMask, flags, MEMORY_OBJECT_NULL, 0, FALSE, protection, PROT_READ | PROT_WRITE | PROT_EXEC, VM_INHERIT_DEFAULT); + return PageAllocationAligned(reinterpret_cast(address), size); +#else + size_t alignmentDelta = alignment - pageSize(); + + // Resererve with suffcient additional VM to correctly align. + size_t reservationSize = size + alignmentDelta; + void* reservationBase = OSAllocator::reserveUncommitted(reservationSize, usage, writable, executable); + + // Select an aligned region within the reservation and commit. + void* alignedBase = reinterpret_cast(reservationBase) & alignmentMask + ? reinterpret_cast((reinterpret_cast(reservationBase) & ~alignmentMask) + alignment) + : reservationBase; + OSAllocator::commit(alignedBase, size, writable, executable); + + return PageAllocationAligned(alignedBase, size, reservationBase, reservationSize); +#endif +} + +void PageAllocationAligned::deallocate() +{ + // Clear base & size before calling release; if this is *inside* allocation + // then we won't be able to clear then after deallocating the memory. + PageAllocationAligned tmp; + std::swap(tmp, *this); + + ASSERT(tmp); + ASSERT(!*this); + +#if OS(DARWIN) + vm_deallocate(current_task(), reinterpret_cast(tmp.base()), tmp.size()); +#else + ASSERT(tmp.m_reservation.contains(tmp.base(), tmp.size())); + OSAllocator::decommitAndRelease(tmp.m_reservation.base(), tmp.m_reservation.size(), tmp.base(), tmp.size()); +#endif +} + +} // namespace WTF diff --git a/src/3rdparty/masm/wtf/PageAllocationAligned.h b/src/3rdparty/masm/wtf/PageAllocationAligned.h new file mode 100644 index 0000000000..c018dabd8e --- /dev/null +++ b/src/3rdparty/masm/wtf/PageAllocationAligned.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PageAllocationAligned_h +#define PageAllocationAligned_h + +#include +#include + +namespace WTF { + +class PageAllocationAligned : private PageBlock { +public: + PageAllocationAligned() + { + } + + using PageBlock::operator bool; + using PageBlock::size; + using PageBlock::base; + + static PageAllocationAligned allocate(size_t size, size_t alignment, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false); + + void deallocate(); + +private: +#if OS(DARWIN) + PageAllocationAligned(void* base, size_t size) + : PageBlock(base, size, false) + { + } +#else + PageAllocationAligned(void* base, size_t size, void* reservationBase, size_t reservationSize) + : PageBlock(base, size, false) + , m_reservation(reservationBase, reservationSize, false) + { + } + + PageBlock m_reservation; +#endif +}; + + +} // namespace WTF + +using WTF::PageAllocationAligned; + +#endif // PageAllocationAligned_h diff --git a/src/3rdparty/masm/wtf/PageBlock.cpp b/src/3rdparty/masm/wtf/PageBlock.cpp new file mode 100644 index 0000000000..8bbd7eb600 --- /dev/null +++ b/src/3rdparty/masm/wtf/PageBlock.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PageBlock.h" + +#if OS(UNIX) +#include +#endif + +#if OS(WINDOWS) +#include +#include +#endif + +namespace WTF { + +static size_t s_pageSize; +static size_t s_pageMask; + +#if OS(UNIX) + +inline size_t systemPageSize() +{ + return getpagesize(); +} + +#elif OS(WINDOWS) + +inline size_t systemPageSize() +{ + static size_t size = 0; + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + size = system_info.dwPageSize; + return size; +} + +#endif + +size_t pageSize() +{ + if (!s_pageSize) + s_pageSize = systemPageSize(); + ASSERT(isPowerOfTwo(s_pageSize)); + return s_pageSize; +} + +size_t pageMask() +{ + if (!s_pageMask) + s_pageMask = ~(pageSize() - 1); + return s_pageMask; +} + +} // namespace WTF diff --git a/src/3rdparty/masm/wtf/PageBlock.h b/src/3rdparty/masm/wtf/PageBlock.h new file mode 100644 index 0000000000..56e5570178 --- /dev/null +++ b/src/3rdparty/masm/wtf/PageBlock.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PageBlock_h +#define PageBlock_h + +namespace WTF { + +WTF_EXPORT_PRIVATE size_t pageSize(); +WTF_EXPORT_PRIVATE size_t pageMask(); +inline bool isPageAligned(void* address) { return !(reinterpret_cast(address) & (pageSize() - 1)); } +inline bool isPageAligned(size_t size) { return !(size & (pageSize() - 1)); } +inline bool isPowerOfTwo(size_t size) { return !(size & (size - 1)); } + +class PageBlock { +public: + PageBlock(); + PageBlock(const PageBlock&); + PageBlock(void*, size_t, bool hasGuardPages); + + void* base() const { return m_base; } + size_t size() const { return m_size; } + + operator bool() const { return !!m_realBase; } + + bool contains(void* containedBase, size_t containedSize) + { + return containedBase >= m_base + && (static_cast(containedBase) + containedSize) <= (static_cast(m_base) + m_size); + } + +private: + void* m_realBase; + void* m_base; + size_t m_size; +}; + +inline PageBlock::PageBlock() + : m_realBase(0) + , m_base(0) + , m_size(0) +{ +} + +inline PageBlock::PageBlock(const PageBlock& other) + : m_realBase(other.m_realBase) + , m_base(other.m_base) + , m_size(other.m_size) +{ +} + +inline PageBlock::PageBlock(void* base, size_t size, bool hasGuardPages) + : m_realBase(base) + , m_base(static_cast(base) + ((base && hasGuardPages) ? pageSize() : 0)) + , m_size(size) +{ +} + +} // namespace WTF + +using WTF::pageSize; +using WTF::isPageAligned; +using WTF::isPageAligned; +using WTF::isPowerOfTwo; + +#endif // PageBlock_h diff --git a/src/3rdparty/masm/wtf/PageReservation.h b/src/3rdparty/masm/wtf/PageReservation.h new file mode 100644 index 0000000000..77783ebcc4 --- /dev/null +++ b/src/3rdparty/masm/wtf/PageReservation.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PageReservation_h +#define PageReservation_h + +#include + +namespace WTF { + +/* + PageReservation + + Like PageAllocation, the PageReservation class provides a cross-platform memory + allocation interface, but with a set of capabilities more similar to that of + VirtualAlloc than posix mmap. PageReservation can be used to allocate virtual + memory without committing physical memory pages using PageReservation::reserve. + Following a call to reserve all memory in the region is in a decommited state, + in which the memory should not be used (accessing the memory may cause a fault). + + Before using memory it must be committed by calling commit, which is passed start + and size values (both of which require system page size granularity). One the + committed memory is no longer needed 'decommit' may be called to return the + memory to its devommitted state. Commit should only be called on memory that is + currently decommitted, and decommit should only be called on memory regions that + are currently committed. All memory should be decommited before the reservation + is deallocated. Values in memory may not be retained accross a pair of calls if + the region of memory is decommitted and then committed again. + + Memory protection should not be changed on decommitted memory, and if protection + is changed on memory while it is committed it should be returned to the orignal + protection before decommit is called. +*/ + +class PageReservation : private PageBlock { +public: + PageReservation() + : m_committed(0) + , m_writable(false) + , m_executable(false) + { + } + + using PageBlock::base; + using PageBlock::size; + +#ifndef __clang__ + using PageBlock::operator bool; +#else + // FIXME: This is a workaround for , wherein Clang incorrectly emits an access + // control warning when a client tries to use operator bool exposed above via "using PageBlock::operator bool". + operator bool() const { return PageBlock::operator bool(); } +#endif + + void commit(void* start, size_t size) + { + ASSERT(*this); + ASSERT(isPageAligned(start)); + ASSERT(isPageAligned(size)); + ASSERT(contains(start, size)); + + m_committed += size; + OSAllocator::commit(start, size, m_writable, m_executable); + } + + void decommit(void* start, size_t size) + { + ASSERT(*this); + ASSERT(isPageAligned(start)); + ASSERT(isPageAligned(size)); + ASSERT(contains(start, size)); + + m_committed -= size; + OSAllocator::decommit(start, size); + } + + size_t committed() + { + return m_committed; + } + + static PageReservation reserve(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) + { + ASSERT(isPageAligned(size)); + return PageReservation(OSAllocator::reserveUncommitted(size, usage, writable, executable), size, writable, executable, false); + } + + static PageReservation reserveWithGuardPages(size_t size, OSAllocator::Usage usage = OSAllocator::UnknownUsage, bool writable = true, bool executable = false) + { + ASSERT(isPageAligned(size)); + return PageReservation(OSAllocator::reserveUncommitted(size + pageSize() * 2, usage, writable, executable, true), size, writable, executable, true); + } + + void deallocate() + { + ASSERT(!m_committed); + + // Clear base & size before calling release; if this is *inside* allocation + // then we won't be able to clear then after deallocating the memory. + PageReservation tmp; + std::swap(tmp, *this); + + ASSERT(tmp); + ASSERT(!*this); + + OSAllocator::releaseDecommitted(tmp.base(), tmp.size()); + } + +private: + PageReservation(void* base, size_t size, bool writable, bool executable, bool hasGuardPages) + : PageBlock(base, size, hasGuardPages) + , m_committed(0) + , m_writable(writable) + , m_executable(executable) + { + } + + size_t m_committed; + bool m_writable; + bool m_executable; +}; + +} + +using WTF::PageReservation; + +#endif // PageReservation_h diff --git a/src/3rdparty/masm/wtf/Platform.h b/src/3rdparty/masm/wtf/Platform.h new file mode 100644 index 0000000000..f2fd3b0ad5 --- /dev/null +++ b/src/3rdparty/masm/wtf/Platform.h @@ -0,0 +1,1212 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_Platform_h +#define WTF_Platform_h + +/* Include compiler specific macros */ +#include + +/* ==== PLATFORM handles OS, operating environment, graphics API, and + CPU. This macro will be phased out in favor of platform adaptation + macros, policy decision macros, and top-level port definitions. ==== */ +#define PLATFORM(WTF_FEATURE) (defined WTF_PLATFORM_##WTF_FEATURE && WTF_PLATFORM_##WTF_FEATURE) + + +/* ==== Platform adaptation macros: these describe properties of the target environment. ==== */ + +/* CPU() - the target CPU architecture */ +#define CPU(WTF_FEATURE) (defined WTF_CPU_##WTF_FEATURE && WTF_CPU_##WTF_FEATURE) +/* HAVE() - specific system features (headers, functions or similar) that are present or not */ +#define HAVE(WTF_FEATURE) (defined HAVE_##WTF_FEATURE && HAVE_##WTF_FEATURE) +/* OS() - underlying operating system; only to be used for mandated low-level services like + virtual memory, not to choose a GUI toolkit */ +#define OS(WTF_FEATURE) (defined WTF_OS_##WTF_FEATURE && WTF_OS_##WTF_FEATURE) + + +/* ==== Policy decision macros: these define policy choices for a particular port. ==== */ + +/* USE() - use a particular third-party library or optional OS service */ +#define USE(WTF_FEATURE) (defined WTF_USE_##WTF_FEATURE && WTF_USE_##WTF_FEATURE) +/* ENABLE() - turn on a specific feature of WebKit */ +#define ENABLE(WTF_FEATURE) (defined ENABLE_##WTF_FEATURE && ENABLE_##WTF_FEATURE) + + +/* ==== CPU() - the target CPU architecture ==== */ + +/* This also defines CPU(BIG_ENDIAN) or CPU(MIDDLE_ENDIAN) or neither, as appropriate. */ + +/* CPU(ALPHA) - DEC Alpha */ +#if defined(__alpha__) +#define WTF_CPU_ALPHA 1 +#endif + +/* CPU(IA64) - Itanium / IA-64 */ +#if defined(__ia64__) +#define WTF_CPU_IA64 1 +/* 32-bit mode on Itanium */ +#if !defined(__LP64__) +#define WTF_CPU_IA64_32 1 +#endif +#endif + +/* CPU(MIPS) - MIPS 32-bit */ +/* Note: Only O32 ABI is tested, so we enable it for O32 ABI for now. */ +#if (defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_)) \ + && defined(_ABIO32) +#define WTF_CPU_MIPS 1 +#if defined(__MIPSEB__) +#define WTF_CPU_BIG_ENDIAN 1 +#endif +#define WTF_MIPS_PIC (defined __PIC__) +#define WTF_MIPS_ARCH __mips +#define WTF_MIPS_ISA(v) (defined WTF_MIPS_ARCH && WTF_MIPS_ARCH == v) +#define WTF_MIPS_ISA_AT_LEAST(v) (defined WTF_MIPS_ARCH && WTF_MIPS_ARCH >= v) +#define WTF_MIPS_ARCH_REV __mips_isa_rev +#define WTF_MIPS_ISA_REV(v) (defined WTF_MIPS_ARCH_REV && WTF_MIPS_ARCH_REV == v) +#define WTF_MIPS_DOUBLE_FLOAT (defined __mips_hard_float && !defined __mips_single_float) +#define WTF_MIPS_FP64 (defined __mips_fpr && __mips_fpr == 64) +/* MIPS requires allocators to use aligned memory */ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 +#endif /* MIPS */ + +/* CPU(PPC) - PowerPC 32-bit */ +#if defined(__ppc__) \ + || defined(__PPC__) \ + || defined(__powerpc__) \ + || defined(__powerpc) \ + || defined(__POWERPC__) \ + || defined(_M_PPC) \ + || defined(__PPC) +#define WTF_CPU_PPC 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(PPC64) - PowerPC 64-bit */ +#if defined(__ppc64__) \ + || defined(__PPC64__) +#define WTF_CPU_PPC64 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(SH4) - SuperH SH-4 */ +#if defined(__SH4__) +#define WTF_CPU_SH4 1 +#endif + +/* CPU(SPARC32) - SPARC 32-bit */ +#if defined(__sparc) && !defined(__arch64__) || defined(__sparcv8) +#define WTF_CPU_SPARC32 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(SPARC64) - SPARC 64-bit */ +#if defined(__sparc__) && defined(__arch64__) || defined (__sparcv9) +#define WTF_CPU_SPARC64 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(SPARC) - any SPARC, true for CPU(SPARC32) and CPU(SPARC64) */ +#if CPU(SPARC32) || CPU(SPARC64) +#define WTF_CPU_SPARC 1 +#endif + +/* CPU(S390X) - S390 64-bit */ +#if defined(__s390x__) +#define WTF_CPU_S390X 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(S390) - S390 32-bit */ +#if defined(__s390__) +#define WTF_CPU_S390 1 +#define WTF_CPU_BIG_ENDIAN 1 +#endif + +/* CPU(X86) - i386 / x86 32-bit */ +#if defined(__i386__) \ + || defined(i386) \ + || defined(_M_IX86) \ + || defined(_X86_) \ + || defined(__THW_INTEL) +#define WTF_CPU_X86 1 +#endif + +/* CPU(X86_64) - AMD64 / Intel64 / x86_64 64-bit */ +#if defined(__x86_64__) \ + || defined(_M_X64) +#define WTF_CPU_X86_64 1 +#endif + +/* CPU(ARM) - ARM, any version*/ +#if defined(arm) \ + || defined(__arm__) \ + || defined(ARM) \ + || defined(_ARM_) +#define WTF_CPU_ARM 1 + +#if defined(__ARM_PCS_VFP) +#define WTF_CPU_ARM_HARDFP 1 +#endif + +#if defined(__ARMEB__) || (COMPILER(RVCT) && defined(__BIG_ENDIAN)) +#define WTF_CPU_BIG_ENDIAN 1 + +#elif !defined(__ARM_EABI__) \ + && !defined(__EABI__) \ + && !defined(__VFP_FP__) \ + && !defined(_WIN32_WCE) \ + && !defined(ANDROID) +#define WTF_CPU_MIDDLE_ENDIAN 1 + +#endif + +#define WTF_ARM_ARCH_AT_LEAST(N) (CPU(ARM) && WTF_ARM_ARCH_VERSION >= N) + +/* Set WTF_ARM_ARCH_VERSION */ +#if defined(__ARM_ARCH_4__) \ + || defined(__ARM_ARCH_4T__) \ + || defined(__MARM_ARMV4__) \ + || defined(_ARMV4I_) +#define WTF_ARM_ARCH_VERSION 4 + +#elif defined(__ARM_ARCH_5__) \ + || defined(__ARM_ARCH_5T__) \ + || defined(__MARM_ARMV5__) +#define WTF_ARM_ARCH_VERSION 5 + +#elif defined(__ARM_ARCH_5E__) \ + || defined(__ARM_ARCH_5TE__) \ + || defined(__ARM_ARCH_5TEJ__) +#define WTF_ARM_ARCH_VERSION 5 +/*ARMv5TE requires allocators to use aligned memory*/ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 + +#elif defined(__ARM_ARCH_6__) \ + || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6T2__) \ + || defined(__ARMV6__) +#define WTF_ARM_ARCH_VERSION 6 + +#elif defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) +#define WTF_ARM_ARCH_VERSION 7 + +/* RVCT sets _TARGET_ARCH_ARM */ +#elif defined(__TARGET_ARCH_ARM) +#define WTF_ARM_ARCH_VERSION __TARGET_ARCH_ARM + +#if defined(__TARGET_ARCH_5E) \ + || defined(__TARGET_ARCH_5TE) \ + || defined(__TARGET_ARCH_5TEJ) +/*ARMv5TE requires allocators to use aligned memory*/ +#define WTF_USE_ARENA_ALLOC_ALIGNMENT_INTEGER 1 +#endif + +#else +#define WTF_ARM_ARCH_VERSION 0 + +#endif + +/* Set WTF_THUMB_ARCH_VERSION */ +#if defined(__ARM_ARCH_4T__) +#define WTF_THUMB_ARCH_VERSION 1 + +#elif defined(__ARM_ARCH_5T__) \ + || defined(__ARM_ARCH_5TE__) \ + || defined(__ARM_ARCH_5TEJ__) +#define WTF_THUMB_ARCH_VERSION 2 + +#elif defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6M__) +#define WTF_THUMB_ARCH_VERSION 3 + +#elif defined(__ARM_ARCH_6T2__) \ + || defined(__ARM_ARCH_7__) \ + || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) \ + || defined(__ARM_ARCH_7M__) +#define WTF_THUMB_ARCH_VERSION 4 + +/* RVCT sets __TARGET_ARCH_THUMB */ +#elif defined(__TARGET_ARCH_THUMB) +#define WTF_THUMB_ARCH_VERSION __TARGET_ARCH_THUMB + +#else +#define WTF_THUMB_ARCH_VERSION 0 +#endif + + +/* CPU(ARMV5_OR_LOWER) - ARM instruction set v5 or earlier */ +/* On ARMv5 and below the natural alignment is required. + And there are some other differences for v5 or earlier. */ +#if !defined(ARMV5_OR_LOWER) && !WTF_ARM_ARCH_AT_LEAST(6) +#define WTF_CPU_ARMV5_OR_LOWER 1 +#endif + + +/* CPU(ARM_TRADITIONAL) - Thumb2 is not available, only traditional ARM (v4 or greater) */ +/* CPU(ARM_THUMB2) - Thumb2 instruction set is available */ +/* Only one of these will be defined. */ +#if !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) +# if defined(thumb2) || defined(__thumb2__) \ + || ((defined(__thumb) || defined(__thumb__)) && WTF_THUMB_ARCH_VERSION == 4) +# define WTF_CPU_ARM_TRADITIONAL 0 +# define WTF_CPU_ARM_THUMB2 1 +# elif WTF_ARM_ARCH_AT_LEAST(4) +# define WTF_CPU_ARM_TRADITIONAL 1 +# define WTF_CPU_ARM_THUMB2 0 +# else +# error "Not supported ARM architecture" +# endif +#elif CPU(ARM_TRADITIONAL) && CPU(ARM_THUMB2) /* Sanity Check */ +# error "Cannot use both of WTF_CPU_ARM_TRADITIONAL and WTF_CPU_ARM_THUMB2 platforms" +#endif /* !defined(WTF_CPU_ARM_TRADITIONAL) && !defined(WTF_CPU_ARM_THUMB2) */ + +#if defined(__ARM_NEON__) && !defined(WTF_CPU_ARM_NEON) +#define WTF_CPU_ARM_NEON 1 +#endif + +#if CPU(ARM_NEON) && (!COMPILER(GCC) || GCC_VERSION_AT_LEAST(4, 7, 0)) +// All NEON intrinsics usage can be disabled by this macro. +#define HAVE_ARM_NEON_INTRINSICS 1 +#endif + +#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) +#define WTF_CPU_ARM_VFP 1 +#endif + +#if defined(__ARM_ARCH_7S__) +#define WTF_CPU_APPLE_ARMV7S 1 +#endif + +#endif /* ARM */ + +#if CPU(ARM) || CPU(MIPS) || CPU(SH4) || CPU(SPARC) +#define WTF_CPU_NEEDS_ALIGNED_ACCESS 1 +#endif + +/* ==== OS() - underlying operating system; only to be used for mandated low-level services like + virtual memory, not to choose a GUI toolkit ==== */ + +/* OS(ANDROID) - Android */ +#ifdef ANDROID +#define WTF_OS_ANDROID 1 +#endif + +/* OS(AIX) - AIX */ +#ifdef _AIX +#define WTF_OS_AIX 1 +#endif + +/* OS(DARWIN) - Any Darwin-based OS, including Mac OS X and iPhone OS */ +#ifdef __APPLE__ +#define WTF_OS_DARWIN 1 + +#include +#include +#include +#endif + +/* OS(IOS) - iOS */ +/* OS(MAC_OS_X) - Mac OS X (not including iOS) */ +#if OS(DARWIN) && ((defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED) \ + || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) \ + || (defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR)) +#define WTF_OS_IOS 1 +#elif OS(DARWIN) && defined(TARGET_OS_MAC) && TARGET_OS_MAC +#define WTF_OS_MAC_OS_X 1 + +/* FIXME: These can be removed after sufficient time has passed since the removal of BUILDING_ON / TARGETING macros. */ + +#define ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED 0 / 0 +#define ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED 0 / 0 + +#define BUILDING_ON_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED +#define BUILDING_ON_SNOW_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED +#define BUILDING_ON_LION ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MIN_REQUIRED + +#define TARGETING_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED +#define TARGETING_SNOW_LEOPARD ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED +#define TARGETING_LION ERROR_PLEASE_COMPARE_WITH_MAC_OS_X_VERSION_MAX_ALLOWED +#endif + +/* OS(FREEBSD) - FreeBSD */ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +#define WTF_OS_FREEBSD 1 +#endif + +/* OS(HURD) - GNU/Hurd */ +#ifdef __GNU__ +#define WTF_OS_HURD 1 +#endif + +/* OS(LINUX) - Linux */ +#ifdef __linux__ +#define WTF_OS_LINUX 1 +#endif + +/* OS(NETBSD) - NetBSD */ +#if defined(__NetBSD__) +#define WTF_OS_NETBSD 1 +#endif + +/* OS(OPENBSD) - OpenBSD */ +#ifdef __OpenBSD__ +#define WTF_OS_OPENBSD 1 +#endif + +/* OS(QNX) - QNX */ +#if defined(__QNXNTO__) +#define WTF_OS_QNX 1 +#endif + +/* OS(SOLARIS) - Solaris */ +#if defined(sun) || defined(__sun) +#define WTF_OS_SOLARIS 1 +#endif + +/* OS(WINCE) - Windows CE; note that for this platform OS(WINDOWS) is also defined */ +#if defined(_WIN32_WCE) +#define WTF_OS_WINCE 1 +#endif + +/* OS(WINDOWS) - Any version of Windows */ +#if defined(WIN32) || defined(_WIN32) +#define WTF_OS_WINDOWS 1 +#endif + +#define WTF_OS_WIN ERROR "USE WINDOWS WITH OS NOT WIN" +#define WTF_OS_MAC ERROR "USE MAC_OS_X WITH OS NOT MAC" + +/* OS(UNIX) - Any Unix-like system */ +#if OS(AIX) \ + || OS(ANDROID) \ + || OS(DARWIN) \ + || OS(FREEBSD) \ + || OS(HURD) \ + || OS(LINUX) \ + || OS(NETBSD) \ + || OS(OPENBSD) \ + || OS(QNX) \ + || OS(SOLARIS) \ + || defined(unix) \ + || defined(__unix) \ + || defined(__unix__) +#define WTF_OS_UNIX 1 +#endif + +/* Operating environments */ + +/* FIXME: these are all mixes of OS, operating environment and policy choices. */ +/* PLATFORM(CHROMIUM) */ +/* PLATFORM(QT) */ +/* PLATFORM(WX) */ +/* PLATFORM(EFL) */ +/* PLATFORM(GTK) */ +/* PLATFORM(BLACKBERRY) */ +/* PLATFORM(MAC) */ +/* PLATFORM(WIN) */ +#if defined(BUILDING_CHROMIUM__) +#define WTF_PLATFORM_CHROMIUM 1 +#elif defined(BUILDING_QT__) +#define WTF_PLATFORM_QT 1 +#elif defined(BUILDING_WX__) +#define WTF_PLATFORM_WX 1 +#elif defined(BUILDING_EFL__) +#define WTF_PLATFORM_EFL 1 +#elif defined(BUILDING_GTK__) +#define WTF_PLATFORM_GTK 1 +#elif defined(BUILDING_BLACKBERRY__) +#define WTF_PLATFORM_BLACKBERRY 1 +#elif OS(DARWIN) +#define WTF_PLATFORM_MAC 1 +#elif OS(WINDOWS) +#define WTF_PLATFORM_WIN 1 +#endif + +/* PLATFORM(IOS) */ +/* FIXME: this is sometimes used as an OS switch and sometimes for higher-level things */ +#if (defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) +#define WTF_PLATFORM_IOS 1 +#endif + +/* PLATFORM(IOS_SIMULATOR) */ +#if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR +#define WTF_PLATFORM_IOS 1 +#define WTF_PLATFORM_IOS_SIMULATOR 1 +#endif + +/* Graphics engines */ + +/* USE(CG) and PLATFORM(CI) */ +#if PLATFORM(MAC) || PLATFORM(IOS) +#define WTF_USE_CG 1 +#endif +#if PLATFORM(MAC) || PLATFORM(IOS) || (PLATFORM(WIN) && USE(CG)) +#define WTF_USE_CA 1 +#endif + +/* USE(SKIA) for Win/Linux/Mac/Android */ +#if PLATFORM(CHROMIUM) +#if OS(DARWIN) +#define WTF_USE_SKIA 1 +#define WTF_USE_ICCJPEG 1 +#define WTF_USE_QCMSLIB 1 +#elif OS(ANDROID) +#define WTF_USE_SKIA 1 +#define WTF_USE_LOW_QUALITY_IMAGE_INTERPOLATION 1 +#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_DITHERING 1 +#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_FANCY_UPSAMPLING 1 +#else +#define WTF_USE_SKIA 1 +#define WTF_USE_ICCJPEG 1 +#define WTF_USE_QCMSLIB 1 +#endif +#endif + +#if OS(QNX) +#define USE_SYSTEM_MALLOC 1 +#endif + +#if PLATFORM(BLACKBERRY) +#define WTF_USE_MERSENNE_TWISTER_19937 1 +#define WTF_USE_SKIA 1 +#define WTF_USE_LOW_QUALITY_IMAGE_INTERPOLATION 1 +#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_DITHERING 1 +#define WTF_USE_LOW_QUALITY_IMAGE_NO_JPEG_FANCY_UPSAMPLING 1 +#endif + +#if PLATFORM(GTK) +#define WTF_USE_CAIRO 1 +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#endif + + +#if OS(WINCE) +#define WTF_USE_MERSENNE_TWISTER_19937 1 +#endif + +/* On Windows, use QueryPerformanceCounter by default */ +#if OS(WINDOWS) +#define WTF_USE_QUERY_PERFORMANCE_COUNTER 1 +#endif + +#if OS(WINCE) && !PLATFORM(QT) +#define NOSHLWAPI /* shlwapi.h not available on WinCe */ + +/* MSDN documentation says these functions are provided with uspce.lib. But we cannot find this file. */ +#define __usp10__ /* disable "usp10.h" */ + +#define _INC_ASSERT /* disable "assert.h" */ +#define assert(x) + +#endif /* OS(WINCE) && !PLATFORM(QT) */ + +#if OS(WINCE) && !PLATFORM(QT) +#define WTF_USE_WCHAR_UNICODE 1 +#elif PLATFORM(GTK) +/* The GTK+ Unicode backend is configurable */ +#else +#define WTF_USE_ICU_UNICODE 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) +#if CPU(X86_64) +#define WTF_USE_PLUGIN_HOST_PROCESS 1 +#endif +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 +#define ENABLE_GESTURE_EVENTS 1 +#define ENABLE_RUBBER_BANDING 1 +#define WTF_USE_SCROLLBAR_PAINTER 1 +#define HAVE_XPC 1 +#endif +#if !defined(ENABLE_DASHBOARD_SUPPORT) +#define ENABLE_DASHBOARD_SUPPORT 1 +#endif +#define WTF_USE_CF 1 +#define WTF_USE_PTHREADS 1 +#define HAVE_READLINE 1 +#define HAVE_RUNLOOP_TIMER 1 +#define ENABLE_FULLSCREEN_API 1 +#define ENABLE_SMOOTH_SCROLLING 1 +#define ENABLE_WEB_ARCHIVE 1 +#define ENABLE_WEB_AUDIO 1 +#if defined(ENABLE_VIDEO) +#define ENABLE_VIDEO_TRACK 1 +#endif +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 +#define HAVE_LAYER_HOSTING_IN_WINDOW_SERVER 1 +#endif +#define WTF_USE_APPKIT 1 +#define WTF_USE_SECURITY_FRAMEWORK 1 +#endif /* PLATFORM(MAC) && !PLATFORM(IOS) */ + +#if PLATFORM(CHROMIUM) && OS(DARWIN) +#define WTF_USE_CF 1 +#define WTF_USE_PTHREADS 1 +#define WTF_USE_WK_SCROLLBAR_PAINTER 1 +#endif + +#if PLATFORM(CHROMIUM) +#if OS(DARWIN) +/* We can't override the global operator new and delete on OS(DARWIN) because + * some object are allocated by WebKit and deallocated by the embedder. */ +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#else /* !OS(DARWIN) */ +/* On non-OS(DARWIN), the "system malloc" is actually TCMalloc anyway, so there's + * no need to use WebKit's copy of TCMalloc. */ +#define USE_SYSTEM_MALLOC 1 +#endif /* OS(DARWIN) */ +#endif /* PLATFORM(CHROMIUM) */ + +#if PLATFORM(IOS) +#define DONT_FINALIZE_ON_MAIN_THREAD 1 +#endif + +#if PLATFORM(QT) && OS(DARWIN) +#define WTF_USE_CF 1 +#endif + +#if OS(DARWIN) && !PLATFORM(GTK) && !PLATFORM(QT) +#define ENABLE_PURGEABLE_MEMORY 1 +#endif + +#if PLATFORM(IOS) +#define ENABLE_CONTEXT_MENUS 0 +#define ENABLE_DRAG_SUPPORT 0 +#define ENABLE_GEOLOCATION 1 +#define ENABLE_ICONDATABASE 0 +#define ENABLE_INSPECTOR 1 +#define ENABLE_NETSCAPE_PLUGIN_API 0 +#define ENABLE_ORIENTATION_EVENTS 1 +#define ENABLE_REPAINT_THROTTLING 1 +#define ENABLE_WEB_ARCHIVE 1 +#define HAVE_READLINE 1 +#define WTF_USE_CF 1 +#define WTF_USE_CFNETWORK 1 +#define WTF_USE_NETWORK_CFDATA_ARRAY_CALLBACK 1 +#define WTF_USE_PTHREADS 1 + +#if PLATFORM(IOS_SIMULATOR) + #define ENABLE_JIT 0 + #define ENABLE_YARR_JIT 0 +#else + #define ENABLE_JIT 1 + #define ENABLE_LLINT 1 + #define ENABLE_YARR_JIT 1 +#endif + +#define WTF_USE_APPKIT 0 +#define WTF_USE_SECURITY_FRAMEWORK 0 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) +#define WTF_USE_CF 1 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(WIN_CAIRO) +#define WTF_USE_CFNETWORK 1 +#endif + +#if USE(CFNETWORK) || PLATFORM(MAC) || PLATFORM(IOS) +#define WTF_USE_CFURLCACHE 1 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(QT) +#define ENABLE_WEB_ARCHIVE 1 +#endif + +#if PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(CHROMIUM) && !PLATFORM(WIN_CAIRO) && !PLATFORM(QT) +#define ENABLE_FULLSCREEN_API 1 +#endif + +#if PLATFORM(WX) +#if !CPU(PPC) +#if !defined(ENABLE_ASSEMBLER) +#define ENABLE_ASSEMBLER 1 +#endif +#define ENABLE_JIT 1 +#endif +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#define ENABLE_LLINT 0 +#if OS(DARWIN) +#define WTF_USE_CF 1 +#define ENABLE_WEB_ARCHIVE 1 +#endif +#endif + +#if OS(UNIX) && (PLATFORM(GTK) || PLATFORM(QT)) +#define WTF_USE_PTHREADS 1 +#endif + +#if !defined(HAVE_ACCESSIBILITY) +#if PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && !OS(ANDROID)) || PLATFORM(EFL) +#define HAVE_ACCESSIBILITY 1 +#endif +#endif /* !defined(HAVE_ACCESSIBILITY) */ + +#if OS(UNIX) +#define HAVE_SIGNAL_H 1 +#define WTF_USE_OS_RANDOMNESS 1 +#endif + +#if (OS(FREEBSD) || OS(OPENBSD)) && !defined(__GLIBC__) +#define HAVE_PTHREAD_NP_H 1 +#endif + +#if !defined(HAVE_VASPRINTF) +#if !COMPILER(MSVC) && !COMPILER(RVCT) && !COMPILER(MINGW) && !(COMPILER(GCC) && OS(QNX)) +#define HAVE_VASPRINTF 1 +#endif +#endif + +#if !defined(HAVE_STRNSTR) +#if OS(DARWIN) || (OS(FREEBSD) && !defined(__GLIBC__)) +#define HAVE_STRNSTR 1 +#endif +#endif + +#if !OS(WINDOWS) && !OS(SOLARIS) \ + && !OS(RVCT) \ + && !OS(ANDROID) +#define HAVE_TM_GMTOFF 1 +#define HAVE_TM_ZONE 1 +#define HAVE_TIMEGM 1 +#endif + +#if OS(DARWIN) + +#define HAVE_ERRNO_H 1 +#define HAVE_LANGINFO_H 1 +#define HAVE_MMAP 1 +#define HAVE_MERGESORT 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_SYS_TIMEB_H 1 +#define WTF_USE_ACCELERATE 1 + +#if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + +#define HAVE_DISPATCH_H 1 +#define HAVE_HOSTED_CORE_ANIMATION 1 + +#if !PLATFORM(IOS) +#define HAVE_MADV_FREE_REUSE 1 +#define HAVE_MADV_FREE 1 +#define HAVE_PTHREAD_SETNAME_NP 1 +#endif + +#endif + +#if PLATFORM(IOS) +#define HAVE_MADV_FREE 1 +#define HAVE_PTHREAD_SETNAME_NP 1 +#endif + +#elif OS(WINDOWS) + +#if !OS(WINCE) +#define HAVE_SYS_TIMEB_H 1 +#define HAVE_ALIGNED_MALLOC 1 +#define HAVE_ISDEBUGGERPRESENT 1 +#endif +#define HAVE_VIRTUALALLOC 1 +#define WTF_USE_OS_RANDOMNESS 1 + +#elif OS(QNX) + +#define HAVE_ERRNO_H 1 +#define HAVE_MMAP 1 +#define HAVE_MADV_FREE_REUSE 1 +#define HAVE_MADV_FREE 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 +#define WTF_USE_PTHREADS 1 + +#elif OS(ANDROID) + +#define HAVE_ERRNO_H 1 +#define HAVE_NMAP 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 + +#else + +/* FIXME: is this actually used or do other platforms generate their own config.h? */ + +#define HAVE_ERRNO_H 1 +#define HAVE_LANGINFO_H 1 +#define HAVE_MMAP 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_TIME_H 1 + +#endif + +/* ENABLE macro defaults */ + +#if PLATFORM(QT) +/* We must not customize the global operator new and delete for the Qt port. */ +#define ENABLE_GLOBAL_FASTMALLOC_NEW 0 +#if !OS(UNIX) +#define USE_SYSTEM_MALLOC 1 +#endif +#endif + +#if !defined(ENABLE_ICONDATABASE) +#define ENABLE_ICONDATABASE 1 +#endif + +#if !defined(ENABLE_SQL_DATABASE) +#define ENABLE_SQL_DATABASE 1 +#endif + +#if !defined(ENABLE_JAVASCRIPT_DEBUGGER) +#define ENABLE_JAVASCRIPT_DEBUGGER 1 +#endif + +#if !defined(ENABLE_FTPDIR) +#define ENABLE_FTPDIR 1 +#endif + +#if !defined(ENABLE_CONTEXT_MENUS) +#define ENABLE_CONTEXT_MENUS 1 +#endif + +#if !defined(ENABLE_DRAG_SUPPORT) +#define ENABLE_DRAG_SUPPORT 1 +#endif + +#if !defined(ENABLE_INSPECTOR) +#define ENABLE_INSPECTOR 1 +#endif + +#if !defined(ENABLE_NETSCAPE_PLUGIN_API) +#define ENABLE_NETSCAPE_PLUGIN_API 1 +#endif + +#if !defined(ENABLE_GLOBAL_FASTMALLOC_NEW) +#define ENABLE_GLOBAL_FASTMALLOC_NEW 1 +#endif + +#if !defined(ENABLE_PARSED_STYLE_SHEET_CACHING) +#define ENABLE_PARSED_STYLE_SHEET_CACHING 1 +#endif + +#if !defined(ENABLE_SUBPIXEL_LAYOUT) +#if PLATFORM(CHROMIUM) +#define ENABLE_SUBPIXEL_LAYOUT 1 +#else +#define ENABLE_SUBPIXEL_LAYOUT 0 +#endif +#endif + +#if !defined(ENABLE_SATURATED_LAYOUT_ARITHMETIC) +#define ENABLE_SATURATED_LAYOUT_ARITHMETIC 0 +#endif + +#if ENABLE(ENABLE_SATURATED_LAYOUT_ARITHMETIC) && !ENABLE(ENABLE_SUBPIXEL_LAYOUT) +#error "ENABLE_SATURATED_LAYOUT_ARITHMETIC requires ENABLE_SUBPIXEL_LAYOUT" +#endif + +#if ENABLE(INPUT_TYPE_DATE) || ENABLE(INPUT_TYPE_DATETIME) || ENABLE(INPUT_TYPE_DATETIMELOCAL) || ENABLE(INPUT_TYPE_MONTH) || ENABLE(INPUT_TYPE_TIME) || ENABLE(INPUT_TYPE_WEEK) +#define ENABLE_DATE_AND_TIME_INPUT_TYPES 1 +#endif + +#define ENABLE_DEBUG_WITH_BREAKPOINT 0 +#define ENABLE_SAMPLING_COUNTERS 0 +#define ENABLE_SAMPLING_FLAGS 0 +#define ENABLE_SAMPLING_REGIONS 0 +#define ENABLE_OPCODE_SAMPLING 0 +#define ENABLE_CODEBLOCK_SAMPLING 0 +#if ENABLE(CODEBLOCK_SAMPLING) && !ENABLE(OPCODE_SAMPLING) +#error "CODEBLOCK_SAMPLING requires OPCODE_SAMPLING" +#endif +#if ENABLE(OPCODE_SAMPLING) || ENABLE(SAMPLING_FLAGS) || ENABLE(SAMPLING_REGIONS) +#define ENABLE_SAMPLING_THREAD 1 +#endif + +#if !defined(ENABLE_TEXT_CARET) && !PLATFORM(IOS) +#define ENABLE_TEXT_CARET 1 +#endif + +#if !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32_64) +#if (CPU(X86_64) && (OS(UNIX) || OS(WINDOWS))) \ + || (CPU(IA64) && !CPU(IA64_32)) \ + || CPU(ALPHA) \ + || CPU(SPARC64) \ + || CPU(S390X) \ + || CPU(PPC64) +#define WTF_USE_JSVALUE64 1 +#else +#define WTF_USE_JSVALUE32_64 1 +#endif +#endif /* !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32_64) */ + +/* Disable the JIT on versions of GCC prior to 4.1 */ +#if !defined(ENABLE_JIT) && COMPILER(GCC) && !GCC_VERSION_AT_LEAST(4, 1, 0) +#define ENABLE_JIT 0 +#endif + +/* JIT is not implemented for Windows 64-bit */ +#if !defined(ENABLE_JIT) && OS(WINDOWS) && CPU(X86_64) +#define ENABLE_JIT 0 +#define ENABLE_YARR_JIT 0 +#endif + +#if !defined(ENABLE_JIT) && CPU(SH4) && PLATFORM(QT) +#define ENABLE_JIT 1 +#endif + +/* The JIT is enabled by default on all x86, x86-64, ARM & MIPS platforms. */ +#if !defined(ENABLE_JIT) \ + && (CPU(X86) || CPU(X86_64) || CPU(ARM) || CPU(MIPS)) \ + && (OS(DARWIN) || !COMPILER(GCC) || GCC_VERSION_AT_LEAST(4, 1, 0)) \ + && !OS(WINCE) \ + && !(OS(QNX) && !PLATFORM(QT)) /* We use JIT in QNX Qt */ +#define ENABLE_JIT 1 +#endif + +/* If possible, try to enable a disassembler. This is optional. We proceed in two + steps: first we try to find some disassembler that we can use, and then we + decide if the high-level disassembler API can be enabled. */ +#if !defined(WTF_USE_UDIS86) && ENABLE(JIT) && (PLATFORM(MAC) || (PLATFORM(QT) && OS(LINUX))) \ + && (CPU(X86) || CPU(X86_64)) +#define WTF_USE_UDIS86 1 +#endif + +#if !defined(ENABLE_DISASSEMBLER) && USE(UDIS86) +#define ENABLE_DISASSEMBLER 1 +#endif + +/* On the GTK+ port we take an extra precaution for LLINT support: + * We disable it on x86 builds if the build target doesn't support SSE2 + * instructions (LLINT requires SSE2 on this platform). */ +#if !defined(ENABLE_LLINT) && PLATFORM(GTK) && CPU(X86) && COMPILER(GCC) \ + && !defined(__SSE2__) +#define ENABLE_LLINT 0 +#endif + +/* On some of the platforms where we have a JIT, we want to also have the + low-level interpreter. */ +#if !defined(ENABLE_LLINT) \ + && ENABLE(JIT) \ + && (OS(DARWIN) || OS(LINUX)) \ + && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(GTK) || PLATFORM(QT)) \ + && (CPU(X86) || CPU(X86_64) || CPU(ARM_THUMB2)) +#define ENABLE_LLINT 1 +#endif + +#if !defined(ENABLE_DFG_JIT) && ENABLE(JIT) && !COMPILER(MSVC) +/* Enable the DFG JIT on X86 and X86_64. Only tested on Mac and GNU/Linux. */ +#if (CPU(X86) || CPU(X86_64)) && (PLATFORM(MAC) || OS(LINUX)) +#define ENABLE_DFG_JIT 1 +#endif +/* Enable the DFG JIT on ARMv7. Only tested on iOS and Qt Linux. */ +#if CPU(ARM_THUMB2) && (PLATFORM(IOS) || PLATFORM(BLACKBERRY) || PLATFORM(QT)) +#define ENABLE_DFG_JIT 1 +#endif +/* Enable the DFG JIT on ARM. */ +#if CPU(ARM_TRADITIONAL) +#define ENABLE_DFG_JIT 1 +#endif +#endif + +/* If the jit is not available, enable the LLInt C Loop: */ +#if !ENABLE(JIT) +#undef ENABLE_LLINT /* Undef so that we can redefine it. */ +#undef ENABLE_LLINT_C_LOOP /* Undef so that we can redefine it. */ +#undef ENABLE_DFG_JIT /* Undef so that we can redefine it. */ +#define ENABLE_LLINT 1 +#define ENABLE_LLINT_C_LOOP 1 +#define ENABLE_DFG_JIT 0 +#endif + +/* Do a sanity check to make sure that we at least have one execution engine in + use: */ +#if !(ENABLE(JIT) || ENABLE(LLINT)) +#error You have to have at least one execution model enabled to build JSC +#endif + +/* Profiling of types and values used by JIT code. DFG_JIT depends on it, but you + can enable it manually with DFG turned off if you want to use it as a standalone + profiler. In that case, you probably want to also enable VERBOSE_VALUE_PROFILE + below. */ +#if !defined(ENABLE_VALUE_PROFILER) && ENABLE(DFG_JIT) +#define ENABLE_VALUE_PROFILER 1 +#endif + +#if !defined(ENABLE_VERBOSE_VALUE_PROFILE) && ENABLE(VALUE_PROFILER) +#define ENABLE_VERBOSE_VALUE_PROFILE 0 +#endif + +#if !defined(ENABLE_SIMPLE_HEAP_PROFILING) +#define ENABLE_SIMPLE_HEAP_PROFILING 0 +#endif + +/* Counts uses of write barriers using sampling counters. Be sure to also + set ENABLE_SAMPLING_COUNTERS to 1. */ +#if !defined(ENABLE_WRITE_BARRIER_PROFILING) +#define ENABLE_WRITE_BARRIER_PROFILING 0 +#endif + +/* Configure the JIT */ +#if CPU(X86) && COMPILER(MSVC) +#define JSC_HOST_CALL __fastcall +#elif CPU(X86) && COMPILER(GCC) +#define JSC_HOST_CALL __attribute__ ((fastcall)) +#else +#define JSC_HOST_CALL +#endif + +/* Configure the interpreter */ +#if COMPILER(GCC) || (RVCT_VERSION_AT_LEAST(4, 0, 0, 0) && defined(__GNUC__)) +#define HAVE_COMPUTED_GOTO 1 +#endif + +/* Determine if we need to enable Computed Goto Opcodes or not: */ +#if HAVE(COMPUTED_GOTO) && ENABLE(LLINT) +#define ENABLE_COMPUTED_GOTO_OPCODES 1 +#endif + +/* Regular Expression Tracing - Set to 1 to trace RegExp's in jsc. Results dumped at exit */ +#define ENABLE_REGEXP_TRACING 0 + +/* Yet Another Regex Runtime - turned on by default for JIT enabled ports. */ +#if !defined(ENABLE_YARR_JIT) && (ENABLE(JIT) || ENABLE(LLINT_C_LOOP)) && !PLATFORM(CHROMIUM) && !(OS(QNX) && PLATFORM(QT)) +#define ENABLE_YARR_JIT 1 + +/* Setting this flag compares JIT results with interpreter results. */ +#define ENABLE_YARR_JIT_DEBUG 0 +#endif + +/* If either the JIT or the RegExp JIT is enabled, then the Assembler must be + enabled as well: */ +#if ENABLE(JIT) || ENABLE(YARR_JIT) +#if defined(ENABLE_ASSEMBLER) && !ENABLE_ASSEMBLER +#error "Cannot enable the JIT or RegExp JIT without enabling the Assembler" +#else +#undef ENABLE_ASSEMBLER +#define ENABLE_ASSEMBLER 1 +#endif +#endif + +/* Pick which allocator to use; we only need an executable allocator if the assembler is compiled in. + On x86-64 we use a single fixed mmap, on other platforms we mmap on demand. */ +#if ENABLE(ASSEMBLER) +#if CPU(X86_64) || PLATFORM(IOS) +#define ENABLE_EXECUTABLE_ALLOCATOR_FIXED 1 +#else +#define ENABLE_EXECUTABLE_ALLOCATOR_DEMAND 1 +#endif +#endif + +#if !defined(ENABLE_PAN_SCROLLING) && OS(WINDOWS) +#define ENABLE_PAN_SCROLLING 1 +#endif + +/*Add other platforms as they update their platfrom specific code to handle TextRun's with 8 bit data. */ +#if PLATFORM(MAC) +#define ENABLE_8BIT_TEXTRUN 1 +#endif + +/* Use the QXmlStreamReader implementation for XMLDocumentParser */ +/* Use the QXmlQuery implementation for XSLTProcessor */ +#if PLATFORM(QT) +#if !USE(LIBXML2) +#define WTF_USE_QXMLSTREAM 1 +#define WTF_USE_QXMLQUERY 1 +#endif +#endif + +/* Accelerated compositing */ +#if PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(QT) || (PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(WIN_CAIRO)) +#define WTF_USE_ACCELERATED_COMPOSITING 1 +#endif + +#if PLATFORM(MAC) || PLATFORM(IOS) +#define ENABLE_CSS_IMAGE_SET 1 +#endif + +#if ENABLE(WEBGL) && !defined(WTF_USE_3D_GRAPHICS) +#define WTF_USE_3D_GRAPHICS 1 +#endif + +/* Qt always uses Texture Mapper */ +#if PLATFORM(QT) +#define WTF_USE_TEXTURE_MAPPER 1 +#endif + +#if USE(TEXTURE_MAPPER) && USE(3D_GRAPHICS) && !defined(WTF_USE_TEXTURE_MAPPER_GL) +#define WTF_USE_TEXTURE_MAPPER_GL 1 +#endif + +/* Compositing on the UI-process in WebKit2 */ +#if PLATFORM(QT) +#define WTF_USE_COORDINATED_GRAPHICS 1 +#endif + +#if PLATFORM(MAC) || PLATFORM(IOS) +#define WTF_USE_PROTECTION_SPACE_AUTH_CALLBACK 1 +#endif + +#if !ENABLE(NETSCAPE_PLUGIN_API) || (ENABLE(NETSCAPE_PLUGIN_API) && ((OS(UNIX) && (PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(WX))) || PLATFORM(EFL))) +#define ENABLE_PLUGIN_PACKAGE_SIMPLE_HASH 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 +#define ENABLE_THREADED_SCROLLING 1 +#endif + +/* Set up a define for a common error that is intended to cause a build error -- thus the space after Error. */ +#define WTF_PLATFORM_CFNETWORK Error USE_macro_should_be_used_with_CFNETWORK + +/* FIXME: Eventually we should enable this for all platforms and get rid of the define. */ +#if PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL) +#define WTF_USE_PLATFORM_STRATEGIES 1 +#endif + +#if PLATFORM(WIN) +#define WTF_USE_CROSS_PLATFORM_CONTEXT_MENUS 1 +#endif + +#if PLATFORM(MAC) && HAVE(ACCESSIBILITY) +#define WTF_USE_ACCESSIBILITY_CONTEXT_MENUS 1 +#endif + +#if CPU(ARM_THUMB2) +#define ENABLE_BRANCH_COMPACTION 1 +#endif + +#if !defined(ENABLE_THREADING_LIBDISPATCH) && HAVE(DISPATCH_H) +#define ENABLE_THREADING_LIBDISPATCH 1 +#elif !defined(ENABLE_THREADING_OPENMP) && defined(_OPENMP) +#define ENABLE_THREADING_OPENMP 1 +#elif !defined(THREADING_GENERIC) +#define ENABLE_THREADING_GENERIC 1 +#endif + +#if ENABLE(GLIB_SUPPORT) +#include +#endif + +/* FIXME: This define won't be needed once #27551 is fully landed. However, + since most ports try to support sub-project independence, adding new headers + to WTF causes many ports to break, and so this way we can address the build + breakages one port at a time. */ +#if !defined(WTF_USE_EXPORT_MACROS) && (PLATFORM(MAC) || PLATFORM(QT) || PLATFORM(WX)) +#define WTF_USE_EXPORT_MACROS 1 +#endif + +#if !defined(WTF_USE_EXPORT_MACROS_FOR_TESTING) && (PLATFORM(GTK) || PLATFORM(WIN)) +#define WTF_USE_EXPORT_MACROS_FOR_TESTING 1 +#endif + +#if (PLATFORM(QT) && !OS(DARWIN) && !OS(WINDOWS)) || PLATFORM(GTK) || PLATFORM(EFL) +#define WTF_USE_UNIX_DOMAIN_SOCKETS 1 +#endif + +#if !defined(ENABLE_COMPARE_AND_SWAP) && (OS(WINDOWS) || (COMPILER(GCC) && (CPU(X86) || CPU(X86_64) || CPU(ARM_THUMB2)))) +#define ENABLE_COMPARE_AND_SWAP 1 +#endif + +#define ENABLE_OBJECT_MARK_LOGGING 0 + +#if !defined(ENABLE_PARALLEL_GC) && !ENABLE(OBJECT_MARK_LOGGING) && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(BLACKBERRY) || PLATFORM(GTK)) && ENABLE(COMPARE_AND_SWAP) +#define ENABLE_PARALLEL_GC 1 +#elif PLATFORM(QT) +// Parallel GC is temporarily disabled on Qt because of regular crashes, see https://bugs.webkit.org/show_bug.cgi?id=90957 for details +#define ENABLE_PARALLEL_GC 0 +#endif + +#if !defined(ENABLE_GC_VALIDATION) && !defined(NDEBUG) +#define ENABLE_GC_VALIDATION 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 +#define WTF_USE_AVFOUNDATION 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 +#define WTF_USE_COREMEDIA 1 +#endif + +#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 +#define HAVE_AVFOUNDATION_TEXT_TRACK_SUPPORT 1 +#endif + +#if PLATFORM(MAC) || PLATFORM(GTK) || PLATFORM(EFL) || (PLATFORM(WIN) && !OS(WINCE) && !PLATFORM(WIN_CAIRO)) || PLATFORM(BLACKBERRY) +#define WTF_USE_REQUEST_ANIMATION_FRAME_TIMER 1 +#endif + +#if PLATFORM(MAC) || PLATFORM(BLACKBERRY) +#define WTF_USE_REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR 1 +#endif + +#if PLATFORM(MAC) && (PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) +#define HAVE_INVERTED_WHEEL_EVENTS 1 +#endif + +#if PLATFORM(MAC) +#define WTF_USE_COREAUDIO 1 +#endif + +#if !defined(WTF_USE_V8) && PLATFORM(CHROMIUM) +#define WTF_USE_V8 1 +#endif + +/* Not using V8 implies using JSC and vice versa */ +#if !USE(V8) +#define WTF_USE_JSC 1 +#endif + +#if ENABLE(NOTIFICATIONS) && PLATFORM(MAC) +#define ENABLE_TEXT_NOTIFICATIONS_ONLY 1 +#endif + +#if !defined(WTF_USE_ZLIB) && !PLATFORM(QT) +#define WTF_USE_ZLIB 1 +#endif + +#if PLATFORM(QT) +#include +#if defined(QT_OPENGL_ES_2) && !defined(WTF_USE_OPENGL_ES_2) +#define WTF_USE_OPENGL_ES_2 1 +#endif +#endif + +#endif /* WTF_Platform_h */ diff --git a/src/3rdparty/masm/wtf/PossiblyNull.h b/src/3rdparty/masm/wtf/PossiblyNull.h new file mode 100644 index 0000000000..46a7d713be --- /dev/null +++ b/src/3rdparty/masm/wtf/PossiblyNull.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PossiblyNull_h +#define PossiblyNull_h + +#include + +namespace WTF { + +template struct PossiblyNull { + PossiblyNull(T data) + : m_data(data) + { + } + PossiblyNull(const PossiblyNull& source) + : m_data(source.m_data) + { + source.m_data = 0; + } + ~PossiblyNull() { ASSERT(!m_data); } + bool getValue(T& out) WARN_UNUSED_RETURN; +private: + mutable T m_data; +}; + +template bool PossiblyNull::getValue(T& out) +{ + out = m_data; + bool result = !!m_data; + m_data = 0; + return result; +} + +} + +#endif diff --git a/src/3rdparty/masm/wtf/PrintStream.cpp b/src/3rdparty/masm/wtf/PrintStream.cpp new file mode 100644 index 0000000000..7dd4060971 --- /dev/null +++ b/src/3rdparty/masm/wtf/PrintStream.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PrintStream.h" + +#include + +namespace WTF { + +PrintStream::PrintStream() { } +PrintStream::~PrintStream() { } // Force the vtable to be in this module + +void PrintStream::printf(const char* format, ...) +{ + va_list argList; + va_start(argList, format); + vprintf(format, argList); + va_end(argList); +} + +void PrintStream::flush() +{ +} + +void printInternal(PrintStream& out, const char* string) +{ + out.printf("%s", string); +} + +void printInternal(PrintStream& out, bool value) +{ + if (value) + out.print("true"); + else + out.print("false"); +} + +void printInternal(PrintStream& out, int value) +{ + out.printf("%d", value); +} + +void printInternal(PrintStream& out, unsigned value) +{ + out.printf("%u", value); +} + +void printInternal(PrintStream& out, long value) +{ + out.printf("%ld", value); +} + +void printInternal(PrintStream& out, unsigned long value) +{ + out.printf("%lu", value); +} + +void printInternal(PrintStream& out, long long value) +{ + out.printf("%lld", value); +} + +void printInternal(PrintStream& out, unsigned long long value) +{ + out.printf("%llu", value); +} + +void printInternal(PrintStream& out, float value) +{ + out.print(static_cast(value)); +} + +void printInternal(PrintStream& out, double value) +{ + out.printf("%lf", value); +} + +void printInternal(PrintStream& out, RawPointer value) +{ + out.printf("%p", value.value()); +} + +void dumpCharacter(PrintStream& out, char value) +{ + out.printf("%c", value); +} + +} // namespace WTF + diff --git a/src/3rdparty/masm/wtf/PrintStream.h b/src/3rdparty/masm/wtf/PrintStream.h new file mode 100644 index 0000000000..4134dcf182 --- /dev/null +++ b/src/3rdparty/masm/wtf/PrintStream.h @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PrintStream_h +#define PrintStream_h + +#include +#include +#include +#include +#include +#include + +namespace WTF { + +class CString; +class String; + +class PrintStream { + WTF_MAKE_FAST_ALLOCATED; WTF_MAKE_NONCOPYABLE(PrintStream); +public: + PrintStream(); + virtual ~PrintStream(); + + void printf(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); + virtual void vprintf(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(2, 0) = 0; + + // Typically a no-op for many subclasses of PrintStream, this is a hint that + // the implementation should flush its buffers if it had not done so already. + virtual void flush(); + + template + void print(const T& value) + { + printInternal(*this, value); + } + + template + void print(const T1& value1, const T2& value2) + { + print(value1); + print(value2); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3) + { + print(value1); + print(value2); + print(value3); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4) + { + print(value1); + print(value2); + print(value3); + print(value4); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + print(value11); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + print(value11); + print(value12); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12, const T13& value13) + { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + print(value11); + print(value12); + print(value13); + } +}; + +void printInternal(PrintStream&, const char*); +void printInternal(PrintStream&, const CString&); +void printInternal(PrintStream&, const String&); +inline void printInternal(PrintStream& out, char* value) { printInternal(out, static_cast(value)); } +inline void printInternal(PrintStream& out, CString& value) { printInternal(out, static_cast(value)); } +inline void printInternal(PrintStream& out, String& value) { printInternal(out, static_cast(value)); } +void printInternal(PrintStream&, bool); +void printInternal(PrintStream&, int); +void printInternal(PrintStream&, unsigned); +void printInternal(PrintStream&, long); +void printInternal(PrintStream&, unsigned long); +void printInternal(PrintStream&, long long); +void printInternal(PrintStream&, unsigned long long); +void printInternal(PrintStream&, float); +void printInternal(PrintStream&, double); +void printInternal(PrintStream&, RawPointer); + +template +void printInternal(PrintStream& out, const T& value) +{ + value.dump(out); +} + +#define MAKE_PRINT_ADAPTOR(Name, Type, function) \ + class Name { \ + public: \ + Name(const Type& value) \ + : m_value(value) \ + { \ + } \ + void dump(PrintStream& out) const \ + { \ + function(out, m_value); \ + } \ + private: \ + Type m_value; \ + } + +#define MAKE_PRINT_METHOD_ADAPTOR(Name, Type, method) \ + class Name { \ + public: \ + Name(const Type& value) \ + : m_value(value) \ + { \ + } \ + void dump(PrintStream& out) const \ + { \ + m_value.method(out); \ + } \ + private: \ + Type m_value; \ + } + +// Use an adaptor-based dumper for characters to avoid situations where +// you've "compressed" an integer to a character and it ends up printing +// as ASCII when you wanted it to print as a number. +void dumpCharacter(PrintStream&, char); +MAKE_PRINT_ADAPTOR(CharacterDump, char, dumpCharacter); + +template +class PointerDump { +public: + PointerDump(const T* ptr) + : m_ptr(ptr) + { + } + + void dump(PrintStream& out) const + { + if (m_ptr) + printInternal(out, *m_ptr); + else + out.print("(null)"); + } +private: + const T* m_ptr; +}; + +template +PointerDump pointerDump(const T* ptr) { return PointerDump(ptr); } + +} // namespace WTF + +using WTF::CharacterDump; +using WTF::PointerDump; +using WTF::PrintStream; +using WTF::pointerDump; + +#endif // PrintStream_h + diff --git a/src/3rdparty/masm/wtf/RawPointer.h b/src/3rdparty/masm/wtf/RawPointer.h new file mode 100644 index 0000000000..6dc7292fb4 --- /dev/null +++ b/src/3rdparty/masm/wtf/RawPointer.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RawPointer_h +#define RawPointer_h + +namespace WTF { + +class RawPointer { +public: + RawPointer() + : m_value(0) + { + } + + explicit RawPointer(void* value) + : m_value(value) + { + } + + explicit RawPointer(const void* value) + : m_value(value) + { + } + + const void* value() const { return m_value; } + +private: + const void* m_value; +}; + +} // namespace WTF + +using WTF::RawPointer; + +#endif // RawPointer_h diff --git a/src/3rdparty/masm/wtf/StdLibExtras.h b/src/3rdparty/masm/wtf/StdLibExtras.h new file mode 100644 index 0000000000..f5e9f78df1 --- /dev/null +++ b/src/3rdparty/masm/wtf/StdLibExtras.h @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_StdLibExtras_h +#define WTF_StdLibExtras_h + +#include +#include + +// Use these to declare and define a static local variable (static T;) so that +// it is leaked so that its destructors are not called at exit. Using this +// macro also allows workarounds a compiler bug present in Apple's version of GCC 4.0.1. +#ifndef DEFINE_STATIC_LOCAL +#if COMPILER(GCC) && defined(__APPLE_CC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 1 +#define DEFINE_STATIC_LOCAL(type, name, arguments) \ + static type* name##Ptr = new type arguments; \ + type& name = *name##Ptr +#else +#define DEFINE_STATIC_LOCAL(type, name, arguments) \ + static type& name = *new type arguments +#endif +#endif + +// Use this macro to declare and define a debug-only global variable that may have a +// non-trivial constructor and destructor. When building with clang, this will suppress +// warnings about global constructors and exit-time destructors. +#ifndef NDEBUG +#if COMPILER(CLANG) +#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \ + _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \ + static type name arguments; \ + _Pragma("clang diagnostic pop") +#else +#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ + static type name arguments; +#endif // COMPILER(CLANG) +#else +#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) +#endif // NDEBUG + +// OBJECT_OFFSETOF: Like the C++ offsetof macro, but you can use it with classes. +// The magic number 0x4000 is insignificant. We use it to avoid using NULL, since +// NULL can cause compiler problems, especially in cases of multiple inheritance. +#define OBJECT_OFFSETOF(class, field) (reinterpret_cast(&(reinterpret_cast(0x4000)->field)) - 0x4000) + +// STRINGIZE: Can convert any value to quoted string, even expandable macros +#define STRINGIZE(exp) #exp +#define STRINGIZE_VALUE_OF(exp) STRINGIZE(exp) + +/* + * The reinterpret_cast([pointer to Type2]) expressions - where + * sizeof(Type1) > sizeof(Type2) - cause the following warning on ARM with GCC: + * increases required alignment of target type. + * + * An implicit or an extra static_cast bypasses the warning. + * For more info see the following bugzilla entries: + * - https://bugs.webkit.org/show_bug.cgi?id=38045 + * - http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43976 + */ +#if (CPU(ARM) || CPU(MIPS)) && COMPILER(GCC) +template +bool isPointerTypeAlignmentOkay(Type* ptr) +{ + return !(reinterpret_cast(ptr) % __alignof__(Type)); +} + +template +TypePtr reinterpret_cast_ptr(void* ptr) +{ + ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast(ptr))); + return reinterpret_cast(ptr); +} + +template +TypePtr reinterpret_cast_ptr(const void* ptr) +{ + ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast(ptr))); + return reinterpret_cast(ptr); +} +#else +template +bool isPointerTypeAlignmentOkay(Type*) +{ + return true; +} +#define reinterpret_cast_ptr reinterpret_cast +#endif + +namespace WTF { + +static const size_t KB = 1024; +static const size_t MB = 1024 * 1024; + +inline bool isPointerAligned(void* p) +{ + return !((intptr_t)(p) & (sizeof(char*) - 1)); +} + +inline bool is8ByteAligned(void* p) +{ + return !((uintptr_t)(p) & (sizeof(double) - 1)); +} + +/* + * C++'s idea of a reinterpret_cast lacks sufficient cojones. + */ +template +inline TO bitwise_cast(FROM from) +{ + COMPILE_ASSERT(sizeof(TO) == sizeof(FROM), WTF_bitwise_cast_sizeof_casted_types_is_equal); + union { + FROM from; + TO to; + } u; + u.from = from; + return u.to; +} + +template +inline To safeCast(From value) +{ + ASSERT(isInBounds(value)); + return static_cast(value); +} + +// Returns a count of the number of bits set in 'bits'. +inline size_t bitCount(unsigned bits) +{ + bits = bits - ((bits >> 1) & 0x55555555); + bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); + return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; +} + +// Macro that returns a compile time constant with the length of an array, but gives an error if passed a non-array. +template char (&ArrayLengthHelperFunction(T (&)[Size]))[Size]; +// GCC needs some help to deduce a 0 length array. +#if COMPILER(GCC) +template char (&ArrayLengthHelperFunction(T (&)[0]))[0]; +#endif +#define WTF_ARRAY_LENGTH(array) sizeof(::WTF::ArrayLengthHelperFunction(array)) + +// Efficient implementation that takes advantage of powers of two. +inline size_t roundUpToMultipleOf(size_t divisor, size_t x) +{ + ASSERT(divisor && !(divisor & (divisor - 1))); + size_t remainderMask = divisor - 1; + return (x + remainderMask) & ~remainderMask; +} +template inline size_t roundUpToMultipleOf(size_t x) +{ + COMPILE_ASSERT(divisor && !(divisor & (divisor - 1)), divisor_is_a_power_of_two); + return roundUpToMultipleOf(divisor, x); +} + +enum BinarySearchMode { + KeyMustBePresentInArray, + KeyMightNotBePresentInArray, + ReturnAdjacentElementIfKeyIsNotPresent +}; + +template +inline ArrayElementType* binarySearchImpl(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + size_t offset = 0; + while (size > 1) { + int pos = (size - 1) >> 1; + KeyType val = extractKey(&array[offset + pos]); + + if (val == key) + return &array[offset + pos]; + // The item we are looking for is smaller than the item being check; reduce the value of 'size', + // chopping off the right hand half of the array. + if (key < val) + size = pos; + // Discard all values in the left hand half of the array, up to and including the item at pos. + else { + size -= (pos + 1); + offset += (pos + 1); + } + + ASSERT(mode != KeyMustBePresentInArray || size); + } + + if (mode == KeyMightNotBePresentInArray && !size) + return 0; + + ArrayElementType* result = &array[offset]; + + if (mode == KeyMightNotBePresentInArray && key != extractKey(result)) + return 0; + + if (mode == KeyMustBePresentInArray) { + ASSERT(size == 1); + ASSERT(key == extractKey(result)); + } + + return result; +} + +// If the element is not found, crash if asserts are enabled, and behave like approximateBinarySearch in release builds. +template +inline ArrayElementType* binarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(array, size, key, extractKey); +} + +// Return zero if the element is not found. +template +inline ArrayElementType* tryBinarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(array, size, key, extractKey); +} + +// Return the element that is either to the left, or the right, of where the element would have been found. +template +inline ArrayElementType* approximateBinarySearch(ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(array, size, key, extractKey); +} + +// Variants of the above that use const. +template +inline ArrayElementType* binarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(const_cast(array), size, key, extractKey); +} +template +inline ArrayElementType* tryBinarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(const_cast(array), size, key, extractKey); +} +template +inline ArrayElementType* approximateBinarySearch(const ArrayType& array, size_t size, KeyType key, const ExtractKey& extractKey = ExtractKey()) +{ + return binarySearchImpl(const_cast(array), size, key, extractKey); +} + +} // namespace WTF + +// This version of placement new omits a 0 check. +enum NotNullTag { NotNull }; +inline void* operator new(size_t, NotNullTag, void* location) +{ + ASSERT(location); + return location; +} + +using WTF::KB; +using WTF::MB; +using WTF::isPointerAligned; +using WTF::is8ByteAligned; +using WTF::binarySearch; +using WTF::tryBinarySearch; +using WTF::approximateBinarySearch; +using WTF::bitwise_cast; +using WTF::safeCast; + +#endif // WTF_StdLibExtras_h diff --git a/src/3rdparty/masm/wtf/VMTags.h b/src/3rdparty/masm/wtf/VMTags.h new file mode 100644 index 0000000000..117bc3721e --- /dev/null +++ b/src/3rdparty/masm/wtf/VMTags.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VMTags_h +#define VMTags_h + +// On Mac OS X, the VM subsystem allows tagging memory requested from mmap and vm_map +// in order to aid tools that inspect system memory use. +#if OS(DARWIN) + +#include + +#if defined(VM_MEMORY_TCMALLOC) +#define VM_TAG_FOR_TCMALLOC_MEMORY VM_MAKE_TAG(VM_MEMORY_TCMALLOC) +#else +#define VM_TAG_FOR_TCMALLOC_MEMORY VM_MAKE_TAG(53) +#endif // defined(VM_MEMORY_TCMALLOC) + +#if defined(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) +#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) +#else +#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY VM_MAKE_TAG(64) +#endif // defined(VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR) + +#if defined(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) +#define VM_TAG_FOR_REGISTERFILE_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) +#else +#define VM_TAG_FOR_REGISTERFILE_MEMORY VM_MAKE_TAG(65) +#endif // defined(VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE) + +#if defined(VM_MEMORY_JAVASCRIPT_CORE) +#define VM_TAG_FOR_COLLECTOR_MEMORY VM_MAKE_TAG(VM_MEMORY_JAVASCRIPT_CORE) +#else +#define VM_TAG_FOR_COLLECTOR_MEMORY VM_MAKE_TAG(63) +#endif // defined(VM_MEMORY_JAVASCRIPT_CORE) + +#if defined(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) +#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY VM_MAKE_TAG(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) +#else +#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY VM_MAKE_TAG(69) +#endif // defined(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS) + +#else // OS(DARWIN) + +#define VM_TAG_FOR_TCMALLOC_MEMORY -1 +#define VM_TAG_FOR_COLLECTOR_MEMORY -1 +#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY -1 +#define VM_TAG_FOR_REGISTERFILE_MEMORY -1 +#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY -1 + +#endif // OS(DARWIN) + +#endif // VMTags_h diff --git a/src/3rdparty/masm/yarr/Yarr.h b/src/3rdparty/masm/yarr/Yarr.h new file mode 100644 index 0000000000..d393e9fa90 --- /dev/null +++ b/src/3rdparty/masm/yarr/Yarr.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Yarr_h +#define Yarr_h + +#include "YarrInterpreter.h" +#include "YarrPattern.h" + +namespace JSC { namespace Yarr { + +#define YarrStackSpaceForBackTrackInfoPatternCharacter 1 // Only for !fixed quantifiers. +#define YarrStackSpaceForBackTrackInfoCharacterClass 1 // Only for !fixed quantifiers. +#define YarrStackSpaceForBackTrackInfoBackReference 2 +#define YarrStackSpaceForBackTrackInfoAlternative 1 // One per alternative. +#define YarrStackSpaceForBackTrackInfoParentheticalAssertion 1 +#define YarrStackSpaceForBackTrackInfoParenthesesOnce 1 // Only for !fixed quantifiers. +#define YarrStackSpaceForBackTrackInfoParenthesesTerminal 1 +#define YarrStackSpaceForBackTrackInfoParentheses 2 + +static const unsigned quantifyInfinite = UINT_MAX; +static const unsigned offsetNoMatch = (unsigned)-1; + +// The below limit restricts the number of "recursive" match calls in order to +// avoid spending exponential time on complex regular expressions. +static const unsigned matchLimit = 1000000; + +enum JSRegExpResult { + JSRegExpMatch = 1, + JSRegExpNoMatch = 0, + JSRegExpErrorNoMatch = -1, + JSRegExpErrorHitLimit = -2, + JSRegExpErrorNoMemory = -3, + JSRegExpErrorInternal = -4 +}; + +enum YarrCharSize { + Char8, + Char16 +}; + +} } // namespace JSC::Yarr + +#endif // Yarr_h + diff --git a/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp b/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp new file mode 100644 index 0000000000..7bb3d08eb5 --- /dev/null +++ b/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// DO NOT EDIT! - this file autogenerated by YarrCanonicalizeUCS2.js + +#include "config.h" +#include "YarrCanonicalizeUCS2.h" + +namespace JSC { namespace Yarr { + +#include + +uint16_t ucs2CharacterSet0[] = { 0x01c4u, 0x01c5u, 0x01c6u, 0 }; +uint16_t ucs2CharacterSet1[] = { 0x01c7u, 0x01c8u, 0x01c9u, 0 }; +uint16_t ucs2CharacterSet2[] = { 0x01cau, 0x01cbu, 0x01ccu, 0 }; +uint16_t ucs2CharacterSet3[] = { 0x01f1u, 0x01f2u, 0x01f3u, 0 }; +uint16_t ucs2CharacterSet4[] = { 0x0392u, 0x03b2u, 0x03d0u, 0 }; +uint16_t ucs2CharacterSet5[] = { 0x0395u, 0x03b5u, 0x03f5u, 0 }; +uint16_t ucs2CharacterSet6[] = { 0x0398u, 0x03b8u, 0x03d1u, 0 }; +uint16_t ucs2CharacterSet7[] = { 0x0345u, 0x0399u, 0x03b9u, 0x1fbeu, 0 }; +uint16_t ucs2CharacterSet8[] = { 0x039au, 0x03bau, 0x03f0u, 0 }; +uint16_t ucs2CharacterSet9[] = { 0x00b5u, 0x039cu, 0x03bcu, 0 }; +uint16_t ucs2CharacterSet10[] = { 0x03a0u, 0x03c0u, 0x03d6u, 0 }; +uint16_t ucs2CharacterSet11[] = { 0x03a1u, 0x03c1u, 0x03f1u, 0 }; +uint16_t ucs2CharacterSet12[] = { 0x03a3u, 0x03c2u, 0x03c3u, 0 }; +uint16_t ucs2CharacterSet13[] = { 0x03a6u, 0x03c6u, 0x03d5u, 0 }; +uint16_t ucs2CharacterSet14[] = { 0x1e60u, 0x1e61u, 0x1e9bu, 0 }; + +static const size_t UCS2_CANONICALIZATION_SETS = 15; +uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = { + ucs2CharacterSet0, + ucs2CharacterSet1, + ucs2CharacterSet2, + ucs2CharacterSet3, + ucs2CharacterSet4, + ucs2CharacterSet5, + ucs2CharacterSet6, + ucs2CharacterSet7, + ucs2CharacterSet8, + ucs2CharacterSet9, + ucs2CharacterSet10, + ucs2CharacterSet11, + ucs2CharacterSet12, + ucs2CharacterSet13, + ucs2CharacterSet14, +}; + +const size_t UCS2_CANONICALIZATION_RANGES = 364; +UCS2CanonicalizationRange rangeInfo[UCS2_CANONICALIZATION_RANGES] = { + { 0x0000u, 0x0040u, 0x0000u, CanonicalizeUnique }, + { 0x0041u, 0x005au, 0x0020u, CanonicalizeRangeLo }, + { 0x005bu, 0x0060u, 0x0000u, CanonicalizeUnique }, + { 0x0061u, 0x007au, 0x0020u, CanonicalizeRangeHi }, + { 0x007bu, 0x00b4u, 0x0000u, CanonicalizeUnique }, + { 0x00b5u, 0x00b5u, 0x0009u, CanonicalizeSet }, + { 0x00b6u, 0x00bfu, 0x0000u, CanonicalizeUnique }, + { 0x00c0u, 0x00d6u, 0x0020u, CanonicalizeRangeLo }, + { 0x00d7u, 0x00d7u, 0x0000u, CanonicalizeUnique }, + { 0x00d8u, 0x00deu, 0x0020u, CanonicalizeRangeLo }, + { 0x00dfu, 0x00dfu, 0x0000u, CanonicalizeUnique }, + { 0x00e0u, 0x00f6u, 0x0020u, CanonicalizeRangeHi }, + { 0x00f7u, 0x00f7u, 0x0000u, CanonicalizeUnique }, + { 0x00f8u, 0x00feu, 0x0020u, CanonicalizeRangeHi }, + { 0x00ffu, 0x00ffu, 0x0079u, CanonicalizeRangeLo }, + { 0x0100u, 0x012fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0130u, 0x0131u, 0x0000u, CanonicalizeUnique }, + { 0x0132u, 0x0137u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0138u, 0x0138u, 0x0000u, CanonicalizeUnique }, + { 0x0139u, 0x0148u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0149u, 0x0149u, 0x0000u, CanonicalizeUnique }, + { 0x014au, 0x0177u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0178u, 0x0178u, 0x0079u, CanonicalizeRangeHi }, + { 0x0179u, 0x017eu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x017fu, 0x017fu, 0x0000u, CanonicalizeUnique }, + { 0x0180u, 0x0180u, 0x00c3u, CanonicalizeRangeLo }, + { 0x0181u, 0x0181u, 0x00d2u, CanonicalizeRangeLo }, + { 0x0182u, 0x0185u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0186u, 0x0186u, 0x00ceu, CanonicalizeRangeLo }, + { 0x0187u, 0x0188u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0189u, 0x018au, 0x00cdu, CanonicalizeRangeLo }, + { 0x018bu, 0x018cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x018du, 0x018du, 0x0000u, CanonicalizeUnique }, + { 0x018eu, 0x018eu, 0x004fu, CanonicalizeRangeLo }, + { 0x018fu, 0x018fu, 0x00cau, CanonicalizeRangeLo }, + { 0x0190u, 0x0190u, 0x00cbu, CanonicalizeRangeLo }, + { 0x0191u, 0x0192u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0193u, 0x0193u, 0x00cdu, CanonicalizeRangeLo }, + { 0x0194u, 0x0194u, 0x00cfu, CanonicalizeRangeLo }, + { 0x0195u, 0x0195u, 0x0061u, CanonicalizeRangeLo }, + { 0x0196u, 0x0196u, 0x00d3u, CanonicalizeRangeLo }, + { 0x0197u, 0x0197u, 0x00d1u, CanonicalizeRangeLo }, + { 0x0198u, 0x0199u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x019au, 0x019au, 0x00a3u, CanonicalizeRangeLo }, + { 0x019bu, 0x019bu, 0x0000u, CanonicalizeUnique }, + { 0x019cu, 0x019cu, 0x00d3u, CanonicalizeRangeLo }, + { 0x019du, 0x019du, 0x00d5u, CanonicalizeRangeLo }, + { 0x019eu, 0x019eu, 0x0082u, CanonicalizeRangeLo }, + { 0x019fu, 0x019fu, 0x00d6u, CanonicalizeRangeLo }, + { 0x01a0u, 0x01a5u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01a6u, 0x01a6u, 0x00dau, CanonicalizeRangeLo }, + { 0x01a7u, 0x01a8u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01a9u, 0x01a9u, 0x00dau, CanonicalizeRangeLo }, + { 0x01aau, 0x01abu, 0x0000u, CanonicalizeUnique }, + { 0x01acu, 0x01adu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01aeu, 0x01aeu, 0x00dau, CanonicalizeRangeLo }, + { 0x01afu, 0x01b0u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01b1u, 0x01b2u, 0x00d9u, CanonicalizeRangeLo }, + { 0x01b3u, 0x01b6u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01b7u, 0x01b7u, 0x00dbu, CanonicalizeRangeLo }, + { 0x01b8u, 0x01b9u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01bau, 0x01bbu, 0x0000u, CanonicalizeUnique }, + { 0x01bcu, 0x01bdu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01beu, 0x01beu, 0x0000u, CanonicalizeUnique }, + { 0x01bfu, 0x01bfu, 0x0038u, CanonicalizeRangeLo }, + { 0x01c0u, 0x01c3u, 0x0000u, CanonicalizeUnique }, + { 0x01c4u, 0x01c6u, 0x0000u, CanonicalizeSet }, + { 0x01c7u, 0x01c9u, 0x0001u, CanonicalizeSet }, + { 0x01cau, 0x01ccu, 0x0002u, CanonicalizeSet }, + { 0x01cdu, 0x01dcu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01ddu, 0x01ddu, 0x004fu, CanonicalizeRangeHi }, + { 0x01deu, 0x01efu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01f0u, 0x01f0u, 0x0000u, CanonicalizeUnique }, + { 0x01f1u, 0x01f3u, 0x0003u, CanonicalizeSet }, + { 0x01f4u, 0x01f5u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01f6u, 0x01f6u, 0x0061u, CanonicalizeRangeHi }, + { 0x01f7u, 0x01f7u, 0x0038u, CanonicalizeRangeHi }, + { 0x01f8u, 0x021fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0220u, 0x0220u, 0x0082u, CanonicalizeRangeHi }, + { 0x0221u, 0x0221u, 0x0000u, CanonicalizeUnique }, + { 0x0222u, 0x0233u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0234u, 0x0239u, 0x0000u, CanonicalizeUnique }, + { 0x023au, 0x023au, 0x2a2bu, CanonicalizeRangeLo }, + { 0x023bu, 0x023cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x023du, 0x023du, 0x00a3u, CanonicalizeRangeHi }, + { 0x023eu, 0x023eu, 0x2a28u, CanonicalizeRangeLo }, + { 0x023fu, 0x0240u, 0x2a3fu, CanonicalizeRangeLo }, + { 0x0241u, 0x0242u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0243u, 0x0243u, 0x00c3u, CanonicalizeRangeHi }, + { 0x0244u, 0x0244u, 0x0045u, CanonicalizeRangeLo }, + { 0x0245u, 0x0245u, 0x0047u, CanonicalizeRangeLo }, + { 0x0246u, 0x024fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0250u, 0x0250u, 0x2a1fu, CanonicalizeRangeLo }, + { 0x0251u, 0x0251u, 0x2a1cu, CanonicalizeRangeLo }, + { 0x0252u, 0x0252u, 0x2a1eu, CanonicalizeRangeLo }, + { 0x0253u, 0x0253u, 0x00d2u, CanonicalizeRangeHi }, + { 0x0254u, 0x0254u, 0x00ceu, CanonicalizeRangeHi }, + { 0x0255u, 0x0255u, 0x0000u, CanonicalizeUnique }, + { 0x0256u, 0x0257u, 0x00cdu, CanonicalizeRangeHi }, + { 0x0258u, 0x0258u, 0x0000u, CanonicalizeUnique }, + { 0x0259u, 0x0259u, 0x00cau, CanonicalizeRangeHi }, + { 0x025au, 0x025au, 0x0000u, CanonicalizeUnique }, + { 0x025bu, 0x025bu, 0x00cbu, CanonicalizeRangeHi }, + { 0x025cu, 0x025fu, 0x0000u, CanonicalizeUnique }, + { 0x0260u, 0x0260u, 0x00cdu, CanonicalizeRangeHi }, + { 0x0261u, 0x0262u, 0x0000u, CanonicalizeUnique }, + { 0x0263u, 0x0263u, 0x00cfu, CanonicalizeRangeHi }, + { 0x0264u, 0x0264u, 0x0000u, CanonicalizeUnique }, + { 0x0265u, 0x0265u, 0xa528u, CanonicalizeRangeLo }, + { 0x0266u, 0x0267u, 0x0000u, CanonicalizeUnique }, + { 0x0268u, 0x0268u, 0x00d1u, CanonicalizeRangeHi }, + { 0x0269u, 0x0269u, 0x00d3u, CanonicalizeRangeHi }, + { 0x026au, 0x026au, 0x0000u, CanonicalizeUnique }, + { 0x026bu, 0x026bu, 0x29f7u, CanonicalizeRangeLo }, + { 0x026cu, 0x026eu, 0x0000u, CanonicalizeUnique }, + { 0x026fu, 0x026fu, 0x00d3u, CanonicalizeRangeHi }, + { 0x0270u, 0x0270u, 0x0000u, CanonicalizeUnique }, + { 0x0271u, 0x0271u, 0x29fdu, CanonicalizeRangeLo }, + { 0x0272u, 0x0272u, 0x00d5u, CanonicalizeRangeHi }, + { 0x0273u, 0x0274u, 0x0000u, CanonicalizeUnique }, + { 0x0275u, 0x0275u, 0x00d6u, CanonicalizeRangeHi }, + { 0x0276u, 0x027cu, 0x0000u, CanonicalizeUnique }, + { 0x027du, 0x027du, 0x29e7u, CanonicalizeRangeLo }, + { 0x027eu, 0x027fu, 0x0000u, CanonicalizeUnique }, + { 0x0280u, 0x0280u, 0x00dau, CanonicalizeRangeHi }, + { 0x0281u, 0x0282u, 0x0000u, CanonicalizeUnique }, + { 0x0283u, 0x0283u, 0x00dau, CanonicalizeRangeHi }, + { 0x0284u, 0x0287u, 0x0000u, CanonicalizeUnique }, + { 0x0288u, 0x0288u, 0x00dau, CanonicalizeRangeHi }, + { 0x0289u, 0x0289u, 0x0045u, CanonicalizeRangeHi }, + { 0x028au, 0x028bu, 0x00d9u, CanonicalizeRangeHi }, + { 0x028cu, 0x028cu, 0x0047u, CanonicalizeRangeHi }, + { 0x028du, 0x0291u, 0x0000u, CanonicalizeUnique }, + { 0x0292u, 0x0292u, 0x00dbu, CanonicalizeRangeHi }, + { 0x0293u, 0x0344u, 0x0000u, CanonicalizeUnique }, + { 0x0345u, 0x0345u, 0x0007u, CanonicalizeSet }, + { 0x0346u, 0x036fu, 0x0000u, CanonicalizeUnique }, + { 0x0370u, 0x0373u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0374u, 0x0375u, 0x0000u, CanonicalizeUnique }, + { 0x0376u, 0x0377u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0378u, 0x037au, 0x0000u, CanonicalizeUnique }, + { 0x037bu, 0x037du, 0x0082u, CanonicalizeRangeLo }, + { 0x037eu, 0x0385u, 0x0000u, CanonicalizeUnique }, + { 0x0386u, 0x0386u, 0x0026u, CanonicalizeRangeLo }, + { 0x0387u, 0x0387u, 0x0000u, CanonicalizeUnique }, + { 0x0388u, 0x038au, 0x0025u, CanonicalizeRangeLo }, + { 0x038bu, 0x038bu, 0x0000u, CanonicalizeUnique }, + { 0x038cu, 0x038cu, 0x0040u, CanonicalizeRangeLo }, + { 0x038du, 0x038du, 0x0000u, CanonicalizeUnique }, + { 0x038eu, 0x038fu, 0x003fu, CanonicalizeRangeLo }, + { 0x0390u, 0x0390u, 0x0000u, CanonicalizeUnique }, + { 0x0391u, 0x0391u, 0x0020u, CanonicalizeRangeLo }, + { 0x0392u, 0x0392u, 0x0004u, CanonicalizeSet }, + { 0x0393u, 0x0394u, 0x0020u, CanonicalizeRangeLo }, + { 0x0395u, 0x0395u, 0x0005u, CanonicalizeSet }, + { 0x0396u, 0x0397u, 0x0020u, CanonicalizeRangeLo }, + { 0x0398u, 0x0398u, 0x0006u, CanonicalizeSet }, + { 0x0399u, 0x0399u, 0x0007u, CanonicalizeSet }, + { 0x039au, 0x039au, 0x0008u, CanonicalizeSet }, + { 0x039bu, 0x039bu, 0x0020u, CanonicalizeRangeLo }, + { 0x039cu, 0x039cu, 0x0009u, CanonicalizeSet }, + { 0x039du, 0x039fu, 0x0020u, CanonicalizeRangeLo }, + { 0x03a0u, 0x03a0u, 0x000au, CanonicalizeSet }, + { 0x03a1u, 0x03a1u, 0x000bu, CanonicalizeSet }, + { 0x03a2u, 0x03a2u, 0x0000u, CanonicalizeUnique }, + { 0x03a3u, 0x03a3u, 0x000cu, CanonicalizeSet }, + { 0x03a4u, 0x03a5u, 0x0020u, CanonicalizeRangeLo }, + { 0x03a6u, 0x03a6u, 0x000du, CanonicalizeSet }, + { 0x03a7u, 0x03abu, 0x0020u, CanonicalizeRangeLo }, + { 0x03acu, 0x03acu, 0x0026u, CanonicalizeRangeHi }, + { 0x03adu, 0x03afu, 0x0025u, CanonicalizeRangeHi }, + { 0x03b0u, 0x03b0u, 0x0000u, CanonicalizeUnique }, + { 0x03b1u, 0x03b1u, 0x0020u, CanonicalizeRangeHi }, + { 0x03b2u, 0x03b2u, 0x0004u, CanonicalizeSet }, + { 0x03b3u, 0x03b4u, 0x0020u, CanonicalizeRangeHi }, + { 0x03b5u, 0x03b5u, 0x0005u, CanonicalizeSet }, + { 0x03b6u, 0x03b7u, 0x0020u, CanonicalizeRangeHi }, + { 0x03b8u, 0x03b8u, 0x0006u, CanonicalizeSet }, + { 0x03b9u, 0x03b9u, 0x0007u, CanonicalizeSet }, + { 0x03bau, 0x03bau, 0x0008u, CanonicalizeSet }, + { 0x03bbu, 0x03bbu, 0x0020u, CanonicalizeRangeHi }, + { 0x03bcu, 0x03bcu, 0x0009u, CanonicalizeSet }, + { 0x03bdu, 0x03bfu, 0x0020u, CanonicalizeRangeHi }, + { 0x03c0u, 0x03c0u, 0x000au, CanonicalizeSet }, + { 0x03c1u, 0x03c1u, 0x000bu, CanonicalizeSet }, + { 0x03c2u, 0x03c3u, 0x000cu, CanonicalizeSet }, + { 0x03c4u, 0x03c5u, 0x0020u, CanonicalizeRangeHi }, + { 0x03c6u, 0x03c6u, 0x000du, CanonicalizeSet }, + { 0x03c7u, 0x03cbu, 0x0020u, CanonicalizeRangeHi }, + { 0x03ccu, 0x03ccu, 0x0040u, CanonicalizeRangeHi }, + { 0x03cdu, 0x03ceu, 0x003fu, CanonicalizeRangeHi }, + { 0x03cfu, 0x03cfu, 0x0008u, CanonicalizeRangeLo }, + { 0x03d0u, 0x03d0u, 0x0004u, CanonicalizeSet }, + { 0x03d1u, 0x03d1u, 0x0006u, CanonicalizeSet }, + { 0x03d2u, 0x03d4u, 0x0000u, CanonicalizeUnique }, + { 0x03d5u, 0x03d5u, 0x000du, CanonicalizeSet }, + { 0x03d6u, 0x03d6u, 0x000au, CanonicalizeSet }, + { 0x03d7u, 0x03d7u, 0x0008u, CanonicalizeRangeHi }, + { 0x03d8u, 0x03efu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x03f0u, 0x03f0u, 0x0008u, CanonicalizeSet }, + { 0x03f1u, 0x03f1u, 0x000bu, CanonicalizeSet }, + { 0x03f2u, 0x03f2u, 0x0007u, CanonicalizeRangeLo }, + { 0x03f3u, 0x03f4u, 0x0000u, CanonicalizeUnique }, + { 0x03f5u, 0x03f5u, 0x0005u, CanonicalizeSet }, + { 0x03f6u, 0x03f6u, 0x0000u, CanonicalizeUnique }, + { 0x03f7u, 0x03f8u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x03f9u, 0x03f9u, 0x0007u, CanonicalizeRangeHi }, + { 0x03fau, 0x03fbu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x03fcu, 0x03fcu, 0x0000u, CanonicalizeUnique }, + { 0x03fdu, 0x03ffu, 0x0082u, CanonicalizeRangeHi }, + { 0x0400u, 0x040fu, 0x0050u, CanonicalizeRangeLo }, + { 0x0410u, 0x042fu, 0x0020u, CanonicalizeRangeLo }, + { 0x0430u, 0x044fu, 0x0020u, CanonicalizeRangeHi }, + { 0x0450u, 0x045fu, 0x0050u, CanonicalizeRangeHi }, + { 0x0460u, 0x0481u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0482u, 0x0489u, 0x0000u, CanonicalizeUnique }, + { 0x048au, 0x04bfu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x04c0u, 0x04c0u, 0x000fu, CanonicalizeRangeLo }, + { 0x04c1u, 0x04ceu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x04cfu, 0x04cfu, 0x000fu, CanonicalizeRangeHi }, + { 0x04d0u, 0x0527u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0528u, 0x0530u, 0x0000u, CanonicalizeUnique }, + { 0x0531u, 0x0556u, 0x0030u, CanonicalizeRangeLo }, + { 0x0557u, 0x0560u, 0x0000u, CanonicalizeUnique }, + { 0x0561u, 0x0586u, 0x0030u, CanonicalizeRangeHi }, + { 0x0587u, 0x109fu, 0x0000u, CanonicalizeUnique }, + { 0x10a0u, 0x10c5u, 0x1c60u, CanonicalizeRangeLo }, + { 0x10c6u, 0x1d78u, 0x0000u, CanonicalizeUnique }, + { 0x1d79u, 0x1d79u, 0x8a04u, CanonicalizeRangeLo }, + { 0x1d7au, 0x1d7cu, 0x0000u, CanonicalizeUnique }, + { 0x1d7du, 0x1d7du, 0x0ee6u, CanonicalizeRangeLo }, + { 0x1d7eu, 0x1dffu, 0x0000u, CanonicalizeUnique }, + { 0x1e00u, 0x1e5fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x1e60u, 0x1e61u, 0x000eu, CanonicalizeSet }, + { 0x1e62u, 0x1e95u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x1e96u, 0x1e9au, 0x0000u, CanonicalizeUnique }, + { 0x1e9bu, 0x1e9bu, 0x000eu, CanonicalizeSet }, + { 0x1e9cu, 0x1e9fu, 0x0000u, CanonicalizeUnique }, + { 0x1ea0u, 0x1effu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x1f00u, 0x1f07u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f08u, 0x1f0fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f10u, 0x1f15u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f16u, 0x1f17u, 0x0000u, CanonicalizeUnique }, + { 0x1f18u, 0x1f1du, 0x0008u, CanonicalizeRangeHi }, + { 0x1f1eu, 0x1f1fu, 0x0000u, CanonicalizeUnique }, + { 0x1f20u, 0x1f27u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f28u, 0x1f2fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f30u, 0x1f37u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f38u, 0x1f3fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f40u, 0x1f45u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f46u, 0x1f47u, 0x0000u, CanonicalizeUnique }, + { 0x1f48u, 0x1f4du, 0x0008u, CanonicalizeRangeHi }, + { 0x1f4eu, 0x1f50u, 0x0000u, CanonicalizeUnique }, + { 0x1f51u, 0x1f51u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f52u, 0x1f52u, 0x0000u, CanonicalizeUnique }, + { 0x1f53u, 0x1f53u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f54u, 0x1f54u, 0x0000u, CanonicalizeUnique }, + { 0x1f55u, 0x1f55u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f56u, 0x1f56u, 0x0000u, CanonicalizeUnique }, + { 0x1f57u, 0x1f57u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f58u, 0x1f58u, 0x0000u, CanonicalizeUnique }, + { 0x1f59u, 0x1f59u, 0x0008u, CanonicalizeRangeHi }, + { 0x1f5au, 0x1f5au, 0x0000u, CanonicalizeUnique }, + { 0x1f5bu, 0x1f5bu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f5cu, 0x1f5cu, 0x0000u, CanonicalizeUnique }, + { 0x1f5du, 0x1f5du, 0x0008u, CanonicalizeRangeHi }, + { 0x1f5eu, 0x1f5eu, 0x0000u, CanonicalizeUnique }, + { 0x1f5fu, 0x1f5fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f60u, 0x1f67u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f68u, 0x1f6fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f70u, 0x1f71u, 0x004au, CanonicalizeRangeLo }, + { 0x1f72u, 0x1f75u, 0x0056u, CanonicalizeRangeLo }, + { 0x1f76u, 0x1f77u, 0x0064u, CanonicalizeRangeLo }, + { 0x1f78u, 0x1f79u, 0x0080u, CanonicalizeRangeLo }, + { 0x1f7au, 0x1f7bu, 0x0070u, CanonicalizeRangeLo }, + { 0x1f7cu, 0x1f7du, 0x007eu, CanonicalizeRangeLo }, + { 0x1f7eu, 0x1fafu, 0x0000u, CanonicalizeUnique }, + { 0x1fb0u, 0x1fb1u, 0x0008u, CanonicalizeRangeLo }, + { 0x1fb2u, 0x1fb7u, 0x0000u, CanonicalizeUnique }, + { 0x1fb8u, 0x1fb9u, 0x0008u, CanonicalizeRangeHi }, + { 0x1fbau, 0x1fbbu, 0x004au, CanonicalizeRangeHi }, + { 0x1fbcu, 0x1fbdu, 0x0000u, CanonicalizeUnique }, + { 0x1fbeu, 0x1fbeu, 0x0007u, CanonicalizeSet }, + { 0x1fbfu, 0x1fc7u, 0x0000u, CanonicalizeUnique }, + { 0x1fc8u, 0x1fcbu, 0x0056u, CanonicalizeRangeHi }, + { 0x1fccu, 0x1fcfu, 0x0000u, CanonicalizeUnique }, + { 0x1fd0u, 0x1fd1u, 0x0008u, CanonicalizeRangeLo }, + { 0x1fd2u, 0x1fd7u, 0x0000u, CanonicalizeUnique }, + { 0x1fd8u, 0x1fd9u, 0x0008u, CanonicalizeRangeHi }, + { 0x1fdau, 0x1fdbu, 0x0064u, CanonicalizeRangeHi }, + { 0x1fdcu, 0x1fdfu, 0x0000u, CanonicalizeUnique }, + { 0x1fe0u, 0x1fe1u, 0x0008u, CanonicalizeRangeLo }, + { 0x1fe2u, 0x1fe4u, 0x0000u, CanonicalizeUnique }, + { 0x1fe5u, 0x1fe5u, 0x0007u, CanonicalizeRangeLo }, + { 0x1fe6u, 0x1fe7u, 0x0000u, CanonicalizeUnique }, + { 0x1fe8u, 0x1fe9u, 0x0008u, CanonicalizeRangeHi }, + { 0x1feau, 0x1febu, 0x0070u, CanonicalizeRangeHi }, + { 0x1fecu, 0x1fecu, 0x0007u, CanonicalizeRangeHi }, + { 0x1fedu, 0x1ff7u, 0x0000u, CanonicalizeUnique }, + { 0x1ff8u, 0x1ff9u, 0x0080u, CanonicalizeRangeHi }, + { 0x1ffau, 0x1ffbu, 0x007eu, CanonicalizeRangeHi }, + { 0x1ffcu, 0x2131u, 0x0000u, CanonicalizeUnique }, + { 0x2132u, 0x2132u, 0x001cu, CanonicalizeRangeLo }, + { 0x2133u, 0x214du, 0x0000u, CanonicalizeUnique }, + { 0x214eu, 0x214eu, 0x001cu, CanonicalizeRangeHi }, + { 0x214fu, 0x215fu, 0x0000u, CanonicalizeUnique }, + { 0x2160u, 0x216fu, 0x0010u, CanonicalizeRangeLo }, + { 0x2170u, 0x217fu, 0x0010u, CanonicalizeRangeHi }, + { 0x2180u, 0x2182u, 0x0000u, CanonicalizeUnique }, + { 0x2183u, 0x2184u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2185u, 0x24b5u, 0x0000u, CanonicalizeUnique }, + { 0x24b6u, 0x24cfu, 0x001au, CanonicalizeRangeLo }, + { 0x24d0u, 0x24e9u, 0x001au, CanonicalizeRangeHi }, + { 0x24eau, 0x2bffu, 0x0000u, CanonicalizeUnique }, + { 0x2c00u, 0x2c2eu, 0x0030u, CanonicalizeRangeLo }, + { 0x2c2fu, 0x2c2fu, 0x0000u, CanonicalizeUnique }, + { 0x2c30u, 0x2c5eu, 0x0030u, CanonicalizeRangeHi }, + { 0x2c5fu, 0x2c5fu, 0x0000u, CanonicalizeUnique }, + { 0x2c60u, 0x2c61u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x2c62u, 0x2c62u, 0x29f7u, CanonicalizeRangeHi }, + { 0x2c63u, 0x2c63u, 0x0ee6u, CanonicalizeRangeHi }, + { 0x2c64u, 0x2c64u, 0x29e7u, CanonicalizeRangeHi }, + { 0x2c65u, 0x2c65u, 0x2a2bu, CanonicalizeRangeHi }, + { 0x2c66u, 0x2c66u, 0x2a28u, CanonicalizeRangeHi }, + { 0x2c67u, 0x2c6cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2c6du, 0x2c6du, 0x2a1cu, CanonicalizeRangeHi }, + { 0x2c6eu, 0x2c6eu, 0x29fdu, CanonicalizeRangeHi }, + { 0x2c6fu, 0x2c6fu, 0x2a1fu, CanonicalizeRangeHi }, + { 0x2c70u, 0x2c70u, 0x2a1eu, CanonicalizeRangeHi }, + { 0x2c71u, 0x2c71u, 0x0000u, CanonicalizeUnique }, + { 0x2c72u, 0x2c73u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x2c74u, 0x2c74u, 0x0000u, CanonicalizeUnique }, + { 0x2c75u, 0x2c76u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2c77u, 0x2c7du, 0x0000u, CanonicalizeUnique }, + { 0x2c7eu, 0x2c7fu, 0x2a3fu, CanonicalizeRangeHi }, + { 0x2c80u, 0x2ce3u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x2ce4u, 0x2ceau, 0x0000u, CanonicalizeUnique }, + { 0x2cebu, 0x2ceeu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2cefu, 0x2cffu, 0x0000u, CanonicalizeUnique }, + { 0x2d00u, 0x2d25u, 0x1c60u, CanonicalizeRangeHi }, + { 0x2d26u, 0xa63fu, 0x0000u, CanonicalizeUnique }, + { 0xa640u, 0xa66du, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa66eu, 0xa67fu, 0x0000u, CanonicalizeUnique }, + { 0xa680u, 0xa697u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa698u, 0xa721u, 0x0000u, CanonicalizeUnique }, + { 0xa722u, 0xa72fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa730u, 0xa731u, 0x0000u, CanonicalizeUnique }, + { 0xa732u, 0xa76fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa770u, 0xa778u, 0x0000u, CanonicalizeUnique }, + { 0xa779u, 0xa77cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0xa77du, 0xa77du, 0x8a04u, CanonicalizeRangeHi }, + { 0xa77eu, 0xa787u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa788u, 0xa78au, 0x0000u, CanonicalizeUnique }, + { 0xa78bu, 0xa78cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0xa78du, 0xa78du, 0xa528u, CanonicalizeRangeHi }, + { 0xa78eu, 0xa78fu, 0x0000u, CanonicalizeUnique }, + { 0xa790u, 0xa791u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa792u, 0xa79fu, 0x0000u, CanonicalizeUnique }, + { 0xa7a0u, 0xa7a9u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa7aau, 0xff20u, 0x0000u, CanonicalizeUnique }, + { 0xff21u, 0xff3au, 0x0020u, CanonicalizeRangeLo }, + { 0xff3bu, 0xff40u, 0x0000u, CanonicalizeUnique }, + { 0xff41u, 0xff5au, 0x0020u, CanonicalizeRangeHi }, + { 0xff5bu, 0xffffu, 0x0000u, CanonicalizeUnique }, +}; + +const size_t LATIN_CANONICALIZATION_RANGES = 20; +LatinCanonicalizationRange latinRangeInfo[LATIN_CANONICALIZATION_RANGES] = { + { 0x0000u, 0x0040u, 0x0000u, CanonicalizeLatinSelf }, + { 0x0041u, 0x005au, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x005bu, 0x0060u, 0x0000u, CanonicalizeLatinSelf }, + { 0x0061u, 0x007au, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x007bu, 0x00bfu, 0x0000u, CanonicalizeLatinSelf }, + { 0x00c0u, 0x00d6u, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00d7u, 0x00d7u, 0x0000u, CanonicalizeLatinSelf }, + { 0x00d8u, 0x00deu, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00dfu, 0x00dfu, 0x0000u, CanonicalizeLatinSelf }, + { 0x00e0u, 0x00f6u, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00f7u, 0x00f7u, 0x0000u, CanonicalizeLatinSelf }, + { 0x00f8u, 0x00feu, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00ffu, 0x00ffu, 0x0000u, CanonicalizeLatinSelf }, + { 0x0100u, 0x0177u, 0x0000u, CanonicalizeLatinInvalid }, + { 0x0178u, 0x0178u, 0x00ffu, CanonicalizeLatinOther }, + { 0x0179u, 0x039bu, 0x0000u, CanonicalizeLatinInvalid }, + { 0x039cu, 0x039cu, 0x00b5u, CanonicalizeLatinOther }, + { 0x039du, 0x03bbu, 0x0000u, CanonicalizeLatinInvalid }, + { 0x03bcu, 0x03bcu, 0x00b5u, CanonicalizeLatinOther }, + { 0x03bdu, 0xffffu, 0x0000u, CanonicalizeLatinInvalid }, +}; + +} } // JSC::Yarr + diff --git a/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.h b/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.h new file mode 100644 index 0000000000..be0ead43d2 --- /dev/null +++ b/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrCanonicalizeUCS2_H +#define YarrCanonicalizeUCS2_H + +#include +#include + +namespace JSC { namespace Yarr { + +// This set of data (autogenerated using YarrCanonicalizeUCS2.js into YarrCanonicalizeUCS2.cpp) +// provides information for each UCS2 code point as to the set of code points that it should +// match under the ES5.1 case insensitive RegExp matching rules, specified in 15.10.2.8. +enum UCS2CanonicalizationType { + CanonicalizeUnique, // No canonically equal values, e.g. 0x0. + CanonicalizeSet, // Value indicates a set in characterSetInfo. + CanonicalizeRangeLo, // Value is positive delta to pair, E.g. 0x41 has value 0x20, -> 0x61. + CanonicalizeRangeHi, // Value is positive delta to pair, E.g. 0x61 has value 0x20, -> 0x41. + CanonicalizeAlternatingAligned, // Aligned consequtive pair, e.g. 0x1f4,0x1f5. + CanonicalizeAlternatingUnaligned, // Unaligned consequtive pair, e.g. 0x241,0x242. +}; +struct UCS2CanonicalizationRange { uint16_t begin, end, value, type; }; +extern const size_t UCS2_CANONICALIZATION_RANGES; +extern uint16_t* characterSetInfo[]; +extern UCS2CanonicalizationRange rangeInfo[]; + +// This table is similar to the full rangeInfo table, however this maps from UCS2 codepoints to +// the set of Latin1 codepoints that could match. +enum LatinCanonicalizationType { + CanonicalizeLatinSelf, // This character is in the Latin1 range, but has no canonical equivalent in the range. + CanonicalizeLatinMask0x20, // One of a pair of characters, under the mask 0x20. + CanonicalizeLatinOther, // This character is not in the Latin1 range, but canonicalizes to another that is. + CanonicalizeLatinInvalid, // Cannot match against Latin1 input. +}; +struct LatinCanonicalizationRange { uint16_t begin, end, value, type; }; +extern const size_t LATIN_CANONICALIZATION_RANGES; +extern LatinCanonicalizationRange latinRangeInfo[]; + +// This searches in log2 time over ~364 entries, so should typically result in 8 compares. +inline UCS2CanonicalizationRange* rangeInfoFor(UChar ch) +{ + UCS2CanonicalizationRange* info = rangeInfo; + size_t entries = UCS2_CANONICALIZATION_RANGES; + + while (true) { + size_t candidate = entries >> 1; + UCS2CanonicalizationRange* candidateInfo = info + candidate; + if (ch < candidateInfo->begin) + entries = candidate; + else if (ch <= candidateInfo->end) + return candidateInfo; + else { + info = candidateInfo + 1; + entries -= (candidate + 1); + } + } +} + +// Should only be called for characters that have one canonically matching value. +inline UChar getCanonicalPair(UCS2CanonicalizationRange* info, UChar ch) +{ + ASSERT(ch >= info->begin && ch <= info->end); + switch (info->type) { + case CanonicalizeRangeLo: + return ch + info->value; + case CanonicalizeRangeHi: + return ch - info->value; + case CanonicalizeAlternatingAligned: + return ch ^ 1; + case CanonicalizeAlternatingUnaligned: + return ((ch - 1) ^ 1) + 1; + default: + ASSERT_NOT_REACHED(); + } + ASSERT_NOT_REACHED(); + return 0; +} + +// Returns true if no other UCS2 codepoint can match this value. +inline bool isCanonicallyUnique(UChar ch) +{ + return rangeInfoFor(ch)->type == CanonicalizeUnique; +} + +// Returns true if values are equal, under the canonicalization rules. +inline bool areCanonicallyEquivalent(UChar a, UChar b) +{ + UCS2CanonicalizationRange* info = rangeInfoFor(a); + switch (info->type) { + case CanonicalizeUnique: + return a == b; + case CanonicalizeSet: { + for (uint16_t* set = characterSetInfo[info->value]; (a = *set); ++set) { + if (a == b) + return true; + } + return false; + } + case CanonicalizeRangeLo: + return (a == b) || (a + info->value == b); + case CanonicalizeRangeHi: + return (a == b) || (a - info->value == b); + case CanonicalizeAlternatingAligned: + return (a | 1) == (b | 1); + case CanonicalizeAlternatingUnaligned: + return ((a - 1) | 1) == ((b - 1) | 1); + } + + ASSERT_NOT_REACHED(); + return false; +} + +} } // JSC::Yarr + +#endif diff --git a/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js b/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js new file mode 100644 index 0000000000..00361dd46e --- /dev/null +++ b/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// See ES 5.1, 15.10.2.8 +function canonicalize(ch) +{ + var u = String.fromCharCode(ch).toUpperCase(); + if (u.length > 1) + return ch; + var cu = u.charCodeAt(0); + if (ch >= 128 && cu < 128) + return ch; + return cu; +} + +var MAX_UCS2 = 0xFFFF; +var MAX_LATIN = 0xFF; + +var groupedCanonically = []; +// Pass 1: populate groupedCanonically - this is mapping from canonicalized +// values back to the set of character code that canonicalize to them. +for (var i = 0; i <= MAX_UCS2; ++i) { + var ch = canonicalize(i); + if (!groupedCanonically[ch]) + groupedCanonically[ch] = []; + groupedCanonically[ch].push(i); +} + +var typeInfo = []; +var latinTypeInfo = []; +var characterSetInfo = []; +// Pass 2: populate typeInfo & characterSetInfo. For every character calculate +// a typeInfo value, described by the types above, and a value payload. +for (cu in groupedCanonically) { + // The set of characters that canonicalize to cu + var characters = groupedCanonically[cu]; + + // If there is only one, it is unique. + if (characters.length == 1) { + typeInfo[characters[0]] = "CanonicalizeUnique:0"; + latinTypeInfo[characters[0]] = characters[0] <= MAX_LATIN ? "CanonicalizeLatinSelf:0" : "CanonicalizeLatinInvalid:0"; + continue; + } + + // Sort the array. + characters.sort(function(x,y){return x-y;}); + + // If there are more than two characters, create an entry in characterSetInfo. + if (characters.length > 2) { + for (i in characters) + typeInfo[characters[i]] = "CanonicalizeSet:" + characterSetInfo.length; + characterSetInfo.push(characters); + + if (characters[1] <= MAX_LATIN) + throw new Error("sets with more than one latin character not supported!"); + if (characters[0] <= MAX_LATIN) { + for (i in characters) + latinTypeInfo[characters[i]] = "CanonicalizeLatinOther:" + characters[0]; + latinTypeInfo[characters[0]] = "CanonicalizeLatinSelf:0"; + } else { + for (i in characters) + latinTypeInfo[characters[i]] = "CanonicalizeLatinInvalid:0"; + } + + continue; + } + + // We have a pair, mark alternating ranges, otherwise track whether this is the low or high partner. + var lo = characters[0]; + var hi = characters[1]; + var delta = hi - lo; + if (delta == 1) { + var type = lo & 1 ? "CanonicalizeAlternatingUnaligned:0" : "CanonicalizeAlternatingAligned:0"; + typeInfo[lo] = type; + typeInfo[hi] = type; + } else { + typeInfo[lo] = "CanonicalizeRangeLo:" + delta; + typeInfo[hi] = "CanonicalizeRangeHi:" + delta; + } + + if (lo > MAX_LATIN) { + latinTypeInfo[lo] = "CanonicalizeLatinInvalid:0"; + latinTypeInfo[hi] = "CanonicalizeLatinInvalid:0"; + } else if (hi > MAX_LATIN) { + latinTypeInfo[lo] = "CanonicalizeLatinSelf:0"; + latinTypeInfo[hi] = "CanonicalizeLatinOther:" + lo; + } else { + if (delta != 0x20 || lo & 0x20) + throw new Error("pairs of latin characters that don't mask with 0x20 not supported!"); + latinTypeInfo[lo] = "CanonicalizeLatinMask0x20:0"; + latinTypeInfo[hi] = "CanonicalizeLatinMask0x20:0"; + } +} + +var rangeInfo = []; +// Pass 3: coallesce types into ranges. +for (var end = 0; end <= MAX_UCS2; ++end) { + var begin = end; + var type = typeInfo[end]; + while (end < MAX_UCS2 && typeInfo[end + 1] == type) + ++end; + rangeInfo.push({begin:begin, end:end, type:type}); +} + +var latinRangeInfo = []; +// Pass 4: coallesce latin-1 types into ranges. +for (var end = 0; end <= MAX_UCS2; ++end) { + var begin = end; + var type = latinTypeInfo[end]; + while (end < MAX_UCS2 && latinTypeInfo[end + 1] == type) + ++end; + latinRangeInfo.push({begin:begin, end:end, type:type}); +} + + +// Helper function to convert a number to a fixed width hex representation of a C uint16_t. +function hex(x) +{ + var s = Number(x).toString(16); + while (s.length < 4) + s = 0 + s; + return "0x" + s + "u"; +} + +var copyright = ( + "/*" + "\n" + + " * Copyright (C) 2012 Apple Inc. All rights reserved." + "\n" + + " *" + "\n" + + " * Redistribution and use in source and binary forms, with or without" + "\n" + + " * modification, are permitted provided that the following conditions" + "\n" + + " * are met:" + "\n" + + " * 1. Redistributions of source code must retain the above copyright" + "\n" + + " * notice, this list of conditions and the following disclaimer." + "\n" + + " * 2. Redistributions in binary form must reproduce the above copyright" + "\n" + + " * notice, this list of conditions and the following disclaimer in the" + "\n" + + " * documentation and/or other materials provided with the distribution." + "\n" + + " *" + "\n" + + " * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY" + "\n" + + " * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE" + "\n" + + " * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR" + "\n" + + " * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR" + "\n" + + " * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL," + "\n" + + " * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO," + "\n" + + " * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR" + "\n" + + " * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY" + "\n" + + " * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" + "\n" + + " * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" + "\n" + + " * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. " + "\n" + + " */"); + +print(copyright); +print(); +print("// DO NOT EDIT! - this file autogenerated by YarrCanonicalizeUCS2.js"); +print(); +print('#include "config.h"'); +print('#include "YarrCanonicalizeUCS2.h"'); +print(); +print("namespace JSC { namespace Yarr {"); +print(); +print("#include "); +print(); + +for (i in characterSetInfo) { + var characters = "" + var set = characterSetInfo[i]; + for (var j in set) + characters += hex(set[j]) + ", "; + print("uint16_t ucs2CharacterSet" + i + "[] = { " + characters + "0 };"); +} +print(); +print("static const size_t UCS2_CANONICALIZATION_SETS = " + characterSetInfo.length + ";"); +print("uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = {"); +for (i in characterSetInfo) +print(" ucs2CharacterSet" + i + ","); +print("};"); +print(); +print("const size_t UCS2_CANONICALIZATION_RANGES = " + rangeInfo.length + ";"); +print("UCS2CanonicalizationRange rangeInfo[UCS2_CANONICALIZATION_RANGES] = {"); +for (i in rangeInfo) { + var info = rangeInfo[i]; + var typeAndValue = info.type.split(':'); + print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },"); +} +print("};"); +print(); +print("const size_t LATIN_CANONICALIZATION_RANGES = " + latinRangeInfo.length + ";"); +print("LatinCanonicalizationRange latinRangeInfo[LATIN_CANONICALIZATION_RANGES] = {"); +for (i in latinRangeInfo) { + var info = latinRangeInfo[i]; + var typeAndValue = info.type.split(':'); + print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },"); +} +print("};"); +print(); +print("} } // JSC::Yarr"); +print(); + diff --git a/src/3rdparty/masm/yarr/YarrInterpreter.cpp b/src/3rdparty/masm/yarr/YarrInterpreter.cpp new file mode 100644 index 0000000000..31603f6d34 --- /dev/null +++ b/src/3rdparty/masm/yarr/YarrInterpreter.cpp @@ -0,0 +1,1964 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "YarrInterpreter.h" + +#include "Yarr.h" +#include "YarrCanonicalizeUCS2.h" +#include +#include +#include +#include + +#ifndef NDEBUG +#include +#endif + +using namespace WTF; + +namespace JSC { namespace Yarr { + +template +class Interpreter { +public: + struct ParenthesesDisjunctionContext; + + struct BackTrackInfoPatternCharacter { + uintptr_t matchAmount; + }; + struct BackTrackInfoCharacterClass { + uintptr_t matchAmount; + }; + struct BackTrackInfoBackReference { + uintptr_t begin; // Not really needed for greedy quantifiers. + uintptr_t matchAmount; // Not really needed for fixed quantifiers. + }; + struct BackTrackInfoAlternative { + uintptr_t offset; + }; + struct BackTrackInfoParentheticalAssertion { + uintptr_t begin; + }; + struct BackTrackInfoParenthesesOnce { + uintptr_t begin; + }; + struct BackTrackInfoParenthesesTerminal { + uintptr_t begin; + }; + struct BackTrackInfoParentheses { + uintptr_t matchAmount; + ParenthesesDisjunctionContext* lastContext; + }; + + static inline void appendParenthesesDisjunctionContext(BackTrackInfoParentheses* backTrack, ParenthesesDisjunctionContext* context) + { + context->next = backTrack->lastContext; + backTrack->lastContext = context; + ++backTrack->matchAmount; + } + + static inline void popParenthesesDisjunctionContext(BackTrackInfoParentheses* backTrack) + { + ASSERT(backTrack->matchAmount); + ASSERT(backTrack->lastContext); + backTrack->lastContext = backTrack->lastContext->next; + --backTrack->matchAmount; + } + + struct DisjunctionContext + { + DisjunctionContext() + : term(0) + { + } + + void* operator new(size_t, void* where) + { + return where; + } + + int term; + unsigned matchBegin; + unsigned matchEnd; + uintptr_t frame[1]; + }; + + DisjunctionContext* allocDisjunctionContext(ByteDisjunction* disjunction) + { + size_t size = sizeof(DisjunctionContext) - sizeof(uintptr_t) + disjunction->m_frameSize * sizeof(uintptr_t); + allocatorPool = allocatorPool->ensureCapacity(size); + if (!allocatorPool) + CRASH(); + return new (allocatorPool->alloc(size)) DisjunctionContext(); + } + + void freeDisjunctionContext(DisjunctionContext* context) + { + allocatorPool = allocatorPool->dealloc(context); + } + + struct ParenthesesDisjunctionContext + { + ParenthesesDisjunctionContext(unsigned* output, ByteTerm& term) + : next(0) + { + unsigned firstSubpatternId = term.atom.subpatternId; + unsigned numNestedSubpatterns = term.atom.parenthesesDisjunction->m_numSubpatterns; + + for (unsigned i = 0; i < (numNestedSubpatterns << 1); ++i) { + subpatternBackup[i] = output[(firstSubpatternId << 1) + i]; + output[(firstSubpatternId << 1) + i] = offsetNoMatch; + } + + new (getDisjunctionContext(term)) DisjunctionContext(); + } + + void* operator new(size_t, void* where) + { + return where; + } + + void restoreOutput(unsigned* output, unsigned firstSubpatternId, unsigned numNestedSubpatterns) + { + for (unsigned i = 0; i < (numNestedSubpatterns << 1); ++i) + output[(firstSubpatternId << 1) + i] = subpatternBackup[i]; + } + + DisjunctionContext* getDisjunctionContext(ByteTerm& term) + { + return reinterpret_cast(&(subpatternBackup[term.atom.parenthesesDisjunction->m_numSubpatterns << 1])); + } + + ParenthesesDisjunctionContext* next; + unsigned subpatternBackup[1]; + }; + + ParenthesesDisjunctionContext* allocParenthesesDisjunctionContext(ByteDisjunction* disjunction, unsigned* output, ByteTerm& term) + { + size_t size = sizeof(ParenthesesDisjunctionContext) - sizeof(unsigned) + (term.atom.parenthesesDisjunction->m_numSubpatterns << 1) * sizeof(unsigned) + sizeof(DisjunctionContext) - sizeof(uintptr_t) + disjunction->m_frameSize * sizeof(uintptr_t); + allocatorPool = allocatorPool->ensureCapacity(size); + if (!allocatorPool) + CRASH(); + return new (allocatorPool->alloc(size)) ParenthesesDisjunctionContext(output, term); + } + + void freeParenthesesDisjunctionContext(ParenthesesDisjunctionContext* context) + { + allocatorPool = allocatorPool->dealloc(context); + } + + class InputStream { + public: + InputStream(const CharType* input, unsigned start, unsigned length) + : input(input) + , pos(start) + , length(length) + { + } + + void next() + { + ++pos; + } + + void rewind(unsigned amount) + { + ASSERT(pos >= amount); + pos -= amount; + } + + int read() + { + ASSERT(pos < length); + if (pos < length) + return input[pos]; + return -1; + } + + int readPair() + { + ASSERT(pos + 1 < length); + return input[pos] | input[pos + 1] << 16; + } + + int readChecked(unsigned negativePositionOffest) + { + if (pos < negativePositionOffest) + CRASH(); + unsigned p = pos - negativePositionOffest; + ASSERT(p < length); + return input[p]; + } + + int reread(unsigned from) + { + ASSERT(from < length); + return input[from]; + } + + int prev() + { + ASSERT(!(pos > length)); + if (pos && length) + return input[pos - 1]; + return -1; + } + + unsigned getPos() + { + return pos; + } + + void setPos(unsigned p) + { + pos = p; + } + + bool atStart() + { + return pos == 0; + } + + bool atEnd() + { + return pos == length; + } + + unsigned end() + { + return length; + } + + bool checkInput(unsigned count) + { + if (((pos + count) <= length) && ((pos + count) >= pos)) { + pos += count; + return true; + } + return false; + } + + void uncheckInput(unsigned count) + { + if (pos < count) + CRASH(); + pos -= count; + } + + bool atStart(unsigned negativePositionOffest) + { + return pos == negativePositionOffest; + } + + bool atEnd(unsigned negativePositionOffest) + { + if (pos < negativePositionOffest) + CRASH(); + return (pos - negativePositionOffest) == length; + } + + bool isAvailableInput(unsigned offset) + { + return (((pos + offset) <= length) && ((pos + offset) >= pos)); + } + + private: + const CharType* input; + unsigned pos; + unsigned length; + }; + + bool testCharacterClass(CharacterClass* characterClass, int ch) + { + if (ch & 0xFF80) { + for (unsigned i = 0; i < characterClass->m_matchesUnicode.size(); ++i) + if (ch == characterClass->m_matchesUnicode[i]) + return true; + for (unsigned i = 0; i < characterClass->m_rangesUnicode.size(); ++i) + if ((ch >= characterClass->m_rangesUnicode[i].begin) && (ch <= characterClass->m_rangesUnicode[i].end)) + return true; + } else { + for (unsigned i = 0; i < characterClass->m_matches.size(); ++i) + if (ch == characterClass->m_matches[i]) + return true; + for (unsigned i = 0; i < characterClass->m_ranges.size(); ++i) + if ((ch >= characterClass->m_ranges[i].begin) && (ch <= characterClass->m_ranges[i].end)) + return true; + } + + return false; + } + + bool checkCharacter(int testChar, unsigned negativeInputOffset) + { + return testChar == input.readChecked(negativeInputOffset); + } + + bool checkCasedCharacter(int loChar, int hiChar, unsigned negativeInputOffset) + { + int ch = input.readChecked(negativeInputOffset); + return (loChar == ch) || (hiChar == ch); + } + + bool checkCharacterClass(CharacterClass* characterClass, bool invert, unsigned negativeInputOffset) + { + bool match = testCharacterClass(characterClass, input.readChecked(negativeInputOffset)); + return invert ? !match : match; + } + + bool tryConsumeBackReference(int matchBegin, int matchEnd, unsigned negativeInputOffset) + { + unsigned matchSize = (unsigned)(matchEnd - matchBegin); + + if (!input.checkInput(matchSize)) + return false; + + if (pattern->m_ignoreCase) { + for (unsigned i = 0; i < matchSize; ++i) { + int oldCh = input.reread(matchBegin + i); + int ch = input.readChecked(negativeInputOffset + matchSize - i); + + if (oldCh == ch) + continue; + + // The definition for canonicalize (see ES 5.1, 15.10.2.8) means that + // unicode values are never allowed to match against ascii ones. + if (isASCII(oldCh) || isASCII(ch)) { + if (toASCIIUpper(oldCh) == toASCIIUpper(ch)) + continue; + } else if (areCanonicallyEquivalent(oldCh, ch)) + continue; + + input.uncheckInput(matchSize); + return false; + } + } else { + for (unsigned i = 0; i < matchSize; ++i) { + if (!checkCharacter(input.reread(matchBegin + i), negativeInputOffset + matchSize - i)) { + input.uncheckInput(matchSize); + return false; + } + } + } + + return true; + } + + bool matchAssertionBOL(ByteTerm& term) + { + return (input.atStart(term.inputPosition)) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition + 1))); + } + + bool matchAssertionEOL(ByteTerm& term) + { + if (term.inputPosition) + return (input.atEnd(term.inputPosition)) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition))); + + return (input.atEnd()) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.read())); + } + + bool matchAssertionWordBoundary(ByteTerm& term) + { + bool prevIsWordchar = !input.atStart(term.inputPosition) && testCharacterClass(pattern->wordcharCharacterClass, input.readChecked(term.inputPosition + 1)); + bool readIsWordchar; + if (term.inputPosition) + readIsWordchar = !input.atEnd(term.inputPosition) && testCharacterClass(pattern->wordcharCharacterClass, input.readChecked(term.inputPosition)); + else + readIsWordchar = !input.atEnd() && testCharacterClass(pattern->wordcharCharacterClass, input.read()); + + bool wordBoundary = prevIsWordchar != readIsWordchar; + return term.invert() ? !wordBoundary : wordBoundary; + } + + bool backtrackPatternCharacter(ByteTerm& term, DisjunctionContext* context) + { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierFixedCount: + break; + + case QuantifierGreedy: + if (backTrack->matchAmount) { + --backTrack->matchAmount; + input.uncheckInput(1); + return true; + } + break; + + case QuantifierNonGreedy: + if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { + ++backTrack->matchAmount; + if (checkCharacter(term.atom.patternCharacter, term.inputPosition + 1)) + return true; + } + input.uncheckInput(backTrack->matchAmount); + break; + } + + return false; + } + + bool backtrackPatternCasedCharacter(ByteTerm& term, DisjunctionContext* context) + { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierFixedCount: + break; + + case QuantifierGreedy: + if (backTrack->matchAmount) { + --backTrack->matchAmount; + input.uncheckInput(1); + return true; + } + break; + + case QuantifierNonGreedy: + if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { + ++backTrack->matchAmount; + if (checkCasedCharacter(term.atom.casedCharacter.lo, term.atom.casedCharacter.hi, term.inputPosition + 1)) + return true; + } + input.uncheckInput(backTrack->matchAmount); + break; + } + + return false; + } + + bool matchCharacterClass(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeCharacterClass); + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierFixedCount: { + for (unsigned matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) { + if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition - matchAmount)) + return false; + } + return true; + } + + case QuantifierGreedy: { + unsigned matchAmount = 0; + while ((matchAmount < term.atom.quantityCount) && input.checkInput(1)) { + if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) { + input.uncheckInput(1); + break; + } + ++matchAmount; + } + backTrack->matchAmount = matchAmount; + + return true; + } + + case QuantifierNonGreedy: + backTrack->matchAmount = 0; + return true; + } + + ASSERT_NOT_REACHED(); + return false; + } + + bool backtrackCharacterClass(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeCharacterClass); + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierFixedCount: + break; + + case QuantifierGreedy: + if (backTrack->matchAmount) { + --backTrack->matchAmount; + input.uncheckInput(1); + return true; + } + break; + + case QuantifierNonGreedy: + if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { + ++backTrack->matchAmount; + if (checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) + return true; + } + input.uncheckInput(backTrack->matchAmount); + break; + } + + return false; + } + + bool matchBackReference(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeBackReference); + BackTrackInfoBackReference* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + unsigned matchBegin = output[(term.atom.subpatternId << 1)]; + unsigned matchEnd = output[(term.atom.subpatternId << 1) + 1]; + + // If the end position of the referenced match hasn't set yet then the backreference in the same parentheses where it references to that. + // In this case the result of match is empty string like when it references to a parentheses with zero-width match. + // Eg.: /(a\1)/ + if (matchEnd == offsetNoMatch) + return true; + + if (matchBegin == offsetNoMatch) + return true; + + ASSERT(matchBegin <= matchEnd); + + if (matchBegin == matchEnd) + return true; + + switch (term.atom.quantityType) { + case QuantifierFixedCount: { + backTrack->begin = input.getPos(); + for (unsigned matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) { + if (!tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) { + input.setPos(backTrack->begin); + return false; + } + } + return true; + } + + case QuantifierGreedy: { + unsigned matchAmount = 0; + while ((matchAmount < term.atom.quantityCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) + ++matchAmount; + backTrack->matchAmount = matchAmount; + return true; + } + + case QuantifierNonGreedy: + backTrack->begin = input.getPos(); + backTrack->matchAmount = 0; + return true; + } + + ASSERT_NOT_REACHED(); + return false; + } + + bool backtrackBackReference(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeBackReference); + BackTrackInfoBackReference* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + unsigned matchBegin = output[(term.atom.subpatternId << 1)]; + unsigned matchEnd = output[(term.atom.subpatternId << 1) + 1]; + + if (matchBegin == offsetNoMatch) + return false; + + ASSERT(matchBegin <= matchEnd); + + if (matchBegin == matchEnd) + return false; + + switch (term.atom.quantityType) { + case QuantifierFixedCount: + // for quantityCount == 1, could rewind. + input.setPos(backTrack->begin); + break; + + case QuantifierGreedy: + if (backTrack->matchAmount) { + --backTrack->matchAmount; + input.rewind(matchEnd - matchBegin); + return true; + } + break; + + case QuantifierNonGreedy: + if ((backTrack->matchAmount < term.atom.quantityCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) { + ++backTrack->matchAmount; + return true; + } + input.setPos(backTrack->begin); + break; + } + + return false; + } + + void recordParenthesesMatch(ByteTerm& term, ParenthesesDisjunctionContext* context) + { + if (term.capture()) { + unsigned subpatternId = term.atom.subpatternId; + output[(subpatternId << 1)] = context->getDisjunctionContext(term)->matchBegin + term.inputPosition; + output[(subpatternId << 1) + 1] = context->getDisjunctionContext(term)->matchEnd + term.inputPosition; + } + } + void resetMatches(ByteTerm& term, ParenthesesDisjunctionContext* context) + { + unsigned firstSubpatternId = term.atom.subpatternId; + unsigned count = term.atom.parenthesesDisjunction->m_numSubpatterns; + context->restoreOutput(output, firstSubpatternId, count); + } + JSRegExpResult parenthesesDoBacktrack(ByteTerm& term, BackTrackInfoParentheses* backTrack) + { + while (backTrack->matchAmount) { + ParenthesesDisjunctionContext* context = backTrack->lastContext; + + JSRegExpResult result = matchDisjunction(term.atom.parenthesesDisjunction, context->getDisjunctionContext(term), true); + if (result == JSRegExpMatch) + return JSRegExpMatch; + + resetMatches(term, context); + popParenthesesDisjunctionContext(backTrack); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + } + + return JSRegExpNoMatch; + } + + bool matchParenthesesOnceBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierGreedy: { + // set this speculatively; if we get to the parens end this will be true. + backTrack->begin = input.getPos(); + break; + } + case QuantifierNonGreedy: { + backTrack->begin = notFound; + context->term += term.atom.parenthesesWidth; + return true; + } + case QuantifierFixedCount: + break; + } + + if (term.capture()) { + unsigned subpatternId = term.atom.subpatternId; + output[(subpatternId << 1)] = input.getPos() - term.inputPosition; + } + + return true; + } + + bool matchParenthesesOnceEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceEnd); + ASSERT(term.atom.quantityCount == 1); + + if (term.capture()) { + unsigned subpatternId = term.atom.subpatternId; + output[(subpatternId << 1) + 1] = input.getPos() + term.inputPosition; + } + + if (term.atom.quantityType == QuantifierFixedCount) + return true; + + BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); + return backTrack->begin != input.getPos(); + } + + bool backtrackParenthesesOnceBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + if (term.capture()) { + unsigned subpatternId = term.atom.subpatternId; + output[(subpatternId << 1)] = offsetNoMatch; + output[(subpatternId << 1) + 1] = offsetNoMatch; + } + + switch (term.atom.quantityType) { + case QuantifierGreedy: + // if we backtrack to this point, there is another chance - try matching nothing. + ASSERT(backTrack->begin != notFound); + backTrack->begin = notFound; + context->term += term.atom.parenthesesWidth; + return true; + case QuantifierNonGreedy: + ASSERT(backTrack->begin != notFound); + case QuantifierFixedCount: + break; + } + + return false; + } + + bool backtrackParenthesesOnceEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceEnd); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + switch (term.atom.quantityType) { + case QuantifierGreedy: + if (backTrack->begin == notFound) { + context->term -= term.atom.parenthesesWidth; + return false; + } + case QuantifierNonGreedy: + if (backTrack->begin == notFound) { + backTrack->begin = input.getPos(); + if (term.capture()) { + // Technically this access to inputPosition should be accessing the begin term's + // inputPosition, but for repeats other than fixed these values should be + // the same anyway! (We don't pre-check for greedy or non-greedy matches.) + ASSERT((&term - term.atom.parenthesesWidth)->type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + ASSERT((&term - term.atom.parenthesesWidth)->inputPosition == term.inputPosition); + unsigned subpatternId = term.atom.subpatternId; + output[subpatternId << 1] = input.getPos() + term.inputPosition; + } + context->term -= term.atom.parenthesesWidth; + return true; + } + case QuantifierFixedCount: + break; + } + + return false; + } + + bool matchParenthesesTerminalBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); + ASSERT(term.atom.quantityType == QuantifierGreedy); + ASSERT(term.atom.quantityCount == quantifyInfinite); + ASSERT(!term.capture()); + + BackTrackInfoParenthesesTerminal* backTrack = reinterpret_cast(context->frame + term.frameLocation); + backTrack->begin = input.getPos(); + return true; + } + + bool matchParenthesesTerminalEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalEnd); + + BackTrackInfoParenthesesTerminal* backTrack = reinterpret_cast(context->frame + term.frameLocation); + // Empty match is a failed match. + if (backTrack->begin == input.getPos()) + return false; + + // Successful match! Okay, what's next? - loop around and try to match moar! + context->term -= (term.atom.parenthesesWidth + 1); + return true; + } + + bool backtrackParenthesesTerminalBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); + ASSERT(term.atom.quantityType == QuantifierGreedy); + ASSERT(term.atom.quantityCount == quantifyInfinite); + ASSERT(!term.capture()); + + // If we backtrack to this point, we have failed to match this iteration of the parens. + // Since this is greedy / zero minimum a failed is also accepted as a match! + context->term += term.atom.parenthesesWidth; + return true; + } + + bool backtrackParenthesesTerminalEnd(ByteTerm&, DisjunctionContext*) + { + // 'Terminal' parentheses are at the end of the regex, and as such a match past end + // should always be returned as a successful match - we should never backtrack to here. + ASSERT_NOT_REACHED(); + return false; + } + + bool matchParentheticalAssertionBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParentheticalAssertionBegin); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + backTrack->begin = input.getPos(); + return true; + } + + bool matchParentheticalAssertionEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParentheticalAssertionEnd); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + input.setPos(backTrack->begin); + + // We've reached the end of the parens; if they are inverted, this is failure. + if (term.invert()) { + context->term -= term.atom.parenthesesWidth; + return false; + } + + return true; + } + + bool backtrackParentheticalAssertionBegin(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParentheticalAssertionBegin); + ASSERT(term.atom.quantityCount == 1); + + // We've failed to match parens; if they are inverted, this is win! + if (term.invert()) { + context->term += term.atom.parenthesesWidth; + return true; + } + + return false; + } + + bool backtrackParentheticalAssertionEnd(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParentheticalAssertionEnd); + ASSERT(term.atom.quantityCount == 1); + + BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast(context->frame + term.frameLocation); + + input.setPos(backTrack->begin); + + context->term -= term.atom.parenthesesWidth; + return false; + } + + JSRegExpResult matchParentheses(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpattern); + + BackTrackInfoParentheses* backTrack = reinterpret_cast(context->frame + term.frameLocation); + ByteDisjunction* disjunctionBody = term.atom.parenthesesDisjunction; + + backTrack->matchAmount = 0; + backTrack->lastContext = 0; + + switch (term.atom.quantityType) { + case QuantifierFixedCount: { + // While we haven't yet reached our fixed limit, + while (backTrack->matchAmount < term.atom.quantityCount) { + // Try to do a match, and it it succeeds, add it to the list. + ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + JSRegExpResult result = matchDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + if (result == JSRegExpMatch) + appendParenthesesDisjunctionContext(backTrack, context); + else { + // The match failed; try to find an alternate point to carry on from. + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack); + if (backtrackResult != JSRegExpMatch) + return backtrackResult; + } + } + + ASSERT(backTrack->matchAmount == term.atom.quantityCount); + ParenthesesDisjunctionContext* context = backTrack->lastContext; + recordParenthesesMatch(term, context); + return JSRegExpMatch; + } + + case QuantifierGreedy: { + while (backTrack->matchAmount < term.atom.quantityCount) { + ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + if (result == JSRegExpMatch) + appendParenthesesDisjunctionContext(backTrack, context); + else { + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + + break; + } + } + + if (backTrack->matchAmount) { + ParenthesesDisjunctionContext* context = backTrack->lastContext; + recordParenthesesMatch(term, context); + } + return JSRegExpMatch; + } + + case QuantifierNonGreedy: + return JSRegExpMatch; + } + + ASSERT_NOT_REACHED(); + return JSRegExpErrorNoMatch; + } + + // Rules for backtracking differ depending on whether this is greedy or non-greedy. + // + // Greedy matches never should try just adding more - you should already have done + // the 'more' cases. Always backtrack, at least a leetle bit. However cases where + // you backtrack an item off the list needs checking, since we'll never have matched + // the one less case. Tracking forwards, still add as much as possible. + // + // Non-greedy, we've already done the one less case, so don't match on popping. + // We haven't done the one more case, so always try to add that. + // + JSRegExpResult backtrackParentheses(ByteTerm& term, DisjunctionContext* context) + { + ASSERT(term.type == ByteTerm::TypeParenthesesSubpattern); + + BackTrackInfoParentheses* backTrack = reinterpret_cast(context->frame + term.frameLocation); + ByteDisjunction* disjunctionBody = term.atom.parenthesesDisjunction; + + switch (term.atom.quantityType) { + case QuantifierFixedCount: { + ASSERT(backTrack->matchAmount == term.atom.quantityCount); + + ParenthesesDisjunctionContext* context = 0; + JSRegExpResult result = parenthesesDoBacktrack(term, backTrack); + + if (result != JSRegExpMatch) + return result; + + // While we haven't yet reached our fixed limit, + while (backTrack->matchAmount < term.atom.quantityCount) { + // Try to do a match, and it it succeeds, add it to the list. + context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + result = matchDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + + if (result == JSRegExpMatch) + appendParenthesesDisjunctionContext(backTrack, context); + else { + // The match failed; try to find an alternate point to carry on from. + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack); + if (backtrackResult != JSRegExpMatch) + return backtrackResult; + } + } + + ASSERT(backTrack->matchAmount == term.atom.quantityCount); + context = backTrack->lastContext; + recordParenthesesMatch(term, context); + return JSRegExpMatch; + } + + case QuantifierGreedy: { + if (!backTrack->matchAmount) + return JSRegExpNoMatch; + + ParenthesesDisjunctionContext* context = backTrack->lastContext; + JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term), true); + if (result == JSRegExpMatch) { + while (backTrack->matchAmount < term.atom.quantityCount) { + ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + JSRegExpResult parenthesesResult = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + if (parenthesesResult == JSRegExpMatch) + appendParenthesesDisjunctionContext(backTrack, context); + else { + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (parenthesesResult != JSRegExpNoMatch) + return parenthesesResult; + + break; + } + } + } else { + resetMatches(term, context); + popParenthesesDisjunctionContext(backTrack); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + } + + if (backTrack->matchAmount) { + ParenthesesDisjunctionContext* context = backTrack->lastContext; + recordParenthesesMatch(term, context); + } + return JSRegExpMatch; + } + + case QuantifierNonGreedy: { + // If we've not reached the limit, try to add one more match. + if (backTrack->matchAmount < term.atom.quantityCount) { + ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term); + JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term)); + if (result == JSRegExpMatch) { + appendParenthesesDisjunctionContext(backTrack, context); + recordParenthesesMatch(term, context); + return JSRegExpMatch; + } + + resetMatches(term, context); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + } + + // Nope - okay backtrack looking for an alternative. + while (backTrack->matchAmount) { + ParenthesesDisjunctionContext* context = backTrack->lastContext; + JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term), true); + if (result == JSRegExpMatch) { + // successful backtrack! we're back in the game! + if (backTrack->matchAmount) { + context = backTrack->lastContext; + recordParenthesesMatch(term, context); + } + return JSRegExpMatch; + } + + // pop a match off the stack + resetMatches(term, context); + popParenthesesDisjunctionContext(backTrack); + freeParenthesesDisjunctionContext(context); + + if (result != JSRegExpNoMatch) + return result; + } + + return JSRegExpNoMatch; + } + } + + ASSERT_NOT_REACHED(); + return JSRegExpErrorNoMatch; + } + + bool matchDotStarEnclosure(ByteTerm& term, DisjunctionContext* context) + { + UNUSED_PARAM(term); + unsigned matchBegin = context->matchBegin; + + if (matchBegin) { + for (matchBegin--; true; matchBegin--) { + if (testCharacterClass(pattern->newlineCharacterClass, input.reread(matchBegin))) { + ++matchBegin; + break; + } + + if (!matchBegin) + break; + } + } + + unsigned matchEnd = input.getPos(); + + for (; (matchEnd != input.end()) + && (!testCharacterClass(pattern->newlineCharacterClass, input.reread(matchEnd))); matchEnd++) { } + + if (((matchBegin && term.anchors.m_bol) + || ((matchEnd != input.end()) && term.anchors.m_eol)) + && !pattern->m_multiline) + return false; + + context->matchBegin = matchBegin; + context->matchEnd = matchEnd; + return true; + } + +#define MATCH_NEXT() { ++context->term; goto matchAgain; } +#define BACKTRACK() { --context->term; goto backtrack; } +#define currentTerm() (disjunction->terms[context->term]) + JSRegExpResult matchDisjunction(ByteDisjunction* disjunction, DisjunctionContext* context, bool btrack = false) + { + if (!--remainingMatchCount) + return JSRegExpErrorHitLimit; + + if (btrack) + BACKTRACK(); + + context->matchBegin = input.getPos(); + context->term = 0; + + matchAgain: + ASSERT(context->term < static_cast(disjunction->terms.size())); + + switch (currentTerm().type) { + case ByteTerm::TypeSubpatternBegin: + MATCH_NEXT(); + case ByteTerm::TypeSubpatternEnd: + context->matchEnd = input.getPos(); + return JSRegExpMatch; + + case ByteTerm::TypeBodyAlternativeBegin: + MATCH_NEXT(); + case ByteTerm::TypeBodyAlternativeDisjunction: + case ByteTerm::TypeBodyAlternativeEnd: + context->matchEnd = input.getPos(); + return JSRegExpMatch; + + case ByteTerm::TypeAlternativeBegin: + MATCH_NEXT(); + case ByteTerm::TypeAlternativeDisjunction: + case ByteTerm::TypeAlternativeEnd: { + int offset = currentTerm().alternative.end; + BackTrackInfoAlternative* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + backTrack->offset = offset; + context->term += offset; + MATCH_NEXT(); + } + + case ByteTerm::TypeAssertionBOL: + if (matchAssertionBOL(currentTerm())) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeAssertionEOL: + if (matchAssertionEOL(currentTerm())) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeAssertionWordBoundary: + if (matchAssertionWordBoundary(currentTerm())) + MATCH_NEXT(); + BACKTRACK(); + + case ByteTerm::TypePatternCharacterOnce: + case ByteTerm::TypePatternCharacterFixed: { + for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityCount; ++matchAmount) { + if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition - matchAmount)) + BACKTRACK(); + } + MATCH_NEXT(); + } + case ByteTerm::TypePatternCharacterGreedy: { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + unsigned matchAmount = 0; + while ((matchAmount < currentTerm().atom.quantityCount) && input.checkInput(1)) { + if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition + 1)) { + input.uncheckInput(1); + break; + } + ++matchAmount; + } + backTrack->matchAmount = matchAmount; + + MATCH_NEXT(); + } + case ByteTerm::TypePatternCharacterNonGreedy: { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + backTrack->matchAmount = 0; + MATCH_NEXT(); + } + + case ByteTerm::TypePatternCasedCharacterOnce: + case ByteTerm::TypePatternCasedCharacterFixed: { + for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityCount; ++matchAmount) { + if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition - matchAmount)) + BACKTRACK(); + } + MATCH_NEXT(); + } + case ByteTerm::TypePatternCasedCharacterGreedy: { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + unsigned matchAmount = 0; + while ((matchAmount < currentTerm().atom.quantityCount) && input.checkInput(1)) { + if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition + 1)) { + input.uncheckInput(1); + break; + } + ++matchAmount; + } + backTrack->matchAmount = matchAmount; + + MATCH_NEXT(); + } + case ByteTerm::TypePatternCasedCharacterNonGreedy: { + BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + backTrack->matchAmount = 0; + MATCH_NEXT(); + } + + case ByteTerm::TypeCharacterClass: + if (matchCharacterClass(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeBackReference: + if (matchBackReference(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpattern: { + JSRegExpResult result = matchParentheses(currentTerm(), context); + + if (result == JSRegExpMatch) { + MATCH_NEXT(); + } else if (result != JSRegExpNoMatch) + return result; + + BACKTRACK(); + } + case ByteTerm::TypeParenthesesSubpatternOnceBegin: + if (matchParenthesesOnceBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternOnceEnd: + if (matchParenthesesOnceEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternTerminalBegin: + if (matchParenthesesTerminalBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternTerminalEnd: + if (matchParenthesesTerminalEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParentheticalAssertionBegin: + if (matchParentheticalAssertionBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParentheticalAssertionEnd: + if (matchParentheticalAssertionEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + + case ByteTerm::TypeCheckInput: + if (input.checkInput(currentTerm().checkInputCount)) + MATCH_NEXT(); + BACKTRACK(); + + case ByteTerm::TypeUncheckInput: + input.uncheckInput(currentTerm().checkInputCount); + MATCH_NEXT(); + + case ByteTerm::TypeDotStarEnclosure: + if (matchDotStarEnclosure(currentTerm(), context)) + return JSRegExpMatch; + BACKTRACK(); + } + + // We should never fall-through to here. + ASSERT_NOT_REACHED(); + + backtrack: + ASSERT(context->term < static_cast(disjunction->terms.size())); + + switch (currentTerm().type) { + case ByteTerm::TypeSubpatternBegin: + return JSRegExpNoMatch; + case ByteTerm::TypeSubpatternEnd: + ASSERT_NOT_REACHED(); + + case ByteTerm::TypeBodyAlternativeBegin: + case ByteTerm::TypeBodyAlternativeDisjunction: { + int offset = currentTerm().alternative.next; + context->term += offset; + if (offset > 0) + MATCH_NEXT(); + + if (input.atEnd()) + return JSRegExpNoMatch; + + input.next(); + + context->matchBegin = input.getPos(); + + if (currentTerm().alternative.onceThrough) + context->term += currentTerm().alternative.next; + + MATCH_NEXT(); + } + case ByteTerm::TypeBodyAlternativeEnd: + ASSERT_NOT_REACHED(); + + case ByteTerm::TypeAlternativeBegin: + case ByteTerm::TypeAlternativeDisjunction: { + int offset = currentTerm().alternative.next; + context->term += offset; + if (offset > 0) + MATCH_NEXT(); + BACKTRACK(); + } + case ByteTerm::TypeAlternativeEnd: { + // We should never backtrack back into an alternative of the main body of the regex. + BackTrackInfoAlternative* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + unsigned offset = backTrack->offset; + context->term -= offset; + BACKTRACK(); + } + + case ByteTerm::TypeAssertionBOL: + case ByteTerm::TypeAssertionEOL: + case ByteTerm::TypeAssertionWordBoundary: + BACKTRACK(); + + case ByteTerm::TypePatternCharacterOnce: + case ByteTerm::TypePatternCharacterFixed: + case ByteTerm::TypePatternCharacterGreedy: + case ByteTerm::TypePatternCharacterNonGreedy: + if (backtrackPatternCharacter(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypePatternCasedCharacterOnce: + case ByteTerm::TypePatternCasedCharacterFixed: + case ByteTerm::TypePatternCasedCharacterGreedy: + case ByteTerm::TypePatternCasedCharacterNonGreedy: + if (backtrackPatternCasedCharacter(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeCharacterClass: + if (backtrackCharacterClass(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeBackReference: + if (backtrackBackReference(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpattern: { + JSRegExpResult result = backtrackParentheses(currentTerm(), context); + + if (result == JSRegExpMatch) { + MATCH_NEXT(); + } else if (result != JSRegExpNoMatch) + return result; + + BACKTRACK(); + } + case ByteTerm::TypeParenthesesSubpatternOnceBegin: + if (backtrackParenthesesOnceBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternOnceEnd: + if (backtrackParenthesesOnceEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternTerminalBegin: + if (backtrackParenthesesTerminalBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternTerminalEnd: + if (backtrackParenthesesTerminalEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParentheticalAssertionBegin: + if (backtrackParentheticalAssertionBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParentheticalAssertionEnd: + if (backtrackParentheticalAssertionEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + + case ByteTerm::TypeCheckInput: + input.uncheckInput(currentTerm().checkInputCount); + BACKTRACK(); + + case ByteTerm::TypeUncheckInput: + input.checkInput(currentTerm().checkInputCount); + BACKTRACK(); + + case ByteTerm::TypeDotStarEnclosure: + ASSERT_NOT_REACHED(); + } + + ASSERT_NOT_REACHED(); + return JSRegExpErrorNoMatch; + } + + JSRegExpResult matchNonZeroDisjunction(ByteDisjunction* disjunction, DisjunctionContext* context, bool btrack = false) + { + JSRegExpResult result = matchDisjunction(disjunction, context, btrack); + + if (result == JSRegExpMatch) { + while (context->matchBegin == context->matchEnd) { + result = matchDisjunction(disjunction, context, true); + if (result != JSRegExpMatch) + return result; + } + return JSRegExpMatch; + } + + return result; + } + + unsigned interpret() + { + if (!input.isAvailableInput(0)) + return offsetNoMatch; + + for (unsigned i = 0; i < pattern->m_body->m_numSubpatterns + 1; ++i) + output[i << 1] = offsetNoMatch; + + allocatorPool = pattern->m_allocator->startAllocator(); + if (!allocatorPool) + CRASH(); + + DisjunctionContext* context = allocDisjunctionContext(pattern->m_body.get()); + + JSRegExpResult result = matchDisjunction(pattern->m_body.get(), context, false); + if (result == JSRegExpMatch) { + output[0] = context->matchBegin; + output[1] = context->matchEnd; + } + + freeDisjunctionContext(context); + + pattern->m_allocator->stopAllocator(); + + ASSERT((result == JSRegExpMatch) == (output[0] != offsetNoMatch)); + return output[0]; + } + + Interpreter(BytecodePattern* pattern, unsigned* output, const CharType* input, unsigned length, unsigned start) + : pattern(pattern) + , output(output) + , input(input, start, length) + , allocatorPool(0) + , remainingMatchCount(matchLimit) + { + } + +private: + BytecodePattern* pattern; + unsigned* output; + InputStream input; + BumpPointerPool* allocatorPool; + unsigned remainingMatchCount; +}; + + + +class ByteCompiler { + struct ParenthesesStackEntry { + unsigned beginTerm; + unsigned savedAlternativeIndex; + ParenthesesStackEntry(unsigned beginTerm, unsigned savedAlternativeIndex/*, unsigned subpatternId, bool capture = false*/) + : beginTerm(beginTerm) + , savedAlternativeIndex(savedAlternativeIndex) + { + } + }; + +public: + ByteCompiler(YarrPattern& pattern) + : m_pattern(pattern) + { + m_currentAlternativeIndex = 0; + } + + PassOwnPtr compile(BumpPointerAllocator* allocator) + { + regexBegin(m_pattern.m_numSubpatterns, m_pattern.m_body->m_callFrameSize, m_pattern.m_body->m_alternatives[0]->onceThrough()); + emitDisjunction(m_pattern.m_body); + regexEnd(); + + return adoptPtr(new BytecodePattern(m_bodyDisjunction.release(), m_allParenthesesInfo, m_pattern, allocator)); + } + + void checkInput(unsigned count) + { + m_bodyDisjunction->terms.append(ByteTerm::CheckInput(count)); + } + + void uncheckInput(unsigned count) + { + m_bodyDisjunction->terms.append(ByteTerm::UncheckInput(count)); + } + + void assertionBOL(unsigned inputPosition) + { + m_bodyDisjunction->terms.append(ByteTerm::BOL(inputPosition)); + } + + void assertionEOL(unsigned inputPosition) + { + m_bodyDisjunction->terms.append(ByteTerm::EOL(inputPosition)); + } + + void assertionWordBoundary(bool invert, unsigned inputPosition) + { + m_bodyDisjunction->terms.append(ByteTerm::WordBoundary(invert, inputPosition)); + } + + void atomPatternCharacter(UChar ch, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + if (m_pattern.m_ignoreCase) { + UChar lo = Unicode::toLower(ch); + UChar hi = Unicode::toUpper(ch); + + if (lo != hi) { + m_bodyDisjunction->terms.append(ByteTerm(lo, hi, inputPosition, frameLocation, quantityCount, quantityType)); + return; + } + } + + m_bodyDisjunction->terms.append(ByteTerm(ch, inputPosition, frameLocation, quantityCount, quantityType)); + } + + void atomCharacterClass(CharacterClass* characterClass, bool invert, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + m_bodyDisjunction->terms.append(ByteTerm(characterClass, invert, inputPosition)); + + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType; + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + } + + void atomBackReference(unsigned subpatternId, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + ASSERT(subpatternId); + + m_bodyDisjunction->terms.append(ByteTerm::BackReference(subpatternId, inputPosition)); + + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType; + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + } + + void atomParenthesesOnceBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) + { + int beginTerm = m_bodyDisjunction->terms.size(); + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceBegin, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; + + m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); + m_currentAlternativeIndex = beginTerm + 1; + } + + void atomParenthesesTerminalBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) + { + int beginTerm = m_bodyDisjunction->terms.size(); + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternTerminalBegin, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; + + m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); + m_currentAlternativeIndex = beginTerm + 1; + } + + void atomParenthesesSubpatternBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) + { + // Errrk! - this is a little crazy, we initially generate as a TypeParenthesesSubpatternOnceBegin, + // then fix this up at the end! - simplifying this should make it much clearer. + // https://bugs.webkit.org/show_bug.cgi?id=50136 + + int beginTerm = m_bodyDisjunction->terms.size(); + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceBegin, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; + + m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); + m_currentAlternativeIndex = beginTerm + 1; + } + + void atomParentheticalAssertionBegin(unsigned subpatternId, bool invert, unsigned frameLocation, unsigned alternativeFrameLocation) + { + int beginTerm = m_bodyDisjunction->terms.size(); + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParentheticalAssertionBegin, subpatternId, false, invert, 0)); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeBegin()); + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = alternativeFrameLocation; + + m_parenthesesStack.append(ParenthesesStackEntry(beginTerm, m_currentAlternativeIndex)); + m_currentAlternativeIndex = beginTerm + 1; + } + + void atomParentheticalAssertionEnd(unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + unsigned beginTerm = popParenthesesStack(); + closeAlternative(beginTerm + 1); + unsigned endTerm = m_bodyDisjunction->terms.size(); + + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParentheticalAssertionBegin); + + bool invert = m_bodyDisjunction->terms[beginTerm].invert(); + unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParentheticalAssertionEnd, subpatternId, false, invert, inputPosition)); + m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; + + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; + m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; + } + + void assertionDotStarEnclosure(bool bolAnchored, bool eolAnchored) + { + m_bodyDisjunction->terms.append(ByteTerm::DotStarEnclosure(bolAnchored, eolAnchored)); + } + + unsigned popParenthesesStack() + { + ASSERT(m_parenthesesStack.size()); + int stackEnd = m_parenthesesStack.size() - 1; + unsigned beginTerm = m_parenthesesStack[stackEnd].beginTerm; + m_currentAlternativeIndex = m_parenthesesStack[stackEnd].savedAlternativeIndex; + m_parenthesesStack.shrink(stackEnd); + + ASSERT(beginTerm < m_bodyDisjunction->terms.size()); + ASSERT(m_currentAlternativeIndex < m_bodyDisjunction->terms.size()); + + return beginTerm; + } + +#ifndef NDEBUG + void dumpDisjunction(ByteDisjunction* disjunction) + { + dataLogF("ByteDisjunction(%p):\n\t", disjunction); + for (unsigned i = 0; i < disjunction->terms.size(); ++i) + dataLogF("{ %d } ", disjunction->terms[i].type); + dataLogF("\n"); + } +#endif + + void closeAlternative(int beginTerm) + { + int origBeginTerm = beginTerm; + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeAlternativeBegin); + int endIndex = m_bodyDisjunction->terms.size(); + + unsigned frameLocation = m_bodyDisjunction->terms[beginTerm].frameLocation; + + if (!m_bodyDisjunction->terms[beginTerm].alternative.next) + m_bodyDisjunction->terms.remove(beginTerm); + else { + while (m_bodyDisjunction->terms[beginTerm].alternative.next) { + beginTerm += m_bodyDisjunction->terms[beginTerm].alternative.next; + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeAlternativeDisjunction); + m_bodyDisjunction->terms[beginTerm].alternative.end = endIndex - beginTerm; + m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; + } + + m_bodyDisjunction->terms[beginTerm].alternative.next = origBeginTerm - beginTerm; + + m_bodyDisjunction->terms.append(ByteTerm::AlternativeEnd()); + m_bodyDisjunction->terms[endIndex].frameLocation = frameLocation; + } + } + + void closeBodyAlternative() + { + int beginTerm = 0; + int origBeginTerm = 0; + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeBodyAlternativeBegin); + int endIndex = m_bodyDisjunction->terms.size(); + + unsigned frameLocation = m_bodyDisjunction->terms[beginTerm].frameLocation; + + while (m_bodyDisjunction->terms[beginTerm].alternative.next) { + beginTerm += m_bodyDisjunction->terms[beginTerm].alternative.next; + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeBodyAlternativeDisjunction); + m_bodyDisjunction->terms[beginTerm].alternative.end = endIndex - beginTerm; + m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; + } + + m_bodyDisjunction->terms[beginTerm].alternative.next = origBeginTerm - beginTerm; + + m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeEnd()); + m_bodyDisjunction->terms[endIndex].frameLocation = frameLocation; + } + + void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType, unsigned callFrameSize = 0) + { + unsigned beginTerm = popParenthesesStack(); + closeAlternative(beginTerm + 1); + unsigned endTerm = m_bodyDisjunction->terms.size(); + + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + + ByteTerm& parenthesesBegin = m_bodyDisjunction->terms[beginTerm]; + + bool capture = parenthesesBegin.capture(); + unsigned subpatternId = parenthesesBegin.atom.subpatternId; + + unsigned numSubpatterns = lastSubpatternId - subpatternId + 1; + ByteDisjunction* parenthesesDisjunction = new ByteDisjunction(numSubpatterns, callFrameSize); + + parenthesesDisjunction->terms.append(ByteTerm::SubpatternBegin()); + for (unsigned termInParentheses = beginTerm + 1; termInParentheses < endTerm; ++termInParentheses) + parenthesesDisjunction->terms.append(m_bodyDisjunction->terms[termInParentheses]); + parenthesesDisjunction->terms.append(ByteTerm::SubpatternEnd()); + + m_bodyDisjunction->terms.shrink(beginTerm); + + m_allParenthesesInfo.append(parenthesesDisjunction); + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture, inputPosition)); + + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; + m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; + } + + void atomParenthesesOnceEnd(int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + unsigned beginTerm = popParenthesesStack(); + closeAlternative(beginTerm + 1); + unsigned endTerm = m_bodyDisjunction->terms.size(); + + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternOnceBegin); + + bool capture = m_bodyDisjunction->terms[beginTerm].capture(); + unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceEnd, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; + + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; + m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; + } + + void atomParenthesesTerminalEnd(int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + { + unsigned beginTerm = popParenthesesStack(); + closeAlternative(beginTerm + 1); + unsigned endTerm = m_bodyDisjunction->terms.size(); + + ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternTerminalBegin); + + bool capture = m_bodyDisjunction->terms[beginTerm].capture(); + unsigned subpatternId = m_bodyDisjunction->terms[beginTerm].atom.subpatternId; + + m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternTerminalEnd, subpatternId, capture, false, inputPosition)); + m_bodyDisjunction->terms[beginTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; + m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; + + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; + m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); + m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; + } + + void regexBegin(unsigned numSubpatterns, unsigned callFrameSize, bool onceThrough) + { + m_bodyDisjunction = adoptPtr(new ByteDisjunction(numSubpatterns, callFrameSize)); + m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeBegin(onceThrough)); + m_bodyDisjunction->terms[0].frameLocation = 0; + m_currentAlternativeIndex = 0; + } + + void regexEnd() + { + closeBodyAlternative(); + } + + void alternativeBodyDisjunction(bool onceThrough) + { + int newAlternativeIndex = m_bodyDisjunction->terms.size(); + m_bodyDisjunction->terms[m_currentAlternativeIndex].alternative.next = newAlternativeIndex - m_currentAlternativeIndex; + m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeDisjunction(onceThrough)); + + m_currentAlternativeIndex = newAlternativeIndex; + } + + void alternativeDisjunction() + { + int newAlternativeIndex = m_bodyDisjunction->terms.size(); + m_bodyDisjunction->terms[m_currentAlternativeIndex].alternative.next = newAlternativeIndex - m_currentAlternativeIndex; + m_bodyDisjunction->terms.append(ByteTerm::AlternativeDisjunction()); + + m_currentAlternativeIndex = newAlternativeIndex; + } + + void emitDisjunction(PatternDisjunction* disjunction, unsigned inputCountAlreadyChecked = 0, unsigned parenthesesInputCountAlreadyChecked = 0) + { + for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { + unsigned currentCountAlreadyChecked = inputCountAlreadyChecked; + + PatternAlternative* alternative = disjunction->m_alternatives[alt]; + + if (alt) { + if (disjunction == m_pattern.m_body) + alternativeBodyDisjunction(alternative->onceThrough()); + else + alternativeDisjunction(); + } + + unsigned minimumSize = alternative->m_minimumSize; + ASSERT(minimumSize >= parenthesesInputCountAlreadyChecked); + unsigned countToCheck = minimumSize - parenthesesInputCountAlreadyChecked; + + if (countToCheck) { + checkInput(countToCheck); + currentCountAlreadyChecked += countToCheck; + } + + for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { + PatternTerm& term = alternative->m_terms[i]; + + switch (term.type) { + case PatternTerm::TypeAssertionBOL: + assertionBOL(currentCountAlreadyChecked - term.inputPosition); + break; + + case PatternTerm::TypeAssertionEOL: + assertionEOL(currentCountAlreadyChecked - term.inputPosition); + break; + + case PatternTerm::TypeAssertionWordBoundary: + assertionWordBoundary(term.invert(), currentCountAlreadyChecked - term.inputPosition); + break; + + case PatternTerm::TypePatternCharacter: + atomPatternCharacter(term.patternCharacter, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); + break; + + case PatternTerm::TypeCharacterClass: + atomCharacterClass(term.characterClass, term.invert(), currentCountAlreadyChecked- term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); + break; + + case PatternTerm::TypeBackReference: + atomBackReference(term.backReferenceSubpatternId, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); + break; + + case PatternTerm::TypeForwardReference: + break; + + case PatternTerm::TypeParenthesesSubpattern: { + unsigned disjunctionAlreadyCheckedCount = 0; + if (term.quantityCount == 1 && !term.parentheses.isCopy) { + unsigned alternativeFrameLocation = term.frameLocation; + // For QuantifierFixedCount we pre-check the minimum size; for greedy/non-greedy we reserve a slot in the frame. + if (term.quantityType == QuantifierFixedCount) + disjunctionAlreadyCheckedCount = term.parentheses.disjunction->m_minimumSize; + else + alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; + unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; + atomParenthesesOnceBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, alternativeFrameLocation); + emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount); + atomParenthesesOnceEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType); + } else if (term.parentheses.isTerminal) { + unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; + atomParenthesesTerminalBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, term.frameLocation + YarrStackSpaceForBackTrackInfoParenthesesOnce); + emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount); + atomParenthesesTerminalEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType); + } else { + unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; + atomParenthesesSubpatternBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, 0); + emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, 0); + atomParenthesesSubpatternEnd(term.parentheses.lastSubpatternId, delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType, term.parentheses.disjunction->m_callFrameSize); + } + break; + } + + case PatternTerm::TypeParentheticalAssertion: { + unsigned alternativeFrameLocation = term.frameLocation + YarrStackSpaceForBackTrackInfoParentheticalAssertion; + + ASSERT(currentCountAlreadyChecked >= static_cast(term.inputPosition)); + unsigned positiveInputOffset = currentCountAlreadyChecked - static_cast(term.inputPosition); + unsigned uncheckAmount = 0; + if (positiveInputOffset > term.parentheses.disjunction->m_minimumSize) { + uncheckAmount = positiveInputOffset - term.parentheses.disjunction->m_minimumSize; + uncheckInput(uncheckAmount); + currentCountAlreadyChecked -= uncheckAmount; + } + + atomParentheticalAssertionBegin(term.parentheses.subpatternId, term.invert(), term.frameLocation, alternativeFrameLocation); + emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, positiveInputOffset - uncheckAmount); + atomParentheticalAssertionEnd(0, term.frameLocation, term.quantityCount, term.quantityType); + if (uncheckAmount) { + checkInput(uncheckAmount); + currentCountAlreadyChecked += uncheckAmount; + } + break; + } + + case PatternTerm::TypeDotStarEnclosure: + assertionDotStarEnclosure(term.anchors.bolAnchor, term.anchors.eolAnchor); + break; + } + } + } + } + +private: + YarrPattern& m_pattern; + OwnPtr m_bodyDisjunction; + unsigned m_currentAlternativeIndex; + Vector m_parenthesesStack; + Vector m_allParenthesesInfo; +}; + +PassOwnPtr byteCompile(YarrPattern& pattern, BumpPointerAllocator* allocator) +{ + return ByteCompiler(pattern).compile(allocator); +} + +unsigned interpret(BytecodePattern* bytecode, const String& input, unsigned start, unsigned* output) +{ + if (input.is8Bit()) + return Interpreter(bytecode, output, input.characters8(), input.length(), start).interpret(); + return Interpreter(bytecode, output, input.characters16(), input.length(), start).interpret(); +} + +unsigned interpret(BytecodePattern* bytecode, const LChar* input, unsigned length, unsigned start, unsigned* output) +{ + return Interpreter(bytecode, output, input, length, start).interpret(); +} + +unsigned interpret(BytecodePattern* bytecode, const UChar* input, unsigned length, unsigned start, unsigned* output) +{ + return Interpreter(bytecode, output, input, length, start).interpret(); +} + +// These should be the same for both UChar & LChar. +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoPatternCharacter) == (YarrStackSpaceForBackTrackInfoPatternCharacter * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoPatternCharacter); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoCharacterClass) == (YarrStackSpaceForBackTrackInfoCharacterClass * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoCharacterClass); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoBackReference) == (YarrStackSpaceForBackTrackInfoBackReference * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoBackReference); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoAlternative) == (YarrStackSpaceForBackTrackInfoAlternative * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoAlternative); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParentheticalAssertion) == (YarrStackSpaceForBackTrackInfoParentheticalAssertion * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheticalAssertion); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParenthesesOnce) == (YarrStackSpaceForBackTrackInfoParenthesesOnce * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParenthesesOnce); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParentheses) == (YarrStackSpaceForBackTrackInfoParentheses * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheses); + + +} } diff --git a/src/3rdparty/masm/yarr/YarrInterpreter.h b/src/3rdparty/masm/yarr/YarrInterpreter.h new file mode 100644 index 0000000000..fb60bd979d --- /dev/null +++ b/src/3rdparty/masm/yarr/YarrInterpreter.h @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrInterpreter_h +#define YarrInterpreter_h + +#include "YarrPattern.h" +#include +#include + +namespace WTF { +class BumpPointerAllocator; +} +using WTF::BumpPointerAllocator; + +namespace JSC { namespace Yarr { + +class ByteDisjunction; + +struct ByteTerm { + enum Type { + TypeBodyAlternativeBegin, + TypeBodyAlternativeDisjunction, + TypeBodyAlternativeEnd, + TypeAlternativeBegin, + TypeAlternativeDisjunction, + TypeAlternativeEnd, + TypeSubpatternBegin, + TypeSubpatternEnd, + TypeAssertionBOL, + TypeAssertionEOL, + TypeAssertionWordBoundary, + TypePatternCharacterOnce, + TypePatternCharacterFixed, + TypePatternCharacterGreedy, + TypePatternCharacterNonGreedy, + TypePatternCasedCharacterOnce, + TypePatternCasedCharacterFixed, + TypePatternCasedCharacterGreedy, + TypePatternCasedCharacterNonGreedy, + TypeCharacterClass, + TypeBackReference, + TypeParenthesesSubpattern, + TypeParenthesesSubpatternOnceBegin, + TypeParenthesesSubpatternOnceEnd, + TypeParenthesesSubpatternTerminalBegin, + TypeParenthesesSubpatternTerminalEnd, + TypeParentheticalAssertionBegin, + TypeParentheticalAssertionEnd, + TypeCheckInput, + TypeUncheckInput, + TypeDotStarEnclosure, + } type; + union { + struct { + union { + UChar patternCharacter; + struct { + UChar lo; + UChar hi; + } casedCharacter; + CharacterClass* characterClass; + unsigned subpatternId; + }; + union { + ByteDisjunction* parenthesesDisjunction; + unsigned parenthesesWidth; + }; + QuantifierType quantityType; + unsigned quantityCount; + } atom; + struct { + int next; + int end; + bool onceThrough; + } alternative; + struct { + bool m_bol : 1; + bool m_eol : 1; + } anchors; + unsigned checkInputCount; + }; + unsigned frameLocation; + bool m_capture : 1; + bool m_invert : 1; + unsigned inputPosition; + + ByteTerm(UChar ch, int inputPos, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + : frameLocation(frameLocation) + , m_capture(false) + , m_invert(false) + { + switch (quantityType) { + case QuantifierFixedCount: + type = (quantityCount == 1) ? ByteTerm::TypePatternCharacterOnce : ByteTerm::TypePatternCharacterFixed; + break; + case QuantifierGreedy: + type = ByteTerm::TypePatternCharacterGreedy; + break; + case QuantifierNonGreedy: + type = ByteTerm::TypePatternCharacterNonGreedy; + break; + } + + atom.patternCharacter = ch; + atom.quantityType = quantityType; + atom.quantityCount = quantityCount.unsafeGet(); + inputPosition = inputPos; + } + + ByteTerm(UChar lo, UChar hi, int inputPos, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) + : frameLocation(frameLocation) + , m_capture(false) + , m_invert(false) + { + switch (quantityType) { + case QuantifierFixedCount: + type = (quantityCount == 1) ? ByteTerm::TypePatternCasedCharacterOnce : ByteTerm::TypePatternCasedCharacterFixed; + break; + case QuantifierGreedy: + type = ByteTerm::TypePatternCasedCharacterGreedy; + break; + case QuantifierNonGreedy: + type = ByteTerm::TypePatternCasedCharacterNonGreedy; + break; + } + + atom.casedCharacter.lo = lo; + atom.casedCharacter.hi = hi; + atom.quantityType = quantityType; + atom.quantityCount = quantityCount.unsafeGet(); + inputPosition = inputPos; + } + + ByteTerm(CharacterClass* characterClass, bool invert, int inputPos) + : type(ByteTerm::TypeCharacterClass) + , m_capture(false) + , m_invert(invert) + { + atom.characterClass = characterClass; + atom.quantityType = QuantifierFixedCount; + atom.quantityCount = 1; + inputPosition = inputPos; + } + + ByteTerm(Type type, unsigned subpatternId, ByteDisjunction* parenthesesInfo, bool capture, int inputPos) + : type(type) + , m_capture(capture) + , m_invert(false) + { + atom.subpatternId = subpatternId; + atom.parenthesesDisjunction = parenthesesInfo; + atom.quantityType = QuantifierFixedCount; + atom.quantityCount = 1; + inputPosition = inputPos; + } + + ByteTerm(Type type, bool invert = false) + : type(type) + , m_capture(false) + , m_invert(invert) + { + atom.quantityType = QuantifierFixedCount; + atom.quantityCount = 1; + } + + ByteTerm(Type type, unsigned subpatternId, bool capture, bool invert, int inputPos) + : type(type) + , m_capture(capture) + , m_invert(invert) + { + atom.subpatternId = subpatternId; + atom.quantityType = QuantifierFixedCount; + atom.quantityCount = 1; + inputPosition = inputPos; + } + + static ByteTerm BOL(int inputPos) + { + ByteTerm term(TypeAssertionBOL); + term.inputPosition = inputPos; + return term; + } + + static ByteTerm CheckInput(Checked count) + { + ByteTerm term(TypeCheckInput); + term.checkInputCount = count.unsafeGet(); + return term; + } + + static ByteTerm UncheckInput(Checked count) + { + ByteTerm term(TypeUncheckInput); + term.checkInputCount = count.unsafeGet(); + return term; + } + + static ByteTerm EOL(int inputPos) + { + ByteTerm term(TypeAssertionEOL); + term.inputPosition = inputPos; + return term; + } + + static ByteTerm WordBoundary(bool invert, int inputPos) + { + ByteTerm term(TypeAssertionWordBoundary, invert); + term.inputPosition = inputPos; + return term; + } + + static ByteTerm BackReference(unsigned subpatternId, int inputPos) + { + return ByteTerm(TypeBackReference, subpatternId, false, false, inputPos); + } + + static ByteTerm BodyAlternativeBegin(bool onceThrough) + { + ByteTerm term(TypeBodyAlternativeBegin); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = onceThrough; + return term; + } + + static ByteTerm BodyAlternativeDisjunction(bool onceThrough) + { + ByteTerm term(TypeBodyAlternativeDisjunction); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = onceThrough; + return term; + } + + static ByteTerm BodyAlternativeEnd() + { + ByteTerm term(TypeBodyAlternativeEnd); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = false; + return term; + } + + static ByteTerm AlternativeBegin() + { + ByteTerm term(TypeAlternativeBegin); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = false; + return term; + } + + static ByteTerm AlternativeDisjunction() + { + ByteTerm term(TypeAlternativeDisjunction); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = false; + return term; + } + + static ByteTerm AlternativeEnd() + { + ByteTerm term(TypeAlternativeEnd); + term.alternative.next = 0; + term.alternative.end = 0; + term.alternative.onceThrough = false; + return term; + } + + static ByteTerm SubpatternBegin() + { + return ByteTerm(TypeSubpatternBegin); + } + + static ByteTerm SubpatternEnd() + { + return ByteTerm(TypeSubpatternEnd); + } + + static ByteTerm DotStarEnclosure(bool bolAnchor, bool eolAnchor) + { + ByteTerm term(TypeDotStarEnclosure); + term.anchors.m_bol = bolAnchor; + term.anchors.m_eol = eolAnchor; + return term; + } + + bool invert() + { + return m_invert; + } + + bool capture() + { + return m_capture; + } +}; + +class ByteDisjunction { + WTF_MAKE_FAST_ALLOCATED; +public: + ByteDisjunction(unsigned numSubpatterns, unsigned frameSize) + : m_numSubpatterns(numSubpatterns) + , m_frameSize(frameSize) + { + } + + Vector terms; + unsigned m_numSubpatterns; + unsigned m_frameSize; +}; + +struct BytecodePattern { + WTF_MAKE_FAST_ALLOCATED; +public: + BytecodePattern(PassOwnPtr body, Vector allParenthesesInfo, YarrPattern& pattern, BumpPointerAllocator* allocator) + : m_body(body) + , m_ignoreCase(pattern.m_ignoreCase) + , m_multiline(pattern.m_multiline) + , m_allocator(allocator) + { + newlineCharacterClass = pattern.newlineCharacterClass(); + wordcharCharacterClass = pattern.wordcharCharacterClass(); + + m_allParenthesesInfo.append(allParenthesesInfo); + m_userCharacterClasses.append(pattern.m_userCharacterClasses); + // 'Steal' the YarrPattern's CharacterClasses! We clear its + // array, so that it won't delete them on destruction. We'll + // take responsibility for that. + pattern.m_userCharacterClasses.clear(); + } + + ~BytecodePattern() + { + deleteAllValues(m_allParenthesesInfo); + deleteAllValues(m_userCharacterClasses); + } + + OwnPtr m_body; + bool m_ignoreCase; + bool m_multiline; + // Each BytecodePattern is associated with a RegExp, each RegExp is associated + // with a JSGlobalData. Cache a pointer to out JSGlobalData's m_regExpAllocator. + BumpPointerAllocator* m_allocator; + + CharacterClass* newlineCharacterClass; + CharacterClass* wordcharCharacterClass; + +private: + Vector m_allParenthesesInfo; + Vector m_userCharacterClasses; +}; + +JS_EXPORT_PRIVATE PassOwnPtr byteCompile(YarrPattern&, BumpPointerAllocator*); +JS_EXPORT_PRIVATE unsigned interpret(BytecodePattern*, const String& input, unsigned start, unsigned* output); +unsigned interpret(BytecodePattern*, const LChar* input, unsigned length, unsigned start, unsigned* output); +unsigned interpret(BytecodePattern*, const UChar* input, unsigned length, unsigned start, unsigned* output); + +} } // namespace JSC::Yarr + +#endif // YarrInterpreter_h diff --git a/src/3rdparty/masm/yarr/YarrJIT.cpp b/src/3rdparty/masm/yarr/YarrJIT.cpp new file mode 100644 index 0000000000..ce84e2c74f --- /dev/null +++ b/src/3rdparty/masm/yarr/YarrJIT.cpp @@ -0,0 +1,2667 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "YarrJIT.h" + +#include +#include "LinkBuffer.h" +#include "Options.h" +#include "Yarr.h" +#include "YarrCanonicalizeUCS2.h" + +#if ENABLE(YARR_JIT) + +using namespace WTF; + +namespace JSC { namespace Yarr { + +template +class YarrGenerator : private MacroAssembler { + friend void jitCompile(JSGlobalData*, YarrCodeBlock& jitObject, const String& pattern, unsigned& numSubpatterns, const char*& error, bool ignoreCase, bool multiline); + +#if CPU(ARM) + static const RegisterID input = ARMRegisters::r0; + static const RegisterID index = ARMRegisters::r1; + static const RegisterID length = ARMRegisters::r2; + static const RegisterID output = ARMRegisters::r4; + + static const RegisterID regT0 = ARMRegisters::r5; + static const RegisterID regT1 = ARMRegisters::r6; + + static const RegisterID returnRegister = ARMRegisters::r0; + static const RegisterID returnRegister2 = ARMRegisters::r1; +#elif CPU(MIPS) + static const RegisterID input = MIPSRegisters::a0; + static const RegisterID index = MIPSRegisters::a1; + static const RegisterID length = MIPSRegisters::a2; + static const RegisterID output = MIPSRegisters::a3; + + static const RegisterID regT0 = MIPSRegisters::t4; + static const RegisterID regT1 = MIPSRegisters::t5; + + static const RegisterID returnRegister = MIPSRegisters::v0; + static const RegisterID returnRegister2 = MIPSRegisters::v1; +#elif CPU(SH4) + static const RegisterID input = SH4Registers::r4; + static const RegisterID index = SH4Registers::r5; + static const RegisterID length = SH4Registers::r6; + static const RegisterID output = SH4Registers::r7; + + static const RegisterID regT0 = SH4Registers::r0; + static const RegisterID regT1 = SH4Registers::r1; + + static const RegisterID returnRegister = SH4Registers::r0; + static const RegisterID returnRegister2 = SH4Registers::r1; +#elif CPU(X86) + static const RegisterID input = X86Registers::eax; + static const RegisterID index = X86Registers::edx; + static const RegisterID length = X86Registers::ecx; + static const RegisterID output = X86Registers::edi; + + static const RegisterID regT0 = X86Registers::ebx; + static const RegisterID regT1 = X86Registers::esi; + + static const RegisterID returnRegister = X86Registers::eax; + static const RegisterID returnRegister2 = X86Registers::edx; +#elif CPU(X86_64) + static const RegisterID input = X86Registers::edi; + static const RegisterID index = X86Registers::esi; + static const RegisterID length = X86Registers::edx; + static const RegisterID output = X86Registers::ecx; + + static const RegisterID regT0 = X86Registers::eax; + static const RegisterID regT1 = X86Registers::ebx; + + static const RegisterID returnRegister = X86Registers::eax; + static const RegisterID returnRegister2 = X86Registers::edx; +#endif + + void optimizeAlternative(PatternAlternative* alternative) + { + if (!alternative->m_terms.size()) + return; + + for (unsigned i = 0; i < alternative->m_terms.size() - 1; ++i) { + PatternTerm& term = alternative->m_terms[i]; + PatternTerm& nextTerm = alternative->m_terms[i + 1]; + + if ((term.type == PatternTerm::TypeCharacterClass) + && (term.quantityType == QuantifierFixedCount) + && (nextTerm.type == PatternTerm::TypePatternCharacter) + && (nextTerm.quantityType == QuantifierFixedCount)) { + PatternTerm termCopy = term; + alternative->m_terms[i] = nextTerm; + alternative->m_terms[i + 1] = termCopy; + } + } + } + + void matchCharacterClassRange(RegisterID character, JumpList& failures, JumpList& matchDest, const CharacterRange* ranges, unsigned count, unsigned* matchIndex, const UChar* matches, unsigned matchCount) + { + do { + // pick which range we're going to generate + int which = count >> 1; + char lo = ranges[which].begin; + char hi = ranges[which].end; + + // check if there are any ranges or matches below lo. If not, just jl to failure - + // if there is anything else to check, check that first, if it falls through jmp to failure. + if ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) { + Jump loOrAbove = branch32(GreaterThanOrEqual, character, Imm32((unsigned short)lo)); + + // generate code for all ranges before this one + if (which) + matchCharacterClassRange(character, failures, matchDest, ranges, which, matchIndex, matches, matchCount); + + while ((*matchIndex < matchCount) && (matches[*matchIndex] < lo)) { + matchDest.append(branch32(Equal, character, Imm32((unsigned short)matches[*matchIndex]))); + ++*matchIndex; + } + failures.append(jump()); + + loOrAbove.link(this); + } else if (which) { + Jump loOrAbove = branch32(GreaterThanOrEqual, character, Imm32((unsigned short)lo)); + + matchCharacterClassRange(character, failures, matchDest, ranges, which, matchIndex, matches, matchCount); + failures.append(jump()); + + loOrAbove.link(this); + } else + failures.append(branch32(LessThan, character, Imm32((unsigned short)lo))); + + while ((*matchIndex < matchCount) && (matches[*matchIndex] <= hi)) + ++*matchIndex; + + matchDest.append(branch32(LessThanOrEqual, character, Imm32((unsigned short)hi))); + // fall through to here, the value is above hi. + + // shuffle along & loop around if there are any more matches to handle. + unsigned next = which + 1; + ranges += next; + count -= next; + } while (count); + } + + void matchCharacterClass(RegisterID character, JumpList& matchDest, const CharacterClass* charClass) + { + if (charClass->m_table) { + ExtendedAddress tableEntry(character, reinterpret_cast(charClass->m_table->m_table)); + matchDest.append(branchTest8(charClass->m_table->m_inverted ? Zero : NonZero, tableEntry)); + return; + } + Jump unicodeFail; + if (charClass->m_matchesUnicode.size() || charClass->m_rangesUnicode.size()) { + Jump isAscii = branch32(LessThanOrEqual, character, TrustedImm32(0x7f)); + + if (charClass->m_matchesUnicode.size()) { + for (unsigned i = 0; i < charClass->m_matchesUnicode.size(); ++i) { + UChar ch = charClass->m_matchesUnicode[i]; + matchDest.append(branch32(Equal, character, Imm32(ch))); + } + } + + if (charClass->m_rangesUnicode.size()) { + for (unsigned i = 0; i < charClass->m_rangesUnicode.size(); ++i) { + UChar lo = charClass->m_rangesUnicode[i].begin; + UChar hi = charClass->m_rangesUnicode[i].end; + + Jump below = branch32(LessThan, character, Imm32(lo)); + matchDest.append(branch32(LessThanOrEqual, character, Imm32(hi))); + below.link(this); + } + } + + unicodeFail = jump(); + isAscii.link(this); + } + + if (charClass->m_ranges.size()) { + unsigned matchIndex = 0; + JumpList failures; + matchCharacterClassRange(character, failures, matchDest, charClass->m_ranges.begin(), charClass->m_ranges.size(), &matchIndex, charClass->m_matches.begin(), charClass->m_matches.size()); + while (matchIndex < charClass->m_matches.size()) + matchDest.append(branch32(Equal, character, Imm32((unsigned short)charClass->m_matches[matchIndex++]))); + + failures.link(this); + } else if (charClass->m_matches.size()) { + // optimization: gather 'a','A' etc back together, can mask & test once. + Vector matchesAZaz; + + for (unsigned i = 0; i < charClass->m_matches.size(); ++i) { + char ch = charClass->m_matches[i]; + if (m_pattern.m_ignoreCase) { + if (isASCIILower(ch)) { + matchesAZaz.append(ch); + continue; + } + if (isASCIIUpper(ch)) + continue; + } + matchDest.append(branch32(Equal, character, Imm32((unsigned short)ch))); + } + + if (unsigned countAZaz = matchesAZaz.size()) { + or32(TrustedImm32(32), character); + for (unsigned i = 0; i < countAZaz; ++i) + matchDest.append(branch32(Equal, character, TrustedImm32(matchesAZaz[i]))); + } + } + + if (charClass->m_matchesUnicode.size() || charClass->m_rangesUnicode.size()) + unicodeFail.link(this); + } + + // Jumps if input not available; will have (incorrectly) incremented already! + Jump jumpIfNoAvailableInput(unsigned countToCheck = 0) + { + if (countToCheck) + add32(Imm32(countToCheck), index); + return branch32(Above, index, length); + } + + Jump jumpIfAvailableInput(unsigned countToCheck) + { + add32(Imm32(countToCheck), index); + return branch32(BelowOrEqual, index, length); + } + + Jump checkInput() + { + return branch32(BelowOrEqual, index, length); + } + + Jump atEndOfInput() + { + return branch32(Equal, index, length); + } + + Jump notAtEndOfInput() + { + return branch32(NotEqual, index, length); + } + + Jump jumpIfCharNotEquals(UChar ch, int inputPosition, RegisterID character) + { + readCharacter(inputPosition, character); + + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { + or32(TrustedImm32(0x20), character); + ch |= 0x20; + } + + return branch32(NotEqual, character, Imm32(ch)); + } + + void readCharacter(int inputPosition, RegisterID reg) + { + if (m_charSize == Char8) + load8(BaseIndex(input, index, TimesOne, inputPosition * sizeof(char)), reg); + else + load16(BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), reg); + } + + void storeToFrame(RegisterID reg, unsigned frameLocation) + { + poke(reg, frameLocation); + } + + void storeToFrame(TrustedImm32 imm, unsigned frameLocation) + { + poke(imm, frameLocation); + } + + DataLabelPtr storeToFrameWithPatch(unsigned frameLocation) + { + return storePtrWithPatch(TrustedImmPtr(0), Address(stackPointerRegister, frameLocation * sizeof(void*))); + } + + void loadFromFrame(unsigned frameLocation, RegisterID reg) + { + peek(reg, frameLocation); + } + + void loadFromFrameAndJump(unsigned frameLocation) + { + jump(Address(stackPointerRegister, frameLocation * sizeof(void*))); + } + + void initCallFrame() + { + unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; + if (callFrameSize) + subPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); + } + void removeCallFrame() + { + unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; + if (callFrameSize) + addPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); + } + + // Used to record subpatters, should only be called if compileMode is IncludeSubpatterns. + void setSubpatternStart(RegisterID reg, unsigned subpattern) + { + ASSERT(subpattern); + // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( + store32(reg, Address(output, (subpattern << 1) * sizeof(int))); + } + void setSubpatternEnd(RegisterID reg, unsigned subpattern) + { + ASSERT(subpattern); + // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( + store32(reg, Address(output, ((subpattern << 1) + 1) * sizeof(int))); + } + void clearSubpatternStart(unsigned subpattern) + { + ASSERT(subpattern); + // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( + store32(TrustedImm32(-1), Address(output, (subpattern << 1) * sizeof(int))); + } + + // We use one of three different strategies to track the start of the current match, + // while matching. + // 1) If the pattern has a fixed size, do nothing! - we calculate the value lazily + // at the end of matching. This is irrespective of compileMode, and in this case + // these methods should never be called. + // 2) If we're compiling IncludeSubpatterns, 'output' contains a pointer to an output + // vector, store the match start in the output vector. + // 3) If we're compiling MatchOnly, 'output' is unused, store the match start directly + // in this register. + void setMatchStart(RegisterID reg) + { + ASSERT(!m_pattern.m_body->m_hasFixedSize); + if (compileMode == IncludeSubpatterns) + store32(reg, output); + else + move(reg, output); + } + void getMatchStart(RegisterID reg) + { + ASSERT(!m_pattern.m_body->m_hasFixedSize); + if (compileMode == IncludeSubpatterns) + load32(output, reg); + else + move(output, reg); + } + + enum YarrOpCode { + // These nodes wrap body alternatives - those in the main disjunction, + // rather than subpatterns or assertions. These are chained together in + // a doubly linked list, with a 'begin' node for the first alternative, + // a 'next' node for each subsequent alternative, and an 'end' node at + // the end. In the case of repeating alternatives, the 'end' node also + // has a reference back to 'begin'. + OpBodyAlternativeBegin, + OpBodyAlternativeNext, + OpBodyAlternativeEnd, + // Similar to the body alternatives, but used for subpatterns with two + // or more alternatives. + OpNestedAlternativeBegin, + OpNestedAlternativeNext, + OpNestedAlternativeEnd, + // Used for alternatives in subpatterns where there is only a single + // alternative (backtrackingis easier in these cases), or for alternatives + // which never need to be backtracked (those in parenthetical assertions, + // terminal subpatterns). + OpSimpleNestedAlternativeBegin, + OpSimpleNestedAlternativeNext, + OpSimpleNestedAlternativeEnd, + // Used to wrap 'Once' subpattern matches (quantityCount == 1). + OpParenthesesSubpatternOnceBegin, + OpParenthesesSubpatternOnceEnd, + // Used to wrap 'Terminal' subpattern matches (at the end of the regexp). + OpParenthesesSubpatternTerminalBegin, + OpParenthesesSubpatternTerminalEnd, + // Used to wrap parenthetical assertions. + OpParentheticalAssertionBegin, + OpParentheticalAssertionEnd, + // Wraps all simple terms (pattern characters, character classes). + OpTerm, + // Where an expression contains only 'once through' body alternatives + // and no repeating ones, this op is used to return match failure. + OpMatchFailed + }; + + // This structure is used to hold the compiled opcode information, + // including reference back to the original PatternTerm/PatternAlternatives, + // and JIT compilation data structures. + struct YarrOp { + explicit YarrOp(PatternTerm* term) + : m_op(OpTerm) + , m_term(term) + , m_isDeadCode(false) + { + } + + explicit YarrOp(YarrOpCode op) + : m_op(op) + , m_isDeadCode(false) + { + } + + // The operation, as a YarrOpCode, and also a reference to the PatternTerm. + YarrOpCode m_op; + PatternTerm* m_term; + + // For alternatives, this holds the PatternAlternative and doubly linked + // references to this alternative's siblings. In the case of the + // OpBodyAlternativeEnd node at the end of a section of repeating nodes, + // m_nextOp will reference the OpBodyAlternativeBegin node of the first + // repeating alternative. + PatternAlternative* m_alternative; + size_t m_previousOp; + size_t m_nextOp; + + // Used to record a set of Jumps out of the generated code, typically + // used for jumps out to backtracking code, and a single reentry back + // into the code for a node (likely where a backtrack will trigger + // rematching). + Label m_reentry; + JumpList m_jumps; + + // Used for backtracking when the prior alternative did not consume any + // characters but matched. + Jump m_zeroLengthMatch; + + // This flag is used to null out the second pattern character, when + // two are fused to match a pair together. + bool m_isDeadCode; + + // Currently used in the case of some of the more complex management of + // 'm_checked', to cache the offset used in this alternative, to avoid + // recalculating it. + int m_checkAdjust; + + // Used by OpNestedAlternativeNext/End to hold the pointer to the + // value that will be pushed into the pattern's frame to return to, + // upon backtracking back into the disjunction. + DataLabelPtr m_returnAddress; + }; + + // BacktrackingState + // This class encapsulates information about the state of code generation + // whilst generating the code for backtracking, when a term fails to match. + // Upon entry to code generation of the backtracking code for a given node, + // the Backtracking state will hold references to all control flow sources + // that are outputs in need of further backtracking from the prior node + // generated (which is the subsequent operation in the regular expression, + // and in the m_ops Vector, since we generated backtracking backwards). + // These references to control flow take the form of: + // - A jump list of jumps, to be linked to code that will backtrack them + // further. + // - A set of DataLabelPtr values, to be populated with values to be + // treated effectively as return addresses backtracking into complex + // subpatterns. + // - A flag indicating that the current sequence of generated code up to + // this point requires backtracking. + class BacktrackingState { + public: + BacktrackingState() + : m_pendingFallthrough(false) + { + } + + // Add a jump or jumps, a return address, or set the flag indicating + // that the current 'fallthrough' control flow requires backtracking. + void append(const Jump& jump) + { + m_laterFailures.append(jump); + } + void append(JumpList& jumpList) + { + m_laterFailures.append(jumpList); + } + void append(const DataLabelPtr& returnAddress) + { + m_pendingReturns.append(returnAddress); + } + void fallthrough() + { + ASSERT(!m_pendingFallthrough); + m_pendingFallthrough = true; + } + + // These methods clear the backtracking state, either linking to the + // current location, a provided label, or copying the backtracking out + // to a JumpList. All actions may require code generation to take place, + // and as such are passed a pointer to the assembler. + void link(MacroAssembler* assembler) + { + if (m_pendingReturns.size()) { + Label here(assembler); + for (unsigned i = 0; i < m_pendingReturns.size(); ++i) + m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], here)); + m_pendingReturns.clear(); + } + m_laterFailures.link(assembler); + m_laterFailures.clear(); + m_pendingFallthrough = false; + } + void linkTo(Label label, MacroAssembler* assembler) + { + if (m_pendingReturns.size()) { + for (unsigned i = 0; i < m_pendingReturns.size(); ++i) + m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], label)); + m_pendingReturns.clear(); + } + if (m_pendingFallthrough) + assembler->jump(label); + m_laterFailures.linkTo(label, assembler); + m_laterFailures.clear(); + m_pendingFallthrough = false; + } + void takeBacktracksToJumpList(JumpList& jumpList, MacroAssembler* assembler) + { + if (m_pendingReturns.size()) { + Label here(assembler); + for (unsigned i = 0; i < m_pendingReturns.size(); ++i) + m_backtrackRecords.append(ReturnAddressRecord(m_pendingReturns[i], here)); + m_pendingReturns.clear(); + m_pendingFallthrough = true; + } + if (m_pendingFallthrough) + jumpList.append(assembler->jump()); + jumpList.append(m_laterFailures); + m_laterFailures.clear(); + m_pendingFallthrough = false; + } + + bool isEmpty() + { + return m_laterFailures.empty() && m_pendingReturns.isEmpty() && !m_pendingFallthrough; + } + + // Called at the end of code generation to link all return addresses. + void linkDataLabels(LinkBuffer& linkBuffer) + { + ASSERT(isEmpty()); + for (unsigned i = 0; i < m_backtrackRecords.size(); ++i) + linkBuffer.patch(m_backtrackRecords[i].m_dataLabel, linkBuffer.locationOf(m_backtrackRecords[i].m_backtrackLocation)); + } + + private: + struct ReturnAddressRecord { + ReturnAddressRecord(DataLabelPtr dataLabel, Label backtrackLocation) + : m_dataLabel(dataLabel) + , m_backtrackLocation(backtrackLocation) + { + } + + DataLabelPtr m_dataLabel; + Label m_backtrackLocation; + }; + + JumpList m_laterFailures; + bool m_pendingFallthrough; + Vector m_pendingReturns; + Vector m_backtrackRecords; + }; + + // Generation methods: + // =================== + + // This method provides a default implementation of backtracking common + // to many terms; terms commonly jump out of the forwards matching path + // on any failed conditions, and add these jumps to the m_jumps list. If + // no special handling is required we can often just backtrack to m_jumps. + void backtrackTermDefault(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + m_backtrackingState.append(op.m_jumps); + } + + void generateAssertionBOL(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + if (m_pattern.m_multiline) { + const RegisterID character = regT0; + + JumpList matchDest; + if (!term->inputPosition) + matchDest.append(branch32(Equal, index, Imm32(m_checked))); + + readCharacter((term->inputPosition - m_checked) - 1, character); + matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass()); + op.m_jumps.append(jump()); + + matchDest.link(this); + } else { + // Erk, really should poison out these alternatives early. :-/ + if (term->inputPosition) + op.m_jumps.append(jump()); + else + op.m_jumps.append(branch32(NotEqual, index, Imm32(m_checked))); + } + } + void backtrackAssertionBOL(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generateAssertionEOL(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + if (m_pattern.m_multiline) { + const RegisterID character = regT0; + + JumpList matchDest; + if (term->inputPosition == m_checked) + matchDest.append(atEndOfInput()); + + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass()); + op.m_jumps.append(jump()); + + matchDest.link(this); + } else { + if (term->inputPosition == m_checked) + op.m_jumps.append(notAtEndOfInput()); + // Erk, really should poison out these alternatives early. :-/ + else + op.m_jumps.append(jump()); + } + } + void backtrackAssertionEOL(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + // Also falls though on nextIsNotWordChar. + void matchAssertionWordchar(size_t opIndex, JumpList& nextIsWordChar, JumpList& nextIsNotWordChar) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + + if (term->inputPosition == m_checked) + nextIsNotWordChar.append(atEndOfInput()); + + readCharacter((term->inputPosition - m_checked), character); + matchCharacterClass(character, nextIsWordChar, m_pattern.wordcharCharacterClass()); + } + + void generateAssertionWordBoundary(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + + Jump atBegin; + JumpList matchDest; + if (!term->inputPosition) + atBegin = branch32(Equal, index, Imm32(m_checked)); + readCharacter((term->inputPosition - m_checked) - 1, character); + matchCharacterClass(character, matchDest, m_pattern.wordcharCharacterClass()); + if (!term->inputPosition) + atBegin.link(this); + + // We fall through to here if the last character was not a wordchar. + JumpList nonWordCharThenWordChar; + JumpList nonWordCharThenNonWordChar; + if (term->invert()) { + matchAssertionWordchar(opIndex, nonWordCharThenNonWordChar, nonWordCharThenWordChar); + nonWordCharThenWordChar.append(jump()); + } else { + matchAssertionWordchar(opIndex, nonWordCharThenWordChar, nonWordCharThenNonWordChar); + nonWordCharThenNonWordChar.append(jump()); + } + op.m_jumps.append(nonWordCharThenNonWordChar); + + // We jump here if the last character was a wordchar. + matchDest.link(this); + JumpList wordCharThenWordChar; + JumpList wordCharThenNonWordChar; + if (term->invert()) { + matchAssertionWordchar(opIndex, wordCharThenNonWordChar, wordCharThenWordChar); + wordCharThenWordChar.append(jump()); + } else { + matchAssertionWordchar(opIndex, wordCharThenWordChar, wordCharThenNonWordChar); + // This can fall-though! + } + + op.m_jumps.append(wordCharThenWordChar); + + nonWordCharThenWordChar.link(this); + wordCharThenNonWordChar.link(this); + } + void backtrackAssertionWordBoundary(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generatePatternCharacterOnce(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + + if (op.m_isDeadCode) + return; + + // m_ops always ends with a OpBodyAlternativeEnd or OpMatchFailed + // node, so there must always be at least one more node. + ASSERT(opIndex + 1 < m_ops.size()); + YarrOp* nextOp = &m_ops[opIndex + 1]; + + PatternTerm* term = op.m_term; + UChar ch = term->patternCharacter; + + if ((ch > 0xff) && (m_charSize == Char8)) { + // Have a 16 bit pattern character and an 8 bit string - short circuit + op.m_jumps.append(jump()); + return; + } + + const RegisterID character = regT0; + int maxCharactersAtOnce = m_charSize == Char8 ? 4 : 2; + unsigned ignoreCaseMask = 0; + int allCharacters = ch; + int numberCharacters; + int startTermPosition = term->inputPosition; + + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); + + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) + ignoreCaseMask |= 32; + + for (numberCharacters = 1; numberCharacters < maxCharactersAtOnce && nextOp->m_op == OpTerm; ++numberCharacters, nextOp = &m_ops[opIndex + numberCharacters]) { + PatternTerm* nextTerm = nextOp->m_term; + + if (nextTerm->type != PatternTerm::TypePatternCharacter + || nextTerm->quantityType != QuantifierFixedCount + || nextTerm->quantityCount != 1 + || nextTerm->inputPosition != (startTermPosition + numberCharacters)) + break; + + nextOp->m_isDeadCode = true; + + int shiftAmount = (m_charSize == Char8 ? 8 : 16) * numberCharacters; + + UChar currentCharacter = nextTerm->patternCharacter; + + if ((currentCharacter > 0xff) && (m_charSize == Char8)) { + // Have a 16 bit pattern character and an 8 bit string - short circuit + op.m_jumps.append(jump()); + return; + } + + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(currentCharacter) || isCanonicallyUnique(currentCharacter)); + + allCharacters |= (currentCharacter << shiftAmount); + + if ((m_pattern.m_ignoreCase) && (isASCIIAlpha(currentCharacter))) + ignoreCaseMask |= 32 << shiftAmount; + } + + if (m_charSize == Char8) { + switch (numberCharacters) { + case 1: + op.m_jumps.append(jumpIfCharNotEquals(ch, startTermPosition - m_checked, character)); + return; + case 2: { + BaseIndex address(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); + load16Unaligned(address, character); + break; + } + case 3: { + BaseIndex highAddress(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); + load16Unaligned(highAddress, character); + if (ignoreCaseMask) + or32(Imm32(ignoreCaseMask), character); + op.m_jumps.append(branch32(NotEqual, character, Imm32((allCharacters & 0xffff) | ignoreCaseMask))); + op.m_jumps.append(jumpIfCharNotEquals(allCharacters >> 16, startTermPosition + 2 - m_checked, character)); + return; + } + case 4: { + BaseIndex address(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); + load32WithUnalignedHalfWords(address, character); + break; + } + } + } else { + switch (numberCharacters) { + case 1: + op.m_jumps.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); + return; + case 2: + BaseIndex address(input, index, TimesTwo, (term->inputPosition - m_checked) * sizeof(UChar)); + load32WithUnalignedHalfWords(address, character); + break; + } + } + + if (ignoreCaseMask) + or32(Imm32(ignoreCaseMask), character); + op.m_jumps.append(branch32(NotEqual, character, Imm32(allCharacters | ignoreCaseMask))); + return; + } + void backtrackPatternCharacterOnce(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generatePatternCharacterFixed(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + UChar ch = term->patternCharacter; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + move(index, countRegister); + sub32(Imm32(term->quantityCount.unsafeGet()), countRegister); + + Label loop(this); + BaseIndex address(input, countRegister, m_charScale, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(m_charSize == Char8 ? sizeof(char) : sizeof(UChar))).unsafeGet()); + + if (m_charSize == Char8) + load8(address, character); + else + load16(address, character); + + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { + or32(TrustedImm32(0x20), character); + ch |= 0x20; + } + + op.m_jumps.append(branch32(NotEqual, character, Imm32(ch))); + add32(TrustedImm32(1), countRegister); + branch32(NotEqual, countRegister, index).linkTo(loop, this); + } + void backtrackPatternCharacterFixed(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generatePatternCharacterGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + UChar ch = term->patternCharacter; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + move(TrustedImm32(0), countRegister); + + // Unless have a 16 bit pattern character and an 8 bit string - short circuit + if (!((ch > 0xff) && (m_charSize == Char8))) { + JumpList failures; + Label loop(this); + failures.append(atEndOfInput()); + failures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); + + add32(TrustedImm32(1), countRegister); + add32(TrustedImm32(1), index); + if (term->quantityCount == quantifyInfinite) + jump(loop); + else + branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this); + + failures.link(this); + } + op.m_reentry = label(); + + storeToFrame(countRegister, term->frameLocation); + } + void backtrackPatternCharacterGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID countRegister = regT1; + + m_backtrackingState.link(this); + + loadFromFrame(term->frameLocation, countRegister); + m_backtrackingState.append(branchTest32(Zero, countRegister)); + sub32(TrustedImm32(1), countRegister); + sub32(TrustedImm32(1), index); + jump(op.m_reentry); + } + + void generatePatternCharacterNonGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID countRegister = regT1; + + move(TrustedImm32(0), countRegister); + op.m_reentry = label(); + storeToFrame(countRegister, term->frameLocation); + } + void backtrackPatternCharacterNonGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + UChar ch = term->patternCharacter; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + m_backtrackingState.link(this); + + loadFromFrame(term->frameLocation, countRegister); + + // Unless have a 16 bit pattern character and an 8 bit string - short circuit + if (!((ch > 0xff) && (m_charSize == Char8))) { + JumpList nonGreedyFailures; + nonGreedyFailures.append(atEndOfInput()); + if (term->quantityCount != quantifyInfinite) + nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet()))); + nonGreedyFailures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); + + add32(TrustedImm32(1), countRegister); + add32(TrustedImm32(1), index); + + jump(op.m_reentry); + nonGreedyFailures.link(this); + } + + sub32(countRegister, index); + m_backtrackingState.fallthrough(); + } + + void generateCharacterClassOnce(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + + JumpList matchDest; + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, matchDest, term->characterClass); + + if (term->invert()) + op.m_jumps.append(matchDest); + else { + op.m_jumps.append(jump()); + matchDest.link(this); + } + } + void backtrackCharacterClassOnce(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generateCharacterClassFixed(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + move(index, countRegister); + sub32(Imm32(term->quantityCount.unsafeGet()), countRegister); + + Label loop(this); + JumpList matchDest; + if (m_charSize == Char8) + load8(BaseIndex(input, countRegister, TimesOne, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(sizeof(char))).unsafeGet()), character); + else + load16(BaseIndex(input, countRegister, TimesTwo, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(sizeof(UChar))).unsafeGet()), character); + matchCharacterClass(character, matchDest, term->characterClass); + + if (term->invert()) + op.m_jumps.append(matchDest); + else { + op.m_jumps.append(jump()); + matchDest.link(this); + } + + add32(TrustedImm32(1), countRegister); + branch32(NotEqual, countRegister, index).linkTo(loop, this); + } + void backtrackCharacterClassFixed(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + void generateCharacterClassGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + move(TrustedImm32(0), countRegister); + + JumpList failures; + Label loop(this); + failures.append(atEndOfInput()); + + if (term->invert()) { + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, failures, term->characterClass); + } else { + JumpList matchDest; + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, matchDest, term->characterClass); + failures.append(jump()); + matchDest.link(this); + } + + add32(TrustedImm32(1), countRegister); + add32(TrustedImm32(1), index); + if (term->quantityCount != quantifyInfinite) { + branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this); + failures.append(jump()); + } else + jump(loop); + + failures.link(this); + op.m_reentry = label(); + + storeToFrame(countRegister, term->frameLocation); + } + void backtrackCharacterClassGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID countRegister = regT1; + + m_backtrackingState.link(this); + + loadFromFrame(term->frameLocation, countRegister); + m_backtrackingState.append(branchTest32(Zero, countRegister)); + sub32(TrustedImm32(1), countRegister); + sub32(TrustedImm32(1), index); + jump(op.m_reentry); + } + + void generateCharacterClassNonGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID countRegister = regT1; + + move(TrustedImm32(0), countRegister); + op.m_reentry = label(); + storeToFrame(countRegister, term->frameLocation); + } + void backtrackCharacterClassNonGreedy(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + const RegisterID countRegister = regT1; + + JumpList nonGreedyFailures; + + m_backtrackingState.link(this); + + loadFromFrame(term->frameLocation, countRegister); + + nonGreedyFailures.append(atEndOfInput()); + nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet()))); + + JumpList matchDest; + readCharacter(term->inputPosition - m_checked, character); + matchCharacterClass(character, matchDest, term->characterClass); + + if (term->invert()) + nonGreedyFailures.append(matchDest); + else { + nonGreedyFailures.append(jump()); + matchDest.link(this); + } + + add32(TrustedImm32(1), countRegister); + add32(TrustedImm32(1), index); + + jump(op.m_reentry); + + nonGreedyFailures.link(this); + sub32(countRegister, index); + m_backtrackingState.fallthrough(); + } + + void generateDotStarEnclosure(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + const RegisterID matchPos = regT1; + + JumpList foundBeginningNewLine; + JumpList saveStartIndex; + JumpList foundEndingNewLine; + + ASSERT(!m_pattern.m_body->m_hasFixedSize); + getMatchStart(matchPos); + + saveStartIndex.append(branchTest32(Zero, matchPos)); + Label findBOLLoop(this); + sub32(TrustedImm32(1), matchPos); + if (m_charSize == Char8) + load8(BaseIndex(input, matchPos, TimesOne, 0), character); + else + load16(BaseIndex(input, matchPos, TimesTwo, 0), character); + matchCharacterClass(character, foundBeginningNewLine, m_pattern.newlineCharacterClass()); + branchTest32(NonZero, matchPos).linkTo(findBOLLoop, this); + saveStartIndex.append(jump()); + + foundBeginningNewLine.link(this); + add32(TrustedImm32(1), matchPos); // Advance past newline + saveStartIndex.link(this); + + if (!m_pattern.m_multiline && term->anchors.bolAnchor) + op.m_jumps.append(branchTest32(NonZero, matchPos)); + + ASSERT(!m_pattern.m_body->m_hasFixedSize); + setMatchStart(matchPos); + + move(index, matchPos); + + Label findEOLLoop(this); + foundEndingNewLine.append(branch32(Equal, matchPos, length)); + if (m_charSize == Char8) + load8(BaseIndex(input, matchPos, TimesOne, 0), character); + else + load16(BaseIndex(input, matchPos, TimesTwo, 0), character); + matchCharacterClass(character, foundEndingNewLine, m_pattern.newlineCharacterClass()); + add32(TrustedImm32(1), matchPos); + jump(findEOLLoop); + + foundEndingNewLine.link(this); + + if (!m_pattern.m_multiline && term->anchors.eolAnchor) + op.m_jumps.append(branch32(NotEqual, matchPos, length)); + + move(matchPos, index); + } + + void backtrackDotStarEnclosure(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + + // Code generation/backtracking for simple terms + // (pattern characters, character classes, and assertions). + // These methods farm out work to the set of functions above. + void generateTerm(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + switch (term->type) { + case PatternTerm::TypePatternCharacter: + switch (term->quantityType) { + case QuantifierFixedCount: + if (term->quantityCount == 1) + generatePatternCharacterOnce(opIndex); + else + generatePatternCharacterFixed(opIndex); + break; + case QuantifierGreedy: + generatePatternCharacterGreedy(opIndex); + break; + case QuantifierNonGreedy: + generatePatternCharacterNonGreedy(opIndex); + break; + } + break; + + case PatternTerm::TypeCharacterClass: + switch (term->quantityType) { + case QuantifierFixedCount: + if (term->quantityCount == 1) + generateCharacterClassOnce(opIndex); + else + generateCharacterClassFixed(opIndex); + break; + case QuantifierGreedy: + generateCharacterClassGreedy(opIndex); + break; + case QuantifierNonGreedy: + generateCharacterClassNonGreedy(opIndex); + break; + } + break; + + case PatternTerm::TypeAssertionBOL: + generateAssertionBOL(opIndex); + break; + + case PatternTerm::TypeAssertionEOL: + generateAssertionEOL(opIndex); + break; + + case PatternTerm::TypeAssertionWordBoundary: + generateAssertionWordBoundary(opIndex); + break; + + case PatternTerm::TypeForwardReference: + break; + + case PatternTerm::TypeParenthesesSubpattern: + case PatternTerm::TypeParentheticalAssertion: + ASSERT_NOT_REACHED(); + case PatternTerm::TypeBackReference: + m_shouldFallBack = true; + break; + case PatternTerm::TypeDotStarEnclosure: + generateDotStarEnclosure(opIndex); + break; + } + } + void backtrackTerm(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + switch (term->type) { + case PatternTerm::TypePatternCharacter: + switch (term->quantityType) { + case QuantifierFixedCount: + if (term->quantityCount == 1) + backtrackPatternCharacterOnce(opIndex); + else + backtrackPatternCharacterFixed(opIndex); + break; + case QuantifierGreedy: + backtrackPatternCharacterGreedy(opIndex); + break; + case QuantifierNonGreedy: + backtrackPatternCharacterNonGreedy(opIndex); + break; + } + break; + + case PatternTerm::TypeCharacterClass: + switch (term->quantityType) { + case QuantifierFixedCount: + if (term->quantityCount == 1) + backtrackCharacterClassOnce(opIndex); + else + backtrackCharacterClassFixed(opIndex); + break; + case QuantifierGreedy: + backtrackCharacterClassGreedy(opIndex); + break; + case QuantifierNonGreedy: + backtrackCharacterClassNonGreedy(opIndex); + break; + } + break; + + case PatternTerm::TypeAssertionBOL: + backtrackAssertionBOL(opIndex); + break; + + case PatternTerm::TypeAssertionEOL: + backtrackAssertionEOL(opIndex); + break; + + case PatternTerm::TypeAssertionWordBoundary: + backtrackAssertionWordBoundary(opIndex); + break; + + case PatternTerm::TypeForwardReference: + break; + + case PatternTerm::TypeParenthesesSubpattern: + case PatternTerm::TypeParentheticalAssertion: + ASSERT_NOT_REACHED(); + + case PatternTerm::TypeDotStarEnclosure: + backtrackDotStarEnclosure(opIndex); + break; + + case PatternTerm::TypeBackReference: + m_shouldFallBack = true; + break; + } + } + + void generate() + { + // Forwards generate the matching code. + ASSERT(m_ops.size()); + size_t opIndex = 0; + + do { + YarrOp& op = m_ops[opIndex]; + switch (op.m_op) { + + case OpTerm: + generateTerm(opIndex); + break; + + // OpBodyAlternativeBegin/Next/End + // + // These nodes wrap the set of alternatives in the body of the regular expression. + // There may be either one or two chains of OpBodyAlternative nodes, one representing + // the 'once through' sequence of alternatives (if any exist), and one representing + // the repeating alternatives (again, if any exist). + // + // Upon normal entry to the Begin alternative, we will check that input is available. + // Reentry to the Begin alternative will take place after the check has taken place, + // and will assume that the input position has already been progressed as appropriate. + // + // Entry to subsequent Next/End alternatives occurs when the prior alternative has + // successfully completed a match - return a success state from JIT code. + // + // Next alternatives allow for reentry optimized to suit backtracking from its + // preceding alternative. It expects the input position to still be set to a position + // appropriate to its predecessor, and it will only perform an input check if the + // predecessor had a minimum size less than its own. + // + // In the case 'once through' expressions, the End node will also have a reentry + // point to jump to when the last alternative fails. Again, this expects the input + // position to still reflect that expected by the prior alternative. + case OpBodyAlternativeBegin: { + PatternAlternative* alternative = op.m_alternative; + + // Upon entry at the head of the set of alternatives, check if input is available + // to run the first alternative. (This progresses the input position). + op.m_jumps.append(jumpIfNoAvailableInput(alternative->m_minimumSize)); + // We will reenter after the check, and assume the input position to have been + // set as appropriate to this alternative. + op.m_reentry = label(); + + m_checked += alternative->m_minimumSize; + break; + } + case OpBodyAlternativeNext: + case OpBodyAlternativeEnd: { + PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; + PatternAlternative* alternative = op.m_alternative; + + // If we get here, the prior alternative matched - return success. + + // Adjust the stack pointer to remove the pattern's frame. + removeCallFrame(); + + // Load appropriate values into the return register and the first output + // slot, and return. In the case of pattern with a fixed size, we will + // not have yet set the value in the first + ASSERT(index != returnRegister); + if (m_pattern.m_body->m_hasFixedSize) { + move(index, returnRegister); + if (priorAlternative->m_minimumSize) + sub32(Imm32(priorAlternative->m_minimumSize), returnRegister); + if (compileMode == IncludeSubpatterns) + store32(returnRegister, output); + } else + getMatchStart(returnRegister); + if (compileMode == IncludeSubpatterns) + store32(index, Address(output, 4)); + move(index, returnRegister2); + + generateReturn(); + + // This is the divide between the tail of the prior alternative, above, and + // the head of the subsequent alternative, below. + + if (op.m_op == OpBodyAlternativeNext) { + // This is the reentry point for the Next alternative. We expect any code + // that jumps here to do so with the input position matching that of the + // PRIOR alteranative, and we will only check input availability if we + // need to progress it forwards. + op.m_reentry = label(); + if (alternative->m_minimumSize > priorAlternative->m_minimumSize) { + add32(Imm32(alternative->m_minimumSize - priorAlternative->m_minimumSize), index); + op.m_jumps.append(jumpIfNoAvailableInput()); + } else if (priorAlternative->m_minimumSize > alternative->m_minimumSize) + sub32(Imm32(priorAlternative->m_minimumSize - alternative->m_minimumSize), index); + } else if (op.m_nextOp == notFound) { + // This is the reentry point for the End of 'once through' alternatives, + // jumped to when the last alternative fails to match. + op.m_reentry = label(); + sub32(Imm32(priorAlternative->m_minimumSize), index); + } + + if (op.m_op == OpBodyAlternativeNext) + m_checked += alternative->m_minimumSize; + m_checked -= priorAlternative->m_minimumSize; + break; + } + + // OpSimpleNestedAlternativeBegin/Next/End + // OpNestedAlternativeBegin/Next/End + // + // These nodes are used to handle sets of alternatives that are nested within + // subpatterns and parenthetical assertions. The 'simple' forms are used where + // we do not need to be able to backtrack back into any alternative other than + // the last, the normal forms allow backtracking into any alternative. + // + // Each Begin/Next node is responsible for planting an input check to ensure + // sufficient input is available on entry. Next nodes additionally need to + // jump to the end - Next nodes use the End node's m_jumps list to hold this + // set of jumps. + // + // In the non-simple forms, successful alternative matches must store a + // 'return address' using a DataLabelPtr, used to store the address to jump + // to when backtracking, to get to the code for the appropriate alternative. + case OpSimpleNestedAlternativeBegin: + case OpNestedAlternativeBegin: { + PatternTerm* term = op.m_term; + PatternAlternative* alternative = op.m_alternative; + PatternDisjunction* disjunction = term->parentheses.disjunction; + + // Calculate how much input we need to check for, and if non-zero check. + op.m_checkAdjust = alternative->m_minimumSize; + if ((term->quantityType == QuantifierFixedCount) && (term->type != PatternTerm::TypeParentheticalAssertion)) + op.m_checkAdjust -= disjunction->m_minimumSize; + if (op.m_checkAdjust) + op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust)); + + m_checked += op.m_checkAdjust; + break; + } + case OpSimpleNestedAlternativeNext: + case OpNestedAlternativeNext: { + PatternTerm* term = op.m_term; + PatternAlternative* alternative = op.m_alternative; + PatternDisjunction* disjunction = term->parentheses.disjunction; + + // In the non-simple case, store a 'return address' so we can backtrack correctly. + if (op.m_op == OpNestedAlternativeNext) { + unsigned parenthesesFrameLocation = term->frameLocation; + unsigned alternativeFrameLocation = parenthesesFrameLocation; + if (term->quantityType != QuantifierFixedCount) + alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; + op.m_returnAddress = storeToFrameWithPatch(alternativeFrameLocation); + } + + if (term->quantityType != QuantifierFixedCount && !m_ops[op.m_previousOp].m_alternative->m_minimumSize) { + // If the previous alternative matched without consuming characters then + // backtrack to try to match while consumming some input. + op.m_zeroLengthMatch = branch32(Equal, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + } + + // If we reach here then the last alternative has matched - jump to the + // End node, to skip over any further alternatives. + // + // FIXME: this is logically O(N^2) (though N can be expected to be very + // small). We could avoid this either by adding an extra jump to the JIT + // data structures, or by making backtracking code that jumps to Next + // alternatives are responsible for checking that input is available (if + // we didn't need to plant the input checks, then m_jumps would be free). + YarrOp* endOp = &m_ops[op.m_nextOp]; + while (endOp->m_nextOp != notFound) { + ASSERT(endOp->m_op == OpSimpleNestedAlternativeNext || endOp->m_op == OpNestedAlternativeNext); + endOp = &m_ops[endOp->m_nextOp]; + } + ASSERT(endOp->m_op == OpSimpleNestedAlternativeEnd || endOp->m_op == OpNestedAlternativeEnd); + endOp->m_jumps.append(jump()); + + // This is the entry point for the next alternative. + op.m_reentry = label(); + + // Calculate how much input we need to check for, and if non-zero check. + op.m_checkAdjust = alternative->m_minimumSize; + if ((term->quantityType == QuantifierFixedCount) && (term->type != PatternTerm::TypeParentheticalAssertion)) + op.m_checkAdjust -= disjunction->m_minimumSize; + if (op.m_checkAdjust) + op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust)); + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked -= lastOp.m_checkAdjust; + m_checked += op.m_checkAdjust; + break; + } + case OpSimpleNestedAlternativeEnd: + case OpNestedAlternativeEnd: { + PatternTerm* term = op.m_term; + + // In the non-simple case, store a 'return address' so we can backtrack correctly. + if (op.m_op == OpNestedAlternativeEnd) { + unsigned parenthesesFrameLocation = term->frameLocation; + unsigned alternativeFrameLocation = parenthesesFrameLocation; + if (term->quantityType != QuantifierFixedCount) + alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; + op.m_returnAddress = storeToFrameWithPatch(alternativeFrameLocation); + } + + if (term->quantityType != QuantifierFixedCount && !m_ops[op.m_previousOp].m_alternative->m_minimumSize) { + // If the previous alternative matched without consuming characters then + // backtrack to try to match while consumming some input. + op.m_zeroLengthMatch = branch32(Equal, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + } + + // If this set of alternatives contains more than one alternative, + // then the Next nodes will have planted jumps to the End, and added + // them to this node's m_jumps list. + op.m_jumps.link(this); + op.m_jumps.clear(); + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked -= lastOp.m_checkAdjust; + break; + } + + // OpParenthesesSubpatternOnceBegin/End + // + // These nodes support (optionally) capturing subpatterns, that have a + // quantity count of 1 (this covers fixed once, and ?/?? quantifiers). + case OpParenthesesSubpatternOnceBegin: { + PatternTerm* term = op.m_term; + unsigned parenthesesFrameLocation = term->frameLocation; + const RegisterID indexTemporary = regT0; + ASSERT(term->quantityCount == 1); + + // Upon entry to a Greedy quantified set of parenthese store the index. + // We'll use this for two purposes: + // - To indicate which iteration we are on of mathing the remainder of + // the expression after the parentheses - the first, including the + // match within the parentheses, or the second having skipped over them. + // - To check for empty matches, which must be rejected. + // + // At the head of a NonGreedy set of parentheses we'll immediately set the + // value on the stack to -1 (indicating a match skipping the subpattern), + // and plant a jump to the end. We'll also plant a label to backtrack to + // to reenter the subpattern later, with a store to set up index on the + // second iteration. + // + // FIXME: for capturing parens, could use the index in the capture array? + if (term->quantityType == QuantifierGreedy) + storeToFrame(index, parenthesesFrameLocation); + else if (term->quantityType == QuantifierNonGreedy) { + storeToFrame(TrustedImm32(-1), parenthesesFrameLocation); + op.m_jumps.append(jump()); + op.m_reentry = label(); + storeToFrame(index, parenthesesFrameLocation); + } + + // If the parenthese are capturing, store the starting index value to the + // captures array, offsetting as necessary. + // + // FIXME: could avoid offsetting this value in JIT code, apply + // offsets only afterwards, at the point the results array is + // being accessed. + if (term->capture() && compileMode == IncludeSubpatterns) { + int inputOffset = term->inputPosition - m_checked; + if (term->quantityType == QuantifierFixedCount) + inputOffset -= term->parentheses.disjunction->m_minimumSize; + if (inputOffset) { + move(index, indexTemporary); + add32(Imm32(inputOffset), indexTemporary); + setSubpatternStart(indexTemporary, term->parentheses.subpatternId); + } else + setSubpatternStart(index, term->parentheses.subpatternId); + } + break; + } + case OpParenthesesSubpatternOnceEnd: { + PatternTerm* term = op.m_term; + const RegisterID indexTemporary = regT0; + ASSERT(term->quantityCount == 1); + +#ifndef NDEBUG + // Runtime ASSERT to make sure that the nested alternative handled the + // "no input consumed" check. + if (term->quantityType != QuantifierFixedCount && !term->parentheses.disjunction->m_minimumSize) { + Jump pastBreakpoint; + pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + breakpoint(); + pastBreakpoint.link(this); + } +#endif + + // If the parenthese are capturing, store the ending index value to the + // captures array, offsetting as necessary. + // + // FIXME: could avoid offsetting this value in JIT code, apply + // offsets only afterwards, at the point the results array is + // being accessed. + if (term->capture() && compileMode == IncludeSubpatterns) { + int inputOffset = term->inputPosition - m_checked; + if (inputOffset) { + move(index, indexTemporary); + add32(Imm32(inputOffset), indexTemporary); + setSubpatternEnd(indexTemporary, term->parentheses.subpatternId); + } else + setSubpatternEnd(index, term->parentheses.subpatternId); + } + + // If the parentheses are quantified Greedy then add a label to jump back + // to if get a failed match from after the parentheses. For NonGreedy + // parentheses, link the jump from before the subpattern to here. + if (term->quantityType == QuantifierGreedy) + op.m_reentry = label(); + else if (term->quantityType == QuantifierNonGreedy) { + YarrOp& beginOp = m_ops[op.m_previousOp]; + beginOp.m_jumps.link(this); + } + break; + } + + // OpParenthesesSubpatternTerminalBegin/End + case OpParenthesesSubpatternTerminalBegin: { + PatternTerm* term = op.m_term; + ASSERT(term->quantityType == QuantifierGreedy); + ASSERT(term->quantityCount == quantifyInfinite); + ASSERT(!term->capture()); + + // Upon entry set a label to loop back to. + op.m_reentry = label(); + + // Store the start index of the current match; we need to reject zero + // length matches. + storeToFrame(index, term->frameLocation); + break; + } + case OpParenthesesSubpatternTerminalEnd: { + YarrOp& beginOp = m_ops[op.m_previousOp]; +#ifndef NDEBUG + PatternTerm* term = op.m_term; + + // Runtime ASSERT to make sure that the nested alternative handled the + // "no input consumed" check. + Jump pastBreakpoint; + pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + breakpoint(); + pastBreakpoint.link(this); +#endif + + // We know that the match is non-zero, we can accept it and + // loop back up to the head of the subpattern. + jump(beginOp.m_reentry); + + // This is the entry point to jump to when we stop matching - we will + // do so once the subpattern cannot match any more. + op.m_reentry = label(); + break; + } + + // OpParentheticalAssertionBegin/End + case OpParentheticalAssertionBegin: { + PatternTerm* term = op.m_term; + + // Store the current index - assertions should not update index, so + // we will need to restore it upon a successful match. + unsigned parenthesesFrameLocation = term->frameLocation; + storeToFrame(index, parenthesesFrameLocation); + + // Check + op.m_checkAdjust = m_checked - term->inputPosition; + if (op.m_checkAdjust) + sub32(Imm32(op.m_checkAdjust), index); + + m_checked -= op.m_checkAdjust; + break; + } + case OpParentheticalAssertionEnd: { + PatternTerm* term = op.m_term; + + // Restore the input index value. + unsigned parenthesesFrameLocation = term->frameLocation; + loadFromFrame(parenthesesFrameLocation, index); + + // If inverted, a successful match of the assertion must be treated + // as a failure, so jump to backtracking. + if (term->invert()) { + op.m_jumps.append(jump()); + op.m_reentry = label(); + } + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked += lastOp.m_checkAdjust; + break; + } + + case OpMatchFailed: + removeCallFrame(); + move(TrustedImmPtr((void*)WTF::notFound), returnRegister); + move(TrustedImm32(0), returnRegister2); + generateReturn(); + break; + } + + ++opIndex; + } while (opIndex < m_ops.size()); + } + + void backtrack() + { + // Backwards generate the backtracking code. + size_t opIndex = m_ops.size(); + ASSERT(opIndex); + + do { + --opIndex; + YarrOp& op = m_ops[opIndex]; + switch (op.m_op) { + + case OpTerm: + backtrackTerm(opIndex); + break; + + // OpBodyAlternativeBegin/Next/End + // + // For each Begin/Next node representing an alternative, we need to decide what to do + // in two circumstances: + // - If we backtrack back into this node, from within the alternative. + // - If the input check at the head of the alternative fails (if this exists). + // + // We treat these two cases differently since in the former case we have slightly + // more information - since we are backtracking out of a prior alternative we know + // that at least enough input was available to run it. For example, given the regular + // expression /a|b/, if we backtrack out of the first alternative (a failed pattern + // character match of 'a'), then we need not perform an additional input availability + // check before running the second alternative. + // + // Backtracking required differs for the last alternative, which in the case of the + // repeating set of alternatives must loop. The code generated for the last alternative + // will also be used to handle all input check failures from any prior alternatives - + // these require similar functionality, in seeking the next available alternative for + // which there is sufficient input. + // + // Since backtracking of all other alternatives simply requires us to link backtracks + // to the reentry point for the subsequent alternative, we will only be generating any + // code when backtracking the last alternative. + case OpBodyAlternativeBegin: + case OpBodyAlternativeNext: { + PatternAlternative* alternative = op.m_alternative; + + if (op.m_op == OpBodyAlternativeNext) { + PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; + m_checked += priorAlternative->m_minimumSize; + } + m_checked -= alternative->m_minimumSize; + + // Is this the last alternative? If not, then if we backtrack to this point we just + // need to jump to try to match the next alternative. + if (m_ops[op.m_nextOp].m_op != OpBodyAlternativeEnd) { + m_backtrackingState.linkTo(m_ops[op.m_nextOp].m_reentry, this); + break; + } + YarrOp& endOp = m_ops[op.m_nextOp]; + + YarrOp* beginOp = &op; + while (beginOp->m_op != OpBodyAlternativeBegin) { + ASSERT(beginOp->m_op == OpBodyAlternativeNext); + beginOp = &m_ops[beginOp->m_previousOp]; + } + + bool onceThrough = endOp.m_nextOp == notFound; + + // First, generate code to handle cases where we backtrack out of an attempted match + // of the last alternative. If this is a 'once through' set of alternatives then we + // have nothing to do - link this straight through to the End. + if (onceThrough) + m_backtrackingState.linkTo(endOp.m_reentry, this); + else { + // If we don't need to move the input poistion, and the pattern has a fixed size + // (in which case we omit the store of the start index until the pattern has matched) + // then we can just link the backtrack out of the last alternative straight to the + // head of the first alternative. + if (m_pattern.m_body->m_hasFixedSize + && (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize) + && (alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize == 1)) + m_backtrackingState.linkTo(beginOp->m_reentry, this); + else { + // We need to generate a trampoline of code to execute before looping back + // around to the first alternative. + m_backtrackingState.link(this); + + // If the pattern size is not fixed, then store the start index, for use if we match. + if (!m_pattern.m_body->m_hasFixedSize) { + if (alternative->m_minimumSize == 1) + setMatchStart(index); + else { + move(index, regT0); + if (alternative->m_minimumSize) + sub32(Imm32(alternative->m_minimumSize - 1), regT0); + else + add32(TrustedImm32(1), regT0); + setMatchStart(regT0); + } + } + + // Generate code to loop. Check whether the last alternative is longer than the + // first (e.g. /a|xy/ or /a|xyz/). + if (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize) { + // We want to loop, and increment input position. If the delta is 1, it is + // already correctly incremented, if more than one then decrement as appropriate. + unsigned delta = alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize; + ASSERT(delta); + if (delta != 1) + sub32(Imm32(delta - 1), index); + jump(beginOp->m_reentry); + } else { + // If the first alternative has minimum size 0xFFFFFFFFu, then there cannot + // be sufficent input available to handle this, so just fall through. + unsigned delta = beginOp->m_alternative->m_minimumSize - alternative->m_minimumSize; + if (delta != 0xFFFFFFFFu) { + // We need to check input because we are incrementing the input. + add32(Imm32(delta + 1), index); + checkInput().linkTo(beginOp->m_reentry, this); + } + } + } + } + + // We can reach this point in the code in two ways: + // - Fallthrough from the code above (a repeating alternative backtracked out of its + // last alternative, and did not have sufficent input to run the first). + // - We will loop back up to the following label when a releating alternative loops, + // following a failed input check. + // + // Either way, we have just failed the input check for the first alternative. + Label firstInputCheckFailed(this); + + // Generate code to handle input check failures from alternatives except the last. + // prevOp is the alternative we're handling a bail out from (initially Begin), and + // nextOp is the alternative we will be attempting to reenter into. + // + // We will link input check failures from the forwards matching path back to the code + // that can handle them. + YarrOp* prevOp = beginOp; + YarrOp* nextOp = &m_ops[beginOp->m_nextOp]; + while (nextOp->m_op != OpBodyAlternativeEnd) { + prevOp->m_jumps.link(this); + + // We only get here if an input check fails, it is only worth checking again + // if the next alternative has a minimum size less than the last. + if (prevOp->m_alternative->m_minimumSize > nextOp->m_alternative->m_minimumSize) { + // FIXME: if we added an extra label to YarrOp, we could avoid needing to + // subtract delta back out, and reduce this code. Should performance test + // the benefit of this. + unsigned delta = prevOp->m_alternative->m_minimumSize - nextOp->m_alternative->m_minimumSize; + sub32(Imm32(delta), index); + Jump fail = jumpIfNoAvailableInput(); + add32(Imm32(delta), index); + jump(nextOp->m_reentry); + fail.link(this); + } else if (prevOp->m_alternative->m_minimumSize < nextOp->m_alternative->m_minimumSize) + add32(Imm32(nextOp->m_alternative->m_minimumSize - prevOp->m_alternative->m_minimumSize), index); + prevOp = nextOp; + nextOp = &m_ops[nextOp->m_nextOp]; + } + + // We fall through to here if there is insufficient input to run the last alternative. + + // If there is insufficient input to run the last alternative, then for 'once through' + // alternatives we are done - just jump back up into the forwards matching path at the End. + if (onceThrough) { + op.m_jumps.linkTo(endOp.m_reentry, this); + jump(endOp.m_reentry); + break; + } + + // For repeating alternatives, link any input check failure from the last alternative to + // this point. + op.m_jumps.link(this); + + bool needsToUpdateMatchStart = !m_pattern.m_body->m_hasFixedSize; + + // Check for cases where input position is already incremented by 1 for the last + // alternative (this is particularly useful where the minimum size of the body + // disjunction is 0, e.g. /a*|b/). + if (needsToUpdateMatchStart && alternative->m_minimumSize == 1) { + // index is already incremented by 1, so just store it now! + setMatchStart(index); + needsToUpdateMatchStart = false; + } + + // Check whether there is sufficient input to loop. Increment the input position by + // one, and check. Also add in the minimum disjunction size before checking - there + // is no point in looping if we're just going to fail all the input checks around + // the next iteration. + ASSERT(alternative->m_minimumSize >= m_pattern.m_body->m_minimumSize); + if (alternative->m_minimumSize == m_pattern.m_body->m_minimumSize) { + // If the last alternative had the same minimum size as the disjunction, + // just simply increment input pos by 1, no adjustment based on minimum size. + add32(TrustedImm32(1), index); + } else { + // If the minumum for the last alternative was one greater than than that + // for the disjunction, we're already progressed by 1, nothing to do! + unsigned delta = (alternative->m_minimumSize - m_pattern.m_body->m_minimumSize) - 1; + if (delta) + sub32(Imm32(delta), index); + } + Jump matchFailed = jumpIfNoAvailableInput(); + + if (needsToUpdateMatchStart) { + if (!m_pattern.m_body->m_minimumSize) + setMatchStart(index); + else { + move(index, regT0); + sub32(Imm32(m_pattern.m_body->m_minimumSize), regT0); + setMatchStart(regT0); + } + } + + // Calculate how much more input the first alternative requires than the minimum + // for the body as a whole. If no more is needed then we dont need an additional + // input check here - jump straight back up to the start of the first alternative. + if (beginOp->m_alternative->m_minimumSize == m_pattern.m_body->m_minimumSize) + jump(beginOp->m_reentry); + else { + if (beginOp->m_alternative->m_minimumSize > m_pattern.m_body->m_minimumSize) + add32(Imm32(beginOp->m_alternative->m_minimumSize - m_pattern.m_body->m_minimumSize), index); + else + sub32(Imm32(m_pattern.m_body->m_minimumSize - beginOp->m_alternative->m_minimumSize), index); + checkInput().linkTo(beginOp->m_reentry, this); + jump(firstInputCheckFailed); + } + + // We jump to here if we iterate to the point that there is insufficient input to + // run any matches, and need to return a failure state from JIT code. + matchFailed.link(this); + + removeCallFrame(); + move(TrustedImmPtr((void*)WTF::notFound), returnRegister); + move(TrustedImm32(0), returnRegister2); + generateReturn(); + break; + } + case OpBodyAlternativeEnd: { + // We should never backtrack back into a body disjunction. + ASSERT(m_backtrackingState.isEmpty()); + + PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative; + m_checked += priorAlternative->m_minimumSize; + break; + } + + // OpSimpleNestedAlternativeBegin/Next/End + // OpNestedAlternativeBegin/Next/End + // + // Generate code for when we backtrack back out of an alternative into + // a Begin or Next node, or when the entry input count check fails. If + // there are more alternatives we need to jump to the next alternative, + // if not we backtrack back out of the current set of parentheses. + // + // In the case of non-simple nested assertions we need to also link the + // 'return address' appropriately to backtrack back out into the correct + // alternative. + case OpSimpleNestedAlternativeBegin: + case OpSimpleNestedAlternativeNext: + case OpNestedAlternativeBegin: + case OpNestedAlternativeNext: { + YarrOp& nextOp = m_ops[op.m_nextOp]; + bool isBegin = op.m_previousOp == notFound; + bool isLastAlternative = nextOp.m_nextOp == notFound; + ASSERT(isBegin == (op.m_op == OpSimpleNestedAlternativeBegin || op.m_op == OpNestedAlternativeBegin)); + ASSERT(isLastAlternative == (nextOp.m_op == OpSimpleNestedAlternativeEnd || nextOp.m_op == OpNestedAlternativeEnd)); + + // Treat an input check failure the same as a failed match. + m_backtrackingState.append(op.m_jumps); + + // Set the backtracks to jump to the appropriate place. We may need + // to link the backtracks in one of three different way depending on + // the type of alternative we are dealing with: + // - A single alternative, with no simplings. + // - The last alternative of a set of two or more. + // - An alternative other than the last of a set of two or more. + // + // In the case of a single alternative on its own, we don't need to + // jump anywhere - if the alternative fails to match we can just + // continue to backtrack out of the parentheses without jumping. + // + // In the case of the last alternative in a set of more than one, we + // need to jump to return back out to the beginning. We'll do so by + // adding a jump to the End node's m_jumps list, and linking this + // when we come to generate the Begin node. For alternatives other + // than the last, we need to jump to the next alternative. + // + // If the alternative had adjusted the input position we must link + // backtracking to here, correct, and then jump on. If not we can + // link the backtracks directly to their destination. + if (op.m_checkAdjust) { + // Handle the cases where we need to link the backtracks here. + m_backtrackingState.link(this); + sub32(Imm32(op.m_checkAdjust), index); + if (!isLastAlternative) { + // An alternative that is not the last should jump to its successor. + jump(nextOp.m_reentry); + } else if (!isBegin) { + // The last of more than one alternatives must jump back to the beginning. + nextOp.m_jumps.append(jump()); + } else { + // A single alternative on its own can fall through. + m_backtrackingState.fallthrough(); + } + } else { + // Handle the cases where we can link the backtracks directly to their destinations. + if (!isLastAlternative) { + // An alternative that is not the last should jump to its successor. + m_backtrackingState.linkTo(nextOp.m_reentry, this); + } else if (!isBegin) { + // The last of more than one alternatives must jump back to the beginning. + m_backtrackingState.takeBacktracksToJumpList(nextOp.m_jumps, this); + } + // In the case of a single alternative on its own do nothing - it can fall through. + } + + // If there is a backtrack jump from a zero length match link it here. + if (op.m_zeroLengthMatch.isSet()) + m_backtrackingState.append(op.m_zeroLengthMatch); + + // At this point we've handled the backtracking back into this node. + // Now link any backtracks that need to jump to here. + + // For non-simple alternatives, link the alternative's 'return address' + // so that we backtrack back out into the previous alternative. + if (op.m_op == OpNestedAlternativeNext) + m_backtrackingState.append(op.m_returnAddress); + + // If there is more than one alternative, then the last alternative will + // have planted a jump to be linked to the end. This jump was added to the + // End node's m_jumps list. If we are back at the beginning, link it here. + if (isBegin) { + YarrOp* endOp = &m_ops[op.m_nextOp]; + while (endOp->m_nextOp != notFound) { + ASSERT(endOp->m_op == OpSimpleNestedAlternativeNext || endOp->m_op == OpNestedAlternativeNext); + endOp = &m_ops[endOp->m_nextOp]; + } + ASSERT(endOp->m_op == OpSimpleNestedAlternativeEnd || endOp->m_op == OpNestedAlternativeEnd); + m_backtrackingState.append(endOp->m_jumps); + } + + if (!isBegin) { + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked += lastOp.m_checkAdjust; + } + m_checked -= op.m_checkAdjust; + break; + } + case OpSimpleNestedAlternativeEnd: + case OpNestedAlternativeEnd: { + PatternTerm* term = op.m_term; + + // If there is a backtrack jump from a zero length match link it here. + if (op.m_zeroLengthMatch.isSet()) + m_backtrackingState.append(op.m_zeroLengthMatch); + + // If we backtrack into the end of a simple subpattern do nothing; + // just continue through into the last alternative. If we backtrack + // into the end of a non-simple set of alterntives we need to jump + // to the backtracking return address set up during generation. + if (op.m_op == OpNestedAlternativeEnd) { + m_backtrackingState.link(this); + + // Plant a jump to the return address. + unsigned parenthesesFrameLocation = term->frameLocation; + unsigned alternativeFrameLocation = parenthesesFrameLocation; + if (term->quantityType != QuantifierFixedCount) + alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; + loadFromFrameAndJump(alternativeFrameLocation); + + // Link the DataLabelPtr associated with the end of the last + // alternative to this point. + m_backtrackingState.append(op.m_returnAddress); + } + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked += lastOp.m_checkAdjust; + break; + } + + // OpParenthesesSubpatternOnceBegin/End + // + // When we are backtracking back out of a capturing subpattern we need + // to clear the start index in the matches output array, to record that + // this subpattern has not been captured. + // + // When backtracking back out of a Greedy quantified subpattern we need + // to catch this, and try running the remainder of the alternative after + // the subpattern again, skipping the parentheses. + // + // Upon backtracking back into a quantified set of parentheses we need to + // check whether we were currently skipping the subpattern. If not, we + // can backtrack into them, if we were we need to either backtrack back + // out of the start of the parentheses, or jump back to the forwards + // matching start, depending of whether the match is Greedy or NonGreedy. + case OpParenthesesSubpatternOnceBegin: { + PatternTerm* term = op.m_term; + ASSERT(term->quantityCount == 1); + + // We only need to backtrack to thispoint if capturing or greedy. + if ((term->capture() && compileMode == IncludeSubpatterns) || term->quantityType == QuantifierGreedy) { + m_backtrackingState.link(this); + + // If capturing, clear the capture (we only need to reset start). + if (term->capture() && compileMode == IncludeSubpatterns) + clearSubpatternStart(term->parentheses.subpatternId); + + // If Greedy, jump to the end. + if (term->quantityType == QuantifierGreedy) { + // Clear the flag in the stackframe indicating we ran through the subpattern. + unsigned parenthesesFrameLocation = term->frameLocation; + storeToFrame(TrustedImm32(-1), parenthesesFrameLocation); + // Jump to after the parentheses, skipping the subpattern. + jump(m_ops[op.m_nextOp].m_reentry); + // A backtrack from after the parentheses, when skipping the subpattern, + // will jump back to here. + op.m_jumps.link(this); + } + + m_backtrackingState.fallthrough(); + } + break; + } + case OpParenthesesSubpatternOnceEnd: { + PatternTerm* term = op.m_term; + + if (term->quantityType != QuantifierFixedCount) { + m_backtrackingState.link(this); + + // Check whether we should backtrack back into the parentheses, or if we + // are currently in a state where we had skipped over the subpattern + // (in which case the flag value on the stack will be -1). + unsigned parenthesesFrameLocation = term->frameLocation; + Jump hadSkipped = branch32(Equal, Address(stackPointerRegister, parenthesesFrameLocation * sizeof(void*)), TrustedImm32(-1)); + + if (term->quantityType == QuantifierGreedy) { + // For Greedy parentheses, we skip after having already tried going + // through the subpattern, so if we get here we're done. + YarrOp& beginOp = m_ops[op.m_previousOp]; + beginOp.m_jumps.append(hadSkipped); + } else { + // For NonGreedy parentheses, we try skipping the subpattern first, + // so if we get here we need to try running through the subpattern + // next. Jump back to the start of the parentheses in the forwards + // matching path. + ASSERT(term->quantityType == QuantifierNonGreedy); + YarrOp& beginOp = m_ops[op.m_previousOp]; + hadSkipped.linkTo(beginOp.m_reentry, this); + } + + m_backtrackingState.fallthrough(); + } + + m_backtrackingState.append(op.m_jumps); + break; + } + + // OpParenthesesSubpatternTerminalBegin/End + // + // Terminal subpatterns will always match - there is nothing after them to + // force a backtrack, and they have a minimum count of 0, and as such will + // always produce an acceptable result. + case OpParenthesesSubpatternTerminalBegin: { + // We will backtrack to this point once the subpattern cannot match any + // more. Since no match is accepted as a successful match (we are Greedy + // quantified with a minimum of zero) jump back to the forwards matching + // path at the end. + YarrOp& endOp = m_ops[op.m_nextOp]; + m_backtrackingState.linkTo(endOp.m_reentry, this); + break; + } + case OpParenthesesSubpatternTerminalEnd: + // We should never be backtracking to here (hence the 'terminal' in the name). + ASSERT(m_backtrackingState.isEmpty()); + m_backtrackingState.append(op.m_jumps); + break; + + // OpParentheticalAssertionBegin/End + case OpParentheticalAssertionBegin: { + PatternTerm* term = op.m_term; + YarrOp& endOp = m_ops[op.m_nextOp]; + + // We need to handle the backtracks upon backtracking back out + // of a parenthetical assertion if either we need to correct + // the input index, or the assertion was inverted. + if (op.m_checkAdjust || term->invert()) { + m_backtrackingState.link(this); + + if (op.m_checkAdjust) + add32(Imm32(op.m_checkAdjust), index); + + // In an inverted assertion failure to match the subpattern + // is treated as a successful match - jump to the end of the + // subpattern. We already have adjusted the input position + // back to that before the assertion, which is correct. + if (term->invert()) + jump(endOp.m_reentry); + + m_backtrackingState.fallthrough(); + } + + // The End node's jump list will contain any backtracks into + // the end of the assertion. Also, if inverted, we will have + // added the failure caused by a successful match to this. + m_backtrackingState.append(endOp.m_jumps); + + m_checked += op.m_checkAdjust; + break; + } + case OpParentheticalAssertionEnd: { + // FIXME: We should really be clearing any nested subpattern + // matches on bailing out from after the pattern. Firefox has + // this bug too (presumably because they use YARR!) + + // Never backtrack into an assertion; later failures bail to before the begin. + m_backtrackingState.takeBacktracksToJumpList(op.m_jumps, this); + + YarrOp& lastOp = m_ops[op.m_previousOp]; + m_checked -= lastOp.m_checkAdjust; + break; + } + + case OpMatchFailed: + break; + } + + } while (opIndex); + } + + // Compilation methods: + // ==================== + + // opCompileParenthesesSubpattern + // Emits ops for a subpattern (set of parentheses). These consist + // of a set of alternatives wrapped in an outer set of nodes for + // the parentheses. + // Supported types of parentheses are 'Once' (quantityCount == 1) + // and 'Terminal' (non-capturing parentheses quantified as greedy + // and infinite). + // Alternatives will use the 'Simple' set of ops if either the + // subpattern is terminal (in which case we will never need to + // backtrack), or if the subpattern only contains one alternative. + void opCompileParenthesesSubpattern(PatternTerm* term) + { + YarrOpCode parenthesesBeginOpCode; + YarrOpCode parenthesesEndOpCode; + YarrOpCode alternativeBeginOpCode = OpSimpleNestedAlternativeBegin; + YarrOpCode alternativeNextOpCode = OpSimpleNestedAlternativeNext; + YarrOpCode alternativeEndOpCode = OpSimpleNestedAlternativeEnd; + + // We can currently only compile quantity 1 subpatterns that are + // not copies. We generate a copy in the case of a range quantifier, + // e.g. /(?:x){3,9}/, or /(?:x)+/ (These are effectively expanded to + // /(?:x){3,3}(?:x){0,6}/ and /(?:x)(?:x)*/ repectively). The problem + // comes where the subpattern is capturing, in which case we would + // need to restore the capture from the first subpattern upon a + // failure in the second. + if (term->quantityCount == 1 && !term->parentheses.isCopy) { + // Select the 'Once' nodes. + parenthesesBeginOpCode = OpParenthesesSubpatternOnceBegin; + parenthesesEndOpCode = OpParenthesesSubpatternOnceEnd; + + // If there is more than one alternative we cannot use the 'simple' nodes. + if (term->parentheses.disjunction->m_alternatives.size() != 1) { + alternativeBeginOpCode = OpNestedAlternativeBegin; + alternativeNextOpCode = OpNestedAlternativeNext; + alternativeEndOpCode = OpNestedAlternativeEnd; + } + } else if (term->parentheses.isTerminal) { + // Select the 'Terminal' nodes. + parenthesesBeginOpCode = OpParenthesesSubpatternTerminalBegin; + parenthesesEndOpCode = OpParenthesesSubpatternTerminalEnd; + } else { + // This subpattern is not supported by the JIT. + m_shouldFallBack = true; + return; + } + + size_t parenBegin = m_ops.size(); + m_ops.append(parenthesesBeginOpCode); + + m_ops.append(alternativeBeginOpCode); + m_ops.last().m_previousOp = notFound; + m_ops.last().m_term = term; + Vector& alternatives = term->parentheses.disjunction->m_alternatives; + for (unsigned i = 0; i < alternatives.size(); ++i) { + size_t lastOpIndex = m_ops.size() - 1; + + PatternAlternative* nestedAlternative = alternatives[i]; + opCompileAlternative(nestedAlternative); + + size_t thisOpIndex = m_ops.size(); + m_ops.append(YarrOp(alternativeNextOpCode)); + + YarrOp& lastOp = m_ops[lastOpIndex]; + YarrOp& thisOp = m_ops[thisOpIndex]; + + lastOp.m_alternative = nestedAlternative; + lastOp.m_nextOp = thisOpIndex; + thisOp.m_previousOp = lastOpIndex; + thisOp.m_term = term; + } + YarrOp& lastOp = m_ops.last(); + ASSERT(lastOp.m_op == alternativeNextOpCode); + lastOp.m_op = alternativeEndOpCode; + lastOp.m_alternative = 0; + lastOp.m_nextOp = notFound; + + size_t parenEnd = m_ops.size(); + m_ops.append(parenthesesEndOpCode); + + m_ops[parenBegin].m_term = term; + m_ops[parenBegin].m_previousOp = notFound; + m_ops[parenBegin].m_nextOp = parenEnd; + m_ops[parenEnd].m_term = term; + m_ops[parenEnd].m_previousOp = parenBegin; + m_ops[parenEnd].m_nextOp = notFound; + } + + // opCompileParentheticalAssertion + // Emits ops for a parenthetical assertion. These consist of an + // OpSimpleNestedAlternativeBegin/Next/End set of nodes wrapping + // the alternatives, with these wrapped by an outer pair of + // OpParentheticalAssertionBegin/End nodes. + // We can always use the OpSimpleNestedAlternative nodes in the + // case of parenthetical assertions since these only ever match + // once, and will never backtrack back into the assertion. + void opCompileParentheticalAssertion(PatternTerm* term) + { + size_t parenBegin = m_ops.size(); + m_ops.append(OpParentheticalAssertionBegin); + + m_ops.append(OpSimpleNestedAlternativeBegin); + m_ops.last().m_previousOp = notFound; + m_ops.last().m_term = term; + Vector& alternatives = term->parentheses.disjunction->m_alternatives; + for (unsigned i = 0; i < alternatives.size(); ++i) { + size_t lastOpIndex = m_ops.size() - 1; + + PatternAlternative* nestedAlternative = alternatives[i]; + opCompileAlternative(nestedAlternative); + + size_t thisOpIndex = m_ops.size(); + m_ops.append(YarrOp(OpSimpleNestedAlternativeNext)); + + YarrOp& lastOp = m_ops[lastOpIndex]; + YarrOp& thisOp = m_ops[thisOpIndex]; + + lastOp.m_alternative = nestedAlternative; + lastOp.m_nextOp = thisOpIndex; + thisOp.m_previousOp = lastOpIndex; + thisOp.m_term = term; + } + YarrOp& lastOp = m_ops.last(); + ASSERT(lastOp.m_op == OpSimpleNestedAlternativeNext); + lastOp.m_op = OpSimpleNestedAlternativeEnd; + lastOp.m_alternative = 0; + lastOp.m_nextOp = notFound; + + size_t parenEnd = m_ops.size(); + m_ops.append(OpParentheticalAssertionEnd); + + m_ops[parenBegin].m_term = term; + m_ops[parenBegin].m_previousOp = notFound; + m_ops[parenBegin].m_nextOp = parenEnd; + m_ops[parenEnd].m_term = term; + m_ops[parenEnd].m_previousOp = parenBegin; + m_ops[parenEnd].m_nextOp = notFound; + } + + // opCompileAlternative + // Called to emit nodes for all terms in an alternative. + void opCompileAlternative(PatternAlternative* alternative) + { + optimizeAlternative(alternative); + + for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { + PatternTerm* term = &alternative->m_terms[i]; + + switch (term->type) { + case PatternTerm::TypeParenthesesSubpattern: + opCompileParenthesesSubpattern(term); + break; + + case PatternTerm::TypeParentheticalAssertion: + opCompileParentheticalAssertion(term); + break; + + default: + m_ops.append(term); + } + } + } + + // opCompileBody + // This method compiles the body disjunction of the regular expression. + // The body consists of two sets of alternatives - zero or more 'once + // through' (BOL anchored) alternatives, followed by zero or more + // repeated alternatives. + // For each of these two sets of alteratives, if not empty they will be + // wrapped in a set of OpBodyAlternativeBegin/Next/End nodes (with the + // 'begin' node referencing the first alternative, and 'next' nodes + // referencing any further alternatives. The begin/next/end nodes are + // linked together in a doubly linked list. In the case of repeating + // alternatives, the end node is also linked back to the beginning. + // If no repeating alternatives exist, then a OpMatchFailed node exists + // to return the failing result. + void opCompileBody(PatternDisjunction* disjunction) + { + Vector& alternatives = disjunction->m_alternatives; + size_t currentAlternativeIndex = 0; + + // Emit the 'once through' alternatives. + if (alternatives.size() && alternatives[0]->onceThrough()) { + m_ops.append(YarrOp(OpBodyAlternativeBegin)); + m_ops.last().m_previousOp = notFound; + + do { + size_t lastOpIndex = m_ops.size() - 1; + PatternAlternative* alternative = alternatives[currentAlternativeIndex]; + opCompileAlternative(alternative); + + size_t thisOpIndex = m_ops.size(); + m_ops.append(YarrOp(OpBodyAlternativeNext)); + + YarrOp& lastOp = m_ops[lastOpIndex]; + YarrOp& thisOp = m_ops[thisOpIndex]; + + lastOp.m_alternative = alternative; + lastOp.m_nextOp = thisOpIndex; + thisOp.m_previousOp = lastOpIndex; + + ++currentAlternativeIndex; + } while (currentAlternativeIndex < alternatives.size() && alternatives[currentAlternativeIndex]->onceThrough()); + + YarrOp& lastOp = m_ops.last(); + + ASSERT(lastOp.m_op == OpBodyAlternativeNext); + lastOp.m_op = OpBodyAlternativeEnd; + lastOp.m_alternative = 0; + lastOp.m_nextOp = notFound; + } + + if (currentAlternativeIndex == alternatives.size()) { + m_ops.append(YarrOp(OpMatchFailed)); + return; + } + + // Emit the repeated alternatives. + size_t repeatLoop = m_ops.size(); + m_ops.append(YarrOp(OpBodyAlternativeBegin)); + m_ops.last().m_previousOp = notFound; + do { + size_t lastOpIndex = m_ops.size() - 1; + PatternAlternative* alternative = alternatives[currentAlternativeIndex]; + ASSERT(!alternative->onceThrough()); + opCompileAlternative(alternative); + + size_t thisOpIndex = m_ops.size(); + m_ops.append(YarrOp(OpBodyAlternativeNext)); + + YarrOp& lastOp = m_ops[lastOpIndex]; + YarrOp& thisOp = m_ops[thisOpIndex]; + + lastOp.m_alternative = alternative; + lastOp.m_nextOp = thisOpIndex; + thisOp.m_previousOp = lastOpIndex; + + ++currentAlternativeIndex; + } while (currentAlternativeIndex < alternatives.size()); + YarrOp& lastOp = m_ops.last(); + ASSERT(lastOp.m_op == OpBodyAlternativeNext); + lastOp.m_op = OpBodyAlternativeEnd; + lastOp.m_alternative = 0; + lastOp.m_nextOp = repeatLoop; + } + + void generateEnter() + { +#if CPU(X86_64) + push(X86Registers::ebp); + move(stackPointerRegister, X86Registers::ebp); + push(X86Registers::ebx); +#elif CPU(X86) + push(X86Registers::ebp); + move(stackPointerRegister, X86Registers::ebp); + // TODO: do we need spill registers to fill the output pointer if there are no sub captures? + push(X86Registers::ebx); + push(X86Registers::edi); + push(X86Registers::esi); + // load output into edi (2 = saved ebp + return address). + #if COMPILER(MSVC) + loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), input); + loadPtr(Address(X86Registers::ebp, 3 * sizeof(void*)), index); + loadPtr(Address(X86Registers::ebp, 4 * sizeof(void*)), length); + if (compileMode == IncludeSubpatterns) + loadPtr(Address(X86Registers::ebp, 5 * sizeof(void*)), output); + #else + if (compileMode == IncludeSubpatterns) + loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), output); + #endif +#elif CPU(ARM) + push(ARMRegisters::r4); + push(ARMRegisters::r5); + push(ARMRegisters::r6); +#if CPU(ARM_TRADITIONAL) + push(ARMRegisters::r8); // scratch register +#endif + if (compileMode == IncludeSubpatterns) + move(ARMRegisters::r3, output); +#elif CPU(SH4) + push(SH4Registers::r11); + push(SH4Registers::r13); +#elif CPU(MIPS) + // Do nothing. +#endif + } + + void generateReturn() + { +#if CPU(X86_64) + pop(X86Registers::ebx); + pop(X86Registers::ebp); +#elif CPU(X86) + pop(X86Registers::esi); + pop(X86Registers::edi); + pop(X86Registers::ebx); + pop(X86Registers::ebp); +#elif CPU(ARM) +#if CPU(ARM_TRADITIONAL) + pop(ARMRegisters::r8); // scratch register +#endif + pop(ARMRegisters::r6); + pop(ARMRegisters::r5); + pop(ARMRegisters::r4); +#elif CPU(SH4) + pop(SH4Registers::r13); + pop(SH4Registers::r11); +#elif CPU(MIPS) + // Do nothing +#endif + ret(); + } + +public: + YarrGenerator(YarrPattern& pattern, YarrCharSize charSize) + : m_pattern(pattern) + , m_charSize(charSize) + , m_charScale(m_charSize == Char8 ? TimesOne: TimesTwo) + , m_shouldFallBack(false) + , m_checked(0) + { + } + + void compile(JSGlobalData* globalData, YarrCodeBlock& jitObject) + { + generateEnter(); + + Jump hasInput = checkInput(); + move(TrustedImmPtr((void*)WTF::notFound), returnRegister); + move(TrustedImm32(0), returnRegister2); + generateReturn(); + hasInput.link(this); + + if (compileMode == IncludeSubpatterns) { + for (unsigned i = 0; i < m_pattern.m_numSubpatterns + 1; ++i) + store32(TrustedImm32(-1), Address(output, (i << 1) * sizeof(int))); + } + + if (!m_pattern.m_body->m_hasFixedSize) + setMatchStart(index); + + initCallFrame(); + + // Compile the pattern to the internal 'YarrOp' representation. + opCompileBody(m_pattern.m_body); + + // If we encountered anything we can't handle in the JIT code + // (e.g. backreferences) then return early. + if (m_shouldFallBack) { + jitObject.setFallBack(true); + return; + } + + generate(); + backtrack(); + + // Link & finalize the code. + LinkBuffer linkBuffer(*globalData, this, REGEXP_CODE_ID); + m_backtrackingState.linkDataLabels(linkBuffer); + + if (compileMode == MatchOnly) { + if (m_charSize == Char8) + jitObject.set8BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, ("Match-only 8-bit regular expression"))); + else + jitObject.set16BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, ("Match-only 16-bit regular expression"))); + } else { + if (m_charSize == Char8) + jitObject.set8BitCode(FINALIZE_CODE(linkBuffer, ("8-bit regular expression"))); + else + jitObject.set16BitCode(FINALIZE_CODE(linkBuffer, ("16-bit regular expression"))); + } + jitObject.setFallBack(m_shouldFallBack); + } + +private: + YarrPattern& m_pattern; + + YarrCharSize m_charSize; + + Scale m_charScale; + + // Used to detect regular expression constructs that are not currently + // supported in the JIT; fall back to the interpreter when this is detected. + bool m_shouldFallBack; + + // The regular expression expressed as a linear sequence of operations. + Vector m_ops; + + // This records the current input offset being applied due to the current + // set of alternatives we are nested within. E.g. when matching the + // character 'b' within the regular expression /abc/, we will know that + // the minimum size for the alternative is 3, checked upon entry to the + // alternative, and that 'b' is at offset 1 from the start, and as such + // when matching 'b' we need to apply an offset of -2 to the load. + // + // FIXME: This should go away. Rather than tracking this value throughout + // code generation, we should gather this information up front & store it + // on the YarrOp structure. + int m_checked; + + // This class records state whilst generating the backtracking path of code. + BacktrackingState m_backtrackingState; +}; + +void jitCompile(YarrPattern& pattern, YarrCharSize charSize, JSGlobalData* globalData, YarrCodeBlock& jitObject, YarrJITCompileMode mode) +{ + if (mode == MatchOnly) + YarrGenerator(pattern, charSize).compile(globalData, jitObject); + else + YarrGenerator(pattern, charSize).compile(globalData, jitObject); +} + +}} + +#endif diff --git a/src/3rdparty/masm/yarr/YarrJIT.h b/src/3rdparty/masm/yarr/YarrJIT.h new file mode 100644 index 0000000000..bb7033fdea --- /dev/null +++ b/src/3rdparty/masm/yarr/YarrJIT.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrJIT_h +#define YarrJIT_h + +#if ENABLE(YARR_JIT) + +#include "JSGlobalData.h" +#include "MacroAssemblerCodeRef.h" +#include "MatchResult.h" +#include "Yarr.h" +#include "YarrPattern.h" + +#if CPU(X86) && !COMPILER(MSVC) +#define YARR_CALL __attribute__ ((regparm (3))) +#else +#define YARR_CALL +#endif + +namespace JSC { + +class JSGlobalData; +class ExecutablePool; + +namespace Yarr { + +class YarrCodeBlock { +#if CPU(X86_64) + typedef MatchResult (*YarrJITCode8)(const LChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + typedef MatchResult (*YarrJITCode16)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + typedef MatchResult (*YarrJITCodeMatchOnly8)(const LChar* input, unsigned start, unsigned length) YARR_CALL; + typedef MatchResult (*YarrJITCodeMatchOnly16)(const UChar* input, unsigned start, unsigned length) YARR_CALL; +#else + typedef EncodedMatchResult (*YarrJITCode8)(const LChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + typedef EncodedMatchResult (*YarrJITCode16)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + typedef EncodedMatchResult (*YarrJITCodeMatchOnly8)(const LChar* input, unsigned start, unsigned length) YARR_CALL; + typedef EncodedMatchResult (*YarrJITCodeMatchOnly16)(const UChar* input, unsigned start, unsigned length) YARR_CALL; +#endif + +public: + YarrCodeBlock() + : m_needFallBack(false) + { + } + + ~YarrCodeBlock() + { + } + + void setFallBack(bool fallback) { m_needFallBack = fallback; } + bool isFallBack() { return m_needFallBack; } + + bool has8BitCode() { return m_ref8.size(); } + bool has16BitCode() { return m_ref16.size(); } + void set8BitCode(MacroAssemblerCodeRef ref) { m_ref8 = ref; } + void set16BitCode(MacroAssemblerCodeRef ref) { m_ref16 = ref; } + + bool has8BitCodeMatchOnly() { return m_matchOnly8.size(); } + bool has16BitCodeMatchOnly() { return m_matchOnly16.size(); } + void set8BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly8 = matchOnly; } + void set16BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly16 = matchOnly; } + + MatchResult execute(const LChar* input, unsigned start, unsigned length, int* output) + { + ASSERT(has8BitCode()); + return MatchResult(reinterpret_cast(m_ref8.code().executableAddress())(input, start, length, output)); + } + + MatchResult execute(const UChar* input, unsigned start, unsigned length, int* output) + { + ASSERT(has16BitCode()); + return MatchResult(reinterpret_cast(m_ref16.code().executableAddress())(input, start, length, output)); + } + + MatchResult execute(const LChar* input, unsigned start, unsigned length) + { + ASSERT(has8BitCodeMatchOnly()); + return MatchResult(reinterpret_cast(m_matchOnly8.code().executableAddress())(input, start, length)); + } + + MatchResult execute(const UChar* input, unsigned start, unsigned length) + { + ASSERT(has16BitCodeMatchOnly()); + return MatchResult(reinterpret_cast(m_matchOnly16.code().executableAddress())(input, start, length)); + } + +#if ENABLE(REGEXP_TRACING) + void *getAddr() { return m_ref.code().executableAddress(); } +#endif + + void clear() + { + m_ref8 = MacroAssemblerCodeRef(); + m_ref16 = MacroAssemblerCodeRef(); + m_matchOnly8 = MacroAssemblerCodeRef(); + m_matchOnly16 = MacroAssemblerCodeRef(); + m_needFallBack = false; + } + +private: + MacroAssemblerCodeRef m_ref8; + MacroAssemblerCodeRef m_ref16; + MacroAssemblerCodeRef m_matchOnly8; + MacroAssemblerCodeRef m_matchOnly16; + bool m_needFallBack; +}; + +enum YarrJITCompileMode { + MatchOnly, + IncludeSubpatterns +}; +void jitCompile(YarrPattern&, YarrCharSize, JSGlobalData*, YarrCodeBlock& jitObject, YarrJITCompileMode = IncludeSubpatterns); + +} } // namespace JSC::Yarr + +#endif + +#endif // YarrJIT_h diff --git a/src/3rdparty/masm/yarr/YarrParser.h b/src/3rdparty/masm/yarr/YarrParser.h new file mode 100644 index 0000000000..4bab1a0903 --- /dev/null +++ b/src/3rdparty/masm/yarr/YarrParser.h @@ -0,0 +1,880 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrParser_h +#define YarrParser_h + +#include "Yarr.h" +#include +#include +#include + +namespace JSC { namespace Yarr { + +#define REGEXP_ERROR_PREFIX "Invalid regular expression: " + +enum BuiltInCharacterClassID { + DigitClassID, + SpaceClassID, + WordClassID, + NewlineClassID, +}; + +// The Parser class should not be used directly - only via the Yarr::parse() method. +template +class Parser { +private: + template + friend const char* parse(FriendDelegate&, const String& pattern, unsigned backReferenceLimit); + + enum ErrorCode { + NoError, + PatternTooLarge, + QuantifierOutOfOrder, + QuantifierWithoutAtom, + QuantifierTooLarge, + MissingParentheses, + ParenthesesUnmatched, + ParenthesesTypeInvalid, + CharacterClassUnmatched, + CharacterClassOutOfOrder, + EscapeUnterminated, + NumberOfErrorCodes + }; + + /* + * CharacterClassParserDelegate: + * + * The class CharacterClassParserDelegate is used in the parsing of character + * classes. This class handles detection of character ranges. This class + * implements enough of the delegate interface such that it can be passed to + * parseEscape() as an EscapeDelegate. This allows parseEscape() to be reused + * to perform the parsing of escape characters in character sets. + */ + class CharacterClassParserDelegate { + public: + CharacterClassParserDelegate(Delegate& delegate, ErrorCode& err) + : m_delegate(delegate) + , m_err(err) + , m_state(Empty) + , m_character(0) + { + } + + /* + * begin(): + * + * Called at beginning of construction. + */ + void begin(bool invert) + { + m_delegate.atomCharacterClassBegin(invert); + } + + /* + * atomPatternCharacter(): + * + * This method is called either from parseCharacterClass() (for an unescaped + * character in a character class), or from parseEscape(). In the former case + * the value true will be passed for the argument 'hyphenIsRange', and in this + * mode we will allow a hypen to be treated as indicating a range (i.e. /[a-z]/ + * is different to /[a\-z]/). + */ + void atomPatternCharacter(UChar ch, bool hyphenIsRange = false) + { + switch (m_state) { + case AfterCharacterClass: + // Following a builtin character class we need look out for a hyphen. + // We're looking for invalid ranges, such as /[\d-x]/ or /[\d-\d]/. + // If we see a hyphen following a charater class then unlike usual + // we'll report it to the delegate immediately, and put ourself into + // a poisoned state. Any following calls to add another character or + // character class will result in an error. (A hypen following a + // character-class is itself valid, but only at the end of a regex). + if (hyphenIsRange && ch == '-') { + m_delegate.atomCharacterClassAtom('-'); + m_state = AfterCharacterClassHyphen; + return; + } + // Otherwise just fall through - cached character so treat this as Empty. + + case Empty: + m_character = ch; + m_state = CachedCharacter; + return; + + case CachedCharacter: + if (hyphenIsRange && ch == '-') + m_state = CachedCharacterHyphen; + else { + m_delegate.atomCharacterClassAtom(m_character); + m_character = ch; + } + return; + + case CachedCharacterHyphen: + if (ch < m_character) { + m_err = CharacterClassOutOfOrder; + return; + } + m_delegate.atomCharacterClassRange(m_character, ch); + m_state = Empty; + return; + + // See coment in atomBuiltInCharacterClass below. + // This too is technically an error, per ECMA-262, and again we + // we chose to allow this. Note a subtlely here that while we + // diverge from the spec's definition of CharacterRange we do + // remain in compliance with the grammar. For example, consider + // the expression /[\d-a-z]/. We comply with the grammar in + // this case by not allowing a-z to be matched as a range. + case AfterCharacterClassHyphen: + m_delegate.atomCharacterClassAtom(ch); + m_state = Empty; + return; + } + } + + /* + * atomBuiltInCharacterClass(): + * + * Adds a built-in character class, called by parseEscape(). + */ + void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert) + { + switch (m_state) { + case CachedCharacter: + // Flush the currently cached character, then fall through. + m_delegate.atomCharacterClassAtom(m_character); + + case Empty: + case AfterCharacterClass: + m_state = AfterCharacterClass; + m_delegate.atomCharacterClassBuiltIn(classID, invert); + return; + + // If we hit either of these cases, we have an invalid range that + // looks something like /[x-\d]/ or /[\d-\d]/. + // According to ECMA-262 this should be a syntax error, but + // empirical testing shows this to break teh webz. Instead we + // comply with to the ECMA-262 grammar, and assume the grammar to + // have matched the range correctly, but tweak our interpretation + // of CharacterRange. Effectively we implicitly handle the hyphen + // as if it were escaped, e.g. /[\w-_]/ is treated as /[\w\-_]/. + case CachedCharacterHyphen: + m_delegate.atomCharacterClassAtom(m_character); + m_delegate.atomCharacterClassAtom('-'); + // fall through + case AfterCharacterClassHyphen: + m_delegate.atomCharacterClassBuiltIn(classID, invert); + m_state = Empty; + return; + } + } + + /* + * end(): + * + * Called at end of construction. + */ + void end() + { + if (m_state == CachedCharacter) + m_delegate.atomCharacterClassAtom(m_character); + else if (m_state == CachedCharacterHyphen) { + m_delegate.atomCharacterClassAtom(m_character); + m_delegate.atomCharacterClassAtom('-'); + } + m_delegate.atomCharacterClassEnd(); + } + + // parseEscape() should never call these delegate methods when + // invoked with inCharacterClass set. + NO_RETURN_DUE_TO_ASSERT void assertionWordBoundary(bool) { ASSERT_NOT_REACHED(); } + NO_RETURN_DUE_TO_ASSERT void atomBackReference(unsigned) { ASSERT_NOT_REACHED(); } + + private: + Delegate& m_delegate; + ErrorCode& m_err; + enum CharacterClassConstructionState { + Empty, + CachedCharacter, + CachedCharacterHyphen, + AfterCharacterClass, + AfterCharacterClassHyphen, + } m_state; + UChar m_character; + }; + + Parser(Delegate& delegate, const String& pattern, unsigned backReferenceLimit) + : m_delegate(delegate) + , m_backReferenceLimit(backReferenceLimit) + , m_err(NoError) + , m_data(pattern.getCharacters()) + , m_size(pattern.length()) + , m_index(0) + , m_parenthesesNestingDepth(0) + { + } + + /* + * parseEscape(): + * + * Helper for parseTokens() AND parseCharacterClass(). + * Unlike the other parser methods, this function does not report tokens + * directly to the member delegate (m_delegate), instead tokens are + * emitted to the delegate provided as an argument. In the case of atom + * escapes, parseTokens() will call parseEscape() passing m_delegate as + * an argument, and as such the escape will be reported to the delegate. + * + * However this method may also be used by parseCharacterClass(), in which + * case a CharacterClassParserDelegate will be passed as the delegate that + * tokens should be added to. A boolean flag is also provided to indicate + * whether that an escape in a CharacterClass is being parsed (some parsing + * rules change in this context). + * + * The boolean value returned by this method indicates whether the token + * parsed was an atom (outside of a characted class \b and \B will be + * interpreted as assertions). + */ + template + bool parseEscape(EscapeDelegate& delegate) + { + ASSERT(!m_err); + ASSERT(peek() == '\\'); + consume(); + + if (atEndOfPattern()) { + m_err = EscapeUnterminated; + return false; + } + + switch (peek()) { + // Assertions + case 'b': + consume(); + if (inCharacterClass) + delegate.atomPatternCharacter('\b'); + else { + delegate.assertionWordBoundary(false); + return false; + } + break; + case 'B': + consume(); + if (inCharacterClass) + delegate.atomPatternCharacter('B'); + else { + delegate.assertionWordBoundary(true); + return false; + } + break; + + // CharacterClassEscape + case 'd': + consume(); + delegate.atomBuiltInCharacterClass(DigitClassID, false); + break; + case 's': + consume(); + delegate.atomBuiltInCharacterClass(SpaceClassID, false); + break; + case 'w': + consume(); + delegate.atomBuiltInCharacterClass(WordClassID, false); + break; + case 'D': + consume(); + delegate.atomBuiltInCharacterClass(DigitClassID, true); + break; + case 'S': + consume(); + delegate.atomBuiltInCharacterClass(SpaceClassID, true); + break; + case 'W': + consume(); + delegate.atomBuiltInCharacterClass(WordClassID, true); + break; + + // DecimalEscape + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + // To match Firefox, we parse an invalid backreference in the range [1-7] as an octal escape. + // First, try to parse this as backreference. + if (!inCharacterClass) { + ParseState state = saveState(); + + unsigned backReference = consumeNumber(); + if (backReference <= m_backReferenceLimit) { + delegate.atomBackReference(backReference); + break; + } + + restoreState(state); + } + + // Not a backreference, and not octal. + if (peek() >= '8') { + delegate.atomPatternCharacter('\\'); + break; + } + + // Fall-through to handle this as an octal escape. + } + + // Octal escape + case '0': + delegate.atomPatternCharacter(consumeOctal()); + break; + + // ControlEscape + case 'f': + consume(); + delegate.atomPatternCharacter('\f'); + break; + case 'n': + consume(); + delegate.atomPatternCharacter('\n'); + break; + case 'r': + consume(); + delegate.atomPatternCharacter('\r'); + break; + case 't': + consume(); + delegate.atomPatternCharacter('\t'); + break; + case 'v': + consume(); + delegate.atomPatternCharacter('\v'); + break; + + // ControlLetter + case 'c': { + ParseState state = saveState(); + consume(); + if (!atEndOfPattern()) { + int control = consume(); + + // To match Firefox, inside a character class, we also accept numbers and '_' as control characters. + if (inCharacterClass ? WTF::isASCIIAlphanumeric(control) || (control == '_') : WTF::isASCIIAlpha(control)) { + delegate.atomPatternCharacter(control & 0x1f); + break; + } + } + restoreState(state); + delegate.atomPatternCharacter('\\'); + break; + } + + // HexEscape + case 'x': { + consume(); + int x = tryConsumeHex(2); + if (x == -1) + delegate.atomPatternCharacter('x'); + else + delegate.atomPatternCharacter(x); + break; + } + + // UnicodeEscape + case 'u': { + consume(); + int u = tryConsumeHex(4); + if (u == -1) + delegate.atomPatternCharacter('u'); + else + delegate.atomPatternCharacter(u); + break; + } + + // IdentityEscape + default: + delegate.atomPatternCharacter(consume()); + } + + return true; + } + + /* + * parseAtomEscape(), parseCharacterClassEscape(): + * + * These methods alias to parseEscape(). + */ + bool parseAtomEscape() + { + return parseEscape(m_delegate); + } + void parseCharacterClassEscape(CharacterClassParserDelegate& delegate) + { + parseEscape(delegate); + } + + /* + * parseCharacterClass(): + * + * Helper for parseTokens(); calls dirctly and indirectly (via parseCharacterClassEscape) + * to an instance of CharacterClassParserDelegate, to describe the character class to the + * delegate. + */ + void parseCharacterClass() + { + ASSERT(!m_err); + ASSERT(peek() == '['); + consume(); + + CharacterClassParserDelegate characterClassConstructor(m_delegate, m_err); + + characterClassConstructor.begin(tryConsume('^')); + + while (!atEndOfPattern()) { + switch (peek()) { + case ']': + consume(); + characterClassConstructor.end(); + return; + + case '\\': + parseCharacterClassEscape(characterClassConstructor); + break; + + default: + characterClassConstructor.atomPatternCharacter(consume(), true); + } + + if (m_err) + return; + } + + m_err = CharacterClassUnmatched; + } + + /* + * parseParenthesesBegin(): + * + * Helper for parseTokens(); checks for parentheses types other than regular capturing subpatterns. + */ + void parseParenthesesBegin() + { + ASSERT(!m_err); + ASSERT(peek() == '('); + consume(); + + if (tryConsume('?')) { + if (atEndOfPattern()) { + m_err = ParenthesesTypeInvalid; + return; + } + + switch (consume()) { + case ':': + m_delegate.atomParenthesesSubpatternBegin(false); + break; + + case '=': + m_delegate.atomParentheticalAssertionBegin(); + break; + + case '!': + m_delegate.atomParentheticalAssertionBegin(true); + break; + + default: + m_err = ParenthesesTypeInvalid; + } + } else + m_delegate.atomParenthesesSubpatternBegin(); + + ++m_parenthesesNestingDepth; + } + + /* + * parseParenthesesEnd(): + * + * Helper for parseTokens(); checks for parse errors (due to unmatched parentheses). + */ + void parseParenthesesEnd() + { + ASSERT(!m_err); + ASSERT(peek() == ')'); + consume(); + + if (m_parenthesesNestingDepth > 0) + m_delegate.atomParenthesesEnd(); + else + m_err = ParenthesesUnmatched; + + --m_parenthesesNestingDepth; + } + + /* + * parseQuantifier(): + * + * Helper for parseTokens(); checks for parse errors and non-greedy quantifiers. + */ + void parseQuantifier(bool lastTokenWasAnAtom, unsigned min, unsigned max) + { + ASSERT(!m_err); + ASSERT(min <= max); + + if (min == UINT_MAX) { + m_err = QuantifierTooLarge; + return; + } + + if (lastTokenWasAnAtom) + m_delegate.quantifyAtom(min, max, !tryConsume('?')); + else + m_err = QuantifierWithoutAtom; + } + + /* + * parseTokens(): + * + * This method loops over the input pattern reporting tokens to the delegate. + * The method returns when a parse error is detected, or the end of the pattern + * is reached. One piece of state is tracked around the loop, which is whether + * the last token passed to the delegate was an atom (this is necessary to detect + * a parse error when a quantifier provided without an atom to quantify). + */ + void parseTokens() + { + bool lastTokenWasAnAtom = false; + + while (!atEndOfPattern()) { + switch (peek()) { + case '|': + consume(); + m_delegate.disjunction(); + lastTokenWasAnAtom = false; + break; + + case '(': + parseParenthesesBegin(); + lastTokenWasAnAtom = false; + break; + + case ')': + parseParenthesesEnd(); + lastTokenWasAnAtom = true; + break; + + case '^': + consume(); + m_delegate.assertionBOL(); + lastTokenWasAnAtom = false; + break; + + case '$': + consume(); + m_delegate.assertionEOL(); + lastTokenWasAnAtom = false; + break; + + case '.': + consume(); + m_delegate.atomBuiltInCharacterClass(NewlineClassID, true); + lastTokenWasAnAtom = true; + break; + + case '[': + parseCharacterClass(); + lastTokenWasAnAtom = true; + break; + + case '\\': + lastTokenWasAnAtom = parseAtomEscape(); + break; + + case '*': + consume(); + parseQuantifier(lastTokenWasAnAtom, 0, quantifyInfinite); + lastTokenWasAnAtom = false; + break; + + case '+': + consume(); + parseQuantifier(lastTokenWasAnAtom, 1, quantifyInfinite); + lastTokenWasAnAtom = false; + break; + + case '?': + consume(); + parseQuantifier(lastTokenWasAnAtom, 0, 1); + lastTokenWasAnAtom = false; + break; + + case '{': { + ParseState state = saveState(); + + consume(); + if (peekIsDigit()) { + unsigned min = consumeNumber(); + unsigned max = min; + + if (tryConsume(',')) + max = peekIsDigit() ? consumeNumber() : quantifyInfinite; + + if (tryConsume('}')) { + if (min <= max) + parseQuantifier(lastTokenWasAnAtom, min, max); + else + m_err = QuantifierOutOfOrder; + lastTokenWasAnAtom = false; + break; + } + } + + restoreState(state); + } // if we did not find a complete quantifer, fall through to the default case. + + default: + m_delegate.atomPatternCharacter(consume()); + lastTokenWasAnAtom = true; + } + + if (m_err) + return; + } + + if (m_parenthesesNestingDepth > 0) + m_err = MissingParentheses; + } + + /* + * parse(): + * + * This method calls parseTokens() to parse over the input and converts any + * error code to a const char* for a result. + */ + const char* parse() + { + if (m_size > MAX_PATTERN_SIZE) + m_err = PatternTooLarge; + else + parseTokens(); + ASSERT(atEndOfPattern() || m_err); + + // The order of this array must match the ErrorCode enum. + static const char* errorMessages[NumberOfErrorCodes] = { + 0, // NoError + REGEXP_ERROR_PREFIX "regular expression too large", + REGEXP_ERROR_PREFIX "numbers out of order in {} quantifier", + REGEXP_ERROR_PREFIX "nothing to repeat", + REGEXP_ERROR_PREFIX "number too large in {} quantifier", + REGEXP_ERROR_PREFIX "missing )", + REGEXP_ERROR_PREFIX "unmatched parentheses", + REGEXP_ERROR_PREFIX "unrecognized character after (?", + REGEXP_ERROR_PREFIX "missing terminating ] for character class", + REGEXP_ERROR_PREFIX "range out of order in character class", + REGEXP_ERROR_PREFIX "\\ at end of pattern" + }; + + return errorMessages[m_err]; + } + + // Misc helper functions: + + typedef unsigned ParseState; + + ParseState saveState() + { + return m_index; + } + + void restoreState(ParseState state) + { + m_index = state; + } + + bool atEndOfPattern() + { + ASSERT(m_index <= m_size); + return m_index == m_size; + } + + int peek() + { + ASSERT(m_index < m_size); + return m_data[m_index]; + } + + bool peekIsDigit() + { + return !atEndOfPattern() && WTF::isASCIIDigit(peek()); + } + + unsigned peekDigit() + { + ASSERT(peekIsDigit()); + return peek() - '0'; + } + + int consume() + { + ASSERT(m_index < m_size); + return m_data[m_index++]; + } + + unsigned consumeDigit() + { + ASSERT(peekIsDigit()); + return consume() - '0'; + } + + unsigned consumeNumber() + { + unsigned n = consumeDigit(); + // check for overflow. + for (unsigned newValue; peekIsDigit() && ((newValue = n * 10 + peekDigit()) >= n); ) { + n = newValue; + consume(); + } + return n; + } + + unsigned consumeOctal() + { + ASSERT(WTF::isASCIIOctalDigit(peek())); + + unsigned n = consumeDigit(); + while (n < 32 && !atEndOfPattern() && WTF::isASCIIOctalDigit(peek())) + n = n * 8 + consumeDigit(); + return n; + } + + bool tryConsume(UChar ch) + { + if (atEndOfPattern() || (m_data[m_index] != ch)) + return false; + ++m_index; + return true; + } + + int tryConsumeHex(int count) + { + ParseState state = saveState(); + + int n = 0; + while (count--) { + if (atEndOfPattern() || !WTF::isASCIIHexDigit(peek())) { + restoreState(state); + return -1; + } + n = (n << 4) | WTF::toASCIIHexValue(consume()); + } + return n; + } + + Delegate& m_delegate; + unsigned m_backReferenceLimit; + ErrorCode m_err; + const CharType* m_data; + unsigned m_size; + unsigned m_index; + unsigned m_parenthesesNestingDepth; + + // Derived by empirical testing of compile time in PCRE and WREC. + static const unsigned MAX_PATTERN_SIZE = 1024 * 1024; +}; + +/* + * Yarr::parse(): + * + * The parse method is passed a pattern to be parsed and a delegate upon which + * callbacks will be made to record the parsed tokens forming the regex. + * Yarr::parse() returns null on success, or a const C string providing an error + * message where a parse error occurs. + * + * The Delegate must implement the following interface: + * + * void assertionBOL(); + * void assertionEOL(); + * void assertionWordBoundary(bool invert); + * + * void atomPatternCharacter(UChar ch); + * void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert); + * void atomCharacterClassBegin(bool invert) + * void atomCharacterClassAtom(UChar ch) + * void atomCharacterClassRange(UChar begin, UChar end) + * void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert) + * void atomCharacterClassEnd() + * void atomParenthesesSubpatternBegin(bool capture = true); + * void atomParentheticalAssertionBegin(bool invert = false); + * void atomParenthesesEnd(); + * void atomBackReference(unsigned subpatternId); + * + * void quantifyAtom(unsigned min, unsigned max, bool greedy); + * + * void disjunction(); + * + * The regular expression is described by a sequence of assertion*() and atom*() + * callbacks to the delegate, describing the terms in the regular expression. + * Following an atom a quantifyAtom() call may occur to indicate that the previous + * atom should be quantified. In the case of atoms described across multiple + * calls (parentheses and character classes) the call to quantifyAtom() will come + * after the call to the atom*End() method, never after atom*Begin(). + * + * Character classes may either be described by a single call to + * atomBuiltInCharacterClass(), or by a sequence of atomCharacterClass*() calls. + * In the latter case, ...Begin() will be called, followed by a sequence of + * calls to ...Atom(), ...Range(), and ...BuiltIn(), followed by a call to ...End(). + * + * Sequences of atoms and assertions are broken into alternatives via calls to + * disjunction(). Assertions, atoms, and disjunctions emitted between calls to + * atomParenthesesBegin() and atomParenthesesEnd() form the body of a subpattern. + * atomParenthesesBegin() is passed a subpatternId. In the case of a regular + * capturing subpattern, this will be the subpatternId associated with these + * parentheses, and will also by definition be the lowest subpatternId of these + * parentheses and of any nested paretheses. The atomParenthesesEnd() method + * is passed the subpatternId of the last capturing subexpression nested within + * these paretheses. In the case of a capturing subpattern with no nested + * capturing subpatterns, the same subpatternId will be passed to the begin and + * end functions. In the case of non-capturing subpatterns the subpatternId + * passed to the begin method is also the first possible subpatternId that might + * be nested within these paretheses. If a set of non-capturing parentheses does + * not contain any capturing subpatterns, then the subpatternId passed to begin + * will be greater than the subpatternId passed to end. + */ + +template +const char* parse(Delegate& delegate, const String& pattern, unsigned backReferenceLimit = quantifyInfinite) +{ + if (pattern.is8Bit()) + return Parser(delegate, pattern, backReferenceLimit).parse(); + return Parser(delegate, pattern, backReferenceLimit).parse(); +} + +} } // namespace JSC::Yarr + +#endif // YarrParser_h diff --git a/src/3rdparty/masm/yarr/YarrPattern.cpp b/src/3rdparty/masm/yarr/YarrPattern.cpp new file mode 100644 index 0000000000..c953a38d2f --- /dev/null +++ b/src/3rdparty/masm/yarr/YarrPattern.cpp @@ -0,0 +1,874 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "YarrPattern.h" + +#include "Yarr.h" +#include "YarrCanonicalizeUCS2.h" +#include "YarrParser.h" +#include + +using namespace WTF; + +namespace JSC { namespace Yarr { + +#include "RegExpJitTables.h" + +class CharacterClassConstructor { +public: + CharacterClassConstructor(bool isCaseInsensitive = false) + : m_isCaseInsensitive(isCaseInsensitive) + { + } + + void reset() + { + m_matches.clear(); + m_ranges.clear(); + m_matchesUnicode.clear(); + m_rangesUnicode.clear(); + } + + void append(const CharacterClass* other) + { + for (size_t i = 0; i < other->m_matches.size(); ++i) + addSorted(m_matches, other->m_matches[i]); + for (size_t i = 0; i < other->m_ranges.size(); ++i) + addSortedRange(m_ranges, other->m_ranges[i].begin, other->m_ranges[i].end); + for (size_t i = 0; i < other->m_matchesUnicode.size(); ++i) + addSorted(m_matchesUnicode, other->m_matchesUnicode[i]); + for (size_t i = 0; i < other->m_rangesUnicode.size(); ++i) + addSortedRange(m_rangesUnicode, other->m_rangesUnicode[i].begin, other->m_rangesUnicode[i].end); + } + + void putChar(UChar ch) + { + // Handle ascii cases. + if (ch <= 0x7f) { + if (m_isCaseInsensitive && isASCIIAlpha(ch)) { + addSorted(m_matches, toASCIIUpper(ch)); + addSorted(m_matches, toASCIILower(ch)); + } else + addSorted(m_matches, ch); + return; + } + + // Simple case, not a case-insensitive match. + if (!m_isCaseInsensitive) { + addSorted(m_matchesUnicode, ch); + return; + } + + // Add multiple matches, if necessary. + UCS2CanonicalizationRange* info = rangeInfoFor(ch); + if (info->type == CanonicalizeUnique) + addSorted(m_matchesUnicode, ch); + else + putUnicodeIgnoreCase(ch, info); + } + + void putUnicodeIgnoreCase(UChar ch, UCS2CanonicalizationRange* info) + { + ASSERT(m_isCaseInsensitive); + ASSERT(ch > 0x7f); + ASSERT(ch >= info->begin && ch <= info->end); + ASSERT(info->type != CanonicalizeUnique); + if (info->type == CanonicalizeSet) { + for (uint16_t* set = characterSetInfo[info->value]; (ch = *set); ++set) + addSorted(m_matchesUnicode, ch); + } else { + addSorted(m_matchesUnicode, ch); + addSorted(m_matchesUnicode, getCanonicalPair(info, ch)); + } + } + + void putRange(UChar lo, UChar hi) + { + if (lo <= 0x7f) { + char asciiLo = lo; + char asciiHi = std::min(hi, (UChar)0x7f); + addSortedRange(m_ranges, lo, asciiHi); + + if (m_isCaseInsensitive) { + if ((asciiLo <= 'Z') && (asciiHi >= 'A')) + addSortedRange(m_ranges, std::max(asciiLo, 'A')+('a'-'A'), std::min(asciiHi, 'Z')+('a'-'A')); + if ((asciiLo <= 'z') && (asciiHi >= 'a')) + addSortedRange(m_ranges, std::max(asciiLo, 'a')+('A'-'a'), std::min(asciiHi, 'z')+('A'-'a')); + } + } + if (hi <= 0x7f) + return; + + lo = std::max(lo, (UChar)0x80); + addSortedRange(m_rangesUnicode, lo, hi); + + if (!m_isCaseInsensitive) + return; + + UCS2CanonicalizationRange* info = rangeInfoFor(lo); + while (true) { + // Handle the range [lo .. end] + UChar end = std::min(info->end, hi); + + switch (info->type) { + case CanonicalizeUnique: + // Nothing to do - no canonical equivalents. + break; + case CanonicalizeSet: { + UChar ch; + for (uint16_t* set = characterSetInfo[info->value]; (ch = *set); ++set) + addSorted(m_matchesUnicode, ch); + break; + } + case CanonicalizeRangeLo: + addSortedRange(m_rangesUnicode, lo + info->value, end + info->value); + break; + case CanonicalizeRangeHi: + addSortedRange(m_rangesUnicode, lo - info->value, end - info->value); + break; + case CanonicalizeAlternatingAligned: + // Use addSortedRange since there is likely an abutting range to combine with. + if (lo & 1) + addSortedRange(m_rangesUnicode, lo - 1, lo - 1); + if (!(end & 1)) + addSortedRange(m_rangesUnicode, end + 1, end + 1); + break; + case CanonicalizeAlternatingUnaligned: + // Use addSortedRange since there is likely an abutting range to combine with. + if (!(lo & 1)) + addSortedRange(m_rangesUnicode, lo - 1, lo - 1); + if (end & 1) + addSortedRange(m_rangesUnicode, end + 1, end + 1); + break; + } + + if (hi == end) + return; + + ++info; + lo = info->begin; + }; + + } + + CharacterClass* charClass() + { + CharacterClass* characterClass = new CharacterClass(0); + + characterClass->m_matches.swap(m_matches); + characterClass->m_ranges.swap(m_ranges); + characterClass->m_matchesUnicode.swap(m_matchesUnicode); + characterClass->m_rangesUnicode.swap(m_rangesUnicode); + + return characterClass; + } + +private: + void addSorted(Vector& matches, UChar ch) + { + unsigned pos = 0; + unsigned range = matches.size(); + + // binary chop, find position to insert char. + while (range) { + unsigned index = range >> 1; + + int val = matches[pos+index] - ch; + if (!val) + return; + else if (val > 0) + range = index; + else { + pos += (index+1); + range -= (index+1); + } + } + + if (pos == matches.size()) + matches.append(ch); + else + matches.insert(pos, ch); + } + + void addSortedRange(Vector& ranges, UChar lo, UChar hi) + { + unsigned end = ranges.size(); + + // Simple linear scan - I doubt there are that many ranges anyway... + // feel free to fix this with something faster (eg binary chop). + for (unsigned i = 0; i < end; ++i) { + // does the new range fall before the current position in the array + if (hi < ranges[i].begin) { + // optional optimization: concatenate appending ranges? - may not be worthwhile. + if (hi == (ranges[i].begin - 1)) { + ranges[i].begin = lo; + return; + } + ranges.insert(i, CharacterRange(lo, hi)); + return; + } + // Okay, since we didn't hit the last case, the end of the new range is definitely at or after the begining + // If the new range start at or before the end of the last range, then the overlap (if it starts one after the + // end of the last range they concatenate, which is just as good. + if (lo <= (ranges[i].end + 1)) { + // found an intersect! we'll replace this entry in the array. + ranges[i].begin = std::min(ranges[i].begin, lo); + ranges[i].end = std::max(ranges[i].end, hi); + + // now check if the new range can subsume any subsequent ranges. + unsigned next = i+1; + // each iteration of the loop we will either remove something from the list, or break the loop. + while (next < ranges.size()) { + if (ranges[next].begin <= (ranges[i].end + 1)) { + // the next entry now overlaps / concatenates this one. + ranges[i].end = std::max(ranges[i].end, ranges[next].end); + ranges.remove(next); + } else + break; + } + + return; + } + } + + // CharacterRange comes after all existing ranges. + ranges.append(CharacterRange(lo, hi)); + } + + bool m_isCaseInsensitive; + + Vector m_matches; + Vector m_ranges; + Vector m_matchesUnicode; + Vector m_rangesUnicode; +}; + +class YarrPatternConstructor { +public: + YarrPatternConstructor(YarrPattern& pattern) + : m_pattern(pattern) + , m_characterClassConstructor(pattern.m_ignoreCase) + , m_invertParentheticalAssertion(false) + { + m_pattern.m_body = new PatternDisjunction(); + m_alternative = m_pattern.m_body->addNewAlternative(); + m_pattern.m_disjunctions.append(m_pattern.m_body); + } + + ~YarrPatternConstructor() + { + } + + void reset() + { + m_pattern.reset(); + m_characterClassConstructor.reset(); + + m_pattern.m_body = new PatternDisjunction(); + m_alternative = m_pattern.m_body->addNewAlternative(); + m_pattern.m_disjunctions.append(m_pattern.m_body); + } + + void assertionBOL() + { + if (!m_alternative->m_terms.size() & !m_invertParentheticalAssertion) { + m_alternative->m_startsWithBOL = true; + m_alternative->m_containsBOL = true; + m_pattern.m_containsBOL = true; + } + m_alternative->m_terms.append(PatternTerm::BOL()); + } + void assertionEOL() + { + m_alternative->m_terms.append(PatternTerm::EOL()); + } + void assertionWordBoundary(bool invert) + { + m_alternative->m_terms.append(PatternTerm::WordBoundary(invert)); + } + + void atomPatternCharacter(UChar ch) + { + // We handle case-insensitive checking of unicode characters which do have both + // cases by handling them as if they were defined using a CharacterClass. + if (!m_pattern.m_ignoreCase || isASCII(ch)) { + m_alternative->m_terms.append(PatternTerm(ch)); + return; + } + + UCS2CanonicalizationRange* info = rangeInfoFor(ch); + if (info->type == CanonicalizeUnique) { + m_alternative->m_terms.append(PatternTerm(ch)); + return; + } + + m_characterClassConstructor.putUnicodeIgnoreCase(ch, info); + CharacterClass* newCharacterClass = m_characterClassConstructor.charClass(); + m_pattern.m_userCharacterClasses.append(newCharacterClass); + m_alternative->m_terms.append(PatternTerm(newCharacterClass, false)); + } + + void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert) + { + switch (classID) { + case DigitClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.digitsCharacterClass(), invert)); + break; + case SpaceClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.spacesCharacterClass(), invert)); + break; + case WordClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.wordcharCharacterClass(), invert)); + break; + case NewlineClassID: + m_alternative->m_terms.append(PatternTerm(m_pattern.newlineCharacterClass(), invert)); + break; + } + } + + void atomCharacterClassBegin(bool invert = false) + { + m_invertCharacterClass = invert; + } + + void atomCharacterClassAtom(UChar ch) + { + m_characterClassConstructor.putChar(ch); + } + + void atomCharacterClassRange(UChar begin, UChar end) + { + m_characterClassConstructor.putRange(begin, end); + } + + void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert) + { + ASSERT(classID != NewlineClassID); + + switch (classID) { + case DigitClassID: + m_characterClassConstructor.append(invert ? m_pattern.nondigitsCharacterClass() : m_pattern.digitsCharacterClass()); + break; + + case SpaceClassID: + m_characterClassConstructor.append(invert ? m_pattern.nonspacesCharacterClass() : m_pattern.spacesCharacterClass()); + break; + + case WordClassID: + m_characterClassConstructor.append(invert ? m_pattern.nonwordcharCharacterClass() : m_pattern.wordcharCharacterClass()); + break; + + default: + ASSERT_NOT_REACHED(); + } + } + + void atomCharacterClassEnd() + { + CharacterClass* newCharacterClass = m_characterClassConstructor.charClass(); + m_pattern.m_userCharacterClasses.append(newCharacterClass); + m_alternative->m_terms.append(PatternTerm(newCharacterClass, m_invertCharacterClass)); + } + + void atomParenthesesSubpatternBegin(bool capture = true) + { + unsigned subpatternId = m_pattern.m_numSubpatterns + 1; + if (capture) + m_pattern.m_numSubpatterns++; + + PatternDisjunction* parenthesesDisjunction = new PatternDisjunction(m_alternative); + m_pattern.m_disjunctions.append(parenthesesDisjunction); + m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture, false)); + m_alternative = parenthesesDisjunction->addNewAlternative(); + } + + void atomParentheticalAssertionBegin(bool invert = false) + { + PatternDisjunction* parenthesesDisjunction = new PatternDisjunction(m_alternative); + m_pattern.m_disjunctions.append(parenthesesDisjunction); + m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParentheticalAssertion, m_pattern.m_numSubpatterns + 1, parenthesesDisjunction, false, invert)); + m_alternative = parenthesesDisjunction->addNewAlternative(); + m_invertParentheticalAssertion = invert; + } + + void atomParenthesesEnd() + { + ASSERT(m_alternative->m_parent); + ASSERT(m_alternative->m_parent->m_parent); + + PatternDisjunction* parenthesesDisjunction = m_alternative->m_parent; + m_alternative = m_alternative->m_parent->m_parent; + + PatternTerm& lastTerm = m_alternative->lastTerm(); + + unsigned numParenAlternatives = parenthesesDisjunction->m_alternatives.size(); + unsigned numBOLAnchoredAlts = 0; + + for (unsigned i = 0; i < numParenAlternatives; i++) { + // Bubble up BOL flags + if (parenthesesDisjunction->m_alternatives[i]->m_startsWithBOL) + numBOLAnchoredAlts++; + } + + if (numBOLAnchoredAlts) { + m_alternative->m_containsBOL = true; + // If all the alternatives in parens start with BOL, then so does this one + if (numBOLAnchoredAlts == numParenAlternatives) + m_alternative->m_startsWithBOL = true; + } + + lastTerm.parentheses.lastSubpatternId = m_pattern.m_numSubpatterns; + m_invertParentheticalAssertion = false; + } + + void atomBackReference(unsigned subpatternId) + { + ASSERT(subpatternId); + m_pattern.m_containsBackreferences = true; + m_pattern.m_maxBackReference = std::max(m_pattern.m_maxBackReference, subpatternId); + + if (subpatternId > m_pattern.m_numSubpatterns) { + m_alternative->m_terms.append(PatternTerm::ForwardReference()); + return; + } + + PatternAlternative* currentAlternative = m_alternative; + ASSERT(currentAlternative); + + // Note to self: if we waited until the AST was baked, we could also remove forwards refs + while ((currentAlternative = currentAlternative->m_parent->m_parent)) { + PatternTerm& term = currentAlternative->lastTerm(); + ASSERT((term.type == PatternTerm::TypeParenthesesSubpattern) || (term.type == PatternTerm::TypeParentheticalAssertion)); + + if ((term.type == PatternTerm::TypeParenthesesSubpattern) && term.capture() && (subpatternId == term.parentheses.subpatternId)) { + m_alternative->m_terms.append(PatternTerm::ForwardReference()); + return; + } + } + + m_alternative->m_terms.append(PatternTerm(subpatternId)); + } + + // deep copy the argument disjunction. If filterStartsWithBOL is true, + // skip alternatives with m_startsWithBOL set true. + PatternDisjunction* copyDisjunction(PatternDisjunction* disjunction, bool filterStartsWithBOL = false) + { + PatternDisjunction* newDisjunction = 0; + for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { + PatternAlternative* alternative = disjunction->m_alternatives[alt]; + if (!filterStartsWithBOL || !alternative->m_startsWithBOL) { + if (!newDisjunction) { + newDisjunction = new PatternDisjunction(); + newDisjunction->m_parent = disjunction->m_parent; + } + PatternAlternative* newAlternative = newDisjunction->addNewAlternative(); + for (unsigned i = 0; i < alternative->m_terms.size(); ++i) + newAlternative->m_terms.append(copyTerm(alternative->m_terms[i], filterStartsWithBOL)); + } + } + + if (newDisjunction) + m_pattern.m_disjunctions.append(newDisjunction); + return newDisjunction; + } + + PatternTerm copyTerm(PatternTerm& term, bool filterStartsWithBOL = false) + { + if ((term.type != PatternTerm::TypeParenthesesSubpattern) && (term.type != PatternTerm::TypeParentheticalAssertion)) + return PatternTerm(term); + + PatternTerm termCopy = term; + termCopy.parentheses.disjunction = copyDisjunction(termCopy.parentheses.disjunction, filterStartsWithBOL); + return termCopy; + } + + void quantifyAtom(unsigned min, unsigned max, bool greedy) + { + ASSERT(min <= max); + ASSERT(m_alternative->m_terms.size()); + + if (!max) { + m_alternative->removeLastTerm(); + return; + } + + PatternTerm& term = m_alternative->lastTerm(); + ASSERT(term.type > PatternTerm::TypeAssertionWordBoundary); + ASSERT((term.quantityCount == 1) && (term.quantityType == QuantifierFixedCount)); + + if (term.type == PatternTerm::TypeParentheticalAssertion) { + // If an assertion is quantified with a minimum count of zero, it can simply be removed. + // This arises from the RepeatMatcher behaviour in the spec. Matching an assertion never + // results in any input being consumed, however the continuation passed to the assertion + // (called in steps, 8c and 9 of the RepeatMatcher definition, ES5.1 15.10.2.5) will + // reject all zero length matches (see step 2.1). A match from the continuation of the + // expression will still be accepted regardless (via steps 8a and 11) - the upshot of all + // this is that matches from the assertion are not required, and won't be accepted anyway, + // so no need to ever run it. + if (!min) + m_alternative->removeLastTerm(); + // We never need to run an assertion more than once. Subsequent interations will be run + // with the same start index (since assertions are non-capturing) and the same captures + // (per step 4 of RepeatMatcher in ES5.1 15.10.2.5), and as such will always produce the + // same result and captures. If the first match succeeds then the subsequent (min - 1) + // matches will too. Any additional optional matches will fail (on the same basis as the + // minimum zero quantified assertions, above), but this will still result in a match. + return; + } + + if (min == 0) + term.quantify(max, greedy ? QuantifierGreedy : QuantifierNonGreedy); + else if (min == max) + term.quantify(min, QuantifierFixedCount); + else { + term.quantify(min, QuantifierFixedCount); + m_alternative->m_terms.append(copyTerm(term)); + // NOTE: this term is interesting from an analysis perspective, in that it can be ignored..... + m_alternative->lastTerm().quantify((max == quantifyInfinite) ? max : max - min, greedy ? QuantifierGreedy : QuantifierNonGreedy); + if (m_alternative->lastTerm().type == PatternTerm::TypeParenthesesSubpattern) + m_alternative->lastTerm().parentheses.isCopy = true; + } + } + + void disjunction() + { + m_alternative = m_alternative->m_parent->addNewAlternative(); + } + + unsigned setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition) + { + alternative->m_hasFixedSize = true; + Checked currentInputPosition = initialInputPosition; + + for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { + PatternTerm& term = alternative->m_terms[i]; + + switch (term.type) { + case PatternTerm::TypeAssertionBOL: + case PatternTerm::TypeAssertionEOL: + case PatternTerm::TypeAssertionWordBoundary: + term.inputPosition = currentInputPosition.unsafeGet(); + break; + + case PatternTerm::TypeBackReference: + term.inputPosition = currentInputPosition.unsafeGet(); + term.frameLocation = currentCallFrameSize; + currentCallFrameSize += YarrStackSpaceForBackTrackInfoBackReference; + alternative->m_hasFixedSize = false; + break; + + case PatternTerm::TypeForwardReference: + break; + + case PatternTerm::TypePatternCharacter: + term.inputPosition = currentInputPosition.unsafeGet(); + if (term.quantityType != QuantifierFixedCount) { + term.frameLocation = currentCallFrameSize; + currentCallFrameSize += YarrStackSpaceForBackTrackInfoPatternCharacter; + alternative->m_hasFixedSize = false; + } else + currentInputPosition += term.quantityCount; + break; + + case PatternTerm::TypeCharacterClass: + term.inputPosition = currentInputPosition.unsafeGet(); + if (term.quantityType != QuantifierFixedCount) { + term.frameLocation = currentCallFrameSize; + currentCallFrameSize += YarrStackSpaceForBackTrackInfoCharacterClass; + alternative->m_hasFixedSize = false; + } else + currentInputPosition += term.quantityCount; + break; + + case PatternTerm::TypeParenthesesSubpattern: + // Note: for fixed once parentheses we will ensure at least the minimum is available; others are on their own. + term.frameLocation = currentCallFrameSize; + if (term.quantityCount == 1 && !term.parentheses.isCopy) { + if (term.quantityType != QuantifierFixedCount) + currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesOnce; + currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet()); + // If quantity is fixed, then pre-check its minimum size. + if (term.quantityType == QuantifierFixedCount) + currentInputPosition += term.parentheses.disjunction->m_minimumSize; + term.inputPosition = currentInputPosition.unsafeGet(); + } else if (term.parentheses.isTerminal) { + currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesTerminal; + currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet()); + term.inputPosition = currentInputPosition.unsafeGet(); + } else { + term.inputPosition = currentInputPosition.unsafeGet(); + setupDisjunctionOffsets(term.parentheses.disjunction, 0, currentInputPosition.unsafeGet()); + currentCallFrameSize += YarrStackSpaceForBackTrackInfoParentheses; + } + // Fixed count of 1 could be accepted, if they have a fixed size *AND* if all alternatives are of the same length. + alternative->m_hasFixedSize = false; + break; + + case PatternTerm::TypeParentheticalAssertion: + term.inputPosition = currentInputPosition.unsafeGet(); + term.frameLocation = currentCallFrameSize; + currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition.unsafeGet()); + break; + + case PatternTerm::TypeDotStarEnclosure: + alternative->m_hasFixedSize = false; + term.inputPosition = initialInputPosition; + break; + } + } + + alternative->m_minimumSize = (currentInputPosition - initialInputPosition).unsafeGet(); + return currentCallFrameSize; + } + + unsigned setupDisjunctionOffsets(PatternDisjunction* disjunction, unsigned initialCallFrameSize, unsigned initialInputPosition) + { + if ((disjunction != m_pattern.m_body) && (disjunction->m_alternatives.size() > 1)) + initialCallFrameSize += YarrStackSpaceForBackTrackInfoAlternative; + + unsigned minimumInputSize = UINT_MAX; + unsigned maximumCallFrameSize = 0; + bool hasFixedSize = true; + + for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) { + PatternAlternative* alternative = disjunction->m_alternatives[alt]; + unsigned currentAlternativeCallFrameSize = setupAlternativeOffsets(alternative, initialCallFrameSize, initialInputPosition); + minimumInputSize = std::min(minimumInputSize, alternative->m_minimumSize); + maximumCallFrameSize = std::max(maximumCallFrameSize, currentAlternativeCallFrameSize); + hasFixedSize &= alternative->m_hasFixedSize; + } + + ASSERT(minimumInputSize != UINT_MAX); + ASSERT(maximumCallFrameSize >= initialCallFrameSize); + + disjunction->m_hasFixedSize = hasFixedSize; + disjunction->m_minimumSize = minimumInputSize; + disjunction->m_callFrameSize = maximumCallFrameSize; + return maximumCallFrameSize; + } + + void setupOffsets() + { + setupDisjunctionOffsets(m_pattern.m_body, 0, 0); + } + + // This optimization identifies sets of parentheses that we will never need to backtrack. + // In these cases we do not need to store state from prior iterations. + // We can presently avoid backtracking for: + // * where the parens are at the end of the regular expression (last term in any of the + // alternatives of the main body disjunction). + // * where the parens are non-capturing, and quantified unbounded greedy (*). + // * where the parens do not contain any capturing subpatterns. + void checkForTerminalParentheses() + { + // This check is much too crude; should be just checking whether the candidate + // node contains nested capturing subpatterns, not the whole expression! + if (m_pattern.m_numSubpatterns) + return; + + Vector& alternatives = m_pattern.m_body->m_alternatives; + for (size_t i = 0; i < alternatives.size(); ++i) { + Vector& terms = alternatives[i]->m_terms; + if (terms.size()) { + PatternTerm& term = terms.last(); + if (term.type == PatternTerm::TypeParenthesesSubpattern + && term.quantityType == QuantifierGreedy + && term.quantityCount == quantifyInfinite + && !term.capture()) + term.parentheses.isTerminal = true; + } + } + } + + void optimizeBOL() + { + // Look for expressions containing beginning of line (^) anchoring and unroll them. + // e.g. /^a|^b|c/ becomes /^a|^b|c/ which is executed once followed by /c/ which loops + // This code relies on the parsing code tagging alternatives with m_containsBOL and + // m_startsWithBOL and rolling those up to containing alternatives. + // At this point, this is only valid for non-multiline expressions. + PatternDisjunction* disjunction = m_pattern.m_body; + + if (!m_pattern.m_containsBOL || m_pattern.m_multiline) + return; + + PatternDisjunction* loopDisjunction = copyDisjunction(disjunction, true); + + // Set alternatives in disjunction to "onceThrough" + for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) + disjunction->m_alternatives[alt]->setOnceThrough(); + + if (loopDisjunction) { + // Move alternatives from loopDisjunction to disjunction + for (unsigned alt = 0; alt < loopDisjunction->m_alternatives.size(); ++alt) + disjunction->m_alternatives.append(loopDisjunction->m_alternatives[alt]); + + loopDisjunction->m_alternatives.clear(); + } + } + + bool containsCapturingTerms(PatternAlternative* alternative, size_t firstTermIndex, size_t lastTermIndex) + { + Vector& terms = alternative->m_terms; + + for (size_t termIndex = firstTermIndex; termIndex <= lastTermIndex; ++termIndex) { + PatternTerm& term = terms[termIndex]; + + if (term.m_capture) + return true; + + if (term.type == PatternTerm::TypeParenthesesSubpattern) { + PatternDisjunction* nestedDisjunction = term.parentheses.disjunction; + for (unsigned alt = 0; alt < nestedDisjunction->m_alternatives.size(); ++alt) { + if (containsCapturingTerms(nestedDisjunction->m_alternatives[alt], 0, nestedDisjunction->m_alternatives[alt]->m_terms.size() - 1)) + return true; + } + } + } + + return false; + } + + // This optimization identifies alternatives in the form of + // [^].*[?].*[$] for expressions that don't have any + // capturing terms. The alternative is changed to + // followed by processing of the dot stars to find and adjust the + // beginning and the end of the match. + void optimizeDotStarWrappedExpressions() + { + Vector& alternatives = m_pattern.m_body->m_alternatives; + if (alternatives.size() != 1) + return; + + PatternAlternative* alternative = alternatives[0]; + Vector& terms = alternative->m_terms; + if (terms.size() >= 3) { + bool startsWithBOL = false; + bool endsWithEOL = false; + size_t termIndex, firstExpressionTerm, lastExpressionTerm; + + termIndex = 0; + if (terms[termIndex].type == PatternTerm::TypeAssertionBOL) { + startsWithBOL = true; + ++termIndex; + } + + PatternTerm& firstNonAnchorTerm = terms[termIndex]; + if ((firstNonAnchorTerm.type != PatternTerm::TypeCharacterClass) || (firstNonAnchorTerm.characterClass != m_pattern.newlineCharacterClass()) || !((firstNonAnchorTerm.quantityType == QuantifierGreedy) || (firstNonAnchorTerm.quantityType == QuantifierNonGreedy))) + return; + + firstExpressionTerm = termIndex + 1; + + termIndex = terms.size() - 1; + if (terms[termIndex].type == PatternTerm::TypeAssertionEOL) { + endsWithEOL = true; + --termIndex; + } + + PatternTerm& lastNonAnchorTerm = terms[termIndex]; + if ((lastNonAnchorTerm.type != PatternTerm::TypeCharacterClass) || (lastNonAnchorTerm.characterClass != m_pattern.newlineCharacterClass()) || (lastNonAnchorTerm.quantityType != QuantifierGreedy)) + return; + + lastExpressionTerm = termIndex - 1; + + if (firstExpressionTerm > lastExpressionTerm) + return; + + if (!containsCapturingTerms(alternative, firstExpressionTerm, lastExpressionTerm)) { + for (termIndex = terms.size() - 1; termIndex > lastExpressionTerm; --termIndex) + terms.remove(termIndex); + + for (termIndex = firstExpressionTerm; termIndex > 0; --termIndex) + terms.remove(termIndex - 1); + + terms.append(PatternTerm(startsWithBOL, endsWithEOL)); + + m_pattern.m_containsBOL = false; + } + } + } + +private: + YarrPattern& m_pattern; + PatternAlternative* m_alternative; + CharacterClassConstructor m_characterClassConstructor; + bool m_invertCharacterClass; + bool m_invertParentheticalAssertion; +}; + +const char* YarrPattern::compile(const String& patternString) +{ + YarrPatternConstructor constructor(*this); + + if (const char* error = parse(constructor, patternString)) + return error; + + // If the pattern contains illegal backreferences reset & reparse. + // Quoting Netscape's "What's new in JavaScript 1.2", + // "Note: if the number of left parentheses is less than the number specified + // in \#, the \# is taken as an octal escape as described in the next row." + if (containsIllegalBackReference()) { + unsigned numSubpatterns = m_numSubpatterns; + + constructor.reset(); +#if !ASSERT_DISABLED + const char* error = +#endif + parse(constructor, patternString, numSubpatterns); + + ASSERT(!error); + ASSERT(numSubpatterns == m_numSubpatterns); + } + + constructor.checkForTerminalParentheses(); + constructor.optimizeDotStarWrappedExpressions(); + constructor.optimizeBOL(); + + constructor.setupOffsets(); + + return 0; +} + +YarrPattern::YarrPattern(const String& pattern, bool ignoreCase, bool multiline, const char** error) + : m_ignoreCase(ignoreCase) + , m_multiline(multiline) + , m_containsBackreferences(false) + , m_containsBOL(false) + , m_numSubpatterns(0) + , m_maxBackReference(0) + , newlineCached(0) + , digitsCached(0) + , spacesCached(0) + , wordcharCached(0) + , nondigitsCached(0) + , nonspacesCached(0) + , nonwordcharCached(0) +{ + *error = compile(pattern); +} + +} } diff --git a/src/3rdparty/masm/yarr/YarrPattern.h b/src/3rdparty/masm/yarr/YarrPattern.h new file mode 100644 index 0000000000..14e89b8e09 --- /dev/null +++ b/src/3rdparty/masm/yarr/YarrPattern.h @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrPattern_h +#define YarrPattern_h + +#include +#include +#include +#include +#include + +namespace JSC { namespace Yarr { + +struct PatternDisjunction; + +struct CharacterRange { + UChar begin; + UChar end; + + CharacterRange(UChar begin, UChar end) + : begin(begin) + , end(end) + { + } +}; + +struct CharacterClassTable : RefCounted { + const char* m_table; + bool m_inverted; + static PassRefPtr create(const char* table, bool inverted) + { + return adoptRef(new CharacterClassTable(table, inverted)); + } + +private: + CharacterClassTable(const char* table, bool inverted) + : m_table(table) + , m_inverted(inverted) + { + } +}; + +struct CharacterClass { + WTF_MAKE_FAST_ALLOCATED; +public: + // All CharacterClass instances have to have the full set of matches and ranges, + // they may have an optional table for faster lookups (which must match the + // specified matches and ranges) + CharacterClass(PassRefPtr table) + : m_table(table) + { + } + Vector m_matches; + Vector m_ranges; + Vector m_matchesUnicode; + Vector m_rangesUnicode; + RefPtr m_table; +}; + +enum QuantifierType { + QuantifierFixedCount, + QuantifierGreedy, + QuantifierNonGreedy, +}; + +struct PatternTerm { + enum Type { + TypeAssertionBOL, + TypeAssertionEOL, + TypeAssertionWordBoundary, + TypePatternCharacter, + TypeCharacterClass, + TypeBackReference, + TypeForwardReference, + TypeParenthesesSubpattern, + TypeParentheticalAssertion, + TypeDotStarEnclosure, + } type; + bool m_capture :1; + bool m_invert :1; + union { + UChar patternCharacter; + CharacterClass* characterClass; + unsigned backReferenceSubpatternId; + struct { + PatternDisjunction* disjunction; + unsigned subpatternId; + unsigned lastSubpatternId; + bool isCopy; + bool isTerminal; + } parentheses; + struct { + bool bolAnchor : 1; + bool eolAnchor : 1; + } anchors; + }; + QuantifierType quantityType; + Checked quantityCount; + int inputPosition; + unsigned frameLocation; + + PatternTerm(UChar ch) + : type(PatternTerm::TypePatternCharacter) + , m_capture(false) + , m_invert(false) + { + patternCharacter = ch; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(CharacterClass* charClass, bool invert) + : type(PatternTerm::TypeCharacterClass) + , m_capture(false) + , m_invert(invert) + { + characterClass = charClass; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(Type type, unsigned subpatternId, PatternDisjunction* disjunction, bool capture = false, bool invert = false) + : type(type) + , m_capture(capture) + , m_invert(invert) + { + parentheses.disjunction = disjunction; + parentheses.subpatternId = subpatternId; + parentheses.isCopy = false; + parentheses.isTerminal = false; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(Type type, bool invert = false) + : type(type) + , m_capture(false) + , m_invert(invert) + { + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(unsigned spatternId) + : type(TypeBackReference) + , m_capture(false) + , m_invert(false) + { + backReferenceSubpatternId = spatternId; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + PatternTerm(bool bolAnchor, bool eolAnchor) + : type(TypeDotStarEnclosure) + , m_capture(false) + , m_invert(false) + { + anchors.bolAnchor = bolAnchor; + anchors.eolAnchor = eolAnchor; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + static PatternTerm ForwardReference() + { + return PatternTerm(TypeForwardReference); + } + + static PatternTerm BOL() + { + return PatternTerm(TypeAssertionBOL); + } + + static PatternTerm EOL() + { + return PatternTerm(TypeAssertionEOL); + } + + static PatternTerm WordBoundary(bool invert) + { + return PatternTerm(TypeAssertionWordBoundary, invert); + } + + bool invert() + { + return m_invert; + } + + bool capture() + { + return m_capture; + } + + void quantify(unsigned count, QuantifierType type) + { + quantityCount = count; + quantityType = type; + } +}; + +struct PatternAlternative { + WTF_MAKE_FAST_ALLOCATED; +public: + PatternAlternative(PatternDisjunction* disjunction) + : m_parent(disjunction) + , m_onceThrough(false) + , m_hasFixedSize(false) + , m_startsWithBOL(false) + , m_containsBOL(false) + { + } + + PatternTerm& lastTerm() + { + ASSERT(m_terms.size()); + return m_terms[m_terms.size() - 1]; + } + + void removeLastTerm() + { + ASSERT(m_terms.size()); + m_terms.shrink(m_terms.size() - 1); + } + + void setOnceThrough() + { + m_onceThrough = true; + } + + bool onceThrough() + { + return m_onceThrough; + } + + Vector m_terms; + PatternDisjunction* m_parent; + unsigned m_minimumSize; + bool m_onceThrough : 1; + bool m_hasFixedSize : 1; + bool m_startsWithBOL : 1; + bool m_containsBOL : 1; +}; + +struct PatternDisjunction { + WTF_MAKE_FAST_ALLOCATED; +public: + PatternDisjunction(PatternAlternative* parent = 0) + : m_parent(parent) + , m_hasFixedSize(false) + { + } + + ~PatternDisjunction() + { + deleteAllValues(m_alternatives); + } + + PatternAlternative* addNewAlternative() + { + PatternAlternative* alternative = new PatternAlternative(this); + m_alternatives.append(alternative); + return alternative; + } + + Vector m_alternatives; + PatternAlternative* m_parent; + unsigned m_minimumSize; + unsigned m_callFrameSize; + bool m_hasFixedSize; +}; + +// You probably don't want to be calling these functions directly +// (please to be calling newlineCharacterClass() et al on your +// friendly neighborhood YarrPattern instance to get nicely +// cached copies). +CharacterClass* newlineCreate(); +CharacterClass* digitsCreate(); +CharacterClass* spacesCreate(); +CharacterClass* wordcharCreate(); +CharacterClass* nondigitsCreate(); +CharacterClass* nonspacesCreate(); +CharacterClass* nonwordcharCreate(); + +struct TermChain { + TermChain(PatternTerm term) + : term(term) + {} + + PatternTerm term; + Vector hotTerms; +}; + +struct YarrPattern { + JS_EXPORT_PRIVATE YarrPattern(const String& pattern, bool ignoreCase, bool multiline, const char** error); + + ~YarrPattern() + { + deleteAllValues(m_disjunctions); + deleteAllValues(m_userCharacterClasses); + } + + void reset() + { + m_numSubpatterns = 0; + m_maxBackReference = 0; + + m_containsBackreferences = false; + m_containsBOL = false; + + newlineCached = 0; + digitsCached = 0; + spacesCached = 0; + wordcharCached = 0; + nondigitsCached = 0; + nonspacesCached = 0; + nonwordcharCached = 0; + + deleteAllValues(m_disjunctions); + m_disjunctions.clear(); + deleteAllValues(m_userCharacterClasses); + m_userCharacterClasses.clear(); + } + + bool containsIllegalBackReference() + { + return m_maxBackReference > m_numSubpatterns; + } + + CharacterClass* newlineCharacterClass() + { + if (!newlineCached) + m_userCharacterClasses.append(newlineCached = newlineCreate()); + return newlineCached; + } + CharacterClass* digitsCharacterClass() + { + if (!digitsCached) + m_userCharacterClasses.append(digitsCached = digitsCreate()); + return digitsCached; + } + CharacterClass* spacesCharacterClass() + { + if (!spacesCached) + m_userCharacterClasses.append(spacesCached = spacesCreate()); + return spacesCached; + } + CharacterClass* wordcharCharacterClass() + { + if (!wordcharCached) + m_userCharacterClasses.append(wordcharCached = wordcharCreate()); + return wordcharCached; + } + CharacterClass* nondigitsCharacterClass() + { + if (!nondigitsCached) + m_userCharacterClasses.append(nondigitsCached = nondigitsCreate()); + return nondigitsCached; + } + CharacterClass* nonspacesCharacterClass() + { + if (!nonspacesCached) + m_userCharacterClasses.append(nonspacesCached = nonspacesCreate()); + return nonspacesCached; + } + CharacterClass* nonwordcharCharacterClass() + { + if (!nonwordcharCached) + m_userCharacterClasses.append(nonwordcharCached = nonwordcharCreate()); + return nonwordcharCached; + } + + bool m_ignoreCase : 1; + bool m_multiline : 1; + bool m_containsBackreferences : 1; + bool m_containsBOL : 1; + unsigned m_numSubpatterns; + unsigned m_maxBackReference; + PatternDisjunction* m_body; + Vector m_disjunctions; + Vector m_userCharacterClasses; + +private: + const char* compile(const String& patternString); + + CharacterClass* newlineCached; + CharacterClass* digitsCached; + CharacterClass* spacesCached; + CharacterClass* wordcharCached; + CharacterClass* nondigitsCached; + CharacterClass* nonspacesCached; + CharacterClass* nonwordcharCached; +}; + +} } // namespace JSC::Yarr + +#endif // YarrPattern_h diff --git a/src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp b/src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp new file mode 100644 index 0000000000..aa98c4a354 --- /dev/null +++ b/src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "YarrSyntaxChecker.h" + +#include "YarrParser.h" + +namespace JSC { namespace Yarr { + +class SyntaxChecker { +public: + void assertionBOL() {} + void assertionEOL() {} + void assertionWordBoundary(bool) {} + void atomPatternCharacter(UChar) {} + void atomBuiltInCharacterClass(BuiltInCharacterClassID, bool) {} + void atomCharacterClassBegin(bool = false) {} + void atomCharacterClassAtom(UChar) {} + void atomCharacterClassRange(UChar, UChar) {} + void atomCharacterClassBuiltIn(BuiltInCharacterClassID, bool) {} + void atomCharacterClassEnd() {} + void atomParenthesesSubpatternBegin(bool = true) {} + void atomParentheticalAssertionBegin(bool = false) {} + void atomParenthesesEnd() {} + void atomBackReference(unsigned) {} + void quantifyAtom(unsigned, unsigned, bool) {} + void disjunction() {} +}; + +const char* checkSyntax(const String& pattern) +{ + SyntaxChecker syntaxChecker; + return parse(syntaxChecker, pattern); +} + +}} // JSC::YARR diff --git a/src/3rdparty/masm/yarr/YarrSyntaxChecker.h b/src/3rdparty/masm/yarr/YarrSyntaxChecker.h new file mode 100644 index 0000000000..104ced3ab4 --- /dev/null +++ b/src/3rdparty/masm/yarr/YarrSyntaxChecker.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrSyntaxChecker_h +#define YarrSyntaxChecker_h + +#include + +namespace JSC { namespace Yarr { + +const char* checkSyntax(const String& pattern); + +}} // JSC::YARR + +#endif // YarrSyntaxChecker_h + diff --git a/src/3rdparty/masm/yarr/yarr.pri b/src/3rdparty/masm/yarr/yarr.pri new file mode 100644 index 0000000000..7e9b4d3f3b --- /dev/null +++ b/src/3rdparty/masm/yarr/yarr.pri @@ -0,0 +1,12 @@ +# ------------------------------------------------------------------- +# Project file for YARR +# +# See 'Tools/qmake/README' for an overview of the build system +# ------------------------------------------------------------------- + +SOURCES += \ + $$PWD/YarrInterpreter.cpp \ + $$PWD/YarrPattern.cpp \ + $$PWD/YarrSyntaxChecker.cpp \ + $$PWD/YarrCanonicalizeUCS2.cpp + diff --git a/src/src.pro b/src/src.pro new file mode 100644 index 0000000000..706285498d --- /dev/null +++ b/src/src.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = v4 tools +CONFIG += ordered diff --git a/src/tools/main.cpp b/src/tools/main.cpp new file mode 100644 index 0000000000..1983191942 --- /dev/null +++ b/src/tools/main.cpp @@ -0,0 +1,426 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLJS_NO_LLVM +# include "private/qv4_llvm_p.h" +#endif + +#include "private/debugging.h" +#include "private/qv4object.h" +#include "private/qmljs_runtime.h" +#include "private/qv4functionobject.h" +#include "private/qv4errorobject.h" +#include "private/qv4globalobject.h" +#include "private/qv4codegen_p.h" +#include "private/qv4isel_masm_p.h" +#include "private/qv4isel_moth_p.h" +#include "private/qv4vme_moth_p.h" +#include "private/qv4syntaxchecker_p.h" +#include "private/qv4objectproto.h" +#include "private/qv4isel_p.h" +#include "private/qv4mm.h" +#include "private/qmljs_environment.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace builtins { + +using namespace QQmlJS::VM; + +struct Q_V4_EXPORT Print: FunctionObject +{ + Print(ExecutionContext *scope): FunctionObject(scope) { + name = scope->engine->newString("print"); + } + + virtual Value call(ExecutionContext *ctx) + { + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { + String *s = ctx->argument(i).toString(ctx); + if (i) + std::cout << ' '; + std::cout << qPrintable(s->toQString()); + } + std::cout << std::endl; + return Value::undefinedValue(); + } +}; + +struct Q_V4_EXPORT TestHarnessError: FunctionObject +{ + TestHarnessError(ExecutionContext *scope, bool &errorInTestHarness): FunctionObject(scope), errorOccurred(errorInTestHarness) { + name = scope->engine->newString("$ERROR"); + } + + virtual Value call(ExecutionContext *ctx) + { + errorOccurred = true; + + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { + String *s = ctx->argument(i).toString(ctx); + if (i) + std::cerr << ' '; + std::cerr << qPrintable(s->toQString()); + } + std::cerr << std::endl; + return Value::undefinedValue(); + } + + bool &errorOccurred; +}; + +struct Q_V4_EXPORT GC: public FunctionObject +{ + GC(ExecutionContext* scope) + : FunctionObject(scope) + { + name = scope->engine->newString("gc"); + } + virtual Value call(ExecutionContext *ctx) + { + ctx->engine->memoryManager->runGC(); + return Value::undefinedValue(); + } +}; + +} // builtins + +static void showException(QQmlJS::VM::ExecutionContext *ctx) +{ + QQmlJS::VM::ErrorObject *e = ctx->engine->exception.asErrorObject(); + if (!e) { + std::cerr << "Uncaught exception: " << qPrintable(ctx->engine->exception.toString(ctx)->toQString()) << std::endl; + return; + } + + if (QQmlJS::VM::SyntaxErrorObject *err = e->asSyntaxError()) { + QQmlJS::VM::DiagnosticMessage *msg = err->message(); + if (!msg) { + std::cerr << "Uncaught exception: Syntax error" << std::endl; + return; + } + + for (; msg; msg = msg->next) { + std::cerr << qPrintable(msg->buildFullMessage(ctx)->toQString()) << std::endl; + } + } else { + std::cerr << "Uncaught exception: " << qPrintable(e->__get__(ctx, ctx->engine->identifier(QStringLiteral("message")), 0).toString(ctx)->toQString()) << std::endl; + } +} + +#ifndef QMLJS_NO_LLVM +int executeLLVMCode(void *codePtr) +{ + using namespace QQmlJS; + + if (!codePtr) + return EXIT_FAILURE; + void (*code)(VM::ExecutionContext *) = (void (*)(VM::ExecutionContext *)) codePtr; + + QScopedPointer iSelFactory(new QQmlJS::Moth::ISelFactory); + VM::ExecutionEngine vm(iSelFactory.data()); + VM::ExecutionContext *ctx = vm.rootContext; + + QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); + globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), + QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::Print(ctx))); + + void * buf = __qmljs_create_exception_handler(ctx); + if (setjmp(*(jmp_buf *)buf)) { + showException(ctx); + return EXIT_FAILURE; + } + + code(ctx); + return EXIT_SUCCESS; +} + +int compile(const QString &fileName, const QString &source, QQmlJS::LLVMOutputType outputType) +{ + using namespace QQmlJS; + + IR::Module module; + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(source, 1, false); + Parser parser(engine); + + const bool parsed = parser.parseProgram(); + + foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { + std::cerr << qPrintable(fileName) << ':' + << m.loc.startLine << ':' + << m.loc.startColumn << ": error: " + << qPrintable(m.message) << std::endl; + } + + if (!parsed) + return EXIT_FAILURE; + + using namespace AST; + Program *program = AST::cast(parser.rootNode()); + + class MyErrorHandler: public ErrorHandler { + public: + virtual void syntaxError(QQmlJS::VM::DiagnosticMessage *message) { + for (; message; message = message->next) { + std::cerr << qPrintable(message->fileName) << ':' + << message->startLine << ':' + << message->startColumn << ": " + << (message->type == QQmlJS::VM::DiagnosticMessage::Error ? "error" : "warning") << ": " + << qPrintable(message->message) << std::endl; + } + } + } errorHandler; + + Codegen cg(&errorHandler, false); + // FIXME: if the program is empty, we should we generate an empty %entry, or give an error? + /*IR::Function *globalCode =*/ cg(fileName, program, &module); + + int (*exec)(void *) = outputType == LLVMOutputJit ? executeLLVMCode : 0; + return compileWithLLVM(&module, fileName, outputType, exec); +} + +int compileFiles(const QStringList &files, QQmlJS::LLVMOutputType outputType) +{ + foreach (const QString &fileName, files) { + QFile file(fileName); + if (file.open(QFile::ReadOnly)) { + QString source = QString::fromUtf8(file.readAll()); + int result = compile(fileName, source, outputType); + if (result != EXIT_SUCCESS) + return result; + } else { + std::cerr << "Error: cannot open file " << fileName.toUtf8().constData() << std::endl; + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} + +int evaluateCompiledCode(const QStringList &files) +{ + using namespace QQmlJS; + + foreach (const QString &libName, files) { + QFileInfo libInfo(libName); + QLibrary lib(libInfo.absoluteFilePath()); + lib.load(); + QFunctionPointer ptr = lib.resolve("%entry"); +// qDebug("_%%entry resolved to address %p", ptr); + int result = executeLLVMCode((void *) ptr); + if (result != EXIT_SUCCESS) + return result; + } + + return EXIT_SUCCESS; +} + +#endif + + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + QStringList args = app.arguments(); + args.removeFirst(); + + enum { + use_masm, + use_moth, + use_llvm_compiler, + use_llvm_runtime, + use_llvm_jit + } mode = use_masm; + +#ifndef QMLJS_NO_LLVM + QQmlJS::LLVMOutputType fileType = QQmlJS::LLVMOutputObject; +#endif // QMLJS_NO_LLVM + bool enableDebugging = false; + + if (!args.isEmpty()) { + if (args.first() == QLatin1String("-d") || args.first() == QLatin1String("--debug")) { + enableDebugging = true; + args.removeFirst(); + } + } + + if (!args.isEmpty()) { + if (args.first() == QLatin1String("--jit")) { + mode = use_masm; + args.removeFirst(); + } + + if (args.first() == QLatin1String("--interpret")) { + mode = use_moth; + args.removeFirst(); + } + +#ifndef QMLJS_NO_LLVM + if (args.first() == QLatin1String("--compile")) { + mode = use_llvm_compiler; + args.removeFirst(); + + if (!args.isEmpty() && args.first() == QLatin1String("-t")) { + args.removeFirst(); + // Note: keep this list in sync with the enum! + static QStringList fileTypes = QStringList() << QLatin1String("ll") << QLatin1String("bc") << QLatin1String("asm") << QLatin1String("obj"); + if (args.isEmpty() || !fileTypes.contains(args.first())) { + std::cerr << "file types: ll, bc, asm, obj" << std::endl; + return EXIT_FAILURE; + } + fileType = (QQmlJS::LLVMOutputType) fileTypes.indexOf(args.first()); + args.removeFirst(); + } + } + + if (args.first() == QLatin1String("--aot")) { + mode = use_llvm_runtime; + args.removeFirst(); + } + + if (args.first() == QLatin1String("--llvm-jit")) { + mode = use_llvm_jit; + args.removeFirst(); + } +#endif // QMLJS_NO_LLVM + if (args.first() == QLatin1String("--help")) { + std::cerr << "Usage: v4 [|--debug|-d] [|--jit|--interpret|--compile|--aot|--llvm-jit] file..." << std::endl; + return EXIT_SUCCESS; + } + } + + switch (mode) { +#ifdef QMLJS_NO_LLVM + case use_llvm_compiler: + case use_llvm_runtime: + case use_llvm_jit: + std::cerr << "LLVM backend was not built, compiler is unavailable." << std::endl; + return EXIT_FAILURE; +#else // QMLJS_NO_LLVM + case use_llvm_jit: + return compileFiles(args, QQmlJS::LLVMOutputJit); + case use_llvm_compiler: + return compileFiles(args, fileType); + case use_llvm_runtime: + return evaluateCompiledCode(args); +#endif // QMLJS_NO_LLVM + case use_masm: + case use_moth: { + QScopedPointer iSelFactory; + if (mode == use_moth) { + iSelFactory.reset(new QQmlJS::Moth::ISelFactory); + } else { + iSelFactory.reset(new QQmlJS::MASM::ISelFactory); + } + + QQmlJS::VM::ExecutionEngine vm(iSelFactory.data()); + + QScopedPointer debugger; + if (enableDebugging) + debugger.reset(new QQmlJS::Debugging::Debugger(&vm)); + vm.debugger = debugger.data(); + + QQmlJS::VM::ExecutionContext *ctx = vm.rootContext; + + QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); + QQmlJS::VM::Object *print = new (ctx->engine->memoryManager) builtins::Print(ctx); + print->prototype = ctx->engine->objectPrototype; + globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), + QQmlJS::VM::Value::fromObject(print)); + QQmlJS::VM::Object *gc = new (ctx->engine->memoryManager) builtins::GC(ctx); + gc->prototype = ctx->engine->objectPrototype; + globalObject->__put__(ctx, vm.identifier(QStringLiteral("gc")), + QQmlJS::VM::Value::fromObject(gc)); + + bool errorInTestHarness = false; + if (!qgetenv("IN_TEST_HARNESS").isEmpty()) + globalObject->__put__(ctx, vm.identifier(QStringLiteral("$ERROR")), + QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::TestHarnessError(ctx, errorInTestHarness))); + + foreach (const QString &fn, args) { + QFile file(fn); + if (file.open(QFile::ReadOnly)) { + const QString code = QString::fromUtf8(file.readAll()); + file.close(); + + void * buf = __qmljs_create_exception_handler(ctx); + if (setjmp(*(jmp_buf *)buf)) { + showException(ctx); + return EXIT_FAILURE; + } + + QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode, + /*strictMode =*/ false, /*inheritContext =*/ false); + if (!f) + continue; + vm.globalCode = f; + + ctx->strictMode = f->isStrict; + if (debugger) + debugger->aboutToCall(0, ctx); + QQmlJS::VM::Value result = f->code(ctx, f->codeData); + if (debugger) + debugger->justLeft(ctx); + if (!result.isUndefined()) { + if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) + std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; + } + + if (errorInTestHarness) + return EXIT_FAILURE; + } else { + std::cerr << "Error: cannot open file " << fn.toUtf8().constData() << std::endl; + return EXIT_FAILURE; + } + } + + vm.memoryManager->dumpStats(); + } return EXIT_SUCCESS; + } // switch (mode) +} diff --git a/src/tools/tools.pro b/src/tools/tools.pro new file mode 100644 index 0000000000..820eb18769 --- /dev/null +++ b/src/tools/tools.pro @@ -0,0 +1,17 @@ +TEMPLATE = app +QT = core v4 v4-private core-private qmldevtools-private +SOURCES = main.cpp + +TARGET = v4 + +INCLUDEPATH += ../v4 +INCLUDEPATH += ../3rdparty/masm +INCLUDEPATH += ../3rdparty/masm/wtf +INCLUDEPATH += ../3rdparty/masm/stubs +INCLUDEPATH += ../3rdparty/masm/stubs/wtf +INCLUDEPATH += ../3rdparty/masm/jit +INCLUDEPATH += ../3rdparty/masm/assembler +INCLUDEPATH += ../3rdparty/masm/disassembler + +DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" +DEFINES += QMLJS_NO_LLVM \ No newline at end of file diff --git a/src/v4/debugging.cpp b/src/v4/debugging.cpp new file mode 100644 index 0000000000..61ae0e62d0 --- /dev/null +++ b/src/v4/debugging.cpp @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "debugging.h" +#include "qv4object.h" +#include "qv4functionobject.h" +#include + +#define LOW_LEVEL_DEBUGGING_HELPERS + +using namespace QQmlJS; +using namespace QQmlJS::Debugging; + +FunctionState::FunctionState(VM::ExecutionContext *context) + : _context(context) +{ + if (debugger()) + debugger()->enterFunction(this); +} + +FunctionState::~FunctionState() +{ + if (debugger()) + debugger()->leaveFunction(this); +} + +VM::Value *FunctionState::argument(unsigned idx) +{ + if (idx < _context->argumentCount) + return _context->arguments + idx; + else + return 0; +} + +VM::Value *FunctionState::local(unsigned idx) +{ + if (idx < _context->variableCount()) + return _context->locals + idx; + return 0; +} + +#ifdef LOW_LEVEL_DEBUGGING_HELPERS +Debugger *globalInstance = 0; + +void printStackTrace() +{ + if (globalInstance) + globalInstance->printStackTrace(); + else + std::cerr << "No debugger." << std::endl; +} +#endif // DO_TRACE_INSTR + +Debugger::Debugger(VM::ExecutionEngine *engine) + : _engine(engine) +{ +#ifdef LOW_LEVEL_DEBUGGING_HELPERS + globalInstance = this; +#endif // DO_TRACE_INSTR +} + +Debugger::~Debugger() +{ +#ifdef LOW_LEVEL_DEBUGGING_HELPERS + globalInstance = 0; +#endif // DO_TRACE_INSTR + + qDeleteAll(_functionInfo.values()); +} + +void Debugger::addFunction(IR::Function *function) +{ + _functionInfo.insert(function, new FunctionDebugInfo(function)); +} + +void Debugger::setSourceLocation(IR::Function *function, unsigned line, unsigned column) +{ + _functionInfo[function]->setSourceLocation(line, column); +} + +void Debugger::mapFunction(VM::Function *vmf, IR::Function *irf) +{ + _vmToIr.insert(vmf, irf); +} + +FunctionDebugInfo *Debugger::debugInfo(VM::FunctionObject *function) const +{ + if (!function) + return 0; + + if (VM::ScriptFunction *sf = function->asScriptFunction()) + return _functionInfo[irFunction(sf->function)]; + else + return 0; +} + +QString Debugger::name(VM::FunctionObject *function) const +{ + if (FunctionDebugInfo *i = debugInfo(function)) + return i->name; + + return QString(); +} + +void Debugger::aboutToCall(VM::FunctionObject *function, VM::ExecutionContext *context) +{ + _callStack.append(CallInfo(context, function)); +} + +void Debugger::justLeft(VM::ExecutionContext *context) +{ + int idx = callIndex(context); + if (idx < 0) + qDebug() << "Oops, leaving a function that was not registered...?"; + else + _callStack.resize(idx); +} + +void Debugger::enterFunction(FunctionState *state) +{ + _callStack[callIndex(state->context())].state = state; + +#ifdef DO_TRACE_INSTR + QString n = name(_callStack[callIndex(state->context())].function); + std::cerr << "*** Entering \"" << qPrintable(n) << "\" with " << state->context()->argumentCount << " args" << std::endl; +// for (unsigned i = 0; i < state->context()->variableEnvironment->argumentCount; ++i) +// std::cerr << " " << i << ": " << currentArg(i) << std::endl; +#endif // DO_TRACE_INSTR +} + +void Debugger::leaveFunction(FunctionState *state) +{ + _callStack[callIndex(state->context())].state = 0; +} + +void Debugger::aboutToThrow(VM::Value *value) +{ + qDebug() << "*** We are about to throw...:" << value->toString(currentState()->context())->toQString(); +} + +FunctionState *Debugger::currentState() const +{ + if (_callStack.isEmpty()) + return 0; + else + return _callStack.last().state; +} + +const char *Debugger::currentArg(unsigned idx) const +{ + FunctionState *state = currentState(); + return qPrintable(state->argument(idx)->toString(state->context())->toQString()); +} + +const char *Debugger::currentLocal(unsigned idx) const +{ + FunctionState *state = currentState(); + return qPrintable(state->local(idx)->toString(state->context())->toQString()); +} + +const char *Debugger::currentTemp(unsigned idx) const +{ + FunctionState *state = currentState(); + return qPrintable(state->temp(idx)->toString(state->context())->toQString()); +} + +void Debugger::printStackTrace() const +{ + for (int i = _callStack.size() - 1; i >=0; --i) { + QString n = name(_callStack[i].function); + std::cerr << "\tframe #" << i << ": " << qPrintable(n) << std::endl; + } +} + +int Debugger::callIndex(VM::ExecutionContext *context) +{ + for (int idx = _callStack.size() - 1; idx >= 0; --idx) { + if (_callStack[idx].context == context) + return idx; + } + + return -1; +} + +IR::Function *Debugger::irFunction(VM::Function *vmf) const +{ + return _vmToIr[vmf]; +} diff --git a/src/v4/debugging.h b/src/v4/debugging.h new file mode 100644 index 0000000000..00c96717a5 --- /dev/null +++ b/src/v4/debugging.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef DEBUGGING_H +#define DEBUGGING_H + +#include "qv4global.h" +#include "qmljs_engine.h" +#include "qmljs_environment.h" + +#include + +namespace QQmlJS { + +namespace IR { +struct BasicBlock; +struct Function; +} // namespace IR + +namespace Debugging { + +class Debugger; + +struct FunctionDebugInfo { // TODO: use opaque d-pointers here + QString name; + unsigned startLine, startColumn; + + FunctionDebugInfo(IR::Function *function): + startLine(0), startColumn(0) + { + if (function->name) + name = *function->name; + } + + void setSourceLocation(unsigned line, unsigned column) + { startLine = line; startColumn = column; } +}; + +class FunctionState +{ +public: + FunctionState(VM::ExecutionContext *context); + virtual ~FunctionState(); + + virtual VM::Value *argument(unsigned idx); + virtual VM::Value *local(unsigned idx); + virtual VM::Value *temp(unsigned idx) = 0; + + VM::ExecutionContext *context() const + { return _context; } + + Debugger *debugger() const + { return _context->engine->debugger; } + +private: + VM::ExecutionContext *_context; +}; + +struct CallInfo +{ + VM::ExecutionContext *context; + VM::FunctionObject *function; + FunctionState *state; + + CallInfo(VM::ExecutionContext *context = 0, VM::FunctionObject *function = 0, FunctionState *state = 0) + : context(context) + , function(function) + , state(state) + {} +}; + +class Q_V4_EXPORT Debugger +{ +public: + Debugger(VM::ExecutionEngine *_engine); + ~Debugger(); + +public: // compile-time interface + void addFunction(IR::Function *function); + void setSourceLocation(IR::Function *function, unsigned line, unsigned column); + void mapFunction(VM::Function *vmf, IR::Function *irf); + +public: // run-time querying interface + FunctionDebugInfo *debugInfo(VM::FunctionObject *function) const; + QString name(VM::FunctionObject *function) const; + +public: // execution hooks + void aboutToCall(VM::FunctionObject *function, VM::ExecutionContext *context); + void justLeft(VM::ExecutionContext *context); + void enterFunction(FunctionState *state); + void leaveFunction(FunctionState *state); + void aboutToThrow(VM::Value *value); + +public: // debugging hooks + FunctionState *currentState() const; + const char *currentArg(unsigned idx) const; + const char *currentLocal(unsigned idx) const; + const char *currentTemp(unsigned idx) const; + void printStackTrace() const; + +private: + int callIndex(VM::ExecutionContext *context); + IR::Function *irFunction(VM::Function *vmf) const; + +private: // TODO: use opaque d-pointers here + VM::ExecutionEngine *_engine; + QHash _functionInfo; + QHash _vmToIr; + QVector _callStack; +}; + +} // namespace Debugging +} // namespace QQmlJS + +#endif // DEBUGGING_H diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp new file mode 100644 index 0000000000..212c8d17d5 --- /dev/null +++ b/src/v4/llvm_runtime.cpp @@ -0,0 +1,523 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmljs_runtime.h" +#include "qmljs_environment.h" +#include +#include + +using namespace QQmlJS::VM; + +extern "C" { + +Value __qmljs_llvm_return(ExecutionContext */*ctx*/, Value *result) +{ + return *result; +} + +Value *__qmljs_llvm_get_argument(ExecutionContext *ctx, int index) +{ + return &ctx->arguments[index]; +} + +void __qmljs_llvm_init_undefined(Value *result) +{ + *result = Value::undefinedValue(); +} + +void __qmljs_llvm_init_null(Value *result) +{ + *result = Value::nullValue(); +} + +void __qmljs_llvm_init_boolean(Value *result, bool value) +{ + *result = Value::fromBoolean(value); +} + +void __qmljs_llvm_init_number(Value *result, double value) +{ + *result = Value::fromDouble(value); +} + +void __qmljs_llvm_init_string(ExecutionContext *ctx, Value *result, const char *str) +{ + *result = Value::fromString(__qmljs_string_from_utf8(ctx, str)); +} + +void __qmljs_llvm_init_closure(ExecutionContext *ctx, Value *result, + String *name, bool hasDirectEval, + bool usesArgumentsObject, bool isStrict, + bool hasNestedFunctions, + String **formals, unsigned formalCount, + String **locals, unsigned localCount) +{ + Function *clos = __qmljs_register_function(ctx, name, hasDirectEval, + usesArgumentsObject, isStrict, + hasNestedFunctions, + formals, formalCount, + locals, localCount); + *result = __qmljs_init_closure(clos, ctx); +} + +bool __qmljs_llvm_to_boolean(ExecutionContext *ctx, const Value *value) +{ + return __qmljs_to_boolean(*value, ctx); +} + +void __qmljs_llvm_bit_and(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_bit_and(*left, *right, ctx); +} + +void __qmljs_llvm_bit_or(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_bit_or(*left, *right, ctx); +} + +void __qmljs_llvm_bit_xor(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_bit_xor(*left, *right, ctx); +} + +void __qmljs_llvm_add(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_add(*left, *right, ctx); +} + +void __qmljs_llvm_sub(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_sub(*left, *right, ctx); +} + +void __qmljs_llvm_mul(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_mul(*left, *right, ctx); +} + +void __qmljs_llvm_div(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_div(*left, *right, ctx); +} + +void __qmljs_llvm_mod(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_mod(*left, *right, ctx); +} + +void __qmljs_llvm_shl(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_shl(*left, *right, ctx); +} + +void __qmljs_llvm_shr(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_shr(*left, *right, ctx); +} + +void __qmljs_llvm_ushr(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_ushr(*left, *right, ctx); +} + +void __qmljs_llvm_gt(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_gt(*left, *right, ctx); +} + +void __qmljs_llvm_lt(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_lt(*left, *right, ctx); +} + +void __qmljs_llvm_ge(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_ge(*left, *right, ctx); +} + +void __qmljs_llvm_le(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_le(*left, *right, ctx); +} + +void __qmljs_llvm_eq(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_eq(*left, *right, ctx); +} + +void __qmljs_llvm_ne(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_ne(*left, *right, ctx); +} + +void __qmljs_llvm_se(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_se(*left, *right, ctx); +} + +void __qmljs_llvm_sne(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_sne(*left, *right, ctx); +} + +void __qmljs_llvm_instanceof(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_instanceof(*left, *right, ctx); +} + +void __qmljs_llvm_in(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + *result = __qmljs_in(*left, *right, ctx); +} + +void __qmljs_llvm_uplus(ExecutionContext *ctx, Value *result, const Value *value) +{ + *result = __qmljs_uplus(*value, ctx); +} + +void __qmljs_llvm_uminus(ExecutionContext *ctx, Value *result, const Value *value) +{ + *result = __qmljs_uminus(*value, ctx); +} + +void __qmljs_llvm_compl(ExecutionContext *ctx, Value *result, const Value *value) +{ + *result = __qmljs_compl(*value, ctx); +} + +void __qmljs_llvm_not(ExecutionContext *ctx, Value *result, const Value *value) +{ + *result = __qmljs_not(*value, ctx); +} + +void __qmljs_llvm_inplace_bit_and_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_bit_and_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_bit_or_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_bit_or_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_bit_xor_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_bit_xor_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_add_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_add_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_sub_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_sub_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_mul_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_mul_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_div_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_div_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_mod_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_mod_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_shl_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_shl_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_shr_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_shr_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_ushr_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_ushr_name(*src, dest, ctx); +} + +void __qmljs_llvm_inplace_bit_and_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_bit_and_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_bit_or_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_bit_or_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_bit_xor_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_bit_xor_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_add_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_add_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_sub_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_sub_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_mul_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_mul_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_div_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_div_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_mod_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_mod_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_shl_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_shl_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_shr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_shr_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_ushr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_ushr_element(*base, *index, *value, ctx); +} + +void __qmljs_llvm_inplace_bit_and_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_bit_and_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_bit_or_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_bit_or_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_bit_xor_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_bit_xor_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_add_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_add_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_sub_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_sub_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_mul_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_mul_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_div_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_div_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_mod_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_mod_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_shl_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_shl_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_shr_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_shr_member(*value, *base, member, ctx); +} + +void __qmljs_llvm_inplace_ushr_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_ushr_member(*value, *base, member, ctx); +} + +String *__qmljs_llvm_identifier_from_utf8(ExecutionContext *ctx, const char *str) +{ + return __qmljs_identifier_from_utf8(ctx, str); // ### make it unique +} + +void __qmljs_llvm_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) +{ + *result = __qmljs_call_activation_property(context, name, args, argc); +} + +void __qmljs_llvm_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) +{ + Value that = thisObject ? *thisObject : Value::undefinedValue(); + *result = __qmljs_call_value(context, that, *func, args, argc); +} + +void __qmljs_llvm_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) +{ + *result = __qmljs_construct_activation_property(context, name, args, argc); +} + +void __qmljs_llvm_construct_value(ExecutionContext *context, Value *result, const Value *func, Value *args, int argc) +{ + *result = __qmljs_construct_value(context, *func, args, argc); +} + +void __qmljs_llvm_get_activation_property(ExecutionContext *ctx, Value *result, String *name) +{ + *result = __qmljs_get_activation_property(ctx, name); +} + +void __qmljs_llvm_set_activation_property(ExecutionContext *ctx, String *name, Value *value) +{ + __qmljs_set_activation_property(ctx, name, *value); +} + +void __qmljs_llvm_get_property(ExecutionContext *ctx, Value *result, Value *object, String *name) +{ + *result = __qmljs_get_property(ctx, *object, name); +} + +void __qmljs_llvm_call_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) +{ + *result = __qmljs_call_property(context, *base, name, args, argc); +} + +void __qmljs_llvm_construct_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) +{ + *result = __qmljs_construct_property(context, *base, name, args, argc); +} + +void __qmljs_llvm_get_element(ExecutionContext *ctx, Value *result, Value *object, Value *index) +{ + *result = __qmljs_get_element(ctx, *object, *index); +} + +void __qmljs_llvm_set_element(ExecutionContext *ctx, Value *object, Value *index, Value *value) +{ + __qmljs_set_element(ctx, *object, *index, *value); +} + +void __qmljs_llvm_set_property(ExecutionContext *ctx, Value *object, String *name, Value *value) +{ + __qmljs_set_property(ctx, *object, name, *value); +} + +void __qmljs_llvm_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) +{ + __qmljs_builtin_declare_var(ctx, deletable, name); +} + +void __qmljs_llvm_typeof(ExecutionContext *ctx, Value *result, const Value *value) +{ + *result = __qmljs_builtin_typeof(*value, ctx); +} + +void __qmljs_llvm_throw(ExecutionContext *context, Value *value) +{ + __qmljs_throw(*value, context); +} + +void __qmljs_llvm_create_exception_handler(ExecutionContext *context, Value *result) +{ + void *buf = __qmljs_create_exception_handler(context); + *result = Value::fromInt32(setjmp(* static_cast(buf))); +} + +void __qmljs_llvm_delete_exception_handler(ExecutionContext *context) +{ + __qmljs_delete_exception_handler(context); +} + +void __qmljs_llvm_get_exception(ExecutionContext *context, Value *result) +{ + *result = __qmljs_get_exception(context); +} + +void __qmljs_llvm_foreach_iterator_object(ExecutionContext *context, Value *result, Value *in) +{ + *result = __qmljs_foreach_iterator_object(*in, context); +} + +void __qmljs_llvm_foreach_next_property_name(Value *result, Value *it) +{ + *result = __qmljs_foreach_next_property_name(*it); +} + +void __qmljs_llvm_get_this_object(ExecutionContext *ctx, Value *result) +{ + *result = __qmljs_get_thisObject(ctx); +} + +void __qmljs_llvm_delete_subscript(ExecutionContext *ctx, Value *result, Value *base, Value *index) +{ + *result = __qmljs_delete_subscript(ctx, *base, *index); +} + +void __qmljs_llvm_delete_member(ExecutionContext *ctx, Value *result, Value *base, String *name) +{ + *result = __qmljs_delete_member(ctx, *base, name); +} + +void __qmljs_llvm_delete_name(ExecutionContext *ctx, Value *result, String *name) +{ + *result = __qmljs_delete_name(ctx, name); +} + +} // extern "C" diff --git a/src/v4/moth/moth.pri b/src/v4/moth/moth.pri new file mode 100644 index 0000000000..73bd893286 --- /dev/null +++ b/src/v4/moth/moth.pri @@ -0,0 +1,13 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qv4isel_moth_p.h \ + $$PWD/qv4instr_moth_p.h \ + $$PWD/qv4vme_moth_p.h + +SOURCES += \ + $$PWD/qv4isel_moth.cpp \ + $$PWD/qv4instr_moth.cpp \ + $$PWD/qv4vme_moth.cpp + +#DEFINES += DO_TRACE_INSTR diff --git a/src/v4/moth/qv4instr_moth.cpp b/src/v4/moth/qv4instr_moth.cpp new file mode 100644 index 0000000000..a2bad39e00 --- /dev/null +++ b/src/v4/moth/qv4instr_moth.cpp @@ -0,0 +1,15 @@ +#include "qv4instr_moth_p.h" + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +int Instr::size(Type type) +{ +#define MOTH_RETURN_INSTR_SIZE(I, FMT) case I: return InstrMeta<(int)I>::Size; + switch (type) { + FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE) + default: return 0; + } +#undef MOTH_RETURN_INSTR_SIZE +} + diff --git a/src/v4/moth/qv4instr_moth_p.h b/src/v4/moth/qv4instr_moth_p.h new file mode 100644 index 0000000000..741ddba79d --- /dev/null +++ b/src/v4/moth/qv4instr_moth_p.h @@ -0,0 +1,514 @@ +#ifndef QV4INSTR_MOTH_P_H +#define QV4INSTR_MOTH_P_H + +#include +#include "qv4object.h" + +#define FOR_EACH_MOTH_INSTR(F) \ + F(Ret, ret) \ + F(LoadValue, loadValue) \ + F(LoadClosure, loadClosure) \ + F(MoveTemp, moveTemp) \ + F(LoadName, loadName) \ + F(StoreName, storeName) \ + F(LoadElement, loadElement) \ + F(StoreElement, storeElement) \ + F(LoadProperty, loadProperty) \ + F(StoreProperty, storeProperty) \ + F(Push, push) \ + F(CallValue, callValue) \ + F(CallProperty, callProperty) \ + F(CallElement, callElement) \ + F(CallActivationProperty, callActivationProperty) \ + F(CallBuiltinThrow, callBuiltinThrow) \ + F(CallBuiltinCreateExceptionHandler, callBuiltinCreateExceptionHandler) \ + F(CallBuiltinDeleteExceptionHandler, callBuiltinDeleteExceptionHandler) \ + F(CallBuiltinGetException, callBuiltinGetException) \ + F(CallBuiltinPushScope, callBuiltinPushScope) \ + F(CallBuiltinPopScope, callBuiltinPopScope) \ + F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ + F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \ + F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ + F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ + F(CallBuiltinDeleteName, callBuiltinDeleteName) \ + F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \ + F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \ + F(CallBuiltinTypeofName, callBuiltinTypeofName) \ + F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \ + F(CallBuiltinPostIncMember, callBuiltinPostIncMember) \ + F(CallBuiltinPostIncSubscript, callBuiltinPostIncSubscript) \ + F(CallBuiltinPostIncName, callBuiltinPostIncName) \ + F(CallBuiltinPostIncValue, callBuiltinPostIncValue) \ + F(CallBuiltinPostDecMember, callBuiltinPostDecMember) \ + F(CallBuiltinPostDecSubscript, callBuiltinPostDecSubscript) \ + F(CallBuiltinPostDecName, callBuiltinPostDecName) \ + F(CallBuiltinPostDecValue, callBuiltinPostDecValue) \ + F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ + F(CallBuiltinDefineGetterSetter, callBuiltinDefineGetterSetter) \ + F(CallBuiltinDefineProperty, callBuiltinDefineProperty) \ + F(CallBuiltinDefineArrayProperty, callBuiltinDefineArrayProperty) \ + F(CreateValue, createValue) \ + F(CreateProperty, createProperty) \ + F(CreateActivationProperty, createActivationProperty) \ + F(Jump, jump) \ + F(CJump, cjump) \ + F(Unop, unop) \ + F(Binop, binop) \ + F(LoadThis, loadThis) \ + F(InplaceElementOp, inplaceElementOp) \ + F(InplaceMemberOp, inplaceMemberOp) \ + F(InplaceNameOp, inplaceNameOp) + +#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) +# define MOTH_THREADED_INTERPRETER +#endif + +#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QQmlJS::Moth::Instr) - 1) + +#ifdef MOTH_THREADED_INTERPRETER +# define MOTH_INSTR_HEADER void *code; +#else +# define MOTH_INSTR_HEADER quint8 instructionType; +#endif + +#define MOTH_INSTR_ENUM(I, FMT) I, +#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QQmlJS::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK) + + +namespace QQmlJS { +namespace Moth { + +union Instr +{ + struct Param { + enum { + ValueType = 0, + ArgumentType = 1, + LocalType = 2, + TempType = 3 + }; + VM::Value value; + unsigned type : 2; + unsigned index : 30; + + bool isValue() const { return type == ValueType; } + bool isArgument() const { return type == ArgumentType; } + bool isLocal() const { return type == LocalType; } + bool isTemp() const { return type == TempType; } + + static Param createValue(const VM::Value &v) + { + Param p; + p.type = ValueType; + p.value = v; + return p; + } + + static Param createArgument(unsigned idx) + { + Param p; + p.type = ArgumentType; + p.index = idx; + return p; + } + + static Param createLocal(unsigned idx) + { + Param p; + p.type = LocalType; + p.index = idx; + return p; + } + + static Param createTemp(unsigned idx) + { + Param p; + p.type = TempType; + p.index = idx; + return p; + } + }; + + enum Type { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) + }; + + struct instr_common { + MOTH_INSTR_HEADER + }; + struct instr_ret { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_loadValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_moveTemp { + MOTH_INSTR_HEADER + Param source; + Param result; + }; + struct instr_loadClosure { + MOTH_INSTR_HEADER + VM::Function *value; + Param result; + }; + struct instr_loadName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_storeName { + MOTH_INSTR_HEADER + VM::String *name; + Param source; + }; + struct instr_loadProperty { + MOTH_INSTR_HEADER + VM::String *name; + Param base; + Param result; + }; + struct instr_storeProperty { + MOTH_INSTR_HEADER + VM::String *name; + Param base; + Param source; + }; + struct instr_loadElement { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_storeElement { + MOTH_INSTR_HEADER + Param base; + Param index; + Param source; + }; + struct instr_push { + MOTH_INSTR_HEADER + quint32 value; + }; + struct instr_callValue { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param dest; + Param result; + }; + struct instr_callProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + Param base; + Param result; + }; + struct instr_callElement { + MOTH_INSTR_HEADER + Param base; + Param index; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callActivationProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callBuiltinThrow { + MOTH_INSTR_HEADER + Param arg; + }; + struct instr_callBuiltinCreateExceptionHandler { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_callBuiltinDeleteExceptionHandler { + MOTH_INSTR_HEADER + }; + struct instr_callBuiltinGetException { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_callBuiltinPushScope { + MOTH_INSTR_HEADER + Param arg; + }; + struct instr_callBuiltinPopScope { + MOTH_INSTR_HEADER + }; + struct instr_callBuiltinForeachIteratorObject { + MOTH_INSTR_HEADER + Param arg; + Param result; + }; + struct instr_callBuiltinForeachNextPropertyName { + MOTH_INSTR_HEADER + Param arg; + Param result; + }; + struct instr_callBuiltinDeleteMember { + MOTH_INSTR_HEADER + VM::String *member; + Param base; + Param result; + }; + struct instr_callBuiltinDeleteSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinDeleteName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_callBuiltinTypeofMember { + MOTH_INSTR_HEADER + VM::String *member; + Param base; + Param result; + }; + struct instr_callBuiltinTypeofSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinTypeofName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_callBuiltinTypeofValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinPostIncMember { + MOTH_INSTR_HEADER + Param base; + VM::String *member; + Param result; + }; + struct instr_callBuiltinPostIncSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinPostIncName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_callBuiltinPostIncValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinPostDecMember { + MOTH_INSTR_HEADER + Param base; + VM::String *member; + Param result; + }; + struct instr_callBuiltinPostDecSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinPostDecName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_callBuiltinPostDecValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinDeclareVar { + MOTH_INSTR_HEADER + VM::String *varName; + bool isDeletable; + }; + struct instr_callBuiltinDefineGetterSetter { + MOTH_INSTR_HEADER + VM::String *name; + Param object; + Param getter; + Param setter; + }; + struct instr_callBuiltinDefineProperty { + MOTH_INSTR_HEADER + VM::String *name; + Param object; + Param value; + }; + struct instr_callBuiltinDefineArrayProperty { + MOTH_INSTR_HEADER + Param object; + Param value; + int index; + }; + struct instr_createValue { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param func; + Param result; + }; + struct instr_createProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + Param base; + Param result; + }; + struct instr_createActivationProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_jump { + MOTH_INSTR_HEADER + ptrdiff_t offset; + }; + struct instr_cjump { + MOTH_INSTR_HEADER + ptrdiff_t offset; + Param condition; + }; + struct instr_unop { + MOTH_INSTR_HEADER + VM::Value (*alu)(const VM::Value value, VM::ExecutionContext *ctx); + Param source; + Param result; + }; + struct instr_binop { + MOTH_INSTR_HEADER + VM::Value (*alu)(const VM::Value , const VM::Value, VM::ExecutionContext *); + Param lhs; + Param rhs; + Param result; + }; + struct instr_loadThis { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_inplaceElementOp { + MOTH_INSTR_HEADER + void (*alu)(VM::Value, VM::Value, VM::Value, VM::ExecutionContext *); + Param base; + Param index; + Param source; + }; + struct instr_inplaceMemberOp { + MOTH_INSTR_HEADER + void (*alu)(VM::Value, VM::Value, VM::String *, VM::ExecutionContext *); + VM::String *member; + Param base; + Param source; + }; + struct instr_inplaceNameOp { + MOTH_INSTR_HEADER + void (*alu)(VM::Value, VM::String *, VM::ExecutionContext *); + VM::String *name; + Param source; + }; + + instr_common common; + instr_ret ret; + instr_loadValue loadValue; + instr_moveTemp moveTemp; + instr_loadClosure loadClosure; + instr_loadName loadName; + instr_storeName storeName; + instr_loadElement loadElement; + instr_storeElement storeElement; + instr_loadProperty loadProperty; + instr_storeProperty storeProperty; + instr_push push; + instr_callValue callValue; + instr_callProperty callProperty; + instr_callElement callElement; + instr_callActivationProperty callActivationProperty; + instr_callBuiltinThrow callBuiltinThrow; + instr_callBuiltinCreateExceptionHandler callBuiltinCreateExceptionHandler; + instr_callBuiltinDeleteExceptionHandler callBuiltinDeleteExceptionHandler; + instr_callBuiltinGetException callBuiltinGetException; + instr_callBuiltinPushScope callBuiltinPushScope; + instr_callBuiltinPopScope callBuiltinPopScope; + instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; + instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName; + instr_callBuiltinDeleteMember callBuiltinDeleteMember; + instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; + instr_callBuiltinDeleteName callBuiltinDeleteName; + instr_callBuiltinTypeofMember callBuiltinTypeofMember; + instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript; + instr_callBuiltinTypeofName callBuiltinTypeofName; + instr_callBuiltinTypeofValue callBuiltinTypeofValue; + instr_callBuiltinPostIncMember callBuiltinPostIncMember; + instr_callBuiltinPostIncSubscript callBuiltinPostIncSubscript; + instr_callBuiltinPostIncName callBuiltinPostIncName; + instr_callBuiltinPostIncValue callBuiltinPostIncValue; + instr_callBuiltinPostDecMember callBuiltinPostDecMember; + instr_callBuiltinPostDecSubscript callBuiltinPostDecSubscript; + instr_callBuiltinPostDecName callBuiltinPostDecName; + instr_callBuiltinPostDecValue callBuiltinPostDecValue; + instr_callBuiltinDeclareVar callBuiltinDeclareVar; + instr_callBuiltinDefineGetterSetter callBuiltinDefineGetterSetter; + instr_callBuiltinDefineProperty callBuiltinDefineProperty; + instr_callBuiltinDefineArrayProperty callBuiltinDefineArrayProperty; + instr_createValue createValue; + instr_createProperty createProperty; + instr_createActivationProperty createActivationProperty; + instr_jump jump; + instr_cjump cjump; + instr_unop unop; + instr_binop binop; + instr_loadThis loadThis; + instr_inplaceElementOp inplaceElementOp; + instr_inplaceMemberOp inplaceMemberOp; + instr_inplaceNameOp inplaceNameOp; + + static int size(Type type); +}; + +template +struct InstrMeta { +}; + +#define MOTH_INSTR_META_TEMPLATE(I, FMT) \ + template<> struct InstrMeta<(int)Instr::I> { \ + enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \ + typedef Instr::instr_##FMT DataType; \ + static const DataType &data(const Instr &instr) { return instr.FMT; } \ + static void setData(Instr &instr, const DataType &v) { instr.FMT = v; } \ + }; +FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); +#undef MOTH_INSTR_META_TEMPLATE + +template +class InstrData : public InstrMeta::DataType +{ +}; + +} // namespace Moth +} // namespace QQmlJS + +#endif // QV4INSTR_MOTH_P_H diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp new file mode 100644 index 0000000000..5522be4c32 --- /dev/null +++ b/src/v4/moth/qv4isel_moth.cpp @@ -0,0 +1,975 @@ +#include "qv4isel_util_p.h" +#include "qv4isel_moth_p.h" +#include "qv4vme_moth_p.h" +#include "qv4functionobject.h" +#include "qv4regexpobject.h" +#include "debugging.h" + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +namespace { + +QTextStream qout(stderr, QIODevice::WriteOnly); + +//#define DEBUG_TEMP_COMPRESSION +#ifdef DEBUG_TEMP_COMPRESSION +# define DBTC(x) x +#else // !DEBUG_TEMP_COMPRESSION +# define DBTC(x) +#endif // DEBUG_TEMP_COMPRESSION +class CompressTemps: public IR::StmtVisitor, IR::ExprVisitor +{ +public: + void run(IR::Function *function) + { + DBTC(qDebug() << "starting on function" << (*function->name) << "with" << function->tempCount << "temps.";) + + _seenTemps.clear(); + _nextFree = 0; + _active.reserve(function->tempCount); + _localCount = function->locals.size(); + + QVector pinned; + foreach (IR::BasicBlock *block, function->basicBlocks) { + if (IR::Stmt *last = block->terminator()) { + const QBitArray &liveOut = last->d->liveOut; + for (int i = 0, ei = liveOut.size(); i < ei; ++i) { + if (liveOut.at(i) && !pinned.contains(i)) { + pinned.append(i); + add(i - _localCount, _nextFree); + } + } + } + } + _pinnedCount = _nextFree; + + int maxUsed = _nextFree; + + foreach (IR::BasicBlock *block, function->basicBlocks) { + DBTC(qDebug("L%d:", block->index)); + + for (int i = 0, ei = block->statements.size(); i < ei; ++i ) { + _currentStatement = block->statements[i]; + if (i == 0) + expireOld(); + + DBTC(_currentStatement->dump(qout);qout<d) + _currentStatement->accept(this); + } + maxUsed = std::max(maxUsed, _nextFree); + } + DBTC(qDebug() << "function" << (*function->name) << "uses" << maxUsed << "temps.";) + function->tempCount = maxUsed + _localCount; + } + +private: + virtual void visitConst(IR::Const *) {} + virtual void visitString(IR::String *) {} + virtual void visitRegExp(IR::RegExp *) {} + virtual void visitName(IR::Name *) {} + virtual void visitClosure(IR::Closure *) {} + virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); } + virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(IR::Member *e) { e->base->accept(this); } + virtual void visitExp(IR::Exp *s) { s->expr->accept(this); } + virtual void visitEnter(IR::Enter *) {} + virtual void visitLeave(IR::Leave *) {} + virtual void visitJump(IR::Jump *) {} + virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); } + virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } + + virtual void visitTemp(IR::Temp *e) { + if (_seenTemps.contains(e)) + return; + _seenTemps.insert(e); + + if (e->index < 0) + return; + if (e->index < _localCount) // don't optimise locals yet. + return; + + e->index = remap(e->index - _localCount) + _localCount; + } + + virtual void visitCall(IR::Call *e) { + e->base->accept(this); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(IR::New *e) { + e->base->accept(this); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitMove(IR::Move *s) { + s->target->accept(this); + s->source->accept(this); + } + + int remap(int tempIndex) { + for (ActiveTemps::const_iterator i = _active.begin(), ei = _active.end(); i < ei; ++i) { + if (i->first == tempIndex) { + DBTC(qDebug() << " lookup" << (tempIndex + _localCount) << "->" << (i->second + _localCount);) + return i->second; + } + } + + int firstFree = expireOld(); + add(tempIndex, firstFree); + return firstFree; + } + + void add(int tempIndex, int firstFree) { + if (_nextFree <= firstFree) + _nextFree = firstFree + 1; + _active.prepend(qMakePair(tempIndex, firstFree)); + DBTC(qDebug() << " add" << (tempIndex + _localCount) << "->" << (firstFree+ _localCount);) + } + + int expireOld() { + Q_ASSERT(_currentStatement->d); + + const QBitArray &liveIn = _currentStatement->d->liveIn; + QBitArray inUse(_nextFree); + int i = 0; + while (i < _active.size()) { + const QPair &p = _active[i]; + + if (p.second < _pinnedCount) { + inUse.setBit(p.second); + ++i; + continue; + } + + if (liveIn[p.first + _localCount]) { + inUse[p.second] = true; + ++i; + } else { + DBTC(qDebug() << " remove" << (p.first + _localCount) << "->" << (p.second + _localCount);) + _active.remove(i); + } + } + for (int i = 0, ei = inUse.size(); i < ei; ++i) + if (!inUse[i]) + return i; + return _nextFree; + } + +private: + typedef QVector > ActiveTemps; + ActiveTemps _active; + QSet _seenTemps; + IR::Stmt *_currentStatement; + int _localCount; + int _nextFree; + int _pinnedCount; +}; +#undef DBTC + +typedef VM::Value (*ALUFunction)(const VM::Value, const VM::Value, VM::ExecutionContext*); +inline ALUFunction aluOpFunction(IR::AluOp op) +{ + switch (op) { + case IR::OpInvalid: + return 0; + case IR::OpIfTrue: + return 0; + case IR::OpNot: + return 0; + case IR::OpUMinus: + return 0; + case IR::OpUPlus: + return 0; + case IR::OpCompl: + return 0; + case IR::OpBitAnd: + return VM::__qmljs_bit_and; + case IR::OpBitOr: + return VM::__qmljs_bit_or; + case IR::OpBitXor: + return VM::__qmljs_bit_xor; + case IR::OpAdd: + return VM::__qmljs_add; + case IR::OpSub: + return VM::__qmljs_sub; + case IR::OpMul: + return VM::__qmljs_mul; + case IR::OpDiv: + return VM::__qmljs_div; + case IR::OpMod: + return VM::__qmljs_mod; + case IR::OpLShift: + return VM::__qmljs_shl; + case IR::OpRShift: + return VM::__qmljs_shr; + case IR::OpURShift: + return VM::__qmljs_ushr; + case IR::OpGt: + return VM::__qmljs_gt; + case IR::OpLt: + return VM::__qmljs_lt; + case IR::OpGe: + return VM::__qmljs_ge; + case IR::OpLe: + return VM::__qmljs_le; + case IR::OpEqual: + return VM::__qmljs_eq; + case IR::OpNotEqual: + return VM::__qmljs_ne; + case IR::OpStrictEqual: + return VM::__qmljs_se; + case IR::OpStrictNotEqual: + return VM::__qmljs_sne; + case IR::OpInstanceof: + return VM::__qmljs_instanceof; + case IR::OpIn: + return VM::__qmljs_in; + case IR::OpAnd: + return 0; + case IR::OpOr: + return 0; + default: + assert(!"Unknown AluOp"); + return 0; + } +}; + +} // anonymous namespace + +InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module) + : EvalInstructionSelection(engine, module) + , _function(0) + , _vmFunction(0) + , _block(0) + , _codeStart(0) + , _codeNext(0) + , _codeEnd(0) +{ +} + +InstructionSelection::~InstructionSelection() +{ +} + +void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) +{ + IR::BasicBlock *block; + + QHash > patches; + QHash addrs; + + int codeSize = 4096; + uchar *codeStart = new uchar[codeSize]; + uchar *codeNext = codeStart; + uchar *codeEnd = codeStart + codeSize; + + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(block, _block); + qSwap(patches, _patches); + qSwap(addrs, _addrs); + qSwap(codeStart, _codeStart); + qSwap(codeNext, _codeNext); + qSwap(codeEnd, _codeEnd); + + // TODO: FIXME: fix the temp compression with the new temp index layout. +// CompressTemps().run(_function); + + int locals = frameSize(); + assert(locals >= 0); + + Instruction::Push push; + push.value = quint32(locals); + addInstruction(push); + + foreach (_block, _function->basicBlocks) { + _addrs.insert(_block, _codeNext - _codeStart); + + foreach (IR::Stmt *s, _block->statements) + s->accept(this); + } + + patchJumpAddresses(); + + _vmFunction->code = VME::exec; + _vmFunction->codeData = squeezeCode(); + + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(block, _block); + qSwap(patches, _patches); + qSwap(addrs, _addrs); + qSwap(codeStart, _codeStart); + qSwap(codeNext, _codeNext); + qSwap(codeEnd, _codeEnd); + + delete[] codeStart; +} + +void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) +{ + Instruction::CallValue call; + prepareCallArgs(args, call.argc, call.args); + call.dest = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) +{ + // call the property on the loaded base + Instruction::CallProperty call; + call.base = getParam(base); + call.name = identifier(name); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) +{ + // call the property on the loaded base + Instruction::CallElement call; + call.base = getParam(base); + call.index = getParam(index); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::constructActivationProperty(IR::Name *func, + IR::ExprList *args, + IR::Temp *result) +{ + Instruction::CreateActivationProperty create; + create.name = identifier(*func->id); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) +{ + Instruction::CreateProperty create; + create.base = getParam(base); + create.name = identifier(name); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) +{ + Instruction::CreateValue create; + create.func = getParam(value); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::loadThisObject(IR::Temp *temp) +{ + Instruction::LoadThis load; + load.result = getResultParam(temp); + addInstruction(load); +} + +void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) +{ + assert(sourceConst); + + Instruction::LoadValue load; + load.value = getParam(sourceConst); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) +{ + Instruction::LoadValue load; + load.value = Instr::Param::createValue(VM::Value::fromString(identifier(str))); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) +{ + Instruction::LoadValue load; + load.value = Instr::Param::createValue( + VM::Value::fromObject(engine()->newRegExpObject( + *sourceRegexp->value, + sourceRegexp->flags))); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) +{ + Instruction::LoadName load; + load.name = identifier(name); + load.result = getResultParam(temp); + addInstruction(load); +} + +void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) +{ + Instruction::StoreName store; + store.source = getParam(source); + store.name = identifier(targetName); + addInstruction(store); +} + +void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) +{ + VM::Function *vmFunc = vmFunction(closure->value); + assert(vmFunc); + Instruction::LoadClosure load; + load.value = vmFunc; + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::Temp *target) +{ + Instruction::LoadProperty load; + load.base = getParam(base); + load.name = identifier(name); + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +{ + Instruction::StoreProperty store; + store.base = getParam(targetBase); + store.name = identifier(targetName); + store.source = getParam(source); + addInstruction(store); +} + +void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) +{ + Instruction::LoadElement load; + load.base = getParam(base); + load.index = getParam(index); + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) +{ + Instruction::StoreElement store; + store.base = getParam(targetBase); + store.index = getParam(targetIndex); + store.source = getParam(source); + addInstruction(store); +} + +void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + Instruction::MoveTemp move; + move.source = getParam(sourceTemp); + move.result = getResultParam(targetTemp); + addInstruction(move); +} + +void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + VM::Value (*op)(const VM::Value value, VM::ExecutionContext *ctx) = 0; + switch (oper) { + case IR::OpIfTrue: assert(!"unreachable"); break; + case IR::OpNot: op = VM::__qmljs_not; break; + case IR::OpUMinus: op = VM::__qmljs_uminus; break; + case IR::OpUPlus: op = VM::__qmljs_uplus; break; + case IR::OpCompl: op = VM::__qmljs_compl; break; + case IR::OpIncrement: op = VM::__qmljs_increment; break; + case IR::OpDecrement: op = VM::__qmljs_decrement; break; + default: assert(!"unreachable"); break; + } // switch + + if (op) { + Instruction::Unop unop; + unop.alu = op; + unop.source = getParam(sourceTemp); + unop.result = getResultParam(targetTemp); + addInstruction(unop); + } else { + qWarning(" UNOP1"); + } +} + +void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) +{ + Instruction::Binop binop; + binop.alu = aluOpFunction(oper); + binop.lhs = getParam(leftSource); + binop.rhs = getParam(rightSource); + binop.result = getResultParam(target); + addInstruction(binop); +} + +void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) +{ + void (*op)(VM::Value value, VM::String *name, VM::ExecutionContext *ctx) = 0; + switch (oper) { + case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_name; break; + case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_name; break; + case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_name; break; + case IR::OpAdd: op = VM::__qmljs_inplace_add_name; break; + case IR::OpSub: op = VM::__qmljs_inplace_sub_name; break; + case IR::OpMul: op = VM::__qmljs_inplace_mul_name; break; + case IR::OpDiv: op = VM::__qmljs_inplace_div_name; break; + case IR::OpMod: op = VM::__qmljs_inplace_mod_name; break; + case IR::OpLShift: op = VM::__qmljs_inplace_shl_name; break; + case IR::OpRShift: op = VM::__qmljs_inplace_shr_name; break; + case IR::OpURShift: op = VM::__qmljs_inplace_ushr_name; break; + default: break; + } + + if (op) { + Instruction::InplaceNameOp ieo; + ieo.alu = op; + ieo.name = identifier(targetName); + ieo.source = getParam(sourceExpr); + addInstruction(ieo); + } +} + +void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) +{ + void (*op)(VM::Value base, VM::Value index, VM::Value value, VM::ExecutionContext *ctx) = 0; + switch (oper) { + case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_element; break; + case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_element; break; + case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_element; break; + case IR::OpAdd: op = VM::__qmljs_inplace_add_element; break; + case IR::OpSub: op = VM::__qmljs_inplace_sub_element; break; + case IR::OpMul: op = VM::__qmljs_inplace_mul_element; break; + case IR::OpDiv: op = VM::__qmljs_inplace_div_element; break; + case IR::OpMod: op = VM::__qmljs_inplace_mod_element; break; + case IR::OpLShift: op = VM::__qmljs_inplace_shl_element; break; + case IR::OpRShift: op = VM::__qmljs_inplace_shr_element; break; + case IR::OpURShift: op = VM::__qmljs_inplace_ushr_element; break; + default: break; + } + + Instruction::InplaceElementOp ieo; + ieo.alu = op; + ieo.base = getParam(targetBaseTemp); + ieo.index = getParam(targetIndexTemp); + ieo.source = getParam(sourceExpr); + addInstruction(ieo); +} + +void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +{ + void (*op)(VM::Value value, VM::Value base, VM::String *name, VM::ExecutionContext *ctx) = 0; + switch (oper) { + case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_member; break; + case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_member; break; + case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_member; break; + case IR::OpAdd: op = VM::__qmljs_inplace_add_member; break; + case IR::OpSub: op = VM::__qmljs_inplace_sub_member; break; + case IR::OpMul: op = VM::__qmljs_inplace_mul_member; break; + case IR::OpDiv: op = VM::__qmljs_inplace_div_member; break; + case IR::OpMod: op = VM::__qmljs_inplace_mod_member; break; + case IR::OpLShift: op = VM::__qmljs_inplace_shl_member; break; + case IR::OpRShift: op = VM::__qmljs_inplace_shr_member; break; + case IR::OpURShift: op = VM::__qmljs_inplace_ushr_member; break; + default: break; + } + + Instruction::InplaceMemberOp imo; + imo.alu = op; + imo.base = getParam(targetBase); + imo.member = identifier(targetName); + imo.source = getParam(source); + addInstruction(imo); +} + +void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 &args) +{ + bool singleArgIsTemp = false; + if (e && e->next == 0) { + // ok, only 1 argument in the call... + const int idx = e->expr->asTemp()->index; + // We can only pass a reference into the stack, which holds temps that + // are not arguments (idx >= 0) nor locals (idx >= localCound). + singleArgIsTemp = idx >= _function->locals.size(); + } + + if (singleArgIsTemp) { + // We pass single arguments as references to the stack, but only if it's not a local or an argument. + argc = 1; + args = e->expr->asTemp()->index - _function->locals.size(); + } else if (e) { + // We need to move all the temps into the function arg array + int argLocation = outgoingArgumentTempStart(); + assert(argLocation >= 0); + argc = 0; + args = argLocation; + while (e) { + Instruction::MoveTemp move; + move.source = getParam(e->expr); + move.result = Instr::Param::createTemp(argLocation); + addInstruction(move); + ++argLocation; + ++argc; + e = e->next; + } + } else { + argc = 0; + args = 0; + } +} + +void InstructionSelection::visitJump(IR::Jump *s) +{ + Instruction::Jump jump; + jump.offset = 0; + ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + + _patches[s->target].append(loc); +} + +void InstructionSelection::visitCJump(IR::CJump *s) +{ + Instr::Param condition; + if (IR::Temp *t = s->cond->asTemp()) { + condition = getResultParam(t); + } else if (IR::Binop *b = s->cond->asBinop()) { + condition = getResultParam(0); + Instruction::Binop binop; + binop.alu = aluOpFunction(b->op); + binop.lhs = getParam(b->left); + binop.rhs = getParam(b->right); + binop.result = condition; + addInstruction(binop); + } else { + Q_UNIMPLEMENTED(); + } + + Instruction::CJump jump; + jump.offset = 0; + jump.condition = condition; + ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + _patches[s->iftrue].append(trueLoc); + + if (_block->index + 1 != s->iffalse->index) { + Instruction::Jump jump; + jump.offset = 0; + ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + _patches[s->iffalse].append(falseLoc); + } +} + +void InstructionSelection::visitRet(IR::Ret *s) +{ + Instruction::Ret ret; + ret.result = getParam(s->expr); + addInstruction(ret); +} + +void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) +{ + Instruction::CallActivationProperty call; + call.name = identifier(*func->id); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinTypeofMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + Instruction::CallBuiltinTypeofSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinTypeofName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) +{ + Instruction::CallBuiltinTypeofValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinDeleteMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + Instruction::CallBuiltinDeleteSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinDeleteName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) +{ + Instruction::LoadValue load; + load.value = Instr::Param::createValue(VM::Value::fromBoolean(false)); + load.result = getResultParam(result); + addInstruction(load); +} + +void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinPostDecMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + Instruction::CallBuiltinPostDecSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinPostDecName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) +{ + Instruction::CallBuiltinPostDecValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinPostIncMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + Instruction::CallBuiltinPostIncSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinPostIncName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) +{ + Instruction::CallBuiltinPostIncValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinThrow(IR::Temp *arg) +{ + Instruction::CallBuiltinThrow call; + call.arg = getParam(arg); + addInstruction(call); +} + +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) +{ + Instruction::CallBuiltinCreateExceptionHandler call; + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteExceptionHandler() +{ + Instruction::CallBuiltinDeleteExceptionHandler call; + addInstruction(call); +} + +void InstructionSelection::callBuiltinGetException(IR::Temp *result) +{ + Instruction::CallBuiltinGetException call; + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) +{ + Instruction::CallBuiltinForeachIteratorObject call; + call.arg = getParam(arg); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) +{ + Instruction::CallBuiltinForeachNextPropertyName call; + call.arg = getParam(arg); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) +{ + Instruction::CallBuiltinPushScope call; + call.arg = getParam(arg); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPopScope() +{ + Instruction::CallBuiltinPopScope call; + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + Instruction::CallBuiltinDeclareVar call; + call.isDeletable = deletable; + call.varName = identifier(name); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) +{ + Instruction::CallBuiltinDefineGetterSetter call; + call.object = getParam(object); + call.name = identifier(name); + call.getter = getParam(getter); + call.setter = getParam(setter); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) +{ + Instruction::CallBuiltinDefineProperty call; + call.object = getParam(object); + call.name = identifier(name); + call.value = getParam(value); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) +{ + Instruction::CallBuiltinDefineArrayProperty call; + call.object = getParam(object); + call.index = index; + call.value = getParam(value); + addInstruction(call); +} + +ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) +{ +#ifdef MOTH_THREADED_INTERPRETER + instr.common.code = VME::instructionJumpTable()[static_cast(type)]; +#else + instr.common.instructionType = type; +#endif + + int instructionSize = Instr::size(type); + if (_codeEnd - _codeNext < instructionSize) { + int currSize = _codeEnd - _codeStart; + uchar *newCode = new uchar[currSize * 2]; + ::memset(newCode + currSize, 0, currSize); + ::memcpy(newCode, _codeStart, currSize); + _codeNext = _codeNext - _codeStart + newCode; + delete[] _codeStart; + _codeStart = newCode; + _codeEnd = _codeStart + currSize * 2; + } + + ::memcpy(_codeNext, reinterpret_cast(&instr), instructionSize); + ptrdiff_t ptrOffset = _codeNext - _codeStart; + _codeNext += instructionSize; + + return ptrOffset; +} + +void InstructionSelection::patchJumpAddresses() +{ + typedef QHash >::ConstIterator PatchIt; + for (PatchIt i = _patches.begin(), ei = _patches.end(); i != ei; ++i) { + Q_ASSERT(_addrs.contains(i.key())); + ptrdiff_t target = _addrs.value(i.key()); + + const QVector &patchList = i.value(); + for (int ii = 0, eii = patchList.count(); ii < eii; ++ii) { + ptrdiff_t patch = patchList.at(ii); + + *((ptrdiff_t *)(_codeStart + patch)) = target - patch; + } + } + + _patches.clear(); + _addrs.clear(); +} + +uchar *InstructionSelection::squeezeCode() const +{ + int codeSize = _codeNext - _codeStart; + uchar *squeezed = new uchar[codeSize]; + ::memcpy(squeezed, _codeStart, codeSize); + return squeezed; +} + +VM::String *InstructionSelection::identifier(const QString &s) +{ + VM::String *str = engine()->identifier(s); + _vmFunction->identifiers.append(str); + return str; +} diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h new file mode 100644 index 0000000000..0b93ea853f --- /dev/null +++ b/src/v4/moth/qv4isel_moth_p.h @@ -0,0 +1,168 @@ +#ifndef QV4ISEL_MOTH_P_H +#define QV4ISEL_MOTH_P_H + +#include "qv4global.h" +#include "qv4isel_p.h" +#include "qv4ir_p.h" +#include "qv4object.h" +#include "qv4instr_moth_p.h" + +namespace QQmlJS { +namespace Moth { + +class Q_V4_EXPORT InstructionSelection: + public IR::InstructionSelection, + public EvalInstructionSelection +{ +public: + InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); + ~InstructionSelection(); + + virtual void run(VM::Function *vmFunction, IR::Function *function); + +protected: + virtual void visitJump(IR::Jump *); + virtual void visitCJump(IR::CJump *); + virtual void visitRet(IR::Ret *); + + virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result); + virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteValue(IR::Temp *result); + virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinThrow(IR::Temp *arg); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); + virtual void callBuiltinDeleteExceptionHandler(); + virtual void callBuiltinGetException(IR::Temp *result); + virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinPushWithScope(IR::Temp *arg); + virtual void callBuiltinPopScope(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); + virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); + virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); + virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); + virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result); + virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); + virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); + virtual void loadThisObject(IR::Temp *temp); + virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp); + virtual void loadString(const QString &str, IR::Temp *targetTemp); + virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); + virtual void getActivationProperty(const QString &name, IR::Temp *temp); + virtual void setActivationProperty(IR::Expr *source, const QString &targetName); + virtual void initClosure(IR::Closure *closure, IR::Temp *target); + virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); + virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target); + virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); + virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); + virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); + virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + +private: + struct Instruction { +#define MOTH_INSTR_DATA_TYPEDEF(I, FMT) typedef InstrData I; + FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF) +#undef MOTH_INSTR_DATA_TYPEDEF + private: + Instruction(); + }; + + Instr::Param getParam(IR::Expr *e) + { + Q_ASSERT(e); + + typedef Instr::Param Param; + if (IR::Const *c = e->asConst()) { + return Param::createValue(convertToValue(c)); + } else if (IR::Temp *t = e->asTemp()) { + const int index = t->index; + if (index < 0) { + return Param::createArgument(-index - 1); + } else { + const int localCount = _function->locals.size(); + if (index < localCount) + return Param::createLocal(index); + else + return Param::createTemp(index - localCount); + } + } else { + Q_UNIMPLEMENTED(); + return Param(); + } + } + + Instr::Param getResultParam(IR::Temp *result) + { + if (result) + return getParam(result); + else + return Instr::Param::createTemp(scratchTempIndex()); + } + + void simpleMove(IR::Move *); + void prepareCallArgs(IR::ExprList *, quint32 &, quint32 &); + + int outgoingArgumentTempStart() const { return _function->tempCount - _function->locals.size(); } + int scratchTempIndex() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; } + int frameSize() const { return scratchTempIndex() + 1; } + + template + inline ptrdiff_t addInstruction(const InstrData &data); + ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); + void patchJumpAddresses(); + uchar *squeezeCode() const; + + VM::String *identifier(const QString &s); + + IR::Function *_function; + VM::Function *_vmFunction; + IR::BasicBlock *_block; + + QHash > _patches; + QHash _addrs; + + uchar *_codeStart; + uchar *_codeNext; + uchar *_codeEnd; +}; + +class Q_V4_EXPORT ISelFactory: public EvalISelFactory +{ +public: + virtual ~ISelFactory() {} + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module) + { return new InstructionSelection(engine, module); } +}; + +template +ptrdiff_t InstructionSelection::addInstruction(const InstrData &data) +{ + Instr genericInstr; + InstrMeta::setData(genericInstr, data); + return addInstructionHelper(static_cast(InstrT), genericInstr); +} + +} // namespace Moth +} // namespace QQmlJS + +#endif // QV4ISEL_MOTH_P_H diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp new file mode 100644 index 0000000000..a3ab1de46a --- /dev/null +++ b/src/v4/moth/qv4vme_moth.cpp @@ -0,0 +1,483 @@ +#include "qv4vme_moth_p.h" +#include "qv4instr_moth_p.h" +#include "qmljs_value.h" +#include "debugging.h" + +#include + +#include + +#ifdef DO_TRACE_INSTR +# define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); +# define TRACE(n, str, ...) { char buf[4096]; snprintf(buf, 4096, str, __VA_ARGS__); fprintf(stderr, " %s : %s\n", #n, buf); } +#else +# define TRACE_INSTR(I) +# define TRACE(n, str, ...) +#endif // DO_TRACE_INSTR + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +class FunctionState: public Debugging::FunctionState +{ +public: + FunctionState(QQmlJS::VM::ExecutionContext *context, const uchar **code) + : Debugging::FunctionState(context) + , stack(0) + , stackSize(0) + , code(code) + {} + + virtual VM::Value *temp(unsigned idx) { return stack + idx; } + + void setStack(VM::Value *stack, unsigned stackSize) + { this->stack = stack; this->stackSize = stackSize; } + +private: + VM::Value *stack; + unsigned stackSize; + const uchar **code; +}; + +#define MOTH_BEGIN_INSTR_COMMON(I) { \ + const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ + code += InstrMeta<(int)Instr::I>::Size; \ + Q_UNUSED(instr); \ + TRACE_INSTR(I) + +#ifdef MOTH_THREADED_INTERPRETER + +# define MOTH_BEGIN_INSTR(I) op_##I: \ + MOTH_BEGIN_INSTR_COMMON(I) + +# define MOTH_NEXT_INSTR(I) { \ + genericInstr = reinterpret_cast(code); \ + goto *genericInstr->common.code; \ + } + +# define MOTH_END_INSTR(I) } \ + genericInstr = reinterpret_cast(code); \ + goto *genericInstr->common.code; \ + +#else + +# define MOTH_BEGIN_INSTR(I) \ + case Instr::I: \ + MOTH_BEGIN_INSTR_COMMON(I) + +# define MOTH_NEXT_INSTR(I) { \ + break; \ + } + +# define MOTH_END_INSTR(I) } \ + break; + +#endif + +static inline VM::Value *getValueRef(QQmlJS::VM::ExecutionContext *context, + VM::Value* stack, + const Instr::Param ¶m +#if !defined(QT_NO_DEBUG) + , unsigned stackSize +#endif + ) +{ +#ifdef DO_TRACE_INSTR + if (param.isValue()) { + fprintf(stderr, " value\n"); + } else if (param.isArgument()) { + fprintf(stderr, " argument %d\n", param.index); + } else if (param.isLocal()) { + fprintf(stderr, " local %d\n", param.index); + } else if (param.isTemp()) { + fprintf(stderr, " temp %d\n", param.index); + } else { + Q_ASSERT(!"INVALID"); + } +#endif // DO_TRACE_INSTR + + if (param.isValue()) { + return const_cast(¶m.value); + } else if (param.isArgument()) { + const unsigned arg = param.index; + Q_ASSERT(arg >= 0); + Q_ASSERT((unsigned) arg < context->argumentCount); + Q_ASSERT(context->arguments); + return context->arguments + arg; + } else if (param.isLocal()) { + const unsigned index = param.index; + Q_ASSERT(index >= 0); + Q_ASSERT(index < context->variableCount()); + Q_ASSERT(context->locals); + return context->locals + index; + } else if (param.isTemp()) { + Q_ASSERT(param.index < stackSize); + return stack + param.index; + } else { + Q_UNIMPLEMENTED(); + return 0; + } +} + +#if defined(QT_NO_DEBUG) +# define VALUE(param) *getValueRef(context, stack, param) +# define VALUEPTR(param) getValueRef(context, stack, param) +#else +# define VALUE(param) *getValueRef(context, stack, param, stackSize) +# define VALUEPTR(param) getValueRef(context, stack, param, stackSize) +#endif + +VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code +#ifdef MOTH_THREADED_INTERPRETER + , void ***storeJumpTable +#endif + ) +{ +#ifdef DO_TRACE_INSTR + qDebug("Starting VME with context=%p and code=%p", context, code); +#endif // DO_TRACE_INSTR + +#ifdef MOTH_THREADED_INTERPRETER + if (storeJumpTable) { +#define MOTH_INSTR_ADDR(I, FMT) &&op_##I, + static void *jumpTable[] = { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) + }; +#undef MOTH_INSTR_ADDR + *storeJumpTable = jumpTable; + return VM::Value::undefinedValue(); + } +#endif + + VM::Value *stack = 0; + unsigned stackSize = 0; + FunctionState state(context, &code); + +#ifdef MOTH_THREADED_INTERPRETER + const Instr *genericInstr = reinterpret_cast(code); + goto *genericInstr->common.code; +#else + for (;;) { + const Instr *genericInstr = reinterpret_cast(code); + switch (genericInstr->common.instructionType) { +#endif + + MOTH_BEGIN_INSTR(MoveTemp) + VALUE(instr.result) = VALUE(instr.source); + MOTH_END_INSTR(MoveTemp) + + MOTH_BEGIN_INSTR(LoadValue) +// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); + VALUE(instr.result) = VALUE(instr.value); + MOTH_END_INSTR(LoadValue) + + MOTH_BEGIN_INSTR(LoadClosure) + VALUE(instr.result) = __qmljs_init_closure(instr.value, context); + MOTH_END_INSTR(LoadClosure) + + MOTH_BEGIN_INSTR(LoadName) + TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); + VALUE(instr.result) = __qmljs_get_activation_property(context, instr.name); + MOTH_END_INSTR(LoadName) + + MOTH_BEGIN_INSTR(StoreName) + TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); + __qmljs_set_activation_property(context, instr.name, VALUE(instr.source)); + MOTH_END_INSTR(StoreName) + + MOTH_BEGIN_INSTR(LoadElement) + VALUE(instr.result) = __qmljs_get_element(context, VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(StoreElement) + __qmljs_set_element(context, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); + MOTH_END_INSTR(StoreElement) + + MOTH_BEGIN_INSTR(LoadProperty) + VALUE(instr.result) = __qmljs_get_property(context, VALUE(instr.base), instr.name); + MOTH_END_INSTR(LoadProperty) + + MOTH_BEGIN_INSTR(StoreProperty) + __qmljs_set_property(context, VALUE(instr.base), instr.name, VALUE(instr.source)); + MOTH_END_INSTR(StoreProperty) + + MOTH_BEGIN_INSTR(Push) + TRACE(inline, "stack size: %u", instr.value); + stackSize = instr.value; + stack = static_cast(alloca(stackSize * sizeof(VM::Value))); + state.setStack(stack, stackSize); + MOTH_END_INSTR(Push) + + MOTH_BEGIN_INSTR(CallValue) +#ifdef DO_TRACE_INSTR + if (Debugging::Debugger *debugger = context->engine->debugger) { + if (VM::FunctionObject *o = (VALUE(instr.dest)).asFunctionObject()) { + if (Debugging::FunctionDebugInfo *info = debugger->debugInfo(o)) { + QString n = debugger->name(o); + std::cerr << "*** Call to \"" << (n.isNull() ? "" : qPrintable(n)) << "\" defined @" << info->startLine << ":" << info->startColumn << std::endl; + } + } + } +#endif // DO_TRACE_INSTR + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_call_value(context, VM::Value::undefinedValue(), VALUE(instr.dest), args, instr.argc); + MOTH_END_INSTR(CallValue) + + MOTH_BEGIN_INSTR(CallProperty) + TRACE(property name, "%s", qPrintable(instr.name->toQString())); + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_call_property(context, VALUE(instr.base), instr.name, args, instr.argc); + MOTH_END_INSTR(CallProperty) + + MOTH_BEGIN_INSTR(CallElement) + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_call_element(context, VALUE(instr.base), VALUE(instr.index), args, instr.argc); + MOTH_END_INSTR(CallProperty) + + MOTH_BEGIN_INSTR(CallActivationProperty) + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_call_activation_property(context, instr.name, args, instr.argc); + MOTH_END_INSTR(CallActivationProperty) + + MOTH_BEGIN_INSTR(CallBuiltinThrow) + __qmljs_builtin_throw(VALUE(instr.arg), context); + MOTH_END_INSTR(CallBuiltinThrow) + + MOTH_BEGIN_INSTR(CallBuiltinCreateExceptionHandler) + void *buf = __qmljs_create_exception_handler(context); + // The result is the only value we need from the instr to + // continue execution when an exception is caught. + VM::Value *result = getValueRef(context, stack, instr.result +#if !defined(QT_NO_DEBUG) + , stackSize +#endif + ); + int didThrow = setjmp(* static_cast(buf)); + // Two ways to come here: after a create, or after a throw. + if (didThrow) + // At this point, the interpreter state can be anything but + // valid, so first restore the state. + restoreState(context, result, code); + else + // Save the state and any variables we need when catching an + // exception, so we can restore the state at that point. + saveState(context, result, code); + *result = VM::Value::fromInt32(didThrow); + MOTH_END_INSTR(CallBuiltinCreateExceptionHandler) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteExceptionHandler) + __qmljs_delete_exception_handler(context); + MOTH_END_INSTR(CallBuiltinDeleteExceptionHandler) + + MOTH_BEGIN_INSTR(CallBuiltinGetException) + VALUE(instr.result) = __qmljs_get_exception(context); + MOTH_END_INSTR(CallBuiltinGetException) + + MOTH_BEGIN_INSTR(CallBuiltinPushScope) + context = __qmljs_builtin_push_with_scope(VALUE(instr.arg), context); + MOTH_END_INSTR(CallBuiltinPushScope) + + MOTH_BEGIN_INSTR(CallBuiltinPopScope) + context = __qmljs_builtin_pop_scope(context); + MOTH_END_INSTR(CallBuiltinPopScope) + + MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) + VALUE(instr.result) = __qmljs_foreach_iterator_object(VALUE(instr.arg), context); + MOTH_END_INSTR(CallBuiltinForeachIteratorObject) + + MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) + VALUE(instr.result) = __qmljs_foreach_next_property_name(VALUE(instr.arg)); + MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) + VALUE(instr.result) = __qmljs_delete_member(context, VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinDeleteMember) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) + VALUE(instr.result) = __qmljs_delete_subscript(context, VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(CallBuiltinDeleteSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteName) + VALUE(instr.result) = __qmljs_delete_name(context, instr.name); + MOTH_END_INSTR(CallBuiltinDeleteName) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) + VALUE(instr.result) = __qmljs_builtin_typeof_member(VALUE(instr.base), instr.member, context); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) + VALUE(instr.result) = __qmljs_builtin_typeof_element(VALUE(instr.base), VALUE(instr.index), context); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofName) + VALUE(instr.result) = __qmljs_builtin_typeof_name(instr.name, context); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) + VALUE(instr.result) = __qmljs_builtin_typeof(VALUE(instr.value), context); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncMember) + VALUE(instr.result) = __qmljs_builtin_post_increment_member(VALUE(instr.base), instr.member, context); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncSubscript) + VALUE(instr.result) = __qmljs_builtin_post_increment_element(VALUE(instr.base), VALUE(instr.index), context); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncName) + VALUE(instr.result) = __qmljs_builtin_post_increment_name(instr.name, context); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncValue) + VALUE(instr.result) = __qmljs_builtin_post_increment(VALUEPTR(instr.value), context); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecMember) + VALUE(instr.result) = __qmljs_builtin_post_decrement_member(VALUE(instr.base), instr.member, context); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecSubscript) + VALUE(instr.result) = __qmljs_builtin_post_decrement_element(VALUE(instr.base), VALUE(instr.index), context); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecName) + VALUE(instr.result) = __qmljs_builtin_post_decrement_name(instr.name, context); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecValue) + VALUE(instr.result) = __qmljs_builtin_post_decrement(VALUEPTR(instr.value), context); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) + __qmljs_builtin_declare_var(context, instr.isDeletable, instr.varName); + MOTH_END_INSTR(CallBuiltinDeclareVar) + + MOTH_BEGIN_INSTR(CallBuiltinDefineGetterSetter) + __qmljs_builtin_define_getter_setter(VALUE(instr.object), instr.name, VALUE(instr.getter), VALUE(instr.setter), context); + MOTH_END_INSTR(CallBuiltinDefineGetterSetter) + + MOTH_BEGIN_INSTR(CallBuiltinDefineProperty) + __qmljs_builtin_define_property(VALUE(instr.object), instr.name, VALUE(instr.value), context); + MOTH_END_INSTR(CallBuiltinDefineProperty) + + MOTH_BEGIN_INSTR(CallBuiltinDefineArrayProperty) + __qmljs_builtin_define_array_property(VALUE(instr.object), instr.index, VALUE(instr.value), context); + MOTH_END_INSTR(CallBuiltinDefineArrayProperty) + + MOTH_BEGIN_INSTR(CreateValue) + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_construct_value(context, VALUE(instr.func), args, instr.argc); + MOTH_END_INSTR(CreateValue) + + MOTH_BEGIN_INSTR(CreateProperty) + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_construct_property(context, VALUE(instr.base), instr.name, args, instr.argc); + MOTH_END_INSTR(CreateProperty) + + MOTH_BEGIN_INSTR(CreateActivationProperty) + TRACE(inline, "property name = %s, args = %d, argc = %d", instr.name->toQString().toUtf8().constData(), instr.args, instr.argc); + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); + MOTH_END_INSTR(CreateActivationProperty) + + MOTH_BEGIN_INSTR(Jump) + code = ((uchar *)&instr.offset) + instr.offset; + MOTH_END_INSTR(Jump) + + MOTH_BEGIN_INSTR(CJump) + uint cond = __qmljs_to_boolean(VALUE(instr.condition), context); + TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); + if (cond) + code = ((uchar *)&instr.offset) + instr.offset; + MOTH_END_INSTR(CJump) + + MOTH_BEGIN_INSTR(Unop) + VALUE(instr.result) = instr.alu(VALUE(instr.source), context); + MOTH_END_INSTR(Unop) + + MOTH_BEGIN_INSTR(Binop) + VALUE(instr.result) = instr.alu(VALUE(instr.lhs), VALUE(instr.rhs), context); + MOTH_END_INSTR(Binop) + + MOTH_BEGIN_INSTR(Ret) + VM::Value &result = VALUE(instr.result); +// TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); + return result; + MOTH_END_INSTR(Ret) + + MOTH_BEGIN_INSTR(LoadThis) + VALUE(instr.result) = __qmljs_get_thisObject(context); + MOTH_END_INSTR(LoadThis) + + MOTH_BEGIN_INSTR(InplaceElementOp) + instr.alu(VALUE(instr.base), + VALUE(instr.index), + VALUE(instr.source), + context); + MOTH_END_INSTR(InplaceElementOp) + + MOTH_BEGIN_INSTR(InplaceMemberOp) + instr.alu(VALUE(instr.source), + VALUE(instr.base), + instr.member, + context); + MOTH_END_INSTR(InplaceMemberOp) + + MOTH_BEGIN_INSTR(InplaceNameOp) + TRACE(name, "%s", instr.name->toQString().toUtf8().constData()); + instr.alu(VALUE(instr.source), + instr.name, + context); + MOTH_END_INSTR(InplaceNameOp) + +#ifdef MOTH_THREADED_INTERPRETER + // nothing to do +#else + default: + qFatal("QQmlJS::Moth::VME: Internal error - unknown instruction %d", genericInstr->common.instructionType); + break; + } + } +#endif + +} + +#ifdef MOTH_THREADED_INTERPRETER +void **VME::instructionJumpTable() +{ + static void **jumpTable = 0; + if (!jumpTable) { + VME dummy; + dummy(0, 0, &jumpTable); + } + return jumpTable; +} +#endif + +VM::Value VME::exec(VM::ExecutionContext *ctxt, const uchar *code) +{ + VME vme; + return vme(ctxt, code); +} + +void VME::restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code) +{ + VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); + target = handler.target; + code = handler.code; +} + +void VME::saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code) +{ + VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); + handler.target = target; + handler.code = code; +} diff --git a/src/v4/moth/qv4vme_moth_p.h b/src/v4/moth/qv4vme_moth_p.h new file mode 100644 index 0000000000..2fd877f7b9 --- /dev/null +++ b/src/v4/moth/qv4vme_moth_p.h @@ -0,0 +1,37 @@ +#ifndef QV4VME_MOTH_P_H +#define QV4VME_MOTH_P_H + +#include "qmljs_runtime.h" +#include "qv4instr_moth_p.h" + +namespace QQmlJS { +namespace VM { + struct Value; +} + +namespace Moth { + +class VME +{ +public: + static VM::Value exec(VM::ExecutionContext *, const uchar *); + + VM::Value operator()(QQmlJS::VM::ExecutionContext *, const uchar *code +#ifdef MOTH_THREADED_INTERPRETER + , void ***storeJumpTable = 0 +#endif + ); + +#ifdef MOTH_THREADED_INTERPRETER + static void **instructionJumpTable(); +#endif + +private: + static void restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code); + static void saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code); +}; + +} // namespace Moth +} // namespace QQmlJS + +#endif // QV4VME_MOTH_P_H diff --git a/src/v4/qmljs_engine.cpp b/src/v4/qmljs_engine.cpp new file mode 100644 index 0000000000..0b1b14b13e --- /dev/null +++ b/src/v4/qmljs_engine.cpp @@ -0,0 +1,493 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qv4mm.h" +#include +#include +#include +#include + +namespace QQmlJS { +namespace VM { + +ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) + : memoryManager(new QQmlJS::VM::MemoryManager) + , iselFactory(factory) + , debugger(0) + , globalObject(Value::nullValue()) + , globalCode(0) + , exception(Value::nullValue()) +{ + MemoryManager::GCBlocker gcBlocker(memoryManager); + + memoryManager->setExecutionEngine(this); + + rootContext = newContext(); + rootContext->init(this); + current = rootContext; + + id_length = identifier(QStringLiteral("length")); + id_prototype = identifier(QStringLiteral("prototype")); + id_constructor = identifier(QStringLiteral("constructor")); + id_arguments = identifier(QStringLiteral("arguments")); + id_caller = identifier(QStringLiteral("caller")); + id_this = identifier(QStringLiteral("this")); + id___proto__ = identifier(QStringLiteral("__proto__")); + id_enumerable = identifier(QStringLiteral("enumerable")); + id_configurable = identifier(QStringLiteral("configurable")); + id_writable = identifier(QStringLiteral("writable")); + id_value = identifier(QStringLiteral("value")); + id_get = identifier(QStringLiteral("get")); + id_set = identifier(QStringLiteral("set")); + id_eval = identifier(QStringLiteral("eval")); + + objectPrototype = new (memoryManager) ObjectPrototype(); + stringPrototype = new (memoryManager) StringPrototype(rootContext); + numberPrototype = new (memoryManager) NumberPrototype(); + booleanPrototype = new (memoryManager) BooleanPrototype(); + arrayPrototype = new (memoryManager) ArrayPrototype(rootContext); + datePrototype = new (memoryManager) DatePrototype(); + functionPrototype = new (memoryManager) FunctionPrototype(rootContext); + regExpPrototype = new (memoryManager) RegExpPrototype(this); + errorPrototype = new (memoryManager) ErrorPrototype(this); + evalErrorPrototype = new (memoryManager) EvalErrorPrototype(rootContext); + rangeErrorPrototype = new (memoryManager) RangeErrorPrototype(rootContext); + referenceErrorPrototype = new (memoryManager) ReferenceErrorPrototype(rootContext); + syntaxErrorPrototype = new (memoryManager) SyntaxErrorPrototype(rootContext); + typeErrorPrototype = new (memoryManager) TypeErrorPrototype(rootContext); + uRIErrorPrototype = new (memoryManager) URIErrorPrototype(rootContext); + + stringPrototype->prototype = objectPrototype; + numberPrototype->prototype = objectPrototype; + booleanPrototype->prototype = objectPrototype; + arrayPrototype->prototype = objectPrototype; + datePrototype->prototype = objectPrototype; + functionPrototype->prototype = objectPrototype; + regExpPrototype->prototype = objectPrototype; + errorPrototype->prototype = objectPrototype; + evalErrorPrototype->prototype = objectPrototype; + rangeErrorPrototype->prototype = objectPrototype; + referenceErrorPrototype->prototype = objectPrototype; + syntaxErrorPrototype->prototype = objectPrototype; + typeErrorPrototype->prototype = objectPrototype; + uRIErrorPrototype->prototype = objectPrototype; + + objectCtor = Value::fromObject(new (memoryManager) ObjectCtor(rootContext)); + stringCtor = Value::fromObject(new (memoryManager) StringCtor(rootContext)); + numberCtor = Value::fromObject(new (memoryManager) NumberCtor(rootContext)); + booleanCtor = Value::fromObject(new (memoryManager) BooleanCtor(rootContext)); + arrayCtor = Value::fromObject(new (memoryManager) ArrayCtor(rootContext)); + functionCtor = Value::fromObject(new (memoryManager) FunctionCtor(rootContext)); + dateCtor = Value::fromObject(new (memoryManager) DateCtor(rootContext)); + regExpCtor = Value::fromObject(new (memoryManager) RegExpCtor(rootContext)); + errorCtor = Value::fromObject(new (memoryManager) ErrorCtor(rootContext)); + evalErrorCtor = Value::fromObject(new (memoryManager) EvalErrorCtor(rootContext)); + rangeErrorCtor = Value::fromObject(new (memoryManager) RangeErrorCtor(rootContext)); + referenceErrorCtor = Value::fromObject(new (memoryManager) ReferenceErrorCtor(rootContext)); + syntaxErrorCtor = Value::fromObject(new (memoryManager) SyntaxErrorCtor(rootContext)); + typeErrorCtor = Value::fromObject(new (memoryManager) TypeErrorCtor(rootContext)); + uRIErrorCtor = Value::fromObject(new (memoryManager) URIErrorCtor(rootContext)); + + objectCtor.objectValue()->prototype = functionPrototype; + stringCtor.objectValue()->prototype = functionPrototype; + numberCtor.objectValue()->prototype = functionPrototype; + booleanCtor.objectValue()->prototype = functionPrototype; + arrayCtor.objectValue()->prototype = functionPrototype; + functionCtor.objectValue()->prototype = functionPrototype; + dateCtor.objectValue()->prototype = functionPrototype; + regExpCtor.objectValue()->prototype = functionPrototype; + errorCtor.objectValue()->prototype = functionPrototype; + evalErrorCtor.objectValue()->prototype = functionPrototype; + rangeErrorCtor.objectValue()->prototype = functionPrototype; + referenceErrorCtor.objectValue()->prototype = functionPrototype; + syntaxErrorCtor.objectValue()->prototype = functionPrototype; + typeErrorCtor.objectValue()->prototype = functionPrototype; + uRIErrorCtor.objectValue()->prototype = functionPrototype; + + objectPrototype->init(rootContext, objectCtor); + stringPrototype->init(rootContext, stringCtor); + numberPrototype->init(rootContext, numberCtor); + booleanPrototype->init(rootContext, booleanCtor); + arrayPrototype->init(rootContext, arrayCtor); + datePrototype->init(rootContext, dateCtor); + functionPrototype->init(rootContext, functionCtor); + regExpPrototype->init(rootContext, regExpCtor); + errorPrototype->init(rootContext, errorCtor); + evalErrorPrototype->init(rootContext, evalErrorCtor); + rangeErrorPrototype->init(rootContext, rangeErrorCtor); + referenceErrorPrototype->init(rootContext, referenceErrorCtor); + syntaxErrorPrototype->init(rootContext, syntaxErrorCtor); + typeErrorPrototype->init(rootContext, typeErrorCtor); + uRIErrorPrototype->init(rootContext, uRIErrorCtor); + + // + // set up the global object + // + VM::Object *glo = newObject(/*rootContext*/); + globalObject = Value::fromObject(glo); + rootContext->activation = glo; + rootContext->thisObject = Value::fromObject(glo); + + glo->defineDefaultProperty(rootContext, QStringLiteral("Object"), objectCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("String"), stringCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("Number"), numberCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("Boolean"), booleanCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("Array"), arrayCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("Function"), functionCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("Date"), dateCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("RegExp"), regExpCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("Error"), errorCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("EvalError"), evalErrorCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("RangeError"), rangeErrorCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("ReferenceError"), referenceErrorCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("SyntaxError"), syntaxErrorCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("TypeError"), typeErrorCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("URIError"), uRIErrorCtor); + glo->defineDefaultProperty(rootContext, QStringLiteral("Math"), Value::fromObject(newMathObject(rootContext))); + glo->defineDefaultProperty(rootContext, QStringLiteral("JSON"), Value::fromObject(new (memoryManager) JsonObject(rootContext))); + + glo->defineReadonlyProperty(this, QStringLiteral("undefined"), Value::undefinedValue()); + glo->defineReadonlyProperty(this, QStringLiteral("NaN"), Value::fromDouble(nan(""))); + glo->defineReadonlyProperty(this, QStringLiteral("Infinity"), Value::fromDouble(INFINITY)); + + evalFunction = new (memoryManager) EvalFunction(rootContext); + glo->defineDefaultProperty(rootContext, QStringLiteral("eval"), Value::fromObject(evalFunction)); + + glo->defineDefaultProperty(rootContext, QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); + glo->defineDefaultProperty(rootContext, QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("escape"), GlobalFunctions::method_escape, 1); + glo->defineDefaultProperty(rootContext, QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1); +} + +ExecutionEngine::~ExecutionEngine() +{ + delete globalObject.asObject(); + rootContext->destroy(); + delete rootContext; + qDeleteAll(functions); + delete memoryManager; +} + +ExecutionContext *ExecutionEngine::newContext() +{ + return new ExecutionContext(); +} + +String *ExecutionEngine::identifier(const QString &s) +{ + return new (memoryManager) String(s); +} + +Function *ExecutionEngine::newFunction(const QString &name) +{ + VM::Function *f = new VM::Function(identifier(name)); + functions.append(f); + return f; +} + +FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) +{ + BuiltinFunctionOld *f = new (memoryManager) BuiltinFunctionOld(scope, name, code); + return f; +} + +FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)) +{ + BuiltinFunction *f = new (memoryManager) BuiltinFunction(scope, name, code); + return f; +} + +FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, VM::Function *function) +{ + assert(function); + + ScriptFunction *f = new (memoryManager) ScriptFunction(scope, function); + return f; +} + +BoundFunction *ExecutionEngine::newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) +{ + assert(target); + + BoundFunction *f = new (memoryManager) BoundFunction(scope, target, boundThis, boundArgs); + return f; +} + + +Object *ExecutionEngine::newObject() +{ + Object *object = new (memoryManager) Object(); + object->prototype = objectPrototype; + return object; +} + +FunctionObject *ExecutionEngine::newObjectCtor(ExecutionContext *ctx) +{ + return new (memoryManager) ObjectCtor(ctx); +} + +String *ExecutionEngine::newString(const QString &s) +{ + return new (memoryManager) String(s); +} + +Object *ExecutionEngine::newStringObject(ExecutionContext *ctx, const Value &value) +{ + StringObject *object = new (memoryManager) StringObject(ctx, value); + object->prototype = stringPrototype; + return object; +} + +FunctionObject *ExecutionEngine::newStringCtor(ExecutionContext *ctx) +{ + return new (memoryManager) StringCtor(ctx); +} + +Object *ExecutionEngine::newNumberObject(const Value &value) +{ + NumberObject *object = new (memoryManager) NumberObject(value); + object->prototype = numberPrototype; + return object; +} + +FunctionObject *ExecutionEngine::newNumberCtor(ExecutionContext *ctx) +{ + return new (memoryManager) NumberCtor(ctx); +} + +Object *ExecutionEngine::newBooleanObject(const Value &value) +{ + Object *object = new (memoryManager) BooleanObject(value); + object->prototype = booleanPrototype; + return object; +} + +FunctionObject *ExecutionEngine::newBooleanCtor(ExecutionContext *ctx) +{ + return new (memoryManager) BooleanCtor(ctx); +} + +Object *ExecutionEngine::newFunctionObject(ExecutionContext *ctx) +{ + Object *object = new (memoryManager) FunctionObject(ctx); + object->prototype = functionPrototype; + return object; +} + +ArrayObject *ExecutionEngine::newArrayObject(ExecutionContext *ctx) +{ + ArrayObject *object = new (memoryManager) ArrayObject(ctx); + object->prototype = arrayPrototype; + return object; +} + +ArrayObject *ExecutionEngine::newArrayObject(ExecutionContext *ctx, const Array &value) +{ + ArrayObject *object = new (memoryManager) ArrayObject(ctx, value); + object->prototype = arrayPrototype; + return object; +} + +FunctionObject *ExecutionEngine::newArrayCtor(ExecutionContext *ctx) +{ + return new (memoryManager) ArrayCtor(ctx); +} + +Object *ExecutionEngine::newDateObject(const Value &value) +{ + Object *object = new (memoryManager) DateObject(value); + object->prototype = datePrototype; + return object; +} + +FunctionObject *ExecutionEngine::newDateCtor(ExecutionContext *ctx) +{ + return new (memoryManager) DateCtor(ctx); +} + +RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) +{ + bool global = (flags & IR::RegExp::RegExp_Global); + bool ignoreCase = false; + bool multiline = false; + if (flags & IR::RegExp::RegExp_IgnoreCase) + ignoreCase = true; + if (flags & IR::RegExp::RegExp_Multiline) + multiline = true; + + return newRegExpObject(RegExp::create(this, pattern, ignoreCase, multiline), global); +} + +RegExpObject *ExecutionEngine::newRegExpObject(PassRefPtr re, bool global) +{ + RegExpObject *object = new (memoryManager) RegExpObject(this, re, global); + object->prototype = regExpPrototype; + return object; +} + +FunctionObject *ExecutionEngine::newRegExpCtor(ExecutionContext *ctx) +{ + return new (memoryManager) RegExpCtor(ctx); +} + +Object *ExecutionEngine::newErrorObject(const Value &value) +{ + ErrorObject *object = new (memoryManager) ErrorObject(this, value); + object->prototype = errorPrototype; + return object; +} + +Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) +{ + return new (memoryManager) SyntaxErrorObject(ctx, message); +} + +Object *ExecutionEngine::newReferenceErrorObject(ExecutionContext *ctx, const QString &message) +{ + return new (memoryManager) ReferenceErrorObject(ctx, message); +} + +Object *ExecutionEngine::newTypeErrorObject(ExecutionContext *ctx, const QString &message) +{ + return new (memoryManager) TypeErrorObject(ctx, message); +} + +Object *ExecutionEngine::newRangeErrorObject(ExecutionContext *ctx, const QString &message) +{ + return new (memoryManager) RangeErrorObject(ctx, message); +} + +Object *ExecutionEngine::newURIErrorObject(ExecutionContext *ctx, Value message) +{ + return new (memoryManager) URIErrorObject(ctx, message); +} + +Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) +{ + MathObject *object = new (memoryManager) MathObject(ctx); + object->prototype = objectPrototype; + return object; +} + +Object *ExecutionEngine::newActivationObject() +{ + return new (memoryManager) Object(); +} + +Object *ExecutionEngine::newForEachIteratorObject(ExecutionContext *ctx, Object *o) +{ + return new (memoryManager) ForEachIteratorObject(ctx, o); +} + +void ExecutionEngine::requireArgumentsAccessors(int n) +{ + if (n <= argumentsAccessors.size()) + return; + + uint oldSize = argumentsAccessors.size(); + argumentsAccessors.resize(n); + for (int i = oldSize; i < n; ++i) { + FunctionObject *get = new (memoryManager) ArgumentsGetterFunction(rootContext, i); + get->prototype = functionPrototype; + FunctionObject *set = new (memoryManager) ArgumentsSetterFunction(rootContext, i); + set->prototype = functionPrototype; + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(get, set); + pd.configurable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + argumentsAccessors[i] = pd; + } +} + +void ExecutionEngine::markObjects() +{ + globalObject.mark(); + + if (globalCode) + globalCode->mark(); + + exception.mark(); + + for (int i = 0; i < argumentsAccessors.size(); ++i) { + const PropertyDescriptor &pd = argumentsAccessors.at(i); + pd.get->mark(); + pd.set->mark(); + } + + + for (int i = 0; i < functions.size(); ++i) + functions.at(i)->mark(); + + id_length->mark(); + id_prototype->mark(); + id_constructor->mark(); + id_arguments->mark(); + id_caller->mark(); + id_this->mark(); + id___proto__->mark(); + id_enumerable->mark(); + id_configurable->mark(); + id_writable->mark(); + id_value->mark(); + id_get->mark(); + id_set->mark(); + id_eval->mark(); +} + +} // namespace VM +} // namespace QQmlJS diff --git a/src/v4/qmljs_engine.h b/src/v4/qmljs_engine.h new file mode 100644 index 0000000000..f7416880a2 --- /dev/null +++ b/src/v4/qmljs_engine.h @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_ENGINE_H +#define QMLJS_ENGINE_H + +#include "qv4global.h" +#include "qv4isel_p.h" +#include "qv4object.h" +#include "qmljs_environment.h" +#include + +#include +#include + +namespace QQmlJS { + +namespace Debugging { +class Debugger; +} // namespace Debugging + +namespace VM { + +struct Value; +class Array; +struct Function; +struct Object; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct BoundFunction; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct ExecutionEngine; +class MemoryManager; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct RegExpPrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; +struct EvalFunction; + +class RegExp; + +struct Q_V4_EXPORT ExecutionEngine +{ + MemoryManager *memoryManager; + EvalISelFactory *iselFactory; + ExecutionContext *current; + ExecutionContext *rootContext; + WTF::BumpPointerAllocator bumperPointerAllocator; // Used by Yarr Regex engine. + + Debugging::Debugger *debugger; + + Value globalObject; + + VM::Function *globalCode; + + Value objectCtor; + Value stringCtor; + Value numberCtor; + Value booleanCtor; + Value arrayCtor; + Value functionCtor; + Value dateCtor; + Value regExpCtor; + Value errorCtor; + Value evalErrorCtor; + Value rangeErrorCtor; + Value referenceErrorCtor; + Value syntaxErrorCtor; + Value typeErrorCtor; + Value uRIErrorCtor; + + ObjectPrototype *objectPrototype; + StringPrototype *stringPrototype; + NumberPrototype *numberPrototype; + BooleanPrototype *booleanPrototype; + ArrayPrototype *arrayPrototype; + FunctionPrototype *functionPrototype; + DatePrototype *datePrototype; + RegExpPrototype *regExpPrototype; + ErrorPrototype *errorPrototype; + EvalErrorPrototype *evalErrorPrototype; + RangeErrorPrototype *rangeErrorPrototype; + ReferenceErrorPrototype *referenceErrorPrototype; + SyntaxErrorPrototype *syntaxErrorPrototype; + TypeErrorPrototype *typeErrorPrototype; + URIErrorPrototype *uRIErrorPrototype; + + EvalFunction *evalFunction; + + QVector argumentsAccessors; + + String *id_length; + String *id_prototype; + String *id_constructor; + String *id_arguments; + String *id_caller; + String *id_this; + String *id___proto__; + String *id_enumerable; + String *id_configurable; + String *id_writable; + String *id_value; + String *id_get; + String *id_set; + String *id_eval; + + struct ExceptionHandler { + ExecutionContext *context; + const uchar *code; // Interpreter state + Value *target; // Interpreter state + jmp_buf stackFrame; + }; + + QVector unwindStack; + Value exception; + + QVector functions; + + ExecutionEngine(EvalISelFactory *iselFactory); + ~ExecutionEngine(); + + ExecutionContext *newContext(); + + String *identifier(const QString &s); + + VM::Function *newFunction(const QString &name); + + FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); + FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)); + FunctionObject *newScriptFunction(ExecutionContext *scope, VM::Function *function); + BoundFunction *newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); + + Object *newObject(); + FunctionObject *newObjectCtor(ExecutionContext *ctx); + + String *newString(const QString &s); + Object *newStringObject(ExecutionContext *ctx, const Value &value); + FunctionObject *newStringCtor(ExecutionContext *ctx); + + Object *newNumberObject(const Value &value); + FunctionObject *newNumberCtor(ExecutionContext *ctx); + + Object *newBooleanObject(const Value &value); + FunctionObject *newBooleanCtor(ExecutionContext *ctx); + + Object *newFunctionObject(ExecutionContext *ctx); + + ArrayObject *newArrayObject(ExecutionContext *ctx); + ArrayObject *newArrayObject(ExecutionContext *ctx, const Array &value); + FunctionObject *newArrayCtor(ExecutionContext *ctx); + + Object *newDateObject(const Value &value); + FunctionObject *newDateCtor(ExecutionContext *ctx); + + RegExpObject *newRegExpObject(const QString &pattern, int flags); + RegExpObject *newRegExpObject(PassRefPtr re, bool global); + FunctionObject *newRegExpCtor(ExecutionContext *ctx); + + Object *newErrorObject(const Value &value); + Object *newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message); + Object *newReferenceErrorObject(ExecutionContext *ctx, const QString &message); + Object *newTypeErrorObject(ExecutionContext *ctx, const QString &message); + Object *newRangeErrorObject(ExecutionContext *ctx, const QString &message); + Object *newURIErrorObject(ExecutionContext *ctx, Value message); + + Object *newMathObject(ExecutionContext *ctx); + Object *newActivationObject(); + + Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o); + + void requireArgumentsAccessors(int n); + + void markObjects(); +}; + +} // namespace VM +} // namespace QQmlJS + +#endif diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp new file mode 100644 index 0000000000..9042a435a0 --- /dev/null +++ b/src/v4/qmljs_environment.cpp @@ -0,0 +1,564 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "debugging.h" +#include +#include +#include +#include "qv4mm.h" +#include + +namespace QQmlJS { +namespace VM { + +DiagnosticMessage::DiagnosticMessage() + : offset(0) + , length(0) + , startLine(0) + , startColumn(0) + , type(0) + , next(0) +{} + +DiagnosticMessage::~DiagnosticMessage() +{ + delete next; +} + +String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const +{ + QString msg; + if (!fileName.isEmpty()) + msg = fileName + QLatin1Char(':'); + msg += QString::number(startLine) + QLatin1Char(':') + QString::number(startColumn) + QLatin1String(": "); + if (type == QQmlJS::VM::DiagnosticMessage::Error) + msg += QLatin1String("error"); + else + msg += QLatin1String("warning"); + msg += ": " + message; + + return ctx->engine->newString(msg); +} + +bool ExecutionContext::hasBinding(String *name) const +{ + if (!function) + return false; + + for (unsigned int i = 0; i < function->varCount; ++i) { + if (function->varList[i]->isEqualTo(name)) + return true; + } + for (unsigned int i = 0; i < function->formalParameterCount; ++i) { + if (function->formalParameterList[i]->isEqualTo(name)) + return true; + } + if (activation) + return activation->__hasProperty__(this, name); + return false; +} + +void ExecutionContext::createMutableBinding(String *name, bool deletable) +{ + if (!activation) + activation = engine->newActivationObject(); + + if (activation->__hasProperty__(this, name)) + return; + PropertyDescriptor desc; + desc.value = Value::undefinedValue(); + desc.type = PropertyDescriptor::Data; + desc.configurable = deletable ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; + desc.writable = PropertyDescriptor::Enabled; + desc.enumberable = PropertyDescriptor::Enabled; + activation->__defineOwnProperty__(this, name, &desc); +} + +bool ExecutionContext::setMutableBinding(ExecutionContext *scope, String *name, Value value) +{ + // ### throw if scope->strict is true, and it would change an immutable binding + if (function) { + for (unsigned int i = 0; i < function->varCount; ++i) + if (function->varList[i]->isEqualTo(name)) { + locals[i] = value; + return true; + } + for (int i = (int)function->formalParameterCount - 1; i >= 0; --i) + if (function->formalParameterList[i]->isEqualTo(name)) { + arguments[i] = value; + return true; + } + } + + if (activation && activation->__hasProperty__(scope, name)) { + activation->__put__(scope, name, value); + return true; + } + + return false; +} + +Value ExecutionContext::getBindingValue(ExecutionContext *scope, String *name, bool strict) const +{ + Q_UNUSED(strict); + assert(function); + + if (function) { + for (unsigned int i = 0; i < function->varCount; ++i) + if (function->varList[i]->isEqualTo(name)) + return locals[i]; + for (int i = (int)function->formalParameterCount - 1; i >= 0; --i) + if (function->formalParameterList[i]->isEqualTo(name)) + return arguments[i]; + } + + if (activation) { + bool hasProperty = false; + Value v = activation->__get__(scope, name, &hasProperty); + if (hasProperty) + return v; + } + assert(false); +} + +bool ExecutionContext::deleteBinding(ExecutionContext *scope, String *name) +{ + if (activation) + activation->__delete__(scope, name); + + if (scope->strictMode) + __qmljs_throw_type_error(scope); + return false; +} + +ExecutionContext *ExecutionContext::createWithScope(Object *with) +{ + ExecutionContext *withCtx = engine->newContext(); + withCtx->init(this, with); + engine->current = withCtx; + return withCtx; +} + +ExecutionContext *ExecutionContext::popScope() +{ + assert(engine->current == this); + assert(withObject != 0); + + engine->current = parent; + parent = 0; + return engine->current; +} + +String * const *ExecutionContext::formals() const +{ + return function ? function->formalParameterList : 0; +} + +unsigned int ExecutionContext::formalCount() const +{ + return function ? function->formalParameterCount : 0; +} + +String * const *ExecutionContext::variables() const +{ + return function ? function->varList : 0; +} + +unsigned int ExecutionContext::variableCount() const +{ + return function ? function->varCount : 0; +} + + +void ExecutionContext::init(ExecutionEngine *eng) +{ + engine = eng; + parent = 0; + outer = 0; + thisObject = eng->globalObject; + + function = 0; + arguments = 0; + argumentCount = 0; + locals = 0; + strictMode = false; + activation = 0; + withObject = 0; + + eng->exception = Value::undefinedValue(); +} + +void ExecutionContext::init(ExecutionContext *p, Object *with) +{ + engine = p->engine; + parent = p; + outer = p; + thisObject = p->thisObject; + + function = 0; + arguments = 0; + argumentCount = 0; + locals = 0; + strictMode = false; + activation = 0; + withObject = with; +} + +void ExecutionContext::destroy() +{ + delete[] arguments; + delete[] locals; +} + +bool ExecutionContext::deleteProperty(String *name) +{ + bool hasWith = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->withObject) { + hasWith = true; + if (ctx->withObject->__hasProperty__(this, name)) + return ctx->withObject->__delete__(this, name); + } else { + if (ctx->activation && ctx->activation->__hasProperty__(this, name)) + return ctx->activation->__delete__(this, name); + } + if (FunctionObject *f = ctx->function) { + if (f->needsActivation || hasWith) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return false; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return false; + } + } + } + if (strictMode) + throwSyntaxError(0); + return true; +} + +void ExecutionContext::mark() +{ + thisObject.mark(); + if (function) + function->mark(); + for (unsigned arg = 0, lastArg = formalCount(); arg < lastArg; ++arg) + arguments[arg].mark(); + for (unsigned local = 0, lastLocal = variableCount(); local < lastLocal; ++local) + locals[local].mark(); + if (activation) + activation->mark(); + if (withObject) + withObject->mark(); +} + +void ExecutionContext::setProperty(String *name, Value value) +{ +// qDebug() << "=== SetProperty" << value.toString(this)->toQString(); + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (Object *w = ctx->withObject) { +// qDebug() << ctx << "hasWith"; + if (w->__hasProperty__(ctx, name)) { +// qDebug() << " withHasProp"; + w->__put__(ctx, name, value); + return; + } + } else { +// qDebug() << ctx << "setting mutable binding"; + if (ctx->setMutableBinding(this, name, value)) + return; + } + } + if (strictMode || name->isEqualTo(engine->id_this)) + throwReferenceError(Value::fromString(name)); + engine->globalObject.objectValue()->__put__(this, name, value); +} + +Value ExecutionContext::getProperty(String *name) +{ + if (name->isEqualTo(engine->id_this)) + return thisObject; + + bool hasWith = false; +// qDebug() << "=== getProperty" << name->toQString(); + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (Object *w = ctx->withObject) { + hasWith = true; +// qDebug() << ctx << "hasWith"; + bool hasProperty = false; + Value v = w->__get__(ctx, name, &hasProperty); + if (hasProperty) { +// qDebug() << " withHasProp"; + return v; + } + continue; + } + + if (FunctionObject *f = ctx->function) { + if (f->needsActivation || hasWith) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return ctx->locals[i]; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return ctx->arguments[i]; + } + } + if (ctx->activation) { + bool hasProperty = false; + Value v = ctx->activation->__get__(ctx, name, &hasProperty); + if (hasProperty) + return v; + } + } + throwReferenceError(Value::fromString(name)); + return Value::undefinedValue(); +} + +Value ExecutionContext::getPropertyNoThrow(String *name) +{ + if (name->isEqualTo(engine->id_this)) + return thisObject; + + bool hasWith = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (Object *w = ctx->withObject) { + hasWith = true; + bool hasProperty = false; + Value v = w->__get__(ctx, name, &hasProperty); + if (hasProperty) + return v; + continue; + } + + if (FunctionObject *f = ctx->function) { + if (f->needsActivation || hasWith) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return ctx->locals[i]; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return ctx->arguments[i]; + } + } + if (ctx->activation) { + bool hasProperty = false; + Value v = ctx->activation->__get__(ctx, name, &hasProperty); + if (hasProperty) + return v; + } + } + return Value::undefinedValue(); +} + +Value ExecutionContext::getPropertyAndBase(String *name, Object **base) +{ + *base = 0; + + if (name->isEqualTo(engine->id_this)) + return thisObject; + + bool hasWith = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (Object *w = ctx->withObject) { + hasWith = true; + bool hasProperty = false; + Value v = w->__get__(ctx, name, &hasProperty); + if (hasProperty) { + *base = w; + return v; + } + continue; + } + + if (FunctionObject *f = ctx->function) { + if (f->needsActivation || hasWith) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return ctx->locals[i]; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return ctx->arguments[i]; + } + } + if (ctx->activation) { + bool hasProperty = false; + Value v = ctx->activation->__get__(ctx, name, &hasProperty); + if (hasProperty) + return v; + } + } + throwReferenceError(Value::fromString(name)); + return Value::undefinedValue(); +} + + + +void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) +{ + Value lhs = getProperty(name); + value = op(lhs, value, this); + setProperty(name, value); +} + +void ExecutionContext::throwError(Value value) +{ + __qmljs_builtin_throw(value, this); +} + +void ExecutionContext::throwError(const QString &message) +{ + Value v = Value::fromString(this, message); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void ExecutionContext::throwSyntaxError(DiagnosticMessage *message) +{ + throwError(Value::fromObject(engine->newSyntaxErrorObject(this, message))); +} + +void ExecutionContext::throwTypeError() +{ + throwError(Value::fromObject(engine->newTypeErrorObject(this, QStringLiteral("Type error")))); +} + +void ExecutionContext::throwUnimplemented(const QString &message) +{ + Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void ExecutionContext::throwReferenceError(Value value) +{ + String *s = value.toString(this); + QString msg = s->toQString() + QStringLiteral(" is not defined"); + throwError(Value::fromObject(engine->newReferenceErrorObject(this, msg))); +} + +void ExecutionContext::throwRangeError(Value value) +{ + String *s = value.toString(this); + QString msg = s->toQString() + QStringLiteral(" out of range"); + throwError(Value::fromObject(engine->newRangeErrorObject(this, msg))); +} + +void ExecutionContext::throwURIError(Value msg) +{ + throwError(Value::fromObject(engine->newURIErrorObject(this, msg))); +} + +void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc) +{ + MemoryManager::GCBlocker blockGC(parent->engine->memoryManager); + + engine = parent->engine; + this->parent = parent; + outer = f->scope; + engine->current = this; + + function = f; + strictMode = f->strictMode; + + thisObject = that; + if (!strictMode && !thisObject.isObject()) { + if (thisObject.isUndefined() || thisObject.isNull()) + thisObject = engine->globalObject; + else + thisObject = thisObject.toObject(this); + } + + locals = function->varCount ? reinterpret_cast(this + 1) : 0; + if (locals) + std::fill(locals, locals + function->varCount, Value::undefinedValue()); + + arguments = args; + argumentCount = argc; + if (function->needsActivation || argc < function->formalParameterCount){ + argumentCount = qMax(argc, function->formalParameterCount); + arguments = reinterpret_cast(this + 1) + function->varCount; + if (argc) + std::copy(args, args + argc, arguments); + if (argc < function->formalParameterCount) + std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); + } + + + activation = 0; + withObject = 0; + + if (function->usesArgumentsObject) { + ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this, function->formalParameterCount, argc); + args->prototype = engine->objectPrototype; + Value arguments = Value::fromObject(args); + createMutableBinding(engine->id_arguments, false); + setMutableBinding(this, engine->id_arguments, arguments); + } + + if (engine->debugger) + engine->debugger->aboutToCall(f, this); +} + +void ExecutionContext::leaveCallContext() +{ + if (!function->needsActivation) + locals = 0; + engine->current = parent; + parent = 0; + + if (engine->debugger) + engine->debugger->justLeft(this); +} + +void ExecutionContext::wireUpPrototype() +{ + assert(thisObject.isObject()); + + Value proto = function->__get__(this, engine->id_prototype); + if (proto.isObject()) + thisObject.objectValue()->prototype = proto.objectValue(); + else + thisObject.objectValue()->prototype = engine->objectPrototype; +} + +} // namespace VM +} // namespace QQmlJS diff --git a/src/v4/qmljs_environment.h b/src/v4/qmljs_environment.h new file mode 100644 index 0000000000..111c6ae239 --- /dev/null +++ b/src/v4/qmljs_environment.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_ENVIRONMENT_H +#define QMLJS_ENVIRONMENT_H + +#include "qv4global.h" +#include + +namespace QQmlJS { +namespace VM { + +struct Value; +struct Object; +struct ExecutionEngine; +struct ExecutionContext; +struct DeclarativeEnvironment; + +struct Q_V4_EXPORT DiagnosticMessage +{ + enum { Error, Warning }; + + QString fileName; + quint32 offset; + quint32 length; + quint32 startLine; + unsigned startColumn: 31; + unsigned type: 1; + QString message; + DiagnosticMessage *next; + + DiagnosticMessage(); + ~DiagnosticMessage(); + String *buildFullMessage(ExecutionContext *ctx) const; +}; + +struct ExecutionContext +{ + ExecutionEngine *engine; + ExecutionContext *parent; + ExecutionContext *outer; + Value thisObject; + + FunctionObject *function; + + Value *arguments; + unsigned int argumentCount; + Value *locals; + + String * const *formals() const; + unsigned int formalCount() const; + String * const *variables() const; + unsigned int variableCount() const; + + bool strictMode; + + Object *activation; + Object *withObject; + + void init(ExecutionEngine *e); + void init(ExecutionContext *p, Object *with); + void destroy(); + + bool hasBinding(String *name) const; + void createMutableBinding(String *name, bool deletable); + bool setMutableBinding(ExecutionContext *scope, String *name, Value value); + Value getBindingValue(ExecutionContext *scope, String *name, bool strict) const; + bool deleteBinding(ExecutionContext *ctx, String *name); + + ExecutionContext *createWithScope(Object *with); + ExecutionContext *popScope(); + + void initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc); + void leaveCallContext(); + + void wireUpPrototype(); + + void throwError(Value value); + void throwError(const QString &message); + void throwSyntaxError(DiagnosticMessage *message); + void throwTypeError(); + void throwReferenceError(Value value); + void throwRangeError(Value value); + void throwURIError(Value msg); + void throwUnimplemented(const QString &message); + + void setProperty(String *name, Value value); + Value getProperty(String *name); + Value getPropertyNoThrow(String *name); + Value getPropertyAndBase(String *name, Object **base); + void inplaceBitOp(Value value, String *name, BinOp op); + bool deleteProperty(String *name); + + inline Value argument(unsigned int index = 0) + { + if (index < argumentCount) + return arguments[index]; + return Value::undefinedValue(); + } + + void mark(); + +}; + +/* Function *f, int argc */ +#define requiredMemoryForExecutionContect(f, argc) \ + sizeof(ExecutionContext) + sizeof(Value) * (f->varCount + qMax((uint)argc, f->formalParameterCount)) + + +} // namespace VM +} // namespace QQmlJS + +#endif diff --git a/src/v4/qmljs_math.h b/src/v4/qmljs_math.h new file mode 100644 index 0000000000..4645c5a324 --- /dev/null +++ b/src/v4/qmljs_math.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_MATH_H +#define QMLJS_MATH_H + +#ifndef QMLJS_LLVM_RUNTIME +# include +#endif // QMLJS_LLVM_RUNTIME +#include + +#if !defined(QMLJS_LLVM_RUNTIME) && 1 //CPU(X86_64) + +namespace QQmlJS { +namespace VM { + +static inline Value add_int32(int a, int b) +{ + quint8 overflow = 0; + int aa = a; + + asm ("addl %2, %1\n" + "seto %0" + : "=q" (overflow), "=r" (aa) + : "r" (b), "1" (aa) + : "cc" + ); + if (!overflow) + return Value::fromInt32(aa); + return Value::fromDouble((double)a + (double)b); +} + +static inline Value sub_int32(int a, int b) +{ + quint8 overflow = 0; + int aa = a; + + asm ("subl %2, %1\n" + "seto %0" + : "=q" (overflow), "=r" (aa) + : "r" (b), "1" (aa) + : "cc" + ); + if (!overflow) + return Value::fromInt32(aa); + return Value::fromDouble((double)a - (double)b); +} + +static inline Value mul_int32(int a, int b) +{ + quint8 overflow = 0; + int aa = a; + + asm ("imul %2, %1\n" + "setc %0" + : "=q" (overflow), "=r" (aa) + : "r" (b), "1" (aa) + : "cc" + ); + if (!overflow) + return Value::fromInt32(aa); + return Value::fromDouble((double)a * (double)b); +} + +} // namespace VM +} // namespace QQmlJS + +#endif // !defined(QMLJS_LLVM_RUNTIME) && 1 //CPU(X86_64) +#endif // QMLJS_MATH_H diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp new file mode 100644 index 0000000000..ce1d1abbaa --- /dev/null +++ b/src/v4/qmljs_runtime.cpp @@ -0,0 +1,1161 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4global.h" +#include "debugging.h" +#include "qmljs_runtime.h" +#include "qv4object.h" +#include "qv4ir_p.h" +#include "qv4objectproto.h" +#include "qv4globalobject.h" +#include "qv4stringobject.h" +#include "private/qlocale_tools_p.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "../3rdparty/double-conversion/double-conversion.h" + +namespace QQmlJS { +namespace VM { + +QString numberToString(double num, int radix = 10) +{ + if (std::isnan(num)) { + return QStringLiteral("NaN"); + } else if (qIsInf(num)) { + return QLatin1String(num < 0 ? "-Infinity" : "Infinity"); + } + + if (radix == 10) { + char str[100]; + double_conversion::StringBuilder builder(str, sizeof(str)); + double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToShortest(num, &builder); + return QString::fromLatin1(builder.Finalize()); + } + + QString str; + bool negative = false; + + if (num < 0) { + negative = true; + num = -num; + } + + double frac = num - ::floor(num); + num = Value::toInteger(num); + + do { + char c = (char)::fmod(num, radix); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.prepend(QLatin1Char(c)); + num = ::floor(num / radix); + } while (num != 0); + + if (frac != 0) { + str.append(QLatin1Char('.')); + do { + frac = frac * radix; + char c = (char)::floor(frac); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.append(QLatin1Char(c)); + frac = frac - ::floor(frac); + } while (frac != 0); + } + + if (negative) + str.prepend(QLatin1Char('-')); + + return str; +} + +extern "C" { + +Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx) +{ + assert(clos); + return Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); +} + +Function *__qmljs_register_function(ExecutionContext *ctx, String *name, + bool hasDirectEval, + bool usesArgumentsObject, bool isStrict, + bool hasNestedFunctions, + String **formals, unsigned formalCount, + String **locals, unsigned localCount) +{ + Function *f = ctx->engine->newFunction(name ? name->toQString() : QString()); + + f->hasDirectEval = hasDirectEval; + f->usesArgumentsObject = usesArgumentsObject; + f->isStrict = isStrict; + f->hasNestedFunctions = hasNestedFunctions; + + for (unsigned i = 0; i < formalCount; ++i) + if (formals[i]) + f->formals.append(formals[i]); + for (unsigned i = 0; i < localCount; ++i) + if (locals[i]) + f->locals.append(locals[i]); + + return f; +} + +Value __qmljs_string_literal_undefined(ExecutionContext *ctx) +{ + return Value::fromString(ctx->engine->identifier(QStringLiteral("undefined"))); +} + +Value __qmljs_string_literal_null(ExecutionContext *ctx) +{ + return Value::fromString(ctx->engine->identifier(QStringLiteral("null"))); +} + +Value __qmljs_string_literal_true(ExecutionContext *ctx) +{ + return Value::fromString(ctx->engine->identifier(QStringLiteral("true"))); +} + +Value __qmljs_string_literal_false(ExecutionContext *ctx) +{ + return Value::fromString(ctx->engine->identifier(QStringLiteral("false"))); +} + +Value __qmljs_string_literal_object(ExecutionContext *ctx) +{ + return Value::fromString(ctx->engine->identifier(QStringLiteral("object"))); +} + +Value __qmljs_string_literal_boolean(ExecutionContext *ctx) +{ + return Value::fromString(ctx->engine->identifier(QStringLiteral("boolean"))); +} + +Value __qmljs_string_literal_number(ExecutionContext *ctx) +{ + return Value::fromString(ctx->engine->identifier(QStringLiteral("number"))); +} + +Value __qmljs_string_literal_string(ExecutionContext *ctx) +{ + return Value::fromString(ctx->engine->identifier(QStringLiteral("string"))); +} + +Value __qmljs_string_literal_function(ExecutionContext *ctx) +{ + return Value::fromString(ctx->engine->identifier(QStringLiteral("function"))); +} + +Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index) +{ + if (Object *o = base.asObject()) { + uint n = UINT_MAX; + if (index.isInteger()) + n = index.integerValue(); + else if (index.isDouble()) + n = index.doubleValue(); + if (n < UINT_MAX) + return Value::fromBoolean(o->__delete__(ctx, n)); + } + + String *name = index.toString(ctx); + return __qmljs_delete_member(ctx, base, name); +} + +Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name) +{ + Value obj = base.toObject(ctx); + return Value::fromBoolean(obj.objectValue()->__delete__(ctx, name)); +} + +Value __qmljs_delete_name(ExecutionContext *ctx, String *name) +{ + return Value::fromBoolean(ctx->deleteProperty(name)); +} + +Value __qmljs_add_helper(Value left, Value right, ExecutionContext *ctx) +{ + Value pleft = __qmljs_to_primitive(left, ctx, PREFERREDTYPE_HINT); + Value pright = __qmljs_to_primitive(right, ctx, PREFERREDTYPE_HINT); + if (pleft.isString() || pright.isString()) { + if (!pleft.isString()) + pleft = __qmljs_to_string(pleft, ctx); + if (!pright.isString()) + pright = __qmljs_to_string(pright, ctx); + String *string = __qmljs_string_concat(ctx, pleft.stringValue(), pright.stringValue()); + return Value::fromString(string); + } + double x = __qmljs_to_number(pleft, ctx); + double y = __qmljs_to_number(pright, ctx); + return Value::fromDouble(x + y); +} + +Value __qmljs_instanceof(Value left, Value right, ExecutionContext *ctx) +{ + if (FunctionObject *function = right.asFunctionObject()) { + bool r = function->hasInstance(ctx, left); + return Value::fromBoolean(r); + } + + return __qmljs_throw_type_error(ctx); +} + +Value __qmljs_in(Value left, Value right, ExecutionContext *ctx) +{ + if (right.isObject()) { + String *s = left.toString(ctx); + bool r = right.objectValue()->__hasProperty__(ctx, s); + return Value::fromBoolean(r); + } else { + return __qmljs_throw_type_error(ctx); + } +} + +void __qmljs_inplace_bit_and_name(Value value, String *name, ExecutionContext *ctx) +{ + ctx->inplaceBitOp(value, name, __qmljs_bit_and); +} + +void __qmljs_inplace_bit_or_name(Value value, String *name, ExecutionContext *ctx) +{ + ctx->inplaceBitOp(value, name, __qmljs_bit_or); +} + +void __qmljs_inplace_bit_xor_name(Value value, String *name, ExecutionContext *ctx) +{ + ctx->inplaceBitOp(value, name, __qmljs_bit_xor); +} + +void __qmljs_inplace_add_name(Value value, String *name, ExecutionContext *ctx) +{ + ctx->inplaceBitOp(value, name, __qmljs_add); +} + +void __qmljs_inplace_sub_name(Value value, String *name, ExecutionContext *ctx) +{ + ctx->inplaceBitOp(value, name, __qmljs_sub); +} + +void __qmljs_inplace_mul_name(Value value, String *name, ExecutionContext *ctx) +{ + ctx->inplaceBitOp(value, name, __qmljs_mul); +} + +void __qmljs_inplace_div_name(Value value, String *name, ExecutionContext *ctx) +{ + ctx->inplaceBitOp(value, name, __qmljs_div); +} + +void __qmljs_inplace_mod_name(Value value, String *name, ExecutionContext *ctx) +{ + ctx->inplaceBitOp(value, name, __qmljs_mod); +} + +void __qmljs_inplace_shl_name(Value value, String *name, ExecutionContext *ctx) +{ + ctx->inplaceBitOp(value, name, __qmljs_shl); +} + +void __qmljs_inplace_shr_name(Value value, String *name, ExecutionContext *ctx) +{ + ctx->inplaceBitOp(value, name, __qmljs_shr); +} + +void __qmljs_inplace_ushr_name(Value value, String *name, ExecutionContext *ctx) +{ + ctx->inplaceBitOp(value, name, __qmljs_ushr); +} + +void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, ExecutionContext *ctx) +{ + Object *obj = base.toObject(ctx).objectValue(); + obj->inplaceBinOp(value, index, __qmljs_bit_and, ctx); +} + +void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, ExecutionContext *ctx) +{ + Object *obj = base.toObject(ctx).objectValue(); + obj->inplaceBinOp(value, index, __qmljs_bit_or, ctx); +} + +void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, ExecutionContext *ctx) +{ + Object *obj = base.toObject(ctx).objectValue(); + obj->inplaceBinOp(value, index, __qmljs_bit_xor, ctx); +} + +void __qmljs_inplace_add_element(Value base, Value index, Value value, ExecutionContext *ctx) +{ + Object *obj = base.toObject(ctx).objectValue(); + obj->inplaceBinOp(value, index, __qmljs_add, ctx); +} + +void __qmljs_inplace_sub_element(Value base, Value index, Value value, ExecutionContext *ctx) +{ + Object *obj = base.toObject(ctx).objectValue(); + obj->inplaceBinOp(value, index, __qmljs_sub, ctx); +} + +void __qmljs_inplace_mul_element(Value base, Value index, Value value, ExecutionContext *ctx) +{ + Object *obj = base.toObject(ctx).objectValue(); + obj->inplaceBinOp(value, index, __qmljs_mul, ctx); +} + +void __qmljs_inplace_div_element(Value base, Value index, Value value, ExecutionContext *ctx) +{ + Object *obj = base.toObject(ctx).objectValue(); + obj->inplaceBinOp(value, index, __qmljs_div, ctx); +} + +void __qmljs_inplace_mod_element(Value base, Value index, Value value, ExecutionContext *ctx) +{ + Object *obj = base.toObject(ctx).objectValue(); + obj->inplaceBinOp(value, index, __qmljs_mod, ctx); +} + +void __qmljs_inplace_shl_element(Value base, Value index, Value value, ExecutionContext *ctx) +{ + Object *obj = base.toObject(ctx).objectValue(); + obj->inplaceBinOp(value, index, __qmljs_shl, ctx); +} + +void __qmljs_inplace_shr_element(Value base, Value index, Value value, ExecutionContext *ctx) +{ + Object *obj = base.toObject(ctx).objectValue(); + obj->inplaceBinOp(value, index, __qmljs_shr, ctx); +} + +void __qmljs_inplace_ushr_element(Value base, Value index, Value value, ExecutionContext *ctx) +{ + Object *obj = base.toObject(ctx).objectValue(); + obj->inplaceBinOp(value, index, __qmljs_ushr, ctx); +} + +void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, ExecutionContext *ctx) +{ + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, name, __qmljs_bit_and, ctx); +} + +void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, ExecutionContext *ctx) +{ + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, name, __qmljs_bit_or, ctx); +} + +void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, ExecutionContext *ctx) +{ + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, name, __qmljs_bit_xor, ctx); +} + +void __qmljs_inplace_add_member(Value value, Value base, String *name, ExecutionContext *ctx) +{ + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, name, __qmljs_add, ctx); +} + +void __qmljs_inplace_sub_member(Value value, Value base, String *name, ExecutionContext *ctx) +{ + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, name, __qmljs_sub, ctx); +} + +void __qmljs_inplace_mul_member(Value value, Value base, String *name, ExecutionContext *ctx) +{ + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, name, __qmljs_mul, ctx); +} + +void __qmljs_inplace_div_member(Value value, Value base, String *name, ExecutionContext *ctx) +{ + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, name, __qmljs_div, ctx); +} + +void __qmljs_inplace_mod_member(Value value, Value base, String *name, ExecutionContext *ctx) +{ + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, name, __qmljs_mod, ctx); +} + +void __qmljs_inplace_shl_member(Value value, Value base, String *name, ExecutionContext *ctx) +{ + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, name, __qmljs_shl, ctx); +} + +void __qmljs_inplace_shr_member(Value value, Value base, String *name, ExecutionContext *ctx) +{ + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, name, __qmljs_shr, ctx); +} + +void __qmljs_inplace_ushr_member(Value value, Value base, String *name, ExecutionContext *ctx) +{ + Object *o = base.toObject(ctx).objectValue(); + o->inplaceBinOp(value, name, __qmljs_ushr, ctx); +} + +String *__qmljs_string_from_utf8(ExecutionContext *ctx, const char *s) +{ + return ctx->engine->newString(QString::fromUtf8(s)); +} + +String *__qmljs_identifier_from_utf8(ExecutionContext *ctx, const char *s) +{ + return ctx->engine->identifier(QString::fromUtf8(s)); +} + +int __qmljs_string_length(ExecutionContext *, String *string) +{ + return string->toQString().length(); +} + +double __qmljs_string_to_number(const String *string) +{ + QString s = string->toQString(); + s = s.trimmed(); + if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) + return s.toLong(0, 16); + bool ok; + QByteArray ba = s.toLatin1(); + const char *begin = ba.constData(); + const char *end = 0; + double d = qstrtod(begin, &end, &ok); + if (end - begin != ba.size()) { + if (ba == "Infinity" || ba == "+Infinity") + d = INFINITY; + else if (ba == "-Infinity") + d = -INFINITY; + else + d = nan(""); + } + return d; +} + +Value __qmljs_string_from_number(ExecutionContext *ctx, double number) +{ + String *string = ctx->engine->newString(numberToString(number, 10)); + return Value::fromString(string); +} + +Bool __qmljs_string_compare(ExecutionContext *, String *left, String *right) +{ + return left->toQString() < right->toQString(); +} + +Bool __qmljs_string_equal(String *left, String *right) +{ + return left->isEqualTo(right); +} + +String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second) +{ + return ctx->engine->newString(first->toQString() + second->toQString()); +} + +Bool __qmljs_is_function(Value value) +{ + return value.objectValue()->asFunctionObject() != 0; +} + +Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int typeHint) +{ + if (typeHint == PREFERREDTYPE_HINT) { + if (object.asDateObject()) + typeHint = STRING_HINT; + else + typeHint = NUMBER_HINT; + } + + String *meth1 = ctx->engine->identifier("toString"); + String *meth2 = ctx->engine->identifier("valueOf"); + + if (typeHint == NUMBER_HINT) + qSwap(meth1, meth2); + + assert(object.isObject()); + Object *oo = object.objectValue(); + + Value conv = oo->__get__(ctx, meth1); + if (FunctionObject *o = conv.asFunctionObject()) { + Value r = o->call(ctx, object, 0, 0); + if (r.isPrimitive()) + return r; + } + + conv = oo->__get__(ctx, meth2); + if (FunctionObject *o = conv.asFunctionObject()) { + Value r = o->call(ctx, object, 0, 0); + if (r.isPrimitive()) + return r; + } + + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +Value __qmljs_throw_type_error(ExecutionContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +Value __qmljs_new_object(ExecutionContext *ctx) +{ + return Value::fromObject(ctx->engine->newObject()); +} + +Value __qmljs_new_boolean_object(ExecutionContext *ctx, bool boolean) +{ + Value value = Value::fromBoolean(boolean); + return Value::fromObject(ctx->engine->newBooleanObject(value)); +} + +Value __qmljs_new_number_object(ExecutionContext *ctx, double number) +{ + Value value = Value::fromDouble(number); + return Value::fromObject(ctx->engine->newNumberObject(value)); +} + +Value __qmljs_new_string_object(ExecutionContext *ctx, String *string) +{ + Value value = Value::fromString(string); + return Value::fromObject(ctx->engine->newStringObject(ctx, value)); +} + +void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Value value) +{ + if (! object.isObject()) + object = __qmljs_to_object(object, ctx); + object.objectValue()->__put__(ctx, name, value); +} + +Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) +{ + uint type = object.type(); + uint idx = index.asArrayIndex(); + + if (type != Value::Object_Type) { + if (type == Value::String_Type && idx < UINT_MAX) { + String *str = object.stringValue(); + if (idx >= (uint)str->toQString().length()) + return Value::undefinedValue(); + const QString s = str->toQString().mid(idx, 1); + return Value::fromString(ctx, s); + } + + object = __qmljs_to_object(object, ctx); + } + + Object *o = object.objectValue(); + + if (idx < UINT_MAX) { + const PropertyDescriptor *p = o->array.nonSparseAt(idx); + if (p && p->type == PropertyDescriptor::Data) + return p->value; + + return o->__get__(ctx, idx); + } + + String *name = index.toString(ctx); + return o->__get__(ctx, name); +} + +void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value value) +{ + if (!object.isObject()) + object = __qmljs_to_object(object, ctx); + + Object *o = object.objectValue(); + + uint idx = index.asArrayIndex(); + if (idx < UINT_MAX) { + PropertyDescriptor *p = o->array.nonSparseAtRef(idx); + if (p && p->type == PropertyDescriptor::Data && p->isWritable()) { + p->value = value; + return; + } + o->__put__(ctx, idx, value); + return; + } + + String *name = index.toString(ctx); + o->__put__(ctx, name, value); +} + +Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx) +{ + if (!in.isNull() && !in.isUndefined()) + in = __qmljs_to_object(in, ctx); + Object *it = ctx->engine->newForEachIteratorObject(ctx, in.asObject()); + return Value::fromObject(it); +} + +Value __qmljs_foreach_next_property_name(Value foreach_iterator) +{ + assert(foreach_iterator.isObject()); + + ForEachIteratorObject *it = static_cast(foreach_iterator.objectValue()); + assert(it->asForeachIteratorObject()); + + return it->nextPropertyName(); +} + + +void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, Value value) +{ + ctx->setProperty(name, value); +} + +Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name) +{ + if (object.isObject()) { + return object.objectValue()->__get__(ctx, name); + } else if (object.isString() && name->isEqualTo(ctx->engine->id_length)) { + return Value::fromInt32(object.stringValue()->toQString().length()); + } else { + object = __qmljs_to_object(object, ctx); + + if (object.isObject()) { + return object.objectValue()->__get__(ctx, name); + } else { + ctx->throwTypeError(); + return Value::undefinedValue(); + } + } +} + +Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name) +{ + return ctx->getProperty(name); +} + +Value __qmljs_get_thisObject(ExecutionContext *ctx) +{ + return ctx->thisObject; +} + +uint __qmljs_equal(Value x, Value y, ExecutionContext *ctx) +{ + if (x.type() == y.type()) { + switch (x.type()) { + case Value::Undefined_Type: + return true; + case Value::Null_Type: + return true; + case Value::Boolean_Type: + return x.booleanValue() == y.booleanValue(); + break; + case Value::Integer_Type: + return x.integerValue() == y.integerValue(); + case Value::String_Type: + return x.stringValue()->isEqualTo(y.stringValue()); + case Value::Object_Type: + return x.objectValue() == y.objectValue(); + default: // double + return x.doubleValue() == y.doubleValue(); + } + // unreachable + } else { + if (x.isNumber() && y.isNumber()) + return x.asDouble() == y.asDouble(); + if (x.isNull() && y.isUndefined()) { + return true; + } else if (x.isUndefined() && y.isNull()) { + return true; + } else if (x.isNumber() && y.isString()) { + Value ny = Value::fromDouble(__qmljs_to_number(y, ctx)); + return __qmljs_equal(x, ny, ctx); + } else if (x.isString() && y.isNumber()) { + Value nx = Value::fromDouble(__qmljs_to_number(x, ctx)); + return __qmljs_equal(nx, y, ctx); + } else if (x.isBoolean()) { + Value nx = Value::fromDouble((double) x.booleanValue()); + return __qmljs_equal(nx, y, ctx); + } else if (y.isBoolean()) { + Value ny = Value::fromDouble((double) y.booleanValue()); + return __qmljs_equal(x, ny, ctx); + } else if ((x.isNumber() || x.isString()) && y.isObject()) { + Value py = __qmljs_to_primitive(y, ctx, PREFERREDTYPE_HINT); + return __qmljs_equal(x, py, ctx); + } else if (x.isObject() && (y.isNumber() || y.isString())) { + Value px = __qmljs_to_primitive(x, ctx, PREFERREDTYPE_HINT); + return __qmljs_equal(px, y, ctx); + } + } + + return false; +} + +Value __qmljs_call_activation_property(ExecutionContext *context, String *name, Value *args, int argc) +{ + Object *base; + Value func = context->getPropertyAndBase(name, &base); + FunctionObject *o = func.asFunctionObject(); + if (!o) + context->throwTypeError(); + + Value thisObject = base ? Value::fromObject(base) : Value::undefinedValue(); + + if (o == context->engine->evalFunction && name->isEqualTo(context->engine->id_eval)) + return static_cast(o)->evalCall(context, thisObject, args, argc, true); + + return o->call(context, thisObject, args, argc); +} + +Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String *name, Value *args, int argc) +{ + Object *baseObject; + if (thisObject.isString()) { + baseObject = context->engine->stringPrototype; + } else { + if (!thisObject.isObject()) + thisObject = __qmljs_to_object(thisObject, context); + + assert(thisObject.isObject()); + baseObject = thisObject.objectValue(); + } + + Value func = baseObject->__get__(context, name); + FunctionObject *o = func.asFunctionObject(); + if (!o) + context->throwTypeError(); + + return o->call(context, thisObject, args, argc); +} + +Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc) +{ + Value thisObject = that; + if (!thisObject.isObject()) + thisObject = __qmljs_to_object(thisObject, context); + + assert(thisObject.isObject()); + Object *baseObject = thisObject.objectValue(); + + Value func = baseObject->__get__(context, index.toString(context)); + FunctionObject *o = func.asFunctionObject(); + if (!o) + context->throwTypeError(); + + return o->call(context, thisObject, args, argc); +} + +Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc) +{ + FunctionObject *o = func.asFunctionObject(); + if (!o) + context->throwTypeError(); + return o->call(context, thisObject, args, argc); +} + +Value __qmljs_construct_activation_property(ExecutionContext *context, String *name, Value *args, int argc) +{ + Value func = context->getProperty(name); + return __qmljs_construct_value(context, func, args, argc); +} + +Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc) +{ + if (FunctionObject *f = func.asFunctionObject()) + return f->construct(context, args, argc); + + context->throwTypeError(); + return Value::undefinedValue(); +} + +Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc) +{ + Value thisObject = base; + if (!thisObject.isObject()) + thisObject = __qmljs_to_object(base, context); + + Value func = thisObject.objectValue()->__get__(context, name); + if (FunctionObject *f = func.asFunctionObject()) + return f->construct(context, args, argc); + + context->throwTypeError(); + return Value::undefinedValue(); +} + +void __qmljs_throw(Value value, ExecutionContext *context) +{ + assert(!context->engine->unwindStack.isEmpty()); + + if (context->engine->debugger) + context->engine->debugger->aboutToThrow(&value); + + ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); + + // clean up call contexts + while (context != handler.context) { + ExecutionContext *parent = context->parent; + if (!context->withObject) + context->leaveCallContext(); + context = parent; + } + + context->engine->exception = value; + + longjmp(handler.stackFrame, 1); +} + +Q_V4_EXPORT void * __qmljs_create_exception_handler(ExecutionContext *context) +{ + context->engine->exception = Value::undefinedValue(); + context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler()); + ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); + handler.context = context; + return handler.stackFrame; +} + +void __qmljs_delete_exception_handler(ExecutionContext *context) +{ + assert(!context->engine->unwindStack.isEmpty()); + + context->engine->unwindStack.pop_back(); +} + +Value __qmljs_get_exception(ExecutionContext *context) +{ + return context->engine->exception; +} + +Value __qmljs_builtin_typeof(Value value, ExecutionContext *ctx) +{ + switch (value.type()) { + case Value::Undefined_Type: + return __qmljs_string_literal_undefined(ctx); + break; + case Value::Null_Type: + return __qmljs_string_literal_object(ctx); + break; + case Value::Boolean_Type: + return __qmljs_string_literal_boolean(ctx); + break; + case Value::String_Type: + return __qmljs_string_literal_string(ctx); + break; + case Value::Object_Type: + if (__qmljs_is_callable(value, ctx)) + return __qmljs_string_literal_function(ctx); + else + return __qmljs_string_literal_object(ctx); // ### implementation-defined + break; + default: + return __qmljs_string_literal_number(ctx); + break; + } +} + +Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context) +{ + return __qmljs_builtin_typeof(context->getPropertyNoThrow(name), context); +} + +Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext *context) +{ + Value obj = base.toObject(context); + return __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context); +} + +Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context) +{ + String *name = index.toString(context); + Value obj = base.toObject(context); + return __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context); +} + +Value __qmljs_builtin_post_increment(Value *val, ExecutionContext *ctx) +{ + if (val->isInteger() && val->integerValue() < INT_MAX) { + Value retval = *val; + val->int_32 += 1; + return retval; + } + + double d = __qmljs_to_number(*val, ctx); + *val = Value::fromDouble(d + 1); + return Value::fromDouble(d); +} + +Value __qmljs_builtin_post_increment_name(String *name, ExecutionContext *context) +{ + Value v = context->getProperty(name); + Value retval; + + if (v.isInteger() && v.integerValue() < INT_MAX) { + retval = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v, context); + retval = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + context->setProperty(name, v); + return retval; +} + +Value __qmljs_builtin_post_increment_member(Value base, String *name, ExecutionContext *context) +{ + Object *o = __qmljs_to_object(base, context).objectValue(); + + Value v = o->__get__(context, name); + Value retval; + + if (v.isInteger() && v.integerValue() < INT_MAX) { + retval = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v, context); + retval = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + o->__put__(context, name, v); + return retval; +} + +Value __qmljs_builtin_post_increment_element(Value base, Value index, ExecutionContext *context) +{ + Object *o = __qmljs_to_object(base, context).objectValue(); + + uint idx = index.asArrayIndex(); + + if (idx == UINT_MAX) { + String *s = index.toString(context); + return __qmljs_builtin_post_increment_member(base, s, context); + } + + Value v = o->__get__(context, idx); + Value retval; + + if (v.isInteger() && v.integerValue() < INT_MAX) { + retval = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v, context); + retval = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + o->__put__(context, idx, v); + return retval; +} + +Value __qmljs_builtin_post_decrement(Value *val, ExecutionContext *ctx) +{ + if (val->isInteger() && val->integerValue() > INT_MIN) { + Value retval = *val; + val->int_32 -= 1; + return retval; + } + + double d = __qmljs_to_number(*val, ctx); + *val = Value::fromDouble(d - 1); + return Value::fromDouble(d); +} + +Value __qmljs_builtin_post_decrement_name(String *name, ExecutionContext *context) +{ + Value v = context->getProperty(name); + Value retval; + + if (v.isInteger() && v.integerValue() > INT_MIN) { + retval = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v, context); + retval = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + context->setProperty(name, v); + return retval; +} + +Value __qmljs_builtin_post_decrement_member(Value base, String *name, ExecutionContext *context) +{ + Object *o = __qmljs_to_object(base, context).objectValue(); + + Value v = o->__get__(context, name); + Value retval; + + if (v.isInteger() && v.integerValue() > INT_MIN) { + retval = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v, context); + retval = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + o->__put__(context, name, v); + return retval; +} + +Value __qmljs_builtin_post_decrement_element(Value base, Value index, ExecutionContext *context) +{ + Object *o = __qmljs_to_object(base, context).objectValue(); + + uint idx = index.asArrayIndex(); + + if (idx == UINT_MAX) { + String *s = index.toString(context); + return __qmljs_builtin_post_decrement_member(base, s, context); + } + + Value v = o->__get__(context, idx); + Value retval; + + if (v.isInteger() && v.integerValue() > INT_MIN) { + retval = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v, context); + retval = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + o->__put__(context, idx, v); + return retval; +} + +void __qmljs_builtin_throw(Value val, ExecutionContext *context) +{ + __qmljs_throw(val, context); +} + +ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx) +{ + Object *obj = __qmljs_to_object(o, ctx).asObject(); + return ctx->createWithScope(obj); +} + +ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx) +{ + return ctx->popScope(); +} + +void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) +{ + ctx->createMutableBinding(name, deletable); +} + +void __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) +{ + Object *o = object.asObject(); + assert(o); + + PropertyDescriptor pd; + pd.value = val; + pd.type = PropertyDescriptor::Data; + pd.writable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + pd.configurable = PropertyDescriptor::Enabled; + o->__defineOwnProperty__(ctx, name, &pd); +} + +void __qmljs_builtin_define_array_property(Value object, int index, Value val, ExecutionContext *ctx) +{ + Object *o = object.asObject(); + assert(o); + + PropertyDescriptor pd; + pd.value = val; + pd.type = PropertyDescriptor::Data; + pd.writable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + pd.configurable = PropertyDescriptor::Enabled; + o->__defineOwnProperty__(ctx, index, &pd); +} + +void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx) +{ + Object *o = object.asObject(); + assert(o); + + PropertyDescriptor pd; + pd.get = getter.asFunctionObject(); + pd.set = setter.asFunctionObject(); + pd.type = PropertyDescriptor::Accessor; + pd.writable = PropertyDescriptor::Undefined; + pd.enumberable = PropertyDescriptor::Enabled; + pd.configurable = PropertyDescriptor::Enabled; + o->__defineOwnProperty__(ctx, name, &pd); +} + +Value __qmljs_increment(Value value, ExecutionContext *ctx) +{ + TRACE1(value); + + if (value.isInteger()) + return Value::fromInt32(value.integerValue() + 1); + + double d = __qmljs_to_number(value, ctx); + return Value::fromDouble(d + 1); +} + +Value __qmljs_decrement(Value value, ExecutionContext *ctx) +{ + TRACE1(value); + + if (value.isInteger()) + return Value::fromInt32(value.integerValue() - 1); + + double d = __qmljs_to_number(value, ctx); + return Value::fromDouble(d - 1); +} + +} // extern "C" + + +} // namespace VM +} // namespace QQmlJS diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h new file mode 100644 index 0000000000..2ea612642c --- /dev/null +++ b/src/v4/qmljs_runtime.h @@ -0,0 +1,862 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_RUNTIME_H +#define QMLJS_RUNTIME_H + +#include "qmljs_value.h" +#include "qmljs_math.h" + + +#include +#include +#include + +#include +#include + +#ifdef DO_TRACE_INSTR +# define TRACE1(x) fprintf(stderr, " %s\n", __FUNCTION__); +# define TRACE2(x, y) fprintf(stderr, " %s\n", __FUNCTION__); +#else +# define TRACE1(x) +# define TRACE2(x, y) +#endif // TRACE1 + +namespace QQmlJS { +namespace VM { + +enum TypeHint { + PREFERREDTYPE_HINT, + NUMBER_HINT, + STRING_HINT +}; + +struct Function; +struct Object; +struct String; +struct PropertyDescriptor; +struct ExecutionContext; +struct FunctionObject; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct DateObject; +struct RegExpObject; +struct ArrayObject; +struct ErrorObject; +struct ExecutionEngine; + +extern "C" { + +// context +Value __qmljs_call_activation_property(ExecutionContext *, String *name, Value *args, int argc); +Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, Value *args, int argc); +Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc); +Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc); + +Value __qmljs_construct_activation_property(ExecutionContext *, String *name, Value *args, int argc); +Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc); +Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc); + +Value __qmljs_builtin_typeof(Value val, ExecutionContext *ctx); +Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context); +Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext *context); +Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context); + +Value __qmljs_builtin_post_increment(Value *val, ExecutionContext *ctx); +Value __qmljs_builtin_post_increment_name(String *name, ExecutionContext *context); +Value __qmljs_builtin_post_increment_member(Value base, String *name, ExecutionContext *context); +Value __qmljs_builtin_post_increment_element(Value base, Value index, ExecutionContext *context); + +Value __qmljs_builtin_post_decrement(Value *val, ExecutionContext *ctx); +Value __qmljs_builtin_post_decrement_name(String *name, ExecutionContext *context); +Value __qmljs_builtin_post_decrement_member(Value base, String *name, ExecutionContext *context); +Value __qmljs_builtin_post_decrement_element(Value base, Value index, ExecutionContext *context); + +void __qmljs_builtin_throw(Value val, ExecutionContext *context); +void __qmljs_builtin_rethrow(ExecutionContext *context); +ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx); +ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx); +void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name); +void __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx); +void __qmljs_builtin_define_array_property(Value object, int index, Value val, ExecutionContext *ctx); +void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); + +// constructors +Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx); +VM::Function *__qmljs_register_function(ExecutionContext *ctx, String *name, + bool hasDirectEval, + bool usesArgumentsObject, bool isStrict, + bool hasNestedFunctions, + String **formals, unsigned formalCount, + String **locals, unsigned localCount); + +Bool __qmljs_is_function(Value value); + +// string literals +Value __qmljs_string_literal_undefined(ExecutionContext *ctx); +Value __qmljs_string_literal_null(ExecutionContext *ctx); +Value __qmljs_string_literal_true(ExecutionContext *ctx); +Value __qmljs_string_literal_false(ExecutionContext *ctx); +Value __qmljs_string_literal_object(ExecutionContext *ctx); +Value __qmljs_string_literal_boolean(ExecutionContext *ctx); +Value __qmljs_string_literal_number(ExecutionContext *ctx); +Value __qmljs_string_literal_string(ExecutionContext *ctx); +Value __qmljs_string_literal_function(ExecutionContext *ctx); + +// strings +String *__qmljs_string_from_utf8(ExecutionContext *ctx, const char *s); +int __qmljs_string_length(ExecutionContext *ctx, String *string); +double __qmljs_string_to_number(const String *string); +Value __qmljs_string_from_number(ExecutionContext *ctx, double number); +Bool __qmljs_string_compare(ExecutionContext *ctx, String *left, String *right); +Bool __qmljs_string_equal(String *left, String *right); +String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second); +String *__qmljs_identifier_from_utf8(ExecutionContext *ctx, const char *s); + +// objects +Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int typeHint); +Value __qmljs_throw_type_error(ExecutionContext *ctx); +Value __qmljs_new_object(ExecutionContext *ctx); +Value __qmljs_new_boolean_object(ExecutionContext *ctx, bool boolean); +Value __qmljs_new_number_object(ExecutionContext *ctx, double n); +Value __qmljs_new_string_object(ExecutionContext *ctx, String *string); +void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, Value value); +void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Value value); +Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name); +Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name); + +Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index); +void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value value); + +// For each +Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx); +Value __qmljs_foreach_next_property_name(Value foreach_iterator); + +// context +Value __qmljs_get_thisObject(ExecutionContext *ctx); + +// type conversion and testing +Value __qmljs_to_primitive(Value value, ExecutionContext *ctx, int typeHint); +Bool __qmljs_to_boolean(Value value, ExecutionContext *ctx); +double __qmljs_to_number(Value value, ExecutionContext *ctx); +double __qmljs_to_integer(Value value, ExecutionContext *ctx); +int __qmljs_to_int32(Value value, ExecutionContext *ctx); +unsigned short __qmljs_to_uint16(Value value, ExecutionContext *ctx); +Value __qmljs_to_string(Value value, ExecutionContext *ctx); +Value __qmljs_to_object(Value value, ExecutionContext *ctx); +//uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *value); +Bool __qmljs_is_callable(Value value, ExecutionContext *ctx); +Value __qmljs_default_value(Value value, ExecutionContext *ctx, int typeHint); + +Bool __qmljs_equal(Value x, Value y, ExecutionContext *ctx); +Bool __qmljs_strict_equal(Value x, Value y); + +// unary operators +Value __qmljs_uplus(Value value, ExecutionContext *ctx); +Value __qmljs_uminus(Value value, ExecutionContext *ctx); +Value __qmljs_compl(Value value, ExecutionContext *ctx); +Value __qmljs_not(Value value, ExecutionContext *ctx); +Value __qmljs_increment(Value value, ExecutionContext *ctx); +Value __qmljs_decrement(Value value, ExecutionContext *ctx); + +Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index); +Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name); +Value __qmljs_delete_name(ExecutionContext *ctx, String *name); + +void __qmljs_throw(Value value, ExecutionContext *context); +// actually returns a jmp_buf * +void *__qmljs_create_exception_handler(ExecutionContext *context); +void __qmljs_delete_exception_handler(ExecutionContext *context); +Value __qmljs_get_exception(ExecutionContext *context); + +// binary operators +typedef Value (*BinOp)(Value left, Value right, ExecutionContext *ctx); + +Value __qmljs_instanceof(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_in(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_bit_or(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_bit_xor(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_bit_and(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_add(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_sub(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_mul(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_div(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_mod(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_shl(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_shr(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_ushr(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_gt(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_lt(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_ge(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_le(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_eq(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_ne(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_se(Value left, Value right, ExecutionContext *ctx); +Value __qmljs_sne(Value left, Value right, ExecutionContext *ctx); + +Value __qmljs_add_helper(Value left, Value right, ExecutionContext *ctx); + +void __qmljs_inplace_bit_and_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_bit_or_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_bit_xor_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_add_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_sub_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_mul_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_div_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_mod_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_shl_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_shr_name(Value value, String *name, ExecutionContext *ctx); +void __qmljs_inplace_ushr_name(Value value, String *name, ExecutionContext *ctx); + +void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_add_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_sub_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_mul_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_div_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_mod_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_shl_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_shr_element(Value base, Value index, Value value, ExecutionContext *ctx); +void __qmljs_inplace_ushr_element(Value base, Value index, Value value, ExecutionContext *ctx); + +void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_add_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_sub_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_mul_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_div_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_mod_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_shl_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_shr_member(Value value, Value base, String *name, ExecutionContext *ctx); +void __qmljs_inplace_ushr_member(Value value, Value base, String *name, ExecutionContext *ctx); + +Bool __qmljs_cmp_gt(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_lt(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_ge(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_le(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_eq(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_ne(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_se(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_sne(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_instanceof(Value left, Value right, ExecutionContext *ctx); +Bool __qmljs_cmp_in(Value left, Value right, ExecutionContext *ctx); + +// type conversion and testing +inline Value __qmljs_to_primitive(Value value, ExecutionContext *ctx, int typeHint) +{ + if (!value.isObject()) + return value; + return __qmljs_default_value(value, ctx, typeHint); +} + +inline Bool __qmljs_to_boolean(Value value, ExecutionContext *ctx) +{ + switch (value.type()) { + case Value::Undefined_Type: + case Value::Null_Type: + return false; + case Value::Boolean_Type: + case Value::Integer_Type: + return (bool)value.int_32; + case Value::String_Type: + return __qmljs_string_length(ctx, value.stringValue()) > 0; + case Value::Object_Type: + return true; + default: // double + if (! value.doubleValue() || std::isnan(value.doubleValue())) + return false; + return true; + } +} + +inline double __qmljs_to_number(Value value, ExecutionContext *ctx) +{ + switch (value.type()) { + case Value::Undefined_Type: + return nan(""); + case Value::Null_Type: + return 0; + case Value::Boolean_Type: + return (value.booleanValue() ? 1. : 0.); + case Value::Integer_Type: + return value.int_32; + case Value::String_Type: + return __qmljs_string_to_number(value.stringValue()); + case Value::Object_Type: { + Value prim = __qmljs_to_primitive(value, ctx, NUMBER_HINT); + return __qmljs_to_number(prim, ctx); + } + default: // double + return value.doubleValue(); + } +} + +inline double __qmljs_to_integer(Value value, ExecutionContext *ctx) +{ + if (value.isConvertibleToInt()) + return value.int_32; + + return Value::toInteger(__qmljs_to_number(value, ctx)); +} + +inline int __qmljs_to_int32(Value value, ExecutionContext *ctx) +{ + if (value.isConvertibleToInt()) + return value.int_32; + + return Value::toInt32(__qmljs_to_number(value, ctx)); +} + +inline unsigned short __qmljs_to_uint16(Value value, ExecutionContext *ctx) +{ + if (value.isConvertibleToInt()) + return (ushort)(uint)value.integerValue(); + + double number = __qmljs_to_number(value, ctx); + + double D16 = 65536.0; + if ((number >= 0 && number < D16)) + return static_cast(number); + + if (!std::isfinite(number)) + return +0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D16); + + if (number < 0) + number += D16; + + return (unsigned short)number; +} + +inline Value __qmljs_to_string(Value value, ExecutionContext *ctx) +{ + switch (value.type()) { + case Value::Undefined_Type: + return __qmljs_string_literal_undefined(ctx); + break; + case Value::Null_Type: + return __qmljs_string_literal_null(ctx); + break; + case Value::Boolean_Type: + if (value.booleanValue()) + return __qmljs_string_literal_true(ctx); + else + return __qmljs_string_literal_false(ctx); + break; + case Value::String_Type: + return value; + break; + case Value::Object_Type: { + Value prim = __qmljs_to_primitive(value, ctx, STRING_HINT); + if (prim.isPrimitive()) + return __qmljs_to_string(prim, ctx); + else + return __qmljs_throw_type_error(ctx); + break; + } + case Value::Integer_Type: + return __qmljs_string_from_number(ctx, value.int_32); + break; + default: // double + return __qmljs_string_from_number(ctx, value.doubleValue()); + break; + + } // switch +} + +inline Value __qmljs_to_object(Value value, ExecutionContext *ctx) +{ + switch (value.type()) { + case Value::Undefined_Type: + case Value::Null_Type: + return __qmljs_throw_type_error(ctx); + break; + case Value::Boolean_Type: + return __qmljs_new_boolean_object(ctx, value.booleanValue()); + break; + case Value::String_Type: + return __qmljs_new_string_object(ctx, value.stringValue()); + break; + case Value::Object_Type: + return value; + break; + case Value::Integer_Type: + return __qmljs_new_number_object(ctx, value.int_32); + break; + default: // double + return __qmljs_new_number_object(ctx, value.doubleValue()); + break; + } +} + +/* +inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *value) +{ + switch (value->type()) { + case Value::Undefined_Type: + case Value::Null_Type: + *result = __qmljs_throw_type_error(ctx); + return false; + default: + return true; + } +} +*/ + +inline Bool __qmljs_is_callable(Value value, ExecutionContext * /*ctx*/) +{ + if (value.isObject()) + return __qmljs_is_function(value); + else + return false; +} + +inline Value __qmljs_default_value(Value value, ExecutionContext *ctx, int typeHint) +{ + if (value.isObject()) + return __qmljs_object_default_value(ctx, value, typeHint); + return Value::undefinedValue(); +} + + +inline Value __qmljs_uplus(Value value, ExecutionContext *ctx) +{ + TRACE1(value); + + if (value.tryIntegerConversion()) + return value; + + double n = __qmljs_to_number(value, ctx); + return Value::fromDouble(n); +} + +inline Value __qmljs_uminus(Value value, ExecutionContext *ctx) +{ + TRACE1(value); + + // +0 != -0, so we need to convert to double when negating 0 + if (value.isInteger() && value.integerValue()) + return Value::fromInt32(-value.integerValue()); + double n = __qmljs_to_number(value, ctx); + return Value::fromDouble(-n); +} + +inline Value __qmljs_compl(Value value, ExecutionContext *ctx) +{ + TRACE1(value); + + int n; + if (value.isConvertibleToInt()) + n = value.int_32; + else + n = Value::toInt32(__qmljs_to_number(value, ctx)); + + return Value::fromInt32(~n); +} + +inline Value __qmljs_not(Value value, ExecutionContext *ctx) +{ + TRACE1(value); + + bool b = __qmljs_to_boolean(value, ctx); + return Value::fromBoolean(!b); +} + +// binary operators +inline Value __qmljs_bit_or(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + if (Value::integerCompatible(left, right)) + return Value::fromInt32(left.integerValue() | right.integerValue()); + + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + int rval = Value::toInt32(__qmljs_to_number(right, ctx)); + return Value::fromInt32(lval | rval); +} + +inline Value __qmljs_bit_xor(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + if (Value::integerCompatible(left, right)) + return Value::fromInt32(left.integerValue() ^ right.integerValue()); + + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + int rval = Value::toInt32(__qmljs_to_number(right, ctx)); + return Value::fromInt32(lval ^ rval); +} + +inline Value __qmljs_bit_and(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + if (Value::integerCompatible(left, right)) + return Value::fromInt32(left.integerValue() & right.integerValue()); + + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + int rval = Value::toInt32(__qmljs_to_number(right, ctx)); + return Value::fromInt32(lval & rval); +} + +inline Value __qmljs_add(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + +#ifndef QMLJS_LLVM_RUNTIME + if (Value::integerCompatible(left, right)) + return add_int32(left.integerValue(), right.integerValue()); +#endif // QMLJS_LLVM_RUNTIME + + if (Value::bothDouble(left, right)) + return Value::fromDouble(left.doubleValue() + right.doubleValue()); + + return __qmljs_add_helper(left, right, ctx); +} + +inline Value __qmljs_sub(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + +#ifndef QMLJS_LLVM_RUNTIME + if (Value::integerCompatible(left, right)) + return sub_int32(left.integerValue(), right.integerValue()); +#endif // QMLJS_LLVM_RUNTIME + + double lval = __qmljs_to_number(left, ctx); + double rval = __qmljs_to_number(right, ctx); + return Value::fromDouble(lval - rval); +} + +inline Value __qmljs_mul(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + +#ifndef QMLJS_LLVM_RUNTIME + if (Value::integerCompatible(left, right)) + return mul_int32(left.integerValue(), right.integerValue()); +#endif // QMLJS_LLVM_RUNTIME + + double lval = __qmljs_to_number(left, ctx); + double rval = __qmljs_to_number(right, ctx); + return Value::fromDouble(lval * rval); +} + +inline Value __qmljs_div(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + double lval = __qmljs_to_number(left, ctx); + double rval = __qmljs_to_number(right, ctx); + return Value::fromDouble(lval / rval); +} + +inline Value __qmljs_mod(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + if (Value::integerCompatible(left, right) && right.integerValue() != 0) + return Value::fromInt32(left.integerValue() % right.integerValue()); + + double lval = __qmljs_to_number(left, ctx); + double rval = __qmljs_to_number(right, ctx); + return Value::fromDouble(fmod(lval, rval)); +} + +inline Value __qmljs_shl(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + if (Value::integerCompatible(left, right)) + return Value::fromInt32(left.integerValue() << ((uint(right.integerValue()) & 0x1f))); + + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; + return Value::fromInt32(lval << rval); +} + +inline Value __qmljs_shr(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + if (Value::integerCompatible(left, right)) + return Value::fromInt32(left.integerValue() >> ((uint(right.integerValue()) & 0x1f))); + + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; + return Value::fromInt32(lval >> rval); +} + +inline Value __qmljs_ushr(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + uint result; + if (Value::integerCompatible(left, right)) { + result = uint(left.integerValue()) >> (uint(right.integerValue()) & 0x1f); + } else { + unsigned lval = Value::toUInt32(__qmljs_to_number(left, ctx)); + unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; + result = lval >> rval; + } + + if (result > INT_MAX) + return Value::fromDouble(result); + return Value::fromInt32(result); +} + +inline Value __qmljs_gt(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + return Value::fromBoolean(__qmljs_cmp_gt(left, right, ctx)); +} + +inline Value __qmljs_lt(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + return Value::fromBoolean(__qmljs_cmp_lt(left, right, ctx)); +} + +inline Value __qmljs_ge(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + return Value::fromBoolean(__qmljs_cmp_ge(left, right, ctx)); +} + +inline Value __qmljs_le(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + return Value::fromBoolean(__qmljs_cmp_le(left, right, ctx)); +} + +inline Value __qmljs_eq(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + return Value::fromBoolean(__qmljs_cmp_eq(left, right, ctx)); +} + +inline Value __qmljs_ne(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + return Value::fromBoolean(!__qmljs_cmp_eq(left, right, ctx)); +} + +inline Value __qmljs_se(Value left, Value right, ExecutionContext *) +{ + TRACE2(left, right); + + bool r = __qmljs_strict_equal(left, right); + return Value::fromBoolean(r); +} + +inline Value __qmljs_sne(Value left, Value right, ExecutionContext *) +{ + TRACE2(left, right); + + bool r = ! __qmljs_strict_equal(left, right); + return Value::fromBoolean(r); +} + +inline Bool __qmljs_cmp_gt(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); + right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); + + if (Value::integerCompatible(left, right)) + return left.integerValue() > right.integerValue(); + if (Value::bothDouble(left, right)) { + return left.doubleValue() > right.doubleValue(); + } else if (left.isString() && right.isString()) { + return __qmljs_string_compare(ctx, right.stringValue(), left.stringValue()); + } else { + double l = __qmljs_to_number(left, ctx); + double r = __qmljs_to_number(right, ctx); + return l > r; + } +} + +inline Bool __qmljs_cmp_lt(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); + right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); + + if (Value::integerCompatible(left, right)) + return left.integerValue() < right.integerValue(); + if (Value::bothDouble(left, right)) { + return left.doubleValue() < right.doubleValue(); + } else if (left.isString() && right.isString()) { + return __qmljs_string_compare(ctx, left.stringValue(), right.stringValue()); + } else { + double l = __qmljs_to_number(left, ctx); + double r = __qmljs_to_number(right, ctx); + return l < r; + } +} + +inline Bool __qmljs_cmp_ge(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); + right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); + + if (Value::integerCompatible(left, right)) + return left.integerValue() >= right.integerValue(); + if (Value::bothDouble(left, right)) { + return left.doubleValue() >= right.doubleValue(); + } else if (left.isString() && right.isString()) { + return !__qmljs_string_compare(ctx, left.stringValue(), right.stringValue()); + } else { + double l = __qmljs_to_number(left, ctx); + double r = __qmljs_to_number(right, ctx); + return l >= r; + } +} + +inline Bool __qmljs_cmp_le(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); + right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); + + if (Value::integerCompatible(left, right)) + return left.integerValue() <= right.integerValue(); + if (Value::bothDouble(left, right)) { + return left.doubleValue() <= right.doubleValue(); + } else if (left.isString() && right.isString()) { + return !__qmljs_string_compare(ctx, right.stringValue(), left.stringValue()); + } else { + double l = __qmljs_to_number(left, ctx); + double r = __qmljs_to_number(right, ctx); + return l <= r; + } +} + +inline Bool __qmljs_cmp_eq(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + // need to test for doubles first as NaN != NaN + if (Value::bothDouble(left, right)) + return left.doubleValue() == right.doubleValue(); + if (left.val == right.val) + return true; + if (left.isString() && right.isString()) + return __qmljs_string_equal(left.stringValue(), right.stringValue()); + + return __qmljs_equal(left, right, ctx); +} + +inline Bool __qmljs_cmp_ne(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + return !__qmljs_cmp_eq(left, right, ctx); +} + +inline Bool __qmljs_cmp_se(Value left, Value right, ExecutionContext *) +{ + TRACE2(left, right); + + return __qmljs_strict_equal(left, right); +} + +inline Bool __qmljs_cmp_sne(Value left, Value right, ExecutionContext *) +{ + TRACE2(left, right); + + return ! __qmljs_strict_equal(left, right); +} + +inline Bool __qmljs_cmp_instanceof(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + Value v = __qmljs_instanceof(left, right, ctx); + return v.booleanValue(); +} + +inline uint __qmljs_cmp_in(Value left, Value right, ExecutionContext *ctx) +{ + TRACE2(left, right); + + Value v = __qmljs_in(left, right, ctx); + return v.booleanValue(); +} + +inline Bool __qmljs_strict_equal(Value x, Value y) +{ + TRACE2(x, y); + + if (x.isDouble() || y.isDouble()) + return x.asDouble() == y.asDouble(); + if (x.rawValue() == y.rawValue()) + return true; + if (x.isString() && y.isString()) + return __qmljs_string_equal(x.stringValue(), y.stringValue()); + return false; +} + +} // extern "C" + +} // namespace VM +} // namespace QQmlJS + +#endif // QMLJS_RUNTIME_H diff --git a/src/v4/qmljs_value.cpp b/src/v4/qmljs_value.cpp new file mode 100644 index 0000000000..8dba13a9c9 --- /dev/null +++ b/src/v4/qmljs_value.cpp @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include + +namespace QQmlJS { +namespace VM { + + +int Value::toUInt16(ExecutionContext *ctx) const +{ + return __qmljs_to_uint16(*this, ctx); +} + +Bool Value::toBoolean(ExecutionContext *ctx) const +{ + return __qmljs_to_boolean(*this, ctx); +} + +double Value::toInteger(ExecutionContext *ctx) const +{ + return __qmljs_to_integer(*this, ctx); +} + +double Value::toNumber(ExecutionContext *ctx) const +{ + return __qmljs_to_number(*this, ctx); +} + +String *Value::toString(ExecutionContext *ctx) const +{ + Value v = __qmljs_to_string(*this, ctx); + assert(v.isString()); + return v.stringValue(); +} + +Value Value::toObject(ExecutionContext *ctx) const +{ + return __qmljs_to_object(*this, ctx); +} + +bool Value::sameValue(Value other) const { + if (val == other.val) + return true; + if (isString() && other.isString()) + return stringValue()->isEqualTo(other.stringValue()); + if (isInteger() && int_32 == 0 && other.dbl == 0) + return true; + if (dbl == 0 && other.isInteger() && other.int_32 == 0) + return true; + return false; +} + +Value Value::fromString(ExecutionContext *ctx, const QString &s) +{ + return fromString(ctx->engine->newString(s)); +} + +int Value::toInt32(double number) +{ + const double D32 = 4294967296.0; + const double D31 = D32 / 2.0; + + if ((number >= -D31 && number < D31)) + return static_cast(number); + + + if (!std::isfinite(number)) + return 0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D32); + + if (number < -D31) + number += D32; + else if (number >= D31) + number -= D32; + + return int(number); +} + +unsigned int Value::toUInt32(double number) +{ + const double D32 = 4294967296.0; + if ((number >= 0 && number < D32)) + return static_cast(number); + + if (!std::isfinite(number)) + return +0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D32); + + if (number < 0) + number += D32; + + return unsigned(number); +} + +double Value::toInteger(double number) +{ + if (std::isnan(number)) + return +0; + else if (! number || std::isinf(number)) + return number; + const double v = floor(fabs(number)); + return std::signbit(number) ? -v : v; +} + +Value Value::property(ExecutionContext *ctx, String *name) const +{ + return isObject() ? objectValue()->__get__(ctx, name) : undefinedValue(); +} + + + +} // namespace VM +} // namespace QQmlJS diff --git a/src/v4/qmljs_value.h b/src/v4/qmljs_value.h new file mode 100644 index 0000000000..e11b08f50c --- /dev/null +++ b/src/v4/qmljs_value.h @@ -0,0 +1,480 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_VALUE_H +#define QMLJS_VALUE_H + +#include +#include +#include "qv4global.h" +#include "qv4string.h" +#include +#include "qv4managed.h" + +namespace QQmlJS { +namespace VM { + +struct String; +struct ExecutionContext; +struct ExecutionEngine; +struct Value; + +extern "C" { +double __qmljs_to_number(Value value, ExecutionContext *ctx); +} + +typedef uint Bool; + + +struct Q_V4_EXPORT Value +{ + union { + quint64 val; + double dbl; + struct { +#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN + uint tag; +#endif + union { + uint uint_32; + int int_32; +#if CPU(X86_64) +#else + Managed *m; + Object *o; + String *s; +#endif + }; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + uint tag; +#endif + }; + }; + + enum Masks { + NotDouble_Mask = 0xfffc0000, + Type_Mask = 0xffff8000, + Immediate_Mask = NotDouble_Mask | 0x00008000, + Tag_Shift = 32 + }; + enum ValueType { + Undefined_Type = Immediate_Mask | 0x00000, + Null_Type = Immediate_Mask | 0x10000, + Boolean_Type = Immediate_Mask | 0x20000, + Integer_Type = Immediate_Mask | 0x30000, + Object_Type = NotDouble_Mask | 0x00000, + String_Type = NotDouble_Mask | 0x10000 + }; + + enum ImmediateFlags { + ConvertibleToInt = Immediate_Mask | 0x1 + }; + + enum ValueTypeInternal { + _Undefined_Type = Undefined_Type, + _Null_Type = Null_Type | ConvertibleToInt, + _Boolean_Type = Boolean_Type | ConvertibleToInt, + _Integer_Type = Integer_Type | ConvertibleToInt, + _Object_Type = Object_Type, + _String_Type = String_Type + + }; + + inline unsigned type() const { + return tag & Type_Mask; + } + + inline bool isUndefined() const { return tag == _Undefined_Type; } + inline bool isNull() const { return tag == _Null_Type; } + inline bool isBoolean() const { return tag == _Boolean_Type; } + inline bool isInteger() const { return tag == _Integer_Type; } + inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } + inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } +#if CPU(X86_64) + inline bool isString() const { return (tag & Type_Mask) == String_Type; } + inline bool isObject() const { return (tag & Type_Mask) == Object_Type; } +#else + inline bool isString() const { return tag == String_Type; } + inline bool isObject() const { return tag == Object_Type; } +#endif + inline bool isConvertibleToInt() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } + + Bool booleanValue() const { + return int_32; + } + double doubleValue() const { + return dbl; + } + void setDouble(double d) { + dbl = d; + } + double asDouble() const { + if (tag == _Integer_Type) + return int_32; + return dbl; + } + int integerValue() const { + return int_32; + } + +#if CPU(X86_64) + String *stringValue() const { + return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } + Object *objectValue() const { + return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } + Managed *managed() const { + return (Managed *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } +#else + String *stringValue() const { + return s; + } + Object *objectValue() const { + return o; + } + Managed *managed() const { + return m; + } +#endif + + quint64 rawValue() const { + return val; + } + + static Value undefinedValue(); + static Value nullValue(); + static Value fromBoolean(Bool b); + static Value fromDouble(double d); + static Value fromInt32(int i); + static Value fromUInt32(uint i); + static Value fromString(String *s); + static Value fromObject(Object *o); + +#ifndef QMLJS_LLVM_RUNTIME + static Value fromString(ExecutionContext *ctx, const QString &fromString); +#endif + + static double toInteger(double fromNumber); + static int toInt32(double value); + static unsigned int toUInt32(double value); + + int toUInt16(ExecutionContext *ctx) const; + int toInt32(ExecutionContext *ctx) const; + unsigned int toUInt32(ExecutionContext *ctx) const; + + Bool toBoolean(ExecutionContext *ctx) const; + double toInteger(ExecutionContext *ctx) const; + double toNumber(ExecutionContext *ctx) const; + String *toString(ExecutionContext *ctx) const; + Value toObject(ExecutionContext *ctx) const; + + inline bool isPrimitive() const { return !isObject(); } +#if CPU(X86_64) + inline bool integerCompatible() const { + const quint64 mask = quint64(ConvertibleToInt) << 32; + return (val & mask) == mask; + } + static inline bool integerCompatible(Value a, Value b) { + const quint64 mask = quint64(ConvertibleToInt) << 32; + return ((a.val & b.val) & mask) == mask; + } + static inline bool bothDouble(Value a, Value b) { + const quint64 mask = quint64(NotDouble_Mask) << 32; + return ((a.val | b.val) & mask) != mask; + } +#else + inline bool integerCompatible() const { + return (tag & ConvertibleToInt) == ConvertibleToInt; + } + static inline bool integerCompatible(Value a, Value b) { + return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; + } + static inline bool bothDouble(Value a, Value b) { + return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask; + } +#endif + inline bool tryIntegerConversion() { + bool b = isConvertibleToInt(); + if (b) + tag = _Integer_Type; + return b; + } + + Managed *asManaged() const; + Object *asObject() const; + FunctionObject *asFunctionObject() const; + BooleanObject *asBooleanObject() const; + NumberObject *asNumberObject() const; + StringObject *asStringObject() const; + DateObject *asDateObject() const; + RegExpObject *asRegExpObject() const; + ArrayObject *asArrayObject() const; + ErrorObject *asErrorObject() const; + uint asArrayIndex() const; + uint asArrayLength(ExecutionContext *ctx, bool *ok) const; + + Value property(ExecutionContext *ctx, String *name) const; + + // Section 9.12 + bool sameValue(Value other) const; + + void mark() const { + Managed *m = asManaged(); + if (m) + m->mark(); + } +}; + +inline Value Value::undefinedValue() +{ + Value v; +#if CPU(X86_64) + v.val = quint64(_Undefined_Type) << Tag_Shift; +#else + v.tag = _Undefined_Type; + v.int_32 = 0; +#endif + return v; +} + +inline Value Value::nullValue() +{ + Value v; +#if CPU(X86_64) + v.val = quint64(_Null_Type) << Tag_Shift; +#else + v.tag = _Null_Type; + v.int_32 = 0; +#endif + return v; +} + +inline Value Value::fromBoolean(Bool b) +{ + Value v; + v.tag = _Boolean_Type; + v.int_32 = (bool)b; + return v; +} + +inline Value Value::fromDouble(double d) +{ + Value v; + v.dbl = d; + return v; +} + +inline Value Value::fromInt32(int i) +{ + Value v; + v.tag = _Integer_Type; + v.int_32 = i; + return v; +} + +inline Value Value::fromUInt32(uint i) +{ + Value v; + if (i < INT_MAX) { + v.tag = _Integer_Type; + v.int_32 = (int)i; + } else { + v.dbl = i; + } + return v; +} + +inline Value Value::fromString(String *s) +{ + Value v; +#if CPU(X86_64) + v.val = (quint64)s; + v.val |= quint64(_String_Type) << Tag_Shift; +#else + v.tag = _String_Type; + v.s = s; +#endif + return v; +} + +inline Value Value::fromObject(Object *o) +{ + Value v; +#if CPU(X86_64) + v.val = (quint64)o; + v.val |= quint64(_Object_Type) << Tag_Shift; +#else + v.tag = _Object_Type; + v.o = o; +#endif + return v; +} + +inline int Value::toInt32(ExecutionContext *ctx) const +{ + if (isConvertibleToInt()) + return int_32; + double d; + if (isDouble()) + d = dbl; + else + d = __qmljs_to_number(*this, ctx); + + const double D32 = 4294967296.0; + const double D31 = D32 / 2.0; + + if ((d >= -D31 && d < D31)) + return static_cast(d); + + return Value::toInt32(__qmljs_to_number(*this, ctx)); +} + +inline unsigned int Value::toUInt32(ExecutionContext *ctx) const { + if (isConvertibleToInt()) + return (unsigned) int_32; + double d; + if (isDouble()) + d = dbl; + else + d = __qmljs_to_number(*this, ctx); + + const double D32 = 4294967296.0; + if (dbl >= 0 && dbl < D32) + return static_cast(dbl); + return toUInt32(d); +} + +inline uint Value::asArrayIndex() const +{ + if (isInteger() && int_32 >= 0) + return (uint)int_32; + if (!isDouble()) + return UINT_MAX; + uint idx = (uint)dbl; + if (idx != dbl) + return UINT_MAX; + return idx; +} + +inline uint Value::asArrayLength(ExecutionContext *ctx, bool *ok) const +{ + *ok = true; + if (isConvertibleToInt() && int_32 >= 0) + return (uint)int_32; + if (isDouble()) { + uint idx = (uint)dbl; + if ((double)idx != dbl) { + *ok = false; + return UINT_MAX; + } + return idx; + } + if (isString()) + return stringValue()->toUInt(ok); + + uint idx = toUInt32(ctx); + double d = toNumber(ctx); + if (d != idx) { + *ok = false; + return UINT_MAX; + } + return idx; +} + + +inline Managed *Value::asManaged() const +{ + if (isObject() || isString()) + return managed(); + return 0; +} + +inline Object *Value::asObject() const +{ + return isObject() ? objectValue() : 0; +} + +inline FunctionObject *Value::asFunctionObject() const +{ + return isObject() ? managed()->asFunctionObject() : 0; +} + +inline BooleanObject *Value::asBooleanObject() const +{ + return isObject() ? managed()->asBooleanObject() : 0; +} + +inline NumberObject *Value::asNumberObject() const +{ + return isObject() ? managed()->asNumberObject() : 0; +} + +inline StringObject *Value::asStringObject() const +{ + return isObject() ? managed()->asStringObject() : 0; +} + +inline DateObject *Value::asDateObject() const +{ + return isObject() ? managed()->asDateObject() : 0; +} + +inline RegExpObject *Value::asRegExpObject() const +{ + return isObject() ? managed()->asRegExpObject() : 0; +} + +inline ArrayObject *Value::asArrayObject() const +{ + return isObject() ? managed()->asArrayObject() : 0; +} + +inline ErrorObject *Value::asErrorObject() const +{ + return isObject() ? managed()->asErrorObject() : 0; +} + + +} // namespace VM +} // namespace QQmlJS + +#endif diff --git a/src/v4/qv4_llvm_p.h b/src/v4/qv4_llvm_p.h new file mode 100644 index 0000000000..60625c8677 --- /dev/null +++ b/src/v4/qv4_llvm_p.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QV4_LLVM_P_H +#define QV4_LLVM_P_H + +#include "qv4global.h" +#include "qv4ir_p.h" + +#include + +namespace QQmlJS { + +// Note: keep this enum in sync with the command-line option! +enum LLVMOutputType { + LLVMOutputJit = -1, + LLVMOutputIR = 0, // .ll + LLVMOutputBitcode = 1, // .bc + LLVMOutputAssembler = 2, // .s + LLVMOutputObject = 3 // .o +}; + +Q_V4_EXPORT int compileWithLLVM(IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*)(void *)); + +} // QQmlJS + +#endif // QV4_LLVM_P_H diff --git a/src/v4/qv4argumentsobject.cpp b/src/v4/qv4argumentsobject.cpp new file mode 100644 index 0000000000..f7327506e9 --- /dev/null +++ b/src/v4/qv4argumentsobject.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include + +namespace QQmlJS { +namespace VM { + + +ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount) + : context(context) +{ + type = Type_ArgumentsObject; + + defineDefaultProperty(context->engine->id_length, Value::fromInt32(actualParameterCount)); + if (context->strictMode) { + for (uint i = 0; i < context->argumentCount; ++i) + Object::__put__(context, QString::number(i), context->arguments[i]); + FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, __qmljs_throw_type_error); + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); + pd.configurable = PropertyDescriptor::Disabled; + pd.enumberable = PropertyDescriptor::Disabled; + __defineOwnProperty__(context, QStringLiteral("callee"), &pd); + __defineOwnProperty__(context, QStringLiteral("caller"), &pd); + } else { + uint numAccessors = qMin(formalParameterCount, actualParameterCount); + context->engine->requireArgumentsAccessors(numAccessors); + for (uint i = 0; i < (uint)numAccessors; ++i) { + mappedArguments.append(context->argument(i)); + __defineOwnProperty__(context, i, &context->engine->argumentsAccessors.at(i)); + } + PropertyDescriptor pd; + pd.type = PropertyDescriptor::Data; + pd.writable = PropertyDescriptor::Enabled; + pd.configurable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + for (uint i = numAccessors; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) { + pd.value = context->argument(i); + __defineOwnProperty__(context, i, &pd); + } + defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); + isNonStrictArgumentsObject = true; + } +} + +bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc) +{ + PropertyDescriptor *pd = array.at(index); + PropertyDescriptor map; + bool isMapped = false; + if (pd && index < (uint)mappedArguments.size()) + isMapped = pd->isAccessor() && pd->get == context->engine->argumentsAccessors.at(index).get; + + if (isMapped) { + map = *pd; + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->configurable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Enabled; + pd->value = mappedArguments.at(index); + } + + isNonStrictArgumentsObject = false; + bool strict = ctx->strictMode; + ctx->strictMode = false; + bool result = Object::__defineOwnProperty__(ctx, index, desc); + ctx->strictMode = strict; + isNonStrictArgumentsObject = true; + + if (isMapped && desc->isData()) { + if (desc->type != PropertyDescriptor::Generic) { + Value arg = desc->value; + map.set->call(ctx, Value::fromObject(this), &arg, 1); + } + if (desc->writable != PropertyDescriptor::Disabled) + *pd = map; + } + + if (ctx->strictMode && !result) + __qmljs_throw_type_error(ctx); + return result; +} + +void ArgumentsObject::markObjects() +{ + for (int i = 0; i < mappedArguments.size(); ++i) { + Managed *m = mappedArguments.at(i).asManaged(); + if (m) + m->mark(); + } + Object::markObjects(); +} + + +Value ArgumentsGetterFunction::call(ExecutionContext *ctx, Value thisObject, Value *, int) +{ + Object *that = thisObject.asObject(); + if (!that) + __qmljs_throw_type_error(ctx); + ArgumentsObject *o = that->asArgumentsObject(); + if (!o) + __qmljs_throw_type_error(ctx); + + assert(index < o->context->argumentCount); + return o->context->argument(index); +} + +Value ArgumentsSetterFunction::call(ExecutionContext *ctx, Value thisObject, Value *args, int argc) +{ + Object *that = thisObject.asObject(); + if (!that) + __qmljs_throw_type_error(ctx); + ArgumentsObject *o = that->asArgumentsObject(); + if (!o) + __qmljs_throw_type_error(ctx); + + assert(index < o->context->argumentCount); + o->context->arguments[index] = argc ? args[0] : Value::undefinedValue(); + return Value::undefinedValue(); +} + + +} +} diff --git a/src/v4/qv4argumentsobject.h b/src/v4/qv4argumentsobject.h new file mode 100644 index 0000000000..adbaf5db1a --- /dev/null +++ b/src/v4/qv4argumentsobject.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ARGUMENTSOBJECTS_H +#define QV4ARGUMENTSOBJECTS_H + +#include +#include + +namespace QQmlJS { +namespace VM { + +struct ArgumentsGetterFunction: FunctionObject +{ + uint index; + + ArgumentsGetterFunction(ExecutionContext *scope, uint index) + : FunctionObject(scope), index(index) {} + + virtual Value call(ExecutionContext *ctx, Value thisObject, Value *, int); +}; + +struct ArgumentsSetterFunction: FunctionObject +{ + uint index; + + ArgumentsSetterFunction(ExecutionContext *scope, uint index) + : FunctionObject(scope), index(index) {} + + virtual Value call(ExecutionContext *ctx, Value thisObject, Value *args, int argc); +}; + + +struct ArgumentsObject: Object { + ExecutionContext *context; + QVector mappedArguments; + ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount); + + bool defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); + + virtual void markObjects(); +}; + +} +} + +#endif + diff --git a/src/v4/qv4array.cpp b/src/v4/qv4array.cpp new file mode 100644 index 0000000000..9879b2f372 --- /dev/null +++ b/src/v4/qv4array.cpp @@ -0,0 +1,649 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4array.h" +#include "qmljs_runtime.h" +#include "qv4object.h" +#include "qv4functionobject.h" +#include + +#ifdef QT_QMAP_DEBUG +# include +# include +#endif + +namespace QQmlJS { +namespace VM { + +bool ArrayElementLessThan::operator()(const PropertyDescriptor &p1, const PropertyDescriptor &p2) const +{ + if (p1.type == PropertyDescriptor::Generic) + return false; + if (p2.type == PropertyDescriptor::Generic) + return true; + Value v1 = thisObject->getValue(m_context, &p1); + Value v2 = thisObject->getValue(m_context, &p2); + + if (v1.isUndefined()) + return false; + if (v2.isUndefined()) + return true; + if (!m_comparefn.isUndefined()) { + Value args[] = { v1, v2 }; + Value result = __qmljs_call_value(m_context, Value::undefinedValue(), m_comparefn, args, 2); + return result.toNumber(m_context) <= 0; + } + return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); +} + + +const SparseArrayNode *SparseArrayNode::nextNode() const +{ + const SparseArrayNode *n = this; + if (n->right) { + n = n->right; + while (n->left) + n = n->left; + } else { + const SparseArrayNode *y = n->parent(); + while (y && n == y->right) { + n = y; + y = n->parent(); + } + n = y; + } + return n; +} + +const SparseArrayNode *SparseArrayNode::previousNode() const +{ + const SparseArrayNode *n = this; + if (n->left) { + n = n->left; + while (n->right) + n = n->right; + } else { + const SparseArrayNode *y = n->parent(); + while (y && n == y->left) { + n = y; + y = n->parent(); + } + n = y; + } + return n; +} + +SparseArrayNode *SparseArrayNode::copy(SparseArray *d) const +{ + SparseArrayNode *n = d->createNode(size_left, 0, false); + n->value = value; + n->setColor(color()); + if (left) { + n->left = left->copy(d); + n->left->setParent(n); + } else { + n->left = 0; + } + if (right) { + n->right = right->copy(d); + n->right->setParent(n); + } else { + n->right = 0; + } + return n; +} + +/* + x y + \ / \ + y --> x b + / \ \ + a b a +*/ +void SparseArray::rotateLeft(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = x->right; + x->right = y->left; + if (y->left != 0) + y->left->setParent(x); + y->setParent(x->parent()); + if (x == root) + root = y; + else if (x == x->parent()->left) + x->parent()->left = y; + else + x->parent()->right = y; + y->left = x; + x->setParent(y); + y->size_left += x->size_left; +} + + +/* + x y + / / \ + y --> a x + / \ / + a b b +*/ +void SparseArray::rotateRight(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = x->left; + x->left = y->right; + if (y->right != 0) + y->right->setParent(x); + y->setParent(x->parent()); + if (x == root) + root = y; + else if (x == x->parent()->right) + x->parent()->right = y; + else + x->parent()->left = y; + y->right = x; + x->setParent(y); + x->size_left -= y->size_left; +} + + +void SparseArray::rebalance(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + x->setColor(SparseArrayNode::Red); + while (x != root && x->parent()->color() == SparseArrayNode::Red) { + if (x->parent() == x->parent()->parent()->left) { + SparseArrayNode *y = x->parent()->parent()->right; + if (y && y->color() == SparseArrayNode::Red) { + x->parent()->setColor(SparseArrayNode::Black); + y->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + x = x->parent()->parent(); + } else { + if (x == x->parent()->right) { + x = x->parent(); + rotateLeft(x); + } + x->parent()->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + rotateRight (x->parent()->parent()); + } + } else { + SparseArrayNode *y = x->parent()->parent()->left; + if (y && y->color() == SparseArrayNode::Red) { + x->parent()->setColor(SparseArrayNode::Black); + y->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + x = x->parent()->parent(); + } else { + if (x == x->parent()->left) { + x = x->parent(); + rotateRight(x); + } + x->parent()->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + rotateLeft(x->parent()->parent()); + } + } + } + root->setColor(SparseArrayNode::Black); +} + +void SparseArray::deleteNode(SparseArrayNode *z) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = z; + SparseArrayNode *x; + SparseArrayNode *x_parent; + if (y->left == 0) { + x = y->right; + if (y == mostLeftNode) { + if (x) + mostLeftNode = x; // It cannot have (left) children due the red black invariant. + else + mostLeftNode = y->parent(); + } + } else { + if (y->right == 0) { + x = y->left; + } else { + y = y->right; + while (y->left != 0) + y = y->left; + x = y->right; + } + } + if (y != z) { + z->left->setParent(y); + y->left = z->left; + if (y != z->right) { + x_parent = y->parent(); + if (x) + x->setParent(y->parent()); + y->parent()->left = x; + y->right = z->right; + z->right->setParent(y); + } else { + x_parent = y; + } + if (root == z) + root = y; + else if (z->parent()->left == z) + z->parent()->left = y; + else + z->parent()->right = y; + y->setParent(z->parent()); + // Swap the colors + SparseArrayNode::Color c = y->color(); + y->setColor(z->color()); + z->setColor(c); + y = z; + } else { + x_parent = y->parent(); + if (x) + x->setParent(y->parent()); + if (root == z) + root = x; + else if (z->parent()->left == z) + z->parent()->left = x; + else + z->parent()->right = x; + } + if (y->color() != SparseArrayNode::Red) { + while (x != root && (x == 0 || x->color() == SparseArrayNode::Black)) { + if (x == x_parent->left) { + SparseArrayNode *w = x_parent->right; + if (w->color() == SparseArrayNode::Red) { + w->setColor(SparseArrayNode::Black); + x_parent->setColor(SparseArrayNode::Red); + rotateLeft(x_parent); + w = x_parent->right; + } + if ((w->left == 0 || w->left->color() == SparseArrayNode::Black) && + (w->right == 0 || w->right->color() == SparseArrayNode::Black)) { + w->setColor(SparseArrayNode::Red); + x = x_parent; + x_parent = x_parent->parent(); + } else { + if (w->right == 0 || w->right->color() == SparseArrayNode::Black) { + if (w->left) + w->left->setColor(SparseArrayNode::Black); + w->setColor(SparseArrayNode::Red); + rotateRight(w); + w = x_parent->right; + } + w->setColor(x_parent->color()); + x_parent->setColor(SparseArrayNode::Black); + if (w->right) + w->right->setColor(SparseArrayNode::Black); + rotateLeft(x_parent); + break; + } + } else { + SparseArrayNode *w = x_parent->left; + if (w->color() == SparseArrayNode::Red) { + w->setColor(SparseArrayNode::Black); + x_parent->setColor(SparseArrayNode::Red); + rotateRight(x_parent); + w = x_parent->left; + } + if ((w->right == 0 || w->right->color() == SparseArrayNode::Black) && + (w->left == 0 || w->left->color() == SparseArrayNode::Black)) { + w->setColor(SparseArrayNode::Red); + x = x_parent; + x_parent = x_parent->parent(); + } else { + if (w->left == 0 || w->left->color() == SparseArrayNode::Black) { + if (w->right) + w->right->setColor(SparseArrayNode::Black); + w->setColor(SparseArrayNode::Red); + rotateLeft(w); + w = x_parent->left; + } + w->setColor(x_parent->color()); + x_parent->setColor(SparseArrayNode::Black); + if (w->left) + w->left->setColor(SparseArrayNode::Black); + rotateRight(x_parent); + break; + } + } + } + if (x) + x->setColor(SparseArrayNode::Black); + } + free(y); + --numEntries; +} + +void SparseArray::recalcMostLeftNode() +{ + mostLeftNode = &header; + while (mostLeftNode->left) + mostLeftNode = mostLeftNode->left; +} + +static inline int qMapAlignmentThreshold() +{ + // malloc on 32-bit platforms should return pointers that are 8-byte + // aligned or more while on 64-bit platforms they should be 16-byte aligned + // or more + return 2 * sizeof(void*); +} + +static inline void *qMapAllocate(int alloc, int alignment) +{ + return alignment > qMapAlignmentThreshold() + ? qMallocAligned(alloc, alignment) + : ::malloc(alloc); +} + +static inline void qMapDeallocate(SparseArrayNode *node, int alignment) +{ + if (alignment > qMapAlignmentThreshold()) + qFreeAligned(node); + else + ::free(node); +} + +SparseArrayNode *SparseArray::createNode(uint sl, SparseArrayNode *parent, bool left) +{ + SparseArrayNode *node = static_cast(qMapAllocate(sizeof(SparseArrayNode), Q_ALIGNOF(SparseArrayNode))); + Q_CHECK_PTR(node); + + node->p = (quintptr)parent; + node->left = 0; + node->right = 0; + node->size_left = sl; + node->value = UINT_MAX; + ++numEntries; + + if (parent) { + if (left) { + parent->left = node; + if (parent == mostLeftNode) + mostLeftNode = node; + } else { + parent->right = node; + } + node->setParent(parent); + rebalance(node); + } + return node; +} + +void SparseArray::freeTree(SparseArrayNode *root, int alignment) +{ + if (root->left) + freeTree(root->left, alignment); + if (root->right) + freeTree(root->right, alignment); + qMapDeallocate(root, alignment); +} + +SparseArray::SparseArray() + : numEntries(0) +{ + header.p = 0; + header.left = 0; + header.right = 0; + mostLeftNode = &header; +} + +SparseArray::SparseArray(const SparseArray &other) +{ + header.p = 0; + header.right = 0; + if (other.header.left) { + header.left = other.header.left->copy(this); + header.left->setParent(&header); + recalcMostLeftNode(); + } +} + +SparseArrayNode *SparseArray::insert(uint akey) +{ + SparseArrayNode *n = root(); + SparseArrayNode *y = end(); + bool left = true; + uint s = akey; + while (n) { + y = n; + if (s == n->size_left) { + return n; + } else if (s < n->size_left) { + left = true; + n = n->left; + } else { + left = false; + s -= n->size_left; + n = n->right; + } + } + + return createNode(s, y, left); +} + +Array::Array(const Array &other) + : len(other.len) + , lengthProperty(0) + , values(other.values) + , sparse(0) +{ + freeList = other.freeList; + if (other.sparse) + sparse = new SparseArray(*other.sparse); +} + + +Value Array::indexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o) +{ + bool protoHasArray = false; + Object *p = o; + while ((p = p->prototype)) + if (p->array.length()) + protoHasArray = true; + + if (protoHasArray) { + // lets be safe and slow + for (uint i = fromIndex; i < endIndex; ++i) { + bool exists; + Value value = o->__get__(ctx, i, &exists); + if (exists && __qmljs_strict_equal(value, v)) + return Value::fromDouble(i); + } + } else if (sparse) { + for (SparseArrayNode *n = sparse->lowerBound(fromIndex); n && n->key() < endIndex; n = n->nextNode()) { + bool exists; + Value value = o->getValueChecked(ctx, descriptor(n->value), &exists); + if (exists && __qmljs_strict_equal(value, v)) + return Value::fromDouble(n->key()); + } + } else { + if ((int) endIndex > values.size()) + endIndex = values.size(); + PropertyDescriptor *pd = values.data() + offset; + PropertyDescriptor *end = pd + endIndex; + pd += fromIndex; + while (pd < end) { + bool exists; + Value value = o->getValueChecked(ctx, pd, &exists); + if (exists && __qmljs_strict_equal(value, v)) + return Value::fromDouble(pd - offset - values.constData()); + ++pd; + } + } + return Value::fromInt32(-1); +} + +void Array::concat(const Array &other) +{ + initSparse(); + int newLen = len + other.length(); + if (other.sparse) + initSparse(); + if (sparse) { + if (other.sparse) { + for (const SparseArrayNode *it = other.sparse->begin(); it != other.sparse->end(); it = it->nextNode()) + set(len + it->key(), other.descriptor(it->value)); + } else { + int oldSize = values.size(); + values.resize(oldSize + other.length()); + memcpy(values.data() + oldSize, other.values.constData() + other.offset, other.length()*sizeof(PropertyDescriptor)); + for (uint i = 0; i < other.length(); ++i) { + SparseArrayNode *n = sparse->insert(len + i); + n->value = oldSize + i; + } + } + } else { + int oldSize = values.size(); + values.resize(oldSize + other.length()); + memcpy(values.data() + oldSize, other.values.constData() + other.offset, other.length()*sizeof(PropertyDescriptor)); + } + setLengthUnchecked(newLen); +} + +void Array::sort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len) +{ + if (sparse) { + context->throwUnimplemented("Array::sort unimplemented for sparse arrays"); + return; + delete sparse; + } + + ArrayElementLessThan lessThan(context, thisObject, comparefn); + if (len > values.size() - offset) + len = values.size() - offset; + PropertyDescriptor *begin = values.begin() + offset; + std::sort(begin, begin + len, lessThan); +} + + +void Array::initSparse() +{ + if (!sparse) { + sparse = new SparseArray; + for (int i = offset; i < values.size(); ++i) { + SparseArrayNode *n = sparse->insert(i - offset); + n->value = i; + } + + if (offset) { + int o = offset; + for (int i = 0; i < o - 1; ++i) { + values[i].type = PropertyDescriptor::Generic; + values[i].value = Value::fromInt32(i + 1); + } + values[o - 1].type = PropertyDescriptor::Generic; + values[o - 1].value = Value::fromInt32(values.size()); + freeList = 0; + } else { + freeList = values.size(); + } + } +} + +bool Array::setLength(uint newLen) { + if (lengthProperty && !lengthProperty->isWritable()) + return false; + uint oldLen = length(); + bool ok = true; + if (newLen < oldLen) { + if (sparse) { + SparseArrayNode *begin = sparse->lowerBound(newLen); + SparseArrayNode *it = sparse->end()->previousNode(); + while (1) { + PropertyDescriptor &pd = values[it->value]; + if (pd.type != PropertyDescriptor::Generic && !pd.isConfigurable()) { + ok = false; + newLen = it->key() + 1; + break; + } + pd.type = PropertyDescriptor::Generic; + pd.value.tag = Value::_Undefined_Type; + pd.value.int_32 = freeList; + freeList = it->value; + bool brk = (it == begin); + SparseArrayNode *prev = it->previousNode(); + sparse->erase(it); + if (brk) + break; + it = prev; + } + } else { + PropertyDescriptor *it = values.data() + values.size(); + const PropertyDescriptor *begin = values.constData() + offset + newLen; + while (--it >= begin) { + if (it->type != PropertyDescriptor::Generic && !it->isConfigurable()) { + ok = false; + newLen = it - values.data() + offset + 1; + break; + } + } + values.resize(newLen + offset); + } + } else { + if (newLen >= 0x100000) + initSparse(); + } + setLengthUnchecked(newLen); + return ok; +} + +void Array::markObjects() const +{ + uint i = sparse ? 0 : offset; + for (; i < (uint)values.size(); ++i) { + const PropertyDescriptor &pd = values.at(i); + if (pd.isData()) { + if (Managed *m = pd.value.asManaged()) + m->mark(); + } else if (pd.isAccessor()) { + if (pd.get) + pd.get->mark(); + if (pd.set) + pd.set->mark(); + } + } +} + +} +} diff --git a/src/v4/qv4array.h b/src/v4/qv4array.h new file mode 100644 index 0000000000..86d438e4e1 --- /dev/null +++ b/src/v4/qv4array.h @@ -0,0 +1,639 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ARRAY_H +#define QV4ARRAY_H + +#include "qv4global.h" +#include +#include +#include +#include + +#ifdef Q_MAP_DEBUG +#include +#endif + +#include + +namespace QQmlJS { +namespace VM { + +struct SparseArray; + +class ArrayElementLessThan +{ +public: + inline ArrayElementLessThan(ExecutionContext *context, Object *thisObject, const Value &comparefn) + : m_context(context), thisObject(thisObject), m_comparefn(comparefn) {} + + bool operator()(const PropertyDescriptor &v1, const PropertyDescriptor &v2) const; + +private: + ExecutionContext *m_context; + Object *thisObject; + Value m_comparefn; +}; + + +struct SparseArrayNode +{ + quintptr p; + SparseArrayNode *left; + SparseArrayNode *right; + uint size_left; + uint value; + + enum Color { Red = 0, Black = 1 }; + enum { Mask = 3 }; // reserve the second bit as well + + const SparseArrayNode *nextNode() const; + SparseArrayNode *nextNode() { return const_cast(const_cast(this)->nextNode()); } + const SparseArrayNode *previousNode() const; + SparseArrayNode *previousNode() { return const_cast(const_cast(this)->previousNode()); } + + Color color() const { return Color(p & 1); } + void setColor(Color c) { if (c == Black) p |= Black; else p &= ~Black; } + SparseArrayNode *parent() const { return reinterpret_cast(p & ~Mask); } + void setParent(SparseArrayNode *pp) { p = (p & Mask) | quintptr(pp); } + + uint key() const { + uint k = size_left; + const SparseArrayNode *n = this; + while (SparseArrayNode *p = n->parent()) { + if (p && p->right == n) + k += p->size_left; + n = p; + } + return k; + } + + SparseArrayNode *copy(SparseArray *d) const; + + SparseArrayNode *lowerBound(uint key); + SparseArrayNode *upperBound(uint key); +}; + + +inline SparseArrayNode *SparseArrayNode::lowerBound(uint akey) +{ + SparseArrayNode *n = this; + SparseArrayNode *last = 0; + while (n) { + if (akey <= n->size_left) { + last = n; + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + return last; +} + + +inline SparseArrayNode *SparseArrayNode::upperBound(uint akey) +{ + SparseArrayNode *n = this; + SparseArrayNode *last = 0; + while (n) { + if (akey < n->size_left) { + last = n; + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + return last; +} + + + +struct Q_V4_EXPORT SparseArray +{ + SparseArray(); + ~SparseArray() { + if (root()) + freeTree(header.left, Q_ALIGNOF(SparseArrayNode)); + } + + SparseArray(const SparseArray &other); +private: + SparseArray &operator=(const SparseArray &other); + + int numEntries; + SparseArrayNode header; + SparseArrayNode *mostLeftNode; + + void rotateLeft(SparseArrayNode *x); + void rotateRight(SparseArrayNode *x); + void rebalance(SparseArrayNode *x); + void recalcMostLeftNode(); + + SparseArrayNode *root() const { return header.left; } + + void deleteNode(SparseArrayNode *z); + + +public: + SparseArrayNode *createNode(uint sl, SparseArrayNode *parent, bool left); + void freeTree(SparseArrayNode *root, int alignment); + + SparseArrayNode *findNode(uint akey) const; + + uint pop_front(); + void push_front(uint at); + uint pop_back(uint len); + void push_back(uint at, uint len); + + QList keys() const; + + const SparseArrayNode *end() const { return &header; } + SparseArrayNode *end() { return &header; } + const SparseArrayNode *begin() const { if (root()) return mostLeftNode; return end(); } + SparseArrayNode *begin() { if (root()) return mostLeftNode; return end(); } + + SparseArrayNode *erase(SparseArrayNode *n); + + SparseArrayNode *lowerBound(uint key); + const SparseArrayNode *lowerBound(uint key) const; + SparseArrayNode *upperBound(uint key); + const SparseArrayNode *upperBound(uint key) const; + SparseArrayNode *insert(uint akey); + + // STL compatibility + typedef uint key_type; + typedef int mapped_type; + typedef qptrdiff difference_type; + typedef int size_type; + +#ifdef Q_MAP_DEBUG + void dump() const; +#endif +}; + +inline SparseArrayNode *SparseArray::findNode(uint akey) const +{ + SparseArrayNode *n = root(); + + while (n) { + if (akey == n->size_left) { + return n; + } else if (akey < n->size_left) { + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + + return 0; +} + +inline uint SparseArray::pop_front() +{ + uint idx = UINT_MAX ; + + SparseArrayNode *n = findNode(0); + if (n) { + idx = n->value; + deleteNode(n); + // adjust all size_left indices on the path to leftmost item by 1 + SparseArrayNode *n = root(); + while (n) { + n->size_left -= 1; + n = n->left; + } + } + return idx; +} + +inline void SparseArray::push_front(uint value) +{ + // adjust all size_left indices on the path to leftmost item by 1 + SparseArrayNode *n = root(); + while (n) { + n->size_left += 1; + n = n->left; + } + n = insert(0); + n->value = value; +} + +inline uint SparseArray::pop_back(uint len) +{ + uint idx = UINT_MAX; + if (!len) + return idx; + + SparseArrayNode *n = findNode(len - 1); + if (n) { + idx = n->value; + deleteNode(n); + } + return idx; +} + +inline void SparseArray::push_back(uint index, uint len) +{ + SparseArrayNode *n = insert(len); + n->value = index; +} + +#ifdef Q_MAP_DEBUG + +void SparseArray::dump() const +{ + const_iterator it = begin(); + qDebug() << "map dump:"; + while (it != end()) { + const SparseArrayNode *n = it.i; + int depth = 0; + while (n && n != root()) { + ++depth; + n = n->parent(); + } + QByteArray space(4*depth, ' '); + qDebug() << space << (it.i->color() == SparseArrayNode::Red ? "Red " : "Black") << it.i << it.i->left << it.i->right + << it.key() << it.value(); + ++it; + } + qDebug() << "---------"; +} +#endif + + +inline SparseArrayNode *SparseArray::erase(SparseArrayNode *n) +{ + if (n == end()) + return n; + + SparseArrayNode *next = n->nextNode(); + deleteNode(n); + return next; +} + +inline QList SparseArray::keys() const +{ + QList res; + res.reserve(numEntries); + SparseArrayNode *n = mostLeftNode; + while (n != end()) { + res.append(n->key()); + n = n->nextNode(); + } + return res; +} + +inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const +{ + const SparseArrayNode *lb = root()->lowerBound(akey); + if (!lb) + lb = end(); + return lb; +} + + +inline SparseArrayNode *SparseArray::lowerBound(uint akey) +{ + SparseArrayNode *lb = root()->lowerBound(akey); + if (!lb) + lb = end(); + return lb; +} + + +inline const SparseArrayNode *SparseArray::upperBound(uint akey) const +{ + const SparseArrayNode *ub = root()->upperBound(akey); + if (!ub) + ub = end(); + return ub; +} + + +inline SparseArrayNode *SparseArray::upperBound(uint akey) +{ + SparseArrayNode *ub = root()->upperBound(akey); + if (!ub) + ub = end(); + return ub; +} + + +class Array +{ + friend struct ArrayPrototype; + + uint len; + PropertyDescriptor *lengthProperty; + union { + uint freeList; + uint offset; + }; + QVector values; + SparseArray *sparse; + + void fillDescriptor(PropertyDescriptor *pd, Value v) + { + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Enabled; + pd->configurable = PropertyDescriptor::Enabled; + pd->value = v; + } + + uint allocValue() { + uint idx = freeList; + if (values.size() <= (int)freeList) + values.resize(++freeList); + else + freeList = values.at(freeList).value.integerValue(); + return idx; + } + + uint allocValue(Value v) { + uint idx = allocValue(); + PropertyDescriptor *pd = &values[idx]; + fillDescriptor(pd, v); + return idx; + } + void freeValue(int idx) { + PropertyDescriptor &pd = values[idx]; + pd.type = PropertyDescriptor::Generic; + pd.value.tag = Value::_Undefined_Type; + pd.value.int_32 = freeList; + freeList = idx; + } + + PropertyDescriptor *descriptor(uint index) { + PropertyDescriptor *pd = values.data() + index; + if (!sparse) + pd += offset; + return pd; + } + const PropertyDescriptor *descriptor(uint index) const { + const PropertyDescriptor *pd = values.data() + index; + if (!sparse) + pd += offset; + return pd; + } + + void getHeadRoom() { + assert(!sparse && !offset); + offset = qMax(values.size() >> 2, 16); + QVector newValues(values.size() + offset); + memcpy(newValues.data() + offset, values.constData(), values.size()*sizeof(PropertyDescriptor)); + values = newValues; + } + +public: + Array() : len(0), lengthProperty(0), offset(0), sparse(0) {} + Array(const Array &other); + ~Array() { delete sparse; } + void initSparse(); + + uint length() const { return len; } + bool setLength(uint newLen); + + void setLengthProperty(PropertyDescriptor *pd) { lengthProperty = pd; } + PropertyDescriptor *getLengthProperty() { return lengthProperty; } + + void setLengthUnchecked(uint l) { + len = l; + if (lengthProperty) + lengthProperty->value = Value::fromUInt32(l); + } + + PropertyDescriptor *insert(uint index) { + PropertyDescriptor *pd; + if (!sparse && (index < 0x1000 || index < len + (len >> 2))) { + if (index + offset >= (uint)values.size()) { + values.resize(offset + index + 1); + for (uint i = len + 1; i < index; ++i) { + values[i].type = PropertyDescriptor::Generic; + values[i].value.tag = Value::_Undefined_Type; + } + } + pd = descriptor(index); + } else { + initSparse(); + SparseArrayNode *n = sparse->insert(index); + if (n->value == UINT_MAX) + n->value = allocValue(); + pd = descriptor(n->value); + } + if (index >= len) + setLengthUnchecked(index + 1); + return pd; + } + + void set(uint index, const PropertyDescriptor *pd) { + *insert(index) = *pd; + } + + void set(uint index, Value value) { + PropertyDescriptor *pd = insert(index); + fillDescriptor(pd, value); + } + + bool deleteIndex(uint index) { + if (index >= len) + return true; + PropertyDescriptor *pd = 0; + if (!sparse) { + pd = at(index); + } else { + SparseArrayNode *n = sparse->findNode(index); + if (n) + pd = descriptor(n->value); + } + if (!pd || pd->type == PropertyDescriptor::Generic) + return true; + if (!pd->isConfigurable()) + return false; + pd->type = PropertyDescriptor::Generic; + pd->value.tag = Value::_Undefined_Type; + if (sparse) { + pd->value.int_32 = freeList; + freeList = pd - values.constData(); + } + return true; + } + + PropertyDescriptor *at(uint index) { + if (!sparse) { + if (index >= values.size() - offset) + return 0; + return values.data() + index + offset; + } else { + SparseArrayNode *n = sparse->findNode(index); + if (!n) + return 0; + return values.data() + n->value; + } + } + + const PropertyDescriptor *nonSparseAt(uint index) const { + if (sparse) + return 0; + index += offset; + if (index >= (uint)values.size()) + return 0; + return values.constData() + index; + } + + PropertyDescriptor *nonSparseAtRef(uint index) { + if (sparse) + return 0; + index += offset; + if (index >= (uint)values.size()) + return 0; + return values.data() + index; + } + + const PropertyDescriptor *at(uint index) const { + if (!sparse) { + if (index >= values.size() - offset) + return 0; + return values.constData() + index + offset; + } else { + SparseArrayNode *n = sparse->findNode(index); + if (!n) + return 0; + return values.constData() + n->value; + } + } + + void markObjects() const; + + void push_front(Value v) { + if (!sparse) { + if (!offset) + getHeadRoom(); + + PropertyDescriptor pd; + fillDescriptor(&pd, v); + --offset; + values[offset] = pd; + } else { + uint idx = allocValue(v); + sparse->push_front(idx); + } + setLengthUnchecked(len + 1); + } + PropertyDescriptor *front() { + PropertyDescriptor *pd = 0; + if (!sparse) { + if (len) + pd = values.data() + offset; + } else { + SparseArrayNode *n = sparse->findNode(0); + if (n) + pd = descriptor(n->value); + } + if (pd && pd->type == PropertyDescriptor::Generic) + return 0; + return pd; + } + void pop_front() { + if (!len) + return; + if (!sparse) { + ++offset; + } else { + uint idx = sparse->pop_front(); + freeValue(idx); + } + setLengthUnchecked(len - 1); + } + void push_back(Value v) { + if (!sparse) { + PropertyDescriptor pd; + fillDescriptor(&pd, v); + values.append(pd); + } else { + uint idx = allocValue(v); + sparse->push_back(idx, len); + } + setLengthUnchecked(len + 1); + } + PropertyDescriptor *back() { + PropertyDescriptor *pd = 0; + if (!sparse) { + if (len) + pd = values.data() + offset + len; + } else { + SparseArrayNode *n = sparse->findNode(len - 1); + if (n) + pd = descriptor(n->value); + } + if (pd && pd->type == PropertyDescriptor::Generic) + return 0; + return pd; + } + void pop_back() { + if (!len) + return; + if (!sparse) { + values.resize(values.size() - 1); + } else { + uint idx = sparse->pop_back(len); + if (idx != UINT_MAX) + freeValue(idx); + } + setLengthUnchecked(len - 1); + } + + SparseArrayNode *sparseLowerBound(uint idx) { return sparse ? sparse->lowerBound(idx) : 0; } + SparseArrayNode *sparseBegin() { return sparse ? sparse->begin() : 0; } + SparseArrayNode *sparseEnd() { return sparse ? sparse->end() : 0; } + + void concat(const Array &other); + void sort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len); + Value indexOf(Value v, uint fromIndex, uint len, ExecutionContext *ctx, Object *o); +}; + +} +} + +#endif // QMAP_H diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp new file mode 100644 index 0000000000..cbf34d3077 --- /dev/null +++ b/src/v4/qv4arrayobject.cpp @@ -0,0 +1,798 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4arrayobject.h" +#include "qv4array.h" + +using namespace QQmlJS::VM; + +ArrayCtor::ArrayCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value ArrayCtor::call(ExecutionContext *ctx) +{ + ArrayObject *a = ctx->engine->newArrayObject(ctx); + Array &value = a->array; + if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { + bool ok; + uint len = ctx->argument(0).asArrayLength(ctx, &ok); + + if (!ok) { + ctx->throwRangeError(ctx->argument(0)); + return Value::undefinedValue(); + } + + value.setLengthUnchecked(len); + } else { + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { + value.set(i, ctx->argument(i)); + } + } + + return Value::fromObject(a); +} + +void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isArray"), method_isArray, 1); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(ctx, QStringLiteral("join"), method_join, 1); + defineDefaultProperty(ctx, QStringLiteral("pop"), method_pop, 0); + defineDefaultProperty(ctx, QStringLiteral("push"), method_push, 1); + defineDefaultProperty(ctx, QStringLiteral("reverse"), method_reverse, 0); + defineDefaultProperty(ctx, QStringLiteral("shift"), method_shift, 0); + defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); + defineDefaultProperty(ctx, QStringLiteral("sort"), method_sort, 1); + defineDefaultProperty(ctx, QStringLiteral("splice"), method_splice, 2); + defineDefaultProperty(ctx, QStringLiteral("unshift"), method_unshift, 1); + defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("every"), method_every, 1); + defineDefaultProperty(ctx, QStringLiteral("some"), method_some, 1); + defineDefaultProperty(ctx, QStringLiteral("forEach"), method_forEach, 1); + defineDefaultProperty(ctx, QStringLiteral("map"), method_map, 1); + defineDefaultProperty(ctx, QStringLiteral("filter"), method_filter, 1); + defineDefaultProperty(ctx, QStringLiteral("reduce"), method_reduce, 1); + defineDefaultProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 1); +} + +uint ArrayPrototype::getLength(ExecutionContext *ctx, Object *o) +{ + if (o->isArrayObject()) + return o->array.length(); + return o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); +} + +Value ArrayPrototype::method_isArray(ExecutionContext *ctx) +{ + Value arg = ctx->argument(0); + bool isArray = arg.asArrayObject(); + return Value::fromBoolean(isArray); +} + +Value ArrayPrototype::method_toString(ExecutionContext *ctx) +{ + return method_join(ctx); +} + +Value ArrayPrototype::method_toLocaleString(ExecutionContext *ctx) +{ + return method_toString(ctx); +} + +Value ArrayPrototype::method_concat(ExecutionContext *ctx) +{ + Array result; + + if (ArrayObject *instance = ctx->thisObject.asArrayObject()) + result = instance->array; + else { + QString v = ctx->thisObject.toString(ctx)->toQString(); + result.set(0, Value::fromString(ctx, v)); + } + + for (uint i = 0; i < ctx->argumentCount; ++i) { + quint32 k = result.length(); + Value arg = ctx->argument(i); + + if (ArrayObject *elt = arg.asArrayObject()) + result.concat(elt->array); + + else + result.set(k, arg); + } + + return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); +} + +Value ArrayPrototype::method_join(ExecutionContext *ctx) +{ + Value arg = ctx->argument(0); + + QString r4; + if (arg.isUndefined()) + r4 = QStringLiteral(","); + else + r4 = arg.toString(ctx)->toQString(); + + Value self = ctx->thisObject; + const Value length = self.property(ctx, ctx->engine->id_length); + const quint32 r2 = Value::toUInt32(length.isUndefined() ? 0 : length.toNumber(ctx)); + + static QSet visitedArrayElements; + + if (! r2 || visitedArrayElements.contains(self.objectValue())) + return Value::fromString(ctx, QString()); + + // avoid infinite recursion + visitedArrayElements.insert(self.objectValue()); + + QString R; + + // ### FIXME + if (ArrayObject *a = self.asArrayObject()) { + for (uint i = 0; i < a->array.length(); ++i) { + if (i) + R += r4; + + Value e = a->__get__(ctx, i); + if (! (e.isUndefined() || e.isNull())) + R += e.toString(ctx)->toQString(); + } + } else { + // + // crazy! + // + Value r6 = self.property(ctx, ctx->engine->identifier(QStringLiteral("0"))); + if (!(r6.isUndefined() || r6.isNull())) + R = r6.toString(ctx)->toQString(); + + for (quint32 k = 1; k < r2; ++k) { + R += r4; + + String *name = Value::fromDouble(k).toString(ctx); + Value r12 = self.property(ctx, name); + + if (! (r12.isUndefined() || r12.isNull())) + R += r12.toString(ctx)->toQString(); + } + } + + visitedArrayElements.remove(self.objectValue()); + return Value::fromString(ctx, R); +} + +Value ArrayPrototype::method_pop(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); + + if (!len) { + if (!instance->isArrayObject()) + instance->__put__(ctx, ctx->engine->id_length, Value::fromInt32(0)); + return Value::undefinedValue(); + } + + Value result = instance->__get__(ctx, len - 1); + + instance->__delete__(ctx, len - 1); + if (instance->isArrayObject()) + instance->array.setLengthUnchecked(len - 1); + else + instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - 1)); + return result; +} + +Value ArrayPrototype::method_push(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); + + if (len + ctx->argumentCount < len) { + // ughh... + double l = len; + for (double i = 0; i < ctx->argumentCount; ++i) { + Value idx = Value::fromDouble(l + i); + instance->__put__(ctx, idx.toString(ctx), ctx->argument(i)); + } + double newLen = l + ctx->argumentCount; + if (!instance->isArrayObject()) + instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); + else + ctx->throwRangeError(Value::fromString(ctx, QStringLiteral("Array.prototype.push: Overflow"))); + return Value::fromDouble(newLen); + } + + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->array.length()) + protoHasArray = true; + + if (!protoHasArray && len == instance->array.length()) { + for (uint i = 0; i < ctx->argumentCount; ++i) { + Value v = ctx->argument(i); + instance->array.push_back(v); + } + } else { + for (uint i = 0; i < ctx->argumentCount; ++i) + instance->__put__(ctx, len + i, ctx->argument(i)); + } + uint newLen = len + ctx->argumentCount; + if (!instance->isArrayObject()) + instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); + + if (newLen < INT_MAX) + return Value::fromInt32(newLen); + return Value::fromDouble((double)newLen); + +} + +Value ArrayPrototype::method_reverse(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint length = getLength(ctx, instance); + + int lo = 0, hi = length - 1; + + for (; lo < hi; ++lo, --hi) { + bool loExists, hiExists; + Value lval = instance->__get__(ctx, lo, &loExists); + Value hval = instance->__get__(ctx, hi, &hiExists); + if (hiExists) + instance->__put__(ctx, lo, hval); + else + instance->__delete__(ctx, lo); + if (loExists) + instance->__put__(ctx, hi, lval); + else + instance->__delete__(ctx, hi); + } + return Value::fromObject(instance); +} + +Value ArrayPrototype::method_shift(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); + + if (!len) { + if (!instance->isArrayObject()) + instance->__put__(ctx, ctx->engine->id_length, Value::fromInt32(0)); + return Value::undefinedValue(); + } + + Value result = instance->getValueChecked(ctx, instance->array.front()); + + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->array.length()) + protoHasArray = true; + + if (!protoHasArray && len >= instance->array.length()) { + instance->array.pop_front(); + } else { + // do it the slow way + for (uint k = 1; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (exists) + instance->__put__(ctx, k - 1, v); + else + instance->__delete__(ctx, k - 1); + } + instance->__delete__(ctx, len - 1); + } + + if (!instance->isArrayObject()) + instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - 1)); + return result; +} + +Value ArrayPrototype::method_slice(ExecutionContext *ctx) +{ + Object *o = ctx->thisObject.toObject(ctx).objectValue(); + + Array result; + uint len = o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); + double s = ctx->argument(0).toInteger(ctx); + uint start; + if (s < 0) + start = (uint)qMax(len + s, 0.); + else if (s > len) + start = len; + else + start = (uint) s; + uint end = len; + if (!ctx->argument(1).isUndefined()) { + double e = ctx->argument(1).toInteger(ctx); + if (e < 0) + end = (uint)qMax(len + e, 0.); + else if (e > len) + end = len; + else + end = (uint) e; + } + + uint n = 0; + for (uint i = start; i < end; ++i) { + bool exists; + Value v = o->__get__(ctx, i, &exists); + if (exists) + result.set(n, v); + ++n; + } + return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); +} + +Value ArrayPrototype::method_sort(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + Value comparefn = ctx->argument(0); + instance->array.sort(ctx, instance, comparefn, len); + return ctx->thisObject; +} + +Value ArrayPrototype::method_splice(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); + + ArrayObject *newArray = ctx->engine->newArrayObject(ctx); + + double rs = ctx->argument(0).toInteger(ctx); + uint start; + if (rs < 0) + start = (uint) qMax(0., len + rs); + else + start = (uint) qMin(rs, (double)len); + + uint deleteCount = (uint)qMin(qMax(ctx->argument(1).toInteger(ctx), 0.), (double)(len - start)); + + newArray->array.values.resize(deleteCount); + PropertyDescriptor *pd = newArray->array.values.data(); + for (uint i = 0; i < deleteCount; ++i) { + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Enabled; + pd->configurable = PropertyDescriptor::Enabled; + pd->value = instance->__get__(ctx, start + i); + ++pd; + } + newArray->array.setLengthUnchecked(deleteCount); + + uint itemCount = ctx->argumentCount < 2 ? 0 : ctx->argumentCount - 2; + + if (itemCount < deleteCount) { + for (uint k = start; k < len - deleteCount; ++k) { + bool exists; + Value v = instance->__get__(ctx, k + deleteCount, &exists); + if (exists) + instance->array.set(k + itemCount, v); + else + instance->__delete__(ctx, k + itemCount); + } + for (uint k = len; k > len - deleteCount + itemCount; --k) + instance->__delete__(ctx, k - 1); + } else if (itemCount > deleteCount) { + uint k = len - deleteCount; + while (k > start) { + bool exists; + Value v = instance->__get__(ctx, k + deleteCount - 1, &exists); + if (exists) + instance->array.set(k + itemCount - 1, v); + else + instance->__delete__(ctx, k + itemCount - 1); + --k; + } + } + + for (uint i = 0; i < itemCount; ++i) + instance->array.set(start + i, ctx->argument(i + 2)); + + ctx->strictMode = true; + instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - deleteCount + itemCount)); + + return Value::fromObject(newArray); +} + +Value ArrayPrototype::method_unshift(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); + + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->array.length()) + protoHasArray = true; + + if (!protoHasArray && len >= instance->array.length()) { + for (int i = ctx->argumentCount - 1; i >= 0; --i) { + Value v = ctx->argument(i); + instance->array.push_front(v); + } + } else { + for (uint k = len; k > 0; --k) { + bool exists; + Value v = instance->__get__(ctx, k - 1, &exists); + if (exists) + instance->__put__(ctx, k + ctx->argumentCount - 1, v); + else + instance->__delete__(ctx, k + ctx->argumentCount - 1); + } + for (uint i = 0; i < ctx->argumentCount; ++i) + instance->__put__(ctx, i, ctx->argument(i)); + } + uint newLen = len + ctx->argumentCount; + if (!instance->isArrayObject()) + instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); + + if (newLen < INT_MAX) + return Value::fromInt32(newLen); + return Value::fromDouble((double)newLen); +} + +Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); + if (!len) + return Value::fromInt32(-1); + + Value searchValue; + uint fromIndex = 0; + + if (ctx->argumentCount >= 1) + searchValue = ctx->argument(0); + else + searchValue = Value::undefinedValue(); + + if (ctx->argumentCount >= 2) { + double f = ctx->argument(1).toInteger(ctx); + if (f >= len) + return Value::fromInt32(-1); + if (f < 0) + f = qMax(len + f, 0.); + fromIndex = (uint) f; + } + + if (instance->isStringObject()) { + for (uint k = fromIndex; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (exists && __qmljs_strict_equal(v, searchValue)) + return Value::fromDouble(k); + } + return Value::fromInt32(-1); + } + + return instance->array.indexOf(searchValue, fromIndex, len, ctx, instance); +} + +Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + uint len = getLength(ctx, instance); + if (!len) + return Value::fromInt32(-1); + + Value searchValue; + uint fromIndex = len; + + if (ctx->argumentCount >= 1) + searchValue = ctx->argument(0); + else + searchValue = Value::undefinedValue(); + + if (ctx->argumentCount >= 2) { + double f = ctx->argument(1).toInteger(ctx); + if (f > 0) + f = qMin(f, (double)(len - 1)); + else if (f < 0) { + f = len + f; + if (f < 0) + return Value::fromInt32(-1); + } + fromIndex = (uint) f + 1; + } + + for (uint k = fromIndex; k > 0;) { + --k; + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (exists && __qmljs_strict_equal(v, searchValue)) + return Value::fromDouble(k); + } + return Value::fromInt32(-1); +} + +Value ArrayPrototype::method_every(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + Value thisArg = ctx->argument(1); + + bool ok = true; + for (uint k = 0; ok && k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = callback->call(ctx, thisArg, args, 3); + ok = __qmljs_to_boolean(r, ctx); + } + return Value::fromBoolean(ok); +} + +Value ArrayPrototype::method_some(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + Value thisArg = ctx->argument(1); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = callback->call(ctx, thisArg, args, 3); + if (__qmljs_to_boolean(r, ctx)) + return Value::fromBoolean(true); + } + return Value::fromBoolean(false); +} + +Value ArrayPrototype::method_forEach(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + Value thisArg = ctx->argument(1); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + callback->call(ctx, thisArg, args, 3); + } + return Value::undefinedValue(); +} + +Value ArrayPrototype::method_map(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + Value thisArg = ctx->argument(1); + + ArrayObject *a = ctx->engine->newArrayObject(ctx); + a->array.setLength(len); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value mapped = callback->call(ctx, thisArg, args, 3); + a->array.set(k, mapped); + } + return Value::fromObject(a); +} + +Value ArrayPrototype::method_filter(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + Value thisArg = ctx->argument(1); + + ArrayObject *a = ctx->engine->newArrayObject(ctx); + + uint to = 0; + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->__get__(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value selected = callback->call(ctx, thisArg, args, 3); + if (__qmljs_to_boolean(selected, ctx)) { + a->array.set(to, v); + ++to; + } + } + return Value::fromObject(a); +} + +Value ArrayPrototype::method_reduce(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + uint k = 0; + Value acc; + if (ctx->argumentCount > 1) { + acc = ctx->argument(1); + } else { + bool kPresent = false; + while (k < len && !kPresent) { + Value v = instance->__get__(ctx, k, &kPresent); + if (kPresent) + acc = v; + ++k; + } + if (!kPresent) + __qmljs_throw_type_error(ctx); + } + + while (k < len) { + bool kPresent; + Value v = instance->__get__(ctx, k, &kPresent); + if (kPresent) { + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k); + args[3] = ctx->thisObject; + acc = callback->call(ctx, Value::undefinedValue(), args, 4); + } + ++k; + } + return acc; +} + +Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) +{ + Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + __qmljs_throw_type_error(ctx); + + if (len == 0) { + if (ctx->argumentCount == 1) + __qmljs_throw_type_error(ctx); + return ctx->argument(1); + } + + uint k = len; + Value acc; + if (ctx->argumentCount > 1) { + acc = ctx->argument(1); + } else { + bool kPresent = false; + while (k > 0 && !kPresent) { + Value v = instance->__get__(ctx, k - 1, &kPresent); + if (kPresent) + acc = v; + --k; + } + if (!kPresent) + __qmljs_throw_type_error(ctx); + } + + while (k > 0) { + bool kPresent; + Value v = instance->__get__(ctx, k - 1, &kPresent); + if (kPresent) { + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k - 1); + args[3] = ctx->thisObject; + acc = callback->call(ctx, Value::undefinedValue(), args, 4); + } + --k; + } + return acc; +} + diff --git a/src/v4/qv4arrayobject.h b/src/v4/qv4arrayobject.h new file mode 100644 index 0000000000..8a15546c1c --- /dev/null +++ b/src/v4/qv4arrayobject.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ARRAYOBJECT_H +#define QV4ARRAYOBJECT_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include + +namespace QQmlJS { +namespace VM { + + +struct ArrayCtor: FunctionObject +{ + ArrayCtor(ExecutionContext *scope); + + virtual Value call(ExecutionContext *ctx); +}; + +struct ArrayPrototype: ArrayObject +{ + ArrayPrototype(ExecutionContext *context) : ArrayObject(context) {} + + void init(ExecutionContext *ctx, const Value &ctor); + + static uint getLength(ExecutionContext *ctx, Object *o); + + static Value method_isArray(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *ctx); + static Value method_toLocaleString(ExecutionContext *ctx); + static Value method_concat(ExecutionContext *ctx); + static Value method_join(ExecutionContext *ctx); + static Value method_pop(ExecutionContext *ctx); + static Value method_push(ExecutionContext *ctx); + static Value method_reverse(ExecutionContext *ctx); + static Value method_shift(ExecutionContext *ctx); + static Value method_slice(ExecutionContext *ctx); + static Value method_sort(ExecutionContext *ctx); + static Value method_splice(ExecutionContext *ctx); + static Value method_unshift(ExecutionContext *ctx); + static Value method_indexOf(ExecutionContext *ctx); + static Value method_lastIndexOf(ExecutionContext *ctx); + static Value method_every(ExecutionContext *ctx); + static Value method_some(ExecutionContext *ctx); + static Value method_forEach(ExecutionContext *ctx); + static Value method_map(ExecutionContext *ctx); + static Value method_filter(ExecutionContext *ctx); + static Value method_reduce(ExecutionContext *ctx); + static Value method_reduceRight(ExecutionContext *ctx); +}; + + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/v4/qv4booleanobject.cpp b/src/v4/qv4booleanobject.cpp new file mode 100644 index 0000000000..9df0f23a6a --- /dev/null +++ b/src/v4/qv4booleanobject.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4booleanobject.h" + +using namespace QQmlJS::VM; + +BooleanCtor::BooleanCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value BooleanCtor::construct(ExecutionContext *ctx) +{ + const double n = ctx->argument(0).toBoolean(ctx); + return Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n))); +} + +Value BooleanCtor::call(ExecutionContext *ctx) +{ + bool value = ctx->argumentCount ? ctx->argument(0).toBoolean(ctx) : 0; + return Value::fromBoolean(value); +} + +void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); +} + +Value BooleanPrototype::method_toString(ExecutionContext *ctx) +{ + bool result; + if (ctx->thisObject.isBoolean()) { + result = ctx->thisObject.booleanValue(); + } else { + BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); + if (!thisObject) + ctx->throwTypeError(); + result = thisObject->value.booleanValue(); + } + + return Value::fromString(ctx, QLatin1String(result ? "true" : "false")); +} + +Value BooleanPrototype::method_valueOf(ExecutionContext *ctx) +{ + BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); + if (!thisObject) + ctx->throwTypeError(); + + return thisObject->value; +} diff --git a/src/v4/qv4booleanobject.h b/src/v4/qv4booleanobject.h new file mode 100644 index 0000000000..44d87b1d50 --- /dev/null +++ b/src/v4/qv4booleanobject.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4BOOLEANOBJECT_H +#define QBOOLEANOBJECT_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include + +namespace QQmlJS { +namespace VM { + +struct BooleanCtor: FunctionObject +{ + BooleanCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct BooleanPrototype: BooleanObject +{ + BooleanPrototype(): BooleanObject(Value::fromBoolean(false)) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(ExecutionContext *ctx); + static Value method_valueOf(ExecutionContext *ctx); +}; + + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp new file mode 100644 index 0000000000..4c4f7dd1d6 --- /dev/null +++ b/src/v4/qv4codegen.cpp @@ -0,0 +1,2614 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4codegen_p.h" +#include "debugging.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace QQmlJS; +using namespace AST; + +namespace { +QTextStream qout(stdout, QIODevice::WriteOnly); + +void dfs(IR::BasicBlock *block, + QSet *V, + QVector *blocks) +{ + if (! V->contains(block)) { + V->insert(block); + + foreach (IR::BasicBlock *succ, block->out) + dfs(succ, V, blocks); + + blocks->append(block); + } +} + +struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor +{ + IR::Function *_function; + IR::Stmt *_stmt; + + ComputeUseDef(IR::Function *function) + : _function(function) + , _stmt(0) {} + + void operator()(IR::Stmt *s) { + assert(! s->d); + s->d = new IR::Stmt::Data; + qSwap(_stmt, s); + _stmt->accept(this); + qSwap(_stmt, s); + } + + virtual void visitConst(IR::Const *) {} + virtual void visitString(IR::String *) {} + virtual void visitRegExp(IR::RegExp *) {} + virtual void visitName(IR::Name *) {} + virtual void visitClosure(IR::Closure *) {} + virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); } + virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(IR::Member *e) { e->base->accept(this); } + virtual void visitExp(IR::Exp *s) { s->expr->accept(this); } + virtual void visitEnter(IR::Enter *) {} + virtual void visitLeave(IR::Leave *) {} + virtual void visitJump(IR::Jump *) {} + virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); } + virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } + + virtual void visitTemp(IR::Temp *e) { + if (e->index < 0) + return; + + if (! _stmt->d->uses.contains(e->index)) + _stmt->d->uses.append(e->index); + } + + virtual void visitCall(IR::Call *e) { + e->base->accept(this); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(IR::New *e) { + e->base->accept(this); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitMove(IR::Move *s) { + if (IR::Temp *t = s->target->asTemp()) { + if (t->index >= 0) { + if (! _stmt->d->defs.contains(t->index)) + _stmt->d->defs.append(t->index); + } + } else { + s->target->accept(this); + } + s->source->accept(this); + } +}; + +void liveness(IR::Function *function) +{ + QSet V; + QVector blocks; + + ComputeUseDef computeUseDef(function); + foreach (IR::BasicBlock *block, function->basicBlocks) { + foreach (IR::Stmt *s, block->statements) + computeUseDef(s); + } + + dfs(function->basicBlocks.at(0), &V, &blocks); + + bool changed; + do { + changed = false; + + foreach (IR::BasicBlock *block, blocks) { + const QBitArray previousLiveIn = block->liveIn; + const QBitArray previousLiveOut = block->liveOut; + QBitArray live(function->tempCount); + foreach (IR::BasicBlock *succ, block->out) + live |= succ->liveIn; + block->liveOut = live; + for (int i = block->statements.size() - 1; i != -1; --i) { + IR::Stmt *s = block->statements.at(i); + s->d->liveOut = live; + foreach (unsigned d, s->d->defs) + live.clearBit(d); + foreach (unsigned u, s->d->uses) + live.setBit(u); + s->d->liveIn = live; + } + block->liveIn = live; + if (! changed) { + if (previousLiveIn != block->liveIn || previousLiveOut != block->liveOut) + changed = true; + } + } + } while (changed); +} + +} // end of anonymous namespace + +class Codegen::ScanFunctions: Visitor +{ +public: + ScanFunctions(Codegen *cg) + : _cg(cg) + , _env(0) + { + } + + void operator()(Node *node) + { + if (node) + node->accept(this); + } + + inline void enterEnvironment(Node *node) + { + Environment *e = _cg->newEnvironment(node, _env); + if (!e->isStrict) + e->isStrict = _cg->_strictMode; + _envStack.append(e); + _env = e; + } + + inline void leaveEnvironment() + { + _envStack.pop(); + _env = _envStack.isEmpty() ? 0 : _envStack.top(); + } + +protected: + using Visitor::visit; + using Visitor::endVisit; + + void checkDirectivePrologue(SourceElements *ast) + { + for (SourceElements *it = ast; it; it = it->next) { + if (StatementSourceElement *stmt = cast(it->element)) { + if (ExpressionStatement *expr = cast(stmt->statement)) { + if (StringLiteral *strLit = cast(expr->expression)) { + if (strLit->value == QLatin1String("use strict")) { + _env->isStrict = true; + } else { + // TODO: give a warning. + } + continue; + } + } + } + + break; + } + } + + void checkName(const QStringRef &name, const SourceLocation &loc) + { + if (_env->isStrict) { + if (name == QLatin1String("implements") + || name == QLatin1String("interface") + || name == QLatin1String("let") + || name == QLatin1String("package") + || name == QLatin1String("private") + || name == QLatin1String("protected") + || name == QLatin1String("public") + || name == QLatin1String("static") + || name == QLatin1String("yield")) { + _cg->throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Unexpected strict mode reserved word")); + } + } + } + void checkForArguments(AST::FormalParameterList *parameters) + { + while (parameters) { + if (parameters->name == QStringLiteral("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + parameters = parameters->next; + } + } + + virtual bool visit(Program *ast) + { + enterEnvironment(ast); + checkDirectivePrologue(ast->elements); + return true; + } + + virtual void endVisit(Program *) + { + leaveEnvironment(); + } + + virtual bool visit(CallExpression *ast) + { + if (! _env->hasDirectEval) { + if (IdentifierExpression *id = cast(ast->base)) { + if (id->name == QStringLiteral("eval")) { + if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown) + _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; + _env->hasDirectEval = true; + } + } + } + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + return true; + } + + virtual bool visit(NewMemberExpression *ast) + { + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + return true; + } + + virtual bool visit(VariableDeclaration *ast) + { + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); + checkName(ast->name, ast->identifierToken); + if (ast->name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration); + return true; + } + + virtual bool visit(IdentifierExpression *ast) + { + checkName(ast->name, ast->identifierToken); + if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; + return true; + } + + virtual bool visit(FunctionExpression *ast) + { + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Function name may not be eval or arguments in strict mode")); + enterFunction(ast, ast->name.toString(), ast->formals, ast->body); + return true; + } + + virtual void endVisit(FunctionExpression *) + { + leaveEnvironment(); + } + + virtual bool visit(PropertyGetterSetter *ast) + { + enterFunction(ast, QString(), ast->formals, ast->functionBody); + return true; + } + + virtual void endVisit(PropertyGetterSetter *) + { + leaveEnvironment(); + } + + virtual bool visit(FunctionDeclaration *ast) + { + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Function name may not be eval or arguments in strict mode")); + enterFunction(ast, ast->name.toString(), ast->formals, ast->body, ast); + return true; + } + + virtual void endVisit(FunctionDeclaration *) + { + leaveEnvironment(); + } + + virtual bool visit(WithStatement *ast) + { + if (_env->isStrict) { + _cg->throwSyntaxError(ast->withToken, QCoreApplication::translate("qv4codegen", "'with' statement is not allowed in strict mode")); + return false; + } + + return true; + } + +private: + void enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionDeclaration *decl = 0) + { + bool wasStrict = false; + if (_env) { + _env->hasNestedFunctions = true; + _env->enter(name, Environment::FunctionDefinition, decl); + if (name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + wasStrict = _env->isStrict; + } + + enterEnvironment(ast); + checkForArguments(formals); + + if (body) + checkDirectivePrologue(body->elements); + + if (wasStrict || _env->isStrict) { + QStringList args; + for (FormalParameterList *it = formals; it; it = it->next) { + QString arg = it->name.toString(); + if (args.contains(arg)) + _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "Duplicate parameter name '%1' in strict mode").arg(arg)); + if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) + _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "'%1' cannot be used as parameter name in strict mode").arg(arg)); + args += arg; + } + } + } + + + Codegen *_cg; + Environment *_env; + QStack _envStack; +}; + +Codegen::Codegen(VM::ExecutionContext *context, bool strict) + : _module(0) + , _function(0) + , _block(0) + , _exitBlock(0) + , _throwBlock(0) + , _returnAddress(0) + , _mode(GlobalCode) + , _env(0) + , _loop(0) + , _labelledStatement(0) + , _scopeAndFinally(0) + , _context(context) + , _strictMode(strict) + , _debugger(context->engine->debugger) + , _errorHandler(0) +{ +} + +Codegen::Codegen(ErrorHandler *errorHandler, bool strictMode) + : _module(0) + , _function(0) + , _block(0) + , _exitBlock(0) + , _throwBlock(0) + , _returnAddress(0) + , _mode(GlobalCode) + , _env(0) + , _loop(0) + , _labelledStatement(0) + , _scopeAndFinally(0) + , _context(0) + , _strictMode(strictMode) + , _debugger(0) + , _errorHandler(errorHandler) +{ +} + +IR::Function *Codegen::operator()(const QString &fileName, Program *node, + IR::Module *module, Mode mode, + const QStringList &inheritedLocals) +{ + assert(node); + + _fileName = fileName; + _module = module; + _env = 0; + + ScanFunctions scan(this); + scan(node); + + IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, + node->elements, mode, inheritedLocals); + if (_debugger) { + if (node->elements->element) { + SourceLocation loc = node->elements->element->firstSourceLocation(); + _debugger->setSourceLocation(globalCode, loc.startLine, loc.startColumn); + } + } + + foreach (IR::Function *function, _module->functions) { + linearize(function); + } + + qDeleteAll(_envMap); + _envMap.clear(); + + return globalCode; +} + +IR::Function *Codegen::operator()(const QString &fileName, AST::FunctionExpression *ast, IR::Module *module) +{ + _fileName = fileName; + _module = module; + _env = 0; + + ScanFunctions scan(this); + // fake a global environment + scan.enterEnvironment(0); + scan(ast); + scan.leaveEnvironment(); + + IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + if (_debugger) + _debugger->setSourceLocation(function, ast->functionToken.startLine, ast->functionToken.startColumn); + + foreach (IR::Function *function, _module->functions) { + linearize(function); + } + + qDeleteAll(_envMap); + _envMap.clear(); + + return function; +} + + +void Codegen::enterEnvironment(Node *node) +{ + _env = _envMap.value(node); + assert(_env); +} + +void Codegen::leaveEnvironment() +{ + assert(_env); + _env = _env->parent; +} + +void Codegen::enterLoop(Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock) +{ + _loop = new Loop(node, breakBlock, continueBlock, _loop); + _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement + _loop->scopeAndFinally = _scopeAndFinally; + _labelledStatement = 0; +} + +void Codegen::leaveLoop() +{ + Loop *current = _loop; + _loop = _loop->parent; + delete current; +} + +IR::Expr *Codegen::member(IR::Expr *base, const QString *name) +{ + if (base->asTemp() /*|| base->asName()*/) + return _block->MEMBER(base->asTemp(), name); + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), base); + return _block->MEMBER(_block->TEMP(t), name); + } +} + +IR::Expr *Codegen::subscript(IR::Expr *base, IR::Expr *index) +{ + if (! base->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), base); + base = _block->TEMP(t); + } + + if (! index->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), index); + index = _block->TEMP(t); + } + + assert(base->asTemp() && index->asTemp()); + return _block->SUBSCRIPT(base->asTemp(), index->asTemp()); +} + +IR::Expr *Codegen::argument(IR::Expr *expr) +{ + if (expr && ! expr->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + return expr; +} + +// keeps references alive, converts other expressions to temps +IR::Expr *Codegen::reference(IR::Expr *expr) +{ + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember() && !expr->asSubscript()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + return expr; +} + +IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr) +{ + if (IR::Const *c = expr->asConst()) { + if (c->type == IR::NumberType) { + switch (op) { + case IR::OpNot: + return _block->CONST(IR::BoolType, !c->value); + case IR::OpUMinus: + return _block->CONST(IR::NumberType, -c->value); + case IR::OpUPlus: + return expr; + case IR::OpCompl: + return _block->CONST(IR::NumberType, ~VM::Value::toInt32(c->value)); + case IR::OpIncrement: + return _block->CONST(IR::NumberType, c->value + 1); + case IR::OpDecrement: + return _block->CONST(IR::NumberType, c->value - 1); + default: + break; + } + } + } + if (! expr->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + assert(expr->asTemp()); + return _block->UNOP(op, expr->asTemp()); +} + +IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) +{ + if (IR::Const *c1 = left->asConst()) { + if (IR::Const *c2 = right->asConst()) { + if (c1->type == IR::NumberType && c2->type == IR::NumberType) { + switch (op) { + case IR::OpAdd: return _block->CONST(IR::NumberType, c1->value + c2->value); + case IR::OpAnd: return _block->CONST(IR::BoolType, c1->value ? c2->value : 0); + case IR::OpBitAnd: return _block->CONST(IR::NumberType, int(c1->value) & int(c2->value)); + case IR::OpBitOr: return _block->CONST(IR::NumberType, int(c1->value) | int(c2->value)); + case IR::OpBitXor: return _block->CONST(IR::NumberType, int(c1->value) ^ int(c2->value)); + case IR::OpDiv: return _block->CONST(IR::NumberType, c1->value / c2->value); + case IR::OpEqual: return _block->CONST(IR::BoolType, c1->value == c2->value); + case IR::OpNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value); + case IR::OpStrictEqual: return _block->CONST(IR::BoolType, c1->value == c2->value); + case IR::OpStrictNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value); + case IR::OpGe: return _block->CONST(IR::BoolType, c1->value >= c2->value); + case IR::OpGt: return _block->CONST(IR::BoolType, c1->value > c2->value); + case IR::OpLe: return _block->CONST(IR::BoolType, c1->value <= c2->value); + case IR::OpLt: return _block->CONST(IR::BoolType, c1->value < c2->value); + case IR::OpLShift: return _block->CONST(IR::NumberType, VM::Value::toInt32(c1->value) << (VM::Value::toUInt32(c2->value) & 0x1f)); + case IR::OpMod: return _block->CONST(IR::NumberType, ::fmod(c1->value, c2->value)); + case IR::OpMul: return _block->CONST(IR::NumberType, c1->value * c2->value); + case IR::OpOr: return _block->CONST(IR::NumberType, c1->value ? c1->value : c2->value); + case IR::OpRShift: return _block->CONST(IR::NumberType, VM::Value::toInt32(c1->value) >> (VM::Value::toUInt32(c2->value) & 0x1f)); + case IR::OpSub: return _block->CONST(IR::NumberType, c1->value - c2->value); + case IR::OpURShift: return _block->CONST(IR::NumberType,VM::Value::toUInt32(c1->value) >> (VM::Value::toUInt32(c2->value) & 0x1f)); + + case IR::OpInstanceof: + case IR::OpIn: + assert(!"unreachabe"); + break; + + case IR::OpIfTrue: // unary ops + case IR::OpNot: + case IR::OpUMinus: + case IR::OpUPlus: + case IR::OpCompl: + case IR::OpIncrement: + case IR::OpDecrement: + case IR::OpInvalid: + break; + } + } + } + } else if (op == IR::OpAdd) { + if (IR::String *s1 = left->asString()) { + if (IR::String *s2 = right->asString()) { + return _block->STRING(_function->newString(*s1->value + *s2->value)); + } + } + } + + if (!left->asTemp() && !left->asConst()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + if (!right->asTemp() && !right->asConst()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), right); + right = _block->TEMP(t); + } + + assert(left->asTemp() || left->asConst()); + assert(right->asTemp() || right->asConst()); + + return _block->BINOP(op, left, right); +} + +IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args) +{ + base = reference(base); + return _block->CALL(base, args); +} + +void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) +{ + assert(target->isLValue()); + + if (!source->asTemp() && !source->asConst() && (op != IR::OpInvalid || ! target->asTemp())) { + unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), source); + source = _block->TEMP(t); + } + + _block->MOVE(target, source, op); +} + +void Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) +{ + if (! (cond->asTemp() || cond->asBinop())) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), cond); + cond = _block->TEMP(t); + } + _block->CJUMP(cond, iftrue, iffalse); +} + +void Codegen::accept(Node *node) +{ + if (node) + node->accept(this); +} + +void Codegen::statement(Statement *ast) +{ + accept(ast); +} + +void Codegen::statement(ExpressionNode *ast) +{ + if (! ast) { + return; + } else { + Result r(nx); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + if (r.format == ex) { + if (r->asCall()) { + _block->EXP(*r); // the nest nx representation for calls is EXP(CALL(c..)) + } else if (r->asTemp()) { + // there is nothing to do + } else { + unsigned t = _block->newTemp(); + move(_block->TEMP(t), *r); + } + } + } +} + +void Codegen::condition(ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) +{ + if (ast) { + Result r(iftrue, iffalse); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + if (r.format == ex) { + cjump(*r, r.iftrue, r.iffalse); + } + } +} + +Codegen::Result Codegen::expression(ExpressionNode *ast) +{ + Result r; + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + } + return r; +} + +QString Codegen::propertyName(PropertyName *ast) +{ + QString p; + if (ast) { + qSwap(_property, p); + accept(ast); + qSwap(_property, p); + } + return p; +} + +Codegen::Result Codegen::sourceElement(SourceElement *ast) +{ + Result r(nx); + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + } + return r; +} + +Codegen::UiMember Codegen::uiObjectMember(UiObjectMember *ast) +{ + UiMember m; + if (ast) { + qSwap(_uiMember, m); + accept(ast); + qSwap(_uiMember, m); + } + return m; +} + +void Codegen::functionBody(FunctionBody *ast) +{ + if (ast) + sourceElements(ast->elements); +} + +void Codegen::program(Program *ast) +{ + if (ast) { + sourceElements(ast->elements); + } +} + +void Codegen::sourceElements(SourceElements *ast) +{ + for (SourceElements *it = ast; it; it = it->next) { + sourceElement(it->element); + } +} + +void Codegen::variableDeclaration(VariableDeclaration *ast) +{ + IR::Expr *initializer = 0; + if (!ast->expression) + return; + Result expr = expression(ast->expression); + assert(expr.code); + initializer = *expr; + + if (! _env->parent || _function->insideWith) { + // it's global code. + move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), initializer); + } else { + const int index = _env->findMember(ast->name.toString()); + assert(index != -1); + move(_block->TEMP(index), initializer); + } +} + +void Codegen::variableDeclarationList(VariableDeclarationList *ast) +{ + for (VariableDeclarationList *it = ast; it; it = it->next) { + variableDeclaration(it->declaration); + } +} + + +bool Codegen::visit(ArgumentList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseBlock *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseClause *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseClauses *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Catch *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(DefaultClause *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(ElementList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Elision *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Finally *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(FormalParameterList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(FunctionBody *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Program *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyAssignmentList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyNameAndValue *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyGetterSetter *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(SourceElements *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(StatementList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiArrayMemberList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiImport *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiImportList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiObjectInitializer *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiObjectMemberList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiParameterList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiProgram *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiQualifiedId *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(VariableDeclaration *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(VariableDeclarationList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Expression *ast) +{ + statement(ast->left); + accept(ast->right); + return false; +} + +bool Codegen::visit(ArrayLiteral *ast) +{ + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Array"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); + int index = 0; + unsigned value = 0; + for (ElementList *it = ast->elements; it; it = it->next) { + for (Elision *elision = it->elision; elision; elision = elision->next) + ++index; + Result expr = expression(it->expression); + + IR::ExprList *args = _function->New(); + IR::ExprList *current = args; + current->expr = _block->TEMP(t); + current->next = _function->New(); + current = current->next; + current->expr = _block->CONST(IR::NumberType, index); + current->next = _function->New(); + current = current->next; + + if (!value) + value = _block->newTemp(); + move(_block->TEMP(value), *expr); + // __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) + current->expr = _block->TEMP(value); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_define_array_property, 0, 0), args)); + + ++index; + } + if (ast->elision) { + for (Elision *elision = ast->elision->next; elision; elision = elision->next) + ++index; + // ### the new string leaks + move(member(_block->TEMP(t), _function->newString(QStringLiteral("length"))), _block->CONST(IR::NumberType, index + 1)); + } + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(ArrayMemberExpression *ast) +{ + Result base = expression(ast->base); + Result index = expression(ast->expression); + _expr.code = subscript(*base, *index); + return false; +} + +static IR::AluOp baseOp(int op) +{ + switch ((QSOperator::Op) op) { + case QSOperator::InplaceAnd: return IR::OpBitAnd; + case QSOperator::InplaceSub: return IR::OpSub; + case QSOperator::InplaceDiv: return IR::OpDiv; + case QSOperator::InplaceAdd: return IR::OpAdd; + case QSOperator::InplaceLeftShift: return IR::OpLShift; + case QSOperator::InplaceMod: return IR::OpMod; + case QSOperator::InplaceMul: return IR::OpMul; + case QSOperator::InplaceOr: return IR::OpBitOr; + case QSOperator::InplaceRightShift: return IR::OpRShift; + case QSOperator::InplaceURightShift: return IR::OpURShift; + case QSOperator::InplaceXor: return IR::OpBitXor; + default: return IR::OpInvalid; + } +} + +bool Codegen::visit(BinaryExpression *ast) +{ + if (ast->op == QSOperator::And) { + if (_expr.accept(cx)) { + IR::BasicBlock *iftrue = _function->newBasicBlock(); + condition(ast->left, iftrue, _expr.iffalse); + _block = iftrue; + condition(ast->right, _expr.iftrue, _expr.iffalse); + } else { + IR::BasicBlock *iftrue = _function->newBasicBlock(); + IR::BasicBlock *endif = _function->newBasicBlock(); + + const unsigned r = _block->newTemp(); + + move(_block->TEMP(r), *expression(ast->left)); + cjump(_block->TEMP(r), iftrue, endif); + _block = iftrue; + move(_block->TEMP(r), *expression(ast->right)); + _block->JUMP(endif); + + _expr.code = _block->TEMP(r); + _block = endif; + } + return false; + } else if (ast->op == QSOperator::Or) { + if (_expr.accept(cx)) { + IR::BasicBlock *iffalse = _function->newBasicBlock(); + condition(ast->left, _expr.iftrue, iffalse); + _block = iffalse; + condition(ast->right, _expr.iftrue, _expr.iffalse); + } else { + IR::BasicBlock *iffalse = _function->newBasicBlock(); + IR::BasicBlock *endif = _function->newBasicBlock(); + + const unsigned r = _block->newTemp(); + move(_block->TEMP(r), *expression(ast->left)); + cjump(_block->TEMP(r), endif, iffalse); + _block = iffalse; + move(_block->TEMP(r), *expression(ast->right)); + _block->JUMP(endif); + + _block = endif; + _expr.code = _block->TEMP(r); + } + return false; + } + + IR::Expr* left = *expression(ast->left); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()); + + switch (ast->op) { + case QSOperator::Or: + case QSOperator::And: + break; + + case QSOperator::Assign: { + IR::Expr* right = *expression(ast->right); + if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) + throwReferenceError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of assignment operator is not an lvalue")); + + if (_expr.accept(nx)) { + move(left, right); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), right); + move(left, _block->TEMP(t)); + _expr.code = left; + } + break; + } + + case QSOperator::InplaceAnd: + case QSOperator::InplaceSub: + case QSOperator::InplaceDiv: + case QSOperator::InplaceAdd: + case QSOperator::InplaceLeftShift: + case QSOperator::InplaceMod: + case QSOperator::InplaceMul: + case QSOperator::InplaceOr: + case QSOperator::InplaceRightShift: + case QSOperator::InplaceURightShift: + case QSOperator::InplaceXor: { + IR::Expr* right = *expression(ast->right); + if (!left->isLValue()) + throwSyntaxError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of inplace operator is not an lvalue")); + + if (_expr.accept(nx)) { + move(left, right, baseOp(ast->op)); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), right); + move(left, _block->TEMP(t), baseOp(ast->op)); + _expr.code = left; + } + break; + } + + case QSOperator::In: + case QSOperator::InstanceOf: + case QSOperator::Equal: + case QSOperator::NotEqual: + case QSOperator::Ge: + case QSOperator::Gt: + case QSOperator::Le: + case QSOperator::Lt: + case QSOperator::StrictEqual: + case QSOperator::StrictNotEqual: { + if (!left->asTemp() && !left->asConst()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + IR::Expr* right = *expression(ast->right); + + if (_expr.accept(cx)) { + cjump(binop(IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse); + } else { + IR::Expr *e = binop(IR::binaryOperator(ast->op), left, right); + if (e->asConst() || e->asString()) + _expr.code = e; + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), e); + _expr.code = _block->TEMP(t); + } + } + break; + } + + case QSOperator::Add: + case QSOperator::BitAnd: + case QSOperator::BitOr: + case QSOperator::BitXor: + case QSOperator::Div: + case QSOperator::LShift: + case QSOperator::Mod: + case QSOperator::Mul: + case QSOperator::RShift: + case QSOperator::Sub: + case QSOperator::URShift: { + if (!left->asTemp() && !left->asConst()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + IR::Expr* right = *expression(ast->right); + + IR::Expr *e = binop(IR::binaryOperator(ast->op), left, right); + if (e->asConst() || e->asString()) + _expr.code = e; + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), e); + _expr.code = _block->TEMP(t); + } + break; + } + + } // switch + + return false; +} + +bool Codegen::visit(CallExpression *ast) +{ + Result base = expression(ast->base); + IR::ExprList *args = 0, **args_it = &args; + for (ArgumentList *it = ast->arguments; it; it = it->next) { + Result arg = expression(it->expression); + IR::Expr *actual = argument(*arg); + *args_it = _function->New(); + (*args_it)->init(actual); + args_it = &(*args_it)->next; + } + _expr.code = call(*base, args); + return false; +} + +bool Codegen::visit(ConditionalExpression *ast) +{ + IR::BasicBlock *iftrue = _function->newBasicBlock(); + IR::BasicBlock *iffalse = _function->newBasicBlock(); + IR::BasicBlock *endif = _function->newBasicBlock(); + + const unsigned t = _block->newTemp(); + + condition(ast->expression, iftrue, iffalse); + + _block = iftrue; + move(_block->TEMP(t), *expression(ast->ok)); + _block->JUMP(endif); + + _block = iffalse; + move(_block->TEMP(t), *expression(ast->ko)); + _block->JUMP(endif); + + _block = endif; + + _expr.code = _block->TEMP(t); + + return false; +} + +bool Codegen::visit(DeleteExpression *ast) +{ + IR::Expr* expr = *expression(ast->expression); + // Temporaries cannot be deleted + if (expr->asTemp() && expr->asTemp()->index < _env->members.size()) { + // Trying to delete a function argument might throw. + if (_function->isStrict && expr->asTemp()->index < 0) + throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); + _expr.code = _block->CONST(IR::BoolType, 0); + return false; + } + if (_function->isStrict && expr->asName()) + throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); + + // [[11.4.1]] Return true if it's not a reference + if (expr->asConst() || expr->asString()) { + _expr.code = _block->CONST(IR::BoolType, 1); + return false; + } + + // Return values from calls are also not a reference, but we have to + // perform the call to allow for side effects. + if (expr->asCall()) { + _block->EXP(expr); + _expr.code = _block->CONST(IR::BoolType, 1); + return false; + } + if (expr->asTemp() && expr->asTemp()->index >= _env->members.size()) { + _expr.code = _block->CONST(IR::BoolType, 1); + return false; + } + + IR::ExprList *args = _function->New(); + args->init(reference(expr)); + _expr.code = call(_block->NAME(IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args); + return false; +} + +bool Codegen::visit(FalseLiteral *) +{ + if (_expr.accept(cx)) { + _block->JUMP(_expr.iffalse); + } else { + _expr.code = _block->CONST(IR::BoolType, 0); + } + return false; +} + +bool Codegen::visit(FieldMemberExpression *ast) +{ + Result base = expression(ast->base); + _expr.code = member(*base, _function->newString(ast->name.toString())); + return false; +} + +bool Codegen::visit(FunctionExpression *ast) +{ + IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + if (_debugger) + _debugger->setSourceLocation(function, ast->functionToken.startLine, ast->functionToken.startColumn); + _expr.code = _block->CLOSURE(function); + return false; +} + +IR::Expr *Codegen::identifier(const QString &name, int line, int col) +{ + int index = _env->findMember(name); + + if (! _function->hasDirectEval && !_function->insideWith && _env->parent) { + if (index != -1) { + return _block->TEMP(index); + } + index = indexOfArgument(&name); + if (index != -1) { + return _block->TEMP(-(index + 1)); + } + } + + if (index >= _env->members.size()) { + // named local variable, e.g. in a catch statement + return _block->TEMP(index); + } + + return _block->NAME(name, line, col); + +} + +bool Codegen::visit(IdentifierExpression *ast) +{ + _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn); + return false; +} + +bool Codegen::visit(NestedExpression *ast) +{ + accept(ast->expression); + return false; +} + +bool Codegen::visit(NewExpression *ast) +{ + Result base = expression(ast->expression); + IR::Expr *expr = *base; + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + _expr.code = _block->NEW(expr, 0); + return false; +} + +bool Codegen::visit(NewMemberExpression *ast) +{ + Result base = expression(ast->base); + IR::Expr *expr = *base; + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + + IR::ExprList *args = 0, **args_it = &args; + for (ArgumentList *it = ast->arguments; it; it = it->next) { + Result arg = expression(it->expression); + IR::Expr *actual = argument(*arg); + *args_it = _function->New(); + (*args_it)->init(actual); + args_it = &(*args_it)->next; + } + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->NEW(expr, args)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(NotExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned r = _block->newTemp(); + move(_block->TEMP(r), unop(IR::OpNot, *expr)); + _expr.code = _block->TEMP(r); + return false; +} + +bool Codegen::visit(NullExpression *) +{ + if (_expr.accept(cx)) _block->JUMP(_expr.iffalse); + else _expr.code = _block->CONST(IR::NullType, 0); + + return false; +} + +bool Codegen::visit(NumericLiteral *ast) +{ + if (_expr.accept(cx)) { + if (ast->value) _block->JUMP(_expr.iftrue); + else _block->JUMP(_expr.iffalse); + } else { + _expr.code = _block->CONST(IR::NumberType, ast->value); + } + return false; +} + +struct ObjectPropertyValue { + IR::Expr *value; + IR::Function *getter; + IR::Function *setter; +}; + +bool Codegen::visit(ObjectLiteral *ast) +{ + QMap valueMap; + + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Object"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); + for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { + if (PropertyNameAndValue *nv = AST::cast(it->assignment)) { + QString name = propertyName(nv->name); + Result value = expression(nv->value); + ObjectPropertyValue &v = valueMap[name]; + if (v.getter || v.setter || (_function->isStrict && v.value)) + throwSyntaxError(nv->lastSourceLocation(), + QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); + + valueMap[name].value = *value; + } else if (PropertyGetterSetter *gs = AST::cast(it->assignment)) { + QString name = propertyName(gs->name); + IR::Function *function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0); + if (_debugger) + _debugger->setSourceLocation(function, gs->getSetToken.startLine, gs->getSetToken.startColumn); + ObjectPropertyValue &v = valueMap[name]; + if (v.value || + (gs->type == PropertyGetterSetter::Getter && v.getter) || + (gs->type == PropertyGetterSetter::Setter && v.setter)) + throwSyntaxError(gs->lastSourceLocation(), + QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); + if (gs->type == PropertyGetterSetter::Getter) + v.getter = function; + else + v.setter = function; + } else { + Q_UNREACHABLE(); + } + } + if (!valueMap.isEmpty()) { + unsigned value = 0; + unsigned getter = 0; + unsigned setter = 0; + for (QMap::const_iterator it = valueMap.constBegin(); it != valueMap.constEnd(); ++it) { + IR::ExprList *args = _function->New(); + IR::ExprList *current = args; + current->expr = _block->TEMP(t); + current->next = _function->New(); + current = current->next; + current->expr = _block->NAME(it.key(), 0, 0); + current->next = _function->New(); + current = current->next; + + if (it->value) { + if (!value) + value = _block->newTemp(); + move(_block->TEMP(value), it->value); + // __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) + current->expr = _block->TEMP(value); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_define_property, 0, 0), args)); + } else { + if (!getter) { + getter = _block->newTemp(); + setter = _block->newTemp(); + } + move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(IR::UndefinedType, 0)); + move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(IR::UndefinedType, 0)); + + + // __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); + current->expr = _block->TEMP(getter); + current->next = _function->New(); + current = current->next; + current->expr = _block->TEMP(setter); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_define_getter_setter, 0, 0), args)); + } + } + } + + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(PostDecrementExpression *ast) +{ + Result expr = expression(ast->base); + if (!expr->isLValue()) + throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); + + IR::ExprList *args = _function->New(); + args->init(*expr); + _expr.code = call(_block->NAME(IR::Name::builtin_postdecrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); + return false; +} + +bool Codegen::visit(PostIncrementExpression *ast) +{ + Result expr = expression(ast->base); + if (!expr->isLValue()) + throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); + + IR::ExprList *args = _function->New(); + args->init(*expr); + _expr.code = call(_block->NAME(IR::Name::builtin_postincrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); + return false; +} + +bool Codegen::visit(PreDecrementExpression *ast) +{ + Result expr = expression(ast->expression); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); + move(*expr, unop(IR::OpDecrement, *expr)); + if (_expr.accept(nx)) { + // nothing to do + } else { + _expr.code = *expr; + } + return false; +} + +bool Codegen::visit(PreIncrementExpression *ast) +{ + Result expr = expression(ast->expression); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); + move(*expr, unop(IR::OpIncrement, *expr)); + if (_expr.accept(nx)) { + // nothing to do + } else { + _expr.code = *expr; + } + return false; +} + +bool Codegen::visit(RegExpLiteral *ast) +{ + _expr.code = _block->REGEXP(_function->newString(ast->pattern.toString()), ast->flags); + return false; +} + +bool Codegen::visit(StringLiteral *ast) +{ + _expr.code = _block->STRING(_function->newString(ast->value.toString())); + return false; +} + +bool Codegen::visit(ThisExpression *ast) +{ + _expr.code = _block->NAME(QStringLiteral("this"), ast->thisToken.startLine, ast->thisToken.startColumn); + return false; +} + +bool Codegen::visit(TildeExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(IR::OpCompl, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(TrueLiteral *) +{ + if (_expr.accept(cx)) { + _block->JUMP(_expr.iftrue); + } else { + _expr.code = _block->CONST(IR::BoolType, 1); + } + return false; +} + +bool Codegen::visit(TypeOfExpression *ast) +{ + Result expr = expression(ast->expression); + IR::ExprList *args = _function->New(); + args->init(reference(*expr)); + _expr.code = call(_block->NAME(IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args); + return false; +} + +bool Codegen::visit(UnaryMinusExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(IR::OpUMinus, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(UnaryPlusExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(IR::OpUPlus, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(VoidExpression *ast) +{ + statement(ast->expression); + _expr.code = _block->CONST(IR::UndefinedType, 0); + return false; +} + +bool Codegen::visit(FunctionDeclaration * /*ast*/) +{ + _expr.accept(nx); + return false; +} + +void Codegen::linearize(IR::Function *function) +{ + IR::BasicBlock *exitBlock = function->basicBlocks.last(); + assert(exitBlock->isTerminated()); + assert(exitBlock->terminator()->asRet()); + + QSet V; + V.insert(exitBlock); + + QVector trace; + + for (int i = 0; i < function->basicBlocks.size(); ++i) { + IR::BasicBlock *block = function->basicBlocks.at(i); + if (!block->isTerminated() && (i + 1) < function->basicBlocks.size()) { + IR::BasicBlock *next = function->basicBlocks.at(i + 1); + block->JUMP(next); + } + } + + struct I { static void trace(IR::BasicBlock *block, QSet *V, + QVector *output) { + if (block == 0 || V->contains(block)) + return; + + V->insert(block); + block->index = output->size(); + output->append(block); + + if (IR::Stmt *term = block->terminator()) { + if (IR::Jump *j = term->asJump()) { + trace(j->target, V, output); + } else if (IR::CJump *cj = term->asCJump()) { + if (! V->contains(cj->iffalse)) + trace(cj->iffalse, V, output); + else + trace(cj->iftrue, V, output); + } + } + + // We could do this for each type above, but it is safer to have a + // "catchall" here + for (int ii = 0; ii < block->out.count(); ++ii) + trace(block->out.at(ii), V, output); + } + }; + + I::trace(function->basicBlocks.first(), &V, &trace); + + V.insert(exitBlock); + exitBlock->index = trace.size(); + trace.append(exitBlock); + + QVarLengthArray blocksToDelete; + foreach (IR::BasicBlock *b, function->basicBlocks) + if (!V.contains(b)) { + foreach (IR::BasicBlock *out, b->out) { + int idx = out->in.indexOf(b); + if (idx >= 0) + out->in.remove(idx); + } + blocksToDelete.append(b); + } + qDeleteAll(blocksToDelete); + function->basicBlocks = trace; + +#ifndef QV4_NO_LIVENESS + liveness(function); +#endif + + static bool showCode = !qgetenv("SHOW_CODE").isNull(); + if (showCode) { + QVector code; + QHash leader; + + foreach (IR::BasicBlock *block, function->basicBlocks) { + leader.insert(block->statements.first(), block); + foreach (IR::Stmt *s, block->statements) { + code.append(s); + } + } + + QString name; + if (function->name && !function->name->isEmpty()) + name = *function->name; + else + name.sprintf("%p", function); + + qout << "function " << name << "("; + for (int i = 0; i < function->formals.size(); ++i) { + if (i != 0) + qout << ", "; + qout << *function->formals.at(i); + } + qout << ")" << endl + << "{" << endl; + + foreach (const QString *local, function->locals) { + qout << " var " << *local << ';' << endl; + } + + for (int i = 0; i < code.size(); ++i) { + IR::Stmt *s = code.at(i); + + if (IR::BasicBlock *bb = leader.value(s)) { + qout << endl; + QByteArray str; + str.append('L'); + str.append(QByteArray::number(bb->index)); + str.append(':'); + for (int i = 66 - str.length(); i; --i) + str.append(' '); + qout << str; + qout << "// predecessor blocks:"; + foreach (IR::BasicBlock *in, bb->in) + qout << " L" << in->index; + qout << endl; + } + IR::Stmt *n = (i + 1) < code.size() ? code.at(i + 1) : 0; + if (n && s->asJump() && s->asJump()->target == leader.value(n)) { + continue; + } + + QByteArray str; + QBuffer buf(&str); + buf.open(QIODevice::WriteOnly); + QTextStream out(&buf); + s->dump(out, IR::Stmt::MIR); + out.flush(); + +#ifndef QV4_NO_LIVENESS + for (int i = 60 - str.size(); i >= 0; --i) + str.append(' '); + + qout << " " << str; + + // if (! s->uses.isEmpty()) { + // qout << " // uses:"; + // foreach (unsigned use, s->uses) { + // qout << " %" << use; + // } + // } + + // if (! s->defs.isEmpty()) { + // qout << " // defs:"; + // foreach (unsigned def, s->defs) { + // qout << " %" << def; + // } + // } + + if (! s->d->liveOut.isEmpty()) { + qout << " // lives out:"; + for (int i = 0; i < s->d->liveOut.size(); ++i) { + if (s->d->liveOut.testBit(i)) + qout << " %" << i; + } + } +#else + qout << " " << str; +#endif + + qout << endl; + + if (n && s->asCJump() && s->asCJump()->iffalse != leader.value(n)) { + qout << " goto L" << s->asCJump()->iffalse << ";" << endl; + } + } + + qout << "}" << endl + << endl; + } +} + +IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body, Mode mode, + const QStringList &inheritedLocals) +{ + qSwap(_mode, mode); // enter function code. + + ScopeAndFinally *scopeAndFinally = 0; + + enterEnvironment(ast); + IR::Function *function = _module->newFunction(name, _function); + + if (_debugger) + _debugger->addFunction(function); + IR::BasicBlock *entryBlock = function->newBasicBlock(); + IR::BasicBlock *exitBlock = function->newBasicBlock(IR::Function::DontInsertBlock); + IR::BasicBlock *throwBlock = function->newBasicBlock(); + function->hasDirectEval = _env->hasDirectEval; + function->usesArgumentsObject = (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed); + function->maxNumberOfArguments = _env->maxNumberOfArguments; + function->isStrict = _env->isStrict; + + // variables in global code are properties of the global context object, not locals as with other functions. + if (_mode == FunctionCode) { + for (Environment::MemberMap::iterator it = _env->members.begin(); it != _env->members.end(); ++it) { + const QString &local = it.key(); + function->LOCAL(local); + unsigned t = entryBlock->newTemp(); + (*it).index = t; + } + } else { + if (!_env->isStrict) { + foreach (const QString &inheritedLocal, inheritedLocals) { + function->LOCAL(inheritedLocal); + unsigned tempIndex = entryBlock->newTemp(); + Environment::Member member = { Environment::UndefinedMember, tempIndex, 0 }; + _env->members.insert(inheritedLocal, member); + } + } + + IR::ExprList *args = 0; + for (Environment::MemberMap::const_iterator it = _env->members.constBegin(); it != _env->members.constEnd(); ++it) { + const QString &local = it.key(); + IR::ExprList *next = function->New(); + next->expr = entryBlock->NAME(local, 0, 0); + next->next = args; + args = next; + } + if (args) { + IR::ExprList *next = function->New(); + next->expr = entryBlock->CONST(IR::BoolType, mode == EvalCode); + next->next = args; + args = next; + + entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(IR::Name::builtin_declare_vars, 0, 0), args)); + } + } + + unsigned returnAddress = entryBlock->newTemp(); + + entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(IR::UndefinedType, 0)); + exitBlock->RET(exitBlock->TEMP(returnAddress)); + IR::ExprList *throwArgs = function->New(); + throwArgs->expr = throwBlock->TEMP(returnAddress); + throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); + Loop *loop = 0; + + qSwap(_function, function); + qSwap(_block, entryBlock); + qSwap(_exitBlock, exitBlock); + qSwap(_throwBlock, throwBlock); + qSwap(_returnAddress, returnAddress); + qSwap(_scopeAndFinally, scopeAndFinally); + qSwap(_loop, loop); + + for (FormalParameterList *it = formals; it; it = it->next) { + _function->RECEIVE(it->name.toString()); + } + + foreach (const Environment::Member &member, _env->members) { + if (member.function) { + IR::Function *function = defineFunction(member.function->name.toString(), member.function, member.function->formals, + member.function->body ? member.function->body->elements : 0); + if (_debugger) + _debugger->setSourceLocation(function, member.function->functionToken.startLine, member.function->functionToken.startColumn); + if (! _env->parent) { + move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), + _block->CLOSURE(function)); + } else { + assert(member.index >= 0); + move(_block->TEMP(member.index), _block->CLOSURE(function)); + } + } + } + + sourceElements(body); + + _function->insertBasicBlock(_exitBlock); + + _block->JUMP(_exitBlock); + + qSwap(_function, function); + qSwap(_block, entryBlock); + qSwap(_exitBlock, exitBlock); + qSwap(_throwBlock, throwBlock); + qSwap(_returnAddress, returnAddress); + qSwap(_scopeAndFinally, scopeAndFinally); + qSwap(_loop, loop); + + leaveEnvironment(); + + qSwap(_mode, mode); + + return function; +} + +int Codegen::indexOfArgument(const QStringRef &string) const +{ + for (int i = _function->formals.size() - 1; i >= 0; --i) { + if (*_function->formals.at(i) == string) + return i; + } + return -1; +} + +bool Codegen::visit(IdentifierPropertyName *ast) +{ + _property = ast->id.toString(); + return false; +} + +bool Codegen::visit(NumericLiteralPropertyName *ast) +{ + _property = QString::number(ast->id, 'g', 16); + return false; +} + +bool Codegen::visit(StringLiteralPropertyName *ast) +{ + _property = ast->id.toString(); + return false; +} + +bool Codegen::visit(FunctionSourceElement *ast) +{ + statement(ast->declaration); + return false; +} + +bool Codegen::visit(StatementSourceElement *ast) +{ + statement(ast->statement); + return false; +} + +bool Codegen::visit(Block *ast) +{ + for (StatementList *it = ast->statements; it; it = it->next) { + statement(it->statement); + } + return false; +} + +bool Codegen::visit(BreakStatement *ast) +{ + if (!_loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Break outside of loop")); + Loop *loop = 0; + if (ast->label.isEmpty()) + loop = _loop; + else { + for (loop = _loop; loop; loop = loop->parent) { + if (loop->labelledStatement && loop->labelledStatement->label == ast->label) + break; + } + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); + } + unwindException(loop->scopeAndFinally); + _block->JUMP(loop->breakBlock); + return false; +} + +bool Codegen::visit(ContinueStatement *ast) +{ + Loop *loop = 0; + if (ast->label.isEmpty()) { + for (loop = _loop; loop; loop = loop->parent) { + if (loop->continueBlock) + break; + } + } else { + for (loop = _loop; loop; loop = loop->parent) { + if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { + if (!loop->continueBlock) + loop = 0; + break; + } + } + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); + } + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "continue outside of loop")); + unwindException(loop->scopeAndFinally); + _block->JUMP(loop->continueBlock); + return false; +} + +bool Codegen::visit(DebuggerStatement *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(DoWhileStatement *ast) +{ + IR::BasicBlock *loopbody = _function->newBasicBlock(); + IR::BasicBlock *loopcond = _function->newBasicBlock(); + IR::BasicBlock *loopend = _function->newBasicBlock(); + + enterLoop(ast, loopend, loopcond); + + _block->JUMP(loopbody); + + _block = loopbody; + statement(ast->statement); + _block->JUMP(loopcond); + + _block = loopcond; + condition(ast->expression, loopbody, loopend); + + _block = loopend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(EmptyStatement *) +{ + return false; +} + +bool Codegen::visit(ExpressionStatement *ast) +{ + if (_mode == EvalCode) { + Result e = expression(ast->expression); + if (*e) + move(_block->TEMP(_returnAddress), *e); + } else { + statement(ast->expression); + } + return false; +} + +bool Codegen::visit(ForEachStatement *ast) +{ + IR::BasicBlock *foreachin = _function->newBasicBlock(); + IR::BasicBlock *foreachbody = _function->newBasicBlock(); + IR::BasicBlock *foreachend = _function->newBasicBlock(); + + enterLoop(ast, foreachend, foreachin); + + int iterator = _block->newTemp(); + move(_block->TEMP(iterator), *expression(ast->expression)); + IR::ExprList *args = _function->New(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + + _block->JUMP(foreachin); + + _block = foreachbody; + int temp = _block->newTemp(); + move(*expression(ast->initialiser), _block->TEMP(temp)); + statement(ast->statement); + _block->JUMP(foreachin); + + _block = foreachin; + + args = _function->New(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args)); + int null = _block->newTemp(); + move(_block->TEMP(null), _block->CONST(IR::NullType, 0)); + cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); + _block = foreachend; + + leaveLoop(); + return false; +} + +bool Codegen::visit(ForStatement *ast) +{ + IR::BasicBlock *forcond = _function->newBasicBlock(); + IR::BasicBlock *forbody = _function->newBasicBlock(); + IR::BasicBlock *forstep = _function->newBasicBlock(); + IR::BasicBlock *forend = _function->newBasicBlock(); + + enterLoop(ast, forend, forstep); + + statement(ast->initialiser); + _block->JUMP(forcond); + + _block = forcond; + condition(ast->condition, forbody, forend); + + _block = forbody; + statement(ast->statement); + _block->JUMP(forstep); + + _block = forstep; + statement(ast->expression); + _block->JUMP(forcond); + _block = forend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(IfStatement *ast) +{ + IR::BasicBlock *iftrue = _function->newBasicBlock(); + IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock() : 0; + IR::BasicBlock *endif = _function->newBasicBlock(); + condition(ast->expression, iftrue, ast->ko ? iffalse : endif); + + _block = iftrue; + statement(ast->ok); + _block->JUMP(endif); + + if (ast->ko) { + _block = iffalse; + statement(ast->ko); + _block->JUMP(endif); + } + + _block = endif; + + return false; +} + +bool Codegen::visit(LabelledStatement *ast) +{ + _labelledStatement = ast; + + if (AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement)) { + statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. + } else { + IR::BasicBlock *breakBlock = _function->newBasicBlock(); + enterLoop(ast->statement, breakBlock, /*continueBlock*/ 0); + statement(ast->statement); + _block->JUMP(breakBlock); + _block = breakBlock; + leaveLoop(); + } + + return false; +} + +bool Codegen::visit(LocalForEachStatement *ast) +{ + IR::BasicBlock *foreachin = _function->newBasicBlock(); + IR::BasicBlock *foreachbody = _function->newBasicBlock(); + IR::BasicBlock *foreachend = _function->newBasicBlock(); + + enterLoop(ast, foreachend, foreachin); + + variableDeclaration(ast->declaration); + + int iterator = _block->newTemp(); + move(_block->TEMP(iterator), *expression(ast->expression)); + IR::ExprList *args = _function->New(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + + _block->JUMP(foreachin); + + _block = foreachbody; + int temp = _block->newTemp(); + move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); + statement(ast->statement); + _block->JUMP(foreachin); + + _block = foreachin; + + args = _function->New(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args)); + int null = _block->newTemp(); + move(_block->TEMP(null), _block->CONST(IR::NullType, 0)); + cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); + _block = foreachend; + + leaveLoop(); + return false; +} + +bool Codegen::visit(LocalForStatement *ast) +{ + IR::BasicBlock *forcond = _function->newBasicBlock(); + IR::BasicBlock *forbody = _function->newBasicBlock(); + IR::BasicBlock *forstep = _function->newBasicBlock(); + IR::BasicBlock *forend = _function->newBasicBlock(); + + enterLoop(ast, forend, forstep); + + variableDeclarationList(ast->declarations); + _block->JUMP(forcond); + + _block = forcond; + condition(ast->condition, forbody, forend); + + _block = forbody; + statement(ast->statement); + _block->JUMP(forstep); + + _block = forstep; + statement(ast->expression); + _block->JUMP(forcond); + _block = forend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(ReturnStatement *ast) +{ + if (_mode != FunctionCode) + throwSyntaxError(ast->returnToken, QCoreApplication::translate("qv4codegen", "Return statement outside of function")); + if (ast->expression) { + Result expr = expression(ast->expression); + move(_block->TEMP(_returnAddress), *expr); + } + unwindException(0); + + _block->JUMP(_exitBlock); + return false; +} + +bool Codegen::visit(SwitchStatement *ast) +{ + IR::BasicBlock *switchend = _function->newBasicBlock(); + + if (ast->block) { + Result lhs = expression(ast->expression); + IR::BasicBlock *switchcond = _block; + + QHash blockMap; + + enterLoop(ast, switchend, 0); + + for (CaseClauses *it = ast->block->clauses; it; it = it->next) { + CaseClause *clause = it->clause; + + _block = _function->newBasicBlock(); + blockMap[clause] = _block; + + for (StatementList *it2 = clause->statements; it2; it2 = it2->next) + statement(it2->statement); + } + + if (ast->block->defaultClause) { + _block = _function->newBasicBlock(); + blockMap[ast->block->defaultClause] = _block; + + for (StatementList *it2 = ast->block->defaultClause->statements; it2; it2 = it2->next) + statement(it2->statement); + } + + for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { + CaseClause *clause = it->clause; + + _block = _function->newBasicBlock(); + blockMap[clause] = _block; + + for (StatementList *it2 = clause->statements; it2; it2 = it2->next) + statement(it2->statement); + } + + leaveLoop(); + + _block->JUMP(switchend); + + _block = switchcond; + for (CaseClauses *it = ast->block->clauses; it; it = it->next) { + CaseClause *clause = it->clause; + Result rhs = expression(clause->expression); + IR::BasicBlock *iftrue = blockMap[clause]; + IR::BasicBlock *iffalse = _function->newBasicBlock(); + cjump(binop(IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); + _block = iffalse; + } + + for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { + CaseClause *clause = it->clause; + Result rhs = expression(clause->expression); + IR::BasicBlock *iftrue = blockMap[clause]; + IR::BasicBlock *iffalse = _function->newBasicBlock(); + cjump(binop(IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); + _block = iffalse; + } + + if (ast->block->defaultClause) { + _block->JUMP(blockMap[ast->block->defaultClause]); + } + } + + _block->JUMP(switchend); + + _block = switchend; + return false; +} + +bool Codegen::visit(ThrowStatement *ast) +{ + Result expr = expression(ast->expression); + move(_block->TEMP(_returnAddress), *expr); + _block->JUMP(_throwBlock); + return false; +} + +bool Codegen::visit(TryStatement *ast) +{ + if (_function->isStrict && ast->catchExpression && + (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) + throwSyntaxError(ast->catchExpression->identifierToken, QCoreApplication::translate("qv4codegen", "Catch variable name may not be eval or arguments in strict mode")); + + IR::BasicBlock *tryBody = _function->newBasicBlock(); + IR::BasicBlock *catchBody = ast->catchExpression ? _function->newBasicBlock() : 0; + // We always need a finally body to clean up the exception handler + IR::BasicBlock *finallyBody = _function->newBasicBlock(); + + int inCatch = 0; + if (catchBody) { + inCatch = _block->newTemp(); + move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, false)); + } + + int hasException = _block->newTemp(); + move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_create_exception_handler, 0, 0), 0)); + + // Pass the hidden "inCatch" and "hasException" TEMPs to the + // builtin_delete_exception_handler, in order to have those TEMPs alive for + // the duration of the exception handling block. + IR::ExprList *deleteExceptionArgs = _function->New(); + deleteExceptionArgs->init(_block->TEMP(hasException)); + if (inCatch) { + deleteExceptionArgs->next = _function->New(); + deleteExceptionArgs->next->init(_block->TEMP(inCatch)); + } + + ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression, deleteExceptionArgs); + _scopeAndFinally = &tcf; + + _block->CJUMP(_block->TEMP(hasException), catchBody ? catchBody : finallyBody, tryBody); + + _block = tryBody; + statement(ast->statement); + _block->JUMP(finallyBody); + + // regular flow does not go into the catch statement + if (catchBody) { + _block = catchBody; + + if (inCatch != 0) { + // check if an exception got thrown within catch. Go to finally + // and then rethrow + IR::BasicBlock *b = _function->newBasicBlock(); + _block->CJUMP(_block->TEMP(inCatch), finallyBody, b); + _block = b; + } + // if we have finally we need to clear the exception here, so we don't rethrow + move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, true)); + move(_block->TEMP(hasException), _block->CONST(IR::BoolType, false)); + + const int exception = _block->newTemp(); + move(_block->TEMP(exception), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0)); + + // the variable used in the catch statement is local and hides any global + // variable with the same name. + const Environment::Member undefinedMember = { Environment::UndefinedMember, -1 , 0 }; + const Environment::Member catchMember = { Environment::VariableDefinition, exception, 0 }; + Environment::Member m = _env->members.value(ast->catchExpression->name.toString(), undefinedMember); + _env->members.insert(ast->catchExpression->name.toString(), catchMember); + + statement(ast->catchExpression->statement); + + // reset the variable name to the one from the outer scope + if (m.type == Environment::UndefinedMember) + _env->members.remove(ast->catchExpression->name.toString()); + else + _env->members.insert(ast->catchExpression->name.toString(), m); + _block->JUMP(finallyBody); + } + + _scopeAndFinally = tcf.parent; + + IR::BasicBlock *after = _function->newBasicBlock(); + _block = finallyBody; + + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), deleteExceptionArgs)); + int exception_to_rethrow = _block->newTemp(); + move(_block->TEMP(exception_to_rethrow), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0)); + + if (ast->finallyExpression && ast->finallyExpression->statement) + statement(ast->finallyExpression->statement); + + IR::BasicBlock *rethrowBlock = _function->newBasicBlock(); + _block->CJUMP(_block->TEMP(hasException), rethrowBlock, after); + _block = rethrowBlock; + move(_block->TEMP(_returnAddress), _block->TEMP(exception_to_rethrow)); + _block->JUMP(_throwBlock); + + _block = after; + + return false; +} + +void Codegen::unwindException(Codegen::ScopeAndFinally *outest) +{ + ScopeAndFinally *scopeAndFinally = _scopeAndFinally; + qSwap(_scopeAndFinally, scopeAndFinally); + while (_scopeAndFinally != outest) { + if (_scopeAndFinally->popScope) { + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0))); + _scopeAndFinally = _scopeAndFinally->parent; + } else { + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), _scopeAndFinally->deleteExceptionArgs)); + ScopeAndFinally *tc = _scopeAndFinally; + _scopeAndFinally = tc->parent; + if (tc->finally && tc->finally->statement) + statement(tc->finally->statement); + } + } + qSwap(_scopeAndFinally, scopeAndFinally); +} + +bool Codegen::visit(VariableStatement *ast) +{ + variableDeclarationList(ast->declarations); + return false; +} + +bool Codegen::visit(WhileStatement *ast) +{ + IR::BasicBlock *whilecond = _function->newBasicBlock(); + IR::BasicBlock *whilebody = _function->newBasicBlock(); + IR::BasicBlock *whileend = _function->newBasicBlock(); + + enterLoop(ast, whileend, whilecond); + + _block->JUMP(whilecond); + _block = whilecond; + condition(ast->expression, whilebody, whileend); + + _block = whilebody; + statement(ast->statement); + _block->JUMP(whilecond); + + _block = whileend; + leaveLoop(); + + return false; +} + +bool Codegen::visit(WithStatement *ast) +{ + IR::BasicBlock *withBlock = _function->newBasicBlock(); + + _block->JUMP(withBlock); + _block = withBlock; + int withObject = _block->newTemp(); + _block->MOVE(_block->TEMP(withObject), *expression(ast->expression)); + IR::ExprList *args = _function->New(); + args->init(_block->TEMP(withObject)); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with_scope, 0, 0), args)); + + ++_function->insideWith; + { + ScopeAndFinally scope(_scopeAndFinally); + _scopeAndFinally = &scope; + statement(ast->statement); + _scopeAndFinally = scope.parent; + } + --_function->insideWith; + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); + + IR::BasicBlock *next = _function->newBasicBlock(); + _block->JUMP(next); + _block = next; + + return false; +} + +bool Codegen::visit(UiArrayBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiObjectBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiObjectDefinition *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiPublicMember *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiScriptBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiSourceElement *) +{ + assert(!"not implemented"); + return false; +} + +void Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(IR::Expr *expr, const SourceLocation& loc) +{ + if (!_env->isStrict) + return; + IR::Name *n = expr->asName(); + if (!n) + return; + if (*n->id == QLatin1String("eval") || *n->id == QLatin1String("arguments")) + throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); +} + +void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) +{ + VM::DiagnosticMessage *msg = new VM::DiagnosticMessage; + msg->fileName = _fileName; + msg->offset = loc.begin(); + msg->startLine = loc.startLine; + msg->startColumn = loc.startColumn; + msg->message = detail; + if (_context) + _context->throwSyntaxError(msg); + else if (_errorHandler) + _errorHandler->syntaxError(msg); + else + Q_ASSERT(!"No error handler available."); +} + +void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail) +{ + if (_context) + _context->throwReferenceError(VM::Value::fromString(_context, detail)); + else if (_errorHandler) + throwSyntaxError(loc, detail); + else + Q_ASSERT(!"No error handler available."); +} diff --git a/src/v4/qv4codegen_p.h b/src/v4/qv4codegen_p.h new file mode 100644 index 0000000000..d83e129782 --- /dev/null +++ b/src/v4/qv4codegen_p.h @@ -0,0 +1,421 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4CODEGEN_P_H +#define QV4CODEGEN_P_H + +#include "qv4global.h" +#include "qv4ir_p.h" +#include +#include +#include + +namespace QQmlJS { + +namespace AST { +class UiParameterList; +} + +namespace VM { +struct DiagnosticMessage; +struct ExecutionContext; +} + +namespace Debugging { +class Debugger; +} // namespace Debugging + +class ErrorHandler +{ +public: + virtual void syntaxError(VM::DiagnosticMessage *message) = 0; +}; + +class Q_V4_EXPORT Codegen: protected AST::Visitor +{ +public: + Codegen(VM::ExecutionContext *ctx, bool strict); + Codegen(ErrorHandler *errorHandler, bool strictMode); + + enum Mode { + GlobalCode, + EvalCode, + FunctionCode + }; + + IR::Function *operator()(const QString &fileName, AST::Program *ast, IR::Module *module, Mode mode = GlobalCode, const QStringList &inheritedLocals = QStringList()); + IR::Function *operator()(const QString &fileName, AST::FunctionExpression *ast, IR::Module *module); + +protected: + enum Format { ex, cx, nx }; + struct Result { + IR::Expr *code; + IR::BasicBlock *iftrue; + IR::BasicBlock *iffalse; + Format format; + Format requested; + + explicit Result(Format requested = ex) + : code(0) + , iftrue(0) + , iffalse(0) + , format(ex) + , requested(requested) {} + + explicit Result(IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) + : code(0) + , iftrue(iftrue) + , iffalse(iffalse) + , format(ex) + , requested(cx) {} + + inline IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; } + inline IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; } + + bool accept(Format f) + { + if (requested == f) { + format = f; + return true; + } + return false; + } + }; + + struct Environment { + Environment *parent; + + enum MemberType { + UndefinedMember, + VariableDefinition, + VariableDeclaration, + FunctionDefinition + }; + struct Member { + MemberType type; + int index; + AST::FunctionDeclaration *function; + }; + typedef QMap MemberMap; + + MemberMap members; + int maxNumberOfArguments; + bool hasDirectEval; + bool hasNestedFunctions; + bool isStrict; + enum UsesArgumentsObject { + ArgumentsObjectUnknown, + ArgumentsObjectNotUsed, + ArgumentsObjectUsed + }; + + UsesArgumentsObject usesArgumentsObject; + + Environment(Environment *parent) + : parent(parent) + , maxNumberOfArguments(0) + , hasDirectEval(false) + , hasNestedFunctions(false) + , isStrict(false) + , usesArgumentsObject(ArgumentsObjectUnknown) + { + if (parent && parent->isStrict) + isStrict = true; + } + + int findMember(const QString &name) const + { + MemberMap::const_iterator it = members.find(name); + if (it == members.end()) + return -1; + assert((*it).index != -1 || !parent); + return (*it).index; + } + + bool lookupMember(const QString &name, Environment **scope, int *index, int *distance) + { + Environment *it = this; + *distance = 0; + for (; it; it = it->parent, ++(*distance)) { + int idx = it->findMember(name); + if (idx != -1) { + *scope = it; + *index = idx; + return true; + } + } + return false; + } + + void enter(const QString &name, MemberType type, AST::FunctionDeclaration *function = 0) + { + if (! name.isEmpty()) { + MemberMap::iterator it = members.find(name); + if (it == members.end()) { + Member m; + m.index = -1; + m.type = type; + m.function = function; + members.insert(name, m); + } else { + if ((*it).type <= type) { + (*it).type = type; + (*it).function = function; + } + } + } + } + }; + + Environment *newEnvironment(AST::Node *node, Environment *parent) + { + Environment *env = new Environment(parent); + _envMap.insert(node, env); + return env; + } + + struct UiMember { + }; + + struct ScopeAndFinally { + ScopeAndFinally *parent; + AST::Finally *finally; + IR::ExprList *deleteExceptionArgs; + bool popScope; + + ScopeAndFinally(ScopeAndFinally *parent) : parent(parent), finally(0), deleteExceptionArgs(0), popScope(true) {} + ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally, IR::ExprList *deleteExceptionArgs) + : parent(parent), finally(finally), deleteExceptionArgs(deleteExceptionArgs), popScope(false) + {} + }; + + struct Loop { + AST::LabelledStatement *labelledStatement; + AST::Statement *node; + IR::BasicBlock *breakBlock; + IR::BasicBlock *continueBlock; + Loop *parent; + ScopeAndFinally *scopeAndFinally; + + Loop(AST::Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock, Loop *parent) + : labelledStatement(0), node(node), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {} + }; + + void enterEnvironment(AST::Node *node); + void leaveEnvironment(); + + void enterLoop(AST::Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock); + void leaveLoop(); + + + IR::Expr *member(IR::Expr *base, const QString *name); + IR::Expr *subscript(IR::Expr *base, IR::Expr *index); + IR::Expr *argument(IR::Expr *expr); + IR::Expr *reference(IR::Expr *expr); + IR::Expr *unop(IR::AluOp op, IR::Expr *expr); + IR::Expr *binop(IR::AluOp op, IR::Expr *left, IR::Expr *right); + IR::Expr *call(IR::Expr *base, IR::ExprList *args); + void move(IR::Expr *target, IR::Expr *source, IR::AluOp op = IR::OpInvalid); + void cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse); + + void linearize(IR::Function *function); + IR::Function *defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body, + Mode mode = FunctionCode, + const QStringList &inheritedLocals = QStringList()); + int indexOfArgument(const QStringRef &string) const; + + void unwindException(ScopeAndFinally *outest); + + void statement(AST::Statement *ast); + void statement(AST::ExpressionNode *ast); + void condition(AST::ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse); + Result expression(AST::ExpressionNode *ast); + QString propertyName(AST::PropertyName *ast); + Result sourceElement(AST::SourceElement *ast); + UiMember uiObjectMember(AST::UiObjectMember *ast); + + void accept(AST::Node *node); + + void functionBody(AST::FunctionBody *ast); + void program(AST::Program *ast); + void sourceElements(AST::SourceElements *ast); + void variableDeclaration(AST::VariableDeclaration *ast); + void variableDeclarationList(AST::VariableDeclarationList *ast); + + IR::Expr *identifier(const QString &name, int line = 0, int col = 0); + + // nodes + virtual bool visit(AST::ArgumentList *ast); + virtual bool visit(AST::CaseBlock *ast); + virtual bool visit(AST::CaseClause *ast); + virtual bool visit(AST::CaseClauses *ast); + virtual bool visit(AST::Catch *ast); + virtual bool visit(AST::DefaultClause *ast); + virtual bool visit(AST::ElementList *ast); + virtual bool visit(AST::Elision *ast); + virtual bool visit(AST::Finally *ast); + virtual bool visit(AST::FormalParameterList *ast); + virtual bool visit(AST::FunctionBody *ast); + virtual bool visit(AST::Program *ast); + virtual bool visit(AST::PropertyNameAndValue *ast); + virtual bool visit(AST::PropertyAssignmentList *ast); + virtual bool visit(AST::PropertyGetterSetter *ast); + virtual bool visit(AST::SourceElements *ast); + virtual bool visit(AST::StatementList *ast); + virtual bool visit(AST::UiArrayMemberList *ast); + virtual bool visit(AST::UiImport *ast); + virtual bool visit(AST::UiImportList *ast); + virtual bool visit(AST::UiObjectInitializer *ast); + virtual bool visit(AST::UiObjectMemberList *ast); + virtual bool visit(AST::UiParameterList *ast); + virtual bool visit(AST::UiProgram *ast); + virtual bool visit(AST::UiQualifiedId *ast); + virtual bool visit(AST::VariableDeclaration *ast); + virtual bool visit(AST::VariableDeclarationList *ast); + + // expressions + virtual bool visit(AST::Expression *ast); + virtual bool visit(AST::ArrayLiteral *ast); + virtual bool visit(AST::ArrayMemberExpression *ast); + virtual bool visit(AST::BinaryExpression *ast); + virtual bool visit(AST::CallExpression *ast); + virtual bool visit(AST::ConditionalExpression *ast); + virtual bool visit(AST::DeleteExpression *ast); + virtual bool visit(AST::FalseLiteral *ast); + virtual bool visit(AST::FieldMemberExpression *ast); + virtual bool visit(AST::FunctionExpression *ast); + virtual bool visit(AST::IdentifierExpression *ast); + virtual bool visit(AST::NestedExpression *ast); + virtual bool visit(AST::NewExpression *ast); + virtual bool visit(AST::NewMemberExpression *ast); + virtual bool visit(AST::NotExpression *ast); + virtual bool visit(AST::NullExpression *ast); + virtual bool visit(AST::NumericLiteral *ast); + virtual bool visit(AST::ObjectLiteral *ast); + virtual bool visit(AST::PostDecrementExpression *ast); + virtual bool visit(AST::PostIncrementExpression *ast); + virtual bool visit(AST::PreDecrementExpression *ast); + virtual bool visit(AST::PreIncrementExpression *ast); + virtual bool visit(AST::RegExpLiteral *ast); + virtual bool visit(AST::StringLiteral *ast); + virtual bool visit(AST::ThisExpression *ast); + virtual bool visit(AST::TildeExpression *ast); + virtual bool visit(AST::TrueLiteral *ast); + virtual bool visit(AST::TypeOfExpression *ast); + virtual bool visit(AST::UnaryMinusExpression *ast); + virtual bool visit(AST::UnaryPlusExpression *ast); + virtual bool visit(AST::VoidExpression *ast); + virtual bool visit(AST::FunctionDeclaration *ast); + + // property names + virtual bool visit(AST::IdentifierPropertyName *ast); + virtual bool visit(AST::NumericLiteralPropertyName *ast); + virtual bool visit(AST::StringLiteralPropertyName *ast); + + // source elements + virtual bool visit(AST::FunctionSourceElement *ast); + virtual bool visit(AST::StatementSourceElement *ast); + + // statements + virtual bool visit(AST::Block *ast); + virtual bool visit(AST::BreakStatement *ast); + virtual bool visit(AST::ContinueStatement *ast); + virtual bool visit(AST::DebuggerStatement *ast); + virtual bool visit(AST::DoWhileStatement *ast); + virtual bool visit(AST::EmptyStatement *ast); + virtual bool visit(AST::ExpressionStatement *ast); + virtual bool visit(AST::ForEachStatement *ast); + virtual bool visit(AST::ForStatement *ast); + virtual bool visit(AST::IfStatement *ast); + virtual bool visit(AST::LabelledStatement *ast); + virtual bool visit(AST::LocalForEachStatement *ast); + virtual bool visit(AST::LocalForStatement *ast); + virtual bool visit(AST::ReturnStatement *ast); + virtual bool visit(AST::SwitchStatement *ast); + virtual bool visit(AST::ThrowStatement *ast); + virtual bool visit(AST::TryStatement *ast); + virtual bool visit(AST::VariableStatement *ast); + virtual bool visit(AST::WhileStatement *ast); + virtual bool visit(AST::WithStatement *ast); + + // ui object members + virtual bool visit(AST::UiArrayBinding *ast); + virtual bool visit(AST::UiObjectBinding *ast); + virtual bool visit(AST::UiObjectDefinition *ast); + virtual bool visit(AST::UiPublicMember *ast); + virtual bool visit(AST::UiScriptBinding *ast); + virtual bool visit(AST::UiSourceElement *ast); + + void throwSyntaxErrorOnEvalOrArgumentsInStrictMode(IR::Expr* expr, const AST::SourceLocation &loc); + + void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); + void throwReferenceError(const AST::SourceLocation &loc, const QString &detail); + +private: + QString _fileName; + Result _expr; + QString _property; + UiMember _uiMember; + IR::Module *_module; + IR::Function *_function; + IR::BasicBlock *_block; + IR::BasicBlock *_exitBlock; + IR::BasicBlock *_throwBlock; + unsigned _returnAddress; + Mode _mode; + Environment *_env; + Loop *_loop; + AST::LabelledStatement *_labelledStatement; + ScopeAndFinally *_scopeAndFinally; + QHash _envMap; + QHash _functionMap; + VM::ExecutionContext *_context; + bool _strictMode; + Debugging::Debugger *_debugger; + ErrorHandler *_errorHandler; + + class ScanFunctions; +}; + +} // end of namespace QQmlJS + +#endif // QV4CODEGEN_P_H diff --git a/src/v4/qv4dateobject.cpp b/src/v4/qv4dateobject.cpp new file mode 100644 index 0000000000..b9a4446087 --- /dev/null +++ b/src/v4/qv4dateobject.cpp @@ -0,0 +1,1313 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4dateobject.h" +#include "qv4objectproto.h" +#include "qv4mm.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef Q_WS_WIN +# include +# ifndef Q_OS_VXWORKS +# include +# else +# include "qplatformdefs.h" +# endif +#else +# include +#endif + +using namespace QQmlJS::VM; + +static const double HoursPerDay = 24.0; +static const double MinutesPerHour = 60.0; +static const double SecondsPerMinute = 60.0; +static const double msPerSecond = 1000.0; +static const double msPerMinute = 60000.0; +static const double msPerHour = 3600000.0; +static const double msPerDay = 86400000.0; + +static double LocalTZA = 0.0; // initialized at startup + +static inline double TimeWithinDay(double t) +{ + double r = ::fmod(t, msPerDay); + return (r >= 0) ? r : r + msPerDay; +} + +static inline int HourFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerHour), HoursPerDay)); + return (r >= 0) ? r : r + int(HoursPerDay); +} + +static inline int MinFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerMinute), MinutesPerHour)); + return (r >= 0) ? r : r + int(MinutesPerHour); +} + +static inline int SecFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerSecond), SecondsPerMinute)); + return (r >= 0) ? r : r + int(SecondsPerMinute); +} + +static inline int msFromTime(double t) +{ + int r = int(::fmod(t, msPerSecond)); + return (r >= 0) ? r : r + int(msPerSecond); +} + +static inline double Day(double t) +{ + return ::floor(t / msPerDay); +} + +static inline double DaysInYear(double y) +{ + if (::fmod(y, 4)) + return 365; + + else if (::fmod(y, 100)) + return 366; + + else if (::fmod(y, 400)) + return 365; + + return 366; +} + +static inline double DayFromYear(double y) +{ + return 365 * (y - 1970) + + ::floor((y - 1969) / 4) + - ::floor((y - 1901) / 100) + + ::floor((y - 1601) / 400); +} + +static inline double TimeFromYear(double y) +{ + return msPerDay * DayFromYear(y); +} + +static inline double YearFromTime(double t) +{ + int y = 1970; + y += (int) ::floor(t / (msPerDay * 365.2425)); + + double t2 = TimeFromYear(y); + return (t2 > t) ? y - 1 : ((t2 + msPerDay * DaysInYear(y)) <= t) ? y + 1 : y; +} + +static inline bool InLeapYear(double t) +{ + double x = DaysInYear(YearFromTime(t)); + if (x == 365) + return 0; + + assert(x == 366); + return 1; +} + +static inline double DayWithinYear(double t) +{ + return Day(t) - DayFromYear(YearFromTime(t)); +} + +static inline double MonthFromTime(double t) +{ + double d = DayWithinYear(t); + double l = InLeapYear(t); + + if (d < 31.0) + return 0; + + else if (d < 59.0 + l) + return 1; + + else if (d < 90.0 + l) + return 2; + + else if (d < 120.0 + l) + return 3; + + else if (d < 151.0 + l) + return 4; + + else if (d < 181.0 + l) + return 5; + + else if (d < 212.0 + l) + return 6; + + else if (d < 243.0 + l) + return 7; + + else if (d < 273.0 + l) + return 8; + + else if (d < 304.0 + l) + return 9; + + else if (d < 334.0 + l) + return 10; + + else if (d < 365.0 + l) + return 11; + + return qSNaN(); // ### assert? +} + +static inline double DateFromTime(double t) +{ + int m = (int) Value::toInteger(MonthFromTime(t)); + double d = DayWithinYear(t); + double l = InLeapYear(t); + + switch (m) { + case 0: return d + 1.0; + case 1: return d - 30.0; + case 2: return d - 58.0 - l; + case 3: return d - 89.0 - l; + case 4: return d - 119.0 - l; + case 5: return d - 150.0 - l; + case 6: return d - 180.0 - l; + case 7: return d - 211.0 - l; + case 8: return d - 242.0 - l; + case 9: return d - 272.0 - l; + case 10: return d - 303.0 - l; + case 11: return d - 333.0 - l; + } + + return qSNaN(); // ### assert +} + +static inline double WeekDay(double t) +{ + double r = ::fmod (Day(t) + 4.0, 7.0); + return (r >= 0) ? r : r + 7.0; +} + + +static inline double MakeTime(double hour, double min, double sec, double ms) +{ + return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms; +} + +static inline double DayFromMonth(double month, double leap) +{ + switch ((int) month) { + case 0: return 0; + case 1: return 31.0; + case 2: return 59.0 + leap; + case 3: return 90.0 + leap; + case 4: return 120.0 + leap; + case 5: return 151.0 + leap; + case 6: return 181.0 + leap; + case 7: return 212.0 + leap; + case 8: return 243.0 + leap; + case 9: return 273.0 + leap; + case 10: return 304.0 + leap; + case 11: return 334.0 + leap; + } + + return qSNaN(); // ### assert? +} + +static double MakeDay(double year, double month, double day) +{ + year += ::floor(month / 12.0); + + month = ::fmod(month, 12.0); + if (month < 0) + month += 12.0; + + double d = DayFromYear(year); + bool leap = InLeapYear(d*msPerDay); + + d += DayFromMonth(month, leap); + d += day - 1; + + return d; +} + +static inline double MakeDate(double day, double time) +{ + return day * msPerDay + time; +} + +static inline double DaylightSavingTA(double t) +{ +#ifndef Q_WS_WIN + long int tt = (long int)(t / msPerSecond); + struct tm tmtm; + if (!localtime_r((const time_t*)&tt, &tmtm)) + return 0; + return (tmtm.tm_isdst > 0) ? msPerHour : 0; +#else + Q_UNUSED(t); + /// ### implement me + return 0; +#endif +} + +static inline double LocalTime(double t) +{ + return t + LocalTZA + DaylightSavingTA(t); +} + +static inline double UTC(double t) +{ + return t - LocalTZA - DaylightSavingTA(t - LocalTZA); +} + +static inline double currentTime() +{ +#ifndef Q_WS_WIN + struct timeval tv; + + gettimeofday(&tv, 0); + return ::floor(tv.tv_sec * msPerSecond + (tv.tv_usec / 1000.0)); +#else + SYSTEMTIME st; + GetSystemTime(&st); + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + LARGE_INTEGER li; + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + return double(li.QuadPart - Q_INT64_C(116444736000000000)) / 10000.0; +#endif +} + +static inline double TimeClip(double t) +{ + if (! qIsFinite(t) || fabs(t) > 8.64e15) + return qSNaN(); + return Value::toInteger(t); +} + +static inline double FromDateTime(const QDateTime &dt) +{ + if (!dt.isValid()) + return qSNaN(); + QDate date = dt.date(); + QTime taim = dt.time(); + int year = date.year(); + int month = date.month() - 1; + int day = date.day(); + int hours = taim.hour(); + int mins = taim.minute(); + int secs = taim.second(); + int ms = taim.msec(); + double t = MakeDate(MakeDay(year, month, day), + MakeTime(hours, mins, secs, ms)); + if (dt.timeSpec() == Qt::LocalTime) + t = UTC(t); + return TimeClip(t); +} + +static inline double ParseString(const QString &s) +{ + // first try the format defined in 15.9.1.15, only if that fails fall back to + // QDateTime for parsing + + // the define string format is YYYY-MM-DDTHH:mm:ss.sssZ + // It can be date or time only, and the second and later components + // of both fields are optional + // and extended syntax for negative and large positive years exists: +/-YYYYYY + + enum Format { + Year, + Month, + Day, + Hour, + Minute, + Second, + MilliSecond, + TimezoneHour, + TimezoneMinute, + Done + }; + + const QChar *ch = s.constData(); + const QChar *end = ch + s.length(); + + uint format = Year; + int current = 0; + int currentSize = 0; + bool extendedYear = false; + + int yearSign = 1; + int year = 0; + int month = 0; + int day = 1; + int hour = 0; + int minute = 0; + int second = 0; + int msec = 0; + int offsetSign = 1; + int offset = 0; + + bool error = false; + if (*ch == '+' || *ch == '-') { + extendedYear = true; + if (*ch == '-') + yearSign = -1; + ++ch; + } + while (ch <= end) { + if (*ch >= '0' && *ch <= '9') { + current *= 10; + current += ch->unicode() - '0'; + ++currentSize; + } else { // other char, delimits field + switch (format) { + case Year: + year = current; + if (extendedYear) + error = (currentSize != 6); + else + error = (currentSize != 4); + break; + case Month: + month = current - 1; + error = (currentSize != 2) || month > 11; + break; + case Day: + day = current; + error = (currentSize != 2) || day > 31; + break; + case Hour: + hour = current; + error = (currentSize != 2) || hour > 24; + break; + case Minute: + minute = current; + error = (currentSize != 2) || minute > 60; + break; + case Second: + second = current; + error = (currentSize != 2) || second > 60; + break; + case MilliSecond: + msec = current; + error = (currentSize != 3); + break; + case TimezoneHour: + offset = current*60; + error = (currentSize != 2) || offset > 23*60; + break; + case TimezoneMinute: + offset += current; + error = (currentSize != 2) || current >= 60; + break; + } + if (*ch == 'T') { + if (format >= Hour) + error = true; + format = Hour; + } else if (*ch == '-') { + if (format < Day) + ++format; + else if (format < Minute) + error = true; + else if (format >= TimezoneHour) + error = true; + else { + offsetSign = -1; + format = TimezoneHour; + } + } else if (*ch == ':') { + if (format != Hour && format != Minute && format != TimezoneHour) + error = true; + ++format; + } else if (*ch == '.') { + if (format != Second) + error = true; + ++format; + } else if (*ch == '+') { + if (format < Minute || format >= TimezoneHour) + error = true; + format = TimezoneHour; + } else if (*ch == 'Z' || *ch == 0) { + format = Done; + } + current = 0; + currentSize = 0; + } + if (error || format == Done) + break; + ++ch; + } + + if (!error) { + double t = MakeDate(MakeDay(year * yearSign, month, day), MakeTime(hour, minute, second, msec)); + t += offset * offsetSign * 60 * 1000; + return t; + } + + QDateTime dt = QDateTime::fromString(s, Qt::TextDate); + if (!dt.isValid()) + dt = QDateTime::fromString(s, Qt::ISODate); + if (!dt.isValid()) { + QStringList formats; + formats << QStringLiteral("M/d/yyyy") + << QStringLiteral("M/d/yyyy hh:mm") + << QStringLiteral("M/d/yyyy hh:mm A") + + << QStringLiteral("M/d/yyyy, hh:mm") + << QStringLiteral("M/d/yyyy, hh:mm A") + + << QStringLiteral("MMM d yyyy") + << QStringLiteral("MMM d yyyy hh:mm") + << QStringLiteral("MMM d yyyy hh:mm:ss") + << QStringLiteral("MMM d yyyy, hh:mm") + << QStringLiteral("MMM d yyyy, hh:mm:ss") + + << QStringLiteral("MMMM d yyyy") + << QStringLiteral("MMMM d yyyy hh:mm") + << QStringLiteral("MMMM d yyyy hh:mm:ss") + << QStringLiteral("MMMM d yyyy, hh:mm") + << QStringLiteral("MMMM d yyyy, hh:mm:ss") + + << QStringLiteral("MMM d, yyyy") + << QStringLiteral("MMM d, yyyy hh:mm") + << QStringLiteral("MMM d, yyyy hh:mm:ss") + + << QStringLiteral("MMMM d, yyyy") + << QStringLiteral("MMMM d, yyyy hh:mm") + << QStringLiteral("MMMM d, yyyy hh:mm:ss") + + << QStringLiteral("d MMM yyyy") + << QStringLiteral("d MMM yyyy hh:mm") + << QStringLiteral("d MMM yyyy hh:mm:ss") + << QStringLiteral("d MMM yyyy, hh:mm") + << QStringLiteral("d MMM yyyy, hh:mm:ss") + + << QStringLiteral("d MMMM yyyy") + << QStringLiteral("d MMMM yyyy hh:mm") + << QStringLiteral("d MMMM yyyy hh:mm:ss") + << QStringLiteral("d MMMM yyyy, hh:mm") + << QStringLiteral("d MMMM yyyy, hh:mm:ss") + + << QStringLiteral("d MMM, yyyy") + << QStringLiteral("d MMM, yyyy hh:mm") + << QStringLiteral("d MMM, yyyy hh:mm:ss") + + << QStringLiteral("d MMMM, yyyy") + << QStringLiteral("d MMMM, yyyy hh:mm") + << QStringLiteral("d MMMM, yyyy hh:mm:ss"); + + for (int i = 0; i < formats.size(); ++i) { + dt = QDateTime::fromString(s, formats.at(i)); + if (dt.isValid()) + break; + } + } + return FromDateTime(dt); +} + +/*! + \internal + + Converts the ECMA Date value \tt (in UTC form) to QDateTime + according to \a spec. +*/ +static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) +{ + if (std::isnan(t)) + return QDateTime(); + if (spec == Qt::LocalTime) + t = LocalTime(t); + int year = int(YearFromTime(t)); + int month = int(MonthFromTime(t) + 1); + int day = int(DateFromTime(t)); + int hours = HourFromTime(t); + int mins = MinFromTime(t); + int secs = SecFromTime(t); + int ms = msFromTime(t); + return QDateTime(QDate(year, month, day), QTime(hours, mins, secs, ms), spec); +} + +static inline QString ToString(double t) +{ + if (std::isnan(t)) + return QStringLiteral("Invalid Date"); + QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT"); + double tzoffset = LocalTZA + DaylightSavingTA(t); + if (tzoffset) { + int hours = static_cast(::fabs(tzoffset) / 1000 / 60 / 60); + int mins = int(::fabs(tzoffset) / 1000 / 60) % 60; + str.append(QLatin1Char((tzoffset > 0) ? '+' : '-')); + if (hours < 10) + str.append(QLatin1Char('0')); + str.append(QString::number(hours)); + if (mins < 10) + str.append(QLatin1Char('0')); + str.append(QString::number(mins)); + } + return str; +} + +static inline QString ToUTCString(double t) +{ + if (std::isnan(t)) + return QStringLiteral("Invalid Date"); + return ToDateTime(t, Qt::UTC).toString() + QStringLiteral(" GMT"); +} + +static inline QString ToDateString(double t) +{ + return ToDateTime(t, Qt::LocalTime).date().toString(); +} + +static inline QString ToTimeString(double t) +{ + return ToDateTime(t, Qt::LocalTime).time().toString(); +} + +static inline QString ToLocaleString(double t) +{ + return ToDateTime(t, Qt::LocalTime).toString(Qt::LocaleDate); +} + +static inline QString ToLocaleDateString(double t) +{ + return ToDateTime(t, Qt::LocalTime).date().toString(Qt::LocaleDate); +} + +static inline QString ToLocaleTimeString(double t) +{ + return ToDateTime(t, Qt::LocalTime).time().toString(Qt::LocaleDate); +} + +static double getLocalTZA() +{ +#ifndef Q_WS_WIN + struct tm t; + time_t curr; + time(&curr); + localtime_r(&curr, &t); + time_t locl = mktime(&t); + gmtime_r(&curr, &t); + time_t globl = mktime(&t); + return double(locl - globl) * 1000.0; +#else + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + return -tzInfo.Bias * 60.0 * 1000.0; +#endif +} + + +DateCtor::DateCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value DateCtor::construct(ExecutionContext *ctx) +{ + double t = 0; + + if (ctx->argumentCount == 0) + t = currentTime(); + + else if (ctx->argumentCount == 1) { + Value arg = ctx->argument(0); + if (DateObject *d = arg.asDateObject()) + arg = d->value; + else + arg = __qmljs_to_primitive(arg, ctx, PREFERREDTYPE_HINT); + + if (arg.isString()) + t = ParseString(arg.stringValue()->toQString()); + else + t = TimeClip(arg.toNumber(ctx)); + } + + else { // ctx->argumentCount > 1 + double year = ctx->argument(0).toNumber(ctx); + double month = ctx->argument(1).toNumber(ctx); + double day = ctx->argumentCount >= 3 ? ctx->argument(2).toNumber(ctx) : 1; + double hours = ctx->argumentCount >= 4 ? ctx->argument(3).toNumber(ctx) : 0; + double mins = ctx->argumentCount >= 5 ? ctx->argument(4).toNumber(ctx) : 0; + double secs = ctx->argumentCount >= 6 ? ctx->argument(5).toNumber(ctx) : 0; + double ms = ctx->argumentCount >= 7 ? ctx->argument(6).toNumber(ctx) : 0; + if (year >= 0 && year <= 99) + year += 1900; + t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); + t = TimeClip(UTC(t)); + } + + Object *d = ctx->engine->newDateObject(Value::fromDouble(t)); + return Value::fromObject(d); +} + +Value DateCtor::call(ExecutionContext *ctx) +{ + double t = currentTime(); + return Value::fromString(ctx, ToString(t)); +} + +void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(7)); + LocalTZA = getLocalTZA(); + + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("parse"), method_parse, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("now"), method_now, 0); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toDateString"), method_toDateString, 0); + defineDefaultProperty(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + defineDefaultProperty(ctx, QStringLiteral("getTime"), method_getTime, 0); + defineDefaultProperty(ctx, QStringLiteral("getYear"), method_getYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getFullYear"), method_getFullYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getMonth"), method_getMonth, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMonth"), method_getUTCMonth, 0); + defineDefaultProperty(ctx, QStringLiteral("getDate"), method_getDate, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCDate"), method_getUTCDate, 0); + defineDefaultProperty(ctx, QStringLiteral("getDay"), method_getDay, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCDay"), method_getUTCDay, 0); + defineDefaultProperty(ctx, QStringLiteral("getHours"), method_getHours, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCHours"), method_getUTCHours, 0); + defineDefaultProperty(ctx, QStringLiteral("getMinutes"), method_getMinutes, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0); + defineDefaultProperty(ctx, QStringLiteral("getSeconds"), method_getSeconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getMilliseconds"), method_getMilliseconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0); + defineDefaultProperty(ctx, QStringLiteral("setTime"), method_setTime, 1); + defineDefaultProperty(ctx, QStringLiteral("setMilliseconds"), method_setMilliseconds, 1); + defineDefaultProperty(ctx, QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1); + defineDefaultProperty(ctx, QStringLiteral("setSeconds"), method_setSeconds, 2); + defineDefaultProperty(ctx, QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2); + defineDefaultProperty(ctx, QStringLiteral("setMinutes"), method_setMinutes, 3); + defineDefaultProperty(ctx, QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3); + defineDefaultProperty(ctx, QStringLiteral("setHours"), method_setHours, 4); + defineDefaultProperty(ctx, QStringLiteral("setUTCHours"), method_setUTCHours, 4); + defineDefaultProperty(ctx, QStringLiteral("setDate"), method_setDate, 1); + defineDefaultProperty(ctx, QStringLiteral("setUTCDate"), method_setUTCDate, 1); + defineDefaultProperty(ctx, QStringLiteral("setMonth"), method_setMonth, 2); + defineDefaultProperty(ctx, QStringLiteral("setUTCMonth"), method_setUTCMonth, 2); + defineDefaultProperty(ctx, QStringLiteral("setYear"), method_setYear, 1); + defineDefaultProperty(ctx, QStringLiteral("setFullYear"), method_setFullYear, 3); + defineDefaultProperty(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); + defineDefaultProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); + defineDefaultProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); + defineDefaultProperty(ctx, QStringLiteral("toISOString"), method_toISOString, 0); + defineDefaultProperty(ctx, QStringLiteral("toJSON"), method_toJSON, 1); +} + +double DatePrototype::getThisDate(ExecutionContext *ctx) +{ + if (DateObject *thisObject = ctx->thisObject.asDateObject()) + return thisObject->value.asDouble(); + else { + ctx->throwTypeError(); + return 0; + } +} + +Value DatePrototype::method_parse(ExecutionContext *ctx) +{ + return Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString())); +} + +Value DatePrototype::method_UTC(ExecutionContext *ctx) +{ + const int numArgs = ctx->argumentCount; + if (numArgs >= 2) { + double year = ctx->argument(0).toNumber(ctx); + double month = ctx->argument(1).toNumber(ctx); + double day = numArgs >= 3 ? ctx->argument(2).toNumber(ctx) : 1; + double hours = numArgs >= 4 ? ctx->argument(3).toNumber(ctx) : 0; + double mins = numArgs >= 5 ? ctx->argument(4).toNumber(ctx) : 0; + double secs = numArgs >= 6 ? ctx->argument(5).toNumber(ctx) : 0; + double ms = numArgs >= 7 ? ctx->argument(6).toNumber(ctx) : 0; + if (year >= 0 && year <= 99) + year += 1900; + double t = MakeDate(MakeDay(year, month, day), + MakeTime(hours, mins, secs, ms)); + return Value::fromDouble(TimeClip(t)); + } + return Value::undefinedValue(); +} + +Value DatePrototype::method_now(ExecutionContext *ctx) +{ + Q_UNUSED(ctx); + double t = currentTime(); + return Value::fromDouble(t); +} + +Value DatePrototype::method_toString(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToString(t)); +} + +Value DatePrototype::method_toDateString(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToDateString(t)); +} + +Value DatePrototype::method_toTimeString(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToTimeString(t)); +} + +Value DatePrototype::method_toLocaleString(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleString(t)); +} + +Value DatePrototype::method_toLocaleDateString(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleDateString(t)); +} + +Value DatePrototype::method_toLocaleTimeString(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleTimeString(t)); +} + +Value DatePrototype::method_valueOf(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getTime(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getYear(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = YearFromTime(LocalTime(t)) - 1900; + return Value::fromDouble(t); +} + +Value DatePrototype::method_getFullYear(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = YearFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCFullYear(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = YearFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMonth(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MonthFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMonth(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MonthFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getDate(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = DateFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCDate(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = DateFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getDay(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = WeekDay(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCDay(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = WeekDay(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getHours(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = HourFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCHours(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = HourFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMinutes(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MinFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMinutes(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MinFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getSeconds(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = SecFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCSeconds(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = SecFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMilliseconds(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = msFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMilliseconds(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = msFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getTimezoneOffset(ExecutionContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = (t - LocalTime(t)) / msPerMinute; + return Value::fromDouble(t); +} + +Value DatePrototype::method_setTime(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + self->value.setDouble(TimeClip(ctx->argument(0).toNumber(ctx))); + return self->value; +} + +Value DatePrototype::method_setMilliseconds(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double ms = ctx->argument(0).toNumber(ctx); + self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + return self->value; +} + +Value DatePrototype::method_setUTCMilliseconds(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double ms = ctx->argument(0).toNumber(ctx); + self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + return self->value; +} + +Value DatePrototype::method_setSeconds(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double sec = ctx->argument(0).toNumber(ctx); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCSeconds(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double sec = ctx->argument(0).toNumber(ctx); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setMinutes(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double min = ctx->argument(0).toNumber(ctx); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCMinutes(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double min = ctx->argument(0).toNumber(ctx); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setHours(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double hour = ctx->argument(0).toNumber(ctx); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCHours(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double hour = ctx->argument(0).toNumber(ctx); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setDate(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double date = ctx->argument(0).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCDate(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double date = ctx->argument(0).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setMonth(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double month = ctx->argument(0).toNumber(ctx); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCMonth(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double month = ctx->argument(0).toNumber(ctx); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setYear(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + if (std::isnan(t)) + t = 0; + else + t = LocalTime(t); + double year = ctx->argument(0).toNumber(ctx); + double r; + if (std::isnan(year)) { + r = qSNaN(); + } else { + if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99)) + year += 1900; + r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); + r = UTC(MakeDate(r, TimeWithinDay(t))); + r = TimeClip(r); + } + self->value.setDouble(r); + return self->value; +} + +Value DatePrototype::method_setUTCFullYear(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double year = ctx->argument(0).toNumber(ctx); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setFullYear(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + if (std::isnan(t)) + t = 0; + double year = ctx->argument(0).toNumber(ctx); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_toUTCString(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + return Value::fromString(ctx, ToUTCString(t)); +} + +static void addZeroPrefixedInt(QString &str, int num, int nDigits) +{ + str.resize(str.size() + nDigits); + + QChar *c = str.data() + str.size() - 1; + while (nDigits) { + *c = QChar(num % 10 + '0'); + num /= 10; + --c; + --nDigits; + } +} + +Value DatePrototype::method_toISOString(ExecutionContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + if (!std::isfinite(t)) + ctx->throwRangeError(ctx->thisObject); + + QString result; + int year = (int)YearFromTime(t); + if (year < 0 || year > 9999) { + if (qAbs(year) >= 1000000) + return Value::fromString(ctx, QStringLiteral("Invalid Date")); + result += year < 0 ? '-' : '+'; + year = qAbs(year); + addZeroPrefixedInt(result, year, 6); + } else { + addZeroPrefixedInt(result, year, 4); + } + result += '-'; + addZeroPrefixedInt(result, (int)MonthFromTime(t) + 1, 2); + result += '-'; + addZeroPrefixedInt(result, (int)DateFromTime(t), 2); + result += 'T'; + addZeroPrefixedInt(result, HourFromTime(t), 2); + result += ':'; + addZeroPrefixedInt(result, MinFromTime(t), 2); + result += ':'; + addZeroPrefixedInt(result, SecFromTime(t), 2); + result += '.'; + addZeroPrefixedInt(result, msFromTime(t), 3); + result += 'Z'; + + return Value::fromString(ctx, result); +} + +Value DatePrototype::method_toJSON(ExecutionContext *ctx) +{ + Value O = __qmljs_to_object(ctx->thisObject, ctx); + Value tv = __qmljs_to_primitive(O, ctx, NUMBER_HINT); + + if (tv.isNumber() && !std::isfinite(tv.toNumber(ctx))) + return Value::nullValue(); + + FunctionObject *toIso = O.objectValue()->__get__(ctx, ctx->engine->identifier(QStringLiteral("toISOString"))).asFunctionObject(); + + if (!toIso) + __qmljs_throw_type_error(ctx); + + return toIso->call(ctx, ctx->thisObject, 0, 0); +} diff --git a/src/v4/qv4dateobject.h b/src/v4/qv4dateobject.h new file mode 100644 index 0000000000..ede4473217 --- /dev/null +++ b/src/v4/qv4dateobject.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4DATEOBJECT_P_H +#define QV4DATEOBJECT_P_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include + +namespace QQmlJS { +namespace VM { + +struct DateObject: Object { + Value value; + DateObject(const Value &value): value(value) { type = Type_DateObject; } +}; + +struct DateCtor: FunctionObject +{ + DateCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct DatePrototype: DateObject +{ + DatePrototype(): DateObject(Value::fromDouble(qSNaN())) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static double getThisDate(ExecutionContext *ctx); + + static Value method_parse(ExecutionContext *ctx); + static Value method_UTC(ExecutionContext *ctx); + static Value method_now(ExecutionContext *ctx); + + static Value method_toString(ExecutionContext *ctx); + static Value method_toDateString(ExecutionContext *ctx); + static Value method_toTimeString(ExecutionContext *ctx); + static Value method_toLocaleString(ExecutionContext *ctx); + static Value method_toLocaleDateString(ExecutionContext *ctx); + static Value method_toLocaleTimeString(ExecutionContext *ctx); + static Value method_valueOf(ExecutionContext *ctx); + static Value method_getTime(ExecutionContext *ctx); + static Value method_getYear(ExecutionContext *ctx); + static Value method_getFullYear(ExecutionContext *ctx); + static Value method_getUTCFullYear(ExecutionContext *ctx); + static Value method_getMonth(ExecutionContext *ctx); + static Value method_getUTCMonth(ExecutionContext *ctx); + static Value method_getDate(ExecutionContext *ctx); + static Value method_getUTCDate(ExecutionContext *ctx); + static Value method_getDay(ExecutionContext *ctx); + static Value method_getUTCDay(ExecutionContext *ctx); + static Value method_getHours(ExecutionContext *ctx); + static Value method_getUTCHours(ExecutionContext *ctx); + static Value method_getMinutes(ExecutionContext *ctx); + static Value method_getUTCMinutes(ExecutionContext *ctx); + static Value method_getSeconds(ExecutionContext *ctx); + static Value method_getUTCSeconds(ExecutionContext *ctx); + static Value method_getMilliseconds(ExecutionContext *ctx); + static Value method_getUTCMilliseconds(ExecutionContext *ctx); + static Value method_getTimezoneOffset(ExecutionContext *ctx); + static Value method_setTime(ExecutionContext *ctx); + static Value method_setMilliseconds(ExecutionContext *ctx); + static Value method_setUTCMilliseconds(ExecutionContext *ctx); + static Value method_setSeconds(ExecutionContext *ctx); + static Value method_setUTCSeconds(ExecutionContext *ctx); + static Value method_setMinutes(ExecutionContext *ctx); + static Value method_setUTCMinutes(ExecutionContext *ctx); + static Value method_setHours(ExecutionContext *ctx); + static Value method_setUTCHours(ExecutionContext *ctx); + static Value method_setDate(ExecutionContext *ctx); + static Value method_setUTCDate(ExecutionContext *ctx); + static Value method_setMonth(ExecutionContext *ctx); + static Value method_setUTCMonth(ExecutionContext *ctx); + static Value method_setYear(ExecutionContext *ctx); + static Value method_setFullYear(ExecutionContext *ctx); + static Value method_setUTCFullYear(ExecutionContext *ctx); + static Value method_toUTCString(ExecutionContext *ctx); + static Value method_toISOString(ExecutionContext *ctx); + static Value method_toJSON(ExecutionContext *ctx); +}; + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/v4/qv4errorobject.cpp b/src/v4/qv4errorobject.cpp new file mode 100644 index 0000000000..9d4a067f35 --- /dev/null +++ b/src/v4/qv4errorobject.cpp @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4errorobject.h" +#include "qv4mm.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef Q_WS_WIN +# include +# ifndef Q_OS_VXWORKS +# include +# else +# include "qplatformdefs.h" +# endif +#else +# include +#endif + +using namespace QQmlJS::VM; + +ErrorObject::ErrorObject(ExecutionEngine* engine, const Value &message) + : errorType(Error) +{ + type = Type_ErrorObject; + + if (message.type() != Value::Undefined_Type) + defineDefaultProperty(engine->identifier(QStringLiteral("message")), message); +} + +void ErrorObject::setNameProperty(ExecutionContext *ctx) +{ + defineDefaultProperty(ctx, QLatin1String("name"), Value::fromString(ctx, className())); +} + +SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) + : ErrorObject(ctx->engine, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0)) + , msg(message) +{ + errorType = SyntaxError; + prototype = ctx->engine->syntaxErrorPrototype; + setNameProperty(ctx); +} + + + +EvalErrorObject::EvalErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->engine, ctx->argument(0)) +{ + errorType = EvalError; + setNameProperty(ctx); + prototype = ctx->engine->evalErrorPrototype; +} + +RangeErrorObject::RangeErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->engine, ctx->argument(0)) +{ + errorType = RangeError; + setNameProperty(ctx); + prototype = ctx->engine->rangeErrorPrototype; +} + +RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &msg) + : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) +{ + errorType = RangeError; + setNameProperty(ctx); + prototype = ctx->engine->rangeErrorPrototype; +} + +ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->engine, ctx->argument(0)) +{ + errorType = ReferenceError; + setNameProperty(ctx); + prototype = ctx->engine->referenceErrorPrototype; +} + +ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString &msg) + : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) +{ + errorType = ReferenceError; + setNameProperty(ctx); + prototype = ctx->engine->referenceErrorPrototype; +} + +TypeErrorObject::TypeErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->engine, ctx->argument(0)) +{ + errorType = TypeError; + setNameProperty(ctx); + prototype = ctx->engine->typeErrorPrototype; +} + +TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &msg) + : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) +{ + errorType = TypeError; + setNameProperty(ctx); + prototype = ctx->engine->typeErrorPrototype; +} + +URIErrorObject::URIErrorObject(ExecutionContext *ctx) + : ErrorObject(ctx->engine, ctx->argument(0)) +{ + errorType = URIError; + setNameProperty(ctx); + prototype = ctx->engine->uRIErrorPrototype; +} + +URIErrorObject::URIErrorObject(ExecutionContext *ctx, Value msg) + : ErrorObject(ctx->engine, msg) +{ + errorType = URIError; + setNameProperty(ctx); + prototype = ctx->engine->uRIErrorPrototype; +} + + +ErrorCtor::ErrorCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value ErrorCtor::construct(ExecutionContext *ctx) +{ + return Value::fromObject(ctx->engine->newErrorObject(ctx->argument(0))); +} + +Value ErrorCtor::call(ExecutionContext *ctx) +{ + return construct(ctx); +} + +Value EvalErrorCtor::construct(ExecutionContext *ctx) +{ + return Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx)); +} + +Value RangeErrorCtor::construct(ExecutionContext *ctx) +{ + return Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx)); +} + +Value ReferenceErrorCtor::construct(ExecutionContext *ctx) +{ + return Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx)); +} + +Value SyntaxErrorCtor::construct(ExecutionContext *ctx) +{ + return Value::fromObject(new (ctx->engine->memoryManager) SyntaxErrorObject(ctx, 0)); +} + +Value TypeErrorCtor::construct(ExecutionContext *ctx) +{ + return Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx)); +} + +Value URIErrorCtor::construct(ExecutionContext *ctx) +{ + return Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx)); +} + +void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(obj)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + obj->defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + obj->defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + obj->defineDefaultProperty(ctx, QStringLiteral("message"), Value::fromString(ctx, QString())); + obj->defineDefaultProperty(ctx, QStringLiteral("name"), Value::fromString(ctx, QStringLiteral("Error"))); +} + +Value ErrorPrototype::method_toString(ExecutionContext *ctx) +{ + Object *o = ctx->thisObject.asObject(); + if (!o) + __qmljs_throw_type_error(ctx); + + Value name = o->__get__(ctx, ctx->engine->newString(QString::fromLatin1("name"))); + QString qname; + if (name.isUndefined()) + qname = QString::fromLatin1("Error"); + else + qname = __qmljs_to_string(name, ctx).stringValue()->toQString(); + + Value message = o->__get__(ctx, ctx->engine->newString(QString::fromLatin1("message"))); + QString qmessage; + if (!message.isUndefined()) + qmessage = __qmljs_to_string(message, ctx).stringValue()->toQString(); + + QString str; + if (qname.isEmpty()) { + str = qmessage; + } else if (qmessage.isEmpty()) { + str = qname; + } else { + str = qname + QLatin1String(": ") + qmessage; + } + + return Value::fromString(ctx, str); +} diff --git a/src/v4/qv4errorobject.h b/src/v4/qv4errorobject.h new file mode 100644 index 0000000000..d2d2340fae --- /dev/null +++ b/src/v4/qv4errorobject.h @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ERROROBJECT_H +#define QV4ERROROBJECT_H + +#include "qv4object.h" +#include "qv4functionobject.h" + +namespace QQmlJS { +namespace VM { + +struct ErrorObject: Object { + enum ErrorType { + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError + }; + ErrorType errorType; + + ErrorObject(ExecutionEngine* engine, const Value &message); + + virtual struct SyntaxErrorObject *asSyntaxError() { return 0; } + +protected: + void setNameProperty(ExecutionContext *ctx); +}; + +struct EvalErrorObject: ErrorObject { + EvalErrorObject(ExecutionContext *ctx); +}; + +struct RangeErrorObject: ErrorObject { + RangeErrorObject(ExecutionContext *ctx); + RangeErrorObject(ExecutionContext *ctx, const QString &msg); +}; + +struct ReferenceErrorObject: ErrorObject { + ReferenceErrorObject(ExecutionContext *ctx); + ReferenceErrorObject(ExecutionContext *ctx, const QString &msg); +}; + +struct SyntaxErrorObject: ErrorObject { + SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg); + ~SyntaxErrorObject() { delete msg; } + + virtual SyntaxErrorObject *asSyntaxError() { return this; } + DiagnosticMessage *message() { return msg; } + +private: + DiagnosticMessage *msg; +}; + +struct TypeErrorObject: ErrorObject { + TypeErrorObject(ExecutionContext *ctx); + TypeErrorObject(ExecutionContext *ctx, const QString &msg); +}; + +struct URIErrorObject: ErrorObject { + URIErrorObject(ExecutionContext *ctx); + URIErrorObject(ExecutionContext *ctx, Value); +}; + +struct ErrorCtor: FunctionObject +{ + ErrorCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct EvalErrorCtor: ErrorCtor +{ + EvalErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + + virtual Value construct(ExecutionContext *ctx); +}; + +struct RangeErrorCtor: ErrorCtor +{ + RangeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + + virtual Value construct(ExecutionContext *ctx); +}; + +struct ReferenceErrorCtor: ErrorCtor +{ + ReferenceErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + + virtual Value construct(ExecutionContext *ctx); +}; + +struct SyntaxErrorCtor: ErrorCtor +{ + SyntaxErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + + virtual Value construct(ExecutionContext *ctx); +}; + +struct TypeErrorCtor: ErrorCtor +{ + TypeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + + virtual Value construct(ExecutionContext *ctx); +}; + +struct URIErrorCtor: ErrorCtor +{ + URIErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + + virtual Value construct(ExecutionContext *ctx); +}; + + +struct ErrorPrototype: ErrorObject +{ + // ### shouldn't be undefined + ErrorPrototype(ExecutionEngine* engine): ErrorObject(engine, Value::undefinedValue()) {} + void init(ExecutionContext *ctx, const Value &ctor) { init(ctx, ctor, this); } + + static void init(ExecutionContext *ctx, const Value &ctor, Object *obj); + static Value method_toString(ExecutionContext *ctx); +}; + +struct EvalErrorPrototype: EvalErrorObject +{ + EvalErrorPrototype(ExecutionContext *ctx): EvalErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct RangeErrorPrototype: RangeErrorObject +{ + RangeErrorPrototype(ExecutionContext *ctx): RangeErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct ReferenceErrorPrototype: ReferenceErrorObject +{ + ReferenceErrorPrototype(ExecutionContext *ctx): ReferenceErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct SyntaxErrorPrototype: SyntaxErrorObject +{ + SyntaxErrorPrototype(ExecutionContext *ctx): SyntaxErrorObject(ctx, 0) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct TypeErrorPrototype: TypeErrorObject +{ + TypeErrorPrototype(ExecutionContext *ctx): TypeErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct URIErrorPrototype: URIErrorObject +{ + URIErrorPrototype(ExecutionContext *ctx): URIErrorObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp new file mode 100644 index 0000000000..f5e37def0c --- /dev/null +++ b/src/v4/qv4functionobject.cpp @@ -0,0 +1,465 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4object.h" +#include "qv4ir_p.h" +#include "qv4isel_p.h" +#include "qv4objectproto.h" +#include "qv4stringobject.h" +#include "qv4mm.h" + +#include +#include +#include +#include +#include +#include +#include "private/qlocale_tools_p.h" + +#include +#include +#include +#include +#include +#include + +using namespace QQmlJS::VM; + + +Function::~Function() +{ + delete[] codeData; +} + +void Function::mark() +{ + if (name) + name->mark(); + for (int i = 0; i < formals.size(); ++i) + formals.at(i)->mark(); + for (int i = 0; i < locals.size(); ++i) + locals.at(i)->mark(); + for (int i = 0; i < generatedValues.size(); ++i) + if (Managed *m = generatedValues.at(i).asManaged()) + m->mark(); + for (int i = 0; i < identifiers.size(); ++i) + identifiers.at(i)->mark(); +} + +FunctionObject::FunctionObject(ExecutionContext *scope) + : scope(scope) + , name(0) + , formalParameterList(0) + , varList(0) + , formalParameterCount(0) + , varCount(0) +{ + prototype = scope->engine->functionPrototype; + + type = Type_FunctionObject; + needsActivation = false; + usesArgumentsObject = false; + strictMode = false; +} + +bool FunctionObject::hasInstance(ExecutionContext *ctx, const Value &value) +{ + if (! value.isObject()) + return false; + + Value o = __get__(ctx, ctx->engine->id_prototype); + if (! o.isObject()) { + ctx->throwTypeError(); + return false; + } + + Object *v = value.objectValue(); + while (v) { + v = v->prototype; + + if (! v) + break; + else if (o.objectValue() == v) + return true; + } + + return false; +} + +Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc) +{ + Object *obj = context->engine->newObject(); + Value proto = __get__(context, context->engine->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + + uint size = requiredMemoryForExecutionContect(this, argc); + ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); + + ctx->initCallContext(context, Value::fromObject(obj), this, args, argc); + Value result = construct(ctx); + ctx->leaveCallContext(); + + if (result.isObject()) + return result; + return Value::fromObject(obj); +} + +Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +{ + uint size = requiredMemoryForExecutionContect(this, argc); + ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); + + + ctx->initCallContext(context, thisObject, this, args, argc); + if (isBuiltinFunction) { + // Built-in functions allow for the this object to be null or undefined. This overrides + // the behaviour of changing thisObject to the global object if null/undefined and allows + // the built-in functions for example to throw a type error if null is passed. + if (thisObject.isNull() || thisObject.isUndefined()) + ctx->thisObject = thisObject; + } + Value result = call(ctx); + ctx->leaveCallContext(); + return result; +} + +void FunctionObject::markObjects() +{ + if (name) + name->mark(); + // these are marked in VM::Function: +// for (uint i = 0; i < formalParameterCount; ++i) +// formalParameterList[i]->mark(); +// for (uint i = 0; i < varCount; ++i) +// varList[i]->mark(); + scope->mark(); + Object::markObjects(); +} + +Value FunctionObject::call(ExecutionContext *ctx) +{ + Q_UNUSED(ctx); + return Value::undefinedValue(); +} + +Value FunctionObject::construct(ExecutionContext *ctx) +{ + return call(ctx); +} + + +FunctionCtor::FunctionCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +// 15.3.2 +Value FunctionCtor::construct(ExecutionContext *ctx) +{ + MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); + + QString args; + QString body; + if (ctx->argumentCount > 0) { + for (uint i = 0; i < ctx->argumentCount - 1; ++i) { + if (i) + args += QLatin1String(", "); + args += ctx->argument(i).toString(ctx)->toQString(); + } + body = ctx->argument(ctx->argumentCount - 1).toString(ctx)->toQString(); + } + + QString function = QLatin1String("function(") + args + QLatin1String("){") + body + QLatin1String("}"); + + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(function, 1, false); + Parser parser(engine); + + const bool parsed = parser.parseExpression(); + + if (!parsed) + ctx->throwSyntaxError(0); + + using namespace AST; + FunctionExpression *fe = AST::cast(parser.rootNode()); + if (!fe) + ctx->throwSyntaxError(0); + + IR::Module module; + + Codegen cg(ctx, ctx->strictMode); + IR::Function *irf = cg(QString(), fe, &module); + + QScopedPointer isel(ctx->engine->iselFactory->create(ctx->engine, &module)); + VM::Function *vmf = isel->vmFunction(irf); + + return Value::fromObject(ctx->engine->newScriptFunction(ctx->engine->rootContext, vmf)); +} + +// 15.3.1: This is equivalent to new Function(...) +Value FunctionCtor::call(ExecutionContext *ctx) +{ + return construct(ctx); +} + +void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2); + defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 1); + defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 1); + + defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(0)); +} + +Value FunctionPrototype::method_toString(ExecutionContext *ctx) +{ + FunctionObject *fun = ctx->thisObject.asFunctionObject(); + if (!fun) + ctx->throwTypeError(); + + return Value::fromString(ctx, QStringLiteral("function() { [code] }")); +} + +Value FunctionPrototype::method_apply(ExecutionContext *ctx) +{ + Value thisArg = ctx->argument(0); + + Value arg = ctx->argument(1); + QVector args; + + if (Object *arr = arg.asObject()) { + quint32 len = arr->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); + for (quint32 i = 0; i < len; ++i) { + Value a = arr->__get__(ctx, i); + args.append(a); + } + } else if (!(arg.isUndefined() || arg.isNull())) { + ctx->throwTypeError(); + return Value::undefinedValue(); + } + + FunctionObject *o = ctx->thisObject.asFunctionObject(); + if (!o) + ctx->throwTypeError(); + + return o->call(ctx, thisArg, args.data(), args.size()); +} + +Value FunctionPrototype::method_call(ExecutionContext *ctx) +{ + Value thisArg = ctx->argument(0); + + QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); + if (ctx->argumentCount) + qCopy(ctx->arguments + 1, + ctx->arguments + ctx->argumentCount, args.begin()); + + FunctionObject *o = ctx->thisObject.asFunctionObject(); + if (!o) + ctx->throwTypeError(); + + return o->call(ctx, thisArg, args.data(), args.size()); +} + +Value FunctionPrototype::method_bind(ExecutionContext *ctx) +{ + FunctionObject *target = ctx->thisObject.asFunctionObject(); + if (!target) + ctx->throwTypeError(); + + Value boundThis = ctx->argument(0); + QVector boundArgs; + for (uint i = 1; i < ctx->argumentCount; ++i) + boundArgs += ctx->argument(i); + + + BoundFunction *f = ctx->engine->newBoundFunction(ctx, target, boundThis, boundArgs); + return Value::fromObject(f); +} + +ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) + : FunctionObject(scope) + , function(function) +{ + assert(function); + assert(function->code); + + // global function + if (!scope) + return; + + MemoryManager::GCBlocker gcBlocker(scope->engine->memoryManager); + + name = function->name; + needsActivation = function->needsActivation(); + usesArgumentsObject = function->usesArgumentsObject; + strictMode = function->isStrict; + formalParameterCount = function->formals.size(); + formalParameterList = function->formals.constData(); + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(formalParameterCount)); + + varCount = function->locals.size(); + varList = function->locals.constData(); + + Object *proto = scope->engine->newObject(); + proto->defineDefaultProperty(scope->engine->id_constructor, Value::fromObject(this)); + PropertyDescriptor *pd = members->insert(scope->engine->id_prototype); + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Disabled; + pd->value = Value::fromObject(proto); + + if (scope->strictMode) { + FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error); + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); + pd.configurable = PropertyDescriptor::Disabled; + pd.enumberable = PropertyDescriptor::Disabled; + __defineOwnProperty__(scope, QStringLiteral("caller"), &pd); + __defineOwnProperty__(scope, QStringLiteral("arguments"), &pd); + } +} + +ScriptFunction::~ScriptFunction() +{ +} + +Value ScriptFunction::call(VM::ExecutionContext *ctx) +{ + assert(function->code); + return function->code(ctx, function->codeData); +} + +void ScriptFunction::markObjects() +{ + function->mark(); + FunctionObject::markObjects(); +} + + +BuiltinFunctionOld::BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) + : FunctionObject(scope) + , code(code) +{ + this->name = name; + isBuiltinFunction = true; +} + +Value BuiltinFunctionOld::construct(ExecutionContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)) + : FunctionObject(scope) + , code(code) +{ + this->name = name; + isBuiltinFunction = true; +} + +Value BuiltinFunction::construct(ExecutionContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + + +BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) + : FunctionObject(scope) + , target(target) + , boundThis(boundThis) + , boundArgs(boundArgs) +{ + int len = target->__get__(scope, scope->engine->id_length).toUInt32(scope); + len -= boundArgs.size(); + if (len < 0) + len = 0; + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(len)); + + FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error); + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); + pd.configurable = PropertyDescriptor::Disabled; + pd.enumberable = PropertyDescriptor::Disabled; + *members->insert(scope->engine->id_arguments) = pd; + *members->insert(scope->engine->id_caller) = pd; +} + +Value BoundFunction::call(ExecutionContext *context, Value, Value *args, int argc) +{ + Value *newArgs = static_cast(alloca(sizeof(Value)*(boundArgs.size() + argc))); + memcpy(newArgs, boundArgs.constData(), boundArgs.size()*sizeof(Value)); + memcpy(newArgs + boundArgs.size(), args, argc*sizeof(Value)); + + return target->call(context, boundThis, newArgs, boundArgs.size() + argc); +} + +Value BoundFunction::construct(ExecutionContext *context, Value *args, int argc) +{ + Value *newArgs = static_cast(alloca(sizeof(Value)*(boundArgs.size() + argc))); + memcpy(newArgs, boundArgs.constData(), boundArgs.size()*sizeof(Value)); + memcpy(newArgs + boundArgs.size(), args, argc*sizeof(Value)); + + return target->construct(context, newArgs, boundArgs.size() + argc); +} + +bool BoundFunction::hasInstance(ExecutionContext *ctx, const Value &value) +{ + return target->hasInstance(ctx, value); +} + +void BoundFunction::markObjects() +{ + target->mark(); + if (Managed *m = boundThis.asManaged()) + m->mark(); + for (int i = 0; i < boundArgs.size(); ++i) + if (Managed *m = boundArgs.at(i).asManaged()) + m->mark(); + FunctionObject::markObjects(); +} diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h new file mode 100644 index 0000000000..b651eedcae --- /dev/null +++ b/src/v4/qv4functionobject.h @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4FUNCTIONOBJECT_H +#define QV4FUNCTIONOBJECT_H + +#include "qv4global.h" +#include "qmljs_runtime.h" +#include "qmljs_engine.h" +#include "qmljs_environment.h" +#include "qv4object.h" +#include "qv4array.h" +#include "qv4string.h" +#include "qv4codegen_p.h" +#include "qv4isel_p.h" +#include "qv4managed.h" +#include "qv4propertydescriptor.h" +#include "qv4propertytable.h" +#include "qv4objectiterator.h" +#include "qv4regexp.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace QQmlJS { + +namespace VM { + +struct Value; +struct Function; +struct Object; +struct ObjectIterator; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct ExecutionEngine; +class MemoryManager; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct RegExpPrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; + +struct Function { + String *name; + + VM::Value (*code)(VM::ExecutionContext *, const uchar *); + const uchar *codeData; + JSC::MacroAssemblerCodeRef codeRef; + + QVector formals; + QVector locals; + QVector generatedValues; + QVector identifiers; + + bool hasNestedFunctions : 1; + bool hasDirectEval : 1; + bool usesArgumentsObject : 1; + bool isStrict : 1; + + Function(String *name) + : name(name) + , code(0) + , codeData(0) + , hasNestedFunctions(0) + , hasDirectEval(false) + , usesArgumentsObject(false) + , isStrict(false) + {} + ~Function(); + + inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; } + + void mark(); +}; + +struct Q_V4_EXPORT FunctionObject: Object { + ExecutionContext *scope; + String *name; + String * const *formalParameterList; + String * const *varList; + unsigned int formalParameterCount; + unsigned int varCount; + + FunctionObject(ExecutionContext *scope); + + virtual bool hasInstance(ExecutionContext *ctx, const Value &value); + + virtual Value construct(ExecutionContext *context, Value *args, int argc); + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); + + virtual struct ScriptFunction *asScriptFunction() { return 0; } + + virtual void markObjects(); + +protected: + virtual Value call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx); +}; + +struct FunctionCtor: FunctionObject +{ + FunctionCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct FunctionPrototype: FunctionObject +{ + FunctionPrototype(ExecutionContext *ctx): FunctionObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(ExecutionContext *ctx); + static Value method_apply(ExecutionContext *ctx); + static Value method_call(ExecutionContext *ctx); + static Value method_bind(ExecutionContext *ctx); +}; + +struct BuiltinFunctionOld: FunctionObject { + Value (*code)(ExecutionContext *); + + BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); + virtual Value call(ExecutionContext *ctx) { return code(ctx); } + virtual Value construct(ExecutionContext *ctx); +}; + +struct BuiltinFunction: FunctionObject { + Value (*code)(ExecutionContext *parentContext, Value thisObject, Value *args, int argc); + + BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)); + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc) { + return code(context, thisObject, args, argc); + } + virtual Value construct(ExecutionContext *ctx); +}; + +struct ScriptFunction: FunctionObject { + VM::Function *function; + + ScriptFunction(ExecutionContext *scope, VM::Function *function); + virtual ~ScriptFunction(); + + virtual Value call(ExecutionContext *ctx); + + virtual ScriptFunction *asScriptFunction() { return this; } + + virtual void markObjects(); +}; + +struct BoundFunction: FunctionObject { + FunctionObject *target; + Value boundThis; + QVector boundArgs; + + BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); + virtual ~BoundFunction() {} + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); + virtual Value construct(ExecutionContext *context, Value *args, int argc); + virtual bool hasInstance(ExecutionContext *ctx, const Value &value); + virtual void markObjects(); +}; + +} // namespace VM +} // namespace QQmlJS + +#endif // QMLJS_OBJECTS_H diff --git a/src/v4/qv4global.h b/src/v4/qv4global.h new file mode 100644 index 0000000000..6b1c9a21ec --- /dev/null +++ b/src/v4/qv4global.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QV4GLOBAL_H +#define QV4GLOBAL_H + +#include + +QT_BEGIN_HEADER + +#ifndef QT_STATIC +# if defined(QT_BUILD_V4_LIB) +# define Q_V4_EXPORT Q_DECL_EXPORT +# else +# define Q_V4_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_V4_EXPORT +#endif + +QT_END_NAMESPACE + +#endif // QV4GLOBAL_H diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp new file mode 100644 index 0000000000..74cd32d88a --- /dev/null +++ b/src/v4/qv4globalobject.cpp @@ -0,0 +1,670 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4globalobject.h" +#include "qv4mm.h" +#include "qmljs_value.h" +#include "qmljs_environment.h" + +#include +#include +#include +#include +#include +#include +#include "private/qlocale_tools_p.h" + +#include +#include +#include +#include + +using namespace QQmlJS::VM; + +static inline char toHex(char c) +{ + static const char hexnumbers[] = "0123456789ABCDEF"; + return hexnumbers[c & 0xf]; +} + +static int fromHex(QChar ch) +{ + ushort c = ch.unicode(); + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'A') && (c <= 'F')) + return c - 'A' + 10; + if ((c >= 'a') && (c <= 'f')) + return c - 'a' + 10; + return -1; +} + +static QString escape(const QString &input) +{ + QString output; + output.reserve(input.size() * 3); + const int length = input.length(); + for (int i = 0; i < length; ++i) { + ushort uc = input.at(i).unicode(); + if (uc < 0x100) { + if ( (uc > 0x60 && uc < 0x7B) + || (uc > 0x3F && uc < 0x5B) + || (uc > 0x2C && uc < 0x3A) + || (uc == 0x2A) + || (uc == 0x2B) + || (uc == 0x5F)) { + output.append(QChar(uc)); + } else { + output.append('%'); + output.append(QChar(toHex(uc >> 4))); + output.append(QChar(toHex(uc))); + } + } else { + output.append('%'); + output.append('u'); + output.append(QChar(toHex(uc >> 12))); + output.append(QChar(toHex(uc >> 8))); + output.append(QChar(toHex(uc >> 4))); + output.append(QChar(toHex(uc))); + } + } + return output; +} + +static QString unescape(const QString &input) +{ + QString result; + result.reserve(input.length()); + int i = 0; + const int length = input.length(); + while (i < length) { + QChar c = input.at(i++); + if ((c == '%') && (i + 1 < length)) { + QChar a = input.at(i); + if ((a == 'u') && (i + 4 < length)) { + int d3 = fromHex(input.at(i+1)); + int d2 = fromHex(input.at(i+2)); + int d1 = fromHex(input.at(i+3)); + int d0 = fromHex(input.at(i+4)); + if ((d3 != -1) && (d2 != -1) && (d1 != -1) && (d0 != -1)) { + ushort uc = ushort((d3 << 12) | (d2 << 8) | (d1 << 4) | d0); + result.append(QChar(uc)); + i += 5; + } else { + result.append(c); + } + } else { + int d1 = fromHex(a); + int d0 = fromHex(input.at(i+1)); + if ((d1 != -1) && (d0 != -1)) { + c = (d1 << 4) | d0; + i += 2; + } + result.append(c); + } + } else { + result.append(c); + } + } + return result; +} + +static const char uriReserved[] = ";/?:@&=+$,"; +static const char uriUnescaped[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()"; + +static void addEscapeSequence(QString &output, uchar ch) +{ + output.append(QLatin1Char('%')); + output.append(QLatin1Char(toHex(ch >> 4))); + output.append(QLatin1Char(toHex(ch & 0xf))); +} + +static QString encode(const QString &input, const QString &unescapedSet, bool *ok) +{ + *ok = true; + QString output; + const int length = input.length(); + int i = 0; + while (i < length) { + const QChar c = input.at(i); + if (!unescapedSet.contains(c)) { + uint uc = c.unicode(); + if ((uc >= 0xDC00) && (uc <= 0xDFFF)) { + *ok = false; + break; + } + if (!((uc < 0xD800) || (uc > 0xDBFF))) { + ++i; + if (i == length) { + *ok = false; + break; + } + const uint uc2 = input.at(i).unicode(); + if ((uc2 < 0xDC00) || (uc2 > 0xDFFF)) { + *ok = false; + break; + } + uc = ((uc - 0xD800) * 0x400) + (uc2 - 0xDC00) + 0x10000; + } + if (uc < 0x80) { + addEscapeSequence(output, (uchar)uc); + } else { + if (uc < 0x0800) { + addEscapeSequence(output, 0xc0 | ((uchar) (uc >> 6))); + } else { + + if (QChar::requiresSurrogates(uc)) { + addEscapeSequence(output, 0xf0 | ((uchar) (uc >> 18))); + addEscapeSequence(output, 0x80 | (((uchar) (uc >> 12)) & 0x3f)); + } else { + addEscapeSequence(output, 0xe0 | (((uchar) (uc >> 12)) & 0x3f)); + } + addEscapeSequence(output, 0x80 | (((uchar) (uc >> 6)) & 0x3f)); + } + addEscapeSequence(output, 0x80 | ((uchar) (uc&0x3f))); + } + } else { + output.append(c); + } + ++i; + } + if (i != length) + *ok = false; + return output; +} + +static QString decode(const QString &input, const QString &reservedSet, bool *ok) +{ + *ok = true; + QString output; + const int length = input.length(); + int i = 0; + const QChar percent = QLatin1Char('%'); + while (i < length) { + const QChar ch = input.at(i); + if (ch == percent) { + int start = i; + if (i + 2 >= length) + goto error; + + int d1 = fromHex(input.at(i+1)); + int d0 = fromHex(input.at(i+2)); + if ((d1 == -1) || (d0 == -1)) + goto error; + + int b = (d1 << 4) | d0; + i += 2; + if (b & 0x80) { + int uc; + int min_uc; + int need; + if ((b & 0xe0) == 0xc0) { + uc = b & 0x1f; + need = 1; + min_uc = 0x80; + } else if ((b & 0xf0) == 0xe0) { + uc = b & 0x0f; + need = 2; + min_uc = 0x800; + } else if ((b & 0xf8) == 0xf0) { + uc = b & 0x07; + need = 3; + min_uc = 0x10000; + } else { + goto error; + } + + if (i + (3 * need) >= length) + goto error; + + for (int j = 0; j < need; ++j) { + ++i; + if (input.at(i) != percent) + goto error; + + d1 = fromHex(input.at(i+1)); + d0 = fromHex(input.at(i+2)); + if ((d1 == -1) || (d0 == -1)) + goto error; + + b = (d1 << 4) | d0; + if ((b & 0xC0) != 0x80) + goto error; + + i += 2; + uc = (uc << 6) + (b & 0x3f); + } + if (uc < min_uc) + goto error; + + if (uc < 0x10000) { + output.append(QChar(uc)); + } else { + if (uc > 0x10FFFF) + goto error; + + ushort l = ushort(((uc - 0x10000) & 0x3FF) + 0xDC00); + ushort h = ushort((((uc - 0x10000) >> 10) & 0x3FF) + 0xD800); + output.append(QChar(h)); + output.append(QChar(l)); + } + } else { + QChar z(b); + if (!reservedSet.contains(z)) { + output.append(z); + } else { + output.append(input.mid(start, i - start + 1)); + } + } + } else { + output.append(ch); + } + ++i; + } + if (i != length) + *ok = false; + return output; + error: + *ok = false; + return QString(); +} + + +EvalFunction::EvalFunction(ExecutionContext *scope) + : FunctionObject(scope) +{ + name = scope->engine->id_eval; + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); +} + +Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc, bool directCall) +{ + if (argc < 1) + return Value::undefinedValue(); + + ExecutionContext *ctx = context; + + if (!directCall) { + // the context for eval should be the global scope + while (ctx->parent) + ctx = ctx->parent; + } + + if (!args[0].isString()) + return args[0]; + + const QString code = args[0].stringValue()->toQString(); + bool inheritContext = !ctx->strictMode; + + QQmlJS::VM::Function *f = parseSource(context, QStringLiteral("eval code"), + code, QQmlJS::Codegen::EvalCode, + (directCall && context->strictMode), inheritContext); + + if (!f) + return Value::undefinedValue(); + + bool strict = f->isStrict || (directCall && context->strictMode); + + uint size = requiredMemoryForExecutionContect(this, argc); + ExecutionContext *k = static_cast(alloca(size)); + + if (strict) { + ctx = k; + ctx->initCallContext(context, context->thisObject, this, args, argc); + } + + // set the correct strict mode flag on the context + bool cstrict = ctx->strictMode; + ctx->strictMode = strict; + + Value result = f->code(ctx, f->codeData); + + ctx->strictMode = cstrict; + + if (strict) + ctx->leaveCallContext(); + + return result; +} + + +Value EvalFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +{ + // indirect call + return evalCall(context, thisObject, args, argc, false); +} + +Value EvalFunction::construct(ExecutionContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, + const QString &fileName, const QString &source, + QQmlJS::Codegen::Mode mode, + bool strictMode, bool inheritContext) +{ + using namespace QQmlJS; + + MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); + + VM::ExecutionEngine *vm = ctx->engine; + IR::Module module; + VM::Function *globalCode = 0; + + { + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(source, 1, false); + Parser parser(engine); + + const bool parsed = parser.parseProgram(); + + VM::DiagnosticMessage *error = 0, **errIt = &error; + foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { + if (m.isError()) { + *errIt = new VM::DiagnosticMessage; + (*errIt)->fileName = fileName; + (*errIt)->offset = m.loc.offset; + (*errIt)->length = m.loc.length; + (*errIt)->startLine = m.loc.startLine; + (*errIt)->startColumn = m.loc.startColumn; + (*errIt)->type = VM::DiagnosticMessage::Error; + (*errIt)->message = m.message; + errIt = &(*errIt)->next; + } else { + std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn + << ": warning: " << qPrintable(m.message) << std::endl; + } + } + if (error) + ctx->throwSyntaxError(error); + + if (parsed) { + using namespace AST; + Program *program = AST::cast(parser.rootNode()); + if (!program) { + // if parsing was successful, and we have no program, then + // we're done...: + return 0; + } + + QStringList inheritedLocals; + if (inheritContext) + for (String * const *i = ctx->variables(), * const *ei = i + ctx->variableCount(); i < ei; ++i) + inheritedLocals.append(*i ? (*i)->toQString() : QString()); + + Codegen cg(ctx, strictMode); + IR::Function *globalIRCode = cg(fileName, program, &module, mode, inheritedLocals); + QScopedPointer isel(ctx->engine->iselFactory->create(vm, &module)); + if (globalIRCode) + globalCode = isel->vmFunction(globalIRCode); + } + + if (! globalCode) + // ### should be a syntax error + __qmljs_throw_type_error(ctx); + } + + return globalCode; +} + +static inline int toInt(const QChar &qc, int R) +{ + ushort c = qc.unicode(); + int v = -1; + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'A' && c <= 'Z') + v = c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + v = c - 'a' + 10; + if (v >= 0 && v < R) + return v; + else + return -1; +} + +// parseInt [15.1.2.2] +Value GlobalFunctions::method_parseInt(ExecutionContext *context) +{ + Value string = context->argument(0); + Value radix = context->argument(1); + int R = radix.isUndefined() ? 0 : radix.toInt32(context); + + // [15.1.2.2] step by step: + String *inputString = string.toString(context); // 1 + QString trimmed = inputString->toQString().trimmed(); // 2 + const QChar *pos = trimmed.constData(); + const QChar *end = pos + trimmed.length(); + + int sign = 1; // 3 + if (pos != end) { + if (*pos == QLatin1Char('-')) + sign = -1; // 4 + if (*pos == QLatin1Char('-') || *pos == QLatin1Char('+')) + ++pos; // 5 + } + bool stripPrefix = true; // 7 + if (R) { // 8 + if (R < 2 || R > 36) + return Value::fromDouble(nan("")); // 8a + if (R != 16) + stripPrefix = false; // 8b + } else { // 9 + R = 10; // 9a + } + if (stripPrefix) { // 10 + if ((end - pos >= 2) + && (pos[0] == QLatin1Char('0')) + && (pos[1] == QLatin1Char('x') || pos[1] == QLatin1Char('X'))) { // 10a + pos += 2; + R = 16; + } + } + // 11: Z is progressively built below + // 13: this is handled by the toInt function + if (pos == end) // 12 + return Value::fromDouble(nan("")); + bool overflow = false; + qint64 v_overflow; + unsigned overflow_digit_count = 0; + int d = toInt(*pos++, R); + if (d == -1) + return Value::fromDouble(nan("")); + qint64 v = d; + while (pos != end) { + d = toInt(*pos++, R); + if (d == -1) + break; + if (overflow) { + if (overflow_digit_count == 0) { + v_overflow = v; + v = 0; + } + ++overflow_digit_count; + v = v * R + d; + } else { + qint64 vNew = v * R + d; + if (vNew < v) { + overflow = true; + --pos; + } else { + v = vNew; + } + } + } + + if (overflow) { + double result = (double) v_overflow * pow(R, overflow_digit_count); + result += v; + return Value::fromDouble(sign * result); + } else { + return Value::fromDouble(sign * (double) v); // 15 + } +} + +// parseFloat [15.1.2.3] +Value GlobalFunctions::method_parseFloat(ExecutionContext *context) +{ + Value string = context->argument(0); + + // [15.1.2.3] step by step: + String *inputString = string.toString(context); // 1 + QString trimmed = inputString->toQString().trimmed(); // 2 + + // 4: + if (trimmed.startsWith(QLatin1String("Infinity")) + || trimmed.startsWith(QLatin1String("+Infinity"))) + return Value::fromDouble(INFINITY); + if (trimmed.startsWith("-Infinity")) + return Value::fromDouble(-INFINITY); + QByteArray ba = trimmed.toLatin1(); + bool ok; + const char *begin = ba.constData(); + const char *end = 0; + double d = qstrtod(begin, &end, &ok); + if (end - begin == 0) + return Value::fromDouble(nan("")); // 3 + else + return Value::fromDouble(d); +} + +/// isNaN [15.1.2.4] +Value GlobalFunctions::method_isNaN(ExecutionContext *context) +{ + const Value &v = context->argument(0); + if (v.integerCompatible()) + return Value::fromBoolean(false); + + double d = v.toNumber(context); + return Value::fromBoolean(std::isnan(d)); +} + +/// isFinite [15.1.2.5] +Value GlobalFunctions::method_isFinite(ExecutionContext *context) +{ + const Value &v = context->argument(0); + if (v.integerCompatible()) + return Value::fromBoolean(true); + + double d = v.toNumber(context); + return Value::fromBoolean(std::isfinite(d)); +} + +/// decodeURI [15.1.3.1] +Value GlobalFunctions::method_decodeURI(ExecutionContext *parentCtx, Value, Value *argv, int argc) +{ + if (argc == 0) + return Value::undefinedValue(); + + QString uriString = argv[0].toString(parentCtx)->toQString(); + bool ok; + QString out = decode(uriString, QString::fromUtf8(uriReserved) + QString::fromUtf8("#"), &ok); + if (!ok) + parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(parentCtx, out); +} + +/// decodeURIComponent [15.1.3.2] +Value GlobalFunctions::method_decodeURIComponent(ExecutionContext *parentCtx, Value, Value *argv, int argc) +{ + if (argc == 0) + return Value::undefinedValue(); + + QString uriString = argv[0].toString(parentCtx)->toQString(); + bool ok; + QString out = decode(uriString, QString(), &ok); + if (!ok) + parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(parentCtx, out); +} + +/// encodeURI [15.1.3.3] +Value GlobalFunctions::method_encodeURI(ExecutionContext *parentCtx, Value, Value *argv, int argc) +{ + if (argc == 0) + return Value::undefinedValue(); + + QString uriString = argv[0].toString(parentCtx)->toQString(); + bool ok; + QString out = encode(uriString, QLatin1String(uriReserved) + QLatin1String(uriUnescaped) + QString::fromUtf8("#"), &ok); + if (!ok) + parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(parentCtx, out); +} + +/// encodeURIComponent [15.1.3.4] +Value GlobalFunctions::method_encodeURIComponent(ExecutionContext *parentCtx, Value, Value *argv, int argc) +{ + if (argc == 0) + return Value::undefinedValue(); + + QString uriString = argv[0].toString(parentCtx)->toQString(); + bool ok; + QString out = encode(uriString, QString(uriUnescaped), &ok); + if (!ok) + parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(parentCtx, out); +} + +Value GlobalFunctions::method_escape(ExecutionContext *context) +{ + if (!context->argumentCount) + return Value::fromString(context, QStringLiteral("undefined")); + + QString str = context->argument(0).toString(context)->toQString(); + return Value::fromString(context, escape(str)); +} + +Value GlobalFunctions::method_unescape(ExecutionContext *context) +{ + if (!context->argumentCount) + return Value::fromString(context, QStringLiteral("undefined")); + + QString str = context->argument(0).toString(context)->toQString(); + return Value::fromString(context, unescape(str)); +} diff --git a/src/v4/qv4globalobject.h b/src/v4/qv4globalobject.h new file mode 100644 index 0000000000..6a586c1802 --- /dev/null +++ b/src/v4/qv4globalobject.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4GLOBALOBJECT_H +#define QV4GLOBALOBJECT_H + +#include "qv4global.h" +#include "qv4functionobject.h" + +namespace QQmlJS { + +namespace VM { + +struct Q_V4_EXPORT EvalFunction : FunctionObject +{ + EvalFunction(ExecutionContext *scope); + + static QQmlJS::VM::Function *parseSource(QQmlJS::VM::ExecutionContext *ctx, + const QString &fileName, + const QString &source, + QQmlJS::Codegen::Mode mode, bool strictMode, + bool inheritContext); + + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); + Value evalCall(ExecutionContext *context, Value thisObject, Value *args, int argc, bool directCall); + + Value construct(ExecutionContext *ctx); +}; + +struct GlobalFunctions +{ + static Value method_parseInt(ExecutionContext *context); + static Value method_parseFloat(ExecutionContext *context); + static Value method_isNaN(ExecutionContext *context); + static Value method_isFinite(ExecutionContext *context); + static Value method_decodeURI(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_decodeURIComponent(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_encodeURI(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_encodeURIComponent(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_escape(ExecutionContext *context); + static Value method_unescape(ExecutionContext *context); +}; + +} // namespace VM +} // namespace QQmlJS + +#endif // QMLJS_OBJECTS_H diff --git a/src/v4/qv4ir.cpp b/src/v4/qv4ir.cpp new file mode 100644 index 0000000000..1c2a9d9e62 --- /dev/null +++ b/src/v4/qv4ir.cpp @@ -0,0 +1,670 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4ir_p.h" +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace IR { + +const char *typeName(Type t) +{ + switch (t) { + case UndefinedType: return "undefined"; + case NullType: return "null"; + case BoolType: return "bool"; + case NumberType: return "number"; + default: return "invalid"; + } +} + +const char *opname(AluOp op) +{ + switch (op) { + case OpInvalid: return "?"; + + case OpIfTrue: return "(bool)"; + case OpNot: return "!"; + case OpUMinus: return "-"; + case OpUPlus: return "+"; + case OpCompl: return "~"; + case OpIncrement: return "++"; + case OpDecrement: return "--"; + + case OpBitAnd: return "&"; + case OpBitOr: return "|"; + case OpBitXor: return "^"; + + case OpAdd: return "+"; + case OpSub: return "-"; + case OpMul: return "*"; + case OpDiv: return "/"; + case OpMod: return "%"; + + case OpLShift: return "<<"; + case OpRShift: return ">>"; + case OpURShift: return ">>>"; + + case OpGt: return ">"; + case OpLt: return "<"; + case OpGe: return ">="; + case OpLe: return "<="; + case OpEqual: return "=="; + case OpNotEqual: return "!="; + case OpStrictEqual: return "==="; + case OpStrictNotEqual: return "!=="; + + case OpInstanceof: return "instanceof"; + case OpIn: return "in"; + + case OpAnd: return "&&"; + case OpOr: return "||"; + + default: return "?"; + + } // switch +} + +AluOp binaryOperator(int op) +{ + switch (static_cast(op)) { + case QSOperator::Add: return OpAdd; + case QSOperator::And: return OpAnd; + case QSOperator::BitAnd: return OpBitAnd; + case QSOperator::BitOr: return OpBitOr; + case QSOperator::BitXor: return OpBitXor; + case QSOperator::Div: return OpDiv; + case QSOperator::Equal: return OpEqual; + case QSOperator::Ge: return OpGe; + case QSOperator::Gt: return OpGt; + case QSOperator::Le: return OpLe; + case QSOperator::LShift: return OpLShift; + case QSOperator::Lt: return OpLt; + case QSOperator::Mod: return OpMod; + case QSOperator::Mul: return OpMul; + case QSOperator::NotEqual: return OpNotEqual; + case QSOperator::Or: return OpOr; + case QSOperator::RShift: return OpRShift; + case QSOperator::StrictEqual: return OpStrictEqual; + case QSOperator::StrictNotEqual: return OpStrictNotEqual; + case QSOperator::Sub: return OpSub; + case QSOperator::URShift: return OpURShift; + case QSOperator::InstanceOf: return OpInstanceof; + case QSOperator::In: return OpIn; + default: return OpInvalid; + } +} + +void Const::dump(QTextStream &out) +{ + switch (type) { + case QQmlJS::IR::UndefinedType: + out << "undefined"; + break; + case QQmlJS::IR::NullType: + out << "null"; + break; + case QQmlJS::IR::BoolType: + out << (value ? "true" : "false"); + break; + default: + out << QString::number(value, 'g', 16); + break; + } +} + +void String::dump(QTextStream &out) +{ + out << '"' << escape(*value) << '"'; +} + +QString String::escape(const QString &s) +{ + QString r; + for (int i = 0; i < s.length(); ++i) { + const QChar ch = s.at(i); + if (ch == QLatin1Char('\n')) + r += QStringLiteral("\\n"); + else if (ch == QLatin1Char('\r')) + r += QStringLiteral("\\r"); + else if (ch == QLatin1Char('\\')) + r += QStringLiteral("\\\\"); + else if (ch == QLatin1Char('"')) + r += QStringLiteral("\\\""); + else if (ch == QLatin1Char('\'')) + r += QStringLiteral("\\'"); + else + r += ch; + } + return r; +} + +void RegExp::dump(QTextStream &out) +{ + char f[3]; + int i = 0; + if (flags & RegExp_Global) + f[i++] = 'g'; + if (flags & RegExp_IgnoreCase) + f[i++] = 'i'; + if (flags & RegExp_Multiline) + f[i++] = 'm'; + f[i] = 0; + + out << '/' << *value << '/' << f; +} + +void Name::init(const QString *id, quint32 line, quint32 column) +{ + this->id = id; + this->builtin = builtin_invalid; + this->line = line; + this->column = column; +} + +void Name::init(Builtin builtin, quint32 line, quint32 column) +{ + this->id = 0; + this->builtin = builtin; + this->line = line; + this->column = column; +} + +static const char *builtin_to_string(Name::Builtin b) +{ + switch (b) { + case Name::builtin_invalid: + return "builtin_invalid"; + case Name::builtin_typeof: + return "builtin_typeof"; + case Name::builtin_delete: + return "builtin_delete"; + case Name::builtin_postincrement: + return "builtin_postincrement"; + case Name::builtin_postdecrement: + return "builtin_postdecrement"; + case Name::builtin_throw: + return "builtin_throw"; + case Name::builtin_create_exception_handler: + return "builtin_create_exception_handler"; + case Name::builtin_delete_exception_handler: + return "builtin_delete_exception_handler"; + case Name::builtin_get_exception: + return "builtin_get_exception"; + case IR::Name::builtin_foreach_iterator_object: + return "builtin_foreach_iterator_object"; + case IR::Name::builtin_foreach_next_property_name: + return "builtin_foreach_next_property_name"; + case IR::Name::builtin_push_with_scope: + return "builtin_push_with_scope"; + case IR::Name::builtin_pop_scope: + return "builtin_pop_scope"; + case IR::Name::builtin_declare_vars: + return "builtin_declare_vars"; + case IR::Name::builtin_define_property: + return "builtin_define_property"; + case IR::Name::builtin_define_array_property: + return "builtin_define_array_property"; + case IR::Name::builtin_define_getter_setter: + return "builtin_define_getter_setter"; + } + return "builtin_(###FIXME)"; +}; + + +void Name::dump(QTextStream &out) +{ + if (id) + out << *id; + else + out << builtin_to_string(builtin); +} + +void Temp::dump(QTextStream &out) +{ + if (index < 0) { + out << '#' << -(index + 1); // negative and 1-based. + } else { + out << '%' << index; // temp + } +} + +void Closure::dump(QTextStream &out) +{ + QString name = value->name ? *value->name : QString(); + if (name.isEmpty()) + name.sprintf("%p", value); + out << "closure(" << name << ')'; +} + +void Unop::dump(QTextStream &out) +{ + out << opname(op); + expr->dump(out); +} + +void Binop::dump(QTextStream &out) +{ + left->dump(out); + out << ' ' << opname(op) << ' '; + right->dump(out); +} + +void Call::dump(QTextStream &out) +{ + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +void New::dump(QTextStream &out) +{ + out << "new "; + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +void Subscript::dump(QTextStream &out) +{ + base->dump(out); + out << '['; + index->dump(out); + out << ']'; +} + +void Member::dump(QTextStream &out) +{ + base->dump(out); + out << '.' << *name; +} + +void Exp::dump(QTextStream &out, Mode) +{ + out << "(void) "; + expr->dump(out); + out << ';'; +} + +void Enter::dump(QTextStream &out, Mode) +{ + out << "%enter("; + expr->dump(out); + out << ");"; +} + +void Leave::dump(QTextStream &out, Mode) +{ + out << "%leave"; + out << ';'; +} + +void Move::dump(QTextStream &out, Mode) +{ + target->dump(out); + out << ' '; + if (op != OpInvalid) + out << opname(op); + out << "= "; +// if (source->type != target->type) +// out << typeName(source->type) << "_to_" << typeName(target->type) << '('; + source->dump(out); +// if (source->type != target->type) +// out << ')'; + out << ';'; +} + +void Jump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "goto " << 'L' << target->index << ';'; +} + +void CJump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "if ("; + cond->dump(out); + if (mode == HIR) + out << ") goto " << 'L' << iftrue->index << "; else goto " << 'L' << iffalse->index << ';'; + else + out << ") goto " << 'L' << iftrue->index << ";"; +} + +void Ret::dump(QTextStream &out, Mode) +{ + out << "return"; + if (expr) { + out << ' '; + expr->dump(out); + } + out << ';'; +} + +Function *Module::newFunction(const QString &name, Function *outer) +{ + Function *f = new Function(this, name); + functions.append(f); + if (!outer) { + assert(!rootFunction); + rootFunction = f; + } else { + outer->nestedFunctions.append(f); + } + return f; +} + +Module::~Module() +{ + foreach (Function *f, functions) { + delete f; + } +} + +Function::~Function() +{ + // destroy the Stmt::Data blocks manually, because memory pool cleanup won't + // call the Stmt destructors. + foreach (IR::BasicBlock *b, basicBlocks) + foreach (IR::Stmt *s, b->statements) + s->destroyData(); + + qDeleteAll(basicBlocks); + pool = 0; + module = 0; +} + + +const QString *Function::newString(const QString &text) +{ + return &*strings.insert(text); +} + +BasicBlock *Function::newBasicBlock(BasicBlockInsertMode mode) +{ + BasicBlock *block = new BasicBlock(this); + return mode == InsertBlock ? insertBasicBlock(block) : block; +} + +void Function::dump(QTextStream &out, Stmt::Mode mode) +{ + QString n = name ? *name : QString(); + if (n.isEmpty()) + n.sprintf("%p", this); + out << "function " << n << "() {" << endl; + foreach (const QString *formal, formals) + out << "\treceive " << *formal << ';' << endl; + foreach (const QString *local, locals) + out << "\tlocal " << *local << ';' << endl; + foreach (BasicBlock *bb, basicBlocks) + bb->dump(out, mode); + out << '}' << endl; +} + +unsigned BasicBlock::newTemp() +{ + return function->tempCount++; +} + +Temp *BasicBlock::TEMP(int index) +{ + Temp *e = function->New(); + e->init(index); + return e; +} + +Expr *BasicBlock::CONST(Type type, double value) +{ + Const *e = function->New(); + e->init(type, value); + return e; +} + +Expr *BasicBlock::STRING(const QString *value) +{ + String *e = function->New(); + e->init(value); + return e; +} + +Expr *BasicBlock::REGEXP(const QString *value, int flags) +{ + RegExp *e = function->New(); + e->init(value, flags); + return e; +} + +Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) +{ + Name *e = function->New(); + e->init(function->newString(id), line, column); + return e; +} + +Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column) +{ + Name *e = function->New(); + e->init(builtin, line, column); + return e; +} + +Closure *BasicBlock::CLOSURE(Function *function) +{ + Closure *clos = function->New(); + clos->init(function); + return clos; +} + +Expr *BasicBlock::UNOP(AluOp op, Temp *expr) +{ + Unop *e = function->New(); + e->init(op, expr); + return e; +} + +Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) +{ + Binop *e = function->New(); + e->init(op, left, right); + return e; +} + +Expr *BasicBlock::CALL(Expr *base, ExprList *args) +{ + Call *e = function->New(); + e->init(base, args); + return e; +} + +Expr *BasicBlock::NEW(Expr *base, ExprList *args) +{ + New *e = function->New(); + e->init(base, args); + return e; +} + +Expr *BasicBlock::SUBSCRIPT(Temp *base, Temp *index) +{ + Subscript *e = function->New(); + e->init(base, index); + return e; +} + +Expr *BasicBlock::MEMBER(Temp *base, const QString *name) +{ + Member*e = function->New(); + e->init(base, name); + return e; +} + +Stmt *BasicBlock::EXP(Expr *expr) +{ + if (isTerminated()) + return 0; + + Exp *s = function->New(); + s->init(expr); + statements.append(s); + return s; +} + +Stmt *BasicBlock::ENTER(Expr *expr) +{ + if (isTerminated()) + return 0; + + Enter *s = function->New(); + s->init(expr); + statements.append(s); + return s; +} + +Stmt *BasicBlock::LEAVE() +{ + if (isTerminated()) + return 0; + + Leave *s = function->New(); + s->init(); + statements.append(s); + return s; +} + +Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op) +{ + if (isTerminated()) + return 0; + + Move *s = function->New(); + s->init(target, source, op); + statements.append(s); + return s; +} + +Stmt *BasicBlock::JUMP(BasicBlock *target) +{ + if (isTerminated()) + return 0; + + Jump *s = function->New(); + s->init(target); + statements.append(s); + + assert(! out.contains(target)); + out.append(target); + + assert(! target->in.contains(this)); + target->in.append(this); + + return s; +} + +Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) +{ + if (isTerminated()) + return 0; + + if (iftrue == iffalse) { + MOVE(TEMP(newTemp()), cond); + return JUMP(iftrue); + } + + CJump *s = function->New(); + s->init(cond, iftrue, iffalse); + statements.append(s); + + assert(! out.contains(iftrue)); + out.append(iftrue); + + assert(! iftrue->in.contains(this)); + iftrue->in.append(this); + + assert(! out.contains(iffalse)); + out.append(iffalse); + + assert(! iffalse->in.contains(this)); + iffalse->in.append(this); + + return s; +} + +Stmt *BasicBlock::RET(Temp *expr) +{ + if (isTerminated()) + return 0; + + Ret *s = function->New(); + s->init(expr); + statements.append(s); + return s; +} + +void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) +{ + out << 'L' << index << ':' << endl; + foreach (Stmt *s, statements) { + out << '\t'; + s->dump(out, mode); + out << endl; + } +} + +} // end of namespace IR +} // end of namespace QQmlJS + +QT_END_NAMESPACE diff --git a/src/v4/qv4ir_p.h b/src/v4/qv4ir_p.h new file mode 100644 index 0000000000..4e60fa96dd --- /dev/null +++ b/src/v4/qv4ir_p.h @@ -0,0 +1,724 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4IR_P_H +#define QV4IR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4global.h" +#include + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QTextStream; +class QQmlType; + +namespace QQmlJS { + +namespace VM { +struct ExecutionContext; +struct Value; +} + +namespace IR { + +struct BasicBlock; +struct Function; +struct Module; + +struct Stmt; +struct Expr; + +// expressions +struct Const; +struct String; +struct RegExp; +struct Name; +struct Temp; +struct Closure; +struct Unop; +struct Binop; +struct Call; +struct New; +struct Subscript; +struct Member; + +// statements +struct Exp; +struct Enter; +struct Leave; +struct Move; +struct Jump; +struct CJump; +struct Ret; + +enum AluOp { + OpInvalid = 0, + + OpIfTrue, + OpNot, + OpUMinus, + OpUPlus, + OpCompl, + OpIncrement, + OpDecrement, + + OpBitAnd, + OpBitOr, + OpBitXor, + + OpAdd, + OpSub, + OpMul, + OpDiv, + OpMod, + + OpLShift, + OpRShift, + OpURShift, + + OpGt, + OpLt, + OpGe, + OpLe, + OpEqual, + OpNotEqual, + OpStrictEqual, + OpStrictNotEqual, + + OpInstanceof, + OpIn, + + OpAnd, + OpOr, + + LastAluOp = OpOr +}; +AluOp binaryOperator(int op); +const char *opname(IR::AluOp op); + +enum Type { + UndefinedType, + NullType, + BoolType, + NumberType +}; + +struct ExprVisitor { + virtual ~ExprVisitor() {} + virtual void visitConst(Const *) = 0; + virtual void visitString(String *) = 0; + virtual void visitRegExp(RegExp *) = 0; + virtual void visitName(Name *) = 0; + virtual void visitTemp(Temp *) = 0; + virtual void visitClosure(Closure *) = 0; + virtual void visitUnop(Unop *) = 0; + virtual void visitBinop(Binop *) = 0; + virtual void visitCall(Call *) = 0; + virtual void visitNew(New *) = 0; + virtual void visitSubscript(Subscript *) = 0; + virtual void visitMember(Member *) = 0; +}; + +struct StmtVisitor { + virtual ~StmtVisitor() {} + virtual void visitExp(Exp *) = 0; + virtual void visitEnter(Enter *) = 0; + virtual void visitLeave(Leave *) = 0; + virtual void visitMove(Move *) = 0; + virtual void visitJump(Jump *) = 0; + virtual void visitCJump(CJump *) = 0; + virtual void visitRet(Ret *) = 0; +}; + +struct Expr { + virtual ~Expr() {} + virtual void accept(ExprVisitor *) = 0; + virtual bool isLValue() { return false; } + virtual Const *asConst() { return 0; } + virtual String *asString() { return 0; } + virtual RegExp *asRegExp() { return 0; } + virtual Name *asName() { return 0; } + virtual Temp *asTemp() { return 0; } + virtual Closure *asClosure() { return 0; } + virtual Unop *asUnop() { return 0; } + virtual Binop *asBinop() { return 0; } + virtual Call *asCall() { return 0; } + virtual New *asNew() { return 0; } + virtual Subscript *asSubscript() { return 0; } + virtual Member *asMember() { return 0; } + virtual void dump(QTextStream &out) = 0; +}; + +struct ExprList { + Expr *expr; + ExprList *next; + + void init(Expr *expr, ExprList *next = 0) + { + this->expr = expr; + this->next = next; + } +}; + +struct Const: Expr { + Type type; + double value; + + void init(Type type, double value) + { + this->type = type; + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitConst(this); } + virtual Const *asConst() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct String: Expr { + const QString *value; + + void init(const QString *value) + { + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitString(this); } + virtual String *asString() { return this; } + + virtual void dump(QTextStream &out); + static QString escape(const QString &s); +}; + +struct RegExp: Expr { + // needs to be compatible with the flags in the lexer + enum Flags { + RegExp_Global = 0x01, + RegExp_IgnoreCase = 0x02, + RegExp_Multiline = 0x04 + }; + + const QString *value; + int flags; + + void init(const QString *value, int flags) + { + this->value = value; + this->flags = flags; + } + + virtual void accept(ExprVisitor *v) { v->visitRegExp(this); } + virtual RegExp *asRegExp() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Name: Expr { + enum Builtin { + builtin_invalid, + builtin_typeof, + builtin_delete, + builtin_postincrement, + builtin_postdecrement, + builtin_throw, + builtin_create_exception_handler, + builtin_delete_exception_handler, + builtin_get_exception, + builtin_foreach_iterator_object, + builtin_foreach_next_property_name, + builtin_push_with_scope, + builtin_pop_scope, + builtin_declare_vars, + builtin_define_property, + builtin_define_array_property, + builtin_define_getter_setter + }; + + const QString *id; + Builtin builtin; + quint32 line; + quint32 column; + + void init(const QString *id, quint32 line, quint32 column); + void init(Builtin builtin, quint32 line, quint32 column); + + virtual void accept(ExprVisitor *v) { v->visitName(this); } + virtual bool isLValue() { return true; } + virtual Name *asName() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Temp: Expr { + int index; + + void init(int index) + { + this->index = index; + } + + virtual void accept(ExprVisitor *v) { v->visitTemp(this); } + virtual bool isLValue() { return true; } + virtual Temp *asTemp() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Closure: Expr { + Function *value; + + void init(Function *value) + { + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitClosure(this); } + virtual Closure *asClosure() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Unop: Expr { + AluOp op; + Temp *expr; + + void init(AluOp op, Temp *expr) + { + this->op = op; + this->expr = expr; + } + + virtual void accept(ExprVisitor *v) { v->visitUnop(this); } + virtual Unop *asUnop() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Binop: Expr { + AluOp op; + Expr *left; // Temp or Const + Expr *right; // Temp or Const + + void init(AluOp op, Expr *left, Expr *right) + { + this->op = op; + this->left = left; + this->right = right; + } + + virtual void accept(ExprVisitor *v) { v->visitBinop(this); } + virtual Binop *asBinop() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Call: Expr { + Expr *base; // Name, Member, Temp + ExprList *args; // List of Temps + + void init(Expr *base, ExprList *args) + { + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitCall(this); } + virtual Call *asCall() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct New: Expr { + Expr *base; // Name, Member, Temp + ExprList *args; // List of Temps + + void init(Expr *base, ExprList *args) + { + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitNew(this); } + virtual New *asNew() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Subscript: Expr { + Temp *base; + Temp *index; + + void init(Temp *base, Temp *index) + { + this->base = base; + this->index = index; + } + + virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } + virtual bool isLValue() { return true; } + virtual Subscript *asSubscript() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Member: Expr { + Temp *base; + const QString *name; + + void init(Temp *base, const QString *name) + { + this->base = base; + this->name = name; + } + + virtual void accept(ExprVisitor *v) { v->visitMember(this); } + virtual bool isLValue() { return true; } + virtual Member *asMember() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Stmt { + enum Mode { + HIR, + MIR + }; + + struct Data { + QVector uses; + QVector defs; + QBitArray liveIn; + QBitArray liveOut; + }; + + Data *d; + + Stmt(): d(0) {} + virtual ~Stmt() { Q_UNREACHABLE(); } + virtual Stmt *asTerminator() { return 0; } + + virtual void accept(StmtVisitor *) = 0; + virtual Exp *asExp() { return 0; } + virtual Move *asMove() { return 0; } + virtual Enter *asEnter() { return 0; } + virtual Leave *asLeave() { return 0; } + virtual Jump *asJump() { return 0; } + virtual CJump *asCJump() { return 0; } + virtual Ret *asRet() { return 0; } + virtual void dump(QTextStream &out, Mode mode = HIR) = 0; + + void destroyData() { + delete d; + d = 0; + } +}; + +struct Exp: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual void accept(StmtVisitor *v) { v->visitExp(this); } + virtual Exp *asExp() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Move: Stmt { + Expr *target; // LHS - Temp, Name, Member or Subscript + Expr *source; + AluOp op; + + void init(Expr *target, Expr *source, AluOp op) + { + this->target = target; + this->source = source; + this->op = op; + } + + virtual void accept(StmtVisitor *v) { v->visitMove(this); } + virtual Move *asMove() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Enter: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual void accept(StmtVisitor *v) { v->visitEnter(this); } + virtual Enter *asEnter() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Leave: Stmt { + void init() {} + + virtual void accept(StmtVisitor *v) { v->visitLeave(this); } + virtual Leave *asLeave() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Jump: Stmt { + BasicBlock *target; + + void init(BasicBlock *target) + { + this->target = target; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitJump(this); } + virtual Jump *asJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct CJump: Stmt { + Expr *cond; // Temp, Binop + BasicBlock *iftrue; + BasicBlock *iffalse; + + void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) + { + this->cond = cond; + this->iftrue = iftrue; + this->iffalse = iffalse; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitCJump(this); } + virtual CJump *asCJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Ret: Stmt { + Temp *expr; + + void init(Temp *expr) + { + this->expr = expr; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitRet(this); } + virtual Ret *asRet() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Q_V4_EXPORT Module { + MemoryPool pool; + QVector functions; + Function *rootFunction; + + Function *newFunction(const QString &name, Function *outer); + + Module() : rootFunction(0) {} + ~Module(); +}; + +struct Function { + Module *module; + MemoryPool *pool; + const QString *name; + QVector basicBlocks; + int tempCount; + int maxNumberOfArguments; + QSet strings; + QList formals; + QList locals; + QVector nestedFunctions; + + int insideWith; + + uint hasDirectEval: 1; + uint usesArgumentsObject : 1; + uint isStrict: 1; + uint unused : 29; + + template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } + + Function(Module *module, const QString &name) + : module(module) + , pool(&module->pool) + , tempCount(0) + , maxNumberOfArguments(0) + , insideWith(0) + , hasDirectEval(false) + , usesArgumentsObject(false) + , isStrict(false) + , unused(0) + { this->name = newString(name); } + + ~Function(); + + enum BasicBlockInsertMode { + InsertBlock, + DontInsertBlock + }; + + BasicBlock *newBasicBlock(BasicBlockInsertMode mode = InsertBlock); + const QString *newString(const QString &text); + + void RECEIVE(const QString &name) { formals.append(newString(name)); } + void LOCAL(const QString &name) { locals.append(newString(name)); } + + inline BasicBlock *insertBasicBlock(BasicBlock *block) { basicBlocks.append(block); return block; } + + void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); +}; + +struct BasicBlock { + Function *function; + QVector statements; + QVector in; + QVector out; + QBitArray liveIn; + QBitArray liveOut; + int index; + int offset; + + BasicBlock(Function *function): function(function), index(-1), offset(-1) {} + ~BasicBlock() {} + + template inline Instr i(Instr i) { statements.append(i); return i; } + + inline bool isEmpty() const { + return statements.isEmpty(); + } + + inline Stmt *terminator() const { + if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0) + return statements.at(statements.size() - 1); + return 0; + } + + inline bool isTerminated() const { + if (terminator() != 0) + return true; + return false; + } + + unsigned newTemp(); + + Temp *TEMP(int index); + + Expr *CONST(Type type, double value); + Expr *STRING(const QString *value); + Expr *REGEXP(const QString *value, int flags); + + Name *NAME(const QString &id, quint32 line, quint32 column); + Name *NAME(Name::Builtin builtin, quint32 line, quint32 column); + + Closure *CLOSURE(Function *function); + + Expr *UNOP(AluOp op, Temp *expr); + Expr *BINOP(AluOp op, Expr *left, Expr *right); + Expr *CALL(Expr *base, ExprList *args = 0); + Expr *NEW(Expr *base, ExprList *args = 0); + Expr *SUBSCRIPT(Temp *base, Temp *index); + Expr *MEMBER(Temp *base, const QString *name); + + Stmt *EXP(Expr *expr); + Stmt *ENTER(Expr *expr); + Stmt *LEAVE(); + + Stmt *MOVE(Expr *target, Expr *source, AluOp op = IR::OpInvalid); + + Stmt *JUMP(BasicBlock *target); + Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); + Stmt *RET(Temp *expr); + + void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); +}; + +} // end of namespace IR + +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV4IR_P_H diff --git a/src/v4/qv4isel_llvm.cpp b/src/v4/qv4isel_llvm.cpp new file mode 100644 index 0000000000..091936813d --- /dev/null +++ b/src/v4/qv4isel_llvm.cpp @@ -0,0 +1,1388 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-parameter" +#endif // __clang__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif // __clang__ + +#include +#include +#include +#include +#include +#include + +// These includes have to come last, because WTF/Platform.h defines some macros +// with very unfriendly names that collide with class fields in LLVM. +#include "qv4isel_llvm_p.h" +#include "qv4_llvm_p.h" +#include "qv4ir_p.h" +#include "qv4string.h" +#include "qv4global.h" + +namespace QQmlJS { + +Q_V4_EXPORT int compileWithLLVM(IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*exec)(void *)) +{ + Q_ASSERT(module); + Q_ASSERT(exec || outputType != LLVMOutputJit); + + // TODO: should this be done here? + LLVMInitializeX86TargetInfo(); + LLVMInitializeX86Target(); + LLVMInitializeX86AsmPrinter(); + LLVMInitializeX86AsmParser(); + LLVMInitializeX86Disassembler(); + LLVMInitializeX86TargetMC(); + + //---- + + llvm::InitializeNativeTarget(); + LLVM::InstructionSelection llvmIsel(llvm::getGlobalContext()); + + const QString moduleName = QFileInfo(fileName).fileName(); + llvm::StringRef moduleId(moduleName.toUtf8().constData()); + llvm::Module *llvmModule = new llvm::Module(moduleId, llvmIsel.getContext()); + + if (outputType == LLVMOutputJit) { + // The execution engine takes ownership of the model. No need to delete it anymore. + std::string errStr; + llvm::ExecutionEngine *execEngine = llvm::EngineBuilder(llvmModule) +// .setUseMCJIT(true) + .setErrorStr(&errStr).create(); + if (!execEngine) { + std::cerr << "Could not create LLVM JIT: " << errStr << std::endl; + return EXIT_FAILURE; + } + + llvm::FunctionPassManager functionPassManager(llvmModule); + // Set up the optimizer pipeline. Start with registering info about how the + // target lays out data structures. + functionPassManager.add(new llvm::DataLayout(*execEngine->getDataLayout())); + // Promote allocas to registers. + functionPassManager.add(llvm::createPromoteMemoryToRegisterPass()); + // Provide basic AliasAnalysis support for GVN. + functionPassManager.add(llvm::createBasicAliasAnalysisPass()); + // Do simple "peephole" optimizations and bit-twiddling optzns. + functionPassManager.add(llvm::createInstructionCombiningPass()); + // Reassociate expressions. + functionPassManager.add(llvm::createReassociatePass()); + // Eliminate Common SubExpressions. + functionPassManager.add(llvm::createGVNPass()); + // Simplify the control flow graph (deleting unreachable blocks, etc). + functionPassManager.add(llvm::createCFGSimplificationPass()); + + functionPassManager.doInitialization(); + + llvmIsel.buildLLVMModule(module, llvmModule, &functionPassManager); + + llvm::Function *entryPoint = llvmModule->getFunction("%entry"); + Q_ASSERT(entryPoint); + void *funcPtr = execEngine->getPointerToFunction(entryPoint); + return exec(funcPtr); + } else { + llvm::FunctionPassManager functionPassManager(llvmModule); + // Set up the optimizer pipeline. + // Promote allocas to registers. + functionPassManager.add(llvm::createPromoteMemoryToRegisterPass()); + // Provide basic AliasAnalysis support for GVN. + functionPassManager.add(llvm::createBasicAliasAnalysisPass()); + // Do simple "peephole" optimizations and bit-twiddling optzns. + functionPassManager.add(llvm::createInstructionCombiningPass()); + // Reassociate expressions. + functionPassManager.add(llvm::createReassociatePass()); + // Eliminate Common SubExpressions. + functionPassManager.add(llvm::createGVNPass()); + // Simplify the control flow graph (deleting unreachable blocks, etc). + functionPassManager.add(llvm::createCFGSimplificationPass()); + + functionPassManager.doInitialization(); + + llvmIsel.buildLLVMModule(module, llvmModule, &functionPassManager); + + // TODO: if output type is .ll, print the module to file + + const std::string triple = llvm::sys::getDefaultTargetTriple(); + + std::string err; + const llvm::Target *target = llvm::TargetRegistry::lookupTarget(triple, err); + if (! err.empty()) { + std::cerr << err << ", triple: " << triple << std::endl; + assert(!"cannot create target for the host triple"); + } + + std::string cpu; + std::string features; + llvm::TargetOptions options; + llvm::TargetMachine *targetMachine = target->createTargetMachine(triple, cpu, features, options, llvm::Reloc::PIC_); + assert(targetMachine); + + llvm::TargetMachine::CodeGenFileType ft; + QString ofName; + + if (outputType == LLVMOutputObject) { + ft = llvm::TargetMachine::CGFT_ObjectFile; + ofName = fileName + QLatin1String(".o"); + } else if (outputType == LLVMOutputAssembler) { + ft = llvm::TargetMachine::CGFT_AssemblyFile; + ofName = fileName + QLatin1String(".s"); + } else { + // ft is not used. + ofName = fileName + QLatin1String(".ll"); + } + + llvm::raw_fd_ostream dest(ofName.toUtf8().constData(), err, llvm::raw_fd_ostream::F_Binary); + llvm::formatted_raw_ostream destf(dest); + if (!err.empty()) { + std::cerr << err << std::endl; + delete llvmModule; + } + + llvm::PassManager globalPassManager; + globalPassManager.add(llvm::createScalarReplAggregatesPass()); + globalPassManager.add(llvm::createInstructionCombiningPass()); + globalPassManager.add(llvm::createGlobalOptimizerPass()); + globalPassManager.add(llvm::createFunctionInliningPass(25)); +// globalPassManager.add(llvm::createFunctionInliningPass(125)); + + if (outputType == LLVMOutputObject || outputType == LLVMOutputAssembler) { + if (targetMachine->addPassesToEmitFile(globalPassManager, destf, ft)) { + std::cerr << err << " (probably no DataLayout in TargetMachine)" << std::endl; + } else { + globalPassManager.run(*llvmModule); + + destf.flush(); + dest.flush(); + } + } else { // .ll + globalPassManager.run(*llvmModule); + llvmModule->print(destf, 0); + + destf.flush(); + dest.flush(); + } + + delete llvmModule; + return EXIT_SUCCESS; + } +} + +} // QQmlJS + +using namespace QQmlJS; +using namespace QQmlJS::LLVM; + +namespace { +QTextStream qerr(stderr, QIODevice::WriteOnly); +} + +InstructionSelection::InstructionSelection(llvm::LLVMContext &context) + : llvm::IRBuilder<>(context) + , _llvmModule(0) + , _llvmFunction(0) + , _llvmValue(0) + , _numberTy(0) + , _valueTy(0) + , _contextPtrTy(0) + , _stringPtrTy(0) + , _functionTy(0) + , _allocaInsertPoint(0) + , _function(0) + , _block(0) + , _fpm(0) +{ +} + +void InstructionSelection::buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm) +{ + qSwap(_llvmModule, llvmModule); + qSwap(_fpm, fpm); + + _numberTy = getDoubleTy(); + + std::string err; + + llvm::OwningPtr buffer; + qDebug()<<"llvm runtime:"<getTypeByName("struct.QQmlJS::VM::Value"); + _contextPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::ExecutionContext")->getPointerTo(); + _stringPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::String")->getPointerTo(); + + { + llvm::Type *args[] = { _contextPtrTy }; + _functionTy = llvm::FunctionType::get(getVoidTy(), llvm::makeArrayRef(args), false); + } + + + foreach (IR::Function *function, module->functions) + (void) compileLLVMFunction(function); + qSwap(_fpm, fpm); + qSwap(_llvmModule, llvmModule); +} + +void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinThrow(IR::Temp *arg) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteExceptionHandler() +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinGetException(IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPopScope() +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + llvm::ConstantInt *isDeletable = getInt1(deletable != 0); + llvm::Value *varName = getIdentifier(name); + CreateCall3(getRuntimeFunction("__qmljs_builtin_declare_var"), + _llvmFunction->arg_begin(), isDeletable, varName); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::constructActivationProperty(IR::Name *func, + IR::ExprList *args, + IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::loadThisObject(IR::Temp *temp) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::loadConst(IR::Const *con, IR::Temp *temp) +{ + llvm::Value *target = getLLVMTemp(temp); + llvm::Value *source = CreateLoad(createValue(con)); + CreateStore(source, target); +} + +void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) +{ + llvm::Value *name = getIdentifier(targetName); + llvm::Value *src = toValuePtr(source); + CreateCall3(getRuntimeFunction("__qmljs_llvm_set_activation_property"), + _llvmFunction->arg_begin(), name, src); +} + +void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) +{ + IR::Function *f = closure->value; + QString name; + if (f->name) + name = *f->name; + + llvm::Value *args[] = { + _llvmFunction->arg_begin(), + getLLVMTemp(target), + getIdentifier(name), + getInt1(f->hasDirectEval), + getInt1(f->usesArgumentsObject), + getInt1(f->isStrict), + getInt1(!f->nestedFunctions.isEmpty()), + genStringList(f->formals, "formals", "formal"), + getInt32(f->formals.size()), + genStringList(f->locals, "locals", "local"), + getInt32(f->locals.size()) + }; + llvm::Function *callee = _llvmModule->getFunction("__qmljs_llvm_init_closure"); + CreateCall(callee, args); +} + +void InstructionSelection::getProperty(IR::Temp *sourceBase, const QString &sourceName, IR::Temp *target) +{ + llvm::Value *base = getLLVMTempReference(sourceBase); + llvm::Value *name = getIdentifier(sourceName); + llvm::Value *t = getLLVMTemp(target); + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_property"), + _llvmFunction->arg_begin(), t, base, name); +} + +void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +{ + llvm::Value *base = getLLVMTempReference(targetBase); + llvm::Value *name = getIdentifier(targetName); + llvm::Value *src = toValuePtr(source); + CreateCall4(getRuntimeFunction("__qmljs_llvm_set_property"), + _llvmFunction->arg_begin(), base, name, src); +} + +void InstructionSelection::getElement(IR::Temp *sourceBase, IR::Temp *sourceIndex, IR::Temp *target) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); + + llvm::Value *base = getLLVMTempReference(sourceBase); + llvm::Value *index = getLLVMTempReference(sourceIndex); + llvm::Value *t = getLLVMTemp(target); + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_element"), + _llvmFunction->arg_begin(), t, base, index); +} + +void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) +{ + llvm::Value *base = getLLVMTempReference(targetBase); + llvm::Value *index = getLLVMTempReference(targetIndex); + llvm::Value *src = toValuePtr(source); + CreateCall4(getRuntimeFunction("__qmljs_llvm_set_element"), + _llvmFunction->arg_begin(), base, index, src); +} + +void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + llvm::Value *t = getLLVMTemp(targetTemp); + llvm::Value *s = getLLVMTemp(sourceTemp); + CreateStore(s, t); +} + +void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + const char *opName = 0; + switch (oper) { + case IR::OpNot: opName = "__qmljs_not"; break; + case IR::OpUMinus: opName = "__qmljs_uminus"; break; + case IR::OpUPlus: opName = "__qmljs_uplus"; break; + case IR::OpCompl: opName = "__qmljs_compl"; break; + case IR::OpIncrement: opName = "__qmljs_increment"; break; + case IR::OpDecrement: opName = "__qmljs_decrement"; break; + default: assert(!"unreachable"); break; + } + + if (opName) { + llvm::Value *t = getLLVMTemp(targetTemp); + llvm::Value *s = getLLVMTemp(sourceTemp); + CreateCall3(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), t, s); + } +} + +void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) +{ + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: opName = "__qmljs_llvm_bit_and"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_bit_or"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_bit_xor"; break; + case IR::OpAdd: opName = "__qmljs_llvm_add"; break; + case IR::OpSub: opName = "__qmljs_llvm_sub"; break; + case IR::OpMul: opName = "__qmljs_llvm_mul"; break; + case IR::OpDiv: opName = "__qmljs_llvm_div"; break; + case IR::OpMod: opName = "__qmljs_llvm_mod"; break; + case IR::OpLShift: opName = "__qmljs_llvm_shl"; break; + case IR::OpRShift: opName = "__qmljs_llvm_shr"; break; + case IR::OpURShift: opName = "__qmljs_llvm_ushr"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *t = getLLVMTemp(target); + llvm::Value *s1 = toValuePtr(leftSource); + llvm::Value *s2 = toValuePtr(rightSource); + CreateCall4(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), t, s1, s2); + return; + } +} + +void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) +{ + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_name"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_name"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_name"; break; + case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_name"; break; + case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_name"; break; + case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_name"; break; + case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_name"; break; + case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_name"; break; + case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_name"; break; + case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_name"; break; + case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_name"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *dst = getIdentifier(targetName); + llvm::Value *src = toValuePtr(sourceExpr); + CreateCall3(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), dst, src); + return; + } +} + +void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) +{ + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_element"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_element"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_element"; break; + case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_element"; break; + case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_element"; break; + case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_element"; break; + case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_element"; break; + case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_element"; break; + case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_element"; break; + case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_element"; break; + case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_element"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *base = getLLVMTemp(targetBaseTemp); + llvm::Value *index = getLLVMTemp(targetIndexTemp); + llvm::Value *value = toValuePtr(sourceExpr); + CreateCall4(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), base, index, value); + } +} + +void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +{ + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_member"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_member"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_member"; break; + case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_member"; break; + case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_member"; break; + case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_member"; break; + case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_member"; break; + case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_member"; break; + case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_member"; break; + case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_member"; break; + case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_member"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *base = getLLVMTemp(targetBase); + llvm::Value *member = getIdentifier(targetName); + llvm::Value *value = toValuePtr(source); + CreateCall4(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), value, base, member); + } +} + +llvm::Function *InstructionSelection::getLLVMFunction(IR::Function *function) +{ + llvm::Function *&f = _functionMap[function]; + if (! f) { + QString name = QStringLiteral("__qmljs_native_"); + if (function->name) { + if (*function->name == QStringLiteral("%entry")) + name = *function->name; + else + name += *function->name; + } + f = llvm::Function::Create(_functionTy, llvm::Function::ExternalLinkage, // ### make it internal + qPrintable(name), _llvmModule); + } + return f; +} + +llvm::Function *InstructionSelection::compileLLVMFunction(IR::Function *function) +{ + llvm::Function *llvmFunction = getLLVMFunction(function); + + QHash blockMap; + QVector tempMap; + + qSwap(_llvmFunction, llvmFunction); + qSwap(_function, function); + qSwap(_tempMap, tempMap); + qSwap(_blockMap, blockMap); + + // create the LLVM blocks + foreach (IR::BasicBlock *block, _function->basicBlocks) + (void) getLLVMBasicBlock(block); + + // entry block + SetInsertPoint(getLLVMBasicBlock(_function->basicBlocks.first())); + + llvm::Instruction *allocaInsertPoint = new llvm::BitCastInst(llvm::UndefValue::get(getInt32Ty()), + getInt32Ty(), "", GetInsertBlock()); + qSwap(_allocaInsertPoint, allocaInsertPoint); + + for (int i = 0; i < _function->tempCount; ++i) { + llvm::AllocaInst *t = newLLVMTemp(_valueTy); + _tempMap.append(t); + } + + foreach (llvm::Value *t, _tempMap) { + CreateStore(llvm::Constant::getNullValue(_valueTy), t); + } + +// CreateCall(getRuntimeFunction("__qmljs_llvm_init_this_object"), +// _llvmFunction->arg_begin()); + + foreach (IR::BasicBlock *block, _function->basicBlocks) { + qSwap(_block, block); + SetInsertPoint(getLLVMBasicBlock(_block)); + foreach (IR::Stmt *s, _block->statements) + s->accept(this); + qSwap(_block, block); + } + + qSwap(_allocaInsertPoint, allocaInsertPoint); + + allocaInsertPoint->eraseFromParent(); + + qSwap(_blockMap, blockMap); + qSwap(_tempMap, tempMap); + qSwap(_function, function); + qSwap(_llvmFunction, llvmFunction); + + // Validate the generated code, checking for consistency. + llvm::verifyFunction(*llvmFunction); + // Optimize the function. + if (_fpm) + _fpm->run(*llvmFunction); + + return llvmFunction; +} + +llvm::BasicBlock *InstructionSelection::getLLVMBasicBlock(IR::BasicBlock *block) +{ + llvm::BasicBlock *&llvmBlock = _blockMap[block]; + if (! llvmBlock) + llvmBlock = llvm::BasicBlock::Create(getContext(), llvm::Twine(), + _llvmFunction); + return llvmBlock; +} + +llvm::Value *InstructionSelection::getLLVMTempReference(IR::Expr *expr) +{ + if (IR::Temp *t = expr->asTemp()) + return getLLVMTemp(t); + + assert(!"TODO!"); + llvm::Value *addr = newLLVMTemp(_valueTy); +// CreateStore(getLLVMValue(expr), addr); + return addr; +} + +llvm::Value *InstructionSelection::getLLVMCondition(IR::Expr *expr) +{ + llvm::Value *value = 0; + if (IR::Temp *t = expr->asTemp()) { + value = getLLVMTemp(t); + } else { + assert(!"TODO!"); + Q_UNREACHABLE(); + +#if 0 + value = getLLVMValue(expr); + if (! value) { + Q_UNIMPLEMENTED(); + return getInt1(false); + } + + llvm::Value *tmp = newLLVMTemp(_valueTy); + CreateStore(value, tmp); + value = tmp; +#endif + } + + return CreateCall2(getRuntimeFunction("__qmljs_llvm_to_boolean"), + _llvmFunction->arg_begin(), + value); +} + +llvm::Value *InstructionSelection::getLLVMTemp(IR::Temp *temp) +{ + if (temp->index < 0) { + const int index = -temp->index -1; + return CreateCall2(getRuntimeFunction("__qmljs_llvm_get_argument"), + _llvmFunction->arg_begin(), getInt32(index)); + } + + return _tempMap[temp->index]; +} + +llvm::Value *InstructionSelection::getStringPtr(const QString &s) +{ + llvm::Value *&value = _stringMap[s]; + if (! value) { + const QByteArray bytes = s.toUtf8(); + value = CreateGlobalStringPtr(llvm::StringRef(bytes.constData(), bytes.size())); + _stringMap[s] = value; + } + return value; +} + +llvm::Value *InstructionSelection::getIdentifier(const QString &s) +{ + llvm::Value *str = getStringPtr(s); + llvm::Value *id = CreateCall2(getRuntimeFunction("__qmljs_identifier_from_utf8"), + _llvmFunction->arg_begin(), str); + return id; +} + +void InstructionSelection::visitJump(IR::Jump *s) +{ + CreateBr(getLLVMBasicBlock(s->target)); +} + +void InstructionSelection::visitCJump(IR::CJump *s) +{ + CreateCondBr(getLLVMCondition(s->cond), + getLLVMBasicBlock(s->iftrue), + getLLVMBasicBlock(s->iffalse)); +} + +void InstructionSelection::visitRet(IR::Ret *s) +{ + IR::Temp *t = s->expr->asTemp(); + assert(t != 0); + llvm::Value *result = getLLVMTemp(t); + llvm::Value *ctx = _llvmFunction->arg_begin(); + CreateCall2(getRuntimeFunction("__qmljs_llvm_return"), ctx, result); + CreateRetVoid(); +} + +#if 0 +void InstructionSelection::visitString(IR::String *e) +{ + llvm::Value *tmp = newLLVMTemp(_valueTy); + CreateCall3(getRuntimeFunction("__qmljs_llvm_init_string"), + _llvmFunction->arg_begin(), tmp, + getStringPtr(*e->value)); + _llvmValue = CreateLoad(tmp); +} +#endif + +llvm::AllocaInst *InstructionSelection::newLLVMTemp(llvm::Type *type, llvm::Value *size) +{ + llvm::AllocaInst *addr = new llvm::AllocaInst(type, size, llvm::Twine(), _allocaInsertPoint); + return addr; +} + +llvm::Value * InstructionSelection::genArguments(IR::ExprList *exprs, int &argc) +{ + llvm::Value *args = 0; + + argc = 0; + for (IR::ExprList *it = exprs; it; it = it->next) + ++argc; + + if (argc) + args = newLLVMTemp(_valueTy, getInt32(argc)); + else + args = llvm::Constant::getNullValue(_valueTy->getPointerTo()); + + int i = 0; + for (IR::ExprList *it = exprs; it; it = it->next) { +// llvm::Value *arg = getLLVMValue(it->expr); +// CreateStore(arg, CreateConstGEP1_32(args, i++)); + } + + return args; +} + +void InstructionSelection::genCallMember(IR::Call *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + IR::Member *m = e->base->asMember(); + llvm::Value *thisObject = getLLVMTemp(m->base->asTemp()); + llvm::Value *name = getIdentifier(*m->name); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + thisObject, + name, + args, + getInt32(argc) + }; + + CreateCall(getRuntimeFunction("__qmljs_llvm_call_property"), llvm::ArrayRef(actuals)); + _llvmValue = CreateLoad(result); +} + +void InstructionSelection::genConstructMember(IR::New *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + IR::Member *m = e->base->asMember(); + llvm::Value *thisObject = getLLVMTemp(m->base->asTemp()); + llvm::Value *name = getIdentifier(*m->name); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + thisObject, + name, + args, + getInt32(argc) + }; + + CreateCall(getRuntimeFunction("__qmljs_llvm_construct_property"), llvm::ArrayRef(actuals)); + _llvmValue = CreateLoad(result); +} + +void InstructionSelection::genCallTemp(IR::Call *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + llvm::Value *func = getLLVMTempReference(e->base); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *thisObject = llvm::Constant::getNullValue(_valueTy->getPointerTo()); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + thisObject, + func, + args, + getInt32(argc) + }; + + CreateCall(getRuntimeFunction("__qmljs_llvm_call_value"), actuals); + + _llvmValue = CreateLoad(result); +} + +void InstructionSelection::genConstructTemp(IR::New *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + llvm::Value *func = getLLVMTempReference(e->base); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + func, + args, + getInt32(argc) + }; + + CreateCall(getRuntimeFunction("__qmljs_llvm_construct_value"), actuals); + + _llvmValue = CreateLoad(result); +} + +void InstructionSelection::genCallName(IR::Call *e, llvm::Value *result) +{ + IR::Name *base = e->base->asName(); + + if (! result) + result = newLLVMTemp(_valueTy); + + if (! base->id) { + switch (base->builtin) { + case IR::Name::builtin_invalid: + break; + + case IR::Name::builtin_typeof: + CreateCall3(getRuntimeFunction("__qmljs_llvm_typeof"), + _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + + case IR::Name::builtin_throw: + CreateCall2(getRuntimeFunction("__qmljs_llvm_throw"), + _llvmFunction->arg_begin(), getLLVMTempReference(e->args->expr)); + _llvmValue = llvm::UndefValue::get(_valueTy); + return; + + case IR::Name::builtin_create_exception_handler: + CreateCall2(getRuntimeFunction("__qmljs_llvm_create_exception_handler"), + _llvmFunction->arg_begin(), result); + _llvmValue = CreateLoad(result); + return; + + case IR::Name::builtin_delete_exception_handler: + CreateCall(getRuntimeFunction("__qmljs_llvm_delete_exception_handler"), + _llvmFunction->arg_begin()); + return; + + case IR::Name::builtin_get_exception: + CreateCall2(getRuntimeFunction("__qmljs_llvm_get_exception"), + _llvmFunction->arg_begin(), result); + _llvmValue = CreateLoad(result); + return; + + case IR::Name::builtin_foreach_iterator_object: + CreateCall3(getRuntimeFunction("__qmljs_llvm_foreach_iterator_object"), + _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + + case IR::Name::builtin_foreach_next_property_name: + CreateCall2(getRuntimeFunction("__qmljs_llvm_foreach_next_property_name"), + result, getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + + case IR::Name::builtin_delete: { + if (IR::Subscript *subscript = e->args->expr->asSubscript()) { + CreateCall4(getRuntimeFunction("__qmljs_llvm_delete_subscript"), + _llvmFunction->arg_begin(), + result, + getLLVMTempReference(subscript->base), + getLLVMTempReference(subscript->index)); + _llvmValue = CreateLoad(result); + return; + } else if (IR::Member *member = e->args->expr->asMember()) { + CreateCall4(getRuntimeFunction("__qmljs_llvm_delete_member"), + _llvmFunction->arg_begin(), + result, + getLLVMTempReference(member->base), + getIdentifier(*member->name)); + _llvmValue = CreateLoad(result); + return; + } else if (IR::Name *name = e->args->expr->asName()) { + CreateCall3(getRuntimeFunction("__qmljs_llvm_delete_property"), + _llvmFunction->arg_begin(), + result, + getIdentifier(*name->id)); + _llvmValue = CreateLoad(result); + return; + } else { + CreateCall3(getRuntimeFunction("__qmljs_llvm_delete_value"), + _llvmFunction->arg_begin(), + result, + getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + } + } break; + + default: + Q_UNREACHABLE(); + } + } else { + llvm::Value *name = getIdentifier(*base->id); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + CreateCall5(getRuntimeFunction("__qmljs_llvm_call_activation_property"), + _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); + + _llvmValue = CreateLoad(result); + } +} + +void InstructionSelection::genConstructName(IR::New *e, llvm::Value *result) +{ + IR::Name *base = e->base->asName(); + + if (! result) + result = newLLVMTemp(_valueTy); + + if (! base->id) { + Q_UNREACHABLE(); + } else { + llvm::Value *name = getIdentifier(*base->id); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + CreateCall5(getRuntimeFunction("__qmljs_llvm_construct_activation_property"), + _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); + + _llvmValue = CreateLoad(result); + } +} + +#if 0 +void InstructionSelection::visitCall(IR::Call *e) +{ + if (e->base->asMember()) { + genCallMember(e); + } else if (e->base->asTemp()) { + genCallTemp(e); + } else if (e->base->asName()) { + genCallName(e); + } else if (IR::Temp *t = e->base->asTemp()) { + llvm::Value *base = getLLVMTemp(t); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *result = newLLVMTemp(_valueTy); + CreateStore(llvm::Constant::getNullValue(_valueTy), result); + CreateCall5(getRuntimeFunction("__qmljs_llvm_call_value"), + _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); + _llvmValue = CreateLoad(result); + } else { + Q_UNIMPLEMENTED(); + } +} +#endif + +#if 0 +void InstructionSelection::visitNew(IR::New *e) +{ + if (e->base->asMember()) { + genConstructMember(e); + } else if (e->base->asTemp()) { + genConstructTemp(e); + } else if (e->base->asName()) { + genConstructName(e); + } else if (IR::Temp *t = e->base->asTemp()) { + llvm::Value *base = getLLVMTemp(t); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *result = newLLVMTemp(_valueTy); + CreateStore(llvm::Constant::getNullValue(_valueTy), result); + CreateCall5(getRuntimeFunction("__qmljs_llvm_construct_value"), + _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); + _llvmValue = CreateLoad(result); + } else { + Q_UNIMPLEMENTED(); + } +} +#endif + +#if 0 +void InstructionSelection::visitSubscript(IR::Subscript *e) +{ + llvm::Value *result = newLLVMTemp(_valueTy); + llvm::Value *base = getLLVMTempReference(e->base); + llvm::Value *index = getLLVMTempReference(e->index); + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_element"), + _llvmFunction->arg_begin(), result, base, index); + _llvmValue = CreateLoad(result); +} +#endif + +#if 0 +void InstructionSelection::visitMember(IR::Member *e) +{ + llvm::Value *result = newLLVMTemp(_valueTy); + llvm::Value *base = getLLVMTempReference(e->base); + llvm::Value *name = getIdentifier(*e->name); + + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_property"), + _llvmFunction->arg_begin(), result, base, name); + _llvmValue = CreateLoad(result); +} +#endif + +llvm::Function *InstructionSelection::getRuntimeFunction(llvm::StringRef str) +{ + llvm::Function *func = _llvmModule->getFunction(str); + if (!func) { + std::cerr << "Cannot find runtime function \"" + << str.str() << "\"!" << std::endl; + assert(func); + } + return func; +} + +llvm::Value *InstructionSelection::createValue(IR::Const *e) +{ + llvm::Value *tmp = newLLVMTemp(_valueTy); + + switch (e->type) { + case IR::UndefinedType: + CreateCall(getRuntimeFunction("__qmljs_llvm_init_undefined"), tmp); + break; + + case IR::NullType: + CreateCall(getRuntimeFunction("__qmljs_llvm_init_null"), tmp); + break; + + case IR::BoolType: + CreateCall2(getRuntimeFunction("__qmljs_llvm_init_boolean"), tmp, + getInt1(e->value ? 1 : 0)); + break; + + case IR::NumberType: + CreateCall2(getRuntimeFunction("__qmljs_llvm_init_number"), tmp, + llvm::ConstantFP::get(_numberTy, e->value)); + break; + + default: + Q_UNREACHABLE(); + } + + return tmp; +} + +llvm::Value *InstructionSelection::toValuePtr(IR::Expr *e) +{ + if (IR::Temp *t = e->asTemp()) { + return getLLVMTemp(t); + } else if (IR::Const *c = e->asConst()) { + return createValue(c); + } else { + Q_UNREACHABLE(); + } +} + +llvm::Value *InstructionSelection::genStringList(const QList &strings, const char *arrayName, const char *elementName) +{ + llvm::Value *array = CreateAlloca(_stringPtrTy, getInt32(strings.size()), + arrayName); + for (int i = 0, ei = strings.size(); i < ei; ++i) { + llvm::Value *el; + if (const QString *string = strings.at(i)) + el = getIdentifier(*string); + else + el = llvm::Constant::getNullValue(_stringPtrTy); + llvm::Value *ptr = CreateGEP(array, getInt32(i), elementName); + CreateStore(el, ptr); + } + + return array; +} diff --git a/src/v4/qv4isel_llvm_p.h b/src/v4/qv4isel_llvm_p.h new file mode 100644 index 0000000000..46fd2fefa4 --- /dev/null +++ b/src/v4/qv4isel_llvm_p.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ISEL_LLVM_P_H +#define QV4ISEL_LLVM_P_H + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-parameter" +#endif // __clang__ + +#include +#include +#include + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif // __clang__ + +#include "qv4isel_p.h" +#include "qv4ir_p.h" + +namespace QQmlJS { +namespace LLVM { + +class InstructionSelection: + public llvm::IRBuilder<>, + public IR::InstructionSelection +{ +public: + InstructionSelection(llvm::LLVMContext &context); + + void buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm); + +public: // methods from InstructionSelection: + virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result); + virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteValue(IR::Temp *result); + virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinThrow(IR::Temp *arg); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); + virtual void callBuiltinDeleteExceptionHandler(); + virtual void callBuiltinGetException(IR::Temp *result); + virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinPushWithScope(IR::Temp *arg); + virtual void callBuiltinPopScope(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); + virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); + virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); + virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); + virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result); + virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); + virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); + virtual void loadThisObject(IR::Temp *temp); + virtual void loadConst(IR::Const *con, IR::Temp *temp); + virtual void loadString(const QString &str, IR::Temp *targetTemp); + virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); + virtual void getActivationProperty(const QString &name, IR::Temp *temp); + virtual void setActivationProperty(IR::Expr *source, const QString &targetName); + virtual void initClosure(IR::Closure *closure, IR::Temp *target); + virtual void getProperty(IR::Temp *sourceBase, const QString &sourceName, IR::Temp *target); + virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + virtual void getElement(IR::Temp *sourceBase, IR::Temp *sourceIndex, IR::Temp *target); + virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); + virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); + virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); + virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + +public: // visitor methods for StmtVisitor: + virtual void visitJump(IR::Jump *); + virtual void visitCJump(IR::CJump *); + virtual void visitRet(IR::Ret *); + +private: + llvm::Function *getRuntimeFunction(llvm::StringRef str); + llvm::Function *getLLVMFunction(IR::Function *function); + llvm::Function *compileLLVMFunction(IR::Function *function); + llvm::BasicBlock *getLLVMBasicBlock(IR::BasicBlock *block); + llvm::Value *getLLVMTempReference(IR::Expr *expr); + llvm::Value *getLLVMCondition(IR::Expr *expr); + llvm::Value *getLLVMTemp(IR::Temp *temp); + llvm::Value *getStringPtr(const QString &s); + llvm::Value *getIdentifier(const QString &s); + llvm::AllocaInst *newLLVMTemp(llvm::Type *type, llvm::Value *size = 0); + llvm::Value * genArguments(IR::ExprList *args, int &argc); + void genCallTemp(IR::Call *e, llvm::Value *result = 0); + void genCallName(IR::Call *e, llvm::Value *result = 0); + void genCallMember(IR::Call *e, llvm::Value *result = 0); + void genConstructTemp(IR::New *e, llvm::Value *result = 0); + void genConstructName(IR::New *e, llvm::Value *result = 0); + void genConstructMember(IR::New *e, llvm::Value *result = 0); + llvm::Value *createValue(IR::Const *e); + llvm::Value *toValuePtr(IR::Expr *e); + llvm::Value *genStringList(const QList &strings, + const char *arrayName, const char *elementName); + + +private: + llvm::Module *_llvmModule; + llvm::Function *_llvmFunction; + llvm::Value *_llvmValue; + llvm::Type *_numberTy; + llvm::Type *_valueTy; + llvm::Type *_contextPtrTy; + llvm::Type *_stringPtrTy; + llvm::FunctionType *_functionTy; + llvm::Instruction *_allocaInsertPoint; + IR::Function *_function; + IR::BasicBlock *_block; + QHash _functionMap; + QHash _blockMap; + QVector _tempMap; + QHash _stringMap; + llvm::FunctionPassManager *_fpm; +}; + +} // LLVM namespace +} // QQmlJS namespace + +#endif // QV4ISEL_LLVM_P_H diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp new file mode 100644 index 0000000000..a4a136c656 --- /dev/null +++ b/src/v4/qv4isel_masm.cpp @@ -0,0 +1,933 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4isel_masm_p.h" +#include "qmljs_runtime.h" +#include "qv4object.h" +#include "qv4functionobject.h" +#include "qv4regexpobject.h" + +#include +#include + +#include +#include +#include + +#ifndef NO_UDIS86 +# include +#endif + +using namespace QQmlJS; +using namespace QQmlJS::MASM; +using namespace QQmlJS::VM; + +Assembler::Assembler(IR::Function* function) + : _function(function) +{ +} + +void Assembler::registerBlock(IR::BasicBlock* block) +{ + _addrs[block] = label(); +} + +void Assembler::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) +{ + if (current->index + 1 != target->index) + _patches[target].append(jump()); +} + +void Assembler::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) +{ + _patches[targetBlock].append(targetJump); +} + +Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, IR::Temp *t) +{ + int32_t offset = 0; + if (t->index < 0) { + const int arg = -t->index - 1; + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, arguments)), reg); + offset = arg * sizeof(Value); + } else if (t->index < _function->locals.size()) { + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, locals)), reg); + offset = t->index * sizeof(Value); + } else { + const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size() + 1; + // StackFrameRegister points to its old value on the stack, so even for the first temp we need to + // subtract at least sizeof(Value). + offset = - sizeof(Value) * (arg + 1); + reg = StackFrameRegister; + } + return Pointer(reg, offset); +} + +template +void Assembler::copyValue(Result result, Source source) +{ +#ifdef VALUE_FITS_IN_REGISTER + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgument(source, ReturnValueRegister); + storeArgument(ReturnValueRegister, result); +#else + loadDouble(source, FPGpr0); + storeDouble(FPGpr0, result); +#endif +} + +void Assembler::storeValue(VM::Value value, IR::Temp* destination) +{ + Address addr = loadTempAddress(ScratchRegister, destination); + storeValue(value, addr); +} + +void Assembler::enterStandardStackFrame(int locals) +{ +#if CPU(ARM) + push(JSC::ARMRegisters::lr); +#endif + push(StackFrameRegister); + move(StackPointerRegister, StackFrameRegister); + + // space for the locals and the ContextRegister + int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*); + +#if CPU(X86) || CPU(X86_64) + frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX +#endif + subPtr(TrustedImm32(frameSize), StackPointerRegister); + +#if CPU(X86) || CPU(ARM) + for (int saveReg = CalleeSavedFirstRegister; saveReg <= CalleeSavedLastRegister; ++saveReg) + push(static_cast(saveReg)); +#endif + // save the ContextRegister + storePtr(ContextRegister, StackPointerRegister); +} + +void Assembler::leaveStandardStackFrame(int locals) +{ + // restore the ContextRegister + loadPtr(StackPointerRegister, ContextRegister); + +#if CPU(X86) || CPU(ARM) + for (int saveReg = CalleeSavedLastRegister; saveReg >= CalleeSavedFirstRegister; --saveReg) + pop(static_cast(saveReg)); +#endif + // space for the locals and the ContextRegister + int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*); +#if CPU(X86) || CPU(X86_64) + frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX +#endif + addPtr(TrustedImm32(frameSize), StackPointerRegister); + + pop(StackFrameRegister); +#if CPU(ARM) + pop(JSC::ARMRegisters::lr); +#endif +} + + + +#define OP(op) \ + { isel_stringIfy(op), op, 0, 0 } + +#define INLINE_OP(op, memOp, immOp) \ + { isel_stringIfy(op), op, memOp, immOp } + +#define NULL_OP \ + { 0, 0, 0, 0 } + +const Assembler::BinaryOperationInfo Assembler::binaryOperations[QQmlJS::IR::LastAluOp + 1] = { + NULL_OP, // OpInvalid + NULL_OP, // OpIfTrue + NULL_OP, // OpNot + NULL_OP, // OpUMinus + NULL_OP, // OpUPlus + NULL_OP, // OpCompl + NULL_OP, // OpIncrement + NULL_OP, // OpDecrement + + INLINE_OP(__qmljs_bit_and, &Assembler::inline_and32, &Assembler::inline_and32), // OpBitAnd + INLINE_OP(__qmljs_bit_or, &Assembler::inline_or32, &Assembler::inline_or32), // OpBitOr + INLINE_OP(__qmljs_bit_xor, &Assembler::inline_xor32, &Assembler::inline_xor32), // OpBitXor + + INLINE_OP(__qmljs_add, &Assembler::inline_add32, &Assembler::inline_add32), // OpAdd + INLINE_OP(__qmljs_sub, &Assembler::inline_sub32, &Assembler::inline_sub32), // OpSub + INLINE_OP(__qmljs_mul, &Assembler::inline_mul32, &Assembler::inline_mul32), // OpMul + + OP(__qmljs_div), // OpDiv + OP(__qmljs_mod), // OpMod + + INLINE_OP(__qmljs_shl, &Assembler::inline_shl32, &Assembler::inline_shl32), // OpLShift + INLINE_OP(__qmljs_shr, &Assembler::inline_shr32, &Assembler::inline_shr32), // OpRShift + INLINE_OP(__qmljs_ushr, &Assembler::inline_ushr32, &Assembler::inline_ushr32), // OpURShift + + OP(__qmljs_gt), // OpGt + OP(__qmljs_lt), // OpLt + OP(__qmljs_ge), // OpGe + OP(__qmljs_le), // OpLe + OP(__qmljs_eq), // OpEqual + OP(__qmljs_ne), // OpNotEqual + OP(__qmljs_se), // OpStrictEqual + OP(__qmljs_sne), // OpStrictNotEqual + + OP(__qmljs_instanceof), // OpInstanceof + OP(__qmljs_in), // OpIn + + NULL_OP, // OpAnd + NULL_OP // OpOr +}; + +void Assembler::generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Expr* left, IR::Expr* right) +{ + const BinaryOperationInfo& info = binaryOperations[operation]; + if (!info.fallbackImplementation) { + assert(!"unreachable"); + return; + } + + Value leftConst = Value::undefinedValue(); + Value rightConst = Value::undefinedValue(); + + bool canDoInline = info.inlineMemRegOp && info.inlineImmRegOp; + + if (canDoInline) { + if (left->asConst()) { + leftConst = convertToValue(left->asConst()); + canDoInline = canDoInline && leftConst.tryIntegerConversion(); + } + if (right->asConst()) { + rightConst = convertToValue(right->asConst()); + canDoInline = canDoInline && rightConst.tryIntegerConversion(); + } + } + + Jump binOpFinished; + + if (canDoInline) { + + Jump leftTypeCheck; + if (left->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, left->asTemp()); + typeAddress.offset += offsetof(VM::Value, tag); + leftTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); + } + + Jump rightTypeCheck; + if (right->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, right->asTemp()); + typeAddress.offset += offsetof(VM::Value, tag); + rightTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); + } + + if (left->asTemp()) { + Address leftValue = loadTempAddress(ScratchRegister, left->asTemp()); + leftValue.offset += offsetof(VM::Value, int_32); + load32(leftValue, IntegerOpRegister); + } else { // left->asConst() + move(TrustedImm32(leftConst.integerValue()), IntegerOpRegister); + } + + Jump overflowCheck; + + if (right->asTemp()) { + Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); + rightValue.offset += offsetof(VM::Value, int_32); + + overflowCheck = (this->*info.inlineMemRegOp)(rightValue, IntegerOpRegister); + } else { // right->asConst() + overflowCheck = (this->*info.inlineImmRegOp)(TrustedImm32(rightConst.integerValue()), IntegerOpRegister); + } + + Address resultAddr = loadTempAddress(ScratchRegister, target); + Address resultValueAddr = resultAddr; + resultValueAddr.offset += offsetof(VM::Value, int_32); + store32(IntegerOpRegister, resultValueAddr); + + Address resultTypeAddr = resultAddr; + resultTypeAddr.offset += offsetof(VM::Value, tag); + store32(TrustedImm32(VM::Value::_Integer_Type), resultTypeAddr); + + binOpFinished = jump(); + + if (leftTypeCheck.isSet()) + leftTypeCheck.link(this); + if (rightTypeCheck.isSet()) + rightTypeCheck.link(this); + if (overflowCheck.isSet()) + overflowCheck.link(this); + } + + // Fallback + generateFunctionCallImp(target, info.name, info.fallbackImplementation, left, right, ContextRegister); + + if (binOpFinished.isSet()) + binOpFinished.link(this); +} +#if OS(LINUX) +static void printDisassembledOutputWithCalls(const char* output, const QHash& functions) +{ + QByteArray processedOutput(output); + for (QHash::ConstIterator it = functions.begin(), end = functions.end(); + it != end; ++it) { + QByteArray ptrString = QByteArray::number(qlonglong(it.key()), 16); + ptrString.prepend("0x"); + processedOutput = processedOutput.replace(ptrString, it.value()); + } + fprintf(stderr, "%s\n", processedOutput.constData()); +} +#endif + +void Assembler::link(VM::Function *vmFunc) +{ + QHashIterator > it(_patches); + while (it.hasNext()) { + it.next(); + IR::BasicBlock *block = it.key(); + Label target = _addrs.value(block); + assert(target.isSet()); + foreach (Jump jump, it.value()) + jump.linkTo(target, this); + } + + JSC::JSGlobalData dummy; + JSC::LinkBuffer linkBuffer(dummy, this, 0); + QHash functions; + foreach (CallToLink ctl, _callsToLink) { + linkBuffer.link(ctl.call, ctl.externalFunction); + functions[ctl.externalFunction.value()] = ctl.functionName; + } + + static bool showCode = !qgetenv("SHOW_CODE").isNull(); + if (showCode) { +#if OS(LINUX) + char* disasmOutput = 0; + size_t disasmLength = 0; + FILE* disasmStream = open_memstream(&disasmOutput, &disasmLength); + WTF::setDataFile(disasmStream); +#endif + + QByteArray name = _function->name->toUtf8(); + vmFunc->codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); + + WTF::setDataFile(stderr); +#if OS(LINUX) + fclose(disasmStream); +#if CPU(X86) || CPU(X86_64) + printDisassembledOutputWithCalls(disasmOutput, functions); +#endif + free(disasmOutput); +#endif + } else { + vmFunc->codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); + } + + vmFunc->code = (Value (*)(VM::ExecutionContext *, const uchar *)) vmFunc->codeRef.code().executableAddress(); +} + +InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module) + : EvalInstructionSelection(engine, module) + , _block(0) + , _function(0) + , _vmFunction(0) + , _asm(0) +{ +} + +InstructionSelection::~InstructionSelection() +{ + delete _asm; +} + +void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) +{ + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + Assembler* oldAssembler = _asm; + _asm = new Assembler(_function); + + int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) + 1; + locals = (locals + 1) & ~1; + _asm->enterStandardStackFrame(locals); + + int contextPointer = 0; +#ifndef VALUE_FITS_IN_REGISTER + // When the return VM value doesn't fit into a register, then + // the caller provides a pointer for storage as first argument. + // That shifts the index the context pointer argument by one. + contextPointer++; +#endif +#if CPU(X86) + _asm->loadPtr(addressForArgument(contextPointer), Assembler::ContextRegister); +#elif CPU(X86_64) || CPU(ARM) + _asm->move(_asm->registerForArgument(contextPointer), Assembler::ContextRegister); +#else + assert(!"TODO"); +#endif + + foreach (IR::BasicBlock *block, _function->basicBlocks) { + _block = block; + _asm->registerBlock(_block); + foreach (IR::Stmt *s, block->statements) { + s->accept(this); + } + } + + _asm->leaveStandardStackFrame(locals); +#ifndef VALUE_FITS_IN_REGISTER + // Emulate ret(n) instruction + // Pop off return address into scratch register ... + _asm->pop(Assembler::ScratchRegister); + // ... and overwrite the invisible argument with + // the return address. + _asm->poke(Assembler::ScratchRegister); +#endif + _asm->ret(); + + _asm->link(_vmFunction); + + qSwap(_vmFunction, vmFunction); + qSwap(_function, function); + delete _asm; + _asm = oldAssembler; +} + +void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) +{ + callRuntimeMethod(result, __qmljs_call_activation_property, func, args); +} + +void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_typeof_member, base, identifier(name), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_typeof_element, base, index, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_typeof_name, identifier(name), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_typeof, value, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_delete_member, Assembler::ContextRegister, base, identifier(name)); +} + +void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_delete_subscript, Assembler::ContextRegister, base, index); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_delete_name, Assembler::ContextRegister, identifier(name)); +} + +void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) +{ + _asm->storeValue(Value::fromBoolean(false), result); +} + +void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_increment_member, base, identifier(name), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_increment_element, base, index, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_increment_name, identifier(name), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_increment, Assembler::PointerToValue(value), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_decrement_member, base, identifier(name), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_decrement_element, base, index, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_decrement_name, identifier(name), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_builtin_post_decrement, Assembler::PointerToValue(value), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinThrow(IR::Temp *arg) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, arg, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) +{ + generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_create_exception_handler, Assembler::ContextRegister); + generateFunctionCall(Assembler::ReturnValueRegister, setjmp, Assembler::ReturnValueRegister); + Address addr = _asm->loadTempAddress(Assembler::ScratchRegister, result); + _asm->store32(Assembler::ReturnValueRegister, addr); + addr.offset += 4; + _asm->store32(Assembler::TrustedImm32(Value::Boolean_Type), addr); +} + +void InstructionSelection::callBuiltinDeleteExceptionHandler() +{ + generateFunctionCall(Assembler::Void, __qmljs_delete_exception_handler, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinGetException(IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_get_exception, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_foreach_iterator_object, arg, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) +{ + generateFunctionCall(result, __qmljs_foreach_next_property_name, arg); +} + +void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) +{ + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with_scope, arg, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPopScope() +{ + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_pop_scope, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_declare_var, Assembler::ContextRegister, + Assembler::TrustedImm32(deletable), identifier(name)); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter, + object, identifier(name), getter, setter, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property, + object, identifier(name), value, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_array_property, + object, Assembler::TrustedImm32(index), value, Assembler::ContextRegister); +} + +void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) +{ + int argc = prepareVariableArguments(args); + IR::Temp* thisObject = 0; + generateFunctionCall(result, __qmljs_call_value, Assembler::ContextRegister, thisObject, value, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::loadThisObject(IR::Temp *temp) +{ + generateFunctionCall(temp, __qmljs_get_thisObject, Assembler::ContextRegister); +} + +void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) +{ + _asm->storeValue(convertToValue(sourceConst), targetTemp); +} + +void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) +{ + Value v = Value::fromString(identifier(str)); + _asm->storeValue(v, targetTemp); +} + +void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) +{ + Value v = Value::fromObject(engine()->newRegExpObject(*sourceRegexp->value, + sourceRegexp->flags)); + _vmFunction->generatedValues.append(v); + _asm->storeValue(v, targetTemp); +} + +void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) +{ + String *propertyName = identifier(name); + generateFunctionCall(temp, __qmljs_get_activation_property, Assembler::ContextRegister, propertyName); +} + +void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) +{ + String *propertyName = identifier(targetName); + generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, Assembler::ContextRegister, propertyName, source); +} + +void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) +{ + VM::Function *vmFunc = vmFunction(closure->value); + assert(vmFunc); + generateFunctionCall(target, __qmljs_init_closure, Assembler::TrustedImmPtr(vmFunc), Assembler::ContextRegister); +} + +void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::Temp *target) +{ + generateFunctionCall(target, __qmljs_get_property, Assembler::ContextRegister, base, identifier(name)); +} + +void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +{ + generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, targetBase, identifier(targetName), source); +} + +void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) +{ + generateFunctionCall(target, __qmljs_get_element, Assembler::ContextRegister, base, index); +} + +void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) +{ + generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, targetBase, targetIndex, source); +} + +void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + _asm->copyValue(targetTemp, sourceTemp); +} + +#define setOp(op, opName, operation) \ + do { op = operation; opName = isel_stringIfy(operation); } while (0) + +void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + Value (*op)(const Value value, ExecutionContext *ctx) = 0; + const char *opName = 0; + switch (oper) { + case IR::OpIfTrue: assert(!"unreachable"); break; + case IR::OpNot: setOp(op, opName, __qmljs_not); break; + case IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break; + case IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break; + case IR::OpCompl: setOp(op, opName, __qmljs_compl); break; + case IR::OpIncrement: setOp(op, opName, __qmljs_increment); break; + case IR::OpDecrement: setOp(op, opName, __qmljs_decrement); break; + default: assert(!"unreachable"); break; + } // switch + + if (op) + _asm->generateFunctionCallImp(targetTemp, opName, op, sourceTemp, + Assembler::ContextRegister); +} + +void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) +{ + _asm->generateBinOp(oper, target, leftSource, rightSource); +} + +void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) +{ + void (*op)(const Value value, String *name, ExecutionContext *ctx) = 0; + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break; + case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_name); break; + case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_name); break; + case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_name); break; + case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_name); break; + case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_name); break; + case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_name); break; + case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_name); break; + case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_name); break; + case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_name); break; + case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_name); break; + default: + Q_UNREACHABLE(); + break; + } + if (op) { + _asm->generateFunctionCallImp(Assembler::Void, opName, op, sourceExpr, identifier(targetName), Assembler::ContextRegister); + } +} + +void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) +{ + void (*op)(Value base, Value index, Value value, ExecutionContext *ctx) = 0; + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; + case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_element); break; + case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_element); break; + case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_element); break; + case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_element); break; + case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_element); break; + case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_element); break; + case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_element); break; + case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_element); break; + case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_element); break; + case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_element); break; + default: + Q_UNREACHABLE(); + break; + } + + if (op) { + _asm->generateFunctionCallImp(Assembler::Void, opName, op, targetBaseTemp, targetIndexTemp, sourceExpr, Assembler::ContextRegister); + } +} + +void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +{ + void (*op)(Value value, Value base, String *name, ExecutionContext *ctx) = 0; + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; + case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_member); break; + case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_member); break; + case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_member); break; + case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_member); break; + case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_member); break; + case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_member); break; + case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_member); break; + case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_member); break; + case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_member); break; + case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_member); break; + default: + Q_UNREACHABLE(); + break; + } + + if (op) { + String* member = identifier(targetName); + _asm->generateFunctionCallImp(Assembler::Void, opName, op, source, targetBase, member, Assembler::ContextRegister); + } +} + +void InstructionSelection::callProperty(IR::Temp *base, const QString &name, + IR::ExprList *args, IR::Temp *result) +{ + assert(base != 0); + + int argc = prepareVariableArguments(args); + generateFunctionCall(result, __qmljs_call_property, + Assembler::ContextRegister, base, identifier(name), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) +{ + assert(base != 0); + + int argc = prepareVariableArguments(args); + generateFunctionCall(result, __qmljs_call_element, + Assembler::ContextRegister, base, index, + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); +} + +String *InstructionSelection::identifier(const QString &s) +{ + String *str = engine()->identifier(s); + _vmFunction->identifiers.append(str); + return str; +} + +void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result) +{ + assert(func != 0); + + callRuntimeMethod(result, __qmljs_construct_activation_property, func, args); +} + +void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) +{ + int argc = prepareVariableArguments(args); + generateFunctionCall(result, __qmljs_construct_property, Assembler::ContextRegister, base, identifier(name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) +{ + assert(value != 0); + + int argc = prepareVariableArguments(args); + generateFunctionCall(result, __qmljs_construct_value, Assembler::ContextRegister, value, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::visitJump(IR::Jump *s) +{ + _asm->jumpToBlock(_block, s->target); +} + +void InstructionSelection::visitCJump(IR::CJump *s) +{ + if (IR::Temp *t = s->cond->asTemp()) { + Address temp = _asm->loadTempAddress(Assembler::ScratchRegister, t); + Address tag = temp; + tag.offset += offsetof(VM::Value, tag); + Assembler::Jump booleanConversion = _asm->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(VM::Value::Boolean_Type)); + + Address data = temp; + data.offset += offsetof(VM::Value, int_32); + _asm->load32(data, Assembler::ReturnValueRegister); + Assembler::Jump testBoolean = _asm->jump(); + + booleanConversion.link(_asm); + { + generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, t, Assembler::ContextRegister); + } + + testBoolean.link(_asm); + Assembler::Jump target = _asm->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); + _asm->addPatch(s->iftrue, target); + + _asm->jumpToBlock(_block, s->iffalse); + return; + } else if (IR::Binop *b = s->cond->asBinop()) { + if ((b->left->asTemp() || b->left->asConst()) && + (b->right->asTemp() || b->right->asConst())) { + Bool (*op)(const Value, const Value, ExecutionContext *ctx) = 0; + const char *opName = 0; + switch (b->op) { + default: Q_UNREACHABLE(); assert(!"todo"); break; + case IR::OpGt: setOp(op, opName, __qmljs_cmp_gt); break; + case IR::OpLt: setOp(op, opName, __qmljs_cmp_lt); break; + case IR::OpGe: setOp(op, opName, __qmljs_cmp_ge); break; + case IR::OpLe: setOp(op, opName, __qmljs_cmp_le); break; + case IR::OpEqual: setOp(op, opName, __qmljs_cmp_eq); break; + case IR::OpNotEqual: setOp(op, opName, __qmljs_cmp_ne); break; + case IR::OpStrictEqual: setOp(op, opName, __qmljs_cmp_se); break; + case IR::OpStrictNotEqual: setOp(op, opName, __qmljs_cmp_sne); break; + case IR::OpInstanceof: setOp(op, opName, __qmljs_cmp_instanceof); break; + case IR::OpIn: setOp(op, opName, __qmljs_cmp_in); break; + } // switch + + _asm->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, b->left, b->right, Assembler::ContextRegister); + + Assembler::Jump target = _asm->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); + _asm->addPatch(s->iftrue, target); + + _asm->jumpToBlock(_block, s->iffalse); + return; + } else { + assert(!"wip"); + } + Q_UNIMPLEMENTED(); + } + Q_UNIMPLEMENTED(); + assert(!"TODO"); +} + +void InstructionSelection::visitRet(IR::Ret *s) +{ + if (IR::Temp *t = s->expr->asTemp()) { +#ifdef VALUE_FITS_IN_REGISTER + _asm->copyValue(Assembler::ReturnValueRegister, t); +#else + _asm->loadPtr(addressForArgument(0), Assembler::ReturnValueRegister); + _asm->copyValue(Address(Assembler::ReturnValueRegister, 0), t); +#endif + return; + } + Q_UNIMPLEMENTED(); + Q_UNUSED(s); +} + +int InstructionSelection::prepareVariableArguments(IR::ExprList* args) +{ + int argc = 0; + for (IR::ExprList *it = args; it; it = it->next) { + ++argc; + } + + int i = 0; + for (IR::ExprList *it = args; it; it = it->next, ++i) { + IR::Temp *arg = it->expr->asTemp(); + assert(arg != 0); + _asm->copyValue(argumentAddressForCall(i), arg); + } + + return argc; +} + +void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, ActivationMethod method, IR::Expr *base, IR::ExprList *args) +{ + IR::Name *baseName = base->asName(); + assert(baseName != 0); + + int argc = prepareVariableArguments(args); + _asm->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args) +{ + int argc = prepareVariableArguments(args); + _asm->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + + diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h new file mode 100644 index 0000000000..de0971f0cb --- /dev/null +++ b/src/v4/qv4isel_masm_p.h @@ -0,0 +1,798 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ISEL_MASM_P_H +#define QV4ISEL_MASM_P_H + +#include "qv4global.h" +#include "qv4ir_p.h" +#include "qv4isel_p.h" +#include "qv4isel_util_p.h" +#include "qv4object.h" +#include "qmljs_runtime.h" + +#include +#include +#include +#include + +namespace QQmlJS { +namespace MASM { + +class Assembler : public JSC::MacroAssembler +{ +public: + Assembler(IR::Function* function); +#if CPU(X86) + +#undef VALUE_FITS_IN_REGISTER + + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID ContextRegister = JSC::X86Registers::esi; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::ecx; + static const RegisterID CalleeSavedFirstRegister = ScratchRegister; + static const RegisterID CalleeSavedLastRegister = ScratchRegister; + static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + + static const int RegisterSize = 4; + + static const int RegisterArgumentCount = 0; + static RegisterID registerForArgument(int) + { + assert(false); + // Not reached. + return JSC::X86Registers::eax; + } +#elif CPU(X86_64) + +#define VALUE_FITS_IN_REGISTER + + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID ContextRegister = JSC::X86Registers::r14; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::r10; + static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + + static const int RegisterSize = 8; + + static const int RegisterArgumentCount = 6; + static RegisterID registerForArgument(int index) + { + static RegisterID regs[RegisterArgumentCount] = { + JSC::X86Registers::edi, + JSC::X86Registers::esi, + JSC::X86Registers::edx, + JSC::X86Registers::ecx, + JSC::X86Registers::r8, + JSC::X86Registers::r9 + }; + assert(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; +#elif CPU(ARM) + +#undef VALUE_FITS_IN_REGISTER + + static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4; + static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; + static const RegisterID ContextRegister = JSC::ARMRegisters::r5; + static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; + static const RegisterID ScratchRegister = JSC::ARMRegisters::r6; + static const RegisterID CalleeSavedFirstRegister = JSC::ARMRegisters::r4; + static const RegisterID CalleeSavedLastRegister = JSC::ARMRegisters::r11; + static const RegisterID IntegerOpRegister = JSC::X86Registers::r0; + static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; + + static const int RegisterSize = 4; + + static const RegisterID RegisterArgument1 = JSC::ARMRegisters::r0; + static const RegisterID RegisterArgument2 = JSC::ARMRegisters::r1; + static const RegisterID RegisterArgument3 = JSC::ARMRegisters::r2; + static const RegisterID RegisterArgument4 = JSC::ARMRegisters::r3; + + static const int RegisterArgumentCount = 4; + static RegisterID registerForArgument(int index) + { + assert(index >= 0 && index < RegisterArgumentCount); + return static_cast(JSC::ARMRegisters::r0 + index); + }; +#else +#error Argh. +#endif + + // Explicit type to allow distinguishing between + // pushing an address itself or the value it points + // to onto the stack when calling functions. + struct Pointer : public Address + { + explicit Pointer(const Address& addr) + : Address(addr) + {} + explicit Pointer(RegisterID reg, int32_t offset) + : Address(reg, offset) + {} + }; + + struct VoidType {}; + static const VoidType Void; + + + typedef JSC::FunctionPtr FunctionPtr; + + struct CallToLink { + Call call; + FunctionPtr externalFunction; + const char* functionName; + }; + struct PointerToValue { + PointerToValue(IR::Temp *value) : value(value) {} + IR::Temp *value; + }; + + void callAbsolute(const char* functionName, FunctionPtr function) { + CallToLink ctl; + ctl.call = call(); + ctl.externalFunction = function; + ctl.functionName = functionName; + _callsToLink.append(ctl); + } + + void registerBlock(IR::BasicBlock*); + void jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target); + void addPatch(IR::BasicBlock* targetBlock, Jump targetJump); + + Pointer loadTempAddress(RegisterID reg, IR::Temp *t); + + void loadArgument(RegisterID source, RegisterID dest) + { + move(source, dest); + } + + void loadArgument(TrustedImmPtr ptr, RegisterID dest) + { + move(TrustedImmPtr(ptr), dest); + } + + void loadArgument(const Pointer& ptr, RegisterID dest) + { + addPtr(TrustedImm32(ptr.offset), ptr.base, dest); + } + + void loadArgument(PointerToValue temp, RegisterID dest) + { + assert(temp.value); + + Pointer addr = loadTempAddress(dest, temp.value); + loadArgument(addr, dest); + } + +#ifdef VALUE_FITS_IN_REGISTER + void loadArgument(IR::Temp* temp, RegisterID dest) + { + if (!temp) { + VM::Value undefined = VM::Value::undefinedValue(); + move(TrustedImm64(undefined.val), dest); + } else { + Pointer addr = loadTempAddress(dest, temp); + load64(addr, dest); + } + } + + void loadArgument(IR::Const* c, RegisterID dest) + { + VM::Value v = convertToValue(c); + move(TrustedImm64(v.val), dest); + } + + void loadArgument(IR::Expr* expr, RegisterID dest) + { + if (!expr) { + VM::Value undefined = VM::Value::undefinedValue(); + move(TrustedImm64(undefined.val), dest); + } else if (expr->asTemp()){ + loadArgument(expr->asTemp(), dest); + } else if (expr->asConst()) { + loadArgument(expr->asConst(), dest); + } else { + assert(!"unimplemented expression type in loadArgument"); + } + } +#else + void loadArgument(IR::Expr*, RegisterID) + { + assert(!"unimplemented: expression in loadArgument"); + } +#endif + + void loadArgument(VM::String* string, RegisterID dest) + { + loadArgument(TrustedImmPtr(string), dest); + } + + void loadArgument(TrustedImm32 imm32, RegisterID dest) + { + xorPtr(dest, dest); + if (imm32.m_value) + move(imm32, dest); + } + + void storeArgument(RegisterID src, IR::Temp *temp) + { + if (temp) { + Pointer addr = loadTempAddress(ScratchRegister, temp); +#ifdef VALUE_FITS_IN_REGISTER + store64(src, addr); +#else + // If the value doesn't fit into a register, then the + // register contains the address to where the argument + // (return value) is stored. Copy it from there. + copyValue(addr, Pointer(src, 0)); +#endif + } + } + +#ifdef VALUE_FITS_IN_REGISTER + void storeArgument(RegisterID src, const Pointer &dest) + { + store64(src, dest); + } +#endif + + void storeArgument(RegisterID src, RegisterID dest) + { + move(src, dest); + } + + void storeArgument(RegisterID, VoidType) + { + } + + using JSC::MacroAssembler::push; + + void push(const Pointer& ptr) + { + addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister); + push(ScratchRegister); + } + + void push(VM::Value value) + { +#ifdef VALUE_FITS_IN_REGISTER + move(TrustedImm64(value.val), ScratchRegister); + push(ScratchRegister); +#else + move(TrustedImm32(value.tag), ScratchRegister); + push(ScratchRegister); + move(TrustedImm32(value.int_32), ScratchRegister); + push(ScratchRegister); +#endif + } + + void push(PointerToValue temp) + { + assert (temp.value); + + Pointer ptr = loadTempAddress(ScratchRegister, temp.value); + push(ptr); + } + + void push(IR::Temp* temp) + { + if (temp) { + Address addr = loadTempAddress(ScratchRegister, temp); + addr.offset += 4; + push(addr); + addr.offset -= 4; + push(addr); + } else { + VM::Value undefined = VM::Value::undefinedValue(); + push(undefined); + } + } + + void push(IR::Const* c) + { + VM::Value v = convertToValue(c); + push(v); + } + + void push(IR::Expr* e) + { + if (!e) { + VM::Value undefined = VM::Value::undefinedValue(); + push(undefined); + } else if (IR::Const *c = e->asConst()) + push(c); + else if (IR::Temp *t = e->asTemp()) { + push(t); + } else { + assert(!"Trying to push an expression that is not a Temp or Const"); + } + } + + void push(TrustedImmPtr ptr) + { + move(TrustedImmPtr(ptr), ScratchRegister); + push(ScratchRegister); + } + + void push(VM::String* name) + { + push(TrustedImmPtr(name)); + } + + using JSC::MacroAssembler::loadDouble; + void loadDouble(IR::Temp* temp, FPRegisterID dest) + { + Pointer ptr = loadTempAddress(ScratchRegister, temp); + loadDouble(ptr, dest); + } + + using JSC::MacroAssembler::storeDouble; + void storeDouble(FPRegisterID source, IR::Temp* temp) + { + Pointer ptr = loadTempAddress(ScratchRegister, temp); + storeDouble(source, ptr); + } + + template + void copyValue(Result result, Source source); + + void storeValue(VM::Value value, Address destination) + { +#ifdef VALUE_FITS_IN_REGISTER + store64(TrustedImm64(value.val), destination); +#else + store32(TrustedImm32(value.int_32), destination); + destination.offset += 4; + store32(TrustedImm32(value.tag), destination); +#endif + } + + void storeValue(VM::Value value, IR::Temp* temp); + + void enterStandardStackFrame(int locals); + void leaveStandardStackFrame(int locals); + + void callFunctionPrologue() + { +#if CPU(X86) + // Callee might clobber it :( + push(ContextRegister); +#endif + } + void callFunctionEpilogue() + { +#if CPU(X86) + pop(ContextRegister); +#endif + } + + static inline int sizeOfArgument(VoidType) + { return 0; } + static inline int sizeOfArgument(RegisterID) + { return RegisterSize; } + static inline int sizeOfArgument(IR::Temp*) + { return 8; } // Size of value + static inline int sizeOfArgument(IR::Expr*) + { return 8; } // Size of value + static inline int sizeOfArgument(const Pointer&) + { return sizeof(void*); } + static inline int sizeOfArgument(VM::String* string) + { return sizeof(string); } + static inline int sizeOfArgument(const PointerToValue &) + { return sizeof(void *); } + static inline int sizeOfArgument(TrustedImmPtr) + { return sizeof(void*); } + static inline int sizeOfArgument(TrustedImm32) + { return 4; } + + struct ArgumentLoader + { + ArgumentLoader(Assembler* _assembler, int totalNumberOfArguments) + : assembler(_assembler) + , stackSpaceForArguments(0) + , currentRegisterIndex(qMin(totalNumberOfArguments - 1, RegisterArgumentCount - 1)) + { + } + + template + void load(T argument) + { + if (currentRegisterIndex >= 0) { + assembler->loadArgument(argument, registerForArgument(currentRegisterIndex)); + --currentRegisterIndex; + } else { + assembler->push(argument); + stackSpaceForArguments += sizeOfArgument(argument); + } + } + + void load(VoidType) + { + if (currentRegisterIndex >= 0) + --currentRegisterIndex; + } + + Assembler *assembler; + int stackSpaceForArguments; + int currentRegisterIndex; + }; + + template + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + { + callFunctionPrologue(); + + int totalNumberOfArgs = 5; + + // If necessary reserve space for the return value on the stack and + // pass the pointer to it as the first hidden parameter. + bool returnValueOnStack = false; + int sizeOfReturnValueOnStack = sizeOfArgument(r); + if (sizeOfReturnValueOnStack > RegisterSize) { + sub32(TrustedImm32(sizeOfReturnValueOnStack), StackPointerRegister); + ++totalNumberOfArgs; + returnValueOnStack = true; + } + + ArgumentLoader l(this, totalNumberOfArgs); + l.load(arg5); + l.load(arg4); + l.load(arg3); + l.load(arg2); + l.load(arg1); + + if (returnValueOnStack) { + // Load address of return value + l.load(Pointer(StackPointerRegister, l.stackSpaceForArguments)); + } + + callAbsolute(functionName, function); + + int stackSizeToCorrect = l.stackSpaceForArguments; + if (returnValueOnStack) { + stackSizeToCorrect -= sizeof(void*); // Callee removed the hidden argument (address of return value) + stackSizeToCorrect += sizeOfReturnValueOnStack; + } + + storeArgument(ReturnValueRegister, r); + + if (stackSizeToCorrect) + add32(TrustedImm32(stackSizeToCorrect), StackPointerRegister); + + callFunctionEpilogue(); + } + + template + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType()); + } + + template + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType()); + } + + template + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType()); + } + + template + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1) + { + generateFunctionCallImp(r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType()); + } + + typedef Jump (Assembler::*MemRegBinOp)(Address, RegisterID); + typedef Jump (Assembler::*ImmRegBinOp)(TrustedImm32, RegisterID); + + struct BinaryOperationInfo { + const char *name; + VM::Value (*fallbackImplementation)(const VM::Value, const VM::Value, VM::ExecutionContext *); + MemRegBinOp inlineMemRegOp; + ImmRegBinOp inlineImmRegOp; + }; + + static const BinaryOperationInfo binaryOperations[QQmlJS::IR::LastAluOp + 1]; + + void generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Expr* left, IR::Expr* right); + + Jump inline_add32(Address addr, RegisterID reg) + { + return branchAdd32(Overflow, addr, reg); + } + + Jump inline_add32(TrustedImm32 imm, RegisterID reg) + { + return branchAdd32(Overflow, imm, reg); + } + + Jump inline_sub32(Address addr, RegisterID reg) + { + return branchSub32(Overflow, addr, reg); + } + + Jump inline_sub32(TrustedImm32 imm, RegisterID reg) + { + return branchSub32(Overflow, imm, reg); + } + + Jump inline_mul32(Address addr, RegisterID reg) + { + return branchMul32(Overflow, addr, reg); + } + + Jump inline_mul32(TrustedImm32 imm, RegisterID reg) + { + return branchMul32(Overflow, imm, reg, reg); + } + + Jump inline_shl32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + lshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_shl32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + lshift32(imm, reg); + return Jump(); + } + + Jump inline_shr32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + rshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_shr32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + rshift32(imm, reg); + return Jump(); + } + + Jump inline_ushr32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + urshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_ushr32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + urshift32(imm, reg); + return Jump(); + } + + Jump inline_and32(Address addr, RegisterID reg) + { + and32(addr, reg); + return Jump(); + } + + Jump inline_and32(TrustedImm32 imm, RegisterID reg) + { + and32(imm, reg); + return Jump(); + } + + Jump inline_or32(Address addr, RegisterID reg) + { + or32(addr, reg); + return Jump(); + } + + Jump inline_or32(TrustedImm32 imm, RegisterID reg) + { + or32(imm, reg); + return Jump(); + } + + Jump inline_xor32(Address addr, RegisterID reg) + { + xor32(addr, reg); + return Jump(); + } + + Jump inline_xor32(TrustedImm32 imm, RegisterID reg) + { + xor32(imm, reg); + return Jump(); + } + + void link(VM::Function *vmFunc); + +private: + IR::Function* _function; + QHash _addrs; + QHash > _patches; + QList _callsToLink; +}; + +class Q_V4_EXPORT InstructionSelection: + protected IR::InstructionSelection, + public EvalInstructionSelection +{ +public: + InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); + ~InstructionSelection(); + + virtual void run(VM::Function *vmFunction, IR::Function *function); + +protected: + virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result); + virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteValue(IR::Temp *result); + virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinThrow(IR::Temp *arg); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); + virtual void callBuiltinDeleteExceptionHandler(); + virtual void callBuiltinGetException(IR::Temp *result); + virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinPushWithScope(IR::Temp *arg); + virtual void callBuiltinPopScope(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); + virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); + virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); + virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result); + virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); + virtual void loadThisObject(IR::Temp *temp); + virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp); + virtual void loadString(const QString &str, IR::Temp *targetTemp); + virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); + virtual void getActivationProperty(const QString &name, IR::Temp *temp); + virtual void setActivationProperty(IR::Expr *source, const QString &targetName); + virtual void initClosure(IR::Closure *closure, IR::Temp *target); + virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); + virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target); + virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); + virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); + virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); + virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + + typedef Assembler::Address Address; + typedef Assembler::Pointer Pointer; + + Address addressForArgument(int index) const + { + if (index < Assembler::RegisterArgumentCount) + return Address(_asm->registerForArgument(index), 0); + + // StackFrameRegister points to its old value on the stack, and above + // it we have the return address, hence the need to step over two + // values before reaching the first argument. + return Address(Assembler::StackFrameRegister, (index - Assembler::RegisterArgumentCount + 2) * sizeof(void*)); + } + + // Some run-time functions take (Value* args, int argc). This function is for populating + // the args. + Pointer argumentAddressForCall(int argument) + { + const int index = _function->maxNumberOfArguments - argument; + return Pointer(Assembler::StackFrameRegister, sizeof(VM::Value) * (-index) + - sizeof(void*) // size of ebp + ); + } + Pointer baseAddressForCallArguments() + { + return argumentAddressForCall(0); + } + + VM::String *identifier(const QString &s); + virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); + virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); + + virtual void visitJump(IR::Jump *); + virtual void visitCJump(IR::CJump *); + virtual void visitRet(IR::Ret *); + +private: + #define isel_stringIfyx(s) #s + #define isel_stringIfy(s) isel_stringIfyx(s) + + #define generateFunctionCall(t, function, ...) \ + _asm->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) + + int prepareVariableArguments(IR::ExprList* args); + + typedef VM::Value (*ActivationMethod)(VM::ExecutionContext *, VM::String *name, VM::Value *args, int argc); + typedef VM::Value (*BuiltinMethod)(VM::ExecutionContext *, VM::Value *args, int argc); + void callRuntimeMethodImp(IR::Temp *result, const char* name, ActivationMethod method, IR::Expr *base, IR::ExprList *args); + void callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args); +#define callRuntimeMethod(result, function, ...) \ + callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__) + + IR::BasicBlock *_block; + IR::Function* _function; + VM::Function* _vmFunction; + Assembler* _asm; +}; + +class Q_V4_EXPORT ISelFactory: public EvalISelFactory +{ +public: + virtual ~ISelFactory() {} + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module) + { return new InstructionSelection(engine, module); } +}; + +} // end of namespace MASM +} // end of namespace QQmlJS + +#endif // QV4ISEL_MASM_P_H diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp new file mode 100644 index 0000000000..b408be3673 --- /dev/null +++ b/src/v4/qv4isel_p.cpp @@ -0,0 +1,414 @@ +#include "debugging.h" +#include "qmljs_engine.h" +#include "qv4ir_p.h" +#include "qv4isel_p.h" +#include "qv4isel_util_p.h" +#include "qv4functionobject.h" + +#include + +#include + +namespace { +QTextStream qout(stderr, QIODevice::WriteOnly); +} // anonymous namespace + +using namespace QQmlJS; +using namespace QQmlJS::IR; + +EvalInstructionSelection::EvalInstructionSelection(VM::ExecutionEngine *engine, Module *module) + : _engine(engine) +{ + assert(engine); + assert(module); + + createFunctionMapping(engine, module->rootFunction); + foreach (IR::Function *f, module->functions) { + assert(_irToVM.contains(f)); + } +} + +EvalInstructionSelection::~EvalInstructionSelection() +{} + +EvalISelFactory::~EvalISelFactory() +{} + +VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngine *engine, Function *irFunction) +{ + VM::Function *vmFunction = engine->newFunction(irFunction->name ? *irFunction->name : QString()); + _irToVM.insert(irFunction, vmFunction); + + vmFunction->hasDirectEval = irFunction->hasDirectEval; + vmFunction->usesArgumentsObject = irFunction->usesArgumentsObject; + vmFunction->hasNestedFunctions = !irFunction->nestedFunctions.isEmpty(); + vmFunction->isStrict = irFunction->isStrict; + + foreach (const QString *formal, irFunction->formals) + if (formal) + vmFunction->formals.append(engine->identifier(*formal)); + foreach (const QString *local, irFunction->locals) + if (local) + vmFunction->locals.append(engine->identifier(*local)); + + foreach (IR::Function *function, irFunction->nestedFunctions) + createFunctionMapping(engine, function); + + + if (engine->debugger) + engine->debugger->mapFunction(vmFunction, irFunction); + + return vmFunction; +} + +VM::Function *EvalInstructionSelection::vmFunction(Function *f) { + VM::Function *function = _irToVM[f]; + if (!function->code) + run(function, f); + return function; +} + +void InstructionSelection::visitMove(IR::Move *s) +{ + if (s->op == IR::OpInvalid) { + if (IR::Name *n = s->target->asName()) { + if (s->source->asTemp() || s->source->asConst()) { + setActivationProperty(s->source, *n->id); + return; + } + } else if (IR::Temp *t = s->target->asTemp()) { + if (IR::Name *n = s->source->asName()) { + if (*n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. + loadThisObject(t); + else + getActivationProperty(*n->id, t); + return; + } else if (IR::Const *c = s->source->asConst()) { + loadConst(c, t); + return; + } else if (IR::Temp *t2 = s->source->asTemp()) { + copyValue(t2, t); + return; + } else if (IR::String *str = s->source->asString()) { + loadString(*str->value, t); + return; + } else if (IR::RegExp *re = s->source->asRegExp()) { + loadRegexp(re, t); + return; + } else if (IR::Closure *clos = s->source->asClosure()) { + initClosure(clos, t); + return; + } else if (IR::New *ctor = s->source->asNew()) { + if (Name *func = ctor->base->asName()) { + constructActivationProperty(func, ctor->args, t); + return; + } else if (IR::Member *member = ctor->base->asMember()) { + constructProperty(member->base->asTemp(), *member->name, ctor->args, t); + return; + } else if (IR::Temp *value = ctor->base->asTemp()) { + constructValue(value, ctor->args, t); + return; + } + } else if (IR::Member *m = s->source->asMember()) { + if (IR::Temp *base = m->base->asTemp()) { + getProperty(base, *m->name, t); + return; + } + } else if (IR::Subscript *ss = s->source->asSubscript()) { + getElement(ss->base->asTemp(), ss->index->asTemp(), t); + return; + } else if (IR::Unop *u = s->source->asUnop()) { + if (IR::Temp *e = u->expr->asTemp()) { + unop(u->op, e, t); + return; + } + } else if (IR::Binop *b = s->source->asBinop()) { + if ((b->left->asTemp() || b->left->asConst()) + && (b->right->asTemp() || b->right->asConst())) { + binop(b->op, b->left, b->right, t); + return; + } + } else if (IR::Call *c = s->source->asCall()) { + if (c->base->asName()) { + callBuiltin(c, t); + return; + } else if (Member *member = c->base->asMember()) { + callProperty(member->base, *member->name, c->args, t); + return; + } else if (Subscript *s = c->base->asSubscript()) { + callSubscript(s->base, s->index, c->args, t); + return; + } else if (IR::Temp *value = c->base->asTemp()) { + callValue(value, c->args, t); + return; + } + } + } else if (IR::Member *m = s->target->asMember()) { + if (IR::Temp *base = m->base->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { + setProperty(s->source, base, *m->name); + return; + } + } + } else if (IR::Subscript *ss = s->target->asSubscript()) { + if (s->source->asTemp() || s->source->asConst()) { + setElement(s->source, ss->base->asTemp(), ss->index->asTemp()); + return; + } + } + } else { + // inplace assignment, e.g. x += 1, ++x, ... + if (IR::Temp *t = s->target->asTemp()) { + if (s->source->asTemp() || s->source->asConst()) { + binop(s->op, t, s->source, t); + return; + } + } else if (IR::Name *n = s->target->asName()) { + if (s->source->asTemp() || s->source->asConst()) { + inplaceNameOp(s->op, s->source, *n->id); + return; + } + } else if (IR::Subscript *ss = s->target->asSubscript()) { + if (s->source->asTemp() || s->source->asConst()) { + inplaceElementOp(s->op, s->source, ss->base->asTemp(), + ss->index->asTemp()); + return; + } + } else if (IR::Member *m = s->target->asMember()) { + if (s->source->asTemp() || s->source->asConst()) { + inplaceMemberOp(s->op, s->source, m->base->asTemp(), *m->name); + return; + } + } + } + + // For anything else...: + Q_UNIMPLEMENTED(); + s->dump(qout, IR::Stmt::MIR); + qout << endl; + assert(!"TODO"); +} + +InstructionSelection::~InstructionSelection() +{ +} + +void InstructionSelection::visitEnter(Enter *) +{ + Q_UNREACHABLE(); +} + +void InstructionSelection::visitLeave(Leave *) +{ + Q_UNREACHABLE(); +} + +void InstructionSelection::visitExp(IR::Exp *s) +{ + if (IR::Call *c = s->expr->asCall()) { + // These are calls where the result is ignored. + if (c->base->asName()) { + callBuiltin(c, 0); + } else if (Temp *value = c->base->asTemp()) { + callValue(value, c->args, 0); + } else if (Member *member = c->base->asMember()) { + callProperty(member->base, *member->name, c->args, 0); + } else if (Subscript *s = c->base->asSubscript()) { + callSubscript(s->base, s->index, c->args, 0); + } else { + Q_UNIMPLEMENTED(); + } + } else { + Q_UNIMPLEMENTED(); + } +} + +void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result) +{ + IR::Name *baseName = call->base->asName(); + assert(baseName != 0); + + switch (baseName->builtin) { + case IR::Name::builtin_invalid: + callBuiltinInvalid(baseName, call->args, result); + return; + + case IR::Name::builtin_typeof: { + if (IR::Member *m = call->args->expr->asMember()) { + callBuiltinTypeofMember(m->base->asTemp(), *m->name, result); + return; + } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinTypeofSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (IR::Name *n = call->args->expr->asName()) { + callBuiltinTypeofName(*n->id, result); + return; + } else if (IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinTypeofValue(arg, result); + return; + } + } break; + + case IR::Name::builtin_delete: { + if (IR::Member *m = call->args->expr->asMember()) { + callBuiltinDeleteMember(m->base->asTemp(), *m->name, result); + return; + } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinDeleteSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (IR::Name *n = call->args->expr->asName()) { + callBuiltinDeleteName(*n->id, result); + return; + } else if (call->args->expr->asTemp()){ + // TODO: should throw in strict mode + callBuiltinDeleteValue(result); + return; + } + } break; + + case IR::Name::builtin_postincrement: { + if (IR::Member *m = call->args->expr->asMember()) { + callBuiltinPostIncrementMember(m->base->asTemp(), *m->name, result); + return; + } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinPostIncrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (IR::Name *n = call->args->expr->asName()) { + callBuiltinPostIncrementName(*n->id, result); + return; + } else if (IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinPostIncrementValue(arg, result); + return; + } + } break; + + case IR::Name::builtin_postdecrement: { + if (IR::Member *m = call->args->expr->asMember()) { + callBuiltinPostDecrementMember(m->base->asTemp(), *m->name, result); + return; + } else if (IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinPostDecrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (IR::Name *n = call->args->expr->asName()) { + callBuiltinPostDecrementName(*n->id, result); + return; + } else if (IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinPostDecrementValue(arg, result); + return; + } + } break; + + case IR::Name::builtin_throw: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinThrow(arg); + } return; + + case IR::Name::builtin_create_exception_handler: + callBuiltinCreateExceptionHandler(result); + return; + + case IR::Name::builtin_delete_exception_handler: + callBuiltinDeleteExceptionHandler(); + return; + + case IR::Name::builtin_get_exception: + callBuiltinGetException(result); + return; + + case IR::Name::builtin_foreach_iterator_object: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinForeachIteratorObject(arg, result); + } return; + + case IR::Name::builtin_foreach_next_property_name: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinForeachNextPropertyname(arg, result); + } return; + case IR::Name::builtin_push_with_scope: { + IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinPushWithScope(arg); + } return; + + case IR::Name::builtin_pop_scope: + callBuiltinPopScope(); + return; + + case IR::Name::builtin_declare_vars: { + if (!call->args) + return; + IR::Const *deletable = call->args->expr->asConst(); + assert(deletable->type == IR::BoolType); + for (IR::ExprList *it = call->args->next; it; it = it->next) { + IR::Name *arg = it->expr->asName(); + assert(arg != 0); + callBuiltinDeclareVar(deletable->value != 0, *arg->id); + } + } return; + + case IR::Name::builtin_define_getter_setter: { + if (!call->args) + return; + IR::ExprList *args = call->args; + IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + IR::Name *name = args->expr->asName(); + args = args->next; + assert(args); + IR::Temp *getter = args->expr->asTemp(); + args = args->next; + assert(args); + IR::Temp *setter = args->expr->asTemp(); + + callBuiltinDefineGetterSetter(object, *name->id, getter, setter); + } return; + + case IR::Name::builtin_define_property: { + if (!call->args) + return; + IR::ExprList *args = call->args; + IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + IR::Name *name = args->expr->asName(); + args = args->next; + assert(args); + IR::Temp *value = args->expr->asTemp(); + + callBuiltinDefineProperty(object, *name->id, value); + } return; + + case IR::Name::builtin_define_array_property: { + if (!call->args) + return; + IR::ExprList *args = call->args; + IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + IR::Const *index = args->expr->asConst(); + args = args->next; + assert(args); + IR::Temp *value = args->expr->asTemp(); + + callBuiltinDefineArrayProperty(object, int(index->value), value); + } return; + + default: + break; + } + + Q_UNIMPLEMENTED(); + call->dump(qout); qout << endl; + assert(!"TODO!"); + Q_UNREACHABLE(); +} diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h new file mode 100644 index 0000000000..d4f7052373 --- /dev/null +++ b/src/v4/qv4isel_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QV4ISEL_P_H +#define QV4ISEL_P_H + +#include "qv4global.h" +#include "qv4ir_p.h" + +#include +#include + +namespace QQmlJS { + +namespace VM { +struct ExecutionEngine; +struct Function; +} // namespace VM + +class Q_V4_EXPORT EvalInstructionSelection +{ +public: + EvalInstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); + virtual ~EvalInstructionSelection() = 0; + + VM::Function *vmFunction(IR::Function *f); + +protected: + VM::Function *createFunctionMapping(VM::ExecutionEngine *engine, IR::Function *irFunction); + VM::ExecutionEngine *engine() const { return _engine; } + virtual void run(VM::Function *vmFunction, IR::Function *function) = 0; + +private: + VM::ExecutionEngine *_engine; + QHash _irToVM; +}; + +class Q_V4_EXPORT EvalISelFactory +{ +public: + virtual ~EvalISelFactory() = 0; + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module) = 0; +}; + +namespace IR { +class Q_V4_EXPORT InstructionSelection: protected IR::StmtVisitor +{ +public: + virtual ~InstructionSelection() = 0; + +public: // visitor methods for StmtVisitor: + virtual void visitMove(IR::Move *s); + virtual void visitEnter(IR::Enter *); + virtual void visitLeave(IR::Leave *); + virtual void visitExp(IR::Exp *s); + +public: // to implement by subclasses: + virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) = 0; + virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0; + virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) = 0; + virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0; + virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinDeleteValue(IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) = 0; + virtual void callBuiltinThrow(IR::Temp *arg) = 0; + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result) = 0; + virtual void callBuiltinDeleteExceptionHandler() = 0; + virtual void callBuiltinGetException(IR::Temp *result) = 0; + virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) = 0; + virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) = 0; + virtual void callBuiltinPushWithScope(IR::Temp *arg) = 0; + virtual void callBuiltinPopScope() = 0; + virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0; + virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) = 0; + virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) = 0; + virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) = 0; + virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) = 0; + virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) = 0; + virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) = 0; + virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result) = 0; + virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) = 0; + virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) = 0; + virtual void loadThisObject(IR::Temp *temp) = 0; + virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) = 0; + virtual void loadString(const QString &str, IR::Temp *targetTemp) = 0; + virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) = 0; + virtual void getActivationProperty(const QString &name, IR::Temp *temp) = 0; + virtual void setActivationProperty(IR::Expr *source, const QString &targetName) = 0; + virtual void initClosure(IR::Closure *closure, IR::Temp *target) = 0; + virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target) = 0; + virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) = 0; + virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) = 0; + virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) = 0; + virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0; + virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0; + virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) = 0; + virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) = 0; + virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) = 0; + virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) = 0; + +private: + void callBuiltin(IR::Call *c, IR::Temp *temp); +}; +} // namespace IR + +} // namespace QQmlJS + +#endif // QV4ISEL_P_H diff --git a/src/v4/qv4isel_util_p.h b/src/v4/qv4isel_util_p.h new file mode 100644 index 0000000000..693af03e88 --- /dev/null +++ b/src/v4/qv4isel_util_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QV4ISEL_UTIL_P_H +#define QV4ISEL_UTIL_P_H + +#include "qmljs_runtime.h" +#include "qv4ir_p.h" + +namespace QQmlJS { + +inline VM::Value convertToValue(IR::Const *c) +{ + switch (c->type) { + case IR::NullType: + return VM::Value::nullValue(); + case IR::UndefinedType: + return VM::Value::undefinedValue(); + case IR::BoolType: + return VM::Value::fromBoolean(c->value != 0); + case IR::NumberType: { + int ival = (int)c->value; + // +0 != -0, so we need to convert to double when negating 0 + if (ival == c->value && c->value != -0) { + return VM::Value::fromInt32(ival); + } else { + return VM::Value::fromDouble(c->value); + } + } + default: + Q_UNREACHABLE(); + } +} + +} // namespace QQmlJS + +#endif // QV4ISEL_UTIL_P_H diff --git a/src/v4/qv4jsonobject.cpp b/src/v4/qv4jsonobject.cpp new file mode 100644 index 0000000000..fa39476f4e --- /dev/null +++ b/src/v4/qv4jsonobject.cpp @@ -0,0 +1,935 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace QQmlJS { +namespace VM { + +//#define PARSER_DEBUG +#ifdef PARSER_DEBUG +static int indent = 0; +#define BEGIN qDebug() << QByteArray(4*indent++, ' ').constData() +#define END --indent +#define DEBUG qDebug() << QByteArray(4*indent, ' ').constData() +#else +#define BEGIN if (1) ; else qDebug() +#define END do {} while (0) +#define DEBUG if (1) ; else qDebug() +#endif + + +class Parser +{ +public: + Parser(ExecutionContext *context, const QChar *json, int length); + + Value parse(QJsonParseError *error); + +private: + inline bool eatSpace(); + inline QChar nextToken(); + + Value parseObject(); + Value parseArray(); + bool parseMember(Object *o); + bool parseString(QString *string); + bool parseValue(Value *val); + bool parseNumber(Value *val); + + ExecutionContext *context; + const QChar *head; + const QChar *json; + const QChar *end; + + int nestingLevel; + QJsonParseError::ParseError lastError; +}; + +static const int nestingLimit = 1024; + + +Parser::Parser(ExecutionContext *context, const QChar *json, int length) + : context(context), head(json), json(json), nestingLevel(0), lastError(QJsonParseError::NoError) +{ + end = json + length; +} + + + +/* + +begin-array = ws %x5B ws ; [ left square bracket + +begin-object = ws %x7B ws ; { left curly bracket + +end-array = ws %x5D ws ; ] right square bracket + +end-object = ws %x7D ws ; } right curly bracket + +name-separator = ws %x3A ws ; : colon + +value-separator = ws %x2C ws ; , comma + +Insignificant whitespace is allowed before or after any of the six +structural characters. + +ws = *( + %x20 / ; Space + %x09 / ; Horizontal tab + %x0A / ; Line feed or New line + %x0D ; Carriage return + ) + +*/ + +enum { + Space = 0x20, + Tab = 0x09, + LineFeed = 0x0a, + Return = 0x0d, + BeginArray = 0x5b, + BeginObject = 0x7b, + EndArray = 0x5d, + EndObject = 0x7d, + NameSeparator = 0x3a, + ValueSeparator = 0x2c, + Quote = 0x22 +}; + +bool Parser::eatSpace() +{ + while (json < end) { + if (*json > Space) + break; + if (*json != Space && + *json != Tab && + *json != LineFeed && + *json != Return) + break; + ++json; + } + return (json < end); +} + +QChar Parser::nextToken() +{ + if (!eatSpace()) + return 0; + QChar token = *json++; + switch (token.unicode()) { + case BeginArray: + case BeginObject: + case NameSeparator: + case ValueSeparator: + case EndArray: + case EndObject: + eatSpace(); + case Quote: + break; + default: + token = 0; + break; + } + return token; +} + +/* + JSON-text = object / array +*/ +Value Parser::parse(QJsonParseError *error) +{ +#ifdef PARSER_DEBUG + indent = 0; + qDebug() << ">>>>> parser begin"; +#endif + + eatSpace(); + + Value v; + if (!parseValue(&v)) { +#ifdef PARSER_DEBUG + qDebug() << ">>>>> parser error"; +#endif + if (lastError == QJsonParseError::NoError) + lastError = QJsonParseError::IllegalValue; + error->offset = json - head; + error->error = lastError; + return Value::undefinedValue(); + } + + // some input left... + if (eatSpace()) { + lastError = QJsonParseError::IllegalValue; + error->offset = json - head; + error->error = lastError; + return Value::undefinedValue(); + } + + END; + error->offset = 0; + error->error = QJsonParseError::NoError; + return v; +} + +/* + object = begin-object [ member *( value-separator member ) ] + end-object +*/ + +Value Parser::parseObject() +{ + if (++nestingLevel > nestingLimit) { + lastError = QJsonParseError::DeepNesting; + return Value::undefinedValue(); + } + + BEGIN << "parseObject pos=" << json; + + Object *o = context->engine->newObject(); + Value objectVal = Value::fromObject(o); + + QChar token = nextToken(); + while (token == Quote) { + if (!parseMember(o)) + return Value::undefinedValue(); + token = nextToken(); + if (token != ValueSeparator) + break; + token = nextToken(); + if (token == EndObject) { + lastError = QJsonParseError::MissingObject; + return Value::undefinedValue(); + } + } + + DEBUG << "end token=" << token; + if (token != EndObject) { + lastError = QJsonParseError::UnterminatedObject; + return Value::undefinedValue(); + } + + END; + + --nestingLevel; + return objectVal; +} + +/* + member = string name-separator value +*/ +bool Parser::parseMember(Object *o) +{ + BEGIN << "parseMember"; + if (!o->members) + o->members.reset(new PropertyTable()); + + QString key; + if (!parseString(&key)) + return false; + QChar token = nextToken(); + if (token != NameSeparator) { + lastError = QJsonParseError::MissingNameSeparator; + return false; + } + Value val; + if (!parseValue(&val)) + return false; + + PropertyDescriptor *p = o->members->insert(context->engine->identifier(key)); + p->value = val; + + END; + return true; +} + +/* + array = begin-array [ value *( value-separator value ) ] end-array +*/ +Value Parser::parseArray() +{ + BEGIN << "parseArray"; + Array array; + + if (++nestingLevel > nestingLimit) { + lastError = QJsonParseError::DeepNesting; + return Value::undefinedValue(); + } + + if (!eatSpace()) { + lastError = QJsonParseError::UnterminatedArray; + return Value::undefinedValue(); + } + if (*json == EndArray) { + nextToken(); + } else { + uint index = 0; + while (1) { + Value val; + if (!parseValue(&val)) + return Value::undefinedValue(); + array.set(index, val); + QChar token = nextToken(); + if (token == EndArray) + break; + else if (token != ValueSeparator) { + if (!eatSpace()) + lastError = QJsonParseError::UnterminatedArray; + else + lastError = QJsonParseError::MissingValueSeparator; + return Value::undefinedValue(); + } + ++index; + } + } + + DEBUG << "size =" << array.length(); + END; + + --nestingLevel; + return Value::fromObject(context->engine->newArrayObject(context, array)); +} + +/* +value = false / null / true / object / array / number / string + +*/ + +bool Parser::parseValue(Value *val) +{ + BEGIN << "parse Value" << *json; + + switch ((json++)->unicode()) { + case 'n': + if (end - json < 4) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'u' && + *json++ == 'l' && + *json++ == 'l') { + *val = Value::nullValue(); + DEBUG << "value: null"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case 't': + if (end - json < 4) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'r' && + *json++ == 'u' && + *json++ == 'e') { + *val = Value::fromBoolean(true); + DEBUG << "value: true"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case 'f': + if (end - json < 5) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'a' && + *json++ == 'l' && + *json++ == 's' && + *json++ == 'e') { + *val = Value::fromBoolean(false); + DEBUG << "value: false"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case Quote: { + QString value; + if (!parseString(&value)) + return false; + DEBUG << "value: string"; + END; + *val = Value::fromString(context, value); + return true; + } + case BeginArray: { + *val = parseArray(); + if (val->isUndefined()) + return false; + DEBUG << "value: array"; + END; + return true; + } + case BeginObject: { + *val = parseObject(); + if (val->isUndefined()) + return false; + DEBUG << "value: object"; + END; + return true; + } + case EndArray: + lastError = QJsonParseError::MissingObject; + return false; + default: + --json; + if (!parseNumber(val)) + return false; + DEBUG << "value: number"; + END; + } + + return true; +} + + + + + +/* + number = [ minus ] int [ frac ] [ exp ] + decimal-point = %x2E ; . + digit1-9 = %x31-39 ; 1-9 + e = %x65 / %x45 ; e E + exp = e [ minus / plus ] 1*DIGIT + frac = decimal-point 1*DIGIT + int = zero / ( digit1-9 *DIGIT ) + minus = %x2D ; - + plus = %x2B ; + + zero = %x30 ; 0 + +*/ + +bool Parser::parseNumber(Value *val) +{ + BEGIN << "parseNumber" << *json; + + const QChar *start = json; + bool isInt = true; + + // minus + if (json < end && *json == '-') + ++json; + + // int = zero / ( digit1-9 *DIGIT ) + if (json < end && *json == '0') { + ++json; + } else { + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // frac = decimal-point 1*DIGIT + if (json < end && *json == '.') { + isInt = false; + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // exp = e [ minus / plus ] 1*DIGIT + if (json < end && (*json == 'e' || *json == 'E')) { + isInt = false; + ++json; + if (json < end && (*json == '-' || *json == '+')) + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + QString number(start, json - start); + DEBUG << "numberstring" << number; + + if (isInt) { + bool ok; + int n = number.toInt(&ok); + if (ok && n < (1<<25) && n > -(1<<25)) { + *val = Value::fromInt32(n); + END; + return true; + } + } + + bool ok; + double d; + d = number.toDouble(&ok); + + if (!ok) { + lastError = QJsonParseError::IllegalNumber; + return false; + } + + * val = Value::fromDouble(d); + + END; + return true; +} + +/* + + string = quotation-mark *char quotation-mark + + char = unescaped / + escape ( + %x22 / ; " quotation mark U+0022 + %x5C / ; \ reverse solidus U+005C + %x2F / ; / solidus U+002F + %x62 / ; b backspace U+0008 + %x66 / ; f form feed U+000C + %x6E / ; n line feed U+000A + %x72 / ; r carriage return U+000D + %x74 / ; t tab U+0009 + %x75 4HEXDIG ) ; uXXXX U+XXXX + + escape = %x5C ; \ + + quotation-mark = %x22 ; " + + unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + */ +static inline bool addHexDigit(QChar digit, uint *result) +{ + ushort d = digit.unicode(); + *result <<= 4; + if (d >= '0' && d <= '9') + *result |= (d - '0'); + else if (d >= 'a' && d <= 'f') + *result |= (d - 'a') + 10; + else if (d >= 'A' && d <= 'F') + *result |= (d - 'A') + 10; + else + return false; + return true; +} + +static inline bool scanEscapeSequence(const QChar *&json, const QChar *end, uint *ch) +{ + ++json; + if (json >= end) + return false; + + DEBUG << "scan escape"; + uint escaped = (json++)->unicode(); + switch (escaped) { + case '"': + *ch = '"'; break; + case '\\': + *ch = '\\'; break; + case '/': + *ch = '/'; break; + case 'b': + *ch = 0x8; break; + case 'f': + *ch = 0xc; break; + case 'n': + *ch = 0xa; break; + case 'r': + *ch = 0xd; break; + case 't': + *ch = 0x9; break; + case 'u': { + *ch = 0; + if (json > end - 4) + return false; + for (int i = 0; i < 4; ++i) { + if (!addHexDigit(*json, ch)) + return false; + ++json; + } + if (*ch <= 0x1f) + return false; + return true; + } + default: + return false; + } + return true; +} + + +bool Parser::parseString(QString *string) +{ + BEGIN << "parse string stringPos=" << json; + + while (json < end) { + if (*json == '"') + break; + else if (*json == '\\') { + uint ch = 0; + if (!scanEscapeSequence(json, end, &ch)) { + lastError = QJsonParseError::IllegalEscapeSequence; + return false; + } + qDebug() << "scanEscape" << hex << ch; + if (QChar::requiresSurrogates(ch)) { + *string += QChar::highSurrogate(ch); + *string += QChar::lowSurrogate(ch); + } else { + *string += QChar(ch); + } + } else { + if (json->unicode() <= 0x1f) { + lastError = QJsonParseError::IllegalEscapeSequence; + return false; + } + *string += *json; + ++json; + } + } + ++json; + + if (json > end) { + lastError = QJsonParseError::UnterminatedString; + return false; + } + + END; + return true; +} + + +struct Stringify +{ + ExecutionContext *ctx; + FunctionObject *replacerFunction; + QVector propertyList; + QString gap; + QString indent; + + QStack stack; + + Stringify(ExecutionContext *ctx) : ctx(ctx), replacerFunction(0) {} + + QString Str(const QString &key, Value value); + QString JA(ArrayObject *a); + QString JO(Object *o); + + QString makeMember(const QString &key, Value v); +}; + +static QString quote(const QString &str) +{ + QString product = "\""; + for (int i = 0; i < str.length(); ++i) { + QChar c = str.at(i); + switch (c.unicode()) { + case '"': + product += "\\\""; + break; + case '\\': + product += "\\\\"; + break; + case '\b': + product += "\\b"; + break; + case '\f': + product += "\\f"; + break; + case '\n': + product += "\\n"; + break; + case '\r': + product += "\\r"; + break; + case '\t': + product += "\\t"; + break; + default: + if (c.unicode() <= 0x1f) { + product += "\\u00"; + product += c.unicode() > 0xf ? '1' : '0'; + product += "0123456789abcdef"[c.unicode() & 0xf]; + } else { + product += c; + } + } + } + product += '"'; + return product; +} + +QString Stringify::Str(const QString &key, Value value) +{ + QString result; + + if (Object *o = value.asObject()) { + FunctionObject *toJSON = o->__get__(ctx, ctx->engine->identifier(QStringLiteral("toJSON"))).asFunctionObject(); + if (toJSON) { + Value arg = Value::fromString(ctx, key); + value = toJSON->call(ctx, value, &arg, 1); + } + } + + if (replacerFunction) { + Object *holder = ctx->engine->newObject(); + Value holderValue = Value::fromObject(holder); + holder->__put__(ctx, QString(), value); + Value args[2]; + args[0] = Value::fromString(ctx, key); + args[1] = value; + value = replacerFunction->call(ctx, holderValue, args, 2); + } + + if (Object *o = value.asObject()) { + if (NumberObject *n = o->asNumberObject()) + value = n->value; + else if (StringObject *so = o->asStringObject()) + value = so->value; + else if (BooleanObject *b =o->asBooleanObject()) + value = b->value; + } + + if (value.isNull()) + return QStringLiteral("null"); + if (value.isBoolean()) + return value.booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); + if (value.isString()) + return quote(value.stringValue()->toQString()); + + if (value.isNumber()) { + double d = value.toNumber(ctx); + return std::isfinite(d) ? value.toString(ctx)->toQString() : QStringLiteral("null"); + } + + if (Object *o = value.asObject()) { + if (!o->asFunctionObject()) { + if (o->asArrayObject()) + return JA(static_cast(o)); + else + return JO(o); + } + } + + return QString(); +} + +QString Stringify::makeMember(const QString &key, Value v) +{ + QString strP = Str(key, v); + if (!strP.isEmpty()) { + QString member = quote(key) + ':'; + if (!gap.isEmpty()) + member += ' '; + member += strP; + return member; + } + return QString(); +} + +QString Stringify::JO(Object *o) +{ + if (stack.contains(o)) + ctx->throwTypeError(); + + QString result; + stack.push(o); + QString stepback = indent; + indent += gap; + + QStringList partial; + if (propertyList.isEmpty()) { + ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); + + while (1) { + String *name; + uint index; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + Value v = o->getValueChecked(ctx, pd); + QString key; + if (name) + key = name->toQString(); + else + key = QString::number(index); + QString member = makeMember(key, v); + if (!member.isEmpty()) + partial += member; + } + } else { + for (int i = 0; i < propertyList.size(); ++i) { + bool exists; + Value v = o->__get__(ctx, propertyList.at(i), &exists); + if (!exists) + continue; + QString member = makeMember(propertyList.at(i)->toQString(), v); + if (!member.isEmpty()) + partial += member; + } + } + + if (partial.isEmpty()) { + result = QStringLiteral("{}"); + } else if (gap.isEmpty()) { + result = "{" + partial.join(",") + "}"; + } else { + QString separator = ",\n" + indent; + result = "{\n" + indent + partial.join(separator) + "\n" + stepback + "}"; + } + + indent = stepback; + stack.pop(); + return result; +} + +QString Stringify::JA(ArrayObject *a) +{ + if (stack.contains(a)) + ctx->throwTypeError(); + + QString result; + stack.push(a); + QString stepback = indent; + indent += gap; + + QStringList partial; + uint len = a->array.length(); + for (uint i = 0; i < len; ++i) { + bool exists; + Value v = a->__get__(ctx, i, &exists); + if (!exists) { + partial += QStringLiteral("null"); + continue; + } + QString strP = Str(QString::number(i), v); + if (!strP.isEmpty()) + partial += strP; + else + partial += QStringLiteral("null"); + } + + if (partial.isEmpty()) { + result = QStringLiteral("[]"); + } else if (gap.isEmpty()) { + result = "[" + partial.join(",") + "]"; + } else { + QString separator = ",\n" + indent; + result = "[\n" + indent + partial.join(separator) + "\n" + stepback + "]"; + } + + indent = stepback; + stack.pop(); + return result; +} + + +JsonObject::JsonObject(ExecutionContext *context) + : Object() +{ + type = Type_JSONObject; + prototype = context->engine->objectPrototype; + + defineDefaultProperty(context, QStringLiteral("parse"), method_parse, 2); + defineDefaultProperty(context, QStringLiteral("stringify"), method_stringify, 3); +} + + +Value JsonObject::method_parse(ExecutionContext *ctx) +{ + QString jtext = ctx->argument(0).toString(ctx)->toQString(); + + DEBUG << "parsing source = " << jtext; + Parser parser(ctx, jtext.constData(), jtext.length()); + QJsonParseError error; + Value result = parser.parse(&error); + if (error.error != QJsonParseError::NoError) { + DEBUG << "parse error" << error.errorString(); + ctx->throwSyntaxError(0); + } + + return result; +} + +Value JsonObject::method_stringify(ExecutionContext *ctx) +{ + Stringify stringify(ctx); + + Object *o = ctx->argument(1).asObject(); + if (o) { + stringify.replacerFunction = o->asFunctionObject(); + if (o->isArrayObject()) { + for (uint i = 0; i < o->array.length(); ++i) { + Value v = o->__get__(ctx, i); + if (v.asNumberObject() || v.asStringObject() || v.isNumber()) + v = __qmljs_to_string(v, ctx); + if (v.isString()) { + String *s = v.stringValue(); + if (!stringify.propertyList.contains(s)) + stringify.propertyList.append(s); + } + } + } + } + + Value s = ctx->argument(2); + if (NumberObject *n = s.asNumberObject()) + s = n->value; + else if (StringObject *so = s.asStringObject()) + s = so->value; + + if (s.isNumber()) { + stringify.gap = QString(qMin(10, (int)s.toInteger(ctx)), ' '); + } else if (s.isString()) { + stringify.gap = s.stringValue()->toQString().left(10); + } + + + QString result = stringify.Str(QString(), ctx->argument(0)); + if (result.isEmpty()) + return Value::undefinedValue(); + return Value::fromString(ctx, result); +} + + + +} +} diff --git a/src/v4/qv4jsonobject.h b/src/v4/qv4jsonobject.h new file mode 100644 index 0000000000..7c1df7c40b --- /dev/null +++ b/src/v4/qv4jsonobject.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4JSONOBJECTS_H +#define QV4SJONOBJECTS_H + +#include + +namespace QQmlJS { +namespace VM { + +struct JsonObject : Object { + JsonObject(ExecutionContext *context); + + static Value method_parse(ExecutionContext *ctx); + static Value method_stringify(ExecutionContext *ctx); + +}; + +} +} + +#endif + diff --git a/src/v4/qv4managed.cpp b/src/v4/qv4managed.cpp new file mode 100644 index 0000000000..8156878508 --- /dev/null +++ b/src/v4/qv4managed.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4managed.h" +#include "qv4mm.h" +#include "qv4errorobject.h" + +using namespace QQmlJS::VM; + +Managed::~Managed() +{ + nextFree = 0; + inUse = 0; +} + +void *Managed::operator new(size_t size, MemoryManager *mm) +{ + assert(mm); + + return mm->allocManaged(size); +} + +void Managed::operator delete(void *ptr) +{ + if (!ptr) + return; + + Managed *m = static_cast(ptr); + m->~Managed(); +} + + +QString Managed::className() const +{ + const char *s = 0; + switch (Type(type)) { + case Type_Invalid: + case Type_String: + return QString(); + case Type_Object: + s = "Object"; + break; + case Type_ArrayObject: + s = "Array"; + break; + case Type_FunctionObject: + s = "Function"; + break; + case Type_BooleanObject: + s = "Boolean"; + break; + case Type_NumberObject: + s = "Number"; + break; + case Type_StringObject: + s = "String"; + break; + case Type_DateObject: + s = "Date"; + break; + case Type_RegExpObject: + s = "RegExp"; + break; + case Type_ErrorObject: + switch (static_cast(this)->errorType) { + case ErrorObject::Error: + s = "Error"; + break; + case ErrorObject::EvalError: + s = "EvalError"; + break; + case ErrorObject::RangeError: + s = "RangeError"; + break; + case ErrorObject::ReferenceError: + s = "ReferenceError"; + break; + case ErrorObject::SyntaxError: + s = "SyntaxError"; + break; + case ErrorObject::TypeError: + s = "TypeError"; + break; + case ErrorObject::URIError: + s = "URIError"; + break; + } + break; + case Type_ArgumentsObject: + s = "Arguments"; + break; + case Type_JSONObject: + s = "JSON"; + break; + case Type_MathObject: + s = "Math"; + break; + case Type_ForeachIteratorObject: + s = "__ForeachIterator"; + break; + } + return QString::fromLatin1(s); +} diff --git a/src/v4/qv4managed.h b/src/v4/qv4managed.h new file mode 100644 index 0000000000..dc4d1f2243 --- /dev/null +++ b/src/v4/qv4managed.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_MANAGED_H +#define QMLJS_MANAGED_H + +#include +#include +#include +#include +#include "qv4global.h" + +namespace QQmlJS { + +namespace VM { + +class MemoryManager; +struct String; +struct Object; +struct ObjectPrototype; +struct ExecutionContext; +struct ScriptFunction; + +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct JSONObject; +struct ForeachIteratorObject; + +struct Q_V4_EXPORT Managed +{ +private: + void *operator new(size_t); + Managed(const Managed &other); + void operator = (const Managed &other); + +protected: + Managed() + : markBit(0) + , inUse(1) + , extensible(1) + , isNonStrictArgumentsObject(0) + , isBuiltinFunction(0) + , needsActivation(0) + , usesArgumentsObject(0) + , strictMode(0) + , type(Type_Invalid) + , unused(0) + , stringHash(0) + {} + virtual ~Managed(); + +public: + void *operator new(size_t size, MemoryManager *mm); + void operator delete(void *ptr); + + inline void mark() { + if (markBit) + return; + markBit = 1; + if (type != Type_String) + markObjects(); + } + + enum Type { + Type_Invalid, + Type_String, + Type_Object, + Type_ArrayObject, + Type_FunctionObject, + Type_BooleanObject, + Type_NumberObject, + Type_StringObject, + Type_DateObject, + Type_RegExpObject, + Type_ErrorObject, + Type_ArgumentsObject, + Type_JSONObject, + Type_MathObject, + Type_ForeachIteratorObject + }; + + String *asString() { return reinterpret_cast(this); } + Object *asObject() { return reinterpret_cast(this); } + ArrayObject *asArrayObject() { return type == Type_ArrayObject ? reinterpret_cast(this) : 0; } + FunctionObject *asFunctionObject() { return type == Type_FunctionObject ? reinterpret_cast(this) : 0; } + BooleanObject *asBooleanObject() { return type == Type_BooleanObject ? reinterpret_cast(this) : 0; } + NumberObject *asNumberObject() { return type == Type_NumberObject ? reinterpret_cast(this) : 0; } + StringObject *asStringObject() { return type == Type_StringObject ? reinterpret_cast(this) : 0; } + DateObject *asDateObject() { return type == Type_DateObject ? reinterpret_cast(this) : 0; } + RegExpObject *asRegExpObject() { return type == Type_RegExpObject ? reinterpret_cast(this) : 0; } + ErrorObject *asErrorObject() { return type == Type_ErrorObject ? reinterpret_cast(this) : 0; } + ArgumentsObject *asArgumentsObject() { return type == Type_ArgumentsObject ? reinterpret_cast(this) : 0; } + JSONObject *asJSONObject() { return type == Type_JSONObject ? reinterpret_cast(this) : 0; } + ForeachIteratorObject *asForeachIteratorObject() { return type == Type_ForeachIteratorObject ? reinterpret_cast(this) : 0; } + + bool isArrayObject() const { return type == Type_ArrayObject; } + bool isStringObject() const { return type == Type_StringObject; } + + QString className() const; + +protected: + virtual void markObjects() {} + + union { + Managed *nextFree; + struct { + quintptr markBit : 1; + quintptr inUse : 1; + quintptr extensible : 1; // used by Object + quintptr isNonStrictArgumentsObject : 1; + quintptr isBuiltinFunction : 1; // used by FunctionObject + quintptr needsActivation : 1; // used by FunctionObject + quintptr usesArgumentsObject : 1; // used by FunctionObject + quintptr strictMode : 1; // used by FunctionObject + quintptr type : 8; + quintptr unused : 16; + mutable quintptr stringHash : 32; + }; + }; + +private: + friend class MemoryManager; + friend struct ExecutionContext; +}; + +} +} + +#endif diff --git a/src/v4/qv4mathobject.cpp b/src/v4/qv4mathobject.cpp new file mode 100644 index 0000000000..e40faba861 --- /dev/null +++ b/src/v4/qv4mathobject.cpp @@ -0,0 +1,299 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4mathobject.h" + +#include +#include +#include + +using namespace QQmlJS::VM; + +static const double qt_PI = 2.0 * ::asin(1.0); + +MathObject::MathObject(ExecutionContext *ctx) +{ + type = Type_MathObject; + + defineReadonlyProperty(ctx->engine, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LN10"), Value::fromDouble(::log(10.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LOG2E"), Value::fromDouble(1.0/::log(2.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LOG10E"), Value::fromDouble(1.0/::log(10.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("PI"), Value::fromDouble(qt_PI)); + defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT1_2"), Value::fromDouble(::sqrt(0.5))); + defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT2"), Value::fromDouble(::sqrt(2.0))); + + defineDefaultProperty(ctx, QStringLiteral("abs"), method_abs, 1); + defineDefaultProperty(ctx, QStringLiteral("acos"), method_acos, 1); + defineDefaultProperty(ctx, QStringLiteral("asin"), method_asin, 0); + defineDefaultProperty(ctx, QStringLiteral("atan"), method_atan, 1); + defineDefaultProperty(ctx, QStringLiteral("atan2"), method_atan2, 2); + defineDefaultProperty(ctx, QStringLiteral("ceil"), method_ceil, 1); + defineDefaultProperty(ctx, QStringLiteral("cos"), method_cos, 1); + defineDefaultProperty(ctx, QStringLiteral("exp"), method_exp, 1); + defineDefaultProperty(ctx, QStringLiteral("floor"), method_floor, 1); + defineDefaultProperty(ctx, QStringLiteral("log"), method_log, 1); + defineDefaultProperty(ctx, QStringLiteral("max"), method_max, 2); + defineDefaultProperty(ctx, QStringLiteral("min"), method_min, 2); + defineDefaultProperty(ctx, QStringLiteral("pow"), method_pow, 2); + defineDefaultProperty(ctx, QStringLiteral("random"), method_random, 0); + defineDefaultProperty(ctx, QStringLiteral("round"), method_round, 1); + defineDefaultProperty(ctx, QStringLiteral("sin"), method_sin, 1); + defineDefaultProperty(ctx, QStringLiteral("sqrt"), method_sqrt, 1); + defineDefaultProperty(ctx, QStringLiteral("tan"), method_tan, 1); +} + +/* copies the sign from y to x and returns the result */ +static double copySign(double x, double y) +{ + uchar *xch = (uchar *)&x; + uchar *ych = (uchar *)&y; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + xch[0] = (xch[0] & 0x7f) | (ych[0] & 0x80); + else + xch[7] = (xch[7] & 0x7f) | (ych[7] & 0x80); + return x; +} + +Value MathObject::method_abs(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v == 0) // 0 | -0 + return Value::fromDouble(0); + + return Value::fromDouble(v < 0 ? -v : v); +} + +Value MathObject::method_acos(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v > 1) + return Value::fromDouble(qSNaN()); + + return Value::fromDouble(::acos(v)); +} + +Value MathObject::method_asin(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v > 1) + return Value::fromDouble(qSNaN()); + else + return Value::fromDouble(::asin(v)); +} + +Value MathObject::method_atan(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v == 0.0) + return Value::fromDouble(v); + else + return Value::fromDouble(::atan(v)); +} + +Value MathObject::method_atan2(ExecutionContext *ctx) +{ + double v1 = ctx->argument(0).toNumber(ctx); + double v2 = ctx->argument(1).toNumber(ctx); + if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) { + return Value::fromDouble(copySign(0, -1.0)); + } + if ((v1 == 0.0) && (v2 == 0.0)) { + if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) { + return Value::fromDouble(qt_PI); + } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) { + return Value::fromDouble(-qt_PI); + } + } + return Value::fromDouble(::atan2(v1, v2)); +} + +Value MathObject::method_ceil(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v < 0.0 && v > -1.0) + return Value::fromDouble(copySign(0, -1.0)); + else + return Value::fromDouble(::ceil(v)); +} + +Value MathObject::method_cos(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + return Value::fromDouble(::cos(v)); +} + +Value MathObject::method_exp(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (qIsInf(v)) { + if (copySign(1.0, v) == -1.0) + return Value::fromDouble(0); + else + return Value::fromDouble(qInf()); + } else { + return Value::fromDouble(::exp(v)); + } +} + +Value MathObject::method_floor(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + return Value::fromDouble(::floor(v)); +} + +Value MathObject::method_log(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v < 0) + return Value::fromDouble(qSNaN()); + else + return Value::fromDouble(::log(v)); +} + +Value MathObject::method_max(ExecutionContext *ctx) +{ + double mx = -qInf(); + for (unsigned i = 0; i < ctx->argumentCount; ++i) { + double x = ctx->argument(i).toNumber(ctx); + if (x > mx || std::isnan(x)) + mx = x; + } + return Value::fromDouble(mx); +} + +Value MathObject::method_min(ExecutionContext *ctx) +{ + double mx = qInf(); + for (unsigned i = 0; i < ctx->argumentCount; ++i) { + double x = ctx->argument(i).toNumber(ctx); + if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) + || (x < mx) || std::isnan(x)) { + mx = x; + } + } + return Value::fromDouble(mx); +} + +Value MathObject::method_pow(ExecutionContext *parentCtx, Value, Value *argv, int argc) +{ + double x = argc > 0 ? argv[0].toNumber(parentCtx) : qSNaN(); + double y = argc > 1 ? argv[1].toNumber(parentCtx) : qSNaN(); + + if (std::isnan(y)) + return Value::fromDouble(qSNaN()); + + if (y == 0) { + return Value::fromDouble(1); + } else if (((x == 1) || (x == -1)) && std::isinf(y)) { + return Value::fromDouble(qSNaN()); + } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { + return Value::fromDouble(qInf()); + } else if ((x == 0) && copySign(1.0, x) == -1.0) { + if (y < 0) { + if (::fmod(-y, 2.0) == 1.0) + return Value::fromDouble(-qInf()); + else + return Value::fromDouble(qInf()); + } else if (y > 0) { + if (::fmod(y, 2.0) == 1.0) + return Value::fromDouble(copySign(0, -1.0)); + else + return Value::fromDouble(0); + } + } + +#ifdef Q_OS_AIX + else if (qIsInf(x) && copySign(1.0, x) == -1.0) { + if (y > 0) { + if (::fmod(y, 2.0) == 1.0) + return Value::fromDouble(-qInf()); + else + return Value::fromDouble(qInf()); + } else if (y < 0) { + if (::fmod(-y, 2.0) == 1.0) + return Value::fromDouble(copySign(0, -1.0)); + else + return Value::fromDouble(0); + } + } +#endif + else { + return Value::fromDouble(::pow(x, y)); + } + // ### + return Value::fromDouble(qSNaN()); +} + +Value MathObject::method_random(ExecutionContext */*ctx*/) +{ + return Value::fromDouble(qrand() / (double) RAND_MAX); +} + +Value MathObject::method_round(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + v = copySign(::floor(v + 0.5), v); + return Value::fromDouble(v); +} + +Value MathObject::method_sin(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + return Value::fromDouble(::sin(v)); +} + +Value MathObject::method_sqrt(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + return Value::fromDouble(::sqrt(v)); +} + +Value MathObject::method_tan(ExecutionContext *ctx) +{ + double v = ctx->argument(0).toNumber(ctx); + if (v == 0.0) + return Value::fromDouble(v); + else + return Value::fromDouble(::tan(v)); +} + diff --git a/src/v4/qv4mathobject.h b/src/v4/qv4mathobject.h new file mode 100644 index 0000000000..c8428d2942 --- /dev/null +++ b/src/v4/qv4mathobject.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4MATHOBJECT_H +#define QV$MATHOBJECT_H + +#include "qv4object.h" + +namespace QQmlJS { + +namespace VM { + +struct MathObject: Object +{ + MathObject(ExecutionContext *ctx); + virtual QString className() { return QStringLiteral("Math"); } + + static Value method_abs(ExecutionContext *ctx); + static Value method_acos(ExecutionContext *ctx); + static Value method_asin(ExecutionContext *ctx); + static Value method_atan(ExecutionContext *ctx); + static Value method_atan2(ExecutionContext *ctx); + static Value method_ceil(ExecutionContext *ctx); + static Value method_cos(ExecutionContext *ctx); + static Value method_exp(ExecutionContext *ctx); + static Value method_floor(ExecutionContext *ctx); + static Value method_log(ExecutionContext *ctx); + static Value method_max(ExecutionContext *ctx); + static Value method_min(ExecutionContext *ctx); + static Value method_pow(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_random(ExecutionContext *ctx); + static Value method_round(ExecutionContext *ctx); + static Value method_sin(ExecutionContext *ctx); + static Value method_sqrt(ExecutionContext *ctx); + static Value method_tan(ExecutionContext *ctx); +}; + +} // namespace VM +} // namespace QQmlJS + +#endif // QMLJS_OBJECTS_H diff --git a/src/v4/qv4mm.cpp b/src/v4/qv4mm.cpp new file mode 100644 index 0000000000..c77ec1eaba --- /dev/null +++ b/src/v4/qv4mm.cpp @@ -0,0 +1,396 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qmljs_engine.h" +#include "qv4object.h" +#include "qv4objectproto.h" +#include "qv4mm.h" +#include "PageAllocation.h" +#include "StdLibExtras.h" + +#include +#include +#include +#include + +#include +#include +#include + +using namespace QQmlJS::VM; +using namespace WTF; + +static const std::size_t CHUNK_SIZE = 65536; + +struct MemoryManager::Data +{ + bool enableGC; + bool gcBlocked; + bool scribble; + bool aggressiveGC; + ExecutionEngine *engine; + + enum { MaxItemSize = 256 }; + Managed *smallItems[MaxItemSize/16]; + uint nChunks[MaxItemSize/16]; + struct Chunk { + PageAllocation memory; + int chunkSize; + }; + + QVector heapChunks; + QHash protectedObject; + + // statistics: +#ifdef DETAILED_MM_STATS + QVector allocSizeCounters; +#endif // DETAILED_MM_STATS + + Data(bool enableGC) + : enableGC(enableGC) + , gcBlocked(false) + , engine(0) + { + memset(smallItems, 0, sizeof(smallItems)); + memset(nChunks, 0, sizeof(nChunks)); + scribble = qgetenv("MM_NO_SCRIBBLE").isEmpty(); + aggressiveGC = !qgetenv("MM_AGGRESSIVE_GC").isEmpty(); + } + + ~Data() + { + for (QVector::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) + i->memory.deallocate(); + } +}; + +namespace QQmlJS { namespace VM { + +bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::Chunk &b) +{ + return a.memory.base() < b.memory.base(); +} + +} } // namespace QQmlJS::VM + +MemoryManager::MemoryManager() + : m_d(new Data(true)) +{ + setEnableGC(true); +} + +Managed *MemoryManager::alloc(std::size_t size) +{ + if (m_d->aggressiveGC) + runGC(); +#ifdef DETAILED_MM_STATS + willAllocate(size); +#endif // DETAILED_MM_STATS + + assert(size >= 16); + assert(size % 16 == 0); + + size_t pos = size >> 4; + + // fits into a small bucket + assert(size < MemoryManager::Data::MaxItemSize); + + Managed *m = m_d->smallItems[pos]; + if (m) + goto found; + + // try to free up space, otherwise allocate + if (!m_d->aggressiveGC) { + runGC(); + m = m_d->smallItems[pos]; + if (m) + goto found; + } + + // no free item available, allocate a new chunk + { + // allocate larger chunks at a time to avoid excessive GC, but cap at 64M chunks + uint shift = ++m_d->nChunks[pos]; + if (shift > 10) + shift = 10; + std::size_t allocSize = std::max(size, CHUNK_SIZE*(1 << shift)); + allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); + Data::Chunk allocation; + allocation.memory = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); + allocation.chunkSize = size; + m_d->heapChunks.append(allocation); + qSort(m_d->heapChunks); + char *chunk = (char *)allocation.memory.base(); + char *end = chunk + allocation.memory.size() - size; + memset(chunk, 0, allocation.memory.size()); + Managed **last = &m_d->smallItems[pos]; + while (chunk <= end) { + Managed *o = reinterpret_cast(chunk); + o->inUse = 0; + o->markBit = 0; + *last = o; + last = &o->nextFree; + chunk += size; + } + *last = 0; + m = m_d->smallItems[pos]; + } + + found: + m_d->smallItems[pos] = m->nextFree; + return m; +} + +void MemoryManager::scribble(Managed *obj, int c, int size) const +{ + if (m_d->scribble) + ::memset((void *)(obj + 1), c, size - sizeof(Managed)); +} + +void MemoryManager::mark() +{ + m_d->engine->markObjects(); + + for (ExecutionContext *ctxt = engine()->current; ctxt; ctxt = ctxt->parent) + ctxt->mark(); + + for (QHash::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it) + it.key()->mark(); + + collectFromStack(); + + return; +} + +std::size_t MemoryManager::sweep() +{ + std::size_t freedCount = 0; + + for (QVector::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) + freedCount += sweep(reinterpret_cast(i->memory.base()), i->memory.size(), i->chunkSize); + + return freedCount; +} + +std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t size) +{ +// qDebug("chunkStart @ %p, size=%x, pos=%x (%x)", chunkStart, size, size>>4, m_d->smallItems[size >> 4]); + std::size_t freedCount = 0; + + Managed **f = &m_d->smallItems[size >> 4]; + + for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize - size; chunk <= chunkEnd; ) { + Managed *m = reinterpret_cast(chunk); +// qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s", +// chunk, m->size, (m->inUse ? "yes" : "no"), (m->markBit ? "true" : "false")); + + assert((intptr_t) chunk % 16 == 0); + + chunk = chunk + size; + if (m->inUse) { + if (m->markBit) { + m->markBit = 0; + } else { +// qDebug() << "-- collecting it." << m << *f << &m->nextFree; + m->~Managed(); + + m->nextFree = *f; + f = &m->nextFree; + //scribble(m, 0x99, size); + ++freedCount; + } + } + } + + return freedCount; +} + +bool MemoryManager::isGCBlocked() const +{ + return m_d->gcBlocked; +} + +void MemoryManager::setGCBlocked(bool blockGC) +{ + m_d->gcBlocked = blockGC; +} + +void MemoryManager::runGC() +{ + if (!m_d->enableGC || m_d->gcBlocked) { +// qDebug() << "Not running GC."; + return; + } + +// QTime t; t.start(); + +// qDebug() << ">>>>>>>>runGC"; + + mark(); +// std::cerr << "GC: marked " << marks +// << " objects in " << t.elapsed() +// << "ms" << std::endl; + +// t.restart(); + /*std::size_t freedCount =*/ sweep(); +// std::cerr << "GC: sweep freed " << freedCount +// << " objects in " << t.elapsed() +// << "ms" << std::endl; +} + +void MemoryManager::setEnableGC(bool enableGC) +{ + m_d->enableGC = enableGC; +} + +MemoryManager::~MemoryManager() +{ + sweep(); +} + +void MemoryManager::protect(Managed *m) +{ + ++m_d->protectedObject[m]; +} + +void MemoryManager::unprotect(Managed *m) +{ + if (!--m_d->protectedObject[m]) + m_d->protectedObject.remove(m); +} + +static inline void add(QVector &values, const Value &v) +{ + if (Object *o = v.asObject()) + values.append(o); +} + +void MemoryManager::setExecutionEngine(ExecutionEngine *engine) +{ + m_d->engine = engine; +} + +void MemoryManager::dumpStats() const +{ +#ifdef DETAILED_MM_STATS + std::cerr << "=================" << std::endl; + std::cerr << "Allocation stats:" << std::endl; + std::cerr << "Requests for each chunk size:" << std::endl; + for (int i = 0; i < m_d->allocSizeCounters.size(); ++i) { + if (unsigned count = m_d->allocSizeCounters[i]) { + std::cerr << "\t" << (i << 4) << " bytes chunks: " << count << std::endl; + } + } +#endif // DETAILED_MM_STATS +} + +ExecutionEngine *MemoryManager::engine() const +{ + return m_d->engine; +} + +#ifdef DETAILED_MM_STATS +void MemoryManager::willAllocate(std::size_t size) +{ + unsigned alignedSize = (size + 15) >> 4; + QVector &counters = m_d->allocSizeCounters; + if ((unsigned) counters.size() < alignedSize + 1) + counters.resize(alignedSize + 1); + counters[alignedSize]++; +} + +#endif // DETAILED_MM_STATS + +void MemoryManager::collectFromStack() const +{ + if (!m_d->heapChunks.count()) + return; + + quintptr valueOnStack = 0; + +#if USE(PTHREADS) +#if OS(DARWIN) + void* stackTop = 0; + stackTop = pthread_get_stackaddr_np(pthread_self()); + quintptr *top = static_cast(stackTop); +#else + void* stackBottom = 0; + pthread_attr_t attr; + pthread_getattr_np(pthread_self(), &attr); + size_t stackSize = 0; + pthread_attr_getstack(&attr, &stackBottom, &stackSize); + pthread_attr_destroy(&attr); + + quintptr *top = static_cast(stackBottom) + stackSize/sizeof(quintptr); +#endif +#endif +// qDebug() << "stack:" << hex << stackTop << stackSize << (stackTop + stackSize); + + quintptr *current = (&valueOnStack) + 1; +// qDebug() << "collectFromStack" << top << current << &valueOnStack; + + char** heapChunkBoundaries = (char**)alloca(m_d->heapChunks.count() * 2 * sizeof(char*)); + char** heapChunkBoundariesEnd = heapChunkBoundaries + 2 * m_d->heapChunks.count(); + int i = 0; + for (QVector::Iterator it = m_d->heapChunks.begin(), end = + m_d->heapChunks.end(); it != end; ++it) { + heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()); + heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()) + it->memory.size(); + } + + for (; current < top; ++current) { + char* genericPtr = +#if CPU(X86_64) + reinterpret_cast((*current) & ~(quint64(Value::Type_Mask) << Value::Tag_Shift)); +#else + reinterpret_cast(*current); +#endif + + if (genericPtr < *heapChunkBoundaries || genericPtr >= *(heapChunkBoundariesEnd - 1)) + continue; + int index = qLowerBound(heapChunkBoundaries, heapChunkBoundariesEnd, genericPtr) - heapChunkBoundaries; + // An odd index means the pointer is _before_ the end of a heap chunk and therefore valid. + if (index & 1) { + int size = m_d->heapChunks.at(index >> 1).chunkSize; + Managed *m = reinterpret_cast(genericPtr); +// qDebug() << " inside" << size << m; + + if (((quintptr)m - (quintptr)heapChunkBoundaries[index-1]) % size) + // wrongly aligned value, skip it + continue; + + if (!m->inUse) + // Skip pointers to already freed objects, they are bogus as well + continue; + + m->mark(); +// qDebug() << " marking"; + } + } +} diff --git a/src/v4/qv4mm.h b/src/v4/qv4mm.h new file mode 100644 index 0000000000..6f747f53d2 --- /dev/null +++ b/src/v4/qv4mm.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QV4GC_H +#define QV4GC_H + +#include "qv4global.h" +#include "qv4object.h" + +#include + +#define DETAILED_MM_STATS + +namespace QQmlJS { +namespace VM { + +struct Managed; + +class Q_V4_EXPORT MemoryManager +{ + MemoryManager(const MemoryManager &); + MemoryManager &operator=(const MemoryManager&); + +public: + struct Data; + + class GCBlocker + { + public: + GCBlocker(MemoryManager *mm) + : mm(mm) + , wasBlocked(mm->isGCBlocked()) + { + mm->setGCBlocked(true); + } + + ~GCBlocker() + { + mm->setGCBlocked(wasBlocked); + } + + private: + MemoryManager *mm; + bool wasBlocked; + }; + +public: + MemoryManager(); + ~MemoryManager(); + + void protect(Managed *m); + void unprotect(Managed *m); + + // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries). + // Note: all occurances of "16" in alloc/dealloc are also due to the alignment. + static inline std::size_t align(std::size_t size) + { return (size + 15) & ~0xf; } + + inline Managed *allocManaged(std::size_t size) + { + size = align(size); + Managed *o = alloc(size); + return o; + } + + bool isGCBlocked() const; + void setGCBlocked(bool blockGC); + void runGC(); + + void setEnableGC(bool enableGC); + void setExecutionEngine(ExecutionEngine *engine); + + void dumpStats() const; + +protected: + /// expects size to be aligned + // TODO: try to inline + Managed *alloc(std::size_t size); + + void scribble(Managed *obj, int c, int size) const; + + ExecutionEngine *engine() const; + +#ifdef DETAILED_MM_STATS + void willAllocate(std::size_t size); +#endif // DETAILED_MM_STATS + +private: + void collectFromStack() const; + void mark(); + std::size_t sweep(); + std::size_t sweep(char *chunkStart, std::size_t chunkSize, size_t size); + +protected: + QScopedPointer m_d; +}; + + +} // namespace VM +} // namespace QQmlJS + +#endif // QV4GC_H diff --git a/src/v4/qv4numberobject.cpp b/src/v4/qv4numberobject.cpp new file mode 100644 index 0000000000..08711a8187 --- /dev/null +++ b/src/v4/qv4numberobject.cpp @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4numberobject.h" +#include +#include +#include +#include + + +using namespace QQmlJS::VM; + + +NumberCtor::NumberCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value NumberCtor::construct(ExecutionContext *ctx) +{ + double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; + return Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(d))); +} + +Value NumberCtor::call(ExecutionContext *ctx) +{ + double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; + return Value::fromDouble(d); +} + +void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308)); + +#ifdef __INTEL_COMPILER +# pragma warning( push ) +# pragma warning(disable: 239) +#endif + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324)); +#ifdef __INTEL_COMPILER +# pragma warning( pop ) +#endif + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); + defineDefaultProperty(ctx, QStringLiteral("toFixed"), method_toFixed, 1); + defineDefaultProperty(ctx, QStringLiteral("toExponential"), method_toExponential); + defineDefaultProperty(ctx, QStringLiteral("toPrecision"), method_toPrecision); +} + +Value NumberPrototype::method_toString(ExecutionContext *ctx) +{ + double num; + if (ctx->thisObject.isNumber()) { + num = ctx->thisObject.asDouble(); + } else { + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + num = thisObject->value.asDouble(); + } + + Value arg = ctx->argument(0); + if (!arg.isUndefined()) { + int radix = arg.toInt32(ctx); + if (radix < 2 || radix > 36) { + ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") + .arg(radix)); + return Value::undefinedValue(); + } + + if (std::isnan(num)) { + return Value::fromString(ctx, QStringLiteral("NaN")); + } else if (qIsInf(num)) { + return Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); + } + + if (radix != 10) { + QString str; + bool negative = false; + if (num < 0) { + negative = true; + num = -num; + } + double frac = num - ::floor(num); + num = Value::toInteger(num); + do { + char c = (char)::fmod(num, radix); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.prepend(QLatin1Char(c)); + num = ::floor(num / radix); + } while (num != 0); + if (frac != 0) { + str.append(QLatin1Char('.')); + do { + frac = frac * radix; + char c = (char)::floor(frac); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.append(QLatin1Char(c)); + frac = frac - ::floor(frac); + } while (frac != 0); + } + if (negative) + str.prepend(QLatin1Char('-')); + return Value::fromString(ctx, str); + } + } + + String *str = Value::fromDouble(num).toString(ctx); + return Value::fromString(str); +} + +Value NumberPrototype::method_toLocaleString(ExecutionContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + String *str = thisObject->value.toString(ctx); + return Value::fromString(str); +} + +Value NumberPrototype::method_valueOf(ExecutionContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + return thisObject->value; +} + +Value NumberPrototype::method_toFixed(ExecutionContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + double fdigits = 0; + + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(ctx); + + if (std::isnan(fdigits)) + fdigits = 0; + + if (fdigits < 0 || fdigits > 20) + ctx->throwRangeError(ctx->thisObject); + + double v = thisObject->value.asDouble(); + QString str; + if (std::isnan(v)) + str = QString::fromLatin1("NaN"); + else if (qIsInf(v)) + str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); + else + str = QString::number(v, 'f', int (fdigits)); + return Value::fromString(ctx, str); +} + +Value NumberPrototype::method_toExponential(ExecutionContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + double fdigits = 0; + + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(ctx); + + QString z = QString::number(thisObject->value.asDouble(), 'e', int (fdigits)); + return Value::fromString(ctx, z); +} + +Value NumberPrototype::method_toPrecision(ExecutionContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + double fdigits = 0; + + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(ctx); + + return Value::fromString(ctx, QString::number(thisObject->value.asDouble(), 'g', int (fdigits))); +} diff --git a/src/v4/qv4numberobject.h b/src/v4/qv4numberobject.h new file mode 100644 index 0000000000..85f9e1ff2b --- /dev/null +++ b/src/v4/qv4numberobject.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4NUMBEROBJECT_H +#define QV4NUMBEROBJECT_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include + +namespace QQmlJS { +namespace VM { + +struct NumberCtor: FunctionObject +{ + NumberCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct NumberPrototype: NumberObject +{ + NumberPrototype(): NumberObject(Value::fromDouble(0)) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(ExecutionContext *ctx); + static Value method_toLocaleString(ExecutionContext *ctx); + static Value method_valueOf(ExecutionContext *ctx); + static Value method_toFixed(ExecutionContext *ctx); + static Value method_toExponential(ExecutionContext *ctx); + static Value method_toPrecision(ExecutionContext *ctx); +}; + + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp new file mode 100644 index 0000000000..dadf8479c2 --- /dev/null +++ b/src/v4/qv4object.cpp @@ -0,0 +1,674 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4object.h" +#include "qv4ir_p.h" +#include "qv4isel_p.h" +#include "qv4objectproto.h" +#include "qv4stringobject.h" +#include "qv4argumentsobject.h" +#include "qv4mm.h" + +#include +#include +#include +#include +#include +#include +#include "private/qlocale_tools_p.h" + +#include +#include +#include +#include +#include +#include + +using namespace QQmlJS::VM; + + +// +// Object +// +Object::~Object() +{ +} + +void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &value) +{ + __put__(ctx, ctx->engine->identifier(name), value); +} + +Value Object::getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const +{ + if (p->isData()) + return p->value; + if (!p->get) + return Value::undefinedValue(); + + return p->get->call(ctx, Value::fromObject(const_cast(this)), 0, 0); +} + +Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const +{ + if (!p || p->type == PropertyDescriptor::Generic) + return Value::undefinedValue(); + return getValue(ctx, p); +} + +Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const +{ + *exists = p && p->type != PropertyDescriptor::Generic; + if (!*exists) + return Value::undefinedValue(); + return getValue(ctx, p); +} + +void Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx) +{ + bool hasProperty = false; + Value v = __get__(ctx, name, &hasProperty); + Value result = op(v, rhs, ctx); + __put__(ctx, name, result); +} + +void Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) +{ + uint idx = index.asArrayIndex(); + if (idx < UINT_MAX) { + bool hasProperty = false; + Value v = __get__(ctx, idx, &hasProperty); + v = op(v, rhs, ctx); + __put__(ctx, idx, v); + return; + } + String *name = index.toString(ctx); + assert(name); + inplaceBinOp(rhs, name, op, ctx); +} + +void Object::defineDefaultProperty(String *name, Value value) +{ + if (!members) + members.reset(new PropertyTable()); + PropertyDescriptor *pd = members->insert(name); + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Enabled; + pd->value = value; +} + +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value value) +{ + defineDefaultProperty(context->engine->identifier(name), value); +} + +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int argumentCount) +{ + Q_UNUSED(argumentCount); + String *s = context->engine->identifier(name); + FunctionObject* function = context->engine->newBuiltinFunction(context, s, code); + function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); + defineDefaultProperty(s, Value::fromObject(function)); +} + +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *, Value, Value *, int), int argumentCount) +{ + Q_UNUSED(argumentCount); + String *s = context->engine->identifier(name); + FunctionObject* function = context->engine->newBuiltinFunction(context, s, code); + function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); + defineDefaultProperty(s, Value::fromObject(function)); +} + +void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value) +{ + defineReadonlyProperty(engine->identifier(name), value); +} + +void Object::defineReadonlyProperty(String *name, Value value) +{ + if (!members) + members.reset(new PropertyTable()); + PropertyDescriptor *pd = members->insert(name); + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Disabled; + pd->enumberable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Disabled; + pd->value = value; +} + +void Object::markObjects() +{ + if (prototype) + prototype->mark(); + + if (members) { + for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) { + if (!(*it)) + continue; + (*it)->name->mark(); + PropertyDescriptor &pd = (*it)->descriptor; + if (pd.isData()) { + if (Managed *m = pd.value.asManaged()) + m->mark(); + } else if (pd.isAccessor()) { + if (pd.get) + pd.get->mark(); + if (pd.set) + pd.set->mark(); + } + } + } + array.markObjects(); +} + +// Section 8.12.1 +PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *name) +{ + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __getOwnProperty__(ctx, idx); + + if (members) + return members->find(name); + return 0; +} + +PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, uint index) +{ + PropertyDescriptor *p = array.at(index); + if(p && p->type != PropertyDescriptor::Generic) + return p; + if (isStringObject()) + return static_cast(this)->getIndex(ctx, index); + + return 0; +} + +// Section 8.12.2 +PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name) +{ + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __getPropertyDescriptor__(ctx, idx); + + + Object *o = this; + while (o) { + if (o->members) { + if (PropertyDescriptor *p = o->members->find(name)) + return p; + } + o = o->prototype; + } + return 0; +} + +PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, uint index) +{ + Object *o = this; + while (o) { + PropertyDescriptor *p = o->array.at(index); + if(p && p->type != PropertyDescriptor::Generic) + return p; + if (o->isStringObject()) { + p = static_cast(o)->getIndex(ctx, index); + if (p) + return p; + } + o = o->prototype; + } + return 0; +} + +// Section 8.12.3 +Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) +{ + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __get__(ctx, idx, hasProperty); + + if (name->isEqualTo(ctx->engine->id___proto__)) { + if (hasProperty) + *hasProperty = true; + return Value::fromObject(prototype); + } + + if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name)) { + if (hasProperty) + *hasProperty = true; + return getValue(ctx, p); + } + + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); +} + +Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) +{ + const PropertyDescriptor *p = __getPropertyDescriptor__(ctx, index); + if (p && p->type != PropertyDescriptor::Generic) { + if (hasProperty) + *hasProperty = true; + return getValue(ctx, p); + } + + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); +} + + +// Section 8.12.5 +void Object::__put__(ExecutionContext *ctx, String *name, Value value) +{ + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __put__(ctx, idx, value); + + PropertyDescriptor *pd = __getOwnProperty__(ctx, name); + // clause 1 + if (pd) { + if (pd->isAccessor()) { + if (pd->set) + goto cont; + goto reject; + } else if (!pd->isWritable()) + goto reject; + else if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { + bool ok; + uint l = value.asArrayLength(ctx, &ok); + if (!ok) + ctx->throwRangeError(value); + ok = array.setLength(l); + if (!ok) + goto reject; + } else { + pd->value = value; + } + return; + } else if (!prototype) { + if (!extensible) + goto reject; + } else { + if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, name)) { + if (p->isAccessor()) { + if (p->set) + goto cont; + goto reject; + } + if (!extensible) + goto reject; + if (!p->isWritable()) + goto reject; + } else { + if (!extensible) + goto reject; + } + } + + cont: + + if (!members) + members.reset(new PropertyTable()); + + + // clause 4 + if (!pd && prototype) + pd = prototype->__getPropertyDescriptor__(ctx, name); + + // Clause 5 + if (pd && pd->isAccessor()) { + assert(pd->set != 0); + + Value args[1]; + args[0] = value; + pd->set->call(ctx, Value::fromObject(this), args, 1); + return; + } + + { + PropertyDescriptor *p = members->insert(name); + p->type = PropertyDescriptor::Data; + p->value = value; + p->configurable = PropertyDescriptor::Enabled; + p->enumberable = PropertyDescriptor::Enabled; + p->writable = PropertyDescriptor::Enabled; + return; + } + + reject: + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); +} + +void Object::__put__(ExecutionContext *ctx, uint index, Value value) +{ + PropertyDescriptor *pd = __getOwnProperty__(ctx, index); + // clause 1 + if (pd) { + if (pd->isAccessor()) { + if (pd->set) + goto cont; + goto reject; + } else if (!pd->isWritable()) + goto reject; + else + pd->value = value; + return; + } else if (!prototype) { + if (!extensible) + goto reject; + } else { + if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, index)) { + if (p->isAccessor()) { + if (p->set) + goto cont; + goto reject; + } + if (!extensible) + goto reject; + if (!p->isWritable()) + goto reject; + } else { + if (!extensible) + goto reject; + } + } + + cont: + + // clause 4 + if (!pd && prototype) + pd = prototype->__getPropertyDescriptor__(ctx, index); + + // Clause 5 + if (pd && pd->isAccessor()) { + assert(pd->set != 0); + + Value args[1]; + args[0] = value; + pd->set->call(ctx, Value::fromObject(this), args, 1); + return; + } + + array.set(index, value); + return; + + reject: + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); +} + +// Section 8.12.6 +bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const +{ + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __hasProperty__(ctx, idx); + + if (members && members->find(name) != 0) + return true; + + return prototype ? prototype->__hasProperty__(ctx, name) : false; +} + +bool Object::__hasProperty__(const ExecutionContext *ctx, uint index) const +{ + const PropertyDescriptor *p = array.at(index); + if (p && p->type != PropertyDescriptor::Generic) + return true; + + return prototype ? prototype->__hasProperty__(ctx, index) : false; +} + +// Section 8.12.7 +bool Object::__delete__(ExecutionContext *ctx, String *name) +{ + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __delete__(ctx, idx); + + if (members) { + if (PropertyTableEntry *entry = members->findEntry(name)) { + if (entry->descriptor.isConfigurable()) { + members->remove(entry); + return true; + } + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; + } + } + return true; +} + +bool Object::__delete__(ExecutionContext *ctx, uint index) +{ + if (array.deleteIndex(index)) + return true; + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; +} + +// Section 8.12.9 +bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc) +{ + uint idx = name->asArrayIndex(); + if (idx != String::InvalidArrayIndex) + return __defineOwnProperty__(ctx, idx, desc); + + PropertyDescriptor *current; + + if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { + PropertyDescriptor *lp = array.getLengthProperty(); + if (desc->isEmpty() || desc->isSubset(lp)) + return true; + if (!lp->isWritable() || desc->type == PropertyDescriptor::Accessor || desc->isConfigurable() || desc->isEnumerable()) + goto reject; + bool succeeded = true; + if (desc->type == PropertyDescriptor::Data) { + bool ok; + uint l = desc->value.asArrayLength(ctx, &ok); + if (!ok) + ctx->throwRangeError(desc->value); + succeeded = array.setLength(l); + } + if (desc->writable == PropertyDescriptor::Disabled) + lp->writable = PropertyDescriptor::Disabled; + if (!succeeded) + goto reject; + return true; + } + + if (!members) + members.reset(new PropertyTable()); + + // Clause 1 + current = __getOwnProperty__(ctx, name); + if (!current) { + // clause 3 + if (!extensible) + goto reject; + // clause 4 + PropertyDescriptor *pd = members->insert(name); + *pd = *desc; + pd->fullyPopulated(); + return true; + } + + return __defineOwnProperty__(ctx, current, desc); +reject: + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; +} + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc) +{ + PropertyDescriptor *current; + + // 15.4.5.1, 4b + if (isArrayObject() && index >= array.length() && !array.getLengthProperty()->isWritable()) + goto reject; + + if (isNonStrictArgumentsObject) + return static_cast(this)->defineOwnProperty(ctx, index, desc); + + // Clause 1 + current = __getOwnProperty__(ctx, index); + if (!current) { + // clause 3 + if (!extensible) + goto reject; + // clause 4 + PropertyDescriptor *pd = array.insert(index); + *pd = *desc; + pd->fullyPopulated(); + return true; + } + + return __defineOwnProperty__(ctx, current, desc); +reject: + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; +} + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc) +{ + // clause 5 + if (desc->isEmpty()) + return true; + + // clause 6 + if (desc->isSubset(current)) + return true; + + // clause 7 + if (!current->isConfigurable()) { + if (desc->isConfigurable()) + goto reject; + if (desc->enumberable != PropertyDescriptor::Undefined && desc->enumberable != current->enumberable) + goto reject; + } + + // clause 8 + if (desc->isGeneric()) + goto accept; + + // clause 9 + if (current->isData() != desc->isData()) { + // 9a + if (!current->isConfigurable()) + goto reject; + if (current->isData()) { + // 9b + current->type = PropertyDescriptor::Accessor; + current->writable = PropertyDescriptor::Undefined; + current->get = 0; + current->set = 0; + } else { + // 9c + current->type = PropertyDescriptor::Data; + current->writable = PropertyDescriptor::Disabled; + current->value = Value::undefinedValue(); + } + } else if (current->isData() && desc->isData()) { // clause 10 + if (!current->isConfigurable() && !current->isWritable()) { + if (desc->isWritable() || !current->value.sameValue(desc->value)) + goto reject; + } + } else { // clause 10 + assert(current->isAccessor() && desc->isAccessor()); + if (!current->isConfigurable()) { + if (desc->get && !(current->get == desc->get || (!current->get && (quintptr)desc->get == 0x1))) + goto reject; + if (desc->set && !(current->set == desc->set || (!current->set && (quintptr)desc->set == 0x1))) + goto reject; + } + } + + accept: + + *current += *desc; + return true; + reject: + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; +} + + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc) +{ + return __defineOwnProperty__(ctx, ctx->engine->identifier(name), desc); +} + + +void ArrayObject::init(ExecutionContext *context) +{ + type = Type_ArrayObject; + + if (!members) + members.reset(new PropertyTable()); + PropertyDescriptor *pd = members->insert(context->engine->id_length); + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Disabled; + pd->value = Value::fromInt32(0); + array.setLengthProperty(pd); +} + + + +void ForEachIteratorObject::markObjects() +{ + Object::markObjects(); + if (it.object) + it.object->mark(); +} + diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h new file mode 100644 index 0000000000..73d62bf209 --- /dev/null +++ b/src/v4/qv4object.h @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_OBJECTS_H +#define QMLJS_OBJECTS_H + +#include "qv4global.h" +#include "qmljs_runtime.h" +#include "qmljs_engine.h" +#include "qmljs_environment.h" +#include "qv4array.h" +#include "qv4string.h" +#include "qv4codegen_p.h" +#include "qv4isel_p.h" +#include "qv4managed.h" +#include "qv4propertydescriptor.h" +#include "qv4propertytable.h" +#include "qv4objectiterator.h" +#include "qv4regexp.h" + +#include +#include +#include +#include +#include + +namespace QQmlJS { + +namespace VM { + +struct Value; +struct Function; +struct Object; +struct ObjectIterator; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct ExecutionEngine; +class MemoryManager; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct RegExpPrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; + + +struct Q_V4_EXPORT Object: Managed { + Object *prototype; + QScopedPointer members; + Array array; + + Object() + : prototype(0) { type = Type_Object; } + Object(const Array &a) + : prototype(0), array(a) {} + + virtual ~Object(); + + PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); + PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); + PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name); + PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, uint index); + + Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); + Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); + + void __put__(ExecutionContext *ctx, String *name, Value value); + void __put__(ExecutionContext *ctx, uint index, Value value); + + bool __hasProperty__(const ExecutionContext *ctx, String *name) const; + bool __hasProperty__(const ExecutionContext *ctx, uint index) const; + bool __delete__(ExecutionContext *ctx, String *name); + bool __delete__(ExecutionContext *ctx, uint index); + bool __defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc); + + // + // helpers + // + void __put__(ExecutionContext *ctx, const QString &name, const Value &value); + + Value getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const; + Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const; + Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const; + + void inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); + void inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); + + /* The spec default: Writable: true, Enumerable: false, Configurable: true */ + void defineDefaultProperty(String *name, Value value); + void defineDefaultProperty(ExecutionContext *context, const QString &name, Value value); + void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int count = 0); + void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *, Value, Value *, int), int argumentCount = 0); + /* Fixed: Writable: false, Enumerable: false, Configurable: false */ + void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value); + void defineReadonlyProperty(String *name, Value value); + +protected: + virtual void markObjects(); + + friend struct ObjectIterator; +}; + +struct ForEachIteratorObject: Object { + ObjectIterator it; + ForEachIteratorObject(ExecutionContext *ctx, Object *o) + : it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) { type = Type_ForeachIteratorObject; } + + Value nextPropertyName() { return it.nextPropertyNameAsString(); } + +protected: + virtual void markObjects(); +}; + +struct BooleanObject: Object { + Value value; + BooleanObject(const Value &value): value(value) { type = Type_BooleanObject; } +}; + +struct NumberObject: Object { + Value value; + NumberObject(const Value &value): value(value) { type = Type_NumberObject; } +}; + +struct ArrayObject: Object { + ArrayObject(ExecutionContext *ctx) { init(ctx); } + ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setLengthUnchecked(array.length()); } + void init(ExecutionContext *context); +}; + + +} // namespace VM +} // namespace QQmlJS + +#endif // QMLJS_OBJECTS_H diff --git a/src/v4/qv4objectiterator.cpp b/src/v4/qv4objectiterator.cpp new file mode 100644 index 0000000000..8da2e7bdba --- /dev/null +++ b/src/v4/qv4objectiterator.cpp @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qv4objectiterator.h" +#include "qv4object.h" +#include "qv4stringobject.h" + +namespace QQmlJS { +namespace VM { + +ObjectIterator::ObjectIterator(ExecutionContext *context, Object *o, uint flags) + : context(context) + , object(o) + , current(o) + , arrayNode(0) + , arrayIndex(0) + , tableIndex(0) + , flags(flags) +{ + if (current && current->asStringObject()) + this->flags |= CurrentIsString; +} + +PropertyDescriptor *ObjectIterator::next(String **name, uint *index) +{ + PropertyDescriptor *p = 0; + *name = 0; + *index = UINT_MAX; + while (1) { + if (!current) + break; + + if (flags & CurrentIsString) { + StringObject *s = static_cast(current); + uint slen = s->value.stringValue()->toQString().length(); + while (arrayIndex < slen) { + *index = arrayIndex; + ++arrayIndex; + return s->__getOwnProperty__(context, *index); + } + flags &= ~CurrentIsString; + arrayNode = current->array.sparseBegin(); + // iterate until we're past the end of the string + while (arrayNode && arrayNode->key() < slen) + arrayNode = arrayNode->nextNode(); + } + + if (!arrayIndex) + arrayNode = current->array.sparseBegin(); + + // sparse arrays + if (arrayNode) { + while (arrayNode != current->array.sparseEnd()) { + int k = arrayNode->key(); + p = current->array.at(k); + arrayNode = arrayNode->nextNode(); + if (p && (!(flags & EnumberableOnly) || p->isEnumerable())) { + arrayIndex = k + 1; + *index = k; + return p; + } + } + arrayNode = 0; + arrayIndex = UINT_MAX; + } + // dense arrays + while (arrayIndex < current->array.length()) { + p = current->array.at(arrayIndex); + ++arrayIndex; + if (p && p->type != PropertyDescriptor::Generic && (!(flags & EnumberableOnly) || p->isEnumerable())) { + *index = arrayIndex - 1; + return p; + } + } + + if (!current->members || tableIndex >= (uint)current->members->_propertyCount) { + if (flags & WithProtoChain) + current = current->prototype; + else + current = 0; + if (current && current->asStringObject()) + flags |= CurrentIsString; + else + flags &= ~CurrentIsString; + + + arrayIndex = 0; + tableIndex = 0; + continue; + } + PropertyTableEntry *pt = current->members->_properties[tableIndex]; + ++tableIndex; + // ### check that it's not a repeated attribute + if (pt && (!(flags & EnumberableOnly) || pt->descriptor.isEnumerable())) { + *name = pt->name; + p = &pt->descriptor; + return p; + } + } + return 0; +} + +Value ObjectIterator::nextPropertyName() +{ + uint index; + String *name; + next(&name, &index); + if (name) + return Value::fromString(name); + if (index < UINT_MAX) + return Value::fromDouble(index); + return Value::nullValue(); +} + +Value ObjectIterator::nextPropertyNameAsString() +{ + uint index; + String *name; + next(&name, &index); + if (name) + return Value::fromString(name); + if (index < UINT_MAX) + return __qmljs_to_string(Value::fromDouble(index), context); + return Value::nullValue(); +} + +} +} + diff --git a/src/v4/qv4objectiterator.h b/src/v4/qv4objectiterator.h new file mode 100644 index 0000000000..baecc25424 --- /dev/null +++ b/src/v4/qv4objectiterator.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4OBJECTITERATOR_H +#define QV4OBJECTITERATOR_H + +#include "qmljs_value.h" + +namespace QQmlJS { +namespace VM { + +struct SparseArrayNode; +struct Object; +struct PropertyDescriptor; + +struct ObjectIterator +{ + enum Flags { + NoFlags = 0, + EnumberableOnly = 0x1, + WithProtoChain = 0x2, + CurrentIsString = 0x4 + }; + + ExecutionContext *context; + Object *object; + Object *current; + SparseArrayNode *arrayNode; + uint arrayIndex; + uint tableIndex; + uint flags; + + ObjectIterator(ExecutionContext *context, Object *o, uint flags); + PropertyDescriptor *next(String **name, uint *index); + Value nextPropertyName(); + Value nextPropertyNameAsString(); +}; + +} +} + +#endif diff --git a/src/v4/qv4objectproto.cpp b/src/v4/qv4objectproto.cpp new file mode 100644 index 0000000000..98f205d1d8 --- /dev/null +++ b/src/v4/qv4objectproto.cpp @@ -0,0 +1,558 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4objectproto.h" +#include "qv4mm.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef Q_WS_WIN +# include +# ifndef Q_OS_VXWORKS +# include +# else +# include "qplatformdefs.h" +# endif +#else +# include +#endif + +using namespace QQmlJS::VM; + + +// +// Object +// +ObjectCtor::ObjectCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value ObjectCtor::construct(ExecutionContext *ctx) +{ + if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull()) + return ctx->thisObject; + return __qmljs_to_object(ctx->argument(0), ctx); +} + +Value ObjectCtor::call(ExecutionContext *ctx) +{ + if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull()) + return Value::fromObject(ctx->engine->newObject()); + return __qmljs_to_object(ctx->argument(0), ctx); +} + +void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("create"), method_create, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 3); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("seal"), method_seal, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("freeze"), method_freeze, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("keys"), method_keys, 1); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + defineDefaultProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1); + defineDefaultProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1); + defineDefaultProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1); + defineDefaultProperty(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 0); + defineDefaultProperty(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 0); +} + +Value ObjectPrototype::method_getPrototypeOf(ExecutionContext *ctx) +{ + Value o = ctx->argument(0); + if (! o.isObject()) + ctx->throwTypeError(); + + Object *p = o.objectValue()->prototype; + return p ? Value::fromObject(p) : Value::nullValue(); +} + +Value ObjectPrototype::method_getOwnPropertyDescriptor(ExecutionContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + String *name = ctx->argument(1).toString(ctx); + PropertyDescriptor *desc = O.objectValue()->__getOwnProperty__(ctx, name); + return fromPropertyDescriptor(ctx, desc); +} + +Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) +{ + Object *O = ctx->argument(0).asObject(); + if (!O) + ctx->throwTypeError(); + + ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); + Array &a = array->array; + ObjectIterator it(ctx, O, ObjectIterator::NoFlags); + while (1) { + Value v = it.nextPropertyNameAsString(); + if (v.isNull()) + break; + a.push_back(v); + } + return Value::fromObject(array); +} + +Value ObjectPrototype::method_create(ExecutionContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject() && !O.isNull()) + ctx->throwTypeError(); + + Object *newObject = ctx->engine->newObject(); + newObject->prototype = O.objectValue(); + + Value objValue = Value::fromObject(newObject); + if (ctx->argumentCount > 1 && !ctx->argument(1).isUndefined()) { + ctx->arguments[0] = objValue; + method_defineProperties(ctx); + } + + return objValue; +} + +Value ObjectPrototype::method_defineProperty(ExecutionContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + String *name = ctx->argument(1).toString(ctx); + + Value attributes = ctx->argument(2); + PropertyDescriptor pd; + toPropertyDescriptor(ctx, attributes, &pd); + + if (!O.objectValue()->__defineOwnProperty__(ctx, name, &pd)) + __qmljs_throw_type_error(ctx); + + return O; +} + +Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(1).toObject(ctx).objectValue(); + + ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + PropertyDescriptor n; + toPropertyDescriptor(ctx, o->getValue(ctx, pd), &n); + bool ok; + if (name) + ok = O.objectValue()->__defineOwnProperty__(ctx, name, &n); + else + ok = O.objectValue()->__defineOwnProperty__(ctx, index, &n); + if (!ok) + __qmljs_throw_type_error(ctx); + } + + return O; +} + +Value ObjectPrototype::method_seal(ExecutionContext *ctx) +{ + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + + ObjectIterator it(ctx, o, ObjectIterator::NoFlags); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + pd->configurable = PropertyDescriptor::Disabled; + } + return ctx->argument(0); +} + +Value ObjectPrototype::method_freeze(ExecutionContext *ctx) +{ + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + + ObjectIterator it(ctx, o, ObjectIterator::NoFlags); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + if (pd->type == PropertyDescriptor::Data) + pd->writable = PropertyDescriptor::Disabled; + pd->configurable = PropertyDescriptor::Disabled; + } + return ctx->argument(0); +} + +Value ObjectPrototype::method_preventExtensions(ExecutionContext *ctx) +{ + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + return ctx->argument(0); +} + +Value ObjectPrototype::method_isSealed(ExecutionContext *ctx) +{ + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + if (o->extensible) + return Value::fromBoolean(false); + + ObjectIterator it(ctx, o, ObjectIterator::NoFlags); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + if (pd->configurable != PropertyDescriptor::Disabled) + return Value::fromBoolean(false); + } + return Value::fromBoolean(true); +} + +Value ObjectPrototype::method_isFrozen(ExecutionContext *ctx) +{ + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + if (o->extensible) + return Value::fromBoolean(false); + + ObjectIterator it(ctx, o, ObjectIterator::NoFlags); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + if (pd->isWritable() || pd->isConfigurable()) + return Value::fromBoolean(false); + } + return Value::fromBoolean(true); +} + +Value ObjectPrototype::method_isExtensible(ExecutionContext *ctx) +{ + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + return Value::fromBoolean(o->extensible); +} + +Value ObjectPrototype::method_keys(ExecutionContext *ctx) +{ + if (!ctx->argument(0).isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->argument(0).objectValue(); + + ArrayObject *a = ctx->engine->newArrayObject(ctx); + + ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); + while (1) { + uint index; + String *name; + PropertyDescriptor *pd = it.next(&name, &index); + if (!pd) + break; + Value key; + if (name) { + key = Value::fromString(name); + } else { + key = Value::fromDouble(index); + key = __qmljs_to_string(key, ctx); + } + a->array.push_back(key); + } + + return Value::fromObject(a); +} + +Value ObjectPrototype::method_toString(ExecutionContext *ctx) +{ + if (ctx->thisObject.isUndefined()) { + return Value::fromString(ctx, QStringLiteral("[object Undefined]")); + } else if (ctx->thisObject.isNull()) { + return Value::fromString(ctx, QStringLiteral("[object Null]")); + } else { + Value obj = __qmljs_to_object(ctx->thisObject, ctx); + QString className = obj.objectValue()->className(); + return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(className)); + } +} + +Value ObjectPrototype::method_toLocaleString(ExecutionContext *ctx) +{ + Object *o = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Value ts = o->__get__(ctx, ctx->engine->identifier(QStringLiteral("toString"))); + FunctionObject *f = ts.asFunctionObject(); + if (!f) + __qmljs_throw_type_error(ctx); + return f->call(ctx, Value::fromObject(o), 0, 0); +} + +Value ObjectPrototype::method_valueOf(ExecutionContext *ctx) +{ + return ctx->thisObject.toObject(ctx); +} + +Value ObjectPrototype::method_hasOwnProperty(ExecutionContext *ctx) +{ + String *P = ctx->argument(0).toString(ctx); + Value O = ctx->thisObject.toObject(ctx); + bool r = O.objectValue()->__getOwnProperty__(ctx, P) != 0; + return Value::fromBoolean(r); +} + +Value ObjectPrototype::method_isPrototypeOf(ExecutionContext *ctx) +{ + Value V = ctx->argument(0); + if (! V.isObject()) + return Value::fromBoolean(false); + + Object *O = ctx->thisObject.toObject(ctx).objectValue(); + Object *proto = V.objectValue()->prototype; + while (proto) { + if (O == proto) + return Value::fromBoolean(true); + proto = proto->prototype; + } + return Value::fromBoolean(false); +} + +Value ObjectPrototype::method_propertyIsEnumerable(ExecutionContext *ctx) +{ + String *p = ctx->argument(0).toString(ctx); + + Object *o = ctx->thisObject.toObject(ctx).objectValue(); + PropertyDescriptor *pd = o->__getOwnProperty__(ctx, p); + return Value::fromBoolean(pd && pd->isEnumerable()); +} + +Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx) +{ + if (ctx->argumentCount < 2) + __qmljs_throw_type_error(ctx); + String *prop = ctx->argument(0).toString(ctx); + + FunctionObject *f = ctx->argument(1).asFunctionObject(); + if (!f) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->thisObject.toObject(ctx).objectValue(); + + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(f, 0); + pd.configurable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + o->__defineOwnProperty__(ctx, prop, &pd); + return Value::undefinedValue(); +} + +Value ObjectPrototype::method_defineSetter(ExecutionContext *ctx) +{ + if (ctx->argumentCount < 2) + __qmljs_throw_type_error(ctx); + String *prop = ctx->argument(0).toString(ctx); + + FunctionObject *f = ctx->argument(1).asFunctionObject(); + if (!f) + __qmljs_throw_type_error(ctx); + + Object *o = ctx->thisObject.toObject(ctx).objectValue(); + + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(0, f); + pd.configurable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + o->__defineOwnProperty__(ctx, prop, &pd); + return Value::undefinedValue(); +} + +void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc) +{ + if (!v.isObject()) + __qmljs_throw_type_error(ctx); + + Object *o = v.objectValue(); + + desc->type = PropertyDescriptor::Generic; + + desc->enumberable = PropertyDescriptor::Undefined; + if (o->__hasProperty__(ctx, ctx->engine->id_enumerable)) + desc->enumberable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_enumerable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; + + desc->configurable = PropertyDescriptor::Undefined; + if (o->__hasProperty__(ctx, ctx->engine->id_configurable)) + desc->configurable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_configurable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; + + desc->get = 0; + if (o->__hasProperty__(ctx, ctx->engine->id_get)) { + Value get = o->__get__(ctx, ctx->engine->id_get); + FunctionObject *f = get.asFunctionObject(); + if (f) { + desc->get = f; + } else if (get.isUndefined()) { + desc->get = (FunctionObject *)0x1; + } else { + __qmljs_throw_type_error(ctx); + } + desc->type = PropertyDescriptor::Accessor; + } + + desc->set = 0; + if (o->__hasProperty__(ctx, ctx->engine->id_set)) { + Value set = o->__get__(ctx, ctx->engine->id_set); + FunctionObject *f = set.asFunctionObject(); + if (f) { + desc->set = f; + } else if (set.isUndefined()) { + desc->set = (FunctionObject *)0x1; + } else { + __qmljs_throw_type_error(ctx); + } + desc->type = PropertyDescriptor::Accessor; + } + + desc->writable = PropertyDescriptor::Undefined; + if (o->__hasProperty__(ctx, ctx->engine->id_writable)) { + if (desc->isAccessor()) + __qmljs_throw_type_error(ctx); + desc->writable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_writable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; + // writable forces it to be a data descriptor + desc->value = Value::undefinedValue(); + } + + if (o->__hasProperty__(ctx, ctx->engine->id_value)) { + if (desc->isAccessor()) + __qmljs_throw_type_error(ctx); + desc->value = o->__get__(ctx, ctx->engine->id_value); + desc->type = PropertyDescriptor::Data; + } + +} + + +Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc) +{ + if (!desc) + return Value::undefinedValue(); + + ExecutionEngine *engine = ctx->engine; +// Let obj be the result of creating a new object as if by the expression new Object() where Object is the standard built-in constructor with that name. + Object *o = engine->newObject(); + + PropertyDescriptor pd; + pd.type = PropertyDescriptor::Data; + pd.writable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + pd.configurable = PropertyDescriptor::Enabled; + + if (desc->isData()) { + pd.value = desc->value; + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("value")), &pd); + pd.value = Value::fromBoolean(desc->writable == PropertyDescriptor::Enabled ? true : false); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("writable")), &pd); + } else { + pd.value = desc->get ? Value::fromObject(desc->get) : Value::undefinedValue(); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("get")), &pd); + pd.value = desc->set ? Value::fromObject(desc->set) : Value::undefinedValue(); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("set")), &pd); + } + pd.value = Value::fromBoolean(desc->enumberable == PropertyDescriptor::Enabled ? true : false); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("enumerable")), &pd); + pd.value = Value::fromBoolean(desc->configurable == PropertyDescriptor::Enabled ? true : false); + o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("configurable")), &pd); + + return Value::fromObject(o); +} diff --git a/src/v4/qv4objectproto.h b/src/v4/qv4objectproto.h new file mode 100644 index 0000000000..93527a28ef --- /dev/null +++ b/src/v4/qv4objectproto.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ECMAOBJECTS_P_H +#define QV4ECMAOBJECTS_P_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include + +namespace QQmlJS { +namespace VM { + +struct ObjectCtor: FunctionObject +{ + ObjectCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct ObjectPrototype: Object +{ + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_getPrototypeOf(ExecutionContext *ctx); + static Value method_getOwnPropertyDescriptor(ExecutionContext *ctx); + static Value method_getOwnPropertyNames(ExecutionContext *ctx); + static Value method_create(ExecutionContext *ctx); + static Value method_defineProperty(ExecutionContext *ctx); + static Value method_defineProperties(ExecutionContext *ctx); + static Value method_seal(ExecutionContext *ctx); + static Value method_freeze(ExecutionContext *ctx); + static Value method_preventExtensions(ExecutionContext *ctx); + static Value method_isSealed(ExecutionContext *ctx); + static Value method_isFrozen(ExecutionContext *ctx); + static Value method_isExtensible(ExecutionContext *ctx); + static Value method_keys(ExecutionContext *ctx); + + static Value method_toString(ExecutionContext *ctx); + static Value method_toLocaleString(ExecutionContext *ctx); + static Value method_valueOf(ExecutionContext *ctx); + static Value method_hasOwnProperty(ExecutionContext *ctx); + static Value method_isPrototypeOf(ExecutionContext *ctx); + static Value method_propertyIsEnumerable(ExecutionContext *ctx); + + static Value method_defineGetter(ExecutionContext *ctx); + static Value method_defineSetter(ExecutionContext *ctx); + + static void toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc); + static Value fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc); +}; + + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/v4/qv4propertydescriptor.h b/src/v4/qv4propertydescriptor.h new file mode 100644 index 0000000000..dc9e1c556d --- /dev/null +++ b/src/v4/qv4propertydescriptor.h @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4PROPERTYDESCRIPTOR_H +#define QV4PROPERTYDESCRIPTOR_H + +#include "qmljs_value.h" + +namespace QQmlJS { +namespace VM { + +struct FunctionObject; + +struct PropertyDescriptor { + enum Type { + Generic, + Data, + Accessor + }; + enum State { + Undefined, + Disabled, + Enabled + }; + union { + Value value; + struct { + FunctionObject *get; + FunctionObject *set; + }; + }; + uint type : 8; + uint writable : 8; + uint enumberable : 8; + uint configurable : 8; + + static inline PropertyDescriptor fromValue(Value v) { + PropertyDescriptor pd; + pd.value = v; + pd.type = Data; + pd.writable = Undefined; + pd.enumberable = Undefined; + pd.configurable = Undefined; + return pd; + } + static inline PropertyDescriptor fromAccessor(FunctionObject *getter, FunctionObject *setter) { + PropertyDescriptor pd; + pd.get = getter; + pd.set = setter; + pd.type = Accessor; + pd.writable = Undefined; + pd.enumberable = Undefined; + pd.configurable = Undefined; + return pd; + } + + // Section 8.10 + inline void fullyPopulated() { + if (type == Generic) { + type = Data; + value = Value::undefinedValue(); + } + if (type == Data) { + if (writable == Undefined) + writable = Disabled; + } else { + writable = Undefined; + if ((quintptr)get == 0x1) + get = 0; + if ((quintptr)set == 0x1) + set = 0; + } + if (enumberable == Undefined) + enumberable = Disabled; + if (configurable == Undefined) + configurable = Disabled; + } + + inline bool isData() const { return type == Data || writable != Undefined; } + inline bool isAccessor() const { return type == Accessor; } + inline bool isGeneric() const { return type == Generic && writable == Undefined; } + + inline bool isWritable() const { return writable == Enabled; } + inline bool isEnumerable() const { return enumberable == Enabled; } + inline bool isConfigurable() const { return configurable == Enabled; } + + inline bool isEmpty() const { + return type == Generic && writable == Undefined && enumberable == Undefined && configurable == Undefined; + } + inline bool isSubset(PropertyDescriptor *other) const { + if (type != Generic && type != other->type) + return false; + if (enumberable != Undefined && enumberable != other->enumberable) + return false; + if (configurable != Undefined && configurable != other->configurable) + return false; + if (writable != Undefined && writable != other->writable) + return false; + if (type == Data && !value.sameValue(other->value)) + return false; + if (type == Accessor) { + if (get != other->get) + return false; + if (set != other->set) + return false; + } + return true; + } + inline void operator+=(const PropertyDescriptor &other) { + if (other.enumberable != Undefined) + enumberable = other.enumberable; + if (other.configurable != Undefined) + configurable = other.configurable; + if (other.writable != Undefined) + writable = other.writable; + if (other.type == Accessor) { + type = Accessor; + if (other.get) + get = ((quintptr)other.get == 0x1) ? 0 : other.get; + if (other.set) + set = ((quintptr)other.set == 0x1) ? 0 : other.set; + } else if (other.type == Data){ + type = Data; + value = other.value; + } + } +}; + +} +} + +#endif diff --git a/src/v4/qv4propertytable.h b/src/v4/qv4propertytable.h new file mode 100644 index 0000000000..3bd47679b5 --- /dev/null +++ b/src/v4/qv4propertytable.h @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4PROPERTYTABLE_H +#define QV4PROPERTYTABLE_H + +#include "qmljs_value.h" +#include "qv4propertydescriptor.h" + +namespace QQmlJS { +namespace VM { + +struct ObjectIterator; + +struct PropertyTableEntry { + PropertyDescriptor descriptor; + String *name; + PropertyTableEntry *next; + int index; + + inline PropertyTableEntry(String *name) + : name(name), + next(0), + index(-1) + { } + + inline bool hasName(String *n) const { return name->isEqualTo(n); } + inline unsigned hashValue() const { return name->hashValue(); } +}; + +class PropertyTable +{ + Q_DISABLE_COPY(PropertyTable) + +public: + PropertyTable() + : _properties(0) + , _buckets(0) + , _freeList(0) + , _propertyCount(0) + , _bucketCount(0) + , _primeIdx(-1) + , _allocated(0) + {} + + ~PropertyTable() + { + qDeleteAll(_properties, _properties + _propertyCount); + delete[] _properties; + delete[] _buckets; + } + + typedef PropertyTableEntry **iterator; + inline iterator begin() const { return _properties; } + inline iterator end() const { return _properties + _propertyCount; } + + void remove(PropertyTableEntry *prop) + { + PropertyTableEntry **bucket = _buckets + (prop->hashValue() % _bucketCount); + if (*bucket == prop) { + *bucket = prop->next; + } else { + for (PropertyTableEntry *it = *bucket; it; it = it->next) { + if (it->next == prop) { + it->next = it->next->next; + break; + } + } + } + + _properties[prop->index] = 0; + prop->next = _freeList; + _freeList = prop; + } + + PropertyTableEntry *findEntry(String *name) const + { + if (_properties) { + for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { + if (prop && (prop->name == name || prop->hasName(name))) + return prop; + } + } + + return 0; + } + + PropertyDescriptor *find(String *name) const + { + if (_properties) { + for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { + if (prop && (prop->name == name || prop->hasName(name))) + return &prop->descriptor; + } + } + + return 0; + } + + PropertyDescriptor *insert(String *name) + { + if (PropertyTableEntry *prop = findEntry(name)) + return &prop->descriptor; + + if (_propertyCount == _allocated) { + if (! _allocated) + _allocated = 4; + else + _allocated *= 2; + + PropertyTableEntry **properties = new PropertyTableEntry*[_allocated]; + std::copy(_properties, _properties + _propertyCount, properties); + delete[] _properties; + _properties = properties; + } + + PropertyTableEntry *prop; + if (_freeList) { + prop = _freeList; + prop->name = name; + _freeList = _freeList->next; + } else { + prop = new PropertyTableEntry(name); + } + + prop->index = _propertyCount; + _properties[_propertyCount] = prop; + ++_propertyCount; + + if (! _buckets || 3 * _propertyCount >= 2 * _bucketCount) { + rehash(); + } else { + PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; + prop->next = bucket; + bucket = prop; + } + + return &prop->descriptor; + } + +private: + void rehash() + { + _bucketCount = nextPrime(); + + delete[] _buckets; + _buckets = new PropertyTableEntry *[_bucketCount]; + std::fill(_buckets, _buckets + _bucketCount, (PropertyTableEntry *) 0); + + for (int i = 0; i < _propertyCount; ++i) { + PropertyTableEntry *prop = _properties[i]; + if (prop) { + PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; + prop->next = bucket; + bucket = prop; + } + } + } + + inline int nextPrime() + { + // IMPORTANT: do not add more primes without checking if _primeIdx needs more bits! + static const int primes[] = { + 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853, 25717, 51437, 102877 + }; + + if (_primeIdx < (int) (sizeof(primes)/sizeof(int))) + return primes[++_primeIdx]; + else + return _bucketCount * 2 + 1; // Yes, we're degrading here. But who needs more than about 68000 properties? + } + +private: + friend struct ObjectIterator; + PropertyTableEntry **_properties; + PropertyTableEntry **_buckets; + PropertyTableEntry *_freeList; + int _propertyCount; + int _bucketCount; + int _primeIdx: 5; + int _allocated: 27; +}; + +} +} + +#endif diff --git a/src/v4/qv4regexp.cpp b/src/v4/qv4regexp.cpp new file mode 100644 index 0000000000..bcef815e25 --- /dev/null +++ b/src/v4/qv4regexp.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4regexp.h" + +#include "qmljs_engine.h" + +namespace QQmlJS { +namespace VM { + +uint RegExp::match(const QString &string, int start, uint *matchOffsets) +{ + if (!isValid()) + return JSC::Yarr::offsetNoMatch; + + return JSC::Yarr::interpret(m_byteCode.get(), WTF::String(string).characters16(), string.length(), start, matchOffsets); +} + +RegExp::RegExp(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline) + : m_pattern(pattern) + , m_subPatternCount(0) + , m_ignoreCase(ignoreCase) + , m_multiLine(multiline) +{ + if (!engine) + return; + const char* error = 0; + JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiline, &error); + if (error) + return; + m_subPatternCount = yarrPattern.m_numSubpatterns; + m_byteCode = JSC::Yarr::byteCompile(yarrPattern, &engine->bumperPointerAllocator); +} + +} // end of namespace VM +} // end of namespace QQmlJS + + diff --git a/src/v4/qv4regexp.h b/src/v4/qv4regexp.h new file mode 100644 index 0000000000..7eae033bec --- /dev/null +++ b/src/v4/qv4regexp.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4REGEXP_H +#define QV4REGEXP_H + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +namespace QQmlJS { +namespace VM { + +struct ExecutionEngine; + +class RegExp : public RefCounted +{ +public: + static PassRefPtr create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false) + { return adoptRef(new RegExp(engine, pattern, ignoreCase, multiline)); } + + QString pattern() const { return m_pattern; } + + bool isValid() const { return m_byteCode.get(); } + + uint match(const QString& string, int start, uint *matchOffsets); + + bool ignoreCase() const { return m_ignoreCase; } + bool multiLine() const { return m_multiLine; } + int captureCount() const { return m_subPatternCount + 1; } + +private: + Q_DISABLE_COPY(RegExp); + RegExp(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline); + + const QString m_pattern; + OwnPtr m_byteCode; + int m_subPatternCount; + const bool m_ignoreCase; + const bool m_multiLine; +}; + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4REGEXP_H diff --git a/src/v4/qv4regexpobject.cpp b/src/v4/qv4regexpobject.cpp new file mode 100644 index 0000000000..1f78c56782 --- /dev/null +++ b/src/v4/qv4regexpobject.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4regexpobject.h" +#include "qv4ir_p.h" +#include "qv4isel_p.h" +#include "qv4objectproto.h" +#include "qv4stringobject.h" +#include "qv4mm.h" + +#include +#include +#include +#include +#include +#include +#include "private/qlocale_tools_p.h" + +#include +#include +#include +#include +#include +#include + +using namespace QQmlJS::VM; + + +RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global) + : value(value) + , global(global) +{ + type = Type_RegExpObject; + + if (!members) + members.reset(new PropertyTable()); + lastIndexProperty = members->insert(engine->identifier(QStringLiteral("lastIndex"))); + lastIndexProperty->type = PropertyDescriptor::Data; + lastIndexProperty->writable = PropertyDescriptor::Enabled; + lastIndexProperty->enumberable = PropertyDescriptor::Disabled; + lastIndexProperty->configurable = PropertyDescriptor::Disabled; + lastIndexProperty->value = Value::fromInt32(0); + if (!this->value.get()) + return; + defineReadonlyProperty(engine->identifier(QStringLiteral("source")), Value::fromString(engine->newString(this->value->pattern()))); + defineReadonlyProperty(engine->identifier(QStringLiteral("global")), Value::fromBoolean(global)); + defineReadonlyProperty(engine->identifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase())); + defineReadonlyProperty(engine->identifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); +} + + +RegExpCtor::RegExpCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value RegExpCtor::construct(ExecutionContext *ctx) +{ + Value r = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); + Value f = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); + if (RegExpObject *re = r.asRegExpObject()) { + if (!f.isUndefined()) + ctx->throwTypeError(); + + RegExpObject *o = ctx->engine->newRegExpObject(re->value, re->global); + return Value::fromObject(o); + } + + if (r.isUndefined()) + r = Value::fromString(ctx, QString()); + else if (!r.isString()) + r = __qmljs_to_string(r, ctx); + + bool global = false; + bool ignoreCase = false; + bool multiLine = false; + if (!f.isUndefined()) { + f = __qmljs_to_string(f, ctx); + QString str = f.stringValue()->toQString(); + for (int i = 0; i < str.length(); ++i) { + if (str.at(i) == QChar('g') && !global) { + global = true; + } else if (str.at(i) == QChar('i') && !ignoreCase) { + ignoreCase = true; + } else if (str.at(i) == QChar('m') && !multiLine) { + multiLine = true; + } else { + ctx->throwSyntaxError(0); + } + } + } + + RefPtr re = RegExp::create(ctx->engine, r.stringValue()->toQString(), ignoreCase, multiLine); + if (!re->isValid()) + ctx->throwSyntaxError(0); + + RegExpObject *o = ctx->engine->newRegExpObject(re, global); + return Value::fromObject(o); +} + +Value RegExpCtor::call(ExecutionContext *ctx) +{ + if (ctx->argumentCount > 0 && ctx->argument(0).asRegExpObject()) { + if (ctx->argumentCount == 1 || ctx->argument(1).isUndefined()) + return ctx->argument(0); + } + + return construct(ctx); +} + +void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(2)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 1); + defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 1); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("compile"), method_compile, 2); +} + +Value RegExpPrototype::method_exec(ExecutionContext *ctx) +{ + RegExpObject *r = ctx->thisObject.asRegExpObject(); + if (!r) + ctx->throwTypeError(); + + Value arg = ctx->argument(0); + arg = __qmljs_to_string(arg, ctx); + QString s = arg.stringValue()->toQString(); + + int offset = r->global ? r->lastIndexProperty->value.toInt32(ctx) : 0; + if (offset < 0 || offset > s.length()) { + r->lastIndexProperty->value = Value::fromInt32(0); + return Value::nullValue(); + } + + uint* matchOffsets = (uint*)alloca(r->value->captureCount() * 2 * sizeof(uint)); + int result = r->value->match(s, offset, matchOffsets); + if (result == -1) { + r->lastIndexProperty->value = Value::fromInt32(0); + return Value::nullValue(); + } + + // fill in result data + ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); + for (int i = 0; i < r->value->captureCount(); ++i) { + int start = matchOffsets[i * 2]; + int end = matchOffsets[i * 2 + 1]; + Value entry = Value::undefinedValue(); + if (start != -1 && end != -1) + entry = Value::fromString(ctx, s.mid(start, end - start)); + array->array.push_back(entry); + } + + array->__put__(ctx, QLatin1String("index"), Value::fromInt32(result)); + array->__put__(ctx, QLatin1String("input"), arg); + + if (r->global) + r->lastIndexProperty->value = Value::fromInt32(matchOffsets[1]); + + return Value::fromObject(array); +} + +Value RegExpPrototype::method_test(ExecutionContext *ctx) +{ + Value r = method_exec(ctx); + return Value::fromBoolean(!r.isNull()); +} + +Value RegExpPrototype::method_toString(ExecutionContext *ctx) +{ + RegExpObject *r = ctx->thisObject.asRegExpObject(); + if (!r) + ctx->throwTypeError(); + + QString result = QChar('/') + r->value->pattern(); + result += QChar('/'); + // ### 'g' option missing + if (r->value->ignoreCase()) + result += QChar('i'); + if (r->value->multiLine()) + result += QChar('m'); + return Value::fromString(ctx, result); +} + +Value RegExpPrototype::method_compile(ExecutionContext *ctx) +{ + RegExpObject *r = ctx->thisObject.asRegExpObject(); + if (!r) + ctx->throwTypeError(); + + RegExpObject *re = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ctx->arguments, ctx->argumentCount).asRegExpObject(); + + r->value = re->value; + r->global = re->global; + return Value::undefinedValue(); +} + diff --git a/src/v4/qv4regexpobject.h b/src/v4/qv4regexpobject.h new file mode 100644 index 0000000000..50241f2daf --- /dev/null +++ b/src/v4/qv4regexpobject.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4REGEXPOBJECT_H +#define QV4REGEXPOBJECT_H + +#include "qmljs_runtime.h" +#include "qmljs_engine.h" +#include "qmljs_environment.h" +#include "qv4functionobject.h" +#include "qv4array.h" +#include "qv4string.h" +#include "qv4codegen_p.h" +#include "qv4isel_p.h" +#include "qv4managed.h" +#include "qv4propertydescriptor.h" +#include "qv4propertytable.h" +#include "qv4objectiterator.h" +#include "qv4regexp.h" + +#include +#include +#include +#include +#include + +namespace QQmlJS { + +namespace VM { + +struct RegExpObject: Object { + RefPtr value; + PropertyDescriptor *lastIndexProperty; + bool global; + RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global); +}; + + +struct RegExpCtor: FunctionObject +{ + RegExpCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct RegExpPrototype: RegExpObject +{ + RegExpPrototype(ExecutionEngine* engine): RegExpObject(engine, RegExp::create(0, QString()), false) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_exec(ExecutionContext *ctx); + static Value method_test(ExecutionContext *ctx); + static Value method_toString(ExecutionContext *ctx); + static Value method_compile(ExecutionContext *ctx); +}; + + + +} // namespace VM +} // namespace QQmlJS + +#endif // QMLJS_OBJECTS_H diff --git a/src/v4/qv4string.cpp b/src/v4/qv4string.cpp new file mode 100644 index 0000000000..6e0c14d108 --- /dev/null +++ b/src/v4/qv4string.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4string.h" +#include "qmljs_runtime.h" +#include + +namespace QQmlJS { +namespace VM { + +static uint toArrayIndex(const QChar *ch, const QChar *end) +{ + uint i = ch->unicode() - '0'; + if (i > 9) + return String::InvalidArrayIndex; + ++ch; + // reject "01", "001", ... + if (i == 0 && ch != end) + return String::InvalidArrayIndex; + + while (ch < end) { + uint x = ch->unicode() - '0'; + if (x > 9) + return String::InvalidArrayIndex; + uint n = i*10 + x; + if (n < i) + // overflow + return String::InvalidArrayIndex; + i = n; + ++ch; + } + return i; +} + +uint String::asArrayIndexSlow() const +{ + if (stringHash < LargestHashedArrayIndex) + return stringHash; + + const QChar *ch = _text.constData(); + const QChar *end = ch + _text.length(); + return toArrayIndex(ch, end); +} + +uint String::toUInt(bool *ok) const +{ + *ok = true; + + if (stringHash == InvalidHashValue) + createHashValue(); + if (stringHash < LargestHashedArrayIndex) + return stringHash; + + double d = __qmljs_string_to_number(this); + uint l = (uint)d; + if (d == l) + return l; + *ok = false; + return UINT_MAX; +} + +void String::createHashValue() const +{ + const QChar *ch = _text.constData(); + const QChar *end = ch + _text.length(); + + // array indices get their number as hash value, for large numbers we set to INT_MAX + stringHash = toArrayIndex(ch, end); + if (stringHash < UINT_MAX) { + if (stringHash > INT_MAX) + stringHash = INT_MAX; + return; + } + + uint h = 0xffffffff; + while (ch < end) { + h = 31 * h + ch->unicode(); + ++ch; + } + + // set highest bit to mark it as a non number + stringHash = h | 0xf0000000; +} + +} +} diff --git a/src/v4/qv4string.h b/src/v4/qv4string.h new file mode 100644 index 0000000000..34e241218c --- /dev/null +++ b/src/v4/qv4string.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STRING_H +#define QV4STRING_H + +#include +#include + +namespace QQmlJS { +namespace VM { + +struct String : public Managed { + String(const QString &text) + : _text(text) { type = Type_String; stringHash = InvalidHashValue; } + + inline bool isEqualTo(const String *other) const { + if (this == other) + return true; + else if (other && hashValue() == other->hashValue()) + return toQString() == other->toQString(); + return false; + } + + inline const QString &toQString() const { + return _text; + } + + inline unsigned hashValue() const { + if (stringHash == InvalidHashValue) + createHashValue(); + + return stringHash; + } + enum { + InvalidArrayIndex = 0xffffffff, + LargestHashedArrayIndex = 0x7fffffff, + InvalidHashValue = 0xffffffff + }; + uint asArrayIndex() const { + if (stringHash == InvalidHashValue) + createHashValue(); + if (stringHash > LargestHashedArrayIndex) + return InvalidArrayIndex; + if (stringHash < LargestHashedArrayIndex) + return stringHash; + return asArrayIndexSlow(); + } + uint asArrayIndexSlow() const; + uint toUInt(bool *ok) const; + +private: + void createHashValue() const; + + QString _text; +}; + +} +} + +#endif diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp new file mode 100644 index 0000000000..81968cc55a --- /dev/null +++ b/src/v4/qv4stringobject.cpp @@ -0,0 +1,722 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4stringobject.h" +#include "qv4regexpobject.h" +#include "qv4objectproto.h" +#include "qv4mm.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef Q_WS_WIN +# include +# ifndef Q_OS_VXWORKS +# include +# else +# include "qplatformdefs.h" +# endif +#else +# include +#endif + +using namespace QQmlJS::VM; + +StringObject::StringObject(ExecutionContext *ctx, const Value &value) + : value(value) +{ + type = Type_StringObject; + + tmpProperty.type = PropertyDescriptor::Data; + tmpProperty.enumberable = PropertyDescriptor::Enabled; + tmpProperty.writable = PropertyDescriptor::Disabled; + tmpProperty.configurable = PropertyDescriptor::Disabled; + tmpProperty.value = Value::undefinedValue(); + + assert(value.isString()); + defineReadonlyProperty(ctx->engine->id_length, Value::fromUInt32(value.stringValue()->toQString().length())); +} + +PropertyDescriptor *StringObject::getIndex(ExecutionContext *ctx, uint index) +{ + QString str = value.stringValue()->toQString(); + if (index >= (uint)str.length()) + return 0; + String *result = ctx->engine->newString(str.mid(index, 1)); + tmpProperty.value = Value::fromString(result); + return &tmpProperty; +} + +void StringObject::markObjects() +{ + value.stringValue()->mark(); + Object::markObjects(); +} + + +StringCtor::StringCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ +} + +Value StringCtor::construct(ExecutionContext *ctx) +{ + Value value; + if (ctx->argumentCount) + value = Value::fromString(ctx->argument(0).toString(ctx)); + else + value = Value::fromString(ctx, QString()); + return Value::fromObject(ctx->engine->newStringObject(ctx, value)); +} + +Value StringCtor::call(ExecutionContext *ctx) +{ + Value value; + if (ctx->argumentCount) + value = Value::fromString(ctx->argument(0).toString(ctx)); + else + value = Value::fromString(ctx, QString()); + return value; +} + +void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode, 1); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_toString); // valueOf and toString are identical + defineDefaultProperty(ctx, QStringLiteral("charAt"), method_charAt, 1); + defineDefaultProperty(ctx, QStringLiteral("charCodeAt"), method_charCodeAt, 1); + defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("localeCompare"), method_localeCompare, 1); + defineDefaultProperty(ctx, QStringLiteral("match"), method_match, 1); + defineDefaultProperty(ctx, QStringLiteral("replace"), method_replace, 2); + defineDefaultProperty(ctx, QStringLiteral("search"), method_search, 1); + defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); + defineDefaultProperty(ctx, QStringLiteral("split"), method_split, 2); + defineDefaultProperty(ctx, QStringLiteral("substr"), method_substr, 2); + defineDefaultProperty(ctx, QStringLiteral("substring"), method_substring, 2); + defineDefaultProperty(ctx, QStringLiteral("toLowerCase"), method_toLowerCase); + defineDefaultProperty(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); + defineDefaultProperty(ctx, QStringLiteral("toUpperCase"), method_toUpperCase); + defineDefaultProperty(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); + defineDefaultProperty(ctx, QStringLiteral("trim"), method_trim); +} + +static QString getThisString(ExecutionContext *ctx) +{ + String* str = 0; + Value thisObject = ctx->thisObject; + if (StringObject *thisString = thisObject.asStringObject()) + str = thisString->value.stringValue(); + else if (thisObject.isUndefined() || thisObject.isNull()) + ctx->throwTypeError(); + else + str = ctx->thisObject.toString(ctx); + return str->toQString(); +} + +static QString getThisString(ExecutionContext *parentCtx, Value thisObject) +{ + if (thisObject.isString()) + return thisObject.stringValue()->toQString(); + + String* str = 0; + if (StringObject *thisString = thisObject.asStringObject()) + str = thisString->value.stringValue(); + else if (thisObject.isUndefined() || thisObject.isNull()) + parentCtx->throwTypeError(); + else + str = thisObject.toString(parentCtx); + return str->toQString(); +} + +Value StringPrototype::method_toString(ExecutionContext *parentCtx, Value thisObject, Value *, int) +{ + if (thisObject.isString()) + return thisObject; + + StringObject *o = thisObject.asStringObject(); + if (!o) + parentCtx->throwTypeError(); + return o->value; +} + +Value StringPrototype::method_charAt(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) +{ + const QString str = getThisString(parentCtx, thisObject); + + int pos = 0; + if (argc > 0) + pos = (int) argv[0].toInteger(parentCtx); + + QString result; + if (pos >= 0 && pos < str.length()) + result += str.at(pos); + + return Value::fromString(parentCtx, result); +} + +Value StringPrototype::method_charCodeAt(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) +{ + const QString str = getThisString(parentCtx, thisObject); + + int pos = 0; + if (argc > 0) + pos = (int) argv[0].toInteger(parentCtx); + + + if (pos >= 0 && pos < str.length()) + return Value::fromInt32(str.at(pos).unicode()); + + return Value::fromDouble(qSNaN()); +} + +Value StringPrototype::method_concat(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) +{ + QString value = getThisString(parentCtx, thisObject); + + for (int i = 0; i < argc; ++i) { + Value v = __qmljs_to_string(argv[i], parentCtx); + assert(v.isString()); + value += v.stringValue()->toQString(); + } + + return Value::fromString(parentCtx, value); +} + +Value StringPrototype::method_indexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) +{ + QString value = getThisString(parentCtx, thisObject); + + QString searchString; + if (argc) + searchString = argv[0].toString(parentCtx)->toQString(); + + int pos = 0; + if (argc > 1) + pos = (int) argv[1].toInteger(parentCtx); + + int index = -1; + if (! value.isEmpty()) + index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); + + return Value::fromDouble(index); +} + +Value StringPrototype::method_lastIndexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) +{ + const QString value = getThisString(parentCtx, thisObject); + + QString searchString; + if (argc) { + Value v = __qmljs_to_string(argv[0], parentCtx); + searchString = v.stringValue()->toQString(); + } + + Value posArg = argc > 1 ? argv[1] : Value::undefinedValue(); + double position = __qmljs_to_number(posArg, parentCtx); + if (std::isnan(position)) + position = +qInf(); + else + position = trunc(position); + + int pos = trunc(qMin(qMax(position, 0.0), double(value.length()))); + if (!searchString.isEmpty() && pos == value.length()) + --pos; + int index = value.lastIndexOf(searchString, pos); + return Value::fromDouble(index); +} + +Value StringPrototype::method_localeCompare(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) +{ + const QString value = getThisString(parentCtx, thisObject); + const QString that = (argc ? argv[0] : Value::undefinedValue()).toString(parentCtx)->toQString(); + return Value::fromDouble(QString::localeAwareCompare(value, that)); +} + +Value StringPrototype::method_match(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) +{ + if (thisObject.isUndefined() || thisObject.isNull()) + __qmljs_throw_type_error(parentCtx); + + String *s = thisObject.toString(parentCtx); + + Value regexp = argc ? argv[0] : Value::undefinedValue(); + RegExpObject *rx = regexp.asRegExpObject(); + if (!rx) + rx = parentCtx->engine->regExpCtor.asFunctionObject()->construct(parentCtx, ®exp, 1).asRegExpObject(); + + if (!rx) + // ### CHECK + __qmljs_throw_type_error(parentCtx); + + bool global = rx->global; + + // ### use the standard builtin function, not the one that might be redefined in the proto + FunctionObject *exec = parentCtx->engine->regExpPrototype->__get__(parentCtx, parentCtx->engine->identifier(QStringLiteral("exec")), 0).asFunctionObject(); + + Value arg = Value::fromString(s); + if (!global) + return exec->call(parentCtx, Value::fromObject(rx), &arg, 1); + + String *lastIndex = parentCtx->engine->identifier(QStringLiteral("lastIndex")); + rx->__put__(parentCtx, lastIndex, Value::fromInt32(0)); + ArrayObject *a = parentCtx->engine->newArrayObject(parentCtx); + + double previousLastIndex = 0; + uint n = 0; + while (1) { + Value result = exec->call(parentCtx, Value::fromObject(rx), &arg, 1); + if (result.isNull()) + break; + assert(result.isObject()); + double thisIndex = rx->__get__(parentCtx, lastIndex, 0).toInteger(parentCtx); + if (previousLastIndex == thisIndex) { + previousLastIndex = thisIndex + 1; + rx->__put__(parentCtx, lastIndex, Value::fromDouble(previousLastIndex)); + } else { + previousLastIndex = thisIndex; + } + Value matchStr = result.objectValue()->__get__(parentCtx, (uint)0, (bool *)0); + a->array.set(n, matchStr); + ++n; + } + if (!n) + return Value::nullValue(); + + return Value::fromObject(a); + +} + +static QString makeReplacementString(const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) +{ + QString result; + result.reserve(replaceValue.length()); + for (int i = 0; i < replaceValue.length(); ++i) { + if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) { + char ch = replaceValue.at(++i).toLatin1(); + uint substStart = JSC::Yarr::offsetNoMatch; + uint substEnd = JSC::Yarr::offsetNoMatch; + if (ch == '$') { + result += ch; + continue; + } else if (ch == '&') { + substStart = matchOffsets[0]; + substEnd = matchOffsets[1]; + } else if (ch == '`') { + substStart = 0; + substEnd = matchOffsets[0]; + } else if (ch == '\'') { + substStart = matchOffsets[1]; + substEnd = input.length(); + } else if (ch >= '1' && ch <= '9') { + char capture = ch - '0'; + if (capture > 0 && capture < captureCount) { + substStart = matchOffsets[capture * 2]; + substEnd = matchOffsets[capture * 2 + 1]; + } + } else if (ch == '0' && i < replaceValue.length() - 1) { + int capture = (ch - '0') * 10; + ch = replaceValue.at(++i).toLatin1(); + capture += ch - '0'; + if (capture > 0 && capture < captureCount) { + substStart = matchOffsets[capture * 2]; + substEnd = matchOffsets[capture * 2 + 1]; + } + } + if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch) + result += input.midRef(substStart, substEnd - substStart); + } else { + result += replaceValue.at(i); + } + } + return result; +} + +Value StringPrototype::method_replace(ExecutionContext *ctx) +{ + QString string; + if (StringObject *thisString = ctx->thisObject.asStringObject()) + string = thisString->value.stringValue()->toQString(); + else + string = ctx->thisObject.toString(ctx)->toQString(); + + int numCaptures = 0; + QVarLengthArray matchOffsets; + int numStringMatches = 0; + + Value searchValue = ctx->argument(0); + RegExpObject *regExp = searchValue.asRegExpObject(); + if (regExp) { + uint offset = 0; + while (true) { + int oldSize = matchOffsets.size(); + matchOffsets.resize(matchOffsets.size() + regExp->value->captureCount() * 2); + if (regExp->value->match(string, offset, matchOffsets.data() + oldSize) == JSC::Yarr::offsetNoMatch) { + matchOffsets.resize(oldSize); + break; + } + if (!regExp->global) + break; + offset = qMax(offset + 1, matchOffsets[oldSize + 1]); + } + if (regExp->global) + regExp->lastIndexProperty->value = Value::fromUInt32(0); + numStringMatches = matchOffsets.size() / (regExp->value->captureCount() * 2); + numCaptures = regExp->value->captureCount(); + } else { + numCaptures = 1; + QString searchString = searchValue.toString(ctx)->toQString(); + int idx = string.indexOf(searchString); + if (idx != -1) { + numStringMatches = 1; + matchOffsets.resize(2); + matchOffsets[0] = idx; + matchOffsets[1] = idx + searchString.length(); + } + } + + QString result = string; + Value replaceValue = ctx->argument(1); + if (FunctionObject* searchCallback = replaceValue.asFunctionObject()) { + int replacementDelta = 0; + int argc = numCaptures + 2; + Value *args = (Value*)alloca((numCaptures + 2) * sizeof(Value)); + for (int i = 0; i < numStringMatches; ++i) { + for (int k = 0; k < numCaptures; ++k) { + int idx = (i * numCaptures + k) * 2; + uint start = matchOffsets[idx]; + uint end = matchOffsets[idx + 1]; + Value entry = Value::undefinedValue(); + if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch) + entry = Value::fromString(ctx, string.mid(start, end - start)); + args[k] = entry; + } + uint matchStart = matchOffsets[i * numCaptures * 2]; + uint matchEnd = matchOffsets[i * numCaptures * 2 + 1]; + args[numCaptures] = Value::fromUInt32(matchStart); + args[numCaptures + 1] = Value::fromString(ctx, string); + Value replacement = searchCallback->call(ctx, Value::undefinedValue(), args, argc); + QString replacementString = replacement.toString(ctx)->toQString(); + result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacementString); + replacementDelta += replacementString.length() - matchEnd + matchStart; + } + } else { + QString newString = replaceValue.toString(ctx)->toQString(); + int replacementDelta = 0; + + for (int i = 0; i < numStringMatches; ++i) { + int baseIndex = i * numCaptures * 2; + uint matchStart = matchOffsets[baseIndex]; + uint matchEnd = matchOffsets[baseIndex + 1]; + if (matchStart == JSC::Yarr::offsetNoMatch) + continue; + + QString replacement = makeReplacementString(string, newString, matchOffsets.data() + baseIndex, numCaptures); + result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacement); + replacementDelta += replacement.length() - matchEnd + matchStart; + } + } + + return Value::fromString(ctx, result); +} + +Value StringPrototype::method_search(ExecutionContext *ctx) +{ + QString string; + if (StringObject *thisString = ctx->thisObject.asStringObject()) + string = thisString->value.stringValue()->toQString(); + else + string = ctx->thisObject.toString(ctx)->toQString(); + + Value regExpValue = ctx->argument(0); + RegExpObject *regExp = regExpValue.asRegExpObject(); + if (!regExp) { + regExpValue = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ®ExpValue, 1); + regExp = regExpValue.asRegExpObject(); + } + uint* matchOffsets = (uint*)alloca(regExp->value->captureCount() * 2 * sizeof(uint)); + uint result = regExp->value->match(string, /*offset*/0, matchOffsets); + if (result == JSC::Yarr::offsetNoMatch) + return Value::fromInt32(-1); + return Value::fromUInt32(result); +} + +Value StringPrototype::method_slice(ExecutionContext *ctx) +{ + const QString text = getThisString(ctx); + const double length = text.length(); + + double start = ctx->argument(0).toInteger(ctx); + double end = ctx->argument(1).isUndefined() + ? length : ctx->argument(1).toInteger(ctx); + + if (start < 0) + start = qMax(length + start, 0.); + else + start = qMin(start, length); + + if (end < 0) + end = qMax(length + end, 0.); + else + end = qMin(end, length); + + const int intStart = int(start); + const int intEnd = int(end); + + int count = qMax(0, intEnd - intStart); + return Value::fromString(ctx, text.mid(intStart, count)); +} + +Value StringPrototype::method_split(ExecutionContext *ctx) +{ + QString text; + if (StringObject *thisObject = ctx->thisObject.asStringObject()) + text = thisObject->value.stringValue()->toQString(); + else + text = ctx->thisObject.toString(ctx)->toQString(); + + Value separatorValue = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); + Value limitValue = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); + + ArrayObject* array = ctx->engine->newArrayObject(ctx); + Value result = Value::fromObject(array); + + if (separatorValue.isUndefined()) { + if (limitValue.isUndefined()) { + array->array.push_back(Value::fromString(ctx, text)); + return result; + } + return Value::fromString(ctx, text.left(limitValue.toInteger(ctx))); + } + + uint limit = limitValue.isUndefined() ? UINT_MAX : limitValue.toUInt32(ctx); + + if (limit == 0) + return result; + + if (RegExpObject* re = separatorValue.asRegExpObject()) { + if (re->value->pattern().isEmpty()) { + re = 0; + separatorValue = Value::fromString(ctx, QString()); + } + } + + if (RegExpObject* re = separatorValue.asRegExpObject()) { + uint offset = 0; + uint* matchOffsets = (uint*)alloca(re->value->captureCount() * 2 * sizeof(uint)); + while (true) { + uint result = re->value->match(text, offset, matchOffsets); + if (result == JSC::Yarr::offsetNoMatch) + break; + + array->array.push_back(Value::fromString(ctx, text.mid(offset, matchOffsets[0] - offset))); + offset = qMax(offset + 1, matchOffsets[1]); + + if (array->array.length() >= limit) + break; + + for (int i = 1; i < re->value->captureCount(); ++i) { + uint start = matchOffsets[i * 2]; + uint end = matchOffsets[i * 2 + 1]; + array->array.push_back(Value::fromString(ctx, text.mid(start, end - start))); + if (array->array.length() >= limit) + break; + } + } + if (array->array.length() < limit) + array->array.push_back(Value::fromString(ctx, text.mid(offset))); + } else { + QString separator = separatorValue.toString(ctx)->toQString(); + if (separator.isEmpty()) { + for (uint i = 0; i < qMin(limit, uint(text.length())); ++i) + array->array.push_back(Value::fromString(ctx, text.mid(i, 1))); + return result; + } + + int start = 0; + int end; + while ((end = text.indexOf(separator, start)) != -1) { + array->array.push_back(Value::fromString(ctx, text.mid(start, end - start))); + start = end + separator.size(); + if (array->array.length() >= limit) + break; + } + if (array->array.length() < limit && start != -1) + array->array.push_back(Value::fromString(ctx, text.mid(start))); + } + return result; +} + +Value StringPrototype::method_substr(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) +{ + const QString value = getThisString(parentCtx, thisObject); + + double start = 0; + if (argc > 0) + start = argv[0].toInteger(parentCtx); + + double length = +qInf(); + if (argc > 1) + length = argv[1].toInteger(parentCtx); + + double count = value.length(); + if (start < 0) + start = qMax(count + start, 0.0); + + length = qMin(qMax(length, 0.0), count - start); + + qint32 x = Value::toInt32(start); + qint32 y = Value::toInt32(length); + return Value::fromString(parentCtx, value.mid(x, y)); +} + +Value StringPrototype::method_substring(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) +{ + QString value = getThisString(parentCtx, thisObject); + int length = value.length(); + + double start = 0; + double end = length; + + if (argc > 0) + start = argv[0].toInteger(parentCtx); + + Value endValue = argc > 1 ? argv[1] : Value::undefinedValue(); + if (!endValue.isUndefined()) + end = endValue.toInteger(parentCtx); + + if (std::isnan(start) || start < 0) + start = 0; + + if (std::isnan(end) || end < 0) + end = 0; + + if (start > length) + start = length; + + if (end > length) + end = length; + + if (start > end) { + double was = start; + start = end; + end = was; + } + + qint32 x = (int)start; + qint32 y = (int)(end - start); + return Value::fromString(parentCtx, value.mid(x, y)); +} + +Value StringPrototype::method_toLowerCase(ExecutionContext *ctx) +{ + QString value = getThisString(ctx); + return Value::fromString(ctx, value.toLower()); +} + +Value StringPrototype::method_toLocaleLowerCase(ExecutionContext *ctx) +{ + return method_toLowerCase(ctx); +} + +Value StringPrototype::method_toUpperCase(ExecutionContext *ctx) +{ + QString value = getThisString(ctx); + return Value::fromString(ctx, value.toUpper()); +} + +Value StringPrototype::method_toLocaleUpperCase(ExecutionContext *ctx) +{ + return method_toUpperCase(ctx); +} + +Value StringPrototype::method_fromCharCode(ExecutionContext *parentCtx, Value, Value *argv, int argc) +{ + QString str(argc, Qt::Uninitialized); + QChar *ch = str.data(); + for (int i = 0; i < argc; ++i) { + *ch = QChar(argv[i].toUInt16(parentCtx)); + ++ch; + } + return Value::fromString(parentCtx, str); +} + +Value StringPrototype::method_trim(ExecutionContext *ctx) +{ + if (ctx->thisObject.isNull() || ctx->thisObject.isUndefined()) + __qmljs_throw_type_error(ctx); + + QString s = __qmljs_to_string(ctx->thisObject, ctx).stringValue()->toQString(); + const QChar *chars = s.constData(); + int start, end; + for (start = 0; start < s.length(); ++start) { + if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff) + break; + } + for (end = s.length() - 1; end >= start; --end) { + if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff) + break; + } + + return Value::fromString(ctx, QString(chars + start, end - start + 1)); +} diff --git a/src/v4/qv4stringobject.h b/src/v4/qv4stringobject.h new file mode 100644 index 0000000000..1becd97a46 --- /dev/null +++ b/src/v4/qv4stringobject.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STRINGOBJECT_P_H +#define QV4STRINGOBJECT_P_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include + +namespace QQmlJS { +namespace VM { + +struct StringObject: Object { + Value value; + PropertyDescriptor tmpProperty; + StringObject(ExecutionContext *ctx, const Value &value); + + PropertyDescriptor *getIndex(ExecutionContext *ctx, uint index); + +protected: + virtual void markObjects(); +}; + +struct StringCtor: FunctionObject +{ + StringCtor(ExecutionContext *scope); + + virtual Value construct(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx); +}; + +struct StringPrototype: StringObject +{ + StringPrototype(ExecutionContext *ctx): StringObject(ctx, Value::fromString(ctx, QString())) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_charAt(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_charCodeAt(ExecutionContext *, Value thisObject, Value *argv, int argc); + static Value method_concat(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_indexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_lastIndexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_localeCompare(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_match(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_replace(ExecutionContext *ctx); + static Value method_search(ExecutionContext *ctx); + static Value method_slice(ExecutionContext *ctx); + static Value method_split(ExecutionContext *ctx); + static Value method_substr(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_substring(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_toLowerCase(ExecutionContext *ctx); + static Value method_toLocaleLowerCase(ExecutionContext *ctx); + static Value method_toUpperCase(ExecutionContext *ctx); + static Value method_toLocaleUpperCase(ExecutionContext *ctx); + static Value method_fromCharCode(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_trim(ExecutionContext *ctx); +}; + +} // end of namespace VM +} // end of namespace QQmlJS + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/v4/qv4syntaxchecker.cpp b/src/v4/qv4syntaxchecker.cpp new file mode 100644 index 0000000000..fcda486af2 --- /dev/null +++ b/src/v4/qv4syntaxchecker.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4syntaxchecker_p.h" + +using namespace QQmlJS; + +SyntaxChecker::SyntaxChecker() + : Lexer(&m_engine) + , m_stateStack(128) +{ +} + +void QQmlJS::SyntaxChecker::clearText() +{ + m_code.clear(); + m_tokens.clear(); +} + +void SyntaxChecker::appendText(const QString &text) +{ + m_code += text; +} + +QString SyntaxChecker::text() const +{ + return m_code; +} + +bool SyntaxChecker::canEvaluate() +{ + int yyaction = 0; + int yytoken = -1; + int yytos = -1; + + setCode(m_code, 1); + + m_tokens.clear(); + m_tokens.append(T_FEED_JS_PROGRAM); + + do { + if (++yytos == m_stateStack.size()) + m_stateStack.resize(m_stateStack.size() * 2); + + m_stateStack[yytos] = yyaction; + +again: + if (yytoken == -1 && action_index[yyaction] != -TERMINAL_COUNT) { + if (m_tokens.isEmpty()) + yytoken = lex(); + else + yytoken = m_tokens.takeFirst(); + } + + yyaction = t_action(yyaction, yytoken); + if (yyaction > 0) { + if (yyaction == ACCEPT_STATE) { + --yytos; + return true; + } + yytoken = -1; + } else if (yyaction < 0) { + const int ruleno = -yyaction - 1; + yytos -= rhs[ruleno]; + yyaction = nt_action(m_stateStack[yytos], lhs[ruleno] - TERMINAL_COUNT); + } + } while (yyaction); + + const int errorState = m_stateStack[yytos]; + if (t_action(errorState, T_AUTOMATIC_SEMICOLON) && canInsertAutomaticSemicolon(yytoken)) { + yyaction = errorState; + m_tokens.prepend(yytoken); + yytoken = T_SEMICOLON; + goto again; + } + + if (yytoken != EOF_SYMBOL) + return true; + + return false; +} diff --git a/src/v4/qv4syntaxchecker_p.h b/src/v4/qv4syntaxchecker_p.h new file mode 100644 index 0000000000..38e123762e --- /dev/null +++ b/src/v4/qv4syntaxchecker_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4SYNTAXCHECKER_P_H +#define QV4SYNTAXCHECKER_P_H + +#include +#include + +#include +#include +#include + +namespace QQmlJS { + +class SyntaxChecker: Lexer +{ +public: + SyntaxChecker(); + + QString text() const; + void clearText(); + void appendText(const QString &text); + + bool canEvaluate(); + +private: + Engine m_engine; + QVector m_stateStack; + QList m_tokens; + QString m_code; +}; + +} // end of QQmlJS namespace + +#endif // QV4SYNTAXCHECKER_P_H diff --git a/src/v4/v4.pro b/src/v4/v4.pro new file mode 100644 index 0000000000..6d990ed234 --- /dev/null +++ b/src/v4/v4.pro @@ -0,0 +1,155 @@ +TARGET = QtV4 +QT_PRIVATE = core-private qmldevtools-private + +CONFIG += internal_module + +LLVM_CONFIG=llvm-config + +OBJECTS_DIR=.obj + +load(qt_build_config) +load(qt_module) + +CONFIG += warn_off + +#win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x66000000 #TODO ??! + + +# Pick up the qmake variable or environment variable for LLVM_INSTALL_DIR. If either was set, change the LLVM_CONFIG to use that. +isEmpty(LLVM_INSTALL_DIR):LLVM_INSTALL_DIR=$$(LLVM_INSTALL_DIR) +!isEmpty(LLVM_INSTALL_DIR):LLVM_CONFIG=$$LLVM_INSTALL_DIR/bin/llvm-config + +!macx-clang*:LIBS += -rdynamic + +SOURCES += \ + qv4codegen.cpp \ + qv4ir.cpp \ + qmljs_engine.cpp \ + qmljs_environment.cpp \ + qmljs_runtime.cpp \ + qmljs_value.cpp \ + qv4syntaxchecker.cpp \ + qv4isel_masm.cpp \ + llvm_runtime.cpp \ + qv4isel_p.cpp \ + debugging.cpp \ + qv4mm.cpp \ + qv4managed.cpp \ + qv4array.cpp \ + qv4arrayobject.cpp \ + qv4argumentsobject.cpp \ + qv4booleanobject.cpp \ + qv4dateobject.cpp \ + qv4errorobject.cpp \ + qv4functionobject.cpp \ + qv4globalobject.cpp \ + qv4jsonobject.cpp \ + qv4mathobject.cpp \ + qv4numberobject.cpp \ + qv4object.cpp \ + qv4objectproto.cpp \ + qv4regexpobject.cpp \ + qv4stringobject.cpp \ + qv4string.cpp \ + qv4objectiterator.cpp \ + qv4regexp.cpp + +HEADERS += \ + qv4global.h \ + qv4codegen_p.h \ + qv4ir_p.h \ + qmljs_engine.h \ + qmljs_environment.h \ + qmljs_runtime.h \ + qmljs_math.h \ + qmljs_value.h \ + qv4syntaxchecker_p.h \ + qv4isel_masm_p.h \ + qv4isel_p.h \ + qv4isel_util_p.h \ + debugging.h \ + qv4mm.h \ + qv4managed.h \ + qv4array.h \ + qv4arrayobject.h \ + qv4argumentsobject.h \ + qv4booleanobject.h \ + qv4dateobject.h \ + qv4errorobject.h \ + qv4functionobject.h \ + qv4globalobject.h \ + qv4jsonobject.h \ + qv4mathobject.h \ + qv4numberobject.h \ + qv4object.h \ + qv4objectproto.h \ + qv4regexpobject.h \ + qv4stringobject.h \ + qv4string.h \ + qv4propertydescriptor.h \ + qv4propertytable.h \ + qv4objectiterator.h \ + qv4regexp.h + +llvm { + +SOURCES += \ + qv4isel_llvm.cpp + +HEADERS += \ + qv4isel_llvm_p.h \ + qv4_llvm_p.h + +LLVM_RUNTIME_BC = $$PWD/llvm_runtime.bc +DEFINES += LLVM_RUNTIME="\"\\\"$$LLVM_RUNTIME_BC\\\"\"" + +INCLUDEPATH += \ + $$system($$LLVM_CONFIG --includedir) + +QMAKE_CXXFLAGS += $$system($$LLVM_CONFIG --cppflags) -fvisibility-inlines-hidden +QMAKE_CXXFLAGS -= -pedantic +QMAKE_CXXFLAGS -= -Wcovered-switch-default + +LIBS += \ + $$system($$LLVM_CONFIG --ldflags) \ + $$system($$LLVM_CONFIG --libs core jit bitreader linker ipo target x86 arm native) + +QMAKE_EXTRA_TARGETS += gen_llvm_runtime + +GEN_LLVM_RUNTIME_FLAGS = $$system($$LLVM_CONFIG --cppflags) +GEN_LLVM_RUNTIME_FLAGS -= -pedantic + +gen_llvm_runtime.target = llvm_runtime +gen_llvm_runtime.commands = clang -O2 -emit-llvm -c $(INCPATH) $$GEN_LLVM_RUNTIME_FLAGS -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o $$LLVM_RUNTIME_BC + + +} else { + +DEFINES += QMLJS_NO_LLVM + +} + +# Use SSE2 floating point math on 32 bit instead of the default +# 387 to make test results pass on 32 and on 64 bit builds. +linux-g++*:isEqual(QT_ARCH,i386) { + QMAKE_CFLAGS += -march=pentium4 -msse2 -mfpmath=sse + QMAKE_CXXFLAGS += -march=pentium4 -msse2 -mfpmath=sse +} + +TESTSCRIPT=$$PWD/../../tests/test262.py +V4CMD = $$OUT_PWD/../tools/v4 + +checktarget.target = check +checktarget.commands = python $$TESTSCRIPT --command=$$V4CMD --parallel --with-test-expectations --update-expectations +checktarget.depends = all +QMAKE_EXTRA_TARGETS += checktarget + +checkmothtarget.target = check-interpreter +checkmothtarget.commands = python $$TESTSCRIPT --command=\"$$V4CMD --interpret\" --parallel --with-test-expectations +checkmothtarget.depends = all +QMAKE_EXTRA_TARGETS += checkmothtarget + + +include(moth/moth.pri) +include(../3rdparty/masm/masm.pri) +include(../3rdparty/double-conversion/double-conversion.pri) diff --git a/sync.profile b/sync.profile new file mode 100644 index 0000000000..4e6f5f7cff --- /dev/null +++ b/sync.profile @@ -0,0 +1,20 @@ +%modules = ( # path to module name map + "QtV4" => "$basedir/src", +); +%moduleheaders = ( # restrict the module headers to those found in relative path + #"QtV4" => "3rdparty/masm;v4;", +); +@allmoduleheadersprivate = ( + "QtV4" +); + +# Module dependencies. +# Every module that is required to build this module should have one entry. +# Each of the module version specifiers can take one of the following values: +# - A specific Git revision. +# - any git symbolic ref resolvable from the module's repository (e.g. "refs/heads/master" to track master branch) +# +%dependencies = ( + "qtbase" => "refs/heads/dev", + "qtdeclarative" => "refs/heads/dev", +); diff --git a/v4.pro b/v4.pro deleted file mode 100644 index 12e9013c6c..0000000000 --- a/v4.pro +++ /dev/null @@ -1,145 +0,0 @@ -QT = core-private qmldevtools-private -CONFIG -= app_bundle -CONFIG += console - -LLVM_CONFIG=llvm-config - -OBJECTS_DIR=.obj - -# Pick up the qmake variable or environment variable for LLVM_INSTALL_DIR. If either was set, change the LLVM_CONFIG to use that. -isEmpty(LLVM_INSTALL_DIR):LLVM_INSTALL_DIR=$$(LLVM_INSTALL_DIR) -!isEmpty(LLVM_INSTALL_DIR):LLVM_CONFIG=$$LLVM_INSTALL_DIR/bin/llvm-config - -!macx-clang*:LIBS += -rdynamic - -SOURCES += main.cpp \ - qv4codegen.cpp \ - qv4ir.cpp \ - qmljs_engine.cpp \ - qmljs_environment.cpp \ - qmljs_runtime.cpp \ - qmljs_value.cpp \ - qv4syntaxchecker.cpp \ - qv4isel_masm.cpp \ - llvm_runtime.cpp \ - qv4isel_p.cpp \ - debugging.cpp \ - qv4mm.cpp \ - qv4managed.cpp \ - qv4array.cpp \ - qv4arrayobject.cpp \ - qv4argumentsobject.cpp \ - qv4booleanobject.cpp \ - qv4dateobject.cpp \ - qv4errorobject.cpp \ - qv4functionobject.cpp \ - qv4globalobject.cpp \ - qv4jsonobject.cpp \ - qv4mathobject.cpp \ - qv4numberobject.cpp \ - qv4object.cpp \ - qv4objectproto.cpp \ - qv4regexpobject.cpp \ - qv4stringobject.cpp \ - qv4string.cpp \ - qv4objectiterator.cpp \ - qv4regexp.cpp - -HEADERS += \ - qv4codegen_p.h \ - qv4ir_p.h \ - qmljs_engine.h \ - qmljs_environment.h \ - qmljs_runtime.h \ - qmljs_math.h \ - qmljs_value.h \ - qv4syntaxchecker_p.h \ - qv4isel_masm_p.h \ - qv4isel_p.h \ - qv4isel_util_p.h \ - debugging.h \ - qv4mm.h \ - qv4managed.h \ - qv4array.h \ - qv4arrayobject.h \ - qv4argumentsobject.h \ - qv4booleanobject.h \ - qv4dateobject.h \ - qv4errorobject.h \ - qv4functionobject.h \ - qv4globalobject.h \ - qv4jsonobject.h \ - qv4mathobject.h \ - qv4numberobject.h \ - qv4object.h \ - qv4objectproto.h \ - qv4regexpobject.h \ - qv4stringobject.h \ - qv4string.h \ - qv4propertydescriptor.h \ - qv4propertytable.h \ - qv4objectiterator.h \ - qv4regexp.h - -llvm { - -SOURCES += \ - qv4isel_llvm.cpp - -HEADERS += \ - qv4isel_llvm_p.h \ - qv4_llvm_p.h - -LLVM_RUNTIME_BC = $$PWD/llvm_runtime.bc -DEFINES += LLVM_RUNTIME="\"\\\"$$LLVM_RUNTIME_BC\\\"\"" - -INCLUDEPATH += \ - $$system($$LLVM_CONFIG --includedir) - -QMAKE_CXXFLAGS += $$system($$LLVM_CONFIG --cppflags) -fvisibility-inlines-hidden -QMAKE_CXXFLAGS -= -pedantic -QMAKE_CXXFLAGS -= -Wcovered-switch-default - -LIBS += \ - $$system($$LLVM_CONFIG --ldflags) \ - $$system($$LLVM_CONFIG --libs core jit bitreader linker ipo target x86 arm native) - -QMAKE_EXTRA_TARGETS += gen_llvm_runtime - -GEN_LLVM_RUNTIME_FLAGS = $$system($$LLVM_CONFIG --cppflags) -GEN_LLVM_RUNTIME_FLAGS -= -pedantic - -gen_llvm_runtime.target = llvm_runtime -gen_llvm_runtime.commands = clang -O2 -emit-llvm -c $(INCPATH) $$GEN_LLVM_RUNTIME_FLAGS -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o $$LLVM_RUNTIME_BC - - -} else { - -DEFINES += QMLJS_NO_LLVM - -} - -# Use SSE2 floating point math on 32 bit instead of the default -# 387 to make test results pass on 32 and on 64 bit builds. -linux-g++*:isEqual(QT_ARCH,i386) { - QMAKE_CFLAGS += -march=pentium4 -msse2 -mfpmath=sse - QMAKE_CXXFLAGS += -march=pentium4 -msse2 -mfpmath=sse -} - -TESTSCRIPT=$$PWD/tests/test262.py -V4CMD = $$OUT_PWD/v4 - -checktarget.target = check -checktarget.commands = python $$TESTSCRIPT --command=$$V4CMD --parallel --with-test-expectations --update-expectations -checktarget.depends = all -QMAKE_EXTRA_TARGETS += checktarget - -checkmothtarget.target = check-interpreter -checkmothtarget.commands = python $$TESTSCRIPT --command=\"$$V4CMD --interpret\" --parallel --with-test-expectations -checkmothtarget.depends = all -QMAKE_EXTRA_TARGETS += checkmothtarget - - -include(moth/moth.pri) -include(3rdparty/masm/masm.pri) -include(3rdparty/double-conversion/double-conversion.pri) diff --git a/v4vm.pro b/v4vm.pro new file mode 100644 index 0000000000..0c67e6684a --- /dev/null +++ b/v4vm.pro @@ -0,0 +1,2 @@ +load(configure) +load(qt_parts) -- cgit v1.2.3 From 830b27afaa24740382b44b18df9ce02d27d9d59c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Wed, 30 Jan 2013 13:26:12 +0100 Subject: Build fixup, QT_BEGIN_HEADER was removed. Change-Id: I97b33e4c317aac53cfff1c7ef6f1a04bca9434ba Reviewed-by: Lars Knoll --- src/v4/qv4global.h | 4 ---- src/v4/qv4ir_p.h | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/v4/qv4global.h b/src/v4/qv4global.h index 6b1c9a21ec..e6150b0c9c 100644 --- a/src/v4/qv4global.h +++ b/src/v4/qv4global.h @@ -32,8 +32,6 @@ #include -QT_BEGIN_HEADER - #ifndef QT_STATIC # if defined(QT_BUILD_V4_LIB) # define Q_V4_EXPORT Q_DECL_EXPORT @@ -44,6 +42,4 @@ QT_BEGIN_HEADER # define Q_V4_EXPORT #endif -QT_END_NAMESPACE - #endif // QV4GLOBAL_H diff --git a/src/v4/qv4ir_p.h b/src/v4/qv4ir_p.h index 4e60fa96dd..8ad783c92e 100644 --- a/src/v4/qv4ir_p.h +++ b/src/v4/qv4ir_p.h @@ -59,8 +59,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QTextStream; @@ -719,6 +717,4 @@ struct BasicBlock { QT_END_NAMESPACE -QT_END_HEADER - #endif // QV4IR_P_H -- cgit v1.2.3 From e199714a287e18695d9f370ae23593f06648ca86 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 13:31:35 +0100 Subject: Fix compilation on 64bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I044a7aa1b52aa3df86a22db706d05ffd6a8d2e71 Reviewed-by: JÄ™drzej Nowacki --- src/tools/tools.pro | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/tools.pro b/src/tools/tools.pro index 820eb18769..b08f2242b7 100644 --- a/src/tools/tools.pro +++ b/src/tools/tools.pro @@ -14,4 +14,5 @@ INCLUDEPATH += ../3rdparty/masm/assembler INCLUDEPATH += ../3rdparty/masm/disassembler DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" -DEFINES += QMLJS_NO_LLVM \ No newline at end of file +DEFINES += QMLJS_NO_LLVM +DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 -- cgit v1.2.3 From 42adbb7da686a85b836bf12737d2079488242563 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 09:44:15 +0100 Subject: Optimise decode/encodeURI The code contained some needless creation of temporary QStrings, that were taking up ~50% of the time spent in these methods. Change-Id: Iba494e5e92c08a0056d6eb6a947d080209aa7ce5 Reviewed-by: Simon Hausmann --- src/v4/qv4globalobject.cpp | 56 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp index 74cd32d88a..70ae41c3ed 100644 --- a/src/v4/qv4globalobject.cpp +++ b/src/v4/qv4globalobject.cpp @@ -147,8 +147,9 @@ static QString unescape(const QString &input) return result; } -static const char uriReserved[] = ";/?:@&=+$,"; -static const char uriUnescaped[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()"; +static const char uriReserved[] = ";/?:@&=+$,#"; +static const char uriUnescaped[] = "-_.!~*'()"; +static const char uriUnescapedReserved[] = "-_.!~*'();/?:@&=+$,#"; static void addEscapeSequence(QString &output, uchar ch) { @@ -157,7 +158,7 @@ static void addEscapeSequence(QString &output, uchar ch) output.append(QLatin1Char(toHex(ch & 0xf))); } -static QString encode(const QString &input, const QString &unescapedSet, bool *ok) +static QString encode(const QString &input, const char *unescapedSet, bool *ok) { *ok = true; QString output; @@ -165,7 +166,22 @@ static QString encode(const QString &input, const QString &unescapedSet, bool *o int i = 0; while (i < length) { const QChar c = input.at(i); - if (!unescapedSet.contains(c)) { + bool escape = true; + if ((c.unicode() >= 'a' && c.unicode() <= 'z') || + (c.unicode() >= 'A' && c.unicode() <= 'Z') || + (c.unicode() >= '0' && c.unicode() <= '9')) { + escape = false; + } else { + const char *r = unescapedSet; + while (*r) { + if (*r == c.unicode()) { + escape = false; + break; + } + ++r; + } + } + if (escape) { uint uc = c.unicode(); if ((uc >= 0xDC00) && (uc <= 0xDFFF)) { *ok = false; @@ -211,10 +227,16 @@ static QString encode(const QString &input, const QString &unescapedSet, bool *o return output; } -static QString decode(const QString &input, const QString &reservedSet, bool *ok) +enum DecodeMode { + DecodeAll, + DecodeNonReserved +}; + +static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) { *ok = true; QString output; + output.reserve(input.length()); const int length = input.length(); int i = 0; const QChar percent = QLatin1Char('%'); @@ -287,11 +309,19 @@ static QString decode(const QString &input, const QString &reservedSet, bool *ok output.append(QChar(l)); } } else { - QChar z(b); - if (!reservedSet.contains(z)) { - output.append(z); + if (decodeMode == DecodeNonReserved && b <= 0x40) { + const char *r = uriReserved; + while (*r) { + if (*r == b) + break; + ++r; + } + if (*r) + output.append(input.mid(start, i - start + 1)); + else + output.append(QChar(b)); } else { - output.append(input.mid(start, i - start + 1)); + output.append(QChar(b)); } } } else { @@ -599,7 +629,7 @@ Value GlobalFunctions::method_decodeURI(ExecutionContext *parentCtx, Value, Valu QString uriString = argv[0].toString(parentCtx)->toQString(); bool ok; - QString out = decode(uriString, QString::fromUtf8(uriReserved) + QString::fromUtf8("#"), &ok); + QString out = decode(uriString, DecodeNonReserved, &ok); if (!ok) parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); @@ -614,7 +644,7 @@ Value GlobalFunctions::method_decodeURIComponent(ExecutionContext *parentCtx, Va QString uriString = argv[0].toString(parentCtx)->toQString(); bool ok; - QString out = decode(uriString, QString(), &ok); + QString out = decode(uriString, DecodeAll, &ok); if (!ok) parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); @@ -629,7 +659,7 @@ Value GlobalFunctions::method_encodeURI(ExecutionContext *parentCtx, Value, Valu QString uriString = argv[0].toString(parentCtx)->toQString(); bool ok; - QString out = encode(uriString, QLatin1String(uriReserved) + QLatin1String(uriUnescaped) + QString::fromUtf8("#"), &ok); + QString out = encode(uriString, uriUnescapedReserved, &ok); if (!ok) parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); @@ -644,7 +674,7 @@ Value GlobalFunctions::method_encodeURIComponent(ExecutionContext *parentCtx, Va QString uriString = argv[0].toString(parentCtx)->toQString(); bool ok; - QString out = encode(uriString, QString(uriUnescaped), &ok); + QString out = encode(uriString, uriUnescaped, &ok); if (!ok) parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence"))); -- cgit v1.2.3 From 3e8634bfccb8a5947edc1c075678e735f0c01825 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 11:04:45 +0100 Subject: Move string hash and error type fields Move the string hash back into the String class. Add a subtype field to Managed, and use that to store the type of the ErrorObject. Change-Id: I4aa8f1f39f512e3a9084b249e173bff41175d543 Reviewed-by: Simon Hausmann --- src/v4/qv4errorobject.cpp | 22 +++++++++++----------- src/v4/qv4errorobject.h | 1 - src/v4/qv4managed.cpp | 2 +- src/v4/qv4managed.h | 10 +++++++--- src/v4/qv4string.h | 4 +++- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/v4/qv4errorobject.cpp b/src/v4/qv4errorobject.cpp index 9d4a067f35..09f7f8e4e5 100644 --- a/src/v4/qv4errorobject.cpp +++ b/src/v4/qv4errorobject.cpp @@ -74,9 +74,9 @@ using namespace QQmlJS::VM; ErrorObject::ErrorObject(ExecutionEngine* engine, const Value &message) - : errorType(Error) { type = Type_ErrorObject; + subtype = Error; if (message.type() != Value::Undefined_Type) defineDefaultProperty(engine->identifier(QStringLiteral("message")), message); @@ -91,7 +91,7 @@ SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *m : ErrorObject(ctx->engine, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0)) , msg(message) { - errorType = SyntaxError; + subtype = SyntaxError; prototype = ctx->engine->syntaxErrorPrototype; setNameProperty(ctx); } @@ -101,7 +101,7 @@ SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *m EvalErrorObject::EvalErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->engine, ctx->argument(0)) { - errorType = EvalError; + subtype = EvalError; setNameProperty(ctx); prototype = ctx->engine->evalErrorPrototype; } @@ -109,7 +109,7 @@ EvalErrorObject::EvalErrorObject(ExecutionContext *ctx) RangeErrorObject::RangeErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->engine, ctx->argument(0)) { - errorType = RangeError; + subtype = RangeError; setNameProperty(ctx); prototype = ctx->engine->rangeErrorPrototype; } @@ -117,7 +117,7 @@ RangeErrorObject::RangeErrorObject(ExecutionContext *ctx) RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &msg) : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) { - errorType = RangeError; + subtype = RangeError; setNameProperty(ctx); prototype = ctx->engine->rangeErrorPrototype; } @@ -125,7 +125,7 @@ RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &msg) ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->engine, ctx->argument(0)) { - errorType = ReferenceError; + subtype = ReferenceError; setNameProperty(ctx); prototype = ctx->engine->referenceErrorPrototype; } @@ -133,7 +133,7 @@ ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx) ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString &msg) : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) { - errorType = ReferenceError; + subtype = ReferenceError; setNameProperty(ctx); prototype = ctx->engine->referenceErrorPrototype; } @@ -141,7 +141,7 @@ ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString TypeErrorObject::TypeErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->engine, ctx->argument(0)) { - errorType = TypeError; + subtype = TypeError; setNameProperty(ctx); prototype = ctx->engine->typeErrorPrototype; } @@ -149,7 +149,7 @@ TypeErrorObject::TypeErrorObject(ExecutionContext *ctx) TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &msg) : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) { - errorType = TypeError; + subtype = TypeError; setNameProperty(ctx); prototype = ctx->engine->typeErrorPrototype; } @@ -157,7 +157,7 @@ TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &msg) URIErrorObject::URIErrorObject(ExecutionContext *ctx) : ErrorObject(ctx->engine, ctx->argument(0)) { - errorType = URIError; + subtype = URIError; setNameProperty(ctx); prototype = ctx->engine->uRIErrorPrototype; } @@ -165,7 +165,7 @@ URIErrorObject::URIErrorObject(ExecutionContext *ctx) URIErrorObject::URIErrorObject(ExecutionContext *ctx, Value msg) : ErrorObject(ctx->engine, msg) { - errorType = URIError; + subtype = URIError; setNameProperty(ctx); prototype = ctx->engine->uRIErrorPrototype; } diff --git a/src/v4/qv4errorobject.h b/src/v4/qv4errorobject.h index d2d2340fae..d158909474 100644 --- a/src/v4/qv4errorobject.h +++ b/src/v4/qv4errorobject.h @@ -57,7 +57,6 @@ struct ErrorObject: Object { TypeError, URIError }; - ErrorType errorType; ErrorObject(ExecutionEngine* engine, const Value &message); diff --git a/src/v4/qv4managed.cpp b/src/v4/qv4managed.cpp index 8156878508..e0448a4848 100644 --- a/src/v4/qv4managed.cpp +++ b/src/v4/qv4managed.cpp @@ -100,7 +100,7 @@ QString Managed::className() const s = "RegExp"; break; case Type_ErrorObject: - switch (static_cast(this)->errorType) { + switch (ErrorObject::ErrorType(subtype)) { case ErrorObject::Error: s = "Error"; break; diff --git a/src/v4/qv4managed.h b/src/v4/qv4managed.h index dc4d1f2243..8252fd6eaa 100644 --- a/src/v4/qv4managed.h +++ b/src/v4/qv4managed.h @@ -88,8 +88,8 @@ protected: , usesArgumentsObject(0) , strictMode(0) , type(Type_Invalid) + , subtype(0) , unused(0) - , stringHash(0) {} virtual ~Managed(); @@ -157,8 +157,12 @@ protected: quintptr usesArgumentsObject : 1; // used by FunctionObject quintptr strictMode : 1; // used by FunctionObject quintptr type : 8; - quintptr unused : 16; - mutable quintptr stringHash : 32; + quintptr subtype : 8; +#if CPU(X86_64) + quintptr unused : 40; +#else + quintptr unused : 8; +#endif }; }; diff --git a/src/v4/qv4string.h b/src/v4/qv4string.h index 34e241218c..958e6e108c 100644 --- a/src/v4/qv4string.h +++ b/src/v4/qv4string.h @@ -49,7 +49,8 @@ namespace VM { struct String : public Managed { String(const QString &text) - : _text(text) { type = Type_String; stringHash = InvalidHashValue; } + : _text(text), stringHash(0) + { type = Type_String; stringHash = InvalidHashValue; } inline bool isEqualTo(const String *other) const { if (this == other) @@ -90,6 +91,7 @@ private: void createHashValue() const; QString _text; + mutable uint stringHash; }; } -- cgit v1.2.3 From 73b2b4ac6402aa73f6ee1d10bd7a3be584c3caac Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 13:04:16 +0100 Subject: Add subtypes to String This allows to store the full array index in the string, as well as to optimise identifiers going forward. Change-Id: I08974a5ee1149869ecfb635932bf54026d2bbab4 Reviewed-by: Simon Hausmann --- src/v4/qv4managed.h | 12 +++++++----- src/v4/qv4string.cpp | 32 ++++++++++++-------------------- src/v4/qv4string.h | 28 +++++++++++++++++----------- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/v4/qv4managed.h b/src/v4/qv4managed.h index 8252fd6eaa..7f36c83239 100644 --- a/src/v4/qv4managed.h +++ b/src/v4/qv4managed.h @@ -89,7 +89,10 @@ protected: , strictMode(0) , type(Type_Invalid) , subtype(0) + , stringIdentifier(0) +#if CPU(X86_64) , unused(0) +#endif {} virtual ~Managed(); @@ -156,12 +159,11 @@ protected: quintptr needsActivation : 1; // used by FunctionObject quintptr usesArgumentsObject : 1; // used by FunctionObject quintptr strictMode : 1; // used by FunctionObject - quintptr type : 8; - quintptr subtype : 8; + quintptr type : 5; + mutable quintptr subtype : 3; + quintptr stringIdentifier : 16; #if CPU(X86_64) - quintptr unused : 40; -#else - quintptr unused : 8; + quintptr unused : 32; #endif }; }; diff --git a/src/v4/qv4string.cpp b/src/v4/qv4string.cpp index 6e0c14d108..cdadbe7049 100644 --- a/src/v4/qv4string.cpp +++ b/src/v4/qv4string.cpp @@ -46,8 +46,9 @@ namespace QQmlJS { namespace VM { -static uint toArrayIndex(const QChar *ch, const QChar *end) +static uint toArrayIndex(const QChar *ch, const QChar *end, bool *ok) { + *ok = false; uint i = ch->unicode() - '0'; if (i > 9) return String::InvalidArrayIndex; @@ -67,26 +68,17 @@ static uint toArrayIndex(const QChar *ch, const QChar *end) i = n; ++ch; } + *ok = true; return i; } -uint String::asArrayIndexSlow() const -{ - if (stringHash < LargestHashedArrayIndex) - return stringHash; - - const QChar *ch = _text.constData(); - const QChar *end = ch + _text.length(); - return toArrayIndex(ch, end); -} - uint String::toUInt(bool *ok) const { *ok = true; - if (stringHash == InvalidHashValue) + if (subtype == StringType_Unknown) createHashValue(); - if (stringHash < LargestHashedArrayIndex) + if (subtype == StringType_ArrayIndex) return stringHash; double d = __qmljs_string_to_number(this); @@ -102,11 +94,11 @@ void String::createHashValue() const const QChar *ch = _text.constData(); const QChar *end = ch + _text.length(); - // array indices get their number as hash value, for large numbers we set to INT_MAX - stringHash = toArrayIndex(ch, end); - if (stringHash < UINT_MAX) { - if (stringHash > INT_MAX) - stringHash = INT_MAX; + // array indices get their number as hash value + bool ok; + stringHash = toArrayIndex(ch, end, &ok); + if (ok) { + subtype = StringType_ArrayIndex; return; } @@ -116,8 +108,8 @@ void String::createHashValue() const ++ch; } - // set highest bit to mark it as a non number - stringHash = h | 0xf0000000; + stringHash = h; + subtype = StringType_Regular; } } diff --git a/src/v4/qv4string.h b/src/v4/qv4string.h index 958e6e108c..2df221efba 100644 --- a/src/v4/qv4string.h +++ b/src/v4/qv4string.h @@ -48,15 +48,25 @@ namespace QQmlJS { namespace VM { struct String : public Managed { + enum StringType { + StringType_Unknown, + StringType_ArrayIndex, + StringType_Regular, + StringType_Identifier, + }; + String(const QString &text) - : _text(text), stringHash(0) - { type = Type_String; stringHash = InvalidHashValue; } + : _text(text), stringHash(InvalidHashValue) + { type = Type_String; subtype = StringType_Unknown; } inline bool isEqualTo(const String *other) const { if (this == other) return true; - else if (other && hashValue() == other->hashValue()) + else if (hashValue() == other->hashValue()) { + if (subtype == StringType_ArrayIndex && other->subtype == StringType_ArrayIndex) + return true; return toQString() == other->toQString(); + } return false; } @@ -65,26 +75,22 @@ struct String : public Managed { } inline unsigned hashValue() const { - if (stringHash == InvalidHashValue) + if (subtype == StringType_Unknown) createHashValue(); return stringHash; } enum { InvalidArrayIndex = 0xffffffff, - LargestHashedArrayIndex = 0x7fffffff, InvalidHashValue = 0xffffffff }; uint asArrayIndex() const { - if (stringHash == InvalidHashValue) + if (subtype == StringType_Unknown) createHashValue(); - if (stringHash > LargestHashedArrayIndex) - return InvalidArrayIndex; - if (stringHash < LargestHashedArrayIndex) + if (subtype == StringType_ArrayIndex) return stringHash; - return asArrayIndexSlow(); + return UINT_MAX; } - uint asArrayIndexSlow() const; uint toUInt(bool *ok) const; private: -- cgit v1.2.3 From a49e2acc5ad43ffc02bd34d628d4e14b3d6da95c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 14:56:19 +0100 Subject: Don't ignore the v4 subdir Change-Id: Iea769db7251ba6c6289f23a2d29e194e7d6b2ba5 Reviewed-by: Simon Hausmann --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ec496ad579..dede3138fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ *.o Makefile -v4 +src/tools/v4 udis86_itab.* *.pyc *.pro.user -- cgit v1.2.3 From 38094f774b9e36345f157a39e01acc75329f48a9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 14:56:40 +0100 Subject: Add an identifier cache This is very similar to the old string cache with a slight twist: Only strings we know will be used as identifiers (property names) get entered into the cache. These are then also persistent and won't ever get collected. Change-Id: Id441694ef9faf30a87433ae22eb833d1db1d84f1 Reviewed-by: Simon Hausmann --- src/tools/main.cpp | 10 ++-- src/v4/moth/qv4isel_moth.cpp | 2 +- src/v4/qmljs_engine.cpp | 47 ++++++++++-------- src/v4/qmljs_engine.h | 8 +++- src/v4/qmljs_runtime.cpp | 24 +++++----- src/v4/qv4arrayobject.cpp | 2 +- src/v4/qv4dateobject.cpp | 2 +- src/v4/qv4errorobject.cpp | 2 +- src/v4/qv4identifier.h | 110 +++++++++++++++++++++++++++++++++++++++++++ src/v4/qv4isel_masm.cpp | 2 +- src/v4/qv4isel_p.cpp | 4 +- src/v4/qv4jsonobject.cpp | 4 +- src/v4/qv4managed.h | 3 +- src/v4/qv4object.cpp | 22 ++++++--- src/v4/qv4objectproto.cpp | 14 +++--- src/v4/qv4propertytable.h | 5 +- src/v4/qv4regexpobject.cpp | 10 ++-- src/v4/qv4string.cpp | 6 +++ src/v4/qv4string.h | 24 ++++++++-- src/v4/qv4stringobject.cpp | 4 +- src/v4/v4.pro | 1 + 21 files changed, 229 insertions(+), 77 deletions(-) create mode 100644 src/v4/qv4identifier.h diff --git a/src/tools/main.cpp b/src/tools/main.cpp index 1983191942..7ac064fb9d 100644 --- a/src/tools/main.cpp +++ b/src/tools/main.cpp @@ -149,7 +149,7 @@ static void showException(QQmlJS::VM::ExecutionContext *ctx) std::cerr << qPrintable(msg->buildFullMessage(ctx)->toQString()) << std::endl; } } else { - std::cerr << "Uncaught exception: " << qPrintable(e->__get__(ctx, ctx->engine->identifier(QStringLiteral("message")), 0).toString(ctx)->toQString()) << std::endl; + std::cerr << "Uncaught exception: " << qPrintable(e->__get__(ctx, ctx->engine->newString(QStringLiteral("message")), 0).toString(ctx)->toQString()) << std::endl; } } @@ -167,7 +167,7 @@ int executeLLVMCode(void *codePtr) VM::ExecutionContext *ctx = vm.rootContext; QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); - globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), + globalObject->__put__(ctx, vm.newIdentifier(QStringLiteral("print")), QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::Print(ctx))); void * buf = __qmljs_create_exception_handler(ctx); @@ -371,16 +371,16 @@ int main(int argc, char *argv[]) QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); QQmlJS::VM::Object *print = new (ctx->engine->memoryManager) builtins::Print(ctx); print->prototype = ctx->engine->objectPrototype; - globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")), + globalObject->__put__(ctx, vm.newIdentifier(QStringLiteral("print")), QQmlJS::VM::Value::fromObject(print)); QQmlJS::VM::Object *gc = new (ctx->engine->memoryManager) builtins::GC(ctx); gc->prototype = ctx->engine->objectPrototype; - globalObject->__put__(ctx, vm.identifier(QStringLiteral("gc")), + globalObject->__put__(ctx, vm.newIdentifier(QStringLiteral("gc")), QQmlJS::VM::Value::fromObject(gc)); bool errorInTestHarness = false; if (!qgetenv("IN_TEST_HARNESS").isEmpty()) - globalObject->__put__(ctx, vm.identifier(QStringLiteral("$ERROR")), + globalObject->__put__(ctx, vm.newIdentifier(QStringLiteral("$ERROR")), QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::TestHarnessError(ctx, errorInTestHarness))); foreach (const QString &fn, args) { diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index 5522be4c32..f3c5b6bf6c 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -969,7 +969,7 @@ uchar *InstructionSelection::squeezeCode() const VM::String *InstructionSelection::identifier(const QString &s) { - VM::String *str = engine()->identifier(s); + VM::String *str = engine()->newIdentifier(s); _vmFunction->identifiers.append(str); return str; } diff --git a/src/v4/qmljs_engine.cpp b/src/v4/qmljs_engine.cpp index 0b1b14b13e..f17da76e2d 100644 --- a/src/v4/qmljs_engine.cpp +++ b/src/v4/qmljs_engine.cpp @@ -56,6 +56,7 @@ #include #include #include +#include namespace QQmlJS { namespace VM { @@ -72,24 +73,26 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) memoryManager->setExecutionEngine(this); + identifierCache = new Identifiers(this); + rootContext = newContext(); rootContext->init(this); current = rootContext; - id_length = identifier(QStringLiteral("length")); - id_prototype = identifier(QStringLiteral("prototype")); - id_constructor = identifier(QStringLiteral("constructor")); - id_arguments = identifier(QStringLiteral("arguments")); - id_caller = identifier(QStringLiteral("caller")); - id_this = identifier(QStringLiteral("this")); - id___proto__ = identifier(QStringLiteral("__proto__")); - id_enumerable = identifier(QStringLiteral("enumerable")); - id_configurable = identifier(QStringLiteral("configurable")); - id_writable = identifier(QStringLiteral("writable")); - id_value = identifier(QStringLiteral("value")); - id_get = identifier(QStringLiteral("get")); - id_set = identifier(QStringLiteral("set")); - id_eval = identifier(QStringLiteral("eval")); + id_length = newIdentifier(QStringLiteral("length")); + id_prototype = newIdentifier(QStringLiteral("prototype")); + id_constructor = newIdentifier(QStringLiteral("constructor")); + id_arguments = newIdentifier(QStringLiteral("arguments")); + id_caller = newIdentifier(QStringLiteral("caller")); + id_this = newIdentifier(QStringLiteral("this")); + id___proto__ = newIdentifier(QStringLiteral("__proto__")); + id_enumerable = newIdentifier(QStringLiteral("enumerable")); + id_configurable = newIdentifier(QStringLiteral("configurable")); + id_writable = newIdentifier(QStringLiteral("writable")); + id_value = newIdentifier(QStringLiteral("value")); + id_get = newIdentifier(QStringLiteral("get")); + id_set = newIdentifier(QStringLiteral("set")); + id_eval = newIdentifier(QStringLiteral("eval")); objectPrototype = new (memoryManager) ObjectPrototype(); stringPrototype = new (memoryManager) StringPrototype(rootContext); @@ -229,14 +232,9 @@ ExecutionContext *ExecutionEngine::newContext() return new ExecutionContext(); } -String *ExecutionEngine::identifier(const QString &s) -{ - return new (memoryManager) String(s); -} - Function *ExecutionEngine::newFunction(const QString &name) { - VM::Function *f = new VM::Function(identifier(name)); + VM::Function *f = new VM::Function(newIdentifier(name)); functions.append(f); return f; } @@ -287,6 +285,13 @@ String *ExecutionEngine::newString(const QString &s) return new (memoryManager) String(s); } +String *ExecutionEngine::newIdentifier(const QString &text) +{ + return identifierCache->insert(text); +} + + + Object *ExecutionEngine::newStringObject(ExecutionContext *ctx, const Value &value) { StringObject *object = new (memoryManager) StringObject(ctx, value); @@ -456,6 +461,8 @@ void ExecutionEngine::requireArgumentsAccessors(int n) void ExecutionEngine::markObjects() { + identifierCache->mark(); + globalObject.mark(); if (globalCode) diff --git a/src/v4/qmljs_engine.h b/src/v4/qmljs_engine.h index f7416880a2..0f82e5d786 100644 --- a/src/v4/qmljs_engine.h +++ b/src/v4/qmljs_engine.h @@ -92,6 +92,7 @@ struct SyntaxErrorPrototype; struct TypeErrorPrototype; struct URIErrorPrototype; struct EvalFunction; +struct Identifiers; class RegExp; @@ -103,6 +104,8 @@ struct Q_V4_EXPORT ExecutionEngine ExecutionContext *rootContext; WTF::BumpPointerAllocator bumperPointerAllocator; // Used by Yarr Regex engine. + Identifiers *identifierCache; + Debugging::Debugger *debugger; Value globalObject; @@ -177,8 +180,6 @@ struct Q_V4_EXPORT ExecutionEngine ExecutionContext *newContext(); - String *identifier(const QString &s); - VM::Function *newFunction(const QString &name); FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); @@ -190,6 +191,9 @@ struct Q_V4_EXPORT ExecutionEngine FunctionObject *newObjectCtor(ExecutionContext *ctx); String *newString(const QString &s); + String *newIdentifier(const QString &text); + + Object *newStringObject(ExecutionContext *ctx, const Value &value); FunctionObject *newStringCtor(ExecutionContext *ctx); diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index ce1d1abbaa..2161d89053 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -146,47 +146,47 @@ Function *__qmljs_register_function(ExecutionContext *ctx, String *name, Value __qmljs_string_literal_undefined(ExecutionContext *ctx) { - return Value::fromString(ctx->engine->identifier(QStringLiteral("undefined"))); + return Value::fromString(ctx->engine->newString(QStringLiteral("undefined"))); } Value __qmljs_string_literal_null(ExecutionContext *ctx) { - return Value::fromString(ctx->engine->identifier(QStringLiteral("null"))); + return Value::fromString(ctx->engine->newString(QStringLiteral("null"))); } Value __qmljs_string_literal_true(ExecutionContext *ctx) { - return Value::fromString(ctx->engine->identifier(QStringLiteral("true"))); + return Value::fromString(ctx->engine->newString(QStringLiteral("true"))); } Value __qmljs_string_literal_false(ExecutionContext *ctx) { - return Value::fromString(ctx->engine->identifier(QStringLiteral("false"))); + return Value::fromString(ctx->engine->newString(QStringLiteral("false"))); } Value __qmljs_string_literal_object(ExecutionContext *ctx) { - return Value::fromString(ctx->engine->identifier(QStringLiteral("object"))); + return Value::fromString(ctx->engine->newString(QStringLiteral("object"))); } Value __qmljs_string_literal_boolean(ExecutionContext *ctx) { - return Value::fromString(ctx->engine->identifier(QStringLiteral("boolean"))); + return Value::fromString(ctx->engine->newString(QStringLiteral("boolean"))); } Value __qmljs_string_literal_number(ExecutionContext *ctx) { - return Value::fromString(ctx->engine->identifier(QStringLiteral("number"))); + return Value::fromString(ctx->engine->newString(QStringLiteral("number"))); } Value __qmljs_string_literal_string(ExecutionContext *ctx) { - return Value::fromString(ctx->engine->identifier(QStringLiteral("string"))); + return Value::fromString(ctx->engine->newString(QStringLiteral("string"))); } Value __qmljs_string_literal_function(ExecutionContext *ctx) { - return Value::fromString(ctx->engine->identifier(QStringLiteral("function"))); + return Value::fromString(ctx->engine->newString(QStringLiteral("function"))); } Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index) @@ -448,7 +448,7 @@ String *__qmljs_string_from_utf8(ExecutionContext *ctx, const char *s) String *__qmljs_identifier_from_utf8(ExecutionContext *ctx, const char *s) { - return ctx->engine->identifier(QString::fromUtf8(s)); + return ctx->engine->newString(QString::fromUtf8(s)); } int __qmljs_string_length(ExecutionContext *, String *string) @@ -513,8 +513,8 @@ Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int type typeHint = NUMBER_HINT; } - String *meth1 = ctx->engine->identifier("toString"); - String *meth2 = ctx->engine->identifier("valueOf"); + String *meth1 = ctx->engine->newString("toString"); + String *meth2 = ctx->engine->newString("valueOf"); if (typeHint == NUMBER_HINT) qSwap(meth1, meth2); diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp index cbf34d3077..032b56f27b 100644 --- a/src/v4/qv4arrayobject.cpp +++ b/src/v4/qv4arrayobject.cpp @@ -188,7 +188,7 @@ Value ArrayPrototype::method_join(ExecutionContext *ctx) // // crazy! // - Value r6 = self.property(ctx, ctx->engine->identifier(QStringLiteral("0"))); + Value r6 = self.property(ctx, ctx->engine->newString(QStringLiteral("0"))); if (!(r6.isUndefined() || r6.isNull())) R = r6.toString(ctx)->toQString(); diff --git a/src/v4/qv4dateobject.cpp b/src/v4/qv4dateobject.cpp index b9a4446087..737a583e1f 100644 --- a/src/v4/qv4dateobject.cpp +++ b/src/v4/qv4dateobject.cpp @@ -1304,7 +1304,7 @@ Value DatePrototype::method_toJSON(ExecutionContext *ctx) if (tv.isNumber() && !std::isfinite(tv.toNumber(ctx))) return Value::nullValue(); - FunctionObject *toIso = O.objectValue()->__get__(ctx, ctx->engine->identifier(QStringLiteral("toISOString"))).asFunctionObject(); + FunctionObject *toIso = O.objectValue()->__get__(ctx, ctx->engine->newString(QStringLiteral("toISOString"))).asFunctionObject(); if (!toIso) __qmljs_throw_type_error(ctx); diff --git a/src/v4/qv4errorobject.cpp b/src/v4/qv4errorobject.cpp index 09f7f8e4e5..fd03f1f243 100644 --- a/src/v4/qv4errorobject.cpp +++ b/src/v4/qv4errorobject.cpp @@ -79,7 +79,7 @@ ErrorObject::ErrorObject(ExecutionEngine* engine, const Value &message) subtype = Error; if (message.type() != Value::Undefined_Type) - defineDefaultProperty(engine->identifier(QStringLiteral("message")), message); + defineDefaultProperty(engine->newString(QStringLiteral("message")), message); } void ErrorObject::setNameProperty(ExecutionContext *ctx) diff --git a/src/v4/qv4identifier.h b/src/v4/qv4identifier.h new file mode 100644 index 0000000000..5a335ee6e9 --- /dev/null +++ b/src/v4/qv4identifier.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4IDENTIFIER_H +#define QV4IDENTIFIER_H + +#include +#include +#include + +namespace QQmlJS { +namespace VM { + +struct Identifiers +{ + ExecutionEngine *engine; + uint currentIndex; + QHash identifiers; +public: + + Identifiers(ExecutionEngine *engine) : engine(engine), currentIndex(0) {} + + String *insert(const QString &s) + { + QHash::const_iterator it = identifiers.find(s); + if (it != identifiers.constEnd()) + return it.value(); + + String *str = engine->newString(s); + str->createHashValue(); + if (str->subtype == String::StringType_ArrayIndex) + return str; + + str->stringIdentifier = currentIndex; + if (currentIndex <= USHRT_MAX) { + str->subtype = String::StringType_Identifier; + ++currentIndex; + identifiers.insert(s, str); + } + return str; + } + + void toIdentifier(String *s) { + if (s->subtype >= String::StringType_Identifier) + return; + if (s->subtype == String::StringType_Unknown) + s->createHashValue(); + if (s->subtype >= String::StringType_Identifier) + return; + QHash::const_iterator it = identifiers.find(s->toQString()); + if (it != identifiers.constEnd()) { + s->subtype = String::StringType_Identifier; + s->stringIdentifier = (*it)->stringIdentifier; + return; + } + s->stringIdentifier = currentIndex; + if (currentIndex <= USHRT_MAX) { + s->subtype = String::StringType_Identifier; + ++currentIndex; + identifiers.insert(s->toQString(), s); + } + } + + void mark() { + for (QHash::const_iterator it = identifiers.constBegin(); it != identifiers.constEnd(); ++it) + (*it)->mark(); + } +}; + +} +} + +#endif diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index a4a136c656..c02f565733 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -793,7 +793,7 @@ void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::Ex String *InstructionSelection::identifier(const QString &s) { - String *str = engine()->identifier(s); + String *str = engine()->newIdentifier(s); _vmFunction->identifiers.append(str); return str; } diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index b408be3673..3e3b42cecc 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -46,10 +46,10 @@ VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngin foreach (const QString *formal, irFunction->formals) if (formal) - vmFunction->formals.append(engine->identifier(*formal)); + vmFunction->formals.append(engine->newString(*formal)); foreach (const QString *local, irFunction->locals) if (local) - vmFunction->locals.append(engine->identifier(*local)); + vmFunction->locals.append(engine->newString(*local)); foreach (IR::Function *function, irFunction->nestedFunctions) createFunctionMapping(engine, function); diff --git a/src/v4/qv4jsonobject.cpp b/src/v4/qv4jsonobject.cpp index fa39476f4e..4c7ea3de7d 100644 --- a/src/v4/qv4jsonobject.cpp +++ b/src/v4/qv4jsonobject.cpp @@ -282,7 +282,7 @@ bool Parser::parseMember(Object *o) if (!parseValue(&val)) return false; - PropertyDescriptor *p = o->members->insert(context->engine->identifier(key)); + PropertyDescriptor *p = o->members->insert(context->engine->newIdentifier(key)); p->value = val; END; @@ -702,7 +702,7 @@ QString Stringify::Str(const QString &key, Value value) QString result; if (Object *o = value.asObject()) { - FunctionObject *toJSON = o->__get__(ctx, ctx->engine->identifier(QStringLiteral("toJSON"))).asFunctionObject(); + FunctionObject *toJSON = o->__get__(ctx, ctx->engine->newString(QStringLiteral("toJSON"))).asFunctionObject(); if (toJSON) { Value arg = Value::fromString(ctx, key); value = toJSON->call(ctx, value, &arg, 1); diff --git a/src/v4/qv4managed.h b/src/v4/qv4managed.h index 7f36c83239..a033b133aa 100644 --- a/src/v4/qv4managed.h +++ b/src/v4/qv4managed.h @@ -161,7 +161,7 @@ protected: quintptr strictMode : 1; // used by FunctionObject quintptr type : 5; mutable quintptr subtype : 3; - quintptr stringIdentifier : 16; + mutable quintptr stringIdentifier : 16; #if CPU(X86_64) quintptr unused : 32; #endif @@ -171,6 +171,7 @@ protected: private: friend class MemoryManager; friend struct ExecutionContext; + friend struct Identifiers; }; } diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index dadf8479c2..e9e3d78370 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -74,7 +74,7 @@ Object::~Object() void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &value) { - __put__(ctx, ctx->engine->identifier(name), value); + __put__(ctx, ctx->engine->newString(name), value); } Value Object::getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const @@ -139,13 +139,13 @@ void Object::defineDefaultProperty(String *name, Value value) void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value value) { - defineDefaultProperty(context->engine->identifier(name), value); + defineDefaultProperty(context->engine->newIdentifier(name), value); } void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int argumentCount) { Q_UNUSED(argumentCount); - String *s = context->engine->identifier(name); + String *s = context->engine->newIdentifier(name); FunctionObject* function = context->engine->newBuiltinFunction(context, s, code); function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); defineDefaultProperty(s, Value::fromObject(function)); @@ -154,7 +154,7 @@ void Object::defineDefaultProperty(ExecutionContext *context, const QString &nam void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *, Value, Value *, int), int argumentCount) { Q_UNUSED(argumentCount); - String *s = context->engine->identifier(name); + String *s = context->engine->newIdentifier(name); FunctionObject* function = context->engine->newBuiltinFunction(context, s, code); function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); defineDefaultProperty(s, Value::fromObject(function)); @@ -162,7 +162,7 @@ void Object::defineDefaultProperty(ExecutionContext *context, const QString &nam void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value) { - defineReadonlyProperty(engine->identifier(name), value); + defineReadonlyProperty(engine->newIdentifier(name), value); } void Object::defineReadonlyProperty(String *name, Value value) @@ -268,6 +268,8 @@ Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) if (idx != String::InvalidArrayIndex) return __get__(ctx, idx, hasProperty); + name->makeIdentifier(ctx); + if (name->isEqualTo(ctx->engine->id___proto__)) { if (hasProperty) *hasProperty = true; @@ -307,6 +309,8 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) if (idx != String::InvalidArrayIndex) return __put__(ctx, idx, value); + name->makeIdentifier(ctx); + PropertyDescriptor *pd = __getOwnProperty__(ctx, name); // clause 1 if (pd) { @@ -448,6 +452,8 @@ bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const if (idx != String::InvalidArrayIndex) return __hasProperty__(ctx, idx); + name->makeIdentifier(ctx); + if (members && members->find(name) != 0) return true; @@ -470,6 +476,8 @@ bool Object::__delete__(ExecutionContext *ctx, String *name) if (idx != String::InvalidArrayIndex) return __delete__(ctx, idx); + name->makeIdentifier(ctx); + if (members) { if (PropertyTableEntry *entry = members->findEntry(name)) { if (entry->descriptor.isConfigurable()) { @@ -500,6 +508,8 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr if (idx != String::InvalidArrayIndex) return __defineOwnProperty__(ctx, idx, desc); + name->makeIdentifier(ctx); + PropertyDescriptor *current; if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { @@ -644,7 +654,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *cu bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc) { - return __defineOwnProperty__(ctx, ctx->engine->identifier(name), desc); + return __defineOwnProperty__(ctx, ctx->engine->newString(name), desc); } diff --git a/src/v4/qv4objectproto.cpp b/src/v4/qv4objectproto.cpp index 98f205d1d8..65153a8cf1 100644 --- a/src/v4/qv4objectproto.cpp +++ b/src/v4/qv4objectproto.cpp @@ -376,7 +376,7 @@ Value ObjectPrototype::method_toString(ExecutionContext *ctx) Value ObjectPrototype::method_toLocaleString(ExecutionContext *ctx) { Object *o = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); - Value ts = o->__get__(ctx, ctx->engine->identifier(QStringLiteral("toString"))); + Value ts = o->__get__(ctx, ctx->engine->newString(QStringLiteral("toString"))); FunctionObject *f = ts.asFunctionObject(); if (!f) __qmljs_throw_type_error(ctx); @@ -540,19 +540,19 @@ Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Prope if (desc->isData()) { pd.value = desc->value; - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("value")), &pd); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("value")), &pd); pd.value = Value::fromBoolean(desc->writable == PropertyDescriptor::Enabled ? true : false); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("writable")), &pd); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("writable")), &pd); } else { pd.value = desc->get ? Value::fromObject(desc->get) : Value::undefinedValue(); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("get")), &pd); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("get")), &pd); pd.value = desc->set ? Value::fromObject(desc->set) : Value::undefinedValue(); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("set")), &pd); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("set")), &pd); } pd.value = Value::fromBoolean(desc->enumberable == PropertyDescriptor::Enabled ? true : false); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("enumerable")), &pd); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("enumerable")), &pd); pd.value = Value::fromBoolean(desc->configurable == PropertyDescriptor::Enabled ? true : false); - o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("configurable")), &pd); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("configurable")), &pd); return Value::fromObject(o); } diff --git a/src/v4/qv4propertytable.h b/src/v4/qv4propertytable.h index 3bd47679b5..d50b26e4cc 100644 --- a/src/v4/qv4propertytable.h +++ b/src/v4/qv4propertytable.h @@ -61,7 +61,6 @@ struct PropertyTableEntry { index(-1) { } - inline bool hasName(String *n) const { return name->isEqualTo(n); } inline unsigned hashValue() const { return name->hashValue(); } }; @@ -114,7 +113,7 @@ public: { if (_properties) { for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { - if (prop && (prop->name == name || prop->hasName(name))) + if (prop && prop->name->isEqualTo(name)) return prop; } } @@ -126,7 +125,7 @@ public: { if (_properties) { for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { - if (prop && (prop->name == name || prop->hasName(name))) + if (prop && prop->name->isEqualTo(name)) return &prop->descriptor; } } diff --git a/src/v4/qv4regexpobject.cpp b/src/v4/qv4regexpobject.cpp index 1f78c56782..31fb584fb1 100644 --- a/src/v4/qv4regexpobject.cpp +++ b/src/v4/qv4regexpobject.cpp @@ -72,7 +72,7 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bo if (!members) members.reset(new PropertyTable()); - lastIndexProperty = members->insert(engine->identifier(QStringLiteral("lastIndex"))); + lastIndexProperty = members->insert(engine->newIdentifier(QStringLiteral("lastIndex"))); lastIndexProperty->type = PropertyDescriptor::Data; lastIndexProperty->writable = PropertyDescriptor::Enabled; lastIndexProperty->enumberable = PropertyDescriptor::Disabled; @@ -80,10 +80,10 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bo lastIndexProperty->value = Value::fromInt32(0); if (!this->value.get()) return; - defineReadonlyProperty(engine->identifier(QStringLiteral("source")), Value::fromString(engine->newString(this->value->pattern()))); - defineReadonlyProperty(engine->identifier(QStringLiteral("global")), Value::fromBoolean(global)); - defineReadonlyProperty(engine->identifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase())); - defineReadonlyProperty(engine->identifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("source")), Value::fromString(engine->newString(this->value->pattern()))); + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("global")), Value::fromBoolean(global)); + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase())); + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); } diff --git a/src/v4/qv4string.cpp b/src/v4/qv4string.cpp index cdadbe7049..6829c01029 100644 --- a/src/v4/qv4string.cpp +++ b/src/v4/qv4string.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qv4string.h" +#include "qv4identifier.h" #include "qmljs_runtime.h" #include @@ -89,6 +90,11 @@ uint String::toUInt(bool *ok) const return UINT_MAX; } +void String::makeIdentifierImpl(const ExecutionContext *ctx) +{ + ctx->engine->identifierCache->toIdentifier(this); +} + void String::createHashValue() const { const QChar *ch = _text.constData(); diff --git a/src/v4/qv4string.h b/src/v4/qv4string.h index 2df221efba..2ebc9e8c88 100644 --- a/src/v4/qv4string.h +++ b/src/v4/qv4string.h @@ -47,12 +47,14 @@ namespace QQmlJS { namespace VM { +struct ExecutionEngine; + struct String : public Managed { enum StringType { StringType_Unknown, - StringType_ArrayIndex, StringType_Regular, StringType_Identifier, + StringType_ArrayIndex }; String(const QString &text) @@ -62,12 +64,15 @@ struct String : public Managed { inline bool isEqualTo(const String *other) const { if (this == other) return true; - else if (hashValue() == other->hashValue()) { - if (subtype == StringType_ArrayIndex && other->subtype == StringType_ArrayIndex) + if (hashValue() != other->hashValue()) + return false; + if (subtype == other->subtype) { + if (subtype == StringType_ArrayIndex) return true; - return toQString() == other->toQString(); + if (subtype == StringType_Identifier) + return stringIdentifier == other->stringIdentifier; } - return false; + return toQString() == other->toQString(); } inline const QString &toQString() const { @@ -93,7 +98,16 @@ struct String : public Managed { } uint toUInt(bool *ok) const; + void makeIdentifier(const ExecutionContext *ctx) { + if (subtype == StringType_Identifier) + return; + makeIdentifierImpl(ctx); + } + + void makeIdentifierImpl(const ExecutionContext *ctx); + private: + friend struct Identifiers; void createHashValue() const; QString _text; diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp index 81968cc55a..4b46fc117a 100644 --- a/src/v4/qv4stringobject.cpp +++ b/src/v4/qv4stringobject.cpp @@ -312,13 +312,13 @@ Value StringPrototype::method_match(ExecutionContext *parentCtx, Value thisObjec bool global = rx->global; // ### use the standard builtin function, not the one that might be redefined in the proto - FunctionObject *exec = parentCtx->engine->regExpPrototype->__get__(parentCtx, parentCtx->engine->identifier(QStringLiteral("exec")), 0).asFunctionObject(); + FunctionObject *exec = parentCtx->engine->regExpPrototype->__get__(parentCtx, parentCtx->engine->newString(QStringLiteral("exec")), 0).asFunctionObject(); Value arg = Value::fromString(s); if (!global) return exec->call(parentCtx, Value::fromObject(rx), &arg, 1); - String *lastIndex = parentCtx->engine->identifier(QStringLiteral("lastIndex")); + String *lastIndex = parentCtx->engine->newString(QStringLiteral("lastIndex")); rx->__put__(parentCtx, lastIndex, Value::fromInt32(0)); ArrayObject *a = parentCtx->engine->newArrayObject(parentCtx); diff --git a/src/v4/v4.pro b/src/v4/v4.pro index 6d990ed234..6f409ea68c 100644 --- a/src/v4/v4.pro +++ b/src/v4/v4.pro @@ -68,6 +68,7 @@ HEADERS += \ qv4isel_p.h \ qv4isel_util_p.h \ debugging.h \ + qv4identifier.h \ qv4mm.h \ qv4managed.h \ qv4array.h \ -- cgit v1.2.3 From 7578373f465339e45342b75d6112bd0f74c94cc7 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 15:10:39 +0100 Subject: Convert a few more builtins into versions that don't need a context Change-Id: I24920f3952cd356d50a056891dc075e56c951775 Reviewed-by: Simon Hausmann --- src/v4/qv4booleanobject.cpp | 4 ++-- src/v4/qv4booleanobject.h | 2 +- src/v4/qv4numberobject.cpp | 4 ++-- src/v4/qv4numberobject.h | 2 +- src/v4/qv4stringobject.cpp | 8 ++++---- src/v4/qv4stringobject.h | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/v4/qv4booleanobject.cpp b/src/v4/qv4booleanobject.cpp index 9df0f23a6a..640773de0c 100644 --- a/src/v4/qv4booleanobject.cpp +++ b/src/v4/qv4booleanobject.cpp @@ -54,9 +54,9 @@ Value BooleanCtor::construct(ExecutionContext *ctx) return Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n))); } -Value BooleanCtor::call(ExecutionContext *ctx) +Value BooleanCtor::call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - bool value = ctx->argumentCount ? ctx->argument(0).toBoolean(ctx) : 0; + bool value = argc ? argv[0].toBoolean(parentCtx) : 0; return Value::fromBoolean(value); } diff --git a/src/v4/qv4booleanobject.h b/src/v4/qv4booleanobject.h index 44d87b1d50..97dfc50166 100644 --- a/src/v4/qv4booleanobject.h +++ b/src/v4/qv4booleanobject.h @@ -53,7 +53,7 @@ struct BooleanCtor: FunctionObject BooleanCtor(ExecutionContext *scope); virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); + virtual Value call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); }; struct BooleanPrototype: BooleanObject diff --git a/src/v4/qv4numberobject.cpp b/src/v4/qv4numberobject.cpp index 08711a8187..d732297f4e 100644 --- a/src/v4/qv4numberobject.cpp +++ b/src/v4/qv4numberobject.cpp @@ -60,9 +60,9 @@ Value NumberCtor::construct(ExecutionContext *ctx) return Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(d))); } -Value NumberCtor::call(ExecutionContext *ctx) +Value NumberCtor::call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; + double d = argc ? argv[0].toNumber(parentCtx) : 0.; return Value::fromDouble(d); } diff --git a/src/v4/qv4numberobject.h b/src/v4/qv4numberobject.h index 85f9e1ff2b..83fb4ad124 100644 --- a/src/v4/qv4numberobject.h +++ b/src/v4/qv4numberobject.h @@ -53,7 +53,7 @@ struct NumberCtor: FunctionObject NumberCtor(ExecutionContext *scope); virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); + virtual Value call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); }; struct NumberPrototype: NumberObject diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp index 4b46fc117a..4e0285cdb0 100644 --- a/src/v4/qv4stringobject.cpp +++ b/src/v4/qv4stringobject.cpp @@ -122,13 +122,13 @@ Value StringCtor::construct(ExecutionContext *ctx) return Value::fromObject(ctx->engine->newStringObject(ctx, value)); } -Value StringCtor::call(ExecutionContext *ctx) +Value StringCtor::call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { Value value; - if (ctx->argumentCount) - value = Value::fromString(ctx->argument(0).toString(ctx)); + if (argc) + value = Value::fromString(argv[0].toString(parentCtx)); else - value = Value::fromString(ctx, QString()); + value = Value::fromString(parentCtx, QString()); return value; } diff --git a/src/v4/qv4stringobject.h b/src/v4/qv4stringobject.h index 1becd97a46..306f00c78e 100644 --- a/src/v4/qv4stringobject.h +++ b/src/v4/qv4stringobject.h @@ -64,7 +64,7 @@ struct StringCtor: FunctionObject StringCtor(ExecutionContext *scope); virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); + virtual Value call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); }; struct StringPrototype: StringObject -- cgit v1.2.3 From 021a2ff9c94005bfcf7245e330a5d03da4437af9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 15:39:08 +0100 Subject: Make sure we compare identifiers Change-Id: I1fc63a062799857ccd937c194e7f305b31ca3567 Reviewed-by: Simon Hausmann --- src/v4/qmljs_environment.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp index 9042a435a0..67f5994d34 100644 --- a/src/v4/qmljs_environment.cpp +++ b/src/v4/qmljs_environment.cpp @@ -316,6 +316,8 @@ void ExecutionContext::setProperty(String *name, Value value) Value ExecutionContext::getProperty(String *name) { + name->makeIdentifier(this); + if (name->isEqualTo(engine->id_this)) return thisObject; @@ -357,6 +359,8 @@ Value ExecutionContext::getProperty(String *name) Value ExecutionContext::getPropertyNoThrow(String *name) { + name->makeIdentifier(this); + if (name->isEqualTo(engine->id_this)) return thisObject; @@ -394,6 +398,7 @@ Value ExecutionContext::getPropertyNoThrow(String *name) Value ExecutionContext::getPropertyAndBase(String *name, Object **base) { *base = 0; + name->makeIdentifier(this); if (name->isEqualTo(engine->id_this)) return thisObject; -- cgit v1.2.3 From 7e24a7d61f57b3f961d81daa11b336ed60ee4e2c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 15:43:22 +0100 Subject: Inline some code for property lookups Speeds up fact.2.js and crypto.js by ~5-10% Change-Id: I121b6c3dbbd89060f323a422286adf946d4f924a Reviewed-by: Simon Hausmann --- src/v4/qv4object.cpp | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index e9e3d78370..6f5fb60723 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -276,10 +276,16 @@ Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) return Value::fromObject(prototype); } - if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name)) { - if (hasProperty) - *hasProperty = true; - return getValue(ctx, p); + Object *o = this; + while (o) { + if (o->members) { + if (PropertyDescriptor *p = o->members->find(name)) { + if (hasProperty) + *hasProperty = true; + return getValue(ctx, p); + } + } + o = o->prototype; } if (hasProperty) @@ -289,11 +295,28 @@ Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) { - const PropertyDescriptor *p = __getPropertyDescriptor__(ctx, index); - if (p && p->type != PropertyDescriptor::Generic) { + PropertyDescriptor *pd = 0; + Object *o = this; + while (o) { + PropertyDescriptor *p = o->array.at(index); + if (p && p->type != PropertyDescriptor::Generic) { + pd = p; + break; + } + if (o->isStringObject()) { + p = static_cast(o)->getIndex(ctx, index); + if (p) { + pd = p; + break; + } + } + o = o->prototype; + } + + if (pd) { if (hasProperty) *hasProperty = true; - return getValue(ctx, p); + return getValue(ctx, pd); } if (hasProperty) -- cgit v1.2.3 From b2bf89d53c84a28e9f0b4791f27db5a2f4607fbf Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 16:25:57 +0100 Subject: Remove some unused methods Change-Id: Ib487078db163e03bb0bd1be762f1aa0da04434b4 Reviewed-by: Simon Hausmann --- src/v4/qmljs_engine.cpp | 51 +------------------------------------------- src/v4/qmljs_engine.h | 14 ------------ src/v4/qmljs_environment.cpp | 2 +- src/v4/qv4mathobject.cpp | 2 ++ 4 files changed, 4 insertions(+), 65 deletions(-) diff --git a/src/v4/qmljs_engine.cpp b/src/v4/qmljs_engine.cpp index f17da76e2d..55121cd11f 100644 --- a/src/v4/qmljs_engine.cpp +++ b/src/v4/qmljs_engine.cpp @@ -196,7 +196,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) glo->defineDefaultProperty(rootContext, QStringLiteral("SyntaxError"), syntaxErrorCtor); glo->defineDefaultProperty(rootContext, QStringLiteral("TypeError"), typeErrorCtor); glo->defineDefaultProperty(rootContext, QStringLiteral("URIError"), uRIErrorCtor); - glo->defineDefaultProperty(rootContext, QStringLiteral("Math"), Value::fromObject(newMathObject(rootContext))); + glo->defineDefaultProperty(rootContext, QStringLiteral("Math"), Value::fromObject(new (memoryManager) MathObject(rootContext))); glo->defineDefaultProperty(rootContext, QStringLiteral("JSON"), Value::fromObject(new (memoryManager) JsonObject(rootContext))); glo->defineReadonlyProperty(this, QStringLiteral("undefined"), Value::undefinedValue()); @@ -275,11 +275,6 @@ Object *ExecutionEngine::newObject() return object; } -FunctionObject *ExecutionEngine::newObjectCtor(ExecutionContext *ctx) -{ - return new (memoryManager) ObjectCtor(ctx); -} - String *ExecutionEngine::newString(const QString &s) { return new (memoryManager) String(s); @@ -290,8 +285,6 @@ String *ExecutionEngine::newIdentifier(const QString &text) return identifierCache->insert(text); } - - Object *ExecutionEngine::newStringObject(ExecutionContext *ctx, const Value &value) { StringObject *object = new (memoryManager) StringObject(ctx, value); @@ -299,11 +292,6 @@ Object *ExecutionEngine::newStringObject(ExecutionContext *ctx, const Value &val return object; } -FunctionObject *ExecutionEngine::newStringCtor(ExecutionContext *ctx) -{ - return new (memoryManager) StringCtor(ctx); -} - Object *ExecutionEngine::newNumberObject(const Value &value) { NumberObject *object = new (memoryManager) NumberObject(value); @@ -311,11 +299,6 @@ Object *ExecutionEngine::newNumberObject(const Value &value) return object; } -FunctionObject *ExecutionEngine::newNumberCtor(ExecutionContext *ctx) -{ - return new (memoryManager) NumberCtor(ctx); -} - Object *ExecutionEngine::newBooleanObject(const Value &value) { Object *object = new (memoryManager) BooleanObject(value); @@ -323,11 +306,6 @@ Object *ExecutionEngine::newBooleanObject(const Value &value) return object; } -FunctionObject *ExecutionEngine::newBooleanCtor(ExecutionContext *ctx) -{ - return new (memoryManager) BooleanCtor(ctx); -} - Object *ExecutionEngine::newFunctionObject(ExecutionContext *ctx) { Object *object = new (memoryManager) FunctionObject(ctx); @@ -349,11 +327,6 @@ ArrayObject *ExecutionEngine::newArrayObject(ExecutionContext *ctx, const Array return object; } -FunctionObject *ExecutionEngine::newArrayCtor(ExecutionContext *ctx) -{ - return new (memoryManager) ArrayCtor(ctx); -} - Object *ExecutionEngine::newDateObject(const Value &value) { Object *object = new (memoryManager) DateObject(value); @@ -361,11 +334,6 @@ Object *ExecutionEngine::newDateObject(const Value &value) return object; } -FunctionObject *ExecutionEngine::newDateCtor(ExecutionContext *ctx) -{ - return new (memoryManager) DateCtor(ctx); -} - RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) { bool global = (flags & IR::RegExp::RegExp_Global); @@ -386,11 +354,6 @@ RegExpObject *ExecutionEngine::newRegExpObject(PassRefPtr re, bool globa return object; } -FunctionObject *ExecutionEngine::newRegExpCtor(ExecutionContext *ctx) -{ - return new (memoryManager) RegExpCtor(ctx); -} - Object *ExecutionEngine::newErrorObject(const Value &value) { ErrorObject *object = new (memoryManager) ErrorObject(this, value); @@ -423,18 +386,6 @@ Object *ExecutionEngine::newURIErrorObject(ExecutionContext *ctx, Value message) return new (memoryManager) URIErrorObject(ctx, message); } -Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) -{ - MathObject *object = new (memoryManager) MathObject(ctx); - object->prototype = objectPrototype; - return object; -} - -Object *ExecutionEngine::newActivationObject() -{ - return new (memoryManager) Object(); -} - Object *ExecutionEngine::newForEachIteratorObject(ExecutionContext *ctx, Object *o) { return new (memoryManager) ForEachIteratorObject(ctx, o); diff --git a/src/v4/qmljs_engine.h b/src/v4/qmljs_engine.h index 0f82e5d786..013a2b6aca 100644 --- a/src/v4/qmljs_engine.h +++ b/src/v4/qmljs_engine.h @@ -188,33 +188,22 @@ struct Q_V4_EXPORT ExecutionEngine BoundFunction *newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); Object *newObject(); - FunctionObject *newObjectCtor(ExecutionContext *ctx); String *newString(const QString &s); String *newIdentifier(const QString &text); - Object *newStringObject(ExecutionContext *ctx, const Value &value); - FunctionObject *newStringCtor(ExecutionContext *ctx); - Object *newNumberObject(const Value &value); - FunctionObject *newNumberCtor(ExecutionContext *ctx); - Object *newBooleanObject(const Value &value); - FunctionObject *newBooleanCtor(ExecutionContext *ctx); - Object *newFunctionObject(ExecutionContext *ctx); ArrayObject *newArrayObject(ExecutionContext *ctx); ArrayObject *newArrayObject(ExecutionContext *ctx, const Array &value); - FunctionObject *newArrayCtor(ExecutionContext *ctx); Object *newDateObject(const Value &value); - FunctionObject *newDateCtor(ExecutionContext *ctx); RegExpObject *newRegExpObject(const QString &pattern, int flags); RegExpObject *newRegExpObject(PassRefPtr re, bool global); - FunctionObject *newRegExpCtor(ExecutionContext *ctx); Object *newErrorObject(const Value &value); Object *newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message); @@ -223,9 +212,6 @@ struct Q_V4_EXPORT ExecutionEngine Object *newRangeErrorObject(ExecutionContext *ctx, const QString &message); Object *newURIErrorObject(ExecutionContext *ctx, Value message); - Object *newMathObject(ExecutionContext *ctx); - Object *newActivationObject(); - Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o); void requireArgumentsAccessors(int n); diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp index 67f5994d34..8ff93c1b98 100644 --- a/src/v4/qmljs_environment.cpp +++ b/src/v4/qmljs_environment.cpp @@ -100,7 +100,7 @@ bool ExecutionContext::hasBinding(String *name) const void ExecutionContext::createMutableBinding(String *name, bool deletable) { if (!activation) - activation = engine->newActivationObject(); + activation = engine->newObject(); if (activation->__hasProperty__(this, name)) return; diff --git a/src/v4/qv4mathobject.cpp b/src/v4/qv4mathobject.cpp index e40faba861..eae50c4db4 100644 --- a/src/v4/qv4mathobject.cpp +++ b/src/v4/qv4mathobject.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qv4mathobject.h" +#include "qv4objectproto.h" #include #include @@ -52,6 +53,7 @@ static const double qt_PI = 2.0 * ::asin(1.0); MathObject::MathObject(ExecutionContext *ctx) { type = Type_MathObject; + prototype = ctx->engine->objectPrototype; defineReadonlyProperty(ctx->engine, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); defineReadonlyProperty(ctx->engine, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); -- cgit v1.2.3 From b1d9d6efa2ac138a15aac6b9f330a36a9d4a7390 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 20:48:35 +0100 Subject: Make things more compliant with a regular Qt module Move the v4 tool into basedir/tools/v4 Small fix to sync.profile Install v4 into QT_INSTALL_BINS adjust test262.py to simply call 'v4' by default Change-Id: If78f40632c226bc26f64b0eea06fe93d7770af3e Reviewed-by: Simon Hausmann --- src/src.pro | 2 +- src/tools/main.cpp | 426 ---------------------------------------------------- src/tools/tools.pro | 18 --- src/v4/v4.pro | 2 +- sync.profile | 4 +- tests/test262.py | 2 +- tools/tools.pro | 2 + tools/v4/main.cpp | 426 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/v4/v4.pro | 18 +++ 9 files changed, 451 insertions(+), 449 deletions(-) delete mode 100644 src/tools/main.cpp delete mode 100644 src/tools/tools.pro create mode 100644 tools/tools.pro create mode 100644 tools/v4/main.cpp create mode 100644 tools/v4/v4.pro diff --git a/src/src.pro b/src/src.pro index 706285498d..75f8fb8b75 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,3 +1,3 @@ TEMPLATE = subdirs -SUBDIRS = v4 tools +SUBDIRS = v4 CONFIG += ordered diff --git a/src/tools/main.cpp b/src/tools/main.cpp deleted file mode 100644 index 7ac064fb9d..0000000000 --- a/src/tools/main.cpp +++ /dev/null @@ -1,426 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QMLJS_NO_LLVM -# include "private/qv4_llvm_p.h" -#endif - -#include "private/debugging.h" -#include "private/qv4object.h" -#include "private/qmljs_runtime.h" -#include "private/qv4functionobject.h" -#include "private/qv4errorobject.h" -#include "private/qv4globalobject.h" -#include "private/qv4codegen_p.h" -#include "private/qv4isel_masm_p.h" -#include "private/qv4isel_moth_p.h" -#include "private/qv4vme_moth_p.h" -#include "private/qv4syntaxchecker_p.h" -#include "private/qv4objectproto.h" -#include "private/qv4isel_p.h" -#include "private/qv4mm.h" -#include "private/qmljs_environment.h" - -#include -#include -#include -#include -#include - -#include -#include - -namespace builtins { - -using namespace QQmlJS::VM; - -struct Q_V4_EXPORT Print: FunctionObject -{ - Print(ExecutionContext *scope): FunctionObject(scope) { - name = scope->engine->newString("print"); - } - - virtual Value call(ExecutionContext *ctx) - { - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { - String *s = ctx->argument(i).toString(ctx); - if (i) - std::cout << ' '; - std::cout << qPrintable(s->toQString()); - } - std::cout << std::endl; - return Value::undefinedValue(); - } -}; - -struct Q_V4_EXPORT TestHarnessError: FunctionObject -{ - TestHarnessError(ExecutionContext *scope, bool &errorInTestHarness): FunctionObject(scope), errorOccurred(errorInTestHarness) { - name = scope->engine->newString("$ERROR"); - } - - virtual Value call(ExecutionContext *ctx) - { - errorOccurred = true; - - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { - String *s = ctx->argument(i).toString(ctx); - if (i) - std::cerr << ' '; - std::cerr << qPrintable(s->toQString()); - } - std::cerr << std::endl; - return Value::undefinedValue(); - } - - bool &errorOccurred; -}; - -struct Q_V4_EXPORT GC: public FunctionObject -{ - GC(ExecutionContext* scope) - : FunctionObject(scope) - { - name = scope->engine->newString("gc"); - } - virtual Value call(ExecutionContext *ctx) - { - ctx->engine->memoryManager->runGC(); - return Value::undefinedValue(); - } -}; - -} // builtins - -static void showException(QQmlJS::VM::ExecutionContext *ctx) -{ - QQmlJS::VM::ErrorObject *e = ctx->engine->exception.asErrorObject(); - if (!e) { - std::cerr << "Uncaught exception: " << qPrintable(ctx->engine->exception.toString(ctx)->toQString()) << std::endl; - return; - } - - if (QQmlJS::VM::SyntaxErrorObject *err = e->asSyntaxError()) { - QQmlJS::VM::DiagnosticMessage *msg = err->message(); - if (!msg) { - std::cerr << "Uncaught exception: Syntax error" << std::endl; - return; - } - - for (; msg; msg = msg->next) { - std::cerr << qPrintable(msg->buildFullMessage(ctx)->toQString()) << std::endl; - } - } else { - std::cerr << "Uncaught exception: " << qPrintable(e->__get__(ctx, ctx->engine->newString(QStringLiteral("message")), 0).toString(ctx)->toQString()) << std::endl; - } -} - -#ifndef QMLJS_NO_LLVM -int executeLLVMCode(void *codePtr) -{ - using namespace QQmlJS; - - if (!codePtr) - return EXIT_FAILURE; - void (*code)(VM::ExecutionContext *) = (void (*)(VM::ExecutionContext *)) codePtr; - - QScopedPointer iSelFactory(new QQmlJS::Moth::ISelFactory); - VM::ExecutionEngine vm(iSelFactory.data()); - VM::ExecutionContext *ctx = vm.rootContext; - - QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); - globalObject->__put__(ctx, vm.newIdentifier(QStringLiteral("print")), - QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::Print(ctx))); - - void * buf = __qmljs_create_exception_handler(ctx); - if (setjmp(*(jmp_buf *)buf)) { - showException(ctx); - return EXIT_FAILURE; - } - - code(ctx); - return EXIT_SUCCESS; -} - -int compile(const QString &fileName, const QString &source, QQmlJS::LLVMOutputType outputType) -{ - using namespace QQmlJS; - - IR::Module module; - QQmlJS::Engine ee, *engine = ⅇ - Lexer lexer(engine); - lexer.setCode(source, 1, false); - Parser parser(engine); - - const bool parsed = parser.parseProgram(); - - foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { - std::cerr << qPrintable(fileName) << ':' - << m.loc.startLine << ':' - << m.loc.startColumn << ": error: " - << qPrintable(m.message) << std::endl; - } - - if (!parsed) - return EXIT_FAILURE; - - using namespace AST; - Program *program = AST::cast(parser.rootNode()); - - class MyErrorHandler: public ErrorHandler { - public: - virtual void syntaxError(QQmlJS::VM::DiagnosticMessage *message) { - for (; message; message = message->next) { - std::cerr << qPrintable(message->fileName) << ':' - << message->startLine << ':' - << message->startColumn << ": " - << (message->type == QQmlJS::VM::DiagnosticMessage::Error ? "error" : "warning") << ": " - << qPrintable(message->message) << std::endl; - } - } - } errorHandler; - - Codegen cg(&errorHandler, false); - // FIXME: if the program is empty, we should we generate an empty %entry, or give an error? - /*IR::Function *globalCode =*/ cg(fileName, program, &module); - - int (*exec)(void *) = outputType == LLVMOutputJit ? executeLLVMCode : 0; - return compileWithLLVM(&module, fileName, outputType, exec); -} - -int compileFiles(const QStringList &files, QQmlJS::LLVMOutputType outputType) -{ - foreach (const QString &fileName, files) { - QFile file(fileName); - if (file.open(QFile::ReadOnly)) { - QString source = QString::fromUtf8(file.readAll()); - int result = compile(fileName, source, outputType); - if (result != EXIT_SUCCESS) - return result; - } else { - std::cerr << "Error: cannot open file " << fileName.toUtf8().constData() << std::endl; - return EXIT_FAILURE; - } - } - return EXIT_SUCCESS; -} - -int evaluateCompiledCode(const QStringList &files) -{ - using namespace QQmlJS; - - foreach (const QString &libName, files) { - QFileInfo libInfo(libName); - QLibrary lib(libInfo.absoluteFilePath()); - lib.load(); - QFunctionPointer ptr = lib.resolve("%entry"); -// qDebug("_%%entry resolved to address %p", ptr); - int result = executeLLVMCode((void *) ptr); - if (result != EXIT_SUCCESS) - return result; - } - - return EXIT_SUCCESS; -} - -#endif - - -int main(int argc, char *argv[]) -{ - QCoreApplication app(argc, argv); - QStringList args = app.arguments(); - args.removeFirst(); - - enum { - use_masm, - use_moth, - use_llvm_compiler, - use_llvm_runtime, - use_llvm_jit - } mode = use_masm; - -#ifndef QMLJS_NO_LLVM - QQmlJS::LLVMOutputType fileType = QQmlJS::LLVMOutputObject; -#endif // QMLJS_NO_LLVM - bool enableDebugging = false; - - if (!args.isEmpty()) { - if (args.first() == QLatin1String("-d") || args.first() == QLatin1String("--debug")) { - enableDebugging = true; - args.removeFirst(); - } - } - - if (!args.isEmpty()) { - if (args.first() == QLatin1String("--jit")) { - mode = use_masm; - args.removeFirst(); - } - - if (args.first() == QLatin1String("--interpret")) { - mode = use_moth; - args.removeFirst(); - } - -#ifndef QMLJS_NO_LLVM - if (args.first() == QLatin1String("--compile")) { - mode = use_llvm_compiler; - args.removeFirst(); - - if (!args.isEmpty() && args.first() == QLatin1String("-t")) { - args.removeFirst(); - // Note: keep this list in sync with the enum! - static QStringList fileTypes = QStringList() << QLatin1String("ll") << QLatin1String("bc") << QLatin1String("asm") << QLatin1String("obj"); - if (args.isEmpty() || !fileTypes.contains(args.first())) { - std::cerr << "file types: ll, bc, asm, obj" << std::endl; - return EXIT_FAILURE; - } - fileType = (QQmlJS::LLVMOutputType) fileTypes.indexOf(args.first()); - args.removeFirst(); - } - } - - if (args.first() == QLatin1String("--aot")) { - mode = use_llvm_runtime; - args.removeFirst(); - } - - if (args.first() == QLatin1String("--llvm-jit")) { - mode = use_llvm_jit; - args.removeFirst(); - } -#endif // QMLJS_NO_LLVM - if (args.first() == QLatin1String("--help")) { - std::cerr << "Usage: v4 [|--debug|-d] [|--jit|--interpret|--compile|--aot|--llvm-jit] file..." << std::endl; - return EXIT_SUCCESS; - } - } - - switch (mode) { -#ifdef QMLJS_NO_LLVM - case use_llvm_compiler: - case use_llvm_runtime: - case use_llvm_jit: - std::cerr << "LLVM backend was not built, compiler is unavailable." << std::endl; - return EXIT_FAILURE; -#else // QMLJS_NO_LLVM - case use_llvm_jit: - return compileFiles(args, QQmlJS::LLVMOutputJit); - case use_llvm_compiler: - return compileFiles(args, fileType); - case use_llvm_runtime: - return evaluateCompiledCode(args); -#endif // QMLJS_NO_LLVM - case use_masm: - case use_moth: { - QScopedPointer iSelFactory; - if (mode == use_moth) { - iSelFactory.reset(new QQmlJS::Moth::ISelFactory); - } else { - iSelFactory.reset(new QQmlJS::MASM::ISelFactory); - } - - QQmlJS::VM::ExecutionEngine vm(iSelFactory.data()); - - QScopedPointer debugger; - if (enableDebugging) - debugger.reset(new QQmlJS::Debugging::Debugger(&vm)); - vm.debugger = debugger.data(); - - QQmlJS::VM::ExecutionContext *ctx = vm.rootContext; - - QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); - QQmlJS::VM::Object *print = new (ctx->engine->memoryManager) builtins::Print(ctx); - print->prototype = ctx->engine->objectPrototype; - globalObject->__put__(ctx, vm.newIdentifier(QStringLiteral("print")), - QQmlJS::VM::Value::fromObject(print)); - QQmlJS::VM::Object *gc = new (ctx->engine->memoryManager) builtins::GC(ctx); - gc->prototype = ctx->engine->objectPrototype; - globalObject->__put__(ctx, vm.newIdentifier(QStringLiteral("gc")), - QQmlJS::VM::Value::fromObject(gc)); - - bool errorInTestHarness = false; - if (!qgetenv("IN_TEST_HARNESS").isEmpty()) - globalObject->__put__(ctx, vm.newIdentifier(QStringLiteral("$ERROR")), - QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::TestHarnessError(ctx, errorInTestHarness))); - - foreach (const QString &fn, args) { - QFile file(fn); - if (file.open(QFile::ReadOnly)) { - const QString code = QString::fromUtf8(file.readAll()); - file.close(); - - void * buf = __qmljs_create_exception_handler(ctx); - if (setjmp(*(jmp_buf *)buf)) { - showException(ctx); - return EXIT_FAILURE; - } - - QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode, - /*strictMode =*/ false, /*inheritContext =*/ false); - if (!f) - continue; - vm.globalCode = f; - - ctx->strictMode = f->isStrict; - if (debugger) - debugger->aboutToCall(0, ctx); - QQmlJS::VM::Value result = f->code(ctx, f->codeData); - if (debugger) - debugger->justLeft(ctx); - if (!result.isUndefined()) { - if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) - std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; - } - - if (errorInTestHarness) - return EXIT_FAILURE; - } else { - std::cerr << "Error: cannot open file " << fn.toUtf8().constData() << std::endl; - return EXIT_FAILURE; - } - } - - vm.memoryManager->dumpStats(); - } return EXIT_SUCCESS; - } // switch (mode) -} diff --git a/src/tools/tools.pro b/src/tools/tools.pro deleted file mode 100644 index b08f2242b7..0000000000 --- a/src/tools/tools.pro +++ /dev/null @@ -1,18 +0,0 @@ -TEMPLATE = app -QT = core v4 v4-private core-private qmldevtools-private -SOURCES = main.cpp - -TARGET = v4 - -INCLUDEPATH += ../v4 -INCLUDEPATH += ../3rdparty/masm -INCLUDEPATH += ../3rdparty/masm/wtf -INCLUDEPATH += ../3rdparty/masm/stubs -INCLUDEPATH += ../3rdparty/masm/stubs/wtf -INCLUDEPATH += ../3rdparty/masm/jit -INCLUDEPATH += ../3rdparty/masm/assembler -INCLUDEPATH += ../3rdparty/masm/disassembler - -DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" -DEFINES += QMLJS_NO_LLVM -DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 diff --git a/src/v4/v4.pro b/src/v4/v4.pro index 6f409ea68c..e2987da2f3 100644 --- a/src/v4/v4.pro +++ b/src/v4/v4.pro @@ -138,7 +138,7 @@ linux-g++*:isEqual(QT_ARCH,i386) { } TESTSCRIPT=$$PWD/../../tests/test262.py -V4CMD = $$OUT_PWD/../tools/v4 +V4CMD = v4 checktarget.target = check checktarget.commands = python $$TESTSCRIPT --command=$$V4CMD --parallel --with-test-expectations --update-expectations diff --git a/sync.profile b/sync.profile index 4e6f5f7cff..d392e0120c 100644 --- a/sync.profile +++ b/sync.profile @@ -1,8 +1,8 @@ %modules = ( # path to module name map - "QtV4" => "$basedir/src", + "QtV4" => "$basedir/src/v4", ); %moduleheaders = ( # restrict the module headers to those found in relative path - #"QtV4" => "3rdparty/masm;v4;", +# "QtV4" => "../3rdparty/;../v4;", ); @allmoduleheadersprivate = ( "QtV4" diff --git a/tests/test262.py b/tests/test262.py index 607fca7266..25cfec3b57 100755 --- a/tests/test262.py +++ b/tests/test262.py @@ -113,7 +113,7 @@ EXCLUDE_LIST = [x.getAttribute("id") for x in EXCLUDE_LIST] def BuildOptions(): result = optparse.OptionParser() - result.add_option("--command", default=path.abspath(rootDir + "/../v4"), help="The command-line to run") + result.add_option("--command", default="v4", help="The command-line to run") result.add_option("--tests", default=path.abspath(rootDir + '/test262'), help="Path to the tests") result.add_option("--cat", default=False, action="store_true", diff --git a/tools/tools.pro b/tools/tools.pro new file mode 100644 index 0000000000..f783b654b5 --- /dev/null +++ b/tools/tools.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += v4 diff --git a/tools/v4/main.cpp b/tools/v4/main.cpp new file mode 100644 index 0000000000..7ac064fb9d --- /dev/null +++ b/tools/v4/main.cpp @@ -0,0 +1,426 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLJS_NO_LLVM +# include "private/qv4_llvm_p.h" +#endif + +#include "private/debugging.h" +#include "private/qv4object.h" +#include "private/qmljs_runtime.h" +#include "private/qv4functionobject.h" +#include "private/qv4errorobject.h" +#include "private/qv4globalobject.h" +#include "private/qv4codegen_p.h" +#include "private/qv4isel_masm_p.h" +#include "private/qv4isel_moth_p.h" +#include "private/qv4vme_moth_p.h" +#include "private/qv4syntaxchecker_p.h" +#include "private/qv4objectproto.h" +#include "private/qv4isel_p.h" +#include "private/qv4mm.h" +#include "private/qmljs_environment.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace builtins { + +using namespace QQmlJS::VM; + +struct Q_V4_EXPORT Print: FunctionObject +{ + Print(ExecutionContext *scope): FunctionObject(scope) { + name = scope->engine->newString("print"); + } + + virtual Value call(ExecutionContext *ctx) + { + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { + String *s = ctx->argument(i).toString(ctx); + if (i) + std::cout << ' '; + std::cout << qPrintable(s->toQString()); + } + std::cout << std::endl; + return Value::undefinedValue(); + } +}; + +struct Q_V4_EXPORT TestHarnessError: FunctionObject +{ + TestHarnessError(ExecutionContext *scope, bool &errorInTestHarness): FunctionObject(scope), errorOccurred(errorInTestHarness) { + name = scope->engine->newString("$ERROR"); + } + + virtual Value call(ExecutionContext *ctx) + { + errorOccurred = true; + + for (unsigned int i = 0; i < ctx->argumentCount; ++i) { + String *s = ctx->argument(i).toString(ctx); + if (i) + std::cerr << ' '; + std::cerr << qPrintable(s->toQString()); + } + std::cerr << std::endl; + return Value::undefinedValue(); + } + + bool &errorOccurred; +}; + +struct Q_V4_EXPORT GC: public FunctionObject +{ + GC(ExecutionContext* scope) + : FunctionObject(scope) + { + name = scope->engine->newString("gc"); + } + virtual Value call(ExecutionContext *ctx) + { + ctx->engine->memoryManager->runGC(); + return Value::undefinedValue(); + } +}; + +} // builtins + +static void showException(QQmlJS::VM::ExecutionContext *ctx) +{ + QQmlJS::VM::ErrorObject *e = ctx->engine->exception.asErrorObject(); + if (!e) { + std::cerr << "Uncaught exception: " << qPrintable(ctx->engine->exception.toString(ctx)->toQString()) << std::endl; + return; + } + + if (QQmlJS::VM::SyntaxErrorObject *err = e->asSyntaxError()) { + QQmlJS::VM::DiagnosticMessage *msg = err->message(); + if (!msg) { + std::cerr << "Uncaught exception: Syntax error" << std::endl; + return; + } + + for (; msg; msg = msg->next) { + std::cerr << qPrintable(msg->buildFullMessage(ctx)->toQString()) << std::endl; + } + } else { + std::cerr << "Uncaught exception: " << qPrintable(e->__get__(ctx, ctx->engine->newString(QStringLiteral("message")), 0).toString(ctx)->toQString()) << std::endl; + } +} + +#ifndef QMLJS_NO_LLVM +int executeLLVMCode(void *codePtr) +{ + using namespace QQmlJS; + + if (!codePtr) + return EXIT_FAILURE; + void (*code)(VM::ExecutionContext *) = (void (*)(VM::ExecutionContext *)) codePtr; + + QScopedPointer iSelFactory(new QQmlJS::Moth::ISelFactory); + VM::ExecutionEngine vm(iSelFactory.data()); + VM::ExecutionContext *ctx = vm.rootContext; + + QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); + globalObject->__put__(ctx, vm.newIdentifier(QStringLiteral("print")), + QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::Print(ctx))); + + void * buf = __qmljs_create_exception_handler(ctx); + if (setjmp(*(jmp_buf *)buf)) { + showException(ctx); + return EXIT_FAILURE; + } + + code(ctx); + return EXIT_SUCCESS; +} + +int compile(const QString &fileName, const QString &source, QQmlJS::LLVMOutputType outputType) +{ + using namespace QQmlJS; + + IR::Module module; + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(source, 1, false); + Parser parser(engine); + + const bool parsed = parser.parseProgram(); + + foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { + std::cerr << qPrintable(fileName) << ':' + << m.loc.startLine << ':' + << m.loc.startColumn << ": error: " + << qPrintable(m.message) << std::endl; + } + + if (!parsed) + return EXIT_FAILURE; + + using namespace AST; + Program *program = AST::cast(parser.rootNode()); + + class MyErrorHandler: public ErrorHandler { + public: + virtual void syntaxError(QQmlJS::VM::DiagnosticMessage *message) { + for (; message; message = message->next) { + std::cerr << qPrintable(message->fileName) << ':' + << message->startLine << ':' + << message->startColumn << ": " + << (message->type == QQmlJS::VM::DiagnosticMessage::Error ? "error" : "warning") << ": " + << qPrintable(message->message) << std::endl; + } + } + } errorHandler; + + Codegen cg(&errorHandler, false); + // FIXME: if the program is empty, we should we generate an empty %entry, or give an error? + /*IR::Function *globalCode =*/ cg(fileName, program, &module); + + int (*exec)(void *) = outputType == LLVMOutputJit ? executeLLVMCode : 0; + return compileWithLLVM(&module, fileName, outputType, exec); +} + +int compileFiles(const QStringList &files, QQmlJS::LLVMOutputType outputType) +{ + foreach (const QString &fileName, files) { + QFile file(fileName); + if (file.open(QFile::ReadOnly)) { + QString source = QString::fromUtf8(file.readAll()); + int result = compile(fileName, source, outputType); + if (result != EXIT_SUCCESS) + return result; + } else { + std::cerr << "Error: cannot open file " << fileName.toUtf8().constData() << std::endl; + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} + +int evaluateCompiledCode(const QStringList &files) +{ + using namespace QQmlJS; + + foreach (const QString &libName, files) { + QFileInfo libInfo(libName); + QLibrary lib(libInfo.absoluteFilePath()); + lib.load(); + QFunctionPointer ptr = lib.resolve("%entry"); +// qDebug("_%%entry resolved to address %p", ptr); + int result = executeLLVMCode((void *) ptr); + if (result != EXIT_SUCCESS) + return result; + } + + return EXIT_SUCCESS; +} + +#endif + + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + QStringList args = app.arguments(); + args.removeFirst(); + + enum { + use_masm, + use_moth, + use_llvm_compiler, + use_llvm_runtime, + use_llvm_jit + } mode = use_masm; + +#ifndef QMLJS_NO_LLVM + QQmlJS::LLVMOutputType fileType = QQmlJS::LLVMOutputObject; +#endif // QMLJS_NO_LLVM + bool enableDebugging = false; + + if (!args.isEmpty()) { + if (args.first() == QLatin1String("-d") || args.first() == QLatin1String("--debug")) { + enableDebugging = true; + args.removeFirst(); + } + } + + if (!args.isEmpty()) { + if (args.first() == QLatin1String("--jit")) { + mode = use_masm; + args.removeFirst(); + } + + if (args.first() == QLatin1String("--interpret")) { + mode = use_moth; + args.removeFirst(); + } + +#ifndef QMLJS_NO_LLVM + if (args.first() == QLatin1String("--compile")) { + mode = use_llvm_compiler; + args.removeFirst(); + + if (!args.isEmpty() && args.first() == QLatin1String("-t")) { + args.removeFirst(); + // Note: keep this list in sync with the enum! + static QStringList fileTypes = QStringList() << QLatin1String("ll") << QLatin1String("bc") << QLatin1String("asm") << QLatin1String("obj"); + if (args.isEmpty() || !fileTypes.contains(args.first())) { + std::cerr << "file types: ll, bc, asm, obj" << std::endl; + return EXIT_FAILURE; + } + fileType = (QQmlJS::LLVMOutputType) fileTypes.indexOf(args.first()); + args.removeFirst(); + } + } + + if (args.first() == QLatin1String("--aot")) { + mode = use_llvm_runtime; + args.removeFirst(); + } + + if (args.first() == QLatin1String("--llvm-jit")) { + mode = use_llvm_jit; + args.removeFirst(); + } +#endif // QMLJS_NO_LLVM + if (args.first() == QLatin1String("--help")) { + std::cerr << "Usage: v4 [|--debug|-d] [|--jit|--interpret|--compile|--aot|--llvm-jit] file..." << std::endl; + return EXIT_SUCCESS; + } + } + + switch (mode) { +#ifdef QMLJS_NO_LLVM + case use_llvm_compiler: + case use_llvm_runtime: + case use_llvm_jit: + std::cerr << "LLVM backend was not built, compiler is unavailable." << std::endl; + return EXIT_FAILURE; +#else // QMLJS_NO_LLVM + case use_llvm_jit: + return compileFiles(args, QQmlJS::LLVMOutputJit); + case use_llvm_compiler: + return compileFiles(args, fileType); + case use_llvm_runtime: + return evaluateCompiledCode(args); +#endif // QMLJS_NO_LLVM + case use_masm: + case use_moth: { + QScopedPointer iSelFactory; + if (mode == use_moth) { + iSelFactory.reset(new QQmlJS::Moth::ISelFactory); + } else { + iSelFactory.reset(new QQmlJS::MASM::ISelFactory); + } + + QQmlJS::VM::ExecutionEngine vm(iSelFactory.data()); + + QScopedPointer debugger; + if (enableDebugging) + debugger.reset(new QQmlJS::Debugging::Debugger(&vm)); + vm.debugger = debugger.data(); + + QQmlJS::VM::ExecutionContext *ctx = vm.rootContext; + + QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue(); + QQmlJS::VM::Object *print = new (ctx->engine->memoryManager) builtins::Print(ctx); + print->prototype = ctx->engine->objectPrototype; + globalObject->__put__(ctx, vm.newIdentifier(QStringLiteral("print")), + QQmlJS::VM::Value::fromObject(print)); + QQmlJS::VM::Object *gc = new (ctx->engine->memoryManager) builtins::GC(ctx); + gc->prototype = ctx->engine->objectPrototype; + globalObject->__put__(ctx, vm.newIdentifier(QStringLiteral("gc")), + QQmlJS::VM::Value::fromObject(gc)); + + bool errorInTestHarness = false; + if (!qgetenv("IN_TEST_HARNESS").isEmpty()) + globalObject->__put__(ctx, vm.newIdentifier(QStringLiteral("$ERROR")), + QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::TestHarnessError(ctx, errorInTestHarness))); + + foreach (const QString &fn, args) { + QFile file(fn); + if (file.open(QFile::ReadOnly)) { + const QString code = QString::fromUtf8(file.readAll()); + file.close(); + + void * buf = __qmljs_create_exception_handler(ctx); + if (setjmp(*(jmp_buf *)buf)) { + showException(ctx); + return EXIT_FAILURE; + } + + QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode, + /*strictMode =*/ false, /*inheritContext =*/ false); + if (!f) + continue; + vm.globalCode = f; + + ctx->strictMode = f->isStrict; + if (debugger) + debugger->aboutToCall(0, ctx); + QQmlJS::VM::Value result = f->code(ctx, f->codeData); + if (debugger) + debugger->justLeft(ctx); + if (!result.isUndefined()) { + if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) + std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; + } + + if (errorInTestHarness) + return EXIT_FAILURE; + } else { + std::cerr << "Error: cannot open file " << fn.toUtf8().constData() << std::endl; + return EXIT_FAILURE; + } + } + + vm.memoryManager->dumpStats(); + } return EXIT_SUCCESS; + } // switch (mode) +} diff --git a/tools/v4/v4.pro b/tools/v4/v4.pro new file mode 100644 index 0000000000..db187530a3 --- /dev/null +++ b/tools/v4/v4.pro @@ -0,0 +1,18 @@ +TEMPLATE = app +QT = v4-private core-private qmldevtools-private +SOURCES = main.cpp + +INCLUDEPATH += ../../src/v4 +INCLUDEPATH += ../../src/3rdparty/masm +INCLUDEPATH += ../../src/3rdparty/masm/wtf +INCLUDEPATH += ../../src/3rdparty/masm/stubs +INCLUDEPATH += ../../src/3rdparty/masm/stubs/wtf +INCLUDEPATH += ../../src/3rdparty/masm/jit +INCLUDEPATH += ../../src/3rdparty/masm/assembler +INCLUDEPATH += ../../src/3rdparty/masm/disassembler + +DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" +DEFINES += QMLJS_NO_LLVM +DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 + +load(qt_tool) -- cgit v1.2.3 From cab1cac7bb5d7f3f3cac6dd987e1c3fb137576fe Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 16:39:36 +0100 Subject: Convert Math functions to contextless form Change-Id: I49123f4396d64fb5e2a888d339c87c800981f4f3 Reviewed-by: Simon Hausmann --- src/v4/qv4mathobject.cpp | 85 ++++++++++++++++++++++++++---------------------- src/v4/qv4mathobject.h | 34 +++++++++---------- 2 files changed, 64 insertions(+), 55 deletions(-) diff --git a/src/v4/qv4mathobject.cpp b/src/v4/qv4mathobject.cpp index eae50c4db4..2f92e52e2f 100644 --- a/src/v4/qv4mathobject.cpp +++ b/src/v4/qv4mathobject.cpp @@ -96,49 +96,58 @@ static double copySign(double x, double y) return x; } -Value MathObject::method_abs(ExecutionContext *ctx) +Value MathObject::method_abs(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double v = ctx->argument(0).toNumber(ctx); + if (!argc) + return Value::fromDouble(qSNaN()); + + if (argv[0].isInteger()) { + int i = argv[0].integerValue(); + return Value::fromInt32(i < 0 ? - i : i); + } + + double v = argv[0].toNumber(parentCtx); if (v == 0) // 0 | -0 return Value::fromDouble(0); return Value::fromDouble(v < 0 ? -v : v); } -Value MathObject::method_acos(ExecutionContext *ctx) +Value MathObject::method_acos(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double v = ctx->argument(0).toNumber(ctx); + double v = argc ? argv[0].toNumber(parentCtx) : 2; if (v > 1) return Value::fromDouble(qSNaN()); return Value::fromDouble(::acos(v)); } -Value MathObject::method_asin(ExecutionContext *ctx) +Value MathObject::method_asin(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double v = ctx->argument(0).toNumber(ctx); + double v = argc ? argv[0].toNumber(parentCtx) : 2; if (v > 1) return Value::fromDouble(qSNaN()); else return Value::fromDouble(::asin(v)); } -Value MathObject::method_atan(ExecutionContext *ctx) +Value MathObject::method_atan(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double v = ctx->argument(0).toNumber(ctx); + double v = argc ? argv[0].toNumber(parentCtx) : qSNaN(); if (v == 0.0) return Value::fromDouble(v); else return Value::fromDouble(::atan(v)); } -Value MathObject::method_atan2(ExecutionContext *ctx) +Value MathObject::method_atan2(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double v1 = ctx->argument(0).toNumber(ctx); - double v2 = ctx->argument(1).toNumber(ctx); - if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) { + double v1 = argc ? argv[0].toNumber(parentCtx) : qSNaN(); + double v2 = argc > 1 ? argv[1].toNumber(parentCtx) : qSNaN(); + + if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) return Value::fromDouble(copySign(0, -1.0)); - } + if ((v1 == 0.0) && (v2 == 0.0)) { if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) { return Value::fromDouble(qt_PI); @@ -149,24 +158,24 @@ Value MathObject::method_atan2(ExecutionContext *ctx) return Value::fromDouble(::atan2(v1, v2)); } -Value MathObject::method_ceil(ExecutionContext *ctx) +Value MathObject::method_ceil(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double v = ctx->argument(0).toNumber(ctx); + double v = argc ? argv[0].toNumber(parentCtx) : qSNaN(); if (v < 0.0 && v > -1.0) return Value::fromDouble(copySign(0, -1.0)); else return Value::fromDouble(::ceil(v)); } -Value MathObject::method_cos(ExecutionContext *ctx) +Value MathObject::method_cos(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double v = ctx->argument(0).toNumber(ctx); + double v = argc ? argv[0].toNumber(parentCtx) : qSNaN(); return Value::fromDouble(::cos(v)); } -Value MathObject::method_exp(ExecutionContext *ctx) +Value MathObject::method_exp(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double v = ctx->argument(0).toNumber(ctx); + double v = argc ? argv[0].toNumber(parentCtx) : qSNaN(); if (qIsInf(v)) { if (copySign(1.0, v) == -1.0) return Value::fromDouble(0); @@ -177,37 +186,37 @@ Value MathObject::method_exp(ExecutionContext *ctx) } } -Value MathObject::method_floor(ExecutionContext *ctx) +Value MathObject::method_floor(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double v = ctx->argument(0).toNumber(ctx); + double v = argc ? argv[0].toNumber(parentCtx) : qSNaN(); return Value::fromDouble(::floor(v)); } -Value MathObject::method_log(ExecutionContext *ctx) +Value MathObject::method_log(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double v = ctx->argument(0).toNumber(ctx); + double v = argc ? argv[0].toNumber(parentCtx) : qSNaN(); if (v < 0) return Value::fromDouble(qSNaN()); else return Value::fromDouble(::log(v)); } -Value MathObject::method_max(ExecutionContext *ctx) +Value MathObject::method_max(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { double mx = -qInf(); - for (unsigned i = 0; i < ctx->argumentCount; ++i) { - double x = ctx->argument(i).toNumber(ctx); + for (unsigned i = 0; i < argc; ++i) { + double x = argv[i].toNumber(parentCtx); if (x > mx || std::isnan(x)) mx = x; } return Value::fromDouble(mx); } -Value MathObject::method_min(ExecutionContext *ctx) +Value MathObject::method_min(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { double mx = qInf(); - for (unsigned i = 0; i < ctx->argumentCount; ++i) { - double x = ctx->argument(i).toNumber(ctx); + for (unsigned i = 0; i < argc; ++i) { + double x = argv[i].toNumber(parentCtx); if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) || (x < mx) || std::isnan(x)) { mx = x; @@ -266,33 +275,33 @@ Value MathObject::method_pow(ExecutionContext *parentCtx, Value, Value *argv, in return Value::fromDouble(qSNaN()); } -Value MathObject::method_random(ExecutionContext */*ctx*/) +Value MathObject::method_random(ExecutionContext *, Value, Value *, int) { return Value::fromDouble(qrand() / (double) RAND_MAX); } -Value MathObject::method_round(ExecutionContext *ctx) +Value MathObject::method_round(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double v = ctx->argument(0).toNumber(ctx); + double v = argc ? argv[0].toNumber(parentCtx) : qSNaN(); v = copySign(::floor(v + 0.5), v); return Value::fromDouble(v); } -Value MathObject::method_sin(ExecutionContext *ctx) +Value MathObject::method_sin(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double v = ctx->argument(0).toNumber(ctx); + double v = argc ? argv[0].toNumber(parentCtx) : qSNaN(); return Value::fromDouble(::sin(v)); } -Value MathObject::method_sqrt(ExecutionContext *ctx) +Value MathObject::method_sqrt(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double v = ctx->argument(0).toNumber(ctx); + double v = argc ? argv[0].toNumber(parentCtx) : qSNaN(); return Value::fromDouble(::sqrt(v)); } -Value MathObject::method_tan(ExecutionContext *ctx) +Value MathObject::method_tan(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { - double v = ctx->argument(0).toNumber(ctx); + double v = argc ? argv[0].toNumber(parentCtx) : qSNaN(); if (v == 0.0) return Value::fromDouble(v); else diff --git a/src/v4/qv4mathobject.h b/src/v4/qv4mathobject.h index c8428d2942..180313786f 100644 --- a/src/v4/qv4mathobject.h +++ b/src/v4/qv4mathobject.h @@ -52,24 +52,24 @@ struct MathObject: Object MathObject(ExecutionContext *ctx); virtual QString className() { return QStringLiteral("Math"); } - static Value method_abs(ExecutionContext *ctx); - static Value method_acos(ExecutionContext *ctx); - static Value method_asin(ExecutionContext *ctx); - static Value method_atan(ExecutionContext *ctx); - static Value method_atan2(ExecutionContext *ctx); - static Value method_ceil(ExecutionContext *ctx); - static Value method_cos(ExecutionContext *ctx); - static Value method_exp(ExecutionContext *ctx); - static Value method_floor(ExecutionContext *ctx); - static Value method_log(ExecutionContext *ctx); - static Value method_max(ExecutionContext *ctx); - static Value method_min(ExecutionContext *ctx); + static Value method_abs(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_acos(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_asin(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_atan(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_atan2(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_ceil(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_cos(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_exp(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_floor(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_log(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_max(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_min(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); static Value method_pow(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); - static Value method_random(ExecutionContext *ctx); - static Value method_round(ExecutionContext *ctx); - static Value method_sin(ExecutionContext *ctx); - static Value method_sqrt(ExecutionContext *ctx); - static Value method_tan(ExecutionContext *ctx); + static Value method_random(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_round(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_sin(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_sqrt(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value method_tan(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); }; } // namespace VM -- cgit v1.2.3 From bd30bc124488c6214364f7716b43b99d7553ecf8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 30 Jan 2013 17:11:09 +0100 Subject: Minor improvements to Context creation Avoid converting the this object for builtins, and remove the GC blocker that's not needed anymore. Move some parts of the code out to the caller of initCallContext(). Change-Id: I8b11dd1604fb14940e4d4e9f403c2a8bfc837c4b Reviewed-by: Simon Hausmann --- src/v4/qmljs_environment.cpp | 51 +++++++++++++++++--------------------------- src/v4/qmljs_environment.h | 2 +- src/v4/qv4functionobject.cpp | 26 ++++++++++++++++------ src/v4/qv4globalobject.cpp | 6 +++++- tests/TestExpectations | 3 +-- 5 files changed, 46 insertions(+), 42 deletions(-) diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp index 8ff93c1b98..76d08337c8 100644 --- a/src/v4/qmljs_environment.cpp +++ b/src/v4/qmljs_environment.cpp @@ -492,61 +492,48 @@ void ExecutionContext::throwURIError(Value msg) throwError(Value::fromObject(engine->newURIErrorObject(this, msg))); } -void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc) +void ExecutionContext::initCallContext(ExecutionContext *parent) { - MemoryManager::GCBlocker blockGC(parent->engine->memoryManager); - engine = parent->engine; this->parent = parent; - outer = f->scope; + outer = function->scope; engine->current = this; - function = f; - strictMode = f->strictMode; + activation = 0; + withObject = 0; - thisObject = that; - if (!strictMode && !thisObject.isObject()) { - if (thisObject.isUndefined() || thisObject.isNull()) - thisObject = engine->globalObject; - else - thisObject = thisObject.toObject(this); - } + strictMode = function->strictMode; - locals = function->varCount ? reinterpret_cast(this + 1) : 0; - if (locals) + if (function->varCount) { + locals = reinterpret_cast(this + 1); std::fill(locals, locals + function->varCount, Value::undefinedValue()); + } - arguments = args; - argumentCount = argc; - if (function->needsActivation || argc < function->formalParameterCount){ + uint argc = argumentCount; + if (function->needsActivation || argumentCount < function->formalParameterCount){ + Value *args = arguments; argumentCount = qMax(argc, function->formalParameterCount); arguments = reinterpret_cast(this + 1) + function->varCount; if (argc) std::copy(args, args + argc, arguments); if (argc < function->formalParameterCount) std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); - } - - activation = 0; - withObject = 0; - - if (function->usesArgumentsObject) { - ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this, function->formalParameterCount, argc); - args->prototype = engine->objectPrototype; - Value arguments = Value::fromObject(args); - createMutableBinding(engine->id_arguments, false); - setMutableBinding(this, engine->id_arguments, arguments); + if (function->usesArgumentsObject) { + ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this, function->formalParameterCount, argc); + args->prototype = engine->objectPrototype; + Value arguments = Value::fromObject(args); + createMutableBinding(engine->id_arguments, false); + setMutableBinding(this, engine->id_arguments, arguments); + } } if (engine->debugger) - engine->debugger->aboutToCall(f, this); + engine->debugger->aboutToCall(function, this); } void ExecutionContext::leaveCallContext() { - if (!function->needsActivation) - locals = 0; engine->current = parent; parent = 0; diff --git a/src/v4/qmljs_environment.h b/src/v4/qmljs_environment.h index 111c6ae239..28f000b39f 100644 --- a/src/v4/qmljs_environment.h +++ b/src/v4/qmljs_environment.h @@ -107,7 +107,7 @@ struct ExecutionContext ExecutionContext *createWithScope(Object *with); ExecutionContext *popScope(); - void initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc); + void initCallContext(ExecutionContext *parent); void leaveCallContext(); void wireUpPrototype(); diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index f5e37def0c..41c06f0896 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -134,7 +134,12 @@ Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc uint size = requiredMemoryForExecutionContect(this, argc); ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); - ctx->initCallContext(context, Value::fromObject(obj), this, args, argc); + ctx->thisObject = Value::fromObject(obj); + ctx->function = this; + ctx->arguments = args; + ctx->argumentCount = argc; + + ctx->initCallContext(context); Value result = construct(ctx); ctx->leaveCallContext(); @@ -148,15 +153,24 @@ Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *a uint size = requiredMemoryForExecutionContect(this, argc); ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); - - ctx->initCallContext(context, thisObject, this, args, argc); - if (isBuiltinFunction) { + if (!strictMode && !thisObject.isObject()) { // Built-in functions allow for the this object to be null or undefined. This overrides // the behaviour of changing thisObject to the global object if null/undefined and allows // the built-in functions for example to throw a type error if null is passed. - if (thisObject.isNull() || thisObject.isUndefined()) - ctx->thisObject = thisObject; + if (thisObject.isUndefined() || thisObject.isNull()) { + if (!isBuiltinFunction) + thisObject = context->engine->globalObject; + } else { + thisObject = thisObject.toObject(context); + } } + + ctx->thisObject = thisObject; + ctx->function = this; + ctx->arguments = args; + ctx->argumentCount = argc; + + ctx->initCallContext(context); Value result = call(ctx); ctx->leaveCallContext(); return result; diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp index 70ae41c3ed..fe4301160e 100644 --- a/src/v4/qv4globalobject.cpp +++ b/src/v4/qv4globalobject.cpp @@ -378,7 +378,11 @@ Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Va if (strict) { ctx = k; - ctx->initCallContext(context, context->thisObject, this, args, argc); + ctx->thisObject = directCall ? context->thisObject : context->engine->globalObject; + ctx->function = this; + ctx->arguments = args; + ctx->argumentCount = argc; + ctx->initCallContext(context); } // set the correct strict mode flag on the context diff --git a/tests/TestExpectations b/tests/TestExpectations index 39ace8bd60..9b1165cbf3 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -6,7 +6,6 @@ 10.4.2-1-2 failing 10.4.3-1-104 failing 10.4.3-1-106 failing -10.4.3-1-17-s failing 10.4.3-1-63-s failing 10.4.3-1-82-s failing 11.2.3-3_3 failing @@ -53,4 +52,4 @@ Sbp_A4_T2 failing S12.4_A1 failing S15.2.4.4_A14 failing # Try/catch scoping issue -S12.14_A4 failing +S12.14_A4 failing \ No newline at end of file -- cgit v1.2.3 From 0f29cb233a50f26869a0a52f738cad887d3f5fa0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 31 Jan 2013 07:21:19 +0100 Subject: Don't link v4 against QtGui The QT variable defaults to core and gui it seems, but we don't need the latter. Change-Id: I355d2ab5fb42da4eeaebb6a41ed638203d11650a Reviewed-by: Lars Knoll --- src/v4/v4.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/src/v4/v4.pro b/src/v4/v4.pro index e2987da2f3..fb7ea80544 100644 --- a/src/v4/v4.pro +++ b/src/v4/v4.pro @@ -1,5 +1,6 @@ TARGET = QtV4 QT_PRIVATE = core-private qmldevtools-private +QT = core CONFIG += internal_module -- cgit v1.2.3 From 20c4eef39c1534b1b7caa96879d9060cbb159c3e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 31 Jan 2013 09:16:48 +0100 Subject: V8 benchmark suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're currently failing two tests. Performance is between 2 and 20 times worse then V8 in full compiler mode. Change-Id: Ie78b4e996511777cb665dbf161e41dcfa0210b9b Reviewed-by: JÄ™drzej Nowacki --- tests/v8-bench.js | 11591 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 11591 insertions(+) create mode 100644 tests/v8-bench.js diff --git a/tests/v8-bench.js b/tests/v8-bench.js new file mode 100644 index 0000000000..baa6274136 --- /dev/null +++ b/tests/v8-bench.js @@ -0,0 +1,11591 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Simple framework for running the benchmark suites and +// computing a score based on the timing measurements. + +if (typeof print == "undefined") + + print = console.log; + +// A benchmark has a name (string) and a function that will be run to +// do the performance measurement. The optional setup and tearDown +// arguments are functions that will be invoked before and after +// running the benchmark, but the running time of these functions will +// not be accounted for in the benchmark score. +function Benchmark(name, run, setup, tearDown) { + this.name = name; + this.run = run; + this.Setup = setup ? setup : function() { }; + this.TearDown = tearDown ? tearDown : function() { }; +} + + +// Benchmark results hold the benchmark and the measured time used to +// run the benchmark. The benchmark score is computed later once a +// full benchmark suite has run to completion. +function BenchmarkResult(benchmark, time) { + this.benchmark = benchmark; + this.time = time; +} + + +// Automatically convert results to numbers. Used by the geometric +// mean computation. +BenchmarkResult.prototype.valueOf = function() { + return this.time; +} + + +// Suites of benchmarks consist of a name and the set of benchmarks in +// addition to the reference timing that the final score will be based +// on. This way, all scores are relative to a reference run and higher +// scores implies better performance. +function BenchmarkSuite(name, reference, benchmarks) { + this.name = name; + this.reference = reference; + this.benchmarks = benchmarks; + BenchmarkSuite.suites.push(this); +} + + +// Keep track of all declared benchmark suites. +BenchmarkSuite.suites = []; + + +// Scores are not comparable across versions. Bump the version if +// you're making changes that will affect that scores, e.g. if you add +// a new benchmark or change an existing one. +BenchmarkSuite.version = '7'; + + +// To make the benchmark results predictable, we replace Math.random +// with a 100% deterministic alternative. +Math.random = (function() { + var seed = 49734321; + return function() { + // Robert Jenkins' 32 bit integer hash function. + seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff; + seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff; + seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff; + seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff; + seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff; + seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff; + return (seed & 0xfffffff) / 0x10000000; + }; +})(); + + +// Runs all registered benchmark suites and optionally yields between +// each individual benchmark to avoid running for too long in the +// context of browsers. Once done, the final score is reported to the +// runner. +BenchmarkSuite.RunSuites = function(runner) { + var continuation = null; + var suites = BenchmarkSuite.suites; + var length = suites.length; + BenchmarkSuite.scores = []; + var index = 0; + function RunStep() { + while (continuation || index < length) { + if (continuation) { + continuation = continuation(); + } else { + var suite = suites[index++]; + if (runner.NotifyStart) runner.NotifyStart(suite.name); + continuation = suite.RunStep(runner); + } + if (continuation && typeof window != 'undefined' && window.setTimeout) { + window.setTimeout(RunStep, 25); + return; + } + } + if (runner.NotifyScore) { + var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores); + var formatted = BenchmarkSuite.FormatScore(100 * score); + runner.NotifyScore(formatted); + } + } + RunStep(); +} + + +// Counts the total number of registered benchmarks. Useful for +// showing progress as a percentage. +BenchmarkSuite.CountBenchmarks = function() { + var result = 0; + var suites = BenchmarkSuite.suites; + for (var i = 0; i < suites.length; i++) { + result += suites[i].benchmarks.length; + } + return result; +} + + +// Computes the geometric mean of a set of numbers. +BenchmarkSuite.GeometricMean = function(numbers) { + var log = 0; + for (var i = 0; i < numbers.length; i++) { + log += Math.log(numbers[i]); + } + return Math.pow(Math.E, log / numbers.length); +} + + +// Converts a score value to a string with at least three significant +// digits. +BenchmarkSuite.FormatScore = function(value) { + if (value > 100) { + return value.toFixed(0); + } else { + return value.toPrecision(3); + } +} + +// Notifies the runner that we're done running a single benchmark in +// the benchmark suite. This can be useful to report progress. +BenchmarkSuite.prototype.NotifyStep = function(result) { + this.results.push(result); + if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name); +} + + +// Notifies the runner that we're done with running a suite and that +// we have a result which can be reported to the user if needed. +BenchmarkSuite.prototype.NotifyResult = function() { + var mean = BenchmarkSuite.GeometricMean(this.results); + var score = this.reference / mean; + BenchmarkSuite.scores.push(score); + if (this.runner.NotifyResult) { + var formatted = BenchmarkSuite.FormatScore(100 * score); + this.runner.NotifyResult(this.name, formatted); + } +} + + +// Notifies the runner that running a benchmark resulted in an error. +BenchmarkSuite.prototype.NotifyError = function(error) { + if (this.runner.NotifyError) { + this.runner.NotifyError(this.name, error); + } + if (this.runner.NotifyStep) { + this.runner.NotifyStep(this.name); + } +} + + +// Runs a single benchmark for at least a second and computes the +// average time it takes to run a single iteration. +BenchmarkSuite.prototype.RunSingleBenchmark = function(benchmark, data) { + function Measure(data) { + var elapsed = 0; + var start = new Date(); + for (var n = 0; elapsed < 1000; n++) { + benchmark.run(); + elapsed = new Date() - start; + } + if (data != null) { + data.runs += n; + data.elapsed += elapsed; + } + } + + if (data == null) { + // Measure the benchmark once for warm up and throw the result + // away. Return a fresh data object. + Measure(null); + return { runs: 0, elapsed: 0 }; + } else { + Measure(data); + // If we've run too few iterations, we continue for another second. + if (data.runs < 32) return data; + var usec = (data.elapsed * 1000) / data.runs; + this.NotifyStep(new BenchmarkResult(benchmark, usec)); + return null; + } +} + + +// This function starts running a suite, but stops between each +// individual benchmark in the suite and returns a continuation +// function which can be invoked to run the next benchmark. Once the +// last benchmark has been executed, null is returned. +BenchmarkSuite.prototype.RunStep = function(runner) { + this.results = []; + this.runner = runner; + var length = this.benchmarks.length; + var index = 0; + var suite = this; + var data; + + // Run the setup, the actual benchmark, and the tear down in three + // separate steps to allow the framework to yield between any of the + // steps. + + function RunNextSetup() { + if (index < length) { + try { + suite.benchmarks[index].Setup(); + } catch (e) { + suite.NotifyError(e); + return null; + } + return RunNextBenchmark; + } + suite.NotifyResult(); + return null; + } + + function RunNextBenchmark() { + try { + data = suite.RunSingleBenchmark(suite.benchmarks[index], data); + } catch (e) { + suite.NotifyError(e); + return null; + } + // If data is null, we're done with this benchmark. + return (data == null) ? RunNextTearDown : RunNextBenchmark(); + } + + function RunNextTearDown() { + try { + suite.benchmarks[index++].TearDown(); + } catch (e) { + suite.NotifyError(e); + return null; + } + return RunNextSetup; + } + + // Start out running the setup. + return RunNextSetup(); +} +// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// This is a JavaScript implementation of the Richards +// benchmark from: +// +// http://www.cl.cam.ac.uk/~mr10/Bench.html +// +// The benchmark was originally implemented in BCPL by +// Martin Richards. + + +var Richards = new BenchmarkSuite('Richards', 35302, [ + new Benchmark("Richards", runRichards) +]); + + +/** + * The Richards benchmark simulates the task dispatcher of an + * operating system. + **/ +function runRichards() { + var scheduler = new Scheduler(); + scheduler.addIdleTask(ID_IDLE, 0, null, COUNT); + + var queue = new Packet(null, ID_WORKER, KIND_WORK); + queue = new Packet(queue, ID_WORKER, KIND_WORK); + scheduler.addWorkerTask(ID_WORKER, 1000, queue); + + queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE); + queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE); + queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE); + scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue); + + queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE); + queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE); + queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE); + scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue); + + scheduler.addDeviceTask(ID_DEVICE_A, 4000, null); + + scheduler.addDeviceTask(ID_DEVICE_B, 5000, null); + + scheduler.schedule(); + + if (scheduler.queueCount != EXPECTED_QUEUE_COUNT || + scheduler.holdCount != EXPECTED_HOLD_COUNT) { + var msg = + "Error during execution: queueCount = " + scheduler.queueCount + + ", holdCount = " + scheduler.holdCount + "."; + throw new Error(msg); + } +} + +var COUNT = 1000; + +/** + * These two constants specify how many times a packet is queued and + * how many times a task is put on hold in a correct run of richards. + * They don't have any meaning a such but are characteristic of a + * correct run so if the actual queue or hold count is different from + * the expected there must be a bug in the implementation. + **/ +var EXPECTED_QUEUE_COUNT = 2322; +var EXPECTED_HOLD_COUNT = 928; + + +/** + * A scheduler can be used to schedule a set of tasks based on their relative + * priorities. Scheduling is done by maintaining a list of task control blocks + * which holds tasks and the data queue they are processing. + * @constructor + */ +function Scheduler() { + this.queueCount = 0; + this.holdCount = 0; + this.blocks = new Array(NUMBER_OF_IDS); + this.list = null; + this.currentTcb = null; + this.currentId = null; +} + +var ID_IDLE = 0; +var ID_WORKER = 1; +var ID_HANDLER_A = 2; +var ID_HANDLER_B = 3; +var ID_DEVICE_A = 4; +var ID_DEVICE_B = 5; +var NUMBER_OF_IDS = 6; + +var KIND_DEVICE = 0; +var KIND_WORK = 1; + +/** + * Add an idle task to this scheduler. + * @param {int} id the identity of the task + * @param {int} priority the task's priority + * @param {Packet} queue the queue of work to be processed by the task + * @param {int} count the number of times to schedule the task + */ +Scheduler.prototype.addIdleTask = function (id, priority, queue, count) { + this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count)); +}; + +/** + * Add a work task to this scheduler. + * @param {int} id the identity of the task + * @param {int} priority the task's priority + * @param {Packet} queue the queue of work to be processed by the task + */ +Scheduler.prototype.addWorkerTask = function (id, priority, queue) { + this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0)); +}; + +/** + * Add a handler task to this scheduler. + * @param {int} id the identity of the task + * @param {int} priority the task's priority + * @param {Packet} queue the queue of work to be processed by the task + */ +Scheduler.prototype.addHandlerTask = function (id, priority, queue) { + this.addTask(id, priority, queue, new HandlerTask(this)); +}; + +/** + * Add a handler task to this scheduler. + * @param {int} id the identity of the task + * @param {int} priority the task's priority + * @param {Packet} queue the queue of work to be processed by the task + */ +Scheduler.prototype.addDeviceTask = function (id, priority, queue) { + this.addTask(id, priority, queue, new DeviceTask(this)) +}; + +/** + * Add the specified task and mark it as running. + * @param {int} id the identity of the task + * @param {int} priority the task's priority + * @param {Packet} queue the queue of work to be processed by the task + * @param {Task} task the task to add + */ +Scheduler.prototype.addRunningTask = function (id, priority, queue, task) { + this.addTask(id, priority, queue, task); + this.currentTcb.setRunning(); +}; + +/** + * Add the specified task to this scheduler. + * @param {int} id the identity of the task + * @param {int} priority the task's priority + * @param {Packet} queue the queue of work to be processed by the task + * @param {Task} task the task to add + */ +Scheduler.prototype.addTask = function (id, priority, queue, task) { + this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task); + this.list = this.currentTcb; + this.blocks[id] = this.currentTcb; +}; + +/** + * Execute the tasks managed by this scheduler. + */ +Scheduler.prototype.schedule = function () { + this.currentTcb = this.list; + while (this.currentTcb != null) { + if (this.currentTcb.isHeldOrSuspended()) { + this.currentTcb = this.currentTcb.link; + } else { + this.currentId = this.currentTcb.id; + this.currentTcb = this.currentTcb.run(); + } + } +}; + +/** + * Release a task that is currently blocked and return the next block to run. + * @param {int} id the id of the task to suspend + */ +Scheduler.prototype.release = function (id) { + var tcb = this.blocks[id]; + if (tcb == null) return tcb; + tcb.markAsNotHeld(); + if (tcb.priority > this.currentTcb.priority) { + return tcb; + } else { + return this.currentTcb; + } +}; + +/** + * Block the currently executing task and return the next task control block + * to run. The blocked task will not be made runnable until it is explicitly + * released, even if new work is added to it. + */ +Scheduler.prototype.holdCurrent = function () { + this.holdCount++; + this.currentTcb.markAsHeld(); + return this.currentTcb.link; +}; + +/** + * Suspend the currently executing task and return the next task control block + * to run. If new work is added to the suspended task it will be made runnable. + */ +Scheduler.prototype.suspendCurrent = function () { + this.currentTcb.markAsSuspended(); + return this.currentTcb; +}; + +/** + * Add the specified packet to the end of the worklist used by the task + * associated with the packet and make the task runnable if it is currently + * suspended. + * @param {Packet} packet the packet to add + */ +Scheduler.prototype.queue = function (packet) { + var t = this.blocks[packet.id]; + if (t == null) return t; + this.queueCount++; + packet.link = null; + packet.id = this.currentId; + return t.checkPriorityAdd(this.currentTcb, packet); +}; + +/** + * A task control block manages a task and the queue of work packages associated + * with it. + * @param {TaskControlBlock} link the preceding block in the linked block list + * @param {int} id the id of this block + * @param {int} priority the priority of this block + * @param {Packet} queue the queue of packages to be processed by the task + * @param {Task} task the task + * @constructor + */ +function TaskControlBlock(link, id, priority, queue, task) { + this.link = link; + this.id = id; + this.priority = priority; + this.queue = queue; + this.task = task; + if (queue == null) { + this.state = STATE_SUSPENDED; + } else { + this.state = STATE_SUSPENDED_RUNNABLE; + } +} + +/** + * The task is running and is currently scheduled. + */ +var STATE_RUNNING = 0; + +/** + * The task has packets left to process. + */ +var STATE_RUNNABLE = 1; + +/** + * The task is not currently running. The task is not blocked as such and may +* be started by the scheduler. + */ +var STATE_SUSPENDED = 2; + +/** + * The task is blocked and cannot be run until it is explicitly released. + */ +var STATE_HELD = 4; + +var STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE; +var STATE_NOT_HELD = ~STATE_HELD; + +TaskControlBlock.prototype.setRunning = function () { + this.state = STATE_RUNNING; +}; + +TaskControlBlock.prototype.markAsNotHeld = function () { + this.state = this.state & STATE_NOT_HELD; +}; + +TaskControlBlock.prototype.markAsHeld = function () { + this.state = this.state | STATE_HELD; +}; + +TaskControlBlock.prototype.isHeldOrSuspended = function () { + return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED); +}; + +TaskControlBlock.prototype.markAsSuspended = function () { + this.state = this.state | STATE_SUSPENDED; +}; + +TaskControlBlock.prototype.markAsRunnable = function () { + this.state = this.state | STATE_RUNNABLE; +}; + +/** + * Runs this task, if it is ready to be run, and returns the next task to run. + */ +TaskControlBlock.prototype.run = function () { + var packet; + if (this.state == STATE_SUSPENDED_RUNNABLE) { + packet = this.queue; + this.queue = packet.link; + if (this.queue == null) { + this.state = STATE_RUNNING; + } else { + this.state = STATE_RUNNABLE; + } + } else { + packet = null; + } + return this.task.run(packet); +}; + +/** + * Adds a packet to the worklist of this block's task, marks this as runnable if + * necessary, and returns the next runnable object to run (the one + * with the highest priority). + */ +TaskControlBlock.prototype.checkPriorityAdd = function (task, packet) { + if (this.queue == null) { + this.queue = packet; + this.markAsRunnable(); + if (this.priority > task.priority) return this; + } else { + this.queue = packet.addTo(this.queue); + } + return task; +}; + +TaskControlBlock.prototype.toString = function () { + return "tcb { " + this.task + "@" + this.state + " }"; +}; + +/** + * An idle task doesn't do any work itself but cycles control between the two + * device tasks. + * @param {Scheduler} scheduler the scheduler that manages this task + * @param {int} v1 a seed value that controls how the device tasks are scheduled + * @param {int} count the number of times this task should be scheduled + * @constructor + */ +function IdleTask(scheduler, v1, count) { + this.scheduler = scheduler; + this.v1 = v1; + this.count = count; +} + +IdleTask.prototype.run = function (packet) { + this.count--; + if (this.count == 0) return this.scheduler.holdCurrent(); + if ((this.v1 & 1) == 0) { + this.v1 = this.v1 >> 1; + return this.scheduler.release(ID_DEVICE_A); + } else { + this.v1 = (this.v1 >> 1) ^ 0xD008; + return this.scheduler.release(ID_DEVICE_B); + } +}; + +IdleTask.prototype.toString = function () { + return "IdleTask" +}; + +/** + * A task that suspends itself after each time it has been run to simulate + * waiting for data from an external device. + * @param {Scheduler} scheduler the scheduler that manages this task + * @constructor + */ +function DeviceTask(scheduler) { + this.scheduler = scheduler; + this.v1 = null; +} + +DeviceTask.prototype.run = function (packet) { + if (packet == null) { + if (this.v1 == null) return this.scheduler.suspendCurrent(); + var v = this.v1; + this.v1 = null; + return this.scheduler.queue(v); + } else { + this.v1 = packet; + return this.scheduler.holdCurrent(); + } +}; + +DeviceTask.prototype.toString = function () { + return "DeviceTask"; +}; + +/** + * A task that manipulates work packets. + * @param {Scheduler} scheduler the scheduler that manages this task + * @param {int} v1 a seed used to specify how work packets are manipulated + * @param {int} v2 another seed used to specify how work packets are manipulated + * @constructor + */ +function WorkerTask(scheduler, v1, v2) { + this.scheduler = scheduler; + this.v1 = v1; + this.v2 = v2; +} + +WorkerTask.prototype.run = function (packet) { + if (packet == null) { + return this.scheduler.suspendCurrent(); + } else { + if (this.v1 == ID_HANDLER_A) { + this.v1 = ID_HANDLER_B; + } else { + this.v1 = ID_HANDLER_A; + } + packet.id = this.v1; + packet.a1 = 0; + for (var i = 0; i < DATA_SIZE; i++) { + this.v2++; + if (this.v2 > 26) this.v2 = 1; + packet.a2[i] = this.v2; + } + return this.scheduler.queue(packet); + } +}; + +WorkerTask.prototype.toString = function () { + return "WorkerTask"; +}; + +/** + * A task that manipulates work packets and then suspends itself. + * @param {Scheduler} scheduler the scheduler that manages this task + * @constructor + */ +function HandlerTask(scheduler) { + this.scheduler = scheduler; + this.v1 = null; + this.v2 = null; +} + +HandlerTask.prototype.run = function (packet) { + if (packet != null) { + if (packet.kind == KIND_WORK) { + this.v1 = packet.addTo(this.v1); + } else { + this.v2 = packet.addTo(this.v2); + } + } + if (this.v1 != null) { + var count = this.v1.a1; + var v; + if (count < DATA_SIZE) { + if (this.v2 != null) { + v = this.v2; + this.v2 = this.v2.link; + v.a1 = this.v1.a2[count]; + this.v1.a1 = count + 1; + return this.scheduler.queue(v); + } + } else { + v = this.v1; + this.v1 = this.v1.link; + return this.scheduler.queue(v); + } + } + return this.scheduler.suspendCurrent(); +}; + +HandlerTask.prototype.toString = function () { + return "HandlerTask"; +}; + +/* --- * + * P a c k e t + * --- */ + +var DATA_SIZE = 4; + +/** + * A simple package of data that is manipulated by the tasks. The exact layout + * of the payload data carried by a packet is not importaint, and neither is the + * nature of the work performed on packets by the tasks. + * + * Besides carrying data, packets form linked lists and are hence used both as + * data and worklists. + * @param {Packet} link the tail of the linked list of packets + * @param {int} id an ID for this packet + * @param {int} kind the type of this packet + * @constructor + */ +function Packet(link, id, kind) { + this.link = link; + this.id = id; + this.kind = kind; + this.a1 = 0; + this.a2 = new Array(DATA_SIZE); +} + +/** + * Add this packet to the end of a worklist, and return the worklist. + * @param {Packet} queue the worklist to add this packet to + */ +Packet.prototype.addTo = function (queue) { + this.link = null; + if (queue == null) return this; + var peek, next = queue; + while ((peek = next.link) != null) + next = peek; + next.link = this; + return queue; +}; + +Packet.prototype.toString = function () { + return "Packet"; +}; +// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 1996 John Maloney and Mario Wolczko. + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +// This implementation of the DeltaBlue benchmark is derived +// from the Smalltalk implementation by John Maloney and Mario +// Wolczko. Some parts have been translated directly, whereas +// others have been modified more aggresively to make it feel +// more like a JavaScript program. + + +var DeltaBlue = new BenchmarkSuite('DeltaBlue', 66118, [ + new Benchmark('DeltaBlue', deltaBlue) +]); + + +/** + * A JavaScript implementation of the DeltaBlue constraint-solving + * algorithm, as described in: + * + * "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver" + * Bjorn N. Freeman-Benson and John Maloney + * January 1990 Communications of the ACM, + * also available as University of Washington TR 89-08-06. + * + * Beware: this benchmark is written in a grotesque style where + * the constraint model is built by side-effects from constructors. + * I've kept it this way to avoid deviating too much from the original + * implementation. + */ + + +/* --- O b j e c t M o d e l --- */ + +Object.prototype.inheritsFrom = function (shuper) { + function Inheriter() { } + Inheriter.prototype = shuper.prototype; + this.prototype = new Inheriter(); + this.superConstructor = shuper; +} + +function OrderedCollection() { + this.elms = new Array(); +} + +OrderedCollection.prototype.add = function (elm) { + this.elms.push(elm); +} + +OrderedCollection.prototype.at = function (index) { + return this.elms[index]; +} + +OrderedCollection.prototype.size = function () { + return this.elms.length; +} + +OrderedCollection.prototype.removeFirst = function () { + return this.elms.pop(); +} + +OrderedCollection.prototype.remove = function (elm) { + var index = 0, skipped = 0; + for (var i = 0; i < this.elms.length; i++) { + var value = this.elms[i]; + if (value != elm) { + this.elms[index] = value; + index++; + } else { + skipped++; + } + } + for (var i = 0; i < skipped; i++) + this.elms.pop(); +} + +/* --- * + * S t r e n g t h + * --- */ + +/** + * Strengths are used to measure the relative importance of constraints. + * New strengths may be inserted in the strength hierarchy without + * disrupting current constraints. Strengths cannot be created outside + * this class, so pointer comparison can be used for value comparison. + */ +function Strength(strengthValue, name) { + this.strengthValue = strengthValue; + this.name = name; +} + +Strength.stronger = function (s1, s2) { + return s1.strengthValue < s2.strengthValue; +} + +Strength.weaker = function (s1, s2) { + return s1.strengthValue > s2.strengthValue; +} + +Strength.weakestOf = function (s1, s2) { + return this.weaker(s1, s2) ? s1 : s2; +} + +Strength.strongest = function (s1, s2) { + return this.stronger(s1, s2) ? s1 : s2; +} + +Strength.prototype.nextWeaker = function () { + switch (this.strengthValue) { + case 0: return Strength.WEAKEST; + case 1: return Strength.WEAK_DEFAULT; + case 2: return Strength.NORMAL; + case 3: return Strength.STRONG_DEFAULT; + case 4: return Strength.PREFERRED; + case 5: return Strength.REQUIRED; + } +} + +// Strength constants. +Strength.REQUIRED = new Strength(0, "required"); +Strength.STONG_PREFERRED = new Strength(1, "strongPreferred"); +Strength.PREFERRED = new Strength(2, "preferred"); +Strength.STRONG_DEFAULT = new Strength(3, "strongDefault"); +Strength.NORMAL = new Strength(4, "normal"); +Strength.WEAK_DEFAULT = new Strength(5, "weakDefault"); +Strength.WEAKEST = new Strength(6, "weakest"); + +/* --- * + * C o n s t r a i n t + * --- */ + +/** + * An abstract class representing a system-maintainable relationship + * (or "constraint") between a set of variables. A constraint supplies + * a strength instance variable; concrete subclasses provide a means + * of storing the constrained variables and other information required + * to represent a constraint. + */ +function Constraint(strength) { + this.strength = strength; +} + +/** + * Activate this constraint and attempt to satisfy it. + */ +Constraint.prototype.addConstraint = function () { + this.addToGraph(); + planner.incrementalAdd(this); +} + +/** + * Attempt to find a way to enforce this constraint. If successful, + * record the solution, perhaps modifying the current dataflow + * graph. Answer the constraint that this constraint overrides, if + * there is one, or nil, if there isn't. + * Assume: I am not already satisfied. + */ +Constraint.prototype.satisfy = function (mark) { + this.chooseMethod(mark); + if (!this.isSatisfied()) { + if (this.strength == Strength.REQUIRED) + alert("Could not satisfy a required constraint!"); + return null; + } + this.markInputs(mark); + var out = this.output(); + var overridden = out.determinedBy; + if (overridden != null) overridden.markUnsatisfied(); + out.determinedBy = this; + if (!planner.addPropagate(this, mark)) + alert("Cycle encountered"); + out.mark = mark; + return overridden; +} + +Constraint.prototype.destroyConstraint = function () { + if (this.isSatisfied()) planner.incrementalRemove(this); + else this.removeFromGraph(); +} + +/** + * Normal constraints are not input constraints. An input constraint + * is one that depends on external state, such as the mouse, the + * keybord, a clock, or some arbitraty piece of imperative code. + */ +Constraint.prototype.isInput = function () { + return false; +} + +/* --- * + * U n a r y C o n s t r a i n t + * --- */ + +/** + * Abstract superclass for constraints having a single possible output + * variable. + */ +function UnaryConstraint(v, strength) { + UnaryConstraint.superConstructor.call(this, strength); + this.myOutput = v; + this.satisfied = false; + this.addConstraint(); +} + +UnaryConstraint.inheritsFrom(Constraint); + +/** + * Adds this constraint to the constraint graph + */ +UnaryConstraint.prototype.addToGraph = function () { + this.myOutput.addConstraint(this); + this.satisfied = false; +} + +/** + * Decides if this constraint can be satisfied and records that + * decision. + */ +UnaryConstraint.prototype.chooseMethod = function (mark) { + this.satisfied = (this.myOutput.mark != mark) + && Strength.stronger(this.strength, this.myOutput.walkStrength); +} + +/** + * Returns true if this constraint is satisfied in the current solution. + */ +UnaryConstraint.prototype.isSatisfied = function () { + return this.satisfied; +} + +UnaryConstraint.prototype.markInputs = function (mark) { + // has no inputs +} + +/** + * Returns the current output variable. + */ +UnaryConstraint.prototype.output = function () { + return this.myOutput; +} + +/** + * Calculate the walkabout strength, the stay flag, and, if it is + * 'stay', the value for the current output of this constraint. Assume + * this constraint is satisfied. + */ +UnaryConstraint.prototype.recalculate = function () { + this.myOutput.walkStrength = this.strength; + this.myOutput.stay = !this.isInput(); + if (this.myOutput.stay) this.execute(); // Stay optimization +} + +/** + * Records that this constraint is unsatisfied + */ +UnaryConstraint.prototype.markUnsatisfied = function () { + this.satisfied = false; +} + +UnaryConstraint.prototype.inputsKnown = function () { + return true; +} + +UnaryConstraint.prototype.removeFromGraph = function () { + if (this.myOutput != null) this.myOutput.removeConstraint(this); + this.satisfied = false; +} + +/* --- * + * S t a y C o n s t r a i n t + * --- */ + +/** + * Variables that should, with some level of preference, stay the same. + * Planners may exploit the fact that instances, if satisfied, will not + * change their output during plan execution. This is called "stay + * optimization". + */ +function StayConstraint(v, str) { + StayConstraint.superConstructor.call(this, v, str); +} + +StayConstraint.inheritsFrom(UnaryConstraint); + +StayConstraint.prototype.execute = function () { + // Stay constraints do nothing +} + +/* --- * + * E d i t C o n s t r a i n t + * --- */ + +/** + * A unary input constraint used to mark a variable that the client + * wishes to change. + */ +function EditConstraint(v, str) { + EditConstraint.superConstructor.call(this, v, str); +} + +EditConstraint.inheritsFrom(UnaryConstraint); + +/** + * Edits indicate that a variable is to be changed by imperative code. + */ +EditConstraint.prototype.isInput = function () { + return true; +} + +EditConstraint.prototype.execute = function () { + // Edit constraints do nothing +} + +/* --- * + * B i n a r y C o n s t r a i n t + * --- */ + +var Direction = new Object(); +Direction.NONE = 0; +Direction.FORWARD = 1; +Direction.BACKWARD = -1; + +/** + * Abstract superclass for constraints having two possible output + * variables. + */ +function BinaryConstraint(var1, var2, strength) { + BinaryConstraint.superConstructor.call(this, strength); + this.v1 = var1; + this.v2 = var2; + this.direction = Direction.NONE; + this.addConstraint(); +} + +BinaryConstraint.inheritsFrom(Constraint); + +/** + * Decides if this constraint can be satisfied and which way it + * should flow based on the relative strength of the variables related, + * and record that decision. + */ +BinaryConstraint.prototype.chooseMethod = function (mark) { + if (this.v1.mark == mark) { + this.direction = (this.v2.mark != mark && Strength.stronger(this.strength, this.v2.walkStrength)) + ? Direction.FORWARD + : Direction.NONE; + } + if (this.v2.mark == mark) { + this.direction = (this.v1.mark != mark && Strength.stronger(this.strength, this.v1.walkStrength)) + ? Direction.BACKWARD + : Direction.NONE; + } + if (Strength.weaker(this.v1.walkStrength, this.v2.walkStrength)) { + this.direction = Strength.stronger(this.strength, this.v1.walkStrength) + ? Direction.BACKWARD + : Direction.NONE; + } else { + this.direction = Strength.stronger(this.strength, this.v2.walkStrength) + ? Direction.FORWARD + : Direction.BACKWARD + } +} + +/** + * Add this constraint to the constraint graph + */ +BinaryConstraint.prototype.addToGraph = function () { + this.v1.addConstraint(this); + this.v2.addConstraint(this); + this.direction = Direction.NONE; +} + +/** + * Answer true if this constraint is satisfied in the current solution. + */ +BinaryConstraint.prototype.isSatisfied = function () { + return this.direction != Direction.NONE; +} + +/** + * Mark the input variable with the given mark. + */ +BinaryConstraint.prototype.markInputs = function (mark) { + this.input().mark = mark; +} + +/** + * Returns the current input variable + */ +BinaryConstraint.prototype.input = function () { + return (this.direction == Direction.FORWARD) ? this.v1 : this.v2; +} + +/** + * Returns the current output variable + */ +BinaryConstraint.prototype.output = function () { + return (this.direction == Direction.FORWARD) ? this.v2 : this.v1; +} + +/** + * Calculate the walkabout strength, the stay flag, and, if it is + * 'stay', the value for the current output of this + * constraint. Assume this constraint is satisfied. + */ +BinaryConstraint.prototype.recalculate = function () { + var ihn = this.input(), out = this.output(); + out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength); + out.stay = ihn.stay; + if (out.stay) this.execute(); +} + +/** + * Record the fact that this constraint is unsatisfied. + */ +BinaryConstraint.prototype.markUnsatisfied = function () { + this.direction = Direction.NONE; +} + +BinaryConstraint.prototype.inputsKnown = function (mark) { + var i = this.input(); + return i.mark == mark || i.stay || i.determinedBy == null; +} + +BinaryConstraint.prototype.removeFromGraph = function () { + if (this.v1 != null) this.v1.removeConstraint(this); + if (this.v2 != null) this.v2.removeConstraint(this); + this.direction = Direction.NONE; +} + +/* --- * + * S c a l e C o n s t r a i n t + * --- */ + +/** + * Relates two variables by the linear scaling relationship: "v2 = + * (v1 * scale) + offset". Either v1 or v2 may be changed to maintain + * this relationship but the scale factor and offset are considered + * read-only. + */ +function ScaleConstraint(src, scale, offset, dest, strength) { + this.direction = Direction.NONE; + this.scale = scale; + this.offset = offset; + ScaleConstraint.superConstructor.call(this, src, dest, strength); +} + +ScaleConstraint.inheritsFrom(BinaryConstraint); + +/** + * Adds this constraint to the constraint graph. + */ +ScaleConstraint.prototype.addToGraph = function () { + ScaleConstraint.superConstructor.prototype.addToGraph.call(this); + this.scale.addConstraint(this); + this.offset.addConstraint(this); +} + +ScaleConstraint.prototype.removeFromGraph = function () { + ScaleConstraint.superConstructor.prototype.removeFromGraph.call(this); + if (this.scale != null) this.scale.removeConstraint(this); + if (this.offset != null) this.offset.removeConstraint(this); +} + +ScaleConstraint.prototype.markInputs = function (mark) { + ScaleConstraint.superConstructor.prototype.markInputs.call(this, mark); + this.scale.mark = this.offset.mark = mark; +} + +/** + * Enforce this constraint. Assume that it is satisfied. + */ +ScaleConstraint.prototype.execute = function () { + if (this.direction == Direction.FORWARD) { + this.v2.value = this.v1.value * this.scale.value + this.offset.value; + } else { + this.v1.value = (this.v2.value - this.offset.value) / this.scale.value; + } +} + +/** + * Calculate the walkabout strength, the stay flag, and, if it is + * 'stay', the value for the current output of this constraint. Assume + * this constraint is satisfied. + */ +ScaleConstraint.prototype.recalculate = function () { + var ihn = this.input(), out = this.output(); + out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength); + out.stay = ihn.stay && this.scale.stay && this.offset.stay; + if (out.stay) this.execute(); +} + +/* --- * + * E q u a l i t y C o n s t r a i n t + * --- */ + +/** + * Constrains two variables to have the same value. + */ +function EqualityConstraint(var1, var2, strength) { + EqualityConstraint.superConstructor.call(this, var1, var2, strength); +} + +EqualityConstraint.inheritsFrom(BinaryConstraint); + +/** + * Enforce this constraint. Assume that it is satisfied. + */ +EqualityConstraint.prototype.execute = function () { + this.output().value = this.input().value; +} + +/* --- * + * V a r i a b l e + * --- */ + +/** + * A constrained variable. In addition to its value, it maintain the + * structure of the constraint graph, the current dataflow graph, and + * various parameters of interest to the DeltaBlue incremental + * constraint solver. + **/ +function Variable(name, initialValue) { + this.value = initialValue || 0; + this.constraints = new OrderedCollection(); + this.determinedBy = null; + this.mark = 0; + this.walkStrength = Strength.WEAKEST; + this.stay = true; + this.name = name; +} + +/** + * Add the given constraint to the set of all constraints that refer + * this variable. + */ +Variable.prototype.addConstraint = function (c) { + this.constraints.add(c); +} + +/** + * Removes all traces of c from this variable. + */ +Variable.prototype.removeConstraint = function (c) { + this.constraints.remove(c); + if (this.determinedBy == c) this.determinedBy = null; +} + +/* --- * + * P l a n n e r + * --- */ + +/** + * The DeltaBlue planner + */ +function Planner() { + this.currentMark = 0; +} + +/** + * Attempt to satisfy the given constraint and, if successful, + * incrementally update the dataflow graph. Details: If satifying + * the constraint is successful, it may override a weaker constraint + * on its output. The algorithm attempts to resatisfy that + * constraint using some other method. This process is repeated + * until either a) it reaches a variable that was not previously + * determined by any constraint or b) it reaches a constraint that + * is too weak to be satisfied using any of its methods. The + * variables of constraints that have been processed are marked with + * a unique mark value so that we know where we've been. This allows + * the algorithm to avoid getting into an infinite loop even if the + * constraint graph has an inadvertent cycle. + */ +Planner.prototype.incrementalAdd = function (c) { + var mark = this.newMark(); + var overridden = c.satisfy(mark); + while (overridden != null) + overridden = overridden.satisfy(mark); +} + +/** + * Entry point for retracting a constraint. Remove the given + * constraint and incrementally update the dataflow graph. + * Details: Retracting the given constraint may allow some currently + * unsatisfiable downstream constraint to be satisfied. We therefore collect + * a list of unsatisfied downstream constraints and attempt to + * satisfy each one in turn. This list is traversed by constraint + * strength, strongest first, as a heuristic for avoiding + * unnecessarily adding and then overriding weak constraints. + * Assume: c is satisfied. + */ +Planner.prototype.incrementalRemove = function (c) { + var out = c.output(); + c.markUnsatisfied(); + c.removeFromGraph(); + var unsatisfied = this.removePropagateFrom(out); + var strength = Strength.REQUIRED; + do { + for (var i = 0; i < unsatisfied.size(); i++) { + var u = unsatisfied.at(i); + if (u.strength == strength) + this.incrementalAdd(u); + } + strength = strength.nextWeaker(); + } while (strength != Strength.WEAKEST); +} + +/** + * Select a previously unused mark value. + */ +Planner.prototype.newMark = function () { + return ++this.currentMark; +} + +/** + * Extract a plan for resatisfaction starting from the given source + * constraints, usually a set of input constraints. This method + * assumes that stay optimization is desired; the plan will contain + * only constraints whose output variables are not stay. Constraints + * that do no computation, such as stay and edit constraints, are + * not included in the plan. + * Details: The outputs of a constraint are marked when it is added + * to the plan under construction. A constraint may be appended to + * the plan when all its input variables are known. A variable is + * known if either a) the variable is marked (indicating that has + * been computed by a constraint appearing earlier in the plan), b) + * the variable is 'stay' (i.e. it is a constant at plan execution + * time), or c) the variable is not determined by any + * constraint. The last provision is for past states of history + * variables, which are not stay but which are also not computed by + * any constraint. + * Assume: sources are all satisfied. + */ +Planner.prototype.makePlan = function (sources) { + var mark = this.newMark(); + var plan = new Plan(); + var todo = sources; + while (todo.size() > 0) { + var c = todo.removeFirst(); + if (c.output().mark != mark && c.inputsKnown(mark)) { + plan.addConstraint(c); + c.output().mark = mark; + this.addConstraintsConsumingTo(c.output(), todo); + } + } + return plan; +} + +/** + * Extract a plan for resatisfying starting from the output of the + * given constraints, usually a set of input constraints. + */ +Planner.prototype.extractPlanFromConstraints = function (constraints) { + var sources = new OrderedCollection(); + for (var i = 0; i < constraints.size(); i++) { + var c = constraints.at(i); + if (c.isInput() && c.isSatisfied()) + // not in plan already and eligible for inclusion + sources.add(c); + } + return this.makePlan(sources); +} + +/** + * Recompute the walkabout strengths and stay flags of all variables + * downstream of the given constraint and recompute the actual + * values of all variables whose stay flag is true. If a cycle is + * detected, remove the given constraint and answer + * false. Otherwise, answer true. + * Details: Cycles are detected when a marked variable is + * encountered downstream of the given constraint. The sender is + * assumed to have marked the inputs of the given constraint with + * the given mark. Thus, encountering a marked node downstream of + * the output constraint means that there is a path from the + * constraint's output to one of its inputs. + */ +Planner.prototype.addPropagate = function (c, mark) { + var todo = new OrderedCollection(); + todo.add(c); + while (todo.size() > 0) { + var d = todo.removeFirst(); + if (d.output().mark == mark) { + this.incrementalRemove(c); + return false; + } + d.recalculate(); + this.addConstraintsConsumingTo(d.output(), todo); + } + return true; +} + + +/** + * Update the walkabout strengths and stay flags of all variables + * downstream of the given constraint. Answer a collection of + * unsatisfied constraints sorted in order of decreasing strength. + */ +Planner.prototype.removePropagateFrom = function (out) { + out.determinedBy = null; + out.walkStrength = Strength.WEAKEST; + out.stay = true; + var unsatisfied = new OrderedCollection(); + var todo = new OrderedCollection(); + todo.add(out); + while (todo.size() > 0) { + var v = todo.removeFirst(); + for (var i = 0; i < v.constraints.size(); i++) { + var c = v.constraints.at(i); + if (!c.isSatisfied()) + unsatisfied.add(c); + } + var determining = v.determinedBy; + for (var i = 0; i < v.constraints.size(); i++) { + var next = v.constraints.at(i); + if (next != determining && next.isSatisfied()) { + next.recalculate(); + todo.add(next.output()); + } + } + } + return unsatisfied; +} + +Planner.prototype.addConstraintsConsumingTo = function (v, coll) { + var determining = v.determinedBy; + var cc = v.constraints; + for (var i = 0; i < cc.size(); i++) { + var c = cc.at(i); + if (c != determining && c.isSatisfied()) + coll.add(c); + } +} + +/* --- * + * P l a n + * --- */ + +/** + * A Plan is an ordered list of constraints to be executed in sequence + * to resatisfy all currently satisfiable constraints in the face of + * one or more changing inputs. + */ +function Plan() { + this.v = new OrderedCollection(); +} + +Plan.prototype.addConstraint = function (c) { + this.v.add(c); +} + +Plan.prototype.size = function () { + return this.v.size(); +} + +Plan.prototype.constraintAt = function (index) { + return this.v.at(index); +} + +Plan.prototype.execute = function () { + for (var i = 0; i < this.size(); i++) { + var c = this.constraintAt(i); + c.execute(); + } +} + +/* --- * + * M a i n + * --- */ + +/** + * This is the standard DeltaBlue benchmark. A long chain of equality + * constraints is constructed with a stay constraint on one end. An + * edit constraint is then added to the opposite end and the time is + * measured for adding and removing this constraint, and extracting + * and executing a constraint satisfaction plan. There are two cases. + * In case 1, the added constraint is stronger than the stay + * constraint and values must propagate down the entire length of the + * chain. In case 2, the added constraint is weaker than the stay + * constraint so it cannot be accomodated. The cost in this case is, + * of course, very low. Typical situations lie somewhere between these + * two extremes. + */ +function chainTest(n) { + planner = new Planner(); + var prev = null, first = null, last = null; + + // Build chain of n equality constraints + for (var i = 0; i <= n; i++) { + var name = "v" + i; + var v = new Variable(name); + if (prev != null) + new EqualityConstraint(prev, v, Strength.REQUIRED); + if (i == 0) first = v; + if (i == n) last = v; + prev = v; + } + + new StayConstraint(last, Strength.STRONG_DEFAULT); + var edit = new EditConstraint(first, Strength.PREFERRED); + var edits = new OrderedCollection(); + edits.add(edit); + var plan = planner.extractPlanFromConstraints(edits); + for (var i = 0; i < 100; i++) { + first.value = i; + plan.execute(); + if (last.value != i) + alert("Chain test failed."); + } +} + +/** + * This test constructs a two sets of variables related to each + * other by a simple linear transformation (scale and offset). The + * time is measured to change a variable on either side of the + * mapping and to change the scale and offset factors. + */ +function projectionTest(n) { + planner = new Planner(); + var scale = new Variable("scale", 10); + var offset = new Variable("offset", 1000); + var src = null, dst = null; + + var dests = new OrderedCollection(); + for (var i = 0; i < n; i++) { + src = new Variable("src" + i, i); + dst = new Variable("dst" + i, i); + dests.add(dst); + new StayConstraint(src, Strength.NORMAL); + new ScaleConstraint(src, scale, offset, dst, Strength.REQUIRED); + } + + change(src, 17); + if (dst.value != 1170) alert("Projection 1 failed"); + change(dst, 1050); + if (src.value != 5) alert("Projection 2 failed"); + change(scale, 5); + for (var i = 0; i < n - 1; i++) { + if (dests.at(i).value != i * 5 + 1000) + alert("Projection 3 failed"); + } + change(offset, 2000); + for (var i = 0; i < n - 1; i++) { + if (dests.at(i).value != i * 5 + 2000) + alert("Projection 4 failed"); + } +} + +function change(v, newValue) { + var edit = new EditConstraint(v, Strength.PREFERRED); + var edits = new OrderedCollection(); + edits.add(edit); + var plan = planner.extractPlanFromConstraints(edits); + for (var i = 0; i < 10; i++) { + v.value = newValue; + plan.execute(); + } + edit.destroyConstraint(); +} + +// Global variable holding the current planner. +var planner = null; + +function deltaBlue() { + chainTest(100); + projectionTest(100); +} +/* + * Copyright (c) 2003-2005 Tom Wu + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * In addition, the following condition applies: + * + * All redistributions must retain an intact copy of this copyright notice + * and disclaimer. + */ + + +// The code has been adapted for use as a benchmark by Google. +var Crypto = new BenchmarkSuite('Crypto', 266181, [ + new Benchmark("Encrypt", encrypt), + new Benchmark("Decrypt", decrypt) +]); + + +// Basic JavaScript BN library - subset useful for RSA encryption. + +// Bits per digit +var dbits; +var BI_DB; +var BI_DM; +var BI_DV; + +var BI_FP; +var BI_FV; +var BI_F1; +var BI_F2; + +// JavaScript engine analysis +var canary = 0xdeadbeefcafe; +var j_lm = ((canary&0xffffff)==0xefcafe); + +// (public) Constructor +function BigInteger(a,b,c) { + this.array = new Array(); + if(a != null) + if("number" == typeof a) this.fromNumber(a,b,c); + else if(b == null && "string" != typeof a) this.fromString(a,256); + else this.fromString(a,b); +} + +// return new, unset BigInteger +function nbi() { return new BigInteger(null); } + +// am: Compute w_j += (x*this_i), propagate carries, +// c is initial carry, returns final carry. +// c < 3*dvalue, x < 2*dvalue, this_i < dvalue +// We need to select the fastest one that works in this environment. + +// am1: use a single mult and divide to get the high bits, +// max digit bits should be 26 because +// max internal value = 2*dvalue^2-2*dvalue (< 2^53) +function am1(i,x,w,j,c,n) { + var this_array = this.array; + var w_array = w.array; + while(--n >= 0) { + var v = x*this_array[i++]+w_array[j]+c; + c = Math.floor(v/0x4000000); + w_array[j++] = v&0x3ffffff; + } + return c; +} + +// am2 avoids a big mult-and-extract completely. +// Max digit bits should be <= 30 because we do bitwise ops +// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) +function am2(i,x,w,j,c,n) { + var this_array = this.array; + var w_array = w.array; + var xl = x&0x7fff, xh = x>>15; + while(--n >= 0) { + var l = this_array[i]&0x7fff; + var h = this_array[i++]>>15; + var m = xh*l+h*xl; + l = xl*l+((m&0x7fff)<<15)+w_array[j]+(c&0x3fffffff); + c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); + w_array[j++] = l&0x3fffffff; + } + return c; +} + +// Alternately, set max digit bits to 28 since some +// browsers slow down when dealing with 32-bit numbers. +function am3(i,x,w,j,c,n) { + var this_array = this.array; + var w_array = w.array; + + var xl = x&0x3fff, xh = x>>14; + while(--n >= 0) { + var l = this_array[i]&0x3fff; + var h = this_array[i++]>>14; + var m = xh*l+h*xl; + l = xl*l+((m&0x3fff)<<14)+w_array[j]+c; + c = (l>>28)+(m>>14)+xh*h; + w_array[j++] = l&0xfffffff; + } + return c; +} + +// This is tailored to VMs with 2-bit tagging. It makes sure +// that all the computations stay within the 29 bits available. +function am4(i,x,w,j,c,n) { + var this_array = this.array; + var w_array = w.array; + + var xl = x&0x1fff, xh = x>>13; + while(--n >= 0) { + var l = this_array[i]&0x1fff; + var h = this_array[i++]>>13; + var m = xh*l+h*xl; + l = xl*l+((m&0x1fff)<<13)+w_array[j]+c; + c = (l>>26)+(m>>13)+xh*h; + w_array[j++] = l&0x3ffffff; + } + return c; +} + +// am3/28 is best for SM, Rhino, but am4/26 is best for v8. +// Kestrel (Opera 9.5) gets its best result with am4/26. +// IE7 does 9% better with am3/28 than with am4/26. +// Firefox (SM) gets 10% faster with am3/28 than with am4/26. + +setupEngine = function(fn, bits) { + BigInteger.prototype.am = fn; + dbits = bits; + + BI_DB = dbits; + BI_DM = ((1<= 0; --i) r_array[i] = this_array[i]; + r.t = this.t; + r.s = this.s; +} + +// (protected) set from integer value x, -DV <= x < DV +function bnpFromInt(x) { + var this_array = this.array; + this.t = 1; + this.s = (x<0)?-1:0; + if(x > 0) this_array[0] = x; + else if(x < -1) this_array[0] = x+DV; + else this.t = 0; +} + +// return bigint initialized to value +function nbv(i) { var r = nbi(); r.fromInt(i); return r; } + +// (protected) set from string and radix +function bnpFromString(s,b) { + var this_array = this.array; + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 256) k = 8; // byte array + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else { this.fromRadix(s,b); return; } + this.t = 0; + this.s = 0; + var i = s.length, mi = false, sh = 0; + while(--i >= 0) { + var x = (k==8)?s[i]&0xff:intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-") mi = true; + continue; + } + mi = false; + if(sh == 0) + this_array[this.t++] = x; + else if(sh+k > BI_DB) { + this_array[this.t-1] |= (x&((1<<(BI_DB-sh))-1))<>(BI_DB-sh)); + } + else + this_array[this.t-1] |= x<= BI_DB) sh -= BI_DB; + } + if(k == 8 && (s[0]&0x80) != 0) { + this.s = -1; + if(sh > 0) this_array[this.t-1] |= ((1<<(BI_DB-sh))-1)< 0 && this_array[this.t-1] == c) --this.t; +} + +// (public) return string representation in given radix +function bnToString(b) { + var this_array = this.array; + if(this.s < 0) return "-"+this.negate().toString(b); + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else return this.toRadix(b); + var km = (1< 0) { + if(p < BI_DB && (d = this_array[i]>>p) > 0) { m = true; r = int2char(d); } + while(i >= 0) { + if(p < k) { + d = (this_array[i]&((1<>(p+=BI_DB-k); + } + else { + d = (this_array[i]>>(p-=k))&km; + if(p <= 0) { p += BI_DB; --i; } + } + if(d > 0) m = true; + if(m) r += int2char(d); + } + } + return m?r:"0"; +} + +// (public) -this +function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } + +// (public) |this| +function bnAbs() { return (this.s<0)?this.negate():this; } + +// (public) return + if this > a, - if this < a, 0 if equal +function bnCompareTo(a) { + var this_array = this.array; + var a_array = a.array; + + var r = this.s-a.s; + if(r != 0) return r; + var i = this.t; + r = i-a.t; + if(r != 0) return r; + while(--i >= 0) if((r=this_array[i]-a_array[i]) != 0) return r; + return 0; +} + +// returns bit length of the integer x +function nbits(x) { + var r = 1, t; + if((t=x>>>16) != 0) { x = t; r += 16; } + if((t=x>>8) != 0) { x = t; r += 8; } + if((t=x>>4) != 0) { x = t; r += 4; } + if((t=x>>2) != 0) { x = t; r += 2; } + if((t=x>>1) != 0) { x = t; r += 1; } + return r; +} + +// (public) return the number of bits in "this" +function bnBitLength() { + var this_array = this.array; + if(this.t <= 0) return 0; + return BI_DB*(this.t-1)+nbits(this_array[this.t-1]^(this.s&BI_DM)); +} + +// (protected) r = this << n*DB +function bnpDLShiftTo(n,r) { + var this_array = this.array; + var r_array = r.array; + var i; + for(i = this.t-1; i >= 0; --i) r_array[i+n] = this_array[i]; + for(i = n-1; i >= 0; --i) r_array[i] = 0; + r.t = this.t+n; + r.s = this.s; +} + +// (protected) r = this >> n*DB +function bnpDRShiftTo(n,r) { + var this_array = this.array; + var r_array = r.array; + for(var i = n; i < this.t; ++i) r_array[i-n] = this_array[i]; + r.t = Math.max(this.t-n,0); + r.s = this.s; +} + +// (protected) r = this << n +function bnpLShiftTo(n,r) { + var this_array = this.array; + var r_array = r.array; + var bs = n%BI_DB; + var cbs = BI_DB-bs; + var bm = (1<= 0; --i) { + r_array[i+ds+1] = (this_array[i]>>cbs)|c; + c = (this_array[i]&bm)<= 0; --i) r_array[i] = 0; + r_array[ds] = c; + r.t = this.t+ds+1; + r.s = this.s; + r.clamp(); +} + +// (protected) r = this >> n +function bnpRShiftTo(n,r) { + var this_array = this.array; + var r_array = r.array; + r.s = this.s; + var ds = Math.floor(n/BI_DB); + if(ds >= this.t) { r.t = 0; return; } + var bs = n%BI_DB; + var cbs = BI_DB-bs; + var bm = (1<>bs; + for(var i = ds+1; i < this.t; ++i) { + r_array[i-ds-1] |= (this_array[i]&bm)<>bs; + } + if(bs > 0) r_array[this.t-ds-1] |= (this.s&bm)<>= BI_DB; + } + if(a.t < this.t) { + c -= a.s; + while(i < this.t) { + c += this_array[i]; + r_array[i++] = c&BI_DM; + c >>= BI_DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c -= a_array[i]; + r_array[i++] = c&BI_DM; + c >>= BI_DB; + } + c -= a.s; + } + r.s = (c<0)?-1:0; + if(c < -1) r_array[i++] = BI_DV+c; + else if(c > 0) r_array[i++] = c; + r.t = i; + r.clamp(); +} + +// (protected) r = this * a, r != this,a (HAC 14.12) +// "this" should be the larger one if appropriate. +function bnpMultiplyTo(a,r) { + var this_array = this.array; + var r_array = r.array; + var x = this.abs(), y = a.abs(); + var y_array = y.array; + + var i = x.t; + r.t = i+y.t; + while(--i >= 0) r_array[i] = 0; + for(i = 0; i < y.t; ++i) r_array[i+x.t] = x.am(0,y_array[i],r,i,0,x.t); + r.s = 0; + r.clamp(); + if(this.s != a.s) BigInteger.ZERO.subTo(r,r); +} + +// (protected) r = this^2, r != this (HAC 14.16) +function bnpSquareTo(r) { + var x = this.abs(); + var x_array = x.array; + var r_array = r.array; + + var i = r.t = 2*x.t; + while(--i >= 0) r_array[i] = 0; + for(i = 0; i < x.t-1; ++i) { + var c = x.am(i,x_array[i],r,2*i,0,1); + if((r_array[i+x.t]+=x.am(i+1,2*x_array[i],r,2*i+1,c,x.t-i-1)) >= BI_DV) { + r_array[i+x.t] -= BI_DV; + r_array[i+x.t+1] = 1; + } + } + if(r.t > 0) r_array[r.t-1] += x.am(i,x_array[i],r,2*i,0,1); + r.s = 0; + r.clamp(); +} + +// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) +// r != q, this != m. q or r may be null. +function bnpDivRemTo(m,q,r) { + var pm = m.abs(); + if(pm.t <= 0) return; + var pt = this.abs(); + if(pt.t < pm.t) { + if(q != null) q.fromInt(0); + if(r != null) this.copyTo(r); + return; + } + if(r == null) r = nbi(); + var y = nbi(), ts = this.s, ms = m.s; + var pm_array = pm.array; + var nsh = BI_DB-nbits(pm_array[pm.t-1]); // normalize modulus + if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } + else { pm.copyTo(y); pt.copyTo(r); } + var ys = y.t; + + var y_array = y.array; + var y0 = y_array[ys-1]; + if(y0 == 0) return; + var yt = y0*(1<1)?y_array[ys-2]>>BI_F2:0); + var d1 = BI_FV/yt, d2 = (1<= 0) { + r_array[r.t++] = 1; + r.subTo(t,r); + } + BigInteger.ONE.dlShiftTo(ys,t); + t.subTo(y,y); // "negative" y so we can replace sub with am later + while(y.t < ys) y_array[y.t++] = 0; + while(--j >= 0) { + // Estimate quotient digit + var qd = (r_array[--i]==y0)?BI_DM:Math.floor(r_array[i]*d1+(r_array[i-1]+e)*d2); + if((r_array[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out + y.dlShiftTo(j,t); + r.subTo(t,r); + while(r_array[i] < --qd) r.subTo(t,r); + } + } + if(q != null) { + r.drShiftTo(ys,q); + if(ts != ms) BigInteger.ZERO.subTo(q,q); + } + r.t = ys; + r.clamp(); + if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder + if(ts < 0) BigInteger.ZERO.subTo(r,r); +} + +// (public) this mod a +function bnMod(a) { + var r = nbi(); + this.abs().divRemTo(a,null,r); + if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); + return r; +} + +// Modular reduction using "classic" algorithm +function Classic(m) { this.m = m; } +function cConvert(x) { + if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; +} +function cRevert(x) { return x; } +function cReduce(x) { x.divRemTo(this.m,null,x); } +function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } +function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +Classic.prototype.convert = cConvert; +Classic.prototype.revert = cRevert; +Classic.prototype.reduce = cReduce; +Classic.prototype.mulTo = cMulTo; +Classic.prototype.sqrTo = cSqrTo; + +// (protected) return "-1/this % 2^DB"; useful for Mont. reduction +// justification: +// xy == 1 (mod m) +// xy = 1+km +// xy(2-xy) = (1+km)(1-km) +// x[y(2-xy)] = 1-k^2m^2 +// x[y(2-xy)] == 1 (mod m^2) +// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 +// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. +// JS multiply "overflows" differently from C/C++, so care is needed here. +function bnpInvDigit() { + var this_array = this.array; + if(this.t < 1) return 0; + var x = this_array[0]; + if((x&1) == 0) return 0; + var y = x&3; // y == 1/x mod 2^2 + y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 + y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 + y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y*(2-x*y%BI_DV))%BI_DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y>0)?BI_DV-y:-y; +} + +// Montgomery reduction +function Montgomery(m) { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp&0x7fff; + this.mph = this.mp>>15; + this.um = (1<<(BI_DB-15))-1; + this.mt2 = 2*m.t; +} + +// xR mod m +function montConvert(x) { + var r = nbi(); + x.abs().dlShiftTo(this.m.t,r); + r.divRemTo(this.m,null,r); + if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); + return r; +} + +// x/R mod m +function montRevert(x) { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; +} + +// x = x/R mod m (HAC 14.32) +function montReduce(x) { + var x_array = x.array; + while(x.t <= this.mt2) // pad x so am has enough room later + x_array[x.t++] = 0; + for(var i = 0; i < this.m.t; ++i) { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x_array[i]&0x7fff; + var u0 = (j*this.mpl+(((j*this.mph+(x_array[i]>>15)*this.mpl)&this.um)<<15))&BI_DM; + // use am to combine the multiply-shift-add into one call + j = i+this.m.t; + x_array[j] += this.m.am(0,u0,x,i,0,this.m.t); + // propagate carry + while(x_array[j] >= BI_DV) { x_array[j] -= BI_DV; x_array[++j]++; } + } + x.clamp(); + x.drShiftTo(this.m.t,x); + if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = "x^2/R mod m"; x != r +function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = "xy/R mod m"; x,y != r +function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Montgomery.prototype.convert = montConvert; +Montgomery.prototype.revert = montRevert; +Montgomery.prototype.reduce = montReduce; +Montgomery.prototype.mulTo = montMulTo; +Montgomery.prototype.sqrTo = montSqrTo; + +// (protected) true iff this is even +function bnpIsEven() { + var this_array = this.array; + return ((this.t>0)?(this_array[0]&1):this.s) == 0; +} + +// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) +function bnpExp(e,z) { + if(e > 0xffffffff || e < 1) return BigInteger.ONE; + var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; + g.copyTo(r); + while(--i >= 0) { + z.sqrTo(r,r2); + if((e&(1< 0) z.mulTo(r2,g,r); + else { var t = r; r = r2; r2 = t; } + } + return z.revert(r); +} + +// (public) this^e % m, 0 <= e < 2^32 +function bnModPowInt(e,m) { + var z; + if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); + return this.exp(e,z); +} + +// protected +BigInteger.prototype.copyTo = bnpCopyTo; +BigInteger.prototype.fromInt = bnpFromInt; +BigInteger.prototype.fromString = bnpFromString; +BigInteger.prototype.clamp = bnpClamp; +BigInteger.prototype.dlShiftTo = bnpDLShiftTo; +BigInteger.prototype.drShiftTo = bnpDRShiftTo; +BigInteger.prototype.lShiftTo = bnpLShiftTo; +BigInteger.prototype.rShiftTo = bnpRShiftTo; +BigInteger.prototype.subTo = bnpSubTo; +BigInteger.prototype.multiplyTo = bnpMultiplyTo; +BigInteger.prototype.squareTo = bnpSquareTo; +BigInteger.prototype.divRemTo = bnpDivRemTo; +BigInteger.prototype.invDigit = bnpInvDigit; +BigInteger.prototype.isEven = bnpIsEven; +BigInteger.prototype.exp = bnpExp; + +// public +BigInteger.prototype.toString = bnToString; +BigInteger.prototype.negate = bnNegate; +BigInteger.prototype.abs = bnAbs; +BigInteger.prototype.compareTo = bnCompareTo; +BigInteger.prototype.bitLength = bnBitLength; +BigInteger.prototype.mod = bnMod; +BigInteger.prototype.modPowInt = bnModPowInt; + +// "constants" +BigInteger.ZERO = nbv(0); +BigInteger.ONE = nbv(1); +// Copyright (c) 2005 Tom Wu +// All Rights Reserved. +// See "LICENSE" for details. + +// Extended JavaScript BN functions, required for RSA private ops. + +// (public) +function bnClone() { var r = nbi(); this.copyTo(r); return r; } + +// (public) return value as integer +function bnIntValue() { + var this_array = this.array; + if(this.s < 0) { + if(this.t == 1) return this_array[0]-BI_DV; + else if(this.t == 0) return -1; + } + else if(this.t == 1) return this_array[0]; + else if(this.t == 0) return 0; + // assumes 16 < DB < 32 + return ((this_array[1]&((1<<(32-BI_DB))-1))<>24; +} + +// (public) return value as short (assumes DB>=16) +function bnShortValue() { + var this_array = this.array; + return (this.t==0)?this.s:(this_array[0]<<16)>>16; +} + +// (protected) return x s.t. r^x < DV +function bnpChunkSize(r) { return Math.floor(Math.LN2*BI_DB/Math.log(r)); } + +// (public) 0 if this == 0, 1 if this > 0 +function bnSigNum() { + var this_array = this.array; + if(this.s < 0) return -1; + else if(this.t <= 0 || (this.t == 1 && this_array[0] <= 0)) return 0; + else return 1; +} + +// (protected) convert to radix string +function bnpToRadix(b) { + if(b == null) b = 10; + if(this.signum() == 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b,cs); + var d = nbv(a), y = nbi(), z = nbi(), r = ""; + this.divRemTo(d,y,z); + while(y.signum() > 0) { + r = (a+z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d,y,z); + } + return z.intValue().toString(b) + r; +} + +// (protected) convert from radix string +function bnpFromRadix(s,b) { + this.fromInt(0); + if(b == null) b = 10; + var cs = this.chunkSize(b); + var d = Math.pow(b,cs), mi = false, j = 0, w = 0; + for(var i = 0; i < s.length; ++i) { + var x = intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-" && this.signum() == 0) mi = true; + continue; + } + w = b*w+x; + if(++j >= cs) { + this.dMultiply(d); + this.dAddOffset(w,0); + j = 0; + w = 0; + } + } + if(j > 0) { + this.dMultiply(Math.pow(b,j)); + this.dAddOffset(w,0); + } + if(mi) BigInteger.ZERO.subTo(this,this); +} + +// (protected) alternate constructor +function bnpFromNumber(a,b,c) { + if("number" == typeof b) { + // new BigInteger(int,int,RNG) + if(a < 2) this.fromInt(1); + else { + this.fromNumber(a,c); + if(!this.testBit(a-1)) // force MSB set + this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); + if(this.isEven()) this.dAddOffset(1,0); // force odd + while(!this.isProbablePrime(b)) { + this.dAddOffset(2,0); + if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); + } + } + } + else { + // new BigInteger(int,RNG) + var x = new Array(), t = a&7; + x.length = (a>>3)+1; + b.nextBytes(x); + if(t > 0) x[0] &= ((1< 0) { + if(p < BI_DB && (d = this_array[i]>>p) != (this.s&BI_DM)>>p) + r[k++] = d|(this.s<<(BI_DB-p)); + while(i >= 0) { + if(p < 8) { + d = (this_array[i]&((1<>(p+=BI_DB-8); + } + else { + d = (this_array[i]>>(p-=8))&0xff; + if(p <= 0) { p += BI_DB; --i; } + } + if((d&0x80) != 0) d |= -256; + if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; + if(k > 0 || d != this.s) r[k++] = d; + } + } + return r; +} + +function bnEquals(a) { return(this.compareTo(a)==0); } +function bnMin(a) { return(this.compareTo(a)<0)?this:a; } +function bnMax(a) { return(this.compareTo(a)>0)?this:a; } + +// (protected) r = this op a (bitwise) +function bnpBitwiseTo(a,op,r) { + var this_array = this.array; + var a_array = a.array; + var r_array = r.array; + var i, f, m = Math.min(a.t,this.t); + for(i = 0; i < m; ++i) r_array[i] = op(this_array[i],a_array[i]); + if(a.t < this.t) { + f = a.s&BI_DM; + for(i = m; i < this.t; ++i) r_array[i] = op(this_array[i],f); + r.t = this.t; + } + else { + f = this.s&BI_DM; + for(i = m; i < a.t; ++i) r_array[i] = op(f,a_array[i]); + r.t = a.t; + } + r.s = op(this.s,a.s); + r.clamp(); +} + +// (public) this & a +function op_and(x,y) { return x&y; } +function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } + +// (public) this | a +function op_or(x,y) { return x|y; } +function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } + +// (public) this ^ a +function op_xor(x,y) { return x^y; } +function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } + +// (public) this & ~a +function op_andnot(x,y) { return x&~y; } +function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } + +// (public) ~this +function bnNot() { + var this_array = this.array; + var r = nbi(); + var r_array = r.array; + + for(var i = 0; i < this.t; ++i) r_array[i] = BI_DM&~this_array[i]; + r.t = this.t; + r.s = ~this.s; + return r; +} + +// (public) this << n +function bnShiftLeft(n) { + var r = nbi(); + if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); + return r; +} + +// (public) this >> n +function bnShiftRight(n) { + var r = nbi(); + if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); + return r; +} + +// return index of lowest 1-bit in x, x < 2^31 +function lbit(x) { + if(x == 0) return -1; + var r = 0; + if((x&0xffff) == 0) { x >>= 16; r += 16; } + if((x&0xff) == 0) { x >>= 8; r += 8; } + if((x&0xf) == 0) { x >>= 4; r += 4; } + if((x&3) == 0) { x >>= 2; r += 2; } + if((x&1) == 0) ++r; + return r; +} + +// (public) returns index of lowest 1-bit (or -1 if none) +function bnGetLowestSetBit() { + var this_array = this.array; + for(var i = 0; i < this.t; ++i) + if(this_array[i] != 0) return i*BI_DB+lbit(this_array[i]); + if(this.s < 0) return this.t*BI_DB; + return -1; +} + +// return number of 1 bits in x +function cbit(x) { + var r = 0; + while(x != 0) { x &= x-1; ++r; } + return r; +} + +// (public) return number of set bits +function bnBitCount() { + var r = 0, x = this.s&BI_DM; + for(var i = 0; i < this.t; ++i) r += cbit(this_array[i]^x); + return r; +} + +// (public) true iff nth bit is set +function bnTestBit(n) { + var this_array = this.array; + var j = Math.floor(n/BI_DB); + if(j >= this.t) return(this.s!=0); + return((this_array[j]&(1<<(n%BI_DB)))!=0); +} + +// (protected) this op (1<>= BI_DB; + } + if(a.t < this.t) { + c += a.s; + while(i < this.t) { + c += this_array[i]; + r_array[i++] = c&BI_DM; + c >>= BI_DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c += a_array[i]; + r_array[i++] = c&BI_DM; + c >>= BI_DB; + } + c += a.s; + } + r.s = (c<0)?-1:0; + if(c > 0) r_array[i++] = c; + else if(c < -1) r_array[i++] = BI_DV+c; + r.t = i; + r.clamp(); +} + +// (public) this + a +function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } + +// (public) this - a +function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } + +// (public) this * a +function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } + +// (public) this / a +function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } + +// (public) this % a +function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } + +// (public) [this/a,this%a] +function bnDivideAndRemainder(a) { + var q = nbi(), r = nbi(); + this.divRemTo(a,q,r); + return new Array(q,r); +} + +// (protected) this *= n, this >= 0, 1 < n < DV +function bnpDMultiply(n) { + var this_array = this.array; + this_array[this.t] = this.am(0,n-1,this,0,0,this.t); + ++this.t; + this.clamp(); +} + +// (protected) this += n << w words, this >= 0 +function bnpDAddOffset(n,w) { + var this_array = this.array; + while(this.t <= w) this_array[this.t++] = 0; + this_array[w] += n; + while(this_array[w] >= BI_DV) { + this_array[w] -= BI_DV; + if(++w >= this.t) this_array[this.t++] = 0; + ++this_array[w]; + } +} + +// A "null" reducer +function NullExp() {} +function nNop(x) { return x; } +function nMulTo(x,y,r) { x.multiplyTo(y,r); } +function nSqrTo(x,r) { x.squareTo(r); } + +NullExp.prototype.convert = nNop; +NullExp.prototype.revert = nNop; +NullExp.prototype.mulTo = nMulTo; +NullExp.prototype.sqrTo = nSqrTo; + +// (public) this^e +function bnPow(e) { return this.exp(e,new NullExp()); } + +// (protected) r = lower n words of "this * a", a.t <= n +// "this" should be the larger one if appropriate. +function bnpMultiplyLowerTo(a,n,r) { + var r_array = r.array; + var a_array = a.array; + var i = Math.min(this.t+a.t,n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while(i > 0) r_array[--i] = 0; + var j; + for(j = r.t-this.t; i < j; ++i) r_array[i+this.t] = this.am(0,a_array[i],r,i,0,this.t); + for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a_array[i],r,i,0,n-i); + r.clamp(); +} + +// (protected) r = "this * a" without lower n words, n > 0 +// "this" should be the larger one if appropriate. +function bnpMultiplyUpperTo(a,n,r) { + var r_array = r.array; + var a_array = a.array; + --n; + var i = r.t = this.t+a.t-n; + r.s = 0; // assumes a,this >= 0 + while(--i >= 0) r_array[i] = 0; + for(i = Math.max(n-this.t,0); i < a.t; ++i) + r_array[this.t+i-n] = this.am(n-i,a_array[i],r,0,0,this.t+i-n); + r.clamp(); + r.drShiftTo(1,r); +} + +// Barrett modular reduction +function Barrett(m) { + // setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2*m.t,this.r2); + this.mu = this.r2.divide(m); + this.m = m; +} + +function barrettConvert(x) { + if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); + else if(x.compareTo(this.m) < 0) return x; + else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } +} + +function barrettRevert(x) { return x; } + +// x = x mod m (HAC 14.42) +function barrettReduce(x) { + x.drShiftTo(this.m.t-1,this.r2); + if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } + this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); + this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); + while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); + x.subTo(this.r2,x); + while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = x^2 mod m; x != r +function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = x*y mod m; x,y != r +function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Barrett.prototype.convert = barrettConvert; +Barrett.prototype.revert = barrettRevert; +Barrett.prototype.reduce = barrettReduce; +Barrett.prototype.mulTo = barrettMulTo; +Barrett.prototype.sqrTo = barrettSqrTo; + +// (public) this^e % m (HAC 14.85) +function bnModPow(e,m) { + var e_array = e.array; + var i = e.bitLength(), k, r = nbv(1), z; + if(i <= 0) return r; + else if(i < 18) k = 1; + else if(i < 48) k = 3; + else if(i < 144) k = 4; + else if(i < 768) k = 5; + else k = 6; + if(i < 8) + z = new Classic(m); + else if(m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); + + // precomputation + var g = new Array(), n = 3, k1 = k-1, km = (1< 1) { + var g2 = nbi(); + z.sqrTo(g[1],g2); + while(n <= km) { + g[n] = nbi(); + z.mulTo(g2,g[n-2],g[n]); + n += 2; + } + } + + var j = e.t-1, w, is1 = true, r2 = nbi(), t; + i = nbits(e_array[j])-1; + while(j >= 0) { + if(i >= k1) w = (e_array[j]>>(i-k1))&km; + else { + w = (e_array[j]&((1<<(i+1))-1))<<(k1-i); + if(j > 0) w |= e_array[j-1]>>(BI_DB+i-k1); + } + + n = k; + while((w&1) == 0) { w >>= 1; --n; } + if((i -= n) < 0) { i += BI_DB; --j; } + if(is1) { // ret == 1, don't bother squaring or multiplying it + g[w].copyTo(r); + is1 = false; + } + else { + while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } + if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } + z.mulTo(r2,g[w],r); + } + + while(j >= 0 && (e_array[j]&(1< 0) { + x.rShiftTo(g,x); + y.rShiftTo(g,y); + } + while(x.signum() > 0) { + if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); + if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); + if(x.compareTo(y) >= 0) { + x.subTo(y,x); + x.rShiftTo(1,x); + } + else { + y.subTo(x,y); + y.rShiftTo(1,y); + } + } + if(g > 0) y.lShiftTo(g,y); + return y; +} + +// (protected) this % n, n < 2^26 +function bnpModInt(n) { + var this_array = this.array; + if(n <= 0) return 0; + var d = BI_DV%n, r = (this.s<0)?n-1:0; + if(this.t > 0) + if(d == 0) r = this_array[0]%n; + else for(var i = this.t-1; i >= 0; --i) r = (d*r+this_array[i])%n; + return r; +} + +// (public) 1/this % m (HAC 14.61) +function bnModInverse(m) { + var ac = m.isEven(); + if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; + var u = m.clone(), v = this.clone(); + var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); + while(u.signum() != 0) { + while(u.isEven()) { + u.rShiftTo(1,u); + if(ac) { + if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } + a.rShiftTo(1,a); + } + else if(!b.isEven()) b.subTo(m,b); + b.rShiftTo(1,b); + } + while(v.isEven()) { + v.rShiftTo(1,v); + if(ac) { + if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } + c.rShiftTo(1,c); + } + else if(!d.isEven()) d.subTo(m,d); + d.rShiftTo(1,d); + } + if(u.compareTo(v) >= 0) { + u.subTo(v,u); + if(ac) a.subTo(c,a); + b.subTo(d,b); + } + else { + v.subTo(u,v); + if(ac) c.subTo(a,c); + d.subTo(b,d); + } + } + if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; + if(d.compareTo(m) >= 0) return d.subtract(m); + if(d.signum() < 0) d.addTo(m,d); else return d; + if(d.signum() < 0) return d.add(m); else return d; +} + +var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509]; +var lplim = (1<<26)/lowprimes[lowprimes.length-1]; + +// (public) test primality with certainty >= 1-.5^t +function bnIsProbablePrime(t) { + var i, x = this.abs(); + var x_array = x.array; + if(x.t == 1 && x_array[0] <= lowprimes[lowprimes.length-1]) { + for(i = 0; i < lowprimes.length; ++i) + if(x_array[0] == lowprimes[i]) return true; + return false; + } + if(x.isEven()) return false; + i = 1; + while(i < lowprimes.length) { + var m = lowprimes[i], j = i+1; + while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; + m = x.modInt(m); + while(i < j) if(m%lowprimes[i++] == 0) return false; + } + return x.millerRabin(t); +} + +// (protected) true if probably prime (HAC 4.24, Miller-Rabin) +function bnpMillerRabin(t) { + var n1 = this.subtract(BigInteger.ONE); + var k = n1.getLowestSetBit(); + if(k <= 0) return false; + var r = n1.shiftRight(k); + t = (t+1)>>1; + if(t > lowprimes.length) t = lowprimes.length; + var a = nbi(); + for(var i = 0; i < t; ++i) { + a.fromInt(lowprimes[i]); + var y = a.modPow(r,this); + if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { + var j = 1; + while(j++ < k && y.compareTo(n1) != 0) { + y = y.modPowInt(2,this); + if(y.compareTo(BigInteger.ONE) == 0) return false; + } + if(y.compareTo(n1) != 0) return false; + } + } + return true; +} + +// protected +BigInteger.prototype.chunkSize = bnpChunkSize; +BigInteger.prototype.toRadix = bnpToRadix; +BigInteger.prototype.fromRadix = bnpFromRadix; +BigInteger.prototype.fromNumber = bnpFromNumber; +BigInteger.prototype.bitwiseTo = bnpBitwiseTo; +BigInteger.prototype.changeBit = bnpChangeBit; +BigInteger.prototype.addTo = bnpAddTo; +BigInteger.prototype.dMultiply = bnpDMultiply; +BigInteger.prototype.dAddOffset = bnpDAddOffset; +BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; +BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; +BigInteger.prototype.modInt = bnpModInt; +BigInteger.prototype.millerRabin = bnpMillerRabin; + +// public +BigInteger.prototype.clone = bnClone; +BigInteger.prototype.intValue = bnIntValue; +BigInteger.prototype.byteValue = bnByteValue; +BigInteger.prototype.shortValue = bnShortValue; +BigInteger.prototype.signum = bnSigNum; +BigInteger.prototype.toByteArray = bnToByteArray; +BigInteger.prototype.equals = bnEquals; +BigInteger.prototype.min = bnMin; +BigInteger.prototype.max = bnMax; +BigInteger.prototype.and = bnAnd; +BigInteger.prototype.or = bnOr; +BigInteger.prototype.xor = bnXor; +BigInteger.prototype.andNot = bnAndNot; +BigInteger.prototype.not = bnNot; +BigInteger.prototype.shiftLeft = bnShiftLeft; +BigInteger.prototype.shiftRight = bnShiftRight; +BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; +BigInteger.prototype.bitCount = bnBitCount; +BigInteger.prototype.testBit = bnTestBit; +BigInteger.prototype.setBit = bnSetBit; +BigInteger.prototype.clearBit = bnClearBit; +BigInteger.prototype.flipBit = bnFlipBit; +BigInteger.prototype.add = bnAdd; +BigInteger.prototype.subtract = bnSubtract; +BigInteger.prototype.multiply = bnMultiply; +BigInteger.prototype.divide = bnDivide; +BigInteger.prototype.remainder = bnRemainder; +BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; +BigInteger.prototype.modPow = bnModPow; +BigInteger.prototype.modInverse = bnModInverse; +BigInteger.prototype.pow = bnPow; +BigInteger.prototype.gcd = bnGCD; +BigInteger.prototype.isProbablePrime = bnIsProbablePrime; + +// BigInteger interfaces not implemented in jsbn: + +// BigInteger(int signum, byte[] magnitude) +// double doubleValue() +// float floatValue() +// int hashCode() +// long longValue() +// static BigInteger valueOf(long val) +// prng4.js - uses Arcfour as a PRNG + +function Arcfour() { + this.i = 0; + this.j = 0; + this.S = new Array(); +} + +// Initialize arcfour context from key, an array of ints, each from [0..255] +function ARC4init(key) { + var i, j, t; + for(i = 0; i < 256; ++i) + this.S[i] = i; + j = 0; + for(i = 0; i < 256; ++i) { + j = (j + this.S[i] + key[i % key.length]) & 255; + t = this.S[i]; + this.S[i] = this.S[j]; + this.S[j] = t; + } + this.i = 0; + this.j = 0; +} + +function ARC4next() { + var t; + this.i = (this.i + 1) & 255; + this.j = (this.j + this.S[this.i]) & 255; + t = this.S[this.i]; + this.S[this.i] = this.S[this.j]; + this.S[this.j] = t; + return this.S[(t + this.S[this.i]) & 255]; +} + +Arcfour.prototype.init = ARC4init; +Arcfour.prototype.next = ARC4next; + +// Plug in your RNG constructor here +function prng_newstate() { + return new Arcfour(); +} + +// Pool size must be a multiple of 4 and greater than 32. +// An array of bytes the size of the pool will be passed to init() +var rng_psize = 256; +// Random number generator - requires a PRNG backend, e.g. prng4.js + +// For best results, put code like +// +// in your main HTML document. + +var rng_state; +var rng_pool; +var rng_pptr; + +// Mix in a 32-bit integer into the pool +function rng_seed_int(x) { + rng_pool[rng_pptr++] ^= x & 255; + rng_pool[rng_pptr++] ^= (x >> 8) & 255; + rng_pool[rng_pptr++] ^= (x >> 16) & 255; + rng_pool[rng_pptr++] ^= (x >> 24) & 255; + if(rng_pptr >= rng_psize) rng_pptr -= rng_psize; +} + +// Mix in the current time (w/milliseconds) into the pool +function rng_seed_time() { + // Use pre-computed date to avoid making the benchmark + // results dependent on the current date. + rng_seed_int(1122926989487); +} + +// Initialize the pool with junk if needed. +if(rng_pool == null) { + rng_pool = new Array(); + rng_pptr = 0; + var t; + while(rng_pptr < rng_psize) { // extract some randomness from Math.random() + t = Math.floor(65536 * Math.random()); + rng_pool[rng_pptr++] = t >>> 8; + rng_pool[rng_pptr++] = t & 255; + } + rng_pptr = 0; + rng_seed_time(); + //rng_seed_int(window.screenX); + //rng_seed_int(window.screenY); +} + +function rng_get_byte() { + if(rng_state == null) { + rng_seed_time(); + rng_state = prng_newstate(); + rng_state.init(rng_pool); + for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) + rng_pool[rng_pptr] = 0; + rng_pptr = 0; + //rng_pool = null; + } + // TODO: allow reseeding after first request + return rng_state.next(); +} + +function rng_get_bytes(ba) { + var i; + for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte(); +} + +function SecureRandom() {} + +SecureRandom.prototype.nextBytes = rng_get_bytes; +// Depends on jsbn.js and rng.js + +// convert a (hex) string to a bignum object +function parseBigInt(str,r) { + return new BigInteger(str,r); +} + +function linebrk(s,n) { + var ret = ""; + var i = 0; + while(i + n < s.length) { + ret += s.substring(i,i+n) + "\n"; + i += n; + } + return ret + s.substring(i,s.length); +} + +function byte2Hex(b) { + if(b < 0x10) + return "0" + b.toString(16); + else + return b.toString(16); +} + +// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint +function pkcs1pad2(s,n) { + if(n < s.length + 11) { + alert("Message too long for RSA"); + return null; + } + var ba = new Array(); + var i = s.length - 1; + while(i >= 0 && n > 0) ba[--n] = s.charCodeAt(i--); + ba[--n] = 0; + var rng = new SecureRandom(); + var x = new Array(); + while(n > 2) { // random non-zero pad + x[0] = 0; + while(x[0] == 0) rng.nextBytes(x); + ba[--n] = x[0]; + } + ba[--n] = 2; + ba[--n] = 0; + return new BigInteger(ba); +} + +// "empty" RSA key constructor +function RSAKey() { + this.n = null; + this.e = 0; + this.d = null; + this.p = null; + this.q = null; + this.dmp1 = null; + this.dmq1 = null; + this.coeff = null; +} + +// Set the public key fields N and e from hex strings +function RSASetPublic(N,E) { + if(N != null && E != null && N.length > 0 && E.length > 0) { + this.n = parseBigInt(N,16); + this.e = parseInt(E,16); + } + else + alert("Invalid RSA public key"); +} + +// Perform raw public operation on "x": return x^e (mod n) +function RSADoPublic(x) { + return x.modPowInt(this.e, this.n); +} + +// Return the PKCS#1 RSA encryption of "text" as an even-length hex string +function RSAEncrypt(text) { + var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3); + if(m == null) return null; + var c = this.doPublic(m); + if(c == null) return null; + var h = c.toString(16); + if((h.length & 1) == 0) return h; else return "0" + h; +} + +// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string +//function RSAEncryptB64(text) { +// var h = this.encrypt(text); +// if(h) return hex2b64(h); else return null; +//} + +// protected +RSAKey.prototype.doPublic = RSADoPublic; + +// public +RSAKey.prototype.setPublic = RSASetPublic; +RSAKey.prototype.encrypt = RSAEncrypt; +//RSAKey.prototype.encrypt_b64 = RSAEncryptB64; +// Depends on rsa.js and jsbn2.js + +// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext +function pkcs1unpad2(d,n) { + var b = d.toByteArray(); + var i = 0; + while(i < b.length && b[i] == 0) ++i; + if(b.length-i != n-1 || b[i] != 2) + return null; + ++i; + while(b[i] != 0) + if(++i >= b.length) return null; + var ret = ""; + while(++i < b.length) + ret += String.fromCharCode(b[i]); + return ret; +} + +// Set the private key fields N, e, and d from hex strings +function RSASetPrivate(N,E,D) { + if(N != null && E != null && N.length > 0 && E.length > 0) { + this.n = parseBigInt(N,16); + this.e = parseInt(E,16); + this.d = parseBigInt(D,16); + } + else + alert("Invalid RSA private key"); +} + +// Set the private key fields N, e, d and CRT params from hex strings +function RSASetPrivateEx(N,E,D,P,Q,DP,DQ,C) { + if(N != null && E != null && N.length > 0 && E.length > 0) { + this.n = parseBigInt(N,16); + this.e = parseInt(E,16); + this.d = parseBigInt(D,16); + this.p = parseBigInt(P,16); + this.q = parseBigInt(Q,16); + this.dmp1 = parseBigInt(DP,16); + this.dmq1 = parseBigInt(DQ,16); + this.coeff = parseBigInt(C,16); + } + else + alert("Invalid RSA private key"); +} + +// Generate a new random private key B bits long, using public expt E +function RSAGenerate(B,E) { + var rng = new SecureRandom(); + var qs = B>>1; + this.e = parseInt(E,16); + var ee = new BigInteger(E,16); + for(;;) { + for(;;) { + this.p = new BigInteger(B-qs,1,rng); + if(this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break; + } + for(;;) { + this.q = new BigInteger(qs,1,rng); + if(this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break; + } + if(this.p.compareTo(this.q) <= 0) { + var t = this.p; + this.p = this.q; + this.q = t; + } + var p1 = this.p.subtract(BigInteger.ONE); + var q1 = this.q.subtract(BigInteger.ONE); + var phi = p1.multiply(q1); + if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) { + this.n = this.p.multiply(this.q); + this.d = ee.modInverse(phi); + this.dmp1 = this.d.mod(p1); + this.dmq1 = this.d.mod(q1); + this.coeff = this.q.modInverse(this.p); + break; + } + } +} + +// Perform raw private operation on "x": return x^d (mod n) +function RSADoPrivate(x) { + if(this.p == null || this.q == null) + return x.modPow(this.d, this.n); + + // TODO: re-calculate any missing CRT params + var xp = x.mod(this.p).modPow(this.dmp1, this.p); + var xq = x.mod(this.q).modPow(this.dmq1, this.q); + + while(xp.compareTo(xq) < 0) + xp = xp.add(this.p); + return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq); +} + +// Return the PKCS#1 RSA decryption of "ctext". +// "ctext" is an even-length hex string and the output is a plain string. +function RSADecrypt(ctext) { + var c = parseBigInt(ctext, 16); + var m = this.doPrivate(c); + if(m == null) return null; + return pkcs1unpad2(m, (this.n.bitLength()+7)>>3); +} + +// Return the PKCS#1 RSA decryption of "ctext". +// "ctext" is a Base64-encoded string and the output is a plain string. +//function RSAB64Decrypt(ctext) { +// var h = b64tohex(ctext); +// if(h) return this.decrypt(h); else return null; +//} + +// protected +RSAKey.prototype.doPrivate = RSADoPrivate; + +// public +RSAKey.prototype.setPrivate = RSASetPrivate; +RSAKey.prototype.setPrivateEx = RSASetPrivateEx; +RSAKey.prototype.generate = RSAGenerate; +RSAKey.prototype.decrypt = RSADecrypt; +//RSAKey.prototype.b64_decrypt = RSAB64Decrypt; + + +nValue="a5261939975948bb7a58dffe5ff54e65f0498f9175f5a09288810b8975871e99af3b5dd94057b0fc07535f5f97444504fa35169d461d0d30cf0192e307727c065168c788771c561a9400fb49175e9e6aa4e23fe11af69e9412dd23b0cb6684c4c2429bce139e848ab26d0829073351f4acd36074eafd036a5eb83359d2a698d3"; +eValue="10001"; +dValue="8e9912f6d3645894e8d38cb58c0db81ff516cf4c7e5a14c7f1eddb1459d2cded4d8d293fc97aee6aefb861859c8b6a3d1dfe710463e1f9ddc72048c09751971c4a580aa51eb523357a3cc48d31cfad1d4a165066ed92d4748fb6571211da5cb14bc11b6e2df7c1a559e6d5ac1cd5c94703a22891464fba23d0d965086277a161"; +pValue="d090ce58a92c75233a6486cb0a9209bf3583b64f540c76f5294bb97d285eed33aec220bde14b2417951178ac152ceab6da7090905b478195498b352048f15e7d"; +qValue="cab575dc652bb66df15a0359609d51d1db184750c00c6698b90ef3465c99655103edbf0d54c56aec0ce3c4d22592338092a126a0cc49f65a4a30d222b411e58f"; +dmp1Value="1a24bca8e273df2f0e47c199bbf678604e7df7215480c77c8db39f49b000ce2cf7500038acfff5433b7d582a01f1826e6f4d42e1c57f5e1fef7b12aabc59fd25"; +dmq1Value="3d06982efbbe47339e1f6d36b1216b8a741d410b0c662f54f7118b27b9a4ec9d914337eb39841d8666f3034408cf94f5b62f11c402fc994fe15a05493150d9fd"; +coeffValue="3a3e731acd8960b7ff9eb81a7ff93bd1cfa74cbd56987db58b4594fb09c09084db1734c8143f98b602b981aaa9243ca28deb69b5b280ee8dcee0fd2625e53250"; + +setupEngine(am3, 28); + +var TEXT = "The quick brown fox jumped over the extremely lazy frog! " + + "Now is the time for all good men to come to the party."; +var encrypted; + +function encrypt() { + var RSA = new RSAKey(); + RSA.setPublic(nValue, eValue); + RSA.setPrivateEx(nValue, eValue, dValue, pValue, qValue, dmp1Value, dmq1Value, coeffValue); + encrypted = RSA.encrypt(TEXT); +} + +function decrypt() { + var RSA = new RSAKey(); + RSA.setPublic(nValue, eValue); + RSA.setPrivateEx(nValue, eValue, dValue, pValue, qValue, dmp1Value, dmq1Value, coeffValue); + var decrypted = RSA.decrypt(encrypted); + if (decrypted != TEXT) { + throw new Error("Crypto operation failed"); + } +} +// The ray tracer code in this file is written by Adam Burmister. It +// is available in its original form from: +// +// http://labs.flog.nz.co/raytracer/ +// +// It has been modified slightly by Google to work as a standalone +// benchmark, but the all the computational code remains +// untouched. This file also contains a copy of parts of the Prototype +// JavaScript framework which is used by the ray tracer. + +var RayTrace = new BenchmarkSuite('RayTrace', 739989, [ + new Benchmark('RayTrace', renderScene) +]); + + +// Variable used to hold a number that can be used to verify that +// the scene was ray traced correctly. +var checkNumber; + + +// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ + +// The following is a copy of parts of the Prototype JavaScript library: + +// Prototype JavaScript framework, version 1.5.0 +// (c) 2005-2007 Sam Stephenson +// +// Prototype is freely distributable under the terms of an MIT-style license. +// For details, see the Prototype web site: http://prototype.conio.net/ + + +var Class = { + create: function() { + return function() { + this.initialize.apply(this, arguments); + } + } +}; + + +Object.extend = function(destination, source) { + for (var property in source) { + destination[property] = source[property]; + } + return destination; +}; + + +// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ + +// The rest of this file is the actual ray tracer written by Adam +// Burmister. It's a concatenation of the following files: +// +// flog/color.js +// flog/light.js +// flog/vector.js +// flog/ray.js +// flog/scene.js +// flog/material/basematerial.js +// flog/material/solid.js +// flog/material/chessboard.js +// flog/shape/baseshape.js +// flog/shape/sphere.js +// flog/shape/plane.js +// flog/intersectioninfo.js +// flog/camera.js +// flog/background.js +// flog/engine.js + + +/* Fake a Flog.* namespace */ +if(typeof(Flog) == 'undefined') var Flog = {}; +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; + +Flog.RayTracer.Color = Class.create(); + +Flog.RayTracer.Color.prototype = { + red : 0.0, + green : 0.0, + blue : 0.0, + + initialize : function(r, g, b) { + if(!r) r = 0.0; + if(!g) g = 0.0; + if(!b) b = 0.0; + + this.red = r; + this.green = g; + this.blue = b; + }, + + add : function(c1, c2){ + var result = new Flog.RayTracer.Color(0,0,0); + + result.red = c1.red + c2.red; + result.green = c1.green + c2.green; + result.blue = c1.blue + c2.blue; + + return result; + }, + + addScalar: function(c1, s){ + var result = new Flog.RayTracer.Color(0,0,0); + + result.red = c1.red + s; + result.green = c1.green + s; + result.blue = c1.blue + s; + + result.limit(); + + return result; + }, + + subtract: function(c1, c2){ + var result = new Flog.RayTracer.Color(0,0,0); + + result.red = c1.red - c2.red; + result.green = c1.green - c2.green; + result.blue = c1.blue - c2.blue; + + return result; + }, + + multiply : function(c1, c2) { + var result = new Flog.RayTracer.Color(0,0,0); + + result.red = c1.red * c2.red; + result.green = c1.green * c2.green; + result.blue = c1.blue * c2.blue; + + return result; + }, + + multiplyScalar : function(c1, f) { + var result = new Flog.RayTracer.Color(0,0,0); + + result.red = c1.red * f; + result.green = c1.green * f; + result.blue = c1.blue * f; + + return result; + }, + + divideFactor : function(c1, f) { + var result = new Flog.RayTracer.Color(0,0,0); + + result.red = c1.red / f; + result.green = c1.green / f; + result.blue = c1.blue / f; + + return result; + }, + + limit: function(){ + this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0; + this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0; + this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0; + }, + + distance : function(color) { + var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue); + return d; + }, + + blend: function(c1, c2, w){ + var result = new Flog.RayTracer.Color(0,0,0); + result = Flog.RayTracer.Color.prototype.add( + Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w), + Flog.RayTracer.Color.prototype.multiplyScalar(c2, w) + ); + return result; + }, + + brightness : function() { + var r = Math.floor(this.red*255); + var g = Math.floor(this.green*255); + var b = Math.floor(this.blue*255); + return (r * 77 + g * 150 + b * 29) >> 8; + }, + + toString : function () { + var r = Math.floor(this.red*255); + var g = Math.floor(this.green*255); + var b = Math.floor(this.blue*255); + + return "rgb("+ r +","+ g +","+ b +")"; + } +} +/* Fake a Flog.* namespace */ +if(typeof(Flog) == 'undefined') var Flog = {}; +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; + +Flog.RayTracer.Light = Class.create(); + +Flog.RayTracer.Light.prototype = { + position: null, + color: null, + intensity: 10.0, + + initialize : function(pos, color, intensity) { + this.position = pos; + this.color = color; + this.intensity = (intensity ? intensity : 10.0); + }, + + toString : function () { + return 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']'; + } +} +/* Fake a Flog.* namespace */ +if(typeof(Flog) == 'undefined') var Flog = {}; +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; + +Flog.RayTracer.Vector = Class.create(); + +Flog.RayTracer.Vector.prototype = { + x : 0.0, + y : 0.0, + z : 0.0, + + initialize : function(x, y, z) { + this.x = (x ? x : 0); + this.y = (y ? y : 0); + this.z = (z ? z : 0); + }, + + copy: function(vector){ + this.x = vector.x; + this.y = vector.y; + this.z = vector.z; + }, + + normalize : function() { + var m = this.magnitude(); + return new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m); + }, + + magnitude : function() { + return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z)); + }, + + cross : function(w) { + return new Flog.RayTracer.Vector( + -this.z * w.y + this.y * w.z, + this.z * w.x - this.x * w.z, + -this.y * w.x + this.x * w.y); + }, + + dot : function(w) { + return this.x * w.x + this.y * w.y + this.z * w.z; + }, + + add : function(v, w) { + return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z); + }, + + subtract : function(v, w) { + if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']'; + return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z); + }, + + multiplyVector : function(v, w) { + return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z); + }, + + multiplyScalar : function(v, w) { + return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w); + }, + + toString : function () { + return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']'; + } +} +/* Fake a Flog.* namespace */ +if(typeof(Flog) == 'undefined') var Flog = {}; +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; + +Flog.RayTracer.Ray = Class.create(); + +Flog.RayTracer.Ray.prototype = { + position : null, + direction : null, + initialize : function(pos, dir) { + this.position = pos; + this.direction = dir; + }, + + toString : function () { + return 'Ray [' + this.position + ',' + this.direction + ']'; + } +} +/* Fake a Flog.* namespace */ +if(typeof(Flog) == 'undefined') var Flog = {}; +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; + +Flog.RayTracer.Scene = Class.create(); + +Flog.RayTracer.Scene.prototype = { + camera : null, + shapes : [], + lights : [], + background : null, + + initialize : function() { + this.camera = new Flog.RayTracer.Camera( + new Flog.RayTracer.Vector(0,0,-5), + new Flog.RayTracer.Vector(0,0,1), + new Flog.RayTracer.Vector(0,1,0) + ); + this.shapes = new Array(); + this.lights = new Array(); + this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0,0,0.5), 0.2); + } +} +/* Fake a Flog.* namespace */ +if(typeof(Flog) == 'undefined') var Flog = {}; +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; +if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {}; + +Flog.RayTracer.Material.BaseMaterial = Class.create(); + +Flog.RayTracer.Material.BaseMaterial.prototype = { + + gloss: 2.0, // [0...infinity] 0 = matt + transparency: 0.0, // 0=opaque + reflection: 0.0, // [0...infinity] 0 = no reflection + refraction: 0.50, + hasTexture: false, + + initialize : function() { + + }, + + getColor: function(u, v){ + + }, + + wrapUp: function(t){ + t = t % 2.0; + if(t < -1) t += 2.0; + if(t >= 1) t -= 2.0; + return t; + }, + + toString : function () { + return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']'; + } +} +/* Fake a Flog.* namespace */ +if(typeof(Flog) == 'undefined') var Flog = {}; +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; + +Flog.RayTracer.Material.Solid = Class.create(); + +Flog.RayTracer.Material.Solid.prototype = Object.extend( + new Flog.RayTracer.Material.BaseMaterial(), { + initialize : function(color, reflection, refraction, transparency, gloss) { + this.color = color; + this.reflection = reflection; + this.transparency = transparency; + this.gloss = gloss; + this.hasTexture = false; + }, + + getColor: function(u, v){ + return this.color; + }, + + toString : function () { + return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']'; + } + } +); +/* Fake a Flog.* namespace */ +if(typeof(Flog) == 'undefined') var Flog = {}; +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; + +Flog.RayTracer.Material.Chessboard = Class.create(); + +Flog.RayTracer.Material.Chessboard.prototype = Object.extend( + new Flog.RayTracer.Material.BaseMaterial(), { + colorEven: null, + colorOdd: null, + density: 0.5, + + initialize : function(colorEven, colorOdd, reflection, transparency, gloss, density) { + this.colorEven = colorEven; + this.colorOdd = colorOdd; + this.reflection = reflection; + this.transparency = transparency; + this.gloss = gloss; + this.density = density; + this.hasTexture = true; + }, + + getColor: function(u, v){ + var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density); + + if(t < 0.0) + return this.colorEven; + else + return this.colorOdd; + }, + + toString : function () { + return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']'; + } + } +); +/* Fake a Flog.* namespace */ +if(typeof(Flog) == 'undefined') var Flog = {}; +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; +if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {}; + +Flog.RayTracer.Shape.Sphere = Class.create(); + +Flog.RayTracer.Shape.Sphere.prototype = { + initialize : function(pos, radius, material) { + this.radius = radius; + this.position = pos; + this.material = material; + }, + + intersect: function(ray){ + var info = new Flog.RayTracer.IntersectionInfo(); + info.shape = this; + + var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position); + + var B = dst.dot(ray.direction); + var C = dst.dot(dst) - (this.radius * this.radius); + var D = (B * B) - C; + + if(D > 0){ // intersection! + info.isHit = true; + info.distance = (-B) - Math.sqrt(D); + info.position = Flog.RayTracer.Vector.prototype.add( + ray.position, + Flog.RayTracer.Vector.prototype.multiplyScalar( + ray.direction, + info.distance + ) + ); + info.normal = Flog.RayTracer.Vector.prototype.subtract( + info.position, + this.position + ).normalize(); + + info.color = this.material.getColor(0,0); + } else { + info.isHit = false; + } + return info; + }, + + toString : function () { + return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']'; + } +} +/* Fake a Flog.* namespace */ +if(typeof(Flog) == 'undefined') var Flog = {}; +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; +if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {}; + +Flog.RayTracer.Shape.Plane = Class.create(); + +Flog.RayTracer.Shape.Plane.prototype = { + d: 0.0, + + initialize : function(pos, d, material) { + this.position = pos; + this.d = d; + this.material = material; + }, + + intersect: function(ray){ + var info = new Flog.RayTracer.IntersectionInfo(); + + var Vd = this.position.dot(ray.direction); + if(Vd == 0) return info; // no intersection + + var t = -(this.position.dot(ray.position) + this.d) / Vd; + if(t <= 0) return info; + + info.shape = this; + info.isHit = true; + info.position = Flog.RayTracer.Vector.prototype.add( + ray.position, + Flog.RayTracer.Vector.prototype.multiplyScalar( + ray.direction, + t + ) + ); + info.normal = this.position; + info.distance = t; + + if(this.material.hasTexture){ + var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x); + var vV = vU.cross(this.position); + var u = info.position.dot(vU); + var v = info.position.dot(vV); + info.color = this.material.getColor(u,v); + } else { + info.color = this.material.getColor(0,0); + } + + return info; + }, + + toString : function () { + return 'Plane [' + this.position + ', d=' + this.d + ']'; + } +} +/* Fake a Flog.* namespace */ +if(typeof(Flog) == 'undefined') var Flog = {}; +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; + +Flog.RayTracer.IntersectionInfo = Class.create(); + +Flog.RayTracer.IntersectionInfo.prototype = { + isHit: false, + hitCount: 0, + shape: null, + position: null, + normal: null, + color: null, + distance: null, + + initialize : function() { + this.color = new Flog.RayTracer.Color(0,0,0); + }, + + toString : function () { + return 'Intersection [' + this.position + ']'; + } +} +/* Fake a Flog.* namespace */ +if(typeof(Flog) == 'undefined') var Flog = {}; +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; + +Flog.RayTracer.Camera = Class.create(); + +Flog.RayTracer.Camera.prototype = { + position: null, + lookAt: null, + equator: null, + up: null, + screen: null, + + initialize : function(pos, lookAt, up) { + this.position = pos; + this.lookAt = lookAt; + this.up = up; + this.equator = lookAt.normalize().cross(this.up); + this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt); + }, + + getRay: function(vx, vy){ + var pos = Flog.RayTracer.Vector.prototype.subtract( + this.screen, + Flog.RayTracer.Vector.prototype.subtract( + Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx), + Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy) + ) + ); + pos.y = pos.y * -1; + var dir = Flog.RayTracer.Vector.prototype.subtract( + pos, + this.position + ); + + var ray = new Flog.RayTracer.Ray(pos, dir.normalize()); + + return ray; + }, + + toString : function () { + return 'Ray []'; + } +} +/* Fake a Flog.* namespace */ +if(typeof(Flog) == 'undefined') var Flog = {}; +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; + +Flog.RayTracer.Background = Class.create(); + +Flog.RayTracer.Background.prototype = { + color : null, + ambience : 0.0, + + initialize : function(color, ambience) { + this.color = color; + this.ambience = ambience; + } +} +/* Fake a Flog.* namespace */ +if(typeof(Flog) == 'undefined') var Flog = {}; +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; + +Flog.RayTracer.Engine = Class.create(); + +Flog.RayTracer.Engine.prototype = { + canvas: null, /* 2d context we can render to */ + + initialize: function(options){ + this.options = Object.extend({ + canvasHeight: 100, + canvasWidth: 100, + pixelWidth: 2, + pixelHeight: 2, + renderDiffuse: false, + renderShadows: false, + renderHighlights: false, + renderReflections: false, + rayDepth: 2 + }, options || {}); + + this.options.canvasHeight /= this.options.pixelHeight; + this.options.canvasWidth /= this.options.pixelWidth; + + /* TODO: dynamically include other scripts */ + }, + + setPixel: function(x, y, color){ + var pxW, pxH; + pxW = this.options.pixelWidth; + pxH = this.options.pixelHeight; + + if (this.canvas) { + this.canvas.fillStyle = color.toString(); + this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH); + } else { + if (x === y) { + checkNumber += color.brightness(); + } + // print(x * pxW, y * pxH, pxW, pxH); + } + }, + + renderScene: function(scene, canvas){ + checkNumber = 0; + /* Get canvas */ + if (canvas) { + this.canvas = canvas.getContext("2d"); + } else { + this.canvas = null; + } + + var canvasHeight = this.options.canvasHeight; + var canvasWidth = this.options.canvasWidth; + + for(var y=0; y < canvasHeight; y++){ + for(var x=0; x < canvasWidth; x++){ + var yp = y * 1.0 / canvasHeight * 2 - 1; + var xp = x * 1.0 / canvasWidth * 2 - 1; + + var ray = scene.camera.getRay(xp, yp); + + var color = this.getPixelColor(ray, scene); + + this.setPixel(x, y, color); + } + } + if (checkNumber !== 2321) { + throw new Error("Scene rendered incorrectly"); + } + }, + + getPixelColor: function(ray, scene){ + var info = this.testIntersection(ray, scene, null); + if(info.isHit){ + var color = this.rayTrace(info, ray, scene, 0); + return color; + } + return scene.background.color; + }, + + testIntersection: function(ray, scene, exclude){ + var hits = 0; + var best = new Flog.RayTracer.IntersectionInfo(); + best.distance = 2000; + + for(var i=0; i= 0 && info.distance < best.distance){ + best = info; + hits++; + } + } + } + best.hitCount = hits; + return best; + }, + + getReflectionRay: function(P,N,V){ + var c1 = -N.dot(V); + var R1 = Flog.RayTracer.Vector.prototype.add( + Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1), + V + ); + return new Flog.RayTracer.Ray(P, R1); + }, + + rayTrace: function(info, ray, scene, depth){ + // Calc ambient + var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience); + var oldColor = color; + var shininess = Math.pow(10, info.shape.material.gloss + 1); + + for(var i=0; i 0.0){ + color = Flog.RayTracer.Color.prototype.add( + color, + Flog.RayTracer.Color.prototype.multiply( + info.color, + Flog.RayTracer.Color.prototype.multiplyScalar( + light.color, + L + ) + ) + ); + } + } + + // The greater the depth the more accurate the colours, but + // this is exponentially (!) expensive + if(depth <= this.options.rayDepth){ + // calculate reflection ray + if(this.options.renderReflections && info.shape.material.reflection > 0) + { + var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction); + var refl = this.testIntersection(reflectionRay, scene, info.shape); + + if (refl.isHit && refl.distance > 0){ + refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1); + } else { + refl.color = scene.background.color; + } + + color = Flog.RayTracer.Color.prototype.blend( + color, + refl.color, + info.shape.material.reflection + ); + } + + // Refraction + /* TODO */ + } + + /* Render shadows and highlights */ + + var shadowInfo = new Flog.RayTracer.IntersectionInfo(); + + if(this.options.renderShadows){ + var shadowRay = new Flog.RayTracer.Ray(info.position, v); + + shadowInfo = this.testIntersection(shadowRay, scene, info.shape); + if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/){ + var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5); + var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5)); + color = Flog.RayTracer.Color.prototype.addScalar(vA,dB); + } + } + + // Phong specular highlights + if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0){ + var Lv = Flog.RayTracer.Vector.prototype.subtract( + info.shape.position, + light.position + ).normalize(); + + var E = Flog.RayTracer.Vector.prototype.subtract( + scene.camera.position, + info.shape.position + ).normalize(); + + var H = Flog.RayTracer.Vector.prototype.subtract( + E, + Lv + ).normalize(); + + var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess); + color = Flog.RayTracer.Color.prototype.add( + Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight), + color + ); + } + } + color.limit(); + return color; + } +}; + + +function renderScene(){ + var scene = new Flog.RayTracer.Scene(); + + scene.camera = new Flog.RayTracer.Camera( + new Flog.RayTracer.Vector(0, 0, -15), + new Flog.RayTracer.Vector(-0.2, 0, 5), + new Flog.RayTracer.Vector(0, 1, 0) + ); + + scene.background = new Flog.RayTracer.Background( + new Flog.RayTracer.Color(0.5, 0.5, 0.5), + 0.4 + ); + + var sphere = new Flog.RayTracer.Shape.Sphere( + new Flog.RayTracer.Vector(-1.5, 1.5, 2), + 1.5, + new Flog.RayTracer.Material.Solid( + new Flog.RayTracer.Color(0,0.5,0.5), + 0.3, + 0.0, + 0.0, + 2.0 + ) + ); + + var sphere1 = new Flog.RayTracer.Shape.Sphere( + new Flog.RayTracer.Vector(1, 0.25, 1), + 0.5, + new Flog.RayTracer.Material.Solid( + new Flog.RayTracer.Color(0.9,0.9,0.9), + 0.1, + 0.0, + 0.0, + 1.5 + ) + ); + + var plane = new Flog.RayTracer.Shape.Plane( + new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(), + 1.2, + new Flog.RayTracer.Material.Chessboard( + new Flog.RayTracer.Color(1,1,1), + new Flog.RayTracer.Color(0,0,0), + 0.2, + 0.0, + 1.0, + 0.7 + ) + ); + + scene.shapes.push(plane); + scene.shapes.push(sphere); + scene.shapes.push(sphere1); + + var light = new Flog.RayTracer.Light( + new Flog.RayTracer.Vector(5, 10, -1), + new Flog.RayTracer.Color(0.8, 0.8, 0.8) + ); + + var light1 = new Flog.RayTracer.Light( + new Flog.RayTracer.Vector(-3, 5, -15), + new Flog.RayTracer.Color(0.8, 0.8, 0.8), + 100 + ); + + scene.lights.push(light); + scene.lights.push(light1); + + var imageWidth = 100; // $F('imageWidth'); + var imageHeight = 100; // $F('imageHeight'); + var pixelSize = "5,5".split(','); // $F('pixelSize').split(','); + var renderDiffuse = true; // $F('renderDiffuse'); + var renderShadows = true; // $F('renderShadows'); + var renderHighlights = true; // $F('renderHighlights'); + var renderReflections = true; // $F('renderReflections'); + var rayDepth = 2;//$F('rayDepth'); + + var raytracer = new Flog.RayTracer.Engine( + { + canvasWidth: imageWidth, + canvasHeight: imageHeight, + pixelWidth: pixelSize[0], + pixelHeight: pixelSize[1], + "renderDiffuse": renderDiffuse, + "renderHighlights": renderHighlights, + "renderShadows": renderShadows, + "renderReflections": renderReflections, + "rayDepth": rayDepth + } + ); + + raytracer.renderScene(scene, null, 0); +} +// This file is automatically generated by scheme2js, except for the +// benchmark harness code at the beginning and end of the file. + +var EarleyBoyer = new BenchmarkSuite('EarleyBoyer', 666463, [ + new Benchmark("Earley", function () { BgL_earleyzd2benchmarkzd2(); }), + new Benchmark("Boyer", function () { BgL_nboyerzd2benchmarkzd2(); }) +]); + + +/************* GENERATED FILE - DO NOT EDIT *************/ +/************* GENERATED FILE - DO NOT EDIT *************/ +/************* GENERATED FILE - DO NOT EDIT *************/ +/************* GENERATED FILE - DO NOT EDIT *************/ +/************* GENERATED FILE - DO NOT EDIT *************/ +/************* GENERATED FILE - DO NOT EDIT *************/ +/************* GENERATED FILE - DO NOT EDIT *************/ +/************* GENERATED FILE - DO NOT EDIT *************/ +/* + * To use write/prints/... the default-output port has to be set first. + * Simply setting SC_DEFAULT_OUT and SC_ERROR_OUT to the desired values + * should do the trick. + * In the following example the std-out and error-port are redirected to + * a DIV. +function initRuntime() { + function escapeHTML(s) { + var tmp = s; + tmp = tmp.replace(/&/g, "&"); + tmp = tmp.replace(//g, ">"); + tmp = tmp.replace(/ /g, " "); + tmp = tmp.replace(/\n/g, "
"); + tmp = tmp.replace(/\t/g, "    "); + return tmp; + + } + + document.write("
"); + SC_DEFAULT_OUT = new sc_GenericOutputPort( + function(s) { + var stdout = document.getElementById('stdout'); + stdout.innerHTML = stdout.innerHTML + escapeHTML(s); + }); + SC_ERROR_OUT = SC_DEFAULT_OUT; +} +*/ + + +function sc_print_debug() { + sc_print.apply(null, arguments); +} +/*** META ((export *js*)) */ +var sc_JS_GLOBALS = this; + +var __sc_LINE=-1; +var __sc_FILE=""; + +/*** META ((export #t)) */ +function sc_alert() { + var len = arguments.length; + var s = ""; + var i; + + for( i = 0; i < len; i++ ) { + s += sc_toDisplayString(arguments[ i ]); + } + + return alert( s ); +} + +/*** META ((export #t)) */ +function sc_typeof( x ) { + return typeof x; +} + +/*** META ((export #t)) */ +function sc_error() { + var a = [sc_jsstring2symbol("*error*")]; + for (var i = 0; i < arguments.length; i++) { + a[i+1] = arguments[i]; + } + throw a; +} + +/*** META ((export #t) + (peephole (prefix "throw "))) +*/ +function sc_raise(obj) { + throw obj; +} + +/*** META ((export with-handler-lambda)) */ +function sc_withHandlerLambda(handler, body) { + try { + return body(); + } catch(e) { + if (!e._internalException) + return handler(e); + else + throw e; + } +} + +var sc_properties = new Object(); + +/*** META ((export #t)) */ +function sc_putpropBang(sym, key, val) { + var ht = sc_properties[sym]; + if (!ht) { + ht = new Object(); + sc_properties[sym] = ht; + } + ht[key] = val; +} + +/*** META ((export #t)) */ +function sc_getprop(sym, key) { + var ht = sc_properties[sym]; + if (ht) { + if (key in ht) + return ht[key]; + else + return false; + } else + return false; +} + +/*** META ((export #t)) */ +function sc_rempropBang(sym, key) { + var ht = sc_properties[sym]; + if (ht) + delete ht[key]; +} + +/*** META ((export #t)) */ +function sc_any2String(o) { + return jsstring2string(sc_toDisplayString(o)); +} + +/*** META ((export #t) + (peephole (infix 2 2 "===")) + (type bool)) +*/ +function sc_isEqv(o1, o2) { + return (o1 === o2); +} + +/*** META ((export #t) + (peephole (infix 2 2 "===")) + (type bool)) +*/ +function sc_isEq(o1, o2) { + return (o1 === o2); +} + +/*** META ((export #t) + (type bool)) +*/ +function sc_isNumber(n) { + return (typeof n === "number"); +} + +/*** META ((export #t) + (type bool)) +*/ +function sc_isComplex(n) { + return sc_isNumber(n); +} + +/*** META ((export #t) + (type bool)) +*/ +function sc_isReal(n) { + return sc_isNumber(n); +} + +/*** META ((export #t) + (type bool)) +*/ +function sc_isRational(n) { + return sc_isReal(n); +} + +/*** META ((export #t) + (type bool)) +*/ +function sc_isInteger(n) { + return (parseInt(n) === n); +} + +/*** META ((export #t) + (type bool) + (peephole (postfix ", false"))) +*/ +// we don't have exact numbers... +function sc_isExact(n) { + return false; +} + +/*** META ((export #t) + (peephole (postfix ", true")) + (type bool)) +*/ +function sc_isInexact(n) { + return true; +} + +/*** META ((export = =fx =fl) + (type bool) + (peephole (infix 2 2 "==="))) +*/ +function sc_equal(x) { + for (var i = 1; i < arguments.length; i++) + if (x !== arguments[i]) + return false; + return true; +} + +/*** META ((export < = arguments[i]) + return false; + x = arguments[i]; + } + return true; +} + +/*** META ((export > >fx >fl) + (type bool) + (peephole (infix 2 2 ">"))) +*/ +function sc_greater(x, y) { + for (var i = 1; i < arguments.length; i++) { + if (x <= arguments[i]) + return false; + x = arguments[i]; + } + return true; +} + +/*** META ((export <= <=fx <=fl) + (type bool) + (peephole (infix 2 2 "<="))) +*/ +function sc_lessEqual(x, y) { + for (var i = 1; i < arguments.length; i++) { + if (x > arguments[i]) + return false; + x = arguments[i]; + } + return true; +} + +/*** META ((export >= >=fl >=fx) + (type bool) + (peephole (infix 2 2 ">="))) +*/ +function sc_greaterEqual(x, y) { + for (var i = 1; i < arguments.length; i++) { + if (x < arguments[i]) + return false; + x = arguments[i]; + } + return true; +} + +/*** META ((export #t) + (type bool) + (peephole (postfix "=== 0"))) +*/ +function sc_isZero(x) { + return (x === 0); +} + +/*** META ((export #t) + (type bool) + (peephole (postfix "> 0"))) +*/ +function sc_isPositive(x) { + return (x > 0); +} + +/*** META ((export #t) + (type bool) + (peephole (postfix "< 0"))) +*/ +function sc_isNegative(x) { + return (x < 0); +} + +/*** META ((export #t) + (type bool) + (peephole (postfix "%2===1"))) +*/ +function sc_isOdd(x) { + return (x % 2 === 1); +} + +/*** META ((export #t) + (type bool) + (peephole (postfix "%2===0"))) +*/ +function sc_isEven(x) { + return (x % 2 === 0); +} + +/*** META ((export #t)) */ +var sc_max = Math.max; +/*** META ((export #t)) */ +var sc_min = Math.min; + +/*** META ((export + +fx +fl) + (peephole (infix 0 #f "+" "0"))) +*/ +function sc_plus() { + var sum = 0; + for (var i = 0; i < arguments.length; i++) + sum += arguments[i]; + return sum; +} + +/*** META ((export * *fx *fl) + (peephole (infix 0 #f "*" "1"))) +*/ +function sc_multi() { + var product = 1; + for (var i = 0; i < arguments.length; i++) + product *= arguments[i]; + return product; +} + +/*** META ((export - -fx -fl) + (peephole (minus))) +*/ +function sc_minus(x) { + if (arguments.length === 1) + return -x; + else { + var res = x; + for (var i = 1; i < arguments.length; i++) + res -= arguments[i]; + return res; + } +} + +/*** META ((export / /fl) + (peephole (div))) +*/ +function sc_div(x) { + if (arguments.length === 1) + return 1/x; + else { + var res = x; + for (var i = 1; i < arguments.length; i++) + res /= arguments[i]; + return res; + } +} + +/*** META ((export #t)) */ +var sc_abs = Math.abs; + +/*** META ((export quotient /fx) + (peephole (hole 2 "parseInt(" x "/" y ")"))) +*/ +function sc_quotient(x, y) { + return parseInt(x / y); +} + +/*** META ((export #t) + (peephole (infix 2 2 "%"))) +*/ +function sc_remainder(x, y) { + return x % y; +} + +/*** META ((export #t) + (peephole (modulo))) +*/ +function sc_modulo(x, y) { + var remainder = x % y; + // if they don't have the same sign + if ((remainder * y) < 0) + return remainder + y; + else + return remainder; +} + +function sc_euclid_gcd(a, b) { + var temp; + if (a === 0) return b; + if (b === 0) return a; + if (a < 0) {a = -a;}; + if (b < 0) {b = -b;}; + if (b > a) {temp = a; a = b; b = temp;}; + while (true) { + a %= b; + if(a === 0) {return b;}; + b %= a; + if(b === 0) {return a;}; + }; + return b; +} + +/*** META ((export #t)) */ +function sc_gcd() { + var gcd = 0; + for (var i = 0; i < arguments.length; i++) + gcd = sc_euclid_gcd(gcd, arguments[i]); + return gcd; +} + +/*** META ((export #t)) */ +function sc_lcm() { + var lcm = 1; + for (var i = 0; i < arguments.length; i++) { + var f = Math.round(arguments[i] / sc_euclid_gcd(arguments[i], lcm)); + lcm *= Math.abs(f); + } + return lcm; +} + +// LIMITATION: numerator and denominator don't make sense in floating point world. +//var SC_MAX_DECIMALS = 1000000 +// +// function sc_numerator(x) { +// var rounded = Math.round(x * SC_MAX_DECIMALS); +// return Math.round(rounded / sc_euclid_gcd(rounded, SC_MAX_DECIMALS)); +// } + +// function sc_denominator(x) { +// var rounded = Math.round(x * SC_MAX_DECIMALS); +// return Math.round(SC_MAX_DECIMALS / sc_euclid_gcd(rounded, SC_MAX_DECIMALS)); +// } + +/*** META ((export #t)) */ +var sc_floor = Math.floor; +/*** META ((export #t)) */ +var sc_ceiling = Math.ceil; +/*** META ((export #t)) */ +var sc_truncate = parseInt; +/*** META ((export #t)) */ +var sc_round = Math.round; + +// LIMITATION: sc_rationalize doesn't make sense in a floating point world. + +/*** META ((export #t)) */ +var sc_exp = Math.exp; +/*** META ((export #t)) */ +var sc_log = Math.log; +/*** META ((export #t)) */ +var sc_sin = Math.sin; +/*** META ((export #t)) */ +var sc_cos = Math.cos; +/*** META ((export #t)) */ +var sc_tan = Math.tan; +/*** META ((export #t)) */ +var sc_asin = Math.asin; +/*** META ((export #t)) */ +var sc_acos = Math.acos; +/*** META ((export #t)) */ +var sc_atan = Math.atan; + +/*** META ((export #t)) */ +var sc_sqrt = Math.sqrt; +/*** META ((export #t)) */ +var sc_expt = Math.pow; + +// LIMITATION: we don't have complex numbers. +// LIMITATION: the following functions are hence not implemented. +// LIMITATION: make-rectangular, make-polar, real-part, imag-part, magnitude, angle +// LIMITATION: 2 argument atan + +/*** META ((export #t) + (peephole (id))) +*/ +function sc_exact2inexact(x) { + return x; +} + +/*** META ((export #t) + (peephole (id))) +*/ +function sc_inexact2exact(x) { + return x; +} + +function sc_number2jsstring(x, radix) { + if (radix) + return x.toString(radix); + else + return x.toString(); +} + +function sc_jsstring2number(s, radix) { + if (s === "") return false; + + if (radix) { + var t = parseInt(s, radix); + if (!t && t !== 0) return false; + // verify that each char is in range. (parseInt ignores leading + // white and trailing chars) + var allowedChars = "01234567890abcdefghijklmnopqrstuvwxyz".substring(0, radix+1); + if ((new RegExp("^["+allowedChars+"]*$", "i")).test(s)) + return t; + else return false; + } else { + var t = +s; // does not ignore trailing chars. + if (!t && t !== 0) return false; + // simply verify that first char is not whitespace. + var c = s.charAt(0); + // if +c is 0, but the char is not "0", then we have a whitespace. + if (+c === 0 && c !== "0") return false; + return t; + } +} + +/*** META ((export #t) + (type bool) + (peephole (not))) +*/ +function sc_not(b) { + return b === false; +} + +/*** META ((export #t) + (type bool)) +*/ +function sc_isBoolean(b) { + return (b === true) || (b === false); +} + +function sc_Pair(car, cdr) { + this.car = car; + this.cdr = cdr; +} + +sc_Pair.prototype.toString = function() { + return sc_toDisplayString(this); +}; +sc_Pair.prototype.sc_toWriteOrDisplayString = function(writeOrDisplay) { + var current = this; + + var res = "("; + + while(true) { + res += writeOrDisplay(current.car); + if (sc_isPair(current.cdr)) { + res += " "; + current = current.cdr; + } else if (current.cdr !== null) { + res += " . " + writeOrDisplay(current.cdr); + break; + } else // current.cdr == null + break; + } + + res += ")"; + + return res; +}; +sc_Pair.prototype.sc_toDisplayString = function() { + return this.sc_toWriteOrDisplayString(sc_toDisplayString); +}; +sc_Pair.prototype.sc_toWriteString = function() { + return this.sc_toWriteOrDisplayString(sc_toWriteString); +}; +// sc_Pair.prototype.sc_toWriteCircleString in IO.js + +/*** META ((export #t) + (type bool) + (peephole (postfix " instanceof sc_Pair"))) +*/ +function sc_isPair(p) { + return (p instanceof sc_Pair); +} + +function sc_isPairEqual(p1, p2, comp) { + return (comp(p1.car, p2.car) && comp(p1.cdr, p2.cdr)); +} + +/*** META ((export #t) + (peephole (hole 2 "new sc_Pair(" car ", " cdr ")"))) +*/ +function sc_cons(car, cdr) { + return new sc_Pair(car, cdr); +} + +/*** META ((export cons*)) */ +function sc_consStar() { + var res = arguments[arguments.length - 1]; + for (var i = arguments.length-2; i >= 0; i--) + res = new sc_Pair(arguments[i], res); + return res; +} + +/*** META ((export #t) + (peephole (postfix ".car"))) +*/ +function sc_car(p) { + return p.car; +} + +/*** META ((export #t) + (peephole (postfix ".cdr"))) +*/ +function sc_cdr(p) { + return p.cdr; +} + +/*** META ((export #t) + (peephole (hole 2 p ".car = " val))) +*/ +function sc_setCarBang(p, val) { + p.car = val; +} + +/*** META ((export #t) + (peephole (hole 2 p ".cdr = " val))) +*/ +function sc_setCdrBang(p, val) { + p.cdr = val; +} + +/*** META ((export #t) + (peephole (postfix ".car.car"))) +*/ +function sc_caar(p) { return p.car.car; } +/*** META ((export #t) + (peephole (postfix ".cdr.car"))) +*/ +function sc_cadr(p) { return p.cdr.car; } +/*** META ((export #t) + (peephole (postfix ".car.cdr"))) +*/ +function sc_cdar(p) { return p.car.cdr; } +/*** META ((export #t) + (peephole (postfix ".cdr.cdr"))) +*/ +function sc_cddr(p) { return p.cdr.cdr; } +/*** META ((export #t) + (peephole (postfix ".car.car.car"))) +*/ +function sc_caaar(p) { return p.car.car.car; } +/*** META ((export #t) + (peephole (postfix ".car.cdr.car"))) +*/ +function sc_cadar(p) { return p.car.cdr.car; } +/*** META ((export #t) + (peephole (postfix ".cdr.car.car"))) +*/ +function sc_caadr(p) { return p.cdr.car.car; } +/*** META ((export #t) + (peephole (postfix ".cdr.cdr.car"))) +*/ +function sc_caddr(p) { return p.cdr.cdr.car; } +/*** META ((export #t) + (peephole (postfix ".car.car.cdr"))) +*/ +function sc_cdaar(p) { return p.car.car.cdr; } +/*** META ((export #t) + (peephole (postfix ".cdr.car.cdr"))) +*/ +function sc_cdadr(p) { return p.cdr.car.cdr; } +/*** META ((export #t) + (peephole (postfix ".car.cdr.cdr"))) +*/ +function sc_cddar(p) { return p.car.cdr.cdr; } +/*** META ((export #t) + (peephole (postfix ".cdr.cdr.cdr"))) +*/ +function sc_cdddr(p) { return p.cdr.cdr.cdr; } +/*** META ((export #t) + (peephole (postfix ".car.car.car.car"))) +*/ +function sc_caaaar(p) { return p.car.car.car.car; } +/*** META ((export #t) + (peephole (postfix ".car.cdr.car.car"))) +*/ +function sc_caadar(p) { return p.car.cdr.car.car; } +/*** META ((export #t) + (peephole (postfix ".cdr.car.car.car"))) +*/ +function sc_caaadr(p) { return p.cdr.car.car.car; } +/*** META ((export #t) + (peephole (postfix ".cdr.cdr.car.car"))) +*/ +function sc_caaddr(p) { return p.cdr.cdr.car.car; } +/*** META ((export #t) + (peephole (postfix ".car.car.car.cdr"))) +*/ +function sc_cdaaar(p) { return p.car.car.car.cdr; } +/*** META ((export #t) + (peephole (postfix ".car.cdr.car.cdr"))) +*/ +function sc_cdadar(p) { return p.car.cdr.car.cdr; } +/*** META ((export #t) + (peephole (postfix ".cdr.car.car.cdr"))) +*/ +function sc_cdaadr(p) { return p.cdr.car.car.cdr; } +/*** META ((export #t) + (peephole (postfix ".cdr.cdr.car.cdr"))) +*/ +function sc_cdaddr(p) { return p.cdr.cdr.car.cdr; } +/*** META ((export #t) + (peephole (postfix ".car.car.cdr.car"))) +*/ +function sc_cadaar(p) { return p.car.car.cdr.car; } +/*** META ((export #t) + (peephole (postfix ".car.cdr.cdr.car"))) +*/ +function sc_caddar(p) { return p.car.cdr.cdr.car; } +/*** META ((export #t) + (peephole (postfix ".cdr.car.cdr.car"))) +*/ +function sc_cadadr(p) { return p.cdr.car.cdr.car; } +/*** META ((export #t) + (peephole (postfix ".cdr.cdr.cdr.car"))) +*/ +function sc_cadddr(p) { return p.cdr.cdr.cdr.car; } +/*** META ((export #t) + (peephole (postfix ".car.car.cdr.cdr"))) +*/ +function sc_cddaar(p) { return p.car.car.cdr.cdr; } +/*** META ((export #t) + (peephole (postfix ".car.cdr.cdr.cdr"))) +*/ +function sc_cdddar(p) { return p.car.cdr.cdr.cdr; } +/*** META ((export #t) + (peephole (postfix ".cdr.car.cdr.cdr"))) +*/ +function sc_cddadr(p) { return p.cdr.car.cdr.cdr; } +/*** META ((export #t) + (peephole (postfix ".cdr.cdr.cdr.cdr"))) +*/ +function sc_cddddr(p) { return p.cdr.cdr.cdr.cdr; } + +/*** META ((export #t)) */ +function sc_lastPair(l) { + if (!sc_isPair(l)) sc_error("sc_lastPair: pair expected"); + var res = l; + var cdr = l.cdr; + while (sc_isPair(cdr)) { + res = cdr; + cdr = res.cdr; + } + return res; +} + +/*** META ((export #t) + (type bool) + (peephole (postfix " === null"))) +*/ +function sc_isNull(o) { + return (o === null); +} + +/*** META ((export #t) + (type bool)) +*/ +function sc_isList(o) { + var rabbit; + var turtle; + + var rabbit = o; + var turtle = o; + while (true) { + if (rabbit === null || + (rabbit instanceof sc_Pair && rabbit.cdr === null)) + return true; // end of list + else if ((rabbit instanceof sc_Pair) && + (rabbit.cdr instanceof sc_Pair)) { + rabbit = rabbit.cdr.cdr; + turtle = turtle.cdr; + if (rabbit === turtle) return false; // cycle + } else + return false; // not pair + } +} + +/*** META ((export #t)) */ +function sc_list() { + var res = null; + var a = arguments; + for (var i = a.length-1; i >= 0; i--) + res = new sc_Pair(a[i], res); + return res; +} + +/*** META ((export #t)) */ +function sc_iota(num, init) { + var res = null; + if (!init) init = 0; + for (var i = num - 1; i >= 0; i--) + res = new sc_Pair(i + init, res); + return res; +} + +/*** META ((export #t)) */ +function sc_makeList(nbEls, fill) { + var res = null; + for (var i = 0; i < nbEls; i++) + res = new sc_Pair(fill, res); + return res; +} + +/*** META ((export #t)) */ +function sc_length(l) { + var res = 0; + while (l !== null) { + res++; + l = l.cdr; + } + return res; +} + +/*** META ((export #t)) */ +function sc_remq(o, l) { + var dummy = { cdr : null }; + var tail = dummy; + while (l !== null) { + if (l.car !== o) { + tail.cdr = sc_cons(l.car, null); + tail = tail.cdr; + } + l = l.cdr; + } + return dummy.cdr; +} + +/*** META ((export #t)) */ +function sc_remqBang(o, l) { + var dummy = { cdr : null }; + var tail = dummy; + var needsAssig = true; + while (l !== null) { + if (l.car === o) { + needsAssig = true; + } else { + if (needsAssig) { + tail.cdr = l; + needsAssig = false; + } + tail = l; + } + l = l.cdr; + } + tail.cdr = null; + return dummy.cdr; +} + +/*** META ((export #t)) */ +function sc_delete(o, l) { + var dummy = { cdr : null }; + var tail = dummy; + while (l !== null) { + if (!sc_isEqual(l.car, o)) { + tail.cdr = sc_cons(l.car, null); + tail = tail.cdr; + } + l = l.cdr; + } + return dummy.cdr; +} + +/*** META ((export #t)) */ +function sc_deleteBang(o, l) { + var dummy = { cdr : null }; + var tail = dummy; + var needsAssig = true; + while (l !== null) { + if (sc_isEqual(l.car, o)) { + needsAssig = true; + } else { + if (needsAssig) { + tail.cdr = l; + needsAssig = false; + } + tail = l; + } + l = l.cdr; + } + tail.cdr = null; + return dummy.cdr; +} + +function sc_reverseAppendBang(l1, l2) { + var res = l2; + while (l1 !== null) { + var tmp = res; + res = l1; + l1 = l1.cdr; + res.cdr = tmp; + } + return res; +} + +function sc_dualAppend(l1, l2) { + if (l1 === null) return l2; + if (l2 === null) return l1; + var rev = sc_reverse(l1); + return sc_reverseAppendBang(rev, l2); +} + +/*** META ((export #t)) */ +function sc_append() { + if (arguments.length === 0) + return null; + var res = arguments[arguments.length - 1]; + for (var i = arguments.length - 2; i >= 0; i--) + res = sc_dualAppend(arguments[i], res); + return res; +} + +function sc_dualAppendBang(l1, l2) { + if (l1 === null) return l2; + if (l2 === null) return l1; + var tmp = l1; + while (tmp.cdr !== null) tmp=tmp.cdr; + tmp.cdr = l2; + return l1; +} + +/*** META ((export #t)) */ +function sc_appendBang() { + var res = null; + for (var i = 0; i < arguments.length; i++) + res = sc_dualAppendBang(res, arguments[i]); + return res; +} + +/*** META ((export #t)) */ +function sc_reverse(l1) { + var res = null; + while (l1 !== null) { + res = sc_cons(l1.car, res); + l1 = l1.cdr; + } + return res; +} + +/*** META ((export #t)) */ +function sc_reverseBang(l) { + return sc_reverseAppendBang(l, null); +} + +/*** META ((export #t)) */ +function sc_listTail(l, k) { + var res = l; + for (var i = 0; i < k; i++) { + res = res.cdr; + } + return res; +} + +/*** META ((export #t)) */ +function sc_listRef(l, k) { + return sc_listTail(l, k).car; +} + +/* // unoptimized generic versions +function sc_memX(o, l, comp) { + while (l != null) { + if (comp(l.car, o)) + return l; + l = l.cdr; + } + return false; +} +function sc_memq(o, l) { return sc_memX(o, l, sc_isEq); } +function sc_memv(o, l) { return sc_memX(o, l, sc_isEqv); } +function sc_member(o, l) { return sc_memX(o, l, sc_isEqual); } +*/ + +/* optimized versions */ +/*** META ((export #t)) */ +function sc_memq(o, l) { + while (l !== null) { + if (l.car === o) + return l; + l = l.cdr; + } + return false; +} +/*** META ((export #t)) */ +function sc_memv(o, l) { + while (l !== null) { + if (l.car === o) + return l; + l = l.cdr; + } + return false; +} +/*** META ((export #t)) */ +function sc_member(o, l) { + while (l !== null) { + if (sc_isEqual(l.car,o)) + return l; + l = l.cdr; + } + return false; +} + +/* // generic unoptimized versions +function sc_assX(o, al, comp) { + while (al != null) { + if (comp(al.car.car, o)) + return al.car; + al = al.cdr; + } + return false; +} +function sc_assq(o, al) { return sc_assX(o, al, sc_isEq); } +function sc_assv(o, al) { return sc_assX(o, al, sc_isEqv); } +function sc_assoc(o, al) { return sc_assX(o, al, sc_isEqual); } +*/ +// optimized versions +/*** META ((export #t)) */ +function sc_assq(o, al) { + while (al !== null) { + if (al.car.car === o) + return al.car; + al = al.cdr; + } + return false; +} +/*** META ((export #t)) */ +function sc_assv(o, al) { + while (al !== null) { + if (al.car.car === o) + return al.car; + al = al.cdr; + } + return false; +} +/*** META ((export #t)) */ +function sc_assoc(o, al) { + while (al !== null) { + if (sc_isEqual(al.car.car, o)) + return al.car; + al = al.cdr; + } + return false; +} + +/* can be used for mutable strings and characters */ +function sc_isCharStringEqual(cs1, cs2) { return cs1.val === cs2.val; } +function sc_isCharStringLess(cs1, cs2) { return cs1.val < cs2.val; } +function sc_isCharStringGreater(cs1, cs2) { return cs1.val > cs2.val; } +function sc_isCharStringLessEqual(cs1, cs2) { return cs1.val <= cs2.val; } +function sc_isCharStringGreaterEqual(cs1, cs2) { return cs1.val >= cs2.val; } +function sc_isCharStringCIEqual(cs1, cs2) + { return cs1.val.toLowerCase() === cs2.val.toLowerCase(); } +function sc_isCharStringCILess(cs1, cs2) + { return cs1.val.toLowerCase() < cs2.val.toLowerCase(); } +function sc_isCharStringCIGreater(cs1, cs2) + { return cs1.val.toLowerCase() > cs2.val.toLowerCase(); } +function sc_isCharStringCILessEqual(cs1, cs2) + { return cs1.val.toLowerCase() <= cs2.val.toLowerCase(); } +function sc_isCharStringCIGreaterEqual(cs1, cs2) + { return cs1.val.toLowerCase() >= cs2.val.toLowerCase(); } + + + + +function sc_Char(c) { + var cached = sc_Char.lazy[c]; + if (cached) + return cached; + this.val = c; + sc_Char.lazy[c] = this; + // add return, so FF does not complain. + return undefined; +} +sc_Char.lazy = new Object(); +// thanks to Eric +sc_Char.char2readable = { +// "\000": "#\\null", +// "\007": "#\\bell", +// "\010": "#\\backspace", +// "\011": "#\\tab", +// "\012": "#\\newline", +// "\014": "#\\page", +// "\015": "#\\return", +// "\033": "#\\escape", +// "\040": "#\\space", +// "\177": "#\\delete", + +// /* poeticless names */ +// "\001": "#\\soh", +// "\002": "#\\stx", +// "\003": "#\\etx", +// "\004": "#\\eot", +// "\005": "#\\enq", +// "\006": "#\\ack", + +// "\013": "#\\vt", +// "\016": "#\\so", +// "\017": "#\\si", + +// "\020": "#\\dle", +// "\021": "#\\dc1", +// "\022": "#\\dc2", +// "\023": "#\\dc3", +// "\024": "#\\dc4", +// "\025": "#\\nak", +// "\026": "#\\syn", +// "\027": "#\\etb", + +// "\030": "#\\can", +// "\031": "#\\em", +// "\032": "#\\sub", +// "\033": "#\\esc", +// "\034": "#\\fs", +// "\035": "#\\gs", +// "\036": "#\\rs", +// "\037": "#\\us" +}; + +sc_Char.readable2char = { +// "null": "\000", +// "bell": "\007", +// "backspace": "\010", +// "tab": "\011", +// "newline": "\012", +// "page": "\014", +// "return": "\015", +// "escape": "\033", +// "space": "\040", +// "delete": "\000", +// "soh": "\001", +// "stx": "\002", +// "etx": "\003", +// "eot": "\004", +// "enq": "\005", +// "ack": "\006", +// "bel": "\007", +// "bs": "\010", +// "ht": "\011", +// "nl": "\012", +// "vt": "\013", +// "np": "\014", +// "cr": "\015", +// "so": "\016", +// "si": "\017", +// "dle": "\020", +// "dc1": "\021", +// "dc2": "\022", +// "dc3": "\023", +// "dc4": "\024", +// "nak": "\025", +// "syn": "\026", +// "etb": "\027", +// "can": "\030", +// "em": "\031", +// "sub": "\032", +// "esc": "\033", +// "fs": "\034", +// "gs": "\035", +// "rs": "\036", +// "us": "\037", +// "sp": "\040", +// "del": "\177" +}; + +sc_Char.prototype.toString = function() { + return this.val; +}; +// sc_toDisplayString == toString +sc_Char.prototype.sc_toWriteString = function() { + var entry = sc_Char.char2readable[this.val]; + if (entry) + return entry; + else + return "#\\" + this.val; +}; + +/*** META ((export #t) + (type bool) + (peephole (postfix "instanceof sc_Char"))) +*/ +function sc_isChar(c) { + return (c instanceof sc_Char); +} + +/*** META ((export char=?) + (type bool) + (peephole (hole 2 c1 ".val === " c2 ".val"))) +*/ +var sc_isCharEqual = sc_isCharStringEqual; +/*** META ((export char?) + (type bool) + (peephole (hole 2 c1 ".val > " c2 ".val"))) +*/ +var sc_isCharGreater = sc_isCharStringGreater; +/*** META ((export char<=?) + (type bool) + (peephole (hole 2 c1 ".val <= " c2 ".val"))) +*/ +var sc_isCharLessEqual = sc_isCharStringLessEqual; +/*** META ((export char>=?) + (type bool) + (peephole (hole 2 c1 ".val >= " c2 ".val"))) +*/ +var sc_isCharGreaterEqual = sc_isCharStringGreaterEqual; +/*** META ((export char-ci=?) + (type bool) + (peephole (hole 2 c1 ".val.toLowerCase() === " c2 ".val.toLowerCase()"))) +*/ +var sc_isCharCIEqual = sc_isCharStringCIEqual; +/*** META ((export char-ci?) + (type bool) + (peephole (hole 2 c1 ".val.toLowerCase() > " c2 ".val.toLowerCase()"))) +*/ +var sc_isCharCIGreater = sc_isCharStringCIGreater; +/*** META ((export char-ci<=?) + (type bool) + (peephole (hole 2 c1 ".val.toLowerCase() <= " c2 ".val.toLowerCase()"))) +*/ +var sc_isCharCILessEqual = sc_isCharStringCILessEqual; +/*** META ((export char-ci>=?) + (type bool) + (peephole (hole 2 c1 ".val.toLowerCase() >= " c2 ".val.toLowerCase()"))) +*/ +var sc_isCharCIGreaterEqual = sc_isCharStringCIGreaterEqual; + +var SC_NUMBER_CLASS = "0123456789"; +var SC_WHITESPACE_CLASS = ' \r\n\t\f'; +var SC_LOWER_CLASS = 'abcdefghijklmnopqrstuvwxyz'; +var SC_UPPER_CLASS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + +function sc_isCharOfClass(c, cl) { return (cl.indexOf(c) != -1); } +/*** META ((export #t) + (type bool)) +*/ +function sc_isCharAlphabetic(c) + { return sc_isCharOfClass(c.val, SC_LOWER_CLASS) || + sc_isCharOfClass(c.val, SC_UPPER_CLASS); } +/*** META ((export #t) + (type bool) + (peephole (hole 1 "SC_NUMBER_CLASS.indexOf(" c ".val) != -1"))) +*/ +function sc_isCharNumeric(c) + { return sc_isCharOfClass(c.val, SC_NUMBER_CLASS); } +/*** META ((export #t) + (type bool)) +*/ +function sc_isCharWhitespace(c) { + var tmp = c.val; + return tmp === " " || tmp === "\r" || tmp === "\n" || tmp === "\t" || tmp === "\f"; +} +/*** META ((export #t) + (type bool) + (peephole (hole 1 "SC_UPPER_CLASS.indexOf(" c ".val) != -1"))) +*/ +function sc_isCharUpperCase(c) + { return sc_isCharOfClass(c.val, SC_UPPER_CLASS); } +/*** META ((export #t) + (type bool) + (peephole (hole 1 "SC_LOWER_CLASS.indexOf(" c ".val) != -1"))) +*/ +function sc_isCharLowerCase(c) + { return sc_isCharOfClass(c.val, SC_LOWER_CLASS); } + +/*** META ((export #t) + (peephole (postfix ".val.charCodeAt(0)"))) +*/ +function sc_char2integer(c) + { return c.val.charCodeAt(0); } +/*** META ((export #t) + (peephole (hole 1 "new sc_Char(String.fromCharCode(" n "))"))) +*/ +function sc_integer2char(n) + { return new sc_Char(String.fromCharCode(n)); } + +/*** META ((export #t) + (peephole (hole 1 "new sc_Char(" c ".val.toUpperCase())"))) +*/ +function sc_charUpcase(c) + { return new sc_Char(c.val.toUpperCase()); } +/*** META ((export #t) + (peephole (hole 1 "new sc_Char(" c ".val.toLowerCase())"))) +*/ +function sc_charDowncase(c) + { return new sc_Char(c.val.toLowerCase()); } + +function sc_makeJSStringOfLength(k, c) { + var fill; + if (c === undefined) + fill = " "; + else + fill = c; + var res = ""; + var len = 1; + // every round doubles the size of fill. + while (k >= len) { + if (k & len) + res = res.concat(fill); + fill = fill.concat(fill); + len *= 2; + } + return res; +} + +function sc_makejsString(k, c) { + var fill; + if (c) + fill = c.val; + else + fill = " "; + return sc_makeJSStringOfLength(k, fill); +} + +function sc_jsstring2list(s) { + var res = null; + for (var i = s.length - 1; i >= 0; i--) + res = sc_cons(new sc_Char(s.charAt(i)), res); + return res; +} + +function sc_list2jsstring(l) { + var a = new Array(); + while(l !== null) { + a.push(l.car.val); + l = l.cdr; + } + return "".concat.apply("", a); +} + +var sc_Vector = Array; + +sc_Vector.prototype.sc_toWriteOrDisplayString = function(writeOrDisplay) { + if (this.length === 0) return "#()"; + + var res = "#(" + writeOrDisplay(this[0]); + for (var i = 1; i < this.length; i++) + res += " " + writeOrDisplay(this[i]); + res += ")"; + return res; +}; +sc_Vector.prototype.sc_toDisplayString = function() { + return this.sc_toWriteOrDisplayString(sc_toDisplayString); +}; +sc_Vector.prototype.sc_toWriteString = function() { + return this.sc_toWriteOrDisplayString(sc_toWriteString); +}; + +/*** META ((export vector? array?) + (type bool) + (peephole (postfix " instanceof sc_Vector"))) +*/ +function sc_isVector(v) { + return (v instanceof sc_Vector); +} + +// only applies to vectors +function sc_isVectorEqual(v1, v2, comp) { + if (v1.length !== v2.length) return false; + for (var i = 0; i < v1.length; i++) + if (!comp(v1[i], v2[i])) return false; + return true; +} + +/*** META ((export make-vector make-array)) */ +function sc_makeVector(size, fill) { + var a = new sc_Vector(size); + if (fill !== undefined) + sc_vectorFillBang(a, fill); + return a; +} + +/*** META ((export vector array) + (peephole (vector))) +*/ +function sc_vector() { + var a = new sc_Vector(); + for (var i = 0; i < arguments.length; i++) + a.push(arguments[i]); + return a; +} + +/*** META ((export vector-length array-length) + (peephole (postfix ".length"))) +*/ +function sc_vectorLength(v) { + return v.length; +} + +/*** META ((export vector-ref array-ref) + (peephole (hole 2 v "[" pos "]"))) +*/ +function sc_vectorRef(v, pos) { + return v[pos]; +} + +/*** META ((export vector-set! array-set!) + (peephole (hole 3 v "[" pos "] = " val))) +*/ +function sc_vectorSetBang(v, pos, val) { + v[pos] = val; +} + +/*** META ((export vector->list array->list)) */ +function sc_vector2list(a) { + var res = null; + for (var i = a.length-1; i >= 0; i--) + res = sc_cons(a[i], res); + return res; +} + +/*** META ((export list->vector list->array)) */ +function sc_list2vector(l) { + var a = new sc_Vector(); + while(l !== null) { + a.push(l.car); + l = l.cdr; + } + return a; +} + +/*** META ((export vector-fill! array-fill!)) */ +function sc_vectorFillBang(a, fill) { + for (var i = 0; i < a.length; i++) + a[i] = fill; +} + + +/*** META ((export #t)) */ +function sc_copyVector(a, len) { + if (len <= a.length) + return a.slice(0, len); + else { + var tmp = a.concat(); + tmp.length = len; + return tmp; + } +} + +/*** META ((export #t) + (peephole (hole 3 a ".slice(" start "," end ")"))) +*/ +function sc_vectorCopy(a, start, end) { + return a.slice(start, end); +} + +/*** META ((export #t)) */ +function sc_vectorCopyBang(target, tstart, source, sstart, send) { + if (!sstart) sstart = 0; + if (!send) send = source.length; + + // if target == source we don't want to overwrite not yet copied elements. + if (tstart <= sstart) { + for (var i = tstart, j = sstart; j < send; i++, j++) { + target[i] = source[j]; + } + } else { + var diff = send - sstart; + for (var i = tstart + diff - 1, j = send - 1; + j >= sstart; + i--, j--) { + target[i] = source[j]; + } + } + return target; +} + +/*** META ((export #t) + (type bool) + (peephole (hole 1 "typeof " o " === 'function'"))) +*/ +function sc_isProcedure(o) { + return (typeof o === "function"); +} + +/*** META ((export #t)) */ +function sc_apply(proc) { + var args = new Array(); + // first part of arguments are not in list-form. + for (var i = 1; i < arguments.length - 1; i++) + args.push(arguments[i]); + var l = arguments[arguments.length - 1]; + while (l !== null) { + args.push(l.car); + l = l.cdr; + } + return proc.apply(null, args); +} + +/*** META ((export #t)) */ +function sc_map(proc, l1) { + if (l1 === undefined) + return null; + // else + var nbApplyArgs = arguments.length - 1; + var applyArgs = new Array(nbApplyArgs); + var revres = null; + while (l1 !== null) { + for (var i = 0; i < nbApplyArgs; i++) { + applyArgs[i] = arguments[i + 1].car; + arguments[i + 1] = arguments[i + 1].cdr; + } + revres = sc_cons(proc.apply(null, applyArgs), revres); + } + return sc_reverseAppendBang(revres, null); +} + +/*** META ((export #t)) */ +function sc_mapBang(proc, l1) { + if (l1 === undefined) + return null; + // else + var l1_orig = l1; + var nbApplyArgs = arguments.length - 1; + var applyArgs = new Array(nbApplyArgs); + while (l1 !== null) { + var tmp = l1; + for (var i = 0; i < nbApplyArgs; i++) { + applyArgs[i] = arguments[i + 1].car; + arguments[i + 1] = arguments[i + 1].cdr; + } + tmp.car = proc.apply(null, applyArgs); + } + return l1_orig; +} + +/*** META ((export #t)) */ +function sc_forEach(proc, l1) { + if (l1 === undefined) + return undefined; + // else + var nbApplyArgs = arguments.length - 1; + var applyArgs = new Array(nbApplyArgs); + while (l1 !== null) { + for (var i = 0; i < nbApplyArgs; i++) { + applyArgs[i] = arguments[i + 1].car; + arguments[i + 1] = arguments[i + 1].cdr; + } + proc.apply(null, applyArgs); + } + // add return so FF does not complain. + return undefined; +} + +/*** META ((export #t)) */ +function sc_filter(proc, l1) { + var dummy = { cdr : null }; + var tail = dummy; + while (l1 !== null) { + if (proc(l1.car) !== false) { + tail.cdr = sc_cons(l1.car, null); + tail = tail.cdr; + } + l1 = l1.cdr; + } + return dummy.cdr; +} + +/*** META ((export #t)) */ +function sc_filterBang(proc, l1) { + var head = sc_cons("dummy", l1); + var it = head; + var next = l1; + while (next !== null) { + if (proc(next.car) !== false) { + it.cdr = next + it = next; + } + next = next.cdr; + } + it.cdr = null; + return head.cdr; +} + +function sc_filterMap1(proc, l1) { + var revres = null; + while (l1 !== null) { + var tmp = proc(l1.car) + if (tmp !== false) revres = sc_cons(tmp, revres); + l1 = l1.cdr; + } + return sc_reverseAppendBang(revres, null); +} +function sc_filterMap2(proc, l1, l2) { + var revres = null; + while (l1 !== null) { + var tmp = proc(l1.car, l2.car); + if(tmp !== false) revres = sc_cons(tmp, revres); + l1 = l1.cdr; + l2 = l2.cdr + } + return sc_reverseAppendBang(revres, null); +} + +/*** META ((export #t)) */ +function sc_filterMap(proc, l1, l2, l3) { + if (l2 === undefined) + return sc_filterMap1(proc, l1); + else if (l3 === undefined) + return sc_filterMap2(proc, l1, l2); + // else + var nbApplyArgs = arguments.length - 1; + var applyArgs = new Array(nbApplyArgs); + var revres = null; + while (l1 !== null) { + for (var i = 0; i < nbApplyArgs; i++) { + applyArgs[i] = arguments[i + 1].car; + arguments[i + 1] = arguments[i + 1].cdr; + } + var tmp = proc.apply(null, applyArgs); + if(tmp !== false) revres = sc_cons(tmp, revres); + } + return sc_reverseAppendBang(revres, null); +} + +/*** META ((export #t)) */ +function sc_any(proc, l) { + var revres = null; + while (l !== null) { + var tmp = proc(l.car); + if(tmp !== false) return tmp; + l = l.cdr; + } + return false; +} + +/*** META ((export any?) + (peephole (hole 2 "sc_any(" proc "," l ") !== false"))) +*/ +function sc_anyPred(proc, l) { + return sc_any(proc, l)!== false; +} + +/*** META ((export #t)) */ +function sc_every(proc, l) { + var revres = null; + var tmp = true; + while (l !== null) { + tmp = proc(l.car); + if (tmp === false) return false; + l = l.cdr; + } + return tmp; +} + +/*** META ((export every?) + (peephole (hole 2 "sc_every(" proc "," l ") !== false"))) +*/ +function sc_everyPred(proc, l) { + var tmp = sc_every(proc, l); + if (tmp !== false) return true; + return false; +} + +/*** META ((export #t) + (peephole (postfix "()"))) +*/ +function sc_force(o) { + return o(); +} + +/*** META ((export #t)) */ +function sc_makePromise(proc) { + var isResultReady = false; + var result = undefined; + return function() { + if (!isResultReady) { + var tmp = proc(); + if (!isResultReady) { + isResultReady = true; + result = tmp; + } + } + return result; + }; +} + +function sc_Values(values) { + this.values = values; +} + +/*** META ((export #t) + (peephole (values))) +*/ +function sc_values() { + if (arguments.length === 1) + return arguments[0]; + else + return new sc_Values(arguments); +} + +/*** META ((export #t)) */ +function sc_callWithValues(producer, consumer) { + var produced = producer(); + if (produced instanceof sc_Values) + return consumer.apply(null, produced.values); + else + return consumer(produced); +} + +/*** META ((export #t)) */ +function sc_dynamicWind(before, thunk, after) { + before(); + try { + var res = thunk(); + return res; + } finally { + after(); + } +} + + +// TODO: eval/scheme-report-environment/null-environment/interaction-environment + +// LIMITATION: 'load' doesn't exist without files. +// LIMITATION: transcript-on/transcript-off doesn't exist without files. + + +function sc_Struct(name) { + this.name = name; +} +sc_Struct.prototype.sc_toDisplayString = function() { + return "#"; +}; +sc_Struct.prototype.sc_toWriteString = sc_Struct.prototype.sc_toDisplayString; + +/*** META ((export #t) + (peephole (hole 1 "new sc_Struct(" name ")"))) +*/ +function sc_makeStruct(name) { + return new sc_Struct(name); +} + +/*** META ((export #t) + (type bool) + (peephole (postfix " instanceof sc_Struct"))) +*/ +function sc_isStruct(o) { + return (o instanceof sc_Struct); +} + +/*** META ((export #t) + (type bool) + (peephole (hole 2 "(" 1 " instanceof sc_Struct) && ( " 1 ".name === " 0 ")"))) +*/ +function sc_isStructNamed(name, s) { + return ((s instanceof sc_Struct) && (s.name === name)); +} + +/*** META ((export struct-field) + (peephole (hole 3 0 "[" 2 "]"))) +*/ +function sc_getStructField(s, name, field) { + return s[field]; +} + +/*** META ((export struct-field-set!) + (peephole (hole 4 0 "[" 2 "] = " 3))) +*/ +function sc_setStructFieldBang(s, name, field, val) { + s[field] = val; +} + +/*** META ((export #t) + (peephole (prefix "~"))) +*/ +function sc_bitNot(x) { + return ~x; +} + +/*** META ((export #t) + (peephole (infix 2 2 "&"))) +*/ +function sc_bitAnd(x, y) { + return x & y; +} + +/*** META ((export #t) + (peephole (infix 2 2 "|"))) +*/ +function sc_bitOr(x, y) { + return x | y; +} + +/*** META ((export #t) + (peephole (infix 2 2 "^"))) +*/ +function sc_bitXor(x, y) { + return x ^ y; +} + +/*** META ((export #t) + (peephole (infix 2 2 "<<"))) +*/ +function sc_bitLsh(x, y) { + return x << y; +} + +/*** META ((export #t) + (peephole (infix 2 2 ">>"))) +*/ +function sc_bitRsh(x, y) { + return x >> y; +} + +/*** META ((export #t) + (peephole (infix 2 2 ">>>"))) +*/ +function sc_bitUrsh(x, y) { + return x >>> y; +} + +/*** META ((export js-field js-property) + (peephole (hole 2 o "[" field "]"))) +*/ +function sc_jsField(o, field) { + return o[field]; +} + +/*** META ((export js-field-set! js-property-set!) + (peephole (hole 3 o "[" field "] = " val))) +*/ +function sc_setJsFieldBang(o, field, val) { + return o[field] = val; +} + +/*** META ((export js-field-delete! js-property-delete!) + (peephole (hole 2 "delete" o "[" field "]"))) +*/ +function sc_deleteJsFieldBang(o, field) { + delete o[field]; +} + +/*** META ((export #t) + (peephole (jsCall))) +*/ +function sc_jsCall(o, fun) { + var args = new Array(); + for (var i = 2; i < arguments.length; i++) + args[i-2] = arguments[i]; + return fun.apply(o, args); +} + +/*** META ((export #t) + (peephole (jsMethodCall))) +*/ +function sc_jsMethodCall(o, field) { + var args = new Array(); + for (var i = 2; i < arguments.length; i++) + args[i-2] = arguments[i]; + return o[field].apply(o, args); +} + +/*** META ((export new js-new) + (peephole (jsNew))) +*/ +function sc_jsNew(c) { + var evalStr = "new c("; + evalStr +=arguments.length > 1? "arguments[1]": ""; + for (var i = 2; i < arguments.length; i++) + evalStr += ", arguments[" + i + "]"; + evalStr +=")"; + return eval(evalStr); +} + +// ======================== RegExp ==================== +/*** META ((export #t)) */ +function sc_pregexp(re) { + return new RegExp(sc_string2jsstring(re)); +} + +/*** META ((export #t)) */ +function sc_pregexpMatch(re, s) { + var reg = (re instanceof RegExp) ? re : sc_pregexp(re); + var tmp = reg.exec(sc_string2jsstring(s)); + + if (tmp == null) return false; + + var res = null; + for (var i = tmp.length-1; i >= 0; i--) { + if (tmp[i] !== null) { + res = sc_cons(sc_jsstring2string(tmp[i]), res); + } else { + res = sc_cons(false, res); + } + } + return res; +} + +/*** META ((export #t)) */ +function sc_pregexpReplace(re, s1, s2) { + var reg; + var jss1 = sc_string2jsstring(s1); + var jss2 = sc_string2jsstring(s2); + + if (re instanceof RegExp) { + if (re.global) + reg = re; + else + reg = new RegExp(re.source); + } else { + reg = new RegExp(sc_string2jsstring(re)); + } + + return jss1.replace(reg, jss2); +} + +/*** META ((export pregexp-replace*)) */ +function sc_pregexpReplaceAll(re, s1, s2) { + var reg; + var jss1 = sc_string2jsstring(s1); + var jss2 = sc_string2jsstring(s2); + + if (re instanceof RegExp) { + if (re.global) + reg = re; + else + reg = new RegExp(re.source, "g"); + } else { + reg = new RegExp(sc_string2jsstring(re), "g"); + } + + return jss1.replace(reg, jss2); +} + +/*** META ((export #t)) */ +function sc_pregexpSplit(re, s) { + var reg = ((re instanceof RegExp) ? + re : + new RegExp(sc_string2jsstring(re))); + var jss = sc_string2jsstring(s); + var tmp = jss.split(reg); + + if (tmp == null) return false; + + return sc_vector2list(tmp); +} + + +/* =========================================================================== */ +/* Other library stuff */ +/* =========================================================================== */ + +/*** META ((export #t) + (peephole (hole 1 "Math.floor(Math.random()*" 'n ")"))) +*/ +function sc_random(n) { + return Math.floor(Math.random()*n); +} + +/*** META ((export current-date) + (peephole (hole 0 "new Date()"))) +*/ +function sc_currentDate() { + return new Date(); +} + +function sc_Hashtable() { +} +sc_Hashtable.prototype.toString = function() { + return "#{%hashtable}"; +}; +// sc_toWriteString == sc_toDisplayString == toString + +function sc_HashtableElement(key, val) { + this.key = key; + this.val = val; +} + +/*** META ((export #t) + (peephole (hole 0 "new sc_Hashtable()"))) +*/ +function sc_makeHashtable() { + return new sc_Hashtable(); +} + +/*** META ((export #t)) */ +function sc_hashtablePutBang(ht, key, val) { + var hash = sc_hash(key); + ht[hash] = new sc_HashtableElement(key, val); +} + +/*** META ((export #t)) */ +function sc_hashtableGet(ht, key) { + var hash = sc_hash(key); + if (hash in ht) + return ht[hash].val; + else + return false; +} + +/*** META ((export #t)) */ +function sc_hashtableForEach(ht, f) { + for (var v in ht) { + if (ht[v] instanceof sc_HashtableElement) + f(ht[v].key, ht[v].val); + } +} + +/*** META ((export hashtable-contains?) + (peephole (hole 2 "sc_hash(" 1 ") in " 0))) +*/ +function sc_hashtableContains(ht, key) { + var hash = sc_hash(key); + if (hash in ht) + return true; + else + return false; +} + +var SC_HASH_COUNTER = 0; + +function sc_hash(o) { + if (o === null) + return "null"; + else if (o === undefined) + return "undefined"; + else if (o === true) + return "true"; + else if (o === false) + return "false"; + else if (typeof o === "number") + return "num-" + o; + else if (typeof o === "string") + return "jsstr-" + o; + else if (o.sc_getHash) + return o.sc_getHash(); + else + return sc_counterHash.call(o); +} +function sc_counterHash() { + if (!this.sc_hash) { + this.sc_hash = "hash-" + SC_HASH_COUNTER; + SC_HASH_COUNTER++; + } + return this.sc_hash; +} + +function sc_Trampoline(args, maxTailCalls) { + this['__trampoline return__'] = true; + this.args = args; + this.MAX_TAIL_CALLs = maxTailCalls; +} +// TODO: call/cc stuff +sc_Trampoline.prototype.restart = function() { + var o = this; + while (true) { + // set both globals. + SC_TAIL_OBJECT.calls = o.MAX_TAIL_CALLs-1; + var fun = o.args.callee; + var res = fun.apply(SC_TAIL_OBJECT, o.args); + if (res instanceof sc_Trampoline) + o = res; + else + return res; + } +} + +/*** META ((export bind-exit-lambda)) */ +function sc_bindExitLambda(proc) { + var escape_obj = new sc_BindExitException(); + var escape = function(res) { + escape_obj.res = res; + throw escape_obj; + }; + try { + return proc(escape); + } catch(e) { + if (e === escape_obj) { + return e.res; + } + throw e; + } +} +function sc_BindExitException() { + this._internalException = true; +} + +var SC_SCM2JS_GLOBALS = new Object(); + +// default tail-call depth. +// normally the program should set it again. but just in case... +var SC_TAIL_OBJECT = new Object(); +SC_SCM2JS_GLOBALS.TAIL_OBJECT = SC_TAIL_OBJECT; +// ======================== I/O ======================= + +/*------------------------------------------------------------------*/ + +function sc_EOF() { +} +var SC_EOF_OBJECT = new sc_EOF(); + +function sc_Port() { +} + +/* --------------- Input ports -------------------------------------*/ + +function sc_InputPort() { +} +sc_InputPort.prototype = new sc_Port(); + +sc_InputPort.prototype.peekChar = function() { + if (!("peeked" in this)) + this.peeked = this.getNextChar(); + return this.peeked; +} +sc_InputPort.prototype.readChar = function() { + var tmp = this.peekChar(); + delete this.peeked; + return tmp; +} +sc_InputPort.prototype.isCharReady = function() { + return true; +} +sc_InputPort.prototype.close = function() { + // do nothing +} + +/* .............. String port ..........................*/ +function sc_ErrorInputPort() { +}; +sc_ErrorInputPort.prototype = new sc_InputPort(); +sc_ErrorInputPort.prototype.getNextChar = function() { + throw "can't read from error-port."; +}; +sc_ErrorInputPort.prototype.isCharReady = function() { + return false; +}; + + +/* .............. String port ..........................*/ + +function sc_StringInputPort(jsStr) { + // we are going to do some charAts on the str. + // instead of recreating all the time a String-object, we + // create one in the beginning. (not sure, if this is really an optim) + this.str = new String(jsStr); + this.pos = 0; +} +sc_StringInputPort.prototype = new sc_InputPort(); +sc_StringInputPort.prototype.getNextChar = function() { + if (this.pos >= this.str.length) + return SC_EOF_OBJECT; + return this.str.charAt(this.pos++); +}; + +/* ------------- Read and other lib-funs -------------------------------*/ +function sc_Token(type, val, pos) { + this.type = type; + this.val = val; + this.pos = pos; +} +sc_Token.EOF = 0/*EOF*/; +sc_Token.OPEN_PAR = 1/*OPEN_PAR*/; +sc_Token.CLOSE_PAR = 2/*CLOSE_PAR*/; +sc_Token.OPEN_BRACE = 3/*OPEN_BRACE*/; +sc_Token.CLOSE_BRACE = 4/*CLOSE_BRACE*/; +sc_Token.OPEN_BRACKET = 5/*OPEN_BRACKET*/; +sc_Token.CLOSE_BRACKET = 6/*CLOSE_BRACKET*/; +sc_Token.WHITESPACE = 7/*WHITESPACE*/; +sc_Token.QUOTE = 8/*QUOTE*/; +sc_Token.ID = 9/*ID*/; +sc_Token.DOT = 10/*DOT*/; +sc_Token.STRING = 11/*STRING*/; +sc_Token.NUMBER = 12/*NUMBER*/; +sc_Token.ERROR = 13/*ERROR*/; +sc_Token.VECTOR_BEGIN = 14/*VECTOR_BEGIN*/; +sc_Token.TRUE = 15/*TRUE*/; +sc_Token.FALSE = 16/*FALSE*/; +sc_Token.UNSPECIFIED = 17/*UNSPECIFIED*/; +sc_Token.REFERENCE = 18/*REFERENCE*/; +sc_Token.STORE = 19/*STORE*/; +sc_Token.CHAR = 20/*CHAR*/; + +var SC_ID_CLASS = SC_LOWER_CLASS + SC_UPPER_CLASS + "!$%*+-./:<=>?@^_~"; +function sc_Tokenizer(port) { + this.port = port; +} +sc_Tokenizer.prototype.peekToken = function() { + if (this.peeked) + return this.peeked; + var newToken = this.nextToken(); + this.peeked = newToken; + return newToken; +}; +sc_Tokenizer.prototype.readToken = function() { + var tmp = this.peekToken(); + delete this.peeked; + return tmp; +}; +sc_Tokenizer.prototype.nextToken = function() { + var port = this.port; + + function isNumberChar(c) { + return (c >= "0" && c <= "9"); + }; + function isIdOrNumberChar(c) { + return SC_ID_CLASS.indexOf(c) != -1 || // ID-char + (c >= "0" && c <= "9"); + } + function isWhitespace(c) { + return c === " " || c === "\r" || c === "\n" || c === "\t" || c === "\f"; + }; + function isWhitespaceOrEOF(c) { + return isWhitespace(c) || c === SC_EOF_OBJECT; + }; + + function readString() { + res = ""; + while (true) { + var c = port.readChar(); + switch (c) { + case '"': + return new sc_Token(11/*STRING*/, res); + case "\\": + var tmp = port.readChar(); + switch (tmp) { + case '0': res += "\0"; break; + case 'a': res += "\a"; break; + case 'b': res += "\b"; break; + case 'f': res += "\f"; break; + case 'n': res += "\n"; break; + case 'r': res += "\r"; break; + case 't': res += "\t"; break; + case 'v': res += "\v"; break; + case '"': res += '"'; break; + case '\\': res += '\\'; break; + case 'x': + /* hexa-number */ + var nb = 0; + while (true) { + var hexC = port.peekChar(); + if (hexC >= '0' && hexC <= '9') { + port.readChar(); + nb = nb * 16 + hexC.charCodeAt(0) - '0'.charCodeAt(0); + } else if (hexC >= 'a' && hexC <= 'f') { + port.readChar(); + nb = nb * 16 + hexC.charCodeAt(0) - 'a'.charCodeAt(0); + } else if (hexC >= 'A' && hexC <= 'F') { + port.readChar(); + nb = nb * 16 + hexC.charCodeAt(0) - 'A'.charCodeAt(0); + } else { + // next char isn't part of hex. + res += String.fromCharCode(nb); + break; + } + } + break; + default: + if (tmp === SC_EOF_OBJECT) { + return new sc_Token(13/*ERROR*/, "unclosed string-literal" + res); + } + res += tmp; + } + break; + default: + if (c === SC_EOF_OBJECT) { + return new sc_Token(13/*ERROR*/, "unclosed string-literal" + res); + } + res += c; + } + } + }; + function readIdOrNumber(firstChar) { + var res = firstChar; + while (isIdOrNumberChar(port.peekChar())) + res += port.readChar(); + if (isNaN(res)) + return new sc_Token(9/*ID*/, res); + else + return new sc_Token(12/*NUMBER*/, res - 0); + }; + + function skipWhitespaceAndComments() { + var done = false; + while (!done) { + done = true; + while (isWhitespace(port.peekChar())) + port.readChar(); + if (port.peekChar() === ';') { + port.readChar(); + done = false; + while (true) { + curChar = port.readChar(); + if (curChar === SC_EOF_OBJECT || + curChar === '\n') + break; + } + } + } + }; + + function readDot() { + if (isWhitespace(port.peekChar())) + return new sc_Token(10/*DOT*/); + else + return readIdOrNumber("."); + }; + + function readSharp() { + var c = port.readChar(); + if (isWhitespace(c)) + return new sc_Token(13/*ERROR*/, "bad #-pattern0."); + + // reference + if (isNumberChar(c)) { + var nb = c - 0; + while (isNumberChar(port.peekChar())) + nb = nb*10 + (port.readChar() - 0); + switch (port.readChar()) { + case '#': + return new sc_Token(18/*REFERENCE*/, nb); + case '=': + return new sc_Token(19/*STORE*/, nb); + default: + return new sc_Token(13/*ERROR*/, "bad #-pattern1." + nb); + } + } + + if (c === "(") + return new sc_Token(14/*VECTOR_BEGIN*/); + + if (c === "\\") { // character + var tmp = "" + while (!isWhitespaceOrEOF(port.peekChar())) + tmp += port.readChar(); + switch (tmp.length) { + case 0: // it's escaping a whitespace char: + if (sc_isEOFObject(port.peekChar)) + return new sc_Token(13/*ERROR*/, "bad #-pattern2."); + else + return new sc_Token(20/*CHAR*/, port.readChar()); + case 1: + return new sc_Token(20/*CHAR*/, tmp); + default: + var entry = sc_Char.readable2char[tmp.toLowerCase()]; + if (entry) + return new sc_Token(20/*CHAR*/, entry); + else + return new sc_Token(13/*ERROR*/, "unknown character description: #\\" + tmp); + } + } + + // some constants (#t, #f, #unspecified) + var res; + var needing; + switch (c) { + case 't': res = new sc_Token(15/*TRUE*/, true); needing = ""; break; + case 'f': res = new sc_Token(16/*FALSE*/, false); needing = ""; break; + case 'u': res = new sc_Token(17/*UNSPECIFIED*/, undefined); needing = "nspecified"; break; + default: + return new sc_Token(13/*ERROR*/, "bad #-pattern3: " + c); + } + while(true) { + c = port.peekChar(); + if ((isWhitespaceOrEOF(c) || c === ')') && + needing == "") + return res; + else if (isWhitespace(c) || needing == "") + return new sc_Token(13/*ERROR*/, "bad #-pattern4 " + c + " " + needing); + else if (needing.charAt(0) == c) { + port.readChar(); // consume + needing = needing.slice(1); + } else + return new sc_Token(13/*ERROR*/, "bad #-pattern5"); + } + + }; + + skipWhitespaceAndComments(); + var curChar = port.readChar(); + if (curChar === SC_EOF_OBJECT) + return new sc_Token(0/*EOF*/, curChar); + switch (curChar) + { + case " ": + case "\n": + case "\t": + return readWhitespace(); + case "(": + return new sc_Token(1/*OPEN_PAR*/); + case ")": + return new sc_Token(2/*CLOSE_PAR*/); + case "{": + return new sc_Token(3/*OPEN_BRACE*/); + case "}": + return new sc_Token(4/*CLOSE_BRACE*/); + case "[": + return new sc_Token(5/*OPEN_BRACKET*/); + case "]": + return new sc_Token(6/*CLOSE_BRACKET*/); + case "'": + return new sc_Token(8/*QUOTE*/); + case "#": + return readSharp(); + case ".": + return readDot(); + case '"': + return readString(); + default: + if (isIdOrNumberChar(curChar)) + return readIdOrNumber(curChar); + throw "unexpected character: " + curChar; + } +}; + +function sc_Reader(tokenizer) { + this.tokenizer = tokenizer; + this.backref = new Array(); +} +sc_Reader.prototype.read = function() { + function readList(listBeginType) { + function matchesPeer(open, close) { + return open === 1/*OPEN_PAR*/ && close === 2/*CLOSE_PAR*/ + || open === 3/*OPEN_BRACE*/ && close === 4/*CLOSE_BRACE*/ + || open === 5/*OPEN_BRACKET*/ && close === 6/*CLOSE_BRACKET*/; + }; + var res = null; + + while (true) { + var token = tokenizer.peekToken(); + + switch (token.type) { + case 2/*CLOSE_PAR*/: + case 4/*CLOSE_BRACE*/: + case 6/*CLOSE_BRACKET*/: + if (matchesPeer(listBeginType, token.type)) { + tokenizer.readToken(); // consume token + return sc_reverseBang(res); + } else + throw "closing par doesn't match: " + listBeginType + + " " + listEndType; + + case 0/*EOF*/: + throw "unexpected end of file"; + + case 10/*DOT*/: + tokenizer.readToken(); // consume token + var cdr = this.read(); + var par = tokenizer.readToken(); + if (!matchesPeer(listBeginType, par.type)) + throw "closing par doesn't match: " + listBeginType + + " " + par.type; + else + return sc_reverseAppendBang(res, cdr); + + + default: + res = sc_cons(this.read(), res); + } + } + }; + function readQuote() { + return sc_cons("quote", sc_cons(this.read(), null)); + }; + function readVector() { + // opening-parenthesis is already consumed + var a = new Array(); + while (true) { + var token = tokenizer.peekToken(); + switch (token.type) { + case 2/*CLOSE_PAR*/: + tokenizer.readToken(); + return a; + + default: + a.push(this.read()); + } + } + }; + + function storeRefence(nb) { + var tmp = this.read(); + this.backref[nb] = tmp; + return tmp; + }; + + function readReference(nb) { + if (nb in this.backref) + return this.backref[nb]; + else + throw "bad reference: " + nb; + }; + + var tokenizer = this.tokenizer; + + var token = tokenizer.readToken(); + + // handle error + if (token.type === 13/*ERROR*/) + throw token.val; + + switch (token.type) { + case 1/*OPEN_PAR*/: + case 3/*OPEN_BRACE*/: + case 5/*OPEN_BRACKET*/: + return readList.call(this, token.type); + case 8/*QUOTE*/: + return readQuote.call(this); + case 11/*STRING*/: + return sc_jsstring2string(token.val); + case 20/*CHAR*/: + return new sc_Char(token.val); + case 14/*VECTOR_BEGIN*/: + return readVector.call(this); + case 18/*REFERENCE*/: + return readReference.call(this, token.val); + case 19/*STORE*/: + return storeRefence.call(this, token.val); + case 9/*ID*/: + return sc_jsstring2symbol(token.val); + case 0/*EOF*/: + case 12/*NUMBER*/: + case 15/*TRUE*/: + case 16/*FALSE*/: + case 17/*UNSPECIFIED*/: + return token.val; + default: + throw "unexpected token " + token.type + " " + token.val; + } +}; + +/*** META ((export #t)) */ +function sc_read(port) { + if (port === undefined) // we assume the port hasn't been given. + port = SC_DEFAULT_IN; // THREAD: shared var... + var reader = new sc_Reader(new sc_Tokenizer(port)); + return reader.read(); +} +/*** META ((export #t)) */ +function sc_readChar(port) { + if (port === undefined) // we assume the port hasn't been given. + port = SC_DEFAULT_IN; // THREAD: shared var... + var t = port.readChar(); + return t === SC_EOF_OBJECT? t: new sc_Char(t); +} +/*** META ((export #t)) */ +function sc_peekChar(port) { + if (port === undefined) // we assume the port hasn't been given. + port = SC_DEFAULT_IN; // THREAD: shared var... + var t = port.peekChar(); + return t === SC_EOF_OBJECT? t: new sc_Char(t); +} +/*** META ((export #t) + (type bool)) +*/ +function sc_isCharReady(port) { + if (port === undefined) // we assume the port hasn't been given. + port = SC_DEFAULT_IN; // THREAD: shared var... + return port.isCharReady(); +} +/*** META ((export #t) + (peephole (postfix ".close()"))) +*/ +function sc_closeInputPort(p) { + return p.close(); +} + +/*** META ((export #t) + (type bool) + (peephole (postfix " instanceof sc_InputPort"))) +*/ +function sc_isInputPort(o) { + return (o instanceof sc_InputPort); +} + +/*** META ((export eof-object?) + (type bool) + (peephole (postfix " === SC_EOF_OBJECT"))) +*/ +function sc_isEOFObject(o) { + return o === SC_EOF_OBJECT; +} + +/*** META ((export #t) + (peephole (hole 0 "SC_DEFAULT_IN"))) +*/ +function sc_currentInputPort() { + return SC_DEFAULT_IN; +} + +/* ------------ file operations are not supported -----------*/ +/*** META ((export #t)) */ +function sc_callWithInputFile(s, proc) { + throw "can't open " + s; +} + +/*** META ((export #t)) */ +function sc_callWithOutputFile(s, proc) { + throw "can't open " + s; +} + +/*** META ((export #t)) */ +function sc_withInputFromFile(s, thunk) { + throw "can't open " + s; +} + +/*** META ((export #t)) */ +function sc_withOutputToFile(s, thunk) { + throw "can't open " + s; +} + +/*** META ((export #t)) */ +function sc_openInputFile(s) { + throw "can't open " + s; +} + +/*** META ((export #t)) */ +function sc_openOutputFile(s) { + throw "can't open " + s; +} + +/* ----------------------------------------------------------------------------*/ +/*** META ((export #t)) */ +function sc_basename(p) { + var i = p.lastIndexOf('/'); + + if(i >= 0) + return p.substring(i + 1, p.length); + else + return ''; +} + +/*** META ((export #t)) */ +function sc_dirname(p) { + var i = p.lastIndexOf('/'); + + if(i >= 0) + return p.substring(0, i); + else + return ''; +} + +/* ----------------------------------------------------------------------------*/ + +/*** META ((export #t)) */ +function sc_withInputFromPort(p, thunk) { + try { + var tmp = SC_DEFAULT_IN; // THREAD: shared var. + SC_DEFAULT_IN = p; + return thunk(); + } finally { + SC_DEFAULT_IN = tmp; + } +} + +/*** META ((export #t)) */ +function sc_withInputFromString(s, thunk) { + return sc_withInputFromPort(new sc_StringInputPort(sc_string2jsstring(s)), thunk); +} + +/*** META ((export #t)) */ +function sc_withOutputToPort(p, thunk) { + try { + var tmp = SC_DEFAULT_OUT; // THREAD: shared var. + SC_DEFAULT_OUT = p; + return thunk(); + } finally { + SC_DEFAULT_OUT = tmp; + } +} + +/*** META ((export #t)) */ +function sc_withOutputToString(thunk) { + var p = new sc_StringOutputPort(); + sc_withOutputToPort(p, thunk); + return p.close(); +} + +/*** META ((export #t)) */ +function sc_withOutputToProcedure(proc, thunk) { + var t = function(s) { proc(sc_jsstring2string(s)); }; + return sc_withOutputToPort(new sc_GenericOutputPort(t), thunk); +} + +/*** META ((export #t) + (peephole (hole 0 "new sc_StringOutputPort()"))) +*/ +function sc_openOutputString() { + return new sc_StringOutputPort(); +} + +/*** META ((export #t)) */ +function sc_openInputString(str) { + return new sc_StringInputPort(sc_string2jsstring(str)); +} + +/* ----------------------------------------------------------------------------*/ + +function sc_OutputPort() { +} +sc_OutputPort.prototype = new sc_Port(); +sc_OutputPort.prototype.appendJSString = function(obj) { + /* do nothing */ +} +sc_OutputPort.prototype.close = function() { + /* do nothing */ +} + +function sc_StringOutputPort() { + this.res = ""; +} +sc_StringOutputPort.prototype = new sc_OutputPort(); +sc_StringOutputPort.prototype.appendJSString = function(s) { + this.res += s; +} +sc_StringOutputPort.prototype.close = function() { + return sc_jsstring2string(this.res); +} + +/*** META ((export #t)) */ +function sc_getOutputString(sp) { + return sc_jsstring2string(sp.res); +} + + +function sc_ErrorOutputPort() { +} +sc_ErrorOutputPort.prototype = new sc_OutputPort(); +sc_ErrorOutputPort.prototype.appendJSString = function(s) { + throw "don't write on ErrorPort!"; +} +sc_ErrorOutputPort.prototype.close = function() { + /* do nothing */ +} + +function sc_GenericOutputPort(appendJSString, close) { + this.appendJSString = appendJSString; + if (close) + this.close = close; +} +sc_GenericOutputPort.prototype = new sc_OutputPort(); + +/*** META ((export #t) + (type bool) + (peephole (postfix " instanceof sc_OutputPort"))) +*/ +function sc_isOutputPort(o) { + return (o instanceof sc_OutputPort); +} + +/*** META ((export #t) + (peephole (postfix ".close()"))) +*/ +function sc_closeOutputPort(p) { + return p.close(); +} + +/* ------------------ write ---------------------------------------------------*/ + +/*** META ((export #t)) */ +function sc_write(o, p) { + if (p === undefined) // we assume not given + p = SC_DEFAULT_OUT; + p.appendJSString(sc_toWriteString(o)); +} + +function sc_toWriteString(o) { + if (o === null) + return "()"; + else if (o === true) + return "#t"; + else if (o === false) + return "#f"; + else if (o === undefined) + return "#unspecified"; + else if (typeof o === 'function') + return "#"; + else if (o.sc_toWriteString) + return o.sc_toWriteString(); + else + return o.toString(); +} + +function sc_escapeWriteString(s) { + var res = ""; + var j = 0; + for (i = 0; i < s.length; i++) { + switch (s.charAt(i)) { + case "\0": res += s.substring(j, i) + "\\0"; j = i + 1; break; + case "\b": res += s.substring(j, i) + "\\b"; j = i + 1; break; + case "\f": res += s.substring(j, i) + "\\f"; j = i + 1; break; + case "\n": res += s.substring(j, i) + "\\n"; j = i + 1; break; + case "\r": res += s.substring(j, i) + "\\r"; j = i + 1; break; + case "\t": res += s.substring(j, i) + "\\t"; j = i + 1; break; + case "\v": res += s.substring(j, i) + "\\v"; j = i + 1; break; + case '"': res += s.substring(j, i) + '\\"'; j = i + 1; break; + case "\\": res += s.substring(j, i) + "\\\\"; j = i + 1; break; + default: + var c = s.charAt(i); + if ("\a" !== "a" && c == "\a") { + res += s.substring(j, i) + "\\a"; j = i + 1; continue; + } + if ("\v" !== "v" && c == "\v") { + res += s.substring(j, i) + "\\v"; j = i + 1; continue; + } + //if (s.charAt(i) < ' ' || s.charCodeAt(i) > 127) { + // CARE: Manuel is this OK with HOP? + if (s.charAt(i) < ' ') { + /* non printable character and special chars */ + res += s.substring(j, i) + "\\x" + s.charCodeAt(i).toString(16); + j = i + 1; + } + // else just let i increase... + } + } + res += s.substring(j, i); + return res; +} + +/* ------------------ display ---------------------------------------------------*/ + +/*** META ((export #t)) */ +function sc_display(o, p) { + if (p === undefined) // we assume not given + p = SC_DEFAULT_OUT; + p.appendJSString(sc_toDisplayString(o)); +} + +function sc_toDisplayString(o) { + if (o === null) + return "()"; + else if (o === true) + return "#t"; + else if (o === false) + return "#f"; + else if (o === undefined) + return "#unspecified"; + else if (typeof o === 'function') + return "#"; + else if (o.sc_toDisplayString) + return o.sc_toDisplayString(); + else + return o.toString(); +} + +/* ------------------ newline ---------------------------------------------------*/ + +/*** META ((export #t)) */ +function sc_newline(p) { + if (p === undefined) // we assume not given + p = SC_DEFAULT_OUT; + p.appendJSString("\n"); +} + +/* ------------------ write-char ---------------------------------------------------*/ + +/*** META ((export #t)) */ +function sc_writeChar(c, p) { + if (p === undefined) // we assume not given + p = SC_DEFAULT_OUT; + p.appendJSString(c.val); +} + +/* ------------------ write-circle ---------------------------------------------------*/ + +/*** META ((export #t)) */ +function sc_writeCircle(o, p) { + if (p === undefined) // we assume not given + p = SC_DEFAULT_OUT; + p.appendJSString(sc_toWriteCircleString(o)); +} + +function sc_toWriteCircleString(o) { + var symb = sc_gensym("writeCircle"); + var nbPointer = new Object(); + nbPointer.nb = 0; + sc_prepWriteCircle(o, symb, nbPointer); + return sc_genToWriteCircleString(o, symb); +} + +function sc_prepWriteCircle(o, symb, nbPointer) { + // TODO sc_Struct + if (o instanceof sc_Pair || + o instanceof sc_Vector) { + if (o[symb] !== undefined) { + // not the first visit. + o[symb]++; + // unless there is already a number, assign one. + if (!o[symb + "nb"]) o[symb + "nb"] = nbPointer.nb++; + return; + } + o[symb] = 0; + if (o instanceof sc_Pair) { + sc_prepWriteCircle(o.car, symb, nbPointer); + sc_prepWriteCircle(o.cdr, symb, nbPointer); + } else { + for (var i = 0; i < o.length; i++) + sc_prepWriteCircle(o[i], symb, nbPointer); + } + } +} + +function sc_genToWriteCircleString(o, symb) { + if (!(o instanceof sc_Pair || + o instanceof sc_Vector)) + return sc_toWriteString(o); + return o.sc_toWriteCircleString(symb); +} +sc_Pair.prototype.sc_toWriteCircleString = function(symb, inList) { + if (this[symb + "use"]) { // use-flag is set. Just use it. + var nb = this[symb + "nb"]; + if (this[symb]-- === 0) { // if we are the last use. remove all fields. + delete this[symb]; + delete this[symb + "nb"]; + delete this[symb + "use"]; + } + if (inList) + return '. #' + nb + '#'; + else + return '#' + nb + '#'; + } + if (this[symb]-- === 0) { // if we are the last use. remove all fields. + delete this[symb]; + delete this[symb + "nb"]; + delete this[symb + "use"]; + } + + var res = ""; + + if (this[symb] !== undefined) { // implies > 0 + this[symb + "use"] = true; + if (inList) + res += '. #' + this[symb + "nb"] + '='; + else + res += '#' + this[symb + "nb"] + '='; + inList = false; + } + + if (!inList) + res += "("; + + // print car + res += sc_genToWriteCircleString(this.car, symb); + + if (sc_isPair(this.cdr)) { + res += " " + this.cdr.sc_toWriteCircleString(symb, true); + } else if (this.cdr !== null) { + res += " . " + sc_genToWriteCircleString(this.cdr, symb); + } + if (!inList) + res += ")"; + return res; +}; +sc_Vector.prototype.sc_toWriteCircleString = function(symb) { + if (this[symb + "use"]) { // use-flag is set. Just use it. + var nb = this[symb + "nb"]; + if (this[symb]-- === 0) { // if we are the last use. remove all fields. + delete this[symb]; + delete this[symb + "nb"]; + delete this[symb + "use"]; + } + return '#' + nb + '#'; + } + if (this[symb]-- === 0) { // if we are the last use. remove all fields. + delete this[symb]; + delete this[symb + "nb"]; + delete this[symb + "use"]; + } + + var res = ""; + if (this[symb] !== undefined) { // implies > 0 + this[symb + "use"] = true; + res += '#' + this[symb + "nb"] + '='; + } + res += "#("; + for (var i = 0; i < this.length; i++) { + res += sc_genToWriteCircleString(this[i], symb); + if (i < this.length - 1) res += " "; + } + res += ")"; + return res; +}; + + +/* ------------------ print ---------------------------------------------------*/ + +/*** META ((export #t)) */ +function sc_print(s) { + if (arguments.length === 1) { + sc_display(s); + sc_newline(); + } + else { + for (var i = 0; i < arguments.length; i++) + sc_display(arguments[i]); + sc_newline(); + } +} + +/* ------------------ format ---------------------------------------------------*/ +/*** META ((export #t)) */ +function sc_format(s, args) { + var len = s.length; + var p = new sc_StringOutputPort(); + var i = 0, j = 1; + + while( i < len ) { + var i2 = s.indexOf("~", i); + + if (i2 == -1) { + p.appendJSString( s.substring( i, len ) ); + return p.close(); + } else { + if (i2 > i) { + if (i2 == (len - 1)) { + p.appendJSString(s.substring(i, len)); + return p.close(); + } else { + p.appendJSString(s.substring(i, i2)); + i = i2; + } + } + + switch(s.charCodeAt(i2 + 1)) { + case 65: + case 97: + // a + sc_display(arguments[j], p); + i += 2; j++; + break; + + case 83: + case 115: + // s + sc_write(arguments[j], p); + i += 2; j++; + break; + + case 86: + case 118: + // v + sc_display(arguments[j], p); + p.appendJSString("\n"); + i += 2; j++; + break; + + case 67: + case 99: + // c + p.appendJSString(String.fromCharCode(arguments[j])); + i += 2; j++; + break; + + case 88: + case 120: + // x + p.appendJSString(arguments[j].toString(6)); + i += 2; j++; + break; + + case 79: + case 111: + // o + p.appendJSString(arguments[j].toString(8)); + i += 2; j++; + break; + + case 66: + case 98: + // b + p.appendJSString(arguments[j].toString(2)); + i += 2; j++; + break; + + case 37: + case 110: + // %, n + p.appendJSString("\n"); + i += 2; break; + + case 114: + // r + p.appendJSString("\r"); + i += 2; break; + + case 126: + // ~ + p.appendJSString("~"); + i += 2; break; + + default: + sc_error( "format: illegal ~" + + String.fromCharCode(s.charCodeAt(i2 + 1)) + + " sequence" ); + return ""; + } + } + } + + return p.close(); +} + +/* ------------------ global ports ---------------------------------------------------*/ + +var SC_DEFAULT_IN = new sc_ErrorInputPort(); +var SC_DEFAULT_OUT = new sc_ErrorOutputPort(); +var SC_ERROR_OUT = new sc_ErrorOutputPort(); + +var sc_SYMBOL_PREFIX = "\u1E9C"; +var sc_KEYWORD_PREFIX = "\u1E9D"; + +/*** META ((export #t) + (peephole (id))) */ +function sc_jsstring2string(s) { + return s; +} + +/*** META ((export #t) + (peephole (prefix "'\\u1E9C' +"))) +*/ +function sc_jsstring2symbol(s) { + return sc_SYMBOL_PREFIX + s; +} + +/*** META ((export #t) + (peephole (id))) +*/ +function sc_string2jsstring(s) { + return s; +} + +/*** META ((export #t) + (peephole (symbol2jsstring_immutable))) +*/ +function sc_symbol2jsstring(s) { + return s.slice(1); +} + +/*** META ((export #t) + (peephole (postfix ".slice(1)"))) +*/ +function sc_keyword2jsstring(k) { + return k.slice(1); +} + +/*** META ((export #t) + (peephole (prefix "'\\u1E9D' +"))) +*/ +function sc_jsstring2keyword(s) { + return sc_KEYWORD_PREFIX + s; +} + +/*** META ((export #t) + (type bool)) +*/ +function sc_isKeyword(s) { + return (typeof s === "string") && + (s.charAt(0) === sc_KEYWORD_PREFIX); +} + + +/*** META ((export #t)) */ +var sc_gensym = function() { + var counter = 1000; + return function(sym) { + counter++; + if (!sym) sym = sc_SYMBOL_PREFIX; + return sym + "s" + counter + "~" + "^sC-GeNsYm "; + }; +}(); + + +/*** META ((export #t) + (type bool)) +*/ +function sc_isEqual(o1, o2) { + return ((o1 === o2) || + (sc_isPair(o1) && sc_isPair(o2) + && sc_isPairEqual(o1, o2, sc_isEqual)) || + (sc_isVector(o1) && sc_isVector(o2) + && sc_isVectorEqual(o1, o2, sc_isEqual))); +} + +/*** META ((export number->symbol integer->symbol)) */ +function sc_number2symbol(x, radix) { + return sc_SYMBOL_PREFIX + sc_number2jsstring(x, radix); +} + +/*** META ((export number->string integer->string)) */ +var sc_number2string = sc_number2jsstring; + +/*** META ((export #t)) */ +function sc_symbol2number(s, radix) { + return sc_jsstring2number(s.slice(1), radix); +} + +/*** META ((export #t)) */ +var sc_string2number = sc_jsstring2number; + +/*** META ((export #t) + (peephole (prefix "+" s))) + ;; peephole will only apply if no radix is given. +*/ +function sc_string2integer(s, radix) { + if (!radix) return +s; + return parseInt(s, radix); +} + +/*** META ((export #t) + (peephole (prefix "+"))) +*/ +function sc_string2real(s) { + return +s; +} + + +/*** META ((export #t) + (type bool)) +*/ +function sc_isSymbol(s) { + return (typeof s === "string") && + (s.charAt(0) === sc_SYMBOL_PREFIX); +} + +/*** META ((export #t) + (peephole (symbol2string_immutable))) +*/ +function sc_symbol2string(s) { + return s.slice(1); +} + +/*** META ((export #t) + (peephole (prefix "'\\u1E9C' +"))) +*/ +function sc_string2symbol(s) { + return sc_SYMBOL_PREFIX + s; +} + +/*** META ((export symbol-append) + (peephole (symbolAppend_immutable))) +*/ +function sc_symbolAppend() { + var res = sc_SYMBOL_PREFIX; + for (var i = 0; i < arguments.length; i++) + res += arguments[i].slice(1); + return res; +} + +/*** META ((export #t) + (peephole (postfix ".val"))) +*/ +function sc_char2string(c) { return c.val; } + +/*** META ((export #t) + (peephole (hole 1 "'\\u1E9C' + " c ".val"))) +*/ +function sc_char2symbol(c) { return sc_SYMBOL_PREFIX + c.val; } + +/*** META ((export #t) + (type bool)) +*/ +function sc_isString(s) { + return (typeof s === "string") && + (s.charAt(0) !== sc_SYMBOL_PREFIX); +} + +/*** META ((export #t)) */ +var sc_makeString = sc_makejsString; + + +/*** META ((export #t)) */ +function sc_string() { + for (var i = 0; i < arguments.length; i++) + arguments[i] = arguments[i].val; + return "".concat.apply("", arguments); +} + +/*** META ((export #t) + (peephole (postfix ".length"))) +*/ +function sc_stringLength(s) { return s.length; } + +/*** META ((export #t)) */ +function sc_stringRef(s, k) { + return new sc_Char(s.charAt(k)); +} + +/* there's no stringSet in the immutable version +function sc_stringSet(s, k, c) +*/ + + +/*** META ((export string=?) + (type bool) + (peephole (hole 2 str1 " === " str2))) +*/ +function sc_isStringEqual(s1, s2) { + return s1 === s2; +} +/*** META ((export string?) + (type bool) + (peephole (hole 2 str1 " > " str2))) +*/ +function sc_isStringGreater(s1, s2) { + return s1 > s2; +} +/*** META ((export string<=?) + (type bool) + (peephole (hole 2 str1 " <= " str2))) +*/ +function sc_isStringLessEqual(s1, s2) { + return s1 <= s2; +} +/*** META ((export string>=?) + (type bool) + (peephole (hole 2 str1 " >= " str2))) +*/ +function sc_isStringGreaterEqual(s1, s2) { + return s1 >= s2; +} +/*** META ((export string-ci=?) + (type bool) + (peephole (hole 2 str1 ".toLowerCase() === " str2 ".toLowerCase()"))) +*/ +function sc_isStringCIEqual(s1, s2) { + return s1.toLowerCase() === s2.toLowerCase(); +} +/*** META ((export string-ci?) + (type bool) + (peephole (hole 2 str1 ".toLowerCase() > " str2 ".toLowerCase()"))) +*/ +function sc_isStringCIGreater(s1, s2) { + return s1.toLowerCase() > s2.toLowerCase(); +} +/*** META ((export string-ci<=?) + (type bool) + (peephole (hole 2 str1 ".toLowerCase() <= " str2 ".toLowerCase()"))) +*/ +function sc_isStringCILessEqual(s1, s2) { + return s1.toLowerCase() <= s2.toLowerCase(); +} +/*** META ((export string-ci>=?) + (type bool) + (peephole (hole 2 str1 ".toLowerCase() >= " str2 ".toLowerCase()"))) +*/ +function sc_isStringCIGreaterEqual(s1, s2) { + return s1.toLowerCase() >= s2.toLowerCase(); +} + +/*** META ((export #t) + (peephole (hole 3 s ".substring(" start ", " end ")"))) +*/ +function sc_substring(s, start, end) { + return s.substring(start, end); +} + +/*** META ((export #t)) +*/ +function sc_isSubstring_at(s1, s2, i) { + return s2 == s1.substring(i, i+ s2.length); +} + +/*** META ((export #t) + (peephole (infix 0 #f "+" "''"))) +*/ +function sc_stringAppend() { + return "".concat.apply("", arguments); +} + +/*** META ((export #t)) */ +var sc_string2list = sc_jsstring2list; + +/*** META ((export #t)) */ +var sc_list2string = sc_list2jsstring; + +/*** META ((export #t) + (peephole (id))) +*/ +function sc_stringCopy(s) { + return s; +} + +/* there's no string-fill in the immutable version +function sc_stringFill(s, c) +*/ + +/*** META ((export #t) + (peephole (postfix ".slice(1)"))) +*/ +function sc_keyword2string(o) { + return o.slice(1); +} + +/*** META ((export #t) + (peephole (prefix "'\\u1E9D' +"))) +*/ +function sc_string2keyword(o) { + return sc_KEYWORD_PREFIX + o; +} + +String.prototype.sc_toDisplayString = function() { + if (this.charAt(0) === sc_SYMBOL_PREFIX) + // TODO: care for symbols with spaces (escape-chars symbols). + return this.slice(1); + else if (this.charAt(0) === sc_KEYWORD_PREFIX) + return ":" + this.slice(1); + else + return this.toString(); +}; + +String.prototype.sc_toWriteString = function() { + if (this.charAt(0) === sc_SYMBOL_PREFIX) + // TODO: care for symbols with spaces (escape-chars symbols). + return this.slice(1); + else if (this.charAt(0) === sc_KEYWORD_PREFIX) + return ":" + this.slice(1); + else + return '"' + sc_escapeWriteString(this) + '"'; +}; +/* Exported Variables */ +var BgL_testzd2boyerzd2; +var BgL_nboyerzd2benchmarkzd2; +var BgL_setupzd2boyerzd2; +/* End Exports */ + +var translate_term_nboyer; +var translate_args_nboyer; +var untranslate_term_nboyer; +var BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer; +var BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer; +var translate_alist_nboyer; +var apply_subst_nboyer; +var apply_subst_lst_nboyer; +var tautologyp_nboyer; +var if_constructor_nboyer; +var rewrite_count_nboyer; +var rewrite_nboyer; +var rewrite_args_nboyer; +var unify_subst_nboyer; +var one_way_unify1_nboyer; +var false_term_nboyer; +var true_term_nboyer; +var trans_of_implies1_nboyer; +var is_term_equal_nboyer; +var is_term_member_nboyer; +var const_nboyer; +var sc_const_3_nboyer; +var sc_const_4_nboyer; +{ + (sc_const_4_nboyer = (new sc_Pair("\u1E9Cimplies",(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cu",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cu",(new sc_Pair("\u1E9Cw",null)))))),null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cw",null)))))),null))))))); + (sc_const_3_nboyer = sc_list((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccompile",(new sc_Pair("\u1E9Cform",null)))),(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair((new sc_Pair("\u1E9Ccodegen",(new sc_Pair((new sc_Pair("\u1E9Coptimize",(new sc_Pair("\u1E9Cform",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ceqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreaterp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clesseqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatereqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cboolean",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ciff",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ceven1",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Codd",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccountps-",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Cpred",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccountps-loop",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Cpred",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfact-",(new sc_Pair("\u1E9Ci",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfact-loop",(new sc_Pair("\u1E9Ci",(new sc_Pair((1),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse-",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Creverse-loop",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdivides",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cassume-true",(new sc_Pair("\u1E9Cvar",(new sc_Pair("\u1E9Calist",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cvar",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))),(new sc_Pair("\u1E9Calist",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cassume-false",(new sc_Pair("\u1E9Cvar",(new sc_Pair("\u1E9Calist",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cvar",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))),(new sc_Pair("\u1E9Calist",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctautology-checker",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ctautologyp",(new sc_Pair((new sc_Pair("\u1E9Cnormalize",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfalsify",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfalsify1",(new sc_Pair((new sc_Pair("\u1E9Cnormalize",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cprime",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))),null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cprime1",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Cx",null)))),null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair("\u1E9Cp",(new sc_Pair("\u1E9Cq",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cq",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair("\u1E9Cp",(new sc_Pair("\u1E9Cq",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cq",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair("\u1E9Cp",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cp",(new sc_Pair("\u1E9Cq",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cq",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))))),(new sc_Pair("\u1E9Cd",(new sc_Pair("\u1E9Ce",null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cd",(new sc_Pair("\u1E9Ce",null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cd",(new sc_Pair("\u1E9Ce",null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Ca",null)))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cb",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cc",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair("\u1E9Ca",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair((new sc_Pair("\u1E9Cplus-fringe",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Ca",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cexec",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cpds",(new sc_Pair("\u1E9Cenvrn",null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cexec",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cexec",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cpds",(new sc_Pair("\u1E9Cenvrn",null)))))))),(new sc_Pair("\u1E9Cenvrn",null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmc-flatten",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Cy",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cb",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cy",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Cintersect",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Ck",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Ck",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Ck",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),(new sc_Pair("\u1E9Ck",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse-loop",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Cy",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse-loop",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccount-list",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Csort-lp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Ccount-list",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccount-list",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cbig-plus1",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair("\u1E9Ci",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cbig-plus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cbase",null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Ci",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cj",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cj",(new sc_Pair((1),null)))))),null)))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cpower-rep",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Ci",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cbig-plus",(new sc_Pair((new sc_Pair("\u1E9Cpower-rep",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cpower-rep",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair("\u1E9Cbase",null)))))))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair("\u1E9Cb",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Ca",null)))),null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cy",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cy",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cw",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cw",(new sc_Pair("\u1E9Cx",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cb",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cy",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cz",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cvalue",(new sc_Pair((new sc_Pair("\u1E9Cnormalize",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cvalue",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnlistp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair((new sc_Pair("\u1E9Cgopher",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csamefringe",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatest-factor",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatest-factor",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((1),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((1),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair((new sc_Pair("\u1E9Cgreatest-factor",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes-list",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Ctimes-list",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ctimes-list",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cprime-list",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cprime-list",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cprime-list",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cw",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cz",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cw",(new sc_Pair((1),null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatereqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((1),null)))))),(new sc_Pair(sc_list("\u1E9Cand", (new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))), (new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cb",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))), (new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Ca",null)))), (new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cb",null)))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Ca",null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cl",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cl",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cl",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csort2",(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cl",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Csort2",(new sc_Pair("\u1E9Cl",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdsort",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Csort2",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx1",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx2",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx3",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx4",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx5",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx6",(new sc_Pair("\u1E9Cx7",null)))))),null)))))),null)))))),null)))))),null)))))),null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((6),(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cx7",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((2),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((2),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair("\u1E9Cy",(new sc_Pair((2),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csigma",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Ci",null)))),null)))))),(new sc_Pair((2),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cy",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cx",null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cz",null)))),null)))))),null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair("\u1E9Ca",null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cy",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Ci",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clast",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Clast",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Ca",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Ccar",(new sc_Pair((new sc_Pair("\u1E9Clast",(new sc_Pair("\u1E9Ca",null)))),null)))),(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair("\u1E9Cb",null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),(new sc_Pair("\u1E9Cz",null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cassignment",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cassignedp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cassignment",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cassignment",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cb",null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccar",(new sc_Pair((new sc_Pair("\u1E9Cgopher",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccar",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair((new sc_Pair("\u1E9Ccdr",(new sc_Pair((new sc_Pair("\u1E9Cgopher",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccdr",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cget",(new sc_Pair("\u1E9Cj",(new sc_Pair((new sc_Pair("\u1E9Cset",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cval",(new sc_Pair("\u1E9Cmem",null)))))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Ceqp",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair("\u1E9Cval",(new sc_Pair((new sc_Pair("\u1E9Cget",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Cmem",null)))))),null)))))))),null)))))))); + (const_nboyer = (new sc_Pair((new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cf",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cc",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cf",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cd",null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cf",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cu",(new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cw",(new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cb",null)))),null)))))),null)))))))),null))))))))))); + BgL_nboyerzd2benchmarkzd2 = function() { + var args = null; + for (var sc_tmp = arguments.length - 1; sc_tmp >= 0; sc_tmp--) { + args = sc_cons(arguments[sc_tmp], args); + } + var n; + return ((n = ((args === null)?(0):(args.car))), (BgL_setupzd2boyerzd2()), (BgL_runzd2benchmarkzd2(("nboyer"+(sc_number2string(n))), (1), function() { + return (BgL_testzd2boyerzd2(n)); + }, function(rewrites) { + if ((sc_isNumber(rewrites))) + switch (n) { + case (0): + return (rewrites===(95024)); + break; + case (1): + return (rewrites===(591777)); + break; + case (2): + return (rewrites===(1813975)); + break; + case (3): + return (rewrites===(5375678)); + break; + case (4): + return (rewrites===(16445406)); + break; + case (5): + return (rewrites===(51507739)); + break; + default: + return true; + break; + } + else + return false; + }))); + }; + BgL_setupzd2boyerzd2 = function() { + return true; + }; + BgL_testzd2boyerzd2 = function() { + return true; + }; + translate_term_nboyer = function(term) { + var lst; + return (!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), ((lst = (term.cdr)), ((lst === null)?null:(new sc_Pair((translate_term_nboyer((lst.car))), (translate_args_nboyer((lst.cdr)))))))))); + }; + translate_args_nboyer = function(lst) { + var sc_lst_5; + var term; + return ((lst === null)?null:(new sc_Pair(((term = (lst.car)), (!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr))))))), ((sc_lst_5 = (lst.cdr)), ((sc_lst_5 === null)?null:(new sc_Pair((translate_term_nboyer((sc_lst_5.car))), (translate_args_nboyer((sc_lst_5.cdr)))))))))); + }; + untranslate_term_nboyer = function(term) { + var optrOpnd; + var tail1131; + var L1127; + var falseHead1130; + var symbol_record; + if (!(term instanceof sc_Pair)) + return term; + else + { + (falseHead1130 = (new sc_Pair(null, null))); + (L1127 = (term.cdr)); + (tail1131 = falseHead1130); + while (!(L1127 === null)) { + { + (tail1131.cdr = (new sc_Pair((untranslate_term_nboyer((L1127.car))), null))); + (tail1131 = (tail1131.cdr)); + (L1127 = (L1127.cdr)); + } + } + (optrOpnd = (falseHead1130.cdr)); + return (new sc_Pair(((symbol_record = (term.car)), (symbol_record[(0)])), optrOpnd)); + } + }; + BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer = function(sym) { + var r; + var x; + return ((x = (sc_assq(sym, BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer))), ((x!== false)?(x.cdr):((r = [sym, null]), (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = (new sc_Pair((new sc_Pair(sym, r)), BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer))), r))); + }; + (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = null); + translate_alist_nboyer = function(alist) { + var sc_alist_6; + var term; + return ((alist === null)?null:(new sc_Pair((new sc_Pair((alist.car.car), ((term = (alist.car.cdr)), (!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr))))))))), ((sc_alist_6 = (alist.cdr)), ((sc_alist_6 === null)?null:(new sc_Pair((new sc_Pair((sc_alist_6.car.car), (translate_term_nboyer((sc_alist_6.car.cdr))))), (translate_alist_nboyer((sc_alist_6.cdr)))))))))); + }; + apply_subst_nboyer = function(alist, term) { + var lst; + var temp_temp; + return (!(term instanceof sc_Pair)?((temp_temp = (sc_assq(term, alist))), ((temp_temp!== false)?(temp_temp.cdr):term)):(new sc_Pair((term.car), ((lst = (term.cdr)), ((lst === null)?null:(new sc_Pair((apply_subst_nboyer(alist, (lst.car))), (apply_subst_lst_nboyer(alist, (lst.cdr)))))))))); + }; + apply_subst_lst_nboyer = function(alist, lst) { + var sc_lst_7; + return ((lst === null)?null:(new sc_Pair((apply_subst_nboyer(alist, (lst.car))), ((sc_lst_7 = (lst.cdr)), ((sc_lst_7 === null)?null:(new sc_Pair((apply_subst_nboyer(alist, (sc_lst_7.car))), (apply_subst_lst_nboyer(alist, (sc_lst_7.cdr)))))))))); + }; + tautologyp_nboyer = function(sc_x_11, true_lst, false_lst) { + var tmp1125; + var x; + var tmp1126; + var sc_x_8; + var sc_tmp1125_9; + var sc_tmp1126_10; + var sc_x_11; + var true_lst; + var false_lst; + while (true) { + if ((((sc_tmp1126_10 = (is_term_equal_nboyer(sc_x_11, true_term_nboyer))), ((sc_tmp1126_10!== false)?sc_tmp1126_10:(is_term_member_nboyer(sc_x_11, true_lst))))!== false)) + return true; + else + if ((((sc_tmp1125_9 = (is_term_equal_nboyer(sc_x_11, false_term_nboyer))), ((sc_tmp1125_9!== false)?sc_tmp1125_9:(is_term_member_nboyer(sc_x_11, false_lst))))!== false)) + return false; + else + if (!(sc_x_11 instanceof sc_Pair)) + return false; + else + if (((sc_x_11.car)===if_constructor_nboyer)) + if ((((sc_x_8 = (sc_x_11.cdr.car)), (tmp1126 = (is_term_equal_nboyer(sc_x_8, true_term_nboyer))), ((tmp1126!== false)?tmp1126:(is_term_member_nboyer(sc_x_8, true_lst))))!== false)) + (sc_x_11 = (sc_x_11.cdr.cdr.car)); + else + if ((((x = (sc_x_11.cdr.car)), (tmp1125 = (is_term_equal_nboyer(x, false_term_nboyer))), ((tmp1125!== false)?tmp1125:(is_term_member_nboyer(x, false_lst))))!== false)) + (sc_x_11 = (sc_x_11.cdr.cdr.cdr.car)); + else + if (((tautologyp_nboyer((sc_x_11.cdr.cdr.car), (new sc_Pair((sc_x_11.cdr.car), true_lst)), false_lst))!== false)) + { + (false_lst = (new sc_Pair((sc_x_11.cdr.car), false_lst))); + (sc_x_11 = (sc_x_11.cdr.cdr.cdr.car)); + } + else + return false; + else + return false; + } + }; + (if_constructor_nboyer = "\u1E9C*"); + (rewrite_count_nboyer = (0)); + rewrite_nboyer = function(term) { + var term2; + var sc_term_12; + var lst; + var symbol_record; + var sc_lst_13; + { + (++rewrite_count_nboyer); + if (!(term instanceof sc_Pair)) + return term; + else + { + (sc_term_12 = (new sc_Pair((term.car), ((sc_lst_13 = (term.cdr)), ((sc_lst_13 === null)?null:(new sc_Pair((rewrite_nboyer((sc_lst_13.car))), (rewrite_args_nboyer((sc_lst_13.cdr)))))))))); + (lst = ((symbol_record = (term.car)), (symbol_record[(1)]))); + while (true) { + if ((lst === null)) + return sc_term_12; + else + if ((((term2 = ((lst.car).cdr.car)), (unify_subst_nboyer = null), (one_way_unify1_nboyer(sc_term_12, term2)))!== false)) + return (rewrite_nboyer((apply_subst_nboyer(unify_subst_nboyer, ((lst.car).cdr.cdr.car))))); + else + (lst = (lst.cdr)); + } + } + } + }; + rewrite_args_nboyer = function(lst) { + var sc_lst_14; + return ((lst === null)?null:(new sc_Pair((rewrite_nboyer((lst.car))), ((sc_lst_14 = (lst.cdr)), ((sc_lst_14 === null)?null:(new sc_Pair((rewrite_nboyer((sc_lst_14.car))), (rewrite_args_nboyer((sc_lst_14.cdr)))))))))); + }; + (unify_subst_nboyer = "\u1E9C*"); + one_way_unify1_nboyer = function(term1, term2) { + var lst1; + var lst2; + var temp_temp; + if (!(term2 instanceof sc_Pair)) + { + (temp_temp = (sc_assq(term2, unify_subst_nboyer))); + if ((temp_temp!== false)) + return (is_term_equal_nboyer(term1, (temp_temp.cdr))); + else + if ((sc_isNumber(term2))) + return (sc_isEqual(term1, term2)); + else + { + (unify_subst_nboyer = (new sc_Pair((new sc_Pair(term2, term1)), unify_subst_nboyer))); + return true; + } + } + else + if (!(term1 instanceof sc_Pair)) + return false; + else + if (((term1.car)===(term2.car))) + { + (lst1 = (term1.cdr)); + (lst2 = (term2.cdr)); + while (true) { + if ((lst1 === null)) + return (lst2 === null); + else + if ((lst2 === null)) + return false; + else + if (((one_way_unify1_nboyer((lst1.car), (lst2.car)))!== false)) + { + (lst1 = (lst1.cdr)); + (lst2 = (lst2.cdr)); + } + else + return false; + } + } + else + return false; + }; + (false_term_nboyer = "\u1E9C*"); + (true_term_nboyer = "\u1E9C*"); + trans_of_implies1_nboyer = function(n) { + var sc_n_15; + return ((sc_isEqual(n, (1)))?(sc_list("\u1E9Cimplies", (0), (1))):(sc_list("\u1E9Cand", (sc_list("\u1E9Cimplies", (n-(1)), n)), ((sc_n_15 = (n-(1))), ((sc_isEqual(sc_n_15, (1)))?(sc_list("\u1E9Cimplies", (0), (1))):(sc_list("\u1E9Cand", (sc_list("\u1E9Cimplies", (sc_n_15-(1)), sc_n_15)), (trans_of_implies1_nboyer((sc_n_15-(1))))))))))); + }; + is_term_equal_nboyer = function(x, y) { + var lst1; + var lst2; + var r2; + var r1; + if ((x instanceof sc_Pair)) + if ((y instanceof sc_Pair)) + if ((((r1 = (x.car)), (r2 = (y.car)), (r1===r2))!== false)) + { + (lst1 = (x.cdr)); + (lst2 = (y.cdr)); + while (true) { + if ((lst1 === null)) + return (lst2 === null); + else + if ((lst2 === null)) + return false; + else + if (((is_term_equal_nboyer((lst1.car), (lst2.car)))!== false)) + { + (lst1 = (lst1.cdr)); + (lst2 = (lst2.cdr)); + } + else + return false; + } + } + else + return false; + else + return false; + else + return (sc_isEqual(x, y)); + }; + is_term_member_nboyer = function(x, lst) { + var x; + var lst; + while (true) { + if ((lst === null)) + return false; + else + if (((is_term_equal_nboyer(x, (lst.car)))!== false)) + return true; + else + (lst = (lst.cdr)); + } + }; + BgL_setupzd2boyerzd2 = function() { + var symbol_record; + var value; + var BgL_sc_symbolzd2record_16zd2; + var sym; + var sc_sym_17; + var term; + var lst; + var sc_term_18; + var sc_term_19; + { + (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = null); + (if_constructor_nboyer = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer("\u1E9Cif"))); + (false_term_nboyer = ((sc_term_19 = (new sc_Pair("\u1E9Cf",null))), (!(sc_term_19 instanceof sc_Pair)?sc_term_19:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_19.car))), (translate_args_nboyer((sc_term_19.cdr)))))))); + (true_term_nboyer = ((sc_term_18 = (new sc_Pair("\u1E9Ct",null))), (!(sc_term_18 instanceof sc_Pair)?sc_term_18:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_18.car))), (translate_args_nboyer((sc_term_18.cdr)))))))); + (lst = sc_const_3_nboyer); + while (!(lst === null)) { + { + (term = (lst.car)); + if (((term instanceof sc_Pair)&&(((term.car)==="\u1E9Cequal")&&((term.cdr.car) instanceof sc_Pair)))) + { + (sc_sym_17 = ((term.cdr.car).car)); + (value = (new sc_Pair((!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr)))))), ((sym = ((term.cdr.car).car)), (BgL_sc_symbolzd2record_16zd2 = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer(sym))), (BgL_sc_symbolzd2record_16zd2[(1)]))))); + (symbol_record = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer(sc_sym_17))); + (symbol_record[(1)] = value); + } + else + (sc_error("ADD-LEMMA did not like term: ", term)); + (lst = (lst.cdr)); + } + } + return true; + } + }; + BgL_testzd2boyerzd2 = function(n) { + var optrOpnd; + var term; + var sc_n_20; + var answer; + var sc_term_21; + var sc_term_22; + { + (rewrite_count_nboyer = (0)); + (term = sc_const_4_nboyer); + (sc_n_20 = n); + while (!(sc_n_20=== 0)) { + { + (term = (sc_list("\u1E9Cor", term, (new sc_Pair("\u1E9Cf",null))))); + (--sc_n_20); + } + } + (sc_term_22 = term); + if (!(sc_term_22 instanceof sc_Pair)) + (optrOpnd = sc_term_22); + else + (optrOpnd = (new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_22.car))), (translate_args_nboyer((sc_term_22.cdr)))))); + (sc_term_21 = (apply_subst_nboyer(((const_nboyer === null)?null:(new sc_Pair((new sc_Pair((const_nboyer.car.car), (translate_term_nboyer((const_nboyer.car.cdr))))), (translate_alist_nboyer((const_nboyer.cdr)))))), optrOpnd))); + (answer = (tautologyp_nboyer((rewrite_nboyer(sc_term_21)), null, null))); + (sc_write(rewrite_count_nboyer)); + (sc_display(" rewrites")); + (sc_newline()); + if ((answer!== false)) + return rewrite_count_nboyer; + else + return false; + } + }; +} +/* Exported Variables */ +var BgL_parsezd2ze3nbzd2treesze3; +var BgL_earleyzd2benchmarkzd2; +var BgL_parsezd2ze3parsedzf3zc2; +var test; +var BgL_parsezd2ze3treesz31; +var BgL_makezd2parserzd2; +/* End Exports */ + +var const_earley; +{ + (const_earley = (new sc_Pair((new sc_Pair("\u1E9Cs",(new sc_Pair((new sc_Pair("\u1E9Ca",null)),(new sc_Pair((new sc_Pair("\u1E9Cs",(new sc_Pair("\u1E9Cs",null)))),null)))))),null))); + BgL_makezd2parserzd2 = function(grammar, lexer) { + var i; + var parser_descr; + var def_loop; + var nb_nts; + var names; + var steps; + var predictors; + var enders; + var starters; + var nts; + var sc_names_1; + var sc_steps_2; + var sc_predictors_3; + var sc_enders_4; + var sc_starters_5; + var nb_confs; + var BgL_sc_defzd2loop_6zd2; + var BgL_sc_nbzd2nts_7zd2; + var sc_nts_8; + var BgL_sc_defzd2loop_9zd2; + var ind; + { + ind = function(nt, sc_nts_10) { + var i; + { + (i = ((sc_nts_10.length)-(1))); + while (true) { + if ((i>=(0))) + if ((sc_isEqual((sc_nts_10[i]), nt))) + return i; + else + (--i); + else + return false; + } + } + }; + (sc_nts_8 = ((BgL_sc_defzd2loop_9zd2 = function(defs, sc_nts_11) { + var rule_loop; + var head; + var def; + return ((defs instanceof sc_Pair)?((def = (defs.car)), (head = (def.car)), (rule_loop = function(rules, sc_nts_12) { + var nt; + var l; + var sc_nts_13; + var rule; + if ((rules instanceof sc_Pair)) + { + (rule = (rules.car)); + (l = rule); + (sc_nts_13 = sc_nts_12); + while ((l instanceof sc_Pair)) { + { + (nt = (l.car)); + (l = (l.cdr)); + (sc_nts_13 = (((sc_member(nt, sc_nts_13))!== false)?sc_nts_13:(new sc_Pair(nt, sc_nts_13)))); + } + } + return (rule_loop((rules.cdr), sc_nts_13)); + } + else + return (BgL_sc_defzd2loop_9zd2((defs.cdr), sc_nts_12)); + }), (rule_loop((def.cdr), (((sc_member(head, sc_nts_11))!== false)?sc_nts_11:(new sc_Pair(head, sc_nts_11)))))):(sc_list2vector((sc_reverse(sc_nts_11))))); + }), (BgL_sc_defzd2loop_9zd2(grammar, null)))); + (BgL_sc_nbzd2nts_7zd2 = (sc_nts_8.length)); + (nb_confs = (((BgL_sc_defzd2loop_6zd2 = function(defs, BgL_sc_nbzd2confs_14zd2) { + var rule_loop; + var def; + return ((defs instanceof sc_Pair)?((def = (defs.car)), (rule_loop = function(rules, BgL_sc_nbzd2confs_15zd2) { + var l; + var BgL_sc_nbzd2confs_16zd2; + var rule; + if ((rules instanceof sc_Pair)) + { + (rule = (rules.car)); + (l = rule); + (BgL_sc_nbzd2confs_16zd2 = BgL_sc_nbzd2confs_15zd2); + while ((l instanceof sc_Pair)) { + { + (l = (l.cdr)); + (++BgL_sc_nbzd2confs_16zd2); + } + } + return (rule_loop((rules.cdr), (BgL_sc_nbzd2confs_16zd2+(1)))); + } + else + return (BgL_sc_defzd2loop_6zd2((defs.cdr), BgL_sc_nbzd2confs_15zd2)); + }), (rule_loop((def.cdr), BgL_sc_nbzd2confs_14zd2))):BgL_sc_nbzd2confs_14zd2); + }), (BgL_sc_defzd2loop_6zd2(grammar, (0))))+BgL_sc_nbzd2nts_7zd2)); + (sc_starters_5 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null))); + (sc_enders_4 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null))); + (sc_predictors_3 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null))); + (sc_steps_2 = (sc_makeVector(nb_confs, false))); + (sc_names_1 = (sc_makeVector(nb_confs, false))); + (nts = sc_nts_8); + (starters = sc_starters_5); + (enders = sc_enders_4); + (predictors = sc_predictors_3); + (steps = sc_steps_2); + (names = sc_names_1); + (nb_nts = (sc_nts_8.length)); + (i = (nb_nts-(1))); + while ((i>=(0))) { + { + (sc_steps_2[i] = (i-nb_nts)); + (sc_names_1[i] = (sc_list((sc_nts_8[i]), (0)))); + (sc_enders_4[i] = (sc_list(i))); + (--i); + } + } + def_loop = function(defs, conf) { + var rule_loop; + var head; + var def; + return ((defs instanceof sc_Pair)?((def = (defs.car)), (head = (def.car)), (rule_loop = function(rules, conf, rule_num) { + var i; + var sc_i_17; + var nt; + var l; + var sc_conf_18; + var sc_i_19; + var rule; + if ((rules instanceof sc_Pair)) + { + (rule = (rules.car)); + (names[conf] = (sc_list(head, rule_num))); + (sc_i_19 = (ind(head, nts))); + (starters[sc_i_19] = (new sc_Pair(conf, (starters[sc_i_19])))); + (l = rule); + (sc_conf_18 = conf); + while ((l instanceof sc_Pair)) { + { + (nt = (l.car)); + (steps[sc_conf_18] = (ind(nt, nts))); + (sc_i_17 = (ind(nt, nts))); + (predictors[sc_i_17] = (new sc_Pair(sc_conf_18, (predictors[sc_i_17])))); + (l = (l.cdr)); + (++sc_conf_18); + } + } + (steps[sc_conf_18] = ((ind(head, nts))-nb_nts)); + (i = (ind(head, nts))); + (enders[i] = (new sc_Pair(sc_conf_18, (enders[i])))); + return (rule_loop((rules.cdr), (sc_conf_18+(1)), (rule_num+(1)))); + } + else + return (def_loop((defs.cdr), conf)); + }), (rule_loop((def.cdr), conf, (1)))):undefined); + }; + (def_loop(grammar, (sc_nts_8.length))); + (parser_descr = [lexer, sc_nts_8, sc_starters_5, sc_enders_4, sc_predictors_3, sc_steps_2, sc_names_1]); + return function(input) { + var optrOpnd; + var sc_optrOpnd_20; + var sc_optrOpnd_21; + var sc_optrOpnd_22; + var loop1; + var BgL_sc_stateza2_23za2; + var toks; + var BgL_sc_nbzd2nts_24zd2; + var sc_steps_25; + var sc_enders_26; + var state_num; + var BgL_sc_statesza2_27za2; + var states; + var i; + var conf; + var l; + var tok_nts; + var sc_i_28; + var sc_i_29; + var l1; + var l2; + var tok; + var tail1129; + var L1125; + var goal_enders; + var BgL_sc_statesza2_30za2; + var BgL_sc_nbzd2nts_31zd2; + var BgL_sc_nbzd2confs_32zd2; + var nb_toks; + var goal_starters; + var sc_states_33; + var BgL_sc_nbzd2confs_34zd2; + var BgL_sc_nbzd2toks_35zd2; + var sc_toks_36; + var falseHead1128; + var sc_names_37; + var sc_steps_38; + var sc_predictors_39; + var sc_enders_40; + var sc_starters_41; + var sc_nts_42; + var lexer; + var sc_ind_43; + var make_states; + var BgL_sc_confzd2setzd2getza2_44za2; + var conf_set_merge_new_bang; + var conf_set_adjoin; + var BgL_sc_confzd2setzd2adjoinza2_45za2; + var BgL_sc_confzd2setzd2adjoinza2za2_46z00; + var conf_set_union; + var forw; + var is_parsed; + var deriv_trees; + var BgL_sc_derivzd2treesza2_47z70; + var nb_deriv_trees; + var BgL_sc_nbzd2derivzd2treesza2_48za2; + { + sc_ind_43 = function(nt, sc_nts_49) { + var i; + { + (i = ((sc_nts_49.length)-(1))); + while (true) { + if ((i>=(0))) + if ((sc_isEqual((sc_nts_49[i]), nt))) + return i; + else + (--i); + else + return false; + } + } + }; + make_states = function(BgL_sc_nbzd2toks_50zd2, BgL_sc_nbzd2confs_51zd2) { + var v; + var i; + var sc_states_52; + { + (sc_states_52 = (sc_makeVector((BgL_sc_nbzd2toks_50zd2+(1)), false))); + (i = BgL_sc_nbzd2toks_50zd2); + while ((i>=(0))) { + { + (v = (sc_makeVector((BgL_sc_nbzd2confs_51zd2+(1)), false))); + (v[(0)] = (-1)); + (sc_states_52[i] = v); + (--i); + } + } + return sc_states_52; + } + }; + BgL_sc_confzd2setzd2getza2_44za2 = function(state, BgL_sc_statezd2num_53zd2, sc_conf_54) { + var conf_set; + var BgL_sc_confzd2set_55zd2; + return ((BgL_sc_confzd2set_55zd2 = (state[(sc_conf_54+(1))])), ((BgL_sc_confzd2set_55zd2!== false)?BgL_sc_confzd2set_55zd2:((conf_set = (sc_makeVector((BgL_sc_statezd2num_53zd2+(6)), false))), (conf_set[(1)] = (-3)), (conf_set[(2)] = (-1)), (conf_set[(3)] = (-1)), (conf_set[(4)] = (-1)), (state[(sc_conf_54+(1))] = conf_set), conf_set))); + }; + conf_set_merge_new_bang = function(conf_set) { + return ((conf_set[((conf_set[(1)])+(5))] = (conf_set[(4)])), (conf_set[(1)] = (conf_set[(3)])), (conf_set[(3)] = (-1)), (conf_set[(4)] = (-1))); + }; + conf_set_adjoin = function(state, conf_set, sc_conf_56, i) { + var tail; + return ((tail = (conf_set[(3)])), (conf_set[(i+(5))] = (-1)), (conf_set[(tail+(5))] = i), (conf_set[(3)] = i), ((tail<(0))?((conf_set[(0)] = (state[(0)])), (state[(0)] = sc_conf_56)):undefined)); + }; + BgL_sc_confzd2setzd2adjoinza2_45za2 = function(sc_states_57, BgL_sc_statezd2num_58zd2, l, i) { + var conf_set; + var sc_conf_59; + var l1; + var state; + { + (state = (sc_states_57[BgL_sc_statezd2num_58zd2])); + (l1 = l); + while ((l1 instanceof sc_Pair)) { + { + (sc_conf_59 = (l1.car)); + (conf_set = (BgL_sc_confzd2setzd2getza2_44za2(state, BgL_sc_statezd2num_58zd2, sc_conf_59))); + if (((conf_set[(i+(5))])=== false)) + { + (conf_set_adjoin(state, conf_set, sc_conf_59, i)); + (l1 = (l1.cdr)); + } + else + (l1 = (l1.cdr)); + } + } + return undefined; + } + }; + BgL_sc_confzd2setzd2adjoinza2za2_46z00 = function(sc_states_60, BgL_sc_statesza2_61za2, BgL_sc_statezd2num_62zd2, sc_conf_63, i) { + var BgL_sc_confzd2setza2_64z70; + var BgL_sc_stateza2_65za2; + var conf_set; + var state; + return ((state = (sc_states_60[BgL_sc_statezd2num_62zd2])), ((((conf_set = (state[(sc_conf_63+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)?((BgL_sc_stateza2_65za2 = (BgL_sc_statesza2_61za2[BgL_sc_statezd2num_62zd2])), (BgL_sc_confzd2setza2_64z70 = (BgL_sc_confzd2setzd2getza2_44za2(BgL_sc_stateza2_65za2, BgL_sc_statezd2num_62zd2, sc_conf_63))), (((BgL_sc_confzd2setza2_64z70[(i+(5))])=== false)?(conf_set_adjoin(BgL_sc_stateza2_65za2, BgL_sc_confzd2setza2_64z70, sc_conf_63, i)):undefined), true):false)); + }; + conf_set_union = function(state, conf_set, sc_conf_66, other_set) { + var i; + { + (i = (other_set[(2)])); + while ((i>=(0))) { + if (((conf_set[(i+(5))])=== false)) + { + (conf_set_adjoin(state, conf_set, sc_conf_66, i)); + (i = (other_set[(i+(5))])); + } + else + (i = (other_set[(i+(5))])); + } + return undefined; + } + }; + forw = function(sc_states_67, BgL_sc_statezd2num_68zd2, sc_starters_69, sc_enders_70, sc_predictors_71, sc_steps_72, sc_nts_73) { + var next_set; + var next; + var conf_set; + var ender; + var l; + var starter_set; + var starter; + var sc_l_74; + var sc_loop1_75; + var head; + var BgL_sc_confzd2set_76zd2; + var BgL_sc_statezd2num_77zd2; + var state; + var sc_states_78; + var preds; + var BgL_sc_confzd2set_79zd2; + var step; + var sc_conf_80; + var BgL_sc_nbzd2nts_81zd2; + var sc_state_82; + { + (sc_state_82 = (sc_states_67[BgL_sc_statezd2num_68zd2])); + (BgL_sc_nbzd2nts_81zd2 = (sc_nts_73.length)); + while (true) { + { + (sc_conf_80 = (sc_state_82[(0)])); + if ((sc_conf_80>=(0))) + { + (step = (sc_steps_72[sc_conf_80])); + (BgL_sc_confzd2set_79zd2 = (sc_state_82[(sc_conf_80+(1))])); + (head = (BgL_sc_confzd2set_79zd2[(4)])); + (sc_state_82[(0)] = (BgL_sc_confzd2set_79zd2[(0)])); + (conf_set_merge_new_bang(BgL_sc_confzd2set_79zd2)); + if ((step>=(0))) + { + (sc_l_74 = (sc_starters_69[step])); + while ((sc_l_74 instanceof sc_Pair)) { + { + (starter = (sc_l_74.car)); + (starter_set = (BgL_sc_confzd2setzd2getza2_44za2(sc_state_82, BgL_sc_statezd2num_68zd2, starter))); + if (((starter_set[(BgL_sc_statezd2num_68zd2+(5))])=== false)) + { + (conf_set_adjoin(sc_state_82, starter_set, starter, BgL_sc_statezd2num_68zd2)); + (sc_l_74 = (sc_l_74.cdr)); + } + else + (sc_l_74 = (sc_l_74.cdr)); + } + } + (l = (sc_enders_70[step])); + while ((l instanceof sc_Pair)) { + { + (ender = (l.car)); + if ((((conf_set = (sc_state_82[(ender+(1))])), ((conf_set!== false)?(conf_set[(BgL_sc_statezd2num_68zd2+(5))]):false))!== false)) + { + (next = (sc_conf_80+(1))); + (next_set = (BgL_sc_confzd2setzd2getza2_44za2(sc_state_82, BgL_sc_statezd2num_68zd2, next))); + (conf_set_union(sc_state_82, next_set, next, BgL_sc_confzd2set_79zd2)); + (l = (l.cdr)); + } + else + (l = (l.cdr)); + } + } + } + else + { + (preds = (sc_predictors_71[(step+BgL_sc_nbzd2nts_81zd2)])); + (sc_states_78 = sc_states_67); + (state = sc_state_82); + (BgL_sc_statezd2num_77zd2 = BgL_sc_statezd2num_68zd2); + (BgL_sc_confzd2set_76zd2 = BgL_sc_confzd2set_79zd2); + sc_loop1_75 = function(l) { + var sc_state_83; + var BgL_sc_nextzd2set_84zd2; + var sc_next_85; + var pred_set; + var i; + var pred; + if ((l instanceof sc_Pair)) + { + (pred = (l.car)); + (i = head); + while ((i>=(0))) { + { + (pred_set = ((sc_state_83 = (sc_states_78[i])), (sc_state_83[(pred+(1))]))); + if ((pred_set!== false)) + { + (sc_next_85 = (pred+(1))); + (BgL_sc_nextzd2set_84zd2 = (BgL_sc_confzd2setzd2getza2_44za2(state, BgL_sc_statezd2num_77zd2, sc_next_85))); + (conf_set_union(state, BgL_sc_nextzd2set_84zd2, sc_next_85, pred_set)); + } + (i = (BgL_sc_confzd2set_76zd2[(i+(5))])); + } + } + return (sc_loop1_75((l.cdr))); + } + else + return undefined; + }; + (sc_loop1_75(preds)); + } + } + else + return undefined; + } + } + } + }; + is_parsed = function(nt, i, j, sc_nts_86, sc_enders_87, sc_states_88) { + var conf_set; + var state; + var sc_conf_89; + var l; + var BgL_sc_ntza2_90za2; + { + (BgL_sc_ntza2_90za2 = (sc_ind_43(nt, sc_nts_86))); + if ((BgL_sc_ntza2_90za2!== false)) + { + (sc_nts_86.length); + (l = (sc_enders_87[BgL_sc_ntza2_90za2])); + while (true) { + if ((l instanceof sc_Pair)) + { + (sc_conf_89 = (l.car)); + if ((((state = (sc_states_88[j])), (conf_set = (state[(sc_conf_89+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)) + return true; + else + (l = (l.cdr)); + } + else + return false; + } + } + else + return false; + } + }; + deriv_trees = function(sc_conf_91, i, j, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2) { + var sc_loop1_98; + var prev; + var name; + return ((name = (sc_names_94[sc_conf_91])), ((name!== false)?((sc_conf_91=(0))) + if (((k>=i)&&(((sc_state_99 = (sc_states_96[k])), (conf_set = (sc_state_99[(prev+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false))) + { + (prev_trees = (deriv_trees(prev, i, k, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2))); + (ender_trees = (deriv_trees(ender, k, j, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2))); + loop3 = function(l3, l2) { + var l4; + var sc_l2_100; + var ender_tree; + if ((l3 instanceof sc_Pair)) + { + (ender_tree = (sc_list((l3.car)))); + (l4 = prev_trees); + (sc_l2_100 = l2); + while ((l4 instanceof sc_Pair)) { + { + (sc_l2_100 = (new sc_Pair((sc_append((l4.car), ender_tree)), sc_l2_100))); + (l4 = (l4.cdr)); + } + } + return (loop3((l3.cdr), sc_l2_100)); + } + else + return (loop2((ender_set[(k+(5))]), l2)); + }; + return (loop3(ender_trees, l2)); + } + else + (k = (ender_set[(k+(5))])); + else + return (sc_loop1_98((l1.cdr), l2)); + } + }; + return (loop2((ender_set[(2)]), l2)); + } + else + (l1 = (l1.cdr)); + } + else + return l2; + } + }), (sc_loop1_98((sc_enders_92[(sc_steps_93[prev])]), null))))); + }; + BgL_sc_derivzd2treesza2_47z70 = function(nt, i, j, sc_nts_101, sc_enders_102, sc_steps_103, sc_names_104, sc_toks_105, sc_states_106) { + var conf_set; + var state; + var sc_conf_107; + var l; + var trees; + var BgL_sc_nbzd2nts_108zd2; + var BgL_sc_ntza2_109za2; + { + (BgL_sc_ntza2_109za2 = (sc_ind_43(nt, sc_nts_101))); + if ((BgL_sc_ntza2_109za2!== false)) + { + (BgL_sc_nbzd2nts_108zd2 = (sc_nts_101.length)); + (l = (sc_enders_102[BgL_sc_ntza2_109za2])); + (trees = null); + while ((l instanceof sc_Pair)) { + { + (sc_conf_107 = (l.car)); + if ((((state = (sc_states_106[j])), (conf_set = (state[(sc_conf_107+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)) + { + (l = (l.cdr)); + (trees = (sc_append((deriv_trees(sc_conf_107, i, j, sc_enders_102, sc_steps_103, sc_names_104, sc_toks_105, sc_states_106, BgL_sc_nbzd2nts_108zd2)), trees))); + } + else + (l = (l.cdr)); + } + } + return trees; + } + else + return false; + } + }; + nb_deriv_trees = function(sc_conf_110, i, j, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2) { + var sc_loop1_116; + var tmp1124; + var prev; + return ((prev = (sc_conf_110-(1))), ((((tmp1124 = (sc_conf_110=(0))) { + if (((k>=i)&&(((state = (sc_states_114[k])), (conf_set = (state[(prev+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false))) + { + (nb_prev_trees = (nb_deriv_trees(prev, i, k, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2))); + (nb_ender_trees = (nb_deriv_trees(ender, k, j, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2))); + (k = (ender_set[(k+(5))])); + (n +=(nb_prev_trees*nb_ender_trees)); + } + else + (k = (ender_set[(k+(5))])); + } + return (sc_loop1_116((l.cdr), n)); + } + else + (l = (l.cdr)); + } + else + return sc_n_118; + } + }), (sc_loop1_116((sc_enders_111[(sc_steps_112[prev])]), (0)))))); + }; + BgL_sc_nbzd2derivzd2treesza2_48za2 = function(nt, i, j, sc_nts_119, sc_enders_120, sc_steps_121, sc_toks_122, sc_states_123) { + var conf_set; + var state; + var sc_conf_124; + var l; + var nb_trees; + var BgL_sc_nbzd2nts_125zd2; + var BgL_sc_ntza2_126za2; + { + (BgL_sc_ntza2_126za2 = (sc_ind_43(nt, sc_nts_119))); + if ((BgL_sc_ntza2_126za2!== false)) + { + (BgL_sc_nbzd2nts_125zd2 = (sc_nts_119.length)); + (l = (sc_enders_120[BgL_sc_ntza2_126za2])); + (nb_trees = (0)); + while ((l instanceof sc_Pair)) { + { + (sc_conf_124 = (l.car)); + if ((((state = (sc_states_123[j])), (conf_set = (state[(sc_conf_124+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)) + { + (l = (l.cdr)); + (nb_trees = ((nb_deriv_trees(sc_conf_124, i, j, sc_enders_120, sc_steps_121, sc_toks_122, sc_states_123, BgL_sc_nbzd2nts_125zd2))+nb_trees)); + } + else + (l = (l.cdr)); + } + } + return nb_trees; + } + else + return false; + } + }; + (lexer = (parser_descr[(0)])); + (sc_nts_42 = (parser_descr[(1)])); + (sc_starters_41 = (parser_descr[(2)])); + (sc_enders_40 = (parser_descr[(3)])); + (sc_predictors_39 = (parser_descr[(4)])); + (sc_steps_38 = (parser_descr[(5)])); + (sc_names_37 = (parser_descr[(6)])); + (falseHead1128 = (new sc_Pair(null, null))); + (L1125 = (lexer(input))); + (tail1129 = falseHead1128); + while (!(L1125 === null)) { + { + (tok = (L1125.car)); + (l1 = (tok.cdr)); + (l2 = null); + while ((l1 instanceof sc_Pair)) { + { + (sc_i_29 = (sc_ind_43((l1.car), sc_nts_42))); + if ((sc_i_29!== false)) + { + (l1 = (l1.cdr)); + (l2 = (new sc_Pair(sc_i_29, l2))); + } + else + (l1 = (l1.cdr)); + } + } + (sc_optrOpnd_22 = (new sc_Pair((tok.car), (sc_reverse(l2))))); + (sc_optrOpnd_21 = (new sc_Pair(sc_optrOpnd_22, null))); + (tail1129.cdr = sc_optrOpnd_21); + (tail1129 = (tail1129.cdr)); + (L1125 = (L1125.cdr)); + } + } + (sc_optrOpnd_20 = (falseHead1128.cdr)); + (sc_toks_36 = (sc_list2vector(sc_optrOpnd_20))); + (BgL_sc_nbzd2toks_35zd2 = (sc_toks_36.length)); + (BgL_sc_nbzd2confs_34zd2 = (sc_steps_38.length)); + (sc_states_33 = (make_states(BgL_sc_nbzd2toks_35zd2, BgL_sc_nbzd2confs_34zd2))); + (goal_starters = (sc_starters_41[(0)])); + (BgL_sc_confzd2setzd2adjoinza2_45za2(sc_states_33, (0), goal_starters, (0))); + (forw(sc_states_33, (0), sc_starters_41, sc_enders_40, sc_predictors_39, sc_steps_38, sc_nts_42)); + (sc_i_28 = (0)); + while ((sc_i_28=(0))) { + { + (states = sc_states_33); + (BgL_sc_statesza2_27za2 = BgL_sc_statesza2_30za2); + (state_num = i); + (sc_enders_26 = sc_enders_40); + (sc_steps_25 = sc_steps_38); + (BgL_sc_nbzd2nts_24zd2 = BgL_sc_nbzd2nts_31zd2); + (toks = sc_toks_36); + (BgL_sc_stateza2_23za2 = (BgL_sc_statesza2_30za2[i])); + loop1 = function() { + var sc_loop1_127; + var prev; + var BgL_sc_statesza2_128za2; + var sc_states_129; + var j; + var i; + var sc_i_130; + var head; + var conf_set; + var sc_conf_131; + { + (sc_conf_131 = (BgL_sc_stateza2_23za2[(0)])); + if ((sc_conf_131>=(0))) + { + (conf_set = (BgL_sc_stateza2_23za2[(sc_conf_131+(1))])); + (head = (conf_set[(4)])); + (BgL_sc_stateza2_23za2[(0)] = (conf_set[(0)])); + (conf_set_merge_new_bang(conf_set)); + (sc_i_130 = head); + while ((sc_i_130>=(0))) { + { + (i = sc_i_130); + (j = state_num); + (sc_states_129 = states); + (BgL_sc_statesza2_128za2 = BgL_sc_statesza2_27za2); + (prev = (sc_conf_131-(1))); + if (((sc_conf_131>=BgL_sc_nbzd2nts_24zd2)&&((sc_steps_25[prev])>=(0)))) + { + sc_loop1_127 = function(l) { + var k; + var ender_set; + var state; + var ender; + var l; + while (true) { + if ((l instanceof sc_Pair)) + { + (ender = (l.car)); + (ender_set = ((state = (sc_states_129[j])), (state[(ender+(1))]))); + if ((ender_set!== false)) + { + (k = (ender_set[(2)])); + while ((k>=(0))) { + { + if ((k>=i)) + if (((BgL_sc_confzd2setzd2adjoinza2za2_46z00(sc_states_129, BgL_sc_statesza2_128za2, k, prev, i))!== false)) + (BgL_sc_confzd2setzd2adjoinza2za2_46z00(sc_states_129, BgL_sc_statesza2_128za2, j, ender, k)); + (k = (ender_set[(k+(5))])); + } + } + return (sc_loop1_127((l.cdr))); + } + else + (l = (l.cdr)); + } + else + return undefined; + } + }; + (sc_loop1_127((sc_enders_26[(sc_steps_25[prev])]))); + } + (sc_i_130 = (conf_set[(sc_i_130+(5))])); + } + } + return (loop1()); + } + else + return undefined; + } + }; + (loop1()); + (--i); + } + } + (optrOpnd = BgL_sc_statesza2_30za2); + return [sc_nts_42, sc_starters_41, sc_enders_40, sc_predictors_39, sc_steps_38, sc_names_37, sc_toks_36, optrOpnd, is_parsed, BgL_sc_derivzd2treesza2_47z70, BgL_sc_nbzd2derivzd2treesza2_48za2]; + } + }; + } + }; + BgL_parsezd2ze3parsedzf3zc2 = function(parse, nt, i, j) { + var is_parsed; + var states; + var enders; + var nts; + return ((nts = (parse[(0)])), (enders = (parse[(2)])), (states = (parse[(7)])), (is_parsed = (parse[(8)])), (is_parsed(nt, i, j, nts, enders, states))); + }; + BgL_parsezd2ze3treesz31 = function(parse, nt, i, j) { + var BgL_sc_derivzd2treesza2_132z70; + var states; + var toks; + var names; + var steps; + var enders; + var nts; + return ((nts = (parse[(0)])), (enders = (parse[(2)])), (steps = (parse[(4)])), (names = (parse[(5)])), (toks = (parse[(6)])), (states = (parse[(7)])), (BgL_sc_derivzd2treesza2_132z70 = (parse[(9)])), (BgL_sc_derivzd2treesza2_132z70(nt, i, j, nts, enders, steps, names, toks, states))); + }; + BgL_parsezd2ze3nbzd2treesze3 = function(parse, nt, i, j) { + var BgL_sc_nbzd2derivzd2treesza2_133za2; + var states; + var toks; + var steps; + var enders; + var nts; + return ((nts = (parse[(0)])), (enders = (parse[(2)])), (steps = (parse[(4)])), (toks = (parse[(6)])), (states = (parse[(7)])), (BgL_sc_nbzd2derivzd2treesza2_133za2 = (parse[(10)])), (BgL_sc_nbzd2derivzd2treesza2_133za2(nt, i, j, nts, enders, steps, toks, states))); + }; + test = function(k) { + var x; + var p; + return ((p = (BgL_makezd2parserzd2(const_earley, function(l) { + var sc_x_134; + var tail1134; + var L1130; + var falseHead1133; + { + (falseHead1133 = (new sc_Pair(null, null))); + (tail1134 = falseHead1133); + (L1130 = l); + while (!(L1130 === null)) { + { + (tail1134.cdr = (new sc_Pair(((sc_x_134 = (L1130.car)), (sc_list(sc_x_134, sc_x_134))), null))); + (tail1134 = (tail1134.cdr)); + (L1130 = (L1130.cdr)); + } + } + return (falseHead1133.cdr); + } + }))), (x = (p((sc_vector2list((sc_makeVector(k, "\u1E9Ca"))))))), (sc_length((BgL_parsezd2ze3treesz31(x, "\u1E9Cs", (0), k))))); + }; + BgL_earleyzd2benchmarkzd2 = function() { + var args = null; + for (var sc_tmp = arguments.length - 1; sc_tmp >= 0; sc_tmp--) { + args = sc_cons(arguments[sc_tmp], args); + } + var k; + return ((k = ((args === null)?(7):(args.car))), (BgL_runzd2benchmarkzd2("earley", (1), function() { + return (test(k)); + }, function(result) { + return ((sc_display(result)), (sc_newline()), result == 132); + }))); + }; +} + + +/************* END OF GENERATED CODE *************/ +// Invoke this function to run a benchmark. +// The first argument is a string identifying the benchmark. +// The second argument is the number of times to run the benchmark. +// The third argument is a function that runs the benchmark. +// The fourth argument is a unary function that warns if the result +// returned by the benchmark is incorrect. +// +// Example: +// RunBenchmark("new Array()", +// 1, +// function () { new Array(1000000); } +// function (v) { +// return (v instanceof Array) && (v.length == 1000000); +// }); + +SC_DEFAULT_OUT = new sc_GenericOutputPort(function(s) {}); +SC_ERROR_OUT = SC_DEFAULT_OUT; + +function RunBenchmark(name, count, run, warn) { + for (var n = 0; n < count; ++n) { + result = run(); + if (!warn(result)) { + throw new Error("Earley or Boyer did incorrect number of rewrites"); + } + } +} + +var BgL_runzd2benchmarkzd2 = RunBenchmark; +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Automatically generated on 2009-01-30. Manually updated on 2010-09-17. + +// This benchmark is generated by loading 50 of the most popular pages +// on the web and logging all regexp operations performed. Each +// operation is given a weight that is calculated from an estimate of +// the popularity of the pages where it occurs and the number of times +// it is executed while loading each page. Furthermore the literal +// letters in the data are encoded using ROT13 in a way that does not +// affect how the regexps match their input. Finally the strings are +// scrambled to exercise the regexp engine on different input strings. + + +var RegExp = new BenchmarkSuite('RegExp', 910985, [ + new Benchmark("RegExp", RegExpRun, RegExpSetup, RegExpTearDown) +]); + +var regExpBenchmark = null; + +function RegExpSetup() { + regExpBenchmark = new RegExpBenchmark(); + RegExpRun(); // run once to get system initialized +} + +function RegExpRun() { + regExpBenchmark.run(); +} + +function RegExpTearDown() { + regExpBenchmark = null; +} + +// Returns an array of n different variants of the input string str. +// The variants are computed by randomly rotating one random +// character. +function computeInputVariants(str, n) { + var variants = [ str ]; + for (var i = 1; i < n; i++) { + var pos = Math.floor(Math.random() * str.length); + var chr = String.fromCharCode((str.charCodeAt(pos) + Math.floor(Math.random() * 128)) % 128); + variants[i] = str.substring(0, pos) + chr + str.substring(pos + 1, str.length); + } + return variants; +} + +function RegExpBenchmark() { + var re0 = /^ba/; + var re1 = /(((\w+):\/\/)([^\/:]*)(:(\d+))?)?([^#?]*)(\?([^#]*))?(#(.*))?/; + var re2 = /^\s*|\s*$/g; + var re3 = /\bQBZPbageby_cynprubyqre\b/; + var re4 = /,/; + var re5 = /\bQBZPbageby_cynprubyqre\b/g; + var re6 = /^[\s\xa0]+|[\s\xa0]+$/g; + var re7 = /(\d*)(\D*)/g; + var re8 = /=/; + var re9 = /(^|\s)lhv\-h(\s|$)/; + var str0 = 'Zbmvyyn/5.0 (Jvaqbjf; H; Jvaqbjf AG 5.1; ra-HF) NccyrJroXvg/528.9 (XUGZY, yvxr Trpxb) Puebzr/2.0.157.0 Fnsnev/528.9'; + var re10 = /\#/g; + var re11 = /\./g; + var re12 = /'/g; + var re13 = /\?[\w\W]*(sevraqvq|punaaryvq|tebhcvq)=([^\&\?#]*)/i; + var str1 = 'Fubpxjnir Synfu 9.0 e115'; + var re14 = /\s+/g; + var re15 = /^\s*(\S*(\s+\S+)*)\s*$/; + var re16 = /(-[a-z])/i; + + var s0 = computeInputVariants('pyvpx', 6511); + var s1 = computeInputVariants('uggc://jjj.snprobbx.pbz/ybtva.cuc', 1844); + var s2 = computeInputVariants('QBZPbageby_cynprubyqre', 739); + var s3 = computeInputVariants('uggc://jjj.snprobbx.pbz/', 598); + var s4 = computeInputVariants('uggc://jjj.snprobbx.pbz/fepu.cuc', 454); + var s5 = computeInputVariants('qqqq, ZZZ q, llll', 352); + var s6 = computeInputVariants('vachggrkg QBZPbageby_cynprubyqre', 312); + var s7 = computeInputVariants('/ZlFcnprUbzrcntr/Vaqrk-FvgrUbzr,10000000', 282); + var s8 = computeInputVariants('vachggrkg', 177); + var s9 = computeInputVariants('528.9', 170); + var s10 = computeInputVariants('528', 170); + var s11 = computeInputVariants('VCPhygher=ra-HF', 156); + var s12 = computeInputVariants('CersreerqPhygher=ra-HF', 156); + var s13 = computeInputVariants('xrlcerff', 144); + var s14 = computeInputVariants('521', 139); + var s15 = computeInputVariants(str0, 139); + var s16 = computeInputVariants('qvi .so_zrah', 137); + var s17 = computeInputVariants('qvi.so_zrah', 137); + var s18 = computeInputVariants('uvqqra_ryrz', 117); + var s19 = computeInputVariants('sevraqfgre_naba=nvq%3Qn6ss9p85n868ro9s059pn854735956o3%26ers%3Q%26df%3Q%26vpgl%3QHF', 95); + var s20 = computeInputVariants('uggc://ubzr.zlfcnpr.pbz/vaqrk.psz', 93); + var s21 = computeInputVariants(str1, 92); + var s22 = computeInputVariants('svefg', 85); + var s23 = computeInputVariants('uggc://cebsvyr.zlfcnpr.pbz/vaqrk.psz', 85); + var s24 = computeInputVariants('ynfg', 85); + var s25 = computeInputVariants('qvfcynl', 85); + + function runBlock0() { + for (var i = 0; i < 6511; i++) { + re0.exec(s0[i]); + } + for (var i = 0; i < 1844; i++) { + re1.exec(s1[i]); + } + for (var i = 0; i < 739; i++) { + s2[i].replace(re2, ''); + } + for (var i = 0; i < 598; i++) { + re1.exec(s3[i]); + } + for (var i = 0; i < 454; i++) { + re1.exec(s4[i]); + } + for (var i = 0; i < 352; i++) { + /qqqq|qqq|qq|q|ZZZZ|ZZZ|ZZ|Z|llll|ll|l|uu|u|UU|U|zz|z|ff|f|gg|g|sss|ss|s|mmm|mm|m/g.exec(s5[i]); + } + for (var i = 0; i < 312; i++) { + re3.exec(s6[i]); + } + for (var i = 0; i < 282; i++) { + re4.exec(s7[i]); + } + for (var i = 0; i < 177; i++) { + s8[i].replace(re5, ''); + } + for (var i = 0; i < 170; i++) { + s9[i].replace(re6, ''); + re7.exec(s10[i]); + } + for (var i = 0; i < 156; i++) { + re8.exec(s11[i]); + re8.exec(s12[i]); + } + for (var i = 0; i < 144; i++) { + re0.exec(s13[i]); + } + for (var i = 0; i < 139; i++) { + s14[i].replace(re6, ''); + re7.exec(s14[i]); + re9.exec(''); + /JroXvg\/(\S+)/.exec(s15[i]); + } + for (var i = 0; i < 137; i++) { + s16[i].replace(re10, ''); + s16[i].replace(/\[/g, ''); + s17[i].replace(re11, ''); + } + for (var i = 0; i < 117; i++) { + s18[i].replace(re2, ''); + } + for (var i = 0; i < 95; i++) { + /(?:^|;)\s*sevraqfgre_ynat=([^;]*)/.exec(s19[i]); + } + for (var i = 0; i < 93; i++) { + s20[i].replace(re12, ''); + re13.exec(s20[i]); + } + for (var i = 0; i < 92; i++) { + s21[i].replace(/([a-zA-Z]|\s)+/, ''); + } + for (var i = 0; i < 85; i++) { + s22[i].replace(re14, ''); + s22[i].replace(re15, ''); + s23[i].replace(re12, ''); + s24[i].replace(re14, ''); + s24[i].replace(re15, ''); + re16.exec(s25[i]); + re13.exec(s23[i]); + } + } + var re17 = /(^|[^\\])\"\\\/Qngr\((-?[0-9]+)\)\\\/\"/g; + var str2 = '{"anzr":"","ahzoreSbezng":{"PheeraplQrpvznyQvtvgf":2,"PheeraplQrpvznyFrcnengbe":".","VfErnqBayl":gehr,"PheeraplTebhcFvmrf":[3],"AhzoreTebhcFvmrf":[3],"CrepragTebhcFvmrf":[3],"PheeraplTebhcFrcnengbe":",","PheeraplFlzoby":"\xa4","AnAFlzoby":"AnA","PheeraplArtngvirCnggrea":0,"AhzoreArtngvirCnggrea":1,"CrepragCbfvgvirCnggrea":0,"CrepragArtngvirCnggrea":0,"ArtngvirVasvavglFlzoby":"-Vasvavgl","ArtngvirFvta":"-","AhzoreQrpvznyQvtvgf":2,"AhzoreQrpvznyFrcnengbe":".","AhzoreTebhcFrcnengbe":",","PheeraplCbfvgvirCnggrea":0,"CbfvgvirVasvavglFlzoby":"Vasvavgl","CbfvgvirFvta":"+","CrepragQrpvznyQvtvgf":2,"CrepragQrpvznyFrcnengbe":".","CrepragTebhcFrcnengbe":",","CrepragFlzoby":"%","CreZvyyrFlzoby":"\u2030","AngvirQvtvgf":["0","1","2","3","4","5","6","7","8","9"],"QvtvgFhofgvghgvba":1},"qngrGvzrSbezng":{"NZQrfvtangbe":"NZ","Pnyraqne":{"ZvaFhccbegrqQngrGvzr":"@-62135568000000@","ZnkFhccbegrqQngrGvzr":"@253402300799999@","NytbevguzGlcr":1,"PnyraqneGlcr":1,"Renf":[1],"GjbQvtvgLrneZnk":2029,"VfErnqBayl":gehr},"QngrFrcnengbe":"/","SvefgQnlBsJrrx":0,"PnyraqneJrrxEhyr":0,"ShyyQngrGvzrCnggrea":"qqqq, qq ZZZZ llll UU:zz:ff","YbatQngrCnggrea":"qqqq, qq ZZZZ llll","YbatGvzrCnggrea":"UU:zz:ff","ZbaguQnlCnggrea":"ZZZZ qq","CZQrfvtangbe":"CZ","ESP1123Cnggrea":"qqq, qq ZZZ llll UU\':\'zz\':\'ff \'TZG\'","FubegQngrCnggrea":"ZZ/qq/llll","FubegGvzrCnggrea":"UU:zz","FbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq\'G\'UU\':\'zz\':\'ff","GvzrFrcnengbe":":","HavirefnyFbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq UU\':\'zz\':\'ff\'M\'","LrneZbaguCnggrea":"llll ZZZZ","NooerivngrqQnlAnzrf":["Fha","Zba","Ghr","Jrq","Guh","Sev","Fng"],"FubegrfgQnlAnzrf":["Fh","Zb","Gh","Jr","Gu","Se","Fn"],"QnlAnzrf":["Fhaqnl","Zbaqnl","Ghrfqnl","Jrqarfqnl","Guhefqnl","Sevqnl","Fngheqnl"],"NooerivngrqZbaguAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""],"VfErnqBayl":gehr,"AngvirPnyraqneAnzr":"Tertbevna Pnyraqne","NooerivngrqZbaguTravgvirAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguTravgvirAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""]}}'; + var str3 = '{"anzr":"ra-HF","ahzoreSbezng":{"PheeraplQrpvznyQvtvgf":2,"PheeraplQrpvznyFrcnengbe":".","VfErnqBayl":snyfr,"PheeraplTebhcFvmrf":[3],"AhzoreTebhcFvmrf":[3],"CrepragTebhcFvmrf":[3],"PheeraplTebhcFrcnengbe":",","PheeraplFlzoby":"$","AnAFlzoby":"AnA","PheeraplArtngvirCnggrea":0,"AhzoreArtngvirCnggrea":1,"CrepragCbfvgvirCnggrea":0,"CrepragArtngvirCnggrea":0,"ArtngvirVasvavglFlzoby":"-Vasvavgl","ArtngvirFvta":"-","AhzoreQrpvznyQvtvgf":2,"AhzoreQrpvznyFrcnengbe":".","AhzoreTebhcFrcnengbe":",","PheeraplCbfvgvirCnggrea":0,"CbfvgvirVasvavglFlzoby":"Vasvavgl","CbfvgvirFvta":"+","CrepragQrpvznyQvtvgf":2,"CrepragQrpvznyFrcnengbe":".","CrepragTebhcFrcnengbe":",","CrepragFlzoby":"%","CreZvyyrFlzoby":"\u2030","AngvirQvtvgf":["0","1","2","3","4","5","6","7","8","9"],"QvtvgFhofgvghgvba":1},"qngrGvzrSbezng":{"NZQrfvtangbe":"NZ","Pnyraqne":{"ZvaFhccbegrqQngrGvzr":"@-62135568000000@","ZnkFhccbegrqQngrGvzr":"@253402300799999@","NytbevguzGlcr":1,"PnyraqneGlcr":1,"Renf":[1],"GjbQvtvgLrneZnk":2029,"VfErnqBayl":snyfr},"QngrFrcnengbe":"/","SvefgQnlBsJrrx":0,"PnyraqneJrrxEhyr":0,"ShyyQngrGvzrCnggrea":"qqqq, ZZZZ qq, llll u:zz:ff gg","YbatQngrCnggrea":"qqqq, ZZZZ qq, llll","YbatGvzrCnggrea":"u:zz:ff gg","ZbaguQnlCnggrea":"ZZZZ qq","CZQrfvtangbe":"CZ","ESP1123Cnggrea":"qqq, qq ZZZ llll UU\':\'zz\':\'ff \'TZG\'","FubegQngrCnggrea":"Z/q/llll","FubegGvzrCnggrea":"u:zz gg","FbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq\'G\'UU\':\'zz\':\'ff","GvzrFrcnengbe":":","HavirefnyFbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq UU\':\'zz\':\'ff\'M\'","LrneZbaguCnggrea":"ZZZZ, llll","NooerivngrqQnlAnzrf":["Fha","Zba","Ghr","Jrq","Guh","Sev","Fng"],"FubegrfgQnlAnzrf":["Fh","Zb","Gh","Jr","Gu","Se","Fn"],"QnlAnzrf":["Fhaqnl","Zbaqnl","Ghrfqnl","Jrqarfqnl","Guhefqnl","Sevqnl","Fngheqnl"],"NooerivngrqZbaguAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""],"VfErnqBayl":snyfr,"AngvirPnyraqneAnzr":"Tertbevna Pnyraqne","NooerivngrqZbaguTravgvirAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguTravgvirAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""]}}'; + var str4 = 'HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str5 = 'HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; + var re18 = /^\s+|\s+$/g; + var str6 = 'uggc://jjj.snprobbx.pbz/vaqrk.cuc'; + var re19 = /(?:^|\s+)ba(?:\s+|$)/; + var re20 = /[+, ]/; + var re21 = /ybnqrq|pbzcyrgr/; + var str7 = ';;jvaqbj.IjPurpxZbhfrCbfvgvbaNQ_VQ=shapgvba(r){vs(!r)ine r=jvaqbj.rirag;ine c=-1;vs(d1)c=d1.EbyybssCnary;ine bo=IjTrgBow("IjCnayNQ_VQ_"+c);vs(bo&&bo.fglyr.ivfvovyvgl=="ivfvoyr"){ine fns=IjFns?8:0;ine pheK=r.pyvragK+IjBOFpe("U")+fns,pheL=r.pyvragL+IjBOFpe("I")+fns;ine y=IjBOEC(NQ_VQ,bo,"Y"),g=IjBOEC(NQ_VQ,bo,"G");ine e=y+d1.Cnaryf[c].Jvqgu,o=g+d1.Cnaryf[c].Urvtug;vs((pheKe)||(pheLo)){vs(jvaqbj.IjBaEbyybssNQ_VQ)IjBaEbyybssNQ_VQ(c);ryfr IjPybfrNq(NQ_VQ,c,gehr,"");}ryfr erghea;}IjPnapryZbhfrYvfgrareNQ_VQ();};;jvaqbj.IjFrgEbyybssCnaryNQ_VQ=shapgvba(c){ine z="zbhfrzbir",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;c=IjTc(NQ_VQ,c);vs(d1&&d1.EbyybssCnary>-1)IjPnapryZbhfrYvfgrareNQ_VQ();vs(d1)d1.EbyybssCnary=c;gel{vs(q.nqqRiragYvfgrare)q.nqqRiragYvfgrare(z,s,snyfr);ryfr vs(q.nggnpuRirag)q.nggnpuRirag("ba"+z,s);}pngpu(r){}};;jvaqbj.IjPnapryZbhfrYvfgrareNQ_VQ=shapgvba(){ine z="zbhfrzbir",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;vs(d1)d1.EbyybssCnary=-1;gel{vs(q.erzbirRiragYvfgrare)q.erzbirRiragYvfgrare(z,s,snyfr);ryfr vs(q.qrgnpuRirag)q.qrgnpuRirag("ba"+z,s);}pngpu(r){}};;d1.IjTc=d2(n,c){ine nq=d1;vs(vfAnA(c)){sbe(ine v=0;v0){vs(nq.FzV.yratgu>0)nq.FzV+="/";nq.FzV+=vh[v];nq.FtZ[nq.FtZ.yratgu]=snyfr;}}};;d1.IjYvzvg0=d2(n,f){ine nq=d1,vh=f.fcyvg("/");sbe(ine v=0;v0){vs(nq.OvC.yratgu>0)nq.OvC+="/";nq.OvC+=vh[v];}}};;d1.IjRVST=d2(n,c){jvaqbj["IjCnayNQ_VQ_"+c+"_Bow"]=IjTrgBow("IjCnayNQ_VQ_"+c+"_Bow");vs(jvaqbj["IjCnayNQ_VQ_"+c+"_Bow"]==ahyy)frgGvzrbhg("IjRVST(NQ_VQ,"+c+")",d1.rvsg);};;d1.IjNavzSHC=d2(n,c){ine nq=d1;vs(c>nq.Cnaryf.yratgu)erghea;ine cna=nq.Cnaryf[c],nn=gehr,on=gehr,yn=gehr,en=gehr,cn=nq.Cnaryf[0],sf=nq.ShF,j=cn.Jvqgu,u=cn.Urvtug;vs(j=="100%"){j=sf;en=snyfr;yn=snyfr;}vs(u=="100%"){u=sf;nn=snyfr;on=snyfr;}vs(cn.YnY=="Y")yn=snyfr;vs(cn.YnY=="E")en=snyfr;vs(cn.GnY=="G")nn=snyfr;vs(cn.GnY=="O")on=snyfr;ine k=0,l=0;fjvgpu(nq.NshP%8){pnfr 0:oernx;pnfr 1:vs(nn)l=-sf;oernx;pnfr 2:k=j-sf;oernx;pnfr 3:vs(en)k=j;oernx;pnfr 4:k=j-sf;l=u-sf;oernx;pnfr 5:k=j-sf;vs(on)l=u;oernx;pnfr 6:l=u-sf;oernx;pnfr 7:vs(yn)k=-sf;l=u-sf;oernx;}vs(nq.NshP++ 0)||(nethzragf.yratgu==3&&bG>0))){pyrneGvzrbhg(cay.UgU);cay.UgU=frgGvzrbhg(cay.UvqrNpgvba,(nethzragf.yratgu==3?bG:cay.UvqrGvzrbhgInyhr));}};;d1.IjErfrgGvzrbhg=d2(n,c,bG){c=IjTc(n,c);IjPnapryGvzrbhg(n,c);riny("IjFgnegGvzrbhg(NQ_VQ,c"+(nethzragf.yratgu==3?",bG":"")+")");};;d1.IjErfrgNyyGvzrbhgf=d2(n){sbe(ine c=0;c]/g; + var str15 = 'FrffvbaQQS2=s6r4579npn4rn2135s904r0s75pp1o5334p6s6pospo12696; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669316860113296&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzs-aowb_80=44132r503660'; + var str16 = 'FrffvbaQQS2=s6r4579npn4rn2135s904r0s75pp1o5334p6s6pospo12696; AFP_zp_dfctwzs-aowb_80=44132r503660; __hgzm=144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.965867047679498800.1231363638.1231363638.1231363638.1; __hgzo=144631658.0.10.1231363638; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669316860113296&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str17 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231363621014&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231363621014&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyr.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=348699119.1231363624&tn_fvq=1231363624&tn_uvq=895511034&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; + var str18 = 'uggc://jjj.yrobapbva.se/yv'; + var str19 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669316860113296&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str20 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669316860113296&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; + + var s67 = computeInputVariants('e115', 27); + var s68 = computeInputVariants('qvfcynl', 27); + var s69 = computeInputVariants('cbfvgvba', 27); + var s70 = computeInputVariants('uggc://jjj.zlfcnpr.pbz/', 27); + var s71 = computeInputVariants('cntrivrj', 27); + var s72 = computeInputVariants('VC=74.125.75.3', 27); + var s73 = computeInputVariants('ra', 27); + var s74 = computeInputVariants(str10, 27); + var s75 = computeInputVariants(str11, 27); + var s76 = computeInputVariants(str12, 27); + var s77 = computeInputVariants(str17, 27); + var s78 = computeInputVariants(str18, 27); + + function runBlock3() { + for (var i = 0; i < 27; i++) { + s67[i].replace(/[A-Za-z]/g, ''); + } + for (var i = 0; i < 23; i++) { + s68[i].replace(re27, ''); + s69[i].replace(re27, ''); + } + for (var i = 0; i < 22; i++) { + 'unaqyr'.replace(re14, ''); + 'unaqyr'.replace(re15, ''); + 'yvar'.replace(re14, ''); + 'yvar'.replace(re15, ''); + 'cnerag puebzr6 fvatyr1 gno'.replace(re14, ''); + 'cnerag puebzr6 fvatyr1 gno'.replace(re15, ''); + 'fyvqre'.replace(re14, ''); + 'fyvqre'.replace(re15, ''); + re28.exec(''); + } + for (var i = 0; i < 21; i++) { + s70[i].replace(re12, ''); + re13.exec(s70[i]); + } + for (var i = 0; i < 20; i++) { + s71[i].replace(re29, ''); + s71[i].replace(re30, ''); + re19.exec('ynfg'); + re19.exec('ba svefg'); + re8.exec(s72[i]); + } + for (var i = 0; i < 19; i++) { + re31.exec(s73[i]); + } + for (var i = 0; i < 18; i++) { + s74[i].split(re32); + s75[i].split(re32); + s76[i].replace(re33, ''); + re8.exec('144631658.0.10.1231363570'); + re8.exec('144631658.1231363570.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re8.exec('144631658.3426875219718084000.1231363570.1231363570.1231363570.1'); + re8.exec(str13); + re8.exec(str14); + re8.exec('__hgzn=144631658.3426875219718084000.1231363570.1231363570.1231363570.1'); + re8.exec('__hgzo=144631658.0.10.1231363570'); + re8.exec('__hgzm=144631658.1231363570.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re34.exec(s74[i]); + re34.exec(s75[i]); + } + for (var i = 0; i < 17; i++) { + s15[i].match(/zfvr/gi); + s15[i].match(/bcren/gi); + str15.split(re32); + str16.split(re32); + 'ohggba'.replace(re14, ''); + 'ohggba'.replace(re15, ''); + 'puvyq p1 svefg sylbhg pybfrq'.replace(re14, ''); + 'puvyq p1 svefg sylbhg pybfrq'.replace(re15, ''); + 'pvgvrf'.replace(re14, ''); + 'pvgvrf'.replace(re15, ''); + 'pybfrq'.replace(re14, ''); + 'pybfrq'.replace(re15, ''); + 'qry'.replace(re14, ''); + 'qry'.replace(re15, ''); + 'uqy_zba'.replace(re14, ''); + 'uqy_zba'.replace(re15, ''); + s77[i].replace(re33, ''); + s78[i].replace(/%3P/g, ''); + s78[i].replace(/%3R/g, ''); + s78[i].replace(/%3q/g, ''); + s78[i].replace(re35, ''); + 'yvaxyvfg16'.replace(re14, ''); + 'yvaxyvfg16'.replace(re15, ''); + 'zvahf'.replace(re14, ''); + 'zvahf'.replace(re15, ''); + 'bcra'.replace(re14, ''); + 'bcra'.replace(re15, ''); + 'cnerag puebzr5 fvatyr1 ps NU'.replace(re14, ''); + 'cnerag puebzr5 fvatyr1 ps NU'.replace(re15, ''); + 'cynlre'.replace(re14, ''); + 'cynlre'.replace(re15, ''); + 'cyhf'.replace(re14, ''); + 'cyhf'.replace(re15, ''); + 'cb_uqy'.replace(re14, ''); + 'cb_uqy'.replace(re15, ''); + 'hyJVzt'.replace(re14, ''); + 'hyJVzt'.replace(re15, ''); + re8.exec('144631658.0.10.1231363638'); + re8.exec('144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re8.exec('144631658.965867047679498800.1231363638.1231363638.1231363638.1'); + re8.exec('4413268q3660'); + re8.exec('4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n'); + re8.exec('SbeprqRkcvengvba=633669321699093060'); + re8.exec('VC=74.125.75.20'); + re8.exec(str19); + re8.exec(str20); + re8.exec('AFP_zp_tfwsbrg-aowb_80=4413268q3660'); + re8.exec('FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n'); + re8.exec('__hgzn=144631658.965867047679498800.1231363638.1231363638.1231363638.1'); + re8.exec('__hgzo=144631658.0.10.1231363638'); + re8.exec('__hgzm=144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re34.exec(str15); + re34.exec(str16); + } + } + var re36 = /uers|fep|fryrpgrq/; + var re37 = /\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g; + var re38 = /^(\w+|\*)$/; + var str21 = 'FrffvbaQQS2=s15q53p9n372sn76npr13o271n4s3p5r29p235746p908p58; ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669358527244818&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; + var str22 = 'FrffvbaQQS2=s15q53p9n372sn76npr13o271n4s3p5r29p235746p908p58; __hgzm=144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.4127520630321984500.1231367822.1231367822.1231367822.1; __hgzo=144631658.0.10.1231367822; __hgzp=144631658; ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669358527244818&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str23 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231367803797&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367803797&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Szrffntvat.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1192552091.1231367807&tn_fvq=1231367807&tn_uvq=1155446857&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; + var str24 = 'ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669358527244818&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str25 = 'ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669358527244818&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; + var str26 = 'hy.ynat-fryrpgbe'; + var re39 = /\\/g; + var re40 = / /g; + var re41 = /\/\xc4\/t/; + var re42 = /\/\xd6\/t/; + var re43 = /\/\xdc\/t/; + var re44 = /\/\xdf\/t/; + var re45 = /\/\xe4\/t/; + var re46 = /\/\xf6\/t/; + var re47 = /\/\xfc\/t/; + var re48 = /\W/g; + var re49 = /uers|fep|fglyr/; + var s79 = computeInputVariants(str21, 16); + var s80 = computeInputVariants(str22, 16); + var s81 = computeInputVariants(str23, 16); + var s82 = computeInputVariants(str26, 16); + + function runBlock4() { + for (var i = 0; i < 16; i++) { + ''.replace(/\*/g, ''); + /\bnpgvir\b/.exec('npgvir'); + /sversbk/i.exec(s15[i]); + re36.exec('glcr'); + /zfvr/i.exec(s15[i]); + /bcren/i.exec(s15[i]); + } + for (var i = 0; i < 15; i++) { + s79[i].split(re32); + s80[i].split(re32); + 'uggc://ohyyrgvaf.zlfcnpr.pbz/vaqrk.psz'.replace(re12, ''); + s81[i].replace(re33, ''); + 'yv'.replace(re37, ''); + 'yv'.replace(re18, ''); + re8.exec('144631658.0.10.1231367822'); + re8.exec('144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re8.exec('144631658.4127520630321984500.1231367822.1231367822.1231367822.1'); + re8.exec(str24); + re8.exec(str25); + re8.exec('__hgzn=144631658.4127520630321984500.1231367822.1231367822.1231367822.1'); + re8.exec('__hgzo=144631658.0.10.1231367822'); + re8.exec('__hgzm=144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re34.exec(s79[i]); + re34.exec(s80[i]); + /\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)["']?(.*?)["']?)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g.exec(s82[i]); + re13.exec('uggc://ohyyrgvaf.zlfcnpr.pbz/vaqrk.psz'); + re38.exec('yv'); + } + for (var i = 0; i < 14; i++) { + ''.replace(re18, ''); + '9.0 e115'.replace(/(\s+e|\s+o[0-9]+)/, ''); + 'Funer guvf tnqtrg'.replace(//g, ''); + 'Funer guvf tnqtrg'.replace(re39, ''); + 'uggc://cebsvyrrqvg.zlfcnpr.pbz/vaqrk.psz'.replace(re12, ''); + 'grnfre'.replace(re40, ''); + 'grnfre'.replace(re41, ''); + 'grnfre'.replace(re42, ''); + 'grnfre'.replace(re43, ''); + 'grnfre'.replace(re44, ''); + 'grnfre'.replace(re45, ''); + 'grnfre'.replace(re46, ''); + 'grnfre'.replace(re47, ''); + 'grnfre'.replace(re48, ''); + re16.exec('znetva-gbc'); + re16.exec('cbfvgvba'); + re19.exec('gno1'); + re9.exec('qz'); + re9.exec('qg'); + re9.exec('zbqobk'); + re9.exec('zbqobkva'); + re9.exec('zbqgvgyr'); + re13.exec('uggc://cebsvyrrqvg.zlfcnpr.pbz/vaqrk.psz'); + re26.exec('/vt/znvytnqtrg'); + re49.exec('glcr'); + } + } + var re50 = /(?:^|\s+)fryrpgrq(?:\s+|$)/; + var re51 = /\&/g; + var re52 = /\+/g; + var re53 = /\?/g; + var re54 = /\t/g; + var re55 = /(\$\{nqiHey\})|(\$nqiHey\b)/g; + var re56 = /(\$\{cngu\})|(\$cngu\b)/g; + function runBlock5() { + for (var i = 0; i < 13; i++) { + 'purpx'.replace(re14, ''); + 'purpx'.replace(re15, ''); + 'pvgl'.replace(re14, ''); + 'pvgl'.replace(re15, ''); + 'qrpe fyvqrgrkg'.replace(re14, ''); + 'qrpe fyvqrgrkg'.replace(re15, ''); + 'svefg fryrpgrq'.replace(re14, ''); + 'svefg fryrpgrq'.replace(re15, ''); + 'uqy_rag'.replace(re14, ''); + 'uqy_rag'.replace(re15, ''); + 'vape fyvqrgrkg'.replace(re14, ''); + 'vape fyvqrgrkg'.replace(re15, ''); + 'vachggrkg QBZPbageby_cynprubyqre'.replace(re5, ''); + 'cnerag puebzr6 fvatyr1 gno fryrpgrq'.replace(re14, ''); + 'cnerag puebzr6 fvatyr1 gno fryrpgrq'.replace(re15, ''); + 'cb_guz'.replace(re14, ''); + 'cb_guz'.replace(re15, ''); + 'fhozvg'.replace(re14, ''); + 'fhozvg'.replace(re15, ''); + re50.exec(''); + /NccyrJroXvg\/([^\s]*)/.exec(s15[i]); + /XUGZY/.exec(s15[i]); + } + for (var i = 0; i < 12; i++) { + '${cebg}://${ubfg}${cngu}/${dz}'.replace(/(\$\{cebg\})|(\$cebg\b)/g, ''); + '1'.replace(re40, ''); + '1'.replace(re10, ''); + '1'.replace(re51, ''); + '1'.replace(re52, ''); + '1'.replace(re53, ''); + '1'.replace(re39, ''); + '1'.replace(re54, ''); + '9.0 e115'.replace(/^(.*)\..*$/, ''); + '9.0 e115'.replace(/^.*e(.*)$/, ''); + ''.replace(re55, ''); + ''.replace(re55, ''); + s21[i].replace(/^.*\s+(\S+\s+\S+$)/, ''); + 'tzk%2Subzrcntr%2Sfgneg%2Sqr%2S'.replace(re30, ''); + 'tzk'.replace(re30, ''); + 'uggc://${ubfg}${cngu}/${dz}'.replace(/(\$\{ubfg\})|(\$ubfg\b)/g, ''); + 'uggc://nqpyvrag.hvzfrei.arg${cngu}/${dz}'.replace(re56, ''); + 'uggc://nqpyvrag.hvzfrei.arg/wf.at/${dz}'.replace(/(\$\{dz\})|(\$dz\b)/g, ''); + 'frpgvba'.replace(re29, ''); + 'frpgvba'.replace(re30, ''); + 'fvgr'.replace(re29, ''); + 'fvgr'.replace(re30, ''); + 'fcrpvny'.replace(re29, ''); + 'fcrpvny'.replace(re30, ''); + re36.exec('anzr'); + /e/.exec('9.0 e115'); + } + } + var re57 = /##yv4##/gi; + var re58 = /##yv16##/gi; + var re59 = /##yv19##/gi; + var str27 = '##yv4##Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.##yv16##Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##'; + var str28 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.##yv16##Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##'; + var str29 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##'; + var str30 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##'; + var str31 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl. ##N##Yrnea zber##/N##'; + var str32 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl. Yrnea zber##/N##'; + var str33 = 'Bar Jvaqbjf Yvir VQ trgf lbh vagb Ubgznvy, Zrffratre, Kobk YVIR \u2014 naq bgure cynprf lbh frr #~#argjbexybtb#~#'; + var re60 = /(?:^|\s+)bss(?:\s+|$)/; + var re61 = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/; + var re62 = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/; + var str34 = '${1}://${2}${3}${4}${5}'; + var str35 = ' O=6gnyg0g4znrrn&o=3&f=gc; Q=_lyu=K3bQZGSxnT4lZzD3OS9GNmV3ZGLkAQxRpTyxNmRlZmRmAmNkAQLRqTImqNZjOUEgpTjQnJ5xMKtgoN--; SCF=qy'; + var s83 = computeInputVariants(str27, 11); + var s84 = computeInputVariants(str28, 11); + var s85 = computeInputVariants(str29, 11); + var s86 = computeInputVariants(str30, 11); + var s87 = computeInputVariants(str31, 11); + var s88 = computeInputVariants(str32, 11); + var s89 = computeInputVariants(str33, 11); + var s90 = computeInputVariants(str34, 11); + + function runBlock6() { + for (var i = 0; i < 11; i++) { + s83[i].replace(/##yv0##/gi, ''); + s83[i].replace(re57, ''); + s84[i].replace(re58, ''); + s85[i].replace(re59, ''); + s86[i].replace(/##\/o##/gi, ''); + s86[i].replace(/##\/v##/gi, ''); + s86[i].replace(/##\/h##/gi, ''); + s86[i].replace(/##o##/gi, ''); + s86[i].replace(/##oe##/gi, ''); + s86[i].replace(/##v##/gi, ''); + s86[i].replace(/##h##/gi, ''); + s87[i].replace(/##n##/gi, ''); + s88[i].replace(/##\/n##/gi, ''); + s89[i].replace(/#~#argjbexybtb#~#/g, ''); + / Zbovyr\//.exec(s15[i]); + /##yv1##/gi.exec(s83[i]); + /##yv10##/gi.exec(s84[i]); + /##yv11##/gi.exec(s84[i]); + /##yv12##/gi.exec(s84[i]); + /##yv13##/gi.exec(s84[i]); + /##yv14##/gi.exec(s84[i]); + /##yv15##/gi.exec(s84[i]); + re58.exec(s84[i]); + /##yv17##/gi.exec(s85[i]); + /##yv18##/gi.exec(s85[i]); + re59.exec(s85[i]); + /##yv2##/gi.exec(s83[i]); + /##yv20##/gi.exec(s86[i]); + /##yv21##/gi.exec(s86[i]); + /##yv22##/gi.exec(s86[i]); + /##yv23##/gi.exec(s86[i]); + /##yv3##/gi.exec(s83[i]); + re57.exec(s83[i]); + /##yv5##/gi.exec(s84[i]); + /##yv6##/gi.exec(s84[i]); + /##yv7##/gi.exec(s84[i]); + /##yv8##/gi.exec(s84[i]); + /##yv9##/gi.exec(s84[i]); + re8.exec('473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29'); + re8.exec('SbeprqRkcvengvba=633669325184628362'); + re8.exec('FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29'); + /AbxvnA[^\/]*/.exec(s15[i]); + } + for (var i = 0; i < 10; i++) { + ' bss'.replace(/(?:^|\s+)bss(?:\s+|$)/g, ''); + s90[i].replace(/(\$\{0\})|(\$0\b)/g, ''); + s90[i].replace(/(\$\{1\})|(\$1\b)/g, ''); + s90[i].replace(/(\$\{pbzcyrgr\})|(\$pbzcyrgr\b)/g, ''); + s90[i].replace(/(\$\{sentzrag\})|(\$sentzrag\b)/g, ''); + s90[i].replace(/(\$\{ubfgcbeg\})|(\$ubfgcbeg\b)/g, ''); + s90[i].replace(re56, ''); + s90[i].replace(/(\$\{cebgbpby\})|(\$cebgbpby\b)/g, ''); + s90[i].replace(/(\$\{dhrel\})|(\$dhrel\b)/g, ''); + 'nqfvmr'.replace(re29, ''); + 'nqfvmr'.replace(re30, ''); + 'uggc://${2}${3}${4}${5}'.replace(/(\$\{2\})|(\$2\b)/g, ''); + 'uggc://wf.hv-cbegny.qr${3}${4}${5}'.replace(/(\$\{3\})|(\$3\b)/g, ''); + 'arjf'.replace(re40, ''); + 'arjf'.replace(re41, ''); + 'arjf'.replace(re42, ''); + 'arjf'.replace(re43, ''); + 'arjf'.replace(re44, ''); + 'arjf'.replace(re45, ''); + 'arjf'.replace(re46, ''); + 'arjf'.replace(re47, ''); + 'arjf'.replace(re48, ''); + / PC=i=(\d+)&oe=(.)/.exec(str35); + re60.exec(' '); + re60.exec(' bss'); + re60.exec(''); + re19.exec(' '); + re19.exec('svefg ba'); + re19.exec('ynfg vtaber'); + re19.exec('ba'); + re9.exec('scnq so '); + re9.exec('zrqvgobk'); + re9.exec('hsgy'); + re9.exec('lhv-h'); + /Fnsnev|Xbadhrebe|XUGZY/gi.exec(s15[i]); + re61.exec('uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf'); + re62.exec('#Ybtva_rznvy'); + } + } + var re63 = /\{0\}/g; + var str36 = 'FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n; ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669321699093060&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_tfwsbrg-aowb_80=4413268q3660'; + var str37 = 'FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n; AFP_zp_tfwsbrg-aowb_80=4413268q3660; __hgzm=144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.2294274870215848400.1231364074.1231364074.1231364074.1; __hgzo=144631658.0.10.1231364074; __hgzp=144631658; ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669321699093060&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str38 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231364057761&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231364057761&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Ssevraqf.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1667363813.1231364061&tn_fvq=1231364061&tn_uvq=1917563877&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; + var str39 = 'ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669321699093060&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str40 = 'ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669321699093060&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; + var s91 = computeInputVariants(str36, 9); + var s92 = computeInputVariants(str37, 9); + var s93 = computeInputVariants(str38, 9); + function runBlock7() { + for (var i = 0; i < 9; i++) { + '0'.replace(re40, ''); + '0'.replace(re10, ''); + '0'.replace(re51, ''); + '0'.replace(re52, ''); + '0'.replace(re53, ''); + '0'.replace(re39, ''); + '0'.replace(re54, ''); + 'Lrf'.replace(re40, ''); + 'Lrf'.replace(re10, ''); + 'Lrf'.replace(re51, ''); + 'Lrf'.replace(re52, ''); + 'Lrf'.replace(re53, ''); + 'Lrf'.replace(re39, ''); + 'Lrf'.replace(re54, ''); + } + for (var i = 0; i < 8; i++) { + 'Pybfr {0}'.replace(re63, ''); + 'Bcra {0}'.replace(re63, ''); + s91[i].split(re32); + s92[i].split(re32); + 'puvyq p1 svefg gnournqref'.replace(re14, ''); + 'puvyq p1 svefg gnournqref'.replace(re15, ''); + 'uqy_fcb'.replace(re14, ''); + 'uqy_fcb'.replace(re15, ''); + 'uvag'.replace(re14, ''); + 'uvag'.replace(re15, ''); + s93[i].replace(re33, ''); + 'yvfg'.replace(re14, ''); + 'yvfg'.replace(re15, ''); + 'at_bhgre'.replace(re30, ''); + 'cnerag puebzr5 qbhoyr2 NU'.replace(re14, ''); + 'cnerag puebzr5 qbhoyr2 NU'.replace(re15, ''); + 'cnerag puebzr5 dhnq5 ps NU osyvax zbarl'.replace(re14, ''); + 'cnerag puebzr5 dhnq5 ps NU osyvax zbarl'.replace(re15, ''); + 'cnerag puebzr6 fvatyr1'.replace(re14, ''); + 'cnerag puebzr6 fvatyr1'.replace(re15, ''); + 'cb_qrs'.replace(re14, ''); + 'cb_qrs'.replace(re15, ''); + 'gnopbagrag'.replace(re14, ''); + 'gnopbagrag'.replace(re15, ''); + 'iv_svefg_gvzr'.replace(re30, ''); + /(^|.)(ronl|qri-ehf3.wbg)(|fgberf|zbgbef|yvirnhpgvbaf|jvxv|rkcerff|punggre).(pbz(|.nh|.pa|.ux|.zl|.ft|.oe|.zk)|pb(.hx|.xe|.am)|pn|qr|se|vg|ay|or|ng|pu|vr|va|rf|cy|cu|fr)$/i.exec('cntrf.ronl.pbz'); + re8.exec('144631658.0.10.1231364074'); + re8.exec('144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re8.exec('144631658.2294274870215848400.1231364074.1231364074.1231364074.1'); + re8.exec('4413241q3660'); + re8.exec('SbeprqRkcvengvba=633669357391353591'); + re8.exec(str39); + re8.exec(str40); + re8.exec('AFP_zp_kkk-gdzogv_80=4413241q3660'); + re8.exec('FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7'); + re8.exec('__hgzn=144631658.2294274870215848400.1231364074.1231364074.1231364074.1'); + re8.exec('__hgzo=144631658.0.10.1231364074'); + re8.exec('__hgzm=144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re8.exec('p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7'); + re34.exec(s91[i]); + re34.exec(s92[i]); + } + } + var re64 = /\b[a-z]/g; + var re65 = /^uggc:\/\//; + var re66 = /(?:^|\s+)qvfnoyrq(?:\s+|$)/; + var str41 = 'uggc://cebsvyr.zlfcnpr.pbz/Zbqhyrf/Nccyvpngvbaf/Cntrf/Pnainf.nfck'; + function runBlock8() { + for (var i = 0; i < 7; i++) { + s21[i].match(/\d+/g); + 'nsgre'.replace(re64, ''); + 'orsber'.replace(re64, ''); + 'obggbz'.replace(re64, ''); + 'ohvygva_jrngure.kzy'.replace(re65, ''); + 'ohggba'.replace(re37, ''); + 'ohggba'.replace(re18, ''); + 'qngrgvzr.kzy'.replace(re65, ''); + 'uggc://eff.paa.pbz/eff/paa_gbcfgbevrf.eff'.replace(re65, ''); + 'vachg'.replace(re37, ''); + 'vachg'.replace(re18, ''); + 'vafvqr'.replace(re64, ''); + 'cbvagre'.replace(re27, ''); + 'cbfvgvba'.replace(/[A-Z]/g, ''); + 'gbc'.replace(re27, ''); + 'gbc'.replace(re64, ''); + 'hy'.replace(re37, ''); + 'hy'.replace(re18, ''); + str26.replace(re37, ''); + str26.replace(re18, ''); + 'lbhghor_vtbbtyr/i2/lbhghor.kzy'.replace(re65, ''); + 'm-vaqrk'.replace(re27, ''); + /#([\w-]+)/.exec(str26); + re16.exec('urvtug'); + re16.exec('znetvaGbc'); + re16.exec('jvqgu'); + re19.exec('gno0 svefg ba'); + re19.exec('gno0 ba'); + re19.exec('gno4 ynfg'); + re19.exec('gno4'); + re19.exec('gno5'); + re19.exec('gno6'); + re19.exec('gno7'); + re19.exec('gno8'); + /NqborNVE\/([^\s]*)/.exec(s15[i]); + /NccyrJroXvg\/([^ ]*)/.exec(s15[i]); + /XUGZY/gi.exec(s15[i]); + /^(?:obql|ugzy)$/i.exec('YV'); + re38.exec('ohggba'); + re38.exec('vachg'); + re38.exec('hy'); + re38.exec(str26); + /^(\w+|\*)/.exec(str26); + /znp|jva|yvahk/i.exec('Jva32'); + /eton?\([\d\s,]+\)/.exec('fgngvp'); + } + for (var i = 0; i < 6; i++) { + ''.replace(/\r/g, ''); + '/'.replace(re40, ''); + '/'.replace(re10, ''); + '/'.replace(re51, ''); + '/'.replace(re52, ''); + '/'.replace(re53, ''); + '/'.replace(re39, ''); + '/'.replace(re54, ''); + 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/{0}?[NDO]&{1}&{2}&[NDR]'.replace(re63, ''); + str41.replace(re12, ''); + 'uggc://jjj.snprobbx.pbz/fepu.cuc'.replace(re23, ''); + 'freivpr'.replace(re40, ''); + 'freivpr'.replace(re41, ''); + 'freivpr'.replace(re42, ''); + 'freivpr'.replace(re43, ''); + 'freivpr'.replace(re44, ''); + 'freivpr'.replace(re45, ''); + 'freivpr'.replace(re46, ''); + 'freivpr'.replace(re47, ''); + 'freivpr'.replace(re48, ''); + /((ZFVR\s+([6-9]|\d\d)\.))/.exec(s15[i]); + re66.exec(''); + re50.exec('fryrpgrq'); + re8.exec('8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn'); + re8.exec('SbeprqRkcvengvba=633669340386893867'); + re8.exec('VC=74.125.75.17'); + re8.exec('FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn'); + /Xbadhrebe|Fnsnev|XUGZY/.exec(s15[i]); + re13.exec(str41); + re49.exec('unfsbphf'); + } + } + var re67 = /zrah_byq/g; + var str42 = 'FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669325184628362&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; + var str43 = 'FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29; __hgzm=144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.3931862196947939300.1231364380.1231364380.1231364380.1; __hgzo=144631658.0.10.1231364380; __hgzp=144631658; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669325184628362&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str44 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_vzntrf_wf&qg=1231364373088&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231364373088&punaary=svz_zlfcnpr_hfre-ivrj-pbzzragf%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Spbzzrag.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1158737789.1231364375&tn_fvq=1231364375&tn_uvq=415520832&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; + var str45 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669325184628362&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str46 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669325184628362&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; + var re68 = /^([#.]?)((?:[\w\u0128-\uffff*_-]|\\.)*)/; + var re69 = /\{1\}/g; + var re70 = /\s+/; + var re71 = /(\$\{4\})|(\$4\b)/g; + var re72 = /(\$\{5\})|(\$5\b)/g; + var re73 = /\{2\}/g; + var re74 = /[^+>] [^+>]/; + var re75 = /\bucpyv\s*=\s*([^;]*)/i; + var re76 = /\bucuvqr\s*=\s*([^;]*)/i; + var re77 = /\bucfie\s*=\s*([^;]*)/i; + var re78 = /\bhfucjrn\s*=\s*([^;]*)/i; + var re79 = /\bmvc\s*=\s*([^;]*)/i; + var re80 = /^((?:[\w\u0128-\uffff*_-]|\\.)+)(#)((?:[\w\u0128-\uffff*_-]|\\.)+)/; + var re81 = /^([>+~])\s*(\w*)/i; + var re82 = /^>\s*((?:[\w\u0128-\uffff*_-]|\\.)+)/; + var re83 = /^[\s[]?shapgvba/; + var re84 = /v\/g.tvs#(.*)/i; + var str47 = '#Zbq-Vasb-Vasb-WninFpevcgUvag'; + var str48 = ',n.svryqOgaPnapry'; + var str49 = 'FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669357391353591&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_kkk-gdzogv_80=4413241q3660'; + var str50 = 'FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7; AFP_zp_kkk-gdzogv_80=4413241q3660; AFP_zp_kkk-aowb_80=4413235p3660; __hgzm=144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.2770915348920628700.1231367708.1231367708.1231367708.1; __hgzo=144631658.0.10.1231367708; __hgzp=144631658; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669357391353591&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str51 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231367691141&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367691141&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Sjjj.zlfcnpr.pbz%2S&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=320757904.1231367694&tn_fvq=1231367694&tn_uvq=1758792003&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; + var str52 = 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55332979829981?[NDO]&aqu=1&g=7%2S0%2S2009%2014%3N38%3N42%203%20480&af=zfacbegny&cntrAnzr=HF%20UCZFSGJ&t=uggc%3N%2S%2Sjjj.zfa.pbz%2S&f=1024k768&p=24&x=L&oj=994&ou=634&uc=A&{2}&[NDR]'; + var str53 = 'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq qbhoyr2 ps'; + var str54 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669357391353591&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str55 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669357391353591&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; + var str56 = 'ne;ng;nh;or;oe;pn;pu;py;pa;qr;qx;rf;sv;se;to;ux;vq;vr;va;vg;wc;xe;zk;zl;ay;ab;am;cu;cy;cg;eh;fr;ft;gu;ge;gj;mn;'; + var str57 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886&GHVQ=1'; + var str58 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886'; + var str59 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886; mvc=m:94043|yn:37.4154|yb:-122.0585|p:HF|ue:1'; + var str60 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886; mvc=m:94043|yn:37.4154|yb:-122.0585|p:HF'; + var str61 = 'uggc://gx2.fgp.f-zfa.pbz/oe/uc/11/ra-hf/pff/v/g.tvs#uggc://gx2.fgo.f-zfa.pbz/v/29/4RQP4969777N048NPS4RRR3PO2S7S.wct'; + var str62 = 'uggc://gx2.fgp.f-zfa.pbz/oe/uc/11/ra-hf/pff/v/g.tvs#uggc://gx2.fgo.f-zfa.pbz/v/OQ/63NP9O94NS5OQP1249Q9S1ROP7NS3.wct'; + var str63 = 'zbmvyyn/5.0 (jvaqbjf; h; jvaqbjf ag 5.1; ra-hf) nccyrjroxvg/528.9 (xugzy, yvxr trpxb) puebzr/2.0.157.0 fnsnev/528.9'; + var s94 = computeInputVariants(str42, 5); + var s95 = computeInputVariants(str43, 5); + var s96 = computeInputVariants(str44, 5); + var s97 = computeInputVariants(str47, 5); + var s98 = computeInputVariants(str48, 5); + var s99 = computeInputVariants(str49, 5); + var s100 = computeInputVariants(str50, 5); + var s101 = computeInputVariants(str51, 5); + var s102 = computeInputVariants(str52, 5); + var s103 = computeInputVariants(str53, 5); + + function runBlock9() { + for (var i = 0; i < 5; i++) { + s94[i].split(re32); + s95[i].split(re32); + 'svz_zlfcnpr_hfre-ivrj-pbzzragf,svz_zlfcnpr_havgrq-fgngrf'.split(re20); + s96[i].replace(re33, ''); + 'zrah_arj zrah_arj_gbttyr zrah_gbttyr'.replace(re67, ''); + 'zrah_byq zrah_byq_gbttyr zrah_gbttyr'.replace(re67, ''); + re8.exec('102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98'); + re8.exec('144631658.0.10.1231364380'); + re8.exec('144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re8.exec('144631658.3931862196947939300.1231364380.1231364380.1231364380.1'); + re8.exec('441326q33660'); + re8.exec('SbeprqRkcvengvba=633669341278771470'); + re8.exec(str45); + re8.exec(str46); + re8.exec('AFP_zp_dfctwzssrwh-aowb_80=441326q33660'); + re8.exec('FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98'); + re8.exec('__hgzn=144631658.3931862196947939300.1231364380.1231364380.1231364380.1'); + re8.exec('__hgzo=144631658.0.10.1231364380'); + re8.exec('__hgzm=144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + } + for (var i = 0; i < 4; i++) { + ' yvfg1'.replace(re14, ''); + ' yvfg1'.replace(re15, ''); + ' yvfg2'.replace(re14, ''); + ' yvfg2'.replace(re15, ''); + ' frneputebhc1'.replace(re14, ''); + ' frneputebhc1'.replace(re15, ''); + s97[i].replace(re68, ''); + s97[i].replace(re18, ''); + ''.replace(/&/g, ''); + ''.replace(re35, ''); + '(..-{0})(\|(\d+)|)'.replace(re63, ''); + s98[i].replace(re18, ''); + '//vzt.jro.qr/vij/FC/${cngu}/${anzr}/${inyhr}?gf=${abj}'.replace(re56, ''); + '//vzt.jro.qr/vij/FC/tzk_uc/${anzr}/${inyhr}?gf=${abj}'.replace(/(\$\{anzr\})|(\$anzr\b)/g, ''); + 'Jvaqbjf Yvir Ubgznvy{1}'.replace(re69, ''); + '{0}{1}'.replace(re63, ''); + '{1}'.replace(re69, ''); + '{1}'.replace(re63, ''); + 'Vzntrf'.replace(re15, ''); + 'ZFA'.replace(re15, ''); + 'Zncf'.replace(re15, ''); + 'Zbq-Vasb-Vasb-WninFpevcgUvag'.replace(re39, ''); + 'Arjf'.replace(re15, ''); + s99[i].split(re32); + s100[i].split(re32); + 'Ivqrb'.replace(re15, ''); + 'Jro'.replace(re15, ''); + 'n'.replace(re39, ''); + 'nwnkFgneg'.split(re70); + 'nwnkFgbc'.split(re70); + 'ovaq'.replace(re14, ''); + 'ovaq'.replace(re15, ''); + 'oevatf lbh zber. Zber fcnpr (5TO), zber frphevgl, fgvyy serr.'.replace(re63, ''); + 'puvyq p1 svefg qrpx'.replace(re14, ''); + 'puvyq p1 svefg qrpx'.replace(re15, ''); + 'puvyq p1 svefg qbhoyr2'.replace(re14, ''); + 'puvyq p1 svefg qbhoyr2'.replace(re15, ''); + 'puvyq p2 ynfg'.replace(re14, ''); + 'puvyq p2 ynfg'.replace(re15, ''); + 'puvyq p2'.replace(re14, ''); + 'puvyq p2'.replace(re15, ''); + 'puvyq p3'.replace(re14, ''); + 'puvyq p3'.replace(re15, ''); + 'puvyq p4 ynfg'.replace(re14, ''); + 'puvyq p4 ynfg'.replace(re15, ''); + 'pbclevtug'.replace(re14, ''); + 'pbclevtug'.replace(re15, ''); + 'qZFAZR_1'.replace(re14, ''); + 'qZFAZR_1'.replace(re15, ''); + 'qbhoyr2 ps'.replace(re14, ''); + 'qbhoyr2 ps'.replace(re15, ''); + 'qbhoyr2'.replace(re14, ''); + 'qbhoyr2'.replace(re15, ''); + 'uqy_arj'.replace(re14, ''); + 'uqy_arj'.replace(re15, ''); + 'uc_fubccvatobk'.replace(re30, ''); + 'ugzy%2Rvq'.replace(re29, ''); + 'ugzy%2Rvq'.replace(re30, ''); + s101[i].replace(re33, ''); + 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/cebgbglcr.wf${4}${5}'.replace(re71, ''); + 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/cebgbglcr.wf${5}'.replace(re72, ''); + s102[i].replace(re73, ''); + 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55332979829981?[NDO]&{1}&{2}&[NDR]'.replace(re69, ''); + 'vztZFSG'.replace(re14, ''); + 'vztZFSG'.replace(re15, ''); + 'zfasbbg1 ps'.replace(re14, ''); + 'zfasbbg1 ps'.replace(re15, ''); + s103[i].replace(re14, ''); + s103[i].replace(re15, ''); + 'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq'.replace(re14, ''); + 'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq'.replace(re15, ''); + 'cevznel'.replace(re14, ''); + 'cevznel'.replace(re15, ''); + 'erpgnatyr'.replace(re30, ''); + 'frpbaqnel'.replace(re14, ''); + 'frpbaqnel'.replace(re15, ''); + 'haybnq'.split(re70); + '{0}{1}1'.replace(re63, ''); + '|{1}1'.replace(re69, ''); + /(..-HF)(\|(\d+)|)/i.exec('xb-xe,ra-va,gu-gu'); + re4.exec('/ZlFcnprNccf/NccPnainf,45000012'); + re8.exec('144631658.0.10.1231367708'); + re8.exec('144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re8.exec('144631658.2770915348920628700.1231367708.1231367708.1231367708.1'); + re8.exec('4413235p3660'); + re8.exec('441327q73660'); + re8.exec('9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473'); + re8.exec('SbeprqRkcvengvba=633669350559478880'); + re8.exec(str54); + re8.exec(str55); + re8.exec('AFP_zp_dfctwzs-aowb_80=441327q73660'); + re8.exec('AFP_zp_kkk-aowb_80=4413235p3660'); + re8.exec('FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473'); + re8.exec('__hgzn=144631658.2770915348920628700.1231367708.1231367708.1231367708.1'); + re8.exec('__hgzo=144631658.0.10.1231367708'); + re8.exec('__hgzm=144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re34.exec(s99[i]); + re34.exec(s100[i]); + /ZFVR\s+5[.]01/.exec(s15[i]); + /HF(?=;)/i.exec(str56); + re74.exec(s97[i]); + re28.exec('svefg npgvir svefgNpgvir'); + re28.exec('ynfg'); + /\bp:(..)/i.exec('m:94043|yn:37.4154|yb:-122.0585|p:HF'); + re75.exec(str57); + re75.exec(str58); + re76.exec(str57); + re76.exec(str58); + re77.exec(str57); + re77.exec(str58); + /\bhfucce\s*=\s*([^;]*)/i.exec(str59); + re78.exec(str57); + re78.exec(str58); + /\bjci\s*=\s*([^;]*)/i.exec(str59); + re79.exec(str58); + re79.exec(str60); + re79.exec(str59); + /\|p:([a-z]{2})/i.exec('m:94043|yn:37.4154|yb:-122.0585|p:HF|ue:1'); + re80.exec(s97[i]); + re61.exec('cebgbglcr.wf'); + re68.exec(s97[i]); + re81.exec(s97[i]); + re82.exec(s97[i]); + /^Fubpxjnir Synfu (\d)/.exec(s21[i]); + /^Fubpxjnir Synfu (\d+)/.exec(s21[i]); + re83.exec('[bowrpg tybony]'); + re62.exec(s97[i]); + re84.exec(str61); + re84.exec(str62); + /jroxvg/.exec(str63); + } + } + var re85 = /eaq_zbqobkva/; + var str64 = '1231365729213'; + var str65 = '74.125.75.3-1057165600.29978900'; + var str66 = '74.125.75.3-1057165600.29978900.1231365730214'; + var str67 = 'Frnepu%20Zvpebfbsg.pbz'; + var str68 = 'FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn; ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669340386893867&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; + var str69 = 'FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn; __hgzm=144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1877536177953918500.1231365779.1231365779.1231365779.1; __hgzo=144631658.0.10.1231365779; __hgzp=144631658; ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669340386893867&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str70 = 'I=3%26THVQ=757q3ss871q44o7o805n8113n5p72q52'; + var str71 = 'I=3&THVQ=757q3ss871q44o7o805n8113n5p72q52'; + var str72 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231365765292&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231365765292&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Sohyyrgvaf.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1579793869.1231365768&tn_fvq=1231365768&tn_uvq=2056210897&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; + var str73 = 'frnepu.zvpebfbsg.pbz'; + var str74 = 'frnepu.zvpebfbsg.pbz/'; + var str75 = 'ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669340386893867&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str76 = 'ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669340386893867&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; + function runBlock10() { + for (var i = 0; i < 3; i++) { + '%3Szxg=ra-HF'.replace(re39, ''); + '-8'.replace(re40, ''); + '-8'.replace(re10, ''); + '-8'.replace(re51, ''); + '-8'.replace(re52, ''); + '-8'.replace(re53, ''); + '-8'.replace(re39, ''); + '-8'.replace(re54, ''); + '1.5'.replace(re40, ''); + '1.5'.replace(re10, ''); + '1.5'.replace(re51, ''); + '1.5'.replace(re52, ''); + '1.5'.replace(re53, ''); + '1.5'.replace(re39, ''); + '1.5'.replace(re54, ''); + '1024k768'.replace(re40, ''); + '1024k768'.replace(re10, ''); + '1024k768'.replace(re51, ''); + '1024k768'.replace(re52, ''); + '1024k768'.replace(re53, ''); + '1024k768'.replace(re39, ''); + '1024k768'.replace(re54, ''); + str64.replace(re40, ''); + str64.replace(re10, ''); + str64.replace(re51, ''); + str64.replace(re52, ''); + str64.replace(re53, ''); + str64.replace(re39, ''); + str64.replace(re54, ''); + '14'.replace(re40, ''); + '14'.replace(re10, ''); + '14'.replace(re51, ''); + '14'.replace(re52, ''); + '14'.replace(re53, ''); + '14'.replace(re39, ''); + '14'.replace(re54, ''); + '24'.replace(re40, ''); + '24'.replace(re10, ''); + '24'.replace(re51, ''); + '24'.replace(re52, ''); + '24'.replace(re53, ''); + '24'.replace(re39, ''); + '24'.replace(re54, ''); + str65.replace(re40, ''); + str65.replace(re10, ''); + str65.replace(re51, ''); + str65.replace(re52, ''); + str65.replace(re53, ''); + str65.replace(re39, ''); + str65.replace(re54, ''); + str66.replace(re40, ''); + str66.replace(re10, ''); + str66.replace(re51, ''); + str66.replace(re52, ''); + str66.replace(re53, ''); + str66.replace(re39, ''); + str66.replace(re54, ''); + '9.0'.replace(re40, ''); + '9.0'.replace(re10, ''); + '9.0'.replace(re51, ''); + '9.0'.replace(re52, ''); + '9.0'.replace(re53, ''); + '9.0'.replace(re39, ''); + '9.0'.replace(re54, ''); + '994k634'.replace(re40, ''); + '994k634'.replace(re10, ''); + '994k634'.replace(re51, ''); + '994k634'.replace(re52, ''); + '994k634'.replace(re53, ''); + '994k634'.replace(re39, ''); + '994k634'.replace(re54, ''); + '?zxg=ra-HF'.replace(re40, ''); + '?zxg=ra-HF'.replace(re10, ''); + '?zxg=ra-HF'.replace(re51, ''); + '?zxg=ra-HF'.replace(re52, ''); + '?zxg=ra-HF'.replace(re53, ''); + '?zxg=ra-HF'.replace(re54, ''); + 'PAA.pbz'.replace(re25, ''); + 'PAA.pbz'.replace(re12, ''); + 'PAA.pbz'.replace(re39, ''); + 'Qngr & Gvzr'.replace(re25, ''); + 'Qngr & Gvzr'.replace(re12, ''); + 'Qngr & Gvzr'.replace(re39, ''); + 'Frnepu Zvpebfbsg.pbz'.replace(re40, ''); + 'Frnepu Zvpebfbsg.pbz'.replace(re54, ''); + str67.replace(re10, ''); + str67.replace(re51, ''); + str67.replace(re52, ''); + str67.replace(re53, ''); + str67.replace(re39, ''); + str68.split(re32); + str69.split(re32); + str70.replace(re52, ''); + str70.replace(re53, ''); + str70.replace(re39, ''); + str71.replace(re40, ''); + str71.replace(re10, ''); + str71.replace(re51, ''); + str71.replace(re54, ''); + 'Jrngure'.replace(re25, ''); + 'Jrngure'.replace(re12, ''); + 'Jrngure'.replace(re39, ''); + 'LbhGhor'.replace(re25, ''); + 'LbhGhor'.replace(re12, ''); + 'LbhGhor'.replace(re39, ''); + str72.replace(re33, ''); + 'erzbgr_vsenzr_1'.replace(/^erzbgr_vsenzr_/, ''); + str73.replace(re40, ''); + str73.replace(re10, ''); + str73.replace(re51, ''); + str73.replace(re52, ''); + str73.replace(re53, ''); + str73.replace(re39, ''); + str73.replace(re54, ''); + str74.replace(re40, ''); + str74.replace(re10, ''); + str74.replace(re51, ''); + str74.replace(re52, ''); + str74.replace(re53, ''); + str74.replace(re39, ''); + str74.replace(re54, ''); + 'lhv-h'.replace(/\-/g, ''); + re9.exec('p'); + re9.exec('qz p'); + re9.exec('zbqynory'); + re9.exec('lhv-h svefg'); + re8.exec('144631658.0.10.1231365779'); + re8.exec('144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re8.exec('144631658.1877536177953918500.1231365779.1231365779.1231365779.1'); + re8.exec(str75); + re8.exec(str76); + re8.exec('__hgzn=144631658.1877536177953918500.1231365779.1231365779.1231365779.1'); + re8.exec('__hgzo=144631658.0.10.1231365779'); + re8.exec('__hgzm=144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re34.exec(str68); + re34.exec(str69); + /^$/.exec(''); + re31.exec('qr'); + /^znk\d+$/.exec(''); + /^zva\d+$/.exec(''); + /^erfgber$/.exec(''); + re85.exec('zbqobkva zbqobk_abcnqqvat '); + re85.exec('zbqgvgyr'); + re85.exec('eaq_zbqobkva '); + re85.exec('eaq_zbqgvgyr '); + /frpgvba\d+_pbagragf/.exec('obggbz_ani'); + } + } + var re86 = /;\s*/; + var re87 = /(\$\{inyhr\})|(\$inyhr\b)/g; + var re88 = /(\$\{abj\})|(\$abj\b)/g; + var re89 = /\s+$/; + var re90 = /^\s+/; + var re91 = /(\\\"|\x00-|\x1f|\x7f-|\x9f|\u00ad|\u0600-|\u0604|\u070f|\u17b4|\u17b5|\u200c-|\u200f|\u2028-|\u202f|\u2060-|\u206f|\ufeff|\ufff0-|\uffff)/g; + var re92 = /^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/; + var re93 = /^([:.#]*)((?:[\w\u0128-\uffff*_-]|\\.)+)/; + var re94 = /^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/; + var str77 = '#fubhgobk .pybfr'; + var str78 = 'FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669341278771470&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzssrwh-aowb_80=441326q33660'; + var str79 = 'FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98; AFP_zp_dfctwzssrwh-aowb_80=441326q33660; __hgzm=144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1670816052019209000.1231365869.1231365869.1231365869.1; __hgzo=144631658.0.10.1231365869; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669341278771470&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str80 = 'FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669350559478880&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzs-aowb_80=441327q73660'; + var str81 = 'FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473; AFP_zp_dfctwzs-aowb_80=441327q73660; __hgzm=144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1796080716621419500.1231367054.1231367054.1231367054.1; __hgzo=144631658.0.10.1231367054; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669350559478880&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str82 = '[glcr=fhozvg]'; + var str83 = 'n.svryqOga,n.svryqOgaPnapry'; + var str84 = 'n.svryqOgaPnapry'; + var str85 = 'oyvpxchaxg'; + var str86 = 'qvi.bow-nppbeqvba qg'; + var str87 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_nccf_wf&qg=1231367052227&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367052227&punaary=svz_zlfcnpr_nccf-pnainf%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyr.zlfcnpr.pbz%2SZbqhyrf%2SNccyvpngvbaf%2SCntrf%2SPnainf.nfck&nq_glcr=grkg&rvq=6083027&rn=0&sez=1&tn_ivq=716357910.1231367056&tn_fvq=1231367056&tn_uvq=1387206491&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; + var str88 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231365851658&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231365851658&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyrrqvg.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1979828129.1231365855&tn_fvq=1231365855&tn_uvq=2085229649&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; + var str89 = 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55023338617756?[NDO]&aqu=1&g=7%2S0%2S2009%2014%3N12%3N47%203%20480&af=zfacbegny&cntrAnzr=HF%20UCZFSGJ&t=uggc%3N%2S%2Sjjj.zfa.pbz%2S&f=0k0&p=43835816&x=A&oj=994&ou=634&uc=A&{2}&[NDR]'; + var str90 = 'zrgn[anzr=nwnkHey]'; + var str91 = 'anpuevpugra'; + var str92 = 'b oS={\'oT\':1.1};x $8n(B){z(B!=o9)};x $S(B){O(!$8n(B))z A;O(B.4L)z\'T\';b S=7t B;O(S==\'2P\'&&B.p4){23(B.7f){12 1:z\'T\';12 3:z/\S/.2g(B.8M)?\'ox\':\'oh\'}}O(S==\'2P\'||S==\'x\'){23(B.nE){12 2V:z\'1O\';12 7I:z\'5a\';12 18:z\'4B\'}O(7t B.I==\'4F\'){O(B.3u)z\'pG\';O(B.8e)z\'1p\'}}z S};x $2p(){b 4E={};Z(b v=0;v<1p.I;v++){Z(b X 1o 1p[v]){b nc=1p[v][X];b 6E=4E[X];O(6E&&$S(nc)==\'2P\'&&$S(6E)==\'2P\')4E[X]=$2p(6E,nc);17 4E[X]=nc}}z 4E};b $E=7p.E=x(){b 1d=1p;O(!1d[1])1d=[p,1d[0]];Z(b X 1o 1d[1])1d[0][X]=1d[1][X];z 1d[0]};b $4D=7p.pJ=x(){Z(b v=0,y=1p.I;v-1:p.3F(2R)>-1},nX:x(){z p.3y(/([.*+?^${}()|[\]\/\\])/t,\'\\$1\')}});2V.E({5V:x(1O){O(p.I<3)z A;O(p.I==4&&p[3]==0&&!1O)z\'p5\';b 3P=[];Z(b v=0;v<3;v++){b 52=(p[v]-0).4h(16);3P.1x((52.I==1)?\'0\'+52:52)}z 1O?3P:\'#\'+3P.2u(\'\')},5U:x(1O){O(p.I!=3)z A;b 1i=[];Z(b v=0;v<3;v++){1i.1x(5K((p[v].I==1)?p[v]+p[v]:p[v],16))}z 1O?1i:\'1i(\'+1i.2u(\',\')+\')\'}});7F.E({3n:x(P){b J=p;P=$2p({\'L\':J,\'V\':A,\'1p\':1S,\'2x\':A,\'4s\':A,\'6W\':A},P);O($2O(P.1p)&&$S(P.1p)!=\'1O\')P.1p=[P.1p];z x(V){b 1d;O(P.V){V=V||H.V;1d=[(P.V===1r)?V:Y P.V(V)];O(P.1p)1d.E(P.1p)}17 1d=P.1p||1p;b 3C=x(){z J.3H($5S(P'; + var str93 = 'hagreunyghat'; + var str94 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669341278771470&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str95 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669350559478880&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; + var str96 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669341278771470&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; + var str97 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669350559478880&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; + var str98 = 'shapgvba (){Cuk.Nccyvpngvba.Frghc.Pber();Cuk.Nccyvpngvba.Frghc.Nwnk();Cuk.Nccyvpngvba.Frghc.Synfu();Cuk.Nccyvpngvba.Frghc.Zbqhyrf()}'; + function runBlock11() { + for (var i = 0; i < 2; i++) { + ' .pybfr'.replace(re18, ''); + ' n.svryqOgaPnapry'.replace(re18, ''); + ' qg'.replace(re18, ''); + str77.replace(re68, ''); + str77.replace(re18, ''); + ''.replace(re39, ''); + ''.replace(/^/, ''); + ''.split(re86); + '*'.replace(re39, ''); + '*'.replace(re68, ''); + '*'.replace(re18, ''); + '.pybfr'.replace(re68, ''); + '.pybfr'.replace(re18, ''); + '//vzt.jro.qr/vij/FC/tzk_uc/fperra/${inyhr}?gf=${abj}'.replace(re87, ''); + '//vzt.jro.qr/vij/FC/tzk_uc/fperra/1024?gf=${abj}'.replace(re88, ''); + '//vzt.jro.qr/vij/FC/tzk_uc/jvafvmr/${inyhr}?gf=${abj}'.replace(re87, ''); + '//vzt.jro.qr/vij/FC/tzk_uc/jvafvmr/992/608?gf=${abj}'.replace(re88, ''); + '300k120'.replace(re30, ''); + '300k250'.replace(re30, ''); + '310k120'.replace(re30, ''); + '310k170'.replace(re30, ''); + '310k250'.replace(re30, ''); + '9.0 e115'.replace(/^.*\.(.*)\s.*$/, ''); + 'Nppbeqvba'.replace(re2, ''); + 'Nxghryy\x0a'.replace(re89, ''); + 'Nxghryy\x0a'.replace(re90, ''); + 'Nccyvpngvba'.replace(re2, ''); + 'Oyvpxchaxg\x0a'.replace(re89, ''); + 'Oyvpxchaxg\x0a'.replace(re90, ''); + 'Svanamra\x0a'.replace(re89, ''); + 'Svanamra\x0a'.replace(re90, ''); + 'Tnzrf\x0a'.replace(re89, ''); + 'Tnzrf\x0a'.replace(re90, ''); + 'Ubebfxbc\x0a'.replace(re89, ''); + 'Ubebfxbc\x0a'.replace(re90, ''); + 'Xvab\x0a'.replace(re89, ''); + 'Xvab\x0a'.replace(re90, ''); + 'Zbqhyrf'.replace(re2, ''); + 'Zhfvx\x0a'.replace(re89, ''); + 'Zhfvx\x0a'.replace(re90, ''); + 'Anpuevpugra\x0a'.replace(re89, ''); + 'Anpuevpugra\x0a'.replace(re90, ''); + 'Cuk'.replace(re2, ''); + 'ErdhrfgSvavfu'.split(re70); + 'ErdhrfgSvavfu.NWNK.Cuk'.split(re70); + 'Ebhgr\x0a'.replace(re89, ''); + 'Ebhgr\x0a'.replace(re90, ''); + str78.split(re32); + str79.split(re32); + str80.split(re32); + str81.split(re32); + 'Fcbeg\x0a'.replace(re89, ''); + 'Fcbeg\x0a'.replace(re90, ''); + 'GI-Fcbg\x0a'.replace(re89, ''); + 'GI-Fcbg\x0a'.replace(re90, ''); + 'Gbhe\x0a'.replace(re89, ''); + 'Gbhe\x0a'.replace(re90, ''); + 'Hagreunyghat\x0a'.replace(re89, ''); + 'Hagreunyghat\x0a'.replace(re90, ''); + 'Ivqrb\x0a'.replace(re89, ''); + 'Ivqrb\x0a'.replace(re90, ''); + 'Jrggre\x0a'.replace(re89, ''); + 'Jrggre\x0a'.replace(re90, ''); + str82.replace(re68, ''); + str82.replace(re18, ''); + str83.replace(re68, ''); + str83.replace(re18, ''); + str84.replace(re68, ''); + str84.replace(re18, ''); + 'nqiFreivprObk'.replace(re30, ''); + 'nqiFubccvatObk'.replace(re30, ''); + 'nwnk'.replace(re39, ''); + 'nxghryy'.replace(re40, ''); + 'nxghryy'.replace(re41, ''); + 'nxghryy'.replace(re42, ''); + 'nxghryy'.replace(re43, ''); + 'nxghryy'.replace(re44, ''); + 'nxghryy'.replace(re45, ''); + 'nxghryy'.replace(re46, ''); + 'nxghryy'.replace(re47, ''); + 'nxghryy'.replace(re48, ''); + str85.replace(re40, ''); + str85.replace(re41, ''); + str85.replace(re42, ''); + str85.replace(re43, ''); + str85.replace(re44, ''); + str85.replace(re45, ''); + str85.replace(re46, ''); + str85.replace(re47, ''); + str85.replace(re48, ''); + 'pngrtbel'.replace(re29, ''); + 'pngrtbel'.replace(re30, ''); + 'pybfr'.replace(re39, ''); + 'qvi'.replace(re39, ''); + str86.replace(re68, ''); + str86.replace(re18, ''); + 'qg'.replace(re39, ''); + 'qg'.replace(re68, ''); + 'qg'.replace(re18, ''); + 'rzorq'.replace(re39, ''); + 'rzorq'.replace(re68, ''); + 'rzorq'.replace(re18, ''); + 'svryqOga'.replace(re39, ''); + 'svryqOgaPnapry'.replace(re39, ''); + 'svz_zlfcnpr_nccf-pnainf,svz_zlfcnpr_havgrq-fgngrf'.split(re20); + 'svanamra'.replace(re40, ''); + 'svanamra'.replace(re41, ''); + 'svanamra'.replace(re42, ''); + 'svanamra'.replace(re43, ''); + 'svanamra'.replace(re44, ''); + 'svanamra'.replace(re45, ''); + 'svanamra'.replace(re46, ''); + 'svanamra'.replace(re47, ''); + 'svanamra'.replace(re48, ''); + 'sbphf'.split(re70); + 'sbphf.gno sbphfva.gno'.split(re70); + 'sbphfva'.split(re70); + 'sbez'.replace(re39, ''); + 'sbez.nwnk'.replace(re68, ''); + 'sbez.nwnk'.replace(re18, ''); + 'tnzrf'.replace(re40, ''); + 'tnzrf'.replace(re41, ''); + 'tnzrf'.replace(re42, ''); + 'tnzrf'.replace(re43, ''); + 'tnzrf'.replace(re44, ''); + 'tnzrf'.replace(re45, ''); + 'tnzrf'.replace(re46, ''); + 'tnzrf'.replace(re47, ''); + 'tnzrf'.replace(re48, ''); + 'ubzrcntr'.replace(re30, ''); + 'ubebfxbc'.replace(re40, ''); + 'ubebfxbc'.replace(re41, ''); + 'ubebfxbc'.replace(re42, ''); + 'ubebfxbc'.replace(re43, ''); + 'ubebfxbc'.replace(re44, ''); + 'ubebfxbc'.replace(re45, ''); + 'ubebfxbc'.replace(re46, ''); + 'ubebfxbc'.replace(re47, ''); + 'ubebfxbc'.replace(re48, ''); + 'uc_cebzbobk_ugzy%2Puc_cebzbobk_vzt'.replace(re30, ''); + 'uc_erpgnatyr'.replace(re30, ''); + str87.replace(re33, ''); + str88.replace(re33, ''); + 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf${4}${5}'.replace(re71, ''); + 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf${5}'.replace(re72, ''); + 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/qlaYvo.wf${4}${5}'.replace(re71, ''); + 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/qlaYvo.wf${5}'.replace(re72, ''); + 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/rssrpgYvo.wf${4}${5}'.replace(re71, ''); + 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/rssrpgYvo.wf${5}'.replace(re72, ''); + str89.replace(re73, ''); + 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55023338617756?[NDO]&{1}&{2}&[NDR]'.replace(re69, ''); + str6.replace(re23, ''); + 'xvab'.replace(re40, ''); + 'xvab'.replace(re41, ''); + 'xvab'.replace(re42, ''); + 'xvab'.replace(re43, ''); + 'xvab'.replace(re44, ''); + 'xvab'.replace(re45, ''); + 'xvab'.replace(re46, ''); + 'xvab'.replace(re47, ''); + 'xvab'.replace(re48, ''); + 'ybnq'.split(re70); + 'zrqvnzbqgno lhv-anifrg lhv-anifrg-gbc'.replace(re18, ''); + 'zrgn'.replace(re39, ''); + str90.replace(re68, ''); + str90.replace(re18, ''); + 'zbhfrzbir'.split(re70); + 'zbhfrzbir.gno'.split(re70); + str63.replace(/^.*jroxvg\/(\d+(\.\d+)?).*$/, ''); + 'zhfvx'.replace(re40, ''); + 'zhfvx'.replace(re41, ''); + 'zhfvx'.replace(re42, ''); + 'zhfvx'.replace(re43, ''); + 'zhfvx'.replace(re44, ''); + 'zhfvx'.replace(re45, ''); + 'zhfvx'.replace(re46, ''); + 'zhfvx'.replace(re47, ''); + 'zhfvx'.replace(re48, ''); + 'zlfcnpr_nccf_pnainf'.replace(re52, ''); + str91.replace(re40, ''); + str91.replace(re41, ''); + str91.replace(re42, ''); + str91.replace(re43, ''); + str91.replace(re44, ''); + str91.replace(re45, ''); + str91.replace(re46, ''); + str91.replace(re47, ''); + str91.replace(re48, ''); + 'anzr'.replace(re39, ''); + str92.replace(/\b\w+\b/g, ''); + 'bow-nppbeqvba'.replace(re39, ''); + 'bowrpg'.replace(re39, ''); + 'bowrpg'.replace(re68, ''); + 'bowrpg'.replace(re18, ''); + 'cnenzf%2Rfglyrf'.replace(re29, ''); + 'cnenzf%2Rfglyrf'.replace(re30, ''); + 'cbchc'.replace(re30, ''); + 'ebhgr'.replace(re40, ''); + 'ebhgr'.replace(re41, ''); + 'ebhgr'.replace(re42, ''); + 'ebhgr'.replace(re43, ''); + 'ebhgr'.replace(re44, ''); + 'ebhgr'.replace(re45, ''); + 'ebhgr'.replace(re46, ''); + 'ebhgr'.replace(re47, ''); + 'ebhgr'.replace(re48, ''); + 'freivprobk_uc'.replace(re30, ''); + 'fubccvatobk_uc'.replace(re30, ''); + 'fubhgobk'.replace(re39, ''); + 'fcbeg'.replace(re40, ''); + 'fcbeg'.replace(re41, ''); + 'fcbeg'.replace(re42, ''); + 'fcbeg'.replace(re43, ''); + 'fcbeg'.replace(re44, ''); + 'fcbeg'.replace(re45, ''); + 'fcbeg'.replace(re46, ''); + 'fcbeg'.replace(re47, ''); + 'fcbeg'.replace(re48, ''); + 'gbhe'.replace(re40, ''); + 'gbhe'.replace(re41, ''); + 'gbhe'.replace(re42, ''); + 'gbhe'.replace(re43, ''); + 'gbhe'.replace(re44, ''); + 'gbhe'.replace(re45, ''); + 'gbhe'.replace(re46, ''); + 'gbhe'.replace(re47, ''); + 'gbhe'.replace(re48, ''); + 'gi-fcbg'.replace(re40, ''); + 'gi-fcbg'.replace(re41, ''); + 'gi-fcbg'.replace(re42, ''); + 'gi-fcbg'.replace(re43, ''); + 'gi-fcbg'.replace(re44, ''); + 'gi-fcbg'.replace(re45, ''); + 'gi-fcbg'.replace(re46, ''); + 'gi-fcbg'.replace(re47, ''); + 'gi-fcbg'.replace(re48, ''); + 'glcr'.replace(re39, ''); + 'haqrsvarq'.replace(/\//g, ''); + str93.replace(re40, ''); + str93.replace(re41, ''); + str93.replace(re42, ''); + str93.replace(re43, ''); + str93.replace(re44, ''); + str93.replace(re45, ''); + str93.replace(re46, ''); + str93.replace(re47, ''); + str93.replace(re48, ''); + 'ivqrb'.replace(re40, ''); + 'ivqrb'.replace(re41, ''); + 'ivqrb'.replace(re42, ''); + 'ivqrb'.replace(re43, ''); + 'ivqrb'.replace(re44, ''); + 'ivqrb'.replace(re45, ''); + 'ivqrb'.replace(re46, ''); + 'ivqrb'.replace(re47, ''); + 'ivqrb'.replace(re48, ''); + 'ivfvgf=1'.split(re86); + 'jrggre'.replace(re40, ''); + 'jrggre'.replace(re41, ''); + 'jrggre'.replace(re42, ''); + 'jrggre'.replace(re43, ''); + 'jrggre'.replace(re44, ''); + 'jrggre'.replace(re45, ''); + 'jrggre'.replace(re46, ''); + 'jrggre'.replace(re47, ''); + 'jrggre'.replace(re48, ''); + /#[a-z0-9]+$/i.exec('uggc://jjj.fpuhryreim.arg/Qrsnhyg'); + re66.exec('fryrpgrq'); + /(?:^|\s+)lhv-ani(?:\s+|$)/.exec('sff lhv-ani'); + /(?:^|\s+)lhv-anifrg(?:\s+|$)/.exec('zrqvnzbqgno lhv-anifrg'); + /(?:^|\s+)lhv-anifrg-gbc(?:\s+|$)/.exec('zrqvnzbqgno lhv-anifrg'); + re91.exec('GnoThvq'); + re91.exec('thvq'); + /(pbzcngvoyr|jroxvg)/.exec(str63); + /.+(?:ei|vg|en|vr)[\/: ]([\d.]+)/.exec(str63); + re8.exec('144631658.0.10.1231365869'); + re8.exec('144631658.0.10.1231367054'); + re8.exec('144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re8.exec('144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re8.exec('144631658.1670816052019209000.1231365869.1231365869.1231365869.1'); + re8.exec('144631658.1796080716621419500.1231367054.1231367054.1231367054.1'); + re8.exec(str94); + re8.exec(str95); + re8.exec(str96); + re8.exec(str97); + re8.exec('__hgzn=144631658.1670816052019209000.1231365869.1231365869.1231365869.1'); + re8.exec('__hgzn=144631658.1796080716621419500.1231367054.1231367054.1231367054.1'); + re8.exec('__hgzo=144631658.0.10.1231365869'); + re8.exec('__hgzo=144631658.0.10.1231367054'); + re8.exec('__hgzm=144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re8.exec('__hgzm=144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); + re34.exec(str78); + re34.exec(str79); + re34.exec(str81); + re74.exec(str77); + re74.exec('*'); + re74.exec(str82); + re74.exec(str83); + re74.exec(str86); + re74.exec('rzorq'); + re74.exec('sbez.nwnk'); + re74.exec(str90); + re74.exec('bowrpg'); + /\/onfr.wf(\?.+)?$/.exec('/uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf'); + re28.exec('uvag ynfgUvag ynfg'); + re75.exec(''); + re76.exec(''); + re77.exec(''); + re78.exec(''); + re80.exec(str77); + re80.exec('*'); + re80.exec('.pybfr'); + re80.exec(str82); + re80.exec(str83); + re80.exec(str84); + re80.exec(str86); + re80.exec('qg'); + re80.exec('rzorq'); + re80.exec('sbez.nwnk'); + re80.exec(str90); + re80.exec('bowrpg'); + re61.exec('qlaYvo.wf'); + re61.exec('rssrpgYvo.wf'); + re61.exec('uggc://jjj.tzk.arg/qr/?fgnghf=uvajrvf'); + re92.exec(' .pybfr'); + re92.exec(' n.svryqOgaPnapry'); + re92.exec(' qg'); + re92.exec(str48); + re92.exec('.nwnk'); + re92.exec('.svryqOga,n.svryqOgaPnapry'); + re92.exec('.svryqOgaPnapry'); + re92.exec('.bow-nppbeqvba qg'); + re68.exec(str77); + re68.exec('*'); + re68.exec('.pybfr'); + re68.exec(str82); + re68.exec(str83); + re68.exec(str84); + re68.exec(str86); + re68.exec('qg'); + re68.exec('rzorq'); + re68.exec('sbez.nwnk'); + re68.exec(str90); + re68.exec('bowrpg'); + re93.exec(' .pybfr'); + re93.exec(' n.svryqOgaPnapry'); + re93.exec(' qg'); + re93.exec(str48); + re93.exec('.nwnk'); + re93.exec('.svryqOga,n.svryqOgaPnapry'); + re93.exec('.svryqOgaPnapry'); + re93.exec('.bow-nppbeqvba qg'); + re81.exec(str77); + re81.exec('*'); + re81.exec(str48); + re81.exec('.pybfr'); + re81.exec(str82); + re81.exec(str83); + re81.exec(str84); + re81.exec(str86); + re81.exec('qg'); + re81.exec('rzorq'); + re81.exec('sbez.nwnk'); + re81.exec(str90); + re81.exec('bowrpg'); + re94.exec(' .pybfr'); + re94.exec(' n.svryqOgaPnapry'); + re94.exec(' qg'); + re94.exec(str48); + re94.exec('.nwnk'); + re94.exec('.svryqOga,n.svryqOgaPnapry'); + re94.exec('.svryqOgaPnapry'); + re94.exec('.bow-nppbeqvba qg'); + re94.exec('[anzr=nwnkHey]'); + re94.exec(str82); + re31.exec('rf'); + re31.exec('wn'); + re82.exec(str77); + re82.exec('*'); + re82.exec(str48); + re82.exec('.pybfr'); + re82.exec(str82); + re82.exec(str83); + re82.exec(str84); + re82.exec(str86); + re82.exec('qg'); + re82.exec('rzorq'); + re82.exec('sbez.nwnk'); + re82.exec(str90); + re82.exec('bowrpg'); + re83.exec(str98); + re83.exec('shapgvba sbphf() { [angvir pbqr] }'); + re62.exec('#Ybtva'); + re62.exec('#Ybtva_cnffjbeq'); + re62.exec(str77); + re62.exec('#fubhgobkWf'); + re62.exec('#fubhgobkWfReebe'); + re62.exec('#fubhgobkWfFhpprff'); + re62.exec('*'); + re62.exec(str82); + re62.exec(str83); + re62.exec(str86); + re62.exec('rzorq'); + re62.exec('sbez.nwnk'); + re62.exec(str90); + re62.exec('bowrpg'); + re49.exec('pbagrag'); + re24.exec(str6); + /xbadhrebe/.exec(str63); + /znp/.exec('jva32'); + /zbmvyyn/.exec(str63); + /zfvr/.exec(str63); + /ag\s5\.1/.exec(str63); + /bcren/.exec(str63); + /fnsnev/.exec(str63); + /jva/.exec('jva32'); + /jvaqbjf/.exec(str63); + } + } + + function run() { + for (var i = 0; i < 5; i++) { + runBlock0(); + runBlock1(); + runBlock2(); + runBlock3(); + runBlock4(); + runBlock5(); + runBlock6(); + runBlock7(); + runBlock8(); + runBlock9(); + runBlock10(); + runBlock11(); + } + } + + this.run = run; +} +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This benchmark is based on a JavaScript log processing module used +// by the V8 profiler to generate execution time profiles for runs of +// JavaScript applications, and it effectively measures how fast the +// JavaScript engine is at allocating nodes and reclaiming the memory +// used for old nodes. Because of the way splay trees work, the engine +// also has to deal with a lot of changes to the large tree object +// graph. + +var Splay = new BenchmarkSuite('Splay', 81491, [ + new Benchmark("Splay", SplayRun, SplaySetup, SplayTearDown) +]); + + +// Configuration. +var kSplayTreeSize = 8000; +var kSplayTreeModifications = 80; +var kSplayTreePayloadDepth = 5; + +var splayTree = null; + + +function GeneratePayloadTree(depth, tag) { + if (depth == 0) { + return { + array : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], + string : 'String for key ' + tag + ' in leaf node' + }; + } else { + return { + left: GeneratePayloadTree(depth - 1, tag), + right: GeneratePayloadTree(depth - 1, tag) + }; + } +} + + +function GenerateKey() { + // The benchmark framework guarantees that Math.random is + // deterministic; see base.js. + return Math.random(); +} + + +function InsertNewNode() { + // Insert new node with a unique key. + var key; + do { + key = GenerateKey(); + } while (splayTree.find(key) != null); + var payload = GeneratePayloadTree(kSplayTreePayloadDepth, String(key)); + splayTree.insert(key, payload); + return key; +} + + + +function SplaySetup() { + splayTree = new SplayTree(); + for (var i = 0; i < kSplayTreeSize; i++) InsertNewNode(); +} + + +function SplayTearDown() { + // Allow the garbage collector to reclaim the memory + // used by the splay tree no matter how we exit the + // tear down function. + var keys = splayTree.exportKeys(); + splayTree = null; + + // Verify that the splay tree has the right size. + var length = keys.length; + if (length != kSplayTreeSize) { + throw new Error("Splay tree has wrong size"); + } + + // Verify that the splay tree has sorted, unique keys. + for (var i = 0; i < length - 1; i++) { + if (keys[i] >= keys[i + 1]) { + throw new Error("Splay tree not sorted"); + } + } +} + + +function SplayRun() { + // Replace a few nodes in the splay tree. + for (var i = 0; i < kSplayTreeModifications; i++) { + var key = InsertNewNode(); + var greatest = splayTree.findGreatestLessThan(key); + if (greatest == null) splayTree.remove(key); + else splayTree.remove(greatest.key); + } +} + + +/** + * Constructs a Splay tree. A splay tree is a self-balancing binary + * search tree with the additional property that recently accessed + * elements are quick to access again. It performs basic operations + * such as insertion, look-up and removal in O(log(n)) amortized time. + * + * @constructor + */ +function SplayTree() { +}; + + +/** + * Pointer to the root node of the tree. + * + * @type {SplayTree.Node} + * @private + */ +SplayTree.prototype.root_ = null; + + +/** + * @return {boolean} Whether the tree is empty. + */ +SplayTree.prototype.isEmpty = function() { + return !this.root_; +}; + + +/** + * Inserts a node into the tree with the specified key and value if + * the tree does not already contain a node with the specified key. If + * the value is inserted, it becomes the root of the tree. + * + * @param {number} key Key to insert into the tree. + * @param {*} value Value to insert into the tree. + */ +SplayTree.prototype.insert = function(key, value) { + if (this.isEmpty()) { + this.root_ = new SplayTree.Node(key, value); + return; + } + // Splay on the key to move the last node on the search path for + // the key to the root of the tree. + this.splay_(key); + if (this.root_.key == key) { + return; + } + var node = new SplayTree.Node(key, value); + if (key > this.root_.key) { + node.left = this.root_; + node.right = this.root_.right; + this.root_.right = null; + } else { + node.right = this.root_; + node.left = this.root_.left; + this.root_.left = null; + } + this.root_ = node; +}; + + +/** + * Removes a node with the specified key from the tree if the tree + * contains a node with this key. The removed node is returned. If the + * key is not found, an exception is thrown. + * + * @param {number} key Key to find and remove from the tree. + * @return {SplayTree.Node} The removed node. + */ +SplayTree.prototype.remove = function(key) { + if (this.isEmpty()) { + throw Error('Key not found: ' + key); + } + this.splay_(key); + if (this.root_.key != key) { + throw Error('Key not found: ' + key); + } + var removed = this.root_; + if (!this.root_.left) { + this.root_ = this.root_.right; + } else { + var right = this.root_.right; + this.root_ = this.root_.left; + // Splay to make sure that the new root has an empty right child. + this.splay_(key); + // Insert the original right child as the right child of the new + // root. + this.root_.right = right; + } + return removed; +}; + + +/** + * Returns the node having the specified key or null if the tree doesn't contain + * a node with the specified key. + * + * @param {number} key Key to find in the tree. + * @return {SplayTree.Node} Node having the specified key. + */ +SplayTree.prototype.find = function(key) { + if (this.isEmpty()) { + return null; + } + this.splay_(key); + return this.root_.key == key ? this.root_ : null; +}; + + +/** + * @return {SplayTree.Node} Node having the maximum key value. + */ +SplayTree.prototype.findMax = function(opt_startNode) { + if (this.isEmpty()) { + return null; + } + var current = opt_startNode || this.root_; + while (current.right) { + current = current.right; + } + return current; +}; + + +/** + * @return {SplayTree.Node} Node having the maximum key value that + * is less than the specified key value. + */ +SplayTree.prototype.findGreatestLessThan = function(key) { + if (this.isEmpty()) { + return null; + } + // Splay on the key to move the node with the given key or the last + // node on the search path to the top of the tree. + this.splay_(key); + // Now the result is either the root node or the greatest node in + // the left subtree. + if (this.root_.key < key) { + return this.root_; + } else if (this.root_.left) { + return this.findMax(this.root_.left); + } else { + return null; + } +}; + + +/** + * @return {Array<*>} An array containing all the keys of tree's nodes. + */ +SplayTree.prototype.exportKeys = function() { + var result = []; + if (!this.isEmpty()) { + this.root_.traverse_(function(node) { result.push(node.key); }); + } + return result; +}; + + +/** + * Perform the splay operation for the given key. Moves the node with + * the given key to the top of the tree. If no node has the given + * key, the last node on the search path is moved to the top of the + * tree. This is the simplified top-down splaying algorithm from: + * "Self-adjusting Binary Search Trees" by Sleator and Tarjan + * + * @param {number} key Key to splay the tree on. + * @private + */ +SplayTree.prototype.splay_ = function(key) { + if (this.isEmpty()) { + return; + } + // Create a dummy node. The use of the dummy node is a bit + // counter-intuitive: The right child of the dummy node will hold + // the L tree of the algorithm. The left child of the dummy node + // will hold the R tree of the algorithm. Using a dummy node, left + // and right will always be nodes and we avoid special cases. + var dummy, left, right; + dummy = left = right = new SplayTree.Node(null, null); + var current = this.root_; + while (true) { + if (key < current.key) { + if (!current.left) { + break; + } + if (key < current.left.key) { + // Rotate right. + var tmp = current.left; + current.left = tmp.right; + tmp.right = current; + current = tmp; + if (!current.left) { + break; + } + } + // Link right. + right.left = current; + right = current; + current = current.left; + } else if (key > current.key) { + if (!current.right) { + break; + } + if (key > current.right.key) { + // Rotate left. + var tmp = current.right; + current.right = tmp.left; + tmp.left = current; + current = tmp; + if (!current.right) { + break; + } + } + // Link left. + left.right = current; + left = current; + current = current.right; + } else { + break; + } + } + // Assemble. + left.right = current.left; + right.left = current.right; + current.left = dummy.right; + current.right = dummy.left; + this.root_ = current; +}; + + +/** + * Constructs a Splay tree node. + * + * @param {number} key Key. + * @param {*} value Value. + */ +SplayTree.Node = function(key, value) { + this.key = key; + this.value = value; +}; + + +/** + * @type {SplayTree.Node} + */ +SplayTree.Node.prototype.left = null; + + +/** + * @type {SplayTree.Node} + */ +SplayTree.Node.prototype.right = null; + + +/** + * Performs an ordered traversal of the subtree starting at + * this SplayTree.Node. + * + * @param {function(SplayTree.Node)} f Visitor function. + * @private + */ +SplayTree.Node.prototype.traverse_ = function(f) { + var current = this; + while (current) { + var left = current.left; + if (left) left.traverse_(f); + f(current); + current = current.right; + } +}; +/** + * Copyright 2012 the V8 project authors. All rights reserved. + * Copyright 2009 Oliver Hunt + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, 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. + */ + +var NavierStokes = new BenchmarkSuite('NavierStokes', 1484000, + [new Benchmark('NavierStokes', + runNavierStokes, + setupNavierStokes, + tearDownNavierStokes)]); + +var solver = null; + +function runNavierStokes() +{ + solver.update(); +} + +function setupNavierStokes() +{ + solver = new FluidField(null); + solver.setResolution(128, 128); + solver.setIterations(20); + solver.setDisplayFunction(function(){}); + solver.setUICallback(prepareFrame); + solver.reset(); +} + +function tearDownNavierStokes() +{ + solver = null; +} + +function addPoints(field) { + var n = 64; + for (var i = 1; i <= n; i++) { + field.setVelocity(i, i, n, n); + field.setDensity(i, i, 5); + field.setVelocity(i, n - i, -n, -n); + field.setDensity(i, n - i, 20); + field.setVelocity(128 - i, n + i, -n, -n); + field.setDensity(128 - i, n + i, 30); + } +} + +var framesTillAddingPoints = 0; +var framesBetweenAddingPoints = 5; + +function prepareFrame(field) +{ + if (framesTillAddingPoints == 0) { + addPoints(field); + framesTillAddingPoints = framesBetweenAddingPoints; + framesBetweenAddingPoints++; + } else { + framesTillAddingPoints--; + } +} + +// Code from Oliver Hunt (http://nerget.com/fluidSim/pressure.js) starts here. +function FluidField(canvas) { + function addFields(x, s, dt) + { + for (var i=0; i Wp5) + x = Wp5; + var i0 = x | 0; + var i1 = i0 + 1; + if (y < 0.5) + y = 0.5; + else if (y > Hp5) + y = Hp5; + var j0 = y | 0; + var j1 = j0 + 1; + var s1 = x - i0; + var s0 = 1 - s1; + var t1 = y - j0; + var t0 = 1 - t1; + var row1 = j0 * rowSize; + var row2 = j1 * rowSize; + d[pos] = s0 * (t0 * d0[i0 + row1] + t1 * d0[i0 + row2]) + s1 * (t0 * d0[i1 + row1] + t1 * d0[i1 + row2]); + } + } + set_bnd(b, d); + } + + function project(u, v, p, div) + { + var h = -0.5 / Math.sqrt(width * height); + for (var j = 1 ; j <= height; j++ ) { + var row = j * rowSize; + var previousRow = (j - 1) * rowSize; + var prevValue = row - 1; + var currentRow = row; + var nextValue = row + 1; + var nextRow = (j + 1) * rowSize; + for (var i = 1; i <= width; i++ ) { + div[++currentRow] = h * (u[++nextValue] - u[++prevValue] + v[++nextRow] - v[++previousRow]); + p[currentRow] = 0; + } + } + set_bnd(0, div); + set_bnd(0, p); + + lin_solve(0, p, div, 1, 4 ); + var wScale = 0.5 * width; + var hScale = 0.5 * height; + for (var j = 1; j<= height; j++ ) { + var prevPos = j * rowSize - 1; + var currentPos = j * rowSize; + var nextPos = j * rowSize + 1; + var prevRow = (j - 1) * rowSize; + var currentRow = j * rowSize; + var nextRow = (j + 1) * rowSize; + + for (var i = 1; i<= width; i++) { + u[++currentPos] -= wScale * (p[++nextPos] - p[++prevPos]); + v[currentPos] -= hScale * (p[++nextRow] - p[++prevRow]); + } + } + set_bnd(1, u); + set_bnd(2, v); + } + + function dens_step(x, x0, u, v, dt) + { + addFields(x, x0, dt); + diffuse(0, x0, x, dt ); + advect(0, x, x0, u, v, dt ); + } + + function vel_step(u, v, u0, v0, dt) + { + addFields(u, u0, dt ); + addFields(v, v0, dt ); + var temp = u0; u0 = u; u = temp; + var temp = v0; v0 = v; v = temp; + diffuse2(u,u0,v,v0, dt); + project(u, v, u0, v0); + var temp = u0; u0 = u; u = temp; + var temp = v0; v0 = v; v = temp; + advect(1, u, u0, u0, v0, dt); + advect(2, v, v0, u0, v0, dt); + project(u, v, u0, v0 ); + } + var uiCallback = function(d,u,v) {}; + + function Field(dens, u, v) { + // Just exposing the fields here rather than using accessors is a measurable win during display (maybe 5%) + // but makes the code ugly. + this.setDensity = function(x, y, d) { + dens[(x + 1) + (y + 1) * rowSize] = d; + } + this.getDensity = function(x, y) { + return dens[(x + 1) + (y + 1) * rowSize]; + } + this.setVelocity = function(x, y, xv, yv) { + u[(x + 1) + (y + 1) * rowSize] = xv; + v[(x + 1) + (y + 1) * rowSize] = yv; + } + this.getXVelocity = function(x, y) { + return u[(x + 1) + (y + 1) * rowSize]; + } + this.getYVelocity = function(x, y) { + return v[(x + 1) + (y + 1) * rowSize]; + } + this.width = function() { return width; } + this.height = function() { return height; } + } + function queryUI(d, u, v) + { + for (var i = 0; i < size; i++) + u[i] = v[i] = d[i] = 0.0; + uiCallback(new Field(d, u, v)); + } + + this.update = function () { + queryUI(dens_prev, u_prev, v_prev); + vel_step(u, v, u_prev, v_prev, dt); + dens_step(dens, dens_prev, u, v, dt); + displayFunc(new Field(dens, u, v)); + } + this.setDisplayFunction = function(func) { + displayFunc = func; + } + + this.iterations = function() { return iterations; } + this.setIterations = function(iters) { + if (iters > 0 && iters <= 100) + iterations = iters; + } + this.setUICallback = function(callback) { + uiCallback = callback; + } + var iterations = 10; + var visc = 0.5; + var dt = 0.1; + var dens; + var dens_prev; + var u; + var u_prev; + var v; + var v_prev; + var width; + var height; + var rowSize; + var size; + var displayFunc; + function reset() + { + rowSize = width + 2; + size = (width+2)*(height+2); + dens = new Array(size); + dens_prev = new Array(size); + u = new Array(size); + u_prev = new Array(size); + v = new Array(size); + v_prev = new Array(size); + for (var i = 0; i < size; i++) + dens_prev[i] = u_prev[i] = v_prev[i] = dens[i] = u[i] = v[i] = 0; + } + this.reset = reset; + this.setResolution = function (hRes, wRes) + { + var res = wRes * hRes; + if (res > 0 && res < 1000000 && (wRes != width || hRes != height)) { + width = wRes; + height = hRes; + reset(); + return true; + } + return false; + } + this.setResolution(64, 64); +} +// Copyright 2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +var success = true; + +function PrintResult(name, result) { + print(name + ': ' + result); +} + + +function PrintError(name, error) { + PrintResult(name, error); + success = false; +} + + +function PrintScore(score) { + if (success) { + print('----'); + print('Score (version ' + BenchmarkSuite.version + '): ' + score); + } +} + + +BenchmarkSuite.RunSuites({ NotifyResult: PrintResult, + NotifyError: PrintError, + NotifyScore: PrintScore }); -- cgit v1.2.3 From ea553dc9fa5bba56dfa6ffe26f06040978d9920b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Thu, 31 Jan 2013 10:00:06 +0100 Subject: Cleanup namespaces. QT_BEGIN_NAMESPACE and QT_END_NAMESPACE was added in header files. Namespace coding convention got unified. Change-Id: I971c9a9179d5cd512f8214457609f40992a8ca66 Reviewed-by: Lars Knoll --- src/v4/debugging.h | 4 ++++ src/v4/qmljs_engine.h | 4 ++++ src/v4/qmljs_environment.h | 4 ++++ src/v4/qmljs_math.h | 4 ++++ src/v4/qmljs_runtime.h | 4 ++++ src/v4/qmljs_value.h | 4 ++++ src/v4/qv4argumentsobject.h | 4 ++++ src/v4/qv4array.h | 4 ++++ src/v4/qv4arrayobject.h | 4 ++++ src/v4/qv4booleanobject.h | 4 ++++ src/v4/qv4dateobject.h | 4 ++++ src/v4/qv4errorobject.h | 4 ++++ src/v4/qv4functionobject.h | 5 ++++- src/v4/qv4global.h | 4 ++++ src/v4/qv4globalobject.h | 5 ++++- src/v4/qv4identifier.h | 8 ++++++-- src/v4/qv4jsonobject.h | 8 ++++++-- src/v4/qv4managed.h | 4 ++++ src/v4/qv4mathobject.h | 5 ++++- src/v4/qv4mm.h | 4 ++++ src/v4/qv4numberobject.h | 4 ++++ src/v4/qv4object.h | 5 ++++- src/v4/qv4objectiterator.h | 8 ++++++-- src/v4/qv4objectproto.h | 4 ++++ src/v4/qv4propertydescriptor.h | 8 ++++++-- src/v4/qv4propertytable.h | 8 ++++++-- src/v4/qv4regexp.h | 4 ++++ src/v4/qv4regexpobject.h | 7 ++++--- src/v4/qv4string.h | 8 ++++++-- src/v4/qv4stringobject.h | 4 ++++ 30 files changed, 132 insertions(+), 19 deletions(-) diff --git a/src/v4/debugging.h b/src/v4/debugging.h index 00c96717a5..ecb596fe6e 100644 --- a/src/v4/debugging.h +++ b/src/v4/debugging.h @@ -36,6 +36,8 @@ #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace IR { @@ -138,4 +140,6 @@ private: // TODO: use opaque d-pointers here } // namespace Debugging } // namespace QQmlJS +QT_END_NAMESPACE + #endif // DEBUGGING_H diff --git a/src/v4/qmljs_engine.h b/src/v4/qmljs_engine.h index 013a2b6aca..bf3ccb233f 100644 --- a/src/v4/qmljs_engine.h +++ b/src/v4/qmljs_engine.h @@ -50,6 +50,8 @@ #include #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace Debugging { @@ -222,4 +224,6 @@ struct Q_V4_EXPORT ExecutionEngine } // namespace VM } // namespace QQmlJS +QT_END_NAMESPACE + #endif diff --git a/src/v4/qmljs_environment.h b/src/v4/qmljs_environment.h index 28f000b39f..5dc4a56609 100644 --- a/src/v4/qmljs_environment.h +++ b/src/v4/qmljs_environment.h @@ -44,6 +44,8 @@ #include "qv4global.h" #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -147,4 +149,6 @@ struct ExecutionContext } // namespace VM } // namespace QQmlJS +QT_END_NAMESPACE + #endif diff --git a/src/v4/qmljs_math.h b/src/v4/qmljs_math.h index 4645c5a324..b14152db31 100644 --- a/src/v4/qmljs_math.h +++ b/src/v4/qmljs_math.h @@ -48,6 +48,8 @@ #if !defined(QMLJS_LLVM_RUNTIME) && 1 //CPU(X86_64) +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -102,5 +104,7 @@ static inline Value mul_int32(int a, int b) } // namespace VM } // namespace QQmlJS +QT_END_NAMESPACE + #endif // !defined(QMLJS_LLVM_RUNTIME) && 1 //CPU(X86_64) #endif // QMLJS_MATH_H diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 2ea612642c..b93aa0c96e 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -60,6 +60,8 @@ # define TRACE2(x, y) #endif // TRACE1 +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -859,4 +861,6 @@ inline Bool __qmljs_strict_equal(Value x, Value y) } // namespace VM } // namespace QQmlJS +QT_END_NAMESPACE + #endif // QMLJS_RUNTIME_H diff --git a/src/v4/qmljs_value.h b/src/v4/qmljs_value.h index e11b08f50c..d33d5ed9c5 100644 --- a/src/v4/qmljs_value.h +++ b/src/v4/qmljs_value.h @@ -48,6 +48,8 @@ #include #include "qv4managed.h" +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -477,4 +479,6 @@ inline ErrorObject *Value::asErrorObject() const } // namespace VM } // namespace QQmlJS +QT_END_NAMESPACE + #endif diff --git a/src/v4/qv4argumentsobject.h b/src/v4/qv4argumentsobject.h index adbaf5db1a..be618a992e 100644 --- a/src/v4/qv4argumentsobject.h +++ b/src/v4/qv4argumentsobject.h @@ -44,6 +44,8 @@ #include #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -81,5 +83,7 @@ struct ArgumentsObject: Object { } } +QT_END_NAMESPACE + #endif diff --git a/src/v4/qv4array.h b/src/v4/qv4array.h index 86d438e4e1..13d2e0cc69 100644 --- a/src/v4/qv4array.h +++ b/src/v4/qv4array.h @@ -54,6 +54,8 @@ #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -636,4 +638,6 @@ public: } } +QT_END_NAMESPACE + #endif // QMAP_H diff --git a/src/v4/qv4arrayobject.h b/src/v4/qv4arrayobject.h index 8a15546c1c..eb8bd05128 100644 --- a/src/v4/qv4arrayobject.h +++ b/src/v4/qv4arrayobject.h @@ -45,6 +45,8 @@ #include "qv4functionobject.h" #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -92,4 +94,6 @@ struct ArrayPrototype: ArrayObject } // end of namespace VM } // end of namespace QQmlJS +QT_END_NAMESPACE + #endif // QV4ECMAOBJECTS_P_H diff --git a/src/v4/qv4booleanobject.h b/src/v4/qv4booleanobject.h index 97dfc50166..d0a7cdaf6b 100644 --- a/src/v4/qv4booleanobject.h +++ b/src/v4/qv4booleanobject.h @@ -45,6 +45,8 @@ #include "qv4functionobject.h" #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -69,4 +71,6 @@ struct BooleanPrototype: BooleanObject } // end of namespace VM } // end of namespace QQmlJS +QT_END_NAMESPACE + #endif // QV4ECMAOBJECTS_P_H diff --git a/src/v4/qv4dateobject.h b/src/v4/qv4dateobject.h index ede4473217..2c09ad28f3 100644 --- a/src/v4/qv4dateobject.h +++ b/src/v4/qv4dateobject.h @@ -45,6 +45,8 @@ #include "qv4functionobject.h" #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -122,4 +124,6 @@ struct DatePrototype: DateObject } // end of namespace VM } // end of namespace QQmlJS +QT_END_NAMESPACE + #endif // QV4ECMAOBJECTS_P_H diff --git a/src/v4/qv4errorobject.h b/src/v4/qv4errorobject.h index d158909474..452b7b3b45 100644 --- a/src/v4/qv4errorobject.h +++ b/src/v4/qv4errorobject.h @@ -44,6 +44,8 @@ #include "qv4object.h" #include "qv4functionobject.h" +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -202,4 +204,6 @@ struct URIErrorPrototype: URIErrorObject } // end of namespace VM } // end of namespace QQmlJS +QT_END_NAMESPACE + #endif // QV4ECMAOBJECTS_P_H diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index b651eedcae..53e06f320a 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -65,8 +65,9 @@ #include #include -namespace QQmlJS { +QT_BEGIN_NAMESPACE +namespace QQmlJS { namespace VM { struct Value; @@ -226,4 +227,6 @@ struct BoundFunction: FunctionObject { } // namespace VM } // namespace QQmlJS +QT_END_NAMESPACE + #endif // QMLJS_OBJECTS_H diff --git a/src/v4/qv4global.h b/src/v4/qv4global.h index e6150b0c9c..b9520b9498 100644 --- a/src/v4/qv4global.h +++ b/src/v4/qv4global.h @@ -32,6 +32,8 @@ #include +QT_BEGIN_NAMESPACE + #ifndef QT_STATIC # if defined(QT_BUILD_V4_LIB) # define Q_V4_EXPORT Q_DECL_EXPORT @@ -42,4 +44,6 @@ # define Q_V4_EXPORT #endif +QT_END_NAMESPACE + #endif // QV4GLOBAL_H diff --git a/src/v4/qv4globalobject.h b/src/v4/qv4globalobject.h index 6a586c1802..8399c94fe0 100644 --- a/src/v4/qv4globalobject.h +++ b/src/v4/qv4globalobject.h @@ -44,8 +44,9 @@ #include "qv4global.h" #include "qv4functionobject.h" -namespace QQmlJS { +QT_END_NAMESPACE +namespace QQmlJS { namespace VM { struct Q_V4_EXPORT EvalFunction : FunctionObject @@ -81,4 +82,6 @@ struct GlobalFunctions } // namespace VM } // namespace QQmlJS +QT_END_NAMESPACE + #endif // QMLJS_OBJECTS_H diff --git a/src/v4/qv4identifier.h b/src/v4/qv4identifier.h index 5a335ee6e9..3fa84d21a4 100644 --- a/src/v4/qv4identifier.h +++ b/src/v4/qv4identifier.h @@ -45,6 +45,8 @@ #include #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -104,7 +106,9 @@ public: } }; -} -} +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE #endif diff --git a/src/v4/qv4jsonobject.h b/src/v4/qv4jsonobject.h index 7c1df7c40b..0d1c776206 100644 --- a/src/v4/qv4jsonobject.h +++ b/src/v4/qv4jsonobject.h @@ -43,6 +43,8 @@ #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -54,8 +56,10 @@ struct JsonObject : Object { }; -} -} +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE #endif diff --git a/src/v4/qv4managed.h b/src/v4/qv4managed.h index a033b133aa..7aa1452d12 100644 --- a/src/v4/qv4managed.h +++ b/src/v4/qv4managed.h @@ -47,6 +47,8 @@ #include #include "qv4global.h" +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -177,4 +179,6 @@ private: } } +QT_END_NAMESPACE + #endif diff --git a/src/v4/qv4mathobject.h b/src/v4/qv4mathobject.h index 180313786f..339bb4f7f8 100644 --- a/src/v4/qv4mathobject.h +++ b/src/v4/qv4mathobject.h @@ -43,8 +43,9 @@ #include "qv4object.h" -namespace QQmlJS { +QT_BEGIN_NAMESPACE +namespace QQmlJS { namespace VM { struct MathObject: Object @@ -75,4 +76,6 @@ struct MathObject: Object } // namespace VM } // namespace QQmlJS +QT_END_NAMESPACE + #endif // QMLJS_OBJECTS_H diff --git a/src/v4/qv4mm.h b/src/v4/qv4mm.h index 6f747f53d2..2abc1b45e9 100644 --- a/src/v4/qv4mm.h +++ b/src/v4/qv4mm.h @@ -37,6 +37,8 @@ #define DETAILED_MM_STATS +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -125,4 +127,6 @@ protected: } // namespace VM } // namespace QQmlJS +QT_END_NAMESPACE + #endif // QV4GC_H diff --git a/src/v4/qv4numberobject.h b/src/v4/qv4numberobject.h index 83fb4ad124..aa2c9cf2f8 100644 --- a/src/v4/qv4numberobject.h +++ b/src/v4/qv4numberobject.h @@ -45,6 +45,8 @@ #include "qv4functionobject.h" #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -73,4 +75,6 @@ struct NumberPrototype: NumberObject } // end of namespace VM } // end of namespace QQmlJS +QT_END_NAMESPACE + #endif // QV4ECMAOBJECTS_P_H diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index 73d62bf209..5bcc5df6a2 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -61,8 +61,9 @@ #include #include -namespace QQmlJS { +QT_BEGIN_NAMESPACE +namespace QQmlJS { namespace VM { struct Value; @@ -189,4 +190,6 @@ struct ArrayObject: Object { } // namespace VM } // namespace QQmlJS +QT_END_NAMESPACE + #endif // QMLJS_OBJECTS_H diff --git a/src/v4/qv4objectiterator.h b/src/v4/qv4objectiterator.h index baecc25424..a463eb8798 100644 --- a/src/v4/qv4objectiterator.h +++ b/src/v4/qv4objectiterator.h @@ -43,6 +43,8 @@ #include "qmljs_value.h" +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -73,7 +75,9 @@ struct ObjectIterator Value nextPropertyNameAsString(); }; -} -} +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE #endif diff --git a/src/v4/qv4objectproto.h b/src/v4/qv4objectproto.h index 93527a28ef..585ccb4e6c 100644 --- a/src/v4/qv4objectproto.h +++ b/src/v4/qv4objectproto.h @@ -45,6 +45,8 @@ #include "qv4functionobject.h" #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -92,4 +94,6 @@ struct ObjectPrototype: Object } // end of namespace VM } // end of namespace QQmlJS +QT_END_NAMESPACE + #endif // QV4ECMAOBJECTS_P_H diff --git a/src/v4/qv4propertydescriptor.h b/src/v4/qv4propertydescriptor.h index dc9e1c556d..1cddf40b7d 100644 --- a/src/v4/qv4propertydescriptor.h +++ b/src/v4/qv4propertydescriptor.h @@ -43,6 +43,8 @@ #include "qmljs_value.h" +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -163,7 +165,9 @@ struct PropertyDescriptor { } }; -} -} +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE #endif diff --git a/src/v4/qv4propertytable.h b/src/v4/qv4propertytable.h index d50b26e4cc..b00acfab41 100644 --- a/src/v4/qv4propertytable.h +++ b/src/v4/qv4propertytable.h @@ -44,6 +44,8 @@ #include "qmljs_value.h" #include "qv4propertydescriptor.h" +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -217,7 +219,9 @@ private: int _allocated: 27; }; -} -} +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE #endif diff --git a/src/v4/qv4regexp.h b/src/v4/qv4regexp.h index 7eae033bec..f781417366 100644 --- a/src/v4/qv4regexp.h +++ b/src/v4/qv4regexp.h @@ -54,6 +54,8 @@ #include #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -89,4 +91,6 @@ private: } // end of namespace VM } // end of namespace QQmlJS +QT_END_NAMESPACE + #endif // QV4REGEXP_H diff --git a/src/v4/qv4regexpobject.h b/src/v4/qv4regexpobject.h index 50241f2daf..a37905f293 100644 --- a/src/v4/qv4regexpobject.h +++ b/src/v4/qv4regexpobject.h @@ -61,8 +61,9 @@ #include #include -namespace QQmlJS { +QT_BEGIN_NAMESPACE +namespace QQmlJS { namespace VM { struct RegExpObject: Object { @@ -92,9 +93,9 @@ struct RegExpPrototype: RegExpObject static Value method_compile(ExecutionContext *ctx); }; - - } // namespace VM } // namespace QQmlJS +QT_END_NAMESPACE + #endif // QMLJS_OBJECTS_H diff --git a/src/v4/qv4string.h b/src/v4/qv4string.h index 2ebc9e8c88..383c054e5e 100644 --- a/src/v4/qv4string.h +++ b/src/v4/qv4string.h @@ -44,6 +44,8 @@ #include #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -114,7 +116,9 @@ private: mutable uint stringHash; }; -} -} +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE #endif diff --git a/src/v4/qv4stringobject.h b/src/v4/qv4stringobject.h index 306f00c78e..85e11d5dcb 100644 --- a/src/v4/qv4stringobject.h +++ b/src/v4/qv4stringobject.h @@ -45,6 +45,8 @@ #include "qv4functionobject.h" #include +QT_BEGIN_NAMESPACE + namespace QQmlJS { namespace VM { @@ -97,4 +99,6 @@ struct StringPrototype: StringObject } // end of namespace VM } // end of namespace QQmlJS +QT_END_NAMESPACE + #endif // QV4ECMAOBJECTS_P_H -- cgit v1.2.3 From 0e1d8b6b89a390c81ff815c82f268159efc41544 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 31 Jan 2013 10:58:01 +0100 Subject: Fix compilation on Mac with clang. Change-Id: Ib5ecd92a0b7209ed505d17620ba4097e8d54a53c Reviewed-by: Simon Hausmann --- src/v4/qv4codegen.cpp | 3 ++- tools/v4/v4.pro | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index 4c4f7dd1d6..241b3e8329 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -1872,7 +1872,8 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, foreach (const QString &inheritedLocal, inheritedLocals) { function->LOCAL(inheritedLocal); unsigned tempIndex = entryBlock->newTemp(); - Environment::Member member = { Environment::UndefinedMember, tempIndex, 0 }; + Environment::Member member = { Environment::UndefinedMember, + static_cast(tempIndex), 0 }; _env->members.insert(inheritedLocal, member); } } diff --git a/tools/v4/v4.pro b/tools/v4/v4.pro index db187530a3..56d6b830ba 100644 --- a/tools/v4/v4.pro +++ b/tools/v4/v4.pro @@ -13,6 +13,6 @@ INCLUDEPATH += ../../src/3rdparty/masm/disassembler DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" DEFINES += QMLJS_NO_LLVM -DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 +DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 ENABLE_LLINT=0 load(qt_tool) -- cgit v1.2.3 From 80a48217325684e8109f6212fa86677181a1071c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 31 Jan 2013 11:04:47 +0100 Subject: Re-enable llvm code in main. The QMLJS_NO_LLVM flag should be provided by a pri file somewhere next to the other v4.pro, not here. Change-Id: I55c17d6a09f81141e9fa671bab9d3ee9a5830e6a Reviewed-by: Simon Hausmann --- tools/v4/v4.pro | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/v4/v4.pro b/tools/v4/v4.pro index 56d6b830ba..6b7a144373 100644 --- a/tools/v4/v4.pro +++ b/tools/v4/v4.pro @@ -12,7 +12,6 @@ INCLUDEPATH += ../../src/3rdparty/masm/assembler INCLUDEPATH += ../../src/3rdparty/masm/disassembler DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" -DEFINES += QMLJS_NO_LLVM DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 ENABLE_LLINT=0 load(qt_tool) -- cgit v1.2.3 From 62663f29542c4d5139b68c1b6cf69e1275103980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Thu, 31 Jan 2013 12:18:02 +0100 Subject: Build fix. Add proper LLVM detection for v4 tool. Change-Id: I03ad3099ce9622df67b7c61ce1e72752b9f6e4bd Reviewed-by: Simon Hausmann --- tools/v4/v4.pro | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/v4/v4.pro b/tools/v4/v4.pro index 6b7a144373..0b23270ff4 100644 --- a/tools/v4/v4.pro +++ b/tools/v4/v4.pro @@ -14,4 +14,9 @@ INCLUDEPATH += ../../src/3rdparty/masm/disassembler DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 ENABLE_LLINT=0 +llvm { + # TODO LLVM support doesn't work correctly +} else { + DEFINES += QMLJS_NO_LLVM +} load(qt_tool) -- cgit v1.2.3 From 0689c111cf7286b449dba6c625d477e47d8a0d39 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 31 Jan 2013 23:52:09 +0100 Subject: Avoid duplicated feature defines between tools/v4/v4.pro and src/v4/v4.pro Centralize the macros in v4.pri that's included by both. Change-Id: I1ae2ed3b7b97a4e905d2e4ae563c99c964253bb1 Reviewed-by: Lars Knoll --- .gitignore | 1 + .../double-conversion/double-conversion.pri | 2 +- src/3rdparty/masm/masm-defs.pri | 24 ++++++++++++++++++++++ src/3rdparty/masm/masm.pri | 24 ---------------------- src/v4/v4.pri | 6 ++++++ src/v4/v4.pro | 6 +----- tools/v4/v4.pro | 17 +-------------- 7 files changed, 34 insertions(+), 46 deletions(-) create mode 100644 src/3rdparty/masm/masm-defs.pri create mode 100644 src/v4/v4.pri diff --git a/.gitignore b/.gitignore index dede3138fa..8df072ad56 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ udis86_itab.* *.bc *.ll RegExpJitTables.h +.qmake.cache diff --git a/src/3rdparty/double-conversion/double-conversion.pri b/src/3rdparty/double-conversion/double-conversion.pri index 8bb37c63e9..4ad5f9f7a7 100644 --- a/src/3rdparty/double-conversion/double-conversion.pri +++ b/src/3rdparty/double-conversion/double-conversion.pri @@ -1,4 +1,4 @@ -INCLUDEPATH += $PWD +INCLUDEPATH += $$PWD VPATH += $$PWD SOURCES += $$PWD/*.cc HEADERS += $$PWD/*.h diff --git a/src/3rdparty/masm/masm-defs.pri b/src/3rdparty/masm/masm-defs.pri new file mode 100644 index 0000000000..34b86e30e4 --- /dev/null +++ b/src/3rdparty/masm/masm-defs.pri @@ -0,0 +1,24 @@ + +DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" + +DEFINES += ENABLE_LLINT=0 +DEFINES += ENABLE_DFG_JIT=0 +DEFINES += ENABLE_JIT=1 +DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 +DEFINES += ENABLE_ASSEMBLER=1 +DEFINES += ENABLE_YARR_JIT=0 +DEFINES += BUILDING_QT__ + +INCLUDEPATH += $$PWD/jit +INCLUDEPATH += $$PWD/assembler +INCLUDEPATH += $$PWD/runtime +INCLUDEPATH += $$PWD/wtf +INCLUDEPATH += $$PWD/stubs +INCLUDEPATH += $$PWD/stubs/wtf +INCLUDEPATH += $$PWD + +DEFINES += WTF_USE_UDIS86=1 +INCLUDEPATH += $$PWD/disassembler +INCLUDEPATH += $$PWD/disassembler/udis86 +INCLUDEPATH += $$_OUT_PWD + diff --git a/src/3rdparty/masm/masm.pri b/src/3rdparty/masm/masm.pri index 92ad32d4ea..f172762089 100644 --- a/src/3rdparty/masm/masm.pri +++ b/src/3rdparty/masm/masm.pri @@ -1,4 +1,3 @@ - HEADERS += $$PWD/assembler/*.h SOURCES += $$PWD/assembler/ARMAssembler.cpp SOURCES += $$PWD/assembler/ARMv7Assembler.cpp @@ -31,28 +30,6 @@ HEADERS += $$PWD/wtf/PageReservation.h SOURCES += $$PWD/stubs/WTFStubs.cpp HEADERS += $$PWD/stubs/WTFStubs.h -DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" - -DEFINES += ENABLE_LLINT=0 -DEFINES += ENABLE_DFG_JIT=0 -DEFINES += ENABLE_JIT=1 -DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 -DEFINES += ENABLE_ASSEMBLER=1 - -DEFINES += BUILDING_QT__ - -INCLUDEPATH += $$PWD/jit -INCLUDEPATH += $$PWD/assembler -INCLUDEPATH += $$PWD/runtime -INCLUDEPATH += $$PWD/wtf -INCLUDEPATH += $$PWD/stubs -INCLUDEPATH += $$PWD/stubs/wtf -INCLUDEPATH += $$PWD - -DEFINES += WTF_USE_UDIS86=1 -INCLUDEPATH += $$PWD/disassembler -INCLUDEPATH += $$PWD/disassembler/udis86 -INCLUDEPATH += $$_OUT_PWD SOURCES += $$PWD/disassembler/Disassembler.cpp SOURCES += $$PWD/disassembler/UDis86Disassembler.cpp SOURCES += $$PWD/disassembler/udis86/udis86.c @@ -63,7 +40,6 @@ SOURCES += $$PWD/disassembler/udis86/udis86_syn-att.c SOURCES += $$PWD/disassembler/udis86/udis86_syn.c SOURCES += $$PWD/disassembler/udis86/udis86_syn-intel.c -DEFINES += ENABLE_YARR_JIT=0 SOURCES += \ $$PWD/yarr/YarrCanonicalizeUCS2.cpp \ $$PWD/yarr/YarrInterpreter.cpp \ diff --git a/src/v4/v4.pri b/src/v4/v4.pri new file mode 100644 index 0000000000..6f70a8c694 --- /dev/null +++ b/src/v4/v4.pri @@ -0,0 +1,6 @@ + +include(../3rdparty/masm/masm-defs.pri) + +!llvm: DEFINES += QMLJS_NO_LLVM + +INCLUDEPATH += $$PWD diff --git a/src/v4/v4.pro b/src/v4/v4.pro index fb7ea80544..24832ecd9b 100644 --- a/src/v4/v4.pro +++ b/src/v4/v4.pro @@ -124,11 +124,6 @@ GEN_LLVM_RUNTIME_FLAGS -= -pedantic gen_llvm_runtime.target = llvm_runtime gen_llvm_runtime.commands = clang -O2 -emit-llvm -c $(INCPATH) $$GEN_LLVM_RUNTIME_FLAGS -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o $$LLVM_RUNTIME_BC - -} else { - -DEFINES += QMLJS_NO_LLVM - } # Use SSE2 floating point math on 32 bit instead of the default @@ -155,3 +150,4 @@ QMAKE_EXTRA_TARGETS += checkmothtarget include(moth/moth.pri) include(../3rdparty/masm/masm.pri) include(../3rdparty/double-conversion/double-conversion.pri) +include(v4.pri) diff --git a/tools/v4/v4.pro b/tools/v4/v4.pro index 0b23270ff4..3f878fa6d4 100644 --- a/tools/v4/v4.pro +++ b/tools/v4/v4.pro @@ -2,21 +2,6 @@ TEMPLATE = app QT = v4-private core-private qmldevtools-private SOURCES = main.cpp -INCLUDEPATH += ../../src/v4 -INCLUDEPATH += ../../src/3rdparty/masm -INCLUDEPATH += ../../src/3rdparty/masm/wtf -INCLUDEPATH += ../../src/3rdparty/masm/stubs -INCLUDEPATH += ../../src/3rdparty/masm/stubs/wtf -INCLUDEPATH += ../../src/3rdparty/masm/jit -INCLUDEPATH += ../../src/3rdparty/masm/assembler -INCLUDEPATH += ../../src/3rdparty/masm/disassembler +include(../../src/v4/v4.pri) -DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" -DEFINES += ENABLE_JIT_CONSTANT_BLINDING=0 ENABLE_LLINT=0 - -llvm { - # TODO LLVM support doesn't work correctly -} else { - DEFINES += QMLJS_NO_LLVM -} load(qt_tool) -- cgit v1.2.3 From 0316c863456aec5b81197b80679971f5f17abf6d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 1 Feb 2013 14:31:24 +0100 Subject: Fix possible out of bounds access This lead to crashes in some complex code where a pointer was just pointing to the end of the allocated chunk, and we'd then read outside the chunk bounds for marking. Change-Id: Ica41c4e98eb9901fbb8d291289a9c91078ded111 Reviewed-by: Erik Verbruggen --- src/v4/qv4mm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qv4mm.cpp b/src/v4/qv4mm.cpp index c77ec1eaba..d12388a01b 100644 --- a/src/v4/qv4mm.cpp +++ b/src/v4/qv4mm.cpp @@ -361,7 +361,7 @@ void MemoryManager::collectFromStack() const for (QVector::Iterator it = m_d->heapChunks.begin(), end = m_d->heapChunks.end(); it != end; ++it) { heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()); - heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()) + it->memory.size(); + heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()) + it->memory.size() - it->chunkSize; } for (; current < top; ++current) { -- cgit v1.2.3 From deac4a2cd6a17b42e66021f4babf6ea67f050330 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 1 Feb 2013 14:32:30 +0100 Subject: Fix a bug in Array::push_back We would in some cases write the value to the wrong place. This fixes the type error in the DeltaBlue V8 benchmark. Change-Id: Ic64570248d83890d5e66d718b9087c2bf966690a Reviewed-by: Erik Verbruggen --- src/v4/qv4array.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/v4/qv4array.h b/src/v4/qv4array.h index 13d2e0cc69..9fb8bb608b 100644 --- a/src/v4/qv4array.h +++ b/src/v4/qv4array.h @@ -553,10 +553,9 @@ public: if (!offset) getHeadRoom(); - PropertyDescriptor pd; - fillDescriptor(&pd, v); --offset; - values[offset] = pd; + PropertyDescriptor &pd = values[offset]; + fillDescriptor(&pd, v); } else { uint idx = allocValue(v); sparse->push_front(idx); @@ -590,9 +589,10 @@ public: } void push_back(Value v) { if (!sparse) { - PropertyDescriptor pd; + if (len + offset >= (uint)values.size()) + values.resize(values.size() + 1); + PropertyDescriptor &pd = values[len + offset]; fillDescriptor(&pd, v); - values.append(pd); } else { uint idx = allocValue(v); sparse->push_back(idx, len); -- cgit v1.2.3 From 3d96b151936e8f9ee03d7cef54b09d5032a5257e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 1 Feb 2013 15:59:22 +0100 Subject: Refactor property access Move the property descriptors into a a vector and separate them from the lookup table. This is a pre-requirement for a hidden class implementation later on. Change-Id: Ib08b8152597ae0b6c883eef0fced55ee0cb4c286 Reviewed-by: Simon Hausmann --- src/v4/qv4array.cpp | 13 ++++++++++++- src/v4/qv4array.h | 13 ++++--------- src/v4/qv4arrayobject.cpp | 2 +- src/v4/qv4object.cpp | 16 ++++++++++------ src/v4/qv4objectiterator.cpp | 11 +++++++---- src/v4/qv4propertytable.h | 21 ++++++++++++++++----- src/v4/qv4regexpobject.cpp | 16 +++++++++++----- src/v4/qv4regexpobject.h | 2 +- src/v4/qv4stringobject.cpp | 2 +- 9 files changed, 63 insertions(+), 33 deletions(-) diff --git a/src/v4/qv4array.cpp b/src/v4/qv4array.cpp index 9879b2f372..5f0d838a93 100644 --- a/src/v4/qv4array.cpp +++ b/src/v4/qv4array.cpp @@ -464,7 +464,7 @@ SparseArrayNode *SparseArray::insert(uint akey) Array::Array(const Array &other) : len(other.len) - , lengthProperty(0) + , arrayObject(0) , values(other.values) , sparse(0) { @@ -581,7 +581,18 @@ void Array::initSparse() } } +void Array::setLengthUnchecked(uint l) +{ + len = l; + if (arrayObject) { + // length is always the first property of an array + PropertyDescriptor &lengthProperty = arrayObject->members->values[0]; + lengthProperty.value = Value::fromUInt32(l); + } +} + bool Array::setLength(uint newLen) { + const PropertyDescriptor *lengthProperty = arrayObject->members->values.constData(); if (lengthProperty && !lengthProperty->isWritable()) return false; uint oldLen = length(); diff --git a/src/v4/qv4array.h b/src/v4/qv4array.h index 9fb8bb608b..b165ed260a 100644 --- a/src/v4/qv4array.h +++ b/src/v4/qv4array.h @@ -367,7 +367,7 @@ class Array friend struct ArrayPrototype; uint len; - PropertyDescriptor *lengthProperty; + ArrayObject *arrayObject; union { uint freeList; uint offset; @@ -429,7 +429,7 @@ class Array } public: - Array() : len(0), lengthProperty(0), offset(0), sparse(0) {} + Array() : len(0), arrayObject(0), offset(0), sparse(0) {} Array(const Array &other); ~Array() { delete sparse; } void initSparse(); @@ -437,14 +437,9 @@ public: uint length() const { return len; } bool setLength(uint newLen); - void setLengthProperty(PropertyDescriptor *pd) { lengthProperty = pd; } - PropertyDescriptor *getLengthProperty() { return lengthProperty; } + void setArrayObject(ArrayObject *a) { arrayObject = a; } - void setLengthUnchecked(uint l) { - len = l; - if (lengthProperty) - lengthProperty->value = Value::fromUInt32(l); - } + void setLengthUnchecked(uint l); PropertyDescriptor *insert(uint index) { PropertyDescriptor *pd; diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp index 032b56f27b..680a373d86 100644 --- a/src/v4/qv4arrayobject.cpp +++ b/src/v4/qv4arrayobject.cpp @@ -654,7 +654,7 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject(ctx); - a->array.setLength(len); + a->array.setLengthUnchecked(len); for (uint k = 0; k < len; ++k) { bool exists; diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 6f5fb60723..6a5aa213e3 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -187,11 +187,13 @@ void Object::markObjects() if (!(*it)) continue; (*it)->name->mark(); - PropertyDescriptor &pd = (*it)->descriptor; + } + for (int i = 0; i < (uint)members->values.size(); ++i) { + const PropertyDescriptor &pd = members->values.at(i); if (pd.isData()) { if (Managed *m = pd.value.asManaged()) m->mark(); - } else if (pd.isAccessor()) { + } else if (pd.isAccessor()) { if (pd.get) pd.get->mark(); if (pd.set) @@ -503,7 +505,7 @@ bool Object::__delete__(ExecutionContext *ctx, String *name) if (members) { if (PropertyTableEntry *entry = members->findEntry(name)) { - if (entry->descriptor.isConfigurable()) { + if (members->values[entry->valueIndex].isConfigurable()) { members->remove(entry); return true; } @@ -536,7 +538,8 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr PropertyDescriptor *current; if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { - PropertyDescriptor *lp = array.getLengthProperty(); + PropertyDescriptor *lp = members->values.data(); // length is always the first property + assert(lp == members->find(ctx->engine->id_length)); if (desc->isEmpty() || desc->isSubset(lp)) return true; if (!lp->isWritable() || desc->type == PropertyDescriptor::Accessor || desc->isConfigurable() || desc->isEnumerable()) @@ -584,7 +587,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Prop PropertyDescriptor *current; // 15.4.5.1, 4b - if (isArrayObject() && index >= array.length() && !array.getLengthProperty()->isWritable()) + if (isArrayObject() && index >= array.length() && !members->values.at(0).isWritable()) goto reject; if (isNonStrictArgumentsObject) @@ -693,7 +696,8 @@ void ArrayObject::init(ExecutionContext *context) pd->enumberable = PropertyDescriptor::Disabled; pd->configurable = PropertyDescriptor::Disabled; pd->value = Value::fromInt32(0); - array.setLengthProperty(pd); + array.setArrayObject(this); + assert(pd = members->values.data()); } diff --git a/src/v4/qv4objectiterator.cpp b/src/v4/qv4objectiterator.cpp index 8da2e7bdba..8ce62371ed 100644 --- a/src/v4/qv4objectiterator.cpp +++ b/src/v4/qv4objectiterator.cpp @@ -128,10 +128,13 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) PropertyTableEntry *pt = current->members->_properties[tableIndex]; ++tableIndex; // ### check that it's not a repeated attribute - if (pt && (!(flags & EnumberableOnly) || pt->descriptor.isEnumerable())) { - *name = pt->name; - p = &pt->descriptor; - return p; + if (pt) { + PropertyDescriptor *pd = current->members->values.data() + pt->valueIndex; + if (!(flags & EnumberableOnly) || pd->isEnumerable()) { + *name = pt->name; + p = pd; + return p; + } } } return 0; diff --git a/src/v4/qv4propertytable.h b/src/v4/qv4propertytable.h index b00acfab41..30aa5d2146 100644 --- a/src/v4/qv4propertytable.h +++ b/src/v4/qv4propertytable.h @@ -52,13 +52,14 @@ namespace VM { struct ObjectIterator; struct PropertyTableEntry { - PropertyDescriptor descriptor; String *name; + uint valueIndex; PropertyTableEntry *next; int index; inline PropertyTableEntry(String *name) : name(name), + valueIndex(UINT_MAX), next(0), index(-1) { } @@ -69,6 +70,8 @@ struct PropertyTableEntry { class PropertyTable { Q_DISABLE_COPY(PropertyTable) + friend class Array; + friend class ArrayObject; public: PropertyTable() @@ -109,6 +112,9 @@ public: _properties[prop->index] = 0; prop->next = _freeList; _freeList = prop; + // ### empty space in value array + values[prop->valueIndex].type = PropertyDescriptor::Generic; + values[prop->valueIndex].writable = PropertyDescriptor::Undefined; } PropertyTableEntry *findEntry(String *name) const @@ -123,12 +129,12 @@ public: return 0; } - PropertyDescriptor *find(String *name) const + PropertyDescriptor *find(String *name) { if (_properties) { for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { if (prop && prop->name->isEqualTo(name)) - return &prop->descriptor; + return values.data() + prop->valueIndex; } } @@ -138,7 +144,7 @@ public: PropertyDescriptor *insert(String *name) { if (PropertyTableEntry *prop = findEntry(name)) - return &prop->descriptor; + return values.data() + prop->valueIndex; if (_propertyCount == _allocated) { if (! _allocated) @@ -173,7 +179,9 @@ public: bucket = prop; } - return &prop->descriptor; + prop->valueIndex = values.size(); + values.resize(values.size() + 1); + return values.data() + prop->valueIndex; } private: @@ -210,6 +218,8 @@ private: private: friend struct ObjectIterator; + friend struct Object; + PropertyTableEntry **_properties; PropertyTableEntry **_buckets; PropertyTableEntry *_freeList; @@ -217,6 +227,7 @@ private: int _bucketCount; int _primeIdx: 5; int _allocated: 27; + QVector values; }; } // namespace VM diff --git a/src/v4/qv4regexpobject.cpp b/src/v4/qv4regexpobject.cpp index 31fb584fb1..5a0faf12d0 100644 --- a/src/v4/qv4regexpobject.cpp +++ b/src/v4/qv4regexpobject.cpp @@ -72,7 +72,7 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bo if (!members) members.reset(new PropertyTable()); - lastIndexProperty = members->insert(engine->newIdentifier(QStringLiteral("lastIndex"))); + PropertyDescriptor *lastIndexProperty = members->insert(engine->newIdentifier(QStringLiteral("lastIndex"))); lastIndexProperty->type = PropertyDescriptor::Data; lastIndexProperty->writable = PropertyDescriptor::Enabled; lastIndexProperty->enumberable = PropertyDescriptor::Disabled; @@ -86,6 +86,12 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bo defineReadonlyProperty(engine->newIdentifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); } +PropertyDescriptor *RegExpObject::lastIndexProperty(ExecutionContext *ctx) +{ + return members->find(ctx->engine->newIdentifier(QStringLiteral("lastIndex"))); +} + + RegExpCtor::RegExpCtor(ExecutionContext *scope) : FunctionObject(scope) @@ -167,16 +173,16 @@ Value RegExpPrototype::method_exec(ExecutionContext *ctx) arg = __qmljs_to_string(arg, ctx); QString s = arg.stringValue()->toQString(); - int offset = r->global ? r->lastIndexProperty->value.toInt32(ctx) : 0; + int offset = r->global ? r->lastIndexProperty(ctx)->value.toInt32(ctx) : 0; if (offset < 0 || offset > s.length()) { - r->lastIndexProperty->value = Value::fromInt32(0); + r->lastIndexProperty(ctx)->value = Value::fromInt32(0); return Value::nullValue(); } uint* matchOffsets = (uint*)alloca(r->value->captureCount() * 2 * sizeof(uint)); int result = r->value->match(s, offset, matchOffsets); if (result == -1) { - r->lastIndexProperty->value = Value::fromInt32(0); + r->lastIndexProperty(ctx)->value = Value::fromInt32(0); return Value::nullValue(); } @@ -195,7 +201,7 @@ Value RegExpPrototype::method_exec(ExecutionContext *ctx) array->__put__(ctx, QLatin1String("input"), arg); if (r->global) - r->lastIndexProperty->value = Value::fromInt32(matchOffsets[1]); + r->lastIndexProperty(ctx)->value = Value::fromInt32(matchOffsets[1]); return Value::fromObject(array); } diff --git a/src/v4/qv4regexpobject.h b/src/v4/qv4regexpobject.h index a37905f293..cd7953304b 100644 --- a/src/v4/qv4regexpobject.h +++ b/src/v4/qv4regexpobject.h @@ -68,7 +68,7 @@ namespace VM { struct RegExpObject: Object { RefPtr value; - PropertyDescriptor *lastIndexProperty; + PropertyDescriptor *lastIndexProperty(ExecutionContext *ctx); bool global; RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global); }; diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp index 4e0285cdb0..788d6cd1a9 100644 --- a/src/v4/qv4stringobject.cpp +++ b/src/v4/qv4stringobject.cpp @@ -420,7 +420,7 @@ Value StringPrototype::method_replace(ExecutionContext *ctx) offset = qMax(offset + 1, matchOffsets[oldSize + 1]); } if (regExp->global) - regExp->lastIndexProperty->value = Value::fromUInt32(0); + regExp->lastIndexProperty(ctx)->value = Value::fromUInt32(0); numStringMatches = matchOffsets.size() / (regExp->value->captureCount() * 2); numCaptures = regExp->value->captureCount(); } else { -- cgit v1.2.3 From acd1918f135469f3267796627adbb7e32befc217 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 2 Feb 2013 09:52:27 +0100 Subject: Move the property data out of the hash table Change-Id: I814afa75706cec785ed53039f55ee407dec1ef90 Reviewed-by: Simon Hausmann --- src/v4/qv4array.cpp | 4 +-- src/v4/qv4functionobject.cpp | 6 ++--- src/v4/qv4jsonobject.cpp | 2 +- src/v4/qv4object.cpp | 60 ++++++++++++++++++++++++++++++-------------- src/v4/qv4object.h | 7 ++++++ src/v4/qv4objectiterator.cpp | 2 +- src/v4/qv4propertytable.h | 21 +++++++--------- src/v4/qv4regexpobject.cpp | 5 ++-- 8 files changed, 67 insertions(+), 40 deletions(-) diff --git a/src/v4/qv4array.cpp b/src/v4/qv4array.cpp index 5f0d838a93..decef65d94 100644 --- a/src/v4/qv4array.cpp +++ b/src/v4/qv4array.cpp @@ -586,13 +586,13 @@ void Array::setLengthUnchecked(uint l) len = l; if (arrayObject) { // length is always the first property of an array - PropertyDescriptor &lengthProperty = arrayObject->members->values[0]; + PropertyDescriptor &lengthProperty = arrayObject->memberData[ArrayObject::LengthPropertyIndex]; lengthProperty.value = Value::fromUInt32(l); } } bool Array::setLength(uint newLen) { - const PropertyDescriptor *lengthProperty = arrayObject->members->values.constData(); + const PropertyDescriptor *lengthProperty = arrayObject->memberData.constData() + ArrayObject::LengthPropertyIndex; if (lengthProperty && !lengthProperty->isWritable()) return false; uint oldLen = length(); diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 41c06f0896..2d96f9287f 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -361,7 +361,7 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) Object *proto = scope->engine->newObject(); proto->defineDefaultProperty(scope->engine->id_constructor, Value::fromObject(this)); - PropertyDescriptor *pd = members->insert(scope->engine->id_prototype); + PropertyDescriptor *pd = insertMember(scope->engine->id_prototype); pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Enabled; pd->enumberable = PropertyDescriptor::Disabled; @@ -440,8 +440,8 @@ BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Va PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); pd.configurable = PropertyDescriptor::Disabled; pd.enumberable = PropertyDescriptor::Disabled; - *members->insert(scope->engine->id_arguments) = pd; - *members->insert(scope->engine->id_caller) = pd; + *insertMember(scope->engine->id_arguments) = pd; + *insertMember(scope->engine->id_caller) = pd; } Value BoundFunction::call(ExecutionContext *context, Value, Value *args, int argc) diff --git a/src/v4/qv4jsonobject.cpp b/src/v4/qv4jsonobject.cpp index 4c7ea3de7d..5c0027f538 100644 --- a/src/v4/qv4jsonobject.cpp +++ b/src/v4/qv4jsonobject.cpp @@ -282,7 +282,7 @@ bool Parser::parseMember(Object *o) if (!parseValue(&val)) return false; - PropertyDescriptor *p = o->members->insert(context->engine->newIdentifier(key)); + PropertyDescriptor *p = o->insertMember(context->engine->newIdentifier(key)); p->value = val; END; diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 6a5aa213e3..5903df4c43 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -129,7 +129,7 @@ void Object::defineDefaultProperty(String *name, Value value) { if (!members) members.reset(new PropertyTable()); - PropertyDescriptor *pd = members->insert(name); + PropertyDescriptor *pd = insertMember(name); pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Enabled; pd->enumberable = PropertyDescriptor::Disabled; @@ -169,7 +169,7 @@ void Object::defineReadonlyProperty(String *name, Value value) { if (!members) members.reset(new PropertyTable()); - PropertyDescriptor *pd = members->insert(name); + PropertyDescriptor *pd = insertMember(name); pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Disabled; pd->enumberable = PropertyDescriptor::Disabled; @@ -188,8 +188,8 @@ void Object::markObjects() continue; (*it)->name->mark(); } - for (int i = 0; i < (uint)members->values.size(); ++i) { - const PropertyDescriptor &pd = members->values.at(i); + for (int i = 0; i < (uint)memberData.size(); ++i) { + const PropertyDescriptor &pd = memberData.at(i); if (pd.isData()) { if (Managed *m = pd.value.asManaged()) m->mark(); @@ -204,6 +204,19 @@ void Object::markObjects() array.markObjects(); } +PropertyDescriptor *Object::insertMember(String *s) +{ + if (!members) + members.reset(new PropertyTable); + + PropertyTableEntry *e = members->insert(s); + if (e->valueIndex == UINT_MAX) { + e->valueIndex = memberData.size(); + memberData.resize(memberData.size() + 1); + } + return memberData.data() + e->valueIndex; +} + // Section 8.12.1 PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *name) { @@ -211,8 +224,11 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *na if (idx != String::InvalidArrayIndex) return __getOwnProperty__(ctx, idx); - if (members) - return members->find(name); + if (members) { + uint idx = members->find(name); + if (idx < UINT_MAX) + return memberData.data() + idx; + } return 0; } @@ -238,8 +254,9 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, Str Object *o = this; while (o) { if (o->members) { - if (PropertyDescriptor *p = o->members->find(name)) - return p; + uint idx = o->members->find(name); + if (idx < UINT_MAX) + return o->memberData.data() + idx; } o = o->prototype; } @@ -281,10 +298,11 @@ Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) Object *o = this; while (o) { if (o->members) { - if (PropertyDescriptor *p = o->members->find(name)) { + uint idx = o->members->find(name); + if (idx < UINT_MAX) { if (hasProperty) *hasProperty = true; - return getValue(ctx, p); + return getValue(ctx, o->memberData.data() + idx); } } o = o->prototype; @@ -398,7 +416,7 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) } { - PropertyDescriptor *p = members->insert(name); + PropertyDescriptor *p = insertMember(name); p->type = PropertyDescriptor::Data; p->value = value; p->configurable = PropertyDescriptor::Enabled; @@ -479,7 +497,7 @@ bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const name->makeIdentifier(ctx); - if (members && members->find(name) != 0) + if (members && members->find(name) != UINT_MAX) return true; return prototype ? prototype->__hasProperty__(ctx, name) : false; @@ -505,8 +523,12 @@ bool Object::__delete__(ExecutionContext *ctx, String *name) if (members) { if (PropertyTableEntry *entry = members->findEntry(name)) { - if (members->values[entry->valueIndex].isConfigurable()) { + PropertyDescriptor &pd = memberData[entry->valueIndex]; + if (pd.isConfigurable()) { members->remove(entry); + // ### leaves a hole in memberData + pd.type = PropertyDescriptor::Generic; + pd.writable = PropertyDescriptor::Undefined; return true; } if (ctx->strictMode) @@ -538,8 +560,8 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr PropertyDescriptor *current; if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { - PropertyDescriptor *lp = members->values.data(); // length is always the first property - assert(lp == members->find(ctx->engine->id_length)); + PropertyDescriptor *lp = memberData.data() + ArrayObject::LengthPropertyIndex; + assert(0 == members->find(ctx->engine->id_length)); if (desc->isEmpty() || desc->isSubset(lp)) return true; if (!lp->isWritable() || desc->type == PropertyDescriptor::Accessor || desc->isConfigurable() || desc->isEnumerable()) @@ -569,7 +591,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr if (!extensible) goto reject; // clause 4 - PropertyDescriptor *pd = members->insert(name); + PropertyDescriptor *pd = insertMember(name); *pd = *desc; pd->fullyPopulated(); return true; @@ -587,7 +609,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Prop PropertyDescriptor *current; // 15.4.5.1, 4b - if (isArrayObject() && index >= array.length() && !members->values.at(0).isWritable()) + if (isArrayObject() && index >= array.length() && !memberData.at(ArrayObject::LengthPropertyIndex).isWritable()) goto reject; if (isNonStrictArgumentsObject) @@ -690,14 +712,14 @@ void ArrayObject::init(ExecutionContext *context) if (!members) members.reset(new PropertyTable()); - PropertyDescriptor *pd = members->insert(context->engine->id_length); + PropertyDescriptor *pd = insertMember(context->engine->id_length); + assert(pd == memberData.constData() + LengthPropertyIndex); pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Enabled; pd->enumberable = PropertyDescriptor::Disabled; pd->configurable = PropertyDescriptor::Disabled; pd->value = Value::fromInt32(0); array.setArrayObject(this); - assert(pd = members->values.data()); } diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index 5bcc5df6a2..dd67bee488 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -103,6 +103,7 @@ struct URIErrorPrototype; struct Q_V4_EXPORT Object: Managed { Object *prototype; QScopedPointer members; + QVector memberData; Array array; Object() @@ -153,6 +154,8 @@ struct Q_V4_EXPORT Object: Managed { void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value); void defineReadonlyProperty(String *name, Value value); + PropertyDescriptor *insertMember(String *s); + protected: virtual void markObjects(); @@ -181,6 +184,10 @@ struct NumberObject: Object { }; struct ArrayObject: Object { + enum { + LengthPropertyIndex = 0 + }; + ArrayObject(ExecutionContext *ctx) { init(ctx); } ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setLengthUnchecked(array.length()); } void init(ExecutionContext *context); diff --git a/src/v4/qv4objectiterator.cpp b/src/v4/qv4objectiterator.cpp index 8ce62371ed..62f3c30f40 100644 --- a/src/v4/qv4objectiterator.cpp +++ b/src/v4/qv4objectiterator.cpp @@ -129,7 +129,7 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) ++tableIndex; // ### check that it's not a repeated attribute if (pt) { - PropertyDescriptor *pd = current->members->values.data() + pt->valueIndex; + PropertyDescriptor *pd = current->memberData.data() + pt->valueIndex; if (!(flags & EnumberableOnly) || pd->isEnumerable()) { *name = pt->name; p = pd; diff --git a/src/v4/qv4propertytable.h b/src/v4/qv4propertytable.h index 30aa5d2146..787c319121 100644 --- a/src/v4/qv4propertytable.h +++ b/src/v4/qv4propertytable.h @@ -112,9 +112,6 @@ public: _properties[prop->index] = 0; prop->next = _freeList; _freeList = prop; - // ### empty space in value array - values[prop->valueIndex].type = PropertyDescriptor::Generic; - values[prop->valueIndex].writable = PropertyDescriptor::Undefined; } PropertyTableEntry *findEntry(String *name) const @@ -129,22 +126,22 @@ public: return 0; } - PropertyDescriptor *find(String *name) + uint find(String *name) { if (_properties) { for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { if (prop && prop->name->isEqualTo(name)) - return values.data() + prop->valueIndex; + return prop->valueIndex; } } - return 0; + return UINT_MAX; } - PropertyDescriptor *insert(String *name) + PropertyTableEntry *insert(String *name) { if (PropertyTableEntry *prop = findEntry(name)) - return values.data() + prop->valueIndex; + return prop; if (_propertyCount == _allocated) { if (! _allocated) @@ -179,9 +176,10 @@ public: bucket = prop; } - prop->valueIndex = values.size(); - values.resize(values.size() + 1); - return values.data() + prop->valueIndex; + return prop; +// prop->valueIndex = values.size(); +// values.resize(values.size() + 1); +// return values.data() + prop->valueIndex; } private: @@ -227,7 +225,6 @@ private: int _bucketCount; int _primeIdx: 5; int _allocated: 27; - QVector values; }; } // namespace VM diff --git a/src/v4/qv4regexpobject.cpp b/src/v4/qv4regexpobject.cpp index 5a0faf12d0..69fd5bbe58 100644 --- a/src/v4/qv4regexpobject.cpp +++ b/src/v4/qv4regexpobject.cpp @@ -72,7 +72,7 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bo if (!members) members.reset(new PropertyTable()); - PropertyDescriptor *lastIndexProperty = members->insert(engine->newIdentifier(QStringLiteral("lastIndex"))); + PropertyDescriptor *lastIndexProperty = insertMember(engine->newIdentifier(QStringLiteral("lastIndex"))); lastIndexProperty->type = PropertyDescriptor::Data; lastIndexProperty->writable = PropertyDescriptor::Enabled; lastIndexProperty->enumberable = PropertyDescriptor::Disabled; @@ -88,7 +88,8 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bo PropertyDescriptor *RegExpObject::lastIndexProperty(ExecutionContext *ctx) { - return members->find(ctx->engine->newIdentifier(QStringLiteral("lastIndex"))); + assert(0 == members->find(ctx->engine->newIdentifier(QStringLiteral("lastIndex")))); + return &memberData[0]; } -- cgit v1.2.3 From 463599739485b537deca3de0fac12f6d90f63540 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 3 Feb 2013 10:25:55 +0100 Subject: Rename variables and functions Prepare for merging the Array class with ArrayObject Change-Id: I03a2b10f290e18f8933efc11f83c76716e3721f4 Reviewed-by: Simon Hausmann --- src/v4/qmljs_runtime.cpp | 4 +- src/v4/qv4argumentsobject.cpp | 2 +- src/v4/qv4array.cpp | 138 +++++++++++------------ src/v4/qv4array.h | 253 +++++++++++++++++++++--------------------- src/v4/qv4arrayobject.cpp | 54 ++++----- src/v4/qv4jsonobject.cpp | 8 +- src/v4/qv4object.cpp | 22 ++-- src/v4/qv4object.h | 2 +- src/v4/qv4objectiterator.cpp | 12 +- src/v4/qv4stringobject.cpp | 12 +- 10 files changed, 253 insertions(+), 254 deletions(-) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 2161d89053..63304355d2 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -596,7 +596,7 @@ Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) Object *o = object.objectValue(); if (idx < UINT_MAX) { - const PropertyDescriptor *p = o->array.nonSparseAt(idx); + const PropertyDescriptor *p = o->array.nonSparseArrayAt(idx); if (p && p->type == PropertyDescriptor::Data) return p->value; @@ -616,7 +616,7 @@ void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value uint idx = index.asArrayIndex(); if (idx < UINT_MAX) { - PropertyDescriptor *p = o->array.nonSparseAtRef(idx); + PropertyDescriptor *p = o->array.nonSparseArrayAtRef(idx); if (p && p->type == PropertyDescriptor::Data && p->isWritable()) { p->value = value; return; diff --git a/src/v4/qv4argumentsobject.cpp b/src/v4/qv4argumentsobject.cpp index f7327506e9..cb8802809d 100644 --- a/src/v4/qv4argumentsobject.cpp +++ b/src/v4/qv4argumentsobject.cpp @@ -82,7 +82,7 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterC bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc) { - PropertyDescriptor *pd = array.at(index); + PropertyDescriptor *pd = array.arrayAt(index); PropertyDescriptor map; bool isMapped = false; if (pd && index < (uint)mappedArguments.size()) diff --git a/src/v4/qv4array.cpp b/src/v4/qv4array.cpp index decef65d94..ffefd1bd16 100644 --- a/src/v4/qv4array.cpp +++ b/src/v4/qv4array.cpp @@ -463,23 +463,23 @@ SparseArrayNode *SparseArray::insert(uint akey) } Array::Array(const Array &other) - : len(other.len) + : arrayLen(other.arrayLen) , arrayObject(0) - , values(other.values) - , sparse(0) + , arrayData(other.arrayData) + , sparseArray(0) { - freeList = other.freeList; - if (other.sparse) - sparse = new SparseArray(*other.sparse); + arrayFreeList = other.arrayFreeList; + if (other.sparseArray) + sparseArray = new SparseArray(*other.sparseArray); } -Value Array::indexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o) +Value Array::arrayIndexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o) { bool protoHasArray = false; Object *p = o; while ((p = p->prototype)) - if (p->array.length()) + if (p->array.arrayLength()) protoHasArray = true; if (protoHasArray) { @@ -490,100 +490,100 @@ Value Array::indexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *c if (exists && __qmljs_strict_equal(value, v)) return Value::fromDouble(i); } - } else if (sparse) { - for (SparseArrayNode *n = sparse->lowerBound(fromIndex); n && n->key() < endIndex; n = n->nextNode()) { + } else if (sparseArray) { + for (SparseArrayNode *n = sparseArray->lowerBound(fromIndex); n && n->key() < endIndex; n = n->nextNode()) { bool exists; - Value value = o->getValueChecked(ctx, descriptor(n->value), &exists); + Value value = o->getValueChecked(ctx, arrayDecriptor(n->value), &exists); if (exists && __qmljs_strict_equal(value, v)) return Value::fromDouble(n->key()); } } else { - if ((int) endIndex > values.size()) - endIndex = values.size(); - PropertyDescriptor *pd = values.data() + offset; + if ((int) endIndex > arrayData.size()) + endIndex = arrayData.size(); + PropertyDescriptor *pd = arrayData.data() + arrayOffset; PropertyDescriptor *end = pd + endIndex; pd += fromIndex; while (pd < end) { bool exists; Value value = o->getValueChecked(ctx, pd, &exists); if (exists && __qmljs_strict_equal(value, v)) - return Value::fromDouble(pd - offset - values.constData()); + return Value::fromDouble(pd - arrayOffset - arrayData.constData()); ++pd; } } return Value::fromInt32(-1); } -void Array::concat(const Array &other) +void Array::arrayConcat(const Array &other) { initSparse(); - int newLen = len + other.length(); - if (other.sparse) + int newLen = arrayLen + other.arrayLength(); + if (other.sparseArray) initSparse(); - if (sparse) { - if (other.sparse) { - for (const SparseArrayNode *it = other.sparse->begin(); it != other.sparse->end(); it = it->nextNode()) - set(len + it->key(), other.descriptor(it->value)); + if (sparseArray) { + if (other.sparseArray) { + for (const SparseArrayNode *it = other.sparseArray->begin(); it != other.sparseArray->end(); it = it->nextNode()) + arraySet(arrayLen + it->key(), other.arrayDecriptor(it->value)); } else { - int oldSize = values.size(); - values.resize(oldSize + other.length()); - memcpy(values.data() + oldSize, other.values.constData() + other.offset, other.length()*sizeof(PropertyDescriptor)); - for (uint i = 0; i < other.length(); ++i) { - SparseArrayNode *n = sparse->insert(len + i); + int oldSize = arrayData.size(); + arrayData.resize(oldSize + other.arrayLength()); + memcpy(arrayData.data() + oldSize, other.arrayData.constData() + other.arrayOffset, other.arrayLength()*sizeof(PropertyDescriptor)); + for (uint i = 0; i < other.arrayLength(); ++i) { + SparseArrayNode *n = sparseArray->insert(arrayLen + i); n->value = oldSize + i; } } } else { - int oldSize = values.size(); - values.resize(oldSize + other.length()); - memcpy(values.data() + oldSize, other.values.constData() + other.offset, other.length()*sizeof(PropertyDescriptor)); + int oldSize = arrayData.size(); + arrayData.resize(oldSize + other.arrayLength()); + memcpy(arrayData.data() + oldSize, other.arrayData.constData() + other.arrayOffset, other.arrayLength()*sizeof(PropertyDescriptor)); } - setLengthUnchecked(newLen); + setArrayLengthUnchecked(newLen); } -void Array::sort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len) +void Array::arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len) { - if (sparse) { + if (sparseArray) { context->throwUnimplemented("Array::sort unimplemented for sparse arrays"); return; - delete sparse; + delete sparseArray; } ArrayElementLessThan lessThan(context, thisObject, comparefn); - if (len > values.size() - offset) - len = values.size() - offset; - PropertyDescriptor *begin = values.begin() + offset; + if (len > arrayData.size() - arrayOffset) + len = arrayData.size() - arrayOffset; + PropertyDescriptor *begin = arrayData.begin() + arrayOffset; std::sort(begin, begin + len, lessThan); } void Array::initSparse() { - if (!sparse) { - sparse = new SparseArray; - for (int i = offset; i < values.size(); ++i) { - SparseArrayNode *n = sparse->insert(i - offset); + if (!sparseArray) { + sparseArray = new SparseArray; + for (int i = arrayOffset; i < arrayData.size(); ++i) { + SparseArrayNode *n = sparseArray->insert(i - arrayOffset); n->value = i; } - if (offset) { - int o = offset; + if (arrayOffset) { + int o = arrayOffset; for (int i = 0; i < o - 1; ++i) { - values[i].type = PropertyDescriptor::Generic; - values[i].value = Value::fromInt32(i + 1); + arrayData[i].type = PropertyDescriptor::Generic; + arrayData[i].value = Value::fromInt32(i + 1); } - values[o - 1].type = PropertyDescriptor::Generic; - values[o - 1].value = Value::fromInt32(values.size()); - freeList = 0; + arrayData[o - 1].type = PropertyDescriptor::Generic; + arrayData[o - 1].value = Value::fromInt32(arrayData.size()); + arrayFreeList = 0; } else { - freeList = values.size(); + arrayFreeList = arrayData.size(); } } } -void Array::setLengthUnchecked(uint l) +void Array::setArrayLengthUnchecked(uint l) { - len = l; + arrayLen = l; if (arrayObject) { // length is always the first property of an array PropertyDescriptor &lengthProperty = arrayObject->memberData[ArrayObject::LengthPropertyIndex]; @@ -591,18 +591,18 @@ void Array::setLengthUnchecked(uint l) } } -bool Array::setLength(uint newLen) { +bool Array::setArrayLength(uint newLen) { const PropertyDescriptor *lengthProperty = arrayObject->memberData.constData() + ArrayObject::LengthPropertyIndex; if (lengthProperty && !lengthProperty->isWritable()) return false; - uint oldLen = length(); + uint oldLen = arrayLength(); bool ok = true; if (newLen < oldLen) { - if (sparse) { - SparseArrayNode *begin = sparse->lowerBound(newLen); - SparseArrayNode *it = sparse->end()->previousNode(); + if (sparseArray) { + SparseArrayNode *begin = sparseArray->lowerBound(newLen); + SparseArrayNode *it = sparseArray->end()->previousNode(); while (1) { - PropertyDescriptor &pd = values[it->value]; + PropertyDescriptor &pd = arrayData[it->value]; if (pd.type != PropertyDescriptor::Generic && !pd.isConfigurable()) { ok = false; newLen = it->key() + 1; @@ -610,40 +610,40 @@ bool Array::setLength(uint newLen) { } pd.type = PropertyDescriptor::Generic; pd.value.tag = Value::_Undefined_Type; - pd.value.int_32 = freeList; - freeList = it->value; + pd.value.int_32 = arrayFreeList; + arrayFreeList = it->value; bool brk = (it == begin); SparseArrayNode *prev = it->previousNode(); - sparse->erase(it); + sparseArray->erase(it); if (brk) break; it = prev; } } else { - PropertyDescriptor *it = values.data() + values.size(); - const PropertyDescriptor *begin = values.constData() + offset + newLen; + PropertyDescriptor *it = arrayData.data() + arrayData.size(); + const PropertyDescriptor *begin = arrayData.constData() + arrayOffset + newLen; while (--it >= begin) { if (it->type != PropertyDescriptor::Generic && !it->isConfigurable()) { ok = false; - newLen = it - values.data() + offset + 1; + newLen = it - arrayData.data() + arrayOffset + 1; break; } } - values.resize(newLen + offset); + arrayData.resize(newLen + arrayOffset); } } else { if (newLen >= 0x100000) initSparse(); } - setLengthUnchecked(newLen); + setArrayLengthUnchecked(newLen); return ok; } -void Array::markObjects() const +void Array::markArrayObjects() const { - uint i = sparse ? 0 : offset; - for (; i < (uint)values.size(); ++i) { - const PropertyDescriptor &pd = values.at(i); + uint i = sparseArray ? 0 : arrayOffset; + for (; i < (uint)arrayData.size(); ++i) { + const PropertyDescriptor &pd = arrayData.at(i); if (pd.isData()) { if (Managed *m = pd.value.asManaged()) m->mark(); diff --git a/src/v4/qv4array.h b/src/v4/qv4array.h index b165ed260a..d0057e43c2 100644 --- a/src/v4/qv4array.h +++ b/src/v4/qv4array.h @@ -366,14 +366,14 @@ class Array { friend struct ArrayPrototype; - uint len; + uint arrayLen; ArrayObject *arrayObject; union { - uint freeList; - uint offset; + uint arrayFreeList; + uint arrayOffset; }; - QVector values; - SparseArray *sparse; + QVector arrayData; + SparseArray *sparseArray; void fillDescriptor(PropertyDescriptor *pd, Value v) { @@ -384,105 +384,105 @@ class Array pd->value = v; } - uint allocValue() { - uint idx = freeList; - if (values.size() <= (int)freeList) - values.resize(++freeList); + uint allocArrayValue() { + uint idx = arrayFreeList; + if (arrayData.size() <= (int)arrayFreeList) + arrayData.resize(++arrayFreeList); else - freeList = values.at(freeList).value.integerValue(); + arrayFreeList = arrayData.at(arrayFreeList).value.integerValue(); return idx; } - uint allocValue(Value v) { - uint idx = allocValue(); - PropertyDescriptor *pd = &values[idx]; + uint allocArrayValue(Value v) { + uint idx = allocArrayValue(); + PropertyDescriptor *pd = &arrayData[idx]; fillDescriptor(pd, v); return idx; } - void freeValue(int idx) { - PropertyDescriptor &pd = values[idx]; + void freeArrayValue(int idx) { + PropertyDescriptor &pd = arrayData[idx]; pd.type = PropertyDescriptor::Generic; pd.value.tag = Value::_Undefined_Type; - pd.value.int_32 = freeList; - freeList = idx; + pd.value.int_32 = arrayFreeList; + arrayFreeList = idx; } - PropertyDescriptor *descriptor(uint index) { - PropertyDescriptor *pd = values.data() + index; - if (!sparse) - pd += offset; + PropertyDescriptor *arrayDecriptor(uint index) { + PropertyDescriptor *pd = arrayData.data() + index; + if (!sparseArray) + pd += arrayOffset; return pd; } - const PropertyDescriptor *descriptor(uint index) const { - const PropertyDescriptor *pd = values.data() + index; - if (!sparse) - pd += offset; + const PropertyDescriptor *arrayDecriptor(uint index) const { + const PropertyDescriptor *pd = arrayData.data() + index; + if (!sparseArray) + pd += arrayOffset; return pd; } - void getHeadRoom() { - assert(!sparse && !offset); - offset = qMax(values.size() >> 2, 16); - QVector newValues(values.size() + offset); - memcpy(newValues.data() + offset, values.constData(), values.size()*sizeof(PropertyDescriptor)); - values = newValues; + void getArrayHeadRoom() { + assert(!sparseArray && !arrayOffset); + arrayOffset = qMax(arrayData.size() >> 2, 16); + QVector newValues(arrayData.size() + arrayOffset); + memcpy(newValues.data() + arrayOffset, arrayData.constData(), arrayData.size()*sizeof(PropertyDescriptor)); + arrayData = newValues; } public: - Array() : len(0), arrayObject(0), offset(0), sparse(0) {} + Array() : arrayLen(0), arrayObject(0), arrayOffset(0), sparseArray(0) {} Array(const Array &other); - ~Array() { delete sparse; } + ~Array() { delete sparseArray; } void initSparse(); - uint length() const { return len; } - bool setLength(uint newLen); + uint arrayLength() const { return arrayLen; } + bool setArrayLength(uint newLen); void setArrayObject(ArrayObject *a) { arrayObject = a; } - void setLengthUnchecked(uint l); + void setArrayLengthUnchecked(uint l); - PropertyDescriptor *insert(uint index) { + PropertyDescriptor *arrayInsert(uint index) { PropertyDescriptor *pd; - if (!sparse && (index < 0x1000 || index < len + (len >> 2))) { - if (index + offset >= (uint)values.size()) { - values.resize(offset + index + 1); - for (uint i = len + 1; i < index; ++i) { - values[i].type = PropertyDescriptor::Generic; - values[i].value.tag = Value::_Undefined_Type; + if (!sparseArray && (index < 0x1000 || index < arrayLen + (arrayLen >> 2))) { + if (index + arrayOffset >= (uint)arrayData.size()) { + arrayData.resize(arrayOffset + index + 1); + for (uint i = arrayLen + 1; i < index; ++i) { + arrayData[i].type = PropertyDescriptor::Generic; + arrayData[i].value.tag = Value::_Undefined_Type; } } - pd = descriptor(index); + pd = arrayDecriptor(index); } else { initSparse(); - SparseArrayNode *n = sparse->insert(index); + SparseArrayNode *n = sparseArray->insert(index); if (n->value == UINT_MAX) - n->value = allocValue(); - pd = descriptor(n->value); + n->value = allocArrayValue(); + pd = arrayDecriptor(n->value); } - if (index >= len) - setLengthUnchecked(index + 1); + if (index >= arrayLen) + setArrayLengthUnchecked(index + 1); return pd; } - void set(uint index, const PropertyDescriptor *pd) { - *insert(index) = *pd; + void arraySet(uint index, const PropertyDescriptor *pd) { + *arrayInsert(index) = *pd; } - void set(uint index, Value value) { - PropertyDescriptor *pd = insert(index); + void arraySet(uint index, Value value) { + PropertyDescriptor *pd = arrayInsert(index); fillDescriptor(pd, value); } - bool deleteIndex(uint index) { - if (index >= len) + bool deleteArrayIndex(uint index) { + if (index >= arrayLen) return true; PropertyDescriptor *pd = 0; - if (!sparse) { - pd = at(index); + if (!sparseArray) { + pd = arrayAt(index); } else { - SparseArrayNode *n = sparse->findNode(index); + SparseArrayNode *n = sparseArray->findNode(index); if (n) - pd = descriptor(n->value); + pd = arrayDecriptor(n->value); } if (!pd || pd->type == PropertyDescriptor::Generic) return true; @@ -490,144 +490,143 @@ public: return false; pd->type = PropertyDescriptor::Generic; pd->value.tag = Value::_Undefined_Type; - if (sparse) { - pd->value.int_32 = freeList; - freeList = pd - values.constData(); + if (sparseArray) { + pd->value.int_32 = arrayFreeList; + arrayFreeList = pd - arrayData.constData(); } return true; } - PropertyDescriptor *at(uint index) { - if (!sparse) { - if (index >= values.size() - offset) + PropertyDescriptor *arrayAt(uint index) { + if (!sparseArray) { + if (index >= arrayData.size() - arrayOffset) return 0; - return values.data() + index + offset; + return arrayData.data() + index + arrayOffset; } else { - SparseArrayNode *n = sparse->findNode(index); + SparseArrayNode *n = sparseArray->findNode(index); if (!n) return 0; - return values.data() + n->value; + return arrayData.data() + n->value; } } - const PropertyDescriptor *nonSparseAt(uint index) const { - if (sparse) + const PropertyDescriptor *nonSparseArrayAt(uint index) const { + if (sparseArray) return 0; - index += offset; - if (index >= (uint)values.size()) + index += arrayOffset; + if (index >= (uint)arrayData.size()) return 0; - return values.constData() + index; + return arrayData.constData() + index; } - PropertyDescriptor *nonSparseAtRef(uint index) { - if (sparse) + PropertyDescriptor *nonSparseArrayAtRef(uint index) { + if (sparseArray) return 0; - index += offset; - if (index >= (uint)values.size()) + index += arrayOffset; + if (index >= (uint)arrayData.size()) return 0; - return values.data() + index; + return arrayData.data() + index; } - const PropertyDescriptor *at(uint index) const { - if (!sparse) { - if (index >= values.size() - offset) + const PropertyDescriptor *arrayAt(uint index) const { + if (!sparseArray) { + if (index >= arrayData.size() - arrayOffset) return 0; - return values.constData() + index + offset; + return arrayData.constData() + index + arrayOffset; } else { - SparseArrayNode *n = sparse->findNode(index); + SparseArrayNode *n = sparseArray->findNode(index); if (!n) return 0; - return values.constData() + n->value; + return arrayData.constData() + n->value; } } - void markObjects() const; + void markArrayObjects() const; void push_front(Value v) { - if (!sparse) { - if (!offset) - getHeadRoom(); + if (!sparseArray) { + if (!arrayOffset) + getArrayHeadRoom(); - --offset; + --arrayOffset; PropertyDescriptor &pd = values[offset]; fillDescriptor(&pd, v); } else { - uint idx = allocValue(v); - sparse->push_front(idx); + uint idx = allocArrayValue(v); + sparseArray->push_front(idx); } - setLengthUnchecked(len + 1); + setArrayLengthUnchecked(arrayLen + 1); } PropertyDescriptor *front() { PropertyDescriptor *pd = 0; - if (!sparse) { - if (len) - pd = values.data() + offset; + if (!sparseArray) { + if (arrayLen) + pd = arrayData.data() + arrayOffset; } else { - SparseArrayNode *n = sparse->findNode(0); + SparseArrayNode *n = sparseArray->findNode(0); if (n) - pd = descriptor(n->value); + pd = arrayDecriptor(n->value); } if (pd && pd->type == PropertyDescriptor::Generic) return 0; return pd; } void pop_front() { - if (!len) + if (!arrayLen) return; - if (!sparse) { - ++offset; + if (!sparseArray) { + ++arrayOffset; } else { - uint idx = sparse->pop_front(); - freeValue(idx); + uint idx = sparseArray->pop_front(); + freeArrayValue(idx); } - setLengthUnchecked(len - 1); + setArrayLengthUnchecked(arrayLen - 1); } void push_back(Value v) { - if (!sparse) { - if (len + offset >= (uint)values.size()) - values.resize(values.size() + 1); - PropertyDescriptor &pd = values[len + offset]; + if (!sparseArray) { + PropertyDescriptor pd; fillDescriptor(&pd, v); + arrayData.append(pd); } else { - uint idx = allocValue(v); - sparse->push_back(idx, len); + uint idx = allocArrayValue(v); + sparseArray->push_back(idx, arrayLen); } - setLengthUnchecked(len + 1); + setArrayLengthUnchecked(arrayLen + 1); } PropertyDescriptor *back() { PropertyDescriptor *pd = 0; - if (!sparse) { - if (len) - pd = values.data() + offset + len; + if (!sparseArray) { + if (arrayLen) + pd = arrayData.data() + arrayOffset + arrayLen; } else { - SparseArrayNode *n = sparse->findNode(len - 1); + SparseArrayNode *n = sparseArray->findNode(arrayLen - 1); if (n) - pd = descriptor(n->value); + pd = arrayDecriptor(n->value); } if (pd && pd->type == PropertyDescriptor::Generic) return 0; return pd; } void pop_back() { - if (!len) + if (!arrayLen) return; - if (!sparse) { - values.resize(values.size() - 1); + if (!sparseArray) { + arrayData.resize(arrayData.size() - 1); } else { - uint idx = sparse->pop_back(len); + uint idx = sparseArray->pop_back(arrayLen); if (idx != UINT_MAX) - freeValue(idx); + freeArrayValue(idx); } - setLengthUnchecked(len - 1); + setArrayLengthUnchecked(arrayLen - 1); } - SparseArrayNode *sparseLowerBound(uint idx) { return sparse ? sparse->lowerBound(idx) : 0; } - SparseArrayNode *sparseBegin() { return sparse ? sparse->begin() : 0; } - SparseArrayNode *sparseEnd() { return sparse ? sparse->end() : 0; } + SparseArrayNode *sparseArrayLowerBound(uint idx) { return sparseArray ? sparseArray->lowerBound(idx) : 0; } + SparseArrayNode *sparseArrayBegin() { return sparseArray ? sparseArray->begin() : 0; } + SparseArrayNode *sparseArrayEnd() { return sparseArray ? sparseArray->end() : 0; } - void concat(const Array &other); - void sort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len); - Value indexOf(Value v, uint fromIndex, uint len, ExecutionContext *ctx, Object *o); + void arrayConcat(const Array &other); + void arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint arrayLen); + Value arrayIndexOf(Value v, uint fromIndex, uint arrayLen, ExecutionContext *ctx, Object *o); }; } diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp index 680a373d86..b24ca3270a 100644 --- a/src/v4/qv4arrayobject.cpp +++ b/src/v4/qv4arrayobject.cpp @@ -62,10 +62,10 @@ Value ArrayCtor::call(ExecutionContext *ctx) return Value::undefinedValue(); } - value.setLengthUnchecked(len); + value.setArrayLengthUnchecked(len); } else { for (unsigned int i = 0; i < ctx->argumentCount; ++i) { - value.set(i, ctx->argument(i)); + value.arraySet(i, ctx->argument(i)); } } @@ -104,7 +104,7 @@ void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) uint ArrayPrototype::getLength(ExecutionContext *ctx, Object *o) { if (o->isArrayObject()) - return o->array.length(); + return o->array.arrayLength(); return o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); } @@ -133,18 +133,18 @@ Value ArrayPrototype::method_concat(ExecutionContext *ctx) result = instance->array; else { QString v = ctx->thisObject.toString(ctx)->toQString(); - result.set(0, Value::fromString(ctx, v)); + result.arraySet(0, Value::fromString(ctx, v)); } for (uint i = 0; i < ctx->argumentCount; ++i) { - quint32 k = result.length(); + quint32 k = result.arrayLength(); Value arg = ctx->argument(i); if (ArrayObject *elt = arg.asArrayObject()) - result.concat(elt->array); + result.arrayConcat(elt->array); else - result.set(k, arg); + result.arraySet(k, arg); } return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); @@ -176,7 +176,7 @@ Value ArrayPrototype::method_join(ExecutionContext *ctx) // ### FIXME if (ArrayObject *a = self.asArrayObject()) { - for (uint i = 0; i < a->array.length(); ++i) { + for (uint i = 0; i < a->array.arrayLength(); ++i) { if (i) R += r4; @@ -222,7 +222,7 @@ Value ArrayPrototype::method_pop(ExecutionContext *ctx) instance->__delete__(ctx, len - 1); if (instance->isArrayObject()) - instance->array.setLengthUnchecked(len - 1); + instance->array.setArrayLengthUnchecked(len - 1); else instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - 1)); return result; @@ -251,10 +251,10 @@ Value ArrayPrototype::method_push(ExecutionContext *ctx) bool protoHasArray = false; Object *p = instance; while ((p = p->prototype)) - if (p->array.length()) + if (p->array.arrayLength()) protoHasArray = true; - if (!protoHasArray && len == instance->array.length()) { + if (!protoHasArray && len == instance->array.arrayLength()) { for (uint i = 0; i < ctx->argumentCount; ++i) { Value v = ctx->argument(i); instance->array.push_back(v); @@ -312,10 +312,10 @@ Value ArrayPrototype::method_shift(ExecutionContext *ctx) bool protoHasArray = false; Object *p = instance; while ((p = p->prototype)) - if (p->array.length()) + if (p->array.arrayLength()) protoHasArray = true; - if (!protoHasArray && len >= instance->array.length()) { + if (!protoHasArray && len >= instance->array.arrayLength()) { instance->array.pop_front(); } else { // do it the slow way @@ -365,7 +365,7 @@ Value ArrayPrototype::method_slice(ExecutionContext *ctx) bool exists; Value v = o->__get__(ctx, i, &exists); if (exists) - result.set(n, v); + result.arraySet(n, v); ++n; } return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); @@ -378,7 +378,7 @@ Value ArrayPrototype::method_sort(ExecutionContext *ctx) uint len = getLength(ctx, instance); Value comparefn = ctx->argument(0); - instance->array.sort(ctx, instance, comparefn, len); + instance->array.arraySort(ctx, instance, comparefn, len); return ctx->thisObject; } @@ -398,8 +398,8 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) uint deleteCount = (uint)qMin(qMax(ctx->argument(1).toInteger(ctx), 0.), (double)(len - start)); - newArray->array.values.resize(deleteCount); - PropertyDescriptor *pd = newArray->array.values.data(); + newArray->array.arrayData.resize(deleteCount); + PropertyDescriptor *pd = newArray->array.arrayData.data(); for (uint i = 0; i < deleteCount; ++i) { pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Enabled; @@ -408,7 +408,7 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) pd->value = instance->__get__(ctx, start + i); ++pd; } - newArray->array.setLengthUnchecked(deleteCount); + newArray->array.setArrayLengthUnchecked(deleteCount); uint itemCount = ctx->argumentCount < 2 ? 0 : ctx->argumentCount - 2; @@ -417,7 +417,7 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) bool exists; Value v = instance->__get__(ctx, k + deleteCount, &exists); if (exists) - instance->array.set(k + itemCount, v); + instance->array.arraySet(k + itemCount, v); else instance->__delete__(ctx, k + itemCount); } @@ -429,7 +429,7 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) bool exists; Value v = instance->__get__(ctx, k + deleteCount - 1, &exists); if (exists) - instance->array.set(k + itemCount - 1, v); + instance->array.arraySet(k + itemCount - 1, v); else instance->__delete__(ctx, k + itemCount - 1); --k; @@ -437,7 +437,7 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) } for (uint i = 0; i < itemCount; ++i) - instance->array.set(start + i, ctx->argument(i + 2)); + instance->array.arraySet(start + i, ctx->argument(i + 2)); ctx->strictMode = true; instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - deleteCount + itemCount)); @@ -453,10 +453,10 @@ Value ArrayPrototype::method_unshift(ExecutionContext *ctx) bool protoHasArray = false; Object *p = instance; while ((p = p->prototype)) - if (p->array.length()) + if (p->array.arrayLength()) protoHasArray = true; - if (!protoHasArray && len >= instance->array.length()) { + if (!protoHasArray && len >= instance->array.arrayLength()) { for (int i = ctx->argumentCount - 1; i >= 0; --i) { Value v = ctx->argument(i); instance->array.push_front(v); @@ -516,7 +516,7 @@ Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) return Value::fromInt32(-1); } - return instance->array.indexOf(searchValue, fromIndex, len, ctx, instance); + return instance->array.arrayIndexOf(searchValue, fromIndex, len, ctx, instance); } Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) @@ -654,7 +654,7 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject(ctx); - a->array.setLengthUnchecked(len); + a->array.setArrayLengthUnchecked(len); for (uint k = 0; k < len; ++k) { bool exists; @@ -667,7 +667,7 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; Value mapped = callback->call(ctx, thisArg, args, 3); - a->array.set(k, mapped); + a->array.arraySet(k, mapped); } return Value::fromObject(a); } @@ -699,7 +699,7 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) args[2] = ctx->thisObject; Value selected = callback->call(ctx, thisArg, args, 3); if (__qmljs_to_boolean(selected, ctx)) { - a->array.set(to, v); + a->array.arraySet(to, v); ++to; } } diff --git a/src/v4/qv4jsonobject.cpp b/src/v4/qv4jsonobject.cpp index 5c0027f538..c5df590460 100644 --- a/src/v4/qv4jsonobject.cpp +++ b/src/v4/qv4jsonobject.cpp @@ -314,7 +314,7 @@ Value Parser::parseArray() Value val; if (!parseValue(&val)) return Value::undefinedValue(); - array.set(index, val); + array.arraySet(index, val); QChar token = nextToken(); if (token == EndArray) break; @@ -329,7 +329,7 @@ Value Parser::parseArray() } } - DEBUG << "size =" << array.length(); + DEBUG << "size =" << array.arrayLength(); END; --nestingLevel; @@ -832,7 +832,7 @@ QString Stringify::JA(ArrayObject *a) indent += gap; QStringList partial; - uint len = a->array.length(); + uint len = a->array.arrayLength(); for (uint i = 0; i < len; ++i) { bool exists; Value v = a->__get__(ctx, i, &exists); @@ -897,7 +897,7 @@ Value JsonObject::method_stringify(ExecutionContext *ctx) if (o) { stringify.replacerFunction = o->asFunctionObject(); if (o->isArrayObject()) { - for (uint i = 0; i < o->array.length(); ++i) { + for (uint i = 0; i < o->array.arrayLength(); ++i) { Value v = o->__get__(ctx, i); if (v.asNumberObject() || v.asStringObject() || v.isNumber()) v = __qmljs_to_string(v, ctx); diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 5903df4c43..e2e49eb259 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -201,7 +201,7 @@ void Object::markObjects() } } } - array.markObjects(); + array.markArrayObjects(); } PropertyDescriptor *Object::insertMember(String *s) @@ -234,7 +234,7 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *na PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, uint index) { - PropertyDescriptor *p = array.at(index); + PropertyDescriptor *p = array.arrayAt(index); if(p && p->type != PropertyDescriptor::Generic) return p; if (isStringObject()) @@ -267,7 +267,7 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, uin { Object *o = this; while (o) { - PropertyDescriptor *p = o->array.at(index); + PropertyDescriptor *p = o->array.arrayAt(index); if(p && p->type != PropertyDescriptor::Generic) return p; if (o->isStringObject()) { @@ -318,7 +318,7 @@ Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) PropertyDescriptor *pd = 0; Object *o = this; while (o) { - PropertyDescriptor *p = o->array.at(index); + PropertyDescriptor *p = o->array.arrayAt(index); if (p && p->type != PropertyDescriptor::Generic) { pd = p; break; @@ -368,7 +368,7 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) uint l = value.asArrayLength(ctx, &ok); if (!ok) ctx->throwRangeError(value); - ok = array.setLength(l); + ok = array.setArrayLength(l); if (!ok) goto reject; } else { @@ -480,7 +480,7 @@ void Object::__put__(ExecutionContext *ctx, uint index, Value value) return; } - array.set(index, value); + array.arraySet(index, value); return; reject: @@ -505,7 +505,7 @@ bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const bool Object::__hasProperty__(const ExecutionContext *ctx, uint index) const { - const PropertyDescriptor *p = array.at(index); + const PropertyDescriptor *p = array.arrayAt(index); if (p && p->type != PropertyDescriptor::Generic) return true; @@ -541,7 +541,7 @@ bool Object::__delete__(ExecutionContext *ctx, String *name) bool Object::__delete__(ExecutionContext *ctx, uint index) { - if (array.deleteIndex(index)) + if (array.deleteArrayIndex(index)) return true; if (ctx->strictMode) __qmljs_throw_type_error(ctx); @@ -572,7 +572,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr uint l = desc->value.asArrayLength(ctx, &ok); if (!ok) ctx->throwRangeError(desc->value); - succeeded = array.setLength(l); + succeeded = array.setArrayLength(l); } if (desc->writable == PropertyDescriptor::Disabled) lp->writable = PropertyDescriptor::Disabled; @@ -609,7 +609,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Prop PropertyDescriptor *current; // 15.4.5.1, 4b - if (isArrayObject() && index >= array.length() && !memberData.at(ArrayObject::LengthPropertyIndex).isWritable()) + if (isArrayObject() && index >= array.arrayLength() && !memberData.at(ArrayObject::LengthPropertyIndex).isWritable()) goto reject; if (isNonStrictArgumentsObject) @@ -622,7 +622,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Prop if (!extensible) goto reject; // clause 4 - PropertyDescriptor *pd = array.insert(index); + PropertyDescriptor *pd = array.arrayInsert(index); *pd = *desc; pd->fullyPopulated(); return true; diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index dd67bee488..d8c0e56ef3 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -189,7 +189,7 @@ struct ArrayObject: Object { }; ArrayObject(ExecutionContext *ctx) { init(ctx); } - ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setLengthUnchecked(array.length()); } + ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setArrayLengthUnchecked(array.arrayLength()); } void init(ExecutionContext *context); }; diff --git a/src/v4/qv4objectiterator.cpp b/src/v4/qv4objectiterator.cpp index 62f3c30f40..ecbeaec162 100644 --- a/src/v4/qv4objectiterator.cpp +++ b/src/v4/qv4objectiterator.cpp @@ -76,20 +76,20 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) return s->__getOwnProperty__(context, *index); } flags &= ~CurrentIsString; - arrayNode = current->array.sparseBegin(); + arrayNode = current->array.sparseArrayBegin(); // iterate until we're past the end of the string while (arrayNode && arrayNode->key() < slen) arrayNode = arrayNode->nextNode(); } if (!arrayIndex) - arrayNode = current->array.sparseBegin(); + arrayNode = current->array.sparseArrayBegin(); // sparse arrays if (arrayNode) { - while (arrayNode != current->array.sparseEnd()) { + while (arrayNode != current->array.sparseArrayEnd()) { int k = arrayNode->key(); - p = current->array.at(k); + p = current->array.arrayAt(k); arrayNode = arrayNode->nextNode(); if (p && (!(flags & EnumberableOnly) || p->isEnumerable())) { arrayIndex = k + 1; @@ -101,8 +101,8 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) arrayIndex = UINT_MAX; } // dense arrays - while (arrayIndex < current->array.length()) { - p = current->array.at(arrayIndex); + while (arrayIndex < current->array.arrayLength()) { + p = current->array.arrayAt(arrayIndex); ++arrayIndex; if (p && p->type != PropertyDescriptor::Generic && (!(flags & EnumberableOnly) || p->isEnumerable())) { *index = arrayIndex - 1; diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp index 788d6cd1a9..a026e4616a 100644 --- a/src/v4/qv4stringobject.cpp +++ b/src/v4/qv4stringobject.cpp @@ -337,7 +337,7 @@ Value StringPrototype::method_match(ExecutionContext *parentCtx, Value thisObjec previousLastIndex = thisIndex; } Value matchStr = result.objectValue()->__get__(parentCtx, (uint)0, (bool *)0); - a->array.set(n, matchStr); + a->array.arraySet(n, matchStr); ++n; } if (!n) @@ -572,18 +572,18 @@ Value StringPrototype::method_split(ExecutionContext *ctx) array->array.push_back(Value::fromString(ctx, text.mid(offset, matchOffsets[0] - offset))); offset = qMax(offset + 1, matchOffsets[1]); - if (array->array.length() >= limit) + if (array->array.arrayLength() >= limit) break; for (int i = 1; i < re->value->captureCount(); ++i) { uint start = matchOffsets[i * 2]; uint end = matchOffsets[i * 2 + 1]; array->array.push_back(Value::fromString(ctx, text.mid(start, end - start))); - if (array->array.length() >= limit) + if (array->array.arrayLength() >= limit) break; } } - if (array->array.length() < limit) + if (array->array.arrayLength() < limit) array->array.push_back(Value::fromString(ctx, text.mid(offset))); } else { QString separator = separatorValue.toString(ctx)->toQString(); @@ -598,10 +598,10 @@ Value StringPrototype::method_split(ExecutionContext *ctx) while ((end = text.indexOf(separator, start)) != -1) { array->array.push_back(Value::fromString(ctx, text.mid(start, end - start))); start = end + separator.size(); - if (array->array.length() >= limit) + if (array->array.arrayLength() >= limit) break; } - if (array->array.length() < limit && start != -1) + if (array->array.arrayLength() < limit && start != -1) array->array.push_back(Value::fromString(ctx, text.mid(start))); } return result; -- cgit v1.2.3 From 90a710fe96849b24eff58dccd4629c57e55e119c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 3 Feb 2013 11:36:55 +0100 Subject: Move the Array code into Object itself This allows simplifications of the code moving forward. Change-Id: If65809fd3646e6dc2da6bc62190d6465b1b1ec12 Reviewed-by: Simon Hausmann --- src/v4/qmljs_engine.cpp | 7 -- src/v4/qmljs_engine.h | 2 +- src/v4/qmljs_runtime.cpp | 4 +- src/v4/qv4argumentsobject.cpp | 2 +- src/v4/qv4array.cpp | 193 ----------------------------- src/v4/qv4array.h | 268 ----------------------------------------- src/v4/qv4arrayobject.cpp | 83 ++++++------- src/v4/qv4jsonobject.cpp | 12 +- src/v4/qv4object.cpp | 219 +++++++++++++++++++++++++++++++-- src/v4/qv4object.h | 273 +++++++++++++++++++++++++++++++++++++++++- src/v4/qv4objectiterator.cpp | 12 +- src/v4/qv4objectproto.cpp | 5 +- src/v4/qv4regexpobject.cpp | 2 +- src/v4/qv4stringobject.cpp | 26 ++-- tests/TestExpectations | 1 - 15 files changed, 549 insertions(+), 560 deletions(-) diff --git a/src/v4/qmljs_engine.cpp b/src/v4/qmljs_engine.cpp index 55121cd11f..8c5dcfc80d 100644 --- a/src/v4/qmljs_engine.cpp +++ b/src/v4/qmljs_engine.cpp @@ -320,13 +320,6 @@ ArrayObject *ExecutionEngine::newArrayObject(ExecutionContext *ctx) return object; } -ArrayObject *ExecutionEngine::newArrayObject(ExecutionContext *ctx, const Array &value) -{ - ArrayObject *object = new (memoryManager) ArrayObject(ctx, value); - object->prototype = arrayPrototype; - return object; -} - Object *ExecutionEngine::newDateObject(const Value &value) { Object *object = new (memoryManager) DateObject(value); diff --git a/src/v4/qmljs_engine.h b/src/v4/qmljs_engine.h index bf3ccb233f..a221c068b8 100644 --- a/src/v4/qmljs_engine.h +++ b/src/v4/qmljs_engine.h @@ -200,7 +200,7 @@ struct Q_V4_EXPORT ExecutionEngine Object *newFunctionObject(ExecutionContext *ctx); ArrayObject *newArrayObject(ExecutionContext *ctx); - ArrayObject *newArrayObject(ExecutionContext *ctx, const Array &value); +// ArrayObject *newArrayObject(ExecutionContext *ctx, const Array &value); Object *newDateObject(const Value &value); diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 63304355d2..d01d76d462 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -596,7 +596,7 @@ Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) Object *o = object.objectValue(); if (idx < UINT_MAX) { - const PropertyDescriptor *p = o->array.nonSparseArrayAt(idx); + const PropertyDescriptor *p = o->nonSparseArrayAt(idx); if (p && p->type == PropertyDescriptor::Data) return p->value; @@ -616,7 +616,7 @@ void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value uint idx = index.asArrayIndex(); if (idx < UINT_MAX) { - PropertyDescriptor *p = o->array.nonSparseArrayAtRef(idx); + PropertyDescriptor *p = o->nonSparseArrayAtRef(idx); if (p && p->type == PropertyDescriptor::Data && p->isWritable()) { p->value = value; return; diff --git a/src/v4/qv4argumentsobject.cpp b/src/v4/qv4argumentsobject.cpp index cb8802809d..8211ef2528 100644 --- a/src/v4/qv4argumentsobject.cpp +++ b/src/v4/qv4argumentsobject.cpp @@ -82,7 +82,7 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterC bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc) { - PropertyDescriptor *pd = array.arrayAt(index); + PropertyDescriptor *pd = arrayAt(index); PropertyDescriptor map; bool isMapped = false; if (pd && index < (uint)mappedArguments.size()) diff --git a/src/v4/qv4array.cpp b/src/v4/qv4array.cpp index ffefd1bd16..2f4205750d 100644 --- a/src/v4/qv4array.cpp +++ b/src/v4/qv4array.cpp @@ -462,199 +462,6 @@ SparseArrayNode *SparseArray::insert(uint akey) return createNode(s, y, left); } -Array::Array(const Array &other) - : arrayLen(other.arrayLen) - , arrayObject(0) - , arrayData(other.arrayData) - , sparseArray(0) -{ - arrayFreeList = other.arrayFreeList; - if (other.sparseArray) - sparseArray = new SparseArray(*other.sparseArray); -} - - -Value Array::arrayIndexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o) -{ - bool protoHasArray = false; - Object *p = o; - while ((p = p->prototype)) - if (p->array.arrayLength()) - protoHasArray = true; - - if (protoHasArray) { - // lets be safe and slow - for (uint i = fromIndex; i < endIndex; ++i) { - bool exists; - Value value = o->__get__(ctx, i, &exists); - if (exists && __qmljs_strict_equal(value, v)) - return Value::fromDouble(i); - } - } else if (sparseArray) { - for (SparseArrayNode *n = sparseArray->lowerBound(fromIndex); n && n->key() < endIndex; n = n->nextNode()) { - bool exists; - Value value = o->getValueChecked(ctx, arrayDecriptor(n->value), &exists); - if (exists && __qmljs_strict_equal(value, v)) - return Value::fromDouble(n->key()); - } - } else { - if ((int) endIndex > arrayData.size()) - endIndex = arrayData.size(); - PropertyDescriptor *pd = arrayData.data() + arrayOffset; - PropertyDescriptor *end = pd + endIndex; - pd += fromIndex; - while (pd < end) { - bool exists; - Value value = o->getValueChecked(ctx, pd, &exists); - if (exists && __qmljs_strict_equal(value, v)) - return Value::fromDouble(pd - arrayOffset - arrayData.constData()); - ++pd; - } - } - return Value::fromInt32(-1); -} - -void Array::arrayConcat(const Array &other) -{ - initSparse(); - int newLen = arrayLen + other.arrayLength(); - if (other.sparseArray) - initSparse(); - if (sparseArray) { - if (other.sparseArray) { - for (const SparseArrayNode *it = other.sparseArray->begin(); it != other.sparseArray->end(); it = it->nextNode()) - arraySet(arrayLen + it->key(), other.arrayDecriptor(it->value)); - } else { - int oldSize = arrayData.size(); - arrayData.resize(oldSize + other.arrayLength()); - memcpy(arrayData.data() + oldSize, other.arrayData.constData() + other.arrayOffset, other.arrayLength()*sizeof(PropertyDescriptor)); - for (uint i = 0; i < other.arrayLength(); ++i) { - SparseArrayNode *n = sparseArray->insert(arrayLen + i); - n->value = oldSize + i; - } - } - } else { - int oldSize = arrayData.size(); - arrayData.resize(oldSize + other.arrayLength()); - memcpy(arrayData.data() + oldSize, other.arrayData.constData() + other.arrayOffset, other.arrayLength()*sizeof(PropertyDescriptor)); - } - setArrayLengthUnchecked(newLen); -} - -void Array::arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len) -{ - if (sparseArray) { - context->throwUnimplemented("Array::sort unimplemented for sparse arrays"); - return; - delete sparseArray; - } - - ArrayElementLessThan lessThan(context, thisObject, comparefn); - if (len > arrayData.size() - arrayOffset) - len = arrayData.size() - arrayOffset; - PropertyDescriptor *begin = arrayData.begin() + arrayOffset; - std::sort(begin, begin + len, lessThan); -} - - -void Array::initSparse() -{ - if (!sparseArray) { - sparseArray = new SparseArray; - for (int i = arrayOffset; i < arrayData.size(); ++i) { - SparseArrayNode *n = sparseArray->insert(i - arrayOffset); - n->value = i; - } - - if (arrayOffset) { - int o = arrayOffset; - for (int i = 0; i < o - 1; ++i) { - arrayData[i].type = PropertyDescriptor::Generic; - arrayData[i].value = Value::fromInt32(i + 1); - } - arrayData[o - 1].type = PropertyDescriptor::Generic; - arrayData[o - 1].value = Value::fromInt32(arrayData.size()); - arrayFreeList = 0; - } else { - arrayFreeList = arrayData.size(); - } - } -} - -void Array::setArrayLengthUnchecked(uint l) -{ - arrayLen = l; - if (arrayObject) { - // length is always the first property of an array - PropertyDescriptor &lengthProperty = arrayObject->memberData[ArrayObject::LengthPropertyIndex]; - lengthProperty.value = Value::fromUInt32(l); - } -} - -bool Array::setArrayLength(uint newLen) { - const PropertyDescriptor *lengthProperty = arrayObject->memberData.constData() + ArrayObject::LengthPropertyIndex; - if (lengthProperty && !lengthProperty->isWritable()) - return false; - uint oldLen = arrayLength(); - bool ok = true; - if (newLen < oldLen) { - if (sparseArray) { - SparseArrayNode *begin = sparseArray->lowerBound(newLen); - SparseArrayNode *it = sparseArray->end()->previousNode(); - while (1) { - PropertyDescriptor &pd = arrayData[it->value]; - if (pd.type != PropertyDescriptor::Generic && !pd.isConfigurable()) { - ok = false; - newLen = it->key() + 1; - break; - } - pd.type = PropertyDescriptor::Generic; - pd.value.tag = Value::_Undefined_Type; - pd.value.int_32 = arrayFreeList; - arrayFreeList = it->value; - bool brk = (it == begin); - SparseArrayNode *prev = it->previousNode(); - sparseArray->erase(it); - if (brk) - break; - it = prev; - } - } else { - PropertyDescriptor *it = arrayData.data() + arrayData.size(); - const PropertyDescriptor *begin = arrayData.constData() + arrayOffset + newLen; - while (--it >= begin) { - if (it->type != PropertyDescriptor::Generic && !it->isConfigurable()) { - ok = false; - newLen = it - arrayData.data() + arrayOffset + 1; - break; - } - } - arrayData.resize(newLen + arrayOffset); - } - } else { - if (newLen >= 0x100000) - initSparse(); - } - setArrayLengthUnchecked(newLen); - return ok; -} - -void Array::markArrayObjects() const -{ - uint i = sparseArray ? 0 : arrayOffset; - for (; i < (uint)arrayData.size(); ++i) { - const PropertyDescriptor &pd = arrayData.at(i); - if (pd.isData()) { - if (Managed *m = pd.value.asManaged()) - m->mark(); - } else if (pd.isAccessor()) { - if (pd.get) - pd.get->mark(); - if (pd.set) - pd.set->mark(); - } - } -} } } diff --git a/src/v4/qv4array.h b/src/v4/qv4array.h index d0057e43c2..ce6a697a77 100644 --- a/src/v4/qv4array.h +++ b/src/v4/qv4array.h @@ -361,274 +361,6 @@ inline SparseArrayNode *SparseArray::upperBound(uint akey) return ub; } - -class Array -{ - friend struct ArrayPrototype; - - uint arrayLen; - ArrayObject *arrayObject; - union { - uint arrayFreeList; - uint arrayOffset; - }; - QVector arrayData; - SparseArray *sparseArray; - - void fillDescriptor(PropertyDescriptor *pd, Value v) - { - pd->type = PropertyDescriptor::Data; - pd->writable = PropertyDescriptor::Enabled; - pd->enumberable = PropertyDescriptor::Enabled; - pd->configurable = PropertyDescriptor::Enabled; - pd->value = v; - } - - uint allocArrayValue() { - uint idx = arrayFreeList; - if (arrayData.size() <= (int)arrayFreeList) - arrayData.resize(++arrayFreeList); - else - arrayFreeList = arrayData.at(arrayFreeList).value.integerValue(); - return idx; - } - - uint allocArrayValue(Value v) { - uint idx = allocArrayValue(); - PropertyDescriptor *pd = &arrayData[idx]; - fillDescriptor(pd, v); - return idx; - } - void freeArrayValue(int idx) { - PropertyDescriptor &pd = arrayData[idx]; - pd.type = PropertyDescriptor::Generic; - pd.value.tag = Value::_Undefined_Type; - pd.value.int_32 = arrayFreeList; - arrayFreeList = idx; - } - - PropertyDescriptor *arrayDecriptor(uint index) { - PropertyDescriptor *pd = arrayData.data() + index; - if (!sparseArray) - pd += arrayOffset; - return pd; - } - const PropertyDescriptor *arrayDecriptor(uint index) const { - const PropertyDescriptor *pd = arrayData.data() + index; - if (!sparseArray) - pd += arrayOffset; - return pd; - } - - void getArrayHeadRoom() { - assert(!sparseArray && !arrayOffset); - arrayOffset = qMax(arrayData.size() >> 2, 16); - QVector newValues(arrayData.size() + arrayOffset); - memcpy(newValues.data() + arrayOffset, arrayData.constData(), arrayData.size()*sizeof(PropertyDescriptor)); - arrayData = newValues; - } - -public: - Array() : arrayLen(0), arrayObject(0), arrayOffset(0), sparseArray(0) {} - Array(const Array &other); - ~Array() { delete sparseArray; } - void initSparse(); - - uint arrayLength() const { return arrayLen; } - bool setArrayLength(uint newLen); - - void setArrayObject(ArrayObject *a) { arrayObject = a; } - - void setArrayLengthUnchecked(uint l); - - PropertyDescriptor *arrayInsert(uint index) { - PropertyDescriptor *pd; - if (!sparseArray && (index < 0x1000 || index < arrayLen + (arrayLen >> 2))) { - if (index + arrayOffset >= (uint)arrayData.size()) { - arrayData.resize(arrayOffset + index + 1); - for (uint i = arrayLen + 1; i < index; ++i) { - arrayData[i].type = PropertyDescriptor::Generic; - arrayData[i].value.tag = Value::_Undefined_Type; - } - } - pd = arrayDecriptor(index); - } else { - initSparse(); - SparseArrayNode *n = sparseArray->insert(index); - if (n->value == UINT_MAX) - n->value = allocArrayValue(); - pd = arrayDecriptor(n->value); - } - if (index >= arrayLen) - setArrayLengthUnchecked(index + 1); - return pd; - } - - void arraySet(uint index, const PropertyDescriptor *pd) { - *arrayInsert(index) = *pd; - } - - void arraySet(uint index, Value value) { - PropertyDescriptor *pd = arrayInsert(index); - fillDescriptor(pd, value); - } - - bool deleteArrayIndex(uint index) { - if (index >= arrayLen) - return true; - PropertyDescriptor *pd = 0; - if (!sparseArray) { - pd = arrayAt(index); - } else { - SparseArrayNode *n = sparseArray->findNode(index); - if (n) - pd = arrayDecriptor(n->value); - } - if (!pd || pd->type == PropertyDescriptor::Generic) - return true; - if (!pd->isConfigurable()) - return false; - pd->type = PropertyDescriptor::Generic; - pd->value.tag = Value::_Undefined_Type; - if (sparseArray) { - pd->value.int_32 = arrayFreeList; - arrayFreeList = pd - arrayData.constData(); - } - return true; - } - - PropertyDescriptor *arrayAt(uint index) { - if (!sparseArray) { - if (index >= arrayData.size() - arrayOffset) - return 0; - return arrayData.data() + index + arrayOffset; - } else { - SparseArrayNode *n = sparseArray->findNode(index); - if (!n) - return 0; - return arrayData.data() + n->value; - } - } - - const PropertyDescriptor *nonSparseArrayAt(uint index) const { - if (sparseArray) - return 0; - index += arrayOffset; - if (index >= (uint)arrayData.size()) - return 0; - return arrayData.constData() + index; - } - - PropertyDescriptor *nonSparseArrayAtRef(uint index) { - if (sparseArray) - return 0; - index += arrayOffset; - if (index >= (uint)arrayData.size()) - return 0; - return arrayData.data() + index; - } - - const PropertyDescriptor *arrayAt(uint index) const { - if (!sparseArray) { - if (index >= arrayData.size() - arrayOffset) - return 0; - return arrayData.constData() + index + arrayOffset; - } else { - SparseArrayNode *n = sparseArray->findNode(index); - if (!n) - return 0; - return arrayData.constData() + n->value; - } - } - - void markArrayObjects() const; - - void push_front(Value v) { - if (!sparseArray) { - if (!arrayOffset) - getArrayHeadRoom(); - - --arrayOffset; - PropertyDescriptor &pd = values[offset]; - fillDescriptor(&pd, v); - } else { - uint idx = allocArrayValue(v); - sparseArray->push_front(idx); - } - setArrayLengthUnchecked(arrayLen + 1); - } - PropertyDescriptor *front() { - PropertyDescriptor *pd = 0; - if (!sparseArray) { - if (arrayLen) - pd = arrayData.data() + arrayOffset; - } else { - SparseArrayNode *n = sparseArray->findNode(0); - if (n) - pd = arrayDecriptor(n->value); - } - if (pd && pd->type == PropertyDescriptor::Generic) - return 0; - return pd; - } - void pop_front() { - if (!arrayLen) - return; - if (!sparseArray) { - ++arrayOffset; - } else { - uint idx = sparseArray->pop_front(); - freeArrayValue(idx); - } - setArrayLengthUnchecked(arrayLen - 1); - } - void push_back(Value v) { - if (!sparseArray) { - PropertyDescriptor pd; - fillDescriptor(&pd, v); - arrayData.append(pd); - } else { - uint idx = allocArrayValue(v); - sparseArray->push_back(idx, arrayLen); - } - setArrayLengthUnchecked(arrayLen + 1); - } - PropertyDescriptor *back() { - PropertyDescriptor *pd = 0; - if (!sparseArray) { - if (arrayLen) - pd = arrayData.data() + arrayOffset + arrayLen; - } else { - SparseArrayNode *n = sparseArray->findNode(arrayLen - 1); - if (n) - pd = arrayDecriptor(n->value); - } - if (pd && pd->type == PropertyDescriptor::Generic) - return 0; - return pd; - } - void pop_back() { - if (!arrayLen) - return; - if (!sparseArray) { - arrayData.resize(arrayData.size() - 1); - } else { - uint idx = sparseArray->pop_back(arrayLen); - if (idx != UINT_MAX) - freeArrayValue(idx); - } - setArrayLengthUnchecked(arrayLen - 1); - } - - SparseArrayNode *sparseArrayLowerBound(uint idx) { return sparseArray ? sparseArray->lowerBound(idx) : 0; } - SparseArrayNode *sparseArrayBegin() { return sparseArray ? sparseArray->begin() : 0; } - SparseArrayNode *sparseArrayEnd() { return sparseArray ? sparseArray->end() : 0; } - - void arrayConcat(const Array &other); - void arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint arrayLen); - Value arrayIndexOf(Value v, uint fromIndex, uint arrayLen, ExecutionContext *ctx, Object *o); -}; - } } diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp index b24ca3270a..3fa18855a1 100644 --- a/src/v4/qv4arrayobject.cpp +++ b/src/v4/qv4arrayobject.cpp @@ -52,22 +52,23 @@ ArrayCtor::ArrayCtor(ExecutionContext *scope) Value ArrayCtor::call(ExecutionContext *ctx) { ArrayObject *a = ctx->engine->newArrayObject(ctx); - Array &value = a->array; + uint len; if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { bool ok; - uint len = ctx->argument(0).asArrayLength(ctx, &ok); + len = ctx->argument(0).asArrayLength(ctx, &ok); if (!ok) { ctx->throwRangeError(ctx->argument(0)); return Value::undefinedValue(); } - value.setArrayLengthUnchecked(len); } else { - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { - value.arraySet(i, ctx->argument(i)); + len = ctx->argumentCount; + for (unsigned int i = 0; i < len; ++i) { + a->arraySet(i, ctx->argument(i)); } } + a->setArrayLengthUnchecked(len); return Value::fromObject(a); } @@ -104,7 +105,7 @@ void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) uint ArrayPrototype::getLength(ExecutionContext *ctx, Object *o) { if (o->isArrayObject()) - return o->array.arrayLength(); + return o->arrayLength(); return o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); } @@ -127,27 +128,27 @@ Value ArrayPrototype::method_toLocaleString(ExecutionContext *ctx) Value ArrayPrototype::method_concat(ExecutionContext *ctx) { - Array result; + ArrayObject *result = ctx->engine->newArrayObject(ctx); - if (ArrayObject *instance = ctx->thisObject.asArrayObject()) - result = instance->array; - else { + if (ArrayObject *instance = ctx->thisObject.asArrayObject()) { + result->copyArrayData(instance); + } else { QString v = ctx->thisObject.toString(ctx)->toQString(); - result.arraySet(0, Value::fromString(ctx, v)); + result->arraySet(0, Value::fromString(ctx, v)); } for (uint i = 0; i < ctx->argumentCount; ++i) { - quint32 k = result.arrayLength(); + quint32 k = result->arrayLength(); Value arg = ctx->argument(i); if (ArrayObject *elt = arg.asArrayObject()) - result.arrayConcat(elt->array); + result->arrayConcat(elt); else - result.arraySet(k, arg); + result->arraySet(k, arg); } - return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); + return Value::fromObject(result); } Value ArrayPrototype::method_join(ExecutionContext *ctx) @@ -176,7 +177,7 @@ Value ArrayPrototype::method_join(ExecutionContext *ctx) // ### FIXME if (ArrayObject *a = self.asArrayObject()) { - for (uint i = 0; i < a->array.arrayLength(); ++i) { + for (uint i = 0; i < a->arrayLength(); ++i) { if (i) R += r4; @@ -222,7 +223,7 @@ Value ArrayPrototype::method_pop(ExecutionContext *ctx) instance->__delete__(ctx, len - 1); if (instance->isArrayObject()) - instance->array.setArrayLengthUnchecked(len - 1); + instance->setArrayLengthUnchecked(len - 1); else instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - 1)); return result; @@ -251,13 +252,13 @@ Value ArrayPrototype::method_push(ExecutionContext *ctx) bool protoHasArray = false; Object *p = instance; while ((p = p->prototype)) - if (p->array.arrayLength()) + if (p->arrayLength()) protoHasArray = true; - if (!protoHasArray && len == instance->array.arrayLength()) { + if (!protoHasArray && len == instance->arrayLength()) { for (uint i = 0; i < ctx->argumentCount; ++i) { Value v = ctx->argument(i); - instance->array.push_back(v); + instance->push_back(v); } } else { for (uint i = 0; i < ctx->argumentCount; ++i) @@ -307,16 +308,16 @@ Value ArrayPrototype::method_shift(ExecutionContext *ctx) return Value::undefinedValue(); } - Value result = instance->getValueChecked(ctx, instance->array.front()); + Value result = instance->getValueChecked(ctx, instance->front()); bool protoHasArray = false; Object *p = instance; while ((p = p->prototype)) - if (p->array.arrayLength()) + if (p->arrayLength()) protoHasArray = true; - if (!protoHasArray && len >= instance->array.arrayLength()) { - instance->array.pop_front(); + if (!protoHasArray && len >= instance->arrayLength()) { + instance->pop_front(); } else { // do it the slow way for (uint k = 1; k < len; ++k) { @@ -339,7 +340,7 @@ Value ArrayPrototype::method_slice(ExecutionContext *ctx) { Object *o = ctx->thisObject.toObject(ctx).objectValue(); - Array result; + ArrayObject *result = ctx->engine->newArrayObject(ctx); uint len = o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); double s = ctx->argument(0).toInteger(ctx); uint start; @@ -365,10 +366,10 @@ Value ArrayPrototype::method_slice(ExecutionContext *ctx) bool exists; Value v = o->__get__(ctx, i, &exists); if (exists) - result.arraySet(n, v); + result->arraySet(n, v); ++n; } - return Value::fromObject(ctx->engine->newArrayObject(ctx, result)); + return Value::fromObject(result); } Value ArrayPrototype::method_sort(ExecutionContext *ctx) @@ -378,7 +379,7 @@ Value ArrayPrototype::method_sort(ExecutionContext *ctx) uint len = getLength(ctx, instance); Value comparefn = ctx->argument(0); - instance->array.arraySort(ctx, instance, comparefn, len); + instance->arraySort(ctx, instance, comparefn, len); return ctx->thisObject; } @@ -398,8 +399,8 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) uint deleteCount = (uint)qMin(qMax(ctx->argument(1).toInteger(ctx), 0.), (double)(len - start)); - newArray->array.arrayData.resize(deleteCount); - PropertyDescriptor *pd = newArray->array.arrayData.data(); + newArray->arrayData.resize(deleteCount); + PropertyDescriptor *pd = newArray->arrayData.data(); for (uint i = 0; i < deleteCount; ++i) { pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Enabled; @@ -408,7 +409,7 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) pd->value = instance->__get__(ctx, start + i); ++pd; } - newArray->array.setArrayLengthUnchecked(deleteCount); + newArray->setArrayLengthUnchecked(deleteCount); uint itemCount = ctx->argumentCount < 2 ? 0 : ctx->argumentCount - 2; @@ -417,7 +418,7 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) bool exists; Value v = instance->__get__(ctx, k + deleteCount, &exists); if (exists) - instance->array.arraySet(k + itemCount, v); + instance->arraySet(k + itemCount, v); else instance->__delete__(ctx, k + itemCount); } @@ -429,7 +430,7 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) bool exists; Value v = instance->__get__(ctx, k + deleteCount - 1, &exists); if (exists) - instance->array.arraySet(k + itemCount - 1, v); + instance->arraySet(k + itemCount - 1, v); else instance->__delete__(ctx, k + itemCount - 1); --k; @@ -437,7 +438,7 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) } for (uint i = 0; i < itemCount; ++i) - instance->array.arraySet(start + i, ctx->argument(i + 2)); + instance->arraySet(start + i, ctx->argument(i + 2)); ctx->strictMode = true; instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - deleteCount + itemCount)); @@ -453,13 +454,13 @@ Value ArrayPrototype::method_unshift(ExecutionContext *ctx) bool protoHasArray = false; Object *p = instance; while ((p = p->prototype)) - if (p->array.arrayLength()) + if (p->arrayLength()) protoHasArray = true; - if (!protoHasArray && len >= instance->array.arrayLength()) { + if (!protoHasArray && len >= instance->arrayLength()) { for (int i = ctx->argumentCount - 1; i >= 0; --i) { Value v = ctx->argument(i); - instance->array.push_front(v); + instance->push_front(v); } } else { for (uint k = len; k > 0; --k) { @@ -516,7 +517,7 @@ Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) return Value::fromInt32(-1); } - return instance->array.arrayIndexOf(searchValue, fromIndex, len, ctx, instance); + return instance->arrayIndexOf(searchValue, fromIndex, len, ctx, instance); } Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) @@ -654,7 +655,7 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject(ctx); - a->array.setArrayLengthUnchecked(len); + a->setArrayLengthUnchecked(len); for (uint k = 0; k < len; ++k) { bool exists; @@ -667,7 +668,7 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; Value mapped = callback->call(ctx, thisArg, args, 3); - a->array.arraySet(k, mapped); + a->arraySet(k, mapped); } return Value::fromObject(a); } @@ -699,7 +700,7 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) args[2] = ctx->thisObject; Value selected = callback->call(ctx, thisArg, args, 3); if (__qmljs_to_boolean(selected, ctx)) { - a->array.arraySet(to, v); + a->arraySet(to, v); ++to; } } diff --git a/src/v4/qv4jsonobject.cpp b/src/v4/qv4jsonobject.cpp index c5df590460..d30c10a3e7 100644 --- a/src/v4/qv4jsonobject.cpp +++ b/src/v4/qv4jsonobject.cpp @@ -295,7 +295,7 @@ bool Parser::parseMember(Object *o) Value Parser::parseArray() { BEGIN << "parseArray"; - Array array; + ArrayObject *array = context->engine->newArrayObject(context); if (++nestingLevel > nestingLimit) { lastError = QJsonParseError::DeepNesting; @@ -314,7 +314,7 @@ Value Parser::parseArray() Value val; if (!parseValue(&val)) return Value::undefinedValue(); - array.arraySet(index, val); + array->arraySet(index, val); QChar token = nextToken(); if (token == EndArray) break; @@ -329,11 +329,11 @@ Value Parser::parseArray() } } - DEBUG << "size =" << array.arrayLength(); + DEBUG << "size =" << array->arrayLength(); END; --nestingLevel; - return Value::fromObject(context->engine->newArrayObject(context, array)); + return Value::fromObject(array); } /* @@ -832,7 +832,7 @@ QString Stringify::JA(ArrayObject *a) indent += gap; QStringList partial; - uint len = a->array.arrayLength(); + uint len = a->arrayLength(); for (uint i = 0; i < len; ++i) { bool exists; Value v = a->__get__(ctx, i, &exists); @@ -897,7 +897,7 @@ Value JsonObject::method_stringify(ExecutionContext *ctx) if (o) { stringify.replacerFunction = o->asFunctionObject(); if (o->isArrayObject()) { - for (uint i = 0; i < o->array.arrayLength(); ++i) { + for (uint i = 0; i < o->arrayLength(); ++i) { Value v = o->__get__(ctx, i); if (v.asNumberObject() || v.asStringObject() || v.isNumber()) v = __qmljs_to_string(v, ctx); diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index e2e49eb259..ff4fffc4df 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -70,6 +70,7 @@ using namespace QQmlJS::VM; // Object::~Object() { + delete sparseArray; } void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &value) @@ -201,7 +202,7 @@ void Object::markObjects() } } } - array.markArrayObjects(); + markArrayObjects(); } PropertyDescriptor *Object::insertMember(String *s) @@ -234,7 +235,7 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *na PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, uint index) { - PropertyDescriptor *p = array.arrayAt(index); + PropertyDescriptor *p = arrayAt(index); if(p && p->type != PropertyDescriptor::Generic) return p; if (isStringObject()) @@ -267,7 +268,7 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, uin { Object *o = this; while (o) { - PropertyDescriptor *p = o->array.arrayAt(index); + PropertyDescriptor *p = o->arrayAt(index); if(p && p->type != PropertyDescriptor::Generic) return p; if (o->isStringObject()) { @@ -318,7 +319,7 @@ Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) PropertyDescriptor *pd = 0; Object *o = this; while (o) { - PropertyDescriptor *p = o->array.arrayAt(index); + PropertyDescriptor *p = o->arrayAt(index); if (p && p->type != PropertyDescriptor::Generic) { pd = p; break; @@ -368,7 +369,7 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) uint l = value.asArrayLength(ctx, &ok); if (!ok) ctx->throwRangeError(value); - ok = array.setArrayLength(l); + ok = setArrayLength(l); if (!ok) goto reject; } else { @@ -480,7 +481,7 @@ void Object::__put__(ExecutionContext *ctx, uint index, Value value) return; } - array.arraySet(index, value); + arraySet(index, value); return; reject: @@ -505,7 +506,7 @@ bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const bool Object::__hasProperty__(const ExecutionContext *ctx, uint index) const { - const PropertyDescriptor *p = array.arrayAt(index); + const PropertyDescriptor *p = arrayAt(index); if (p && p->type != PropertyDescriptor::Generic) return true; @@ -541,7 +542,7 @@ bool Object::__delete__(ExecutionContext *ctx, String *name) bool Object::__delete__(ExecutionContext *ctx, uint index) { - if (array.deleteArrayIndex(index)) + if (deleteArrayIndex(index)) return true; if (ctx->strictMode) __qmljs_throw_type_error(ctx); @@ -572,7 +573,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr uint l = desc->value.asArrayLength(ctx, &ok); if (!ok) ctx->throwRangeError(desc->value); - succeeded = array.setArrayLength(l); + succeeded = setArrayLength(l); } if (desc->writable == PropertyDescriptor::Disabled) lp->writable = PropertyDescriptor::Disabled; @@ -609,7 +610,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Prop PropertyDescriptor *current; // 15.4.5.1, 4b - if (isArrayObject() && index >= array.arrayLength() && !memberData.at(ArrayObject::LengthPropertyIndex).isWritable()) + if (isArrayObject() && index >= arrayLength() && !memberData.at(ArrayObject::LengthPropertyIndex).isWritable()) goto reject; if (isNonStrictArgumentsObject) @@ -622,7 +623,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Prop if (!extensible) goto reject; // clause 4 - PropertyDescriptor *pd = array.arrayInsert(index); + PropertyDescriptor *pd = arrayInsert(index); *pd = *desc; pd->fullyPopulated(); return true; @@ -706,6 +707,201 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, c } +void Object::copyArrayData(Object *other) +{ + arrayLen = other->arrayLen; + arrayData = other->arrayData; + arrayFreeList = other->arrayFreeList; + if (other->sparseArray) + sparseArray = new SparseArray(*other->sparseArray); + if (isArrayObject()) + setArrayLengthUnchecked(arrayLen); +} + + +Value Object::arrayIndexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o) +{ + bool protoHasArray = false; + Object *p = o; + while ((p = p->prototype)) + if (p->arrayLength()) + protoHasArray = true; + + if (protoHasArray) { + // lets be safe and slow + for (uint i = fromIndex; i < endIndex; ++i) { + bool exists; + Value value = o->__get__(ctx, i, &exists); + if (exists && __qmljs_strict_equal(value, v)) + return Value::fromDouble(i); + } + } else if (sparseArray) { + for (SparseArrayNode *n = sparseArray->lowerBound(fromIndex); n && n->key() < endIndex; n = n->nextNode()) { + bool exists; + Value value = o->getValueChecked(ctx, arrayDecriptor(n->value), &exists); + if (exists && __qmljs_strict_equal(value, v)) + return Value::fromDouble(n->key()); + } + } else { + if ((int) endIndex > arrayData.size()) + endIndex = arrayData.size(); + PropertyDescriptor *pd = arrayData.data() + arrayOffset; + PropertyDescriptor *end = pd + endIndex; + pd += fromIndex; + while (pd < end) { + bool exists; + Value value = o->getValueChecked(ctx, pd, &exists); + if (exists && __qmljs_strict_equal(value, v)) + return Value::fromDouble(pd - arrayOffset - arrayData.constData()); + ++pd; + } + } + return Value::fromInt32(-1); +} + +void Object::arrayConcat(const ArrayObject *other) +{ + int newLen = arrayLen + other->arrayLength(); + if (other->sparseArray) + initSparse(); + if (sparseArray) { + if (other->sparseArray) { + for (const SparseArrayNode *it = other->sparseArray->begin(); it != other->sparseArray->end(); it = it->nextNode()) + arraySet(arrayLen + it->key(), other->arrayDecriptor(it->value)); + } else { + int oldSize = arrayData.size(); + arrayData.resize(oldSize + other->arrayLength()); + memcpy(arrayData.data() + oldSize, other->arrayData.constData() + other->arrayOffset, other->arrayLength()*sizeof(PropertyDescriptor)); + for (uint i = 0; i < other->arrayLength(); ++i) { + SparseArrayNode *n = sparseArray->insert(arrayLen + i); + n->value = oldSize + i; + } + } + } else { + int oldSize = arrayData.size(); + arrayData.resize(oldSize + other->arrayLength()); + memcpy(arrayData.data() + oldSize, other->arrayData.constData() + other->arrayOffset, other->arrayLength()*sizeof(PropertyDescriptor)); + } + setArrayLengthUnchecked(newLen); +} + +void Object::arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len) +{ + if (sparseArray) { + context->throwUnimplemented("Object::sort unimplemented for sparse arrays"); + return; + delete sparseArray; + } + + ArrayElementLessThan lessThan(context, thisObject, comparefn); + if (len > arrayData.size() - arrayOffset) + len = arrayData.size() - arrayOffset; + PropertyDescriptor *begin = arrayData.begin() + arrayOffset; + std::sort(begin, begin + len, lessThan); +} + + +void Object::initSparse() +{ + if (!sparseArray) { + sparseArray = new SparseArray; + for (int i = arrayOffset; i < arrayData.size(); ++i) { + SparseArrayNode *n = sparseArray->insert(i - arrayOffset); + n->value = i; + } + + if (arrayOffset) { + int o = arrayOffset; + for (int i = 0; i < o - 1; ++i) { + arrayData[i].type = PropertyDescriptor::Generic; + arrayData[i].value = Value::fromInt32(i + 1); + } + arrayData[o - 1].type = PropertyDescriptor::Generic; + arrayData[o - 1].value = Value::fromInt32(arrayData.size()); + arrayFreeList = 0; + } else { + arrayFreeList = arrayData.size(); + } + } +} + +void Object::setArrayLengthUnchecked(uint l) +{ + arrayLen = l; + if (isArrayObject()) { + // length is always the first property of an array + PropertyDescriptor &lengthProperty = memberData[ArrayObject::LengthPropertyIndex]; + lengthProperty.value = Value::fromUInt32(l); + } +} + +bool Object::setArrayLength(uint newLen) { + assert(isArrayObject()); + const PropertyDescriptor *lengthProperty = memberData.constData() + ArrayObject::LengthPropertyIndex; + if (lengthProperty && !lengthProperty->isWritable()) + return false; + uint oldLen = arrayLength(); + bool ok = true; + if (newLen < oldLen) { + if (sparseArray) { + SparseArrayNode *begin = sparseArray->lowerBound(newLen); + SparseArrayNode *it = sparseArray->end()->previousNode(); + while (1) { + PropertyDescriptor &pd = arrayData[it->value]; + if (pd.type != PropertyDescriptor::Generic && !pd.isConfigurable()) { + ok = false; + newLen = it->key() + 1; + break; + } + pd.type = PropertyDescriptor::Generic; + pd.value.tag = Value::_Undefined_Type; + pd.value.int_32 = arrayFreeList; + arrayFreeList = it->value; + bool brk = (it == begin); + SparseArrayNode *prev = it->previousNode(); + sparseArray->erase(it); + if (brk) + break; + it = prev; + } + } else { + PropertyDescriptor *it = arrayData.data() + arrayData.size(); + const PropertyDescriptor *begin = arrayData.constData() + arrayOffset + newLen; + while (--it >= begin) { + if (it->type != PropertyDescriptor::Generic && !it->isConfigurable()) { + ok = false; + newLen = it - arrayData.data() + arrayOffset + 1; + break; + } + } + arrayData.resize(newLen + arrayOffset); + } + } else { + if (newLen >= 0x100000) + initSparse(); + } + setArrayLengthUnchecked(newLen); + return ok; +} + +void Object::markArrayObjects() const +{ + uint i = sparseArray ? 0 : arrayOffset; + for (; i < (uint)arrayData.size(); ++i) { + const PropertyDescriptor &pd = arrayData.at(i); + if (pd.isData()) { + if (Managed *m = pd.value.asManaged()) + m->mark(); + } else if (pd.isAccessor()) { + if (pd.get) + pd.get->mark(); + if (pd.set) + pd.set->mark(); + } + } +} + + void ArrayObject::init(ExecutionContext *context) { type = Type_ArrayObject; @@ -719,7 +915,6 @@ void ArrayObject::init(ExecutionContext *context) pd->enumberable = PropertyDescriptor::Disabled; pd->configurable = PropertyDescriptor::Disabled; pd->value = Value::fromInt32(0); - array.setArrayObject(this); } diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index d8c0e56ef3..6e69f9f602 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -104,12 +104,18 @@ struct Q_V4_EXPORT Object: Managed { Object *prototype; QScopedPointer members; QVector memberData; - Array array; + + uint arrayLen; + union { + uint arrayFreeList; + uint arrayOffset; + }; + QVector arrayData; + SparseArray *sparseArray; Object() - : prototype(0) { type = Type_Object; } - Object(const Array &a) - : prototype(0), array(a) {} + : prototype(0) + , arrayLen(0), arrayOffset(0), sparseArray(0) { type = Type_Object; } virtual ~Object(); @@ -156,6 +162,259 @@ struct Q_V4_EXPORT Object: Managed { PropertyDescriptor *insertMember(String *s); + // Array handling + + void fillDescriptor(PropertyDescriptor *pd, Value v) + { + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Enabled; + pd->configurable = PropertyDescriptor::Enabled; + pd->value = v; + } + + uint allocArrayValue() { + uint idx = arrayFreeList; + if (arrayData.size() <= (int)arrayFreeList) + arrayData.resize(++arrayFreeList); + else + arrayFreeList = arrayData.at(arrayFreeList).value.integerValue(); + return idx; + } + + uint allocArrayValue(Value v) { + uint idx = allocArrayValue(); + PropertyDescriptor *pd = &arrayData[idx]; + fillDescriptor(pd, v); + return idx; + } + void freeArrayValue(int idx) { + PropertyDescriptor &pd = arrayData[idx]; + pd.type = PropertyDescriptor::Generic; + pd.value.tag = Value::_Undefined_Type; + pd.value.int_32 = arrayFreeList; + arrayFreeList = idx; + } + + PropertyDescriptor *arrayDecriptor(uint index) { + PropertyDescriptor *pd = arrayData.data() + index; + if (!sparseArray) + pd += arrayOffset; + return pd; + } + const PropertyDescriptor *arrayDecriptor(uint index) const { + const PropertyDescriptor *pd = arrayData.data() + index; + if (!sparseArray) + pd += arrayOffset; + return pd; + } + + void getArrayHeadRoom() { + assert(!sparseArray && !arrayOffset); + arrayOffset = qMax(arrayData.size() >> 2, 16); + QVector newValues(arrayData.size() + arrayOffset); + memcpy(newValues.data() + arrayOffset, arrayData.constData(), arrayData.size()*sizeof(PropertyDescriptor)); + arrayData = newValues; + } + +public: + void copyArrayData(Object *other); + void initSparse(); + + uint arrayLength() const { return arrayLen; } + bool setArrayLength(uint newLen); + + void setArrayLengthUnchecked(uint l); + + PropertyDescriptor *arrayInsert(uint index) { + PropertyDescriptor *pd; + if (!sparseArray && (index < 0x1000 || index < arrayLen + (arrayLen >> 2))) { + if (index + arrayOffset >= (uint)arrayData.size()) { + arrayData.resize(arrayOffset + index + 1); + for (uint i = arrayLen + 1; i < index; ++i) { + arrayData[i].type = PropertyDescriptor::Generic; + arrayData[i].value.tag = Value::_Undefined_Type; + } + } + pd = arrayDecriptor(index); + } else { + initSparse(); + SparseArrayNode *n = sparseArray->insert(index); + if (n->value == UINT_MAX) + n->value = allocArrayValue(); + pd = arrayDecriptor(n->value); + } + if (index >= arrayLen) + setArrayLengthUnchecked(index + 1); + return pd; + } + + void arraySet(uint index, const PropertyDescriptor *pd) { + *arrayInsert(index) = *pd; + } + + void arraySet(uint index, Value value) { + PropertyDescriptor *pd = arrayInsert(index); + fillDescriptor(pd, value); + } + + bool deleteArrayIndex(uint index) { + if (index >= arrayLen) + return true; + PropertyDescriptor *pd = 0; + if (!sparseArray) { + pd = arrayAt(index); + } else { + SparseArrayNode *n = sparseArray->findNode(index); + if (n) + pd = arrayDecriptor(n->value); + } + if (!pd || pd->type == PropertyDescriptor::Generic) + return true; + if (!pd->isConfigurable()) + return false; + pd->type = PropertyDescriptor::Generic; + pd->value.tag = Value::_Undefined_Type; + if (sparseArray) { + pd->value.int_32 = arrayFreeList; + arrayFreeList = pd - arrayData.constData(); + } + return true; + } + + PropertyDescriptor *arrayAt(uint index) { + if (!sparseArray) { + if (index >= arrayData.size() - arrayOffset) + return 0; + return arrayData.data() + index + arrayOffset; + } else { + SparseArrayNode *n = sparseArray->findNode(index); + if (!n) + return 0; + return arrayData.data() + n->value; + } + } + + const PropertyDescriptor *nonSparseArrayAt(uint index) const { + if (sparseArray) + return 0; + index += arrayOffset; + if (index >= (uint)arrayData.size()) + return 0; + return arrayData.constData() + index; + } + + PropertyDescriptor *nonSparseArrayAtRef(uint index) { + if (sparseArray) + return 0; + index += arrayOffset; + if (index >= (uint)arrayData.size()) + return 0; + return arrayData.data() + index; + } + + const PropertyDescriptor *arrayAt(uint index) const { + if (!sparseArray) { + if (index >= arrayData.size() - arrayOffset) + return 0; + return arrayData.constData() + index + arrayOffset; + } else { + SparseArrayNode *n = sparseArray->findNode(index); + if (!n) + return 0; + return arrayData.constData() + n->value; + } + } + + void markArrayObjects() const; + + void push_front(Value v) { + if (!sparseArray) { + if (!arrayOffset) + getArrayHeadRoom(); + + PropertyDescriptor pd; + fillDescriptor(&pd, v); + --arrayOffset; + arrayData[arrayOffset] = pd; + } else { + uint idx = allocArrayValue(v); + sparseArray->push_front(idx); + } + setArrayLengthUnchecked(arrayLen + 1); + } + PropertyDescriptor *front() { + PropertyDescriptor *pd = 0; + if (!sparseArray) { + if (arrayLen) + pd = arrayData.data() + arrayOffset; + } else { + SparseArrayNode *n = sparseArray->findNode(0); + if (n) + pd = arrayDecriptor(n->value); + } + if (pd && pd->type == PropertyDescriptor::Generic) + return 0; + return pd; + } + void pop_front() { + if (!arrayLen) + return; + if (!sparseArray) { + ++arrayOffset; + } else { + uint idx = sparseArray->pop_front(); + freeArrayValue(idx); + } + setArrayLengthUnchecked(arrayLen - 1); + } + void push_back(Value v) { + if (!sparseArray) { + PropertyDescriptor pd; + fillDescriptor(&pd, v); + arrayData.append(pd); + } else { + uint idx = allocArrayValue(v); + sparseArray->push_back(idx, arrayLen); + } + setArrayLengthUnchecked(arrayLen + 1); + } + PropertyDescriptor *back() { + PropertyDescriptor *pd = 0; + if (!sparseArray) { + if (arrayLen) + pd = arrayData.data() + arrayOffset + arrayLen; + } else { + SparseArrayNode *n = sparseArray->findNode(arrayLen - 1); + if (n) + pd = arrayDecriptor(n->value); + } + if (pd && pd->type == PropertyDescriptor::Generic) + return 0; + return pd; + } + void pop_back() { + if (!arrayLen) + return; + if (!sparseArray) { + arrayData.resize(arrayData.size() - 1); + } else { + uint idx = sparseArray->pop_back(arrayLen); + if (idx != UINT_MAX) + freeArrayValue(idx); + } + setArrayLengthUnchecked(arrayLen - 1); + } + + SparseArrayNode *sparseArrayLowerBound(uint idx) { return sparseArray ? sparseArray->lowerBound(idx) : 0; } + SparseArrayNode *sparseArrayBegin() { return sparseArray ? sparseArray->begin() : 0; } + SparseArrayNode *sparseArrayEnd() { return sparseArray ? sparseArray->end() : 0; } + + void arrayConcat(const ArrayObject *other); + void arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint arrayLen); + Value arrayIndexOf(Value v, uint fromIndex, uint arrayLen, ExecutionContext *ctx, Object *o); + + protected: virtual void markObjects(); @@ -189,7 +448,11 @@ struct ArrayObject: Object { }; ArrayObject(ExecutionContext *ctx) { init(ctx); } - ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setArrayLengthUnchecked(array.arrayLength()); } + ArrayObject(ExecutionContext *ctx, const QVector &value): Object() { + init(ctx); + arrayData = value; + setArrayLengthUnchecked(value.size()); + } void init(ExecutionContext *context); }; diff --git a/src/v4/qv4objectiterator.cpp b/src/v4/qv4objectiterator.cpp index ecbeaec162..d94d778675 100644 --- a/src/v4/qv4objectiterator.cpp +++ b/src/v4/qv4objectiterator.cpp @@ -76,20 +76,20 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) return s->__getOwnProperty__(context, *index); } flags &= ~CurrentIsString; - arrayNode = current->array.sparseArrayBegin(); + arrayNode = current->sparseArrayBegin(); // iterate until we're past the end of the string while (arrayNode && arrayNode->key() < slen) arrayNode = arrayNode->nextNode(); } if (!arrayIndex) - arrayNode = current->array.sparseArrayBegin(); + arrayNode = current->sparseArrayBegin(); // sparse arrays if (arrayNode) { - while (arrayNode != current->array.sparseArrayEnd()) { + while (arrayNode != current->sparseArrayEnd()) { int k = arrayNode->key(); - p = current->array.arrayAt(k); + p = current->arrayAt(k); arrayNode = arrayNode->nextNode(); if (p && (!(flags & EnumberableOnly) || p->isEnumerable())) { arrayIndex = k + 1; @@ -101,8 +101,8 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) arrayIndex = UINT_MAX; } // dense arrays - while (arrayIndex < current->array.arrayLength()) { - p = current->array.arrayAt(arrayIndex); + while (arrayIndex < current->arrayLength()) { + p = current->arrayAt(arrayIndex); ++arrayIndex; if (p && p->type != PropertyDescriptor::Generic && (!(flags & EnumberableOnly) || p->isEnumerable())) { *index = arrayIndex - 1; diff --git a/src/v4/qv4objectproto.cpp b/src/v4/qv4objectproto.cpp index 65153a8cf1..6b7bebc60f 100644 --- a/src/v4/qv4objectproto.cpp +++ b/src/v4/qv4objectproto.cpp @@ -150,13 +150,12 @@ Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx) ctx->throwTypeError(); ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); - Array &a = array->array; ObjectIterator it(ctx, O, ObjectIterator::NoFlags); while (1) { Value v = it.nextPropertyNameAsString(); if (v.isNull()) break; - a.push_back(v); + array->push_back(v); } return Value::fromObject(array); } @@ -354,7 +353,7 @@ Value ObjectPrototype::method_keys(ExecutionContext *ctx) key = Value::fromDouble(index); key = __qmljs_to_string(key, ctx); } - a->array.push_back(key); + a->push_back(key); } return Value::fromObject(a); diff --git a/src/v4/qv4regexpobject.cpp b/src/v4/qv4regexpobject.cpp index 69fd5bbe58..0240830e71 100644 --- a/src/v4/qv4regexpobject.cpp +++ b/src/v4/qv4regexpobject.cpp @@ -195,7 +195,7 @@ Value RegExpPrototype::method_exec(ExecutionContext *ctx) Value entry = Value::undefinedValue(); if (start != -1 && end != -1) entry = Value::fromString(ctx, s.mid(start, end - start)); - array->array.push_back(entry); + array->push_back(entry); } array->__put__(ctx, QLatin1String("index"), Value::fromInt32(result)); diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp index a026e4616a..87506ecf36 100644 --- a/src/v4/qv4stringobject.cpp +++ b/src/v4/qv4stringobject.cpp @@ -337,7 +337,7 @@ Value StringPrototype::method_match(ExecutionContext *parentCtx, Value thisObjec previousLastIndex = thisIndex; } Value matchStr = result.objectValue()->__get__(parentCtx, (uint)0, (bool *)0); - a->array.arraySet(n, matchStr); + a->arraySet(n, matchStr); ++n; } if (!n) @@ -543,7 +543,7 @@ Value StringPrototype::method_split(ExecutionContext *ctx) if (separatorValue.isUndefined()) { if (limitValue.isUndefined()) { - array->array.push_back(Value::fromString(ctx, text)); + array->push_back(Value::fromString(ctx, text)); return result; } return Value::fromString(ctx, text.left(limitValue.toInteger(ctx))); @@ -569,40 +569,40 @@ Value StringPrototype::method_split(ExecutionContext *ctx) if (result == JSC::Yarr::offsetNoMatch) break; - array->array.push_back(Value::fromString(ctx, text.mid(offset, matchOffsets[0] - offset))); + array->push_back(Value::fromString(ctx, text.mid(offset, matchOffsets[0] - offset))); offset = qMax(offset + 1, matchOffsets[1]); - if (array->array.arrayLength() >= limit) + if (array->arrayLength() >= limit) break; for (int i = 1; i < re->value->captureCount(); ++i) { uint start = matchOffsets[i * 2]; uint end = matchOffsets[i * 2 + 1]; - array->array.push_back(Value::fromString(ctx, text.mid(start, end - start))); - if (array->array.arrayLength() >= limit) + array->push_back(Value::fromString(ctx, text.mid(start, end - start))); + if (array->arrayLength() >= limit) break; } } - if (array->array.arrayLength() < limit) - array->array.push_back(Value::fromString(ctx, text.mid(offset))); + if (array->arrayLength() < limit) + array->push_back(Value::fromString(ctx, text.mid(offset))); } else { QString separator = separatorValue.toString(ctx)->toQString(); if (separator.isEmpty()) { for (uint i = 0; i < qMin(limit, uint(text.length())); ++i) - array->array.push_back(Value::fromString(ctx, text.mid(i, 1))); + array->push_back(Value::fromString(ctx, text.mid(i, 1))); return result; } int start = 0; int end; while ((end = text.indexOf(separator, start)) != -1) { - array->array.push_back(Value::fromString(ctx, text.mid(start, end - start))); + array->push_back(Value::fromString(ctx, text.mid(start, end - start))); start = end + separator.size(); - if (array->array.arrayLength() >= limit) + if (array->arrayLength() >= limit) break; } - if (array->array.arrayLength() < limit && start != -1) - array->array.push_back(Value::fromString(ctx, text.mid(start))); + if (array->arrayLength() < limit && start != -1) + array->push_back(Value::fromString(ctx, text.mid(start))); } return result; } diff --git a/tests/TestExpectations b/tests/TestExpectations index 9b1165cbf3..13383782bb 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -30,7 +30,6 @@ S15.12.2_A1 failing S15.4.4.3_A1_T1 failing S15.4.4.3_A3_T1 failing 15.4.4.4-5-c-i-1 failing -S15.4.4.4_A1_T2 failing S15.4.4.4_A2_T1 failing S15.4.4.4_A2_T2 failing S15.4.4.4_A3_T1 failing -- cgit v1.2.3 From b18b6ecbfd55903d57bb8853b2ad614c083647ec Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 3 Feb 2013 11:43:38 +0100 Subject: Rename qv4array.* to qv4sparsearray.* Change-Id: I2c5a683145a4a8e4b243a7f2fe4274dfe2b5abe4 Reviewed-by: Simon Hausmann --- src/v4/qmljs_engine.h | 2 - src/v4/qv4array.cpp | 467 --------------------------------------------- src/v4/qv4array.h | 369 ----------------------------------- src/v4/qv4arrayobject.cpp | 2 +- src/v4/qv4functionobject.h | 1 - src/v4/qv4object.h | 2 +- src/v4/qv4propertytable.h | 1 - src/v4/qv4regexpobject.h | 1 - src/v4/qv4sparsearray.cpp | 467 +++++++++++++++++++++++++++++++++++++++++++++ src/v4/qv4sparsearray.h | 369 +++++++++++++++++++++++++++++++++++ src/v4/v4.pro | 4 +- 11 files changed, 840 insertions(+), 845 deletions(-) delete mode 100644 src/v4/qv4array.cpp delete mode 100644 src/v4/qv4array.h create mode 100644 src/v4/qv4sparsearray.cpp create mode 100644 src/v4/qv4sparsearray.h diff --git a/src/v4/qmljs_engine.h b/src/v4/qmljs_engine.h index a221c068b8..e581beb46e 100644 --- a/src/v4/qmljs_engine.h +++ b/src/v4/qmljs_engine.h @@ -61,7 +61,6 @@ class Debugger; namespace VM { struct Value; -class Array; struct Function; struct Object; struct BooleanObject; @@ -200,7 +199,6 @@ struct Q_V4_EXPORT ExecutionEngine Object *newFunctionObject(ExecutionContext *ctx); ArrayObject *newArrayObject(ExecutionContext *ctx); -// ArrayObject *newArrayObject(ExecutionContext *ctx, const Array &value); Object *newDateObject(const Value &value); diff --git a/src/v4/qv4array.cpp b/src/v4/qv4array.cpp deleted file mode 100644 index 2f4205750d..0000000000 --- a/src/v4/qv4array.cpp +++ /dev/null @@ -1,467 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4array.h" -#include "qmljs_runtime.h" -#include "qv4object.h" -#include "qv4functionobject.h" -#include - -#ifdef QT_QMAP_DEBUG -# include -# include -#endif - -namespace QQmlJS { -namespace VM { - -bool ArrayElementLessThan::operator()(const PropertyDescriptor &p1, const PropertyDescriptor &p2) const -{ - if (p1.type == PropertyDescriptor::Generic) - return false; - if (p2.type == PropertyDescriptor::Generic) - return true; - Value v1 = thisObject->getValue(m_context, &p1); - Value v2 = thisObject->getValue(m_context, &p2); - - if (v1.isUndefined()) - return false; - if (v2.isUndefined()) - return true; - if (!m_comparefn.isUndefined()) { - Value args[] = { v1, v2 }; - Value result = __qmljs_call_value(m_context, Value::undefinedValue(), m_comparefn, args, 2); - return result.toNumber(m_context) <= 0; - } - return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); -} - - -const SparseArrayNode *SparseArrayNode::nextNode() const -{ - const SparseArrayNode *n = this; - if (n->right) { - n = n->right; - while (n->left) - n = n->left; - } else { - const SparseArrayNode *y = n->parent(); - while (y && n == y->right) { - n = y; - y = n->parent(); - } - n = y; - } - return n; -} - -const SparseArrayNode *SparseArrayNode::previousNode() const -{ - const SparseArrayNode *n = this; - if (n->left) { - n = n->left; - while (n->right) - n = n->right; - } else { - const SparseArrayNode *y = n->parent(); - while (y && n == y->left) { - n = y; - y = n->parent(); - } - n = y; - } - return n; -} - -SparseArrayNode *SparseArrayNode::copy(SparseArray *d) const -{ - SparseArrayNode *n = d->createNode(size_left, 0, false); - n->value = value; - n->setColor(color()); - if (left) { - n->left = left->copy(d); - n->left->setParent(n); - } else { - n->left = 0; - } - if (right) { - n->right = right->copy(d); - n->right->setParent(n); - } else { - n->right = 0; - } - return n; -} - -/* - x y - \ / \ - y --> x b - / \ \ - a b a -*/ -void SparseArray::rotateLeft(SparseArrayNode *x) -{ - SparseArrayNode *&root = header.left; - SparseArrayNode *y = x->right; - x->right = y->left; - if (y->left != 0) - y->left->setParent(x); - y->setParent(x->parent()); - if (x == root) - root = y; - else if (x == x->parent()->left) - x->parent()->left = y; - else - x->parent()->right = y; - y->left = x; - x->setParent(y); - y->size_left += x->size_left; -} - - -/* - x y - / / \ - y --> a x - / \ / - a b b -*/ -void SparseArray::rotateRight(SparseArrayNode *x) -{ - SparseArrayNode *&root = header.left; - SparseArrayNode *y = x->left; - x->left = y->right; - if (y->right != 0) - y->right->setParent(x); - y->setParent(x->parent()); - if (x == root) - root = y; - else if (x == x->parent()->right) - x->parent()->right = y; - else - x->parent()->left = y; - y->right = x; - x->setParent(y); - x->size_left -= y->size_left; -} - - -void SparseArray::rebalance(SparseArrayNode *x) -{ - SparseArrayNode *&root = header.left; - x->setColor(SparseArrayNode::Red); - while (x != root && x->parent()->color() == SparseArrayNode::Red) { - if (x->parent() == x->parent()->parent()->left) { - SparseArrayNode *y = x->parent()->parent()->right; - if (y && y->color() == SparseArrayNode::Red) { - x->parent()->setColor(SparseArrayNode::Black); - y->setColor(SparseArrayNode::Black); - x->parent()->parent()->setColor(SparseArrayNode::Red); - x = x->parent()->parent(); - } else { - if (x == x->parent()->right) { - x = x->parent(); - rotateLeft(x); - } - x->parent()->setColor(SparseArrayNode::Black); - x->parent()->parent()->setColor(SparseArrayNode::Red); - rotateRight (x->parent()->parent()); - } - } else { - SparseArrayNode *y = x->parent()->parent()->left; - if (y && y->color() == SparseArrayNode::Red) { - x->parent()->setColor(SparseArrayNode::Black); - y->setColor(SparseArrayNode::Black); - x->parent()->parent()->setColor(SparseArrayNode::Red); - x = x->parent()->parent(); - } else { - if (x == x->parent()->left) { - x = x->parent(); - rotateRight(x); - } - x->parent()->setColor(SparseArrayNode::Black); - x->parent()->parent()->setColor(SparseArrayNode::Red); - rotateLeft(x->parent()->parent()); - } - } - } - root->setColor(SparseArrayNode::Black); -} - -void SparseArray::deleteNode(SparseArrayNode *z) -{ - SparseArrayNode *&root = header.left; - SparseArrayNode *y = z; - SparseArrayNode *x; - SparseArrayNode *x_parent; - if (y->left == 0) { - x = y->right; - if (y == mostLeftNode) { - if (x) - mostLeftNode = x; // It cannot have (left) children due the red black invariant. - else - mostLeftNode = y->parent(); - } - } else { - if (y->right == 0) { - x = y->left; - } else { - y = y->right; - while (y->left != 0) - y = y->left; - x = y->right; - } - } - if (y != z) { - z->left->setParent(y); - y->left = z->left; - if (y != z->right) { - x_parent = y->parent(); - if (x) - x->setParent(y->parent()); - y->parent()->left = x; - y->right = z->right; - z->right->setParent(y); - } else { - x_parent = y; - } - if (root == z) - root = y; - else if (z->parent()->left == z) - z->parent()->left = y; - else - z->parent()->right = y; - y->setParent(z->parent()); - // Swap the colors - SparseArrayNode::Color c = y->color(); - y->setColor(z->color()); - z->setColor(c); - y = z; - } else { - x_parent = y->parent(); - if (x) - x->setParent(y->parent()); - if (root == z) - root = x; - else if (z->parent()->left == z) - z->parent()->left = x; - else - z->parent()->right = x; - } - if (y->color() != SparseArrayNode::Red) { - while (x != root && (x == 0 || x->color() == SparseArrayNode::Black)) { - if (x == x_parent->left) { - SparseArrayNode *w = x_parent->right; - if (w->color() == SparseArrayNode::Red) { - w->setColor(SparseArrayNode::Black); - x_parent->setColor(SparseArrayNode::Red); - rotateLeft(x_parent); - w = x_parent->right; - } - if ((w->left == 0 || w->left->color() == SparseArrayNode::Black) && - (w->right == 0 || w->right->color() == SparseArrayNode::Black)) { - w->setColor(SparseArrayNode::Red); - x = x_parent; - x_parent = x_parent->parent(); - } else { - if (w->right == 0 || w->right->color() == SparseArrayNode::Black) { - if (w->left) - w->left->setColor(SparseArrayNode::Black); - w->setColor(SparseArrayNode::Red); - rotateRight(w); - w = x_parent->right; - } - w->setColor(x_parent->color()); - x_parent->setColor(SparseArrayNode::Black); - if (w->right) - w->right->setColor(SparseArrayNode::Black); - rotateLeft(x_parent); - break; - } - } else { - SparseArrayNode *w = x_parent->left; - if (w->color() == SparseArrayNode::Red) { - w->setColor(SparseArrayNode::Black); - x_parent->setColor(SparseArrayNode::Red); - rotateRight(x_parent); - w = x_parent->left; - } - if ((w->right == 0 || w->right->color() == SparseArrayNode::Black) && - (w->left == 0 || w->left->color() == SparseArrayNode::Black)) { - w->setColor(SparseArrayNode::Red); - x = x_parent; - x_parent = x_parent->parent(); - } else { - if (w->left == 0 || w->left->color() == SparseArrayNode::Black) { - if (w->right) - w->right->setColor(SparseArrayNode::Black); - w->setColor(SparseArrayNode::Red); - rotateLeft(w); - w = x_parent->left; - } - w->setColor(x_parent->color()); - x_parent->setColor(SparseArrayNode::Black); - if (w->left) - w->left->setColor(SparseArrayNode::Black); - rotateRight(x_parent); - break; - } - } - } - if (x) - x->setColor(SparseArrayNode::Black); - } - free(y); - --numEntries; -} - -void SparseArray::recalcMostLeftNode() -{ - mostLeftNode = &header; - while (mostLeftNode->left) - mostLeftNode = mostLeftNode->left; -} - -static inline int qMapAlignmentThreshold() -{ - // malloc on 32-bit platforms should return pointers that are 8-byte - // aligned or more while on 64-bit platforms they should be 16-byte aligned - // or more - return 2 * sizeof(void*); -} - -static inline void *qMapAllocate(int alloc, int alignment) -{ - return alignment > qMapAlignmentThreshold() - ? qMallocAligned(alloc, alignment) - : ::malloc(alloc); -} - -static inline void qMapDeallocate(SparseArrayNode *node, int alignment) -{ - if (alignment > qMapAlignmentThreshold()) - qFreeAligned(node); - else - ::free(node); -} - -SparseArrayNode *SparseArray::createNode(uint sl, SparseArrayNode *parent, bool left) -{ - SparseArrayNode *node = static_cast(qMapAllocate(sizeof(SparseArrayNode), Q_ALIGNOF(SparseArrayNode))); - Q_CHECK_PTR(node); - - node->p = (quintptr)parent; - node->left = 0; - node->right = 0; - node->size_left = sl; - node->value = UINT_MAX; - ++numEntries; - - if (parent) { - if (left) { - parent->left = node; - if (parent == mostLeftNode) - mostLeftNode = node; - } else { - parent->right = node; - } - node->setParent(parent); - rebalance(node); - } - return node; -} - -void SparseArray::freeTree(SparseArrayNode *root, int alignment) -{ - if (root->left) - freeTree(root->left, alignment); - if (root->right) - freeTree(root->right, alignment); - qMapDeallocate(root, alignment); -} - -SparseArray::SparseArray() - : numEntries(0) -{ - header.p = 0; - header.left = 0; - header.right = 0; - mostLeftNode = &header; -} - -SparseArray::SparseArray(const SparseArray &other) -{ - header.p = 0; - header.right = 0; - if (other.header.left) { - header.left = other.header.left->copy(this); - header.left->setParent(&header); - recalcMostLeftNode(); - } -} - -SparseArrayNode *SparseArray::insert(uint akey) -{ - SparseArrayNode *n = root(); - SparseArrayNode *y = end(); - bool left = true; - uint s = akey; - while (n) { - y = n; - if (s == n->size_left) { - return n; - } else if (s < n->size_left) { - left = true; - n = n->left; - } else { - left = false; - s -= n->size_left; - n = n->right; - } - } - - return createNode(s, y, left); -} - - -} -} diff --git a/src/v4/qv4array.h b/src/v4/qv4array.h deleted file mode 100644 index ce6a697a77..0000000000 --- a/src/v4/qv4array.h +++ /dev/null @@ -1,369 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4ARRAY_H -#define QV4ARRAY_H - -#include "qv4global.h" -#include -#include -#include -#include - -#ifdef Q_MAP_DEBUG -#include -#endif - -#include - -QT_BEGIN_NAMESPACE - -namespace QQmlJS { -namespace VM { - -struct SparseArray; - -class ArrayElementLessThan -{ -public: - inline ArrayElementLessThan(ExecutionContext *context, Object *thisObject, const Value &comparefn) - : m_context(context), thisObject(thisObject), m_comparefn(comparefn) {} - - bool operator()(const PropertyDescriptor &v1, const PropertyDescriptor &v2) const; - -private: - ExecutionContext *m_context; - Object *thisObject; - Value m_comparefn; -}; - - -struct SparseArrayNode -{ - quintptr p; - SparseArrayNode *left; - SparseArrayNode *right; - uint size_left; - uint value; - - enum Color { Red = 0, Black = 1 }; - enum { Mask = 3 }; // reserve the second bit as well - - const SparseArrayNode *nextNode() const; - SparseArrayNode *nextNode() { return const_cast(const_cast(this)->nextNode()); } - const SparseArrayNode *previousNode() const; - SparseArrayNode *previousNode() { return const_cast(const_cast(this)->previousNode()); } - - Color color() const { return Color(p & 1); } - void setColor(Color c) { if (c == Black) p |= Black; else p &= ~Black; } - SparseArrayNode *parent() const { return reinterpret_cast(p & ~Mask); } - void setParent(SparseArrayNode *pp) { p = (p & Mask) | quintptr(pp); } - - uint key() const { - uint k = size_left; - const SparseArrayNode *n = this; - while (SparseArrayNode *p = n->parent()) { - if (p && p->right == n) - k += p->size_left; - n = p; - } - return k; - } - - SparseArrayNode *copy(SparseArray *d) const; - - SparseArrayNode *lowerBound(uint key); - SparseArrayNode *upperBound(uint key); -}; - - -inline SparseArrayNode *SparseArrayNode::lowerBound(uint akey) -{ - SparseArrayNode *n = this; - SparseArrayNode *last = 0; - while (n) { - if (akey <= n->size_left) { - last = n; - n = n->left; - } else { - akey -= n->size_left; - n = n->right; - } - } - return last; -} - - -inline SparseArrayNode *SparseArrayNode::upperBound(uint akey) -{ - SparseArrayNode *n = this; - SparseArrayNode *last = 0; - while (n) { - if (akey < n->size_left) { - last = n; - n = n->left; - } else { - akey -= n->size_left; - n = n->right; - } - } - return last; -} - - - -struct Q_V4_EXPORT SparseArray -{ - SparseArray(); - ~SparseArray() { - if (root()) - freeTree(header.left, Q_ALIGNOF(SparseArrayNode)); - } - - SparseArray(const SparseArray &other); -private: - SparseArray &operator=(const SparseArray &other); - - int numEntries; - SparseArrayNode header; - SparseArrayNode *mostLeftNode; - - void rotateLeft(SparseArrayNode *x); - void rotateRight(SparseArrayNode *x); - void rebalance(SparseArrayNode *x); - void recalcMostLeftNode(); - - SparseArrayNode *root() const { return header.left; } - - void deleteNode(SparseArrayNode *z); - - -public: - SparseArrayNode *createNode(uint sl, SparseArrayNode *parent, bool left); - void freeTree(SparseArrayNode *root, int alignment); - - SparseArrayNode *findNode(uint akey) const; - - uint pop_front(); - void push_front(uint at); - uint pop_back(uint len); - void push_back(uint at, uint len); - - QList keys() const; - - const SparseArrayNode *end() const { return &header; } - SparseArrayNode *end() { return &header; } - const SparseArrayNode *begin() const { if (root()) return mostLeftNode; return end(); } - SparseArrayNode *begin() { if (root()) return mostLeftNode; return end(); } - - SparseArrayNode *erase(SparseArrayNode *n); - - SparseArrayNode *lowerBound(uint key); - const SparseArrayNode *lowerBound(uint key) const; - SparseArrayNode *upperBound(uint key); - const SparseArrayNode *upperBound(uint key) const; - SparseArrayNode *insert(uint akey); - - // STL compatibility - typedef uint key_type; - typedef int mapped_type; - typedef qptrdiff difference_type; - typedef int size_type; - -#ifdef Q_MAP_DEBUG - void dump() const; -#endif -}; - -inline SparseArrayNode *SparseArray::findNode(uint akey) const -{ - SparseArrayNode *n = root(); - - while (n) { - if (akey == n->size_left) { - return n; - } else if (akey < n->size_left) { - n = n->left; - } else { - akey -= n->size_left; - n = n->right; - } - } - - return 0; -} - -inline uint SparseArray::pop_front() -{ - uint idx = UINT_MAX ; - - SparseArrayNode *n = findNode(0); - if (n) { - idx = n->value; - deleteNode(n); - // adjust all size_left indices on the path to leftmost item by 1 - SparseArrayNode *n = root(); - while (n) { - n->size_left -= 1; - n = n->left; - } - } - return idx; -} - -inline void SparseArray::push_front(uint value) -{ - // adjust all size_left indices on the path to leftmost item by 1 - SparseArrayNode *n = root(); - while (n) { - n->size_left += 1; - n = n->left; - } - n = insert(0); - n->value = value; -} - -inline uint SparseArray::pop_back(uint len) -{ - uint idx = UINT_MAX; - if (!len) - return idx; - - SparseArrayNode *n = findNode(len - 1); - if (n) { - idx = n->value; - deleteNode(n); - } - return idx; -} - -inline void SparseArray::push_back(uint index, uint len) -{ - SparseArrayNode *n = insert(len); - n->value = index; -} - -#ifdef Q_MAP_DEBUG - -void SparseArray::dump() const -{ - const_iterator it = begin(); - qDebug() << "map dump:"; - while (it != end()) { - const SparseArrayNode *n = it.i; - int depth = 0; - while (n && n != root()) { - ++depth; - n = n->parent(); - } - QByteArray space(4*depth, ' '); - qDebug() << space << (it.i->color() == SparseArrayNode::Red ? "Red " : "Black") << it.i << it.i->left << it.i->right - << it.key() << it.value(); - ++it; - } - qDebug() << "---------"; -} -#endif - - -inline SparseArrayNode *SparseArray::erase(SparseArrayNode *n) -{ - if (n == end()) - return n; - - SparseArrayNode *next = n->nextNode(); - deleteNode(n); - return next; -} - -inline QList SparseArray::keys() const -{ - QList res; - res.reserve(numEntries); - SparseArrayNode *n = mostLeftNode; - while (n != end()) { - res.append(n->key()); - n = n->nextNode(); - } - return res; -} - -inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const -{ - const SparseArrayNode *lb = root()->lowerBound(akey); - if (!lb) - lb = end(); - return lb; -} - - -inline SparseArrayNode *SparseArray::lowerBound(uint akey) -{ - SparseArrayNode *lb = root()->lowerBound(akey); - if (!lb) - lb = end(); - return lb; -} - - -inline const SparseArrayNode *SparseArray::upperBound(uint akey) const -{ - const SparseArrayNode *ub = root()->upperBound(akey); - if (!ub) - ub = end(); - return ub; -} - - -inline SparseArrayNode *SparseArray::upperBound(uint akey) -{ - SparseArrayNode *ub = root()->upperBound(akey); - if (!ub) - ub = end(); - return ub; -} - -} -} - -QT_END_NAMESPACE - -#endif // QMAP_H diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp index 3fa18855a1..8f1fc215df 100644 --- a/src/v4/qv4arrayobject.cpp +++ b/src/v4/qv4arrayobject.cpp @@ -40,7 +40,7 @@ ****************************************************************************/ #include "qv4arrayobject.h" -#include "qv4array.h" +#include "qv4sparsearray.h" using namespace QQmlJS::VM; diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index 53e06f320a..900cb8f6e8 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -46,7 +46,6 @@ #include "qmljs_engine.h" #include "qmljs_environment.h" #include "qv4object.h" -#include "qv4array.h" #include "qv4string.h" #include "qv4codegen_p.h" #include "qv4isel_p.h" diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index 6e69f9f602..0b781095ee 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -45,7 +45,7 @@ #include "qmljs_runtime.h" #include "qmljs_engine.h" #include "qmljs_environment.h" -#include "qv4array.h" +#include "qv4sparsearray.h" #include "qv4string.h" #include "qv4codegen_p.h" #include "qv4isel_p.h" diff --git a/src/v4/qv4propertytable.h b/src/v4/qv4propertytable.h index 787c319121..c98410d348 100644 --- a/src/v4/qv4propertytable.h +++ b/src/v4/qv4propertytable.h @@ -70,7 +70,6 @@ struct PropertyTableEntry { class PropertyTable { Q_DISABLE_COPY(PropertyTable) - friend class Array; friend class ArrayObject; public: diff --git a/src/v4/qv4regexpobject.h b/src/v4/qv4regexpobject.h index cd7953304b..6c7f583b50 100644 --- a/src/v4/qv4regexpobject.h +++ b/src/v4/qv4regexpobject.h @@ -45,7 +45,6 @@ #include "qmljs_engine.h" #include "qmljs_environment.h" #include "qv4functionobject.h" -#include "qv4array.h" #include "qv4string.h" #include "qv4codegen_p.h" #include "qv4isel_p.h" diff --git a/src/v4/qv4sparsearray.cpp b/src/v4/qv4sparsearray.cpp new file mode 100644 index 0000000000..703669b88f --- /dev/null +++ b/src/v4/qv4sparsearray.cpp @@ -0,0 +1,467 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4sparsearray.h" +#include "qmljs_runtime.h" +#include "qv4object.h" +#include "qv4functionobject.h" +#include + +#ifdef QT_QMAP_DEBUG +# include +# include +#endif + +namespace QQmlJS { +namespace VM { + +bool ArrayElementLessThan::operator()(const PropertyDescriptor &p1, const PropertyDescriptor &p2) const +{ + if (p1.type == PropertyDescriptor::Generic) + return false; + if (p2.type == PropertyDescriptor::Generic) + return true; + Value v1 = thisObject->getValue(m_context, &p1); + Value v2 = thisObject->getValue(m_context, &p2); + + if (v1.isUndefined()) + return false; + if (v2.isUndefined()) + return true; + if (!m_comparefn.isUndefined()) { + Value args[] = { v1, v2 }; + Value result = __qmljs_call_value(m_context, Value::undefinedValue(), m_comparefn, args, 2); + return result.toNumber(m_context) <= 0; + } + return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); +} + + +const SparseArrayNode *SparseArrayNode::nextNode() const +{ + const SparseArrayNode *n = this; + if (n->right) { + n = n->right; + while (n->left) + n = n->left; + } else { + const SparseArrayNode *y = n->parent(); + while (y && n == y->right) { + n = y; + y = n->parent(); + } + n = y; + } + return n; +} + +const SparseArrayNode *SparseArrayNode::previousNode() const +{ + const SparseArrayNode *n = this; + if (n->left) { + n = n->left; + while (n->right) + n = n->right; + } else { + const SparseArrayNode *y = n->parent(); + while (y && n == y->left) { + n = y; + y = n->parent(); + } + n = y; + } + return n; +} + +SparseArrayNode *SparseArrayNode::copy(SparseArray *d) const +{ + SparseArrayNode *n = d->createNode(size_left, 0, false); + n->value = value; + n->setColor(color()); + if (left) { + n->left = left->copy(d); + n->left->setParent(n); + } else { + n->left = 0; + } + if (right) { + n->right = right->copy(d); + n->right->setParent(n); + } else { + n->right = 0; + } + return n; +} + +/* + x y + \ / \ + y --> x b + / \ \ + a b a +*/ +void SparseArray::rotateLeft(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = x->right; + x->right = y->left; + if (y->left != 0) + y->left->setParent(x); + y->setParent(x->parent()); + if (x == root) + root = y; + else if (x == x->parent()->left) + x->parent()->left = y; + else + x->parent()->right = y; + y->left = x; + x->setParent(y); + y->size_left += x->size_left; +} + + +/* + x y + / / \ + y --> a x + / \ / + a b b +*/ +void SparseArray::rotateRight(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = x->left; + x->left = y->right; + if (y->right != 0) + y->right->setParent(x); + y->setParent(x->parent()); + if (x == root) + root = y; + else if (x == x->parent()->right) + x->parent()->right = y; + else + x->parent()->left = y; + y->right = x; + x->setParent(y); + x->size_left -= y->size_left; +} + + +void SparseArray::rebalance(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + x->setColor(SparseArrayNode::Red); + while (x != root && x->parent()->color() == SparseArrayNode::Red) { + if (x->parent() == x->parent()->parent()->left) { + SparseArrayNode *y = x->parent()->parent()->right; + if (y && y->color() == SparseArrayNode::Red) { + x->parent()->setColor(SparseArrayNode::Black); + y->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + x = x->parent()->parent(); + } else { + if (x == x->parent()->right) { + x = x->parent(); + rotateLeft(x); + } + x->parent()->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + rotateRight (x->parent()->parent()); + } + } else { + SparseArrayNode *y = x->parent()->parent()->left; + if (y && y->color() == SparseArrayNode::Red) { + x->parent()->setColor(SparseArrayNode::Black); + y->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + x = x->parent()->parent(); + } else { + if (x == x->parent()->left) { + x = x->parent(); + rotateRight(x); + } + x->parent()->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + rotateLeft(x->parent()->parent()); + } + } + } + root->setColor(SparseArrayNode::Black); +} + +void SparseArray::deleteNode(SparseArrayNode *z) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = z; + SparseArrayNode *x; + SparseArrayNode *x_parent; + if (y->left == 0) { + x = y->right; + if (y == mostLeftNode) { + if (x) + mostLeftNode = x; // It cannot have (left) children due the red black invariant. + else + mostLeftNode = y->parent(); + } + } else { + if (y->right == 0) { + x = y->left; + } else { + y = y->right; + while (y->left != 0) + y = y->left; + x = y->right; + } + } + if (y != z) { + z->left->setParent(y); + y->left = z->left; + if (y != z->right) { + x_parent = y->parent(); + if (x) + x->setParent(y->parent()); + y->parent()->left = x; + y->right = z->right; + z->right->setParent(y); + } else { + x_parent = y; + } + if (root == z) + root = y; + else if (z->parent()->left == z) + z->parent()->left = y; + else + z->parent()->right = y; + y->setParent(z->parent()); + // Swap the colors + SparseArrayNode::Color c = y->color(); + y->setColor(z->color()); + z->setColor(c); + y = z; + } else { + x_parent = y->parent(); + if (x) + x->setParent(y->parent()); + if (root == z) + root = x; + else if (z->parent()->left == z) + z->parent()->left = x; + else + z->parent()->right = x; + } + if (y->color() != SparseArrayNode::Red) { + while (x != root && (x == 0 || x->color() == SparseArrayNode::Black)) { + if (x == x_parent->left) { + SparseArrayNode *w = x_parent->right; + if (w->color() == SparseArrayNode::Red) { + w->setColor(SparseArrayNode::Black); + x_parent->setColor(SparseArrayNode::Red); + rotateLeft(x_parent); + w = x_parent->right; + } + if ((w->left == 0 || w->left->color() == SparseArrayNode::Black) && + (w->right == 0 || w->right->color() == SparseArrayNode::Black)) { + w->setColor(SparseArrayNode::Red); + x = x_parent; + x_parent = x_parent->parent(); + } else { + if (w->right == 0 || w->right->color() == SparseArrayNode::Black) { + if (w->left) + w->left->setColor(SparseArrayNode::Black); + w->setColor(SparseArrayNode::Red); + rotateRight(w); + w = x_parent->right; + } + w->setColor(x_parent->color()); + x_parent->setColor(SparseArrayNode::Black); + if (w->right) + w->right->setColor(SparseArrayNode::Black); + rotateLeft(x_parent); + break; + } + } else { + SparseArrayNode *w = x_parent->left; + if (w->color() == SparseArrayNode::Red) { + w->setColor(SparseArrayNode::Black); + x_parent->setColor(SparseArrayNode::Red); + rotateRight(x_parent); + w = x_parent->left; + } + if ((w->right == 0 || w->right->color() == SparseArrayNode::Black) && + (w->left == 0 || w->left->color() == SparseArrayNode::Black)) { + w->setColor(SparseArrayNode::Red); + x = x_parent; + x_parent = x_parent->parent(); + } else { + if (w->left == 0 || w->left->color() == SparseArrayNode::Black) { + if (w->right) + w->right->setColor(SparseArrayNode::Black); + w->setColor(SparseArrayNode::Red); + rotateLeft(w); + w = x_parent->left; + } + w->setColor(x_parent->color()); + x_parent->setColor(SparseArrayNode::Black); + if (w->left) + w->left->setColor(SparseArrayNode::Black); + rotateRight(x_parent); + break; + } + } + } + if (x) + x->setColor(SparseArrayNode::Black); + } + free(y); + --numEntries; +} + +void SparseArray::recalcMostLeftNode() +{ + mostLeftNode = &header; + while (mostLeftNode->left) + mostLeftNode = mostLeftNode->left; +} + +static inline int qMapAlignmentThreshold() +{ + // malloc on 32-bit platforms should return pointers that are 8-byte + // aligned or more while on 64-bit platforms they should be 16-byte aligned + // or more + return 2 * sizeof(void*); +} + +static inline void *qMapAllocate(int alloc, int alignment) +{ + return alignment > qMapAlignmentThreshold() + ? qMallocAligned(alloc, alignment) + : ::malloc(alloc); +} + +static inline void qMapDeallocate(SparseArrayNode *node, int alignment) +{ + if (alignment > qMapAlignmentThreshold()) + qFreeAligned(node); + else + ::free(node); +} + +SparseArrayNode *SparseArray::createNode(uint sl, SparseArrayNode *parent, bool left) +{ + SparseArrayNode *node = static_cast(qMapAllocate(sizeof(SparseArrayNode), Q_ALIGNOF(SparseArrayNode))); + Q_CHECK_PTR(node); + + node->p = (quintptr)parent; + node->left = 0; + node->right = 0; + node->size_left = sl; + node->value = UINT_MAX; + ++numEntries; + + if (parent) { + if (left) { + parent->left = node; + if (parent == mostLeftNode) + mostLeftNode = node; + } else { + parent->right = node; + } + node->setParent(parent); + rebalance(node); + } + return node; +} + +void SparseArray::freeTree(SparseArrayNode *root, int alignment) +{ + if (root->left) + freeTree(root->left, alignment); + if (root->right) + freeTree(root->right, alignment); + qMapDeallocate(root, alignment); +} + +SparseArray::SparseArray() + : numEntries(0) +{ + header.p = 0; + header.left = 0; + header.right = 0; + mostLeftNode = &header; +} + +SparseArray::SparseArray(const SparseArray &other) +{ + header.p = 0; + header.right = 0; + if (other.header.left) { + header.left = other.header.left->copy(this); + header.left->setParent(&header); + recalcMostLeftNode(); + } +} + +SparseArrayNode *SparseArray::insert(uint akey) +{ + SparseArrayNode *n = root(); + SparseArrayNode *y = end(); + bool left = true; + uint s = akey; + while (n) { + y = n; + if (s == n->size_left) { + return n; + } else if (s < n->size_left) { + left = true; + n = n->left; + } else { + left = false; + s -= n->size_left; + n = n->right; + } + } + + return createNode(s, y, left); +} + + +} +} diff --git a/src/v4/qv4sparsearray.h b/src/v4/qv4sparsearray.h new file mode 100644 index 0000000000..ce6a697a77 --- /dev/null +++ b/src/v4/qv4sparsearray.h @@ -0,0 +1,369 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ARRAY_H +#define QV4ARRAY_H + +#include "qv4global.h" +#include +#include +#include +#include + +#ifdef Q_MAP_DEBUG +#include +#endif + +#include + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct SparseArray; + +class ArrayElementLessThan +{ +public: + inline ArrayElementLessThan(ExecutionContext *context, Object *thisObject, const Value &comparefn) + : m_context(context), thisObject(thisObject), m_comparefn(comparefn) {} + + bool operator()(const PropertyDescriptor &v1, const PropertyDescriptor &v2) const; + +private: + ExecutionContext *m_context; + Object *thisObject; + Value m_comparefn; +}; + + +struct SparseArrayNode +{ + quintptr p; + SparseArrayNode *left; + SparseArrayNode *right; + uint size_left; + uint value; + + enum Color { Red = 0, Black = 1 }; + enum { Mask = 3 }; // reserve the second bit as well + + const SparseArrayNode *nextNode() const; + SparseArrayNode *nextNode() { return const_cast(const_cast(this)->nextNode()); } + const SparseArrayNode *previousNode() const; + SparseArrayNode *previousNode() { return const_cast(const_cast(this)->previousNode()); } + + Color color() const { return Color(p & 1); } + void setColor(Color c) { if (c == Black) p |= Black; else p &= ~Black; } + SparseArrayNode *parent() const { return reinterpret_cast(p & ~Mask); } + void setParent(SparseArrayNode *pp) { p = (p & Mask) | quintptr(pp); } + + uint key() const { + uint k = size_left; + const SparseArrayNode *n = this; + while (SparseArrayNode *p = n->parent()) { + if (p && p->right == n) + k += p->size_left; + n = p; + } + return k; + } + + SparseArrayNode *copy(SparseArray *d) const; + + SparseArrayNode *lowerBound(uint key); + SparseArrayNode *upperBound(uint key); +}; + + +inline SparseArrayNode *SparseArrayNode::lowerBound(uint akey) +{ + SparseArrayNode *n = this; + SparseArrayNode *last = 0; + while (n) { + if (akey <= n->size_left) { + last = n; + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + return last; +} + + +inline SparseArrayNode *SparseArrayNode::upperBound(uint akey) +{ + SparseArrayNode *n = this; + SparseArrayNode *last = 0; + while (n) { + if (akey < n->size_left) { + last = n; + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + return last; +} + + + +struct Q_V4_EXPORT SparseArray +{ + SparseArray(); + ~SparseArray() { + if (root()) + freeTree(header.left, Q_ALIGNOF(SparseArrayNode)); + } + + SparseArray(const SparseArray &other); +private: + SparseArray &operator=(const SparseArray &other); + + int numEntries; + SparseArrayNode header; + SparseArrayNode *mostLeftNode; + + void rotateLeft(SparseArrayNode *x); + void rotateRight(SparseArrayNode *x); + void rebalance(SparseArrayNode *x); + void recalcMostLeftNode(); + + SparseArrayNode *root() const { return header.left; } + + void deleteNode(SparseArrayNode *z); + + +public: + SparseArrayNode *createNode(uint sl, SparseArrayNode *parent, bool left); + void freeTree(SparseArrayNode *root, int alignment); + + SparseArrayNode *findNode(uint akey) const; + + uint pop_front(); + void push_front(uint at); + uint pop_back(uint len); + void push_back(uint at, uint len); + + QList keys() const; + + const SparseArrayNode *end() const { return &header; } + SparseArrayNode *end() { return &header; } + const SparseArrayNode *begin() const { if (root()) return mostLeftNode; return end(); } + SparseArrayNode *begin() { if (root()) return mostLeftNode; return end(); } + + SparseArrayNode *erase(SparseArrayNode *n); + + SparseArrayNode *lowerBound(uint key); + const SparseArrayNode *lowerBound(uint key) const; + SparseArrayNode *upperBound(uint key); + const SparseArrayNode *upperBound(uint key) const; + SparseArrayNode *insert(uint akey); + + // STL compatibility + typedef uint key_type; + typedef int mapped_type; + typedef qptrdiff difference_type; + typedef int size_type; + +#ifdef Q_MAP_DEBUG + void dump() const; +#endif +}; + +inline SparseArrayNode *SparseArray::findNode(uint akey) const +{ + SparseArrayNode *n = root(); + + while (n) { + if (akey == n->size_left) { + return n; + } else if (akey < n->size_left) { + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + + return 0; +} + +inline uint SparseArray::pop_front() +{ + uint idx = UINT_MAX ; + + SparseArrayNode *n = findNode(0); + if (n) { + idx = n->value; + deleteNode(n); + // adjust all size_left indices on the path to leftmost item by 1 + SparseArrayNode *n = root(); + while (n) { + n->size_left -= 1; + n = n->left; + } + } + return idx; +} + +inline void SparseArray::push_front(uint value) +{ + // adjust all size_left indices on the path to leftmost item by 1 + SparseArrayNode *n = root(); + while (n) { + n->size_left += 1; + n = n->left; + } + n = insert(0); + n->value = value; +} + +inline uint SparseArray::pop_back(uint len) +{ + uint idx = UINT_MAX; + if (!len) + return idx; + + SparseArrayNode *n = findNode(len - 1); + if (n) { + idx = n->value; + deleteNode(n); + } + return idx; +} + +inline void SparseArray::push_back(uint index, uint len) +{ + SparseArrayNode *n = insert(len); + n->value = index; +} + +#ifdef Q_MAP_DEBUG + +void SparseArray::dump() const +{ + const_iterator it = begin(); + qDebug() << "map dump:"; + while (it != end()) { + const SparseArrayNode *n = it.i; + int depth = 0; + while (n && n != root()) { + ++depth; + n = n->parent(); + } + QByteArray space(4*depth, ' '); + qDebug() << space << (it.i->color() == SparseArrayNode::Red ? "Red " : "Black") << it.i << it.i->left << it.i->right + << it.key() << it.value(); + ++it; + } + qDebug() << "---------"; +} +#endif + + +inline SparseArrayNode *SparseArray::erase(SparseArrayNode *n) +{ + if (n == end()) + return n; + + SparseArrayNode *next = n->nextNode(); + deleteNode(n); + return next; +} + +inline QList SparseArray::keys() const +{ + QList res; + res.reserve(numEntries); + SparseArrayNode *n = mostLeftNode; + while (n != end()) { + res.append(n->key()); + n = n->nextNode(); + } + return res; +} + +inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const +{ + const SparseArrayNode *lb = root()->lowerBound(akey); + if (!lb) + lb = end(); + return lb; +} + + +inline SparseArrayNode *SparseArray::lowerBound(uint akey) +{ + SparseArrayNode *lb = root()->lowerBound(akey); + if (!lb) + lb = end(); + return lb; +} + + +inline const SparseArrayNode *SparseArray::upperBound(uint akey) const +{ + const SparseArrayNode *ub = root()->upperBound(akey); + if (!ub) + ub = end(); + return ub; +} + + +inline SparseArrayNode *SparseArray::upperBound(uint akey) +{ + SparseArrayNode *ub = root()->upperBound(akey); + if (!ub) + ub = end(); + return ub; +} + +} +} + +QT_END_NAMESPACE + +#endif // QMAP_H diff --git a/src/v4/v4.pro b/src/v4/v4.pro index 24832ecd9b..3f1d74130a 100644 --- a/src/v4/v4.pro +++ b/src/v4/v4.pro @@ -36,7 +36,7 @@ SOURCES += \ debugging.cpp \ qv4mm.cpp \ qv4managed.cpp \ - qv4array.cpp \ + qv4sparsearray.cpp \ qv4arrayobject.cpp \ qv4argumentsobject.cpp \ qv4booleanobject.cpp \ @@ -72,7 +72,7 @@ HEADERS += \ qv4identifier.h \ qv4mm.h \ qv4managed.h \ - qv4array.h \ + qv4sparsearray.h \ qv4arrayobject.h \ qv4argumentsobject.h \ qv4booleanobject.h \ -- cgit v1.2.3 From 10f24399a7c626f6c497bd47d5aff6e212dcc0cb Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 3 Feb 2013 12:34:32 +0100 Subject: Don't use a QVector to hold the member data We don't need any implicit sharing or atomic refcounting. In addition, array access is faster this way. Change-Id: I85e897b74bad92e126e7502fa8e934c9519b4a63 Reviewed-by: Simon Hausmann --- src/v4/qv4object.cpp | 32 ++++++++++++++++++++------------ src/v4/qv4object.h | 5 ++++- src/v4/qv4objectiterator.cpp | 2 +- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index ff4fffc4df..7ea8f3aeaf 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -70,6 +70,7 @@ using namespace QQmlJS::VM; // Object::~Object() { + delete memberData; delete sparseArray; } @@ -189,8 +190,8 @@ void Object::markObjects() continue; (*it)->name->mark(); } - for (int i = 0; i < (uint)memberData.size(); ++i) { - const PropertyDescriptor &pd = memberData.at(i); + for (int i = 0; i < memberDataSize; ++i) { + const PropertyDescriptor &pd = memberData[i]; if (pd.isData()) { if (Managed *m = pd.value.asManaged()) m->mark(); @@ -212,10 +213,17 @@ PropertyDescriptor *Object::insertMember(String *s) PropertyTableEntry *e = members->insert(s); if (e->valueIndex == UINT_MAX) { - e->valueIndex = memberData.size(); - memberData.resize(memberData.size() + 1); + if (memberDataSize == memberDataAlloc) { + memberDataAlloc = qMax((uint)8, 2*memberDataAlloc); + PropertyDescriptor *newMemberData = new PropertyDescriptor[memberDataAlloc]; + memcpy(newMemberData, memberData, sizeof(PropertyDescriptor)*memberDataSize); + delete memberData; + memberData = newMemberData; + } + e->valueIndex = memberDataSize; + ++memberDataSize; } - return memberData.data() + e->valueIndex; + return memberData + e->valueIndex; } // Section 8.12.1 @@ -228,7 +236,7 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *na if (members) { uint idx = members->find(name); if (idx < UINT_MAX) - return memberData.data() + idx; + return memberData + idx; } return 0; } @@ -257,7 +265,7 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, Str if (o->members) { uint idx = o->members->find(name); if (idx < UINT_MAX) - return o->memberData.data() + idx; + return o->memberData + idx; } o = o->prototype; } @@ -303,7 +311,7 @@ Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) if (idx < UINT_MAX) { if (hasProperty) *hasProperty = true; - return getValue(ctx, o->memberData.data() + idx); + return getValue(ctx, o->memberData + idx); } } o = o->prototype; @@ -561,7 +569,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr PropertyDescriptor *current; if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { - PropertyDescriptor *lp = memberData.data() + ArrayObject::LengthPropertyIndex; + PropertyDescriptor *lp = memberData + ArrayObject::LengthPropertyIndex; assert(0 == members->find(ctx->engine->id_length)); if (desc->isEmpty() || desc->isSubset(lp)) return true; @@ -610,7 +618,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Prop PropertyDescriptor *current; // 15.4.5.1, 4b - if (isArrayObject() && index >= arrayLength() && !memberData.at(ArrayObject::LengthPropertyIndex).isWritable()) + if (isArrayObject() && index >= arrayLength() && !memberData[ArrayObject::LengthPropertyIndex].isWritable()) goto reject; if (isNonStrictArgumentsObject) @@ -837,7 +845,7 @@ void Object::setArrayLengthUnchecked(uint l) bool Object::setArrayLength(uint newLen) { assert(isArrayObject()); - const PropertyDescriptor *lengthProperty = memberData.constData() + ArrayObject::LengthPropertyIndex; + const PropertyDescriptor *lengthProperty = memberData + ArrayObject::LengthPropertyIndex; if (lengthProperty && !lengthProperty->isWritable()) return false; uint oldLen = arrayLength(); @@ -909,7 +917,7 @@ void ArrayObject::init(ExecutionContext *context) if (!members) members.reset(new PropertyTable()); PropertyDescriptor *pd = insertMember(context->engine->id_length); - assert(pd == memberData.constData() + LengthPropertyIndex); + assert(pd == memberData + LengthPropertyIndex); pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Enabled; pd->enumberable = PropertyDescriptor::Disabled; diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index 0b781095ee..e4b5c9f1c9 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -103,7 +103,9 @@ struct URIErrorPrototype; struct Q_V4_EXPORT Object: Managed { Object *prototype; QScopedPointer members; - QVector memberData; + uint memberDataSize; + uint memberDataAlloc; + PropertyDescriptor *memberData; uint arrayLen; union { @@ -115,6 +117,7 @@ struct Q_V4_EXPORT Object: Managed { Object() : prototype(0) + , memberDataSize(0), memberDataAlloc(0), memberData(0) , arrayLen(0), arrayOffset(0), sparseArray(0) { type = Type_Object; } virtual ~Object(); diff --git a/src/v4/qv4objectiterator.cpp b/src/v4/qv4objectiterator.cpp index d94d778675..04f11c6d20 100644 --- a/src/v4/qv4objectiterator.cpp +++ b/src/v4/qv4objectiterator.cpp @@ -129,7 +129,7 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) ++tableIndex; // ### check that it's not a repeated attribute if (pt) { - PropertyDescriptor *pd = current->memberData.data() + pt->valueIndex; + PropertyDescriptor *pd = current->memberData + pt->valueIndex; if (!(flags & EnumberableOnly) || pd->isEnumerable()) { *name = pt->name; p = pd; -- cgit v1.2.3 From ce5af0c8039db0395725e13d855795900d65596d Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 1 Feb 2013 12:57:22 +0100 Subject: Fixed the llvm bits of the build process. Change-Id: I618e0ee413e9548817f84473917ccb8051f689f2 Reviewed-by: Lars Knoll --- .gitignore | 1 + src/v4/llvm_installation.pri | 23 +++++++++++++++++++++++ src/v4/v4.pri | 2 +- src/v4/v4.pro | 14 ++++---------- tools/v4/main.cpp | 33 ++++++++++++++++----------------- tools/v4/v4.pro | 3 +++ 6 files changed, 48 insertions(+), 28 deletions(-) create mode 100644 src/v4/llvm_installation.pri diff --git a/.gitignore b/.gitignore index 8df072ad56..2934c505f4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.o Makefile +Makefile.* src/tools/v4 udis86_itab.* *.pyc diff --git a/src/v4/llvm_installation.pri b/src/v4/llvm_installation.pri new file mode 100644 index 0000000000..99e955fd2b --- /dev/null +++ b/src/v4/llvm_installation.pri @@ -0,0 +1,23 @@ +LLVM_CONFIG=llvm-config +# Pick up the qmake variable or environment variable for LLVM_INSTALL_DIR. If either was set, change the LLVM_CONFIG to use that. +isEmpty(LLVM_INSTALL_DIR):LLVM_INSTALL_DIR=$$(LLVM_INSTALL_DIR) +!isEmpty(LLVM_INSTALL_DIR):LLVM_CONFIG=$$LLVM_INSTALL_DIR/bin/llvm-config +exists ($${LLVM_CONFIG}) { + CONFIG += llvm-libs + message("Found LLVM in $$LLVM_INSTALL_DIR") +} + +llvm-libs { + win32 { + LLVM_INCLUDEPATH = $$LLVM_INSTALL_DIR/include +# TODO: check if the next line is needed somehow for the llvm_runtime target. + LLVM_LIBS += -ladvapi32 -lshell32 + } + + unix { + LLVM_INCLUDEPATH = $$system($$LLVM_CONFIG --includedir) + LLVM_LIBDIR = $$system($$LLVM_CONFIG --libdir) + } + + LLVM_DEFINES += __STDC_LIMIT_MACROS __STDC_CONSTANT_MACROS +} diff --git a/src/v4/v4.pri b/src/v4/v4.pri index 6f70a8c694..c1eabe64d5 100644 --- a/src/v4/v4.pri +++ b/src/v4/v4.pri @@ -1,4 +1,4 @@ - +include(llvm_installation.pri) include(../3rdparty/masm/masm-defs.pri) !llvm: DEFINES += QMLJS_NO_LLVM diff --git a/src/v4/v4.pro b/src/v4/v4.pro index 3f1d74130a..60338cc417 100644 --- a/src/v4/v4.pro +++ b/src/v4/v4.pro @@ -4,7 +4,7 @@ QT = core CONFIG += internal_module -LLVM_CONFIG=llvm-config +include(v4.pri) OBJECTS_DIR=.obj @@ -15,11 +15,6 @@ CONFIG += warn_off #win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x66000000 #TODO ??! - -# Pick up the qmake variable or environment variable for LLVM_INSTALL_DIR. If either was set, change the LLVM_CONFIG to use that. -isEmpty(LLVM_INSTALL_DIR):LLVM_INSTALL_DIR=$$(LLVM_INSTALL_DIR) -!isEmpty(LLVM_INSTALL_DIR):LLVM_CONFIG=$$LLVM_INSTALL_DIR/bin/llvm-config - !macx-clang*:LIBS += -rdynamic SOURCES += \ @@ -93,7 +88,7 @@ HEADERS += \ qv4objectiterator.h \ qv4regexp.h -llvm { +llvm-libs { SOURCES += \ qv4isel_llvm.cpp @@ -104,6 +99,7 @@ HEADERS += \ LLVM_RUNTIME_BC = $$PWD/llvm_runtime.bc DEFINES += LLVM_RUNTIME="\"\\\"$$LLVM_RUNTIME_BC\\\"\"" +DEFINES += QMLJS_WITH_LLVM INCLUDEPATH += \ $$system($$LLVM_CONFIG --includedir) @@ -122,8 +118,7 @@ GEN_LLVM_RUNTIME_FLAGS = $$system($$LLVM_CONFIG --cppflags) GEN_LLVM_RUNTIME_FLAGS -= -pedantic gen_llvm_runtime.target = llvm_runtime -gen_llvm_runtime.commands = clang -O2 -emit-llvm -c $(INCPATH) $$GEN_LLVM_RUNTIME_FLAGS -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o $$LLVM_RUNTIME_BC - +gen_llvm_runtime.commands = clang -O2 -emit-llvm -c -I$$PWD -I$$PWD/../3rdparty/masm $$join(QT.core.includes, " -I", "-I") $$GEN_LLVM_RUNTIME_FLAGS -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o $$LLVM_RUNTIME_BC } # Use SSE2 floating point math on 32 bit instead of the default @@ -150,4 +145,3 @@ QMAKE_EXTRA_TARGETS += checkmothtarget include(moth/moth.pri) include(../3rdparty/masm/masm.pri) include(../3rdparty/double-conversion/double-conversion.pri) -include(v4.pri) diff --git a/tools/v4/main.cpp b/tools/v4/main.cpp index 7ac064fb9d..0d215b9e5e 100644 --- a/tools/v4/main.cpp +++ b/tools/v4/main.cpp @@ -39,9 +39,9 @@ ** ****************************************************************************/ -#ifndef QMLJS_NO_LLVM +#ifdef QMLJS_WITH_LLVM # include "private/qv4_llvm_p.h" -#endif +#endif // QMLJS_WITH_LLVM #include "private/debugging.h" #include "private/qv4object.h" @@ -153,7 +153,7 @@ static void showException(QQmlJS::VM::ExecutionContext *ctx) } } -#ifndef QMLJS_NO_LLVM +#ifdef QMLJS_WITH_LLVM int executeLLVMCode(void *codePtr) { using namespace QQmlJS; @@ -260,8 +260,7 @@ int evaluateCompiledCode(const QStringList &files) return EXIT_SUCCESS; } - -#endif +#endif // QMLJS_WITH_LLVM int main(int argc, char *argv[]) @@ -278,9 +277,9 @@ int main(int argc, char *argv[]) use_llvm_jit } mode = use_masm; -#ifndef QMLJS_NO_LLVM +#ifdef QMLJS_WITH_LLVM QQmlJS::LLVMOutputType fileType = QQmlJS::LLVMOutputObject; -#endif // QMLJS_NO_LLVM +#endif // QMLJS_WITH_LLVM bool enableDebugging = false; if (!args.isEmpty()) { @@ -301,7 +300,7 @@ int main(int argc, char *argv[]) args.removeFirst(); } -#ifndef QMLJS_NO_LLVM +#ifdef QMLJS_WITH_LLVM if (args.first() == QLatin1String("--compile")) { mode = use_llvm_compiler; args.removeFirst(); @@ -328,7 +327,7 @@ int main(int argc, char *argv[]) mode = use_llvm_jit; args.removeFirst(); } -#endif // QMLJS_NO_LLVM +#endif // QMLJS_WITH_LLVM if (args.first() == QLatin1String("--help")) { std::cerr << "Usage: v4 [|--debug|-d] [|--jit|--interpret|--compile|--aot|--llvm-jit] file..." << std::endl; return EXIT_SUCCESS; @@ -336,20 +335,20 @@ int main(int argc, char *argv[]) } switch (mode) { -#ifdef QMLJS_NO_LLVM - case use_llvm_compiler: - case use_llvm_runtime: - case use_llvm_jit: - std::cerr << "LLVM backend was not built, compiler is unavailable." << std::endl; - return EXIT_FAILURE; -#else // QMLJS_NO_LLVM +#ifdef QMLJS_WITH_LLVM case use_llvm_jit: return compileFiles(args, QQmlJS::LLVMOutputJit); case use_llvm_compiler: return compileFiles(args, fileType); case use_llvm_runtime: return evaluateCompiledCode(args); -#endif // QMLJS_NO_LLVM +#else // !QMLJS_WITH_LLVM + case use_llvm_compiler: + case use_llvm_runtime: + case use_llvm_jit: + std::cerr << "LLVM backend was not built, compiler is unavailable." << std::endl; + return EXIT_FAILURE; +#endif // QMLJS_WITH_LLVM case use_masm: case use_moth: { QScopedPointer iSelFactory; diff --git a/tools/v4/v4.pro b/tools/v4/v4.pro index 3f878fa6d4..fcf1b28582 100644 --- a/tools/v4/v4.pro +++ b/tools/v4/v4.pro @@ -4,4 +4,7 @@ SOURCES = main.cpp include(../../src/v4/v4.pri) +llvm-libs { + DEFINES += QMLJS_WITH_LLVM +} load(qt_tool) -- cgit v1.2.3 From 334f10f4203092adaba93ba65c01e622414edf80 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Sun, 3 Feb 2013 11:03:39 +0100 Subject: Fix compilation for access to the extensible flag. Change-Id: Iaff480e43d8bb241d01cd1f6bc75ddf3d060a33c Reviewed-by: Lars Knoll --- src/v4/qv4object.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index e4b5c9f1c9..d4815663cd 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -422,6 +422,7 @@ protected: virtual void markObjects(); friend struct ObjectIterator; + friend struct ObjectPrototype; }; struct ForEachIteratorObject: Object { -- cgit v1.2.3 From 9162f48ddb1a4f280b17bf40c32068dcc6f4dd89 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Sun, 3 Feb 2013 12:19:15 +0100 Subject: MOTH: have the VM::Function hold on to regexps. When regexps were created, they were not added to the VM::Function's generatedValues, resulting in premature collection. Change-Id: Idac2202e3040c6311c6f1ceead641e51a8de9729 Reviewed-by: Lars Knoll --- src/v4/moth/qv4isel_moth.cpp | 10 ++++++---- src/v4/moth/qv4vme_moth.cpp | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index f3c5b6bf6c..f31153a3d2 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -400,11 +400,13 @@ void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) { + VM::Value v = VM::Value::fromObject(engine()->newRegExpObject( + *sourceRegexp->value, + sourceRegexp->flags)); + _vmFunction->generatedValues.append(v); + Instruction::LoadValue load; - load.value = Instr::Param::createValue( - VM::Value::fromObject(engine()->newRegExpObject( - *sourceRegexp->value, - sourceRegexp->flags))); + load.value = Instr::Param::createValue(v); load.result = getResultParam(targetTemp); addInstruction(load); } diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index a3ab1de46a..e9889fca40 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -225,7 +225,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) - TRACE(property name, "%s", qPrintable(instr.name->toQString())); + TRACE(property name, "%s, args=%u, argc=%u", qPrintable(instr.name->toQString()), instr.args, instr.argc); Q_ASSERT(instr.args + instr.argc < stackSize); VM::Value *args = stack + instr.args; VALUE(instr.result) = __qmljs_call_property(context, VALUE(instr.base), instr.name, args, instr.argc); -- cgit v1.2.3 From c0d77db836f7dc46c148d2ca9478b96e660088fe Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 5 Feb 2013 22:41:40 +0100 Subject: Fix compiler warning. Change-Id: I8cf22a60dfe42a4fcc3ab130aeb82f46e018c2f5 Reviewed-by: Lars Knoll --- src/v4/qv4propertytable.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qv4propertytable.h b/src/v4/qv4propertytable.h index c98410d348..79e4c776f5 100644 --- a/src/v4/qv4propertytable.h +++ b/src/v4/qv4propertytable.h @@ -70,7 +70,7 @@ struct PropertyTableEntry { class PropertyTable { Q_DISABLE_COPY(PropertyTable) - friend class ArrayObject; + friend struct ArrayObject; public: PropertyTable() -- cgit v1.2.3 From 2a519f5610b4c284488e730442a758dbbb4d21d3 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 5 Feb 2013 22:47:10 +0100 Subject: Add memory scribbling back in. Disabled by default, turn on by setting the env. var. MM_SCRIBBLE. Change-Id: I616f48976d7f0541390dd1920efc69e294c5056e Reviewed-by: Lars Knoll --- src/v4/qv4mm.cpp | 10 ++++------ src/v4/qv4mm.h | 2 -- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/v4/qv4mm.cpp b/src/v4/qv4mm.cpp index d12388a01b..8e85001954 100644 --- a/src/v4/qv4mm.cpp +++ b/src/v4/qv4mm.cpp @@ -79,7 +79,7 @@ struct MemoryManager::Data { memset(smallItems, 0, sizeof(smallItems)); memset(nChunks, 0, sizeof(nChunks)); - scribble = qgetenv("MM_NO_SCRIBBLE").isEmpty(); + scribble = !qgetenv("MM_SCRIBBLE").isEmpty(); aggressiveGC = !qgetenv("MM_AGGRESSIVE_GC").isEmpty(); } @@ -167,11 +167,9 @@ Managed *MemoryManager::alloc(std::size_t size) return m; } -void MemoryManager::scribble(Managed *obj, int c, int size) const -{ - if (m_d->scribble) +#define SCRIBBLE(obj, c, size) \ + if (m_d->scribble) \ ::memset((void *)(obj + 1), c, size - sizeof(Managed)); -} void MemoryManager::mark() { @@ -222,7 +220,7 @@ std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t m->nextFree = *f; f = &m->nextFree; - //scribble(m, 0x99, size); + SCRIBBLE(m, 0x99, size); ++freedCount; } } diff --git a/src/v4/qv4mm.h b/src/v4/qv4mm.h index 2abc1b45e9..3ee914e16a 100644 --- a/src/v4/qv4mm.h +++ b/src/v4/qv4mm.h @@ -105,8 +105,6 @@ protected: // TODO: try to inline Managed *alloc(std::size_t size); - void scribble(Managed *obj, int c, int size) const; - ExecutionEngine *engine() const; #ifdef DETAILED_MM_STATS -- cgit v1.2.3 From 4a1eba48a10c7b4eae8e0f5dc8ee80edc23b8e63 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 5 Feb 2013 22:49:26 +0100 Subject: Fix GC for the last object in a chunk. heapChunkBoundariesEnd points to the last entry in a chunk, so include it as a valid address to be candidate for marking. Change-Id: Ieb1fbb494ae2fd8ee5d38ae59529cf441fbcd729 Reviewed-by: Lars Knoll --- src/v4/qv4mm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qv4mm.cpp b/src/v4/qv4mm.cpp index 8e85001954..7e60e001fd 100644 --- a/src/v4/qv4mm.cpp +++ b/src/v4/qv4mm.cpp @@ -370,7 +370,7 @@ void MemoryManager::collectFromStack() const reinterpret_cast(*current); #endif - if (genericPtr < *heapChunkBoundaries || genericPtr >= *(heapChunkBoundariesEnd - 1)) + if (genericPtr < *heapChunkBoundaries || genericPtr > *(heapChunkBoundariesEnd - 1)) continue; int index = qLowerBound(heapChunkBoundaries, heapChunkBoundariesEnd, genericPtr) - heapChunkBoundaries; // An odd index means the pointer is _before_ the end of a heap chunk and therefore valid. -- cgit v1.2.3 From 6db89bc9720fb4c1ac948a469d5c53535c84c0e7 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 3 Feb 2013 22:08:39 +0100 Subject: Don't use a QVector to store the array data Refactor the code to store the array data in a simple C array. This will later on allow for direct inline access to the data. Change-Id: Ic829bf1a90abfcda27ab4291cb7d5721bbd58403 Reviewed-by: Erik Verbruggen --- src/v4/qv4arrayobject.cpp | 95 +++++++++++++++++------ src/v4/qv4jsonobject.cpp | 3 +- src/v4/qv4object.cpp | 173 ++++++++++++++++++++++++++--------------- src/v4/qv4object.h | 180 +++++++++++++++++-------------------------- src/v4/qv4objectiterator.cpp | 2 +- 5 files changed, 253 insertions(+), 200 deletions(-) diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp index 8f1fc215df..bec7c2283a 100644 --- a/src/v4/qv4arrayobject.cpp +++ b/src/v4/qv4arrayobject.cpp @@ -62,11 +62,14 @@ Value ArrayCtor::call(ExecutionContext *ctx) return Value::undefinedValue(); } + if (len < 0x1000) + a->arrayReserve(len); } else { len = ctx->argumentCount; - for (unsigned int i = 0; i < len; ++i) { - a->arraySet(i, ctx->argument(i)); - } + a->arrayReserve(len); + for (unsigned int i = 0; i < len; ++i) + fillDescriptor(a->arrayData + i, ctx->argument(i)); + a->arrayDataLen = len; } a->setArrayLengthUnchecked(len); @@ -138,14 +141,13 @@ Value ArrayPrototype::method_concat(ExecutionContext *ctx) } for (uint i = 0; i < ctx->argumentCount; ++i) { - quint32 k = result->arrayLength(); Value arg = ctx->argument(i); if (ArrayObject *elt = arg.asArrayObject()) result->arrayConcat(elt); else - result->arraySet(k, arg); + result->arraySet(getLength(ctx, result), arg); } return Value::fromObject(result); @@ -252,25 +254,37 @@ Value ArrayPrototype::method_push(ExecutionContext *ctx) bool protoHasArray = false; Object *p = instance; while ((p = p->prototype)) - if (p->arrayLength()) + if (p->arrayDataLen) protoHasArray = true; - if (!protoHasArray && len == instance->arrayLength()) { + if (!protoHasArray && instance->arrayDataLen <= len) { for (uint i = 0; i < ctx->argumentCount; ++i) { Value v = ctx->argument(i); - instance->push_back(v); + + if (!instance->sparseArray) { + if (len >= instance->arrayAlloc) + instance->arrayReserve(len + 1); + fillDescriptor(instance->arrayData + len, v); + instance->arrayDataLen = len + 1; + } else { + uint i = instance->allocArrayValue(v); + instance->sparseArray->push_back(i, len); + } + ++len; } } else { for (uint i = 0; i < ctx->argumentCount; ++i) instance->__put__(ctx, len + i, ctx->argument(i)); + len += ctx->argumentCount; } - uint newLen = len + ctx->argumentCount; - if (!instance->isArrayObject()) - instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(len); + else + instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len)); - if (newLen < INT_MAX) - return Value::fromInt32(newLen); - return Value::fromDouble((double)newLen); + if (len < INT_MAX) + return Value::fromInt32(len); + return Value::fromDouble((double)len); } @@ -313,11 +327,21 @@ Value ArrayPrototype::method_shift(ExecutionContext *ctx) bool protoHasArray = false; Object *p = instance; while ((p = p->prototype)) - if (p->arrayLength()) + if (p->arrayDataLen) protoHasArray = true; - if (!protoHasArray && len >= instance->arrayLength()) { - instance->pop_front(); + if (!protoHasArray && instance->arrayDataLen <= len) { + if (!instance->sparseArray) { + if (instance->arrayDataLen) { + ++instance->arrayOffset; + ++instance->arrayData; + --instance->arrayDataLen; + --instance->arrayAlloc; + } + } else { + uint idx = instance->sparseArray->pop_front(); + instance->freeArrayValue(idx); + } } else { // do it the slow way for (uint k = 1; k < len; ++k) { @@ -331,7 +355,9 @@ Value ArrayPrototype::method_shift(ExecutionContext *ctx) instance->__delete__(ctx, len - 1); } - if (!instance->isArrayObject()) + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(len - 1); + else instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - 1)); return result; } @@ -365,8 +391,9 @@ Value ArrayPrototype::method_slice(ExecutionContext *ctx) for (uint i = start; i < end; ++i) { bool exists; Value v = o->__get__(ctx, i, &exists); - if (exists) + if (exists) { result->arraySet(n, v); + } ++n; } return Value::fromObject(result); @@ -399,8 +426,8 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) uint deleteCount = (uint)qMin(qMax(ctx->argument(1).toInteger(ctx), 0.), (double)(len - start)); - newArray->arrayData.resize(deleteCount); - PropertyDescriptor *pd = newArray->arrayData.data(); + newArray->arrayReserve(deleteCount); + PropertyDescriptor *pd = newArray->arrayData; for (uint i = 0; i < deleteCount; ++i) { pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Enabled; @@ -409,6 +436,7 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) pd->value = instance->__get__(ctx, start + i); ++pd; } + newArray->arrayDataLen = deleteCount; newArray->setArrayLengthUnchecked(deleteCount); uint itemCount = ctx->argumentCount < 2 ? 0 : ctx->argumentCount - 2; @@ -454,13 +482,25 @@ Value ArrayPrototype::method_unshift(ExecutionContext *ctx) bool protoHasArray = false; Object *p = instance; while ((p = p->prototype)) - if (p->arrayLength()) + if (p->arrayDataLen) protoHasArray = true; - if (!protoHasArray && len >= instance->arrayLength()) { + if (!protoHasArray && instance->arrayDataLen <= len) { for (int i = ctx->argumentCount - 1; i >= 0; --i) { Value v = ctx->argument(i); - instance->push_front(v); + + if (!instance->sparseArray) { + if (!instance->arrayOffset) + instance->getArrayHeadRoom(); + + --instance->arrayOffset; + --instance->arrayData; + ++instance->arrayDataLen; + fillDescriptor(instance->arrayData, v); + } else { + uint idx = instance->allocArrayValue(v); + instance->sparseArray->push_front(idx); + } } } else { for (uint k = len; k > 0; --k) { @@ -474,8 +514,11 @@ Value ArrayPrototype::method_unshift(ExecutionContext *ctx) for (uint i = 0; i < ctx->argumentCount; ++i) instance->__put__(ctx, i, ctx->argument(i)); } + uint newLen = len + ctx->argumentCount; - if (!instance->isArrayObject()) + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(newLen); + else instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); if (newLen < INT_MAX) @@ -655,6 +698,7 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject(ctx); + a->arrayReserve(len); a->setArrayLengthUnchecked(len); for (uint k = 0; k < len; ++k) { @@ -686,6 +730,7 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) Value thisArg = ctx->argument(1); ArrayObject *a = ctx->engine->newArrayObject(ctx); + a->arrayReserve(len); uint to = 0; for (uint k = 0; k < len; ++k) { diff --git a/src/v4/qv4jsonobject.cpp b/src/v4/qv4jsonobject.cpp index d30c10a3e7..4bb6cd53cd 100644 --- a/src/v4/qv4jsonobject.cpp +++ b/src/v4/qv4jsonobject.cpp @@ -897,7 +897,8 @@ Value JsonObject::method_stringify(ExecutionContext *ctx) if (o) { stringify.replacerFunction = o->asFunctionObject(); if (o->isArrayObject()) { - for (uint i = 0; i < o->arrayLength(); ++i) { + uint arrayLen = o->arrayLength(); + for (uint i = 0; i < arrayLen; ++i) { Value v = o->__get__(ctx, i); if (v.asNumberObject() || v.asStringObject() || v.isNumber()) v = __qmljs_to_string(v, ctx); diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 7ea8f3aeaf..dda340b3b0 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -70,7 +70,8 @@ using namespace QQmlJS::VM; // Object::~Object() { - delete memberData; + delete [] memberData; + delete [] (arrayData - (sparseArray ? 0 : arrayOffset)); delete sparseArray; } @@ -217,7 +218,7 @@ PropertyDescriptor *Object::insertMember(String *s) memberDataAlloc = qMax((uint)8, 2*memberDataAlloc); PropertyDescriptor *newMemberData = new PropertyDescriptor[memberDataAlloc]; memcpy(newMemberData, memberData, sizeof(PropertyDescriptor)*memberDataSize); - delete memberData; + delete [] memberData; memberData = newMemberData; } e->valueIndex = memberDataSize; @@ -717,13 +718,16 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, c void Object::copyArrayData(Object *other) { - arrayLen = other->arrayLen; - arrayData = other->arrayData; - arrayFreeList = other->arrayFreeList; - if (other->sparseArray) + arrayReserve(other->arrayDataLen); + arrayDataLen = other->arrayDataLen; + memcpy(arrayData, other->arrayData, arrayDataLen*sizeof(PropertyDescriptor)); + arrayOffset = 0; + if (other->sparseArray) { sparseArray = new SparseArray(*other->sparseArray); + arrayFreeList = other->arrayFreeList; + } if (isArrayObject()) - setArrayLengthUnchecked(arrayLen); + setArrayLengthUnchecked(other->arrayLength()); } @@ -732,7 +736,7 @@ Value Object::arrayIndexOf(Value v, uint fromIndex, uint endIndex, ExecutionCont bool protoHasArray = false; Object *p = o; while ((p = p->prototype)) - if (p->arrayLength()) + if (p->arrayDataLen) protoHasArray = true; if (protoHasArray) { @@ -744,23 +748,23 @@ Value Object::arrayIndexOf(Value v, uint fromIndex, uint endIndex, ExecutionCont return Value::fromDouble(i); } } else if (sparseArray) { - for (SparseArrayNode *n = sparseArray->lowerBound(fromIndex); n && n->key() < endIndex; n = n->nextNode()) { + for (SparseArrayNode *n = sparseArray->lowerBound(fromIndex); n != sparseArray->end() && n->key() < endIndex; n = n->nextNode()) { bool exists; Value value = o->getValueChecked(ctx, arrayDecriptor(n->value), &exists); if (exists && __qmljs_strict_equal(value, v)) return Value::fromDouble(n->key()); } } else { - if ((int) endIndex > arrayData.size()) - endIndex = arrayData.size(); - PropertyDescriptor *pd = arrayData.data() + arrayOffset; + if ((int) endIndex > arrayDataLen) + endIndex = arrayDataLen; + PropertyDescriptor *pd = arrayData; PropertyDescriptor *end = pd + endIndex; pd += fromIndex; while (pd < end) { bool exists; Value value = o->getValueChecked(ctx, pd, &exists); if (exists && __qmljs_strict_equal(value, v)) - return Value::fromDouble(pd - arrayOffset - arrayData.constData()); + return Value::fromDouble(pd - arrayData); ++pd; } } @@ -769,32 +773,43 @@ Value Object::arrayIndexOf(Value v, uint fromIndex, uint endIndex, ExecutionCont void Object::arrayConcat(const ArrayObject *other) { - int newLen = arrayLen + other->arrayLength(); + int newLen = arrayDataLen + other->arrayLength(); if (other->sparseArray) initSparse(); if (sparseArray) { if (other->sparseArray) { for (const SparseArrayNode *it = other->sparseArray->begin(); it != other->sparseArray->end(); it = it->nextNode()) - arraySet(arrayLen + it->key(), other->arrayDecriptor(it->value)); + arraySet(arrayDataLen + it->key(), other->arrayDecriptor(it->value)); } else { - int oldSize = arrayData.size(); - arrayData.resize(oldSize + other->arrayLength()); - memcpy(arrayData.data() + oldSize, other->arrayData.constData() + other->arrayOffset, other->arrayLength()*sizeof(PropertyDescriptor)); + int oldSize = arrayDataLen; + arrayReserve(oldSize + other->arrayLength()); + memcpy(arrayData + oldSize, other->arrayData, other->arrayLength()*sizeof(PropertyDescriptor)); for (uint i = 0; i < other->arrayLength(); ++i) { - SparseArrayNode *n = sparseArray->insert(arrayLen + i); + SparseArrayNode *n = sparseArray->insert(arrayDataLen + i); n->value = oldSize + i; } } } else { - int oldSize = arrayData.size(); - arrayData.resize(oldSize + other->arrayLength()); - memcpy(arrayData.data() + oldSize, other->arrayData.constData() + other->arrayOffset, other->arrayLength()*sizeof(PropertyDescriptor)); + int oldSize = arrayLength(); + arrayReserve(oldSize + other->arrayDataLen); + if (oldSize > arrayDataLen) { + PropertyDescriptor generic; + generic.type = PropertyDescriptor::Generic; + generic.writable = PropertyDescriptor::Undefined; + generic.value = Value::undefinedValue(); + std::fill(arrayData + arrayDataLen, arrayData + oldSize, generic); + } + arrayDataLen = oldSize + other->arrayDataLen; + memcpy(arrayData + oldSize, other->arrayData, other->arrayDataLen*sizeof(PropertyDescriptor)); } setArrayLengthUnchecked(newLen); } void Object::arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len) { + if (!arrayDataLen) + return; + if (sparseArray) { context->throwUnimplemented("Object::sort unimplemented for sparse arrays"); return; @@ -802,9 +817,10 @@ void Object::arraySort(ExecutionContext *context, Object *thisObject, const Valu } ArrayElementLessThan lessThan(context, thisObject, comparefn); - if (len > arrayData.size() - arrayOffset) - len = arrayData.size() - arrayOffset; - PropertyDescriptor *begin = arrayData.begin() + arrayOffset; + if (len > arrayDataLen) + len = arrayDataLen; + + PropertyDescriptor *begin = arrayData; std::sort(begin, begin + len, lessThan); } @@ -813,36 +829,64 @@ void Object::initSparse() { if (!sparseArray) { sparseArray = new SparseArray; - for (int i = arrayOffset; i < arrayData.size(); ++i) { - SparseArrayNode *n = sparseArray->insert(i - arrayOffset); - n->value = i; + for (int i = 0; i < arrayDataLen; ++i) { + SparseArrayNode *n = sparseArray->insert(i); + n->value = i + arrayOffset; } - if (arrayOffset) { - int o = arrayOffset; + uint off = arrayOffset; + if (!arrayOffset) { + arrayFreeList = arrayDataLen; + } else { + arrayFreeList = 0; + arrayData -= off; + arrayAlloc += off; + int o = off; for (int i = 0; i < o - 1; ++i) { arrayData[i].type = PropertyDescriptor::Generic; arrayData[i].value = Value::fromInt32(i + 1); } arrayData[o - 1].type = PropertyDescriptor::Generic; - arrayData[o - 1].value = Value::fromInt32(arrayData.size()); - arrayFreeList = 0; - } else { - arrayFreeList = arrayData.size(); + arrayData[o - 1].value = Value::fromInt32(arrayDataLen + off); + } + for (int i = arrayDataLen + off; i < arrayAlloc; ++i) { + arrayData[i].type = PropertyDescriptor::Generic; + arrayData[i].value = Value::fromInt32(i + 1); } } } -void Object::setArrayLengthUnchecked(uint l) +void Object::arrayReserve(uint n) { - arrayLen = l; - if (isArrayObject()) { - // length is always the first property of an array - PropertyDescriptor &lengthProperty = memberData[ArrayObject::LengthPropertyIndex]; - lengthProperty.value = Value::fromUInt32(l); + if (n < 8) + n = 8; + if (n >= arrayAlloc) { + uint off; + if (sparseArray) { + assert(arrayFreeList == arrayAlloc); + // ### FIXME + arrayDataLen = arrayAlloc; + off = 0; + } else { + off = arrayOffset; + } + arrayAlloc = qMax(n, 2*arrayAlloc); + PropertyDescriptor *newArrayData = new PropertyDescriptor[arrayAlloc]; + memcpy(newArrayData, arrayData, sizeof(PropertyDescriptor)*arrayDataLen); + delete [] (arrayData - off); + arrayData = newArrayData; + if (sparseArray) { + for (uint i = arrayFreeList; i < arrayAlloc; ++i) { + arrayData[i].type = PropertyDescriptor::Generic; + arrayData[i].value = Value::fromInt32(i + 1); + } + } else { + arrayOffset = 0; + } } } + bool Object::setArrayLength(uint newLen) { assert(isArrayObject()); const PropertyDescriptor *lengthProperty = memberData + ArrayObject::LengthPropertyIndex; @@ -853,36 +897,38 @@ bool Object::setArrayLength(uint newLen) { if (newLen < oldLen) { if (sparseArray) { SparseArrayNode *begin = sparseArray->lowerBound(newLen); - SparseArrayNode *it = sparseArray->end()->previousNode(); - while (1) { - PropertyDescriptor &pd = arrayData[it->value]; - if (pd.type != PropertyDescriptor::Generic && !pd.isConfigurable()) { - ok = false; - newLen = it->key() + 1; - break; + if (begin != sparseArray->end()) { + SparseArrayNode *it = sparseArray->end()->previousNode(); + while (1) { + PropertyDescriptor &pd = arrayData[it->value]; + if (pd.type != PropertyDescriptor::Generic && !pd.isConfigurable()) { + ok = false; + newLen = it->key() + 1; + break; + } + pd.type = PropertyDescriptor::Generic; + pd.value.tag = Value::_Undefined_Type; + pd.value.int_32 = arrayFreeList; + arrayFreeList = it->value; + bool brk = (it == begin); + SparseArrayNode *prev = it->previousNode(); + sparseArray->erase(it); + if (brk) + break; + it = prev; } - pd.type = PropertyDescriptor::Generic; - pd.value.tag = Value::_Undefined_Type; - pd.value.int_32 = arrayFreeList; - arrayFreeList = it->value; - bool brk = (it == begin); - SparseArrayNode *prev = it->previousNode(); - sparseArray->erase(it); - if (brk) - break; - it = prev; } } else { - PropertyDescriptor *it = arrayData.data() + arrayData.size(); - const PropertyDescriptor *begin = arrayData.constData() + arrayOffset + newLen; + PropertyDescriptor *it = arrayData + arrayDataLen; + const PropertyDescriptor *begin = arrayData + newLen; while (--it >= begin) { if (it->type != PropertyDescriptor::Generic && !it->isConfigurable()) { ok = false; - newLen = it - arrayData.data() + arrayOffset + 1; + newLen = it - arrayData + 1; break; } } - arrayData.resize(newLen + arrayOffset); + arrayDataLen = newLen; } } else { if (newLen >= 0x100000) @@ -894,9 +940,8 @@ bool Object::setArrayLength(uint newLen) { void Object::markArrayObjects() const { - uint i = sparseArray ? 0 : arrayOffset; - for (; i < (uint)arrayData.size(); ++i) { - const PropertyDescriptor &pd = arrayData.at(i); + for (uint i = 0; i < arrayDataLen; ++i) { + const PropertyDescriptor &pd = arrayData[i]; if (pd.isData()) { if (Managed *m = pd.value.asManaged()) m->mark(); diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index d4815663cd..948948bb65 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -107,18 +107,19 @@ struct Q_V4_EXPORT Object: Managed { uint memberDataAlloc; PropertyDescriptor *memberData; - uint arrayLen; union { uint arrayFreeList; uint arrayOffset; }; - QVector arrayData; + uint arrayDataLen; + uint arrayAlloc; + PropertyDescriptor *arrayData; SparseArray *sparseArray; Object() : prototype(0) , memberDataSize(0), memberDataAlloc(0), memberData(0) - , arrayLen(0), arrayOffset(0), sparseArray(0) { type = Type_Object; } + , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), sparseArray(0) { type = Type_Object; } virtual ~Object(); @@ -167,7 +168,7 @@ struct Q_V4_EXPORT Object: Managed { // Array handling - void fillDescriptor(PropertyDescriptor *pd, Value v) + static void fillDescriptor(PropertyDescriptor *pd, Value v) { pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Enabled; @@ -178,10 +179,9 @@ struct Q_V4_EXPORT Object: Managed { uint allocArrayValue() { uint idx = arrayFreeList; - if (arrayData.size() <= (int)arrayFreeList) - arrayData.resize(++arrayFreeList); - else - arrayFreeList = arrayData.at(arrayFreeList).value.integerValue(); + if (arrayAlloc <= arrayFreeList) + arrayReserve(arrayAlloc + 1); + arrayFreeList = arrayData[arrayFreeList].value.integerValue(); return idx; } @@ -200,44 +200,42 @@ struct Q_V4_EXPORT Object: Managed { } PropertyDescriptor *arrayDecriptor(uint index) { - PropertyDescriptor *pd = arrayData.data() + index; - if (!sparseArray) - pd += arrayOffset; - return pd; + return arrayData + index; } const PropertyDescriptor *arrayDecriptor(uint index) const { - const PropertyDescriptor *pd = arrayData.data() + index; - if (!sparseArray) - pd += arrayOffset; - return pd; + return arrayData + index; } void getArrayHeadRoom() { assert(!sparseArray && !arrayOffset); - arrayOffset = qMax(arrayData.size() >> 2, 16); - QVector newValues(arrayData.size() + arrayOffset); - memcpy(newValues.data() + arrayOffset, arrayData.constData(), arrayData.size()*sizeof(PropertyDescriptor)); - arrayData = newValues; + arrayOffset = qMax(arrayDataLen >> 2, (uint)16); + PropertyDescriptor *newArray = new PropertyDescriptor[arrayOffset + arrayAlloc]; + memcpy(newArray + arrayOffset, arrayData, arrayDataLen*sizeof(PropertyDescriptor)); + delete [] arrayData; + arrayData = newArray + arrayOffset; } public: void copyArrayData(Object *other); void initSparse(); - uint arrayLength() const { return arrayLen; } + uint arrayLength() const; bool setArrayLength(uint newLen); void setArrayLengthUnchecked(uint l); PropertyDescriptor *arrayInsert(uint index) { PropertyDescriptor *pd; - if (!sparseArray && (index < 0x1000 || index < arrayLen + (arrayLen >> 2))) { - if (index + arrayOffset >= (uint)arrayData.size()) { - arrayData.resize(arrayOffset + index + 1); - for (uint i = arrayLen + 1; i < index; ++i) { + if (!sparseArray && (index < 0x1000 || index < arrayDataLen + (arrayDataLen >> 2))) { + if (index >= arrayAlloc) + arrayReserve(index + 1); + if (index >= arrayDataLen) { + for (uint i = arrayDataLen; i < index; ++i) { arrayData[i].type = PropertyDescriptor::Generic; + arrayData[i].writable = PropertyDescriptor::Undefined; arrayData[i].value.tag = Value::_Undefined_Type; } + arrayDataLen = index + 1; } pd = arrayDecriptor(index); } else { @@ -247,7 +245,7 @@ public: n->value = allocArrayValue(); pd = arrayDecriptor(n->value); } - if (index >= arrayLen) + if (index >= arrayLength()) setArrayLengthUnchecked(index + 1); return pd; } @@ -262,10 +260,10 @@ public: } bool deleteArrayIndex(uint index) { - if (index >= arrayLen) - return true; PropertyDescriptor *pd = 0; if (!sparseArray) { + if (index >= arrayDataLen) + return true; pd = arrayAt(index); } else { SparseArrayNode *n = sparseArray->findNode(index); @@ -277,80 +275,63 @@ public: if (!pd->isConfigurable()) return false; pd->type = PropertyDescriptor::Generic; - pd->value.tag = Value::_Undefined_Type; + pd->value = Value::undefinedValue(); if (sparseArray) { pd->value.int_32 = arrayFreeList; - arrayFreeList = pd - arrayData.constData(); + arrayFreeList = pd - arrayData; } return true; } PropertyDescriptor *arrayAt(uint index) { if (!sparseArray) { - if (index >= arrayData.size() - arrayOffset) + if (index >= arrayDataLen) return 0; - return arrayData.data() + index + arrayOffset; + return arrayData + index; } else { SparseArrayNode *n = sparseArray->findNode(index); if (!n) return 0; - return arrayData.data() + n->value; + return arrayData + n->value; } } const PropertyDescriptor *nonSparseArrayAt(uint index) const { if (sparseArray) return 0; - index += arrayOffset; - if (index >= (uint)arrayData.size()) + if (index >= arrayDataLen) return 0; - return arrayData.constData() + index; + return arrayData + index; } PropertyDescriptor *nonSparseArrayAtRef(uint index) { if (sparseArray) return 0; - index += arrayOffset; - if (index >= (uint)arrayData.size()) + if (index >= arrayDataLen) return 0; - return arrayData.data() + index; + return arrayData + index; } const PropertyDescriptor *arrayAt(uint index) const { if (!sparseArray) { - if (index >= arrayData.size() - arrayOffset) + if (index >= arrayDataLen) return 0; - return arrayData.constData() + index + arrayOffset; + return arrayData + index; } else { SparseArrayNode *n = sparseArray->findNode(index); if (!n) return 0; - return arrayData.constData() + n->value; + return arrayData + n->value; } } void markArrayObjects() const; - void push_front(Value v) { - if (!sparseArray) { - if (!arrayOffset) - getArrayHeadRoom(); - - PropertyDescriptor pd; - fillDescriptor(&pd, v); - --arrayOffset; - arrayData[arrayOffset] = pd; - } else { - uint idx = allocArrayValue(v); - sparseArray->push_front(idx); - } - setArrayLengthUnchecked(arrayLen + 1); - } PropertyDescriptor *front() { PropertyDescriptor *pd = 0; if (!sparseArray) { - if (arrayLen) - pd = arrayData.data() + arrayOffset; + if (arrayDataLen) + pd = arrayData; } else { SparseArrayNode *n = sparseArray->findNode(0); if (n) @@ -360,63 +341,28 @@ public: return 0; return pd; } - void pop_front() { - if (!arrayLen) - return; - if (!sparseArray) { - ++arrayOffset; - } else { - uint idx = sparseArray->pop_front(); - freeArrayValue(idx); - } - setArrayLengthUnchecked(arrayLen - 1); - } void push_back(Value v) { + uint idx = arrayLength(); if (!sparseArray) { - PropertyDescriptor pd; - fillDescriptor(&pd, v); - arrayData.append(pd); + if (idx >= arrayAlloc) + arrayReserve(idx + 1); + fillDescriptor(arrayData + idx, v); + arrayDataLen = idx + 1; } else { uint idx = allocArrayValue(v); - sparseArray->push_back(idx, arrayLen); + sparseArray->push_back(idx, arrayLength()); } - setArrayLengthUnchecked(arrayLen + 1); - } - PropertyDescriptor *back() { - PropertyDescriptor *pd = 0; - if (!sparseArray) { - if (arrayLen) - pd = arrayData.data() + arrayOffset + arrayLen; - } else { - SparseArrayNode *n = sparseArray->findNode(arrayLen - 1); - if (n) - pd = arrayDecriptor(n->value); - } - if (pd && pd->type == PropertyDescriptor::Generic) - return 0; - return pd; - } - void pop_back() { - if (!arrayLen) - return; - if (!sparseArray) { - arrayData.resize(arrayData.size() - 1); - } else { - uint idx = sparseArray->pop_back(arrayLen); - if (idx != UINT_MAX) - freeArrayValue(idx); - } - setArrayLengthUnchecked(arrayLen - 1); + setArrayLengthUnchecked(idx + 1); } - SparseArrayNode *sparseArrayLowerBound(uint idx) { return sparseArray ? sparseArray->lowerBound(idx) : 0; } SparseArrayNode *sparseArrayBegin() { return sparseArray ? sparseArray->begin() : 0; } SparseArrayNode *sparseArrayEnd() { return sparseArray ? sparseArray->end() : 0; } void arrayConcat(const ArrayObject *other); - void arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint arrayLen); - Value arrayIndexOf(Value v, uint fromIndex, uint arrayLen, ExecutionContext *ctx, Object *o); + void arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint arrayDataLen); + Value arrayIndexOf(Value v, uint fromIndex, uint arrayDataLen, ExecutionContext *ctx, Object *o); + void arrayReserve(uint n); protected: virtual void markObjects(); @@ -452,14 +398,30 @@ struct ArrayObject: Object { }; ArrayObject(ExecutionContext *ctx) { init(ctx); } - ArrayObject(ExecutionContext *ctx, const QVector &value): Object() { - init(ctx); - arrayData = value; - setArrayLengthUnchecked(value.size()); - } void init(ExecutionContext *context); }; +inline uint Object::arrayLength() const +{ + if (isArrayObject()) { + // length is always the first property of an array + Value v = memberData[ArrayObject::LengthPropertyIndex].value; + if (v.isInteger()) + return v.integerValue(); + return Value::toUInt32(v.doubleValue()); + } + return 0; +} + +inline void Object::setArrayLengthUnchecked(uint l) +{ + if (isArrayObject()) { + // length is always the first property of an array + PropertyDescriptor &lengthProperty = memberData[ArrayObject::LengthPropertyIndex]; + lengthProperty.value = Value::fromUInt32(l); + } +} + } // namespace VM } // namespace QQmlJS diff --git a/src/v4/qv4objectiterator.cpp b/src/v4/qv4objectiterator.cpp index 04f11c6d20..7bcf87245a 100644 --- a/src/v4/qv4objectiterator.cpp +++ b/src/v4/qv4objectiterator.cpp @@ -101,7 +101,7 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) arrayIndex = UINT_MAX; } // dense arrays - while (arrayIndex < current->arrayLength()) { + while (arrayIndex < current->arrayDataLen) { p = current->arrayAt(arrayIndex); ++arrayIndex; if (p && p->type != PropertyDescriptor::Generic && (!(flags & EnumberableOnly) || p->isEnumerable())) { -- cgit v1.2.3 From b34cd3ad04f8e98bf787f186d258b9a071e5aafb Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 5 Feb 2013 16:15:38 +0100 Subject: Remove some duplicated code Change-Id: I02857ad6ab74bb70aebe1b6b74c21171e8d87d03 Reviewed-by: Erik Verbruggen --- src/v4/qmljs_runtime.cpp | 2 +- src/v4/qv4object.h | 25 ++----------------------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index d01d76d462..e192b40c99 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -616,7 +616,7 @@ void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value uint idx = index.asArrayIndex(); if (idx < UINT_MAX) { - PropertyDescriptor *p = o->nonSparseArrayAtRef(idx); + PropertyDescriptor *p = o->nonSparseArrayAt(idx); if (p && p->type == PropertyDescriptor::Data && p->isWritable()) { p->value = value; return; diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index 948948bb65..c9a4337e79 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -283,7 +283,7 @@ public: return true; } - PropertyDescriptor *arrayAt(uint index) { + PropertyDescriptor *arrayAt(uint index) const { if (!sparseArray) { if (index >= arrayDataLen) return 0; @@ -296,7 +296,7 @@ public: } } - const PropertyDescriptor *nonSparseArrayAt(uint index) const { + PropertyDescriptor *nonSparseArrayAt(uint index) const { if (sparseArray) return 0; if (index >= arrayDataLen) @@ -304,27 +304,6 @@ public: return arrayData + index; } - PropertyDescriptor *nonSparseArrayAtRef(uint index) { - if (sparseArray) - return 0; - if (index >= arrayDataLen) - return 0; - return arrayData + index; - } - - const PropertyDescriptor *arrayAt(uint index) const { - if (!sparseArray) { - if (index >= arrayDataLen) - return 0; - return arrayData + index; - } else { - SparseArrayNode *n = sparseArray->findNode(index); - if (!n) - return 0; - return arrayData + n->value; - } - } - void markArrayObjects() const; PropertyDescriptor *front() { -- cgit v1.2.3 From 02b478edb3476c607002a64e2a57b7e202fcd9f6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 5 Feb 2013 22:13:27 +0100 Subject: Inline some code only used from one place Cleans up the qv4object class definition. Change-Id: Ifca7f89afb3affce5d940b1b4f7dcfe71779012c Reviewed-by: Erik Verbruggen --- src/v4/qv4arrayobject.cpp | 14 +++++++++++++- src/v4/qv4object.cpp | 23 ++++++++++++++++++++++- src/v4/qv4object.h | 38 -------------------------------------- 3 files changed, 35 insertions(+), 40 deletions(-) diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp index bec7c2283a..3079c52361 100644 --- a/src/v4/qv4arrayobject.cpp +++ b/src/v4/qv4arrayobject.cpp @@ -322,7 +322,19 @@ Value ArrayPrototype::method_shift(ExecutionContext *ctx) return Value::undefinedValue(); } - Value result = instance->getValueChecked(ctx, instance->front()); + PropertyDescriptor *front = 0; + if (!instance->sparseArray) { + if (instance->arrayDataLen) + front = instance->arrayData; + } else { + SparseArrayNode *n = instance->sparseArray->findNode(0); + if (n) + front = instance->arrayDecriptor(n->value); + } + if (front && front->type == PropertyDescriptor::Generic) + front = 0; + + Value result = instance->getValueChecked(ctx, front); bool protoHasArray = false; Object *p = instance; diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index dda340b3b0..d8231b5b32 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -551,8 +551,29 @@ bool Object::__delete__(ExecutionContext *ctx, String *name) bool Object::__delete__(ExecutionContext *ctx, uint index) { - if (deleteArrayIndex(index)) + PropertyDescriptor *pd = 0; + if (!sparseArray) { + if (index >= arrayDataLen) + return true; + pd = arrayAt(index); + } else { + SparseArrayNode *n = sparseArray->findNode(index); + if (n) + pd = arrayDecriptor(n->value); + } + if (!pd || pd->type == PropertyDescriptor::Generic) return true; + + if (pd->isConfigurable()) { + pd->type = PropertyDescriptor::Generic; + pd->value = Value::undefinedValue(); + if (sparseArray) { + pd->value.int_32 = arrayFreeList; + arrayFreeList = pd - arrayData; + } + return true; + } + if (ctx->strictMode) __qmljs_throw_type_error(ctx); return false; diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index c9a4337e79..7f99668fa9 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -259,30 +259,6 @@ public: fillDescriptor(pd, value); } - bool deleteArrayIndex(uint index) { - PropertyDescriptor *pd = 0; - if (!sparseArray) { - if (index >= arrayDataLen) - return true; - pd = arrayAt(index); - } else { - SparseArrayNode *n = sparseArray->findNode(index); - if (n) - pd = arrayDecriptor(n->value); - } - if (!pd || pd->type == PropertyDescriptor::Generic) - return true; - if (!pd->isConfigurable()) - return false; - pd->type = PropertyDescriptor::Generic; - pd->value = Value::undefinedValue(); - if (sparseArray) { - pd->value.int_32 = arrayFreeList; - arrayFreeList = pd - arrayData; - } - return true; - } - PropertyDescriptor *arrayAt(uint index) const { if (!sparseArray) { if (index >= arrayDataLen) @@ -306,20 +282,6 @@ public: void markArrayObjects() const; - PropertyDescriptor *front() { - PropertyDescriptor *pd = 0; - if (!sparseArray) { - if (arrayDataLen) - pd = arrayData; - } else { - SparseArrayNode *n = sparseArray->findNode(0); - if (n) - pd = arrayDecriptor(n->value); - } - if (pd && pd->type == PropertyDescriptor::Generic) - return 0; - return pd; - } void push_back(Value v) { uint idx = arrayLength(); if (!sparseArray) { -- cgit v1.2.3 From b1308a37eaa3bc2e3e28642881220040f9f3eae2 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 5 Feb 2013 22:14:03 +0100 Subject: Don't insert undefined elements into the sparse array When converting to a sparse array, skip the missing elements. Change-Id: Ic5ae7973c200d530ad4f89aa23b46a2581198b5a Reviewed-by: Erik Verbruggen --- src/v4/qv4object.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index d8231b5b32..5e57c89d58 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -851,8 +851,10 @@ void Object::initSparse() if (!sparseArray) { sparseArray = new SparseArray; for (int i = 0; i < arrayDataLen; ++i) { - SparseArrayNode *n = sparseArray->insert(i); - n->value = i + arrayOffset; + if (arrayData[i].type != PropertyDescriptor::Generic) { + SparseArrayNode *n = sparseArray->insert(i); + n->value = i + arrayOffset; + } } uint off = arrayOffset; -- cgit v1.2.3 From 57d787747c8c169491b4bde2bf3d50e2c98f4035 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 6 Feb 2013 13:15:10 +0100 Subject: Restore the context pointer after returning from setjmp Change-Id: I68682400c80cf9a8f28d11d8031681d6d92f590b Reviewed-by: Simon Hausmann --- src/v4/moth/qv4isel_moth.cpp | 2 +- src/v4/moth/qv4isel_moth_p.h | 2 +- src/v4/moth/qv4vme_moth.cpp | 2 ++ src/v4/qv4codegen.cpp | 5 ++++- src/v4/qv4isel_llvm.cpp | 2 +- src/v4/qv4isel_llvm_p.h | 2 +- src/v4/qv4isel_masm.cpp | 5 ++++- src/v4/qv4isel_masm_p.h | 2 +- src/v4/qv4isel_p.cpp | 6 ++++-- src/v4/qv4isel_p.h | 2 +- 10 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index f31153a3d2..2f0a614b6c 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -830,7 +830,7 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) addInstruction(call); } -void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *) { Instruction::CallBuiltinCreateExceptionHandler call; call.result = getResultParam(result); diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h index 0b93ea853f..7d57897c68 100644 --- a/src/v4/moth/qv4isel_moth_p.h +++ b/src/v4/moth/qv4isel_moth_p.h @@ -43,7 +43,7 @@ protected: virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp); virtual void callBuiltinDeleteExceptionHandler(); virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index e9889fca40..1cfaf6435b 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -256,7 +256,9 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co , stackSize #endif ); + VM::ExecutionContext *oldContext = context; int didThrow = setjmp(* static_cast(buf)); + context = oldContext; // Two ways to come here: after a create, or after a throw. if (didThrow) // At this point, the interpreter state can be anything but diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index 241b3e8329..0b62256dda 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -2386,7 +2386,10 @@ bool Codegen::visit(TryStatement *ast) } int hasException = _block->newTemp(); - move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_create_exception_handler, 0, 0), 0)); + int contextTemp = _block->newTemp(); + IR::ExprList *createExceptionArgs = _function->New(); + createExceptionArgs->init(_block->TEMP(contextTemp)); + move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_create_exception_handler, 0, 0), createExceptionArgs)); // Pass the hidden "inCatch" and "hasException" TEMPs to the // builtin_delete_exception_handler, in order to have those TEMPs alive for diff --git a/src/v4/qv4isel_llvm.cpp b/src/v4/qv4isel_llvm.cpp index 091936813d..1e98c029a7 100644 --- a/src/v4/qv4isel_llvm.cpp +++ b/src/v4/qv4isel_llvm.cpp @@ -427,7 +427,7 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) Q_UNREACHABLE(); } -void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp) { // TODO assert(!"TODO!"); diff --git a/src/v4/qv4isel_llvm_p.h b/src/v4/qv4isel_llvm_p.h index 46fd2fefa4..ec5ace3df2 100644 --- a/src/v4/qv4isel_llvm_p.h +++ b/src/v4/qv4isel_llvm_p.h @@ -88,7 +88,7 @@ public: // methods from InstructionSelection: virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp); virtual void callBuiltinDeleteExceptionHandler(); virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index c02f565733..bde199d9be 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -523,10 +523,13 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, arg, Assembler::ContextRegister); } -void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp) { + Address contextAddr = _asm->loadTempAddress(Assembler::ScratchRegister, contextTemp); + _asm->storePtr(Assembler::ContextRegister, contextAddr); generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_create_exception_handler, Assembler::ContextRegister); generateFunctionCall(Assembler::ReturnValueRegister, setjmp, Assembler::ReturnValueRegister); + _asm->loadPtr(contextAddr, Assembler::ContextRegister); Address addr = _asm->loadTempAddress(Assembler::ScratchRegister, result); _asm->store32(Assembler::ReturnValueRegister, addr); addr.offset += 4; diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index de0971f0cb..c5cf2d881e 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -693,7 +693,7 @@ protected: virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp); virtual void callBuiltinDeleteExceptionHandler(); virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index 3e3b42cecc..05e7739e00 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -307,9 +307,11 @@ void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result) callBuiltinThrow(arg); } return; - case IR::Name::builtin_create_exception_handler: - callBuiltinCreateExceptionHandler(result); + case IR::Name::builtin_create_exception_handler: { + IR::Temp *arg = call->args->expr->asTemp(); + callBuiltinCreateExceptionHandler(result, arg); return; + } case IR::Name::builtin_delete_exception_handler: callBuiltinDeleteExceptionHandler(); diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h index d4f7052373..d097f91527 100644 --- a/src/v4/qv4isel_p.h +++ b/src/v4/qv4isel_p.h @@ -99,7 +99,7 @@ public: // to implement by subclasses: virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result) = 0; virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) = 0; virtual void callBuiltinThrow(IR::Temp *arg) = 0; - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result) = 0; + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp) = 0; virtual void callBuiltinDeleteExceptionHandler() = 0; virtual void callBuiltinGetException(IR::Temp *result) = 0; virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) = 0; -- cgit v1.2.3 From 0b34144b3f07404b49d1acab734ccbef98da27a3 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 6 Feb 2013 15:41:00 +0100 Subject: Include stdlib for arc4random(). Change-Id: Icf7f09d2007941cd1005e28eaf189d1265c71900 Reviewed-by: Lars Knoll --- src/3rdparty/masm/wtf/OSAllocatorPosix.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp index b5b903b8a3..27ef02805d 100644 --- a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp +++ b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp @@ -32,6 +32,10 @@ #include #include +#if (OS(DARWIN) && CPU(X86_64)) +#include +#endif + namespace WTF { void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) -- cgit v1.2.3 From 8f9ef6af133262e592db6e9374fffc86c6a88b0c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 6 Feb 2013 09:05:47 +0100 Subject: Also mark the last item in the last chunk Change-Id: I9af2232a987c1297cba08579e944b1c30d032088 Reviewed-by: Erik Verbruggen --- src/v4/qv4mm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qv4mm.cpp b/src/v4/qv4mm.cpp index 7e60e001fd..81d775fd5e 100644 --- a/src/v4/qv4mm.cpp +++ b/src/v4/qv4mm.cpp @@ -370,7 +370,7 @@ void MemoryManager::collectFromStack() const reinterpret_cast(*current); #endif - if (genericPtr < *heapChunkBoundaries || genericPtr > *(heapChunkBoundariesEnd - 1)) + if (genericPtr < *heapChunkBoundaries || genericPtr > *heapChunkBoundariesEnd) continue; int index = qLowerBound(heapChunkBoundaries, heapChunkBoundariesEnd, genericPtr) - heapChunkBoundaries; // An odd index means the pointer is _before_ the end of a heap chunk and therefore valid. -- cgit v1.2.3 From cb4920152ea621e9b82a353c1ec9df7d5a23a099 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:13:41 +0100 Subject: Fix objects dir for debug and release builds Don't define OBJECTS_DIR, the qmake bits for Qt 5 modules takes care of setting it to .obj/debug_shared and .obj/debug_release accordingly for example. Hardcoding it just breaks that. Change-Id: I1023de159c459194eba74134626892563d069a22 Reviewed-by: Lars Knoll --- src/v4/v4.pro | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/v4/v4.pro b/src/v4/v4.pro index 60338cc417..b122247391 100644 --- a/src/v4/v4.pro +++ b/src/v4/v4.pro @@ -6,8 +6,6 @@ CONFIG += internal_module include(v4.pri) -OBJECTS_DIR=.obj - load(qt_build_config) load(qt_module) -- cgit v1.2.3 From 361c5ea65caefa44582a80aebff2a2b207d3d0f6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:14:44 +0100 Subject: Fix exports in v4 binary Don't use the export macro on internal debug classes. Change-Id: Id2f8069cc8b6703a3fafd3058524d46252eb57f8 Reviewed-by: Lars Knoll --- tools/v4/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/v4/main.cpp b/tools/v4/main.cpp index 0d215b9e5e..25ebc5c52c 100644 --- a/tools/v4/main.cpp +++ b/tools/v4/main.cpp @@ -72,7 +72,7 @@ namespace builtins { using namespace QQmlJS::VM; -struct Q_V4_EXPORT Print: FunctionObject +struct Print: FunctionObject { Print(ExecutionContext *scope): FunctionObject(scope) { name = scope->engine->newString("print"); @@ -91,7 +91,7 @@ struct Q_V4_EXPORT Print: FunctionObject } }; -struct Q_V4_EXPORT TestHarnessError: FunctionObject +struct TestHarnessError: FunctionObject { TestHarnessError(ExecutionContext *scope, bool &errorInTestHarness): FunctionObject(scope), errorOccurred(errorInTestHarness) { name = scope->engine->newString("$ERROR"); @@ -114,7 +114,7 @@ struct Q_V4_EXPORT TestHarnessError: FunctionObject bool &errorOccurred; }; -struct Q_V4_EXPORT GC: public FunctionObject +struct GC: public FunctionObject { GC(ExecutionContext* scope) : FunctionObject(scope) -- cgit v1.2.3 From 8dc79abcf519c8b30c19dd65fcb7ee3e007f3f20 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:15:26 +0100 Subject: Don't do -rdynamic on Windows It doesn't make sense :) Change-Id: I7455cd157e1d5938317c7e488701bbb1651eb449 Reviewed-by: Lars Knoll --- src/v4/v4.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/v4.pro b/src/v4/v4.pro index b122247391..79b331683a 100644 --- a/src/v4/v4.pro +++ b/src/v4/v4.pro @@ -13,7 +13,7 @@ CONFIG += warn_off #win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x66000000 #TODO ??! -!macx-clang*:LIBS += -rdynamic +!macx-clang*:!win*:LIBS += -rdynamic SOURCES += \ qv4codegen.cpp \ -- cgit v1.2.3 From 1404a36f104e3a862d8eb969313a720867e37243 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:16:38 +0100 Subject: Work around broken min/max macro definitions on Windows Windows defines min and max to macros, which breaks code that for example uses std::max. Defining NOMINMAX globally prevents WinDefs.h from defining those macros. Change-Id: Ib79a48f9139febd1429a11753ffef4430953a3b4 Reviewed-by: Lars Knoll --- src/3rdparty/masm/masm-defs.pri | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/3rdparty/masm/masm-defs.pri b/src/3rdparty/masm/masm-defs.pri index 34b86e30e4..5744905f93 100644 --- a/src/3rdparty/masm/masm-defs.pri +++ b/src/3rdparty/masm/masm-defs.pri @@ -1,6 +1,8 @@ DEFINES += WTF_EXPORT_PRIVATE="" JS_EXPORT_PRIVATE="" +win*: DEFINES += NOMINMAX + DEFINES += ENABLE_LLINT=0 DEFINES += ENABLE_DFG_JIT=0 DEFINES += ENABLE_JIT=1 -- cgit v1.2.3 From d57bc1cf06965faf9e545048e6c996e8059c9d10 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:20:56 +0100 Subject: Make it possible to omit the udis86 assembler from compilation Disable it on Windows and enable it generally only on x86 and amd64 architectures. Change-Id: If8f366a3095608b9afcd30dee6dc636d442d4107 Reviewed-by: Lars Knoll --- src/3rdparty/masm/masm-defs.pri | 4 +++- src/3rdparty/masm/masm.pri | 16 +++++++++------- src/v4/qv4isel_masm.cpp | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/3rdparty/masm/masm-defs.pri b/src/3rdparty/masm/masm-defs.pri index 5744905f93..f8055d0ff4 100644 --- a/src/3rdparty/masm/masm-defs.pri +++ b/src/3rdparty/masm/masm-defs.pri @@ -19,7 +19,9 @@ INCLUDEPATH += $$PWD/stubs INCLUDEPATH += $$PWD/stubs/wtf INCLUDEPATH += $$PWD -DEFINES += WTF_USE_UDIS86=1 +if(isEqual(QT_ARCH, "i386")|isEqual(QT_ARCH, "x86_64")):!win*: DEFINES += WTF_USE_UDIS86=1 +else: DEFINES += WTF_USE_UDIS86=0 + INCLUDEPATH += $$PWD/disassembler INCLUDEPATH += $$PWD/disassembler/udis86 INCLUDEPATH += $$_OUT_PWD diff --git a/src/3rdparty/masm/masm.pri b/src/3rdparty/masm/masm.pri index f172762089..87f08e482f 100644 --- a/src/3rdparty/masm/masm.pri +++ b/src/3rdparty/masm/masm.pri @@ -32,13 +32,15 @@ HEADERS += $$PWD/stubs/WTFStubs.h SOURCES += $$PWD/disassembler/Disassembler.cpp SOURCES += $$PWD/disassembler/UDis86Disassembler.cpp -SOURCES += $$PWD/disassembler/udis86/udis86.c -SOURCES += $$PWD/disassembler/udis86/udis86_decode.c -SOURCES += $$PWD/disassembler/udis86/udis86_input.c -SOURCES += $$PWD/disassembler/udis86/udis86_itab_holder.c -SOURCES += $$PWD/disassembler/udis86/udis86_syn-att.c -SOURCES += $$PWD/disassembler/udis86/udis86_syn.c -SOURCES += $$PWD/disassembler/udis86/udis86_syn-intel.c +contains(DEFINES, WTF_USE_UDIS86=1) { + SOURCES += $$PWD/disassembler/udis86/udis86.c + SOURCES += $$PWD/disassembler/udis86/udis86_decode.c + SOURCES += $$PWD/disassembler/udis86/udis86_input.c + SOURCES += $$PWD/disassembler/udis86/udis86_itab_holder.c + SOURCES += $$PWD/disassembler/udis86/udis86_syn-att.c + SOURCES += $$PWD/disassembler/udis86/udis86_syn.c + SOURCES += $$PWD/disassembler/udis86/udis86_syn-intel.c +} SOURCES += \ $$PWD/yarr/YarrCanonicalizeUCS2.cpp \ diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index bde199d9be..4fdee3fddd 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -52,7 +52,7 @@ #include #include -#ifndef NO_UDIS86 +#if USE(UDIS86) # include #endif -- cgit v1.2.3 From be59020ff931d60cdf18bc32c3dc7bc498510d7b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:23:46 +0100 Subject: Fix build of time related code on Windows Use Q_OS_WIN instead of the non-existent Q_WS_WIN Change-Id: Ia64908dfdb234bce5f0a2256bb292d91da2fcb68 Reviewed-by: Lars Knoll --- src/v4/qv4dateobject.cpp | 8 ++++---- src/v4/qv4errorobject.cpp | 2 +- src/v4/qv4objectproto.cpp | 2 +- src/v4/qv4stringobject.cpp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/v4/qv4dateobject.cpp b/src/v4/qv4dateobject.cpp index 737a583e1f..c808c78d85 100644 --- a/src/v4/qv4dateobject.cpp +++ b/src/v4/qv4dateobject.cpp @@ -61,7 +61,7 @@ #include #include -#ifndef Q_WS_WIN +#ifndef Q_OS_WIN # include # ifndef Q_OS_VXWORKS # include @@ -294,7 +294,7 @@ static inline double MakeDate(double day, double time) static inline double DaylightSavingTA(double t) { -#ifndef Q_WS_WIN +#ifndef Q_OS_WIN long int tt = (long int)(t / msPerSecond); struct tm tmtm; if (!localtime_r((const time_t*)&tt, &tmtm)) @@ -319,7 +319,7 @@ static inline double UTC(double t) static inline double currentTime() { -#ifndef Q_WS_WIN +#ifndef Q_OS_WIN struct timeval tv; gettimeofday(&tv, 0); @@ -640,7 +640,7 @@ static inline QString ToLocaleTimeString(double t) static double getLocalTZA() { -#ifndef Q_WS_WIN +#ifndef Q_OS_WIN struct tm t; time_t curr; time(&curr); diff --git a/src/v4/qv4errorobject.cpp b/src/v4/qv4errorobject.cpp index fd03f1f243..f381a787ad 100644 --- a/src/v4/qv4errorobject.cpp +++ b/src/v4/qv4errorobject.cpp @@ -60,7 +60,7 @@ #include #include -#ifndef Q_WS_WIN +#ifndef Q_OS_WIN # include # ifndef Q_OS_VXWORKS # include diff --git a/src/v4/qv4objectproto.cpp b/src/v4/qv4objectproto.cpp index 6b7bebc60f..09ffb6a202 100644 --- a/src/v4/qv4objectproto.cpp +++ b/src/v4/qv4objectproto.cpp @@ -57,7 +57,7 @@ #include #include -#ifndef Q_WS_WIN +#ifndef Q_OS_WIN # include # ifndef Q_OS_VXWORKS # include diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp index 87506ecf36..a070cc0ccb 100644 --- a/src/v4/qv4stringobject.cpp +++ b/src/v4/qv4stringobject.cpp @@ -62,7 +62,7 @@ #include #include -#ifndef Q_WS_WIN +#ifndef Q_OS_WIN # include # ifndef Q_OS_VXWORKS # include -- cgit v1.2.3 From f003a89a93ae51190ed137cac4f126cee4031138 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:28:17 +0100 Subject: Fix build with non-gcc compilers Properly protect the inline assembler integer math with preprocessor macros. Enable only with gcc and on x86 and amd64. Change-Id: I4a422addc1f3aba0f10d5541357d940bb4c29060 Reviewed-by: Lars Knoll --- src/v4/qmljs_math.h | 8 ++++++-- src/v4/qmljs_runtime.h | 12 ++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/v4/qmljs_math.h b/src/v4/qmljs_math.h index b14152db31..cc37878e5a 100644 --- a/src/v4/qmljs_math.h +++ b/src/v4/qmljs_math.h @@ -46,7 +46,11 @@ #endif // QMLJS_LLVM_RUNTIME #include -#if !defined(QMLJS_LLVM_RUNTIME) && 1 //CPU(X86_64) +#if !defined(QMLJS_LLVM_RUNTIME) && COMPILER(GCC) && (CPU(X86_64) || CPU(X86)) +#define QMLJS_INLINE_MATH +#endif + +#if defined(QMLJS_INLINE_MATH) QT_BEGIN_NAMESPACE @@ -106,5 +110,5 @@ static inline Value mul_int32(int a, int b) QT_END_NAMESPACE -#endif // !defined(QMLJS_LLVM_RUNTIME) && 1 //CPU(X86_64) +#endif // defined(QMLJS_INLINE_MATH) #endif // QMLJS_MATH_H diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index b93aa0c96e..7b0a44af57 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -551,10 +551,10 @@ inline Value __qmljs_add(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); -#ifndef QMLJS_LLVM_RUNTIME +#ifdef QMLJS_INLINE_MATH if (Value::integerCompatible(left, right)) return add_int32(left.integerValue(), right.integerValue()); -#endif // QMLJS_LLVM_RUNTIME +#endif if (Value::bothDouble(left, right)) return Value::fromDouble(left.doubleValue() + right.doubleValue()); @@ -566,10 +566,10 @@ inline Value __qmljs_sub(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); -#ifndef QMLJS_LLVM_RUNTIME +#ifdef QMLJS_INLINE_MATH if (Value::integerCompatible(left, right)) return sub_int32(left.integerValue(), right.integerValue()); -#endif // QMLJS_LLVM_RUNTIME +#endif double lval = __qmljs_to_number(left, ctx); double rval = __qmljs_to_number(right, ctx); @@ -580,10 +580,10 @@ inline Value __qmljs_mul(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); -#ifndef QMLJS_LLVM_RUNTIME +#ifdef QMLJS_INLINE_MATH if (Value::integerCompatible(left, right)) return mul_int32(left.integerValue(), right.integerValue()); -#endif // QMLJS_LLVM_RUNTIME +#endif double lval = __qmljs_to_number(left, ctx); double rval = __qmljs_to_number(right, ctx); -- cgit v1.2.3 From 11bbb8689b259d55aa85996b778d5c27a5327c17 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:29:48 +0100 Subject: Remove unnecessary sys/mman.h inclusion Change-Id: Ic4bbc6a595849042a9c970ba2d0ad5785fe1b89a Reviewed-by: Lars Knoll --- src/v4/qv4isel_masm.cpp | 1 - tools/v4/main.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 4fdee3fddd..e1da6ebd73 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -48,7 +48,6 @@ #include #include -#include #include #include diff --git a/tools/v4/main.cpp b/tools/v4/main.cpp index 25ebc5c52c..2516386eef 100644 --- a/tools/v4/main.cpp +++ b/tools/v4/main.cpp @@ -65,7 +65,6 @@ #include #include -#include #include namespace builtins { -- cgit v1.2.3 From 2b10ebad76f6a04c9fb8511accbfd19853d0b1f1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:31:56 +0100 Subject: Implement thread stack base determination with the MSVC + x86 combination Change-Id: Idfb4961cc15f9740d3f3bdd126424d3fa7b3e454 Reviewed-by: Lars Knoll --- src/v4/qv4mm.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/v4/qv4mm.cpp b/src/v4/qv4mm.cpp index 81d775fd5e..f29381be33 100644 --- a/src/v4/qv4mm.cpp +++ b/src/v4/qv4mm.cpp @@ -347,6 +347,15 @@ void MemoryManager::collectFromStack() const quintptr *top = static_cast(stackBottom) + stackSize/sizeof(quintptr); #endif +#elif OS(WINDOWS) +#if COMPILER(MSVC) + NT_TIB *tib; + __asm { + mov eax, fs:[0x18] + mov [tib], eax + } +#endif + quintptr *top = static_cast(tib->StackBase); #endif // qDebug() << "stack:" << hex << stackTop << stackSize << (stackTop + stackSize); -- cgit v1.2.3 From 63ecc23e6fae7d0bda6e626a7c6a8522c4aba98d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:33:57 +0100 Subject: Fix conflict of member variable with compiler intrinsic Rename the _asm variable to _as, to avoid a conflict with _asm with MSVC. Change-Id: Id8e2ff965c5e26e3d80b1656020d1889b1eb291b Reviewed-by: Lars Knoll --- src/v4/qv4isel_masm.cpp | 102 ++++++++++++++++++++++++------------------------ src/v4/qv4isel_masm_p.h | 6 +-- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index e1da6ebd73..7c9ab8737a 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -370,25 +370,25 @@ InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Modu , _block(0) , _function(0) , _vmFunction(0) - , _asm(0) + , _as(0) { } InstructionSelection::~InstructionSelection() { - delete _asm; + delete _as; } void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) { qSwap(_function, function); qSwap(_vmFunction, vmFunction); - Assembler* oldAssembler = _asm; - _asm = new Assembler(_function); + Assembler* oldAssembler = _as; + _as = new Assembler(_function); int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) + 1; locals = (locals + 1) & ~1; - _asm->enterStandardStackFrame(locals); + _as->enterStandardStackFrame(locals); int contextPointer = 0; #ifndef VALUE_FITS_IN_REGISTER @@ -398,38 +398,38 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) contextPointer++; #endif #if CPU(X86) - _asm->loadPtr(addressForArgument(contextPointer), Assembler::ContextRegister); + _as->loadPtr(addressForArgument(contextPointer), Assembler::ContextRegister); #elif CPU(X86_64) || CPU(ARM) - _asm->move(_asm->registerForArgument(contextPointer), Assembler::ContextRegister); + _as->move(_as->registerForArgument(contextPointer), Assembler::ContextRegister); #else assert(!"TODO"); #endif foreach (IR::BasicBlock *block, _function->basicBlocks) { _block = block; - _asm->registerBlock(_block); + _as->registerBlock(_block); foreach (IR::Stmt *s, block->statements) { s->accept(this); } } - _asm->leaveStandardStackFrame(locals); + _as->leaveStandardStackFrame(locals); #ifndef VALUE_FITS_IN_REGISTER // Emulate ret(n) instruction // Pop off return address into scratch register ... - _asm->pop(Assembler::ScratchRegister); + _as->pop(Assembler::ScratchRegister); // ... and overwrite the invisible argument with // the return address. - _asm->poke(Assembler::ScratchRegister); + _as->poke(Assembler::ScratchRegister); #endif - _asm->ret(); + _as->ret(); - _asm->link(_vmFunction); + _as->link(_vmFunction); qSwap(_vmFunction, vmFunction); qSwap(_function, function); - delete _asm; - _asm = oldAssembler; + delete _as; + _as = oldAssembler; } void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) @@ -474,7 +474,7 @@ void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp * void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) { - _asm->storeValue(Value::fromBoolean(false), result); + _as->storeValue(Value::fromBoolean(false), result); } void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) @@ -524,15 +524,15 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp) { - Address contextAddr = _asm->loadTempAddress(Assembler::ScratchRegister, contextTemp); - _asm->storePtr(Assembler::ContextRegister, contextAddr); + Address contextAddr = _as->loadTempAddress(Assembler::ScratchRegister, contextTemp); + _as->storePtr(Assembler::ContextRegister, contextAddr); generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_create_exception_handler, Assembler::ContextRegister); generateFunctionCall(Assembler::ReturnValueRegister, setjmp, Assembler::ReturnValueRegister); - _asm->loadPtr(contextAddr, Assembler::ContextRegister); - Address addr = _asm->loadTempAddress(Assembler::ScratchRegister, result); - _asm->store32(Assembler::ReturnValueRegister, addr); + _as->loadPtr(contextAddr, Assembler::ContextRegister); + Address addr = _as->loadTempAddress(Assembler::ScratchRegister, result); + _as->store32(Assembler::ReturnValueRegister, addr); addr.offset += 4; - _asm->store32(Assembler::TrustedImm32(Value::Boolean_Type), addr); + _as->store32(Assembler::TrustedImm32(Value::Boolean_Type), addr); } void InstructionSelection::callBuiltinDeleteExceptionHandler() @@ -603,13 +603,13 @@ void InstructionSelection::loadThisObject(IR::Temp *temp) void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) { - _asm->storeValue(convertToValue(sourceConst), targetTemp); + _as->storeValue(convertToValue(sourceConst), targetTemp); } void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) { Value v = Value::fromString(identifier(str)); - _asm->storeValue(v, targetTemp); + _as->storeValue(v, targetTemp); } void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) @@ -617,7 +617,7 @@ void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *target Value v = Value::fromObject(engine()->newRegExpObject(*sourceRegexp->value, sourceRegexp->flags)); _vmFunction->generatedValues.append(v); - _asm->storeValue(v, targetTemp); + _as->storeValue(v, targetTemp); } void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) @@ -661,7 +661,7 @@ void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) { - _asm->copyValue(targetTemp, sourceTemp); + _as->copyValue(targetTemp, sourceTemp); } #define setOp(op, opName, operation) \ @@ -683,13 +683,13 @@ void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp * } // switch if (op) - _asm->generateFunctionCallImp(targetTemp, opName, op, sourceTemp, + _as->generateFunctionCallImp(targetTemp, opName, op, sourceTemp, Assembler::ContextRegister); } void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) { - _asm->generateBinOp(oper, target, leftSource, rightSource); + _as->generateBinOp(oper, target, leftSource, rightSource); } void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) @@ -713,7 +713,7 @@ void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, c break; } if (op) { - _asm->generateFunctionCallImp(Assembler::Void, opName, op, sourceExpr, identifier(targetName), Assembler::ContextRegister); + _as->generateFunctionCallImp(Assembler::Void, opName, op, sourceExpr, identifier(targetName), Assembler::ContextRegister); } } @@ -739,7 +739,7 @@ void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr } if (op) { - _asm->generateFunctionCallImp(Assembler::Void, opName, op, targetBaseTemp, targetIndexTemp, sourceExpr, Assembler::ContextRegister); + _as->generateFunctionCallImp(Assembler::Void, opName, op, targetBaseTemp, targetIndexTemp, sourceExpr, Assembler::ContextRegister); } } @@ -766,7 +766,7 @@ void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR: if (op) { String* member = identifier(targetName); - _asm->generateFunctionCallImp(Assembler::Void, opName, op, source, targetBase, member, Assembler::ContextRegister); + _as->generateFunctionCallImp(Assembler::Void, opName, op, source, targetBase, member, Assembler::ContextRegister); } } @@ -823,32 +823,32 @@ void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, I void InstructionSelection::visitJump(IR::Jump *s) { - _asm->jumpToBlock(_block, s->target); + _as->jumpToBlock(_block, s->target); } void InstructionSelection::visitCJump(IR::CJump *s) { if (IR::Temp *t = s->cond->asTemp()) { - Address temp = _asm->loadTempAddress(Assembler::ScratchRegister, t); + Address temp = _as->loadTempAddress(Assembler::ScratchRegister, t); Address tag = temp; tag.offset += offsetof(VM::Value, tag); - Assembler::Jump booleanConversion = _asm->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(VM::Value::Boolean_Type)); + Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(VM::Value::Boolean_Type)); Address data = temp; data.offset += offsetof(VM::Value, int_32); - _asm->load32(data, Assembler::ReturnValueRegister); - Assembler::Jump testBoolean = _asm->jump(); + _as->load32(data, Assembler::ReturnValueRegister); + Assembler::Jump testBoolean = _as->jump(); - booleanConversion.link(_asm); + booleanConversion.link(_as); { generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, t, Assembler::ContextRegister); } - testBoolean.link(_asm); - Assembler::Jump target = _asm->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); - _asm->addPatch(s->iftrue, target); + testBoolean.link(_as); + Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); + _as->addPatch(s->iftrue, target); - _asm->jumpToBlock(_block, s->iffalse); + _as->jumpToBlock(_block, s->iffalse); return; } else if (IR::Binop *b = s->cond->asBinop()) { if ((b->left->asTemp() || b->left->asConst()) && @@ -869,12 +869,12 @@ void InstructionSelection::visitCJump(IR::CJump *s) case IR::OpIn: setOp(op, opName, __qmljs_cmp_in); break; } // switch - _asm->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, b->left, b->right, Assembler::ContextRegister); + _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, b->left, b->right, Assembler::ContextRegister); - Assembler::Jump target = _asm->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); - _asm->addPatch(s->iftrue, target); + Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); + _as->addPatch(s->iftrue, target); - _asm->jumpToBlock(_block, s->iffalse); + _as->jumpToBlock(_block, s->iffalse); return; } else { assert(!"wip"); @@ -889,10 +889,10 @@ void InstructionSelection::visitRet(IR::Ret *s) { if (IR::Temp *t = s->expr->asTemp()) { #ifdef VALUE_FITS_IN_REGISTER - _asm->copyValue(Assembler::ReturnValueRegister, t); + _as->copyValue(Assembler::ReturnValueRegister, t); #else - _asm->loadPtr(addressForArgument(0), Assembler::ReturnValueRegister); - _asm->copyValue(Address(Assembler::ReturnValueRegister, 0), t); + _as->loadPtr(addressForArgument(0), Assembler::ReturnValueRegister); + _as->copyValue(Address(Assembler::ReturnValueRegister, 0), t); #endif return; } @@ -911,7 +911,7 @@ int InstructionSelection::prepareVariableArguments(IR::ExprList* args) for (IR::ExprList *it = args; it; it = it->next, ++i) { IR::Temp *arg = it->expr->asTemp(); assert(arg != 0); - _asm->copyValue(argumentAddressForCall(i), arg); + _as->copyValue(argumentAddressForCall(i), arg); } return argc; @@ -923,13 +923,13 @@ void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* na assert(baseName != 0); int argc = prepareVariableArguments(args); - _asm->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); + _as->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args) { int argc = prepareVariableArguments(args); - _asm->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); + _as->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index c5cf2d881e..aaa0866be9 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -731,7 +731,7 @@ protected: Address addressForArgument(int index) const { if (index < Assembler::RegisterArgumentCount) - return Address(_asm->registerForArgument(index), 0); + return Address(_as->registerForArgument(index), 0); // StackFrameRegister points to its old value on the stack, and above // it we have the return address, hence the need to step over two @@ -767,7 +767,7 @@ private: #define isel_stringIfy(s) isel_stringIfyx(s) #define generateFunctionCall(t, function, ...) \ - _asm->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) + _as->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) int prepareVariableArguments(IR::ExprList* args); @@ -781,7 +781,7 @@ private: IR::BasicBlock *_block; IR::Function* _function; VM::Function* _vmFunction; - Assembler* _asm; + Assembler* _as; }; class Q_V4_EXPORT ISelFactory: public EvalISelFactory -- cgit v1.2.3 From 285bef0ee925fb3d0554b1e4b9b137a1f7e67455 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:38:06 +0100 Subject: Fix build against Windows.h The system header file is kind enough to define a macro called CONST, which conflicts with our IR member function. Resolve the conflict by deleting the macro. Change-Id: I19ec1eadd54159a9bea128ef2a586d089f56b548 Reviewed-by: Lars Knoll --- src/v4/qv4codegen.cpp | 4 ++++ src/v4/qv4ir_p.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index 0b62256dda..c44e54db1d 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -55,6 +55,10 @@ #include #include +#ifdef CONST +#undef CONST +#endif + using namespace QQmlJS; using namespace AST; diff --git a/src/v4/qv4ir_p.h b/src/v4/qv4ir_p.h index 8ad783c92e..eefc0397e5 100644 --- a/src/v4/qv4ir_p.h +++ b/src/v4/qv4ir_p.h @@ -59,6 +59,10 @@ #include #include +#ifdef CONST +#undef CONST +#endif + QT_BEGIN_NAMESPACE class QTextStream; -- cgit v1.2.3 From 11e932e634cf0a2bd214b9925ad31ecd0e3aba73 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:42:55 +0100 Subject: Centralize code to determine system page size Use the existing WTF::pageSize() instead of calling sysconf ourselves. Change-Id: If68c793898253a239e13070e3454653474f61790 Reviewed-by: Lars Knoll --- src/3rdparty/masm/stubs/ExecutableAllocator.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/3rdparty/masm/stubs/ExecutableAllocator.h b/src/3rdparty/masm/stubs/ExecutableAllocator.h index 7b3004aa90..de04ffe977 100644 --- a/src/3rdparty/masm/stubs/ExecutableAllocator.h +++ b/src/3rdparty/masm/stubs/ExecutableAllocator.h @@ -43,6 +43,7 @@ #include #include +#include #include #include @@ -55,7 +56,7 @@ struct ExecutableMemoryHandle : public RefCounted { ExecutableMemoryHandle(int size) : m_size(size) { - static size_t pageSize = sysconf(_SC_PAGESIZE); + size_t pageSize = WTF::pageSize(); m_size = (m_size + pageSize - 1) & ~(pageSize - 1); #if OS(DARWIN) # define MAP_ANONYMOUS MAP_ANON @@ -92,7 +93,7 @@ struct ExecutableAllocator { static void makeExecutable(void* addr, int size) { - static size_t pageSize = sysconf(_SC_PAGESIZE); + size_t pageSize = WTF::pageSize(); size_t iaddr = reinterpret_cast(addr); size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); int mode = PROT_READ | PROT_WRITE | PROT_EXEC; -- cgit v1.2.3 From 5165917f44b2d05fa0808759e57c6cd244162be3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:46:03 +0100 Subject: Make ExecutableAllocator compile on Windows Use VirtualAlloc and friends instead of mmap. Change-Id: I52a90cebb111cf923d86ce6a821717dc7e02ad85 Reviewed-by: Lars Knoll --- src/3rdparty/masm/stubs/ExecutableAllocator.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/3rdparty/masm/stubs/ExecutableAllocator.h b/src/3rdparty/masm/stubs/ExecutableAllocator.h index de04ffe977..47768c6931 100644 --- a/src/3rdparty/masm/stubs/ExecutableAllocator.h +++ b/src/3rdparty/masm/stubs/ExecutableAllocator.h @@ -45,8 +45,12 @@ #include #include +#if OS(WINDOWS) +#include +#else #include #include +#endif namespace JSC { @@ -58,14 +62,22 @@ struct ExecutableMemoryHandle : public RefCounted { { size_t pageSize = WTF::pageSize(); m_size = (m_size + pageSize - 1) & ~(pageSize - 1); +#if OS(WINDOWS) + m_data = VirtualAlloc(0, m_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +#else #if OS(DARWIN) # define MAP_ANONYMOUS MAP_ANON #endif m_data = mmap(0, m_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif } ~ExecutableMemoryHandle() { +#if OS(WINDOWS) + VirtualFree(m_data, 0, MEM_RELEASE); +#else munmap(m_data, m_size); +#endif } inline void shrink(size_t) { @@ -96,8 +108,13 @@ struct ExecutableAllocator { size_t pageSize = WTF::pageSize(); size_t iaddr = reinterpret_cast(addr); size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); +#if OS(WINDOWS) + DWORD oldProtect; + VirtualProtect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), PAGE_EXECUTE_READWRITE, &oldProtect); +#else int mode = PROT_READ | PROT_WRITE | PROT_EXEC; mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode); +#endif } }; -- cgit v1.2.3 From ab999bb39e796f171f5adeee11977c978e761c8f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:47:03 +0100 Subject: Simplify UChar stub hack Don't use uint16_t, because it's not available with all MSVC versions. Since this is just a stub it's okay to simply write out the type. Change-Id: I9220c9476a7263377b723e46e4f49892908bd53c Reviewed-by: Lars Knoll --- src/3rdparty/masm/stubs/wtf/unicode/Unicode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h b/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h index d61bc64c5a..9e7427e8ac 100644 --- a/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h +++ b/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h @@ -44,7 +44,7 @@ #include typedef unsigned char LChar; -typedef uint16_t UChar; +typedef unsigned short UChar; namespace Unicode { inline UChar toLower(UChar ch) { -- cgit v1.2.3 From a0a58bf6e27f6ebc19fa1313a64d1acecd5b6345 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Feb 2013 16:52:18 +0100 Subject: Fix build on platforms without INFINITY Use Q_INFINITY from qnumeric.h as portable wrapper. Change-Id: I2dc2ef1c5e31ace89730b010c2a884e5b532b01f Reviewed-by: Lars Knoll --- src/v4/qmljs_engine.cpp | 2 +- src/v4/qmljs_runtime.cpp | 4 ++-- src/v4/qv4globalobject.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/v4/qmljs_engine.cpp b/src/v4/qmljs_engine.cpp index 8c5dcfc80d..9dd5e383d7 100644 --- a/src/v4/qmljs_engine.cpp +++ b/src/v4/qmljs_engine.cpp @@ -201,7 +201,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) glo->defineReadonlyProperty(this, QStringLiteral("undefined"), Value::undefinedValue()); glo->defineReadonlyProperty(this, QStringLiteral("NaN"), Value::fromDouble(nan(""))); - glo->defineReadonlyProperty(this, QStringLiteral("Infinity"), Value::fromDouble(INFINITY)); + glo->defineReadonlyProperty(this, QStringLiteral("Infinity"), Value::fromDouble(Q_INFINITY)); evalFunction = new (memoryManager) EvalFunction(rootContext); glo->defineDefaultProperty(rootContext, QStringLiteral("eval"), Value::fromObject(evalFunction)); diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index e192b40c99..a785f0081b 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -469,9 +469,9 @@ double __qmljs_string_to_number(const String *string) double d = qstrtod(begin, &end, &ok); if (end - begin != ba.size()) { if (ba == "Infinity" || ba == "+Infinity") - d = INFINITY; + d = Q_INFINITY; else if (ba == "-Infinity") - d = -INFINITY; + d = -Q_INFINITY; else d = nan(""); } diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp index fe4301160e..220704b54d 100644 --- a/src/v4/qv4globalobject.cpp +++ b/src/v4/qv4globalobject.cpp @@ -589,9 +589,9 @@ Value GlobalFunctions::method_parseFloat(ExecutionContext *context) // 4: if (trimmed.startsWith(QLatin1String("Infinity")) || trimmed.startsWith(QLatin1String("+Infinity"))) - return Value::fromDouble(INFINITY); + return Value::fromDouble(Q_INFINITY); if (trimmed.startsWith("-Infinity")) - return Value::fromDouble(-INFINITY); + return Value::fromDouble(-Q_INFINITY); QByteArray ba = trimmed.toLatin1(); bool ok; const char *begin = ba.constData(); -- cgit v1.2.3 From afe7ad259acbbd4c1aa6dcd8c87469788099c90f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Feb 2013 09:23:58 +0100 Subject: Fix cmath function usage Some of the cmath functions in std don't work reliably on all platforms and on top of that with Visual Studio they are not in the std namespace at all (but for example prefixed as _isnan). Import MathExtras.h from WTF that includes all the necessary workarounds, including things like making pow() with MinGW compliant with what JavaScript needs. Change-Id: I3d8190ce33919db69b2889b439731a9cf5b2a46e Reviewed-by: Lars Knoll --- src/3rdparty/masm/wtf/MathExtras.h | 449 +++++++++++++++++++++++++++++++++++++ src/v4/qmljs_runtime.cpp | 2 +- src/v4/qmljs_runtime.h | 8 +- src/v4/qmljs_value.cpp | 16 +- src/v4/qv4dateobject.cpp | 54 ++--- src/v4/qv4globalobject.cpp | 6 +- src/v4/qv4jsonobject.cpp | 3 +- src/v4/qv4mathobject.cpp | 8 +- src/v4/qv4numberobject.cpp | 6 +- src/v4/qv4stringobject.cpp | 6 +- 10 files changed, 508 insertions(+), 50 deletions(-) create mode 100644 src/3rdparty/masm/wtf/MathExtras.h diff --git a/src/3rdparty/masm/wtf/MathExtras.h b/src/3rdparty/masm/wtf/MathExtras.h new file mode 100644 index 0000000000..524d2acf7d --- /dev/null +++ b/src/3rdparty/masm/wtf/MathExtras.h @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_MathExtras_h +#define WTF_MathExtras_h + +#include +#include +#include +#include +#include +#include +#include + +#if OS(SOLARIS) +#include +#endif + +#if OS(OPENBSD) +#include +#include +#endif + +#if OS(QNX) +// FIXME: Look into a way to have cmath import its functions into both the standard and global +// namespace. For now, we include math.h since the QNX cmath header only imports its functions +// into the standard namespace. +#include +#endif + +#ifndef M_PI +const double piDouble = 3.14159265358979323846; +const float piFloat = 3.14159265358979323846f; +#else +const double piDouble = M_PI; +const float piFloat = static_cast(M_PI); +#endif + +#ifndef M_PI_2 +const double piOverTwoDouble = 1.57079632679489661923; +const float piOverTwoFloat = 1.57079632679489661923f; +#else +const double piOverTwoDouble = M_PI_2; +const float piOverTwoFloat = static_cast(M_PI_2); +#endif + +#ifndef M_PI_4 +const double piOverFourDouble = 0.785398163397448309616; +const float piOverFourFloat = 0.785398163397448309616f; +#else +const double piOverFourDouble = M_PI_4; +const float piOverFourFloat = static_cast(M_PI_4); +#endif + +#if OS(DARWIN) + +// Work around a bug in the Mac OS X libc where ceil(-0.1) return +0. +inline double wtf_ceil(double x) { return copysign(ceil(x), x); } + +#define ceil(x) wtf_ceil(x) + +#endif + +#if OS(SOLARIS) + +#ifndef isfinite +inline bool isfinite(double x) { return finite(x) && !isnand(x); } +#endif +#ifndef isinf +inline bool isinf(double x) { return !finite(x) && !isnand(x); } +#endif +#ifndef signbit +inline bool signbit(double x) { return copysign(1.0, x) < 0; } +#endif + +#endif + +#if OS(OPENBSD) + +#ifndef isfinite +inline bool isfinite(double x) { return finite(x); } +#endif +#ifndef signbit +inline bool signbit(double x) { struct ieee_double *p = (struct ieee_double *)&x; return p->dbl_sign; } +#endif + +#endif + +#if COMPILER(MSVC) || (COMPILER(RVCT) && !(RVCT_VERSION_AT_LEAST(3, 0, 0, 0))) + +// We must not do 'num + 0.5' or 'num - 0.5' because they can cause precision loss. +static double round(double num) +{ + double integer = ceil(num); + if (num > 0) + return integer - num > 0.5 ? integer - 1.0 : integer; + return integer - num >= 0.5 ? integer - 1.0 : integer; +} +static float roundf(float num) +{ + float integer = ceilf(num); + if (num > 0) + return integer - num > 0.5f ? integer - 1.0f : integer; + return integer - num >= 0.5f ? integer - 1.0f : integer; +} +inline long long llround(double num) { return static_cast(round(num)); } +inline long long llroundf(float num) { return static_cast(roundf(num)); } +inline long lround(double num) { return static_cast(round(num)); } +inline long lroundf(float num) { return static_cast(roundf(num)); } +inline double trunc(double num) { return num > 0 ? floor(num) : ceil(num); } + +#endif + +#if COMPILER(GCC) && OS(QNX) +// The stdlib on QNX doesn't contain long abs(long). See PR #104666. +inline long long abs(long num) { return labs(num); } +#endif + +#if OS(ANDROID) || COMPILER(MSVC) +// ANDROID and MSVC's math.h does not currently supply log2 or log2f. +inline double log2(double num) +{ + // This constant is roughly M_LN2, which is not provided by default on Windows and Android. + return log(num) / 0.693147180559945309417232121458176568; +} + +inline float log2f(float num) +{ + // This constant is roughly M_LN2, which is not provided by default on Windows and Android. + return logf(num) / 0.693147180559945309417232121458176568f; +} +#endif + +#if COMPILER(MSVC) +// The 64bit version of abs() is already defined in stdlib.h which comes with VC10 +#if COMPILER(MSVC9_OR_LOWER) +inline long long abs(long long num) { return _abs64(num); } +#endif + +inline bool isinf(double num) { return !_finite(num) && !_isnan(num); } +inline bool isnan(double num) { return !!_isnan(num); } +inline bool signbit(double num) { return _copysign(1.0, num) < 0; } + +inline double nextafter(double x, double y) { return _nextafter(x, y); } +inline float nextafterf(float x, float y) { return x > y ? x - FLT_EPSILON : x + FLT_EPSILON; } + +inline double copysign(double x, double y) { return _copysign(x, y); } +inline int isfinite(double x) { return _finite(x); } + +// Work around a bug in Win, where atan2(+-infinity, +-infinity) yields NaN instead of specific values. +inline double wtf_atan2(double x, double y) +{ + double posInf = std::numeric_limits::infinity(); + double negInf = -std::numeric_limits::infinity(); + double nan = std::numeric_limits::quiet_NaN(); + + double result = nan; + + if (x == posInf && y == posInf) + result = piOverFourDouble; + else if (x == posInf && y == negInf) + result = 3 * piOverFourDouble; + else if (x == negInf && y == posInf) + result = -piOverFourDouble; + else if (x == negInf && y == negInf) + result = -3 * piOverFourDouble; + else + result = ::atan2(x, y); + + return result; +} + +// Work around a bug in the Microsoft CRT, where fmod(x, +-infinity) yields NaN instead of x. +inline double wtf_fmod(double x, double y) { return (!isinf(x) && isinf(y)) ? x : fmod(x, y); } + +// Work around a bug in the Microsoft CRT, where pow(NaN, 0) yields NaN instead of 1. +inline double wtf_pow(double x, double y) { return y == 0 ? 1 : pow(x, y); } + +#define atan2(x, y) wtf_atan2(x, y) +#define fmod(x, y) wtf_fmod(x, y) +#define pow(x, y) wtf_pow(x, y) + +// MSVC's math functions do not bring lrint. +inline long int lrint(double flt) +{ + int64_t intgr; +#if CPU(X86) + __asm { + fld flt + fistp intgr + }; +#else + ASSERT(isfinite(flt)); + double rounded = round(flt); + intgr = static_cast(rounded); + // If the fractional part is exactly 0.5, we need to check whether + // the rounded result is even. If it is not we need to add 1 to + // negative values and subtract one from positive values. + if ((fabs(intgr - flt) == 0.5) & intgr) + intgr -= ((intgr >> 62) | 1); // 1 with the sign of result, i.e. -1 or 1. +#endif + return static_cast(intgr); +} + +#endif // COMPILER(MSVC) + +inline double deg2rad(double d) { return d * piDouble / 180.0; } +inline double rad2deg(double r) { return r * 180.0 / piDouble; } +inline double deg2grad(double d) { return d * 400.0 / 360.0; } +inline double grad2deg(double g) { return g * 360.0 / 400.0; } +inline double turn2deg(double t) { return t * 360.0; } +inline double deg2turn(double d) { return d / 360.0; } +inline double rad2grad(double r) { return r * 200.0 / piDouble; } +inline double grad2rad(double g) { return g * piDouble / 200.0; } + +inline float deg2rad(float d) { return d * piFloat / 180.0f; } +inline float rad2deg(float r) { return r * 180.0f / piFloat; } +inline float deg2grad(float d) { return d * 400.0f / 360.0f; } +inline float grad2deg(float g) { return g * 360.0f / 400.0f; } +inline float turn2deg(float t) { return t * 360.0f; } +inline float deg2turn(float d) { return d / 360.0f; } +inline float rad2grad(float r) { return r * 200.0f / piFloat; } +inline float grad2rad(float g) { return g * piFloat / 200.0f; } + +// std::numeric_limits::min() returns the smallest positive value for floating point types +template inline T defaultMinimumForClamp() { return std::numeric_limits::min(); } +template<> inline float defaultMinimumForClamp() { return -std::numeric_limits::max(); } +template<> inline double defaultMinimumForClamp() { return -std::numeric_limits::max(); } +template inline T defaultMaximumForClamp() { return std::numeric_limits::max(); } + +template inline T clampTo(double value, T min = defaultMinimumForClamp(), T max = defaultMaximumForClamp()) +{ + if (value >= static_cast(max)) + return max; + if (value <= static_cast(min)) + return min; + return static_cast(value); +} +template<> inline long long int clampTo(double, long long int, long long int); // clampTo does not support long long ints. + +inline int clampToInteger(double value) +{ + return clampTo(value); +} + +inline float clampToFloat(double value) +{ + return clampTo(value); +} + +inline int clampToPositiveInteger(double value) +{ + return clampTo(value, 0); +} + +inline int clampToInteger(float value) +{ + return clampTo(value); +} + +inline int clampToInteger(unsigned x) +{ + const unsigned intMax = static_cast(std::numeric_limits::max()); + + if (x >= intMax) + return std::numeric_limits::max(); + return static_cast(x); +} + +inline bool isWithinIntRange(float x) +{ + return x > static_cast(std::numeric_limits::min()) && x < static_cast(std::numeric_limits::max()); +} + +template inline bool hasOneBitSet(T value) +{ + return !((value - 1) & value) && value; +} + +template inline bool hasZeroOrOneBitsSet(T value) +{ + return !((value - 1) & value); +} + +template inline bool hasTwoOrMoreBitsSet(T value) +{ + return !hasZeroOrOneBitsSet(value); +} + +template inline unsigned getLSBSet(T value) +{ + unsigned result = 0; + + while (value >>= 1) + ++result; + + return result; +} + +template inline T timesThreePlusOneDividedByTwo(T value) +{ + // Mathematically equivalent to: + // (value * 3 + 1) / 2; + // or: + // (unsigned)ceil(value * 1.5)); + // This form is not prone to internal overflow. + return value + (value >> 1) + (value & 1); +} + +#if !COMPILER(MSVC) && !COMPILER(RVCT) && !OS(SOLARIS) +using std::isfinite; +#if !COMPILER_QUIRK(GCC11_GLOBAL_ISINF_ISNAN) +using std::isinf; +using std::isnan; +#endif +using std::signbit; +#endif + +#if COMPILER_QUIRK(GCC11_GLOBAL_ISINF_ISNAN) +// A workaround to avoid conflicting declarations of isinf and isnan when compiling with GCC in C++11 mode. +namespace std { + inline bool wtf_isinf(float f) { return std::isinf(f); } + inline bool wtf_isinf(double d) { return std::isinf(d); } + inline bool wtf_isnan(float f) { return std::isnan(f); } + inline bool wtf_isnan(double d) { return std::isnan(d); } +}; + +using std::wtf_isinf; +using std::wtf_isnan; + +#define isinf(x) wtf_isinf(x) +#define isnan(x) wtf_isnan(x) +#endif + +#ifndef UINT64_C +#if COMPILER(MSVC) +#define UINT64_C(c) c ## ui64 +#else +#define UINT64_C(c) c ## ull +#endif +#endif + +#if COMPILER(MINGW64) && (!defined(__MINGW64_VERSION_RC) || __MINGW64_VERSION_RC < 1) +inline double wtf_pow(double x, double y) +{ + // MinGW-w64 has a custom implementation for pow. + // This handles certain special cases that are different. + if ((x == 0.0 || isinf(x)) && isfinite(y)) { + double f; + if (modf(y, &f) != 0.0) + return ((x == 0.0) ^ (y > 0.0)) ? std::numeric_limits::infinity() : 0.0; + } + + if (x == 2.0) { + int yInt = static_cast(y); + if (y == yInt) + return ldexp(1.0, yInt); + } + + return pow(x, y); +} +#define pow(x, y) wtf_pow(x, y) +#endif // COMPILER(MINGW64) && (!defined(__MINGW64_VERSION_RC) || __MINGW64_VERSION_RC < 1) + + +// decompose 'number' to its sign, exponent, and mantissa components. +// The result is interpreted as: +// (sign ? -1 : 1) * pow(2, exponent) * (mantissa / (1 << 52)) +inline void decomposeDouble(double number, bool& sign, int32_t& exponent, uint64_t& mantissa) +{ + ASSERT(isfinite(number)); + + sign = signbit(number); + + uint64_t bits = WTF::bitwise_cast(number); + exponent = (static_cast(bits >> 52) & 0x7ff) - 0x3ff; + mantissa = bits & 0xFFFFFFFFFFFFFull; + + // Check for zero/denormal values; if so, adjust the exponent, + // if not insert the implicit, omitted leading 1 bit. + if (exponent == -0x3ff) + exponent = mantissa ? -0x3fe : 0; + else + mantissa |= 0x10000000000000ull; +} + +// Calculate d % 2^{64}. +inline void doubleToInteger(double d, unsigned long long& value) +{ + if (isnan(d) || isinf(d)) + value = 0; + else { + // -2^{64} < fmodValue < 2^{64}. + double fmodValue = fmod(trunc(d), std::numeric_limits::max() + 1.0); + if (fmodValue >= 0) { + // 0 <= fmodValue < 2^{64}. + // 0 <= value < 2^{64}. This cast causes no loss. + value = static_cast(fmodValue); + } else { + // -2^{64} < fmodValue < 0. + // 0 < fmodValueInUnsignedLongLong < 2^{64}. This cast causes no loss. + unsigned long long fmodValueInUnsignedLongLong = static_cast(-fmodValue); + // -1 < (std::numeric_limits::max() - fmodValueInUnsignedLongLong) < 2^{64} - 1. + // 0 < value < 2^{64}. + value = std::numeric_limits::max() - fmodValueInUnsignedLongLong + 1; + } + } +} + +namespace WTF { + +// From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 +inline uint32_t roundUpToPowerOfTwo(uint32_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +} // namespace WTF + +#endif // #ifndef WTF_MathExtras_h diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index a785f0081b..639e699ccc 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -64,7 +64,7 @@ namespace VM { QString numberToString(double num, int radix = 10) { - if (std::isnan(num)) { + if (isnan(num)) { return QStringLiteral("NaN"); } else if (qIsInf(num)) { return QLatin1String(num < 0 ? "-Infinity" : "Infinity"); diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 7b0a44af57..839133f735 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -52,6 +52,8 @@ #include #include +#include + #ifdef DO_TRACE_INSTR # define TRACE1(x) fprintf(stderr, " %s\n", __FUNCTION__); # define TRACE2(x, y) fprintf(stderr, " %s\n", __FUNCTION__); @@ -306,7 +308,7 @@ inline Bool __qmljs_to_boolean(Value value, ExecutionContext *ctx) case Value::Object_Type: return true; default: // double - if (! value.doubleValue() || std::isnan(value.doubleValue())) + if (! value.doubleValue() || isnan(value.doubleValue())) return false; return true; } @@ -361,11 +363,11 @@ inline unsigned short __qmljs_to_uint16(Value value, ExecutionContext *ctx) if ((number >= 0 && number < D16)) return static_cast(number); - if (!std::isfinite(number)) + if (!isfinite(number)) return +0; double d = ::floor(::fabs(number)); - if (std::signbit(number)) + if (signbit(number)) d = -d; number = ::fmod(d , D16); diff --git a/src/v4/qmljs_value.cpp b/src/v4/qmljs_value.cpp index 8dba13a9c9..83dd9c621c 100644 --- a/src/v4/qmljs_value.cpp +++ b/src/v4/qmljs_value.cpp @@ -42,6 +42,8 @@ #include #include +#include + namespace QQmlJS { namespace VM { @@ -104,11 +106,11 @@ int Value::toInt32(double number) return static_cast(number); - if (!std::isfinite(number)) + if (!isfinite(number)) return 0; double d = ::floor(::fabs(number)); - if (std::signbit(number)) + if (signbit(number)) d = -d; number = ::fmod(d , D32); @@ -127,11 +129,11 @@ unsigned int Value::toUInt32(double number) if ((number >= 0 && number < D32)) return static_cast(number); - if (!std::isfinite(number)) + if (!isfinite(number)) return +0; double d = ::floor(::fabs(number)); - if (std::signbit(number)) + if (signbit(number)) d = -d; number = ::fmod(d , D32); @@ -144,12 +146,12 @@ unsigned int Value::toUInt32(double number) double Value::toInteger(double number) { - if (std::isnan(number)) + if (isnan(number)) return +0; - else if (! number || std::isinf(number)) + else if (! number || isinf(number)) return number; const double v = floor(fabs(number)); - return std::signbit(number) ? -v : v; + return signbit(number) ? -v : v; } Value Value::property(ExecutionContext *ctx, String *name) const diff --git a/src/v4/qv4dateobject.cpp b/src/v4/qv4dateobject.cpp index c808c78d85..bdfc4be843 100644 --- a/src/v4/qv4dateobject.cpp +++ b/src/v4/qv4dateobject.cpp @@ -61,6 +61,8 @@ #include #include +#include + #ifndef Q_OS_WIN # include # ifndef Q_OS_VXWORKS @@ -572,7 +574,7 @@ static inline double ParseString(const QString &s) */ static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) { - if (std::isnan(t)) + if (isnan(t)) return QDateTime(); if (spec == Qt::LocalTime) t = LocalTime(t); @@ -588,7 +590,7 @@ static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) static inline QString ToString(double t) { - if (std::isnan(t)) + if (isnan(t)) return QStringLiteral("Invalid Date"); QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT"); double tzoffset = LocalTZA + DaylightSavingTA(t); @@ -608,7 +610,7 @@ static inline QString ToString(double t) static inline QString ToUTCString(double t) { - if (std::isnan(t)) + if (isnan(t)) return QStringLiteral("Invalid Date"); return ToDateTime(t, Qt::UTC).toString() + QStringLiteral(" GMT"); } @@ -858,7 +860,7 @@ Value DatePrototype::method_getTime(ExecutionContext *ctx) Value DatePrototype::method_getYear(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = YearFromTime(LocalTime(t)) - 1900; return Value::fromDouble(t); } @@ -866,7 +868,7 @@ Value DatePrototype::method_getYear(ExecutionContext *ctx) Value DatePrototype::method_getFullYear(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = YearFromTime(LocalTime(t)); return Value::fromDouble(t); } @@ -874,7 +876,7 @@ Value DatePrototype::method_getFullYear(ExecutionContext *ctx) Value DatePrototype::method_getUTCFullYear(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = YearFromTime(t); return Value::fromDouble(t); } @@ -882,7 +884,7 @@ Value DatePrototype::method_getUTCFullYear(ExecutionContext *ctx) Value DatePrototype::method_getMonth(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = MonthFromTime(LocalTime(t)); return Value::fromDouble(t); } @@ -890,7 +892,7 @@ Value DatePrototype::method_getMonth(ExecutionContext *ctx) Value DatePrototype::method_getUTCMonth(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = MonthFromTime(t); return Value::fromDouble(t); } @@ -898,7 +900,7 @@ Value DatePrototype::method_getUTCMonth(ExecutionContext *ctx) Value DatePrototype::method_getDate(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = DateFromTime(LocalTime(t)); return Value::fromDouble(t); } @@ -906,7 +908,7 @@ Value DatePrototype::method_getDate(ExecutionContext *ctx) Value DatePrototype::method_getUTCDate(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = DateFromTime(t); return Value::fromDouble(t); } @@ -914,7 +916,7 @@ Value DatePrototype::method_getUTCDate(ExecutionContext *ctx) Value DatePrototype::method_getDay(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = WeekDay(LocalTime(t)); return Value::fromDouble(t); } @@ -922,7 +924,7 @@ Value DatePrototype::method_getDay(ExecutionContext *ctx) Value DatePrototype::method_getUTCDay(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = WeekDay(t); return Value::fromDouble(t); } @@ -930,7 +932,7 @@ Value DatePrototype::method_getUTCDay(ExecutionContext *ctx) Value DatePrototype::method_getHours(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = HourFromTime(LocalTime(t)); return Value::fromDouble(t); } @@ -938,7 +940,7 @@ Value DatePrototype::method_getHours(ExecutionContext *ctx) Value DatePrototype::method_getUTCHours(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = HourFromTime(t); return Value::fromDouble(t); } @@ -946,7 +948,7 @@ Value DatePrototype::method_getUTCHours(ExecutionContext *ctx) Value DatePrototype::method_getMinutes(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = MinFromTime(LocalTime(t)); return Value::fromDouble(t); } @@ -954,7 +956,7 @@ Value DatePrototype::method_getMinutes(ExecutionContext *ctx) Value DatePrototype::method_getUTCMinutes(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = MinFromTime(t); return Value::fromDouble(t); } @@ -962,7 +964,7 @@ Value DatePrototype::method_getUTCMinutes(ExecutionContext *ctx) Value DatePrototype::method_getSeconds(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = SecFromTime(LocalTime(t)); return Value::fromDouble(t); } @@ -970,7 +972,7 @@ Value DatePrototype::method_getSeconds(ExecutionContext *ctx) Value DatePrototype::method_getUTCSeconds(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = SecFromTime(t); return Value::fromDouble(t); } @@ -978,7 +980,7 @@ Value DatePrototype::method_getUTCSeconds(ExecutionContext *ctx) Value DatePrototype::method_getMilliseconds(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = msFromTime(LocalTime(t)); return Value::fromDouble(t); } @@ -986,7 +988,7 @@ Value DatePrototype::method_getMilliseconds(ExecutionContext *ctx) Value DatePrototype::method_getUTCMilliseconds(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = msFromTime(t); return Value::fromDouble(t); } @@ -994,7 +996,7 @@ Value DatePrototype::method_getUTCMilliseconds(ExecutionContext *ctx) Value DatePrototype::method_getTimezoneOffset(ExecutionContext *ctx) { double t = getThisDate(ctx); - if (! std::isnan(t)) + if (! isnan(t)) t = (t - LocalTime(t)) / msPerMinute; return Value::fromDouble(t); } @@ -1184,13 +1186,13 @@ Value DatePrototype::method_setYear(ExecutionContext *ctx) ctx->throwTypeError(); double t = self->value.asDouble(); - if (std::isnan(t)) + if (isnan(t)) t = 0; else t = LocalTime(t); double year = ctx->argument(0).toNumber(ctx); double r; - if (std::isnan(year)) { + if (isnan(year)) { r = qSNaN(); } else { if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99)) @@ -1225,7 +1227,7 @@ Value DatePrototype::method_setFullYear(ExecutionContext *ctx) ctx->throwTypeError(); double t = LocalTime(self->value.asDouble()); - if (std::isnan(t)) + if (isnan(t)) t = 0; double year = ctx->argument(0).toNumber(ctx); double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); @@ -1265,7 +1267,7 @@ Value DatePrototype::method_toISOString(ExecutionContext *ctx) ctx->throwTypeError(); double t = self->value.asDouble(); - if (!std::isfinite(t)) + if (!isfinite(t)) ctx->throwRangeError(ctx->thisObject); QString result; @@ -1301,7 +1303,7 @@ Value DatePrototype::method_toJSON(ExecutionContext *ctx) Value O = __qmljs_to_object(ctx->thisObject, ctx); Value tv = __qmljs_to_primitive(O, ctx, NUMBER_HINT); - if (tv.isNumber() && !std::isfinite(tv.toNumber(ctx))) + if (tv.isNumber() && !isfinite(tv.toNumber(ctx))) return Value::nullValue(); FunctionObject *toIso = O.objectValue()->__get__(ctx, ctx->engine->newString(QStringLiteral("toISOString"))).asFunctionObject(); diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp index 220704b54d..86d8c38de9 100644 --- a/src/v4/qv4globalobject.cpp +++ b/src/v4/qv4globalobject.cpp @@ -57,6 +57,8 @@ #include #include +#include + using namespace QQmlJS::VM; static inline char toHex(char c) @@ -611,7 +613,7 @@ Value GlobalFunctions::method_isNaN(ExecutionContext *context) return Value::fromBoolean(false); double d = v.toNumber(context); - return Value::fromBoolean(std::isnan(d)); + return Value::fromBoolean(isnan(d)); } /// isFinite [15.1.2.5] @@ -622,7 +624,7 @@ Value GlobalFunctions::method_isFinite(ExecutionContext *context) return Value::fromBoolean(true); double d = v.toNumber(context); - return Value::fromBoolean(std::isfinite(d)); + return Value::fromBoolean(isfinite(d)); } /// decodeURI [15.1.3.1] diff --git a/src/v4/qv4jsonobject.cpp b/src/v4/qv4jsonobject.cpp index 4bb6cd53cd..241c3c8654 100644 --- a/src/v4/qv4jsonobject.cpp +++ b/src/v4/qv4jsonobject.cpp @@ -48,6 +48,7 @@ #include #include +#include namespace QQmlJS { namespace VM { @@ -737,7 +738,7 @@ QString Stringify::Str(const QString &key, Value value) if (value.isNumber()) { double d = value.toNumber(ctx); - return std::isfinite(d) ? value.toString(ctx)->toQString() : QStringLiteral("null"); + return isfinite(d) ? value.toString(ctx)->toQString() : QStringLiteral("null"); } if (Object *o = value.asObject()) { diff --git a/src/v4/qv4mathobject.cpp b/src/v4/qv4mathobject.cpp index 2f92e52e2f..f0523baa45 100644 --- a/src/v4/qv4mathobject.cpp +++ b/src/v4/qv4mathobject.cpp @@ -206,7 +206,7 @@ Value MathObject::method_max(ExecutionContext *parentCtx, Value thisObject, Valu double mx = -qInf(); for (unsigned i = 0; i < argc; ++i) { double x = argv[i].toNumber(parentCtx); - if (x > mx || std::isnan(x)) + if (x > mx || isnan(x)) mx = x; } return Value::fromDouble(mx); @@ -218,7 +218,7 @@ Value MathObject::method_min(ExecutionContext *parentCtx, Value thisObject, Valu for (unsigned i = 0; i < argc; ++i) { double x = argv[i].toNumber(parentCtx); if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) - || (x < mx) || std::isnan(x)) { + || (x < mx) || isnan(x)) { mx = x; } } @@ -230,12 +230,12 @@ Value MathObject::method_pow(ExecutionContext *parentCtx, Value, Value *argv, in double x = argc > 0 ? argv[0].toNumber(parentCtx) : qSNaN(); double y = argc > 1 ? argv[1].toNumber(parentCtx) : qSNaN(); - if (std::isnan(y)) + if (isnan(y)) return Value::fromDouble(qSNaN()); if (y == 0) { return Value::fromDouble(1); - } else if (((x == 1) || (x == -1)) && std::isinf(y)) { + } else if (((x == 1) || (x == -1)) && isinf(y)) { return Value::fromDouble(qSNaN()); } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { return Value::fromDouble(qInf()); diff --git a/src/v4/qv4numberobject.cpp b/src/v4/qv4numberobject.cpp index d732297f4e..57cfab9d61 100644 --- a/src/v4/qv4numberobject.cpp +++ b/src/v4/qv4numberobject.cpp @@ -115,7 +115,7 @@ Value NumberPrototype::method_toString(ExecutionContext *ctx) return Value::undefinedValue(); } - if (std::isnan(num)) { + if (isnan(num)) { return Value::fromString(ctx, QStringLiteral("NaN")); } else if (qIsInf(num)) { return Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); @@ -186,7 +186,7 @@ Value NumberPrototype::method_toFixed(ExecutionContext *ctx) if (ctx->argumentCount > 0) fdigits = ctx->argument(0).toInteger(ctx); - if (std::isnan(fdigits)) + if (isnan(fdigits)) fdigits = 0; if (fdigits < 0 || fdigits > 20) @@ -194,7 +194,7 @@ Value NumberPrototype::method_toFixed(ExecutionContext *ctx) double v = thisObject->value.asDouble(); QString str; - if (std::isnan(v)) + if (isnan(v)) str = QString::fromLatin1("NaN"); else if (qIsInf(v)) str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp index a070cc0ccb..dee5dae0dc 100644 --- a/src/v4/qv4stringobject.cpp +++ b/src/v4/qv4stringobject.cpp @@ -274,7 +274,7 @@ Value StringPrototype::method_lastIndexOf(ExecutionContext *parentCtx, Value thi Value posArg = argc > 1 ? argv[1] : Value::undefinedValue(); double position = __qmljs_to_number(posArg, parentCtx); - if (std::isnan(position)) + if (isnan(position)) position = +qInf(); else position = trunc(position); @@ -645,10 +645,10 @@ Value StringPrototype::method_substring(ExecutionContext *parentCtx, Value thisO if (!endValue.isUndefined()) end = endValue.toInteger(parentCtx); - if (std::isnan(start) || start < 0) + if (isnan(start) || start < 0) start = 0; - if (std::isnan(end) || end < 0) + if (isnan(end) || end < 0) end = 0; if (start > length) -- cgit v1.2.3 From c25b9fe1d2444f961dc1fce655c1dbcdc55f0b9f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Feb 2013 09:30:40 +0100 Subject: Fix alloca usage on Windows On Windows it's _alloca, so provide a wrapping macro in qv4alloca_p.h and use that instead. Change-Id: I675cb7fd09bab3c84c44e56819c0f17a42b3c211 Reviewed-by: Lars Knoll --- src/v4/moth/qv4vme_moth.cpp | 2 +- src/v4/qv4alloca_p.h | 42 ++++++++++++++++++++++++++++++++++++++++++ src/v4/qv4functionobject.cpp | 2 +- src/v4/qv4globalobject.cpp | 2 +- src/v4/qv4mm.cpp | 2 +- src/v4/qv4object.cpp | 2 +- src/v4/qv4regexpobject.cpp | 2 +- 7 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 src/v4/qv4alloca_p.h diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 1cfaf6435b..8ed3fb1a0a 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -5,7 +5,7 @@ #include -#include +#include "qv4alloca_p.h" #ifdef DO_TRACE_INSTR # define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); diff --git a/src/v4/qv4alloca_p.h b/src/v4/qv4alloca_p.h new file mode 100644 index 0000000000..d3fdae69f9 --- /dev/null +++ b/src/v4/qv4alloca_p.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QV4_ALLOCA_H +#define QV4_ALLOCA_H + +#include + +#if defined(Q_OS_WIN) +#include +#define alloca _alloca +#else +#include +#endif + +#endif diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 2d96f9287f..02f76a7205 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -59,7 +59,7 @@ #include #include #include -#include +#include "qv4alloca_p.h" using namespace QQmlJS::VM; diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp index 86d8c38de9..50202581aa 100644 --- a/src/v4/qv4globalobject.cpp +++ b/src/v4/qv4globalobject.cpp @@ -55,7 +55,7 @@ #include #include #include -#include +#include "qv4alloca_p.h" #include diff --git a/src/v4/qv4mm.cpp b/src/v4/qv4mm.cpp index f29381be33..2724700032 100644 --- a/src/v4/qv4mm.cpp +++ b/src/v4/qv4mm.cpp @@ -41,7 +41,7 @@ #include #include -#include +#include "qv4alloca_p.h" using namespace QQmlJS::VM; using namespace WTF; diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 5e57c89d58..f260762587 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -60,7 +60,7 @@ #include #include #include -#include +#include "qv4alloca_p.h" using namespace QQmlJS::VM; diff --git a/src/v4/qv4regexpobject.cpp b/src/v4/qv4regexpobject.cpp index 0240830e71..8319a0cee0 100644 --- a/src/v4/qv4regexpobject.cpp +++ b/src/v4/qv4regexpobject.cpp @@ -59,7 +59,7 @@ #include #include #include -#include +#include "qv4alloca_p.h" using namespace QQmlJS::VM; -- cgit v1.2.3 From a1c23195c882afd34d977e20badea2b8a846ff65 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Feb 2013 09:39:25 +0100 Subject: Fix build with Visual Studio Which doesn't have nan(const char *tagp). Instead let's just use std::numeric_limits::quiet_NaN(). Change-Id: I13127a3813fc84e85681883c5a0ec89f7cd69667 Reviewed-by: Lars Knoll --- src/v4/qmljs_engine.cpp | 2 +- src/v4/qmljs_runtime.cpp | 2 +- src/v4/qmljs_runtime.h | 2 +- src/v4/qv4globalobject.cpp | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/v4/qmljs_engine.cpp b/src/v4/qmljs_engine.cpp index 9dd5e383d7..05a03f657b 100644 --- a/src/v4/qmljs_engine.cpp +++ b/src/v4/qmljs_engine.cpp @@ -200,7 +200,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) glo->defineDefaultProperty(rootContext, QStringLiteral("JSON"), Value::fromObject(new (memoryManager) JsonObject(rootContext))); glo->defineReadonlyProperty(this, QStringLiteral("undefined"), Value::undefinedValue()); - glo->defineReadonlyProperty(this, QStringLiteral("NaN"), Value::fromDouble(nan(""))); + glo->defineReadonlyProperty(this, QStringLiteral("NaN"), Value::fromDouble(std::numeric_limits::quiet_NaN())); glo->defineReadonlyProperty(this, QStringLiteral("Infinity"), Value::fromDouble(Q_INFINITY)); evalFunction = new (memoryManager) EvalFunction(rootContext); diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 639e699ccc..1fcdf02d4f 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -473,7 +473,7 @@ double __qmljs_string_to_number(const String *string) else if (ba == "-Infinity") d = -Q_INFINITY; else - d = nan(""); + d = std::numeric_limits::quiet_NaN(); } return d; } diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 839133f735..27ffff8e3c 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -318,7 +318,7 @@ inline double __qmljs_to_number(Value value, ExecutionContext *ctx) { switch (value.type()) { case Value::Undefined_Type: - return nan(""); + return std::numeric_limits::quiet_NaN(); case Value::Null_Type: return 0; case Value::Boolean_Type: diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp index 50202581aa..2d7bb71e10 100644 --- a/src/v4/qv4globalobject.cpp +++ b/src/v4/qv4globalobject.cpp @@ -523,7 +523,7 @@ Value GlobalFunctions::method_parseInt(ExecutionContext *context) bool stripPrefix = true; // 7 if (R) { // 8 if (R < 2 || R > 36) - return Value::fromDouble(nan("")); // 8a + return Value::fromDouble(std::numeric_limits::quiet_NaN()); // 8a if (R != 16) stripPrefix = false; // 8b } else { // 9 @@ -540,13 +540,13 @@ Value GlobalFunctions::method_parseInt(ExecutionContext *context) // 11: Z is progressively built below // 13: this is handled by the toInt function if (pos == end) // 12 - return Value::fromDouble(nan("")); + return Value::fromDouble(std::numeric_limits::quiet_NaN()); bool overflow = false; qint64 v_overflow; unsigned overflow_digit_count = 0; int d = toInt(*pos++, R); if (d == -1) - return Value::fromDouble(nan("")); + return Value::fromDouble(std::numeric_limits::quiet_NaN()); qint64 v = d; while (pos != end) { d = toInt(*pos++, R); @@ -600,7 +600,7 @@ Value GlobalFunctions::method_parseFloat(ExecutionContext *context) const char *end = 0; double d = qstrtod(begin, &end, &ok); if (end - begin == 0) - return Value::fromDouble(nan("")); // 3 + return Value::fromDouble(std::numeric_limits::quiet_NaN()); // 3 else return Value::fromDouble(d); } -- cgit v1.2.3 From 223e6207171cd0e69b082306bedad548e9756b7a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Feb 2013 09:45:37 +0100 Subject: Fix link error on Windows __qmljs_create_exception_handler was declared without export but defined with export. Change-Id: I998d33d64eb8acfaba451582e4c0082685f2b42a Reviewed-by: Lars Knoll --- src/v4/qmljs_runtime.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 27ffff8e3c..2742692c51 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -208,7 +208,7 @@ Value __qmljs_delete_name(ExecutionContext *ctx, String *name); void __qmljs_throw(Value value, ExecutionContext *context); // actually returns a jmp_buf * -void *__qmljs_create_exception_handler(ExecutionContext *context); +Q_V4_EXPORT void *__qmljs_create_exception_handler(ExecutionContext *context); void __qmljs_delete_exception_handler(ExecutionContext *context); Value __qmljs_get_exception(ExecutionContext *context); -- cgit v1.2.3 From ea72983d39bf6426e182e7ee97858f534288a487 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Feb 2013 09:51:13 +0100 Subject: Fix build with Visual Studio VC gets confused by the VM:: namespace prefix here, but it turns out that it's not necessary in the first place. Change-Id: I626d0d4e1d64e64d28783045a4d8bf320a060aeb Reviewed-by: Lars Knoll --- src/v4/qv4functionobject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 02f76a7205..bba00b8d1e 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -335,7 +335,7 @@ Value FunctionPrototype::method_bind(ExecutionContext *ctx) return Value::fromObject(f); } -ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function) +ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) : FunctionObject(scope) , function(function) { @@ -382,7 +382,7 @@ ScriptFunction::~ScriptFunction() { } -Value ScriptFunction::call(VM::ExecutionContext *ctx) +Value ScriptFunction::call(ExecutionContext *ctx) { assert(function->code); return function->code(ctx, function->codeData); -- cgit v1.2.3 From 2eca9ab72bcd67c864630723aaaa370a167a951b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Feb 2013 09:54:15 +0100 Subject: Fix debug build with Visual Studio For some reason it wants to reference the Void "value" that should really get optimized away. That doesn't seem to be the case in debug builds, so let's just define it. Change-Id: Ifb25911d4764af87b5a7d3a04f618ebc5e428ff7 Reviewed-by: Lars Knoll --- src/v4/qv4isel_masm.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 7c9ab8737a..24a51a94a3 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -59,6 +59,8 @@ using namespace QQmlJS; using namespace QQmlJS::MASM; using namespace QQmlJS::VM; +const Assembler::VoidType Assembler::Void; + Assembler::Assembler(IR::Function* function) : _function(function) { -- cgit v1.2.3 From e7e2aa45689014640a425d60f4be87b211634b97 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Feb 2013 13:25:23 +0100 Subject: Fix name function expressions We should also insert those into the environment members if they have a name and are part of an expression statement. Simple testcase example: try { function foo() { ... } } catch (e) {} Then foo should be visible as if it was declared outside of the try. Change-Id: I8d23a28e1c4537d4f57f9cb0d559e6163e0fdef0 Reviewed-by: Lars Knoll --- src/v4/qv4codegen.cpp | 29 ++++++++++++++++++++++------- src/v4/qv4codegen_p.h | 4 ++-- tests/TestExpectations | 13 +++++-------- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index c44e54db1d..eda6b0cca7 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -326,12 +326,29 @@ protected: return true; } + virtual bool visit(ExpressionStatement *ast) + { + if (FunctionExpression* expr = AST::cast(ast->expression)) { + enterFunction(expr, /*enterName*/ true); + Node::accept(expr->formals, this); + Node::accept(expr->body, this); + leaveEnvironment(); + return false; + } + return true; + } + virtual bool visit(FunctionExpression *ast) + { + enterFunction(ast, /*enterName*/ false); + return true; + } + + void enterFunction(FunctionExpression *ast, bool enterName) { if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Function name may not be eval or arguments in strict mode")); - enterFunction(ast, ast->name.toString(), ast->formals, ast->body); - return true; + enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0); } virtual void endVisit(FunctionExpression *) @@ -352,9 +369,7 @@ protected: virtual bool visit(FunctionDeclaration *ast) { - if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) - _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Function name may not be eval or arguments in strict mode")); - enterFunction(ast, ast->name.toString(), ast->formals, ast->body, ast); + enterFunction(ast, /*enterName*/ true); return true; } @@ -374,12 +389,12 @@ protected: } private: - void enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionDeclaration *decl = 0) + void enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr = 0) { bool wasStrict = false; if (_env) { _env->hasNestedFunctions = true; - _env->enter(name, Environment::FunctionDefinition, decl); + _env->enter(name, Environment::FunctionDefinition, expr); if (name == QLatin1String("arguments")) _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; wasStrict = _env->isStrict; diff --git a/src/v4/qv4codegen_p.h b/src/v4/qv4codegen_p.h index d83e129782..239027a7d9 100644 --- a/src/v4/qv4codegen_p.h +++ b/src/v4/qv4codegen_p.h @@ -131,7 +131,7 @@ protected: struct Member { MemberType type; int index; - AST::FunctionDeclaration *function; + AST::FunctionExpression *function; }; typedef QMap MemberMap; @@ -184,7 +184,7 @@ protected: return false; } - void enter(const QString &name, MemberType type, AST::FunctionDeclaration *function = 0) + void enter(const QString &name, MemberType type, AST::FunctionExpression *function = 0) { if (! name.isEmpty()) { MemberMap::iterator it = members.find(name); diff --git a/tests/TestExpectations b/tests/TestExpectations index 13383782bb..30aad20bd1 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -3,7 +3,6 @@ 15.2.3.6-2-17-1 failing -10.4.2-1-2 failing 10.4.3-1-104 failing 10.4.3-1-106 failing 10.4.3-1-63-s failing @@ -12,7 +11,6 @@ S11.5.3_A4_T2 failing S11.8.6_A3 failing S11.8.6_A5_T2 failing -12.14-13 failing S13_A15_T4 failing S13_A3_T1 failing S13.2.3_A1 failing @@ -20,13 +18,10 @@ S14_A2 failing 14.1-5-s failing 15.1.1.3-3 failing S15.12.2_A1 failing -15.2.3.6-4-360-3 failing -15.2.3.6-4-360-7 failing 15.4.4.14-9-a-10 failing 15.4.4.14-9-a-17 failing 15.4.4.14-9-a-7 failing 15.4.4.14-9-a-9 failing -15.4.4.19-5-1 failing S15.4.4.3_A1_T1 failing S15.4.4.3_A3_T1 failing 15.4.4.4-5-c-i-1 failing @@ -37,8 +32,6 @@ S15.5.4.11_A5_T1 failing 15.5.4.20-4-1 failing S15.5.4.8_A1_T4 failing S15.7.4.5_A1.4_T01 failing -Sbp_12.5_A9_T3 failing -Sbp_12.6.2_A13_T3 failing Sbp_7.8.4_A6.1_T4 failing Sbp_7.8.4_A6.2_T1 failing Sbp_7.8.4_A6.2_T2 failing @@ -51,4 +44,8 @@ Sbp_A4_T2 failing S12.4_A1 failing S15.2.4.4_A14 failing # Try/catch scoping issue -S12.14_A4 failing \ No newline at end of file +S12.14_A4 failing + +# Function declaration inside if / while +Sbp_12.5_A9_T3 failing +Sbp_12.6.2_A13_T3 failing \ No newline at end of file -- cgit v1.2.3 From b5333bdcf10662970185bd23f1ed10603f070159 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 10 Feb 2013 22:22:53 +0100 Subject: Use internal classes to store the layout of members Add an internal class structure to Object that will allow us to do much more efficient property lookups in the future. Change-Id: I9ee72f6d73113a489f00ad7a31a20e91fbba18ed Reviewed-by: Simon Hausmann --- src/v4/qmljs_engine.cpp | 17 +-- src/v4/qmljs_engine.h | 3 + src/v4/qv4argumentsobject.cpp | 2 +- src/v4/qv4arrayobject.cpp | 2 +- src/v4/qv4booleanobject.cpp | 2 +- src/v4/qv4booleanobject.h | 2 +- src/v4/qv4dateobject.h | 4 +- src/v4/qv4errorobject.cpp | 1 + src/v4/qv4functionobject.cpp | 8 +- src/v4/qv4functionobject.h | 1 - src/v4/qv4identifier.h | 4 +- src/v4/qv4internalclass.cpp | 129 +++++++++++++++++++++++ src/v4/qv4internalclass.h | 79 ++++++++++++++ src/v4/qv4jsonobject.cpp | 4 +- src/v4/qv4mathobject.cpp | 1 + src/v4/qv4numberobject.h | 2 +- src/v4/qv4object.cpp | 135 ++++++++++-------------- src/v4/qv4object.h | 19 ++-- src/v4/qv4objectiterator.cpp | 34 +++--- src/v4/qv4objectiterator.h | 4 +- src/v4/qv4objectproto.h | 2 + src/v4/qv4propertytable.h | 234 ------------------------------------------ src/v4/qv4regexpobject.cpp | 7 +- src/v4/qv4regexpobject.h | 1 - src/v4/qv4stringobject.cpp | 2 +- src/v4/v4.pro | 3 +- 26 files changed, 331 insertions(+), 371 deletions(-) create mode 100644 src/v4/qv4internalclass.cpp create mode 100644 src/v4/qv4internalclass.h delete mode 100644 src/v4/qv4propertytable.h diff --git a/src/v4/qmljs_engine.cpp b/src/v4/qmljs_engine.cpp index 05a03f657b..b71111af22 100644 --- a/src/v4/qmljs_engine.cpp +++ b/src/v4/qmljs_engine.cpp @@ -75,6 +75,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) identifierCache = new Identifiers(this); + emptyClass = new InternalClass(this); rootContext = newContext(); rootContext->init(this); current = rootContext; @@ -94,12 +95,12 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) id_set = newIdentifier(QStringLiteral("set")); id_eval = newIdentifier(QStringLiteral("eval")); - objectPrototype = new (memoryManager) ObjectPrototype(); + objectPrototype = new (memoryManager) ObjectPrototype(this); stringPrototype = new (memoryManager) StringPrototype(rootContext); - numberPrototype = new (memoryManager) NumberPrototype(); - booleanPrototype = new (memoryManager) BooleanPrototype(); + numberPrototype = new (memoryManager) NumberPrototype(this); + booleanPrototype = new (memoryManager) BooleanPrototype(this); arrayPrototype = new (memoryManager) ArrayPrototype(rootContext); - datePrototype = new (memoryManager) DatePrototype(); + datePrototype = new (memoryManager) DatePrototype(this); functionPrototype = new (memoryManager) FunctionPrototype(rootContext); regExpPrototype = new (memoryManager) RegExpPrototype(this); errorPrototype = new (memoryManager) ErrorPrototype(this); @@ -270,7 +271,7 @@ BoundFunction *ExecutionEngine::newBoundFunction(ExecutionContext *scope, Functi Object *ExecutionEngine::newObject() { - Object *object = new (memoryManager) Object(); + Object *object = new (memoryManager) Object(this); object->prototype = objectPrototype; return object; } @@ -294,14 +295,14 @@ Object *ExecutionEngine::newStringObject(ExecutionContext *ctx, const Value &val Object *ExecutionEngine::newNumberObject(const Value &value) { - NumberObject *object = new (memoryManager) NumberObject(value); + NumberObject *object = new (memoryManager) NumberObject(this, value); object->prototype = numberPrototype; return object; } Object *ExecutionEngine::newBooleanObject(const Value &value) { - Object *object = new (memoryManager) BooleanObject(value); + Object *object = new (memoryManager) BooleanObject(this, value); object->prototype = booleanPrototype; return object; } @@ -322,7 +323,7 @@ ArrayObject *ExecutionEngine::newArrayObject(ExecutionContext *ctx) Object *ExecutionEngine::newDateObject(const Value &value) { - Object *object = new (memoryManager) DateObject(value); + Object *object = new (memoryManager) DateObject(this, value); object->prototype = datePrototype; return object; } diff --git a/src/v4/qmljs_engine.h b/src/v4/qmljs_engine.h index e581beb46e..7aaefd4d97 100644 --- a/src/v4/qmljs_engine.h +++ b/src/v4/qmljs_engine.h @@ -94,6 +94,7 @@ struct TypeErrorPrototype; struct URIErrorPrototype; struct EvalFunction; struct Identifiers; +struct InternalClass; class RegExp; @@ -176,6 +177,8 @@ struct Q_V4_EXPORT ExecutionEngine QVector functions; + InternalClass *emptyClass; + ExecutionEngine(EvalISelFactory *iselFactory); ~ExecutionEngine(); diff --git a/src/v4/qv4argumentsobject.cpp b/src/v4/qv4argumentsobject.cpp index 8211ef2528..404edbbd17 100644 --- a/src/v4/qv4argumentsobject.cpp +++ b/src/v4/qv4argumentsobject.cpp @@ -45,7 +45,7 @@ namespace VM { ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount) - : context(context) + : Object(context->engine), context(context) { type = Type_ArgumentsObject; diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp index 3079c52361..3f70029cb8 100644 --- a/src/v4/qv4arrayobject.cpp +++ b/src/v4/qv4arrayobject.cpp @@ -78,8 +78,8 @@ Value ArrayCtor::call(ExecutionContext *ctx) void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isArray"), method_isArray, 1); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); diff --git a/src/v4/qv4booleanobject.cpp b/src/v4/qv4booleanobject.cpp index 640773de0c..748a6953dc 100644 --- a/src/v4/qv4booleanobject.cpp +++ b/src/v4/qv4booleanobject.cpp @@ -62,8 +62,8 @@ Value BooleanCtor::call(ExecutionContext *parentCtx, Value thisObject, Value *ar void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); diff --git a/src/v4/qv4booleanobject.h b/src/v4/qv4booleanobject.h index d0a7cdaf6b..c73ae9b306 100644 --- a/src/v4/qv4booleanobject.h +++ b/src/v4/qv4booleanobject.h @@ -60,7 +60,7 @@ struct BooleanCtor: FunctionObject struct BooleanPrototype: BooleanObject { - BooleanPrototype(): BooleanObject(Value::fromBoolean(false)) {} + BooleanPrototype(ExecutionEngine *engine): BooleanObject(engine, Value::fromBoolean(false)) {} void init(ExecutionContext *ctx, const Value &ctor); static Value method_toString(ExecutionContext *ctx); diff --git a/src/v4/qv4dateobject.h b/src/v4/qv4dateobject.h index 2c09ad28f3..15bd99f8ea 100644 --- a/src/v4/qv4dateobject.h +++ b/src/v4/qv4dateobject.h @@ -52,7 +52,7 @@ namespace VM { struct DateObject: Object { Value value; - DateObject(const Value &value): value(value) { type = Type_DateObject; } + DateObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_DateObject; } }; struct DateCtor: FunctionObject @@ -65,7 +65,7 @@ struct DateCtor: FunctionObject struct DatePrototype: DateObject { - DatePrototype(): DateObject(Value::fromDouble(qSNaN())) {} + DatePrototype(ExecutionEngine *engine): DateObject(engine, Value::fromDouble(qSNaN())) {} void init(ExecutionContext *ctx, const Value &ctor); static double getThisDate(ExecutionContext *ctx); diff --git a/src/v4/qv4errorobject.cpp b/src/v4/qv4errorobject.cpp index f381a787ad..c6b0a128c0 100644 --- a/src/v4/qv4errorobject.cpp +++ b/src/v4/qv4errorobject.cpp @@ -74,6 +74,7 @@ using namespace QQmlJS::VM; ErrorObject::ErrorObject(ExecutionEngine* engine, const Value &message) + : Object(engine) { type = Type_ErrorObject; subtype = Error; diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index bba00b8d1e..67ba4870bf 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -85,7 +85,8 @@ void Function::mark() } FunctionObject::FunctionObject(ExecutionContext *scope) - : scope(scope) + : Object(scope->engine) + , scope(scope) , name(0) , formalParameterList(0) , varList(0) @@ -258,15 +259,16 @@ Value FunctionCtor::call(ExecutionContext *ctx) void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) { - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + + defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(0)); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2); defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 1); defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 1); - defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(0)); } Value FunctionPrototype::method_toString(ExecutionContext *ctx) diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index 900cb8f6e8..87fb696411 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -51,7 +51,6 @@ #include "qv4isel_p.h" #include "qv4managed.h" #include "qv4propertydescriptor.h" -#include "qv4propertytable.h" #include "qv4objectiterator.h" #include "qv4regexp.h" diff --git a/src/v4/qv4identifier.h b/src/v4/qv4identifier.h index 3fa84d21a4..fd4ca7a9d6 100644 --- a/src/v4/qv4identifier.h +++ b/src/v4/qv4identifier.h @@ -73,8 +73,8 @@ public: str->stringIdentifier = currentIndex; if (currentIndex <= USHRT_MAX) { str->subtype = String::StringType_Identifier; - ++currentIndex; identifiers.insert(s, str); + ++currentIndex; } return str; } @@ -95,8 +95,8 @@ public: s->stringIdentifier = currentIndex; if (currentIndex <= USHRT_MAX) { s->subtype = String::StringType_Identifier; - ++currentIndex; identifiers.insert(s->toQString(), s); + ++currentIndex; } } diff --git a/src/v4/qv4internalclass.cpp b/src/v4/qv4internalclass.cpp new file mode 100644 index 0000000000..5577f097bb --- /dev/null +++ b/src/v4/qv4internalclass.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +namespace QQmlJS { +namespace VM { + + +InternalClass::InternalClass(const QQmlJS::VM::InternalClass &other) + : engine(other.engine) + , propertyTable(other.propertyTable) + , nameMap(other.nameMap) + , transitions() + , size(other.size) +{ +} + +uint InternalClass::getOrAddMember(Object *object, String *string) +{ + engine->identifierCache->toIdentifier(string); + uint id = string->stringIdentifier; + + QHash::const_iterator it = propertyTable.constFind(id); + if (it != propertyTable.constEnd()) + return it.value(); + + // new member, need to transition to a new internal class + + QHash::const_iterator tit = transitions.constFind(id); + + if (tit != transitions.constEnd()) { + object->internalClass = tit.value(); + } else { + // create a new class and add it to the tree + InternalClass *newClass = new InternalClass(*this); + newClass->propertyTable.insert(id, size); + newClass->nameMap.append(string); + ++newClass->size; + transitions.insert(id, newClass); + object->internalClass = newClass; + } + return size; +} + +void InternalClass::removeMember(Object *object, uint id) +{ + assert (propertyTable.constFind(id) != propertyTable.constEnd()); + int propIdx = propertyTable.constFind(id).value(); + assert(propIdx < size); + + int toRemove = - (int)id; + QHash::const_iterator tit = transitions.constFind(toRemove); + + if (tit != transitions.constEnd()) { + object->internalClass = tit.value(); + return; + } + + // create a new class and add it to the tree + InternalClass *newClass = new InternalClass(*this); + newClass->propertyTable.remove(id); + newClass->nameMap.remove(propIdx); + --newClass->size; + for (QHash::iterator it = newClass->propertyTable.begin(); it != newClass->propertyTable.end(); ++it) { + if ((*it) > propIdx) + --(*it); + } + + transitions.insert(toRemove, newClass); + object->internalClass = newClass; +} + +uint InternalClass::find(String *string) +{ + engine->identifierCache->toIdentifier(string); + uint id = string->stringIdentifier; + + QHash::const_iterator it = propertyTable.constFind(id); + if (it != propertyTable.constEnd()) + return it.value(); + + return UINT_MAX; +} + + +} +} diff --git a/src/v4/qv4internalclass.h b/src/v4/qv4internalclass.h new file mode 100644 index 0000000000..d7da0aed81 --- /dev/null +++ b/src/v4/qv4internalclass.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4INTERNALCLASS_H +#define QV4INTERNALCLASS_H + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct String; +struct ExecutionEngine; +struct Object; + +struct InternalClass { + ExecutionEngine *engine; + QHash propertyTable; // id to valueIndex + QVector nameMap; + QHash transitions; // id to next class, positive means add, negative delete + uint size; + + InternalClass(ExecutionEngine *engine) : engine(engine), size(0) {} + + uint getOrAddMember(Object *object, String *string); + void removeMember(Object *object, uint id); + uint find(String *s); + +private: + InternalClass(const InternalClass &other); +}; + + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif diff --git a/src/v4/qv4jsonobject.cpp b/src/v4/qv4jsonobject.cpp index 241c3c8654..74b25f3585 100644 --- a/src/v4/qv4jsonobject.cpp +++ b/src/v4/qv4jsonobject.cpp @@ -268,8 +268,6 @@ Value Parser::parseObject() bool Parser::parseMember(Object *o) { BEGIN << "parseMember"; - if (!o->members) - o->members.reset(new PropertyTable()); QString key; if (!parseString(&key)) @@ -864,7 +862,7 @@ QString Stringify::JA(ArrayObject *a) JsonObject::JsonObject(ExecutionContext *context) - : Object() + : Object(context->engine) { type = Type_JSONObject; prototype = context->engine->objectPrototype; diff --git a/src/v4/qv4mathobject.cpp b/src/v4/qv4mathobject.cpp index f0523baa45..e7c455ae1f 100644 --- a/src/v4/qv4mathobject.cpp +++ b/src/v4/qv4mathobject.cpp @@ -51,6 +51,7 @@ using namespace QQmlJS::VM; static const double qt_PI = 2.0 * ::asin(1.0); MathObject::MathObject(ExecutionContext *ctx) + : Object(ctx->engine) { type = Type_MathObject; prototype = ctx->engine->objectPrototype; diff --git a/src/v4/qv4numberobject.h b/src/v4/qv4numberobject.h index aa2c9cf2f8..b24e538eb3 100644 --- a/src/v4/qv4numberobject.h +++ b/src/v4/qv4numberobject.h @@ -60,7 +60,7 @@ struct NumberCtor: FunctionObject struct NumberPrototype: NumberObject { - NumberPrototype(): NumberObject(Value::fromDouble(0)) {} + NumberPrototype(ExecutionEngine *engine): NumberObject(engine, Value::fromDouble(0)) {} void init(ExecutionContext *ctx, const Value &ctor); static Value method_toString(ExecutionContext *ctx); diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index f260762587..31200bcc2d 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -65,9 +65,16 @@ using namespace QQmlJS::VM; -// -// Object -// +Object::Object(ExecutionEngine *engine) + : prototype(0) + , internalClass(engine->emptyClass) + , memberDataAlloc(0), memberData(0) + , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), sparseArray(0) +{ + type = Type_Object; +} + + Object::~Object() { delete [] memberData; @@ -130,8 +137,6 @@ void Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ct void Object::defineDefaultProperty(String *name, Value value) { - if (!members) - members.reset(new PropertyTable()); PropertyDescriptor *pd = insertMember(name); pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Enabled; @@ -170,8 +175,6 @@ void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name void Object::defineReadonlyProperty(String *name, Value value) { - if (!members) - members.reset(new PropertyTable()); PropertyDescriptor *pd = insertMember(name); pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Disabled; @@ -185,23 +188,16 @@ void Object::markObjects() if (prototype) prototype->mark(); - if (members) { - for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) { - if (!(*it)) - continue; - (*it)->name->mark(); - } - for (int i = 0; i < memberDataSize; ++i) { - const PropertyDescriptor &pd = memberData[i]; - if (pd.isData()) { - if (Managed *m = pd.value.asManaged()) - m->mark(); - } else if (pd.isAccessor()) { - if (pd.get) - pd.get->mark(); - if (pd.set) - pd.set->mark(); - } + for (int i = 0; i < internalClass->size; ++i) { + const PropertyDescriptor &pd = memberData[i]; + if (pd.isData()) { + if (Managed *m = pd.value.asManaged()) + m->mark(); + } else if (pd.isAccessor()) { + if (pd.get) + pd.get->mark(); + if (pd.set) + pd.set->mark(); } } markArrayObjects(); @@ -209,22 +205,16 @@ void Object::markObjects() PropertyDescriptor *Object::insertMember(String *s) { - if (!members) - members.reset(new PropertyTable); - - PropertyTableEntry *e = members->insert(s); - if (e->valueIndex == UINT_MAX) { - if (memberDataSize == memberDataAlloc) { - memberDataAlloc = qMax((uint)8, 2*memberDataAlloc); - PropertyDescriptor *newMemberData = new PropertyDescriptor[memberDataAlloc]; - memcpy(newMemberData, memberData, sizeof(PropertyDescriptor)*memberDataSize); - delete [] memberData; - memberData = newMemberData; - } - e->valueIndex = memberDataSize; - ++memberDataSize; + uint idx = internalClass->getOrAddMember(this, s); + + if (idx >= memberDataAlloc) { + memberDataAlloc = qMax((uint)8, 2*memberDataAlloc); + PropertyDescriptor *newMemberData = new PropertyDescriptor[memberDataAlloc]; + memcpy(newMemberData, memberData, sizeof(PropertyDescriptor)*idx); + delete [] memberData; + memberData = newMemberData; } - return memberData + e->valueIndex; + return memberData + idx; } // Section 8.12.1 @@ -234,11 +224,10 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *na if (idx != String::InvalidArrayIndex) return __getOwnProperty__(ctx, idx); - if (members) { - uint idx = members->find(name); - if (idx < UINT_MAX) - return memberData + idx; - } + uint member = internalClass->find(name); + if (member < UINT_MAX) + return memberData + member; + return 0; } @@ -263,11 +252,10 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, Str Object *o = this; while (o) { - if (o->members) { - uint idx = o->members->find(name); - if (idx < UINT_MAX) - return o->memberData + idx; - } + uint idx = o->internalClass->find(name); + if (idx < UINT_MAX) + return o->memberData + idx; + o = o->prototype; } return 0; @@ -307,14 +295,13 @@ Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) Object *o = this; while (o) { - if (o->members) { - uint idx = o->members->find(name); - if (idx < UINT_MAX) { - if (hasProperty) - *hasProperty = true; - return getValue(ctx, o->memberData + idx); - } + uint idx = o->internalClass->find(name); + if (idx < UINT_MAX) { + if (hasProperty) + *hasProperty = true; + return getValue(ctx, o->memberData + idx); } + o = o->prototype; } @@ -407,9 +394,6 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) cont: - if (!members) - members.reset(new PropertyTable()); - // clause 4 if (!pd && prototype) @@ -507,7 +491,7 @@ bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const name->makeIdentifier(ctx); - if (members && members->find(name) != UINT_MAX) + if (internalClass->find(name) != UINT_MAX) return true; return prototype ? prototype->__hasProperty__(ctx, name) : false; @@ -531,21 +515,19 @@ bool Object::__delete__(ExecutionContext *ctx, String *name) name->makeIdentifier(ctx); - if (members) { - if (PropertyTableEntry *entry = members->findEntry(name)) { - PropertyDescriptor &pd = memberData[entry->valueIndex]; - if (pd.isConfigurable()) { - members->remove(entry); - // ### leaves a hole in memberData - pd.type = PropertyDescriptor::Generic; - pd.writable = PropertyDescriptor::Undefined; - return true; - } - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); - return false; + uint memberIdx = internalClass->find(name); + if (memberIdx != UINT_MAX) { + PropertyDescriptor &pd = memberData[memberIdx]; + if (pd.isConfigurable()) { + internalClass->removeMember(this, name->stringIdentifier); + memmove(memberData + memberIdx, memberData + memberIdx + 1, (internalClass->size - memberIdx)*sizeof(PropertyDescriptor)); + return true; } + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + return false; } + return true; } @@ -592,7 +574,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { PropertyDescriptor *lp = memberData + ArrayObject::LengthPropertyIndex; - assert(0 == members->find(ctx->engine->id_length)); + assert(0 == internalClass->find(ctx->engine->id_length)); if (desc->isEmpty() || desc->isSubset(lp)) return true; if (!lp->isWritable() || desc->type == PropertyDescriptor::Accessor || desc->isConfigurable() || desc->isEnumerable()) @@ -612,9 +594,6 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr return true; } - if (!members) - members.reset(new PropertyTable()); - // Clause 1 current = __getOwnProperty__(ctx, name); if (!current) { @@ -982,8 +961,6 @@ void ArrayObject::init(ExecutionContext *context) { type = Type_ArrayObject; - if (!members) - members.reset(new PropertyTable()); PropertyDescriptor *pd = insertMember(context->engine->id_length); assert(pd == memberData + LengthPropertyIndex); pd->type = PropertyDescriptor::Data; diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index 7f99668fa9..964051f8db 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -51,7 +51,7 @@ #include "qv4isel_p.h" #include "qv4managed.h" #include "qv4propertydescriptor.h" -#include "qv4propertytable.h" +#include "qv4internalclass.h" #include "qv4objectiterator.h" #include "qv4regexp.h" @@ -102,8 +102,7 @@ struct URIErrorPrototype; struct Q_V4_EXPORT Object: Managed { Object *prototype; - QScopedPointer members; - uint memberDataSize; + InternalClass *internalClass; uint memberDataAlloc; PropertyDescriptor *memberData; @@ -116,11 +115,7 @@ struct Q_V4_EXPORT Object: Managed { PropertyDescriptor *arrayData; SparseArray *sparseArray; - Object() - : prototype(0) - , memberDataSize(0), memberDataAlloc(0), memberData(0) - , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), sparseArray(0) { type = Type_Object; } - + Object(ExecutionEngine *engine); virtual ~Object(); PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); @@ -315,7 +310,7 @@ protected: struct ForEachIteratorObject: Object { ObjectIterator it; ForEachIteratorObject(ExecutionContext *ctx, Object *o) - : it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) { type = Type_ForeachIteratorObject; } + : Object(ctx->engine), it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) { type = Type_ForeachIteratorObject; } Value nextPropertyName() { return it.nextPropertyNameAsString(); } @@ -325,12 +320,12 @@ protected: struct BooleanObject: Object { Value value; - BooleanObject(const Value &value): value(value) { type = Type_BooleanObject; } + BooleanObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_BooleanObject; } }; struct NumberObject: Object { Value value; - NumberObject(const Value &value): value(value) { type = Type_NumberObject; } + NumberObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_NumberObject; } }; struct ArrayObject: Object { @@ -338,7 +333,7 @@ struct ArrayObject: Object { LengthPropertyIndex = 0 }; - ArrayObject(ExecutionContext *ctx) { init(ctx); } + ArrayObject(ExecutionContext *ctx) : Object(ctx->engine) { init(ctx); } void init(ExecutionContext *context); }; diff --git a/src/v4/qv4objectiterator.cpp b/src/v4/qv4objectiterator.cpp index 7bcf87245a..d7c1a19852 100644 --- a/src/v4/qv4objectiterator.cpp +++ b/src/v4/qv4objectiterator.cpp @@ -41,6 +41,7 @@ #include "qv4objectiterator.h" #include "qv4object.h" #include "qv4stringobject.h" +#include "qv4identifier.h" namespace QQmlJS { namespace VM { @@ -51,11 +52,15 @@ ObjectIterator::ObjectIterator(ExecutionContext *context, Object *o, uint flags) , current(o) , arrayNode(0) , arrayIndex(0) - , tableIndex(0) + , currentClass(0) + , memberIndex(0) , flags(flags) { - if (current && current->asStringObject()) - this->flags |= CurrentIsString; + if (current) { + currentClass = current->internalClass; + if (current->asStringObject()) + this->flags |= CurrentIsString; + } } PropertyDescriptor *ObjectIterator::next(String **name, uint *index) @@ -110,7 +115,7 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) } } - if (!current->members || tableIndex >= (uint)current->members->_propertyCount) { + if (memberIndex == currentClass->size) { if (flags & WithProtoChain) current = current->prototype; else @@ -122,19 +127,20 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) arrayIndex = 0; - tableIndex = 0; + memberIndex = 0; + if (current) + currentClass = current->internalClass; continue; } - PropertyTableEntry *pt = current->members->_properties[tableIndex]; - ++tableIndex; + String *n = currentClass->nameMap.at(memberIndex); + assert(n); // ### check that it's not a repeated attribute - if (pt) { - PropertyDescriptor *pd = current->memberData + pt->valueIndex; - if (!(flags & EnumberableOnly) || pd->isEnumerable()) { - *name = pt->name; - p = pd; - return p; - } + + p = current->memberData + memberIndex; + ++memberIndex; + if (!(flags & EnumberableOnly) || p->isEnumerable()) { + *name = n; + return p; } } return 0; diff --git a/src/v4/qv4objectiterator.h b/src/v4/qv4objectiterator.h index a463eb8798..c2e61dc339 100644 --- a/src/v4/qv4objectiterator.h +++ b/src/v4/qv4objectiterator.h @@ -42,6 +42,7 @@ #define QV4OBJECTITERATOR_H #include "qmljs_value.h" +#include QT_BEGIN_NAMESPACE @@ -66,7 +67,8 @@ struct ObjectIterator Object *current; SparseArrayNode *arrayNode; uint arrayIndex; - uint tableIndex; + InternalClass *currentClass; + uint memberIndex; uint flags; ObjectIterator(ExecutionContext *context, Object *o, uint flags); diff --git a/src/v4/qv4objectproto.h b/src/v4/qv4objectproto.h index 585ccb4e6c..679c271127 100644 --- a/src/v4/qv4objectproto.h +++ b/src/v4/qv4objectproto.h @@ -60,6 +60,8 @@ struct ObjectCtor: FunctionObject struct ObjectPrototype: Object { + ObjectPrototype(ExecutionEngine *engine) : Object(engine) {} + void init(ExecutionContext *ctx, const Value &ctor); static Value method_getPrototypeOf(ExecutionContext *ctx); diff --git a/src/v4/qv4propertytable.h b/src/v4/qv4propertytable.h deleted file mode 100644 index 79e4c776f5..0000000000 --- a/src/v4/qv4propertytable.h +++ /dev/null @@ -1,234 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4PROPERTYTABLE_H -#define QV4PROPERTYTABLE_H - -#include "qmljs_value.h" -#include "qv4propertydescriptor.h" - -QT_BEGIN_NAMESPACE - -namespace QQmlJS { -namespace VM { - -struct ObjectIterator; - -struct PropertyTableEntry { - String *name; - uint valueIndex; - PropertyTableEntry *next; - int index; - - inline PropertyTableEntry(String *name) - : name(name), - valueIndex(UINT_MAX), - next(0), - index(-1) - { } - - inline unsigned hashValue() const { return name->hashValue(); } -}; - -class PropertyTable -{ - Q_DISABLE_COPY(PropertyTable) - friend struct ArrayObject; - -public: - PropertyTable() - : _properties(0) - , _buckets(0) - , _freeList(0) - , _propertyCount(0) - , _bucketCount(0) - , _primeIdx(-1) - , _allocated(0) - {} - - ~PropertyTable() - { - qDeleteAll(_properties, _properties + _propertyCount); - delete[] _properties; - delete[] _buckets; - } - - typedef PropertyTableEntry **iterator; - inline iterator begin() const { return _properties; } - inline iterator end() const { return _properties + _propertyCount; } - - void remove(PropertyTableEntry *prop) - { - PropertyTableEntry **bucket = _buckets + (prop->hashValue() % _bucketCount); - if (*bucket == prop) { - *bucket = prop->next; - } else { - for (PropertyTableEntry *it = *bucket; it; it = it->next) { - if (it->next == prop) { - it->next = it->next->next; - break; - } - } - } - - _properties[prop->index] = 0; - prop->next = _freeList; - _freeList = prop; - } - - PropertyTableEntry *findEntry(String *name) const - { - if (_properties) { - for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { - if (prop && prop->name->isEqualTo(name)) - return prop; - } - } - - return 0; - } - - uint find(String *name) - { - if (_properties) { - for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) { - if (prop && prop->name->isEqualTo(name)) - return prop->valueIndex; - } - } - - return UINT_MAX; - } - - PropertyTableEntry *insert(String *name) - { - if (PropertyTableEntry *prop = findEntry(name)) - return prop; - - if (_propertyCount == _allocated) { - if (! _allocated) - _allocated = 4; - else - _allocated *= 2; - - PropertyTableEntry **properties = new PropertyTableEntry*[_allocated]; - std::copy(_properties, _properties + _propertyCount, properties); - delete[] _properties; - _properties = properties; - } - - PropertyTableEntry *prop; - if (_freeList) { - prop = _freeList; - prop->name = name; - _freeList = _freeList->next; - } else { - prop = new PropertyTableEntry(name); - } - - prop->index = _propertyCount; - _properties[_propertyCount] = prop; - ++_propertyCount; - - if (! _buckets || 3 * _propertyCount >= 2 * _bucketCount) { - rehash(); - } else { - PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; - prop->next = bucket; - bucket = prop; - } - - return prop; -// prop->valueIndex = values.size(); -// values.resize(values.size() + 1); -// return values.data() + prop->valueIndex; - } - -private: - void rehash() - { - _bucketCount = nextPrime(); - - delete[] _buckets; - _buckets = new PropertyTableEntry *[_bucketCount]; - std::fill(_buckets, _buckets + _bucketCount, (PropertyTableEntry *) 0); - - for (int i = 0; i < _propertyCount; ++i) { - PropertyTableEntry *prop = _properties[i]; - if (prop) { - PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount]; - prop->next = bucket; - bucket = prop; - } - } - } - - inline int nextPrime() - { - // IMPORTANT: do not add more primes without checking if _primeIdx needs more bits! - static const int primes[] = { - 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853, 25717, 51437, 102877 - }; - - if (_primeIdx < (int) (sizeof(primes)/sizeof(int))) - return primes[++_primeIdx]; - else - return _bucketCount * 2 + 1; // Yes, we're degrading here. But who needs more than about 68000 properties? - } - -private: - friend struct ObjectIterator; - friend struct Object; - - PropertyTableEntry **_properties; - PropertyTableEntry **_buckets; - PropertyTableEntry *_freeList; - int _propertyCount; - int _bucketCount; - int _primeIdx: 5; - int _allocated: 27; -}; - -} // namespace VM -} // namespace QQmlJS - -QT_END_NAMESPACE - -#endif diff --git a/src/v4/qv4regexpobject.cpp b/src/v4/qv4regexpobject.cpp index 8319a0cee0..2762c98a62 100644 --- a/src/v4/qv4regexpobject.cpp +++ b/src/v4/qv4regexpobject.cpp @@ -65,13 +65,12 @@ using namespace QQmlJS::VM; RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global) - : value(value) + : Object(engine) + , value(value) , global(global) { type = Type_RegExpObject; - if (!members) - members.reset(new PropertyTable()); PropertyDescriptor *lastIndexProperty = insertMember(engine->newIdentifier(QStringLiteral("lastIndex"))); lastIndexProperty->type = PropertyDescriptor::Data; lastIndexProperty->writable = PropertyDescriptor::Enabled; @@ -88,7 +87,7 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bo PropertyDescriptor *RegExpObject::lastIndexProperty(ExecutionContext *ctx) { - assert(0 == members->find(ctx->engine->newIdentifier(QStringLiteral("lastIndex")))); + assert(0 == internalClass->find(ctx->engine->newIdentifier(QStringLiteral("lastIndex")))); return &memberData[0]; } diff --git a/src/v4/qv4regexpobject.h b/src/v4/qv4regexpobject.h index 6c7f583b50..dcf6120f8a 100644 --- a/src/v4/qv4regexpobject.h +++ b/src/v4/qv4regexpobject.h @@ -50,7 +50,6 @@ #include "qv4isel_p.h" #include "qv4managed.h" #include "qv4propertydescriptor.h" -#include "qv4propertytable.h" #include "qv4objectiterator.h" #include "qv4regexp.h" diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp index dee5dae0dc..1e226b1000 100644 --- a/src/v4/qv4stringobject.cpp +++ b/src/v4/qv4stringobject.cpp @@ -76,7 +76,7 @@ using namespace QQmlJS::VM; StringObject::StringObject(ExecutionContext *ctx, const Value &value) - : value(value) + : Object(ctx->engine), value(value) { type = Type_StringObject; diff --git a/src/v4/v4.pro b/src/v4/v4.pro index 79b331683a..2197ae343e 100644 --- a/src/v4/v4.pro +++ b/src/v4/v4.pro @@ -29,6 +29,7 @@ SOURCES += \ debugging.cpp \ qv4mm.cpp \ qv4managed.cpp \ + qv4internalclass.cpp \ qv4sparsearray.cpp \ qv4arrayobject.cpp \ qv4argumentsobject.cpp \ @@ -65,6 +66,7 @@ HEADERS += \ qv4identifier.h \ qv4mm.h \ qv4managed.h \ + qv4internalclass.h \ qv4sparsearray.h \ qv4arrayobject.h \ qv4argumentsobject.h \ @@ -82,7 +84,6 @@ HEADERS += \ qv4stringobject.h \ qv4string.h \ qv4propertydescriptor.h \ - qv4propertytable.h \ qv4objectiterator.h \ qv4regexp.h -- cgit v1.2.3 From 8c10aeb9fa16012f6ebf0c48d44dfbe133fc134d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 12 Feb 2013 11:05:18 +0100 Subject: Fix identifier handling in strings Give identifiers a full 32bit range, so we don't accidentally get into overflows. Make sure UINT_MAX can be used as an identifier. Change-Id: I7d031f9eff0ea2edd7d3e182670fbd37aaf04040 Reviewed-by: Simon Hausmann --- src/v4/qv4identifier.h | 13 +++++-------- src/v4/qv4internalclass.cpp | 4 ++-- src/v4/qv4object.cpp | 18 +++++++++--------- src/v4/qv4string.cpp | 13 +++++++------ src/v4/qv4string.h | 24 +++++++++--------------- 5 files changed, 32 insertions(+), 40 deletions(-) diff --git a/src/v4/qv4identifier.h b/src/v4/qv4identifier.h index fd4ca7a9d6..efd31dae2d 100644 --- a/src/v4/qv4identifier.h +++ b/src/v4/qv4identifier.h @@ -70,9 +70,8 @@ public: if (str->subtype == String::StringType_ArrayIndex) return str; - str->stringIdentifier = currentIndex; + str->identifier = currentIndex; if (currentIndex <= USHRT_MAX) { - str->subtype = String::StringType_Identifier; identifiers.insert(s, str); ++currentIndex; } @@ -80,21 +79,19 @@ public: } void toIdentifier(String *s) { - if (s->subtype >= String::StringType_Identifier) + if (s->identifier != UINT_MAX) return; if (s->subtype == String::StringType_Unknown) s->createHashValue(); - if (s->subtype >= String::StringType_Identifier) + if (s->subtype == String::StringType_ArrayIndex) return; QHash::const_iterator it = identifiers.find(s->toQString()); if (it != identifiers.constEnd()) { - s->subtype = String::StringType_Identifier; - s->stringIdentifier = (*it)->stringIdentifier; + s->identifier = (*it)->identifier; return; } - s->stringIdentifier = currentIndex; + s->identifier = currentIndex; if (currentIndex <= USHRT_MAX) { - s->subtype = String::StringType_Identifier; identifiers.insert(s->toQString(), s); ++currentIndex; } diff --git a/src/v4/qv4internalclass.cpp b/src/v4/qv4internalclass.cpp index 5577f097bb..d062e8d581 100644 --- a/src/v4/qv4internalclass.cpp +++ b/src/v4/qv4internalclass.cpp @@ -60,7 +60,7 @@ InternalClass::InternalClass(const QQmlJS::VM::InternalClass &other) uint InternalClass::getOrAddMember(Object *object, String *string) { engine->identifierCache->toIdentifier(string); - uint id = string->stringIdentifier; + uint id = string->identifier; QHash::const_iterator it = propertyTable.constFind(id); if (it != propertyTable.constEnd()) @@ -115,7 +115,7 @@ void InternalClass::removeMember(Object *object, uint id) uint InternalClass::find(String *string) { engine->identifierCache->toIdentifier(string); - uint id = string->stringIdentifier; + uint id = string->identifier; QHash::const_iterator it = propertyTable.constFind(id); if (it != propertyTable.constEnd()) diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 31200bcc2d..25a453f6f0 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -69,7 +69,7 @@ Object::Object(ExecutionEngine *engine) : prototype(0) , internalClass(engine->emptyClass) , memberDataAlloc(0), memberData(0) - , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), sparseArray(0) + , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayData(0), sparseArray(0) { type = Type_Object; } @@ -221,7 +221,7 @@ PropertyDescriptor *Object::insertMember(String *s) PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *name) { uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) + if (idx != UINT_MAX) return __getOwnProperty__(ctx, idx); uint member = internalClass->find(name); @@ -246,7 +246,7 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, uint index PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name) { uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) + if (idx != UINT_MAX) return __getPropertyDescriptor__(ctx, idx); @@ -282,7 +282,7 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, uin Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) + if (idx != UINT_MAX) return __get__(ctx, idx, hasProperty); name->makeIdentifier(ctx); @@ -346,7 +346,7 @@ Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) void Object::__put__(ExecutionContext *ctx, String *name, Value value) { uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) + if (idx != UINT_MAX) return __put__(ctx, idx, value); name->makeIdentifier(ctx); @@ -486,7 +486,7 @@ void Object::__put__(ExecutionContext *ctx, uint index, Value value) bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const { uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) + if (idx != UINT_MAX) return __hasProperty__(ctx, idx); name->makeIdentifier(ctx); @@ -510,7 +510,7 @@ bool Object::__hasProperty__(const ExecutionContext *ctx, uint index) const bool Object::__delete__(ExecutionContext *ctx, String *name) { uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) + if (idx != UINT_MAX) return __delete__(ctx, idx); name->makeIdentifier(ctx); @@ -519,7 +519,7 @@ bool Object::__delete__(ExecutionContext *ctx, String *name) if (memberIdx != UINT_MAX) { PropertyDescriptor &pd = memberData[memberIdx]; if (pd.isConfigurable()) { - internalClass->removeMember(this, name->stringIdentifier); + internalClass->removeMember(this, name->identifier); memmove(memberData + memberIdx, memberData + memberIdx + 1, (internalClass->size - memberIdx)*sizeof(PropertyDescriptor)); return true; } @@ -565,7 +565,7 @@ bool Object::__delete__(ExecutionContext *ctx, uint index) bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc) { uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) + if (idx != UINT_MAX) return __defineOwnProperty__(ctx, idx, desc); name->makeIdentifier(ctx); diff --git a/src/v4/qv4string.cpp b/src/v4/qv4string.cpp index 6829c01029..22b7354a23 100644 --- a/src/v4/qv4string.cpp +++ b/src/v4/qv4string.cpp @@ -52,20 +52,20 @@ static uint toArrayIndex(const QChar *ch, const QChar *end, bool *ok) *ok = false; uint i = ch->unicode() - '0'; if (i > 9) - return String::InvalidArrayIndex; + return UINT_MAX; ++ch; // reject "01", "001", ... if (i == 0 && ch != end) - return String::InvalidArrayIndex; + return UINT_MAX; while (ch < end) { uint x = ch->unicode() - '0'; if (x > 9) - return String::InvalidArrayIndex; + return UINT_MAX; uint n = i*10 + x; if (n < i) // overflow - return String::InvalidArrayIndex; + return UINT_MAX; i = n; ++ch; } @@ -79,9 +79,10 @@ uint String::toUInt(bool *ok) const if (subtype == StringType_Unknown) createHashValue(); - if (subtype == StringType_ArrayIndex) + if (subtype >= StringType_UInt) return stringHash; + // ### this conversion shouldn't be required double d = __qmljs_string_to_number(this); uint l = (uint)d; if (d == l) @@ -104,7 +105,7 @@ void String::createHashValue() const bool ok; stringHash = toArrayIndex(ch, end, &ok); if (ok) { - subtype = StringType_ArrayIndex; + subtype = (stringHash == UINT_MAX) ? StringType_UInt : StringType_ArrayIndex; return; } diff --git a/src/v4/qv4string.h b/src/v4/qv4string.h index 383c054e5e..81566bdeb3 100644 --- a/src/v4/qv4string.h +++ b/src/v4/qv4string.h @@ -55,12 +55,12 @@ struct String : public Managed { enum StringType { StringType_Unknown, StringType_Regular, - StringType_Identifier, + StringType_UInt, StringType_ArrayIndex }; String(const QString &text) - : _text(text), stringHash(InvalidHashValue) + : _text(text), stringHash(UINT_MAX), identifier(UINT_MAX) { type = Type_String; subtype = StringType_Unknown; } inline bool isEqualTo(const String *other) const { @@ -68,12 +68,11 @@ struct String : public Managed { return true; if (hashValue() != other->hashValue()) return false; - if (subtype == other->subtype) { - if (subtype == StringType_ArrayIndex) - return true; - if (subtype == StringType_Identifier) - return stringIdentifier == other->stringIdentifier; - } + if (identifier != UINT_MAX && identifier == other->identifier) + return true; + if (subtype >= StringType_UInt && subtype == other->subtype) + return true; + return toQString() == other->toQString(); } @@ -87,10 +86,6 @@ struct String : public Managed { return stringHash; } - enum { - InvalidArrayIndex = 0xffffffff, - InvalidHashValue = 0xffffffff - }; uint asArrayIndex() const { if (subtype == StringType_Unknown) createHashValue(); @@ -101,19 +96,18 @@ struct String : public Managed { uint toUInt(bool *ok) const; void makeIdentifier(const ExecutionContext *ctx) { - if (subtype == StringType_Identifier) + if (identifier != UINT_MAX) return; makeIdentifierImpl(ctx); } void makeIdentifierImpl(const ExecutionContext *ctx); -private: - friend struct Identifiers; void createHashValue() const; QString _text; mutable uint stringHash; + mutable uint identifier; }; } // namespace VM -- cgit v1.2.3 From 194c582354785f9a62b2e0c947b9b998a8596898 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 11 Feb 2013 16:41:42 +0100 Subject: Fixes to the memory manager Don't use the location of the bitfield to store the nextFree pointer. This can easily lead to trouble when changing the layout of the bitfield, as a we rely on inUse being 0. Rather use the location where the vtable is being stored. Fix the sweep method in the memory manager to correctly insert all objects into the free list. Fix mark, to really discard objects that are out of bounds. Change-Id: I902e46907043c9d4288d0e3d1564460b5b265c4f Reviewed-by: Simon Hausmann --- src/v4/qv4managed.cpp | 3 +-- src/v4/qv4managed.h | 54 +++++++++++++++++++++++---------------------------- src/v4/qv4mm.cpp | 49 ++++++++++++++++++++++++++++++---------------- 3 files changed, 57 insertions(+), 49 deletions(-) diff --git a/src/v4/qv4managed.cpp b/src/v4/qv4managed.cpp index e0448a4848..656531c823 100644 --- a/src/v4/qv4managed.cpp +++ b/src/v4/qv4managed.cpp @@ -47,8 +47,7 @@ using namespace QQmlJS::VM; Managed::~Managed() { - nextFree = 0; - inUse = 0; + _data = 0; } void *Managed::operator new(size_t size, MemoryManager *mm) diff --git a/src/v4/qv4managed.h b/src/v4/qv4managed.h index 7aa1452d12..14063de321 100644 --- a/src/v4/qv4managed.h +++ b/src/v4/qv4managed.h @@ -81,21 +81,8 @@ private: protected: Managed() - : markBit(0) - , inUse(1) - , extensible(1) - , isNonStrictArgumentsObject(0) - , isBuiltinFunction(0) - , needsActivation(0) - , usesArgumentsObject(0) - , strictMode(0) - , type(Type_Invalid) - , subtype(0) - , stringIdentifier(0) -#if CPU(X86_64) - , unused(0) -#endif - {} + : _data(0) + { inUse = 1; extensible = 1; } virtual ~Managed(); public: @@ -147,26 +134,33 @@ public: QString className() const; + Managed **nextFreeRef() { + return reinterpret_cast(this); + } + Managed *nextFree() { + return *reinterpret_cast(this); + } + void setNextFree(Managed *m) { + *reinterpret_cast(this) = m; + } + protected: virtual void markObjects() {} union { - Managed *nextFree; + uint _data; struct { - quintptr markBit : 1; - quintptr inUse : 1; - quintptr extensible : 1; // used by Object - quintptr isNonStrictArgumentsObject : 1; - quintptr isBuiltinFunction : 1; // used by FunctionObject - quintptr needsActivation : 1; // used by FunctionObject - quintptr usesArgumentsObject : 1; // used by FunctionObject - quintptr strictMode : 1; // used by FunctionObject - quintptr type : 5; - mutable quintptr subtype : 3; - mutable quintptr stringIdentifier : 16; -#if CPU(X86_64) - quintptr unused : 32; -#endif + uint markBit : 1; + uint inUse : 1; + uint extensible : 1; // used by Object + uint isNonStrictArgumentsObject : 1; + uint isBuiltinFunction : 1; // used by FunctionObject + uint needsActivation : 1; // used by FunctionObject + uint usesArgumentsObject : 1; // used by FunctionObject + uint strictMode : 1; // used by FunctionObject + uint type : 5; + mutable uint subtype : 3; + uint unused : 16; }; }; diff --git a/src/v4/qv4mm.cpp b/src/v4/qv4mm.cpp index 2724700032..81dd39a8a3 100644 --- a/src/v4/qv4mm.cpp +++ b/src/v4/qv4mm.cpp @@ -90,6 +90,11 @@ struct MemoryManager::Data } }; +#define SCRIBBLE(obj, c, size) \ + if (m_d->scribble) \ + ::memset((void *)(obj + 1), c, size - sizeof(Managed)); + + namespace QQmlJS { namespace VM { bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::Chunk &b) @@ -152,10 +157,9 @@ Managed *MemoryManager::alloc(std::size_t size) Managed **last = &m_d->smallItems[pos]; while (chunk <= end) { Managed *o = reinterpret_cast(chunk); - o->inUse = 0; - o->markBit = 0; + o->_data = 0; *last = o; - last = &o->nextFree; + last = o->nextFreeRef(); chunk += size; } *last = 0; @@ -163,14 +167,10 @@ Managed *MemoryManager::alloc(std::size_t size) } found: - m_d->smallItems[pos] = m->nextFree; + m_d->smallItems[pos] = m->nextFree(); return m; } -#define SCRIBBLE(obj, c, size) \ - if (m_d->scribble) \ - ::memset((void *)(obj + 1), c, size - sizeof(Managed)); - void MemoryManager::mark() { m_d->engine->markObjects(); @@ -181,6 +181,20 @@ void MemoryManager::mark() for (QHash::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it) it.key()->mark(); +#if CPU(X86_64) + // push all caller saved registers to the stack, so we can find the objects living in these registers + quintptr regs[5]; + asm( + "mov %%rbp, %0\n" + "mov %%r12, %1\n" + "mov %%r13, %2\n" + "mov %%r14, %3\n" + "mov %%r15, %4\n" + : "=m" (regs[0]), "=m" (regs[1]), "=m" (regs[2]), "=m" (regs[3]), "=m" (regs[4]) + : + : + ); +#endif collectFromStack(); return; @@ -203,23 +217,22 @@ std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t Managed **f = &m_d->smallItems[size >> 4]; - for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize - size; chunk <= chunkEnd; ) { + for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize - size; chunk <= chunkEnd; chunk += size) { Managed *m = reinterpret_cast(chunk); // qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s", // chunk, m->size, (m->inUse ? "yes" : "no"), (m->markBit ? "true" : "false")); assert((intptr_t) chunk % 16 == 0); - chunk = chunk + size; if (m->inUse) { if (m->markBit) { m->markBit = 0; } else { -// qDebug() << "-- collecting it." << m << *f << &m->nextFree; +// qDebug() << "-- collecting it." << m << *f << m->nextFree(); m->~Managed(); - m->nextFree = *f; - f = &m->nextFree; + m->setNextFree(*f); + *f = m; SCRIBBLE(m, 0x99, size); ++freedCount; } @@ -360,7 +373,7 @@ void MemoryManager::collectFromStack() const // qDebug() << "stack:" << hex << stackTop << stackSize << (stackTop + stackSize); quintptr *current = (&valueOnStack) + 1; -// qDebug() << "collectFromStack" << top << current << &valueOnStack; +// qDebug() << "collectFromStack";// << top << current << &valueOnStack; char** heapChunkBoundaries = (char**)alloca(m_d->heapChunks.count() * 2 * sizeof(char*)); char** heapChunkBoundariesEnd = heapChunkBoundaries + 2 * m_d->heapChunks.count(); @@ -370,6 +383,7 @@ void MemoryManager::collectFromStack() const heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()); heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()) + it->memory.size() - it->chunkSize; } + assert(i == m_d->heapChunks.count() * 2); for (; current < top; ++current) { char* genericPtr = @@ -379,14 +393,15 @@ void MemoryManager::collectFromStack() const reinterpret_cast(*current); #endif - if (genericPtr < *heapChunkBoundaries || genericPtr > *heapChunkBoundariesEnd) + if (genericPtr < *heapChunkBoundaries || genericPtr > *(heapChunkBoundariesEnd - 1)) continue; int index = qLowerBound(heapChunkBoundaries, heapChunkBoundariesEnd, genericPtr) - heapChunkBoundaries; // An odd index means the pointer is _before_ the end of a heap chunk and therefore valid. + assert(index >= 0 && index < m_d->heapChunks.count() * 2); if (index & 1) { int size = m_d->heapChunks.at(index >> 1).chunkSize; Managed *m = reinterpret_cast(genericPtr); -// qDebug() << " inside" << size << m; +// qDebug() << " inside" << size; if (((quintptr)m - (quintptr)heapChunkBoundaries[index-1]) % size) // wrongly aligned value, skip it @@ -396,8 +411,8 @@ void MemoryManager::collectFromStack() const // Skip pointers to already freed objects, they are bogus as well continue; - m->mark(); // qDebug() << " marking"; + m->mark(); } } } -- cgit v1.2.3 From 6cd0e478860382d4ab5cf50383a464f0780893b4 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 12 Feb 2013 13:14:19 +0100 Subject: Saner handling of property deleting with internal classes Build up a new internal class from the empty class with the deleted member removed. The add a transition for removal from the current class to the new one. This avoids bad behavior in pathological cases where someone would remove a member, then add it again etc. Change-Id: I9f2138785780bc3b9f529056e27abe51c1055bbe Reviewed-by: Simon Hausmann --- src/v4/qv4internalclass.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/v4/qv4internalclass.cpp b/src/v4/qv4internalclass.cpp index d062e8d581..b192459117 100644 --- a/src/v4/qv4internalclass.cpp +++ b/src/v4/qv4internalclass.cpp @@ -99,17 +99,14 @@ void InternalClass::removeMember(Object *object, uint id) } // create a new class and add it to the tree - InternalClass *newClass = new InternalClass(*this); - newClass->propertyTable.remove(id); - newClass->nameMap.remove(propIdx); - --newClass->size; - for (QHash::iterator it = newClass->propertyTable.begin(); it != newClass->propertyTable.end(); ++it) { - if ((*it) > propIdx) - --(*it); + object->internalClass = engine->emptyClass; + for (int i = 0; i < nameMap.size(); ++i) { + if (i == propIdx) + continue; + object->internalClass->getOrAddMember(object, nameMap.at(i)); } - transitions.insert(toRemove, newClass); - object->internalClass = newClass; + transitions.insert(toRemove, object->internalClass); } uint InternalClass::find(String *string) -- cgit v1.2.3 From 4be3e757348fc393cb93dbf8f9fc81ea6d57a408 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 12 Feb 2013 16:23:52 +0100 Subject: Implement a first iteration of the fast property lookup scheme Fast lookups still require a function call, and will only work for properties defined on the object itself. Properties of the prototype will still be slow. Change-Id: I07c601998d312b1bd8e9977708d3375bf72df3e3 Reviewed-by: Simon Hausmann --- src/v4/qmljs_environment.cpp | 7 ++++ src/v4/qmljs_environment.h | 2 + src/v4/qmljs_runtime.cpp | 92 ++++++++++++++++++++++++++++++++++++++++++++ src/v4/qmljs_runtime.h | 5 +++ src/v4/qv4functionobject.cpp | 3 +- src/v4/qv4functionobject.h | 13 ++++++- src/v4/qv4globalobject.cpp | 8 ++-- src/v4/qv4isel_masm.cpp | 52 ++++++++++++++++++++++--- src/v4/qv4isel_masm_p.h | 3 ++ src/v4/qv4isel_p.cpp | 1 + src/v4/qv4isel_p.h | 4 ++ src/v4/qv4object.cpp | 25 ++++++++++++ src/v4/qv4object.h | 3 ++ tests/fact.2.js | 2 +- tests/property_lookup.js | 9 +++++ tools/v4/main.cpp | 1 + 16 files changed, 217 insertions(+), 13 deletions(-) create mode 100644 tests/property_lookup.js diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp index 76d08337c8..b1339b6a23 100644 --- a/src/v4/qmljs_environment.cpp +++ b/src/v4/qmljs_environment.cpp @@ -217,6 +217,8 @@ void ExecutionContext::init(ExecutionEngine *eng) thisObject = eng->globalObject; function = 0; + lookups = 0; + arguments = 0; argumentCount = 0; locals = 0; @@ -235,6 +237,8 @@ void ExecutionContext::init(ExecutionContext *p, Object *with) thisObject = p->thisObject; function = 0; + lookups = parent->lookups; + arguments = 0; argumentCount = 0; locals = 0; @@ -504,6 +508,9 @@ void ExecutionContext::initCallContext(ExecutionContext *parent) strictMode = function->strictMode; + if (function->function) + lookups = function->function->lookups; + if (function->varCount) { locals = reinterpret_cast(this + 1); std::fill(locals, locals + function->varCount, Value::undefinedValue()); diff --git a/src/v4/qmljs_environment.h b/src/v4/qmljs_environment.h index 5dc4a56609..3c4e1fb690 100644 --- a/src/v4/qmljs_environment.h +++ b/src/v4/qmljs_environment.h @@ -54,6 +54,7 @@ struct Object; struct ExecutionEngine; struct ExecutionContext; struct DeclarativeEnvironment; +struct Lookup; struct Q_V4_EXPORT DiagnosticMessage { @@ -81,6 +82,7 @@ struct ExecutionContext Value thisObject; FunctionObject *function; + Lookup *lookups; Value *arguments; unsigned int argumentCount; diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 1fcdf02d4f..4c32b85921 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -676,6 +676,61 @@ Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name) return ctx->getProperty(name); } +Value __qmljs_get_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex) +{ + Lookup *l = ctx->lookups + lookupIndex; + if (Object *o = object.asObject()) { + if (o->internalClass == l->internalClass) + return o->getValue(ctx, o->memberData + l->index); + + uint idx = o->internalClass->find(l->name); + if (idx < UINT_MAX) { + l->internalClass = o->internalClass; + l->index = idx; + return o->getValue(ctx, o->memberData + idx); + } + + return object.objectValue()->__get__(ctx, l->name); + } else if (object.isString() && l->name->isEqualTo(ctx->engine->id_length)) { + return Value::fromInt32(object.stringValue()->toQString().length()); + } else { + object = __qmljs_to_object(object, ctx); + + if (object.isObject()) { + return object.objectValue()->__get__(ctx, l->name); + } else { + ctx->throwTypeError(); + return Value::undefinedValue(); + } + } +} + +void __qmljs_set_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex, Value value) +{ + if (! object.isObject()) + object = __qmljs_to_object(object, ctx); + + Object *o = object.objectValue(); + Lookup *l = ctx->lookups + lookupIndex; + + if (l->index != ArrayObject::LengthPropertyIndex || !o->isArrayObject()) { + if (o->internalClass == l->internalClass) { + o->putValue(ctx, o->memberData + l->index, value); + return; + } + + uint idx = o->internalClass->find(l->name); + if (idx < UINT_MAX) { + l->internalClass = o->internalClass; + l->index = idx; + return o->putValue(ctx, o->memberData + idx, value); + } + } + + o->__put__(ctx, l->name, value); +} + + Value __qmljs_get_thisObject(ExecutionContext *ctx) { return ctx->thisObject; @@ -770,6 +825,43 @@ Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String return o->call(context, thisObject, args, argc); } +Value __qmljs_call_property_lookup(ExecutionContext *context, Value thisObject, uint index, Value *args, int argc) +{ + Lookup *l = context->lookups + index; + + Object *baseObject; + if (thisObject.isString()) { + baseObject = context->engine->stringPrototype; + } else { + if (!thisObject.isObject()) + thisObject = __qmljs_to_object(thisObject, context); + + assert(thisObject.isObject()); + baseObject = thisObject.objectValue(); + } + + + Value func; + + if (baseObject->internalClass == l->internalClass) { + func = baseObject->getValue(context, baseObject->memberData + l->index); + } else { + uint idx = baseObject->internalClass->find(l->name); + if (idx < UINT_MAX) { + l->internalClass = baseObject->internalClass; + l->index = idx; + func = baseObject->getValue(context, baseObject->memberData + idx); + } else { + func = baseObject->__get__(context, l->name); + } + } + FunctionObject *o = func.asFunctionObject(); + if (!o) + context->throwTypeError(); + + return o->call(context, thisObject, args, argc); +} + Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc) { Value thisObject = that; diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 2742692c51..b2efc15e1d 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -93,6 +93,7 @@ extern "C" { // context Value __qmljs_call_activation_property(ExecutionContext *, String *name, Value *args, int argc); Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, Value *args, int argc); +Value __qmljs_call_property_lookup(ExecutionContext *context, Value thisObject, uint index, Value *args, int argc); Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc); Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc); @@ -168,6 +169,10 @@ void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Val Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name); Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name); +Value __qmljs_get_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex); +void __qmljs_set_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex, Value value); + + Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index); void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value value); diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 67ba4870bf..94f923a47e 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -92,6 +92,7 @@ FunctionObject::FunctionObject(ExecutionContext *scope) , varList(0) , formalParameterCount(0) , varCount(0) + , function(0) { prototype = scope->engine->functionPrototype; @@ -339,8 +340,8 @@ Value FunctionPrototype::method_bind(ExecutionContext *ctx) ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) : FunctionObject(scope) - , function(function) { + this->function = function; assert(function); assert(function->code); diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index 87fb696411..9a4967488a 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -100,6 +100,13 @@ struct ReferenceErrorPrototype; struct SyntaxErrorPrototype; struct TypeErrorPrototype; struct URIErrorPrototype; +struct InternalClass; + +struct Lookup { + InternalClass *internalClass; + uint index; + String *name; +}; struct Function { String *name; @@ -113,6 +120,8 @@ struct Function { QVector generatedValues; QVector identifiers; + Lookup *lookups; + bool hasNestedFunctions : 1; bool hasDirectEval : 1; bool usesArgumentsObject : 1; @@ -122,6 +131,7 @@ struct Function { : name(name) , code(0) , codeData(0) + , lookups(0) , hasNestedFunctions(0) , hasDirectEval(false) , usesArgumentsObject(false) @@ -141,6 +151,7 @@ struct Q_V4_EXPORT FunctionObject: Object { String * const *varList; unsigned int formalParameterCount; unsigned int varCount; + VM::Function *function; FunctionObject(ExecutionContext *scope); @@ -196,8 +207,6 @@ struct BuiltinFunction: FunctionObject { }; struct ScriptFunction: FunctionObject { - VM::Function *function; - ScriptFunction(ExecutionContext *scope, VM::Function *function); virtual ~ScriptFunction(); diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp index 2d7bb71e10..8a0f779c51 100644 --- a/src/v4/qv4globalobject.cpp +++ b/src/v4/qv4globalobject.cpp @@ -375,15 +375,15 @@ Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Va bool strict = f->isStrict || (directCall && context->strictMode); - uint size = requiredMemoryForExecutionContect(this, argc); + uint size = requiredMemoryForExecutionContect(this, 0); ExecutionContext *k = static_cast(alloca(size)); if (strict) { ctx = k; ctx->thisObject = directCall ? context->thisObject : context->engine->globalObject; ctx->function = this; - ctx->arguments = args; - ctx->argumentCount = argc; + ctx->arguments = 0; + ctx->argumentCount = 0; ctx->initCallContext(context); } @@ -472,6 +472,8 @@ QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct Codegen cg(ctx, strictMode); IR::Function *globalIRCode = cg(fileName, program, &module, mode, inheritedLocals); QScopedPointer isel(ctx->engine->iselFactory->create(vm, &module)); + if (inheritContext) + isel->setUseFastLookups(false); if (globalIRCode) globalCode = isel->vmFunction(globalIRCode); } diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 24a51a94a3..7e74e0734a 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -383,8 +383,10 @@ InstructionSelection::~InstructionSelection() void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) { + QVector lookups; qSwap(_function, function); qSwap(_vmFunction, vmFunction); + qSwap(_lookups, lookups); Assembler* oldAssembler = _as; _as = new Assembler(_function); @@ -428,8 +430,14 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) _as->link(_vmFunction); + if (_lookups.size()) { + _vmFunction->lookups = new Lookup[_lookups.size()]; + memcpy(_vmFunction->lookups, _lookups.constData(), _lookups.size()*sizeof(Lookup)); + } + qSwap(_vmFunction, vmFunction); qSwap(_function, function); + qSwap(_lookups, lookups); delete _as; _as = oldAssembler; } @@ -643,12 +651,24 @@ void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::Temp *target) { - generateFunctionCall(target, __qmljs_get_property, Assembler::ContextRegister, base, identifier(name)); + if (useFastLookups) { + VM::String *s = identifier(name); + uint index = addLookup(s); + generateFunctionCall(target, __qmljs_get_property_lookup, Assembler::ContextRegister, base, Assembler::TrustedImm32(index)); + } else { + generateFunctionCall(target, __qmljs_get_property, Assembler::ContextRegister, base, identifier(name)); + } } void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) { - generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, targetBase, identifier(targetName), source); + if (useFastLookups) { + VM::String *s = identifier(targetName); + uint index = addLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_set_property_lookup, Assembler::ContextRegister, targetBase, Assembler::TrustedImm32(index), source); + } else { + generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, targetBase, identifier(targetName), source); + } } void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) @@ -778,10 +798,20 @@ void InstructionSelection::callProperty(IR::Temp *base, const QString &name, assert(base != 0); int argc = prepareVariableArguments(args); - generateFunctionCall(result, __qmljs_call_property, - Assembler::ContextRegister, base, identifier(name), - baseAddressForCallArguments(), - Assembler::TrustedImm32(argc)); + VM::String *s = identifier(name); + + if (useFastLookups) { + uint index = addLookup(s); + generateFunctionCall(result, __qmljs_call_property_lookup, + Assembler::ContextRegister, base, Assembler::TrustedImm32(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } else { + generateFunctionCall(result, __qmljs_call_property, + Assembler::ContextRegister, base, s, + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } } void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) @@ -935,3 +965,13 @@ void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* na } +uint InstructionSelection::addLookup(VM::String *name) +{ + uint index = (uint)_lookups.size(); + VM::Lookup l; + l.internalClass = 0; + l.index = 0; + l.name = name; + _lookups.append(l); + return index; +} diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index aaa0866be9..f20ffd90bd 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -778,9 +778,12 @@ private: #define callRuntimeMethod(result, function, ...) \ callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__) + uint addLookup(VM::String *name); + IR::BasicBlock *_block; IR::Function* _function; VM::Function* _vmFunction; + QVector _lookups; Assembler* _as; }; diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index 05e7739e00..6a050b3b47 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -18,6 +18,7 @@ using namespace QQmlJS::IR; EvalInstructionSelection::EvalInstructionSelection(VM::ExecutionEngine *engine, Module *module) : _engine(engine) + , useFastLookups(true) { assert(engine); assert(module); diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h index d097f91527..5ec50b3b63 100644 --- a/src/v4/qv4isel_p.h +++ b/src/v4/qv4isel_p.h @@ -51,6 +51,8 @@ public: VM::Function *vmFunction(IR::Function *f); + void setUseFastLookups(bool b) { useFastLookups = b; } + protected: VM::Function *createFunctionMapping(VM::ExecutionEngine *engine, IR::Function *irFunction); VM::ExecutionEngine *engine() const { return _engine; } @@ -59,6 +61,8 @@ protected: private: VM::ExecutionEngine *_engine; QHash _irToVM; +protected: + bool useFastLookups; }; class Q_V4_EXPORT EvalISelFactory diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 25a453f6f0..a6259f2dda 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -89,6 +89,7 @@ void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &va Value Object::getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const { + assert(p->type != PropertyDescriptor::Generic); if (p->isData()) return p->value; if (!p->get) @@ -112,6 +113,30 @@ Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p return getValue(ctx, p); } +void Object::putValue(ExecutionContext *ctx, PropertyDescriptor *pd, Value value) +{ + if (pd->isAccessor()) { + if (pd->set) { + Value args[1]; + args[0] = value; + pd->set->call(ctx, Value::fromObject(this), args, 1); + return; + } + goto reject; + } + + if (!pd->isWritable()) + goto reject; + + pd->value = value; + return; + + reject: + if (ctx->strictMode) + __qmljs_throw_type_error(ctx); + +} + void Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx) { bool hasProperty = false; diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index 964051f8db..6991a39374 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -68,6 +68,7 @@ namespace VM { struct Value; struct Function; +struct Lookup; struct Object; struct ObjectIterator; struct BooleanObject; @@ -147,6 +148,8 @@ struct Q_V4_EXPORT Object: Managed { Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const; Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const; + void putValue(ExecutionContext *ctx, PropertyDescriptor *pd, Value value); + void inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); void inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); diff --git a/tests/fact.2.js b/tests/fact.2.js index c0f087e4c9..d8f750b5a1 100644 --- a/tests/fact.2.js +++ b/tests/fact.2.js @@ -3,6 +3,6 @@ function fact(n) { return n > 1 ? n * fact(n - 1) : 1 } -for (var i = 0; i < 10000; i = i + 1) +for (var i = 0; i < 1000000; i = i + 1) fact(12) diff --git a/tests/property_lookup.js b/tests/property_lookup.js new file mode 100644 index 0000000000..ee45b65710 --- /dev/null +++ b/tests/property_lookup.js @@ -0,0 +1,9 @@ +function foo() { + var obj = { x: 10 } + + for (var i = 0; i < 1000000; ++i) { + var y = obj.x; + obj.x = y; + } +} +foo(); diff --git a/tools/v4/main.cpp b/tools/v4/main.cpp index 2516386eef..01d4f8fdcc 100644 --- a/tools/v4/main.cpp +++ b/tools/v4/main.cpp @@ -400,6 +400,7 @@ int main(int argc, char *argv[]) vm.globalCode = f; ctx->strictMode = f->isStrict; + ctx->lookups = f->lookups; if (debugger) debugger->aboutToCall(0, ctx); QQmlJS::VM::Value result = f->code(ctx, f->codeData); -- cgit v1.2.3 From 149459788a577756a35a9908f79b4e9df09a0f1d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 12 Feb 2013 21:46:01 +0100 Subject: Fix a crash in JSON Fully initialize the property descriptor when parsing JSON. Change-Id: Ia43ac2dae573c5d5050cb02ca8f177c363262a05 Reviewed-by: Simon Hausmann --- src/v4/qv4jsonobject.cpp | 4 ++++ tests/TestExpectations | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/v4/qv4jsonobject.cpp b/src/v4/qv4jsonobject.cpp index 74b25f3585..40f7338871 100644 --- a/src/v4/qv4jsonobject.cpp +++ b/src/v4/qv4jsonobject.cpp @@ -282,6 +282,10 @@ bool Parser::parseMember(Object *o) return false; PropertyDescriptor *p = o->insertMember(context->engine->newIdentifier(key)); + p->type = PropertyDescriptor::Data; + p->writable = PropertyDescriptor::Enabled; + p->enumberable = PropertyDescriptor::Enabled; + p->configurable = PropertyDescriptor::Enabled; p->value = val; END; diff --git a/tests/TestExpectations b/tests/TestExpectations index 30aad20bd1..22cb3978ba 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -17,7 +17,6 @@ S13.2.3_A1 failing S14_A2 failing 14.1-5-s failing 15.1.1.3-3 failing -S15.12.2_A1 failing 15.4.4.14-9-a-10 failing 15.4.4.14-9-a-17 failing 15.4.4.14-9-a-7 failing -- cgit v1.2.3 From beda28a8561258188ae8c4473db2a8b5c01d96ed Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 12 Feb 2013 22:25:39 +0100 Subject: Also lookup properties in the prototype by index We can only do this when getting properties, as setting values will always happen on the main object. Change-Id: I0336dd393bf78144d54ed8b6008011a7046e325d Reviewed-by: Simon Hausmann --- src/v4/qmljs_runtime.cpp | 80 ++++++++++++++++++++++++++++++++++------------ src/v4/qv4functionobject.h | 3 +- src/v4/qv4isel_masm.cpp | 3 +- 3 files changed, 64 insertions(+), 22 deletions(-) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 4c32b85921..f3555e37c0 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -680,18 +680,40 @@ Value __qmljs_get_property_lookup(ExecutionContext *ctx, Value object, int looku { Lookup *l = ctx->lookups + lookupIndex; if (Object *o = object.asObject()) { - if (o->internalClass == l->internalClass) - return o->getValue(ctx, o->memberData + l->index); + PropertyDescriptor *p = 0; + if (o->internalClass == l->mainClass) { + if (!l->protoClass) { + p = o->memberData + l->index; + } else if (o->prototype && o->prototype->internalClass == l->protoClass) { + p = o->prototype->memberData + l->index; + } + } - uint idx = o->internalClass->find(l->name); - if (idx < UINT_MAX) { - l->internalClass = o->internalClass; - l->index = idx; - return o->getValue(ctx, o->memberData + idx); + if (!p) { + uint idx = o->internalClass->find(l->name); + if (idx < UINT_MAX) { + l->mainClass = o->internalClass; + l->protoClass = 0; + l->index = idx; + p = o->memberData + idx; + } else if (o->prototype) { + idx = o->prototype->internalClass->find(l->name); + if (idx < UINT_MAX) { + l->mainClass = o->internalClass; + l->protoClass = o->prototype->internalClass; + l->index = idx; + p = o->prototype->memberData + idx; + } + } } - return object.objectValue()->__get__(ctx, l->name); - } else if (object.isString() && l->name->isEqualTo(ctx->engine->id_length)) { + if (p) + return o->getValue(ctx, p); + else + return object.objectValue()->__get__(ctx, l->name); + } + + if (object.isString() && l->name->isEqualTo(ctx->engine->id_length)) { return Value::fromInt32(object.stringValue()->toQString().length()); } else { object = __qmljs_to_object(object, ctx); @@ -714,14 +736,14 @@ void __qmljs_set_property_lookup(ExecutionContext *ctx, Value object, int lookup Lookup *l = ctx->lookups + lookupIndex; if (l->index != ArrayObject::LengthPropertyIndex || !o->isArrayObject()) { - if (o->internalClass == l->internalClass) { + if (o->internalClass == l->mainClass) { o->putValue(ctx, o->memberData + l->index, value); return; } uint idx = o->internalClass->find(l->name); if (idx < UINT_MAX) { - l->internalClass = o->internalClass; + l->mainClass = o->internalClass; l->index = idx; return o->putValue(ctx, o->memberData + idx, value); } @@ -840,21 +862,39 @@ Value __qmljs_call_property_lookup(ExecutionContext *context, Value thisObject, baseObject = thisObject.objectValue(); } + PropertyDescriptor *p = 0; + if (baseObject->internalClass == l->mainClass) { + if (!l->protoClass) { + p = baseObject->memberData + l->index; + } else if (baseObject->prototype && baseObject->prototype->internalClass == l->protoClass) { + p = baseObject->prototype->memberData + l->index; + } + } - Value func; - - if (baseObject->internalClass == l->internalClass) { - func = baseObject->getValue(context, baseObject->memberData + l->index); - } else { + if (!p) { uint idx = baseObject->internalClass->find(l->name); if (idx < UINT_MAX) { - l->internalClass = baseObject->internalClass; + l->mainClass = baseObject->internalClass; + l->protoClass = 0; l->index = idx; - func = baseObject->getValue(context, baseObject->memberData + idx); - } else { - func = baseObject->__get__(context, l->name); + p = baseObject->memberData + idx; + } else if (baseObject->prototype) { + idx = baseObject->prototype->internalClass->find(l->name); + if (idx < UINT_MAX) { + l->mainClass = baseObject->internalClass; + l->protoClass = baseObject->prototype->internalClass; + l->index = idx; + p = baseObject->prototype->memberData + idx; + } } } + + Value func; + if (p) + func = baseObject->getValue(context, p); + else + func = baseObject->__get__(context, l->name); + FunctionObject *o = func.asFunctionObject(); if (!o) context->throwTypeError(); diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index 9a4967488a..d6930b9f5b 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -103,7 +103,8 @@ struct URIErrorPrototype; struct InternalClass; struct Lookup { - InternalClass *internalClass; + InternalClass *mainClass; + InternalClass *protoClass; uint index; String *name; }; diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 7e74e0734a..0960adf5ac 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -969,7 +969,8 @@ uint InstructionSelection::addLookup(VM::String *name) { uint index = (uint)_lookups.size(); VM::Lookup l; - l.internalClass = 0; + l.mainClass = 0; + l.protoClass = 0; l.index = 0; l.name = name; _lookups.append(l); -- cgit v1.2.3 From ff5a76e229a024ff44414f7523d4dba8a1d6862f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Feb 2013 15:55:44 +0100 Subject: Cleanup: Reduce cpu specific #ifdefs in common masm code Centralize the callee save register handling as well as potentially cpu specific stack frame enter/leave code (like ARM doesn't automatically save the link register) inside bigger cpu specific #ifdefs instead of sprinkling them throughout the code. That should make it easier in the future to port to new calling conventions and architectures. Change-Id: I92fed7cc3d0f7eb4da86843b7ad59581a64f635f Reviewed-by: Lars Knoll --- src/v4/qv4isel_masm.cpp | 80 +++++++++++++++++++++++++++++++++---------------- src/v4/qv4isel_masm_p.h | 20 ++++++++++--- 2 files changed, 71 insertions(+), 29 deletions(-) diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 0960adf5ac..5098a44009 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -59,6 +59,45 @@ using namespace QQmlJS; using namespace QQmlJS::MASM; using namespace QQmlJS::VM; +/* Platform/Calling convention/Architecture specific section */ + +#if CPU(X86_64) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // Not used: JSC::X86Registers::rbx, + // Not used: JSC::X86Registers::r10, + // Not used: JSC::X86Registers::r13, + JSC::X86Registers::r14 + // Not used: JSC::X86Registers::r15, +}; +#endif + +#if CPU(X86) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // Not used: JSC::X86Registers::ebx, + JSC::X86Registers::esi + // Not used: JSC::X86Registers::edi, +}; +#endif + +#if CPU(ARM) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // ### FIXME: push multi-register push instruction, remove unused registers. + JSC::ARMRegisters::r4, + JSC::ARMRegisters::r5, + JSC::ARMRegisters::r6, + JSC::ARMRegisters::r7, + JSC::ARMRegisters::r8, + JSC::ARMRegisters::r9, + JSC::ARMRegisters::r10, + JSC::ARMRegisters::r11 +}; +#endif + +static const int calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]); + +/* End of platform/calling convention/architecture specific section */ + + const Assembler::VoidType Assembler::Void; Assembler::Assembler(IR::Function* function) @@ -124,38 +163,32 @@ void Assembler::storeValue(VM::Value value, IR::Temp* destination) void Assembler::enterStandardStackFrame(int locals) { -#if CPU(ARM) - push(JSC::ARMRegisters::lr); -#endif + platformEnterStandardStackFrame(); + + // ### FIXME: Handle through calleeSavedRegisters mechanism + // or eliminate StackFrameRegister altogether. push(StackFrameRegister); move(StackPointerRegister, StackFrameRegister); - // space for the locals and the ContextRegister - int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*); + // space for the locals and callee saved registers + int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*) * calleeSavedRegisterCount; #if CPU(X86) || CPU(X86_64) frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX #endif subPtr(TrustedImm32(frameSize), StackPointerRegister); -#if CPU(X86) || CPU(ARM) - for (int saveReg = CalleeSavedFirstRegister; saveReg <= CalleeSavedLastRegister; ++saveReg) - push(static_cast(saveReg)); -#endif - // save the ContextRegister - storePtr(ContextRegister, StackPointerRegister); + for (int i = 0; i < calleeSavedRegisterCount; ++i) + storePtr(calleeSavedRegisters[i], Address(StackPointerRegister, i * sizeof(void*))); } void Assembler::leaveStandardStackFrame(int locals) { - // restore the ContextRegister - loadPtr(StackPointerRegister, ContextRegister); + // restore the callee saved registers + for (int i = calleeSavedRegisterCount - 1; i >= 0; --i) + loadPtr(Address(StackPointerRegister, i * sizeof(void*)), calleeSavedRegisters[i]); -#if CPU(X86) || CPU(ARM) - for (int saveReg = CalleeSavedLastRegister; saveReg >= CalleeSavedFirstRegister; --saveReg) - pop(static_cast(saveReg)); -#endif - // space for the locals and the ContextRegister + // space for the locals and the callee saved registers int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*); #if CPU(X86) || CPU(X86_64) frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX @@ -163,9 +196,7 @@ void Assembler::leaveStandardStackFrame(int locals) addPtr(TrustedImm32(frameSize), StackPointerRegister); pop(StackFrameRegister); -#if CPU(ARM) - pop(JSC::ARMRegisters::lr); -#endif + platformLeaveStandardStackFrame(); } @@ -401,12 +432,11 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) // That shifts the index the context pointer argument by one. contextPointer++; #endif -#if CPU(X86) - _as->loadPtr(addressForArgument(contextPointer), Assembler::ContextRegister); -#elif CPU(X86_64) || CPU(ARM) + +#ifdef ARGUMENTS_IN_REGISTERS _as->move(_as->registerForArgument(contextPointer), Assembler::ContextRegister); #else - assert(!"TODO"); + _as->loadPtr(addressForArgument(contextPointer), Assembler::ContextRegister); #endif foreach (IR::BasicBlock *block, _function->basicBlocks) { diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index f20ffd90bd..36622685a8 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -63,14 +63,13 @@ public: #if CPU(X86) #undef VALUE_FITS_IN_REGISTER +#undef ARGUMENTS_IN_REGISTERS static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; static const RegisterID StackPointerRegister = JSC::X86Registers::esp; static const RegisterID ContextRegister = JSC::X86Registers::esi; static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; static const RegisterID ScratchRegister = JSC::X86Registers::ecx; - static const RegisterID CalleeSavedFirstRegister = ScratchRegister; - static const RegisterID CalleeSavedLastRegister = ScratchRegister; static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; @@ -83,9 +82,13 @@ public: // Not reached. return JSC::X86Registers::eax; } + + inline void platformEnterStandardStackFrame() {} + inline void platformLeaveStandardStackFrame() {} #elif CPU(X86_64) #define VALUE_FITS_IN_REGISTER +#define ARGUMENTS_IN_REGISTERS static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; static const RegisterID StackPointerRegister = JSC::X86Registers::esp; @@ -111,17 +114,18 @@ public: assert(index >= 0 && index < RegisterArgumentCount); return regs[index]; }; + inline void platformEnterStandardStackFrame() {} + inline void platformLeaveStandardStackFrame() {} #elif CPU(ARM) #undef VALUE_FITS_IN_REGISTER +#define ARGUMENTS_IN_REGISTERS static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4; static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; static const RegisterID ContextRegister = JSC::ARMRegisters::r5; static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; static const RegisterID ScratchRegister = JSC::ARMRegisters::r6; - static const RegisterID CalleeSavedFirstRegister = JSC::ARMRegisters::r4; - static const RegisterID CalleeSavedLastRegister = JSC::ARMRegisters::r11; static const RegisterID IntegerOpRegister = JSC::X86Registers::r0; static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; @@ -138,6 +142,14 @@ public: assert(index >= 0 && index < RegisterArgumentCount); return static_cast(JSC::ARMRegisters::r0 + index); }; + inline void platformEnterStandardStackFrame() + { + push(JSC::ARMRegisters::lr); + } + inline void platformLeaveStandardStackFrame() + { + pop(JSC::ARMRegisters::lr); + } #else #error Argh. #endif -- cgit v1.2.3 From 817a3edf1a8f61b2c3bd8c4c086b36e2da5091e3 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 12 Feb 2013 22:50:41 +0100 Subject: Optimise property access Change-Id: I24e41395cd6f648b121f9a08ab1eaacae234e081 Reviewed-by: Simon Hausmann --- src/v4/qmljs_runtime.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index f3555e37c0..51f6b40cfc 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -708,7 +708,7 @@ Value __qmljs_get_property_lookup(ExecutionContext *ctx, Value object, int looku } if (p) - return o->getValue(ctx, p); + return p->type == PropertyDescriptor::Data ? p->value : o->getValue(ctx, p); else return object.objectValue()->__get__(ctx, l->name); } @@ -891,7 +891,7 @@ Value __qmljs_call_property_lookup(ExecutionContext *context, Value thisObject, Value func; if (p) - func = baseObject->getValue(context, p); + func = p->type == PropertyDescriptor::Data ? p->value : baseObject->getValue(context, p); else func = baseObject->__get__(context, l->name); -- cgit v1.2.3 From 8e55a1a9263c651d5db10184f7f44539832feb9b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Feb 2013 13:33:50 +0100 Subject: Fix scoping for catch blocks Based on Lars' idea, done together with Erik. For proper scoping of the exception variable in catch blocks, it is necessary to define the new scope (ExecutionContext for us) at run-time, it cannot be determined at compile time. This patch implements the necessary logic to create the new execution context when entering a try block and re-uses the existing pop_scope infrastructure for destroying it again. Within the catch scope it is necessary to do all lookups by name, so the existing _function->insideWith variable was re-used and renamed to _function->insideWithOrCatch. Additionally the new context also stores the name and value of the separately scoped exception variable that shadows any existing equally named variables in outter scopes. CodeGen::unwindException also had a bug that it would generate the finally code with the wrong _function->insideWithOrCatch level, resulting in name lookups inside finally instead of local index lookups. Change-Id: I5616af38c3558e553e971a6a894ce5239ccb8422 Reviewed-by: Lars Knoll --- src/v4/moth/qv4instr_moth_p.h | 6 ++++ src/v4/moth/qv4isel_moth.cpp | 7 +++++ src/v4/moth/qv4isel_moth_p.h | 1 + src/v4/moth/qv4vme_moth.cpp | 4 +++ src/v4/qmljs_environment.cpp | 70 ++++++++++++++++++++++++++++++++++++++++--- src/v4/qmljs_environment.h | 5 ++++ src/v4/qmljs_runtime.cpp | 5 ++++ src/v4/qmljs_runtime.h | 1 + src/v4/qv4codegen.cpp | 38 +++++++++++------------ src/v4/qv4ir.cpp | 2 ++ src/v4/qv4ir_p.h | 5 ++-- src/v4/qv4isel_masm.cpp | 5 ++++ src/v4/qv4isel_masm_p.h | 1 + src/v4/qv4isel_p.cpp | 6 ++++ src/v4/qv4isel_p.h | 1 + tests/TestExpectations | 4 +-- 16 files changed, 133 insertions(+), 28 deletions(-) diff --git a/src/v4/moth/qv4instr_moth_p.h b/src/v4/moth/qv4instr_moth_p.h index 741ddba79d..d57371324b 100644 --- a/src/v4/moth/qv4instr_moth_p.h +++ b/src/v4/moth/qv4instr_moth_p.h @@ -25,6 +25,7 @@ F(CallBuiltinDeleteExceptionHandler, callBuiltinDeleteExceptionHandler) \ F(CallBuiltinGetException, callBuiltinGetException) \ F(CallBuiltinPushScope, callBuiltinPushScope) \ + F(CallBuiltinPushCatchScope, callBuiltinPushCatchScope) \ F(CallBuiltinPopScope, callBuiltinPopScope) \ F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \ @@ -242,6 +243,10 @@ union Instr MOTH_INSTR_HEADER Param arg; }; + struct instr_callBuiltinPushCatchScope { + MOTH_INSTR_HEADER + VM::String *varName; + }; struct instr_callBuiltinPopScope { MOTH_INSTR_HEADER }; @@ -452,6 +457,7 @@ union Instr instr_callBuiltinDeleteExceptionHandler callBuiltinDeleteExceptionHandler; instr_callBuiltinGetException callBuiltinGetException; instr_callBuiltinPushScope callBuiltinPushScope; + instr_callBuiltinPushCatchScope callBuiltinPushCatchScope; instr_callBuiltinPopScope callBuiltinPopScope; instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName; diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index 2f0a614b6c..87b21aa384 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -873,6 +873,13 @@ void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) addInstruction(call); } +void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionVarName) +{ + Instruction::CallBuiltinPushCatchScope call; + call.varName = identifier(exceptionVarName); + addInstruction(call); +} + void InstructionSelection::callBuiltinPopScope() { Instruction::CallBuiltinPopScope call; diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h index 7d57897c68..012a26b327 100644 --- a/src/v4/moth/qv4isel_moth_p.h +++ b/src/v4/moth/qv4isel_moth_p.h @@ -49,6 +49,7 @@ protected: virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); virtual void callBuiltinPushWithScope(IR::Temp *arg); + virtual void callBuiltinPushCatchScope(const QString &exceptionVarName); virtual void callBuiltinPopScope(); virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 8ed3fb1a0a..fc06e903ed 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -283,6 +283,10 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co context = __qmljs_builtin_push_with_scope(VALUE(instr.arg), context); MOTH_END_INSTR(CallBuiltinPushScope) + MOTH_BEGIN_INSTR(CallBuiltinPushCatchScope) + context = __qmljs_builtin_push_catch_scope(instr.varName, context); + MOTH_END_INSTR(CallBuiltinPushCatchScope) + MOTH_BEGIN_INSTR(CallBuiltinPopScope) context = __qmljs_builtin_pop_scope(context); MOTH_END_INSTR(CallBuiltinPopScope) diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp index b1339b6a23..40b8938aec 100644 --- a/src/v4/qmljs_environment.cpp +++ b/src/v4/qmljs_environment.cpp @@ -178,10 +178,18 @@ ExecutionContext *ExecutionContext::createWithScope(Object *with) return withCtx; } +ExecutionContext *ExecutionContext::createCatchScope(String *exceptionVarName) +{ + ExecutionContext *catchCtx = engine->newContext(); + catchCtx->initForCatch(this, exceptionVarName); + engine->current = catchCtx; + return catchCtx; +} + ExecutionContext *ExecutionContext::popScope() { assert(engine->current == this); - assert(withObject != 0); + assert(withObject != 0 || exceptionVarName != 0); engine->current = parent; parent = 0; @@ -222,6 +230,8 @@ void ExecutionContext::init(ExecutionEngine *eng) arguments = 0; argumentCount = 0; locals = 0; + exceptionVarName = 0; + exceptionValue = Value::undefinedValue(); strictMode = false; activation = 0; withObject = 0; @@ -242,11 +252,31 @@ void ExecutionContext::init(ExecutionContext *p, Object *with) arguments = 0; argumentCount = 0; locals = 0; + exceptionVarName = 0; + exceptionValue = Value::undefinedValue(); strictMode = false; activation = 0; withObject = with; } +void ExecutionContext::initForCatch(ExecutionContext *p, String *exceptionVarName) +{ + engine = p->engine; + parent = p; + outer = p; + thisObject = p->thisObject; + + function = 0; + arguments = 0; + argumentCount = 0; + locals = 0; + this->exceptionVarName = exceptionVarName; + exceptionValue = engine->exception; + strictMode = p->strictMode; + activation = 0; + withObject = 0; +} + void ExecutionContext::destroy() { delete[] arguments; @@ -265,6 +295,8 @@ bool ExecutionContext::deleteProperty(String *name) if (ctx->activation && ctx->activation->__hasProperty__(this, name)) return ctx->activation->__delete__(this, name); } + if (ctx->exceptionVarName && ctx->exceptionVarName->isEqualTo(name)) + return false; if (FunctionObject *f = ctx->function) { if (f->needsActivation || hasWith) { for (unsigned int i = 0; i < f->varCount; ++i) @@ -294,6 +326,9 @@ void ExecutionContext::mark() activation->mark(); if (withObject) withObject->mark(); + if (exceptionVarName) + exceptionVarName->mark(); + exceptionValue.mark(); } void ExecutionContext::setProperty(String *name, Value value) @@ -307,6 +342,9 @@ void ExecutionContext::setProperty(String *name, Value value) w->__put__(ctx, name, value); return; } + } else if (ctx->exceptionVarName && ctx->exceptionVarName->isEqualTo(name)) { + ctx->exceptionValue = value; + return; } else { // qDebug() << ctx << "setting mutable binding"; if (ctx->setMutableBinding(this, name, value)) @@ -326,6 +364,7 @@ Value ExecutionContext::getProperty(String *name) return thisObject; bool hasWith = false; + bool hasCatchScope = false; // qDebug() << "=== getProperty" << name->toQString(); for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { if (Object *w = ctx->withObject) { @@ -340,8 +379,14 @@ Value ExecutionContext::getProperty(String *name) continue; } + if (ctx->exceptionVarName) { + hasCatchScope = true; + if (ctx->exceptionVarName->isEqualTo(name)) + return ctx->exceptionValue; + } + if (FunctionObject *f = ctx->function) { - if (f->needsActivation || hasWith) { + if (f->needsActivation || hasWith || hasCatchScope) { for (unsigned int i = 0; i < f->varCount; ++i) if (f->varList[i]->isEqualTo(name)) return ctx->locals[i]; @@ -369,6 +414,7 @@ Value ExecutionContext::getPropertyNoThrow(String *name) return thisObject; bool hasWith = false; + bool hasCatchScope = false; for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { if (Object *w = ctx->withObject) { hasWith = true; @@ -379,8 +425,14 @@ Value ExecutionContext::getPropertyNoThrow(String *name) continue; } + if (ctx->exceptionVarName) { + hasCatchScope = true; + if (ctx->exceptionVarName->isEqualTo(name)) + return ctx->exceptionValue; + } + if (FunctionObject *f = ctx->function) { - if (f->needsActivation || hasWith) { + if (f->needsActivation || hasWith || hasCatchScope) { for (unsigned int i = 0; i < f->varCount; ++i) if (f->varList[i]->isEqualTo(name)) return ctx->locals[i]; @@ -408,6 +460,7 @@ Value ExecutionContext::getPropertyAndBase(String *name, Object **base) return thisObject; bool hasWith = false; + bool hasCatchScope = false; for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { if (Object *w = ctx->withObject) { hasWith = true; @@ -420,8 +473,14 @@ Value ExecutionContext::getPropertyAndBase(String *name, Object **base) continue; } + if (ctx->exceptionVarName) { + hasCatchScope = true; + if (ctx->exceptionVarName->isEqualTo(name)) + return ctx->exceptionValue; + } + if (FunctionObject *f = ctx->function) { - if (f->needsActivation || hasWith) { + if (f->needsActivation || hasWith || hasCatchScope) { for (unsigned int i = 0; i < f->varCount; ++i) if (f->varList[i]->isEqualTo(name)) return ctx->locals[i]; @@ -503,6 +562,9 @@ void ExecutionContext::initCallContext(ExecutionContext *parent) outer = function->scope; engine->current = this; + exceptionVarName = 0; + exceptionValue = Value::undefinedValue(); + activation = 0; withObject = 0; diff --git a/src/v4/qmljs_environment.h b/src/v4/qmljs_environment.h index 3c4e1fb690..f118c63af6 100644 --- a/src/v4/qmljs_environment.h +++ b/src/v4/qmljs_environment.h @@ -88,6 +88,9 @@ struct ExecutionContext unsigned int argumentCount; Value *locals; + String *exceptionVarName; + Value exceptionValue; + String * const *formals() const; unsigned int formalCount() const; String * const *variables() const; @@ -100,6 +103,7 @@ struct ExecutionContext void init(ExecutionEngine *e); void init(ExecutionContext *p, Object *with); + void initForCatch(ExecutionContext *p, String *exceptionVarName); void destroy(); bool hasBinding(String *name) const; @@ -109,6 +113,7 @@ struct ExecutionContext bool deleteBinding(ExecutionContext *ctx, String *name); ExecutionContext *createWithScope(Object *with); + ExecutionContext *createCatchScope(String* exceptionVarName); ExecutionContext *popScope(); void initCallContext(ExecutionContext *parent); diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 51f6b40cfc..4c77715fba 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -1211,6 +1211,11 @@ ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx return ctx->createWithScope(obj); } +ExecutionContext *__qmljs_builtin_push_catch_scope(String *exceptionVarName, ExecutionContext *ctx) +{ + return ctx->createCatchScope(exceptionVarName); +} + ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx) { return ctx->popScope(); diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index b2efc15e1d..579e83273c 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -119,6 +119,7 @@ Value __qmljs_builtin_post_decrement_element(Value base, Value index, ExecutionC void __qmljs_builtin_throw(Value val, ExecutionContext *context); void __qmljs_builtin_rethrow(ExecutionContext *context); ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx); +ExecutionContext *__qmljs_builtin_push_catch_scope(String *exceptionVarName, ExecutionContext *ctx); ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx); void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name); void __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx); diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index eda6b0cca7..17cf824fd9 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -849,7 +849,7 @@ void Codegen::variableDeclaration(VariableDeclaration *ast) assert(expr.code); initializer = *expr; - if (! _env->parent || _function->insideWith) { + if (! _env->parent || _function->insideWithOrCatch) { // it's global code. move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), initializer); } else { @@ -1374,7 +1374,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col) { int index = _env->findMember(name); - if (! _function->hasDirectEval && !_function->insideWith && _env->parent) { + if (! _function->hasDirectEval && !_function->insideWithOrCatch && _env->parent) { if (index != -1) { return _block->TEMP(index); } @@ -2444,23 +2444,20 @@ bool Codegen::visit(TryStatement *ast) move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, true)); move(_block->TEMP(hasException), _block->CONST(IR::BoolType, false)); - const int exception = _block->newTemp(); - move(_block->TEMP(exception), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0)); + IR::ExprList *catchScopeArgs = _function->New(); + catchScopeArgs->init(_block->NAME(ast->catchExpression->name.toString(), ast->catchExpression->identifierToken.startLine, ast->catchExpression->identifierToken.startColumn)); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_catch_scope, 0, 0), catchScopeArgs)); - // the variable used in the catch statement is local and hides any global - // variable with the same name. - const Environment::Member undefinedMember = { Environment::UndefinedMember, -1 , 0 }; - const Environment::Member catchMember = { Environment::VariableDefinition, exception, 0 }; - Environment::Member m = _env->members.value(ast->catchExpression->name.toString(), undefinedMember); - _env->members.insert(ast->catchExpression->name.toString(), catchMember); - - statement(ast->catchExpression->statement); + ++_function->insideWithOrCatch; + { + ScopeAndFinally scope(_scopeAndFinally); + _scopeAndFinally = &scope; + statement(ast->catchExpression->statement); + _scopeAndFinally = scope.parent; + } + --_function->insideWithOrCatch; - // reset the variable name to the one from the outer scope - if (m.type == Environment::UndefinedMember) - _env->members.remove(ast->catchExpression->name.toString()); - else - _env->members.insert(ast->catchExpression->name.toString(), m); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0))); _block->JUMP(finallyBody); } @@ -2489,12 +2486,14 @@ bool Codegen::visit(TryStatement *ast) void Codegen::unwindException(Codegen::ScopeAndFinally *outest) { + int savedDepthForWidthOrCatch = _function->insideWithOrCatch; ScopeAndFinally *scopeAndFinally = _scopeAndFinally; qSwap(_scopeAndFinally, scopeAndFinally); while (_scopeAndFinally != outest) { if (_scopeAndFinally->popScope) { _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0))); _scopeAndFinally = _scopeAndFinally->parent; + --_function->insideWithOrCatch; } else { _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), _scopeAndFinally->deleteExceptionArgs)); ScopeAndFinally *tc = _scopeAndFinally; @@ -2504,6 +2503,7 @@ void Codegen::unwindException(Codegen::ScopeAndFinally *outest) } } qSwap(_scopeAndFinally, scopeAndFinally); + _function->insideWithOrCatch = savedDepthForWidthOrCatch; } bool Codegen::visit(VariableStatement *ast) @@ -2546,14 +2546,14 @@ bool Codegen::visit(WithStatement *ast) args->init(_block->TEMP(withObject)); _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with_scope, 0, 0), args)); - ++_function->insideWith; + ++_function->insideWithOrCatch; { ScopeAndFinally scope(_scopeAndFinally); _scopeAndFinally = &scope; statement(ast->statement); _scopeAndFinally = scope.parent; } - --_function->insideWith; + --_function->insideWithOrCatch; _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); IR::BasicBlock *next = _function->newBasicBlock(); diff --git a/src/v4/qv4ir.cpp b/src/v4/qv4ir.cpp index 1c2a9d9e62..fca14df0b3 100644 --- a/src/v4/qv4ir.cpp +++ b/src/v4/qv4ir.cpp @@ -242,6 +242,8 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_foreach_next_property_name"; case IR::Name::builtin_push_with_scope: return "builtin_push_with_scope"; + case IR::Name::builtin_push_catch_scope: + return "builtin_push_catch_scope"; case IR::Name::builtin_pop_scope: return "builtin_pop_scope"; case IR::Name::builtin_declare_vars: diff --git a/src/v4/qv4ir_p.h b/src/v4/qv4ir_p.h index eefc0397e5..e39ef94d0e 100644 --- a/src/v4/qv4ir_p.h +++ b/src/v4/qv4ir_p.h @@ -284,6 +284,7 @@ struct Name: Expr { builtin_foreach_iterator_object, builtin_foreach_next_property_name, builtin_push_with_scope, + builtin_push_catch_scope, builtin_pop_scope, builtin_declare_vars, builtin_define_property, @@ -612,7 +613,7 @@ struct Function { QList locals; QVector nestedFunctions; - int insideWith; + int insideWithOrCatch; uint hasDirectEval: 1; uint usesArgumentsObject : 1; @@ -626,7 +627,7 @@ struct Function { , pool(&module->pool) , tempCount(0) , maxNumberOfArguments(0) - , insideWith(0) + , insideWithOrCatch(0) , hasDirectEval(false) , usesArgumentsObject(false) , isStrict(false) diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 5098a44009..355d8a5fb2 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -600,6 +600,11 @@ void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with_scope, arg, Assembler::ContextRegister); } +void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionVarName) +{ + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_catch_scope, identifier(exceptionVarName), Assembler::ContextRegister); +} + void InstructionSelection::callBuiltinPopScope() { generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_pop_scope, Assembler::ContextRegister); diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 36622685a8..9e2c4e6279 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -711,6 +711,7 @@ protected: virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); virtual void callBuiltinPushWithScope(IR::Temp *arg); + virtual void callBuiltinPushCatchScope(const QString &exceptionVarName); virtual void callBuiltinPopScope(); virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index 6a050b3b47..e47f3493a1 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -339,6 +339,12 @@ void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result) callBuiltinPushWithScope(arg); } return; + case IR::Name::builtin_push_catch_scope: { + IR::Name *arg = call->args->expr->asName(); + assert(arg != 0); + callBuiltinPushCatchScope(*arg->id); + } return; + case IR::Name::builtin_pop_scope: callBuiltinPopScope(); return; diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h index 5ec50b3b63..5a0e828046 100644 --- a/src/v4/qv4isel_p.h +++ b/src/v4/qv4isel_p.h @@ -109,6 +109,7 @@ public: // to implement by subclasses: virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) = 0; virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) = 0; virtual void callBuiltinPushWithScope(IR::Temp *arg) = 0; + virtual void callBuiltinPushCatchScope(const QString &exceptionVarName) = 0; virtual void callBuiltinPopScope() = 0; virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0; virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) = 0; diff --git a/tests/TestExpectations b/tests/TestExpectations index 22cb3978ba..214d5f49ab 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -42,9 +42,7 @@ Sbp_A4_T1 failing Sbp_A4_T2 failing S12.4_A1 failing S15.2.4.4_A14 failing -# Try/catch scoping issue -S12.14_A4 failing # Function declaration inside if / while Sbp_12.5_A9_T3 failing -Sbp_12.6.2_A13_T3 failing \ No newline at end of file +Sbp_12.6.2_A13_T3 failing -- cgit v1.2.3 From c615382ab92c224ebb3b92bcc2ff020080c47982 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 13 Feb 2013 10:35:46 +0100 Subject: [masm] Remove unused code We don't need to save & restore esi on ia32 between calls, because it's actually a callee saved register. Change-Id: I452bc59c42b5428a4f5f32e379be144c744cfd2a Reviewed-by: Lars Knoll --- src/v4/qv4isel_masm_p.h | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 9e2c4e6279..953ff7ac82 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -408,20 +408,6 @@ public: void enterStandardStackFrame(int locals); void leaveStandardStackFrame(int locals); - void callFunctionPrologue() - { -#if CPU(X86) - // Callee might clobber it :( - push(ContextRegister); -#endif - } - void callFunctionEpilogue() - { -#if CPU(X86) - pop(ContextRegister); -#endif - } - static inline int sizeOfArgument(VoidType) { return 0; } static inline int sizeOfArgument(RegisterID) @@ -476,8 +462,6 @@ public: template void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { - callFunctionPrologue(); - int totalNumberOfArgs = 5; // If necessary reserve space for the return value on the stack and @@ -514,8 +498,6 @@ public: if (stackSizeToCorrect) add32(TrustedImm32(stackSizeToCorrect), StackPointerRegister); - - callFunctionEpilogue(); } template -- cgit v1.2.3 From c5cfb3ce7fca6d594acbf41d19325e83f9403f2f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 13 Feb 2013 12:44:37 +0100 Subject: Fix lookups in catch scopes Change-Id: I6fa7546bb7c8d5bc1b52de8adb5c5cc8cb97c9ad Reviewed-by: Simon Hausmann --- src/v4/qmljs_environment.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp index 40b8938aec..0aee96eeb9 100644 --- a/src/v4/qmljs_environment.cpp +++ b/src/v4/qmljs_environment.cpp @@ -267,6 +267,7 @@ void ExecutionContext::initForCatch(ExecutionContext *p, String *exceptionVarNam thisObject = p->thisObject; function = 0; + lookups = parent->lookups; arguments = 0; argumentCount = 0; locals = 0; -- cgit v1.2.3 From 20a3dc6f14043cf8ff24026a6422513f9b91e934 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 13 Feb 2013 13:41:49 +0100 Subject: Remove unused method Change-Id: I8fdb8febafa996b26d2db503a57788c9ccbf3307 Reviewed-by: Simon Hausmann --- src/v4/qv4isel_masm.cpp | 6 ------ src/v4/qv4isel_masm_p.h | 1 - 2 files changed, 7 deletions(-) diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 355d8a5fb2..3a8fef320a 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -993,12 +993,6 @@ void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* na _as->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } -void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args) -{ - int argc = prepareVariableArguments(args); - _as->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); -} - uint InstructionSelection::addLookup(VM::String *name) { diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 953ff7ac82..da5486e409 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -769,7 +769,6 @@ private: typedef VM::Value (*ActivationMethod)(VM::ExecutionContext *, VM::String *name, VM::Value *args, int argc); typedef VM::Value (*BuiltinMethod)(VM::ExecutionContext *, VM::Value *args, int argc); void callRuntimeMethodImp(IR::Temp *result, const char* name, ActivationMethod method, IR::Expr *base, IR::ExprList *args); - void callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args); #define callRuntimeMethod(result, function, ...) \ callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__) -- cgit v1.2.3 From fb4204167c4532f5263cb651d3559181593c9d3b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 13 Feb 2013 14:15:08 +0100 Subject: Change activition property calls to be pointer based After longer discussions, we actually found out that passing Values by reference (ie. as pointers) is most likely quite a bit more efficient then passing them by value. This is esp. true on 32bit platforms. So change the runtime back to a pointer based calling convention. Change-Id: I948d361b6876109d77fc58f11ceb47109cf631d1 Reviewed-by: Simon Hausmann --- src/v4/llvm_runtime.cpp | 8 ++++---- src/v4/moth/qv4isel_moth.cpp | 2 +- src/v4/moth/qv4isel_moth_p.h | 2 +- src/v4/moth/qv4vme_moth.cpp | 8 ++++---- src/v4/qmljs_runtime.cpp | 28 ++++++++++++++++++---------- src/v4/qmljs_runtime.h | 8 ++++---- src/v4/qv4codegen.cpp | 5 +++++ src/v4/qv4isel_llvm.cpp | 2 +- src/v4/qv4isel_llvm_p.h | 2 +- src/v4/qv4isel_masm.cpp | 10 ++++++---- src/v4/qv4isel_masm_p.h | 15 ++++++++------- src/v4/qv4isel_p.cpp | 4 ++-- src/v4/qv4isel_p.h | 2 +- 13 files changed, 56 insertions(+), 40 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 212c8d17d5..828fee3d8e 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -400,7 +400,7 @@ String *__qmljs_llvm_identifier_from_utf8(ExecutionContext *ctx, const char *str void __qmljs_llvm_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) { - *result = __qmljs_call_activation_property(context, name, args, argc); + __qmljs_call_activation_property(context, result, name, args, argc); } void __qmljs_llvm_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) @@ -411,7 +411,7 @@ void __qmljs_llvm_call_value(ExecutionContext *context, Value *result, const Val void __qmljs_llvm_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) { - *result = __qmljs_construct_activation_property(context, name, args, argc); + __qmljs_construct_activation_property(context, result, name, args, argc); } void __qmljs_llvm_construct_value(ExecutionContext *context, Value *result, const Value *func, Value *args, int argc) @@ -421,12 +421,12 @@ void __qmljs_llvm_construct_value(ExecutionContext *context, Value *result, cons void __qmljs_llvm_get_activation_property(ExecutionContext *ctx, Value *result, String *name) { - *result = __qmljs_get_activation_property(ctx, name); + __qmljs_get_activation_property(ctx, result, name); } void __qmljs_llvm_set_activation_property(ExecutionContext *ctx, String *name, Value *value) { - __qmljs_set_activation_property(ctx, name, *value); + __qmljs_set_activation_property(ctx, name, value); } void __qmljs_llvm_get_property(ExecutionContext *ctx, Value *result, Value *object, String *name) diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index 87b21aa384..e9ec4095e4 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -419,7 +419,7 @@ void InstructionSelection::getActivationProperty(const QString &name, IR::Temp * addInstruction(load); } -void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) +void InstructionSelection::setActivationProperty(IR::Temp *source, const QString &targetName) { Instruction::StoreName store; store.source = getParam(source); diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h index 012a26b327..6dfc1e8e4c 100644 --- a/src/v4/moth/qv4isel_moth_p.h +++ b/src/v4/moth/qv4isel_moth_p.h @@ -66,7 +66,7 @@ protected: virtual void loadString(const QString &str, IR::Temp *targetTemp); virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); virtual void getActivationProperty(const QString &name, IR::Temp *temp); - virtual void setActivationProperty(IR::Expr *source, const QString &targetName); + virtual void setActivationProperty(IR::Temp *source, const QString &targetName); virtual void initClosure(IR::Closure *closure, IR::Temp *target); virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index fc06e903ed..d526bf14e5 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -177,12 +177,12 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(LoadName) TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); - VALUE(instr.result) = __qmljs_get_activation_property(context, instr.name); + __qmljs_get_activation_property(context, VALUEPTR(instr.result), instr.name); MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(StoreName) TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); - __qmljs_set_activation_property(context, instr.name, VALUE(instr.source)); + __qmljs_set_activation_property(context, instr.name, VALUEPTR(instr.source)); MOTH_END_INSTR(StoreName) MOTH_BEGIN_INSTR(LoadElement) @@ -240,7 +240,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(CallActivationProperty) Q_ASSERT(instr.args + instr.argc < stackSize); VM::Value *args = stack + instr.args; - VALUE(instr.result) = __qmljs_call_activation_property(context, instr.name, args, instr.argc); + __qmljs_call_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc); MOTH_END_INSTR(CallActivationProperty) MOTH_BEGIN_INSTR(CallBuiltinThrow) @@ -391,7 +391,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co TRACE(inline, "property name = %s, args = %d, argc = %d", instr.name->toQString().toUtf8().constData(), instr.args, instr.argc); Q_ASSERT(instr.args + instr.argc < stackSize); VM::Value *args = stack + instr.args; - VALUE(instr.result) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); + __qmljs_construct_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc); MOTH_END_INSTR(CreateActivationProperty) MOTH_BEGIN_INSTR(Jump) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 4c77715fba..fcaa6a8cd0 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -648,9 +648,9 @@ Value __qmljs_foreach_next_property_name(Value foreach_iterator) } -void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, Value value) +void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value *value) { - ctx->setProperty(name, value); + ctx->setProperty(name, *value); } Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name) @@ -671,9 +671,9 @@ Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name) } } -Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name) +void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, String *name) { - return ctx->getProperty(name); + *result = ctx->getProperty(name); } Value __qmljs_get_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex) @@ -810,7 +810,7 @@ uint __qmljs_equal(Value x, Value y, ExecutionContext *ctx) return false; } -Value __qmljs_call_activation_property(ExecutionContext *context, String *name, Value *args, int argc) +void __qmljs_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) { Object *base; Value func = context->getPropertyAndBase(name, &base); @@ -820,10 +820,16 @@ Value __qmljs_call_activation_property(ExecutionContext *context, String *name, Value thisObject = base ? Value::fromObject(base) : Value::undefinedValue(); - if (o == context->engine->evalFunction && name->isEqualTo(context->engine->id_eval)) - return static_cast(o)->evalCall(context, thisObject, args, argc, true); + if (o == context->engine->evalFunction && name->isEqualTo(context->engine->id_eval)) { + Value res = static_cast(o)->evalCall(context, thisObject, args, argc, true); + if (result) + *result = res; + return; + } - return o->call(context, thisObject, args, argc); + Value res = o->call(context, thisObject, args, argc); + if (result) + *result = res; } Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String *name, Value *args, int argc) @@ -927,10 +933,12 @@ Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func return o->call(context, thisObject, args, argc); } -Value __qmljs_construct_activation_property(ExecutionContext *context, String *name, Value *args, int argc) +void __qmljs_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) { Value func = context->getProperty(name); - return __qmljs_construct_value(context, func, args, argc); + Value res = __qmljs_construct_value(context, func, args, argc); + if (result) + *result = res; } Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 579e83273c..786deb01ef 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -91,13 +91,13 @@ struct ExecutionEngine; extern "C" { // context -Value __qmljs_call_activation_property(ExecutionContext *, String *name, Value *args, int argc); +void __qmljs_call_activation_property(ExecutionContext *, Value *result, String *name, Value *args, int argc); Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, Value *args, int argc); Value __qmljs_call_property_lookup(ExecutionContext *context, Value thisObject, uint index, Value *args, int argc); Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc); Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc); -Value __qmljs_construct_activation_property(ExecutionContext *, String *name, Value *args, int argc); +void __qmljs_construct_activation_property(ExecutionContext *, Value *result, String *name, Value *args, int argc); Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc); Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc); @@ -165,10 +165,10 @@ Value __qmljs_new_object(ExecutionContext *ctx); Value __qmljs_new_boolean_object(ExecutionContext *ctx, bool boolean); Value __qmljs_new_number_object(ExecutionContext *ctx, double n); Value __qmljs_new_string_object(ExecutionContext *ctx, String *string); -void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, Value value); +void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value *value); void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Value value); Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name); -Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name); +void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, String *name); Value __qmljs_get_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex); void __qmljs_set_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex, Value value); diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index 17cf824fd9..e3580fc8f8 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -716,6 +716,11 @@ void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) _block->MOVE(_block->TEMP(t), source); source = _block->TEMP(t); } + if (target->asName() && source->asConst()) { + unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), source); + source = _block->TEMP(t); + } _block->MOVE(target, source, op); } diff --git a/src/v4/qv4isel_llvm.cpp b/src/v4/qv4isel_llvm.cpp index 1e98c029a7..6460885d82 100644 --- a/src/v4/qv4isel_llvm.cpp +++ b/src/v4/qv4isel_llvm.cpp @@ -584,7 +584,7 @@ void InstructionSelection::getActivationProperty(const QString &name, IR::Temp * Q_UNREACHABLE(); } -void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) +void InstructionSelection::setActivationProperty(IR::Temp *source, const QString &targetName) { llvm::Value *name = getIdentifier(targetName); llvm::Value *src = toValuePtr(source); diff --git a/src/v4/qv4isel_llvm_p.h b/src/v4/qv4isel_llvm_p.h index ec5ace3df2..f306528724 100644 --- a/src/v4/qv4isel_llvm_p.h +++ b/src/v4/qv4isel_llvm_p.h @@ -110,7 +110,7 @@ public: // methods from InstructionSelection: virtual void loadString(const QString &str, IR::Temp *targetTemp); virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); virtual void getActivationProperty(const QString &name, IR::Temp *temp); - virtual void setActivationProperty(IR::Expr *source, const QString &targetName); + virtual void setActivationProperty(IR::Temp *source, const QString &targetName); virtual void initClosure(IR::Closure *closure, IR::Temp *target); virtual void getProperty(IR::Temp *sourceBase, const QString &sourceName, IR::Temp *target); virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 3a8fef320a..c2f1811824 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -668,13 +668,13 @@ void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *target void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) { String *propertyName = identifier(name); - generateFunctionCall(temp, __qmljs_get_activation_property, Assembler::ContextRegister, propertyName); + generateFunctionCall(Assembler::Void, __qmljs_get_activation_property, Assembler::ContextRegister, Assembler::PointerToValue(temp), propertyName); } -void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) +void InstructionSelection::setActivationProperty(IR::Temp *source, const QString &targetName) { String *propertyName = identifier(targetName); - generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, Assembler::ContextRegister, propertyName, source); + generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, Assembler::ContextRegister, propertyName, Assembler::PointerToValue(source)); } void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) @@ -990,7 +990,9 @@ void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* na assert(baseName != 0); int argc = prepareVariableArguments(args); - _as->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); + _as->generateFunctionCallImp(Assembler::Void, name, method, Assembler::ContextRegister, Assembler::PointerToValue(result), + identifier(*baseName->id), baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); } diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index da5486e409..7b42f110f6 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -214,10 +214,12 @@ public: void loadArgument(PointerToValue temp, RegisterID dest) { - assert(temp.value); - - Pointer addr = loadTempAddress(dest, temp.value); - loadArgument(addr, dest); + if (!temp.value) { + loadArgument(TrustedImmPtr(0), dest); + } else { + Pointer addr = loadTempAddress(dest, temp.value); + loadArgument(addr, dest); + } } #ifdef VALUE_FITS_IN_REGISTER @@ -707,7 +709,7 @@ protected: virtual void loadString(const QString &str, IR::Temp *targetTemp); virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); virtual void getActivationProperty(const QString &name, IR::Temp *temp); - virtual void setActivationProperty(IR::Expr *source, const QString &targetName); + virtual void setActivationProperty(IR::Temp *source, const QString &targetName); virtual void initClosure(IR::Closure *closure, IR::Temp *target); virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); @@ -766,8 +768,7 @@ private: int prepareVariableArguments(IR::ExprList* args); - typedef VM::Value (*ActivationMethod)(VM::ExecutionContext *, VM::String *name, VM::Value *args, int argc); - typedef VM::Value (*BuiltinMethod)(VM::ExecutionContext *, VM::Value *args, int argc); + typedef void (*ActivationMethod)(VM::ExecutionContext *, VM::Value *result, VM::String *name, VM::Value *args, int argc); void callRuntimeMethodImp(IR::Temp *result, const char* name, ActivationMethod method, IR::Expr *base, IR::ExprList *args); #define callRuntimeMethod(result, function, ...) \ callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__) diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index e47f3493a1..2eb1b1db92 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -73,8 +73,8 @@ void InstructionSelection::visitMove(IR::Move *s) { if (s->op == IR::OpInvalid) { if (IR::Name *n = s->target->asName()) { - if (s->source->asTemp() || s->source->asConst()) { - setActivationProperty(s->source, *n->id); + if (s->source->asTemp()) { + setActivationProperty(s->source->asTemp(), *n->id); return; } } else if (IR::Temp *t = s->target->asTemp()) { diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h index 5a0e828046..d135cd3b55 100644 --- a/src/v4/qv4isel_p.h +++ b/src/v4/qv4isel_p.h @@ -126,7 +126,7 @@ public: // to implement by subclasses: virtual void loadString(const QString &str, IR::Temp *targetTemp) = 0; virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) = 0; virtual void getActivationProperty(const QString &name, IR::Temp *temp) = 0; - virtual void setActivationProperty(IR::Expr *source, const QString &targetName) = 0; + virtual void setActivationProperty(IR::Temp *source, const QString &targetName) = 0; virtual void initClosure(IR::Closure *closure, IR::Temp *target) = 0; virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target) = 0; virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) = 0; -- cgit v1.2.3 From 322e1210d52213329593c88bdc3470b42399e8cc Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 13 Feb 2013 14:42:56 +0100 Subject: Move get/set_property over to pointer based calling convention Change-Id: I4afc0e90bd4763d170e06adddf70cf133c9ebbf7 Reviewed-by: Simon Hausmann --- src/v4/llvm_runtime.cpp | 4 +-- src/v4/moth/qv4isel_moth.cpp | 2 +- src/v4/moth/qv4isel_moth_p.h | 2 +- src/v4/moth/qv4vme_moth.cpp | 4 +-- src/v4/qmljs_runtime.cpp | 69 +++++++++++++++++++------------------------- src/v4/qmljs_runtime.h | 8 ++--- src/v4/qv4codegen.cpp | 2 +- src/v4/qv4isel_llvm.cpp | 2 +- src/v4/qv4isel_llvm_p.h | 2 +- src/v4/qv4isel_masm.cpp | 14 +++++---- src/v4/qv4isel_masm_p.h | 2 +- src/v4/qv4isel_p.cpp | 4 +-- src/v4/qv4isel_p.h | 2 +- 13 files changed, 56 insertions(+), 61 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 828fee3d8e..c045507556 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -431,7 +431,7 @@ void __qmljs_llvm_set_activation_property(ExecutionContext *ctx, String *name, V void __qmljs_llvm_get_property(ExecutionContext *ctx, Value *result, Value *object, String *name) { - *result = __qmljs_get_property(ctx, *object, name); + __qmljs_get_property(ctx, result, object, name); } void __qmljs_llvm_call_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) @@ -456,7 +456,7 @@ void __qmljs_llvm_set_element(ExecutionContext *ctx, Value *object, Value *index void __qmljs_llvm_set_property(ExecutionContext *ctx, Value *object, String *name, Value *value) { - __qmljs_set_property(ctx, *object, name, *value); + __qmljs_set_property(ctx, object, name, value); } void __qmljs_llvm_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index e9ec4095e4..b054f13701 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -446,7 +446,7 @@ void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR:: addInstruction(load); } -void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +void InstructionSelection::setProperty(IR::Temp *source, IR::Temp *targetBase, const QString &targetName) { Instruction::StoreProperty store; store.base = getParam(targetBase); diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h index 6dfc1e8e4c..691dca23a9 100644 --- a/src/v4/moth/qv4isel_moth_p.h +++ b/src/v4/moth/qv4isel_moth_p.h @@ -69,7 +69,7 @@ protected: virtual void setActivationProperty(IR::Temp *source, const QString &targetName); virtual void initClosure(IR::Closure *closure, IR::Temp *target); virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); - virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + virtual void setProperty(IR::Temp *source, IR::Temp *targetBase, const QString &targetName); virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target); virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index d526bf14e5..fba5364f64 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -194,11 +194,11 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) - VALUE(instr.result) = __qmljs_get_property(context, VALUE(instr.base), instr.name); + __qmljs_get_property(context, VALUEPTR(instr.result), VALUEPTR(instr.base), instr.name); MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(StoreProperty) - __qmljs_set_property(context, VALUE(instr.base), instr.name, VALUE(instr.source)); + __qmljs_set_property(context, VALUEPTR(instr.base), instr.name, VALUEPTR(instr.source)); MOTH_END_INSTR(StoreProperty) MOTH_BEGIN_INSTR(Push) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index fcaa6a8cd0..356d2a8099 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -569,11 +569,10 @@ Value __qmljs_new_string_object(ExecutionContext *ctx, String *string) return Value::fromObject(ctx->engine->newStringObject(ctx, value)); } -void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Value value) +void __qmljs_set_property(ExecutionContext *ctx, const Value *object, String *name, const Value *value) { - if (! object.isObject()) - object = __qmljs_to_object(object, ctx); - object.objectValue()->__put__(ctx, name, value); + Object *o = object->isObject() ? object->objectValue() : __qmljs_to_object(*object, ctx).objectValue(); + o->__put__(ctx, name, *value); } Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) @@ -653,22 +652,20 @@ void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const ctx->setProperty(name, *value); } -Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name) +void __qmljs_get_property(ExecutionContext *ctx, Value *result, Value *object, String *name) { - if (object.isObject()) { - return object.objectValue()->__get__(ctx, name); - } else if (object.isString() && name->isEqualTo(ctx->engine->id_length)) { - return Value::fromInt32(object.stringValue()->toQString().length()); + Value res; + Object *o = object->asObject(); + if (o) { + res = o->__get__(ctx, name); + } else if (object->isString() && name->isEqualTo(ctx->engine->id_length)) { + res = Value::fromInt32(object->stringValue()->toQString().length()); } else { - object = __qmljs_to_object(object, ctx); - - if (object.isObject()) { - return object.objectValue()->__get__(ctx, name); - } else { - ctx->throwTypeError(); - return Value::undefinedValue(); - } + o = __qmljs_to_object(*object, ctx).objectValue(); + res = o->__get__(ctx, name); } + if (result) + *result = res; } void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, String *name) @@ -676,10 +673,11 @@ void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, Strin *result = ctx->getProperty(name); } -Value __qmljs_get_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex) +void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Value *object, int lookupIndex) { + Value res; Lookup *l = ctx->lookups + lookupIndex; - if (Object *o = object.asObject()) { + if (Object *o = object->asObject()) { PropertyDescriptor *p = 0; if (o->internalClass == l->mainClass) { if (!l->protoClass) { @@ -708,36 +706,29 @@ Value __qmljs_get_property_lookup(ExecutionContext *ctx, Value object, int looku } if (p) - return p->type == PropertyDescriptor::Data ? p->value : o->getValue(ctx, p); + res = p->type == PropertyDescriptor::Data ? p->value : o->getValue(ctx, p); else - return object.objectValue()->__get__(ctx, l->name); - } - - if (object.isString() && l->name->isEqualTo(ctx->engine->id_length)) { - return Value::fromInt32(object.stringValue()->toQString().length()); + res = o->__get__(ctx, l->name); } else { - object = __qmljs_to_object(object, ctx); - - if (object.isObject()) { - return object.objectValue()->__get__(ctx, l->name); + if (object->isString() && l->name->isEqualTo(ctx->engine->id_length)) { + res = Value::fromInt32(object->stringValue()->toQString().length()); } else { - ctx->throwTypeError(); - return Value::undefinedValue(); + o = __qmljs_to_object(*object, ctx).objectValue(); + res = o->__get__(ctx, l->name); } } + if (result) + *result = res; } -void __qmljs_set_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex, Value value) +void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value *object, int lookupIndex, const Value *value) { - if (! object.isObject()) - object = __qmljs_to_object(object, ctx); - - Object *o = object.objectValue(); + Object *o = object->isObject() ? object->objectValue() : __qmljs_to_object(*object, ctx).objectValue(); Lookup *l = ctx->lookups + lookupIndex; if (l->index != ArrayObject::LengthPropertyIndex || !o->isArrayObject()) { if (o->internalClass == l->mainClass) { - o->putValue(ctx, o->memberData + l->index, value); + o->putValue(ctx, o->memberData + l->index, *value); return; } @@ -745,11 +736,11 @@ void __qmljs_set_property_lookup(ExecutionContext *ctx, Value object, int lookup if (idx < UINT_MAX) { l->mainClass = o->internalClass; l->index = idx; - return o->putValue(ctx, o->memberData + idx, value); + return o->putValue(ctx, o->memberData + idx, *value); } } - o->__put__(ctx, l->name, value); + o->__put__(ctx, l->name, *value); } diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 786deb01ef..97fa256b28 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -166,12 +166,12 @@ Value __qmljs_new_boolean_object(ExecutionContext *ctx, bool boolean); Value __qmljs_new_number_object(ExecutionContext *ctx, double n); Value __qmljs_new_string_object(ExecutionContext *ctx, String *string); void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value *value); -void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Value value); -Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name); +void __qmljs_set_property(ExecutionContext *ctx, const Value *object, String *name, const Value *value); +void __qmljs_get_property(ExecutionContext *ctx, Value *result, Value *object, String *name); void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, String *name); -Value __qmljs_get_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex); -void __qmljs_set_property_lookup(ExecutionContext *ctx, Value object, int lookupIndex, Value value); +void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Value *object, int lookupIndex); +void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value *object, int lookupIndex, const Value *value); Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index); diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index e3580fc8f8..7faf22c3bd 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -716,7 +716,7 @@ void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) _block->MOVE(_block->TEMP(t), source); source = _block->TEMP(t); } - if (target->asName() && source->asConst()) { + if (!target->asTemp() && source->asConst()) { unsigned t = _block->newTemp(); _block->MOVE(_block->TEMP(t), source); source = _block->TEMP(t); diff --git a/src/v4/qv4isel_llvm.cpp b/src/v4/qv4isel_llvm.cpp index 6460885d82..6131e304d8 100644 --- a/src/v4/qv4isel_llvm.cpp +++ b/src/v4/qv4isel_llvm.cpp @@ -625,7 +625,7 @@ void InstructionSelection::getProperty(IR::Temp *sourceBase, const QString &sour _llvmFunction->arg_begin(), t, base, name); } -void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +void InstructionSelection::setProperty(IR::Temp *source, IR::Temp *targetBase, const QString &targetName) { llvm::Value *base = getLLVMTempReference(targetBase); llvm::Value *name = getIdentifier(targetName); diff --git a/src/v4/qv4isel_llvm_p.h b/src/v4/qv4isel_llvm_p.h index f306528724..24ed317501 100644 --- a/src/v4/qv4isel_llvm_p.h +++ b/src/v4/qv4isel_llvm_p.h @@ -113,7 +113,7 @@ public: // methods from InstructionSelection: virtual void setActivationProperty(IR::Temp *source, const QString &targetName); virtual void initClosure(IR::Closure *closure, IR::Temp *target); virtual void getProperty(IR::Temp *sourceBase, const QString &sourceName, IR::Temp *target); - virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + virtual void setProperty(IR::Temp *source, IR::Temp *targetBase, const QString &targetName); virtual void getElement(IR::Temp *sourceBase, IR::Temp *sourceIndex, IR::Temp *target); virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index c2f1811824..2f8a5d2d45 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -689,20 +689,24 @@ void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR:: if (useFastLookups) { VM::String *s = identifier(name); uint index = addLookup(s); - generateFunctionCall(target, __qmljs_get_property_lookup, Assembler::ContextRegister, base, Assembler::TrustedImm32(index)); + generateFunctionCall(Assembler::Void, __qmljs_get_property_lookup, Assembler::ContextRegister, Assembler::PointerToValue(target), + Assembler::PointerToValue(base), Assembler::TrustedImm32(index)); } else { - generateFunctionCall(target, __qmljs_get_property, Assembler::ContextRegister, base, identifier(name)); + generateFunctionCall(Assembler::Void, __qmljs_get_property, Assembler::ContextRegister, Assembler::PointerToValue(target), + Assembler::PointerToValue(base), identifier(name)); } } -void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +void InstructionSelection::setProperty(IR::Temp *source, IR::Temp *targetBase, const QString &targetName) { if (useFastLookups) { VM::String *s = identifier(targetName); uint index = addLookup(s); - generateFunctionCall(Assembler::Void, __qmljs_set_property_lookup, Assembler::ContextRegister, targetBase, Assembler::TrustedImm32(index), source); + generateFunctionCall(Assembler::Void, __qmljs_set_property_lookup, Assembler::ContextRegister, Assembler::PointerToValue(targetBase), + Assembler::TrustedImm32(index), Assembler::PointerToValue(source)); } else { - generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, targetBase, identifier(targetName), source); + generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, Assembler::PointerToValue(targetBase), + identifier(targetName), Assembler::PointerToValue(source)); } } diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 7b42f110f6..ee9573e2c8 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -712,7 +712,7 @@ protected: virtual void setActivationProperty(IR::Temp *source, const QString &targetName); virtual void initClosure(IR::Closure *closure, IR::Temp *target); virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); - virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + virtual void setProperty(IR::Temp *source, IR::Temp *targetBase, const QString &targetName); virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target); virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index 2eb1b1db92..35cd826b47 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -146,8 +146,8 @@ void InstructionSelection::visitMove(IR::Move *s) } } else if (IR::Member *m = s->target->asMember()) { if (IR::Temp *base = m->base->asTemp()) { - if (s->source->asTemp() || s->source->asConst()) { - setProperty(s->source, base, *m->name); + if (s->source->asTemp()) { + setProperty(s->source->asTemp(), base, *m->name); return; } } diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h index d135cd3b55..cb04f0cf41 100644 --- a/src/v4/qv4isel_p.h +++ b/src/v4/qv4isel_p.h @@ -129,7 +129,7 @@ public: // to implement by subclasses: virtual void setActivationProperty(IR::Temp *source, const QString &targetName) = 0; virtual void initClosure(IR::Closure *closure, IR::Temp *target) = 0; virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target) = 0; - virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) = 0; + virtual void setProperty(IR::Temp *source, IR::Temp *targetBase, const QString &targetName) = 0; virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) = 0; virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) = 0; virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0; -- cgit v1.2.3 From 19b4d8c55c0f57d613556de1c7c0db8a25d36607 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 13 Feb 2013 15:36:53 +0100 Subject: throwing a type error doesn't return a value Change-Id: Id7a03c1804e66dfad8448e3e3ec70832152e09fa Reviewed-by: Simon Hausmann --- src/v4/qmljs_runtime.cpp | 25 +++++++++++-------------- src/v4/qmljs_runtime.h | 6 +++--- src/v4/qv4argumentsobject.cpp | 7 ++++++- src/v4/qv4functionobject.cpp | 11 +++++++++-- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 356d2a8099..985c3ddeab 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -235,23 +235,21 @@ Value __qmljs_add_helper(Value left, Value right, ExecutionContext *ctx) Value __qmljs_instanceof(Value left, Value right, ExecutionContext *ctx) { - if (FunctionObject *function = right.asFunctionObject()) { - bool r = function->hasInstance(ctx, left); - return Value::fromBoolean(r); - } + FunctionObject *function = right.asFunctionObject(); + if (!function) + __qmljs_throw_type_error(ctx); - return __qmljs_throw_type_error(ctx); + bool r = function->hasInstance(ctx, left); + return Value::fromBoolean(r); } Value __qmljs_in(Value left, Value right, ExecutionContext *ctx) { - if (right.isObject()) { - String *s = left.toString(ctx); - bool r = right.objectValue()->__hasProperty__(ctx, s); - return Value::fromBoolean(r); - } else { - return __qmljs_throw_type_error(ctx); - } + if (!right.isObject()) + __qmljs_throw_type_error(ctx); + String *s = left.toString(ctx); + bool r = right.objectValue()->__hasProperty__(ctx, s); + return Value::fromBoolean(r); } void __qmljs_inplace_bit_and_name(Value value, String *name, ExecutionContext *ctx) @@ -540,10 +538,9 @@ Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int type return Value::undefinedValue(); } -Value __qmljs_throw_type_error(ExecutionContext *ctx) +void __qmljs_throw_type_error(ExecutionContext *ctx) { ctx->throwTypeError(); - return Value::undefinedValue(); } Value __qmljs_new_object(ExecutionContext *ctx) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 97fa256b28..20389f4d44 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -160,7 +160,7 @@ String *__qmljs_identifier_from_utf8(ExecutionContext *ctx, const char *s); // objects Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int typeHint); -Value __qmljs_throw_type_error(ExecutionContext *ctx); +void __qmljs_throw_type_error(ExecutionContext *ctx); Value __qmljs_new_object(ExecutionContext *ctx); Value __qmljs_new_boolean_object(ExecutionContext *ctx, bool boolean); Value __qmljs_new_number_object(ExecutionContext *ctx, double n); @@ -407,7 +407,7 @@ inline Value __qmljs_to_string(Value value, ExecutionContext *ctx) if (prim.isPrimitive()) return __qmljs_to_string(prim, ctx); else - return __qmljs_throw_type_error(ctx); + __qmljs_throw_type_error(ctx); break; } case Value::Integer_Type: @@ -425,7 +425,7 @@ inline Value __qmljs_to_object(Value value, ExecutionContext *ctx) switch (value.type()) { case Value::Undefined_Type: case Value::Null_Type: - return __qmljs_throw_type_error(ctx); + __qmljs_throw_type_error(ctx); break; case Value::Boolean_Type: return __qmljs_new_boolean_object(ctx, value.booleanValue()); diff --git a/src/v4/qv4argumentsobject.cpp b/src/v4/qv4argumentsobject.cpp index 404edbbd17..92251c717d 100644 --- a/src/v4/qv4argumentsobject.cpp +++ b/src/v4/qv4argumentsobject.cpp @@ -43,6 +43,11 @@ namespace QQmlJS { namespace VM { +static Value throwTypeError(ExecutionContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount) : Object(context->engine), context(context) @@ -53,7 +58,7 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterC if (context->strictMode) { for (uint i = 0; i < context->argumentCount; ++i) Object::__put__(context, QString::number(i), context->arguments[i]); - FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, __qmljs_throw_type_error); + FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, throwTypeError); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); pd.configurable = PropertyDescriptor::Disabled; pd.enumberable = PropertyDescriptor::Disabled; diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 94f923a47e..2af08ae7f1 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -338,6 +338,13 @@ Value FunctionPrototype::method_bind(ExecutionContext *ctx) return Value::fromObject(f); } + +static Value throwTypeError(ExecutionContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) : FunctionObject(scope) { @@ -372,7 +379,7 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) pd->value = Value::fromObject(proto); if (scope->strictMode) { - FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error); + FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, throwTypeError); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); pd.configurable = PropertyDescriptor::Disabled; pd.enumberable = PropertyDescriptor::Disabled; @@ -439,7 +446,7 @@ BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Va len = 0; defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(len)); - FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error); + FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, throwTypeError); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); pd.configurable = PropertyDescriptor::Disabled; pd.enumberable = PropertyDescriptor::Disabled; -- cgit v1.2.3 From c66451af37eb3eea958b66268111c9d34820879e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 13 Feb 2013 16:22:10 +0100 Subject: make binops pointer based Change-Id: Ic03e134ba2c0f51f6a72bd875aba1d30eee8fd2d Reviewed-by: Simon Hausmann --- src/v4/llvm_runtime.cpp | 42 +++---- src/v4/moth/qv4instr_moth_p.h | 2 +- src/v4/moth/qv4isel_moth.cpp | 5 +- src/v4/moth/qv4isel_moth_p.h | 2 +- src/v4/moth/qv4vme_moth.cpp | 2 +- src/v4/qmljs_environment.cpp | 5 +- src/v4/qmljs_runtime.cpp | 29 ++--- src/v4/qmljs_runtime.h | 249 +++++++++++++++++++++++------------------- src/v4/qv4codegen.cpp | 6 +- src/v4/qv4isel_llvm.cpp | 2 +- src/v4/qv4isel_llvm_p.h | 2 +- src/v4/qv4isel_masm.cpp | 7 +- src/v4/qv4isel_masm_p.h | 6 +- src/v4/qv4isel_p.cpp | 9 +- src/v4/qv4isel_p.h | 2 +- src/v4/qv4object.cpp | 8 +- 16 files changed, 202 insertions(+), 176 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index c045507556..635d9c01cd 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -105,107 +105,107 @@ bool __qmljs_llvm_to_boolean(ExecutionContext *ctx, const Value *value) void __qmljs_llvm_bit_and(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_bit_and(*left, *right, ctx); + __qmljs_bit_and(ctx, result, left, right); } void __qmljs_llvm_bit_or(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_bit_or(*left, *right, ctx); + __qmljs_bit_or(ctx, result, left, right); } void __qmljs_llvm_bit_xor(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_bit_xor(*left, *right, ctx); + __qmljs_bit_xor(ctx, result, left, right); } void __qmljs_llvm_add(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_add(*left, *right, ctx); + __qmljs_add(ctx, result, left, right); } void __qmljs_llvm_sub(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_sub(*left, *right, ctx); + __qmljs_sub(ctx, result, left, right); } void __qmljs_llvm_mul(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_mul(*left, *right, ctx); + __qmljs_mul(ctx, result, left, right); } void __qmljs_llvm_div(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_div(*left, *right, ctx); + __qmljs_div(ctx, result, left, right); } void __qmljs_llvm_mod(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_mod(*left, *right, ctx); + __qmljs_mod(ctx, result, left, right); } void __qmljs_llvm_shl(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_shl(*left, *right, ctx); + __qmljs_shl(ctx, result, left, right); } void __qmljs_llvm_shr(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_shr(*left, *right, ctx); + __qmljs_shr(ctx, result, left, right); } void __qmljs_llvm_ushr(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_ushr(*left, *right, ctx); + __qmljs_ushr(ctx, result, left, right); } void __qmljs_llvm_gt(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_gt(*left, *right, ctx); + __qmljs_gt(ctx, result, left, right); } void __qmljs_llvm_lt(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_lt(*left, *right, ctx); + __qmljs_lt(ctx, result, left, right); } void __qmljs_llvm_ge(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_ge(*left, *right, ctx); + __qmljs_ge(ctx, result, left, right); } void __qmljs_llvm_le(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_le(*left, *right, ctx); + __qmljs_le(ctx, result, left, right); } void __qmljs_llvm_eq(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_eq(*left, *right, ctx); + __qmljs_eq(ctx, result, left, right); } void __qmljs_llvm_ne(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_ne(*left, *right, ctx); + __qmljs_ne(ctx, result, left, right); } void __qmljs_llvm_se(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_se(*left, *right, ctx); + __qmljs_se(ctx, result, left, right); } void __qmljs_llvm_sne(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_sne(*left, *right, ctx); + __qmljs_sne(ctx, result, left, right); } void __qmljs_llvm_instanceof(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_instanceof(*left, *right, ctx); + __qmljs_instanceof(ctx, result, left, right); } void __qmljs_llvm_in(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - *result = __qmljs_in(*left, *right, ctx); + __qmljs_in(ctx, result, left, right); } void __qmljs_llvm_uplus(ExecutionContext *ctx, Value *result, const Value *value) diff --git a/src/v4/moth/qv4instr_moth_p.h b/src/v4/moth/qv4instr_moth_p.h index d57371324b..9c85228eb9 100644 --- a/src/v4/moth/qv4instr_moth_p.h +++ b/src/v4/moth/qv4instr_moth_p.h @@ -406,7 +406,7 @@ union Instr }; struct instr_binop { MOTH_INSTR_HEADER - VM::Value (*alu)(const VM::Value , const VM::Value, VM::ExecutionContext *); + VM::BinOp alu; Param lhs; Param rhs; Param result; diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index b054f13701..d4ff709a8f 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -172,8 +172,7 @@ private: }; #undef DBTC -typedef VM::Value (*ALUFunction)(const VM::Value, const VM::Value, VM::ExecutionContext*); -inline ALUFunction aluOpFunction(IR::AluOp op) +inline VM::BinOp aluOpFunction(IR::AluOp op) { switch (op) { case IR::OpInvalid: @@ -506,7 +505,7 @@ void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp * } } -void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) +void InstructionSelection::binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target) { Instruction::Binop binop; binop.alu = aluOpFunction(oper); diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h index 691dca23a9..89d7a5bcee 100644 --- a/src/v4/moth/qv4isel_moth_p.h +++ b/src/v4/moth/qv4isel_moth_p.h @@ -74,7 +74,7 @@ protected: virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); - virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); + virtual void binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target); virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index fba5364f64..05e30a6a30 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -410,7 +410,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(Unop) MOTH_BEGIN_INSTR(Binop) - VALUE(instr.result) = instr.alu(VALUE(instr.lhs), VALUE(instr.rhs), context); + instr.alu(context, VALUEPTR(instr.result), VALUEPTR(instr.lhs), VALUEPTR(instr.rhs)); MOTH_END_INSTR(Binop) MOTH_BEGIN_INSTR(Ret) diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp index 0aee96eeb9..1b1a753207 100644 --- a/src/v4/qmljs_environment.cpp +++ b/src/v4/qmljs_environment.cpp @@ -506,8 +506,9 @@ Value ExecutionContext::getPropertyAndBase(String *name, Object **base) void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) { Value lhs = getProperty(name); - value = op(lhs, value, this); - setProperty(name, value); + Value result; + op(this, &result, &lhs, &value); + setProperty(name, result); } void ExecutionContext::throwError(Value value) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 985c3ddeab..c995e4e391 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -216,40 +216,41 @@ Value __qmljs_delete_name(ExecutionContext *ctx, String *name) return Value::fromBoolean(ctx->deleteProperty(name)); } -Value __qmljs_add_helper(Value left, Value right, ExecutionContext *ctx) +void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { - Value pleft = __qmljs_to_primitive(left, ctx, PREFERREDTYPE_HINT); - Value pright = __qmljs_to_primitive(right, ctx, PREFERREDTYPE_HINT); + Value pleft = __qmljs_to_primitive(*left, ctx, PREFERREDTYPE_HINT); + Value pright = __qmljs_to_primitive(*right, ctx, PREFERREDTYPE_HINT); if (pleft.isString() || pright.isString()) { if (!pleft.isString()) pleft = __qmljs_to_string(pleft, ctx); if (!pright.isString()) pright = __qmljs_to_string(pright, ctx); String *string = __qmljs_string_concat(ctx, pleft.stringValue(), pright.stringValue()); - return Value::fromString(string); + *result = Value::fromString(string); + return; } double x = __qmljs_to_number(pleft, ctx); double y = __qmljs_to_number(pright, ctx); - return Value::fromDouble(x + y); + *result = Value::fromDouble(x + y); } -Value __qmljs_instanceof(Value left, Value right, ExecutionContext *ctx) +void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { - FunctionObject *function = right.asFunctionObject(); + FunctionObject *function = right->asFunctionObject(); if (!function) __qmljs_throw_type_error(ctx); - bool r = function->hasInstance(ctx, left); - return Value::fromBoolean(r); + bool r = function->hasInstance(ctx, *left); + *result = Value::fromBoolean(r); } -Value __qmljs_in(Value left, Value right, ExecutionContext *ctx) +void __qmljs_in(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { - if (!right.isObject()) + if (!right->isObject()) __qmljs_throw_type_error(ctx); - String *s = left.toString(ctx); - bool r = right.objectValue()->__hasProperty__(ctx, s); - return Value::fromBoolean(r); + String *s = left->toString(ctx); + bool r = right->objectValue()->__hasProperty__(ctx, s); + *result = Value::fromBoolean(r); } void __qmljs_inplace_bit_and_name(Value value, String *name, ExecutionContext *ctx) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 20389f4d44..70ac132c6f 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -219,31 +219,31 @@ void __qmljs_delete_exception_handler(ExecutionContext *context); Value __qmljs_get_exception(ExecutionContext *context); // binary operators -typedef Value (*BinOp)(Value left, Value right, ExecutionContext *ctx); - -Value __qmljs_instanceof(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_in(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_bit_or(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_bit_xor(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_bit_and(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_add(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_sub(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_mul(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_div(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_mod(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_shl(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_shr(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_ushr(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_gt(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_lt(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_ge(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_le(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_eq(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_ne(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_se(Value left, Value right, ExecutionContext *ctx); -Value __qmljs_sne(Value left, Value right, ExecutionContext *ctx); - -Value __qmljs_add_helper(Value left, Value right, ExecutionContext *ctx); +typedef void (*BinOp)(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); + +void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_in(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_bit_or(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_bit_xor(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_bit_and(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_add(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_sub(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_mul(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_div(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_mod(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_shl(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_shr(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_ushr(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_gt(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_lt(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_ge(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_le(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_eq(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_ne(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_se(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); +void __qmljs_sne(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); + +void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); void __qmljs_inplace_bit_and_name(Value value, String *name, ExecutionContext *ctx); void __qmljs_inplace_bit_or_name(Value value, String *name, ExecutionContext *ctx); @@ -519,204 +519,225 @@ inline Value __qmljs_not(Value value, ExecutionContext *ctx) } // binary operators -inline Value __qmljs_bit_or(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_bit_or(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - if (Value::integerCompatible(left, right)) - return Value::fromInt32(left.integerValue() | right.integerValue()); + if (Value::integerCompatible(*left, *right)) { + *result = Value::fromInt32(left->integerValue() | right->integerValue()); + return; + } - int lval = Value::toInt32(__qmljs_to_number(left, ctx)); - int rval = Value::toInt32(__qmljs_to_number(right, ctx)); - return Value::fromInt32(lval | rval); + int lval = Value::toInt32(__qmljs_to_number(*left, ctx)); + int rval = Value::toInt32(__qmljs_to_number(*right, ctx)); + *result = Value::fromInt32(lval | rval); } -inline Value __qmljs_bit_xor(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_bit_xor(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - if (Value::integerCompatible(left, right)) - return Value::fromInt32(left.integerValue() ^ right.integerValue()); + if (Value::integerCompatible(*left, *right)) { + *result = Value::fromInt32(left->integerValue() ^ right->integerValue()); + return; + } - int lval = Value::toInt32(__qmljs_to_number(left, ctx)); - int rval = Value::toInt32(__qmljs_to_number(right, ctx)); - return Value::fromInt32(lval ^ rval); + int lval = Value::toInt32(__qmljs_to_number(*left, ctx)); + int rval = Value::toInt32(__qmljs_to_number(*right, ctx)); + *result = Value::fromInt32(lval ^ rval); } -inline Value __qmljs_bit_and(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_bit_and(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - if (Value::integerCompatible(left, right)) - return Value::fromInt32(left.integerValue() & right.integerValue()); + if (Value::integerCompatible(*left, *right)) { + *result = Value::fromInt32(left->integerValue() & right->integerValue()); + return; + } - int lval = Value::toInt32(__qmljs_to_number(left, ctx)); - int rval = Value::toInt32(__qmljs_to_number(right, ctx)); - return Value::fromInt32(lval & rval); + int lval = Value::toInt32(__qmljs_to_number(*left, ctx)); + int rval = Value::toInt32(__qmljs_to_number(*right, ctx)); + *result = Value::fromInt32(lval & rval); } -inline Value __qmljs_add(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_add(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); #ifdef QMLJS_INLINE_MATH - if (Value::integerCompatible(left, right)) - return add_int32(left.integerValue(), right.integerValue()); + if (Value::integerCompatible(*left, *right)) { + *result = add_int32(left->integerValue(), right->integerValue()); + return; + } #endif - if (Value::bothDouble(left, right)) - return Value::fromDouble(left.doubleValue() + right.doubleValue()); + if (Value::bothDouble(*left, *right)) { + *result = Value::fromDouble(left->doubleValue() + right->doubleValue()); + return; + } - return __qmljs_add_helper(left, right, ctx); + __qmljs_add_helper(ctx, result, left, right); } -inline Value __qmljs_sub(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_sub(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); #ifdef QMLJS_INLINE_MATH - if (Value::integerCompatible(left, right)) - return sub_int32(left.integerValue(), right.integerValue()); + if (Value::integerCompatible(*left, *right)) { + *result = sub_int32(left->integerValue(), right->integerValue()); + return; + } #endif - double lval = __qmljs_to_number(left, ctx); - double rval = __qmljs_to_number(right, ctx); - return Value::fromDouble(lval - rval); + double lval = __qmljs_to_number(*left, ctx); + double rval = __qmljs_to_number(*right, ctx); + *result = Value::fromDouble(lval - rval); } -inline Value __qmljs_mul(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_mul(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); #ifdef QMLJS_INLINE_MATH - if (Value::integerCompatible(left, right)) - return mul_int32(left.integerValue(), right.integerValue()); + if (Value::integerCompatible(*left, *right)) { + *result = mul_int32(left->integerValue(), right->integerValue()); + return; + } #endif - double lval = __qmljs_to_number(left, ctx); - double rval = __qmljs_to_number(right, ctx); - return Value::fromDouble(lval * rval); + double lval = __qmljs_to_number(*left, ctx); + double rval = __qmljs_to_number(*right, ctx); + *result = Value::fromDouble(lval * rval); } -inline Value __qmljs_div(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_div(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - double lval = __qmljs_to_number(left, ctx); - double rval = __qmljs_to_number(right, ctx); - return Value::fromDouble(lval / rval); + double lval = __qmljs_to_number(*left, ctx); + double rval = __qmljs_to_number(*right, ctx); + *result = Value::fromDouble(lval / rval); } -inline Value __qmljs_mod(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_mod(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - if (Value::integerCompatible(left, right) && right.integerValue() != 0) - return Value::fromInt32(left.integerValue() % right.integerValue()); + if (Value::integerCompatible(*left, *right) && right->integerValue() != 0) { + *result = Value::fromInt32(left->integerValue() % right->integerValue()); + return; + } - double lval = __qmljs_to_number(left, ctx); - double rval = __qmljs_to_number(right, ctx); - return Value::fromDouble(fmod(lval, rval)); + double lval = __qmljs_to_number(*left, ctx); + double rval = __qmljs_to_number(*right, ctx); + *result = Value::fromDouble(fmod(lval, rval)); } -inline Value __qmljs_shl(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_shl(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - if (Value::integerCompatible(left, right)) - return Value::fromInt32(left.integerValue() << ((uint(right.integerValue()) & 0x1f))); + if (Value::integerCompatible(*left, *right)) { + *result = Value::fromInt32(left->integerValue() << ((uint(right->integerValue()) & 0x1f))); + return; + } - int lval = Value::toInt32(__qmljs_to_number(left, ctx)); - unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; - return Value::fromInt32(lval << rval); + int lval = Value::toInt32(__qmljs_to_number(*left, ctx)); + unsigned rval = Value::toUInt32(__qmljs_to_number(*right, ctx)) & 0x1f; + *result = Value::fromInt32(lval << rval); } -inline Value __qmljs_shr(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_shr(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - if (Value::integerCompatible(left, right)) - return Value::fromInt32(left.integerValue() >> ((uint(right.integerValue()) & 0x1f))); + if (Value::integerCompatible(*left, *right)) { + *result = Value::fromInt32(left->integerValue() >> ((uint(right->integerValue()) & 0x1f))); + return; + } - int lval = Value::toInt32(__qmljs_to_number(left, ctx)); - unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; - return Value::fromInt32(lval >> rval); + int lval = Value::toInt32(__qmljs_to_number(*left, ctx)); + unsigned rval = Value::toUInt32(__qmljs_to_number(*right, ctx)) & 0x1f; + *result = Value::fromInt32(lval >> rval); } -inline Value __qmljs_ushr(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_ushr(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - uint result; - if (Value::integerCompatible(left, right)) { - result = uint(left.integerValue()) >> (uint(right.integerValue()) & 0x1f); + uint res; + if (Value::integerCompatible(*left, *right)) { + res = uint(left->integerValue()) >> (uint(right->integerValue()) & 0x1f); } else { - unsigned lval = Value::toUInt32(__qmljs_to_number(left, ctx)); - unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; - result = lval >> rval; + unsigned lval = Value::toUInt32(__qmljs_to_number(*left, ctx)); + unsigned rval = Value::toUInt32(__qmljs_to_number(*right, ctx)) & 0x1f; + res = lval >> rval; } - if (result > INT_MAX) - return Value::fromDouble(result); - return Value::fromInt32(result); + if (res > INT_MAX) + *result = Value::fromDouble(res); + else + *result = Value::fromInt32(res); } -inline Value __qmljs_gt(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_gt(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - return Value::fromBoolean(__qmljs_cmp_gt(left, right, ctx)); + *result = Value::fromBoolean(__qmljs_cmp_gt(*left, *right, ctx)); } -inline Value __qmljs_lt(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_lt(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - return Value::fromBoolean(__qmljs_cmp_lt(left, right, ctx)); + *result = Value::fromBoolean(__qmljs_cmp_lt(*left, *right, ctx)); } -inline Value __qmljs_ge(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_ge(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - return Value::fromBoolean(__qmljs_cmp_ge(left, right, ctx)); + *result = Value::fromBoolean(__qmljs_cmp_ge(*left, *right, ctx)); } -inline Value __qmljs_le(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_le(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - return Value::fromBoolean(__qmljs_cmp_le(left, right, ctx)); + *result = Value::fromBoolean(__qmljs_cmp_le(*left, *right, ctx)); } -inline Value __qmljs_eq(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_eq(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - return Value::fromBoolean(__qmljs_cmp_eq(left, right, ctx)); + *result = Value::fromBoolean(__qmljs_cmp_eq(*left, *right, ctx)); } -inline Value __qmljs_ne(Value left, Value right, ExecutionContext *ctx) +inline void __qmljs_ne(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - return Value::fromBoolean(!__qmljs_cmp_eq(left, right, ctx)); + *result = Value::fromBoolean(!__qmljs_cmp_eq(*left, *right, ctx)); } -inline Value __qmljs_se(Value left, Value right, ExecutionContext *) +inline void __qmljs_se(ExecutionContext *, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - bool r = __qmljs_strict_equal(left, right); - return Value::fromBoolean(r); + bool r = __qmljs_strict_equal(*left, *right); + *result = Value::fromBoolean(r); } -inline Value __qmljs_sne(Value left, Value right, ExecutionContext *) +inline void __qmljs_sne(ExecutionContext *, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - bool r = ! __qmljs_strict_equal(left, right); - return Value::fromBoolean(r); + bool r = ! __qmljs_strict_equal(*left, *right); + *result = Value::fromBoolean(r); } inline Bool __qmljs_cmp_gt(Value left, Value right, ExecutionContext *ctx) @@ -839,7 +860,8 @@ inline Bool __qmljs_cmp_instanceof(Value left, Value right, ExecutionContext *ct { TRACE2(left, right); - Value v = __qmljs_instanceof(left, right, ctx); + Value v; + __qmljs_instanceof(ctx, &v, &left, &right); return v.booleanValue(); } @@ -847,7 +869,8 @@ inline uint __qmljs_cmp_in(Value left, Value right, ExecutionContext *ctx) { TRACE2(left, right); - Value v = __qmljs_in(left, right, ctx); + Value v; + __qmljs_in(ctx, &v, &left, &right); return v.booleanValue(); } diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index 7faf22c3bd..d82f1b1f8e 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -683,13 +683,13 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) } } - if (!left->asTemp() && !left->asConst()) { + if (!left->asTemp()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), left); left = _block->TEMP(t); } - if (!right->asTemp() && !right->asConst()) { + if (!right->asTemp()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), right); right = _block->TEMP(t); @@ -716,7 +716,7 @@ void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) _block->MOVE(_block->TEMP(t), source); source = _block->TEMP(t); } - if (!target->asTemp() && source->asConst()) { + if (source->asConst() && (!target->asTemp() || op != IR::OpInvalid)) { unsigned t = _block->newTemp(); _block->MOVE(_block->TEMP(t), source); source = _block->TEMP(t); diff --git a/src/v4/qv4isel_llvm.cpp b/src/v4/qv4isel_llvm.cpp index 6131e304d8..744f50025f 100644 --- a/src/v4/qv4isel_llvm.cpp +++ b/src/v4/qv4isel_llvm.cpp @@ -684,7 +684,7 @@ void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp * } } -void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) +void InstructionSelection::binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target) { const char *opName = 0; switch (oper) { diff --git a/src/v4/qv4isel_llvm_p.h b/src/v4/qv4isel_llvm_p.h index 24ed317501..36b1c21dbc 100644 --- a/src/v4/qv4isel_llvm_p.h +++ b/src/v4/qv4isel_llvm_p.h @@ -118,7 +118,7 @@ public: // methods from InstructionSelection: virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); - virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); + virtual void binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target); virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 2f8a5d2d45..7de1b08586 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -251,7 +251,7 @@ const Assembler::BinaryOperationInfo Assembler::binaryOperations[QQmlJS::IR::Las NULL_OP // OpOr }; -void Assembler::generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Expr* left, IR::Expr* right) +void Assembler::generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Temp *left, IR::Temp *right) { const BinaryOperationInfo& info = binaryOperations[operation]; if (!info.fallbackImplementation) { @@ -332,7 +332,8 @@ void Assembler::generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Expr* l } // Fallback - generateFunctionCallImp(target, info.name, info.fallbackImplementation, left, right, ContextRegister); + generateFunctionCallImp(Assembler::Void, info.name, info.fallbackImplementation, ContextRegister, + Assembler::PointerToValue(target), Assembler::PointerToValue(left), Assembler::PointerToValue(right)); if (binOpFinished.isSet()) binOpFinished.link(this); @@ -748,7 +749,7 @@ void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp * Assembler::ContextRegister); } -void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) +void InstructionSelection::binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target) { _as->generateBinOp(oper, target, leftSource, rightSource); } diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index ee9573e2c8..fb075cd477 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -531,14 +531,14 @@ public: struct BinaryOperationInfo { const char *name; - VM::Value (*fallbackImplementation)(const VM::Value, const VM::Value, VM::ExecutionContext *); + VM::BinOp fallbackImplementation; MemRegBinOp inlineMemRegOp; ImmRegBinOp inlineImmRegOp; }; static const BinaryOperationInfo binaryOperations[QQmlJS::IR::LastAluOp + 1]; - void generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Expr* left, IR::Expr* right); + void generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Temp* left, IR::Temp* right); Jump inline_add32(Address addr, RegisterID reg) { @@ -717,7 +717,7 @@ protected: virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); - virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); + virtual void binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target); virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index 35cd826b47..8c79c32bed 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -124,9 +124,8 @@ void InstructionSelection::visitMove(IR::Move *s) return; } } else if (IR::Binop *b = s->source->asBinop()) { - if ((b->left->asTemp() || b->left->asConst()) - && (b->right->asTemp() || b->right->asConst())) { - binop(b->op, b->left, b->right, t); + if (b->left->asTemp() && b->right->asTemp()) { + binop(b->op, b->left->asTemp(), b->right->asTemp(), t); return; } } else if (IR::Call *c = s->source->asCall()) { @@ -160,8 +159,8 @@ void InstructionSelection::visitMove(IR::Move *s) } else { // inplace assignment, e.g. x += 1, ++x, ... if (IR::Temp *t = s->target->asTemp()) { - if (s->source->asTemp() || s->source->asConst()) { - binop(s->op, t, s->source, t); + if (s->source->asTemp()) { + binop(s->op, t, s->source->asTemp(), t); return; } } else if (IR::Name *n = s->target->asName()) { diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h index cb04f0cf41..9e55a97aee 100644 --- a/src/v4/qv4isel_p.h +++ b/src/v4/qv4isel_p.h @@ -134,7 +134,7 @@ public: // to implement by subclasses: virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) = 0; virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0; virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0; - virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) = 0; + virtual void binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target) = 0; virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) = 0; virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) = 0; virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) = 0; diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index a6259f2dda..e28a5fb6fc 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -141,7 +141,8 @@ void Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *c { bool hasProperty = false; Value v = __get__(ctx, name, &hasProperty); - Value result = op(v, rhs, ctx); + Value result; + op(ctx, &result, &v, &rhs); __put__(ctx, name, result); } @@ -151,8 +152,9 @@ void Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ct if (idx < UINT_MAX) { bool hasProperty = false; Value v = __get__(ctx, idx, &hasProperty); - v = op(v, rhs, ctx); - __put__(ctx, idx, v); + Value result; + op(ctx, &result, &v, &rhs); + __put__(ctx, idx, result); return; } String *name = index.toString(ctx); -- cgit v1.2.3 From 13f815f6e5fd1def1dbadab8bdb5bb1e97a4b14f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 13 Feb 2013 21:14:17 +0100 Subject: Convert inplace binops to pointer based calling convention Change-Id: Ie39fb9160573c79ea765466fc9750e2f50b52ab3 Reviewed-by: Simon Hausmann --- src/v4/llvm_runtime.cpp | 66 ++++++++-------- src/v4/moth/qv4instr_moth_p.h | 6 +- src/v4/moth/qv4isel_moth.cpp | 16 ++-- src/v4/moth/qv4isel_moth_p.h | 6 +- src/v4/moth/qv4vme_moth.cpp | 18 ++--- src/v4/qmljs_environment.cpp | 4 +- src/v4/qmljs_environment.h | 2 +- src/v4/qmljs_runtime.cpp | 176 +++++++++++++++++++++--------------------- src/v4/qmljs_runtime.h | 74 +++++++++--------- src/v4/qv4isel_llvm.cpp | 10 +-- src/v4/qv4isel_llvm_p.h | 6 +- src/v4/qv4isel_masm.cpp | 22 +++--- src/v4/qv4isel_masm_p.h | 6 +- src/v4/qv4isel_p.cpp | 12 +-- src/v4/qv4isel_p.h | 6 +- src/v4/qv4object.cpp | 14 ++-- src/v4/qv4object.h | 4 +- 17 files changed, 227 insertions(+), 221 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 635d9c01cd..3009da6688 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -230,167 +230,167 @@ void __qmljs_llvm_not(ExecutionContext *ctx, Value *result, const Value *value) void __qmljs_llvm_inplace_bit_and_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_bit_and_name(*src, dest, ctx); + __qmljs_inplace_bit_and_name(ctx, dest, src); } void __qmljs_llvm_inplace_bit_or_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_bit_or_name(*src, dest, ctx); + __qmljs_inplace_bit_or_name(ctx, dest, src); } void __qmljs_llvm_inplace_bit_xor_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_bit_xor_name(*src, dest, ctx); + __qmljs_inplace_bit_xor_name(ctx, dest, src); } void __qmljs_llvm_inplace_add_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_add_name(*src, dest, ctx); + __qmljs_inplace_add_name(ctx, dest, src); } void __qmljs_llvm_inplace_sub_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_sub_name(*src, dest, ctx); + __qmljs_inplace_sub_name(ctx, dest, src); } void __qmljs_llvm_inplace_mul_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_mul_name(*src, dest, ctx); + __qmljs_inplace_mul_name(ctx, dest, src); } void __qmljs_llvm_inplace_div_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_div_name(*src, dest, ctx); + __qmljs_inplace_div_name(ctx, dest, src); } void __qmljs_llvm_inplace_mod_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_mod_name(*src, dest, ctx); + __qmljs_inplace_mod_name(ctx, dest, src); } void __qmljs_llvm_inplace_shl_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_shl_name(*src, dest, ctx); + __qmljs_inplace_shl_name(ctx, dest, src); } void __qmljs_llvm_inplace_shr_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_shr_name(*src, dest, ctx); + __qmljs_inplace_shr_name(ctx, dest, src); } void __qmljs_llvm_inplace_ushr_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_ushr_name(*src, dest, ctx); + __qmljs_inplace_ushr_name(ctx, dest, src); } void __qmljs_llvm_inplace_bit_and_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_bit_and_element(*base, *index, *value, ctx); + __qmljs_inplace_bit_and_element(ctx, base, index, value); } void __qmljs_llvm_inplace_bit_or_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_bit_or_element(*base, *index, *value, ctx); + __qmljs_inplace_bit_or_element(ctx, base, index, value); } void __qmljs_llvm_inplace_bit_xor_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_bit_xor_element(*base, *index, *value, ctx); + __qmljs_inplace_bit_xor_element(ctx, base, index, value); } void __qmljs_llvm_inplace_add_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_add_element(*base, *index, *value, ctx); + __qmljs_inplace_add_element(ctx, base, index, value); } void __qmljs_llvm_inplace_sub_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_sub_element(*base, *index, *value, ctx); + __qmljs_inplace_sub_element(ctx, base, index, value); } void __qmljs_llvm_inplace_mul_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_mul_element(*base, *index, *value, ctx); + __qmljs_inplace_mul_element(ctx, base, index, value); } void __qmljs_llvm_inplace_div_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_div_element(*base, *index, *value, ctx); + __qmljs_inplace_div_element(ctx, base, index, value); } void __qmljs_llvm_inplace_mod_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_mod_element(*base, *index, *value, ctx); + __qmljs_inplace_mod_element(ctx, base, index, value); } void __qmljs_llvm_inplace_shl_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_shl_element(*base, *index, *value, ctx); + __qmljs_inplace_shl_element(ctx, base, index, value); } void __qmljs_llvm_inplace_shr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_shr_element(*base, *index, *value, ctx); + __qmljs_inplace_shr_element(ctx, base, index, value); } void __qmljs_llvm_inplace_ushr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_ushr_element(*base, *index, *value, ctx); + __qmljs_inplace_ushr_element(ctx, base, index, value); } void __qmljs_llvm_inplace_bit_and_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_bit_and_member(*value, *base, member, ctx); + __qmljs_inplace_bit_and_member(ctx, base, member, value); } void __qmljs_llvm_inplace_bit_or_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_bit_or_member(*value, *base, member, ctx); + __qmljs_inplace_bit_or_member(ctx, base, member, value); } void __qmljs_llvm_inplace_bit_xor_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_bit_xor_member(*value, *base, member, ctx); + __qmljs_inplace_bit_xor_member(ctx, base, member, value); } void __qmljs_llvm_inplace_add_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_add_member(*value, *base, member, ctx); + __qmljs_inplace_add_member(ctx, base, member, value); } void __qmljs_llvm_inplace_sub_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_sub_member(*value, *base, member, ctx); + __qmljs_inplace_sub_member(ctx, base, member, value); } void __qmljs_llvm_inplace_mul_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_mul_member(*value, *base, member, ctx); + __qmljs_inplace_mul_member(ctx, base, member, value); } void __qmljs_llvm_inplace_div_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_div_member(*value, *base, member, ctx); + __qmljs_inplace_div_member(ctx, base, member, value); } void __qmljs_llvm_inplace_mod_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_mod_member(*value, *base, member, ctx); + __qmljs_inplace_mod_member(ctx, base, member, value); } void __qmljs_llvm_inplace_shl_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_shl_member(*value, *base, member, ctx); + __qmljs_inplace_shl_member(ctx, base, member, value); } void __qmljs_llvm_inplace_shr_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_shr_member(*value, *base, member, ctx); + __qmljs_inplace_shr_member(ctx, base, member, value); } void __qmljs_llvm_inplace_ushr_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_ushr_member(*value, *base, member, ctx); + __qmljs_inplace_ushr_member(ctx, base, member, value); } String *__qmljs_llvm_identifier_from_utf8(ExecutionContext *ctx, const char *str) diff --git a/src/v4/moth/qv4instr_moth_p.h b/src/v4/moth/qv4instr_moth_p.h index 9c85228eb9..f600ae8980 100644 --- a/src/v4/moth/qv4instr_moth_p.h +++ b/src/v4/moth/qv4instr_moth_p.h @@ -417,21 +417,21 @@ union Instr }; struct instr_inplaceElementOp { MOTH_INSTR_HEADER - void (*alu)(VM::Value, VM::Value, VM::Value, VM::ExecutionContext *); + VM::InplaceBinOpElement alu; Param base; Param index; Param source; }; struct instr_inplaceMemberOp { MOTH_INSTR_HEADER - void (*alu)(VM::Value, VM::Value, VM::String *, VM::ExecutionContext *); + VM::InplaceBinOpMember alu; VM::String *member; Param base; Param source; }; struct instr_inplaceNameOp { MOTH_INSTR_HEADER - void (*alu)(VM::Value, VM::String *, VM::ExecutionContext *); + VM::InplaceBinOpName alu; VM::String *name; Param source; }; diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index d4ff709a8f..c0c282bb72 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -515,9 +515,9 @@ void InstructionSelection::binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp addInstruction(binop); } -void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) +void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Temp *rightSource, const QString &targetName) { - void (*op)(VM::Value value, VM::String *name, VM::ExecutionContext *ctx) = 0; + VM::InplaceBinOpName op = 0; switch (oper) { case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_name; break; case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_name; break; @@ -537,14 +537,14 @@ void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, c Instruction::InplaceNameOp ieo; ieo.alu = op; ieo.name = identifier(targetName); - ieo.source = getParam(sourceExpr); + ieo.source = getParam(rightSource); addInstruction(ieo); } } -void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) +void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) { - void (*op)(VM::Value base, VM::Value index, VM::Value value, VM::ExecutionContext *ctx) = 0; + VM::InplaceBinOpElement op = 0; switch (oper) { case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_element; break; case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_element; break; @@ -564,13 +564,13 @@ void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr ieo.alu = op; ieo.base = getParam(targetBaseTemp); ieo.index = getParam(targetIndexTemp); - ieo.source = getParam(sourceExpr); + ieo.source = getParam(source); addInstruction(ieo); } -void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBase, const QString &targetName) { - void (*op)(VM::Value value, VM::Value base, VM::String *name, VM::ExecutionContext *ctx) = 0; + VM::InplaceBinOpMember op = 0; switch (oper) { case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_member; break; case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_member; break; diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h index 89d7a5bcee..8a859d1e7c 100644 --- a/src/v4/moth/qv4isel_moth_p.h +++ b/src/v4/moth/qv4isel_moth_p.h @@ -75,9 +75,9 @@ protected: virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target); - virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); - virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); - virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + virtual void inplaceNameOp(IR::AluOp oper, IR::Temp *rightSource, const QString &targetName); + virtual void inplaceElementOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBase, const QString &targetName); private: struct Instruction { diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 05e30a6a30..880f2f3a8e 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -424,24 +424,22 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(LoadThis) MOTH_BEGIN_INSTR(InplaceElementOp) - instr.alu(VALUE(instr.base), - VALUE(instr.index), - VALUE(instr.source), - context); + instr.alu(context, + VALUEPTR(instr.base), + VALUEPTR(instr.index), + VALUEPTR(instr.source)); MOTH_END_INSTR(InplaceElementOp) MOTH_BEGIN_INSTR(InplaceMemberOp) - instr.alu(VALUE(instr.source), - VALUE(instr.base), + instr.alu(context, + VALUEPTR(instr.base), instr.member, - context); + VALUEPTR(instr.source)); MOTH_END_INSTR(InplaceMemberOp) MOTH_BEGIN_INSTR(InplaceNameOp) TRACE(name, "%s", instr.name->toQString().toUtf8().constData()); - instr.alu(VALUE(instr.source), - instr.name, - context); + instr.alu(context, instr.name, VALUEPTR(instr.source)); MOTH_END_INSTR(InplaceNameOp) #ifdef MOTH_THREADED_INTERPRETER diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp index 1b1a753207..1d61a1b717 100644 --- a/src/v4/qmljs_environment.cpp +++ b/src/v4/qmljs_environment.cpp @@ -503,11 +503,11 @@ Value ExecutionContext::getPropertyAndBase(String *name, Object **base) -void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) +void ExecutionContext::inplaceBitOp(String *name, const Value *value, BinOp op) { Value lhs = getProperty(name); Value result; - op(this, &result, &lhs, &value); + op(this, &result, &lhs, value); setProperty(name, result); } diff --git a/src/v4/qmljs_environment.h b/src/v4/qmljs_environment.h index f118c63af6..a3af53a043 100644 --- a/src/v4/qmljs_environment.h +++ b/src/v4/qmljs_environment.h @@ -134,7 +134,7 @@ struct ExecutionContext Value getProperty(String *name); Value getPropertyNoThrow(String *name); Value getPropertyAndBase(String *name, Object **base); - void inplaceBitOp(Value value, String *name, BinOp op); + void inplaceBitOp(String *name, const QQmlJS::VM::Value *value, BinOp op); bool deleteProperty(String *name); inline Value argument(unsigned int index = 0) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index c995e4e391..200388f50f 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -253,191 +253,191 @@ void __qmljs_in(ExecutionContext *ctx, Value *result, const Value *left, const V *result = Value::fromBoolean(r); } -void __qmljs_inplace_bit_and_name(Value value, String *name, ExecutionContext *ctx) +void __qmljs_inplace_bit_and_name(ExecutionContext *ctx, String *name, const Value *value) { - ctx->inplaceBitOp(value, name, __qmljs_bit_and); + ctx->inplaceBitOp(name, value, __qmljs_bit_and); } -void __qmljs_inplace_bit_or_name(Value value, String *name, ExecutionContext *ctx) +void __qmljs_inplace_bit_or_name(ExecutionContext *ctx, String *name, const Value *value) { - ctx->inplaceBitOp(value, name, __qmljs_bit_or); + ctx->inplaceBitOp(name, value, __qmljs_bit_or); } -void __qmljs_inplace_bit_xor_name(Value value, String *name, ExecutionContext *ctx) +void __qmljs_inplace_bit_xor_name(ExecutionContext *ctx, String *name, const Value *value) { - ctx->inplaceBitOp(value, name, __qmljs_bit_xor); + ctx->inplaceBitOp(name, value, __qmljs_bit_xor); } -void __qmljs_inplace_add_name(Value value, String *name, ExecutionContext *ctx) +void __qmljs_inplace_add_name(ExecutionContext *ctx, String *name, const Value *value) { - ctx->inplaceBitOp(value, name, __qmljs_add); + ctx->inplaceBitOp(name, value, __qmljs_add); } -void __qmljs_inplace_sub_name(Value value, String *name, ExecutionContext *ctx) +void __qmljs_inplace_sub_name(ExecutionContext *ctx, String *name, const Value *value) { - ctx->inplaceBitOp(value, name, __qmljs_sub); + ctx->inplaceBitOp(name, value, __qmljs_sub); } -void __qmljs_inplace_mul_name(Value value, String *name, ExecutionContext *ctx) +void __qmljs_inplace_mul_name(ExecutionContext *ctx, String *name, const Value *value) { - ctx->inplaceBitOp(value, name, __qmljs_mul); + ctx->inplaceBitOp(name, value, __qmljs_mul); } -void __qmljs_inplace_div_name(Value value, String *name, ExecutionContext *ctx) +void __qmljs_inplace_div_name(ExecutionContext *ctx, String *name, const Value *value) { - ctx->inplaceBitOp(value, name, __qmljs_div); + ctx->inplaceBitOp(name, value, __qmljs_div); } -void __qmljs_inplace_mod_name(Value value, String *name, ExecutionContext *ctx) +void __qmljs_inplace_mod_name(ExecutionContext *ctx, String *name, const Value *value) { - ctx->inplaceBitOp(value, name, __qmljs_mod); + ctx->inplaceBitOp(name, value, __qmljs_mod); } -void __qmljs_inplace_shl_name(Value value, String *name, ExecutionContext *ctx) +void __qmljs_inplace_shl_name(ExecutionContext *ctx, String *name, const Value *value) { - ctx->inplaceBitOp(value, name, __qmljs_shl); + ctx->inplaceBitOp(name, value, __qmljs_shl); } -void __qmljs_inplace_shr_name(Value value, String *name, ExecutionContext *ctx) +void __qmljs_inplace_shr_name(ExecutionContext *ctx, String *name, const Value *value) { - ctx->inplaceBitOp(value, name, __qmljs_shr); + ctx->inplaceBitOp(name, value, __qmljs_shr); } -void __qmljs_inplace_ushr_name(Value value, String *name, ExecutionContext *ctx) +void __qmljs_inplace_ushr_name(ExecutionContext *ctx, String *name, const Value *value) { - ctx->inplaceBitOp(value, name, __qmljs_ushr); + ctx->inplaceBitOp(name, value, __qmljs_ushr); } -void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, ExecutionContext *ctx) +void __qmljs_inplace_bit_and_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) { - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_bit_and, ctx); + Object *obj = base->toObject(ctx).objectValue(); + obj->inplaceBinOp(ctx, __qmljs_bit_and, index, rhs); } -void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, ExecutionContext *ctx) +void __qmljs_inplace_bit_or_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) { - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_bit_or, ctx); + Object *obj = base->toObject(ctx).objectValue(); + obj->inplaceBinOp(ctx, __qmljs_bit_or, index, rhs); } -void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, ExecutionContext *ctx) +void __qmljs_inplace_bit_xor_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) { - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_bit_xor, ctx); + Object *obj = base->toObject(ctx).objectValue(); + obj->inplaceBinOp(ctx, __qmljs_bit_xor, index, rhs); } -void __qmljs_inplace_add_element(Value base, Value index, Value value, ExecutionContext *ctx) +void __qmljs_inplace_add_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) { - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_add, ctx); + Object *obj = base->toObject(ctx).objectValue(); + obj->inplaceBinOp(ctx, __qmljs_add, index, rhs); } -void __qmljs_inplace_sub_element(Value base, Value index, Value value, ExecutionContext *ctx) +void __qmljs_inplace_sub_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) { - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_sub, ctx); + Object *obj = base->toObject(ctx).objectValue(); + obj->inplaceBinOp(ctx, __qmljs_sub, index, rhs); } -void __qmljs_inplace_mul_element(Value base, Value index, Value value, ExecutionContext *ctx) +void __qmljs_inplace_mul_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) { - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_mul, ctx); + Object *obj = base->toObject(ctx).objectValue(); + obj->inplaceBinOp(ctx, __qmljs_mul, index, rhs); } -void __qmljs_inplace_div_element(Value base, Value index, Value value, ExecutionContext *ctx) +void __qmljs_inplace_div_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) { - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_div, ctx); + Object *obj = base->toObject(ctx).objectValue(); + obj->inplaceBinOp(ctx, __qmljs_div, index, rhs); } -void __qmljs_inplace_mod_element(Value base, Value index, Value value, ExecutionContext *ctx) +void __qmljs_inplace_mod_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) { - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_mod, ctx); + Object *obj = base->toObject(ctx).objectValue(); + obj->inplaceBinOp(ctx, __qmljs_mod, index, rhs); } -void __qmljs_inplace_shl_element(Value base, Value index, Value value, ExecutionContext *ctx) +void __qmljs_inplace_shl_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) { - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_shl, ctx); + Object *obj = base->toObject(ctx).objectValue(); + obj->inplaceBinOp(ctx, __qmljs_shl, index, rhs); } -void __qmljs_inplace_shr_element(Value base, Value index, Value value, ExecutionContext *ctx) +void __qmljs_inplace_shr_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) { - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_shr, ctx); + Object *obj = base->toObject(ctx).objectValue(); + obj->inplaceBinOp(ctx, __qmljs_shr, index, rhs); } -void __qmljs_inplace_ushr_element(Value base, Value index, Value value, ExecutionContext *ctx) +void __qmljs_inplace_ushr_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) { - Object *obj = base.toObject(ctx).objectValue(); - obj->inplaceBinOp(value, index, __qmljs_ushr, ctx); + Object *obj = base->toObject(ctx).objectValue(); + obj->inplaceBinOp(ctx, __qmljs_ushr, index, rhs); } -void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, ExecutionContext *ctx) +void __qmljs_inplace_bit_and_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) { - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_bit_and, ctx); + Object *o = base->toObject(ctx).objectValue(); + o->inplaceBinOp(ctx, __qmljs_bit_and, name, rhs); } -void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, ExecutionContext *ctx) +void __qmljs_inplace_bit_or_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) { - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_bit_or, ctx); + Object *o = base->toObject(ctx).objectValue(); + o->inplaceBinOp(ctx, __qmljs_bit_or, name, rhs); } -void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, ExecutionContext *ctx) +void __qmljs_inplace_bit_xor_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) { - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_bit_xor, ctx); + Object *o = base->toObject(ctx).objectValue(); + o->inplaceBinOp(ctx, __qmljs_bit_xor, name, rhs); } -void __qmljs_inplace_add_member(Value value, Value base, String *name, ExecutionContext *ctx) +void __qmljs_inplace_add_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) { - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_add, ctx); + Object *o = base->toObject(ctx).objectValue(); + o->inplaceBinOp(ctx, __qmljs_add, name, rhs); } -void __qmljs_inplace_sub_member(Value value, Value base, String *name, ExecutionContext *ctx) +void __qmljs_inplace_sub_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) { - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_sub, ctx); + Object *o = base->toObject(ctx).objectValue(); + o->inplaceBinOp(ctx, __qmljs_sub, name, rhs); } -void __qmljs_inplace_mul_member(Value value, Value base, String *name, ExecutionContext *ctx) +void __qmljs_inplace_mul_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) { - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_mul, ctx); + Object *o = base->toObject(ctx).objectValue(); + o->inplaceBinOp(ctx, __qmljs_mul, name, rhs); } -void __qmljs_inplace_div_member(Value value, Value base, String *name, ExecutionContext *ctx) +void __qmljs_inplace_div_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) { - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_div, ctx); + Object *o = base->toObject(ctx).objectValue(); + o->inplaceBinOp(ctx, __qmljs_div, name, rhs); } -void __qmljs_inplace_mod_member(Value value, Value base, String *name, ExecutionContext *ctx) +void __qmljs_inplace_mod_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) { - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_mod, ctx); + Object *o = base->toObject(ctx).objectValue(); + o->inplaceBinOp(ctx, __qmljs_mod, name, rhs); } -void __qmljs_inplace_shl_member(Value value, Value base, String *name, ExecutionContext *ctx) +void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) { - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_shl, ctx); + Object *o = base->toObject(ctx).objectValue(); + o->inplaceBinOp(ctx, __qmljs_shl, name, rhs); } -void __qmljs_inplace_shr_member(Value value, Value base, String *name, ExecutionContext *ctx) +void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) { - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_shr, ctx); + Object *o = base->toObject(ctx).objectValue(); + o->inplaceBinOp(ctx, __qmljs_shr, name, rhs); } -void __qmljs_inplace_ushr_member(Value value, Value base, String *name, ExecutionContext *ctx) +void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) { - Object *o = base.toObject(ctx).objectValue(); - o->inplaceBinOp(value, name, __qmljs_ushr, ctx); + Object *o = base->toObject(ctx).objectValue(); + o->inplaceBinOp(ctx, __qmljs_ushr, name, rhs); } String *__qmljs_string_from_utf8(ExecutionContext *ctx, const char *s) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 70ac132c6f..f47c6e6848 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -245,41 +245,45 @@ void __qmljs_sne(ExecutionContext *ctx, Value *result, const Value *left, const void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_inplace_bit_and_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_bit_or_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_bit_xor_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_add_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_sub_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_mul_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_div_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_mod_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_shl_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_shr_name(Value value, String *name, ExecutionContext *ctx); -void __qmljs_inplace_ushr_name(Value value, String *name, ExecutionContext *ctx); - -void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_add_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_sub_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_mul_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_div_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_mod_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_shl_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_shr_element(Value base, Value index, Value value, ExecutionContext *ctx); -void __qmljs_inplace_ushr_element(Value base, Value index, Value value, ExecutionContext *ctx); - -void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_add_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_sub_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_mul_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_div_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_mod_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_shl_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_shr_member(Value value, Value base, String *name, ExecutionContext *ctx); -void __qmljs_inplace_ushr_member(Value value, Value base, String *name, ExecutionContext *ctx); + +typedef void (*InplaceBinOpName)(ExecutionContext *ctx, String *name, const Value *value); +void __qmljs_inplace_bit_and_name(ExecutionContext *ctx, String *name, const Value *value); +void __qmljs_inplace_bit_or_name(ExecutionContext *ctx, String *name, const Value *value); +void __qmljs_inplace_bit_xor_name(ExecutionContext *ctx, String *name, const Value *value); +void __qmljs_inplace_add_name(ExecutionContext *ctx, String *name, const Value *value); +void __qmljs_inplace_sub_name(ExecutionContext *ctx, String *name, const Value *value); +void __qmljs_inplace_mul_name(ExecutionContext *ctx, String *name, const Value *value); +void __qmljs_inplace_div_name(ExecutionContext *ctx, String *name, const Value *value); +void __qmljs_inplace_mod_name(ExecutionContext *ctx, String *name, const Value *value); +void __qmljs_inplace_shl_name(ExecutionContext *ctx, String *name, const Value *value); +void __qmljs_inplace_shr_name(ExecutionContext *ctx, String *name, const Value *value); +void __qmljs_inplace_ushr_name(ExecutionContext *ctx, String *name, const Value *value); + +typedef void (*InplaceBinOpElement)(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); +void __qmljs_inplace_bit_and_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); +void __qmljs_inplace_bit_or_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); +void __qmljs_inplace_bit_xor_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); +void __qmljs_inplace_add_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); +void __qmljs_inplace_sub_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); +void __qmljs_inplace_mul_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); +void __qmljs_inplace_div_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); +void __qmljs_inplace_mod_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); +void __qmljs_inplace_shl_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); +void __qmljs_inplace_shr_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); +void __qmljs_inplace_ushr_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); + +typedef void (*InplaceBinOpMember)(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); +void __qmljs_inplace_bit_and_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); +void __qmljs_inplace_bit_or_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); +void __qmljs_inplace_bit_xor_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); +void __qmljs_inplace_add_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); +void __qmljs_inplace_sub_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); +void __qmljs_inplace_mul_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); +void __qmljs_inplace_div_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); +void __qmljs_inplace_mod_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); +void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); +void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); +void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); Bool __qmljs_cmp_gt(Value left, Value right, ExecutionContext *ctx); Bool __qmljs_cmp_lt(Value left, Value right, ExecutionContext *ctx); diff --git a/src/v4/qv4isel_llvm.cpp b/src/v4/qv4isel_llvm.cpp index 744f50025f..26411d80c7 100644 --- a/src/v4/qv4isel_llvm.cpp +++ b/src/v4/qv4isel_llvm.cpp @@ -714,7 +714,7 @@ void InstructionSelection::binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp } } -void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) +void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Temp *rightSource, const QString &targetName) { const char *opName = 0; switch (oper) { @@ -736,14 +736,14 @@ void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, c if (opName) { llvm::Value *dst = getIdentifier(targetName); - llvm::Value *src = toValuePtr(sourceExpr); + llvm::Value *src = toValuePtr(rightSource); CreateCall3(getRuntimeFunction(opName), _llvmFunction->arg_begin(), dst, src); return; } } -void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) +void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) { const char *opName = 0; switch (oper) { @@ -766,13 +766,13 @@ void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr if (opName) { llvm::Value *base = getLLVMTemp(targetBaseTemp); llvm::Value *index = getLLVMTemp(targetIndexTemp); - llvm::Value *value = toValuePtr(sourceExpr); + llvm::Value *value = toValuePtr(source); CreateCall4(getRuntimeFunction(opName), _llvmFunction->arg_begin(), base, index, value); } } -void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBase, const QString &targetName) { const char *opName = 0; switch (oper) { diff --git a/src/v4/qv4isel_llvm_p.h b/src/v4/qv4isel_llvm_p.h index 36b1c21dbc..3f6dc47558 100644 --- a/src/v4/qv4isel_llvm_p.h +++ b/src/v4/qv4isel_llvm_p.h @@ -119,9 +119,9 @@ public: // methods from InstructionSelection: virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target); - virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); - virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); - virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + virtual void inplaceNameOp(IR::AluOp oper, IR::Temp *rightSource, const QString &targetName); + virtual void inplaceElementOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBase, const QString &targetName); public: // visitor methods for StmtVisitor: virtual void visitJump(IR::Jump *); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 7de1b08586..1378b3dd1d 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -754,9 +754,9 @@ void InstructionSelection::binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp _as->generateBinOp(oper, target, leftSource, rightSource); } -void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) +void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Temp *rightSource, const QString &targetName) { - void (*op)(const Value value, String *name, ExecutionContext *ctx) = 0; + VM::InplaceBinOpName op = 0; const char *opName = 0; switch (oper) { case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break; @@ -775,13 +775,13 @@ void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, c break; } if (op) { - _as->generateFunctionCallImp(Assembler::Void, opName, op, sourceExpr, identifier(targetName), Assembler::ContextRegister); + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, identifier(targetName), Assembler::PointerToValue(rightSource)); } } -void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) +void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) { - void (*op)(Value base, Value index, Value value, ExecutionContext *ctx) = 0; + VM::InplaceBinOpElement op = 0; const char *opName = 0; switch (oper) { case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; @@ -801,13 +801,15 @@ void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr } if (op) { - _as->generateFunctionCallImp(Assembler::Void, opName, op, targetBaseTemp, targetIndexTemp, sourceExpr, Assembler::ContextRegister); + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + Assembler::PointerToValue(targetBaseTemp), Assembler::PointerToValue(targetIndexTemp), + Assembler::PointerToValue(source)); } } -void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBase, const QString &targetName) { - void (*op)(Value value, Value base, String *name, ExecutionContext *ctx) = 0; + VM::InplaceBinOpMember op = 0; const char *opName = 0; switch (oper) { case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; @@ -828,7 +830,9 @@ void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR: if (op) { String* member = identifier(targetName); - _as->generateFunctionCallImp(Assembler::Void, opName, op, source, targetBase, member, Assembler::ContextRegister); + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + Assembler::PointerToValue(targetBase), identifier(targetName), + Assembler::PointerToValue(source)); } } diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index fb075cd477..75b5debcc7 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -718,9 +718,9 @@ protected: virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target); - virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); - virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); - virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + virtual void inplaceNameOp(IR::AluOp oper, IR::Temp *rightSource, const QString &targetName); + virtual void inplaceElementOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBase, const QString &targetName); typedef Assembler::Address Address; typedef Assembler::Pointer Pointer; diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index 8c79c32bed..eed2d0b358 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -164,19 +164,19 @@ void InstructionSelection::visitMove(IR::Move *s) return; } } else if (IR::Name *n = s->target->asName()) { - if (s->source->asTemp() || s->source->asConst()) { - inplaceNameOp(s->op, s->source, *n->id); + if (s->source->asTemp()) { + inplaceNameOp(s->op, s->source->asTemp(), *n->id); return; } } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (s->source->asTemp() || s->source->asConst()) { - inplaceElementOp(s->op, s->source, ss->base->asTemp(), + if (s->source->asTemp()) { + inplaceElementOp(s->op, s->source->asTemp(), ss->base->asTemp(), ss->index->asTemp()); return; } } else if (IR::Member *m = s->target->asMember()) { - if (s->source->asTemp() || s->source->asConst()) { - inplaceMemberOp(s->op, s->source, m->base->asTemp(), *m->name); + if (s->source->asTemp()) { + inplaceMemberOp(s->op, s->source->asTemp(), m->base->asTemp(), *m->name); return; } } diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h index 9e55a97aee..d41bd78fb2 100644 --- a/src/v4/qv4isel_p.h +++ b/src/v4/qv4isel_p.h @@ -135,9 +135,9 @@ public: // to implement by subclasses: virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0; virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0; virtual void binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target) = 0; - virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) = 0; - virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) = 0; - virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) = 0; + virtual void inplaceNameOp(IR::AluOp oper, IR::Temp *rightSource, const QString &targetName) = 0; + virtual void inplaceElementOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) = 0; + virtual void inplaceMemberOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBase, const QString &targetName) = 0; private: void callBuiltin(IR::Call *c, IR::Temp *temp); diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index e28a5fb6fc..0081aea250 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -137,29 +137,29 @@ void Object::putValue(ExecutionContext *ctx, PropertyDescriptor *pd, Value value } -void Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx) +void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, String *name, const Value *rhs) { bool hasProperty = false; Value v = __get__(ctx, name, &hasProperty); Value result; - op(ctx, &result, &v, &rhs); + op(ctx, &result, &v, rhs); __put__(ctx, name, result); } -void Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) +void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value *index, const Value *rhs) { - uint idx = index.asArrayIndex(); + uint idx = index->asArrayIndex(); if (idx < UINT_MAX) { bool hasProperty = false; Value v = __get__(ctx, idx, &hasProperty); Value result; - op(ctx, &result, &v, &rhs); + op(ctx, &result, &v, rhs); __put__(ctx, idx, result); return; } - String *name = index.toString(ctx); + String *name = index->toString(ctx); assert(name); - inplaceBinOp(rhs, name, op, ctx); + inplaceBinOp(ctx, op, name, rhs); } void Object::defineDefaultProperty(String *name, Value value) diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index 6991a39374..9bb43cd51c 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -150,8 +150,8 @@ struct Q_V4_EXPORT Object: Managed { void putValue(ExecutionContext *ctx, PropertyDescriptor *pd, Value value); - void inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx); - void inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); + void inplaceBinOp(ExecutionContext *ctx, BinOp op, String *name, const Value *rhs); + void inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value *index, const Value *rhs); /* The spec default: Writable: true, Enumerable: false, Configurable: true */ void defineDefaultProperty(String *name, Value value); -- cgit v1.2.3 From eec7ea703caf37db4ae1801f5ea016fd3cf8b221 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 13 Feb 2013 22:13:53 +0100 Subject: Convert post inc/dev operators to new calling convention Change-Id: Idbfa6852d308337076a1aa18cdeb43460fb5bed6 Reviewed-by: Simon Hausmann --- src/v4/moth/qv4vme_moth.cpp | 16 ++-- src/v4/qmljs_runtime.cpp | 100 +++++++++++++------------ src/v4/qmljs_runtime.h | 179 ++++++++++++++++++++++---------------------- src/v4/qv4codegen.cpp | 4 +- src/v4/qv4isel_masm.cpp | 32 +++++--- 5 files changed, 172 insertions(+), 159 deletions(-) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 880f2f3a8e..0dbaee65aa 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -328,35 +328,35 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinTypeofValue) MOTH_BEGIN_INSTR(CallBuiltinPostIncMember) - VALUE(instr.result) = __qmljs_builtin_post_increment_member(VALUE(instr.base), instr.member, context); + __qmljs_builtin_post_increment_member(context, VALUEPTR(instr.result), VALUEPTR(instr.base), instr.member); MOTH_END_INSTR(CallBuiltinTypeofMember) MOTH_BEGIN_INSTR(CallBuiltinPostIncSubscript) - VALUE(instr.result) = __qmljs_builtin_post_increment_element(VALUE(instr.base), VALUE(instr.index), context); + __qmljs_builtin_post_increment_element(context, VALUEPTR(instr.result), VALUEPTR(instr.base), VALUEPTR(instr.index)); MOTH_END_INSTR(CallBuiltinTypeofSubscript) MOTH_BEGIN_INSTR(CallBuiltinPostIncName) - VALUE(instr.result) = __qmljs_builtin_post_increment_name(instr.name, context); + __qmljs_builtin_post_increment_name(context, VALUEPTR(instr.result), instr.name); MOTH_END_INSTR(CallBuiltinTypeofName) MOTH_BEGIN_INSTR(CallBuiltinPostIncValue) - VALUE(instr.result) = __qmljs_builtin_post_increment(VALUEPTR(instr.value), context); + __qmljs_builtin_post_increment(context, VALUEPTR(instr.result), VALUEPTR(instr.value)); MOTH_END_INSTR(CallBuiltinTypeofValue) MOTH_BEGIN_INSTR(CallBuiltinPostDecMember) - VALUE(instr.result) = __qmljs_builtin_post_decrement_member(VALUE(instr.base), instr.member, context); + __qmljs_builtin_post_decrement_member(context, VALUEPTR(instr.result), VALUEPTR(instr.base), instr.member); MOTH_END_INSTR(CallBuiltinTypeofMember) MOTH_BEGIN_INSTR(CallBuiltinPostDecSubscript) - VALUE(instr.result) = __qmljs_builtin_post_decrement_element(VALUE(instr.base), VALUE(instr.index), context); + __qmljs_builtin_post_decrement_element(context, VALUEPTR(instr.result), VALUEPTR(instr.base), VALUEPTR(instr.index)); MOTH_END_INSTR(CallBuiltinTypeofSubscript) MOTH_BEGIN_INSTR(CallBuiltinPostDecName) - VALUE(instr.result) = __qmljs_builtin_post_decrement_name(instr.name, context); + __qmljs_builtin_post_decrement_name(context, VALUEPTR(instr.result), instr.name); MOTH_END_INSTR(CallBuiltinTypeofName) MOTH_BEGIN_INSTR(CallBuiltinPostDecValue) - VALUE(instr.result) = __qmljs_builtin_post_decrement(VALUEPTR(instr.value), context); + __qmljs_builtin_post_decrement(context, VALUEPTR(instr.result), VALUEPTR(instr.value)); MOTH_END_INSTR(CallBuiltinTypeofValue) MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 200388f50f..b00d340af7 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -1041,160 +1041,164 @@ Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext * return __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context); } -Value __qmljs_builtin_post_increment(Value *val, ExecutionContext *ctx) +void __qmljs_builtin_post_increment(ExecutionContext *ctx, Value *result, Value *val) { if (val->isInteger() && val->integerValue() < INT_MAX) { - Value retval = *val; + if (result) + *result = *val; val->int_32 += 1; - return retval; + return; } double d = __qmljs_to_number(*val, ctx); *val = Value::fromDouble(d + 1); - return Value::fromDouble(d); + if (result) + *result = Value::fromDouble(d); } -Value __qmljs_builtin_post_increment_name(String *name, ExecutionContext *context) +void __qmljs_builtin_post_increment_name(ExecutionContext *context, Value *result, String *name) { Value v = context->getProperty(name); - Value retval; if (v.isInteger() && v.integerValue() < INT_MAX) { - retval = v; + if (result) + *result = v; v.int_32 += 1; } else { double d = __qmljs_to_number(v, context); - retval = Value::fromDouble(d); + if (result) + *result = Value::fromDouble(d); v = Value::fromDouble(d + 1); } context->setProperty(name, v); - return retval; } -Value __qmljs_builtin_post_increment_member(Value base, String *name, ExecutionContext *context) +void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *result, const Value *base, String *name) { - Object *o = __qmljs_to_object(base, context).objectValue(); + Object *o = __qmljs_to_object(*base, context).objectValue(); Value v = o->__get__(context, name); - Value retval; if (v.isInteger() && v.integerValue() < INT_MAX) { - retval = v; + if (result) + *result = v; v.int_32 += 1; } else { double d = __qmljs_to_number(v, context); - retval = Value::fromDouble(d); + if (result) + *result = Value::fromDouble(d); v = Value::fromDouble(d + 1); } o->__put__(context, name, v); - return retval; } -Value __qmljs_builtin_post_increment_element(Value base, Value index, ExecutionContext *context) +void __qmljs_builtin_post_increment_element(ExecutionContext *context, Value *result, const Value *base, const Value *index) { - Object *o = __qmljs_to_object(base, context).objectValue(); + Object *o = __qmljs_to_object(*base, context).objectValue(); - uint idx = index.asArrayIndex(); + uint idx = index->asArrayIndex(); if (idx == UINT_MAX) { - String *s = index.toString(context); - return __qmljs_builtin_post_increment_member(base, s, context); + String *s = index->toString(context); + return __qmljs_builtin_post_increment_member(context, result, base, s); } Value v = o->__get__(context, idx); - Value retval; if (v.isInteger() && v.integerValue() < INT_MAX) { - retval = v; + if (result) + *result = v; v.int_32 += 1; } else { double d = __qmljs_to_number(v, context); - retval = Value::fromDouble(d); + if (result) + *result = Value::fromDouble(d); v = Value::fromDouble(d + 1); } o->__put__(context, idx, v); - return retval; } -Value __qmljs_builtin_post_decrement(Value *val, ExecutionContext *ctx) +void __qmljs_builtin_post_decrement(ExecutionContext *ctx, Value *result, Value *val) { if (val->isInteger() && val->integerValue() > INT_MIN) { - Value retval = *val; + if (result) + *result = *val; val->int_32 -= 1; - return retval; + return; } double d = __qmljs_to_number(*val, ctx); *val = Value::fromDouble(d - 1); - return Value::fromDouble(d); + if (result) + *result = Value::fromDouble(d); } -Value __qmljs_builtin_post_decrement_name(String *name, ExecutionContext *context) +void __qmljs_builtin_post_decrement_name(ExecutionContext *context, Value *result, String *name) { Value v = context->getProperty(name); - Value retval; if (v.isInteger() && v.integerValue() > INT_MIN) { - retval = v; + if (result) + *result = v; v.int_32 -= 1; } else { double d = __qmljs_to_number(v, context); - retval = Value::fromDouble(d); + if (result) + *result = Value::fromDouble(d); v = Value::fromDouble(d - 1); } context->setProperty(name, v); - return retval; } -Value __qmljs_builtin_post_decrement_member(Value base, String *name, ExecutionContext *context) +void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value *base, String *name) { - Object *o = __qmljs_to_object(base, context).objectValue(); + Object *o = __qmljs_to_object(*base, context).objectValue(); Value v = o->__get__(context, name); - Value retval; if (v.isInteger() && v.integerValue() > INT_MIN) { - retval = v; + if (result) + *result = v; v.int_32 -= 1; } else { double d = __qmljs_to_number(v, context); - retval = Value::fromDouble(d); + if (result) + *result = Value::fromDouble(d); v = Value::fromDouble(d - 1); } o->__put__(context, name, v); - return retval; } -Value __qmljs_builtin_post_decrement_element(Value base, Value index, ExecutionContext *context) +void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value *base, const Value *index) { - Object *o = __qmljs_to_object(base, context).objectValue(); + Object *o = __qmljs_to_object(*base, context).objectValue(); - uint idx = index.asArrayIndex(); + uint idx = index->asArrayIndex(); if (idx == UINT_MAX) { - String *s = index.toString(context); - return __qmljs_builtin_post_decrement_member(base, s, context); + String *s = index->toString(context); + return __qmljs_builtin_post_decrement_member(context, result, base, s); } Value v = o->__get__(context, idx); - Value retval; if (v.isInteger() && v.integerValue() > INT_MIN) { - retval = v; + if (result) + *result = v; v.int_32 -= 1; } else { double d = __qmljs_to_number(v, context); - retval = Value::fromDouble(d); + if (result) + *result = Value::fromDouble(d); v = Value::fromDouble(d - 1); } o->__put__(context, idx, v); - return retval; } void __qmljs_builtin_throw(Value val, ExecutionContext *context) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index f47c6e6848..7dcba81467 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -106,15 +106,15 @@ Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context); Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext *context); Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context); -Value __qmljs_builtin_post_increment(Value *val, ExecutionContext *ctx); -Value __qmljs_builtin_post_increment_name(String *name, ExecutionContext *context); -Value __qmljs_builtin_post_increment_member(Value base, String *name, ExecutionContext *context); -Value __qmljs_builtin_post_increment_element(Value base, Value index, ExecutionContext *context); +void __qmljs_builtin_post_increment(ExecutionContext *ctx, Value *result, Value *val); +void __qmljs_builtin_post_increment_name(ExecutionContext *context, Value *result, String *name); +void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *result, const Value *base, String *name); +void __qmljs_builtin_post_increment_element(ExecutionContext *context, Value *result, const Value *base, const Value *index); -Value __qmljs_builtin_post_decrement(Value *val, ExecutionContext *ctx); -Value __qmljs_builtin_post_decrement_name(String *name, ExecutionContext *context); -Value __qmljs_builtin_post_decrement_member(Value base, String *name, ExecutionContext *context); -Value __qmljs_builtin_post_decrement_element(Value base, Value index, ExecutionContext *context); +void __qmljs_builtin_post_decrement(ExecutionContext *ctx, Value *result, Value *val); +void __qmljs_builtin_post_decrement_name(ExecutionContext *context, Value *result, String *name); +void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value *base, String *name); +void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value *base, const Value *index); void __qmljs_builtin_throw(Value val, ExecutionContext *context); void __qmljs_builtin_rethrow(ExecutionContext *context); @@ -285,16 +285,17 @@ void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value *base, String void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); -Bool __qmljs_cmp_gt(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_lt(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_ge(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_le(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_eq(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_ne(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_se(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_sne(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_instanceof(Value left, Value right, ExecutionContext *ctx); -Bool __qmljs_cmp_in(Value left, Value right, ExecutionContext *ctx); +typedef Bool (*CmpOp)(ExecutionContext *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_gt(ExecutionContext *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_lt(ExecutionContext *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_ge(ExecutionContext *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_le(ExecutionContext *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_eq(ExecutionContext *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_ne(ExecutionContext *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_se(ExecutionContext *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_sne(ExecutionContext *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_instanceof(ExecutionContext *ctx, const Value *left, const Value *right); +Bool __qmljs_cmp_in(ExecutionContext *ctx, const Value *left, const Value *right); // type conversion and testing inline Value __qmljs_to_primitive(Value value, ExecutionContext *ctx, int typeHint) @@ -690,42 +691,42 @@ inline void __qmljs_gt(ExecutionContext *ctx, Value *result, const Value *left, { TRACE2(left, right); - *result = Value::fromBoolean(__qmljs_cmp_gt(*left, *right, ctx)); + *result = Value::fromBoolean(__qmljs_cmp_gt(ctx, left, right)); } inline void __qmljs_lt(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - *result = Value::fromBoolean(__qmljs_cmp_lt(*left, *right, ctx)); + *result = Value::fromBoolean(__qmljs_cmp_lt(ctx, left, right)); } inline void __qmljs_ge(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - *result = Value::fromBoolean(__qmljs_cmp_ge(*left, *right, ctx)); + *result = Value::fromBoolean(__qmljs_cmp_ge(ctx, left, right)); } inline void __qmljs_le(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - *result = Value::fromBoolean(__qmljs_cmp_le(*left, *right, ctx)); + *result = Value::fromBoolean(__qmljs_cmp_le(ctx, left, right)); } inline void __qmljs_eq(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - *result = Value::fromBoolean(__qmljs_cmp_eq(*left, *right, ctx)); + *result = Value::fromBoolean(__qmljs_cmp_eq(ctx, left, right)); } inline void __qmljs_ne(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { TRACE2(left, right); - *result = Value::fromBoolean(!__qmljs_cmp_eq(*left, *right, ctx)); + *result = Value::fromBoolean(!__qmljs_cmp_eq(ctx, left, right)); } inline void __qmljs_se(ExecutionContext *, Value *result, const Value *left, const Value *right) @@ -744,137 +745,137 @@ inline void __qmljs_sne(ExecutionContext *, Value *result, const Value *left, co *result = Value::fromBoolean(r); } -inline Bool __qmljs_cmp_gt(Value left, Value right, ExecutionContext *ctx) +inline Bool __qmljs_cmp_gt(ExecutionContext *ctx, const Value *left, const Value *right) { TRACE2(left, right); - left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); - right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); + Value l = __qmljs_to_primitive(*left, ctx, NUMBER_HINT); + Value r = __qmljs_to_primitive(*right, ctx, NUMBER_HINT); - if (Value::integerCompatible(left, right)) - return left.integerValue() > right.integerValue(); - if (Value::bothDouble(left, right)) { - return left.doubleValue() > right.doubleValue(); - } else if (left.isString() && right.isString()) { - return __qmljs_string_compare(ctx, right.stringValue(), left.stringValue()); + if (Value::integerCompatible(l, r)) + return l.integerValue() > r.integerValue(); + if (Value::bothDouble(l, r)) { + return l.doubleValue() > r.doubleValue(); + } else if (l.isString() && r.isString()) { + return __qmljs_string_compare(ctx, r.stringValue(), l.stringValue()); } else { - double l = __qmljs_to_number(left, ctx); - double r = __qmljs_to_number(right, ctx); - return l > r; + double dl = __qmljs_to_number(l, ctx); + double dr = __qmljs_to_number(r, ctx); + return dl > dr; } } -inline Bool __qmljs_cmp_lt(Value left, Value right, ExecutionContext *ctx) +inline Bool __qmljs_cmp_lt(ExecutionContext *ctx, const Value *left, const Value *right) { TRACE2(left, right); - left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); - right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); + Value l = __qmljs_to_primitive(*left, ctx, NUMBER_HINT); + Value r = __qmljs_to_primitive(*right, ctx, NUMBER_HINT); - if (Value::integerCompatible(left, right)) - return left.integerValue() < right.integerValue(); - if (Value::bothDouble(left, right)) { - return left.doubleValue() < right.doubleValue(); - } else if (left.isString() && right.isString()) { - return __qmljs_string_compare(ctx, left.stringValue(), right.stringValue()); + if (Value::integerCompatible(l, r)) + return l.integerValue() < r.integerValue(); + if (Value::bothDouble(l, r)) { + return l.doubleValue() < r.doubleValue(); + } else if (l.isString() && r.isString()) { + return __qmljs_string_compare(ctx, l.stringValue(), r.stringValue()); } else { - double l = __qmljs_to_number(left, ctx); - double r = __qmljs_to_number(right, ctx); - return l < r; + double dl = __qmljs_to_number(l, ctx); + double dr = __qmljs_to_number(r, ctx); + return dl < dr; } } -inline Bool __qmljs_cmp_ge(Value left, Value right, ExecutionContext *ctx) +inline Bool __qmljs_cmp_ge(ExecutionContext *ctx, const Value *left, const Value *right) { TRACE2(left, right); - left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); - right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); + Value l = __qmljs_to_primitive(*left, ctx, NUMBER_HINT); + Value r = __qmljs_to_primitive(*right, ctx, NUMBER_HINT); - if (Value::integerCompatible(left, right)) - return left.integerValue() >= right.integerValue(); - if (Value::bothDouble(left, right)) { - return left.doubleValue() >= right.doubleValue(); - } else if (left.isString() && right.isString()) { - return !__qmljs_string_compare(ctx, left.stringValue(), right.stringValue()); + if (Value::integerCompatible(l, r)) + return l.integerValue() >= r.integerValue(); + if (Value::bothDouble(l, r)) { + return l.doubleValue() >= r.doubleValue(); + } else if (l.isString() && r.isString()) { + return !__qmljs_string_compare(ctx, l.stringValue(), r.stringValue()); } else { - double l = __qmljs_to_number(left, ctx); - double r = __qmljs_to_number(right, ctx); - return l >= r; + double dl = __qmljs_to_number(l, ctx); + double dr = __qmljs_to_number(r, ctx); + return dl >= dr; } } -inline Bool __qmljs_cmp_le(Value left, Value right, ExecutionContext *ctx) +inline Bool __qmljs_cmp_le(ExecutionContext *ctx, const Value *left, const Value *right) { TRACE2(left, right); - left = __qmljs_to_primitive(left, ctx, NUMBER_HINT); - right = __qmljs_to_primitive(right, ctx, NUMBER_HINT); + Value l = __qmljs_to_primitive(*left, ctx, NUMBER_HINT); + Value r = __qmljs_to_primitive(*right, ctx, NUMBER_HINT); - if (Value::integerCompatible(left, right)) - return left.integerValue() <= right.integerValue(); - if (Value::bothDouble(left, right)) { - return left.doubleValue() <= right.doubleValue(); - } else if (left.isString() && right.isString()) { - return !__qmljs_string_compare(ctx, right.stringValue(), left.stringValue()); + if (Value::integerCompatible(l, r)) + return l.integerValue() <= r.integerValue(); + if (Value::bothDouble(l, r)) { + return l.doubleValue() <= r.doubleValue(); + } else if (l.isString() && r.isString()) { + return !__qmljs_string_compare(ctx, r.stringValue(), l.stringValue()); } else { - double l = __qmljs_to_number(left, ctx); - double r = __qmljs_to_number(right, ctx); - return l <= r; + double dl = __qmljs_to_number(l, ctx); + double dr = __qmljs_to_number(r, ctx); + return dl <= dr; } } -inline Bool __qmljs_cmp_eq(Value left, Value right, ExecutionContext *ctx) +inline Bool __qmljs_cmp_eq(ExecutionContext *ctx, const Value *left, const Value *right) { TRACE2(left, right); // need to test for doubles first as NaN != NaN - if (Value::bothDouble(left, right)) - return left.doubleValue() == right.doubleValue(); - if (left.val == right.val) + if (Value::bothDouble(*left, *right)) + return left->doubleValue() == right->doubleValue(); + if (left->val == right->val) return true; - if (left.isString() && right.isString()) - return __qmljs_string_equal(left.stringValue(), right.stringValue()); + if (left->isString() && right->isString()) + return __qmljs_string_equal(left->stringValue(), right->stringValue()); - return __qmljs_equal(left, right, ctx); + return __qmljs_equal(*left, *right, ctx); } -inline Bool __qmljs_cmp_ne(Value left, Value right, ExecutionContext *ctx) +inline Bool __qmljs_cmp_ne(ExecutionContext *ctx, const Value *left, const Value *right) { TRACE2(left, right); - return !__qmljs_cmp_eq(left, right, ctx); + return !__qmljs_cmp_eq(ctx, left, right); } -inline Bool __qmljs_cmp_se(Value left, Value right, ExecutionContext *) +inline Bool __qmljs_cmp_se(ExecutionContext *, const Value *left, const Value *right) { TRACE2(left, right); - return __qmljs_strict_equal(left, right); + return __qmljs_strict_equal(*left, *right); } -inline Bool __qmljs_cmp_sne(Value left, Value right, ExecutionContext *) +inline Bool __qmljs_cmp_sne(ExecutionContext *, const Value *left, const Value *right) { TRACE2(left, right); - return ! __qmljs_strict_equal(left, right); + return ! __qmljs_strict_equal(*left, *right); } -inline Bool __qmljs_cmp_instanceof(Value left, Value right, ExecutionContext *ctx) +inline Bool __qmljs_cmp_instanceof(ExecutionContext *ctx, const Value *left, const Value *right) { TRACE2(left, right); Value v; - __qmljs_instanceof(ctx, &v, &left, &right); + __qmljs_instanceof(ctx, &v, left, right); return v.booleanValue(); } -inline uint __qmljs_cmp_in(Value left, Value right, ExecutionContext *ctx) +inline uint __qmljs_cmp_in(ExecutionContext *ctx, const Value *left, const Value *right) { TRACE2(left, right); Value v; - __qmljs_in(ctx, &v, &left, &right); + __qmljs_in(ctx, &v, left, right); return v.booleanValue(); } diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index d82f1b1f8e..4ad7b69f8a 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -695,8 +695,8 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) right = _block->TEMP(t); } - assert(left->asTemp() || left->asConst()); - assert(right->asTemp() || right->asConst()); + assert(left->asTemp()); + assert(right->asTemp()); return _block->BINOP(op, left, right); } diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 1378b3dd1d..6cc798a9bf 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -520,42 +520,50 @@ void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) { - generateFunctionCall(result, __qmljs_builtin_post_increment_member, base, identifier(name), Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name)); } void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) { - generateFunctionCall(result, __qmljs_builtin_post_increment_element, base, index, Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_element, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(base), Assembler::PointerToValue(index)); } void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result) { - generateFunctionCall(result, __qmljs_builtin_post_increment_name, identifier(name), Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_name, Assembler::ContextRegister, + Assembler::PointerToValue(result), identifier(name)); } void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) { - generateFunctionCall(result, __qmljs_builtin_post_increment, Assembler::PointerToValue(value), Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(value)); } void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) { - generateFunctionCall(result, __qmljs_builtin_post_decrement_member, base, identifier(name), Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name)); } void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) { - generateFunctionCall(result, __qmljs_builtin_post_decrement_element, base, index, Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_element, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(base), Assembler::PointerToValue(index)); } void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result) { - generateFunctionCall(result, __qmljs_builtin_post_decrement_name, identifier(name), Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_name, Assembler::ContextRegister, + Assembler::PointerToValue(result), identifier(name)); } void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) { - generateFunctionCall(result, __qmljs_builtin_post_decrement, Assembler::PointerToValue(value), Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(value)); } void InstructionSelection::callBuiltinThrow(IR::Temp *arg) @@ -927,9 +935,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) _as->jumpToBlock(_block, s->iffalse); return; } else if (IR::Binop *b = s->cond->asBinop()) { - if ((b->left->asTemp() || b->left->asConst()) && - (b->right->asTemp() || b->right->asConst())) { - Bool (*op)(const Value, const Value, ExecutionContext *ctx) = 0; + if (b->left->asTemp() && b->right->asTemp()) { + VM::CmpOp op = 0; const char *opName = 0; switch (b->op) { default: Q_UNREACHABLE(); assert(!"todo"); break; @@ -945,7 +952,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) case IR::OpIn: setOp(op, opName, __qmljs_cmp_in); break; } // switch - _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, b->left, b->right, Assembler::ContextRegister); + _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, Assembler::ContextRegister, + Assembler::PointerToValue(b->left->asTemp()), Assembler::PointerToValue(b->right->asTemp())); Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); _as->addPatch(s->iftrue, target); -- cgit v1.2.3 From 93f53855c5349f96deda04d6fcf5d21f34c317f8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 13 Feb 2013 22:27:15 +0100 Subject: Replace post increment with pre increment if we don't need the result. Same for decrement operations. Change-Id: I9ef33f12f8cf009e9d441989dbc6bc6a233b8994 Reviewed-by: Simon Hausmann --- src/v4/qv4codegen.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index 4ad7b69f8a..9b9c931523 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -1567,9 +1567,13 @@ bool Codegen::visit(PostDecrementExpression *ast) throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); - IR::ExprList *args = _function->New(); - args->init(*expr); - _expr.code = call(_block->NAME(IR::Name::builtin_postdecrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); + if (_expr.accept(nx)) { + move(*expr, unop(IR::OpDecrement, *expr)); + } else { + IR::ExprList *args = _function->New(); + args->init(*expr); + _expr.code = call(_block->NAME(IR::Name::builtin_postdecrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); + } return false; } @@ -1580,9 +1584,13 @@ bool Codegen::visit(PostIncrementExpression *ast) throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); - IR::ExprList *args = _function->New(); - args->init(*expr); - _expr.code = call(_block->NAME(IR::Name::builtin_postincrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); + if (_expr.accept(nx)) { + move(*expr, unop(IR::OpIncrement, *expr)); + } else { + IR::ExprList *args = _function->New(); + args->init(*expr); + _expr.code = call(_block->NAME(IR::Name::builtin_postincrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); + } return false; } -- cgit v1.2.3 From afb743d72a6830a714f17abe58d5c07ff6bdd0f4 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 13 Feb 2013 23:00:10 +0100 Subject: Add a C style vtable to Managed and use it for markObjects We need to replace the C++ vtable with a home made one for additional flexibility. This will help us deal properly with primitive this values, and allow us to enable certain optimisations where we can change behavior at runtime (e.g. use optimised lookups as long as we don't have any accessor properties defined). Change-Id: I6a3852692bdc5c4f0bde05f6ff2b296013ba47e5 Reviewed-by: Simon Hausmann --- src/v4/qv4argumentsobject.cpp | 30 ++++++++++++++++++---------- src/v4/qv4argumentsobject.h | 3 ++- src/v4/qv4functionobject.cpp | 46 +++++++++++++++++++++++++++---------------- src/v4/qv4functionobject.h | 11 ++++++----- src/v4/qv4managed.cpp | 6 ++++++ src/v4/qv4managed.h | 15 ++++++++++---- src/v4/qv4object.cpp | 31 +++++++++++++++++++---------- src/v4/qv4object.h | 13 ++++++++---- src/v4/qv4stringobject.cpp | 13 +++++++++--- src/v4/qv4stringobject.h | 3 ++- 10 files changed, 116 insertions(+), 55 deletions(-) diff --git a/src/v4/qv4argumentsobject.cpp b/src/v4/qv4argumentsobject.cpp index 92251c717d..16ad93c6ff 100644 --- a/src/v4/qv4argumentsobject.cpp +++ b/src/v4/qv4argumentsobject.cpp @@ -43,6 +43,7 @@ namespace QQmlJS { namespace VM { + static Value throwTypeError(ExecutionContext *ctx) { ctx->throwTypeError(); @@ -52,6 +53,7 @@ static Value throwTypeError(ExecutionContext *ctx) ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount) : Object(context->engine), context(context) { + vtbl = &static_vtbl; type = Type_ArgumentsObject; defineDefaultProperty(context->engine->id_length, Value::fromInt32(actualParameterCount)); @@ -123,16 +125,6 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const return result; } -void ArgumentsObject::markObjects() -{ - for (int i = 0; i < mappedArguments.size(); ++i) { - Managed *m = mappedArguments.at(i).asManaged(); - if (m) - m->mark(); - } - Object::markObjects(); -} - Value ArgumentsGetterFunction::call(ExecutionContext *ctx, Value thisObject, Value *, int) { @@ -161,6 +153,24 @@ Value ArgumentsSetterFunction::call(ExecutionContext *ctx, Value thisObject, Val return Value::undefinedValue(); } +void ArgumentsObject::markObjects(Managed *that) +{ + ArgumentsObject *o = static_cast(that); + for (int i = 0; i < o->mappedArguments.size(); ++i) { + Managed *m = o->mappedArguments.at(i).asManaged(); + if (m) + m->mark(); + } + Object::markObjects(that); +} + + +const ManagedVTable ArgumentsObject::static_vtbl = +{ + ArgumentsObject::markObjects +}; + + } } diff --git a/src/v4/qv4argumentsobject.h b/src/v4/qv4argumentsobject.h index be618a992e..fe62bf7b63 100644 --- a/src/v4/qv4argumentsobject.h +++ b/src/v4/qv4argumentsobject.h @@ -77,7 +77,8 @@ struct ArgumentsObject: Object { bool defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); - virtual void markObjects(); + static const ManagedVTable static_vtbl; + static void markObjects(Managed *that); }; } diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 2af08ae7f1..f904aeeab2 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -94,6 +94,7 @@ FunctionObject::FunctionObject(ExecutionContext *scope) , varCount(0) , function(0) { + vtbl = &static_vtbl; prototype = scope->engine->functionPrototype; type = Type_FunctionObject; @@ -178,17 +179,21 @@ Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *a return result; } -void FunctionObject::markObjects() +void FunctionObject::markObjects(Managed *that) { - if (name) - name->mark(); + FunctionObject *o = static_cast(that); + if (o->name) + o->name->mark(); // these are marked in VM::Function: // for (uint i = 0; i < formalParameterCount; ++i) // formalParameterList[i]->mark(); // for (uint i = 0; i < varCount; ++i) // varList[i]->mark(); - scope->mark(); - Object::markObjects(); + o->scope->mark(); + if (o->function) + o->function->mark(); + + Object::markObjects(that); } Value FunctionObject::call(ExecutionContext *ctx) @@ -202,6 +207,12 @@ Value FunctionObject::construct(ExecutionContext *ctx) return call(ctx); } +const ManagedVTable FunctionObject::static_vtbl = +{ + FunctionObject::markObjects +}; + + FunctionCtor::FunctionCtor(ExecutionContext *scope) : FunctionObject(scope) @@ -398,12 +409,6 @@ Value ScriptFunction::call(ExecutionContext *ctx) return function->code(ctx, function->codeData); } -void ScriptFunction::markObjects() -{ - function->mark(); - FunctionObject::markObjects(); -} - BuiltinFunctionOld::BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) : FunctionObject(scope) @@ -440,6 +445,7 @@ BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Va , boundThis(boundThis) , boundArgs(boundArgs) { + vtbl = &static_vtbl; int len = target->__get__(scope, scope->engine->id_length).toUInt32(scope); len -= boundArgs.size(); if (len < 0) @@ -477,13 +483,19 @@ bool BoundFunction::hasInstance(ExecutionContext *ctx, const Value &value) return target->hasInstance(ctx, value); } -void BoundFunction::markObjects() +void BoundFunction::markObjects(Managed *that) { - target->mark(); - if (Managed *m = boundThis.asManaged()) + BoundFunction *o = static_cast(that); + o->target->mark(); + if (Managed *m = o->boundThis.asManaged()) m->mark(); - for (int i = 0; i < boundArgs.size(); ++i) - if (Managed *m = boundArgs.at(i).asManaged()) + for (int i = 0; i < o->boundArgs.size(); ++i) + if (Managed *m = o->boundArgs.at(i).asManaged()) m->mark(); - FunctionObject::markObjects(); + FunctionObject::markObjects(that); } + +const ManagedVTable BoundFunction::static_vtbl = +{ + BoundFunction::markObjects +}; diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index d6930b9f5b..a0eba1ad7a 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -163,11 +163,12 @@ struct Q_V4_EXPORT FunctionObject: Object { virtual struct ScriptFunction *asScriptFunction() { return 0; } - virtual void markObjects(); - protected: virtual Value call(ExecutionContext *ctx); virtual Value construct(ExecutionContext *ctx); + + static const ManagedVTable static_vtbl; + static void markObjects(Managed *that); }; struct FunctionCtor: FunctionObject @@ -214,8 +215,6 @@ struct ScriptFunction: FunctionObject { virtual Value call(ExecutionContext *ctx); virtual ScriptFunction *asScriptFunction() { return this; } - - virtual void markObjects(); }; struct BoundFunction: FunctionObject { @@ -229,7 +228,9 @@ struct BoundFunction: FunctionObject { virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); virtual Value construct(ExecutionContext *context, Value *args, int argc); virtual bool hasInstance(ExecutionContext *ctx, const Value &value); - virtual void markObjects(); + + static const ManagedVTable static_vtbl; + static void markObjects(Managed *that); }; } // namespace VM diff --git a/src/v4/qv4managed.cpp b/src/v4/qv4managed.cpp index 656531c823..411c725450 100644 --- a/src/v4/qv4managed.cpp +++ b/src/v4/qv4managed.cpp @@ -45,6 +45,12 @@ using namespace QQmlJS::VM; +const ManagedVTable Managed::static_vtbl = +{ + 0 // markObjects +}; + + Managed::~Managed() { _data = 0; diff --git a/src/v4/qv4managed.h b/src/v4/qv4managed.h index 14063de321..00428d5d62 100644 --- a/src/v4/qv4managed.h +++ b/src/v4/qv4managed.h @@ -71,6 +71,12 @@ struct ErrorObject; struct ArgumentsObject; struct JSONObject; struct ForeachIteratorObject; +struct Managed; + +struct ManagedVTable +{ + void (*markObjects)(Managed *); +}; struct Q_V4_EXPORT Managed { @@ -81,7 +87,7 @@ private: protected: Managed() - : _data(0) + : vtbl(&static_vtbl), _data(0) { inUse = 1; extensible = 1; } virtual ~Managed(); @@ -93,8 +99,8 @@ public: if (markBit) return; markBit = 1; - if (type != Type_String) - markObjects(); + if (vtbl->markObjects) + vtbl->markObjects(this); } enum Type { @@ -145,8 +151,9 @@ public: } protected: - virtual void markObjects() {} + static const ManagedVTable static_vtbl; + const ManagedVTable *vtbl; union { uint _data; struct { diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 0081aea250..b174e85e45 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -71,6 +71,7 @@ Object::Object(ExecutionEngine *engine) , memberDataAlloc(0), memberData(0) , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayData(0), sparseArray(0) { + vtbl = &static_vtbl; type = Type_Object; } @@ -210,13 +211,14 @@ void Object::defineReadonlyProperty(String *name, Value value) pd->value = value; } -void Object::markObjects() +void Object::markObjects(Managed *that) { - if (prototype) - prototype->mark(); + Object *o = static_cast(that); + if (o->prototype) + o->prototype->mark(); - for (int i = 0; i < internalClass->size; ++i) { - const PropertyDescriptor &pd = memberData[i]; + for (int i = 0; i < o->internalClass->size; ++i) { + const PropertyDescriptor &pd = o->memberData[i]; if (pd.isData()) { if (Managed *m = pd.value.asManaged()) m->mark(); @@ -227,7 +229,7 @@ void Object::markObjects() pd.set->mark(); } } - markArrayObjects(); + o->markArrayObjects(); } PropertyDescriptor *Object::insertMember(String *s) @@ -983,6 +985,10 @@ void Object::markArrayObjects() const } } +const ManagedVTable Object::static_vtbl = +{ + Object::markObjects +}; void ArrayObject::init(ExecutionContext *context) { @@ -999,10 +1005,15 @@ void ArrayObject::init(ExecutionContext *context) -void ForEachIteratorObject::markObjects() +void ForEachIteratorObject::markObjects(Managed *that) { - Object::markObjects(); - if (it.object) - it.object->mark(); + ForEachIteratorObject *o = static_cast(that); + Object::markObjects(that); + if (o->it.object) + o->it.object->mark(); } +const ManagedVTable ForEachIteratorObject::static_vtbl = +{ + ForEachIteratorObject::markObjects +}; diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index 9bb43cd51c..593e4e7609 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -117,7 +117,7 @@ struct Q_V4_EXPORT Object: Managed { SparseArray *sparseArray; Object(ExecutionEngine *engine); - virtual ~Object(); + ~Object(); PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); @@ -304,7 +304,8 @@ public: void arrayReserve(uint n); protected: - virtual void markObjects(); + static const ManagedVTable static_vtbl; + static void markObjects(Managed *that); friend struct ObjectIterator; friend struct ObjectPrototype; @@ -313,12 +314,16 @@ protected: struct ForEachIteratorObject: Object { ObjectIterator it; ForEachIteratorObject(ExecutionContext *ctx, Object *o) - : Object(ctx->engine), it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) { type = Type_ForeachIteratorObject; } + : Object(ctx->engine), it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) { + vtbl = &static_vtbl; + type = Type_ForeachIteratorObject; + } Value nextPropertyName() { return it.nextPropertyNameAsString(); } protected: - virtual void markObjects(); + static const ManagedVTable static_vtbl; + static void markObjects(Managed *that); }; struct BooleanObject: Object { diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp index 1e226b1000..eab9e0c0d7 100644 --- a/src/v4/qv4stringobject.cpp +++ b/src/v4/qv4stringobject.cpp @@ -78,6 +78,7 @@ using namespace QQmlJS::VM; StringObject::StringObject(ExecutionContext *ctx, const Value &value) : Object(ctx->engine), value(value) { + vtbl = &static_vtbl; type = Type_StringObject; tmpProperty.type = PropertyDescriptor::Data; @@ -100,12 +101,18 @@ PropertyDescriptor *StringObject::getIndex(ExecutionContext *ctx, uint index) return &tmpProperty; } -void StringObject::markObjects() +void StringObject::markObjects(Managed *that) { - value.stringValue()->mark(); - Object::markObjects(); + StringObject *o = static_cast(that); + o->value.stringValue()->mark(); + Object::markObjects(that); } +const ManagedVTable StringObject::static_vtbl = +{ + StringObject::markObjects +}; + StringCtor::StringCtor(ExecutionContext *scope) : FunctionObject(scope) diff --git a/src/v4/qv4stringobject.h b/src/v4/qv4stringobject.h index 85e11d5dcb..106ba14057 100644 --- a/src/v4/qv4stringobject.h +++ b/src/v4/qv4stringobject.h @@ -58,7 +58,8 @@ struct StringObject: Object { PropertyDescriptor *getIndex(ExecutionContext *ctx, uint index); protected: - virtual void markObjects(); + static const ManagedVTable static_vtbl; + static void markObjects(Managed *that); }; struct StringCtor: FunctionObject -- cgit v1.2.3 From 9c1e46ee675969cc4fb1355ff035f67c73dd7b56 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 09:10:30 +0100 Subject: Implement hasInstance through the new 'vtable' Change-Id: I59aea0f64e7ac955c3f1243936d77f2c12103621 Reviewed-by: Simon Hausmann --- src/v4/qmljs_runtime.cpp | 6 +++--- src/v4/qv4argumentsobject.cpp | 15 +++++++-------- src/v4/qv4functionobject.cpp | 36 ++++++++++++++++++++---------------- src/v4/qv4functionobject.h | 5 ++--- src/v4/qv4managed.cpp | 9 ++++++++- src/v4/qv4managed.h | 14 ++++++++++++++ src/v4/qv4object.cpp | 8 ++++++-- 7 files changed, 60 insertions(+), 33 deletions(-) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index b00d340af7..77f8b83dac 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -236,11 +236,11 @@ void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value *left, void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) { - FunctionObject *function = right->asFunctionObject(); - if (!function) + Object *o = right->asObject(); + if (!o) __qmljs_throw_type_error(ctx); - bool r = function->hasInstance(ctx, *left); + bool r = o->hasInstance(ctx, left); *result = Value::fromBoolean(r); } diff --git a/src/v4/qv4argumentsobject.cpp b/src/v4/qv4argumentsobject.cpp index 16ad93c6ff..f805aa6db4 100644 --- a/src/v4/qv4argumentsobject.cpp +++ b/src/v4/qv4argumentsobject.cpp @@ -50,6 +50,13 @@ static Value throwTypeError(ExecutionContext *ctx) return Value::undefinedValue(); } +const ManagedVTable ArgumentsObject::static_vtbl = +{ + ArgumentsObject::markObjects, + Managed::hasInstance, + ManagedVTable::EndOfVTable +}; + ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount) : Object(context->engine), context(context) { @@ -164,13 +171,5 @@ void ArgumentsObject::markObjects(Managed *that) Object::markObjects(that); } - -const ManagedVTable ArgumentsObject::static_vtbl = -{ - ArgumentsObject::markObjects -}; - - - } } diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index f904aeeab2..b404ecf9e3 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -103,24 +103,24 @@ FunctionObject::FunctionObject(ExecutionContext *scope) strictMode = false; } -bool FunctionObject::hasInstance(ExecutionContext *ctx, const Value &value) +bool FunctionObject::hasInstance(Managed *that, ExecutionContext *ctx, const Value *value) { - if (! value.isObject()) + FunctionObject *f = static_cast(that); + + Object *v = value->asObject(); + if (!v) return false; - Value o = __get__(ctx, ctx->engine->id_prototype); - if (! o.isObject()) { + Object *o = f->__get__(ctx, ctx->engine->id_prototype).asObject(); + if (!o) ctx->throwTypeError(); - return false; - } - Object *v = value.objectValue(); while (v) { v = v->prototype; if (! v) break; - else if (o.objectValue() == v) + else if (o == v) return true; } @@ -209,7 +209,9 @@ Value FunctionObject::construct(ExecutionContext *ctx) const ManagedVTable FunctionObject::static_vtbl = { - FunctionObject::markObjects + FunctionObject::markObjects, + FunctionObject::hasInstance, + ManagedVTable::EndOfVTable }; @@ -438,6 +440,12 @@ Value BuiltinFunction::construct(ExecutionContext *ctx) return Value::undefinedValue(); } +const ManagedVTable BoundFunction::static_vtbl = +{ + BoundFunction::markObjects, + BoundFunction::hasInstance, + ManagedVTable::EndOfVTable +}; BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) : FunctionObject(scope) @@ -478,9 +486,10 @@ Value BoundFunction::construct(ExecutionContext *context, Value *args, int argc) return target->construct(context, newArgs, boundArgs.size() + argc); } -bool BoundFunction::hasInstance(ExecutionContext *ctx, const Value &value) +bool BoundFunction::hasInstance(Managed *that, ExecutionContext *ctx, const Value *value) { - return target->hasInstance(ctx, value); + BoundFunction *f = static_cast(that); + return FunctionObject::hasInstance(f->target, ctx, value); } void BoundFunction::markObjects(Managed *that) @@ -494,8 +503,3 @@ void BoundFunction::markObjects(Managed *that) m->mark(); FunctionObject::markObjects(that); } - -const ManagedVTable BoundFunction::static_vtbl = -{ - BoundFunction::markObjects -}; diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index a0eba1ad7a..5cc0aab083 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -156,8 +156,6 @@ struct Q_V4_EXPORT FunctionObject: Object { FunctionObject(ExecutionContext *scope); - virtual bool hasInstance(ExecutionContext *ctx, const Value &value); - virtual Value construct(ExecutionContext *context, Value *args, int argc); virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); @@ -169,6 +167,7 @@ protected: static const ManagedVTable static_vtbl; static void markObjects(Managed *that); + static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value *value); }; struct FunctionCtor: FunctionObject @@ -227,10 +226,10 @@ struct BoundFunction: FunctionObject { virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); virtual Value construct(ExecutionContext *context, Value *args, int argc); - virtual bool hasInstance(ExecutionContext *ctx, const Value &value); static const ManagedVTable static_vtbl; static void markObjects(Managed *that); + static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value *value); }; } // namespace VM diff --git a/src/v4/qv4managed.cpp b/src/v4/qv4managed.cpp index 411c725450..c5935fcdc2 100644 --- a/src/v4/qv4managed.cpp +++ b/src/v4/qv4managed.cpp @@ -47,7 +47,9 @@ using namespace QQmlJS::VM; const ManagedVTable Managed::static_vtbl = { - 0 // markObjects + 0 /*markObjects*/, + Managed::hasInstance, + ManagedVTable::EndOfVTable }; @@ -144,3 +146,8 @@ QString Managed::className() const } return QString::fromLatin1(s); } + +bool Managed::hasInstance(Managed *, ExecutionContext *ctx, const Value *) +{ + ctx->throwTypeError(); +} diff --git a/src/v4/qv4managed.h b/src/v4/qv4managed.h index 00428d5d62..81c6cb50bc 100644 --- a/src/v4/qv4managed.h +++ b/src/v4/qv4managed.h @@ -72,10 +72,17 @@ struct ArgumentsObject; struct JSONObject; struct ForeachIteratorObject; struct Managed; +struct Value; + struct ManagedVTable { + enum _EndOfVTable { + EndOfVTable + }; void (*markObjects)(Managed *); + bool (*hasInstance)(Managed *, ExecutionContext *ctx, const Value *value); + _EndOfVTable endofVTable; }; struct Q_V4_EXPORT Managed @@ -150,10 +157,17 @@ public: *reinterpret_cast(this) = m; } + inline bool hasInstance(ExecutionContext *ctx, const Value *v) { + return vtbl->hasInstance(this, ctx, v); + } + + static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value *value); + protected: static const ManagedVTable static_vtbl; const ManagedVTable *vtbl; + union { uint _data; struct { diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index b174e85e45..b440cfbc5d 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -987,7 +987,9 @@ void Object::markArrayObjects() const const ManagedVTable Object::static_vtbl = { - Object::markObjects + Object::markObjects, + Managed::hasInstance, + ManagedVTable::EndOfVTable }; void ArrayObject::init(ExecutionContext *context) @@ -1015,5 +1017,7 @@ void ForEachIteratorObject::markObjects(Managed *that) const ManagedVTable ForEachIteratorObject::static_vtbl = { - ForEachIteratorObject::markObjects + ForEachIteratorObject::markObjects, + Managed::hasInstance, + ManagedVTable::EndOfVTable }; -- cgit v1.2.3 From 6a23171e04edbb8c8416d8d304fb8cfca3a4fd59 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 09:20:33 +0100 Subject: Use faster call/construct methods Change-Id: I3e3c2ab42f20d9d5f38d3f9681d05de9487d80bd Reviewed-by: Simon Hausmann --- src/v4/qv4booleanobject.cpp | 4 +-- src/v4/qv4booleanobject.h | 2 +- src/v4/qv4dateobject.cpp | 24 +++++++-------- src/v4/qv4dateobject.h | 2 +- src/v4/qv4errorobject.cpp | 72 ++++++++++++++++++++------------------------ src/v4/qv4errorobject.h | 37 +++++++++++------------ src/v4/qv4functionobject.cpp | 26 ++++++++-------- src/v4/qv4functionobject.h | 8 ++--- src/v4/qv4globalobject.cpp | 2 +- src/v4/qv4globalobject.h | 2 +- src/v4/qv4numberobject.cpp | 4 +-- src/v4/qv4numberobject.h | 2 +- src/v4/qv4objectproto.cpp | 19 +++++++----- src/v4/qv4objectproto.h | 4 +-- 14 files changed, 102 insertions(+), 106 deletions(-) diff --git a/src/v4/qv4booleanobject.cpp b/src/v4/qv4booleanobject.cpp index 748a6953dc..02fab7b658 100644 --- a/src/v4/qv4booleanobject.cpp +++ b/src/v4/qv4booleanobject.cpp @@ -48,9 +48,9 @@ BooleanCtor::BooleanCtor(ExecutionContext *scope) { } -Value BooleanCtor::construct(ExecutionContext *ctx) +Value BooleanCtor::construct(ExecutionContext *ctx, Value *args, int argc) { - const double n = ctx->argument(0).toBoolean(ctx); + bool n = argc ? args[0].toBoolean(ctx) : false; return Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n))); } diff --git a/src/v4/qv4booleanobject.h b/src/v4/qv4booleanobject.h index c73ae9b306..da9105eda4 100644 --- a/src/v4/qv4booleanobject.h +++ b/src/v4/qv4booleanobject.h @@ -54,7 +54,7 @@ struct BooleanCtor: FunctionObject { BooleanCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *args, int argc); virtual Value call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); }; diff --git a/src/v4/qv4dateobject.cpp b/src/v4/qv4dateobject.cpp index bdfc4be843..77157d2c25 100644 --- a/src/v4/qv4dateobject.cpp +++ b/src/v4/qv4dateobject.cpp @@ -664,15 +664,15 @@ DateCtor::DateCtor(ExecutionContext *scope) { } -Value DateCtor::construct(ExecutionContext *ctx) +Value DateCtor::construct(ExecutionContext *ctx, Value *args, int argc) { double t = 0; - if (ctx->argumentCount == 0) + if (argc == 0) t = currentTime(); - else if (ctx->argumentCount == 1) { - Value arg = ctx->argument(0); + else if (argc == 1) { + Value arg = args[0]; if (DateObject *d = arg.asDateObject()) arg = d->value; else @@ -684,14 +684,14 @@ Value DateCtor::construct(ExecutionContext *ctx) t = TimeClip(arg.toNumber(ctx)); } - else { // ctx->argumentCount > 1 - double year = ctx->argument(0).toNumber(ctx); - double month = ctx->argument(1).toNumber(ctx); - double day = ctx->argumentCount >= 3 ? ctx->argument(2).toNumber(ctx) : 1; - double hours = ctx->argumentCount >= 4 ? ctx->argument(3).toNumber(ctx) : 0; - double mins = ctx->argumentCount >= 5 ? ctx->argument(4).toNumber(ctx) : 0; - double secs = ctx->argumentCount >= 6 ? ctx->argument(5).toNumber(ctx) : 0; - double ms = ctx->argumentCount >= 7 ? ctx->argument(6).toNumber(ctx) : 0; + else { // argc > 1 + double year = args[0].toNumber(ctx); + double month = args[1].toNumber(ctx); + double day = argc >= 3 ? args[2].toNumber(ctx) : 1; + double hours = argc >= 4 ? args[3].toNumber(ctx) : 0; + double mins = argc >= 5 ? args[4].toNumber(ctx) : 0; + double secs = argc >= 6 ? args[5].toNumber(ctx) : 0; + double ms = argc >= 7 ? args[6].toNumber(ctx) : 0; if (year >= 0 && year <= 99) year += 1900; t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); diff --git a/src/v4/qv4dateobject.h b/src/v4/qv4dateobject.h index 15bd99f8ea..56f96aadf8 100644 --- a/src/v4/qv4dateobject.h +++ b/src/v4/qv4dateobject.h @@ -59,7 +59,7 @@ struct DateCtor: FunctionObject { DateCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *args, int argc); virtual Value call(ExecutionContext *ctx); }; diff --git a/src/v4/qv4errorobject.cpp b/src/v4/qv4errorobject.cpp index c6b0a128c0..50797b7af6 100644 --- a/src/v4/qv4errorobject.cpp +++ b/src/v4/qv4errorobject.cpp @@ -79,7 +79,7 @@ ErrorObject::ErrorObject(ExecutionEngine* engine, const Value &message) type = Type_ErrorObject; subtype = Error; - if (message.type() != Value::Undefined_Type) + if (!message.isUndefined()) defineDefaultProperty(engine->newString(QStringLiteral("message")), message); } @@ -99,72 +99,64 @@ SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *m -EvalErrorObject::EvalErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) +EvalErrorObject::EvalErrorObject(ExecutionContext *ctx, const Value &message) + : ErrorObject(ctx->engine, message) { subtype = EvalError; setNameProperty(ctx); prototype = ctx->engine->evalErrorPrototype; } -RangeErrorObject::RangeErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) +RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const Value &message) + : ErrorObject(ctx->engine, message) { subtype = RangeError; setNameProperty(ctx); prototype = ctx->engine->rangeErrorPrototype; } -RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) +RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &message) + : ErrorObject(ctx->engine, Value::fromString(ctx,message)) { subtype = RangeError; setNameProperty(ctx); prototype = ctx->engine->rangeErrorPrototype; } -ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) +ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const Value &message) + : ErrorObject(ctx->engine, message) { subtype = ReferenceError; setNameProperty(ctx); prototype = ctx->engine->referenceErrorPrototype; } -ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) +ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString &message) + : ErrorObject(ctx->engine, Value::fromString(ctx,message)) { subtype = ReferenceError; setNameProperty(ctx); prototype = ctx->engine->referenceErrorPrototype; } -TypeErrorObject::TypeErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) +TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const Value &message) + : ErrorObject(ctx->engine, message) { subtype = TypeError; setNameProperty(ctx); prototype = ctx->engine->typeErrorPrototype; } -TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &msg) - : ErrorObject(ctx->engine, Value::fromString(ctx,msg)) +TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &message) + : ErrorObject(ctx->engine, Value::fromString(ctx,message)) { subtype = TypeError; setNameProperty(ctx); prototype = ctx->engine->typeErrorPrototype; } -URIErrorObject::URIErrorObject(ExecutionContext *ctx) - : ErrorObject(ctx->engine, ctx->argument(0)) -{ - subtype = URIError; - setNameProperty(ctx); - prototype = ctx->engine->uRIErrorPrototype; -} - -URIErrorObject::URIErrorObject(ExecutionContext *ctx, Value msg) - : ErrorObject(ctx->engine, msg) +URIErrorObject::URIErrorObject(ExecutionContext *ctx, const Value &message) + : ErrorObject(ctx->engine, message) { subtype = URIError; setNameProperty(ctx); @@ -177,44 +169,44 @@ ErrorCtor::ErrorCtor(ExecutionContext *scope) { } -Value ErrorCtor::construct(ExecutionContext *ctx) +Value ErrorCtor::construct(ExecutionContext *context, Value *args, int argc) { - return Value::fromObject(ctx->engine->newErrorObject(ctx->argument(0))); + return Value::fromObject(context->engine->newErrorObject(argc ? args[0] : Value::undefinedValue())); } -Value ErrorCtor::call(ExecutionContext *ctx) +Value ErrorCtor::call(ExecutionContext *ctx, Value thisObject, Value *args, int argc) { - return construct(ctx); + return construct(ctx, args, argc); } -Value EvalErrorCtor::construct(ExecutionContext *ctx) +Value EvalErrorCtor::construct(ExecutionContext *ctx, Value *args, int argc) { - return Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx)); + return Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); } -Value RangeErrorCtor::construct(ExecutionContext *ctx) +Value RangeErrorCtor::construct(ExecutionContext *ctx, Value *args, int argc) { - return Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx)); + return Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); } -Value ReferenceErrorCtor::construct(ExecutionContext *ctx) +Value ReferenceErrorCtor::construct(ExecutionContext *ctx, Value *args, int argc) { - return Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx)); + return Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); } -Value SyntaxErrorCtor::construct(ExecutionContext *ctx) +Value SyntaxErrorCtor::construct(ExecutionContext *ctx, Value *, int) { return Value::fromObject(new (ctx->engine->memoryManager) SyntaxErrorObject(ctx, 0)); } -Value TypeErrorCtor::construct(ExecutionContext *ctx) +Value TypeErrorCtor::construct(ExecutionContext *ctx, Value *args, int argc) { - return Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx)); + return Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); } -Value URIErrorCtor::construct(ExecutionContext *ctx) +Value URIErrorCtor::construct(ExecutionContext *ctx, Value *args, int argc) { - return Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx)); + return Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); } void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) diff --git a/src/v4/qv4errorobject.h b/src/v4/qv4errorobject.h index 452b7b3b45..651951cab1 100644 --- a/src/v4/qv4errorobject.h +++ b/src/v4/qv4errorobject.h @@ -69,16 +69,16 @@ protected: }; struct EvalErrorObject: ErrorObject { - EvalErrorObject(ExecutionContext *ctx); + EvalErrorObject(ExecutionContext *ctx, const Value &message); }; struct RangeErrorObject: ErrorObject { - RangeErrorObject(ExecutionContext *ctx); + RangeErrorObject(ExecutionContext *ctx, const Value &message); RangeErrorObject(ExecutionContext *ctx, const QString &msg); }; struct ReferenceErrorObject: ErrorObject { - ReferenceErrorObject(ExecutionContext *ctx); + ReferenceErrorObject(ExecutionContext *ctx, const Value &message); ReferenceErrorObject(ExecutionContext *ctx, const QString &msg); }; @@ -94,63 +94,62 @@ private: }; struct TypeErrorObject: ErrorObject { - TypeErrorObject(ExecutionContext *ctx); + TypeErrorObject(ExecutionContext *ctx, const Value &message); TypeErrorObject(ExecutionContext *ctx, const QString &msg); }; struct URIErrorObject: ErrorObject { - URIErrorObject(ExecutionContext *ctx); - URIErrorObject(ExecutionContext *ctx, Value); + URIErrorObject(ExecutionContext *ctx, const Value &message); }; struct ErrorCtor: FunctionObject { ErrorCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *context, Value *args, int argc); + virtual Value call(ExecutionContext *ctx, Value thisObject, Value *args, int argc); }; struct EvalErrorCtor: ErrorCtor { EvalErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual Value construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *, int argc); }; struct RangeErrorCtor: ErrorCtor { RangeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual Value construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *args, int argc); }; struct ReferenceErrorCtor: ErrorCtor { ReferenceErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual Value construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *args, int argc); }; struct SyntaxErrorCtor: ErrorCtor { SyntaxErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual Value construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *, int); }; struct TypeErrorCtor: ErrorCtor { TypeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual Value construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *args, int argc); }; struct URIErrorCtor: ErrorCtor { URIErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} - virtual Value construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *args, int argc); }; @@ -166,19 +165,19 @@ struct ErrorPrototype: ErrorObject struct EvalErrorPrototype: EvalErrorObject { - EvalErrorPrototype(ExecutionContext *ctx): EvalErrorObject(ctx) {} + EvalErrorPrototype(ExecutionContext *ctx): EvalErrorObject(ctx, Value::undefinedValue()) {} void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; struct RangeErrorPrototype: RangeErrorObject { - RangeErrorPrototype(ExecutionContext *ctx): RangeErrorObject(ctx) {} + RangeErrorPrototype(ExecutionContext *ctx): RangeErrorObject(ctx, Value::undefinedValue()) {} void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; struct ReferenceErrorPrototype: ReferenceErrorObject { - ReferenceErrorPrototype(ExecutionContext *ctx): ReferenceErrorObject(ctx) {} + ReferenceErrorPrototype(ExecutionContext *ctx): ReferenceErrorObject(ctx, Value::undefinedValue()) {} void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; @@ -190,13 +189,13 @@ struct SyntaxErrorPrototype: SyntaxErrorObject struct TypeErrorPrototype: TypeErrorObject { - TypeErrorPrototype(ExecutionContext *ctx): TypeErrorObject(ctx) {} + TypeErrorPrototype(ExecutionContext *ctx): TypeErrorObject(ctx, Value::undefinedValue()) {} void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; struct URIErrorPrototype: URIErrorObject { - URIErrorPrototype(ExecutionContext *ctx): URIErrorObject(ctx) {} + URIErrorPrototype(ExecutionContext *ctx): URIErrorObject(ctx, Value::undefinedValue()) {} void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index b404ecf9e3..0f9655c606 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -222,22 +222,22 @@ FunctionCtor::FunctionCtor(ExecutionContext *scope) } // 15.3.2 -Value FunctionCtor::construct(ExecutionContext *ctx) +Value FunctionCtor::construct(ExecutionContext *ctx, Value *args, int argc) { MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); - QString args; + QString arguments; QString body; - if (ctx->argumentCount > 0) { - for (uint i = 0; i < ctx->argumentCount - 1; ++i) { + if (argc > 0) { + for (uint i = 0; i < argc - 1; ++i) { if (i) - args += QLatin1String(", "); - args += ctx->argument(i).toString(ctx)->toQString(); + arguments += QLatin1String(", "); + arguments += args[i].toString(ctx)->toQString(); } - body = ctx->argument(ctx->argumentCount - 1).toString(ctx)->toQString(); + body = args[argc - 1].toString(ctx)->toQString(); } - QString function = QLatin1String("function(") + args + QLatin1String("){") + body + QLatin1String("}"); + QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1String("}"); QQmlJS::Engine ee, *engine = ⅇ Lexer lexer(engine); @@ -256,7 +256,7 @@ Value FunctionCtor::construct(ExecutionContext *ctx) IR::Module module; - Codegen cg(ctx, ctx->strictMode); + Codegen cg(ctx, strictMode); IR::Function *irf = cg(QString(), fe, &module); QScopedPointer isel(ctx->engine->iselFactory->create(ctx->engine, &module)); @@ -266,9 +266,9 @@ Value FunctionCtor::construct(ExecutionContext *ctx) } // 15.3.1: This is equivalent to new Function(...) -Value FunctionCtor::call(ExecutionContext *ctx) +Value FunctionCtor::call(ExecutionContext *context, Value thisObject, Value *args, int argc) { - return construct(ctx); + return construct(context, args, argc); } void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) @@ -420,7 +420,7 @@ BuiltinFunctionOld::BuiltinFunctionOld(ExecutionContext *scope, String *name, Va isBuiltinFunction = true; } -Value BuiltinFunctionOld::construct(ExecutionContext *ctx) +Value BuiltinFunctionOld::construct(ExecutionContext *ctx, Value *, int) { ctx->throwTypeError(); return Value::undefinedValue(); @@ -434,7 +434,7 @@ BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (* isBuiltinFunction = true; } -Value BuiltinFunction::construct(ExecutionContext *ctx) +Value BuiltinFunction::construct(ExecutionContext *ctx, Value *, int) { ctx->throwTypeError(); return Value::undefinedValue(); diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index 5cc0aab083..c5f74de29e 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -174,8 +174,8 @@ struct FunctionCtor: FunctionObject { FunctionCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *args, int argc); + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); }; struct FunctionPrototype: FunctionObject @@ -194,7 +194,7 @@ struct BuiltinFunctionOld: FunctionObject { BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); virtual Value call(ExecutionContext *ctx) { return code(ctx); } - virtual Value construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *, int); }; struct BuiltinFunction: FunctionObject { @@ -204,7 +204,7 @@ struct BuiltinFunction: FunctionObject { virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc) { return code(context, thisObject, args, argc); } - virtual Value construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *, int); }; struct ScriptFunction: FunctionObject { diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp index 8a0f779c51..384bce5120 100644 --- a/src/v4/qv4globalobject.cpp +++ b/src/v4/qv4globalobject.cpp @@ -408,7 +408,7 @@ Value EvalFunction::call(ExecutionContext *context, Value thisObject, Value *arg return evalCall(context, thisObject, args, argc, false); } -Value EvalFunction::construct(ExecutionContext *ctx) +Value EvalFunction::construct(ExecutionContext *ctx, Value *, int) { ctx->throwTypeError(); return Value::undefinedValue(); diff --git a/src/v4/qv4globalobject.h b/src/v4/qv4globalobject.h index 8399c94fe0..e15b7b21dc 100644 --- a/src/v4/qv4globalobject.h +++ b/src/v4/qv4globalobject.h @@ -62,7 +62,7 @@ struct Q_V4_EXPORT EvalFunction : FunctionObject virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); Value evalCall(ExecutionContext *context, Value thisObject, Value *args, int argc, bool directCall); - Value construct(ExecutionContext *ctx); + Value construct(ExecutionContext *ctx, Value *, int); }; struct GlobalFunctions diff --git a/src/v4/qv4numberobject.cpp b/src/v4/qv4numberobject.cpp index 57cfab9d61..0601da63fb 100644 --- a/src/v4/qv4numberobject.cpp +++ b/src/v4/qv4numberobject.cpp @@ -54,9 +54,9 @@ NumberCtor::NumberCtor(ExecutionContext *scope) { } -Value NumberCtor::construct(ExecutionContext *ctx) +Value NumberCtor::construct(ExecutionContext *ctx, Value *args, int argc) { - double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0; + double d = argc ? args[0].toNumber(ctx) : 0.; return Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(d))); } diff --git a/src/v4/qv4numberobject.h b/src/v4/qv4numberobject.h index b24e538eb3..e9125ab752 100644 --- a/src/v4/qv4numberobject.h +++ b/src/v4/qv4numberobject.h @@ -54,7 +54,7 @@ struct NumberCtor: FunctionObject { NumberCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *args, int argc); virtual Value call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); }; diff --git a/src/v4/qv4objectproto.cpp b/src/v4/qv4objectproto.cpp index 09ffb6a202..de8b7ff8b0 100644 --- a/src/v4/qv4objectproto.cpp +++ b/src/v4/qv4objectproto.cpp @@ -79,18 +79,23 @@ ObjectCtor::ObjectCtor(ExecutionContext *scope) { } -Value ObjectCtor::construct(ExecutionContext *ctx) +Value ObjectCtor::construct(ExecutionContext *ctx, Value *args, int argc) { - if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull()) - return ctx->thisObject; - return __qmljs_to_object(ctx->argument(0), ctx); + if (!argc || args[0].isUndefined() || args[0].isNull()) { + Object *obj = ctx->engine->newObject(); + Value proto = __get__(ctx, ctx->engine->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + return Value::fromObject(obj); + } + return __qmljs_to_object(args[0], ctx); } -Value ObjectCtor::call(ExecutionContext *ctx) +Value ObjectCtor::call(ExecutionContext *ctx, Value /*thisObject*/, Value *args, int argc) { - if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull()) + if (!argc || args[0].isUndefined() || args[0].isNull()) return Value::fromObject(ctx->engine->newObject()); - return __qmljs_to_object(ctx->argument(0), ctx); + return __qmljs_to_object(args[0], ctx); } void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) diff --git a/src/v4/qv4objectproto.h b/src/v4/qv4objectproto.h index 679c271127..6341128059 100644 --- a/src/v4/qv4objectproto.h +++ b/src/v4/qv4objectproto.h @@ -54,8 +54,8 @@ struct ObjectCtor: FunctionObject { ObjectCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *args, int argc); + virtual Value call(ExecutionContext *ctx, Value, Value *args, int argc); }; struct ObjectPrototype: Object -- cgit v1.2.3 From 2b59eda8304c6c40c9c286abe6070a88f24a3905 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 10:46:30 +0100 Subject: Remove the virtual contruct(ctx) method The more general and faster construct method taking more arguments is now used in all cases. Change-Id: I183e4279526e5a937938a72d494a537faf4bc825 Reviewed-by: Simon Hausmann --- src/v4/qv4errorobject.cpp | 4 ++-- src/v4/qv4errorobject.h | 2 +- src/v4/qv4functionobject.cpp | 7 +------ src/v4/qv4functionobject.h | 1 - src/v4/qv4regexpobject.cpp | 16 ++++++++-------- src/v4/qv4regexpobject.h | 4 ++-- src/v4/qv4stringobject.cpp | 6 +++--- src/v4/qv4stringobject.h | 2 +- 8 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/v4/qv4errorobject.cpp b/src/v4/qv4errorobject.cpp index 50797b7af6..8ad7d5ec9c 100644 --- a/src/v4/qv4errorobject.cpp +++ b/src/v4/qv4errorobject.cpp @@ -169,9 +169,9 @@ ErrorCtor::ErrorCtor(ExecutionContext *scope) { } -Value ErrorCtor::construct(ExecutionContext *context, Value *args, int argc) +Value ErrorCtor::construct(ExecutionContext *ctx, Value *args, int argc) { - return Value::fromObject(context->engine->newErrorObject(argc ? args[0] : Value::undefinedValue())); + return Value::fromObject(ctx->engine->newErrorObject(argc ? args[0] : Value::undefinedValue())); } Value ErrorCtor::call(ExecutionContext *ctx, Value thisObject, Value *args, int argc) diff --git a/src/v4/qv4errorobject.h b/src/v4/qv4errorobject.h index 651951cab1..734ee3f8fd 100644 --- a/src/v4/qv4errorobject.h +++ b/src/v4/qv4errorobject.h @@ -106,7 +106,7 @@ struct ErrorCtor: FunctionObject { ErrorCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *context, Value *args, int argc); + virtual Value construct(ExecutionContext *ctx, Value *args, int argc); virtual Value call(ExecutionContext *ctx, Value thisObject, Value *args, int argc); }; diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 0f9655c606..69302dd235 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -143,7 +143,7 @@ Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc ctx->argumentCount = argc; ctx->initCallContext(context); - Value result = construct(ctx); + Value result = call(ctx); ctx->leaveCallContext(); if (result.isObject()) @@ -202,11 +202,6 @@ Value FunctionObject::call(ExecutionContext *ctx) return Value::undefinedValue(); } -Value FunctionObject::construct(ExecutionContext *ctx) -{ - return call(ctx); -} - const ManagedVTable FunctionObject::static_vtbl = { FunctionObject::markObjects, diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index c5f74de29e..dff95d2431 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -163,7 +163,6 @@ struct Q_V4_EXPORT FunctionObject: Object { protected: virtual Value call(ExecutionContext *ctx); - virtual Value construct(ExecutionContext *ctx); static const ManagedVTable static_vtbl; static void markObjects(Managed *that); diff --git a/src/v4/qv4regexpobject.cpp b/src/v4/qv4regexpobject.cpp index 2762c98a62..19aee55ca5 100644 --- a/src/v4/qv4regexpobject.cpp +++ b/src/v4/qv4regexpobject.cpp @@ -98,10 +98,10 @@ RegExpCtor::RegExpCtor(ExecutionContext *scope) { } -Value RegExpCtor::construct(ExecutionContext *ctx) +Value RegExpCtor::construct(ExecutionContext *ctx, Value *argv, int argc) { - Value r = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); - Value f = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); + Value r = argc > 0 ? argv[0] : Value::undefinedValue(); + Value f = argc > 1 ? argv[1] : Value::undefinedValue(); if (RegExpObject *re = r.asRegExpObject()) { if (!f.isUndefined()) ctx->throwTypeError(); @@ -142,14 +142,14 @@ Value RegExpCtor::construct(ExecutionContext *ctx) return Value::fromObject(o); } -Value RegExpCtor::call(ExecutionContext *ctx) +Value RegExpCtor::call(ExecutionContext *ctx, Value thisObject, Value *argv, int argc) { - if (ctx->argumentCount > 0 && ctx->argument(0).asRegExpObject()) { - if (ctx->argumentCount == 1 || ctx->argument(1).isUndefined()) - return ctx->argument(0); + if (argc > 0 && argv[0].asRegExpObject()) { + if (argc == 1 || argv[1].isUndefined()) + return argv[0]; } - return construct(ctx); + return construct(ctx, argv, argc); } void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) diff --git a/src/v4/qv4regexpobject.h b/src/v4/qv4regexpobject.h index dcf6120f8a..d310374348 100644 --- a/src/v4/qv4regexpobject.h +++ b/src/v4/qv4regexpobject.h @@ -76,8 +76,8 @@ struct RegExpCtor: FunctionObject { RegExpCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx); - virtual Value call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *argv, int argc); + virtual Value call(ExecutionContext *ctx, Value thisObject, Value *argv, int argc); }; struct RegExpPrototype: RegExpObject diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp index eab9e0c0d7..e8ec9e6b78 100644 --- a/src/v4/qv4stringobject.cpp +++ b/src/v4/qv4stringobject.cpp @@ -119,11 +119,11 @@ StringCtor::StringCtor(ExecutionContext *scope) { } -Value StringCtor::construct(ExecutionContext *ctx) +Value StringCtor::construct(ExecutionContext *ctx, Value *argv, int argc) { Value value; - if (ctx->argumentCount) - value = Value::fromString(ctx->argument(0).toString(ctx)); + if (argc) + value = Value::fromString(argv[0].toString(ctx)); else value = Value::fromString(ctx, QString()); return Value::fromObject(ctx->engine->newStringObject(ctx, value)); diff --git a/src/v4/qv4stringobject.h b/src/v4/qv4stringobject.h index 106ba14057..5c295e201e 100644 --- a/src/v4/qv4stringobject.h +++ b/src/v4/qv4stringobject.h @@ -66,7 +66,7 @@ struct StringCtor: FunctionObject { StringCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *argv, int argc); virtual Value call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); }; -- cgit v1.2.3 From 03026421af42a44cf5edca48847abcee8841df4d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 11:00:39 +0100 Subject: Move the generic construct() implementation into ScriptFunction This is the only place we really need it, as the generic construct method defined by the spec mainly applies to functions defined in script. Change-Id: I4fe4219715a4a9393900db6a2532e42fafaea2db Reviewed-by: Simon Hausmann --- src/v4/qv4arrayobject.cpp | 21 ++++++++++++--------- src/v4/qv4arrayobject.h | 3 ++- src/v4/qv4functionobject.cpp | 43 +++++++++++++++++++++++++++---------------- src/v4/qv4functionobject.h | 1 + 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp index 3f70029cb8..2999ce0517 100644 --- a/src/v4/qv4arrayobject.cpp +++ b/src/v4/qv4arrayobject.cpp @@ -49,26 +49,24 @@ ArrayCtor::ArrayCtor(ExecutionContext *scope) { } -Value ArrayCtor::call(ExecutionContext *ctx) +Value ArrayCtor::construct(ExecutionContext *ctx, Value *argv, int argc) { ArrayObject *a = ctx->engine->newArrayObject(ctx); uint len; - if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) { + if (argc == 1 && argv[0].isNumber()) { bool ok; - len = ctx->argument(0).asArrayLength(ctx, &ok); + len = argv[0].asArrayLength(ctx, &ok); - if (!ok) { - ctx->throwRangeError(ctx->argument(0)); - return Value::undefinedValue(); - } + if (!ok) + ctx->throwRangeError(argv[0]); if (len < 0x1000) a->arrayReserve(len); } else { - len = ctx->argumentCount; + len = argc; a->arrayReserve(len); for (unsigned int i = 0; i < len; ++i) - fillDescriptor(a->arrayData + i, ctx->argument(i)); + fillDescriptor(a->arrayData + i, argv[i]); a->arrayDataLen = len; } a->setArrayLengthUnchecked(len); @@ -76,6 +74,11 @@ Value ArrayCtor::call(ExecutionContext *ctx) return Value::fromObject(a); } +Value ArrayCtor::call(ExecutionContext *ctx, Value thisObject, Value *argv, int argc) +{ + return construct(ctx, argv, argc); +} + void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); diff --git a/src/v4/qv4arrayobject.h b/src/v4/qv4arrayobject.h index eb8bd05128..48250dde52 100644 --- a/src/v4/qv4arrayobject.h +++ b/src/v4/qv4arrayobject.h @@ -55,7 +55,8 @@ struct ArrayCtor: FunctionObject { ArrayCtor(ExecutionContext *scope); - virtual Value call(ExecutionContext *ctx); + virtual Value construct(ExecutionContext *ctx, Value *argv, int argc); + virtual Value call(ExecutionContext *ctx, Value thisObject, Value *argv, int argc); }; struct ArrayPrototype: ArrayObject diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 69302dd235..9fdd56a2ea 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -127,27 +127,12 @@ bool FunctionObject::hasInstance(Managed *that, ExecutionContext *ctx, const Val return false; } -Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc) +Value FunctionObject::construct(ExecutionContext *context, Value *, int) { Object *obj = context->engine->newObject(); Value proto = __get__(context, context->engine->id_prototype); if (proto.isObject()) obj->prototype = proto.objectValue(); - - uint size = requiredMemoryForExecutionContect(this, argc); - ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); - - ctx->thisObject = Value::fromObject(obj); - ctx->function = this; - ctx->arguments = args; - ctx->argumentCount = argc; - - ctx->initCallContext(context); - Value result = call(ctx); - ctx->leaveCallContext(); - - if (result.isObject()) - return result; return Value::fromObject(obj); } @@ -400,6 +385,32 @@ ScriptFunction::~ScriptFunction() { } +Value ScriptFunction::construct(ExecutionContext *context, Value *args, int argc) +{ + Object *obj = context->engine->newObject(); + Value proto = __get__(context, context->engine->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + + uint size = requiredMemoryForExecutionContect(this, argc); + ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); + + ctx->thisObject = Value::fromObject(obj); + ctx->function = this; + ctx->arguments = args; + ctx->argumentCount = argc; + + ctx->initCallContext(context); + Value result = call(ctx); + ctx->leaveCallContext(); + + if (result.isObject()) + return result; + return Value::fromObject(obj); +} + + + Value ScriptFunction::call(ExecutionContext *ctx) { assert(function->code); diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index dff95d2431..c1e9b289a9 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -210,6 +210,7 @@ struct ScriptFunction: FunctionObject { ScriptFunction(ExecutionContext *scope, VM::Function *function); virtual ~ScriptFunction(); + virtual Value construct(ExecutionContext *context, Value *args, int argc); virtual Value call(ExecutionContext *ctx); virtual ScriptFunction *asScriptFunction() { return this; } -- cgit v1.2.3 From d9f1373458abeaa89e97bf9fc0d71a57ed4d62b7 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 11:02:57 +0100 Subject: Don't use the slow call() method for Date() Change-Id: Ifc29ef18a173a210a8831b87ba3f1adcbe9911d6 Reviewed-by: Simon Hausmann --- src/v4/qv4dateobject.cpp | 2 +- src/v4/qv4dateobject.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/v4/qv4dateobject.cpp b/src/v4/qv4dateobject.cpp index 77157d2c25..de614cc079 100644 --- a/src/v4/qv4dateobject.cpp +++ b/src/v4/qv4dateobject.cpp @@ -702,7 +702,7 @@ Value DateCtor::construct(ExecutionContext *ctx, Value *args, int argc) return Value::fromObject(d); } -Value DateCtor::call(ExecutionContext *ctx) +Value DateCtor::call(ExecutionContext *ctx, Value, Value *, int) { double t = currentTime(); return Value::fromString(ctx, ToString(t)); diff --git a/src/v4/qv4dateobject.h b/src/v4/qv4dateobject.h index 56f96aadf8..b4916ecde4 100644 --- a/src/v4/qv4dateobject.h +++ b/src/v4/qv4dateobject.h @@ -60,7 +60,7 @@ struct DateCtor: FunctionObject DateCtor(ExecutionContext *scope); virtual Value construct(ExecutionContext *ctx, Value *args, int argc); - virtual Value call(ExecutionContext *ctx); + virtual Value call(ExecutionContext *ctx, Value, Value *, int); }; struct DatePrototype: DateObject -- cgit v1.2.3 From 2ce17e2aa9908aa68136f3e82b6ad5a001a9f5fc Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 11:10:17 +0100 Subject: Remove the virtual call(ctx) method This is better handled in ScriptFunction and BuiltinFunctionOld. Change-Id: Id896b1ddac47a9ce52e86abff901c87b7e627271 Reviewed-by: Simon Hausmann --- src/v4/qv4functionobject.cpp | 94 +++++++++++++++++++++++++++----------------- src/v4/qv4functionobject.h | 8 ++-- 2 files changed, 61 insertions(+), 41 deletions(-) diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 9fdd56a2ea..0e8c1e4d94 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -136,32 +136,9 @@ Value FunctionObject::construct(ExecutionContext *context, Value *, int) return Value::fromObject(obj); } -Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +Value FunctionObject::call(ExecutionContext *, Value, Value *, int) { - uint size = requiredMemoryForExecutionContect(this, argc); - ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); - - if (!strictMode && !thisObject.isObject()) { - // Built-in functions allow for the this object to be null or undefined. This overrides - // the behaviour of changing thisObject to the global object if null/undefined and allows - // the built-in functions for example to throw a type error if null is passed. - if (thisObject.isUndefined() || thisObject.isNull()) { - if (!isBuiltinFunction) - thisObject = context->engine->globalObject; - } else { - thisObject = thisObject.toObject(context); - } - } - - ctx->thisObject = thisObject; - ctx->function = this; - ctx->arguments = args; - ctx->argumentCount = argc; - - ctx->initCallContext(context); - Value result = call(ctx); - ctx->leaveCallContext(); - return result; + return Value::undefinedValue(); } void FunctionObject::markObjects(Managed *that) @@ -181,12 +158,6 @@ void FunctionObject::markObjects(Managed *that) Object::markObjects(that); } -Value FunctionObject::call(ExecutionContext *ctx) -{ - Q_UNUSED(ctx); - return Value::undefinedValue(); -} - const ManagedVTable FunctionObject::static_vtbl = { FunctionObject::markObjects, @@ -387,6 +358,7 @@ ScriptFunction::~ScriptFunction() Value ScriptFunction::construct(ExecutionContext *context, Value *args, int argc) { + assert(function->code); Object *obj = context->engine->newObject(); Value proto = __get__(context, context->engine->id_prototype); if (proto.isObject()) @@ -401,7 +373,7 @@ Value ScriptFunction::construct(ExecutionContext *context, Value *args, int argc ctx->argumentCount = argc; ctx->initCallContext(context); - Value result = call(ctx); + Value result = function->code(ctx, function->codeData); ctx->leaveCallContext(); if (result.isObject()) @@ -409,15 +381,37 @@ Value ScriptFunction::construct(ExecutionContext *context, Value *args, int argc return Value::fromObject(obj); } - - -Value ScriptFunction::call(ExecutionContext *ctx) +Value ScriptFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) { assert(function->code); - return function->code(ctx, function->codeData); + uint size = requiredMemoryForExecutionContect(this, argc); + ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); + + if (!strictMode && !thisObject.isObject()) { + // Built-in functions allow for the this object to be null or undefined. This overrides + // the behaviour of changing thisObject to the global object if null/undefined and allows + // the built-in functions for example to throw a type error if null is passed. + if (thisObject.isUndefined() || thisObject.isNull()) { + if (!isBuiltinFunction) + thisObject = context->engine->globalObject; + } else { + thisObject = thisObject.toObject(context); + } + } + + ctx->thisObject = thisObject; + ctx->function = this; + ctx->arguments = args; + ctx->argumentCount = argc; + + ctx->initCallContext(context); + Value result = function->code(ctx, function->codeData); + ctx->leaveCallContext(); + return result; } + BuiltinFunctionOld::BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) : FunctionObject(scope) , code(code) @@ -432,6 +426,34 @@ Value BuiltinFunctionOld::construct(ExecutionContext *ctx, Value *, int) return Value::undefinedValue(); } +Value BuiltinFunctionOld::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +{ + uint size = requiredMemoryForExecutionContect(this, argc); + ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); + + if (!strictMode && !thisObject.isObject()) { + // Built-in functions allow for the this object to be null or undefined. This overrides + // the behaviour of changing thisObject to the global object if null/undefined and allows + // the built-in functions for example to throw a type error if null is passed. + if (thisObject.isUndefined() || thisObject.isNull()) { + if (!isBuiltinFunction) + thisObject = context->engine->globalObject; + } else { + thisObject = thisObject.toObject(context); + } + } + + ctx->thisObject = thisObject; + ctx->function = this; + ctx->arguments = args; + ctx->argumentCount = argc; + + ctx->initCallContext(context); + Value result = code(ctx); + ctx->leaveCallContext(); + return result; +} + BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)) : FunctionObject(scope) , code(code) diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index c1e9b289a9..7cf73e8db0 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -157,13 +157,11 @@ struct Q_V4_EXPORT FunctionObject: Object { FunctionObject(ExecutionContext *scope); virtual Value construct(ExecutionContext *context, Value *args, int argc); - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); + virtual Value call(ExecutionContext *, Value, Value *, int); virtual struct ScriptFunction *asScriptFunction() { return 0; } protected: - virtual Value call(ExecutionContext *ctx); - static const ManagedVTable static_vtbl; static void markObjects(Managed *that); static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value *value); @@ -192,7 +190,7 @@ struct BuiltinFunctionOld: FunctionObject { Value (*code)(ExecutionContext *); BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); - virtual Value call(ExecutionContext *ctx) { return code(ctx); } + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); virtual Value construct(ExecutionContext *ctx, Value *, int); }; @@ -210,8 +208,8 @@ struct ScriptFunction: FunctionObject { ScriptFunction(ExecutionContext *scope, VM::Function *function); virtual ~ScriptFunction(); + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); virtual Value construct(ExecutionContext *context, Value *args, int argc); - virtual Value call(ExecutionContext *ctx); virtual ScriptFunction *asScriptFunction() { return this; } }; -- cgit v1.2.3 From b09bb08d7440cfcf4ab6440bac4990e0b4542c30 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Feb 2013 10:45:49 +0100 Subject: Convert run-time functions that take a Value * for arguments to use a Value reference This keeps the C++ implementation code simpler while still providing the same (pointer based) calling convention. Change-Id: Ib72acf1dfdf4638f6d109a0771fdafc921a544d2 Reviewed-by: Lars Knoll --- src/v4/llvm_runtime.cpp | 114 +++++------ src/v4/moth/qv4instr_moth_p.h | 2 +- src/v4/moth/qv4isel_moth.cpp | 2 +- src/v4/moth/qv4vme_moth.cpp | 28 +-- src/v4/qmljs_environment.cpp | 8 +- src/v4/qmljs_environment.h | 6 +- src/v4/qmljs_runtime.cpp | 198 +++++++++---------- src/v4/qmljs_runtime.h | 428 +++++++++++++++++++++--------------------- src/v4/qmljs_value.h | 2 +- src/v4/qv4functionobject.cpp | 6 +- src/v4/qv4functionobject.h | 4 +- src/v4/qv4isel_masm.cpp | 10 +- src/v4/qv4managed.cpp | 2 +- src/v4/qv4managed.h | 6 +- src/v4/qv4object.cpp | 18 +- src/v4/qv4object.h | 10 +- 16 files changed, 423 insertions(+), 421 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 3009da6688..2f6e9f14d4 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -105,107 +105,107 @@ bool __qmljs_llvm_to_boolean(ExecutionContext *ctx, const Value *value) void __qmljs_llvm_bit_and(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_bit_and(ctx, result, left, right); + __qmljs_bit_and(ctx, result, *left, *right); } void __qmljs_llvm_bit_or(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_bit_or(ctx, result, left, right); + __qmljs_bit_or(ctx, result, *left, *right); } void __qmljs_llvm_bit_xor(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_bit_xor(ctx, result, left, right); + __qmljs_bit_xor(ctx, result, *left, *right); } void __qmljs_llvm_add(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_add(ctx, result, left, right); + __qmljs_add(ctx, result, *left, *right); } void __qmljs_llvm_sub(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_sub(ctx, result, left, right); + __qmljs_sub(ctx, result, *left, *right); } void __qmljs_llvm_mul(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_mul(ctx, result, left, right); + __qmljs_mul(ctx, result, *left, *right); } void __qmljs_llvm_div(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_div(ctx, result, left, right); + __qmljs_div(ctx, result, *left, *right); } void __qmljs_llvm_mod(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_mod(ctx, result, left, right); + __qmljs_mod(ctx, result, *left, *right); } void __qmljs_llvm_shl(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_shl(ctx, result, left, right); + __qmljs_shl(ctx, result, *left, *right); } void __qmljs_llvm_shr(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_shr(ctx, result, left, right); + __qmljs_shr(ctx, result, *left, *right); } void __qmljs_llvm_ushr(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_ushr(ctx, result, left, right); + __qmljs_ushr(ctx, result, *left, *right); } void __qmljs_llvm_gt(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_gt(ctx, result, left, right); + __qmljs_gt(ctx, result, *left, *right); } void __qmljs_llvm_lt(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_lt(ctx, result, left, right); + __qmljs_lt(ctx, result, *left, *right); } void __qmljs_llvm_ge(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_ge(ctx, result, left, right); + __qmljs_ge(ctx, result, *left, *right); } void __qmljs_llvm_le(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_le(ctx, result, left, right); + __qmljs_le(ctx, result, *left, *right); } void __qmljs_llvm_eq(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_eq(ctx, result, left, right); + __qmljs_eq(ctx, result, *left, *right); } void __qmljs_llvm_ne(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_ne(ctx, result, left, right); + __qmljs_ne(ctx, result, *left, *right); } void __qmljs_llvm_se(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_se(ctx, result, left, right); + __qmljs_se(ctx, result, *left, *right); } void __qmljs_llvm_sne(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_sne(ctx, result, left, right); + __qmljs_sne(ctx, result, *left, *right); } void __qmljs_llvm_instanceof(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_instanceof(ctx, result, left, right); + __qmljs_instanceof(ctx, result, *left, *right); } void __qmljs_llvm_in(ExecutionContext *ctx, Value *result, Value *left, Value *right) { - __qmljs_in(ctx, result, left, right); + __qmljs_in(ctx, result, *left, *right); } void __qmljs_llvm_uplus(ExecutionContext *ctx, Value *result, const Value *value) @@ -230,167 +230,167 @@ void __qmljs_llvm_not(ExecutionContext *ctx, Value *result, const Value *value) void __qmljs_llvm_inplace_bit_and_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_bit_and_name(ctx, dest, src); + __qmljs_inplace_bit_and_name(ctx, dest, *src); } void __qmljs_llvm_inplace_bit_or_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_bit_or_name(ctx, dest, src); + __qmljs_inplace_bit_or_name(ctx, dest, *src); } void __qmljs_llvm_inplace_bit_xor_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_bit_xor_name(ctx, dest, src); + __qmljs_inplace_bit_xor_name(ctx, dest, *src); } void __qmljs_llvm_inplace_add_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_add_name(ctx, dest, src); + __qmljs_inplace_add_name(ctx, dest, *src); } void __qmljs_llvm_inplace_sub_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_sub_name(ctx, dest, src); + __qmljs_inplace_sub_name(ctx, dest, *src); } void __qmljs_llvm_inplace_mul_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_mul_name(ctx, dest, src); + __qmljs_inplace_mul_name(ctx, dest, *src); } void __qmljs_llvm_inplace_div_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_div_name(ctx, dest, src); + __qmljs_inplace_div_name(ctx, dest, *src); } void __qmljs_llvm_inplace_mod_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_mod_name(ctx, dest, src); + __qmljs_inplace_mod_name(ctx, dest, *src); } void __qmljs_llvm_inplace_shl_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_shl_name(ctx, dest, src); + __qmljs_inplace_shl_name(ctx, dest, *src); } void __qmljs_llvm_inplace_shr_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_shr_name(ctx, dest, src); + __qmljs_inplace_shr_name(ctx, dest, *src); } void __qmljs_llvm_inplace_ushr_name(ExecutionContext *ctx, String *dest, Value *src) { - __qmljs_inplace_ushr_name(ctx, dest, src); + __qmljs_inplace_ushr_name(ctx, dest, *src); } void __qmljs_llvm_inplace_bit_and_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_bit_and_element(ctx, base, index, value); + __qmljs_inplace_bit_and_element(ctx, *base, *index, *value); } void __qmljs_llvm_inplace_bit_or_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_bit_or_element(ctx, base, index, value); + __qmljs_inplace_bit_or_element(ctx, *base, *index, *value); } void __qmljs_llvm_inplace_bit_xor_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_bit_xor_element(ctx, base, index, value); + __qmljs_inplace_bit_xor_element(ctx, *base, *index, *value); } void __qmljs_llvm_inplace_add_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_add_element(ctx, base, index, value); + __qmljs_inplace_add_element(ctx, *base, *index, *value); } void __qmljs_llvm_inplace_sub_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_sub_element(ctx, base, index, value); + __qmljs_inplace_sub_element(ctx, *base, *index, *value); } void __qmljs_llvm_inplace_mul_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_mul_element(ctx, base, index, value); + __qmljs_inplace_mul_element(ctx, *base, *index, *value); } void __qmljs_llvm_inplace_div_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_div_element(ctx, base, index, value); + __qmljs_inplace_div_element(ctx, *base, *index, *value); } void __qmljs_llvm_inplace_mod_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_mod_element(ctx, base, index, value); + __qmljs_inplace_mod_element(ctx, *base, *index, *value); } void __qmljs_llvm_inplace_shl_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_shl_element(ctx, base, index, value); + __qmljs_inplace_shl_element(ctx, *base, *index, *value); } void __qmljs_llvm_inplace_shr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_shr_element(ctx, base, index, value); + __qmljs_inplace_shr_element(ctx, *base, *index, *value); } void __qmljs_llvm_inplace_ushr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) { - __qmljs_inplace_ushr_element(ctx, base, index, value); + __qmljs_inplace_ushr_element(ctx, *base, *index, *value); } void __qmljs_llvm_inplace_bit_and_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_bit_and_member(ctx, base, member, value); + __qmljs_inplace_bit_and_member(ctx, *base, member, *value); } void __qmljs_llvm_inplace_bit_or_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_bit_or_member(ctx, base, member, value); + __qmljs_inplace_bit_or_member(ctx, *base, member, *value); } void __qmljs_llvm_inplace_bit_xor_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_bit_xor_member(ctx, base, member, value); + __qmljs_inplace_bit_xor_member(ctx, *base, member, *value); } void __qmljs_llvm_inplace_add_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_add_member(ctx, base, member, value); + __qmljs_inplace_add_member(ctx, *base, member, *value); } void __qmljs_llvm_inplace_sub_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_sub_member(ctx, base, member, value); + __qmljs_inplace_sub_member(ctx, *base, member, *value); } void __qmljs_llvm_inplace_mul_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_mul_member(ctx, base, member, value); + __qmljs_inplace_mul_member(ctx, *base, member, *value); } void __qmljs_llvm_inplace_div_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_div_member(ctx, base, member, value); + __qmljs_inplace_div_member(ctx, *base, member, *value); } void __qmljs_llvm_inplace_mod_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_mod_member(ctx, base, member, value); + __qmljs_inplace_mod_member(ctx, *base, member, *value); } void __qmljs_llvm_inplace_shl_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_shl_member(ctx, base, member, value); + __qmljs_inplace_shl_member(ctx, *base, member, *value); } void __qmljs_llvm_inplace_shr_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_shr_member(ctx, base, member, value); + __qmljs_inplace_shr_member(ctx, *base, member, *value); } void __qmljs_llvm_inplace_ushr_member(ExecutionContext *ctx, Value *value, Value *base, String *member) { - __qmljs_inplace_ushr_member(ctx, base, member, value); + __qmljs_inplace_ushr_member(ctx, *base, member, *value); } String *__qmljs_llvm_identifier_from_utf8(ExecutionContext *ctx, const char *str) @@ -426,12 +426,12 @@ void __qmljs_llvm_get_activation_property(ExecutionContext *ctx, Value *result, void __qmljs_llvm_set_activation_property(ExecutionContext *ctx, String *name, Value *value) { - __qmljs_set_activation_property(ctx, name, value); + __qmljs_set_activation_property(ctx, name, *value); } void __qmljs_llvm_get_property(ExecutionContext *ctx, Value *result, Value *object, String *name) { - __qmljs_get_property(ctx, result, object, name); + __qmljs_get_property(ctx, result, *object, name); } void __qmljs_llvm_call_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) @@ -456,7 +456,7 @@ void __qmljs_llvm_set_element(ExecutionContext *ctx, Value *object, Value *index void __qmljs_llvm_set_property(ExecutionContext *ctx, Value *object, String *name, Value *value) { - __qmljs_set_property(ctx, object, name, value); + __qmljs_set_property(ctx, *object, name, *value); } void __qmljs_llvm_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) diff --git a/src/v4/moth/qv4instr_moth_p.h b/src/v4/moth/qv4instr_moth_p.h index f600ae8980..c28e2a26e6 100644 --- a/src/v4/moth/qv4instr_moth_p.h +++ b/src/v4/moth/qv4instr_moth_p.h @@ -400,7 +400,7 @@ union Instr }; struct instr_unop { MOTH_INSTR_HEADER - VM::Value (*alu)(const VM::Value value, VM::ExecutionContext *ctx); + VM::UnaryOpName alu; Param source; Param result; }; diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index c0c282bb72..8cd9e385a8 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -482,7 +482,7 @@ void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) { - VM::Value (*op)(const VM::Value value, VM::ExecutionContext *ctx) = 0; + VM::Value (*op)(const VM::Value& value, VM::ExecutionContext *ctx) = 0; switch (oper) { case IR::OpIfTrue: assert(!"unreachable"); break; case IR::OpNot: op = VM::__qmljs_not; break; diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 0dbaee65aa..cd6264a0a7 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -182,7 +182,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(StoreName) TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); - __qmljs_set_activation_property(context, instr.name, VALUEPTR(instr.source)); + __qmljs_set_activation_property(context, instr.name, VALUE(instr.source)); MOTH_END_INSTR(StoreName) MOTH_BEGIN_INSTR(LoadElement) @@ -194,11 +194,11 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) - __qmljs_get_property(context, VALUEPTR(instr.result), VALUEPTR(instr.base), instr.name); + __qmljs_get_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name); MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(StoreProperty) - __qmljs_set_property(context, VALUEPTR(instr.base), instr.name, VALUEPTR(instr.source)); + __qmljs_set_property(context, VALUE(instr.base), instr.name, VALUE(instr.source)); MOTH_END_INSTR(StoreProperty) MOTH_BEGIN_INSTR(Push) @@ -328,11 +328,11 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinTypeofValue) MOTH_BEGIN_INSTR(CallBuiltinPostIncMember) - __qmljs_builtin_post_increment_member(context, VALUEPTR(instr.result), VALUEPTR(instr.base), instr.member); + __qmljs_builtin_post_increment_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); MOTH_END_INSTR(CallBuiltinTypeofMember) MOTH_BEGIN_INSTR(CallBuiltinPostIncSubscript) - __qmljs_builtin_post_increment_element(context, VALUEPTR(instr.result), VALUEPTR(instr.base), VALUEPTR(instr.index)); + __qmljs_builtin_post_increment_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUEPTR(instr.index)); MOTH_END_INSTR(CallBuiltinTypeofSubscript) MOTH_BEGIN_INSTR(CallBuiltinPostIncName) @@ -344,11 +344,11 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinTypeofValue) MOTH_BEGIN_INSTR(CallBuiltinPostDecMember) - __qmljs_builtin_post_decrement_member(context, VALUEPTR(instr.result), VALUEPTR(instr.base), instr.member); + __qmljs_builtin_post_decrement_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); MOTH_END_INSTR(CallBuiltinTypeofMember) MOTH_BEGIN_INSTR(CallBuiltinPostDecSubscript) - __qmljs_builtin_post_decrement_element(context, VALUEPTR(instr.result), VALUEPTR(instr.base), VALUEPTR(instr.index)); + __qmljs_builtin_post_decrement_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); MOTH_END_INSTR(CallBuiltinTypeofSubscript) MOTH_BEGIN_INSTR(CallBuiltinPostDecName) @@ -410,7 +410,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(Unop) MOTH_BEGIN_INSTR(Binop) - instr.alu(context, VALUEPTR(instr.result), VALUEPTR(instr.lhs), VALUEPTR(instr.rhs)); + instr.alu(context, VALUEPTR(instr.result), VALUE(instr.lhs), VALUE(instr.rhs)); MOTH_END_INSTR(Binop) MOTH_BEGIN_INSTR(Ret) @@ -425,21 +425,21 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(InplaceElementOp) instr.alu(context, - VALUEPTR(instr.base), - VALUEPTR(instr.index), - VALUEPTR(instr.source)); + VALUE(instr.base), + VALUE(instr.index), + VALUE(instr.source)); MOTH_END_INSTR(InplaceElementOp) MOTH_BEGIN_INSTR(InplaceMemberOp) instr.alu(context, - VALUEPTR(instr.base), + VALUE(instr.base), instr.member, - VALUEPTR(instr.source)); + VALUE(instr.source)); MOTH_END_INSTR(InplaceMemberOp) MOTH_BEGIN_INSTR(InplaceNameOp) TRACE(name, "%s", instr.name->toQString().toUtf8().constData()); - instr.alu(context, instr.name, VALUEPTR(instr.source)); + instr.alu(context, instr.name, VALUE(instr.source)); MOTH_END_INSTR(InplaceNameOp) #ifdef MOTH_THREADED_INTERPRETER diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp index 1d61a1b717..801f8d7b72 100644 --- a/src/v4/qmljs_environment.cpp +++ b/src/v4/qmljs_environment.cpp @@ -113,7 +113,7 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) activation->__defineOwnProperty__(this, name, &desc); } -bool ExecutionContext::setMutableBinding(ExecutionContext *scope, String *name, Value value) +bool ExecutionContext::setMutableBinding(ExecutionContext *scope, String *name, const Value &value) { // ### throw if scope->strict is true, and it would change an immutable binding if (function) { @@ -332,7 +332,7 @@ void ExecutionContext::mark() exceptionValue.mark(); } -void ExecutionContext::setProperty(String *name, Value value) +void ExecutionContext::setProperty(String *name, const Value& value) { // qDebug() << "=== SetProperty" << value.toString(this)->toQString(); for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { @@ -503,11 +503,11 @@ Value ExecutionContext::getPropertyAndBase(String *name, Object **base) -void ExecutionContext::inplaceBitOp(String *name, const Value *value, BinOp op) +void ExecutionContext::inplaceBitOp(String *name, const Value &value, BinOp op) { Value lhs = getProperty(name); Value result; - op(this, &result, &lhs, value); + op(this, &result, lhs, value); setProperty(name, result); } diff --git a/src/v4/qmljs_environment.h b/src/v4/qmljs_environment.h index a3af53a043..473b2ea2b0 100644 --- a/src/v4/qmljs_environment.h +++ b/src/v4/qmljs_environment.h @@ -108,7 +108,7 @@ struct ExecutionContext bool hasBinding(String *name) const; void createMutableBinding(String *name, bool deletable); - bool setMutableBinding(ExecutionContext *scope, String *name, Value value); + bool setMutableBinding(ExecutionContext *scope, String *name, const Value &value); Value getBindingValue(ExecutionContext *scope, String *name, bool strict) const; bool deleteBinding(ExecutionContext *ctx, String *name); @@ -130,11 +130,11 @@ struct ExecutionContext void throwURIError(Value msg); void throwUnimplemented(const QString &message); - void setProperty(String *name, Value value); + void setProperty(String *name, const Value &value); Value getProperty(String *name); Value getPropertyNoThrow(String *name); Value getPropertyAndBase(String *name, Object **base); - void inplaceBitOp(String *name, const QQmlJS::VM::Value *value, BinOp op); + void inplaceBitOp(String *name, const QQmlJS::VM::Value &value, BinOp op); bool deleteProperty(String *name); inline Value argument(unsigned int index = 0) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 77f8b83dac..520c1e3ce3 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -189,7 +189,7 @@ Value __qmljs_string_literal_function(ExecutionContext *ctx) return Value::fromString(ctx->engine->newString(QStringLiteral("function"))); } -Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index) +Value __qmljs_delete_subscript(ExecutionContext *ctx, const Value &base, Value index) { if (Object *o = base.asObject()) { uint n = UINT_MAX; @@ -205,7 +205,7 @@ Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index) return __qmljs_delete_member(ctx, base, name); } -Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name) +Value __qmljs_delete_member(ExecutionContext *ctx, const Value &base, String *name) { Value obj = base.toObject(ctx); return Value::fromBoolean(obj.objectValue()->__delete__(ctx, name)); @@ -216,10 +216,10 @@ Value __qmljs_delete_name(ExecutionContext *ctx, String *name) return Value::fromBoolean(ctx->deleteProperty(name)); } -void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { - Value pleft = __qmljs_to_primitive(*left, ctx, PREFERREDTYPE_HINT); - Value pright = __qmljs_to_primitive(*right, ctx, PREFERREDTYPE_HINT); + Value pleft = __qmljs_to_primitive(left, ctx, PREFERREDTYPE_HINT); + Value pright = __qmljs_to_primitive(right, ctx, PREFERREDTYPE_HINT); if (pleft.isString() || pright.isString()) { if (!pleft.isString()) pleft = __qmljs_to_string(pleft, ctx); @@ -234,9 +234,9 @@ void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value *left, *result = Value::fromDouble(x + y); } -void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { - Object *o = right->asObject(); + Object *o = right.asObject(); if (!o) __qmljs_throw_type_error(ctx); @@ -244,199 +244,199 @@ void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value *left, *result = Value::fromBoolean(r); } -void __qmljs_in(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +void __qmljs_in(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { - if (!right->isObject()) + if (!right.isObject()) __qmljs_throw_type_error(ctx); - String *s = left->toString(ctx); - bool r = right->objectValue()->__hasProperty__(ctx, s); + String *s = left.toString(ctx); + bool r = right.objectValue()->__hasProperty__(ctx, s); *result = Value::fromBoolean(r); } -void __qmljs_inplace_bit_and_name(ExecutionContext *ctx, String *name, const Value *value) +void __qmljs_inplace_bit_and_name(ExecutionContext *ctx, String *name, const Value &value) { ctx->inplaceBitOp(name, value, __qmljs_bit_and); } -void __qmljs_inplace_bit_or_name(ExecutionContext *ctx, String *name, const Value *value) +void __qmljs_inplace_bit_or_name(ExecutionContext *ctx, String *name, const Value &value) { ctx->inplaceBitOp(name, value, __qmljs_bit_or); } -void __qmljs_inplace_bit_xor_name(ExecutionContext *ctx, String *name, const Value *value) +void __qmljs_inplace_bit_xor_name(ExecutionContext *ctx, String *name, const Value &value) { ctx->inplaceBitOp(name, value, __qmljs_bit_xor); } -void __qmljs_inplace_add_name(ExecutionContext *ctx, String *name, const Value *value) +void __qmljs_inplace_add_name(ExecutionContext *ctx, String *name, const Value &value) { ctx->inplaceBitOp(name, value, __qmljs_add); } -void __qmljs_inplace_sub_name(ExecutionContext *ctx, String *name, const Value *value) +void __qmljs_inplace_sub_name(ExecutionContext *ctx, String *name, const Value &value) { ctx->inplaceBitOp(name, value, __qmljs_sub); } -void __qmljs_inplace_mul_name(ExecutionContext *ctx, String *name, const Value *value) +void __qmljs_inplace_mul_name(ExecutionContext *ctx, String *name, const Value &value) { ctx->inplaceBitOp(name, value, __qmljs_mul); } -void __qmljs_inplace_div_name(ExecutionContext *ctx, String *name, const Value *value) +void __qmljs_inplace_div_name(ExecutionContext *ctx, String *name, const Value &value) { ctx->inplaceBitOp(name, value, __qmljs_div); } -void __qmljs_inplace_mod_name(ExecutionContext *ctx, String *name, const Value *value) +void __qmljs_inplace_mod_name(ExecutionContext *ctx, String *name, const Value &value) { ctx->inplaceBitOp(name, value, __qmljs_mod); } -void __qmljs_inplace_shl_name(ExecutionContext *ctx, String *name, const Value *value) +void __qmljs_inplace_shl_name(ExecutionContext *ctx, String *name, const Value &value) { ctx->inplaceBitOp(name, value, __qmljs_shl); } -void __qmljs_inplace_shr_name(ExecutionContext *ctx, String *name, const Value *value) +void __qmljs_inplace_shr_name(ExecutionContext *ctx, String *name, const Value &value) { ctx->inplaceBitOp(name, value, __qmljs_shr); } -void __qmljs_inplace_ushr_name(ExecutionContext *ctx, String *name, const Value *value) +void __qmljs_inplace_ushr_name(ExecutionContext *ctx, String *name, const Value &value) { ctx->inplaceBitOp(name, value, __qmljs_ushr); } -void __qmljs_inplace_bit_and_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) +void __qmljs_inplace_bit_and_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(ctx, __qmljs_bit_and, index, rhs); } -void __qmljs_inplace_bit_or_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) +void __qmljs_inplace_bit_or_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(ctx, __qmljs_bit_or, index, rhs); } -void __qmljs_inplace_bit_xor_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) +void __qmljs_inplace_bit_xor_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(ctx, __qmljs_bit_xor, index, rhs); } -void __qmljs_inplace_add_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) +void __qmljs_inplace_add_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(ctx, __qmljs_add, index, rhs); } -void __qmljs_inplace_sub_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) +void __qmljs_inplace_sub_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(ctx, __qmljs_sub, index, rhs); } -void __qmljs_inplace_mul_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) +void __qmljs_inplace_mul_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(ctx, __qmljs_mul, index, rhs); } -void __qmljs_inplace_div_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) +void __qmljs_inplace_div_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(ctx, __qmljs_div, index, rhs); } -void __qmljs_inplace_mod_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) +void __qmljs_inplace_mod_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(ctx, __qmljs_mod, index, rhs); } -void __qmljs_inplace_shl_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) +void __qmljs_inplace_shl_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(ctx, __qmljs_shl, index, rhs); } -void __qmljs_inplace_shr_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) +void __qmljs_inplace_shr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(ctx, __qmljs_shr, index, rhs); } -void __qmljs_inplace_ushr_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs) +void __qmljs_inplace_ushr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base->toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx).objectValue(); obj->inplaceBinOp(ctx, __qmljs_ushr, index, rhs); } -void __qmljs_inplace_bit_and_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) +void __qmljs_inplace_bit_and_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base->toObject(ctx).objectValue(); + Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(ctx, __qmljs_bit_and, name, rhs); } -void __qmljs_inplace_bit_or_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) +void __qmljs_inplace_bit_or_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base->toObject(ctx).objectValue(); + Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(ctx, __qmljs_bit_or, name, rhs); } -void __qmljs_inplace_bit_xor_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) +void __qmljs_inplace_bit_xor_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base->toObject(ctx).objectValue(); + Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(ctx, __qmljs_bit_xor, name, rhs); } -void __qmljs_inplace_add_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) +void __qmljs_inplace_add_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base->toObject(ctx).objectValue(); + Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(ctx, __qmljs_add, name, rhs); } -void __qmljs_inplace_sub_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) +void __qmljs_inplace_sub_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base->toObject(ctx).objectValue(); + Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(ctx, __qmljs_sub, name, rhs); } -void __qmljs_inplace_mul_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) +void __qmljs_inplace_mul_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base->toObject(ctx).objectValue(); + Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(ctx, __qmljs_mul, name, rhs); } -void __qmljs_inplace_div_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) +void __qmljs_inplace_div_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base->toObject(ctx).objectValue(); + Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(ctx, __qmljs_div, name, rhs); } -void __qmljs_inplace_mod_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) +void __qmljs_inplace_mod_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base->toObject(ctx).objectValue(); + Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(ctx, __qmljs_mod, name, rhs); } -void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) +void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base->toObject(ctx).objectValue(); + Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(ctx, __qmljs_shl, name, rhs); } -void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) +void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base->toObject(ctx).objectValue(); + Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(ctx, __qmljs_shr, name, rhs); } -void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs) +void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base->toObject(ctx).objectValue(); + Object *o = base.toObject(ctx).objectValue(); o->inplaceBinOp(ctx, __qmljs_ushr, name, rhs); } @@ -567,10 +567,10 @@ Value __qmljs_new_string_object(ExecutionContext *ctx, String *string) return Value::fromObject(ctx->engine->newStringObject(ctx, value)); } -void __qmljs_set_property(ExecutionContext *ctx, const Value *object, String *name, const Value *value) +void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *name, const Value &value) { - Object *o = object->isObject() ? object->objectValue() : __qmljs_to_object(*object, ctx).objectValue(); - o->__put__(ctx, name, *value); + Object *o = object.isObject() ? object.objectValue() : __qmljs_to_object(object, ctx).objectValue(); + o->__put__(ctx, name, value); } Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) @@ -645,21 +645,21 @@ Value __qmljs_foreach_next_property_name(Value foreach_iterator) } -void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value *value) +void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value &value) { - ctx->setProperty(name, *value); + ctx->setProperty(name, value); } -void __qmljs_get_property(ExecutionContext *ctx, Value *result, Value *object, String *name) +void __qmljs_get_property(ExecutionContext *ctx, Value *result, const Value &object, String *name) { Value res; - Object *o = object->asObject(); + Object *o = object.asObject(); if (o) { res = o->__get__(ctx, name); - } else if (object->isString() && name->isEqualTo(ctx->engine->id_length)) { - res = Value::fromInt32(object->stringValue()->toQString().length()); + } else if (object.isString() && name->isEqualTo(ctx->engine->id_length)) { + res = Value::fromInt32(object.stringValue()->toQString().length()); } else { - o = __qmljs_to_object(*object, ctx).objectValue(); + o = __qmljs_to_object(object, ctx).objectValue(); res = o->__get__(ctx, name); } if (result) @@ -671,11 +671,11 @@ void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, Strin *result = ctx->getProperty(name); } -void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Value *object, int lookupIndex) +void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Value &object, int lookupIndex) { Value res; Lookup *l = ctx->lookups + lookupIndex; - if (Object *o = object->asObject()) { + if (Object *o = object.asObject()) { PropertyDescriptor *p = 0; if (o->internalClass == l->mainClass) { if (!l->protoClass) { @@ -708,10 +708,10 @@ void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Val else res = o->__get__(ctx, l->name); } else { - if (object->isString() && l->name->isEqualTo(ctx->engine->id_length)) { - res = Value::fromInt32(object->stringValue()->toQString().length()); + if (object.isString() && l->name->isEqualTo(ctx->engine->id_length)) { + res = Value::fromInt32(object.stringValue()->toQString().length()); } else { - o = __qmljs_to_object(*object, ctx).objectValue(); + o = __qmljs_to_object(object, ctx).objectValue(); res = o->__get__(ctx, l->name); } } @@ -719,14 +719,14 @@ void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Val *result = res; } -void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value *object, int lookupIndex, const Value *value) +void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value &object, int lookupIndex, const Value &value) { - Object *o = object->isObject() ? object->objectValue() : __qmljs_to_object(*object, ctx).objectValue(); + Object *o = object.isObject() ? object.objectValue() : __qmljs_to_object(object, ctx).objectValue(); Lookup *l = ctx->lookups + lookupIndex; if (l->index != ArrayObject::LengthPropertyIndex || !o->isArrayObject()) { if (o->internalClass == l->mainClass) { - o->putValue(ctx, o->memberData + l->index, *value); + o->putValue(ctx, o->memberData + l->index, value); return; } @@ -734,11 +734,11 @@ void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value *object, int if (idx < UINT_MAX) { l->mainClass = o->internalClass; l->index = idx; - return o->putValue(ctx, o->memberData + idx, *value); + return o->putValue(ctx, o->memberData + idx, value); } } - o->__put__(ctx, l->name, *value); + o->__put__(ctx, l->name, value); } @@ -747,7 +747,7 @@ Value __qmljs_get_thisObject(ExecutionContext *ctx) return ctx->thisObject; } -uint __qmljs_equal(Value x, Value y, ExecutionContext *ctx) +uint __qmljs_equal(const Value &x, const Value &y, ExecutionContext *ctx) { if (x.type() == y.type()) { switch (x.type()) { @@ -1074,9 +1074,9 @@ void __qmljs_builtin_post_increment_name(ExecutionContext *context, Value *resul context->setProperty(name, v); } -void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *result, const Value *base, String *name) +void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *result, const Value &base, String *name) { - Object *o = __qmljs_to_object(*base, context).objectValue(); + Object *o = __qmljs_to_object(base, context).objectValue(); Value v = o->__get__(context, name); @@ -1094,9 +1094,9 @@ void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *res o->__put__(context, name, v); } -void __qmljs_builtin_post_increment_element(ExecutionContext *context, Value *result, const Value *base, const Value *index) +void __qmljs_builtin_post_increment_element(ExecutionContext *context, Value *result, const Value &base, const Value *index) { - Object *o = __qmljs_to_object(*base, context).objectValue(); + Object *o = __qmljs_to_object(base, context).objectValue(); uint idx = index->asArrayIndex(); @@ -1154,9 +1154,9 @@ void __qmljs_builtin_post_decrement_name(ExecutionContext *context, Value *resul context->setProperty(name, v); } -void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value *base, String *name) +void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value &base, String *name) { - Object *o = __qmljs_to_object(*base, context).objectValue(); + Object *o = __qmljs_to_object(base, context).objectValue(); Value v = o->__get__(context, name); @@ -1174,14 +1174,14 @@ void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *res o->__put__(context, name, v); } -void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value *base, const Value *index) +void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value &base, const Value &index) { - Object *o = __qmljs_to_object(*base, context).objectValue(); + Object *o = __qmljs_to_object(base, context).objectValue(); - uint idx = index->asArrayIndex(); + uint idx = index.asArrayIndex(); if (idx == UINT_MAX) { - String *s = index->toString(context); + String *s = index.toString(context); return __qmljs_builtin_post_decrement_member(context, result, base, s); } @@ -1270,7 +1270,7 @@ void __qmljs_builtin_define_getter_setter(Value object, String *name, Value gett o->__defineOwnProperty__(ctx, name, &pd); } -Value __qmljs_increment(Value value, ExecutionContext *ctx) +Value __qmljs_increment(const Value &value, ExecutionContext *ctx) { TRACE1(value); @@ -1281,7 +1281,7 @@ Value __qmljs_increment(Value value, ExecutionContext *ctx) return Value::fromDouble(d + 1); } -Value __qmljs_decrement(Value value, ExecutionContext *ctx) +Value __qmljs_decrement(const Value &value, ExecutionContext *ctx) { TRACE1(value); diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 7dcba81467..4c507012c5 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -108,13 +108,13 @@ Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext * void __qmljs_builtin_post_increment(ExecutionContext *ctx, Value *result, Value *val); void __qmljs_builtin_post_increment_name(ExecutionContext *context, Value *result, String *name); -void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *result, const Value *base, String *name); -void __qmljs_builtin_post_increment_element(ExecutionContext *context, Value *result, const Value *base, const Value *index); +void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *result, const Value &base, String *name); +void __qmljs_builtin_post_increment_element(ExecutionContext *context, Value *result, const Value &base, const Value *index); void __qmljs_builtin_post_decrement(ExecutionContext *ctx, Value *result, Value *val); void __qmljs_builtin_post_decrement_name(ExecutionContext *context, Value *result, String *name); -void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value *base, String *name); -void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value *base, const Value *index); +void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value &base, String *name); +void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value &base, const Value &index); void __qmljs_builtin_throw(Value val, ExecutionContext *context); void __qmljs_builtin_rethrow(ExecutionContext *context); @@ -165,13 +165,13 @@ Value __qmljs_new_object(ExecutionContext *ctx); Value __qmljs_new_boolean_object(ExecutionContext *ctx, bool boolean); Value __qmljs_new_number_object(ExecutionContext *ctx, double n); Value __qmljs_new_string_object(ExecutionContext *ctx, String *string); -void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value *value); -void __qmljs_set_property(ExecutionContext *ctx, const Value *object, String *name, const Value *value); -void __qmljs_get_property(ExecutionContext *ctx, Value *result, Value *object, String *name); +void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value& value); +void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *name, const Value &value); +void __qmljs_get_property(ExecutionContext *ctx, Value *result, const Value &object, String *name); void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, String *name); -void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Value *object, int lookupIndex); -void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value *object, int lookupIndex, const Value *value); +void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Value &object, int lookupIndex); +void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value &object, int lookupIndex, const Value &value); Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index); @@ -185,31 +185,32 @@ Value __qmljs_foreach_next_property_name(Value foreach_iterator); Value __qmljs_get_thisObject(ExecutionContext *ctx); // type conversion and testing -Value __qmljs_to_primitive(Value value, ExecutionContext *ctx, int typeHint); -Bool __qmljs_to_boolean(Value value, ExecutionContext *ctx); -double __qmljs_to_number(Value value, ExecutionContext *ctx); -double __qmljs_to_integer(Value value, ExecutionContext *ctx); -int __qmljs_to_int32(Value value, ExecutionContext *ctx); -unsigned short __qmljs_to_uint16(Value value, ExecutionContext *ctx); -Value __qmljs_to_string(Value value, ExecutionContext *ctx); -Value __qmljs_to_object(Value value, ExecutionContext *ctx); +Value __qmljs_to_primitive(const Value &value, ExecutionContext *ctx, int typeHint); +Bool __qmljs_to_boolean(const Value &value, ExecutionContext *ctx); +double __qmljs_to_number(const Value &value, ExecutionContext *ctx); +double __qmljs_to_integer(const Value &value, ExecutionContext *ctx); +int __qmljs_to_int32(const Value &value, ExecutionContext *ctx); +unsigned short __qmljs_to_uint16(const Value &value, ExecutionContext *ctx); +Value __qmljs_to_string(const Value &value, ExecutionContext *ctx); +Value __qmljs_to_object(const Value &value, ExecutionContext *ctx); //uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *value); -Bool __qmljs_is_callable(Value value, ExecutionContext *ctx); -Value __qmljs_default_value(Value value, ExecutionContext *ctx, int typeHint); +Bool __qmljs_is_callable(const Value &value, ExecutionContext *ctx); +Value __qmljs_default_value(const Value &value, ExecutionContext *ctx, int typeHint); -Bool __qmljs_equal(Value x, Value y, ExecutionContext *ctx); -Bool __qmljs_strict_equal(Value x, Value y); +Bool __qmljs_equal(const Value &x, const Value &y, ExecutionContext *ctx); +Bool __qmljs_strict_equal(const Value &x, const Value &y); // unary operators -Value __qmljs_uplus(Value value, ExecutionContext *ctx); -Value __qmljs_uminus(Value value, ExecutionContext *ctx); -Value __qmljs_compl(Value value, ExecutionContext *ctx); -Value __qmljs_not(Value value, ExecutionContext *ctx); -Value __qmljs_increment(Value value, ExecutionContext *ctx); -Value __qmljs_decrement(Value value, ExecutionContext *ctx); - -Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index); -Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name); +typedef Value (*UnaryOpName)(const Value &, ExecutionContext* ctx); +Value __qmljs_uplus(const Value &value, ExecutionContext *ctx); +Value __qmljs_uminus(const Value &value, ExecutionContext *ctx); +Value __qmljs_compl(const Value &value, ExecutionContext *ctx); +Value __qmljs_not(const Value &value, ExecutionContext *ctx); +Value __qmljs_increment(const Value &value, ExecutionContext *ctx); +Value __qmljs_decrement(const Value &value, ExecutionContext *ctx); + +Value __qmljs_delete_subscript(ExecutionContext *ctx, const Value &base, Value index); +Value __qmljs_delete_member(ExecutionContext *ctx, const Value &base, String *name); Value __qmljs_delete_name(ExecutionContext *ctx, String *name); void __qmljs_throw(Value value, ExecutionContext *context); @@ -219,93 +220,93 @@ void __qmljs_delete_exception_handler(ExecutionContext *context); Value __qmljs_get_exception(ExecutionContext *context); // binary operators -typedef void (*BinOp)(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); - -void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_in(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_bit_or(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_bit_xor(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_bit_and(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_add(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_sub(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_mul(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_div(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_mod(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_shl(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_shr(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_ushr(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_gt(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_lt(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_ge(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_le(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_eq(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_ne(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_se(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); -void __qmljs_sne(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); - -void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value *left, const Value *right); - - -typedef void (*InplaceBinOpName)(ExecutionContext *ctx, String *name, const Value *value); -void __qmljs_inplace_bit_and_name(ExecutionContext *ctx, String *name, const Value *value); -void __qmljs_inplace_bit_or_name(ExecutionContext *ctx, String *name, const Value *value); -void __qmljs_inplace_bit_xor_name(ExecutionContext *ctx, String *name, const Value *value); -void __qmljs_inplace_add_name(ExecutionContext *ctx, String *name, const Value *value); -void __qmljs_inplace_sub_name(ExecutionContext *ctx, String *name, const Value *value); -void __qmljs_inplace_mul_name(ExecutionContext *ctx, String *name, const Value *value); -void __qmljs_inplace_div_name(ExecutionContext *ctx, String *name, const Value *value); -void __qmljs_inplace_mod_name(ExecutionContext *ctx, String *name, const Value *value); -void __qmljs_inplace_shl_name(ExecutionContext *ctx, String *name, const Value *value); -void __qmljs_inplace_shr_name(ExecutionContext *ctx, String *name, const Value *value); -void __qmljs_inplace_ushr_name(ExecutionContext *ctx, String *name, const Value *value); - -typedef void (*InplaceBinOpElement)(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); -void __qmljs_inplace_bit_and_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); -void __qmljs_inplace_bit_or_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); -void __qmljs_inplace_bit_xor_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); -void __qmljs_inplace_add_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); -void __qmljs_inplace_sub_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); -void __qmljs_inplace_mul_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); -void __qmljs_inplace_div_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); -void __qmljs_inplace_mod_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); -void __qmljs_inplace_shl_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); -void __qmljs_inplace_shr_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); -void __qmljs_inplace_ushr_element(ExecutionContext *ctx, const Value *base, const Value *index, const Value *rhs); - -typedef void (*InplaceBinOpMember)(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); -void __qmljs_inplace_bit_and_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); -void __qmljs_inplace_bit_or_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); -void __qmljs_inplace_bit_xor_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); -void __qmljs_inplace_add_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); -void __qmljs_inplace_sub_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); -void __qmljs_inplace_mul_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); -void __qmljs_inplace_div_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); -void __qmljs_inplace_mod_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); -void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); -void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); -void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value *base, String *name, const Value *rhs); - -typedef Bool (*CmpOp)(ExecutionContext *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_gt(ExecutionContext *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_lt(ExecutionContext *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_ge(ExecutionContext *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_le(ExecutionContext *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_eq(ExecutionContext *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_ne(ExecutionContext *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_se(ExecutionContext *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_sne(ExecutionContext *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_instanceof(ExecutionContext *ctx, const Value *left, const Value *right); -Bool __qmljs_cmp_in(ExecutionContext *ctx, const Value *left, const Value *right); +typedef void (*BinOp)(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); + +void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_in(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_bit_or(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_bit_xor(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_bit_and(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_add(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_sub(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_mul(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_div(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_mod(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_shl(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_shr(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_ushr(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_gt(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_lt(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_ge(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_le(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_eq(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_ne(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_se(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_sne(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); + +void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); + + +typedef void (*InplaceBinOpName)(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_bit_and_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_bit_or_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_bit_xor_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_add_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_sub_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_mul_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_div_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_mod_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_shl_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_shr_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_ushr_name(ExecutionContext *ctx, String *name, const Value &value); + +typedef void (*InplaceBinOpElement)(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_bit_and_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_bit_or_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_bit_xor_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_add_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_sub_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_mul_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_div_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_mod_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_shl_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_shr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_ushr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); + +typedef void (*InplaceBinOpMember)(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_bit_and_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_bit_or_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_bit_xor_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_add_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_sub_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_mul_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_div_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_mod_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); + +typedef Bool (*CmpOp)(ExecutionContext *ctx, const Value &left, const Value &right); +Bool __qmljs_cmp_gt(ExecutionContext *ctx, const Value &left, const Value &right); +Bool __qmljs_cmp_lt(ExecutionContext *ctx, const Value &left, const Value &right); +Bool __qmljs_cmp_ge(ExecutionContext *ctx, const Value &left, const Value &right); +Bool __qmljs_cmp_le(ExecutionContext *ctx, const Value &left, const Value &right); +Bool __qmljs_cmp_eq(ExecutionContext *ctx, const Value &left, const Value &right); +Bool __qmljs_cmp_ne(ExecutionContext *ctx, const Value &left, const Value &right); +Bool __qmljs_cmp_se(ExecutionContext *ctx, const Value &left, const Value &right); +Bool __qmljs_cmp_sne(ExecutionContext *ctx, const Value &left, const Value &right); +Bool __qmljs_cmp_instanceof(ExecutionContext *ctx, const Value &left, const Value &right); +Bool __qmljs_cmp_in(ExecutionContext *ctx, const Value &left, const Value &right); // type conversion and testing -inline Value __qmljs_to_primitive(Value value, ExecutionContext *ctx, int typeHint) +inline Value __qmljs_to_primitive(const Value &value, ExecutionContext *ctx, int typeHint) { if (!value.isObject()) return value; return __qmljs_default_value(value, ctx, typeHint); } -inline Bool __qmljs_to_boolean(Value value, ExecutionContext *ctx) +inline Bool __qmljs_to_boolean(const Value &value, ExecutionContext *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -325,7 +326,7 @@ inline Bool __qmljs_to_boolean(Value value, ExecutionContext *ctx) } } -inline double __qmljs_to_number(Value value, ExecutionContext *ctx) +inline double __qmljs_to_number(const Value &value, ExecutionContext *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -347,7 +348,7 @@ inline double __qmljs_to_number(Value value, ExecutionContext *ctx) } } -inline double __qmljs_to_integer(Value value, ExecutionContext *ctx) +inline double __qmljs_to_integer(const Value &value, ExecutionContext *ctx) { if (value.isConvertibleToInt()) return value.int_32; @@ -355,7 +356,7 @@ inline double __qmljs_to_integer(Value value, ExecutionContext *ctx) return Value::toInteger(__qmljs_to_number(value, ctx)); } -inline int __qmljs_to_int32(Value value, ExecutionContext *ctx) +inline int __qmljs_to_int32(const Value &value, ExecutionContext *ctx) { if (value.isConvertibleToInt()) return value.int_32; @@ -363,7 +364,7 @@ inline int __qmljs_to_int32(Value value, ExecutionContext *ctx) return Value::toInt32(__qmljs_to_number(value, ctx)); } -inline unsigned short __qmljs_to_uint16(Value value, ExecutionContext *ctx) +inline unsigned short __qmljs_to_uint16(const Value &value, ExecutionContext *ctx) { if (value.isConvertibleToInt()) return (ushort)(uint)value.integerValue(); @@ -389,7 +390,7 @@ inline unsigned short __qmljs_to_uint16(Value value, ExecutionContext *ctx) return (unsigned short)number; } -inline Value __qmljs_to_string(Value value, ExecutionContext *ctx) +inline Value __qmljs_to_string(const Value &value, ExecutionContext *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -425,7 +426,7 @@ inline Value __qmljs_to_string(Value value, ExecutionContext *ctx) } // switch } -inline Value __qmljs_to_object(Value value, ExecutionContext *ctx) +inline Value __qmljs_to_object(const Value &value, ExecutionContext *ctx) { switch (value.type()) { case Value::Undefined_Type: @@ -464,7 +465,7 @@ inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *v } */ -inline Bool __qmljs_is_callable(Value value, ExecutionContext * /*ctx*/) +inline Bool __qmljs_is_callable(const Value &value, ExecutionContext * /*ctx*/) { if (value.isObject()) return __qmljs_is_function(value); @@ -472,7 +473,7 @@ inline Bool __qmljs_is_callable(Value value, ExecutionContext * /*ctx*/) return false; } -inline Value __qmljs_default_value(Value value, ExecutionContext *ctx, int typeHint) +inline Value __qmljs_default_value(const Value &value, ExecutionContext *ctx, int typeHint) { if (value.isObject()) return __qmljs_object_default_value(ctx, value, typeHint); @@ -480,18 +481,19 @@ inline Value __qmljs_default_value(Value value, ExecutionContext *ctx, int typeH } -inline Value __qmljs_uplus(Value value, ExecutionContext *ctx) +inline Value __qmljs_uplus(const Value &value, ExecutionContext *ctx) { TRACE1(value); - if (value.tryIntegerConversion()) - return value; + Value copy = value; + if (copy.tryIntegerConversion()) + return copy; double n = __qmljs_to_number(value, ctx); return Value::fromDouble(n); } -inline Value __qmljs_uminus(Value value, ExecutionContext *ctx) +inline Value __qmljs_uminus(const Value &value, ExecutionContext *ctx) { TRACE1(value); @@ -502,7 +504,7 @@ inline Value __qmljs_uminus(Value value, ExecutionContext *ctx) return Value::fromDouble(-n); } -inline Value __qmljs_compl(Value value, ExecutionContext *ctx) +inline Value __qmljs_compl(const Value &value, ExecutionContext *ctx) { TRACE1(value); @@ -515,7 +517,7 @@ inline Value __qmljs_compl(Value value, ExecutionContext *ctx) return Value::fromInt32(~n); } -inline Value __qmljs_not(Value value, ExecutionContext *ctx) +inline Value __qmljs_not(const Value &value, ExecutionContext *ctx) { TRACE1(value); @@ -524,160 +526,160 @@ inline Value __qmljs_not(Value value, ExecutionContext *ctx) } // binary operators -inline void __qmljs_bit_or(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_bit_or(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); - if (Value::integerCompatible(*left, *right)) { - *result = Value::fromInt32(left->integerValue() | right->integerValue()); + if (Value::integerCompatible(left, right)) { + *result = Value::fromInt32(left.integerValue() | right.integerValue()); return; } - int lval = Value::toInt32(__qmljs_to_number(*left, ctx)); - int rval = Value::toInt32(__qmljs_to_number(*right, ctx)); + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + int rval = Value::toInt32(__qmljs_to_number(right, ctx)); *result = Value::fromInt32(lval | rval); } -inline void __qmljs_bit_xor(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_bit_xor(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); - if (Value::integerCompatible(*left, *right)) { - *result = Value::fromInt32(left->integerValue() ^ right->integerValue()); + if (Value::integerCompatible(left, right)) { + *result = Value::fromInt32(left.integerValue() ^ right.integerValue()); return; } - int lval = Value::toInt32(__qmljs_to_number(*left, ctx)); - int rval = Value::toInt32(__qmljs_to_number(*right, ctx)); + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + int rval = Value::toInt32(__qmljs_to_number(right, ctx)); *result = Value::fromInt32(lval ^ rval); } -inline void __qmljs_bit_and(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_bit_and(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); - if (Value::integerCompatible(*left, *right)) { - *result = Value::fromInt32(left->integerValue() & right->integerValue()); + if (Value::integerCompatible(left, right)) { + *result = Value::fromInt32(left.integerValue() & right.integerValue()); return; } - int lval = Value::toInt32(__qmljs_to_number(*left, ctx)); - int rval = Value::toInt32(__qmljs_to_number(*right, ctx)); + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + int rval = Value::toInt32(__qmljs_to_number(right, ctx)); *result = Value::fromInt32(lval & rval); } -inline void __qmljs_add(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_add(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); #ifdef QMLJS_INLINE_MATH - if (Value::integerCompatible(*left, *right)) { - *result = add_int32(left->integerValue(), right->integerValue()); + if (Value::integerCompatible(left, right)) { + *result = add_int32(left.integerValue(), right.integerValue()); return; } #endif - if (Value::bothDouble(*left, *right)) { - *result = Value::fromDouble(left->doubleValue() + right->doubleValue()); + if (Value::bothDouble(left, right)) { + *result = Value::fromDouble(left.doubleValue() + right.doubleValue()); return; } __qmljs_add_helper(ctx, result, left, right); } -inline void __qmljs_sub(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_sub(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); #ifdef QMLJS_INLINE_MATH - if (Value::integerCompatible(*left, *right)) { - *result = sub_int32(left->integerValue(), right->integerValue()); + if (Value::integerCompatible(left, right)) { + *result = sub_int32(left.integerValue(), right.integerValue()); return; } #endif - double lval = __qmljs_to_number(*left, ctx); - double rval = __qmljs_to_number(*right, ctx); + double lval = __qmljs_to_number(left, ctx); + double rval = __qmljs_to_number(right, ctx); *result = Value::fromDouble(lval - rval); } -inline void __qmljs_mul(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_mul(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); #ifdef QMLJS_INLINE_MATH - if (Value::integerCompatible(*left, *right)) { - *result = mul_int32(left->integerValue(), right->integerValue()); + if (Value::integerCompatible(left, right)) { + *result = mul_int32(left.integerValue(), right.integerValue()); return; } #endif - double lval = __qmljs_to_number(*left, ctx); - double rval = __qmljs_to_number(*right, ctx); + double lval = __qmljs_to_number(left, ctx); + double rval = __qmljs_to_number(right, ctx); *result = Value::fromDouble(lval * rval); } -inline void __qmljs_div(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_div(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); - double lval = __qmljs_to_number(*left, ctx); - double rval = __qmljs_to_number(*right, ctx); + double lval = __qmljs_to_number(left, ctx); + double rval = __qmljs_to_number(right, ctx); *result = Value::fromDouble(lval / rval); } -inline void __qmljs_mod(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_mod(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); - if (Value::integerCompatible(*left, *right) && right->integerValue() != 0) { - *result = Value::fromInt32(left->integerValue() % right->integerValue()); + if (Value::integerCompatible(left, right) && right.integerValue() != 0) { + *result = Value::fromInt32(left.integerValue() % right.integerValue()); return; } - double lval = __qmljs_to_number(*left, ctx); - double rval = __qmljs_to_number(*right, ctx); + double lval = __qmljs_to_number(left, ctx); + double rval = __qmljs_to_number(right, ctx); *result = Value::fromDouble(fmod(lval, rval)); } -inline void __qmljs_shl(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_shl(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); - if (Value::integerCompatible(*left, *right)) { - *result = Value::fromInt32(left->integerValue() << ((uint(right->integerValue()) & 0x1f))); + if (Value::integerCompatible(left, right)) { + *result = Value::fromInt32(left.integerValue() << ((uint(right.integerValue()) & 0x1f))); return; } - int lval = Value::toInt32(__qmljs_to_number(*left, ctx)); - unsigned rval = Value::toUInt32(__qmljs_to_number(*right, ctx)) & 0x1f; + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; *result = Value::fromInt32(lval << rval); } -inline void __qmljs_shr(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_shr(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); - if (Value::integerCompatible(*left, *right)) { - *result = Value::fromInt32(left->integerValue() >> ((uint(right->integerValue()) & 0x1f))); + if (Value::integerCompatible(left, right)) { + *result = Value::fromInt32(left.integerValue() >> ((uint(right.integerValue()) & 0x1f))); return; } - int lval = Value::toInt32(__qmljs_to_number(*left, ctx)); - unsigned rval = Value::toUInt32(__qmljs_to_number(*right, ctx)) & 0x1f; + int lval = Value::toInt32(__qmljs_to_number(left, ctx)); + unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; *result = Value::fromInt32(lval >> rval); } -inline void __qmljs_ushr(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_ushr(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); uint res; - if (Value::integerCompatible(*left, *right)) { - res = uint(left->integerValue()) >> (uint(right->integerValue()) & 0x1f); + if (Value::integerCompatible(left, right)) { + res = uint(left.integerValue()) >> (uint(right.integerValue()) & 0x1f); } else { - unsigned lval = Value::toUInt32(__qmljs_to_number(*left, ctx)); - unsigned rval = Value::toUInt32(__qmljs_to_number(*right, ctx)) & 0x1f; + unsigned lval = Value::toUInt32(__qmljs_to_number(left, ctx)); + unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f; res = lval >> rval; } @@ -687,70 +689,70 @@ inline void __qmljs_ushr(ExecutionContext *ctx, Value *result, const Value *left *result = Value::fromInt32(res); } -inline void __qmljs_gt(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_gt(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); *result = Value::fromBoolean(__qmljs_cmp_gt(ctx, left, right)); } -inline void __qmljs_lt(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_lt(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); *result = Value::fromBoolean(__qmljs_cmp_lt(ctx, left, right)); } -inline void __qmljs_ge(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_ge(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); *result = Value::fromBoolean(__qmljs_cmp_ge(ctx, left, right)); } -inline void __qmljs_le(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_le(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); *result = Value::fromBoolean(__qmljs_cmp_le(ctx, left, right)); } -inline void __qmljs_eq(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_eq(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); *result = Value::fromBoolean(__qmljs_cmp_eq(ctx, left, right)); } -inline void __qmljs_ne(ExecutionContext *ctx, Value *result, const Value *left, const Value *right) +inline void __qmljs_ne(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { TRACE2(left, right); *result = Value::fromBoolean(!__qmljs_cmp_eq(ctx, left, right)); } -inline void __qmljs_se(ExecutionContext *, Value *result, const Value *left, const Value *right) +inline void __qmljs_se(ExecutionContext *, Value *result, const Value &left, const Value &right) { TRACE2(left, right); - bool r = __qmljs_strict_equal(*left, *right); + bool r = __qmljs_strict_equal(left, right); *result = Value::fromBoolean(r); } -inline void __qmljs_sne(ExecutionContext *, Value *result, const Value *left, const Value *right) +inline void __qmljs_sne(ExecutionContext *, Value *result, const Value &left, const Value &right) { TRACE2(left, right); - bool r = ! __qmljs_strict_equal(*left, *right); + bool r = ! __qmljs_strict_equal(left, right); *result = Value::fromBoolean(r); } -inline Bool __qmljs_cmp_gt(ExecutionContext *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_gt(ExecutionContext *ctx, const Value &left, const Value &right) { TRACE2(left, right); - Value l = __qmljs_to_primitive(*left, ctx, NUMBER_HINT); - Value r = __qmljs_to_primitive(*right, ctx, NUMBER_HINT); + Value l = __qmljs_to_primitive(left, ctx, NUMBER_HINT); + Value r = __qmljs_to_primitive(right, ctx, NUMBER_HINT); if (Value::integerCompatible(l, r)) return l.integerValue() > r.integerValue(); @@ -765,12 +767,12 @@ inline Bool __qmljs_cmp_gt(ExecutionContext *ctx, const Value *left, const Value } } -inline Bool __qmljs_cmp_lt(ExecutionContext *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_lt(ExecutionContext *ctx, const Value &left, const Value &right) { TRACE2(left, right); - Value l = __qmljs_to_primitive(*left, ctx, NUMBER_HINT); - Value r = __qmljs_to_primitive(*right, ctx, NUMBER_HINT); + Value l = __qmljs_to_primitive(left, ctx, NUMBER_HINT); + Value r = __qmljs_to_primitive(right, ctx, NUMBER_HINT); if (Value::integerCompatible(l, r)) return l.integerValue() < r.integerValue(); @@ -785,12 +787,12 @@ inline Bool __qmljs_cmp_lt(ExecutionContext *ctx, const Value *left, const Value } } -inline Bool __qmljs_cmp_ge(ExecutionContext *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_ge(ExecutionContext *ctx, const Value &left, const Value &right) { TRACE2(left, right); - Value l = __qmljs_to_primitive(*left, ctx, NUMBER_HINT); - Value r = __qmljs_to_primitive(*right, ctx, NUMBER_HINT); + Value l = __qmljs_to_primitive(left, ctx, NUMBER_HINT); + Value r = __qmljs_to_primitive(right, ctx, NUMBER_HINT); if (Value::integerCompatible(l, r)) return l.integerValue() >= r.integerValue(); @@ -805,12 +807,12 @@ inline Bool __qmljs_cmp_ge(ExecutionContext *ctx, const Value *left, const Value } } -inline Bool __qmljs_cmp_le(ExecutionContext *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_le(ExecutionContext *ctx, const Value &left, const Value &right) { TRACE2(left, right); - Value l = __qmljs_to_primitive(*left, ctx, NUMBER_HINT); - Value r = __qmljs_to_primitive(*right, ctx, NUMBER_HINT); + Value l = __qmljs_to_primitive(left, ctx, NUMBER_HINT); + Value r = __qmljs_to_primitive(right, ctx, NUMBER_HINT); if (Value::integerCompatible(l, r)) return l.integerValue() <= r.integerValue(); @@ -825,43 +827,43 @@ inline Bool __qmljs_cmp_le(ExecutionContext *ctx, const Value *left, const Value } } -inline Bool __qmljs_cmp_eq(ExecutionContext *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_eq(ExecutionContext *ctx, const Value &left, const Value &right) { TRACE2(left, right); // need to test for doubles first as NaN != NaN - if (Value::bothDouble(*left, *right)) - return left->doubleValue() == right->doubleValue(); - if (left->val == right->val) + if (Value::bothDouble(left, right)) + return left.doubleValue() == right.doubleValue(); + if (left.val == right.val) return true; - if (left->isString() && right->isString()) - return __qmljs_string_equal(left->stringValue(), right->stringValue()); + if (left.isString() && right.isString()) + return __qmljs_string_equal(left.stringValue(), right.stringValue()); - return __qmljs_equal(*left, *right, ctx); + return __qmljs_equal(left, right, ctx); } -inline Bool __qmljs_cmp_ne(ExecutionContext *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_ne(ExecutionContext *ctx, const Value &left, const Value &right) { TRACE2(left, right); return !__qmljs_cmp_eq(ctx, left, right); } -inline Bool __qmljs_cmp_se(ExecutionContext *, const Value *left, const Value *right) +inline Bool __qmljs_cmp_se(ExecutionContext *, const Value &left, const Value &right) { TRACE2(left, right); - return __qmljs_strict_equal(*left, *right); + return __qmljs_strict_equal(left, right); } -inline Bool __qmljs_cmp_sne(ExecutionContext *, const Value *left, const Value *right) +inline Bool __qmljs_cmp_sne(ExecutionContext *, const Value &left, const Value &right) { TRACE2(left, right); - return ! __qmljs_strict_equal(*left, *right); + return ! __qmljs_strict_equal(left, right); } -inline Bool __qmljs_cmp_instanceof(ExecutionContext *ctx, const Value *left, const Value *right) +inline Bool __qmljs_cmp_instanceof(ExecutionContext *ctx, const Value &left, const Value &right) { TRACE2(left, right); @@ -870,7 +872,7 @@ inline Bool __qmljs_cmp_instanceof(ExecutionContext *ctx, const Value *left, con return v.booleanValue(); } -inline uint __qmljs_cmp_in(ExecutionContext *ctx, const Value *left, const Value *right) +inline uint __qmljs_cmp_in(ExecutionContext *ctx, const Value &left, const Value &right) { TRACE2(left, right); @@ -879,7 +881,7 @@ inline uint __qmljs_cmp_in(ExecutionContext *ctx, const Value *left, const Value return v.booleanValue(); } -inline Bool __qmljs_strict_equal(Value x, Value y) +inline Bool __qmljs_strict_equal(const Value &x, const Value &y) { TRACE2(x, y); diff --git a/src/v4/qmljs_value.h b/src/v4/qmljs_value.h index d33d5ed9c5..7c83af593e 100644 --- a/src/v4/qmljs_value.h +++ b/src/v4/qmljs_value.h @@ -59,7 +59,7 @@ struct ExecutionEngine; struct Value; extern "C" { -double __qmljs_to_number(Value value, ExecutionContext *ctx); +double __qmljs_to_number(const Value &value, ExecutionContext *ctx); } typedef uint Bool; diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 0e8c1e4d94..2476d5c398 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -103,11 +103,11 @@ FunctionObject::FunctionObject(ExecutionContext *scope) strictMode = false; } -bool FunctionObject::hasInstance(Managed *that, ExecutionContext *ctx, const Value *value) +bool FunctionObject::hasInstance(Managed *that, ExecutionContext *ctx, const Value &value) { FunctionObject *f = static_cast(that); - Object *v = value->asObject(); + Object *v = value.asObject(); if (!v) return false; @@ -514,7 +514,7 @@ Value BoundFunction::construct(ExecutionContext *context, Value *args, int argc) return target->construct(context, newArgs, boundArgs.size() + argc); } -bool BoundFunction::hasInstance(Managed *that, ExecutionContext *ctx, const Value *value) +bool BoundFunction::hasInstance(Managed *that, ExecutionContext *ctx, const Value &value) { BoundFunction *f = static_cast(that); return FunctionObject::hasInstance(f->target, ctx, value); diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index 7cf73e8db0..ae8bd531a5 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -164,7 +164,7 @@ struct Q_V4_EXPORT FunctionObject: Object { protected: static const ManagedVTable static_vtbl; static void markObjects(Managed *that); - static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value *value); + static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value &value); }; struct FunctionCtor: FunctionObject @@ -227,7 +227,7 @@ struct BoundFunction: FunctionObject { static const ManagedVTable static_vtbl; static void markObjects(Managed *that); - static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value *value); + static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value &value); }; } // namespace VM diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 6cc798a9bf..467cc3e35e 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -500,12 +500,12 @@ void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *res void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) { - generateFunctionCall(result, __qmljs_delete_member, Assembler::ContextRegister, base, identifier(name)); + generateFunctionCall(result, __qmljs_delete_member, Assembler::ContextRegister, Assembler::PointerToValue(base), identifier(name)); } void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) { - generateFunctionCall(result, __qmljs_delete_subscript, Assembler::ContextRegister, base, index); + generateFunctionCall(result, __qmljs_delete_subscript, Assembler::ContextRegister, Assembler::PointerToValue(base), index); } void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) @@ -739,7 +739,7 @@ void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) { - Value (*op)(const Value value, ExecutionContext *ctx) = 0; + Value (*op)(const Value& value, ExecutionContext *ctx) = 0; const char *opName = 0; switch (oper) { case IR::OpIfTrue: assert(!"unreachable"); break; @@ -753,7 +753,7 @@ void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp * } // switch if (op) - _as->generateFunctionCallImp(targetTemp, opName, op, sourceTemp, + _as->generateFunctionCallImp(targetTemp, opName, op, Assembler::PointerToValue(sourceTemp), Assembler::ContextRegister); } @@ -925,7 +925,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) booleanConversion.link(_as); { - generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, t, Assembler::ContextRegister); + generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, Assembler::PointerToValue(t), Assembler::ContextRegister); } testBoolean.link(_as); diff --git a/src/v4/qv4managed.cpp b/src/v4/qv4managed.cpp index c5935fcdc2..fe71be407f 100644 --- a/src/v4/qv4managed.cpp +++ b/src/v4/qv4managed.cpp @@ -147,7 +147,7 @@ QString Managed::className() const return QString::fromLatin1(s); } -bool Managed::hasInstance(Managed *, ExecutionContext *ctx, const Value *) +bool Managed::hasInstance(Managed *, ExecutionContext *ctx, const Value &) { ctx->throwTypeError(); } diff --git a/src/v4/qv4managed.h b/src/v4/qv4managed.h index 81c6cb50bc..979b308fb7 100644 --- a/src/v4/qv4managed.h +++ b/src/v4/qv4managed.h @@ -81,7 +81,7 @@ struct ManagedVTable EndOfVTable }; void (*markObjects)(Managed *); - bool (*hasInstance)(Managed *, ExecutionContext *ctx, const Value *value); + bool (*hasInstance)(Managed *, ExecutionContext *ctx, const Value &value); _EndOfVTable endofVTable; }; @@ -157,11 +157,11 @@ public: *reinterpret_cast(this) = m; } - inline bool hasInstance(ExecutionContext *ctx, const Value *v) { + inline bool hasInstance(ExecutionContext *ctx, const Value &v) { return vtbl->hasInstance(this, ctx, v); } - static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value *value); + static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value &value); protected: diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index b440cfbc5d..46debdb2e5 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -114,7 +114,7 @@ Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p return getValue(ctx, p); } -void Object::putValue(ExecutionContext *ctx, PropertyDescriptor *pd, Value value) +void Object::putValue(ExecutionContext *ctx, PropertyDescriptor *pd, const Value &value) { if (pd->isAccessor()) { if (pd->set) { @@ -138,27 +138,27 @@ void Object::putValue(ExecutionContext *ctx, PropertyDescriptor *pd, Value value } -void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, String *name, const Value *rhs) +void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, String *name, const Value &rhs) { bool hasProperty = false; Value v = __get__(ctx, name, &hasProperty); Value result; - op(ctx, &result, &v, rhs); + op(ctx, &result, v, rhs); __put__(ctx, name, result); } -void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value *index, const Value *rhs) +void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, const Value &rhs) { - uint idx = index->asArrayIndex(); + uint idx = index.asArrayIndex(); if (idx < UINT_MAX) { bool hasProperty = false; Value v = __get__(ctx, idx, &hasProperty); Value result; - op(ctx, &result, &v, rhs); + op(ctx, &result, v, rhs); __put__(ctx, idx, result); return; } - String *name = index->toString(ctx); + String *name = index.toString(ctx); assert(name); inplaceBinOp(ctx, op, name, rhs); } @@ -372,7 +372,7 @@ Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) // Section 8.12.5 -void Object::__put__(ExecutionContext *ctx, String *name, Value value) +void Object::__put__(ExecutionContext *ctx, String *name, const Value &value) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) @@ -453,7 +453,7 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) __qmljs_throw_type_error(ctx); } -void Object::__put__(ExecutionContext *ctx, uint index, Value value) +void Object::__put__(ExecutionContext *ctx, uint index, const Value &value) { PropertyDescriptor *pd = __getOwnProperty__(ctx, index); // clause 1 diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index 593e4e7609..1b29dafd15 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -127,8 +127,8 @@ struct Q_V4_EXPORT Object: Managed { Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); - void __put__(ExecutionContext *ctx, String *name, Value value); - void __put__(ExecutionContext *ctx, uint index, Value value); + void __put__(ExecutionContext *ctx, String *name, const Value &value); + void __put__(ExecutionContext *ctx, uint index, const Value &value); bool __hasProperty__(const ExecutionContext *ctx, String *name) const; bool __hasProperty__(const ExecutionContext *ctx, uint index) const; @@ -148,10 +148,10 @@ struct Q_V4_EXPORT Object: Managed { Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const; Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const; - void putValue(ExecutionContext *ctx, PropertyDescriptor *pd, Value value); + void putValue(ExecutionContext *ctx, PropertyDescriptor *pd, const Value &value); - void inplaceBinOp(ExecutionContext *ctx, BinOp op, String *name, const Value *rhs); - void inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value *index, const Value *rhs); + void inplaceBinOp(ExecutionContext *ctx, BinOp op, String *name, const Value &rhs); + void inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, const Value &rhs); /* The spec default: Writable: true, Enumerable: false, Configurable: true */ void defineDefaultProperty(String *name, Value value); -- cgit v1.2.3 From 2008f2bd376556a328eb3778198d1cfbc5d09466 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Feb 2013 12:16:30 +0100 Subject: Convert builtin typeof to the new calling convention Change-Id: I40f268c53dacf2ee188b3d1df9391df3e5e812f8 Reviewed-by: Lars Knoll --- src/v4/moth/qv4vme_moth.cpp | 2 +- src/v4/qmljs_runtime.cpp | 5 +++-- src/v4/qmljs_runtime.h | 2 +- src/v4/qv4isel_masm.cpp | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index cd6264a0a7..e366ec33bf 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -312,7 +312,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinDeleteName) MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) - VALUE(instr.result) = __qmljs_builtin_typeof_member(VALUE(instr.base), instr.member, context); + __qmljs_builtin_typeof_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); MOTH_END_INSTR(CallBuiltinTypeofMember) MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 520c1e3ce3..58ff36abf3 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -1028,10 +1028,11 @@ Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context) return __qmljs_builtin_typeof(context->getPropertyNoThrow(name), context); } -Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext *context) +void __qmljs_builtin_typeof_member(ExecutionContext *context, Value *result, const Value &base, String *name) { Value obj = base.toObject(context); - return __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context); + if (result) + *result = __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context); } Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 4c507012c5..0a08b71e02 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -103,7 +103,7 @@ Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args Value __qmljs_builtin_typeof(Value val, ExecutionContext *ctx); Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context); -Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext *context); +void __qmljs_builtin_typeof_member(ExecutionContext* context, Value* result, const Value &base, String *name); Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context); void __qmljs_builtin_post_increment(ExecutionContext *ctx, Value *result, Value *val); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 467cc3e35e..1c1438494e 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -480,7 +480,7 @@ void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) { - generateFunctionCall(result, __qmljs_builtin_typeof_member, base, identifier(name), Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_member, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name)); } void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) -- cgit v1.2.3 From 63cd0f745bb5a3baf47046b8453159cf34b0a23a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Feb 2013 12:29:16 +0100 Subject: Fix print and gc in builtin v4 method Also removed unused TestHarness stuff. Change-Id: I6332d9a34f471df07cfa0e9709a203e99a48b524 Reviewed-by: Lars Knoll --- tools/v4/main.cpp | 38 ++++---------------------------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/tools/v4/main.cpp b/tools/v4/main.cpp index 01d4f8fdcc..7a6e7361b2 100644 --- a/tools/v4/main.cpp +++ b/tools/v4/main.cpp @@ -77,10 +77,10 @@ struct Print: FunctionObject name = scope->engine->newString("print"); } - virtual Value call(ExecutionContext *ctx) + virtual Value call(ExecutionContext *ctx, Value, Value *args, int argc) { - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { - String *s = ctx->argument(i).toString(ctx); + for (int i = 0; i < argc; ++i) { + String *s = args[i].toString(ctx); if (i) std::cout << ' '; std::cout << qPrintable(s->toQString()); @@ -90,29 +90,6 @@ struct Print: FunctionObject } }; -struct TestHarnessError: FunctionObject -{ - TestHarnessError(ExecutionContext *scope, bool &errorInTestHarness): FunctionObject(scope), errorOccurred(errorInTestHarness) { - name = scope->engine->newString("$ERROR"); - } - - virtual Value call(ExecutionContext *ctx) - { - errorOccurred = true; - - for (unsigned int i = 0; i < ctx->argumentCount; ++i) { - String *s = ctx->argument(i).toString(ctx); - if (i) - std::cerr << ' '; - std::cerr << qPrintable(s->toQString()); - } - std::cerr << std::endl; - return Value::undefinedValue(); - } - - bool &errorOccurred; -}; - struct GC: public FunctionObject { GC(ExecutionContext* scope) @@ -120,7 +97,7 @@ struct GC: public FunctionObject { name = scope->engine->newString("gc"); } - virtual Value call(ExecutionContext *ctx) + virtual Value call(ExecutionContext *ctx, Value, Value *, int) { ctx->engine->memoryManager->runGC(); return Value::undefinedValue(); @@ -376,11 +353,6 @@ int main(int argc, char *argv[]) globalObject->__put__(ctx, vm.newIdentifier(QStringLiteral("gc")), QQmlJS::VM::Value::fromObject(gc)); - bool errorInTestHarness = false; - if (!qgetenv("IN_TEST_HARNESS").isEmpty()) - globalObject->__put__(ctx, vm.newIdentifier(QStringLiteral("$ERROR")), - QQmlJS::VM::Value::fromObject(new (ctx->engine->memoryManager) builtins::TestHarnessError(ctx, errorInTestHarness))); - foreach (const QString &fn, args) { QFile file(fn); if (file.open(QFile::ReadOnly)) { @@ -411,8 +383,6 @@ int main(int argc, char *argv[]) std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; } - if (errorInTestHarness) - return EXIT_FAILURE; } else { std::cerr << "Error: cannot open file " << fn.toUtf8().constData() << std::endl; return EXIT_FAILURE; -- cgit v1.2.3 From 4377d44fb7399751ed4f284bb4be7ece494aff6d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Feb 2013 13:28:49 +0100 Subject: Converted calling runtime functions to new calling convention Change-Id: I03837e9b392957bd64a6710c1b85b4429556ba06 Reviewed-by: Lars Knoll --- .../masm/assembler/MacroAssemblerCodeRef.h | 7 ++++++ src/v4/llvm_runtime.cpp | 5 ++--- src/v4/moth/qv4vme_moth.cpp | 6 ++--- src/v4/qmljs_runtime.cpp | 26 +++++++++++++++------- src/v4/qmljs_runtime.h | 8 +++---- src/v4/qv4isel_masm.cpp | 17 ++++++++------ src/v4/qv4isel_masm_p.h | 13 ++++++++--- src/v4/qv4sparsearray.cpp | 3 ++- 8 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/3rdparty/masm/assembler/MacroAssemblerCodeRef.h b/src/3rdparty/masm/assembler/MacroAssemblerCodeRef.h index c2af24060a..89cffb1278 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerCodeRef.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerCodeRef.h @@ -134,6 +134,13 @@ public: ASSERT_VALID_CODE_POINTER(m_value); } + template + FunctionPtr(returnType(*value)(argType1, argType2, argType3, argType4, argType5, argType6)) + : m_value((void*)value) + { + ASSERT_VALID_CODE_POINTER(m_value); + } + // MSVC doesn't seem to treat functions with different calling conventions as // different types; these methods already defined for fastcall, below. #if CALLING_CONVENTION_IS_STDCALL && !OS(WINDOWS) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 2f6e9f14d4..9be3b6dc35 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -405,8 +405,7 @@ void __qmljs_llvm_call_activation_property(ExecutionContext *context, Value *res void __qmljs_llvm_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) { - Value that = thisObject ? *thisObject : Value::undefinedValue(); - *result = __qmljs_call_value(context, that, *func, args, argc); + __qmljs_call_value(context, result, thisObject, *func, args, argc); } void __qmljs_llvm_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) @@ -436,7 +435,7 @@ void __qmljs_llvm_get_property(ExecutionContext *ctx, Value *result, Value *obje void __qmljs_llvm_call_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) { - *result = __qmljs_call_property(context, *base, name, args, argc); + __qmljs_call_property(context, result, *base, name, args, argc); } void __qmljs_llvm_construct_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index e366ec33bf..f711fd0f68 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -221,20 +221,20 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co #endif // DO_TRACE_INSTR Q_ASSERT(instr.args + instr.argc < stackSize); VM::Value *args = stack + instr.args; - VALUE(instr.result) = __qmljs_call_value(context, VM::Value::undefinedValue(), VALUE(instr.dest), args, instr.argc); + __qmljs_call_value(context, VALUEPTR(instr.result), /*thisObject*/0, VALUE(instr.dest), args, instr.argc); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) TRACE(property name, "%s, args=%u, argc=%u", qPrintable(instr.name->toQString()), instr.args, instr.argc); Q_ASSERT(instr.args + instr.argc < stackSize); VM::Value *args = stack + instr.args; - VALUE(instr.result) = __qmljs_call_property(context, VALUE(instr.base), instr.name, args, instr.argc); + __qmljs_call_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallElement) Q_ASSERT(instr.args + instr.argc < stackSize); VM::Value *args = stack + instr.args; - VALUE(instr.result) = __qmljs_call_element(context, VALUE(instr.base), VALUE(instr.index), args, instr.argc); + __qmljs_call_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index), args, instr.argc); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallActivationProperty) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 58ff36abf3..1976bd2fbd 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -821,8 +821,9 @@ void __qmljs_call_activation_property(ExecutionContext *context, Value *result, *result = res; } -Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String *name, Value *args, int argc) +void __qmljs_call_property(ExecutionContext *context, Value *result, const Value &thatObject, String *name, Value *args, int argc) { + Value thisObject = thatObject; Object *baseObject; if (thisObject.isString()) { baseObject = context->engine->stringPrototype; @@ -839,11 +840,14 @@ Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String if (!o) context->throwTypeError(); - return o->call(context, thisObject, args, argc); + Value res = o->call(context, thisObject, args, argc); + if (result) + *result = res; } -Value __qmljs_call_property_lookup(ExecutionContext *context, Value thisObject, uint index, Value *args, int argc) +void __qmljs_call_property_lookup(ExecutionContext *context, Value *result, const Value &thatObject, uint index, Value *args, int argc) { + Value thisObject = thatObject; Lookup *l = context->lookups + index; Object *baseObject; @@ -894,10 +898,12 @@ Value __qmljs_call_property_lookup(ExecutionContext *context, Value thisObject, if (!o) context->throwTypeError(); - return o->call(context, thisObject, args, argc); + Value res = o->call(context, thisObject, args, argc); + if (result) + *result = res; } -Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc) +void __qmljs_call_element(ExecutionContext *context, Value *result, const Value &that, const Value &index, Value *args, int argc) { Value thisObject = that; if (!thisObject.isObject()) @@ -911,15 +917,19 @@ Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, V if (!o) context->throwTypeError(); - return o->call(context, thisObject, args, argc); + Value res = o->call(context, thisObject, args, argc); + if (result) + *result = res; } -Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc) +void __qmljs_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value &func, Value *args, int argc) { FunctionObject *o = func.asFunctionObject(); if (!o) context->throwTypeError(); - return o->call(context, thisObject, args, argc); + Value res = o->call(context, thisObject ? *thisObject : Value::undefinedValue(), args, argc); + if (result) + *result = res; } void __qmljs_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 0a08b71e02..c0f264370b 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -92,10 +92,10 @@ extern "C" { // context void __qmljs_call_activation_property(ExecutionContext *, Value *result, String *name, Value *args, int argc); -Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, Value *args, int argc); -Value __qmljs_call_property_lookup(ExecutionContext *context, Value thisObject, uint index, Value *args, int argc); -Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc); -Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc); +void __qmljs_call_property(ExecutionContext *context, Value *result, const Value &that, String *name, Value *args, int argc); +void __qmljs_call_property_lookup(ExecutionContext *context, Value *result, const Value &thisObject, uint index, Value *args, int argc); +void __qmljs_call_element(ExecutionContext *context, Value *result, const Value &that, const Value &index, Value *args, int argc); +void __qmljs_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value &func, Value *args, int argc); void __qmljs_construct_activation_property(ExecutionContext *, Value *result, String *name, Value *args, int argc); Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 1c1438494e..4337c72fdd 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -647,7 +647,7 @@ void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Te { int argc = prepareVariableArguments(args); IR::Temp* thisObject = 0; - generateFunctionCall(result, __qmljs_call_value, Assembler::ContextRegister, thisObject, value, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); + generateFunctionCall(Assembler::Void, __qmljs_call_value, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(thisObject), Assembler::PointerToValue(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } void InstructionSelection::loadThisObject(IR::Temp *temp) @@ -854,13 +854,15 @@ void InstructionSelection::callProperty(IR::Temp *base, const QString &name, if (useFastLookups) { uint index = addLookup(s); - generateFunctionCall(result, __qmljs_call_property_lookup, - Assembler::ContextRegister, base, Assembler::TrustedImm32(index), + generateFunctionCall(Assembler::Void, __qmljs_call_property_lookup, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::PointerToValue(base), Assembler::TrustedImm32(index), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } else { - generateFunctionCall(result, __qmljs_call_property, - Assembler::ContextRegister, base, s, + generateFunctionCall(Assembler::Void, __qmljs_call_property, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::PointerToValue(base), s, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } @@ -871,8 +873,9 @@ void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::Ex assert(base != 0); int argc = prepareVariableArguments(args); - generateFunctionCall(result, __qmljs_call_element, - Assembler::ContextRegister, base, index, + generateFunctionCall(Assembler::Void, __qmljs_call_element, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::PointerToValue(base), Assembler::PointerToValue(index), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 75b5debcc7..77d1ce933c 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -461,10 +461,10 @@ public: int currentRegisterIndex; }; - template - void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + template + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) { - int totalNumberOfArgs = 5; + int totalNumberOfArgs = 6; // If necessary reserve space for the return value on the stack and // pass the pointer to it as the first hidden parameter. @@ -477,6 +477,7 @@ public: } ArgumentLoader l(this, totalNumberOfArgs); + l.load(arg6); l.load(arg5); l.load(arg4); l.load(arg3); @@ -502,6 +503,12 @@ public: add32(TrustedImm32(stackSizeToCorrect), StackPointerRegister); } + template + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType()); + } + template void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { diff --git a/src/v4/qv4sparsearray.cpp b/src/v4/qv4sparsearray.cpp index 703669b88f..a94e652a47 100644 --- a/src/v4/qv4sparsearray.cpp +++ b/src/v4/qv4sparsearray.cpp @@ -68,7 +68,8 @@ bool ArrayElementLessThan::operator()(const PropertyDescriptor &p1, const Proper return true; if (!m_comparefn.isUndefined()) { Value args[] = { v1, v2 }; - Value result = __qmljs_call_value(m_context, Value::undefinedValue(), m_comparefn, args, 2); + Value result = Value::undefinedValue(); + __qmljs_call_value(m_context, &result, /*thisObject*/0, m_comparefn, args, 2); return result.toNumber(m_context) <= 0; } return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); -- cgit v1.2.3 From 7fb0347efac6320fcbae121b27618f3347717bb8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Feb 2013 12:56:05 +0100 Subject: Convert builtin type of functions to new calling convention Change-Id: Ie0c9300eead2171c899bca54635e6fdf301385d3 Reviewed-by: Lars Knoll --- src/v4/llvm_runtime.cpp | 2 +- src/v4/moth/qv4vme_moth.cpp | 6 +++--- src/v4/qmljs_runtime.cpp | 30 +++++++++++++++++------------- src/v4/qmljs_runtime.h | 6 +++--- src/v4/qv4isel_masm.cpp | 6 +++--- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 9be3b6dc35..1dc480060d 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -465,7 +465,7 @@ void __qmljs_llvm_builtin_declare_var(ExecutionContext *ctx, bool deletable, Str void __qmljs_llvm_typeof(ExecutionContext *ctx, Value *result, const Value *value) { - *result = __qmljs_builtin_typeof(*value, ctx); + __qmljs_builtin_typeof(ctx, result, *value); } void __qmljs_llvm_throw(ExecutionContext *context, Value *value) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index f711fd0f68..a8d92432b1 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -316,15 +316,15 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinTypeofMember) MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) - VALUE(instr.result) = __qmljs_builtin_typeof_element(VALUE(instr.base), VALUE(instr.index), context); + __qmljs_builtin_typeof_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); MOTH_END_INSTR(CallBuiltinTypeofSubscript) MOTH_BEGIN_INSTR(CallBuiltinTypeofName) - VALUE(instr.result) = __qmljs_builtin_typeof_name(instr.name, context); + __qmljs_builtin_typeof_name(context, VALUEPTR(instr.result), instr.name); MOTH_END_INSTR(CallBuiltinTypeofName) MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) - VALUE(instr.result) = __qmljs_builtin_typeof(VALUE(instr.value), context); + __qmljs_builtin_typeof(context, VALUEPTR(instr.result), VALUE(instr.value)); MOTH_END_INSTR(CallBuiltinTypeofValue) MOTH_BEGIN_INSTR(CallBuiltinPostIncMember) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 1976bd2fbd..d35b6f627c 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -1006,50 +1006,54 @@ Value __qmljs_get_exception(ExecutionContext *context) return context->engine->exception; } -Value __qmljs_builtin_typeof(Value value, ExecutionContext *ctx) +void __qmljs_builtin_typeof(ExecutionContext *ctx, Value *result, const Value &value) { + if (!result) + return; switch (value.type()) { case Value::Undefined_Type: - return __qmljs_string_literal_undefined(ctx); + *result =__qmljs_string_literal_undefined(ctx); break; case Value::Null_Type: - return __qmljs_string_literal_object(ctx); + *result = __qmljs_string_literal_object(ctx); break; case Value::Boolean_Type: - return __qmljs_string_literal_boolean(ctx); + *result =__qmljs_string_literal_boolean(ctx); break; case Value::String_Type: - return __qmljs_string_literal_string(ctx); + *result = __qmljs_string_literal_string(ctx); break; case Value::Object_Type: if (__qmljs_is_callable(value, ctx)) - return __qmljs_string_literal_function(ctx); + *result =__qmljs_string_literal_function(ctx); else - return __qmljs_string_literal_object(ctx); // ### implementation-defined + *result = __qmljs_string_literal_object(ctx); // ### implementation-defined break; default: - return __qmljs_string_literal_number(ctx); + *result =__qmljs_string_literal_number(ctx); break; } } -Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context) +void __qmljs_builtin_typeof_name(ExecutionContext *context, Value *result, String *name) { - return __qmljs_builtin_typeof(context->getPropertyNoThrow(name), context); + if (result) + __qmljs_builtin_typeof(context, result, context->getPropertyNoThrow(name)); } void __qmljs_builtin_typeof_member(ExecutionContext *context, Value *result, const Value &base, String *name) { Value obj = base.toObject(context); if (result) - *result = __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context); + __qmljs_builtin_typeof(context, result, obj.objectValue()->__get__(context, name)); } -Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context) +void __qmljs_builtin_typeof_element(ExecutionContext *context, Value *result, const Value &base, const Value &index) { String *name = index.toString(context); Value obj = base.toObject(context); - return __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context); + if (result) + __qmljs_builtin_typeof(context, result, obj.objectValue()->__get__(context, name)); } void __qmljs_builtin_post_increment(ExecutionContext *ctx, Value *result, Value *val) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index c0f264370b..ae9d75bb97 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -101,10 +101,10 @@ void __qmljs_construct_activation_property(ExecutionContext *, Value *result, St Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc); Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc); -Value __qmljs_builtin_typeof(Value val, ExecutionContext *ctx); -Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context); +void __qmljs_builtin_typeof(ExecutionContext *ctx, Value *result, const Value &val); +void __qmljs_builtin_typeof_name(ExecutionContext *context, Value* result, String *name); void __qmljs_builtin_typeof_member(ExecutionContext* context, Value* result, const Value &base, String *name); -Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context); +void __qmljs_builtin_typeof_element(ExecutionContext* context, Value *result, const Value &base, const Value &index); void __qmljs_builtin_post_increment(ExecutionContext *ctx, Value *result, Value *val); void __qmljs_builtin_post_increment_name(ExecutionContext *context, Value *result, String *name); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 4337c72fdd..f7a4cc47bf 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -485,17 +485,17 @@ void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) { - generateFunctionCall(result, __qmljs_builtin_typeof_element, base, index, Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_element, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(base), Assembler::PointerToValue(index)); } void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result) { - generateFunctionCall(result, __qmljs_builtin_typeof_name, identifier(name), Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_name, Assembler::ContextRegister, Assembler::PointerToValue(result), identifier(name)); } void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) { - generateFunctionCall(result, __qmljs_builtin_typeof, value, Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(value)); } void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) -- cgit v1.2.3 From bed14ea2680d121f0cd575d5fc1138bee1b7045a Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 11:53:23 +0100 Subject: Remove another virtual and de-inline a call method Change-Id: Ia7cc0bf9f4024a65020fef75666ae13d3691bc54 Reviewed-by: Simon Hausmann --- src/v4/debugging.cpp | 4 ++-- src/v4/qv4functionobject.cpp | 5 +++++ src/v4/qv4functionobject.h | 8 +------- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/v4/debugging.cpp b/src/v4/debugging.cpp index 61ae0e62d0..80afe01e60 100644 --- a/src/v4/debugging.cpp +++ b/src/v4/debugging.cpp @@ -114,8 +114,8 @@ FunctionDebugInfo *Debugger::debugInfo(VM::FunctionObject *function) const if (!function) return 0; - if (VM::ScriptFunction *sf = function->asScriptFunction()) - return _functionInfo[irFunction(sf->function)]; + if (function->function) + return _functionInfo[irFunction(function->function)]; else return 0; } diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 2476d5c398..991695c8ba 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -462,6 +462,11 @@ BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (* isBuiltinFunction = true; } +Value BuiltinFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +{ + return code(context, thisObject, args, argc); +} + Value BuiltinFunction::construct(ExecutionContext *ctx, Value *, int) { ctx->throwTypeError(); diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index ae8bd531a5..9d55944279 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -159,8 +159,6 @@ struct Q_V4_EXPORT FunctionObject: Object { virtual Value construct(ExecutionContext *context, Value *args, int argc); virtual Value call(ExecutionContext *, Value, Value *, int); - virtual struct ScriptFunction *asScriptFunction() { return 0; } - protected: static const ManagedVTable static_vtbl; static void markObjects(Managed *that); @@ -198,9 +196,7 @@ struct BuiltinFunction: FunctionObject { Value (*code)(ExecutionContext *parentContext, Value thisObject, Value *args, int argc); BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)); - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc) { - return code(context, thisObject, args, argc); - } + virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); virtual Value construct(ExecutionContext *ctx, Value *, int); }; @@ -210,8 +206,6 @@ struct ScriptFunction: FunctionObject { virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); virtual Value construct(ExecutionContext *context, Value *args, int argc); - - virtual ScriptFunction *asScriptFunction() { return this; } }; struct BoundFunction: FunctionObject { -- cgit v1.2.3 From 1056456cb580d6eed1c6344c4114695ef315d7fd Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 14:07:57 +0100 Subject: Move call/construct over into the new vtable. Change-Id: I4f58a1fac25440695bdc62a49adb51a887616a5c Reviewed-by: Simon Hausmann --- src/v4/qmljs_value.h | 7 +++ src/v4/qv4argumentsobject.cpp | 24 ++++---- src/v4/qv4argumentsobject.h | 14 +++-- src/v4/qv4arrayobject.cpp | 9 ++- src/v4/qv4arrayobject.h | 7 ++- src/v4/qv4booleanobject.cpp | 7 ++- src/v4/qv4booleanobject.h | 7 ++- src/v4/qv4dateobject.cpp | 6 +- src/v4/qv4dateobject.h | 7 ++- src/v4/qv4errorobject.cpp | 26 +++++--- src/v4/qv4errorobject.h | 61 ++++++++++++------- src/v4/qv4functionobject.cpp | 135 +++++++++++++++++++++++------------------- src/v4/qv4functionobject.h | 48 ++++++++++----- src/v4/qv4globalobject.cpp | 16 ++--- src/v4/qv4globalobject.h | 7 ++- src/v4/qv4managed.cpp | 15 ++++- src/v4/qv4managed.h | 20 ++++++- src/v4/qv4numberobject.cpp | 6 +- src/v4/qv4numberobject.h | 7 ++- src/v4/qv4object.cpp | 16 +---- src/v4/qv4objectproto.cpp | 13 ++-- src/v4/qv4objectproto.h | 7 ++- src/v4/qv4regexpobject.cpp | 9 +-- src/v4/qv4regexpobject.h | 7 ++- src/v4/qv4stringobject.cpp | 13 ++-- src/v4/qv4stringobject.h | 7 ++- tools/v4/main.cpp | 14 ++++- 27 files changed, 327 insertions(+), 188 deletions(-) diff --git a/src/v4/qmljs_value.h b/src/v4/qmljs_value.h index 7c83af593e..2675d5535f 100644 --- a/src/v4/qmljs_value.h +++ b/src/v4/qmljs_value.h @@ -475,6 +475,13 @@ inline ErrorObject *Value::asErrorObject() const return isObject() ? managed()->asErrorObject() : 0; } +// ### +inline Value Managed::construct(ExecutionContext *context, Value *args, int argc) { + return vtbl->construct(this, context, args, argc); +} +inline Value Managed::call(ExecutionContext *context, const Value &thisObject, Value *args, int argc) { + return vtbl->call(this, context, thisObject, args, argc); +} } // namespace VM } // namespace QQmlJS diff --git a/src/v4/qv4argumentsobject.cpp b/src/v4/qv4argumentsobject.cpp index f805aa6db4..d1791e0189 100644 --- a/src/v4/qv4argumentsobject.cpp +++ b/src/v4/qv4argumentsobject.cpp @@ -50,12 +50,7 @@ static Value throwTypeError(ExecutionContext *ctx) return Value::undefinedValue(); } -const ManagedVTable ArgumentsObject::static_vtbl = -{ - ArgumentsObject::markObjects, - Managed::hasInstance, - ManagedVTable::EndOfVTable -}; +DEFINE_MANAGED_VTABLE(ArgumentsObject); ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount) : Object(context->engine), context(context) @@ -132,9 +127,11 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const return result; } +DEFINE_MANAGED_VTABLE(ArgumentsGetterFunction); -Value ArgumentsGetterFunction::call(ExecutionContext *ctx, Value thisObject, Value *, int) +Value ArgumentsGetterFunction::call(Managed *getter, ExecutionContext *ctx, const Value &thisObject, Value *, int) { + ArgumentsGetterFunction *g = static_cast(getter); Object *that = thisObject.asObject(); if (!that) __qmljs_throw_type_error(ctx); @@ -142,12 +139,15 @@ Value ArgumentsGetterFunction::call(ExecutionContext *ctx, Value thisObject, Val if (!o) __qmljs_throw_type_error(ctx); - assert(index < o->context->argumentCount); - return o->context->argument(index); + assert(g->index < o->context->argumentCount); + return o->context->argument(g->index); } -Value ArgumentsSetterFunction::call(ExecutionContext *ctx, Value thisObject, Value *args, int argc) +DEFINE_MANAGED_VTABLE(ArgumentsSetterFunction); + +Value ArgumentsSetterFunction::call(Managed *setter, ExecutionContext *ctx, const Value &thisObject, Value *args, int argc) { + ArgumentsSetterFunction *s = static_cast(setter); Object *that = thisObject.asObject(); if (!that) __qmljs_throw_type_error(ctx); @@ -155,8 +155,8 @@ Value ArgumentsSetterFunction::call(ExecutionContext *ctx, Value thisObject, Val if (!o) __qmljs_throw_type_error(ctx); - assert(index < o->context->argumentCount); - o->context->arguments[index] = argc ? args[0] : Value::undefinedValue(); + assert(s->index < o->context->argumentCount); + o->context->arguments[s->index] = argc ? args[0] : Value::undefinedValue(); return Value::undefinedValue(); } diff --git a/src/v4/qv4argumentsobject.h b/src/v4/qv4argumentsobject.h index fe62bf7b63..acbcc756a5 100644 --- a/src/v4/qv4argumentsobject.h +++ b/src/v4/qv4argumentsobject.h @@ -54,9 +54,12 @@ struct ArgumentsGetterFunction: FunctionObject uint index; ArgumentsGetterFunction(ExecutionContext *scope, uint index) - : FunctionObject(scope), index(index) {} + : FunctionObject(scope), index(index) { vtbl = &static_vtbl; } - virtual Value call(ExecutionContext *ctx, Value thisObject, Value *, int); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; struct ArgumentsSetterFunction: FunctionObject @@ -64,9 +67,12 @@ struct ArgumentsSetterFunction: FunctionObject uint index; ArgumentsSetterFunction(ExecutionContext *scope, uint index) - : FunctionObject(scope), index(index) {} + : FunctionObject(scope), index(index) { vtbl = &static_vtbl; } - virtual Value call(ExecutionContext *ctx, Value thisObject, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp index 2999ce0517..8117a657e1 100644 --- a/src/v4/qv4arrayobject.cpp +++ b/src/v4/qv4arrayobject.cpp @@ -44,12 +44,15 @@ using namespace QQmlJS::VM; +DEFINE_MANAGED_VTABLE(ArrayCtor); + ArrayCtor::ArrayCtor(ExecutionContext *scope) : FunctionObject(scope) { + vtbl = &static_vtbl; } -Value ArrayCtor::construct(ExecutionContext *ctx, Value *argv, int argc) +Value ArrayCtor::construct(Managed *, ExecutionContext *ctx, Value *argv, int argc) { ArrayObject *a = ctx->engine->newArrayObject(ctx); uint len; @@ -74,9 +77,9 @@ Value ArrayCtor::construct(ExecutionContext *ctx, Value *argv, int argc) return Value::fromObject(a); } -Value ArrayCtor::call(ExecutionContext *ctx, Value thisObject, Value *argv, int argc) +Value ArrayCtor::call(Managed *that, ExecutionContext *ctx, const Value &thisObject, Value *argv, int argc) { - return construct(ctx, argv, argc); + return construct(that, ctx, argv, argc); } void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) diff --git a/src/v4/qv4arrayobject.h b/src/v4/qv4arrayobject.h index 48250dde52..1e616002bb 100644 --- a/src/v4/qv4arrayobject.h +++ b/src/v4/qv4arrayobject.h @@ -55,8 +55,11 @@ struct ArrayCtor: FunctionObject { ArrayCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx, Value *argv, int argc); - virtual Value call(ExecutionContext *ctx, Value thisObject, Value *argv, int argc); + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; struct ArrayPrototype: ArrayObject diff --git a/src/v4/qv4booleanobject.cpp b/src/v4/qv4booleanobject.cpp index 02fab7b658..b2a72f1bc0 100644 --- a/src/v4/qv4booleanobject.cpp +++ b/src/v4/qv4booleanobject.cpp @@ -43,18 +43,21 @@ using namespace QQmlJS::VM; +DEFINE_MANAGED_VTABLE(BooleanCtor); + BooleanCtor::BooleanCtor(ExecutionContext *scope) : FunctionObject(scope) { + vtbl = &static_vtbl; } -Value BooleanCtor::construct(ExecutionContext *ctx, Value *args, int argc) +Value BooleanCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) { bool n = argc ? args[0].toBoolean(ctx) : false; return Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n))); } -Value BooleanCtor::call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) +Value BooleanCtor::call(Managed *, ExecutionContext *parentCtx, const Value &thisObject, Value *argv, int argc) { bool value = argc ? argv[0].toBoolean(parentCtx) : 0; return Value::fromBoolean(value); diff --git a/src/v4/qv4booleanobject.h b/src/v4/qv4booleanobject.h index da9105eda4..68976f3dc6 100644 --- a/src/v4/qv4booleanobject.h +++ b/src/v4/qv4booleanobject.h @@ -54,8 +54,11 @@ struct BooleanCtor: FunctionObject { BooleanCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx, Value *args, int argc); - virtual Value call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; struct BooleanPrototype: BooleanObject diff --git a/src/v4/qv4dateobject.cpp b/src/v4/qv4dateobject.cpp index de614cc079..77ed279c8e 100644 --- a/src/v4/qv4dateobject.cpp +++ b/src/v4/qv4dateobject.cpp @@ -658,13 +658,15 @@ static double getLocalTZA() #endif } +DEFINE_MANAGED_VTABLE(DateCtor); DateCtor::DateCtor(ExecutionContext *scope) : FunctionObject(scope) { + vtbl = &static_vtbl; } -Value DateCtor::construct(ExecutionContext *ctx, Value *args, int argc) +Value DateCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) { double t = 0; @@ -702,7 +704,7 @@ Value DateCtor::construct(ExecutionContext *ctx, Value *args, int argc) return Value::fromObject(d); } -Value DateCtor::call(ExecutionContext *ctx, Value, Value *, int) +Value DateCtor::call(Managed *, ExecutionContext *ctx, const Value &, Value *, int) { double t = currentTime(); return Value::fromString(ctx, ToString(t)); diff --git a/src/v4/qv4dateobject.h b/src/v4/qv4dateobject.h index b4916ecde4..946240ec7f 100644 --- a/src/v4/qv4dateobject.h +++ b/src/v4/qv4dateobject.h @@ -59,8 +59,11 @@ struct DateCtor: FunctionObject { DateCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx, Value *args, int argc); - virtual Value call(ExecutionContext *ctx, Value, Value *, int); + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; struct DatePrototype: DateObject diff --git a/src/v4/qv4errorobject.cpp b/src/v4/qv4errorobject.cpp index 8ad7d5ec9c..418dbb7fa0 100644 --- a/src/v4/qv4errorobject.cpp +++ b/src/v4/qv4errorobject.cpp @@ -163,48 +163,56 @@ URIErrorObject::URIErrorObject(ExecutionContext *ctx, const Value &message) prototype = ctx->engine->uRIErrorPrototype; } +DEFINE_MANAGED_VTABLE(ErrorCtor); +DEFINE_MANAGED_VTABLE(EvalErrorCtor); +DEFINE_MANAGED_VTABLE(RangeErrorCtor); +DEFINE_MANAGED_VTABLE(ReferenceErrorCtor); +DEFINE_MANAGED_VTABLE(SyntaxErrorCtor); +DEFINE_MANAGED_VTABLE(TypeErrorCtor); +DEFINE_MANAGED_VTABLE(URIErrorCtor); ErrorCtor::ErrorCtor(ExecutionContext *scope) : FunctionObject(scope) { + vtbl = &static_vtbl; } -Value ErrorCtor::construct(ExecutionContext *ctx, Value *args, int argc) +Value ErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) { return Value::fromObject(ctx->engine->newErrorObject(argc ? args[0] : Value::undefinedValue())); } -Value ErrorCtor::call(ExecutionContext *ctx, Value thisObject, Value *args, int argc) +Value ErrorCtor::call(Managed *that, ExecutionContext *ctx, const Value &, Value *args, int argc) { - return construct(ctx, args, argc); + return that->construct(ctx, args, argc); } -Value EvalErrorCtor::construct(ExecutionContext *ctx, Value *args, int argc) +Value EvalErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) { return Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); } -Value RangeErrorCtor::construct(ExecutionContext *ctx, Value *args, int argc) +Value RangeErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) { return Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); } -Value ReferenceErrorCtor::construct(ExecutionContext *ctx, Value *args, int argc) +Value ReferenceErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) { return Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); } -Value SyntaxErrorCtor::construct(ExecutionContext *ctx, Value *, int) +Value SyntaxErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *, int) { return Value::fromObject(new (ctx->engine->memoryManager) SyntaxErrorObject(ctx, 0)); } -Value TypeErrorCtor::construct(ExecutionContext *ctx, Value *args, int argc) +Value TypeErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) { return Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); } -Value URIErrorCtor::construct(ExecutionContext *ctx, Value *args, int argc) +Value URIErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) { return Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); } diff --git a/src/v4/qv4errorobject.h b/src/v4/qv4errorobject.h index 734ee3f8fd..2be32426c6 100644 --- a/src/v4/qv4errorobject.h +++ b/src/v4/qv4errorobject.h @@ -106,50 +106,71 @@ struct ErrorCtor: FunctionObject { ErrorCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx, Value *args, int argc); - virtual Value call(ExecutionContext *ctx, Value thisObject, Value *args, int argc); + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; struct EvalErrorCtor: ErrorCtor { - EvalErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + EvalErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; } - virtual Value construct(ExecutionContext *ctx, Value *, int argc); + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; }; struct RangeErrorCtor: ErrorCtor { - RangeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + RangeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; } - virtual Value construct(ExecutionContext *ctx, Value *args, int argc); + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; }; struct ReferenceErrorCtor: ErrorCtor { - ReferenceErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + ReferenceErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; } + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); - virtual Value construct(ExecutionContext *ctx, Value *args, int argc); +protected: + static const ManagedVTable static_vtbl; }; struct SyntaxErrorCtor: ErrorCtor { - SyntaxErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + SyntaxErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; } + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); - virtual Value construct(ExecutionContext *ctx, Value *, int); +protected: + static const ManagedVTable static_vtbl; }; struct TypeErrorCtor: ErrorCtor { - TypeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + TypeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; } + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); - virtual Value construct(ExecutionContext *ctx, Value *args, int argc); +protected: + static const ManagedVTable static_vtbl; }; struct URIErrorCtor: ErrorCtor { - URIErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {} + URIErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; } + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); - virtual Value construct(ExecutionContext *ctx, Value *args, int argc); +protected: + static const ManagedVTable static_vtbl; }; @@ -165,37 +186,37 @@ struct ErrorPrototype: ErrorObject struct EvalErrorPrototype: EvalErrorObject { - EvalErrorPrototype(ExecutionContext *ctx): EvalErrorObject(ctx, Value::undefinedValue()) {} + EvalErrorPrototype(ExecutionContext *ctx): EvalErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; } void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; struct RangeErrorPrototype: RangeErrorObject { - RangeErrorPrototype(ExecutionContext *ctx): RangeErrorObject(ctx, Value::undefinedValue()) {} + RangeErrorPrototype(ExecutionContext *ctx): RangeErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; } void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; struct ReferenceErrorPrototype: ReferenceErrorObject { - ReferenceErrorPrototype(ExecutionContext *ctx): ReferenceErrorObject(ctx, Value::undefinedValue()) {} + ReferenceErrorPrototype(ExecutionContext *ctx): ReferenceErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; } void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; struct SyntaxErrorPrototype: SyntaxErrorObject { - SyntaxErrorPrototype(ExecutionContext *ctx): SyntaxErrorObject(ctx, 0) {} + SyntaxErrorPrototype(ExecutionContext *ctx): SyntaxErrorObject(ctx, 0) { vtbl = &static_vtbl; } void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; struct TypeErrorPrototype: TypeErrorObject { - TypeErrorPrototype(ExecutionContext *ctx): TypeErrorObject(ctx, Value::undefinedValue()) {} + TypeErrorPrototype(ExecutionContext *ctx): TypeErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; } void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; struct URIErrorPrototype: URIErrorObject { - URIErrorPrototype(ExecutionContext *ctx): URIErrorObject(ctx, Value::undefinedValue()) {} + URIErrorPrototype(ExecutionContext *ctx): URIErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; } void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } }; diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 991695c8ba..caa69873a3 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -64,6 +64,8 @@ using namespace QQmlJS::VM; +DEFINE_MANAGED_VTABLE(FunctionObject); + Function::~Function() { delete[] codeData; @@ -127,16 +129,18 @@ bool FunctionObject::hasInstance(Managed *that, ExecutionContext *ctx, const Val return false; } -Value FunctionObject::construct(ExecutionContext *context, Value *, int) +Value FunctionObject::construct(Managed *that, ExecutionContext *context, Value *, int) { + FunctionObject *f = static_cast(that); + Object *obj = context->engine->newObject(); - Value proto = __get__(context, context->engine->id_prototype); + Value proto = f->__get__(context, context->engine->id_prototype); if (proto.isObject()) obj->prototype = proto.objectValue(); return Value::fromObject(obj); } -Value FunctionObject::call(ExecutionContext *, Value, Value *, int) +Value FunctionObject::call(Managed *, ExecutionContext *, const Value &, Value *, int) { return Value::undefinedValue(); } @@ -158,23 +162,19 @@ void FunctionObject::markObjects(Managed *that) Object::markObjects(that); } -const ManagedVTable FunctionObject::static_vtbl = -{ - FunctionObject::markObjects, - FunctionObject::hasInstance, - ManagedVTable::EndOfVTable -}; - +DEFINE_MANAGED_VTABLE(FunctionCtor); FunctionCtor::FunctionCtor(ExecutionContext *scope) : FunctionObject(scope) { + vtbl = &static_vtbl; } // 15.3.2 -Value FunctionCtor::construct(ExecutionContext *ctx, Value *args, int argc) +Value FunctionCtor::construct(Managed *that, ExecutionContext *ctx, Value *args, int argc) { + FunctionCtor *f = static_cast(that); MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); QString arguments; @@ -207,7 +207,7 @@ Value FunctionCtor::construct(ExecutionContext *ctx, Value *args, int argc) IR::Module module; - Codegen cg(ctx, strictMode); + Codegen cg(ctx, f->strictMode); IR::Function *irf = cg(QString(), fe, &module); QScopedPointer isel(ctx->engine->iselFactory->create(ctx->engine, &module)); @@ -217,9 +217,9 @@ Value FunctionCtor::construct(ExecutionContext *ctx, Value *args, int argc) } // 15.3.1: This is equivalent to new Function(...) -Value FunctionCtor::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +Value FunctionCtor::call(Managed *that, ExecutionContext *context, const Value &thisObject, Value *args, int argc) { - return construct(context, args, argc); + return construct(that, context, args, argc); } void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) @@ -309,9 +309,12 @@ static Value throwTypeError(ExecutionContext *ctx) return Value::undefinedValue(); } +DEFINE_MANAGED_VTABLE(ScriptFunction); + ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) : FunctionObject(scope) { + vtbl = &static_vtbl; this->function = function; assert(function); assert(function->code); @@ -356,24 +359,25 @@ ScriptFunction::~ScriptFunction() { } -Value ScriptFunction::construct(ExecutionContext *context, Value *args, int argc) +Value ScriptFunction::construct(Managed *that, ExecutionContext *context, Value *args, int argc) { - assert(function->code); + ScriptFunction *f = static_cast(that); + assert(f->function->code); Object *obj = context->engine->newObject(); - Value proto = __get__(context, context->engine->id_prototype); + Value proto = f->__get__(context, context->engine->id_prototype); if (proto.isObject()) obj->prototype = proto.objectValue(); - uint size = requiredMemoryForExecutionContect(this, argc); - ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); + uint size = requiredMemoryForExecutionContect(f, argc); + ExecutionContext *ctx = static_cast(f->needsActivation ? malloc(size) : alloca(size)); ctx->thisObject = Value::fromObject(obj); - ctx->function = this; + ctx->function = f; ctx->arguments = args; ctx->argumentCount = argc; ctx->initCallContext(context); - Value result = function->code(ctx, function->codeData); + Value result = f->function->code(ctx, f->function->codeData); ctx->leaveCallContext(); if (result.isObject()) @@ -381,104 +385,109 @@ Value ScriptFunction::construct(ExecutionContext *context, Value *args, int argc return Value::fromObject(obj); } -Value ScriptFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +Value ScriptFunction::call(Managed *that, ExecutionContext *context, const Value &thisObject, Value *args, int argc) { - assert(function->code); - uint size = requiredMemoryForExecutionContect(this, argc); - ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); + ScriptFunction *f = static_cast(that); + assert(f->function->code); + uint size = requiredMemoryForExecutionContect(f, argc); + ExecutionContext *ctx = static_cast(f->needsActivation ? malloc(size) : alloca(size)); - if (!strictMode && !thisObject.isObject()) { + ctx->thisObject = thisObject; + if (!f->strictMode && !thisObject.isObject()) { // Built-in functions allow for the this object to be null or undefined. This overrides // the behaviour of changing thisObject to the global object if null/undefined and allows // the built-in functions for example to throw a type error if null is passed. if (thisObject.isUndefined() || thisObject.isNull()) { - if (!isBuiltinFunction) - thisObject = context->engine->globalObject; + if (!f->isBuiltinFunction) + ctx->thisObject = context->engine->globalObject; } else { - thisObject = thisObject.toObject(context); + ctx->thisObject = thisObject.toObject(context); } } - ctx->thisObject = thisObject; - ctx->function = this; + ctx->function = f; ctx->arguments = args; ctx->argumentCount = argc; ctx->initCallContext(context); - Value result = function->code(ctx, function->codeData); + Value result = f->function->code(ctx, f->function->codeData); ctx->leaveCallContext(); return result; } +DEFINE_MANAGED_VTABLE(BuiltinFunctionOld); + BuiltinFunctionOld::BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) : FunctionObject(scope) , code(code) { + vtbl = &static_vtbl; this->name = name; isBuiltinFunction = true; } -Value BuiltinFunctionOld::construct(ExecutionContext *ctx, Value *, int) +Value BuiltinFunctionOld::construct(Managed *, ExecutionContext *ctx, Value *, int) { ctx->throwTypeError(); return Value::undefinedValue(); } -Value BuiltinFunctionOld::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +Value BuiltinFunctionOld::call(Managed *that, ExecutionContext *context, const Value &thisObject, Value *args, int argc) { - uint size = requiredMemoryForExecutionContect(this, argc); - ExecutionContext *ctx = static_cast(needsActivation ? malloc(size) : alloca(size)); + BuiltinFunctionOld *f = static_cast(that); + uint size = requiredMemoryForExecutionContect(f, argc); + ExecutionContext *ctx = static_cast(f->needsActivation ? malloc(size) : alloca(size)); - if (!strictMode && !thisObject.isObject()) { + ctx->thisObject = thisObject; + if (!f->strictMode && !thisObject.isObject()) { // Built-in functions allow for the this object to be null or undefined. This overrides // the behaviour of changing thisObject to the global object if null/undefined and allows // the built-in functions for example to throw a type error if null is passed. if (thisObject.isUndefined() || thisObject.isNull()) { - if (!isBuiltinFunction) - thisObject = context->engine->globalObject; + if (!f->isBuiltinFunction) + ctx->thisObject = context->engine->globalObject; } else { - thisObject = thisObject.toObject(context); + ctx->thisObject = thisObject.toObject(context); } } - ctx->thisObject = thisObject; - ctx->function = this; + ctx->function = f; ctx->arguments = args; ctx->argumentCount = argc; ctx->initCallContext(context); - Value result = code(ctx); + Value result = f->code(ctx); ctx->leaveCallContext(); return result; } + +DEFINE_MANAGED_VTABLE(BuiltinFunction); + BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)) : FunctionObject(scope) , code(code) { + vtbl = &static_vtbl; this->name = name; isBuiltinFunction = true; } -Value BuiltinFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +Value BuiltinFunction::call(Managed *that, ExecutionContext *context, const Value &thisObject, Value *args, int argc) { - return code(context, thisObject, args, argc); + BuiltinFunction *f = static_cast(that); + return f->code(context, thisObject, args, argc); } -Value BuiltinFunction::construct(ExecutionContext *ctx, Value *, int) +Value BuiltinFunction::construct(Managed *, ExecutionContext *ctx, Value *, int) { ctx->throwTypeError(); return Value::undefinedValue(); } -const ManagedVTable BoundFunction::static_vtbl = -{ - BoundFunction::markObjects, - BoundFunction::hasInstance, - ManagedVTable::EndOfVTable -}; +DEFINE_MANAGED_VTABLE(BoundFunction); BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) : FunctionObject(scope) @@ -501,22 +510,24 @@ BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Va *insertMember(scope->engine->id_caller) = pd; } -Value BoundFunction::call(ExecutionContext *context, Value, Value *args, int argc) +Value BoundFunction::call(Managed *that, ExecutionContext *context, const Value &, Value *args, int argc) { - Value *newArgs = static_cast(alloca(sizeof(Value)*(boundArgs.size() + argc))); - memcpy(newArgs, boundArgs.constData(), boundArgs.size()*sizeof(Value)); - memcpy(newArgs + boundArgs.size(), args, argc*sizeof(Value)); + BoundFunction *f = static_cast(that); + Value *newArgs = static_cast(alloca(sizeof(Value)*(f->boundArgs.size() + argc))); + memcpy(newArgs, f->boundArgs.constData(), f->boundArgs.size()*sizeof(Value)); + memcpy(newArgs + f->boundArgs.size(), args, argc*sizeof(Value)); - return target->call(context, boundThis, newArgs, boundArgs.size() + argc); + return f->target->call(context, f->boundThis, newArgs, f->boundArgs.size() + argc); } -Value BoundFunction::construct(ExecutionContext *context, Value *args, int argc) +Value BoundFunction::construct(Managed *that, ExecutionContext *context, Value *args, int argc) { - Value *newArgs = static_cast(alloca(sizeof(Value)*(boundArgs.size() + argc))); - memcpy(newArgs, boundArgs.constData(), boundArgs.size()*sizeof(Value)); - memcpy(newArgs + boundArgs.size(), args, argc*sizeof(Value)); + BoundFunction *f = static_cast(that); + Value *newArgs = static_cast(alloca(sizeof(Value)*(f->boundArgs.size() + argc))); + memcpy(newArgs, f->boundArgs.constData(), f->boundArgs.size()*sizeof(Value)); + memcpy(newArgs + f->boundArgs.size(), args, argc*sizeof(Value)); - return target->construct(context, newArgs, boundArgs.size() + argc); + return f->target->construct(context, newArgs, f->boundArgs.size() + argc); } bool BoundFunction::hasInstance(Managed *that, ExecutionContext *ctx, const Value &value) diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index 9d55944279..cf99b7842e 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -156,8 +156,14 @@ struct Q_V4_EXPORT FunctionObject: Object { FunctionObject(ExecutionContext *scope); - virtual Value construct(ExecutionContext *context, Value *args, int argc); - virtual Value call(ExecutionContext *, Value, Value *, int); + static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + inline Value construct(ExecutionContext *context, Value *args, int argc) { + return vtbl->construct(this, context, args, argc); + } + inline Value call(ExecutionContext *context, const Value &thisObject, Value *args, int argc) { + return vtbl->call(this, context, thisObject, args, argc); + } protected: static const ManagedVTable static_vtbl; @@ -169,8 +175,11 @@ struct FunctionCtor: FunctionObject { FunctionCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx, Value *args, int argc); - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); + static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; struct FunctionPrototype: FunctionObject @@ -188,24 +197,35 @@ struct BuiltinFunctionOld: FunctionObject { Value (*code)(ExecutionContext *); BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)); - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); - virtual Value construct(ExecutionContext *ctx, Value *, int); + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; struct BuiltinFunction: FunctionObject { Value (*code)(ExecutionContext *parentContext, Value thisObject, Value *args, int argc); BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int)); - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); - virtual Value construct(ExecutionContext *ctx, Value *, int); + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; struct ScriptFunction: FunctionObject { ScriptFunction(ExecutionContext *scope, VM::Function *function); - virtual ~ScriptFunction(); + ~ScriptFunction(); - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); - virtual Value construct(ExecutionContext *context, Value *args, int argc); + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; struct BoundFunction: FunctionObject { @@ -214,10 +234,10 @@ struct BoundFunction: FunctionObject { QVector boundArgs; BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); - virtual ~BoundFunction() {} + ~BoundFunction() {} - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); - virtual Value construct(ExecutionContext *context, Value *args, int argc); + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); static const ManagedVTable static_vtbl; static void markObjects(Managed *that); diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp index 384bce5120..2bba71e84c 100644 --- a/src/v4/qv4globalobject.cpp +++ b/src/v4/qv4globalobject.cpp @@ -339,10 +339,12 @@ static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) return QString(); } +DEFINE_MANAGED_VTABLE(EvalFunction); EvalFunction::EvalFunction(ExecutionContext *scope) : FunctionObject(scope) { + vtbl = &static_vtbl; name = scope->engine->id_eval; defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); } @@ -402,17 +404,17 @@ Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Va } -Value EvalFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc) +Value EvalFunction::call(Managed *that, ExecutionContext *context, const Value &thisObject, Value *args, int argc) { // indirect call - return evalCall(context, thisObject, args, argc, false); + return static_cast(that)->evalCall(context, thisObject, args, argc, false); } -Value EvalFunction::construct(ExecutionContext *ctx, Value *, int) -{ - ctx->throwTypeError(); - return Value::undefinedValue(); -} +//Value EvalFunction::construct(ExecutionContext *ctx, Value *, int) +//{ +// ctx->throwTypeError(); +// return Value::undefinedValue(); +//} QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, const QString &fileName, const QString &source, diff --git a/src/v4/qv4globalobject.h b/src/v4/qv4globalobject.h index e15b7b21dc..2aebad16a2 100644 --- a/src/v4/qv4globalobject.h +++ b/src/v4/qv4globalobject.h @@ -59,10 +59,13 @@ struct Q_V4_EXPORT EvalFunction : FunctionObject QQmlJS::Codegen::Mode mode, bool strictMode, bool inheritContext); - virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc); Value evalCall(ExecutionContext *context, Value thisObject, Value *args, int argc, bool directCall); - Value construct(ExecutionContext *ctx, Value *, int); + using Managed::construct; + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; struct GlobalFunctions diff --git a/src/v4/qv4managed.cpp b/src/v4/qv4managed.cpp index fe71be407f..ea4004e26f 100644 --- a/src/v4/qv4managed.cpp +++ b/src/v4/qv4managed.cpp @@ -47,8 +47,11 @@ using namespace QQmlJS::VM; const ManagedVTable Managed::static_vtbl = { + "Managed", 0 /*markObjects*/, - Managed::hasInstance, + hasInstance, + construct, + call, ManagedVTable::EndOfVTable }; @@ -151,3 +154,13 @@ bool Managed::hasInstance(Managed *, ExecutionContext *ctx, const Value &) { ctx->throwTypeError(); } + +Value Managed::construct(Managed *, ExecutionContext *context, Value *, int) +{ + context->throwTypeError(); +} + +Value Managed::call(Managed *, ExecutionContext *context, const Value &, Value *, int) +{ + context->throwTypeError(); +} diff --git a/src/v4/qv4managed.h b/src/v4/qv4managed.h index 979b308fb7..54c8cf4785 100644 --- a/src/v4/qv4managed.h +++ b/src/v4/qv4managed.h @@ -50,7 +50,6 @@ QT_BEGIN_NAMESPACE namespace QQmlJS { - namespace VM { class MemoryManager; @@ -80,11 +79,26 @@ struct ManagedVTable enum _EndOfVTable { EndOfVTable }; + const char *className; void (*markObjects)(Managed *); bool (*hasInstance)(Managed *, ExecutionContext *ctx, const Value &value); + Value (*construct)(Managed *, ExecutionContext *context, Value *args, int argc); + Value (*call)(Managed *, ExecutionContext *context, const Value &thisObject, Value *args, int argc); _EndOfVTable endofVTable; }; +#define DEFINE_MANAGED_VTABLE(classname) \ +const ManagedVTable classname::static_vtbl = \ +{ \ + #classname, \ + markObjects, \ + hasInstance, \ + construct, \ + call, \ + ManagedVTable::EndOfVTable \ +} + + struct Q_V4_EXPORT Managed { private: @@ -160,8 +174,12 @@ public: inline bool hasInstance(ExecutionContext *ctx, const Value &v) { return vtbl->hasInstance(this, ctx, v); } + inline Value construct(ExecutionContext *context, Value *args, int argc); + inline Value call(ExecutionContext *context, const Value &thisObject, Value *args, int argc); static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value &value); + static Value construct(Managed *, ExecutionContext *context, Value *, int); + static Value call(Managed *, ExecutionContext *, const Value &, Value *, int); protected: diff --git a/src/v4/qv4numberobject.cpp b/src/v4/qv4numberobject.cpp index 0601da63fb..4f42ef3ad1 100644 --- a/src/v4/qv4numberobject.cpp +++ b/src/v4/qv4numberobject.cpp @@ -48,19 +48,21 @@ using namespace QQmlJS::VM; +DEFINE_MANAGED_VTABLE(NumberCtor); NumberCtor::NumberCtor(ExecutionContext *scope) : FunctionObject(scope) { + vtbl = &static_vtbl; } -Value NumberCtor::construct(ExecutionContext *ctx, Value *args, int argc) +Value NumberCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) { double d = argc ? args[0].toNumber(ctx) : 0.; return Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(d))); } -Value NumberCtor::call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) +Value NumberCtor::call(Managed *m, ExecutionContext *parentCtx, const Value &thisObject, Value *argv, int argc) { double d = argc ? argv[0].toNumber(parentCtx) : 0.; return Value::fromDouble(d); diff --git a/src/v4/qv4numberobject.h b/src/v4/qv4numberobject.h index e9125ab752..4677a77212 100644 --- a/src/v4/qv4numberobject.h +++ b/src/v4/qv4numberobject.h @@ -54,8 +54,11 @@ struct NumberCtor: FunctionObject { NumberCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx, Value *args, int argc); - virtual Value call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; struct NumberPrototype: NumberObject diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 46debdb2e5..0d68f9e024 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -64,6 +64,7 @@ using namespace QQmlJS::VM; +DEFINE_MANAGED_VTABLE(Object); Object::Object(ExecutionEngine *engine) : prototype(0) @@ -985,13 +986,6 @@ void Object::markArrayObjects() const } } -const ManagedVTable Object::static_vtbl = -{ - Object::markObjects, - Managed::hasInstance, - ManagedVTable::EndOfVTable -}; - void ArrayObject::init(ExecutionContext *context) { type = Type_ArrayObject; @@ -1006,6 +1000,7 @@ void ArrayObject::init(ExecutionContext *context) } +DEFINE_MANAGED_VTABLE(ForEachIteratorObject); void ForEachIteratorObject::markObjects(Managed *that) { @@ -1014,10 +1009,3 @@ void ForEachIteratorObject::markObjects(Managed *that) if (o->it.object) o->it.object->mark(); } - -const ManagedVTable ForEachIteratorObject::static_vtbl = -{ - ForEachIteratorObject::markObjects, - Managed::hasInstance, - ManagedVTable::EndOfVTable -}; diff --git a/src/v4/qv4objectproto.cpp b/src/v4/qv4objectproto.cpp index de8b7ff8b0..6e9f51e6bf 100644 --- a/src/v4/qv4objectproto.cpp +++ b/src/v4/qv4objectproto.cpp @@ -71,19 +71,20 @@ using namespace QQmlJS::VM; -// -// Object -// +DEFINE_MANAGED_VTABLE(ObjectCtor); + ObjectCtor::ObjectCtor(ExecutionContext *scope) : FunctionObject(scope) { + vtbl = &static_vtbl; } -Value ObjectCtor::construct(ExecutionContext *ctx, Value *args, int argc) +Value ObjectCtor::construct(Managed *that, ExecutionContext *ctx, Value *args, int argc) { + ObjectCtor *ctor = static_cast(that); if (!argc || args[0].isUndefined() || args[0].isNull()) { Object *obj = ctx->engine->newObject(); - Value proto = __get__(ctx, ctx->engine->id_prototype); + Value proto = ctor->__get__(ctx, ctx->engine->id_prototype); if (proto.isObject()) obj->prototype = proto.objectValue(); return Value::fromObject(obj); @@ -91,7 +92,7 @@ Value ObjectCtor::construct(ExecutionContext *ctx, Value *args, int argc) return __qmljs_to_object(args[0], ctx); } -Value ObjectCtor::call(ExecutionContext *ctx, Value /*thisObject*/, Value *args, int argc) +Value ObjectCtor::call(Managed *, ExecutionContext *ctx, const Value &/*thisObject*/, Value *args, int argc) { if (!argc || args[0].isUndefined() || args[0].isNull()) return Value::fromObject(ctx->engine->newObject()); diff --git a/src/v4/qv4objectproto.h b/src/v4/qv4objectproto.h index 6341128059..25387c0f13 100644 --- a/src/v4/qv4objectproto.h +++ b/src/v4/qv4objectproto.h @@ -54,8 +54,11 @@ struct ObjectCtor: FunctionObject { ObjectCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx, Value *args, int argc); - virtual Value call(ExecutionContext *ctx, Value, Value *args, int argc); + static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; struct ObjectPrototype: Object diff --git a/src/v4/qv4regexpobject.cpp b/src/v4/qv4regexpobject.cpp index 19aee55ca5..ccb4d3be1c 100644 --- a/src/v4/qv4regexpobject.cpp +++ b/src/v4/qv4regexpobject.cpp @@ -91,14 +91,15 @@ PropertyDescriptor *RegExpObject::lastIndexProperty(ExecutionContext *ctx) return &memberData[0]; } - +DEFINE_MANAGED_VTABLE(RegExpCtor); RegExpCtor::RegExpCtor(ExecutionContext *scope) : FunctionObject(scope) { + vtbl = &static_vtbl; } -Value RegExpCtor::construct(ExecutionContext *ctx, Value *argv, int argc) +Value RegExpCtor::construct(Managed *, ExecutionContext *ctx, Value *argv, int argc) { Value r = argc > 0 ? argv[0] : Value::undefinedValue(); Value f = argc > 1 ? argv[1] : Value::undefinedValue(); @@ -142,14 +143,14 @@ Value RegExpCtor::construct(ExecutionContext *ctx, Value *argv, int argc) return Value::fromObject(o); } -Value RegExpCtor::call(ExecutionContext *ctx, Value thisObject, Value *argv, int argc) +Value RegExpCtor::call(Managed *that, ExecutionContext *ctx, const Value &thisObject, Value *argv, int argc) { if (argc > 0 && argv[0].asRegExpObject()) { if (argc == 1 || argv[1].isUndefined()) return argv[0]; } - return construct(ctx, argv, argc); + return construct(that, ctx, argv, argc); } void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) diff --git a/src/v4/qv4regexpobject.h b/src/v4/qv4regexpobject.h index d310374348..65df03c71a 100644 --- a/src/v4/qv4regexpobject.h +++ b/src/v4/qv4regexpobject.h @@ -76,8 +76,11 @@ struct RegExpCtor: FunctionObject { RegExpCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx, Value *argv, int argc); - virtual Value call(ExecutionContext *ctx, Value thisObject, Value *argv, int argc); + static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; struct RegExpPrototype: RegExpObject diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp index e8ec9e6b78..9829054693 100644 --- a/src/v4/qv4stringobject.cpp +++ b/src/v4/qv4stringobject.cpp @@ -75,6 +75,8 @@ using namespace QQmlJS::VM; +DEFINE_MANAGED_VTABLE(StringObject); + StringObject::StringObject(ExecutionContext *ctx, const Value &value) : Object(ctx->engine), value(value) { @@ -108,18 +110,15 @@ void StringObject::markObjects(Managed *that) Object::markObjects(that); } -const ManagedVTable StringObject::static_vtbl = -{ - StringObject::markObjects -}; - +DEFINE_MANAGED_VTABLE(StringCtor); StringCtor::StringCtor(ExecutionContext *scope) : FunctionObject(scope) { + vtbl = &static_vtbl; } -Value StringCtor::construct(ExecutionContext *ctx, Value *argv, int argc) +Value StringCtor::construct(Managed *, ExecutionContext *ctx, Value *argv, int argc) { Value value; if (argc) @@ -129,7 +128,7 @@ Value StringCtor::construct(ExecutionContext *ctx, Value *argv, int argc) return Value::fromObject(ctx->engine->newStringObject(ctx, value)); } -Value StringCtor::call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) +Value StringCtor::call(Managed *, ExecutionContext *parentCtx, const Value &thisObject, Value *argv, int argc) { Value value; if (argc) diff --git a/src/v4/qv4stringobject.h b/src/v4/qv4stringobject.h index 5c295e201e..0ee72a01a2 100644 --- a/src/v4/qv4stringobject.h +++ b/src/v4/qv4stringobject.h @@ -66,8 +66,11 @@ struct StringCtor: FunctionObject { StringCtor(ExecutionContext *scope); - virtual Value construct(ExecutionContext *ctx, Value *argv, int argc); - virtual Value call(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); + static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; }; struct StringPrototype: StringObject diff --git a/tools/v4/main.cpp b/tools/v4/main.cpp index 7a6e7361b2..998a315d96 100644 --- a/tools/v4/main.cpp +++ b/tools/v4/main.cpp @@ -74,10 +74,11 @@ using namespace QQmlJS::VM; struct Print: FunctionObject { Print(ExecutionContext *scope): FunctionObject(scope) { + vtbl = &static_vtbl; name = scope->engine->newString("print"); } - virtual Value call(ExecutionContext *ctx, Value, Value *args, int argc) + static Value call(Managed *, ExecutionContext *ctx, const Value &, Value *args, int argc) { for (int i = 0; i < argc; ++i) { String *s = args[i].toString(ctx); @@ -88,22 +89,31 @@ struct Print: FunctionObject std::cout << std::endl; return Value::undefinedValue(); } + + static const ManagedVTable static_vtbl; }; +DEFINE_MANAGED_VTABLE(Print); + struct GC: public FunctionObject { GC(ExecutionContext* scope) : FunctionObject(scope) { + vtbl = &static_vtbl; name = scope->engine->newString("gc"); } - virtual Value call(ExecutionContext *ctx, Value, Value *, int) + static Value call(Managed *, ExecutionContext *ctx, const Value &, Value *args, int argc) { ctx->engine->memoryManager->runGC(); return Value::undefinedValue(); } + + static const ManagedVTable static_vtbl; }; +DEFINE_MANAGED_VTABLE(GC); + } // builtins static void showException(QQmlJS::VM::ExecutionContext *ctx) -- cgit v1.2.3 From 99fec393722cbc2840f6c12ae5cde1f90690135d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 14:18:34 +0100 Subject: Remove another virtual Change-Id: I185be24d4c09d2078c3459460875c4711bf17ddb Reviewed-by: Simon Hausmann --- src/v4/qv4errorobject.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/v4/qv4errorobject.h b/src/v4/qv4errorobject.h index 2be32426c6..ae132a92e2 100644 --- a/src/v4/qv4errorobject.h +++ b/src/v4/qv4errorobject.h @@ -49,6 +49,8 @@ QT_BEGIN_NAMESPACE namespace QQmlJS { namespace VM { +struct SyntaxErrorObject; + struct ErrorObject: Object { enum ErrorType { Error, @@ -62,7 +64,7 @@ struct ErrorObject: Object { ErrorObject(ExecutionEngine* engine, const Value &message); - virtual struct SyntaxErrorObject *asSyntaxError() { return 0; } + SyntaxErrorObject *asSyntaxError(); protected: void setNameProperty(ExecutionContext *ctx); @@ -86,7 +88,6 @@ struct SyntaxErrorObject: ErrorObject { SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg); ~SyntaxErrorObject() { delete msg; } - virtual SyntaxErrorObject *asSyntaxError() { return this; } DiagnosticMessage *message() { return msg; } private: @@ -221,6 +222,11 @@ struct URIErrorPrototype: URIErrorObject }; +inline SyntaxErrorObject *ErrorObject::asSyntaxError() +{ + return subtype == SyntaxError ? static_cast(this) : 0; +} + } // end of namespace VM } // end of namespace QQmlJS -- cgit v1.2.3 From 76db0051d97553485b0ead0e5f66ca10fe1374ad Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 14:18:59 +0100 Subject: Silence compiler Change-Id: Id248400c50609811baebb1ea710210915e5274c9 Reviewed-by: Simon Hausmann --- tools/v4/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/v4/main.cpp b/tools/v4/main.cpp index 998a315d96..f6997eb79d 100644 --- a/tools/v4/main.cpp +++ b/tools/v4/main.cpp @@ -103,7 +103,7 @@ struct GC: public FunctionObject vtbl = &static_vtbl; name = scope->engine->newString("gc"); } - static Value call(Managed *, ExecutionContext *ctx, const Value &, Value *args, int argc) + static Value call(Managed *, ExecutionContext *ctx, const Value &, Value *, int) { ctx->engine->memoryManager->runGC(); return Value::undefinedValue(); -- cgit v1.2.3 From 134abefda26a5f8811e4099b5430ab3154e1bace Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 14:21:37 +0100 Subject: Remove another virtual. Change-Id: Id83e7e5153160247b15c1506cb3c741cc6b77368 Reviewed-by: Simon Hausmann --- src/v4/qv4mathobject.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/v4/qv4mathobject.h b/src/v4/qv4mathobject.h index 339bb4f7f8..77db486807 100644 --- a/src/v4/qv4mathobject.h +++ b/src/v4/qv4mathobject.h @@ -51,7 +51,6 @@ namespace VM { struct MathObject: Object { MathObject(ExecutionContext *ctx); - virtual QString className() { return QStringLiteral("Math"); } static Value method_abs(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); static Value method_acos(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc); -- cgit v1.2.3 From da5ac2aabd504e96156efede8aebc6b78370fb7d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 15:00:06 +0100 Subject: Move the destructor into the new vtable. This makes all runtime structures fully non virtual. Change-Id: I804568ca9bc33d4be0324ed542df8eab5892c0eb Reviewed-by: Simon Hausmann --- src/v4/qv4argumentsobject.cpp | 5 +++++ src/v4/qv4argumentsobject.h | 5 ++++- src/v4/qv4errorobject.cpp | 3 +++ src/v4/qv4errorobject.h | 3 +++ src/v4/qv4functionobject.cpp | 9 +++++---- src/v4/qv4functionobject.h | 3 ++- src/v4/qv4managed.cpp | 13 ++++--------- src/v4/qv4managed.h | 22 ++++++++++------------ src/v4/qv4mm.cpp | 2 +- src/v4/qv4object.cpp | 6 ++++++ src/v4/qv4object.h | 1 + src/v4/qv4regexpobject.cpp | 6 ++++++ src/v4/qv4regexpobject.h | 5 +++++ src/v4/qv4string.cpp | 15 +++++++++++++++ src/v4/qv4string.h | 7 ++++++- 15 files changed, 76 insertions(+), 29 deletions(-) diff --git a/src/v4/qv4argumentsobject.cpp b/src/v4/qv4argumentsobject.cpp index d1791e0189..f2afe195b6 100644 --- a/src/v4/qv4argumentsobject.cpp +++ b/src/v4/qv4argumentsobject.cpp @@ -89,6 +89,11 @@ ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterC } } +void ArgumentsObject::destroy(Managed *that) +{ + static_cast(that)->~ArgumentsObject(); +} + bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc) { PropertyDescriptor *pd = arrayAt(index); diff --git a/src/v4/qv4argumentsobject.h b/src/v4/qv4argumentsobject.h index acbcc756a5..13f6b4080b 100644 --- a/src/v4/qv4argumentsobject.h +++ b/src/v4/qv4argumentsobject.h @@ -80,11 +80,14 @@ struct ArgumentsObject: Object { ExecutionContext *context; QVector mappedArguments; ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount); + ~ArgumentsObject() {} bool defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); - static const ManagedVTable static_vtbl; static void markObjects(Managed *that); +protected: + static const ManagedVTable static_vtbl; + static void destroy(Managed *); }; } diff --git a/src/v4/qv4errorobject.cpp b/src/v4/qv4errorobject.cpp index 418dbb7fa0..addf163dda 100644 --- a/src/v4/qv4errorobject.cpp +++ b/src/v4/qv4errorobject.cpp @@ -88,10 +88,13 @@ void ErrorObject::setNameProperty(ExecutionContext *ctx) defineDefaultProperty(ctx, QLatin1String("name"), Value::fromString(ctx, className())); } +DEFINE_MANAGED_VTABLE(SyntaxErrorObject); + SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) : ErrorObject(ctx->engine, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0)) , msg(message) { + vtbl = &static_vtbl; subtype = SyntaxError; prototype = ctx->engine->syntaxErrorPrototype; setNameProperty(ctx); diff --git a/src/v4/qv4errorobject.h b/src/v4/qv4errorobject.h index ae132a92e2..c03d8221ff 100644 --- a/src/v4/qv4errorobject.h +++ b/src/v4/qv4errorobject.h @@ -87,11 +87,14 @@ struct ReferenceErrorObject: ErrorObject { struct SyntaxErrorObject: ErrorObject { SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg); ~SyntaxErrorObject() { delete msg; } + static void destroy(Managed *that) { static_cast(that)->~SyntaxErrorObject(); } DiagnosticMessage *message() { return msg; } private: DiagnosticMessage *msg; +protected: + static const ManagedVTable static_vtbl; }; struct TypeErrorObject: ErrorObject { diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index caa69873a3..af23182026 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -355,10 +355,6 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) } } -ScriptFunction::~ScriptFunction() -{ -} - Value ScriptFunction::construct(Managed *that, ExecutionContext *context, Value *args, int argc) { ScriptFunction *f = static_cast(that); @@ -510,6 +506,11 @@ BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Va *insertMember(scope->engine->id_caller) = pd; } +void BoundFunction::destroy(Managed *that) +{ + static_cast(that)->~BoundFunction(); +} + Value BoundFunction::call(Managed *that, ExecutionContext *context, const Value &, Value *args, int argc) { BoundFunction *f = static_cast(that); diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index cf99b7842e..ebf14f8029 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -219,7 +219,6 @@ protected: struct ScriptFunction: FunctionObject { ScriptFunction(ExecutionContext *scope, VM::Function *function); - ~ScriptFunction(); static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); @@ -236,10 +235,12 @@ struct BoundFunction: FunctionObject { BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); ~BoundFunction() {} + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); static const ManagedVTable static_vtbl; + static void destroy(Managed *); static void markObjects(Managed *that); static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value &value); }; diff --git a/src/v4/qv4managed.cpp b/src/v4/qv4managed.cpp index ea4004e26f..f458565100 100644 --- a/src/v4/qv4managed.cpp +++ b/src/v4/qv4managed.cpp @@ -47,20 +47,15 @@ using namespace QQmlJS::VM; const ManagedVTable Managed::static_vtbl = { - "Managed", + call, + construct, 0 /*markObjects*/, + destroy, hasInstance, - construct, - call, - ManagedVTable::EndOfVTable + "Managed", }; -Managed::~Managed() -{ - _data = 0; -} - void *Managed::operator new(size_t size, MemoryManager *mm) { assert(mm); diff --git a/src/v4/qv4managed.h b/src/v4/qv4managed.h index 54c8cf4785..94aaaf34a2 100644 --- a/src/v4/qv4managed.h +++ b/src/v4/qv4managed.h @@ -76,26 +76,23 @@ struct Value; struct ManagedVTable { - enum _EndOfVTable { - EndOfVTable - }; - const char *className; + Value (*call)(Managed *, ExecutionContext *context, const Value &thisObject, Value *args, int argc); + Value (*construct)(Managed *, ExecutionContext *context, Value *args, int argc); void (*markObjects)(Managed *); + void (*destroy)(Managed *); bool (*hasInstance)(Managed *, ExecutionContext *ctx, const Value &value); - Value (*construct)(Managed *, ExecutionContext *context, Value *args, int argc); - Value (*call)(Managed *, ExecutionContext *context, const Value &thisObject, Value *args, int argc); - _EndOfVTable endofVTable; + const char *className; }; #define DEFINE_MANAGED_VTABLE(classname) \ const ManagedVTable classname::static_vtbl = \ { \ - #classname, \ + call, \ + construct, \ markObjects, \ + destroy, \ hasInstance, \ - construct, \ - call, \ - ManagedVTable::EndOfVTable \ + #classname \ } @@ -110,7 +107,6 @@ protected: Managed() : vtbl(&static_vtbl), _data(0) { inUse = 1; extensible = 1; } - virtual ~Managed(); public: void *operator new(size_t size, MemoryManager *mm); @@ -177,6 +173,7 @@ public: inline Value construct(ExecutionContext *context, Value *args, int argc); inline Value call(ExecutionContext *context, const Value &thisObject, Value *args, int argc); + static void destroy(Managed *that) { that->_data = 0; } static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value &value); static Value construct(Managed *, ExecutionContext *context, Value *, int); static Value call(Managed *, ExecutionContext *, const Value &, Value *, int); @@ -184,6 +181,7 @@ public: protected: static const ManagedVTable static_vtbl; + const ManagedVTable *vtbl; union { diff --git a/src/v4/qv4mm.cpp b/src/v4/qv4mm.cpp index 81dd39a8a3..b81456b778 100644 --- a/src/v4/qv4mm.cpp +++ b/src/v4/qv4mm.cpp @@ -229,7 +229,7 @@ std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t m->markBit = 0; } else { // qDebug() << "-- collecting it." << m << *f << m->nextFree(); - m->~Managed(); + m->vtbl->destroy(m); m->setNextFree(*f); *f = m; diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 0d68f9e024..16ce621fe2 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -82,6 +82,12 @@ Object::~Object() delete [] memberData; delete [] (arrayData - (sparseArray ? 0 : arrayOffset)); delete sparseArray; + _data = 0; +} + +void Object::destroy(Managed *that) +{ + static_cast(that)->~Object(); } void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &value) diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index 1b29dafd15..62283bc407 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -305,6 +305,7 @@ public: protected: static const ManagedVTable static_vtbl; + static void destroy(Managed *that); static void markObjects(Managed *that); friend struct ObjectIterator; diff --git a/src/v4/qv4regexpobject.cpp b/src/v4/qv4regexpobject.cpp index ccb4d3be1c..d9abd513eb 100644 --- a/src/v4/qv4regexpobject.cpp +++ b/src/v4/qv4regexpobject.cpp @@ -63,6 +63,7 @@ using namespace QQmlJS::VM; +DEFINE_MANAGED_VTABLE(RegExpObject); RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global) : Object(engine) @@ -85,6 +86,11 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr value, bo defineReadonlyProperty(engine->newIdentifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); } +void RegExpObject::destroy(Managed *that) +{ + static_cast(that)->~RegExpObject(); +} + PropertyDescriptor *RegExpObject::lastIndexProperty(ExecutionContext *ctx) { assert(0 == internalClass->find(ctx->engine->newIdentifier(QStringLiteral("lastIndex")))); diff --git a/src/v4/qv4regexpobject.h b/src/v4/qv4regexpobject.h index 65df03c71a..c9cdb809f2 100644 --- a/src/v4/qv4regexpobject.h +++ b/src/v4/qv4regexpobject.h @@ -69,6 +69,11 @@ struct RegExpObject: Object { PropertyDescriptor *lastIndexProperty(ExecutionContext *ctx); bool global; RegExpObject(ExecutionEngine *engine, PassRefPtr value, bool global); + ~RegExpObject() {} + +protected: + static const ManagedVTable static_vtbl; + static void destroy(Managed *that); }; diff --git a/src/v4/qv4string.cpp b/src/v4/qv4string.cpp index 22b7354a23..20656ce67e 100644 --- a/src/v4/qv4string.cpp +++ b/src/v4/qv4string.cpp @@ -73,6 +73,21 @@ static uint toArrayIndex(const QChar *ch, const QChar *end, bool *ok) return i; } +const ManagedVTable String::static_vtbl = +{ + call, + construct, + 0 /*markObjects*/, + destroy, + hasInstance, + "String", +}; + +void String::destroy(Managed *that) +{ + static_cast(that)->~String(); +} + uint String::toUInt(bool *ok) const { *ok = true; diff --git a/src/v4/qv4string.h b/src/v4/qv4string.h index 81566bdeb3..eecaa5344f 100644 --- a/src/v4/qv4string.h +++ b/src/v4/qv4string.h @@ -61,7 +61,8 @@ struct String : public Managed { String(const QString &text) : _text(text), stringHash(UINT_MAX), identifier(UINT_MAX) - { type = Type_String; subtype = StringType_Unknown; } + { vtbl = &static_vtbl; type = Type_String; subtype = StringType_Unknown; } + ~String() { _data = 0; } inline bool isEqualTo(const String *other) const { if (this == other) @@ -108,6 +109,10 @@ struct String : public Managed { QString _text; mutable uint stringHash; mutable uint identifier; + +protected: + static void destroy(Managed *); + static const ManagedVTable static_vtbl; }; } // namespace VM -- cgit v1.2.3 From 4d21c8b2b68cb575fb901965df349559c9147ed7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Feb 2013 15:47:06 +0100 Subject: Convert construct runtime functions to new calling convention Change-Id: I063508ff780d2f6371f77eca7138a09d78e1a45e Reviewed-by: Lars Knoll --- src/v4/llvm_runtime.cpp | 4 ++-- src/v4/moth/qv4vme_moth.cpp | 4 ++-- src/v4/qmljs_runtime.cpp | 26 +++++++++++++++----------- src/v4/qmljs_runtime.h | 4 ++-- src/v4/qv4isel_masm.cpp | 4 ++-- 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 1dc480060d..e89c8c8680 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -415,7 +415,7 @@ void __qmljs_llvm_construct_activation_property(ExecutionContext *context, Value void __qmljs_llvm_construct_value(ExecutionContext *context, Value *result, const Value *func, Value *args, int argc) { - *result = __qmljs_construct_value(context, *func, args, argc); + __qmljs_construct_value(context, result, *func, args, argc); } void __qmljs_llvm_get_activation_property(ExecutionContext *ctx, Value *result, String *name) @@ -440,7 +440,7 @@ void __qmljs_llvm_call_property(ExecutionContext *context, Value *result, const void __qmljs_llvm_construct_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) { - *result = __qmljs_construct_property(context, *base, name, args, argc); + __qmljs_construct_property(context, result, *base, name, args, argc); } void __qmljs_llvm_get_element(ExecutionContext *ctx, Value *result, Value *object, Value *index) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index a8d92432b1..00db920a47 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -378,13 +378,13 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_BEGIN_INSTR(CreateValue) Q_ASSERT(instr.args + instr.argc < stackSize); VM::Value *args = stack + instr.args; - VALUE(instr.result) = __qmljs_construct_value(context, VALUE(instr.func), args, instr.argc); + __qmljs_construct_value(context, VALUEPTR(instr.result), VALUE(instr.func), args, instr.argc); MOTH_END_INSTR(CreateValue) MOTH_BEGIN_INSTR(CreateProperty) Q_ASSERT(instr.args + instr.argc < stackSize); VM::Value *args = stack + instr.args; - VALUE(instr.result) = __qmljs_construct_property(context, VALUE(instr.base), instr.name, args, instr.argc); + __qmljs_construct_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); MOTH_END_INSTR(CreateProperty) MOTH_BEGIN_INSTR(CreateActivationProperty) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index d35b6f627c..3efeaaa396 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -935,32 +935,36 @@ void __qmljs_call_value(ExecutionContext *context, Value *result, const Value *t void __qmljs_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) { Value func = context->getProperty(name); - Value res = __qmljs_construct_value(context, func, args, argc); - if (result) - *result = res; + __qmljs_construct_value(context, result, func, args, argc); } -Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc) +void __qmljs_construct_value(ExecutionContext *context, Value *result, const Value &func, Value *args, int argc) { - if (FunctionObject *f = func.asFunctionObject()) - return f->construct(context, args, argc); + if (FunctionObject *f = func.asFunctionObject()) { + Value res = f->construct(context, args, argc); + if (result) + *result = res; + return; + } context->throwTypeError(); - return Value::undefinedValue(); } -Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc) +void __qmljs_construct_property(ExecutionContext *context, Value *result, const Value &base, String *name, Value *args, int argc) { Value thisObject = base; if (!thisObject.isObject()) thisObject = __qmljs_to_object(base, context); Value func = thisObject.objectValue()->__get__(context, name); - if (FunctionObject *f = func.asFunctionObject()) - return f->construct(context, args, argc); + if (FunctionObject *f = func.asFunctionObject()) { + Value res = f->construct(context, args, argc); + if (result) + *result = res; + return; + } context->throwTypeError(); - return Value::undefinedValue(); } void __qmljs_throw(Value value, ExecutionContext *context) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index ae9d75bb97..0662bbcdfc 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -98,8 +98,8 @@ void __qmljs_call_element(ExecutionContext *context, Value *result, const Value void __qmljs_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value &func, Value *args, int argc); void __qmljs_construct_activation_property(ExecutionContext *, Value *result, String *name, Value *args, int argc); -Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc); -Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc); +void __qmljs_construct_property(ExecutionContext *context, Value *result, const Value &base, String *name, Value *args, int argc); +void __qmljs_construct_value(ExecutionContext *context, Value *result, const Value &func, Value *args, int argc); void __qmljs_builtin_typeof(ExecutionContext *ctx, Value *result, const Value &val); void __qmljs_builtin_typeof_name(ExecutionContext *context, Value* result, String *name); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index f7a4cc47bf..d91917ba8f 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -897,7 +897,7 @@ void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprL void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) { int argc = prepareVariableArguments(args); - generateFunctionCall(result, __qmljs_construct_property, Assembler::ContextRegister, base, identifier(name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); + generateFunctionCall(Assembler::Void, __qmljs_construct_property, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) @@ -905,7 +905,7 @@ void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, I assert(value != 0); int argc = prepareVariableArguments(args); - generateFunctionCall(result, __qmljs_construct_value, Assembler::ContextRegister, value, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); + generateFunctionCall(Assembler::Void, __qmljs_construct_value, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } void InstructionSelection::visitJump(IR::Jump *s) -- cgit v1.2.3 From 46f70d324fb223c1f83e0612da90c8c96bf133dd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Feb 2013 15:55:43 +0100 Subject: Convert unary ops to new calling convention Change-Id: I974fd474c4f35885e42dd219e2daa65098f4e0a1 Reviewed-by: Lars Knoll --- src/v4/llvm_runtime.cpp | 8 ++++---- src/v4/moth/qv4isel_moth.cpp | 2 +- src/v4/moth/qv4vme_moth.cpp | 2 +- src/v4/qmljs_runtime.cpp | 22 ++++++++++++---------- src/v4/qmljs_runtime.h | 42 ++++++++++++++++++++++-------------------- src/v4/qv4isel_masm.cpp | 4 ++-- 6 files changed, 42 insertions(+), 38 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index e89c8c8680..af5f036df1 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -210,22 +210,22 @@ void __qmljs_llvm_in(ExecutionContext *ctx, Value *result, Value *left, Value *r void __qmljs_llvm_uplus(ExecutionContext *ctx, Value *result, const Value *value) { - *result = __qmljs_uplus(*value, ctx); + __qmljs_uplus(result, *value, ctx); } void __qmljs_llvm_uminus(ExecutionContext *ctx, Value *result, const Value *value) { - *result = __qmljs_uminus(*value, ctx); + __qmljs_uminus(result, *value, ctx); } void __qmljs_llvm_compl(ExecutionContext *ctx, Value *result, const Value *value) { - *result = __qmljs_compl(*value, ctx); + __qmljs_compl(result, *value, ctx); } void __qmljs_llvm_not(ExecutionContext *ctx, Value *result, const Value *value) { - *result = __qmljs_not(*value, ctx); + __qmljs_not(result, *value, ctx); } void __qmljs_llvm_inplace_bit_and_name(ExecutionContext *ctx, String *dest, Value *src) diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index 8cd9e385a8..fccd750c97 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -482,7 +482,7 @@ void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) { - VM::Value (*op)(const VM::Value& value, VM::ExecutionContext *ctx) = 0; + VM::UnaryOpName op = 0; switch (oper) { case IR::OpIfTrue: assert(!"unreachable"); break; case IR::OpNot: op = VM::__qmljs_not; break; diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 00db920a47..c6abce5623 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -406,7 +406,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CJump) MOTH_BEGIN_INSTR(Unop) - VALUE(instr.result) = instr.alu(VALUE(instr.source), context); + instr.alu(VALUEPTR(instr.result), VALUE(instr.source), context); MOTH_END_INSTR(Unop) MOTH_BEGIN_INSTR(Binop) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 3efeaaa396..be0353b0b9 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -1289,26 +1289,28 @@ void __qmljs_builtin_define_getter_setter(Value object, String *name, Value gett o->__defineOwnProperty__(ctx, name, &pd); } -Value __qmljs_increment(const Value &value, ExecutionContext *ctx) +void __qmljs_increment(Value *result, const Value &value, ExecutionContext *ctx) { TRACE1(value); if (value.isInteger()) - return Value::fromInt32(value.integerValue() + 1); - - double d = __qmljs_to_number(value, ctx); - return Value::fromDouble(d + 1); + *result = Value::fromInt32(value.integerValue() + 1); + else { + double d = __qmljs_to_number(value, ctx); + *result = Value::fromDouble(d + 1); + } } -Value __qmljs_decrement(const Value &value, ExecutionContext *ctx) +void __qmljs_decrement(Value *result, const Value &value, ExecutionContext *ctx) { TRACE1(value); if (value.isInteger()) - return Value::fromInt32(value.integerValue() - 1); - - double d = __qmljs_to_number(value, ctx); - return Value::fromDouble(d - 1); + *result = Value::fromInt32(value.integerValue() - 1); + else { + double d = __qmljs_to_number(value, ctx); + *result = Value::fromDouble(d - 1); + } } } // extern "C" diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 0662bbcdfc..ec418d3729 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -201,13 +201,13 @@ Bool __qmljs_equal(const Value &x, const Value &y, ExecutionContext *ctx); Bool __qmljs_strict_equal(const Value &x, const Value &y); // unary operators -typedef Value (*UnaryOpName)(const Value &, ExecutionContext* ctx); -Value __qmljs_uplus(const Value &value, ExecutionContext *ctx); -Value __qmljs_uminus(const Value &value, ExecutionContext *ctx); -Value __qmljs_compl(const Value &value, ExecutionContext *ctx); -Value __qmljs_not(const Value &value, ExecutionContext *ctx); -Value __qmljs_increment(const Value &value, ExecutionContext *ctx); -Value __qmljs_decrement(const Value &value, ExecutionContext *ctx); +typedef void (*UnaryOpName)(Value *, const Value &, ExecutionContext* ctx); +void __qmljs_uplus(Value *result, const Value &value, ExecutionContext *ctx); +void __qmljs_uminus(Value *result, const Value &value, ExecutionContext *ctx); +void __qmljs_compl(Value *result, const Value &value, ExecutionContext *ctx); +void __qmljs_not(Value *result, const Value &value, ExecutionContext *ctx); +void __qmljs_increment(Value *result, const Value &value, ExecutionContext *ctx); +void __qmljs_decrement(Value *result, const Value &value, ExecutionContext *ctx); Value __qmljs_delete_subscript(ExecutionContext *ctx, const Value &base, Value index); Value __qmljs_delete_member(ExecutionContext *ctx, const Value &base, String *name); @@ -481,30 +481,32 @@ inline Value __qmljs_default_value(const Value &value, ExecutionContext *ctx, in } -inline Value __qmljs_uplus(const Value &value, ExecutionContext *ctx) +inline void __qmljs_uplus(Value *result, const Value &value, ExecutionContext *ctx) { TRACE1(value); - Value copy = value; - if (copy.tryIntegerConversion()) - return copy; + *result = value; + if (result->tryIntegerConversion()) + return; double n = __qmljs_to_number(value, ctx); - return Value::fromDouble(n); + *result = Value::fromDouble(n); } -inline Value __qmljs_uminus(const Value &value, ExecutionContext *ctx) +inline void __qmljs_uminus(Value *result, const Value &value, ExecutionContext *ctx) { TRACE1(value); // +0 != -0, so we need to convert to double when negating 0 if (value.isInteger() && value.integerValue()) - return Value::fromInt32(-value.integerValue()); - double n = __qmljs_to_number(value, ctx); - return Value::fromDouble(-n); + *result = Value::fromInt32(-value.integerValue()); + else { + double n = __qmljs_to_number(value, ctx); + *result = Value::fromDouble(-n); + } } -inline Value __qmljs_compl(const Value &value, ExecutionContext *ctx) +inline void __qmljs_compl(Value *result, const Value &value, ExecutionContext *ctx) { TRACE1(value); @@ -514,15 +516,15 @@ inline Value __qmljs_compl(const Value &value, ExecutionContext *ctx) else n = Value::toInt32(__qmljs_to_number(value, ctx)); - return Value::fromInt32(~n); + *result = Value::fromInt32(~n); } -inline Value __qmljs_not(const Value &value, ExecutionContext *ctx) +inline void __qmljs_not(Value *result, const Value &value, ExecutionContext *ctx) { TRACE1(value); bool b = __qmljs_to_boolean(value, ctx); - return Value::fromBoolean(!b); + *result = Value::fromBoolean(!b); } // binary operators diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index d91917ba8f..ed1a09ee30 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -739,7 +739,7 @@ void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) { - Value (*op)(const Value& value, ExecutionContext *ctx) = 0; + VM::UnaryOpName op = 0; const char *opName = 0; switch (oper) { case IR::OpIfTrue: assert(!"unreachable"); break; @@ -753,7 +753,7 @@ void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp * } // switch if (op) - _as->generateFunctionCallImp(targetTemp, opName, op, Assembler::PointerToValue(sourceTemp), + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::PointerToValue(targetTemp), Assembler::PointerToValue(sourceTemp), Assembler::ContextRegister); } -- cgit v1.2.3 From 30af543228af78378eadb5f5413d8b3dd1a20470 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Feb 2013 16:02:40 +0100 Subject: Convert property deletion runtime functions to new calling convention Change-Id: I312ccbd65d9aad5e8db349af94c00bed0fd73544 Reviewed-by: Lars Knoll --- src/v4/llvm_runtime.cpp | 6 +++--- src/v4/moth/qv4vme_moth.cpp | 6 +++--- src/v4/qmljs_runtime.cpp | 24 ++++++++++++++++-------- src/v4/qmljs_runtime.h | 6 +++--- src/v4/qv4isel_masm.cpp | 6 +++--- 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index af5f036df1..36b10db77d 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -506,17 +506,17 @@ void __qmljs_llvm_get_this_object(ExecutionContext *ctx, Value *result) void __qmljs_llvm_delete_subscript(ExecutionContext *ctx, Value *result, Value *base, Value *index) { - *result = __qmljs_delete_subscript(ctx, *base, *index); + __qmljs_delete_subscript(ctx, result, *base, *index); } void __qmljs_llvm_delete_member(ExecutionContext *ctx, Value *result, Value *base, String *name) { - *result = __qmljs_delete_member(ctx, *base, name); + __qmljs_delete_member(ctx, result, *base, name); } void __qmljs_llvm_delete_name(ExecutionContext *ctx, Value *result, String *name) { - *result = __qmljs_delete_name(ctx, name); + __qmljs_delete_name(ctx, result, name); } } // extern "C" diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index c6abce5623..f26af332cc 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -300,15 +300,15 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) - VALUE(instr.result) = __qmljs_delete_member(context, VALUE(instr.base), instr.member); + __qmljs_delete_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); MOTH_END_INSTR(CallBuiltinDeleteMember) MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) - VALUE(instr.result) = __qmljs_delete_subscript(context, VALUE(instr.base), VALUE(instr.index)); + __qmljs_delete_subscript(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); MOTH_END_INSTR(CallBuiltinDeleteSubscript) MOTH_BEGIN_INSTR(CallBuiltinDeleteName) - VALUE(instr.result) = __qmljs_delete_name(context, instr.name); + __qmljs_delete_name(context, VALUEPTR(instr.result), instr.name); MOTH_END_INSTR(CallBuiltinDeleteName) MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index be0353b0b9..733f165527 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -189,7 +189,7 @@ Value __qmljs_string_literal_function(ExecutionContext *ctx) return Value::fromString(ctx->engine->newString(QStringLiteral("function"))); } -Value __qmljs_delete_subscript(ExecutionContext *ctx, const Value &base, Value index) +void __qmljs_delete_subscript(ExecutionContext *ctx, Value *result, const Value &base, const Value &index) { if (Object *o = base.asObject()) { uint n = UINT_MAX; @@ -197,23 +197,31 @@ Value __qmljs_delete_subscript(ExecutionContext *ctx, const Value &base, Value i n = index.integerValue(); else if (index.isDouble()) n = index.doubleValue(); - if (n < UINT_MAX) - return Value::fromBoolean(o->__delete__(ctx, n)); + if (n < UINT_MAX) { + Value res = Value::fromBoolean(o->__delete__(ctx, n)); + if (result) + *result = res; + return; + } } String *name = index.toString(ctx); - return __qmljs_delete_member(ctx, base, name); + __qmljs_delete_member(ctx, result, base, name); } -Value __qmljs_delete_member(ExecutionContext *ctx, const Value &base, String *name) +void __qmljs_delete_member(ExecutionContext *ctx, Value *result, const Value &base, String *name) { Value obj = base.toObject(ctx); - return Value::fromBoolean(obj.objectValue()->__delete__(ctx, name)); + Value res = Value::fromBoolean(obj.objectValue()->__delete__(ctx, name)); + if (result) + *result = res; } -Value __qmljs_delete_name(ExecutionContext *ctx, String *name) +void __qmljs_delete_name(ExecutionContext *ctx, Value *result, String *name) { - return Value::fromBoolean(ctx->deleteProperty(name)); + Value res = Value::fromBoolean(ctx->deleteProperty(name)); + if (result) + *result = res; } void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index ec418d3729..503a987ae8 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -209,9 +209,9 @@ void __qmljs_not(Value *result, const Value &value, ExecutionContext *ctx); void __qmljs_increment(Value *result, const Value &value, ExecutionContext *ctx); void __qmljs_decrement(Value *result, const Value &value, ExecutionContext *ctx); -Value __qmljs_delete_subscript(ExecutionContext *ctx, const Value &base, Value index); -Value __qmljs_delete_member(ExecutionContext *ctx, const Value &base, String *name); -Value __qmljs_delete_name(ExecutionContext *ctx, String *name); +void __qmljs_delete_subscript(ExecutionContext *ctx, Value *result, const Value &base, const Value &index); +void __qmljs_delete_member(ExecutionContext *ctx, Value *result, const Value &base, String *name); +void __qmljs_delete_name(ExecutionContext *ctx, Value *result, String *name); void __qmljs_throw(Value value, ExecutionContext *context); // actually returns a jmp_buf * diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index ed1a09ee30..29fbf37e86 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -500,17 +500,17 @@ void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *res void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) { - generateFunctionCall(result, __qmljs_delete_member, Assembler::ContextRegister, Assembler::PointerToValue(base), identifier(name)); + generateFunctionCall(Assembler::Void, __qmljs_delete_member, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name)); } void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) { - generateFunctionCall(result, __qmljs_delete_subscript, Assembler::ContextRegister, Assembler::PointerToValue(base), index); + generateFunctionCall(Assembler::Void, __qmljs_delete_subscript, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(base), Assembler::PointerToValue(index)); } void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) { - generateFunctionCall(result, __qmljs_delete_name, Assembler::ContextRegister, identifier(name)); + generateFunctionCall(Assembler::Void, __qmljs_delete_name, Assembler::ContextRegister, Assembler::PointerToValue(result), identifier(name)); } void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) -- cgit v1.2.3 From 79e311cb2cc5ec0557e923f847d0397fe5f46777 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Feb 2013 16:04:15 +0100 Subject: Ported built-in exception getter to new calling convention Change-Id: Ica14229cfa280afba2003b3b50930c2986aa2f23 Reviewed-by: Lars Knoll --- src/v4/llvm_runtime.cpp | 2 +- src/v4/moth/qv4vme_moth.cpp | 2 +- src/v4/qmljs_runtime.cpp | 4 ++-- src/v4/qmljs_runtime.h | 2 +- src/v4/qv4isel_masm.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 36b10db77d..50fc2206bf 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -486,7 +486,7 @@ void __qmljs_llvm_delete_exception_handler(ExecutionContext *context) void __qmljs_llvm_get_exception(ExecutionContext *context, Value *result) { - *result = __qmljs_get_exception(context); + __qmljs_get_exception(context, result); } void __qmljs_llvm_foreach_iterator_object(ExecutionContext *context, Value *result, Value *in) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index f26af332cc..5d62d4d010 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -276,7 +276,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinDeleteExceptionHandler) MOTH_BEGIN_INSTR(CallBuiltinGetException) - VALUE(instr.result) = __qmljs_get_exception(context); + __qmljs_get_exception(context, VALUEPTR(instr.result)); MOTH_END_INSTR(CallBuiltinGetException) MOTH_BEGIN_INSTR(CallBuiltinPushScope) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 733f165527..b8625bbd05 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -1013,9 +1013,9 @@ void __qmljs_delete_exception_handler(ExecutionContext *context) context->engine->unwindStack.pop_back(); } -Value __qmljs_get_exception(ExecutionContext *context) +void __qmljs_get_exception(ExecutionContext *context, Value *result) { - return context->engine->exception; + *result = context->engine->exception; } void __qmljs_builtin_typeof(ExecutionContext *ctx, Value *result, const Value &value) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 503a987ae8..2d7fe89856 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -217,7 +217,7 @@ void __qmljs_throw(Value value, ExecutionContext *context); // actually returns a jmp_buf * Q_V4_EXPORT void *__qmljs_create_exception_handler(ExecutionContext *context); void __qmljs_delete_exception_handler(ExecutionContext *context); -Value __qmljs_get_exception(ExecutionContext *context); +void __qmljs_get_exception(ExecutionContext *context, Value *result); // binary operators typedef void (*BinOp)(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 29fbf37e86..f304074dce 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -591,7 +591,7 @@ void InstructionSelection::callBuiltinDeleteExceptionHandler() void InstructionSelection::callBuiltinGetException(IR::Temp *result) { - generateFunctionCall(result, __qmljs_get_exception, Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_get_exception, Assembler::ContextRegister, Assembler::PointerToValue(result)); } void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) -- cgit v1.2.3 From 86a207e87d18310541f32ea6793720ba3b555002 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Feb 2013 16:11:16 +0100 Subject: Fix argument order of unary ops Change-Id: Id108c4b74f03cac8181a9308413b69e6bf1ef83e Reviewed-by: Lars Knoll --- src/v4/llvm_runtime.cpp | 8 ++++---- src/v4/moth/qv4vme_moth.cpp | 2 +- src/v4/qmljs_runtime.cpp | 4 ++-- src/v4/qmljs_runtime.h | 22 +++++++++++----------- src/v4/qv4isel_masm.cpp | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 50fc2206bf..61b95be300 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -210,22 +210,22 @@ void __qmljs_llvm_in(ExecutionContext *ctx, Value *result, Value *left, Value *r void __qmljs_llvm_uplus(ExecutionContext *ctx, Value *result, const Value *value) { - __qmljs_uplus(result, *value, ctx); + __qmljs_uplus(ctx, result, *value); } void __qmljs_llvm_uminus(ExecutionContext *ctx, Value *result, const Value *value) { - __qmljs_uminus(result, *value, ctx); + __qmljs_uminus(ctx, result, *value); } void __qmljs_llvm_compl(ExecutionContext *ctx, Value *result, const Value *value) { - __qmljs_compl(result, *value, ctx); + __qmljs_compl(ctx, result, *value); } void __qmljs_llvm_not(ExecutionContext *ctx, Value *result, const Value *value) { - __qmljs_not(result, *value, ctx); + __qmljs_not(ctx, result, *value); } void __qmljs_llvm_inplace_bit_and_name(ExecutionContext *ctx, String *dest, Value *src) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 5d62d4d010..0f9f6182c3 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -406,7 +406,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CJump) MOTH_BEGIN_INSTR(Unop) - instr.alu(VALUEPTR(instr.result), VALUE(instr.source), context); + instr.alu(context, VALUEPTR(instr.result), VALUE(instr.source)); MOTH_END_INSTR(Unop) MOTH_BEGIN_INSTR(Binop) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index b8625bbd05..ed4cee27ac 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -1297,7 +1297,7 @@ void __qmljs_builtin_define_getter_setter(Value object, String *name, Value gett o->__defineOwnProperty__(ctx, name, &pd); } -void __qmljs_increment(Value *result, const Value &value, ExecutionContext *ctx) +void __qmljs_increment(ExecutionContext *ctx, Value *result, const Value &value) { TRACE1(value); @@ -1309,7 +1309,7 @@ void __qmljs_increment(Value *result, const Value &value, ExecutionContext *ctx) } } -void __qmljs_decrement(Value *result, const Value &value, ExecutionContext *ctx) +void __qmljs_decrement(ExecutionContext *ctx, Value *result, const Value &value) { TRACE1(value); diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 2d7fe89856..3680c8d21d 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -201,13 +201,13 @@ Bool __qmljs_equal(const Value &x, const Value &y, ExecutionContext *ctx); Bool __qmljs_strict_equal(const Value &x, const Value &y); // unary operators -typedef void (*UnaryOpName)(Value *, const Value &, ExecutionContext* ctx); -void __qmljs_uplus(Value *result, const Value &value, ExecutionContext *ctx); -void __qmljs_uminus(Value *result, const Value &value, ExecutionContext *ctx); -void __qmljs_compl(Value *result, const Value &value, ExecutionContext *ctx); -void __qmljs_not(Value *result, const Value &value, ExecutionContext *ctx); -void __qmljs_increment(Value *result, const Value &value, ExecutionContext *ctx); -void __qmljs_decrement(Value *result, const Value &value, ExecutionContext *ctx); +typedef void (*UnaryOpName)(ExecutionContext *, Value *, const Value &); +void __qmljs_uplus(ExecutionContext *ctx, Value *result, const Value &value); +void __qmljs_uminus(ExecutionContext *ctx, Value *result, const Value &value); +void __qmljs_compl(ExecutionContext *ctx, Value *result, const Value &value); +void __qmljs_not(ExecutionContext *ctx, Value *result, const Value &value); +void __qmljs_increment(ExecutionContext *ctx, Value *result, const Value &value); +void __qmljs_decrement(ExecutionContext *ctx, Value *result, const Value &value); void __qmljs_delete_subscript(ExecutionContext *ctx, Value *result, const Value &base, const Value &index); void __qmljs_delete_member(ExecutionContext *ctx, Value *result, const Value &base, String *name); @@ -481,7 +481,7 @@ inline Value __qmljs_default_value(const Value &value, ExecutionContext *ctx, in } -inline void __qmljs_uplus(Value *result, const Value &value, ExecutionContext *ctx) +inline void __qmljs_uplus(ExecutionContext *ctx, Value *result, const Value &value) { TRACE1(value); @@ -493,7 +493,7 @@ inline void __qmljs_uplus(Value *result, const Value &value, ExecutionContext *c *result = Value::fromDouble(n); } -inline void __qmljs_uminus(Value *result, const Value &value, ExecutionContext *ctx) +inline void __qmljs_uminus(ExecutionContext *ctx, Value *result, const Value &value) { TRACE1(value); @@ -506,7 +506,7 @@ inline void __qmljs_uminus(Value *result, const Value &value, ExecutionContext * } } -inline void __qmljs_compl(Value *result, const Value &value, ExecutionContext *ctx) +inline void __qmljs_compl(ExecutionContext *ctx, Value *result, const Value &value) { TRACE1(value); @@ -519,7 +519,7 @@ inline void __qmljs_compl(Value *result, const Value &value, ExecutionContext *c *result = Value::fromInt32(~n); } -inline void __qmljs_not(Value *result, const Value &value, ExecutionContext *ctx) +inline void __qmljs_not(ExecutionContext *ctx, Value *result, const Value &value) { TRACE1(value); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index f304074dce..1d8a09ed1c 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -753,8 +753,8 @@ void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp * } // switch if (op) - _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::PointerToValue(targetTemp), Assembler::PointerToValue(sourceTemp), - Assembler::ContextRegister); + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, Assembler::PointerToValue(targetTemp), + Assembler::PointerToValue(sourceTemp)); } void InstructionSelection::binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target) -- cgit v1.2.3 From d6ad661feb23e50ab0287a1a47143696b9bb450e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Feb 2013 16:15:19 +0100 Subject: Ported run-time exception throwing functions to new calling convention Change-Id: Icc05eb78deb6d087a06f77d28b71fd49c9705e4c Reviewed-by: Lars Knoll --- src/v4/debugging.cpp | 4 ++-- src/v4/debugging.h | 2 +- src/v4/llvm_runtime.cpp | 2 +- src/v4/moth/qv4vme_moth.cpp | 2 +- src/v4/qmljs_environment.cpp | 4 ++-- src/v4/qmljs_environment.h | 2 +- src/v4/qmljs_runtime.cpp | 8 ++++---- src/v4/qmljs_runtime.h | 4 ++-- src/v4/qv4isel_masm.cpp | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/v4/debugging.cpp b/src/v4/debugging.cpp index 80afe01e60..9f53a4a9d5 100644 --- a/src/v4/debugging.cpp +++ b/src/v4/debugging.cpp @@ -159,9 +159,9 @@ void Debugger::leaveFunction(FunctionState *state) _callStack[callIndex(state->context())].state = 0; } -void Debugger::aboutToThrow(VM::Value *value) +void Debugger::aboutToThrow(const VM::Value &value) { - qDebug() << "*** We are about to throw...:" << value->toString(currentState()->context())->toQString(); + qDebug() << "*** We are about to throw...:" << value.toString(currentState()->context())->toQString(); } FunctionState *Debugger::currentState() const diff --git a/src/v4/debugging.h b/src/v4/debugging.h index ecb596fe6e..ee749de3b7 100644 --- a/src/v4/debugging.h +++ b/src/v4/debugging.h @@ -117,7 +117,7 @@ public: // execution hooks void justLeft(VM::ExecutionContext *context); void enterFunction(FunctionState *state); void leaveFunction(FunctionState *state); - void aboutToThrow(VM::Value *value); + void aboutToThrow(const VM::Value &value); public: // debugging hooks FunctionState *currentState() const; diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 61b95be300..9b1bc1130c 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -470,7 +470,7 @@ void __qmljs_llvm_typeof(ExecutionContext *ctx, Value *result, const Value *valu void __qmljs_llvm_throw(ExecutionContext *context, Value *value) { - __qmljs_throw(*value, context); + __qmljs_throw(context, *value); } void __qmljs_llvm_create_exception_handler(ExecutionContext *context, Value *result) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 0f9f6182c3..9c3901ed5d 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -244,7 +244,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallActivationProperty) MOTH_BEGIN_INSTR(CallBuiltinThrow) - __qmljs_builtin_throw(VALUE(instr.arg), context); + __qmljs_builtin_throw(context, VALUE(instr.arg)); MOTH_END_INSTR(CallBuiltinThrow) MOTH_BEGIN_INSTR(CallBuiltinCreateExceptionHandler) diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp index 801f8d7b72..f31c65c5d7 100644 --- a/src/v4/qmljs_environment.cpp +++ b/src/v4/qmljs_environment.cpp @@ -511,9 +511,9 @@ void ExecutionContext::inplaceBitOp(String *name, const Value &value, BinOp op) setProperty(name, result); } -void ExecutionContext::throwError(Value value) +void ExecutionContext::throwError(const Value &value) { - __qmljs_builtin_throw(value, this); + __qmljs_builtin_throw(this, value); } void ExecutionContext::throwError(const QString &message) diff --git a/src/v4/qmljs_environment.h b/src/v4/qmljs_environment.h index 473b2ea2b0..27de8b6007 100644 --- a/src/v4/qmljs_environment.h +++ b/src/v4/qmljs_environment.h @@ -121,7 +121,7 @@ struct ExecutionContext void wireUpPrototype(); - void throwError(Value value); + void throwError(const Value &value); void throwError(const QString &message); void throwSyntaxError(DiagnosticMessage *message); void throwTypeError(); diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index ed4cee27ac..ad871d62f6 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -975,12 +975,12 @@ void __qmljs_construct_property(ExecutionContext *context, Value *result, const context->throwTypeError(); } -void __qmljs_throw(Value value, ExecutionContext *context) +void __qmljs_throw(ExecutionContext *context, const Value &value) { assert(!context->engine->unwindStack.isEmpty()); if (context->engine->debugger) - context->engine->debugger->aboutToThrow(&value); + context->engine->debugger->aboutToThrow(value); ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); @@ -1228,9 +1228,9 @@ void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *re o->__put__(context, idx, v); } -void __qmljs_builtin_throw(Value val, ExecutionContext *context) +void __qmljs_builtin_throw(ExecutionContext *context, const Value &val) { - __qmljs_throw(val, context); + __qmljs_throw(context, val); } ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 3680c8d21d..98bc2e6f03 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -116,7 +116,7 @@ void __qmljs_builtin_post_decrement_name(ExecutionContext *context, Value *resul void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value &base, String *name); void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value &base, const Value &index); -void __qmljs_builtin_throw(Value val, ExecutionContext *context); +void __qmljs_builtin_throw(ExecutionContext *context, const Value &val); void __qmljs_builtin_rethrow(ExecutionContext *context); ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx); ExecutionContext *__qmljs_builtin_push_catch_scope(String *exceptionVarName, ExecutionContext *ctx); @@ -213,7 +213,7 @@ void __qmljs_delete_subscript(ExecutionContext *ctx, Value *result, const Value void __qmljs_delete_member(ExecutionContext *ctx, Value *result, const Value &base, String *name); void __qmljs_delete_name(ExecutionContext *ctx, Value *result, String *name); -void __qmljs_throw(Value value, ExecutionContext *context); +void __qmljs_throw(ExecutionContext*, const Value &value); // actually returns a jmp_buf * Q_V4_EXPORT void *__qmljs_create_exception_handler(ExecutionContext *context); void __qmljs_delete_exception_handler(ExecutionContext *context); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 1d8a09ed1c..19d237fda6 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -568,7 +568,7 @@ void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Te void InstructionSelection::callBuiltinThrow(IR::Temp *arg) { - generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, arg, Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, Assembler::ContextRegister, Assembler::PointerToValue(arg)); } void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp) -- cgit v1.2.3 From 61b941f7172af8be234f3755be2d0265e89096d7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 14 Feb 2013 16:18:01 +0100 Subject: Ported closure init runtime functions to new calling convention Change-Id: Icb5765069b296977480d896aacfbd09d64dbdad6 Reviewed-by: Lars Knoll --- src/v4/llvm_runtime.cpp | 2 +- src/v4/moth/qv4vme_moth.cpp | 2 +- src/v4/qmljs_runtime.cpp | 4 ++-- src/v4/qmljs_runtime.h | 2 +- src/v4/qv4isel_masm.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 9b1bc1130c..85cb816003 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -95,7 +95,7 @@ void __qmljs_llvm_init_closure(ExecutionContext *ctx, Value *result, hasNestedFunctions, formals, formalCount, locals, localCount); - *result = __qmljs_init_closure(clos, ctx); + __qmljs_init_closure(ctx, result, clos); } bool __qmljs_llvm_to_boolean(ExecutionContext *ctx, const Value *value) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 9c3901ed5d..3850955a14 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -172,7 +172,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(LoadValue) MOTH_BEGIN_INSTR(LoadClosure) - VALUE(instr.result) = __qmljs_init_closure(instr.value, context); + __qmljs_init_closure(context, VALUEPTR(instr.result), instr.value); MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index ad871d62f6..f41208b92f 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -114,10 +114,10 @@ QString numberToString(double num, int radix = 10) extern "C" { -Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx) +void __qmljs_init_closure(ExecutionContext *ctx, Value *result, VM::Function *clos) { assert(clos); - return Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); + *result = Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); } Function *__qmljs_register_function(ExecutionContext *ctx, String *name, diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 98bc2e6f03..39699d9c8a 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -127,7 +127,7 @@ void __qmljs_builtin_define_array_property(Value object, int index, Value val, E void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); // constructors -Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx); +void __qmljs_init_closure(ExecutionContext *ctx, Value *result, VM::Function *clos); VM::Function *__qmljs_register_function(ExecutionContext *ctx, String *name, bool hasDirectEval, bool usesArgumentsObject, bool isStrict, diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 19d237fda6..591de83058 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -690,7 +690,7 @@ void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) { VM::Function *vmFunc = vmFunction(closure->value); assert(vmFunc); - generateFunctionCall(target, __qmljs_init_closure, Assembler::TrustedImmPtr(vmFunc), Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_init_closure, Assembler::ContextRegister, Assembler::PointerToValue(target), Assembler::TrustedImmPtr(vmFunc)); } void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::Temp *target) -- cgit v1.2.3 From e2ebdffd15837d014d7549ea3f9be1f247d626dc Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 20:58:10 +0100 Subject: Adapt get/set_element to new calling convention Change-Id: I5e2bca8ee2435bf678dbf9eb15172ed59c80b52e Reviewed-by: Simon Hausmann --- src/v4/llvm_runtime.cpp | 2 +- src/v4/moth/qv4isel_moth.cpp | 2 +- src/v4/moth/qv4isel_moth_p.h | 2 +- src/v4/moth/qv4vme_moth.cpp | 2 +- src/v4/qmljs_runtime.cpp | 43 ++++++++++++++++++++++++++----------------- src/v4/qmljs_runtime.h | 9 ++++++--- src/v4/qv4isel_llvm.cpp | 2 +- src/v4/qv4isel_llvm_p.h | 2 +- src/v4/qv4isel_masm.cpp | 8 +++++--- src/v4/qv4isel_masm_p.h | 2 +- src/v4/qv4isel_p.cpp | 4 ++-- src/v4/qv4isel_p.h | 2 +- 12 files changed, 47 insertions(+), 33 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 85cb816003..2946b4e46b 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -445,7 +445,7 @@ void __qmljs_llvm_construct_property(ExecutionContext *context, Value *result, c void __qmljs_llvm_get_element(ExecutionContext *ctx, Value *result, Value *object, Value *index) { - *result = __qmljs_get_element(ctx, *object, *index); + __qmljs_get_element(ctx, result, *object, *index); } void __qmljs_llvm_set_element(ExecutionContext *ctx, Value *object, Value *index, Value *value) diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index fccd750c97..bd9fc249ee 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -463,7 +463,7 @@ void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp addInstruction(load); } -void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) +void InstructionSelection::setElement(IR::Temp *source, IR::Temp *targetBase, IR::Temp *targetIndex) { Instruction::StoreElement store; store.base = getParam(targetBase); diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h index 8a859d1e7c..04f8b9d5b4 100644 --- a/src/v4/moth/qv4isel_moth_p.h +++ b/src/v4/moth/qv4isel_moth_p.h @@ -71,7 +71,7 @@ protected: virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); virtual void setProperty(IR::Temp *source, IR::Temp *targetBase, const QString &targetName); virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target); - virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); + virtual void setElement(IR::Temp *source, IR::Temp *targetBase, IR::Temp *targetIndex); virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target); diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 3850955a14..4fea46310f 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -186,7 +186,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(StoreName) MOTH_BEGIN_INSTR(LoadElement) - VALUE(instr.result) = __qmljs_get_element(context, VALUE(instr.base), VALUE(instr.index)); + __qmljs_get_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index f41208b92f..8b9e4f6e97 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -581,43 +581,52 @@ void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *na o->__put__(ctx, name, value); } -Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) +void __qmljs_get_element(ExecutionContext *ctx, Value *result, const Value &object, const Value &index) { uint type = object.type(); uint idx = index.asArrayIndex(); - if (type != Value::Object_Type) { + Object *o = object.asObject(); + if (!o) { if (type == Value::String_Type && idx < UINT_MAX) { String *str = object.stringValue(); - if (idx >= (uint)str->toQString().length()) - return Value::undefinedValue(); + if (idx >= (uint)str->toQString().length()) { + if (result) + *result = Value::undefinedValue(); + return; + } const QString s = str->toQString().mid(idx, 1); - return Value::fromString(ctx, s); + if (result) + *result = Value::fromString(ctx, s); + return; } - object = __qmljs_to_object(object, ctx); + o = __qmljs_to_object(object, ctx).objectValue(); } - Object *o = object.objectValue(); - if (idx < UINT_MAX) { const PropertyDescriptor *p = o->nonSparseArrayAt(idx); - if (p && p->type == PropertyDescriptor::Data) - return p->value; + if (p && p->type == PropertyDescriptor::Data) { + if (result) + *result = p->value; + return; + } - return o->__get__(ctx, idx); + Value res = o->__get__(ctx, idx); + if (result) + *result = res; + return; } String *name = index.toString(ctx); - return o->__get__(ctx, name); + Value res = o->__get__(ctx, name); + if (result) + *result = res; } -void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value value) +void __qmljs_set_element(ExecutionContext *ctx, const Value &object, const Value &index, const Value &value) { - if (!object.isObject()) - object = __qmljs_to_object(object, ctx); - - Object *o = object.objectValue(); + Object *o = __qmljs_to_object(object, ctx).objectValue(); uint idx = index.asArrayIndex(); if (idx < UINT_MAX) { diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 39699d9c8a..7c4e438f74 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -174,8 +174,8 @@ void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Val void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value &object, int lookupIndex, const Value &value); -Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index); -void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value value); +void __qmljs_get_element(ExecutionContext *ctx, Value *retval, const Value &object, const Value &index); +void __qmljs_set_element(ExecutionContext *ctx, const Value &object, const Value &index, const Value &value); // For each Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx); @@ -428,6 +428,9 @@ inline Value __qmljs_to_string(const Value &value, ExecutionContext *ctx) inline Value __qmljs_to_object(const Value &value, ExecutionContext *ctx) { + if (value.isObject()) + return value; + switch (value.type()) { case Value::Undefined_Type: case Value::Null_Type: @@ -440,7 +443,7 @@ inline Value __qmljs_to_object(const Value &value, ExecutionContext *ctx) return __qmljs_new_string_object(ctx, value.stringValue()); break; case Value::Object_Type: - return value; + Q_UNREACHABLE(); break; case Value::Integer_Type: return __qmljs_new_number_object(ctx, value.int_32); diff --git a/src/v4/qv4isel_llvm.cpp b/src/v4/qv4isel_llvm.cpp index 26411d80c7..a10328b005 100644 --- a/src/v4/qv4isel_llvm.cpp +++ b/src/v4/qv4isel_llvm.cpp @@ -647,7 +647,7 @@ void InstructionSelection::getElement(IR::Temp *sourceBase, IR::Temp *sourceInde _llvmFunction->arg_begin(), t, base, index); } -void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) +void InstructionSelection::setElement(IR::Temp *source, IR::Temp *targetBase, IR::Temp *targetIndex) { llvm::Value *base = getLLVMTempReference(targetBase); llvm::Value *index = getLLVMTempReference(targetIndex); diff --git a/src/v4/qv4isel_llvm_p.h b/src/v4/qv4isel_llvm_p.h index 3f6dc47558..9b4aa365e0 100644 --- a/src/v4/qv4isel_llvm_p.h +++ b/src/v4/qv4isel_llvm_p.h @@ -115,7 +115,7 @@ public: // methods from InstructionSelection: virtual void getProperty(IR::Temp *sourceBase, const QString &sourceName, IR::Temp *target); virtual void setProperty(IR::Temp *source, IR::Temp *targetBase, const QString &targetName); virtual void getElement(IR::Temp *sourceBase, IR::Temp *sourceIndex, IR::Temp *target); - virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); + virtual void setElement(IR::Temp *source, IR::Temp *targetBase, IR::Temp *targetIndex); virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 591de83058..7dfd8ed43d 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -721,12 +721,14 @@ void InstructionSelection::setProperty(IR::Temp *source, IR::Temp *targetBase, c void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) { - generateFunctionCall(target, __qmljs_get_element, Assembler::ContextRegister, base, index); + generateFunctionCall(Assembler::Void, __qmljs_get_element, Assembler::ContextRegister, + Assembler::PointerToValue(target), Assembler::PointerToValue(base), Assembler::PointerToValue(index)); } -void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) +void InstructionSelection::setElement(IR::Temp *source, IR::Temp *targetBase, IR::Temp *targetIndex) { - generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, targetBase, targetIndex, source); + generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, + Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex), Assembler::PointerToValue(source)); } void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 77d1ce933c..08ab7ea23b 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -721,7 +721,7 @@ protected: virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); virtual void setProperty(IR::Temp *source, IR::Temp *targetBase, const QString &targetName); virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target); - virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); + virtual void setElement(IR::Temp *source, IR::Temp *targetBase, IR::Temp *targetIndex); virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); virtual void binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target); diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index eed2d0b358..56f4ce74c4 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -151,8 +151,8 @@ void InstructionSelection::visitMove(IR::Move *s) } } } else if (IR::Subscript *ss = s->target->asSubscript()) { - if (s->source->asTemp() || s->source->asConst()) { - setElement(s->source, ss->base->asTemp(), ss->index->asTemp()); + if (s->source->asTemp()) { + setElement(s->source->asTemp(), ss->base->asTemp(), ss->index->asTemp()); return; } } diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h index d41bd78fb2..b336e3a60b 100644 --- a/src/v4/qv4isel_p.h +++ b/src/v4/qv4isel_p.h @@ -131,7 +131,7 @@ public: // to implement by subclasses: virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target) = 0; virtual void setProperty(IR::Temp *source, IR::Temp *targetBase, const QString &targetName) = 0; virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) = 0; - virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) = 0; + virtual void setElement(IR::Temp *source, IR::Temp *targetBase, IR::Temp *targetIndex) = 0; virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0; virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0; virtual void binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target) = 0; -- cgit v1.2.3 From 37c2b54239ebb83812c9b5d3d51b86211a83ac51 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 21:41:49 +0100 Subject: Fix a few more runtime signatures and optimise toObject conversion Change-Id: Ibd1e4b7f2c9609b4ac08d75c8a0e2d5a86521605 Reviewed-by: Simon Hausmann --- src/v4/qmljs_runtime.cpp | 151 +++++++++++++++++++++---------------------- src/v4/qmljs_runtime.h | 32 ++------- src/v4/qmljs_value.cpp | 5 -- src/v4/qmljs_value.h | 10 ++- src/v4/qv4arrayobject.cpp | 34 +++++----- src/v4/qv4dateobject.cpp | 2 +- src/v4/qv4functionobject.cpp | 4 +- src/v4/qv4objectproto.cpp | 24 +++---- 8 files changed, 120 insertions(+), 142 deletions(-) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 8b9e4f6e97..e36e4bfe7e 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -211,8 +211,8 @@ void __qmljs_delete_subscript(ExecutionContext *ctx, Value *result, const Value void __qmljs_delete_member(ExecutionContext *ctx, Value *result, const Value &base, String *name) { - Value obj = base.toObject(ctx); - Value res = Value::fromBoolean(obj.objectValue()->__delete__(ctx, name)); + Object *obj = base.toObject(ctx); + Value res = Value::fromBoolean(obj->__delete__(ctx, name)); if (result) *result = res; } @@ -318,133 +318,133 @@ void __qmljs_inplace_ushr_name(ExecutionContext *ctx, String *name, const Value void __qmljs_inplace_bit_and_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base.toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx); obj->inplaceBinOp(ctx, __qmljs_bit_and, index, rhs); } void __qmljs_inplace_bit_or_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base.toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx); obj->inplaceBinOp(ctx, __qmljs_bit_or, index, rhs); } void __qmljs_inplace_bit_xor_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base.toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx); obj->inplaceBinOp(ctx, __qmljs_bit_xor, index, rhs); } void __qmljs_inplace_add_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base.toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx); obj->inplaceBinOp(ctx, __qmljs_add, index, rhs); } void __qmljs_inplace_sub_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base.toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx); obj->inplaceBinOp(ctx, __qmljs_sub, index, rhs); } void __qmljs_inplace_mul_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base.toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx); obj->inplaceBinOp(ctx, __qmljs_mul, index, rhs); } void __qmljs_inplace_div_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base.toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx); obj->inplaceBinOp(ctx, __qmljs_div, index, rhs); } void __qmljs_inplace_mod_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base.toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx); obj->inplaceBinOp(ctx, __qmljs_mod, index, rhs); } void __qmljs_inplace_shl_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base.toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx); obj->inplaceBinOp(ctx, __qmljs_shl, index, rhs); } void __qmljs_inplace_shr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base.toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx); obj->inplaceBinOp(ctx, __qmljs_shr, index, rhs); } void __qmljs_inplace_ushr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) { - Object *obj = base.toObject(ctx).objectValue(); + Object *obj = base.toObject(ctx); obj->inplaceBinOp(ctx, __qmljs_ushr, index, rhs); } void __qmljs_inplace_bit_and_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base.toObject(ctx).objectValue(); + Object *o = base.toObject(ctx); o->inplaceBinOp(ctx, __qmljs_bit_and, name, rhs); } void __qmljs_inplace_bit_or_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base.toObject(ctx).objectValue(); + Object *o = base.toObject(ctx); o->inplaceBinOp(ctx, __qmljs_bit_or, name, rhs); } void __qmljs_inplace_bit_xor_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base.toObject(ctx).objectValue(); + Object *o = base.toObject(ctx); o->inplaceBinOp(ctx, __qmljs_bit_xor, name, rhs); } void __qmljs_inplace_add_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base.toObject(ctx).objectValue(); + Object *o = base.toObject(ctx); o->inplaceBinOp(ctx, __qmljs_add, name, rhs); } void __qmljs_inplace_sub_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base.toObject(ctx).objectValue(); + Object *o = base.toObject(ctx); o->inplaceBinOp(ctx, __qmljs_sub, name, rhs); } void __qmljs_inplace_mul_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base.toObject(ctx).objectValue(); + Object *o = base.toObject(ctx); o->inplaceBinOp(ctx, __qmljs_mul, name, rhs); } void __qmljs_inplace_div_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base.toObject(ctx).objectValue(); + Object *o = base.toObject(ctx); o->inplaceBinOp(ctx, __qmljs_div, name, rhs); } void __qmljs_inplace_mod_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base.toObject(ctx).objectValue(); + Object *o = base.toObject(ctx); o->inplaceBinOp(ctx, __qmljs_mod, name, rhs); } void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base.toObject(ctx).objectValue(); + Object *o = base.toObject(ctx); o->inplaceBinOp(ctx, __qmljs_shl, name, rhs); } void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base.toObject(ctx).objectValue(); + Object *o = base.toObject(ctx); o->inplaceBinOp(ctx, __qmljs_shr, name, rhs); } void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) { - Object *o = base.toObject(ctx).objectValue(); + Object *o = base.toObject(ctx); o->inplaceBinOp(ctx, __qmljs_ushr, name, rhs); } @@ -552,32 +552,30 @@ void __qmljs_throw_type_error(ExecutionContext *ctx) ctx->throwTypeError(); } -Value __qmljs_new_object(ExecutionContext *ctx) +Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value) { - return Value::fromObject(ctx->engine->newObject()); -} - -Value __qmljs_new_boolean_object(ExecutionContext *ctx, bool boolean) -{ - Value value = Value::fromBoolean(boolean); - return Value::fromObject(ctx->engine->newBooleanObject(value)); -} - -Value __qmljs_new_number_object(ExecutionContext *ctx, double number) -{ - Value value = Value::fromDouble(number); - return Value::fromObject(ctx->engine->newNumberObject(value)); + assert(!value.isObject()); + switch (value.type()) { + case Value::Undefined_Type: + case Value::Null_Type: + __qmljs_throw_type_error(ctx); + case Value::Boolean_Type: + return ctx->engine->newBooleanObject(value); + case Value::String_Type: + return ctx->engine->newStringObject(ctx, value); + break; + case Value::Object_Type: + Q_UNREACHABLE(); + case Value::Integer_Type: + default: // double + return ctx->engine->newNumberObject(value); + } } -Value __qmljs_new_string_object(ExecutionContext *ctx, String *string) -{ - Value value = Value::fromString(string); - return Value::fromObject(ctx->engine->newStringObject(ctx, value)); -} void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *name, const Value &value) { - Object *o = object.isObject() ? object.objectValue() : __qmljs_to_object(object, ctx).objectValue(); + Object *o = object.toObject(ctx); o->__put__(ctx, name, value); } @@ -601,7 +599,7 @@ void __qmljs_get_element(ExecutionContext *ctx, Value *result, const Value &obje return; } - o = __qmljs_to_object(object, ctx).objectValue(); + o = __qmljs_convert_to_object(ctx, object); } if (idx < UINT_MAX) { @@ -626,7 +624,7 @@ void __qmljs_get_element(ExecutionContext *ctx, Value *result, const Value &obje void __qmljs_set_element(ExecutionContext *ctx, const Value &object, const Value &index, const Value &value) { - Object *o = __qmljs_to_object(object, ctx).objectValue(); + Object *o = object.toObject(ctx); uint idx = index.asArrayIndex(); if (idx < UINT_MAX) { @@ -645,9 +643,10 @@ void __qmljs_set_element(ExecutionContext *ctx, const Value &object, const Value Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx) { + Object *o = 0; if (!in.isNull() && !in.isUndefined()) - in = __qmljs_to_object(in, ctx); - Object *it = ctx->engine->newForEachIteratorObject(ctx, in.asObject()); + o = in.toObject(ctx); + Object *it = ctx->engine->newForEachIteratorObject(ctx, o); return Value::fromObject(it); } @@ -676,7 +675,7 @@ void __qmljs_get_property(ExecutionContext *ctx, Value *result, const Value &obj } else if (object.isString() && name->isEqualTo(ctx->engine->id_length)) { res = Value::fromInt32(object.stringValue()->toQString().length()); } else { - o = __qmljs_to_object(object, ctx).objectValue(); + o = __qmljs_convert_to_object(ctx, object); res = o->__get__(ctx, name); } if (result) @@ -728,7 +727,7 @@ void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Val if (object.isString() && l->name->isEqualTo(ctx->engine->id_length)) { res = Value::fromInt32(object.stringValue()->toQString().length()); } else { - o = __qmljs_to_object(object, ctx).objectValue(); + o = __qmljs_convert_to_object(ctx, object); res = o->__get__(ctx, l->name); } } @@ -738,7 +737,7 @@ void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Val void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value &object, int lookupIndex, const Value &value) { - Object *o = object.isObject() ? object.objectValue() : __qmljs_to_object(object, ctx).objectValue(); + Object *o = object.toObject(ctx); Lookup *l = ctx->lookups + lookupIndex; if (l->index != ArrayObject::LengthPropertyIndex || !o->isArrayObject()) { @@ -846,10 +845,10 @@ void __qmljs_call_property(ExecutionContext *context, Value *result, const Value baseObject = context->engine->stringPrototype; } else { if (!thisObject.isObject()) - thisObject = __qmljs_to_object(thisObject, context); + thisObject = Value::fromObject(__qmljs_convert_to_object(context, thisObject)); assert(thisObject.isObject()); - baseObject = thisObject.objectValue(); + baseObject = thisObject.objectValue(); } Value func = baseObject->__get__(context, name); @@ -872,10 +871,10 @@ void __qmljs_call_property_lookup(ExecutionContext *context, Value *result, cons baseObject = context->engine->stringPrototype; } else { if (!thisObject.isObject()) - thisObject = __qmljs_to_object(thisObject, context); + thisObject = Value::fromObject(__qmljs_convert_to_object(context, thisObject)); - assert(thisObject.isObject()); - baseObject = thisObject.objectValue(); + assert(thisObject.isObject()); + baseObject = thisObject.objectValue(); } PropertyDescriptor *p = 0; @@ -922,12 +921,8 @@ void __qmljs_call_property_lookup(ExecutionContext *context, Value *result, cons void __qmljs_call_element(ExecutionContext *context, Value *result, const Value &that, const Value &index, Value *args, int argc) { - Value thisObject = that; - if (!thisObject.isObject()) - thisObject = __qmljs_to_object(thisObject, context); - - assert(thisObject.isObject()); - Object *baseObject = thisObject.objectValue(); + Object *baseObject = that.toObject(context); + Value thisObject = Value::fromObject(baseObject); Value func = baseObject->__get__(context, index.toString(context)); FunctionObject *o = func.asFunctionObject(); @@ -969,11 +964,9 @@ void __qmljs_construct_value(ExecutionContext *context, Value *result, const Val void __qmljs_construct_property(ExecutionContext *context, Value *result, const Value &base, String *name, Value *args, int argc) { - Value thisObject = base; - if (!thisObject.isObject()) - thisObject = __qmljs_to_object(base, context); + Object *thisObject = base.toObject(context); - Value func = thisObject.objectValue()->__get__(context, name); + Value func = thisObject->__get__(context, name); if (FunctionObject *f = func.asFunctionObject()) { Value res = f->construct(context, args, argc); if (result) @@ -1058,23 +1051,29 @@ void __qmljs_builtin_typeof(ExecutionContext *ctx, Value *result, const Value &v void __qmljs_builtin_typeof_name(ExecutionContext *context, Value *result, String *name) { + Value res; + __qmljs_builtin_typeof(context, &res, context->getPropertyNoThrow(name)); if (result) - __qmljs_builtin_typeof(context, result, context->getPropertyNoThrow(name)); + *result = res; } void __qmljs_builtin_typeof_member(ExecutionContext *context, Value *result, const Value &base, String *name) { - Value obj = base.toObject(context); + Object *obj = base.toObject(context); + Value res; + __qmljs_builtin_typeof(context, &res, obj->__get__(context, name)); if (result) - __qmljs_builtin_typeof(context, result, obj.objectValue()->__get__(context, name)); + *result = res; } void __qmljs_builtin_typeof_element(ExecutionContext *context, Value *result, const Value &base, const Value &index) { String *name = index.toString(context); - Value obj = base.toObject(context); + Object *obj = base.toObject(context); + Value res; + __qmljs_builtin_typeof(context, &res, obj->__get__(context, name)); if (result) - __qmljs_builtin_typeof(context, result, obj.objectValue()->__get__(context, name)); + *result = res; } void __qmljs_builtin_post_increment(ExecutionContext *ctx, Value *result, Value *val) @@ -1112,7 +1111,7 @@ void __qmljs_builtin_post_increment_name(ExecutionContext *context, Value *resul void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *result, const Value &base, String *name) { - Object *o = __qmljs_to_object(base, context).objectValue(); + Object *o = base.toObject(context); Value v = o->__get__(context, name); @@ -1132,7 +1131,7 @@ void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *res void __qmljs_builtin_post_increment_element(ExecutionContext *context, Value *result, const Value &base, const Value *index) { - Object *o = __qmljs_to_object(base, context).objectValue(); + Object *o = base.toObject(context); uint idx = index->asArrayIndex(); @@ -1192,7 +1191,7 @@ void __qmljs_builtin_post_decrement_name(ExecutionContext *context, Value *resul void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value &base, String *name) { - Object *o = __qmljs_to_object(base, context).objectValue(); + Object *o = base.toObject(context); Value v = o->__get__(context, name); @@ -1212,7 +1211,7 @@ void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *res void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value &base, const Value &index) { - Object *o = __qmljs_to_object(base, context).objectValue(); + Object *o = base.toObject(context); uint idx = index.asArrayIndex(); @@ -1244,7 +1243,7 @@ void __qmljs_builtin_throw(ExecutionContext *context, const Value &val) ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx) { - Object *obj = __qmljs_to_object(o, ctx).asObject(); + Object *obj = o.toObject(ctx); return ctx->createWithScope(obj); } diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 7c4e438f74..5de6cc3edd 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -161,10 +161,6 @@ String *__qmljs_identifier_from_utf8(ExecutionContext *ctx, const char *s); // objects Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int typeHint); void __qmljs_throw_type_error(ExecutionContext *ctx); -Value __qmljs_new_object(ExecutionContext *ctx); -Value __qmljs_new_boolean_object(ExecutionContext *ctx, bool boolean); -Value __qmljs_new_number_object(ExecutionContext *ctx, double n); -Value __qmljs_new_string_object(ExecutionContext *ctx, String *string); void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value& value); void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *name, const Value &value); void __qmljs_get_property(ExecutionContext *ctx, Value *result, const Value &object, String *name); @@ -192,7 +188,8 @@ double __qmljs_to_integer(const Value &value, ExecutionContext *ctx); int __qmljs_to_int32(const Value &value, ExecutionContext *ctx); unsigned short __qmljs_to_uint16(const Value &value, ExecutionContext *ctx); Value __qmljs_to_string(const Value &value, ExecutionContext *ctx); -Value __qmljs_to_object(const Value &value, ExecutionContext *ctx); +Value __qmljs_to_object(ExecutionContext *ctx, const Value &value); +Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value); //uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *value); Bool __qmljs_is_callable(const Value &value, ExecutionContext *ctx); Value __qmljs_default_value(const Value &value, ExecutionContext *ctx, int typeHint); @@ -426,32 +423,11 @@ inline Value __qmljs_to_string(const Value &value, ExecutionContext *ctx) } // switch } -inline Value __qmljs_to_object(const Value &value, ExecutionContext *ctx) +inline Value __qmljs_to_object(ExecutionContext *ctx, const Value &value) { if (value.isObject()) return value; - - switch (value.type()) { - case Value::Undefined_Type: - case Value::Null_Type: - __qmljs_throw_type_error(ctx); - break; - case Value::Boolean_Type: - return __qmljs_new_boolean_object(ctx, value.booleanValue()); - break; - case Value::String_Type: - return __qmljs_new_string_object(ctx, value.stringValue()); - break; - case Value::Object_Type: - Q_UNREACHABLE(); - break; - case Value::Integer_Type: - return __qmljs_new_number_object(ctx, value.int_32); - break; - default: // double - return __qmljs_new_number_object(ctx, value.doubleValue()); - break; - } + return Value::fromObject(__qmljs_convert_to_object(ctx, value)); } /* diff --git a/src/v4/qmljs_value.cpp b/src/v4/qmljs_value.cpp index 83dd9c621c..042a1e0039 100644 --- a/src/v4/qmljs_value.cpp +++ b/src/v4/qmljs_value.cpp @@ -75,11 +75,6 @@ String *Value::toString(ExecutionContext *ctx) const return v.stringValue(); } -Value Value::toObject(ExecutionContext *ctx) const -{ - return __qmljs_to_object(*this, ctx); -} - bool Value::sameValue(Value other) const { if (val == other.val) return true; diff --git a/src/v4/qmljs_value.h b/src/v4/qmljs_value.h index 2675d5535f..d0639c8518 100644 --- a/src/v4/qmljs_value.h +++ b/src/v4/qmljs_value.h @@ -60,6 +60,7 @@ struct Value; extern "C" { double __qmljs_to_number(const Value &value, ExecutionContext *ctx); +Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value); } typedef uint Bool; @@ -207,7 +208,7 @@ struct Q_V4_EXPORT Value double toInteger(ExecutionContext *ctx) const; double toNumber(ExecutionContext *ctx) const; String *toString(ExecutionContext *ctx) const; - Value toObject(ExecutionContext *ctx) const; + Object *toObject(ExecutionContext *ctx) const; inline bool isPrimitive() const { return !isObject(); } #if CPU(X86_64) @@ -351,6 +352,13 @@ inline Value Value::fromObject(Object *o) return v; } +inline Object *Value::toObject(ExecutionContext *ctx) const +{ + if (isObject()) + return objectValue(); + return __qmljs_convert_to_object(ctx, *this); +} + inline int Value::toInt32(ExecutionContext *ctx) const { if (isConvertibleToInt()) diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp index 8117a657e1..69edf9ed0f 100644 --- a/src/v4/qv4arrayobject.cpp +++ b/src/v4/qv4arrayobject.cpp @@ -218,7 +218,7 @@ Value ArrayPrototype::method_join(ExecutionContext *ctx) Value ArrayPrototype::method_pop(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); if (!len) { @@ -239,7 +239,7 @@ Value ArrayPrototype::method_pop(ExecutionContext *ctx) Value ArrayPrototype::method_push(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); if (len + ctx->argumentCount < len) { @@ -296,7 +296,7 @@ Value ArrayPrototype::method_push(ExecutionContext *ctx) Value ArrayPrototype::method_reverse(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint length = getLength(ctx, instance); int lo = 0, hi = length - 1; @@ -319,7 +319,7 @@ Value ArrayPrototype::method_reverse(ExecutionContext *ctx) Value ArrayPrototype::method_shift(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); if (!len) { @@ -382,7 +382,7 @@ Value ArrayPrototype::method_shift(ExecutionContext *ctx) Value ArrayPrototype::method_slice(ExecutionContext *ctx) { - Object *o = ctx->thisObject.toObject(ctx).objectValue(); + Object *o = ctx->thisObject.toObject(ctx); ArrayObject *result = ctx->engine->newArrayObject(ctx); uint len = o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx); @@ -419,7 +419,7 @@ Value ArrayPrototype::method_slice(ExecutionContext *ctx) Value ArrayPrototype::method_sort(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); @@ -430,7 +430,7 @@ Value ArrayPrototype::method_sort(ExecutionContext *ctx) Value ArrayPrototype::method_splice(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); ArrayObject *newArray = ctx->engine->newArrayObject(ctx); @@ -494,7 +494,7 @@ Value ArrayPrototype::method_splice(ExecutionContext *ctx) Value ArrayPrototype::method_unshift(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); bool protoHasArray = false; @@ -546,7 +546,7 @@ Value ArrayPrototype::method_unshift(ExecutionContext *ctx) Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); if (!len) return Value::fromInt32(-1); @@ -583,7 +583,7 @@ Value ArrayPrototype::method_indexOf(ExecutionContext *ctx) Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); if (!len) return Value::fromInt32(-1); @@ -620,7 +620,7 @@ Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx) Value ArrayPrototype::method_every(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); @@ -649,7 +649,7 @@ Value ArrayPrototype::method_every(ExecutionContext *ctx) Value ArrayPrototype::method_some(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); @@ -678,7 +678,7 @@ Value ArrayPrototype::method_some(ExecutionContext *ctx) Value ArrayPrototype::method_forEach(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); @@ -705,7 +705,7 @@ Value ArrayPrototype::method_forEach(ExecutionContext *ctx) Value ArrayPrototype::method_map(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); @@ -737,7 +737,7 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) Value ArrayPrototype::method_filter(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); @@ -772,7 +772,7 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) Value ArrayPrototype::method_reduce(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); @@ -814,7 +814,7 @@ Value ArrayPrototype::method_reduce(ExecutionContext *ctx) Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) { - Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *instance = ctx->thisObject.toObject(ctx); uint len = getLength(ctx, instance); diff --git a/src/v4/qv4dateobject.cpp b/src/v4/qv4dateobject.cpp index 77ed279c8e..72a96af436 100644 --- a/src/v4/qv4dateobject.cpp +++ b/src/v4/qv4dateobject.cpp @@ -1302,7 +1302,7 @@ Value DatePrototype::method_toISOString(ExecutionContext *ctx) Value DatePrototype::method_toJSON(ExecutionContext *ctx) { - Value O = __qmljs_to_object(ctx->thisObject, ctx); + Value O = __qmljs_to_object(ctx, ctx->thisObject); Value tv = __qmljs_to_primitive(O, ctx, NUMBER_HINT); if (tv.isNumber() && !isfinite(tv.toNumber(ctx))) diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index af23182026..013bdca3e0 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -397,7 +397,7 @@ Value ScriptFunction::call(Managed *that, ExecutionContext *context, const Value if (!f->isBuiltinFunction) ctx->thisObject = context->engine->globalObject; } else { - ctx->thisObject = thisObject.toObject(context); + ctx->thisObject = Value::fromObject(thisObject.toObject(context)); } } @@ -445,7 +445,7 @@ Value BuiltinFunctionOld::call(Managed *that, ExecutionContext *context, const V if (!f->isBuiltinFunction) ctx->thisObject = context->engine->globalObject; } else { - ctx->thisObject = thisObject.toObject(context); + ctx->thisObject = Value::fromObject(thisObject.toObject(context)); } } diff --git a/src/v4/qv4objectproto.cpp b/src/v4/qv4objectproto.cpp index 6e9f51e6bf..0af8c1da20 100644 --- a/src/v4/qv4objectproto.cpp +++ b/src/v4/qv4objectproto.cpp @@ -89,14 +89,14 @@ Value ObjectCtor::construct(Managed *that, ExecutionContext *ctx, Value *args, i obj->prototype = proto.objectValue(); return Value::fromObject(obj); } - return __qmljs_to_object(args[0], ctx); + return __qmljs_to_object(ctx, args[0]); } Value ObjectCtor::call(Managed *, ExecutionContext *ctx, const Value &/*thisObject*/, Value *args, int argc) { if (!argc || args[0].isUndefined() || args[0].isNull()) return Value::fromObject(ctx->engine->newObject()); - return __qmljs_to_object(args[0], ctx); + return __qmljs_to_object(ctx, args[0]); } void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) @@ -208,7 +208,7 @@ Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) if (!O.isObject()) ctx->throwTypeError(); - Object *o = ctx->argument(1).toObject(ctx).objectValue(); + Object *o = ctx->argument(1).toObject(ctx); ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); while (1) { @@ -372,7 +372,7 @@ Value ObjectPrototype::method_toString(ExecutionContext *ctx) } else if (ctx->thisObject.isNull()) { return Value::fromString(ctx, QStringLiteral("[object Null]")); } else { - Value obj = __qmljs_to_object(ctx->thisObject, ctx); + Value obj = __qmljs_to_object(ctx, ctx->thisObject); QString className = obj.objectValue()->className(); return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(className)); } @@ -380,7 +380,7 @@ Value ObjectPrototype::method_toString(ExecutionContext *ctx) Value ObjectPrototype::method_toLocaleString(ExecutionContext *ctx) { - Object *o = __qmljs_to_object(ctx->thisObject, ctx).objectValue(); + Object *o = ctx->thisObject.toObject(ctx); Value ts = o->__get__(ctx, ctx->engine->newString(QStringLiteral("toString"))); FunctionObject *f = ts.asFunctionObject(); if (!f) @@ -390,14 +390,14 @@ Value ObjectPrototype::method_toLocaleString(ExecutionContext *ctx) Value ObjectPrototype::method_valueOf(ExecutionContext *ctx) { - return ctx->thisObject.toObject(ctx); + return Value::fromObject(ctx->thisObject.toObject(ctx)); } Value ObjectPrototype::method_hasOwnProperty(ExecutionContext *ctx) { String *P = ctx->argument(0).toString(ctx); - Value O = ctx->thisObject.toObject(ctx); - bool r = O.objectValue()->__getOwnProperty__(ctx, P) != 0; + Object *O = ctx->thisObject.toObject(ctx); + bool r = O->__getOwnProperty__(ctx, P) != 0; return Value::fromBoolean(r); } @@ -407,7 +407,7 @@ Value ObjectPrototype::method_isPrototypeOf(ExecutionContext *ctx) if (! V.isObject()) return Value::fromBoolean(false); - Object *O = ctx->thisObject.toObject(ctx).objectValue(); + Object *O = ctx->thisObject.toObject(ctx); Object *proto = V.objectValue()->prototype; while (proto) { if (O == proto) @@ -421,7 +421,7 @@ Value ObjectPrototype::method_propertyIsEnumerable(ExecutionContext *ctx) { String *p = ctx->argument(0).toString(ctx); - Object *o = ctx->thisObject.toObject(ctx).objectValue(); + Object *o = ctx->thisObject.toObject(ctx); PropertyDescriptor *pd = o->__getOwnProperty__(ctx, p); return Value::fromBoolean(pd && pd->isEnumerable()); } @@ -436,7 +436,7 @@ Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx) if (!f) __qmljs_throw_type_error(ctx); - Object *o = ctx->thisObject.toObject(ctx).objectValue(); + Object *o = ctx->thisObject.toObject(ctx); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(f, 0); pd.configurable = PropertyDescriptor::Enabled; @@ -455,7 +455,7 @@ Value ObjectPrototype::method_defineSetter(ExecutionContext *ctx) if (!f) __qmljs_throw_type_error(ctx); - Object *o = ctx->thisObject.toObject(ctx).objectValue(); + Object *o = ctx->thisObject.toObject(ctx); PropertyDescriptor pd = PropertyDescriptor::fromAccessor(0, f); pd.configurable = PropertyDescriptor::Enabled; -- cgit v1.2.3 From a7e9ea3c1fd2e4e5a3daa5796f47dfad6fa43421 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 23:00:11 +0100 Subject: Various cleanups in the runtime Remove unused code, inline where we only use a method once. Change-Id: I1896efc3f4d309082aff2f80f944e19c1ede2f50 Reviewed-by: Simon Hausmann --- src/v4/llvm_runtime.cpp | 5 +- src/v4/qmljs_engine.cpp | 9 ++++ src/v4/qmljs_engine.h | 9 ++++ src/v4/qmljs_runtime.cpp | 113 ++++++++++++++++---------------------------- src/v4/qmljs_runtime.h | 120 ++--------------------------------------------- src/v4/qmljs_value.cpp | 35 ++++++++++---- src/v4/qmljs_value.h | 8 ++++ 7 files changed, 101 insertions(+), 198 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 2946b4e46b..7e6f4380bd 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -41,6 +41,7 @@ #include "qmljs_runtime.h" #include "qmljs_environment.h" +#include "qmljs_engine.h" #include #include @@ -80,7 +81,7 @@ void __qmljs_llvm_init_number(Value *result, double value) void __qmljs_llvm_init_string(ExecutionContext *ctx, Value *result, const char *str) { - *result = Value::fromString(__qmljs_string_from_utf8(ctx, str)); + *result = Value::fromString(ctx->engine->newString(QString::fromUtf8(str))); } void __qmljs_llvm_init_closure(ExecutionContext *ctx, Value *result, @@ -395,7 +396,7 @@ void __qmljs_llvm_inplace_ushr_member(ExecutionContext *ctx, Value *value, Value String *__qmljs_llvm_identifier_from_utf8(ExecutionContext *ctx, const char *str) { - return __qmljs_identifier_from_utf8(ctx, str); // ### make it unique + return ctx->engine->newIdentifier(QString::fromUtf8(str)); } void __qmljs_llvm_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) diff --git a/src/v4/qmljs_engine.cpp b/src/v4/qmljs_engine.cpp index b71111af22..9e10efdeab 100644 --- a/src/v4/qmljs_engine.cpp +++ b/src/v4/qmljs_engine.cpp @@ -80,6 +80,15 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) rootContext->init(this); current = rootContext; + id_undefined = newIdentifier(QStringLiteral("undefined")); + id_null = newIdentifier(QStringLiteral("null")); + id_true = newIdentifier(QStringLiteral("true")); + id_false = newIdentifier(QStringLiteral("false")); + id_boolean = newIdentifier(QStringLiteral("boolean")); + id_number = newIdentifier(QStringLiteral("number")); + id_string = newIdentifier(QStringLiteral("string")); + id_object = newIdentifier(QStringLiteral("object")); + id_function = newIdentifier(QStringLiteral("function")); id_length = newIdentifier(QStringLiteral("length")); id_prototype = newIdentifier(QStringLiteral("prototype")); id_constructor = newIdentifier(QStringLiteral("constructor")); diff --git a/src/v4/qmljs_engine.h b/src/v4/qmljs_engine.h index 7aaefd4d97..70a7fd9fc3 100644 --- a/src/v4/qmljs_engine.h +++ b/src/v4/qmljs_engine.h @@ -150,6 +150,15 @@ struct Q_V4_EXPORT ExecutionEngine QVector argumentsAccessors; + String *id_undefined; + String *id_null; + String *id_true; + String *id_false; + String *id_boolean; + String *id_number; + String *id_string; + String *id_object; + String *id_function; String *id_length; String *id_prototype; String *id_constructor; diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index e36e4bfe7e..11ddbb268d 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -144,51 +144,6 @@ Function *__qmljs_register_function(ExecutionContext *ctx, String *name, return f; } -Value __qmljs_string_literal_undefined(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->newString(QStringLiteral("undefined"))); -} - -Value __qmljs_string_literal_null(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->newString(QStringLiteral("null"))); -} - -Value __qmljs_string_literal_true(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->newString(QStringLiteral("true"))); -} - -Value __qmljs_string_literal_false(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->newString(QStringLiteral("false"))); -} - -Value __qmljs_string_literal_object(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->newString(QStringLiteral("object"))); -} - -Value __qmljs_string_literal_boolean(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->newString(QStringLiteral("boolean"))); -} - -Value __qmljs_string_literal_number(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->newString(QStringLiteral("number"))); -} - -Value __qmljs_string_literal_string(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->newString(QStringLiteral("string"))); -} - -Value __qmljs_string_literal_function(ExecutionContext *ctx) -{ - return Value::fromString(ctx->engine->newString(QStringLiteral("function"))); -} - void __qmljs_delete_subscript(ExecutionContext *ctx, Value *result, const Value &base, const Value &index) { if (Object *o = base.asObject()) { @@ -448,21 +403,11 @@ void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value &base, Strin o->inplaceBinOp(ctx, __qmljs_ushr, name, rhs); } -String *__qmljs_string_from_utf8(ExecutionContext *ctx, const char *s) -{ - return ctx->engine->newString(QString::fromUtf8(s)); -} - String *__qmljs_identifier_from_utf8(ExecutionContext *ctx, const char *s) { return ctx->engine->newString(QString::fromUtf8(s)); } -int __qmljs_string_length(ExecutionContext *, String *string) -{ - return string->toQString().length(); -} - double __qmljs_string_to_number(const String *string) { QString s = string->toQString(); @@ -506,11 +451,6 @@ String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *seco return ctx->engine->newString(first->toQString() + second->toQString()); } -Bool __qmljs_is_function(Value value) -{ - return value.objectValue()->asFunctionObject() != 0; -} - Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int typeHint) { if (typeHint == PREFERREDTYPE_HINT) { @@ -572,6 +512,33 @@ Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value) } } +String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value) +{ + switch (value.type()) { + case Value::Undefined_Type: + return ctx->engine->id_undefined; + case Value::Null_Type: + return ctx->engine->id_null; + case Value::Boolean_Type: + if (value.booleanValue()) + return ctx->engine->id_true; + else + return ctx->engine->id_false; + case Value::String_Type: + return value.stringValue(); + case Value::Object_Type: { + Value prim = __qmljs_to_primitive(value, ctx, STRING_HINT); + if (prim.isPrimitive()) + return __qmljs_convert_to_string(ctx, prim); + else + __qmljs_throw_type_error(ctx); + } + case Value::Integer_Type: + return __qmljs_string_from_number(ctx, value.int_32).stringValue(); + default: // double + return __qmljs_string_from_number(ctx, value.doubleValue()).stringValue(); + } // switch +} void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *name, const Value &value) { @@ -925,7 +892,7 @@ void __qmljs_call_element(ExecutionContext *context, Value *result, const Value Value thisObject = Value::fromObject(baseObject); Value func = baseObject->__get__(context, index.toString(context)); - FunctionObject *o = func.asFunctionObject(); + Object *o = func.asObject(); if (!o) context->throwTypeError(); @@ -936,7 +903,7 @@ void __qmljs_call_element(ExecutionContext *context, Value *result, const Value void __qmljs_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value &func, Value *args, int argc) { - FunctionObject *o = func.asFunctionObject(); + Object *o = func.asObject(); if (!o) context->throwTypeError(); Value res = o->call(context, thisObject ? *thisObject : Value::undefinedValue(), args, argc); @@ -952,7 +919,7 @@ void __qmljs_construct_activation_property(ExecutionContext *context, Value *res void __qmljs_construct_value(ExecutionContext *context, Value *result, const Value &func, Value *args, int argc) { - if (FunctionObject *f = func.asFunctionObject()) { + if (Object *f = func.asObject()) { Value res = f->construct(context, args, argc); if (result) *result = res; @@ -967,7 +934,7 @@ void __qmljs_construct_property(ExecutionContext *context, Value *result, const Object *thisObject = base.toObject(context); Value func = thisObject->__get__(context, name); - if (FunctionObject *f = func.asFunctionObject()) { + if (Object *f = func.asObject()) { Value res = f->construct(context, args, argc); if (result) *result = res; @@ -1024,29 +991,31 @@ void __qmljs_builtin_typeof(ExecutionContext *ctx, Value *result, const Value &v { if (!result) return; + String *res = 0; switch (value.type()) { case Value::Undefined_Type: - *result =__qmljs_string_literal_undefined(ctx); + res = ctx->engine->id_undefined; break; case Value::Null_Type: - *result = __qmljs_string_literal_object(ctx); + res = ctx->engine->id_object; break; case Value::Boolean_Type: - *result =__qmljs_string_literal_boolean(ctx); + res = ctx->engine->id_boolean; break; case Value::String_Type: - *result = __qmljs_string_literal_string(ctx); + res = ctx->engine->id_string; break; case Value::Object_Type: - if (__qmljs_is_callable(value, ctx)) - *result =__qmljs_string_literal_function(ctx); + if (value.objectValue()->asFunctionObject()) + res = ctx->engine->id_function; else - *result = __qmljs_string_literal_object(ctx); // ### implementation-defined + res = ctx->engine->id_object; // ### implementation-defined break; default: - *result =__qmljs_string_literal_number(ctx); + res = ctx->engine->id_number; break; } + *result = Value::fromString(res); } void __qmljs_builtin_typeof_name(ExecutionContext *context, Value *result, String *name) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 5de6cc3edd..0e3196b824 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -41,6 +41,7 @@ #ifndef QMLJS_RUNTIME_H #define QMLJS_RUNTIME_H +#include "qv4global.h" #include "qmljs_value.h" #include "qmljs_math.h" @@ -135,28 +136,13 @@ VM::Function *__qmljs_register_function(ExecutionContext *ctx, String *name, String **formals, unsigned formalCount, String **locals, unsigned localCount); -Bool __qmljs_is_function(Value value); - -// string literals -Value __qmljs_string_literal_undefined(ExecutionContext *ctx); -Value __qmljs_string_literal_null(ExecutionContext *ctx); -Value __qmljs_string_literal_true(ExecutionContext *ctx); -Value __qmljs_string_literal_false(ExecutionContext *ctx); -Value __qmljs_string_literal_object(ExecutionContext *ctx); -Value __qmljs_string_literal_boolean(ExecutionContext *ctx); -Value __qmljs_string_literal_number(ExecutionContext *ctx); -Value __qmljs_string_literal_string(ExecutionContext *ctx); -Value __qmljs_string_literal_function(ExecutionContext *ctx); // strings -String *__qmljs_string_from_utf8(ExecutionContext *ctx, const char *s); -int __qmljs_string_length(ExecutionContext *ctx, String *string); double __qmljs_string_to_number(const String *string); Value __qmljs_string_from_number(ExecutionContext *ctx, double number); Bool __qmljs_string_compare(ExecutionContext *ctx, String *left, String *right); Bool __qmljs_string_equal(String *left, String *right); String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second); -String *__qmljs_identifier_from_utf8(ExecutionContext *ctx, const char *s); // objects Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int typeHint); @@ -184,14 +170,10 @@ Value __qmljs_get_thisObject(ExecutionContext *ctx); Value __qmljs_to_primitive(const Value &value, ExecutionContext *ctx, int typeHint); Bool __qmljs_to_boolean(const Value &value, ExecutionContext *ctx); double __qmljs_to_number(const Value &value, ExecutionContext *ctx); -double __qmljs_to_integer(const Value &value, ExecutionContext *ctx); -int __qmljs_to_int32(const Value &value, ExecutionContext *ctx); -unsigned short __qmljs_to_uint16(const Value &value, ExecutionContext *ctx); Value __qmljs_to_string(const Value &value, ExecutionContext *ctx); +Q_V4_EXPORT String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value); Value __qmljs_to_object(ExecutionContext *ctx, const Value &value); Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value); -//uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *value); -Bool __qmljs_is_callable(const Value &value, ExecutionContext *ctx); Value __qmljs_default_value(const Value &value, ExecutionContext *ctx, int typeHint); Bool __qmljs_equal(const Value &x, const Value &y, ExecutionContext *ctx); @@ -313,7 +295,7 @@ inline Bool __qmljs_to_boolean(const Value &value, ExecutionContext *ctx) case Value::Integer_Type: return (bool)value.int_32; case Value::String_Type: - return __qmljs_string_length(ctx, value.stringValue()) > 0; + return value.stringValue()->toQString().length() > 0; case Value::Object_Type: return true; default: // double @@ -345,82 +327,11 @@ inline double __qmljs_to_number(const Value &value, ExecutionContext *ctx) } } -inline double __qmljs_to_integer(const Value &value, ExecutionContext *ctx) -{ - if (value.isConvertibleToInt()) - return value.int_32; - - return Value::toInteger(__qmljs_to_number(value, ctx)); -} - -inline int __qmljs_to_int32(const Value &value, ExecutionContext *ctx) -{ - if (value.isConvertibleToInt()) - return value.int_32; - - return Value::toInt32(__qmljs_to_number(value, ctx)); -} - -inline unsigned short __qmljs_to_uint16(const Value &value, ExecutionContext *ctx) -{ - if (value.isConvertibleToInt()) - return (ushort)(uint)value.integerValue(); - - double number = __qmljs_to_number(value, ctx); - - double D16 = 65536.0; - if ((number >= 0 && number < D16)) - return static_cast(number); - - if (!isfinite(number)) - return +0; - - double d = ::floor(::fabs(number)); - if (signbit(number)) - d = -d; - - number = ::fmod(d , D16); - - if (number < 0) - number += D16; - - return (unsigned short)number; -} - inline Value __qmljs_to_string(const Value &value, ExecutionContext *ctx) { - switch (value.type()) { - case Value::Undefined_Type: - return __qmljs_string_literal_undefined(ctx); - break; - case Value::Null_Type: - return __qmljs_string_literal_null(ctx); - break; - case Value::Boolean_Type: - if (value.booleanValue()) - return __qmljs_string_literal_true(ctx); - else - return __qmljs_string_literal_false(ctx); - break; - case Value::String_Type: + if (value.isString()) return value; - break; - case Value::Object_Type: { - Value prim = __qmljs_to_primitive(value, ctx, STRING_HINT); - if (prim.isPrimitive()) - return __qmljs_to_string(prim, ctx); - else - __qmljs_throw_type_error(ctx); - break; - } - case Value::Integer_Type: - return __qmljs_string_from_number(ctx, value.int_32); - break; - default: // double - return __qmljs_string_from_number(ctx, value.doubleValue()); - break; - - } // switch + return Value::fromString(__qmljs_convert_to_string(ctx, value)); } inline Value __qmljs_to_object(ExecutionContext *ctx, const Value &value) @@ -430,27 +341,6 @@ inline Value __qmljs_to_object(ExecutionContext *ctx, const Value &value) return Value::fromObject(__qmljs_convert_to_object(ctx, value)); } -/* -inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *value) -{ - switch (value->type()) { - case Value::Undefined_Type: - case Value::Null_Type: - *result = __qmljs_throw_type_error(ctx); - return false; - default: - return true; - } -} -*/ - -inline Bool __qmljs_is_callable(const Value &value, ExecutionContext * /*ctx*/) -{ - if (value.isObject()) - return __qmljs_is_function(value); - else - return false; -} inline Value __qmljs_default_value(const Value &value, ExecutionContext *ctx, int typeHint) { diff --git a/src/v4/qmljs_value.cpp b/src/v4/qmljs_value.cpp index 042a1e0039..e1bd2a6074 100644 --- a/src/v4/qmljs_value.cpp +++ b/src/v4/qmljs_value.cpp @@ -50,7 +50,28 @@ namespace VM { int Value::toUInt16(ExecutionContext *ctx) const { - return __qmljs_to_uint16(*this, ctx); + if (isConvertibleToInt()) + return (ushort)(uint)integerValue(); + + double number = __qmljs_to_number(*this, ctx); + + double D16 = 65536.0; + if ((number >= 0 && number < D16)) + return static_cast(number); + + if (!isfinite(number)) + return +0; + + double d = ::floor(::fabs(number)); + if (signbit(number)) + d = -d; + + number = ::fmod(d , D16); + + if (number < 0) + number += D16; + + return (unsigned short)number; } Bool Value::toBoolean(ExecutionContext *ctx) const @@ -60,7 +81,10 @@ Bool Value::toBoolean(ExecutionContext *ctx) const double Value::toInteger(ExecutionContext *ctx) const { - return __qmljs_to_integer(*this, ctx); + if (isConvertibleToInt()) + return int_32; + + return Value::toInteger(__qmljs_to_number(*this, ctx)); } double Value::toNumber(ExecutionContext *ctx) const @@ -68,13 +92,6 @@ double Value::toNumber(ExecutionContext *ctx) const return __qmljs_to_number(*this, ctx); } -String *Value::toString(ExecutionContext *ctx) const -{ - Value v = __qmljs_to_string(*this, ctx); - assert(v.isString()); - return v.stringValue(); -} - bool Value::sameValue(Value other) const { if (val == other.val) return true; diff --git a/src/v4/qmljs_value.h b/src/v4/qmljs_value.h index d0639c8518..62ca005371 100644 --- a/src/v4/qmljs_value.h +++ b/src/v4/qmljs_value.h @@ -60,6 +60,7 @@ struct Value; extern "C" { double __qmljs_to_number(const Value &value, ExecutionContext *ctx); +String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value); Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value); } @@ -352,6 +353,13 @@ inline Value Value::fromObject(Object *o) return v; } +inline String *Value::toString(ExecutionContext *ctx) const +{ + if (isString()) + return stringValue(); + return __qmljs_convert_to_string(ctx, *this); +} + inline Object *Value::toObject(ExecutionContext *ctx) const { if (isObject()) -- cgit v1.2.3 From 7fea47a1bd102ffcb02ef5c10f1662a08faedad9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 23:04:12 +0100 Subject: remove __qmljs_throw_type_error Change-Id: Ib666fa478e3e306117b50afebbd7826fa5b0738e Reviewed-by: Simon Hausmann --- src/v4/qmljs_environment.cpp | 2 +- src/v4/qmljs_runtime.cpp | 13 ++++--------- src/v4/qmljs_runtime.h | 1 - src/v4/qv4argumentsobject.cpp | 10 +++++----- src/v4/qv4arrayobject.cpp | 20 ++++++++++---------- src/v4/qv4dateobject.cpp | 2 +- src/v4/qv4errorobject.cpp | 2 +- src/v4/qv4globalobject.cpp | 2 +- src/v4/qv4object.cpp | 16 ++++++++-------- src/v4/qv4objectproto.cpp | 38 +++++++++++++++++++------------------- src/v4/qv4stringobject.cpp | 6 +++--- 11 files changed, 53 insertions(+), 59 deletions(-) diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp index f31c65c5d7..b8bf4caebb 100644 --- a/src/v4/qmljs_environment.cpp +++ b/src/v4/qmljs_environment.cpp @@ -166,7 +166,7 @@ bool ExecutionContext::deleteBinding(ExecutionContext *scope, String *name) activation->__delete__(scope, name); if (scope->strictMode) - __qmljs_throw_type_error(scope); + scope->throwTypeError(); return false; } diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 11ddbb268d..0b05f841c3 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -201,7 +201,7 @@ void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value &left, { Object *o = right.asObject(); if (!o) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); bool r = o->hasInstance(ctx, left); *result = Value::fromBoolean(r); @@ -210,7 +210,7 @@ void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value &left, void __qmljs_in(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) { if (!right.isObject()) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); String *s = left.toString(ctx); bool r = right.objectValue()->__hasProperty__(ctx, s); *result = Value::fromBoolean(r); @@ -487,18 +487,13 @@ Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int type return Value::undefinedValue(); } -void __qmljs_throw_type_error(ExecutionContext *ctx) -{ - ctx->throwTypeError(); -} - Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value) { assert(!value.isObject()); switch (value.type()) { case Value::Undefined_Type: case Value::Null_Type: - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); case Value::Boolean_Type: return ctx->engine->newBooleanObject(value); case Value::String_Type: @@ -531,7 +526,7 @@ String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value) if (prim.isPrimitive()) return __qmljs_convert_to_string(ctx, prim); else - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); } case Value::Integer_Type: return __qmljs_string_from_number(ctx, value.int_32).stringValue(); diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 0e3196b824..2176d597d8 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -146,7 +146,6 @@ String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *seco // objects Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int typeHint); -void __qmljs_throw_type_error(ExecutionContext *ctx); void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value& value); void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *name, const Value &value); void __qmljs_get_property(ExecutionContext *ctx, Value *result, const Value &object, String *name); diff --git a/src/v4/qv4argumentsobject.cpp b/src/v4/qv4argumentsobject.cpp index f2afe195b6..13e65ee4cb 100644 --- a/src/v4/qv4argumentsobject.cpp +++ b/src/v4/qv4argumentsobject.cpp @@ -128,7 +128,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const } if (ctx->strictMode && !result) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); return result; } @@ -139,10 +139,10 @@ Value ArgumentsGetterFunction::call(Managed *getter, ExecutionContext *ctx, cons ArgumentsGetterFunction *g = static_cast(getter); Object *that = thisObject.asObject(); if (!that) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); ArgumentsObject *o = that->asArgumentsObject(); if (!o) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); assert(g->index < o->context->argumentCount); return o->context->argument(g->index); @@ -155,10 +155,10 @@ Value ArgumentsSetterFunction::call(Managed *setter, ExecutionContext *ctx, cons ArgumentsSetterFunction *s = static_cast(setter); Object *that = thisObject.asObject(); if (!that) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); ArgumentsObject *o = that->asArgumentsObject(); if (!o) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); assert(s->index < o->context->argumentCount); o->context->arguments[s->index] = argc ? args[0] : Value::undefinedValue(); diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp index 69edf9ed0f..1878c15682 100644 --- a/src/v4/qv4arrayobject.cpp +++ b/src/v4/qv4arrayobject.cpp @@ -626,7 +626,7 @@ Value ArrayPrototype::method_every(ExecutionContext *ctx) FunctionObject *callback = ctx->argument(0).asFunctionObject(); if (!callback) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Value thisArg = ctx->argument(1); @@ -655,7 +655,7 @@ Value ArrayPrototype::method_some(ExecutionContext *ctx) FunctionObject *callback = ctx->argument(0).asFunctionObject(); if (!callback) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Value thisArg = ctx->argument(1); @@ -684,7 +684,7 @@ Value ArrayPrototype::method_forEach(ExecutionContext *ctx) FunctionObject *callback = ctx->argument(0).asFunctionObject(); if (!callback) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Value thisArg = ctx->argument(1); @@ -711,7 +711,7 @@ Value ArrayPrototype::method_map(ExecutionContext *ctx) FunctionObject *callback = ctx->argument(0).asFunctionObject(); if (!callback) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Value thisArg = ctx->argument(1); @@ -743,7 +743,7 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) FunctionObject *callback = ctx->argument(0).asFunctionObject(); if (!callback) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Value thisArg = ctx->argument(1); @@ -778,7 +778,7 @@ Value ArrayPrototype::method_reduce(ExecutionContext *ctx) FunctionObject *callback = ctx->argument(0).asFunctionObject(); if (!callback) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); uint k = 0; Value acc; @@ -793,7 +793,7 @@ Value ArrayPrototype::method_reduce(ExecutionContext *ctx) ++k; } if (!kPresent) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); } while (k < len) { @@ -820,11 +820,11 @@ Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) FunctionObject *callback = ctx->argument(0).asFunctionObject(); if (!callback) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); if (len == 0) { if (ctx->argumentCount == 1) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); return ctx->argument(1); } @@ -841,7 +841,7 @@ Value ArrayPrototype::method_reduceRight(ExecutionContext *ctx) --k; } if (!kPresent) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); } while (k > 0) { diff --git a/src/v4/qv4dateobject.cpp b/src/v4/qv4dateobject.cpp index 72a96af436..6f2f014e1a 100644 --- a/src/v4/qv4dateobject.cpp +++ b/src/v4/qv4dateobject.cpp @@ -1311,7 +1311,7 @@ Value DatePrototype::method_toJSON(ExecutionContext *ctx) FunctionObject *toIso = O.objectValue()->__get__(ctx, ctx->engine->newString(QStringLiteral("toISOString"))).asFunctionObject(); if (!toIso) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); return toIso->call(ctx, ctx->thisObject, 0, 0); } diff --git a/src/v4/qv4errorobject.cpp b/src/v4/qv4errorobject.cpp index addf163dda..c13c2131f2 100644 --- a/src/v4/qv4errorobject.cpp +++ b/src/v4/qv4errorobject.cpp @@ -234,7 +234,7 @@ Value ErrorPrototype::method_toString(ExecutionContext *ctx) { Object *o = ctx->thisObject.asObject(); if (!o) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Value name = o->__get__(ctx, ctx->engine->newString(QString::fromLatin1("name"))); QString qname; diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp index 2bba71e84c..2c921b2e50 100644 --- a/src/v4/qv4globalobject.cpp +++ b/src/v4/qv4globalobject.cpp @@ -482,7 +482,7 @@ QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct if (! globalCode) // ### should be a syntax error - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); } return globalCode; diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 16ce621fe2..3e675d36c8 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -141,7 +141,7 @@ void Object::putValue(ExecutionContext *ctx, PropertyDescriptor *pd, const Value reject: if (ctx->strictMode) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); } @@ -457,7 +457,7 @@ void Object::__put__(ExecutionContext *ctx, String *name, const Value &value) reject: if (ctx->strictMode) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); } void Object::__put__(ExecutionContext *ctx, uint index, const Value &value) @@ -515,7 +515,7 @@ void Object::__put__(ExecutionContext *ctx, uint index, const Value &value) reject: if (ctx->strictMode) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); } // Section 8.12.6 @@ -560,7 +560,7 @@ bool Object::__delete__(ExecutionContext *ctx, String *name) return true; } if (ctx->strictMode) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); return false; } @@ -593,7 +593,7 @@ bool Object::__delete__(ExecutionContext *ctx, uint index) } if (ctx->strictMode) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); return false; } @@ -646,7 +646,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr return __defineOwnProperty__(ctx, current, desc); reject: if (ctx->strictMode) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); return false; } @@ -677,7 +677,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Prop return __defineOwnProperty__(ctx, current, desc); reject: if (ctx->strictMode) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); return false; } @@ -741,7 +741,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *cu return true; reject: if (ctx->strictMode) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); return false; } diff --git a/src/v4/qv4objectproto.cpp b/src/v4/qv4objectproto.cpp index 0af8c1da20..1cdf61c4f3 100644 --- a/src/v4/qv4objectproto.cpp +++ b/src/v4/qv4objectproto.cpp @@ -197,7 +197,7 @@ Value ObjectPrototype::method_defineProperty(ExecutionContext *ctx) toPropertyDescriptor(ctx, attributes, &pd); if (!O.objectValue()->__defineOwnProperty__(ctx, name, &pd)) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); return O; } @@ -225,7 +225,7 @@ Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) else ok = O.objectValue()->__defineOwnProperty__(ctx, index, &n); if (!ok) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); } return O; @@ -234,7 +234,7 @@ Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx) Value ObjectPrototype::method_seal(ExecutionContext *ctx) { if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Object *o = ctx->argument(0).objectValue(); o->extensible = false; @@ -254,7 +254,7 @@ Value ObjectPrototype::method_seal(ExecutionContext *ctx) Value ObjectPrototype::method_freeze(ExecutionContext *ctx) { if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Object *o = ctx->argument(0).objectValue(); o->extensible = false; @@ -276,7 +276,7 @@ Value ObjectPrototype::method_freeze(ExecutionContext *ctx) Value ObjectPrototype::method_preventExtensions(ExecutionContext *ctx) { if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Object *o = ctx->argument(0).objectValue(); o->extensible = false; @@ -286,7 +286,7 @@ Value ObjectPrototype::method_preventExtensions(ExecutionContext *ctx) Value ObjectPrototype::method_isSealed(ExecutionContext *ctx) { if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Object *o = ctx->argument(0).objectValue(); if (o->extensible) @@ -308,7 +308,7 @@ Value ObjectPrototype::method_isSealed(ExecutionContext *ctx) Value ObjectPrototype::method_isFrozen(ExecutionContext *ctx) { if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Object *o = ctx->argument(0).objectValue(); if (o->extensible) @@ -330,7 +330,7 @@ Value ObjectPrototype::method_isFrozen(ExecutionContext *ctx) Value ObjectPrototype::method_isExtensible(ExecutionContext *ctx) { if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Object *o = ctx->argument(0).objectValue(); return Value::fromBoolean(o->extensible); @@ -339,7 +339,7 @@ Value ObjectPrototype::method_isExtensible(ExecutionContext *ctx) Value ObjectPrototype::method_keys(ExecutionContext *ctx) { if (!ctx->argument(0).isObject()) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Object *o = ctx->argument(0).objectValue(); @@ -384,7 +384,7 @@ Value ObjectPrototype::method_toLocaleString(ExecutionContext *ctx) Value ts = o->__get__(ctx, ctx->engine->newString(QStringLiteral("toString"))); FunctionObject *f = ts.asFunctionObject(); if (!f) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); return f->call(ctx, Value::fromObject(o), 0, 0); } @@ -429,12 +429,12 @@ Value ObjectPrototype::method_propertyIsEnumerable(ExecutionContext *ctx) Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx) { if (ctx->argumentCount < 2) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); String *prop = ctx->argument(0).toString(ctx); FunctionObject *f = ctx->argument(1).asFunctionObject(); if (!f) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Object *o = ctx->thisObject.toObject(ctx); @@ -448,12 +448,12 @@ Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx) Value ObjectPrototype::method_defineSetter(ExecutionContext *ctx) { if (ctx->argumentCount < 2) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); String *prop = ctx->argument(0).toString(ctx); FunctionObject *f = ctx->argument(1).asFunctionObject(); if (!f) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Object *o = ctx->thisObject.toObject(ctx); @@ -467,7 +467,7 @@ Value ObjectPrototype::method_defineSetter(ExecutionContext *ctx) void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc) { if (!v.isObject()) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); Object *o = v.objectValue(); @@ -490,7 +490,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Prope } else if (get.isUndefined()) { desc->get = (FunctionObject *)0x1; } else { - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); } desc->type = PropertyDescriptor::Accessor; } @@ -504,7 +504,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Prope } else if (set.isUndefined()) { desc->set = (FunctionObject *)0x1; } else { - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); } desc->type = PropertyDescriptor::Accessor; } @@ -512,7 +512,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Prope desc->writable = PropertyDescriptor::Undefined; if (o->__hasProperty__(ctx, ctx->engine->id_writable)) { if (desc->isAccessor()) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); desc->writable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_writable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; // writable forces it to be a data descriptor desc->value = Value::undefinedValue(); @@ -520,7 +520,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Prope if (o->__hasProperty__(ctx, ctx->engine->id_value)) { if (desc->isAccessor()) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); desc->value = o->__get__(ctx, ctx->engine->id_value); desc->type = PropertyDescriptor::Data; } diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp index 9829054693..437c30928e 100644 --- a/src/v4/qv4stringobject.cpp +++ b/src/v4/qv4stringobject.cpp @@ -302,7 +302,7 @@ Value StringPrototype::method_localeCompare(ExecutionContext *parentCtx, Value t Value StringPrototype::method_match(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc) { if (thisObject.isUndefined() || thisObject.isNull()) - __qmljs_throw_type_error(parentCtx); + parentCtx->throwTypeError(); String *s = thisObject.toString(parentCtx); @@ -313,7 +313,7 @@ Value StringPrototype::method_match(ExecutionContext *parentCtx, Value thisObjec if (!rx) // ### CHECK - __qmljs_throw_type_error(parentCtx); + parentCtx->throwTypeError(); bool global = rx->global; @@ -710,7 +710,7 @@ Value StringPrototype::method_fromCharCode(ExecutionContext *parentCtx, Value, V Value StringPrototype::method_trim(ExecutionContext *ctx) { if (ctx->thisObject.isNull() || ctx->thisObject.isUndefined()) - __qmljs_throw_type_error(ctx); + ctx->throwTypeError(); QString s = __qmljs_to_string(ctx->thisObject, ctx).stringValue()->toQString(); const QChar *chars = s.constData(); -- cgit v1.2.3 From f6d0f64ee6dea7243a95319369b13096e4c5ba03 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Feb 2013 23:16:50 +0100 Subject: cleanup __qmljs_to_boolean vs Value.toBoolean() Change-Id: Ic93eed2d4e68972d373bf1521387331ce26bac43 Reviewed-by: Simon Hausmann --- src/v4/llvm_runtime.cpp | 2 +- src/v4/moth/qv4vme_moth.cpp | 2 +- src/v4/qmljs_runtime.cpp | 6 ++++++ src/v4/qmljs_runtime.h | 26 +++----------------------- src/v4/qmljs_value.cpp | 5 ----- src/v4/qmljs_value.h | 24 +++++++++++++++++++++++- src/v4/qv4arrayobject.cpp | 6 +++--- src/v4/qv4booleanobject.cpp | 4 ++-- src/v4/qv4isel_masm.cpp | 2 +- src/v4/qv4objectproto.cpp | 6 +++--- 10 files changed, 43 insertions(+), 40 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 7e6f4380bd..004a7f397f 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -101,7 +101,7 @@ void __qmljs_llvm_init_closure(ExecutionContext *ctx, Value *result, bool __qmljs_llvm_to_boolean(ExecutionContext *ctx, const Value *value) { - return __qmljs_to_boolean(*value, ctx); + return __qmljs_to_boolean(*value); } void __qmljs_llvm_bit_and(ExecutionContext *ctx, Value *result, Value *left, Value *right) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 4fea46310f..290e4b5ff8 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -399,7 +399,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(Jump) MOTH_BEGIN_INSTR(CJump) - uint cond = __qmljs_to_boolean(VALUE(instr.condition), context); + uint cond = __qmljs_to_boolean(VALUE(instr.condition)); TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); if (cond) code = ((uchar *)&instr.offset) + instr.offset; diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 0b05f841c3..706775c2de 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -487,6 +487,12 @@ Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int type return Value::undefinedValue(); } +Bool __qmljs_to_boolean(const Value &value) +{ + return value.toBoolean(); +} + + Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value) { assert(!value.isObject()); diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 2176d597d8..3001f85329 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -167,7 +167,7 @@ Value __qmljs_get_thisObject(ExecutionContext *ctx); // type conversion and testing Value __qmljs_to_primitive(const Value &value, ExecutionContext *ctx, int typeHint); -Bool __qmljs_to_boolean(const Value &value, ExecutionContext *ctx); +Bool __qmljs_to_boolean(const Value &value); double __qmljs_to_number(const Value &value, ExecutionContext *ctx); Value __qmljs_to_string(const Value &value, ExecutionContext *ctx); Q_V4_EXPORT String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value); @@ -284,26 +284,6 @@ inline Value __qmljs_to_primitive(const Value &value, ExecutionContext *ctx, int return __qmljs_default_value(value, ctx, typeHint); } -inline Bool __qmljs_to_boolean(const Value &value, ExecutionContext *ctx) -{ - switch (value.type()) { - case Value::Undefined_Type: - case Value::Null_Type: - return false; - case Value::Boolean_Type: - case Value::Integer_Type: - return (bool)value.int_32; - case Value::String_Type: - return value.stringValue()->toQString().length() > 0; - case Value::Object_Type: - return true; - default: // double - if (! value.doubleValue() || isnan(value.doubleValue())) - return false; - return true; - } -} - inline double __qmljs_to_number(const Value &value, ExecutionContext *ctx) { switch (value.type()) { @@ -387,11 +367,11 @@ inline void __qmljs_compl(ExecutionContext *ctx, Value *result, const Value &val *result = Value::fromInt32(~n); } -inline void __qmljs_not(ExecutionContext *ctx, Value *result, const Value &value) +inline void __qmljs_not(ExecutionContext *, Value *result, const Value &value) { TRACE1(value); - bool b = __qmljs_to_boolean(value, ctx); + bool b = value.toBoolean(); *result = Value::fromBoolean(!b); } diff --git a/src/v4/qmljs_value.cpp b/src/v4/qmljs_value.cpp index e1bd2a6074..91f4ec828f 100644 --- a/src/v4/qmljs_value.cpp +++ b/src/v4/qmljs_value.cpp @@ -74,11 +74,6 @@ int Value::toUInt16(ExecutionContext *ctx) const return (unsigned short)number; } -Bool Value::toBoolean(ExecutionContext *ctx) const -{ - return __qmljs_to_boolean(*this, ctx); -} - double Value::toInteger(ExecutionContext *ctx) const { if (isConvertibleToInt()) diff --git a/src/v4/qmljs_value.h b/src/v4/qmljs_value.h index 62ca005371..56d909422b 100644 --- a/src/v4/qmljs_value.h +++ b/src/v4/qmljs_value.h @@ -48,6 +48,8 @@ #include #include "qv4managed.h" +#include + QT_BEGIN_NAMESPACE namespace QQmlJS { @@ -205,7 +207,7 @@ struct Q_V4_EXPORT Value int toInt32(ExecutionContext *ctx) const; unsigned int toUInt32(ExecutionContext *ctx) const; - Bool toBoolean(ExecutionContext *ctx) const; + Bool toBoolean() const; double toInteger(ExecutionContext *ctx) const; double toNumber(ExecutionContext *ctx) const; String *toString(ExecutionContext *ctx) const; @@ -353,6 +355,26 @@ inline Value Value::fromObject(Object *o) return v; } +inline Bool Value::toBoolean() const +{ + switch (type()) { + case Value::Undefined_Type: + case Value::Null_Type: + return false; + case Value::Boolean_Type: + case Value::Integer_Type: + return (bool)int_32; + case Value::String_Type: + return stringValue()->toQString().length() > 0; + case Value::Object_Type: + return true; + default: // double + if (! doubleValue() || isnan(doubleValue())) + return false; + return true; + } +} + inline String *Value::toString(ExecutionContext *ctx) const { if (isString()) diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp index 1878c15682..04fbc1f9e0 100644 --- a/src/v4/qv4arrayobject.cpp +++ b/src/v4/qv4arrayobject.cpp @@ -642,7 +642,7 @@ Value ArrayPrototype::method_every(ExecutionContext *ctx) args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; Value r = callback->call(ctx, thisArg, args, 3); - ok = __qmljs_to_boolean(r, ctx); + ok = r.toBoolean(); } return Value::fromBoolean(ok); } @@ -670,7 +670,7 @@ Value ArrayPrototype::method_some(ExecutionContext *ctx) args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; Value r = callback->call(ctx, thisArg, args, 3); - if (__qmljs_to_boolean(r, ctx)) + if (r.toBoolean()) return Value::fromBoolean(true); } return Value::fromBoolean(false); @@ -762,7 +762,7 @@ Value ArrayPrototype::method_filter(ExecutionContext *ctx) args[1] = Value::fromDouble(k); args[2] = ctx->thisObject; Value selected = callback->call(ctx, thisArg, args, 3); - if (__qmljs_to_boolean(selected, ctx)) { + if (selected.toBoolean()) { a->arraySet(to, v); ++to; } diff --git a/src/v4/qv4booleanobject.cpp b/src/v4/qv4booleanobject.cpp index b2a72f1bc0..8b1af93bf5 100644 --- a/src/v4/qv4booleanobject.cpp +++ b/src/v4/qv4booleanobject.cpp @@ -53,13 +53,13 @@ BooleanCtor::BooleanCtor(ExecutionContext *scope) Value BooleanCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) { - bool n = argc ? args[0].toBoolean(ctx) : false; + bool n = argc ? args[0].toBoolean() : false; return Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n))); } Value BooleanCtor::call(Managed *, ExecutionContext *parentCtx, const Value &thisObject, Value *argv, int argc) { - bool value = argc ? argv[0].toBoolean(parentCtx) : 0; + bool value = argc ? argv[0].toBoolean() : 0; return Value::fromBoolean(value); } diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 7dfd8ed43d..2f2c9ce5ce 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -930,7 +930,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) booleanConversion.link(_as); { - generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, Assembler::PointerToValue(t), Assembler::ContextRegister); + generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, Assembler::PointerToValue(t)); } testBoolean.link(_as); diff --git a/src/v4/qv4objectproto.cpp b/src/v4/qv4objectproto.cpp index 1cdf61c4f3..21d61820c2 100644 --- a/src/v4/qv4objectproto.cpp +++ b/src/v4/qv4objectproto.cpp @@ -475,11 +475,11 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Prope desc->enumberable = PropertyDescriptor::Undefined; if (o->__hasProperty__(ctx, ctx->engine->id_enumerable)) - desc->enumberable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_enumerable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; + desc->enumberable = o->__get__(ctx, ctx->engine->id_enumerable).toBoolean() ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; desc->configurable = PropertyDescriptor::Undefined; if (o->__hasProperty__(ctx, ctx->engine->id_configurable)) - desc->configurable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_configurable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; + desc->configurable = o->__get__(ctx, ctx->engine->id_configurable).toBoolean() ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; desc->get = 0; if (o->__hasProperty__(ctx, ctx->engine->id_get)) { @@ -513,7 +513,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Prope if (o->__hasProperty__(ctx, ctx->engine->id_writable)) { if (desc->isAccessor()) ctx->throwTypeError(); - desc->writable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_writable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; + desc->writable = o->__get__(ctx, ctx->engine->id_writable).toBoolean() ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled; // writable forces it to be a data descriptor desc->value = Value::undefinedValue(); } -- cgit v1.2.3 From 2f3824a0968d7b13f5114af971878640ea53b5d3 Mon Sep 17 00:00:00 2001 From: Laszlo Papp Date: Thu, 14 Feb 2013 22:57:59 +0000 Subject: Add the mandatory license file to be consistent and proper See the following mailing list thread for further details: http://lists.qt-project.org/pipermail/development/2013-February/009810.html Change-Id: Iec80c7cbeeafca7d2cdd63eb9c918d8dedbc6cd0 Reviewed-by: Lars Knoll --- LGPL_EXCEPTION.txt | 22 ++ LICENSE.FDL | 450 +++++++++++++++++++++++++++++++++++ LICENSE.GPL | 674 +++++++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE.LGPL | 514 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1660 insertions(+) create mode 100644 LGPL_EXCEPTION.txt create mode 100644 LICENSE.FDL create mode 100644 LICENSE.GPL create mode 100644 LICENSE.LGPL diff --git a/LGPL_EXCEPTION.txt b/LGPL_EXCEPTION.txt new file mode 100644 index 0000000000..7e2e30ff9b --- /dev/null +++ b/LGPL_EXCEPTION.txt @@ -0,0 +1,22 @@ +Digia Qt LGPL Exception version 1.1 + +As an additional permission to the GNU Lesser General Public License version +2.1, the object code form of a "work that uses the Library" may incorporate +material from a header file that is part of the Library. You may distribute +such object code under terms of your choice, provided that: + (i) the header files of the Library have not been modified; and + (ii) the incorporated material is limited to numerical parameters, data + structure layouts, accessors, macros, inline functions and + templates; and + (iii) you comply with the terms of Section 6 of the GNU Lesser General + Public License version 2.1. + +Moreover, you may apply this exception to a modified version of the Library, +provided that such modification does not involve copying material from the +Library into the modified Library's header files unless such material is +limited to (i) numerical parameters; (ii) data structure layouts; +(iii) accessors; and (iv) small macros, templates and inline functions of +five lines or less in length. + +Furthermore, you are not required to apply this additional permission to a +modified version of the Library. diff --git a/LICENSE.FDL b/LICENSE.FDL new file mode 100644 index 0000000000..938bb8da9e --- /dev/null +++ b/LICENSE.FDL @@ -0,0 +1,450 @@ + GNU Free Documentation License + Version 1.3, 3 November 2008 + + + Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +The "publisher" means any person or entity that distributes copies of +the Document to the public. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no +other conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to +give them a chance to provide you with an updated version of the +Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other +documents released under this License, and replace the individual +copies of this License in the various documents with a single copy +that is included in the collection, provided that you follow the rules +of this License for verbatim copying of each of the documents in all +other respects. + +You may extract a single document from such a collection, and +distribute it individually under this License, provided you insert a +copy of this License into the extracted document, and follow this +License in all other respects regarding verbatim copying of that +document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense, or distribute it is void, and +will automatically terminate your rights under this License. + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, receipt of a copy of some or all of the same material does +not give you any rights to use it. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions of the +GNU Free Documentation License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in +detail to address new problems or concerns. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. If the Document +specifies that a proxy can decide which future versions of this +License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the +Document. + +11. RELICENSING + +"Massive Multiauthor Collaboration Site" (or "MMC Site") means any +World Wide Web server that publishes copyrightable works and also +provides prominent facilities for anybody to edit those works. A +public wiki that anybody can edit is an example of such a server. A +"Massive Multiauthor Collaboration" (or "MMC") contained in the site +means any set of copyrightable works thus published on the MMC site. + +"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 +license published by Creative Commons Corporation, a not-for-profit +corporation with a principal place of business in San Francisco, +California, as well as future copyleft versions of that license +published by that same organization. + +"Incorporate" means to publish or republish a Document, in whole or in +part, as part of another Document. + +An MMC is "eligible for relicensing" if it is licensed under this +License, and if all works that were first published under this License +somewhere other than this MMC, and subsequently incorporated in whole or +in part into the MMC, (1) had no cover texts or invariant sections, and +(2) were thus incorporated prior to November 1, 2008. + +The operator of an MMC Site may republish an MMC contained in the site +under CC-BY-SA on the same site at any time before August 1, 2009, +provided the MMC is eligible for relicensing. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/LICENSE.GPL b/LICENSE.GPL new file mode 100644 index 0000000000..94a9ed024d --- /dev/null +++ b/LICENSE.GPL @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/LICENSE.LGPL b/LICENSE.LGPL new file mode 100644 index 0000000000..3788d79e24 --- /dev/null +++ b/LICENSE.LGPL @@ -0,0 +1,514 @@ + GNU LESSER GENERAL PUBLIC LICENSE + + The Qt Toolkit is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). + Contact: http://www.qt-project.org/legal + + You may use, distribute and copy the Qt GUI Toolkit under the terms of + GNU Lesser General Public License version 2.1, which is displayed below. + +------------------------------------------------------------------------- + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + -- cgit v1.2.3 From 483b9fc544a7d6e728d958d0ef29fe486012f5bd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 15 Feb 2013 09:30:24 +0100 Subject: Don't assert when running into the JS debugger statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead use Q_UNIMPLEMENTED() that just prints a warning. This really is missing functionality that in the meanwhile shouldn't cause crashes due to failing assertions. Change-Id: I85314d04e35af35b95dc81e9cbdd659d13f43798 Reviewed-by: Jędrzej Nowacki --- src/v4/qv4codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index 9b9c931523..60977ac4b7 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -2081,7 +2081,7 @@ bool Codegen::visit(ContinueStatement *ast) bool Codegen::visit(DebuggerStatement *) { - assert(!"not implemented"); + Q_UNIMPLEMENTED(); return false; } -- cgit v1.2.3 From a8ef93dc6ba5630f5f464efd2f06388a4ce46ec1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 15 Feb 2013 08:27:21 +0100 Subject: [masm] Save guard const reference method calls Introduce a Reference type next to PointerToValue for which we can ensure that it's non-null using an assert. Otherwise implemented push(PointerToValue) to push a null pointer if the temp is null, instead of asserting. Change-Id: I70f15e39dd80a6b2c65630060cba35f3417c0634 Reviewed-by: Lars Knoll --- src/v4/qv4isel_masm.cpp | 84 ++++++++++++++++++++++++++++++------------------- src/v4/qv4isel_masm_p.h | 23 ++++++++++++++ 2 files changed, 74 insertions(+), 33 deletions(-) diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 2f2c9ce5ce..9bc9e32ac9 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -333,7 +333,7 @@ void Assembler::generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Temp *l // Fallback generateFunctionCallImp(Assembler::Void, info.name, info.fallbackImplementation, ContextRegister, - Assembler::PointerToValue(target), Assembler::PointerToValue(left), Assembler::PointerToValue(right)); + Assembler::PointerToValue(target), Assembler::Reference(left), Assembler::Reference(right)); if (binOpFinished.isSet()) binOpFinished.link(this); @@ -480,12 +480,15 @@ void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) { - generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_member, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name)); + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); } void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) { - generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_element, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(base), Assembler::PointerToValue(index)); + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_element, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), Assembler::Reference(index)); } void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result) @@ -495,17 +498,20 @@ void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp * void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) { - generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(value)); + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(value)); } void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) { - generateFunctionCall(Assembler::Void, __qmljs_delete_member, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name)); + generateFunctionCall(Assembler::Void, __qmljs_delete_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); } void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) { - generateFunctionCall(Assembler::Void, __qmljs_delete_subscript, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(base), Assembler::PointerToValue(index)); + generateFunctionCall(Assembler::Void, __qmljs_delete_subscript, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), Assembler::Reference(index)); } void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) @@ -527,7 +533,7 @@ void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) { generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_element, Assembler::ContextRegister, - Assembler::PointerToValue(result), Assembler::PointerToValue(base), Assembler::PointerToValue(index)); + Assembler::PointerToValue(result), Assembler::Reference(base), Assembler::PointerToValue(index)); } void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result) @@ -545,13 +551,14 @@ void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Te void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) { generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_member, Assembler::ContextRegister, - Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name)); + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); } void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) { generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_element, Assembler::ContextRegister, - Assembler::PointerToValue(result), Assembler::PointerToValue(base), Assembler::PointerToValue(index)); + Assembler::PointerToValue(result), Assembler::Reference(base), + Assembler::Reference(index)); } void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result) @@ -568,7 +575,7 @@ void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Te void InstructionSelection::callBuiltinThrow(IR::Temp *arg) { - generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, Assembler::ContextRegister, Assembler::PointerToValue(arg)); + generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, Assembler::ContextRegister, Assembler::Reference(arg)); } void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp) @@ -647,7 +654,9 @@ void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Te { int argc = prepareVariableArguments(args); IR::Temp* thisObject = 0; - generateFunctionCall(Assembler::Void, __qmljs_call_value, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(thisObject), Assembler::PointerToValue(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); + generateFunctionCall(Assembler::Void, __qmljs_call_value, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(thisObject), + Assembler::Reference(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } void InstructionSelection::loadThisObject(IR::Temp *temp) @@ -683,7 +692,8 @@ void InstructionSelection::getActivationProperty(const QString &name, IR::Temp * void InstructionSelection::setActivationProperty(IR::Temp *source, const QString &targetName) { String *propertyName = identifier(targetName); - generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, Assembler::ContextRegister, propertyName, Assembler::PointerToValue(source)); + generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, + Assembler::ContextRegister, propertyName, Assembler::Reference(source)); } void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) @@ -699,10 +709,10 @@ void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR:: VM::String *s = identifier(name); uint index = addLookup(s); generateFunctionCall(Assembler::Void, __qmljs_get_property_lookup, Assembler::ContextRegister, Assembler::PointerToValue(target), - Assembler::PointerToValue(base), Assembler::TrustedImm32(index)); + Assembler::Reference(base), Assembler::TrustedImm32(index)); } else { generateFunctionCall(Assembler::Void, __qmljs_get_property, Assembler::ContextRegister, Assembler::PointerToValue(target), - Assembler::PointerToValue(base), identifier(name)); + Assembler::Reference(base), identifier(name)); } } @@ -711,24 +721,28 @@ void InstructionSelection::setProperty(IR::Temp *source, IR::Temp *targetBase, c if (useFastLookups) { VM::String *s = identifier(targetName); uint index = addLookup(s); - generateFunctionCall(Assembler::Void, __qmljs_set_property_lookup, Assembler::ContextRegister, Assembler::PointerToValue(targetBase), - Assembler::TrustedImm32(index), Assembler::PointerToValue(source)); + generateFunctionCall(Assembler::Void, __qmljs_set_property_lookup, + Assembler::ContextRegister, Assembler::Reference(targetBase), + Assembler::TrustedImm32(index), Assembler::Reference(source)); } else { - generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, Assembler::PointerToValue(targetBase), - identifier(targetName), Assembler::PointerToValue(source)); + generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, + Assembler::Reference(targetBase), + identifier(targetName), Assembler::Reference(source)); } } void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) { generateFunctionCall(Assembler::Void, __qmljs_get_element, Assembler::ContextRegister, - Assembler::PointerToValue(target), Assembler::PointerToValue(base), Assembler::PointerToValue(index)); + Assembler::PointerToValue(target), Assembler::Reference(base), + Assembler::Reference(index)); } void InstructionSelection::setElement(IR::Temp *source, IR::Temp *targetBase, IR::Temp *targetIndex) { generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, - Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex), Assembler::PointerToValue(source)); + Assembler::Reference(targetBase), Assembler::Reference(targetIndex), + Assembler::Reference(source)); } void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) @@ -756,7 +770,7 @@ void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp * if (op) _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, Assembler::PointerToValue(targetTemp), - Assembler::PointerToValue(sourceTemp)); + Assembler::Reference(sourceTemp)); } void InstructionSelection::binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target) @@ -785,7 +799,8 @@ void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Temp *rightSource, break; } if (op) { - _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, identifier(targetName), Assembler::PointerToValue(rightSource)); + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + identifier(targetName), Assembler::Reference(rightSource)); } } @@ -812,8 +827,8 @@ void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Temp *source, IR if (op) { _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, - Assembler::PointerToValue(targetBaseTemp), Assembler::PointerToValue(targetIndexTemp), - Assembler::PointerToValue(source)); + Assembler::Reference(targetBaseTemp), Assembler::Reference(targetIndexTemp), + Assembler::Reference(source)); } } @@ -841,8 +856,8 @@ void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Temp *source, IR: if (op) { String* member = identifier(targetName); _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, - Assembler::PointerToValue(targetBase), identifier(targetName), - Assembler::PointerToValue(source)); + Assembler::Reference(targetBase), identifier(targetName), + Assembler::Reference(source)); } } @@ -858,13 +873,13 @@ void InstructionSelection::callProperty(IR::Temp *base, const QString &name, uint index = addLookup(s); generateFunctionCall(Assembler::Void, __qmljs_call_property_lookup, Assembler::ContextRegister, Assembler::PointerToValue(result), - Assembler::PointerToValue(base), Assembler::TrustedImm32(index), + Assembler::Reference(base), Assembler::TrustedImm32(index), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } else { generateFunctionCall(Assembler::Void, __qmljs_call_property, Assembler::ContextRegister, Assembler::PointerToValue(result), - Assembler::PointerToValue(base), s, + Assembler::Reference(base), s, baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } @@ -877,7 +892,7 @@ void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::Ex int argc = prepareVariableArguments(args); generateFunctionCall(Assembler::Void, __qmljs_call_element, Assembler::ContextRegister, Assembler::PointerToValue(result), - Assembler::PointerToValue(base), Assembler::PointerToValue(index), + Assembler::Reference(base), Assembler::Reference(index), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } @@ -899,7 +914,8 @@ void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprL void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) { int argc = prepareVariableArguments(args); - generateFunctionCall(Assembler::Void, __qmljs_construct_property, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); + generateFunctionCall(Assembler::Void, __qmljs_construct_property, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) @@ -907,7 +923,8 @@ void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, I assert(value != 0); int argc = prepareVariableArguments(args); - generateFunctionCall(Assembler::Void, __qmljs_construct_value, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::PointerToValue(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); + generateFunctionCall(Assembler::Void, __qmljs_construct_value, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); } void InstructionSelection::visitJump(IR::Jump *s) @@ -930,7 +947,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) booleanConversion.link(_as); { - generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, Assembler::PointerToValue(t)); + generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, Assembler::Reference(t)); } testBoolean.link(_as); @@ -958,7 +975,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) } // switch _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, Assembler::ContextRegister, - Assembler::PointerToValue(b->left->asTemp()), Assembler::PointerToValue(b->right->asTemp())); + Assembler::Reference(b->left->asTemp()), + Assembler::Reference(b->right->asTemp())); Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); _as->addPatch(s->iftrue, target); diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 08ab7ea23b..8500c2bc18 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -182,6 +182,10 @@ public: PointerToValue(IR::Temp *value) : value(value) {} IR::Temp *value; }; + struct Reference { + Reference(IR::Temp *value) : value(value) {} + IR::Temp *value; + }; void callAbsolute(const char* functionName, FunctionPtr function) { CallToLink ctl; @@ -222,6 +226,13 @@ public: } } + void loadArgument(Reference temp, RegisterID dest) + { + assert(temp.value); + Pointer addr = loadTempAddress(dest, temp.value); + loadArgument(addr, dest); + } + #ifdef VALUE_FITS_IN_REGISTER void loadArgument(IR::Temp* temp, RegisterID dest) { @@ -325,6 +336,16 @@ public: } void push(PointerToValue temp) + { + if (temp.value) { + Pointer ptr = loadTempAddress(ScratchRegister, temp.value); + push(ptr); + } else { + push(TrustedImmPtr(0)); + } + } + + void push(Reference temp) { assert (temp.value); @@ -424,6 +445,8 @@ public: { return sizeof(string); } static inline int sizeOfArgument(const PointerToValue &) { return sizeof(void *); } + static inline int sizeOfArgument(const Reference &) + { return sizeof(void *); } static inline int sizeOfArgument(TrustedImmPtr) { return sizeof(void*); } static inline int sizeOfArgument(TrustedImm32) -- cgit v1.2.3 From a7b444b52691ec2efe05db17d3920bbf7f042398 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 15 Feb 2013 09:54:10 +0100 Subject: Port the remaining functions called from masm to pass-by-reference/pointer Change-Id: I1ea15fc500d0d2168aded2b6a2739420eb007b45 Reviewed-by: Lars Knoll --- src/v4/llvm_runtime.cpp | 4 ++-- src/v4/moth/qv4vme_moth.cpp | 10 +++++----- src/v4/qmljs_runtime.cpp | 24 ++++++++++++------------ src/v4/qmljs_runtime.h | 12 ++++++------ src/v4/qv4isel_masm.cpp | 18 +++++++++--------- 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 004a7f397f..bfd9ba0e6c 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -492,12 +492,12 @@ void __qmljs_llvm_get_exception(ExecutionContext *context, Value *result) void __qmljs_llvm_foreach_iterator_object(ExecutionContext *context, Value *result, Value *in) { - *result = __qmljs_foreach_iterator_object(*in, context); + __qmljs_foreach_iterator_object(context, result, *in); } void __qmljs_llvm_foreach_next_property_name(Value *result, Value *it) { - *result = __qmljs_foreach_next_property_name(*it); + __qmljs_foreach_next_property_name(result, *it); } void __qmljs_llvm_get_this_object(ExecutionContext *ctx, Value *result) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 290e4b5ff8..092c9ba191 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -292,11 +292,11 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinPopScope) MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) - VALUE(instr.result) = __qmljs_foreach_iterator_object(VALUE(instr.arg), context); + __qmljs_foreach_iterator_object(context, VALUEPTR(instr.result), VALUE(instr.arg)); MOTH_END_INSTR(CallBuiltinForeachIteratorObject) MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) - VALUE(instr.result) = __qmljs_foreach_next_property_name(VALUE(instr.arg)); + __qmljs_foreach_next_property_name(VALUEPTR(instr.result), VALUE(instr.arg)); MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) @@ -364,15 +364,15 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinDeclareVar) MOTH_BEGIN_INSTR(CallBuiltinDefineGetterSetter) - __qmljs_builtin_define_getter_setter(VALUE(instr.object), instr.name, VALUE(instr.getter), VALUE(instr.setter), context); + __qmljs_builtin_define_getter_setter(context, VALUE(instr.object), instr.name, VALUEPTR(instr.getter), VALUEPTR(instr.setter)); MOTH_END_INSTR(CallBuiltinDefineGetterSetter) MOTH_BEGIN_INSTR(CallBuiltinDefineProperty) - __qmljs_builtin_define_property(VALUE(instr.object), instr.name, VALUE(instr.value), context); + __qmljs_builtin_define_property(context, VALUE(instr.object), instr.name, VALUEPTR(instr.value)); MOTH_END_INSTR(CallBuiltinDefineProperty) MOTH_BEGIN_INSTR(CallBuiltinDefineArrayProperty) - __qmljs_builtin_define_array_property(VALUE(instr.object), instr.index, VALUE(instr.value), context); + __qmljs_builtin_define_array_property(context, VALUE(instr.object), instr.index, VALUEPTR(instr.value)); MOTH_END_INSTR(CallBuiltinDefineArrayProperty) MOTH_BEGIN_INSTR(CreateValue) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 706775c2de..1994e37852 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -609,23 +609,23 @@ void __qmljs_set_element(ExecutionContext *ctx, const Value &object, const Value o->__put__(ctx, name, value); } -Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx) +void __qmljs_foreach_iterator_object(ExecutionContext *ctx, Value *result, const Value &in) { Object *o = 0; if (!in.isNull() && !in.isUndefined()) o = in.toObject(ctx); Object *it = ctx->engine->newForEachIteratorObject(ctx, o); - return Value::fromObject(it); + *result = Value::fromObject(it); } -Value __qmljs_foreach_next_property_name(Value foreach_iterator) +void __qmljs_foreach_next_property_name(Value *result, const Value &foreach_iterator) { assert(foreach_iterator.isObject()); ForEachIteratorObject *it = static_cast(foreach_iterator.objectValue()); assert(it->asForeachIteratorObject()); - return it->nextPropertyName(); + *result = it->nextPropertyName(); } @@ -1211,7 +1211,7 @@ void __qmljs_builtin_throw(ExecutionContext *context, const Value &val) __qmljs_throw(context, val); } -ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx) +ExecutionContext *__qmljs_builtin_push_with_scope(const Value &o, ExecutionContext *ctx) { Object *obj = o.toObject(ctx); return ctx->createWithScope(obj); @@ -1232,13 +1232,13 @@ void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String * ctx->createMutableBinding(name, deletable); } -void __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) +void __qmljs_builtin_define_property(ExecutionContext *ctx, const Value &object, String *name, Value *val) { Object *o = object.asObject(); assert(o); PropertyDescriptor pd; - pd.value = val; + pd.value = val ? *val : Value::undefinedValue(); pd.type = PropertyDescriptor::Data; pd.writable = PropertyDescriptor::Enabled; pd.enumberable = PropertyDescriptor::Enabled; @@ -1246,13 +1246,13 @@ void __qmljs_builtin_define_property(Value object, String *name, Value val, Exec o->__defineOwnProperty__(ctx, name, &pd); } -void __qmljs_builtin_define_array_property(Value object, int index, Value val, ExecutionContext *ctx) +void __qmljs_builtin_define_array_property(ExecutionContext *ctx, const Value &object, int index, Value *val) { Object *o = object.asObject(); assert(o); PropertyDescriptor pd; - pd.value = val; + pd.value = val ? *val : Value::undefinedValue(); pd.type = PropertyDescriptor::Data; pd.writable = PropertyDescriptor::Enabled; pd.enumberable = PropertyDescriptor::Enabled; @@ -1260,14 +1260,14 @@ void __qmljs_builtin_define_array_property(Value object, int index, Value val, E o->__defineOwnProperty__(ctx, index, &pd); } -void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx) +void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &object, String *name, const Value *getter, const Value *setter) { Object *o = object.asObject(); assert(o); PropertyDescriptor pd; - pd.get = getter.asFunctionObject(); - pd.set = setter.asFunctionObject(); + pd.get = getter ? getter->asFunctionObject() : 0; + pd.set = setter ? setter->asFunctionObject() : 0; pd.type = PropertyDescriptor::Accessor; pd.writable = PropertyDescriptor::Undefined; pd.enumberable = PropertyDescriptor::Enabled; diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 3001f85329..9a16a441f6 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -119,13 +119,13 @@ void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *re void __qmljs_builtin_throw(ExecutionContext *context, const Value &val); void __qmljs_builtin_rethrow(ExecutionContext *context); -ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx); +ExecutionContext *__qmljs_builtin_push_with_scope(const Value &o, ExecutionContext *ctx); ExecutionContext *__qmljs_builtin_push_catch_scope(String *exceptionVarName, ExecutionContext *ctx); ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx); void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name); -void __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx); -void __qmljs_builtin_define_array_property(Value object, int index, Value val, ExecutionContext *ctx); -void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); +void __qmljs_builtin_define_property(ExecutionContext *ctx, const Value &object, String *name, Value *val); +void __qmljs_builtin_define_array_property(ExecutionContext *ctx, const Value &object, int index, Value *val); +void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &object, String *name, const Value *getter, const Value *setter); // constructors void __qmljs_init_closure(ExecutionContext *ctx, Value *result, VM::Function *clos); @@ -159,8 +159,8 @@ void __qmljs_get_element(ExecutionContext *ctx, Value *retval, const Value &obje void __qmljs_set_element(ExecutionContext *ctx, const Value &object, const Value &index, const Value &value); // For each -Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx); -Value __qmljs_foreach_next_property_name(Value foreach_iterator); +void __qmljs_foreach_iterator_object(ExecutionContext *ctx, Value *result, const Value &in); +void __qmljs_foreach_next_property_name(Value *result, const Value &foreach_iterator); // context Value __qmljs_get_thisObject(ExecutionContext *ctx); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 9bc9e32ac9..2127c8fa70 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -603,17 +603,17 @@ void InstructionSelection::callBuiltinGetException(IR::Temp *result) void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) { - generateFunctionCall(result, __qmljs_foreach_iterator_object, arg, Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_foreach_iterator_object, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::Reference(arg), Assembler::ContextRegister); } void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) { - generateFunctionCall(result, __qmljs_foreach_next_property_name, arg); + generateFunctionCall(Assembler::Void, __qmljs_foreach_next_property_name, Assembler::PointerToValue(result), Assembler::Reference(arg)); } void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) { - generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with_scope, arg, Assembler::ContextRegister); + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with_scope, Assembler::Reference(arg), Assembler::ContextRegister); } void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionVarName) @@ -634,20 +634,20 @@ void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString & void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) { - generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter, - object, identifier(name), getter, setter, Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter, Assembler::ContextRegister, + Assembler::Reference(object), identifier(name), Assembler::PointerToValue(getter), Assembler::PointerToValue(setter)); } void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) { - generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property, - object, identifier(name), value, Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property, Assembler::ContextRegister, + Assembler::Reference(object), identifier(name), Assembler::PointerToValue(value)); } void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) { - generateFunctionCall(Assembler::Void, __qmljs_builtin_define_array_property, - object, Assembler::TrustedImm32(index), value, Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_array_property, Assembler::ContextRegister, + Assembler::Reference(object), Assembler::TrustedImm32(index), Assembler::PointerToValue(value)); } void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) -- cgit v1.2.3 From 7bfdc4be0294f3f5d911a19523daa964a001727e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 15 Feb 2013 12:40:09 +0100 Subject: Fix multiple variable assignments When doing var foo = bar = 42; then we would assign 42 to bar and bar to foo, resulting in the wrong value for foo if bar was read-only for example. The spec says in 11.13.1.6 that the rval is to be returned, so we just do that via the temp we already have. Change-Id: I44ea895abe4796af10c371baac22c2b26f37b519 Reviewed-by: Lars Knoll --- src/v4/qv4codegen.cpp | 2 +- tests/TestExpectations | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index 60977ac4b7..db7ce16102 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -1172,7 +1172,7 @@ bool Codegen::visit(BinaryExpression *ast) const unsigned t = _block->newTemp(); move(_block->TEMP(t), right); move(left, _block->TEMP(t)); - _expr.code = left; + _expr.code = _block->TEMP(t); } break; } diff --git a/tests/TestExpectations b/tests/TestExpectations index 214d5f49ab..91a8e6b375 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -16,7 +16,6 @@ S13_A3_T1 failing S13.2.3_A1 failing S14_A2 failing 14.1-5-s failing -15.1.1.3-3 failing 15.4.4.14-9-a-10 failing 15.4.4.14-9-a-17 failing 15.4.4.14-9-a-7 failing @@ -45,4 +44,4 @@ S15.2.4.4_A14 failing # Function declaration inside if / while Sbp_12.5_A9_T3 failing -Sbp_12.6.2_A13_T3 failing +Sbp_12.6.2_A13_T3 failing \ No newline at end of file -- cgit v1.2.3 From e962832eb6efcdc14fde65bb724034ac14daf9cd Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 15 Feb 2013 13:31:32 +0100 Subject: Retain the proper nesting structure in the VM:Function objects Change-Id: I83c0889be7fe354f96fca68f786ca2a05121bb56 Reviewed-by: Simon Hausmann --- src/v4/qv4functionobject.h | 3 +++ src/v4/qv4isel_p.cpp | 21 ++++++++++++--------- src/v4/qv4isel_p.h | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index ebf14f8029..69d04364c1 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -120,6 +120,8 @@ struct Function { QVector locals; QVector generatedValues; QVector identifiers; + QVector nestedFunctions; + Function *outer; Lookup *lookups; @@ -132,6 +134,7 @@ struct Function { : name(name) , code(0) , codeData(0) + , outer(0) , lookups(0) , hasNestedFunctions(0) , hasDirectEval(false) diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index 56f4ce74c4..ca57009934 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -23,7 +23,7 @@ EvalInstructionSelection::EvalInstructionSelection(VM::ExecutionEngine *engine, assert(engine); assert(module); - createFunctionMapping(engine, module->rootFunction); + createFunctionMapping(0, module->rootFunction); foreach (IR::Function *f, module->functions) { assert(_irToVM.contains(f)); } @@ -35,29 +35,32 @@ EvalInstructionSelection::~EvalInstructionSelection() EvalISelFactory::~EvalISelFactory() {} -VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngine *engine, Function *irFunction) +VM::Function *EvalInstructionSelection::createFunctionMapping(VM::Function *outer, Function *irFunction) { - VM::Function *vmFunction = engine->newFunction(irFunction->name ? *irFunction->name : QString()); + VM::Function *vmFunction = _engine->newFunction(irFunction->name ? *irFunction->name : QString()); _irToVM.insert(irFunction, vmFunction); vmFunction->hasDirectEval = irFunction->hasDirectEval; vmFunction->usesArgumentsObject = irFunction->usesArgumentsObject; vmFunction->hasNestedFunctions = !irFunction->nestedFunctions.isEmpty(); vmFunction->isStrict = irFunction->isStrict; + vmFunction->outer = outer; + + if (outer) + outer->nestedFunctions.append(vmFunction); foreach (const QString *formal, irFunction->formals) if (formal) - vmFunction->formals.append(engine->newString(*formal)); + vmFunction->formals.append(_engine->newString(*formal)); foreach (const QString *local, irFunction->locals) if (local) - vmFunction->locals.append(engine->newString(*local)); + vmFunction->locals.append(_engine->newString(*local)); foreach (IR::Function *function, irFunction->nestedFunctions) - createFunctionMapping(engine, function); - + createFunctionMapping(vmFunction, function); - if (engine->debugger) - engine->debugger->mapFunction(vmFunction, irFunction); + if (_engine->debugger) + _engine->debugger->mapFunction(vmFunction, irFunction); return vmFunction; } diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h index b336e3a60b..cf0c325612 100644 --- a/src/v4/qv4isel_p.h +++ b/src/v4/qv4isel_p.h @@ -54,7 +54,7 @@ public: void setUseFastLookups(bool b) { useFastLookups = b; } protected: - VM::Function *createFunctionMapping(VM::ExecutionEngine *engine, IR::Function *irFunction); + VM::Function *createFunctionMapping(VM::Function *outer, IR::Function *irFunction); VM::ExecutionEngine *engine() const { return _engine; } virtual void run(VM::Function *vmFunction, IR::Function *function) = 0; -- cgit v1.2.3 From 699633d464fc98e847f0c40b935e8f76c4942eba Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 15 Feb 2013 13:36:49 +0100 Subject: reference locals in outer functions by index This makes V4 reference all variables in scopes that are not the global scope by index. The JIT and the interpreter walk up the scope chain to get the correct reference. Variables are only resolved by name for the global scope, if the scope contains an eval statement (as eval can define new variables) and inside with and catch scopes. Change-Id: Ib9f9d1a03d50124130aefd169eeb071533ba3520 Reviewed-by: Simon Hausmann --- src/v4/moth/qv4instr_moth_p.h | 22 +++++++++++++++++++--- src/v4/moth/qv4isel_moth.cpp | 2 +- src/v4/moth/qv4isel_moth_p.h | 4 +++- src/v4/moth/qv4vme_moth.cpp | 18 +++++++++++++++--- src/v4/qv4codegen.cpp | 41 +++++++++++++++++++++++++---------------- src/v4/qv4ir.cpp | 8 +++++--- src/v4/qv4ir_p.h | 10 +++++++--- src/v4/qv4isel_masm.cpp | 28 ++++++++++++++++++++++------ src/v4/qv4isel_masm_p.h | 5 +++-- 9 files changed, 100 insertions(+), 38 deletions(-) diff --git a/src/v4/moth/qv4instr_moth_p.h b/src/v4/moth/qv4instr_moth_p.h index c28e2a26e6..d77f701fed 100644 --- a/src/v4/moth/qv4instr_moth_p.h +++ b/src/v4/moth/qv4instr_moth_p.h @@ -86,21 +86,25 @@ union Instr ValueType = 0, ArgumentType = 1, LocalType = 2, - TempType = 3 + TempType = 3, + ScopedLocalType = 4 }; VM::Value value; - unsigned type : 2; - unsigned index : 30; + unsigned type : 3; + unsigned scope : 29; + unsigned index; bool isValue() const { return type == ValueType; } bool isArgument() const { return type == ArgumentType; } bool isLocal() const { return type == LocalType; } bool isTemp() const { return type == TempType; } + bool isScopedLocal() const { return type == ScopedLocalType; } static Param createValue(const VM::Value &v) { Param p; p.type = ValueType; + p.scope = 0; p.value = v; return p; } @@ -109,6 +113,7 @@ union Instr { Param p; p.type = ArgumentType; + p.scope = 0; p.index = idx; return p; } @@ -117,6 +122,7 @@ union Instr { Param p; p.type = LocalType; + p.scope = 0; p.index = idx; return p; } @@ -125,6 +131,16 @@ union Instr { Param p; p.type = TempType; + p.scope = 0; + p.index = idx; + return p; + } + + static Param createScopedLocal(unsigned idx, uint scope) + { + Param p; + p.type = ScopedLocalType; + p.scope = scope; p.index = idx; return p; } diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index bd9fc249ee..43c5ed53e9 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -602,7 +602,7 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint const int idx = e->expr->asTemp()->index; // We can only pass a reference into the stack, which holds temps that // are not arguments (idx >= 0) nor locals (idx >= localCound). - singleArgIsTemp = idx >= _function->locals.size(); + singleArgIsTemp = idx >= _function->locals.size() && e->expr->asTemp()->scope == 0; } if (singleArgIsTemp) { diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h index 04f8b9d5b4..6009d29164 100644 --- a/src/v4/moth/qv4isel_moth_p.h +++ b/src/v4/moth/qv4isel_moth_p.h @@ -99,12 +99,14 @@ private: const int index = t->index; if (index < 0) { return Param::createArgument(-index - 1); - } else { + } else if (!t->scope) { const int localCount = _function->locals.size(); if (index < localCount) return Param::createLocal(index); else return Param::createTemp(index - localCount); + } else { + return Param::createScopedLocal(t->index, t->scope); } } else { Q_UNIMPLEMENTED(); diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 092c9ba191..ba9f1e567a 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -84,13 +84,15 @@ static inline VM::Value *getValueRef(QQmlJS::VM::ExecutionContext *context, { #ifdef DO_TRACE_INSTR if (param.isValue()) { - fprintf(stderr, " value\n"); + fprintf(stderr, " value %s\n", param.value.toString(context)->toQString().toUtf8().constData()); } else if (param.isArgument()) { fprintf(stderr, " argument %d\n", param.index); } else if (param.isLocal()) { fprintf(stderr, " local %d\n", param.index); } else if (param.isTemp()) { fprintf(stderr, " temp %d\n", param.index); + } else if (param.isScopedLocal()) { + fprintf(stderr, " temp %d@%d\n", param.index, param.scope); } else { Q_ASSERT(!"INVALID"); } @@ -113,6 +115,16 @@ static inline VM::Value *getValueRef(QQmlJS::VM::ExecutionContext *context, } else if (param.isTemp()) { Q_ASSERT(param.index < stackSize); return stack + param.index; + } else if (param.isScopedLocal()) { + VM::ExecutionContext *c = context; + uint scope = param.scope; + while (scope--) + c = c->outer; + const unsigned index = param.index; + Q_ASSERT(index >= 0); + Q_ASSERT(index < c->variableCount()); + Q_ASSERT(c->locals); + return c->locals + index; } else { Q_UNIMPLEMENTED(); return 0; @@ -225,7 +237,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) - TRACE(property name, "%s, args=%u, argc=%u", qPrintable(instr.name->toQString()), instr.args, instr.argc); + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(instr.name->toQString()), instr.args, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); Q_ASSERT(instr.args + instr.argc < stackSize); VM::Value *args = stack + instr.args; __qmljs_call_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); @@ -235,7 +247,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co Q_ASSERT(instr.args + instr.argc < stackSize); VM::Value *args = stack + instr.args; __qmljs_call_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index), args, instr.argc); - MOTH_END_INSTR(CallProperty) + MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallActivationProperty) Q_ASSERT(instr.args + instr.argc < stackSize); diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index db7ce16102..20a202125f 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -113,7 +113,7 @@ struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } virtual void visitTemp(IR::Temp *e) { - if (e->index < 0) + if (e->index < 0 || e->scope != 0) return; if (! _stmt->d->uses.contains(e->index)) @@ -134,10 +134,11 @@ struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor virtual void visitMove(IR::Move *s) { if (IR::Temp *t = s->target->asTemp()) { - if (t->index >= 0) { - if (! _stmt->d->defs.contains(t->index)) - _stmt->d->defs.append(t->index); - } + if (t->index < 0 || t->scope != 0) + return; + + if (! _stmt->d->defs.contains(t->index)) + _stmt->d->defs.append(t->index); } else { s->target->accept(this); } @@ -1377,23 +1378,31 @@ bool Codegen::visit(FunctionExpression *ast) IR::Expr *Codegen::identifier(const QString &name, int line, int col) { - int index = _env->findMember(name); + uint scope = 0; + Environment *e = _env; + IR::Function *f = _function; - if (! _function->hasDirectEval && !_function->insideWithOrCatch && _env->parent) { + while (f && e->parent) { + if ((f->usesArgumentsObject && name == "arguments") || (!f->isStrict && f->hasDirectEval) || f->insideWithOrCatch) + break; + int index = e->findMember(name); + assert (index < e->members.size()); if (index != -1) { - return _block->TEMP(index); + return _block->TEMP(index, scope); } - index = indexOfArgument(&name); - if (index != -1) { - return _block->TEMP(-(index + 1)); + if (!scope) { + // ### should be able to do this for outer scopes as well + index = indexOfArgument(&name); + if (index != -1) { + return _block->TEMP(-(index + 1), scope); + } } + ++scope; + e = e->parent; + f = f->outer; } - if (index >= _env->members.size()) { - // named local variable, e.g. in a catch statement - return _block->TEMP(index); - } - + // global context or with. Lookup by name return _block->NAME(name, line, col); } diff --git a/src/v4/qv4ir.cpp b/src/v4/qv4ir.cpp index fca14df0b3..0d81328ed8 100644 --- a/src/v4/qv4ir.cpp +++ b/src/v4/qv4ir.cpp @@ -274,6 +274,8 @@ void Temp::dump(QTextStream &out) } else { out << '%' << index; // temp } + if (scope) + out << "@" << scope; } void Closure::dump(QTextStream &out) @@ -400,7 +402,7 @@ void Ret::dump(QTextStream &out, Mode) Function *Module::newFunction(const QString &name, Function *outer) { - Function *f = new Function(this, name); + Function *f = new Function(this, outer, name); functions.append(f); if (!outer) { assert(!rootFunction); @@ -463,10 +465,10 @@ unsigned BasicBlock::newTemp() return function->tempCount++; } -Temp *BasicBlock::TEMP(int index) +Temp *BasicBlock::TEMP(int index, uint scope) { Temp *e = function->New(); - e->init(index); + e->init(index, scope); return e; } diff --git a/src/v4/qv4ir_p.h b/src/v4/qv4ir_p.h index e39ef94d0e..8bab68b47e 100644 --- a/src/v4/qv4ir_p.h +++ b/src/v4/qv4ir_p.h @@ -309,10 +309,12 @@ struct Name: Expr { struct Temp: Expr { int index; + int scope; // how many scopes outside the current one? - void init(int index) + void init(int index, int scope = 0) { this->index = index; + this->scope = scope; } virtual void accept(ExprVisitor *v) { v->visitTemp(this); } @@ -612,6 +614,7 @@ struct Function { QList formals; QList locals; QVector nestedFunctions; + Function *outer; int insideWithOrCatch; @@ -622,11 +625,12 @@ struct Function { template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } - Function(Module *module, const QString &name) + Function(Module *module, Function *outer, const QString &name) : module(module) , pool(&module->pool) , tempCount(0) , maxNumberOfArguments(0) + , outer(outer) , insideWithOrCatch(0) , hasDirectEval(false) , usesArgumentsObject(false) @@ -685,7 +689,7 @@ struct BasicBlock { unsigned newTemp(); - Temp *TEMP(int index); + Temp *TEMP(int index, uint scope = 0); Expr *CONST(Type type, double value); Expr *STRING(const QString *value); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 2127c8fa70..6866dc0520 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -100,8 +100,8 @@ static const int calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeo const Assembler::VoidType Assembler::Void; -Assembler::Assembler(IR::Function* function) - : _function(function) +Assembler::Assembler(IR::Function* function, VM::Function *vmFunction) + : _function(function), _vmFunction(vmFunction) { } @@ -124,14 +124,30 @@ void Assembler::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, IR::Temp *t) { int32_t offset = 0; + int scope = t->scope; + VM::Function *f = _vmFunction; + RegisterID context = ContextRegister; + if (scope) { + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, outer)), ScratchRegister); + --scope; + f = f->outer; + context = ScratchRegister; + while (scope) { + loadPtr(Address(context, offsetof(ExecutionContext, outer)), context); + f = f->outer; + --scope; + } + } if (t->index < 0) { + assert(t->scope == 0); const int arg = -t->index - 1; - loadPtr(Address(ContextRegister, offsetof(ExecutionContext, arguments)), reg); + loadPtr(Address(context, offsetof(ExecutionContext, arguments)), reg); offset = arg * sizeof(Value); - } else if (t->index < _function->locals.size()) { - loadPtr(Address(ContextRegister, offsetof(ExecutionContext, locals)), reg); + } else if (t->index < f->locals.size()) { + loadPtr(Address(context, offsetof(ExecutionContext, locals)), reg); offset = t->index * sizeof(Value); } else { + assert(t->scope == 0); const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size() + 1; // StackFrameRegister points to its old value on the stack, so even for the first temp we need to // subtract at least sizeof(Value). @@ -420,7 +436,7 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) qSwap(_vmFunction, vmFunction); qSwap(_lookups, lookups); Assembler* oldAssembler = _as; - _as = new Assembler(_function); + _as = new Assembler(_function, _vmFunction); int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) + 1; locals = (locals + 1) & ~1; diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 8500c2bc18..5d1bf9d747 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -59,7 +59,7 @@ namespace MASM { class Assembler : public JSC::MacroAssembler { public: - Assembler(IR::Function* function); + Assembler(IR::Function* function, VM::Function *vmFunction); #if CPU(X86) #undef VALUE_FITS_IN_REGISTER @@ -684,7 +684,8 @@ public: void link(VM::Function *vmFunc); private: - IR::Function* _function; + IR::Function *_function; + VM::Function *_vmFunction; QHash _addrs; QHash > _patches; QList _callsToLink; -- cgit v1.2.3 From c586479abe5ec10376ca5625e4e5fb1d4b7c4795 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 16 Feb 2013 00:15:34 +0100 Subject: Minor cleanup Change-Id: I8c3fea7c6b330c3e32b10c945f6e7b96a06daa8e Reviewed-by: Simon Hausmann --- src/v4/qv4functionobject.cpp | 12 ++---------- src/v4/qv4functionobject.h | 8 ++++---- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 013bdca3e0..437ea704df 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -390,12 +390,8 @@ Value ScriptFunction::call(Managed *that, ExecutionContext *context, const Value ctx->thisObject = thisObject; if (!f->strictMode && !thisObject.isObject()) { - // Built-in functions allow for the this object to be null or undefined. This overrides - // the behaviour of changing thisObject to the global object if null/undefined and allows - // the built-in functions for example to throw a type error if null is passed. if (thisObject.isUndefined() || thisObject.isNull()) { - if (!f->isBuiltinFunction) - ctx->thisObject = context->engine->globalObject; + ctx->thisObject = context->engine->globalObject; } else { ctx->thisObject = Value::fromObject(thisObject.toObject(context)); } @@ -441,12 +437,8 @@ Value BuiltinFunctionOld::call(Managed *that, ExecutionContext *context, const V // Built-in functions allow for the this object to be null or undefined. This overrides // the behaviour of changing thisObject to the global object if null/undefined and allows // the built-in functions for example to throw a type error if null is passed. - if (thisObject.isUndefined() || thisObject.isNull()) { - if (!f->isBuiltinFunction) - ctx->thisObject = context->engine->globalObject; - } else { + if (!thisObject.isUndefined() && !thisObject.isNull()) ctx->thisObject = Value::fromObject(thisObject.toObject(context)); - } } ctx->function = f; diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index 69d04364c1..bbe6306e98 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -125,10 +125,10 @@ struct Function { Lookup *lookups; - bool hasNestedFunctions : 1; - bool hasDirectEval : 1; - bool usesArgumentsObject : 1; - bool isStrict : 1; + bool hasNestedFunctions; + bool hasDirectEval; + bool usesArgumentsObject; + bool isStrict; Function(String *name) : name(name) -- cgit v1.2.3 From 5172497e7151faba6ddae090e259b88ced988e2b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 17 Feb 2013 20:42:21 +0100 Subject: [masm] Fix incorrect stack pointer adjustment when leaving function Make sure to calculate the frame size when entering the function the same way as when leaving it, otherwise the stack pointer adjustment is wrong and we get nice crashes. Change-Id: I19f953c3243cf6f1448ad95cad7587fbdca2ae6d Reviewed-by: Erik Verbruggen --- src/v4/qv4isel_masm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 6866dc0520..7c120963f3 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -205,7 +205,7 @@ void Assembler::leaveStandardStackFrame(int locals) loadPtr(Address(StackPointerRegister, i * sizeof(void*)), calleeSavedRegisters[i]); // space for the locals and the callee saved registers - int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*); + int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*) * calleeSavedRegisterCount; #if CPU(X86) || CPU(X86_64) frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX #endif -- cgit v1.2.3 From 3aad45174e1c5686077d926d08caf578e1b7e743 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 17 Feb 2013 20:44:48 +0100 Subject: [masm] Fix typo on register assignment On ARM registers are in JSC::ARMRegisters instead of JSC::X86Registers :) Change-Id: Ib11f0b3caa84a5015905f0a7937b4250c6f76c78 Reviewed-by: Erik Verbruggen --- src/v4/qv4isel_masm_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 5d1bf9d747..2bf0f32da5 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -126,7 +126,7 @@ public: static const RegisterID ContextRegister = JSC::ARMRegisters::r5; static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; static const RegisterID ScratchRegister = JSC::ARMRegisters::r6; - static const RegisterID IntegerOpRegister = JSC::X86Registers::r0; + static const RegisterID IntegerOpRegister = JSC::ARMRegisters::r0; static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; static const int RegisterSize = 4; -- cgit v1.2.3 From 25a61d3d233c4d26e7ac8556a15fe2404420dbbe Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 17 Feb 2013 21:06:13 +0100 Subject: [masm] Support inline ops on architectures without memory src operands Change-Id: Idc4240c0fae35e42246f176536b9c16ee28123d2 Reviewed-by: Erik Verbruggen --- src/v4/qv4isel_masm_p.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 2bf0f32da5..7af04fc3f3 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -64,6 +64,7 @@ public: #undef VALUE_FITS_IN_REGISTER #undef ARGUMENTS_IN_REGISTERS +#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; static const RegisterID StackPointerRegister = JSC::X86Registers::esp; @@ -89,6 +90,7 @@ public: #define VALUE_FITS_IN_REGISTER #define ARGUMENTS_IN_REGISTERS +#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; static const RegisterID StackPointerRegister = JSC::X86Registers::esp; @@ -120,6 +122,7 @@ public: #undef VALUE_FITS_IN_REGISTER #define ARGUMENTS_IN_REGISTERS +#undef HAVE_ALU_OPS_WITH_MEM_OPERAND static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4; static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; @@ -572,7 +575,12 @@ public: Jump inline_add32(Address addr, RegisterID reg) { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) return branchAdd32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchAdd32(Overflow, ScratchRegister, reg); +#endif } Jump inline_add32(TrustedImm32 imm, RegisterID reg) @@ -582,7 +590,12 @@ public: Jump inline_sub32(Address addr, RegisterID reg) { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) return branchSub32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchSub32(Overflow, ScratchRegister, reg); +#endif } Jump inline_sub32(TrustedImm32 imm, RegisterID reg) @@ -592,7 +605,12 @@ public: Jump inline_mul32(Address addr, RegisterID reg) { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) return branchMul32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchMul32(Overflow, ScratchRegister, reg); +#endif } Jump inline_mul32(TrustedImm32 imm, RegisterID reg) @@ -647,7 +665,12 @@ public: Jump inline_and32(Address addr, RegisterID reg) { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) and32(addr, reg); +#else + load32(addr, ScratchRegister); + and32(ScratchRegister, reg); +#endif return Jump(); } @@ -659,7 +682,12 @@ public: Jump inline_or32(Address addr, RegisterID reg) { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) or32(addr, reg); +#else + load32(addr, ScratchRegister); + or32(ScratchRegister, reg); +#endif return Jump(); } @@ -671,7 +699,12 @@ public: Jump inline_xor32(Address addr, RegisterID reg) { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) xor32(addr, reg); +#else + load32(addr, ScratchRegister); + xor32(ScratchRegister, reg); +#endif return Jump(); } -- cgit v1.2.3 From 0b2752565b95fdf3e623abdfbd3501f8f9579930 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 17 Feb 2013 21:15:29 +0100 Subject: [masm] Cleanup argument handling Simplify the code for determining whether to push a function call parameter onto the stack or into a register. Change-Id: I3ab9230b8c0a3b2466c3000d89faf4fd79f927eb Reviewed-by: Erik Verbruggen --- src/v4/qv4isel_masm_p.h | 64 +++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 40 deletions(-) diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 7af04fc3f3..821755c8bb 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -455,37 +455,23 @@ public: static inline int sizeOfArgument(TrustedImm32) { return 4; } - struct ArgumentLoader + template + int loadArgumentOnStackOrRegister(const T &value) { - ArgumentLoader(Assembler* _assembler, int totalNumberOfArguments) - : assembler(_assembler) - , stackSpaceForArguments(0) - , currentRegisterIndex(qMin(totalNumberOfArguments - 1, RegisterArgumentCount - 1)) - { - } - - template - void load(T argument) - { - if (currentRegisterIndex >= 0) { - assembler->loadArgument(argument, registerForArgument(currentRegisterIndex)); - --currentRegisterIndex; - } else { - assembler->push(argument); - stackSpaceForArguments += sizeOfArgument(argument); - } - } - - void load(VoidType) - { - if (currentRegisterIndex >= 0) - --currentRegisterIndex; + if (argumentNumber < RegisterArgumentCount) { + loadArgument(value, registerForArgument(argumentNumber)); + return 0; + } else { + push(value); + return sizeOfArgument(value); } + } - Assembler *assembler; - int stackSpaceForArguments; - int currentRegisterIndex; - }; + template + int loadArgumentOnStackOrRegister(const VoidType &value) + { + return 0; + } template void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) @@ -502,26 +488,24 @@ public: returnValueOnStack = true; } - ArgumentLoader l(this, totalNumberOfArgs); - l.load(arg6); - l.load(arg5); - l.load(arg4); - l.load(arg3); - l.load(arg2); - l.load(arg1); + int stackSpaceUsedForArgs = 0; + stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<5>(arg6); + stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<4>(arg5); + stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<3>(arg4); + stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<2>(arg3); + stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<1>(arg2); + stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<0>(arg1); if (returnValueOnStack) { // Load address of return value - l.load(Pointer(StackPointerRegister, l.stackSpaceForArguments)); + push(Pointer(StackPointerRegister, stackSpaceUsedForArgs)); } callAbsolute(functionName, function); - int stackSizeToCorrect = l.stackSpaceForArguments; - if (returnValueOnStack) { - stackSizeToCorrect -= sizeof(void*); // Callee removed the hidden argument (address of return value) + int stackSizeToCorrect = stackSpaceUsedForArgs; + if (returnValueOnStack) stackSizeToCorrect += sizeOfReturnValueOnStack; - } storeArgument(ReturnValueRegister, r); -- cgit v1.2.3 From 6274c47ad3b76a4e248f85af6b3f6a1308f4a69c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 17 Feb 2013 21:36:02 +0100 Subject: [masm] Fix function return on arm Whether we should do ret(n) or ret should depend on whether the caller provides the pointer to the return value as hidden first parameter or not. That's the case on ia32 but not on x86-64 or arm, where the first parameter register is used instead. So the correct preprocessor macro to use here is ARGUMENTS_IN_REGISTERS instead of VALUE_FITS_IN_REGISTER. Change-Id: I3a8a8fa316896848baca37626f87ed98c096e14a Reviewed-by: Erik Verbruggen --- src/v4/qv4isel_masm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 7c120963f3..60103215b1 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -465,7 +465,7 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) } _as->leaveStandardStackFrame(locals); -#ifndef VALUE_FITS_IN_REGISTER +#ifndef ARGUMENTS_IN_REGISTERS // Emulate ret(n) instruction // Pop off return address into scratch register ... _as->pop(Assembler::ScratchRegister); -- cgit v1.2.3 From b76ec5578467fe2261c542e920b334bdd5983ded Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 17 Feb 2013 21:44:05 +0100 Subject: [masm] Fix writing of return value on arm On arm the pointer to the storage of the VM::Value to return is passed in r0. The value in that register is destroyed soon after, so later when we want to access it in visitRet() we'll get garbage. To solve this we behave similar to gcc now, which upon function entry saves the values of the registers used for parameter passing onto the stack. Except that on arm we now do this before pushing the link register, which makes the stack frame look identical to ia32. (old ebp / return address / arg 0 / arg 1 / ...) With that we can theoretically access the pointer to the return value storage. In practice we also need to change meaning of the addressForArgument() helper function to only return the address of arguments on the stack. But that makes sense since Address() is meaningless for values passed in registers. Also tightened the #ifdef in visitRet() for determining whether to use the return value register or not. That wasn't strictly necessary, but makes the condition a bit clearer. Change-Id: I6fbef6645275ebaa75484d666b4bbfd073f945a5 Reviewed-by: Erik Verbruggen --- src/v4/qv4isel_masm.cpp | 2 +- src/v4/qv4isel_masm_p.h | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 60103215b1..bf1d129446 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -1011,7 +1011,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { if (IR::Temp *t = s->expr->asTemp()) { -#ifdef VALUE_FITS_IN_REGISTER +#if defined(ARGUMENTS_IN_REGISTERS) && defined(VALUE_FITS_IN_REGISTER) _as->copyValue(Assembler::ReturnValueRegister, t); #else _as->loadPtr(addressForArgument(0), Assembler::ReturnValueRegister); diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 821755c8bb..c68ee91f3e 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -147,11 +147,22 @@ public: }; inline void platformEnterStandardStackFrame() { + // Move the register arguments onto the stack as if they were + // pushed by the caller, just like on ia32. This gives us consistent + // access to the parameters if we need to. + push(JSC::ARMRegisters::r3); + push(JSC::ARMRegisters::r2); + push(JSC::ARMRegisters::r1); + push(JSC::ARMRegisters::r0); push(JSC::ARMRegisters::lr); } inline void platformLeaveStandardStackFrame() { pop(JSC::ARMRegisters::lr); + push(JSC::ARMRegisters::r0); + push(JSC::ARMRegisters::r1); + push(JSC::ARMRegisters::r2); + push(JSC::ARMRegisters::r3); } #else #error Argh. @@ -775,13 +786,10 @@ protected: Address addressForArgument(int index) const { - if (index < Assembler::RegisterArgumentCount) - return Address(_as->registerForArgument(index), 0); - // StackFrameRegister points to its old value on the stack, and above // it we have the return address, hence the need to step over two // values before reaching the first argument. - return Address(Assembler::StackFrameRegister, (index - Assembler::RegisterArgumentCount + 2) * sizeof(void*)); + return Address(Assembler::StackFrameRegister, (index + 2) * sizeof(void*)); } // Some run-time functions take (Value* args, int argc). This function is for populating -- cgit v1.2.3 From e4809181f2798154c71d5b3fc779f2dee3df8f1c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 17 Feb 2013 22:12:22 +0100 Subject: [masm] Fix function name replacement in disassembly on ia32 Cast the void* to a quintptr, to make sure we call the right QByteArray::number overload depending on the size of a pointer on the archicture. Otherwise we generate 0x1324 strings that we can't successfully replace with the function names in the disassembler output. Change-Id: Iddc82534487d93547b597d39286b92ffdff6da6c Reviewed-by: Erik Verbruggen --- src/v4/qv4isel_masm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index bf1d129446..0d8d2aeab4 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -360,7 +360,7 @@ static void printDisassembledOutputWithCalls(const char* output, const QHash::ConstIterator it = functions.begin(), end = functions.end(); it != end; ++it) { - QByteArray ptrString = QByteArray::number(qlonglong(it.key()), 16); + QByteArray ptrString = QByteArray::number(quintptr(it.key()), 16); ptrString.prepend("0x"); processedOutput = processedOutput.replace(ptrString, it.value()); } -- cgit v1.2.3 From bbf1b545a2fa216c242a748a5877e47f41c73e86 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 17 Feb 2013 22:21:51 +0100 Subject: Port __qmljs_get_thisobject to new calling convention Change-Id: I59a921d6838fd4e8419bf6cf62d5dca39e1142cd Reviewed-by: Erik Verbruggen --- src/v4/llvm_runtime.cpp | 2 +- src/v4/moth/qv4vme_moth.cpp | 2 +- src/v4/qmljs_runtime.cpp | 4 ++-- src/v4/qmljs_runtime.h | 2 +- src/v4/qv4isel_masm.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index bfd9ba0e6c..0322eccca5 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -502,7 +502,7 @@ void __qmljs_llvm_foreach_next_property_name(Value *result, Value *it) void __qmljs_llvm_get_this_object(ExecutionContext *ctx, Value *result) { - *result = __qmljs_get_thisObject(ctx); + __qmljs_get_thisObject(ctx, result); } void __qmljs_llvm_delete_subscript(ExecutionContext *ctx, Value *result, Value *base, Value *index) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index ba9f1e567a..5447e1514b 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -432,7 +432,7 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(Ret) MOTH_BEGIN_INSTR(LoadThis) - VALUE(instr.result) = __qmljs_get_thisObject(context); + __qmljs_get_thisObject(context, VALUEPTR(instr.result)); MOTH_END_INSTR(LoadThis) MOTH_BEGIN_INSTR(InplaceElementOp) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 1994e37852..b237af2fb2 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -726,9 +726,9 @@ void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value &object, int } -Value __qmljs_get_thisObject(ExecutionContext *ctx) +void __qmljs_get_thisObject(ExecutionContext *ctx, Value *result) { - return ctx->thisObject; + *result = ctx->thisObject; } uint __qmljs_equal(const Value &x, const Value &y, ExecutionContext *ctx) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 9a16a441f6..54e3dbfd1e 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -163,7 +163,7 @@ void __qmljs_foreach_iterator_object(ExecutionContext *ctx, Value *result, const void __qmljs_foreach_next_property_name(Value *result, const Value &foreach_iterator); // context -Value __qmljs_get_thisObject(ExecutionContext *ctx); +void __qmljs_get_thisObject(ExecutionContext *ctx, Value *result); // type conversion and testing Value __qmljs_to_primitive(const Value &value, ExecutionContext *ctx, int typeHint); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 0d8d2aeab4..2160b406b8 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -677,7 +677,7 @@ void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Te void InstructionSelection::loadThisObject(IR::Temp *temp) { - generateFunctionCall(temp, __qmljs_get_thisObject, Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_get_thisObject, Assembler::ContextRegister, Assembler::PointerToValue(temp)); } void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) -- cgit v1.2.3 From 1d1b6b0ffcd40d26e30edb1d94b9b8e0300c21cc Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 21 Feb 2013 12:07:17 +0100 Subject: Fix use of CPU(X86_64) to determine 64-bit architecture Use QT_POINTER_SIZE == 8 instead Change-Id: I7e3283132682c9f882b4d7b1ee067428bb23d42a Reviewed-by: Erik Verbruggen --- src/v4/qmljs_value.h | 17 ++++++++--------- src/v4/qv4mm.cpp | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/v4/qmljs_value.h b/src/v4/qmljs_value.h index 56d909422b..223be8a235 100644 --- a/src/v4/qmljs_value.h +++ b/src/v4/qmljs_value.h @@ -81,8 +81,7 @@ struct Q_V4_EXPORT Value union { uint uint_32; int int_32; -#if CPU(X86_64) -#else +#if QT_POINTER_SIZE == 4 Managed *m; Object *o; String *s; @@ -133,7 +132,7 @@ struct Q_V4_EXPORT Value inline bool isInteger() const { return tag == _Integer_Type; } inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } -#if CPU(X86_64) +#if QT_POINTER_SIZE == 8 inline bool isString() const { return (tag & Type_Mask) == String_Type; } inline bool isObject() const { return (tag & Type_Mask) == Object_Type; } #else @@ -160,7 +159,7 @@ struct Q_V4_EXPORT Value return int_32; } -#if CPU(X86_64) +#if QT_POINTER_SIZE == 8 String *stringValue() const { return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift)); } @@ -214,7 +213,7 @@ struct Q_V4_EXPORT Value Object *toObject(ExecutionContext *ctx) const; inline bool isPrimitive() const { return !isObject(); } -#if CPU(X86_64) +#if QT_POINTER_SIZE == 8 inline bool integerCompatible() const { const quint64 mask = quint64(ConvertibleToInt) << 32; return (val & mask) == mask; @@ -273,7 +272,7 @@ struct Q_V4_EXPORT Value inline Value Value::undefinedValue() { Value v; -#if CPU(X86_64) +#if QT_POINTER_SIZE == 8 v.val = quint64(_Undefined_Type) << Tag_Shift; #else v.tag = _Undefined_Type; @@ -285,7 +284,7 @@ inline Value Value::undefinedValue() inline Value Value::nullValue() { Value v; -#if CPU(X86_64) +#if QT_POINTER_SIZE == 8 v.val = quint64(_Null_Type) << Tag_Shift; #else v.tag = _Null_Type; @@ -332,7 +331,7 @@ inline Value Value::fromUInt32(uint i) inline Value Value::fromString(String *s) { Value v; -#if CPU(X86_64) +#if QT_POINTER_SIZE == 8 v.val = (quint64)s; v.val |= quint64(_String_Type) << Tag_Shift; #else @@ -345,7 +344,7 @@ inline Value Value::fromString(String *s) inline Value Value::fromObject(Object *o) { Value v; -#if CPU(X86_64) +#if QT_POINTER_SIZE == 8 v.val = (quint64)o; v.val |= quint64(_Object_Type) << Tag_Shift; #else diff --git a/src/v4/qv4mm.cpp b/src/v4/qv4mm.cpp index b81456b778..6fed5ec5ee 100644 --- a/src/v4/qv4mm.cpp +++ b/src/v4/qv4mm.cpp @@ -387,7 +387,7 @@ void MemoryManager::collectFromStack() const for (; current < top; ++current) { char* genericPtr = -#if CPU(X86_64) +#if QT_POINTER_SIZE == 8 reinterpret_cast((*current) & ~(quint64(Value::Type_Mask) << Value::Tag_Shift)); #else reinterpret_cast(*current); -- cgit v1.2.3 From f41cb7eb71f6429fe94e9c9a70c7550631920c6b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 16 Feb 2013 13:19:36 +0100 Subject: Store 0 constants as integers, not as doubles Change-Id: Ibb49d1fd8221d65262b1c18b9833233ef97cee8e Reviewed-by: Simon Hausmann --- src/v4/qv4isel_util_p.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/v4/qv4isel_util_p.h b/src/v4/qv4isel_util_p.h index 693af03e88..b88f41093c 100644 --- a/src/v4/qv4isel_util_p.h +++ b/src/v4/qv4isel_util_p.h @@ -35,6 +35,16 @@ namespace QQmlJS { +inline bool isNegative(double d) +{ + uchar *dch = (uchar *)&d; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return (dch[0] & 0x80); + else + return (dch[7] & 0x80); + +} + inline VM::Value convertToValue(IR::Const *c) { switch (c->type) { @@ -47,7 +57,7 @@ inline VM::Value convertToValue(IR::Const *c) case IR::NumberType: { int ival = (int)c->value; // +0 != -0, so we need to convert to double when negating 0 - if (ival == c->value && c->value != -0) { + if (ival == c->value && !(c->value == 0 && isNegative(c->value))) { return VM::Value::fromInt32(ival); } else { return VM::Value::fromDouble(c->value); -- cgit v1.2.3 From baf73673e15e7a2a4594a53d953a76c021b9fc82 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 16 Feb 2013 20:44:53 +0100 Subject: Fix inline assembly version of ushr -1 >> 0 should return UINT_MAX, as the result is an unsigned int according to spec. The only way the result of the inline shr operation can be signed is by shifting 0 bytes. But the easiest implementation is to test the result for signed-ness and then fall back to the slow implementation. Change-Id: Ic4614006d06cf01376ef95b6f23ca2c7216a2812 Reviewed-by: Simon Hausmann --- src/v4/qv4isel_masm_p.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index c68ee91f3e..91c908f0b0 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -648,14 +648,24 @@ public: load32(addr, ScratchRegister); and32(TrustedImm32(0x1f), ScratchRegister); urshift32(ScratchRegister, reg); - return Jump(); +#if CPU(X86) || CPU(X86_64) + m_assembler.testl_rr(reg, reg); + return Jump(m_assembler.jCC(x86Condition(Signed))); +#else +#error "Implement me: Branch if result is negative" +#endif } Jump inline_ushr32(TrustedImm32 imm, RegisterID reg) { imm.m_value &= 0x1f; urshift32(imm, reg); - return Jump(); +#if CPU(X86) || CPU(X86_64) + m_assembler.testl_rr(reg, reg); + return Jump(m_assembler.jCC(x86Condition(Signed))); +#else +#error "Implement me: Branch if result is negative" +#endif } Jump inline_and32(Address addr, RegisterID reg) -- cgit v1.2.3 From 78e7edb64ef814a59072bf36ff2f2a95fa8eceac Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 16 Feb 2013 23:26:16 +0100 Subject: Fix the sameValue algorithm when comparing ints to doubles. Change-Id: I96fb3e8c47a336ef4e0e3cab44e6dfd4d5aff70a Reviewed-by: Simon Hausmann --- src/v4/qmljs_value.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/v4/qmljs_value.cpp b/src/v4/qmljs_value.cpp index 91f4ec828f..892bcdfd43 100644 --- a/src/v4/qmljs_value.cpp +++ b/src/v4/qmljs_value.cpp @@ -92,10 +92,10 @@ bool Value::sameValue(Value other) const { return true; if (isString() && other.isString()) return stringValue()->isEqualTo(other.stringValue()); - if (isInteger() && int_32 == 0 && other.dbl == 0) - return true; - if (dbl == 0 && other.isInteger() && other.int_32 == 0) - return true; + if (isInteger()) + return int_32 ? (double(int_32) == other.dbl) : (other.val == 0); + if (other.isInteger()) + return other.int_32 ? (dbl == double(other.int_32)) : (val == 0); return false; } -- cgit v1.2.3 From b9f623b5914cb1f894a2b8c68e6e343c55ec3531 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sat, 16 Feb 2013 23:27:07 +0100 Subject: Small cleanup Change-Id: I1733ad823ef00114b4544bb0bdf40fede4eea073 Reviewed-by: Simon Hausmann --- src/v4/qmljs_runtime.cpp | 18 +++++++++--------- src/v4/qmljs_value.h | 7 +++++++ tests/TestExpectations | 1 - 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index b237af2fb2..8b8bdbb22f 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -549,22 +549,22 @@ void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *na void __qmljs_get_element(ExecutionContext *ctx, Value *result, const Value &object, const Value &index) { - uint type = object.type(); uint idx = index.asArrayIndex(); Object *o = object.asObject(); if (!o) { - if (type == Value::String_Type && idx < UINT_MAX) { - String *str = object.stringValue(); - if (idx >= (uint)str->toQString().length()) { + if (idx < UINT_MAX) { + if (String *str = object.asString()) { + if (idx >= (uint)str->toQString().length()) { + if (result) + *result = Value::undefinedValue(); + return; + } + const QString s = str->toQString().mid(idx, 1); if (result) - *result = Value::undefinedValue(); + *result = Value::fromString(ctx, s); return; } - const QString s = str->toQString().mid(idx, 1); - if (result) - *result = Value::fromString(ctx, s); - return; } o = __qmljs_convert_to_object(ctx, object); diff --git a/src/v4/qmljs_value.h b/src/v4/qmljs_value.h index 223be8a235..40da02b073 100644 --- a/src/v4/qmljs_value.h +++ b/src/v4/qmljs_value.h @@ -244,6 +244,7 @@ struct Q_V4_EXPORT Value return b; } + String *asString() const; Managed *asManaged() const; Object *asObject() const; FunctionObject *asFunctionObject() const; @@ -459,6 +460,12 @@ inline uint Value::asArrayLength(ExecutionContext *ctx, bool *ok) const return idx; } +inline String *Value::asString() const +{ + if (isString()) + return stringValue(); + return 0; +} inline Managed *Value::asManaged() const { diff --git a/tests/TestExpectations b/tests/TestExpectations index 91a8e6b375..694b22a03b 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -9,7 +9,6 @@ 10.4.3-1-82-s failing 11.2.3-3_3 failing S11.5.3_A4_T2 failing -S11.8.6_A3 failing S11.8.6_A5_T2 failing S13_A15_T4 failing S13_A3_T1 failing -- cgit v1.2.3 From be9957687925422af315db8dda988a8655e0b6ac Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 24 Feb 2013 22:31:58 +0100 Subject: Some smaller optimisations Ideally these checks should get inlined in the generated assembly. Change-Id: I4f63f7235a7d3bbdf8413df9f7d674104ff95b07 Reviewed-by: Simon Hausmann --- src/v4/qmljs_runtime.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 54e3dbfd1e..d0daed4cd1 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -600,12 +600,12 @@ inline void __qmljs_sne(ExecutionContext *, Value *result, const Value &left, co inline Bool __qmljs_cmp_gt(ExecutionContext *ctx, const Value &left, const Value &right) { TRACE2(left, right); + if (Value::integerCompatible(left, right)) + return left.integerValue() > right.integerValue(); Value l = __qmljs_to_primitive(left, ctx, NUMBER_HINT); Value r = __qmljs_to_primitive(right, ctx, NUMBER_HINT); - if (Value::integerCompatible(l, r)) - return l.integerValue() > r.integerValue(); if (Value::bothDouble(l, r)) { return l.doubleValue() > r.doubleValue(); } else if (l.isString() && r.isString()) { @@ -620,12 +620,12 @@ inline Bool __qmljs_cmp_gt(ExecutionContext *ctx, const Value &left, const Value inline Bool __qmljs_cmp_lt(ExecutionContext *ctx, const Value &left, const Value &right) { TRACE2(left, right); + if (Value::integerCompatible(left, right)) + return left.integerValue() < right.integerValue(); Value l = __qmljs_to_primitive(left, ctx, NUMBER_HINT); Value r = __qmljs_to_primitive(right, ctx, NUMBER_HINT); - if (Value::integerCompatible(l, r)) - return l.integerValue() < r.integerValue(); if (Value::bothDouble(l, r)) { return l.doubleValue() < r.doubleValue(); } else if (l.isString() && r.isString()) { @@ -640,12 +640,12 @@ inline Bool __qmljs_cmp_lt(ExecutionContext *ctx, const Value &left, const Value inline Bool __qmljs_cmp_ge(ExecutionContext *ctx, const Value &left, const Value &right) { TRACE2(left, right); + if (Value::integerCompatible(left, right)) + return left.integerValue() >= right.integerValue(); Value l = __qmljs_to_primitive(left, ctx, NUMBER_HINT); Value r = __qmljs_to_primitive(right, ctx, NUMBER_HINT); - if (Value::integerCompatible(l, r)) - return l.integerValue() >= r.integerValue(); if (Value::bothDouble(l, r)) { return l.doubleValue() >= r.doubleValue(); } else if (l.isString() && r.isString()) { @@ -660,12 +660,12 @@ inline Bool __qmljs_cmp_ge(ExecutionContext *ctx, const Value &left, const Value inline Bool __qmljs_cmp_le(ExecutionContext *ctx, const Value &left, const Value &right) { TRACE2(left, right); + if (Value::integerCompatible(left, right)) + return left.integerValue() <= right.integerValue(); Value l = __qmljs_to_primitive(left, ctx, NUMBER_HINT); Value r = __qmljs_to_primitive(right, ctx, NUMBER_HINT); - if (Value::integerCompatible(l, r)) - return l.integerValue() <= r.integerValue(); if (Value::bothDouble(l, r)) { return l.doubleValue() <= r.doubleValue(); } else if (l.isString() && r.isString()) { -- cgit v1.2.3 From 5c8c2307c59de7c3b024d9e9987e4bbe0528c440 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 25 Feb 2013 10:13:46 +0100 Subject: [masm] Implement sign text using cross-arch masm api Use branchTest32 to implement the test for the sign bit in right shift operations. Change-Id: I07b3ead4d32761ee3d5f529259be5b5987b7ec5a Reviewed-by: Lars Knoll --- src/v4/qv4isel_masm_p.h | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 91c908f0b0..79b08c911a 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -648,24 +648,14 @@ public: load32(addr, ScratchRegister); and32(TrustedImm32(0x1f), ScratchRegister); urshift32(ScratchRegister, reg); -#if CPU(X86) || CPU(X86_64) - m_assembler.testl_rr(reg, reg); - return Jump(m_assembler.jCC(x86Condition(Signed))); -#else -#error "Implement me: Branch if result is negative" -#endif + return branchTest32(Signed, reg, reg); } Jump inline_ushr32(TrustedImm32 imm, RegisterID reg) { imm.m_value &= 0x1f; urshift32(imm, reg); -#if CPU(X86) || CPU(X86_64) - m_assembler.testl_rr(reg, reg); - return Jump(m_assembler.jCC(x86Condition(Signed))); -#else -#error "Implement me: Branch if result is negative" -#endif + return branchTest32(Signed, reg, reg); } Jump inline_and32(Address addr, RegisterID reg) -- cgit v1.2.3 From 2fa092cbcdc4296e976be2c574073e3b42aced40 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 26 Feb 2013 12:45:47 +0100 Subject: Fix failing assertion when running the test suite The test suite does things like "1 instanceof 1" and expects a type error to be thrown. Therefore we should not assert(!"unreachable") when instanceof is called with a numberic constant but just fall back to the run-time implementation, which does the right thing. Change-Id: Iced93e679d56f4491d38c50b669e12dd160c220c Reviewed-by: Lars Knoll --- src/v4/qv4codegen.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index 20a202125f..8430a29ebe 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -661,7 +661,6 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right) case IR::OpInstanceof: case IR::OpIn: - assert(!"unreachabe"); break; case IR::OpIfTrue: // unary ops -- cgit v1.2.3 From dcfdad0f5c19d16b56d3d2c29e696e615ed36cc0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 26 Feb 2013 12:49:38 +0100 Subject: Fix build with clang It tells us quite explicitly how it wants a default constructor for VoidType :) Change-Id: I854370c869f179da7f842fbf675e05678285630d Reviewed-by: Lars Knoll --- src/v4/qv4isel_masm_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 79b08c911a..28d5dca4fd 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -181,7 +181,7 @@ public: {} }; - struct VoidType {}; + struct VoidType { VoidType() {} }; static const VoidType Void; -- cgit v1.2.3 From 31352048f936e68e8609cf57929eed20e3f48ab1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 26 Feb 2013 12:58:16 +0100 Subject: [masm] Prepare local stack frame for new upcoming exception handling Access local temps through a newly allocated LocalsRegister instead of the regular frame pointer register. In the new exception handling we're going to re-enter our function in the middle and want to access the same local temps, but we can't do that through the stack frame pointer then, because that one will _have_ to continue to point to the local stack frame in order for unwinding to work properly. Also the callee saved registers are now stored right below the stack frame pointer instead of at the bottom of the stack. This way they can be described easily in the unwind info as always relative to the canonical frame address. Change-Id: I53ef6291d99396577a72ceb9246f7ca3d99e5137 Reviewed-by: Lars Knoll --- src/v4/qv4isel_masm.cpp | 20 +++++++++++--------- src/v4/qv4isel_masm_p.h | 6 +++++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 2160b406b8..6b687a6b61 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -65,8 +65,9 @@ using namespace QQmlJS::VM; static const Assembler::RegisterID calleeSavedRegisters[] = { // Not used: JSC::X86Registers::rbx, // Not used: JSC::X86Registers::r10, + JSC::X86Registers::r12, // LocalsRegister // Not used: JSC::X86Registers::r13, - JSC::X86Registers::r14 + JSC::X86Registers::r14 // ContextRegister // Not used: JSC::X86Registers::r15, }; #endif @@ -74,8 +75,8 @@ static const Assembler::RegisterID calleeSavedRegisters[] = { #if CPU(X86) static const Assembler::RegisterID calleeSavedRegisters[] = { // Not used: JSC::X86Registers::ebx, - JSC::X86Registers::esi - // Not used: JSC::X86Registers::edi, + JSC::X86Registers::esi, // ContextRegister + JSC::X86Registers::edi // LocalsRegister }; #endif @@ -93,7 +94,7 @@ static const Assembler::RegisterID calleeSavedRegisters[] = { }; #endif -static const int calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]); +const int Assembler::calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]); /* End of platform/calling convention/architecture specific section */ @@ -149,10 +150,9 @@ Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, IR::Temp *t) } else { assert(t->scope == 0); const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size() + 1; - // StackFrameRegister points to its old value on the stack, so even for the first temp we need to - // subtract at least sizeof(Value). offset = - sizeof(Value) * (arg + 1); - reg = StackFrameRegister; + offset -= sizeof(void*) * calleeSavedRegisterCount; + reg = LocalsRegister; } return Pointer(reg, offset); } @@ -195,14 +195,16 @@ void Assembler::enterStandardStackFrame(int locals) subPtr(TrustedImm32(frameSize), StackPointerRegister); for (int i = 0; i < calleeSavedRegisterCount; ++i) - storePtr(calleeSavedRegisters[i], Address(StackPointerRegister, i * sizeof(void*))); + storePtr(calleeSavedRegisters[i], Address(StackFrameRegister, -(i + 1) * sizeof(void*))); + + move(StackFrameRegister, LocalsRegister); } void Assembler::leaveStandardStackFrame(int locals) { // restore the callee saved registers for (int i = calleeSavedRegisterCount - 1; i >= 0; --i) - loadPtr(Address(StackPointerRegister, i * sizeof(void*)), calleeSavedRegisters[i]); + loadPtr(Address(StackFrameRegister, -(i + 1) * sizeof(void*)), calleeSavedRegisters[i]); // space for the locals and the callee saved registers int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*) * calleeSavedRegisterCount; diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 28d5dca4fd..efe0a32e1f 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -68,6 +68,7 @@ public: static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::edi; static const RegisterID ContextRegister = JSC::X86Registers::esi; static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; static const RegisterID ScratchRegister = JSC::X86Registers::ecx; @@ -94,6 +95,7 @@ public: static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::r12; static const RegisterID ContextRegister = JSC::X86Registers::r14; static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; static const RegisterID ScratchRegister = JSC::X86Registers::r10; @@ -167,6 +169,7 @@ public: #else #error Argh. #endif + static const int calleeSavedRegisterCount; // Explicit type to allow distinguishing between // pushing an address itself or the value it points @@ -797,8 +800,9 @@ protected: Pointer argumentAddressForCall(int argument) { const int index = _function->maxNumberOfArguments - argument; - return Pointer(Assembler::StackFrameRegister, sizeof(VM::Value) * (-index) + return Pointer(Assembler::LocalsRegister, sizeof(VM::Value) * (-index) - sizeof(void*) // size of ebp + - sizeof(void*) * Assembler::calleeSavedRegisterCount ); } Pointer baseAddressForCallArguments() -- cgit v1.2.3 From da6ccb1908b1a2f7670be42267844d9971f59ae7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 18 Feb 2013 22:30:42 +0100 Subject: Fix crypto.js on arm Due to our large amount of temps we also end up creating large stack frames and thus add large constants to the stack pointer. That affects the encoding of the immediates and MacroAssemblerARMv7 ASSERTs out for values that require encoding. This is unlikely to get fixed upstream and it's infact impossible to create a testcase with JSC JIT due to the fact that it barely uses the stack frame. I'd rather not patch the upstream file as it is a condition hard to find and a patch easy to drop by accident. Instead this patch adds a simple workaround that comes are low cost: Just load the immediate into a register and do the addition. Change-Id: Ia551a15d2f5f6243b295a9bfd19df778467189ec Reviewed-by: Lars Knoll --- src/v4/qv4isel_masm.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 6b687a6b61..0e29a0f570 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -211,7 +211,14 @@ void Assembler::leaveStandardStackFrame(int locals) #if CPU(X86) || CPU(X86_64) frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX #endif + // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't + // work well for large immediates. +#if CPU(ARM_THUMB2) + move(TrustedImm32(frameSize), Assembler::ScratchRegister); + add32(Assembler::ScratchRegister, StackPointerRegister); +#else addPtr(TrustedImm32(frameSize), StackPointerRegister); +#endif pop(StackFrameRegister); platformLeaveStandardStackFrame(); -- cgit v1.2.3 From 9cab0bc22c59db252eb169eb88d78a47396ebe18 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 27 Feb 2013 12:16:19 +0100 Subject: Fix declaration linkage type to be the same as the defenition. Change-Id: I86c96bd4076b60b896803ee1c53c2ecf9aa86e1f Reviewed-by: Simon Hausmann --- src/v4/qmljs_value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qmljs_value.h b/src/v4/qmljs_value.h index 40da02b073..6b616ebf4f 100644 --- a/src/v4/qmljs_value.h +++ b/src/v4/qmljs_value.h @@ -62,7 +62,7 @@ struct Value; extern "C" { double __qmljs_to_number(const Value &value, ExecutionContext *ctx); -String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value); +Q_V4_EXPORT String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value); Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value); } -- cgit v1.2.3 From 3ad3a6e838ab4f236a032dac67abae9183cb32b9 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 27 Feb 2013 12:17:38 +0100 Subject: Add ToS retrieval and register saving for MSVC. No __asm no cry. Change-Id: I95f4df5d5ba9d04aa0bcc8d0b0b5901d51533d16 Reviewed-by: Simon Hausmann --- src/v4/qv4mm.cpp | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/v4/qv4mm.cpp b/src/v4/qv4mm.cpp index 6fed5ec5ee..67a594df12 100644 --- a/src/v4/qv4mm.cpp +++ b/src/v4/qv4mm.cpp @@ -181,8 +181,19 @@ void MemoryManager::mark() for (QHash::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it) it.key()->mark(); -#if CPU(X86_64) // push all caller saved registers to the stack, so we can find the objects living in these registers +#if COMPILER(MSVC) +# if CPU(X86_64) + HANDLE thread = GetCurrentThread(); + WOW64_CONTEXT ctxt; + /*bool success =*/ Wow64GetThreadContext(thread, &ctxt); +# elif CPU(X86) + HANDLE thread = GetCurrentThread(); + CONTEXT ctxt; + /*bool success =*/ GetThreadContext(thread, &ctxt); +# endif // CPU +#elif COMPILER(CLANG) || COMPILER(GCC) +# if CPU(X86_64) quintptr regs[5]; asm( "mov %%rbp, %0\n" @@ -194,10 +205,10 @@ void MemoryManager::mark() : : ); -#endif - collectFromStack(); +# endif // CPU +#endif // COMPILER - return; + collectFromStack(); } std::size_t MemoryManager::sweep() @@ -346,11 +357,11 @@ void MemoryManager::collectFromStack() const quintptr valueOnStack = 0; #if USE(PTHREADS) -#if OS(DARWIN) +# if OS(DARWIN) void* stackTop = 0; stackTop = pthread_get_stackaddr_np(pthread_self()); quintptr *top = static_cast(stackTop); -#else +# else void* stackBottom = 0; pthread_attr_t attr; pthread_getattr_np(pthread_self(), &attr); @@ -359,16 +370,16 @@ void MemoryManager::collectFromStack() const pthread_attr_destroy(&attr); quintptr *top = static_cast(stackBottom) + stackSize/sizeof(quintptr); -#endif +# endif #elif OS(WINDOWS) -#if COMPILER(MSVC) - NT_TIB *tib; - __asm { - mov eax, fs:[0x18] - mov [tib], eax - } -#endif +# if COMPILER(MSVC) + PNT_TIB tib = (PNT_TIB)NtCurrentTeb(); quintptr *top = static_cast(tib->StackBase); +# else +# error "Unsupported compiler: no way to get the top-of-stack." +# endif +#else +# error "Unsupported platform: no way to get the top-of-stack." #endif // qDebug() << "stack:" << hex << stackTop << stackSize << (stackTop + stackSize); -- cgit v1.2.3 From 048f896a983aba86c603497dd7f7d23f801bcbf4 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 27 Feb 2013 12:18:51 +0100 Subject: Mark throwing functions as no-return. Now the compiler cannot only optimise for this, but also stop complaining about callers not returning any value. Change-Id: I71d98721f70849178613096408e959d7e24dca8a Reviewed-by: Simon Hausmann --- src/v4/qmljs_environment.h | 16 ++++++++-------- src/v4/qmljs_runtime.h | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/v4/qmljs_environment.h b/src/v4/qmljs_environment.h index 27de8b6007..42aeba1200 100644 --- a/src/v4/qmljs_environment.h +++ b/src/v4/qmljs_environment.h @@ -121,14 +121,14 @@ struct ExecutionContext void wireUpPrototype(); - void throwError(const Value &value); - void throwError(const QString &message); - void throwSyntaxError(DiagnosticMessage *message); - void throwTypeError(); - void throwReferenceError(Value value); - void throwRangeError(Value value); - void throwURIError(Value msg); - void throwUnimplemented(const QString &message); + void Q_NORETURN throwError(const Value &value); + void Q_NORETURN throwError(const QString &message); + void Q_NORETURN throwSyntaxError(DiagnosticMessage *message); + void Q_NORETURN throwTypeError(); + void Q_NORETURN throwReferenceError(Value value); + void Q_NORETURN throwRangeError(Value value); + void Q_NORETURN throwURIError(Value msg); + void Q_NORETURN throwUnimplemented(const QString &message); void setProperty(String *name, const Value &value); Value getProperty(String *name); diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index d0daed4cd1..e982bdb34e 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -117,8 +117,8 @@ void __qmljs_builtin_post_decrement_name(ExecutionContext *context, Value *resul void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value &base, String *name); void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value &base, const Value &index); -void __qmljs_builtin_throw(ExecutionContext *context, const Value &val); -void __qmljs_builtin_rethrow(ExecutionContext *context); +void Q_NORETURN __qmljs_builtin_throw(ExecutionContext *context, const Value &val); +void Q_NORETURN __qmljs_builtin_rethrow(ExecutionContext *context); ExecutionContext *__qmljs_builtin_push_with_scope(const Value &o, ExecutionContext *ctx); ExecutionContext *__qmljs_builtin_push_catch_scope(String *exceptionVarName, ExecutionContext *ctx); ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx); @@ -191,7 +191,7 @@ void __qmljs_delete_subscript(ExecutionContext *ctx, Value *result, const Value void __qmljs_delete_member(ExecutionContext *ctx, Value *result, const Value &base, String *name); void __qmljs_delete_name(ExecutionContext *ctx, Value *result, String *name); -void __qmljs_throw(ExecutionContext*, const Value &value); +void Q_NORETURN __qmljs_throw(ExecutionContext*, const Value &value); // actually returns a jmp_buf * Q_V4_EXPORT void *__qmljs_create_exception_handler(ExecutionContext *context); void __qmljs_delete_exception_handler(ExecutionContext *context); -- cgit v1.2.3 From 7f153b3dcd494677231a46aac71ed6aa0cf1f509 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 27 Feb 2013 12:19:53 +0100 Subject: Replace STL function that MS deprecated. Change-Id: I0577d2ae42a7593e4bc886345c97072523aacc48 Reviewed-by: Simon Hausmann --- src/v4/qmljs_environment.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp index b8bf4caebb..f4b2f51fbe 100644 --- a/src/v4/qmljs_environment.cpp +++ b/src/v4/qmljs_environment.cpp @@ -586,7 +586,7 @@ void ExecutionContext::initCallContext(ExecutionContext *parent) argumentCount = qMax(argc, function->formalParameterCount); arguments = reinterpret_cast(this + 1) + function->varCount; if (argc) - std::copy(args, args + argc, arguments); + ::memcpy(arguments, args, argc * sizeof(Value)); if (argc < function->formalParameterCount) std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); -- cgit v1.2.3 From 25220223e6027fe27809646fb50af4636d2fd914 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 27 Feb 2013 12:22:34 +0100 Subject: Ignore by-produces of builds on Windows. Change-Id: I0626a2ee0f29866b8fa4b37958d1288f50a476f6 Reviewed-by: Simon Hausmann --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 2934c505f4..5a6d2ed756 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.o +*.obj Makefile Makefile.* src/tools/v4 @@ -9,3 +10,8 @@ udis86_itab.* *.ll RegExpJitTables.h .qmake.cache +include/QtV4 +mkspecs +src/v4/Qt5V4_resource.rc +src/v4/Qt5V4d_resource.rc +*.pdb -- cgit v1.2.3 From 896fbe080cdba32a049db5362af2dd933dece27b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 27 Feb 2013 12:15:39 +0100 Subject: Apparently MSVC needs EH enabled for SJLJ. Change-Id: Ieab8157816237151dcfcf3eccfb3de177c4ad221 Reviewed-by: Simon Hausmann --- src/v4/v4.pri | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/v4/v4.pri b/src/v4/v4.pri index c1eabe64d5..3b9711bdb6 100644 --- a/src/v4/v4.pri +++ b/src/v4/v4.pri @@ -1,6 +1,8 @@ include(llvm_installation.pri) include(../3rdparty/masm/masm-defs.pri) +CONFIG += exceptions + !llvm: DEFINES += QMLJS_NO_LLVM INCLUDEPATH += $$PWD -- cgit v1.2.3 From d0818647dadfb4a7c4f8385a11015f9e4c744974 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 27 Feb 2013 13:07:00 +0100 Subject: Add overloaded destructor to suppress warning on MSVC. main.cpp(357) : warning C4291: 'void *QQmlJS::VM::Managed::operator new(size_t,QQmlJS::VM::MemoryManager *)' : no matching operator delete found; memory will not be freed if initialization throws an exception d:\dev\v4vm\src\v4\qv4managed.h(112) : see declaration of 'QQmlJS::VM::Managed::operator new' Change-Id: Idd7f54f257ae93fdf04ecbf3f938e3b2d981bf89 Reviewed-by: Simon Hausmann --- src/v4/qv4managed.cpp | 6 ++++++ src/v4/qv4managed.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/v4/qv4managed.cpp b/src/v4/qv4managed.cpp index f458565100..23b84469f2 100644 --- a/src/v4/qv4managed.cpp +++ b/src/v4/qv4managed.cpp @@ -72,6 +72,12 @@ void Managed::operator delete(void *ptr) m->~Managed(); } +void Managed::operator delete(void *ptr, MemoryManager *mm) +{ + Q_UNUSED(mm); + + operator delete(ptr); +} QString Managed::className() const { diff --git a/src/v4/qv4managed.h b/src/v4/qv4managed.h index 94aaaf34a2..1022b4441a 100644 --- a/src/v4/qv4managed.h +++ b/src/v4/qv4managed.h @@ -111,6 +111,7 @@ protected: public: void *operator new(size_t size, MemoryManager *mm); void operator delete(void *ptr); + void operator delete(void *ptr, MemoryManager *mm); inline void mark() { if (markBit) -- cgit v1.2.3 From e1072a54f4d4a23c1cdd970738455343e3d41c04 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 27 Feb 2013 13:07:21 +0100 Subject: Fix warning about unused parameter. Change-Id: Ic8f95211b29fd5b3f4ffa4bc931fc15f004b30b1 Reviewed-by: Simon Hausmann --- src/v4/qv4isel_masm_p.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index efe0a32e1f..99b8767a38 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -458,8 +458,8 @@ public: { return 8; } // Size of value static inline int sizeOfArgument(const Pointer&) { return sizeof(void*); } - static inline int sizeOfArgument(VM::String* string) - { return sizeof(string); } + static inline int sizeOfArgument(VM::String*) + { return sizeof(VM::String); } static inline int sizeOfArgument(const PointerToValue &) { return sizeof(void *); } static inline int sizeOfArgument(const Reference &) -- cgit v1.2.3 From 5e0c24fd17f4ed112fe7dab0f17083a08cb160f8 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 21 Feb 2013 14:30:35 +0100 Subject: Add class to help the platform unwind JIT code. Currently only x86_64 and x86 is supported. Change-Id: I80fe60543b71e7073a0666d5ebb10144a75a488c Reviewed-by: Lars Knoll --- src/v4/qmljs_engine.cpp | 5 ++ src/v4/qmljs_engine.h | 2 + src/v4/qv4isel_llvm_p.h | 1 + src/v4/qv4isel_masm.cpp | 5 ++ src/v4/qv4unwindhelper.cpp | 37 ++++++++++ src/v4/qv4unwindhelper.h | 36 ++++++++++ src/v4/qv4unwindhelper_p-dw2.h | 152 +++++++++++++++++++++++++++++++++++++++++ src/v4/v4.pro | 7 +- 8 files changed, 243 insertions(+), 2 deletions(-) create mode 100644 src/v4/qv4unwindhelper.cpp create mode 100644 src/v4/qv4unwindhelper.h create mode 100644 src/v4/qv4unwindhelper_p-dw2.h diff --git a/src/v4/qmljs_engine.cpp b/src/v4/qmljs_engine.cpp index 9e10efdeab..f4d523d181 100644 --- a/src/v4/qmljs_engine.cpp +++ b/src/v4/qmljs_engine.cpp @@ -57,6 +57,7 @@ #include #include #include +#include namespace QQmlJS { namespace VM { @@ -70,6 +71,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) , exception(Value::nullValue()) { MemoryManager::GCBlocker gcBlocker(memoryManager); + unwindHelper = UnwindHelper::create(); memoryManager->setExecutionEngine(this); @@ -233,6 +235,9 @@ ExecutionEngine::~ExecutionEngine() delete globalObject.asObject(); rootContext->destroy(); delete rootContext; + if (unwindHelper) + unwindHelper->deregisterFunctions(functions); + delete unwindHelper; qDeleteAll(functions); delete memoryManager; } diff --git a/src/v4/qmljs_engine.h b/src/v4/qmljs_engine.h index 70a7fd9fc3..6d9682ae34 100644 --- a/src/v4/qmljs_engine.h +++ b/src/v4/qmljs_engine.h @@ -76,6 +76,7 @@ struct ArgumentsObject; struct ExecutionContext; struct ExecutionEngine; class MemoryManager; +class UnwindHelper; struct ObjectPrototype; struct StringPrototype; @@ -101,6 +102,7 @@ class RegExp; struct Q_V4_EXPORT ExecutionEngine { MemoryManager *memoryManager; + UnwindHelper *unwindHelper; EvalISelFactory *iselFactory; ExecutionContext *current; ExecutionContext *rootContext; diff --git a/src/v4/qv4isel_llvm_p.h b/src/v4/qv4isel_llvm_p.h index 9b4aa365e0..08d19666aa 100644 --- a/src/v4/qv4isel_llvm_p.h +++ b/src/v4/qv4isel_llvm_p.h @@ -94,6 +94,7 @@ public: // methods from InstructionSelection: virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); virtual void callBuiltinPushWithScope(IR::Temp *arg); + virtual void callBuiltinPushCatchScope(const QString &exceptionVarName){} virtual void callBuiltinPopScope(); virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 0e29a0f570..83c718e2cd 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -44,6 +44,7 @@ #include "qv4object.h" #include "qv4functionobject.h" #include "qv4regexpobject.h" +#include "qv4unwindhelper.h" #include #include @@ -483,6 +484,7 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) _as->poke(Assembler::ScratchRegister); #endif _as->ret(); + // TODO: add a label and a nop, so we can determine the exact function length _as->link(_vmFunction); @@ -491,6 +493,9 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) memcpy(_vmFunction->lookups, _lookups.constData(), _lookups.size()*sizeof(Lookup)); } + if (engine()->unwindHelper) + engine()->unwindHelper->registerFunction(_vmFunction); + qSwap(_vmFunction, vmFunction); qSwap(_function, function); qSwap(_lookups, lookups); diff --git a/src/v4/qv4unwindhelper.cpp b/src/v4/qv4unwindhelper.cpp new file mode 100644 index 0000000000..4d78474671 --- /dev/null +++ b/src/v4/qv4unwindhelper.cpp @@ -0,0 +1,37 @@ +#include + +#include + +#if CPU(X86_64) && OS(LINUX) +# define USE_DW2_HELPER +#elif CPU(X86) && OS(LINUX) +# define USE_DW2_HELPER +#elif OS(WINDOWS) + // SJLJ will unwind on Windows +# define USE_NULL_HELPER +#elif OS(IOS) + // SJLJ will unwind on iOS +# define USE_NULL_HELPER +#elif OS(ANDROID) +# warning "TODO!" +# define USE_NULL_HELPER +#else +# warning "Unsupported/untested platform!" +# define USE_NULL_HELPER +#endif + +#ifdef USE_DW2_HELPER +# include +#endif // USE_DW2_HELPER + +#ifdef USE_NULL_HELPER +using namespace QQmlJS::VM; +UnwindHelper *UnwindHelper::create() { return 0; } +UnwindHelper::UnwindHelper() {} +UnwindHelper::~UnwindHelper() {} +void UnwindHelper::registerFunction(Function *function); +void UnwindHelper::registerFunctions(QVector functions); +void UnwindHelper::deregisterFunction(Function *function); +void UnwindHelper::deregisterFunctions(QVector functions); +#endif // USE_NULL_HELPER + diff --git a/src/v4/qv4unwindhelper.h b/src/v4/qv4unwindhelper.h new file mode 100644 index 0000000000..bf70a01d3c --- /dev/null +++ b/src/v4/qv4unwindhelper.h @@ -0,0 +1,36 @@ +#ifndef QV4UNWINDHELPER_H +#define QV4UNWINDHELPER_H + +#include + +namespace QQmlJS { +namespace VM { + +struct Function; + +class UnwindHelper +{ + Q_DISABLE_COPY(UnwindHelper) + +private: + UnwindHelper(); + +public: + ~UnwindHelper(); + + static UnwindHelper *create(); + + void registerFunction(Function *function); + void registerFunctions(QVector functions); + void deregisterFunction(Function *function); + void deregisterFunctions(QVector functions); + +private: + struct Private; + Private *p; +}; + +} // VM namespace +} // QQmlJS namespace + +#endif // QV4UNWINDHELPER_H diff --git a/src/v4/qv4unwindhelper_p-dw2.h b/src/v4/qv4unwindhelper_p-dw2.h new file mode 100644 index 0000000000..302e173c08 --- /dev/null +++ b/src/v4/qv4unwindhelper_p-dw2.h @@ -0,0 +1,152 @@ +#ifndef QV4UNWINDHELPER_PDW2_H +#define QV4UNWINDHELPER_PDW2_H + +#include +#include +#include + +#include + +extern "C" void __register_frame(void*); +extern "C" void __deregister_frame(void*); + +namespace QQmlJS { +namespace VM { + +namespace { +#if CPU(X86_64) +// Generated by fdegen +static const unsigned char cie_fde_data[] = { + 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x78, 0x10, 0xc, 0x7, 0x8, + 0x90, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, + 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x41, 0x13, 0x7e, 0x86, + 0x2, 0x43, 0xd, 0x6, 0x8c, 0x3, 0x8e, 0x4, + 0x0, 0x0, 0x0, 0x0 +}; +static const int fde_offset = 20; +static const int initial_location_offset = 28; +static const int address_range_offset = 36; +#elif CPU(X86) && OS(LINUX) +static const unsigned char cie_fde_data[] = { + 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x7c, 0x8, 0xc, 0x4, 0x4, + 0x88, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, + 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x41, 0x13, 0x7e, 0x85, + 0x2, 0x43, 0xd, 0x5, 0x86, 0x3, 0x87, 0x4, + 0x0, 0x0, 0x0, 0x0, +}; +static const int fde_offset = 20; +static const int initial_location_offset = 28; +static const int address_range_offset = 32; +#endif +} // anonymous namespace + +namespace { +struct EHInfo { + unsigned char *cie_and_fde; + unsigned char *fde; + + EHInfo(unsigned char *cie_and_fde, unsigned char *fde) + : cie_and_fde(cie_and_fde), fde(fde) + { + Q_ASSERT(cie_and_fde); + Q_ASSERT(fde); + + __register_frame(fde); + } + + ~EHInfo() + { + Q_ASSERT(cie_and_fde); + Q_ASSERT(fde); + + __deregister_frame(fde); + + delete[] cie_and_fde; + } +}; +} // anonymous namespace + +struct Function; + +struct UnwindHelper::Private +{ + QHash ehInfoForFunction; +}; + +UnwindHelper *UnwindHelper::create() +{ + return new UnwindHelper; +} + +UnwindHelper::UnwindHelper() + : p(new Private) +{} + +UnwindHelper::~UnwindHelper() +{ + foreach (Function *f, p->ehInfoForFunction.keys()) + deregisterFunction(f); + delete p; +} + +void UnwindHelper::registerFunctions(QVector functions) +{ + foreach (Function *f, functions) registerFunction(f); +} + +void UnwindHelper::deregisterFunction(Function *function) +{ + QHash::iterator i = p->ehInfoForFunction.find(function); + if (i == p->ehInfoForFunction.end()) + return; + + delete i.value(); + p->ehInfoForFunction.erase(i); +} + +void UnwindHelper::deregisterFunctions(QVector functions) +{ + foreach (Function *f, functions) deregisterFunction(f); +} + +namespace { +void writeIntPtrValue(unsigned char *addr, intptr_t val) +{ + addr[0] = (val >> 0) & 0xff; + addr[1] = (val >> 8) & 0xff; + addr[2] = (val >> 16) & 0xff; + addr[3] = (val >> 24) & 0xff; +#if QT_POINTER_SIZE == 8 + addr[4] = (val >> 32) & 0xff; + addr[5] = (val >> 40) & 0xff; + addr[6] = (val >> 48) & 0xff; + addr[7] = (val >> 56) & 0xff; +#endif +} +} // anonymous namespace + +void UnwindHelper::registerFunction(Function *function) +{ + unsigned char *cie_and_fde = new unsigned char[sizeof(cie_fde_data)]; + memcpy(cie_and_fde, cie_fde_data, sizeof(cie_fde_data)); + + intptr_t ptr = static_cast(function->codeRef.code().executableAddress()) - static_cast(0); + writeIntPtrValue(cie_and_fde + initial_location_offset, ptr); + + size_t len = function->codeRef.size(); + writeIntPtrValue(cie_and_fde + address_range_offset, len); + + p->ehInfoForFunction.insert(function, new EHInfo(cie_and_fde, + cie_and_fde + fde_offset)); +} + +} // VM namespace +} // QQmlJS namespace + +#endif // QV4UNWINDHELPER_PDW2_H diff --git a/src/v4/v4.pro b/src/v4/v4.pro index 2197ae343e..a96d0635f7 100644 --- a/src/v4/v4.pro +++ b/src/v4/v4.pro @@ -47,7 +47,8 @@ SOURCES += \ qv4stringobject.cpp \ qv4string.cpp \ qv4objectiterator.cpp \ - qv4regexp.cpp + qv4regexp.cpp \ + qv4unwindhelper.cpp HEADERS += \ qv4global.h \ @@ -85,7 +86,9 @@ HEADERS += \ qv4string.h \ qv4propertydescriptor.h \ qv4objectiterator.h \ - qv4regexp.h + qv4regexp.h \ + qv4unwindhelper.h \ + qv4unwindhelper_p-dw2.h llvm-libs { -- cgit v1.2.3 From 6fecad5697f37d7a9c2f281851175a4cce5bcdd1 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 27 Feb 2013 13:50:17 +0100 Subject: Do not generate udis86 files when udis86 is disabled. Change-Id: Iaed45c949d29ac81a60adec3437f2790d23a58d5 Reviewed-by: Simon Hausmann --- src/3rdparty/masm/masm.pri | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/3rdparty/masm/masm.pri b/src/3rdparty/masm/masm.pri index 87f08e482f..683a5b19d3 100644 --- a/src/3rdparty/masm/masm.pri +++ b/src/3rdparty/masm/masm.pri @@ -40,6 +40,17 @@ contains(DEFINES, WTF_USE_UDIS86=1) { SOURCES += $$PWD/disassembler/udis86/udis86_syn-att.c SOURCES += $$PWD/disassembler/udis86/udis86_syn.c SOURCES += $$PWD/disassembler/udis86/udis86_syn-intel.c + + ITAB = $$PWD/disassembler/udis86/optable.xml + udis86.output = udis86_itab.h + udis86.input = ITAB + udis86.CONFIG += no_link + udis86.commands = python $$PWD/disassembler/udis86/itab.py ${QMAKE_FILE_IN} + QMAKE_EXTRA_COMPILERS += udis86 + + udis86_tab_cfile.target = $$OUT_PWD/udis86_itab.c + udis86_tab_cfile.depends = udis86_itab.h + QMAKE_EXTRA_TARGETS += udis86_tab_cfile } SOURCES += \ @@ -57,17 +68,6 @@ retgen.CONFIG += no_link retgen.commands = python $$retgen.script > ${QMAKE_FILE_OUT} QMAKE_EXTRA_COMPILERS += retgen -ITAB = $$PWD/disassembler/udis86/optable.xml -udis86.output = udis86_itab.h -udis86.input = ITAB -udis86.CONFIG += no_link -udis86.commands = python $$PWD/disassembler/udis86/itab.py ${QMAKE_FILE_IN} -QMAKE_EXTRA_COMPILERS += udis86 - -udis86_tab_cfile.target = $$OUT_PWD/udis86_itab.c -udis86_tab_cfile.depends = udis86_itab.h -QMAKE_EXTRA_TARGETS += udis86_tab_cfile - # Taken from WebKit/Tools/qmake/mkspecs/features/unix/default_post.prf linux-g++* { greaterThan(QT_GCC_MAJOR_VERSION, 3):greaterThan(QT_GCC_MINOR_VERSION, 5) { -- cgit v1.2.3 From 5250ebdff82c8f4291de618fcaf9281bdd078620 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 27 Feb 2013 15:27:20 +0100 Subject: Fix null-unwind helper compilation. Change-Id: I82af0ae373157d412b138334c8d80fa6e17591df Reviewed-by: Simon Hausmann --- src/v4/qv4unwindhelper.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/v4/qv4unwindhelper.cpp b/src/v4/qv4unwindhelper.cpp index 4d78474671..32d5a8a695 100644 --- a/src/v4/qv4unwindhelper.cpp +++ b/src/v4/qv4unwindhelper.cpp @@ -29,9 +29,9 @@ using namespace QQmlJS::VM; UnwindHelper *UnwindHelper::create() { return 0; } UnwindHelper::UnwindHelper() {} UnwindHelper::~UnwindHelper() {} -void UnwindHelper::registerFunction(Function *function); -void UnwindHelper::registerFunctions(QVector functions); -void UnwindHelper::deregisterFunction(Function *function); -void UnwindHelper::deregisterFunctions(QVector functions); +void UnwindHelper::registerFunction(Function *function) {Q_UNUSED(function);} +void UnwindHelper::registerFunctions(QVector functions) {Q_UNUSED(functions);} +void UnwindHelper::deregisterFunction(Function *function) {Q_UNUSED(function);} +void UnwindHelper::deregisterFunctions(QVector functions) {Q_UNUSED(functions);} #endif // USE_NULL_HELPER -- cgit v1.2.3 From 3fcaf5eaae9c7c44b09aacf77240320ce618ecad Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 27 Feb 2013 15:27:41 +0100 Subject: Enable DW2 unwind helper on MacOS. Change-Id: I1c1671d0b3953ce2e8f57bbf68023731470e3da1 Reviewed-by: Simon Hausmann --- src/v4/qv4unwindhelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qv4unwindhelper.cpp b/src/v4/qv4unwindhelper.cpp index 32d5a8a695..31d9d6871d 100644 --- a/src/v4/qv4unwindhelper.cpp +++ b/src/v4/qv4unwindhelper.cpp @@ -2,7 +2,7 @@ #include -#if CPU(X86_64) && OS(LINUX) +#if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X)) # define USE_DW2_HELPER #elif CPU(X86) && OS(LINUX) # define USE_DW2_HELPER -- cgit v1.2.3 From 077a462c12db01ca92526a3fb9f4b846d9161de9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 22 Feb 2013 17:15:58 +0100 Subject: Tool to generate FDE and CIE tables using libdwarf This makes it easier to generate the right magic bits and bytes across different architectures. Change-Id: I83cf8f348f4ea92febfe463e1ffd627808e1bb44 Reviewed-by: Erik Verbruggen --- .gitignore | 1 + tools/fdegen/fdegen.pro | 8 ++ tools/fdegen/main.cpp | 372 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 381 insertions(+) create mode 100644 tools/fdegen/fdegen.pro create mode 100644 tools/fdegen/main.cpp diff --git a/.gitignore b/.gitignore index 5a6d2ed756..a592d28fab 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ mkspecs src/v4/Qt5V4_resource.rc src/v4/Qt5V4d_resource.rc *.pdb +tools/fdegen/fdegen diff --git a/tools/fdegen/fdegen.pro b/tools/fdegen/fdegen.pro new file mode 100644 index 0000000000..a52533280e --- /dev/null +++ b/tools/fdegen/fdegen.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = fdegen +INCLUDEPATH += . + +# Input +SOURCES += main.cpp + +LIBS += -ldwarf -lelf diff --git a/tools/fdegen/main.cpp b/tools/fdegen/main.cpp new file mode 100644 index 0000000000..313be40351 --- /dev/null +++ b/tools/fdegen/main.cpp @@ -0,0 +1,372 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG + +#ifdef DEBUG +#include +#endif + +#include + +enum DwarfRegs { +#if defined(Q_PROCESSOR_X86_64) + // X86-64 + RAX = 0, + RDX = 1, + RCX = 2, + RBX = 3, + RSI = 4, + RDI = 5, + RBP = 6, + RSP = 7, + R8 = 8, + R9 = 9, + R10 = 10, + R11 = 11, + R12 = 12, + R13 = 13, + R14 = 14, + R15 = 15, + RIP = 16, + + InstructionPointerRegister = RIP, + StackPointerRegister = RSP, + StackFrameRegister = RBP +#elif defined(Q_PROCESSOR_X86) + // x86 + EAX = 0, + EDX = 1, + ECX = 2, + EBX = 3, + ESI = 4, + EDI = 5, + EBP = 6, + ESP = 7, + EIP = 8, + + InstructionPointerRegister = EIP, + StackPointerRegister = ESP, + StackFrameRegister = EBP +#else +#error Not ported yet +#endif +}; + +static const DwarfRegs calleeSavedRegisters[] = { +#if defined(Q_PROCESSOR_X86_64) + R12, + R14 +#endif +}; +static const int calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]); + +#if QT_POINTER_SIZE == 8 +#define Elf_Ehdr Elf64_Ehdr +#define elf_newehdr elf64_newehdr +#define Elf_Shdr Elf64_Shdr +#define elf_getshdr elf64_getshdr +#else +#define Elf_Ehdr Elf32_Ehdr +#define elf_newehdr elf32_newehdr +#define Elf_Shdr Elf32_Shdr +#define elf_getshdr elf32_getshdr +#endif + +static void die(const char *msg) +{ + fprintf(stderr, "error: %s\n", msg); + exit(1); +} + +static int createSectionCallback( + char *name, + int size, + Dwarf_Unsigned /*type*/, + Dwarf_Unsigned /*flags*/, + Dwarf_Unsigned /*link*/, + Dwarf_Unsigned /*info*/, + Dwarf_Unsigned* /*sect_name_index*/, + void * /*user_data*/, + int* /*error*/) +{ + if (strcmp(name, ".debug_frame")) + return 0; + fprintf(stderr, "createsection called with %s and size %d\n", name, size); + return 1; +} + +static unsigned char cie_init_instructions[] = { + DW_CFA_def_cfa, StackPointerRegister, /*offset in bytes */QT_POINTER_SIZE, + DW_CFA_offset | InstructionPointerRegister, 1, +}; + +int main() +{ + Dwarf_Error error = 0; + Dwarf_P_Debug dw = dwarf_producer_init_c(DW_DLC_WRITE | DW_DLC_SIZE_64, + createSectionCallback, + /* error handler */0, + /* error arg */0, + /* user data */0, + &error); + if (error != 0) + die("dwarf_producer_init_c failed"); + + Dwarf_Unsigned cie = dwarf_add_frame_cie(dw, + "", + /* code alignment factor */QT_POINTER_SIZE, + /* data alignment factor */-QT_POINTER_SIZE, + /* return address reg*/InstructionPointerRegister, + cie_init_instructions, + sizeof(cie_init_instructions), + &error); + if (error != 0) + die("dwarf_add_frame_cie failed"); + + Dwarf_P_Fde fde = dwarf_new_fde(dw, &error); + if (error != 0) + die("dwarf_new_fde failed"); + + /* New entry in state machine for code offset 1 after push rbp instruction */ + dwarf_add_fde_inst(fde, + DW_CFA_advance_loc, + /*offset in code alignment units*/ 1, + /* unused*/ 0, + &error); + + /* After "push rbp" the offset to the CFA is now 2 instead of 1 */ + dwarf_add_fde_inst(fde, + DW_CFA_def_cfa_offset_sf, + /*offset in code alignment units*/ -2, + /* unused*/ 0, + &error); + + /* After "push rbp" the value of rbp is now stored at offset 1 from CFA */ + dwarf_add_fde_inst(fde, + DW_CFA_offset, + StackFrameRegister, + 2, + &error); + + /* New entry in state machine for code offset 3 for mov rbp, rsp instruction */ + dwarf_add_fde_inst(fde, + DW_CFA_advance_loc, + /*offset in code alignment units*/ 3, + /* unused */ 0, + &error); + + /* After "mov rbp, rsp" the cfa is reachable via rbp */ + dwarf_add_fde_inst(fde, + DW_CFA_def_cfa_register, + StackFrameRegister, + /* unused */0, + &error); + + /* Callee saved registers */ + for (int i = 0; i < calleeSavedRegisterCount; ++i) { + dwarf_add_fde_inst(fde, + DW_CFA_offset, + calleeSavedRegisters[i], + i + 3, + &error); + } + + dwarf_add_frame_fde(dw, fde, + /* die */0, + cie, + /*virt addr */0, + /* length of code */0, + /* symbol index */0, + &error); + if (error != 0) + die("dwarf_add_frame_fde failed"); + + dwarf_transform_to_disk_form(dw, &error); + if (error != 0) + die("dwarf_transform_to_disk_form failed"); + + Dwarf_Unsigned len = 0; + Dwarf_Signed elfIdx = 0; + unsigned char *bytes = (unsigned char *)dwarf_get_section_bytes(dw, /* section */1, + &elfIdx, &len, &error); + if (error != 0) + die("dwarf_get_section_bytes failed"); + + // libdwarf doesn't add a terminating FDE with zero length, so let's add one + // ourselves. + unsigned char *newBytes = (unsigned char *)alloca(len + 4); + memcpy(newBytes, bytes, len); + newBytes[len] = 0; + newBytes[len + 1] = 0; + newBytes[len + 2] = 0; + newBytes[len + 3] = 0; + newBytes[len + 4] = 0; + bytes = newBytes; + len += 4; + + // Reset CIE-ID back to 0 as expected for .eh_frames + bytes[4] = 0; + bytes[5] = 0; + bytes[6] = 0; + bytes[7] = 0; + + unsigned fde_offset = bytes[0] + 4; + + bytes[fde_offset + 4] = fde_offset + 4; + + printf("static const unsigned char cie_fde_data[] = {\n"); + int i = 0; + while (i < len) { + printf(" "); + for (int j = 0; i < len && j < 8; ++j, ++i) + printf("0x%x, ", bytes[i]); + printf("\n"); + } + printf("};\n"); + + printf("static const int fde_offset = %d;\n", fde_offset); + printf("static const int initial_location_offset = %d;\n", fde_offset + 8); + printf("static const int address_range_offset = %d;\n", fde_offset + 8 + QT_POINTER_SIZE); + +#ifdef DEBUG + { + if (elf_version(EV_CURRENT) == EV_NONE) + die("wrong elf version"); + int fd = open("debug.o", O_WRONLY | O_CREAT, 0777); + if (fd < 0) + die("cannot create debug.o"); + + Elf *e = elf_begin(fd, ELF_C_WRITE, 0); + if (!e) + die("elf_begin failed"); + + Elf_Ehdr *ehdr = elf_newehdr(e); + if (!ehdr) + die(elf_errmsg(-1)); + + ehdr->e_ident[EI_DATA] = ELFDATA2LSB; +#if defined(Q_PROCESSOR_X86_64) + ehdr->e_machine = EM_X86_64; +#elif defined(Q_PROCESSOR_X86) + ehdr->e_machine = EM_386; +#else +#error port me :) +#endif + ehdr->e_type = ET_EXEC; + ehdr->e_version = EV_CURRENT; + + Elf_Scn *section = elf_newscn(e); + if (!section) + die("elf_newscn failed"); + + Elf_Data *data = elf_newdata(section); + if (!data) + die(elf_errmsg(-1)); + data->d_align = 4; + data->d_off = 0; + data->d_buf = bytes; + data->d_size = len; + data->d_type = ELF_T_BYTE; + data->d_version = EV_CURRENT; + + Elf_Shdr *shdr = elf_getshdr(section); + if (!shdr) + die(elf_errmsg(-1)); + + shdr->sh_name = 1; + shdr->sh_type = SHT_PROGBITS; + shdr->sh_entsize = 0; + + char stringTable[] = { + 0, + '.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', 0, + '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0 + }; + + section = elf_newscn(e); + if (!section) + die("elf_newscn failed"); + + data = elf_newdata(section); + if (!data) + die(elf_errmsg(-1)); + data->d_align = 1; + data->d_off = 0; + data->d_buf = stringTable; + data->d_size = sizeof(stringTable); + data->d_type = ELF_T_BYTE; + data->d_version = EV_CURRENT; + + shdr = elf_getshdr(section); + if (!shdr) + die(elf_errmsg(-1)); + + shdr->sh_name = 11; + shdr->sh_type = SHT_STRTAB; + shdr->sh_flags = SHF_STRINGS | SHF_ALLOC; + shdr->sh_entsize = 0; + + ehdr->e_shstrndx = elf_ndxscn(section); + + if (elf_update(e, ELF_C_WRITE) < 0) + die(elf_errmsg(-1)); + + elf_end(e); + close(fd); + } +#endif + + dwarf_producer_finish(dw, &error); + if (error != 0) + die("dwarf_producer_finish failed"); + return 0; +} -- cgit v1.2.3 From 1a9fa76018669a8cf012cb161686af03a204df11 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 27 Feb 2013 20:14:00 +0100 Subject: [masm] Fix build on ARM Allocate a LocalsRegister here, too Change-Id: I1f05f52948616e4979beb8935f6b4e46791a6937 Reviewed-by: Lars Knoll --- src/v4/qv4isel_masm_p.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 99b8767a38..34b12c66be 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -128,6 +128,7 @@ public: static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4; static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; + static const RegisterID LocalsRegister = JSC::ARMRegisters::r7; static const RegisterID ContextRegister = JSC::ARMRegisters::r5; static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; static const RegisterID ScratchRegister = JSC::ARMRegisters::r6; -- cgit v1.2.3 From 9e3e227368ab621e94a14e143a5064213d3fc614 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 1 Mar 2013 13:34:52 +0100 Subject: Don't spend all the time in gc in some cases Some test cases actually caused the GC to get triggered too often. This could happen if a GC run would collect up very few freed objects. In that case the next GC run would get triggered after these few objects where used up. The commit adds a count to make sure we don't trigger GC too early. Change-Id: Ia51056e33869b072e801c0be02807a5d40ef97c9 Reviewed-by: Simon Hausmann --- src/v4/qv4mm.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/v4/qv4mm.cpp b/src/v4/qv4mm.cpp index 67a594df12..2ef583cb98 100644 --- a/src/v4/qv4mm.cpp +++ b/src/v4/qv4mm.cpp @@ -59,6 +59,8 @@ struct MemoryManager::Data enum { MaxItemSize = 256 }; Managed *smallItems[MaxItemSize/16]; uint nChunks[MaxItemSize/16]; + uint availableItems[MaxItemSize/16]; + uint allocCount[MaxItemSize/16]; struct Chunk { PageAllocation memory; int chunkSize; @@ -79,6 +81,8 @@ struct MemoryManager::Data { memset(smallItems, 0, sizeof(smallItems)); memset(nChunks, 0, sizeof(nChunks)); + memset(availableItems, 0, sizeof(availableItems)); + memset(allocCount, 0, sizeof(allocCount)); scribble = !qgetenv("MM_SCRIBBLE").isEmpty(); aggressiveGC = !qgetenv("MM_AGGRESSIVE_GC").isEmpty(); } @@ -122,6 +126,7 @@ Managed *MemoryManager::alloc(std::size_t size) assert(size % 16 == 0); size_t pos = size >> 4; + ++m_d->allocCount[pos]; // fits into a small bucket assert(size < MemoryManager::Data::MaxItemSize); @@ -131,7 +136,7 @@ Managed *MemoryManager::alloc(std::size_t size) goto found; // try to free up space, otherwise allocate - if (!m_d->aggressiveGC) { + if (m_d->allocCount[pos] > (m_d->availableItems[pos] >> 1) && !m_d->aggressiveGC) { runGC(); m = m_d->smallItems[pos]; if (m) @@ -164,6 +169,7 @@ Managed *MemoryManager::alloc(std::size_t size) } *last = 0; m = m_d->smallItems[pos]; + m_d->availableItems[pos] += allocation.memory.size()/size - 1; } found: @@ -284,6 +290,7 @@ void MemoryManager::runGC() // std::cerr << "GC: sweep freed " << freedCount // << " objects in " << t.elapsed() // << "ms" << std::endl; + memset(m_d->allocCount, 0, sizeof(m_d->allocCount)); } void MemoryManager::setEnableGC(bool enableGC) -- cgit v1.2.3 From 72c1fe5822aa65f4a3f70f78e058fb7e3154a4b6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 1 Mar 2013 14:03:04 +0100 Subject: Save some overhead in builtin_define_property Change-Id: I632b8a5e46bc8119789acde6362cfd8e86ce901b Reviewed-by: Simon Hausmann --- src/v4/qmljs_runtime.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 8b8bdbb22f..60ef909633 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -1237,13 +1237,13 @@ void __qmljs_builtin_define_property(ExecutionContext *ctx, const Value &object, Object *o = object.asObject(); assert(o); - PropertyDescriptor pd; - pd.value = val ? *val : Value::undefinedValue(); - pd.type = PropertyDescriptor::Data; - pd.writable = PropertyDescriptor::Enabled; - pd.enumberable = PropertyDescriptor::Enabled; - pd.configurable = PropertyDescriptor::Enabled; - o->__defineOwnProperty__(ctx, name, &pd); + + PropertyDescriptor *pd = o->insertMember(name); + pd->value = val ? *val : Value::undefinedValue(); + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Enabled; + pd->configurable = PropertyDescriptor::Enabled; } void __qmljs_builtin_define_array_property(ExecutionContext *ctx, const Value &object, int index, Value *val) -- cgit v1.2.3 From 36376e72516a2ed0f2808ba767650be5d41c4403 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 1 Mar 2013 15:09:17 +0100 Subject: Implemented DaylightSavingTA for Windows. Change-Id: I5421c325a307e11837fd8135a6306b519fe29ad4 Reviewed-by: Simon Hausmann --- src/v4/qv4dateobject.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/v4/qv4dateobject.cpp b/src/v4/qv4dateobject.cpp index 6f2f014e1a..c44fab7fb3 100644 --- a/src/v4/qv4dateobject.cpp +++ b/src/v4/qv4dateobject.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -63,15 +64,14 @@ #include -#ifndef Q_OS_WIN -# include +#ifdef Q_OS_WIN +# include +#else # ifndef Q_OS_VXWORKS # include # else # include "qplatformdefs.h" # endif -#else -# include #endif using namespace QQmlJS::VM; @@ -296,17 +296,16 @@ static inline double MakeDate(double day, double time) static inline double DaylightSavingTA(double t) { -#ifndef Q_OS_WIN - long int tt = (long int)(t / msPerSecond); struct tm tmtm; - if (!localtime_r((const time_t*)&tt, &tmtm)) - return 0; - return (tmtm.tm_isdst > 0) ? msPerHour : 0; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + __time64_t tt = (__time64_t)(t / msPerSecond); + if (!_localtime64_s(&tmtm, &tt)) #else - Q_UNUSED(t); - /// ### implement me - return 0; + long int tt = (long int)(t / msPerSecond); + if (!localtime_r((const time_t*) &tt, &tmtm)) #endif + return 0; + return (tmtm.tm_isdst > 0) ? msPerHour : 0; } static inline double LocalTime(double t) -- cgit v1.2.3 From d09ad4b103ace8c6a4c26280726b1819b1f797a6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 1 Mar 2013 23:16:03 +0100 Subject: Fix crashes on ia32 sizeof(VM::String) != sizeof(VM::String*) :) Change-Id: I1e59de64ad5f73e478519c618f28806c151d94f5 Reviewed-by: Lars Knoll --- src/v4/qv4isel_masm_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 34b12c66be..c11152afa4 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -460,7 +460,7 @@ public: static inline int sizeOfArgument(const Pointer&) { return sizeof(void*); } static inline int sizeOfArgument(VM::String*) - { return sizeof(VM::String); } + { return sizeof(VM::String*); } static inline int sizeOfArgument(const PointerToValue &) { return sizeof(void *); } static inline int sizeOfArgument(const Reference &) -- cgit v1.2.3 From 35a700d8bee368e2a190a12fd1b0d79357119aea Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 1 Mar 2013 15:02:46 +0100 Subject: Minor optimisation: Avoid some function calls Change-Id: Ie30b95af01c6623262fbbd93f51c115262531fe8 Reviewed-by: Simon Hausmann --- src/v4/qv4object.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 3e675d36c8..33a31604d7 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -910,8 +910,10 @@ void Object::arrayReserve(uint n) } arrayAlloc = qMax(n, 2*arrayAlloc); PropertyDescriptor *newArrayData = new PropertyDescriptor[arrayAlloc]; - memcpy(newArrayData, arrayData, sizeof(PropertyDescriptor)*arrayDataLen); - delete [] (arrayData - off); + if (arrayData) { + memcpy(newArrayData, arrayData, sizeof(PropertyDescriptor)*arrayDataLen); + delete [] (arrayData - off); + } arrayData = newArrayData; if (sparseArray) { for (uint i = arrayFreeList; i < arrayAlloc; ++i) { -- cgit v1.2.3 From e58a2572beb3a97e3dbc1a873fe4eef825db4a55 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 3 Mar 2013 00:54:48 +0100 Subject: Skip some really slow tests The test cases themself finish in ~10-20 secs. Unfortunately __deregister_frame seems to be extremely slow if you have many functions (as is the case in these tests). It takes many minutes to then clean up the engine and deregister all frames. Change-Id: Idaa829f64c91d324e650b1d22b94dec6becad3d4 Reviewed-by: Simon Hausmann --- tests/TestExpectations | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/TestExpectations b/tests/TestExpectations index 694b22a03b..07ad6ecdeb 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -1,3 +1,9 @@ +# Skip these, they are currently too slow +S7.8.5_A2.4_T2 +S7.8.5_A1.1_T2 +S7.8.5_A1.4_T2 +S7.8.5_A2.1_T2 + # wrong tests # uses octal number 15.2.3.6-2-17-1 failing @@ -43,4 +49,4 @@ S15.2.4.4_A14 failing # Function declaration inside if / while Sbp_12.5_A9_T3 failing -Sbp_12.6.2_A13_T3 failing \ No newline at end of file +Sbp_12.6.2_A13_T3 failing -- cgit v1.2.3 From afa65627ac34bc61106a5995fe36a16e409d687e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 1 Mar 2013 16:47:07 +0100 Subject: More efficient initialization of array literals Initialize array literals in one go instead of going through repeated calls to the runtime for each value. This gives quite a nice speed improvement on the splay tree benchmark. Change-Id: Id5757dd7a910be13db4958da7e449172ec1b04ae Reviewed-by: Simon Hausmann --- src/v4/moth/qv4instr_moth_p.h | 12 +++--- src/v4/moth/qv4isel_moth.cpp | 11 +++--- src/v4/moth/qv4isel_moth_p.h | 6 +-- src/v4/moth/qv4vme_moth.cpp | 8 ++-- src/v4/qmljs_runtime.cpp | 41 +++++++++++++++------ src/v4/qmljs_runtime.h | 2 +- src/v4/qv4codegen.cpp | 86 ++++++++++++++++++++++++++++--------------- src/v4/qv4ir.cpp | 4 +- src/v4/qv4ir_p.h | 3 +- src/v4/qv4isel_llvm.cpp | 2 +- src/v4/qv4isel_llvm_p.h | 2 +- src/v4/qv4isel_masm.cpp | 14 ++++--- src/v4/qv4isel_masm_p.h | 2 +- src/v4/qv4isel_p.cpp | 18 ++------- src/v4/qv4isel_p.h | 2 +- src/v4/qv4isel_util_p.h | 10 +++++ 16 files changed, 135 insertions(+), 88 deletions(-) diff --git a/src/v4/moth/qv4instr_moth_p.h b/src/v4/moth/qv4instr_moth_p.h index d77f701fed..07766874ff 100644 --- a/src/v4/moth/qv4instr_moth_p.h +++ b/src/v4/moth/qv4instr_moth_p.h @@ -47,7 +47,7 @@ F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ F(CallBuiltinDefineGetterSetter, callBuiltinDefineGetterSetter) \ F(CallBuiltinDefineProperty, callBuiltinDefineProperty) \ - F(CallBuiltinDefineArrayProperty, callBuiltinDefineArrayProperty) \ + F(CallBuiltinDefineArray, callBuiltinDefineArray) \ F(CreateValue, createValue) \ F(CreateProperty, createProperty) \ F(CreateActivationProperty, createActivationProperty) \ @@ -377,11 +377,11 @@ union Instr Param object; Param value; }; - struct instr_callBuiltinDefineArrayProperty { + struct instr_callBuiltinDefineArray { MOTH_INSTR_HEADER - Param object; - Param value; - int index; + quint32 argc; + quint32 args; + Param result; }; struct instr_createValue { MOTH_INSTR_HEADER @@ -495,7 +495,7 @@ union Instr instr_callBuiltinDeclareVar callBuiltinDeclareVar; instr_callBuiltinDefineGetterSetter callBuiltinDefineGetterSetter; instr_callBuiltinDefineProperty callBuiltinDefineProperty; - instr_callBuiltinDefineArrayProperty callBuiltinDefineArrayProperty; + instr_callBuiltinDefineArray callBuiltinDefineArray; instr_createValue createValue; instr_createProperty createProperty; instr_createActivationProperty createActivationProperty; diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index 43c5ed53e9..125014a1bc 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -597,7 +597,7 @@ void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Temp *source, IR: void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 &args) { bool singleArgIsTemp = false; - if (e && e->next == 0) { + if (e && e->next == 0 && e->expr->asTemp()) { // ok, only 1 argument in the call... const int idx = e->expr->asTemp()->index; // We can only pass a reference into the stack, which holds temps that @@ -912,12 +912,11 @@ void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QSt addInstruction(call); } -void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) +void InstructionSelection::callBuiltinDefineArray(IR::Temp *result, IR::ExprList *args) { - Instruction::CallBuiltinDefineArrayProperty call; - call.object = getParam(object); - call.index = index; - call.value = getParam(value); + Instruction::CallBuiltinDefineArray call; + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); addInstruction(call); } diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h index 6009d29164..af60ca4fac 100644 --- a/src/v4/moth/qv4isel_moth_p.h +++ b/src/v4/moth/qv4isel_moth_p.h @@ -54,7 +54,7 @@ protected: virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); - virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); + virtual void callBuiltinDefineArray(IR::Temp *result, IR::ExprList *args); virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result); @@ -90,9 +90,9 @@ private: Instr::Param getParam(IR::Expr *e) { - Q_ASSERT(e); - typedef Instr::Param Param; + assert(e); + if (IR::Const *c = e->asConst()) { return Param::createValue(convertToValue(c)); } else if (IR::Temp *t = e->asTemp()) { diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 5447e1514b..ffade9602e 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -383,9 +383,11 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co __qmljs_builtin_define_property(context, VALUE(instr.object), instr.name, VALUEPTR(instr.value)); MOTH_END_INSTR(CallBuiltinDefineProperty) - MOTH_BEGIN_INSTR(CallBuiltinDefineArrayProperty) - __qmljs_builtin_define_array_property(context, VALUE(instr.object), instr.index, VALUEPTR(instr.value)); - MOTH_END_INSTR(CallBuiltinDefineArrayProperty) + MOTH_BEGIN_INSTR(CallBuiltinDefineArray) + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + __qmljs_builtin_define_array(context, VALUEPTR(instr.result), args, instr.argc); + MOTH_END_INSTR(CallBuiltinDefineArray) MOTH_BEGIN_INSTR(CreateValue) Q_ASSERT(instr.args + instr.argc < stackSize); diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 60ef909633..b511cf3afe 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -1246,18 +1246,35 @@ void __qmljs_builtin_define_property(ExecutionContext *ctx, const Value &object, pd->configurable = PropertyDescriptor::Enabled; } -void __qmljs_builtin_define_array_property(ExecutionContext *ctx, const Value &object, int index, Value *val) -{ - Object *o = object.asObject(); - assert(o); - - PropertyDescriptor pd; - pd.value = val ? *val : Value::undefinedValue(); - pd.type = PropertyDescriptor::Data; - pd.writable = PropertyDescriptor::Enabled; - pd.enumberable = PropertyDescriptor::Enabled; - pd.configurable = PropertyDescriptor::Enabled; - o->__defineOwnProperty__(ctx, index, &pd); +void __qmljs_builtin_define_array(ExecutionContext *ctx, Value *array, Value *values, uint length) +{ + ArrayObject *a = ctx->engine->newArrayObject(ctx); + + // ### FIXME: We need to allocate the array data to avoid crashes other places + // This should rather be done when required + a->arrayReserve(length); + if (length) { + PropertyDescriptor *pd = a->arrayData; + for (uint i = 0; i < length; ++i) { + if (values[i].isUndefined() && values[i].uint_32 == UINT_MAX) { + pd->value = Value::undefinedValue(); + pd->type = PropertyDescriptor::Generic; + pd->writable = PropertyDescriptor::Undefined; + pd->enumberable = PropertyDescriptor::Undefined; + pd->configurable = PropertyDescriptor::Undefined; + } else { + pd->value = values[i]; + pd->type = PropertyDescriptor::Data; + pd->writable = PropertyDescriptor::Enabled; + pd->enumberable = PropertyDescriptor::Enabled; + pd->configurable = PropertyDescriptor::Enabled; + } + ++pd; + } + a->arrayDataLen = length; + a->setArrayLengthUnchecked(length); + } + *array = Value::fromObject(a); } void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &object, String *name, const Value *getter, const Value *setter) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index e982bdb34e..e649debe3e 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -124,7 +124,7 @@ ExecutionContext *__qmljs_builtin_push_catch_scope(String *exceptionVarName, Exe ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx); void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name); void __qmljs_builtin_define_property(ExecutionContext *ctx, const Value &object, String *name, Value *val); -void __qmljs_builtin_define_array_property(ExecutionContext *ctx, const Value &object, int index, Value *val); +void __qmljs_builtin_define_array(ExecutionContext *ctx, Value *array, QQmlJS::VM::Value *values, uint length); void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &object, String *name, const Value *getter, const Value *setter); // constructors diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index 8430a29ebe..7ad09ca3ec 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -308,6 +308,22 @@ protected: return true; } + virtual bool visit(ArrayLiteral *ast) + { + int index = 0; + for (ElementList *it = ast->elements; it; it = it->next) { + for (Elision *elision = it->elision; elision; elision = elision->next) + ++index; + ++index; + } + if (ast->elision) { + for (Elision *elision = ast->elision->next; elision; elision = elision->next) + ++index; + } + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, index); + return true; + } + virtual bool visit(VariableDeclaration *ast) { if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) @@ -1043,39 +1059,51 @@ bool Codegen::visit(Expression *ast) bool Codegen::visit(ArrayLiteral *ast) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Array"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); - int index = 0; - unsigned value = 0; + IR::ExprList *args = 0; + IR::ExprList *current = 0; for (ElementList *it = ast->elements; it; it = it->next) { - for (Elision *elision = it->elision; elision; elision = elision->next) - ++index; + for (Elision *elision = it->elision; elision; elision = elision->next) { + IR::ExprList *arg = _function->New(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + current->expr = _block->CONST(IR::MissingType, 0); + } Result expr = expression(it->expression); - IR::ExprList *args = _function->New(); - IR::ExprList *current = args; - current->expr = _block->TEMP(t); - current->next = _function->New(); - current = current->next; - current->expr = _block->CONST(IR::NumberType, index); - current->next = _function->New(); - current = current->next; - - if (!value) - value = _block->newTemp(); - move(_block->TEMP(value), *expr); - // __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) - current->expr = _block->TEMP(value); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_define_array_property, 0, 0), args)); - - ++index; - } - if (ast->elision) { - for (Elision *elision = ast->elision->next; elision; elision = elision->next) - ++index; - // ### the new string leaks - move(member(_block->TEMP(t), _function->newString(QStringLiteral("length"))), _block->CONST(IR::NumberType, index + 1)); + IR::ExprList *arg = _function->New(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + + IR::Expr *exp = *expr; + if (exp->asTemp() || exp->asConst()) { + current->expr = exp; + } else { + unsigned value = _block->newTemp(); + move(_block->TEMP(value), exp); + current->expr = _block->TEMP(value); + } + } + for (Elision *elision = ast->elision; elision; elision = elision->next) { + IR::ExprList *arg = _function->New(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + current->expr = _block->CONST(IR::MissingType, 0); } + + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_array, 0, 0), args)); _expr.code = _block->TEMP(t); return false; } diff --git a/src/v4/qv4ir.cpp b/src/v4/qv4ir.cpp index 0d81328ed8..a35cf01dfd 100644 --- a/src/v4/qv4ir.cpp +++ b/src/v4/qv4ir.cpp @@ -250,8 +250,8 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_declare_vars"; case IR::Name::builtin_define_property: return "builtin_define_property"; - case IR::Name::builtin_define_array_property: - return "builtin_define_array_property"; + case IR::Name::builtin_define_array: + return "builtin_define_array"; case IR::Name::builtin_define_getter_setter: return "builtin_define_getter_setter"; } diff --git a/src/v4/qv4ir_p.h b/src/v4/qv4ir_p.h index 8bab68b47e..581633d8b6 100644 --- a/src/v4/qv4ir_p.h +++ b/src/v4/qv4ir_p.h @@ -153,6 +153,7 @@ AluOp binaryOperator(int op); const char *opname(IR::AluOp op); enum Type { + MissingType, // Used to indicate holes in array initialization (e.g. [,,]) UndefinedType, NullType, BoolType, @@ -288,7 +289,7 @@ struct Name: Expr { builtin_pop_scope, builtin_declare_vars, builtin_define_property, - builtin_define_array_property, + builtin_define_array, builtin_define_getter_setter }; diff --git a/src/v4/qv4isel_llvm.cpp b/src/v4/qv4isel_llvm.cpp index a10328b005..5717532055 100644 --- a/src/v4/qv4isel_llvm.cpp +++ b/src/v4/qv4isel_llvm.cpp @@ -498,7 +498,7 @@ void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QSt Q_UNREACHABLE(); } -void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) +void InstructionSelection::callBuiltinDefineArray(IR::Temp *result, IR::ExprList *args) { // TODO assert(!"TODO!"); diff --git a/src/v4/qv4isel_llvm_p.h b/src/v4/qv4isel_llvm_p.h index 08d19666aa..43e3dfc060 100644 --- a/src/v4/qv4isel_llvm_p.h +++ b/src/v4/qv4isel_llvm_p.h @@ -99,7 +99,7 @@ public: // methods from InstructionSelection: virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); - virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); + virtual void callBuiltinDefineArray(IR::Temp *result, IR::ExprList *args); virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 83c718e2cd..660319286c 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -674,10 +674,12 @@ void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QSt Assembler::Reference(object), identifier(name), Assembler::PointerToValue(value)); } -void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) +void InstructionSelection::callBuiltinDefineArray(IR::Temp *result, IR::ExprList *args) { - generateFunctionCall(Assembler::Void, __qmljs_builtin_define_array_property, Assembler::ContextRegister, - Assembler::Reference(object), Assembler::TrustedImm32(index), Assembler::PointerToValue(value)); + int length = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_array, Assembler::ContextRegister, + Assembler::PointerToValue(result), + baseAddressForCallArguments(), Assembler::TrustedImm32(length)); } void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) @@ -1046,9 +1048,9 @@ int InstructionSelection::prepareVariableArguments(IR::ExprList* args) int i = 0; for (IR::ExprList *it = args; it; it = it->next, ++i) { - IR::Temp *arg = it->expr->asTemp(); - assert(arg != 0); - _as->copyValue(argumentAddressForCall(i), arg); +// IR::Temp *arg = it->expr->asTemp(); +// assert(arg != 0); + _as->copyValue(argumentAddressForCall(i), it->expr); } return argc; diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index c11152afa4..4952f7a709 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -763,7 +763,7 @@ protected: virtual void callBuiltinDeclareVar(bool deletable, const QString &name); virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); - virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); + virtual void callBuiltinDefineArray(IR::Temp *result, IR::ExprList *args); virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result); virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index ca57009934..b4360153ce 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -398,21 +398,9 @@ void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result) callBuiltinDefineProperty(object, *name->id, value); } return; - case IR::Name::builtin_define_array_property: { - if (!call->args) - return; - IR::ExprList *args = call->args; - IR::Temp *object = args->expr->asTemp(); - assert(object); - args = args->next; - assert(args); - IR::Const *index = args->expr->asConst(); - args = args->next; - assert(args); - IR::Temp *value = args->expr->asTemp(); - - callBuiltinDefineArrayProperty(object, int(index->value), value); - } return; + case IR::Name::builtin_define_array: + callBuiltinDefineArray(result, call->args); + return; default: break; diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h index cf0c325612..9c15829d8d 100644 --- a/src/v4/qv4isel_p.h +++ b/src/v4/qv4isel_p.h @@ -114,7 +114,7 @@ public: // to implement by subclasses: virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0; virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) = 0; virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) = 0; - virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) = 0; + virtual void callBuiltinDefineArray(IR::Temp *result, IR::ExprList *args) = 0; virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) = 0; virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) = 0; virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) = 0; diff --git a/src/v4/qv4isel_util_p.h b/src/v4/qv4isel_util_p.h index b88f41093c..9fc30cfc34 100644 --- a/src/v4/qv4isel_util_p.h +++ b/src/v4/qv4isel_util_p.h @@ -45,9 +45,19 @@ inline bool isNegative(double d) } +inline VM::Value nonExistantValue() +{ + VM::Value v; + v.tag = VM::Value::Undefined_Type; + v.uint_32 = UINT_MAX; + return v; +} + inline VM::Value convertToValue(IR::Const *c) { switch (c->type) { + case IR::MissingType: + return nonExistantValue(); case IR::NullType: return VM::Value::nullValue(); case IR::UndefinedType: -- cgit v1.2.3 From 6261e92f9da9bcc8426e0bb502ae3ac2ef2ae03c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 3 Mar 2013 10:20:05 +0100 Subject: Fix crashes on ia32 with array literals prepareVariableArguments uses copyValue to copy/initialize the arguments for a variable arguments call. Previously it called copyValue with only temps as source, which on ia32 is implemented as (1) load temp from memory into FP reg (2) store FP reg into destination memory location After Gerrit change Id5757dd7a910be13db4958da7e449172ec1b04ae the source of copyValue can now also be an IR::Expr and hence a constant value. On ia32 it is unfortunately not possible to load a constant floating point value into an FP reg right away. However in this very situation it's sufficient to store the correct VM::Value in the destination memory address right away. This is now done using a template specialization of copyValue. The old code compiled because we accidentally picked the loadDouble(const void *address) overload of MASM, with address pointing to the IR::Expr :) Change-Id: Ie43f9be432cfcb844cc5e706e63bd3e91e857bac Reviewed-by: Lars Knoll --- src/v4/qv4isel_masm.cpp | 22 ++++++++++++++++++++++ src/v4/qv4isel_masm_p.h | 2 ++ 2 files changed, 24 insertions(+) diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 660319286c..82b0634a4a 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -172,6 +172,28 @@ void Assembler::copyValue(Result result, Source source) #endif } +template +void Assembler::copyValue(Result result, IR::Expr* source) +{ +#ifdef VALUE_FITS_IN_REGISTER + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgument(source, ReturnValueRegister); + storeArgument(ReturnValueRegister, result); +#else + if (IR::Temp *temp = source->asTemp()) { + loadDouble(temp, FPGpr0); + storeDouble(FPGpr0, result); + } else if (IR::Const *c = source->asConst()) { + VM::Value v = convertToValue(c); + storeValue(v, result); + } else { + assert(! "not implemented"); + } +#endif +} + + void Assembler::storeValue(VM::Value value, IR::Temp* destination) { Address addr = loadTempAddress(ScratchRegister, destination); diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 4952f7a709..4a12351b70 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -432,6 +432,8 @@ public: template void copyValue(Result result, Source source); + template + void copyValue(Result result, IR::Expr* source); void storeValue(VM::Value value, Address destination) { -- cgit v1.2.3 From e5f6264ad2cac6dfa3ef8d92e2c85b07319dcc78 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 3 Mar 2013 00:50:33 +0100 Subject: Fix builtin_define_property Commit 72c1fe5822aa65f4a3f70f78e058fb7e3154a4b6 broke object literals that uses numbers as keys (e.g. { "2": "bla" }. This fixes it while keeping the faster code path. Change-Id: I0e89eb6e03da6a2e55d833ac0ad956f35e597297 Reviewed-by: Simon Hausmann --- src/v4/qmljs_runtime.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index b511cf3afe..76361c0c9a 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -1237,8 +1237,8 @@ void __qmljs_builtin_define_property(ExecutionContext *ctx, const Value &object, Object *o = object.asObject(); assert(o); - - PropertyDescriptor *pd = o->insertMember(name); + uint idx = name->asArrayIndex(); + PropertyDescriptor *pd = (idx != UINT_MAX) ? o->arrayInsert(idx) : o->insertMember(name); pd->value = val ? *val : Value::undefinedValue(); pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Enabled; @@ -1282,14 +1282,14 @@ void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &ob Object *o = object.asObject(); assert(o); - PropertyDescriptor pd; - pd.get = getter ? getter->asFunctionObject() : 0; - pd.set = setter ? setter->asFunctionObject() : 0; - pd.type = PropertyDescriptor::Accessor; - pd.writable = PropertyDescriptor::Undefined; - pd.enumberable = PropertyDescriptor::Enabled; - pd.configurable = PropertyDescriptor::Enabled; - o->__defineOwnProperty__(ctx, name, &pd); + uint idx = name->asArrayIndex(); + PropertyDescriptor *pd = (idx != UINT_MAX) ? o->arrayInsert(idx) : o->insertMember(name); + pd->get = getter ? getter->asFunctionObject() : 0; + pd->set = setter ? setter->asFunctionObject() : 0; + pd->type = PropertyDescriptor::Accessor; + pd->writable = PropertyDescriptor::Undefined; + pd->enumberable = PropertyDescriptor::Enabled; + pd->configurable = PropertyDescriptor::Enabled; } void __qmljs_increment(ExecutionContext *ctx, Value *result, const Value &value) -- cgit v1.2.3 From ee967a10f258ea2acae2c2ea64f2923e91c8a2c5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 1 Mar 2013 17:04:21 +0100 Subject: Implement JavaScript exceptions using C++ exceptions Instead of registering catch handlers with setjmp and throwing JS exceptions with longjmp, they are now thrown and caught as C++ exceptions. This allows for tight interoperability between C++ and JS in the future and allows for clear semantics with regards to cleaning up memory in the engine when throwing exceptions. (destructors are guaranteed to be called, unlike with setjmp/longjmp). The recent unwind table additions allow for the exceptions to be thrown through JIT generated code. Catching the exception is done by re-using the existing IR semantics where the beginning of a try block is marked by registering an exception handler. Execution after the registration continues conditionally, based on the return value of builtin_create_exception_handler. A return value of is 0 the try block(s) are executed. If an exception is thrown during that time, execution resumes at the point where builtin_create_exception_handler returns, but with a return value of 1. If an exception is thrown within the catch handler, the execution resumes again at the same point, but the inCatch IR variable will guide execution straight to the finally block(s), which calls delete_exception_handler. In the JIT as well as the interpreter this is implemented by entering a C++ code section that contains a C++ try {} catch {} block, in which the calling function is called again and continues right at the next instruction (or the interpreter loop is recursively entered). An exception will throw us out of that scope and back into the try {} catch {} wrapper, which can call again into the calling function. The IR guarantees that delete_exception_handler is always called, regardless of how the try or catch blocks are terminated. That is where in the JIT and interpreter we return from the nested function call and return back into the original stack frame, effectively unregistering the catch handler. Further cleanups with regards to the naming and the exception handler stack will come in subsequent patches, this is merely the minimal patch set to change to the new mechanism. This patch set breaks ARM until ARM exception handler tables are implemented. The interpreter changes are based on a patchset from Erik from https://codereview.qt-project.org/#change,45750 Change-Id: I543f2bd37b2186f7e48ffcab177d57b5ce932a0c Reviewed-by: Lars Knoll --- src/v4/moth/qv4isel_moth.cpp | 2 +- src/v4/moth/qv4isel_moth_p.h | 2 +- src/v4/moth/qv4vme_moth.cpp | 47 +++++++++++++++++--------------- src/v4/moth/qv4vme_moth_p.h | 11 ++++---- src/v4/qmljs_runtime.cpp | 2 +- src/v4/qmljs_runtime.h | 3 ++ src/v4/qv4codegen.cpp | 5 +--- src/v4/qv4isel_llvm.cpp | 2 +- src/v4/qv4isel_llvm_p.h | 2 +- src/v4/qv4isel_masm.cpp | 65 ++++++++++++++++++++++++++++++++++++++++---- src/v4/qv4isel_masm_p.h | 9 +++++- src/v4/qv4isel_p.cpp | 3 +- src/v4/qv4isel_p.h | 2 +- tools/v4/main.cpp | 40 +++++++++++++-------------- 14 files changed, 129 insertions(+), 66 deletions(-) diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index 125014a1bc..d8d7040134 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -829,7 +829,7 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) addInstruction(call); } -void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *) +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) { Instruction::CallBuiltinCreateExceptionHandler call; call.result = getResultParam(result); diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h index af60ca4fac..6eaee6dd02 100644 --- a/src/v4/moth/qv4isel_moth_p.h +++ b/src/v4/moth/qv4isel_moth_p.h @@ -43,7 +43,7 @@ protected: virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); virtual void callBuiltinDeleteExceptionHandler(); virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index ffade9602e..aea4fce8c5 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -139,7 +139,8 @@ static inline VM::Value *getValueRef(QQmlJS::VM::ExecutionContext *context, # define VALUEPTR(param) getValueRef(context, stack, param, stackSize) #endif -VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code +VM::Value VME::run(QQmlJS::VM::ExecutionContext *context, const uchar *&code, + VM::Value *stack, unsigned stackSize #ifdef MOTH_THREADED_INTERPRETER , void ***storeJumpTable #endif @@ -161,8 +162,6 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co } #endif - VM::Value *stack = 0; - unsigned stackSize = 0; FunctionState state(context, &code); #ifdef MOTH_THREADED_INTERPRETER @@ -260,31 +259,35 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co MOTH_END_INSTR(CallBuiltinThrow) MOTH_BEGIN_INSTR(CallBuiltinCreateExceptionHandler) - void *buf = __qmljs_create_exception_handler(context); - // The result is the only value we need from the instr to - // continue execution when an exception is caught. + __qmljs_create_exception_handler(context); VM::Value *result = getValueRef(context, stack, instr.result #if !defined(QT_NO_DEBUG) , stackSize #endif ); - VM::ExecutionContext *oldContext = context; - int didThrow = setjmp(* static_cast(buf)); - context = oldContext; - // Two ways to come here: after a create, or after a throw. - if (didThrow) - // At this point, the interpreter state can be anything but - // valid, so first restore the state. - restoreState(context, result, code); - else - // Save the state and any variables we need when catching an - // exception, so we can restore the state at that point. - saveState(context, result, code); - *result = VM::Value::fromInt32(didThrow); + try { + *result = VM::Value::fromInt32(0); + const uchar *tryCode = code; + run(context, tryCode, stack, stackSize); + code = tryCode; + } catch (const VM::Exception &) { + try { + *result = VM::Value::fromInt32(1); + const uchar *catchCode = code; + run(context, catchCode, stack, stackSize); + code = catchCode; + } catch (const VM::Exception &) { + *result = VM::Value::fromInt32(1); + const uchar *catchCode = code; + run(context, catchCode, stack, stackSize); + code = catchCode; + } + } MOTH_END_INSTR(CallBuiltinCreateExceptionHandler) MOTH_BEGIN_INSTR(CallBuiltinDeleteExceptionHandler) __qmljs_delete_exception_handler(context); + return VM::Value(); MOTH_END_INSTR(CallBuiltinDeleteExceptionHandler) MOTH_BEGIN_INSTR(CallBuiltinGetException) @@ -473,8 +476,8 @@ void **VME::instructionJumpTable() { static void **jumpTable = 0; if (!jumpTable) { - VME dummy; - dummy(0, 0, &jumpTable); + const uchar *code = 0; + VME().run(0, code, 0, 0, &jumpTable); } return jumpTable; } @@ -483,7 +486,7 @@ void **VME::instructionJumpTable() VM::Value VME::exec(VM::ExecutionContext *ctxt, const uchar *code) { VME vme; - return vme(ctxt, code); + return vme.run(ctxt, code); } void VME::restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code) diff --git a/src/v4/moth/qv4vme_moth_p.h b/src/v4/moth/qv4vme_moth_p.h index 2fd877f7b9..0b7fc9b7af 100644 --- a/src/v4/moth/qv4vme_moth_p.h +++ b/src/v4/moth/qv4vme_moth_p.h @@ -16,17 +16,18 @@ class VME public: static VM::Value exec(VM::ExecutionContext *, const uchar *); - VM::Value operator()(QQmlJS::VM::ExecutionContext *, const uchar *code #ifdef MOTH_THREADED_INTERPRETER - , void ***storeJumpTable = 0 + static void **instructionJumpTable(); #endif - ); +private: + VM::Value run(QQmlJS::VM::ExecutionContext *, const uchar *&code, + VM::Value *stack = 0, unsigned stackSize = 0 #ifdef MOTH_THREADED_INTERPRETER - static void **instructionJumpTable(); + , void ***storeJumpTable = 0 #endif + ); -private: static void restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code); static void saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code); }; diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 76361c0c9a..43940e68fb 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -964,7 +964,7 @@ void __qmljs_throw(ExecutionContext *context, const Value &value) context->engine->exception = value; - longjmp(handler.stackFrame, 1); + throw Exception(); } Q_V4_EXPORT void * __qmljs_create_exception_handler(ExecutionContext *context) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index e649debe3e..4da9015ab6 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -89,6 +89,9 @@ struct ArrayObject; struct ErrorObject; struct ExecutionEngine; +struct Exception { +}; + extern "C" { // context diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index 7ad09ca3ec..ad390dfb8c 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -2454,10 +2454,7 @@ bool Codegen::visit(TryStatement *ast) } int hasException = _block->newTemp(); - int contextTemp = _block->newTemp(); - IR::ExprList *createExceptionArgs = _function->New(); - createExceptionArgs->init(_block->TEMP(contextTemp)); - move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_create_exception_handler, 0, 0), createExceptionArgs)); + move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_create_exception_handler, 0, 0), 0)); // Pass the hidden "inCatch" and "hasException" TEMPs to the // builtin_delete_exception_handler, in order to have those TEMPs alive for diff --git a/src/v4/qv4isel_llvm.cpp b/src/v4/qv4isel_llvm.cpp index 5717532055..e6010cfd2a 100644 --- a/src/v4/qv4isel_llvm.cpp +++ b/src/v4/qv4isel_llvm.cpp @@ -427,7 +427,7 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) Q_UNREACHABLE(); } -void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp) +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) { // TODO assert(!"TODO!"); diff --git a/src/v4/qv4isel_llvm_p.h b/src/v4/qv4isel_llvm_p.h index 43e3dfc060..fb28456b64 100644 --- a/src/v4/qv4isel_llvm_p.h +++ b/src/v4/qv4isel_llvm_p.h @@ -88,7 +88,7 @@ public: // methods from InstructionSelection: virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); virtual void callBuiltinDeleteExceptionHandler(); virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 82b0634a4a..1659276009 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -123,6 +123,14 @@ void Assembler::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) _patches[targetBlock].append(targetJump); } +void Assembler::addPatch(DataLabelPtr patch, Label target) +{ + DataLabelPatch p; + p.dataLabel = patch; + p.target = target; + _dataLabelPatches.append(p); +} + Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, IR::Temp *t) { int32_t offset = 0; @@ -420,6 +428,9 @@ void Assembler::link(VM::Function *vmFunc) functions[ctl.externalFunction.value()] = ctl.functionName; } + foreach (const DataLabelPatch &p, _dataLabelPatches) + linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); + static bool showCode = !qgetenv("SHOW_CODE").isNull(); if (showCode) { #if OS(LINUX) @@ -630,22 +641,64 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg) generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, Assembler::ContextRegister, Assembler::Reference(arg)); } -void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp) +static void *tryWrapper(ExecutionContext *context, void *localsPtr, void *(*exceptionEntryPointInCallingFunction)(ExecutionContext*, void*, int)) +{ + void *addressToContinueAt = 0; + try { + addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 0); + } catch (const Exception&) { + try { + addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 1); + } catch (const Exception&) { + addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 1); + } + } + return addressToContinueAt; +} + +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) { - Address contextAddr = _as->loadTempAddress(Assembler::ScratchRegister, contextTemp); - _as->storePtr(Assembler::ContextRegister, contextAddr); - generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_create_exception_handler, Assembler::ContextRegister); - generateFunctionCall(Assembler::ReturnValueRegister, setjmp, Assembler::ReturnValueRegister); - _as->loadPtr(contextAddr, Assembler::ContextRegister); + generateFunctionCall(Assembler::Void, __qmljs_create_exception_handler, Assembler::ContextRegister); + + // Call tryWrapper, which is going to re-enter the same function again below. + // When tryWrapper returns, it returns the with address of where to continue. + Assembler::DataLabelPtr movePatch = _as->moveWithPatch(Assembler::TrustedImmPtr(0), Assembler::ScratchRegister); + generateFunctionCall(Assembler::ReturnValueRegister, tryWrapper, Assembler::ContextRegister, Assembler::LocalsRegister, Assembler::ScratchRegister); + _as->jump(Assembler::ReturnValueRegister); + + // tryWrapper calls us at this place with arg3 == 0 if we're supposed to execute the try block + // and arg == 1 if we caught an exception. The generated IR takes care of returning from this + // call when deleteExceptionHandler is called. + _as->addPatch(movePatch, _as->label()); + _as->enterStandardStackFrame(/*locals*/0); +#ifdef ARGUMENTS_IN_REGISTERS + _as->move(Assembler::registerForArgument(0), Assembler::ContextRegister); + _as->move(Assembler::registerForArgument(1), Assembler::LocalsRegister); +#else + _as->loadPtr(addressForArgument(0), Assembler::ContextRegister); + _as->loadPtr(addressForArgument(1), Assembler::LocalsRegister); +#endif + Address addr = _as->loadTempAddress(Assembler::ScratchRegister, result); +#ifdef ARGUMENTS_IN_REGISTERS + _as->store32(Assembler::registerForArgument(2), addr); +#else + _as->load32(addressForArgument(2), Assembler::ReturnValueRegister); _as->store32(Assembler::ReturnValueRegister, addr); +#endif addr.offset += 4; _as->store32(Assembler::TrustedImm32(Value::Boolean_Type), addr); } void InstructionSelection::callBuiltinDeleteExceptionHandler() { + // This assumes that we're in code that was called by tryWrapper, so we return to try wrapper + // with the address that we'd like to continue at, which is right after the ret below. generateFunctionCall(Assembler::Void, __qmljs_delete_exception_handler, Assembler::ContextRegister); + Assembler::DataLabelPtr continuation = _as->moveWithPatch(Assembler::TrustedImmPtr(0), Assembler::ReturnValueRegister); + _as->leaveStandardStackFrame(/*locals*/0); + _as->ret(); + _as->addPatch(continuation, _as->label()); } void InstructionSelection::callBuiltinGetException(IR::Temp *result) diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 4a12351b70..d850246ad4 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -216,6 +216,7 @@ public: void registerBlock(IR::BasicBlock*); void jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target); void addPatch(IR::BasicBlock* targetBlock, Jump targetJump); + void addPatch(DataLabelPtr patch, Label target); Pointer loadTempAddress(RegisterID reg, IR::Temp *t); @@ -723,6 +724,12 @@ private: QHash _addrs; QHash > _patches; QList _callsToLink; + + struct DataLabelPatch { + DataLabelPtr dataLabel; + Label target; + }; + QList _dataLabelPatches; }; class Q_V4_EXPORT InstructionSelection: @@ -754,7 +761,7 @@ protected: virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); virtual void callBuiltinDeleteExceptionHandler(); virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index b4360153ce..03fa074584 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -311,8 +311,7 @@ void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result) } return; case IR::Name::builtin_create_exception_handler: { - IR::Temp *arg = call->args->expr->asTemp(); - callBuiltinCreateExceptionHandler(result, arg); + callBuiltinCreateExceptionHandler(result); return; } diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h index 9c15829d8d..40cf86eddb 100644 --- a/src/v4/qv4isel_p.h +++ b/src/v4/qv4isel_p.h @@ -103,7 +103,7 @@ public: // to implement by subclasses: virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result) = 0; virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) = 0; virtual void callBuiltinThrow(IR::Temp *arg) = 0; - virtual void callBuiltinCreateExceptionHandler(IR::Temp *result, IR::Temp *contextTemp) = 0; + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result) = 0; virtual void callBuiltinDeleteExceptionHandler() = 0; virtual void callBuiltinGetException(IR::Temp *result) = 0; virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) = 0; diff --git a/tools/v4/main.cpp b/tools/v4/main.cpp index f6997eb79d..d73fa480da 100644 --- a/tools/v4/main.cpp +++ b/tools/v4/main.cpp @@ -369,30 +369,30 @@ int main(int argc, char *argv[]) const QString code = QString::fromUtf8(file.readAll()); file.close(); - void * buf = __qmljs_create_exception_handler(ctx); - if (setjmp(*(jmp_buf *)buf)) { + __qmljs_create_exception_handler(ctx); + try { + QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode, + /*strictMode =*/ false, /*inheritContext =*/ false); + if (!f) + continue; + vm.globalCode = f; + + ctx->strictMode = f->isStrict; + ctx->lookups = f->lookups; + if (debugger) + debugger->aboutToCall(0, ctx); + QQmlJS::VM::Value result = f->code(ctx, f->codeData); + if (debugger) + debugger->justLeft(ctx); + if (!result.isUndefined()) { + if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) + std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; + } + } catch (const QQmlJS::VM::Exception&) { showException(ctx); return EXIT_FAILURE; } - QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode, - /*strictMode =*/ false, /*inheritContext =*/ false); - if (!f) - continue; - vm.globalCode = f; - - ctx->strictMode = f->isStrict; - ctx->lookups = f->lookups; - if (debugger) - debugger->aboutToCall(0, ctx); - QQmlJS::VM::Value result = f->code(ctx, f->codeData); - if (debugger) - debugger->justLeft(ctx); - if (!result.isUndefined()) { - if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) - std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; - } - } else { std::cerr << "Error: cannot open file " << fn.toUtf8().constData() << std::endl; return EXIT_FAILURE; -- cgit v1.2.3 From 1200e15c9bd3c334c1ddff114c099bea3808ea76 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 4 Mar 2013 09:05:38 +0100 Subject: Fix warning about unused parameter. Change-Id: Ia94e1ac073dc16a0eb9841677343dbaddad923ae Reviewed-by: Lars Knoll --- src/v4/qv4isel_masm_p.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index d850246ad4..23f9da0c55 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -488,6 +488,7 @@ public: template int loadArgumentOnStackOrRegister(const VoidType &value) { + Q_UNUSED(value); return 0; } -- cgit v1.2.3 From 798b446f0d3035e88c01986af96bb0c698d6c912 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 1 Mar 2013 14:26:35 +0100 Subject: Remove shared subexpressions from the function's IR code. Change-Id: I4502eb9c86aba14142f1ae15f1ba560255a2cb6a Reviewed-by: Lars Knoll --- src/v4/qv4codegen.cpp | 1 + src/v4/qv4ir.cpp | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/v4/qv4ir_p.h | 46 +++++++++++ 3 files changed, 258 insertions(+) diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index ad390dfb8c..77e1a15af6 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -1799,6 +1799,7 @@ void Codegen::linearize(IR::Function *function) function->basicBlocks = trace; #ifndef QV4_NO_LIVENESS + function->removeSharedExpressions(); liveness(function); #endif diff --git a/src/v4/qv4ir.cpp b/src/v4/qv4ir.cpp index a35cf01dfd..2fa8f0a94a 100644 --- a/src/v4/qv4ir.cpp +++ b/src/v4/qv4ir.cpp @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -140,6 +141,127 @@ AluOp binaryOperator(int op) } } +struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor +{ + CloneExpr clone; + QSet subexpressions; // contains all the non-cloned subexpressions in the given function + Expr *uniqueExpr; + + RemoveSharedExpressions(): uniqueExpr(0) {} + + void operator()(IR::Function *function) + { + subexpressions.clear(); + + foreach (BasicBlock *block, function->basicBlocks) { + clone.setBasicBlock(block); + + foreach (Stmt *s, block->statements) { + s->accept(this); + } + } + } + + template + _Expr *cleanup(_Expr *expr) + { + if (subexpressions.contains(expr)) { + // the cloned expression is unique by definition + // so we don't need to add it to `subexpressions'. + return clone(expr); + } + + subexpressions.insert(expr); + IR::Expr *e = expr; + qSwap(uniqueExpr, e); + expr->accept(this); + qSwap(uniqueExpr, e); + return static_cast<_Expr *>(e); + } + + // statements + virtual void visitExp(Exp *s) + { + s->expr = cleanup(s->expr); + } + + virtual void visitEnter(Enter *s) + { + s->expr = cleanup(s->expr); + } + + virtual void visitLeave(Leave *) + { + // nothing to do for Leave statements + } + + virtual void visitMove(Move *s) + { + s->target = cleanup(s->target); + s->source = cleanup(s->source); + } + + virtual void visitJump(Jump *) + { + // nothing to do for Jump statements + } + + virtual void visitCJump(CJump *s) + { + s->cond = cleanup(s->cond); + } + + virtual void visitRet(Ret *s) + { + s->expr = cleanup(s->expr); + } + + + // expressions + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *) {} + virtual void visitClosure(Closure *) {} + + virtual void visitUnop(Unop *e) + { + e->expr = cleanup(e->expr); + } + + virtual void visitBinop(Binop *e) + { + e->left = cleanup(e->left); + e->right = cleanup(e->right); + } + + virtual void visitCall(Call *e) + { + e->base = cleanup(e->base); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr = cleanup(it->expr); + } + + virtual void visitNew(New *e) + { + e->base = cleanup(e->base); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr = cleanup(it->expr); + } + + virtual void visitSubscript(Subscript *e) + { + e->base = cleanup(e->base); + e->index = cleanup(e->index); + } + + virtual void visitMember(Member *e) + { + e->base = cleanup(e->base); + } +}; + void Const::dump(QTextStream &out) { switch (type) { @@ -460,6 +582,12 @@ void Function::dump(QTextStream &out, Stmt::Mode mode) out << '}' << endl; } +void Function::removeSharedExpressions() +{ + RemoveSharedExpressions removeSharedExpressions; + removeSharedExpressions(this); +} + unsigned BasicBlock::newTemp() { return function->tempCount++; @@ -668,6 +796,89 @@ void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) } } +CloneExpr::CloneExpr(BasicBlock *block) + : block(block), cloned(0) +{ +} + +void CloneExpr::setBasicBlock(BasicBlock *block) +{ + this->block = block; +} + +ExprList *CloneExpr::clone(ExprList *list) +{ + if (! list) + return 0; + + ExprList *clonedList = block->function->New(); + clonedList->init(clone(list->expr), clone(list->next)); + return clonedList; +} + +void CloneExpr::visitConst(Const *e) +{ + cloned = block->CONST(e->type, e->value); +} + +void CloneExpr::visitString(String *e) +{ + cloned = block->STRING(e->value); +} + +void CloneExpr::visitRegExp(RegExp *e) +{ + cloned = block->REGEXP(e->value, e->flags); +} + +void CloneExpr::visitName(Name *e) +{ + if (e->id) + cloned = block->NAME(*e->id, e->line, e->column); + else + cloned = block->NAME(e->builtin, e->line, e->column); +} + +void CloneExpr::visitTemp(Temp *e) +{ + cloned = block->TEMP(e->index, e->scope); +} + +void CloneExpr::visitClosure(Closure *e) +{ + cloned = block->CLOSURE(e->value); +} + +void CloneExpr::visitUnop(Unop *e) +{ + cloned = block->UNOP(e->op, clone(e->expr)); +} + +void CloneExpr::visitBinop(Binop *e) +{ + cloned = block->BINOP(e->op, clone(e->left), clone(e->right)); +} + +void CloneExpr::visitCall(Call *e) +{ + cloned = block->CALL(clone(e->base), clone(e->args)); +} + +void CloneExpr::visitNew(New *e) +{ + cloned = block->NEW(clone(e->base), clone(e->args)); +} + +void CloneExpr::visitSubscript(Subscript *e) +{ + cloned = block->SUBSCRIPT(clone(e->base), clone(e->index)); +} + +void CloneExpr::visitMember(Member *e) +{ + cloned = block->MEMBER(clone(e->base), e->name); +} + } // end of namespace IR } // end of namespace QQmlJS diff --git a/src/v4/qv4ir_p.h b/src/v4/qv4ir_p.h index 581633d8b6..9c2b63696f 100644 --- a/src/v4/qv4ir_p.h +++ b/src/v4/qv4ir_p.h @@ -655,6 +655,8 @@ struct Function { inline BasicBlock *insertBasicBlock(BasicBlock *block) { basicBlocks.append(block); return block; } void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); + + void removeSharedExpressions(); }; struct BasicBlock { @@ -721,6 +723,50 @@ struct BasicBlock { void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); }; +class CloneExpr: protected IR::ExprVisitor +{ +public: + explicit CloneExpr(IR::BasicBlock *block = 0); + + void setBasicBlock(IR::BasicBlock *block); + + template + _Expr *operator()(_Expr *expr) + { + return clone(expr); + } + + template + _Expr *clone(_Expr *expr) + { + Expr *c = expr; + qSwap(cloned, c); + expr->accept(this); + qSwap(cloned, c); + return static_cast<_Expr *>(c); + } + +protected: + IR::ExprList *clone(IR::ExprList *list); + + virtual void visitConst(Const *); + virtual void visitString(String *); + virtual void visitRegExp(RegExp *); + virtual void visitName(Name *); + virtual void visitTemp(Temp *); + virtual void visitClosure(Closure *); + virtual void visitUnop(Unop *); + virtual void visitBinop(Binop *); + virtual void visitCall(Call *); + virtual void visitNew(New *); + virtual void visitSubscript(Subscript *); + virtual void visitMember(Member *); + +private: + IR::BasicBlock *block; + IR::Expr *cloned; +}; + } // end of namespace IR } // end of namespace QQmlJS -- cgit v1.2.3 From c750d7cc2b1fde34d8cc7ae472c9cedf2d0f41b9 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 1 Mar 2013 14:28:46 +0100 Subject: Moved isNegative(double) into another header. So it can be used in codegen. Change-Id: Iba1294a58cf6785eb9c37b34c39c4ec0281cd694 Reviewed-by: Lars Knoll --- src/v4/qv4ir_p.h | 10 ++++++++++ src/v4/qv4isel_util_p.h | 10 ---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/v4/qv4ir_p.h b/src/v4/qv4ir_p.h index 9c2b63696f..352575935f 100644 --- a/src/v4/qv4ir_p.h +++ b/src/v4/qv4ir_p.h @@ -70,6 +70,16 @@ class QQmlType; namespace QQmlJS { +inline bool isNegative(double d) +{ + uchar *dch = (uchar *)&d; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return (dch[0] & 0x80); + else + return (dch[7] & 0x80); + +} + namespace VM { struct ExecutionContext; struct Value; diff --git a/src/v4/qv4isel_util_p.h b/src/v4/qv4isel_util_p.h index 9fc30cfc34..4a928ad11f 100644 --- a/src/v4/qv4isel_util_p.h +++ b/src/v4/qv4isel_util_p.h @@ -35,16 +35,6 @@ namespace QQmlJS { -inline bool isNegative(double d) -{ - uchar *dch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - return (dch[0] & 0x80); - else - return (dch[7] & 0x80); - -} - inline VM::Value nonExistantValue() { VM::Value v; -- cgit v1.2.3 From 76246ab72d303cc59094e746831eaca3bcc59bef Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 1 Mar 2013 14:34:56 +0100 Subject: Do not re-use temp for both parameter and result. Change-Id: Iffd50459bc55960ac5cef1e246cd3d2664565a8a Reviewed-by: Lars Knoll --- src/v4/qv4codegen.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index 77e1a15af6..16839fac21 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -2171,10 +2171,12 @@ bool Codegen::visit(ForEachStatement *ast) enterLoop(ast, foreachend, foreachin); - int iterator = _block->newTemp(); - move(_block->TEMP(iterator), *expression(ast->expression)); + int objectToIterateOn = _block->newTemp(); + move(_block->TEMP(objectToIterateOn), *expression(ast->expression)); IR::ExprList *args = _function->New(); - args->init(_block->TEMP(iterator)); + args->init(_block->TEMP(objectToIterateOn)); + + int iterator = _block->newTemp(); move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args)); _block->JUMP(foreachin); -- cgit v1.2.3 From 66fb7618f7817728f2c37d82d7f2b5bb2f180aab Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 4 Mar 2013 11:47:59 +0100 Subject: Small optimisation for string concatenation The real fix will require a string class that can hold several substrings. Change-Id: I0e23f248048781b0f623d01087397afaeefcc97a Reviewed-by: Simon Hausmann --- src/v4/qmljs_runtime.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 43940e68fb..4047a5c809 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -448,7 +448,15 @@ Bool __qmljs_string_equal(String *left, String *right) String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second) { - return ctx->engine->newString(first->toQString() + second->toQString()); + const QString &a = first->toQString(); + const QString &b = second->toQString(); + QString newStr(a.length() + b.length(), Qt::Uninitialized); + QChar *data = newStr.data(); + memcpy(data, a.constData(), a.length()*sizeof(QChar)); + data += a.length(); + memcpy(data, b.constData(), b.length()*sizeof(QChar)); + + return ctx->engine->newString(newStr); } Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int typeHint) -- cgit v1.2.3 From 5aeccbe297ed7939f016568d104e589b3e4ff260 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 4 Mar 2013 11:48:49 +0100 Subject: Optimise array construction Cache the InternalClass for ArrayObject and use it directly in the constructor. Change-Id: I17346045cd14852c400c7f3886414953e4dff869 Reviewed-by: Simon Hausmann --- src/v4/qmljs_engine.cpp | 11 ++++++----- src/v4/qmljs_engine.h | 5 +++-- src/v4/qv4internalclass.cpp | 13 +++++++++++++ src/v4/qv4internalclass.h | 1 + src/v4/qv4object.cpp | 5 +++-- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/v4/qmljs_engine.cpp b/src/v4/qmljs_engine.cpp index f4d523d181..6df44ef1ef 100644 --- a/src/v4/qmljs_engine.cpp +++ b/src/v4/qmljs_engine.cpp @@ -77,11 +77,6 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) identifierCache = new Identifiers(this); - emptyClass = new InternalClass(this); - rootContext = newContext(); - rootContext->init(this); - current = rootContext; - id_undefined = newIdentifier(QStringLiteral("undefined")); id_null = newIdentifier(QStringLiteral("null")); id_true = newIdentifier(QStringLiteral("true")); @@ -106,6 +101,12 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) id_set = newIdentifier(QStringLiteral("set")); id_eval = newIdentifier(QStringLiteral("eval")); + emptyClass = new InternalClass(this); + arrayClass = emptyClass->addMember(id_length); + rootContext = newContext(); + rootContext->init(this); + current = rootContext; + objectPrototype = new (memoryManager) ObjectPrototype(this); stringPrototype = new (memoryManager) StringPrototype(rootContext); numberPrototype = new (memoryManager) NumberPrototype(this); diff --git a/src/v4/qmljs_engine.h b/src/v4/qmljs_engine.h index 6d9682ae34..cc978c63e9 100644 --- a/src/v4/qmljs_engine.h +++ b/src/v4/qmljs_engine.h @@ -148,6 +148,9 @@ struct Q_V4_EXPORT ExecutionEngine TypeErrorPrototype *typeErrorPrototype; URIErrorPrototype *uRIErrorPrototype; + InternalClass *emptyClass; + InternalClass *arrayClass; + EvalFunction *evalFunction; QVector argumentsAccessors; @@ -188,8 +191,6 @@ struct Q_V4_EXPORT ExecutionEngine QVector functions; - InternalClass *emptyClass; - ExecutionEngine(EvalISelFactory *iselFactory); ~ExecutionEngine(); diff --git a/src/v4/qv4internalclass.cpp b/src/v4/qv4internalclass.cpp index b192459117..0849442fbf 100644 --- a/src/v4/qv4internalclass.cpp +++ b/src/v4/qv4internalclass.cpp @@ -57,6 +57,19 @@ InternalClass::InternalClass(const QQmlJS::VM::InternalClass &other) { } +InternalClass *InternalClass::addMember(String *string) +{ + engine->identifierCache->toIdentifier(string); + uint id = string->identifier; + + InternalClass *newClass = new InternalClass(*this); + newClass->propertyTable.insert(id, size); + newClass->nameMap.append(string); + ++newClass->size; + transitions.insert(id, newClass); + return newClass; +} + uint InternalClass::getOrAddMember(Object *object, String *string) { engine->identifierCache->toIdentifier(string); diff --git a/src/v4/qv4internalclass.h b/src/v4/qv4internalclass.h index d7da0aed81..0d20d4001b 100644 --- a/src/v4/qv4internalclass.h +++ b/src/v4/qv4internalclass.h @@ -66,6 +66,7 @@ struct InternalClass { void removeMember(Object *object, uint id); uint find(String *s); + InternalClass *addMember(String *string); private: InternalClass(const InternalClass &other); }; diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 33a31604d7..3c7356b8f8 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -997,9 +997,10 @@ void Object::markArrayObjects() const void ArrayObject::init(ExecutionContext *context) { type = Type_ArrayObject; + internalClass = context->engine->arrayClass; - PropertyDescriptor *pd = insertMember(context->engine->id_length); - assert(pd == memberData + LengthPropertyIndex); + memberData = new PropertyDescriptor[4]; + PropertyDescriptor *pd = memberData + LengthPropertyIndex; pd->type = PropertyDescriptor::Data; pd->writable = PropertyDescriptor::Enabled; pd->enumberable = PropertyDescriptor::Disabled; -- cgit v1.2.3 From 3dc6f446d300f686d7324d99cc65b01852d399b2 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 4 Mar 2013 09:32:16 +0100 Subject: Fix off-by-one in assertion for argc/args in calls. When a call is done with 1 argument, it is not copied into the outgoing args array, but passed by-reference. So when it happens to be the last element in the stack frame, then args + argc == stackSize (because argc is 1). Change-Id: Idb769c95e9066c24a9d93cdcc24e13d3a9acc995 Reviewed-by: Simon Hausmann --- src/v4/moth/qv4vme_moth.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index aea4fce8c5..caa58101e1 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -230,26 +230,26 @@ VM::Value VME::run(QQmlJS::VM::ExecutionContext *context, const uchar *&code, } } #endif // DO_TRACE_INSTR - Q_ASSERT(instr.args + instr.argc < stackSize); + Q_ASSERT(instr.args + instr.argc <= stackSize); VM::Value *args = stack + instr.args; __qmljs_call_value(context, VALUEPTR(instr.result), /*thisObject*/0, VALUE(instr.dest), args, instr.argc); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(instr.name->toQString()), instr.args, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); - Q_ASSERT(instr.args + instr.argc < stackSize); + Q_ASSERT(instr.args + instr.argc <= stackSize); VM::Value *args = stack + instr.args; __qmljs_call_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallElement) - Q_ASSERT(instr.args + instr.argc < stackSize); + Q_ASSERT(instr.args + instr.argc <= stackSize); VM::Value *args = stack + instr.args; __qmljs_call_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index), args, instr.argc); MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallActivationProperty) - Q_ASSERT(instr.args + instr.argc < stackSize); + Q_ASSERT(instr.args + instr.argc <= stackSize); VM::Value *args = stack + instr.args; __qmljs_call_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc); MOTH_END_INSTR(CallActivationProperty) @@ -387,26 +387,26 @@ VM::Value VME::run(QQmlJS::VM::ExecutionContext *context, const uchar *&code, MOTH_END_INSTR(CallBuiltinDefineProperty) MOTH_BEGIN_INSTR(CallBuiltinDefineArray) - Q_ASSERT(instr.args + instr.argc < stackSize); + Q_ASSERT(instr.args + instr.argc <= stackSize); VM::Value *args = stack + instr.args; __qmljs_builtin_define_array(context, VALUEPTR(instr.result), args, instr.argc); MOTH_END_INSTR(CallBuiltinDefineArray) MOTH_BEGIN_INSTR(CreateValue) - Q_ASSERT(instr.args + instr.argc < stackSize); + Q_ASSERT(instr.args + instr.argc <= stackSize); VM::Value *args = stack + instr.args; __qmljs_construct_value(context, VALUEPTR(instr.result), VALUE(instr.func), args, instr.argc); MOTH_END_INSTR(CreateValue) MOTH_BEGIN_INSTR(CreateProperty) - Q_ASSERT(instr.args + instr.argc < stackSize); + Q_ASSERT(instr.args + instr.argc <= stackSize); VM::Value *args = stack + instr.args; __qmljs_construct_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); MOTH_END_INSTR(CreateProperty) MOTH_BEGIN_INSTR(CreateActivationProperty) TRACE(inline, "property name = %s, args = %d, argc = %d", instr.name->toQString().toUtf8().constData(), instr.args, instr.argc); - Q_ASSERT(instr.args + instr.argc < stackSize); + Q_ASSERT(instr.args + instr.argc <= stackSize); VM::Value *args = stack + instr.args; __qmljs_construct_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc); MOTH_END_INSTR(CreateActivationProperty) -- cgit v1.2.3 From ab23a26e671ad56b868cf2237400b22be1173470 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 4 Mar 2013 11:17:55 +0100 Subject: Fix IR control flow after a throw statement. A throw statement in a try block will now jump to the catch/finally, and a throw statement outside will jump to the exit block. This follows what happens during run-time, which fixes liveness issues with the return-temp of calls to builtin_create_exception_handler. Change-Id: I70f14f03f0431d3fc962c22984d4f486a1d81c7d Reviewed-by: Simon Hausmann --- src/v4/qv4codegen.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index 16839fac21..c602b34f54 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -1786,15 +1786,16 @@ void Codegen::linearize(IR::Function *function) trace.append(exitBlock); QVarLengthArray blocksToDelete; - foreach (IR::BasicBlock *b, function->basicBlocks) + foreach (IR::BasicBlock *b, function->basicBlocks) { if (!V.contains(b)) { - foreach (IR::BasicBlock *out, b->out) { - int idx = out->in.indexOf(b); - if (idx >= 0) - out->in.remove(idx); - } - blocksToDelete.append(b); + foreach (IR::BasicBlock *out, b->out) { + int idx = out->in.indexOf(b); + if (idx >= 0) + out->in.remove(idx); } + blocksToDelete.append(b); + } + } qDeleteAll(blocksToDelete); function->basicBlocks = trace; @@ -1972,6 +1973,7 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, IR::ExprList *throwArgs = function->New(); throwArgs->expr = throwBlock->TEMP(returnAddress); throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); + throwBlock->JUMP(exitBlock); Loop *loop = 0; qSwap(_function, function); @@ -2450,6 +2452,13 @@ bool Codegen::visit(TryStatement *ast) // We always need a finally body to clean up the exception handler IR::BasicBlock *finallyBody = _function->newBasicBlock(); + IR::BasicBlock *throwBlock = _function->newBasicBlock(); + IR::ExprList *throwArgs = _function->New(); + throwArgs->expr = throwBlock->TEMP(_returnAddress); + throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); + throwBlock->JUMP(catchBody ? catchBody : finallyBody); + qSwap(_throwBlock, throwBlock); + int inCatch = 0; if (catchBody) { inCatch = _block->newTemp(); @@ -2512,12 +2521,14 @@ bool Codegen::visit(TryStatement *ast) _scopeAndFinally = tcf.parent; + qSwap(_throwBlock, throwBlock); + IR::BasicBlock *after = _function->newBasicBlock(); _block = finallyBody; - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), deleteExceptionArgs)); int exception_to_rethrow = _block->newTemp(); move(_block->TEMP(exception_to_rethrow), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0)); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), deleteExceptionArgs)); if (ast->finallyExpression && ast->finallyExpression->statement) statement(ast->finallyExpression->statement); -- cgit v1.2.3 From 2cf8be29bc31f557af47c143048b5bb70d6000cc Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 4 Mar 2013 12:12:21 +0100 Subject: Cleanup: Get rid of manual unwind stack Instead of doing the ExecutionContext unwinding at the time of throw, do it at the time of catch, conveniently through an accept() method of the Exception object. That allows us to get rid of the separate handler registration stack. The only tricky part are that some execution contexts are allocated on the stack. When exceptions are thrown through that, we have to catch, unwinding up until that point (or rather the parent of the stack allocated context that's going to be deleted) and then simply rethrow. This patch also gets rid of the __builtin_delete_exception_handler. The next patch will rename the remainder to what it really does now. Change-Id: I00bb113b3a2fe24f7054c03fdfb8fed5cc1258b1 Reviewed-by: Lars Knoll --- src/v4/llvm_runtime.cpp | 7 +++--- src/v4/moth/qv4vme_moth.cpp | 21 +++------------- src/v4/moth/qv4vme_moth_p.h | 3 --- src/v4/qmljs_engine.h | 8 ------ src/v4/qmljs_runtime.cpp | 59 +++++++++++++++++++++++++------------------- src/v4/qmljs_runtime.h | 14 ++++++++--- src/v4/qv4functionobject.cpp | 25 ++++++++++++++++--- src/v4/qv4globalobject.cpp | 9 ++++++- src/v4/qv4isel_masm.cpp | 7 +++--- tools/v4/main.cpp | 3 ++- 10 files changed, 89 insertions(+), 67 deletions(-) diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp index 0322eccca5..c3aede7826 100644 --- a/src/v4/llvm_runtime.cpp +++ b/src/v4/llvm_runtime.cpp @@ -476,13 +476,14 @@ void __qmljs_llvm_throw(ExecutionContext *context, Value *value) void __qmljs_llvm_create_exception_handler(ExecutionContext *context, Value *result) { - void *buf = __qmljs_create_exception_handler(context); - *result = Value::fromInt32(setjmp(* static_cast(buf))); + // ### FIXME. + __qmljs_create_exception_handler(context); + *result = Value::undefinedValue(); } void __qmljs_llvm_delete_exception_handler(ExecutionContext *context) { - __qmljs_delete_exception_handler(context); + // ### FIXME. } void __qmljs_llvm_get_exception(ExecutionContext *context, Value *result) diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index caa58101e1..1238b369f6 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -270,13 +270,15 @@ VM::Value VME::run(QQmlJS::VM::ExecutionContext *context, const uchar *&code, const uchar *tryCode = code; run(context, tryCode, stack, stackSize); code = tryCode; - } catch (const VM::Exception &) { + } catch (VM::Exception &ex) { + ex.accept(context); try { *result = VM::Value::fromInt32(1); const uchar *catchCode = code; run(context, catchCode, stack, stackSize); code = catchCode; - } catch (const VM::Exception &) { + } catch (VM::Exception &ex) { + ex.accept(context); *result = VM::Value::fromInt32(1); const uchar *catchCode = code; run(context, catchCode, stack, stackSize); @@ -286,7 +288,6 @@ VM::Value VME::run(QQmlJS::VM::ExecutionContext *context, const uchar *&code, MOTH_END_INSTR(CallBuiltinCreateExceptionHandler) MOTH_BEGIN_INSTR(CallBuiltinDeleteExceptionHandler) - __qmljs_delete_exception_handler(context); return VM::Value(); MOTH_END_INSTR(CallBuiltinDeleteExceptionHandler) @@ -488,17 +489,3 @@ VM::Value VME::exec(VM::ExecutionContext *ctxt, const uchar *code) VME vme; return vme.run(ctxt, code); } - -void VME::restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code) -{ - VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); - target = handler.target; - code = handler.code; -} - -void VME::saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code) -{ - VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); - handler.target = target; - handler.code = code; -} diff --git a/src/v4/moth/qv4vme_moth_p.h b/src/v4/moth/qv4vme_moth_p.h index 0b7fc9b7af..7c09fc0638 100644 --- a/src/v4/moth/qv4vme_moth_p.h +++ b/src/v4/moth/qv4vme_moth_p.h @@ -27,9 +27,6 @@ private: , void ***storeJumpTable = 0 #endif ); - - static void restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code); - static void saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code); }; } // namespace Moth diff --git a/src/v4/qmljs_engine.h b/src/v4/qmljs_engine.h index cc978c63e9..2289e7347e 100644 --- a/src/v4/qmljs_engine.h +++ b/src/v4/qmljs_engine.h @@ -179,14 +179,6 @@ struct Q_V4_EXPORT ExecutionEngine String *id_set; String *id_eval; - struct ExceptionHandler { - ExecutionContext *context; - const uchar *code; // Interpreter state - Value *target; // Interpreter state - jmp_buf stackFrame; - }; - - QVector unwindStack; Value exception; QVector functions; diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 4047a5c809..1f73753429 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -112,6 +112,38 @@ QString numberToString(double num, int radix = 10) return str; } +Exception::Exception(ExecutionContext *throwingContext) +{ + this->throwingContext = throwingContext; + accepted = false; +} + +Exception::~Exception() +{ + assert(accepted); +} + +void Exception::accept(ExecutionContext *catchingContext) +{ + assert(!accepted); + accepted = true; + partiallyUnwindContext(catchingContext); +} + +void Exception::partiallyUnwindContext(ExecutionContext *catchingContext) +{ + if (!throwingContext) + return; + ExecutionContext *context = throwingContext; + while (context != catchingContext) { + ExecutionContext *parent = context->parent; + if (!context->withObject) + context->leaveCallContext(); + context = parent; + } + throwingContext = context; +} + extern "C" { void __qmljs_init_closure(ExecutionContext *ctx, Value *result, VM::Function *clos) @@ -955,40 +987,17 @@ void __qmljs_construct_property(ExecutionContext *context, Value *result, const void __qmljs_throw(ExecutionContext *context, const Value &value) { - assert(!context->engine->unwindStack.isEmpty()); - if (context->engine->debugger) context->engine->debugger->aboutToThrow(value); - ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); - - // clean up call contexts - while (context != handler.context) { - ExecutionContext *parent = context->parent; - if (!context->withObject) - context->leaveCallContext(); - context = parent; - } - context->engine->exception = value; - throw Exception(); + throw Exception(context); } -Q_V4_EXPORT void * __qmljs_create_exception_handler(ExecutionContext *context) +Q_V4_EXPORT void __qmljs_create_exception_handler(ExecutionContext *context) { context->engine->exception = Value::undefinedValue(); - context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler()); - ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); - handler.context = context; - return handler.stackFrame; -} - -void __qmljs_delete_exception_handler(ExecutionContext *context) -{ - assert(!context->engine->unwindStack.isEmpty()); - - context->engine->unwindStack.pop_back(); } void __qmljs_get_exception(ExecutionContext *context, Value *result) diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h index 4da9015ab6..f504e6ddfe 100644 --- a/src/v4/qmljs_runtime.h +++ b/src/v4/qmljs_runtime.h @@ -89,7 +89,16 @@ struct ArrayObject; struct ErrorObject; struct ExecutionEngine; -struct Exception { +struct Q_V4_EXPORT Exception { + explicit Exception(ExecutionContext *throwingContext); + ~Exception(); + + void accept(ExecutionContext *catchingContext); + + void partiallyUnwindContext(ExecutionContext *catchingContext); +private: + ExecutionContext *throwingContext; + bool accepted; }; extern "C" { @@ -196,8 +205,7 @@ void __qmljs_delete_name(ExecutionContext *ctx, Value *result, String *name); void Q_NORETURN __qmljs_throw(ExecutionContext*, const Value &value); // actually returns a jmp_buf * -Q_V4_EXPORT void *__qmljs_create_exception_handler(ExecutionContext *context); -void __qmljs_delete_exception_handler(ExecutionContext *context); +Q_V4_EXPORT void __qmljs_create_exception_handler(ExecutionContext *context); void __qmljs_get_exception(ExecutionContext *context, Value *result); // binary operators diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 437ea704df..79905146fe 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -373,7 +373,13 @@ Value ScriptFunction::construct(Managed *that, ExecutionContext *context, Value ctx->argumentCount = argc; ctx->initCallContext(context); - Value result = f->function->code(ctx, f->function->codeData); + Value result = Value::undefinedValue(); + try { + result = f->function->code(ctx, f->function->codeData); + } catch (Exception &ex) { + ex.partiallyUnwindContext(ctx->parent); + throw; + } ctx->leaveCallContext(); if (result.isObject()) @@ -402,7 +408,13 @@ Value ScriptFunction::call(Managed *that, ExecutionContext *context, const Value ctx->argumentCount = argc; ctx->initCallContext(context); - Value result = f->function->code(ctx, f->function->codeData); + Value result = Value::undefinedValue(); + try { + result = f->function->code(ctx, f->function->codeData); + } catch (Exception &ex) { + ex.partiallyUnwindContext(ctx->parent); + throw; + } ctx->leaveCallContext(); return result; } @@ -446,7 +458,14 @@ Value BuiltinFunctionOld::call(Managed *that, ExecutionContext *context, const V ctx->argumentCount = argc; ctx->initCallContext(context); - Value result = f->code(ctx); + Value result = Value::undefinedValue(); + try { + result = f->code(ctx); + } catch (Exception &ex) { + ex.partiallyUnwindContext(ctx->parent); + throw; + } + ctx->leaveCallContext(); return result; } diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp index 2c921b2e50..2454126c90 100644 --- a/src/v4/qv4globalobject.cpp +++ b/src/v4/qv4globalobject.cpp @@ -393,7 +393,14 @@ Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Va bool cstrict = ctx->strictMode; ctx->strictMode = strict; - Value result = f->code(ctx, f->codeData); + Value result = Value::undefinedValue(); + try { + result = f->code(ctx, f->codeData); + } catch (Exception &ex) { + if (strict) + ex.partiallyUnwindContext(ctx->parent); + throw; + } ctx->strictMode = cstrict; diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 1659276009..26307e8a78 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -646,10 +646,12 @@ static void *tryWrapper(ExecutionContext *context, void *localsPtr, void *(*exce void *addressToContinueAt = 0; try { addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 0); - } catch (const Exception&) { + } catch (Exception& ex) { + ex.accept(context); try { addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 1); - } catch (const Exception&) { + } catch (Exception& ex) { + ex.accept(context); addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 1); } } @@ -694,7 +696,6 @@ void InstructionSelection::callBuiltinDeleteExceptionHandler() { // This assumes that we're in code that was called by tryWrapper, so we return to try wrapper // with the address that we'd like to continue at, which is right after the ret below. - generateFunctionCall(Assembler::Void, __qmljs_delete_exception_handler, Assembler::ContextRegister); Assembler::DataLabelPtr continuation = _as->moveWithPatch(Assembler::TrustedImmPtr(0), Assembler::ReturnValueRegister); _as->leaveStandardStackFrame(/*locals*/0); _as->ret(); diff --git a/tools/v4/main.cpp b/tools/v4/main.cpp index d73fa480da..89a25973e8 100644 --- a/tools/v4/main.cpp +++ b/tools/v4/main.cpp @@ -388,7 +388,8 @@ int main(int argc, char *argv[]) if (! qgetenv("SHOW_EXIT_VALUE").isEmpty()) std::cout << "exit value: " << qPrintable(result.toString(ctx)->toQString()) << std::endl; } - } catch (const QQmlJS::VM::Exception&) { + } catch (QQmlJS::VM::Exception& ex) { + ex.accept(ctx); showException(ctx); return EXIT_FAILURE; } -- cgit v1.2.3 From ea5b6e425e6d6e7f56194dea40242b848604489e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 4 Mar 2013 12:14:54 +0100 Subject: Cleanup: Rename __builtin_delete_exception_handler This IR builtin function no more deletes an exception handler, instead it tells the back-end to stop try catching exceptions. In MASM and Moth this is implemented by returning from the nested function call initiated by the try. Change-Id: Ia8336c998817a73aeed03f4a05d4b592cc9143ad Reviewed-by: Lars Knoll --- src/v4/moth/qv4instr_moth_p.h | 6 +++--- src/v4/moth/qv4isel_moth.cpp | 4 ++-- src/v4/moth/qv4isel_moth_p.h | 2 +- src/v4/moth/qv4vme_moth.cpp | 4 ++-- src/v4/qv4codegen.cpp | 14 +++++++------- src/v4/qv4codegen_p.h | 8 ++++---- src/v4/qv4ir.cpp | 4 ++-- src/v4/qv4ir_p.h | 2 +- src/v4/qv4isel_llvm.cpp | 7 +++---- src/v4/qv4isel_llvm_p.h | 2 +- src/v4/qv4isel_masm.cpp | 2 +- src/v4/qv4isel_masm_p.h | 2 +- src/v4/qv4isel_p.cpp | 4 ++-- src/v4/qv4isel_p.h | 2 +- 14 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/v4/moth/qv4instr_moth_p.h b/src/v4/moth/qv4instr_moth_p.h index 07766874ff..8810a92928 100644 --- a/src/v4/moth/qv4instr_moth_p.h +++ b/src/v4/moth/qv4instr_moth_p.h @@ -22,7 +22,7 @@ F(CallActivationProperty, callActivationProperty) \ F(CallBuiltinThrow, callBuiltinThrow) \ F(CallBuiltinCreateExceptionHandler, callBuiltinCreateExceptionHandler) \ - F(CallBuiltinDeleteExceptionHandler, callBuiltinDeleteExceptionHandler) \ + F(CallBuiltinFinishTry, callBuiltinFinishTry) \ F(CallBuiltinGetException, callBuiltinGetException) \ F(CallBuiltinPushScope, callBuiltinPushScope) \ F(CallBuiltinPushCatchScope, callBuiltinPushCatchScope) \ @@ -248,7 +248,7 @@ union Instr MOTH_INSTR_HEADER Param result; }; - struct instr_callBuiltinDeleteExceptionHandler { + struct instr_callBuiltinFinishTry { MOTH_INSTR_HEADER }; struct instr_callBuiltinGetException { @@ -470,7 +470,7 @@ union Instr instr_callActivationProperty callActivationProperty; instr_callBuiltinThrow callBuiltinThrow; instr_callBuiltinCreateExceptionHandler callBuiltinCreateExceptionHandler; - instr_callBuiltinDeleteExceptionHandler callBuiltinDeleteExceptionHandler; + instr_callBuiltinFinishTry callBuiltinFinishTry; instr_callBuiltinGetException callBuiltinGetException; instr_callBuiltinPushScope callBuiltinPushScope; instr_callBuiltinPushCatchScope callBuiltinPushCatchScope; diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp index d8d7040134..c416839548 100644 --- a/src/v4/moth/qv4isel_moth.cpp +++ b/src/v4/moth/qv4isel_moth.cpp @@ -836,9 +836,9 @@ void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) addInstruction(call); } -void InstructionSelection::callBuiltinDeleteExceptionHandler() +void InstructionSelection::callBuiltinFinishTry() { - Instruction::CallBuiltinDeleteExceptionHandler call; + Instruction::CallBuiltinFinishTry call; addInstruction(call); } diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h index 6eaee6dd02..c6677f234f 100644 --- a/src/v4/moth/qv4isel_moth_p.h +++ b/src/v4/moth/qv4isel_moth_p.h @@ -44,7 +44,7 @@ protected: virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); - virtual void callBuiltinDeleteExceptionHandler(); + virtual void callBuiltinFinishTry(); virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp index 1238b369f6..0d2f2bcdcd 100644 --- a/src/v4/moth/qv4vme_moth.cpp +++ b/src/v4/moth/qv4vme_moth.cpp @@ -287,9 +287,9 @@ VM::Value VME::run(QQmlJS::VM::ExecutionContext *context, const uchar *&code, } MOTH_END_INSTR(CallBuiltinCreateExceptionHandler) - MOTH_BEGIN_INSTR(CallBuiltinDeleteExceptionHandler) + MOTH_BEGIN_INSTR(CallBuiltinFinishTry) return VM::Value(); - MOTH_END_INSTR(CallBuiltinDeleteExceptionHandler) + MOTH_END_INSTR(CallBuiltinFinishTry) MOTH_BEGIN_INSTR(CallBuiltinGetException) __qmljs_get_exception(context, VALUEPTR(instr.result)); diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp index c602b34f54..e6c6f34223 100644 --- a/src/v4/qv4codegen.cpp +++ b/src/v4/qv4codegen.cpp @@ -2471,14 +2471,14 @@ bool Codegen::visit(TryStatement *ast) // Pass the hidden "inCatch" and "hasException" TEMPs to the // builtin_delete_exception_handler, in order to have those TEMPs alive for // the duration of the exception handling block. - IR::ExprList *deleteExceptionArgs = _function->New(); - deleteExceptionArgs->init(_block->TEMP(hasException)); + IR::ExprList *finishTryArgs = _function->New(); + finishTryArgs->init(_block->TEMP(hasException)); if (inCatch) { - deleteExceptionArgs->next = _function->New(); - deleteExceptionArgs->next->init(_block->TEMP(inCatch)); + finishTryArgs->next = _function->New(); + finishTryArgs->next->init(_block->TEMP(inCatch)); } - ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression, deleteExceptionArgs); + ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression, finishTryArgs); _scopeAndFinally = &tcf; _block->CJUMP(_block->TEMP(hasException), catchBody ? catchBody : finallyBody, tryBody); @@ -2528,7 +2528,7 @@ bool Codegen::visit(TryStatement *ast) int exception_to_rethrow = _block->newTemp(); move(_block->TEMP(exception_to_rethrow), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0)); - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), deleteExceptionArgs)); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_finish_try, 0, 0), finishTryArgs)); if (ast->finallyExpression && ast->finallyExpression->statement) statement(ast->finallyExpression->statement); @@ -2555,7 +2555,7 @@ void Codegen::unwindException(Codegen::ScopeAndFinally *outest) _scopeAndFinally = _scopeAndFinally->parent; --_function->insideWithOrCatch; } else { - _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), _scopeAndFinally->deleteExceptionArgs)); + _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_finish_try, 0, 0), _scopeAndFinally->finishTryArgs)); ScopeAndFinally *tc = _scopeAndFinally; _scopeAndFinally = tc->parent; if (tc->finally && tc->finally->statement) diff --git a/src/v4/qv4codegen_p.h b/src/v4/qv4codegen_p.h index 239027a7d9..13d8315cf7 100644 --- a/src/v4/qv4codegen_p.h +++ b/src/v4/qv4codegen_p.h @@ -217,12 +217,12 @@ protected: struct ScopeAndFinally { ScopeAndFinally *parent; AST::Finally *finally; - IR::ExprList *deleteExceptionArgs; + IR::ExprList *finishTryArgs; bool popScope; - ScopeAndFinally(ScopeAndFinally *parent) : parent(parent), finally(0), deleteExceptionArgs(0), popScope(true) {} - ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally, IR::ExprList *deleteExceptionArgs) - : parent(parent), finally(finally), deleteExceptionArgs(deleteExceptionArgs), popScope(false) + ScopeAndFinally(ScopeAndFinally *parent) : parent(parent), finally(0), finishTryArgs(0), popScope(true) {} + ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally, IR::ExprList *finishTryArgs) + : parent(parent), finally(finally), finishTryArgs(finishTryArgs), popScope(false) {} }; diff --git a/src/v4/qv4ir.cpp b/src/v4/qv4ir.cpp index 2fa8f0a94a..c44af3da76 100644 --- a/src/v4/qv4ir.cpp +++ b/src/v4/qv4ir.cpp @@ -354,8 +354,8 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_throw"; case Name::builtin_create_exception_handler: return "builtin_create_exception_handler"; - case Name::builtin_delete_exception_handler: - return "builtin_delete_exception_handler"; + case Name::builtin_finish_try: + return "builtin_finish_try"; case Name::builtin_get_exception: return "builtin_get_exception"; case IR::Name::builtin_foreach_iterator_object: diff --git a/src/v4/qv4ir_p.h b/src/v4/qv4ir_p.h index 352575935f..50a5247277 100644 --- a/src/v4/qv4ir_p.h +++ b/src/v4/qv4ir_p.h @@ -290,7 +290,7 @@ struct Name: Expr { builtin_postdecrement, builtin_throw, builtin_create_exception_handler, - builtin_delete_exception_handler, + builtin_finish_try, builtin_get_exception, builtin_foreach_iterator_object, builtin_foreach_next_property_name, diff --git a/src/v4/qv4isel_llvm.cpp b/src/v4/qv4isel_llvm.cpp index e6010cfd2a..f0b8526fea 100644 --- a/src/v4/qv4isel_llvm.cpp +++ b/src/v4/qv4isel_llvm.cpp @@ -434,7 +434,7 @@ void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) Q_UNREACHABLE(); } -void InstructionSelection::callBuiltinDeleteExceptionHandler() +void InstructionSelection::callBuiltinFinishTry() { // TODO assert(!"TODO!"); @@ -1146,9 +1146,8 @@ void InstructionSelection::genCallName(IR::Call *e, llvm::Value *result) _llvmValue = CreateLoad(result); return; - case IR::Name::builtin_delete_exception_handler: - CreateCall(getRuntimeFunction("__qmljs_llvm_delete_exception_handler"), - _llvmFunction->arg_begin()); + case IR::Name::builtin_finish_try: + // ### FIXME. return; case IR::Name::builtin_get_exception: diff --git a/src/v4/qv4isel_llvm_p.h b/src/v4/qv4isel_llvm_p.h index fb28456b64..e749863605 100644 --- a/src/v4/qv4isel_llvm_p.h +++ b/src/v4/qv4isel_llvm_p.h @@ -89,7 +89,7 @@ public: // methods from InstructionSelection: virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); - virtual void callBuiltinDeleteExceptionHandler(); + virtual void callBuiltinFinishTry(); virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 26307e8a78..35bd638bed 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -692,7 +692,7 @@ void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) _as->store32(Assembler::TrustedImm32(Value::Boolean_Type), addr); } -void InstructionSelection::callBuiltinDeleteExceptionHandler() +void InstructionSelection::callBuiltinFinishTry() { // This assumes that we're in code that was called by tryWrapper, so we return to try wrapper // with the address that we'd like to continue at, which is right after the ret below. diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index 23f9da0c55..acd6e5f662 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -763,7 +763,7 @@ protected: virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); virtual void callBuiltinThrow(IR::Temp *arg); virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); - virtual void callBuiltinDeleteExceptionHandler(); + virtual void callBuiltinFinishTry(); virtual void callBuiltinGetException(IR::Temp *result); virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index 03fa074584..cbf2e4cf69 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -315,8 +315,8 @@ void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result) return; } - case IR::Name::builtin_delete_exception_handler: - callBuiltinDeleteExceptionHandler(); + case IR::Name::builtin_finish_try: + callBuiltinFinishTry(); return; case IR::Name::builtin_get_exception: diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h index 40cf86eddb..cc1ebf08da 100644 --- a/src/v4/qv4isel_p.h +++ b/src/v4/qv4isel_p.h @@ -104,7 +104,7 @@ public: // to implement by subclasses: virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) = 0; virtual void callBuiltinThrow(IR::Temp *arg) = 0; virtual void callBuiltinCreateExceptionHandler(IR::Temp *result) = 0; - virtual void callBuiltinDeleteExceptionHandler() = 0; + virtual void callBuiltinFinishTry() = 0; virtual void callBuiltinGetException(IR::Temp *result) = 0; virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) = 0; virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) = 0; -- cgit v1.2.3 From 03b0e23ccec7b040709c9cf52cbd3efe224982b5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 4 Mar 2013 15:30:40 +0100 Subject: Cleanup unwind handler Store the platform specific unwind info (CIE/FDE on Linux/Mac x86/x86-64) directly inside the VM::Function and therefore make all the functions of UnwindHelper static. Also calculate the function size correctly. Change-Id: If6a6b92a250044c6799218ef3506f34d630674c7 Reviewed-by: Lars Knoll --- src/v4/qmljs_engine.cpp | 5 +-- src/v4/qmljs_engine.h | 1 - src/v4/qv4functionobject.h | 1 + src/v4/qv4isel_masm.cpp | 7 ++-- src/v4/qv4unwindhelper.cpp | 4 +-- src/v4/qv4unwindhelper.h | 21 +++--------- src/v4/qv4unwindhelper_p-dw2.h | 76 +++++++++--------------------------------- 7 files changed, 28 insertions(+), 87 deletions(-) diff --git a/src/v4/qmljs_engine.cpp b/src/v4/qmljs_engine.cpp index 6df44ef1ef..126162e38f 100644 --- a/src/v4/qmljs_engine.cpp +++ b/src/v4/qmljs_engine.cpp @@ -71,7 +71,6 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) , exception(Value::nullValue()) { MemoryManager::GCBlocker gcBlocker(memoryManager); - unwindHelper = UnwindHelper::create(); memoryManager->setExecutionEngine(this); @@ -236,9 +235,7 @@ ExecutionEngine::~ExecutionEngine() delete globalObject.asObject(); rootContext->destroy(); delete rootContext; - if (unwindHelper) - unwindHelper->deregisterFunctions(functions); - delete unwindHelper; + UnwindHelper::deregisterFunctions(functions); qDeleteAll(functions); delete memoryManager; } diff --git a/src/v4/qmljs_engine.h b/src/v4/qmljs_engine.h index 2289e7347e..0d8dd05fc3 100644 --- a/src/v4/qmljs_engine.h +++ b/src/v4/qmljs_engine.h @@ -102,7 +102,6 @@ class RegExp; struct Q_V4_EXPORT ExecutionEngine { MemoryManager *memoryManager; - UnwindHelper *unwindHelper; EvalISelFactory *iselFactory; ExecutionContext *current; ExecutionContext *rootContext; diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index bbe6306e98..2cfb66fc94 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -115,6 +115,7 @@ struct Function { VM::Value (*code)(VM::ExecutionContext *, const uchar *); const uchar *codeData; JSC::MacroAssemblerCodeRef codeRef; + QByteArray unwindInfo; // CIE+FDE on x86/x86-64 QVector formals; QVector locals; diff --git a/src/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp index 35bd638bed..fc39f4ce31 100644 --- a/src/v4/qv4isel_masm.cpp +++ b/src/v4/qv4isel_masm.cpp @@ -422,6 +422,8 @@ void Assembler::link(VM::Function *vmFunc) JSC::JSGlobalData dummy; JSC::LinkBuffer linkBuffer(dummy, this, 0); + uint32_t codeSize = linkBuffer.offsetOf(label()); + QHash functions; foreach (CallToLink ctl, _callsToLink) { linkBuffer.link(ctl.call, ctl.externalFunction); @@ -456,6 +458,7 @@ void Assembler::link(VM::Function *vmFunc) } vmFunc->code = (Value (*)(VM::ExecutionContext *, const uchar *)) vmFunc->codeRef.code().executableAddress(); + vmFunc->unwindInfo = UnwindHelper::createUnwindInfo(vmFunc, codeSize); } InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module) @@ -517,7 +520,6 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) _as->poke(Assembler::ScratchRegister); #endif _as->ret(); - // TODO: add a label and a nop, so we can determine the exact function length _as->link(_vmFunction); @@ -526,8 +528,7 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) memcpy(_vmFunction->lookups, _lookups.constData(), _lookups.size()*sizeof(Lookup)); } - if (engine()->unwindHelper) - engine()->unwindHelper->registerFunction(_vmFunction); + UnwindHelper::registerFunction(_vmFunction); qSwap(_vmFunction, vmFunction); qSwap(_function, function); diff --git a/src/v4/qv4unwindhelper.cpp b/src/v4/qv4unwindhelper.cpp index 31d9d6871d..a376b33aa6 100644 --- a/src/v4/qv4unwindhelper.cpp +++ b/src/v4/qv4unwindhelper.cpp @@ -26,12 +26,10 @@ #ifdef USE_NULL_HELPER using namespace QQmlJS::VM; -UnwindHelper *UnwindHelper::create() { return 0; } -UnwindHelper::UnwindHelper() {} -UnwindHelper::~UnwindHelper() {} void UnwindHelper::registerFunction(Function *function) {Q_UNUSED(function);} void UnwindHelper::registerFunctions(QVector functions) {Q_UNUSED(functions);} void UnwindHelper::deregisterFunction(Function *function) {Q_UNUSED(function);} void UnwindHelper::deregisterFunctions(QVector functions) {Q_UNUSED(functions);} +QByteArray UnwindHelper::createUnwindInfo(Function*, size_t) { return QByteArray(); } #endif // USE_NULL_HELPER diff --git a/src/v4/qv4unwindhelper.h b/src/v4/qv4unwindhelper.h index bf70a01d3c..19d33bbd33 100644 --- a/src/v4/qv4unwindhelper.h +++ b/src/v4/qv4unwindhelper.h @@ -10,24 +10,13 @@ struct Function; class UnwindHelper { - Q_DISABLE_COPY(UnwindHelper) - -private: - UnwindHelper(); - public: - ~UnwindHelper(); - - static UnwindHelper *create(); - - void registerFunction(Function *function); - void registerFunctions(QVector functions); - void deregisterFunction(Function *function); - void deregisterFunctions(QVector functions); + static QByteArray createUnwindInfo(Function*f, size_t functionSize); -private: - struct Private; - Private *p; + static void registerFunction(Function *function); + static void registerFunctions(QVector functions); + static void deregisterFunction(Function *function); + static void deregisterFunctions(QVector functions); }; } // VM namespace diff --git a/src/v4/qv4unwindhelper_p-dw2.h b/src/v4/qv4unwindhelper_p-dw2.h index 302e173c08..4d608123cd 100644 --- a/src/v4/qv4unwindhelper_p-dw2.h +++ b/src/v4/qv4unwindhelper_p-dw2.h @@ -46,55 +46,6 @@ static const int address_range_offset = 32; #endif } // anonymous namespace -namespace { -struct EHInfo { - unsigned char *cie_and_fde; - unsigned char *fde; - - EHInfo(unsigned char *cie_and_fde, unsigned char *fde) - : cie_and_fde(cie_and_fde), fde(fde) - { - Q_ASSERT(cie_and_fde); - Q_ASSERT(fde); - - __register_frame(fde); - } - - ~EHInfo() - { - Q_ASSERT(cie_and_fde); - Q_ASSERT(fde); - - __deregister_frame(fde); - - delete[] cie_and_fde; - } -}; -} // anonymous namespace - -struct Function; - -struct UnwindHelper::Private -{ - QHash ehInfoForFunction; -}; - -UnwindHelper *UnwindHelper::create() -{ - return new UnwindHelper; -} - -UnwindHelper::UnwindHelper() - : p(new Private) -{} - -UnwindHelper::~UnwindHelper() -{ - foreach (Function *f, p->ehInfoForFunction.keys()) - deregisterFunction(f); - delete p; -} - void UnwindHelper::registerFunctions(QVector functions) { foreach (Function *f, functions) registerFunction(f); @@ -102,12 +53,9 @@ void UnwindHelper::registerFunctions(QVector functions) void UnwindHelper::deregisterFunction(Function *function) { - QHash::iterator i = p->ehInfoForFunction.find(function); - if (i == p->ehInfoForFunction.end()) + if (function->unwindInfo.isEmpty()) return; - - delete i.value(); - p->ehInfoForFunction.erase(i); + __deregister_frame(function->unwindInfo.data() + fde_offset); } void UnwindHelper::deregisterFunctions(QVector functions) @@ -133,17 +81,25 @@ void writeIntPtrValue(unsigned char *addr, intptr_t val) void UnwindHelper::registerFunction(Function *function) { - unsigned char *cie_and_fde = new unsigned char[sizeof(cie_fde_data)]; + if (function->unwindInfo.isEmpty()) + return; + __register_frame(function->unwindInfo.data() + fde_offset); +} + +QByteArray UnwindHelper::createUnwindInfo(Function *f, size_t functionSize) +{ + QByteArray info; + info.resize(sizeof(cie_fde_data)); + + unsigned char *cie_and_fde = reinterpret_cast(info.data()); memcpy(cie_and_fde, cie_fde_data, sizeof(cie_fde_data)); - intptr_t ptr = static_cast(function->codeRef.code().executableAddress()) - static_cast(0); + intptr_t ptr = static_cast(f->codeRef.code().executableAddress()) - static_cast(0); writeIntPtrValue(cie_and_fde + initial_location_offset, ptr); - size_t len = function->codeRef.size(); - writeIntPtrValue(cie_and_fde + address_range_offset, len); + writeIntPtrValue(cie_and_fde + address_range_offset, functionSize); - p->ehInfoForFunction.insert(function, new EHInfo(cie_and_fde, - cie_and_fde + fde_offset)); + return info; } } // VM namespace -- cgit v1.2.3 From f40724b9d09575c6894c2b2c4f01256b0c731eee Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 5 Mar 2013 12:49:35 +0100 Subject: Rename qv4ir_p.h to qv4jsir_p.h to avoid conflict with qtdeclarative Change-Id: I0e222141232f191e47950318f8262144de486703 Reviewed-by: Lars Knoll --- src/v4/moth/qv4isel_moth_p.h | 2 +- src/v4/qmljs_runtime.cpp | 2 +- src/v4/qv4_llvm_p.h | 2 +- src/v4/qv4codegen_p.h | 2 +- src/v4/qv4dateobject.cpp | 2 +- src/v4/qv4errorobject.cpp | 2 +- src/v4/qv4functionobject.cpp | 4 +- src/v4/qv4globalobject.cpp | 2 +- src/v4/qv4ir.cpp | 885 ------------------------------------------- src/v4/qv4ir_p.h | 786 -------------------------------------- src/v4/qv4isel_llvm.cpp | 2 +- src/v4/qv4isel_llvm_p.h | 2 +- src/v4/qv4isel_masm_p.h | 2 +- src/v4/qv4isel_p.cpp | 2 +- src/v4/qv4isel_p.h | 2 +- src/v4/qv4isel_util_p.h | 2 +- src/v4/qv4jsir.cpp | 885 +++++++++++++++++++++++++++++++++++++++++++ src/v4/qv4jsir_p.h | 786 ++++++++++++++++++++++++++++++++++++++ src/v4/qv4object.cpp | 4 +- src/v4/qv4objectproto.cpp | 2 +- src/v4/qv4regexpobject.cpp | 4 +- src/v4/qv4stringobject.cpp | 2 +- src/v4/v4.pro | 4 +- 23 files changed, 1694 insertions(+), 1694 deletions(-) delete mode 100644 src/v4/qv4ir.cpp delete mode 100644 src/v4/qv4ir_p.h create mode 100644 src/v4/qv4jsir.cpp create mode 100644 src/v4/qv4jsir_p.h diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h index c6677f234f..161fbf3574 100644 --- a/src/v4/moth/qv4isel_moth_p.h +++ b/src/v4/moth/qv4isel_moth_p.h @@ -3,7 +3,7 @@ #include "qv4global.h" #include "qv4isel_p.h" -#include "qv4ir_p.h" +#include "qv4jsir_p.h" #include "qv4object.h" #include "qv4instr_moth_p.h" diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp index 1f73753429..4f88f3f65e 100644 --- a/src/v4/qmljs_runtime.cpp +++ b/src/v4/qmljs_runtime.cpp @@ -43,7 +43,7 @@ #include "debugging.h" #include "qmljs_runtime.h" #include "qv4object.h" -#include "qv4ir_p.h" +#include "qv4jsir_p.h" #include "qv4objectproto.h" #include "qv4globalobject.h" #include "qv4stringobject.h" diff --git a/src/v4/qv4_llvm_p.h b/src/v4/qv4_llvm_p.h index 60625c8677..d9390f429f 100644 --- a/src/v4/qv4_llvm_p.h +++ b/src/v4/qv4_llvm_p.h @@ -31,7 +31,7 @@ #define QV4_LLVM_P_H #include "qv4global.h" -#include "qv4ir_p.h" +#include "qv4jsir_p.h" #include diff --git a/src/v4/qv4codegen_p.h b/src/v4/qv4codegen_p.h index 13d8315cf7..283e28251f 100644 --- a/src/v4/qv4codegen_p.h +++ b/src/v4/qv4codegen_p.h @@ -42,7 +42,7 @@ #define QV4CODEGEN_P_H #include "qv4global.h" -#include "qv4ir_p.h" +#include "qv4jsir_p.h" #include #include #include diff --git a/src/v4/qv4dateobject.cpp b/src/v4/qv4dateobject.cpp index c44fab7fb3..554b822557 100644 --- a/src/v4/qv4dateobject.cpp +++ b/src/v4/qv4dateobject.cpp @@ -58,7 +58,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/v4/qv4errorobject.cpp b/src/v4/qv4errorobject.cpp index c13c2131f2..df737083e3 100644 --- a/src/v4/qv4errorobject.cpp +++ b/src/v4/qv4errorobject.cpp @@ -56,7 +56,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 79905146fe..d8f796a9ee 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -40,7 +40,7 @@ ****************************************************************************/ #include "qv4object.h" -#include "qv4ir_p.h" +#include "qv4jsir_p.h" #include "qv4isel_p.h" #include "qv4objectproto.h" #include "qv4stringobject.h" @@ -50,7 +50,7 @@ #include #include #include -#include +#include #include #include "private/qlocale_tools_p.h" diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp index 2454126c90..e5192fa010 100644 --- a/src/v4/qv4globalobject.cpp +++ b/src/v4/qv4globalobject.cpp @@ -48,7 +48,7 @@ #include #include #include -#include +#include #include #include "private/qlocale_tools_p.h" diff --git a/src/v4/qv4ir.cpp b/src/v4/qv4ir.cpp deleted file mode 100644 index c44af3da76..0000000000 --- a/src/v4/qv4ir.cpp +++ /dev/null @@ -1,885 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4ir_p.h" -#include - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QQmlJS { -namespace IR { - -const char *typeName(Type t) -{ - switch (t) { - case UndefinedType: return "undefined"; - case NullType: return "null"; - case BoolType: return "bool"; - case NumberType: return "number"; - default: return "invalid"; - } -} - -const char *opname(AluOp op) -{ - switch (op) { - case OpInvalid: return "?"; - - case OpIfTrue: return "(bool)"; - case OpNot: return "!"; - case OpUMinus: return "-"; - case OpUPlus: return "+"; - case OpCompl: return "~"; - case OpIncrement: return "++"; - case OpDecrement: return "--"; - - case OpBitAnd: return "&"; - case OpBitOr: return "|"; - case OpBitXor: return "^"; - - case OpAdd: return "+"; - case OpSub: return "-"; - case OpMul: return "*"; - case OpDiv: return "/"; - case OpMod: return "%"; - - case OpLShift: return "<<"; - case OpRShift: return ">>"; - case OpURShift: return ">>>"; - - case OpGt: return ">"; - case OpLt: return "<"; - case OpGe: return ">="; - case OpLe: return "<="; - case OpEqual: return "=="; - case OpNotEqual: return "!="; - case OpStrictEqual: return "==="; - case OpStrictNotEqual: return "!=="; - - case OpInstanceof: return "instanceof"; - case OpIn: return "in"; - - case OpAnd: return "&&"; - case OpOr: return "||"; - - default: return "?"; - - } // switch -} - -AluOp binaryOperator(int op) -{ - switch (static_cast(op)) { - case QSOperator::Add: return OpAdd; - case QSOperator::And: return OpAnd; - case QSOperator::BitAnd: return OpBitAnd; - case QSOperator::BitOr: return OpBitOr; - case QSOperator::BitXor: return OpBitXor; - case QSOperator::Div: return OpDiv; - case QSOperator::Equal: return OpEqual; - case QSOperator::Ge: return OpGe; - case QSOperator::Gt: return OpGt; - case QSOperator::Le: return OpLe; - case QSOperator::LShift: return OpLShift; - case QSOperator::Lt: return OpLt; - case QSOperator::Mod: return OpMod; - case QSOperator::Mul: return OpMul; - case QSOperator::NotEqual: return OpNotEqual; - case QSOperator::Or: return OpOr; - case QSOperator::RShift: return OpRShift; - case QSOperator::StrictEqual: return OpStrictEqual; - case QSOperator::StrictNotEqual: return OpStrictNotEqual; - case QSOperator::Sub: return OpSub; - case QSOperator::URShift: return OpURShift; - case QSOperator::InstanceOf: return OpInstanceof; - case QSOperator::In: return OpIn; - default: return OpInvalid; - } -} - -struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor -{ - CloneExpr clone; - QSet subexpressions; // contains all the non-cloned subexpressions in the given function - Expr *uniqueExpr; - - RemoveSharedExpressions(): uniqueExpr(0) {} - - void operator()(IR::Function *function) - { - subexpressions.clear(); - - foreach (BasicBlock *block, function->basicBlocks) { - clone.setBasicBlock(block); - - foreach (Stmt *s, block->statements) { - s->accept(this); - } - } - } - - template - _Expr *cleanup(_Expr *expr) - { - if (subexpressions.contains(expr)) { - // the cloned expression is unique by definition - // so we don't need to add it to `subexpressions'. - return clone(expr); - } - - subexpressions.insert(expr); - IR::Expr *e = expr; - qSwap(uniqueExpr, e); - expr->accept(this); - qSwap(uniqueExpr, e); - return static_cast<_Expr *>(e); - } - - // statements - virtual void visitExp(Exp *s) - { - s->expr = cleanup(s->expr); - } - - virtual void visitEnter(Enter *s) - { - s->expr = cleanup(s->expr); - } - - virtual void visitLeave(Leave *) - { - // nothing to do for Leave statements - } - - virtual void visitMove(Move *s) - { - s->target = cleanup(s->target); - s->source = cleanup(s->source); - } - - virtual void visitJump(Jump *) - { - // nothing to do for Jump statements - } - - virtual void visitCJump(CJump *s) - { - s->cond = cleanup(s->cond); - } - - virtual void visitRet(Ret *s) - { - s->expr = cleanup(s->expr); - } - - - // expressions - virtual void visitConst(Const *) {} - virtual void visitString(String *) {} - virtual void visitRegExp(RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *) {} - virtual void visitClosure(Closure *) {} - - virtual void visitUnop(Unop *e) - { - e->expr = cleanup(e->expr); - } - - virtual void visitBinop(Binop *e) - { - e->left = cleanup(e->left); - e->right = cleanup(e->right); - } - - virtual void visitCall(Call *e) - { - e->base = cleanup(e->base); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr = cleanup(it->expr); - } - - virtual void visitNew(New *e) - { - e->base = cleanup(e->base); - for (IR::ExprList *it = e->args; it; it = it->next) - it->expr = cleanup(it->expr); - } - - virtual void visitSubscript(Subscript *e) - { - e->base = cleanup(e->base); - e->index = cleanup(e->index); - } - - virtual void visitMember(Member *e) - { - e->base = cleanup(e->base); - } -}; - -void Const::dump(QTextStream &out) -{ - switch (type) { - case QQmlJS::IR::UndefinedType: - out << "undefined"; - break; - case QQmlJS::IR::NullType: - out << "null"; - break; - case QQmlJS::IR::BoolType: - out << (value ? "true" : "false"); - break; - default: - out << QString::number(value, 'g', 16); - break; - } -} - -void String::dump(QTextStream &out) -{ - out << '"' << escape(*value) << '"'; -} - -QString String::escape(const QString &s) -{ - QString r; - for (int i = 0; i < s.length(); ++i) { - const QChar ch = s.at(i); - if (ch == QLatin1Char('\n')) - r += QStringLiteral("\\n"); - else if (ch == QLatin1Char('\r')) - r += QStringLiteral("\\r"); - else if (ch == QLatin1Char('\\')) - r += QStringLiteral("\\\\"); - else if (ch == QLatin1Char('"')) - r += QStringLiteral("\\\""); - else if (ch == QLatin1Char('\'')) - r += QStringLiteral("\\'"); - else - r += ch; - } - return r; -} - -void RegExp::dump(QTextStream &out) -{ - char f[3]; - int i = 0; - if (flags & RegExp_Global) - f[i++] = 'g'; - if (flags & RegExp_IgnoreCase) - f[i++] = 'i'; - if (flags & RegExp_Multiline) - f[i++] = 'm'; - f[i] = 0; - - out << '/' << *value << '/' << f; -} - -void Name::init(const QString *id, quint32 line, quint32 column) -{ - this->id = id; - this->builtin = builtin_invalid; - this->line = line; - this->column = column; -} - -void Name::init(Builtin builtin, quint32 line, quint32 column) -{ - this->id = 0; - this->builtin = builtin; - this->line = line; - this->column = column; -} - -static const char *builtin_to_string(Name::Builtin b) -{ - switch (b) { - case Name::builtin_invalid: - return "builtin_invalid"; - case Name::builtin_typeof: - return "builtin_typeof"; - case Name::builtin_delete: - return "builtin_delete"; - case Name::builtin_postincrement: - return "builtin_postincrement"; - case Name::builtin_postdecrement: - return "builtin_postdecrement"; - case Name::builtin_throw: - return "builtin_throw"; - case Name::builtin_create_exception_handler: - return "builtin_create_exception_handler"; - case Name::builtin_finish_try: - return "builtin_finish_try"; - case Name::builtin_get_exception: - return "builtin_get_exception"; - case IR::Name::builtin_foreach_iterator_object: - return "builtin_foreach_iterator_object"; - case IR::Name::builtin_foreach_next_property_name: - return "builtin_foreach_next_property_name"; - case IR::Name::builtin_push_with_scope: - return "builtin_push_with_scope"; - case IR::Name::builtin_push_catch_scope: - return "builtin_push_catch_scope"; - case IR::Name::builtin_pop_scope: - return "builtin_pop_scope"; - case IR::Name::builtin_declare_vars: - return "builtin_declare_vars"; - case IR::Name::builtin_define_property: - return "builtin_define_property"; - case IR::Name::builtin_define_array: - return "builtin_define_array"; - case IR::Name::builtin_define_getter_setter: - return "builtin_define_getter_setter"; - } - return "builtin_(###FIXME)"; -}; - - -void Name::dump(QTextStream &out) -{ - if (id) - out << *id; - else - out << builtin_to_string(builtin); -} - -void Temp::dump(QTextStream &out) -{ - if (index < 0) { - out << '#' << -(index + 1); // negative and 1-based. - } else { - out << '%' << index; // temp - } - if (scope) - out << "@" << scope; -} - -void Closure::dump(QTextStream &out) -{ - QString name = value->name ? *value->name : QString(); - if (name.isEmpty()) - name.sprintf("%p", value); - out << "closure(" << name << ')'; -} - -void Unop::dump(QTextStream &out) -{ - out << opname(op); - expr->dump(out); -} - -void Binop::dump(QTextStream &out) -{ - left->dump(out); - out << ' ' << opname(op) << ' '; - right->dump(out); -} - -void Call::dump(QTextStream &out) -{ - base->dump(out); - out << '('; - for (ExprList *it = args; it; it = it->next) { - if (it != args) - out << ", "; - it->expr->dump(out); - } - out << ')'; -} - -void New::dump(QTextStream &out) -{ - out << "new "; - base->dump(out); - out << '('; - for (ExprList *it = args; it; it = it->next) { - if (it != args) - out << ", "; - it->expr->dump(out); - } - out << ')'; -} - -void Subscript::dump(QTextStream &out) -{ - base->dump(out); - out << '['; - index->dump(out); - out << ']'; -} - -void Member::dump(QTextStream &out) -{ - base->dump(out); - out << '.' << *name; -} - -void Exp::dump(QTextStream &out, Mode) -{ - out << "(void) "; - expr->dump(out); - out << ';'; -} - -void Enter::dump(QTextStream &out, Mode) -{ - out << "%enter("; - expr->dump(out); - out << ");"; -} - -void Leave::dump(QTextStream &out, Mode) -{ - out << "%leave"; - out << ';'; -} - -void Move::dump(QTextStream &out, Mode) -{ - target->dump(out); - out << ' '; - if (op != OpInvalid) - out << opname(op); - out << "= "; -// if (source->type != target->type) -// out << typeName(source->type) << "_to_" << typeName(target->type) << '('; - source->dump(out); -// if (source->type != target->type) -// out << ')'; - out << ';'; -} - -void Jump::dump(QTextStream &out, Mode mode) -{ - Q_UNUSED(mode); - out << "goto " << 'L' << target->index << ';'; -} - -void CJump::dump(QTextStream &out, Mode mode) -{ - Q_UNUSED(mode); - out << "if ("; - cond->dump(out); - if (mode == HIR) - out << ") goto " << 'L' << iftrue->index << "; else goto " << 'L' << iffalse->index << ';'; - else - out << ") goto " << 'L' << iftrue->index << ";"; -} - -void Ret::dump(QTextStream &out, Mode) -{ - out << "return"; - if (expr) { - out << ' '; - expr->dump(out); - } - out << ';'; -} - -Function *Module::newFunction(const QString &name, Function *outer) -{ - Function *f = new Function(this, outer, name); - functions.append(f); - if (!outer) { - assert(!rootFunction); - rootFunction = f; - } else { - outer->nestedFunctions.append(f); - } - return f; -} - -Module::~Module() -{ - foreach (Function *f, functions) { - delete f; - } -} - -Function::~Function() -{ - // destroy the Stmt::Data blocks manually, because memory pool cleanup won't - // call the Stmt destructors. - foreach (IR::BasicBlock *b, basicBlocks) - foreach (IR::Stmt *s, b->statements) - s->destroyData(); - - qDeleteAll(basicBlocks); - pool = 0; - module = 0; -} - - -const QString *Function::newString(const QString &text) -{ - return &*strings.insert(text); -} - -BasicBlock *Function::newBasicBlock(BasicBlockInsertMode mode) -{ - BasicBlock *block = new BasicBlock(this); - return mode == InsertBlock ? insertBasicBlock(block) : block; -} - -void Function::dump(QTextStream &out, Stmt::Mode mode) -{ - QString n = name ? *name : QString(); - if (n.isEmpty()) - n.sprintf("%p", this); - out << "function " << n << "() {" << endl; - foreach (const QString *formal, formals) - out << "\treceive " << *formal << ';' << endl; - foreach (const QString *local, locals) - out << "\tlocal " << *local << ';' << endl; - foreach (BasicBlock *bb, basicBlocks) - bb->dump(out, mode); - out << '}' << endl; -} - -void Function::removeSharedExpressions() -{ - RemoveSharedExpressions removeSharedExpressions; - removeSharedExpressions(this); -} - -unsigned BasicBlock::newTemp() -{ - return function->tempCount++; -} - -Temp *BasicBlock::TEMP(int index, uint scope) -{ - Temp *e = function->New(); - e->init(index, scope); - return e; -} - -Expr *BasicBlock::CONST(Type type, double value) -{ - Const *e = function->New(); - e->init(type, value); - return e; -} - -Expr *BasicBlock::STRING(const QString *value) -{ - String *e = function->New(); - e->init(value); - return e; -} - -Expr *BasicBlock::REGEXP(const QString *value, int flags) -{ - RegExp *e = function->New(); - e->init(value, flags); - return e; -} - -Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) -{ - Name *e = function->New(); - e->init(function->newString(id), line, column); - return e; -} - -Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column) -{ - Name *e = function->New(); - e->init(builtin, line, column); - return e; -} - -Closure *BasicBlock::CLOSURE(Function *function) -{ - Closure *clos = function->New(); - clos->init(function); - return clos; -} - -Expr *BasicBlock::UNOP(AluOp op, Temp *expr) -{ - Unop *e = function->New(); - e->init(op, expr); - return e; -} - -Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) -{ - Binop *e = function->New(); - e->init(op, left, right); - return e; -} - -Expr *BasicBlock::CALL(Expr *base, ExprList *args) -{ - Call *e = function->New(); - e->init(base, args); - return e; -} - -Expr *BasicBlock::NEW(Expr *base, ExprList *args) -{ - New *e = function->New(); - e->init(base, args); - return e; -} - -Expr *BasicBlock::SUBSCRIPT(Temp *base, Temp *index) -{ - Subscript *e = function->New(); - e->init(base, index); - return e; -} - -Expr *BasicBlock::MEMBER(Temp *base, const QString *name) -{ - Member*e = function->New(); - e->init(base, name); - return e; -} - -Stmt *BasicBlock::EXP(Expr *expr) -{ - if (isTerminated()) - return 0; - - Exp *s = function->New(); - s->init(expr); - statements.append(s); - return s; -} - -Stmt *BasicBlock::ENTER(Expr *expr) -{ - if (isTerminated()) - return 0; - - Enter *s = function->New(); - s->init(expr); - statements.append(s); - return s; -} - -Stmt *BasicBlock::LEAVE() -{ - if (isTerminated()) - return 0; - - Leave *s = function->New(); - s->init(); - statements.append(s); - return s; -} - -Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op) -{ - if (isTerminated()) - return 0; - - Move *s = function->New(); - s->init(target, source, op); - statements.append(s); - return s; -} - -Stmt *BasicBlock::JUMP(BasicBlock *target) -{ - if (isTerminated()) - return 0; - - Jump *s = function->New(); - s->init(target); - statements.append(s); - - assert(! out.contains(target)); - out.append(target); - - assert(! target->in.contains(this)); - target->in.append(this); - - return s; -} - -Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) -{ - if (isTerminated()) - return 0; - - if (iftrue == iffalse) { - MOVE(TEMP(newTemp()), cond); - return JUMP(iftrue); - } - - CJump *s = function->New(); - s->init(cond, iftrue, iffalse); - statements.append(s); - - assert(! out.contains(iftrue)); - out.append(iftrue); - - assert(! iftrue->in.contains(this)); - iftrue->in.append(this); - - assert(! out.contains(iffalse)); - out.append(iffalse); - - assert(! iffalse->in.contains(this)); - iffalse->in.append(this); - - return s; -} - -Stmt *BasicBlock::RET(Temp *expr) -{ - if (isTerminated()) - return 0; - - Ret *s = function->New(); - s->init(expr); - statements.append(s); - return s; -} - -void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) -{ - out << 'L' << index << ':' << endl; - foreach (Stmt *s, statements) { - out << '\t'; - s->dump(out, mode); - out << endl; - } -} - -CloneExpr::CloneExpr(BasicBlock *block) - : block(block), cloned(0) -{ -} - -void CloneExpr::setBasicBlock(BasicBlock *block) -{ - this->block = block; -} - -ExprList *CloneExpr::clone(ExprList *list) -{ - if (! list) - return 0; - - ExprList *clonedList = block->function->New(); - clonedList->init(clone(list->expr), clone(list->next)); - return clonedList; -} - -void CloneExpr::visitConst(Const *e) -{ - cloned = block->CONST(e->type, e->value); -} - -void CloneExpr::visitString(String *e) -{ - cloned = block->STRING(e->value); -} - -void CloneExpr::visitRegExp(RegExp *e) -{ - cloned = block->REGEXP(e->value, e->flags); -} - -void CloneExpr::visitName(Name *e) -{ - if (e->id) - cloned = block->NAME(*e->id, e->line, e->column); - else - cloned = block->NAME(e->builtin, e->line, e->column); -} - -void CloneExpr::visitTemp(Temp *e) -{ - cloned = block->TEMP(e->index, e->scope); -} - -void CloneExpr::visitClosure(Closure *e) -{ - cloned = block->CLOSURE(e->value); -} - -void CloneExpr::visitUnop(Unop *e) -{ - cloned = block->UNOP(e->op, clone(e->expr)); -} - -void CloneExpr::visitBinop(Binop *e) -{ - cloned = block->BINOP(e->op, clone(e->left), clone(e->right)); -} - -void CloneExpr::visitCall(Call *e) -{ - cloned = block->CALL(clone(e->base), clone(e->args)); -} - -void CloneExpr::visitNew(New *e) -{ - cloned = block->NEW(clone(e->base), clone(e->args)); -} - -void CloneExpr::visitSubscript(Subscript *e) -{ - cloned = block->SUBSCRIPT(clone(e->base), clone(e->index)); -} - -void CloneExpr::visitMember(Member *e) -{ - cloned = block->MEMBER(clone(e->base), e->name); -} - -} // end of namespace IR -} // end of namespace QQmlJS - -QT_END_NAMESPACE diff --git a/src/v4/qv4ir_p.h b/src/v4/qv4ir_p.h deleted file mode 100644 index 50a5247277..0000000000 --- a/src/v4/qv4ir_p.h +++ /dev/null @@ -1,786 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the V4VM module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4IR_P_H -#define QV4IR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qv4global.h" -#include - -#include -#include -#include - -#ifdef CONST -#undef CONST -#endif - -QT_BEGIN_NAMESPACE - -class QTextStream; -class QQmlType; - -namespace QQmlJS { - -inline bool isNegative(double d) -{ - uchar *dch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - return (dch[0] & 0x80); - else - return (dch[7] & 0x80); - -} - -namespace VM { -struct ExecutionContext; -struct Value; -} - -namespace IR { - -struct BasicBlock; -struct Function; -struct Module; - -struct Stmt; -struct Expr; - -// expressions -struct Const; -struct String; -struct RegExp; -struct Name; -struct Temp; -struct Closure; -struct Unop; -struct Binop; -struct Call; -struct New; -struct Subscript; -struct Member; - -// statements -struct Exp; -struct Enter; -struct Leave; -struct Move; -struct Jump; -struct CJump; -struct Ret; - -enum AluOp { - OpInvalid = 0, - - OpIfTrue, - OpNot, - OpUMinus, - OpUPlus, - OpCompl, - OpIncrement, - OpDecrement, - - OpBitAnd, - OpBitOr, - OpBitXor, - - OpAdd, - OpSub, - OpMul, - OpDiv, - OpMod, - - OpLShift, - OpRShift, - OpURShift, - - OpGt, - OpLt, - OpGe, - OpLe, - OpEqual, - OpNotEqual, - OpStrictEqual, - OpStrictNotEqual, - - OpInstanceof, - OpIn, - - OpAnd, - OpOr, - - LastAluOp = OpOr -}; -AluOp binaryOperator(int op); -const char *opname(IR::AluOp op); - -enum Type { - MissingType, // Used to indicate holes in array initialization (e.g. [,,]) - UndefinedType, - NullType, - BoolType, - NumberType -}; - -struct ExprVisitor { - virtual ~ExprVisitor() {} - virtual void visitConst(Const *) = 0; - virtual void visitString(String *) = 0; - virtual void visitRegExp(RegExp *) = 0; - virtual void visitName(Name *) = 0; - virtual void visitTemp(Temp *) = 0; - virtual void visitClosure(Closure *) = 0; - virtual void visitUnop(Unop *) = 0; - virtual void visitBinop(Binop *) = 0; - virtual void visitCall(Call *) = 0; - virtual void visitNew(New *) = 0; - virtual void visitSubscript(Subscript *) = 0; - virtual void visitMember(Member *) = 0; -}; - -struct StmtVisitor { - virtual ~StmtVisitor() {} - virtual void visitExp(Exp *) = 0; - virtual void visitEnter(Enter *) = 0; - virtual void visitLeave(Leave *) = 0; - virtual void visitMove(Move *) = 0; - virtual void visitJump(Jump *) = 0; - virtual void visitCJump(CJump *) = 0; - virtual void visitRet(Ret *) = 0; -}; - -struct Expr { - virtual ~Expr() {} - virtual void accept(ExprVisitor *) = 0; - virtual bool isLValue() { return false; } - virtual Const *asConst() { return 0; } - virtual String *asString() { return 0; } - virtual RegExp *asRegExp() { return 0; } - virtual Name *asName() { return 0; } - virtual Temp *asTemp() { return 0; } - virtual Closure *asClosure() { return 0; } - virtual Unop *asUnop() { return 0; } - virtual Binop *asBinop() { return 0; } - virtual Call *asCall() { return 0; } - virtual New *asNew() { return 0; } - virtual Subscript *asSubscript() { return 0; } - virtual Member *asMember() { return 0; } - virtual void dump(QTextStream &out) = 0; -}; - -struct ExprList { - Expr *expr; - ExprList *next; - - void init(Expr *expr, ExprList *next = 0) - { - this->expr = expr; - this->next = next; - } -}; - -struct Const: Expr { - Type type; - double value; - - void init(Type type, double value) - { - this->type = type; - this->value = value; - } - - virtual void accept(ExprVisitor *v) { v->visitConst(this); } - virtual Const *asConst() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct String: Expr { - const QString *value; - - void init(const QString *value) - { - this->value = value; - } - - virtual void accept(ExprVisitor *v) { v->visitString(this); } - virtual String *asString() { return this; } - - virtual void dump(QTextStream &out); - static QString escape(const QString &s); -}; - -struct RegExp: Expr { - // needs to be compatible with the flags in the lexer - enum Flags { - RegExp_Global = 0x01, - RegExp_IgnoreCase = 0x02, - RegExp_Multiline = 0x04 - }; - - const QString *value; - int flags; - - void init(const QString *value, int flags) - { - this->value = value; - this->flags = flags; - } - - virtual void accept(ExprVisitor *v) { v->visitRegExp(this); } - virtual RegExp *asRegExp() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Name: Expr { - enum Builtin { - builtin_invalid, - builtin_typeof, - builtin_delete, - builtin_postincrement, - builtin_postdecrement, - builtin_throw, - builtin_create_exception_handler, - builtin_finish_try, - builtin_get_exception, - builtin_foreach_iterator_object, - builtin_foreach_next_property_name, - builtin_push_with_scope, - builtin_push_catch_scope, - builtin_pop_scope, - builtin_declare_vars, - builtin_define_property, - builtin_define_array, - builtin_define_getter_setter - }; - - const QString *id; - Builtin builtin; - quint32 line; - quint32 column; - - void init(const QString *id, quint32 line, quint32 column); - void init(Builtin builtin, quint32 line, quint32 column); - - virtual void accept(ExprVisitor *v) { v->visitName(this); } - virtual bool isLValue() { return true; } - virtual Name *asName() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Temp: Expr { - int index; - int scope; // how many scopes outside the current one? - - void init(int index, int scope = 0) - { - this->index = index; - this->scope = scope; - } - - virtual void accept(ExprVisitor *v) { v->visitTemp(this); } - virtual bool isLValue() { return true; } - virtual Temp *asTemp() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Closure: Expr { - Function *value; - - void init(Function *value) - { - this->value = value; - } - - virtual void accept(ExprVisitor *v) { v->visitClosure(this); } - virtual Closure *asClosure() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Unop: Expr { - AluOp op; - Temp *expr; - - void init(AluOp op, Temp *expr) - { - this->op = op; - this->expr = expr; - } - - virtual void accept(ExprVisitor *v) { v->visitUnop(this); } - virtual Unop *asUnop() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Binop: Expr { - AluOp op; - Expr *left; // Temp or Const - Expr *right; // Temp or Const - - void init(AluOp op, Expr *left, Expr *right) - { - this->op = op; - this->left = left; - this->right = right; - } - - virtual void accept(ExprVisitor *v) { v->visitBinop(this); } - virtual Binop *asBinop() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Call: Expr { - Expr *base; // Name, Member, Temp - ExprList *args; // List of Temps - - void init(Expr *base, ExprList *args) - { - this->base = base; - this->args = args; - } - - Expr *onlyArgument() const { - if (args && ! args->next) - return args->expr; - return 0; - } - - virtual void accept(ExprVisitor *v) { v->visitCall(this); } - virtual Call *asCall() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct New: Expr { - Expr *base; // Name, Member, Temp - ExprList *args; // List of Temps - - void init(Expr *base, ExprList *args) - { - this->base = base; - this->args = args; - } - - Expr *onlyArgument() const { - if (args && ! args->next) - return args->expr; - return 0; - } - - virtual void accept(ExprVisitor *v) { v->visitNew(this); } - virtual New *asNew() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Subscript: Expr { - Temp *base; - Temp *index; - - void init(Temp *base, Temp *index) - { - this->base = base; - this->index = index; - } - - virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } - virtual bool isLValue() { return true; } - virtual Subscript *asSubscript() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Member: Expr { - Temp *base; - const QString *name; - - void init(Temp *base, const QString *name) - { - this->base = base; - this->name = name; - } - - virtual void accept(ExprVisitor *v) { v->visitMember(this); } - virtual bool isLValue() { return true; } - virtual Member *asMember() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Stmt { - enum Mode { - HIR, - MIR - }; - - struct Data { - QVector uses; - QVector defs; - QBitArray liveIn; - QBitArray liveOut; - }; - - Data *d; - - Stmt(): d(0) {} - virtual ~Stmt() { Q_UNREACHABLE(); } - virtual Stmt *asTerminator() { return 0; } - - virtual void accept(StmtVisitor *) = 0; - virtual Exp *asExp() { return 0; } - virtual Move *asMove() { return 0; } - virtual Enter *asEnter() { return 0; } - virtual Leave *asLeave() { return 0; } - virtual Jump *asJump() { return 0; } - virtual CJump *asCJump() { return 0; } - virtual Ret *asRet() { return 0; } - virtual void dump(QTextStream &out, Mode mode = HIR) = 0; - - void destroyData() { - delete d; - d = 0; - } -}; - -struct Exp: Stmt { - Expr *expr; - - void init(Expr *expr) - { - this->expr = expr; - } - - virtual void accept(StmtVisitor *v) { v->visitExp(this); } - virtual Exp *asExp() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Move: Stmt { - Expr *target; // LHS - Temp, Name, Member or Subscript - Expr *source; - AluOp op; - - void init(Expr *target, Expr *source, AluOp op) - { - this->target = target; - this->source = source; - this->op = op; - } - - virtual void accept(StmtVisitor *v) { v->visitMove(this); } - virtual Move *asMove() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Enter: Stmt { - Expr *expr; - - void init(Expr *expr) - { - this->expr = expr; - } - - virtual void accept(StmtVisitor *v) { v->visitEnter(this); } - virtual Enter *asEnter() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Leave: Stmt { - void init() {} - - virtual void accept(StmtVisitor *v) { v->visitLeave(this); } - virtual Leave *asLeave() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Jump: Stmt { - BasicBlock *target; - - void init(BasicBlock *target) - { - this->target = target; - } - - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitJump(this); } - virtual Jump *asJump() { return this; } - - virtual void dump(QTextStream &out, Mode mode); -}; - -struct CJump: Stmt { - Expr *cond; // Temp, Binop - BasicBlock *iftrue; - BasicBlock *iffalse; - - void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) - { - this->cond = cond; - this->iftrue = iftrue; - this->iffalse = iffalse; - } - - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitCJump(this); } - virtual CJump *asCJump() { return this; } - - virtual void dump(QTextStream &out, Mode mode); -}; - -struct Ret: Stmt { - Temp *expr; - - void init(Temp *expr) - { - this->expr = expr; - } - - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitRet(this); } - virtual Ret *asRet() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Q_V4_EXPORT Module { - MemoryPool pool; - QVector functions; - Function *rootFunction; - - Function *newFunction(const QString &name, Function *outer); - - Module() : rootFunction(0) {} - ~Module(); -}; - -struct Function { - Module *module; - MemoryPool *pool; - const QString *name; - QVector basicBlocks; - int tempCount; - int maxNumberOfArguments; - QSet strings; - QList formals; - QList locals; - QVector nestedFunctions; - Function *outer; - - int insideWithOrCatch; - - uint hasDirectEval: 1; - uint usesArgumentsObject : 1; - uint isStrict: 1; - uint unused : 29; - - template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } - - Function(Module *module, Function *outer, const QString &name) - : module(module) - , pool(&module->pool) - , tempCount(0) - , maxNumberOfArguments(0) - , outer(outer) - , insideWithOrCatch(0) - , hasDirectEval(false) - , usesArgumentsObject(false) - , isStrict(false) - , unused(0) - { this->name = newString(name); } - - ~Function(); - - enum BasicBlockInsertMode { - InsertBlock, - DontInsertBlock - }; - - BasicBlock *newBasicBlock(BasicBlockInsertMode mode = InsertBlock); - const QString *newString(const QString &text); - - void RECEIVE(const QString &name) { formals.append(newString(name)); } - void LOCAL(const QString &name) { locals.append(newString(name)); } - - inline BasicBlock *insertBasicBlock(BasicBlock *block) { basicBlocks.append(block); return block; } - - void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); - - void removeSharedExpressions(); -}; - -struct BasicBlock { - Function *function; - QVector statements; - QVector in; - QVector out; - QBitArray liveIn; - QBitArray liveOut; - int index; - int offset; - - BasicBlock(Function *function): function(function), index(-1), offset(-1) {} - ~BasicBlock() {} - - template inline Instr i(Instr i) { statements.append(i); return i; } - - inline bool isEmpty() const { - return statements.isEmpty(); - } - - inline Stmt *terminator() const { - if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0) - return statements.at(statements.size() - 1); - return 0; - } - - inline bool isTerminated() const { - if (terminator() != 0) - return true; - return false; - } - - unsigned newTemp(); - - Temp *TEMP(int index, uint scope = 0); - - Expr *CONST(Type type, double value); - Expr *STRING(const QString *value); - Expr *REGEXP(const QString *value, int flags); - - Name *NAME(const QString &id, quint32 line, quint32 column); - Name *NAME(Name::Builtin builtin, quint32 line, quint32 column); - - Closure *CLOSURE(Function *function); - - Expr *UNOP(AluOp op, Temp *expr); - Expr *BINOP(AluOp op, Expr *left, Expr *right); - Expr *CALL(Expr *base, ExprList *args = 0); - Expr *NEW(Expr *base, ExprList *args = 0); - Expr *SUBSCRIPT(Temp *base, Temp *index); - Expr *MEMBER(Temp *base, const QString *name); - - Stmt *EXP(Expr *expr); - Stmt *ENTER(Expr *expr); - Stmt *LEAVE(); - - Stmt *MOVE(Expr *target, Expr *source, AluOp op = IR::OpInvalid); - - Stmt *JUMP(BasicBlock *target); - Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); - Stmt *RET(Temp *expr); - - void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); -}; - -class CloneExpr: protected IR::ExprVisitor -{ -public: - explicit CloneExpr(IR::BasicBlock *block = 0); - - void setBasicBlock(IR::BasicBlock *block); - - template - _Expr *operator()(_Expr *expr) - { - return clone(expr); - } - - template - _Expr *clone(_Expr *expr) - { - Expr *c = expr; - qSwap(cloned, c); - expr->accept(this); - qSwap(cloned, c); - return static_cast<_Expr *>(c); - } - -protected: - IR::ExprList *clone(IR::ExprList *list); - - virtual void visitConst(Const *); - virtual void visitString(String *); - virtual void visitRegExp(RegExp *); - virtual void visitName(Name *); - virtual void visitTemp(Temp *); - virtual void visitClosure(Closure *); - virtual void visitUnop(Unop *); - virtual void visitBinop(Binop *); - virtual void visitCall(Call *); - virtual void visitNew(New *); - virtual void visitSubscript(Subscript *); - virtual void visitMember(Member *); - -private: - IR::BasicBlock *block; - IR::Expr *cloned; -}; - -} // end of namespace IR - -} // end of namespace QQmlJS - -QT_END_NAMESPACE - -#endif // QV4IR_P_H diff --git a/src/v4/qv4isel_llvm.cpp b/src/v4/qv4isel_llvm.cpp index f0b8526fea..2dee4a8aa0 100644 --- a/src/v4/qv4isel_llvm.cpp +++ b/src/v4/qv4isel_llvm.cpp @@ -78,7 +78,7 @@ // with very unfriendly names that collide with class fields in LLVM. #include "qv4isel_llvm_p.h" #include "qv4_llvm_p.h" -#include "qv4ir_p.h" +#include "qv4jsir_p.h" #include "qv4string.h" #include "qv4global.h" diff --git a/src/v4/qv4isel_llvm_p.h b/src/v4/qv4isel_llvm_p.h index e749863605..0f34547aa3 100644 --- a/src/v4/qv4isel_llvm_p.h +++ b/src/v4/qv4isel_llvm_p.h @@ -55,7 +55,7 @@ #endif // __clang__ #include "qv4isel_p.h" -#include "qv4ir_p.h" +#include "qv4jsir_p.h" namespace QQmlJS { namespace LLVM { diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h index acd6e5f662..0d3a5a154b 100644 --- a/src/v4/qv4isel_masm_p.h +++ b/src/v4/qv4isel_masm_p.h @@ -42,7 +42,7 @@ #define QV4ISEL_MASM_P_H #include "qv4global.h" -#include "qv4ir_p.h" +#include "qv4jsir_p.h" #include "qv4isel_p.h" #include "qv4isel_util_p.h" #include "qv4object.h" diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp index cbf2e4cf69..49cc3b3334 100644 --- a/src/v4/qv4isel_p.cpp +++ b/src/v4/qv4isel_p.cpp @@ -1,6 +1,6 @@ #include "debugging.h" #include "qmljs_engine.h" -#include "qv4ir_p.h" +#include "qv4jsir_p.h" #include "qv4isel_p.h" #include "qv4isel_util_p.h" #include "qv4functionobject.h" diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h index cc1ebf08da..7fabf3a2fe 100644 --- a/src/v4/qv4isel_p.h +++ b/src/v4/qv4isel_p.h @@ -31,7 +31,7 @@ #define QV4ISEL_P_H #include "qv4global.h" -#include "qv4ir_p.h" +#include "qv4jsir_p.h" #include #include diff --git a/src/v4/qv4isel_util_p.h b/src/v4/qv4isel_util_p.h index 4a928ad11f..495371a601 100644 --- a/src/v4/qv4isel_util_p.h +++ b/src/v4/qv4isel_util_p.h @@ -31,7 +31,7 @@ #define QV4ISEL_UTIL_P_H #include "qmljs_runtime.h" -#include "qv4ir_p.h" +#include "qv4jsir_p.h" namespace QQmlJS { diff --git a/src/v4/qv4jsir.cpp b/src/v4/qv4jsir.cpp new file mode 100644 index 0000000000..fbac69eee1 --- /dev/null +++ b/src/v4/qv4jsir.cpp @@ -0,0 +1,885 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4jsir_p.h" +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace IR { + +const char *typeName(Type t) +{ + switch (t) { + case UndefinedType: return "undefined"; + case NullType: return "null"; + case BoolType: return "bool"; + case NumberType: return "number"; + default: return "invalid"; + } +} + +const char *opname(AluOp op) +{ + switch (op) { + case OpInvalid: return "?"; + + case OpIfTrue: return "(bool)"; + case OpNot: return "!"; + case OpUMinus: return "-"; + case OpUPlus: return "+"; + case OpCompl: return "~"; + case OpIncrement: return "++"; + case OpDecrement: return "--"; + + case OpBitAnd: return "&"; + case OpBitOr: return "|"; + case OpBitXor: return "^"; + + case OpAdd: return "+"; + case OpSub: return "-"; + case OpMul: return "*"; + case OpDiv: return "/"; + case OpMod: return "%"; + + case OpLShift: return "<<"; + case OpRShift: return ">>"; + case OpURShift: return ">>>"; + + case OpGt: return ">"; + case OpLt: return "<"; + case OpGe: return ">="; + case OpLe: return "<="; + case OpEqual: return "=="; + case OpNotEqual: return "!="; + case OpStrictEqual: return "==="; + case OpStrictNotEqual: return "!=="; + + case OpInstanceof: return "instanceof"; + case OpIn: return "in"; + + case OpAnd: return "&&"; + case OpOr: return "||"; + + default: return "?"; + + } // switch +} + +AluOp binaryOperator(int op) +{ + switch (static_cast(op)) { + case QSOperator::Add: return OpAdd; + case QSOperator::And: return OpAnd; + case QSOperator::BitAnd: return OpBitAnd; + case QSOperator::BitOr: return OpBitOr; + case QSOperator::BitXor: return OpBitXor; + case QSOperator::Div: return OpDiv; + case QSOperator::Equal: return OpEqual; + case QSOperator::Ge: return OpGe; + case QSOperator::Gt: return OpGt; + case QSOperator::Le: return OpLe; + case QSOperator::LShift: return OpLShift; + case QSOperator::Lt: return OpLt; + case QSOperator::Mod: return OpMod; + case QSOperator::Mul: return OpMul; + case QSOperator::NotEqual: return OpNotEqual; + case QSOperator::Or: return OpOr; + case QSOperator::RShift: return OpRShift; + case QSOperator::StrictEqual: return OpStrictEqual; + case QSOperator::StrictNotEqual: return OpStrictNotEqual; + case QSOperator::Sub: return OpSub; + case QSOperator::URShift: return OpURShift; + case QSOperator::InstanceOf: return OpInstanceof; + case QSOperator::In: return OpIn; + default: return OpInvalid; + } +} + +struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor +{ + CloneExpr clone; + QSet subexpressions; // contains all the non-cloned subexpressions in the given function + Expr *uniqueExpr; + + RemoveSharedExpressions(): uniqueExpr(0) {} + + void operator()(IR::Function *function) + { + subexpressions.clear(); + + foreach (BasicBlock *block, function->basicBlocks) { + clone.setBasicBlock(block); + + foreach (Stmt *s, block->statements) { + s->accept(this); + } + } + } + + template + _Expr *cleanup(_Expr *expr) + { + if (subexpressions.contains(expr)) { + // the cloned expression is unique by definition + // so we don't need to add it to `subexpressions'. + return clone(expr); + } + + subexpressions.insert(expr); + IR::Expr *e = expr; + qSwap(uniqueExpr, e); + expr->accept(this); + qSwap(uniqueExpr, e); + return static_cast<_Expr *>(e); + } + + // statements + virtual void visitExp(Exp *s) + { + s->expr = cleanup(s->expr); + } + + virtual void visitEnter(Enter *s) + { + s->expr = cleanup(s->expr); + } + + virtual void visitLeave(Leave *) + { + // nothing to do for Leave statements + } + + virtual void visitMove(Move *s) + { + s->target = cleanup(s->target); + s->source = cleanup(s->source); + } + + virtual void visitJump(Jump *) + { + // nothing to do for Jump statements + } + + virtual void visitCJump(CJump *s) + { + s->cond = cleanup(s->cond); + } + + virtual void visitRet(Ret *s) + { + s->expr = cleanup(s->expr); + } + + + // expressions + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *) {} + virtual void visitClosure(Closure *) {} + + virtual void visitUnop(Unop *e) + { + e->expr = cleanup(e->expr); + } + + virtual void visitBinop(Binop *e) + { + e->left = cleanup(e->left); + e->right = cleanup(e->right); + } + + virtual void visitCall(Call *e) + { + e->base = cleanup(e->base); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr = cleanup(it->expr); + } + + virtual void visitNew(New *e) + { + e->base = cleanup(e->base); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr = cleanup(it->expr); + } + + virtual void visitSubscript(Subscript *e) + { + e->base = cleanup(e->base); + e->index = cleanup(e->index); + } + + virtual void visitMember(Member *e) + { + e->base = cleanup(e->base); + } +}; + +void Const::dump(QTextStream &out) +{ + switch (type) { + case QQmlJS::IR::UndefinedType: + out << "undefined"; + break; + case QQmlJS::IR::NullType: + out << "null"; + break; + case QQmlJS::IR::BoolType: + out << (value ? "true" : "false"); + break; + default: + out << QString::number(value, 'g', 16); + break; + } +} + +void String::dump(QTextStream &out) +{ + out << '"' << escape(*value) << '"'; +} + +QString String::escape(const QString &s) +{ + QString r; + for (int i = 0; i < s.length(); ++i) { + const QChar ch = s.at(i); + if (ch == QLatin1Char('\n')) + r += QStringLiteral("\\n"); + else if (ch == QLatin1Char('\r')) + r += QStringLiteral("\\r"); + else if (ch == QLatin1Char('\\')) + r += QStringLiteral("\\\\"); + else if (ch == QLatin1Char('"')) + r += QStringLiteral("\\\""); + else if (ch == QLatin1Char('\'')) + r += QStringLiteral("\\'"); + else + r += ch; + } + return r; +} + +void RegExp::dump(QTextStream &out) +{ + char f[3]; + int i = 0; + if (flags & RegExp_Global) + f[i++] = 'g'; + if (flags & RegExp_IgnoreCase) + f[i++] = 'i'; + if (flags & RegExp_Multiline) + f[i++] = 'm'; + f[i] = 0; + + out << '/' << *value << '/' << f; +} + +void Name::init(const QString *id, quint32 line, quint32 column) +{ + this->id = id; + this->builtin = builtin_invalid; + this->line = line; + this->column = column; +} + +void Name::init(Builtin builtin, quint32 line, quint32 column) +{ + this->id = 0; + this->builtin = builtin; + this->line = line; + this->column = column; +} + +static const char *builtin_to_string(Name::Builtin b) +{ + switch (b) { + case Name::builtin_invalid: + return "builtin_invalid"; + case Name::builtin_typeof: + return "builtin_typeof"; + case Name::builtin_delete: + return "builtin_delete"; + case Name::builtin_postincrement: + return "builtin_postincrement"; + case Name::builtin_postdecrement: + return "builtin_postdecrement"; + case Name::builtin_throw: + return "builtin_throw"; + case Name::builtin_create_exception_handler: + return "builtin_create_exception_handler"; + case Name::builtin_finish_try: + return "builtin_finish_try"; + case Name::builtin_get_exception: + return "builtin_get_exception"; + case IR::Name::builtin_foreach_iterator_object: + return "builtin_foreach_iterator_object"; + case IR::Name::builtin_foreach_next_property_name: + return "builtin_foreach_next_property_name"; + case IR::Name::builtin_push_with_scope: + return "builtin_push_with_scope"; + case IR::Name::builtin_push_catch_scope: + return "builtin_push_catch_scope"; + case IR::Name::builtin_pop_scope: + return "builtin_pop_scope"; + case IR::Name::builtin_declare_vars: + return "builtin_declare_vars"; + case IR::Name::builtin_define_property: + return "builtin_define_property"; + case IR::Name::builtin_define_array: + return "builtin_define_array"; + case IR::Name::builtin_define_getter_setter: + return "builtin_define_getter_setter"; + } + return "builtin_(###FIXME)"; +}; + + +void Name::dump(QTextStream &out) +{ + if (id) + out << *id; + else + out << builtin_to_string(builtin); +} + +void Temp::dump(QTextStream &out) +{ + if (index < 0) { + out << '#' << -(index + 1); // negative and 1-based. + } else { + out << '%' << index; // temp + } + if (scope) + out << "@" << scope; +} + +void Closure::dump(QTextStream &out) +{ + QString name = value->name ? *value->name : QString(); + if (name.isEmpty()) + name.sprintf("%p", value); + out << "closure(" << name << ')'; +} + +void Unop::dump(QTextStream &out) +{ + out << opname(op); + expr->dump(out); +} + +void Binop::dump(QTextStream &out) +{ + left->dump(out); + out << ' ' << opname(op) << ' '; + right->dump(out); +} + +void Call::dump(QTextStream &out) +{ + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +void New::dump(QTextStream &out) +{ + out << "new "; + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +void Subscript::dump(QTextStream &out) +{ + base->dump(out); + out << '['; + index->dump(out); + out << ']'; +} + +void Member::dump(QTextStream &out) +{ + base->dump(out); + out << '.' << *name; +} + +void Exp::dump(QTextStream &out, Mode) +{ + out << "(void) "; + expr->dump(out); + out << ';'; +} + +void Enter::dump(QTextStream &out, Mode) +{ + out << "%enter("; + expr->dump(out); + out << ");"; +} + +void Leave::dump(QTextStream &out, Mode) +{ + out << "%leave"; + out << ';'; +} + +void Move::dump(QTextStream &out, Mode) +{ + target->dump(out); + out << ' '; + if (op != OpInvalid) + out << opname(op); + out << "= "; +// if (source->type != target->type) +// out << typeName(source->type) << "_to_" << typeName(target->type) << '('; + source->dump(out); +// if (source->type != target->type) +// out << ')'; + out << ';'; +} + +void Jump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "goto " << 'L' << target->index << ';'; +} + +void CJump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "if ("; + cond->dump(out); + if (mode == HIR) + out << ") goto " << 'L' << iftrue->index << "; else goto " << 'L' << iffalse->index << ';'; + else + out << ") goto " << 'L' << iftrue->index << ";"; +} + +void Ret::dump(QTextStream &out, Mode) +{ + out << "return"; + if (expr) { + out << ' '; + expr->dump(out); + } + out << ';'; +} + +Function *Module::newFunction(const QString &name, Function *outer) +{ + Function *f = new Function(this, outer, name); + functions.append(f); + if (!outer) { + assert(!rootFunction); + rootFunction = f; + } else { + outer->nestedFunctions.append(f); + } + return f; +} + +Module::~Module() +{ + foreach (Function *f, functions) { + delete f; + } +} + +Function::~Function() +{ + // destroy the Stmt::Data blocks manually, because memory pool cleanup won't + // call the Stmt destructors. + foreach (IR::BasicBlock *b, basicBlocks) + foreach (IR::Stmt *s, b->statements) + s->destroyData(); + + qDeleteAll(basicBlocks); + pool = 0; + module = 0; +} + + +const QString *Function::newString(const QString &text) +{ + return &*strings.insert(text); +} + +BasicBlock *Function::newBasicBlock(BasicBlockInsertMode mode) +{ + BasicBlock *block = new BasicBlock(this); + return mode == InsertBlock ? insertBasicBlock(block) : block; +} + +void Function::dump(QTextStream &out, Stmt::Mode mode) +{ + QString n = name ? *name : QString(); + if (n.isEmpty()) + n.sprintf("%p", this); + out << "function " << n << "() {" << endl; + foreach (const QString *formal, formals) + out << "\treceive " << *formal << ';' << endl; + foreach (const QString *local, locals) + out << "\tlocal " << *local << ';' << endl; + foreach (BasicBlock *bb, basicBlocks) + bb->dump(out, mode); + out << '}' << endl; +} + +void Function::removeSharedExpressions() +{ + RemoveSharedExpressions removeSharedExpressions; + removeSharedExpressions(this); +} + +unsigned BasicBlock::newTemp() +{ + return function->tempCount++; +} + +Temp *BasicBlock::TEMP(int index, uint scope) +{ + Temp *e = function->New(); + e->init(index, scope); + return e; +} + +Expr *BasicBlock::CONST(Type type, double value) +{ + Const *e = function->New(); + e->init(type, value); + return e; +} + +Expr *BasicBlock::STRING(const QString *value) +{ + String *e = function->New(); + e->init(value); + return e; +} + +Expr *BasicBlock::REGEXP(const QString *value, int flags) +{ + RegExp *e = function->New(); + e->init(value, flags); + return e; +} + +Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) +{ + Name *e = function->New(); + e->init(function->newString(id), line, column); + return e; +} + +Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column) +{ + Name *e = function->New(); + e->init(builtin, line, column); + return e; +} + +Closure *BasicBlock::CLOSURE(Function *function) +{ + Closure *clos = function->New(); + clos->init(function); + return clos; +} + +Expr *BasicBlock::UNOP(AluOp op, Temp *expr) +{ + Unop *e = function->New(); + e->init(op, expr); + return e; +} + +Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) +{ + Binop *e = function->New(); + e->init(op, left, right); + return e; +} + +Expr *BasicBlock::CALL(Expr *base, ExprList *args) +{ + Call *e = function->New(); + e->init(base, args); + return e; +} + +Expr *BasicBlock::NEW(Expr *base, ExprList *args) +{ + New *e = function->New(); + e->init(base, args); + return e; +} + +Expr *BasicBlock::SUBSCRIPT(Temp *base, Temp *index) +{ + Subscript *e = function->New(); + e->init(base, index); + return e; +} + +Expr *BasicBlock::MEMBER(Temp *base, const QString *name) +{ + Member*e = function->New(); + e->init(base, name); + return e; +} + +Stmt *BasicBlock::EXP(Expr *expr) +{ + if (isTerminated()) + return 0; + + Exp *s = function->New(); + s->init(expr); + statements.append(s); + return s; +} + +Stmt *BasicBlock::ENTER(Expr *expr) +{ + if (isTerminated()) + return 0; + + Enter *s = function->New(); + s->init(expr); + statements.append(s); + return s; +} + +Stmt *BasicBlock::LEAVE() +{ + if (isTerminated()) + return 0; + + Leave *s = function->New(); + s->init(); + statements.append(s); + return s; +} + +Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op) +{ + if (isTerminated()) + return 0; + + Move *s = function->New(); + s->init(target, source, op); + statements.append(s); + return s; +} + +Stmt *BasicBlock::JUMP(BasicBlock *target) +{ + if (isTerminated()) + return 0; + + Jump *s = function->New(); + s->init(target); + statements.append(s); + + assert(! out.contains(target)); + out.append(target); + + assert(! target->in.contains(this)); + target->in.append(this); + + return s; +} + +Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) +{ + if (isTerminated()) + return 0; + + if (iftrue == iffalse) { + MOVE(TEMP(newTemp()), cond); + return JUMP(iftrue); + } + + CJump *s = function->New(); + s->init(cond, iftrue, iffalse); + statements.append(s); + + assert(! out.contains(iftrue)); + out.append(iftrue); + + assert(! iftrue->in.contains(this)); + iftrue->in.append(this); + + assert(! out.contains(iffalse)); + out.append(iffalse); + + assert(! iffalse->in.contains(this)); + iffalse->in.append(this); + + return s; +} + +Stmt *BasicBlock::RET(Temp *expr) +{ + if (isTerminated()) + return 0; + + Ret *s = function->New(); + s->init(expr); + statements.append(s); + return s; +} + +void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) +{ + out << 'L' << index << ':' << endl; + foreach (Stmt *s, statements) { + out << '\t'; + s->dump(out, mode); + out << endl; + } +} + +CloneExpr::CloneExpr(BasicBlock *block) + : block(block), cloned(0) +{ +} + +void CloneExpr::setBasicBlock(BasicBlock *block) +{ + this->block = block; +} + +ExprList *CloneExpr::clone(ExprList *list) +{ + if (! list) + return 0; + + ExprList *clonedList = block->function->New(); + clonedList->init(clone(list->expr), clone(list->next)); + return clonedList; +} + +void CloneExpr::visitConst(Const *e) +{ + cloned = block->CONST(e->type, e->value); +} + +void CloneExpr::visitString(String *e) +{ + cloned = block->STRING(e->value); +} + +void CloneExpr::visitRegExp(RegExp *e) +{ + cloned = block->REGEXP(e->value, e->flags); +} + +void CloneExpr::visitName(Name *e) +{ + if (e->id) + cloned = block->NAME(*e->id, e->line, e->column); + else + cloned = block->NAME(e->builtin, e->line, e->column); +} + +void CloneExpr::visitTemp(Temp *e) +{ + cloned = block->TEMP(e->index, e->scope); +} + +void CloneExpr::visitClosure(Closure *e) +{ + cloned = block->CLOSURE(e->value); +} + +void CloneExpr::visitUnop(Unop *e) +{ + cloned = block->UNOP(e->op, clone(e->expr)); +} + +void CloneExpr::visitBinop(Binop *e) +{ + cloned = block->BINOP(e->op, clone(e->left), clone(e->right)); +} + +void CloneExpr::visitCall(Call *e) +{ + cloned = block->CALL(clone(e->base), clone(e->args)); +} + +void CloneExpr::visitNew(New *e) +{ + cloned = block->NEW(clone(e->base), clone(e->args)); +} + +void CloneExpr::visitSubscript(Subscript *e) +{ + cloned = block->SUBSCRIPT(clone(e->base), clone(e->index)); +} + +void CloneExpr::visitMember(Member *e) +{ + cloned = block->MEMBER(clone(e->base), e->name); +} + +} // end of namespace IR +} // end of namespace QQmlJS + +QT_END_NAMESPACE diff --git a/src/v4/qv4jsir_p.h b/src/v4/qv4jsir_p.h new file mode 100644 index 0000000000..50a5247277 --- /dev/null +++ b/src/v4/qv4jsir_p.h @@ -0,0 +1,786 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4IR_P_H +#define QV4IR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4global.h" +#include + +#include +#include +#include + +#ifdef CONST +#undef CONST +#endif + +QT_BEGIN_NAMESPACE + +class QTextStream; +class QQmlType; + +namespace QQmlJS { + +inline bool isNegative(double d) +{ + uchar *dch = (uchar *)&d; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return (dch[0] & 0x80); + else + return (dch[7] & 0x80); + +} + +namespace VM { +struct ExecutionContext; +struct Value; +} + +namespace IR { + +struct BasicBlock; +struct Function; +struct Module; + +struct Stmt; +struct Expr; + +// expressions +struct Const; +struct String; +struct RegExp; +struct Name; +struct Temp; +struct Closure; +struct Unop; +struct Binop; +struct Call; +struct New; +struct Subscript; +struct Member; + +// statements +struct Exp; +struct Enter; +struct Leave; +struct Move; +struct Jump; +struct CJump; +struct Ret; + +enum AluOp { + OpInvalid = 0, + + OpIfTrue, + OpNot, + OpUMinus, + OpUPlus, + OpCompl, + OpIncrement, + OpDecrement, + + OpBitAnd, + OpBitOr, + OpBitXor, + + OpAdd, + OpSub, + OpMul, + OpDiv, + OpMod, + + OpLShift, + OpRShift, + OpURShift, + + OpGt, + OpLt, + OpGe, + OpLe, + OpEqual, + OpNotEqual, + OpStrictEqual, + OpStrictNotEqual, + + OpInstanceof, + OpIn, + + OpAnd, + OpOr, + + LastAluOp = OpOr +}; +AluOp binaryOperator(int op); +const char *opname(IR::AluOp op); + +enum Type { + MissingType, // Used to indicate holes in array initialization (e.g. [,,]) + UndefinedType, + NullType, + BoolType, + NumberType +}; + +struct ExprVisitor { + virtual ~ExprVisitor() {} + virtual void visitConst(Const *) = 0; + virtual void visitString(String *) = 0; + virtual void visitRegExp(RegExp *) = 0; + virtual void visitName(Name *) = 0; + virtual void visitTemp(Temp *) = 0; + virtual void visitClosure(Closure *) = 0; + virtual void visitUnop(Unop *) = 0; + virtual void visitBinop(Binop *) = 0; + virtual void visitCall(Call *) = 0; + virtual void visitNew(New *) = 0; + virtual void visitSubscript(Subscript *) = 0; + virtual void visitMember(Member *) = 0; +}; + +struct StmtVisitor { + virtual ~StmtVisitor() {} + virtual void visitExp(Exp *) = 0; + virtual void visitEnter(Enter *) = 0; + virtual void visitLeave(Leave *) = 0; + virtual void visitMove(Move *) = 0; + virtual void visitJump(Jump *) = 0; + virtual void visitCJump(CJump *) = 0; + virtual void visitRet(Ret *) = 0; +}; + +struct Expr { + virtual ~Expr() {} + virtual void accept(ExprVisitor *) = 0; + virtual bool isLValue() { return false; } + virtual Const *asConst() { return 0; } + virtual String *asString() { return 0; } + virtual RegExp *asRegExp() { return 0; } + virtual Name *asName() { return 0; } + virtual Temp *asTemp() { return 0; } + virtual Closure *asClosure() { return 0; } + virtual Unop *asUnop() { return 0; } + virtual Binop *asBinop() { return 0; } + virtual Call *asCall() { return 0; } + virtual New *asNew() { return 0; } + virtual Subscript *asSubscript() { return 0; } + virtual Member *asMember() { return 0; } + virtual void dump(QTextStream &out) = 0; +}; + +struct ExprList { + Expr *expr; + ExprList *next; + + void init(Expr *expr, ExprList *next = 0) + { + this->expr = expr; + this->next = next; + } +}; + +struct Const: Expr { + Type type; + double value; + + void init(Type type, double value) + { + this->type = type; + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitConst(this); } + virtual Const *asConst() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct String: Expr { + const QString *value; + + void init(const QString *value) + { + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitString(this); } + virtual String *asString() { return this; } + + virtual void dump(QTextStream &out); + static QString escape(const QString &s); +}; + +struct RegExp: Expr { + // needs to be compatible with the flags in the lexer + enum Flags { + RegExp_Global = 0x01, + RegExp_IgnoreCase = 0x02, + RegExp_Multiline = 0x04 + }; + + const QString *value; + int flags; + + void init(const QString *value, int flags) + { + this->value = value; + this->flags = flags; + } + + virtual void accept(ExprVisitor *v) { v->visitRegExp(this); } + virtual RegExp *asRegExp() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Name: Expr { + enum Builtin { + builtin_invalid, + builtin_typeof, + builtin_delete, + builtin_postincrement, + builtin_postdecrement, + builtin_throw, + builtin_create_exception_handler, + builtin_finish_try, + builtin_get_exception, + builtin_foreach_iterator_object, + builtin_foreach_next_property_name, + builtin_push_with_scope, + builtin_push_catch_scope, + builtin_pop_scope, + builtin_declare_vars, + builtin_define_property, + builtin_define_array, + builtin_define_getter_setter + }; + + const QString *id; + Builtin builtin; + quint32 line; + quint32 column; + + void init(const QString *id, quint32 line, quint32 column); + void init(Builtin builtin, quint32 line, quint32 column); + + virtual void accept(ExprVisitor *v) { v->visitName(this); } + virtual bool isLValue() { return true; } + virtual Name *asName() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Temp: Expr { + int index; + int scope; // how many scopes outside the current one? + + void init(int index, int scope = 0) + { + this->index = index; + this->scope = scope; + } + + virtual void accept(ExprVisitor *v) { v->visitTemp(this); } + virtual bool isLValue() { return true; } + virtual Temp *asTemp() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Closure: Expr { + Function *value; + + void init(Function *value) + { + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitClosure(this); } + virtual Closure *asClosure() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Unop: Expr { + AluOp op; + Temp *expr; + + void init(AluOp op, Temp *expr) + { + this->op = op; + this->expr = expr; + } + + virtual void accept(ExprVisitor *v) { v->visitUnop(this); } + virtual Unop *asUnop() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Binop: Expr { + AluOp op; + Expr *left; // Temp or Const + Expr *right; // Temp or Const + + void init(AluOp op, Expr *left, Expr *right) + { + this->op = op; + this->left = left; + this->right = right; + } + + virtual void accept(ExprVisitor *v) { v->visitBinop(this); } + virtual Binop *asBinop() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Call: Expr { + Expr *base; // Name, Member, Temp + ExprList *args; // List of Temps + + void init(Expr *base, ExprList *args) + { + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitCall(this); } + virtual Call *asCall() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct New: Expr { + Expr *base; // Name, Member, Temp + ExprList *args; // List of Temps + + void init(Expr *base, ExprList *args) + { + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitNew(this); } + virtual New *asNew() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Subscript: Expr { + Temp *base; + Temp *index; + + void init(Temp *base, Temp *index) + { + this->base = base; + this->index = index; + } + + virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } + virtual bool isLValue() { return true; } + virtual Subscript *asSubscript() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Member: Expr { + Temp *base; + const QString *name; + + void init(Temp *base, const QString *name) + { + this->base = base; + this->name = name; + } + + virtual void accept(ExprVisitor *v) { v->visitMember(this); } + virtual bool isLValue() { return true; } + virtual Member *asMember() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Stmt { + enum Mode { + HIR, + MIR + }; + + struct Data { + QVector uses; + QVector defs; + QBitArray liveIn; + QBitArray liveOut; + }; + + Data *d; + + Stmt(): d(0) {} + virtual ~Stmt() { Q_UNREACHABLE(); } + virtual Stmt *asTerminator() { return 0; } + + virtual void accept(StmtVisitor *) = 0; + virtual Exp *asExp() { return 0; } + virtual Move *asMove() { return 0; } + virtual Enter *asEnter() { return 0; } + virtual Leave *asLeave() { return 0; } + virtual Jump *asJump() { return 0; } + virtual CJump *asCJump() { return 0; } + virtual Ret *asRet() { return 0; } + virtual void dump(QTextStream &out, Mode mode = HIR) = 0; + + void destroyData() { + delete d; + d = 0; + } +}; + +struct Exp: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual void accept(StmtVisitor *v) { v->visitExp(this); } + virtual Exp *asExp() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Move: Stmt { + Expr *target; // LHS - Temp, Name, Member or Subscript + Expr *source; + AluOp op; + + void init(Expr *target, Expr *source, AluOp op) + { + this->target = target; + this->source = source; + this->op = op; + } + + virtual void accept(StmtVisitor *v) { v->visitMove(this); } + virtual Move *asMove() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Enter: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual void accept(StmtVisitor *v) { v->visitEnter(this); } + virtual Enter *asEnter() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Leave: Stmt { + void init() {} + + virtual void accept(StmtVisitor *v) { v->visitLeave(this); } + virtual Leave *asLeave() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Jump: Stmt { + BasicBlock *target; + + void init(BasicBlock *target) + { + this->target = target; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitJump(this); } + virtual Jump *asJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct CJump: Stmt { + Expr *cond; // Temp, Binop + BasicBlock *iftrue; + BasicBlock *iffalse; + + void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) + { + this->cond = cond; + this->iftrue = iftrue; + this->iffalse = iffalse; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitCJump(this); } + virtual CJump *asCJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Ret: Stmt { + Temp *expr; + + void init(Temp *expr) + { + this->expr = expr; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitRet(this); } + virtual Ret *asRet() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Q_V4_EXPORT Module { + MemoryPool pool; + QVector functions; + Function *rootFunction; + + Function *newFunction(const QString &name, Function *outer); + + Module() : rootFunction(0) {} + ~Module(); +}; + +struct Function { + Module *module; + MemoryPool *pool; + const QString *name; + QVector basicBlocks; + int tempCount; + int maxNumberOfArguments; + QSet strings; + QList formals; + QList locals; + QVector nestedFunctions; + Function *outer; + + int insideWithOrCatch; + + uint hasDirectEval: 1; + uint usesArgumentsObject : 1; + uint isStrict: 1; + uint unused : 29; + + template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } + + Function(Module *module, Function *outer, const QString &name) + : module(module) + , pool(&module->pool) + , tempCount(0) + , maxNumberOfArguments(0) + , outer(outer) + , insideWithOrCatch(0) + , hasDirectEval(false) + , usesArgumentsObject(false) + , isStrict(false) + , unused(0) + { this->name = newString(name); } + + ~Function(); + + enum BasicBlockInsertMode { + InsertBlock, + DontInsertBlock + }; + + BasicBlock *newBasicBlock(BasicBlockInsertMode mode = InsertBlock); + const QString *newString(const QString &text); + + void RECEIVE(const QString &name) { formals.append(newString(name)); } + void LOCAL(const QString &name) { locals.append(newString(name)); } + + inline BasicBlock *insertBasicBlock(BasicBlock *block) { basicBlocks.append(block); return block; } + + void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); + + void removeSharedExpressions(); +}; + +struct BasicBlock { + Function *function; + QVector statements; + QVector in; + QVector out; + QBitArray liveIn; + QBitArray liveOut; + int index; + int offset; + + BasicBlock(Function *function): function(function), index(-1), offset(-1) {} + ~BasicBlock() {} + + template inline Instr i(Instr i) { statements.append(i); return i; } + + inline bool isEmpty() const { + return statements.isEmpty(); + } + + inline Stmt *terminator() const { + if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0) + return statements.at(statements.size() - 1); + return 0; + } + + inline bool isTerminated() const { + if (terminator() != 0) + return true; + return false; + } + + unsigned newTemp(); + + Temp *TEMP(int index, uint scope = 0); + + Expr *CONST(Type type, double value); + Expr *STRING(const QString *value); + Expr *REGEXP(const QString *value, int flags); + + Name *NAME(const QString &id, quint32 line, quint32 column); + Name *NAME(Name::Builtin builtin, quint32 line, quint32 column); + + Closure *CLOSURE(Function *function); + + Expr *UNOP(AluOp op, Temp *expr); + Expr *BINOP(AluOp op, Expr *left, Expr *right); + Expr *CALL(Expr *base, ExprList *args = 0); + Expr *NEW(Expr *base, ExprList *args = 0); + Expr *SUBSCRIPT(Temp *base, Temp *index); + Expr *MEMBER(Temp *base, const QString *name); + + Stmt *EXP(Expr *expr); + Stmt *ENTER(Expr *expr); + Stmt *LEAVE(); + + Stmt *MOVE(Expr *target, Expr *source, AluOp op = IR::OpInvalid); + + Stmt *JUMP(BasicBlock *target); + Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); + Stmt *RET(Temp *expr); + + void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); +}; + +class CloneExpr: protected IR::ExprVisitor +{ +public: + explicit CloneExpr(IR::BasicBlock *block = 0); + + void setBasicBlock(IR::BasicBlock *block); + + template + _Expr *operator()(_Expr *expr) + { + return clone(expr); + } + + template + _Expr *clone(_Expr *expr) + { + Expr *c = expr; + qSwap(cloned, c); + expr->accept(this); + qSwap(cloned, c); + return static_cast<_Expr *>(c); + } + +protected: + IR::ExprList *clone(IR::ExprList *list); + + virtual void visitConst(Const *); + virtual void visitString(String *); + virtual void visitRegExp(RegExp *); + virtual void visitName(Name *); + virtual void visitTemp(Temp *); + virtual void visitClosure(Closure *); + virtual void visitUnop(Unop *); + virtual void visitBinop(Binop *); + virtual void visitCall(Call *); + virtual void visitNew(New *); + virtual void visitSubscript(Subscript *); + virtual void visitMember(Member *); + +private: + IR::BasicBlock *block; + IR::Expr *cloned; +}; + +} // end of namespace IR + +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4IR_P_H diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 3c7356b8f8..97bdcc0110 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -40,7 +40,7 @@ ****************************************************************************/ #include "qv4object.h" -#include "qv4ir_p.h" +#include "qv4jsir_p.h" #include "qv4isel_p.h" #include "qv4objectproto.h" #include "qv4stringobject.h" @@ -51,7 +51,7 @@ #include #include #include -#include +#include #include #include "private/qlocale_tools_p.h" diff --git a/src/v4/qv4objectproto.cpp b/src/v4/qv4objectproto.cpp index 21d61820c2..b4f38ddcda 100644 --- a/src/v4/qv4objectproto.cpp +++ b/src/v4/qv4objectproto.cpp @@ -53,7 +53,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/v4/qv4regexpobject.cpp b/src/v4/qv4regexpobject.cpp index d9abd513eb..ed0ef144e0 100644 --- a/src/v4/qv4regexpobject.cpp +++ b/src/v4/qv4regexpobject.cpp @@ -40,7 +40,7 @@ ****************************************************************************/ #include "qv4regexpobject.h" -#include "qv4ir_p.h" +#include "qv4jsir_p.h" #include "qv4isel_p.h" #include "qv4objectproto.h" #include "qv4stringobject.h" @@ -50,7 +50,7 @@ #include #include #include -#include +#include #include #include "private/qlocale_tools_p.h" diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp index 437c30928e..eab6989135 100644 --- a/src/v4/qv4stringobject.cpp +++ b/src/v4/qv4stringobject.cpp @@ -58,7 +58,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/v4/v4.pro b/src/v4/v4.pro index a96d0635f7..c644e45892 100644 --- a/src/v4/v4.pro +++ b/src/v4/v4.pro @@ -17,7 +17,7 @@ CONFIG += warn_off SOURCES += \ qv4codegen.cpp \ - qv4ir.cpp \ + qv4jsir.cpp \ qmljs_engine.cpp \ qmljs_environment.cpp \ qmljs_runtime.cpp \ @@ -53,7 +53,7 @@ SOURCES += \ HEADERS += \ qv4global.h \ qv4codegen_p.h \ - qv4ir_p.h \ + qv4jsir_p.h \ qmljs_engine.h \ qmljs_environment.h \ qmljs_runtime.h \ -- cgit v1.2.3 From 090b94a045e1bec572554d10b16c4feda6cf83ab Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 15 Feb 2013 14:58:01 +0100 Subject: Remove unused wtf inclusion from qv4managed.h This allows including this header file outside of the module, i.e. in a qml integration. Change-Id: I0aed18d76dfacfdf5e615cc12f3d2904fcef9091 Reviewed-by: Lars Knoll --- src/v4/qv4managed.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/v4/qv4managed.h b/src/v4/qv4managed.h index 1022b4441a..f9436926b5 100644 --- a/src/v4/qv4managed.h +++ b/src/v4/qv4managed.h @@ -44,7 +44,6 @@ #include #include #include -#include #include "qv4global.h" QT_BEGIN_NAMESPACE -- cgit v1.2.3 From 85734b5799617627386557df89079a98ae2dd60a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 5 Mar 2013 13:12:37 +0100 Subject: Add support for associating external resources with garbage collected JS objects Change-Id: Ib09096b684997ac77902e3cc50458092e2f569a2 Reviewed-by: Lars Knoll --- src/v4/qv4object.cpp | 2 ++ src/v4/qv4object.h | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 97bdcc0110..8c96e23c2f 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -71,6 +71,7 @@ Object::Object(ExecutionEngine *engine) , internalClass(engine->emptyClass) , memberDataAlloc(0), memberData(0) , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayData(0), sparseArray(0) + , externalResource(0) { vtbl = &static_vtbl; type = Type_Object; @@ -79,6 +80,7 @@ Object::Object(ExecutionEngine *engine) Object::~Object() { + delete externalResource; delete [] memberData; delete [] (arrayData - (sparseArray ? 0 : arrayOffset)); delete sparseArray; diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index 62283bc407..93fe8bf755 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -102,6 +102,12 @@ struct URIErrorPrototype; struct Q_V4_EXPORT Object: Managed { + + class ExternalResource { + public: + virtual ~ExternalResource() {} + }; + Object *prototype; InternalClass *internalClass; uint memberDataAlloc; @@ -115,6 +121,7 @@ struct Q_V4_EXPORT Object: Managed { uint arrayAlloc; PropertyDescriptor *arrayData; SparseArray *sparseArray; + ExternalResource *externalResource; Object(ExecutionEngine *engine); ~Object(); -- cgit v1.2.3 From 471e0b0f7522c246f6f4f214a8483343eb237285 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 15 Feb 2013 14:59:34 +0100 Subject: Initial v8'ish API wrappers for QML integration The goal is for these wrappers to exist only for a limited period of time until all of QML is ported to use pure v4vm. Change-Id: I4568420a2116420ae6f45bc5eb1e3d464a7c4cc3 Reviewed-by: Lars Knoll --- src/v4/qcalculatehash_p.h | 66 + src/v4/qv4mm.h | 3 +- src/v4/qv4string.h | 2 +- src/v4/qv4v8.cpp | 437 ++++++ src/v4/qv4v8.h | 3826 +++++++++++++++++++++++++++++++++++++++++++++ src/v4/v4.pro | 7 +- 6 files changed, 4336 insertions(+), 5 deletions(-) create mode 100644 src/v4/qcalculatehash_p.h create mode 100644 src/v4/qv4v8.cpp create mode 100644 src/v4/qv4v8.h diff --git a/src/v4/qcalculatehash_p.h b/src/v4/qcalculatehash_p.h new file mode 100644 index 0000000000..9eedbfc645 --- /dev/null +++ b/src/v4/qcalculatehash_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtV8 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CALCULATEHASH_P_H +#define CALCULATEHASH_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +uint32_t calculateHash(const quint8* chars, int length); +uint32_t calculateHash(const quint16* chars, int length); + +QT_END_NAMESPACE + +#endif // CALCULATEHASH_P_H diff --git a/src/v4/qv4mm.h b/src/v4/qv4mm.h index 3ee914e16a..2c188dc628 100644 --- a/src/v4/qv4mm.h +++ b/src/v4/qv4mm.h @@ -31,7 +31,6 @@ #define QV4GC_H #include "qv4global.h" -#include "qv4object.h" #include @@ -42,6 +41,7 @@ QT_BEGIN_NAMESPACE namespace QQmlJS { namespace VM { +struct ExecutionEngine; struct Managed; class Q_V4_EXPORT MemoryManager @@ -121,7 +121,6 @@ protected: QScopedPointer m_d; }; - } // namespace VM } // namespace QQmlJS diff --git a/src/v4/qv4string.h b/src/v4/qv4string.h index eecaa5344f..4642189cc7 100644 --- a/src/v4/qv4string.h +++ b/src/v4/qv4string.h @@ -42,7 +42,7 @@ #define QV4STRING_H #include -#include +#include "qv4managed.h" QT_BEGIN_NAMESPACE diff --git a/src/v4/qv4v8.cpp b/src/v4/qv4v8.cpp new file mode 100644 index 0000000000..088f02249b --- /dev/null +++ b/src/v4/qv4v8.cpp @@ -0,0 +1,437 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4v8.h" + +#include "qmljs_engine.h" +#include "qmljs_runtime.h" +#include "qv4mm.h" +#include "qv4managed.h" +#include "qv4functionobject.h" +#include "qmljs_value.h" +#include "qv4isel_masm_p.h" + +#include + +using namespace QQmlJS; +using namespace QQmlJS::VM; + +namespace v8 { + +#define currentEngine() Isolate::GetCurrent()->GetCurrentContext()->GetEngine() + +#define Q_D(obj) QQmlJS::VM::Value *d = reinterpret_cast(obj) + +void gcProtect(void *handle) +{ + Q_D(handle); + if (VM::Managed *m = d->asManaged()) + currentEngine()->memoryManager->protect(m); +} + +void gcUnprotect(void *handle) +{ + Q_D(handle); + if (VM::Managed *m = d->asManaged()) + currentEngine()->memoryManager->unprotect(m); +} + +#define ValuePtr(obj) reinterpret_cast(obj) +#define ConstValuePtr(obj) reinterpret_cast(obj) + +bool Value::IsUndefined() const { return ConstValuePtr(this)->isUndefined(); } +bool Value::IsNull() const { return ConstValuePtr(this)->isNull(); } + +bool Value::IsTrue() const +{ + return ConstValuePtr(this)->isBoolean() && ConstValuePtr(this)->booleanValue(); +} + +bool Value::IsFalse() const +{ + return !IsTrue(); +} + +bool Value::IsString() const +{ + return ConstValuePtr(this)->isString(); +} + +bool Value::IsFunction() const +{ + return ConstValuePtr(this)->asFunctionObject(); +} + +bool Value::IsArray() const +{ + return ConstValuePtr(this)->asArrayObject(); +} + +bool Value::StrictEquals(Handle that) const +{ + return __qmljs_strict_equal(*ConstValuePtr(this), *ConstValuePtr(&that)); +} + +Local Value::ToBoolean() const +{ + return Local::New(Value::NewFromInternalValue(QQmlJS::VM::Value::fromBoolean(ConstValuePtr(this)->toBoolean()).val)); +} + +Local Value::ToNumber() const +{ + return Local::New(Value::NewFromInternalValue(QQmlJS::VM::Value::fromDouble(ConstValuePtr(this)->toNumber(currentEngine()->current)).val)); +} + +Local Value::ToString() const +{ + return Local::New(Value::NewFromInternalValue(QQmlJS::VM::Value::fromString(ConstValuePtr(this)->toString(currentEngine()->current)).val)); +} + +Local Value::ToDetailString() const +{ + return ToString(); +} + +Local Value::ToInteger() const +{ + return Local::New(Value::NewFromInternalValue(QQmlJS::VM::Value::fromDouble(ConstValuePtr(this)->toInteger(currentEngine()->current)).val)); +} + +Local Value::ToUint32() const +{ + return Local::New(Value::NewFromInternalValue(QQmlJS::VM::Value::fromUInt32(ConstValuePtr(this)->toUInt32(currentEngine()->current)).val)); +} + +Local Value::ToInt32() const +{ + return Local::New(Value::NewFromInternalValue(QQmlJS::VM::Value::fromInt32(ConstValuePtr(this)->toInt32(currentEngine()->current)).val)); +} + +Local Value::ToObject() const +{ + return Local::New(Value::NewFromInternalValue(QQmlJS::VM::Value::fromObject(ConstValuePtr(this)->toObject(currentEngine()->current)).val)); +} + +bool Value::IsObject() const +{ + return ConstValuePtr(this)->isObject(); +} + +bool Value::IsBoolean() const +{ + return ConstValuePtr(this)->isBoolean(); +} + +bool Value::IsNumber() const +{ + return ConstValuePtr(this)->isNumber(); +} + +bool Value::BooleanValue() const +{ + return ConstValuePtr(this)->toBoolean(); +} + +String::CompleteHashData String::CompleteHash() const +{ + CompleteHashData data; + data.hash = asVMString()->hashValue(); + data.length = asQString().length(); + data.symbol_id = 0; // ### + return data; +} + +bool String::Equals(uint16_t *str, int length) +{ + return asQString() == QString(reinterpret_cast(str), length); +} + +bool String::Equals(char *str, int length) +{ + return asQString() == QString::fromLatin1(str, length); +} + +Local String::New(const char *data, int length) +{ + QQmlJS::VM::Value v = QQmlJS::VM::Value::fromString(currentEngine()->current, QString::fromLatin1(data, length)); + return Local::New(v8::Value::NewFromInternalValue(v.val)); +} + +QString String::asQString() const +{ + return asVMString()->toQString(); +} + +VM::String *String::asVMString() const +{ + const VM::Value *v = ConstValuePtr(this); + ASSERT(v->isString()); + return v->stringValue(); +} + +double Number::Value() const +{ + const VM::Value *v = ConstValuePtr(this); + assert(v->isNumber()); + return v->asDouble(); +} + +int64_t Integer::Value() const +{ + const VM::Value *v = ConstValuePtr(this); + assert(v->isNumber()); + return (int64_t)v->asDouble(); +} + +int32_t Int32::Value() const +{ + const VM::Value *v = ConstValuePtr(this); + assert(v->isInteger()); + return v->int_32; +} + +uint32_t Uint32::Value() const +{ + const VM::Value *v = ConstValuePtr(this); + assert(v->isNumber()); + return v->toUInt32(currentEngine()->current); +} + +struct Context::Private +{ + Private() + { + iselFactory.reset(new QQmlJS::MASM::ISelFactory); + engine.reset(new QQmlJS::VM::ExecutionEngine(iselFactory.data())); + } + + QScopedPointer iselFactory; + QScopedPointer engine; +}; + +Context::Context() + : m_lastContext(0) + , d(new Private) +{ +} + +Context::~Context() +{ + delete d; +} + +Persistent Context::New(ExtensionConfiguration *extensions, Handle global_template, Handle global_object) +{ + Context *result = new Context; + return Persistent::New(Handle(result)); +} + +Local Context::Global() +{ + return Local::New(Value::NewFromInternalValue(d->engine->globalObject.val)); +} + +void Context::Enter() +{ + Isolate* iso = Isolate::GetCurrent(); + m_lastContext = iso->m_currentContext; + iso->m_currentContext = this; +} + +void Context::Exit() +{ + Isolate::GetCurrent()->m_currentContext = m_lastContext; +} + +Local Context::GetCurrent() +{ + return Context::Adopt(Isolate::GetCurrent()->m_currentContext); +} + +QQmlJS::VM::ExecutionEngine *Context::GetEngine() +{ + return d->engine.data(); +} + +static QThreadStorage currentIsolate; + +Isolate::Isolate() + : m_lastIsolate(0) +{ +} + +Isolate::~Isolate() +{ +} + +void Isolate::Enter() +{ + m_lastIsolate = currentIsolate.localData(); + currentIsolate.localData() = this; +} + +void Isolate::Exit() +{ + currentIsolate.localData() = m_lastIsolate; + m_lastIsolate = 0; +} + +Isolate *Isolate::GetCurrent() +{ + if (!currentIsolate.hasLocalData()) + currentIsolate.setLocalData(new Isolate); + return currentIsolate.localData(); +} + +Local Object::Get(Handle key) +{ + Local result; + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + if (!o) + return result; + QQmlJS::VM::ExecutionContext *ctx = currentEngine()->current; + QQmlJS::VM::Value prop = o->__get__(ctx, ValuePtr(&key)->toString(ctx)); + return Local::New(Value::NewFromInternalValue(prop.val)); +} + +Local Object::Get(uint32_t key) +{ + Local result; + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + if (!o) + return result; + QQmlJS::VM::ExecutionContext *ctx = currentEngine()->current; + QQmlJS::VM::Value prop = o->__get__(ctx, key); + return Local::New(Value::NewFromInternalValue(prop.val)); +} + +Local Object::GetPrototype() +{ + Local result; + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + if (!o) + return result; + return Local::New(Value::NewFromInternalValue(QQmlJS::VM::Value::fromObject(o->prototype).val)); +} + +bool Object::Has(Handle key) +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + if (!o) + return false; + return o->__hasProperty__(currentEngine()->current, ValuePtr(&key)->asString()); +} + +Local Function::Call(Handle thisObj, int argc, Handle argv[]) +{ + QQmlJS::VM::FunctionObject *f = ConstValuePtr(this)->asFunctionObject(); + if (!f) + return Local(); + QQmlJS::VM::Value result = f->call(currentEngine()->current, + *ConstValuePtr(&thisObj), + reinterpret_cast(argv), + argc); + return Local::New(Value::NewFromInternalValue(result.val)); +} + +struct ExternalResourceWrapper : public QQmlJS::VM::Object::ExternalResource +{ + ExternalResourceWrapper(v8::Object::ExternalResource *wrapped) + { + this->wrapped = wrapped; + } + + virtual ~ExternalResourceWrapper() + { + wrapped->Dispose(); + } + + v8::Object::ExternalResource *wrapped; +}; + +void Object::SetExternalResource(Object::ExternalResource *resource) +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + if (!o) + return; + o->externalResource = new ExternalResourceWrapper(resource); +} + +Object::ExternalResource *Object::GetExternalResource() +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + if (!o || !o->externalResource) + return 0; + return static_cast(o->externalResource)->wrapped; +} + +Local